diff --git a/.appveyor.yml b/.appveyor.yml deleted file mode 100644 index 4f4083c99b16..000000000000 --- a/.appveyor.yml +++ /dev/null @@ -1,150 +0,0 @@ -# As config was originally based on an example by Olivier Grisel. Thanks! -# https://github.com/ogrisel/python-appveyor-demo/blob/master/appveyor.yml -clone_depth: 50 - -# No reason for us to restrict the number concurrent jobs -max_jobs: 100 - -cache: - - '%LOCALAPPDATA%\pip\Cache' - -environment: - global: - MINGW_32: C:\mingw-w64\i686-6.3.0-posix-dwarf-rt_v5-rev1\mingw32\bin - MINGW_64: C:\mingw-w64\x86_64-6.3.0-posix-seh-rt_v5-rev1\mingw64\bin - OPENBLAS_32: https://3f23b170c54c2533c070-1c8a9b3114517dc5fe17b7c3f8c63a43.ssl.cf2.rackcdn.com/openblas-5f998ef_gcc7_1_0_win32.zip - OPENBLAS_64: https://3f23b170c54c2533c070-1c8a9b3114517dc5fe17b7c3f8c63a43.ssl.cf2.rackcdn.com/openblas-5f998ef_gcc7_1_0_win64.zip - APPVEYOR_SAVE_CACHE_ON_ERROR: true - APPVEYOR_SKIP_FINALIZE_ON_EXIT: true - TEST_TIMEOUT: 1000 - NPY_NUM_BUILD_JOBS: 4 - - matrix: - - PYTHON: C:\Python34-x64 - PYTHON_VERSION: 3.4 - PYTHON_ARCH: 64 - TEST_MODE: fast - - - PYTHON: C:\Python36 - PYTHON_VERSION: 3.6 - PYTHON_ARCH: 32 - TEST_MODE: fast - - - PYTHON: C:\Python27-x64 - PYTHON_VERSION: 2.7 - PYTHON_ARCH: 64 - TEST_MODE: fast - - - PYTHON: C:\Python36-x64 - PYTHON_VERSION: 3.6 - PYTHON_ARCH: 64 - TEST_MODE: full - -init: - - "ECHO %PYTHON% %PYTHON_VERSION% %PYTHON_ARCH%" - - "ECHO \"%APPVEYOR_SCHEDULED_BUILD%\"" - # If there is a newer build queued for the same PR, cancel this one. - # The AppVeyor 'rollout builds' option is supposed to serve the same - # purpose but it is problematic because it tends to cancel builds pushed - # directly to master instead of just PR builds (or the converse). - # credits: JuliaLang developers. - - ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod ` - https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | ` - Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { ` - raise "There are newer queued builds for this pull request, skipping build." - } - -install: - # Prepend newly installed Python to the PATH of this build (this cannot be - # done from inside the powershell script as it would require to restart - # the parent CMD process). - - SET PATH=%PYTHON%;%PYTHON%\Scripts;%PATH% - - if [%PYTHON_ARCH%]==[32] SET PATH=%MINGW_32%;%PATH% & SET OPENBLAS=%OPENBLAS_32% - - if [%PYTHON_ARCH%]==[64] SET PATH=%MINGW_64%;%PATH% & SET OPENBLAS=%OPENBLAS_64% - - # Check that we have the expected version and architecture for Python - - python --version - - >- - %CMD_IN_ENV% - python -c "import sys,platform,struct; - print(sys.platform, platform.machine(), struct.calcsize('P') * 8, )" - - # Install "openblas.a" to PYTHON\lib - # Library provided by Matthew Brett at https://github.com/matthew-brett/build-openblas - - ps: | - $clnt = new-object System.Net.WebClient - $file = "$(New-TemporaryFile).zip" - $tmpdir = New-TemporaryFile | %{ rm $_; mkdir $_ } - $destination = "$env:PYTHON\lib\openblas.a" - - echo $file - echo $tmpdir - echo $env:OPENBLAS - - $clnt.DownloadFile($env:OPENBLAS, $file) - Get-FileHash $file | Format-List - - Expand-Archive $file $tmpdir - - rm $tmpdir\$env:PYTHON_ARCH\lib\*.dll.a - $lib = ls $tmpdir\$env:PYTHON_ARCH\lib\*.a | ForEach { ls $_ } | Select-Object -first 1 - echo $lib - - cp $lib $destination - ls $destination - - # Upgrade to the latest pip. - - 'python -m pip install -U pip setuptools wheel' - - # Install the numpy test dependencies. - - 'pip install -U --timeout 5 --retries 2 -r tools/ci/appveyor/requirements.txt' - -build_script: - # Here, we add MinGW to the path to be able to link an OpenBLAS.dll - # We then use the import library from the DLL to compile with MSVC - - ps: | - pip wheel -v -v -v --wheel-dir=dist . - - # For each wheel that pip has placed in the "dist" directory - # First, upload the wheel to the "artifacts" tab and then - # install the wheel. If we have only built numpy (as is the case here), - # then there will be one wheel to install. - - # This method is more representative of what will be distributed, - # because it actually tests what the built wheels will be rather than - # what 'setup.py install' will do and at it uploads the wheels so that - # they can be inspected. - - ls dist -r | Foreach-Object { - Push-AppveyorArtifact $_.FullName - pip install $_.FullName - } - -test_script: - python runtests.py -v -n -m %TEST_MODE% -- --junitxml=%cd%\junit-results.xml - -after_build: - # Remove old or huge cache files to hopefully not exceed the 1GB cache limit. - # - # If the cache limit is reached, the cache will not be updated (of not even - # created in the first run). So this is a trade of between keeping the cache - # current and having a cache at all. - # NB: This is done only `on_success` since the cache in uploaded only on - # success anyway. - - C:\cygwin\bin\find "%LOCALAPPDATA%\pip" -type f -mtime +360 -delete - - C:\cygwin\bin\find "%LOCALAPPDATA%\pip" -type f -size +10M -delete - - C:\cygwin\bin\find "%LOCALAPPDATA%\pip" -empty -delete - # Show size of cache - - C:\cygwin\bin\du -hs "%LOCALAPPDATA%\pip\Cache" - -on_finish: - # We can get a nice display of test results in the "test" tab with py.test - # For now, this does nothing. - - ps: | - If (Test-Path .\junit-results.xml) { - (new-object net.webclient).UploadFile( - "https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)", - (Resolve-Path .\junit-results.xml) - ) - } - $LastExitCode = 0 diff --git a/.circleci/config.yml b/.circleci/config.yml index e2eb01b04bed..182f7e678e60 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -8,53 +8,91 @@ jobs: docker: # CircleCI maintains a library of pre-built images # documented at https://circleci.com/docs/2.0/circleci-images/ - - image: circleci/python:3.6.1 + # circleci/python3.8 images come with old versions of Doxygen(1.6.x), + # therefore a new base image chose instead to guarantee to + # have a newer version >= 1.8.10 to avoid warnings + # that related to the default behaviors or non-exist config options + - image: cimg/base:2021.05 working_directory: ~/repo steps: - - checkout + - checkout: + - run: + name: pull changes from merge + command: | + if [[ -v CI_PULL_REQUEST ]] ; then git pull --ff-only origin "refs/pull/${CI_PULL_REQUEST//*pull\//}/merge" ; fi - run: - name: install dependencies + name: update submodules + command: | + git submodule init + git submodule update + + - run: + name: create virtual environment, install dependencies command: | - python3 -m venv venv - . venv/bin/activate - pip install cython sphinx matplotlib sudo apt-get update - sudo apt-get install -y graphviz texlive-fonts-recommended texlive-latex-recommended texlive-latex-extra texlive-generic-extra latexmk texlive-xetex + sudo apt-get install -y python3.8 python3.8-dev python3-venv graphviz texlive-fonts-recommended texlive-latex-recommended \ + texlive-latex-extra latexmk texlive-xetex doxygen + python3.8 -m venv venv + . venv/bin/activate - run: name: build numpy command: | . venv/bin/activate - pip install --upgrade pip setuptools - pip install cython + pip install --progress-bar=off --upgrade pip 'setuptools<49.2.0' + pip install --progress-bar=off -r test_requirements.txt + pip install --progress-bar=off -r doc_requirements.txt pip install . - pip install scipy + + - run: + name: create release notes + command: | + . venv/bin/activate + pip install towncrier + VERSION=$(python -c "import setup; print(setup.VERSION)") + towncrier build --version $VERSION --yes + ./tools/ci/test_all_newsfragments_used.py + + - run: + name: run doctests on documentation + command: | + . venv/bin/activate + (cd doc ; git submodule update --init) + python tools/refguide_check.py --rst + + - run: + name: build devdocs w/ref warnings + command: | + . venv/bin/activate + cd doc + # Don't use -q, show warning summary" + SPHINXOPTS="-j4 -n" make -e html || echo "ignoring errors for now, see gh-13114" - run: name: build devdocs + no_output_timeout: 30m command: | . venv/bin/activate cd doc - git submodule update --init - make html + make clean + SPHINXOPTS="-j4 -q" make -e html - run: name: build neps command: | . venv/bin/activate cd doc/neps - make html + SPHINXOPTS="-j4 -q" make -e html - # - store_artifacts: - # path: doc/build/html/ - # destination: devdocs + - store_artifacts: + path: doc/build/html/ - # - store_artifacts: - # path: doc/neps/_build/html/ + - store_artifacts: + path: doc/neps/_build/html/ # destination: neps - add_ssh_keys: @@ -64,7 +102,7 @@ jobs: - run: name: deploy devdocs command: | - if [ "${CIRCLE_BRANCH}" == "master" ]; then + if [ "${CIRCLE_BRANCH}" == "main" ]; then touch doc/build/html/.nojekyll ./tools/ci/push_docs_to_repo.py doc/build/html \ @@ -74,7 +112,7 @@ jobs: --message "Docs build of $CIRCLE_SHA1" \ --force else - echo "Not on the master branch; skipping deployment" + echo "Not on the main branch; skipping deployment" fi - add_ssh_keys: @@ -93,7 +131,7 @@ jobs: - run: name: deploy neps command: | - if [ "${CIRCLE_BRANCH}" == "master" ]; then + if [ "${CIRCLE_BRANCH}" == "main" ]; then touch doc/neps/_build/html/.nojekyll ./tools/ci/push_docs_to_repo.py doc/neps/_build/html \ @@ -103,5 +141,5 @@ jobs: --message "Docs build of $CIRCLE_SHA1" \ --force else - echo "Not on the master branch; skipping deployment" + echo "Not on the main branch; skipping deployment" fi diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000000..60b1066bcff7 --- /dev/null +++ b/.clang-format @@ -0,0 +1,37 @@ +# A clang-format style that approximates Python's PEP 7 +# Useful for IDE integration +# +# Based on Paul Ganssle's version at +# https://gist.github.com/pganssle/0e3a5f828b4d07d79447f6ced8e7e4db +# and modified for NumPy +BasedOnStyle: Google +AlignAfterOpenBracket: Align +AllowShortEnumsOnASingleLine: false +AllowShortIfStatementsOnASingleLine: false +AlwaysBreakAfterReturnType: TopLevel +BreakBeforeBraces: Stroustrup +ColumnLimit: 79 +ContinuationIndentWidth: 8 +DerivePointerAlignment: false +IndentWidth: 4 +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^[<"](Python|structmember|pymem)\.h' + Priority: -3 + CaseSensitive: true + - Regex: '^"numpy/' + Priority: -2 + - Regex: '^"(npy_pycompat|npy_config)' + Priority: -1 + - Regex: '^"[[:alnum:]_.]+"' + Priority: 1 + - Regex: '^<[[:alnum:]_.]+"' + Priority: 2 +Language: Cpp +PointerAlignment: Right +ReflowComments: true +SpaceBeforeParens: ControlStatements +SpacesInParentheses: false +StatementMacros: [PyObject_HEAD, PyObject_VAR_HEAD, PyObject_HEAD_EXTRA] +TabWidth: 4 +UseTab: Never diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 000000000000..165b3099df18 --- /dev/null +++ b/.codecov.yml @@ -0,0 +1,14 @@ +codecov: + notify: + require_ci_to_pass: no + after_n_builds: 1 +coverage: + status: + project: + default: + informational: true + patch: + default: + informational: true + changes: false +comment: off diff --git a/.coveragerc b/.coveragerc index 1f61c25a4e0c..9048b9cc427c 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,3 +1,4 @@ [run] branch = True include = */numpy/* +disable_warnings = include-ignored diff --git a/.ctags.d b/.ctags.d new file mode 100644 index 000000000000..60f7d6c65f13 --- /dev/null +++ b/.ctags.d @@ -0,0 +1 @@ +--langmaps=c:+.src diff --git a/.gitattributes b/.gitattributes index dad6dde37cd0..911db2b72f0b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,113 @@ -# Numerical data files -numpy/lib/tests/data/*.npy binary +# Highlight our custom templating language as C, since it's hopefully better +# than nothing. This also affects repo statistics. +*.c.src text linguist-language=C +*.inc.src text linguist-language=C +*.h.src text linguist-language=C +*.pyx.in text linguist-language=Python +*.pxd.in text linguist-language=Python + +# Mark some files as vendored +numpy/linalg/lapack_lite/f2c.c linguist-vendored +numpy/linalg/lapack_lite/f2c.h linguist-vendored +tools/npy_tempita/* linguist-vendored +numpy/core/include/numpy/libdivide/* linguist-vendored +numpy/core/src/umath/svml/* linguist-vendored + +# Mark some files as generated +numpy/linalg/lapack_lite/f2c_*.c linguist-generated +numpy/linalg/lapack_lite/lapack_lite_names.h linguist-generated +numpy/_version.py linguist-generated + +# versioneer config +numpy/_version.py export-subst + +# Configuration files +*.ini text +*.cfg text +./MANIFEST.in text +./numpy/core/npymath.ini.in text +./numpy/core/mlib.ini.in text +./site.cfg.example text + +# Python sources +*.py text diff=python +*.pxd text diff=python +*.pyx text diff=python +*.pyi text diff=python + +# C/C++ sources +*.c text diff=c +*.h text diff=c +*.cc text diff=cpp +*.cxx text diff=cpp +*.cpp text diff=cpp +*.hpp text diff=cpp +*.hh text diff=cpp + +# Fortran sources +*.f text diff=fortran +*.for text diff=fortran +*.f90 text diff=fortran +*.f95 text diff=fortran +*.f03 text diff=fortran + +# JavaScript +*.js text + +# F2py +./doc/source/f2py/*.pyf text +./doc/source/f2py/*.dat text +./numpy/f2py/tests/src/module_data/mod.mod binary + +# Documents +*.md text diff=markdown +*.txt text +*.rst text +*.pdf binary +*.css text diff=css +*.html text diff=html + +# Graphics +*.png binary +*.ico binary +*.dia binary +*.gif binary +*.odg binary +*.fig text +*.svg text +# SVG is treated as an asset (binary) by default. If you want +# to treat it as binary, use the following line instead. +# *.svg binary + +# Scripts +*.sh text eol=lf +*.sed text +# These are explicitly windows files and should use crlf +*.bat text eol=crlf +*.cmd text eol=crlf + +# Serialisation +*.json text +*.toml text +*.xml text +*.yaml text +*.yml text + +# Data files +*.csv text +*.pkl binary +*.fits binary +*.npy binary +*.npz binary + +# Misc. +*.swg text +*.patch text +./doc/neps/index.rst.tmpl text +./benchmarks/asv_compare.conf.json.tpl text +./tools/swig/test/*.i text +./tools/gitpod/gitpod.Dockerfile text +./doc/source/dev/gitwash/git_links.inc text +./doc/source/reference/simd/*.inc text +./numpy/core/src/_simd/*.inc text diff=c -# Release notes, reduce number of conflicts. -doc/release/*.rst merge=union diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md new file mode 100644 index 000000000000..079098fae68a --- /dev/null +++ b/.github/CODE_OF_CONDUCT.md @@ -0,0 +1 @@ +NumPy has a Code of Conduct, please see: https://numpy.org/code-of-conduct diff --git a/CONTRIBUTING.md b/.github/CONTRIBUTING.md similarity index 57% rename from CONTRIBUTING.md rename to .github/CONTRIBUTING.md index e70585d0c52e..8f16950f765e 100644 --- a/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -8,7 +8,7 @@ also include a brief, self-contained code example that demonstrates the problem. If you are reporting a segfault please include a GDB traceback, which you can generate by following -[these instructions.](https://github.com/numpy/numpy/blob/master/doc/source/dev/development_environment.rst#debugging) +[these instructions.](https://github.com/numpy/numpy/blob/main/doc/source/dev/development_environment.rst#debugging) ## Contributing code @@ -16,12 +16,12 @@ Thanks for your interest in contributing code to numpy! + If this is your first time contributing to a project on GitHub, please read through our -[guide to contributing to numpy](http://docs.scipy.org/doc/numpy/dev/index.html) +[guide to contributing to numpy](https://numpy.org/devdocs/dev/index.html) + If you have contributed to other projects on GitHub you can go straight to our -[development workflow](http://docs.scipy.org/doc/numpy/dev/gitwash/development_workflow.html) +[development workflow](https://numpy.org/devdocs/dev/development_workflow.html) Either way, please be sure to follow our -[convention for commit messages](http://docs.scipy.org/doc/numpy/dev/gitwash/development_workflow.html#writing-the-commit-message). +[convention for commit messages](https://numpy.org/devdocs/dev/development_workflow.html#writing-the-commit-message). If you are writing new C code, please follow the style described in ``doc/C_STYLE_GUIDE``. @@ -29,3 +29,12 @@ If you are writing new C code, please follow the style described in Suggested ways to work on your development version (compile and run the tests without interfering with system packages) are described in ``doc/source/dev/development_environment.rst``. + +### A note on feature enhancements/API changes + +If you are interested in adding a new feature to NumPy, consider +submitting your feature proposal to the [mailing list][mail], +which is the preferred forum for discussing new features and +API changes. + +[mail]: https://mail.python.org/mailman/listinfo/numpy-discussion diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000000..8c3502443d19 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +github: [numfocus] +tidelift: pypi/numpy +custom: https://numpy.org/about#donate diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml new file mode 100644 index 000000000000..29d0d0dd0f22 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -0,0 +1,51 @@ +name: Bug report +description: Report a bug. For security vulnerabilities see Report a security vulnerability in the templates. +title: "BUG: " +labels: [00 - Bug] + +body: +- type: markdown + attributes: + value: > + Thank you for taking the time to file a bug report. Before creating a new + issue, please make sure to take a few minutes to check the issue tracker + for existing issues about the bug. + +- type: textarea + attributes: + label: "Describe the issue:" + validations: + required: true + +- type: textarea + attributes: + label: "Reproduce the code example:" + description: > + A short code example that reproduces the problem/missing feature. It + should be self-contained, i.e., can be copy-pasted into the Python + interpreter or run as-is via `python myproblem.py`. + placeholder: | + import numpy as np + << your code here >> + render: python + validations: + required: true + +- type: textarea + attributes: + label: "Error message:" + description: > + Please include full error message, if any. + If you are reporting a segfault please include a GDB traceback, + which you can generate by following + [these instructions](https://github.com/numpy/numpy/blob/main/doc/source/dev/development_environment.rst#debugging). + placeholder: | + << Full traceback starting from `Traceback: ...` >> + render: shell + +- type: textarea + attributes: + label: "NumPy/Python version information:" + description: Output from `import sys, numpy; print(numpy.__version__, sys.version)`. + validations: + required: true \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000000..adfff81bd004 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,7 @@ +contact_links: + - name: Question/Help/Support + url: https://numpy.org/gethelp/ + about: "If you have a question, please look at the listed resources available on the website." + - name: Development-related matters + url: https://numpy.org/community/ + about: "If you would like to discuss development-related matters or need help from the NumPy team, see our community's communication channels." diff --git a/.github/ISSUE_TEMPLATE/documentation.yml b/.github/ISSUE_TEMPLATE/documentation.yml new file mode 100644 index 000000000000..afff9ab5f1cd --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation.yml @@ -0,0 +1,23 @@ +name: Documentation +description: Report an issue related to the NumPy documentation. +title: "DOC: " +labels: [04 - Documentation] + +body: +- type: textarea + attributes: + label: "Issue with current documentation:" + description: > + Please make sure to leave a reference to the document/code you're + referring to. You can also check the development version of the + documentation and see if this issue has already been addressed at + https://numpy.org/devdocs. + +- type: textarea + attributes: + label: "Idea or request for content:" + description: > + Please describe as clearly as possible what topics you think are missing + from the current documentation. Make sure to check + https://github.com/numpy/numpy-tutorials and see if this issue might be + more appropriate there. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml new file mode 100644 index 000000000000..390c3d53bba2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.yml @@ -0,0 +1,22 @@ +name: Feature request +description: Check instructions for submitting your idea on the mailing list first. +title: "ENH: " + +body: +- type: markdown + attributes: + value: > + If you're looking to request a new feature or change in functionality, + including adding or changing the meaning of arguments to an existing + function, please post your idea on the + [numpy-discussion mailing list](https://mail.python.org/mailman/listinfo/numpy-discussion) + to explain your reasoning in addition to opening an issue or pull request. + You can also check out our + [Contributor Guide](https://github.com/numpy/numpy/blob/main/doc/source/dev/index.rst) + if you need more information. + +- type: textarea + attributes: + label: "Proposed new feature or change:" + validations: + required: true \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/post-install.yml b/.github/ISSUE_TEMPLATE/post-install.yml new file mode 100644 index 000000000000..a5fa07be0279 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/post-install.yml @@ -0,0 +1,31 @@ +name: Post-install/importing issue +description: Report an issue if you have trouble importing or using NumPy after installation. +title: "" +labels: [32 - Installation] + +body: +- type: textarea + attributes: + label: "Steps to reproduce:" + description: > + Please describe the installation method (e.g. building from source, + Anaconda, pip), your OS and NumPy/Python version information. + validations: + required: true + +- type: textarea + attributes: + label: "Error message:" + description: > + Please include full error message, if any. + If you are reporting a segfault please include a GDB traceback, + which you can generate by following + [these instructions](https://github.com/numpy/numpy/blob/main/doc/source/dev/development_environment.rst#debugging). + placeholder: | + << Full traceback starting from `Traceback: ...` >> + render: shell + +- type: textarea + attributes: + label: "Additional information:" + description: Please add any additional information that could help us diagnose the problem better. \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000000..704d2d16fd9c --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,16 @@ + diff --git a/.github/actions/action.yml b/.github/actions/action.yml new file mode 100644 index 000000000000..43a7d0c7ac03 --- /dev/null +++ b/.github/actions/action.yml @@ -0,0 +1,28 @@ +name: DoTheWork +description: "checkout repo, build and run tests" +runs: + using: composite + steps: + - name: Show env + shell: bash + run: | + echo NPY_RELAXED_STRIDES_DEBUG $NPY_RELAXED_STRIDES_DEBUG + echo NPY_RELAXED_STRIDES_CHECKING $NPY_RELAXED_STRIDES_CHECKING + echo CHECK_BLAS $CHECK_BLAS + echo DOWNLOAD_OPENBLAS $DOWNLOAD_OPENBLAS + echo USE_DEBUG $USE_DEBUG + echo NPY_USE_BLAS_ILP64 $NPY_USE_BLAS_ILP64 + echo NUMPY_EXPERIMENTAL_ARRAY_FUNCTION $NUMPY_EXPERIMENTAL_ARRAY_FUNCTION + echo USE_ASV $USE_ASV + echo PATH $PATH + echo python `which python` + python -c "import sys; print(sys.version)" + + - name: BeforeInstall + shell: bash + run: ./tools/travis-before-install.sh + + - name: Test + shell: bash + run: ./tools/travis-test.sh + diff --git a/.github/pr-prefix-labeler.yml b/.github/pr-prefix-labeler.yml new file mode 100644 index 000000000000..ab7ad9d28b9b --- /dev/null +++ b/.github/pr-prefix-labeler.yml @@ -0,0 +1,14 @@ +"API": "30 - API" +"BENCH": "28 - Benchmark" +"BUG": "00 - Bug" +"BLD": "36 - Build" +"DEP": "07 - Deprecation" +"DEV": "16 - Development" +"DOC": "04 - Documentation" +"ENH": "01 - Enhancement" +"MAINT": "03 - Maintenance" +"REV": "34 - Reversion" +"STY": "03 - Maintenance" +"TST": "05 - Testing" +"REL": "14 - Release" +"WIP": "25 - WIP" diff --git a/.github/workflows/build_test.yml b/.github/workflows/build_test.yml new file mode 100644 index 000000000000..620d9c1efd36 --- /dev/null +++ b/.github/workflows/build_test.yml @@ -0,0 +1,311 @@ +name: Build_Test + +on: + push: + branches: + - main + - maintenance/** + pull_request: + branches: + - main + - maintenance/** + +defaults: + run: + shell: bash + +env: + DOWNLOAD_OPENBLAS: 1 + PYTHON_VERSION: 3.8 + +jobs: + lint: + if: "github.repository == 'numpy/numpy' && github.ref != 'refs/heads/main' && !contains(github.event.head_commit.message, '[ci skip]') && !contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[skip github]')" + runs-on: ubuntu-latest + continue-on-error: true + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + fetch-depth: 0 + - uses: actions/setup-python@v2 + with: + python-version: ${{ env.PYTHON_VERSION }} + - name: Install linter requirements + run: + python -m pip install -r linter_requirements.txt + - name: Run linter on PR diff + run: + python tools/linter.py --branch origin/${{ github.base_ref }} + + smoke_test: + if: "github.repository == 'numpy/numpy' && !contains(github.event.head_commit.message, '[ci skip]') && !contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[skip github]')" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + fetch-depth: 0 + - uses: actions/setup-python@v2 + with: + python-version: ${{ env.PYTHON_VERSION }} + - uses: ./.github/actions + + basic: + needs: [smoke_test] + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.9", "3.10"] + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + fetch-depth: 0 + - uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - uses: ./.github/actions + + debug: + needs: [smoke_test] + runs-on: ubuntu-20.04 + env: + USE_DEBUG: 1 + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + fetch-depth: 0 + - uses: actions/setup-python@v2 + with: + python-version: ${{ env.PYTHON_VERSION }} + - uses: ./.github/actions + + blas64: + needs: [smoke_test] + runs-on: ubuntu-latest + env: + NPY_USE_BLAS_ILP64: 1 + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + fetch-depth: 0 + - uses: actions/setup-python@v2 + with: + python-version: ${{ env.PYTHON_VERSION }} + - uses: ./.github/actions + + full: + needs: [smoke_test] + runs-on: ubuntu-18.04 + env: + USE_WHEEL: 1 + RUN_FULL_TESTS: 1 + RUN_COVERAGE: 1 + INSTALL_PICKLE5: 1 + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + fetch-depth: 0 + - uses: actions/setup-python@v2 + with: + python-version: ${{ env.PYTHON_VERSION }} + - uses: ./.github/actions + + benchmark: + needs: [smoke_test] + runs-on: ubuntu-latest + env: + PYTHONOPTIMIZE: 2 + BLAS: None + LAPACK: None + ATLAS: None + NPY_BLAS_ORDER: mkl,blis,openblas,atlas,blas + NPY_LAPACK_ORDER: MKL,OPENBLAS,ATLAS,LAPACK + USE_ASV: 1 + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + fetch-depth: 0 + - uses: actions/setup-python@v2 + with: + python-version: ${{ env.PYTHON_VERSION }} + - uses: ./.github/actions + + no_relaxed_strides: + needs: [smoke_test] + runs-on: ubuntu-latest + env: + NPY_RELAXED_STRIDES_CHECKING: 0 + CHECK_BLAS: 1 + NPY_USE_BLAS_ILP64: 1 + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + fetch-depth: 0 + - uses: actions/setup-python@v2 + with: + python-version: ${{ env.PYTHON_VERSION }} + - uses: ./.github/actions + + use_wheel: + needs: [smoke_test] + runs-on: ubuntu-latest + env: + USE_WHEEL: 1 + NPY_RELAXED_STRIDES_DEBUG: 1 + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + fetch-depth: 0 + - uses: actions/setup-python@v2 + with: + python-version: ${{ env.PYTHON_VERSION }} + - uses: ./.github/actions + + no_array_func: + needs: [smoke_test] + runs-on: ubuntu-latest + env: + NUMPY_EXPERIMENTAL_ARRAY_FUNCTION: 0 + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + fetch-depth: 0 + - uses: actions/setup-python@v2 + with: + python-version: ${{ env.PYTHON_VERSION }} + - uses: ./.github/actions + + no_openblas: + needs: [smoke_test] + runs-on: ubuntu-latest + env: + BLAS: None + LAPACK: None + ATLAS: None + DOWNLOAD_OPENBLAS: '' + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + fetch-depth: 0 + - uses: actions/setup-python@v2 + with: + python-version: ${{ env.PYTHON_VERSION }} + - uses: ./.github/actions + + pypy38: + needs: [smoke_test] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + fetch-depth: 0 + - uses: actions/setup-python@v2 + with: + python-version: pypy-3.8-v7.3.7 + - uses: ./.github/actions + + sdist: + needs: [smoke_test] + runs-on: ubuntu-latest + env: + USE_SDIST: 1 + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + fetch-depth: 0 + - uses: actions/setup-python@v2 + with: + python-version: ${{ env.PYTHON_VERSION }} + - uses: ./.github/actions + + armv7_simd_test: + needs: [smoke_test] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + fetch-depth: 0 + - name: Initialize binfmt_misc for qemu-user-static + run: | + docker run --rm --privileged multiarch/qemu-user-static --reset -p yes + - name: Creates new container + run: | + # use x86_64 cross-compiler to speed up the build + sudo apt update + sudo apt install -y gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf + docker run --name the_container --interactive -v /:/host arm32v7/ubuntu:latest /bin/bash -c " + apt update && + apt install -y git python3 python3-dev python3-pip && + pip3 install cython==0.29.24 setuptools\<49.2.0 hypothesis==6.23.3 pytest==6.2.5 && + ln -s /host/lib64 /lib64 && + ln -s /host/lib/x86_64-linux-gnu /lib/x86_64-linux-gnu && + ln -s /host/usr/arm-linux-gnueabihf /usr/arm-linux-gnueabihf && + rm -rf /usr/lib/gcc/arm-linux-gnueabihf && ln -s /host/usr/lib/gcc-cross/arm-linux-gnueabihf /usr/lib/gcc/arm-linux-gnueabihf && + rm -f /usr/bin/arm-linux-gnueabihf-gcc && ln -s /host/usr/bin/arm-linux-gnueabihf-gcc /usr/bin/arm-linux-gnueabihf-gcc && + rm -f /usr/bin/arm-linux-gnueabihf-g++ && ln -s /host/usr/bin/arm-linux-gnueabihf-g++ /usr/bin/arm-linux-gnueabihf-g++ && + rm -f /usr/bin/arm-linux-gnueabihf-ar && ln -s /host/usr/bin/arm-linux-gnueabihf-ar /usr/bin/arm-linux-gnueabihf-ar && + rm -f /usr/bin/arm-linux-gnueabihf-as && ln -s /host/usr/bin/arm-linux-gnueabihf-as /usr/bin/arm-linux-gnueabihf-as && + rm -f /usr/bin/arm-linux-gnueabihf-ld && ln -s /host/usr/bin/arm-linux-gnueabihf-ld /usr/bin/arm-linux-gnueabihf-ld && + rm -f /usr/bin/arm-linux-gnueabihf-ld.bfd && ln -s /host/usr/bin/arm-linux-gnueabihf-ld.bfd /usr/bin/arm-linux-gnueabihf-ld.bfd + " + docker commit the_container the_container + - name: Build + run: | + sudo docker run --name the_build --interactive -v $(pwd):/numpy -v /:/host the_container /bin/bash -c " + uname -a && + gcc --version && + g++ --version && + python3 --version && + cd /numpy && python3 setup.py install + " + docker commit the_build the_build + - name: Run SIMD Tests + run: | + docker run --rm --interactive -v $(pwd):/numpy the_build /bin/bash -c " + cd /numpy && python3 runtests.py -n -v -- -k test_simd + " + + sde_simd_avx512_test: + # Intel Software Development Emulator (SDE) is used to run a given program + # on a specific instruction set architecture and capture various performance details. + # see https://www.intel.com/content/www/us/en/developer/articles/tool/software-development-emulator.html + needs: [smoke_test] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + fetch-depth: 0 + - uses: actions/setup-python@v2 + with: + python-version: ${{ env.PYTHON_VERSION }} + - name: Install Intel SDE + run: | + curl -o /tmp/sde.tar.bz2 https://www.intel.com/content/dam/develop/external/us/en/documents/downloads/sde-external-8.69.1-2021-07-18-lin.tar.bz2 + mkdir /tmp/sde && tar -xvf /tmp/sde.tar.bz2 -C /tmp/sde/ + sudo mv /tmp/sde/* /opt/sde && sudo ln -s /opt/sde/sde64 /usr/bin/sde + - name: Install dependencies + run: python -m pip install -r test_requirements.txt + - name: Build + run: python setup.py build + --simd-test="\$werror AVX512F AVX512_KNL AVX512_KNM AVX512_SKX AVX512_CLX AVX512_CNL AVX512_ICL" + install + # KNM implies KNL + - name: Run SIMD tests (Xeon PHI) + run: sde -knm -- python runtests.py -n -v -- -k test_simd + # ICL implies SKX, CLX and CNL + - name: Run SIMD tests (Ice Lake) + run: sde -icl -- python runtests.py -n -v -- -k test_simd diff --git a/.github/workflows/circleci.yml b/.github/workflows/circleci.yml new file mode 100644 index 000000000000..de02ac6d330d --- /dev/null +++ b/.github/workflows/circleci.yml @@ -0,0 +1,12 @@ +on: [status] +jobs: + circleci_artifacts_redirector_job: + runs-on: ubuntu-latest + name: Run CircleCI artifacts redirector + steps: + - name: GitHub Action step + uses: larsoner/circleci-artifacts-redirector-action@master + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + artifact-path: 0/doc/build/html/index.html + circleci-jobs: build diff --git a/.github/workflows/cygwin.yml b/.github/workflows/cygwin.yml new file mode 100644 index 000000000000..78fa25995f3c --- /dev/null +++ b/.github/workflows/cygwin.yml @@ -0,0 +1,70 @@ +name: Test on Cygwin +on: + push: + branches: + - main + pull_request: + branches: + - main +jobs: + cygwin_build_test: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + fetch-depth: 0 + - name: Install Cygwin + uses: egor-tensin/setup-cygwin@v3 + with: + platform: x64 + install-dir: 'C:\tools\cygwin' + packages: > + python38-devel python38-zipp python38-importlib-metadata + python38-cython python38-pip python38-wheel python38-cffi + python38-pytz python38-setuptools python38-pytest + python38-hypothesis liblapack-devel libopenblas + gcc-fortran gcc-g++ git dash + - name: Set Windows PATH + uses: egor-tensin/cleanup-path@v1 + with: + dirs: 'C:\tools\cygwin\bin;C:\tools\cygwin\lib\lapack' + - name: Verify that bash is Cygwin bash + run: | + command bash + bash -c "uname -svrmo" + - name: Update with Cygwin git + # fetch-depth=0 above should make this short. + run: | + dash -c "which git; /usr/bin/git fetch --all -p" + - name: Verify python version + # Make sure it's the Cygwin one, not a Windows one + run: | + dash -c "which python3.8; /usr/bin/python3.8 --version -V" + - name: Build NumPy wheel + run: | + dash -c "/usr/bin/python3.8 -m pip install 'setuptools<49.2.0' pytest pytz cffi pickle5 importlib_metadata typing_extensions" + dash -c "/usr/bin/python3.8 -m pip install -r test_requirements.txt" + dash -c "/usr/bin/python3.8 setup.py bdist_wheel" + - name: Install new NumPy + run: | + bash -c "/usr/bin/python3.8 -m pip install dist/numpy-*cp38*.whl" + - name: Rebase NumPy compiled extensions + run: | + dash "tools/rebase_installed_dlls_cygwin.sh" 3.8 + - name: Run NumPy test suite + run: >- + dash -c "/usr/bin/python3.8 runtests.py -n -vv" + - name: Upload wheel if tests fail + uses: actions/upload-artifact@v2 + if: failure() + with: + name: numpy-cygwin-wheel + path: dist/numpy-*cp38*.whl + - name: On failure check the extension modules + if: failure() + run: | + dash -c "/usr/bin/python3.8 -m pip show numpy" + dash -c "/usr/bin/python3.8 -m pip show -f numpy | grep .dll" + dash -c "/bin/tr -d '\r' list_dlls_unix.sh" + dash "list_dlls_unix.sh" 3.8 diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 000000000000..cc4950590af0 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,57 @@ +name: Build Base Docker Image + +on: + push: + branches: + - main + paths: + - 'environment.yml' + +jobs: + build: + name: Build base Docker image + runs-on: ubuntu-latest + environment: numpy-dev + if: "github.repository_owner == 'numpy' && !contains(github.event.head_commit.message, '[ci skip]') && !contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[skip github]')" + steps: + - name: Clone repository + uses: actions/checkout@v2 + - name: Lint Docker + uses: brpaz/hadolint-action@v1.2.1 + with: + dockerfile: ./tools/gitpod/Dockerfile + - name: Get refs + shell: bash + run: | + export raw_branch=${GITHUB_REF#refs/heads/} + echo "::set-output name=branch::${raw_branch//\//-}" + echo "::set-output name=date::$(date +'%Y%m%d')" + echo "::set-output name=sha8::$(echo ${GITHUB_SHA} | cut -c1-8)" + id: getrefs + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + - name: Cache Docker layers + uses: actions/cache@v2 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: ${{ runner.os }}-buildx- + - name: Login to Docker Hub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Build and push + id: docker_build + uses: docker/build-push-action@v2 + with: + context: "." + file: "./tools/gitpod/Dockerfile" + push: ${{ github.event_name != 'pull_request' }} + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache + tags: | + numpy/numpy-dev:${{ steps.getrefs.outputs.date }}-${{ steps.getrefs.outputs.branch}}-${{ steps.getrefs.outputs.sha8 }}, numpy/numpy-dev:latest + - name: Image digest + # Return details of the image build: sha and shell + run: echo ${{ steps.docker_build.outputs.digest }} diff --git a/.github/workflows/gitpod.yml b/.github/workflows/gitpod.yml new file mode 100644 index 000000000000..bbca928656df --- /dev/null +++ b/.github/workflows/gitpod.yml @@ -0,0 +1,57 @@ +name: Build Gitpod Docker image + +on: + push: + branches: + - main + +jobs: + build: + name: Build Gitpod Docker image + runs-on: ubuntu-latest + environment: numpy-dev + if: "github.repository_owner == 'numpy' && !contains(github.event.head_commit.message, '[ci skip]') && !contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[skip github]')" + steps: + - name: Clone repository + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Lint Docker + uses: brpaz/hadolint-action@v1.2.1 + with: + dockerfile: ./tools/gitpod/gitpod.Dockerfile + - name: Get refs + shell: bash + run: | + export raw_branch=${GITHUB_REF#refs/heads/} + echo "::set-output name=branch::${raw_branch//\//-}" + echo "::set-output name=date::$(date +'%Y%m%d')" + echo "::set-output name=sha8::$(echo ${GITHUB_SHA} | cut -c1-8)" + id: getrefs + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + - name: Cache Docker layers + uses: actions/cache@v2 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: ${{ runner.os }}-buildx- + - name: Login to Docker Hub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Build and push + id: docker_build + uses: docker/build-push-action@v2 + with: + context: "." + file: "./tools/gitpod/gitpod.Dockerfile" + push: ${{ github.event_name != 'pull_request' }} + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache + tags: | + numpy/numpy-gitpod:${{ steps.getrefs.outputs.date }}-${{ steps.getrefs.outputs.branch}}-${{ steps.getrefs.outputs.sha8 }}, numpy/numpy-gitpod:latest + - name: Image digest + # Return details of the image build: sha and shell + run: echo ${{ steps.docker_build.outputs.digest }} diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml new file mode 100644 index 000000000000..99db967b383b --- /dev/null +++ b/.github/workflows/labeler.yml @@ -0,0 +1,13 @@ +name: "Pull Request Labeler" +on: + pull_request_target: + types: [opened, synchronize, reopened, edited] + +jobs: + pr-labeler: + runs-on: ubuntu-latest + steps: + - name: Label the PR + uses: gerrymanoim/pr-prefix-labeler@v3 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml new file mode 100644 index 000000000000..3c382f8b32db --- /dev/null +++ b/.github/workflows/wheels.yml @@ -0,0 +1,104 @@ +# Workflow to build and test wheels. +# To work on the wheel building infrastructure on a fork, comment out: +# +# if: github.repository == 'numpy/numpy' +# +# in the get_commit_message job. Be sure to include [cd build] in your commit +# message to trigger the build. All files related to wheel building are located +# at tools/wheels/ +name: Wheel builder + +on: + schedule: + # Nightly build at 1:42 UTC + - cron: "42 1 * * *" + push: + pull_request: + workflow_dispatch: + +jobs: + get_commit_message: + name: Get commit message + runs-on: ubuntu-latest + if: github.repository == 'numpy/numpy' + outputs: + message: ${{ steps.commit_message.outputs.message }} + steps: + - name: Checkout numpy + uses: actions/checkout@v2 + # Gets the correct commit message for pull request + with: + ref: ${{ github.event.pull_request.head.sha }} + - name: Get commit message + id: commit_message + run: | + set -xe + COMMIT_MSG=$(git log --no-merges -1 --oneline) + echo "::set-output name=message::$COMMIT_MSG" + + build_wheels: + name: Build wheel for cp${{ matrix.python }}-${{ matrix.platform }} + needs: get_commit_message + if: >- + contains(needs.get_commit_message.outputs.message, '[wheel build]') || + github.event_name == 'schedule' || + github.event_name == 'workflow_dispatch' + runs-on: ${{ matrix.os }} + strategy: + # Ensure that a wheel builder finishes even if another fails + fail-fast: false + matrix: + include: + # manylinux builds + - os: ubuntu-latest + python: "38" + platform: manylinux_x86_64 + - os: ubuntu-latest + python: "39" + platform: manylinux_x86_64 + - os: ubuntu-latest + python: "310" + platform: manylinux_x86_64 + + # macos builds + - os: macos-latest + python: "38" + platform: macosx_x86_64 + - os: macos-latest + python: "39" + platform: macosx_x86_64 + - os: macos-latest + python: "310" + platform: macosx_x86_64 + + steps: + - name: Checkout numpy + uses: actions/checkout@v2 + with: + submodules: true + # versioneer.py requires the latest tag to be reachable. Here we + # fetch the complete history to get access to the tags. + # A shallow clone can work when the following issue is resolved: + # https://github.com/actions/checkout/issues/338 + fetch-depth: 0 + + - name: Build wheels + uses: pypa/cibuildwheel@v2.1.3 + env: + NPY_USE_BLAS_ILP64: 1 + CIBW_BUILD: cp${{ matrix.python }}-${{ matrix.platform }} + CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014 + CIBW_ENVIRONMENT_LINUX: CFLAGS='-std=c99 -fno-strict-aliasing' + LDFLAGS='-Wl,--strip-debug' + OPENBLAS64_=/usr/local + # MACOS linker doesn't support stripping symbols + CIBW_ENVIRONMENT_MACOS: CFLAGS='-std=c99 -fno-strict-aliasing' + OPENBLAS64_=/usr/local + CIBW_BUILD_VERBOSITY: 3 + CIBW_BEFORE_BUILD: bash {project}/tools/wheels/cibw_before_build.sh {project} + CIBW_BEFORE_TEST: pip install -r {project}/test_requirements.txt + CIBW_TEST_COMMAND: bash {project}/tools/wheels/cibw_test_command.sh {project} + + - uses: actions/upload-artifact@v2 + with: + path: ./wheelhouse/*.whl diff --git a/.gitignore b/.gitignore index fbdd4f784d94..52997523cdea 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ GRTAGS GSYMS GTAGS .cache +.mypy_cache/ # Compiled source # ################### @@ -74,6 +75,8 @@ doc/cdoc/build ./.shelf MANIFEST .cache +pip-wheel-metadata +.python-version # Paver generated files # ######################### @@ -99,11 +102,17 @@ Icon? .gdb_history ehthumbs.db Thumbs.db +.directory # pytest generated files # ########################## /.pytest_cache +# doc build generated files # +############################# +doc/source/savefig/ + + # Things specific to this project # ################################### numpy/core/__svn_version__.py @@ -112,9 +121,7 @@ numpy/__config__.py numpy/core/include/numpy/__multiarray_api.h numpy/core/include/numpy/__ufunc_api.h numpy/core/include/numpy/_numpyconfig.h -numpy/version.py site.cfg -setup.cfg .tox numpy/core/include/numpy/__multiarray_api.c numpy/core/include/numpy/__ufunc_api.c @@ -123,8 +130,15 @@ numpy/core/include/numpy/config.h numpy/core/include/numpy/multiarray_api.txt numpy/core/include/numpy/ufunc_api.txt numpy/core/lib/ +numpy/core/src/common/npy_binsearch.h +numpy/core/src/common/npy_cpu_features.c +numpy/core/src/common/npy_partition.h +numpy/core/src/common/npy_sort.h +numpy/core/src/common/templ_common.h +numpy/core/src/multiarray/_multiarray_tests.c numpy/core/src/multiarray/arraytypes.c numpy/core/src/multiarray/einsum.c +numpy/core/src/multiarray/einsum_sumprod.c numpy/core/src/multiarray/lowlevel_strided_loops.c numpy/core/src/multiarray/multiarray_tests.c numpy/core/src/multiarray/nditer_templ.c @@ -136,26 +150,74 @@ numpy/core/src/npysort/binsearch.c numpy/core/src/npysort/heapsort.c numpy/core/src/npysort/mergesort.c numpy/core/src/npysort/quicksort.c +numpy/core/src/npysort/radixsort.c numpy/core/src/npysort/selection.c +numpy/core/src/npysort/timsort.c numpy/core/src/npysort/sort.c numpy/core/src/private/npy_binsearch.h numpy/core/src/private/npy_partition.h numpy/core/src/private/templ_common.h +numpy/core/src/umath/_operand_flag_tests.c +numpy/core/src/umath/_rational_tests.c +numpy/core/src/umath/_struct_ufunc_tests.c +numpy/core/src/umath/_umath_tests.c numpy/core/src/umath/scalarmath.c numpy/core/src/umath/funcs.inc +numpy/core/src/umath/clip.[ch] numpy/core/src/umath/loops.[ch] +numpy/core/src/umath/matmul.[ch] numpy/core/src/umath/operand_flag_tests.c numpy/core/src/umath/simd.inc numpy/core/src/umath/struct_ufunc_test.c numpy/core/src/umath/test_rational.c numpy/core/src/umath/umath_tests.c +numpy/core/src/umath/loops_utils.h numpy/distutils/__config__.py numpy/linalg/umath_linalg.c -doc/source/reference/generated +doc/source/**/generated/ benchmarks/results +benchmarks/html benchmarks/env benchmarks/numpy +benchmarks/_asv_compare.conf.json # cythonized files cythonize.dat -numpy/random/mtrand/mtrand.c -numpy/random/mtrand/randint_helpers.pxi +numpy/random/_mtrand/_mtrand.c +numpy/random/*.c +numpy/random/legacy/*.c +numpy/random/_mtrand/randint_helpers.pxi +numpy/random/bounded_integers.pyx +numpy/random/bounded_integers.pxd +numpy/random/lib/npyrandom.lib +tools/swig/test/Array_wrap.cxx +tools/swig/test/Farray_wrap.cxx +tools/swig/test/Farray.py +tools/swig/test/Flat_wrap.cxx +tools/swig/test/Flat.py +tools/swig/test/Fortran_wrap.cxx +tools/swig/test/Fortran.py +tools/swig/test/Matrix_wrap.cxx +tools/swig/test/Matrix.py +tools/swig/test/Tensor_wrap.cxx +tools/swig/test/Tensor.py +tools/swig/test/Vector.py +tools/swig/test/Vector_wrap.cxx +tools/swig/test/Array.py + +# SIMD generated files # +################################### +# config headers of dispatchable sources +*.dispatch.h +# wrapped sources of dispatched targets, e.g. *.dispatch.avx2.c +*.dispatch.*.c +# _simd module +numpy/core/src/_simd/_simd.dispatch.c +numpy/core/src/_simd/_simd_data.inc +numpy/core/src/_simd/_simd_inc.h +# umath module +numpy/core/src/umath/loops_unary_fp.dispatch.c +numpy/core/src/umath/loops_arithm_fp.dispatch.c +numpy/core/src/umath/loops_arithmetic.dispatch.c +numpy/core/src/umath/loops_trigonometric.dispatch.c +numpy/core/src/umath/loops_exponent_log.dispatch.c +numpy/core/src/umath/loops_umath_fp.dispatch.c diff --git a/.gitmodules b/.gitmodules index 1b0706f658e2..1ea274daf3b9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ -[submodule "doc/scipy-sphinx-theme"] - path = doc/scipy-sphinx-theme - url = https://github.com/scipy/scipy-sphinx-theme.git -[submodule "doc/sphinxext"] - path = doc/sphinxext - url = https://github.com/numpy/numpydoc.git +[submodule "doc/source/_static/scipy-mathjax"] + path = doc/source/_static/scipy-mathjax + url = https://github.com/scipy/scipy-mathjax.git +[submodule "numpy/core/src/umath/svml"] + path = numpy/core/src/umath/svml + url = https://github.com/numpy/SVML.git diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 000000000000..f9c35fd9bcf5 --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,62 @@ +# Rebuilding NumPy on init - rather than on prebuild: this ensures +# that even forks do have a usable freshly built NumPy +# Might delegate this later to prebuild with Q2 improvements on gitpod +# https://www.gitpod.io/docs/config-start-tasks/#configuring-the-terminal +# ------------------------------------------------------------------------- + +image: numpy/numpy-gitpod:latest +tasks: + - name: Prepare development + init: | + mkdir -p .vscode + cp tools/gitpod/settings.json .vscode/settings.json + conda activate numpy-dev + python setup.py build_ext --inplace + echo "🛠 Completed rebuilding NumPy!! 🛠 " + echo "📖 Building docs 📖 " + cd doc + make html + echo "✨ Pre-build complete! You can close this terminal ✨ " + + +# -------------------------------------------------------- +# exposing ports for liveserve +ports: + - port: 5500 + onOpen: notify + +# -------------------------------------------------------- +# some useful extensions to have +vscode: + extensions: + - eamodio.gitlens + - njpwerner.autodocstring + - lextudio.restructuredtext + - ritwickdey.liveserver + - ms-python.python + - yzhang.markdown-all-in-one + - bungcip.better-toml + - mhutchie.git-graph + +# -------------------------------------------------------- +# using prebuilds for the container - note: atm this only +# works for the NumPy repo +# With this configuration the prebuild will happen on push to master +github: + prebuilds: + # enable for main/default branch + master: true + # enable for other branches (defaults to false) + branches: false + # enable for pull requests coming from this repo (defaults to true) + pullRequests: false + # enable for pull requests coming from forks (defaults to false) + pullRequestsFromForks: false + # add a check to pull requests (defaults to true) + addCheck: false + # add a "Review in Gitpod" button as a comment to pull requests (defaults to false) + addComment: false + # add a "Review in Gitpod" button to the pull request's description (defaults to false) + addBadge: false + # add a label once the prebuild is ready to pull requests (defaults to false) + addLabel: false diff --git a/.hadolint.yaml b/.hadolint.yaml new file mode 100644 index 000000000000..0188ba2cf627 --- /dev/null +++ b/.hadolint.yaml @@ -0,0 +1,7 @@ +--- +ignored: + - DL3006 + - DL3008 + - SC2016 + - DL3004 + - DL3007 \ No newline at end of file diff --git a/.lgtm.yml b/.lgtm.yml new file mode 100644 index 000000000000..242afd59708a --- /dev/null +++ b/.lgtm.yml @@ -0,0 +1,25 @@ +path_classifiers: + library: + - tools + - numpy/_version.py + generated: + # The exports defined in __init__.py are defined in the Cython module + # np.random.mtrand. By excluding this file we suppress a number of + # "undefined export" alerts + - numpy/random/__init__.py + +extraction: + python: + python_setup: + requirements: + - cython>=0.29 + cpp: + index: + build_command: + - python3 setup.py build + after_prepare: + - pip3 install --upgrade --user cython + - export PATH="$HOME/.local/bin:$PATH" + +queries: + - include: py/file-not-closed diff --git a/.mailmap b/.mailmap index b4e67747bff1..abd7b31eaaad 100644 --- a/.mailmap +++ b/.mailmap @@ -8,171 +8,496 @@ # This file is up-to-date if the command git log --format="%aN <%aE>" | sort -u # gives no duplicates. -Aaron Baecker abaecker -Abdul Muneer abdulmuneer -Adam Ginsburg Adam Ginsburg -Albert Jornet Puig jurnix -Alexander Belopolsky Alexander Belopolsky -Alexander Shadchin Alexandr Shadchin -Alex Griffing alex -Alex Griffing argriffing -Alex Griffing argriffing -Alex Griffing argriffing -Alexander Belopolsky Alexander Belopolsky -Alexander Shadchin shadchin -Allan Haldane ahaldane +@8bitmp3 <19637339+8bitmp3@users.noreply.github.com> +@DWesl <22566757+DWesl@users.noreply.github.com> +@Endolith +@Illviljan <14371165+Illviljan@users.noreply.github.com> +@LSchroefl <65246829+LSchroefl@users.noreply.github.com> +@Lbogula +@Lisa <34400837+lyzlisa@users.noreply.github.com> +@Patrick <39380924+xamm@users.noreply.github.com> +@Scian <65375075+hoony6134@users.noreply.github.com> +@h-vetinari +@h6197627 <44726212+h6197627@users.noreply.github.com> +@jbCodeHub +@legoffant <58195095+legoffant@users.noreply.github.com> +@luzpaz +@luzpaz +@sfolje0 +@spacescientist +@tautaus +@xoviat <49173759+xoviat@users.noreply.github.com> +@xoviat <49173759+xoviat@users.noreply.github.com> +@yan-wyb +@yetanothercheer +Aaron Baecker +Aarthi Agurusa +Andrei Batomunkuev +Ajay DS +Ajay DS +Alan Fontenot +Alan Fontenot <36168460+logeaux@users.noreply.github.com> +Abdul Muneer +Abhilash Barigidad +Abhilash Barigidad <64172584+abhilash42@users.noreply.github.com> +Abhinav Reddy +Abel Aoun +Adam Ginsburg +Aerik Pawson <45904740+aerikpawson@users.noreply.github.com> +Ahmet Can Solak +Albert Jornet Puig +Alberto Rubiales +Alex Rockhill +Alex Griffing +Alex Griffing +Alex Griffing +Alex Henrie +Alex Rogozhnikov +Alex Thomas +Alexander Belopolsky +Alexander Belopolsky +Alexander Belopolsky +Alexander Belopolsky sasha +Alexander Hunt +Alexander Jung +Alexander Shadchin +Alexander Shadchin +Alizé Papp <68250865+alize-papp@users.noreply.github.com> +Allan Haldane +Al-Baraa El-Hag <48454648+a-elhag@users.noreply.github.com> Alok Singhal Alok Singhal -Amir Sarabadani amir -Anatoly Techtonik anatoly techtonik -Andrei Kucharavy chiffa -Anne Archibald aarchiba -Anne Archibald Anne Archibald -Anže Starič astaric -Aron Ahmadia ahmadia -Arun Persaud Arun Persaud -Åsmund Hjulstad Åsmund Hjulstad -Auke Wiggers auke -Badhri Narayanan Krishnakumar badhrink -Behzad Nouri behzad nouri -Benjamin Root Ben Root +Alyssa Quek +Ankit Dwivedi +Ankit Dwivedi +Amir Sarabadani +Anas Khan +Anatoly Techtonik +Andras Deak +Andrea Olivo +Andrea Pattori +Andrea Sangalli <53617841+and-sang@users.noreply.github.com> +Andreas Klöckner +Andreas Schwab +Andrei Kucharavy +Andrew Lawson +Anirudh Subramanian +Anne Archibald +Anne Archibald +Anne Bonner <35413198+bonn0062@users.noreply.github.com> +Anthony Vo <43098273+anthonyhvo12@users.noreply.github.com> +Antoine Pitrou +Anže Starič +Arfy Slowy +Aron Ahmadia +Arun Palaniappen +Arun Persaud +Ashutosh Singh +Ashutosh Singh <55102089+Ashutosh619-sudo@users.noreply.github.com> +Åsmund Hjulstad +Auke Wiggers +Badhri Narayanan Krishnakumar +Bangcheng Yang +Bhargav V <12525622+brpy@users.noreply.github.com> +Bas van Beek <43369155+BvB93@users.noreply.github.com> +Behzad Nouri +Ben Nathanson +Benjamin Root Benjamin Root weathergod -Bertrand Lefebvre bertrand -Bertrand Lefebvre Bertrand -Brett R Murphy brettrmurphy +Bernardt Duvenhage +Bernie Gray +Bertrand Lefebvre +Bharat Raghunathan +Bharat Raghunathan +Bob Eldering +Brent Brewington +Brett R Murphy +Brigitta Sipocz +Brian Soto +Brian Soto +Brian Soto Bryan Van de Ven Bryan Van de Ven Bryan Van de Ven Bryan Van de Ven -Carl Kleffner carlkl -Chris Burns chris.burns -Chris Kerr Chris Kerr -Christopher Hanley chanley +Bui Duc Minh <41239569+Mibu287@users.noreply.github.com> +Carl Kleffner +Carl Leake +Charles Stern <62192187+cisaacstern@users.noreply.github.com> +Chiara Marmo +Chiara Marmo +Chiara Marmo +Chris Barker +Chris Burns +Chris Fu (傅立业) <17433201@qq.com> +Chris Holland <41524756+ChrisAHolland@users.noreply.github.com> +Chris Kerr +Chris Vavaliaris +Christian Clauss +Christopher Dahlin +Christopher Hanley +Christoph Gohlke +Christoph Gohlke Christoph Gohlke cgholke -Christoph Gohlke cgohlke -Christoph Gohlke Christolph Gohlke -Daniel da Silva Daniel da Silva -Daniel da Silva Daniel da Silva -Daniel J Farrell danieljfarrell +Chunlin Fang +Chunlin Fang +Chunlin Fang <834352945@qq.com> +Colin Snyder <8csnyder@gmail.com> <47012605+colinsnyder@users.noreply.github.com> +Constanza Fierro +Daniel B Allan +Daniel da Silva +Daniel da Silva +Daniel Hrisca +Daniel J Farrell +Daniel Montes <53720019+Aerysv@users.noreply.github.com> +Daniel Müllner Daniel Müllner Daniel -Daniel Müllner dmuellner -Daniel Rasmussen drasmuss +Daniel Rasmussen +Daniel G. A. Smith +Daniel G. A. Smith +Dario Mory +David Badnar +David Cortes David Huard dhuard -David M Cooke cookedm -David Nicholson davidjn -David Ochoa ochoadavid -Derek Homeier Derek Homeier -Derek Homeier Derek Homeir -Derek Homeier Derek Homier -Egor Zindy zindy -Endolith +David M Cooke +David Nicholson +David Ochoa +David Pitchford +Davide Dal Bosco <62077652+davidedalbosco@users.noreply.github.com> +Dawid Zych +Dennis Zollo +Derek Homeier +Derek Homeier +Derek Homeier +Derrick Williams +Dima Pasechnik +Dima Pasechnik +Dmitriy Shalyga +Dustan Levenstein <43019642+dustanlevenstein@users.noreply.github.com> +Dylan Cutler +Ed Schofield +Egor Zindy +Elliott M. Forney +Erik M. Bray +Erik M. Bray +Erik M. Bray Eric Fode Eric Fode -Eric Quintero e-q -Ernest N. Mamikonyan mamikony +Eric Quintero +Ernest N. Mamikonyan +Eskild Eriksen +Eskild Eriksen <42120229+iameskild@users.noreply.github.com> +Eskild Eriksen +Etienne Guesnet <51407514+EGuesnet@users.noreply.github.com> +Eva Jau Evgeni Burovski Evgeni Burovski -Evgeny Toder eltjpm -Fernando Perez Fernando Perez +Evgeny Toder +Fernando Perez +Filip Trojan +François Le Lay +Frank Breitling Friedrich Dunne dunneff Frederic Bastien Frederic -Gael Varoquaux GaelVaroquaux -Gerrit Holl Gerrit Holl -Giuseppe Venturini ggventurini -Golnaz Irannejad golnazir -Gopal Singh Meena gopalmeena -Greg Knoll gkBCCN -Greg Yang eulerreich -Greg Young gfyoung -Greg Young gfyoung -Han Genuit 87 -Han Genuit hangenuit@gmail.com -Han Genuit Han +FX Coudert +Gael Varoquaux +Gagandeep Singh +Gerrit Holl +Gerrit Holl +Giuseppe Venturini +Golnaz Irannejad +Gopal Singh Meena +Greg Knoll +Greg Yang +Greg Young +Greg Young +Gregory R. Lee +Gregory R. Lee +Guo Ci guoci +Guo Shuai +Hameer Abbasi +Han Genuit Hanno Klemm hklemm -Hemil Desai hemildesai -Irvin Probst I--P -Jaime Fernandez Jaime Fernandez -Jaime Fernandez jaimefrio -Jaime Fernandez Jaime +Helder Oliveira +Hemil Desai +Himanshu +Hiroyuki V. Yamazaki +Hugo van Kemenade +I-Shen Leong +Imen Rajhi +Inessa Pawson +Irina Maria Mocan <28827042+IrinaMaria@users.noreply.github.com> +Irvin Probst +Isabela Presedo-Floyd +Gerhard Hobler +Giannis Zapantis +Guillaume Peillex +Jack J. Woehr +Jaime Fernandez +Jaime Fernandez +Jaime Fernandez +Jamie Macey +Jakob Jakobson +Jakob Jakobson <43045863+jakobjakobson13@users.noreply.github.com> +James Bourbeau +James Webber +Jan Schlüter Jarrod Millman Jarrod Millman -Jason Grout Jason Grout -Jason King jason king -Jay Bourque jayvius -Jean Utke jutke -Jerome Kelleher jeromekelleher -Johannes Schönberger Johannes Schönberger -Joseph Fox-Rabinovitz Joseph Fox-Rabinovitz -Joseph Fox-Rabinovitz Mad Physicist -Joseph Martinot-Lagarde Joseph Martinot-Lagarde -Julian Taylor Julian Taylor -Julian Taylor Julian Taylor +Jason Grout +Jason King +Jay Bourque +Jean Utke +Jeff VanOss +Jeffrey Yancey <3820914+jeffyancey@users.noreply.github.com> +Jeremy Lay +Jérémie du Boisberranger jeremiedbb <34657725+jeremiedbb@users.noreply.github.com> +Jérome Eertmans +Jerome Kelleher +Jessi J Zhao <35235453+jessijzhao@users.noreply.github.com> +Johannes Hampp <42553970+euronion@users.noreply.github.com> +Johannes Schönberger +Johann Faouzi +John Darbyshire <24256554+attack68@users.noreply.github.com> <24256554+attack68@users.noreply.github.com> +John Hagen +John Kirkham +John Kirkham +Jonas I. Liechti +Jonas I. Liechti +Jonas I. Liechti +Joseph Fox-Rabinovitz +Joseph Fox-Rabinovitz +Joseph Fox-Rabinovitz +Joseph Martinot-Lagarde +Joshua Himmens +Julian Taylor +Julian Taylor +Julian Taylor Julien Lhermitte Julien Lhermitte -Julien Schueller jschueller -Khaled Ben Abdallah Okuda KhaledTo -Konrad Kapp k_kapp@yahoo.com +Julien Schueller +Justus Magin +Justus Magin +Kai Striega +Kai Striega +Kasia Leszek +Kasia Leszek <39829548+katleszek@users.noreply.github.com> +Karan Dhir +Keller Meier +Kenny Huynh +Kevin Granados +Kevin Granados <54990613+NectDz@users.noreply.github.com> +Kevin Sheppard +Kevin Sheppard +Kerem Hallaç +Khaled Ben Abdallah Okuda +Kiko Correoso kikocorreoso +Kiko Correoso kikocorreoso +Konrad Kapp +Kriti Singh +Kmol Yuan +Kumud Lakara <55556183+kumudlakara@users.noreply.github.com> +Lalit Musmade Lars Buitinck Lars Buitinck Lars Buitinck Lars Buitinck -Luis Pedro Coelho Luis Pedro Coelho -Luke Zoltan Kelley lzkelley -Manoj Kumar MechCoder -Mark DePristo markdepristo -Mark Wiebe Mark -Mark Wiebe Mark Wiebe -Mark Wiebe Mark Wiebe -Mark Wiebe Mark Wiebe -Martin Goodson martingoodson -Martin Teichmann Martin Teichmann -Martino Sorbaro martinosorb -Mattheus Ueckermann empeeu -Matthew Harrigan MattHarrigan -Matti Picus mattip +Lars Grüter +Lars Grüter +Leonardus Chen +Licht Takeuchi +Luis Pedro Coelho +Luke Zoltan Kelley +Madhulika Jain Chambers <53166646+madhulikajc@users.noreply.github.com> +Magdalena Proszewska +Magdalena Proszewska <38814059+mproszewska@users.noreply.github.com> +Malik Idrees Hasan Khan <77000356+MalikIdreesHasanKhan@users.noreply.github.com>C +Manoj Kumar +Marcin Podhajski <36967358+m-podhajski@users.noreply.github.com> +Margret Pax +Margret Pax <13646646+paxcodes@users.noreply.github.com> +Mark DePristo +Mark Weissman +Mark Wiebe +Mark Wiebe +Mark Wiebe +Mark Wiebe +Mars Lee +Mars Lee <46167686+MarsBarLee@users.noreply.github.com> +Martin Goodson +Martin Reinecke +Martin Teichmann +Mary Conley +Masashi Kishimoto +Matheus Vieira Portela +Mathieu Lamarre +Matías Ríos +Matt Ord +Matt Ord <55235095+Matt-Ord@users.noreply.github.com> +Matt Hancock +Martino Sorbaro +Mattheus Ueckermann +Matthew Barber +Matthew Harrigan +Matthias Bussonnier +Matti Picus +Maximilian Konrad +Melissa Weber Mendonça +Melissa Weber Mendonça +Meltem Eren Copur Michael Behrisch behrisch Michael Droettboom mdroe -Michael K. Tran mtran -Michael Martin mmartin -Michael Schnaitter schnaitterm -Nathaniel J. Smith njsmith +Michael Dubravski +Michael Dubravski <41096057+mdubravski@users.noreply.github.com> +Michael Felt +Michael Hirsch +Michael K. Tran +Michael Martin +Michael Schnaitter +Michael Seifert +Michel Fruchart +Mike Toews +Mircea Akos Bruma +Mircea Akos Bruma +Mitchell Faas <35742861+Mitchell-Faas@users.noreply.github.com> +Muhammad Kasim +Mukulika Pahari +Mukulika Pahari <60316606+Mukulikaa@users.noreply.github.com> +Nathaniel J. Smith Naveen Arunachalam naveenarun +Neil Girdhar +Nick Papior +Nicola Soranzo Nicolas Scheffer Nicolas Scheffer Nicholas A. Del Grosso nickdg -Ondřej Čertík Ondrej Certik -Óscar Villellas Guillén ovillellas +Nicholas McKibben +Nick Minkyu Lee fivemok <9394929+fivemok@users.noreply.github.com> +Oliver Eberle +Ondřej Čertík +Óscar Villellas Guillén +Panos Mavrogiorgos Pat Miller patmiller -Paul Ivanov Paul Ivanov -Paul Jacobson hpaulj -Pearu Peterson Pearu Peterson -Pete Peeradej Tanruangporn petetanru -Peter J Cock peterjc +Paul Ivanov +Paul Ivanov +Paul YS Lee Paul +Paul Jacobson +Pearu Peterson +Pete Peeradej Tanruangporn +Peter Bell +Peter J Cock Phil Elson -Pierre GM pierregm +Pierre GM Pierre GM pierregm +Piotr Gaiński +Piotr Gaiński Pan Jan Prabhu Ramachandran prabhu -Ralf Gommers Ralf Gommers -Ralf Gommers rgommers -Rehas Sachdeva rehassachdeva -Ritta Narita RittaNarita -Robert Kern Robert Kern -Robert LU RobberPhex +Prathmesh Shirsat +Prathmesh Shirsat <55539563+Fayyr@users.noreply.github.com> +Przemyslaw Bartosik +Raghuveer Devulapalli +Raghuveer Devulapalli <44766858+r-devulap@users.noreply.github.com> +Rajas Rade lkdmttg7 +Rakesh Vasudevan +Ralf Gommers +Rehas Sachdeva +Ritta Narita +Riya Sharma +Robert Kern +Robert LU +Robert T. McGibbon +Rohit Goswami +Rohit Goswami +Rohit Goswami +Roland Kaufmann +Roman Yurchak Ronan Lamy Ronan Lamy -Russell Hewett rhewett -Ryan Blakemore ryanblak -Sam Preston jspreston -Sam Radhakrishnan = <=> -Sam Radhakrishnan sam09 -Sanchez Gonzalez Alvaro alvarosg -Saullo Giovani saullogiovani +Roy Jacobson +Russell Hewett +Ryan Blakemore +Ryan Polley +Ryan Soklaski +Ryan Soklaski +Sabrina Simao +Sabrina Simao SabrinaSimao +Sam Preston +Sam Radhakrishnan = <=> # committed without an email address +Samesh Lakhotia +Samesh Lakhotia <43701530+sameshl@users.noreply.github.com> +Sami Salonen +Sanchez Gonzalez Alvaro +Saullo Giovani Saurabh Mehta -Sebastian Berg seberg -Shota Kawabuchi skwbc -Stefan van der Walt Stefan van der Walt -Stefan van der Walt Stefan van der Walt -Stephan Hoyer Stephan Hoyer +Sayantika Banik +Sebastian Berg +Sebastian Schleehauf +Serge Guelton +Sergei Vorfolomeev <39548292+vorfol@users.noreply.github.com> +Shubham Gupta +Shubham Gupta <63910248+shubham11941140@users.noreply.github.com> +Shekhar Prasad Rajak +Shen Zhou +Shota Kawabuchi +Siavash Eliasi +Simon Conseil +Simon Gasse +Simon Gasse +Sista Seetaram +Sista Seetaram <65669128+sistaseetaram@users.noreply.github.com> +Søren Rasmussen <47032123+sorenrasmussenai@users.noreply.github.com> +Spencer Hill +Stefan Behnel +Stefan van der Walt +Stefan van der Walt +Stephan Hoyer +Stephan Hoyer +Steve Stagg Steven J Kern -Thomas A Caswell Thomas A Caswell -Tim Cera tim cera -Tom Boyd pezcore -Tom Poole tpoole -Travis Oliphant Travis E. Oliphant -Travis Oliphant Travis Oliphant -Valentin Haenel Valentin Haenel -Warren Weckesser Warren Weckesser -Wendell Smith Wendell Smith -William Spotz wfspotz@sandia.gov -Wojtek Ruszczewski wrwrwr -Zixu Zhao ZZhaoTireless -Ziyan Zhou Ziyan +Stuart Archibald +Stuart Archibald +SuryaChand P +Takanori Hirano +Theodoros Nikolaou +David Cortes +Thomas A Caswell +Thomas Kluyver +Thomas Orgis +Tim Cera +Tim Teichmann +Tim Teichmann <44259103+tteichmann@users.noreply.github.com> +Tirth Patel +Tobias Pitters +Tobias Pitters <31857876+CloseChoice@users.noreply.github.com> +Tobias Uelwer +Tom Boyd +Tom Poole +Tong Zou +Tony LaTorre +Toshiki Kataoka +Travis Oliphant +Travis Oliphant +Travis Oliphant +Valentin Haenel +Valentin Haenel +Varun Nayyar +Vinith Kishore +Vinith Kishore <85550536+vinith2@users.noreply.github.com> +Vrinda Narayan +Vrinda Narayan +Vrinda Narayan <48102157+vrindaaa@users.noreply.github.com> +Wansoo Kim +Warren Weckesser +Warren Weckesser +Weitang Li +Wendell Smith +William Spotz +Wim Glenn +Wojtek Ruszczewski +Wojciech Rzadkowski <33913808+wrzadkow@users.noreply.github.com> +Yang Hau +Yang Hau +Yashasvi Misra +Yashasvi Misra <54177363+yashasvimisra2798@users.noreply.github.com> +Yogesh Raisinghani <46864533+raisinghanii@users.noreply.github.com> +Yu Feng +Yuji Kanagawa +Yury Kirienko +Zac Hatfield-Dodds +Zé Vinícius +Zhang Na +Zixu Zhao +Ziyan Zhou +Zieji Pohz +Zieji Pohz <8103276+zjpoh@users.noreply.github.com> +Zolboo Erdenebaatar +Zolisa Bleki <44142765+zoj613@users.noreply.github.com> diff --git a/.travis.yml b/.travis.yml index 6b010e58f6a2..5652e2dbde68 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,80 +1,65 @@ # After changing this file, check it on: # http://lint.travis-ci.org/ language: python +group: travis_latest +os: linux +dist: focal -# Run jobs on container-based infrastructure, can be overridden per job -sudo: false - -# Travis whitelists the installable packages, additions can be requested -# https://github.com/travis-ci/apt-package-whitelist +# Travis allows these packages, additions can be requested +# https://github.com/travis-ci/apt-package-safelist addons: apt: packages: &common_packages - gfortran - - libatlas-dev + - libgfortran5 - libatlas-base-dev # Speedup builds, particularly when USE_CHROOT=1 - eatmydata +# Disable clone depth +git: + depth: false + cache: directories: - $HOME/.cache/pip -env: - global: - - WHEELHOUSE_UPLOADER_USERNAME=travis.numpy - # The following is generated with the command: - # travis encrypt -r numpy/numpy WHEELHOUSE_UPLOADER_SECRET=tH3AP1KeY - - secure: "IEicLPrP2uW+jW51GRwkONQpdPqMVtQL5bdroqR/U8r9Tr\ - XrbCVRhp4AP8JYZT0ptoBpmZWWGjmKBndB68QlMiUjQPow\ - iFWt9Ka92CaqYdU7nqfWp9VImSndPmssjmCXJ1v1IjZPAM\ - ahp7Qnm0rWRmA0z9SomuRUQOJQ6s684vU=" - -python: - - 2.7 - - 3.4 - - 3.5 - - 3.6 - - 3.7-dev -matrix: +jobs: include: - - python: 3.6 - env: USE_CHROOT=1 ARCH=i386 DIST=bionic PYTHON=3.6 - sudo: true - addons: - apt: - update: true - packages: - - dpkg - - debootstrap - - python: 3.4 - env: USE_DEBUG=1 - addons: - apt: - packages: - - *common_packages - - cython3-dbg - - python3-dbg - - python3-dev - - python3-setuptools - - python: 3.6 - env: USE_WHEEL=1 RUN_FULL_TESTS=1 - - python: 2.7 - env: USE_WHEEL=1 RUN_FULL_TESTS=1 PYTHON_OPTS="-3 -OO" - - python: 3.6 - env: USE_SDIST=1 - - python: 3.6 + - python: 3.8 + os: linux + arch: ppc64le + env: + # use OpenBLAS build, not system ATLAS + - DOWNLOAD_OPENBLAS=1 + - NPY_USE_BLAS_ILP64=1 + - ATLAS=None + + - python: 3.8 + os: linux + arch: s390x env: - - PYTHONOPTIMIZE=2 - - USE_ASV=1 - - python: 3.5 - env: NPY_RELAXED_STRIDES_CHECKING=0 - - python: 3.6 - env: USE_WHEEL=1 NPY_RELAXED_STRIDES_DEBUG=1 - - python: 3.6 + # use OpenBLAS build, not system ATLAS + - DOWNLOAD_OPENBLAS=1 + - NPY_USE_BLAS_ILP64=1 + - ATLAS=None + + - python: 3.8 + os: linux + arch: arm64 + virt: vm + env: + # use OpenBLAS build, not system ATLAS + - DOWNLOAD_OPENBLAS=1 + - ATLAS=None + + - python: 3.10-dev + os: linux + arch: arm64 + virt: vm env: - - BLAS=None - - LAPACK=None + # use OpenBLAS build, not system ATLAS + - DOWNLOAD_OPENBLAS=1 - ATLAS=None before_install: @@ -82,6 +67,3 @@ before_install: script: - ./tools/travis-test.sh - -after_success: - - ./tools/travis-upload-wheel.sh diff --git a/CITATION.bib b/CITATION.bib new file mode 100644 index 000000000000..66e7dfd372a4 --- /dev/null +++ b/CITATION.bib @@ -0,0 +1,20 @@ +@ARTICLE{2020NumPy-Array, + author = {Harris, Charles R. and Millman, K. Jarrod and + van der Walt, Stéfan J and Gommers, Ralf and + Virtanen, Pauli and Cournapeau, David and + Wieser, Eric and Taylor, Julian and Berg, Sebastian and + Smith, Nathaniel J. and Kern, Robert and Picus, Matti and + Hoyer, Stephan and van Kerkwijk, Marten H. and + Brett, Matthew and Haldane, Allan and + Fernández del Río, Jaime and Wiebe, Mark and + Peterson, Pearu and Gérard-Marchant, Pierre and + Sheppard, Kevin and Reddy, Tyler and Weckesser, Warren and + Abbasi, Hameer and Gohlke, Christoph and + Oliphant, Travis E.}, + title = {Array programming with {NumPy}}, + journal = {Nature}, + year = {2020}, + volume = {585}, + pages = {357–362}, + doi = {10.1038/s41586-020-2649-2} +} diff --git a/INSTALL.rst.txt b/INSTALL.rst.txt index d8ca80d926e5..1bc97c4b5f86 100644 --- a/INSTALL.rst.txt +++ b/INSTALL.rst.txt @@ -4,7 +4,7 @@ Building and installing NumPy **IMPORTANT**: the below notes are about building NumPy, which for most users is *not* the recommended way to install NumPy. Instead, use either a complete scientific Python distribution (recommended) or a binary installer - see -http://scipy.org/install.html. +https://scipy.org/install.html. .. Contents:: @@ -12,47 +12,45 @@ http://scipy.org/install.html. Prerequisites ============= -Building NumPy requires the following software installed: +Building NumPy requires the following installed software: -1) For Python 2, Python__ 2.7.x or newer. - For Python 3, Python__ 3.4.x or newer. +1) Python__ 3.7.x or newer. - On Debian and derivative (Ubuntu): python python-dev + Please note that the Python development headers also need to be installed, + e.g., on Debian/Ubuntu one needs to install both `python3` and + `python3-dev`. On Windows and macOS this is normally not an issue. - On Windows: the official python installer on Python__ is enough +2) Cython >= 0.29.21 - Make sure that the Python package distutils is installed before - continuing. For example, in Debian GNU/Linux, distutils is included - in the python-dev package. +3) pytest__ (optional) 1.15 or later - Python must also be compiled with the zlib module enabled. + This is required for testing NumPy, but not for using it. -2) Cython >= 0.19 (for development versions of numpy, not for released - versions) -3) nose__ (optional) 1.0 or later +4) Hypothesis__ (optional) 5.3.0 or later - This is required for testing numpy, but not for using it. + This is required for testing NumPy, but not for using it. -Python__ http://www.python.org -nose__ http://nose.readthedocs.io +Python__ https://www.python.org/ +pytest__ https://docs.pytest.org/en/stable/ +Hypothesis__ https://hypothesis.readthedocs.io/en/latest/ -.. note:: +.. note:: If you want to build NumPy in order to work on NumPy itself, use ``runtests.py``. For more details, see - http://docs.scipy.org/doc/numpy/dev/development_environment.html + https://numpy.org/devdocs/dev/development_environment.html .. note:: - More extensive information on building NumPy (and Scipy) is maintained at - http://scipy.org/scipylib/building/index.html + More extensive information on building NumPy is maintained at + https://numpy.org/devdocs/user/building.html#building-from-source Basic Installation ================== -To install numpy run:: +To install NumPy, run:: python setup.py build -j 4 install --prefix $HOME/.local @@ -61,8 +59,6 @@ To perform an inplace build that can be run from the source folder run:: python setup.py build_ext --inplace -j 4 -Note that the ``python`` command here is the system default Python, generally -python 2, the ``python3`` command may be needed to install on python 3. See `Requirements for Installing Packages `_ for more details. @@ -73,14 +69,15 @@ NPY_NUM_BUILD_JOBS. Choosing compilers ================== -NumPy needs a C compiler, and for development versions also Cython. A Fortran +NumPy needs a C compiler, and for development versions also needs Cython. A Fortran compiler isn't needed to build NumPy itself; the ``numpy.f2py`` tests will be skipped when running the test suite if no Fortran compiler is available. For building Scipy a Fortran compiler is needed though, so we include some details on Fortran compilers in the rest of this section. -On OS X and Linux, all common compilers will work. Note that for Fortran, -``gfortran`` is strongly preferred over ``g77``, but if you happen to have both +On OS X and Linux, all common compilers will work. + +For Fortran, ``gfortran`` works, ``g77`` does not. In case ``g77`` is installed then ``g77`` will be detected and used first. To explicitly select ``gfortran`` in that case, do:: @@ -89,18 +86,18 @@ installed then ``g77`` will be detected and used first. To explicitly select Windows ------- -On Windows, building from source can be difficult. Currently the most robust -option is to use the Intel compilers, or alternatively MSVC (the same version -as used to build Python itself) with Intel ifort. Intel itself maintains a -good `application note `_ +On Windows, building from source can be difficult (in particular if you need to +build SciPy as well, because that requires a Fortran compiler). Currently, the +most robust option is to use MSVC (for NumPy only). If you also need SciPy, +you can either use MSVC + Intel Fortran or the Intel compiler suite. +Intel itself maintains a good `application note +`_ on this. -If you want to use a free compiler toolchain, the recommended compiler is MingwPy__. -The older MinGW32 compiler set used to produce older .exe installers for NumPy -itself is still available at https://github.com/numpy/numpy-vendor, but not -recommended for use anymore. - -MingwPy__ http://mingwpy.github.io +If you want to use a free compiler toolchain, our current recommendation is to +use Docker or Windows subsystem for Linux (WSL). See +https://scipy.github.io/devdocs/dev/contributor/contributor_toc.html#development-environment +for more details. Building with optimized BLAS support @@ -114,22 +111,22 @@ details. Windows ------- -The Intel compilers work with Intel MKL, see the application note linked above. -MingwPy__ works with OpenBLAS. -For an overview of the state of BLAS/LAPACK libraries on Windows, see -`here `_. +The Intel compilers work with Intel MKL, see the application note linked above. + +For an overview of the state of BLAS/LAPACK libraries on Windows, see +`here `_. -OS X ----- +macOS +----- -OS X ships the Accelerate framework, which NumPy can build against without any -manual configuration. Other BLAS/LAPACK implementations (OpenBLAS, Intel MKL, -ATLAS) will also work. +You will need to install a BLAS/LAPACK library. We recommend using OpenBLAS or +Intel MKL. Apple's Accelerate also still works, however it has bugs and we are +likely to drop support for it in the near future. Ubuntu/Debian ------------- -For best performance a development package providing BLAS and CBLAS should be +For best performance, a development package providing BLAS and CBLAS should be installed. Some of the options available are: - ``libblas-dev``: reference BLAS (not very optimized) @@ -154,7 +151,7 @@ Or by preloading a specific BLAS library with:: Build issues ============ -If you run into build issues and need help, the NumPy -`mailing list `_ is the best -place to ask. If the issue is clearly a bug in NumPy, please file an issue (or +If you run into build issues and need help, the NumPy and SciPy +`mailing list `_ is the best +place to ask. If the issue is clearly a bug in NumPy, please file an issue (or even better, a pull request) at https://github.com/numpy/numpy. diff --git a/LICENSE.txt b/LICENSE.txt index 0065d465f27d..4723d4ea009e 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2005-2017, NumPy Developers. +Copyright (c) 2005-2021, NumPy Developers. All rights reserved. Redistribution and use in source and binary forms, with or without @@ -28,33 +28,3 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - - -The NumPy repository and source distributions bundle several libraries that are -compatibly licensed. We list these here. - -Name: Numpydoc -Files: doc/sphinxext/numpydoc/* -License: 2-clause BSD - For details, see doc/sphinxext/LICENSE.txt - -Name: scipy-sphinx-theme -Files: doc/scipy-sphinx-theme/* -License: 3-clause BSD, PSF and Apache 2.0 - For details, see doc/sphinxext/LICENSE.txt - -Name: lapack-lite -Files: numpy/linalg/lapack_lite/* -License: 3-clause BSD - For details, see numpy/linalg/lapack_lite/LICENSE.txt - -Name: tempita -Files: tools/npy_tempita/* -License: BSD derived - For details, see tools/npy_tempita/license.txt - -Name: dragon4 -Files: numpy/core/src/multiarray/dragon4.c -License: One of a kind - For license text, see numpy/core/src/multiarray/dragon4.c diff --git a/LICENSES_bundled.txt b/LICENSES_bundled.txt new file mode 100644 index 000000000000..26c7a7829361 --- /dev/null +++ b/LICENSES_bundled.txt @@ -0,0 +1,22 @@ +The NumPy repository and source distributions bundle several libraries that are +compatibly licensed. We list these here. + +Name: lapack-lite +Files: numpy/linalg/lapack_lite/* +License: BSD-3-Clause + For details, see numpy/linalg/lapack_lite/LICENSE.txt + +Name: tempita +Files: tools/npy_tempita/* +License: MIT + For details, see tools/npy_tempita/license.txt + +Name: dragon4 +Files: numpy/core/src/multiarray/dragon4.c +License: MIT + For license text, see numpy/core/src/multiarray/dragon4.c + +Name: libdivide +Files: numpy/core/include/numpy/libdivide/* +License: Zlib + For license text, see numpy/core/include/numpy/libdivide/LICENSE.txt diff --git a/MANIFEST.in b/MANIFEST.in index eff19e20a965..ab6ecd518e1b 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,32 +1,50 @@ # # Use .add_data_files and .add_data_dir methods in a appropriate # setup.py files to include non-python files such as documentation, -# data, etc files to distribution. Avoid using MANIFEST.in for that. +# data, etc files to distribution (*for installation*). +# Avoid using MANIFEST.in for that. # -include MANIFEST.in -include pytest.ini -include *.txt -include site.cfg.example -include numpy/random/mtrand/generate_mtrand_c.py -recursive-include numpy/random/mtrand *.pyx *.pxd +# Files in top-level directory: +include *.* +# Exclude license file that we append to the main license when running +# `python setup.py sdist`. And exclude generated files in repo root. +exclude LICENSES_bundled.txt +exclude .* +exclude azure-*.yml + +# Include coveragerc for runtests.py +include .coveragerc + +# Sub-directories. Included are: numpy/, doc/, benchmarks/, tools/ +include numpy/_version.py +recursive-include numpy/random *.pyx *.pxd *.pyx.in *.pxd.in +include numpy/py.typed +include numpy/random/include/* +include numpy/*.pxd # Add build support that should go in sdist, but not go in bdist/be installed -recursive-include numpy/_build_utils * -recursive-include numpy/linalg/lapack_lite *.c *.h -include tox.ini +# Note that sub-directories that don't have __init__ are apparently not +# included by 'recursive-include', so list those separately +recursive-include numpy * +recursive-include numpy/linalg/lapack_lite * +recursive-include tools * # Add sdist files whose use depends on local configuration. -include numpy/core/src/multiarray/cblasfuncs.c -include numpy/core/src/multiarray/python_xerbla.c -# Adding scons build related files not found by distutils +include numpy/core/src/common/cblasfuncs.c +include numpy/core/src/common/python_xerbla.c +# Adding build related files not found by distutils recursive-include numpy/core/code_generators *.py *.txt recursive-include numpy/core *.in *.h -# Add documentation: we don't use add_data_dir since we do not want to include -# this at installation, only for sdist-generated tarballs -include doc/Makefile doc/postprocess.py -recursive-include doc/release * -recursive-include doc/source * -recursive-include doc/sphinxext * -recursive-include tools/allocation_tracking * -recursive-include tools/swig * -recursive-include doc/scipy-sphinx-theme * - +# Add documentation and benchmarks: we don't use add_data_dir since we do not +# want to include this at installation, only for sdist-generated tarballs +# Docs: +recursive-include doc * +prune doc/build +prune doc/source/generated +# Benchmarks: +recursive-include benchmarks * +prune benchmarks/env +prune benchmarks/results +prune benchmarks/html +prune benchmarks/numpy +# Exclude generated files +prune */__pycache__ global-exclude *.pyc *.pyo *.pyd *.swp *.bak *~ diff --git a/README.md b/README.md index cd11b7bc5289..04825dc5d941 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,36 @@ -# NumPy +# NumPy -[![Travis](https://img.shields.io/travis/numpy/numpy/master.svg?label=Travis%20CI)](https://travis-ci.org/numpy/numpy) -[![AppVeyor](https://img.shields.io/appveyor/ci/charris/numpy/master.svg?label=AppVeyor)](https://ci.appveyor.com/project/charris/numpy) + + + + + + + + + + + +[![Powered by NumFOCUS](https://img.shields.io/badge/powered%20by-NumFOCUS-orange.svg?style=flat&colorA=E1523D&colorB=007D8A)]( +https://numfocus.org) +[![Pypi Downloads](https://img.shields.io/pypi/dm/numpy.svg?label=Pypi%20downloads)]( +https://pypi.org/project/numpy/) +[![Conda Downloads](https://img.shields.io/conda/dn/conda-forge/numpy.svg?label=Conda%20downloads)]( +https://anaconda.org/conda-forge/numpy) +[![Stack Overflow](https://img.shields.io/badge/stackoverflow-Ask%20questions-blue.svg)]( +https://stackoverflow.com/questions/tagged/numpy) +[![Nature Paper](https://img.shields.io/badge/DOI-10.1038%2Fs41592--019--0686--2-blue)]( +https://doi.org/10.1038/s41586-020-2649-2) NumPy is the fundamental package needed for scientific computing with Python. -- **Website (including documentation):** http://www.numpy.org +- **Website:** https://www.numpy.org +- **Documentation:** https://numpy.org/doc - **Mailing list:** https://mail.python.org/mailman/listinfo/numpy-discussion -- **Source:** https://github.com/numpy/numpy +- **Source code:** https://github.com/numpy/numpy +- **Contributing:** https://www.numpy.org/devdocs/dev/index.html - **Bug reports:** https://github.com/numpy/numpy/issues +- **Report a security vulnerability:** https://tidelift.com/docs/security It provides: @@ -17,8 +39,46 @@ It provides: - tools for integrating C/C++ and Fortran code - useful linear algebra, Fourier transform, and random number capabilities -If ``nose`` is installed, tests can be run after installation with: +Testing: + +NumPy requires `pytest`. Tests can then be run after installation with: python -c 'import numpy; numpy.test()' -[![Powered by NumFOCUS](https://img.shields.io/badge/powered%20by-NumFOCUS-orange.svg?style=flat&colorA=E1523D&colorB=007D8A)](https://numfocus.org) + +Call for Contributions +---------------------- + +The NumPy project welcomes your expertise and enthusiasm! + +Small improvements or fixes are always appreciated; issues labeled as ["good +first issue"](https://github.com/numpy/numpy/labels/good%20first%20issue) +may be a good starting point. If you are considering larger contributions +to the source code, please contact us through the [mailing +list](https://mail.python.org/mailman/listinfo/numpy-discussion) first. + +Writing code isn’t the only way to contribute to NumPy. You can also: +- review pull requests +- triage issues +- develop tutorials, presentations, and other educational materials +- maintain and improve [our website](https://github.com/numpy/numpy.org) +- develop graphic design for our brand assets and promotional materials +- translate website content +- help with outreach and onboard new contributors +- write grant proposals and help with other fundraising efforts + +If you’re unsure where to start or how your skills fit in, reach out! You can +ask on the mailing list or here, on GitHub, by opening a new issue or leaving a +comment on a relevant issue that is already open. + +Our preferred channels of communication are all public, but if you’d like to +speak to us in private first, contact our community coordinators at +numpy-team@googlegroups.com or on Slack (write numpy-team@googlegroups.com for +an invitation). + +We also have a biweekly community call, details of which are announced on the +mailing list. You are very welcome to join. + +If you are new to contributing to open source, [this +guide](https://opensource.guide/how-to-contribute/) helps explain why, what, +and how to successfully get involved. diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 000000000000..9e65f9a20745 --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,326 @@ +trigger: + # start a new build for every push + batch: False + branches: + include: + - main + - maintenance/* + + +pr: + branches: + include: + - '*' # must quote since "*" is a YAML reserved character; we want a string + + +stages: + +- stage: InitialTests + jobs: + + # Native build is based on gcc flag `-march=native` + - job: Linux_baseline_native + pool: + vmImage: 'ubuntu-20.04' + steps: + - script: | + git submodule update --init + displayName: 'Fetch submodules' + - script: | + if ! `gcc 2>/dev/null`; then + sudo apt install gcc + fi + sudo apt install python3 + sudo apt install python3-dev + # python3 has no setuptools, so install one to get us going + python3 -m pip install --user --upgrade pip 'setuptools<49.2.0' + python3 -m pip install --user -r test_requirements.txt + displayName: 'install python/requirements' + - script: | + python3 runtests.py --show-build-log --cpu-baseline=native --cpu-dispatch=none \ + --debug-info --mode=full -- -rsx --junitxml=junit/test-results.xml + displayName: 'Run native baseline Build / Tests' + - task: PublishTestResults@2 + condition: succeededOrFailed() + inputs: + testResultsFiles: '**/test-*.xml' + failTaskOnFailedTests: true + testRunTitle: 'Publish test results for baseline/native' + +- stage: ComprehensiveTests + jobs: + + - job: Lint + condition: and(succeeded(), eq(variables['Build.Reason'], 'PullRequest')) + pool: + vmImage: 'ubuntu-18.04' + steps: + - task: UsePythonVersion@0 + inputs: + versionSpec: '3.8' + addToPath: true + architecture: 'x64' + - script: >- + python -m pip install -r linter_requirements.txt + displayName: 'Install tools' + # pip 21.1 emits a pile of garbage messages to annoy users :) + # failOnStderr: true + - script: | + python tools/linter.py --branch origin/$(System.PullRequest.TargetBranch) + displayName: 'Run Lint Checks' + failOnStderr: true + + - job: Linux_Python_38_32bit_full_with_asserts + pool: + vmImage: 'ubuntu-20.04' + steps: + - script: | + git submodule update --init + displayName: 'Fetch submodules' + - script: | + docker run -v $(pwd):/numpy -e CFLAGS="-msse2 -std=c99 -UNDEBUG" \ + -e F77=gfortran-5 -e F90=gfortran-5 quay.io/pypa/manylinux2014_i686 \ + /bin/bash -xc "cd numpy && \ + /opt/python/cp38-cp38/bin/python -mvenv venv &&\ + source venv/bin/activate && \ + target=\$(python3 tools/openblas_support.py) && \ + cp -r \$target/lib/* /usr/lib && \ + cp \$target/include/* /usr/include && \ + python3 -m pip install -r test_requirements.txt && \ + echo CFLAGS \$CFLAGS && \ + python3 -m pip install -v . && \ + python3 runtests.py -n --debug-info --mode=full -- -rsx --junitxml=junit/test-results.xml && \ + python3 tools/openblas_support.py --check_version" + displayName: 'Run 32-bit manylinux2010 Docker Build / Tests' + - task: PublishTestResults@2 + condition: succeededOrFailed() + inputs: + testResultsFiles: '**/test-*.xml' + failTaskOnFailedTests: true + testRunTitle: 'Publish test results for Python 3.8-32 bit full Linux' + + + - job: macOS + pool: + # NOTE: at time of writing, there is a danger + # that using an invalid vmIMage string for macOS + # image silently redirects to a Windows build on Azure; + # for now, use the only image name officially present in + # the docs even though i.e., numba uses another in their + # azure config for mac os -- Microsoft has indicated + # they will patch this issue + vmImage: 'macOS-1015' + strategy: + maxParallel: 3 + matrix: + Python38: + PYTHON_VERSION: '3.8' + USE_OPENBLAS: '1' + Python38-ILP64: + PYTHON_VERSION: '3.8' + NPY_USE_BLAS_ILP64: '1' + USE_OPENBLAS: '1' + steps: + # the @0 refers to the (major) version of the *task* on Microsoft's + # end, not the order in the build matrix nor anything to do + # with version of Python selected + - task: UsePythonVersion@0 + inputs: + versionSpec: $(PYTHON_VERSION) + addToPath: true + architecture: 'x64' + - script: | + set -xe + [ -n "$USE_XCODE_10" ] && /bin/bash -c "sudo xcode-select -s /Applications/Xcode_10.app/Contents/Developer" + clang --version + displayName: 'report clang version' + # NOTE: might be better if we could avoid installing + # two C compilers, but with homebrew looks like we're + # now stuck getting the full gcc toolchain instead of + # just pulling in gfortran + - script: | + set -xe + # same version of gfortran as the open-libs and numpy-wheel builds + curl -L https://github.com/MacPython/gfortran-install/raw/master/archives/gfortran-4.9.0-Mavericks.dmg -o gfortran.dmg + GFORTRAN_SHA256=$(shasum -a 256 gfortran.dmg) + KNOWN_SHA256="d2d5ca5ba8332d63bbe23a07201c4a0a5d7e09ee56f0298a96775f928c3c4b30 gfortran.dmg" + if [ "$GFORTRAN_SHA256" != "$KNOWN_SHA256" ]; then + echo sha256 mismatch + exit 1 + fi + hdiutil attach -mountpoint /Volumes/gfortran gfortran.dmg + sudo installer -pkg /Volumes/gfortran/gfortran.pkg -target / + otool -L /usr/local/gfortran/lib/libgfortran.3.dylib + # Manually symlink gfortran-4.9 to plain gfortran for f2py. + # No longer needed after Feb 13 2020 as gfortran is already present + # and the attempted link errors. Keep this for future reference. + # ln -s /usr/local/bin/gfortran-4.9 /usr/local/bin/gfortran + displayName: 'make libgfortran available on mac os for openblas' + # use the pre-built openblas binary that most closely + # matches our MacOS wheel builds -- currently based + # primarily on file size / name details + - script: | + set -xe + target=$(python tools/openblas_support.py) + ls -lR $target + # manually link to appropriate system paths + cp $target/lib/lib* /usr/local/lib/ + cp $target/include/* /usr/local/include/ + otool -L /usr/local/lib/libopenblas* + displayName: 'install pre-built openblas' + condition: eq(variables['USE_OPENBLAS'], '1') + - script: python -m pip install --upgrade pip 'setuptools<49.2.0' wheel + displayName: 'Install tools' + - script: | + python -m pip install -r test_requirements.txt + python -m pip install vulture docutils sphinx==2.2.0 numpydoc + displayName: 'Install dependencies; some are optional to avoid test skips' + - script: /bin/bash -c "! vulture . --min-confidence 100 --exclude doc/,numpy/distutils/ | grep 'unreachable'" + displayName: 'Check for unreachable code paths in Python modules' + # prefer usage of clang over gcc proper + # to match likely scenario on many user mac machines + - script: python setup.py build -j 4 build_src --verbose-cfg install + displayName: 'Build NumPy' + env: + BLAS: None + LAPACK: None + ATLAS: None + CC: /usr/bin/clang + condition: eq(variables['USE_OPENBLAS'], '1') + - script: python setup.py build -j 4 build_ext --inplace install + displayName: 'Build NumPy without OpenBLAS and new casting' + env: + BLAS: None + LAPACK: None + ATLAS: None + CC: /usr/bin/clang + condition: eq(variables['USE_OPENBLAS'], '0') + # wait until after dev build of NumPy to pip + # install matplotlib to avoid pip install of older numpy + - script: python -m pip install matplotlib + displayName: 'Install matplotlib before refguide run' + - script: python runtests.py -g --refguide-check + displayName: 'Run Refguide Check' + condition: eq(variables['USE_OPENBLAS'], '1') + - script: python runtests.py -n --mode=full -- -rsx --junitxml=junit/test-results.xml + displayName: 'Run Full NumPy Test Suite' + condition: eq(variables['USE_OPENBLAS'], '1') + - bash: python tools/openblas_support.py --check_version + displayName: 'Verify OpenBLAS version' + condition: eq(variables['USE_OPENBLAS'], '1') + # import doesn't work when in numpy src directory , so do a pip dev install of build lib to test + - script: | + #!/bin/bash -v + set +e + python -c "import numpy as np" > test_output.log 2>&1 + check_output_code=$? + cat test_output.log + grep "buggy Accelerate backend" test_output.log + check_message=$? + if [ $check_output_code == 1 ] && [ $check_message == 0 ]; then exit 0; else exit 1;fi + displayName: "Check if numpy import fails with accelerate" + condition: eq(variables['USE_OPENBLAS'], '0') + - task: PublishTestResults@2 + condition: succeededOrFailed() + inputs: + testResultsFiles: '**/test-*.xml' + failTaskOnFailedTests: true + testRunTitle: 'Publish test results for Python 3.8 64-bit full Mac OS' + + + - job: Windows + pool: + vmImage: 'windows-2019' + strategy: + maxParallel: 6 + matrix: + Python38-32bit-fast: + PYTHON_VERSION: '3.8' + PYTHON_ARCH: 'x86' + TEST_MODE: fast + BITS: 32 + Python38-64bit-full: + PYTHON_VERSION: '3.8' + PYTHON_ARCH: 'x64' + TEST_MODE: full + BITS: 64 + Python39-32bit-fast: + PYTHON_VERSION: '3.9' + PYTHON_ARCH: 'x86' + TEST_MODE: fast + BITS: 32 + Python39-64bit-full: + PYTHON_VERSION: '3.9' + PYTHON_ARCH: 'x64' + TEST_MODE: full + BITS: 64 + NPY_USE_BLAS_ILP64: '1' + PyPy38-64bit-fast: + PYTHON_VERSION: 'PyPy' + PYTHON_ARCH: 'x64' + TEST_MODE: fast + BITS: 64 + NPY_USE_BLAS_ILP64: '1' + + steps: + - template: azure-steps-windows.yml + + + - job: Linux_conda + pool: + vmImage: 'ubuntu-20.04' + steps: + - script: | + git submodule update --init + displayName: 'Fetch submodules' + - script: | + # create and activate conda environment + conda env create -f environment.yml + displayName: 'Create conda environment.' + - script: | + # >>> conda initialize >>> + # !! Contents within this block are 'conda init' !! + # see https://github.com/conda/conda/issues/7980 + __conda_setup="$('conda' 'shell.bash' 'hook' 2> /dev/null)" + eval "$__conda_setup" + unset __conda_setup + # <<< conda initialize <<< + conda activate numpy-dev + # Run native baseline Build / Tests + python runtests.py --show-build-log --cpu-baseline=native --cpu-dispatch=none \ + --debug-info --mode=full -- -rsx --junitxml=junit/test-results.xml + displayName: 'Run native baseline Build / Tests in conda.' + - task: PublishTestResults@2 + condition: succeededOrFailed() + inputs: + testResultsFiles: '**/test-*.xml' + failTaskOnFailedTests: true + testRunTitle: 'Publish test results for conda installation' + + + #- job: Linux_gcc48 + #pool: + ## ubuntu-20.04 does not provide a gcc-4.8 package + #vmImage: 'ubuntu-18.04' + #steps: + #- script: | + #sudo apt update + #sudo apt install python3.7 + #sudo apt install python3.7-dev + #if ! `gcc-4.8 2>/dev/null`; then + #sudo apt install gcc-4.8 + #fi + #displayName: 'add gcc 4.8' + #- script: | + ## python3 has no setuptools, so install one to get us going + #python3.7 -m pip install --user --upgrade pip 'setuptools<49.2.0' + #python3.7 -m pip install --user -r test_requirements.txt + #CPPFLAGS='' CC=gcc-4.8 F77=gfortran-5 F90=gfortran-5 \ + #python3.7 runtests.py --debug-info --mode=full -- -rsx --junitxml=junit/test-results.xml + #displayName: 'Run gcc4.8 Build / Tests' + #- task: PublishTestResults@2 + #condition: succeededOrFailed() + #inputs: + #testResultsFiles: '**/test-*.xml' + #failTaskOnFailedTests: true + #testRunTitle: 'Publish test results for gcc 4.8' diff --git a/azure-steps-windows.yml b/azure-steps-windows.yml new file mode 100644 index 000000000000..95a359c89993 --- /dev/null +++ b/azure-steps-windows.yml @@ -0,0 +1,93 @@ +steps: +- task: UsePythonVersion@0 + inputs: + versionSpec: $(PYTHON_VERSION) + addToPath: true + architecture: $(PYTHON_ARCH) + condition: not(contains(variables['PYTHON_VERSION'], 'PyPy')) +- powershell: | + # $url = "http://buildbot.pypy.org/nightly/py3.8/pypy-c-jit-latest-win64.zip" + $url = "https://downloads.python.org/pypy/pypy3.8-v7.3.7-win64.zip" + $output = "pypy.zip" + $wc = New-Object System.Net.WebClient + $wc.DownloadFile($url, $output) + echo "downloaded $url to $output" + mkdir pypy3 + Expand-Archive $output -DestinationPath pypy3 + # move pypy3/pypy-c-*/* pypy3 + move pypy3/pypy*/* pypy3 + $pypypath = Join-Path (Get-Item .).FullName pypy3 + $env:Path = $pypypath + ";" + $env:Path + setx PATH $env:Path + python -mensurepip + echo "##vso[task.prependpath]$pypypath" + condition: contains(variables['PYTHON_VERSION'], 'PyPy') + displayName: "Install PyPy3.8 " + +- script: python -m pip install --upgrade pip wheel + displayName: 'Install tools' + +- script: python -m pip install -r test_requirements.txt + displayName: 'Install dependencies; some are optional to avoid test skips' + +- powershell: | + choco install -y mingw --forcex86 --force --version=7.3.0 + refreshenv + displayName: 'Install 32-bit mingw for 32-bit builds' + condition: eq(variables['BITS'], 32) + +- powershell: | + $ErrorActionPreference = "Stop" + # Download and get the path to "openblas.a". We cannot copy it + # to $PYTHON_EXE's directory since that is on a different drive which + # mingw does not like. Instead copy it to a directory and set OPENBLAS, + # since OPENBLAS will be picked up by the openblas discovery + $target = $(python tools/openblas_support.py) + mkdir openblas + echo "Copying $target to openblas/" + cp $target openblas/ + displayName: 'Download / Install OpenBLAS' + +# NOTE: for Windows builds it seems much more tractable to use runtests.py +# vs. manual setup.py and then runtests.py for testing only + +- powershell: | + ls openblas + If ($(BITS) -eq 32) { + $env:CFLAGS = "-m32" + $env:LDFLAGS = "-m32" + $env:PATH = "C:\\ProgramData\\chocolatey\\lib\\mingw\\tools\\install\\mingw$(BITS)\\bin;" + $env:PATH + } + If ( Test-Path env:NPY_USE_BLAS_ILP64 ) { + $env:OPENBLAS64_ = "openblas" + } else { + $env:OPENBLAS = "openblas" + } + python -c "from tools import openblas_support; openblas_support.make_init('numpy')" + python -m pip wheel -v -v -v --no-build-isolation --no-use-pep517 --wheel-dir=dist . + + ls dist -r | Foreach-Object { + python -m pip install $_.FullName + } + displayName: 'Build NumPy' + +- bash: | + pushd . && cd .. && target=$(python -c "import numpy, os; print(os.path.abspath(os.path.join(os.path.dirname(numpy.__file__), '.libs')))") && popd + python -m pip download -d destination --only-binary :all: --no-deps numpy==1.14 + cd destination && unzip numpy*.whl && cp numpy/.libs/*.dll $target + ls $target + displayName: 'Add extraneous & older DLL to numpy/.libs to probe DLL handling robustness' + condition: eq(variables['PYTHON_VERSION'], '3.6') +- script: pushd . && cd .. && python -c "from ctypes import windll; windll.kernel32.SetDefaultDllDirectories(0x00000800); import numpy" && popd + displayName: 'For gh-12667; Windows DLL resolution' + condition: eq(variables['PYTHON_VERSION'], '3.6') + +- script: python runtests.py -n --show-build-log --mode=$(TEST_MODE) -- -rsx --junitxml=junit/test-results.xml + displayName: 'Run NumPy Test Suite' + +- task: PublishTestResults@2 + condition: succeededOrFailed() + inputs: + testResultsFiles: '**/test-*.xml' + failTaskOnFailedTests: true + testRunTitle: 'Publish test results for Python $(PYTHON_VERSION) $(BITS)-bit $(TEST_MODE) Windows' diff --git a/benchmarks/README.rst b/benchmarks/README.rst index f4f0b0de93ac..2700e95e7ab2 100644 --- a/benchmarks/README.rst +++ b/benchmarks/README.rst @@ -16,19 +16,50 @@ unless told otherwise. Some of the benchmarking features in ``runtests.py``. To run the benchmarks, you do not need to install a development version of NumPy to your current Python environment. -Run a benchmark against currently checked out NumPy version (don't -record the result):: +Before beginning, ensure that *airspeed velocity* is installed. +By default, `asv` ships with support for anaconda and virtualenv:: + + pip install asv + pip install virtualenv + +After contributing new benchmarks, you should test them locally +before submitting a pull request. + +To run all benchmarks, navigate to the root NumPy directory at +the command line and execute:: + + python runtests.py --bench + +where ``--bench`` activates the benchmark suite instead of the +test suite. This builds NumPy and runs all available benchmarks +defined in ``benchmarks/``. (Note: this could take a while. Each +benchmark is run multiple times to measure the distribution in +execution times.) + +To run benchmarks from a particular benchmark module, such as +``bench_core.py``, simply append the filename without the extension:: python runtests.py --bench bench_core -Compare change in benchmark results to another version:: +To run a benchmark defined in a class, such as ``Mandelbrot`` +from ``bench_avx.py``:: + + python runtests.py --bench bench_avx.Mandelbrot + +Compare change in benchmark results to another version/commit/branch:: python runtests.py --bench-compare v1.6.2 bench_core + python runtests.py --bench-compare 8bf4e9b bench_core + python runtests.py --bench-compare main bench_core -Run ASV commands (record results and generate HTML):: +All of the commands above display the results in plain text in +the console, and the results are not saved for comparison with +future commits. For greater control, a graphical view, and to +have results saved for future comparison you can run ASV commands +(record results and generate HTML):: cd benchmarks - asv run --skip-existing-commits --steps 10 ALL + asv run -n -e --python=same asv publish asv preview @@ -60,3 +91,11 @@ Some things to consider: - Preparing arrays etc. should generally be put in the ``setup`` method rather than the ``time_`` methods, to avoid counting preparation time together with the time of the benchmarked operation. + +- Be mindful that large arrays created with ``np.empty`` or ``np.zeros`` might + not be allocated in physical memory until the memory is accessed. If this is + desired behaviour, make sure to comment it in your setup function. If + you are benchmarking an algorithm, it is unlikely that a user will be + executing said algorithm on a newly created empty/zero array. One can force + pagefaults to occur in the setup phase either by calling ``np.ones`` or + ``arr.fill(value)`` after creating the array, diff --git a/benchmarks/asv.conf.json b/benchmarks/asv.conf.json index d837b0d6719c..029adb5898db 100644 --- a/benchmarks/asv.conf.json +++ b/benchmarks/asv.conf.json @@ -7,7 +7,7 @@ "project": "numpy", // The project's homepage - "project_url": "http://numpy.org/", + "project_url": "https://www.numpy.org/", // The URL or local path of the source code repository for the // project being benchmarked @@ -15,7 +15,7 @@ // List of branches to benchmark. If not provided, defaults to "master" // (for git) or "tip" (for mercurial). - "branches": ["master"], + "branches": ["HEAD"], // The DVCS being used. If not set, it will be automatically // determined from "repo" by looking at the protocol in the URL @@ -35,14 +35,14 @@ // The Pythons you'd like to test against. If not provided, defaults // to the current version of Python used to run `asv`. - "pythons": ["2.7"], + // "pythons": ["3.9"], // The matrix of dependencies to test. Each key is the name of a // package (in PyPI) and the values are version numbers. An empty // list indicates to just test against the default (latest) // version. "matrix": { - "six": [], + "Cython": [], }, // The directory (relative to the current directory) that benchmarks are @@ -68,7 +68,7 @@ // `asv` will cache wheels of the recent builds in each // environment, making them faster to install next time. This is // number of builds to keep, per environment. - "wheel_cache_size": 2, + "build_cache_size": 8, // The commits after which the regression search in `asv publish` // should start looking for regressions. Dictionary whose keys are diff --git a/benchmarks/asv_compare.conf.json.tpl b/benchmarks/asv_compare.conf.json.tpl new file mode 100644 index 000000000000..93d12d4a0b77 --- /dev/null +++ b/benchmarks/asv_compare.conf.json.tpl @@ -0,0 +1,97 @@ +// This config file is almost similar to 'asv.conf.json' except it contains +// custom tokens that can be substituted by 'runtests.py' and ASV, +// due to the necessity to add custom build options when `--bench-compare` +// is used. +{ + // The version of the config file format. Do not change, unless + // you know what you are doing. + "version": 1, + + // The name of the project being benchmarked + "project": "numpy", + + // The project's homepage + "project_url": "https://www.numpy.org/", + + // The URL or local path of the source code repository for the + // project being benchmarked + "repo": "..", + + // List of branches to benchmark. If not provided, defaults to "master" + // (for git) or "tip" (for mercurial). + "branches": ["HEAD"], + + // The DVCS being used. If not set, it will be automatically + // determined from "repo" by looking at the protocol in the URL + // (if remote), or by looking for special directories, such as + // ".git" (if local). + "dvcs": "git", + + // The tool to use to create environments. May be "conda", + // "virtualenv" or other value depending on the plugins in use. + // If missing or the empty string, the tool will be automatically + // determined by looking for tools on the PATH environment + // variable. + "environment_type": "virtualenv", + + // the base URL to show a commit for the project. + "show_commit_url": "https://github.com/numpy/numpy/commit/", + + // The Pythons you'd like to test against. If not provided, defaults + // to the current version of Python used to run `asv`. + // "pythons": ["3.9"], + + // The matrix of dependencies to test. Each key is the name of a + // package (in PyPI) and the values are version numbers. An empty + // list indicates to just test against the default (latest) + // version. + "matrix": { + "Cython": [], + }, + + // The directory (relative to the current directory) that benchmarks are + // stored in. If not provided, defaults to "benchmarks" + "benchmark_dir": "benchmarks", + + // The directory (relative to the current directory) to cache the Python + // environments in. If not provided, defaults to "env" + // NOTE: changes dir name will requires update `generate_asv_config()` in + // runtests.py + "env_dir": "env", + + + // The directory (relative to the current directory) that raw benchmark + // results are stored in. If not provided, defaults to "results". + "results_dir": "results", + + // The directory (relative to the current directory) that the html tree + // should be written to. If not provided, defaults to "html". + "html_dir": "html", + + // The number of characters to retain in the commit hashes. + // "hash_length": 8, + + // `asv` will cache wheels of the recent builds in each + // environment, making them faster to install next time. This is + // number of builds to keep, per environment. + "build_cache_size": 8, + + "build_command" : [ + "python setup.py build {numpy_build_options}", + // pip ignores '--global-option' when pep517 is enabled, we also enabling pip verbose to + // be reached from asv `--verbose` so we can verify the build options. + "PIP_NO_BUILD_ISOLATION=false python {build_dir}/benchmarks/asv_pip_nopep517.py -v {numpy_global_options} --no-deps --no-index -w {build_cache_dir} {build_dir}" + ], + // The commits after which the regression search in `asv publish` + // should start looking for regressions. Dictionary whose keys are + // regexps matching to benchmark names, and values corresponding to + // the commit (exclusive) after which to start looking for + // regressions. The default is to start from the first commit + // with results. If the commit is `null`, regression detection is + // skipped for the matching benchmark. + // + // "regressions_first_commits": { + // "some_benchmark": "352cdf", // Consider regressions only after this commit + // "another_benchmark": null, // Skip regression detection altogether + // } +} diff --git a/benchmarks/asv_pip_nopep517.py b/benchmarks/asv_pip_nopep517.py new file mode 100644 index 000000000000..9ba165493085 --- /dev/null +++ b/benchmarks/asv_pip_nopep517.py @@ -0,0 +1,15 @@ +""" +This file is used by asv_compare.conf.json.tpl. +""" +import subprocess, sys +# pip ignores '--global-option' when pep517 is enabled therefore we disable it. +cmd = [sys.executable, '-mpip', 'wheel', '--no-use-pep517'] +try: + output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, universal_newlines=True) +except Exception as e: + output = str(e.output) +if "no such option" in output: + print("old version of pip, escape '--no-use-pep517'") + cmd.pop() + +subprocess.run(cmd + sys.argv[1:]) diff --git a/benchmarks/benchmarks/__init__.py b/benchmarks/benchmarks/__init__.py index e8a859ff4784..7b9f1d3e688d 100644 --- a/benchmarks/benchmarks/__init__.py +++ b/benchmarks/benchmarks/__init__.py @@ -1,3 +1,53 @@ -from __future__ import absolute_import, division, print_function - from . import common +import sys +import os + +def show_cpu_features(): + from numpy.lib.utils import _opt_info + info = _opt_info() + info = "NumPy CPU features: " + (info if info else 'nothing enabled') + # ASV wrapping stdout & stderr, so we assume having a tty here + if 'SHELL' in os.environ and sys.platform != 'win32': + # to avoid the red color that imposed by ASV + print(f"\033[33m{info}\033[0m") + else: + print(info) + +def dirty_lock(lock_name, lock_on_count=1): + # this lock occurred before each round to avoid duplicate printing + if not hasattr(os, "getppid"): + return False + ppid = os.getppid() + if not ppid or ppid == os.getpid(): + # not sure if this gonna happen, but ASV run each round in + # a separate process so the lock should be based on the parent + # process id only + return False + lock_path = os.path.abspath(os.path.join( + os.path.dirname(__file__), "..", "env", lock_name) + ) + # ASV load the 'benchmark_dir' to discovering the available benchmarks + # the issue here is ASV doesn't capture any strings from stdout or stderr + # during this stage so we escape it and lock on the second increment + try: + with open(lock_path, 'a+') as f: + f.seek(0) + count, _ppid = (f.read().split() + [0, 0])[:2] + count, _ppid = int(count), int(_ppid) + if _ppid == ppid: + if count >= lock_on_count: + return True + count += 1 + else: + count = 0 + f.seek(0) + f.truncate() + f.write(f"{str(count)} {str(ppid)}") + except IOError: + pass + return False + + +# FIXME: there's no official way to provide extra information to the test log +if not dirty_lock("print_cpu_features.lock"): + show_cpu_features() diff --git a/benchmarks/benchmarks/bench_app.py b/benchmarks/benchmarks/bench_app.py index ccf6e4c4af85..d22aa2e09604 100644 --- a/benchmarks/benchmarks/bench_app.py +++ b/benchmarks/benchmarks/bench_app.py @@ -1,11 +1,7 @@ -from __future__ import absolute_import, division, print_function - from .common import Benchmark import numpy as np -from six.moves import xrange - class LaplaceInplace(Benchmark): params = ['inplace', 'normal'] @@ -61,7 +57,7 @@ def setup(self): ntime = 200 self.arrays = [np.random.normal(size=(ntime, nfeat)) - for i in xrange(nsubj)] + for i in range(nsubj)] def maxes_of_dots(self, arrays): """ @@ -74,8 +70,8 @@ def maxes_of_dots(self, arrays): Arrays must agree only on the first dimension. - For numpy it a join benchmark of dot products and max() - on a set of arrays. + Numpy uses this as a simultaneous benchmark of 1) dot products + and 2) max(, axis=). """ feature_scores = ([0] * len(arrays)) for (i, sd) in enumerate(arrays): diff --git a/benchmarks/benchmarks/bench_array_coercion.py b/benchmarks/benchmarks/bench_array_coercion.py new file mode 100644 index 000000000000..2bae4c0024a2 --- /dev/null +++ b/benchmarks/benchmarks/bench_array_coercion.py @@ -0,0 +1,57 @@ +from __future__ import absolute_import, division, print_function + +from .common import Benchmark + +import numpy as np + + +class ArrayCoercionSmall(Benchmark): + # More detailed benchmarks for array coercion, + # some basic benchmarks are in `bench_core.py`. + params = [[range(3), [1], 1, np.array([5], dtype=np.int64), np.int64(5)]] + param_names = ['array_like'] + int64 = np.dtype(np.int64) + + def time_array_invalid_kwarg(self, array_like): + try: + np.array(array_like, ndmin="not-integer") + except TypeError: + pass + + def time_array(self, array_like): + np.array(array_like) + + def time_array_dtype_not_kwargs(self, array_like): + np.array(array_like, self.int64) + + def time_array_no_copy(self, array_like): + np.array(array_like, copy=False) + + def time_array_subok(self, array_like): + np.array(array_like, subok=True) + + def time_array_all_kwargs(self, array_like): + np.array(array_like, dtype=self.int64, copy=False, order="F", + subok=False, ndmin=2) + + def time_asarray(self, array_like): + np.asarray(array_like) + + def time_asarray_dtype(self, array_like): + np.array(array_like, dtype=self.int64) + + def time_asarray_dtype(self, array_like): + np.array(array_like, dtype=self.int64, order="F") + + def time_asanyarray(self, array_like): + np.asarray(array_like) + + def time_asanyarray_dtype(self, array_like): + np.array(array_like, dtype=self.int64) + + def time_asanyarray_dtype(self, array_like): + np.array(array_like, dtype=self.int64, order="F") + + def time_ascontiguousarray(self, array_like): + np.ascontiguousarray(array_like) + diff --git a/benchmarks/benchmarks/bench_core.py b/benchmarks/benchmarks/bench_core.py index 26cffcab1192..30647f4b850f 100644 --- a/benchmarks/benchmarks/bench_core.py +++ b/benchmarks/benchmarks/bench_core.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, division, print_function - from .common import Benchmark import numpy as np @@ -9,8 +7,13 @@ class Core(Benchmark): def setup(self): self.l100 = range(100) self.l50 = range(50) + self.float_l1000 = [float(i) for i in range(1000)] + self.float64_l1000 = [np.float64(i) for i in range(1000)] + self.int_l1000 = list(range(1000)) self.l = [np.arange(1000), np.arange(1000)] + self.l_view = [memoryview(a) for a in self.l] self.l10x10 = np.ones((10, 10)) + self.float64_dtype = np.dtype(np.float64) def time_array_1(self): np.array(1) @@ -24,9 +27,24 @@ def time_array_l1(self): def time_array_l100(self): np.array(self.l100) + def time_array_float_l1000(self): + np.array(self.float_l1000) + + def time_array_float_l1000_dtype(self): + np.array(self.float_l1000, dtype=self.float64_dtype) + + def time_array_float64_l1000(self): + np.array(self.float64_l1000) + + def time_array_int_l1000(self): + np.array(self.int_l1000) + def time_array_l(self): np.array(self.l) + def time_array_l_view(self): + np.array(self.l_view) + def time_vstack_l(self): np.vstack(self.l) @@ -75,6 +93,12 @@ def time_triu_l10x10(self): def time_tril_l10x10(self): np.tril(self.l10x10) + def time_triu_indices_500(self): + np.triu_indices(500) + + def time_tril_indices_500(self): + np.tril_indices(500) + class Temporaries(Benchmark): def setup(self): @@ -97,8 +121,8 @@ def time_large2(self): class CorrConv(Benchmark): - params = [[50, 1000, 1e5], - [10, 100, 1000, 1e4], + params = [[50, 1000, int(1e5)], + [10, 100, 1000, int(1e4)], ['valid', 'same', 'full']] param_names = ['size1', 'size2', 'mode'] @@ -118,7 +142,7 @@ class CountNonzero(Benchmark): params = [ [1, 2, 3], [100, 10000, 1000000], - [bool, int, str, object] + [bool, np.int8, np.int16, np.int32, np.int64, str, object] ] def setup(self, numaxes, size, dtype): @@ -147,6 +171,9 @@ def setup(self, dtype): def time_packbits(self, dtype): np.packbits(self.d) + def time_packbits_little(self, dtype): + np.packbits(self.d, bitorder="little") + def time_packbits_axis0(self, dtype): np.packbits(self.d2, axis=0) @@ -162,13 +189,30 @@ def setup(self): def time_unpackbits(self): np.unpackbits(self.d) + def time_unpackbits_little(self): + np.unpackbits(self.d, bitorder="little") + def time_unpackbits_axis0(self): np.unpackbits(self.d2, axis=0) def time_unpackbits_axis1(self): np.unpackbits(self.d2, axis=1) + def time_unpackbits_axis1_little(self): + np.unpackbits(self.d2, bitorder="little", axis=1) + class Indices(Benchmark): def time_indices(self): np.indices((1000, 500)) + +class VarComplex(Benchmark): + params = [10**n for n in range(1, 9)] + def setup(self, n): + self.arr = np.random.randn(n) + 1j * np.random.randn(n) + + def teardown(self, n): + del self.arr + + def time_var(self, n): + self.arr.var() diff --git a/benchmarks/benchmarks/bench_function_base.py b/benchmarks/benchmarks/bench_function_base.py index a45525793b14..062843d10cc0 100644 --- a/benchmarks/benchmarks/bench_function_base.py +++ b/benchmarks/benchmarks/bench_function_base.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, division, print_function - from .common import Benchmark import numpy as np @@ -49,6 +47,8 @@ class Median(Benchmark): def setup(self): self.e = np.arange(10000, dtype=np.float32) self.o = np.arange(10001, dtype=np.float32) + self.tall = np.random.random((10000, 20)) + self.wide = np.random.random((20, 10000)) def time_even(self): np.median(self.e) @@ -68,6 +68,12 @@ def time_even_small(self): def time_odd_small(self): np.median(self.o[:500], overwrite_input=True) + def time_tall(self): + np.median(self.tall, axis=-1) + + def time_wide(self): + np.median(self.wide, axis=0) + class Percentile(Benchmark): def setup(self): @@ -95,16 +101,169 @@ def time_select_larger(self): np.select(self.cond_large, ([self.d, self.e] * 10)) +def memoize(f): + _memoized = {} + def wrapped(*args): + if args not in _memoized: + _memoized[args] = f(*args) + + return _memoized[args].copy() + + return f + + +class SortGenerator: + # The size of the unsorted area in the "random unsorted area" + # benchmarks + AREA_SIZE = 100 + # The size of the "partially ordered" sub-arrays + BUBBLE_SIZE = 100 + + @staticmethod + @memoize + def random(size, dtype): + """ + Returns a randomly-shuffled array. + """ + arr = np.arange(size, dtype=dtype) + np.random.shuffle(arr) + return arr + + @staticmethod + @memoize + def ordered(size, dtype): + """ + Returns an ordered array. + """ + return np.arange(size, dtype=dtype) + + @staticmethod + @memoize + def reversed(size, dtype): + """ + Returns an array that's in descending order. + """ + return np.arange(size-1, -1, -1, dtype=dtype) + + @staticmethod + @memoize + def uniform(size, dtype): + """ + Returns an array that has the same value everywhere. + """ + return np.ones(size, dtype=dtype) + + @staticmethod + @memoize + def swapped_pair(size, dtype, swap_frac): + """ + Returns an ordered array, but one that has ``swap_frac * size`` + pairs swapped. + """ + a = np.arange(size, dtype=dtype) + for _ in range(int(size * swap_frac)): + x, y = np.random.randint(0, size, 2) + a[x], a[y] = a[y], a[x] + return a + + @staticmethod + @memoize + def sorted_block(size, dtype, block_size): + """ + Returns an array with blocks that are all sorted. + """ + a = np.arange(size, dtype=dtype) + b = [] + if size < block_size: + return a + block_num = size // block_size + for i in range(block_num): + b.extend(a[i::block_num]) + return np.array(b) + + @classmethod + @memoize + def random_unsorted_area(cls, size, dtype, frac, area_size=None): + """ + This type of array has random unsorted areas such that they + compose the fraction ``frac`` of the original array. + """ + if area_size is None: + area_size = cls.AREA_SIZE + + area_num = int(size * frac / area_size) + a = np.arange(size, dtype=dtype) + for _ in range(area_num): + start = np.random.randint(size-area_size) + end = start + area_size + np.random.shuffle(a[start:end]) + return a + + @classmethod + @memoize + def random_bubble(cls, size, dtype, bubble_num, bubble_size=None): + """ + This type of array has ``bubble_num`` random unsorted areas. + """ + if bubble_size is None: + bubble_size = cls.BUBBLE_SIZE + frac = bubble_size * bubble_num / size + + return cls.random_unsorted_area(size, dtype, frac, bubble_size) + + class Sort(Benchmark): + """ + This benchmark tests sorting performance with several + different types of arrays that are likely to appear in + real-world applications. + """ + params = [ + # In NumPy 1.17 and newer, 'merge' can be one of several + # stable sorts, it isn't necessarily merge sort. + ['quick', 'merge', 'heap'], + ['float64', 'int64', 'int16'], + [ + ('random',), + ('ordered',), + ('reversed',), + ('uniform',), + ('sorted_block', 10), + ('sorted_block', 100), + ('sorted_block', 1000), + # ('swapped_pair', 0.01), + # ('swapped_pair', 0.1), + # ('swapped_pair', 0.5), + # ('random_unsorted_area', 0.5), + # ('random_unsorted_area', 0.1), + # ('random_unsorted_area', 0.01), + # ('random_bubble', 1), + # ('random_bubble', 5), + # ('random_bubble', 10), + ], + ] + param_names = ['kind', 'dtype', 'array_type'] + + # The size of the benchmarked arrays. + ARRAY_SIZE = 10000 + + def setup(self, kind, dtype, array_type): + np.random.seed(1234) + array_class = array_type[0] + self.arr = getattr(SortGenerator, array_class)(self.ARRAY_SIZE, dtype, *array_type[1:]) + + def time_sort(self, kind, dtype, array_type): + # Using np.sort(...) instead of arr.sort(...) because it makes a copy. + # This is important because the data is prepared once per benchmark, but + # used across multiple runs. + np.sort(self.arr, kind=kind) + + def time_argsort(self, kind, dtype, array_type): + np.argsort(self.arr, kind=kind) + + +class SortWorst(Benchmark): def setup(self): - self.e = np.arange(10000, dtype=np.float32) - self.o = np.arange(10001, dtype=np.float32) - np.random.seed(25) - np.random.shuffle(self.o) - # quicksort implementations can have issues with equal elements - self.equal = np.ones(10000) - self.many_equal = np.sort(np.arange(10000) % 10) - # quicksort median of 3 worst case self.worst = np.arange(1000000) x = self.worst @@ -113,29 +272,11 @@ def setup(self): x[mid], x[-2] = x[-2], x[mid] x = x[:-2] - def time_sort(self): - np.sort(self.e) - - def time_sort_random(self): - np.sort(self.o) - - def time_sort_inplace(self): - self.e.sort() - - def time_sort_equal(self): - self.equal.sort() - - def time_sort_many_equal(self): - self.many_equal.sort() - def time_sort_worst(self): np.sort(self.worst) - def time_argsort(self): - self.e.argsort() - - def time_argsort_random(self): - self.o.argsort() + # Retain old benchmark name for backward compatibility + time_sort_worst.benchmark_name = "bench_function_base.Sort.time_sort_worst" class Where(Benchmark): diff --git a/benchmarks/benchmarks/bench_import.py b/benchmarks/benchmarks/bench_import.py new file mode 100644 index 000000000000..4b6ecbc7bbb1 --- /dev/null +++ b/benchmarks/benchmarks/bench_import.py @@ -0,0 +1,34 @@ +from subprocess import call +from sys import executable +from timeit import default_timer + +from .common import Benchmark + + +class Import(Benchmark): + timer = default_timer + + def execute(self, command): + call((executable, '-c', command)) + + def time_numpy(self): + self.execute('import numpy') + + def time_numpy_inspect(self): + # What are the savings from avoiding to import the inspect module? + self.execute('import numpy, inspect') + + def time_fft(self): + self.execute('from numpy import fft') + + def time_linalg(self): + self.execute('from numpy import linalg') + + def time_ma(self): + self.execute('from numpy import ma') + + def time_matlib(self): + self.execute('from numpy import matlib') + + def time_random(self): + self.execute('from numpy import random') diff --git a/benchmarks/benchmarks/bench_indexing.py b/benchmarks/benchmarks/bench_indexing.py index a62a2050e283..3206392ea26f 100644 --- a/benchmarks/benchmarks/bench_indexing.py +++ b/benchmarks/benchmarks/bench_indexing.py @@ -1,11 +1,7 @@ -from __future__ import absolute_import, division, print_function - from .common import Benchmark, get_squares_, get_indexes_, get_indexes_rand_ from os.path import join as pjoin import shutil -import sys -import six from numpy import memmap, float32, array import numpy as np from tempfile import mkdtemp @@ -25,19 +21,46 @@ def setup(self, indexes, sel, op): 'indexes_': get_indexes_(), 'indexes_rand_': get_indexes_rand_()} - if sys.version_info[0] >= 3: - code = "def run():\n for a in squares_.values(): a[%s]%s" - else: - code = "def run():\n for a in squares_.itervalues(): a[%s]%s" + code = "def run():\n for a in squares_.values(): a[%s]%s" code = code % (sel, op) - six.exec_(code, ns) + exec(code, ns) self.func = ns['run'] def time_op(self, indexes, sel, op): self.func() +class ScalarIndexing(Benchmark): + params = [[0, 1, 2]] + param_names = ["ndim"] + + def setup(self, ndim): + self.array = np.ones((5,) * ndim) + + def time_index(self, ndim): + # time indexing. + arr = self.array + indx = (1,) * ndim + for i in range(100): + arr[indx] + + def time_assign(self, ndim): + # time assignment from a python scalar + arr = self.array + indx = (1,) * ndim + for i in range(100): + arr[indx] = 5. + + def time_assign_cast(self, ndim): + # time an assignment which may use a cast operation + arr = self.array + indx = (1,) * ndim + val = np.int16(43) + for i in range(100): + arr[indx] = val + + class IndexingSeparate(Benchmark): def setup(self): self.tmp_dir = mkdtemp() diff --git a/benchmarks/benchmarks/bench_io.py b/benchmarks/benchmarks/bench_io.py index 782d4ab30f1b..d5ce9a271cba 100644 --- a/benchmarks/benchmarks/bench_io.py +++ b/benchmarks/benchmarks/bench_io.py @@ -1,8 +1,7 @@ -from __future__ import absolute_import, division, print_function - from .common import Benchmark, get_squares import numpy as np +from io import StringIO class Copy(Benchmark): @@ -20,6 +19,10 @@ def setup(self, typename): def time_memcpy(self, typename): self.d[...] = self.e_d + def time_memcpy_large_out_of_place(self, typename): + l = np.ones(1024**2, dtype=np.dtype(typename)) + l.copy() + def time_cont_assign(self, typename): self.d[...] = 1 @@ -61,4 +64,181 @@ def setup(self): self.squares = get_squares() def time_vb_savez_squares(self): - np.savez('tmp.npz', self.squares) + np.savez('tmp.npz', **self.squares) + + +class LoadtxtCSVComments(Benchmark): + # benchmarks for np.loadtxt comment handling + # when reading in CSV files + + params = [10, int(1e2), int(1e4), int(1e5)] + param_names = ['num_lines'] + + def setup(self, num_lines): + data = [u'1,2,3 # comment'] * num_lines + # unfortunately, timeit will only run setup() + # between repeat events, but not for iterations + # within repeats, so the StringIO object + # will have to be rewinded in the benchmark proper + self.data_comments = StringIO(u'\n'.join(data)) + + def time_comment_loadtxt_csv(self, num_lines): + # benchmark handling of lines with comments + # when loading in from csv files + + # inspired by similar benchmark in pandas + # for read_csv + + # need to rewind StringIO object (unfortunately + # confounding timing result somewhat) for every + # call to timing test proper + np.loadtxt(self.data_comments, + delimiter=u',') + self.data_comments.seek(0) + +class LoadtxtCSVdtypes(Benchmark): + # benchmarks for np.loadtxt operating with + # different dtypes parsed / cast from CSV files + + params = (['float32', 'float64', 'int32', 'int64', + 'complex128', 'str', 'object'], + [10, int(1e2), int(1e4), int(1e5)]) + param_names = ['dtype', 'num_lines'] + + def setup(self, dtype, num_lines): + data = [u'5, 7, 888'] * num_lines + self.csv_data = StringIO(u'\n'.join(data)) + + def time_loadtxt_dtypes_csv(self, dtype, num_lines): + # benchmark loading arrays of various dtypes + # from csv files + + # state-dependent timing benchmark requires + # rewind of StringIO object + + np.loadtxt(self.csv_data, + delimiter=u',', + dtype=dtype) + self.csv_data.seek(0) + +class LoadtxtCSVStructured(Benchmark): + # benchmarks for np.loadtxt operating with + # a structured data type & CSV file + + def setup(self): + num_lines = 50000 + data = [u"M, 21, 72, X, 155"] * num_lines + self.csv_data = StringIO(u'\n'.join(data)) + + def time_loadtxt_csv_struct_dtype(self): + # obligate rewind of StringIO object + # between iterations of a repeat: + + np.loadtxt(self.csv_data, + delimiter=u',', + dtype=[('category_1', 'S1'), + ('category_2', 'i4'), + ('category_3', 'f8'), + ('category_4', 'S1'), + ('category_5', 'f8')]) + self.csv_data.seek(0) + + +class LoadtxtCSVSkipRows(Benchmark): + # benchmarks for loadtxt row skipping when + # reading in csv file data; a similar benchmark + # is present in the pandas asv suite + + params = [0, 500, 10000] + param_names = ['skiprows'] + + def setup(self, skiprows): + np.random.seed(123) + test_array = np.random.rand(100000, 3) + self.fname = 'test_array.csv' + np.savetxt(fname=self.fname, + X=test_array, + delimiter=',') + + def time_skiprows_csv(self, skiprows): + np.loadtxt(self.fname, + delimiter=',', + skiprows=skiprows) + +class LoadtxtReadUint64Integers(Benchmark): + # pandas has a similar CSV reading benchmark + # modified to suit np.loadtxt + + params = [550, 1000, 10000] + param_names = ['size'] + + def setup(self, size): + arr = np.arange(size).astype('uint64') + 2**63 + self.data1 = StringIO(u'\n'.join(arr.astype(str).tolist())) + arr = arr.astype(object) + arr[500] = -1 + self.data2 = StringIO(u'\n'.join(arr.astype(str).tolist())) + + def time_read_uint64(self, size): + # mandatory rewind of StringIO object + # between iterations of a repeat: + np.loadtxt(self.data1) + self.data1.seek(0) + + def time_read_uint64_neg_values(self, size): + # mandatory rewind of StringIO object + # between iterations of a repeat: + np.loadtxt(self.data2) + self.data2.seek(0) + +class LoadtxtUseColsCSV(Benchmark): + # benchmark selective column reading from CSV files + # using np.loadtxt + + params = [2, [1, 3], [1, 3, 5, 7]] + param_names = ['usecols'] + + def setup(self, usecols): + num_lines = 5000 + data = [u'0, 1, 2, 3, 4, 5, 6, 7, 8, 9'] * num_lines + self.csv_data = StringIO(u'\n'.join(data)) + + def time_loadtxt_usecols_csv(self, usecols): + # must rewind StringIO because of state + # dependence of file reading + np.loadtxt(self.csv_data, + delimiter=u',', + usecols=usecols) + self.csv_data.seek(0) + +class LoadtxtCSVDateTime(Benchmark): + # benchmarks for np.loadtxt operating with + # datetime data in a CSV file + + params = [20, 200, 2000, 20000] + param_names = ['num_lines'] + + def setup(self, num_lines): + # create the equivalent of a two-column CSV file + # with date strings in the first column and random + # floating point data in the second column + dates = np.arange('today', 20, dtype=np.datetime64) + np.random.seed(123) + values = np.random.rand(20) + date_line = u'' + + for date, value in zip(dates, values): + date_line += (str(date) + ',' + str(value) + '\n') + + # expand data to specified number of lines + data = date_line * (num_lines // 20) + self.csv_data = StringIO(data) + + def time_loadtxt_csv_datetime(self, num_lines): + # rewind StringIO object -- the timing iterations + # are state-dependent + X = np.loadtxt(self.csv_data, + delimiter=u',', + dtype=([('dates', 'M8[us]'), + ('values', 'float64')])) + self.csv_data.seek(0) diff --git a/benchmarks/benchmarks/bench_itemselection.py b/benchmarks/benchmarks/bench_itemselection.py new file mode 100644 index 000000000000..27fc49e305b8 --- /dev/null +++ b/benchmarks/benchmarks/bench_itemselection.py @@ -0,0 +1,45 @@ +from __future__ import absolute_import, division, print_function + +from .common import Benchmark, TYPES1 + +import numpy as np + + +class Take(Benchmark): + params = [ + [(1000, 1), (1000, 2), (2, 1000, 1), (1000, 3)], + ["raise", "wrap", "clip"], + TYPES1] + param_names = ["shape", "mode", "dtype"] + + def setup(self, shape, mode, dtype): + self.arr = np.ones(shape, dtype) + self.indices = np.arange(1000) + + def time_contiguous(self, shape, mode, dtype): + self.arr.take(self.indices, axis=-2, mode=mode) + + +class PutMask(Benchmark): + params = [ + [True, False], + TYPES1] + param_names = ["values_is_scalar", "dtype"] + + def setup(self, values_is_scalar, dtype): + if values_is_scalar: + self.vals = np.array(1., dtype=dtype) + else: + self.vals = np.ones(1000, dtype=dtype) + + self.arr = np.ones(1000, dtype=dtype) + + self.dense_mask = np.ones(1000, dtype="bool") + self.sparse_mask = np.zeros(1000, dtype="bool") + + def time_dense(self, values_is_scalar, dtype): + np.putmask(self.arr, self.dense_mask, self.vals) + + def time_sparse(self, values_is_scalar, dtype): + np.putmask(self.arr, self.sparse_mask, self.vals) + diff --git a/benchmarks/benchmarks/bench_lib.py b/benchmarks/benchmarks/bench_lib.py index 83f26c9d1170..f7884cd6c309 100644 --- a/benchmarks/benchmarks/bench_lib.py +++ b/benchmarks/benchmarks/bench_lib.py @@ -1,25 +1,139 @@ """Benchmarks for `numpy.lib`.""" -from __future__ import absolute_import, division, print_function - from .common import Benchmark import numpy as np class Pad(Benchmark): - """Benchmarks for `numpy.pad`.""" + """Benchmarks for `numpy.pad`. + + When benchmarking the pad function it is useful to cover scenarios where + the ratio between the size of the input array and the output array differs + significantly (original area vs. padded area). This allows to evaluate for + which scenario a padding algorithm is optimized. Furthermore involving + large range of array sizes ensures that the effects of CPU-bound caching is + visible. + + The table below shows the sizes of the arrays involved in this benchmark: + + +-----------------+----------+-----------+-----------+-----------------+ + | shape | original | padded: 1 | padded: 8 | padded: (0, 32) | + +=================+==========+===========+===========+=================+ + | (2 ** 22,) | 32 MiB | 32.0 MiB | 32.0 MiB | 32.0 MiB | + +-----------------+----------+-----------+-----------+-----------------+ + | (1024, 1024) | 8 MiB | 8.03 MiB | 8.25 MiB | 8.51 MiB | + +-----------------+----------+-----------+-----------+-----------------+ + | (256, 256, 1) | 256 KiB | 786 KiB | 5.08 MiB | 11.6 MiB | + +-----------------+----------+-----------+-----------+-----------------+ + | (4, 4, 4, 4) | 2 KiB | 10.1 KiB | 1.22 MiB | 12.8 MiB | + +-----------------+----------+-----------+-----------+-----------------+ + | (1, 1, 1, 1, 1) | 8 B | 1.90 MiB | 10.8 MiB | 299 MiB | + +-----------------+----------+-----------+-----------+-----------------+ + """ param_names = ["shape", "pad_width", "mode"] params = [ - [(1000,), (10, 100), (10, 10, 10)], - [1, 3, (0, 5)], + # Shape of the input arrays + [(2 ** 22,), (1024, 1024), (256, 128, 1), + (4, 4, 4, 4), (1, 1, 1, 1, 1)], + # Tested pad widths + [1, 8, (0, 32)], + # Tested modes: mean, median, minimum & maximum use the same code path + # reflect & symmetric share a lot of their code path ["constant", "edge", "linear_ramp", "mean", "reflect", "wrap"], ] def setup(self, shape, pad_width, mode): - self.array = np.empty(shape) + # Make sure to fill the array to make the OS page fault + # in the setup phase and not the timed phase + self.array = np.full(shape, fill_value=1, dtype=np.float64) def time_pad(self, shape, pad_width, mode): np.pad(self.array, pad_width, mode) + + +class Nan(Benchmark): + """Benchmarks for nan functions""" + + param_names = ["array_size", "percent_nans"] + params = [ + # sizes of the 1D arrays + [200, int(2e5)], + # percent of np.nan in arrays + [0, 0.1, 2., 50., 90.], + ] + + def setup(self, array_size, percent_nans): + np.random.seed(123) + # produce a randomly shuffled array with the + # approximate desired percentage np.nan content + base_array = np.random.uniform(size=array_size) + base_array[base_array < percent_nans / 100.] = np.nan + self.arr = base_array + + def time_nanmin(self, array_size, percent_nans): + np.nanmin(self.arr) + + def time_nanmax(self, array_size, percent_nans): + np.nanmax(self.arr) + + def time_nanargmin(self, array_size, percent_nans): + np.nanargmin(self.arr) + + def time_nanargmax(self, array_size, percent_nans): + np.nanargmax(self.arr) + + def time_nansum(self, array_size, percent_nans): + np.nansum(self.arr) + + def time_nanprod(self, array_size, percent_nans): + np.nanprod(self.arr) + + def time_nancumsum(self, array_size, percent_nans): + np.nancumsum(self.arr) + + def time_nancumprod(self, array_size, percent_nans): + np.nancumprod(self.arr) + + def time_nanmean(self, array_size, percent_nans): + np.nanmean(self.arr) + + def time_nanvar(self, array_size, percent_nans): + np.nanvar(self.arr) + + def time_nanstd(self, array_size, percent_nans): + np.nanstd(self.arr) + + def time_nanmedian(self, array_size, percent_nans): + np.nanmedian(self.arr) + + def time_nanquantile(self, array_size, percent_nans): + np.nanquantile(self.arr, q=0.2) + + def time_nanpercentile(self, array_size, percent_nans): + np.nanpercentile(self.arr, q=50) + + +class Unique(Benchmark): + """Benchmark for np.unique with np.nan values.""" + + param_names = ["array_size", "percent_nans"] + params = [ + # sizes of the 1D arrays + [200, int(2e5)], + # percent of np.nan in arrays + [0, 0.1, 2., 50., 90.], + ] + + def setup(self, array_size, percent_nans): + np.random.seed(123) + # produce a randomly shuffled array with the + # approximate desired percentage np.nan content + base_array = np.random.uniform(size=array_size) + base_array[base_array < percent_nans / 100.] = np.nan + self.arr = base_array + + def time_unique(self, array_size, percent_nans): + np.unique(self.arr) diff --git a/benchmarks/benchmarks/bench_linalg.py b/benchmarks/benchmarks/bench_linalg.py index a65d510be276..5ed5b6eecd6d 100644 --- a/benchmarks/benchmarks/bench_linalg.py +++ b/benchmarks/benchmarks/bench_linalg.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, division, print_function - from .common import Benchmark, get_squares_, get_indexes_rand, TYPES1 import numpy as np @@ -93,8 +91,8 @@ def setup(self, op, typename): # check that dtype is supported at all try: self.func(self.a[:2, :2]) - except TypeError: - raise NotImplementedError() + except TypeError as e: + raise NotImplementedError() from e def time_op(self, op, typename): self.func(self.a) @@ -106,4 +104,77 @@ def setup(self): self.b = get_indexes_rand()[:100].astype(np.float64) def time_numpy_linalg_lstsq_a__b_float64(self): - np.linalg.lstsq(self.a, self.b) + np.linalg.lstsq(self.a, self.b, rcond=-1) + +class Einsum(Benchmark): + param_names = ['dtype'] + params = [[np.float32, np.float64]] + def setup(self, dtype): + self.one_dim_small = np.arange(600, dtype=dtype) + self.one_dim = np.arange(3000, dtype=dtype) + self.one_dim_big = np.arange(480000, dtype=dtype) + self.two_dim_small = np.arange(1200, dtype=dtype).reshape(30, 40) + self.two_dim = np.arange(240000, dtype=dtype).reshape(400, 600) + self.three_dim_small = np.arange(10000, dtype=dtype).reshape(10,100,10) + self.three_dim = np.arange(24000, dtype=dtype).reshape(20, 30, 40) + # non_contigous arrays + self.non_contigous_dim1_small = np.arange(1, 80, 2, dtype=dtype) + self.non_contigous_dim1 = np.arange(1, 4000, 2, dtype=dtype) + self.non_contigous_dim2 = np.arange(1, 2400, 2, dtype=dtype).reshape(30, 40) + self.non_contigous_dim3 = np.arange(1, 48000, 2, dtype=dtype).reshape(20, 30, 40) + + # outer(a,b): trigger sum_of_products_contig_stride0_outcontig_two + def time_einsum_outer(self, dtype): + np.einsum("i,j", self.one_dim, self.one_dim, optimize=True) + + # multiply(a, b):trigger sum_of_products_contig_two + def time_einsum_multiply(self, dtype): + np.einsum("..., ...", self.two_dim_small, self.three_dim , optimize=True) + + # sum and multiply:trigger sum_of_products_contig_stride0_outstride0_two + def time_einsum_sum_mul(self, dtype): + np.einsum(",i...->", 300, self.three_dim_small, optimize=True) + + # sum and multiply:trigger sum_of_products_stride0_contig_outstride0_two + def time_einsum_sum_mul2(self, dtype): + np.einsum("i...,->", self.three_dim_small, 300, optimize=True) + + # scalar mul: trigger sum_of_products_stride0_contig_outcontig_two + def time_einsum_mul(self, dtype): + np.einsum("i,->i", self.one_dim_big, 300, optimize=True) + + # trigger contig_contig_outstride0_two + def time_einsum_contig_contig(self, dtype): + np.einsum("ji,i->", self.two_dim, self.one_dim_small, optimize=True) + + # trigger sum_of_products_contig_outstride0_one + def time_einsum_contig_outstride0(self, dtype): + np.einsum("i->", self.one_dim_big, optimize=True) + + # outer(a,b): non_contigous arrays + def time_einsum_noncon_outer(self, dtype): + np.einsum("i,j", self.non_contigous_dim1, self.non_contigous_dim1, optimize=True) + + # multiply(a, b):non_contigous arrays + def time_einsum_noncon_multiply(self, dtype): + np.einsum("..., ...", self.non_contigous_dim2, self.non_contigous_dim3 , optimize=True) + + # sum and multiply:non_contigous arrays + def time_einsum_noncon_sum_mul(self, dtype): + np.einsum(",i...->", 300, self.non_contigous_dim3, optimize=True) + + # sum and multiply:non_contigous arrays + def time_einsum_noncon_sum_mul2(self, dtype): + np.einsum("i...,->", self.non_contigous_dim3, 300, optimize=True) + + # scalar mul: non_contigous arrays + def time_einsum_noncon_mul(self, dtype): + np.einsum("i,->i", self.non_contigous_dim1, 300, optimize=True) + + # contig_contig_outstride0_two: non_contigous arrays + def time_einsum_noncon_contig_contig(self, dtype): + np.einsum("ji,i->", self.non_contigous_dim2, self.non_contigous_dim1_small, optimize=True) + + # sum_of_products_contig_outstride0_one:non_contigous arrays + def time_einsum_noncon_contig_outstride0(self, dtype): + np.einsum("i->", self.non_contigous_dim1, optimize=True) diff --git a/benchmarks/benchmarks/bench_ma.py b/benchmarks/benchmarks/bench_ma.py index d313f01dc427..b214c0b86519 100644 --- a/benchmarks/benchmarks/bench_ma.py +++ b/benchmarks/benchmarks/bench_ma.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, division, print_function - from .common import Benchmark import numpy as np @@ -89,7 +87,9 @@ class Concatenate(Benchmark): ] def setup(self, mode, n): - normal = np.zeros((n, n), int) + # avoid np.zeros's lazy allocation that cause page faults during benchmark. + # np.fill will cause pagefaults to happen during setup. + normal = np.full((n, n), 0, int) unmasked = np.ma.zeros((n, n), int) masked = np.ma.array(normal, mask=True) diff --git a/benchmarks/benchmarks/bench_overrides.py b/benchmarks/benchmarks/bench_overrides.py new file mode 100644 index 000000000000..e449517851ec --- /dev/null +++ b/benchmarks/benchmarks/bench_overrides.py @@ -0,0 +1,67 @@ +from .common import Benchmark + +try: + from numpy.core.overrides import array_function_dispatch +except ImportError: + # Don't fail at import time with old Numpy versions + def array_function_dispatch(*args, **kwargs): + def wrap(*args, **kwargs): + return None + return wrap + +import numpy as np + + +def _broadcast_to_dispatcher(array, shape, subok=None): + return (array,) + + +@array_function_dispatch(_broadcast_to_dispatcher) +def mock_broadcast_to(array, shape, subok=False): + pass + + +def _concatenate_dispatcher(arrays, axis=None, out=None): + if out is not None: + arrays = list(arrays) + arrays.append(out) + return arrays + + +@array_function_dispatch(_concatenate_dispatcher) +def mock_concatenate(arrays, axis=0, out=None): + pass + + +class DuckArray: + def __array_function__(self, func, types, args, kwargs): + pass + + +class ArrayFunction(Benchmark): + + def setup(self): + self.numpy_array = np.array(1) + self.numpy_arrays = [np.array(1), np.array(2)] + self.many_arrays = 500 * self.numpy_arrays + self.duck_array = DuckArray() + self.duck_arrays = [DuckArray(), DuckArray()] + self.mixed_arrays = [np.array(1), DuckArray()] + + def time_mock_broadcast_to_numpy(self): + mock_broadcast_to(self.numpy_array, ()) + + def time_mock_broadcast_to_duck(self): + mock_broadcast_to(self.duck_array, ()) + + def time_mock_concatenate_numpy(self): + mock_concatenate(self.numpy_arrays, axis=0) + + def time_mock_concatenate_many(self): + mock_concatenate(self.many_arrays, axis=0) + + def time_mock_concatenate_duck(self): + mock_concatenate(self.duck_arrays, axis=0) + + def time_mock_concatenate_mixed(self): + mock_concatenate(self.mixed_arrays, axis=0) diff --git a/benchmarks/benchmarks/bench_random.py b/benchmarks/benchmarks/bench_random.py index 9d84d83d310a..9482eb04de97 100644 --- a/benchmarks/benchmarks/bench_random.py +++ b/benchmarks/benchmarks/bench_random.py @@ -1,9 +1,12 @@ -from __future__ import absolute_import, division, print_function - from .common import Benchmark import numpy as np +try: + from numpy.random import Generator +except ImportError: + pass + class Random(Benchmark): params = ['normal', 'uniform', 'weibull 1', 'binomial 10 0.5', @@ -69,14 +72,113 @@ def time_randint_slow(self, name): class Permutation(Benchmark): def setup(self): self.n = 10000 - self.a_1d = np.random.random_sample(self.n) - self.a_2d = np.random.random_sample((self.n, 2)) - + self.a_1d = np.random.random(self.n) + self.a_2d = np.random.random((self.n, 2)) + def time_permutation_1d(self): np.random.permutation(self.a_1d) def time_permutation_2d(self): - np.random.permutation(self.a_2d) + np.random.permutation(self.a_2d) def time_permutation_int(self): np.random.permutation(self.n) + +nom_size = 100000 + +class RNG(Benchmark): + param_names = ['rng'] + params = ['PCG64', 'MT19937', 'Philox', 'SFC64', 'numpy'] + + def setup(self, bitgen): + if bitgen == 'numpy': + self.rg = np.random.RandomState() + else: + self.rg = Generator(getattr(np.random, bitgen)()) + self.rg.random() + self.int32info = np.iinfo(np.int32) + self.uint32info = np.iinfo(np.uint32) + self.uint64info = np.iinfo(np.uint64) + + def time_raw(self, bitgen): + if bitgen == 'numpy': + self.rg.random_integers(self.int32info.max, size=nom_size) + else: + self.rg.integers(self.int32info.max, size=nom_size, endpoint=True) + + def time_32bit(self, bitgen): + min, max = self.uint32info.min, self.uint32info.max + if bitgen == 'numpy': + self.rg.randint(min, max + 1, nom_size, dtype=np.uint32) + else: + self.rg.integers(min, max + 1, nom_size, dtype=np.uint32) + + def time_64bit(self, bitgen): + min, max = self.uint64info.min, self.uint64info.max + if bitgen == 'numpy': + self.rg.randint(min, max + 1, nom_size, dtype=np.uint64) + else: + self.rg.integers(min, max + 1, nom_size, dtype=np.uint64) + + def time_normal_zig(self, bitgen): + self.rg.standard_normal(nom_size) + +class Bounded(Benchmark): + u8 = np.uint8 + u16 = np.uint16 + u32 = np.uint32 + u64 = np.uint64 + param_names = ['rng', 'dt_max'] + params = [['PCG64', 'MT19937', 'Philox', 'SFC64', 'numpy'], + [[u8, 95], + [u8, 64], # Worst case for legacy + [u8, 127], # Best case for legacy + [u16, 95], + [u16, 1024], # Worst case for legacy + [u16, 1535], # Typ. avg. case for legacy + [u16, 2047], # Best case for legacy + [u32, 1024], # Worst case for legacy + [u32, 1535], # Typ. avg. case for legacy + [u32, 2047], # Best case for legacy + [u64, 95], + [u64, 1024], # Worst case for legacy + [u64, 1535], # Typ. avg. case for legacy + [u64, 2047], # Best case for legacy + ]] + + def setup(self, bitgen, args): + if bitgen == 'numpy': + self.rg = np.random.RandomState() + else: + self.rg = Generator(getattr(np.random, bitgen)()) + self.rg.random() + + def time_bounded(self, bitgen, args): + """ + Timer for 8-bit bounded values. + + Parameters (packed as args) + ---------- + dt : {uint8, uint16, uint32, unit64} + output dtype + max : int + Upper bound for range. Lower is always 0. Must be <= 2**bits. + """ + dt, max = args + if bitgen == 'numpy': + self.rg.randint(0, max + 1, nom_size, dtype=dt) + else: + self.rg.integers(0, max + 1, nom_size, dtype=dt) + +class Choice(Benchmark): + params = [1e3, 1e6, 1e8] + + def setup(self, v): + self.a = np.arange(v) + self.rng = np.random.default_rng() + + def time_legacy_choice(self, v): + np.random.choice(self.a, 1000, replace=False) + + def time_choice(self, v): + self.rng.choice(self.a, 1000, replace=False) diff --git a/benchmarks/benchmarks/bench_records.py b/benchmarks/benchmarks/bench_records.py new file mode 100644 index 000000000000..2d9c104d2904 --- /dev/null +++ b/benchmarks/benchmarks/bench_records.py @@ -0,0 +1,40 @@ +from .common import Benchmark + +import numpy as np + + +class Records(Benchmark): + def setup(self): + self.l50 = np.arange(1000) + self.fields_number = 10000 + self.arrays = [self.l50 for _ in range(self.fields_number)] + self.formats = [self.l50.dtype.str for _ in range(self.fields_number)] + self.formats_str = ','.join(self.formats) + self.dtype_ = np.dtype( + [ + ('field_{}'.format(i), self.l50.dtype.str) + for i in range(self.fields_number) + ] + ) + self.buffer = self.l50.tostring() * self.fields_number + + def time_fromarrays_w_dtype(self): + np.core.records.fromarrays(self.arrays, dtype=self.dtype_) + + def time_fromarrays_wo_dtype(self): + np.core.records.fromarrays(self.arrays) + + def time_fromarrays_formats_as_list(self): + np.core.records.fromarrays(self.arrays, formats=self.formats) + + def time_fromarrays_formats_as_string(self): + np.core.records.fromarrays(self.arrays, formats=self.formats_str) + + def time_fromstring_w_dtype(self): + np.core.records.fromstring(self.buffer, dtype=self.dtype_) + + def time_fromstring_formats_as_list(self): + np.core.records.fromstring(self.buffer, formats=self.formats) + + def time_fromstring_formats_as_string(self): + np.core.records.fromstring(self.buffer, formats=self.formats_str) diff --git a/benchmarks/benchmarks/bench_reduce.py b/benchmarks/benchmarks/bench_reduce.py index 353eb980c6de..7b05f4fcce31 100644 --- a/benchmarks/benchmarks/bench_reduce.py +++ b/benchmarks/benchmarks/bench_reduce.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, division, print_function - from .common import Benchmark, TYPES1, get_squares import numpy as np @@ -29,8 +27,10 @@ def time_reduce(self, axis, typename): class AnyAll(Benchmark): def setup(self): - self.zeros = np.zeros(100000, bool) - self.ones = np.ones(100000, bool) + # avoid np.zeros's lazy allocation that would + # cause page faults during benchmark + self.zeros = np.full(100000, 0, bool) + self.ones = np.full(100000, 1, bool) def time_all_fast(self): self.zeros.all() @@ -58,6 +58,15 @@ def time_min(self, dtype): def time_max(self, dtype): np.max(self.d) +class ArgMax(Benchmark): + params = [np.float32, bool] + param_names = ['dtype'] + + def setup(self, dtype): + self.d = np.zeros(200000, dtype=dtype) + + def time_argmax(self, dtype): + np.argmax(self.d) class SmallReduction(Benchmark): def setup(self): diff --git a/benchmarks/benchmarks/bench_scalar.py b/benchmarks/benchmarks/bench_scalar.py new file mode 100644 index 000000000000..219e48bede94 --- /dev/null +++ b/benchmarks/benchmarks/bench_scalar.py @@ -0,0 +1,33 @@ +from .common import Benchmark, TYPES1 + +import numpy as np + + +class ScalarMath(Benchmark): + # Test scalar math, note that each of these is run repeatedly to offset + # the function call overhead to some degree. + params = [TYPES1] + param_names = ["type"] + def setup(self, typename): + self.num = np.dtype(typename).type(2) + + def time_addition(self, typename): + n = self.num + res = n + n + n + n + n + n + n + n + n + n + + def time_addition_pyint(self, typename): + n = self.num + res = n + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + + def time_multiplication(self, typename): + n = self.num + res = n * n * n * n * n * n * n * n * n * n + + def time_power_of_two(self, typename): + n = self.num + res = n**2, n**2, n**2, n**2, n**2, n**2, n**2, n**2, n**2, n**2 + + def time_abs(self, typename): + n = self.num + res = abs(abs(abs(abs(abs(abs(abs(abs(abs(abs(n)))))))))) + diff --git a/benchmarks/benchmarks/bench_shape_base.py b/benchmarks/benchmarks/bench_shape_base.py index 9d0f0ae04547..0c7dc4e728ec 100644 --- a/benchmarks/benchmarks/bench_shape_base.py +++ b/benchmarks/benchmarks/bench_shape_base.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, division, print_function - from .common import Benchmark import numpy as np @@ -23,7 +21,9 @@ def setup(self, n): self.four_1d = np.ones(6 * n) self.five_0d = np.ones(1 * n) self.six_1d = np.ones(5 * n) - self.zero_2d = np.zeros((2 * n, 6 * n)) + # avoid np.zeros's lazy allocation that might cause + # page faults during benchmark + self.zero_2d = np.full((2 * n, 6 * n), 0) self.one = np.ones(3 * n) self.two = 2 * np.ones((3, 3 * n)) @@ -31,19 +31,9 @@ def setup(self, n): self.four = 4 * np.ones(3 * n) self.five = 5 * np.ones(1 * n) self.six = 6 * np.ones(5 * n) - self.zero = np.zeros((2 * n, 6 * n)) - - self.a000 = np.ones((2 * n, 2 * n, 2 * n), int) * 1 - - self.a100 = np.ones((3 * n, 2 * n, 2 * n), int) * 2 - self.a010 = np.ones((2 * n, 3 * n, 2 * n), int) * 3 - self.a001 = np.ones((2 * n, 2 * n, 3 * n), int) * 4 - - self.a011 = np.ones((2 * n, 3 * n, 3 * n), int) * 5 - self.a101 = np.ones((3 * n, 2 * n, 3 * n), int) * 6 - self.a110 = np.ones((3 * n, 3 * n, 2 * n), int) * 7 - - self.a111 = np.ones((3 * n, 3 * n, 3 * n), int) * 8 + # avoid np.zeros's lazy allocation that might cause + # page faults during benchmark + self.zero = np.full((2 * n, 6 * n), 0) def time_block_simple_row_wise(self, n): np.block([self.a_2d, self.b_2d]) @@ -72,8 +62,56 @@ def time_nested(self, n): [self.zero] ]) - def time_3d(self, n): - np.block([ + def time_no_lists(self, n): + np.block(1) + np.block(np.eye(3 * n)) + + +class Block2D(Benchmark): + params = [[(16, 16), (32, 32), (64, 64), (128, 128), (256, 256), (512, 512), (1024, 1024)], + ['uint8', 'uint16', 'uint32', 'uint64'], + [(2, 2), (4, 4)]] + param_names = ['shape', 'dtype', 'n_chunks'] + + def setup(self, shape, dtype, n_chunks): + + self.block_list = [ + [np.full(shape=[s//n_chunk for s, n_chunk in zip(shape, n_chunks)], + fill_value=1, dtype=dtype) for _ in range(n_chunks[1])] + for _ in range(n_chunks[0]) + ] + + def time_block2d(self, shape, dtype, n_chunks): + np.block(self.block_list) + + +class Block3D(Benchmark): + """This benchmark concatenates an array of size ``(5n)^3``""" + # Having copy as a `mode` of the block3D + # allows us to directly compare the benchmark of block + # to that of a direct memory copy into new buffers with + # the ASV framework. + # block and copy will be plotted on the same graph + # as opposed to being displayed as separate benchmarks + params = [[1, 10, 100], + ['block', 'copy']] + param_names = ['n', 'mode'] + + def setup(self, n, mode): + # Slow setup method: hence separated from the others above + self.a000 = np.ones((2 * n, 2 * n, 2 * n), int) * 1 + + self.a100 = np.ones((3 * n, 2 * n, 2 * n), int) * 2 + self.a010 = np.ones((2 * n, 3 * n, 2 * n), int) * 3 + self.a001 = np.ones((2 * n, 2 * n, 3 * n), int) * 4 + + self.a011 = np.ones((2 * n, 3 * n, 3 * n), int) * 5 + self.a101 = np.ones((3 * n, 2 * n, 3 * n), int) * 6 + self.a110 = np.ones((3 * n, 3 * n, 2 * n), int) * 7 + + self.a111 = np.ones((3 * n, 3 * n, 3 * n), int) * 8 + + self.block = [ [ [self.a000, self.a001], [self.a010, self.a011], @@ -82,8 +120,17 @@ def time_3d(self, n): [self.a100, self.a101], [self.a110, self.a111], ] - ]) - - def time_no_lists(self, n): - np.block(1) - np.block(np.eye(3 * n)) + ] + self.arr_list = [a + for two_d in self.block + for one_d in two_d + for a in one_d] + + def time_3d(self, n, mode): + if mode == 'block': + np.block(self.block) + else: # mode == 'copy' + [arr.copy() for arr in self.arr_list] + + # Retain old benchmark name for backward compat + time_3d.benchmark_name = "bench_shape_base.Block.time_3d" diff --git a/benchmarks/benchmarks/bench_trim_zeros.py b/benchmarks/benchmarks/bench_trim_zeros.py new file mode 100644 index 000000000000..4e25a8b021b7 --- /dev/null +++ b/benchmarks/benchmarks/bench_trim_zeros.py @@ -0,0 +1,27 @@ +from .common import Benchmark + +import numpy as np + +_FLOAT = np.dtype('float64') +_COMPLEX = np.dtype('complex128') +_INT = np.dtype('int64') +_BOOL = np.dtype('bool') + + +class TrimZeros(Benchmark): + param_names = ["dtype", "size"] + params = [ + [_INT, _FLOAT, _COMPLEX, _BOOL], + [3000, 30_000, 300_000] + ] + + def setup(self, dtype, size): + n = size // 3 + self.array = np.hstack([ + np.zeros(n), + np.random.uniform(size=n), + np.zeros(n), + ]).astype(dtype) + + def time_trim_zeros(self, dtype, size): + np.trim_zeros(self.array) diff --git a/benchmarks/benchmarks/bench_ufunc.py b/benchmarks/benchmarks/bench_ufunc.py index 1d4e70a3ad93..b036581e1aae 100644 --- a/benchmarks/benchmarks/bench_ufunc.py +++ b/benchmarks/benchmarks/bench_ufunc.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, division, print_function - from .common import Benchmark, get_squares_ import numpy as np @@ -10,15 +8,17 @@ 'bitwise_or', 'bitwise_xor', 'cbrt', 'ceil', 'conj', 'conjugate', 'copysign', 'cos', 'cosh', 'deg2rad', 'degrees', 'divide', 'divmod', 'equal', 'exp', 'exp2', 'expm1', 'fabs', 'float_power', 'floor', - 'floor_divide', 'fmax', 'fmin', 'fmod', 'frexp', 'greater', - 'greater_equal', 'heaviside', 'hypot', 'invert', 'isfinite', 'isinf', - 'isnan', 'isnat', 'ldexp', 'left_shift', 'less', 'less_equal', 'log', - 'log10', 'log1p', 'log2', 'logaddexp', 'logaddexp2', 'logical_and', - 'logical_not', 'logical_or', 'logical_xor', 'maximum', 'minimum', - 'mod', 'modf', 'multiply', 'negative', 'nextafter', 'not_equal', - 'positive', 'power', 'rad2deg', 'radians', 'reciprocal', 'remainder', - 'right_shift', 'rint', 'sign', 'signbit', 'sin', 'sinh', 'spacing', - 'sqrt', 'square', 'subtract', 'tan', 'tanh', 'true_divide', 'trunc'] + 'floor_divide', 'fmax', 'fmin', 'fmod', 'frexp', 'gcd', 'greater', + 'greater_equal', 'heaviside', 'hypot', 'invert', 'isfinite', + 'isinf', 'isnan', 'isnat', 'lcm', 'ldexp', 'left_shift', 'less', + 'less_equal', 'log', 'log10', 'log1p', 'log2', 'logaddexp', + 'logaddexp2', 'logical_and', 'logical_not', 'logical_or', + 'logical_xor', 'matmul', 'maximum', 'minimum', 'mod', 'modf', 'multiply', + 'negative', 'nextafter', 'not_equal', 'positive', 'power', + 'rad2deg', 'radians', 'reciprocal', 'remainder', 'right_shift', + 'rint', 'sign', 'signbit', 'sin', 'sinh', 'spacing', 'sqrt', + 'square', 'subtract', 'tan', 'tanh', 'true_divide', 'trunc'] + for name in dir(np): if isinstance(getattr(np, name, None), np.ufunc) and name not in ufuncs: @@ -134,6 +134,23 @@ def time_less_than_scalar2(self, dtype): (self.d < 1) +class CustomScalarFloorDivideInt(Benchmark): + params = (np.sctypes['int'] + np.sctypes['uint'], [8, -8, 43, -43]) + param_names = ['dtype', 'divisors'] + + def setup(self, dtype, divisor): + if dtype in np.sctypes['uint'] and divisor < 0: + raise NotImplementedError( + "Skipping test for negative divisor with unsigned type") + + iinfo = np.iinfo(dtype) + self.x = np.random.randint( + iinfo.min, iinfo.max, size=10000, dtype=dtype) + + def time_floor_divide_int(self, dtype, divisor): + self.x // divisor + + class Scalar(Benchmark): def setup(self): self.x = np.asarray(1.0) @@ -148,3 +165,62 @@ def time_add_scalar_conv(self): def time_add_scalar_conv_complex(self): (self.y + self.z) + + +class ArgPack: + __slots__ = ['args', 'kwargs'] + def __init__(self, *args, **kwargs): + self.args = args + self.kwargs = kwargs + def __repr__(self): + return '({})'.format(', '.join( + [repr(a) for a in self.args] + + ['{}={}'.format(k, repr(v)) for k, v in self.kwargs.items()] + )) + + +class ArgParsing(Benchmark): + # In order to benchmark the speed of argument parsing, all but the + # out arguments are chosen such that they have no effect on the + # calculation. In particular, subok=True and where=True are + # defaults, and the dtype is the correct one (the latter will + # still have some effect on the search for the correct inner loop). + x = np.array(1.) + y = np.array(2.) + out = np.array(3.) + param_names = ['arg_kwarg'] + params = [[ + ArgPack(x, y), + ArgPack(x, y, out), + ArgPack(x, y, out=out), + ArgPack(x, y, out=(out,)), + ArgPack(x, y, out=out, subok=True, where=True), + ArgPack(x, y, subok=True), + ArgPack(x, y, subok=True, where=True), + ArgPack(x, y, out, subok=True, where=True) + ]] + + def time_add_arg_parsing(self, arg_pack): + np.add(*arg_pack.args, **arg_pack.kwargs) + + +class ArgParsingReduce(Benchmark): + # In order to benchmark the speed of argument parsing, all but the + # out arguments are chosen such that they have minimal effect on the + # calculation. + a = np.arange(2.) + out = np.array(0.) + param_names = ['arg_kwarg'] + params = [[ + ArgPack(a,), + ArgPack(a, 0), + ArgPack(a, axis=0), + ArgPack(a, 0, None), + ArgPack(a, axis=0, dtype=None), + ArgPack(a, 0, None, out), + ArgPack(a, axis=0, dtype=None, out=out), + ArgPack(a, out=out) + ]] + + def time_add_reduce_arg_parsing(self, arg_pack): + np.add.reduce(*arg_pack.args, **arg_pack.kwargs) diff --git a/benchmarks/benchmarks/bench_ufunc_strides.py b/benchmarks/benchmarks/bench_ufunc_strides.py new file mode 100644 index 000000000000..75aa510a6b81 --- /dev/null +++ b/benchmarks/benchmarks/bench_ufunc_strides.py @@ -0,0 +1,185 @@ +from .common import Benchmark + +import numpy as np + +UNARY_UFUNCS = [obj for obj in np.core.umath.__dict__.values() if + isinstance(obj, np.ufunc)] +UNARY_OBJECT_UFUNCS = [uf for uf in UNARY_UFUNCS if "O->O" in uf.types] +UNARY_OBJECT_UFUNCS.remove(getattr(np, 'invert')) + +stride = [1, 2, 4] +stride_out = [1, 2, 4] +dtype = ['f', 'd'] + +class Unary(Benchmark): + params = [UNARY_OBJECT_UFUNCS, stride, stride_out, dtype] + param_names = ['ufunc', 'stride_in', 'stride_out', 'dtype'] + timeout = 10 + + def setup(self, ufuncname, stride, stride_out, dtype): + np.seterr(all='ignore') + try: + self.f = ufuncname + except AttributeError: + raise NotImplementedError(f"No ufunc {ufuncname} found") from None + N = 100000 + self.arr_out = np.empty(stride_out*N, dtype) + self.arr = np.random.rand(stride*N).astype(dtype) + if (ufuncname.__name__ == 'arccosh'): + self.arr = 1.0 + self.arr + + def time_ufunc(self, ufuncname, stride, stride_out, dtype): + self.f(self.arr[::stride], self.arr_out[::stride_out]) + +class AVX_UFunc_log(Benchmark): + params = [stride, dtype] + param_names = ['stride', 'dtype'] + timeout = 10 + + def setup(self, stride, dtype): + np.seterr(all='ignore') + N = 10000 + self.arr = np.array(np.random.random_sample(stride*N), dtype=dtype) + + def time_log(self, stride, dtype): + np.log(self.arr[::stride]) + +avx_bfuncs = ['maximum', + 'minimum'] + +class AVX_BFunc(Benchmark): + + params = [avx_bfuncs, dtype, stride] + param_names = ['avx_based_bfunc', 'dtype', 'stride'] + timeout = 10 + + def setup(self, ufuncname, dtype, stride): + np.seterr(all='ignore') + try: + self.f = getattr(np, ufuncname) + except AttributeError: + raise NotImplementedError(f"No ufunc {ufuncname} found") from None + N = 10000 + self.arr1 = np.array(np.random.rand(stride*N), dtype=dtype) + self.arr2 = np.array(np.random.rand(stride*N), dtype=dtype) + + def time_ufunc(self, ufuncname, dtype, stride): + self.f(self.arr1[::stride], self.arr2[::stride]) + +class AVX_ldexp(Benchmark): + + params = [dtype, stride] + param_names = ['dtype', 'stride'] + timeout = 10 + + def setup(self, dtype, stride): + np.seterr(all='ignore') + self.f = getattr(np, 'ldexp') + N = 10000 + self.arr1 = np.array(np.random.rand(stride*N), dtype=dtype) + self.arr2 = np.array(np.random.rand(stride*N), dtype='i') + + def time_ufunc(self, dtype, stride): + self.f(self.arr1[::stride], self.arr2[::stride]) + +cmplx_bfuncs = ['add', + 'subtract', + 'multiply', + 'divide'] +cmplxstride = [1, 2, 4] +cmplxdtype = ['F', 'D'] + +class AVX_cmplx_arithmetic(Benchmark): + params = [cmplx_bfuncs, cmplxstride, cmplxdtype] + param_names = ['bfunc', 'stride', 'dtype'] + timeout = 10 + + def setup(self, bfuncname, stride, dtype): + np.seterr(all='ignore') + try: + self.f = getattr(np, bfuncname) + except AttributeError: + raise NotImplementedError(f"No bfunc {bfuncname} found") from None + N = 10000 + self.arr1 = np.ones(stride*N, dtype) + self.arr2 = np.ones(stride*N, dtype) + + def time_ufunc(self, bfuncname, stride, dtype): + self.f(self.arr1[::stride], self.arr2[::stride]) + +cmplx_ufuncs = ['reciprocal', + 'absolute', + 'square', + 'conjugate'] + +class AVX_cmplx_funcs(Benchmark): + params = [cmplx_ufuncs, cmplxstride, cmplxdtype] + param_names = ['bfunc', 'stride', 'dtype'] + timeout = 10 + + def setup(self, bfuncname, stride, dtype): + np.seterr(all='ignore') + try: + self.f = getattr(np, bfuncname) + except AttributeError: + raise NotImplementedError(f"No bfunc {bfuncname} found") from None + N = 10000 + self.arr1 = np.ones(stride*N, dtype) + + def time_ufunc(self, bfuncname, stride, dtype): + self.f(self.arr1[::stride]) + +class Mandelbrot(Benchmark): + def f(self,z): + return np.abs(z) < 4.0 + + def g(self,z,c): + return np.sum(np.multiply(z,z) + c) + + def mandelbrot_numpy(self, c, maxiter): + output = np.zeros(c.shape, np.int) + z = np.empty(c.shape, np.complex64) + for it in range(maxiter): + notdone = self.f(z) + output[notdone] = it + z[notdone] = self.g(z[notdone],c[notdone]) + output[output == maxiter-1] = 0 + return output + + def mandelbrot_set(self,xmin,xmax,ymin,ymax,width,height,maxiter): + r1 = np.linspace(xmin, xmax, width, dtype=np.float32) + r2 = np.linspace(ymin, ymax, height, dtype=np.float32) + c = r1 + r2[:,None]*1j + n3 = self.mandelbrot_numpy(c,maxiter) + return (r1,r2,n3.T) + + def time_mandel(self): + self.mandelbrot_set(-0.74877,-0.74872,0.06505,0.06510,1000,1000,2048) + +class LogisticRegression(Benchmark): + param_names = ['dtype'] + params = [np.float32, np.float64] + + timeout = 1000 + def train(self, max_epoch): + for epoch in range(max_epoch): + z = np.matmul(self.X_train, self.W) + A = 1 / (1 + np.exp(-z)) # sigmoid(z) + loss = -np.mean(self.Y_train * np.log(A) + (1-self.Y_train) * np.log(1-A)) + dz = A - self.Y_train + dw = (1/self.size) * np.matmul(self.X_train.T, dz) + self.W = self.W - self.alpha*dw + + def setup(self, dtype): + np.random.seed(42) + self.size = 250 + features = 16 + self.X_train = np.random.rand(self.size,features).astype(dtype) + self.Y_train = np.random.choice(2,self.size).astype(dtype) + # Initialize weights + self.W = np.zeros((features,1), dtype=dtype) + self.b = np.zeros((1,1), dtype=dtype) + self.alpha = 0.1 + + def time_train(self, dtype): + self.train(1000) diff --git a/benchmarks/benchmarks/common.py b/benchmarks/benchmarks/common.py index 18a09fd40551..0c40e85b0612 100644 --- a/benchmarks/benchmarks/common.py +++ b/benchmarks/benchmarks/common.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, division, print_function - import numpy import random @@ -16,14 +14,14 @@ # time-consuming functions (ufunc, linalg, etc) nxs, nys = 100, 100 -# a set of interesting types to test +# a list of interesting types to test TYPES1 = [ 'int16', 'float16', 'int32', 'float32', 'int64', 'float64', 'complex64', 'longfloat', 'complex128', ] -if 'complex256' in numpy.typeDict: +if 'complex256' in numpy.sctypeDict: TYPES1.append('complex256') @@ -112,5 +110,5 @@ def get_indexes_rand_(): return indexes_rand_ -class Benchmark(object): - goal_time = 0.25 +class Benchmark: + pass diff --git a/branding/icons/numpylogo.svg b/branding/icons/numpylogo.svg deleted file mode 100644 index bd9b834da3be..000000000000 --- a/branding/icons/numpylogo.svg +++ /dev/null @@ -1,7109 +0,0 @@ - - - - - - - - - - - - -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - eJzsveuOJMeRJvq/gX6HPD8GEHFW1XG/CAcLRGRGarWQREKiZmeOsCCK7CLZO91V3OrumeV5+mN3 -N/fwyKyqLHEoqtNZxerITI/wm7m52Wef/dP/9cWffz29vvv65tf1VbF7+eKf/ml/f3P94e7+Nzu6 -vPvd27cf33+4x0u/+tNnu7K6KvBT0++Gr+ST/3xz//7N3e1v6L2rEt894vd/9eX99b+/eb/75+uP -333/4bPdr5bbD9/f4d//Zfe722+uPsNPfvnmw9sb+Oztx3c//Pj27ru7q+s3n9ljQLWH6w/wfvuq -6l5VRTHs+t+03e6LP+BH5ruPt6/f3H433/2f3+yGXTlWu37od21BT/jf3vzp5n38katxbMoGP3hV -dG0Dn+6umgK+1ozjVVW2A37vcPfNx3c3tx++uL/75ub9+/3d27v797/Z7X+8vt394fo7eOd69683 -b9/e/cdufnv9zb/57xzvbj/AZ7+4fgvPfnv3e/j58OMPN7/+092761v/wT/e3Ly+eX3u49Pv2q+O -b97eQHe+u/4Az1/QxbL6av745u3rP3589/UNdHRVtHS9/ooe9i/v4SnhgfFvut5/9bt3cOnPNx/g -Lt/BDal//vTbeQ8je/eOPggXqfzqr3+6+e4NDTh0/v/8TD7pO4M+etVVfdOW8Effd01f7n7127d3 -X1+/3f3h5vWbDzf399e3N9Bh89uPN1TH/021h4998fH+Jn63xLv7d397f3NzK2+X/HD+7T/dvA5v -XjXjUNSj+8yf//fH6/ff2yd87Tx6n0mffXnz7gfo/xuaIk1RXLW7CqZD6//Wj8JY0MfKbtfB7Gl2 -7djIe2Ha3Pz7m5v/+M3uj3e3NzKG0/2HP7/5/2BMhqLYdUUhl//08e3N/V9u3+AcqOjayCP4h7vX -N2/hLuHrx7fXNHDSi/ZbPvHl9f13Nx9gvt+9/fiB1uJgd4FZ8vvrH2/u3U0+/+Hm9su7f6bH/HUF -C6EbauyjYix2A7StHOgOsFJKvRf3oD4RVoBf14r7Hvv5C5hkn9+/+e7N7W/00fqvfnv/5nWYeX0F -C5V+UcVXg/sZ9UeeEtr84cPNrfYDzPr9H9wcLq7+8Ge863L7en/3Drv+PQkFmL63MF1Blsi74R/0 -HlTx8YeXL/768kU9vvrfH+8+3LyHGt/e7Mbu1XcgsW5Ajg2vlo/3d7uyLtwnvr5+f/PqWxieN7d8 -9fXXfA2m05sf3r+B2796ff3ddzf38j94/9U3b+5hXnz79ub/vPrh5h7l3/vr29ev/vzN9f3d7avv -YBlT3W9vvv3w6vNlVzYt140XoD76G7r0+w92S/qo/oPf+vojXP3w6ub2NUz5Vzfv6H8fQELcvIKF -/Prm3fU9iKlX75Ob8pfvbl69vgMh/P79m13Zjq/+9fWbm3to8fvdq/c/XH8D3dE1r775eH9/c/vN -j/CP7tXX93f/dnP79TVIl7IbXunnX31z98OPUuf9629v3r25fXMLX++rV9D/b765fgvi7dX3P/7w -/c3tq3uSMvDF16/eXX+DjwXdCoP06gfYbuCbH9+/+vAfd+8/Qqe9ubt/9eF7EAb2r+tvPn64efXu -I0zU+hVde/0NjD/V9g3In7dvr6EmWH/2DXigd9fvv/n4lp5oGPBNEBD38B388/vrt9/yPeTie9wk -Xk00H6Cqie84udGcuH8na/2EDXg1La/28givFvr6q4W+DLUs7uuLfe93/Knf8S1+5z7zO/vM8uH7 -V3+kG0I1n/MXPucvfO6+8Dk/0+f2vXcf335488PbH199/v4tzom/aIP+wl/+i/vyX+xb/8pvfvn9 -3T3MlhvYe25hqr1/dc03vtb2XLtvX/Otr62Sa+qO65tX32h33PDXb7j2m/BlqOrGvveGP/WGP/XG -3eKNfeYGuuOWb3jHH7/TZ7pzX7iTj9j3Xr/59zd4gTvjI3/1I9/pY/Q8H+07P/LbH6gzftTLL198 -ubAsbP/7V1++B8nvBH79FQma5fabO9Q9frP7Kr/Db278f3218Y1XGxXxvvDl//uS7g4yT++9+/L+ -482X8EER3PRgX8GTfgHD84FW3B9/oPeGr754+xHe/O393ccffnf77d3LF79iDfCL6w/fw1YN0uU9 -KHF8jf+546/A1d+/+Xe9CArcD5+dqRI0w2/w6T7/+n/dfIOqoVwIf/35I2gRD6nqC9wk7m8/v+XH -vP/4/vvdl3d3b+1R5QPylj0xbKr8nZ/NTezDuRvAmz/fyvfXsAPCavrh+zff5OrPvG832vjuQ24L -myj2eu6O8Vt2s/U3fh73wY7+9s3ta/gKTfzQc3fvfsAjzO7P31//gJfxk0f3yYc8/+HmWzhAuLGl -q8vtv9+8vfvhJly3KyDvd//j+v6HB3XOj+++vnv75v270Cfuiv39kKpA4bt3j0P/hP9/i/9/0Cp9 -e317fb+jN+xpSDJ9cQ3SLpFWdC1U26LY9JLx178+LTOrcjff+o/89h53v9sPcJqd72HHe3MN9/wT -3OVrOmxkLoLM7nbza1JG/5+XL/atlA5KT2WgMlKZoMxQwusAZYFy3B8PBZWSfspDheXlC/hdU2ms -tFY6KT39cBmojFIm+nEFasT/z1D496VFa0yLPsEQFX3KLpSXL/4r9V1d1FXdQOnqoZ7qGRp9rI9w -2q+bpumaoZmaGZp/bI5t2dZt2/bt2M7Q2UtXdFXXdF03QJmg65e+6KHv+qbv+qGf4YbHARTNoR36 -YRxmepRlLMZqrMd27MdxnGB4lvE4lVM11VM7ddMwjdMMA7ZMx7mYy7ma65cv5mZu526GKudhHudp -nmE4D/MyH+fjvtiXUKp9vW+gtDID1mPvxhxmjI58aaOu410n491FhfpR+66qq6oqqqI8lgtUM5dD -2UPpyqasy6osi2OxwE3gEeGAOBR90RVt0eAXiuzr5Yv89ae/fn41Ut8V1H3075IK9mJNpYHSQumo -wIyCjsMC86aYoMxUsEtx/BYqx+KIlcCpil8VlBpKQwXmLQxJR0MzUBmhwJyDAZtLmj00CxYox/JY -0cNgFRVMZhhgWB9VQ6WFAnO+6qEMVGAuVzB3K5inFczBiuXHAuVYHalB+CBURY2vBhZcU8M6guXW -1T0VWCE1rAhYfLj85npPBWfjAgUXI3YKNgUeQafeVIxHWDoLrPX9OMNCGkdYY7CoOlhaDVRXQSOL -4QhlgWW3h+U3wQcGWIodLMgGbooNKGFWHvulP8Cy3cP6mvoRlm4PC7iFZVxDQ0scgu7YLTD7990M -C32E5d7Dsm9h8dfQHSXM6mO7wFrZg2CYQDwMICRAwICwaKCp2G0lzPtjs8C62oM4mZoRBEsP4qUF -IVNDu8qmgJYu0OY99MAEvTFAz3TQTw30WgV9WFSwbKFfD9DLM/T4CI/fw1i00Cm4DEtbhnsY1QlG -eKCl2OpiLAvtO9omfOk3y7BZxlCgxjEq00aZN0vyghr9VpUry0Y55gtsF8VmKTdKFQr1XbP7p6/m -e+pDnv5DLaWh0lLpXOmp4IumJ8xBLLMU7kraFqDGhQrO2ONIy32khTrSIsMFAvO6gdmNpZMC05Uq -1hfsHrAasEAf0mDgbrhIOXKZSJCgAJho8U61lAa2IPxpaSvC0kvBx4Zxxuonfc1UcGB1912oHGnr -ws2rpA2swkUNW1hD2xhuZLyVwWYGW9tAjzvRpjbTxoZb2552+YW3OC77gjY7LmVUqlBg5tTZ0rjS -xkXXxTPsDomo/1Tj6sW7He90vMvh7oYTkocW9jPaEnEDQqEP4hX2Ndy1aMLSgC+0S5W0raB4xaWI -ExknAOw9WLmO6fGZX3/TGg8wzScSAP2xhY0Pt+NiOS7LciDhNsMiHpZ+6ZZ2aaCh5eF4OIAwnEnx -HUjBhS0GZF0NMqsEubeABMUlNYGg6UHStzD7a1ozsJpgde1hxU2wBntamQ2s1XIuYA0fYFVPsNZ7 -kgJQI0iHknbcPe21Pe2yFeywKLP2IB4GkHYN7qi4m9I+inso7J+wd+K+OcCOCRse7JcF7JUgnUCM -9bBD1rBBHeGhJxCgHeyGJeyDexBGA+x+NWxcC4wp7nVtVTvViTWHvpJCuk1fyFZ9pO16oS37QNs2 -PsBMm/dEG/go8rmnjRy28pcvYDtv6QFr2tQr2thLnJ+wueP2vpA6zBJjpo1+Ilk80Hbf49fblrZ8 -3PThYWXjL1Gbw/MDbP8LqdZ7UgJQDUBFYKS9oxd1ABUCVAlq0rhwBRSiGsAWQUrRnlSEmST2SLtP -T6pCR2pVQ2pWRSoDKQ1QFlIbWHGYaaGMrD6AQtFTU1tS72pSJEiVIGWC1YkDLbmZFt8oakVPCmVL -C7QW9QIUjAImM+n8qvXjPjDRXjbw4MgJoCEBUJEwCEt1kNJbkcE8tlKoG4+1FGocLRJYJlxgsRxp -wcDr5QtYNgdZOrh4sPAexfvhQIuppwXV0aJqaWFhwQ6njltIL6YmoRIBa5Qq5RfrInyq1dNnOHHy -9MNpwyeqmpZlJcoFdpGcxOgBsSp+8YKdRJkaaOn2oqq1tIQb2dRQ4tFipuWMBVoNzTzQFrqXLRU3 -V9xkB1roPS8FWfANbc4VLXwsOGAgg6ibDiQGYKnSRs8vVABQGehFNLSkMjSkPuDUwmkCQ25bqhxN -oKQHk/hYEo4k4ThyFDFf2iEE1ikdQfgAwocPPXjQocMOHDWd6Vs5ZAx2vNCDxVGPFKDV4UkfV14H -a3CgFYln/j0N2gLn/gIeFddyDR9qYZXjah9g5U9kB8ABXtojTO0SlhLcEGRJA8KgA9kygKTBbpph -6A4wxY6wCEqSWDUs+BbETw/64QidiQOzh0mz9EfYD0tSLRtRJmGdkgI5kcp4QCWRlMPKVMKO1MCR -lL89KXxHUvAqUeo6UuFYeUOVDY473sJg9oXEukCKFlsW2n2XqsC0mVe2kQ90QJ3pUHqksa5phAca -2TnatvEoySM4y9gd6xLkG49bZ4dCtsfoaFU0TjpKOkYLWWbSEaLxARk8k7wOI1SSZK9thHiMdJR4 -nHSkimi0cLxg/tGodTJyA42ejt9MZ6YDjSKWI41lQeNZ0phS0bVBcr6kDQ/FDUr1kSQ6TiKcvSTD -SX7vSXbjfGd5jbKapTTbXVA6D6QmtSSVK5bGUFgKowRm6duQ/WXTAvMoVe4Jr+eokfqvVE0A1KYF -hPKeVKeRdo+Odoqa9oagPs0k94P6VJNsL0ieOyUKZHxPUrsxaV2QhD6QZJ5IIgdVqjJV6iDK1ESS -ticJi9IVxpuUqqOoVbPI0YEkaEuyE+VmzqDB5ozYoLEMsHoTk8aGUQOKGjXUrMGGjaavdRaKDbFw -R6lwYFKbcpfYlKfEphwO5LSjuWN1FdkWG2dTDHZjtRmrZZh3VXzR9s27Llu6ZDcmLZ8K79a4b/Me -zksPd3be43m/572fHxGqBM2AX6QtiO7AegTrFKxfsLbBmgfrIaqXwDBkDo1lplSrUg== - - - r0rjC9TYmBEwLd2q9JkyxAVqVANiWqZMmbNl7wvUuDcT5LosG+XoS7Sb8FKm08VMEx+nPU15mvA8 -1XGi8yQvaYrjBOfJjRMbrXV0CIBxYkvdCBsx2+paOnewpY7VeLbTsdre0imEVHVT0lU9V9VcFHMn -eko+h6S7db+5U9cbO3W0V8O+nN2vybhyFKOKGlTUlDLIvj05A4oZT/TQFHf18WAl2MiCrWWywhaj -oJen2rjo4VCjaOFOA49176B1q86t+nYt2nbJ04Q1bFj1qmHPolsPcsTtRD43Tp8uRI9WLTpo0KY9 -i5+sWWnOS6IxT3QkDroyasp1Tk+G0cJOEj2ZZHusKY9iPpODp5jWWjO4VVbEugE1lmqms7KkZTUN -i1gV600Z24s6pup3TYp3Jyo3q2WibJNqVrGt39Tswez7oqaJih0r2EFZYwV78Qo2qddQo6huXsVW -BY6V7KBmsxoXVG1Wtic6dqvCDZ1ActkrdTUd4FtS7VS5S9U7VfCwHJ2aV5LrjpU9VvdI4XPqXh+p -fKz2cZmd+rc3JfBAXgVVBkM5piVdon+Ff11Bj1ZlhwjHBvq7Z6TOVdUOQ9XAHwXBaPHtCmTXuKt2 -bbEDpQ6rQHTjV0+vYX6P9+/7qm0QVVkO+AH8o4ZLrVSTonjh2+VV21XtbsA7D7V7kIur4idqi3Eo -GgWwXjX4b0R9au0Ftwy+3l31Zdfs2uFqHCr3IE+tge6PfUfTigGt+LzNWFTwd7VrevruroFKEU1u -t3zMl/QuXTnCacq+YABl+EZ1xT2EjzqO0W0e/i29zwjTo7UHo4/W9RWaj3ZFVPXJD2Jt8yEgIRTM -sAFw+POHm5u3u/2PbwmKguiG5ApUOTpogylWKt28fDMJR/Itlm5r+UbSDSRbY7LNyTWSarWTaJPJ -MpRklRgJ+PAZHT3J0KcSiw+eLKWCSQBlkhoD1BQQjAErUwD5Ig92gCzFz6QKRqxisJqxiM8oVjU6 -9Q6BbqWeoeARUk+Q+oHYA9SIB0i9P4MoIqP5fOglRqHZnHuxD2hh7cH5gcq5SlRm7/duzBuQjq3u -XydGGHbB9R7m9y8/ymH30pEeZe9yo21GoWBs2EfGoMTQYKagJRl1MwOBLjp584Ib/XT8JzMiHMV4 -kB1/OATqHDiw31A8hlVkGFKFc3QzYD0HaPzJyt9EHsA+MwcmP+5PGlPVSCob0Vgn0TFdBI1wbkzj -Ee1NG9l749FqTOczY7o2HZXxyMKYBvPRvFrZpTMcdaI9qLFon6xwGWMY0yaY/cRvPEXjvJh/OIx1 -8AynPuGJVr6OuvcGp55g5wv+RfhD84fwbYyNzE862h4MU5PXmQ+bpkyejzgbeS6ONdQI83ChOVjT -7DNIGc26BqbSALNtT/OshNmFe8UIs+kA86iEGdTCzEGZsMBMqRBMBrME4WQMJWsISDaPB1rFDUHI -ZpDDR1hGNSjMPQz+jBv0f73MXLZhMINj2hAZzB5rLkuMZTPsp2YsU3PZlrFsbSpLDGVsOaD+ipE/ -wUSWon4c7mcb9QMrfxP3Y6ifh2J+CPUDs2QT97OF+rl4TB9mAr14ROeCDsrLc40njiaM6cPG8wGj -yWNJ58nLR9MhuGBMHzyWiuD6RxrTdFTJzfWsowrr9HnWqI0qjOkjViiPauo4gz2lMGisOsgZHNuS -a1wc48E17j43mBu9NTe6fd59Y6b9zH++oZ0v/w22CpKlHC3kC1nFS7KEt2T3HlBbQFcBmuKPNk0z -n2/OfCP/+a1veMjdSArRlq/jMVPECXGaHjw5dGroxOBpEU+KeErEAhtUTDcdngDS1D4SFMti2JVJ -ECt52Ol6cifTG7px3RKa3tKSbdjpxvQmA2HaJt8q367a+SMFGaItFXzOLLgcReS0hsMpDX+jrdxa -xLSMoaW5dvpFnG/p5kLOtNSPnx/BJtPSWVvqgbjJ8p8SfEwrHiBdqloiz9LPTz232buFZNj2jZzE -MMixNHhF1CeiHpEH4hckKmLazwQvRo/lkbyUFfkmW/JJDuSJnMn/uJDXsSRfY0Mexp78ipO19Blb -Kd6f7VY+GKcR2klA6gvaudjsfc5WYhuhpeda+ah27mHlX9LOZb/abaiFJqfoUG//oqO+rWxCulhP -kVtS/0W7k32S4JGrfS0xDG6aBZNV5cZC6z+BUVrMWeLRL4O7N93dsCnBIbNl6nKIpwRNQ84WU0Ry -nvDg9VaDbt5ERAdwrelxB4ScIpkEj1xQm9V0XtU9U1s6G5zW4nQWG+GsGmvvjqkOgzujjUS8n9g+ -Ir0bVq5bt/ZuWNNuRdu7Ya27lZ62jIEE5lpeBGO4eKyh86CmSMMYcUhmKvGcFtT/PAIHMXPxSOhY -6Gisjx4HNy7ZA0gGS+M0TJsHioNMkJDmv9Unm+254ier5cnk2SQc5ECQG3k6eTb+rc8YZrfXgvkp -O68Ho1ZpuvDpUCbRtqJ1559/3QJFKWk7pI8tAGuxflZzzOja0ZLRE1pibfCt6CPDTBySJXo9SWTF -dQui21qkenKkQVrbEM7eE9ikFCA7gkxqlNPwFBP1YgP3LOBee7gHw9gFxA61GYidzgvQZ3CMHAkw -0ojeeTA4e0uaZiGIvtlOB6xd5k8GoBNEeqVqjbNojcNaa7TI1rWjw7voFVOZOjpip7zbAUC+rPYU -2QPiPeXUbhbtZyc1qKx2YZGna7N/1ui/2bq/RduinZo0gUdpTS4i+Rlb1oJ2sNGy+vGt4jbBCn6k -LpjBDj/NrbsGrZBryAFWUtfQ5W7dTvqPes/pUPm+2+y9aLZr/8U96PrQejFGeateTe48kF+JOy/r -0G2zDt3UlUfOW8KFL7pH8479TG7beAxhFEGy7SOcf3DyPZ/bFkd0vSa67Gpfa8fstp1WuPB4hP0K -SR33ukpa2rnpRCEBoPvInbd23PYPctwyZmzlvn8mt2yIzpBV59yyJcUKPbdb9oQcsxHLxV1su2X3 -D3LLTibh1uPHbtl25Zadn+iW1ZXrx9Tp2pdZYXJ2mZ+wxnNu1+B47QWVTIjj4HbNzECcf7PKCpX4 -TtpL/AjxeLTE48Fu1wO5XStyu/Lc2ovbtfZuV+LxyLldB3G7CocHrdz9sMD4Mn/HNJqO/BO6ay53 -qz6TE+5Z3TWkrcPKfS6nqli0kasxtWn/I7nYfilu01/0mP2U6+wndItGOv9fCXO6AimHS4gjLfmf -/XhVVkWAtyaXGQ08loikkXfKqx56zyF9c+/y9/Apq47f6aor0Ewa973cu/K9YQA9g99pmyvYoVr/ -vcy7glpuUcngd+rxqu0Kf7/cu/y9ZkRLBL9TdfRg7nu5d/l7hYK9sV+7K9CCB/e93Luu54u0zx+N -7/3L7e31u5vXu+/k0q7sEeSbu7wrd3WK9A0qROCICIdDZUGa10ZiUxdYYYB1Bo3ySsNj1dVcgNda -yVH1Rn9q87KroqM/A7Wgp3Cs0RRvVb65Nfpz5Ja5uA1ViOqyOakm6lP535V7unqliplCBjW2ZEzq -7bc8cRQ8hmanOQoT21PQ797hCIjMYhFJvZd4olFiiDqLHKosYkhldiy1TW6/fLGS3ONjYGcRGcgi -T3awmKcper4Q4xTim3xsU4hrmgkxMTkmgc5FNFUa0WSMAatYpoSkZHHPdohisvKxWD4aq0misarA -ihAYEcKTGxeC8iAMxoMALUitNUn8vBJy6bktPm3vo3CeGBgvITygMxzciZvh8b2c18JZTc9pXW89 -lbEEedj3+aXtz6DQDmim2tI8LNifPsPZk06dDpJzMKionltUFLnlSuBmBTjrk/ufibk1RFypyBKx -ZW1T64gKsLbq3IIT5cSrJ15B8SpKZcHIXknxakojrhV1rKiy4lVMr66U7LDgbrlYnYqUKQqxe6QC -bEAjj1oto2U4G2OOCpE03Ds9bMGTQy/3QsGhIYRKvKHhgz78G/o8pwAFUvxq17ZXOLviaJuNT/Dm -XnGQD+zjPQZ14R9NX7QjhTNh9FTh472uBg4Cosiuur6CJeV1pGeoTFQVjnACLUi/VEqQU4EaDaY7 -gD84iAhqxuCvjqO8RlAUd8OAd/PKzbPUd7HiMuYVl/EzGpqguLhQPg3xO9jPQQICZ/qZycqkYYOj -xOVz0RdMmzhSX3iOysByhNLR/F8Hsm/NhqEKKKpOOY1A31dWI4QqBY9wS9s7s3YwZ0cjjB0IcueQ -qUbssWhnOYo1nS3pHtbeo+Qk9NUCkrMSntQAa1em1EksLCjdmSd1ckypDVlJycoyHNHO8vIFiJkO -lvVEdjJ7cvKTKTORchMpHmskKZuyFDUmmUvlK2IVi6PRjbVIEVkKt2QVqDM/W2dqkwIpHZhy20bm -yavU7KjxIFMfk4ccLfLHTJBRXEhgFCSXARwQZ2ESPG2IXEWHbMWEYRiydyIYc+STjsTbB2LdZ+C+ -F0I79DAMvZt10Ltg8jmialKiptZCyj1NU2G7gQ8thz1B+iIlaPL0TCk505INNDdiJpBWvSdmWu0e -FtN5VajkLimQloNZNZy3V8E4sBhlgV1dFVV0Yn2GyvRALdGzHccRS1CvbhsaZNtTcG0uTvmJFVwq -36useK9W0p19dmkQ3kAhOPvE4+l9ngEBZ4gZxswIPkaxcIpsVDxc8ModBB/jAykrYSJoAkqOJk4v -nKJKphm4IJROU5R+R6RZOu7oSjhfiPclYRRn/pfAATMaC0ye8HVN6nogHOJefrZLoHBNiFx5GQaK -kYhnhtnLTxG5ni7yPaGIPfXJyvG3bV1xxLHEo/5sxXbtNcmDnj9q2r97x7rlzR+N85uMRtp8IFrh -mDmtJUtev/Kzqt9OEQgVsRUG7jQhdwC5pWfBg7KoyYnKM3XxyVD9sYpYHIhVkU+KswZhwolR6EIE -x1hExA+B/METQAQSiJgKghCKEpA7k410nxBDCDmElohepxROsFCYi7Ih5ncljYjJI0LR17AqKanE -RJvZvFFinuZDUlLKCSuEStkqK2KKc+XvkmT1H6dGGG2yJ6kQZSuSsmwqn5Wy+SirpjJhlYu+hDVL -mLNsI1mmqAxRcQuJJlcbXXF8m3RQ3yrVY8sjfegPCRJW46in4o/NtnGwcGyIHs1OtY/C2SvKnqB0 -+qPJY+WxVFlcC1U+U+wwek9p8YkQ3yQw6AFEVzUJtc6BfN0FncYqkrYqZ0eyZbFkXcj/XQjjrUpP -tsYxgdZMJ7aDSMKSfOO1WeY6OsMNdI5j6gIsCwUrFxSwzIUJ0qEf6QijL6ZDV055mTpma66kNFaU -9nwIRagtuMQ89BFn0ororMoU6jzi4Noq3WOLIU39MWZ9kImPMt6zFw4zUUICiknw8WEp9jWHflVv -32icxRorRihyx1a8iPdPmYoDT3FnPMVZlmLPUfzyhbAU53iKY6biksDpnq34IKyXswwOcRbDqhkI -Ncs8mB3Rf7eSnkL5iyv2lwjMXYSZlIMxGrPlYCa7q9JvCeO+pMkY1OSi3A9my20ltQ== - - - hvIfa6HbUkRIVcavSNTETHgSLZYrotaC9NnKXbB/fLHD/PkD/NYRPjXE0iEe5rgaY/1RPkW4+6Qc -KbY9Xg2lxM8sGVR4wmZpFrIQORnWRmD8Bhnp7WJ+lWR4vnMs3zmeb03xUfJ2YVzfge2bV9EsQmSM -WL97VVtpVbVElaYc4LrCKptuCvLW7VrI8+qDFc14MIci63Cy1RjKEJU+KV2mkLCAGtvN0jy2GPK5 -MblQm3SonIwQOcGlOlrxKkA4MMFMd5khZle80B9dGVyJlXzZIKFGL3faSP40Tg6Fkr7KuGA+sJQi -tDxmyrIq+UPqnnC1uTJvlOlkgSULNY4ny/DgIhIVauyfWLp8wZSyz1dyxjc0lJG9rAVJIhRyrXoo -8HxZobkK1MexQjNV1V2NXRdxqV1SCRvbhKvPme8G/Aqa6jqxk6kTRyuBK/yRHEXgc1R3sSGuyFvi -ihVCZItg9pjQzAfExDoTljpjO5uEmhFLKehDXqwDKe1pZiylpK8EUVKH3FgShpDPkCUGl2eA+cYH -FjmskDtHobUBtTFLZhXpN3J5lGQqCrD8VvohbjscH8tF2luL+Yhbx62KWmNurtm5tXoJQusoiKqh -QCpWYUsXvsWbtKc3YIhaoGbQMCsGpxWUL2SWQKvOAq0WCbTqKdCqjEgYKHsIyF4UozM5iFpoPSao -OxA9+kDU6D7DyINyUKwyUGSwFoJrMazIGKEtFDHiMSOKGgmZJrKOC3G2e9fFym1hFpoxQo8MUdsy -CJIE83JM0SMbrTndlrglsQMGNIrznL52llolu1q5sEpnY28s+mWwmPOIVZliUzX2PMTY++jzNP48 -E4Fu+TRLsvyejUFPUTZOV3mW8rOsMTFqQ43PYRoPmiAmyTs+pKxp2LOlpLwU5WapNkv+JfruSgde -a9Gp3p3q5pHmLhgtX4LGP9uZgMti5RhKREIPuwIho0ry7usZJDxuK8VM7o0YeIKpRs46s2VdOxAL -jfgCnD+gcv6A4BPgk5Zk1BPa7ED/fGDzIOKYE19A5XwArew9ynIwiZ2fjZIFYRAqwiGQJctiS39+ -qyYUEqvksdkuD1gladFVc3olZEr8qvyZLjuz13NY/87PXC17osjLzl6btfEs5ZfOz16ox9VCiji/ -maK1985PdST0SpF4qmpCAbKfajAL6kToFqMj51lIESyxJbUzzxN7m5R4vBSEY002DPYMKRiEfDgM -AXn5wvzOrQA/Js0lTBiZUvIJs110tN3wSDbO2uUVnjjOEyMUHG+OsqwoxwrvcGF3k70tYlbBPc12 -tAX2T8eqopwqyvOlLF/M8MX8XstpP4jjq3jG8qw1kl4MNa7ze5zK9PHwZ3xsPZufTGqMP5+7i2cI -yV3TVq89J/G/c9lQzADqjjEZrNTlr081nn2FIAtEAfJRkWPuJfHmLyIe9VONn2r8e6sxkZbmr9lK -ef3E8rOsMUGEEW7s8pJPLX76tZ2s3JdJoKBbZdws+bTqZEWjUIO4pCnbuzSJdZTgOmQIlSIhIr54 -10DkIAxpoGYH9pgPrkDDX76QLgivyYqYZTTPuQREhNyjCnPVHKTkaYQaQ5alKNNSljcsZF3K5V0i -YAGxDPYRc5hn6dL8S6VmLY3yLSnjlXF22Tp82Mx4cHnWGqezM/Ipc3WEGk/NWpu5Z+etS7qenbex -qSI/XzdmKmW+XeJZGs3Q3NyM56WfkbXMyCrN+pXMwjAD49mXzZArgHfPWZdyw40WE95HvGrGqmaM -auQ/J1aKyXEjJ4gSB4/PYUqMUS14zY1NbYtv+HzMeMKZTLYkjRhX9l2NFS8dI9qUYURjPrSBTPaU -2dQCvOrnLT95jY9GtyG+LXM14xreKBmICcbWOsDJcbMsmZJ7wfxJtt85KVNUxqgMrhic1TwdWlor -jZXahW7EqGyHGpcSNJVYP4ikaLJTe3nmJZdIKqgxSKkgneKgj30ih4IMiqVPLXljq1VO7pgp81Q+ -7iqSNCRnXr5woZxTRs7UPqTTGDIZpeNlDHE2rn3kBfuM0Z8sbmTMHqZO7ZHTuxVXFKw3XI1tW0Xe -8Sd9nf3ipaWYs1xuZdl3XMVAAY4UzYLRgxTLSH74ajeUV209tLuuuSqaiEHiuWrUEEkKqCmD+xz+ -UId6Z551DXxsS2wsxzTCIb1J3PbPUdvFXvsm77VvNnkdwuEmMBOwecmnu/VJbVPGh72B4Y6O+SGE -WnvgrdHMqTfbpZP3XBDste/FVx+SyR9XnBCBijGQmC2OjDGQmA0WALEm0mwknHyLjrGzQPi9BEoS -YRkFF7TkUVeispAdrBF6OQ5JYrOwGoU1DIlCkMgxWhOMFYSqmIQP7ALNGHvK5DgamxjjUQvjpWOG -oZR7I/ZSlEYpgSqcHt6PmIdLa+jKQcasMLQFUz1qoH8I9U9HrdmgnvPkczJyMFodKSsxAZ2mlg8k -BhERZESorYSBQjhHiQ0WIwpsCSQ9unErzajfJ+NWE8y5lzFDSkd0ZDM1PDqw0Xl9UJxC4cMvloTa -Yp9QR2zTW1QRvQVsjo4mQp38wcWvUfVregvv2O+NCQC2ZgqEUpf+sldGAOUE0IjSEFNaruLqJUpS -gamimpe2NZ4Bp27wGLRBtU4g2rnU6lvESxvUSxRiZGp0luwjpfqIaT7i0QpjFWhTFgrJCOQpgYzk -3FilRCoupTKM1nqsJov9DdG/AT7sAcQeQixRwDBaqsY8ZqSmzXhgJsg+FxP8KIosoo20404mtGlJ -CFp88uw5Q9QSU7UI2AZqfDyMKCVtiWhbYPxTKFHjjhceghMDimJIkQPjwPh3Tr3OxUNnIqIt3XYc -Dd1KQOlWRHQ+HrqPY6Gj5Nsh9bapw1mqeT6415QFr3WgHg6NTSE9tQS7cmhrcHoW6PQkOE9LqwTB -PLO4PjFPETo+G0kngePBKZT25vqsLCFSSHAEt4V5IGmOTjtDzzlJQ85VCxHmFmPvMYSJW4uPVVD/ -1xLYG1qKD3yE0eW13xBoaSTQ0h4BS9ZKDuUa1LkroWKaImoU1+4hfdKntG/d0g0jObspK+HcYnKG -vggpJVh7nIyu5yh0PSDvVyFbrZGNWuhsxB7WJHrHnkEVQorREQgnJrhVLaOUYK06A+23crHDbcPd -Skk48g7lmJLVa3EhxM0zrbGu3RFyVvGiqKNh32AYG/UIaF8I9ECCkCO0qxVaJYRWIJACdF9LpKKB -GI0RlPhwjDgYY9oOXeLgC0x1ISYoJS5UQ5SaopS+MMq9SCapkBIqSgqFdOySFsonhkrTeqWJvYzA -31qqLUtDTUKgyXbbCjOtWbugpaMLx2osFEvNbGlKr3XGuiTVFYU2bLcoSVWWpCOAbdsk0uk0Hpa+ -Y22esMChKHSIko84A6gLH5qzetqWBmA6AJ2lNGQo1gOyZs/U8HmW12lt29j8BJsHOo0L6ISKiawM -tRoXarzY0NtNS9/v4c2y3tUt1uMsAxdWxFSQaFeokGwDP4kBAk1saaBL+BaxMuWNFJfVcqlxos7a -JuqU2yOKc/ERMepq0GhBNaAeXRhA6cIAGPLZSHL51gUB9BYEoGEAeliVSF85tHJcU2nIUUHOgfhR -7KfyMOgh9iBRwIU7ygYzhBgiCImpTAxkinj5wrj5AguDMuIPhmtjhKVnxY8Tmmveg1kPuy5zhHLj -t6uk1yGteciAEFjVjVMdlHtlVYkzSXDkL6O+D4FTxWUWC0hvTVi/t7wGR8sxlmYZG4Q1ZRa+lIV0 -tMCOwnjv1nOgkEdsCpj0J2/aG69/wBpLqZFRYTGVaSU4NFVbgvLCCoz+VnOUmRIpcj8oNr0ZqFjF -2ZtpcbHQoqMzLypXmAYSVZKRR4OIWmFb6YV5bDSz4+xkiA8g8gZICR3SsCGXVTUUD49IHTDxy51Q -3TES/gYVPBNynAsyzAHlVwV0EaixIUxnZzwSPR0BaEFq+hApfGLUY7M81FrNv+yY8KnGuMZPcukX -V6OXi1vHvLhUycFvTQRdryRpkKDIetIx+4kYw7yk8OQFRxclFoxWPv7NG66eYrrKRcJNawo/Ml94 -k1VM4BcbqwZnzgwGTU/ruqASKuapKTJCe36E2Ayt5s3JDj7h0EMmTsk/uTZIB0NnLkX6llG6cjwJ -aZbCxOSZzQuQxXgQQ/M5nMcjcjrUPWmybYj1z+w9OVqMY1L8y++Ee8osvo9mXMxRFKhgAkuRUl9F -jEQ6F6FGnY8erREhKRJ0hGEgItyD4Rxgdo4JusHjGTyOIcYwFNE8dqgFQjOuMQvbeAWPq4qRCmG+ -e5Ps7DBTAS+1RitE5tkYr0BGym2jQIxbSPJnRpg8w0lR9iSHyNsOwntkEUscneOeoxhDhDgtH1e6 -U8WYKNalf2QRxgvh1jlXxgcXGJ8cyVKmzA8qD4ule0i8qANPaSzdGs1SoimFGRqQBbVjYuxR+Ujt -D6JNRT6ErsIPxowPF1TCpiFDmDAHNtlQlLWhWmE+SqRxyLKsXlbPxQaZNm+RadEk06RoEQ1N2UwM -6RKF+TRhk0sPKXZ7olfxeT/6NWagjqkvNcFgmjBVUAMwSzRtZCuBhYOEu0aGlsjMEhAESnM5RuSW -AU/gaS09mWVMYTkJilqT2wWaSqKnfPnCiCkbR0rpKSg96aRnoPZ0ko4aMtCcGzO1lpWE2MCSrBXS -cJiPD+9Mtj04PAmNP9Q4We6RvUs0qcd2/TkKy0U4wNeOB6QlktRWSGN6wZ6MxgMymQco5gFxTCDI -i0H06IxMcWZA2oyYKSNsAOwrCMK2Fx9CMAuqEJwqJSo6qDiTEPkgstYB7D4cV/cydTIxhWAUWC5G -xYjQaRVeLuWM9y3lIamjMdUMM50dKXodXTpa9DLOvRCtj4XPMzPJSHsjzSyUZ/p/pkAjXQxqPOr4 -m/lGjTiFUbLp/wM3DBl15C8ldCOCN0m9rGRvbPJhgl1FLSkh0WB/B5ojTyPHMwu22Ihthj/hiedG -Z5Dmf6cETWNM3kT0daF4ertViTP0PCSP2lYmtVUuNdBspygnj08AcsyASM4m0oD9Jk5fEiUwiVKY -7COtsncnrggHK+euc4j7dS5zl5ddsPeCi4Vz197cUJMw2Y3CbdnLOU1znLdKkWCkDSowgyjFzMoi -91ZsuxkuX5cTL+Ramg3uFeBDuUxGixthnwsonMrbBED0QF6aNaG+8GCvTzIBe+0hJOn5JXN6IbR1 -7vyi8R/75ARzOu4D5hfMNx+N1Bj1QFBAwzorVlFJ4byzROeetGyE3qwyVAV40bwBDPPsPSmYyM7T -aPlNGHzOWmUyJ+JgmaldBELuJJxGBuROwlOM9ofZ4dH+D8H451D+DunPTO4xg/zpOMNz8X9JjNbJ -OKoUjBTlgA657QOLg/I41NnsAcxsdDTOWkyaUUd+rpAt4HgSAqSAJ9QrcDUZfxG1ThiMFPKk7EXG -zRhlT3f5vxQ0G+CyjeX7mpP8LwqRBZUVFMoiAscyMLYyUKzLAkN9YjlgDMIsYFhqdQ== - - - RZBQ9OaN5MVjsBNCnbCNZikQZc5nj9GfWTNxiQoYssnoFh6yyvSqBECNrBQwlLl2eWZql2eGfjSl -n6kpi0v5hz97TqMnQKW9qTr685/la1zltnlwjRt2aqGMC/mRlNc1ZnYN8Wkxt2ugjZsTbtc+4nY1 -MFHC7Bp4XQcD3HSB0TVwuQqcKPC4zsbfOji4yzPl5mWrLuUa9xCWJ2fmVdAKHC837bhRYp/HpbLZ -TGTDqc9W4M00kimOYzoVyUQ6nKQkysZNphLXAZqdJ2C/lT9bQOfUshjELGmMKBILn1yfWEebx5pH -mseZ9UgeZR5jHmEcX7bZw8jScXqRmT8SSWLLoypjyiOK44mjyWOJM3eSMQxEiGyJnwUQxlb4WggR -D0SIGGItGfqltIh4DMVDakVQLzzcMsxLQV7IY80gL4N40VGtC5mv3OwJJfc6uJ3XZ3XJZVipBW7b -RKWN4v+4hIQXalMfV2VizQNqjGMQ169cfOOR2PpcVGQ2bjIXabmOy6yIIcn7EvLRos12Mfk/u7Ii -tj9Rhkzpg5/PlT5T0s90YrDx1HE1/5uy1MTojPCJLQainGfS3nNcS7ldZf3tde15f+kJjiXr71Nc -RvlyyX77hNcvo0btb8Gt5UPoakGsRRllk0A6NpyyvjlSrp85GEotLKsh3ZMRaopPWwyfFtBpAZtm -2b5evljl+/L6+pKg0gIiLdbUHRKNsnh5xtGYbzTLNhpxsq2YRkHmGdeoBCgoN9vMAH7r76MredFz -yJYV6Qgz3ROvXlxyyTTGVRkyhezXUGMflW5V2kxZv8wuCjVuwPAjvv1QirSksCg4I3lG/lDs0Gr8 -tfPZMj2mRDlQJgFQP/WlWVWGJ5R+u0CNJ961zC6PKFCjWus2yjmgdl1foXn8BFA7fIK9ceitKxC1 -XDGBOzr4+AqRqDdlORi62rInkhetGuE7tcdqX16XcMJrpkRL1ljoFQsKr5DpHQPWa71pxyHsIW1u -18C1rvfs8M9bMT+tfXPQFrq4+rHm3r6q4EMldknLiHVq9jheNXAy8owBl1d2qWczHwbffIaTqHiI -YzNxbZrTKjg2Q7bzOeBJQTs/Sli0BbKLg8ky+0Uh0amDk12cgR8Y+kDdnIIqVzbVkOOvMxZVZff1 -/Km8UzhmXzh5BgbVmN9XCHqN4VeX7GQlUAU5U6C4Sn05rtVSc6OeKYk9xeuRjWVZzLmffUCUHytx -RJObR13RYcw0n3sVZWNsiVC/J0cgExCw809Gjh19wkEdxQZYZMAQcT9PLkpA4wT8qPK4lrL/VbIr -ej7nNmLLHRJGZx13Lbbzc/bnRGs45kpbnC8PGJvuDDSgspFx64hymSRryedlk/XUWAyHBvsFyEBY -VcoPXsnaisfFj80UjcvBxuUYxXCUGggYcRjHXNvx2KxHxOtipqkR93Y8NtmRedLY/IQnhH/wGh9w -8oy8gjkPb+rjDTQP6uNVL2/pvLwHYnWaDXWr9A6ty9hQRJQBgeBhXFkVA2PSY6yKp3GHpeRxKBJv -XI4pULkCJ6cyD1ZcClZiEIwz+rUbZTtnoJWzY2NkHG5sAgGHJ3QoXVaNvY2NemBj6+c6l0Z+ZJy1 -F8ZGSThOj8sWe1U0LlgoYP8h/I3T6RHxmRUzo5Hv+fp8+TQ2P/Ox8fQOKT9pyvl4npE08v6L798x -P2a8/sUa2ZzwPzo/BrR/iLwZOYRzlWUdVVxKgkyh025rPG2KS/GolDijYsoI2Vi8QKVYE8GVaNSA -D5Pusx64Mz448cB1sf9NvG+4WQTPWyaTIno0XPZEsi+RTqn2I7UGhQykbM8J+UePzt6iORB99tFe -wG+apTDkFmSTcT7/KOcL9FkBo8yjhBYPmf4Kl8FvnafPZ+LL5RzV7HhxxtE012gVZRhVk3OSNdRl -B52dz2DtFQigu9gvkLP7mxIo6cZ8WRnIHmeRf5Cd/W+lWa2+6ehVTuN35qjk5aRJR5KLqSzwEQ9b -MQ+etdHj1diuFvM3ilxwTLEHo0vw0iGg0xpDpHHW1VLwZ4s42NRDH9hj+yQrcYgvUulwEN/0TPZ3 -pVbwOYq9j77MkGLoxuH99CIv4qyrscxI5EUqLYQmI5EXnF1mNiqQdcbiXNbitdRwMoOiHzSraEZu -JPmKvdyIZYfJjChX8bSWG1mZEeRGFckNWk5EgsMLK85DnM8yfF6GwB6wkiA588wpuZFIj0zellNZ -V87lHoklzTO9nkHSxPJjisz5eW2rz55K7Kyx4jjXoYqlyyErXYJN3iQLSBqPhw26R9A+ZkfJ4jGw -baKBlMxMTRrI4mhavA7itRCvhzxG0vQRIijIG0/BkyPhGUQ7gQKSJoMMIonj86NPhL7ISZuVdkJ2 -1n1G2jw+RzqHCIBcEB0l5DCOM6SHrMRxHuJUVzE9BeTCYUNTiTOj56VOnUicStAkTvKsMp9/kjQ/ -kaRxOP4Iwy92HbXosC1H8VJ4FsVTaAVDg+etGQRGD0uGWfJLwkYhLqqHpYCIqIUQUSNhoWoYrIXO -JiMs6xameSHopxFWOCKfSlrNiHtC1FNLxGA1nRx4bSLuqZNVWNrKwx2eV1sta4z55UfDPLVCeFVS -frtFdudJVgivi4rXge21gb5rkLndyoyu/OyFcViE/mpv83QUjFSYi/HM255Zef33P22ufKrxU42X -S5qicP7O4KtRT00awtk5T6cL4QweTop59gGceaLumPDZhW6uiLpHQk3mcEaVwxmtkUYBa1SlvFgg -u4wZK+LFUsxRI4GYnhkr5sYqJKiyJo0JEcHKkDVY5MBesEieJauyCIIQQzC4PMmWIxk0OY4nKAWj -pFmSGamkeZJDpmTNlRw4tBS7JBmTyYLGbFrKp6WMWsqpRRSkwqulzFqVUK8Gfq1O4n6gH4WgdCLE -00yop71EawiOVI4njButGDtqeNFWMKOGEKU4E8aETooLlfAPjwD1OM+A3AxozUboXqHQXirpZIUa -lYsyTQgza8LktGZvOmaYmtaMTBEDU3DYWelcsbAxyWQ+uDImZUrKvCpRIA1R1wqAd7OsiDs2yoUs -SJ8kzSdJ80nSfJI0nyTNJ0nzSdJ8kjS/GEnjvN0030eb7zrjlfmW4wz8jO8kOjjM+zDz4UW2ZvZc -4W/1Yx81iQB1F3u8+Td7wUM6hUq85I2kVmhopTQus2dPq0a5dX1Jge7sm5/kL/4XvKBG9t8HTz77 -9UMQtvr7V+nlXHFEcJYdMmAGwruZ6XG+OBzCmc/9Vx8HHjMeh7iSvYsGD9KPI0xQ/vXCgMyRJozR -51kxSdwJzw626rOFn2YIRUyXEj1eik+gJt8jeyBbmTWNoDY68y70FGXduTnkS/oayQIYPKHwW/wa -Qeb637EX9VRIvfPAik927zyya5aFR5UsT0P2czKOfgzjeP4Q069jiKNY2C7WkDVUx5Gj2kcax154 -rfcS5X6Av9mGSrH/JHjJc0PjiWOJe11NI9vQyOLvlka3I99PZwibnlJH8Y+WLlPUqzTKT/hXL3iW -4Ima3O94TpwKVHEvqHFSiZT8lb7mhxXzoZ39nDAaNER7NFCiigNsQCWhoTvPaACaU0VJK3rRlg6k -JVUSX4ZsBpqmrXIp2mbRe3xqNlzfOCtqWc24ikNStorWYO9ixCgpG8ldlrJpdNhCOodyObTEdMDc -5KpdhMjbo2oWwsSy1ipCFG2InE01iv0qVhY2RYwyiyNjHbleHAiVBpW1+eI0kJPFUmuczlGbYy/V -PLWzRvP7BCFE8KY+O59KQ5Np9BJPbUlCxC+nKTX2kTeul9QaXeL11xQbx8jPP5v/YUz8+pF37eUL -58tP/fc5T1riO1v7zSwGOOctO+0T2/Z9Pcpa/GkcfyHjuD2KeS7hrVEUPmHKHr1ObDMZRke95bV6 -+8xLrkl75giJ00mqGx1H5w1/0DjG3u7ajaP5tyPfth/H+ZHjmPdd/+3H0aNtY8Y5OiFE7FeKhl1E -G1N9jnlRAhM2F8XApcmAKtoFS8eNveJoM6xMAEFpRy4JipY1qD3pU1qDsrAEpm1hZBFki08xFHBz -B4epjfG0daC6jO6uGrgi9wKed0/PMMtzjHR/5gtpKe1nK8/AT1QrxiZiAp9Fz5zcM7mnsidirDDW -UpD/nRE6I/ng2QtfO4YZ9sQjygYxvov44geSycRA8vKF4yDBFcwMJJrvnddsgxLXmEdwbTrG77o+ -Gb0WqFZDZpuQNK0N9i21bllMk0+vm1q31swAoLf9XT1Rl3mi2AIYcv/EhLQcib8XZi6JyUz4Ezaf -KZUCAumJ6UX9M4anjHMUxVmKLJpUku5qNKlGrnny3DVxLseHzmavLB3fQ0fo8ygzUWKrjK2VvRDg -rjMTVWqppLNejrdNeCASO2XOShlsNotaKc1OWa3slHoG8zabcHYOFku1WRZyRoBiXOgxB3jg7A4k -s3H+KYtjTHNP8dgK89v2/PNrIs49tSZE1qjgkHcqrIzJZqLOxcLGt3Z8HoMRIDP5MXHJGQFyYPZo -hep4EILjXAYqZvlIs1BBgZGXXFRZq4ye5XW0dby9Xdos0zziyD6KO5aNU8S9nmQIe/gY2cqj5JMh -/jfNERZLCZUTgbYaj4KBsprWHSEfuyQ3mPcXqMQIa1BH6WHjFFajX4/ee7A470Hp7Get2F5AIzcP -wmzr0q/M0tnRwgoNDIurEYMVSmP2s49xXWMMczxKD0nwGaLZxXflMlyl3AOLeK84QlrZBiha3SLU -cVYOtA8wU2Qh0eY461Aq7GmulSQFkAFgklWP8hxTqfcwQDNZVUrih+xhsPb9Ao9dEzck2lJQkqIt -xdJqJ6ydIRvG8EhWO4kfE/7hOONLG7PZJTlf4qTWnWMgFv5hl+jyVNaXwA3o2QF9imvhBzT8tM/6 -kvIE5pgC81yBnPflWbO+0P5BZ7NP4/RpnD6N0zOPk2cZzpL7Pa54vvGQ8GGbgbx3FIyWPGJvkRyz -BarQdgs1jpJ2QuPHOhff0QhlRog1Xcd57KM0vT7elNNHh1hTtTf5Pi8iS5Pv7VJ7Gnp3NpsSpX8W -m1bWYfL0kqlxTXA1uNK7oi8NUSHNC2psJKND5aLq4pTTaUptH2Pn417SGLt1AmqfVPvEbBXLnCSd -plw7mnK6D/kuf1a9m+1lsWqGkvZ0kcQUrXvZp/eO44oqZwn1dtBz/as9LEm9KcbhYBEOIXVHNvnn -I4tz70jkgy+HVdm7coqtlNR0qFFtoHm+0bUV9FScTi6W4uJXqrebN7h9huKmoiCjfOlXxb88AZ8a -WRVuQVs1Zak42Dl2EcBM4TzUVeSh9ickf0YyO4bDWlVR5uVV3mXjudR8y4kXUxkuQd8IfPTzfv+p -dz/17t917zrNLH9qL1I+M5/8yp/R/Tmd+AEpZrs0djOPMx0s+7Tmrld2s5QncDBGs4PkwFPEaZUg -TlO8aWz7DtYjtvCJfRl2t2CxncWip3Yi4+UV69BhhSfN8PKiPjqcYh6L+9VbQZoNKw== - - - iEszRfwYe9m7lsgeUkapw2JGRu1rQfZKjm9C90oisDTpnCF8M5yMXYLxPcQYX5hUwboa8L2pr6Fz -FtXYsu/8DTwCrCf9BH0qSdksAZefub30Z8pxGbDSmi2dmVbaiJUvTeNXRZjpwaXxy6GmgxciIKZz -/Zl6b/Jc0w/r04tef8c1ppJvPZ9i+edYHUnjNIuswGSXJE+S5r7S7Fc+/1XgvZMMWC4jEmcO1gxY -g7B5eT6vmNHrkOXBC3xexR6e1OU8C9kmNN9ErXYSyXimzF2jWEZCZulS8pwdJM/IuUwjj8g1Qnt1 -+6lPP/Xp302fxt5oxWod/JnX8FmI6ikIyxOQPBzV31tEf0X5TRbJujTLeVRznLR0yqz09Oj8q63d -oXLYr5Q9QO/ThHsRLmiR87Hcj1gsejnl6sm2Cvd1d/at83dforuPEUqpERaO0u792DsXag4+uARh -Pu3AKvGAgEFPJB5YjhQUUlIASENhHj0FcUwUmIGhFkdniP7HurOb5qZqjrGiaWpRbW64KHwMVSFL -eabKqyYp01Rmo+W59bX64DQz0BneSjPi7iUj7mIqr36vkty3rKZ1YobiSWTcssKSGfGPBAaSzjhI -SmEhOYQzq33bfz8wmKQ1ZOvYyF/p+TlzvLYHqyPKE15zrmz4A5N4d/4P46fvlDi/Uy56+SOTqft5 -6iO2/SSbQdVfwXm92g3DFR4JN7MepJ+TTOSUOBwzEiCNPv1RtANlMbck51DC1/vhqoAzuE9C/tQq -JPuC5jC31Akwm0F/9ynMG/mp+bOuquaqbPvaJ2B4jur4yTpL14DjgkkHOk2uLkkHoE77gUEar6oW -BnmddOHSmvh5Ss3YUEqPw4kGKqa8CIP9hG/X5VUDDfSJ659Yg+bJ4OG8ajS3hf2BA15RZ2PeizHz -B0w8mAPNFaZQjvJlPFedubWh7Sgp5Ua5uTbSzz2yvzWfCCzl9im9neQjKWjdYHWSJqS0dXQF+hPM -YvpDPjTwaoNququyHwef+OTyqi7OpFHkU2kUn0HHNy6VxmbkpKZBTqmblbb5IJTNqDjjNlTS9rGn -5HAo/lHwM/UVCvsZ9FRUhVERLon2ak/Kb0+KLyi9oPBqMkBUcysMzeJEgBLSxXBeTQI4SQrAmlMA -9gs5SNm9zM7lQtzKk0Fv2aFctKAZwN6ZupJTCP22GxldR7FjrqMAKAt8EFVanXOD5KyPFemgYMYq -9NGUaK9Aq/pcJAeip73wXIMHJz404XGpgRobOirhA/BrL0ckTWSMCgcHH5U02pygGHW2kIi4I9Iz -HPnGwqPiJNX7BAIRgx/wd03UaAyBKDCVpIIgcEkYCCJQdM9kSB58ykz73UapMwMhtdB2E+BTw3R9 -Os2QUlPh9z6xZpxes+eAT4ZUwKHNg/CNgtaBkhl0mFISRsSEYtD0pV7BmcnDb5BSTYmhyUp8khIG -C4fkM1hCMpKJYGSWdoZTmBnEWYHDarobBOoc4KULxZYr2QEb8cSAx4ZpShkn8GEyThcERlOwIkNL -FVRKsFIHU2RQqcKGBThsrozgxlDXhbovush5ocDEWeCJi4BJJdQBoaQuEKKU0N4mUNmTudoPSGXG -1yoq6b8bOy9w6Y2Wmv8arfCN91YOcpawswGdD0o5IXCpwo3gUIv/98SUau4Nnyrs/2t63IXu5oJ1 -iCLXE1YOEZEfS60MnZ+5sYPLWtzT4pzeovTLJ7Q86XzeMrnaQb9yroPMyS4xcXvzdmTcdoQg2+6C -M4EJ5qqJHDUvX5xIoPiA9InkuItSJ1IKu8cmT9xwzHHiREov8sTUifnEiXD4dKf5cJa39Xa0RELq -sqgln2Br6YT684B8hXuTmcsDvrfCVNLUQuvEQpLKC2ZCSCnk03mF9E4hEOMQ0gQJMNzLVknlRY6+ -IGdHB+WfLXmXyNsI1B/krlLNiAsFJK9JYQvkEAA5S2OTxwoiryUrpJfNAUwOktcg5RoAsDDETRKR -RzA1V1LfdfpKMUMMZYM+caA2X+qkVEmJWHU9EgZqVCicL2lEdJoheR+V2Rfa386BpGJf/hZASl4E -kYpLmqOzjkqVlDQ9G7Q6YfIVTpiTDvbY5RTkcRwimU33qxI9SV4cl3OJktlxP0Su0Ik0Y3Zncdmb -Q//gnM6hOOhV6V+eTrlyO7vfr30nhunEDtbgtg72PC6gvbtdnCigo9DGOMBxEXKKvYVapmlL45Sg -gUgcQxsb03qMSFxCFJkmWInERwtX5CmoFOK10IgLaJWgqgGEymtd129YmWHF6VoKa2SicMdR57UF -MdecwIRm30IBkEpIPDvIZG9pTAJcciEw30HpwSmpMYMmUfLanmEJIEoBpMQk7T6MVcnZtT9RaAsZ -u5GwM2CG+66n82YnfUaU6yT5UAbOJBkHkpUslxhsG9K6oPylM6j0RaUnUemDAGhUGGNJrfbn0IFa -yylaKgMwguyh/UoDRBHAyInpj1Fyej1/oqJ2lDMoa3nqXsE5j28XBHIMzo3R2Zw30tw1SfJeX6ps -Eb0TagxaaAqSTKmg0lecXl64s8ivt5c8bKFMrsTrbnDFw7EdM4bkcWvF4h6K5+MIPB0hS70HdQbq -q0Vs5mo3N+Hg8eEJmry0UllEpRZNXxgjyDieq0/QYR4VtheOuAgP5tBgKR6swfggwYSFg1Uc9xXH -6i0uPrOU3AlJjCblhOujCM0tTrkMq1yOUU7YroxTTnvXSNDSnKbVhgLlY+/GtfoESzlkQPfKk4+W -1HhJVZd8DF6IlhTFnuLv4qhYVfCDih/yo/sM6V7Nd4o+9O6YVfZPqPunM6XDQSBS+FdONs/K2FBO -1uTYtXnoio9ccuBK+BdReY4PW/6opQctf8xaokhWGBdYZ+GIlR6waBQkjzur9vq7zf7uMr87Ow74 -372L0daDgv/NXB38e5IjxCjZMHjb29txQg4VFCGveUmX6JBBvw03NRr+ZzIEOatMezmqHw1VRgd4 -OcwHfBkf8dn73ogxBI//neTpHcSUwUJKzQVz5ORUz/rRHJ1khEBjBJku1GzBB71aGEWE19O2oCTB -Qta96eAXHnzhoRcGvBC6PQ+82J+FXJwEXIjWMBnFRSupWjgRlAkkl2wvpNnzCfbS1Hr5tHpGJIIJ -O08k1POJcHLp9OIkWmS9FJvYZqIbcwE3AoWpxc1bSTLGkh2+Yv9VG/Ai7t+DpDZNA+NAhCvXhwTI -DRILpccvzWatuXlrC5nTsDkJnXOW43WCQ70tBdTFjmh0T7FbsNCc7KVmQC/VG9aw45W9xOUV0n6Y -C+jJFYg7LTgq1SmNfl300NWU0F5cSKCEtw065Lryqq2HdtdWV1XTR97oS6sSpxy6zVtsE+2M2DCs -tWGXll4DFbUYyb/GDldqWtde9SD1vYfu8srENap9WqvXuZQvUdPU+yt57OGb9PEMTuDCii5117VZ -b12Lie8756wjvUC1As9mORATzmwceLHJr1uj8VkTgIcOmkBnepRqA2b+o0WlGkErHLzKvuvZ8ZDB -rnAAn1pY8ZRnV/l1Z2ceZAa8I2vJwqlbkUYNCoFpb4G7xHMnpGhsQw7T7n8wM2tJBqGKIuBRNwOx -T5HwXjNDTaCKjK3MNjiQIEe9jHWyvTO7OtOr6MY6Fl4782ZYPzI+WsLHTJDOlhiwPU7b81oHTaoz -VhFmYAxsIp5JRA3YXqNic2TgEenE+LjnHjZTY20I7dazUoAm7IyKkT7steHRdOE5mA2h10t3nmk5 -8uUM3juHfl5zNLRp9EeMoQ8MDYadV9T8IeIVDxos9js7DbjPe+IJmYybgeM5OkHA74nxsiTGS2Vm -WBwvA/NdYn9VpJkOvY15lD3WZ4YMOWMtSxuZFkMeSMvKZnli9y1sbyvevzxnnHd6e7f3ijEOWu6j -JgNfXGCLC4x/KZZUWf6WgCY1A1nIlxdy8z445nwVcT6Stn+6D6dsH5adZ9xzYAHoSx8HvRV97oED -PubUR50SfMDUwRQNHGGBH6+UCg9bFgecKKVxjH4coe/i8ykaQ3vqFLtkPjZ/FZm/wiFvmJAyaeZK -c8QwD486t72Du6MTV2ceg9jhvWLJ2nDMtC4OCM6GxvKlDhmNX1FHX23ul4yrT3ag4OiDPnU8Sn7v -ETef7QLNidJmSuwPCBlTO+ZtJAT0mDAmeTfaTPIO1/DBXGp7WtfHxLUm4AUYv5RNqSXpgGaR0bnX -9iQv97QSvFMtABQ6O/sPcgbe0/zBUVpC/gVzFe5PFE+3GOeNjssh5Gy0fI1xxum9+9ciMWr6icB0 -Gdzp7hq5M/21hXLkHen/chYO/zfPCjvqKwMEq0Mffkgv0GvhN0vXuqrciXleNsqa4n3vSpr23ZK+ -Q43hZNqfOZkWEarmEJ1GlY6kt0TOIaF7GZFXapDDtNoevIhzQo5MaX3u9M2izlDpp5jSx0wZouJ5 -OkgHghrD6fT0+dTOpkbuEkZA+x0aDb3tcU0ezVSf6OXQx+GkLwSh5CdJe/gQejg61T+k1CdKFYrh -wcqkxFwHPpPEsrIXqMWArAbQ25HdwI1ZPD6x3cCPSR2PCdQYj8oSj0o2pmA7Ic1DMCcR4iTQTFjA -bCmYotpiDthuPbgQZb6/PsMhiWmgYM9AX+FCcUupuRIkj96hNY/nIGa9kcx6Y3RH8nP6eIRGYxEy -mVAfbaiLMqz6SAetf7GYAx+rwPfRO+m9HPDQ6u0t5qs1b04VRUMcN2IbRiGz7X2EQzo1BDrZS1SZ -7xftlc5AsqXAZPfSH0OuP5zp0uuIaw0xrx/GJksQm8j6Zrqh1wyDuPR6odMKE86mXphtYYkTa9Bi -Ts2gC6aaYAyt3WK8aQQweRpSG04TCqkdStOradxHmWHcp6UYgmeCjSpIVPvyYEDk3vqSe1LhyJOx -DjMoubRe5O0n6kfadJQZOGjYDZ2OyVVMTI7an6xfD1kNe3Y6dqRhi7vY+IRgIkas365/uXc9p1AT -Tm6Zfh449Aj0jXVv7yMYs48GdHBms+Asoi0fzPGvjD7K6jNTy9QVPrjfrTAptdLujsBSDenotWjr -leiFpSAKj3a2PYqGv5jvZCb9f5Lf4oOhXhuc36YljbQmCwr3InPgK8OVzlees+qEHyzrOM9e7N/Q -t9q/nkt9sBnNs7qTnq5FrytZK8Q+J233kHCpj8aI3yYjIGPgx4F4pZWla7HM6crhzyf5EBHKT6rn -+DhmskAwATxRiJhM54LP9qxM7nGm5zFiK+qErT0HCq0iANKak/3E6+cU6W8nuguBqCkMFdbDhUDU -FIYqUL8LgKgpDBVUzwuBqKlfmlT4JwFRDZDve3mrn8/zg1hPC9Au39tb/Z0H/kqPC3O27/OtXt/o -9xQATLgAxVnk+n6r9/O4AOLpyY7A1hg4OLDx1sXsx96KojM1ly8x7UFjPIbZu4W3iLMlembqwEtd -J3gLhVVrz2rf5riPUvYj53Ex7AX1M+h0vfX0ZKiVgMLQ/m4yXheXkchyHNZybPN9Pw== - - - SuahvfO06AgoJLtb+hUHZmdeD5pr0i9hnuksC9nzdIaFDHlubomvKV3ZOrO8b8nDy+NZFc0paGvc -psq3h8PFTfsI65bXrK1YWauag2rspmSN8vzi1alrs5QMdGFVzvBIuhKDF0gyjsn649mhMo97xtYc -HfRzfYI9Iv1he4fLeRodMnVHWec99Sw+Ma85HTsjXvNGrNYxv3nIzRBYqpZVqFCAsqeMP2MKZH8Q -r0+6y29x3Ti2G0O8zJZJ9pzHZ314116lHoUePwgrtzJ6cS9SmBXnOnMoPrWnry3qqU19H3lwWgF2 -R1b1DOvtQ3hv15q/na3Ia2U6qvPbLEH3N3/MI9hFtoxtvhcor1vaCxd5FUBonWX8NR9LAv5Z+Vke -6WmB/kn75Em9EvoEgxNWjMhJz6xZEyySuFPsREtgDoOYhADkq0rjvAV7kQFDPEt1AtBQDEctNSgK -BitTZEyXICoGCpL2XAcXVcNPUjV90dJXG0RxUBsY6uGhJr3GjhvmpMe/ocouQfg8X41p9LnG3+sf -PmZcqR3gD3wXG1pegThssMG5+PMnV8ZPNSpWiTofRrrqPWynhK2hoNFXgoUaSR8E09RfgYRu3GM9 -R20yr2QeGJtHiSH+lcB1pH0BrsMDRNXAd7ty8HPr0qouBv9UefRP9RnRdwT4Dwk8coFRyofCHJW8 -7epmO7pURJIMwmDPkYotUUY+FYxCnPcWratgjsqiwlJo86xxYEgpaXB1hnOwss0vhZ6jqs0xuwoy -13hdBZQzmJwSf0mSH43IbSmxZ28pUDXt8iLBQAoPbx04vLfkyKReSxS5Ji0OCcftaC5JTG1rTwg6 -9RioyuQoSTcCCJn7VEFIC0WcqSrZmyLJgCM8krASyQlvFdrFCW0n6gdOUlubcj0TNdhCqaUrUh9R -pQ7KY0mJTFF1FBpVUqJRheajAR/J9EBGivPLF6DNKc+SxGU6hiXPr3TQtNynX9scBea4rcQLoT8M -OmZrziKg5PXPwX4OAmLek6I3keI3uZ9R4sdGI8GOf1rhVlDKLqXt0h+mvi5WPxe8VupuKUpuiKzz -ai0+JwOYcHofCKrN8GxVYuHgIJ6g0Zzc5GCuThG8Pm8b/r5rPH/A0EjGAClzhJqFBfBseru9v3uw -hOCdoa4VcR0xRhC98jKmyRs1cm0wT3gn6Qkb84hXLhLwOIQEjBzRtpe0h5rycJCQXE572CZxgRTr -JuksQqIKTUwxScSvRghqJG+ciCLE4+aSUCiQjWNmOWZ5sKjY1o5FBvh4LoiUqu3QKxeq7anKLsk0 -ngEkpYcZgkflUkLEB7jzLjJz2JD3eoN7RhAAMXPLwbD6cQKbNH3NZgIbwqL4eAfFlQwnEQ7bSJK9 -pClPV1cc05BhY3GRDMnKEgjikEbaSoytW1vRyjpsryshcvcrS9bV+iDXM1MVnbBELS305DPgOQcv -6LGjdVr7CFMgOcVdXher2jW/BVp5JWRWhRDNoV6sB41e2d4GPhDyObC/ggOO5+Z6hsouVre7vLrd -Rep24f4r9T8/VimVWXKIzr+dI0sb+t1w1VT95nftA5e2PN/wLj1m2KZY0fanSGrFUXPs2CIc5GpN -y9vSlL09C4bJqCd++/Wc6L3bdoNlb6/88saITook0XQGsEyvUJlNdah0yp36FlOCA1XL9Ef/akUx -SOkNQOL7wOnjBptabXxqREssnGqDsaptwGlANp8k9pT7hTuGe4a7hvuGO48GWQlwHngCuF+WHDRi -I1vsnkzLHBMzr6mZGwsQt/BvDfSG00gI69YA7nEZ1xKzVILPQo/qKEOKIbD9KbvnyEFHtBbb/mro -2sqHJF1UT5YEsdidlAeXRw11fX5B95/RPVYrujqxog+rFZ3ax511nNAgTyJUsqPfLJC1+Pdkv6fM -bybJGuWwMwh1gaB9LKN8+N3J79797iRmtdffjz4k5bn3vZQgSQE11iQfWunxJiK8ypOeEBbCy43H -S401DM+B8GAdPzFe1mBzgSihM/hcIEbwZAgRCYKSHwTaA4ZzEl/kbOA6liweYNcbBU8rVMKCOTXZ -syaayJJKbJBJrEgkhELiPH3EijpiJZ3IJMwKD0ddKknuqFGGIytpa2nxuC+KiVvUuwINq6TeiR0V -VSoxal4NqngFo29q/7+snoul2piXaiNKtSgacvCHNHdECwc0JgLFw1mMVTQqUH8oe/li41iGZ5q8 -hykcyLLHMfIbPvhAtnUcczgpmGKn+PQTbyA/c8CyVYa01KOkPnucYZApKiljY4QJ1JZoa0KL0riu -KLbLyerOHTpDSx1KzUx73OpZSFCVCLV3MlRwYdAjARPmzD/iKWf6dIFBG6l6acg6oVM0+kOlXVRA -ddiPBjk0j0b6rvtKZ8i7mBJREXiCfTP829EbL+H52Xzp8/kF82VvyDjf/s72lTVlYsjXV6x6hHeV -54JzKeyF4jeeBc61RTPycF7BPKvgEZ7xVHaChEZFWxaAakMCmNL2hRa2PoDaSGhiSpVqBXOJoTxH -a/MKQgX7pLZ9nSPBw164D7QXtB+0J0K2BBAl0hvaH1GPeAqdyEui7QiwJAUmaXt8SLi1iqxJpbVN -A8PXoeEKVtqTjkDgHmtraC0B+KzNPe3Bo7U8tH3B3V7aX3LMGvUB90LHEcjUD9AT0CMTnVv21h+Z -PBKbx2NvE14SVTpVpvmIvD4ep8r0RpwInDY9TFHBoJJyCzayPNgphYGSzy9RgNfJ9LpM+/xxIT4w -WCspjC4XE3Pq0LAFfw1J2+IWJ6nyPPj1Se1aXGq12iWpcwnVHMnR5e0iwBWBsj3g9CS77C/Aq5M9 -QiW2i8edenLnHrFUgOR5FqYgS7FeMWv4Q70OF7XrRFAVtCvfqse1yXlRYB9/hB/lF90uJ+83YfI5 -yPYqgDuiC6mFRE+1qViXCtpGrEVVgmeINSjVKWAPpqhT1Z9yUHjcX3F3xZ2V9lTaT/GMrppTT7sn -75xsfzPtySC0Ct+MQt8RQOsIQio6WnkAt6fQ2wspC/rKjszD68hCPOMwHuEmOszhwx8RWyKokkrI -DtG3MgqqBJWPhf145J2pCT/RE0h5pFFF1YSJcSrq6YaQFAPNCFQ6jkYuiJ4n9EINQia4J0zFYT66 -fghspSHJu5JqBFqIimCxGoS0GEmbEm0wzVsIOwqkG0oaEag3lDriIKzUewvcGY2Iw4cd+cwChZFy -aII3Dufh8C6l5gjHuMrY4Rc6EDETvHHA80HiqigQC/jTgUQ3wJB0hH8WkOjjvKqUklaO8RF5ifcc -rjzysTc+4y+MPIUEMvce+JX3PfG7xz73tb99T1Q9syM9Sf3stRHIlMIRvQQaGQnTGx2VDBzow9x3 -/fCsr0fXOJwrUOPZzzyu+BrTHeSEv2UQS6ruh3431JDmxvLocCjrICwDuP/h7newXDod7Xq458XZ -dGoJXz3QOjWKS3LSKO+Asjz0Uk+jdZHnnT3uyiegXA1+9zxIaCzKgV785jzzddbvLUDWM1ErfS3M -dHMODEbDxDSivWjWTB/KOnUn1Ev7CKenJ4PoXEC8+koWurgzkKL0cHc/ChETB3tMtPPwjh6i59W8 -H7smBws32CeOFDxD6OkvJBNXZ0ohgRyNnR96y2bgUjGHHAaSwaCSoFKXK+HRrAO5k4idJ4V45cl5 -Lhxpm2Y34LOZkul0rdL7Dy6jjGY7mCwoeG8IVgkYEWdDTOUbE/lWwoivPPnKn9+F54o4RHSviuH7 -YZ56HhHdt0rDQ5EsXhF2xXRTuovFIR+OciqVF7KPxhwneRIsz3FSZnZUeiJ4vhxaSXZV7ZVcy6yW -7UCWdSiL7NGw1+QQUGJut7vGferpuya7oz673TNtQ9ANjG2e7pmY+O2utH6YJ531oVH2dd3TTxrm -I9P8bOlfQwizBi+LYdZCHB8YeLt5pkistKTvbtppH3TXJ+SdkdxLm7ln5K4PDjP2d92+7wPuGuEC -LiD0fCZe0DzaB96oTsICKvXmWULGWtIXhj9Y9/Y3pmgdrqDxz39BJRIAoh/XdKOFNtq+b3y2GpzT -an5P5GtNU0A+W40XpDGl4KBLk5dyJZeNVex3vaSWS72ueShJv2Kg3UuIQj4a2DtRYgfKmMS9inMI -Hto7T9i8kWOjj+OgBzIE+SRUZvQgmqlg+miTOPTgQDqIg+EojiR1MCj3LOg9Lno4xLYqA20cQezN -6p2we4aYdRV8IvpoQ+4T8UciUMSfikA2dHjxG7voLFad2GcbJxJjh50YmnBkkkwBPl+ARK+b7/Q0 -32wfOSUOjqWiTmKGA1/9Ivx9IVrYRwiryneUQJqQlSHw+obtRHIwZPlm1YikAUp75ZtNuADUkOR4 -/zet2VshAv4wEMIDzKkANR6yjoWYcXYxFd4r8CFjgjDO4qFHOCv42JMwzlJgmGecPawZZymoqyYD -ZmCcVQXPA9u3Y5W9ESaGt8dYitgc42HuMdA9zzUbTFzEk5pELMfGGW+eiVAWEc7CoO9yEFgTClaJ -4SqvaleJ+UoUZWGbjYMDtuO9t9hm54htttvsw22+3iLXh3bsf0aWWewx6MeHmfoeHEBBISB/I5bZ -DJus55T1xZOs1lZinlllmVWe2cHxzCrD7EMSADquBFo7QRqmHLOeIaW3nWY2I4ZnrWAVe6ID6l6D -NHM8s0Pglth6aXLRUPSV46K1vwmD0xr7rDLRsum5F/5ZNUgH/tnJWGe1KOssSP3IHMJm7kZS6mpS -xyFK6jhvcM42PJ7kWO5Ejirn7CzZVTjctjjbPz7NatxXuU/Y9ajG9DuN+1dtfVxbdpTapcXUv1ph -C45GYXWlEY5gZhBRHpFOfvfmGqBRIl2gF8SX/63MZfD7+RhnU85ZZZzdyoZSp1yom5yzFiNEaad8 -/B3HBxUi6Dzn7Gji7iQlOajGo7PgZz0cRkk4nyjnuWhXDLRQ42OzoxQ+LmudhwZ63Mdm9ZuxWSEy -K+7tiN9XEn2d6muKwLK4s5QhdqsUp0rgkYX+yXHKpsyyMbvsfmNUaAwkQ82aB/jRY6Bss7ZqUh5g -lxPomdlnvRU4BucHftjAPRsyYvbGDDsIJH9OGGgXzjEYyKpdvHfKNtuZhbxzPLPGMGtoKamXI5s5 -tOinYJ/13LDKPetDYTz7bK9csEJ9XMo0VP5Zoiy2es+xzuY4ZyPGWbtPdSjTqbEZSqQ1KwMt1qNP -6hlokz6RBEoHmY5ef0y1x5zumM9PEGvaj2KgtfSaDtUNWiPpik5TjPXERzPQ0pmjNVfww8Nr1wy0 -3KO99CmDVthtp73ZoDiVnlzE2dY7/tlKBCZoauzGo37sZEvSfmQLAfekbkMFnnsz6PhOe9NYh/ak -R86Ro70irXPtal+x0PpMIpS3b8VF5Xo453DPOd2tvyljAfd4lfS5nu3W5noz2WdYaPcRC+1e5gs7 -uCdxR+HvXk6yCvXg+SRstOT+Zv29MR7aAACphJv3KGy0pEs6IMjBGGknPbdQfh7WQg== - - - B8dE21nOQcbxOzZaiUNYBByibLSj4PrhGa2PG8nWUNJGoIy0a2z/6NhoG5nlTWCjZZejIP0PhpTX -KK3J4q4CI20raHhKt3yGiXYUJlp20sDTUhQCp7PdG3SlN6ZciT2AGVKsuGh1PvBT6HwoHRdt5MiJ -2Wgp2iLEG+TZaKvHsNH+3GCbztb3eIfxCUbaiHHxWRhpo7ydz8JICyppnCft4hAGkPWxVfpCRtps -P+dc87metr7G3LcuP826v3M9nnPPWZ/DYSf0eq7fcz2fCx+xvqcDWRpk4fs/NwK5MTAgJB2z0rAK -Pw4neWnPstLGjL4HlwtYLdHq4ox4aSXfT8xLq3M47s9JcjSv8wBHzLQEF9AMzMZMmw1kyYWyHCJ/ -DPW1+GQ0s/IoPa6zfrFQEA0DiXMCTmt2WjnSaeBH8MgMFvKxT7wxOhI6Fp6ZNmTjC7y09WqN+1m2 -htYeiDLM+6BCewMHa7UVoLRa261f25Sm1vPShgAlC1EKMXsiKb1vaU5yGvv1GtaqhyHPSikG/VM6 -blruqWPSRzpXUt5e7R1deYGl2HPT8shrn8TMtP7YqfvJXq13tqv4fUV3lpiTVvYY6J/ASds6Pagz -a+soPP5JLq+VnTUw08actKNBjp7KTFslHqHUJ2ReISEoc0CxM36hcKzfZqaNMhIKM20EizMfj4fW -rG3wa7BtBm7r7fCWVTD188Q5BTc8FCkrrTtv5eC37hzwi2SmzQUvPMgzk/fNiD94Iz3WyT7ZZKY9 -PztyzLQKkWkF7WGIGuOSNfxHJYwbJ6lpn6M+5ablGgLxpyRF9pV1iv/Q9Mgb/LQXVhUH8IdU0YHm -1hHKClhl4OadpKh9ngpThtoOY/9j7iYjlU3/OMtQ++TKlKGWIUIblLKa5/qhBLUXV3ZxYuo1Qe3F -dV0KDhrzzFFweVfuWgcPEiKoSXgoj5S+sLboyNlS6FYBzsCbVYRAtihIhIy46M6A4kbYTXlSOQrJ -niPqfmKoLS2KSFWkmY8upigxiX+fPbTUUVz4RBx0zEJ7FEZWf2BUhZJUZ6c4h+OKT6MtSbSJrZVj -pRtTL1lhDhHShySWi2OjGSKNcOMhREVTOrJZXB6Li4tWnpRK4uQb4nrtLDbaRUdTbLTFR798cRRK -F46SpijAi17bfLGfavz51RjFRChXLSq3OMVxYlXk5/HywEsDjuggt7648cVtT+zLXn2tnEQIBo96 -w6iUSgQ4nBtIrHKGjTGK8OCowlZYmPeOh7k0JmblYj4gL6scrmuJJOwF3khSQHioG4va5AM0xxEu -wqFQ0wGqIQkwiD8RP8KMCQt5MJUpQVd/WP/4MSY4KIi/uRTeJV77FM0JK175ECa37oX/yK36Sla9 -BlXwqheSTFr1WHjFoyVFWd7wtdjaf7Z5ddnrU41/uxrNhaKc88HZUecyUMuxW98Nn63lSF7ROq7E -VMxF3CVydNckfprCT2Nog+NkJrkwCuSKnSYcv9SL66QxB5SPqi30IGZp2fU4GtxTGl1L0DkxVfQC -LeqkAxpxXDUCQ6oNnCgQRU79JxG4h5BkBQ7umlR7L4l7FMY42f+n6F+jOD5C7C5Db7hwSm+G8IR3 -Q9JvnwLcQ7F6+ctfHwz2o+/o/wd37/gvjVvLF2kd1DhzHN+ZckwL9WamwMiUdXWynH81Ei9NL0ra -+LRXk798QY0br5+mxv8kppG1My1HtxiMbUy26LGca5LFlGgxlGFVyH8hSaRGgXKHv5Sea6uM9v4Y -fVKNf48p6V39v6boGdffytUnSTI2rk+UQWHrO3FR8NJpCBZx42vWgseWrUlA+CTNjZ17nXpPX6yd -SoEaM+jfB5Y2V+gk/Kzlp6nR4El7Ag8pdEiBQwhGQhASQ5zipNW9xaSXxsjCJs1ubsnF4qFC3piZ -A5mfNeuSG6g3toTUjBkbd3MhDWrKNGOmcPb7NGze5O3DG5QDoY/YP2oDfpScgFgIGRXskfB+OM4P -Bclo2j1l/lAgv/J/IPRZ2T9YQ2G3ieopzATCXCDMBtJFmorf52R3pP2zVF1FsA1h3w07cgBi713x -e7zoK5TaNugroQxJ6TOlyxWosdtSMA3UfL44lYDYO55WNl5ngOFPeP00NdqKPxIrBa96SVhPUERl -otBU9QxoFCYKAbQxbNKvedAdV/DAsOY9ODAfVnJ6xWsWhY149xNR6rOPUpcI9cB78pDVHph+orXO -Th9Y8adXehut9PU6X4znRzh+CA4Sr/KwwvvMCo/Xd7K2cU2jy9Ct7Ny6Xq3pZDUnq1jOCbkV/JC1 -mlllsj5PvcoHFVMWERz21FIecwVBcM9bfpoazeW4zsFyFuefRFb42Aq0g4cIizTLEWP/00xHpSis -ISMLM8lw9AUDXTU3S5z5SHmWOC4j5EDiIFjN1lK7SUtYQA5Vc5mRjsbIFDK5aDYX5asJZbJiDyFx -H+HVu9JFpU1KE5VaC4GrQv6luJSZkjnCWMYmKpSUNi7LyXI4Vyi59qHfP6HM+UIhapeWyRfJTPWM -5e+1xhUf1bkX8zkimyNyOSKTo3JXYVgBBxWIjoAWaII7cUiB8lUhBtfrCZ3oCXo6QNnDcAeWNAp9 -lxACggOqnhBCCFgCsI6gp4IY1hC4cAoXPgBaAfRLL4D3QAq/hngowOPg9n4FeAi8QwEe0KUMb8mk -XI7yopx6HcinhpyZyJhZHctED2NOMNXCOO9LOHtx7+LJq3E9O0m/ouQuTPNSvQskL4cRMFOXAEWg -L0EOahiGBmGEEAzUrVJK/YPpUth3Co5BuKn2HvdcSqbvQy8CICbHwScQINi7N0MvDDTLpPWo4xwI -KD4JRLw1baGoSoKtM2g9UL1nf4jlN/eOxiL5n8VI7yk+yaKfCv9DbF/2r+h9paNXevqtH6Wx94Ge -7cZPE306nF5yJxqvd23e36K42iQ9AYPnR8uyGYpa3lpHWt8mcHq23QUS+/psyShocTEC/NPl+SyW -T3idqtGFGodIvnwsXw6kn4fpN01LoeopVD/P7pYD62fg+nCCmjcg+w/nd9ritMpD9/NYjAx4X0Gs -oOnGAH4H4X9mBj0D6gun6wUcemlABMLvHsAV9oiACMdDfArh8oiACAwS3Mrq4IhGmJBlr1mjHS2L -guGVmoWzR/c2SxQWr8B4ni+M2ykMIK8QeQXJM5JnsCAEzi69N7C8wuUFMA+6h5LjhLmlwPmRIptn -QfwcNBeEQegVRK8hC5SFmgDUigXiTNTzg/MrZDMsSAx5yLGgQQ7FOhQ0IbgIdBaNA1V7OPW8BlNb -iAmzaSpZxWC5BcIcLpJwid4FSsTA9U6RGIGeIprBjpTHTv3BBhfoJpTiIHgqA7XEJED0vTFq4mo+ -kEaxqG/WUUk0RiHRkqeXV/koIXWHKCyHCSQYsdbaiuc176Trqg0JAYT8q8q0sXL7swa62f/NRqJW -lLDrBzKIOnlXiR9a95fRQAh8v41+I+DcpzJMyBxCoPq0MiV0gmVWM0JKFkDGA2c2wGXWOZqAGN0c -GwlzroE1FUPiGlgRMKQECz6cPwTyn0xqi70gNOFrc0psShFEtxlRDoLrnlc9gCl11lQJB59O4BQV -gqM7OGbJDtYkByhSYqqJlNwgR2xwklpCYsYzcyUT0dMP57iCgjE3TwfukPkgok5j80/h8rNU4OQm -OZdieTOnVyY/10SCwsdFcFQvQ1I4OkCjeHsXv4vHnEKj0aODkT5RmpNLIqWN1BR/+Nk0UlqpTWvK -neoJTrczcbFa7zNQuRQeico6GQQk8MiVkdBVtSrEBKngJe4eiYm0yKCIw6eyWGhlaXLsTFZ6t8HB -D0VZTy5yiDmalKcpbHyFRRFpJFHVeuKJnzyX1iMzae0XQhM/OZPWAzNk6ROHZ04zZMUhdhMxzMQh -haW1oskGFB5c2J22RtvTU96QVTBhGkpo2aJWObJcIM6zvs7WeJIMKFdks3liWYfoFJgLvPYco7XQ -r+5CQMSIOW85uoD+qHZld0VhEWUFHyprH9rxPBVi0AKnLyeSWK00hLIoSawE2FDAQ11fgYYZQnOe -XMFFATldedXWQ7trq6uq6avLA3LSCi8OMukxA3vTP0+UiVYmYUJPG36oZ+yuiq5vLp9Krq5Hhr78 -j+/ffLj5L7v57fU3/4YxL9G/H5on/VQiuChF2pMSwQ31diK4ITI4lALWZfL93gXPxMaGlHvhBFG3 -xa97U84WF4CPXS+i2PVOtkGi7nZH8uyB3B3HG3IlpkfycCifxBE52xFdj+l6VJfjujuwl2wkuiAJ -3okR3kyClzMw5UxMq0R4IL1T1o3TBqYtE5MZmVyKq5jHYE0PP5nZJmXSjcYfNHqdAwczz5TOqOfn -wDmjDI0/jGlt4x9mQDoHJj/uFyUADGkNH54A8LzRcMNsuBrThxgNY9NsGY8sgdkCX3K6snVt5zgq -9skKlzGmVA2NmeTUbOvH+ZGGOFr5Ouph3BeJPClMApQCGain/yxI8vPWmIc0125mJrwBPD8pUzCz -BhwzMseYpEXW8KwMXAGL8XdrQlE8V4XQSw6zYt7TY8uMFMoivTgW6ZHCLB2HNLJrUxhVO/Rke5mJ -lQPZ4TGAcgatvCAOjo4SrnGytVpCo2bcnC9MJPi3T/woPMmgShSR5ebRXMkprG3bVpWxgGzyJXs7 -CKz8i/iSI1sI2SHo9P2o5GhVffGYPjY55BNHdIZ1egn3dY5vAsb0YeP54CR3yOD0DKPpXOyOE+7s -WFZJmrt/hDE9Az59hlFFbqnnHVUY00es0EqY/BJzAJxxUtKNcImP5EWcJMcuXHDU2v0JLl+/TU9c -eply43w6eH06eH06eH06eH06eH06eD2xxk8Hr08Hr08Hr1+Mkv7p4PXLG9N/1IPX6WPV+WPZiYMX -XPnqj3e3X9y/uf3w5va7X//aH8j8Oy9f/PEHeq/m9764/vDh5v4Wzmq/vX/zend3u8P/4yEt+jc8 -w1XT9vq/vr6qCvkfnoT+6auP/LylJDv9lx/53/8d/v5fcPU/ds3uD7u//s9i9/olXv+XP1H7o0rf -uUtS8+73eC2+W3RNPv77dW106Zaf4nPxNFaYaxM9jerhLAd0PrKnsVdXIzpm0Tc6UGbO3b9ccyV/ -4ko0o2lnlI/CaXhFbIfWARN/vNv9x0t7tLq76oqii1ralHCjUlpVtlewCtr4YtVdgRrb7eryqu7T -i/T/spOL8BAlern9xeTedO1r/EU3Kcod3bSWp6KLVdSJ9M2qja7RHeA7dMemTC7Ks/G38anr+Fp6 -5+SZqrij5FrUJ01/heCk+GLbX7Xl2MYdpRejPkEf+xhfSu4cHolmWp10U2b6dT3MoT7uJru576bo -ibRLugpm1hBfS+8cnknuQbdsu+2Vo/Mpuqj3oFumF+nhumQ+RReTe6/nE44NnHy2B08n1HqWQyNx -aIYhvegfQGZUdC2992pGRV0l16Je0RkVXdShirpKL0YPwDNq/UxVpqN0XKOOkhkV9Q== - - - iU6p7CyPOir7TDKnomvpvcNT6RzVmfBuc0lnF79OtTMSWS/hpxs/zdydwzO5dapPlF092XWmI+pv -nxFvesk/UXzf8DyJMDvfRfSF6tS8z0q3jCzKydGvM5LjfCdFz5SdYrm5KNeiZ8oJrWgu6dSLOioW -Mblln90Gs/ulXMPJPCazKbqNm03RM+XXcm6FZXecE4MXPVNGOvj59PBuoh7vkvkUy7GcbMtIopwU -9fPp4d0UPZPOp4cqcNEz5QTW16JHsTIGKhvTMSNBM18hGmdR2Tolmu4xp3olulu5qYNlJU9ORBXZ -j0SL4Vt+0MIeFNVC1BQH0RRJyZSHHwQGWFW0r/Bz5pSfjCTKCaxNLavKPuQlvZkIAnrKoYYPtxnt -JenGTS0n25eEl0RVfZf0XkOc4oQk5WdKpci7s2pGJAS31e5UMn2bkw/vzu7V0d1O6q7ru8GAyJFD -OgPhm3zSgBMNTR+kyoe51tDgNXA4JjgnsanH/aPzPeqfnF5fbG/q2d1fnrTSs9CgD2bHqsoedecg -stwifsh0E4+6NadVF9v7fFYhkIdswnSnPiTKd3ww6Ll2pM7kpyt44YoQyajc2a0mtycV51XoZxMf -0RNuq6XR822q1FX2IZ/Wg4nE94JjraImHbipyuZ78flWDD1gnayY9XEq3Zq3D16uzmdfOdHDZnfi -7J598uizfti/vHyxvGQT0nL7WgxIL7M2pT9/uL6//3H353/7ES1K7l8YAVCWXbWrarhNidsEdE7Z -YogA3hgJp55qVEpqfucvau06SslNk8taxe9z9dJFb2DiOISA+qe9lWIQeBwLXR0tT0aRK+nN3m3e -7Bka8W14UEt5UY9V2fZhtaIQ0twX9OjyoDinixI2467GtozypPBUVV+C+G0bUCeKcfeWRRzMm74e -4DK8Xw2tXAbRNwwYAQKTrrWr8C244xWGqOnnuit4FrhZU18hEbxchsYM3QgNqIcreOpBL5dys2aA -qVzbpweMLoBHgG+NsPCk6vGq72jKNVf92OoDr5r3NnQY3LYmnbBDrUgNLvCNuobvl217hUlEtTGY -JQTkYdmOICXHOrS8amv8dHlVDvY0RXXVwZyBR2+gRfpZGOmKr8EqtSdsrkYYYGh8C7uCaw58Dvq/ -aUFgFtYl/VXf1w1dHmprJfYU7ejwFG0P0+NttoGh7X17Ndagv5UF9BbcSKwV8NQFVDNi5dpXXXMF -GtywG7G7GxmEvsQcPf1ugM7Q23UjdFyz60cYnkLa3HcwKiDGB2gFjCxfHKBP4F/4SZDW0rSholAR -rLGvC/3kALOHnqft7BrlCeoxTqTpKnnIVXtCU1HU9SPaKyp4PpDz7+QLXTeOu7GHJVPKIMNS61CD -LQu0++o87GFtD9AbY3U1NKP0IojaselqbNjQy9eh+7C3B1ii1RAerBoaOLnBwmhk0kC7mx5WCXyw -L7vBmloWXbMb4INFFfqkhDkHXY9JXOVi2h43o6HiAWdHjYvBZjRMN9w4S9ipmtZmIzRqhAMBTpK2 -0QkD3Y2fhHXa9rXccIRPVgXM2wL0iVI/WxU8TrQCqlEeA1SOYYAtHR+uxUfnzw6wknAhl9Dbo1SL -a70ANQsfq6h7eayqhXMJLCOc6fVY6HJIGxbaDHcsaYHDNg4CuJY24yTERUy31uUDO2LTjLjUYE63 -tS7tkicSrqKx17kAGxS80cK6l0/BpO3w0FJiE3XIS6geZ+0I97OPViiBBmwu7Me9jhBIwr6hzq1h -wesQw/PBwQyHATqs1eW/apSXWjANS5gosK8X42hSqwcdA4QLSCeYWioAYDXANgCqYgez1EQWdDHO -pmaESd7Z1Yq1XugaBDXohGgwzROcamCSdNoSlFQVTlyUWKC56dUSA7tAnQNhYx9tMP4NBheei6z3 -Ui10VwvD2OEptNEK0pa5RsNTNTWqYzAZq1EHucL5OmCM3VjVKiJLTO5ECmSNW5IJ2X6ETQ9keg3b -QNjNalSaG1jY4fuwAPHuIGGryka/Qr0ZdLQaFl34LIxeWcM213So2bs2FzCJUdetBl1F1D01ngWh -08LEThsW2oz7PsxW2hTbVge6a3H7xikMsrrRvofebUeQpHgTEIMqzqDuomxoEVSt9D3sg7yxVDjt -R/0o7Kkj7m6w/miH5vVS8zBhRxdFoyKtlNWMa6svVKhBszp5sEZ3zR4Tq6GQqbEmFQerlrl9CVZM -MdC8p6WoOm/d4Y4PtbRjOZgELvDUgJ8tKt0doHmg8EPzajqvyDxuB1izuBGirNG9qcMBptbB6HUq -sOFmsMRB2oGAg36qbW8bYUzps22nGxluIi3KExBWxRC2RjJ54BOMpU75tGFubqNYRV0Ia6lAcKjj -Bx2ZaGqBNVjqlIXtSkavRh1CmgfSpxuLhsYURPVgV+FYA1dBSFWmgsHdaH1j++pqMIUFxBspFijR -Spu0sELgAMWicRht3bSia6F4AMEclB44fFV0ue5s7a3a59vesLJAe1M9qvYJX6laui9Io3rQjQi1 -gXGkSQo921kzm552jBqXnTYe9tahK6mZsJyt9YX2H0ieourCI4JmXdMGBULeNairSq5krKomNL+j -eYNKUN2ErqKtAJ+jqkO3xu3zghzEDvYLjinqiu9UZ6PtfZSJLIIGj+Hw3KgJjI2pfbCWQWjvxhKz -Kuo9cSfBcR9Qa26shhZaDuM+Qg90pgSoPgmfDXIDH3aE3h9xNtlUAJlNmhLqgUM5hI8iam43ok5X -2GeTpvlW4wkBegQGiCbcO/1CjwMO1TS2PGjW4YCPLTvqrH0jHHNRH3P7F/RQNcBGA+1rahtY7E3q -NwyT7srweMh7yLrW6JqC/Hj42b4NqjUo4jDY0BcFiMvQbaSsQl/4g0nSNKek4EYJOxtKZJisYneA -p8AF3qFa1QQFCsasxZ2xMyUSPknjDGIM9AppMQpDHGbc0kbdsFFfaMYGTSrNqO0FMUuDDJ+kqaXq -yAhdCzsHzXy61hQ8xB1O7GawD9IIUyS7zoa0Qa6pA3cCCGJSbN7Jx/uOmzrY9lsjXQBsnngy64f/ -n7k367F9yerE3pHqO+QjhXxPxzy0n4oLLdk6hhYtWiDLQnChMFblxcKFUH97x29NMezIPHUyd7Wq -h7q514l/DCtWrFhzeFsVbe5Yf0hKTwMptLdjVaRNKFIIUwNIyqPOq+TBp/N6pY8VDKLxQJ9rut0J -56ACFpuqt2PutK0F6nPJBtwWNJc6pKrYOnRLyHlONIsM4Yg0wsGVowqKQ1/2tUE9HXtoQjh0Vcg/ -Q1MfNKDXSk58WULDrUFPwxhtXIKF9NLmVVSAXOXAjPARDF163+UYAvWbjItA2gLzwMyGzqpzGPjL -kEAHlP74zXVty7I7H2oPuSs6uaTHBwheJl1zqj9jBSlDyB9tx3ZGWyCzSszTzk+myyuS0Wju01it -IA6icJzT83TjjQG88q2xkpIzdzCNDlg1OBOgFQqHYQjzGv+tJuU9rGxfdB7zH9RQhjpkDrQ+1BHI -wXQUdXE9jcM66JMkRp1aggQewbFVA4JUjnoZg8WUodLaHEjsGDRfdQYQdUqnhlN0xCHFbo4uY6mq -g0fWIsaMUlAFlnjJoGCcwmQIOBa0CJ6e7BDj1DTIbq/SmiRByFpeTyC2nNQAyLkLOcHqieUnmDVs -wxutifClDRlPYbEX0OlArBPGU0Yx5g8xH6K3U+GmRFbaBnARCT1ecq2kw9hRPZYz17nY7sw+X8To -TRFj8ops17Iam4UP3AuFPXBBDK4qFBE9q724bgdKVP+tbDkAdN4XkA9DYYHBNT+hxKwgiNWkCIy4 -9yOLHFWvoQgLSIaeCW1Xb4DMigSJIc2Y5RABBzOiGVS7myKMJTizuPqMCB5WtqFMguzWMDu2PDZY -HNV2PaYWEtyZ0G+npR0CYhYpWlAzyN051lGmSk7Ce2VJclxIwdBIbJnMXab7DJSPa8ATtKpoBO2o -eMYiGQJ0vSmTJSRMNSBCh/ONgNH2ZmAxiihbYb/5zXVVzyImn6D9j5Mx6B8VW9RwM8TSiEtqTMKZ -/AOTahl0A/un2SGSY50KTNSuLjKX4BaFWdaOEwbrMPNkWNJMkkuFiGg0bSZOwMw4/oEcmFMlh5W3 -4dKkY612Qpg1IR+A/9WwjLUt7AO0RLbO4smRleq0ag09M7FUM8UnrNjBhQLealwR2AkVslJctFsg -0o3/AR6Ign6jSCftPbvV5JFgDx/HamCpO5fmkl1peNN6MBebw0BPzdgJnzZEgt1mqLBz146VPY2e -gtz/Q8cdm6lS9lino+sNrjPdonFN9U5XYWeHEUGHhEAbz5ex7vEQllyFryKNf6/VKHJ8mPkynsIU -WaQoomEjsyI6HAQMs95DSYMKz8KD7k9m25enG9wU2HNpH6Eo3MbgkRBfi/lmYgl8908fxLjMAnQB -uA9yUj4CMWHsCkkP0bh8IpN4JDT0YHvsCGcsoLVmGgQ8SYFcG7nPkznEY1gJcuIjrsiJjMhxskpd -EEm3x5jDZIbHyp5FTw0GhsDWAbVEQXnCXQZ7ZqiqlcBkTbLrWKEzjasV8eS4vlyALfMqyViclR47 -7MkZRvhBIdAxGDrWDs8u1GwqD8XQsStQHdBvNCVgaJ2kWGFmZEZlaBAzhIeG3WS0Y2UfIKYuBlPY -DtIYQbADex7CMLzobjzaYJFkHoeBffpDxrVSeibT8hTlGrz3g16Ah+5UiW5g/hCdXV20e7hhaklk -op8muaGzQugYLVGj0nCTGI8Qv+KCRzKXjxnM+/ZhZc9jT4HVcrDKmJtZAZz4ARMML8o6B67IjEq2 -5OUEiZ4Blu+9WVzgOMMxHhLTmMTiYSQuBvtbM6IiPR6EBzMsuVUFnNkxha7nhkAeIgGUrh5j12Ts -KOTrG/fqdF6eS/wInxpyUsW9xZdz69PuTWIMvKgtmeG6scEat0oMxpzhGAVHwL0W/eoEgOyOS3Bq -nmQWa+SJBdGUxaQUeulk3F2u1bFyMgmgNen3hqeB0Eb2XW8njbEKTW7MZOr2j2uciPqYS7vj4JSX -AodaT3YcycwxNKnlNA5pAH6Dcb9MmanBWBrIgtJM22iFo1HGne68+rpwvEIn6Wxa6nphTzgcsSZx -9cwDDVY9tkO5FDgPT8k5u1kSCw0lLBrMsaCJnzEY6fFFuMmrtCYVkwz9qizCTZngFckLf2hQiTzZ -mqZSOhg4EQa83+Z3bp3jyjOopqmJdRxv8BX4kUy0G5wEGdsvFKeg1AYTLW4lmDCi0iuQAh0b2l1R -oeFc0VwrZG84jXE/e2j5rFFEuRZgdYlqiCJ/B9/DxvjUAwFxpJmpE/5KB+GfJLk8rWhkAaTAg6C9 -DrJlbSkltvAz1JFvhfz/yhJGA2b+OTN/UUWDiAjQaa59WNdccsJ1AHUJ9ppitrQiZn2guNWpNkpg -xhAHEE5iahBMUEBFSHobB/ZFs+xhmIBZV4ItxlWiyhUMd40DNsx2AotYIdaGU6XcAA== - - - 4j+8d4DS9TvRQ6YemMdVpHlY2XLmUyHDx7iFRudOWR5iJhoJgwXnUy8BmDYp/GLIO93ccuqtBE6H -8qZcLAW1lUENN3m/iq8B8m+wnhHgAlLnMBBFKKiHSQOcpJjYhR5BKJje4hFJnc1vAJfUTVM6lris -HvobXYGIOjBvdiqi1MM71eKiqviK7S0d9T2X5VMgLpASklnTYd2ECduzt9DmCJmEDGzwrbs458gO -Icit0S671FnjIUk9q7cFjdh2B1vTdKAAW+QoKrD42Y1+LnJd/zgjKJca2SygonmBUZuOlYos8DyN -XYN+qYaLIbygpARMPT1WU2TGAYDtbLUdD5Fi6CmFa8YK/kPiIwKvZtH7GoaCDNtTIxOUchdoAojs -SVNEH+itL6TlmXZ4rGWuMkQOctDr5FWaE3sZfeBZJ9U/CnlVoW9NHQqh3uOH+tZ5CnDQIZ8wrWor -BAXy6S/qKaSDRtHxwQJtvIirsBu6NNkoORPgVGh66wEjcIAmvyg554KeJSjWzigc55hUUw7dgXrd -6IYiMmdEjWmFQho+XXQMLAiSeqEzqwjNrDEk+BmUSTXHOmUC4cR5ZVLYDKjO7uchwNM5z2IkMJEB -8R4QBKxhoDHzGrl0ruYDEmHLnNwIKzGMLK/SL11iUNWNCVaxdcHQbTdphfaN0ObI1ggGwv8E00ab -kUcDx6Vg8Y3DpHT+ZGOCnGtOn4aAiCEnEGMIU4hyiEcA2bYVd7Cp4Tx49YCcC3qaiuHgFsMl1RD1 -NNMpIetAYKhgHKoINL3wEYaRTX9qTFPg9aGrcDDkFtdgWhwcMjkL4xtaWoFkSbe7ybxDJo8+sBZQ -sikHUPRJwaCoxHlaE4fY8cVgzBURRwyOk10+LpDA/+W7yIk0OzL+gF5jNP97Ykr1FZxDRbsAFzYG -BK7TVChd4jDVwaCmZk/mQvifnAauDSlQQMlbrMwYibVcHMiurANBPCRbk4Ulzvi0xKTDckOY2CSb -3BBbcU0ugTz72p5OW9ADYTjcSQsXyCR6oaywitNGWG3x5itdBXKUpZOsEOFZ80FWEX5sIx+jqnEU -ZzSA0BSYeZ5aspBU6Ottca7rAwzKsA7JLE7LoxBUhKHUdCUhqHG/ZHNdCj2NG8XuO6OmuASRCjWB -l5SDltxJRIkjek8a+rLjjmgIKm2Y0b3HYp5GQQXxJB2hBkvMdeQgruaXiOtEuhVF1S7x1oEz72B+ -N0IZt/rY3pIW5upxU45hEIZl0dYIBIHrFOFP5uRHTEUfO17zwsXJNxPjS3NLoHUYl9eQb8cklyjr -YzkfoBs4eoYq0uoSiV3YCdTCEoc97iMKw4bNbInCHpf6YAe1LTHYEXf5mCFClc0mPcgljaNR/aJz -wr6EKM3B5e2EIfgLrjWIFXZs4ZSErXYAZ+w1LEvQa2rjOfzmcTWLePuxGH04O0nL3eN6yafyGNg7 -BEiE9u6RveQSfwjtJa0MkXVbaC+k9Utsr0eI32NwL92kD9G9YLpc5mAL78UFWwMsbnt878MKF5yN -YW8Rvrg6H0N8oVs4Di7cYnxzvgX5IlL8CPPN7RrnW8Il0BcXziXS1+NwPIb6+hpusb6P63sal8mw -piCWcXcDwbvgwukHguLPEeGbI4hIBF6y3RME/ZiV6d0VlMUsffiCoIiDPZ3OIHfzBpG2CDXicAf1 -gZ784A96WORHbi2EpyCyavMIYVLFny4hUFigoJ3NJ0SRKDDP7U4hYFXsCJtXaOzBjNuZRm0ymz36 -hRC/+ugYwmQjY3X3DDkO/jhcQ8caP82Vcr/p6rDxwYK5KetkSBuD7+o6bHawDmz6OhHOQNOhr48N -A9tfFXacvkeNnSwwYNmryu4pNWPX2YGGm9Le39LaIeIS7zrU9sGxfDj0dhAJJx9vintGOCvixVfN -HYoEhfXtqjvo+tTc+011BxlCLNp0d1IhsA+b8k7IkSzlhRjPhT1PzHGcf7DLOUhCBE42QQckQmbk -XdJBjNEp6uR0k3UGeUPj3mQdXDe0+k3YARZ4qFXagXZFbHETd8BVkG12yDvHuj7EchoJB7vE4yQw -dRV5wGqJanaZB6aFB6HHqwljk3qgcsHXd4g9jlP8VrmHeUM6BB9PGRmx7ZIPyIY8F5voc6zrWaSU -EIiOLLkszi0u+TNmWcGh4W6J6r/FyXdy2VSv3p5B8mOrMhu4TahJsFcmdgDEpsc3wYqEfAEcTeMY -yKil4K4E+Ug1f3JbMceexiPcpWQMHxNLwaaQicsStLkyB9uX9gFqgkW5U9IirPCKHeKknZI1vZHp -kLAoxI6iS51KQvDJecJYKKr5gS9TuhQlsih2kyTqkWNTo4nHUBz0muKiayN30UHogvUj6SUHmxJq -RxCttrCikazTFMDtDbqt61nkVGDcg9sGdA62wYkmiX/QzWK5mLhOXWHj+7hEZqIJp6Shq6rnB347 -ilaFNydaVGaW+D/EEFddcSF/A3splMSKhKuh19IsD8epdX2c9aIuwCKRlmT2yGmOta/sA9REYi00 -iMEwBoo19XyQJwL/MZ5RAyJlcVAqsmUsm0ipoVCElQaBKuEBzdGwm2hWBLWbjtg1aAy4b3rSiN/A -Zw25R8caA5cGUxUpLX6iESoB+0tUAH9Y1qdFIHHkkG3FJCB1VcFpHs3noZ6q4FZhxzxViBCdDhyV -rVdD8eKo+rIYiMxP5bcrQ91Ubc32UC8VbPip2WjipELEwpSDjqUtqFLvDaVCaT6tuaiQk7JcR+Kh -ChkOqjhXzWUO3ZLXuvinVpFpuqc4Q8Rmx94ptyqX0zm12rqmb2ocvinFi2sKoWdukdq2pT2b3SDI -taaD28Dx1HQNymyQ36UXrbIaZOxZ2IlxmsoxojujQeSuP/nMlgsgjIYyx08+g4wzs/orm4FlbIZk -Hwv6BJOhGJxWdh5DVksNyRAeAyuHCdjKYmB+8arlK4cJK1UogwmS+nUwGLY7bPwlrOkfyl8033nj -LyD2uuRQbguaOBniHOV2g+gQIcQhKRwwALkhZb2GKwyN8CPjbpxBX2PsmjhEZhyNCQ0IEKc71y/x -G+SPoSggnVpDmg4s5LCyW1Jji3IwcNZDmX4vkjYxMYuoaAgmdxwzMAh9Orm2dT3ruKBAQMscOhqa -SscILSQnOISrZA69oX5RgME4/IPrNsMNJ2Ei8tl83bVxTgJwMxR7bUtXNcdNoeiwQcnJBZSFaub8 -yBE8FLlbw0QZHQ3PecALdqunKVRv8ZHn0j5wcrpnuzkIQC9npL2NEwvfqKV/wwsH6XgAQ1S7EvL/ -YIRCxQRLmka0Io4xJfxaIKZnYaWmhfIR31OlWE5VXaSLO7GWNYIYNonIU1KDWUdZjEQz6jPmZ1/O -s4hoTKr4IZhXxEYYmjxH+UJpqqr/IroJp7nCwGQU30WRKpzVzsDK2QA1LR5QoB4rhDZproPu+cCQ -zU8lNjg8QH8oCmHx/D1xxFhFPFSdaPbwv9a6qGHnij5AOrDawYJAwWGW3DOQAksntE9zqCeKhy1u -cSnjlAO/yJlqyoUgrsOwmjMnFfxGhqH7E5q+mQxh/IAsAhuX3VgwfyXyUtuVj/y2HCgsLVuEK4UP -jWFgCLI0nnM1zyIdyk8bjKCEJeEAgtbYMzjvo54whL5VLrw005UQYGalGFT4Bd+sXLRrZkAC8Zmz -NZvVKoCyhmz0nBdTH4xi4OpIYTTBBNF4iPrNqxsxS2AM7kKnprZzQR+gHGQgI6Mm+YVyEFtKZf7a -QjnkBqDEskk6HparQbKIrDPSQYYTlDsE8RnpIAa4Z7JmTdJR9xTCdI10kK9TyXtopAPBBVY3jVBj -oGejRWwL6ZzLeRbpoIYFsv6SW0gHMiZMcX0hHXXhjoaTdBC8jBhAxNPOyhFiZkHOrVtaUtw6gEY6 -IsvGuGYPSBElhA1Oz15iWzWCD41ygE+4j6H2G+Wc6/kA5XgJoobbiIKpXmX9lL/QJahEBHBKSu4w -BZpU3xhrFNVvhj2ukgPe6c2A17nUEwoDdSMnpFEOlgvvuqW8kg8Tmd6VBSBbKcz2CFZdcy+gWfe+ -JOI+rOdptOPZn0DeuObmGYNrtXP1EKMIMkShTlGuE0rJRL2tiEJUGSyVZd7CID04egCzyghjFEf5 -9zOPAnJw7fRtnxhBkZacKLXfgoYJoY02bu7Hw3I+QDspSFAcqv/VaoW+89ASKO90pqk3viBiWRz4 -iHkHM4QZvWlQDvpCpCuyyrtqwUncuMimNdkfAiEC7ZDVqQcCxWKQQBDzIuUnZJg7mpH3S0CsR8J2 -XLMnzgU9i3YSpAcEWHT2U6p5lASGuIhgcLAiWwfRyKYNJqA9kq/AQsYRvI/8eiRSLHG7jGRIgX4u -CbliiO2Y6cUUX9fp626HEQbUkCg1OtgtAIQOuYB5kW3HsZyPsJ0kFS/wBymrYqiInJ9IVpYyw2DF -LgOTBR4CUzB0b1K93JLPhG8bF7Ypa54ERkK0BlW2SXFxbbhSuHWdvg0YdEiXcIs4SP5UViYC5+wK -uHBmjt9caA9rfBY9UY5n5WJ1xcLtuRqdIK9lq3yBqfrAofF+OpSjuNJhXG7NT0xTQQVPF3JaTGG8 -AXGRBdEh26DjmlBNsXcIZKTknzjzKTtZTQBNwYLwYKjmvB2/lPR5WOFH2BM0B9Q9hB2qaYR+alJN -xkH8Vat1yrz3lLkVLfuycrQ91dSxQgSgjAg6QkmdZFwGBfYQeejgW9XYRXgYIr1agToaqxCK3A70 -a1ngWevZuLqEY2fxAwE6VOlog+1Le5pw7TVXrVPs1quORgmgSEur6p2GXQKhHA7yQJxMhMI9EBo3 -L2U7eSgAmBap28NaAehgxRPKlOYQa5enIsP+e0dei6nI8B2G/JKUFuTi1I0pzGjjc2Erwmb9UNiT -k5S+bvS4zIujHIvA9uZZ+RdZVWRbHxdpHAdJo8Qq395I7GrRUtnG+WDrOkK5vYXx40YqScoJTJ9x -lIkjZmdJeiCZHUNC6DK7bOeSZ2g70z3omFYyUrclpo/4JHEI6AbmJvBUZgJJJwPc04zHOpb4NPYF -WdtljvdxMAAZ8igZDhZCY+YDGWRzQ9BXXWZm6whW3IvQDBZCBffKXJ7U1QUyRps4V+05ehNJbnFB -MyfrkCS2pPBT2RPeqgXLKfJu52R5eg/L+yyxxT4DwYrWN8Eo8DQM4bNV8x+OpUJshOBu0i+8GyT+ -TbEAXljU5epromoUQRoRsFZGDVcvKtFtIiUcsyTNt6XGIN3Ng2wxIXMH4vqEqQUistlyz/U8Tc6S -3AsqGRZjM0QlT7m6YCJLxUdoMwBmK1BHe89LWNpRUl6vS8E4XGwQvzosVSqODGDzlST3ZuF6WD8E -J5RHi0qolKyW274luHnhH8HWqUHyYUGfpqRKmK+MXsFPZUKCp9KUPWj4IKQiEVIMhE6C4o3JtB2I -WCCkkhcOhoMMQiqrRIXDAkIqUovQEA5CKnJEFZG0C6VRfTttSHSEAAYzCRyreRYZYQ== - - - UsB66RzYpVgiKoIjodhMWSWmQJY6McKzB8GUBZuYfeEEF509EVEpezksIiL4cOZ5kRwWOEytkiVO -K4iItqNOJBMRlTYJ+FjNZ0kIPpfAVSNL7prl4qTKRAyrE7yJSINMzqQOE0yE8pL9Um4AuR2oXUR5 -3Hm6vCoVxomIG1I/QRCfNCKgTXtBYUcUekevVPFd21ZUQqZpBUtH8hKZF1E3RBn/ua5nUVPVQtmw -/AQV1qsk1mIO0fDVKY+TErm1uBBicnkJfrljgFnOpId6Eia+iA4BTUWZCByHMGRS6rfT+kRAArmi -oBlO1LK7hrdmYtbDPYVNrEuN6X1VnxaoIEcOaQOJyxAnXxVI8hR8IM4kIRRdgDiFxPGpDbrG0hTK -Jc4qO66yMIUqFzPUDqUuMFqrayECxOnjJLbCtTKtLclSqHc9CwvA/Q8RpEGrMWlltCVJCpUrLEX8 -XNrT5ChI//iaiiK2NnEGMQq1ukNd0AAxCg6mNPN7mqyhLUFmQC6EKJQQmTq1a/I2QeV0NlsuiVDI -ckoWnQAkgDbaVhkeCIMEdWxPZxGqsYnrN9d1bQiTMJAX1AXi9ySAICBve8Kh8MuTqjBL1efYOUxj -BoM4LrMWZswyRWYNTg8JYamVlNhPR/fATBYW/zUlii9A8o+T8duU4szGdciz01CMdAAKD11rDFEA -Bb0j1BZXP9qK6T5UE0HPhS2okiccPP3faXahQA1cxkGZN3VCc2trHWiNs4uN/2sDcmk6KOhrW3I1 -1FnrjEtOdTJZL4GcSaJsKVO+LrYZcvmh7QyUSUV7nbmkhDIEFWACuacFvevCPk0ydKdTRRo4cvRw -URgB1Y2pVrUXAboUOEi13DVihu6+xFkGesUUkD44MTISlkgzKkKPlvPicuxnQsvJioq4jahTqyBa -6UFUntX4ZnYrdXZAIXXeMPuq3qcXTIMKTY3mlLD6qp0g0gspbbPiIUpzkC6UuXyAQqnIcVqi7iHr -IXqeav24OG80YleAmvxbpGAY0JBN/YOsQGkM6MDKKSBd3bq1+m+EMi9TqG3e9fu6Ps9hGltP6ZQ2 -Fa1RsZjK4IB7zFydwi85cFqlaa9VkmBg1ZyGMBibGtevCTPXssljFWEpooHMQo79RK08y95BpUKK -dIhrln/n4rp0nC2txQuTp6Pr4vRI7Ev7Bo+B7ZpKxiDjumTzKHF4YFrCA2HfqPLoxmAm00VpWad4 -yNqQRrXYKT7NGVNEnavCsTPjdkkGJTcAMOEs+BJOLaqwhh5MGMTrBdavSa6ENani01qe+NmX9mmy -QR4UmbBKobhGK5xCwX4INI1mRUK9ENg8Sl61ADWBIy0nK/NBRUqKRixwTasFAg5aqnpUyA2tUImM -oDSvqUgkqS1S0hIjQZUXJZ1nEEiwtlxGNiONbVYA3lf2PtVgFhw+Wlho0CrMLjQeb2ahRgl4p6Si -2hZMOEHarIcfJVuWym3Yex4oz0wVp8pK/jGyJQWY8MV0/cHVEWVOPYRF85v9VtuiNEtqdLsKHtb2 -eW4DWyDqzCCTYHKbKOymujVLIwq7ASu0GM0o3Kb0ldlE4TbEzJNZL5nblMY1YQUo3AZ3WnR+NkXA -fulL3GYUXgOm21frG/GauqW5nev6BqshP9nYvkquVsWCFIqua6QgmQzBa6ANTbklCq+BTjoLa8Zi -7z7Mok5kRgSvwaXUVkRSYWZEHc5QAcpyGHpA4YJnhgjrdop1UVgNpjDrhR0r+zyjkUJn6qnSakW9 -cHbUjKiFTQPmHYQo2gmH4adQilY12RPOXHpJIy4ZXqArPMWXuKYnw1C/HiVRcDEvdYrIzQ/Wb1kr -JGqOk53y4icmDEeaULH4knM93+AuTapoFC6GrBYdLo6xma4Kh/iS6Gm5FsWyyGZqF1VfGrIJlAir -1RelAimAwU+jLCV1U0W4ZQEUTIqWls4PWpA+7fxEieLF4NMSdyzo0/SBlDgyhkMmyfq4B7wmlOEB -j6La08klRWUzAr0k+BtpOTDELb1VO8r0VFMkL2q3chzZkxANe7GzWuRBU33cEl6Sg3gSU1heAUEy -Zy0s6AwFuVlbD1WehKJqPqJzXe8TCqaRA8sIRR0T2Z46W18UQIx67NyUIh50PL724AmeAXKwPDt2 -tta6OAdZZAbUHjVAtlSI7NReqp8jYcRxpcA663nH2e88RcCaU2+0eQHOpX1eQ5La+7g2ncWIU+g6 -Kn8newwBBk4HRTIsLoQC/RFatl+CCEg1QEZCX/QbaAYwi0XHX6hcz0Wi3FIbsshLVBQS4qZ6NNQq -npKFhYIFdwRfhKXluaD3SQWGOVKaJShFl88l3geyLVqVXjtq9YXqhC9KI3IkUGDdYvRhHob3FOY3 -C68rIjmTTS5MbSlWR6XYpzUaFxOS6umBKhUQcQsm7tMb8wWi4MiNYX3551jQ56UUL7ULS15fw3Kd -C2YXxKlYxqRrnBcNKdQkJ/LWdgqNnLZrAEmUK37JzSDjHT2Fgccc5gN+SPHr9LhFWt7TAoaiZFea -MICquPTwlZRJtH7Jn1zi+kzXsbJvyCmYBa6FktgI+WqdkKdgDcMh0yQJVmkJJUEJXRQQgzxf5gti -cM7gkY6wpvPD/DYEqhLWXFvY2TDGVt4KMyDOhrZTVPKeA8DLWmmG7HeQukml6IsZdFvZE0RbFECn -dEfIZkmRFfk5MSp0MFU6UHWkBCTcxtNpHBgxlFRVp1TmtXBRXCoUQZ1AQgrl7yfLQSJtKfBDH8XM -8WgtyaNUfX02pnNI05svNEHPIH0JYdVzGucKvyXlJnn5IkNes2d54CWA+YXSsMuKksqvgFM13T4H -JaZAJbDSFNgdZxpQ9nue4mdg2x2BwxSBAxd3oDoA0xoRRbCh1nWqCEnLXqbFZw7mKnUeS5il4c81 -fpqQkEeIdznaWvkZRiKw21b2a4MCbVGHNbR5bZBvGjXQTOqEmwt5sG25Qqvnp09aWi6yGthjNxpW -S6Krng3KLX+xe70GzmAYE5r3QJV6Um2xKp/LeZ9mMD7ywlplfepVu0AOX8OzUGUunpTetkRqQ38s -PFGKz7XbBbdjS8vDmLhccVEPYLHSvepwautFgofp4BgBKagVijLWpUu9GIEkZKa0vATKnsv5NHEE -KatH1tGmSX/8klgnE+ISCtilghkMtF1TY5FyRr4yvM2YrCqjk6LLgy/OolTIDfTcNJkGjYhuRzEs -KL+iDJWyP9nC65x6+xDW69S0aSo0Que9mEGbqdAPK3ufUJAHS7ljPi2V+NAJVd5F132uOCIWlE2p -bkIlXCYuhr0u4XSoDmoyPlUQBzeENbe1iRyfvZT6yco6ERnfydRNYXETO7Pf0iZ6yP9PU2i2FefK -NqL5HdMgkzC2VGhxryfQh4EHGJFgcKa8s073uTy4zL9/oq+iuFFno6OXuI+lXxlwoDm+zG7KMVZZ -xrIJSaN09pIuY3HV6i62qkg8+/WEpRwp7IdIC4BAMe1Z/338pM56Y1V5NnnoYh1HvxEYGczoK+kk -7+PkdRyZCjfpZxf9cRwuWa10C+VeC1YvMJol/OhUzB3+vUjKhv67ToCefSKfO8FCP7pYAXV+I7DK -s5yd1H2cauPMqWiTvYtjPXOhSbFKD6dplP4CJOpLktRCD42NVgjkpAbym/qjyFfsx2z00Ms2ln5l -QKI+64Zquy9jyW/9iiekjdrZS7uMxRV8M6Oi50UzW4D0kGAQz08XZy0q79G/y2/eqMT7Yo3S0Una -R9KPFEj/tU7yMVJeR9LpzEZ7J/ky0q/lsPO/1MVwbkCkdA3ZtkjBWZRwRyNwCPl3+q3MhieqjeLR -SdxHMr4mQJIYrZN6jFTXkXQ6s9HeSbqMxPzJyeOOaVmtASNPFKEPYAo9cvdNEhX0N3WHiAMEpyyN -zk62kfQjBdJErZN0jJT2kXg6AlzWQJ2ca5qrRawT1S90rPu/rkDS18dEq+eTUqRcKCQt+Xf6/ZOI -VXTmlkZnJ9tI+pECaaLaSXX7SPpbP6LpWKNwdBIuIzGnEhpH2GDUgtILMCHmJ8lBKVJmK8lp0t/M -POTIWaN0dJL2kfQjBkpKn3VSj5HqOpJOZzbaO8mXkX4tbILwkOuytwaUI4fa3UBmlpoXQQr162/m -HZHHXhqdnWwj6UcKpG2xTuoxUt1H4uloo3R0ki4j8RONgty07u0CpG3JwhaSbFsW3pHWvc3CYKxR -PDqJ+0j6EQNlW6yTeoy07q1NZzbaO0mXkfTde7XZzdUuQJooAu8oykdOVXSMcf2t1z7/42x0drKN -pB8xUF4vsU7CMVLYR+LpBBtx78RfRuK9zWxiDG2hZANWJkKYS+CoCUI69Ags//ukL6RCw2i+NNo7 -SftI+pECiQitk3aM1NaRdDraKB+d5MtI8ix2Znad+rK5K5Q2hnJdgM/URdpxYiBQwE/8XRLGsTR7 -6Ggbz74jaJZYOesou2M8Bdh3fIStWT47ypfx7EVw6hKBosvSJ9RmTPFrNRxrqmGbSuReZrN4dhT3 -8ew7hkadsXYUz/HiiWqaVjwwNDtKl/F46V5elabEYpM8FrBnEqVsEhJc8dwXv8FW5EVdhfB0fNFp -LA0fO9tHtU8NzHa62Vl4GDUco8r0rGF96Oxc6yJtSgWbvPLxBUj7VuQBvSyTopde+N/nduDJahzT -pdHeid9H0o8YKCzYOqnHSCsft+loo3B0Ei4jbZyNni+NO2MDjDQ8ZSX0/ms0diM/N742mxxdpG2c -ja3xe6glvsxOwj5OmOPYVLhJPrvIj+P8WgUm+gdPE3zdYTCnRfJKc5arp5DxpBPknypDcYVhaRLP -LuI2Dn+zwDL53q0Tv4/j13FkKtrkoYuHceyJe3vtULd0A9KGAEL1rMjZFOm31xbaIapnk7JmjcpD -N2UbzT4Tk5njbZkdoab3Ohr//kknyVOajc5u6uNowrjl8iZKmNxrgpXhOCf2lKBMyYmwYBBhpnp/ -rA0fO9tHtU8NzAxndhYeRg3HqDI9a+gfOjvXuuJBGedq0NugZMeghF9+1pCtdQAwN11tepx4inkv -zR572ka0Dw1K1oylq3KOWPYRZWLarDz0VC4jyvNdci9SseI1/NugPOkU1RrUeJQU56PwczJJL9Kl -2WNP24j2oUF50rOrfo7Y9xE3M9U2d+kpXUaUsx+FRMjfqctfoTxpVI4j7oLrBKNooRUFyIGUEI+1 -2dlT2Ee0Dw3Kk55dhXPEsI5oE9Nm8aGneBmRC91JmaJt9SuQptyOtbcgnHRdejtWbnXtrJewj6Vf -GZAm245Vz7GWRbdjzeuMuZd4GUvoPXM1+CY1LV5PKAWKei6FS846tKJ8CGoggJ/0mBD/XZsd/aR9 -OPtOoRQbMjtK53hpG09mZa3y2U++DKdWMtZe+mIBXYBUJg+5QKIC0eXTuulJ0y7ZuilT2mjrZPkd -148USJUurZN+jNSXkWw6AlymS52ca1q2WVO2Q17VkgXK2gSCMoH3oKyWnkDlFouagA== - - - eEMxwUqzenZU9/HsO4YW0Saso3KOV7bxdFpLs6OjdhlP6r2K7bCutu4VSnZq1N5go5wYsqmQCbVY -zd1eldC12UNH23j2nUIlUk47yud4+RiPp6XNlolL6IC7jCeHWxP1nVsvswUqUxb89q5zkW3ofZuL -7NZsVh96qvuI9qFB5QqqKhc6uaWaSo+Lh8MmtjY7e2qXEU0XL+sqXk+oVBNJtvrIc0q2+mUHXbTV -S7N4dhT38ey7iST+I87FR5JQ0lz8IgGluXhtdvaULiOKNu5UQ12k+BXI4rfrquyygO666sSLFO/M -8mKNHrtZR7PPGGjit3UkArqNtkrxNiVpZJO2bo61LRe4CrbrQ+8LrKC0c3Paf8NDIk3laP7J7Fml -bWvy0MU6jn4jsM55kLOTvo/T13FkKtzEn134x3FY7Q76D6tPa0LFrp299i8Oq+x1EqtbK3ud6Wz2 -0NE+nn5nULYJWkf1HK/u48m0tFk4OzrXt3nzuEsnzqLXExrEn8fPrjvxGRWxvBtAnXPVDGLi0js6 -isd4+p1Bm3j1qlkL9/HyOp5NazY7OjrXtxlbhAFsxhaDqbGF3eDOi4XD678vxhZ2pjs1thxdpG0c -/UZg09ginYR9nM3YIlPRJkcX+XEc9vw0sTetVtQFSEIGaruT0UqMo6GKZWs1oZKdPOW10d5J3UfS -jxgoVk/rJB4jrcZTm442akcn7TISU7QEmMFR7LQK1wKk7KEiefPIvEKjwi8D2++f5CN6i8sataOT -to/EH00gvb9onaRjpLSPxNOZjc5OHkfS2ANWYZiALPbAgAXqEVXch06TORsyqF1Ofv8kX8lrXdIo -nb2kfSz+yoBEsvFldpOPsfI2lkzIGj308jiW3MWSHtFWZ9cKZaEZkcKk9RgX9Xym2urvonBr0hH8 -vEyPjtw+nn3HUHFXzY7COd7mhLBpLc2OjvxlPHbz6Qn3iwS6AkkXjsYn2GYR8zQlGiqj8RtplM5e -0j6WfmVAEhitG+/2sfwieNqEZqOjl3wZy96XoPB8etoi2cu9E4zwzc5/cH1BXIdoSHXotQ1BflJd -paJQ6Nbw7Mwdo9qnBs4hvCx9hYdBwzaozc4a+rOvc6UryXd+xRFu5WSy9wKlMgpei9EiTYfTzSV1 -UAFCglJqcTZrZ0dtH8++UyjHcFtH6RwvbePptJZmR0f9Mt6vddDA4eV+84MZ2KnrqlK6KQDmZ6ra -ZHVIVc5A3Ro+9rUPap8a2DxX0pl/GNUfo/LsrF176Otc6cLl1ZIV2hpitkCp4mwI6o5RS4BXn01d -Y7+8OnZms6Mjf4yn3xmU6pfPjto53mrTmdPSZuHs6FzfXHqV2EUELdn2K7BpNFFnDkOGn9EIxfvl -3+ce1Ma8yhq1o5O2j6QfKZCjiaST7vaR+kpjOp2l0d5Jv4wkRgbx2edVyZ5AVo0TV/BIYqPw6rDf -bCSQoH2ajeLRSdxH0o8YWDQgVDopx0hls8bIdGajvZN0GUmVD86I9WtK2wSSFQQyEagnyf1axH2n -v02BACUujfZO4j6SfqRAjpnSTvwxkl9H0uloo3R0ki4jMSWXi5F0AdJEa9mNpDVfjKQ170bSmo9O -8sV0uQBpotZJP0ZarR82HW1Ujk7Km0ZSsikFSvRYYzcmVEIunMhDFlAAc0zoBmBjrQQsKxBWorWb -5XddP2Kg+N2tk3oOtXrv54yWZls/D0tbKFoU2bIlaU6gEiNpreRgYDpz8iDDQmei2FijcHQS9pH0 -IwVqnh13Eo+R4n52eDqz0d5JvIykMe2bRP26AeGCT5QySs8eN3kzSUlHf/MVQb6zvDY6O3mU3Rcg -TdA68cdIfh+JpyPAZQ0co+YuI+n5DfwcE9VFs/NrQHKO1MJvUFEFV0dnsWoD/NazyNX4ZqOjl7yP -pV8ZkMoizG78MZZfxrIJSaNy9lIuY2k4MNtMM+IDgqZ1GZSiCCicKmTOCyxohZQqbUAAnoe8ILQ2 -2/vxx2j6mUFTavVl9lPP4eo6nM1Km4Wzo3N1FzcHPS744ObAHbC5OfTomNthO2DmnVibPXS0jbe7 -OTQ8fXZ0ulXK6VbhaWmzZeK7m2MbT5xbZUYkzqUvUJ5xlJsh6xioyS7hkKtAIbfM2uzoKO/j2XcK -lTQT7Sif4+0CjE7LmpWzo3IZT5Yu4k2Mm19vQsUdJ0EBUWMIoohNcQv/019rs4eOtvHsO4Ymdcdp -R+kcLx3j8bSsWTo7SpfxTM/miOq2Ln2B8oxRdkPEEwmfcLwLCjBFWQUUa3Z05Pbx7DuGakVr66if -421BrTYta+bPjvxlvF8rqQllyEMBrw9gqkAICDMMKvqHLHAzdyjkJ6UYIT5rmB46S8eo9qmBqWrB -0ll/GLVvo9r0loZnZ+daF2+P/lNoS7jGAuRAC7XlkO5IKbs8F/nNjhi1CmmjdPaS9rH0KwNyoIV1 -046xlqCgOSFrdPSSL2PxzosNuW+WBQWqLUAM0arLebFWbwqfF5P20mjvpO4j6UcKZAOAduKPkTZD -gk5HG7Wjk3YZifdXylRBBrSoeQMGSUKSN+2KvNmGoqzy7zOWvcmDFEujs5NtJP1IgZzJpJ3EY6S4 -j8TT0Ubt6KRdRmLnjsQ81tVgsgDp7qHQX35Yj45YFo21rtYSukkGFVqjfHSS95H0IwXSf62Tdoy0 -qnU2ndlo76RcRuLVWghina6sBcZ+KHWPBsrozBbxOJM+LUjfmjx0sY6j3wisiR/KOmn7OG0dR6bC -TfzZhX8chxb617/4oz8f//3V/xb/7s9//sf/+ve//e0//dvPAvjTf/rnf/lZQP/55Y//+uef//71 -n/7xRSAv/pe/+KML8GVQ0RAFx/9D//+O/3FcQHr837/5H/z7fx9//z8D+h8v6eX/ePk//y/38o+/ -APxv/oqo9EVfcUVHX1n6XP9s/FfTPzDjv/sTGsu//BkP8Zf0H7xkIE9L14Iymqh9haLVSCHvZRxs -RC1FlMMCWlF/4KHyGlQtUcUgAvKQ5IGyP0eDr7PtV0Ov49kgXwfvo1ndNv1NL6VGeOzoAefaqBAj -hGv+izUsx8neAvP0ULp9qz+DMUH8RH0hfKPforQjvHxJ/1JPIo0rMJ2WfrxMe1LMn/w1fWlTfP2D -muL3YfYdujHqiGsJ9D5oyKEEeslUuh4l0EE0Vm7ASAV1BoU+UE9x/qn0wW0fSAV3wEoq9lsWhDDZ -ZUFDIVsXhDc7l32QF5v0W/0puJKfikr9VjCdy7YPOq7AdFr68TLtk1TmFF//oKb4fZh9j8VQdczB -WLjwu7fC+CgaR+Xe8c4hClZQIypcIZWBiIm8yp9JuJlb/rqwm5VaYArGGsyPp7+7nDwvKAm0kqZ/ -MHInXnBmGaX6oSKYTyz/EuzLd7I3Tf8QvwwPqG+F6XyC1quw+Z5kYnN7/QOZ23fh8j1OUnDjEFl0 -vWkgZCXcSk0vo55QdBHVTFwcOoe8h9OMPsafShXISZx/XrjMSiF41HylEPstq0IxwWVVyW3Lgm6/ -7AMp2nMj9KcgTH8KPvVbQffoed0MHVf/teybsUz7JJQ5xdc/qCl+H2Z/L/ykOaOXNrnIAC1/KpE0 -d6UX8LyVXuy3rIq45VxVDduqVl47YMqn5dvJtrlx3jZDv51XwLoZOq7+a9s3Y5n2SS9ziq9/UFP8 -Psw+W1RhJqJSbQx2zSx/XhjOTapNbpdqifAX2QsHg2Uv+WuTagUmQqF+qz/dJjLqedVv5Tjjpeb4 -KDIKTKelHy/Tvki1Mp3XP6gpfh9mfy+shZmIirZKJKmtf14Yzk20VXqx33EXwGQt45ysq1rZ7pQb -9dvJwVe5UfFp/N5ug3UzdFyB6bTWi+UNeplTfP2DmuL3Yfb3I7swK1HZdvw/kWjnXxemc5Nt1U1m -v+suj0Un8pj8scq2AmIZ0T7UE7nIj3bURRasIj/KH5v8KDCbj9vlx83xpbKtTOT1D2Ru34XL35ts -q/RBjzPYVbP8eWEzN9lWKcR+110Ck8WM07Eua2W2U3DUbyffXgVH4+pl24vR87oZOq7+a9k3Y5n2 -RbZdKeUPZorfh9nfD8E0ZwTTJhtBJd/5p1JJc1eCUZHLPO9tX5aKYLKYIYKty1ptTlNy1G+n+WqV -HM241bbdQPDeshs6rv5r23djmfZFuF0J5g9mit+H2d+3/VYfwh5/Ortz5l8X5nOTc9su5hLPXIQx -n1QYk782MVdgyp/9JkO2TYRMXG3evuSYFPTLf20SpMBMQpRK9Tbli4grU3n9g5ne92H092C0Vfoo -Sali3Djzrwuvucm1bRdrdTUqfMkaEEKzrGa1Pk2ZUb81O9YqMgoS7UtGMfpdNsBGZZhJhPztnPJF -pF3p4w9iet+H0Z+fduEow3xdJSyBqQRlXDPt1mX7rT4Qv1mX22ZcTrv3ZHee7L6TzR6uVtIs27Ba -w6VQ8sc1PhislvWbOStvEoNNpezrt995lxjU7O82BKiwIjARZfRb/ak+nrghwZSgrILOBQ2fIIMa -NjyYmSZvF6HORn/rZO133i9CWWkNGx7WXub9rd/qz7Dd32bQahse5rQ3PHyEQ8K+0CcGzPrQN+OF -abD6u+zcXe1c5tELyt1XO5dJI2G9kMwGX3fbf93Mcya9dpVzVvOcHohPCxRQnxd0mHLdN93cbIZh -R4f97jszkwXDpxUerwc1IvcNHfqzbAxYvzFzhLV6QEfTs7WxOIEpCzNNIu1WBvsddxYntoW2GRnS -ZBPG4uzD3ZC22UVUWY7C4laryOfPNhSXBQGm1sSNx9lcyo4A+x13Hqf2H7dhYO1l8jj9Vn+6jceZ -Alg2NMxpP5eya9jQYUJ73FjdyqxXdNjvuLM6WXANGzrWi2OyOv12MvOV1Rmrbxs65rSfweqEqWys -To27btd67HfaWZ1qPWbmdcrqVq1n7WWyOjPJ1N0UVDdlzaRva7Uqa89kdSs6TPCqG6tbefaKDvtd -d1YnC85lQ8d6f0xWZxaHvqFDf7qd1c374YKOT4mCIqhufNJrtsAmvOrPtnNJ06n04yRccpVd0+S0 -xiXtw00XW0Vt/Yqn0zZB+xli4Lp2MxwxzFigzKRsa9efpi/Ebe3oOj0ySIHpLeQ3Btk2/qj6ZNkw -MKf8PBFwxYHZQhhmfI8noz/bzhV1HfZxUq644mBVJyZX1G9N0F+ZoupMbcPBnPLEwSeC/eIt2C/+ -8iV/CanT/1LCPb2jTv+tBSN+KPxv7fN1AqRbMfDtY+1A/vTr2dcMFVTt8P76tQcWGz2RTsP/FyJl -rZdy/gNKSaR6+UJ60nKGj109/ov29fgv0pk2eOzs8V8U8vgvg2niAOCop+0mTIADghd1cCyy3pYJ -p8GOw5eC52/lP5KHyO9ICRYW3D9Clt2QZT1CtL+vj+NN+8v29esCUdON4Ob4+e74V+sBnjgWJlnl -OSpwkYj/A9jgrQ6YjAwRxmkVdngpr8K3snd1Wwsl0JQdU0GScTZY+wLqXj+9YetxTQ== - - - l4Wfk5tI3Ru/XvCoBHX8fH/AHa+BhYzBZnEdEaUZWmsfl1RdUI130fEWmIgknZ8vXKmPnowsO7bo -0ddWdxgyp/NOSVdMXBZwWaYd/fXTY34TsTt3ev1Gn4JcBOQPHJ0/10VpvzfY24TNQi8lKDJhtzZo -nV/U6ioL2vEPLDiqI2asMNV9B26ouCzvMrHb/Gvlx8W3Ne3DrmbYjV+634kJ6Sdf9x5utAoRgQSl -AmmB2KOhqgrr1P8mZqEbn5RzuzIoPd7rgSU2kPvG8igxzdoJbre5XlbzuOQbd94nt2BTxT+8tChq -Q1U6cOO/GUey6rlNIjgJg9xZny5z4yLbOpUfbgtVhritSvvagTz7HU0XdN7QLt+uCLhx3Rt31lmv -355rXjEak4jfUd6qwyvpfNWqFF4Us3oZB76DxUchDG4jdOVw++YLi9tQckX9dcL6+bqyGye9clzh -fOu3ygK2QRS4TUe/3iZ+rvuRm65i15U7CmzrQT++Atevn8UJlHfp4l7fRMMVYfr5itkri3znKtjW -qh/vCDgmOZHNHGUTJJ39vHHQN7nS9tGBXZO1PPgNCQDCfxxrrRO5hR95tJO1ye5Er1ZPVgRnEFLe -WZ9S3MYitwle5nxZ2ZUv6xhynr5e5vdUDmESy7p4Uw7W1Svz2pavXG5bq9LgDmQa3JF3QfJtsfrt -ipQrX7/eADbx9fNz3StScUKTXmZdBR2vUiVr+IsIREJpOhCqi3p9czSdzzavK3O+sjPF5Pa5iU7r -QKbRrXi2z9cN0YF20pUp7UCd/Pr5lW42trtdRHceK8BtNP38CnxPBvs0b1Dmput8fROhV9Tb5+sm -3bnoe3fLtm79fEfGMc+TCavBY2VumxFEYUC/wgaWAl643mC/I6vbOn+eEiH8kZabd16tePn61hIv -E7tM/8rRj2EndpXkN/wacEXcDcG3WZ493hD4CfOC8NINgVeGfWXtyks3ROiMtw24LuMdwts+PiY5 -0a2T2dBtwBW11z24Turs88ZHPm53OJHzunLPbYFXPnvnyHLIt8+1xYbI69quWLhu4q9v6H19s+N3 -ras7u7pyeQFuW3bd3H13PmeVVM65nYg7j33vFvo2SV036Bx9kUPobqKrSFyQSnuxQ4/AEldJzr38 -lXAH9dmw4KcfE01al+DCpJX8za/4q/zyH794UHhfF963KhHvGAa+ZWa8WCPfMVpug16U8X/gyXtb -Hf9BFzxLZTiggq6o+++JRARtg4nKjdPwX3L4jqa4kUAq07FD3mCjK8bboeNuh/ubWv7NHPCe2eCb -5sebnfKmhQvWzItVzMUFSlFFVC8ZFnwVW17+uTGS9ZBNJMNVCC7IZMhoOtR+dQo/GDyv5sCbAe89 -Q9837QhXg8PFMKFo0jUKz/9SRQMAsozl69lc7VnfcYR/tR6ETUq+KT83JemqOb6jYn7T+HUzkl21 -Lj2EKmM3vi2JPvTinDEFyqwlhMDoSlFTOtC7H+EmzN0xc9/P3m7cuilVV/Xrqha+q0B+2zZ2taJd -VbrzCGY+NhpsInairH/snH6iNSpJCvZelkiVqsJhZAf9fhR3rF2NaDc709XY865Z6Nv66V2Tvem8 -/3As3/h+F7UOfyqjbhqsojedIk/NcYUbEKWxRICqtcLvk0oNnndmP6GbTesmrd6k2psS8Z628U1b -yc2mcpXlFW8ztEmsRV340Ep0RQSE44Cawmw34YZ0NT+dksWpRmzuzG9bD652hnctEt9WTa5KzFXd -+YcPCWIf4GWnReF1O0Hr2t/V/r+tENxVh/eUjN1AdLN8EJY+ESRSbkEi5ZcvHs8C9kL/RUFUvLDC -AP7DY9wPhYqcPb9uQOldD+g3wNLF11u/F2Ul2Rkk1s0RVWIPKcTyRfJ0ZHm0rNgvuXd6+sM7m7AA -4zgOTkfr48M3YPrx11uPi+LxgaPOGVP+MkkFbhMaLR5nqcBtRmef6zQ/hkrtsV8mOZDyOJ8rsF/m -2M8pptuupduuxdu2xdu+nX0+Yd/Kbd/Kbd/ybd/ybd/OPj+/b+Wyb+W2b/m2b/myb+Wtfau3fau3 -fSu3fSu3fTv7fMK+9du+9du+tdu+tdu+nX1+ft/6Zd/6bd/abd/aZd/6W/uGJ5AvbNJd+eSVUV45 -5dnrMqD8S76x5XJhwTdYvrHl/Hy2nG9sudw48BWYb2z5Ns3PseV448vpxoKvwHhjzPFNzpxvnLnc -mPAVmG+c+YlbV25bV25bl29bl29bd/b5PM4cb6w53bjwFRhvvPlx6+pt6+pt68pt68pt684+n8ic -8405lxsfvgLzjTk/b+v6bev6bevabevabevOPi/8OV/5c7mx4iswX/nzI1qUm7Ybf+Z57rz4Bms3 -/tyez5/bjT9vEzLh8wZsN/58m+bn+HO58ed6Y8VXYLnx5/Imf243/rwtP952Lt627uzzify53fjz -NqN827p827qzz+fx53Ljz/XGiq/AcuPPj1tXb1tXb1tXbltXblt39vlE/txu/HmbUbttXbtt3dnn -8/hzufHnemPFV2C58efHrTNO2q78eWeOV455ZZlnr3NA43L03amM83enNn6Fagdfb73O8cptvHId -L1/Hy7fxzl7neP02Xr+O167jtdt4Z68rmQUL/2My4mPQ5P97dnUeDmOLgDLDuBKmBr+xaU08xZE8 -xbYfTG6v627s6hM3uMFuasYKI8Pi1Vb333gBf/ndi86XaV8NGFfDk5HmzdxwTjw9deLtMvGrBn+1 -vJTLxNtbE69Pnbj3l5kb8NvGh36Z+tnnQi2nXfZzkz8NlRuZ5wtJ32DfNn1uhJ6eSujxRunf1uWv -Ztur+W+j9efM/TRXbcT+bWX2arq8msA2cn/O3I0045Xev63PXQ14D71eKP458z9tQBvFtwt132Df -tiptFF+fSvHlRvHf1o6uFrGrWWWj+OfM/bQBbBT/bfXgahK62hU2in/O3I02y5Xivy0hX60iD71e -KP458z+16o3iZRqH3HIDfltT38UZdSc+h+i3yefr5E14uUK/ra3uQs1zpn/qVhvd7xMt1+lfle2r -xraLNs+ZvhHpNv8pnWxT7dcFXFXOh35vAs47S/jeID1eDEeAqrJ3sZhtnhlR9W6wVbE8+ns08awD -pdtA8TZSvAyV3hqqXoaqt6HKbahyGaq+NdRUlm8a9I6sKwZvKHRvjqaerdtupcvO3GDxtl1vu0zi -bcPSbW+uwHjbsbet/PG2Z+m2PVdgvG3aO4bpeN22dNuhKzBe9+0yoJq8bxtXL5t0g5Xbxr1tSy23 -jau3PboCy23j3jb/ldvG1dseXYHltnHvWKzKdePqbY+uwHLduMuAehRXi47xx812o8f7BtxMN2ef -Fx65jpauo8XrcPE23tnrhVGu49XreOU6XrmNd/Z645YbOt0dn3eEXjF69juH/EScWr3FqdVfvvg4 -rlhEB0bcp/wGaGqF/9s/XtDo7Pd1BUrnJkSsI+5A/fzrrU8CHtllGl1rGQCaPPIyswPI4M4RoyJF -FxwVevws9LbWIEJDTeUzScTSp5qVhpBQ5sj5VHuvJX/Rp1JtoPAFAYwRr47z+3w0Lnht6vP3j3QM -jkbaB9SQNP6rvxV/P64DWaMx/8z1DKkTReU20tnomO2+nh/nQTh2dcXeTNOwImzLnnxJHGO652mc -c9tIp2WOlPV43zTMVV+xcDaymdYvvqM8ohKUS1+Sc0m+OlrVL8HN1bl9iP0fH2Z5Q/OvP0OqOYxJ -tXAl1c+kVmq/C7HaULz9uY212LiFs0+3he0t9HMlQf29oe9sNNh7zOUbJ+JsdE50W8rnUa5zuNK3 -VpdM6nZo0jX1KNHoWXWXwHnae7/bVipQKSilL+MOmFi7YvFsZDMWCtbfO50/tIqDcMo3jtTZ6Jzv -dcM+hX7t6cqc1UWaLF9w+lSb5adY7hxn+ez9LhRvQzEhKVEqWC+lbW1nI/3v9WPjGmcjDDhO7Hun -62xzTHZfzufRLlN4NtEfK3tdYEpC1X9xbblP7hg8Gul0hZbl507wR5vcx7+V+P7ROhsdc71t1KfQ -fkpDn71MT5p9XYG6CsKImxwOaIu9pR0VZyubquDTZLYN6WcrG/O93T0bPUz4dhI/hXdldkQEPSx4 -138BZ8su7YIcXUU1mSSnnysb1N+bJKdAGxP3pr/JV2cLEdC0AxUvtlHORudUz/U8CnILR3zCiT8n -uYlzerxUUuOhL5jYG5hcNbhA92+Is2cjFtW4A7f2vf7Dw8RuKH6KBPdIabi7pHIP5X5f8iI1vy1i -nHD0uFGoAnXbWTjTQYVvbavaW9g0mfQuQtXRQqWyd4/A2ehhlsdSnie9XYja0nqv+atF2amI1Xt3 -2+4ZUIhG5TEe84K6vYHJTEKodzXwbCSC2JsnZm9wTvG6O0+R02507VVBXtNWH2ocWHUHrmWwd7sR -twKVbDYdowe7H7bVnY1swky/FwHqbCES2HuH6GxzzvRcztMEtQt9S2GiseEiN2hE4ctSGqxYPvJa -OupY2iqmKQWpBMYD37C3NVBJScj3JjgdTXSgN0/R3uCY3m1zniKZPfd2PCl1k89M+BTJS64olc4W -VOwtTDwSTF7FpbPRKtJe93Nv8DDH25F7ikg2/ktW0mfRtva7mSZtMJGeVFbTwZVf6u9NoFOgiVaA -xzdErYdGMqJ2ohLHNtLZ6JzuuabPY1+nsfHdJ4qD267aYHJnqbSng18ReTbSTuhyDW9Ix2cjlv20 -C7ePsP/jwyRvG/UU0fBO8VLTIajYorWh0KP6xKtKL2l70kT63SjeBlN9gcQ/HVrY57a2vYWJakLH -V9HtbKTy37vH6mz0MNNjOc8TEm/E/hkb07m8TVpUOlJhUAe/IvJspJ0oHd8VzrORjPjusTobndO9 -btlTJMcr2XMcy8Lom/Ac/KkiujF6qRu297uRvQ0mxLS4UTRqhe6xbXlnI5PihKyvUt1DIxEN3zti -Z5tztueSniY+Xtm8XbLMafyO9KxCvWPI1t+2lzqGUJAKhjrmHXtHo2JViImUb+Ld0WRVdd48V2ej -Y6q3PXqKFPnsa/Uk202ONKO9SIl2o6kkuaHkbKW9KF6vkt7ZaPUHvLnHZ6OH+d7O4maq2o72XxAa -zRX8Cdej28USyAHT5ahjXmWus5FNdIE/CA1nI5Y7tAu3j7D/4zFFm//z7sQNxU90e7n9NuQF6DWn -g16v+rORzXQRCB9l9bPRIsW9uZtno32ycyVPY8PPRrZ06zY+zLNXBqtDXq+Ws5FOc7HGnuzxaLIa -Ut/c0bPRNlFbw/O47xXNn/CuuJ3f8ryVj9pRVW67Lf1sZVNcYxdOHng2WuMN3tzLs9ExW1vKhmaN -cLHYgsTGuc1UOuUwNu89N/7lew2f2wWx+b2+P8DlA26HX2+T3nz7H4nk+ZTDXesJe03QLVN4Ns5h -Okxa3wL4LgS+u4K3onB+pyCZT9DfM0JavtckeU7b2xOjEj32spTga9nK1Y7/c+qMGw== - - - 4r8/5OUDLoZfryteR/9IYM/H/eaf2PLvwt27838rpObbx+5JcSXfbZ59wpm/7f33h518t93/19t6 -18E/FFzzqeCBTxDf92Dv/eige2zLN8/OEyMVPmI4egLvu8YKfCig4UPG4e3q3j283x268Qn36ydo -8LsQ+Pbk72LP7xQT8Qn286Q4ho+YgZ5492yo/1C8w4dsvevlvU3hu8M6PupV/cTOfxfu3p76Xeb5 -fZ+550UofMiI9gS2eyObDwUyfMBou13+2wy+P17jEx7qz9/7vxP23ok1uUlN3zx2T/Rlf8Q+9kQ9 -czNcf8jd/SHT63bl7266jzj3P+U8e8K9/zth8d0VvCU4fdvr/SQv80fsh09UuzcMfsgT/SHz9Hp9 -b1P4iNP94z6tJ8g/vxMC353/W/LPtw/R8zy+H7KwvrmJH3LrfsD2vl2m2ww+5MH+uH/ue5DwvhP9 -LlF8k5bfTpXNAPzdX/zrz//13/7l59/+y8///MMPAqcU2vVffvFHf/H/0r81/rc//bd////+b+vL -0mkJjKTaX/7CvfzqF3/kXv7mP37xR/+OP9ak2XvKLCfM/lBr618KLveWxl+JH5n/oRbKAvZxgr8e -4FrHORxH6OvRy1vw2c3PmNNfjv/5UvFaCo67vTdX5Nx/aVpYzzwuFvzi5vEfIyBtOQ3RZYwQvoTo -Mi8A8EbvexAcD1QBWAc51s5AkmkYmFKTlmPqtJ8MDz1L4zGCfl9kMJJHeaTeo3yPB6/0+4IYY/m+ -DIqVxqUHASZ4QK5rGD0QPf2nX/3bb//sX3767b/8689//2//4+U/A/bH8Lq1WsovX/7Tf/vtoJl/ -fvnjP/3TX/3007+//tW//vbv0faXL/8LWv6v+B+eyyDyBmMUrTG6rlhqeOqoCXzIUALrukgfosAW -HAXndY2N3+JheGpJGs/l+Fq11zyuQFmjq0sHLbeHtnFIdALMXns9lvB0JPUvrSjJRH62kcFz5il0 -nmL/En2XecdeswBdcYINehrnR+1hTj2UVKRxHmhSoNdu9yn8HghhHB4lSs96I8MHGcqepVKboLxm -r+Tu5+4KZ8DBevlJv1+gQtbY8poeYVcyqMnrac1Bmrbe9fz13t5FMRq7ovsRis4/OUHxIF8l5QMD -/xNxnL0yipoUxbEoqbdJ/j4J2nuuE8XR8NHiQIIewCrrrj70/0lL7HBc03aPO9CFeVR8iZ7B3vUk -W+YDk1ZmCYqBKbnOwFhilH30iHx0leEDTEjyeJmMGcoYrSQ6bN7jMWQDFmW7A948U13Bw+1ESz4Y -SrPdBgNYbGLjtlHGPyQf3ZM8TkUkRHsoyLVxt2RWABDiTSgCHOQoW+XlDBG8di/zjVXnlSN3gPNe -kywi5pQnOZccG0+hl6a3VxIQ6bZ8c4TBhwS3JRhfzl8QPyrw8Q8vDFSOkzkrkoEhJC+YgVRoPcwZ -eGdXneuZehjCcrbLq8XMMML8vD6l44T3fZW0nWzaQG1LioSdmJ5MrZBaWlXm0PyUESBeNGHicBMx -LKZIk4njmhkY+FtuOyTCVPQWgIbOwNrKefogBSQ5fR3ijCBkfOdzlp6TL9I4uuYZOM50E+CQcRoD -U3G2Jx6lFbrAoWsJUPCMCQsLCeohxxxCq7OHIBwRL7rJpYO47SrAwVwxMeAs91KlJRSvHxWXRUSo -iOIkQRqXqFyMpOor1n8P+0qUyfgIqU3hNVQvmCbhn4EpVgU2m+Nbq8w9yg7kocxrY7m7BjA3w1Nl -Bhxx3S09JB8VHrvOIWSbGDORVdSOkLLC7EEP24DHnl90yVF7iKG8XPHwbH4/WGLAPcK7Di7BJ2hw -RVKIGN6JIH1ltisUUpn7dUgASvw5Gb/v0NSFnGJyTRqnEgzRtQiwFCXdlPLSg14OY7jaojTu1oOv -fDADwuSTHm0XlNkGZwLv0HmxhQwsNUtjV4kCAOw5yXIdeJr0MJhD9k0bM1sbwCG7dh3OFQGGHHVi -QcmF4dngrVvj2vRouwnsxqBCkztnwIcEpXhIQedAb8cyevucQzU6BnHbHHxRBlXQsyytaOPKkhnQ -ILMaelqfE3AxyQbVXnW2vnblLWWu640JJJeV740dksY5F+lhtM0CrKHpYgc5zx560x7Gxci7FpZT -N/TMLEAaWSjXBEk0DkpmQ+RVjM19X4BvzSEbxgJkXF1arwdBvkMMzgiyNx1OZauFIP1C0jDxWwdV -CUcWO1W6wVPoSIbxRVStpDsTwYIscd4dDIwhV+1Btkb40rzpftIeuvO6Li9TTfSauKAldgUWOSPj -Aq1R78oBr0XnUEsu0ngcTm2cWfwIsNplr0L1RMGA1xCWngWYVSEg5VyAoddFrp49iGzQHDOmkIfA -oArR4LtZgVkvP9I35Pshbhl+qxOUFVg0RIwouSmQBDqR14v1UL40Q7pNd9owIvfFwFSV5/oy51C/ -lO5luJZYvBjA2m24Nu77/66Ne/R6i4vCFhr0fWV5jbWOAYyxp8fhGjeZVxl326xxFe03iJmIgH2K -OAFGFyfS5NjNJPiZyp0ogoQzPTtEvT8Zzh7aXjt9ewaX6V4X9jYWFL/j1ERuO+65nvLSloFTQip9 -Un/ni4zg4wAqzoecoUpdZTPEO1O47Np1fx+I4X3KOcjsb7XxhSav1PsWqVfY/OVkqXY0VpF8V5k7 -u4nJqkaJMraCNz5CV+OrzlMRCzROgxf7IsBW+PpKDbn+AhzUoDaMTGWpBT5kN8JwHqfbMTengntE -DrnxU8AEJK7MPZSs6k3tqCFMS4YtuTJ/QPUz3os6qMwlMZh2Z6tAk0aC54A7ljErNLCaBTjkDAbi -yWu6rdXkypjsnocP3tPwDcSkSoNQ44DNayMXUztHn0OrINxWnjSPTjpII02QLSsDQaHxXT+2vnXr -ILHw0kiV8yy5jcnohSKsFsCC2HYZKo4xef0VVEGSFGvNBTNiyhq7VtgEVYLJ91Ads0ogBZhmMayb -CJS7EXi3NWED5TLqbLfk+WdUrCMi6o4PLOglmZrYxlFgm0QcmrfP0q03STAmsxB3U7xjsMuzDYmg -EFaiM715HEHch3b4o3cCzypLDiJJrCLDMZf0LHVRMRpT20968QTRSgc8Fr3tGxMWCEdM3xCCkpex -wpjTvPqcWHBxLkVeyDgUsufVO51DE/11UHTOuo+YcGA1evyFMyaXXwyVqaOwCo3J9iRmERLOlvu7 -iWVE1doBHGczyXyd9JrwzLu07K0uckyyHnorKgmN0xEYOOTKIkDvlGqJrqcYZXNz1anI1aKXFbto -clipUa1DooH9wFqEza2yKAiFpQexuKSaVLsJqathZLAA1W5ggu1iyGmN2bBvZnyGOGFql158hd8m -lx7mPTcWF1hmGUAOTiWgSEe+snGNgdEcGX7ssZjjC+tuDFR2W0zlQVleYbd5XL4mjKFe92D2sjoR -hdCDZ+zmqfoVE/yEAKyH1BU/Tu6HAfQhq1UvJTXUNdEoMwv2P2kPl8ZvdPvWHK4Tvi7tjgfY3YLM -TV0JHoqRGsrGoVHbJM2RgHRj/qiWRdVlcBb4DBGQxf1kx9WLq4GB6Nasm05MG6NnSLMM7KJ5jcYw -ZP5A1k3FQ+QC+tJDooqz3NgV6SGZLjD+OTQvQHVnDGCzY+GncJ4GTfKlOpDjzJBoVl6xVIqYV+Ls -IWVV09S5NlZRvWrcZFSW9XovYzmceMNk6XoHNt1NCB9qzxh8O4loDPuxU8FH5YjFqEws6F2sT4Nc -MtOdH2rlxFll5dhPI98Ajst96cFEuiETelncYZf52ydbfsADYcPhEaZ/FRwvqskjONPVk7hCo3nz -YIMQYWkz/YCRNrWxmZYIF5vut7FcqJyqaZN4abr6tNFQdA0Dgzd5pxh/Plbx0zPx9Nfirv/zn/9x -d9b/7k78F/8pNz5Jfd2Rs0GMoGEcIro5BPz1AFcQ6Niir4vY+AiaHy9++9/x9bu/5xHBwNia7dgp -9Krw0KLIxS5HEAuAZIFjYbmkKEBfvQBXKy3Oe9aeA1mcCOjYJT0a51wFOPiQCOYtlDB7IK8LwcXt -CaBIPxVmxKjdxsrXh1usPcCn2JHQM8xTX7VnEr0ITvKW4b8hG5NVhB55xMA3M3fSXH/Zd2qIkjUu -I/YWpYchY6XZc4+6RhJMbCaDSwk8JF1NpmQ46pvNEISk7nRqg/tNJJVCx6+2wbdj0caN7v8BJOu+ -dMv2kQodOGoHCdKegtkijLbNKyJaTll2usaYlCZyXXqoJQo8eWlbnO6IS629XImNTeH4p3HlJmL4 -YxYlZCXDIS8EVZrojhK0FRbOGEVQcSacZVCQTPaMusJmNd7tIhMs7MHkFXbVsgDP1UhGV17YrkDA -CusoA0VMq6LFWA8UOyKnpPIcIBJXxUeLAiMtjGBkbppTMDTRX9prE62WmDsDVVVEQT1nZy+PE1X0 -PITERAF3gOrFUVeGe73o6YW4YHvaecXNqRxMQJsDB6QQUeRgRNHK7GCwJl0wPMAMbKHK8Rd1j4A1 -KLCpiY/g3avCDMXsqy7OxSZwua0BpCuNCSV4BZIkyMDY+uxZfEVVQmy+2ohCbUO7S8JbshHEUNh0 -1YMuS9OQqubqRLycJMrqli0qvIrG875R+zwHD+g0fqx3JOC12IHsevBYX3rvlN4bL91O4MOW/lqt -MErGsLbqOQU8Mj5a1lUCqD7VbEZlAGPLi5zO4kIaRzN0jTkISYPUAG8WoCCOmuxsGtgytpsknFc2 -SA3mM80eGVFpJvWJgTE7jjnhxpml0Sx3Egv7cE1aD+S448Y4cF8V3iRAK7OPiReoERXJHPnZMS9h -SRkivPXcWi8Kb1l7DmwzoJ5LsAGHXBecaKfjzFQZcGiHOoukWD72ybawNQ6/FcFYd7DBKGl6Q2OD -IEwUpo6QLUdbqrm1j6tRzUuNtX6GN2KSBBRbVJoms3MKk74Ce5GamaheFa7CJMLxUp94ytEJPDnZ -xsB/sqrs2dD32LMNCsJxzMjrl65RlgIWbtArI5uAZhpMrCokES6mvdBoutciZ5/Y09cJj2WBK5Ds -LyfQr8DZsw/as2dzHB2AJmxNcf2wOFt2cSoPtHXZAEsQl/E7AgohNDMdFlxE3UxZWZdNjUtVOAv5 -AOZaHnrYpzD3JAGRlcVesg/JpmQO5RjwzE76rwoXeaNmjVFLWWM/q7AUBrbAG5jZWPejdtBalsZ8 -QY9bj72BBLMtQZhN0l7dZGBjtOgETjQvMysmxWY7FtQJX1eDRDObDFNhXxYBKzwLP1oPJUlcb5CQ -p2VuMOVzJCjmkFj8KYssTosrdCJg2c3ZS2NyBDNQzAoARu0huRpmD64EWQUZR76+sU9fdQvHZCk8 -m6UFaPOvBk9exB4yHn6d8K7txftGnXgFws5+7fnHdVBXRACE/PHNMcmFy1dd4CDEWtTOTH30/nLr -eOVZMyqOnMvGs8bBVB7es/KmEJuaDFkIu/Omd26BgMqPwtyHyBakD4o34FlInMTj1A== - - - 1ps8lyo8mG7s1/dvpHnYhx6BWIN5B3bzyEmUFV0/Qa6C5tiQ+zjinIwI68QcKLbAeJFemwjPZKaR -u+4EbPByU8GRkJWTxGYxVcVxjgPBk5y+AVQXXtW4zhsrkpm5Jnb1UrLyPdF4O590bihKG3kyup8d -VImtGX813qwCAZyR1tl4x0ANiO2LOgDHCYn2hITBNHQOkU80XBm5a7cB7nhGI5ijIUFV8aYxZMy9 -lUtX8XsA4YE9BoJlm0OOWTaInB0/sKenq6dHAssArD7qbA2PJbIpkTuWONMB1BPSzdheEABWZGXN -zEkl8r1PRo9cOOQfg4UkHiGNHC1B5yrmEekgsMOGwWJqL/DzE3sZwOKKLqGwn6yvFg3APYuiXfS1 -rwovTJDdmRBSAhs9GDkSqDHQ2J3uGik9ht659eSoYaCKz92EXABlvW5mM+B7Fvc6QnR90Zl5Pgs8 -M8+2ZzjiXOpKZ06PBAUAcc/zWgTcECReaZATHx/AhP4B5NsEzrlWlwOo2nBnzsJAJ8JA19hOHCAG -5BkvnGXlPJRJfd2idQfdNFEsGivbfNCEFnJVF08Xvi/91i9UQI2X64VBDWbQkhxhussFyJcyup3H -GlymywrIhcNAZ4Y5UswZGEqXKSREw0gPRVXIJgaIH8ihLUoai3Q56wnB+mdI/jvycOXB4MQTx1xG -8LqTwG9j0WHwkiQuoiU0J0cWMdoaJZ4RYGROplb01uohrzeO9UDBXQRXl0lGSIJ68VTYyAhaU1dZ -KvP7HJqsjZg3A1PpcpMpJeNZZzbjQWec+QYDaymrE08l94Hv2PUmEBsO0YHYoxtedwgLfRSvTmEm -+zwjRyTCQEjRJW0IyckI1/sgVwyZPKWHWAWo/k0KV2h6Q2WLEslI2gjiE05eRyOJnGGq7nQWbvnq -q9vVtV+qJprlsqQCUATXq8G7uiNrCIo48jcyTXUhdXgmNeq/QC4zqg45dKU1ltcA9L0qrXHIzOMc -pjjQ2A3AyywaOkxwuUSQFlb13IdQtTEH3R4tvTFawEtRuNLm2AFXvfbQ8je2ZWmck25sEELGhF+u -a1gFxiy7mDh6wwRGNfEltWGRbCj5DskyJiBFNnXRUZKDsYUYzenVukmXXe0iGuCAsYoXRxbxJ+tB -xaCEyA/lFSrbwD8mJo2HVawLFIs52XJ8nAsMVX20GVYJ41pTKM4SQJGnqwhAzrK4CrQCrxoGo0cz -8FXIsyjFMLdPberDSbV4sl9UnXWZptDE5pCv1l4uHFhM+M4esBjUTSo340O/c0hvpimIss4vOngw -EXdq0GSTogOzCI0q8gGY12u3KBOlC8yuTrEygOXq1dk54lpujaCnQhOiCss3ky81vU0aWzkB9LlI -Y4rXe+8QVr3UwbCcXb5qY4OSKwxz/OktVWeG4WQMYquThLKMXBe7TcT7/g6fItMxN3asPmfYBrzQ -oYRTo1dhw4V22b6fhieXRN0awGI3nygbADo1z5HobFhQMSyzyCMLFhtaYfIXIUa8uQgS8YsQ01qV -w9DZ2cEoU51R9Q0gN2e5EEtdxaDe5lXd9ZLxUa2aGpGNm6frBpOcNS+ZppeD5EGQKBe7rSKrfFey -iiwOuDNKDVUZpXqaISiblViRQ0BL6JqYJOlXY4nGcoo0jinIcGSl1PM2Y5zq7OA4hvOmpLA8OdB0 -El8NXoLYjymvWIQUZ8DI/nEAg7KJvCj3cWHLSw9i6tl7OObADORP/p1NMIgh6mKwK7gMXhWeq5Ow -tCTqWZIYRRFs+PpKCDPN/UGISU1NV9CT5ZgNYGGHBvTG2tUCqokIbbW9J2iLqrsWr25PtI9y0poK -N2SpVHMi3Zg/kHlBWVNbfMNkkpeQ/cZDyyxc0FmIkpmQLZREZqJDaSZUX1PURbMwMeabqwpdRXIJ -BjAkvfLJV2CLUxcXQm+L2luDybTkiJGxxGRRoDPPRWiqxWhbXT7aVvYZCxokdBOTTUtIZIuKd3Ko -maNAQ6IbU7/BszCdxodJOhGBvW1+mqIIHoRoLMOba7FNaRDe8CrTIEFc7t+epVsyCC93dZWekzO7 -mOoHjT8TEcOpdO4X5i2p/DxhySqCRCO5BtPIAX1Igq/OOSh/qayDyCokxmlMTPW/YIpeZbOPHV+V -RKpp0dC0bDtSNS3HGc6rWckxBQmXbGsHVGCI207XhbqZoECkRehpIqIS4ZueVWUNmgSRJVmSG3aL -e6KZqalFigmQsthFX3dTFlO1suHhwWUjNL+jM+NQebOJdm5Guymg9bUOA8hJ7LvQmJx6y7IYOLsl -l4M9mL5MFDJdIWx3pzkk7YGCVgioeUnTHdMk3XAyKcGiCrfgAzIpurSYSQ59Wo1VcbK4yvEfTGLR -DKMEV/5CqoR00hRI4tWVgTN/9y9/pvNTw3hjy5QxeLHDKLHYyHTkhQaMiVE04EYYjz3LzfLXCCfi -0Z1jQwLHhwC/lp8rAfmA56oJupmj9+u8HADknFtEo0xWArjEucx5AtiDuMZVV3ycg4nQMaqzqW6m -Y1SDaV46T9OOHWFIyG1pL51wdlydLOmxZxs0ND0PNa9FBJATwXJWFSvFV4VnCeKC94QFogGUsJma -2cx47dkGdVGN6pW+VK0YcHGJAe6UDFxUDRRwucsA5NBVYFLufwCLuh5Jaf/xjRFNLvJd40YQKgGW -8mrwqCEUYi/3je9dgtXoDCgezMqUIpGUjbNfuAO5CKiHopVtNKD0YQrr/kicCbCI2DLbH7npAJeD -TUB10FnWVzPn7wDCTvGj7TyLStQzW2oBZHUcwFDf3Mjr1AynSJIJUf6JrqdXg1tXXQSjAWzeH25F -AIt6ENXtLz3IrIv58QHMupQu1vfHOfy4TC/5LnRGkRs2PZEUAC98jQygBFkD2Nj8DSBzZZyoYHT2 -2LPhJI3riGtl1sIhwcL7EDfH8XSFt++rwuWyrcUyA1NgNx47VtXb+tDzXGlRQz9oEzrUq8LFNooM -oSD5I2bcAlDimmPh61AiAkyiRs9d4Y1lO8DYnbT3ekxhhgg1tbQj0QdWyleFS2iCwgXIZEJAieMC -kO83Af5oPQTWXzy7l76+MeLXZTIaISIOVJuM805CxVg2QbwbmYPZhVpbFmCLJPgBOKOVKofJCFgC -+Ro7PfYOHmZg+4gI9+aErtD3q4ElhqNb/p2fYZDdrGQIsmfNEgFkxbSPAc9FU8nUzgagBYrh0b2X -2xQmO3eWdF9YxbV7ddpFJYSRgBJOWTTylipkSGJfYTHY7lVvmrbaNjBcsuHktnmcwzq9o3Mr36Ai -X1GXGwGj5QNxNBWVmOD4PbKclKX4AgXOcONkVRJ615mo+ee6QNnaYok+mSu+vBo8O03u0FyJYjJt -ZhcPA4UpAFjdkg7TnYayKWOlHop66KW+y+Mc5hmtGl0KooT96FXhmSOoKerU8dqvQatiMmPSSzOu -EmJo0XBdXzUOVA40KFqjA4v620F6IAPpoWjMHwluEnOMgNoq3aauvVKyJ8GC5X9T6CzTF7HrrqM1 -p/KWWC4owtWp5JdM9yZ4VzHR96ZhulWaKsMobPiVKCe3TKBx0kIVEVgCLY2NFu8VqNJk5ZpwNgEK -k2fZoUoBkKZJSJQA2pRXUr4qYyuUhVcG9iBivRrKWjUygG69WgUYOJwSUhT8WXMObO7XHMyvxvrY -SUwiWrG59SJhyY7d3wAWZhVVHEPWc+GwYNCOGX8B9yEK27Kdr+osqxJA+S5XriI9LsH30H819LZ3 -CSB/OADr/RWjehMamKpdGTGo9YyYpt0+6nFB+xBl6aE1BSoNNovrkEDjH98YcZMvnDrPqJzUq8G7 -mr6IH3w1uNhKKssdDFT7WVW78WPHKwJ60bJ6JCa8Tnha4YYACQIkeFUESAzhAdx7ngF1iJqwjFZs -4uuEq7lJ1TgC6ioR5KXAmnVFZsSitjU9tp3AtddtClPiEnJiARV68euEt6hSLuuTBOz1kIchEZiQ -nKYNU4QCkXLF1AiZJKroyyR7mcOOPLX0rVLoCude0gyfrBZWc135w/dLYwmOAOX0l+sEpq7TLUNR -wsxeDd71HlN7WMC5N6caOMZ/18ZUhoPhEl2zNS5BEo0fhpub6E2sTHy5vb4B/2pwDrcHXJLmAIxF -HJNNvA8AZvVr0p3749s9i4CQ1W5XJZ3CiiJJUUxKcuesvswcnNtKyKuHeha1gxnd6rNWkKnF/JLo -tVngpbiDHqewEdR2liY9cVgouIvajJxFxgsrEiBn+JBV22t1F2o8+RZrI2lG71bLkb8dZ6GoYkaq -wH6WV4VTRCjDpe5dmHkpwU4ogJYAikf0VPktS5kvDQKmHjTVcKlPkbzXgjoT+49zsx1HsrrEbESW -mF4NbhU/rHDR2BtvVblE+QWQbz0qfeSWjHsVbyPbwhk4YxyVDpDu7rOiqJqrDo2tWKcKlSGZQzdM -WwIIXAvbknP0R13F7FnVAlSxCVqLxCoPwMVn5WBN3iA8eC2lIz5ytA1WjFIrW2VbbmBPreF+dmDW -E4ubpqq7XXdPg9+4Yqh1QJYebjuVdupYCw1RfdCvb2+2wCX4MZjbMlg6FtULNFiwAp6W5EUbbYU9 -1Z6EWbBER5GsL1eS+nGhtoNWjNqixcNmcUyFGZCEIn7VgD1a9a0656ZOxDjNUdmC2+Jk5jdyldnN -emOJI2UsMVjzdJLluANoqdIaVYQyV0mlLtrXn7SHIHuIrHEre0aOYumhWb7xPocZICIB3rZ0iz3W -KO3IbP4H9t819X5KWB/59BRHS1WnR7i5G2tuilNxWsEdELQc2ZIC5Ky24Br1cM54XjTO8q08hz68 -GlyEQm8hsyiWyikazVvI7ACKyRPAGWoEeExS8kZFFwCl2IhnTVyAWcvuUDjj7MHr8TZLx5xYULfR -4yImKSVVMsBOYIR4VXiVyBPx7Qj38srnKKbnPZaWzKsUOEpDgE6Zj4b0Ps7hxwX9KuYEtgkZ+tWp -Mgv8ecTzauVBrd3knYUwxDXSa8Bj0POpkV7eLaUSrWCHszj9yA4V60FjwKKV/MFwYv6ISs+Pi1jX -5yTQJLK15HXCtQCll8uDJqKg+q25iaFe4Y+dJuthn8EWfvB4Ymb6FMce67U7k5+alkgkHXnChUeG -qTJ0zfNtwaLf4K9ziq2p1HOwQdHrRQyp8MefxauRU1j0WPhcFleihl359b5J+uADHyPhS40LmHAn -UrMHLjkrah2dVeWASy7pTaZBivC+ZQXmYBEImrw2Wk4JIlVO5OZdkNrLqSwTEwduQrSW1uVaKtwi -8iNooUULTKjGOCJfAoJHr+IOCcaGHo25CGZbIzSodDW+0h6CnewW/BIxoaFwIvEYggfaqkp73rLq -qjaWvPuDw8+IsNSWaBqqdMmrU7ki8T4zfjQsMPHB+R38uHENIRCgZC5IvIsMlzRomDie9Fw4B5vg -Vd3ZxZJsqxUcTsXCM8q2d6h8pNFmGpmOHLKm1lHVOZDp1aPGA+Y2e+CRKOvMUuY01g== - - - qHAygnzvNNZMmV7KSwTwIhenZA7owiZnbiy+WooI47hIjCVlRsqS4009yHkxazLllIm9I6mRlOp8 -CReCC9WyTgGvWg9cnE8p2s1e5llJHKPTjii6FBfbNd0yltkm+SKtrLMQtwyAUnQ/JVNZC1dN+VER -7CXDpvK+MlAjpZaYqmzBanA159mBs0TKIFEq6JUrRpC9xWkVNJWsK/tqDDtRplYtRBDzTRoD5kWY -BFBYyRbvgx5csdg4Du0ZwBA1YkgF8BSXLKMp9CcrJUGxrJwaTm2LaKRZqhRhKy3ifUk+QuPg6kGo -cQnKEiM0ASUuunKqlPUQpOxIZb3SvIVehIRqOlwKS3hq1jTamzmP4apsSUC52Qn1Niv8cpEMJ0Ew -hT3GDCyzTP3MD0nRwvcLm3OFWC212dhctDysvKUfRzV9Ae4n3qRmWbGQnSRFiQkYp56UPNtluAcJ -6wVQ+Ebl6gk/kDWhGubJ5m89uKxwfUgjeSsFVdg7yhjTyOzKV5H10L3G94tVnkpHSqZ+Neklecs9 -qBx2aj1QsDnvkej6yS8WyiKJpdSDUM+CR79SCQ7vV7W1aDkMCbVkYG564uxSv5nvBJ41n0OVxTQT -85bpOguOrGv8bULVSnWyaexr7CxjMOlIPeM4H/copB+LCa3xRcxTEzkyItUpyiI0CwDPLRmpL8p0 -bEvNPQ3jwWjmuhNXbY2zUOKWqRKbmVKqFaeO1ar2VZYaBChRdxLvaH71lhRnFNb6VeEU+crwrqEJ -vSpBaR5PzJrDp1HIrO4iHsLSOLSaQ5yXLJV1eeGRnIWZLsVeEfMQFO0qwcZsjs1qtVMjkj6Uoznz -5kbUp+hres9XhatBqHJYxQ8UszTNgjZaNNdvXa1xgIt1o64SD+AWhK4nLgbLnCkct8JAzeMTM6D1 -rEGTM/g6WjQRpT7LhiYzghS2HNiqnUXNF5YLsDjBROb5MlBjGxO7r6SDoAEEqOWhIV3B7MjZ0r0B -FDMKsu3c0oNqCaMHL+bpYAUbE+txDJweqnnzxZnBkvjmM7t3DNN3JSVLYS3RCuJk1vmBy5CKcwbv -FxhJuDWjR2ptjw5UH0nsy/3hZiA3w/kszZIdX3y0YCf41ToDMViNv8K6j/UgBa1og0y9i545jsCr -zKI6vQydRLVjakVzCuk0WMdZqiLNWwu9Oq8FLZXYPStI1JIuFEOPt1IyhQMMqAev0m4WHQ6INGl3 -5qxGZ+brqlVxAFTdpU7aQc62Xm/Jr+hNmjVOG2TI0awrRPVEQ9rqb7MQIEnE3e44uF9MuqYKbXPK -EjhcvkjcJfw3kn+TOWpSkJPsXZ1mfrLQl1QQK4zflwI3Gnby6EOyHqhMNjfONl/102a7/1FFV7pN -7MX8SXvQcNRiIfmhrtVcshUNVzVFBDeZAxKKVEzrGmiZrVRmtaQamJSa3ntLZvBS2hYxAtVCOKXK -ZuPwcnmsIbKWxkxR7brR8jEL20p/1B6m7FZF8grB1PqpUKDyb9VivnT6pIeV3VZJYaVaj3aOpIQy -HoGw9CZSQqQHx37/jYsDGFUlUYjq77hbvBXKhI0m6RU1U+G8pGELqWaN8FzSjiXqzq8JFjMoCPBi -9YtBU18VrAXOZsKrb3bVFo30ADBZANGSZoPG7OrVwCLpuXIuHou81S/wKVA5q5xLxZbkbCStiKpX -bWZF3PyDPWuGodIEwsC6Vu+1qqzZkgXLmtztxQXLHJCjLKhbSRfI4p3wZUl/ntE1gGs1xKwF6KgU -rmVDaubHNYJJFmwJu/ZMVzFFrpinD9FSs1J4m2VoiwkzxTIYqQztLCvuNfBOdba8lsby0Xwm4o7/ -gau6Wp0xikj+gYqyJovkIqO69DDgthNqQMLLZIY0NTMCKLmOeS29QXZZPUYUKPlV4a3pZUIl4Q0+ -zRwUSmlw1Us0VxBm4K5HLMgjGjDDB5XI4rSbYTxLRpbKcmTcL9HUnajdtqiZyCQYTVx4dSLn1rRx -zKqaa6afX7lBs8Az6rlaSIeI+d5bVkFdUawRjHUVyRbr+xD4fVPTt2YJTVXDoVRKk4wIknK5BzxM -V1VfUt+YW/UPzyHnrmn8rSpW0gEyyxRnGgnoKgsEk/+5qlGbWhZNPq+cVsafiyMY7456Nd7Io1sA -OisnQztiPWggZzW9yFWj6srpuTIrDn5TmcB6iFKQonK4PwNnEqV6qF1hLz0D59tvxZ5UqeyOF2BW -FYxk67/VxpLQQaqORKOunEATxlxZXhegy1uGG2qNiKCZQsm4bWyqvisHXzoQ9Nt8J41JrB69jFeU -Eoxskt2wFXH4fU6h2/VELqevCp9GHU3BckmjHumsyWOVqx1tMf+4ZO/oVQ1kBNC5pimAkv7qosWg -lnUzopkDCymoS+6GFrcfF+YCd6mZeVbCZIO54GcOrgvGSspqL3VW8YYEDqeNZ6iu1kl30Ky6GkCn -KdnNrDHTftFBUszb64UPQ9kU6qwR0Uwvwtt8Sa9qMs78IM/qaW2A3vURP40LyBwRKB17M8jk+aQo -PQOoAcuaZQTVQaOY6eUK60HtBTCERW1cg4oWS7cqyJQ1KQ+P+FUT6PQkeq0W3GbqvnOWbV5WoxA1 -VrEgC4dwC8qiVNFx3lRfqdshHbhFtNEom5mupWnoAvT2mGbLSw9qls8acsnzNRu+0Ii3tygkxcQ6 -KFaNTEPHKNpcr16L0O22PZkVQuqBosedqh6dn3QkYDLzpy8aOv5WnLlexlJtgiMk1cWndgWKrS0q -Ba1htG8HpKasIXBLLO6UEbXAb7NqWklrxsn34l9LrD7ybL0ZFuQ5XwrwFSUlLVWZKOehqdNNHtui -2HKJPET9fYWp5yZy7RnpYOpPkZ0egpv5Nlq1kFKNFohLBi0tIqrLrqjATo8ozhL769Pk0zihL6wh -vN9prZH6/zP2bi22JUly8PtA/4fzIpAGThH3y+NMfnqQSCEhaNFCCNFU9+gCWQ+jaQb9+2/7xcx9 -585zemhoqrxWxo4VKy4e7uZmII6+nLwjKWXoPCmFbBuYO4CTie1cN0bURUPkb9EzqAcQ3X3pVXcG -G0ohZ2k3nXK2gONlWJ78HZM9RFZRuC47yQSfanf2LlH+rJWssyVtGiCZGLz4yja5IYxF9dJOGtmT -FGmbpV7tI7neh+yGlYyuXvQlxgtkXspVlZogbl6QL792oZKJc9cz1fZc+Npip1YnyzY798I0uo1p -3WGObxwrF2MD9tLSGRgZVqLpR2lQ4ohPwhY24d8Owy6D9GODUdIySN4xnnatwQDYoLBdkdQa8EfL -mfbEc7BCu0TJ4C0fxNsQAxEnYWC9Q8f98Q4XifeT3QEnoFfzjmOfkoHMw5TOgncxbrgZOJby3LWB -BN5xBQGmfI0FXFN16peixfhI08M9q4xUtCwXIjPnQItMKj7feZI6uLHaivaTqQGfvuAmFGvuGJBo -p/LggJuiyr8or40/rAhOb2EREJJ4vmPPaPB0bdMAqsp1KXSn9s9RzTvmLvdDSD6BVU5XoLu1c1VU -cANbjcZ0o44l+4bwfQWVs+5R3mGJjvKEjGZ1R2AL2P2qYZrN6BR0Wqo3/1q9H87eYj5MHHCepykG -fvBT0rtRADzTghDf7ksqqc9HarGoMVsOjtcyWWKDo68Axfp1WR9qPNANx+Joh51jpRg1rBudwaIk -/SwdogXROE+6qbOwWQVT6Fk4idK+Bp6JeeUVVDcKoC4moVS522VaWzCxze2OR3g3E8XzztOnxoa6 -RJQgymG0ISiROO5kIVQKR0xUCXaqJYB/UBqwc+9JOMIK7SFf0HepcZotm5jScIH/6g73viQHL0x/ -aiVaTT1DEXIQixZGFfAwvMTPZf3uQ9uskjIqj0vIaWQBWCmp9QCWGCtKy2Pl29Vje4VY3FE8rLa3 -7Zx+yTF1Zmmg5ktOX4U1637F7aw4P1ZsZsbQ2sh64D8uy6/mc8uUOBfbNgRJDmlqSyOjtHqM0fCw -vUd/cJ/ccPEhBpxSjC5EeBh7Kq6DbKVyQR8no9lQLQeUsfos6C+4XeRKxM+ZEEFyGPiKOXHRaRZg -PU9MEc1CnjbNgoAuHIl9mPaVz9lRYgY2xfQWnmRgC+7ibM8ncHz6GN4IWObEeFFnnLwO1xyQgsya -PBQvfZEV42A5OZoLavCwDjvgbNKDiN9LFMFCaNouD+yBilDPBBeijLSibqRog98ClYMavky3KLuK -xVieU9wQu+Xsk5EMpbPQ5TDJIw+PicpLuEgdWThdLNnHgohM1NeXbrJ6anTXb1CgZWVAkryazybJ -IPgYDOQHo/DSQitgEEi6f4W5c9062QKqv25E0CYO9e0JJ7ZwNrfOHkEXclfKLBPwMu3uAaoOUaxk -VrlsJ/Y0ozPPqISQd2+COlT3gpZCWw7U2Dsu5QtBRtW86YhRdgsfbaf7ZDTQr0us29UGOpeQc4yr -8aLkds3ogbsAO0hNJUhpmqB7M9JfPE1mO2Kw05eF23DeYZgx2RFZLvMX7N95s19IuMiPeVWRPOo1 -sGKEifoI4cTo11zQUimt4aecNl7+nisFMlMuSxA+u5c5B2JHZtkZflgAfVVYmiFvlS5Pi2XOQdkl -A95AOsFvuwFxk98im2AR2HDHhoxAJDnrt+M28WmKD2yqiyp0xLDezOiurixiRAs3NES09DmCZwfR -eXkJPHxI8hMwnnIpiLVyvqUwMSxv4UEfyRD4VrRREq3aehYBEWPcUirvGNvj5P6w+xkLZNFis8Sr -kndEyqchkZOJYvKx1igPGIwyiTezNtBQyjI5kfFp3DME8GyXaRH461h8HtoWwciJU3j00KfscJ+1 -JnuCHQLF/JeCej8knZi4lelRVVF4iFl2CUSuk6QTfiawhVD10tvHO5J147C4XLb9sBcU+ofKlmYC -KUgBQGaNcvbDKGllXuLZw6ubU+WEzuWmu33IklQdNHqon8QW4NZuMk1FklNWxyJ3w6yDG0+oty6y -0QQCsAa3wWZRkBgN+SYtzJFSvV65KOPjl+e6kDvfQXb8k2JSlDIvUgjWRc8xMvrS24WDt0XMQnPT -3etZwXQmSWwnv5LDxBw5GfOK+uYb9GcykhZckL557a/IzbY+8DB5NELVurWVkr2eaFJlDsuCSAsV -wjPRqhfo67EwUheqQUe2pzKZjAdz2Dah1bDTgS2YPseIxa1xrLBNH3pZFJ+wgO5LZFl+zoyFRFXw -7xVYMOvrXrXpJPqxwb550AHuVAAOLggjwGAuv7jgqKH2Vfp2QbulgI/oM086JWEkSAKqL8fQF7Q7 -AEOddM/BX2iNyIG/vQCaoQTp20xwDzBEhTR2pdAFbkzfFTDiwTvZEciQKeaJo1UBEO+wO3uqvKHv -6a0kP7QV2Dxzh8Fkw050oIeVa00XZAeCXKkRl6wd4/VZihPpYABL2MiUnTYg0TE98NFSKlnsRpOr -Z1WBWHXfUIJSv9yNfpdchvf7WR+qbePHGA8cA1V4dV2W/+cwzApvAKnRKoIEzRkPcA== - - - 1ZbPfsmXFbq1F3CBHYTvrSCasoMPXT7ZOBwGUosJJGjA0xpOpSC/tuHTDVQufsXX5g932P27i+K2 -s64sYvClAatY384yFNO0Y69SSJvPEMox6aFgg3sWhiZVNokCLbfAFhnJ1tK4w6uSL2RR4p1oERpw -btIyT31tAGQBWtLAhiFXtSzkRZQZ9gM/yvg8bpSLDqJgxy6cM6qwN3qzfrlgTxp3fxfBNIl58MJd -zGBcHrZtN/x7h87qHu9QwE7vY5Oos3X63uupfHwg3bOd1JzvfLnBNmemFLhdwW2h4oU7z7qdEnXS -csygpO4k9oo+lwDgSkEqNdFQO6Ll2BdL168sUt2+ER7SoJ7/4kzxmlT1jtPtENXdFpKechmLCFVb -hrs1z8jLeNqiu71J/9wIKMNbsA+4uoUaQmOhlTrLDWXsJS7tgbuXV+64ooD0Y4I1WqfEAILycPe4 -+e/XWhOztbDmvnc3zgM+CK/DVycl9q8F7JfMYFeyEvKAASm36Vxb8rJWQ7cdzRkfwsNFk7V10i8D -issaqHivseGBtQhIyIAZdltvOA3cDpPybJTKHlahZi8RhGHN85Sx4nzqFXhgfIkB3gJQh8TK6OAk -ma5GIzBSK2lQo5NOdPP9fN+It2AxwHbcAmd6u1j1zZ1RWVvcBdMrexBeBrKUtGpd4GTPAKgO8E7s -yZN2IF25na+SDcSXUzT7Owa+eYhgkkNP5rrBCUVczxGCslR8Ss4UhtfVYlXTekg0TD8l9HEfrgMu -7Bk4jSXFlnQg/C7L2F3GdigbeDJk+TKQn3yydrMX6EnHdoFa1TvvAN3roHOpOUn2wSG5kJIlZyyC -1cBzu6qjtYC1vYFLlvcNFInY6YngwirGPrjHOV5508vbT4jwjcq/7Mts6l67RiGexGSP8sW2GR9b -duC/w+7iOnvlglTtMk4TxGIbuScTN0+7wMA9X2Xk0zn/blozh7mAGRwnB6XB28lH3mLUzSFyhdX3 -z8M54wAm4mIvwp7b5g487aDlaARzrIK3YzTCc4g7js5NO3pE/wQzAOENYul+yFR7wcmeqLNk4DzA -4rIU363QoCCYkjC8LSi1nrwirTWA+LEWzHy38hM/jSbl1aToYtBD4XktpR/k70MVo9RXbBxG2DEf -Rq9Ok5MrMIG9UE59E4HYvXpvu6CVP8dDPVFkSscYRnMMpRRnGK5NmaE2Ck9wQd+2qKOBifyQ3gpZ -4RGBSxbDNVQE4BprRnBC75Rytgoj3PLg+koVTopnXnTYC5QQfojhPYhhrQjz9MKom9+6aIcUeuix -yOcwJMJ2wKkZ4/jZhABo7Qiv746AldqRgjRg8buJPMmLpnJS83vq3mWuk53Y0q3FZ23hi212Xhxz -xY5jWvXZgm/nZaYS1kLFTqVLfy2yxEFzjizNDh0UZeGOd1mmrMOOoF0KoPZKP/ZazYF9OBBNHkp+ -vXKTewsdVa16/zwo6XN0cqJUF+MAAW2e1x1QRYnwNTTgtSASpQSH3mCo41jwhg242JHSr5Me3ZW1 -0V/vwgs9urXseJDtUhWsVzwbZ48OlNu/In82u8OqNRxg8WgphuxMRrrn3kcKJiZVxUcLkdPHQZ4I -roMAXeiaB0OuaZtZieSTH2SlqB9rLKOFnYB2xg6NmDiigcoCvT5vEl8xPluHEQLxYKoZZxzD1U1g -dl/pBtq9oNs2cc8K9yw/vDzO3jcPHPFvubQ2APri6yV6ao+IituNYalgDo9km7KDTz9Q1VXi510m -k67nxQT7uIuLyG/h835FSW72Em7hwPw4vHy4FrrU1Q64u88TVbNAfnCiHPX4RWWSgUpqVH2tuKAh -G9gmZ6Zs6wu1wV6jpaHXic8VHzxhMKTlCRraxpW5N2M95qJJrW/nfSQ4S3onAaxd4FjAi7TC4jVS -tgaGVJDHF+NG6CQprIhKAjO6WCRy92DHHIPchWPg+ETUCBMbqAxwcNp1ACvdHXBbByV/HtxGPuFF -lgE5SDccLoVt44jGHedEwYpWruJ6kApivaJHfs6pB2TvL3T6UlFlI2LAgcGhIVGwJDyhJLPJIzeD -OqeyJv0EmhnrKPNh2KIc1nmWq2P3HUZ1bsblecAVm9BhitZZwr1lqiHKpHZWIqnQ97N82Qnjxo6X -TvUlXcLCkEPwfF0/QN3JUvN4bT+oyd2+9bCB49vuBKKhXybtF4kK++Um9nRj7JR3lUsgunBBIrYn -U2Vdc+HbxzGRwhQu7WF3DTMCN6O7hNv2wR6SFJMHpcZ2YpCp5IceQRnRmOGalpXzFhrzfX4xTgTC -E/NkJzNO+WkXdTKU4LCZluik3YWCEFg0YySOvBJKKVEQz1G00BsawAL1dceORCIZaBB5SV+4k3Hm -wfJ20PC+RQuXe/CG2gIO7kh0fSnBYHYvHtSHeQmTMTXCLNwG3vG1Hv4hIo4upStcHg23TFBuCmWH -YUplJYQMUfq0i0kNacFlURaFbIfgWhEKW3EhkBYu4iekaIqUhhc32KS9FZ96pYP2okY0hbK6FFVj -kA/IKi6AztD+YAsuSJZGXn7OQQssaP2S6hhdW5+HgQJ96VIt/TJ2jr2ycK68xAFKpB4OQ2fY4XqA -XoyejD5PPFyF7nWIe8noBuU8qN0q4Q3nicatkjpfIiZzxzRBAnQztTIIqde0NblosMHvXBcrq/QW -JMRdHFlXUgVQCSxNLXntKeEr67nD29VwGtdz8zBHIIGEbaog37qh2SQEPoNREiZhRxyum1njEU76 -ZiBTeZbwjRKkVOyHmT0w7YwEhGmQ+xsJQJiongYYC3YU2ouxIeyPTYnsHoC28B1wVdlWd2nG46fw -9soPtVlljd44dyJNmnRHPKItjUaO3QFGwgbWsYzDWxXSL55TN5EWDpRH7lBIFSauS+fpDtCf7Y7s -h+LXveWVMpl6Wga9XIVqCBkOWbaj071SgIy3sB5ltUrs1xGa3B0ciWVD5EFjN/Yk7nE76zwJC2DF -PIMorFAZNuZDNmzD0WZPocKxkx87Sb6XcpMgTdyg7NhPyNOxUyq0ezpeFddwdbk4MzaDHu5fswXA -rxeznmJ0QPQyR9dHvMGJVG8o+tBweuprvvPLMXPgWTYl5EO0C9nFsRgdmZa+5KcHMCGq6UYg5Cbr -v+WnFnjpa9Shy8Pc8CHaSXJkcTD8fizkj36JHPlsGQuETup1XIwOkN5BazAkIgctqwRnHhu8v6pn -dRpbwBXqVrAgejmeHGOJ634TwDKZrgoJO3V7OPvjJVLOXqYpKf653nYSA0DkR2a/q09Nq8P3Fg6T -mDPoLi9jT8P4Wr8b36/fuEbW/BTJTo9dD5J4CCnlgk+HArBBvdvtQtFsIS4VgNtMJQJzxx/+5qwo -lpBmW6j2FkZYpuUvKO7p2o8ymE6mKAKLzOso7ZAZzyjuVSYgo9gvWgCQR9Q2jRRWhiKkOb1IdY/M -PSGSki5w42LZ9nASbIDq+uAd6Im9Z3bmQKf5EmYECmySGF9kLTn7Uo39ZK2TxvZdRJN6nXqFLmih -cbPObM8dPJx6LWaH+8ElHIHZx5NIwq7Meydv5/Hhyb1rDoa4J1Cpc6KiWw8RYvXm4Pbrtz7/yDMh -IzxSOj2hYP4vvvEkDH5b0P8NDSS4hUekxDhxQ0TR+xyo9VM3nMFa+UINXiJIrOcAE5IYHT6aRsE1 -LtP4EjAmhfrvsEPUzjNAtAd0jd1rRDNKI9C6P47gWU8v3cAWiYSRz2EHAMa1+LEEEpYgwP9pcS1u -4iKSS3QZkNay4qz2WYzBATxrAgYeZ/gS4/h8WXos8IjKRNWQ7hLEdlCnndzReg3DxtF4t4vgrrS7 -gInxWnoRUq64reOKIpzOBfd9yv/os67NOLOm5qAmmMpbxlwtoIWX7aTHHfP+gnAab5jHuHLs0VXg -xESUMS1w2cYJb0RmMiRfZSFh7yp2hh0CBWMTLvApgTkRwmGiHKACOuIC9jQflJn64B4Y/GfCN90j -w4b3QAYyMJr2xghbSaaEDTfCOkiqe8Fbos4UWKwDxRKgDDH7GkrsruRS1qsSG3WeXS1nCmeBOrkK -+2ULzoulxkVF3QNpq0Sd99zC3mlCuPSzAgoH5KdVHMUf7jGrceFrNx2/N6583gud1vBU9f7gRg+H -byvo4yqEG7Kz0LTrVUhSwJ0xFTeuyDWEv817IeSzJQiL0P9xKhJRfQf2NfhrZqd/tzE55gBrdKpf -kK3XynjltwMwMwfzoakDE3Rq+hUs5jcnD+InfdU5k+juYA5wTtQB54FdPABCrXiuVLRXVmxOi4j6 -1IuVy/6cdHzK5XBDKi6oumZkGg7Ko+TZ3nnpWWigdo5O4Jmm3JAA54AkwQxUwSEJ+PTru/3UXqkL -mErHHAJ7GNHEoH6bmzGnY6XMbCEyVApG8Bb8Zn3DCTtUt7s5CDNDJjOwZVMCqFjV3SH3U1J0TOim -Fi7RghFMWIWp/0MnYVW6uU+S2KsRwXJZd786UVyST7PYzhpE/t4szbR6qsLTo9Em2hqUPyiGKaZ9 -V1grmsat8pI5eg3eYe8vvPfLX/tt6rJwZA1QHUpthH/M9ZQh5R0tuiVz0oIMi9w8mxJ/Ypz8ECl0 -sTpVhK8x/4txEi3juCMzOk+izJDIZ60FJn35QJ5xX5KyQjTM18SaJvxocyESHmsysHjsbvKOhgEe -OASsyq9RHdp1sNYCmxFSt+xZbA7FgcprE8J6eHVbh9i9Y6gib2ETMnIYYViHafDLS780e1GE13tu -AQCsSyIHacEX/CWodUmafKIkJnJa8vDC9PXNd10iMw4rD9dJpdVpWS1ZsA1D5rmjhxG1MJJxH94q -PL2bKEjE7kxW+1A8cJE1QJPrl0avFzkpF7supSQvD69dEmj0eL3gLuCkl1YjOL8rMaoBUtgiO4P0 -NxCMW5ALMN7BjPaOUuVLCNHuCSgBoPQeLI+65iJ5CwP8rPJzjWF8uUluLGILHoqH6BexixwvYFL+ -5xwcoLFtXW3uLDtqbK5VSItxsQruWuJTjJsX7msUdt7yZvTZf9FbIAyk0xYVpysuc+quA1wC0IE6 -megXuDi3i/Naq0FtpfBN7IXgdBQj9zIPzWpAvznrQcA4AbRSc0iC6qs5A1QhumBvSgwUO34xCMVp -ExLQUBxoZ5ktGbK+KbEqdqdq1NqBDe4FX8VibKB06AGy1g12+atY/mYfhNhOtQSoGX1rODWDYndQ -mxcC/3De2495JEF+yZwiMQZIe4cuSTEf6x12pMcvaQ91IFiDfP0jRwnmzTFB9ZFA64Ao0CaskaLZ -W4VWh9uCf2lLhypGsvjheAriOkLo4cxzwg5icU3h6Ahf51TSMRbLgZgRBOsFxAmHVGfaGaYQpeGK -Oam6VTY6QolyUP4OEIUIuziLX2FQRrmCGuZIBHNPKEEUKxrxlj1Uai3bilGinOauA/J8wsG1uxOp -JHZfJUTtHVNKV8yhyrAMn/tbYnQlxpLB3uD1tMH0EqGz4LGJ0VlqTzAqlpzdVjJc/w== - - - Mb8KCqebs3EVYxPwHrjEQVVtH/45JF4rcb1KfFk/j8FGVacsi3Czxe4cmAVRKGHyc75Ml6A0o3rc -sYK9gdAtcuUyMV4S+Bbyrd+CSjZhxCl0km/ImVewrYjRy3tkYg07Xk+QSntYxfsQ1D4eszSjpzrF -6CUcYuQwpFzeuUnlEIm/c0nEVSlxeC4pSlsuO3nYQQLl5TzvsGPge45iQepUt3U+Cv4yyNLJr1k5 -gcgeOee2DjnotdKVSbRaKGMJMlB5eJHjyx1JJTItYIsKqMTZid8NmQwlW6baXRhD+izdh5W9sHkL -ejV5Dzsb8VrswxPyDKa7VDplgP5MlsqvWK0zRg2LZYDRRPWt7HIDtS39rZS1AGuTjZsBF0Vsa4II -DpkMmQMVfFyp/OEoFRbo7xy4IjxMlBPEZUzJmcieV2OPdPi6CmwVbLKe0dSvaUAoFePDrEmUKnqm -4QMhVCWrtINZEDk/JQU5+VP6WXPJD+p5Vz8/LiQpgZnQQwXcbElHEIXo/tX85w4AnafzUwKe7Ea6 -vnqhIBHh4OG4OaNADCMnpuvejET8ZP4W384TPeofYu6hjnzLVQyLQpUsPvudSs5m0ync3NNJvSdG -9yiGJbK9hYbrstotlbyFv4sshB7C3TVP/vQt8qdHGPnRbJKSXXZRld8yp1NmTtAAyM8tKhD6AbAr -jtw0ScRIFdYTkk5yYZhYFPrh3mlvUODzg2EXkmg1itHsIPJsOQ38uJ4s7jFQ2t2Ej8i0d29ghUJN -yxAEbQGab9tTWEudW6x4v6pe1DCpJC+jp9JwCNp5puBhnO4TU3JxXdDLHOcRYgO1gxkT0RdpoIBv -84QRLMgj1yZLy77kh9UwveP6GDst+PAXgz0qcWuu9gqnvKeyibUpedUNymXG6NpwakB5krtO4lRf -y/w064JDjNZCUl9Hxy/GC7hO1UfOQQqwNHaWqonRRZ464WESdXDqft9mf0WUolvYWoU5fSynXSWf -xybk5br5ShHsuVhBdj6qqU28MENTUOzodlLw7/uKg+kyBNWRp5fJ52/BmJecO55zXo3Sp0/sj6sZ -8tPOc7lpvsOO3Xpy/lWKKQ7jqjaj0pgeE7Nh2Fcedo9iMHRBfJvaLlqNmU7GWTFjtcQWtSrqvpQu -0xtoFomxVltLAUHwunam9+TXOEm89u1hc+yxLswITlVesYQV1FzUVRJBLkL/K/Rp7WUZ1ATP5GB0 -+AL6Iq/gCicSVzXSCP06EXw/ZLUcLJeW6PClN+H+/Ny8q3QD0UaAuuBgUXf2HXaQLnYW8kvou0DQ -R/1OM84Oatmc9F7UnOsshJ0E2SdxzrnIXeoyqWwBTMztFyZ1ryW/9HC0IZ8zs8LG5iJ5ARKEohxz -DuCRVe2oItMLeahu5aPMTYAjfUR3JwkfhxUi2JOHgtM7Jf4dDSoLWy7h72hXXQ6zj5wUnq6G0gPu -0KlE3Sl4KJkcP23GL6GzIcliix4qYbI/25E7ldljfoo02kEBm9a7PFuwXHFuz2A3Bw2z2KD7MzLZ -naalKz2zA8TG2DjglyuqSkp3w9HRaGukpXlcAXz4MMYm7UTOaowFeFMDMdd7ZKWLKyJ0UpDJT5XJ -ozVyUjUpDPPhgiLadIzO0K7udjIyq3cqJYF9YRbLqrgvgUYhjPLk5cuzHd6/FuEy3ThPOO9Mrk8K -H09nR5c8seX6pOUEZynA3OkS2kgthuvAz/6VcPg/4IoFgYhuRdYftMe3cwSXGF3kLL7dIT+ODtxK -lzdPY+W7AbNjdhH5sge/aufqt/9P2xHBQHeC/QT6gN3BJhKD8HizYPlXZwjDlnQ/oNs4NYdOxV5B -UovawiECXXs8b7oja3M/Kxwi4CGKMBCQHJNs1dCYrcj4Pl89BB3uzPfdXuKdL10xn0GGqFBlaJUT -7loZSKyZdE265pedalAkfzlXiq+8K8lvOXW4nI+p0uHTuL9j0vSTLnP1cfX/gHnvkIFmeUb43ch+ -9aC/7U81R4cCOJ3Y0n6ozNMpciyFHAtuaLACaeEI7n2gYNZnMcOgi/7yDrYkCmbdQQZJjixxCfiO -0yDHem85IShJNWiHpndyM6nOdK5h+dTy+9N8HwokRuxHvodrli9KQzbONaHMWLBBjnPxst+sC29o -IK4qnlVT23UXnzLAi1teM++NDUDUpzG7It3l2Y5Kptd34IYzFq+pzt/N14Pme2NAfLhmkhk9DTEm -BYdaLmKXh3mB0I2EGNmyq7/O9dpp6Qa/wvGJ8tq36Han2tyw4s4P2LEJj1BJZQ2W3pGA5Yf0Vv9U -kRJS9Cxf6Ugtyex2jUpV6KXLFWRfYucKo4xtRw2tdMEzHGKkL1h4b5Ku+QYQS1QqZfp+eYfH7pEX -OBuAMkn4Z1K8wIMT4vaNUdtu0C420C+cIK8DGY36dV6+5u864fQFY7l+BsNMmddmJgd+qYnQ/Oev -+Lzqy6HT1CwF9AF7BBVAhHtMOsHmOCjuD8UV25MEQcRCm1Xx+a8xMIITrGzS0HuQwFsgyeppQfq5 -KN0W/kJZlDXsFif2FiYd5p5pAYQhtOL65K5q0jXqLE8XowfBW3aupWsVD+OCLOJMJHt3LOjr8L49 -bX21MuVS7cr0ATvuBc2S3u+ww1dvFhQ2I27QTsT53fTIXGqh2i5hXRdBMR+rSj5w0QIzCzY5ESNr -SNnsoHOuEahsmbWgRqyyhdTCpeZg+wVErCQjU+GCIOi+6boPkrIikZ7qrQLzIUaXgm92H/wVLeCK -18jKIF3gDQS8B7VQvf7x/RPj9qUWVw+O10ut5x407hfstboDsZRUBrJB9CKlKOUXqUQBtnMxVsRe -sMYuE6KSQgj5hQiaDXpu+vBFxsLJl2sBL41K38zUs4VgMoh900AMQ7GYsR3o546nmbNcL90LaX06 -bex58A5Eco5COfo3b5i9EJFOP1fBYS23MXex9cnlT5YIv2vLPFM8TSSLhYcPZKKq5OP6F1+ooirv -NFYG14rqAGnWi3prQ1mVforgYWxU4uoGESQpLV6kMbRaSXOuwVnnQCXlkzwZdzcRWYyd0H35OlL4 -EVWAlbVdsgGFViWLq5I/Kl3YOC29HEhtvN1HMLp20ENp5G6jB5D7DS0Y4cU9WEM3DjbtLrJEWmr+ -Dnvn9X56KYKQ6BoCVTWO7c5fJ2U3Zg4mC7+rXzOmQQDNiAj+DI7iSdXY8YkhlvOMPON1Jv3ndRkm -rJNh1KGHRDy+4DqAg1m0P7mSwa66Ut6vEimgLLX4RFxFpMaTCbEx/e7CNs2Tp4aPUMn/UDtnQ7Ug -7H/Bwwi/UMBEjE4coC00rLdP5xD9wCRvXWzj+KCdjSudtTMlXZQ4n0rKhXYoI12CgyvymsVSkvaO -jbVJcg8DV81mwLHwxcXoqcpqhRtswUl05SoIYd1NzsxrdcffnbUM2fMUTGobECrp2+2gFHRNB4AI -zOgsV3LDjDI+4cjznbEY2d13p2qD9ovfSIRHceJ11Vn0BrrJ1vjLDTwMvZzCTHvrqBo4JSsoNY04 -2KOjgA7SEWc6MpN/PwDZqQENE/7LgakynHa4NTC/awbpoFtxl2lB7Ns6Jd69aJ0sk/PgFjYctKDd -WAymNvS3Q8/oJK7CF/uLUS7T/+Vf+nA2lvnVz1XHUzRWPOlLI/iLDb+FeKPnzLwFfSX/OUS/xLhx -78RVUo3HjamaVuhBfQ1VHlItBOGrRdLEWEGFcjye6C1E+tu/MwmAw1csLIZtlVmnmndM5bkFqE1r -6cLesA0hutwKRdIq3adWKBlZcz5LGIpXpXvrx7vUcHDKegmFKFd3uLGJOqCeFBzStBDZl6E3RyUm -Jeb2dFJlAi7JS9cnBeQFVQDxfENvrkY0r1kpsRuNEES/9gF5Oq4d7ZeoAahUFhC/tTRQtTtNDm4e -ZsTZXBJj7wbtzHE9Un9flyyNRHo9jN0Lg+2NBhDqb1TXEeNF8GV5rbpqfaNXCYclX6NAB+zItdS/ -G6MvZJWXGCZuTiql5C1cYuU6uWuVCJvXRc/KVYo16VmZaIfhBEbgRCmzsZRQbB6MzPLBWAX3mO0Q -im9P/IiV518kZFthui/SpsJ4XXAN1QTSr1xerlA5ACRsLJ3R3KLvw5UrfFiSll1LObSBNQ7N1EGC -fzFWgB9GoISVxhrRBgA9m9//DkMDtp9gjo6c2WgkUNEkXKXYfUCbxkCz0ITrBm2LPkxkoOcpeIsI -VJLSuVESzBKZ7EK6Lsk8D/ZmBu711kC7E++dwducvMdCQhTEiGLsEHzMvOaN8qTDOJbYshdURQI4 -eGdVmND3ucHYaU9ztc2Uu0F8V2iQ3SUfdjLZk07NcEZmtxOeXL+ehy8sRLue4x+UkhYO14YEmI4T -W7hGEHacI8t8mUZ5XwRBxOg3ppkr9NtiZlhKyFui0ETMbZGnXlyna65TcNa0ZZQZatQY0K90nTxG -IcBw+F4uaHFWLMMF9g0IM/PloKO46GZN81asAS/qVm5iiCInVGab/LVpIWAf9AoxYXqVk3GulQN8 -bRLQuJiN0J+zTSrRGy/CRZ+Undvi5WgZfP2ddh/KzbK9thgwWmTSanIKNZcLTwQGQnLaIXONUtu2 -khG1XWKk0nENZRdx3V3ib1m4h98+RJCx1W3u4pOoSJkkHULDqbCkbYodTitEdOPFw5BQFg5Yh3fM -rF6hLQCFwL3uJCNcy2McU7bgJIrsDRzuM4PKLvJiVr2k6sO8wDRqwqYIt9gZQSENtvgVxSM4YGle -zLKZTHF8+YNH8+guAz0eYpl8JP3FeNtaRN6OJ47ybZniQ8gd23WaIM0Z35hRcCtAdNYosiW+Rtnp -jZFRa0w4CWcug2QH/MaRmuyZJiiIcxGoIx1qj1SWI1UeRmiAN/Kp9FDqrlZv+IYWAGuplIYQ6lLK -eGIDfb3qxi24U+G+WTTEb8GSMgU+WFMtQUrHFJ6OAu2RrQbcVdj1NhL8yH0K8yzhLelKJTR4jD3P -VYLLLyLgB0Shi/rGmUezJ+Vk+Ht9oJIuheGEEZFeSkq1KlMiYCunkfIS8XpCLYQt0t3QkSWSu5Bi -Ap3ii6GTsEXdJ290G2fUMZhQsOouKIkgC+Xjuyl0G4wgfTO6MYI08xBoNyw/GRnZDYwZCuKF4I/L -H9Cmvq2wyV94pSw7IkouIPwOe/geqFPpZAxPiS8xctJr8P5XvN1hNml5XXDfnMqdV5t+UvAy3bBk -KCpD3JSCVXtHHNe13NXol6ZG9QExXuBY9dBmywCexzWmbyowNzLdyDfawPrnSbEZK+2xJndyPAto -KhdxYj0XnfWF6uLTqeb51AI5QFkepH5gSy3cgpQjpGyE87ERne1xNGFY7cSoh6esrJGAyiCtKy3s -SlR+RbOYw80u4GxhbOw1SNH0CVUnDTb4q83Ugt5avYXJS3ej7kTP6GzEXIQT88SdJeiTZ0qR6T5k -yx7Yar/gYI8gxjeStMI8vPkwBo1FxHpO8NUiCZ2KIeQteAdGFbzsU44ziytdHwwZNw== - - - Y5hkH7pj7dP0G+nTY0LJnofrsi5ebpUoOYv0UXcE4bHLfUerrjV0Wi6bU6LXhjxcyFUq1ytiNJxo -HcRQz0bk/FzImi0jLF+D47SnGh+n1RT218OYyBM9dSACkLMTCtmDjCWJ219Ov+cs8hgskN3mmHzQ -Hrzs7rCoMQjvwkgxs/QB9GGw2/fe2UBlqxPEdsHFkIoGX7tm4JO//b38Z5WlNoEKiVmx52J3fWNx -GqBhva2cy4xGp6jS2BMljIzqqHkg1ubFCSrxXVhvZiP+RRfgdWgjjIQLwzs7B+e5GkUz+tE9bjUs -Cyw9dlEiMbJYUF/PgcXVjrv3r3/wnV1hgbkcl3L/+oAd836AGViMjaEFxZ+JcSXYc+jwin17yerE -xVdaQLJn5LdBPcJMNLvSwp24H3i0TZudcGiU3M6MqO0YzMeb3QVypW9WziI/N4koOZvGT+PwloYI -la/TSOo4RAAJTztl32l3kO+0EBvtuDo6PPj9B+0Tn1Z42wQJ1AftG7SrcDcFLlHAU30dryfCrw1K -LanYqCwK/25WtgouIqT5GuRYP3WBzrPUJk0IT+joOeQydB9MS9RL1QqQbkrNZNzYUuZcwTa7UWjz -0jB/s04K2V4D0ziyQuIooA/ZTptRR+I1AVuKZGMn2Eey8N+wUL2+POpLq4hYQf66eHSrhvLREyn/ -a9/eU7cdzgmWL3Y7JDag+a46lmCQrB6UkyRnh5KF6n3wR0sjW+RlrhXSLTundkHuvC3h9faDvkW3 -qxGKHY0oBGRV8Cq+jS7LuRHH4uLdUoDLy95rM/ELh+w322IAH7D7zX678xdyiw3s5onp/rWd/BOg -9Fy2Tv/qT/zI/qmdPE7O7Xl8MXOcvKj7OL0NBwT3atVoDfOnZuIXBjePYYPJCYQo6bB7BvPycNsH -K5Yq1R4Uoo8c9aeGudYk8D1BDapVEB8I+YL+fFkNPkPqjuLQWUd0k7RTyRwddLyv7cfrbpY5LNOM -+oAdhQMODjUj8GChDSbqpZ7NeiKs/1ELLpF2gsv/tQ8xMuKEEZQolSQcGRSBRoVJqxzhTgFbMR5c -zNSNYzjddZg1LYH8wdkoxgKr4msX/iW9WyxvSL1zBtkTlY6vyZEI9hfmtAazE2sDZcPo8k+618E6 -phGQiSXfOmiBj1dZvMfzgPthh1NRRIRRHEQsieaLGhaN4DHLiw/ptM5uHPxUhRlzPy01DtZzxv0C -sM737qzWahE7fMlVRwtMCSNIoD/HbKBX/rQA4jo3GlsAwj5KsgVkMDC3ChTwXgY4oz4WCek1H0fU -RwneWMdfi44QmQgRkmoX4GSlGc3yRODgoJhFuxSFnqGldummPMk1POzboqt7UjpDFeGgJOGQztd3 -iKn10vYH7GDgmizgaaF0SoEJlaUD+20LhRl59oBHlcmPi0Bo4tX98vW4LoEk21ZLFbuGf/H9JFpW -GbrftgTef9BOFDTURDSZlv7D7tHFPaOiIahvJ9w9ibZS7yW+bq9kRhsR42IdpFSIO/brtQe/pq/T -K6JGei/n13E5OMXzQ8gvkLJ9UZkNdRktQxjl4U2Qqbtu+s0vsU+cvZ+6EEujZZ9a4kIMBXsUN2vN -NGN+sSPtFuiHbcvDP6sE9qz2WsDt3xBJTvSar314wmsVFAvIquTCjf1GLtJEax1uIkn976UVzhwZ -QwdibdtoPmAHwJTM8qLHOHCGAgYhDThoy/Mcb9GV4deejbzWIX43af5eFo0tC5C9fVq9cpRHvmEY -tmWHivTLO+Sd4VPbnHugoFkkjZL3Y+qOWfHL5MQy9Wj2ztXSDlSitNEGU6O45evb/QMiYoOHfsbi -zVRDDUpnMTYAR300fhLuQ6FVlNSL8SCxfn21vHbhLe0peHG/o3BPQQDW3S6q82F7WoYjspk9HXK/ -rHTBjV5Wv7MwTG8sIqaqfad69NmsI5Wf8qjWfsoNvXT5qeSrge5WEyKshwoBS1wg/HlLUor6TaTD -Ve4Hm6erM14KNkwWMbz+XgxsMNUNe+jj5z8pRHmFQjwT2iWhwkPRwpeW40cd/3EspTT4/sPzjT0U -jXryO0FQI+FJEtHMYCaVGCcZM4ZzLLzGX7/sATs3glBvWQ78A/ZuSeHtWfd3Pr9B75uQb6/t8PtL -NQ61ftIJqZVKHLK41IxOfbuZqTRVSwQqAhpTfP9B++/p7ZwD/zivLAtAgS08yK6L9MvEs6NSkcbC -GWcnul4ZnFb90Z7UToQHuPriAI4xFU6K/Ah1ZsDlsg0Q+vaDDvNdZtA3Lit8/IA9RIyQqJubCqXT -SjHcSCW8FmhusVPwg8XsB5DVJLYmtAKGltELOZ01YaitFNe6TNImUt3FTIk2griKD76SGEACPclY -z015620ypmZMjOMzEQiAsV5z5G/ogsNFt2eJzOiVc9vhLtasp3XAxs4+lFCFdt6puSh3sCxb/t3Y -hidk7lPoTxgPqGAASgphLHDRkcnkh1Dek8r8md3es43qgXd/NjSUWrQKAaNp0XI2sOg5I8I1g1py -wtMQwgQqj9e+EpECdB9mFqOfBP6ogFll1ywqpUu7gbp5F2yySfRWRqdCnBB54rmA7t+DNyGZTj5R -hyljswWn3dFjoGO2u0ugSmUT6yL2CnW737CIIJduI/HONcerl5QehJmKHcX1UydrjfUE8CUnesx4 -DY1g8fdGQecAQZ75tCEX/wHmUboQB67wHlP+USGy77Qf6DQDl74KfI3twvJuXLhsJkaqVahi4GTb -bqT0CK4zasRs1byit7CQWvQrDQl7H9PUu9w96ykkOwX3JEf7JSphD8yyXRCWLqJV1mQMbFJ1VNh4 -GpZnqroXQh5KDUyP/q7JMO2iF7EWtVaWnUfsA+6li2p5a1Jfc1lm0Y2+GzwpVawJSgWJK18y/TgE -Ql7NcRFrJB2PQbygmBflGbzwRJiendbdYdBmxJa2rciQLThTwXYvjlRBEPleFBgUaugahPwbBEKl -Yau7USm+WL8A+lNyBa0D6nhXtVihLByyWKsazYR1OSpRxN6goq55RM53ROkXywGWM0NaI62Da9yL -2FU1Kb5HQWXxdll7X+WXpPzbiDjM6OBHFW+qWLg4exRD9oa/xyqILzJvkpoi88alMIYm5mKT8DuG -Rt5JxCibWIdyE5aB7GCr4HD3+92rz0DH7/W8/OBeSiFAxNjktKMKFYplHsbQETw7bcZ9Uc3Q4Qra -asu/9qMj+x+wyqDAYJGij7+ysSxuAc35YYXdm5q3oCF5aTfGY1Am3c+pD9hxlE2W7gh3T6Ur2gYU -D5xTUYM73DFEsuBWnpxZm6AYdGtPZt+FWIjaIDP8Cq+AU+0pnukvXQ5nMYRvhhYofNDsXn2ouT6M -g44IEEs/cQN++DKXkqVezSf9dt9gsHDqpWtvqdO+r0n2+TBUKh10MrlpHmy4Hqyj1Z3//QftxLhs -yJQeF336oAvsRAhOze1+JpBqTiEtVFkN8LUVMF5xNiaqijecWmq3SY78oIFPHXhLfUNIwffZ6Nu1 -sNrJm7XYFy4xLXjxX9vh6+9iE/MQHPJBO/NIoKwXhkMqMpJSmuXsYkxE3oyKqX2Awr3uwhYuWvjU -h7zq5x082BaX/UYoQw/HDm690C50QIt5GBD0jYqHFTpxflJz44DSxPwl6GvXRjJGzDIwtIOneTGF -vzaIW8Xomd+1Kb62jATWO3JS5hfkoqIisKh76ukWkRxgzD3xpgrZ/oanqkuUNIz34lWSzN/yLLrZ -vWBe2CDpcjs6SmwULkMo77uRhlbcHBT/67SaHYzsGiEwRMTjYcizLOP2M2PKFDcuGiENpSZVActp -SVpZo9od+mF07JeehEE7WqiJEJc7acFu53uRxWE7ibv91mJtgMzbA4dQUXBBGkoJci3loB1CedMw -Fz72FIMH+/U6STFTHXR+QHxtkTo011++0oWPhTIoXSmYWakCchfUhexFgOhiSGNv5ku+XGveYdeM -3sxLrk31vE0lw9clmcEoi3d21Rr4oH1C9LIGXaTYqTKEm4kYrbJCG6lAo3xqOf8o7hA755vEcdqY -IXNmSnvHRyuw5ebO/KCTIIVPk+dQn2Kx1kkALo2aqgHffe1hbL8LeEeZEnLesPODusW+lSnDPlFA -DmfemxT0OyG/9O0d07Op1KgREwjEQthPuYmh6pRyBntTbm1TVEYeWRCbQiWLurhYoC1AkK8vF+/d -QYMqi0yKFj5gR5TK71LcWpwDJeE+vmwn/8SnpvgTWJrP37+DHmovkKKKzbOvywQBzAiRmKcr1Jd9 -976MhL5Q6ZMP2L3APcXZVOsSD3cwJ09QC4gxbn6vLecfhdLPys6T2OnPa/Lw/cdNmR0XtUWejT2o -BLrIBPr6i2/pa3wadXbGK4L2CqGXkU4oXDrlFwdO8rhc/PBtvvrM/4DrHUrxVHgXPkUB5Ovs7FSK -vaIoTKHmtDfH9W/Dab9/3Xz8cIOsoEC2wp8T2lzHyfhR5Ebnx4rNUS7NA+gkDaq/sQWXdgj569Ug -M3PiCHntQzhbwzKjj//kjM8fsANZfpnaFQ5h02A6l4yMEofwEoibCHLE7GWOh/T/qzO7HMBAiSxY -JkUI/yPiKvaFFpTM11vwWqyDFNWiRt05fD10d7o9SVyBVewwvCZPlu3OdU9kzC0lNnHPF6NX8YT2 -8w8/UidtSezJMjamd3PS0f1YihfVhUmudekV9VrfUI8i0SOn2r0akoRNL9KiQNFKkg/zl7iuzuvf -3SbPwzjAuvwyGWKekCr6FrsHffD9mvqLt5AGT7XF2sTDt3PYjhsT7/Bi7l26xwY8ESANxKf/1IXo -3UYbWgHcse2Jo02QFY5skcoiiTPCGOskXvMadb1rk6ag53oDcZM2qXgd9CvdICWklKd++7Jvudss -X9o7rjk/+kUUnXcWu66dqtpQ4fCp2adRohqAJoD5i4GJm3GhwdQPHtgv38XtCyy5IDFUI+iXZgir -ferDW/KLajjbwl70QTtzDkBrybHkofPFWPZeKDDfrmxMp8SJoPKBs5gxi0vYax/YPcHIU95Sb7kf -sIfvmvirNEqBCgqUN0soYgHpDXGZl4bjN0+iEO+cIidISgclu87JhM9eySPGTYB+MHE+P+xMKdos -WdU8yPXShV9z557b9t5du8ZZFMTvICobA4Jz3HdO0L45gdkbWlgRX6nEM5+bWR7qSJo02HyFRmtC -agYcOlFU+eV4PF7n97/7m3/1d//u/I9/+9uf/v4f//J//9d/+uM//dOf//E3t/79n//n//7t2f6v -f//bb3/8+POfvqn528P+rf2b35VvfydVPH/459/9zV/kH+q3ov/7w/+Tf/v3j3/6Pw/bP38b3/7D -t//238u3Pz3+4g//Wd9Bewp48ZI3sosqXj6ZBF0oeKf3+MMvTPaHv8kP/8fH/0mQ7bHLC+ncGY8Z -ruxz8zFLRNCoCuzBu/pH/NoppLKDr66dmKiP9nKmdVz2zb6txUFzd8cvYNpf+gUx/g== - - - KG85IYExLC72VQewKNZtSPzKEpuHpTONWm059iH2h1+KOg8Jlb7/oJ2ozpGqBpD+6Qt/wN7nBTTZ -4lvrCha0ItBo+5wYey/ewmMu+mEs9scZ5kwTGrl6h73s5Y30qF0RSleYfVFLLx5Xew9THguofdHl -GLBJysXH2VYW30aQAh317xbkl9KdslEh3A3hK09CxG4khJ3aJz4SC5PESN0Dc2LVuMGvSsEvMa+G -ncwpadRIDL1VFH7xDvn1npvm2/WObUgBU8+v3BH6/knfns0xPSQDDRoRPTo+YAcN4DD/7B32MUCf -zsqnRaxU99JCbbjBq1BR+Dc0cCfOXX5xzcyAlgDj9NK1GKeNHKssM5FkY69j2w+4qxWYoX2vvNWe -FPLnFv+4Ly2/paEah/UQe62//qOPTZpbvH81MXZfBU7dpm+6UXYf7D76cAE5z+kc7EVKmsWh+tS1 -3OvmP2gxe3b6cSnEOTNKw8zkkeLlVfrkRbKClyKdUgfHrmPJdbpvan9YtPS1B29pb3r8Vwc56knK -vQml+i7v9w57G2BO0ZzLe+xloDhxF0CMu4LApS7YPv1g9OUkUJre7FhRuSuG9rFYUVE5Sdrq1Opq -XJg6s1ZWEB6k7o8rb7CiEqR2A0kA7QbJghyW8EXfcrc//Si7jTqgYf47f/Sx69c0v91Ihoe17/7h -68Tym9DJmXfumFTDQvk6uTffVHPyKGcs0ZmXdqJssSQa03OjYlLYwMBeMMkaWETpigIduyf7p2by -Lzw8cAyqhLE+YN+kh4VWcalUWhXqKgteSgsHsjrqs5jP93j44fuipN+C50VoEoN/ALYoQDmBp5Bn -W2iSGQBEGq2QnYPi1us70N8ulf/JJ9IH7GeQAcaDNaUmjgeoW/2ke48Bxpg6FTIzH3qjY5c/dSFm -kJR1BLXq7vF55+Beij1ZsND02+GqiKcFnpC2ol73AoSrLVfuEcLtNrHbX19toumKRpbdpr7oW+72 -mth71VH8+Gs/+jjq0ZTBX8V4aweXm2UI1Miq5rW20XrYs2BOcYYkbbWRDC5a/dS13Os4HTRcwV6D -PImylvr2JU6z/eMhwcOgHXK+GG/Wu6dZri/7kHcTCH/OKEuQ/QfIUSERuw07oUvlTWDtxdgKjoVI -hVnd+/XtxQFz+vC9oJFaO8r3cxd+TWPXK/LouknH/toHWapqjf0Vc35Gcb9IeOM3HRCoT5blc0bv -gm8/+MX3/CHvALHVOulDvv6JPz8uKJBvXCNe2nlP20a78Ps0gMJtA7dV5UTx/YEiiyAL0o0ARFI7 -FJR0M+HFaMamWAq4fi73onoujTu1UCeE2Pbq6EPhAla//st3eEubPoRchCpi9dj0p38hWX7LN9fZ -+vPaU2PHYb4i3VSU+dS3l7Q9Y5xl2HzXf+lC9K5nva3IBRZl4kbjx5I7pT9dNX34u2SNQSUWlCti -35CLyw9vUNLv61oFyj5yXz/gS9/ynKkT5O3K7MJBvRe3SQAJSiFBj2eP3MgXVEA2R/U2+O1a+e/f -nPTbUET9yazxWjr9uYP5USAL6Bxsr+8QX0Uqylr23z5od0KgCF2VlpRZwdzfTJvUbMFFKPYbQSpX -QRDuGvDRIe392oU8o+cJbbOWBj/8czD5iXHTFXdsoRhXcIj2GPtVsbwV6wZjQa34XTB+6sK/pHdr -Q9DEeRzF8eq47yHC/ZOZ8dj+sbSwJKR3VBFwhpqfrdg54BAYj4sNEB8ubf619xOJQ2Q6UuCwDNIY -Sza+Y71B9kZgOhvLGIVejhfgekNV3gIPWlE5UZxdjvL/2ZK/oEQBCu7JWB3sW0KpeWYyiuLMbmpf -vukLoLb4bfBWNIpCk2VEfPx7UKMt4/XyRp2lKcg2i2TYlp+aCXogD18UxnnFVxF8OWzO3iLGU6cX -quiJ9vaD7/BOuydOAlxS5LRAHhO1Ga9fOG/XhZNFkaXcGG7DLfiQS1Eev6CP2579Kk7r5xPOckby -nSeMqXhEvh4n/fAqhKKSzDi6HMj+k128MPp1GmkjXl/lPW1/raOpE/wxjURi04rYzQiG7EnEjW5e -5HKt1Kh62MsApSyAuUX4vBquhJOn/K3hrxD8XqQq9vgEHXHr2GVhijs1dGlk9pL3I1lgUXgXPlW5 -vLdMPKwF5T93bMC+lg5+qZiNvcXyPD87BgpSBE7QqDaKxjpdnYzNpbrsvSsaqAwyRrjk2b6c3lKN -2COXc6Z/MlJNVuwLAagVfauz0pXny9WzeX8p6RPFubzW5K/h4Fxr84X5S4skXWpnXgUuVEuLZp+K -aZbnaU3TbIzBQ6FxfBcuNNCb+UkfHncw9HfseIkIKXKa/XAc4+E86LN/8Xm+/sZf7ilCaof3oC+k -hbJczO18MTw56CwhIdTVav0CrzhwACRmy0hUXbBZ/agZQcCsneD9y5nb5OHhV0nBx3XQER9GvT51 -IfdO4zZ2aFIHS+1+ELFsRYN1F3wrs8SVMSrN143eIVj11ADSdSvurj9+v1knyrFtGur90qU4lwd6 -LFxY/B0oFaftdtR4K/NF3CEbz1Pbf+TOFvfWjbgIwqHT9Gl4g/TMr9yH98HfO5BVY6eMY34a3GfP -bXrMTl+aF0HoLLlsICNt51S8jBz2YXe1gGXoD9qnk9ItpM/UgWvoJHTuXnvydGE6CJwrXv2LC1MJ -TLF4JNy6Eg7vtZ335OEtqlEvZpzU7twQghoitkE8kYFTXsMW7z9oJ4p6BW1ra/taUZrXEwf9xLWs -xTvsj3F3OwSTxOiJCynTcQHXl5Y5dGMz0HytWvODdo90XgPJv8MO3+NadN6Nfqxe4iJeW+aPqnDN -JBV+IX1HSFVLqqeCIh+XHvF7DmwL22AVvOsbGng4AviABzF2a/iz+HobFMZ1ytAvu8ZedwU9uHPZ -BtEcXRnu4ERCQXYbWYZ5oqhXF+J7OPlRZSzmAgge+NqFJdadv+BzU1pbLIw7E9fx3d0rI8pYINFF -VD3KtF7fIb8e8rvXYFd8PSRzBLRG9QRh7nXn5rBaWZh7z3Bol1Jpfdkyf1SKhTdmzmUyTWtnz3Jo -1cjVOM4QqWar6FkQBr1eZP5lu/zJ2kix+RRdEeGsXlDV4vibKoxOm4ElqrM9PF08WUImpWV5TTs1 -9OeomECJkpc+vKVdIPipIwEl9rieo5hiHN7DJ0kFxVgQHK0hSyctkyQfMjli7FR5qIfG5z7E6PWU -LZ1lksdP4cQMKbiQiqSBGXzwGmpRkLuFOyAL2mvwcA1uIrWnMIhmNH82/pqTYTru4ucWPc/mtCyv -bxEApUr5NUkVbKp/V8qYOtGLGzcwpRoccKPzhl/DRr2xBd/ILoIdRrraYWOjzz14Qk952msabJfw -KeHkc3u1mSdw0hkx6Arj7BBF0OpBwqewmiMiIC0c3qEsIfhFH4ifKsKAgFj2qDyGt7BcImzlSCl5 -lmgYvap/1cDb1+2+p1987Ovu4ygjHn8SwUHZSdzTftIdQVwodJnFASHCTuzrugPXENbW7RXJ5O35 -v5c+cECEanMt8GxE8aXYN4g2UMNZJ4ltdszUyK+78Y0tdLbggS9j9sTDu5Pu87kPb2n04oaghRbx -wTqXh/MAFCGGB1BqIpC0E7hBM/0cvT0HHz54OJYiuA9e+5C7twr1AgoSVvonjPxCeEKMBfEPxf2b -8fG5sH2QD1jtA1fivs/lwwT0efLzpQvRu8PzdeRqiKLI0U+YQ5nBm/jGyan3o8E7jUyc7mOJMUEZ -OaN/tF42guvD2Z6kXwOXUnBbvr4E566gdj1G6McBsbVOZCv20YCMfSwyP/fx0mJs9n5ar52gtR4I -Opbud6OH804G537qQriQh+UJ11RCPsJeJuwVFF9tLUf5k/aS9Uxi7Fz4+vDGw935FaVZe7+LMv92 -CGi5GZ3+2rVMjQZx+mu3I/Z6tepDcjvVUXAzFad+3r/yo+OQomcFw9/LL1pn/vYvoTE9q0Ao4L7L -cncnrDHsesgTOVUG3t16i4lNkRDQPeoWw8ZYn6aqsLvfd3yLn0oaD2fQRStniOB6GYa3oDzgPuyz -ez260JwOh9I3J2ieJJNSz/DeaAF85dficu+wOyzoFjJ5Sl33xZVl+/4p9C7s8Ip6R/VFj/fZoVPq -d/LsRsWkGDEK5ybynMetCKPOKmoPe97C+g6t4W4+vKloSmqz63UfGdq4M1D8l5Rsyg6zfEYq8ZO3 -cERxyOsP0tgI5QLezGMzavQQ+qU+lxjr9QFLRClT1vD279Y8XDfFm5g+n1GgMYO+7/4SPIzSs4ll -DOUBaWBgLitPgT+5t3fh8b5s4bLeyWW/yX4xY0Kg/F1qVycG6NoF6mHsB0ZNerLl6cviUhd0CcKo -+/xT5JEZ+6hodvDwFnsdmNbuNkrBlceFL2nPVmE89Rq4n31wtm/9onyL7YlEGTQScyD6dp94R7Tu -CxuJlznPAKqc4HsJafKTY7rCs3PthnqCMaZIMhsruRaS7Dio9lr5m7dQH/sadsqH4wL2FKRgr6ov -fneSFOwkClFhA057oxs7qyCFgYUBDyXGpR2ZQa+TJYsLJEqOsevaw4+d0+cr4ivK9wKPP30QtaMO -TAF1ZJ4pnnaMPVSKgSZnljNvrU7eRFkzJIFanVSlToJrPwcnPJa5VI1Z8YHMwfhOnW7G5bV0RUrt -8mq2BhUur/GTeQuDTuil1rFy7RzMeN/IpZhtDf94N1SFhMzIgkG6VzlpiWdKdGy64VrlyX1RzZYk -09YkGu4a69B3reJ+XH99zbDwaTJlenOSZ00g/W23u95CmQszyEF2a6WbWqKmWgQYXaM6IttCX5P1 -gqB/ijP1cQdGu4hr3qy3pcXoKAEMPL+yLXhk+BJaI7VQxmAinlKtdJ8q9gPNLPyKFqBefZgKX3nX -RlpqHebHr2FZvW+i6FrQgl8tlTCi4OEOZw0xNsWRhrcHeMQxKQG8AzwLJaL4kbfn7zA7p8lBD5Ab -PTYPzdg9yLxJPervYJW4krz128jDCBlSR+CaERFg3yDYAiLG8mc3sVO0irnWQCx1WBl0gF4UI+7S -11LN3vLlTfXEXiCjerxZ52x82Jz7V93KmYgzoHd0qXupRmyMmJK6puFPHmRAtlA/oFkUyMvWjppb -r18RIw6HndSJdzU/9Bi3tAEotmRf78TnMdTIrgxAnUT7Jc9O1tV61a/0IHZkV7yTHly8QRK+frTQ -xwHtpV8shYWjHW5aXmQnVOrFP7oqMngL7bEeFy41rh3SWTdyGFTZss+DUDaSQPLwY+z96n2vfQUx -7gOGG6/oUwFFt4XDs3PJcOIf2gKeOAioOuJ4q4wtK343ytYRC7yZlm0PwuBusJEMIh0PSQSVt2jj -YlZ5Agl9UsHDUBI2bkZc2K5z4Ei6H4s9sQcrafvwljVr7m8nOyc+x3VtCaUVwdzBjqE6EJXb0EiF -kNc9t2NVq+S7QOr9gglmX9IJx93wFAYlrsXdPCBW0i1uBVHsww7BjQuKcRVRn96GVg== - - - iH7/QRjP7H3iKonr76npzAKgSjXYMd2ThLmqV6ILULyUZisj7+4RiHbTxbTWPClbQH2b+xrvsJ+6 -0UgjcYUKl0/cT50aWMR5eDkD7EKE2Hw3uLa/vqGFvq634GrjWg3cs5cmRom9NHz9SY5ETa93fjzb -ZgRE3/Dw8ApiLSiC153IRKzIGHvi5s+VzhnooRLpA93gs0rqQ7joxNdqCSzdWK8N2SZvm71mMW46 -fscIU73hTT/msHDP7H7F0/fzId6UnHusvHlhRLJYVlJM48Pc0lGKNW+gdnSt04iL47FIUAoM49iB -bLXuMMdjioj2q3eJn+qBvVQfCocJInNSMU9QGPgC9SoOtdsdR4xchp0XwlO/77BrUO8Y+KiyBLcC -7n08tqdXKsg1t6Ac0o0OdVj68DvtAwC55REjeS0KCoOf8CcBdcyhybSU7O0FADec1TLeBeFxcBTY -DKobVUrDiVe1yA+SrV54ZzJZQEsluntZ/h1vB+jyqWl8HFCvxk3k+71po2hMLNxa8HBj3FJL6rEH -UrA1DkbZLzcQ8jskKWR/9brFaYeNP9wgYIYIve24SHIliiu5gCGpq0QTtu/XAzDEHmAXao56lqtB -nIwCMiGf3uDDfWB4QINgvDn+KcoI10nSndNbBnusELtM4CiXScXZoUa88wmGo0nEolABkDnGucLO -pK4l+AdjxfB8D5J8MhwplAhvMUE9szvyQafHS0RyfBq/grkYwuadlpz7LgM6dbod0Xkpg6AUR8o9 -nDVcSSaDruLWDch/64X9V/igeyE3CTa3rZE8ID78yifUXx6Rn/lQ3IV+1TCUEXniFnXlVdeG9jmA -egcAXYyFRoeqmhHzr9fkePdLbkUQ5CllKjGKAxxmj6PrYK4zTr8OWLF1Ajpr7yH+2WerGR+eGTKy -vaQWsDMK4GTme0mjyjHpBpQCg48XdhneS+ryIdmNcF63xLAWawkkKxLnB2oUkVC5tVXUeefrihCY -oFy8BqHIwr6PwyClFGYqNrK7JxpI4B25EQ4MsiqPv+PaD0lkr0b8rqEHYPxY/qP0Q2WyG4Ttib0d -TFqrCZfoUC9IgwJY/zA6jZNs2TeusJV+yjJIccSp+uXz4PgdBZF8XSJmhHT5tkAjG3YSQ8WS+/1e -dlEcHNAzX7FpT3O1GcXz6LLOzcy3jbNYCg0Lgq2R+3XIpkScOxDAwZYkMefCrXHOCESjmMuFkoz1 -FyVUZWV6+o5KPdQBSCi9YvWjVkVJ5LGxzcLDXXhUCawrlHSYUkMy4AuMCpLb2O+Q/54idXJ8Qjy6 -wbi51rxz9jhRv7iX2MI2sxKnYxWl2knJVhC3gVjv7Mm9cLyepktWZ16efp3Y+TmVZPcduZxDtyPh -7MTu5wxBinsKq9IlYMIi2eMy0SggGqpqDaEkwXeaztUvyLIGvwGp/Z8AUJwCOw38OAnW1x16LUaH -4LvEDFvQhNoxyJajVTYLqCeR70OYa7hPDWJExiJMfpL8dSzmS4MZfoh3gCIZZVthC4NasoCkjJkK -x4itmakIiHhPERy+9TKTaxN7TEq/D97zx0yF0XoTZguI5MTakhY88t5VK9Ue7KY4pfAl7qHSAJEy -hA6qACSAyI5vHsqQR6hkbsEpY0+UZI2ehY086vMwjg5qsJTbGz3VcCpu6h32crC4gIceKkkFjJ0f -MSNupeuXUEcXM6tIFCvuDVeWKpDqRIzApZIxdkQ59TIUnzf8sE/AZDWc/Q47znwl0fc2XCZNNZ7M -ORUVFLr/wFWZvXm2S0hBQjHokttjslJI9FzO5sMDuisIPwmBCpPZIt9zWfvq/Hb9WIGQjbBf6XqG -EGnIz1s4DApNcvL1nS5k02NKnWIpeiIy0fmwO2Gs/pyrRO10NaEC0Sa93crpREEtLjiCqMXnvaa6 -cvwiysgVSPyPV9proRDSI/lK1h5Rrgrei3ZHkpm/dL6Gv+wiKJfy02IEvnPY9fZXdEElIG2xuCMr -vzZYTO04gz6ZwnPEHLugM9mX4YT8ViA6QPzXIy09WW1v9kEmCMj2TWb27Oj+rppKyLOMnPXsg+Hn -UDPppK3Ua7WbAMGdGdQidpLXeAlJ76wGX2RfFPkn5xWYxnLjDbTk3x3oDRUmbmMnFKlHR/1Mw/k5 -RuIyIiyQrgkxNkD4ZpZtawNe/LNCG2AgKnAdIIs1Lj3oDQHIOKFRa9sOw5iulkH4BoKCk3RzgvVo -oHMCC3TbrABZ2a9usXbEISD+QxYlt5zBmFWLiFN6bdU7wIXLI69tEQk+rTrbfzCfpA9Xr+Phi7PY -62wbVQye6vLFHvxOXiO/26Sy7KRL0ibdgZFLx5o6jPh2w5LubdAlGfFmI920Rly/20gIR4pDypmI -ay6mpUDO46p/cwuTNV7Hgd7SbEehz3CSY2mBAONERN6itFDaqgCn98tiUz+hX+HtbKFFjecKWeTB -9S13XIsqt85A38NYCqRigzhLVSi95Z7oNZymSVtYYK6C7NOXwHl/2K9fwRnQIrpATiQTm+V2FkAZ -sS/sXMd9TDGSdQCKD2KsZLWJGJQ+jHG7ng5rPVH1YdQ7VcfNxr+PS71GAMw4eMEBQF/EeRsbaLG+ -W8Je63X6+zYNZLDJXIjaSpYN4MZUA9sKAXLDeOCtBWTxQwtKxLN7REJi+yvEGg9ytNYgpZuEojyM -nf6wJt+shXr5yplNSPTMb8xgv03XQ2zGoK5mleQWit20mvwNLdQKPjEFnnjLmyPvJKZiXKyB6uZy -fFeYa8y/lDeo7iQdcuOYEWUQgyXJVSkR0EL4UFVOXHAxPY4hrK467L5iX8QxSlVYaCene0UVwdeQ -98ZvOlHXXCOB4iRg31XOvS8gQlfcqGq4tF6i6l0riZhKg1e0uzKyegy29qukWll14wXJtSTChL6Y -CC+ZoEYJSc0YHFFISBU5K1mO2sj+W04qVNeg1ztahmJ2qsEV7xOeHrLTJbi4PPTClvvFMaw1le+w -N97tIR5dDgXIZ3AgSG6Kd4/BDaSc5F6DO1oQxwMeMyrXNjPRw9hrvQGp90HZ8ImHxwQDEJw6bRX7 -pfqQbMEJ/DEFYOSVbdEWhAbhAIp9oeYb/qYasRBwY/sSko2HJx8ufJjfHhQO8g6kUWlRQavSWZgS -hxDtg1o8lGb9BLcd9elz8OHHOmMlwUIlARKqw8j+vYUnNr5lIaiyEvFZB53B4gqYhjZkC4vF0MgC -FYq1nRBlKhIfQD2QAqu8hUkWsWmQRTO2gWpzIB6L/HdmWlb0QWOv2GUcxFhmcnshSllEeAEunV7O -2cLduJVXuQO+4+1agbPmiQMZs4uGNbXhn8LZ3pdSWv6Kvz+OhZIN2ek0FrFFy+kv1dbg+/cSi3gR -Cq2kmdhty06DRk6BmNNxixSgu+9ni+zD/jArPQjMP4n/FDCGQo0aibYGplTh9iCtuMyOC46/oFSP -e6LEtC/kUV0uTBpuaDjtDlG0KWWDNupVxOmXx4EhOVMLpBzOThA/2dcHOfL9diX7OikrIBYn+/pE -bDgFCuTXFgLJ0JYyOwIvK472wtqYRGtySey68q2rXN7LF2MFstkXksaeixZQMTqfnINCEathffa3 -cwLRZWzTdh7OqG6uPHNqJXOifHpLqOnhifwNRIlrhZaF3nboCEvFXsW4oTRVCtH8YKGiudoASSiF -AAg56yc+EdQgamOUSLIaXt4WBBHbctJsAbegg1VUe8Lmqia0GWugXOUM9gY6KRC2QSu9VQIEQGko -nfXIgsICooFQ04bTrdV4EwoIfio8jAH3QJ2pP9wgHoD60NpTNZRjFKRsr0NdeFKMpeoO76MIgHxV -iuz1aRS/KnsyO4LTGyS6arxIS3oGtAaJ6SKTnnmfZWLjA1tZXURJbGybVQLFAD0lPERdyk/tI+6E -UVV5FPwzFL+O1M377TEGALawLmBW13f6qur1QJBhOu+E0T7hxtVt6LeTgd51k/XgsNpCnixAwYU3 -oQ0U1PZ4IvDhxT8GFDCWveDylw7sUALTVJmwACx1rOptlCj2fR3cKr81sQfdWtOlAcwxy9BzZgSH -MKmk9YIyACHYwaRTLxMgC0oNVcBB0wcdQdcajBIOwmYXUIB1eYWTVgNy76wtcqfqwIglCIFcwDoH -0l3Lx1UNoNtLfE0TcjrA1x4nOW//hZAHgcBR5LfFVn+o3SDXS79JHOpAyd1yssogXB2xe57hENTY -innGJ4MtpdmO/GQAdx5X0VjZcFofd+HYIXntbUxY37Te0wVXvivz22KfKIMCeLdVVsdfughinPVV -Q+TxgxohsC/XSNzQJOEGlBhwdK3RS7hMs7ZGNsxrvB1s+S5U20AgRZqNgpbOaAFwEzLpVosWJpcM -44k1kaNAV/thbAvXd11xHLd9sTRKyIa1mpKtO4B/rTFgJSluRFNAjnPokUrUY7aVJttLmOcwMSth -njqxmr1mOgVpXFExh8Fgd8S8GAsQ8MBYtJmQ5idElCTyVwBIrCPCYDOtJgiKtwmJPWm5smUwbV8r -+mWg8QRO1TfcthIstjq6Sp5sKMFNEHQJdnagtMtt/CDuilrL25xSebgAEV1c97tRwFYO3/DcJA5L -3+9g3BZTWMdI+f1J0/hTqHcEYiNGfJDfkPjwBkzZC2KanA+QlrqH2eyWgfugn2gq3YYz6sIGIsyb -Q/tt89Jw6StLqPwA8IkC7B8WbW7CCU9EGeUkRf6fe91mMmdBKfe7RfZZO1Ur70Ni93UrZ0hDzqDy -14rjVcQ44TNdYnykgYVy2Yd/bDQchbxshxjFXuVk4ZOMuvVKxPGx+p132B/n/YDnVtDIOXS8ms2n -3gRI4j+XgC1iL1i0KBjrjRGsOJB65Z3qmMRbJGUa3DTUFvXGQvZL4uouaPrDjSPerhF/IuvQTvye -XV2Pa4htwhmKO1lvrLg8zP5LDunCEenIkXbOyJuBJr0bfYkvzYXMVtncMzCOg/v1ycKVXS7nrGOr -jIt2iULj2EAxXB9krFJydrVNgtqOwbh+RQPBsQI8Wh8kEDwsvJKXaFhuO26GYq/cSjztNsCYoh8N -Azah9paqU7oSmQDo29hbJNhFhcy5bSZjmU/eSV88iY75Y++wA2ZyjTWB9uvsGccKeUk3U6Ls6SCJ -DNCErPpBihuWDzx2lYax3IxsHGZU9GEUXjsAS2wdVRv99pTHVjiePet43JT0PuRiFOMEjF5vIWyh -3xCL81S0NLt938DF8EdMQYFJ2YZ0ciPv+WDC1OHFzVK3nciG+208ZGj7or+hIphuwxa1LILOBgAw -WmRwFiOVF5AL/DFZ0XYZ58W6HDE6ld8yuT/8FLjENLDHYRyMXYVutI4u8B04CGV0T/HRLR5e/JIF -yezggd+gmuonbYdtL3yzT5xIBEs8phP3KMa2FVxBR899t07dWK3YsFBZvwQ7X8uaEMgB4OwNGMb9 -hX60V510Lyy0nh2eb/1Sp5DV5kPYd7dv3iqeZcY4zFNwfVQpFMHp1NhClFTABZInWQ== - - - MJ9IWEfoIVxSloixoCxOtwE3+oX+ieRgVLJxXAthE9mDiohLSLvAfSqvSx4aGTWRkpXJfU7wRQU1 -I8RkNXK1XYqfj/5YuagrT1WKQ0TzuvcZkYlHC44okVIFr095POk1uorN4p+jBPTSYROIFAt9xgRK -K2qN0kksgKyN2hQCjgY+hVYEH7TgsQolzCIbgNg3zkcl5XuHPU4xjy0q2IzFTS57qRg2es9xPI6Z -KpuHXyPHTDwSwzXwxmQp4LVST29hpSmFEsGhiENnL1Bsihl9GES9Me4G2vIBc1ixK9VYuF1KC46z -H4s5oKvUxmzgjpKvgO/o2uFezw+3BObj3305hndsUm/KCdnj3RDSvRSzHH7Fs3la0Fug607OsI8N -Pgw4CaTqw8FyiDrSX0Od0HFXe2xbeu5urWgYSOvDDXWIHwC+BRA3v7IFsoVF6sJegkVQHdZU30pz -L4Pr2CLWI5O3eCGo/D2djaT2/Ep0yIYjfNM9g6EPowofQfNxSTR7LcTgLUswAPsqx+0iViCTyg// -WQyqqS2ke9rDjg30MosixgKeOiTwBZbbSV1BpTWxQ9NVShI6sb0EQd3Cvgl5S2f5q3sFyuiiqIlo -tKHUV6upGlDHnqe4xSLI3xVC7PynsplEHcTrLwXkeG3sJx4Gexgfd+SKjcORzy8ENG9KUlOcoEaW -f2VEhZxzQ5NJGCePygwnkfadnEhcoKivMQ3EoiZ/G88vQfGhDPY6//dLF97Im7i4kR0jK/2APcK/ -bYC/MbxFskIuYkaO8RLQUeo+fsnPlzoBdA5Xz9cusHeSUyGiRSrbPz4nZZxPH0Ygp6B8q0aKc5Rg -Ipa4JVDMpE4WWhJkPDVK+FUXrHNgHyqXbDMO7mIXW0NbSBVIpowaQCBKEuMk7CeoC8TOCgIyi1+b -u9YC9AyCFddZENkCQqiTiUvBKQC2VjBGn17hef5K5f0CAlajwB+wIzxqejNRsjSQmAJGfV1W1AfJ -+WvD9sN/+3t6xIzdKzaNlJ6o44oQtxoROdB70/ef3mSABDrEiqsRVzTUAb324S2Tf/JITdzCagcp -oubg3DjhX6Ha7wfMSnlVeyplBob3goT6a5bQLztGBi/JRVRsVTUU4KvAMkFP4rgaIQJAEAMz7SfZ -kDLozAORpDLGIBbxi/ZrD2KxS7kPoH31BmHeSnAKBSJEsv8AwYRKezESh0P5gxe4M7P1lSC67uWo -JfbKgRP7tWvR68ZLxDYKltBPqAg3OkbwYTseiA+FeDEWMt3Wk0jnT2vI6kXZsKgcjO1ZH8ABRYHB -S+I3kzavXYteO57+GFa2knly8i6idDHfzIbI3rKwkhnbQtklyM7dvonYvYSSoBxv+b8H8vXTH7PG -k2I0k+HkaepnX/Y/v1ogX7RWla8G9MJkjYpAVAbLhbCfDoIfpulre/d002ZBw8C7adG8PQy2wpc+ -vCXixU4Aa6gMHUZDB+mIxXhRz6lQEDd2oPGUdeoNLSDC1C076icD64jAnvG5B3nkQNK7n9T2pPBh -eiRj9ZBmmPRLJfDSwvypmUx4OlcB2Xyo1gs8hrgOzZEGbMbvZEtSlQBH1QrS/L2JpYls6o7chyCp -SKZP0S/pBthRvfLgtWtvmab1uW1uTI/9meWLQCyhBEKePdh/dgOXPdSy8DBSw8DVSqssMsZt8cvX -i22zInmvgQn2DmW726q3uW2i4nrnPNNrO3FsVF4fXGWApNNgiFtW+/9Ou/PRLGZeBRjiN6EEE5IY -PtJmJUq85BcvipKuX3irqJdguDQK/WXfODKCI104/jWz8wE7apA2hSLFeJHtU+UNMyKdutPVUswb -Afr2DFx9/sE8hEABePD0g/YLpiBA+GsoZR3e7GtOYqjTwqGKnAlJpoPv+YDi6rULMVIS/UVkVk/N -D9j7QQa83/jACgkBUmkzzP3aTpCHCioZVGV6sXTyUA39+y2UQPSRbpvgSpR87AwquZMR9uQlU9Yb -a6F2/FxzjKFUFFSkRcqKDJ74FczgVYuSKnN/5e2KVRCf3uItveCnxvmC8PJKY63ApavJtKkrSZsx -1f10unPHTiM3HkZYkP/96vWsc0rrSEkwwSV+wD4qOM6HJEXJTRIaMCcoCF/beU8/sStOTQ1j8if2 -DYE+6Olu1PX1wEXckgqsH567fgf5e3oUcPNef4yvOgYgKRpY4ykmdgvPyEsfyJuq3YqXJKbpnppU -udpVUFhdXF5PClpvAw3gzqHHT78YqhjXhNwtWjm57MeFB6Rsfgx6XeITruWm3n/QDH9hdoR8ZPQK -1Sgedq/V0lH1kvCGoIZ+FYRagktWK73eftJAB843sRC+dsE+xuMa96/+7t+d//Fvf/vT3//jX/7v -//pPf/ynf/rzP/7m1r//8//837892//173/77Y8ff/7TNzV/e9i/jX/zu/Lt7+Qy+od//t3f/EX+ -oX4r+r8//D/5t3//+Kf/87D987fx7T98+2//vXz70+Mv/vCfMaGcEE96NmeSui2WQoH9/ZMdh8n7 -p3Z+ZE/t/Cbd+o+P/xOf4HH2SQzqSO25/MPj2nm+FUV6LL7IH9HYWsAraR3CR1ooh4WemL7HgG4V -5ENB/QdRcLd3HEnYB3VJ4boGsN3rz4eLId8ah5RGuymjA5aFS8530TOLpCSAyt3ofmwTC4Jj0cYr -F+lk+N+dnAyH8pPDfBU7/ZIo1Uzxymrix+YhBnGY1yucxEalq4mOs8Kf7L0Xo65Sp0GcDkn4ZRNH -PpjihZe5GvmNneIvqKA/Qep+Kft14oIqKSVwCz4c9pNamJufeiKKFDnpURhagi8qCeYRnkrpiJUg -xivGGAnHF9RihQbWhVITOLs3cgsGd2SNbdkv8u52NJxVrJpqJptyLN04EyI4EEgIpoqXM5BWRAxO -vUH0Lb1cs7uVn4yOKR6WvDk5L1kfs6RhlhSCtmpcVkPvRYz9M7lgjWlmkA420J0sSaZvRQOoIo0j -Xwq8btC3nQSGxa30kHyoKp0KExIddWOPKzzwT4eJfbFXfohKtpy6jcn5WC6WCFWVUPY37gj1jIJU -guY53tDCXODxVApStqwSpCdnxCTa0zGpwOpYj51W5xNEUOwDoBVlEgu/ksTAwLqJU0ntEtRuV4HL -MKNMqsZ6jBI2ftAbvkSa++B9d1BrJL8m4K8o5LpZB1SqBxltg8dVPcR+chJGALQjPulMFYyTH6R6 -IKMpXgmO3Cwo5BwH1IIpz6tFn0CM3EZE4ibGKxWYolTuZACF+I2diGqWHGsw32x+yRbvdxKWe4nI -UZxjAabH2dWlipj0lqkFKOKczB7TvAzWLzesT74VZJEVFaYSRUEkNMW1m5b747v5CSPGgbV8nI65 -zVRgoDRAbEHzKNbCIvhxTWQsCJPM0dwAJom5kPPYdw5BT/aABqNbj7MfQxPEFG3ZvLd27wHuMbZP -3SnNqJzh/rYB1BXQPjFThE0rBAbngEs+CXSyLSASPNcsGMnasCeWgE3LRRwvNgGdRNHA+cUZVMUW -u3qA0gWOeQMS4aXfO4WqeQtyPuTolbdwjMHZ1wnxlL1h8Shu+7/iYeVT9WPIW5YtYXDyHrdN6s3V -KI5sl/RLfmnyh53yIo6mlo9YhTqxhUtOVYBAerGCRJ8LjkoS0AQZtHl/EDxlQEtcuEHwkbd9GkcB -WTZA9VowcHapsL1YPtXAKb2ysCsS4ArHfDrd2MJq4PDU2kTCNJXl1XwFrxeRn7sA/EEJtKuaFByb -EGDu1QqBn3wChWkyM+N+lAA6N8CQiWhOYJoTnimIxHtLJLAVrCqd0FaHLAbMspP73PVZejfJXx8f -Z1HplMM7Fr8jEvETSOcddsVpmB1gz07/2jVUzHh5BCm3FVu+m4AjwFgFJ7MJMSA5iRaR+7nbOMIj -ySwAQNRdPPxY/nq4DamGa5wVbCBkN4qjfwS9ydO/Apg3yIbjeFdvQUjrGuMRVoUtxopsOTgUBX55 -KFATFcIKy8SYldD/FeaXDdh3GWSJabWnoTQj6n/kFs2kQp+p8IRfSOoNKNMQjDTD+QWv3bbYguYd -bChBHiP8mggPLa8i7FF1+8SaLHw7lPMA85cYvVb0BgJaOZjg+k1mgyVzPghZcr9YYZJw/cirI6BA -jLoiNZj0bIZOkiCOM6ApLhWHwOyXQo+dUm89VtE2SJ31zQvoBDTIYwROeD9WlG4tBASue4TyZFCF -PFxA6A/PUYwVqyLRygoWcaEFaJ2IcVKRBFgD2XdxvKRKcrF3oH3gvfZLN1zwZwZvEYCiVw67TAnR -iMrqZn3zapieSe+hMt2dwtm/WlvRwuNfINXjsHoh3eImrvcdM4ZjlEiFxc7SEq/5U+jiRrFQp7ET -xBes6GLmeXoisvuwj5BmcTyvIhRZVVSBcgxQyg7Bj9Fw47zFX0A1eW24vRB4VMsA2B+HWzVCl/ey -nH7IEXT9DUBQLUayoe/YTaVb3HrZ/0ef6kILkIlsj/UAzztBhEeDeKeGeP1tG3UOLknktQVOu/Cg -BHlZ8W0wHQWj6SVGsV/IkzyvElOTIC8bNup9yIck9n49DghRLcF0jp2HwoylBnaSA9ytDimWlRmd -zUymiF9MhPjuYp9+nE8J6jnoEI+Zie/ASHGDWLDb133+Gj3t3gn4MLoNt+8DPkJyvnKee6xCjHvw -jfdJLdwLWaxiVKeKIo2J6hC8gQX4LPo1RsoxgDF6zBT0xB1NYKFcgDsFg2e6gSI9P5See+DnfNiX -3XRi5ySkEvX7125rZiz0/LAEF8lob9YrkAYI8gVGXSBgXIcgzxW06MXUOSHpMkLZwMWjzLhnz5u6 -Ge/ES2gBNrGTipOwNeDZPjFWqHCd7tN3J4HoG+CMIfV1PHUHoJ4ezVTxtAm6zcc5igNv8dAdhwCq -mwGgyvnk23QFtlUR6P5TNRoI/TbcLRS8CSMoOASheYDPUU5CtrAup1nNCFKl3PFTDHDM1jsmrzMn -i3o2Y0kneIFGUNhfZliF45QeEHLPQwHzPmSJs01gmmespy7MChiPJjgm4I248T+dozP0UK4xHJtx -09EBqlmgl4s7asowyB5O1bFqp+B80pT19Lf2Ac6s4odSHypXgMUzpNkCjC/u8WKsRFCFwoB0uGD+ -Inoi2M/BXdJ3InkLQtSVS8JbaJa2dK/ROyx5bUCo6zb1WhEftIiIQlBD7O1hL52rcKKFNYgY83iG -5GpYI6zJ3MjVXD90s579FPbSiuSq1zZKuuYgyAb6FTHS/daTlS0P3k4SWfC61AieDWzB+yD7mG5O -omEY/neocii78OeVOEcKGFJCUNAv8JZWBApeHyZFckxtDVmbsV/sqgilz0EmmpsBgnMkPxVnhnSD -Bajbg2fyW4xkAiCIPmOIVOzO+8DDDLXj0zWc/RulBga1yCjqJxc4eOZwlefIgMO4togeZEVcYnvF -ihj74MFXwEtdT4d/NHkSqR3HA1OA0yRgA0htxt6Afc8Tc7IK+5Iqbzq/lR98C2DISg== - - - /0H31WihsnJhR4cv5aydSUk61rCLp2Dfwx4aIPhsEy48qkjMuAvdxB4gc7lZVh7UB11YPKX16PSf -6nDhw1mfEQS8v0SGaQa3u+89YY9TmR0+vBykgYzgg85I/p6LLeSN9XGm86Qmgp8RR3X94pWF3RKo -DFz+50ohBTCwzGUgV5/oIX66IHavc50QkRkByjQrt+kW+m0GaqKuQKT3VUaq1I4DWGcPG0Z+T2qQ -rreR3Dm/UoXIqN6IkupnaP+4wyLs7pPVG2dA9BPEpr4lsgGsV0S0RPOzPbl4ZvSrC9xt/v1lpIPD -e5EKEHSCY+5nlHM8uUzzwo9RnIb3QZYusQONcp1+DxDjYZ3QUlwFBh2qkdUcfUNOeExYyP8PYAMq -WOAtCHcDvvqA2MBdiKtoQsSNm0GGGaKYzWSLfZpS2yC0pIbn2ZYwDSEkEotNzAwM4pvHs/peFWqY -Je49wTS0WBYubpCzJYkUZkMJVjLGjTs5VyKR2YGzgASKPNyaG9uAbKYm3OzzRupNnuVhmoQ3Abi/ -LB8QfczLsE7s3avblYC/RkXPNEWCv1AUNWd4EBcimV6aoNU1221ajuguONe72A+eraGy2Qu32WrH -+XLO6jiC2MJcFy37obsGtpdUpLRmGl7NTnsLktbh9ZhFbiK+OVBhhGUhepoNuwDEeuTJDiTLc8Pu -ryv+xkVGFmaqhlss7i8qm4txq8MC1CVASHhiCCCsg+SfaERBDPPkN36SgTx2H9K+tSQR4zwROteo -13i5baDIfUnAtaLlKBhQCRXcTsHuIvowVuyDxeEtdMCWkkarxy/dA9lQfJwTeKvmySypAzHifbwE -W3DHVkeC9SGee9ApdaED6Uzeuhnx3i12lk5iLHdBplBhXN1t8Q4BJhAdyboPhv3i7/VrP72DGCuV -WONCtEtay3qcvcOO7GhattcSfH7NqWi5VCQZUopg+4oy+yUhqvziYmoQW50Km5+c096O1VKjlkq/ -/fjvpbudxEAVwkcRD0h6FLtYDslDApA4mpPbnN9CRU7pwiNJHE0hv6krFs8uTlPMPPmlRle09/QK -ISWNUl5pdUS4ZUBRs5OyIR2a2oXNC7IdDfJw6VhsfiXbNe0OSWRuV5SI6SbOFjAbkZ2S5yoQdYn+ -5Ud//3Bi8GOe699UPpS5m76DqIVi94ajvrWysabumtHp+a4HZr2FhqSrTv+FPnjwLQUGpYU0jjW1 -4Jw7+t19sTXBvHFrQQPj4NqfxTAbnCWBokFyq+Xl7veoLQQrcNRbEnNtpg/vro5rl3VkoeTnoATm -irqYov73HcwKMrxEpTzMrIL39dBRmSGvNWnsKfp4o1GHfah5oFPXZ/j0dKSYBvaaKD/fqUH9V0Ts -dE+s0EtrA9NiBDxJmrxwTyCVmH4aEbSdfYUU4nn6e0eRi6TrRh0t9W4FQoKZnbIUYh84AxaZZfZI -rj2yLSL6FlW7k2+2+GspDaRysQBeQkF+j6Qgjt1oAB+guz1hClvRwP5VEsB5T1OtspWERTPsKPcp -4PK4Qi7O0uMQCxelOyNQ0zOHHpbo0DYE17oHP+XnOL3hIEnLPAxrZAW2kPxjMYHbSMRpN196sW+u -x6tvRz9mTxCVyNu5EuqW9GYciNNtHmiQBiI5Ig3z3pDor/dMdxpE//XX+ieXcLOUEYe6t7ySTwcQ -hCjsHnx+z6npg92/6HpqwFW9ZUp48H8LQRnOHdzK9gLuy1c0G4gJOCCCvAz+7ZvSgqDi5D6Rp/sy -ugRfRy6ZvJLHDTzsZsGT+ror9SFd/RelVEVAePGCLG4gJYgDFAg/Txo/QJGzz9uQZB6mjMNHKhER -k28eZ9rBqXZtUzPj6IRApAkvFYcIY1DkeUOxSz4zNnORbp0nPqc3cBBmyl/uJF9I47lm9Hjdp8nD -oLysLc9XbNdA9w3Fpb0P0nDShSAT2yd9OhQo74OEtywX56jZgvnDxTcFXbQF7l8OitkXKqh50Tsc -J+4jbEELj9xJOvi5FZFkRy5uCrxeD89EH9b4FG+Qh2tIibsA9rHtNIzewk3RWmBt9k04b6e4FVtF -tO2m2SBIcHp6XrS4nW3RYyt2f98XLJh6vO1oYFQ24FyMO6MvkZfbik6AcxHJkX0BT9JLRsfDfeCM -czyM2rD7pjyiNFxxFGv8/x12J+y+Xi1Gu4N7dZcZ7J3lsi+EYD2td71WwH7skHFDT62k1O00kelS -Jm1e3GTdl7mohJXhOqRpP+JPPx/zRwXzknvjv35wPiatulMAChW7Z8EPlb60T4XvdXlHCr4PeZiB -CWzo+nObUR9DA5xiCVRbD4GfPwUhIpnlvv0fP9Ce1oMYuXISaka0fnndHCEA7JkAuATfXUIYI7si -KKd6w3EsuVq5hKM4kAetOsmmzvuRurDr+bT9i9ww8zWjUinYF4kMw8kqxuUito5qwMfDzqeqO0AY -mc5IKRgZnlmYzjiTY4ask7vmYjusVoudSbp2CGl3BJT06yBgijuOqLt3fOAW6QxVfSeLUug2J1SF -BxbFOICqSDuTKNKTogoKEDLqRLQBSPP4POFpKUrCW3h8tkkswXHl8GYYUz8sC75PUAonHNfj4VFJ -4eXY5dMs0vk8px+XgYnRjaPuYY5znOLTPacHnGf49HQgpfjqw+6pULE7K+2RGwrPv1PRQsTZZsTZ -pIWDGYlMmDzcWQXnF14xXgaGQsFRhOtPxK0MSyAdPvfTHn96irUnUTrpw0Q4zBl7DyOZJxAKKt8K -0EHSpD3D8ulP8ZPTiXgNIK08yUWRqvaUNGJyRtqxeP5/9t4ETK6rPBC1LIyNbNkYgcEYQ3mREba7 -fc+592w2CVhtFkN7iW2IAyFKu9WyO/SitFrYgjeTzORl8ZuXzMd7DwiTPUMSZ7LNJJnwDUnIvn35 -MkkgIWCyfEwy2Uxm8BKW2Njv/Nu551bd6q5eJF3JVW6rq/86de5Z/31J8oZPXpOYVKU27ieBFyIP -SzkMkpYJK6slq6bcS8hgIc4bma4bo76FLEpLKTsU6mttKc0J3b5EaTG2OjlVs+IC6xzIpRQ5Hlqm -q1PW3LzPuU3EWUx7XMZ7STwsZrJP3u2sLIKw6JCM4kkhiOntJV9wFpOCKR5Tfiov/dYO2GIqx7SP -MjdUWKSOa/cuqXXqXRbYHJh3w2T84siOGVdSDyG5KVs2h0DIU0pihD72BBTTN7vCcw8+hXXxgKcF -XnvJlZy73QfytaTGJJPieUssd4YjOeUTPdCn4iF4wuVQohqmhifPagl1Qg1sCgtglytk0aTKQajq -IxxSfQyfx3fDc4xEYUm1XNjhFPvGdQpIRZiCT5IuBzpOPl7iUYxrm3afnYQxokCiZSSThyycHDfH -WR6y1awDTfCmpCTURcqu4ENy3/EpfzfGe3gJ5GI7B2pFJbgQBeu00+Iq6kX1A0OoUuJ/rdO4rEoN -U3gj+v7LIZRafBiuIZntdEUpUjDSMaUUyEg3liKQuE6TxlspcYiXXK54XCVIM4tCBLgRePa4OkpO -4m28o+ynA2OAeC95nLh7QEIALRFxGAnCiIY3oo7JwkoVKU9zSLdep0SdliMWsGqCxJlkDreYkEJi -TVAqZqCViyyZPajiTaoYkbK8wcB0KioiNNpStmIaWFUjUCMRk1kaSMTMksw4y+yLtT+tZLsXXJkD -00nnIj8EzJgrI3Qalk0QYJXiDRpApKEEzFijivIe8AqHdI2rFP3pkvyKpFciU0RP60tKPURjq+s7 -eJ2vRqUTq+ulEosk0fWK0hJTSxDiE+togsSGiQsSCC0q9RBqeZ2zmzaSz4OoagQtapsye6KwK7cz -9cwHh7apzMRliTYpa49Tl99EMes5ny0y0yjQUDChdhTsk1QfEoTikwnQueTsD3VmGGizKktZDTxQ -GbGvEpfLSYodjNZGODu4uDrpvxO/ENAVmpCvZFLU6fQ88VkBbWMhIXkSvQ/6SiPoMwvIQNVrqqLD -rsKOC3ETkK8MqIRT5pjMPwvUykryAyV1WJlC71zKvAcKeW1kyapkNXV1dhRHcioAIbBe8j1bF5K1 -kYMrHTnm1qZJjulwxN6z1dQnbxhHZtwJMtIayaIlSbatSak2XJa53dZ5Fh0pSpKxmYVZ75J/KBim -uawLrISWjiM2lo5rjZytS6E7MgBPoB08q9LFjCFYzAu5MVldWFslBysI2SBSDcBUvYtJOjgJWMur -lmF89GuQvDclp6sDDwglQyhrz4zYUAoVeZV5YUj0kCPL6bTALSdrhUmzTwEAvSQGZ3nCarL3E7C2 -ddm6AItLKTKtyq6RFAUHFxMOzHOZRSWCtZPDJqKoLURRjMwyUWoDLn2Oz0NZWx8A7rxkuGL7hQmU -xJ2fRroGE0gnj0DMnZZ6kALGLlkoDfNafNLYhckngcKlhHkML2ThFQuNmWtSs4eUMB/5Uu7BpYLh -jqzKQ7sFdCObiV563INNwfp1NvUIrImOHBPDNZLT4eMOymQ/domJMJpYJV5Icj0DxYTciZq1MHUU -jCOXkTqnbCl1AMUhoQqikoYzIu7cXIDHS/J46rgKIg4jeuA8wXXyBycFxytOfkAjyMIAfKLfdeZ2 -8Eh3cp6kHgGkJK5SGba6bDzAg9wUSRYIKYnZFZEj+RnonGTuqnOfZZ71jjy4JtBhX1TE9THFVNIm -Xe0UMwaRBEZuENodCahTtQWxTAMw7aSpvfWgZyM9Y93TCUpoa1KGOImFMWSipB58Fk+BJWAZXEia -XFPVRzpl1BZnO65ZyD1UKerW1eE4WLxIDqRk31X5uGpCVhUZHqhS/JOEZTuq4UENpXiSI3NM6kDy -BEAeMSK8ANRSKU/44izey+X6Dsh2ryV1pOQJq4oUG+lTer+qyHbC1RG+EF5mpKRcCsQqkvuyq6OS -wDtB0DHa3KmHMuT1sLTEyGV1fVhvVIaU1SVPm1ZiAKe0rdPFlbWBwpG9bwKDDUV0cklAgBytVtiP -Gg1AqKqTqyIlScu8iBSyeRMYLl7Xz8kCzyAcn4O+bF4Aiv0egfln46GuA7stxbBQD6ouLexSRKnC -ECC5mVziA/JDOClikWUhUyFl03apQmJIIWYu+eFiQ8mkl7HaWLlNqoGyFy0kxAhyyESOAKBOifSy -atNclZCIIJuHlRWvFZxwISWoOWQMEzPW6UxMYohtipwAoJeqUsJrQQ8+Ydgir3gt1bstKTWmE7yS -rI9obUlwXaTiHZLRxEjuKthOqXdpxFCRZQqcoGx7XLPFJmYHEkRTWjrYJI75grxCTgpxZkEkWIFX -Dnaq82ySrssmVS+mFk09qLqGqk6aBpccSgoF7DovkSPPkTxFFWMN7AHgdRksEfMHEze9NUvXpjMt -hM7StSFHQ1/h/ESgP6okMr/irK6hSKp+n/vxoEZXibrAppRT2ks2frnjg2OYkuFBuXObvLwBvS4m -eJ1Imd3uUcuc3Kx5eAA04rdT1JZe6KH2Kxf1d1ISh9rnDIBWXCCL2nfEp0h+tN9a0Q== - - - dIcgKk/OOTE4iXp+WuQPHJ2TjIsAd2IESgPRmduG1B7xWlQaqJxPWHaw55QrDu1AycMKdJ+LAnfJ -M9kZMQXVJScwaIuAodRWgIm2ADx52ErkmS8kOkEcNVqHMJWNjuU6cZxJo2OeJyQnTtAqJM/kkkuQ -eXDbEMtxFotIZjGxbbAp1adi8ujJIha4vhGkwYHzgxfnZNyMxQRXMjhOwe+wLE/yWnECZB9MMt0m -BUJtUU0isqOYFvZBqsR3om8A9dhylxpJOwlA7cQmXSQNQigSkPlt8PUxtYt1YjlA3ZAcaoz4U3EN -bXY38uJBFFKYGVZvmWobWJ0V0WclVbKk+ZXPy8h4KzI1wFMqCtQ6TA/pJz0Cyg8YSaAR6oymkHQh -pBBUlfIo1P5zkmPCioY0NCKEsa6BmLsL5kfLPJRXLIuDQ0g7pkMWYIi8/WKCh5RzA8IyUh0/70VF -iCql6SH9TG9b5kWztcyLKV5f0iXHc5F53CN8ug8OdTxdClXALlpA2bfrVIuFqSjXYgE1ymz2BrIt -6gBlsgbfcOZFdDRAomKLvMIBGq0RxdralwXcByj43NbxNKD9pPLhtsjzGAOcyv7ZIhVgrH0KQEPB -aufIrVJQptW58hMszmR5t5z8nsbLme9tRdwoIQi2ImN4RK1DA/UUElnQ6jBj6lIMEmhOCifOGX3L -MEuFICBBFaQ3gAMfeyjxjQV3bFhdj8ks4Y186CnfZZ3XUhmkgiCCJEoXkoyOKSLKlOyVlCiYIoKT -vSr6W/K8UrtgcVKQCIIj3ELyBQcgZxcBILHgILHVkU7oE6a5B3FTB/pA+kYQ+epcs83xT8mqbOnM -OUkaayryU1sUewLnWzRGnH7BQkCOwBjFShwqKP0pggDjXZPMDgl7qXi5KVMlWuiBgTrRUCM6M4z7 -9pmloyDXkiqkLGTAMpN1H5G0TTYg8g8HZUVdLR41nwKXOH+0HSEOq3yKb0fPEcfdogI9Ga34MMPj -XBCjVStQtsyTrDS7Rg9BHMQh2h7cLimLGJg2yewOmiMu5yWiOq5PuuhQFwfvCOiedHKVRt0NDgMD -2IvarIwyLKy6YiMO5i9TBMSY9XrKZIuFWGwwxvOiBVw0iLlm/gCmTMVrDBt4Ug8FFWqAE1Fb0lHW -QaBmqwFuHGplDCf/mxpyKBNBhQAB+kg4kUWBswLA1SpHcND3ioGSOxVc+UljAh6jdYg5eN2THce5 -2oe1JJYxAn2qOYh+7sifOZ8nwAJDAy0d+BQyNnDoKGMJKLYdTckSIr1sEHUIHNCeO/CqlCiDrANO -2uQwsJ+BpcpDD1hWgMbCWYEjEqpcwOBl2JVRSWgWuA3W1SUwXEPz2MRWjI8jaa+g2lK8FZYsM6CY -Sr5qAK8c9+A5bSjuG0ljBd1KBirSERUkRc2mMbAiRaWKUhhcQuK7wuJRBDOcmFiRn0Uy2FguI6xS -oCZErXD5A0VxnAxkpUCByorUQVGRlkpJMU8IzfKWB+C0kZAksWX1h3F5dH9wIQWSuVSD2Pmk+R08 -0Ina2fhygM4rBzb5+g1icQXZwgffMF4HpkDJ5sAKLgpYF9lplChcRhqurpRugpQ+cKygnZIeojjF -10fcoyAwlkzzzqaCWmCooeBtsC66JJhbTVlMIrx227I+MR6O5DVeXFswg5GVsISzS1YSmI+lEE2I -QKCk5QZQmPiKW0pBAzHqdUCJlwAbCDxn2w1ccOKRILS7smKF5eJBxpE3deqBK4mauugzOpsjugET -SuXFDFsEaanrI27FtQ8CvsUxHZRYBbeV/DxomkW7s2HreerBazXYmNV/2K0xaw3BCf9nfB1qCPQP -VVZoBqqETWMDNQBrl9DaoA0R5t6L/ZyrWhl2UWegRWxjAu1Y3YMnTF9nQYPHUQwk2K2YKwTzO9kI -TciUuQ4L4VpiWKuCA+8qUdpaUwcTVCRoRWDIqy9hDA+uo01FXV1KL2trX1dA+2RQxzBRJcuoZHEA -adTRh5xNEckNL0LyVAX7db2M9NyK4IEDaqxL98+k1P9gGdbo9A/3zCZ0A6y2FeondUstlOq2jINk -DBBszqV4VJ6dAoLrOdevovwidNdF31ak5B+2zvveiFoDZp3rzxfJjgD3k3QQqCox0m0TNwknW1pg -53ug9GBGv0wJ6rGAnh/4TeguE94wi01hRdgrJZMbWIYKFFGsT5WHwBRAiqYoyREFAoJFfoVJoWnI -pwqKGIdUDIoN01gQm6uSKckeAaWg6ywuqqDUmfg4cdCMjQsyYmDyNdaol7R5EVhBMUc+YcqIkykY -nBRnnnaUdD0CSzFPQvJrOnWQbcwkRlgrQQBQElVzYZFklq7qpNGQfJiZTXbK5R64bhPxlRzZCdmS -KzwJkJ6H3do183wINHWBS60l7gASpJTJBsG+jcYm85cChUXFHGgAX7K0DJyKxpTJ7R+WrNLcA/pk -UstIbgxjbRBMeSdK8lskjMd4X0FAbsWNUWCdoIz3VAUFEoXUFUyLUlSghqtpT6A+nnPRAQ4SFXsK -7LdckIt7MBLRYG3Kf1JUshfWkQMeATnqDHBB7fhZFGIoAOxGEkwUqsT51CpR9sGZRmM/k0TDmN8G -TfVeiUqQ/7gNWLqBsW4gcmB9CikxgWJY6Fag6UIx5mcuES4h66riFqNA0Hoz022HIhQgkeq4FSVe -+5q/cXzxB94wfwMZYMk6T2XEhcHBBLm49Vh7PSXptWQiAA2Zr+pEwYhgS5NHz0E+XgqoBHse0YOy -IgUoiHLJsR0KhlKFyVLnLnpQGJ1481KnHYakpYTjS/B7J7oKGEXREKosBUyZUm1HcJ0p1lAm3tJI -7NPgGiTNXhEkCYDljDGpwBuH2EKGiEJKLpQkJsNJ5YQv8VZwbLMtCX3xFarEOgjORIZQibKCzGwa -MBrx8OyBaocHR3BLuUjhsrCMqzBxnaXG3onRkivMAcMYkre6Sq4OQLM0J5ovhLSC4iuQZxO0JORv -uBIyIZLWxlpJcnGTJT1XZPPFHnzts6JLyTqOF4urRRWiIwOsI8njlcilxuapPCDHuhMW1hpOllzI -moFELdnYfUJmRa4f0V5y54DOgnkUTLGOhxfqTkvFKiNl64Gs+FKWwQgfXkGcSSGFAFjSh3ScrKLR -hRTNBrqicrrCup8KHDRdKiDlRdPFJ1VribSuGqEwsOjkaQtE15lEFIiswLHmcHGovuDkXtelM7SW -yG5QcwdehJQTHhL9smeVDhJgA8VUk6Cng7CKZe2iU6YibFJreC180YpcWtFQ230ddrfbcMAQdAFc -OKGWKpVaLlMAZcnpxAjIOROQQ6nTjXKgMXIo4hAE3zNorgLnCqZq6OJAilCd63zge6RNggSsnCy/ -KoSLhirhbA+FJMmKaLhupHQmr2ZIfsYFN8D0UXq+T2wag4k7IVJZ2pWyzK80p12BpPGU4wvYUNZ7 -lIXkZQJJBGzBdBtKDs5locNKhnkOOLN1/hrIJE9ZoCICKnQaA5wwI5hNxHu4pYEQcV2XM/JIjjhF -yCsUca30UElML6QFYqEQqlqQNwT4ehI90sAKkTLdkipxVvCSJas5phDyUtTC0TqCMMOxBvHiBHIq -sRxH0s+mOdg90jbCLaMrjXkM2HsjSGYQSK9QG34VhzchXGJjFAeBIFBSeCgvOdhBtCrrGicprh0U -ZeJLUVHIEopZgfNWKS2WN9AEAfqeFZabyRzkP+GEaVDdiAK7XJ2soWAlBi5OzfZjmSaKCrbMAnEP -hZbGnBJ3kNQmBqdSoAQERyjIhA6/qQBXXoKr/w3zN4PsYypR2sJrtnKlQ1jYVn63nTMezka38twt -3PkwVr6N72+VEIaKE62yR6uUMkykaZV/2iSlYWJVqwjWKqsNketaZcAh0mK7aFlK3kQgVFKKNKWE -BILEOiCVgtNKLojMPSQuAugqSx7KS9wIAHUpPFobBW6l1q10fSgT0MYwtHIWw9iQVp6llbsZwgm1 -ck2t/NUQZqydc2vj8YYyhK3cYyufOYwpbeVgt8wYt/LbQ5nzNka+leMfJh60iRKtMscwAaVNmmkT -e1rx9lAk30oOhtEOS8SbNI2cXxfrh6NrF6i6ZQzDRPxWfUCr5qBNzYDUR4sZPE4c+Dh4Q3QIhGmx -GvS/YfIzSNwXE789wAkM4xnaGYxWbqSVbxnG5LQxREM4p3Y2q5Una+PehrF67XxhKwc5jN1s5U1b -udhhLG8be9zKR7dx3O28eRsXP5Tlb5MP2iWJIWJHu4zSKs20Sz7gMkF8D2haCq5fYrIOJI1BFcS4 -A9V1bL0MyfAPQ3NFmRoT9YoUntPXVKk8TOnykE5060dvZ3DssBKFwIJeZanm3ASVt9Bk1VCUnJNO -AhTToA4gW7upK5MQGwHWebahgvM95V/E3OkpxUkZJM4KuClvpWKLp/QiJqTkbsBSkJcCBkbp3KWe -HgeZYLnylxXfFkDxUkkGZlZJQ5edhchZy/3lMD+oFcR30ifdP5wmSk0NWLdKOFMH4WvBSsqnSTtR -SUIcotQJNCkNAbv+JTaCPRsx+JJLzhXiyQb5ujh6Zajc0SqktIozbbIPYl0FZljQZSp2yondAGVA -D6X0P2NZTECHpjINGfWDr50mmVbApJhxgBxi5FYagZKCGWzGVJQSgCnhDAQykhUPOB4xVDPDrOu8 -20L5WNdZiE3LJoMf+iQqSZXsKFZaGk+QOZjyzZQQN5vbk1RRsQsT+w8B+S/lqog7Blh2KbEUcH91 -tnQn9mCIFKpSLu5QVOxEIkVNDJRKs0bQrhMyUXmpkY7pfyspuMIuTOAiwNxqVYo7MabEKsTCCnVc -CjH4i12uAgcIcrIvE8bAKBHyVjd53nnQKXFmU5NXDeOUV1XKLwyqIU9xo2UeQlnW8bcqZSMsndxW -h/VLGMapgcHRLsMNwLAGps1SSLFK+VKBHWJHbgiyohyoQAB9KvAIvlO0wxCxqK1UbDHWsGHdqFTU -xJSe3XbQwMB7CVlMK6Y1heV6Hqlad8VhlxOU+p68vUGe0QnJmhTrhfXknCRQ52T/gP0VdwsxJbgO -ZZFZ/C3GM1kCy3GC9NiBTnlBkZQTFA5L5s2ykfAGXGAoP2uZJZwsJH5Sq8TUDd7qxJKBnQNYMgXm -TcQOoBCoiCUT42f/m9ri0Yc5kytxC5odgpBbkXcrom+jCEOoxzBS00qUWilYO7lrJYzDqGgryW2j -zcPoeCvRH8oetPMSBd0JhHOMIeYdcQKsEjfi6SyZPLc2jI0uEBaFNBJzyiGcOVCJLkuAtAwgfVZi -ZkqNjVTmKnVyfILbQ+72pcrDvwDxUjHqsqirYngpKFjWCTuHXLX2a9l6gYfd9lbU0IZEhmGcYeip -FZG1Y702FNmKTIdi3jYs3YrOh+H+MkVrZnbEkkqCIDCNS6d6dQpCpJNaXUnWUPB4Yw8QRGsUWq0p -sV0qYSsV1XRCGVhy11sZcCWqlXYOrJVda2PsWrlA4p+MBcEKXNpZMFUoHSH/JOiz/w== - - - Te0/jxkQkBnCcN/kP8855HVtvYXsbCR8QJFqlbKwsSUGlFJ1HQSk1DhjqFLNmUZKSciAaN6mfCkG -EZxEDKQOWFENHF2WiaoimzgNmREMdEJnD/U/xOn5igo8Y88hmCwDGe+95lKrNDaOf4TyzOJ1XAL6 -xx58noMYwrqoOI72da4wJVliIFbVlim8wFAPgeI/COtgts6SBbx4mzSzphzZBLX6fMp6z/W8K0cq -QqbPTjjeqs5QBP5SGqNOsXYO5/NHlbP4nAEHxyyrkbhiU2c8E1GegOyghhorK/RO1SU/lPAI0Ben -0IHaEyQSQy0DxnyIP8i7C/BASvWEuj/yeDRJBw7RQZxuSucB9KYZRpKkSS6RHSQUXNxmwdU4Sfmt -3GYra9rOxA7jeFvZ4zZGehjX3c6itzHzQxj/diGhTZoYLnm0iCmtAs0Q6adVVGoTqYYIXz6706Iw -xTSsmoGa6wINIqyEBiHUpQKXXzYZNSJdBNb/Rnx/B44nO/8OOcstp37YFWm9T603b8g1bb/Trbd/ -CKpoxyttGGgYumrHba1YcBjKLMXrVIc80SVnLUWlnxa0zbpRYLOMyyhKSSFzpa4DWRSZjEh1pVNi -W64Igpx+naYZC13ilGEMpcRvRXaGA5ckWACm5sm8AdEbyWAAWUCJl66q5EEMOTwpQhpMJEUpkc3M -Z1Vc5S31wPQywqVKEgCDNJYCdZCWk5hFACqT5fAEzARww+vFZxTKncqXU4wRKPfq9DXQKYkTFWc6 -IyArcqEoKPMx4IXGQJfCVglekSIH4VbWi+PBK5dqgwG/wN8PtRIWUDpPy6f8KvD9SnSrKTskxEMT -XuOkV1NyGC2HHgV6LAELhwfBFJN11mPOmILVLeXrYGb2HMaTCq0EWVQwu3LqJpe0sFC+L0tu5UX8 -i3DhYwCHkc0GgJy5AKIDWM6r8mwLmDFNYnvEdR/z3okvYPLr5kyhIJHYLPdYRCxse+bclFDZgu1s -dWlHYCAdeWVz9tkpwddstLWOclpMoHDPEdG2rlQHBIw5VEOzZrwGVYZR+APCzUnGaiToON0QocuS -yCno40wyO9lUhhQ8sFllB66YFAnlfApAQysaJbMpau2E1DBVKRENOFwSLcZEwixHgXGqYFkhL6MK -KIBKH1UpuhL8rDnxTklBb0w2PT2+pFxmaQySlqgkzpWAUUKyBJSc+mDM5IxscC9V3YHxnoeQUmrV -qaCrVPwFpquII6rIXpEmgX6n1Jj9tmD/NT+LLbOQtYoTvXHiMP6+FxcRkGy4uJXEQRD7xeFzwJBw -YjFFCZKT7BoodsHrlB4CPCIqygjEuHGtMbDRnlZBK9lKSWgCtyn1ICWADeGLNAvmXyB9I2fQgAFb -yUKG5qAJFKq5kCvk/qlTuZu6nLKra7aC/lvSATqmTsZMSkIUjFeZle8rsl9C1J7nsqM2pYd0JGMS -UGpK+dykb9gZD+GKDUno6iG5JKWuAKTp8pKyNM9tZlLJ+jpVEbh6FIHTvUgxUwWLEGgZC1eXjgR7 -uuQKkQD5Cp0ApYCUDZK3qp0Xb2PcW1n8Vnlgm+Lf3TbEv4uqsAjCCCQBtAaB3gyq1mUh7i0g+mId -8O7YzGGFFU3+TCGFbsBQUww2Rt6g5gCuT4rBNqXmKBHtUqoWdhfxRcpwi2k2AneAWtYp6SFy0twY -TcnTqWcvEYZCyiXHBT2OD2JgfUamCgvJeNQcVv1lMRINzGsqZRMBZxwi+46szosCN4WpMvj0IByV -iwnuqGAWHFf2KwLxz3rxW+GKJYNPrAdTScbUinPiLQo8/sH9SArTwcYEDN6X2fASOyvHiPmrpN/g -YsXSSYJjoGE98nXbF8TKVy5VwEOSkDi9YBLDbXx+oGsNDGVcBhm0ZJ6qggSmrDUWT0MP6lJyEmrk -tUI0nQSSbMsqyfUKPfsUFq8KVKPKMIg1ZSotj0t8bMTpZdYzA5mVz9Y/B6KKsxZIghXuUgK0821l -vqwxibyDgd1eIFGUL7DC/+AagwRaZhioueaOYskXB3ezeSqKogyDh3ygn2mWhzeAZpr4cYKiATnt -fEU+CYsC15YyRFapHAXYcxXlhTVkVyEgOyvRjUthxlXKyWpSiXcI16skJxnrCqG2FU0MYHU5zgZc -17l0LR0dQgbUq5UKPoAJbJXltnF0ByBfgtZSGKoqJWOcDMFmeejyIeRt0cKVMv+yD7w8cIIiSY1T -fU9zqRpFc2h1Y55HKiJltOH2mExu4IkVKZJpfpIovSI3otYdnc2SEPGKghsdaCDSZjuN0zQu6caw -1p1jFxrJrgaZgbVlN7qi5k4d1s1lMQUdLAnImYGNryWtdIaAS6wDTY2oIxicapZxDQoTyAqW4EFJ -KG9WBdBIGKvhlBNp9fiAAMdbpirejfaKjSnAuxBlg5yq4kswsHRTo6MAjJi17CuMZru07uIYyZ0S -kGMtm4/HNGgIzBJG1puNXshcTbF1O/NnZannwZvDiGul4/SHMGDK4WjqlOLZLHxWRyJ7mk+yJKwh -C9mQ2r6ueMd77PMSG9CDkwjqrHHLLgws5NTGcWD/VDCTeL0hynGYvIhpWPSwzA9Vczn7FqMG4xRT -NUVJ2jEcnp1jzOtdtoBlt5vHeGA6TBoGsL0iHTXLWiolCFEUvdvELuBmTFHPhszVDKSSV0ICpqQH -Y6uQUYEJcgsoq340qal+3ABGjEJlELh4NUMPPmnz2V6KMQ6CffMx1I15FtOp50px+1Q9FpVePGDN -RlcHnnKBx4CmxIGxZRofyLmhGftKDvDBFa6xbyElGiBPDCjvFwVeOdJdVVS3YrpvUyK8gpRwqaJw -CBXbdcUJ3lGOJwZKfVrU1Uv6oawUX95z84m8iaYiH4YEj1Km6es87QIML+WUKaj+jAw5PdApXw5O -JYfjAFsGgg+c7ju/kJXa1yUWNYnoA/B6jGXKX9XopATP19bNSYKBAvW5Z6UQKv5o31RydwcTNGdt -UZC9SLOmqOTcl+ARFwrWh2QZxrPGOvllgE+dqqqBHmQM3DL1II110mI1uq2jFDEjfd2QzdLgYJtB -p6VbTqsB6jUvWWo5NMbXPsDKS1AJqOez2AvwIiQ0oZLXiwIPDklSo0yKbpWWRe7lrZwUCITcM5xP -S0FOb5FwkxOiFVYVkvWkSCsrWBvy+hRFlos2VJ4TP4jREAJTiJECU1QVJKhKQqICWWO4Z9DmJ19K -ZSTYii9l3i1o21GtJC25B6iBbPhx6G1JQI4yksdNoCO/LzQDs4p1ij1NOGuRxDS5tJJSsTcDqrzK -bJEqOIKWnytrFSEV/8lOE6ZMYzVnlodKFamAQUlZUKcFzkIUeGyUKXCH5W65Q9wyBO4Bq5Glnlmc -R8m2lJVABSUpOzkHB4T+sHdIRYVJuIcaXpMuBblImOxwknNYc64ZwjQnbVCpRP0oBXtVnfO+pkUN -YKhdyCPcaNsn/dT7DkB2o4pAxtwgY7iUsFhBDlzPiZ7ZuQTSI1N0C8C4+IWywsFhWvdkf4IItSAZ -jzEN1rTcDPFxseREk+DMeHq2IiY4RwpIP/zQQhSx4okegVJpyubF17MeWIyiu6wqxXOR7DYQ+ULC -L086IYO6Le7stMClec11KJ+KaGSLlCq/9q1yjcAMmSyaiIrZgAkJ9QkDZ00XqYogwxMOdc2z2d+J -SGyDBCbxDFBShOWqhsSGMRuOuWPh2htA5I6bwIbEhhFEoWpytejDYHXGSE6g/ScEibPClsku5Cvh -dbHnaek5TlFlYx7omSUKW6XERw2pxlbiUWecRCTZUkrbSVsCSmRmswNNBbET/0xtOYzGcLxSc6w5 -G99YHE6xFmGckxqY70CV3MC8T+Wp+lYXko9IaiwsE0dALnwlIljrDifGAwwFvHWBQiAXRf3vWGQL -KYkU2AQ4SghyMYvHMLOt4OIbaisKWI89B6KVnELTGClibDEKT2DEJEMgmqqdEI0kSgKHEvYPQ1ZN -c3pUiSsGC6mjUJ6SonaSEYR10nC8WLloUunoLBgOAns4nEjlgUqoJ0hJXcAXeVrgljSUYHlIvC1s -JHEJtqhLv7gkuwZyumtd9nxHWL9tueTLosCxrlMNnxY46+4hhE2MbpaSxCLQanEBLci5wXLpiLRM -ig6ideLbA6Yih0jPukQeG09Cp4qp/hFTBy0DYwt0fawaz5Jx2dzHIxuXpYwt0/0Hw6ZQMzSIo6AE -8WdsKyZGnV1rsrrOpg6Nq6hK0wTasZjdsRykSkDFlm2TOyuD+y/ZZcE7lasSmiKFu9VZ7QzXm6cg -ujreJoODpT6JKyaV2LV1xcRsGHWWNgMOJo4N7MHYrGeW4CEJok2Fc1gTDFIcB7JCCC25XDvK6MhO -aIG2gXKnccF0rLyD+mkQrtkAUI/LYSnYenWYuQKdnaNcO0YLb89tCeap5rUrye2cOyiljiLE/IBe -cFo2LhLbktqjW9QEWwRR0nS1uxfkLSWui9SGbgCrCJx7TucH/MzYvIOCJKU8gAGRhJEBNTl59iM8 -gafjqqjSVb6alQiNTSDjMUg5WlsnsjGj7Ds4ZJ2KzmV9sJNVczEUxeEmd0nL4kByz896VSnbezZc -Oijp+5Gj5u9nonXWBxeSbhCP/ATWUysa1ueEfyDCA4hCWsygJLIXPS8TrsH9oaBWDrnPkFjAqP8m -UgrEuA3gMK42nTpmRG0DavIZDEYwx7cUA7ETHOsH0q0W33tQoZccICKc/iCarylAoohY8rQwNU3m -yWNlT+WyE5ZKdUiGPNGuIDMqKazBadRKWRbkqGflXoncnRXwUoSXkCcXp0BIEV2KywJW305IR0rJ -eMqH3EAPUKK2YEyNVbm5AELmbQ/ZySqpeKG5jmeNwSF/N4uQ9bELRQNrpGOerOs1vyBm4/q8EM2B -rN61O3hNn3yd8xpSl5Iwjtn4+XANbNIGlPXZLeW7sDgIR11SmobgnOFw7Gd6SP/bYceD+mKJUUBl -5OIgXOojgcExcRApCthJNR3ryOI31dKDFLDywspmPAj4p5LCpMnEiNU1ZywavSo2GA1OIt27qqQS -0zUntShwLqkDqXHBG3xa4OIOx/AJDJqyyRcuLQYwHJTSwOUx4IrC1JE3EDUceF2XuKeZk3CJ1Zx1 -c9QEZy8o66mgxQS5pztiDOpxlU7UoNCtS5l6SieXRjDqBMbZiDNdkdzxIIqcgr0hbXCdfxWy4ZOn -YfMQApwU3aD45HJP6CLvGLEbTlQMsT7EIkvPaX3iacmfmHpmLUI2Zsgywvq1esxx4bW2KmvJPWtJ -zNikI5UWI17GfUGNuELoiHBf2aFg/m1KDgU7I4DLu0pVwQFOojqkY+Cq4NAJoVhbF2saPIbphOog -miDH7lmpXghX0xQj3gTGE1nO4pQBg2ij0P6TauNozOavMzj3wMnXq2Se1T5l1i5zV2SNJXV1xstx -bRIsCVBSJyxoaziMwv5gqqkJzNbCUUJoAS6z1Kd8o6BxoJWLQN5YGcUE5puwhL0hbQ== - - - fF1CUafUsq526wagdpzfV5yENQbzYEtLZS+5Byi7Kvl9JUsP5MFg9tnW+ZKMZAqCPPjgzj/bPwuu -VjstcEYNkE+azwUsBZ0LV5fOgiQ5dIKkhylZYFaSYomPQuLGuGYd5s3XDJPkxY7yd6cO2PPeOQmU -a8CkuG4DaFydyQkUXOhQIHNunoc6njM7k44yJ9XHjyqnkdNCVvBGjiu4TyTnCI2VBUuG88VpAKXu -RwOImzQlPVS6YnhZI3W4SpxJHXS4vebgDCUoaL2KG6D/GrK3Os7MjiXAF9NdMV5lxy/doYKSlUj7 -6SH9bF/dH78tdX8wxwmlgg6YECqHT/fB0QElSRFhCCj7NrpB3rERvgYndP9uYi0rrhmrSJEch3ln -XKID1924snrT/Ozq/PLSzMqx3vUA2zeBubC9U6/qXXfn6sr80r29ffv33zg7e3TxjuXVGWj8qt61 -0PQG+CfJQeyLzPnwtvcRG3cBVTR3sF2T6wSor22SMHyVqbWFCqCJMIiRQArlAZvjuAwjImtCc5C8 -nEg5aMvZj6E2TdZVpAGxEfJEkSPFr8F9FhU6s9rgkuBS2VUxgpcUEoBA9M6n74P4ZsRFWRzWrU0q -cZMkJJsSI4O1BhRUyfu6qEcmxZMLsTrjcAsh31JU0aUAK8itxg5TIbWEnBOkPQGHWI4lKLVEtIW6 -zhKjFnIpA+GEC+ZFcmUIjwXJa1ukGmlZ1XCMj6WBhiSIAeJml7iQfE4gkpxdHDyFFidqyRZ9cBN3 -yRYjy+oTigfDX/KU94XK8sFJ4yCav7gCjKqwgqmX8PE67sLUmtuyFN8U8VScIFsTcVYwBPYQKHSy -2mgKz6E1hJqZquBAAi7rBKnkpSyvoqKDBBQrU0GZxWrrGu94XacIYrdJaweGR9YeQzK8spISJOC9 -OysycWaCFX97l9inWlgH/TTVwILyDxB/klT8HP4BFFviLp04LmABEK7qnoJVgAmA2B6+TmVSjjlK -30h3jKUJLHlSSdUvzrMvAX58o0sJJMICHez4HORAOV9XzeJtKJRJEW4hnSWdytWjwCwGfSnuAPZA -I/UPctdcL+KQrzX+3lO4KAKd+F0H8V+D6JM6wzCc7kIcDWQRoVvy4wV3CS4G7XnpyAOiNmSDWoWj -LMoUCYQFp8hfp8704FM9NTCzVdkYCJTiw52oY6VAAgABs8hZhjLcvAFQeFeOsmfBpnZTx2BKDj40 -wr16Ted0SnpgHgiWK6TJFlw7u0xYVYpzk+2wtMm93KVYgCqVnaaLLI25Eg2WHBZTZenrWEeXvEhq -Z1+sFS1RU2JbxdWu2LCe6QSlaj3C04JD5kgr3lScsc0bsSYCEIRMXklw5SrZNi42V8+p6ciOzz4n -vspcrNDsMiUEMnDtDZPOPVC6kFyWOcOCx6qPbGVWtVkL9oKX3dbhhkbkIWjMwTY+WdawElvC8PCQ -UiodS1oWtCyLF7Hk1gAgKR5hNKADTD2I64EFdYqcnVJKJaeI2ooMiwSs68EDnBJrYh00CQaI68Sd -pshXaCgKx7poDIILYSlC/TAmnDaZLhFoeViSKfP128qltTJD2/wIA+VK2X0IS9KnIBSmXlhFrJCz -x25UQDYkDJpvO3j+cNDjNg5x00x0VWZoCJOicqCTTUF/EFyKe9nC6L9ruzlukJFN8tL3KRBCQR4b -I/4lrGkrlDhFes5WvL3jWUsKMwA9cOvy0u2xo9XY18QEw1E6yz/ZvevWw/iZKujDO9/6htfPL8Su -du+6Lr2HQV539y3Tty4fnIP3+w4hOBvrA4sLS/HDCfgHhD0cbPvH75pZOEqfq951Ny+tNj9dPXaY -PozLtDJzbPDJc3cdXbnn6MLc0uzcCXr+DXWL2fvmFw6uzNFeXNfcxcYwqZvr3rI0PxtBa45y32o+ -oTW+UQ9cb+vAs2bxEK3M33N0de4INoyf1N3m07tn5sjc61fmvvFoHPWxUecJBuEOzrBvLv1TXTq6 -eNvs6sy74AujzVN3cJL5LPpneGR1fnX2vrvmF0af4tLynfilDs60MZv+qa7MHTm6sLqRi9nBGcok -rl27Xf/cD81NLS8eXj4yv9plxLl8eG5lZnV5ZdQ9ml/q4A7Vk+jfhPWGW0/szuWjK7Nzb1iZOXzf -/GwH5zi/1Da7dXBf5+8WzmD9i7WZk/3AyHRybwcX5oHB3R6d8HdxQi2knrjaONDVkQnh8j3fMDe7 -un/56NLB2Gr/8jqbfFJm2pjWwJU9uB4x2HfjzQduXDh838wB1cHJwfj753T//MHVdViTenaq6OTx -5Dn0T+2+ufl77xuZgeno3GQS6+DZ626aO9S7YSyHnig59NDKTBQRFm5dnj9y2kuiXURl2y6IVh2c -5FgQHQuiY0F0LIiOBdGxIDoWRMeCaH1lNyCIdpGxGQuip97cTg9B9A0zR48cmZ9Z2r9w9ESNYDNk -58jqwZvm3jXPtuxTV4JpzmNrjP09tGVdm2LG2G+JaURs3cH5Mcu4MaHltkOHjsytdviCbezoLeN0 -9p+uB7CjN6tNWDk4MkPeRXR4sIUhPzgyR97JGR3bBHa48/Dc7NGFmZVp4CjiPE4OK3D78vzS6jRz -Zl3FUyMf9wlTFEUHD8hWRNCJyAp3clItYui7R56U7uik3n28dChHjq4cmpmdu3N2ZmFk+0oXnd+a -89gaM3skosDbjq7TfMxOHG924sjqsdHP5AKTq4nZ5YXllevvv4+U8l2bJ89pYKpMdF/3wOHlpbml -DagjujjHgckMm+7U8tKR1ZkNTLfDs63nsmGWa2xFOulWpDGB6P7ctmgf67omZyQz2bMAc8yszK/e -tzi32kkb7XZhkIX51dtn5tejfKcoCjnNDe1dxpB6cGbvXIdryqzKHZwSjH5gRiNvVRcZxne27VF5 -Ss+obJnROgrRjp+6ahPE+Ja5lXtPFCFuezT83WE+YHT60WmLzqZMbqfX/nSYeRltd7bhSaP3skmT -ge6ko8mWLAbdnFKLvWA0F679c++aW7jzvpmDy/efphFFVSc3bHscubo5t9PDkeuW5ZXD9y0vLN97 -rMP0bqN6iYPzCzOdtClsl06ii2qxocqIg6fyrNow82nmktY8hzMH54+O7AGuJruYeETmsHHef4wM -T0Fk2MVQmq0jwy7OamvIcIwCTwEUeJp5HN/TRWlvG5BDF6e1NezQSXzX5jk9uhLltHE0HqedOLGb -tK1pJ7pInrY97UQX/VzHaSc2Q7C7eFy3gWB3cVpbJNid5EE2Y/K7af7I4YWZ2bnFuaXVW2YOd5jG -bfAureOqcKrepS5O6/S9S5v1veomymtzuzqykYiaLh6+I+2hNMem7ptZWppbuHNuYW52AxrDGzs4 -xcG59M/2gU3O9o4OznZwLhsmalMQXHPLTGz3QIcJ2kaEtkWZTNd2q11c2yCt7qKyYBtodRentUVa -3UUK0EarcSAji5dS57Ho9QbeqsbbDk5fprphLHkS8tg0RjCzNL840+mYkEPzCwujq9Xm5t7dRYUa -TaJ/+++B4jMj3491btLJ0aPRDPondnA9d9xMcdbFaeH4+ye1unwqKwNh9P0zSq1uxaGPGNIyckqr -kzLPvjn1TzkKKrO3LB8cebYL80tzM130La8nMkBUVpYXT+UQERr/wFmFsmBH1/ckyfXVS10kBflE -BiZ58OD86vy7Rp7iyhzqDbs4yzSTFrFkdWZldE/DhftnjnWRRqR5rM/zbYbt2UzmwC7e5m3NHNhF -wroNMmoXp7VFGbWTYvc4A+K+2S7iiK1foU5Oa+yP1JzRhO/glMb+SNc+y/yRnh0FWTuZ7mvskbQZ -kt1FO/Y2kOwuTmtrJLubXMjYIynboS7azrbhLnVxWqfvXdqsR1I3Ud6WPZK6yNaPPZLGHkljj6ST -tlvb4ZE020Ud4jbQ6i5Oa4u0upPsx/Z7JKmxR1Jn0OTYPtZyD7uo2doGjNnFaW0RY3aSCGxGUzDO -ZLlNmRK7eco3fSROm33ppnV+vC8dzuayPe5Ax8GpaANVyPZ2cHm3lFG0m0kcx4Wwa2ZwHRXczQem -lpcX9nfVxX/rKVRVJ0/oOIXqOIXqNhD2Z0HWwNO76snGstJ1USM4ek66TXGuI5Gwm/BYH+ji8jAB -GyPCMSIcI8JtQ4S2g7PqECLs4vKMEWEnEeHcyvJ6cZtjPNhZPNhFjqcLePB1cKrH/OAYDY7R4LMB -DXaR3+kOGuzi6pz6aPA09ujpohvFgEfPSbtU+cYf6OJSje9Wh++W6+CB6ejd6uJSnep36/TKTLcy -t7i8Xl6fDmWm25ijtOqpG3TRUyb+W/Ti/zfE9/H3DfGDXgfnnPlGj/Px9e9lJ+fVmpBvU+nrCKHd -sd7hPh1y1x05DNnrOjjP4bnrxlneWmnHOMvbyZphnuWtb4KH52ZWbxodsc4vHZw7NL8030lbbjab -9ZnqsQjUKRFoKwn1FtY7v6doyFBHJ/YsKOa8Mdwxtbx4ePnIfKfFvI0aaNbb0I5ZZzYYYigbdtvR -db7TLcwxerhPRzHH1jKPdN+wpsfYgzjhlfnV+xbnVju5TccDi3QxKHvraKTjeHJb0El3OZGWjEbv -XOecZYq9Dk4JRj8wo5G3q4tX7J1te7SOG1THZ1S2zGgdq2vHT121CaI8TgiwXQkBOk4kux7sPI6j -bZ3jjTcfuH3+gbmF2xdmjh3o5LlqUZKMaLXJSjAVPdNFlJpNZJ27MzbOj43z22Gcx5sA5nld3NDV -WzE2yI8N8l2c5TYb5E/BYnJjg3wrvRgb5E/WDMcG+bFBvltbNTbIn7oTGxvkBwp/HDp09MjcNORF -itMYy5Zj2XKNyR2bW1hYvv+Ge1fm5pZuiFd87oZIVOfvXb7hXfPLC3OrN6zMHbxheWVm6d4uLsBY -6FxD6Dx1yrJvSuhcYPw2MQs1ITo41bHkOZY8W6nJWPI8WTMcS54jcI9xiZZWpzk1aFf5t7mF+JyN -iJ22i0rzbBYDaOXd84tHN5B51ndxfmkS6x/DTakfjq4ciqj0zo1UOutiKvjmPLamfjhI0l8HZ7l1 -DUR357bFOuEd1ay0oKWNcN39QmYHp9g/nfa7tyEE00V/jMY0BmRKulVTy0tI+U/leQ5MZcPsz52H -52Yjo78y1p6NtWejiQGgKyPtGavSUIk21p6NtWfHAb+NtWdj7dlYezbWno21Z8dzmqe99mx0kXth -/RJBnRG4xzrBPh5mrBMcldnr4Dptp1LwCEu13YxkfVZF/MtWvO6Bw5Fb24C6pZNndGAyw6a7Ye1S -F1HX4GT6p7s59ejK3Dr0uCu60Y0xQ6djbofTOjPM6U4muj2/Z0VChzEGeVZkh1mYX719Zn49cn+K -opHuZ2LaEiLpsJV9a1lhumix3FpWmC4yyVvLCtPJGY2zwoyJ8WlAjLuI/7ZOjTvMamyREHddXBmn -aOs+ezFO0db9UzdO0bbtfMDGyUcXz/o4Pdupn55tHfLRGeP5OD1bt9KzndY5QLp4KQ== - - - ns05QJ7N6TJuO3ToyFyXnbU2dvSWcTqAM1bmDnZ1o55VHh8H1+Fx6kl1sb4vjH5gRsdO6RkdG8tb -242jRr+4pwR+2hQhOb326RQxco5l41NONr7zvpmDy/d3OW/5WGjsotBoO3hgxkLjWGjs5AUbC42n -zg3bmtDoOzijrQmNnZzRWGgcC41joXEsNB5XoXFkpD+hzN4Orm8L2h8Z63d0SscGpzQW7dcR7bto -3muzed8/f3AD0dRV0cXzyXPon9p9c+sH7mdzKzs5N5nEqa2Suevoyj1HF+aWZk8KKzhOJnaikGdL -MrENJJw6dfJNrS6P7G052cViLzD+/jltKofWPTNH5l6/MveNR+PdXofNGafQOo7zHJ5C69DK8uLo -h7WLGRZoBgPHdZwarI02jFODnawZjlODbY8CgMY92kodWpmZXZ1ZuHV5vpPBunW3jdyio9PMrqPm -vqn0z3Tp6OJtcX/eNbpHdSdz3uTT6J/ikdX51dn77ppfGH2OS8t34pc6ONPGbFoQ9QbsabNd1IBs -Pf9+J6e1tdT7s510Q9mMJeF0DM8+xbItbeDYdfImjRMQbVaBNbYHDM4R7AGrM91MrXDqO/qdcK3y -8RZnVvMJde28bKswUzwrpJkuevGMhZlNCDNwMTs4w0ycGTPKpy2j3NHDN2aVN3euR3Zw6qQ/wlbc -mzo5obFz0waEmZrjPrBOBqXO6ME25uBUdPKMbo+DUzfnNnZwOmEc1diy1i6MdjFCf9tl0S5OciyL -jmXRsSw6lkXHsuhYFh3LomNZdLOyaBeNCWNZ9NSb2+khi57G+U+6KMQ8m/OfjOw219GJbc1x7vQI -9R6ndTnpG7X1+7Usu9a1qW3thnUVbWwpW00Xidg4xSkiv9cvLC+vK3ycMrhvhAK8pyi66+rM2rDd -kdVjo5dhPwQHkKoNX3/PwszsO2/oEWj58Mzs/Oqx67tpreA5bvi+jXW/J133O2anWqZ5BJO4TJ1C -WGYDcRddndXWdPinxMncXAnom6ga7TQXoz85WqWb5o+sziytTrOar6vYevQ7vgAz6WQMUsv1nluI -Y9lQpuAuBtJms+if4My75xePbkA5XnZR558msf4V35TG9OjKoZnZuTtnZ0ZnKLu4TM15bI0h6W6l -7q1zId2d27NGq7PANHdiBK6pnuD993Uzd0f/bNqv3obwSxdjxxvT6J8jX6qp5SXkZ07leQ5MZcOc -5Z1cXnnMWm4/a9nBAzPmLMec5ZizHFUH1OnS88+qShCyFa974PDy0twGiHYXUdXgZIZN93TgUQbn -8qxjsccmkdPaJHK604luz2/s+f+swCAzK/Or9y3OdTOT0nZhkoX51dtn5tej96coGjlFnCU3i0g6 -rKtlJNKY2TvX4RW7zVTC6AdmNPJWdVEoeGfbHq2TYqHjMypbZrSO71LHZ1SNifGYGHdgilsnxh2e -3BbpcNellS3R4k5ixS3R4k5yF1uixZ2c0ZgWj2vRrssGjGvRnm771H0yvz22zI3t8ThX14nd6m3M -1fUsyRvdSbvlOFnXJqTIu7qc0X27lLqnFQeAQT+3zMR2D5wm5GFRJtO1/WknDBu7YG+I4zmyvtvS -KXq/Oo0+2jQ1OJDR6Vyv4P/a3iVIB+cu89wwehkn5zllk/N0XN4dU/OW63aaZbO57VRQvGz9IHZ8 -gltL/zLRRTy5tfwvE12UyjeVAOZ0NNee1t6XsGGALu7qZmLirePCLgs4W7TVnhLUbHM+mKcxm686 -WYl8Oxn9HKd0MUHE1pFKx7Hm5os/Q0DNXSszS0cOnazsu68/ujR7R4ev/EY0h6sz93SS7rQrDnG0 -b92QAkr3umhpaUxkw/cADuAbxgfw1DiAk65X9Lo4wS0fwf3jI3iKHEF1OiDB4697A/7i7shWvH5+ -pYvn4VkhaY6VOae9MoeMEKftJes4FtmWcNouB/ltRpkz9hveJn/Uzt/vbfVK3QIixG2/cWGhg0s0 -ep3HzSzT6FY01ckKUlspSdfRKY2L0hGWG6ko3VcvLx+8d2Wmk1za1ivSmU4e0O2pSNfNuW2wIt3e -G29WxYHXLR1MlekQZgB04NblpdvjtDA70QTD98/dO7+Uf7J7162HpR/88M5ji/csL8DpXpm5Z+7I -Nx6d6030InBxLo4hSouv2l30bty9q+jdff/uXUd377r6KDAbNwHktvjPpFGl07ZXTGqvS1OCf99k -4SoDv41WxgZ447Sp4I0uyqB17+4Z+L64At59DP56U3z3DRF2f6/q3dJ7+zuK3sH46Lvv2L1rwtkQ -JoNTuuerYCaN1aa3CHBXFJOqtAwHMMFKrX3WloCVChUBK2V7s/R9M1kUDtp6H8GFhrZKTxaqKAFY -THpXAkzHrmIbgFWTvig8d6D9pDeFAriZdCZ4aFyqOALrAGjjx65CYDVpC1vxo0J81BT2UOrJ4LHn -oCa9LwL34EoC2kkV3zKwUs4AMEwa42USTThPGMZQVhEYR4cb0fsaaSxwB0tCjysnbYBlMHHulfX0 -ODcZQuEAGKdWxQHT4yodFwV7UCE2pgGb+BAVAKjjllQVjsGEuJCwa0a5ybjxlqeM8CqHtwJpwDYO -2Dochp00sFkAdJO6DCUAy8lQGdwjF9dBO+ihCHEM6XHOT5aFwR7UpPIWxxa3q1S0DnHRSoM7H4q4 -DhWsQ1HRftOUI9wVpRK46gkMZwywugOnUgc6jnAqdaBxJfoaa28Gei1NywjiMVBwpGi4Cibsi3JS -aWMaE/Mq3r9CV/2r4FU1qULcrHzJfNyseCZNY3G91nBDTLYTOAav4yWMt62xx7708Sha3TgNvion -qxLXBo9OnBqNoQ+O160F+DXU2MWl0KFxKOOkJotAe5yObw7ks06PA7gqdXYxpqnnELdZhcbtitdy -sqrKqnEPfRXvrza6/9JCz6qAlc9ueFyWSeMr08AF3pjYmRpAHN7G1VLe5EjGOxsPMI6rxkbxy3yJ -GpgLwKqwoYHlABjPnMnRIcAqE0zWMHVg8fAgnFtGVK4IUpbydFdUlmARm/OBxl4tIs5QTkZ84nhp -EV4EgmvvlAyhKioGxrsowCJoAipfysSci3gy4CLE0+e969FqaWtgGPEyx4tE224jpaHNUZMBjhD1 -EPchmALRb0RVKuDaxj2PO2JpH5T1tL1xc+J20zYWcKtodvFQ082snE4bAYfeBOg2oqSqLC1ewdhD -RIkAjC19vHd8X8NkxDGe4CHw5QYSoOFxNtKdQFg9oYx4xKkHQhlwsLSzzcawaUb3devi/hmnBsYQ -h6mdMo0BA/Z2hbaNqUWME0+uNv3rAMTCl3ie6kUDklc5PFD18gJ9LA0ex8ZeAC1VtmxuXESlcWqF -b2wxUOhC4xga5wFJP5Kh7PDk/EA6Zgi0usrO5JT0wLspZ3g6wdPZpvMOsLinwmj0WlmPOK5DwLHc -RFhR8z4BCtU+rt0i4dtIz6xB9FXJBY/vQig1IezA2Cu+jf8jGfBA6gTHxKUzRVkRXBUKzxvg7gJQ -BOA/E1eSkWXE1N6WSDdMbFFUjMiFRMV3dA/i7pUqeMKJcfP4sMRtniydoI5IA/HIl3EOnoAaMAve -mWoynk28MyEOt5Q7E1ElDh7bWqYOEYfHheVeveNbF2+4dogjfFzbSigUwn2VwaeHwBda4L6QJ7rC -IqmOeLXMiQmD4zgNLWa8/vHPkrajsBYXrQJOxijaN1WEtECWsbvBtXaEEyIW10jvVUQZBSOKgRNB -JwZuX9zJCqfu4lEoSj4sLqIyHTwjgcIjw2jjVcTRVbaKe6PorscxKa8JGE974HsGRETRXY+Titw2 -8ziVQhQf+aU4LLypkbQwlXIFoCNT4xtnYd6AGOK9UMIlVRVcvohkIgJAWByNR547ouoQibMMAVC/ -qgiJVPEUYOMy4kHkeoNLGCQenLiLuBllRF3ayRAC8Eye4MCcc+NS+Yo4oiLKDwwsfGA2CdEKjyEe -TqP5zujI4PVobTQxEgXwmCWiGxeRTEmcmo0LGWQMsC8eOcvYs2XO28GZcYEuEnNqcfJxkwNdpKp0 -gvEi3HtT0ozLuBjcOJRO7kE8an3AKuJtV9Y9CLyIWIIfF0dZGMaZXroFzGKZ44g4WjB/3EGtkOBG -FI2XHM5TXEhdMrlEagrAeCA1ykXIJ8T9Z9rhIs0htiWSHBVwDPFoET6Bbh0x/hGmvRW66uI6cgeV -sBdwGgLRGaBkHqWlyGNZm0ZgnGP+BngH7iDCfeJ7InLjxpFwCNNSkLgFwBIvCgBrphvOt0dSF3dC -64J5FIDHpdK8Q7Q4DhBAFRhWKM9A5QMDI05Iy6tYdohwblcW2hE/6QjR4pdtqQhoq8LWKxsPGNMz -EQXiXY/UQNPuJiEnbjld37i7tnSu7kFVInNGAhZYIgKOSPahlJWplFIELJyWE2ZBftT4uEiKQ2F5 -wNogZYBuCQfFW1J4RLFAqas0BAeYBTmZUNBBIWC8k8z7VZrXpgYCU1x/X6CW8CABo9DpCZ9HaUf3 -5Eki3OYjiOcLuTG4ktrQisXhElcKCERYE9ytwDe9hMmm40FsE8CVojFYIIoV01HDuAIOWAVrC8Ao -aQiuiGsush6y+3S8bBSmi9LQIJTGHkxEuny8IoMhEi/wMyKGoIBDUwO4diqDA7CI7DLqFICLrJJ4 -DGSXWLR4pQIoV4jJi3TAE42OvSiW5yNrrGgvlZZbUkXsqErGFZbYDBdlsihGQQcuEEPYSryErgH3 -EAmd66drwEhrhVez7gcobJRydOOJIAVGDNs/Ougg9M8kciSRMPfN2UdetSiRqcQFkm0GhkcXZb6a -04lvs/hA3JKC2CvHDC/tnRGWy6DYi/tsGEERHPUjsv8EjE+To+KNsGfxjFYk6sOxUo4l9Sg7hMYB -BKAqBFifVh/ZvWCJGyyFlkdCEtGNDo1LAEBSpDRvDIgqNpjm9QKgKZVrXMQcWN/ZHJquNz0MpZQa -EXjgY0srWAOkY16xgjBThmGQ+yUBqkZFsIqx1wG8BXBblaqB5ICp1j7YBjoEIHFxhDsrEbuVIbYg -R7TQbZRPQwMlw2C1pZnl+BsnoXHANbJHoCKxQbZWl4Tkc8oB8NIa1yAzADReeCSiRwBzFRE6pl1T -sgJIz2taNy3weCZUgzAisBLRn0koAp2w5DW99Qp4nJo2E6segXGLdYOKA1BluyAkH9bWaa1z9gD2 -oSxDk4+AIZRF4fuZDrgl8exXDQ7FR56txhbMy8CxM6bgk4Tj4jNaEfPLXJKm41gC5rDCT2nVB8yZ -rxyeODUAmsrIDhHzBc9SpvT9DCDck8IZlzOLMF5SLmVcJVwTHUzoZ0EjgxTnSTrAml+FQ+iI3tWc -bZS0ykoNssERHvGJb/LMgCtZdKy5a9hrT/iqwYrHeVqDWomMb0cBO7gmhx8/LyokbU1xIA444r+q -IToAZrbWNGWMOFjY8AGBBDpQuBO19OKjUGd12ZRzADeWChayKRTBgaw0YrxagvI6jg== - - - ptQNUauFfCWJLQ7ZOJJ2QkQk8YuLskC6skyvSmah4O4Fy9jUeFrhWiUMVDkJGvHgRkJWNdrCffKo -kcg6RQ0jTllGwLJORGDa8sLbyGKwdOcK0rfEeToVGkPAZQeheEqOSaQepq8xaBjwWGfdDq5CWqAy -MJea5GVaoEikg8YzWAvXYGhwuhQxPM68D0giOyufIj0oSHubqwPAkELaXryhdMfhaVFWZk2FjfNB -ICBuYqBzVQWwR4LqRK0BzE1hxJLE+g/gxBTZbEBZ4pNWK7JzTnkvihUSjuL9saZqaGCAQzSldpm6 -ZkqEG1hfus0R5WnmPRFD1oqgt1LjeNlKbxpao7gZsQkaBJJ6CYZli0KJ4qGUq2h0RAK+qaVwVfxa -ZXRDn9GymbTPV78ls/yRsi6iPiaOBZyMZI/j4weadRBZCBgJu8oaEzB4y1a6oJPcCFyGE407HqAJ -1CJGEdESYYhLgd1qS1wM0gCUx9gkV2vW4yo4ku9AkxlIN++SMgX4yHjM6Hwgmkwnz/BZiKscyJhV -xrdECgEVsxUpdht8GejoZgJWH5ymDMCiYG6tADYjmeQEDpI/6WnjKdeW1FBgINQ0CzC2eNlgk/B9 -JQwMGGHwqvCuI1sB5prIQ9OJjpKf85Z2PRSiz0c4aoATvBWYTHKhpLFF5A/2FjrqSC7x6FQmsOgV -QmWJktlkPQCLnEdlI5xoOZAeLSWojYtrVpHYE4AR0Jr56WS9QLhTzFAXhmkkAImgmkYPPvVQJYsc -gL0dbBvkDme9loiH+ocAx6BSMt4qMKdgAyqg6pkhg2eD718GYI7inS3zJQMrgy1pK9PagqwSj0DI -NoJ1klEo9Kq5xaC0VVrss3wYQGtbFNbLyYl8L+tcm3AyrAwCWYkKhtSibJxJMIVFkm4apzcH8lFP -djMTT0l2L6YFrk0oG5cLTDaFIs1/uoaoUQ6o1GncWXyiU6FxwT3gb4OMfY0KwGyEvEsf3gAbUyCG -pUYyYJIjdWCGjsB0RZeoibvQpBbvYgPRAdDbwjdQYqRacW9tlbXkHoDF1QIn1g+AtVXPVyYBExuP -7PqU9IDrjbKMNxGDTye49iXDgftmYCFWPLTT0oCDIw4Y3QLksMVLq5xjHVVkuZEfi4vGFiIHNjNi -l40lDQWy7BXoHNgwF/GqQk4vynos6IAllVjCyPyguo5sCaz0iftm4GqxNiDKAoF4TZ12Q4MZncxf -ceSVE8k4MtzJUGZlCOAcUiEWB3rNijYwC1lUgkam0hbkr5HwBvCJ2MOUsF1lMKbZGHZDF33dxrND -5vnmGAB9+hKVavWAUePiGhMD9iVybaF/FZASBbKQpiUDqhdl3ypfWzDKxcNU9u8DkFNHQnC9aZEb -B1Ncc3uRzvtQ9Z8FhCMhyg5OzhSkI4ZAHXx2HqekB0K16fxOJ3g613zY0aWHjVPpWrTwIH2GuZI3 -CrCYg2OTDHORFSzJ30DuuQLbHtITcDZgLBbfls6zNQFdApIQXrjCE9wq1hdUwN1rZunit1jz42N3 -5PkBh5qumC7ZdgBSV8lCJrgSecd6G1cIUinBW6NglFBZwtDxItiKNNM6SakV2GArZne9aCnhMkWp -mbdFMZGIaNSHwL2yCw7g9yh4lizRAlM5JXhfseaI4dND4AstcLJSAxAxMDppoHU2ERWBR0HH0mJW -sKMlu+wYMBkAULM1EsilTUqh0jGSB+62ZMcN8FUJmt1StCpDr/VE5Ia5yE6UJAqBSJ3sct5aRgIm -ED8b+XGDgwOxMjPFIN1CtFAB/kxWMTpDFdjzyiBmNSSf2G1En2LTYmIFpgPgbGuEwygWtLLEqUAP -pICP47WetLYRHZCmEu51HFySFBH3e0IkgY4QmvEIQ4Ksz1gkSoriFVWR0Jb8l8TDBrRtJK/AKfOG -FaAGtIAEtEZaIm6ZFXFXtPCerH8TZLUkhgK8mjRhwjhLh14S6OIDRq0psRlErpw5NnHjAvqsTUk3 -STg2MMFVogHNVBKwKkYMQj6wiSNubOTe+CaIzaAGgodg1oGA0ddMnhZ3RhGLUEqv8RBUOhCTEYLs -BMjfjvwNNd5xEvtQ74l8h1iP4rmwwbKKLNS2AQt6maQNK7UYirQT3K81mRziMfSMDeLRTIZ+gKsg -jJLohoCUVY7FZ1Tp0RgiJmadIjKSyTLnWK0oFrw4X2FaNFsbPUjvrAqp2W40MtEGxEtfiUUOPBiV -bEzlxKpmS82G1IigxU7n4uXjU5tOl1PiZRXBYrYBoCO504mYD98n0oeK2aqs58Q+NKFK4oAFkzjZ -Nn0u51R0d+NlyoyCYIAj06QHWkLqNbjepkzcjRVgYAeaCjU/s2KPUiqwBa8Sm5gCXwLWYwsCijck -MtdiC7YqGY6NdAyOjOCfRjckoh5m/ELF5s4aGM+OLZNVrYY7RIMEK9klxpNswo/yFWtGmkMg2xVq -RljJGHEj8aSAE2qDK1E7VH4WZXZA2Kcxwq2WG+JYCIQD5uSGRELMEqBVKlOtFJVjGQ4pC5vlQFdd -sAEF9fkk4So8YyCPVCL2RgQURBgJtT8VwgVTiEge+dd4NfiSoHNRssv5IBQ6DiUwk1eUpNICL032 -Fvbimhx305ZWFIRV5Gs96bhLsi5QD6jAQ0WcIaFzkHjldjlPDm45WUP/No+Xs+4FzHKOLfnpeSAK -KkIMjcEBA2L6ZwJ2OeuIVUlzBrucCT4tkGACYHcceafJak4L24bKdN4StorFNSrS5rEQBTYeLToA -bZP/H9p+UEsiJ4B4NpcUBiZy47VlLn4hyNkyYpSqKqIqcghBiLdOYPWBBS2+KQsvh5smDRrpighb -ugZotCAvu8adAXi8gs0Lhoa1EFR+E3NYfmtzeLri9DTCygkZwGBZuQuYA6TkZJeKh1g30AwsTVVV -voGQYB252xx5oRGOPU4SogOgZ6fAhBKB02afMo/yTTKNmXi7vG8gW+ghyhFlAy2j3bQ0AzgcdyiI -zUInw1ztA8KEATsonOojIgAOZakaBAeOglh5EmkCYKlkBEjHkmDgbJERvWSci+KkiC86HcjItgWh -ovh3KREKNbUFa1sgdSaQZl+Knbp0os5kIg4tXb0HtWsfKoODbbAH6IpYuCYjgWOwXjxqE9cBJ8G6 -yjdYFDDMCb4QXgbOPbungSMpjKu2iQXthUei44EsZmWEmzK+D5hxXjk4cWlogVNa9oY4L49spxpg -/tD8XZWqwSnCeEnFlPGUcEm8RQGxwYCCXc7ZUje5VbQDOdXka+NOhSIMMsFgm/ApDIA55qCT4Fjz -1mCYMKqFEfewg7rBtXuUhJ1q8vfwuUL2qSkMgILN6LIpOQTDLnK5jAFmfkXWuoZAEsQTMpNe0GhJ -vlK1nAPL6z0q2BtCEdx18oPNJCgwzKHCNJe1BglYbZgzFIPgwYFexQfUhjmv5GqIaxGo3mzFV7sA -P/CmZhjUmWXmeV0VwnhK4+RZmHfrOQahHgMLOxGDBRRAQGkGdiOSjNgdGxfemMYYcOGDFR1PwL+q -vsYRDZMrctbt4DrUQi2qh0MtMLNlDkz5SjeEa7Q3BGVEDKf5ZUCVeVQDvHQosjQUAhjjZI3c0sA2 -EnDAEO//eIrEGSleUlzOTFcBlrka2YmfZGRvrJa7zxoQYMYs2W5QW1KKbitydNqLb2l8LDGQFdt8 -MyUMOhiStYwVNsw/BnaaBbDxjtjduLTemUwT9FZh+iNbIjEqbLoBbV9JhoGkYEJO0/qke7BiuoGg -EWebioqI2cG9zjZUGoO7WZvm4s/eA9fduLLaDD3cewBiBg/s3hV6+17Vu/ur8wBCDvtriwTcf3R1 -dXmpp3q3LB89Mte7afn+pf4oQAr+w8i+kYP4QPhV6K2EqN3Lha3BcMcU6U49dE1OAYDvtZjSQ0Xe -ZAycFcTM/ph1YyRPipwVUrcZkIfAHQgYyAA6pOS9QsAUai7zISQg33gZb944zSzrdnAVYgf778HV -xP/23wdm1n1vWYLg1YO9e1dmDs7PLa32dPGq3gTw2kB34LftVQ5cNiD4MsRLzfGU9O/+e2Mv8Wj1 -wGUK9OvyewKhjHlIZQ3RD+A7vn+R9af794/3a1v2C0PxMCJWRwRowZkqbpuKrGFEv/3bBe4cQAoi -b2dADSq/J+QDmo63jr1ASx0ljMamoYW+AJc96houJcYBv35+Ye76PCb4jpkjGD0c8UJs9obdu96e -xoN8IGK03jt6kfmJP/EK3ze0CYCoGf34HkQBK4etI6RXxAfvxafeNLM6c33PRBZo9667b971zEZe -V+78phF/NtTt9j76OA1mKwPY+ki25elbGcb2DmATwzgeA9jQMI7fAEYcxvEewLrDODEDWGMYJ3IA -w4Zx0sdw4gcwOIyTNYY0jJM4gO6M4eQOYPwz/hn/jH/GP+OfU+vnpJPOjvAPY37ypLPWHZQvTvww -WgdwIoexxgBOzDDWHcDxHsaIAzh+w9jQAI7HMDYxgG0cyVaevvWRbNfTNzeYDfV5RvO1Y8eZ6bVj -xxnDXzvO3HnO+XteyK8955+z88xhzc88+4IXv/Ir3nzb7fi67c1f8coXX3D2zrbWO3aed9mr71h8 -70M/w6+H3rt451fu3XP2mS2dvnDi7d/5k7/2yb99hF9/+8lf+6n3zk9dft7OvqY7z7t8aulDf/x3 -n/vCU1/m11Nf+Nzff/Ij733bvr7GO8/b97b3/tJfPP7kl5+ul+jp2PyRj/3A172q0Xjnea/6uh/4 -2CNf/HL/ij791GOf+KFG4zPP2/d1P/SJx556ur9pfH358dh433kywR1nX/62H/jE4wOdpsY/8LbL -z+al27ln6r0fe2xI09j4sY+996YX0ih2nHPV/EceeWr4MXjqkV9cmqBR7Nzzuvd/6gttY5UJfvEv -PvT2y2AU2O1n1+gWhvzH3/kVF5w5Qrfx9eTf/eSdL94ZF2Hvet3Gjj/3a4uvPGfHjvO/4r2fXKfb -Z57+wiff+xXn7zhzz5sf+tt1uo1L8bcPvXnPmWe+8PaffmTo2qZBPPLTt78Q2v7MKG1/5vRvO/qa -bWAvNrLHI52dpz77kfm98QCPcCaf/sKn3v+6PTtHOuvQ7VXn7BjpDkm3I91N6XakOz+1Z+fGccla -OIoQWo2jEu77Qn/rp9sRZcSpH/nUZyP2fTpv+dlPtSLgy6fm3/+Ln/z7GllDy198fxtiP/PsPVfd -NP/en6qJwN9+Mra86ao2grFj5zl79n7lnTVxeei90PKcVkIUW+dEK9KsvcNaYuucGO45v524Zc13 -DKexe/e+bukg2JR377r7jXUmbLFt38oG8RsPJ4eVPOV0m3vBYifcCzrgZDGSh0NFHimQQqKYBGcu -NwnBXhBqD+mFabHjL2Uhi6aB1DzKanA/M6VCn4cJSg0Rx6bDpEWvM3mTPqFAo5JyIlWUf6vh9fCW -zMFpqCfUvolyUlXKhDLL879//42zs0cX71herQvLJqcpcG9C34favSn+Prh7l03eVA== - - - B4acoeYHmztF0McWzxF2scWTRFPZ0lnae2BpG3alkVa/d92ty6t3zM0urxyMJxI/X++4XXfH3MzC -LTPxGQ9A896+qRtvfgMf5bsOLa8s0mdyBnr7bjy4fM/cgRtvDgfiwO9cPbYwd6AeggwqVQOIc7nx -5t6NR1eXe4R/5t+dFfUZ7PO2e47Mrbxr7uCBN88dO0CtjjTPX7xyNy4tL/VKcJKnA3dUfsT/jv1x -4L/BnPoTGHXnkgOn1YViH64MDpmqfPJmDRVnRJP8e5CTM1DGKgamBAwYD9xoDB7DBeYjyLrNgDyG -WXFxFXiZJymibuGq87lMQxBYyn9Dw82b1vOq+xxcBPKcHIorrrtl5sg76W3Ru27/8vJCb9/NS++a -W1mdO8h7lOBTC/OHD8fN64ffNH8EKhxKeyXw6fmldybo3V8B/x6lLwKZ2nug4a51YNBhK54zcdna -eyD3yHIBQyNd7x09FbFnwU5bQxshCBvSvz5zSBOvrQMNvy1VVui2tSa9PuOMX//sr8nb33oysi9P -/jaBmZvBD/+B//jH/JNfx3a/A9956rfWfAQMLJH+uIxvTGuVkf+9B24dtsFpN+JN7L3+6LvffawH -W968f3sP4AGpfWcP3H0NYY7YY4Ym4mUe/DKto2YfvLUcYweoSseOZY5RTofjee0NE/L2mpu/YXX+ -zZN0pN5y9Dvf/10PvPUq+EMd+Lcf+L4PfOtsBX9ceefKt3zHtxx5y1X0nRunb5vef/Wz+nj2l24Z -YIsUI7NhFKj/k83QIOxji1SI+tgKHaKZbIkSrcEenQD2Yh2cs7UAglsjWzVYQkhYGDgjA+7Ka53F -TbkyV5AFi6Mm8N07epXrVb7VqbmlMbo34xfo33Xdm21kuTfu3kyv15pvX/dnE91u+xhOwKg2N4xt -H9LWh7FdQ9rekWx6SMdpGJsYz/EeyejjOTEjGWU8J3Ik646nO4M58SNZYzwnazCD4zmJI+naYPLx -nPSRdHMwJ30Y+XhO+hjGgzm1BjP+Gf+Mf07Ln07hmfFg1hhMR8bTQeaqI+PpzmC6LDedxMEMjuRk -jWfYSE7KeLozmLVHciLHM8pITsx4Rh/JcR3SJoZxnMazlZFs45C2ZRhbH9K2D2MTAzsBYzjerwE7 -dO07uWZ4ArU9c+dZZz9vF76ed/ZZwyMUqPFZ5+zec8llV+6Nrysvu2TP7nPOGvqF2PjcPZe+yr/x -rq95+9d+7du/5q43+ldduufc5w5zJn3ensvNm77uyIMf/KH/GF8/9MEHj3zdm8wVLzq35Qk7znzu -7kvUrYsP/uDP//bHPvlwfH3yY7/98z/44NJXucsHfVDRq/U1sw/+xG9+4n888ugT/xxfTzz6yP/4 -xG/91L9fvrW4eFez/Y6duy5Wtx/93l/51D889oV/YQfbp/7lC4/946d/5yce/Ppwybl5+9j6Ev/1 -D/70H/zNY1/KfHef/vJTX3riHz/10fcduv6SvP8zz7nYz73vow9/9vODbsFfevQzv/ndh8LF5yS/ -3B1n7VFf/77f+MxjX/ryoMvx008+8de/+b6vV3ueK93vPPeK2x786Gcef7LVp/rpp574zEcfvH3v -bh7Ojue+yC3/xKcebW8N/T/28E+vvualPJzY+R3//nf+8UtD/bWf/tIjf/A9M9dd+Jwd1Lk//FN/ -/sQaHttf/vxf//K3vOFl2P2Z517xVWt2DsP53Md/4B2v2r0Tl8Us/uSn1+ocuv/Mzx/xe87accaO -sy95w7f/xj+s1Xns/l8e+e0Hb37Z2TvOOPN5l9313X/86Hre8Y9+/Hu++opdZ55x5q4r3/aDn3xi -HY/+L//znz908Nrzd8bme7/2hx/+53WaP/3Fv/6Fo+GFZ+2A5v9x/eYw+DdecvaIzePgP/bdd132 -vDNHbf74J77v7itGb76h3htj39DKbGzdN7arGzwzGzyRGzzvclXbg6Ok8/o2ESL41b/5/NDFadxV -GL2e/d4/+Oyw4fRhgriWl7zm6E8/PARzPP3k4w08E1Hk7r23P/jLf/VoCxZ7+stfeuwzv9HAYnG2 -e9SB/+eXW3Hk5z/78EffN+czHIk41R1IGPjp1PNTX3rsb/7gpx/8et/AwDl+f/Sfv8gIHvD7P3zq -V7736O1qkB4w9fiNj//5XxP5QOrxmz/x4OxrWiIehDZ9x/c89GEiTkSbFm9Vl+x+bgu1RMpXveGt -B48S6WPKd/me5w2hlJGuvuCSK64lwpro6pqE+Oxd57+QyPZ6VBu/kDEF6/IE6SujchzrvtYP71gj -tgP83ik1K9a5hdT+i31wKJfCNTx8MUnldKAAQplqoWDdIgGlsIrKBNNoCl7vXHil7jMDygBS8kqp -J21S8uPULXgelc6HbAQJlGJLcKx5yzSprM+WFeDAjrZgDqM5mMPDOk6EuJ6TqjC52yWlqcTcowVW -tqsgSbii35V3vfoTzJgrmXENJdO/e3Hgy1gveu1vV1AdJa7W/tnGt8uKKoSt820oSQe+ffs382xI -yYzpv/fPNqNNIDLEm7VcIEf1z4ekz1RFtu+QJjiUr+ba9F5JdViszEpFwuCMUM0PAaZCZw7LKmWN -IbO5Uyo0us2AfedU4JgbPQTdy7uFgBQp8JnGkICp9hENOG+cppZ127IOtORqvGAbWLBRgraUo3uu -oPNisjRQKRFKKUICeSg87cuBK19MmqKEOrgQnBVKVf+uPyGX2cLyrfNQ9b0/N22RepbwlGJSFwWX -wbWmkNrBkMkcqx9Bvm5dFuJRG6+lFLuu0mbqkt10NcQgpST3vpDC2Ji5m3GsM5Z6lerxCPRGhpAq -tSMcVx0qQGhGyJZLEuP+8CmrxwB14XwKAYQKokrAphI6QxXWqCgy1SQaXIWpNfC0xbTCAWLuEE1H -auD0YDbh+CHMHMoaTmhD1YAZy1G8mE8j0fHxWiF+hvgpwG2QIX3tb2lNWdcBN8ZvQfGK9b8UJ43l -LvZv6FGQnl1VtomGuxQiQc7u2xYq4bHQjbK9d/Qie6Ek/fLQRgCihvTv+qESpfWjhEqs83rjPd8w -8/oB6Ovvfdd3PPSLv/rh//TvvvnYytzrEnxq6ft/6b8//PePPv7oI3/5Z3/8u//tP9x7A3/h2z/8 -6c89WYskn/2T//SAhw+qB372L59sMP6f+/3vvg37+vZfe6QpEjz+R999K3xSPvBzf/GF/IMn/+a/ -/avr6Tn/53/+/c88ysLN0//yvz/94W+9mcf2mnv+1Y/+yu9/7E/j60/++Pd+8fsWb8om9KZ7j9x/ -7D3vec+x+w8ffN0Zm36dCkEht/Xj6LUjQxQyPYCp9JYwFdznZnwJMr5r8Fftcd1qzCVsF5cQmEuA -0hPAJUQeeQJlm4h2oMySGaw2MBlROTSD2kRYB6d+Ix9RMW1rmNYp3RfJfTojdaUEX1cRLat2pF43 -wrATbEj/jhD/ZquRAuC2/lp63w/+8P+7sHabxQ889F//8vGnvvy5v/qFhz6wPKzVN/7Ipx6vcfUT -D//wUmtfP/zpLzbR/RN//qOHB5q95w+fGFRDff6PjvU1+6Y/bVNXPfPMx/91s7chzZ555s/yHpf+ -cFizZ575w8W63Y98fni7x34kNfs/PjO82TPP/PU3c7N3/ve1mj3zzO9/A7X77kfXbve/P0Dt/sva -zZ555r/QZP9ivXZ/9Y3Q7v2Pr9fuC++Ddh9aJ09NpNMfgnYPrdfsmWd+DNr9+Prtfvx4tBthfA9t -ZL4fWOOw0OsJ3LilT6/X7tN0BH9uvXa0v2d8zzoP/twHqd3hh9du9wm58T/4ubWa/dP3y7l/5x+t -0exLv1UjkH/98aHNnvy992QX89ifDWv2+/+qcdG/+eOtu/Kl32s2O+OM+//4i4PN/um33nNG/2vl -Rx7uW8fH/uz7W3Hg4R/584xbffRPPrTS1gr7/ODPfvrRyA0/+blP/ez7B3Ff/lp83w8/9NCH3rd2 -o214ndYcbc4htXG3m+CQBhndssFdjLncE8zlWocSCmxhAQVDTQ/V3hoK4g5Te0N6mUkD5Tnld660 -iJMxAUsHwlEITo3521OQvz1jx44RHGZ2nLlz53Oes3PnmqauHTufc9ZZZ58TX2efddZzhuU6i62e -e87zzj1v9/nn7z7v3Oed89zWljvOjK3O3X3BhS/Y86IX7XnBhRfsPje2HDDKgXPOuedfuOeil1z8 -0vi6+CUX7bnw/HMHTH3Q7Lzn73nxxZdc+vJX9HqvePmll1z84j3PP6+/Ibj6XHjRxS97ee/yK67c -e+WVV1zee/nLLr7oQjAe5t0955zzLrzopZf2rth71b6rr756376r9l7Ru/SlF1143jnPqTvcsfPs -c59/0UtfftmVr7z62onJ+Jq45upXXnnZy1960fNzl6Azz3reBS966csv37vvmsnrCqW1Kq6buGbf -3ssuvfhFFzwvPTk+dfcLXnLpZXv3XXudivfWmKrUxeS1+6687GUveUH95B1n7brgokt6V0Kzyljn -vbOmVJPXvPKKV7wUOpQ0oWefF7u7/KproJKmD9dff328xDY2vPqqyy59yYVxhPLY81/00ldcuW+i -KGOzG2549atvuD44UxbXQod7dvODdzzneRdc9LLL9l4zqY0LN7z6K+Pr1TdAvfDJq/f2LrnogueJ -5X3X818cH3vtdbG761/9la+Jr6989fXe6Ouuueqyl734+bvOknYXxuG98tr42Njda1772te+5jWx -nS2vu/aqyy998YWp3bkXvuTlV3C7r4R2r33Nq2+I7eIAL48TGdJfbIjPHeyPxnfNddqk8d1wvRsY -H863t/dqKBoern/1q2G6sbuB+cr6xQfHhbn+hviC9dPX0fqdLxsn+xE7jA2hoHrwsMwTV++F/Tjv -7J1pf59/0SWvuOKVcePKykLRelvFg9C/v3ReXta7MjaEQszxVapi4ppXXtl72Yuz8xLP364LXnTx -pfGYXjMRKYSKx2/y2nhQe5devOf8+vzFDs8+L57n2PCqfddcOxFf116976p48C9+0fPPzXMbwm2L -1+jS3uVXXvXKffH1yquuvPwVL4vN4lOzi0TX8kUvueTlvcuuuBKu22WvuPSSl+zBZvnFxIYX8P2N -r5e/7JKXXPSCC9ov+q7zn5/wwYtf9ILn7951dhviQPzy/Av3CH7Zdc5Zg83OYHy169yIrghftbfi -lmc99xzEf8+FVsNR5Y4dgE4jQl3fdWDHjlEQ9JZfz2r+diO8UbsG16+ZO3E93hb4zkAF6YWtrUEK -SsgjV1tOWizsC4wq1jPv52oROCu1s0tduUZjKBeuHVaYT73msJRNswb5yaCIc039BU3lzRsPFyCz -s2mkWeNsSqnXxszXcNhA7bqC0uNFD5XrvrRrsq1q0rvIkZfAogaVs60erKw0Oxci21qphrNG/K6Z -LCJKXee7NrK8yrJFEIpIQ5Xj0R63fxOP81yn+tljFqS0mpF5LnVPt2dQyxoBiBrSvw== - - - 63PYx4XBtre+9a3TpvUjc+vbZ5f+7+/90A9/8N8dPvi1t9m+T9/2b/7Dj//cR3//Ew8//Ik/+NX/ -+hPf+y3v8NnH+4/94Ec//ld//7+e+NKTT37pif/1D5/5xK//2Ld8teZP993+bT/78b/PPR+//IVH -PvlLH7iPsr2dMf1tv/DwY/16n8//1a9+11fhxxOHH/p4i+r0iU/+1BHs4DX/9sOfeXLw86f+5y99 -643w+WvX+fza5R9r6/9x6f+MN3/bhz/9eN/4vvz4X/7Kd91G47/69m/9uT/5h9zR9MnH/+5PP/L/ -HSzS+rznh37143/1d//0+Bf+5ckvPv7I//zzP/roh/7NW1S2vm//5g8+9LO/9Lt/Gtf3dz/yn3/0 -A9/0tfn6xld1y9tml/6v7/vQhz74bUv3vO2W9k30cX/f7Fo/2uzrtCa8rcR2JFw+SGZDP8ldi+I2 -0vK16o76PtmU9ojyBW9Nf0R9bE2DRIn5tqZDWisz3/qAiWDKoI0qfO+6m+Mm77trZWbpCGQavj6+ -Obi82DsylwhHPGmlast0vKmu7LCeKENgL309y7qcHr9GG7t+E1WM0Mas30aPMBxts3uKqQ9vXjqy -OrM0O3cAcMeBm2+KV/fwtq1qmto2dGW2rSu9fcdmcD0B+73ugbnZo9Bxc3VPcAZscxwTVeohiSqL -fgFFJ9w6HNeDltNNRoYeeX1OuF2DAOlYcif0+DYgNoOPHbkTquxvLgZgJyM9sM12kds2SulGjzUQ -nkxfrkHQjVPkXCc9VvSxkyenv1nskRFm7bLp1T0257yWn/mrerVXdJO+GR3xOCcDnTA2/mFso2uj -Ak0fpBOvJysb8XR/6xAmrXE6aw0CQjlpnDKDrWPfKhQhbx371joSqLg8A61LgjdbR4nEFGqgb97J -rKGKRF6FwYaQP9+M2NZVUY4k37+s9ZDFyA5JvhjrSEv7YtMo1zo/aqUE1bsJnITBWb8AJ06tjPW2 -h4Kbqwz+DgUuKHgCKQdvdBHnXba6xUMe1lIp07hACQSOoZUlj14oioB1CWzkM3ww9THmv/kCwVdM -Xzt865s9ZsD6AgnI4ONK08t7xCgMVZh6w/lvvkAywqxdPZesx+acaZPQYDJpKx8aK5FAUXq2yhBj -pCdLb1CkRqfudADT37wS8JXSN9vh20I1esyAaSUSyNDjiJ2SHg0OzGUrwX/zSsgIs3b1XLIem3Ne -A5Vw+RGF554iVoqitOtErFRVZAgnKjOpQ8QpeeCH9TI9FQeji8GQlWoyWG/W+7ZxkxEdi2t0MerD -2Cm6ipcPc1Zv6Ftm0usCaMDa3wLP/BBPOn9LT7pQFes+K+5N4azu09JMRGzoStCirfltyMytvdby -zHIyGL/ul6KIYEDVKJ7iQIbWXXlQ4RlvQr2UKqj1vgMqKOPqr/iIHtZ9kDOTzho7EFc00gGBq+dd -nz+7KENvEvM5I7Z07RMIspr7eOVYHorzDeROKbgJL5/8XRvTy6Cb7VxJaC/vsQZm1z6BsBuOmJMe -K/k4IRz+O7kS0Aizdtn06h6bcx7Rgl+ikhoivOAX+pp6wANWxQOXVYQxvoxiXSh1fBt5DLQmIW7Q -fjLSKoxkkZ2q36UP0W+1iGuHg6NAk1EM+6OXrGkd4HEpWZNVABjVFrDmKq+jpSh6dwxEwt0Im/mm -2EFZRg56N5HTwtqczGUgX1I4EWnR49tANBvqQ8neFIXJgEnfr6pSNRoDQQ4lAutuM6Ac+hwE3XBI -qPRo6GPbeHwCJo0/jTVvnE207rYx+1GLb9GeaD66lgmg08PpHwTaRD4RxMH0ps95JVSGz7gyhogL -qqw48LH9mJPSu9Z390c9AuxNICvt5poNmSCTimW1iELtQlO/oIFfbxVJ2oSXASGHdF5t4lCr4NQv -YOHXW0WxVqGtX7jbe+DQsFUtWGYHsbU3u7x4ePno0sHekftmDs/1FpcPzvVrM5P28malbp9ZjfL3 -0m1HVxfml+Zev7xy5+EopM+tNL+zMVn55qVDy/0Ccm7Bm3TM/Vvg6xW+KQzSQOT9E4s/wPCvLU2j -5axA5j0yBaG0Yg/M4FEmqqg2Gr71jDUsRybi95zJgFMSJVjYKodPE9xRXGSjE7D2hf7HZUAe21Qy -NRIcn6GN9FzD4RkFIZD0OPyYsUoamwCnJKyQ5yJw6rmeeNZJvUTZ4zIgj21qyDpPI4W7OtOE9F/t -CSyfYiutqDsdeQLenggvSuUInhZRAy/IQIzQ5JiQUJkMyPg7ConaqmZjH7lJYyoCGs3FVny8USVr -kSN/m4yubtKpohKtLpmHI1DMtvHewpVsAiHou6zqHmobb5T/DTeugmKgBrU9Ab2mIjURDXsv3FMc -W8TSxNZMRhbC84DL4IKgWlUxMBiNCQMKKqjHPYT4Peohrgj0PC1wF9AUGuFx5xyvTwgWUxHEj30l -FvEiVJqAkRGz9QpHPO8Jrl0l1QiVR7U+DMgG0bGrUHkCGngW9xDFEFiVGj4tcN5oeGJZON59Fbzj -AZdWMTAejkDA0tXl4dRk0MhgwmoUlbBhwCnRusW1lFo9hda8mHEBq3p2lUGjBfQV0ZKMLa6iFRO9 -gohdnqDysqfGi4WjKKqKgJFYZzsSby3DXSEH0waUrOHkK28HgKWpg4NzuKMKjlDlskDyFM+lkgsD -C+UtASvt/3/23rM9dSRpGH4/z3X5P+BMRhIZDCaDMbaxwTlgAbLBREsws+f+8Pz2t4NCK4twZs7u -nJ1Zjy21qrqrqyt1VXecWBviGCJQ8optYwnpWZKmJYoxIpOID2XywnQqTWPoVIbFpRGnxauQGLBK -ouJDsKIjytRLDA/sGzgB+GEySosPo7DAS/sQzZoIIQq3Z2ji+Vh+zoTFgQCewgIRCDgqLq7xaBLT -Vy98pMgJGksUa4I4XtETiXJUGK1o6XlD+xw4b1FaYWQJDH48NoEuCsvbbceor/jPoKs4YPmFK+Aq -zqZTrreY8VpN7F3+dD2MFgMtVcpHgDKZqJ9HYBQyKa0yIA/oaDiMlSjgH7xIYvC8DQYr0bB4Tgkg -bJiKiheJAqefkRRSAvuGol8fkRYUjRKs43HV/alJvBEJV1lS1u+QneO0pnFY7gINbOCkVNgfS0bC -ui7AtUElxf4CvZMUISTCknkQD0ckJyQRizEiFUDDorzM0GOZaA0TYmL+MbsmFRAgDrg/5jQkuzEf -or32zuVs2gTYFgBhILBDXLpFvtn543KO3iXwu+Z4CX5edb8ApwIGxvt3BX4pDFwX7JT95HjXFXCC -AQ/bvHTht0V2PB4Cx2c+GPbEpu3ZbBxyhV3zhesG2sUhg6YeVwDCID+IwQ8qY3bhMvpA1RQY3aAt -hmGKQv1FFH5x9Sc7VjfFMAw+YCipO7awMYwwpcUAJvdqCqz9gao1kBbjkavV44fd7piT2mIYpl/w -vRk7BiKmwoGXHG+MAcMw+Ko9GE6tO1VZ9nFXMAzTdmdT0PU5cmDMAWIYsPkFy48AzwQgH3wOrLtw -zwKgvdl4Btu3xbEMeyNXyOMKihwMuF7Nvz9hLZhMM2R5zHaI4dCf6AJM+C/+v+LYU9ZdXhe9zMMi -9ij8N0rBf2PoX3Hj7qdgVxYExg7+ZvCYgXcQiUpj/ym4w1q6u6Iy1WGwi8G7vz8NPaUmPIOqY8DP -OCVOAv0TB6/IRYwdnSGCGC8ABx5wPPptrxSjdQy7qJZwiHeASJeCZLgt+MxFh1zSP5B5Qi7Rd93C -KrLomkaUmneP+Ue7B2W2ed8if3/foIow7ZDSnSiMWgL/AMb24P4f3GwOo9+SwENIiHr/7+gwoavM -CRn/+wmpVormXUv8/V1T62Ckfc37FyX7Bxx0MNHAo1y1m+sIpSR+J22jCDt/hM6ns7+m6C9oRYuX -54YuQZeh5R3KAxv7T05+HyqKIdvKcAxGjD4Ba284deEm+DFOhgmJbfzGn7UWLKCS9WcFVhj2VN/w -sxFn/RGD/hhf8WJjCMdyxyqEG4pjdDn3HqVc1FCJ+3ClXTt/uNwEKOS5pF0Itwv4Jq5QE3q7RsMC -/m9/OVyYjcj6YzhYGzJaDx8CUAbv4IRHJznBBE1k+IgicuXBrcCV/+SmV/2+OaWsR46XJJkJTasH -7kbvyh8fYM0E2eEcMDZeRrBQQWJxt6TPXKH2cDF2wDBw0zQcxfuYLvcN2x8uhRVm+5/qNgMDccza -3S6MuWl/I0ZDEFZfZgQrKRAseQZ8I6d34pcIhLG0Q3mYkd8y798u81aVcE5SDBgxmYuBp8UFGeCA -xIOwMAxGqaMGJ5KiHem/T3SW+Nnc1Rqw/dlfeiFEvDQXQioITgWRLDphqZo0MrQr3BP4HnoAHHOx -TR8YfegRLbbpjqd42BRMLxVbzeZsT4KEnwxm/P9J3+En3fESbxUbzH4wmUjA2DwMLNPAAqcZ9Es0 -kkQlLGJyJwX/C2cykkxQ4eTmqlHojXmpk3hq5+yQ/yeFMhhbxOF4/g65HP0tl39lubzmDPyq4nzT -IrbtSG4TYU2UCmlFtfzKVWZ7A3NpTVYbOZTVcs8X0rdiFg42HRUJLPTYMVfrNIEbDMimHjXPfYyB -Lftg+PRR8xSV8KAiGyTjIyoEd50KizYKSa0xXU6Ks/mQE9QagIdbSFynxH3ynPgOns0coWIxqclk -9icA2VwI6j4gVI3hVAsRNq/JzSOUpmuasat6Ac1tViQaqbhEshGjioijmg+nzdlQBPdrexZhQNRo -wqln8T8tQuwswtUPoPj3CRW4OfFTpQq9TZlCbUWYaMfsXJrQ/1OihE4Ekwz96wQpdAe+bNMe/veK -wbXPmfwtDX9Lw3+RNAwmfiFpqD3X9bco/HsukfipMg/ANhR45emf3Hg251xAVrjuWX5uztbwrXOe -lpYhRqwMBqkDidXxy9qMH/6f2cs7drzEX6IOpFx5vjcgEglLQ2E+Zn/gP9VyQwQgRR/l4BtKOlwl -WPILq43YT9YawHuJblNx0PComkh8K+pD1zPnnnnsf0p/MME4E4n9KvqD0l+A81uFbMWaXuf2u9/m -9G9z+h8yp7csV+i/xy79NyXe/K9JUEMj3OndwP9eURn/yaIyqMHwi0hLbaccS0vteLZrP/5D6RqR -YBweF2Kbs6F4HJvnbATDMSbsLHEDHssFN1OSUsI0LdYD4sxpWCHIRKlkQjmciwpGcYrH/2T+huYK -75+jDP/+Uf20Qa2elBL7tyel3ECGR8eq4HWFixVUvzDiCXlyMlUMP3nIr7Lk0H07/5PZhrDQHfwv -LuX7S/XNv3y6dbjkwmEKvQoKl8w1D/HZCtYJUh68WArc5DmBW5xzP9RKowdQ8CxQ0cKcQ2tRLZCF -weyv2rDf56atJf/B9hTdj4Fzf87Gf3L5/wyFC+n4GQpG3WU1NWEXHYqSopA0PFuJfENLbyIME1O9 -YTRGBnwWllrDs/CIF7RBYzos9iYCjzghXjCU+CIcScZVL2ip/wyt6iVjAJ4J65+FKQ== - - - g2cS0CQTVnWDlodO0QkVuWgjMIzaoUTPRHIoxAZz/aAJ4YFH2IgM0KTt9eSx56CAjG08/BwsmjPh -Sd0x6fGDRNCk6k1pyD8YfyCatcGo6gXgKG4qDBc/JHBhLbhHiWSJqPbVk7gu8QNKa1bw7ERQBns2 -HS6G7BgY0n3RZpXtTszl1fFM0AZDu1ghcnNBDVxcA0XxUGlpaQjATuTkRRFWzDtpzUw/RSETlr7A -uFXSRzLh8/zigtVi5v6z4Jd9GXNEO4oL6I5obHjUKwmSkicsgipx88VA/cUcyg0e6Oj5bCHjl7rV -gMRWPw2XgI0rSGcPqIQ3LQnvJDQ7ZeG9DbGNxlWEFWTaEDYmRX7SHRI+COophySqityqGbr6+AAS -U02N4fTPoTAErFDlZpJbIrIH9yc3rnGQIB5ZZmOquo5cBfjWcntFnlUECHwJuns2/WeN4fgvZDbG -f5uNv83G32bjb7Pxt9n422z8bTb+Nht/m42/zUZ7szHx22z8bTZuZDZCyiSkvRAKkQadkB1OMgxJ -PQaeQwJfxX6bk7/Nyd/m5G9z8rc5+duc/G1O/k+Zk0nSnGwNJ/OxYk7K1sJattZ6vNcE1urCsuvF -McfyrsISnYn4L7eFf7XjIKy7/69ILbRLzg5HUZohHaHQpTZMmInAm7RcdDBC0X9zmePPOC7mp8m2 -1TwCdMitqzjjp0Bt6r0C1WtzB0EDxXGdnqLz/vEai1/gWLUay09m0x+AjuMx+/lbaP9iQvu/RBzL -olRMSnbRHpeUHU/J/zwTAvTVNf/nwhZ/h7Bl/o7czv8ijgj/6zki/JshSIaI/WaI3yJCxRHxfz1H -/B0Vt/8+G/dqDsz9s+mY/eG6mC0FzlUCr39bur8t3V8m8BChUOAhCa9/gzdhJ2OucDQYhrsk8OJx -SSL+d4Ye6L+p6PdfxjOxJDqYJg6v7qTg1YNxeE1OMshE6KQLXl2Z0F/E/F/FNj9Td2yfbXBoC2+g -QItmoA9sES/Nw1oqCI4PDxH3d76mi7l6j2YyHk7UT2Yfwir1rL/KcjBaAuJZGpEYTKvFVeFUMvZz -z2b6VzJP4n+WeehoGBrdiHfCicjPFZm/7XDJDr+c8RN2/NsG/22D/zL2FLza9rcN/msYU/8tPPPb -Bv+l2OZXN6N+2+C/bfDfNvhvG/wftMHxhad3w9kY8Ogl9/sGUcilQZgbGYkmEoyYIhWEh0/R8Hj7 -KL5sTH6iL1OhwUTSYCKpYCz6MytV/k5yJOF5XHBEkTgVTUo0oZIJdOR/RHwqPTGnCf2/QxM48ISY -UIvIEQsDcw+eUBamIJOgJ0mGjlmQgwn+z1ADrgo4eOlMN0SSKLxqD1KCkUqYouhCbAuShGGizP8G -TaJyij9ZuSUdY0fHY8ixpPB9hOBJOM5EoxakiWyTLlsykv5OFpPS2KHgDcelVYeuXXeRMgqvQ3M6 -/lT++pfnCf2N/BCWzoqM4WM1JKUkPo1I4lh8Ys4Psf8yfvivyiT8O60USQdJMgEzhCRoI5IMFp+Y -M0T8v40h/g5++JULpTeIm28t6vBvcBafwXN2OV682hWiSXFS8jR0tEbFM4+CMeQ+oV+gtQwNR1Fw -4zGuWPxvWpB2mD9LdsrTfp7HVY4CehSFjzqXsCIcfDCcfgYC4vMC9zmcNlngDC84PIxmd4V+u59v -AABhwaN681cPIsHD/0HSqV/s/NHsGZHJfT8YLjjptWNysYjhgEstf6l8IVmcMXRTkXhcgOSxaX9B -kICc53szdiwDE612aLUCIxUCiko+DoPriMT/o8+rPDtXjULCGsYGMfxFhEdBQY3canGg6v8icHlh -IEMKSy6n0m9c7YgunhMNae0vCEprAmS1DEc+Z1o8kgsW30s6I5hIRmga9i6OLXLZ6UeAGqiiWAYk -nVwdkUYSl+svJcIn5EHK48dtMLHZ+bLXG05nyiDREdfk1MnnY8vDjkpYwqgRrOnEvyCYF7PegFUD -jUiUlVx45fTtiDReGTGDHVdIJ+zQY66Y9WZjlhg8gzUq2koC9rfkx4g9kvjyguUF1w3XV2gWg4dV -iJ9R0sEV0mlo4lc3y+4PZZUYl0u7m0ueI0FDYmMjQDpjTWIPsflkPhpOidYx7JdKrcW74cXmre8l -q/Cey/L8NndrORUGw6lCHMnlg52HNI+5pExm1P6RG49nfymkFPtJYw5U2sG1uOA5mKVa5TluSiwq -vMDFTyLY/ZG/q/CcMACfsIKg+TAmBS2UqKpCTHXThGRDUxgZTbC1Et+Q+QUTYs6x/ASekS9DkTgj -zETxVbmAjyUXDrh10knv+PvabDz+oelHUrbhJBOPWEo0eOkizkTBWKT/4j5xrHZkEtvJ60gSaJS0 -UOSyaalJFPOtuGr5YbfLsVNXAV5lJsvruIiclsKEtChDRGdVlIRxOB5R4l9wfSAseZ6d6sAl0dco -kiivDuQR4s+hKYvkMZaT49mAVX+fkMgVx11X9I0y4+oPJFGg8rHpMPwfGcZIYGmJicvOgbjXQZJ4 -QwWJEWOqxpDa7PT/2KlK/0n0TJCBx7C4noyhFHhg0g1ZYAiA8QHLRGF8aWYTBDWDUUmlGALDgXFF -EUp6IyGvblIpG4LAvXBd8b3BsK+VnVCRY8bDC18u1Zdex/CM4DW9FAAMVoQh7qEgg8HvwtqfsCeM -dkzUjRUwEl+I29pkKxe6JGO8EmTpG1U/WwuOG7uKP8bDaV+yhS3gaZqrQCkzfAMMvi68EMMGmv4L -DBDuQ/DA+m6NfjiqNSHbixCq/LDvAnYz/K8zGKovLFScuzqeddmxS9ZyeOWOIAjNG3uNJn0iKjUd -KLWys9Be0geiAtMB0ig2A22j6j0Wy8YjU4vsFWWrBMlAvOqwWYhgcxGq6qoxXK2Ahd1b93wO9yX3 -lwsd7+Jq/cUuegMXLUJtFhSnRnZdFG+G9HN2/rici04Reie5RQ3gmqj8O8OdwR19efiO5owPw6w+ -05IbvX+GO2LvohkOiqbwy9aPSXc2hqD+P6gXebbLCd9LzhVwgTcTbsEPe7hXheViARYire6T8ljp -v/iIIR6dTT9maGj8Z9CFzDSAoDibTjl0bZDUIzAMVX8sxiZOSmnWW0646aLELsC8H3ZC0gPoXsM/ -iagBevBw0bic9Tn8l3u/LzYnztr5z2Q8BS0C8AfcckfhgMOOcYM/5dtZDztJMQ+BfL/4MZdeh/Jg -0Rh3A9CZ7YMRbKMX9Nq9+BPYaSxgzBYHz2j6RXrya3Tk76ZHWmkFrJhxn+emUistRxNNgbcNLO3l -Qjx1D77zGw0qJbAwDAXXzD8w1yuMTdNx+JmQ+tO+v+6jqdABcyekTdqSXWf+gWlRhmI0Qeilw1H2 -lsJiNvm1x6n+04pT1+eN8aw3gmafLdGmsyn3q1JLGoThqmV5sFgv0UhsB9kFngBoQP+qAyUH8/N5 -Y5X19OtLjZ9Csb8TGml1ScoD7yQBI5B2NcUTJF1t7j8LVxl4G2x3OJbO+TzsxKJR4NFg6gM70AWM -ye6M5fuuHjL4aRcvLyHbpp/Yo0KNo0yESZg3Zgi4tk1JuLad6CpcZQu4q+dA2A5dbDpnwZT0fgDk -wGcWpMtHDzuUirwS7V1DAUWoXeigUulYSdPWoymQTbPlAkCfLedKW7ELoKlseYOVvZy7Guz0cwnc -HldzNpc/CCs3NMNuXy0X8yWMMwjAoxz+H9rvAT466BjaX5PYI6r66AIGa20+cT9DN3U5Id68Eova -qL+VMdysmHI8ZkDgximSgTadEj3Rkq45OwdQhOFkOWaJPulWiXRRKgAkwYlLk9qfD4Pa1SHMZwst -PnY8FLTPJqwwkugtnyE8Z/t9ZQXmz1z55WImk5EzWImU60MmSW88nIMBQ6vxP2AdfIJhSGjjCm+r -PuER6MCfyMNzAQ+fnfbs0EiHx7pmf3L8HDp8EpYIMQPiTMGZvF6yUDK4Gvi8VB0Dk8AB846HU861 -AGLFph9SUwFlLQhmE6gR8i1uXGMXgK3gycT8WUlAr40Vgty2MQP+NWQ1VWuyRRs6IBC1/C5Ebn1r -+qXbGrd/L63ikCKrQ71JUJqM2WLA8R5HTXuTHxLnKYuNbMv3eSGITVZlCjCD6ppJ0yEIFshRS6DE -AYn6msVKNvvPPNibTeGdvnhKLSAqTUlGiZo0BYJeI3qQiDNqyuuaGo59jkduM27QCsgoTUNCumrb -wvOOec4G4piGS5xdWLeCbQAoQRmHScMp98nKR8Qbt/qYLoL98Zz/mClLPWY4hs/JKDhh+ZEw+/gI -LmZz543H3AcxIpPWgH+4vhq0g8Zq0HoawMZITXRZ3pL2sCFPJD3YtZ0DxTqEMTWxXVA5wVrXts99 -QGH217AvntUN1CPQhbfTYQ8YaEarFH32NesuYP3NCp/ghf0xHFtNOWqpXrGGlO7x/SBUA2N2HvzT -aUNpgLTxSoBN0YXlMkTbhhLEqJHkA63mM2FoM2OwGZiCuXztN7Dd4mYNZzws7rFjgt6YD8o6sou3 -sywbS9JPPpXduCmY9CCCyQrd4WLCag09w7YzmxmXG3ZhjqQd+j4nDD+nGgIYzNB8zgcHM+XMdat2 -fzlsN1DOYweGTcJYAoB2wwlYf8HubKHosZiJ7JVb8wRomrFuqwghu5aEBDLSe7BhH+9TOFvHwmIs -LuP53EqhwnYiYKWhA9ig8QS8HpNut4OvIPdw/FRvkZv1CX8gewNELp4FKiCqxxwylQjb34GGUTEC -Y2wiwf4MZY/XAVCSX9zCTbXgOisX4Y5lLMAEaZuBQJOMXJZG5AKNJtJ9E7azgOwsdjqdWek7bLYt -pz1rL8wCDTCTiEk2MaRm8/7SroVgC6M3s6ANNALHQ2kRxo2EP2iidmpMzSUgIadWEwEadYfQOpfM -OBPrTO3jGNpSwrIrWMkDbG8R0+7AyFFxt4P2JOOaW0X8zEq3KBBttAXWUjrL2hSmICf4muvqxXCs -hEMMVw6yDnpTwWqYuNF83PthRTqs8j8dWDig0cAKHVDyzpczJJ3NQkMKEcWV7NXrCqYlaE4gjidN -9ZtKF8fM9SCJ3HDaQRuVx5mW44wwATovtXQRLvY6HjMkKVQg7MLOFuI5eOUNB+HxFlwGmwqj4RyY -d1NrrxoZWMAaAJP1yRmOsj7rwi14coxk3IZ2lZstMRgUusMRm4JpxAa3vhJjJC1VjETfrghDR0Ux -dHSjCh05idJBEFahOXIYjCt/5ngUqLH9IFCzjcaAINhEF5Xg4XSmBL9cwykKukEXQ25MBkNF+lSl -IwHYaV8Mj2oFoh1HRwwYcDgdjYUFsFxGYSgI5iTDGgoD5QMqyI4XQCRFZAeVNjR5lC8YYLopt3kB -ujb5WY8TBClZz5DxyR4ableb4YJ+EGGoRAy9Z/IDVr5+y3bstDj2sIUYIFsPBSJs5pCujCPYeNqI -XSkb6GHNrBmZsiRRCMh2FAxrKWjZbRVJjMwh1RiVXtARw5AaSe0Pnvtewm0SR9AZHQ== - - - 49uiYHQoHM5peDX2csYCjI4FgnGblUhrVqKRbiJ7r6WQvHKlrEfb1ai2RJwMPuJwpYe1K91uMWqo -ZT12epUVQK8iQxit/LSZAzG845Aq1GpUCWP/22FXNBS0UCy92dIh1LBmgDB1125RqZnY4RqkHVKQ -1lLQWlTSq4sRykiMWKLQkMimtfMpZfSQnSgR2iG/kB2hg3E74CtNK6OXlU66zmgl2QUwrqeq9Ewz -sto4VWTXVlpUzuUMtYqcCWt0jzzi4g92ajtcag3JTTtdAOEVFwClM6Qsqb+K1cXoWdpJc4mokit2 -Nh25YGIz6YWpEiKwq9CADkmex9Y8yoHRWfPqr7AT4OAzte+AkDlwHShT16+t2klXO5NX2h18WuOn -6RpQZs4ZgSZUGEouTL5VPDtLREscDDWgt5HTq6Nn38l95jjNPvjr4aOrQCHHVyeD1Od0t17Z9buP -i0M2KBzEbmvl2H4qd1vNXkROU42X44scv+zFK2XmInFIRyL7FCWUvkqffuogl34LenMn/rmQE86Z -EECTSzd2ealVfVH4rF03cicRrlUcZrK9UjB4/KnD1eg/AoTxUuUwFX+qLkpfr4XIU8Cfn8waQv6s -tRj4srH9ZaUUObgvfI2P7wGa0gdV7xpCO4gnP+J3188v+XYxeGeOlWyXes2djCqvuZQQnPhK/sNl -xV3tfwA0iF6V987VsvTxeh8vjHPjh9RHYbAoDuJPtIoi70elHt34zp2cHt9jQKDTQvHt820Gfjv6 -Lp31z3YLgcTXQb4V2J/iTjyw/SVAk/xy+3rlXvTaXRxEOumT/GH4yFe49L/7csXj20qRW3qzd/X9 -QbrXY0fwt6Gv/NEYYNQ0FWLj/PDgPTV8q/cL48PT4wDve1nmG62jbzgATy5dH4QBmlj67jWXn/aO -J77MRToUn7xkhvF4SPgI5/neGe0bpWgZZK9UF+4A5eLHXPw+TPVTw2KIBZNMX2TcAT9XGMebEzyG -x8Zhrnh2sn9f9iejsCq+dPYc28/Gi7M338ld/znFdPdfEdzs9BAMKRvz7sNpeY7dx66nkFTZwsgT -C4gMetdvUPTr/kUpxJ4cVXZ9TzxEE4Mv3hAU1ASgobp7ZxH0hy9bORF/O7kvn+P2RX/5HUNjHpkz -wMEPlC+bLfuZ0ulnRgR0nzlJ978u39Bsyj0G8K4KUQkNaFWoy114VbpAuzM3sBUXQc+iu4VSB5G7 -xAmnkdhT7KuXb5e+fKWP0Pl3mWWPDwqx7u31Sa3Uyebbg94i3zzoXeTbTBiyQD7++rgPPuo/lR/e -T5cylTDzqrj1baRAS4yDfE2atMdZ6aNV7iOSArisx5fdT9/jWYKQAZpyh/bdFSIP9UqO5we3kdTF -/SmapUR0yMfADHr9vsIs+aalpnroJOklUuHJhaAAmrQvs3RXSmO6QNWjKfCjSg0xoGysO8ul24u9 -fLu+WOqpqZlNgvTS7D/wu/BZG8q07+AwryXV8ibJVdxHc09xELt5KnepE2+J43k/xTUzabknmCIy -ORrVfKdOI547CbxX4JI9D5bOvuKsKAXwrCZa35Pz/NVb4aJS/KgnKPq8268U+5NHJEkN5qFaGMcy -9wrw2KKReSrU2vsZTScAGtAP7qpUHR1yANf1CRQ2YeojdT/T91fbrgd+8y+q871uMulLRy41NEmf -CbOP4lAYxpDoDDw3Gc/+WU0ZV5pLHU3Amr7xQCa78GXqT3UJ6+cLkGlh8Paw4a0uOv1JvvVeL4WG -kb0yBvBxXIvl2xezz9xd+4ytlBPXDwBN6sLHEDDgPEQlVXNTqXQ8uwPxc+qdKvfY8RBJzRP/42k1 -H3/bFfJUdhlX2pW7oeeDfNN/+oy7CMU0QIMkNdkgML448e8PH/Lt9pGf0EY01V2Wj6fNZ3luJr7y -3UNNUjDeo9JHPswRikB+C1lAbFAelOq8913/uf81dh88j5c+2nysVPc8tKqH1XqEqrzkwvDtDCiz -z2Ul33v+ACu9/w2aXD0CHjol3sLRpPI3e+hJxZ2MecoB/0ekVLg4cssCy5cK8eXd+M1rPwl4OZcD -P4pl+AMuskpe+i0O3xaz8FlO9awCS/xUH+Gm8pfFovwl+q2JPpI/l9tV5B/FCvxxA3+UpCbxJEZT -KatwoVYEIISmpIehdF/5IlOSuoP7VERQRDQFeaRZuVst6RmGVpTRNA2olDfDeoKoIxINgiyeqj7P -wQYZ+CyjwMjLjXMyIa9VRMAkvZSh5InRkLNlNb/OJ0MzEyIaZTKqMr2K8rOsTCo1RfC4lD8L8oAr -mk6IaOQJ1YzGwbSgb/FvFTVW4k+Mxpg97HmjoRqXwi86zsVoTOmFaV5QUYQYdcuMrurlWawQDK0b -qfQR/k2GoelORcPaWTVWBC8nzo1CkYp+ajH5inIvy5TxksGkh59l5LWBebNCsACG65jcDpkGUT0r -omnKH8mzT3zUkr5cgxkRa+dJFijK2K/k31ryQFD3L7F9cjoQllgRLhpnVzl+cXiTby0P99T6YwbM -fMEde5qUJ9AcBNZRPTSYHcaoevm7CCW/t9QrFifAIi+MgC4eRgnvg04C7+O8EDsGBpznmrBU2vWl -j9SBZLtbaA6ybNTYjSKtqMwTofRk9whpI+QgQG/RD/T404lqSNR+7u7YDYVNqT9uvFZK0SdGgyae -fmpU8tFc6LZUc0/38+ePranqLfsS469rrdxJIL5Xqvv2oyqnEDig0O7SGLfAdSRpU+h+ljhvuaEa -K2GB+IC/0v7ON888L6UucFQNAGAfGVnTAA3uG/S4osL0poCtzsU8eO07vWO/JTM3wW5i5iKrU7R0 -IbQWsEmuiovyU//wk3k/OS1hywKa2yfvJ5XSmpY7QiMb7zKjosGJti5deymx0+o9NHhHlTINnMVW -LMlQ9OyIig/2OUCWWMwrm01axwOBAmhEaI1YoXJSfgnK9kzQ2gFz6n1BUHGAJn77yp2XPq6FVmjY -fC6IiwcOPMzsPVv7lI4cyvsv6EmLnHF7OBLDFWoPwjdPv1fcH+fBPHXSfmI8B28nkiVIkqr0GfCc -Yi65Dn/P8uf3DwdgGfkQD5XhaIh5sPY5iJ4A36CSL3HyOr8RZwSR9D4TUNZBcsSdF5Gwuej7Ku+d -bLJUaDyODLoK3ZjP54qnOU6jZaxEA1J1JuDWwFXcDcXXEN2okIg4Tz2X+4neG1UfsTXmPe1twrdU -qj8ZU0B00nUkxKzWVzueS58/7gGfZ7ArswAsIIERkKt8q9r/ApyW4Znc7W4SLxT/Qeg5414yHHDA -jj7wC5nnkSdN+93FpNoz1DrKaeo0/Y7QTO+K1EeyeCZGGZaPh/mreaFWiCwLFO2/FeTF+xoEQvT5 -LZ9MnwfkF23Jec2+x+NtL0fVa3tpQPCgL91NDeIINYynGWDfNmqk1jB2kTO1ARnmabRfHLzspnzZ -aqejAh48L4wufAe+bPNDWYopOAlvhRGT2VVeADRykIQpcbvXl4UxV6CLw93nYyBgC9cl6mCQL/dG -30doRpJffLJSefsoH1fy12dA3levsR9EMbH8kSirH+oF4CV1k6GLm5c8hBxWRQf1fJOnpaZfYQNo -DpdxDZ6zIWtFm5WcHFdmH06WMdkT0cWHaORoAF0tno+1IJu18R254kKFYXHwXAQrPnbxroe7aCrL -2Jd9fPVKc0Mx2e8JEAWfu7nv0x4nzeXxMp/ozj+DdO3OD5mhDd35x9JHcDeKSZqp8wJ19lw9kRVW -OVVnu0EsTrOxSwrgavtqmZcIow4QM7WX43y7eNUqBW/6gVzqcjRUVJjCfTgIe1S7eYCu83PZP7tY -5pOhsVcBJYYNoYvvvpq2UTDlKfAOFFf3FgDvMYo+F5sWZoBLWuH30vn5LF18HxUiwMaIXZXOer5r -8KxNi/aBiN9f/BxUAsBsc+8lWun5U7kboj6hydENPQ1Tl5/pYbnbPvpWmzcxHFlrHx20Kp39o5tK -p5lfwCB317j7n4cjMIfJPRgxrQF45WhhHL/2QQmtWD1YP8VO3Dk+kFzmr72lj0Igxc01WFN0Yvey -4nlsLoBRRfflFw1f5vyyX+pPkn4FNRih2w05rZI/ugfL+SSienc8CA24t3cJBke8BXNY3QXLs/+W -+y7QaSCZwvvlwG4wph2c3A6yAGiaTX3kz89NW8EmZ7HvUJrRNRm3DiudEViUzef6S7l3ehQtnZ/t -tpLNw69KTqg3vqTIrShs9LxUHO4dxMT1mCqDThTmfi2DiFscoUHu7uO2AKd5TpqNIig4N9XXuCeU -j9Zez9Tmqzj7iWGBLb9VmNv8dfr+kDCQxYlMukt1oTkFizwWrB5WXzr5afWWrZxUabcCSrTTnkXe -C/ZzycFLfgyWc+kif9POfZNmudi3ELBrG958/CVdzKXuv4fx+3CYy7fzMx3jMdHRdyHqiUEbOj+t -eb8rJ5WeQLBMNhMLi3Bhe8n0RH++qFjl9MyYS2L7e6AT3GcuNjuA0cHC9eEsFpj627KZlQnn23en -o9xJdn6Zvw2dn5S7x1HTJndAL3gEqBzzsmSC1KztV4r510/wI9CB8bRS84LRwxD8uVmgXQOLJz3Q -Lg/TscobCiSU59ILRHNSjl4CcmcHysQDCX3eLg6ii2Yp9D73Fr4WkyEJ/D4TAQrjRij7vLEP0nIH -PwLzTuEtd3ewWCgdgyyQOX9fJpv1p1dizFAkhw97k5hCbjkU30DiN99aPA9LH0e1cSLKp+/Rdk6a -e2sODJgGchrUaGdA/HiPKsVC0gttuEtg8OX5Mvv+7CZntZdblj73Xx6Bb+LulXsxz0meytYnmiWQ -5m6ZXql+cfsABGstAFj7uQqJplpJKRS+xaZX7+HyA/S8ugBW58N7qZ4JM+W3q+5r6aM9DSpw4U7K -KXJAgV5I1cX9M+BIKNJS8m8AtPdk/mrWmycvo506mJbpDTBCy+18PHnzqV6UX9g6Ar8NZLsLAtgf -5cMLbyl/M70olbsfnYwhGtAq4k5eAd1CXZd797mEdrFRgv8p9nTjZYF5c/xgpDBie5WLIzgJxVJ9 -94MyQxN9Wl6Zw8jeRsr56OlHo+I+ryYJ18piyYrcL6OxWgBy3P6JKw4zpwm0YUJuegV2R3JjLzAr -hEDp7AxaRaHCuMSdvO1BYdO8froExlLhglSDyfwEWAz3B6KnIW5qPuVb3fkH3lNiMtyt6gsirHHK -VNL+PTn8AaODskmJaRO/GzfaMPZ+UO7M+x+IQKSGQKx9/bIPX4xJrLG9z8JkL/FF+Eu52vxbXp6S -LyGiuQ+fsPH09VUTmA23jF7yR/nS2WQoVIpcYQK6OI5V3i8Ok+aq/ip1AoOQxYt+Y794WbyjTQ2I -RHIW6pW7s1rApl376P4VrOqzvcLEd2S8biTszdPLBzC1w7h5B5v9Ug9tLxtoyjAwr4Ke1IUv/Jxv -tw+vlSiHpIGKl+XrT9GXyJaPkbmtUiEtLzQkj/MJ/1kxl369HSPriAp73R4xS2Hx8A== - - - UimXovFCzNdQUEeQ1bmCqAVytr0AavV5Xj2stOeF8WSWUBtEH0YGkTKa5pEvk786fi0HYvEZFnr5 -m10e0SbNJXt34nZ0rH7rS7We9iVCXu9Bw78KpUsPbeQDEdsB6vqlXsb7R3B7iJiba6CGLirHoJfe -a2B6HfsLgXQ6pSZ8GuA/AXAfb3MwzeBZQ7nY8vrmAZjIt35ppWWHKpPjpDqecoXIqN9PV9ijstPP -ZQmdBZ7e3A+WQPsOCGfqU6N0FDtNdh76/V4by6/wIvx04n98vyuMA3S7+tiZv6vFmSTJZCEmK1gs -t8SYDVJrsW86WYWblVThazQ9gGjOS/WHVk4VUmw+xdvvZfeJf/kyjGW/vely79qTUcU1UZNaqFJa -5uHe5hcifaBUaGTv5NCQigWA8qW8e0icJW6WxQvw2+kBsMSmou2iBokWZem8/vqNxKQiHkWgw7ci -Cj9whVR18d3v6hrw/WsV+c4LcMlkSp/hYk3xrhGZwTBL/bLv8voI+KP5vhzXjUFiPSD/pp2/L31G -Y3vJUe6rkYvvTe6lRXy0a0N9eYKkxI/rvXLvkXtOs/ezr1Ko/HVYKSduaqSELsNAQC9/dV5jkZ+J -RCeUpIlSf5m7y8/yHzFt4+Z+7E7ivtxhon0wjSnAlcZwT/pzWu5Dt/6r0snmn2G0qWoU/QbQ/J9A -2Ale0Im5GP0lWUBZeMBGvbt9vTthPz44nb4hAMUT17OWRp+rAAWecienhWXFw59VjQAk4qeZGlBE -R0HAIIFHuDxVofgY3GE/LveWe8MUl9mfyCRIkR43VmuFBOk81T8+RanpZ9IK6vTHuApdXOCPFR8B -k72mxMfLJtCAt8VnIFi+C0SgnFku9oEaLh+U30b0AIi4ynGpMu08lF4/Xym5d8cIykkx+TgBzrbb -LY8mG7t3l0IP7RCGhsQ6mNWvElcJu9VjfYSi6EJJmKocVRtzX35/d8CUzsIXTOngOUVxu81MdLfo -pYGpXhMqXo8fKgIggx58uRN/f1peHJ1loIX3GhEis4dKafF0kL+ZAc9z5H59Pwj/v6ySnsjAGjHX -1Rwm/gmuEjqMFBdYxZNhMnWPbNZSTg4Sr4BtlipihRM8QGKG22nroUgQRB2UXY2S+/lsPF6iIzJm -vGuDkm6jrH7YMdD5Dj6SCp7gM100cQ01/LI5Gw97VhURMPERfi+dXkZ0FR5hBjpWZOf4ILOhXGhm -lDUuASqio1iGqqPPjI+hkb6o8uwPeCwvPt1Tn/1peDCN9HFr2QWTVplNFzfw7AVn1ZwS0cT7Wn5c -Wmc/S81vBQ6VJbbJQlvjc3Ok7l3MprPegJ9NOIsRGtZpr3AsFRON2ZAX9JhrqU5W0ycCS+0LsHy5 -oCmetpju8YwvKMfERCynGpJDIcQZPAoi3539acPgBlxyPpxalVhLH1UA1e657t2Q+8vBGipxAmB4 -dISH43VThbPDLrj2YDnpTtnhWFiNBfG6hRdtoeNYzvpg/Q4/hnLBhWHRk4QcXztdAeyV5xd/zfhR -QzlWw4ol0KQVlfOQHFKzzQ8nF/CcB0d8h5BoGG/1kxXkmZQPT8Ni2l4sOjwHzoohRDrBKhJByxSr -TcyN+nABm9XXsD6fydEBcaaDai3YaZ/l+6RsNuqPsxMLreRlm2fnc8ujELTaJ9/jZ1120WB/cLzg -gJGhUDDmY6P6Lb1QNv7Winqibj2b9oCe1PKE/cze2BwyoRAEHQQDV1ubLII0Pi9Lu0DvyTMI3O0B -IK6L5QGHDDiXeNKMC/An1O6C668BN3UJ7J9w6bFTF2mrwJG6WAE+Vswk6WjNoAvoQwgS/FQD+zFb -uuZAQcELMDhsRCDUGNwnvGByqELkdwFk8qdTQCfXYgZB9DjXEJWTs64x+wMe4wk4ChAcM6Ow7A1g -985gjH74OVXAYGxTQCd45v/sQ0E/FFzL6QheeRd0JqAB6B4/nJOHdK4i2XUWmWNWacsnjTCMiYFg -oBhX0qlaPSAeS+ngS7kKxokSxpKqTZz16gADFoRD+QgJayKgkVhQYTVp3bY85EX6DFiPpDa0M15M -6bxa31SWmQPVZ3wmqLVFrqGmU6GPJReg/JlyxN96ZtBqHxN2m71ZIDspcC7yvI3GNDXi7dWFESWJ -r0wOvXR+vDDwfwEGbA0VZXdRM0Otu2qpXdJWqoGnsDjtatpkyTNoiJegs8qBMDTxCh6/e87xU42a -BW/Q3S6EsmKUV9A9I96ElTeg+70hcSQn8VF5Cubd4Hxn8OpBPuCP7Frz88OgLVJc6KDaOdvT8obY -N3iYLemPqd9iF3OhP1sJdnLSBUodTZQ8LfoIAHFJqebiF8gn+EYYOCHSy50/EAORj4xLB33ZaGkU -Ob16z1L9o1YO/ZlN1g6+lBfhQiKciD3FJk+ofAbtMRKf1TpckeezX9+50MJbAj8KjcL75f5l6alc -Y9BvMPvgqTJ6qo7v+u/5WiT1lj6l7xKl/f6yXCrWXl5pbz41i+4WPdcRIXwAc3q6ISrku2AiJ9ep -dPi0vSiUPpLVUW3/JsOWPqjHrPyW8WVuYoPd43n7G6DZ9Q++aru+XhD81uk/7/qZSmvXXRP88M/H -YLiR3PVlM/NDEc1gERbH8F4/BUN6T6DimhLHF5Zl30XjHmUfSG9Lo2BIiMKQZvK9Xc2U7wvcTS4k -fJ6EavmnczDCuxYYPTUDTesT8FGziDDAcc3Rb1Qo8xjBnS7MhEv8mzjWhsDzkdKQCj2M4ugFnBul -FRwk/8I+R8AYEktftrp7FBEY4ULqVqLKLB/fu+DP6hhM5GOJhJsWHvhXT+mCCkVabnLUYOYAGvCx -7ymyx6S84Et6Cvt7RWB9vY2VTLFGr8Mj3gzrO//6vbhFWPEWGokYfFzsxm7YAGOIVUjc02ZYa5Hk -YX5khBWg4fmTvSPh+GA2Nxouv3wPHri7BzcvRlh9J0zDbKyx/f27p8sowgrQ6IYbeXyiKoXYlRFW -Ya/ylTiYhtNNI6xU5aNXM8IK0CDER0zksWxM5Mhjn6pW+UdDrInK9y1TbHdhZyMz3bweT1IIK5IC -zeNjzdSG+bvQAGEFy7NbVk/tM/9aumtCrB79vEZeI4vgbUCDFSU5IMTzWl0crh5r7JJK75th7fFv -CfrBGOtl2bv/HVte4LnRDVfYnxcqZlhrp8yUfjLGGvE8+tLj20sNVohGZKiMx5u797JGWKlKspw1 -wRrbP2jd8WkTrI+vVOW1hgpTjIa7VxHSh6PQXcsQa9W/vDTFesx95q40WBEahPhkN8QL8+YBxOrV -Efm64j2N53YvANb4XIu1ES88ilgfA24N1lirMbrD+gYgLr+MKqrhPuWoxnM5aoh1r/YtxEf71zFD -rBdefqjBKqk1hDg36hTzJlifPVQrxQnGWM8DL+d9zr8wxNrqnEKVbDrcy5davWCGtUjdHT8kjLE2 -9petz24/oWAFaAjEdw16Zor1thqqjs2wnlN383DOBOspvInu7u3+PW843PvTrtcUa6eTX9ybYH2J -UK9XbY+CdQedViAjvrz6/HoonXgNsb4ugtemWCdN39GDGVZUm9S5/j4xHu5VzsM/54WiIdbyVyBm -gjVR272/pBoYK7u3qCK1piA+2T3ll/fPEYjVp1s8V/tpz9ty0gFYM7wW67v35kDEOkp6VVijMGXj -iLoQlQ9znHXX1FiDvPBZ34dYA3pB0QjuvuzHKgBrTtBp2ZdZEmM9dZfRSRJqIu/mK1fHGOvrIlVX -y8VbX7qWqUOsIb2WvaUD8cbRF8BaWWqwIiU9PPWIiFPXQfVwa7eFw6MThDV8ettoqMa6/yxEu89I -81B6HZA82ntYXJ8BrIxHb3LwfL47vfUdhk9QA93bQuL48bbxUjd8u2R3T6izF99C81ZRBOxxlmaF -5r7R52AeKv7dw8JBCb41EHHdaSxeOaDhWx3n7NUG8OrK2PQ4FjZpMOXjV6+1qMnbxSJBZ5/ixm/P -jnZzV/fla/TWwE7bO1ukz8/q+4Lx5+fUa8Pniy5N3sY6l7ns8ljzViIaaHD+0UzF7wKGnycun2iP -+/T5Fb7166UWdSDPpp7xG7EjgOaWfS5nTRqcuO+KJ/c5k7cF7/0BPyyYvK35O8VY+wW9NSLaRb7y -lfGEPcafX94MvoRO2Wfy9uFrEhZCjOatQrTLyfcsMOISxp8/vjYlu9rg7av3WWZy/dvON1TSknA0 -aMA+MJk9f7xi/JZ7uKws9q4447cf1Ovg+Otsz4xoh/fX/Tv37uWp0ec8n+lch3PXbjd8G9S/ZQpn -V9eFCXwbMliefL4zEXZfj0tKg+zcczInfDQocRTfF3twxYD3BoinRlx2SjXuqSzTdr3naXrXX7q5 -3/XfvbWgx9nedT97lvC3JvA7R8XdwHknqrh4wBWdjYzc7tBFYnoMvMWHJZTubiAoP04IO+1iPzTM -dD3AfNsrC4lmSC0I+T3mONMMYKsX+kGE+M3uh4+Re4rUCvSDRorsVbAiCR3xvCiItVj3Km8BU6zI -DzLBCqxe4Ad1VHYaOdzI45sF1upu1Bwr9INkrGEFK3ajEjXoHrxIw62O1UR+JbFGWgckha9zNwTW -/tHRvoIV+QaknRbWEBn6BnNjrJHHR3Ose5VPygir5EYh98BouIjI0DfommFlLbBWqZTaTlMPFxka -plihoXFrRmG/EVaARhru2ZFmamk/MCIQfvSbOBmXy76jdlf7nFYKmDTdu8rsOgDJLzujQ0JuwFGr -okfIYSfkR//I7yZCOOC3IjTuzhBtdCG36tUNIPOFX/xxSp0pPr0YGov4b9QeQbZ5BOTd8XNR7Ad7 -U0ChtOzc3W9rxRTAXwgNuNIh/LEvY/CSGCRRALrzXEJzU/iqwFZhVbAKyMBstnxI/ABiUrGmNVG8 -a9jPb6mJOGClx3Bu6tFD/AMyhcqXEPt2JY+h5Mt2wzWChjL1Qadvm+DPIzjXS6+mT8i41XVLMO2W -2J39lB/9wCTF8RlN7BBTvbFETQiPwJDw6McN6X4bjPCUPm9YjxAxNBop+1Cynsbw6V37wm4O/Rci -02CfRxqh4q3hQaa91vRyPofV1lThUlGt2dHLAtqdE54HaBywPfVx/P2wOn8pzCWOBvHXjGYPjmtr -Ul9N+pCG9Kq5WZX6XcGpjBAjUF4jQSRKoUf/VCuFyi/FOYlBHr+ZFJLMQf2MvJXp8qtQlWGEjWlY -PT8SjTUD8pVfrhaa5anrERL1fvjjmYy16shXhr7yuYkQN1ueHtPBhQ/S5401B4fQ4PG97NuT+wiv -JbOeUBzbuTUaF1JrCu+/e47QuIxF/aN/rtYajoekFjbll1JQxfHyelSxO8U1g0dqQ56cqmz15VtD -GFl7rkSbj5T7yUxxX8ucoyhunxVDUx/F4PNK0ExXH7Khu8xMuwDZvYZ24GuZAWyFlAImE+ov+/EP -cb7wFoeeN7rhPespVdkCokhEEWyDNc3uXdCm0EKDmS+j71jq29QW6DLfTOEpWTcepg== - - - v+A2sM7MpwUYeok52opQTQv4/Ja3lLxOpVAVEqYi7UbpNZW9NUlMcr+qFrF6NeUv481iB5M8Siw3 -MapUUgB0v7trTS84wpBtn5J7VtpT6ZEDWw8gHOyb9SmnLF40h9KOh/k02th6neWxgznEErpftbP1 -nC7UUYKXVRcBSrFsVobWP2bLTjuGt/AsoNm5Jc46huYGQrPnfafDNLL21ieaPOdbIZpauq04TDEM -psRsmMLDY0Dt3dbgkilvYjmL3Vlk0Z60U0/DWKZ91hyY3limQevb2ICoaR32TZbnZ415XRbOV3Cn -I0ZJMZBAp4bUkcxBpwRy7hmqqEMaUHDOmxOnvoTZdAtG4kGzbkw6o+qJIy9Q0xPSYcedsZMHTnqi -tjpBT4ytThuy2AkAjfPYDXsUy0atswoPnchqrjvc3tTkL0lLICWI5uANGRR3lxarYTAJf5xBL7hq -btkYSRKThQIcNa3eN+oTQOOoWxuKAkl0gvZfZ1sSBWCERyvHBUxHeJq6vnAqv8k8G4NulRZGBoQF -85qY+TBm8yoM6G2xVvVxbiQjxGw7I8/IzHX/OqM+6L3HFfRdZLYDK+JN6LXiYjehFyRWdBXtqaeX -erFnFrqVTteXBAtYOOf2sai6sthtgilWQQdgQx9YO5YOjHtJe4YPUjCiYOc6Owjk1rXGPewdIQWc -RVQADPfqgRCR01SU1mjqNYJEkzrFCaN7pRMEC6w0pPpypaCeiVoLHyS/I6vTRk+YDm8XHdQIBZ85 -gSxiOyRDE3JBjEpoLG30Ecx9ptRm9rnWzDakPwBPSAETSxvI+/1NyXeO3SjTbS3nVjJoPD9X60WC -LRCalVRj6nob6+ZcqxXXYPfU9a5aG4Y1VqezMHb49PYwvNKQyCDklYqDHh2E5y1FwfxcUX/EZvE6 -tFFrPhNRADQfdKNkS9eMQHEHBLI2cwmr0+P2qZNCwLgeWk4XoPXqQwwNRPLGuxswbh8wHbUUUnUa -3oLQQk6YTB2iNZHQEBq1qYgpApLmtNura7AsmjlHOhB6BHaA1M7mWt3BLLDxUkTd0e1zKFDE0TgE -1OFX2TcgFKEqfVSCpt5oXEmtagLq1dYUWaIa0Qke28kPpyE3CEpWP3I6z7pBGthfi10js51CM8vi -oe2QmqRhKks3Ua2pTXuP10C63W4i3bRzczfbgnQTRlp5pHdxnUs3YWS+a6SRboq+MXG2IDRmY+mG -c6Bae5tKgVtr6eZcCtxuIt0I/wYB2ly63W5pFxcB6phuv+UobnEbEhlJsg/g3KhMBGLeYFIGZepn -q3YgyRCdwkgiRyixzmPgJme9mpyx1XMpzMaP629s/W27HX4wpdYSV94pdCR0ATQzm99a4hrt4gJo -myRU4HX7cUx6ldIW3uoLG+Yw+JyKByRsLACZW3irdCeoDQ2tC8jGRJQ38m0Bma8ge3tdtRWBoK0Y -J7QIcnv8inIkZdr9dqx/JNM0ynEtpxB6YTYaTaMcsdVpoh8BNEcupn2CBmQBAG3j+EwRzBJ3sAW1 -dr9JBEzLAptb//dGynEdtXZvbf0bQVHi0FpAK+pHc+XI7qFNr+3oR6QcYRa6X6McUUKNbKdtqB8f -rJTjFdQ3muwOaxKAvr0tzTqmoqZitppv4UFfwsbWdGwEA1ARh4rAfp1DaFHb8IO1HFcRbbrSbPos -GPrB4Yq32oYFK0KJeRsGuuwz9rR9Ml+tmC3QhqTFOtNoqqCBG/foQE1p49CmmqorWLhxtqlxurjA -ozaF34yaThZZ4WG0t63UXgjt21zVIH4h9Y319g+EtliDl82I5iQObbaroJ5NJdPXOnIrQjOxO4rB -kEWmL8kWIULYWHVLs5+6clqsKsoBf8OJ6AYIybNj3DcHcXhwTAvW6DV2A4lAx7qWD6DZTjmfdS3f -jli0tHE5n3UtH0CznXI+PVaylm9HX7q4XjmfdS3fDjoBZgvlfHqsZC2f5BRuXM5nXcu3I54As3E5 -nxFWpZZPjnVuWs5nXcuHN4u3UM5nXcsnbXpZN3VQzmddy2ez7+m8nE9cVSa1fBrj1iYB2rwAb27t -GRtl3psXI+W06W1O+6RxcZtHvF2ueKbrdlC0pUmeMfKknQakmkc2+esrBIgBqZqr5K9b7BQ2j21S -2ZySSlvmo65eFffrvQ6L0nxOSaWAMvFvIDT/lkYIK/g0YTtdsp7TEQZt143zbql3g1bp0462ENMm -bGbRJ6vivR2loMwm5uxM7Dz6eSPPUJ+obFfGtVZ+iaFT+FZeM9ysH1xgz1kanM3gHOaXYKJZlN1p -UkxWDyWiMPMWAsRlB9s+ohtlW3Zn7hE42q3AOx5+qw0LlaNi56VAUAZVYkquuj00dYIXcGyrPrVl -UTF13VeW0Ozeg2kEytwzNs4aqjioe3VW9MpWkChUS+j1YwAVy41xuH9jGmszizX69BGVftW6hmml -Ta9RwvzgAetiNI1xi+vRnhfbqimU4jKmmfeOU/n6VdvyJZTRpcS4LLq1OK2bKE5HdY5E8B50yzzz -Xp4+R3OYPNDM4fppCbqop9UcSuvGgl52NTlOOEJMia9qzmrYjL/0JTq6HY9VoBkn869LNF3RzkZE -M99JWXmYutMaViSaOl4c57UZ/bDWyLagzJmZ+1mzLae8mGnK0rSxTvvSPT0MDQAj2bOjKcRs0/iH -zTpfnGrXubWTsyMfp2tYh7Vw6s4bmmhM4XHvyO6gGVsYDx0br0aZa7OKFbOqvZVmyWBjxdwpMi+2 -s5MCCjlMfE9IEcYpRUzJofCcvDxNKWJlz9Tsz2fZt4yqqy1M5lXoBtUW5pldpa7KvLRk6FdhukqA -w6JQz2qz2EAAmZdlWdsdq9hpoFtOCqmMNkkN7LTXhftoK6Q61iYnrU0q01iQhg9sA10OavQc9An7 -N7CwzvoIJo33Y9Gn8IpqzarYb6XojXlKPKrQW5tU6ujNKV1HtbjquHH4IDm3cfucRG9O6fM9Laet -V1+1QvRmB1cWm3kVk/p2ojdoedLnx5uGOurW0Rslu9u2Hm6T6M2OXLqY/N44H7luGr3Z0RbJ2tUK -rhe9UQfvIW2cFgjZVAcB6sScMbSzxIvcUlcam7redZCr7sSQnp9vpULy9Ja2TkFw6oeco/CPecxm -pQjQ/Nw6S3fnDyfxSjC4jGeTNDysb+bnpiefrVTGZpoMoYzGUYnfuhmTpFN4vnpOoNEsGRWwEMGU -FdJOUJ2f+fla+mWM0FilAwLU+8eaPVnwzG2d76XVfKb+zXYr86z2CLZYmWe0i2tau7J+ZZ4q7Vop -ztsgGdawMm+dZNg1KvOMcwdb267Ms8hP22Zl3g5xyKlpEuLmlXk7qjppuThPtYK2UJmnxGxUxXmb -bdzpK/N2TI8x2Gpl3s4fhsV5Bimda1Xm6b01k30hGHfZvJ5kB9WtVawH7jj1UjlC2c7qdJJ6qUu8 -WFMK3G5e/Q/lkdOESweAVMdvrLlZDAFpXOz1uiPWeGy0RSt2x+p8VVXmvb10u7UMoJnXMZD7npr1 -aLAh+HDndD1qFqM+NAQnY+PDbovKKceK6NxgPZZf7hysII1MM12P5ZfHjQpcsakOaL6F9fg4385R -xwiQs/Vo6eIiQFtYjxCKajGaqzV7QLaV69qjwEwTuyE09Zk4dgdlGUezJccDDtKnCXnBZzYazZnb -/XC/s50i2ZfvbRbJAmhbLJJ9+d5CkSy9F91CdBAVivmdyr4dqyJZAGgLQhRAITdWNgJk41/tOCuS -dX7QtXEETB2BggvF4ijYVfOXoKDwo8WoQZNy20yGw/WoLcrTyrRV64UcFuXtiCVY1iHFjYvydERz -FPxZuShvnVjnGkV5jnzPzYvy0GhUdXnWKWRrFuWR4QcHxa/rFuVp4mnOMvugRl3t9HsbJd0V1DkX -q55+r7HTHrd2JBwsfNPnHRoFIZ0EniE0/ZmM61o2XUF9hPK6OxPIjSoGQ472gCySIWDFoUVQD5nq -tupCzWQhh2xB6Ap1SBWrC3Hb8hpfpWfI1apL1uMfzx9x6Ur1QqPMv2U6mXZpRBcLofp9aZ+rw8Oz -Sqe+Vjsze/fFwG/VJmh6XKw8vFT6zHF2r4T1E4oSE3Fo8TdV2d3lKVkRpq5DA2iE/bvHJhntUpWi -ZVPF50ezsrsH07I7fvmeojVbeJqyO8p7YYI1tn8Qv/G8mpXd2RX7zcOmw6Wq+0+3pliPz7rBT7OK -MI+m2E9TdteMMgRWdSmacBw/Uq7E1ZbdwStAx6bFfhGPRbHfXoUNmmKlKucnTROssf3DyGXp3aLY -r2NVdncQM8davd5/MsK6I95TeHg1vGDNiv1uLAobL8/vzLGWy7cVdfAeID7AZypIv4l1gUtvJqRm -AZN24SJl2g7ZAjL2d28q7wCkLzVblBVlCkb9GNGaqPIGkBRPU+vYolWqv21+ryY535uPetAWE2kL -lFbKnrIqLspoE9eMdzyc3YGmdlntM7rMu1W0yVx1WHEoOMjoWvV6PaM+7RAV+du4Xs9x2E53atva -pJopmt9h7qDDm/UM+mQUtlv/Zj2bESr7N/AuPKeZwXZ9sr9dwTHhHRQpqE+DW/9SPcd9gqLT5noA -o9TaVW/lMwkQb1LYt2LMZt3CPr1d/1bWEW3zwj4TG3rbhX1GUURl3WytsG87hZhr7YlrE/y3UNhn -VNWniapvo7DPCJSztOuVCvtWVtLrFfYZTGkFEW27hX3mGytbLewzquqzyxpao7DPyGI0zRpav7BP -myUMq/p27M5+WL2wz8jkwb7nVgv7jKr6drRH629e2Ef0SXtw8zYL+4zMVk2AeBuFfUZzSFo2Wyrs -M6rq2zG8gXmjwj4jUOJG/jYL+5wQbQuFfUZVfZsTzc5itCPauoV9JpVe2y7sM6rqU1udK8OwlT1i -VR+hpLdV2Ge02SIZUFss7LNwcbdZ2Ge6sbLdwj4LfbPNwj6TqPq2C/uMyLHzx9YL+4zMS41xC7u1 -sY946i7xGh8RuVHCp82BAg4L1XZNSS+5UU6rrxwXBKsjPzqTY/Nb/JQ+WZscG97iZ8Sl5PaqOals -7wA2JpVCJzHh8syBteGMD0oLowt7FRfXWbekPjkWCrhPZqY66tZ6XKXvU0d7BvH6pLK+ptdA2Fh0 -i3O6jG80/hIpbAw2sid1tYYwSOx2EsqrbyXVyv7yv50/HEeaN7n8TydsDO//W3lw2kvMdnQFZc5q -+lY8+twwH1p3/99aQyIv/9uxvGbHslRqlcv/LIKQdd8Gdx5pqCNu5Nvc/7fx5X/qdB6Hacda8p3e -+vatR73j4Iyu+fn2ajxOU9eOCnLt05/A4FLuzauJ5ueO8jtUXKrfJoI1fY7uHLK8ZlHjYhsH753V -9K2QaWyqPWG9o3lmiPNix5izI/TsjetzB0U4ihvl5L6x5kRf0tScONB8O6r7pE2UH6ybC250iYSY -atXaXqpVa6upVq2V1J9pSBXQfPVVqCt4YjseCc3GgCxOPZeg4BRFW0CbFpjC0SBAmw== - - - LkUExVSS76gLmO0TxxC0Vap99TdfaKt9DU5gBs8y1op7heMNIbTiYk3TTw+KcXrTEqHMTKn5xXlN -qamZG8LZtMraBlacR1tCAZ6Z31Zl7LCbZm3frmnXawtj7mYbmxcEqC1e7ng3c1BC4UAKCKPVbvQ1 -YrcdVIiZ9m4MSH8G83pb3wjQugWmpJ1W1J/AvO641JXzmsMaV0uLhQvFaQmFs81iaJN5dSVNx982 -hTGOg/ewVnEb9/6REahN16PFvX/mMs10Pa5z75/eVNdc/bcuu6nv/bMqvrAH5HgZWbi4+Oq/rYxr -O7VR9vf+kfrG7qqxDe792zG8F5e4+k9eQbb3/jk+vh3mvji6YduuLEyXAqJPg3Nc7cvudRzU52Lj -R2Jo82pfdq9rE1uwP/ECFWJuodr35dv6eJ8dZ2d0YUDrB7+UcDcEtIVqXwhFn0Rl6K3ZAzI/2864 -kIq0obXztmb1vdGmNp4bT8BgPT5ssh7VhXKUlUwzqqWyqgnq8KbOGA7eO6+lEoe5qndvMTcPW0v1 -ekAXbKxSS2VNNFMXf2XfE1aerl5IpQvew6LHO3MxvWIhFeiTLvthxVsItBZjMRjQWYzFYMjBcVOO -PLhH68swycwUh/f0DUyPkzC/DNPUTisGbfwmx6dqAVBRZ0FIR3W3xWDMumMrWDa6pJAVdia0Obeo -+HTjyzDh3YEqHUgQbY2620fLY+q05dhW6kJZyVSo0gkYIZTK3bgiz5/Sh7hasH0afyw9VR7bpacy -D7VnvhZr14uFYK9YLITOYb5oay5pxcOxuoNiLEpzNd3tfPxsXJl3svuMyrfIKIeqEPE61SQZSlWZ -50v3rszqAaMD7+HJ7kyDFQkbsW7MvBBR2Kv0Q6ZYqUq7cG2CNbaPStVUnKauVnu3wFoNJAms2qvp -Uj5Bxopr5LBlIxI58jBopE0KEffcpjVy/PKdNq8H9J2kJ6qDzbQlgaa3DsKxPltVIU60tY87qvK4 -yqzdNsV61OAGfTOsnFU94PWDWnRqSwIvn6smWBM11b2OWqzXmnmVdtjFLqDfRI5P6VjAuF3aQbsd -VO/50nAEMuJp4nai4mzEDMxRaQVXlmP9LViW8eWcUSa7VXamT3tCHmLo5tHMaX6albp+Lhml2NgE -U8xLkL5X75NhvgDo1kopNubFVZIRrHjS6yZuPZfsEufIA2ht4k7NY/McNwfTR3hrz6XVErcs6tB0 -WVv6uIDjukSbTFAVS2FbwAKaKY+uNEIcVV8pDcyqT2YMqqwbx4S3u/LFqE87Bgc0wbpEyzSwFfok -5oCRJgfhjeMe6fNw1aIrONPthD/650Zm7g5ZUOZMdL2VV98G1XsEmsPvNqiIKis+8o71YY0O3OS3 -8ja2icDgXjaNp6EKyc0PuoYTb73/rawbu5LEjSLSYpZqeRsHXcN6RMd1a7ZeDYRmngPmKJ6mSUXp -MjNtKS+717AetWNh02WW2/GklRiyImzW3kOvWGblYc/QKK/TJAIDD1EzP6jLOsYnE00dgcHnV6gy -Eqr2J0OYGylah71ftVCXqxW0JRba3ai1j2LQnZ+2YaWXXTa+Koar75Ncf3Oc4UyPYnBgqpN90oRI -YZ8IO23FukS1+LepLcUGlHld4paO08C+511gO9AgKMOqI40t4ByadqveumNYe5pDs70peSWi2V5y -5HyYhoVJaxPN8KajtYkWMYWmq2I2NxvjvBjuXrck0Wk9omn9jbOSRKc1VIYurvOSRKf1iKqQ6ppx -aAf1iJih1y5JdFqPqN1bW6cAz0E9ohwgNptp65JEp/WIO47LfEwp4qQeUcqzMaXISiX4pvWIWAqs -XZKo7ZNZWGWHKFrSzbTGqNrgssIdB5fUbeGywh18YMbeKkJsjcsKifDDz7ysUJVzS5Jqu5cVWsm0 -LV5WKHGaLR9sdlkhYQ7+zMsKd3SVXj/lssId6/s9N7isUNUnyAIfjciLSbeubh1VLNN1wfq2Q+PC -mDUuPLQOSVhk26124aGV+4AKyrZ0qpXlbYfOU3ttLjx0mAO16YWHcgGgoU+vsqGdFUdu41SrtS88 -VM2S7rZDTUr8+hcemjI0WnNyUt+mFx5aW/PmyUkrXnhoUlBGH9pFoFa78NB6NpXcwQ0vPLQenMTQ -G194mLW87XBnhctPLC88tB6SSq1tcuEhMVU2x7dvdOGhbgWrMtp2LCokVy+BMq1/wpy2hQsPTRJ7 -xNsOTfPTVr3w0DqavOPo8CwHFx5a+8NqA2qDCw9VBNfddrgGpxlfeGgNxTD8sM6FhyZQRB1oF7x3 -fOGhNZQd6QBaC0BbKMvfkYtkN7zwkNyP0d92iD3pLVx4KK1H49sOlSTyDS88tN7RFKXA5hceEvs8 -BmYFkcux2YWH1ineaN9zGxceWt92KDL05hceWu+VkQbURhceGswrcduhzda38wsPraHs2N5TuPF5 -I/I9hdu48NDawndcSqK/8NC+5MIitXfdCw9NzHzxtsMd7dH66154qMuRVt12KDkea6zHVXI+JDTm -69HhhYfWe9diWsLmFx5mLW87XMVUt7zwcLVy7LUvPNRCUS9GbRL52hceWkMxM9WNLjxc6YpCNagd -/SFA61146MOFHia3HWo3VtYt9Li3Lg12LNPsLjy0Nn60nLb2hYdGHVN87521j5vSXHi4dnRwtQsP -rW87tGUBh4Cc3iq/6YWHjjK6DC48VO0/r1Z4pb/tUMNp6194SKLR33ZoF7x3fOGhafaYSq1tfOGh -dRKVHFLd9MJDNdG0kR890ZwXXqkuPFwn1rnGhYeOfM/NLzz0Wd52uEaKovGFh9YWI8zo2sqFh9YW -o8mG5OoXHlop6a6ARrONCw+zlrcdKnbahhcemvrqKOpsHlVf8cJDW6Jt58JD69sONRUr6194aK0r -9Enka154aKErGpEgMm416qIRseg0VhMmJ7MqEeHwQXIUUsLBkvbUbIdqFrZ1vr+6iFIbAUMhL1J7 -HqkEgHtChsiQmJbvQkH1CVIdVoKMkqu9VViWtut+9sBKr91A6PgiGG4kD6RWDYHnGSG/6/3kr0OB -w9RR+P4imo/GfMLgLDRbssc1LpH0nj4fPO3tni08u/nKTWjv8S2WOmjd75cOR9NW45gbzAOxVuO7 -Ex/1G58ATW70fjk451rJ5OVL7fuuRc/PP1qD6+H4thq6WN51qh53p1P0eb4eo19Xk6Yv/TH3PeUW -/GHLfczz4YPdvRk3Cx1SB4O056nRu/MlfRfu7PfBpEH1D6clnj9NQRt61/tavdhlCldjX7oXyVEV -6jRLVdr3Faq6P7ukqleXA54fngb45SB7LBz7L7tw6Lti5Wf2u+zLJC6f4bTsogI8qnwX7/DCZ32f -Cl1xPpMAsTg3qOQ1KyxKT5V8o5LJZ3rKBZn4hsXjwveNMb3GdKQ1uD345nPT2OXu/eU5zFU3H+7y -PXjgPTp8aB4k0uPCYbNVrR+/t84ykeTRRcwnl6uCqXopB+KNoy/AG76KsFc5C+zyw9cQrCi9AXrm -it/RXiNaJJfR++FYFZGFNoYYpS0og1O0EUmJ0/hx6mjGRMr3he87yAKhhbfkS0a6oUKZqeXA44t6 -7uO2fZmvxd4vfclo5rSSPLjpF1/r+zU0Vqbw6Ctj1kZXBGar9264PeP2lfyHy4q7dnZGl99SJ8Uh -G6LhBMEbY8q90XeSCj2MAkyn1vdTIfY7APX+AUyyCMDPgQrNzEbh09u9INrskUT9/jHylqhQNOJB -fwKi5Wbgz5QP/QlW9c03+DMXkLaJAEjOj34LH5ycvFc6nudz6uMt95lLN3Z50PNzsqt+avouv/CS -L4qHXfmFX3khZnf35XdB8qPPzIf8giJeBA7OBtKLSw8aK11t7LHyMx/RuPoW7MG5kd4FyHfzFAWf -hUQNEb2kYNHUhK7FGgz8k8HAu2+HMvBrH27SndPwOJHrgCx78FWVWTe8IOc6iFv1YkUI6JpCZ+TT -vdoV+lOE23t6ppEbQ4UeK77QRWsUBm/bfvSW8cRSMmHaQYyG8hwk4Nww49Zh2Z/0vOXS1N0ByZlA -fuI9BeSF6l1cUREAkCEFpBE8IqruGCRFgKRD+8KJ7zbNx7K3kct8/LXvxqvK89TaFXn48Rnqm/xk -1hDy5/f37wqTMZ5lfySN/p5gGaZUy0AT8R4zOVN6OqMx95d6V1Hxt/EtI/62fH6TEseYsoft6HQh -VHBEAW9mfkgIAHl5OhAAaPVDtZaMRk7AHyf1XEiYnyIpUFqyzStDAaC5YFjkw/oEexpATHvQQIAk -q6Ml61Nqo6J332A5+4NwWtzgzxcBNgj5yncPNUjNF/EqlyjtRtIFXRQK/ox78dpn91oRBBz6PN/i -jIC1j0q/oW3xhNPg7njMo1wzfoLl58dt6tkwegIkw4vIJfIP+IL2Hw9CcBl7yPWdXMAAMVIE5OOG -Dy6s8wBeI/63OCdLAQAjflcrvo/yfWi2jAvjvWlFKw8gC6Lpg0IhyJQCzz4qVM35RIa+UGYfWyxq -dwPQplmUbYwTcxsDZvwAwfaQZsrey2uYKB2j6k06Azmt9By7wAP35l/yaEKhDX1N2twoDNbhgQD8 -/DyleznqAop/b2jYPAN+prB4NNg4hCq5ESW38Bh1uuexZjToEmxfplXui1ZEpqeythAbn5xjzww1 -ZortbkTle6LH8MiCCwnG+6EWxrLb/AaGRmKJTa+jJ6ZOqFX0bDfzCMXIzQIaX7eq0vsG9AigOdIk -YURnFS2M8NmzAqOlLsuPYn7x5uNTuG6OGc+kEIc60w10wCcFFSb0b8TH7LdXfDza88Fs4ai8bvoq -eTAbSQZJilbsDm/u3ssikwNaKpBfmzPljmnVxgqFTpzF50KUhJZ8qsAbNMz2RMNsfHsJu7oHXzyq -jyVwS3ODL+u+lI6C+KQ0jgcur4W0iTw+KVaf+nZqqQoQDykX9pmd/IHL0sSby9U911wL7peZZ+B9 -5sNdo2vJQe9SgtQ7BECySeFZGcgcxSyAbjpP7itk0SVtGvQcYRUngw5qJkNzDj4czfqTcYztNAyA -+byWAbySAHpXF2h5msEov2S95OiNYFh2At0JKc+NVT9MYSjz6wCAODc6GIWHx8Am1MS+pwxgRzlo -Rn0ZO1gFJOEtmNFNEpdsR8ATRadbIQFVKcSujE9gAe00vXQ6X6SL68Z3MCBctVKrK+OaqnFhs1w5 -MUdupyHHMVZTxGiOxQ1JeW5eF6k6QenT20ZD6Y7yOf5T9a1bjM9JfwIziD5TjQYDRyqf++JCoqtP -ZrKQjrjn5DK16797i+wGpk148Xzy0FStEmigmx4pDaHg9iFrR3HZUNyLBkouq7GNFasz9Tk9vq2c -lCMz3Inj+e3Rrn/w1d49KMXhjZjA0zujd48EIQOVzkKnQpG67iyBfh4cnKGYTTj/XouGhtWzErTr -X2X9TJi0hKcpxjoR8RQlpWzhwUXsF5XOA7Uv01B/LJRkk8Jr7Fuk0d7ce0TUkcxBSQ== - - - 778nfNkYAzzDaBAyyv4RSW7GHwGmXK4IzZZU+MjrzQOT47kMrWNW7ARwLfXw7pDjARTnPdmF91kV -LKMHQffCe56HuyYeihvdZMJH769p0J2Mn67ljqt0L3hclciMgpo83Op8KDHvzFtYTIAB5PaGa3Qv -foys9EeZImm4luDOec1P6CDcc3Un6JvACXL2ccj+orU4CQ0f+2A2e/sRlDv42G+E86WHiBrXgxwT -DMPcZzhpHuAZVnvgx/gIl1eED9LlevgonCwDjnioMe/pfjpcCA3BsyFwP14X7gxVHxWzyOS4SV4A -wnsg4VOA5sztG+h+ZwGTvV+Aldo4Zd5P2mBuPOlTuttgz8JH968nkGjt0OAjXgkfldg0oOZtGrii -k7K88CaS0R6Blg05vmjN68s+7dNMqR+rk2Ez0ZAk43QGLyTDuD6R1hw+dQhG1WnfKA5PcHp8k2zu -i6nswXVkK+odOMBjFH7wi85A9FF0JEILdxCNX3RyIJeKv9FuZC7toJPIoSFHeCtnc8RBOC5ReHiG -rk0xKH1ZCVXKzEUCDp2MFQDHoyO7DxrvgyXiAuc+tQvSk1+QAQONM0K8mBeVkAhNhhgK37iaKHpJ -BkSqt245IHLpJ198RmSn/zKIDpQCci5HYS+wFqhAu+eSxrZu7fQG/nmNIQM0GEb30wfpe+0XowwH -8ZQoSfwXM+wZ9QIZaM1eh8QwxekZpNy1CLd3fYvgwrO8TvaR24nWKOoEYuhABEqINkEbxnOaSyvL -szAOfnrzzd5Ho3R+tttSxB4lXcBQVKfCaLI4UZ20p1Y52SJIxnPdyMjwCtFF84ADaKqvT4nPfGt5 -MCw/9ZswINSmFTZmXpdvOTH+9vgWlpisHSFDEqdxmV/uA2Jc4roIPd77EOJ+zAL3AHj31QN/oyVX -/x6J+hn8LYx/k+LmUdIFOQod4cCg+NnFVGT8K86LQx3HGVQVIa44wDcoHAjNJkoMB96XWRzA49gO -ZJCaG80+3BD0iyID4oLHSoteM1hu8qoKIPZBa07cKXyQvXzpBhKwCo33lqJnlOQAQ7dXSgGhQtmI -G1qRQarvdqdEMmdTaM0jKUBEP7M5tPbdYpQyWwnKYucVenAjHA0AS5Eq1U9mwLXLXtFykzfML5i4 -1YxXsh4ufZLJoeju/wekdjyepF2JCPgRulmOOf6KH34Opy7/zh/pnT9C+TOavp32ZxWe49rcfxal -WW854aYLV8oVyreKZ2eJaInrzfoc/IA8KIPYNVNFlMKFRDgRe4pNnk4C75VZ/D5M9fWhd3ZaHd99 -dQvvF9f5vH9KvwJ9cZco7feXMJ+zVKy9vCK5abDzxiiMX/pIVke1/ZsMW/qgHrNac+143v6GllJt -19cLHu76O/3nXT9Tae26awIcDXzyiD04bJqoN5aIgy7Zm4ImbwnFErKxw+Ky9FThHlDPi6/Vm4fY -ZbG/J0XN+mkoy5L7o2b5Phd+Sz81plkw8IeXfC222zW0BddTSnhunOulNZUSRuNcL62plDAa53pp -TaWE0Kygl9ZUSnD1r6KX1lRK0vJ0rkTWUkqiUetYL62plFShVAd6aSWlpMQid7TncRypBIC8PFcQ -AKnXq5kbBcsLnjHfBKIgBs8VKJSpYFsvALRn6R5jPvTmE8jh8UgDac7EpQi8JMwlhYc3AQb9gqHh -XvMImPcT6AxSlDsoSNZ0EAc2UC4gMq5x+FzeK5NUR82vxNrBF3dwlmro1IEcdBuCSM/BDSiYEFoL -iTvrBinANUpmlCMimxftrmUX2W/txhoOjKv31PCzWzooSgHNTlpIUokNiop2z08N5EEMsODnFRYK -2ccZEpMeHMAmZl/aRNWkI4j7qIr7UNAl3elWxiWhdA7Kb1E5snutjdXgHfsAGR3uJc51EeaFW5Bh -NDURZp6PeKvBSC0/kaLYM68mLw0HwwMXRDDcnXwMKwFEFITdPSwclCyi6YUQAYCuPFRpZUMJJygH -J1BTuZGSCg2zt2XEqKGhP5VFrC09u/uWnj3yoeHV1Kdsj4VP7y7PVUuAjKwqMU4puGoTWcXn1q4a -WVWFuZX4nGUwzWMazVYFi+m5PKQ3uAVBxIuZT9U50qPE3Ek02yRS+aKLCgaIaDaA8S5FszMMmi/0 -wt09uHkByj9S1R4EXXvPLJSek91GWJUw96NFmDvVXGwS5kaXsiFOW3XfQneRFAaAhm4EQBI2ZjBQ -xvkmnUApsgAAJNq6MND9Bs6oKW526WHcpkKGMEAn5sQSALab1wEzoqyH4qHPEchbGncfH68uH+Z9 -cDNh3sh2wOvRwJNGo5kyU1LBfVRaxFWtFRRckFTfRDugKzxSn77oHeU89dj+YeSy9E6yItuJkX9+ -cXF1WluG/PMtd0oO6XgQ1xCNmibIBg1fmvzzLZ4h//zMZMWwKTDfcRiWjGgZew54NNuPaGk8B5wE -u/2IlsZzkENCW45oaTwHOJqfEdHSeA5obn5CREvjOUhE23ZES+M5QOP2Z0S0NJ4DZujtR7Q0noOS -a7vdiJYmnIUDw9uPaGnCWSjy9BMiWppwlhiu23pEyyCcBSMqMGjVKU/7ZMAKJmGCRy1usZyjJtFO -gfscThvsDw4IQ9qF/6HAP/BnPOmimYSLiUbBH1H4tNHd+cONWrtoj6sBExQ7oTy/KA17i+FsyvI/ -XCn07OGicXtWcqVcuHUHtE673KBPVAc0B688KFbWgT3t7PxBufLwx8NfO38spX8p1xX4EYxFwnQ4 -7qKC4QT4LQJ+oWLgf3HYS+VZNJKkGPDfeCRKJROuBxZ+j0cCwP6Af9XBb1/g2V+uiOvC9fxKufo7 -4B1YmnSEDiYoOuwKh+NBKhllXBP4kAnGqUTUxSTB23As6QImcDIRpOIJGjxLgpcxGj2LB+k4lXSF -I5FgMhZJwGd6iI2dPz52/riFHQFI+zt/xFxujwveV6cMNYo7TwUj4pjRGCNxOEg4xv+fvXfdsWZH -ssOeQO9w/ggYD+Ca5J38OcbIQMMlCJAhQP7VaM2csRvY2S109xi2n96xeMm9M7lYJ3fWZh3UpyNA -0un4ageDwWDcklw0+D/yH2V+wb754MxPOqo36wJkBsn7TElapSZdwl+JxHbxptL0YuXvZDrJiv5u -PbdT8norSoCIXovim7z1f7w5FeQPsCgijLDMQrs3BXvCMCZsQj+OPBBQaN6JdkGLXuWJHLl9KPTf -P9hUWFyEop2PIm0VfDMmK9NyCYKXCd6tSVdrgnFoLRJifZNJqVmMD7YsevRLtQQhivlkokmu0Myb -X5LNpqW1DhtRxSBEGVgEa0TlY8rEaFwaaWKgsX7p/ZsMZ6x7c2h5V4pfQPNvi7Wp0oLKJu3EpJ1p -E9lPuWhb/fRPbLOsbBMMdgvbVXT7/a+yiv+lLvB+czMb/adPbKzHXQQbNeJZxMEYLaImGwaqJAo/ -MpvgB46yVXnlb0FyfnEfrPNhCh2zOX7LKggX3mwU4dbzViEbXiwfvgLGqELT8I7fKZGbTLJhFpNy -VGk+7C65VwnbWPZjcQ3Vg+1FWIkITxh6x2+W5z2YxHp+68v0VFTFKGzc7OSR2yShlZZFMEn/pGVA -45TP2j54/YFLpL6zZ3hKcqfFqSJENBmXYvGP8nuxI51zEFiU3uRXAU5ddGicdlX+zohPu8Ce47xI -3dn5J6T8QktXWr0tEQsva6xDUXq/8EMToYG44/l6x5jHCMnnMYIRTa1P5QqdiDfG84zcl3NupZIM -rdxPRv7DK2Wa6juTZcbAPHnPceqGPRucaG7Ty3o5PF2weqb6s4Hp5HRm2DxxkFSPXMQXusjLGQE3 -+k+Ie133F9z8IbavZ7P/KqRPYkFGpmCd8yxZmGQzDnWWjJ/U4rfi61Sd1XnEG2M5cZfuVbaOlEur -SchphTt+boJ3fBkm6Xw38FMFb6deOpevK9/pKjB5qGHQoEozBJpukq4F6b6QwpEVZyQRJztwZCKl -eD/ngwcBmgVy6oFPF+//ZdRV1D/9w+/+9LfcRlQ//eOf/vbHf7z98Q9//eOf/s//+PPf/q8//8ux -pYhWpn56kO3nYj8fSfnvfy8yoIv6+9xpkP9pRO8BRUgUNbmQu4j//vdRUszgHqjvj1RjHqkbiwF5 -z2Pcff1Fwt/947/8+b/9/JP5p5/+w7/+68///Lf/6ad/+N//9hfR5E9/55oafyp/9Pvf/emvf/vD -n/7559//0x/+9off/+6ffv8f//Df6990fNVP//C//PnPt5/+7r//4S9/WP96/+ff/emPf/vjH25/ -/P9+bsv0P5tFDMuJkfzDf/75D/KTf/nj+p/+9V//+vPf/o/6F0tj9pef//nnP/3t57+0X6olwu/E -7pf/9S57N41//F36/X/4f37+53+DRPtJtdn841/+8odNQb/76R//7W9//uk//+GvMrLIfVdRz/c/ -/be//vyX//vnf/n9//bz//v78ld/3Vujkj//05//JLvJxrj5tH978GywpVf5N53ExYiHkuHg00Lu -YYHoXPCZuPiYvXUmeklE8mosptD8m00yrvbmzdY+MohOfI/2essaQVuc1vkPF2vKHzoEDxSJQRyi -T6YRlxhSJooK7EZE3wlEv/hCtEkcroHDDZJkBbURrRVafNOS2jeaSyZmogrGtOnsJ/7g5/Jck5Jt -5G3pd2StyJaKiL2iAPkf2xwWZ3UWQnnrGzF5WQGImyS/2/S34/mJNuXlsqufxDoQjU+CTrfj+fqs -QlIyMckoUc5JpiahJcsNohW70U7cnoSUsa1Qq+p4TkrlNKLwgsgnA7noNtl3g39k/P02ObKcpPKd -drPY/s1rI8F+EUNbSqLMdyJdh47nDLmPIja5FbKzBd4w+o+cRT+XjuUkdWtrc8vMxyUnBU+4F6Rw -RvmsWatT2NS94zm3vu2FWJkQz/nNjue0XXo0kfWZeCKzhJHjxz6WYqbnOEt0ye0lukqhgA80LhXR -jy5i6EtocO5YTu2mYTixMhnNbx8duEmfjq0dy4nevTf662J+qc3L4Clg6WWVWxezX/qhkbBUr2M5 -wVdiDG9sHiOZUMQ+nYx2Et4YyzNiX8/DgpYwk78Aq7eo7oo/Wiw1BerXe5bTjIbJft7L0zz6mvCX -3c35YHtO2uvB9mnD79wkFYaK/To3eT1FoMZzXdrrtnPF0x9i/Hq6uKxiuvJNVN8z/iPLSTZjgs/j -KpXUVvKdquY7v3hjLCe6mr3O1pF2ac8CcmqkQPLzlgJ1LCep/HHcp5oqnXLZTD4U+pUdcL4ERBxq -FDSs0hSBJpyszKVlOy0sadlGk3K2DUeGUjpEpxzxMFTToE4c8Y/eCddR9I5NoZ1/i96Y0gnXBslm -fCC/78hmAdk2cmMyIO+ZfPNeOL7yRGSYT/fCtVfYN+oH6YXv++CvOfqtrUGUcjmYy89iifCyR4MK -+cucMvUYhOzmjAKB9VhKtqvKiVpx5uJi7s2CI8vXhxsJnOJOcFhAqpzgTDlTC6JOpQ== - - - YNP4St9EFH+vo+gs1Q/2oPkQM1G3z9wdy0kRHgPFGEuFlj/VrGz0oZw4QKuCy0Tlkm7C73l+XbSU -5U4xxPwxVOuWI4oFIfaBqNxSP18I0fqSOPrUrMW9maDxvViCj1JhI0YkwkKUAiW0nyejVSYqY+JY -G1xv3BAWY3S231iOS2aaNil/yDXGNpoN+G4vNIlgm53vJ/4YLbsttLKdMdpDbLPRXfk1p777nZVN -VrKWxdicyUmZ6T/SaKf5juMMJ3EUsEptUUKDGN2iPlrzbiYdx0muTWkIGLem7GkjUUEmoJAVil6d -Kl3/nuXcircXYmVCPGP8Pctp3vloH+sTPgG/Tha1sfw6hFKt9yynVev6zYqcOcn2MmTT+z40jNwm -dbA9z8ltKlUMQjv35hE41pFRn3WRPctppsPM/rqYX2n2WObosPayzC4spnQ4j2s/tBIasDueE/oO -GMOWi5iyRUOR+3xO0Yl4Yzynmvx5h0/zB9jNYpzOlXxamt1c8vhX7AajC3Ub/SlX/8GMHnnOsBvi -Z/qBhyK+0NF84tvzceVHOeZ5h9Pz/LpCQuN8Z0TvSqFgVW1J0hJ1JuLM53ulqZBJemktMrJnqQOi -AY3WT7QipIkrzQtp2O8ThHc288dK4pylvg82PY0g1FK//GQWd3yn5T2vg1l54n4h19N14o0tOc0e -Jzm+x83TqvVzhXm3zW58P86KNb2DoNql7YejK6HLMEnjj+M+1x85KpfNZCz0d+uSa4OPJgk+V/7D -qXpQXEVJOq042438viMbBXL5BvLAZEDeM/nu7fG0lDk+3x5XC+Lbb93xYXdc4bterP48GFdatcm8 -LbL98q0OG11JuK14fr/YfOvUiIMoRHy3QwtJLM8vrhA7nhN6SNrVD0Go0TDwWon5Ngocutd3Ecsn -P9GRrbfuQIweHbsk/5Hq9aojz1mRVSOZCkXORaLoJvxu9KGcFn27oApxuQu/5/llaS3W2y/44I1r -SLgE1WzIeV+ITUgQvSt/uVhrGzEhAJTPYLr2re3bspSegl1aL1vyJafqPalo7VgZXG3UELykWxqX -2KRm09FtRJgdiD6Z7edL0rEQ8bH5xqb+kNf222hlm2O4jeiGY1vzi1rk3e7KVps/zfsc0YO16gOl -MvV3PGe4iqOITW5lys0+vaSP1p1NpuM5ycVpU84pS8Jf5D5vKRq5wBKyamOonwE6nnML+F6IlQnx -3BboeE5z00cbWZ/yDfJz58uvla7XejuW04o3KR3lf+GgXdP5PjwMfSf1so/sJkbF3lZOO0EVMKrK -qe9yt/YLlnK9zbmXYB1twstzmmgsNqjS8Um6ffx8WPKhXbAw3XGb4Bsxhtc2nyM1IWwpx7lEopPw -xlieEft6a8jj/mu+Wi5+1aniXPr1pjZM3XjPcZa5UNHPO3WaAV0T/joy0+nYek7a67H1SbvvXQyV -hUr9MhdzPR+gpnNZ2OuGcy0b2IXz9XRNUCVfgoRao5RUd9bRDGGSwRhfTig73z72ny7DOqd4Yzyn -xaWj0taRemm1CTnRwcXPt5yn4zlJ6buBnyuIO/3SyXxdfU+XgclDLYMGVpokHLNM2tOgXRpaR9Ii -jWbgbCOODKWU9mf88DBQ05De++Ef/aA4etvOofwI7s0sBbsUeCemnGneyO87stE7cmMyIO+ZfPNO -eLnzEOwF0BSXsGt+Oyj+QSvciv/GhRITxRfHerbMqjcYl5WdalIDVs4YCMYJs6XBporvEi+eXD4H -emPcXh9nghHnDrdlJEVyDaoYMwclFZRLyJbwRyKurSc+AP0qsUimlfA5tkCAH5hNiumApxIfl4cx -IW1CPw49kDChEQfUJKHlKyE3wu7roqIFqhxElAVOprYVhZihvECMrYml6kVIoZkU7vhuElWyZWnl -zZ2YMGuxOpc2osSWlInRxDRSxUBl/eK7N40v7wbXqKNuJI9PsLBtU+GsnRh+tmkPm96msp/zY0w8 -bJWVbQK+WciWIhvvi/C/9zsI5qnFaQBRLYgPTRVymCiR6PrAbIILOMpW5TVQsNBcPQY4WOHjHDp2 -c5yWxfGVgJMk1lfMyRMmIXs9XwcUbgUw8Ua4za1fOxFWIsJpG++4zfK5R4NYz295HKeLeXZJZldO -8XT8ZtV/AB+0C3I7WxqlK/H4A3dI/WbPcW6bCfVEzJiNkoW6Jn9nxef8X8dtXojujPyihF9o5ECd -XPCpF0srf5cajuV+vYeWQWNvx3NCsyCPoYqIeVutT6UHnYg3xvOM3J/A/Y7APIVDwD7YEESPxspM -gTnwjt80/9LLfdKb08zmmtyXwcrPBdJzkl4Opc/ZOvGHTBQu86s84iegvomdXxX1sr1cevxjH8PX -s+l9FdLnvBYtO1sQuzqOk2wFyAtQTlLGb/XVuVqqc4E3xnOiZ9krbR2pl1WMEBOTDICIrnjZHcdJ -Kn8c96mKttMtm8nXled0BYg41CZoAKXZAM0oWVeC9Ff66pDVXyzbZvtvZCGlOD/jeQfhmIVt5nl/ -+H61MQ2f2z1CfOsG5u0eIb4bNbodlndjMSDveHz3brVfMJkrCN8BPudHObc9A+FbHIy4op+MB6Zw -aVOBmDGUQFzq6edM9MDV8st2pgEIVbKhA6R9s9rcYasWeANlJCOI218u+UiE/OVi4obH7dAyMFrL -QMY1Yn4AAUSJyBsYb34qAUS/VFQvG4HGK8buo2RXFYoSxPz8gk9v4mM2osNLByAqH9qE9lN/8HJ5 -tvl7vHLljGnWi/g0RC6oQCe/4b2KNnQWQnm/zSF5VeYgHvmuwR3PT7QiP4HxfZzEOhCNT4JOt+P5 -+pQCyFwZ+MsseLwg6gbc7NwCuC48hvFgQL2tMKvqeE7K4jKC2ILIt9jyRuLKRv/I+vt90vGcpfRH -/Wa5JbRo7DAnyc/i9Ad7ka1Ex3OG3EcRm9wKpivE4LX+0F30k+l4TtI3jiegO+ZjRVU/72HwnorB -OQVRrTV6gyff85xb2/ZCrEyI51xnx3PaRj3ayPpMSJFZLl7SefxaZnm3mx3LWbLjxAqOiGRYY2c2 -fPW9lxj6ExqhO55TO1H5eI4HRmLYLoZwoz4dYDuWE118b/bXxfxSq5fBE55/yiCvtQPYL/3QSGjC -1/Gc4C4xhjcpj5Gvq65P5aSdiDfGc67Fn/f4NPvEUyDlg5V+i6rZzSWPf8VujqM/5+rPzWiO3XRu -hmqSSvg6N/MJoGym+evSPq/416IIqxKbDF5arP2QclQTkQswLSFWxGBd3A6I7Rgk3bLU/9BwRhNp -WhrQ1JXmhTTo9+nBO5v7Yxl6ylDfB1uexg9mqM8UoY/r/rnjcncwG+YCz8p+Xh3/WivowXhUgWeJ -//pUGf+ZR2j2BrSe7W3cmKXRlHWSu91t2tZyONdP6rb3jfGcGON630T1S9tmnRejKzFJ6buBn+vs -dfqlkxnL/d1a8zr6hhMedmDjrqGKhx3YeCO7sEMVb0wG5B2T796VNxqv9V44Q649Mub0o5whn9CW -l1iWorDABzVdj/IAviljrIEouUWFqzPgF7P39zFsmE4m4GkDDWAwuyFpmohEUOONprj9PJn88C+2 -ediwtK2CdzOqPHeXiUG8MEQSojYNfjaUb/4g+hR1Iy4GL+UEsfag/EbESwf4Hmjak+VCzKkZiNpV -iKxu7o8JEaYbcEgiX8mJVTG4qoOILDowMWxSJJN5A1/MbwDS0WYIBvWWUi0DO563rwOLRnKT0Zfw -JnN0G+yyWUqNrZX5WNP9mnQ8J0VGDBRj0nkg7xuE7nH0se0wK+t4TmvX4Ba3Kam8t5vs+8FHYtJt -07Gc3DhY3pJFR2MBjkS7S+eFkXbZdNrGPxr90OZ7ltMs5yjm+sze7OW8MZ5nhL/+Ga3zOetAWqb+ -4pyc2jmnnuXrs8Let2SrEY0t8M1+2flP6sV7L9TxnCH3UcQqt03QohCj0+7DQNNPpuM5Sd8AizAO -MJUV6uh0aOL7ueM5t9fEd2snxMisqf1f2qyXPM3RRNZnchH8PFkcp5Gfh1DR7DueE2NUdCWeZJzi -lQSZYTSi6d6R44S6E0MUYFaPYnF7JedkQnqU8MZYTlP4AkRnn5sh4vLjL1j6YQvTPLTnOTUt+MDh -/GK46aW9J2UXHM6TZkPSmV53Q7lfmM984sv3UXmj4uR8WtPz/HAOrwUZX/L5UuNwljS2BUkLMmSH -V+8anjiOu6IuFaJeTO2Skj3L3A/LmmkVQOsamnvQwE69du/f3/t5P5af56z0fbBnaNijVvprd+RP -+cGnZjTyg0tBwWBeiw94lvg1LfnegtbTfY1bb2s055jkbXdbtlUv51pJ3ea+DdzArCh99EtUt7Rf -1nkwsgqTFL4b9rmGXqdbOpWx3N+uG29sgyX3O2xz00DM/Q7bvJG934GYNyYD8o7Jd+/GW3Grbrny -9OeSEFh/a8YPUalVObya781E07DDyhcd0JbWo8XnaV/+cAMmFmLCu9nlc4mrz/yZ/MBVvsKztGau -BixmLEQ80wAicOHwbLKxcIam/CXeAI4QCMRUAdZALN8UlQzekKpxCwTJVQzwixsNyZGQmn/Bj5f8 -TjmIS6ydz8O8HxKhPNWUhRV3FZtS1JuKNewFEzfUY+fQeon+zlqmZf1SLk4bF+7q2/G8/RovrXST -WAei8UnQ6XY8J3SapJIoXyTRBFgqkCmI+cYVordvYL3UUphNdTxntWkAm4aXvHMrQFeld6OPbZ/t -ko7nJKXv9JvlNuVwECJ4qMeF+U5kC9GxnCH2UcImdn76HDnUYvzYWZCpdBwnKTu37XDQRdsGon3S -u2hEfVWsKQaz4WbuWE7upHYyrESG55xmx3LaFj2ax/pMLMnGbEumqzfSnuEsyYG/qHV54mZZaonW -eYehH6FxueM50TP2JnM6fAJL0nmdU92lpSnXTOZyR/IowjrajNdnNdFybNClu5RwHG1lSz80Epbl -dSwnOMqQ51WeejJRbRnruTy0k/DGWJ4R+xOY5hEQqDGfU9Oufp4nq05tmXn1nuMsm6Gin/bxNIW+ -Jvt1SPOzcfacsL9CnKVrQKU97TSvrcFz+5Z5SqrO61OZmB08xvf1bE1ZZVxCPpSKGdQncA8MJ2kb -b5ZAs9srROdL+M4j3hjPaV7mqLN1oFzaqICYGskPfq0qZk/HcpLOdwM/10vp1Esn86HcL4WGoavA -5KGGQYMqTRBoukkLXFqy05qSlmw0KSd7cWQspTl0yo+NAjWN6MSN/fAgMdE0PHK/AzXXDb3c70DN -GznsyBuTAXnH5Lu3wMsFrL6TfQLUPGLX/HYgfYxRLS5cKfeTlf9Qqp6/AwwZoF9BzHXzrRLFsqyR -LRv0HUE14bVeqTdMak/64FOWFxtEeDVm+3Vw+BSHyqRW4MCjtuIR7aK2O8dAnUuFJOI0ZOIQTaG5 -UGApkn2LLliRMAjfAugGmg2QOr5prTZUY8CP2XzVX1cBj3N+9HGYZsqCWggaGoSfZA== - - - YyFPXoeGiB2c1IOQoN0UAsqsd1lQydPMprYdv9uv0fw+TmAdSEZmwCba8Xt9ShGM2GBGTTRv3pQz -N6A5vJURbXn6Z2QdvRF17CYlcDJOQMQz2ROXM5Xd2AM7J9uhYzdH0TulFoBwo2XfW7Ns9s52HNF9 -x26CxAfpqsBSOWVSMGHsD8gsjuzmqFjLDtJpu2l40n0AJ9BkbEUrDracJztym1tzdxKsvQTn3WHH -bdY2PNrDej4+AE7RR5V/6++w93t+s+o/vDmkETyDQ7URq7r3TmDgLGiQ7TnOUjkxlLMBEUCTwfuS -zjqdPmEr15HYjyKsfPtdn9NEmynAoTguZ5NuQKb7ZR8aCM3XOp4TmgYYwweXx0iLDw3X/GRK2Yl4 -YzynKT1IJBGLEKchZlSuYZ/0iyyd7PlNtfaTEemcpFdj0pPWQvYnlYUK/cIder19zWzmE9I+bzav -xk/WESEIIICmHiaGkEZiFYj5qdT3SrSoJwFLFsMdFvm4U6nboRGMJcYsx2fpKEn4WHzv8oB3NunH -2vGcib7T7cKiHTXQX/cIOXEcpyU/q4p2epyNRRR3ivQ1p8Y7k1nPtiBuzLZYMjrJs+62Z+sOnGr3 -dPv4Ntjx0x7o6JwQ1S5tanXuiq7DJJ3vBn6q79apl85lLPZ3a52jCx6Ka/dvsWG4REBwmTvx/ZFo -DYj23kXPv+fUHYNv3iy3Ck8WuSsvgC4B8VP/1iwfgqpLXpGsBBaFdMHbDTIKd6FA1LX1lIkAGsZq -hJA2rMqCmuwlj6nfwzMx40IL8RFxypsYMlHrpWB/2fwtDY7WAK224i9GmRc+vwnR6Dtms8cby0iS -oq6/DuIyJLZblQrqViOKe7K4J6OXO/h6XEImGquq6MeZP6Q9ebIhS+vRWNENs1DclssasEGFTTAh -ZCGEt2nzcjZ/3b53dXuet18JU30/iXUgGp8EnW7H8/XRBeBvygIw0rm3aJfYkPCUwcdW51G7mLGp -MJvqWE6K6BmGL2WERSlIfdV5N/rY9tku6XhO0vlOvRWaXOPhIdl58ssKcce3IlmIjuUMsY8SNrEX -pTMxmTD2FWwmHcdJylayoXIXztXHDs57FyCDqozyhlNv3m7a3vGc2w3ohViZEM+5zY7ntF16sJD1 -mWgCEFVZ7vzj/DnmRjjOklwqKrUkvAUGcb3ZUEJ3DmLoSmho7nhOUzuzmdMBFPiU3iLZsKgV/Gds -5joU/FGGdbQhPzGvicaTAQ6x0I+AxvvVH9oJTfY6nhP8JcbARSQoKJ8DW5/KRzsRb4znNKUvpdpF -2yHFYJ50lDQT7XieEf56IpmHw+G/xykwq6W7kYamnufcfXs+5I4V/ijt9ZD7pOkzf9Mt/1DuF/qb -66kCNZ9PiEsXZGKqsI/27emVX64yq/B4YAOfQqLRPH+YZDb4viDDik+8l/rnqvrON94Iy2n+cq+x -daRZ2rmAkBngXv41OJ3YGkxS927Y51orR9XSmXwo9qvfPTiuAJOGWQQNqzRHoFknLXVp8U6rS1q8 -sdSc7kFuJ/XFg1NueBjsaFhkbvhHPz6u8S5oWUtcoGho5nZHfN8R7fJAbD+nxMeff/M+eP7gawgW -+SkUc9ktP0gbfN8C/9xH4GalgGVSTqUcvbf3S6yRXDKVz/QGn99u/66gLBu4hIgHbFO9nRrwTRgf -aPGvrb195Pn6KAM8sQIX63EZZdkQTxdtS8XziJKbn1I2Ue9Qch1eVQPRKlvRZo88J4X1DIYWALTm -cYi1PG/Sjz6UU1ZiWTBNIRrjbRN+z/PrYiTwE3Fm2GagwDvQcl4LEPMZjVslYo6wjFhfO8/YqD63 -BnxMdwpgs4T2iBXmcFcLRFO/WFNNcJ1RK5AIpPMpBrx9pcNGlP8nE51aNmh9nFoDLb95c2PTfgyS -3RZa2cYgW0gPNxvdll+Hgr/fWRXceUlJ5fzN+XbHleqUab/jOcNNHEVscuNtdySji3bpg2Vnk+l4 -TnFvwh4Yjd40uMJzRqLy2Y2gs1Z9cg0e9s5uboHbj78exh+FjZPTmeaTj0axPuELMp6pQaYrNiHV -md4MZcdyWl0uqo2iMKTUUdfjt11AGDlM5ll7lpNbUks5W5Zf5UG0WEemfNoz9jwnhvPe5D8h51fa -fQb3zVCLstDBFs33qz8yExqpO5YTugx5jCVkpenFbfCoZzKJTr4bY3hG6OttZJy1kzGleDO4pq2a -2juLpZZwdOg9u1nmwuT+JfeueVVwuyz3J6DXPwyrg/KFCnoprD6NSNs5RSoIF/mFTvF6v5va+SfE -vW4013KCfVRfefjvS4Eq+wLMYogppph4ojDJbhDkMbCRmm6rx06UXp0bvDGGExOZvcrWkXJpeYnj -CklSHvxcdGP4MszR+H7g5yrgTr90Ml9X0NNlYPJQy6ChlCYFNMuknQzam6EVJC3PaCpO9+LIXLbH -BU54ZBKgaRSnzviH73ob0boGVg3eGdfljkLFO8GNoI38viNb9UjemAzIeybfvf+dTHmB5BJu+KLT -j9IAnwMcroCAlm+LubCBZEtiX26V+dCwv4WIBwDyVTPdWhYITfiWVT5R+I2Y8Y9hu3fkcAkj9UKb -b1cy0V1CoxK8Y2g0FQDJllS5dtWI+WOclqHvsL9aLEPkkdR1sfFOzCTRh2kkj/ITxHYctZ/2g5PL -Mw1ZVkkLfO1OJ7x0sbic9iSltwkou+BGSwDvejsnFDQwSCvudRtwz/MTrctPAIcfJ7EOROOToNPt -eE7oBmpTHmnJVWwsB6kzMV/WE2IwyYwthZlUx3JWDg0AsWRrBb7oDch6P/pHpt/tkY7lJJXvtJvF -1m9hSS4nb0kv9oONSNahYzlD7KOEm9hLSaDFCaUPXEU/k47jJGXnowp6KTcs16ecC1D2VEYnlHzQ -hg27es9zbqHbC7EyIZ7zmh3PaZv0aCHrE8EEgIQ2lTqn3UbpOc4SHW/wALoUx+RUqF+mOg8xciU0 -LncsJ/rG3mZOx0/IqfKpah8g52ds5jPQ4XsZ1tGG/MS8pvV3loIpiIVeUhW+W/2hnbBEr2M5wV1i -DJtLJUA+O90gWs+lop2EN8byjNifAg/XFsfQpBy3ujbW2LJTc6a+vec5zeNgIBx4fRT+vKenmXTH -c4LZ0K3aDTwU8YVb9Tru+elEYazlRxP5NRIFavuf0Did2LSIdYz17YmRXy4xq5zeW6msFU6815Z4 -x3OS7SOZEf20m2JPVPSdd7wRlhPdzV5n60i7tG8BMXH2GD/PP6HrMEnlu4Gfa60c1Uvn8qHYr8UR -Z6vA5GF2QcMrTRVo7kkLXlrB0xqTlnA0QadbcWQsG5D4iaAyDNo0vLOg8qO3xdHoVnk1w5uNyt6h -xFV9wzxT3x+pVu+olQOn7jl88454vtIQr4CIh/wY1m/t8IGXM+kt4q1oi/ii6rNEdilgbCAupvUr -CuCa5PYSL21t4CEoZdxBwIfd4cLh0i0+NeM8VP1xwBv3+MNFuQIx6wCDKn/otHiCgiAuPgztKKEo -vWykiM+nDnG6fFNNBlhk4liSpB+uHEMFzeIjYEpvEkpj/a34tJhpylR4q27Gj84Nk0wegF/inZQv -nXCDR+JxJjBBL+XtwHwTCKKKCPkmTpkQ/hWSxlhfJOkY3n6NNng3g3UgGpkCm2nP8PXpRNDCOYNB -4Ta2CxUqOr9TYY3DOzZmaCC9HXXsJmVvAH1TxuZxnCuq7sYemHq/ITpuc/S80ykExvkFyYklfsim -Lw8c0U1HdN/xmyDyUbwqsoJCASJWz/1Rn0Cm0fGbWxAGU7wRNCRlrG0mgu+2NgIrsIp/zsd07ObY -yE669bp0N8Ju1l482sR6Pk4EfJJK+adSqphmJjt2swpAgBhm3EBcCDUlKHeegDsMGml7hnObNiqI -7vJwoTR+V24HZwNmz3CWwZCdeV3Iy3vzmsl4FI9YYdsumHXLPjQQlrN1LCe0DDAGZJEh0pI2JM1z -SWUn4I1wPCP09cRK+bfoUeJie6ITuQ6slRkCc4M9x2kWQ0Q/G4VoSnxR9Msw6Cdj0klZL4elJw2e -uEYqDBf7hd7xE1DozOg/Ie5127mUEuzC+nq2SqyS6/xmjuxmr2maMMlk8nMhMuqSlnuJfqoaP/rD -G+M4zcccFLaONEs7DhDTIOWRX2cwMbYGkxS+G/epnkinXDqVD8V+LdQ/WQMmDrUKFk5pYkBTTFa9 -sjqclYysJmMJONmAAxupOP+nHPAgOrMoTt3vD9+8NqqBcocHUO+l4XeHB1DvRozhEb67/Z5THxl8 -99a113mGFzC9PbbKj4LpPQXMBKctA4KJB+SRVQ0wK9+/tfAD9SwmQMscPshZfW8dAvXU41kH8Z1v -xpoKCnbk+foQAwQmZfNLVL4gvlfkXvkpoJrCm1kaUHJA+YmPPOi1NaBkIQYXM1H84IbMuuc5Kapj -IJe8zgPlh/hWNvpQTmBjaZ9p+ZvhjbH8sviI5fYJMnqDd2TiZkIZaUmI2tS73CDiuWQYRkvTAWm2 -xAXfSgFupdSdmC8fAafLbT/3+dSKEEWZZqgMrjVqB7hZhbJY7Df5il4KImZvJTRabbafq6h8Jhpr -dbP0/dQfAmW/i1a2N4a7iO43tjO/Bs+k31zZaEXrCqpy5s1GbT9QKlN/x3OGpziK2OReoHGHdkiD -JqfrzibT8Zzk4fKpCPTNXLINZfqkpUgWmd9ph2qlAt3Qyfc85xa6vRArE+K5LdDxnOaljzayPuUb -5Oc568XPc258YzxnCQ8wQhwdMfhKY1RT/D5GjPwndbRHjlN7UwBH9DhWY1LaLpNwmz7tJ3ue0yyH -mf0n5PxSsw+4A7yovM5iwLYBeO4Wf2glNHB3LCe4S4zhlpiVJuXo9lrJydyiE/HGeJ6R+zrAiU/o -H4Tcfkpp0Q0ltDNaagzUufc8Z5kNFf68q6d50TXhryMqnY6456T9NSIuXQYq7nnXc20Znty+zN9T -jX5iMhMzhX2oX08XDFVOhQ+k1qHw48nDJJVDj7i01p7sOV+hdb7xRlhOi1F7ja0jzdIqFEIqpEEW -j/42vOw9x0nq3g37XJl8VC2dydcV/WQFmDTMImhYpTkCyzlpp4O2bmh1SUs3mpbTPcjtpIJ8n3Jh -w0hNYzpzYT96a1zHBt2tlgeQb7Mjvu+J/oG4/ZwSH37+3fvi+NiDh1KfBzlxKZ+G/60vPgb5Fs+D -r2E2+yW7gRGLX/T5E51Z3AbUaw2wi4wtYAyZiG94C36eIc9qm+/I8/VRJqM2FZQf95Z0w5LFQaVQ -KjWTlg3J2Vn8pWyiGNxGcy5kml0anuyR5aSojoFsAE6cwkMN/o6YvBt9KCdQ5hC2QDRmUU34Pc+v -C5Gy3C5AyCCJ6hLDZkLalQ+lRqcNC28xNmTDiL4+mCLElD+p4rGJ1F6UAxF5Ll6l0O4OpaczySz2 -A11wrTEzkBE1HjaK+RKW2YhKxUx0ymy/Xrwq33hlA2x2vp/4Y5Ts9tDa74zhFqKbjQ== - - - bsuvAvk+7qxssvj8D5A0yd+22VCVEt13HGc4iYOATeiocyK66PTRkrOZHDlO8mwL7vt6QM+F2NCm -zxkJfp6WEEoRE9Nd2Tuec6vzXoiVCfGU9fc8p7nng4WszzgF/Dqf7YCFJFfh2I8sZ9WLeKEVER+5 -ddTLhmm/Dw0jx0kcbM9xYlDsTea0G4ScBa53iSJnPSx2zWQu99U6GdbRfvzEvKb1dmDDuN+74HRX -fd+7W/2BldBw3XGc0GzIY+CrpahHL35LX09mFJ2IN8Zz2m6NQAfFjYHceFLPeUmWTPQsp1r8+VjV -1wRU2uux6jmzYTuVSkOV/MKN+gn0bGI8XMun/eWT1vPSIkKliq0rQydtY3M/NiFyCTHago2QJV9Q -WQjRhFS/CZE9Sx0QDWi0eqL1IE1bWVbIQn6fG7yzmT9WEecM9X2wbWgApJb65TeNuf0y0egk6HQv -er9rKeJ+IdfTNeKNLTlNHCf5vd3eaaX6uaq822W3wX6cldz2LoLqlzYfOmdCV2KO0vcDP9cf6fRL -JzOW+7t1x9HsrgDeaYcCrhvcd9qhgDeyTzu478ZkQN4x+e4NcmsxmQsnxyX+Fcz73zrkow65iukt -hOrWk3INk83jAW58msgASHDg1gOnKOWrp87YevEHH+xyFwkAaLVreWQ5oYukdf0OhHIn+A3t1+Oy -I4j5Sd4mocdfitgSPNVGDPg0Ky4p+Iq71fGcFV418qlaY0qtvCEs70cfygnQufwtVn4R1YawvGf5 -ZaktjEXlZ7lxGcmluFlQfvUa15FiQ5jDBSJT/nLDhk6IcEjf8RnMbrBoIlQq96WCv8PqoTLLF6bq -sUeqi5HSiBnY8rXeisW291UKEamXEKNvVwptmQaI7dhzP/OH1LbfQyvZGcMtRDcb25Zf1CDvtlY2 -WZl2xjWTeL4988N1yrTf8ZzhJ44iNrnzU+BITmIDMx8sez+Zjuck/5bfD8epo2XZ0CZPWorGg3Wx -1MSLTRue+Z7n3BK+F2JlQjy3BTqe03z00UbWp1yDxjtfeFkeP69XmHqes4QHbuGSbD5GJ9V8aji9 -+xAx8p/U0XYsJwbH3mpOu0PIKZlTKs/jxOUzVnMdGfwowzrakp+Y1zTbSeUxOiz0kgoyQL/6Qzuh -obvjOcFjYgybj4PjxeWwvXByLrnoJLwxlmfE/gQ0eACUccg9tdyHWAfrTu2Zuvee5zSzYcKfd/Ys -Mbom+3WE7dMh95Sw1yPuk3bPnA2Vhon9Ql/zCVxwZjvXpb1uO9feD9kH+vV0tVDlzLcpvWQWwVme -O0yyGm2zerY3fk6XZ51fvBGOEx3NTmPrSLW0BIWUCkmQ/FxM0dBFmKTv3bjPFclH5dKpfF3Jz9aA -icOMgkZVmiHQnJN2OmjrhtaXtHijiTndhgNTqYDgpzzxMFTToM5c8Y9+cBw97wLnHXeA4KpCf8cd -IHilhh21ceDUHYfv3hzH/QeJMVcgwT02zQ/SG58ACa7wTnGwMhwykAYlqcq1JBCBZnOrNI3nDIRm -2vtaClhOkgdp+MN2KlTpgmSjs0NcXPt51CpmYlxi9vjeFyw2XJ2pl8m8e4tOxAEpqXJ5FzQrv9Dw -UaGc6cAzwXBsIqb413L0ATT8xOCDoCqgdHg5WFwm/iyZiil2nPGje8MsncnziWopHwoUFOmKNha/ -bHJqiYiSgIsgRRs+X7fPYvr2csuR3yeamZ/ArTxOYOWSkQmwefb8Xp9LWFOsTTuLJyZytmzrZTnQ -GjgrtQ1iQx2/SZmbjBN8cnkc48oZ627skZ3326FjN0fTO61CYg1IQdlJi4KRpeGOI9rv+E0Q+She -FTnidVGhuYrYRx0CmUbHb4qWNRyQJELyA5tT45M+BP1Y5G3aIRcsBwo6dnOL106ElYhw3it27GZt -xqNNrGeDhPwyasm/8UvrSg3ecZsktZTTOolyrBe3YXXV9d4NcG9Bg+yR3dT+UsSRL4R+G2S0ciuK -2e/JUNmxm2UoxL6viviF9o3GC4A9sbbyf3PudlzukVnQLK1j+HpHiCGEdR4iLKbmm2fTyE7AG+F4 -RujLyVQUz4ann3V+Sa5YODNTZgPMa3f8ZtkKkfusC6cZ8DXBrzqWs/HznKiXI+hzpk6cIROFyvw6 -d3g56jOTuS7rZZO5FPf30Xs9VwgWsY0Vbw9cC6trunJkN8VUQg7mCe9OlGOx5yvuzgPeCMdZfuWo -sHWgWdZUyN+bkso/NrYcoO0YztH2bthneh6dYtk8PpT5lV1qpn4mDTMHFjhZBkAySFaXshKbFYOs -2mK5Ndt2A+PIvZtT7nYQhVm0Zt72h+9KL1C6xoIFgKnXc9tOlUi7Ud8fqQYgMDq2w9yNxYC85/HN -G9M64vOLMs83pgOMyvzWmB5eJkPWL5ngTxovnNa3KhH0MvYRiIsx5f6HryhJWIyWU2lf8ZS0wR2v -eovOV+QlUbxkjBXBSn4eFrhB+ctlKZc1cTdDRQ3fiHeiKyCa5A7BLjoTMyZSI6JOtDhoE8qFIUlI -4yKuS+Pd7/rsHIgheREzqe0tMvxai+fLRJG3SnSc+oOXy7PFu8Oy83CLrSA1A0dMw+VHFE3Bb5It -2fktd+xImZhXEgshb2ui9jxvv8Ztsm4S60A0Pgk63Y7n67MJ3BnTapGUTPvNZ2aiyCvE/K03jm2F -GFXPclL2hoHy29wYyLWDiN3oY+Mn26TnOUnnO/VWgAcdxK3oYLaHLAd7sV+InuUMsY8SNrHxhhKI -Ga957C3IXHqec6vCjDoRbVZTezUPuhPriqVb1hBpT/ufjuUcg9lLuH5KwhtjOW2THg1kfSac4Odh -iS7/3DuzweXseU4SHvch80UVyOa02exl7yKGMZdG547n1N4T7qcWM8E1TmQdK7eI88G1ZznNdvrd -+hkxL+/Wa5bjgbiJVba2wMP1Sz80EprsdTxf72ryGAu+7aLXpCqs8fl8tBPxxniekft6KoZnQ1Mq -Hb0QjW7XtDubZcZAHWPPcprZENmfCFA0lb4m/OVT2ueD1Tlpr0erJy2fOEoqDZf7hZ7yOiYLNf1P -iHvdfK5lCvtQv56uMavw+eoXHFN98rJnOcdu8sUsjLskFbfK71xZ3znHG+M5zd8clLaOtEubFxAz -6JR/nZ/cYOswSeW7cZ9rr3TapXP5UO5X45d2i8DEoWZBoytNFWjmSQteWsLTGpMWcDQ/p3txYCwV -wPSUQx7FbBbcqT/+0dvi+CwgJiirodP9AUy92KqKSn3fU8MjdePAqY8cvntPHN96zHIF6tssBZLo -x2iKT8H6xvdfeJWET+6hZoYIMr58oVtS2m7T2JS/2Zi35MN2E8vF3DRIG0RBx3JC9wUvgidJtVDo -BFUxBkGMyuVYngv/KqFX+EsR27d7q/hitKhMy/HpxljOyqlkIGfEr2Igb+rX4m70oZyyDgtun4Oo -9IZ4smf5dYFS6fIQVn6aZmkQoQXnDrSts1vh8GAV3sX6AaQC52mZgI2twVoh9nR+9r0+Tyg/d1F0 -mokphKEquM6YEQA4zrlsutHWa7YgBknB8S13A7mXXyuxp0zUrfN+nPZjlOz2z0p2xWj70I1Gt+QX -oZh026pe1lTOxZzEmYbzzVVKdN+xnOEijhI2sXODJOo7IjBfdDaXjuck14bXzjWuZVbM3fOGgp8n -j/M8olrbzp10POfWvL0QKxPiqR3Q85zmn482sj7hGPBr8ailugnWbOhIe5bT6nUlOralHsgt2ZVF -h4Hv5E62Yzm5VbVUg9BRhivAiNyoT/vJnufE0N7b/Sfk/FK7l4W2CftTFtqZqvlu9Ud2QsN2z3OC -w8QYMRhorQTas0lFL93twG6WqmMSb4IjcAFvX5jn3CPNJI4c527S0zHqlKzXI9SThsJcCxGGCf1K -x3I9rhK7+YSwT9vNyxEQgSkgU5eKx28IiGKVIRO9KWc+MxGTCfVdhPd+l1JHQ0MXrZVo8UezVJoC -0vjeZwLvbNYPdcM5A30f7BYaKKiFfv2FYebvmGR8DnS2lxzetWxwv4rr2YrwxpabpoiTfN3jnlmf -KcF3u+vGd+CcFIT4BKpY0mHofQddgEm63g38RAukUy2dyVjo79YARzfbRXwaMfEtIXAVnBJnCr7c -Rn7fk+Mj+c5kQN4x+e7tb28wmQtA3ioksUHf//K39nezVOVlF+cbOeLJw1JfmPISZ/COLj6n4CEA -7EmgKWEjl0/39Yinwuc35+XXEec+a2V9ZPl6l5PMW3XZi4+5ygRlWUKmbZiYEK9IvKh2Sxek/K1O -Fx9z67hNcu4JiZNK+ZBYe5z9MPJIQJUf+cgfWfOz53kqHb+vS13FPrRLptwucjpsRoPolK8XhQaz -V4Gz8t0k1QykQmyVD1vVvnwF48o3oCr0BYh6Mb5cgbJRD5UxVFu3/vmBLkgpRdpScf8U3qd2NhO9 -CxsxmGAyMYPZ3djMH9PX47ZZ2WYYbhu6wchO/Jqud7efYK2SrgVbSq1QT8gOFNopvuM3wSkcxasi -e3wPRhoSKtj4aLm7eXQcp3gyH1BOoVZr5+5P20dCwDdZqdGWXLVjOLckP0qwEgmeMvkjw1ne+GgY -6zN+IOHbWijXIJdUAF47jrOKBIPKz8RcI+ZicSXBYOgoqUvtWM5tn5lUjAEH/VIo98mJKZ92iT3D -eTH8aO2fEPLrrF1ZlIEu5AVuoFT9qg/tgwbnjueE0hJjLGiSiM6MU+Xb6/n8oRPxxniekft670dq -MfGq5eii1vVJD2awxBaYM+8ZTnM0RPLzzp1mPtdkv+pozgfXc8JeDq/PGT1zj1QYLvbrHOT1Rj01 -+uvSXjedCz7+GNvX08VAFVOCP1rhdivqOpZzjEYBDRrq2e5UnS+/Oqd4Yzwnepq90taRemmVCTmB -9Aw5t+yn4zlJ6buBnyuEO/3SyXxdXU+XgclDLYMGVpol0HyT9TP6zgyrI1mZxrJxtg1HZlIq+nOe -eBCoSTynfvhHP+yt8klEh3WM2yXtAmCiF/9Aft+TU9zQ0h+ZDMg7Jt+95Z0vLqgr8Nw6lAPwv7W8 -hy1vCeHW+viT8cBLMg1gyzgDODPZrEo3SOp8UcRosz20AEA2LZ7DeMCe1Y+cR4avDzRWl+tyZpEk -qT5IB5pKMdNiDJt4Fn8nIpugG7J3dBHT0G9p8Q2Bbc9vUmDHW4qhDGOs3aCQd0MPRAS+HN5PAC3i -B7ee3ddFRlniGCAhXtZ+NJq8Al6VxPZWiXl+QjRBbVBtkm2lbF4qprQRPTqOyhbcsPrzAvYkxLhU -2GWiCqoytvr4eCaWZLwkrs6ERlNSUEmZLLnIZtj5NAf+LpkCedfP+jE2dltmJVth2zJ6t2XY1qJ7 -8Gua3d1Wgp0q3If8yTjJFXx5PIXqkui8YzfBIRykqwIDiA60/JzzaKXJLI7spngwKeJlBzhVLGQ9 -bxm23mQwi9+e3+n4zS1oOxFWIsIgPpya0iwHfDSK9fz2t/Bq4lnwW2vjZig7fpPkjg== - - - yLFxUiQA79BWde+c/8AxUg96ZDe16xRDMWtkqhnAceUmfNoJdgxnGQsx8utCfqGVA4xTbCKvr7ZL -w5DeLfnINGgY7hi+3iFiCJ3rmZyxb+/EnMsTOgFvhOMZoa/jdkvlrsQ54A3SEH0FB+3slJkA891H -drMspZd67Mb1x2n+7aLUlyG7hyH0F+oRIujlCPqcjRNPSCRhAr/QEV7H6+6N5bqoV63lUszfx+31 -bHpf5DZ4gUaci5Rn5c3ejuEUS8kQCtBNjH5DvT5XSnWe70Y4zvIoR4WtA83ScjFLmX8rcka6AJOU -fR/0qVK2U2s/iS+rypnqe1mYIbBQyWI+yRtJD4J1U1gNSKoslk+z7TawioLTfcLLDqIui87Myf7w -LepFNYzt+AjTvTRA7vgI092o7pF6ZzEg73h88wa1eBOZjLvQoA4Z8/43RJIPEEm8uA6zyKY0+StA -w7/S+P6MA/+L0htAs1e43pXgY5YKKwyPFiQZsQAnSxVy6sDy9TEl4yspfL4L4c2bJW1IvAsQSEPE -+7thk9DgL+VfXVw2hO98tQlE1S7ndTwnxXAMlJ9PxkDbgeZu9JGcOj+zizNTQlxMgz8+8vyyuIjl -Dh5foA2gpa3ZTAiPUoC4mHo9FUTcfodhuLBUoE/5lcHdHUSdFNxGtPh4itBitG8/L3BL8pfLkuJQ -GwO9UUvwUpt7nw04uApIKsSQ8AAKopi6A8DnM98gKqtUM/X93B8iZb+NVrI5hruI7je2M78ImKTb -Xdlq8ca0lzCv5T98+kClRPk9yxme4iBhkxowkKDlLGy86GQuHcs5/k3h2brcCHM2bpjRp8wEcHAF -aE6SquRdU/ae5dxytpNhZTI8Zf0dy2kO+mgf6zNOAT/PB87wc+8qCE/Hc5LwQPhT+XAIvrjUJnwf -HoaukzrZjuc0zfdGc94RZmjEfOQmCHsdzXWjuQ42fhRh5dvxM7OaaDle4Xkmn8ph7JUt/dBIaMzu -eL7eWeYxMtAl2k3KqAaGejKt6ES8MZ6zlG6SRJjF5A6T1M7+ST/J0omO5QydEzvvBx5J+EI7vw59 -dDrEntLxrxBiqe18Qt/P285rQYBFBfnjH24S6ZQa2m0+uwmicw3wV4gxvwLty5Xr98GOpe6HRjNa -QNGSkGatLCekIb9PDt7Z1B/LiHNb7X1gfyz+0a329S9GUfNlorE50MledH3XUsT9Mq6nS8QbW3Ca -N87x2/ut04r1c3V5t8lug+04622I3kNQ/dL2Q+dL6EpMUvpu4Oc6JJ1+6WTGcn+3NjmuitT0cnnE -6zYVmbtS33dUiXlvFcLogQOn7jh89+Y4PvkY8hTlL+N161RuKPzWHR/jdeN1ruzPDdKvCpCkFN6w -SOFNLbreKwtvNllc87NvKdQ7Qd4XgBzjgFtVm2N7hhNaR3h+Cy8joMYJsr4NiXeJwB8VZ56jZpXP -4y4xhPa+gTfDDSqVibp+3e55zgqrAOsyQO+Ugby9ox/vRx/KKcuweJyNEGJNc3uWX5fRymJ7pAEm -f/m0qtnPEssHYlUrUJCSLnmD90slZjgLfDX2sth3YO5FI5FHRqS1az93EZca8QrEsvihKrjOqBVI -NhzhVfH50dZH50AMYvn4pmuWeppGfi4ZsspEXROCbt6P6Wy3f9Z+Wwx2D91mdEN+FVj3cVtlc5WF -RfaGGG5cTB8olKm+4znDRxxFbHKjOQJi/iY/XnM2mY7nJN/mUFEZYPm1i74nDQW/Th6vv4hmbfQb -yPie5dyavRdiZUI8swF6ltO889FC1ie8An4tZlFy22Bry6RjOa1gQ2cIN0MDGq9eN73vY8PAc3IX -27Gc27CKqeLtBTwAGbcZ9DZ92k32PCcG9t7sPyHnl9q9LLRNoXR8nClnnvrVH9kJDdo9zwnuEmPE -GPMYKTm1QWWeyit6EW+M5yylx4grADp3Q2KoHxDPOkqWUHQM527Xs7HqlKi/RqRiC0CEfWLDXluB -52yeekmmz+tT+bICAkPn9weNBlB2Cg2iOt+kA1HS7NL6BdGXP1TB164p2arU79BIRgsnWgnSlJXm -gzTc94nBO5v6Qw1xcpHfB5uGBg66zF+P3U29xml5z+tgVn64X8j1bIF4YytOk8ZJLuNx76zPlOT9 -Lrvx7TjraYvOQVDtkrZD70joKkxS+G7gJ/oinWrpTMZCf7tm+GIa+nbaQXjrhtWddhDelWwMyGrD -6q5MBuQ9k+/eEVe23Dm/AOEdxQbDbxDeH+CZuFQuP8OdB6U23OFg8ntd4hid2eC6xZGU+6+mvf4G -0NP8ucal+znQjufrfQ4QmvJ7vihwgjYNrjc/5ys01w5SQ8D8rQ1PGCm30XBHx+ApJuc3tN4dw0ku -PiGNwuvEyUl4KT3kbuyRkCB6vKUCoqgmT6fj+HX5rNiJxsPa+QKSq0+uwHjytSDcQAoN0nSBJdTr -S5KTF6IENjwfXr55tc4qoh0sCnekgtlw/iTy+XJJytWfM2UM1dYbgS1P0Umx8KaSNY0oi2Az0bua -s+DmNF5sB7E9PNHN/DGd7bbPyjbFcPvQjca25BeBeR+3FSxW5oBHshHJ20aj+uwV37Gb4Bz20lV5 -fT73bcpZoY8Wu5vGgd8Ub+bx3DcG2M7in7aNlB+UywqNphza7xjO7S0cJViJBE+Z+5HhLGd8NIv1 -CR+QH9SJLv+4vePXMZxVKBgpDEMtIfOziCsJBEMnSd1px3JeBDway3mXl/ETy6kRKc98jJet5Tr+ -+FGClW7Bz8xpmtWgGPQhL7FUFRsi5X7dhxZCQ3PHc0KBiTGW+mKRkd82pNST2UMn4o3xnKb0IP4E -pwiNOBYd3HPekaYNHcu57p1OgUt2es9enMOTdkO2aj/wUM0v3KyXQchPZwbnDOXp3OC1EMG4Nbig -Xeyk0Fli8z8ZYxcyiqwVIhh3mXQ5Tel0fbiRbVnqf2gsY3USq/pYgtpngCzSdxnBO5vzY6VwzkDf -+aKRsEet8+u73tRfMNHIFNhMLzqLSw+77FdwPVv/3dhSsyxxjpvbb5ZWhp+ruLttdRtswFnhsfcJ -VL20sdB5D7oQk5S+G/i53kenXzqZsdzfre+NTnaF4E4FgnttCCcVsDs9InM3srE7cmMyIO+ZfPe+ -N64eBdkvF3C8fTlE/1vf+yMc7wVPD1i8AG6qzxRisnhnwfu3FCuan0PSJrQA0OSC7W3frBI/ZzXe -LCi0nuHrfY5Vb0kh7fOSKzpXcXuTZGVCwyk33aQz8q8Q2NkK5YvnmKzJtCWWC75HbpMcvM0vfac8 -TgtOx6EHEgKfUDLNQquOs2P3ddkr0Pu8tbLCeJ142SD9MkZbJi7aNluQ+blsC257AxWHa/ElVoxL -qoq4EZ2wMmJmaWmPxok4SwyFGG0a6WKgs371JcuCmcFegy49oEyTEGm1kkDkNrhvK1PINGQENzbr -x/y12zAr2wmDLUN2Ft2BXwTjfdhJMNPlDVpbzJu2Vg01STR+ZDbBF+xlq9KGJWRafuVkvMrdHA7c -priujIpgF13uLKynbQIlV8TDT6LLBiTYsZvbAOlEWIkI5628YzfL9R4tYj2/72V6Fuc28FtfPkl2 -7CaJjUI+4MyDjSKrau8+7Lz+wCNS13nkN7VhgyNdMAFkqG7ZHtvoLPis8+v4zQvTnYlflvELbRw1 -qwaKuyxue9zkuOAjw6DRt2P4emeIIRTyHxkihpQaiOq59KAT8EY4nhH6M+jdbjFiA7jh7jadd3bK -bIB57o7fLFshcp914zSruSb4ZQjvkzH0nKiXo+izaMGdO2SiUJlf6BAvh35mM58Q9rLRXELy3kXw -9WyCX2RMBn0WwPulUlce+U0xFoNH/jDq4tX2KNC5WqrzgTfCcZ5n2etrHSiW1ouQMhQhnbqjve8Y -ztH246hPVbOdYsk0vqwyZ9onwjBjYIGTZQAkh2SdCNJTIaVgX22x1Jrst4FVNCDvX/a0gwDMAjXz -s/8jIHmXSeNr6mLChuRtlXqgvj9SLfD6FxvunesSkTl5z+O7t6gjnsLpz1f/MpC3pLPQxo/RoP77 -rkn9ChBmY/GhLro3HaPaoJEiPvGDeEejwnOyWIkNnBdwjhpvdBk8Ul6BkUA08AX4yLTcgR91dlj5 -c1S7WhJEMOBWWS/D3KGrDeDnMjGm7S8liU2ZGNwdeFcyVjgpLd7KbzQg9Jhk8I8bMWqnC1HZ1Ca0 -n/iDi8M/Fjh8fLJMFeMPr2Rgc8bcDb3juCbc+U54SaFgSCl8f8bJYBva4ys9w0+0G68Dpx1nsDbJ -jN5JRqfApnpk+PrkIYObJSy9jsXNVSBmiSNAyMN3uA9MhBhTz3FSrpYB/0wKeaCgG0TncfSPjL7b -Hj3PSSp/0G6W2ryJgxH7DfdjenwDkmU4Mpwh8lG+JnR+8QhRYKkArSMPcZxJz3KSpj3un6HxpaPf -0ABP+JSMPBmWlLW6PVLVcZxbtfZCrEyIJ1xlz3Ha9jyax/pMAMkoncnZ/PNolrvR7HhOEj7DXXq8 -naNlYVK6vwWxcw7DKEvjccdzomPsjOZ02MzIi9mQNA4gLvYTVvMZuO69DOtgQ46ndYy5HctppoMj -L8aXZdZabfCpu7UfWgnN7zqer3eWGX4XF2WgoHzbcn0qBe1EvDGeZ+T+FHKtyZ+S0LrzxWzowlN7 -ps695znLbpjw5109y56viX4duftstD0l6/Vo+zQIbOdqqDRU7HMedG6SwM1+KO0vOsbrpnPxTY9d -mF9PV5RVzpg/bfpQ2jE0dZhkNh45twxc3695oojv/OKNsJzoaPY6W0faJY0KCOlwglt+HLTzfBUm -KXw38DOdlKNq6Uw+FPqlAKR0BZg8zCZoUKUZAs04aYlLi3ZSV9KqjWbldBOODGUD1D/hiUeBmoZ0 -6op/9O438LTLpI0uz8BsQN178vueHHGsfakd7TuTAXnH5Lv3v/FNZwkXoEk0NlEiMN/fswM+B6wb -4QeXrnBLyDjdwJBChtm3ES90lHwPEJ8xf1zxb8rWGywAqpJy4yeLTy+LqZ2NI88J3SNrgKAKpDRx -Vsk1MF7vYynZTEtTIWEK+DQors7aDdQZOGygmVBv6Bw5zkqqbEZITWUcVe5odIOPhAT6scvAsQ6X -tcoBpo7jJH3vdJulBjaaBB+Ec2fMhn6sJUTmb5+LKGC8Ch3PSXJbhN5oJOWuqM3Marl9A8/P4TM1 -Ljv6hh185DnLVGIqyFyImiE1WLv9Wo+sIuMyBtyIsgFXXMpfHjhObpB22luZ9kZ+5OSKTNynvfBM -JO4ugQmHGeE1jZD0J2T/DPjxToR1ZNDXZ3WqZfR8Sg7jhUOA7WaQ9wbRF2IpPKMpjzKB5nFkJAFS -ZjGNqPMXJRBDeRQebikoXY5kiIXFRtQQKRNjOX/RO7BGROVil6WUjO/NajPParXvg+jXK7P9pYFT -lFm2wy39tq1Er6B2IbZX4DsdzfCeRwffvH7uMEZcoNyg+qWY1ndFDiNBx3La/j2IuQ== - - - fiBmUPKHm2VUH5/f+cy/lppmk/2R5bS+ywJYR1e8NJ42WnvXPXLxva3cGMcJxoIxZGmzb2gPO3RG -el7CG2N5yt9cvlIfZXN6XIgNHk42Nr0fXR515DQt6FlOyxWY7Kdjbe+1bpeFv47+fTZXOynt9Vzt -aWjWLs4yYajYr4uzn4Mt74znE9Jet52LD2zsPPU68Ol9XlDl9CmYLGdsfv7IcpLRWGQuMq5xrbd+ -SHFu3IP2fvHGOE50NTuVrQPd9tlZE9MkX36tPF+FSQrfjbsOJGSr0CuXTuVDsV+Lct+vARWHWgWN -qzRHIAUjbTew7gmt72n+xxIrugkHdrIB3P+yGx4GahrSiRv+4RvUC7CW8AXDqg2ep8B/qAIZpDaM -nUdyArk09h+ZDMg7Jt+9QS0pSp7MBezsgAdJ+9b2bw3qZrCAXLOq1JAxljeDAfZjNDDuHZ6YviP7 -5c9ouMbpkP9kIj6doQRFMuPq6bUjy9cHGwBSJVxbQWGGk6AFjlBHvCghtAxVVOWr39yclEJ3zNyo -82e1DIq1QVztOE4K7gBmw5C5NqsPO3Rjj4TMoHked1y83R4f6Dh+XZDENRCZbbnxsyF25S/o+cKP -u2MQ6hRsJrajFxmdM+Mq4/uTas1tvMeBt4ZxH6khXwN7Ki31RpIxY1UMlEZtQFIPHBGxkoyYUG8R -ghi1ycRo7B1jG6/CgBhcuRJ5nPljmOw2z0q2xHDvlF2m97uM7ccvw83eb6qCQ228iiV9Sw1Yn+qT -aL7jOME7HAWsQjvtSiK63aen601m0nGc49KwCzGElp23ofadMhHgQgoxl42LSg0jc89xbpHbibAS -EX7B7sPHk5rlk4+2sT7jCwCimWy5n5jbsTfCcVapCDxKj4+YONRmUwuE+4AwdJfUsXYsZ2md2Mt5 -95fhEWFFxkqJ5Uq4v2Yw10G0jyKsfCPy1OncrCYaTlgAqS6rvOBk3sqWfmgkNE53PCd0GVA9JlMe -DXK6gs2eTyU6EW+M5xm5PwEsK6V1dCaf25OEwDS83G7hmTkzp95znGY1RPTzPp4lQddE/wQS9ckg -e0rWy0H2SZsnfoYKQ6V+oZ/5BPg6s/nr0l63nGvva+zi+3qyKKiSG4XSLvo3bcuJ4Y7hJJPxyLKj -u98hOl+GdR7xxnhO8zJHpa1cubTWhJQoa/Fj5Ssyfsdxksp3Az9XDnfapZP5utqeLgKTh9oFjak0 -QaD5JutpsA4NKyZZrcbycbYNR4ZSge5P+eFBkGbBnLrhH70BrvC2A476A3vElbNHwBaBDzDpTn7f -kXEu6IFceXDqnsV3737nywZixpcQtDWs67fu9xhBWzajQRzHdVRbDyEKUdJLYF6FtxQbmp5REXiz -OF+bGuJowc4JACKrn7s7hq+PNMBZypA94c2l8v4RSIvHOTsZ3rsGmIyPcJBY9BAbSWwp06rAR2aT -YrqFAw6pjKMLKtpxaC6gRx8mhEJaXMP823P7urAoCrYeEnpbvuY2gwGwZSYubrMEmZ7LluBs3PD1 -os1dJQk0vkJlg+hyx9ELH3VHtFtSKMRyHo2pgmusW3gtq7KYbKqhdGxBWpB9B/2mYvurZHGoA6SW -gnQTfoyIx52ysh0w2CpsS7Gt90XI2ccdJFPBu8TI3XDtxxbEA6bIXt0ds9c7gaNsVdygQqaFxfjx -Kh+ncGQ2xWdJGiyL7YADZyuO4ymbkM2erzlCmcaXqw1HdnOr106CtZfgCSPv2E3yuZ09rKd3vczO -Khi1/NQXoJKO2ySpJfHOGIYmiQNM1ld8272/HzhD5jQ7flMbTDJaxdOTP0y+Sd8Z8Fnn1/GbF587 -E78s4xfaOOAwdS720EsKFS9yv+ADw6Bh98jv9Z4QI6hciiZ8YNpemziXFhzluxGGZ2S+DpuNLxjA -g7VS7DvXEDo7I2UGQLx2x26WnRCxT7pwmtBck/syaPa56HlO0qvh8zkrJ36QSUJFfp0jvI6YTezl -uqyXDebScxm7yL2eTeuLjAmPwFjrUZEplglMMRWD7j9GXcqx4fO1U+f8bj3DeS5lr611oFZaHkLI -gJQGvy6nzjuGc3T9OOpTxetRr2QWX1aHM+UTYYgpsHBJoj5LG1nbgfRPWPVHKiyWUJPdNrCKApd9 -xsvywMviM/OxP3w3GnPNUNeA49/Qsm2qqmjU9x3VLztY7MZiQN7x+ObtaGukGFhIU/mX4bJxzUD9 -KHDZU8BC/FK9rihZfKBpWGZRA+8nLSjZ66Wl8KYXhSs04mOc36BEdf5WYoEc5ioE15Hn66MKMJDK -EaEAXPjaBwQx4nmLKPlXMKmJaBT+MuUvieZOTDET8weLG+M5KYxnrCYkcxgo1LdRusFHYoJYEFBB -VGaD7tux/LKwCGMxFl0RA1tQzYAC3hTPNKUroBw+laG4F7MINtzhwyQ8yZIBRcrqjWjwYQ6XdWql -AaLGMT3Qoh+rYqg0YgUOL1uYbL1JQvRGDCK6tW+mAocKKWoXCk2FDZ5wN++HINlvoJVti+EGoluN -bcqvaVT3+yobrMbjZngRT2K49fEDjRLd9zxnOImjiE3ufKpDiEk1SGS66N1Ueo6TXJtHzYcmmFGq -QTSesxN4Ap8RBESxLui7tnc859ayvRArE+KpDdDznOaejxaynncLmKTGFWv8OFbUm47hJMmB3he8 -RhRQ5UH1lYSGoeNkHrbjODEmdhZz3hFCzmxGJkjsWKz6hMVcRtHuZFgH2/Ez85plOkBuBH8sdG34 -9Ys/shIasI8cX+8qMUQ+zQbt5JecG0jpuZTiKOGNsTwj9icgtGNtF6CX50suSBedGjP16z3PqWb/ -RLyiGVAn7fV4dcVjEv0/EajOzWiC5TNfQ1XJRXyhr/kEjjaz/U+Ie133157b2MX59WydUKWMKqKD -D/hf72nmMMlqEPYxbj4u2oq1c3VZ5xtvjOfErbpX2jrQLq0+IabD84Eqbs+39ywn6Xw38HMFcqde -OpmvK/fpKjB5qGHQ8MoyBZp4si4H7drQ6pIWbzQ3Z1txZCul4D/njkcRm8Z26o5/9N64dhs6tikw -2BVLWx/I7zuyNTtyYzIg75l89+44Pvmgu3IBSxtXAX6Q5vjfdw3yT3s6Ja5Dx/xVy8CXVAA1jysV -mVjfv8hEQAkh7EfTgBwVnrLNFZ73G/yqVxlBJ4oTbE0EBcBRm4k6tGtG7s27Bdd1vAxj7yjMS3SF -GOtF0gxzgLGFGG2FDfO2OD18fFt0vR8Jorc+E22smBQArVi8KsTtTbnDxB8dHS4sZled8EZBKlpZ -ACTlU1aAM6V3nSUDMJB1wGurd8KBqxPzBbzwpmxwbbw9z090Nq9XYt0k1oFodBJ0uj3PCe1Cq4v1 -mShxKTVMaV3sFOmb8cZ8YCvMqjqes7JoIIJpwBRjIJXiJvxu9I+sv98nHc9JSt/pN8stmQoepkb+ -5oxXH+xFthIdzxlyH0VschubcjK6NPzogbfo59KxnKRugFhbY9/Eejbk33MOBhbhHA5bAPrOV/iY -jufcarcXYmVCPOU5e57T9unBRNZnAgpmma9UwkSSbnDMB5azRAe+oMUN0Jjv9TUQ3b2LGDkTHp6P -LOf212S0grAX8/XR1GbQm/Tp6NrznOjge6v/hJxfavXCf4nWlXVWzjT8yd3ij6yEpns9ywneEmME -POcmSlNxCRss6ol8tBfwxjiekfoToN14fCuIE5EKZXHlgBQ1WWoK1LP3POdu2tMRiyfPnbTXI9YV -u2f6Px2qTs5ohuEzT8lUSUV8paf8BG430f1nxL2u+2sPNOwD/Xq2xqxi+hR1FjOmwFOHSWZjUdTL -uMaVC7Zni/reNd4Yx4k7daeylauW9i2KlDgtih+rxBdhlr4fx32qs9Irl87lQ7lfi9vdrwEVh1oF -Da00S2BZJ611afVOq0tau7HEnG7DgalU5O5TrngYrWlcZ674h2+IL7rBbusCu7023JEK0q0f0Lg3 -srV6B9JdmQzIeybfvSGupKhYlv7U9wnsbi8bx/4o58Vf3xJXLqJdpcsFElNfMXKSfuR7HbhB4uwG -C1ce1xLi9koNApMtF0AW1fpjQjT5WY9Q0Prrr0OqBaLXcYP2rd8TUajXygSYqXikAsT83kAjKtya -UXijMFbsYy1OSuPKhH4zQfmNiA98IEbjU/u5wXPYIAZX42I39UdvJ/IanR+hEh9tKvaZ/ALPLCDt -ibE+oaJMeb7U4luvq+0EoAEuS7kpbFP5NN2zvP0KTfFuDutAMj4HOtsjy9cnGEkB7CyVaryEHpDy -08pCCsp+ZCbEoI4MJ2VzgAfLL0WjHsfTyms/9Njmye7oGM7R9U6xkFmMyucM1AIwQX+w/cgKdBwn -CH0UsAqNnCznn67eCOHugcyk4zhH03iEG0PodmvktD9JulgAisVFleZmx3FuaduJsBIRnnKSHcdZ -G/NoG+szoSPlR7fK9UgZJDV72XGcVSAapNyqnpyzDUx/7xWGwZWG4SPHuX00qUGyLeDVGVWgWZgp -n4+hHcdZNkOs/RNSfqG1Z4hI3A7GCkuiYBsO527Zh/ZBE7qO5YS2gkE5mqsZ/Jva3pU5mXN2It4Y -zzNyfwKn2wGWWOfS2y42NrjPo8EyU2DevGc4zWaI5OedO0uVr4l+Gab7dHQ9Jevl6PqkyffekcpC -hX6dd/wESDcz+MvCXjebSznBPqqvpyvHKno+IW7hV6vBHDlOMhhfWnthsRvM9blSvXOGN8JxmoM5 -KGwdaZa2IyBdPqAsv86YyGwNJul7N+5TDZOjbulMPpT6tfjcZAmYOMwmaCSlaQFLMFkhS4pyVjqy -yoxl32z3DSykQnOf8b2DuMziN3O9P3p3W7mlgmqrB2DulBoAt3oA1d6oTu0AuCsHTt1x+O6NbVxb -EN94BZbblZPvP0Zje9fUXloyp7Z8Ov//2C9Sl0sEQaAxR6fWbDQkcd7e/6SjefP1rEyEWzTxJ1zg -sPWkA67keKV/0hb3jDJF/In4Cy271dmCa9oxmxBRdHnyV2sJzPWKsZCsMpkk/xSbcJiwFm+WUgEv -yDTx0KCJ0yvR8sBuVvTWbxFPMePKQfL1NMxh6IGEonhjFpVp1tbDkEd2XxYDZYXTEoKssBUJjW3m -YpPPtAyMfSs0J1EclpBiBTNDdBLNaLynvATfaHBqoMkixvrbCJ8FmlUpjbRAtcXWXUSwEot0jjrV -eE2B+ALN69QM2kRZMNBaVdzN9yECdttkJTuAb5R+N7E990XoJsftkxN5PBMk6gmSodX7plSNRN0d -vwlO4CheFRkHsHRw4gqdG64ymUbHb47fwutFGGLxNot8yjCkpNLYnVBnXOrrTEdmZ+RdFoiGQCHS -6twHqNGhhIraohFHlv+jpO+HsVcy9lkL75jN8rVHQ1jPb3c8POl8/unWAjuymyS21HL5FS7Z+0XW -lfj5gSdkHrPj96SNVAm9xAspLJuN1AnUQJHlDmXt9SLD1CeYiMme83hHZtOK6d6urw== - - - CfiFdi0Lmi/OYkHFbnPo7RZ5YAwszHb8Xu/0MARe94O6cvdqPZ8FdOLdCL9ZqvYSIyRyaY/PsuXF -8JN+jyUAHbtJW/FkjDkl4tUo86SB9M6DScIkfpX7uBAWmXlcFfFp83hlNi/CZPhe7cOblJG5moKA -GjFHpEm+tJoyTf4ZtPyY7jvfi8ylsDjE6hdSibFMkqVqLEQfI/k7me1DLn/GEN/5lmD+nxji1WT+ -6fqeWSeRh4nNpnfJeV1L23YLtp4syG5kYUkmN8d37TbFer7s7TbPjW+yKcGNbHqmVVbWd86BaX+S -qh+HfaLr0KmVTWMs8ndrLAdVXjCRvY3XNFXDD0FO6O7U9x0VJgq8nfcdB07dcfjmHWXt8OSoudBR -liIhP2r6PRvKfmJDWbyZqCZJYHH4GBTLnRwpwGWzifngRkstwPOFDuORLkh+1GghAxtpb3DioCJs -HTlOaM8AXgFHYvSyvNUDbiDhGzBIQdWDBJDP4e9ULBAGjYgEDLiW24HUA8dZYRO3tqMpUm5fWA9j -D2WUFcjoWyBuX2I7jpPUvdNtu1CPT8AowfNDY1lE+W9xRWIRkg0Ercar0PGcI3ceL3cJQul/F5uV -VPTRaKlxI4XCmxh6Ufjs6ZvYe5azmnnd6CsbfbAJz83njOgXqhbJSMrb13Afi3J+8yl4OgReXMUG -uoa0VttM9K5Bl+m3EHX5y2z77/+uXsaKPhtWfmH7vVobQM5AzBfu3pkJNiJSatHREpJqLPeG8D7w -Xr0/fCcqbixzW0rVnOWd7eV3tpfrOAFH5XGD1uuYCtGWS1Ig5vcz35mKZ+ye4wZvux6nQSTXeLjO -eFyHoSvoeE5ztEc516GcR8uqcub31fFzGcZuwu94ThI+AzSkYH4C3J+ydgtwe0c/Cgm9Gd0Yz0lt -rXzR24i9AjuwdfS5LyVOiqYePctpVsOc7nUxL3vdi0YDIta3vc/bL/rQPDovcyMsX+9k8hBSkOYx -nKm4dZ13Oy3hjbE8Z+lP5tEQQ0pVX/qFi93uhXeWSk2AJhI9z1nGwoT/ILs4pEt9lLxdFf5K43yc -1v1SKULFvJzWPWvqxDFSabjcr/OMF9JRbu2fkPOywVxMBvbRfB3E/T73rMJrB9sCxm291dixnGQw -Ctmx6OcO03vMo28jh9n5wRvjOS0cHZS2jrS7rwHsXcwFyY78uiU7HctJKt+Nu44kpOvQaZfO5UO5 -Xwp/zRaBiUPNggZSlhPQ3JI2JkibhTYCaJ1A82+6EQeWcseE/WU3PArRNJhTN/yjn4aWRSuPlEid -+3AcWgUJwAa1diO/78nukXxnMiDvmHz3BraOV49EAzTL6O8Kfx0+6mC/6G1IlT+AyN8gnqdQjxlG -XSAb8T076Ip3ZfA6bf6eJ/8Rw3Z7xSt8VAsZYr+2M488J7RZlMRurUs5ZkxM7eqtUroUO7EhYEFE -i78UuXWsSHUguvxlGP0Ws90v3vOclVnJQC47Q7yeUM6j94MPxZSFWBKOXAgx4DPVjbD8smiJ1c7v -GmiPl1HqM3CwoLwS3hX0qVsl5ikieG1gduZtsfi0qvBoUy3/QXQothXwsBbffu6hQhCDinGsDK42 -agcinkJ5LI7W1tOHmbjA/IWYtI/t51Lk2kyMNvpm6fu5P4TMfhetbG/cd5He7yK639jO/KIj1N3m -qpchpSTIpUwK7awg1SnTfsdyhqM4SNikDqFkoda225p01dlcjiznuLcFa44xjK+b6ryZyM9TcipX -NmqxrhH3PE/Jff3ebyfEyoQYRpFzE5vmoo8msj7lGOTn0En+ufF2w7Ha85xWucsqRZdyti1Vrm2a -30WIofekfrZjOTE29lZz2hfiim0xJTwbY7z5hNVcxjPpZFhHW/IT85poO05MOS+0clX4bvWHdkIj -d8dzgsfEGAofzha8L1VeKHwiuehEvDGeZ+T+FBBbENPIRblN9eIfW3hq0NS/9zxn2Q0V/gNvr3+h -ZrhdFv7yvv0g6P5ShUOl/TWCLl0GKu5593ltGZ7bvtRnUo1+YjITk4V9tF9PFwxVTgUwFG2jlH4h -8QxijtJd0qULeAfwPV2lde7xxnhOczhHra0j/dJiFHLmVmkszxrShZik88dxn6uWO+2yqXxd7U/X -gIhDrYKGV5or0OyTtTxoC4eWmayIowk63YgjS9mQYk/4smHIpsGd+bIfvVeO7rdX+QyJfitnM4So -ddVEIb7vif6BuP2cEh9+/t075CgjrLrwPKQS81UAv/meHfIPQUNe1SHXuOeF42/Bvhld7pIoA6+I -uzca0P5LuZmBgJ8/Efo3KarLRVV8uXPieuDNU0WX6zi+PswAmyx/fEWNE1OuovNzz5APMbzCf0I+ -l79gehxn8I0GbCbQZLvk0NjxmxTVPbyyrrWZKiDb3dgDGSN6dSpl2pJszgE7fl8XG8VENJ5/zjf2 -QsVDgd3kG2m4s5XqK2kg2iWU+366tf4lJuL9kfL5q6GzIb2BOeFSYarP+OG2Sv7qh1uFviB0Ml0M -dNavP6wS9bHYbFTlaH2sj53j1pEO9dKXldmHch10O6DezfoxMHa7ZmW7YbBt2Pai+/Br+uHdboKl -4s2KULJPo0uTm2qTaL3jN8EpHMWrIsdYsswMvTVe7G4aHb85fgzvCuXjRqpAdp41Do9Ib8qtXOtK -Vt3xm1uOdyKsRIQn7L3jN8sNH61iPe8CPBLUUIuZeqWo4zerKFzqQRzkznAqKwkBA/dI/WjHcG4H -SkYrdqCUjOZSlb8z49O+sOc4L3B3lv4JKb/Q1tWSMhRjXuGl6Lxb9qF90Ih85DihjYAhjA95jBQq -EPb5nOEo4Y2xnKbwgHBRKuoomdQzXpGmCz3Hqdv0bFA6KevlsPSkxRDfQoXhYr/Qu1zvbjPL+Yy4 -z9vOS0uEJRRgXe2CVDb1cAKEXBCvQKwlSyYq1A0u3M+Ksg3LfA8LYqwwYkUeS0tZ3scifJcJvLM5 -PxYI52z0nW8YFjCohX79E0vUbplobA5srhc93oW4flzD9Wzhd2OLzfLDSb5ut11a7X2uzO421m2w -BWeFx94rUPXSbkLnP+hCTFL6buDnGh6dfulkxnJ/tyY3etY1n1RvMZn66mPIXzgeqO87anik3lkM -yDse373LjUsNsk2e73In3JtR3/UY+FRkbHF6+PhljC3XTgpOksQyZLISguqzFQaXvSwg9kJBMbxl -vNH8MIOxQK7S2Ul2/Ca4GfVmMkiPyLnY9ibHm0hhMi3zquLh4TYtq5/q2QHQ4pIyTZL/ejjswG+W -W5etuADoTXSUvG0QxPuxBzICJMyI8wJN/qyeVj/w+7K0FQMueNgADwrL8M1ogGwHmrXlTmOm4TVi -PPobN8AtnTHL8lvGSwPz1PgkDJqpNYj8NlqnM81qE0daoNpiK6+B8GbEWE2581dpYm4gecml6k9N -9IUmya6uNr2f7kPC2u2VleyBtlcy3/teYXuK7b2vOt592EI5Q8UbPTIT7e4zYYokCu/4zUg4DuJV -kRc4JY3eU+nRsHUms+jYzfFdaKFiiNxBWs+bhtK48Fd8hVi8b0re8Tsj8hVYnePYKxl7EBJOzWVa -Kn2whvX0rlfAjfMp/3TxNVQc2U0SG2iDCpf+AqAsU4N+37v7gUNkjrPj96SZnIbJBlyQy8losjX/ -J1Z71vEd2c0yEmLcV0X8Qtt2wEfCKaGAxSja7hZ6YBAs4nb8Xu/+MIRzpVqRPaQrxOWphKAT70b4 -nbPrZ7FmnaythH2DN+dTeR+bmCZbd+afO3aTNuMw1vxSyn4jMl6NNVfsmqh7GGsOcfPUXCbYde/3 -mAaZdK/ye1cQvomir4p4Wc+XYvo+MK8n0/YitgkSMI0C/nbNRY785tiHQXGkQobCWs/XRp2Lu/Xs -Zm3Eo7JWrlRW+kFELdkKRJRsxVLlz1H0btgnKtOjVtksvqzEZrpn0hBTYEGRxXaWFLKeAuuPsOKO -lU8sYWb7bWAaBbX+hHMdhFoWkolz/dGPUWPRC9i1foDLzs8XZGBs/QB23ajG6Edg7MqBU/ccvnmX -2Sx4BdT7C3DZeGdU/QaXzeCyZXPC7YireovRbDBoOn9F9GqrxAEv5g3uBnp4loo8jfY9hBTHpivu -4pHhhF4NcJNw1UQ7hdOaG46rhiMVWlCbdPmZA5FYpXrTG0S8B6t9BMRRvU16YDgrQfLI3WwRUrVn -zo+DD6UEwJvCtyMhehd0E33P8ssiIZY6RDRs8AkTn8ub7QAQC0TvfIXQgxvS5S9VUhtOmvIZiFGJ -GVf8S9Pu+fhlqx0yzFq+g4XcWcWxMrjaqBm48i6DBFoJcO36sRBVMpkYbX2TTX4uv3SZmLGQb2zq -D2Gx3z4r2xd8/7BtRvfjF/WeD9sq26t500m0hA5MTA0BhuqTaf7AcYZ7OAq4SS2kKKGgPq3Hl5tN -pOM4yaktuDybKo7jetpAMrDbgmM/Tr8tKvhN0TuGk6rVfvSVjX7e3nuG0/zx3ijWZ3wAfpxfRcfP -dajnCg8cJwme7x+mCCQ/U25XrywUjPwkc6g9y2lKZ+Zy1u3homjIZ5Ss3T4jXTOYK1jg3ejraPdd -n9BEm/H5orgscGs79qs+Mg8amDuWr/eLeQyTioT5neX1qdyhE/HGeM7S+RIlCcd3oJjeTGr2cs41 -0pSh53jO1J8GAyeCU1Ole4/GnquiX8DTPhdKTyr4cix90tCZb6HScLlf51wuwWkze/mEnJdN/WIa -sI/k6+m8vwqvHUJsWrYCrmM5yWAUsIdFP2EJW/l1rtLqfOCNsJzmFw8qW0e6peUkpMyH5uXX7cRt -x3KSwnfjPlfwHpVLp/J15TtbAyYOMwoaQWk6QPNK2rVgXRhSLdJijOTddAsOrKTU7ecc8Ci80UBI -HfCP3tXW2jQMbLsD0tYNMdvugLQr2Sx2h5hdmQzIeybfvLWNzyDW2QutbQXjhj6+Z2/7CKT998f+ -9gtAkMFE8kWj8FiW38ColLImEzPa/q0StXGZqGO9WxJlM1t4mwDInzvesYjlc3Hnrb4DiClcTfGP -0Nz5FjMQ9SSo2Q251rv8jQ6PqtWEAURrfP7Ap2MDRsa1InxO02Ls9WNtJi7RZaKE1A0iVzmkWkIU -5dQJHaf+4O3ybG0sXxmD1hUDecEjEDonPSkYv4kGQCajcbBDbdiq2e9DXpeWuwZ3PD/RqHy6ZumF -XwciceHpNDueEzp/wLaCOXi8KbzEDQdZmVKDhxTjBzbCrOnIclbmLOPIz1IeSPsH5OnHwcc2TzZH -x3GSwnfKzVJrPHuSa5WKOcd3H1uDjuEMoXfyNYlDLEmmbZD61Dmwiez5zdHyEmPKIxgXNpzmk75E -fp5SvlupygMlN8ZzUj3bj76y0Z/zjh3PabvyYBjrU1FDfu01Tq7gIitew7oRlrNEd0lU7FNOou8A -zUefMHIeNAR3LGe1nVwsloCnX7SJm+y9GZ8Onj3PiZ68N/hPyPmlBg8MS403k2SJRQ== - - - FN3ALvfrPrQQms51PCe4SIyhkstjZOjD9amMsxPxxnjOMvfz/p1mlTj5li8VSwVjYzLX/fslizmO -/px/PzejGRbDfAxVJRXxdT7mSlClSv+EnM8r/bUQu7GCKCUg2mrXwGQdWq144MpVOFzQ8lsOImNI -pQ6j+5Q6HRrAaJ7M0n6am/bJHwvwfSbwzmb9WFSeM8/3wU6nAYOa59cVldRmmUhceDrNa47iWiK4 -X8D1dKvgxpaapodzvNx+w7RK/lx7pttat8EmnPXcQu8WqH5pF+roQOhCTNL547jPtck67bKpjKX+ -bp1uNK0rgLV5wL9WDeraPOBfV6JRD8T2c0p8/Pl3b2zHiKlcQL8OYnmxf1nye7a156Bfx7cI/4t7 -FeIUGuBayG9Vi79ZQulORQfIJFwzDW+pPqkW8VkNt2wkfi11Cx/5vd7HAIksP5aLIiaVd85By6/l -woG7ch0P0rn8wTGUZlulIU0DLZ/yuRF+k1y6R7YE94txlNkAhXdjD2SMFRkMtCWFhim45zdH1Tu1 -QuScABZvbqptmAITB0OQsGRH2u/YTZHY48ZQPm+hyiGMbKZoWj3YKTNnUajPbxiLeNY1XNs9u1MS -X4cTPkiwEgn4vjsznzOyX6hngPbl8zPr+ftj2lwI3gHPt8VMffgCROAP5qthqT5fLUSN18/zHbLg -s5hiUAEfxHF/L8ODFZpRUsxlWixgiJ3hVRq+AxstRVBJcjoDeOeOqnd870SzlSEO84DWyqZu276T -bfte1KUz0jYu3IWqGugw5Hcoo+T/VZyjYidsl8Nmrvs75qPrvqBA3ojuB5v+yG6WJz2KuA5F3JtR -2dEaOWj+rSo3XDt+s5J6HO4BdiyKu/wc4Erc+MDd93ZzYxxPiX4dvzqgdxDLiysxtgkc3SbzRjSr -6BnOC75Hz/oJIS8614uA55LGlvVd6kfGbtGH5tG5lRthOaEMxBAmlDFScL5BRe7c2WkBb4zjNIV7 -gAyXzkS0IXwYhA9ZRRdUbozh1C06TIB+IU+nkl7OgJ60FuJVqDBU6hf6lU8AVxOr+YS0z9vNa3Gr -fTnebmTX+VQfR4aQi2ggE2u9kYkKKV3ZsxscdbdXmdeh0YuVNaxEY8UEyT9YbO9ygHc25/dH3OpT -FvrOtwsJFdQ+fwXYama1TDQyBTbTi77u0vsl+xVcebbXVxc3ttQsL5zk53abhVZKLSweQ2W3q26D -/TcrLPYugWq3q/JuzHnQdZik893A60hEuhCdfulkxnJ/u5a0bo+g4DnkO2j10uCp9SNodaUarXfo -1JXFgLzn8d1b0/n6gDMXQKsdbPC7HrmeDFq9eHwawi1q5zdgKtwaMXYpkehW4DERY4wFqpkt2JNG -/AIArJJEA1z5uBF+M9wMQEdRDMiWUPWLtdDEO2aaj2YTz+Hv8C6nb+Ck1st/Grycu9QzOUd2s7y6 -kvwgFBGVqVh8x7EHMgotwVsKqT38fuT2ZQkrYO2SGIDB9UxcqakWY4BXJTSvi8ygIX0BDVhWtwKf -GTL0UQJUWEMCzaDToDlVrtYiEJtKSuV+Vq+BgZ66JZfEyuCXCRacGu6yy0Wa0KLkb5WWgi5/F3Ro -xryf6kOa2m2SlRj/YJOwzcQ23Rchhhz3TjFMma+E4uDfoi+ooVSTvcI7dlM8wF46kTihpQHbCnjM -pZoIX+duFh2/OU5LAZVNhsiHp9bzpgEkudJuXR4eHTvwOyPypWPVh7FXMvYTZt7xm+RtO3NYz297 -IO1Z/F0I5U7ojfCbJLfzb2lBpafzM/amIbbufD31h8RpdszmhbbOSM76uPw6PAxHm/La/FUjuYax -vR975Zvt8lxmGUko6HhY18XYhqK8X2tuEizcduxe7/4wBE7iYgibCgr72WygE+9G+E2ykLNum0V+ -56AFsRAf30wwl732Ndzn3dBPOO0zM5lgH2QnEv0x4V62Ea/BPh/1fFnEZ/X8UohcVyBqjU9Sh5Q7 -PBAw480CCs6Ug82gYccCmSqqBofb7UTmUFgsYkUMK8dYSslyNhaku2D+Tqb7kNefscR3vpNZ2GKW -+GVHoYl5MnmY3Gx+l9zAtYdGdiu2nqzObmRlWTo3x3097ov1dPnbbZ8b3WWzHgc4bnqmU1bcd86B -6X6OnnfDnu89HJXKJjEW+Lu1lvHpCDjk+AxtdYGtzYDUFjjdG/V9R42P1I0Dp+44fPO+snF4zWW5 -cOjZ2vz+0fdsK89FqV7qo2AO+ZBNDS8qunwPQW8FeIa7xKPIRi/lbfVM9ADUF5/txamYUnH1PCe0 -abx5i3qRMRbxx7reZgYRsXDBxxS1of9JOmey2CY80PBFVrZUUvVqS8dxVtgExJEkenkgg9dcVjb6 -UE54/JRiJmb8pBvj+WUpaYaiCyHl1U5L3BDHCtari+Wxhlsl4vlmEMVg7oCpuMECW1OpIo6BiCOk -Mj1JI7xvxJwsmvxiQ322nWpjpLfeEJwEG9Ga9zgKsOFX28XJ3/nwtih7B2F2Pv9hMqUA6yf+kJ32 -W2hlG+O+hexuC9HNRrflV4FVH3ZWNln57whF2fQwG6ZRovkjwxku4ihfE1pEAM2Z5eMV7ybScZzj -2EL0qvSwXG3knraRDP+WMmae2bZTz3NSmd2PvrLRR4Hj5Iym+eWDaaxPOANMMj88g1/LJMNmL48c -J0meofASnjXBtxJdRe9CwshdUsfa85zUucNAUuQCTNBtXwC4GZ92ij3PibG8N/hPyPmVBg/8yHL4 -RJZYCu47TuVu3YcWQoN0x/P1LjKPkZ84Shl5p2GFnkwjOglvhOU5Y78A/Vyei7IyKYS/dWCr1Aao -G+95TnMyRPiPfLv9haLgdlX4S7jV46j6S8ULlfNyVH0eF7dzjlQaLvfrnONF5Ore3D8h52WLuYhc -vQ/oK4/8fRlQxYwWF8Gs2h6g7VnOMZgM4Qv95C90rRo7V3gd/eCNsZzoYfZKW0fqpeVlljP/WPby -YBlmafw+7HPVb6dbMpGvK+XpAvTSUItgIZRmAzSzpA0M2pJhlSMtzFj2TbfgyEo26OoTHngUnmkg -px74fwToaqPxBWVxb2KerdUdWmrZyO97snsk35kMyDsm373dja+oOl7B+MABbCU+7Hs2vL8Gulq7 -WOr6VM+5gJi/pWViA/LMRHxJEaJ17g5IbeBvcH4/VCghEK11ueyLKmxEjUOvmVg+G2bcu4hjBjpi -nNSIGe/K6Pv/1hhXw1NtELTiR+SHAbcx4oZkLZ60fGBXyx2WFnBmoMno20z2c35wc3maeCEAM4qp -ZDv4DopvuPhBe8Eoy7Xgy6PIsKiqDyNDq/x1NJVHd2+E5Se6lddvc3VzWLlkfAp0sh3LCV1AyckC -vv5L4epUvSoMeLCUSjWbP7IPzeRgSkdms1Jm4OG5OpA1VdfHwQe23m+IjtscNe9VmiVWbyGYknEq -Z+IHW48ov2M5Q+yjhE1sb0t+6fWiPnIO/VQ6lqfEvn43GOhcOCmE56rjsqFue+ANGxdw9ts86W+O -LCfZy07C9VMS3hjLabvzaB/rM8EDIi/IeIE8Z+82s+M4S3TwD/lOpJG8uz472vmHoSdhMbhjeUr2 -yxghMlyxETy5ovQ2g84czkbSnuNEr97t1OtSXt6o17CrFa7XY42NueOL7tZ9ZCA0mztynOBiMETG -AJVqckPnPJlsHsW7dfzOCHw92RIXphN6NVJde29U03hnqMwEqCfsWU4zFib7+YhEc+Vrwl92Muej -0zlpr4enp/FRe+dIpaFyv847Xk9nqPVcl/a68VyDMt6F9vamxS8XkFXMkCF/HNg7RbOFOUYjApVO -XgpK3yv4U8X63i3eGMOJnmavsXWkW9qQgJy5onLLPe/peE7S+G7g53omnX7pZD6U+7UY82wZmDzU -Mvq4ypIDmmbSypYV6bSWpJUaTcXZLhwZyoYxf8IRD2M1C+rMEf/orW40rwsWCp5EVgVZr4J9LPh8 -2Mjve7LfkTcmA/KOybdvdeMhVDHLC3DWuABB3nf8Hq3uI2TI/9/d1bVYbhzRd8P+h/uykDx4Iqm/ -pOTJJg5sGJOQ2GAIYZmww8bhajbsLrHz71Onu6W5Uh3N9pVv3zBLTLBrrkrV1dX10eo+VWGrG4Br -Idh02crlul/qNhPbVOCuTcj9B0CM6KW4qjUd+8b1Lhcvp+KT/7QbFjtj5AthjxBHffwGgz9aHxMB -gOyhMYY8h9fEQNubmx7tGkATefxEi1/ghDaDJXcSUOA6hcmQW6+A1sL4cS7P2elZ49I9PnOCiLcY -8qmTwyiRDRhZdrafMOlsvIYiAxeJ+kmkDkOU1+coCwRSvNzA0fsZuu6U2/GaDTSU2CMViIidBhdO -B6e5XT6F8G02sC6eGOkyjGSyRHGDj9DZ2hqI1Sh2lTI2oOrFfuUdkv7U9F69e8uyyQpQ/Opo+lSr -CcPYDCZVI65JB2/oGiPaX7OrIPBauixxb/qUW4Z0uIx6ADIKxa+KjrsBntH71C17LPUaHjEcjZnQ -wcK0E6r6kluJwDtOZal3j+Td3EWUjKTWElybwlgaDOTJ2BUVT/ZDqq0Vt1qlHkDxhpQRB99OsLnL -xb/hJGg8VQzPNJJyOAWf7bg16QjtyK22LCRqdrXMhBj3XhGvaN2Y1ojlinn1vW1nzNHTud40CpaN -KY4VNgTwDqDNQmWStNgJ0LosX1QSHhnLMgM/N3Nq3A2Ojhn0f+tyHylmo8wAmKNWDKsZihZ823l3 -T+d3x51y7/An26HyEzkok3FvsDzXuokLZLJwoS/lBPfcwmLWvVvIvVayL8gvQ/VYVt1luRP0k8jY -e8dDfx0ria0f8d759lJ5Ia383ZHxrOZN1kobt9RL9wsgZ4QRl8clldmYiEpKX7z4vC0NpV86mCfl -vjSyvJ4GJg+1DBozafxn2SOrRVldTSpAVmKxzJotwy0zycjyJa53IxSzkE1d72e/C43Zi7liZ28a -n/qrvnztkUF2J9TbBTWcUh9ZbJAXPJ77HnS8OzDsQ602aNv5PLeg65+2Focsr+kP1oS0Vz9GWoQP -A813LuEuoeeQT79r/Qxq1AcczRmam6FPeQtoPcxPaE6iWaZZWfKR5nO3WoBRxZ/hm9hEsT4kUmjc -TEMvVTMM6dsfaC1iTneQlEncVDruBlonHhW0vhl8psXG2qBl5Lv1WE/8GsYXIqStuKamnfTQeAs9 -ePRFGSaJjLhsawGilb4FGwP8KROltCHdL1f8jlfcfVaCj1wgJjgboOJ3+bxhEJPr8dES5726dGRA -aDEk4pBAnxCoqEEQw1HsqqVpstj6JGNrE9Kdeve2ca+WgOJWRc8LnUZ5mwgwaLvwaOZskRHdr9nV -EHglHQqPm2Gw4uCs5MOmc0+5ADUKxa9apYercC6qJjrHZBmNsMGrO+vac/yJYlfFNBbSjb9AuqNm -V2kFKlsYS8MBINukdo1Pzh9F19wqSQ2cvwZX6ILBzmfOolern/oIFkwVt0pbRfIeaw== - - - 2yGmnPLUBGSupr40ICp+9by0Wou7Zdy9GHcBmRuHuZZ5bUyYcFSXc71hEywRU/wu70TwChuLFSOF -Zze1dCjKE5V4R8KvgsjEqp2VX8l0CH2CxWLZ4MWMesdiLHTXTGwyuit56/Wby4NiyTjKNH0uALAW -mU0x8wlEqftEPh9UWwu92353KnpXw4lVRB6Lyrgk4OCE1AHooAk0vNfxHC0KZVFLBJMaywtl5diO -hF+9VbjQ1siVyvYBICKQPvBsaxO+zZpdHUUv3nrGLoVSKhvFkyJfFCVeq54JwyyBhUIW0Vk2yCpL -ViOTeo4VTCxNZuuNW0bCiC+IutyFMk/LPPLnvonshwld2p7iU/cTErU9xafOVAmrJ9SJA6cuOTzz -HWTbDMCP2bGDbA3WyTPdQK6KT93J4rSIGQ1255sZQ6zHR2FAAeXSG2BuXfwKFsSthCEj0bkb0/pB -FruH00m4hYrl5WNJxB7qUPk54K/nJoIg4suU0Pp+AuR0ua9AAM7VhF/s0vlRE9DdwWTE3DXLWoWs -aLUL8H3OJBjlkb19U05gpDVNH4m9SV+0FcurxULYisGfMNvxuOBkQACWArE3Jsb1RBzSL41/RBoL -LmIZSqDrw0yLl21EKVPpEHHKWvR9haaaod3UxZbSiBnY2ApBgjQuhXYzrfEiuXjbpp1grC1g6Wz8 -5WD6aTTLcZ/ERbWARrYsthYQXWpsTV4Lm3q1rKK5tvh3SSgaXOLMN/yoPpni1xxrOIi1gJPUIUSa -y2fhNydcjURxrOPWQo8brdjQsm7GSC6zEbgBP1g4EcmghnziSPGsVK7qt4/s7WdZveZZzSmvTGM8 -wxdgkAYXkfG0jNHP9nLKsZLkwMoLPY5/4ANJZ+d2B8uAsOEtqVvVLCttf+FFsfQw1s8b/9yKS32i -ZlkxjGtz3y/mNa09AkzGJuoywdPGo571TfugAVrxvLx/jO/oIgSkS72Ox3NSCCXhkbGssgEphfNN -DINDe+NCOoJGTZXaAPXhimU1Y9Gil7t1mvbsEn0XLHVpPC0Tc3c4PdfMiVtkwnCpL+YVd2FSEzvf -L+VeW9nZnmIZxMfSzD9L2VvE1gFQun3geUElW2mA4TsgP51AnUtLLeX9joxlRc+yVNq4pV5aUIqc -HU7wDt3cwVyzrKPyxXvPq3iVdtlQrle/0zkg4lCroNGTZgI0p6T7FnQjhpWMtCJjaTddh1uWklGp -SzzwVmSmMZy64M99e7vruglP2i9AqdsJfdovQKkzWaouIDPP6NOZyQZ5yeSZ73HHrzYAfNkBSo1D -+M1z3eVeH5NeHpHeC5m3XFYtPuLi5gUq+tY1doIRCwZxMQDxPt8RMg7dmfFhDc4z1/5A88TBT9v5 -myZ3Zdc8K+wLAX6ojcVMbAY/wdE2QOL0aAnsHgU0QPnv4egmOGwQ0U2gj62iM/rvkmOtpAq9L1yb -ysf48WrU796UUYixRa0J+KRpZzzXJcerhUoYSueGtB80DGYG8Q64DxOJbfqEHYmAewUxnreN5RHy -Z/RGAMRQ8MNMtMiDQ5sSiEyUf/WJOIQntLGlN2IFEmHRWQq26/NHfxClOLbx06zY40wMaGsdiWLv -04iWYz+JlnoFjWxdbK6gtNbMcq2xVXmlDW+1tKLRimxS5cdcrnX56xJXKlO/4lnDS6xFnOT2LuWg -vp3ui2/Mux6M4lnHu8XzEZKN4ezAjJBYaCniERKynwyqzzdqFcsisX8B3PZKhpHJ8IkF0D89rGou -em0g41mOoQW6Gk65NGgD4x+tZsGzlvAuiI5xH9Lhi42dUWWXIWLTfVJHq3hWDI7KaIp9YcQNjLB2 -LnVS3W0z+0G3lxLM/QjUaqT2XTKoekAPAHfEf2CSpVwfJijG5cxv2ggN24pnBV+JdzTexneEwUwY -oYWJhZLwSFhWU7mHYzWxCvfen+kjaTqxZllD4atlNpLXbspXtHDrLtLy4Fqk4f9HcGWGs1vd7nzD -uSwsrscRaNu0NzKBZnI7bWiHSBRGM5Bsi04LkHAIGWCALFXqdWgMo6UTKQRprkoTQRrndUZwq4d9 -Wj2UrLDbDbNjEY8tsOs3RmJGy6IbHQEd6i5nt6+RyXL+xuKi8KhnmuaIdfz0cr1MxXlZHb5eWceN -JVgrMq59AlUt3WtQ3oNMQiV9L1573maI0i0dyrbcz21DHHvbGW7aLTCrmwmc2i0wqzNZvISQ/QxO -nZlskJdMnvtO+CDKbcMeyGqX7uc/z43wNWR1lY1w8eIWXaLhzl3TzBhmJnaJ7sUxJkRSeH1hiblo -235CiO073CWRqs74/KVb8bu8xwH0UWxWi4LGp0O1oKFXbcgf+LJ48bNeP6QrJhNNngAN+eGRcKvk -2z3SpiaVYWFIilbv5iL22IMZ0ki6kACFFbvrJa64OoRG0fG+kDUzsrdBJ+Z4Ycjn3Z94oaj36bZR -k6uLDgENfVnwgavLDZVA9BGjsL1p/TBMj/fx42D/CLFNVME0xua+RSSK1joMqakjSA2yK+NTI+H0 -qIG/FVI8fXBkIz7NWleLZWSLYGO1sFVFFt91drvXSwhXuESh6PaMgO2a3mypkWh7ze3ybkAJlwUO -PmUdPt0OpHOsx6C4VfFbsQUgjhD1OE81llsFANga3O0VfU6Yhopf3epbiTASEc4wdMWvkuNd28RY -vO6BmIce5ng0ni85am61KoEGNV6XjsXNuNBrn7/hEJnj1AxrxTliJoV+DsiDCakWnVi88/vNZPcu -mRJh5Itv95AqGkwwEaLR3PjezVDLy0nfsg4afxXLCtUj3tHZ1JOntWZGGC1MEZSIR8azRO79GzyN -lWiCe87ySJt3Sdi0M0tmPlwxrGYyeI93C8lLPTrNcTTHqou1NKA+IeuJlndH1DMNnjgYrbdNqS/m -YvbvwjN73y0rm4l6icAynI+F2X4W3FiUMgAscOmLgmJYx1wiEDHea7r81aa8rlKu8Mh4VnMwa6WN -W+ql5WOUMz4c+jbwaail8sfXnlfdKt2SgVyvVKcToKWhFkHDKM0JaG7JNijYXgspFFktRvJutvy2 -DCTDXRf53o2wzMI39b2f+1nutp16o6DT8SPedT8hW7tTvOtMtd0pdWaxQV7yeOb71/OthPPxri3c -znM9yJ33r2+/lj999cq9/ubhze3df+/ff/llJnx9//bHh0jCg+l/ML9oz+JaRGXyT3NwIN7+48UX -v4o/PnS/Ptxu28QP395+/+r3h98e0q9fy69/F5dS81p+Ln9aL5+v8H8//PTii99gvX13//NHcJJJ -+cP7+/vvH968w+/jf96N968e3tz/nAl//fgOE54Jwu+TS23pDv4o//Yvof10sIdvD3/7e3N480L+ -9heyCLkSX/757u39d+/vfjxChW8/3P3n/nD38PDu493H+3/Lnw5v399/ECHvDx/++e4nUPDQ/MDL -l9/8SZzV/wBtC+WQ - - - diff --git a/branding/icons/numpylogoicon.svg b/branding/icons/numpylogoicon.svg deleted file mode 100644 index 840a189a6845..000000000000 --- a/branding/icons/numpylogoicon.svg +++ /dev/null @@ -1,6967 +0,0 @@ - - - - - - - - - - - - -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - eJzsveuOJMeVJvi/gHqH2B8NiNhRjLv5XVg04B7hodWi1U2I6p5eNAZESSxJNVMXbrHYPZqn33O3 -Y+bmEZkZKTZFVRgzmRUZ6e52O3Yu3/nO3/0fX3718/mbD797/fPmWB1evvi7vzt9fP3q04ePvzjQ -24dfvX37/XefPuJbP/vNF4c6HCv81Pyr8Wv55L+8/vjdmw/vf0G/O9b42wv+/c9++/HVv7/57vAv -r77/458+fXH42fr+058+4M//5fCr978/foGf/O2bT29fw2fff//u2z+//fDHD29+/+H98dWbL+xR -4NLnV5/gM91/Df1/DVU1HoZfdN3hy1/jR5YP37//5s37Py4f/tcvDmHoDqHqD13fHrq6xd//329+ -8/q7/EPHvu4DfvJY9W0HH2+Odd028DfNMdQt/eH5w++/f/f6/acvP374/evvvjt9ePvh43e/OJz+ -/Or94dev/gi/eXX4f1+/ffvhP+DT86+6ry9v3r6Gfr979ekw0hjNv6rD18v3b95+84/fv/vdaxiR -UHX0fvM1Xe6fv4PrwCXxZ3p/+PpX7+Ctr15/+gRPC3ejof7NL5cTTMGHd/RBeJPaz/7tN6//+IZm -Bkbov38hn/SPSx899mFouxp+GIa+HerDz3759sPvXr09/Pr1N28+vf748dX719Cl5e33r+ka/ydd -PX7sy+8/vk5/W+Pd/W9/+fH16/fy65ofzv/6N6+/ib88ttNYNZP7zFf/3/evvvuTfcJfncf3Cxmz -375+9+1bWAk0i21VHWGypwm+u5/1ozAX9LG6P/SHYYTlMLXyuzixr//9zev/+MXhHz+8fy1zOH/8 -9NWb/w1zMlbVoa8qefs33799/fGf37/5BFMS6L2JZ/DXH755/RbuEv/88vYVTZyMon2XT/z21cc/ -vv4ES/LD2+8/0aYZ7S6wSv7h1Z9f4yqr5Sb/9O3r97/98C/0mD+v+waeCUbo2PSHumvHwwg/hJ5u -0h7qWm/Hg6gPhdfAK+i1BxzpL2GZ/dPHN3988/4X+nDD17/8+OabuPaGcBj5G133OLqvSb/kOaHX -nz69fq8PDuv+9Gu3iqvjr7/Cu67vvzl9eIeD/x3tXVjA72HBwraX38Z/0O/gEt9/K92gN76Gufry -45v3eOGXL/6Rfzd+/eXb7+GXv/z44ftvf/X+Dx9evvgZy64vX336E6zd1++/+Q7ED7/H/zzwn8C7 -//Dm3/VNEDvffnHjkiDTfg83P/zT7/7H69+jUJM34k9ffQ/b6iGX+hLH7OP7f3rPj/nx++/+dPjt -hw9v7VHlA/Ire2JYZfw3P5qb2IdLN4Bf/ngvfnr19u2bP3589e2f3vy+dP3C7+1GO3/7kNt+9Xsa -9dId01/ZzbZ/8eO4Dw70H968/wb+hBZ+HLkP777FY/fw1Z9efYtv4ycv7pMPef7z6z/Aiermlt5d -3//767cfvn0d37d3XsEN/9urj98+aHD+/O53H96++e5dHBP3jv38kEuB/PvoHof+Cf//A/7/Qbv0 -7av3rz4e6Bf2NCSZvnwF0i6TVvRevGz3NUhOLxl//vPrMjPUh+W9/8gvP7765g1IZdDDlo9vYFW/ -gnv+Bu7yOzp9C2/CidAflm9evvi3ly/+r5cvTp20HtpAbaQ2UZuhLdDi6wxthXY5Xc4VtZq+6nPA -9vIFfG+otdY6a720gb64jdQmaTN9uQZXxP8v0Pj7vU2vmDd9gjFp+pR9bC9f/D2NXVM1oWmh9c3Y -zM0Cnb40lxb00bZt+3Zs53aB7l/aS1d3Tdd1Qzd1Cwz22ld96Nu+70doMwz9OlQDjN3QDv0wDgvc -8DLWYzN24zBO40KPsk7VFKZm6qZhmqYZpmedLnM9h7mZu7mfx3maF5iwdb4s1VIvYWlevljapVv6 -BS65jMu0zMsC03le1uWyXE7VqYYWTs2phdbJCtjOvZtzWDE687XNus53k813nzQaRx270IQQqlDV -l3qFyyz1WA/Q+rqtmzrUdXWpVrgJPGI1VWM1VH3VVS3+QVV8vXxRfv/prx/fFWnsKho++ndNDUex -odZC66D11GBFwcBhg3VTzdAWajikOH8rtUt1wYu8fFHzK0BroLXUYN3ClPQ0NSO1CRqsOZiwpabV -Q6tghXapL4EeBi8RYDHDBMP+CC21Dhqs+TBAG6nBWg6wdgOs0wBrMLD8WKFdwoU6hA9Cl2jwBVYA -bDXYR7Dd+magBjukgR0Bmw+339KcqOFqXKHhZsRBwa7AI+jSm6vpAltnhb1+mhbYSNMEeww2VQ9b -q4XLBehkNV6grbDtTrD9ZvjACFuxhw3Zwk2xAzWsysuwDmfYtifYX/MwwdYFU23oYBs30NEap6C/ -9Cus/lO/wEafYLsPsO3BzIVOBBjaqrt0K+yVEwiGGcTDCEICBAwIixa6isNWw7q/tCvsqxOIk7md -QLAMIF46EDIN9KtuK+jpCn0+wQjMMBojjEwP49TCqAUYwyrAtoVxPcMoLzDiEzz+AHPRwaDgNqxt -G55gVmeY4ZG2Yqebsa507OiY8G3YbeNum2KDK05Jm3fastuyF1zRH1Wltu60S7nBcVHttnqnhdho -7NrD3329fKQx5OU/NtJaah213rWBGr5oecIaxLZI46GkYwGuuFLDFXuZaLtPtFEn2mS4QWBdt7C6 -sfXSYLnShfUFpwfsBmwwhjQZeBqu0i7cZhIkKABm2rxzI62FIwi/OjqKsA3S8LFhnvHys74Wajix -evqu1C50dOHhVdMBFnBTwxHW0jGGBxkfZXCYwdE20uPOdKgtdLDh0XaiU37lI47bqaLDjludtBAb -rJym2FrXurTpvniG0yET9Z+vuHnxaccnHZ9yeLrhguSphfOMjkQ8gFDog3iFcw1PLVqwNOErnVI1 -HSsoXnEr4kLGBQBnD15c5/TyzK+/6BXPsMxnEgDDpYODD4/jar2s63om4bbAJh7XYe3Xbm2ho/X5 -cj6DMFxI8R1JwYUjBmRdAzKrBrm3ggTFLTWDoBlA0new+hvaM7CbYHedYMfNsAcH2pkt7NV6qWAP -n2FXz7DXB5ICcEWQDjWduCc6awc6ZQOcsCizTiAeRpB2LZ6oeJrSOYpnKJyfcHbiuTnCiQkHHpyX -FZyVIJ1AjA1wQjZwQF3goWcQoD2chjWcgycQRiOcfg0cXCvMKZ51XWic6sSawxCkkW4zVHJUX+i4 -XunIPtOxjQ+w0OE90wE+iXwe6CCHo/zlCzjOO3rAhg71QAd7jesTDnc83ldSh1liLHTQzySLRzru -B/zzrqMjHw99eFg5+GvU5tB+gON/JdX6REoAqgGoCEx0dgyiDqBCgCpBQxoX7oBKVAM4IkgpOpGK -sJDEnuj0GUhV6EmtaknNCqQykNIAbSW1gRWHhTbKxOoDKBQDdbUj9a4hRYJUCVImWJ0405ZbaPNN -olYMpFB2tEEbUS9AwahgMZPOr1o/ngMznWUjT45YAC0JgEDCIG7VUdpgTSbz0kmjYbw00qhztElg -m3CDzXKhDQOvly9g25xl6+DmwcZnFJ+HI22mgTZUT5uqo42FDQecBm4lvZi6hEoE7FG6KL9YF2Gr -Vq3PaHHy8sNlwxZVQ9syiHKBQySWGD0gXopfvGFnUaZG2rqDqGodbeFWDjWUeLSZaTtjg15DN890 -hJ7kSMXDFQ/ZkTb6wFtBNnxLh3OgjY8NJwxkEA3TmcQAbFU66PmFCgAqA4OIho5UhpbUB1xauExg -yu1IFdMEWm6YpGZJNEmiOXIRMV+bEQL7lEwQNkDY+FDDg4wOMzgasuk7MTJGMy/UsLioSQFaHVr6 -uPN62IMj7Ui0+U80aSvY/RU8Ku7lBj7UwS7H3T7Czp/JD4ATvHYXWNo1bCW4IciSFoRBD7JlBEmD -w7TA1J1hiV1gE9QksRrY8B2InwH0wwkGEyfmBItmHS5wHtakWraiTMI+JQVyJpXxjEoiKYfBVMKe -1MCJlL8TKXwXUvCCKHU9qXCsvKHKBuaO9zCYfyHzLpCixZ6F7tTnKjAd5sEO8pEM1IWM0gvNdUMz -PNLMLsmxjaYkz+Aic3dpapBvPG+9GYXsj9HZCjRPOks6Ryt5ZvIZovkBGbyQvI4zVJNkb2yGeI50 -lniedKaqZLZwvmD90az1MnMjzZ7O30I205lmEduF5rKi+axpTqnp3iA5X9OBh+IGpfpEEh0XEa5e -kuEkv08ku3G9s7xGWc1Smv0uKJ1HUpM6ksqBpTE0lsIogVn6tuR/2fXAPEqVe8LrOa5I41erJgBq -0wpC+USq00SnR08nRUNnQ1SfFpL7UX1qSLZXJM+dEgUyfiCp3Zq0rkhCn0kyzySRoyoVTJU6izI1 -k6QdSMKidIX5JqXqImrVInJ0JAnakexEuVlyaLA7I3VorCPs3sylsePUgKZODXVrsGOjHRpdheJD -rJwpFQ0m9Sn3mU95znzK0SCnE82Z1SHxLbbOpxj9xuozVs8wn6r4ouObT132dMlpTFo+NT6t8dzm -M5y3Hp7sfMbzec9nPz8iXBI0A36RtiC6A+sRrFOwfsHaBmserIeoXgLTUDAa60ILm9ZsWusbXLE1 -J2De+k0bCm1MG1xRHYh5mwttKbaTb3DFk7kgt23daRffktOEtzJZFwstfFz2tORpwfNSx4XOi7ym -JY4LnBc3Lmz01pERAPPEnroJDmL21XVkd7CnjtV49tOx2t6RFUKquinpqp6rai6KuRM9Ndsh+Wk9 -7J7Uzc5JnZzVcC4Xz2tyrlzEqaIOFXWljHJuz86BYs4TNZrSob6crUUfWfS1zNbYYxT18lwbFz0c -rihauNPAU907at2qc6u+3Yi2XfMyYQ0bdr1q2Ivo1qOYuL3I59bp05Xo0apFRw3atGeJk7UbzXnN -NOaZTOKoK6Om3JT0ZJgtHCTRk0m2p5ryJO4zMTzFtdaZwy1YE+8GXLFWN521NW+bZVilqthgythJ -1DFVvxtSvHtRuVktE2WbVLPAvn5Ts0fz74uaJip2qmBHZY0V7NUr2KRewxVFdfMqtipwrGRHNZvV -uKhqs7I9k9mtCjcMAsllr9Q1ZMB3pNqpcperd6rgYbs4Na+m0B0re6zukcLn1L0hUflY7eO2OPXv -ZErgmaIKqgzGdslbvkX/Df51hBENdY+AnxbGe2BI0zF04xha+KEiXBn+OoDsmg7h0FUHUOrwEgj2 -+frpV1i+w/sPQ+haBBnVI34Af2jgrU4uk8Pa4K/rY9eH7jDincfGPcjdl+In6qpprFpFdB1b/DeC -oPTqFfcM/rw/DjViEMfjNAb3IE+9At0fx46WFcO78HnbqQrwczi0A/3toYWLgrSPt3zMH+ld+noC -a8r+wBB78BfhyCOEjzpNyW0e/ld6nwmWR2cPRh9tmiO6jw5VcumrH8SrLeeIhFAwww7A4atPr1+/ -PZz+/JagKIhuyN6BS04O2mCKlUo3L99MwpF8S6XbVr6RdAPJ1ppsc3KNpFrjJNpssg== - - - DCVZECcBG5+J6UmOPpVYbHiylIouAZRJ6gxQV0B0BmxcARSLPJsBWUucSRWMVMVgNWOVmFGqavQa -HQLdSiNDMSKkkSCNA3EEqJUIkEZ/RlFEJov50EucQosF99IY0Mrag4sD1UvIVGYf924tGpDPrZ5f -V2YYTsHtGebPLz/L8fTSmZ7k7HKzbU6h6Gw4Jc6gzNFgrqA1m3VzA4EuOnv3gpv9fP5ncyJcxHlQ -nH8wAnUNnDluKBHDkDiGVOGc3ArYrgGaf/Lyt0kEcCisgdnP+5PmVDWSYDOa6iQ6p6ugEW7NaTqj -g2kjJ+882szpcmNOt66jOp1ZmNPoPlo2O7t2jqNetAd1Fp2yHS5zDHPaRrefxI3nZJ5Xiw/HuY6R -4TwmPNPO11n30eA8EuxiwT+JeGjZCN/H2Mj6JNP2bJiass583nVl8nrE1chrcWrgirAOV1qDDa0+ -g5TRqmthKY2w2k60zmpYXXhWTLCazrCOalhBHawclAkrrJSAYDJYJQgnYyhZS0CyZTrTLm4JQraA -HL7ANmpAYR5g8hc8oP/+PnfZjsMMzLQxcZg91l2WOcsWOE/NWabusj1n2dZVljnK2HNA45Uif6KL -LEf9ONzPPuoHdv4u7sdQPw/F/BDqB1bJLu5nD/Vz95w+zAV694wuFRnK63PNJ84mzOnD5vMBs8lz -Sfbk/bPpEFwwpw+eS0Vw/S3NaT6rFOZ61lmFffo8e9RmFeb0ETuUZzUPnMGZUhk0VgPkDI7tKDQu -gfEYGnefGy2M3lkY3T7v/mKh88x/vqWTr/wX7BUkTzl6yFfyitfkCe/I7z2itoChAnTFX2yZFj7f -3viL8uf3/sJD7iZSiPZiHY9ZIk6I0/LgxaFLQxcGL4t0UaRLIhXYoGK65fAEkKaOkaBYVsOuzIJY -KcNOt4s7W94wjNue0PKWnuzDTneWNzkI8z75Xvl+NS4eKcgQ7angcxbB5SgipzMcTm34G+3l3iam -bQw9LfXTb+JyT3c3cqGnfv78DLaFni7aUw/Ezbb/nOFjOokA6VbVlkSWfnzqua3ePSTDfmzkKoZB -zNIYFdGYiEZEHohfkKyI+bQQvBgjlheKUgaKTXYUkxwpErlQ/HGlqGNNscaWIowDxRVn6+kz9lKi -P/u9fDBOI/aTgNR39HO11fucvcQ+Qk9v9fJR/TzBzr+nn+tpc9pQD01OkVFv/yJT33Y2IV1spCgs -qf+i08k+SfDIzbmWOQZ33YLZrnJzode/glFaLVji0S+juzfd3bApMSCz5+pyiKcMTUPBFlNESpHw -GPVWh27ZRUQGuF7pcQZCSZHMkkfuuJpd6baqe+Nq+WpwWovTWWyGi2qs/XbKdRg8GW0m0vPEzhEZ -3bhz3b6138Y97Xa0/TbudbfT854xkMBCy6tgDFePNXQR1BxpmCIOyU0lkdOKxp9n4CxuLp4JnQud -ja3pcXbzUjRAClgap2HaOlAcZIaEtPitPtliz5U+WSNPJs8m6SBngtzI08mz8Xd9xri6vRbMT9l7 -PRi1StOFr6cyibaV7Dv//NseKEpJ+yFjbAlYq42zumMm14+OnJ7QE+uD78WQOGbSlCzR60kiK65b -EN3WI9WTEw3S+oZw9oHAJrUA2RFk0qCchqeYaRRbuGcF9zrBPRjGLiB2uJqB2MlegDEDM3IiwEgr -eufZ4OwdaZqVIPoWsw5YuyxbBqATJHqlao2LaI3jVmu0zNZtoMOH6BVTmQc60qC8OwFAvmzOFDkD -0jPl2mmWnGdXNaiidmGZp1u3f9Hpv9u7v0TfkpOaNIFHaU0uI/kZe9aBdrDTs+bxveI+wQ5+pC5Y -wA4/Lay7Ba1QaMgBVvLQ0P1h3V7Gj0bP6VDlsdsdvWS16/ilI+jG0EYxRXmrXk3hPJBfWTivGNDt -igHdPJRHwVvCha96RvOJ/Uxh23QOYRZBsp0SnH8M8j1f2BZndLsn+uJu32rHHLadN7jwdIb9DskD -97pLOjq5yaKQBNBTEs7bBm6HBwVuGTO2Cd8/U1g2ZmfIrnNh2ZpyhZ47LHtFjtmMlfIu9sOypweF -ZWeTcNv547BstwnLLk8My+rO9XPqdO37vDAlv8wPeMVbYdcYeB0ElUyI4xh2LaxAXH+LygqV+E7a -S/4I8Xh0xOPBYdczhV0DhV15bZ0k7Nr4sCvxeJTCrqOEXYXDg3buaVxhfpm/Y55MR/4BwzX3h1Wf -KQj3rOEa0tZh5z5XUFU82rAKNj7tv6UQ208lbPqTnrMfcp/9gGHRROf/N8KcbkDK8S3Ekdb8z2E6 -1qGK8NbsbUYDTzUiaeQ39XGA0XNI39Jv+e/wKYm0EX7ThyNoJq37u9Jv5e/GEVlL6Tdde4QTqvN/ -V/itoJY7VDL4N8107PrK36/0W/67dkJPBP8m9PRg7u9Kv+W/qxTsjePaH0ELHt3flX7rRr7Kx/zR -+N5/fv/+1bvX3xz+KG8d6gFBvqW3D/WhyZG+UYWIHBHROFQWpGXrJDZ1gRUG2GfQKa80PFZdLSV4 -bZUcVW/0q7Eouyo6+jVSDwZKx5pM8Vblm3ujXxfumcvbUIWoqduraqI+lf8e3NM1G1XMFDK4YkfO -pMG+yxMnyWPodlqSNLETJf2eHI6AyCxWkdQnySeaJIeot8yhYBlDKrNTqW1y++WLjeSeHgM7S8hA -Vnmys+U8zcnzxRynmN/kc5tiXtNCiInZMQn0LqMpaEaTMQZscpkykpLVPds5yckq52L5bKw2y8YK -kRUhMiLEJzcuBOVBGI0HAXqQe2uy/Hkl5FK7LbW2T0k6TwqMlxQe0BnOzuJmePwg9lq01dROAxFp -sbatJ8jDvm9vbW+DQj+gm+pL87Bgb31G25OsTgfJORtUVO0WFUVuuxK4WQHO+uT+a2ZuDRFXKrJE -bFnf1DuiAqwLvdtwopx49cQrKF5FCZaM7JUUr6a0ElrRwIoqK17F9OpKzQELHpa71alEmaIUu0cq -wAY08qjVOtmGizHmqBDJ071zYwueHEZ5EAoOTSFU4g1NH/Tp3zDmJQUoUkSHQ9cdcXWl2TY7n+DD -PXCSD5zjAyZ14Q/tUHUTpTNh9lTl872OIycBUWZX0xxhS3kd6RkuJqoKZziBFqR/VEuSU4UaDfJ/ -ww+cRARXxuSvnrO8JlAUD+OId/PKzbNc727FZSorLtMXNDVRcXGpfJrid7avsyQELvS1kJdJ0wYn -ycvnpi9YNmmmvvAc1ZHlCKWjxb/O5N9aDEMVUVS9chqBvq+sRghVihHhjo53Zu1gzo5WGDsQ5M4p -U634Y9HPchFvOnvSPax9QMlJ6KsVJGcQntQIa1em1Fk8LCjdmSd1dkypLXlJycsyXtDP8vIFiJke -tvVMfjJ7coqTKTORchMpHmsiKZuzFLUmmWvlK2IVi7PRjbVIEVkKt2QVqLc4W29qkwIpHZhy30fm -yavU7aj5IPOQkodcLPPHXJBJXkhkFKSQARiIizAJXndEbrJD9nLCMA3ZBxGMOfJJJvG+QaznDNz3 -TmiHGsMwusUAvUsmXxKqJiVq6iyl3NM0VXYa+NRyOBNkLHKCJk/PlJMzrcVEcyNmAmk1eGKmzelh -OZ3HSiV3TYm0nMyq6byDCsaRxSgL7HCsQmKxPsPF1KCW7Nme84glqVePDU2yHSi5tpSn/MQL3Cvf -Q1G8h41055hdnoQ3UgrOKYt4+phnRMAZYoYxM4KPUSycIhsVDxejcmfBx/hEyiBMBG1EydHCGYRT -VMk0IxeE0mmK0u+INGvHHR2E84V4XzJGceZ/iRwwk7HAlAlft6SuZ8IhnuRrv0UK14zIlbdhpBhJ -eGaYvfwakev1Jn8nFLHXPhkcf9veO444lnjUn63Zqb0leVD7o6Hze3CsW9790bq4yWSkzWeiFU6Z -0zry5A2bOKvG7RSBEIitMHKnCbkDyC21Bc/KoiYWlWfqYstQ47GKWByJVZEtxUWTMMFiFLoQwTFW -CfFDJH/wBBCRBCKlgiCEoiTkLuQjPWXEEEIOoS2h16mFEyw25qJsifldSSNS8ojY9DVuWk4qMdNh -tuy0lKf5nLWccsIaoVL22oaY4lb7qyRZ/du5Isw2+ZNUiLIXSVk2lc9K2XyUVVOZsOpVX8KaJcxZ -dpCsc9LGpLmNRIurS95xfJtkqO+18Nj2yBj6Q5KE1TnqqfhTt22aLJw6oifzU52SdPZA1ROUTn8y -eaw8liqLG6HKZ4odRu8pLT4R4psEBj2A6KpmodY5U6y7ImsskLRVOTuRL4sl60rx70oYb1V6sjeO -CbQWstjOIglrio035pnryYYbyY5j6gJsKyUrV5SwzI0J0mEcyYTRF9OhK6e8LB3zNQdprTWlPR9j -E2oLbikPfcKZtCE6C4VGg0ccXHutf2wzpKk3Y7aGTGrK+MheNGaSggSUk+Dzw3Lsawn9qtG+yTiL -NVeMUOSOrXiV6J8yFUee4t54iossxZ6j+OULYSku8RSnTMU1gdM9W/FZWC8XmRziLIZdMxJqlnkw -e6L/7qQ8hfIXB46XCMxdhJm0szEas+dgIb+r0m8J476UyRjV5aLcD+bL7aS0hvIfa6PbUkZIqNNX -ImpSJjzJFis1UWtB+uzVLjg9vpkxf9uA3zPhc0csGfGwxtUZ6035HOHui3Lk2PZ0N9SSP7MWUOEZ -m6V5yGLmZNwbkfEbZKT3i/ldUuD5LrF8l3i+tcRHzceFcX1Htm/eRYsIkSlh/R5UbaVd1RFVmnKA -6w4LttwU5K3HtZDnNWdrWvFgiU324Wy7MbYxaUPW+kIjYQFX7HZb+9hmyOfW5EJj0iE4GSFyglu4 -WPMqQDSYYKW7yhCLa17oT66NrqVKvhyQcEUvd7pE/rRODsWWv+q0YT2wnCK0vhTaumllI/VEuNpS -W3bafLXBloUrTlfb+OAmEhWuODyx9eUGV9z5zVNayfmGjjLyl3UgSYRCrtMIBdqXAd1VoD5OAd1U -oT9OfZ9wqd1zEXa2CVefc9+N+CfoquvFT6ZBHL0IvMMfKVEEPsfl7nbEVWVPXLVBiOwRzF4ymvmI -mNhWwtJgbG+LUCtiKQV9rIt1JqU9r4yllPRBECVNrI0laQjlClnicHkGmG9qsIixQuEchdZG1MYi -lVVk3CjkUZOrKMLyOxmHtO9gPtar9LcR9xH3jnuV9MbCXIsLaw2ShNZTElVLiVSswtYufYsPaU9v -wBC1SM2gaVYMTquoXsgiiVa9JVqtkmg1UKJVnZAwUPUQkL0oRhcKEHXQeyxQdyZ69JGo0X2FkQfV -oNhUoChgLQTXYliRKUFbKGLEY0YUNRIrTRQDFxJs96GLTdjCPDRTgh4Zk74VECQZ5uWSo0d2enO9 -L2lP0gAMaBS3OX3NltoUu9qEsGrnY28t+2W0nPOEVZlyUzX3PObY++zzPP+8kIFu9TRr8vzezEHP -UTZOV3mW9qO8YubUhis+h2s8aoJYJO/ykLalYS+2mupS1Lst7LbyS/TdjQ681aJzvQ== - - - O9fNE81dMFq+RY1/MZuA22rtEltCQg+nAiGjaoruqw0SH7eTZi73Vhw80VUjts5iVdfOxEIjsQAX -DwguHhBjAmxpSUU9oc2O9M9ndg8ijjmLBQQXA+jk7FGWg1n8/OyUrAiDEAiHQJ4syy398e2a2Eis -UsRmvz1gl+RNd831nVBo6St4m664srdrWH8ur1xtJ6LIK65eW7XpKuWXrs9BqMfVQ4o4v4WytU8u -TnUh9EqVRaoaQgFynGo0D+pM6BajI+dVSBksqSe1t8gTR5uUeLwWhGNDPgyODCkYhGI4DAF5+cLi -zp0AP2atJUwYmVrqCbNfdLLT8EI+zsbVFZ45zxMzFBxvjrKsKMcKn3DxdJOzLWFWwTPNTrQVzk/H -qqKcKsrzpSxfzPDF/F7r9TiI46t4xvasVyS9GK64re9xrdLHw5/xsdfZ/WR2xfTzpbt4hpDSe9rr -beQk/XepGoo5QJ0ZU8BK3f/6fMWbr5hkgShANhU5514Kb/4k8lE/X/HzFf/arphJS4vX7JW8fmL7 -UV4xQ4QRbuz+Vi4tfv21X6zct1mgoHtt2m3lsurkRaNUg7TlJdv7vIh1UuA6VgiVJikivvnQQBIg -jGWgFgf2WM6uQcdfvpAhiK/ZmrhltM65JETE2qMKc9UapBRphCvGKktJpaUib1isulSqu0TAAmIZ -HBLmMM/SpfWXaq1amtRbUsYr4+yyffiwlfHg9qxXnG+uyKes1QmueG3V2sq9uW5d0fXiuk1dFeX1 -urNSqfLtmq7SZIWW1ma6Lv2KbGRFhrzqV7YK4wpMV1+xQq4A3j1nXc4NN1lO+JDwqhmrmjGqUfyc -WClmx42cIUocPL6EKTFGtRg1Nza1Pb7h2znjGWcy+ZI0Y1zZdzVXvHaMaHOBEY350EZy2VNlU0vw -ap63/eBXfDS6DfFthXcLoeGdVoCYYG6tA5xcdttaaKUXrJ/s+F2yNidtStromsFZLdKhrbPWWmtc -6kaKynaocWlRU0n1g0SKZie1l2decomkgitGKRWlU5r0ccrkUJRBqfRppG5s2NTkTpkyr9XjDomk -ITnz8oVL5ZwLcqbxKZ3GkMkoHS9jiLNxGyOvOGaM8WQJI2P1MA1qT1zerTpSst54nLouJNHxJ/05 -x8VrKzFntdzqeuj5EiMlOFI2C2YPUi4jxeHDYayPXTN2h749Vm3CIPFcV9QUSUqoqWP4HH7QgHpv -kXVNfOxq7CznNIKR3mZh++e42t1R+7YctW93eR2icROZCdi95Mvd+qK2OePDycBwF8f8EFOtPfDW -aOY0mu3KyXsuCI7aDxKrj8XkLxtOiEjFGEnMVkfGGEnMRkuA2BJptpJOvkfH2Fsi/EkSJYmwjJIL -OoqoK1FZrA7WCr0cpySxW1idwpqGRClIFBhtCMYKQlVcwmcOgRacPXVmjqYuxnTW4nzpnGEq5cmI -vRSlUUuiCpeH9zPm4dKaunKWOasMbcFUj5roH1P981lrd6jnPPmczBzMVk/KSkpAp6XlI4lBQgSZ -EGorYaAQzlFhg9WIAjsCSU9u3mpz6g/ZvDUEcx5kzpDSEQPZTA2PAWwMXp8Vp1D59Is1o7Y4ZdQR -+/QWIaG3gMPR0URokD+G+DWrfktv4QP7gzEBwNFMiVAa0l9PygignACaURpzSutNXr1kSSowVVTz -2o7GG+DUHR6DLqrWGUS7VFp9j3hph3qJUoxMjS6SfeRUHynNRzpbca4ibcpKKRmRPCWSkdyaq5xI -xZVUhtnaztVsub8x+zfChz2A2EOIJQsYZkvVmMfM1LybD8wE2bdygh9FkUW0kWbuFFKb1oygxRfP -XgpELSlVi4Bt4IqPhxHlpC0JbQvMfw4lap154SE4KaAohRQ5MA7Mf+/U61I+dCEj2sptp9nQnSSU -7mVEl/OhhzQXOim+HUtvmzpcpJpnw72hKnidA/VwamwO6Wkk2ZVTW2PQs8KgJ8F5OtolCOZZJPSJ -dYow8NlKOQmcDy6hdLLQZ7CCSLHAEdwW1oGUOboeDL0VJI01Vy1FmHuMo8cQJu4tPlZF499IYm/s -KT7wBWaX935LoKWJQEsnBCxZLzmVa9TgrqSKaYmoSUK75/xJn9K/bU93nOQcpgzCucXkDEMVS0qw -9jgbXc9F6HpA3m9StjojG7XU2YQ9rM30jhODKoQUoycQTkpwq1pGLclaTQHab+3ugNtOuJWKcJQD -yiklq9fiYoqbZ1pjXbsn5KziRVFHw7HBNDYaEdC+EOiBBCEX6FcntEoIrUAgBei+VkhFEzFaIyjx -6RhpMsa8n7rEyRdY6kJcUEpcqI4odUUpfWFSe5FcUrEkVFIUCunYpSyULwyVl/XKC3sZgb/1VHuW -p5rERJP9vlXmWrN+QU8nl47VWiqWutnykl7binVZqStKbdjvUVaqLCtHAMe2SaTrZTysfMfWPWGJ -Q0nqEBUfcQ5Qlz60FPW0PQ3AdACypTRlKNUDim7P3PF5k9dp69vY/QS7B3rNC+iFiom8DI06Fxp8 -s6Vftx39/QC/rJtD0+F1nGfgzgsxFST6FQKSbeAnMUGgTT0N9Bb+iliZyk6K+65yr3OiKfommpzb -I8lz8RkxGmrQbEF1oF5cGkDt0gAY8tlKcfnOJQEMlgSgaQBqrEqmrxitnNdUG3JUkHMgfhT7qTwM -asSeJQu4cqZsdEOII4KQmMrEQK6Ily+Mmy+yMCgj/mi4NkZYelb8tKC51j1Y1Nh1lSOUG7/bFL2O -Zc1jBYTIqm6c6qDcK6tKWkmCM38Z9X2OnCquslhEemvB+pPVNbhYjbG8ytgorCmL8KWspKNFdhTG -e3eeA4UiYnPEpD/50N55/Q1esZYrMiospTINgkNTtSUqL6zA6Hd1R5krkTL3o2IzmIOKVZyTuRZX -Sy26OPeicoVpIlGQijyaRNQJ28ogzGOTuR0XJ0N8ApF3QErqkKYNuaqqsXl4RB6ASV/OQnVmJPwM -Kngh5biUZFgCym8a6CJwxZYwnb3xSAxkAtCG1PIh0thiVLNZHmqr5t9nJny+YnrFz3LpJ3dFLxf3 -zLy0hczw2xJBNxtJGiUosp70zH4izjAvKTx5wcVliUWnlc9/846rp7iuSplw85bCj9wX3mWVEvil -zqrRuTOjQ9PTuq6ohIp7ak6c0J4fIXVDq3tzNsMnGj3k4pT6k1uHdHR0lkqk7zmlg+NJyKsUZi7P -Yl2AIsaDGJpv4TweUdOhGUiT7WKuf+HsKdFiXLLmX/4kPFFl8VOy4lKOokgFE1mKlPoqYSTStQhX -1PXo0RoJkiJDRxgGIsE9GM4BVueUoRs8nsHjGFIMQ5WsY4daIDTjFrOwj1fwuKoUqRDXu3fJLg4z -FfFSW7RC4p5N8QrkpNx3CqS4hax+ZoLJM5wUVU9yiLz9JLxHNvHEkR33HM0YIiRo+bjWX2vGRLFt -wyObMF4It86tNj24wfyUSJYKbXlQe1gu3UPyRR14SnPptmiWGl0pzNCALKg9E2NPykdqPxBtKvIh -9AE/mDI+3HERdg0ZwoQ5sMmHoqwNYYP5qJHGociyet917nbIdGWPTIcumTZHi2hqym5hSFcozJcJ -m115SPHbE72Kr/sxbDEDTUp9qQUG84KpghqAVaJlIztJLBwl3TVxtCRuloggUJrLKSG3jHgCT2vp -ySxTCstZUNRa3C7SVBI95csXRkzZOlJKT0HpSSc9A7Wnk3TUkJHm3JiptW0kxA6WZKuQRmM+Nd6Z -bHt0eBKaf7jibLVHTq7QpJrt+nURlotowDeOB6QjktROSGMGwZ5MxgMyWwQo5QFxTCDIi0H06IxM -cW5AOoyYKSMeABwriMJ2kBhCdAuqEJyDEhWdVZxJinwUWdsEdp+Oq2eZBpmYQjBJLBenYkLotEkv -l3Yj+pbzkDTJnGqFmd5MikFnl0yLQeZ5EKL1qfJ1ZmaZae+kWYTyTP/PFGiki8EVLzr/5r5RJ05l -lGz6/8gNQ04d+UkJ3YjgTUovK9kbu3yYYFdRS0pINNrPkebI08jxyoIjNmGb4U944rnJOaT53zlB -05SSNxF9XWye3m7T0go9D6mjtldJbVNLDTTbOanJ4wuAXAogkpuFNOC8ScuXJAVMkhImp0SrHJzF -leBgxe66hbjf1jJ3ddkFey+4WLC7ThaGmoXJbhJuy0HsNK1x3ilFgpE2qMCMohQrK4vc27DtFrh8 -XU28WGtpMbhXhA+VKhmtboZ9LaBolXcZgOiBvDRbQn3hwd5aMhF77SEkuf1SsF4IbV2yXzT/45RZ -MNfzPmB9wXrz2UitUQ9EBTTus2qTlRTtnTWxe/K2k3qzqVAV4UXLDjDMs/fkYCKzp9HzmzH43PTK -FCzi6JlpXAZCyRLOMwNKlvCcov1hdXi0/0Mw/iWUv0P6M5N7yiB/Pc/wVv5flqN1NY8qByMlNaBj -bfvI4qA8Dk2xegAzG12MsxaLZjRJnCtWC7hchQAp4An1CtxNxl9EvRMGI4U8KXuRcTMm1dNd/S8F -zUa4bGv1vpas/otCZEFlBYWySsCxDIwNBop1VWBoTKwGjEGYBQxLvQ4ECcVo3kRRPAY7IdQJ+2ie -AlHmfPUY/Vq0EpeogLGajB7hsarMoEoAXJGVAoYyN67OTOPqzNCXlvQzNWV1Jf/w68Rl9ASodDJV -R7/+s2KNm9o2D77ijp9aKONifSTldU2ZXWN+WsrtGmnjlozbdUi4XQ1MlDG7Rl7X0QA3fWR0jVyu -AieKPK6L8beODu7yTLV52atLtcY9hOXJlXkVtALm5a4fNyns87hSNruFbLj02Qa8mWcypXlM1zKZ -SIeTkkTFvMlc4jpAs4sEnPbqZwvonHqWgpiljBFlYuGT6xPrbPNc80zzPLMeybPMc8wzjPPLPnuY -WTKnV1n5E5EkdjyrMqc8ozifOJs8l7hyZ5nDSITInvhFAGHshW+EEPFMhIgx15KhX0qLiGYoGqmB -oF5o3DLMS0FeyGPNIC+DeJGp1sfKV271xFZ6nd3J66u6lCqsNAK3bZPWJfl/3GLBC/WpT5s2s+YB -V0xzELevUn7jhdj6XFZkMW+ylGm5zcsMxJDkYwnlbNF2v5n8X1zbENtfaWOhDTHO59pQaPlnenHY -eOq4hv9NVWpSdEb8xB4DUSkyab9zXEulU2X719url+OlVziWbLyvcRmV2z3n7RNeP40r6ngLbq2c -QtcIYi2pKJsl0rHjlPXNiWr9LNFRamlZLemejFBTfNpq+LSITovYNKv29fLFpt6X19fXDJUWEWmp -pu6QaFTFyzOOpnyjRbbRhJNtwzQKMs+4RiVBQbnZFgbw23hfXCuLnnOxbUhHmOmeePXSViqmMW3a -WGjkv4YrDknrN60rtO3L/KJwxR0YfsK3H1uVtxwWBTaSZ+SPzYxW469dbrb5MS2pgTILgPqpL62q -Mj6hDfsNrnjlt1bZ5RENrqjeup12C6jdNEd0j18BasdPcDQOo3UVopYDE7hjgI/fIQ== - - - EvW2rkdDV1v1RIqihQn+pvFY7fuvJZzwWinRijVW+o4lhQdkeseE9UZv2nMKeyyb27fwXj94dvjn -vTA/rf3lqD10efVTw6N9DPChGoekY8Q6dXuaji1YRp4x4P6L3RvZLKfBt1/gIqoeEtjMQpsWtIqB -zVjtfIl4UtDOL5IWbYnsEmCyyn5JSnQe4OQQZ+QHhjHQMKegypVNNdb4641FVdl9PX8qnxSO2Rcs -z8igmvL7CkGvMfzqlp2tRaog5wqUUKlvl61aamHUGy3zp3g9srUqi6Xws0+I8nMlgWgK82goOs6Z -1nMPSTXGjgj1BwoEMgEBB/9k5jjQJxzUSW6AZQaMCffz7LIENE/AzyrPay3nX5BT0fM5dwlb7pgx -Ouu8a7OTn6s/Z1rDpdS66nZ7wNz0N6ABwWbG7SOqZZLtJV+XTfZTazkcmuwXIQNxVyk/eJC9lc6L -n5s5mZezzcslyeGoNREw4TBOubbTudnOiNfFTFMj7u10booz86S5+QEthL/xKz7A8kyigqUIbx7j -jTQPGuPVKG/torxnYnVaDHWr9A6dq9hQJZQBkeBh2ngVI2PSY7yK13GHtdRxqLJoXIkpULkCZ6cy -j9ZcCVZiEEwr+nU7bb9moLWbc2NkHG5uIgGHJ3SoXVWNk82NRmBT7+e2lkZ5Zpy3F+ZGSTiuz8se -e1UyL9goYf8h/I3z9RnxlRULs1Ee+eZ2+zw3P/K58fQOOT9pzvl4m5E0if5L7N8xPxai/tUW2Zzx -P7o4BvR/TKIZJYRzKLKOKi4lQ6aQtdsZT5viUjwqJa2omDNCtpYvEBRrIrgSzRrwadJDMQJ3IwYn -Ebg+jb9J9A0Pixh5K1RSxIiGq55I/iXSKdV/pN6gWIGU/Tmx/ujF+Vu0BqKvPjoI+E2rFMbaguwy -Ltcf5XqBvipgUnmU0OKx0l/lKvht6/T5SnylmqNaHS+tOJrXGg1JhVF1OWdVQ1110MXFDLZRgQi6 -S+MCJb+/KYFSbsy3jYPscR75B/nZ/1Ka1eYvHb3KdfzOkrSynDTpSHIxlwU+42Ev58GzNnq8GvvV -Uv5GkQuOKfZsdAleOkR0WmuINK66Wgv+bJUAm0boI3vskFUljvlFKh3OEpteyP+u1Aq+RrGP0dcF -Ugw9OHycXuRFWnU1lRmZvMilhdBkZPKCq8ssRgWyrVhcqlq8lRpOZlD2g1YVLciNrF6xlxup7DCZ -kdQqnrdyoygzotwIidyg7UQkOLyx0jrE5SrDt2UInAEbCVJyz1yTG5n0KNRtuVZ15VbtkVTSPNPr -GSRNKj/mxJ1f1raGolVitsaG41ynKpUu56J0iT55kywgaTweNuoeUftYHCWLx8B2mQZSMzM1aSCr -o2nxOojXQrwe8hhJMySIoChvPAVPiYRnFO0EGkiaAjKIJI6vjz4T+qIkbTbaCflZTwVp8/ga6Zwi -AHJBdJRYwzitkB6rEqd1iHNdxfQUkAvnHU0lrYxeljpNJnGCoEmc5NlUPv8saX4gSeNw/AmGX/w6 -6tFhX47ipdAWRSs0wNSgvbWAwBhgyzBLfk3YKMRFDbAVEBG1EiJqIixUA5O1km0ywbbuYJlXgn6a -YIcj8qmm3Yy4J0Q9dUQM1pDlwHsTcU+97MLadh6e8LzbGtljzC8/GeapE8KrmurbrXI6z7JDeF8E -3gd21kb6rlHWdicrOvjVC/OwCv3VydbpJBipuBbTlbe/ssr673/aWvl8xc9XvF/SVJWLd8ZYjUZq -8hTO3kU6XQpnjHBSzrNP4CwTdaeEzy51c0PUPRFqsoQzCg5ntEUaRaxRyHmxQHYZM1bCi6WYo1YS -MT0zVsqNVUlSZUMaEyKClSFrtMyBk2CRPEtWsAyCmEMwujrJViMZNDnOJ6gFo6RVkhmppHWSY6Vk -rZUcObQUuyQVk8mDxmxayqeljFrKqUUUpMKrpcxaQahXI79WL3k/MI5CUDoT4mkh1NNJsjUERyrm -CeNGA2NHDS/aCWbUEKKUZ8KY0FlxoZL+4RGgHucZkZsRrdkK3Ss0OkulnKxQo3JTpglhZs2YnLbs -TZcCU9OWkSlhYIoBO2u9a5Y2JpXMR9emrM1ZWzYtSaQh6loB8O62DXHHTruTBemzpPksaT5Lms+S -5rOk+SxpPkuaz5LmJyNpXLSb1vtk611XvDLfcp6BX/G9ZAfHdR9XPrzI18yRK/yuceyLFhGg4eKI -N3/nKHgspxAkSt5KaYWWdkrrKnsOtGuUW9e3HOjOsflZfuJ/wQuuyPH7GMnnuH5MwtZ4/6a8nGuO -CM6qQ0bMQPxtYXncbg6HcONzf+/zwFPG45hXcnLZ4FH6cYYJyr9BGJA504Qx+rwqZsk74dXBXn32 -8NMKoYzpWrLHa4kJNBR75AhkJ6umFdRGb9GFgbKse7eGfMtfE3kAYyQUvktcI8pc/z2Nol5LqXcR -WInJnlxEdsuy8KhW5Gkofk7m0c9hms8fc/p1DnEWKzvFWvKG6jxyVvtE8zgIr/VJstzP8DP7UCn3 -nwQvRW5oPnEu8axraGZbmln83tHs9hT76Q1hM1DpKP7S1heaRpUm+Yr/GgTPEiNRs/uerolriSru -BVecVSJlP+Wv5WHNYmg3PyeMBi3RHo1UqOIMB1BNaOjeMxqA5hSoaMUg2tKZtKQg+WXIZqBl2oIr -0baI3uNLs+H+xlXRyG7GXRyLsgXag4PLEaOibCR3Wcrm2WEr6RzK5dAR0wFzk6t2ETNvL6pZCBPL -VquIWbQxczbXKE6bXFk4FDHLLM2MdeR6aSJUnlTWlZvTQK42K61xvUZtib1U69Qums3vC4QQwZvG -7HwpDS2mMUg+tRUJkbicltQ4JdG4QUpr9FnUX0tsXJI4/2LxhymL6yfRtZcvXCw/j9+XImlZ7Gwb -N7Mc4FK07HpMbD/29Shv8ed5/InM4/4slrmE92ZR+ISpevS2sM1sGB2Nljca7bMouRbtWRIkTi+l -bnQeXTT8QfOYRrsbN48W305i234el0fOYzl2/ZefR4+2TRnnyEJI2K8UDbuKNqb6HPOiRCZsboqB -y4sBBToFa8eNveFoM6xMBEHpQK4ZipY1qBPpU3oFZWGJTNvCyCLIFl9iKOLmzg5Tm+Jpm0h1mdxd -NXBF7kU874meYZHnmOj+zBfSUdnPTp6Bn6hRjE3CBL6Injm7Z3JPZU/EWGG8SkXxd0boTBSD5yh8 -4xhmOBKPKBvE+K4Six9JJhMDycsXjoMEdzAzkGi9d96zLUpcYx7BvekYv5vmavZapFqNlW1i0bQu -+rfUu2U5Tb68bu7d2jIDgN72V/VEfeGJUg9grP2TEtJyJv5JmLkkJzPjT9h9plwKCKQnpRf1zxif -Mq1RlFYpsmxSKbqr2aSauebJc7fEuZwfupi/snZ8Dz2hz5PKRJmvMvVWDkKAu61MFNRTSbZeibdN -eCAyP2XJSxl9Nqt6Kc1PGTZ+SrXBvM8m2s7RY6k+y0psBGjGhZ5ygEfO7kgym9afsjzGvPYUz60w -v+2vP78n0tpTW0JkzQqOdafizphtJeparGx+G8fnMRoBMpMfE5ecESBHZo9OqI5HITguVaBilo+8 -ChU0mHmpRVX0yqgtr7Ot8+390uaZ5hlH9lE8sWyeEu71rELYw+fIdh4Vn4z5v3mNsFRKqJyItNVo -CkbKatp3hHzss9pgPl6gEiPuQZ2lh81T3I1+P/roweqiB7Xzn3XiewGN3CIIi+1LvzNr50eLOzQy -LG5mDHYozdmPPsd1izEs8Sg9pMBnzGaX2JWrcJVzD6wSveIMaWUboGx1y1DHVTnSOcBMkZVkm+Oq -Q6lworVWkxRABoBZdj3KcyylPsAELeRVqYkfcoDJOg0rPHZD3JDoS0FJir4UK6udsXbGahjjI1nt -JH9M+IfTii9dymaX1XxJi1r3joFY+IddoctrVV8iN6BnB/QlroUf0PDTvupLzhNYYgoscwVy3Zdn -rfpC5wfZZp/n6fM8fZ6nZ54nzzJcJPd7XPN847Hgwz4D+eAoGK14xMkyORZLVKHjFq44SdkJzR/r -XX5HK5QZMdd0m+dxSsr0+nxTLh8dc03V3+THvEo8TX60ax1pGN3FfEpU/ll8WsWAydNb4YpbgqvR -tcE1fWmKCmlecMVWKjoEl1WXlpzOS2r7HDuf95Ln2G0LUPui2ldWq3jmpOg01drRktNDrHf5oxrd -4iiLVzO2fKSrLKdoO8q+vHeaVxScJ9T7QW+Nr46wFPWmHIezZTjE0h3F4p+PbC68I5kPvp037eTa -NbZSUtPhiuoDLfONbr2g1/J0SrkUd79yvd2iwd0zNLcUBRnl27Bp/uUJ+NTJqnALOqqpSsXZ7NhV -ADOVi1CHJELtLSRvI5kfw2GtQlJ5eVN32Xgutd5yFsVUhkvQNyIf/XI6fR7dz6P7Vz26TjMrW+1V -zmfmi195G93b6cQPSDnbtbGbeZzpaNWntXa9spvlPIGjMZqdpQaeIk5DhjjN8aap7zt6j9jDJ/5l -ON2ix3YRj576iYyXV7xD5w2etMDLi/roeI15LB1X7wVpd7wgrswU8WOc5OxaE39InZQOSxkZdawF -2Ss1vgndK4XA8qJzhvAtcDL2Gcb3nGJ8YVFF72rE9+axht55VFPPvos38AywnvQDjKkUZbMCXH7l -DjKeOcdlxEprtXRmWukSVr68jF9IMNOjK+NXQk3HKERETJfGM4/elLmmHzamd73+iq+YS77tekrl -n2N1JI3TPLICk12zOkla+0qrX/n6V5H3TipguYpIXDlYK2CNwubl+bxSRq9zkQcv8nlVJ3hSV/Ms -VpvQehON+kmk4pkyd03iGYmVpWupc3aWOiO3Ko08otYIndXd5zH9PKZ/NWOaRqMVq3X2Nq/hsxDV -UxGWJyJ5OKt/sIz+QPVNVqm6tIg9qjVOOrIyg1qPLr7a2R2Cw37l7AF6nzbei3BBq9jHcj9isRjE -ylXLNsT7ujv73vm7r8ndpwSl1AoLR233fuydK3UHn12BMF92YFN4QMCgVwoPrBdKCqkpAaSlNI+B -kjhmSszAVIuLc0T/bd3ZLXNTNadU0TS1qLEwXJI+hqqQlTxT5VWLlGkps8nq3Pqr+uQ0c9AZ3kor -4p6kIu5qKq/+XZDat6ym9eKG4kVk3LLCkpnwj0QGkt44SGphITlHm9X+2v99ZDDJr1C8xk79Ss/P -WeK1Pds1kjrhDdfKhh+wiHfvfzB++l6J83vlopcfCpW6n+d6xLafVTMIwxHs9XAYxyOahLtVD/LP -SSVyKhyOFQmQRp9+qLqRqphbkXNo8c+H8ViBDe6LkD/1ElJ9QWuYW+kEWM2gv/sS5q18NfxZd6n2 -WHdD4wswPMfl+Ml6K9eA84JFB3otri5FB+Ca9gWTNB1DB5O8Lbpw75X4eWqt2FDLiINFAxemugij -fcW/bupjCx30heufeAWtk8HTeWy1toX9gBMeaLCx7sVU+AEWHqyB9ogllJN6Gc91zQ== - - - 0t7QftRUcqPe3Rv55x453lpPBLZy95TRzuqRVLRv8HJSJqS2fXQE/QlWMf0gHxp5t8Fl+mM9TKMv -fHL/pe6upFGVS2lUX8DAt66Uxm7mpJZBzqmblbb5LJTNqDjjMVTT8XGi4nAo/lHwM/UVCvsF9FRU -hVERron26kTK70CKLyi9oPBqMUBUcwOmZnEhQEnpYjivFgGcpQRgwyUAh5UCpBxe5uByJWHl2aC3 -HFCuOtAM4OzMQ8k5hH4/jIyhozQw11MClCU+iCqtwblRataninRUMFMV+mJKtFegVX2uMoPoaS+0 -a9BwYqMJzaUWrtiSqYQPwK+TmEhayBgVDk4+qmm2uUAx6myxEHFPpGc4862lR6VFqk8ZBCIFP+D3 -hqjRGAJRYSlJBUHgljAQRKToXsiRPPqSmfa9S0pnRkJqoe0mwKem6fpymrGkpsLvfWHNtLzmwAmf -DKkAo82D8I2C1oGSGXSYUxImxITi0PSt2cCZKcJvkFItiaHFSnyREgYLx+Iz2GIxkplgZFZ2hkuY -GcRZgcPquhsF6hzhpSvllivZATvxxIHHjmkqGSfwYXJOVwRGU7AiQ0sVVEqwUgdTZFCpwoYFOGyh -jBjG0NCFhi/6JHihwMRF4ImrgEkl1QGhpC4RopbU3jZS2ZO72k9IMOdrSFr+79bsBW6D0VLzT5M1 -vvHJ2llsCbMNyD6oxULgFuKNwKjF/3tiSnX3xk9V9v8tPe5Kd3PJOkSR6wkrx4TIj6VWgc7Pwtgx -ZC3haQlO71H6lQtaXg0+77lczdAPLnRQsOwyF7d3byfObUcIsh8uuJGYYKGaJFDz8sWVAooPKJ9I -gbukdCKVsHts8cSdwBwXTqTyIk8snVgunAjGp7Pmoy1v++1ihYQ0ZNFIPcHOygkNtwH5CvcmN5cH -fO+lqeSlhbaFhaSUF6yEWFLIl/OK5Z1iIsY5lgkSYLiXrVLKiwJ9Uc5ODsq/WPEukbcJqD/KXaWa -kRAKSF6TwpbIIQBylsYmjxVE3khVSC+bI5gcJK9ByjUBYGWImxQiT2BqruWx6/yVY4YYygZj4kBt -vjVZC1lLWHU9EgauqFA43/KM6LxC8ilpi290vt0CSaWx/D2AlLwIIpW2vEZnk7SQtbw8G/Q6Y/IV -TpirAfY05BTlcZoiWSz3qxI9K16ctluFkjlwPyah0Jk0Yw5ncTtZQP/sgs6xOehV7V+eTjm4k92f -134Q43LiAGsMW0d/HjfQ3t0pThTQSWpjmuC4CjnFyVIt87KlaUnQSCSOqY2taT1GJC4pikwTrETi -k6Ur8hJUCvFGaMQFtEpQ1QhC5b2u+zfuzLjjdC/FPTJTuuOk69qSmBsuYEKrb6UESCUkXhxkcrAy -JhEuuRKY76z04FTUmEGTKHntzLACELUAUlKSdp/GquTsOp4otIWM3UjYGTDDYzeQvdnLmBHlOkk+ -lIELScaRZCXLJQbbxrIuKH/JBpWxCGqJyhhEQKPCGGvqtbdDR+otl2gJBmAE2UPnlSaIIoCRC9Nf -kuL0an+ionYRG5S1PA2v4JrHX1cEcozBjcn5nHfK3LVZ8V7fQrGJ3glXjFpoDpLMqaDyV1peXriz -KK53kjpssc2upftudM3DsR0zhtRx68TjHpvn44g8HbFKvQd1RuqrVXzm6jc34eDx4RmavLYWLKNS -m5YvTBFknM81ZOgwjwo7CUdcggdzaLAcD9ZifpBgwqJhleZ9pbl6q8vPrKV2QpajSTXhhiRDc49T -rsAqV2KUE7Yr45TT0TUStLymadhRoHzu3bRVn2ArxwroXnny2ZKaL6nqks/Bi9mSothT/l2aFasK -flTxY310XyHdq/lO0YfRnYrK/hV1/3qldDAEEoV/E2TzrIwt1WTNzK5doys1ucTgyvgXUXlOjS1v -aqmh5c2sNclkhXmBfRZNrNzAolmQOu6s2uv3rvi9L3zvzRzw3weXo62Ggv/OXB38fRYTYpJqGHzs -ncycEKOCMuS1LumaGBn03XBTk+F/ZkOQs8p0ElP9YqgyMuDFmI/4MjbxOfreijMEzf9e6vSO4spg -IaXugiUJcmpk/WKBTnJCoDOCXBfqtmBDrxFGEeH1tCMoK7BQDG86+IUHX3johQEvhG7PAy9ONyEX -VwEXojXMRnHRSakWLgRlAskV24tl9nyBvby0XrmsnhGJYMHOKwX1fCGcUjm9tIgWeS/FJ7Zb6MZC -wK1AYRoJ8wYpxlhzwFf8v+oDXiX8e5bSpnliHIhw5fqQBLlRcqHU/NJq1lqbt7GUOU2bk9Q55zne -FjjU21JCXRqIxvAUhwUrrcleawX0WqNhLQdeOUpcH5H2w0JAT76AhNNioFKD0hjXxQhdQwXtJYQE -SnjXYkCur49dM3aHLhxDOyTR6HsvJUE5DJt32Cc6GbFjeNWWQ1r6Hqio1UTxNQ64Utf67jiA1PcR -uvsvJqFRHdNGo861/BF1TaO/Usce/pI+XsAJ3Hmhe8N1XTFa12Hh+94F60gvUK3As1mOxISzGAde -6vLrt2h81gTgoaMm0JsepdqAuf9oU6lG0AkHr7LvenY8ZLCrHMCnEVY85dlVft3FuQeZAe/CWrJw -6gbSqEEhMO0tcpd47oQcjW3IYTr9z+ZmrckhFCgDHnUzEPuUCe81M9QEQuJsZbbBkQQ56mWsk52c -29W5XkU31rnw2pl3w/qZ8dkSPmeCdLbMge1x2p7XOmpSvbGKMANjZBPxTCLqwPYaFbsjI49IL87H -E4+wuRobQ2h3npUCNGHnVEz0Ya8NT6YLL9FtCKNeO3um48yXG3jvEvp5y9HQ5dkfKYY+MjQYdl5R -8+eEVzxqsDjuHDTgMR+IJ2Q2bgbO5+gFAX8ixsuaGC+VmWF1vAzMd4njFUgzHQeb86R6rK8MGWvG -WpU2ci3GOpBWlc3qxJ46ON42vH9lzjgf9PZh7w1jHPTcZ01GvrjIFhcZ/3IsqbL8rRFNag6yWC8v -1uZ9cM75JuN8Im3/+hjOxTGse8+458ACMJY+D3ov+9wDB3zOqc86JfiAqYM5GjjBAj9eKRUetiIO -OFNK0xz9NEPf5edTNoaO1DV2yXJu/iYzf4ND3nEhFcrM1RaIYR4eDW77AHdPFldvEYM04L1hydoJ -zHQuDwhsQ2P50oCM5q9ooK+x8Esh1CcnUAz0wZg6HiV/9kiYz06B9krrCi2NB8SKqT3zNhICesoY -k3wYbSF5h3v4bCG1E+3rSxZaE/ACzF/OptSRdEC3yOTCayeSlyfaCT6oFgEKvdn+o9jAJ1o/OEtr -rL9gocLTlebpFtO60Wk7x5qNVq8xrTh9cv9aJUdNPxGZLmM43b1H4Uz/3ko18i70f7GF4/8tssKB -+mCAYA3owxfpBfpe/M7StQnBWczLutO2FO8n1/Ky71b0Ha4YLdPhhmVaJaiac2KNKh3JYIWcY0H3 -OiGv1CSHeXM8eBHnhBy50oaS9c2izlDp15jSp0Ibk+Z5OkgHgitG6/S6fWq2qZG7xBnQcYdOw2h7 -XJNHMzVXRjmOcbT0hSCU4iT5CJ/jCCdW/UNac6WF2AwPVmct5TrwlSTWjb9APQbkNYDRTvwGbs7S -+Un9Bn5OmnRO4IrprKzprBRzCvYL0jwEc5IgTiLNhCXM1oIpaizngP3Wo0tR5vvrM5yznAZK9oz0 -FS4Vt5YrB0Hy6B06i3iO4tabyK03JXekOKfPR2g1F6FQCfXRjrqkwqrPdNDrr5Zz4HMV+D56J72X -Ax7adQfL+eosmhOSbIjLTm7DJGS2g89wyJeGQCcHySrz46Kj0htIthaY7EnGYyyNh3Ndeh1xqyGW -9cPUZQliE1nfTDf0mmEUl14vdFphxtk0CLMtbHFiDVotqBl1wVwTTKG1e4w3rQAmr0NqozWhkNqx -Nr2a5n2SFcZjWosjeCHYqIJEdSzPBkQebCx5JBWOPBvrMIOSaxtFPn6ScaRDR5mBo4bdknVMoWJi -ctTxZP16LGrYi9OxEw1bwsXGJwQLMWH9duPLo+s5hdpouRXGeeTUI9A3tqN9SmDMPhvQwZnNg7OK -tny2wL8y+iirz0I901D46L53wqTUSb97Aku1pKM3oq0H0QtrQRRezLa9iIa/WuxkIf1/lu8Sg6FR -G13cpiONtCEPCo8ic+Arw5WuV16zGoQfreo4r14c3zi2Or6eS320Fc2rupeRbkSvq1krxDEnbfec -calPxojfZTMgc+DngXillaVrtcrpyuHPlnzMCOUnVTs+zZmsEEwATxQzJvO14Ks9K5N7Wul5StiK -emFrL4FCQwJA2nKyX3n9mDL9zaK7E4iaw1BhP9wJRM1hqAL1uwOImsNQQfW8E4iax6VJhX8SENUA -+X6U98b5Nj+IjbQA7cqjvTfeZeCvjLgwZ/sx3xv1nXHPAcCEC1CcRWns90a/jAsgnp7iDOzNgYMD -G29dyn7svSi6Ukv1EvMRNMZjWL17eIu0WqJnpo681E2Gt1BYtY6sjm2J+yhnP3IRF8Ne0DiDTjfY -SM+GWokoDB3vthB1cRWJrMZhI2abH/tJKg+dXKRFZ0Ah2f06bDgwe4t60FqTcYnrTFdZrJ6nKyxW -yHNrS2JN+c7WleVjSx5enq6qZE1BX9M+Bd8fThc37SPuW96ztmNlr2oNqqmfsz3K64t3p+7NWirQ -xV25wCPpToxRIKk4JvuPV4fKPB4Z23Nk6JfGBEdExsPODlfzNDEy9UTZ1j31LD4przmZnQmveSte -65TfPNZmiCxV6yZVKELZc8afKQeyP4jXJz/l97huHNuNIV4WqyR7K+KzNd51VGlEYcTPwsqtjF48 -ipRmxbXOHIpP/elbj3ruUz8lEZxOgN2JV73AevsQ3tut5m+2FUWtTEd1cZs16v4Wj3kEu8ies82P -AtV1y0fhrqgCCK2bjL8WY8nAP5s4yyMjLTA++Zg8aVTimGBywoYRORuZLWuCZRL3ip3oCMxhEJOY -gHwMmuct2IsCGOJZLicADcVwNHIFRcHgxRQZ02eIipGSpD3XwV2X4ScJ7VB19KctojioDwz18FCT -QXPHDXMy4M9wyT5D+DzfFfPsc82/1x98zrhSO8AP+FvsaH0Ecdhih0v550++GD/VpFglGnyY6TB4 -2E4NR0NFs68ECw2SPgimaTiChG7dYz3H1WRdyTowNo8aU/yDwHWkfxGuwxNEl4G/7evRr617L3U3 -+CeU0T/hC6LviPAfEngUAqOSD5UFKvnY1cN2cqWIpBiEwZ4TFVuyjHwpGIU4nyxbV8EcwbLCcmjz -onlgSClpcHWGc7CyzS+FnqOqzTm7CjLXfF0FlDOYnAp/SZEfzcjtqLDnYCVQtezyKslACg/vHDh8 -sOLIpF5LFrkWLY4Fx800lyKmdrRnBJ1qBqoyOUnRjQhC5jFVENJKGWeqSg6mSDLgCE0SViK54K1C -u7ig7UzjwEVqG1OuF6IGW6m0dCD1EVXqqDzWVMgUVUehUSUlGlVoNg3YJFODjBTnlw== - - - L0CbU54lyct0DEueX+msZbmvv/Y5CixwGyQKoV8MOmZvziqg5O3X2b7OAmI+kaI3k+I3u69J8scm -I8FOvzrhVlDKLqXt0i+mvq42X3e8NupuLUpuzKzzai0+JwOYcHmfCarN8GxVYsFwkEjQZEFuCjCH -awSvz9uHv+4r3jYwNJMxQsocoWZlCTy70W4f7x6tIHhvqGtFXCeMEUSvvE558UbNXBstEt5LecLW -IuLBZQJexliAkTPaTlL2UEsejpKSy2UPuywvkHLdpJxFLFShhSlmyfjVDEHN5E0LUcR83FIRCgWy -cc4s5yyPlhXbmVlkgI/ngkip2g6jcqfanqvsUkzjGUBSaswQPKpUEiI14G6HyCxgQ9HrHe4ZQQCk -zC1nw+qnBWzy8jW7BWwIi+LzHRRXMl5FOOwjSU5SpjzfXWlOQ4GNxWUyZDtLIIhjnmkrObZubyU7 -67y/r4TI3e8s2VdbQ25gpiqysEQtrdTyGdHOwTfU7Oic1j7BEsisuPuvxap2w78CrTwImVUlRHOo -F6uhMSjb28gGIduBwxEMHM/N9QwXu1vd7svqdp+o25X7r9b//FzlVGaZEV3+dYksbRwO47ENw+7f -2gfu7Xm5431uZtihGOj4UyS14qg5d2wVDnL1ppV9acreXgTDFNQTf/x6TvTBHbvRs3dSfnljRCdF -kmg6I1hmUKjMrjpUO+VOY4s5wYGqZfqlP3WiGOT0BiDxfeL0ZYdNrTE+NaIlFk610VjVduA0IJuv -EnvK/eId4z3jXeN9450ng6xEOA88AdyvSA6asJGtdk+mZU6JmbfUzK0liFv6tyZ6gzUS07o1gXta -p63ErJXgs1JTHWVINUa2P2X3nDjpiPZiNxzHvgs+Jemu6xRJEKvDVXlwf9ZQP5Q39PAF3WOzo8OV -HX3e7OjcP+6844QGeRKhkpl+i0DW0u+zfZ8L35kkaxJjZxTqAkH7WEX5+L2X74P73kvO6qDfH20k -lbn3vZQgSQFXbEg+dDLibUJ4VSY9ISyElxuPlxpbGJ4D4cE+fmK+rMHmIlFCb/C5SIzgyRASEgQl -P4i0BwznJL7IxcB1LFk8wG4wCp5OqIQFc2qyZ0s0USSV2CGT2JBICIXEbfqIDXXERjqRS5gVHs66 -VJLcSbMMJ1bSttLicX8oLm5R7yp0rJJ6J35UVKnEqXkcVfGKTt/c/3/fde6WalNZqk0o1ZJsyNEb -ac5EiwYaE4GicZZiFY0K1BtlL1/smGVo05QjTNEgK5pjFDd8sEG2Z445nBQssWt8+lk0kJ85YtmC -IS3VlNRnTysMMkUlVWxMMIHaE+1N7FGe15XkdjlZ3TujM/bUodTMtce9XoQEVYlQBydDBRcGIxIx -Yc79I5Fypk8XGLSRqteGrBM6RaM/VNpFBVTH82gUo3ky0nc9V3pD3qWUiIrAE+yb4d8u3nkJz8/u -S1/PL7ovB0PG+f73dq5sKRNjvb5qMyJ8qjwXnEthL5S/8Sxwrj2akYfzCpZZBS/wjNeqE2Q0Ktqz -CFQbM8CU9i/2sPMJ1EZCk1KqhA3MJYXyXKzPGwgVnJPa922NBA974THQUdBx0JGI1RJAlMho6Hgk -I+IpdJIoifYjwpIUmKT98Snh1ivyJtXWN00M36aGK1jpRDoCgXusr7G3BOCzPg90Bk/W89j3FU97 -6X/NOWs0BjwKPWcg0zjASMCIzGS3nGw8CnUkds1j7xNeM1U6V6bZRN6ax7kyvZMnAtamhykqGFRK -bsFBVgY75TBQivllCvC2mF5f6J83F1KDwXpJaXSlnJhrRsMe/DUWbUt7nJXK8+DXJ/VrdaXVGlek -zhVUcyRH9/eLAFcEyvaA06vssj+BqE7RhMp8F4+zekp2j3gqQPI8C1OQlVgPzBr+0KjDXf26klQF -/Sr36nF9clEUOMcfEUf5SffLyftdmHwJsr1J4E7oQhoh0VNtKtWloraRalFB8AypBqU6BZzBlHWq -+lMJCo/nK56ueLLSmUrnKdroqjkNdHryycn+N9OeDEKr8M0k9R0BtI4gJJBp5QHcnkLvJKQsGCu7 -MA+vIwvxjMNows1kzOHDXxBbIqiSIGSHGFuZBFWCysfKcTyKzjSEnxgIpDzRrKJqwsQ4gUa6JSTF -SCsClY6LkQti5AmjUKOQCZ4IU3FeLm4cIltpLPKupBqRFiIQLFaTkFYjaVOiDaZ5i2lHkXRDSSMi -9YZSR5yFlfpkiTuTEXH4tCNfWaAyUg4t8MbpPJzepdQc0YwLxg6/kkHETPDGAc+GxLGqEAv4w4FE -d8CQZMI/C0j0cVFVKkkrZnxCXuIjh5uIfBqNL8QLk0ghgcx9BH4Tfc/i7mnMfRtvPxFVz+JIT/I4 -e2MEMrVwRK+RRkbS9CZHJQMGfVz7bhye9fXoK463Glzx5mce1/wV8xPkSrxlFE+qnof+NNSU5tbq -6HAq6ygsA3j+4el3tlo6PZ16eOal1XQaSV890z41iksK0ijvgLI8DHKdVq9FkXeOuCufgHI1+NPz -LKmxKAcGiZvzytdVf7IEWc9ErfS1sNItODAaDRPTiA6iWTN9KOvUvVAvnRKcnloGiV1AvPpKFro6 -G0hReni6X4SIiZM9Zjp5+ESP2fPq3k9Dk6OlG5yyQAraEGr9xWLiGkypJJGjNfthsGoGrhRzrGEg -FQyCJJW6WgmPZh0oWSJmTwrxypPrXDjSNq1uwLaZkun0ndL7j66ijFY7mC0p+GQIVkkYkWBDSuWb -EvkGYcRXnnzlz+/jcyUcInpWpfD9uE49j4ieW7XhoUgWbwi7UropPcXSlA9HOZXLCzlHU46TMgmW -5zipCycqPRE8XwmtJKeqjkqpZ3aV/USWbSqLnNFw1pQQUOJut7umY+rpu2a7oz673TPvQ9QNjG2e -7pm5+O2utH+YJ531oUnOdT3TrzrmE9f8YuVfYwqzJi+LY9ZSHB+YeLtrU2ReWtJ3d/20D7rrE+rO -SO2l3dozctcHpxn7u+7f9wF3TXABdxB6PhMvaBntA78IV2EBQaN5VpCxkfKF8QfWvf2NKVuHL9D6 -57/jIpIAoh/XcqOVdtr+3vhsNTmn0/qeyNeal4B8tiveUcaUkoPuLV7KF7lvrtK46z1XuTfqWoaS -DBsG2pOkKJSzgX0QJQ2gTFneqwSH4KF98ITdGyU2+jQPeiRHkC9CZU4PopmKro8uy0OPAaSzBBgu -EkjSAINyz4Le47KHY26rMtCmGcTerd4Lu2fMWVfBJ6KPDuQhE38kAkX8qQhkR4cXv2mIznLViX22 -dSIxDdiJowlnJqsU4OsFSPa6xU6v880OSVDi7FgqmixnOPLVr8LfF7OFfYawqnwXSaSJVRkir288 -TqQGQ5FvVp1ImqB0Ur7ZjAtAHUmO93/Xm72XIuCNgZgeYEEFuOK5GFhIGWdXU+G9Ah8rJgjjLBo9 -wlnBZk/GOEuJYZ5x9rxlnKWkroYcmJFxVhU8D2zfz1X2TpgU3p5iKVJ3jIe5p0D3MtdsdHERT2qW -sZw6Z7x7JkFZJDgLg76LIbAlFAyZ46qsaofMfSWKsrDNpskB+/nee2yzS8I22++O4T5fb1UaQzP7 -n5FlFkcMxvFhrr4HJ1BQCshfiGW2wCbrOWV98ySrjbWUZ1ZZZpVndnQ8s8ow+5ACgI4rgfZOlIY5 -x6xnSBnspFnMieFZK1jFnslAPWmSZolndozcEnsvLS4am75KXLT2M2FwOmOfVSZadj0Pwj+rDunI -Pzsb66w2ZZ0FqZ+4Q9jN3UpJXS3qOCZFHZcdztmW55MCy73IUeWcXaS6CqfbVjfHx5dZTceq9Al7 -P7li/jet+1djY9xYdZTGlcXUnzphC05mYfNOKxzBzCCiPCK9fB8sNECzRLrAIIgv/12Zy+D78zHO -5pyzyji7Vw2lyblQdzlnLUeIyk75/DvOD6pE0HnO2cnE3VVKclCNJ+fBL0Y4jJJwudJuc9FuGGjh -io+tjlL5vKxtHRoYcZ+bNezmZsXMrHS0E35fKfR1bawpA8vyznKG2L1WXWuRRxbGp8QpmzPLpuyy -p51ZoTmQCjVbHuBHz4GyzdquyXmAXU2gZ2af9V7gFJwf+WEj92ysiDkYM+wokPwlY6BducZgJKt2 -+d4522xvHvLe8cwaw6yhpeS6nNnMqUU/BPus54ZV7lmfCuPZZwflghXq41qWofLPEmWxXfcW62yJ -czZhnLX7hHOdL43dVCK9sjLQ4nX0ST0DbTYmUkDpLMvR64+59ljSHcv1CVJN+1EMtFZe06G6QWsk -XdFpiqme+GgGWrI5OgsFPzy9dstAyyM6yJgyaIXDdjqaLYpTGclVgm2D458NIjBBU+MwHo1jL0eS -jiN7CHgk9Riq0O4toON7HU1jHTqRHrkkgfZAWuc21L5hofWVRKhu34aLyo1wKeBeCrrbeFPFAh7x -kI252nZbd7257AsstKeEhfYk64UD3LOEo/D7IJasQj14PQkbLYW/WX9vjYc2AkCCcPNehI2WdEkH -BDkbI+2sdgvV52EtdHRMtL3VHGQcv2OjlTyEVcAhykY7Ca4fntHGuJVqDTUdBMpIu8X2T46NtpVV -3kY2Wg45CtL/bEh5zdKaLe8qMtJ2goancss3mGgnYaLlIA08LWUhcDnbk0FXBmPKldwDWCHVhotW -1wM/ha6H2nHRJoGclI2Wsi1ivkGZjTY8ho32xwbbdL6+xweMrzDSJoyLz8JIm9TtfBZGWlBJ0zpp -d6cwgKxPvdJ3MtIWx7kUmi+NtI011r519Wm2410a8VJ4zsYcjJ046qVxL418KX3Exp4MsjzJwo9/ -aQZKc2BASDKz8rQKPw9XeWlvstKmjL5nVwtYPdEa4kx4aaXeT8pLq2s4Hc9ZajRv6wAnzLQEF9AK -zMZMW0xkKaWynJN4DI21xGS0svIkI66rfrVUEE0DSWsCzlt2WjHpNPEjRmRGS/k4ZdEYnQmdC89M -G6vxRV7aZrPH/SrbQmvPRBnmY1Cxv5GDNewlKG32duf3NpWp9by0MUHJUpRizp5ISh9bWrKaxn6/ -xr3qYciLUorB+NSOm5ZH6pKNka6VnLdXR0d3XmQp9ty0PPM6JikzrTc79Tw5qffOThV/rujJknLS -yhkD4xM5aTunB/XmbZ2Exz+r5bXxs0Zm2pSTdjLI0VOZaUMWEcpjQhYVEoIyBxS7EReKZv0+M21S -kVCYaRNYnMV4PLRm64Pfgm0LcFvvh7eqgnmcJ60puBOhyFlpnb1Vgt86O+AnyUxbSl54UGSmHJuR -ePBOeayrY7LLTHt7dZSYaRUi0wnawxA1xiVr+I8gjBtXqWmf43rKTctXiMSfUhTZX6xX/IeWR97h -p73zUmkCfywVHWluHaGsgFVG7t5VitrnuWDOUNtj7n/K3WSksvkPNxlqn3wxZahliNAOpazWuX4o -Qe3dF7u7MPWWoPbua90LDprKzFHw9qE+dA4eJERQs/BQXqh8YWPZkYuV0A0RzsCHVQ== - - - gkC2LEiEjLjszojiRthNfVU5isWeE+p+YqitLYtIVaSFTRdTlJjEfygaLU2SFz4TBx2z0F6EkdUb -jKpQkursFOdorvgy2lJEm9haOVe6NfWSFeaYIX3Ocrk4N5oh0gg3HmNWNJUjWyTksbq8aOVJCZIn -3xLXa2+50S47mnKjLT/65YuLULpwljRlAd712ueL/XzFH98Vk5wI5apF5RaXOC6sQHEeLw+8NOCM -DgrrSxhfwvbEvuzV1+AkQnR4NDtOpVwigHFuILHgHBtTkuHBWYWdsDCfHA9zbUzMysV8Rl5WMa4b -ySQcBN5IUkB4qFvL2mQDmvMIV+FQaMiAakkCjBJPxI8wY8JKEUxlStDdH/c/fowJDirib66Fd4n3 -PmVzwo5XPoTZ7XvhP3K7Psiu16QK3vVCkkm7HhvvePSkKMsbvlbb+8+2ru57fb7iX+6KFkJRzvkY -7GhKFajF7Nbfxs82YpIH2sdBXMXcJFwiprsW8dMSfppDGwMnC8mFSSBXHDTh/KVBQietBaB8Vm2l -hpiVZVdzNIanNLuWoHPiqhgEWtTLALQSuGoFhtQYOFEgilz6TzJwz7HIChjuWlT7JIV7FMY42//n -5F+TBD5i7i5Db7hxSW+G8MTfxqLfvgS4h2IN8pN/fzTYj/5G/z+6e6c/ad5auUnv4IoL5/HdaJe8 -0WgWGsxM3YSr7farlXxpelHRxqe92vLbd1xx5/XDXPE/iWlkG0wr0S1GZxuTLXos55ZkMSdajG3c -NIpfSBGpSaDc8Sel59prk/1+Sj6pzr/HtPyu/l9z8ozbvypdT4pk7Lw/UwWFvb9Jm4KXrkOwiBtf -qxY8tu0tAsInaW3s0uva7/TF2qk0uGIB/fvA1pUaWcLP2n6YKxo86UTgIYUOKXAIwUgIQmKIU1q0 -erCc9NoYWdil2S8dhVg8VMg7M0sg85tuXQoDDcaWkLsxU+duKaVBXZnmzBTOfl+Gzbu8fXqDciAM -CftHY8CPmgsQCyGjgj0y3g/H+aEgGS27p8wfCuRX/g+EPiv7B2soHDZRPYWZQJgLhNlA+kRT8eec -nI50ftaqqwi2IZ678USOQOyTa/6MF32FSttGfSW2MWtDofWlBlfs9xRMAzXfbk4lIPaOp7Wd1w1g -+BNeP8wVbcdfiJWCd70UrCcoojJRaKl6BjQKE4UA2hg26fc86I4beGDc8x4cWE4rub7jtYrCTr77 -lSz1xWepS4Z65D15yG6PTD/JXuegD+z46zu9S3b6dp+vxvMjHD8EB0l3edzhQ2GHp/s729u4pzFk -6HZ2aV9v9nS2m7NdLHZCaQc/ZK8Wdpnsz2uv+kHNlEUEhz211ZdSQxDc87Yf5ooWctzWYLmJ888y -K3xuBfrBY4ZFXuWIsf95paNaFNZYkYWZZDj7goGuWpslrXykPEuclxFrIHESrFZradyiJSwgp6q5 -ykgXY2SKlVy0movy1cQ2W7OHkLyP+Bpc65PWZa1NWqONwFWx/lLa6kIrmDBWsYkaFaVN23q1nW81 -Kq59Hk5PaEu5UYravW32TSpTPWP7a73iho/q1ov5HJHNEbkckclRuaswrYCTCkRHQA80wZ04pUD5 -qhCD6/WEXvQEtQ5Q9jDcgSWNQt8lhYDggKonxBQClgCsI6hVkMIaIhdO5dIHQCuAcRkE8B5J4bcQ -DwV4nN3ZrwAPgXcowAOGlOEthZLLSV2Ua68zxdSQMxMZM8OlzvQw5gRTLYzrvkTbi0cXLa/Wjews -44qSuzLNS/UukLycRsBMXQIUgbEEOahpGJqEEVMwULfKKfXPpkvh2Ck4BuGmOno8cjmZvk+9iICY -EgefQIDg7N5NvTDQLJPWo45zJqD4LBDxzrSFKtQEW2fQeqR6L34Ry2/pN5qL5L9WI72n/CTLfqr8 -F7F92b+S3ysdvdLT730pjb1P9Ox2vtrk09F6KVk0Xu/avb9lcXVZeQIGz09WZTM29bx1jrS+y+D0 -7LuLJPbNzVZQ0NJmBPjX2/N5LJ/wunZFl2ocM/nKuXwlkH4Zpt+2HaWq51D9MrtbCaxfgOuDBbXs -QPYfzu+0x2lVhu6XsRgF8L6CWEHTTQH8DsL/zAx6BtQXTtc7OPTyhAiE3z2AK+wRCRGOh/gawuUR -CRGYJLhX1cERjTAhy0mrRjtaFgXDKzULV48ebJUoLF6B8bxeGLdTGUBeIfIKkmckz2hJCFxd+mRg -eYXLC2AedA8lx4lrS4HzE2U2L4L4OWstCIPQK4heUxaoCjUBqBULxJWolwfXVyhWWJAc8lhjQZMc -qm0qaEZwEeksWgeq9nDqZQumthQTZtNUsorRagvENVxl6RKDS5RIgeu9IjEiPUWygh0pj1n90QcX -6SaU4iBGKiO1xCxA9JMxauJuPpNGsWps1lFJtEYh0VGkl3f5JCl15yQthwkkGLHW2Y7nPe+k66YP -GQGE/CsU+hjc+ayJbvZ/85GoFyWe+pEMosl+q8QPnfvJaCAEvt8l3xFw7ksZZmQOMVF93rgSesEy -qxshJwsg54FzG+A26x1NQIpuTp2EpdDAloohCw1sCBhyggWfzh8T+a8WtcVREJrwrTsldaUIotuc -KGfBdS+bEcCSOluqhLMvJ3CNCsHRHVyKZAdbkgMUKSnVRE5uUCI2uEotITnjhbVSyOgZxltcQdGZ -W6YDd8h8EFHXsfnXcPlFKnAKk9wqsbxb06tQn2smQeHzIjirlyEpnB2gWbyDy99FM6fSbPTEMNIn -ymtySaa0kZriFz+bZkortWlDtVM9wel+JS5W630FKlfCI1NZZ4OARB65OhG6qlbFnCAVvMTdIzmR -lhmUcPgEy4VWlibHzmRtcAccfFGW9ewyh5ijSXma4sFXWRaRZhKFzhNP/OC1tB5ZSeu0Epr4yZW0 -HlghS584PnNeIStNsZuJYSZNKaytF20xofDs0u60N9qfgeqGbJIJ81RCqxa1qZHlEnGe9XXzilfJ -gEpNDpsntm2KToW1wBvPMdoI/eohJkRMWPOWswvoh3Co+yOlRdQBPlQ3PrXjeS6ISQtcvpxIYvWi -MZVFSWIlwYYSHprmCBpmTM158gXuSsjp62PXjN2hC8fQDuH+hJz8gncnmQxYgb0dnifLRC8maUJP -m364ztQfq35o719K7lqPTH35b3968+n1fzksb1/9/n9izkvy74fWSb9WCC4pkfakQnBjs18Ibkwc -DrWAdZl8f3DJM6mzIedeuELUbfnr3pWzxwXgc9erJHe9l2OQqLudSV40yJ053lIoMTfJo1E+SyBy -MRNdzXQ11cVcdwZ7zU6iO4rgXZnh3SJ4JQdTycW0KYQH0jtn3bjuYNpzMZmTyZW4SnkMtvTws7lt -cibdZP5Bo9c1cDb3TO2cen4N3HLK0PzDnDY2/3EF5Gtg9vN+VwHAWNbw4QUAbzsNd9yGmzl9iNMw -dc3W6cwSmC3yJec7W/d2iaPilO1wmWMq1dCaS07dtn6eH+mIo52vsx7nfZXMk8okQC2QgWb+z4Ik -P+8Vy5Dmxq3MjDeA1ydVCmbWgEtB5hiTtMgaXpWRK2A1/m4tKIp2VUy95DQr5j29dMxIoSzSq2OR -nijN0nFII7s2pVF140C+l4VYOZAdHhMoF9DKK+Lg6KngGhdbayQ1asHD+c5Cgn/5wo/CkwyqRJV4 -bh7NlZzD2vZ9VQUPyC5fsveDwM6/iy858YWQH4Ks70cVRwvN3XP62OKQT5zRBfbpPdzXJb4JmNOH -zeeDi9whg9MzzKYLsTtOuJtzGbIyd38Lc3oDfPoMs4rcUs87qzCnj9ihQZj8MncA2Dg56UZ8i03y -Ki2SY2/cYWodfgNvv3qbW1z6NtXG+Wx4fTa8Phtenw2vz4bXZ8PriVf8bHh9Nrw+G14/GSX9s+H1 -05vTv1XD67pZddssu2J4wTtf/+OH919+fPP+05v3f/z5z71B5n/z8sU/fku/a/h3X7769On1x/dg -q/3y45tvDh/eH/D/aKQl/4ZnOLbdoP8bmmOo5H9oCf3d19/z89ZS7PRf/8z//n/g5/8B7/7HoT38 -+vBv/706fPMS3//X31D/k4u+c2/JlQ//gO+ld0vek4//w/Zq9NZ7fop/kkhjwFqbGGnUCGc9YvCR -I42DhhoxMIux0ZEqcx7+9RVf5Dd8Ea1o2hvlo3AaHont0AZg5o/3h/94aY/W9Me+qvqkp20NN6ql -V3V3hF3QpW+G/ghqbH9o6mMz5G/S/+te3oSHqDHK7d/M7k3v/Q6/0U2q+kA3beSp6M2QDCL9ZeiS -9+gO8Dd0x7bO3pRn47/Gp27S9/I7Z88U0oGS95IxaYcjgpPSN7vh2NVTlw6UvpmMCcbYp/St7M7x -kWilNdkwFZZfP8AaGtJhspv7YUqeSIekD7CyxvS9/M7xmeQedMuu3985up6SN/UedMv8TXq4PltP -yZvZvbfrCecGLJ/9ydMFtV3l0EmcmnHM3/QPICsqeS+/92ZFJUMl7yWjoisqeVOnKhkqfTN5AF5R -22cKhYHSeU0GSlZUMia6pIqrPBmo4jPJmkrey+8dn0rXqK6Ed7tburj5dandkMj6Fn669cvM3Tk+ -k9un+kTF3VPcZzqj/vYF8aZv+SdK7xufJxNmt4eI/iBcW/dF6VaQRSU5+ruC5Lg9SMkzFZdYaS3K -e8kzlYRWspZ06SUDlYqY0rYvHoPF81Lew8U8ZaspuY1bTckzlfdyaYcVT5wrk5c8U0E6+PX08GGi -Ee+z9ZTKsZJsK0iikhT16+nhw5Q8k66nhypwyTOVBNbvRI9iZQxUNqZjRoJmfodonEVl65VoesCa -6kF0t3pXBytKnpKIqoofSTbDH/hBK3tQVAtRUxxFUyQlUx5+FBhgCHSu8HOWlJ+CJCoJrF0tKxQf -8p7RzAQBPeXYwIe7gvaSDeOullMcS8JLoqp+yEavJU5xQpLyM+VS5N1NNSMRgvtqdy6Z/lCSD+9u -ntXJ3a7qrtu7wYSIySGDgfBNtjTAoqHlg1T5sNZamrwWjGOCcxKbejo+ut6T8Snp9dX+oV48/eVJ -g9pCoz6YmVXBHvXgILLcI37I/BBPhrWkVVf753xRIZCHbONypzEkynd8MBi5bqLB5KereOOKECmo -3MWjpnQmVbdV6GcTH8kT7qulyfPtqtSh+JBPG8FM4nvBsVVRswHcVWXLo/h8O4YesMl2zNacyo/m -fcPLXfPZd07ysMWTuHhmXzV9tg/7zy9frC/ZhbS+/0YcSC+LPqWvPr36+PHPh6/+55/Ro+T+hRkA -dd2HQ2jgNjUeEzA4dYcpAnhjJJx6qlMpu/I7/6ZeXWcpu2n2tl7iH0rXpTe9g4nzECLqn85WykHg -eax0d3S8GEWu5Dd7t3uzZ+jEH+KDWsmLZgp1N8TdikJIa1/Qo8uD4pquajiM+wb7MsmTwlOFoQbx -27WgTlTT4S2LOFg3QzPC2/D7MHbyNoi+ccQMEFh0nb0LfwV3PGKKmn6uP8KzwM3a5g== - - - iETw8jZ0Zuwn6EAzHuGpR327lpu1Iyzlxj49YnYBPAL81QQbTy49HYeellx7HKZOH3jTvbdxwOC2 -DemEPWpF6nCBv2ga+Pu6645YRFQ7g1VCQB7W3QRScmpiz0PX4KfrYz3a01Th2MOagUdvoUf6WZjp -wO/BLrUnbI8TTDB0voNTwXUHPgfj33YgMCsbkuE4DE1Lb4+N9RJHik50eIpugOXxttjB2PehO04N -6G91BaMFNxJvBTx1BZeZ8OI6Vn17BA1uPEw43K1MwlBjjZ7hMMJg6O36CQauPQwTTE8lfR56mBUQ -4yP0AmaW3xxhTOBf+EmQ1tK1MVCqCF5xaCr95Airh56n6+09qhM0YJ5I2wd5yE1/YldR1A0T+isC -PB/I+XfyB30/TYdpgC1TyyTDVutRg60r9PvqOhxgb48wGlM4ju0kowiidmr7Bjs2DvLnMHw42iNs -0TDGBwtjC5YbbIxWFg30ux1gl8AHh7ofrat11beHET5YhTgmNaw5GHos4ipv5v1xKxouPOLqaHAz -2IqG5YYHZw0nVdvZaoROTWAQ4CLpWl0wMNz4Sdin3dDIDSf4ZKhg3VagT9T62VDxPNEOCJM8Bqgc -4whHOj5ch4/Onx1hJ+FGrmG0J7ks7vUK1Cx8rKoZ5LFCB3YJbCNc6c1U6XbIOxb7DHesaYPDMQ4C -uJE+4yLETUy31u0DJ2LbTrjVYE13jW7tmhcS7qJp0LUABxT8ooN9L5+CRduj0VJjF3XKa7g8rtoJ -7mcfDSiBRuwunMeDzhBIwqGlwW1gw+sUw/OBYYbTAAPW6fbfdMpLLViGNSwUONeraTKpNYCOAcIF -pBMsLRUAsBvgGABVsYdVaiILhhhXUzvBIu/t3cBaLwwNghp0QbRY5gmsGlgkvfYEJVXAhYsSCzQ3 -fbfGxC5Q50DY2EdbzH+DyYXnIu+9XBaGq4Np7NEKbfUCec9cp+Gp2gbVMViMYdJJDrheR8yxm0Kj -IrLG4k6kQDZ4JJmQHSY49ECmN3AMxNOsQaW5hY0d/x42IN4dJGwINvsB9WbQ0RrYdPGzMHt1A8dc -26Nm7/pcwSJGXTeMuotoeBq0BWHQ4sLOOxb7jOc+rFY6FLtOJ7rv8PjGJQyyutWxh9HtJpCkeBMQ -gyrO4NpV3dImCJ2MPZyDfLAEXPaTfhTO1AlPN9h/dELzfml4mnCgq6pVkVbLbsa9NVQq1KBbvTxY -q6fmgIXVUMg0eCUVB5ueuXMJdkw10rqnrag6b9PjiQ9X6aZ6NAlcodWAn62Cng7QPVD4oXsN2Suy -jrsR9iwehChr9GzqcYKpdzB7vQpsuBlscZB2IOBgnBo72yaYU/ps1+tBhodIh/IEhFU1xqORXB74 -BFOtSz7vmFvbKFZRF8KrBBAcGvjBQCa6WmAP1rpk4biS2WtQh5DugfTpp6qlOQVRPdq7YNbAuyCk -gqlgcDfa39i/JoymsIB4I8UCJVptixZ2CBhQLBrHyfZNJ7oWigcQzFHpAeMr0NtNb3tv0z/f95aV -BTqbmkm1T/iT0NF9QRo1ox5EqA1MEy1SGNneutkOdGI0uO2083C2jn1N3YTtbL2vdPxA8lShj48I -mnVDBxQIedehPtR8kSmENna/p3WDSlDTxqGiowCfIzRxWNP+eUEOYgfHBecUdcV3qrPR8T7JQhZB -g2Y4PDdqAlNrah/sZRDah6nGqop6TzxJcN5H1Jpbu0IHPYd5n2AEelMCVJ+Ez0a5gQ87wehPuJps -KYDMJk0J9cCxHuNHETV3mFCnq+yzWdd8r9FCgBGBCaIF907/YMAJh8u0tj1o1eGETx0H6qx/E5i5 -qI+58wtGKIxw0ED/2sYmFkeTxg3TpPs6Ph7yHrKuNbmuID8efnboomoNijhMNoxFBeIyDhspqzAW -3jDJuuaUFDwo4WRDiQyLVfwO8BS4wXtUq9qoQMGcdXgy9qZEwidpnkGMgV4hPUZhiNOMR9qkBzbq -C+3UokulnbS/IGZpkuGTtLRUHZlgaOHkoJVP77UVT3GPC7sd7YM0w5TJrqsh75Dr6siDAIKYFJt3 -8vGh566Odvw2SBcAhydaZsNYW69ocqH/odX1BINCcwu9ImtCB4VGCt4k41Gfq+9ATnf+SIcewKKp -cfiqUae7xX0w4HvNqOYtPDtNa4/mc9/Zm0mHYldBq2rGCW1L1PMqsSw6VI7IIgSp3KiiCPZyPYxo -nsIcmhKOtirqP2CpwxrQY6Vr+bBEC3cIuhvgbnAI9mSXjrWqCqhXVSiM8I/Q0aXnXdeEQNdtTYqg -toXCA58MbFZ9Bhi/DjVQeJd+eFvsm+v2xJu6Rr2rqeSQhj9A8DLZmtH8gR60HSr58FmYzsY6yKIS -n9P2T0eHV0NOozhP0FsZOFSFm/h4NZ14cINa5Rb0pO86vkB0OmCvUTLhuwMaHDZC+Fzw/8G0vE3P -0k538PywGnowhyyANoE5gnowbUXt3NTCZoX1SRqjPlqLGniDElstINTKkS8DREwPJq09A6kdsOYH -fQJUdfqJPhhVR9ykOJtwyaYf1AZv2IqAJ2qDGrAkS2AF4y5sbQCyDjnFsyY/BOyaEXW3d/Jp0gRR -16p1B+KUkxmAeq5bTuj1xO636NawCR+pTzRe+kEep+D8BbQ7EOuE91NBAc+Paj6q3pUqN33DRhu8 -6VTCGiu5DmTD2FbNuhP76Xx35p/vxelNiDGpIjsprUbi4UPphcQeeECAVJUV0dRs9uJxC0Oi9u/A -ngN8N54XqB+GnhWGaqzjuySsUBEbWh3ABs/9hlWOQY+hBj0gHdqZaO3qCdCxIUFqyGjCElRAEEb0 -BIOdTQ06S3DP4tFni2DTs2TIBGTnYXbseRzR46i+a3i00GI4E+3b6GlHBbETLVqGBpZ7VbGNEk1y -Ut4H1iThQAo2jCSWyd1ltg8MORwDNb07qGqE1lFf8yiSI0D723bkCQnRDGjQhqtHerOxuYFRbESV -HdB/87bYq+daTHWL1j/sDFj/yNiijhtQSxs8pOAhKtN/0KXaw7pB/6f5IdqKbSoUonZ0kbsET1F0 -y9p2wptN6Obp0JNmmlzb0yKCj46mTqCbEX5BAcxokqOXd8RDk7a1+gnRrYn6Acq/Ibh7JR17wloi -X2dfUyCrHaJXC+zMlrWaqD5hjysMoaBsNamIoxMG1JUaZ93iQFbwDceBVtBbHXSy3rvKuzxa9IfD -toJRmqqqjV2u+hFrWoNwsWeA4Rk6nIm6TQYSxW2HJmyctaxnz7aegpz/YOPCZP7/zL1Zj+1JUif4 -jlTfIR4pNHnb96XnCRJamtEdaNGiBRqNECQUw4hIRkwh1N9+/GebL8cjbt2IU62EbjKOXf+bb+bm -trtK2WOejq43uM50i8Y11TtdhZ0dRgQdEgJtPF/GusdDWHIVvoo0/r1Wo8jxYebLeApTZJGiiIaN -zIrocBAwzHoPJQ0qPAsPuj+ZbV+ebnBTYM+pfYSicBuDR0J8LeabiSXw3T99EOMyC9AF4D7ISfkI -xISxKyQ9ROPyiUzikZahB9tjR2vGAlprpkHAkxTItZH7PJlDPIaVICc+4ro4kRdynKxSl4Wk22OM -YTLDY2bPoqcGA0Ng64BaoqA84S6DPTNU1UpgsibZdczQmcbVinhyXF8uwJZ5lmQszkqPHfbkDCP8 -oBDoGAwdc4dnF2o2lYdi6NgVqA7AG00JGFonKVYYGZlRGRrEDOGhYTfp7ZjZB4ipi8EUtoM0epDV -gT0PYRhedDfubbBIMo/DwD79IeNaKT2TaXmKcg3e+0EvWIfuVIluYP4QnV1dtHu4YWpJZKKfJrmh -s0LoGC1Ro9LWJvE6QvyKyzqSuXyMYN63DzN7HnsKrJaDVcbczArgxA+YYHhR1jnWisyoZEteTpDo -GWD53pvFBY4zHOMhMY1BLB5G4mKwvzUjKtLjQXgww5JbVcCZHVNAPTcE8hAJoHT1GLsmY0chX9+4 -V6fz8pziR/jUkJMq7i2+nFufdm8SY+BFbckM140N1rhVYjDmDMcoOALutehXJwBkd1yCU/Mks1gj -TyyIpiwmpdBLJ+Pucq2OmZNJAK1Jv7d1GgvayL7r7aTxqkKTGyOZuv3jHOdCfcyl3XFwykuBQ60n -O45k5hia1HIahzQAv8G4X6bM1GAsDWRBaaZttMLRKONOd159XTheoZN0Ni11vbAnHI5Yk7h65o4G -qx7boVwKnIeH5JzdLImFhhIWDeaY0Fyf0Rnp8UW4yau0JhWTDP2qLMJNmeAVyQt/aFCJPNmaplI6 -GDgRBrzf5ndunePKM6imqYl1HG/wFfiRTLQbnAQZ2y8Up6DUBhMtbiWYMKLSKxYFOja0u6JCwzmj -OVfI3nAa43720PJZo4hyLcDqEtUQRf4OvoeN8akHAuJIM1Mn/JUOwj9Jcnla0cgCSIEHQbEOsmVt -KSW28DPUkW+F/P/KEkYDZv45M39RRYOICNBprn2Y15xywnUAdQn2mmK2tCJmfSxxq1NtlMCMIQ4g -nMTUIJigsBQh6W0c2BfNsoetBMy6EmwxrhJVrmC4axywYbYTWMQKsTacKuUGEP/hvQOUrt+5PGTq -gXlcRZqHmS1nPhUyfIxbaCB3yvIQM9FIGCw4n3oJwLRJ4RdD3unmllNvJdZ0KG/KxVJQWxnUcJP3 -q/gaIP8Gw4wAF5A6h4HogoJ6mDTASYqJXcAIQsHwFo9I6mx+A7ikbprSMcVl9tDf6ApE1IF5s1MR -pR7eqRYXVcVXbG/pqO+5TJ8CcbEoIZk1HdZNmLA9ewttjJBJyMAG37qLc4zsEILcGu2yS501HpLU -s3pb0Ihtd7A1TQcKVoscRQUWP7vRz0mu8x9nBOVSI5sFVDQvMGrTsVKRBZ6nsWvQL9VwMYQXlJSA -qafHaorMOACwna224yFSDD2lcM1YWf+Q+IjAq1n0voahIMP21MgEpdwFmgAie9IU0cfy1hfS8kw7 -POYyZxkiBznodfIqzYm9DBx41kn1j0JeVehbU4dCqPf4ob51HgIcdMgnTKvaCkGBfPqLegrpoFF0 -fLBAGy/iKuyGLk02Ss4EOBWa3npYEThAk1+UnHNCzxIUa+clHOeYVFMO3YF63eiGIjLnhRrDCoU0 -fLroGFgQJPVCZ1YXNLPGkOBnUCbVHOuUCYQT55VJYTOgOrufhwBP5zyLkcBEBsR7QBCwhoH6zGvk -0jmbD0iELXNyI6zEMLK8Cl66xKCqGxOsYuuCodtu0grtG6HNka0RDIT/CaaNNiOPxhqXgsk3DpPS -8ZONCXKuOX0aAiKGnECMIUwhyiEeAWTb1rWDTQ3nwasH5JzQ01QMB7cYLqmGqKeZTglZBwJDBeNQ -RaDphY8wjGz6U2OaAq8PXYWDIbe4BtPi4JDJWRjf0NIKJEu63U3mHTJ59IG1gJJNOYCiTwoGRSXO -05o4xI4vBmOuiDhicJzs8nGCBP4v30VOpNmR8Qf0GqP53xNTqq/gHCraBbiw0SHWOg== - - - TYXSJQ5THQxqavZkLoT/yWng2pACBZS8xcqMnljLxYHsyjoQxEOyNVlY4oxPS0w6LDeEuZpkkxti -K67JJZBnn9vTaQt6IAyHO2nhAplEL5QVVnHaCKst3nylq0COsnSSFSI8az7IKsKPbeRjVDWO4owG -EJoCM89TSxaSCn29Lc55fYBB2apDMovT8igEFWEoNV1JCGrcL9lcl0JP40ax+86oKS5BpEJN4CXl -oCV3ElHiiN6Thr7sa0c0BJU2zOjeYzJPo6CCeJKOUIMl5jpyEFfzS8R1It2KomqXeOvAmXcwvxuh -jFt9bG9JC3P1uClHNwjDsmhrBILAdYrwJ3PyI6aijx2veeHi5JuJ8aW5JdA6jMtryLdjkEuU9TGd -D9ANHD1DFWl1icQu7ARqYYnDHvcRhWHDZrZEYY9LfbCD2pYY7Ii7fIwQocpmkx7kksbRqH7ROWFf -QpTm4PJ2whD8BdcaxAo7tnBKwlY7gDP2GpYl6DW18Rj+5XE2i3j7sRh9ODtJy93jesmn8hjYOwRI -hPbukb3kEn8I7SWtDJF1W2gvpPVLbK9HiN9jcC/dpA/RvWC6XOZgC+/FBVsDLG57fO/DDJc1G93e -InxxdT6G+EK3cBxcuMX45nwL8kWk+BHmm9s1zreES6AvLpxLpK/H4XgM9fU13GJ9H+f3NC6TYU1B -LOPuBoJ3wYXTDwTFnyPCN0cQkQi8ZLsnCPoxK9O7KyiLWfrwBUERB3s6nUHu5g0ibRFqxOEO6mN5 -8oM/6GGSH7m1EJ6CyKrNI4RBFX+6hEBhgYJ2Np8QRaLAPLc7hbCqYkfYvEJjD2bczjRqk9ns0S+E -+NVHxxAGG3lVd8+Q4+CPwzV0zPHTXCn3m64OGx8smJuyToa00fmursNmB+vApq8T4YxlOvT1sWFg -+6vCjtP3qLGTBQYse1XZPaVm7Do7luGmtPe3tHaIuMS7DrV9cCwfDr0dRMLJx5vinhHOinjxVXOH -IkFhfbvqDro+Nfd+U91BhhCLNt2dVAjsw6a80+JIlvJCjOfEnifmOM4/2OUcJCFiTTZBByRCZuRd -0kGM0Snq5HSTdQZ5Q+PeZB1cNzT7TdjBKnBXq7QD7YrY4ibugKsg2+yQd455fYjlNBIOdonHSWDq -KvKA1RLV7DIPTAsPQo9XE8Ym9UDlgq/vEHscp/itcg/zhnQIPp4yMmLbJR+QDXkuNtHnmNezSCkh -EB1ZclmcW1zyZ4yygkPD3RLVf4uT7+SyqV69PYPkx1ZlNnCbUJNgr0zsAIhNj2+CFQn5AjiaxjGQ -UUvBXQnykWr+5LZijj2NR7hLyRg+BpaCDSETlyVoc2V2tk/tA9QEi3KnpEVY4XV1iJN2Stb0RqZD -wqIQO4oudSoJwSfnacVCUc0PfJnSpSiRRVc3SaIeOTY1mnh0xUGvKS66NnIXHYQuWD+SXnKwKaF2 -BNFqC+syknWaAri9Qbd5PYucCox7cNuAzsE2ONEk8Q+6WSwXE9epK2x8H5fITDThlDSgqnp+4Lej -aFV4c6JFZWaJ/0MMcdUZF/I3sJdCSaxIuBqwlmZ5OE6t6+OsF3UBFom0JLNHTrOvfWYfoCYSa6FB -DIYxllhTzwd5IvAf/Rk1IFIWB6UiW8ayiZQaCkVYaRCoEh6WOdrqJhoVQe2mI3YNGsPaNz1pxG/g -s4bco32NjkuDqYqUFj+XESoB+0tUAH+Y1qdFIHHkkG3FJCB1VcFpHs3noZ6q4FZhxzxViBCdDhyV -rVdD8eKo+rIYiMxP5bcrQ91Ubc32UC8VbPipWW/ipELEwpSDjqktS6XeG0qF0nxac1EhJ2W5jsRD -FTIcVHHOmsscuiWvdfFPrSLTdE9xhoiNjr1TblUup3NqtXVN39Q4fFOKF9cUQs/cIrVtU3s2u0GQ -a00Ht4HjqekclNkgv0svWmU1yNizsBPjNJVjRHdGg8hdf/KZLRdAGA1ljp98BhlnZvVXNgPL2AzJ -Pib0CSZDMTit7DyGrJYakiE8BlYOE7CVxcD84lXLVw4TVqpQBhMk9etgMGx32PhLWNM/lL9ovvPG -X0Dsdcmh3CY012SIc5TbDaJDhBCHpHDAAOSGlPUarjA0wo+Mu3EGfY2+a+IQmXE0JjQgQJzuXL/E -b5A/hqKAdGgNaTqwkMPKbkmNLcrBwFkPZfq9SNrEwCyioiGY3HHMwCD06eTa5vWs44ICAS1z6Gho -Kh0jtJCc4BCukjn0hvpFAQbj8A+u22xtOAkTkc/m666NcxKwNkOx17Z0VXPcFIoOG5ScXFiyUM2c -HzmChyJ3a5hLRkfDcx7wsrrV0xCqt/jIc2ofODnds90cBKCXM9LexomFb9TSv+GFg3Q8gCGqXQn5 -fzBCoWKCJU0jWhHHmBJ+LRDTs7BS00L5iO+pUiynqi7SxZ1YyxpBDJtE5CGpwayjLEaiEfUZ87NP -51lENAZV/BDMK2IjbJk8R/lCaaqq/yK6Cae5wsBkFN9FkSqc1c7AytkANS0eUCw9Zght0lwH3fOB -IZufSmxweID+UBTC4vl74oixinioOpfZw/9a66KGnTP6AOnAagcLAgWHWXLPWBRYOqF9mkM9UTxs -cYtLGacc64ucqaZcCOI6DKs5c1LBv0g3dH9C0zeTIYwfkEVg47IbC+avRF5qu/KR35YDhaVli3Cl -8KHRDQxBlsZzzuZZpEP5aYMRlLAkHEDQGnsG533UE4bQt8qFl2a6EgLMrBSDCr/gm5WLds0MSCx8 -5mzNZrUKoKwhGz3nxdQHoxi4OlIYTTBBNB6ifvPqRswSGIO70Kmp7ZzQBygHGcjIqEl+oRzEllKZ -v7ZQDrkBKLFsko6H5WqQLCLrjHSQ4QTlDkF8RjqIAe6ZrFmTdNQ9hTBdIx3k61TyHhrpQHCB1U0j -1Bjo2WgR20I653SeRTqoYYGsv+QW0oGMCVNcX0hHXbij4SQdBC8jBhDxtLNyhJhZkHPrlpYUtw6g -kY7IsjGu2QNSRAlhg9Ozl9hWjeBDoxysJ9zHUPuNcs75fIByvARRw21EwVSvMn/KX+gSVCICOCUl -d5gCTapvvGoU1W+GPa6SA97pzYDXudQTCgN1IyekUQ6WC++6pbySDxOZ3pUFIJspzPYIVl1zL6BZ -974k4j7M52m049mfQN645uYZg2u1c/UQowgyRKFOUa4TSslEva0LhagyWCrLvIVBenD0AGaVEUYv -jvLvZx4F5ODa6ds+VwRFWnKi1H4LGqYFbbRxcz8epvMB2klBguJQ/a9WK/Sdh5ZAeaczTb3xBRHL -4sBHzDuYIczoTYNygAuRrsgq76oFJ3HjIpvWZH8IhAi0Q1anHggUi0ECQcyLlJ+QYe5oRN4vAbEe -CdtxzZ44J/Qs2kmQHhBg0dlPqeZREhjiIoLBwYpsHUQjmzaYsOyRfAUWMo7gfeTXI5FiidvlRYYU -6OeUkCuG2I6ZXkzxdZ2+7nYYYUANiVKjg90CWNAhFzAvsu04pvMRtpOk4gX+IGVVDBWR8xPJylJm -GKzYZWCywENgCobuTaqXW/KZ8G3jwjZlzZNAT4jWoMo2KS6uDVcKt67TtwGDDukSbhEHyZ/KykTg -nF0BF87M8ZsL7WGOz6InyvGsXKyuWLg9V6OTxWvZKl9gqD5waLyfDuUornQYl1vzc6WpoIKnCzkt -pjDegLjIgkDINui4JlRT7B0CGSn5J858yk5WE0BTsCA8GKo5b8cvJX0eZvgR9gTNAXUPYYdqGqGf -mlSTcRB/1WqdMu89ZW5Fy76sHG1PNXWsEAEoI4KOUFInGZdBgT1EHjr4VjV2ER6GSK9WoI7GKoQi -twN4LQs8az0bV5dw7Cx+IECHKh2ts31qTxOuveaqdYrdetXeKAEUaWlVvdOwSyCUw0EeiJOJULgH -QuPmpWwnDwUA0yJ1e1grAB2seEKZ0hxi7fJUZNh/78hrMRUZvsOQX5LSsrg4dWMIM9r4nNi6YLN+ -KOzJSUpfN3pc5sVRjkVge/Os/IusKrKtj4s0joOkUWKVb28kdrVoqWzjfLB1HaHc3sL4cSOVJOUE -ps84ysARs7MkPZDMji4hdJldtnPJM7Sd6R50TCsZqdsS00d8kjgEdANzE3gqM4GkkwHuacZjHVN8 -GvuCrO0yx/s4GIBs8SgZDhZCY+ZjMcjmhqCvuozM5hGsuBctM1gIFdwrc3pSVxeLMdrEOWvP0ZtI -covLMnOyDkliSwo/lT3hrVpWOUXe7ZwsT+9hep8ltthnIFjR+iboBZ6GIXy2av7DMVWIjRDcTfqF -d4PEvykWwAuLulx9TVSNIkgjAtbKqOHqRSW6TaSEY5ak+bbUGKS7eZAtBmTuQFyfMLVARDZb7jmf -p8lZkntBJcNibLZQyVOuLpjIUvER2gyA2QrU0d7zFJZ2lJTX61IwDhcbxK8OS5WKIwPYfCXJvVm4 -HuYPwQnl0aISKiWr5bZvCW5e+EewdWqQfJjQpymp0spXXl5Zn8qEBE+lKXvQ8EFIRSKkGAidBMUb -k2k7ELFASCUvHAwHGYRUVokKhwWEVKQWoS04CKnIEdWFpF0ojerbaUOiIwQwmEngmM2zyAiDwqqX -zoFdukpERXAkFBspq8QUyFLnivDoQTBlWU2MvnCCi46eiKiUvRwWERF8OPO8SA4LHKZWyRKnFURE -21HnIhMRlTYJ+JjNZ0kIPpfAVSNL7prl4qTKRAyrE7yJSINMzqQOEwyE8pL9Um4AuR2oXUR53Hm6 -vCoVxomIG1I/QRCfNCKgTXtBYUcUegdWqviubSsqIdOwgqUjeYnMi6gbooz/nNezqKlqoWxYfoIK -61USazGGaOvVKY+TErm1uBBicnkKfrljsLKcSQ/1JMz1IjoENBVlInAcwpBJqd9O6xNhEcgVBc1w -Li27a3hr5sp6uKewiXWpMb3P6tMCFeTIIW0gcRni5KsCSZ6CD8SZJISiCxCnkDg+tUHXWJpCucRZ -ZcdVFqZQ5WKG2qHUBXprdS1EgDh9nMRWuFamtSVZCvWuZ2EBuP8hgjRoNSatjLYkSaFyhaWIn1N7 -mhwF6R9fU1HE1uaaQYxCre5Ql2WAGAUHU5r5PU3m0JYgMywuhCiUEJk6tWvyNkHldDabLolQyHJK -Fp2ARQBttK0yPBYMEtSxPZ1FqMYmrn+5zmtbMAkDeUFdIH5PAguExduecCj88qQqzFL1OXYO05jB -II7LrIUZs0yRWYPTQ0JYaiUl9tPRPTCThcV/TYniC5D842T8NqU4s3Ed8uw0FCMdgMJD1xpDFEBB -7wi1xdWPtmK6D9VE0HNiy1LJEw6e/neaXShQA5dxUOZNSGhsba0DrXF2sfF/rUMuTQcFfW1LroY6 -a51xyalOJuslkDNJlC1lytfFNkMuP7SdgTKpKNaZS0pLhqACDCD3tCzvOrFPkwzd6VSRBo4cPVwU -RkB1Y6pV7UWALgUOUi13jZihuy9xloFeMQWkD06MjIQl0oyK0KPlvLgc+5nQcrKiIg== - - - biNCahVEKz2IyqMa30y0UmcHFFLnDbPP6n16wTCo0NRoTgmrr4oEkV5IaZsVD1Gag3ShzOUDFEpF -jtMSdQ9ZD9HzVOvHxXmjEbsC1OTfIgXDsAzZ1D/ICpTGAARWTgHp6obW6r/RknkZQm3zrt/n9XkO -09h6Sqe0qWiNisVUBgfcY+bqFH7JgdMqTXutkgQDq+Y0hMHY1Lh+TZi5lk0eqwhLEQ1kFnLsJ2rl -WfYOKhVSpENcs/w7F9el42xpLV6YPB1dF6dHYp/aN3gMbNdUMgYZ1yWbR4nDA9MSHgj7RpVHNwYz -mS5KyzrFQ9a2aFSLneLTnDFF1LkqHDszbpdkUHIDYCWcBV/CqUUV1oDBhEG8XmB4TXKlVZMqPq3l -uT771D5NNsiDIhNWKRTXaIVTKNgPgabRrEioFwKbR8mrFqAmcKTlZGU+qEhJ0YgFrmm1QMBBS1WP -CrmhFSqREZTmNRWJJLVFSlpiJKjyoqTzDAIJ1pbLyGaksc0KwPvM3qcajILDRwsLDVqF2YXG/c0s -1CgB75RUVNuyEk4WbdbDj5ItS+U27D0PlGemilNlJf8Y2ZKClfDFdP3B1RFlThjCovlNvNW2KM2S -Gt2ugoe5fZ7bwBaIOjPIJJjcJgq7qW7N0ojCbsAKLUYzCrcpfWU2UbgNMfNk1kvmNqVxTVgBCrfB -nRadn00RsF/6ErcZhdeA6fbV+ka8pm5pbue8vsFqyE82tq+Sq1VXQQpF1zVSkEyG4DXQhqbcEoXX -QCedhTVjsXcfZlEnMiOC1+BSautCUmFmRB3OUAHKchh6QOGCZ7YQhnaKdVFYDYYw64UdM/s8o5FC -Z+qp0mpFvXB21IyohU0D5h2EKNoJh+GnUIpWNdkTzlx6SSMuGV6gKzzFl7imJ8NQvx4lUXAxL3WK -yM0P1m9ZKyRqjpOd8uInphWONKBi8SXnfL7BXZpU0ShcDFktOlwcYzNdFQ7xJdHTci2KZZHN1C6q -vjRkEygRVqsvSgVSAIOfRllK6qaKcMsEKJgULS2dH7QgOO38RIniRefTEndM6NP0gZQ4MoZDJsn6 -uAe8JpThAY+i2tPJJUVlMwK9JPgv0nKsELf0Vu0o01NNkbyo3cpxZE9CNOzFzmqRB031cUt4SQ7i -SUxheQUEyZy1sKAzFORmbT1UeRKKqvmIznm9TygYRg4sIxR1TGR76mx9UQAx6rFzU4p40P742oMn -eAbIwfLs2Nla6+IcZJEZUHvUANlSIbJTe6l+joQRx5UC66znHSfeeYqwak690eYFOKf2eQ1Jau/j -2nQWI06h66j8newxBBg4HRTJsLgQCvRHaNl+CSIg1QAZCX3Rb6AZwCwWHX+hcj0XiXJLbcgiL1FR -SIib6tFQq3hIFhYKFtwRfBGWlueE3icVGOZIaZagFJ0+l3gfi23RqvTaUasvVCd8URqRI4EC6xaj -D/MwvKcwv1l4XRHJmWxyYWpLsToqxT6t0biYkFRPD1SpgIhbMDFOb8wXCwVHbgzryz/HhD4vpXip -XVjy+hqW61wwuyBOxTImXeO8aEihJjmRt7ZTaOS0XQNIolzxS24GGe/oKQw85jAf8EOKX6fHLdLy -nhZWKEp2pQkDqIpLD19JmUTDS/7kEtdnuo6ZfUNOwShwLZTERshXQ0KegjUMh0yTJFilJZQEJXRR -QAzyfJkviME5g0c6wprOD/PbEKhKWHNtYWdDH1t5K4yAOBvaTlHJew4AL2ulGbLfQeomlaIvZtBt -Zk8QbVEAndIdIZslXazIz4lRoYOp0oGqIyUg4TaeTuPAC0NJVXVKZV4LF8WlQhHUCSSkUP5+shwk -0pYCP/RRzByP1pI8StXXZ2M6hzS8+UIT9AzSlxBWPYdxzvBbUm6Sly8y5DV7lgdeAphfKA27rEtS -+RVwqqbbZ6fEFKgEVpoCu+NMA8p+z1P8DGy7I3CYInDg4g5UB2BaI6IINtS6ThUhadnLtPjMwVyl -zmMJszT8OcdPExLyCPEuR1srP8NIBHbbyn5tUKAt6rCGNq8N8k2jBppJnXBzIQ+2LVdo9fz0SUvL -RVYDe+xGw2pJdNWzQbnlL3av18AZDGNA8x6oUk+qLVblczrv0wz6R15Yq6xPvSoK5PA1PAtV5uRJ -6W1LpDb0x8IDpfhcu11wO7a0PIyJyxUX9QAWK92rDqe2XiR4mA6OEZCCWqEoY11Q6sWIRUJmSstL -oOw5nU8TR5CyemQdbZr0xy+JdTIhLqGAXSqYwUDbNTUWKWfkK8PbjMmqMjopujz44ixKhdxAz02T -adCI6HYUw4LyK8pQKfuTLbzOqbcPYb1OTZumQiN03osZtJkK/TCz9wkFebCUO+bTUokPSKjyLlD3 -OeOIWFA2pboJlXCZuBj2uoTToTqoyfhUQRzcENbc1ubi+Oyl1E9W1onI+E6mbgqLm6sz8ZY2l4f8 -/zSEZltxzmwjmt8xDTIJY0uFJvd6An0Y6wAjEgzOlHfW6T6XB5f590/0VRQ36mx0YIl7X/qVAccy -x5eJphx9laUvG5A0SieWdOmLq1Z3sVVF4tmvJyzlSGE/RFoABIppz/rv4ych641V5dnkAcXaj34j -MDKY0VeCJO/95LUfGQo36SeK/tgPl6xWuoVyrwWrFxiNEn50KuYO/14kZUP/XQdAzz6Rz51goR8o -VkCd3wis8ignkrr3U62fORRtsqM45jMnmnRV6eE0jdJfgER9SZJa6KGx0QqBnNRAfhM+inzFfsxG -D1i2vvQrAxL1GRqq7b70Jb/1Kx6QNmonlnbpiyv4Zl6KnhfNbAHSQ4JBPD9dnLWovEf/Lr95oxLv -izVKB5K096QfKZD+a0jy0VNee9LhzEY7knzp6Tdy2Plf6mI4NyBSuoZsW6TgLEq4oxE4hPw7/VZm -wwPVRvFAEveejK8JkCRGQ1KPnurakw5nNtqRpEtPzJ+cPO6YltkaMPJAEfoAptAjo2+SqKC/CR0i -DhCcsjQ6kWw96UcKpIEaknT0lPaeeDgCXOZASM45zdki1onqFzrW/V9XIOnrY6DV80kpUi4Ukpb8 -O/3+ScQqOnNLoxPJ1pN+pEAaqCKpbu9Jf+tHNBxrFA4k4dITcyqhcYQNRi0ovQATYn6SHJQiZbaS -nCb9zcxDjpw1SgeStPekHzFQUvoMST16qmtPOpzZaEeSLz39RtgErUOuy94aUI4candjMbPUvAhS -qF9/M++I3PfS6ESy9aQfKZC2xZDUo6e698TD0UbpQJIuPfETjbK4ad3bBUjbkoUtJNm2LLwjrXub -hcFYo3ggiXtP+hEDZVsMST16WvfWhjMb7UjSpSd9915tdnO2C5AGisA7ivKRUxUdr7j+1muf/3E2 -OpFsPelHDJTXSwxJOHoKe088nGA97kj8pSfe28wmxtAWSjZgZSKEuQSOmiCkQ4/A8r9P+kIqNIzm -S6MdSdp70o8USERoSNrRU1t70uFoo3wgyZee5FnszOw69WVzVyhtDOW6YD1TF2nHiYFAAT/xd0kY -x9LsAdHWn31H0CyxcoYou6M/Bdh3fIStWT4R5Ut/9iI4oUSg6DL1CbURU/xaDcecatiGEhnLbBZP -RHHvz75jaNQRK6J49hfPpaZhxWOFJqJ06Y+n7uVVaUosNsljAXsmUcomIcEVz33xG2xFXtRVCA/H -Fx3G0vAR2d6rfWpgttNNZOGh13D0KsOzhvUB2TnXRdqUCjZ55eMLkPatyAN6WQZFL73wv8/twJPV -OKZLox2J33vSjxgoLNiQ1KOnlY/bcLRROJCES08bZ6PnS+PO2AAjDU9ZCb3/Go3dyM+Nr80mB4q0 -9bOxNX4PtcSXiSTs/YTZjw2Fm+QTRX7s5zcqMNE/eBrg6w6DOS2SV5qzXD2FjCcdIP9UGYorDEuT -eKKIWz/8zQLL5Hs3JH7vx6/9yFC0yQOKh37siXt77VC3dAPShgBC9azI2RTpt9cWihDVs0lZs0bl -AU3ZerPPxGTmeFsmItT0Xnvj3z/pIHlIs9GJpj72JoxbLm+ihMm9JlgZjnNiTwnKlJwICwYRZqr3 -x9rwEdneq31qYGY4E1l46DUcvcrwrKF/QHbOdV0HZZyrQW+Dkh2DEn75WUO21gHA3HS16XHiKca9 -NHvEtPVoHxqUrBkLqnL2WPYeZWDarDxgKpce5fkuuRepWPEa/m1QHnSKag1q3EuK81H4OZikF+nS -7BHT1qN9aFAe9ETVzx773uNmptrGLpjSpUc5+1FIhPydOv0VyoNG5TjiLrhO0IsWWlGAHEgJ8Vib -nZjC3qN9aFAe9EQVzh7D2qMNTJvFB0zx0iMXupMyRdvsVyANuR1zb0E46Tr1dszc6toZlrD3pV8Z -kAbbjlnPvpZJt2PO64gZS7z0JfSeuRp8k5oWryeUAkU9l8IlZx1aUT4ENRDAT3pMiP+uzQ48ae/O -vlMoxYZMROnsL239yaisVT7x5Et3aiVj7aUvFtAFSGXykAskKhBdPq2bnjTtkq2bMqWNNiTL77h+ -pECqdGlI+tFTX3qy4QhwGS4hOee0bLOmbIe8qiULlLUJBGVi3YOyWnoClVssagLiDcUEK83qiaju -/dl3DC2iTRiicvZXtv50WEuzA1G79Cf1XsV2WFdb9wolOzVqb7BRTgzZVMiEWqzmbq9K6NrsAdHW -n32nUImUU0T57C8f/fGwtNkycAkdcJf+5HBror5z62W2QGXIsr6961hkG3rfxiK7NZvVB0x179E+ -NKhcQVXlQie3VFPpcfFw2MDWZiemdunRdPGyzuL1hEo1kWSzjzymZLNfdtBFm700iyeiuPdn381F -4j/inHwkCSXNyS8SUJqT12YnpnTpUbRxpxrqIsWvQBa/XVdllwV011UnXqR4Z5YXa/SIZu3NPmOg -id+GSAR0622V4m1I0sgGbWiOuS0XuAq260PvC6ygtHNzir/hIZGmcjT/ZPas0rY1eUCx9qPfCKxz -HuRE0vd++tqPDIWb+BOFf+yH1e6g/7D6tCZU7NrZK35xWGWvg1jdWtnrSGezB0R7f/qdQdkmaIjq -2V/d+5NhabNwIjrnt3nzGKUTZ9HrCQ3iz+Nn1534jIpY3g2gzrlqBjFx6R2I4tGffmfQJl69atbC -vb+89mfDms0OROf8NmOLMIDN2GIwNbawG9x5sXB4/ffF2MLOdKfGlgNF2vrRbwQ2jS2CJOz9bMYW -GYo2OVDkx37Y89PE3rRaURcgCRmo7U5GKzGOhiqWrdWESnbylNdGO5K696QfMVCsnoYkHj2txlMb -jjZqB5J26YkpWgLM4Ch2WoVrAVL2UJG8eWReoVHhl4Ht90/yEb3FZY3agaTtPfFHE0jvLxqSdPSU -9p54OLPRieSxJ409YBWGCchiDwxYoB5RxX3oNJmzIYPa5eT3T/KVvNYljdKJJe198VcGJJKNLxNN -PvrKW18yIGv0gOWxL7mLJT2irc6uFcpCMyKFSesxLur5TLXV30Xh1qQj+HmZHojc3g== - - - n33HUHFXTUTh7G9zQtiwlmYHIn/pj918esL9IoGuQNKFo/EJtlnEPE2JtpTR+I00SieWtPelXxmQ -BEZD493el18ETxvQbHRgyZe+7H0JCs+npy2Svdw7wQjf7PwH1xfEdYiGVIde2xDkJ9VVKgqFbg1P -ZO7o1T41cA7hZcEVHjoNW6c2OmvoT1znTFeS7/yKI9zKyWTvBUplFLwWo0WaDqebS+qgAoQEpdTi -bNZORG3vz75TKMdwG6J09pe2/nRYS7MDUb/09xvtNHB4ud/8YAZ26rqqlG4KgPmZqjZZHVKVM1C3 -ho+49k7tUwOb50qQ+Yde/dErj87atQdc50wXLq+WrNDWELMFShVnQ1B3jFoCvPps6hr75dWxM5sd -iPzRn35nUKpfPhG1s7/VpjOHpc3Cieic35x6ldhFBC3Z9iuwaTRRZw5Dhp/RCMX75d/nHtTGvMoa -tQNJ23vSjxTI0USCpLu9p77SmA5nabQj6ZeexMggPvu8KtkTyKpx4goeSWwUXh32m40EErRPs1E8 -kMS9J/2IgUUDQgVJOXoqmzVGhjMb7UjSpSdVPjgj1q8pbRNIVhDIRKCeJPdrEfed/jYFApS4NNqR -xL0n/UiBHDOlSPzRk1970uFoo3QgSZeemJLLxUi6AGmgtexG0povRtKadyNpzQeSfDFdLkAaqCHp -R0+r9cOGo43KgaS8aSQlm1KgRI81dmNCJeTCiTxkAQUwx4RuADbWSsCyAmElWtEsv+v6EQPF725I -6tnV6r2fI1qabXgeprZQtCiyZUvSnEAlRtJaycHAdObkQYaFzkSxsUbhQBL2nvQjBWqeHSOJR09x -Pzs8nNloRxIvPWlM+yZRv25AuOATpYzSs8dN3kxS0tHffEWQ7yyvjU4kj7L7AqQBGhJ/9OT3nng4 -AlzmwDFq7tKTnt/AzzFRXTQ7vwYk50gt/AYVVXB1dBarNsBvPYtcjW82OrDkvS/9yoBUFmGi8Udf -funLBiSNyomlXPrScGC2mWbEBwRN6zIoRRFQOFXInBdY0AopVdqAADwOeUFobbbj8Udv+plBU2r1 -ZeKpZ3d17c5Gpc3Cieic3cXNQY8LPrg5cAdsbg49OuZ22A6YeSfWZg+Itv52N4eGp09Ep1ulnG4V -HpY2Wwa+uzm2/sS5VWZE4pz6AuURR7kZsvaBmuwSDrkKFHLLrM0ORHnvz75TqKSZKKJ89rcLMDos -a1ZOROXSn0xdxJsYN7/ehIo7ToICosYQRBGb4hb+p7/WZg+Itv7sO4YmdccponT2l47+eFjWLJ2I -0qU/07M5orqtU1+gPGKU3RDxRMInHO+CAkxRVgHFmh2I3N6ffcdQrWhtiPrZ3xbUasOyZv5E5C/9 -/UZJTShDHgp4fQBTBUJAmGFQ0T9kgZu5QyE/KcUI8VnD9IAsHb3apwamqgULsv7Qa996teEtDU9k -51wXb4/+U2hLuMYC5EALteWQ7kgpuzwW+c2OGLUKaaN0Ykl7X/qVATnQwtC0o68lKGgOyBodWPKl -L955sSH3zbKgQLUFiCFadTkv1upN4fNi0l4a7Ujq3pN+pEA2ACgSf/S0GRJ0ONqoHUjapSfeXylT -BRnQouYNGCQJSd60K/JmG4qyyr/PWPYmD1IsjU4kW0/6kQI5k0mRxKOnuPfEw9FG7UDSLj2xc0di -HutqMFmAdPdQ6C8/rEdHLIvGWldrCd0kgwqtUT6Q5L0n/UiB9F9D0o6eVrXOhjMb7UjKpSeerYUg -1unKWmDsh1L3aKCMzmwRjzPp04L0rckDirUf/UZgTfxQhqTt/bS1HxkKN/EnCv/YD030r371B382 -/vvH/1v82z/7+R/+69/99rf/+G8/C+BP/vGf/vlnAf3nlz/8q59//rvXf/yHF4G8+F//6g8uwJdB -RUMUHP8P+P8d/8dxAenxv3/9P/j3/z7+/n8G9D9e0sv/8fJ//l/u5R9+Bfhf/yVR6Yu+4gpEX1n6 -XP9s/FfTPzDiv/0j6su//Cl38Rf0H7xkIE9L14Iymqh9haLVSCHvZRxsRC1FlMPCsqL+wEPlNaha -oopBBOQuyQNlf44GX2fbr7a8jkeDfB28j2Z12/Q3vZQa4bGjB5xro0KMEK75L9awHCd7C8zTQ+n2 -rf4MxgTxE/WF8I1+i9KO8PIl/Us9idSvwHRY+vEy7Ekxf/RX9KUN8fUXNcTvW9l36MaoI64l0Pug -IYcS6CVT6XqUQAfRWLkBIxXUGRT6QD3F+afSB7d9IBXcASup2G+ZEMJklwkNhWydEN7sXPZBXmzS -b/WnrJX81KXUb2Wlc9n2QfsVmA5LP16GfZLKHOLrL2qI37ey77EYqo45GAsXfvdWGB9F46jcO945 -RMEKakSFK6QyEDGRV/kzCTdzy18XdrNSC0zBmIP58fR3l5PnZUkCzaTpH7y4c11wZnlJ9UNdYD6x -/EtWX76TvWn6h/hluEN9K0zHE7RehY33JBMb2+svZGzftZbvcZKCG4fIoutNAyEr4VZqehn1hKKL -qGbi4tA55D2cZvQx/lSqQE7i/PPCZVYKwaPmK4XYb5kVigkus0pumxZ0+2UfSNGeG6E/ZcH0p6yn -fivLPTCvm6H96r+WfTOWYZ+EMof4+osa4vet7O+FnzRn9NImFxmg5U8lkuau9AKet9KL/ZZZEbec -s6phm9XKawdM+bR8O9k2N87bZui38wpYN0P71X9t+2Yswz7pZQ7x9Rc1xO9b2WeLKsxEVKqNwa6Z -5c8Lw7lJtcntUi0R/iJ74WCw7CV/bVKtwEQo1G/1p9tERj2v+q0cZ7zUHB9FRoHpsPTjZdgXqVaG -8/qLGuL3rezvhbUwE1HRVokktfXPC8O5ibZKL/Y77gKYzGWck3VWK9udcqN+Ozn4Kjfqehq/t9tg -3QztV2A6rPVieYNe5hBff1FD/L6V/f3ILsxKVLYd/08k2vnXhencZFt1k9nvustj0Yk8Jn+ssq2A -WEa0D/VELvKjHXWRBavIj/LHJj8KzMbjdvlxc3ypbCsDef2FjO271vL3JtsqfdDjDHbVLH9e2MxN -tlUKsd91l8BkMuN0rNName0UHPXbybdXwdG4etn2YmBeN0P71X8t+2Ysw77Itiul/GKG+H0r+/sh -mOaMYNpkI6jkO/9UKmnuSjAqcpnnve3TUhFMJjNEsHVaq81pSo767TRfrZKjGbfathsI3lt2Q/vV -f237bizDvgi3K8H8Yob4fSv7+7bf6kPY409nd87868J8bnJu28Vc4pmLMOaTCmPy1ybmCkz5s99k -yLaJkImrzduXHJMCvPzXJkEKzCREqVRvQ76IuDKU11/M8L5vRX8PRlulj5KUKsaNM/+68JqbXNt2 -sVZno8KXzAEhNMtsVuvTlBn1W7NjrSKjLKJ9yUsMvMsGWK8MM4mQv51Dvoi0K338Iob3fSv689Mu -HGWYr6uEJTCVoIxrpt26bL/VB+I363LbjMtp957szpPdd7LZw9VKmmUbVmu4FEr+uMYHg9UyfzNn -5U1isKGUff72O+8Sg5r93bYAKqwITEQZ/VZ/qo8nbotgSlBWQeeyDJ8ggxq2dTAzTd4uQh2N/tbB -2u+8X4Qy0xq2dVixzPtbv9WfYbu/zaDVtnWYw97W4SMcEvaFPlfArA99M16YBqu/y87d1c5lHr2g -3H21c5k0EtYLyWzwdbf91808Z9JrVzlnNc/pgfi0QAH1eVkOU677ppubzTDsy2G/+87MZMLwaYXH -60GNyH1bDv1ZNgas35g5wlo9LEfTs7WxOIEpCzNNIu1WBvsddxYntoW2GRnSZBPG4uzD3ZC22UVU -WY7C4laryOfPNhSXZQFMrYkbj7OxlH0B7HfceZzaf9y2AiuWyeP0W/3pNh5nCmDZlmEO+7mUXcO2 -HCa0x43Vrcx6XQ77HXdWJxOuYVuO9eKYrE6/ncx8ZXXG6tu2HHPYz2B1wlQ2VqfGXbdrPfY77axO -tR4z8zpldavWs2KZrM5MMnU3BdVNWTPp21qtytozWd26HCZ41Y3VrTx7XQ77XXdWJxPOZVuO9f6Y -rM4sDn1bDv3pdlY374fLcnxKFBRBdeOTXrMFNuFVf7adS5pOpR8n4ZKr7JompzUuaR9uutgqautX -PJy2CdrPEAPXuZvhiGHGAmUkZZu7/jR9IW5zB+r0yCAFpreQ3xhk2/ij6pNlW4E55OeJgOsamC2E -Ycb3eDD6s+1cUedhHyfliusarOrE5Ir6rQn6K1NUnaltazCHPNfgE8F+8RbsF3/9kr+E1On/UsI9 -vaNO/60FPX4o/G/F+ToBglYMfHtfO5A//XrimqGCqh3eX7/2WMVGT6RT9/+FSFnrpZz/gFISqV6+ -EExazvAR1eO/KK7HfxFk2uAR2eO/KOTxXwbTxAHAUU/bTZgABwQv6uBYZL0tE06DHYcvBc/fyn8k -D5HfkZJVWNb+EbLshkzrEaL4vj72N+0v29evC0RNN7I2x893+79aD/DEsTDJKs9RgYtE/A9gg7c6 -rGRkiDBOq7DDU3kVvpW9q9tcKIGm7CsVJBlng7UvoO7109tqPc7pMvFzcHNR98avl3VUgjp+vt/h -vq6BhYzBZnEdEaXZstY+Lqm6LDXeRcdbYCKSdH6+cKU+ejKy7KtFj762usOQOZ13SrquxGUCl2na -0V8/PcY3F3bnTq/fwCmLi4D8sUbnz3VSivcGe5uwWeilBEUm7NYGrfOLWl1lQTv+gQVHdcSMGaa6 -78BtKS7TuwzsNv5a+XHxbU57t6sZduOX7ndiQvrJ1x3DjVYhIpCgVCAtEHu0parCOvW/iVnoxifl -3K4MSo/3emCJDeS+sTxKTLN2srbbWC+zeZzyjTvvg1tWU8U/vLQoakNVOnDjvxlHsuq5TSI4CYPc -WZ9Oc+Mi2zyVH24TVYa4zUpx7UAe/b5Ml+W8Lbt8uy7AjeveuLOOev32nPO6ojGJ+B3lrTq8ks5X -rUrhRVdWL+PAd7D4KITBbYSuHG7ffGFx25Jcl/46YP18ndmNk145rnC+9VtlAVsnCtyGo19vAz/n -/chNV7Hryh0FtmHQj6/A9etncQLlXTq51zeX4bpg+vm6slcW+c5VsM1VP94X4BjkXGzmKJsg6ezn -jYO+yZW2j47VNVnLg9+QACD8x7HWOhe38COPdrI22Z3o1erJiuAMQso761OK21jkNsDLmC8zu/Jl -7UPO09fL+J7KIUxiWSdvysE6e2Ve2/SVy21zVRrcgUyD++JdFvk2Wf12XZQrX7/eADbw9fNz3uui -4oQmvcy6CjpepUrW8BcRiITSdCyoTur1zd50PNu4rsz5ys50JbfPTXRaOzKNbl1n+3zdEO1oJ10Z -0g7Uwa+fX+lmY7vbRXTnsQLcetPPr8D3ZLBP8wZlbjrP1zcX9Lr09vm6SXcu+t7dss1bP98X4xjn -yYTV4LEyt80IojAsv8LGKgW8cL3BfkdWtyF/nhIh/JGmm3derevy9a0pXgZ2Gf6Vow== - - - H93O1VWS39bXgOvC3Rb4NsoT420BP2FeEF66LeCVYV9Zu/LSbSF0xNsGXKfxDuFtHx+DnMutg9mW -24Dr0l734DqoE+eNj3zc7nAuzuvKPbcJXvnsnSPLId8+1xbbQl7ndl2F6yb+5ra8r28ifte6urOr -K5cX4LZl183dd+dzVknlnNuJuPPY926hb5PUdYPO3hc5hO4muorEBam0Fzv0CExxleTcy18Kd1Cf -DQt++jHRpKEEFyat5K//mL/KL//xqweF93XhfasS8Y5h4Ftmxos18h2j5dbpRRn/ex68t9nxH3TB -s1SGAyrLFXX/PZGILNtgonLjNPyXHL6jKW4kkMp07JA32OiK1+3QcbfD/U0t/2YOeM9s8E3z481O -edPCZdXMi1XMxQVKUUVULxkWfHW1vPxz40XWQzYXGa5CcEEmQ16mQ+1Xp/CDwfNqDrwZ8N4z9H3T -jnA1OFwME7pMOkfh+V+qaABYLGP5ejZXe9Z3HOE/Xg/CJiXflJ+bknTVHN9RMb9p/LoZya5alx5C -lbEb35ZEH3pxzpgCZdYSQmB0pUtTOpZ3P8JNmLtj5r6fvd24dVOqrurXVS18V4H8tm3sakW7qnTn -Ecx8bDTYROxEWf/YOf1c1qgkKav3skSqVBUOIzvo96O4r9rViHazM12NPe+ahb6tn9412ZvO+/fH -9I3vd1Hr8Kcy6qbBKnrT6eKpOa5wA6I0lghQtVb4fVKpwfPO7Cd0s2ndpNWbVHtTIt7TNr5pK7nZ -VK6yvK7bDG0Sa1EXPrQSXREB4TigpjDbTbgtupqfTsniVCM2d+a3rQdXO8O7FolvqyZXJeaq7vz9 -hwSxD/Cy06Lwup2gde7vav/fVgjuqsN7SsZuILpZPmiVPhEkUm5BIuXXLx7PAvZC/0VBVLywwgD+ -w6PfD4WKnJhfN6Bg1wP6DbCg+HrDe1FWkp1BYt0cUSX2kEIsXyRPR5ZHy4r9knunpz+8swELMI7j -4LS3Pj58A6Yff71hXBSPDxx1zpjyl0EqcBvQaPE4SgVuIzpxrsP82FIqxn4Z5FiUx/Fcgf0yxn4O -Md12Ld12Ld62Ld727cT5hH0rt30rt33Lt33Lt307cX5+38pl38pt3/Jt3/Jl38pb+1Zv+1Zv+1Zu -+1Zu+3bifMK+9du+9du+tdu+tdu+nTg/v2/9sm/9tm/ttm/tsm/9rX3DE8gXNumufPLKKK+c8sS6 -dCj/km9suVxY8A2Wb2w5P58t5xtbLjcOfAXmG1u+DfNzbDne+HK6seArMN4Yc3yTM+cbZy43JnwF -5htnfuLWldvWldvW5dvW5dvWnTifx5njjTWnGxe+AuONNz9uXb1tXb1tXbltXblt3Ynzicw535hz -ufHhKzDfmPPztq7ftq7ftq7dtq7dtu7EeeHP+cqfy40VX4H5yp8fl0W5abvxZx7nzotvsHbjz+35 -/Lnd+PM2IBM+b8B248+3YX6OP5cbf643VnwFlht/Lm/y53bjz9v0423n4m3rTpxP5M/txp+3EeXb -1uXb1p04n8efy40/1xsrvgLLjT8/bl29bV29bV25bV25bd2J84n8ud348zaidtu6dtu6E+fz+HO5 -8ed6Y8VXYLnx58etM07arvx5Z45XjnllmSfW2aFxOfruVMb5u1Mbv0IVwdcb1tlfufVXrv3la3/5 -1t+JdfbXb/31a3/t2l+79XdiXcksWPgfkxEfgyb/37Or83AYWwSUGcaVMDX4jU1r4imO5Cm2/WBy -e113Y1efuMENdlMzVhgZFq+2uv/GE/iL7550vgz7asC4Gp6MNG/mhnPg6akDb5eBXzX4q+WlXAbe -3hp4ferAvb+M3IDfNj70y9BPnAu1nHbZzw3+NFRuZJ4vJH2Dfdv0uRF6eiqhxxulf1uXv5ptr+a/ -jdafM/bTXLUR+7eV2avp8moC28j9OWM30oxXev+2Pnc14D1gvVD8c8Z/2oA2im8X6r7Bvm1V2ii+ -PpXiy43iv60dXS1iV7PKRvHPGftpA9go/tvqwdUkdLUrbBT/nLEbbZYrxX9bQr5aRR6wXij+OeM/ -teqN4mUYh9xyA35bU9/FGXUnPofot8Hn6+BNeLlCv62t7kLNc4Z/6lYb3e8DLdfhX5Xtq8a2izbP -Gb4R6Tb+KZ1sQ+3XCVxVzge8NwHnnSl8b5AeT4YjQFXZu1jMNs+MqHo32KpYHvgeTTxrR+nWUbz1 -FC9dpbe6qpeu6q2rcuuqXLqqb3U1leWbBr0v1nUFb0vo3uxNPVu33UqXnbnB4m273naZxNuGpdve -XIHxtmNvW/njbc/SbXuuwHjbtHcM0/G6bem2Q1dgvO7bpUM1ed82rl426QYrt41725ZabhtXb3t0 -BZbbxr1t/iu3jau3PboCy23j3rFYlevG1dseXYHlunGXDvUorhYd44+b7UaP9w24mW5OnBceufaW -rr3Fa3fx1t+J9cIo1/7qtb9y7a/c+jux3rjltpzuvp73Bb2u6Il3dvmJOLV6i1Orv37xcVyxiA6M -uE/5DdDUCv+3f7yg0Yn3dQUKchMi1h53oH7+9YaTgEd2mUbXWgaAJo+8zOwAMrhzxKhI0QVHhR4/ -C72tNYjQUFP5TBKx9KlmpSEklDlyPtWOteQv+lSqdRS+IIAx4tVxfp+P+gWvTX3+/pGOwdFIcUAN -SeO/+lvX78e1I2s0xp+5niEh0aXcejobHaPd5/PjPAjHrq6rN9M0rAjbsidfEseY7nka59g20mmZ -I2U93jcNc9bXVTgb2UjrF99RHlEJyqUvybkkXx2t6pfg5uzc3sX+jw+jvC3zbz5DqjmMQbVwJdXP -pFYq3oVYrSve/tzGXKzfwtmn28T2Fvq5kqD+3pbvbDTYe8zlGyfibHQOdJvK55dcx3Clb60umdTt -0AQ1YZRo9Ky6S+A87R3vtpUKVApK6cu4A+aqXVfxbGQjFgrW3zudP7SKg3DKN47U2egc73XDPrX8 -iunKnNVFmixfcPpUm+WnWO4cZ/nseBeKt66YkJQoFayX0ja3s5H+9/qxcY2zETocJ/a903W2OQa7 -T+fzyy5DeDbRHzN7XWBKQtV/cW25T+4reDTS4Qoty8+d4I82uY9/K/H9o3U2OsZ626hPLfspDX32 -Mj1p9nUF6ixoRdzkcFi22Fval+JsZUOV9TSZbVv0s5X1+d7uno0eBnw7iZ9ad2V2RAQ9LOuu/wLO -ll3aBTm6imoySU4/VzaovzdJToHWJ+5Nf5OvzhYioCkCFS+2Xs5G51DP+TwKcgtHfMKJPwe5iXN6 -vFRS464vK7E3MLlqcIHu3xBnz0YsqjECt+Je/+FhYLclfooE90hpuLukcg/lfl/yIjW/LaKfcGDc -KFSBuu0snGmnwre2We0tbJhMeheh6mihUtm7R+Bs9DDKYyrPk94uRG1pvdf81aLsVMTqHd22ewYU -olF5jPu8LN3ewGQmIdS7Gng2EkHszROzNziHeN2dp8hpN7r2qiCvaasPNQ6sugPXMtjRbsStQCWb -Tcfowe6HbXZnIxsw0+9FgDpbiAT23iE625wjPafzNEHtQt9SmGhsuMgNGlH4spQGK5aPvJaOOqa2 -imlKQSqBcce31dsaqKQk5HsTnI4m2tGbp2hvcAzvtjlPkcyeezuelLrJZyZ8iuQlV5RKZ8tS7C1M -PJKVvIpLZ6NVpL3u597gYYy3I/cUkWz8l6ykz6JtxbuZJq0zkZ5UVtPOlV/q702gU6CJVoDHN0St -h0bSoyJRiWPr6Wx0Dvec0+dXX4ex8d0nioPbrlpncmeptKedXxfybKRI6HINb0jHZyOW/RSF23vY -//FhkLeNeopoeKd4qekQVGzR2lDAqD7xqtJL2p40EbwbxVtnqi+Q+KddC/vc5ra3MFFN6Pgqup2N -VP5791idjR5GekzneULijdg/Y2M6p7dJi0pHKgxq59eFPBspEqXju8J5NpIe3z1WZ6NzuNcte4rk -eCV7jmNZGH0TnoM/VUQ3Ri91w3a8G9lbZ0JMixtFo1boHtumdzYyKU7I+irVPTQS0fC9I3a2OUd7 -Tulp4uOVzdsly5zG74ueVah3DNnwbXupfQgFqWCofd5X72hUrAoxkfJNvDuarKrOm+fqbHQM9bZH -T5Ein32tnmS7yZFmtBcp0W40lSS3JTlbKRZd16ukdzZa/QFv7vHZ6GG8t7O4maq2o/3ntIzmCv6E -69HtYgnkgOly1D6vMtfZyAa6wB+EhrMRyx2Kwu097P94DNHG/7w7cVviJ7q93H4b8gT0mtNOr1f9 -2chGugiEj7L62WiR4t7czbPRPtg5k6ex4WcvtqB1Gx/m0SuD1S6vV8vZSIe5WGNP9ng0WQ2pb+7o -2WgbqM3hedz3usyf8K64nd/yuJWP2lFVbrtN/WxlQ1xjF04eeDZa4w3e3Muz0TFam8q2zBrhYrEF -iY1zm6l0ymFs3ntu/Mv3Gj63C2Lze31/gMsH3A6/2Qa9+fY/EsnzKYe71hP2mqBbpvBsnMN0mLS+ -BfBdC/juDN6KwvmdgmQ+QX/PCGn5XpPkOWxvT4xK9NjLUoKvZStXO/7n1Bm3hf/+kJcPuBh+s854 -7f0jgT0f95t/Ysu/a+3eHf9bITXfPnZPiiv5bvPsE878be+/P+zku+3+v9nmu3b+oeCaTwUPfIL4 -vmf13o8Ouse2fPPsPDFS4SOGoyfwvmuswIcCGj5kHN6u7t3D+92hG59wv36CBr9rAd8e/F3s+Z1i -Ij7Bfp4Ux/ARM9AT755t6T8U7/AhW+96eW9D+O6wjo96VT+x89+1dm8P/S7z/L7P3PMiFD5kRHsC -272RzYcCGT5gtN0u/20E3x+v8QkP9efv/d9p9d6JNblJTd88dk/0ZX/EPvZEPXMzXH/I3f0h0+t2 -5e9uuo849z/lPHvCvf87reK7M3hLcPq21/tJXuaP2A+fqHZvK/ghT/SHzNPr9b0N4SNO94/7tJ4g -//xOC/ju+N+Sf759iJ7n8f2QhfXNTfyQW/cDtvftMt1G8CEP9sf9c9+zCO870e8SxTdp+e1U2QzA -3/75v/78X//tn3/+7T///E8//CBwSqFd/+VXf/Dn/y/9W+N/+5N/+/f/7/82XJZOS2Ak1f76V+7l -j3/1B+7lr//jV3/w7/hjTZq9p8xywuwPtbb+peByb2n8lfiR+R9qoSxgHyf46wGudZzDcYS+Hlje -gk80P2NMfzH+z5eK11Jw3O29uSLn/kvTwnrmcbHgFzeP/+gBactpiC6jh/AlRJd5AoA3et+D4Hig -CsA6yLF2BpJMw8CUmrQcQ6f9ZHjoWRqPHvT7Ip2RPMo99R7lezx4pd8XxBjL92VQrDQuPQgwwQNy -ncPAQPT0n/743377p//802//+V9//rt/+x8v/xmwP4TXrdVSfv3yn/7bbwfN/NPLH/7Jn/zxTz/9 -++tf/utv/w5tf/3yv6Dl/4r/w2MZRN5gjKI5Rtd1lRqeOmoCHzKUwLpO0ocosGWNgg== - - - 8zrHxm/xMDy1JI3ndHytijWPK1Dm6OqCoOX20DYOiU6A2SvWYwpPX6T+pRUlmcjPNjJ4jjyFzkPs -X6LvMu7YaxagK05Wg57G+VExzKGHkoo0zmOZFOgV7T6E3wMhjMOjROlZb2T4IEPZs1RqkyWv2Su5 -+7m7whlwsF5+0u8XqJA1trymR9iVDGryelpzkKatdz1/vbd3lxiNXdH9CEXHn5ws8SBfJeVjBf4n -rnH2yihq0iWORUm9TfL3SZa95zqXONp6tDgWQQ9glXlXH/r/pCl2OK5pu8cd6MI8Kr5Ez2DvepIt -84FJK7MExcCUXGdgLDHKPnpEPrrK8AGmRfJ4mYwZyuitJDps3uMxZAMWZbsD3jxTXcHD7URLPtiS -ZrsNBrDYwMZto4x/SD66J3mcikgL7aEg18ZoyawAIMSbUAQ4yFG2yssZInjtXsYbq44rR0aA816T -TCLmlCc5lxwbD6GXprdXEhDptnxzhMGHZG1LML6cvyB+VODjH14YqBwnc1YkA0NIXlYGUqFhmCPw -zq461zNhGMJytsurxcwwWvl5fQrihPd9lbSdbNpY2pZ0EXZiejK1QmppVZlD81NGgHjRhInDTcSw -mCINJo5rZqzA33DbIRGmorcANHQG1lbO0wcpIMnp6xBnZEHGdz5nwZx8kcbRNc/AcaabAIeM0xiY -irM98Sit0AUOXUuAss4YsLCQoB5yjCG0OjEE4Yh40U0uHcRtVwEO5oqBYc1yL1VaQvH6UdeyiAgV -UZwkSOMSlYuRVH1d9d/DvhJl8nqE1KbwGqqXlSbhn4EpVgU2G+Nbs8w9yg7kocxrY7m7BjA3W6fK -DDjiulswJB8VHruOIWQbGDORVdSOkLLCxKCHbcBjzy865agYYigv13V4Nr8fLDHgHuFdB5fgEzS4 -IilEDO9EkL4y2xUKqcz9OiQAJf6cjN93aOpCTjG5Jo1TCbbQtQiwFCXdlPKCQS+H0V1tURp3w+Ar -H8yAMPmkR9sFZbbBmcA7dF5sIQNLzdLYVaIAAHtOMl0HniYYBnPIvmljZmsDOGTXrt25IsCQow4s -KLkwPBu8dWtcmx5tN4HdGFRocucM+JCgdB1S0DHQ27G8vH2OoRodg7htDL4ogyrALFMr2riyZIZl -kFENPa3PAbiYZINqrzpaX7vyljLn9cYAksvK98YOSeOci2AYbbMAa2g62UHOE0NvimFcjLxrYTl1 -Q8/MAqSehXJNkETjoGQ2RF5dsbnvC/CtMWRbsQAZV6fW60GQ7xCDM4LsTbtT2WohSL+QNEz8hqAq -4chkp0o3eAodyTC+iKqVdGciWJApzruDgTHkqhhka4QvzZvuJ8XQndd5eRlqotfEZVliV2CRMzIu -0Br1rhzwWnQMteQijcfh1MaZxY8Aq132KlTPJRjwGsKCWYBZFQJSzgUYel3k6olBZIPmmDGFPAQG -VYgG380KzHr5kb4h3w9xy9a3OlmyAouGiBElNwWSQCfyejEM5UuzRbfhThtGZFwMTFV5ri9zDPVL -6V66a4nFiwGs3bpr477/79q4R6+3uChsoUHfV5bXWOsYwBh7euyucZN5lTHaZo2raL9BzEQE7FPE -CTC6OJEmx24mWZ+p3IkiSGumZ4eo9ydbs4e2V6Rvj+Ay3OvE3l4FXd9xaiK3HfdcT3lpy8ApIZU+ -qb/zRUbwcQB1zYecoUpdZTPEO0O47Np1fx+I4X3KOcjsb7TxhSav1PsWqVfY/OVkqXY0ZpF8V5k7 -u7mSVY0SZWwFb3yErsZXnaciFmicBi/2RYCt8PWVGnL9BTioQW0YmcpSC3zIbrTCeZxux9ycCu4R -OeTGTwETkLgyYyhZ1ZvaUUOYpgxbcmX+gOpnvBd1UJlLYjDtzmaBJo0EzwF3LGNWaGA1C3DIGQzE -k9d0W6vJlVeye+4+eE/dNxCTKg1CjQM2r41cTO0cOIdWQWtbedDcO+kgjTRBtqyMBQqN7/qx9a0b -gsTCSyNVzrPkNgajF4qwWgALYtulqzj65PlXUAVJUqw1F4yIKWvsWmETVAkm30N1zCqBFKw0i2Hd -RKDcjcC7zQkbKJdRZ7sljz+jYh0RUXd8YEEvydTENo4C2yTi0Lx9FrTeJMGYzELcTfGOwS7PNiSC -QqsSnenN4wjiPrTDH70TeFZZchBJYhUZjrmkZ6mLitGY2n7SiyeIVjrgseht35iwQDhi+oYQlLz0 -FcaY5tXnxIKLcynyQsahkD2v3ukYmuivg6Jz1n3EgAOr0eMvnDG5/GKoTB2FVWgMticxi5Bwttzf -TSwjqtYO4DibScbrBGvCM+/Ssre6yDHJMPRWVBIapyMwcMiVRYDeKdUSXU8xysbmqlORq0UvM3bR -5LBSo1qHRAP7gbUIG1tlURAKSw9icUk1qXYTUlfDyGABqt3ABNvFkNMas2HfzPgMccLULr34Cr9N -LhjmPTcmF1hmGUAOTiWgSEe+snGNgdEcGX7ssZjjC+tuDFR2W0zlQVleYbd5XL4mjKFe92D2MjsR -hYDB8+rmqfoVE/yEAAxD6ro+Tu6HAfQhq1UvJTXUNdEoMwv2PymGS+M30L41huuAr1O7rwPsbkHG -pq4ED8VIDWXj0KhtksZIQLoxf1TLouoyOAt8hgjI4n6y4+rF1cBAoDXrphPTxsAMaZaBXTSv0RiG -zB/IuqnrELmAvmBIVHGWG7siGJLpAuOfQ/MCVHfGADY7Fn4K52nQJF+qY3GcGRLNyiuWShHzSpwY -UlY1TZ1rYxbVq8ZNRmWZr/fSl8OJt5UsXe/AprsJ4UPtGYNvJxGNYT92KvioHLEYlYkFvbvq0yCX -zHTnh1o516yycuynkW8Ax+W+YDCRbsiEXiZ32GX+5smWH/BA2HC4h+lfBceLavIIznT1JK7QaN48 -2CBEWNpMP2CkTW1spiXCxab7bSwXKqdq2iRemq4+bTQUXcPA4E3eKcafj1n89Mx1+itx1//Zz/+w -O+t/dyf+i/+UG5+kvu7I2SBG0DAOEd0cAv56gCsIdGzR10VsfATNjxe//e/4+t3fcY9gYGzNduwU -elV4aFHkYpcjiAVAssCxsFxSFKCvXoCrlRbnPSvmQBYnAjp2SY/GOVcBDj4kgnkLJUwM5HUhuLg9 -ARTpp8KMGBVtrHx9uMXag/UUOxIwwzz1VTGT6EVwkrds/RuyMVlF6JF7DHwzM5Lm+su+U0OUrHHp -sbcoGIaMlSbmHnWOJJjYSAaXEnhIOptMyXCEm80QtEjd6dAG95uLVAodv9oG345FGze6/weQrPuC -lu0jFTpwVAQJ0p6C2SKMts3rQrScsux0jTEpTeS6YKglCjx5aVuc7ohLrb1ciY1N4finceUmYvhj -FCVkJcMhLwRVmuiOkmUrLJzxEkHFmXCWQUEy2fPSFTar8W4XGWBhDybPsKuWBXiuRjI688J2BQJW -WEcZKGJaFS3GMFDsiJySymOASFx1PVoUGGlhBCNz0xyCLRP9pVibaLXE3BmoqiIK6jk7e3mcqKLn -ISQmCrgDVC+OOjPc60VPL8QF29POM25O5WAC2hg4IIWIIgcjilYmgsGadMLwADOwhSrHX9Q9Atag -wKYmPoJ3rwozFLOvOjkXm8DltgaQrjQmlOAVSJIgA2PrE7P4iqqE2Hy1HoXahnaXhLdkI4ihsOms -B12WpiFVzdW58HKSKKtbtqjwLBqP+0bt8xw8LKfxY70jAa/FDmTXg8f60nun9N54QTuBD1v6G7XC -KBnD2qrnFPDI69GyzhJA9almMyoDGFte5HQWF9I4mqFrzEFIGqQGeLMABXHUZGfDwJax3SThvLJB -ajCfafbIiEozqU8MjNlxzAk3ziyNZrmTWNiHa9IwkOOOG+PAfVV4kwCtzD4mnqBGVCRz5GfHvIQl -ZYjwhrm1XhTesmIObDMgzCVYh0OuC06003FmqnQ4tEMdRdJVPvbJtrA1Dr8VwVh3sMEoaXpDY4Mg -TBSmjpAtR1uqubWPq1HNS421foY3YpIEFFtUmiazcwiTvgJ7kZqZqF4VrsIkwvFSn+uUoxN4crKN -gf9kVdmzoe8Rs3UKwnHMyOuXrlGWAhZu0CsvNgHNNJhYVUgiXEx7odF0r0XOPrGnrxMeywJXINlf -TqBfgROzD4rZszmODkATtqZr/TA5m3ZxKg+0ddoASxCX8TsCCiE0Mx0WXETdTFlZp02NS1U4C/kA -5loeMOxDmHuSsJCVxV6yD8mmZA7lGPDMTvqvChd5o2aNUUtZYz+rsBQGtsAbmNlY96MiaC1LY76g -x63H3kCC2ZYgzCYpVjcZ2OgtOoETzcvIikmx2Y4FIeHrapBoZpNhKuzLImCFZ+FHw1CSxPUGCXla -xgZTPkeCYgyJxZ+yyOI0uUInApbdnL00JkcwA8WsAGBUDMnVMDG4EmQWZBz5+sY+fdUtHIOl8GyW -FqDNvxo8eRF7yHj4dcK7thfvGyHxCoSd/Yr5x7VTV0QAhPzxzT7JhctXXeAgxFrUzkw4en+5IV55 -1oyKI+ey8axxMJWH96y8KcSmJkMWwu686Z1bIKDyozD3IbIFwUHxBjwKiZN4HNp6k+dShQfTjf36 -/o00D/vQIxBrMO/Abh45ibKi6yfIVdAcG3Ife5yDEWGdmAPFFhgv0msT4ZnMNHLXnYANXm4qOBKy -cpLYLKaqOM5xIHiS0zeA6sKrGtd5Y0UyMtfErl5KVr4nGm/nk84NRWkjT0b3E0GV2JrxV+PNKhDA -edE6G+8YqAGxfVEH4Dgh0Z4WYTANHUPkEw1XRu6KNsAdz8sI5miLoKp40xgy5t7Kpav4PbDggT0G -sso2hhyzbBA5O35gT09XT48ElgFYfdTR2jqWyKZERixxpgOoJ6Sbsb0gAKzIzJqZk0rke5+MHrlw -yD86C0k8Qho5WoKOVcwjgiCww4bBYmov8PMTexnA4opOobCfrK8WDcA9i6Jd9LWvCi9MkN2ZEFIC -Gz14cSRQYyxjd7prpPTY8s6tJ0cNA1V87ibkAijzdTObAd+zuNcRouuLjszzWeCRebY9wxHnUlc6 -c3okKACIMc9rEXBbIPFKg5z4+AAm9A8g3yZwzrW6HEDVhjtzFgY6EQa6xnbiADEgz3jhLDPnrkzq -6xatO+imiWLRWNnmgya0kKu6eLrwfcFbv1ABNZ6uFwY1mEFLcoTpLhcgX8pAO481uEyXGZALh4HO -DHOkmDMwlC5DSIiGEQxFVcgmBogfyKEtShqLdDnrCcH8Z0j+O/Jw5c7gxBPHXEbwupPAb2PRYfCS -JC6iJTQnRxYx2holnhFgZE6mVvTW6iGvN45hoOAugqvLJCMkQb14KmxkBK2pqyyV+X0OTeZGzJuB -qXS5yZSS8awzm/GgM858g7FqKasTTyX3sd6x600gNhyiA7FHN7zuEBb6KF6dwkz2eUaOSISBkKJL -2hCSkxGu90GuGDJ5CoZYBaj+TQpXaHpDZYsSyUjaCOITTl57I4mcYarudBZu+eqr2w== - - - 1bVfqiaa5bKkAlAE16vBu7ojawi6cORvZJrqQurwTGrUf4FcZlQdcuhKayyvAeh7VVrjkJnHMUxx -oLEbgKdZNHSY4HKJIC2s6rkPoWpjDro9WnpjtICXonClzbEDrnrF0PI3tmVpnJNubBBCxoBfrnNY -BcYsu5g4esMERjXxJbVhkWwo+Q7JMiYgRTZ10VGSg7GFGM3p1bpJl13tIhrggL6KF0cW8SfDoGJQ -QuSH8gqVbeAfE5PGwyzWCYrFnGw5Ps4Jhqo+2gyrhHGtKRRnCaDI01UEIGdZXAVagVcNg9GjGfgq -5FGUYiu3D23qw0m1eLJfVB11mabQxOaQr9ZeLhxYTPjOHrAY1E0qN+MD3tmlN9MURFnnFx08mIg7 -NWiySdGBWYRGFfkAzOu1W5SJ0gVmV6dYGcBy9ersHHEtt0bQU6EJUYXlm8mXmt4mja2cAPpcpDHF -6713CKte6mBYzi5ftbFByRWGOf70lqozw3AyOrHZSUJZRq6L3SbifX+HT5HpmBs7Vp8zbANe6FDC -qYFV2HChXbbvp+HJJVG3BrDYzSfKBoBOzXMkOtsqqBiWWeSRCYsNrTD5ixAj3lwEifhFiGmtymHo -7OzgJVOdUfUNLG7OciGWuopBvc2ruusl46NaNTUiGzdP1w0mOWteMk0vB8mDIFEudptFVvmuZBVZ -HNbOKDVUZZTqaYagbFZiXRwCWkLXXEmSfjWWaEynSOOYgnRHVko9bzPGqU4ExzGcNyWF5cmBppP4 -avASxH5MecUipDgDRvaPAxiUTeRFuY8LW14wiKlnx3CMgRnIH/07m2AQQ9TFYFdwGbwqPFcnYWlJ -1LMkMYoi2PD1lRBmmvuDEJOamq6gJ8sxG8DCDg3ojbWrBVQTEdpqe0/QFlV3LV7dnmgf5aQ1FW7I -UqnmRLoxfyDzgrKmtviGySQvIfuNu5ZRuKCjECUzIVsoicxEh9JMqL6mqJNmYWKMN1cVuorkEgxg -SHrlk6/AJqcuLoTeFrW3BpNpyREjfYnJokBnnpPQVIvRtrp8tK3sM5ZlkNBNDDYtIZEt6rqTQ80c -BRoS3Zj6DZ6F6TQ+TIJEBPa2+WmKLvAgRGMZ3lyLbUqD8IZXGQYJ4nL/9ixoySC83NVVMCdndjHV -Dxp/JiKGU+ncL8xbUvl5wJJVBIlGcg2mkQP6kARfnWNQ/lJZB5FZSIzTGJjqf8EUvcpmHzu+KolU -06Khadl2pGpajrM1r2YlxxAkXLKtCKjAELedrgt1M0GBSIvQ00REJcI3PavKHDQJIkuyJDfsFvdE -I1NTixQTIGWxi77upiymamXDw4PLRmh+R2fGofJmE+3cjHZTQOtrHQaQk9h3oTE59ZZlMXB2Sy4H -ezB9mShkukLY7k5jSIqBglYIqHlJ0x3TJN1wMilZRRVuwQdkUHRpMZMc+rQaq+JkcZXjP5jEohlG -Ca78hVQJQdIUSOLVlYEzf/cvf6rjU8N4Y8uUMXixwyixWM905IUGjIlRNOBGGI+Y5Wb5K4QTce/O -sSGB40OwvpafKwH5gOeqCbqZo/frvBwA5JxbRKNMVgK4xLnMcQLYg7jGVVd8HIOJ0DGqs6lupmNU -g2lekKdpx44wJOS2tBcknB1XJ0t6xGydhqbnoea1iAByIljOqmKl+KrwLEFc8J6wQDSAEjZTM5sZ -r5itUxfVqF7pS9WKAReXGOBOycBF1UABl7sMQA5dxUrK/Q9gUdcjKe0/vtGjyUW+a9wIQiXAUl4N -HjWEQuzlvvG9S7AanQHFg1mZUiSSsnH2CyOQi4AwFK1sowGlD0NY90fiTLCKiC2z/ZGbDnA52ARU -B51lfTVz/g4g7BQ/2s6zqESY2VILIKvjAIb65kZeh2ZriiSZEOWf6Hp6Nbih6iIYDWDz/nArAljU -g6huf8Egoy7mxwcw61S6WN8fx/DjMrzku9AZRW7Y8ERSALzwNTKAEmQNYGPzN4DMlXGigtHZI2Zb -kzSuI66VWQuHBAvvQ9wcx9MV3r6vCpfLthbLDEyB3XjsWFVv6wPmOdOihn7QJnSoV4WLbRQZQkHy -R8y4BaDENcfC16FEBJhEDcxd4Y1lO8DYnbRjPYYwQ4SaWtqR6AMr5avCJTRB4QJkMiGgxHEByPeb -AH80DIH1F8/upa9v9Ph1GYxGiIgD1QbjvJNQMZZNEO9G5mB2odaWBdgiCX4AzmilymEyApZAvsZO -jx3BwwhsHxHh3pzQFXC/GlhiOLrl3/kZBtnNSoYge9YsEUBWTPsY8Fw0lUztbABaoBge3Xu5DWGy -c2dJ94VVXLtXp11UQhgJKOGURSNvqUKGJPYVFoPtXvWmaattA90l605um8cxrMM7kFv5BhX5irrc -CBgtH4ijqajEBMfvkeWkLMUXKHCGGyerktC7jkTNP9cJytYWS/TJXPHl1eDZaXKH5koUk2kzu3gY -KEwBwOqWdJjuNJRNGSthKOqhl/ouj2OYZ7RqdCmIEvajV4VnjqCmqFPHc78GrYrJjEkvzbhKiKFF -w3V91ThQOdCgaI0OLOpvB+mBDARD0Zg/Etwk5hgBtVXQpq5YKdmTYMHyvyl0lumL2HXX3ppTeUss -FxTh6lTyS6Z7E7yrmOh70zDdKk2VYRQ2/EqUk1sG0DhpoYoILIGWxkaL9wpUabJyTTgbAIXJs+xQ -pQBI0yQkSgBtyispX5VXK5SFVwb2IGK+GspaNTKAbr1aBRg4nBJSFPxZcwxs7tcczK/G+thJTCJa -sbH1ImHJjt3fABZmFVUcQ4a5cFgwaMeMv4D7EIVt2c5XdZZVCaB8lytXkR6X4Hvovxp627sEkD8c -gPX+ilG9CQ1M1a6MGNR6RkzTbh/1uKB9iDL10JoClQabxXVIoPGPb/S4yRdOnWdUTurV4F1NX8QP -vhpcbCWV5Q4Gqv2sqt34EfG6AL1oWT0SE14nPK1wWwAJAiR41QWQGMIDuGOeAXWImrCMVmzi64Sr -uUnVOALqLBHkpcCadUZmxKK2NT22ncAV6zaEKXEJObGACr34dcJbVCmX9UkC9nrIw5AITEhO04Yp -QoFIuWJqhEwSVfRlkr2MYV88tfStUugKZyxphk9WC6u5zvzh+6WxBEeAcvrLdQBT1+mWoShhZq8G -73qPqT0s4NybUw0c479rYyrDwXCJrtkalyCJxg/dzU30JlYmvtxe34B/NTiH2wMuSXMAxiKOySbe -BwCz+jXpzv3xbcwiIGS121VJp7CiSFIUk5LcOasvMwfnthLy6qGeRUUwo1t91goytZhfElibBV6K -O+hxCBtBbWdp0hOHhYK7qM3IWWS8sCIBcoYPWbW9VnehxpNvsTaSZvRutRz523EWiipmpArsZ3lV -OEWEMlzq3oWZlxLshAJoCaB4RE+V37KU+dIgYMKgqYZLfYrkvRbUmav/ODbbcSSrS8xGZInp1eBW -8cMKF4298VaVS5RfAPnWo9JHbsm4V/E2si2cgTPGUekA6e4+6xJVc9WhsRXrVKEyJHPohmlLAIFr -YVtyjv6os5iYVS1AFZugtUis8gBcfFYO1uQNWgevpXTER462wYpRamWrbNMN7Km1tZ8IzHpicdNU -dbfr7mnwG1cMNQRk6eG2U2knxFpoiOqDfn17swUuwY/B3JbB0rGoXqDBghXwtCQv2mgr7Kn2JIyC -JTqKZH25ktSPC7UdtGLUFi0eNotjKsyAJBTxqwbs0apv1Tk2dSLGaY7KFtwWJzO/kauMbtYbSxwp -Y4nBmqeTLMcdQEuV1qgilLlKKnXRvv6kGILsIbLGrewZOYoFQ7N8430MM0BEArxt6hZ7rFHakdn8 -D+y/a+r9lLA+8unpGi1VnR7h5m6suemaitMK7oCg5ciWFCBntQXXqIdzxPOicZZv5Tn04dXgIhR6 -C5lFsVRO0WjeQmYHUEyeAM5QI8BjkpI3KroAKMVGPGviAsxadofCGScGr8fbLB1zYEHdRo+TmKSU -VMkAO4ER4lXhVSJPxLcj3Msrn6OYnvdYWjKvUuAoDQE6ZT4a0vs4hh+X5VcxJ7BNyJZfnSqzwJ9H -PK9WHtTaTd5ZCENcI70GPAY9nxrp5d1SKtEKdjiL04/sUDEMGgMWreQPuhPzR1R6fpzEOj8ngSaR -rSWvE64FKL1cHjQQBdVvjU0M9Qp/RJoMwz6CLfzg8cTM9CmOPdZrdyY/NS2RSDryhAuPDFNl6Jrn -24JFv8Ff53S1plLPwQZFrxcxpMIffxavRk5h0WPhc1lciRp25df7JumDD3yMhC81LmDCSKRmD1xy -VtQ6OqvKAZdc0ptMgxThfcsKzMEiEDR5bbScEkSqnMjNuyC1l1NZBiYO3IRoLa3LtVS4ReRH0EKL -FphQjXFEvgRkHb2KOyQY2/JozEUw2xotg0pX4yvFEOxkt+CXiAkNhROJxxZ4LFtVac9bVl3VxpJ3 -f3D4GRGW2hJNQ5UueXYqVyTeZ14fDQtMfHB+Bz9uXEMIBCiZCxLvIt0lDRomjieYC+dgE7yqO7tY -km21gsOpWHhG2fYOlY802kwj05FD1tQ6qjoHMr161HjA3CYG7omyzixlTmONCicjyPdOY82U6aW8 -RAAvcnFK5oAubHLmxuKrpYgwjotEX1JmpCw53oRBzotZkymnTOwdSY2kVOdLuBBcqJZ1CnjVeuDi -fErRbvYyz0riGJ12RNGluNiu6ZaxzDbJF2llHYW4ZQCUovspmcpauGrKj7rAXjJsKu8rAzVSaomp -yhasBldzngicJVIGiVIBVq4YQfYWp1XQVLKu7Kux1YkytGohghhv0hgwL8IkgMJKtngfYHDFYuM4 -tGcAQ9SIIRXAU1yyjKbQn6yUBMWycmo4tS2ikWapUoSttIj3JfkIjYOrB6HGJShLjNAElLjoyqlS -hiFI2ZHKeqV5C70ICdV0uBSW8NSsabQ3cx7DVdmSgHKzE+ptVvjlIulOgmAKe4wZWGaZ+pkfkqKF -7xc25wqxWmqzsbloeVh5Sz+OavoC3M91k5plxUJ2khQlJmCcelLybJdhDBLWC6DwjcrVE34ga0K1 -lSebv2FwWeH6kEbyVgqqsHeUV0wjsytfRYahe43vF6s8lY6UTP1q0kvylntQOezUMFCwOe+R6PrJ -LxbKIomlhEGoZ1lHv1IJDu9XtbVoOQwJtWRgbnri7FK/me8EnjWfQ5XFNBPzluE6C46sa/xtQtVK -dbJp7GvsLGMw6Ug94zgf9yikH4sJrfFFzEMTOTIi1SnKJDQLAM8tGakvynRsS809DeNBb+a6E1dt -jbNQ4papEpuZUqoVp47VqvZVlhoEKFF3Eu9ofvWWdM0orPWrwinyleFdQxN6VYLSPJ6YNYdPo5BZ -3UU8hKVxaDWHOC9ZKuvywj05CzNdir0i5iHosqsEG7M5NqvVTo1I+lCO5sybG1Gfoq/pPV8Vrgah -ymEVP1DM0jQLWm/RXL91tcYBLtaNuko8gFsQup64GCxzpnDcCgM1j0/MgIZZgyZn8A== - - - dbRoIkp9lg1NZgQpbDmwWTuLmi8sF2ByshKZx8tAjW1M7L4SBEEDCFDLQ0O6gtmRs6V7AyhmFGTb -uQWDagkDgxfzdLCCjYn1OAZOD9W8+eLMYEl885ndO4bpu5KSpbCWaAVxMuv8wGVIxTmD9wuMJNya -0SO1tgcC1UcS+3J/uBnIzXA+S7NkxxcfTdjJ+mqdgRisxl9h3ccwSEEr2iBT76JnjiPwKqOoTi9D -J1HtGFrRnEI6DYY4S1WkeWsBq/Na0FKJ3bOCRC3pQrHl8VZKpnCAAWHwKu1m0eGwkCbtzpzV6Mx8 -XbUqDoCqu9RJO8jZ1ust+XV5k2aN0wbZ4mjWFaJ6oi3a6m+zECBJxN3uOLhfTLqmCm1zyBI4XL5I -3CX8N5J/kzlqUhYn2bs6zfxkoS+pIFYYvy8FbjTs5NGHZBioTDY3zjZe9dNmu/9RRVfQJvZi/qQY -NBy1WEh+qGs1l2xFw1VNEcFNxoCEIhXTugZaZiuVWS2pBialpvfekhm8lLZFjEC1EE6pstk4vFwe -a4ispTFTVLtutHzMwrbSHxXDlN2qSF4hmFo/FQpU/q1azJdOn2BY2W2VFFaq9WjnSEoo4xEIS28i -JUQwOPb7b1wcwKgqiUJUf8fd4q1QJmw0Sa+omQrnJQ1bSDVrhOeSdixRd35NsJhBQYAXq18Mmvqq -YC1wNhNefbOrtmikB4DJAoiWNBs0ZlevBhYJ5sq5eCzyVr/Ap0DlrHIuFVuSs5G0IqpetZkVcfMP -9qwZhkoTCAPrWr3XqrJmSxYsa3K3Fxcsc0COsiC0ki6QxTvhy5L+PKNrANdqiFkL0FEpXMuG1MyP -awSTTNgSdu2ZrmKKXDFPH6KlZqXwNsvQFhNmimUwUhnaWVbca+Cd6mx5LY3lo/lMxB3/A1d1tTpj -FJH8AxVlTRbJRUZ1wTDgthNqQMLLZLZoamYEUHId81p6g+yyeowoUPKrwlvTy4RKwht8mjkolNLg -qpdoriDMwF2PWJBHNGCGDyqRxWk3Q3+WjCyV5ci4X6KpO1HRtqiZyCQYzbXw6kTOrWnjmFU110w/ -v3KDZoFnhLlaSIeI+d5bVkFdl1gjGOsqki3W9yHw+6amb80SmqqGQ6mUJhkRJOUyBjxMV1VfUt+Y -W/UPzyHnrmn8rSpWggCZZbpmGgnoKgsEk/+5qlGbWhZNPq+cVsafiyMY7456Nd7Io1sAOisnQzti -GDSQs5pe5KpRdeX0XBkVB7+pTGAYohSkqBzuz8CZRKkealfYS8/A+fZbsSdVKrvjBZhVBSPZ+m+0 -sSR0kKoj0agrJ9CEMVeW1wXo8pbuhlojImimUDJuG5uq78rBFwSy/DbeSWMSq0cv4xWlBCObZDds -RRx+n0Podj2Ry+mrwqdRR1OwXNKoRzpr8ljlakdbzD8u2Tt6VQMZAXSuaQqgpL+6aDGoZd2MaObA -Qgrqkruhxe3HhbnAXWpmnpUw2WAu+JmD64KxkrLaS51VvCGBw2njGaqrddIdNKuuBtBpSnYza8y0 -XyBIuvL2euFDVzaEOmtENNOL8DZf0quajDM/yLN6Whugd33ET+MCMkcECmJvBpk8nxSlZwA1YFmz -jKA6aBQzvVxhGNReAENY1MY1qGixoFVBpqxJeXjEr5pApyfRa7XgNlP3nbNs87IahaixigVZOIRb -lixKFR3nTfWVuh2CwC2ijUbZzHQtTUMXoLfHNFteMKhZPmvIJY/XbPhCI97eopAUE0NQrBqZho5R -tLlevRah2217MiuEhIGix52qHp2fdCRgMvOnLxo6/lacuV7GUm2CIyTVxad2BYqtLSoFrWG0bwek -pqwhcEss7pQRtcBvs2paSWvGyffiX0usPvJovRkW5DlfCvAVJSUtVZko56Gp000e26LYcok8RP19 -hannJnLtGUEw9afITg9Zm/k2WrWQUo0WiEsGLU0iqsuuqMBOjyjOEvvr0+TTOKEvrCG832mtkaqF -o7sRb1peyiA6cc6qbSjtaDgZYK0LcFpdyET+4xyZvh5g0d3dpOpoxgbnrGZp5HfKDYNeL4n95F+V -2Ocjq5q4Dk6StZ5qlOpdePnTe6s66xamoUUmkim+YJNVH8ay10ujlZFty4u0gV2vvEny3ge4obeK -rpL0BWDXyLzFV+X8EuImCfnoresrmXrviqea201ZG3B7q9PSNqPxwmV1g7l1Ewu+81rpujZavdRF -M4wkTtGUq3SWxIFMYhiqhX9LGLZLVn4smZXUJSvekTaulcwAluxhOwfXmsYfFam0B8mBE+2WkgyC -uam9TW0gEBKSnnd9x33Moavjva3igBSgJ3Cd1749GWh+GBct4R3AqmKGXksr7fJCarxjmQUwsRtF -45q8lH5xlIyvbnoVz7xZKsL6XAgop+lbZMj4/Go3qQQ3ej7RcjMFjU8vKiY4Rtc4kKgu6cEz3FSz -/B3VtZHGFMEpGIoFhCx1vifPCCrpMtPQqCp5l4I4tWyHZ+nYuNybIfkWWCXlCohbS60Kr7WBOUcj -C5DW0sam5nuvpZyJR8mAYR21G3KiJY5gGJT7eY5pZqCUoKNUvfytfD+9ex3LMPOCEz+N4+AHuSVl -GE4DzyghRNi9W1Lq1yvVsdXYMM8ary5bio1efU6jWO9pfZrjocOQWBwasNRYcVwaVoBSwcIt72fR -EhV9NE6cbiQsVMuCcSZZSBGl2jl4ZtKVZFD1mQDVlQiR5c7KNGHgxzarCB5TusmaPC91+ggYNC9R -UxBxGVV9UGKpcYeD4O3hiKxZgtFeS9D6g0DA9972cAQn2uvzBbE6P2+zwoQJxE7lVxG4a7fi4M7c -n5SJ5peRaRLyLCzqzKqgjVVKPNP6RYZmqkIaldglcBuxARYptWLAAtBravk8+ax6VMkQmzqKmNVq -Zc4pSg6/zgwEflVyYnGWsy4qbrSM88bJZgycb22s74G/nZbvWeYGSbSubFsfJGlWptYFqyhNEuNE -nJj3UIe1rYidLLGGUwIoDxE2sz05eQeZU+Vm+TisZtBsOY0yJplFx6u1XaAS2XYuEUG4DOTEtKno -BDawtq1SRGCTJ5PZLEA3BYnazO2L7YyaYqbVFJdZiJPBMIiIU8WfYOsTUxIkWmUOwK55xovUIW8O -ICHTLxKKpL7gxEiwHK5mpzl4eg6jhrNhBNN+DysCm9AIr13YSTNCxRPsLMqIMurSYm0QLZBqUKss -E9nKTo/FsJ8TYghrObWtkQwuWqJLMyfP/8/Ym63YmiTZwfcN9Q7nRiA1nMTn4bI7fl1IhJAQlCgh -hCiyqjVA5EWri0Zv/28b1jLbseOcagqKTMsvfPs3uLsNy9aSi8dE5yVcpI4qnC6W7GNBRCb660s3 -WT01uus3KNCyMiBJbs2/Jqkg+DMYqA9G46WlVsAgkHT/CmvnunVyBHR/3cigTRzq2wtOHOFsbp09 -ki7krpSvTMDLtLsHqDpEsZLZ5bKd2NOMzjyjEkI+vQnqUN0LWkptOVBj7wjKF5KMqnnTkaPslj7a -TvfJbKCHS+zb1QE6l5BzjKvxouV2zZiBuwA7SE0lSWmaoHsz01+8TGY7YrDTl4VoOO8wrJjsyCyX -+Qv277zZLxRc5Me8q0gu9R5YMcJEfYRwYvRtLmiplNbwU04bL3/PlQKZKZclCJ/d25wDsSNf2Rl+ -WAB9VdiaIXeVgqfFNueg7JIH3kA6wXe7AXGT3yKbYBHYcMeGjEQkOeu34zbxaoo/2NQXVeiIYb2Z -0V1dWcTIFm5oiGjrcyTPDrLzchO4+JDkJ2A85VIQa+V6S2FhWO7Ckz5SIfCtaKMlWrX1LAMixohS -KmOM7Xlyv9j9jAWyaLFZ4VXJO6Lk01DIyUQx+VhrlAcMRpnEm1kbaChlmZyo+DTuGQJ4tmBaBP46 -Fp+ntkUwcuIUHj30KTvcZ+3JnmCHQDP/paDeD0knJqIyPaoqGg/xlV0Ckesk6YSfCRwhVL00+nhH -sW4cNpfLth/2gkb/UNnSSiAFKQDIrNHOfpglraxLPHt4dfNTOaFzueluH7IkVQeNHuoncQS4tZtM -U1HklNWxyN0w6+DGE+qti2w0gQCswW2w2RQkRkO+yQhzpFKvdy7K8/HguS7UzneQHf+kmRStzIsU -gnXRc4yKvsx24eBtkbPQ2nT3flYwnUkR28mv5DAxR06eeUV/8w36M3mSllyQuXnvr8jNtj5wMXk0 -QtW6tZWKvV5oUmUOq4LICBXCMzGqN+jrsTDSFKpBR7aXMlmMB3PYNqHVsNOBLfh8jhGL2+BYYZs+ -9LIsPmEB3ZfIsvqcGQuJquDfK7Bg1te9atNJ9GODc/OkA9ypABxcEEaAwVx+ccFRQ++rzO2CdksB -HzFnnnRKwkiQBFRfjqEvaHcAhjrpXoO/0BqRA397AzRTCTK3meAeYIgKaexKoQtETN8VMOLJO9kR -yJAp5omjVQEQ77A7e6rcoe/prSQ/tBXYvHKHh8mBnehADyvXmi6oDgS5UiMuWSfG8FmaE+lgAEvY -yJSdNiDRMT3w0VIpWexGk6tnVYFYdd9QglK/3I0eSy7D+/1sDtW28WOMB46BKgxdl9X/+RhmhTeA -0mgVQYLmjAcIteW1X/JlhW7tBVxgB+F7K8im7OBDl1c2Dh8DqcUEEjTgaQ2nUpBf2/DpBjoXv+Jr -84s77P7eRXHbWVcWMfgygHWsb2cZis+0Y69SSJt/IZRj0kPBHu5ZeDSps0kUaLkFtqhItpaeO7wq -eUOWJd6JFqEB5yYj89TXAUAWoC0NHBhyVctSXkSZYT/wo4zXI6JcdBAFO3bhnFGFvdGb9eCCM2nc -/V0E0yTmwQt38QUjeNi23fDvHTqre7xDATu9j02iztbpe6+n9vGBcs92UnPe8+UG25yZUuB2BdFC -xQ13nnU7Fepk5PiCkrqT2CvmXAKAKw2p1ERD74i2Y18sXQ9ZpLt9Iz2kST3/xZnyNanrHafbIaq7 -LRQ9JRiLDFVbhrs1z8jbeNqiu71J/9wIKMNdcA4I3UINobHRSp3lhjb2EkF74O7lljtCFJB+TLBG -6ycxgKA83D1u/vu11sTXWthz37sb5wEfhPfhq5MS+9cC9ku+YFeyEvKAASm36VxbcrPWQ7cdzRkv -wtNFk711Mi8DissaqLivseGBtUhIyAMz7LZGOA3cDpPybJTKHtahZjcRhGHN65Sx4vzTK/DAeBMD -vAWgDomV0cFJMl2NRmCk1tKgRied6Ob7+b4Rd8FmgO24BX7p7WLVN3dGZW1xF0y37El4eZClpFXr -Aid7BkB1gHdiT560A+XK7XyVHCDenKLZ3/Hgm6cIJjn05Fs3OKGI6zlCUJaKf5IzpeF1tVjXtB4S -DZ+fEvq4D9cBF/YKnOaSYks6EH6XZewuYzuUDTwZsnyZyE8+WbvZC/SiY7tArWrMO0D3Ouhcak2S -c3BILqRkyRmLZDXw3K7qaCNgbW/gkuV+A0UidnoiCFjF2Af3OMcrb3p5+wkRvtH5lw== - - - fZlN3WvXKMSV+NijfbFt5seWHfjvsLu4zl65IVWnjNMEudhG7snEzdMuMHDPoYy8OuffTWvmsBYw -g+PkoDV4O/nIWzx1c4hcYfX98+OccQATcbEXYc9tcweedtDyaQRzrIK342mE5xAxjn6bdvSI/gm+ -AKQ3iKX7IVPtBSd7os6SB+cJFpel+G6NBgXJlIThbUGp9eQVaa8BxI+1Yea7tZ/4aTQpryZNF4Me -Cs9raf0gfx+6GKW/YuMwwo75MHp3mpxcgQnshXLqmwjE7t172wWt/Doe6okiUybGNJpjKKU5w3Bt -ygy10XiCAH3boo4BJupDGhWywyMSl2yGa+gIQBhrRnBC71Rytg4jRHlwfaULJ+UzLybsDUpIP8Tj -PchhrUjz9MKsm0ddtEMKPfRY5HUYEmE74NSMcfxsQgC0d4ThuyNgpXekoAxYPDaRKxloKic136fu -XeY62Ykt01q81ha+2GZn4Jg7dhzTqtcWvDtvM5W0Fjp2Kl36a5klPjTnyNLq0EFTFmK8yzZlfexI -2qUEaq/0Y6/1HNiLA9HkoeTXKze5j9DR1arx50FLn6OTE6W6GAcIaPN33QFVlAxfwwDeCyJZSnDo -DaY6jiVvOICLHSn9OunRXVkb8/UpvNCj28iOB9kuVcF+xbNx9uiDcvtX5M9md1i1pgMsHy3NkJ3F -SPfc+0jJxKSq+Bghavo4yBPBdRCgC13zYMo1bTMrkXzyhayU9WOPZYywE9DO2KGRE0c2UFmg1+dN -4ivGZ5swUiCeTDXjjGO4ugnM7itFoN0bum0T96pwz/LDy/PsffPAEf+WS2sDoC++XqKn9oyouN14 -LBXM4VFsU3bw6Qequkp8vctk0vW8mGAfd3ER+S283q8oyc1ewi0c+D4Ogw/XQpe+2gF39/lD1SqQ -H5xoRz0eqEwyUEmPqq8VFzTkANvkzJRtfaE32Hu0NPU68brihScMhow8QUPbuDL3Zq7HXDTp9e2M -R4KzpHcSwFoAxwZelBUWw0jZGphSQR1fjBupk6SwIioJrOhikUjswYk5BrkLx8DxD1EzTBygMsHB -z64DWOnugNs6KPnzw23kE15kGZCDdMPhUtg2jmjEOCcaVrRzFeFBaoj1jh75OacekL2/0OlLTZWN -iAEHBoeGRMGS8IKSfE2euRnUOZU16SfQzFhH+R6GLcphk2e7OnbfYVTnZlxeB1yxCR2WaJ0l3Eem -GqJ81M5KJB36fpYvO2Hc2HHTqb+kS1oYcgher+sHqDtZap6v7Qc9udu3Hg5wfNudQDT0y6L9IlFh -v9zEniLGTnlXCQIxhQsSsT1ZKutaC9/+HBMpTOHSHhZrmBG4Gd0l3LYP9pCkmDwoNbYTg0wlP/QI -yojGCte0qpyP0Fjv88A4EQhPfCc7mXHKTwvUyVCCw2ZaoZN2FwpCYtGMUTjyTiilREE+R9FCbxgA -C9TXHScShWSgQeQmfeFO5pkH29tBw/sWI1zuwRtqCzi4o9D1pQSD2b15UC9mECbP1AizEA284209 -/ENkHF1KV7g8GqJMUG4KZYdhSmUlhAxRerWLRQ0ZwWVRFoVsh+BakQpbERDICBf5E1I0RUnDmxvs -o70Vr3qlg/aiRzSlsro0VeMhH5BVXACdof3BEVyQLD15+TkHLbCh9UuqY0xtfX4MFOhLQbXMy9g5 -9srCuXITByiRevgYOtMO1xP0YvRi9Hni4Sp0r0PcS55uUM6D2q0S3nCeaNwqqfMlYzJ3fCYogG6W -VgYh9Vq2JhcNNvid+2Jlld6CgriLI+tKqgAqgaWpJa89FXxlPXd4u5pO43punuYIJJCwTRXUWzc0 -m4TAZzBLwiLsiMN1s2o8wknfTGQqzxLeUYKUiv2wsgemnZGAMA1yfyMBCBPV0wBjwY5GezE2pP2x -KZHdA9AW3gNClW19l2Y8fgpv7/xQm3XWaMS5E2nSpDviGW0ZNGrsDjASNrCOZRzeqpB+8Zy6ibRw -oD1yh0KqMHFdOk93gP5sd1Q/FL/uI69UydTTMujlKlRDyHDIth393CsFyBiF9WirVWK/jtTk7uBI -LBsiD5q7sSsRx+2s8yQsgBXfGURhhcqwsR6yYRuONntKFY6d/NhJ8r1UmwRp4gZlx35Cno6dSqHd -y/GquIbQ5eLM2Ex6uH/NEQC/Xqx6itEB0cscXX/iDU6kekMxh4bTU2/znW+OlQOvsikhH7JdqC6O -xezItPIlXz2ACdFNNwIhN9n/LT+1wEtfow9dLuaGD9FOkiOLg+HxsZA/ehA58tkyFgid1Ou4eDpA -egetwZCMHLSsEpx5bPD+qp7VaRwBIdStYEH0djw5xhLX/SaAZbJcFRJ26vbw64+bSDV7+UxJ8c/1 -tpMYADI/8vW7+tS0Pnwf4bCIOYPu8jL3NIyv9bvx/XrENbLmp0h2eu56kMRDSCkXfDo0gA3q3W4X -iuYIEVQAbjOVCMwdf/ibs6JZQoZtodpbmGGZVr+guKdrP8rDdDJFEVhkXUdph8x4RnGvMgEZxX4x -AoA8orZppLDyKEKa05tU98jcEyIp6QI3LpZtFyfBBqiuD8ZAT+w9s7MGOs2XMCNQYJPE+CJrya8v -9dhP9jppbt9FNKnXqSF0wQiNm3Vme+7g4dSwmBPuB0E4ErOPK1GEXZn3Tu7O88OTe9ccTHFPoFLn -REe3HiLE6s3B7dejPn/JMyEjPFM6vaBg/i/e8SQMflvS/w0DJLiFZ6TEOBEhoul9DvT6qRvOZK28 -oQYvESTWc4AJSYwOH01PwTUu0/MlYEwa9d9hh6idV4BoD+gap9eIZpRBoHV/HMGznm66gS0SBSP/ -hh0AGGHxYwkkLEGA/9PiWtzERSSX6DIgrWXFWe+zGIMDeNYEDDzO8CXG8TlYeizwyMpE15DuEsR2 -UKed3NEahmHjaIztIrkr4y5gYryXXoSUK6J1hCjC6VwQ71P+R691bcaZNTUHNcFU3jK+1QJaeNlO -esSY9xek0xhhHuPKsUtXgRMTWca0wGUbJ7wRlcmQfJWFhL2r2Bl2CBSMTbjApwTmRAiHiXKACuiI -AOzpe1Bm6oM4MPjPhG+6R4UN94EKZGA07Y6RtpJKCQduhHWQVPeCt0SdKbBYB4olQBli9jWU2F3J -payhEgd1nl1tZwpngTq5CvvlCM6LpcZFRd0DaatEnfc8wt7pg3DpZwUUDshPqziKX9zjq0bA1246 -fm+EfD4L/azhqWr84EZPh29r6OMqhBuys9C061VIUcCdMRU3rqg1hL/NuBDy2ZKERer/OBWJqL4D -+xr8NbPTv9v4OOYAa3TqX5Ct19p45bcDMDMH66FpAhN0avoWLOc3Jw/iJ33VOZPo7mANcE70AecH -u3gAhFrxXKlpr6zYnBYR9WkWK7f9Oen4lOBwQyouqLpmVBoO2qPk2t4Z9CwMUDufTuCZpkRIgHNA -kmAGquCQBHx6+G4/tVeaAj6lYw6BXYxsYlC/zc2c07FWZo4QFSoFI/gIHlnfcMIO1e1uTsLMkMkM -bNmUBCpWdXfI/ZQSHQu6aYRLtGAkE1Zh6f/QSViVbu6TJPZqRLBc9t2vThSX1NMst7MGkb83SzOt -nrrw9Gi0D20Nyh8UwxTTviusFUMjqrxkjl6DMez9hXG//LVHU5eNI2uA6lB6I/xlrqcKKWO0mJZ8 -k5ZkWOTm2ZT4E+Pki0ipi9WpInyN+V+Mk2gZxx2Z0XkS5QuJetZaYNKXF+QV9yUlK2TDfE2sacKP -9i1EwWNNJhaPxSbvGBjggUPAqvwa1aFdB2stsBmhdMuZxeZQHKi8NiGsh6HbOsTuHUMV+QibkJHD -DMM6LINfBv0y7EUTXu95BACwLokcZARf8Jeg1iVl8omWmKhpycULn69vvusSmXHYebhOaq1Oy2rJ -gm14ZF47ehjRCyMV9+GjwtO7iYJE7M5ktQ/FAxdZA7S4fmn0fpGTarHrUkry8vDaJYFGj/cL7gJO -ehk1kvO7EqMaIIUtsjMofwPBuAW5AOMdrGjvaFW+hBDtnoASAErvwfaoay6SjzDAzyo/15jGl0hy -YxFb8lA8RA/ELmq8gEn5n/PhAI1t62pzZ9nRY3OtQ1qMi11w1wqfYtwMuK9R2PnIm9ln/0UfgTCQ -Tlt0nK4I5tRdB7gEoAN1MjEvcHFuF+e1UYPaSuGb2AvB6ShG7mWemtWEfnPWg4BxAmil5pAE1Vtz -BqhCdMHelBgodvziIRSnTUhAQ3GgnWW2ZMj6psSq2J2qUXsHNrgXfBWLsYHSoQfIWjfY5bdi9Zt9 -kGI71QqgZvSt4dQMit1BbV4I/MN5bz/mmQT5JXOKxBgg7R26JMV8rHfYUR6/pD3UB8Ee5OsvOVow -b84Jqo8EWgdkgTZhjRTN3iq0OtwW/EtbJlTxJIsfjqcgryOEHs48J+wgltcUjo7wdU4lHWOxGogZ -QbBeQJxwSHWmk2EJUQau+CZVt8qejlCiHLS/A0Qhwi7O4leYlFGuoIZvJJK5J5QgijWN+MieKrWR -bcUoUU5z1wF1PuHg2t2JVBK7rxKi9o5PSlfMocqwPD73t8ToSowlg73B62kP01uEzoLHJkZnqT3B -qFhydVvJcP3HPBQUTjdn4yrGJuAzcImDqto+/HNIvFbiepX4sn5+BhtdnbIsws0Wu3NgFmShhMnP -+TJdgtKM6nHHCvYBQrfIlcvEeEngW8i3fgs62YQRp9BJviFnXsG2IkZv75EPa9jxeoJU2tMqPoeg -9vGcpRm91ClGb+EQIx9DquWdm1QOUfg7l0RclRKH55KitOW2k4cdJFDezvMOOx58z1ksSJ3qts5L -wV8GWTr5NWsnENkj59zWRw56rRQyiVYLZSxBBioXL3J8uSOpRKYFbFEBlTg78buhkqFky1S7C2NI -n6V4WNkLm4+gocl72DmI92IfnpBnsNyl0ikD9GeyVH7Fap3x1LBYBhhNVN/KghuobelvpaoFWJvs -uRlwUcS2JojgUMmQb6CCjyu1PxylwgL9nQNXhIeJcoIIxpSciex5NfZIh6+rwFbBJusVTX2bBoRS -MT58NYlSRc80vCCkqmSVdjALouanpCAnv0o/ay75Qb3u6ufHhSQlMBN6qICbLekIohHd35r/3AGg -83S+SsCT3UjXVwMKEhEOHo6bXxSIYeTEdN2bkYifzN/i3XmhR/1DfHvoI98SimFRqJLFZ79Tydns -cwo393RS74nRPYphhWwfoSFcVruVkrfwd5GF0FO4u+aPP72L/OqRRn4Mm6RklwWq8lvmdMqXEzQA -8nOLCoR+AOyKIzd9JGKkCusJSScJGCYWhb64d9obFPj8YNiFJFqNYjQ7iDxbLgM/wpPFPQZKu5vw -Efns3RtYoVDTMgRBR4Dm2/YS1lLnFiveQ9WLHiaV5GX2VAYOQTuvFDyM031iSi6uC3qZ4zxCHKB2 -MGMi+yIDFPBtnjCCBXnk3mQZ2Zf8sB6md4SPsdOCD38x2aMSt+Zqr3DKe2qbWJuSVw== - - - 3aBcZoypDacGlCu56yRO9bXMT7MpOMRoLRT19el4YLyA61R95JykAEtjZ6uaGF3kqRMeJlkHp+73 -bfZXZCm6pa1VmNOf5bRQ8vnZhLxcN18pkj0XK8jORzW1iRtmagqKHd1OCv59X3EwXaagOur08vH5 -XTDnJeeO15xXo/TpE/vjaob8tPNcIs132LFbT35/lWKKw7iqzag0psfEbJj2lYvdoxhMXRDfpraL -UeNLJ+OsmLFaYotaFX1fSpfpAzTLxNioraWEIHhdO8t78mv8SLz37WFz7LEuzEhOVYZYwgpqLuoq -iSAXqf8V+rR2s0xqgmdyMDt8AX2RW3CFE8mrGmmEvp1Ivh+yWg62S0t2+NKbcH9+bsYq3UC0kaAu -OFjUnX2HHaSLnY38kvouEPRRv9OMs4NaNhe9FzXnOhthJ0H2SZxzLnKXukwqRwATc/uFRd1rxS89 -HO2Rz5lZYWNzkboACULRjjkH8MiqdlRR6YU8VLf2UdYmwJE+YrqThI/DGhHsykPB6Z0K/44GlYUt -Qfg7xlWXw+wjF4Wnq6H0gDt0KlF3Ch5KJcdPm/FL6GxIsdiyh0qY7Nd21E7l6zE/RQbtoIBN612u -LViuOLdnsJuDhlls0P0ZmexOy9KVntkBYmNsHPDLFVWlpLvh6Gi2NcrSPK4APnwYY5N2Imc1xgK8 -aYD41ntUpYsrInRSkMlPlcmjNWpSNSkM8+KCJtp0jM7Qru52MrKqdyolgX1hFququC+BQSGM8uTl -y7Ud3r824bLcOE847yyuTwofT2dHlzqx1fpk5ARnKcDc6RLaKC2G68DX/pVw+D8gxIJARLcm6w/a -4905gkuMLnIW7+6QH0cf3ErBm5excmzA6pgFIl/O4FedXP32/+k4IhjoTrCfQB+wO9hEchCebxYs -/+pMYdiS7gd0G6fm1KnYK0hq0Vs4RKBrj+dNd2Rt7meFQyQ8RBEGApJjkq0aGrMVFd/n0EPQ4c58 -3+0m3nnTFd8zyBAVqgytcsJdKxOJNZOuydQ82KkGRfKbc6X4ylhJfsupw+V8TJ0On577Oz6aflIw -Vx+h/wfMe4cMNNszwu9G9asH/W1/6jk6FMDpxJb2Q2WeTpFjaeRYcEODFUgbRxD3gYJZr8UXBl30 -l3uwJVHw1R1UkOTIEpeA9zgNcqxxywlBSapBOzS9k5tJdaZzD8unkd+fvvehQGLkfuR9uGb5ojRk -47cmlBkLNshxLgb7zabwhgEiVPGqmtquu/iUAV7c8pp5bxwAoj6N1RWZLs92dDK93gM3nLEYpjp/ -N28Pmu+NCfHhmklm9DLEmBQcarmJXS5mAKEbCTGyZVe/neu90zINvoXjH8rr3GLanWpzw5o7P2DH -JjxCJZU9WBojAcsP6a3+qSMlpOjZvtJRWpKv2zUqVaGXLleQfYmdK4wyth09tDIFr3CIkb5gYdwk -U/MNIJaodMr0/XIPj90jL3AOAGWS8M+keYEHJ8TtG7O23aBdHKBfOEHeBzIa9eu8fc3vdcLpC8Zy -fQ2GmTKvzUwO/FITofnPb/F51ZdDp6lZCegD9kgqgAj3mHSCfeOguD8UV2xPEgSRC23Wxee/xsQI -TrCySUPvSQIfgSSrpwXp56J0W/gLZVHWsFue2EeYdJh7pgUQhtCK8Mld1aRr1NmeLkZPgrfsXMvU -Ki5GgCziTCR7dyzo6+N9e9r6amXJpVrI9AE74oJmRe932OGrN0sKmxERtBNxfjc9MpdaqLZL2NRF -UMyfVSUfuGiBmQWbnIiRNZRsdtA510hUtsxaUCNX2UJq4VJzsP0CIlaSkalwQRB03xTug6SsSKan -+qjAfIjRpeCbxYO/YgSEeI2sDDIFRiDgPaiF6vWP958Yty+1uHpwvF5qPfegcb9gr9UdiK2k8iAb -RC9SiVJ+kUoUYDsXY0XuBWvssiAqJYSQX4ik2aDnphdfVCycfLkW8NKo9M1MM1tIJoPYNz2IYSgW -M7YD/dzx9OUs10v3Rlr/nDb2PHgHIjlHoRz9mzd8vRCRTj9XwWEt0Zi72Hrl8itLpN91ZJ4pXiaS -xcLDBzJRVepx/Ys3VNGVdxo7g2tFd4AM6029taGtSl9F8DA2KnF1gwiSlBY30pharaQ51+Ssc6CS -8kmujNhNRBZjJ3Rfvo6UfkQXYGVvl2xAoVXJ5qrkj8oUNk5LbwdSG6P7SEbXDnoozdxtzAByv6EF -I7y4B2voxsGm00WVSFvN32HvDO+ntyIIia4hUFXj2GL+Oim7MXMyWfhdPcyYBgE0IzL4MziKJ1Vj -xyeGWH5n5BmvM+k/r8s0YZ1Mow49JOLyBdcBHMyi/cmVDHbVlep+lUgBZanFK+IqIjWefBAbn99d -2KZ58tTwESr5H2rn11AtCftfcDHSLxQwEaMTB+gIDevt0zlEPzDJWxfbOD5o5+BKZ+1MSRctzqeS -cqEdykiX4OCKumaxkqTdY2NvksRh4KrZTDgW3rgYvVRZrXGDIziJroSCENbd5My81nf83VnLUD1P -yaS2AaGSud0OSkHXdACIwIzOciURZrTxCUee74zFyO6+O1UbtF88IhEexYnbVWfRB+gmW+M3N3Ax -9HIKK+2to2vglKyg1DTjYJeOAjpIR5zpk5n8+wHITg1omPBfDnwqw2mHWwPzu1aQDqYVsUwLYt/W -KfHuTetkmZwHUdhw0IJOYzGZ2jDfDj2jk7gKX+wvRgmm/8u/9OJsLPOrn6uOp2jseNKbRvIXG34L -8UavmfkIekv+c8h+iXEj7kQoqcbjxtRNK/SgvoYqD6kWgvDVMmlirKBCOZ5P9BGi/O3vmQTA4SsW -NsO2yqpTzTum8twC1Ka9dGFv2IaQXW6FImmV7lMrlIysuZ4lDMWr0r314116OPjJeguFKFd3uLGJ -OqCelBzSshDZl6E3RyUmJeb2clJlAS7JS9cnBeQFVQDxfENvrkY2r1krsRuNEETf9gF5OsKO9kv0 -AFQqC4jfWhqo2p0mB5GHGXE2l8TYu0E7c1yP1O/XJUujkF4Pc/fCYHtjAKT6G9V1xHiRfFneq65a -35hVwmHJ2yjQATsSlvp7Y/aFrPKSw0TkpFJKPsIlVq6Tu1aJsBkuelWuUqxJz8pEOwwnMBInSpmN -pYRm82BklhfGLrjH1w6h+PbEj1h5/kVBthWW+6JsKozXBWGoFpB+5fJyhcoBIGFj64zWFn0frlzh -w4q0nFqqoQ2scWimDhL8i7EC/DACJaw01sg2AOjZPP47TA3YfoJvdOTKRiOBihbhKsXuA9o0BoaF -Jlw3aFvMYaICPU/BXUSikpTOjZJgVsjkFFK4JN95sDczca9RA+1OvHcGozm5j4WCKIgRxdgh+Jh5 -zRvlSYdxLHFkb6iKAnDwzqowoe9zg7nTnr7VNlPtBvldoUF2l3zYyWRXOjXDGZndTnhyPTwPX1iI -dr3GPyglLRyuDQUwfU4c4RpB2HGOLPNlGuV9kQQRo0dMM3fot8XKsLSQt0ShiZzbIk+9uE7XXKfg -rGnLKDPUqDmgX+k6eY5CgOHwvVzQ4qxYhgvsGxBm5s1BR3HRzZrmrdgA3tSt3MQQRU6ozDb5a9NS -wP7QK8SE6VVO5rlWTvC1SUDjYjVCf842qURvvAgXfVJ2bovB0TL4+jvt/ig32/baYsJokUmrySnU -XC48ERgIyWmHzDVabdtKRvR2iZFKxzWUXcR1d4m/ZekevvsQQcZWt7mLT6Ii5SPpEBpOjSVtU+xw -WiOiGy8uhoSycMA6vGNm9QodASgE7nUnGeFaHuOYsgUnWWQf4HCfGVR2kRuz7iVVH2YA06gJmzLc -YmcGhTTY4lcUz+CApXmxymYyxfHmDy7NT3cZ6PEQy+RP0m+M0dYi8nY8cZRvqxQfQu44rtMEac34 -xhcFtwJEZ40iW+JrlJ3uGBW1xoKTcOYySXbAbxylyZ5pgoI4F4k60qH2KGU5UuVhhAZ4I59KD6Xu -av2GbxgBsJZKaQihLqWMJzbQ11A3ouBOhftm2RCPgqVkCnywllqClI4lPH0KtEe1GnBXYdfbKPCj -9inMs4S3pJBKaPCYe56rBJdfZMAPiEIX9Y0zj2ZPysnw9/pAJ11KwwkjIr2UVGpVpkTAVk4j5SXy -9YRaCFuku6EjSyR3IcUEOsUXQydhi7pPPug2zqhjMKFg1V1QEkEVyp/vptBtMIL0zezGCNLMQ6Dd -sPpkVGQ3MGZoiBeCPy5/QJv6tsYmv+GVquzIKLmA8Dvs4XugT6WTMTwVvsTIj16T97/i7g6rScv7 -gvvmp9wZ2vSTkpcpwpJHUZniphSs2jvyuK7lrkYPmhrVB8R4gWPVQ5sjA3geYUzfVGBuZLqRd7SB -9c8fxWautMea3MnxLKCpXMSJ9dx01he6i0+nmufTCOQAZXuQ+oEtjXALSo6QshHOx0Z0tufRhGG1 -E6MenrKyRgIqg7KujLArUfkVw+IbbhaAc4SxsdegRNMnVJ002eC3NtMIGrX6CJNBd6PuRM/obORc -hBPzRMwS9Mkzlch0H7JlD2y1BzjYI4jxjSKtMA9vXoyHxiZiPSd4a1GETs0QcheMgdEFL/uU48wi -pOuDKeNmDJOcQ3esffr8Rnr1+KBkz0O4rIuXWyVazqJ81B1BeCy47xjVtYZOy21zSvTaUIcLuUrl -ekWOhh9aBzHUsxE1Pxey5shIy9fgOO2px8dpNYX99TAn8kRPHYgA1OyEQvagYkni9pfT77mKPAYb -ZLc5Jh+0By+7OyxqDMK7MFLMLL0AvRjs9r13DlA56gSxXXAxpKbB16kZ+ORvfy//WWWpTaBCclac -udhd31icBmhYb2vnMqPRKao09kQLI7M6ah7ItXlzgkp8F/ab2RP/YgrwOnQQZsKF4Z2Tg/NcjaIZ -8+ietxpWBZYZuyiRGNksqLfnwOJqx9371z/4zqmwwVyOS4m/PmDHdz/ADCzGxtSC4s/EuBLsOXR4 -xb69ZXUi8JURUOwZ+W7QjzATza6McCfiA8+26bATDo2S25kRvR2D9Xizu0CuzM3aWeTnJhElZ9P4 -6Tm8pUeEztdpJHV8RAAJTztl32l3kO+0FBvtCB0dHvz+g/GJTyuMNkEC9UH7Bu0q3E2BSxTwVF/H -64nwa4NSS2o2KovCv5udrYKLCGm+BjnWT1Og8yy9SRPCE/r0HHIZug+mJeqtagVIN6VmMm5saXOu -YJvdaLR5GZi/WSeFbK+BaRxZIXkU0Idsp82oI/GagC1FqrET7CNZ+G9Yql5vHv2lVUSsIH9dPLtV -Q/noiZT/dW7vadoO5wTLF6cdEhvQfFcdSzBIVk/KSZGzQ8lC9T74o6WRLfKy1grplp1LuyB33lbw -evvB3GLa1QjFjmYUArIqeBXfRpfV3IhjcfFuacBlsPc6TPzCIfvNthzAB+we2W93/kJusYHdPDHd -v46TfwKUnsvW6V/9iR/ZP42Tn5Nzex5fzHxO3tR9nN6GDwRxtWq0hvnTMPELg5vHsA== - - - h8kPCFnSYXEG6/Jw2wc7lirVHhSijxr1p4G51iTxPUENql0QH0j5gv58WQ8+U+qO4tCvjugmGaeS -OTroeF/Hj9vdbHNYphn1ATsaBxwcakbgwUIbTNRLvZr1RFj/oxFcIu0El//rHOLJiBNGUKJ0kvDJ -oAk0Okxa5RPuFLAV40Fgpm4c0+muw6xlCdQPzkYzFlgVX6fwL5ndYntDmp0zyJ7odHwtjkSyv7Cm -NVidWBsoG2aXfzK9DtYxzYBMLPnWQQt8vMviPa4H3A87nIoiIo3iIGIpNF/0sGgGj1VevEindXbj -4KsqrJj7aal5sJ4r7heAdd53Z7dWi9zhS606RmBJGEkC/TlWA73zpwUQ17nROAIQ9tGSLSCDgW+r -QAHv5QFn1MciIb3W44j6KMEb6/hr0REiEyFSUu0CnKw0o1meCBwcFLNol6LQM7TULt2UJ7mGh31b -dnVPSmeoIhyUJBzS+XoP8Wm9jP0BOxi4Jht4WiidUmBCZenAfttCYUauPeBRZfHjIhGaeHW/vD2u -SyDJtvVSxa7hb3w/iZZVpu63LYH3H4wTDQ01EU2mpf+we3Zxz+hoCOrbCXdPsq3Ue4m32yuZ0Ubk -uNgHKR3ijv16ncGv6e30iqyRxuV8Oy4Hp3h+CPkFUrYvKrOhL6NlCKNcvAkydddN3/kl9olf76cp -xNJo2aeWvBBTwZ7FzVozzZhf7Ei7Bfph2+rwzyqBPau9FnD7N2SSE73m6xye8FoFzQKyKrlwY7+R -QJporcNNJKn/vYzCL0eeoQOxtm00H7ADYEpmedFjHDhDAYOQARy05XWOt5jK8LBno651iN9Nmr+X -TWPLEmRvn1avHOVRbxiGbdmhIv1yD3ln+DQ2vz1Q0CySRsn9sXTHqvhlcWKZejRn52ppBypROmiD -qVHc8vXu/gEZscFDP2PxZuqhBqWzGBuAo/40fpLuQ6NVtNSL8aCwfn21vE7hLe0puHGPUbinIAHr -bhfV+bA9LcMR2Zc9HXK/rHXBjd5Wv7MwTG9sIqaqfad69NnsI5Wf8qzWfqoNvUz5qeWrge5WCyLs -hwoBSwQQfr0VKUX9JsrhKveDzdPVGS8FGyabGF5/Lx5sMNUNu+jj5z8pRHmFQjwT2iWhwkPRwpeR -40cd/3GspDR4/8PrjT0UjXryO0FQI+lJEtHMYCaVHCcZM4ZzLLzmX7+cASc3glBvWQ38A/ZuReHt -Vfd3Xr9B75uQb6/j8P1LNw61ftIJqZ1KfGQR1IxOfbuZqTRVSwQqAppTfP/B+O/p7pwD/zivLBtA -gS08qK6L9MvEtaNSkcbSGWcnul55OK36pT2pnQgPcPXFARxjapwU+RHqzIDLZRsg9O0HE+a9zKBv -XNb4+AF7iBihUDc3FUqntWK4kUp4LdDcYqfgB5vZDyCrSWxNaAUMLaMBOZ01YaitFNe6LNImUt3F -SokOgryKP3wlMYAEepKxnpvy1ttkTM2YGMdnIhAAY73WyN8wBYeLbq8SmdE757bDXWxYL+uAjZ1z -KKEK7bxTc1HuYFm1/LuxDU/I3KfUnzAeUMEAlBTCWOCiI5PFD6G8J5X5M7u9VxvVA+9+bWgotRgV -AkbTsuUcYNFzRoZrBrXkhKchhAlUHq99JSIF6D7MLEY/CfxRAbPKqVlWSpd2A3XzLthkk+itPJ0K -cULUiecCun8PRkLyOfmHOkwZmyM47Y4eAx1fu7sEqlQ2sS5ir1C3+w2LCHLp9iTeueYYeknrQZip -2FFcP3Wy11hPAF9yoseM29AMFn9vFEwOEOSZTxty8R9gHmUKceAK7zHlHxUi+077gU4zcOmrwNfY -LizvxoVgMzFSrUIVAyfbdiOlRxDOqBFfq9YVfYSF0qKHNCTsfXymPuXuVU8h2SmIkxztl6iEPTHL -cUFYuohWWZM5sEnVUWHjaVieqeteCHkoNTA9+7sm07SLXsRa1FpZdh5xDohLF9Xy1qS+5rLKoht9 -N3hSqlgTlAqSV75k+nEIhNya4yLWSDoeg3hBMS/KM3jjiTA9O627w6DNiC1tW5MhR3Cmgu1eHKmC -IPK9KDAo1NA1CPk3CIRKw1Z3o1N8sX8B9KfkCloH1PGuarFCWThksVY1mgmbcnSiiL1BRV3riPze -kaVfbAdYzgxpg7QOrnFvYlfVpHgfBZ3F22XtfZVfkvJvI+Iwo4MfVbypYuHi7FEM2Rv+Hqsg3si8 -SWqKzBuXwhhamItNwmMMzbyTiFE2sQ7lJiwD2cFWweHu8d2rz0DH7/W8/OBeSiFA5NjktKMKFZpl -HsbQETw7bcZ9Uc3Q4Qo6asu/9qMj+x+wyqDAYJmij7+ysSxuAc35YYXdm5q3oCF5GTeex6BMup9T -H7DjKJts3RHunkpXtA0oHjinoiZ3uGOIZMGtPDmzNkEx6NaerL4LsRC1QWb4Fd4Bp9pTPNNfphzO -YgjfDG1Q+KDZvfpQc30YBx0RIJZ+4gb88GYuJUu9m0/m7b7BYOPUy9Te0qR9X5Pq82GqVCboZHLT -PNhwPdhHqzv/+w/GieeyIVN6XPTpgy6wEyE4Nbf7mUCqOYW0UGU1wNdWwHjF2ZjoKt5waqndJjXy -gwE+TeAtzQ0pBd9nY27X0monb9ZiXwhiWvDiv47D29/FPsxDcMgH7awjgbJeGA6pyEhKabazizER -eTMrpvYBCve6C0e4GOHTHPKqn3fwYFtc9hupDD0cO7j1QrvQAS3mYUDQNzoeVujE+UnNjQNKE/OX -oK9dG8UYMcuDoR08zYsl/LVB3CpGr/yuTfG1ZSSwPpGTKr8gFxUVgUXdUy+3iOQAc+6JN1XI9jc8 -VV2ipGG8F7eSZP6WV9HN7g3zwgZJl9vRUWKjcBlSed+NNLQiclD8r9NqdjCya4bAEBGPiyHPsozb -z4ypUty4aIQ0lJpUBSynJWlljWox9MPo2C89CYN2tFATIYI7GcGi873I4rCdxN1+a7E3QL7bA4dQ -UXBBGkoJcm3loB1CedMwF/7sKQYP9ut1kmKmOuh8gXjbInVorr+8pQsfC21QulLwZaUOyF3QF7IX -AaKLKY29WS/5cq35hF0zerMuuTbV8zaVDF+XZAajLMbsqjXwQfuE6GUNukixU2UIkYkYrbNCB6lA -o3waOf8oYoid603iOG18IXNmSnvHRyuw5ebJ/GCSIIVPH8+hPsVir5MAXBo1VQO++zrD2H4X8I7y -Sch5w8kP6hb7VqYM+0QBOZx5b1LQ74T80rt3TM+mUqNmTCAQC2E/5SaGqlOqGexNubVNURm5ZEFs -Cp0s6uJigbYAQb7eXNx3Bw2qLDJpWviAHVkqj6W4tTgHSsJ9fDlO/olPQ/EnsDSf338HPdReIEUV -m1dflwkCmBEiMU8h1Jdz97mMhL5Q6ZMP2L3BPeXZVOsSF3cwJ09QC4gxIr/XkfOPQulnZedJ7PTn -tXj4/uOhzI5AbZFnYw8qgS4ygb7+4lt6G5+eOifjHUF7hdDLSCcUgk75xYGTPIKLH97NV6/5HxDe -oRVPhXfhUxRAvs7OTqXYK5rCFGpOe3Nc/zac9vvXw8cPN8gKCmQr/DmhzXWcjB9FbnR+rNgcJWge -QCdpUv2NI7i0Q8hfrwaZmRNHyOscwtkaVhl9/CdnfP6AHcjyy9KucAibBtO5ZGSUPIS3QNxEkCNm -b3M8pP9fndXlAAZKZsEqKUL4HxlXsS+MoGS+PoL3Yh2UqBY16s7h7WG60+1J4gqsYofpNbmybHeu -eyJjbqmwiThfjN7FE9rPP3xJnbQlsSfLszG9m5OO7sdSvOguTHKtS0PUa3NDP4pkj5xq92pKEjYN -pEWBopUkH+Y3cV2d19+7fTwP4wDr8svHEN8JqaJvsTjog/fX1F+8hTR4qi3WJi6+nY/tuDHxDi/W -3mV6HMALATJAvPpPU4jZbYyhHcAd25442gRZ4cgWqSySOCONsU7iNa/R17s2aQp67jcQN2mTitdB -vzINUkJKe+q3L+eWp832pb0jzPnRL6LpvLPZde3U1YYOh0/DPj0lqgFoAZi/GJi4GQENPv3ggf3y -Xty+wJILEkM1gn5phrDapzm8Jb+ohrMt7EUftLPmALSWHEueOl/MZe+FBvPtysZ0SpwIKh84ixWz -CMJe58DpCUae8pYa5X7AHr5r4q/SLAU6KNDeLKmIBaQ3xGVeBo7fPIlCvPMTOUFSOijZdU4mfPZO -HjFuAvSDifP5YmdK0WHJquZJrpcp/Jon9zy2z+5aGGdZEI9BVDYGBOeId07QvjmB2RtGWJFfqcQz -n5tZHupImjTYfIVGa0JqBhw60VT55fN43M7vf/c3/+rv/t35H//2tz/9/T/+5f/+r//0x3/6pz// -429u/fs//8///duz/V///rff/vjx5z99U/O3h/1b+ze/K9/+Trp4/vDPv/ubv8g/1G9F//eH/yf/ -9u8f//R/HrZ//ja+/Ydv/+2/l29/evzFH/6z3oPOFPDiJXdkgSpuPpkEXSh4p/f4wy9M9oe/yQ// -x8f/SZLtscsL6dwZjy9c2efm4ysRQaMqsAef6h/xa6eQyg6+uk5ioj/a25nWcdk3e7eWB83THb+A -aX/pG8TzR3vLCQmMYXmxryaARbFuQ+FXltg8bJ1p1GrLuQ+xP/xS9HlIqvT9B+NEd450NYD0T2/4 -A/Y+L6DJlt9aV7CgFYlG2+fE2HvxER7foh/GYn+cYc40oZmrd9jLXj5Ij94VoXSF2Re1zOIR2nua -8lhC7YspxwObpFx8nG1l8W4EKdDR/25JfmndKRsdwt0QvnIlROxGQtipfeIlsTFJjNQ9MCdWjRv8 -qhT8EvNq2MmckkaNxNBbR+EX95Bv73lo3l3v2IYUMPV8yx2p75/M7dkcn4dUoEEjokfHB+ygARzm -n73DPgbo09n5tIiV6t5aqAM3eBUqCv+GAe7Eucs3rpUZ0BLgOb1MLZ7TRo1VlplIsnHWse0H3NUa -zDC+d97qTAr5c4u/3JeR39KjGof9EHutv/6jj02aW7y/NTF2XwVO3aZ3utF2H+w+enEBOc/pfNiL -lDSLj+rT1PKsm/+g5ew56UdQiHNmlIYvk0eKt1fplRfFCgZF+kkdHLuOJdfPfVP7w7KlrzN4S3vT -4786yFFPUu5NaNV3eb932NsAc4rWXN5jLwPFibsAYtwVBC51wfbpB2MuJ4HSNLJjR+WueLSPxYqO -yknSVqdWV+PCpzNrZQfhQen+uPIGOypBajdQBNBpkCzIYQlfzC1P+9OPctroAxrmv/NHH7t+Td+3 -G8nwsPbdP7ydWH4TOjnzzh0f1bBUvn7cm3eqNXm0M5aYzMs40bZYEo3pudExKWxgYC+YZA0sonRF -gY7dk/3TMPkXHh44HqqksT5g36SHhVZxqVRaFeoqS17KCAeyOuqzmM/3uPjh+6Kl35LnRWgSg38A -tmhAOYGnkGtbaJIZAEQGrZCdg+LW6z3Q3y6V/8k/pA/YzyADjCdrSk0cD1C3+sn0Hg== - - - DxjP1KmQWfnQiI5T/jSF+IKkrSOoVXeP1zsH91LsyYKFpt8OV0U8LfCEtBX9uhcgXB25co8QbreJ -3f76ahNNVwyyLJr6Ym552mti71VH8eOv/ejjqMdQBn8V460dXG5WIVAju5rX2kbrYdeCOcUZknTU -RjK4GPXT1PKs43TQdAVnDfIkylrq3Zc4zfaPHwkuBu2Q88X4sD49rXJ9OYe8m0D4c0Zbguw/QI4K -idht2AldKm8Cay/GVnAsRCnM+t6vby8OmNOL7wWN1NrRvp+n8Gt6dr2ijq6bdOyvfZClqtbYX/HN -z2juFwlv/KYDAvXKsvyb0Vjw7Qe/+J5f5B0gtlonvcjXP/HrxwUF8o0w4mWc97RttAu/TxMo3DYQ -rSoniu8PFFkEWZBuBCCS2qGgpJsJA6MZm2Ip4Pq53IvquTTuNEKdEGLbq2MOhQtY/fov7+EtbfoQ -chGqiNVj05/+hmT5Ld9cZ+vPa0+NHYf5inJTUeZT317S9oznLI/Nd/2XKcTsetbbilpgUSZuDH6s -uFP6U6jpj79L1RhUYkG5IvYNubh88QYl/b6uVaDsI/f1Bb7MLX8zdYK8XZld+FDvRTQJIEEpJOjx -6pEbeYMKyOZTvQ1+u3b++zsn/TYUUX/y1Xgvnf7cwfdRIAvoHGyv9xBvRTrKWvbfPmh3QqBIXZWW -lFnB3N9Mm9RswUUo9htJKldBEO4a8NGh7P06hfxFzxPaZi09/PDPweQnxk1X3LGFYlzBIdrj2a+K -5a1YNxgLesXvgvHTFP4ls1sbgibO4yiOV0e8hwz3T76Mx/aPpYUlIbOjioAz1Pxsxc4Bh8B4XOwB -8eLS5l+7P5E4RKUjJQ7LII2xVOM71htkbwSms7GM0ejleAGuN3TlLfCgFZUTxdnlKP+fLfkLShSg -4J6M1cG+JZSaZyajKM7spvblm74AaotHg7diUDSaLCPi49+DGm0Zr5cP6ixNQbZZpMK2/NRM0AO5 -+KIxzju+iuDLYXP2FjGeOr1RRU+0tx+8h3favXAS4JIipwXqmOjNeH3Debsu/FgUWcqN4TZEwYdc -inL5BX3c9upXcVo//+CsZiTvecKYmkfk7fGjH96FUFSSGUeXA9l/sosXZr9OI23E6628p+2vdQx1 -gj+mkUhsWhO7GcGQPYm40c2LXK6VGlUPexmglAUwtwifV0NIOHnK3xr+CsHvRbpij3+gI6KOXRY+ -caeGLo3MXnJ/JAssCu/CqyqXccvExdpQ/nPHBuxr6eCXjtnYW6zO87NjoKBE4ASNaqNorNPVybO5 -VJe9d8UAlUnGSJc825fTW6oRe+RyzvRPRqrJin0hAbVibnVWuvK8uXo245eSXlGcy2tN/hoOzrU2 -b5i/tEjSpXbWVeBCtbRo9qn4zPJ3WtNnNsbgodD4fBcCGujN/GQOjxgM8x07biJSivzMfvgc4+L8 -0Gf/4vV8/Y6/3FOE1A73QV9IG2W5mNv54vHkpLOkhNBXq/0LDHHgAEjOlpmoumCz/lEzgoBZJ8H4 -y5nb5OLhoaTg4zroiA+zXp+mkGeneRs7NKmDpXY/iNi2osm6C76VWSJkjE7zdWN2SFY9DYBy3YrY -9cf3N+tEO7Z9hhpfuhTn8kSPpQuL3wOl4nTcjh5vZb6IGLLxPLX9R2K2iFs38iJIh07Tp2EE6ZVf -iYf3wd87kFVzp8xjfnq4z57b9Jyd3jQDQegsuWwgM23nVNyMHPZhd7WAZegP2qeT0i2Uz9SBa5gk -dO5eZ/IUMB0kzhWv/kXAVAJTLB4Jt66Ew3sd5z15eItq1IsVJ7U7N4SghohtEE9k4JTXtMX7D8aJ -pl5B29ravtaU5v3EQT9xrWrxDvvjubsdgkli9MKFtOm4gOvLyHx0YzPRfK1b84N2z3ReA8m/ww7f -41p23o1+rF7iIl5H5o+qcM0kFX4hfUdIVUupp4IiH0GP+D0HtoVtsAre9Q0DPBwBvMCDHLsN/Fl8 -vQ0K4zpl6JdT46y7gh7cuWyDaI6uDHdwIqEgu40swzxR9KsL8T2c/OgyFnMBBA987cIS685f8Lkp -rS0Wxp2J6/ju7p0RZSyQ6CKrHm1ar/eQbw/13WuwK94eijkCWqN6gjD3unNz2K0szL1nOLRLqbS+ -HJk/Ks3CG1/OZTFNe2fPcmjVyN04zhCpZuvoWRAGvd5k/uW4/MnaSLH5lF0R4axe0NXi+JsqjE6b -iSWqsz08XVxZQialZXlNOzX056iYQImSlzm8pV0g+KmjACX2CM/RTDEO4/BJUkExFiRHa8jSycgk -yYdMjhg7VR7qofF5DvH0eqqWzjLJ46dwYqYUXEhFysBMPngPtSjI3cIdkA3tNXi4BjeR2lMaRCua -P3v+WpNhOe7i5xY9z+a0LK93EQClSvk1KRVsqn9Xypg60YsbNzClmhxwo/OGX8NGvXEE38gukh1G -utph46DPM3hCT3nZaxpsl/Ap4eRze7UvT+CkM3LQFcbZIYqg3YOET2E1R0ZARjiMoawg+MUciJ8q -woCAXPaoPIa3sFwibeVIKbmWaBgN1b8a4O3rcd/TLz72dfdxlBGPP4nkoOwk7mk/6Y4gLxS6zOKA -EGEn9nXdgWtIa+v2imLy9vrfyxz4QIRqcy3wbETzpdg3iDbQw1kniW12fKlRX3fjG0foHMETX8bs -iYt3J93n8xze0tOLCEEbLeKFdS4P5wEoQgwPoNREImkncINW+vn09hy8+ODiWIrgPnidQ57eKtQL -KChY6Z8w8wvhCTEW5D8U92/Gx+vC9kE+YLUPhMR9n8uLCejz4ufLFGJ2h+fryN0QRZGjnzCH8gVv -4hsnP70fPbzTyMTpPpYYE5SRX/SP1stGcn0425PMayAoBbfl603w2xXUrucI/TggttaJbMU+GpCx -j0Xm5z5uWozN7k/7tRO01hNBx8r9bvR03sng3E9TCBfysD3hmkrIR9jLhL2C4qut5Sh/0l6yn0mM -nQtfL964uDu/ogxr93fR5t8OAS03o9Nfp5ap0SBOfy064qxXq/5Ibqc6CiJTcern/Ss/Og4pelYw -/L38ok3mb/8SGtOzCoQC7rssd3fCGtOuhzyRU2Xg3a23nNgUCQHdo24xbIzNaaoKu/t9x7f4qaTx -cAZdtHKGCK63YfgIygPuj31270cXmtPhUPrmBM2TZFLqGd4bI4Cv/Fpe7h12hwXdQiZP6eu+CFm2 -759C78IJr+h3VF/0+JwdOqV+J89udEyKEU/h3ESe84iK8NTZRe1pz1vY36E93M0fb2qakt7set1H -hjbuDBT/JSWbssMs/yKV+MlHOKI45P0H6dkI5QLuzHMzavQU+qU+lxjr9QeWiFKmrOHt7615um6K -NzH9e0aDxgz6vvtL8DDKzCaWMZQHZICBb1l5CvzKvX0Kj/vlCJf9Ti77TfaLGR8E2t+ld3XiAV0L -oB7GfmDUoidHnr4sLnVBlyCMun9/ijwyYx8Vww4e3mKvA5+1u43ScOV54Uvas1WYT70G7uccnO1b -3yjvYnshUR4aiTmQfbtPvCPa94WNxNucZwBVTvC9hDT5yTld4dm5FqGeYIwpUszGSq6FJDsOqr3W -/uYj1Me+hp3y4biAPQUl2Kvqi9+dJAU7iUJUOIDT3ujGzi5IYWBhwkOJcWlHZdD7ZMniAomSY+y6 -dvFj5/TvFfkV5XuBx59eiNrRB6aAOjLPFC87xh4qzUCTX5Yzb61O3kRZMySBWp1UpU6Caz8HJzyW -uXSNWfOBfIPxnjrdjMuwdEVJ7TI0W4MKl9f4yXyEQSf0UutYuXYOvnjfyKWZbQ1/eTdUhYTMyJJB -ulc5aYlXSvTZdMO1ypX7opstSaatSTTcNdah79rF/Qh/fc2w8WmyZHpzkWdNIP1tt7s+QpkLX5CD -7NZKkVqiploEGF2jOiLbQl+T/YKgf4oz9REDY1zkNW/W29JmdLQABp5f2RY8M3wJrZFeKGMwEU+p -VrpPFfuBVhZ+xQhQrz4sha+8a6MstQ7r49ewrD43UXQtGMFDSyWMKLi4w1lDjk1xpOHtAR5xTEoA -9wDPQokofuTt+T3Mzs/kYAaojR77Ds3YPcm8ST3q92CduFK89WjkYYQMqSNwzYgMsG8QHAEZY/mz -m9gpWsW31kAsddgZdIBeFCNi6WulZh/5MlI9sRfIUz0+rHM2PmzO/atu5UzEGdA7utS9VCM2RnyS -uqbhTx5UQLZQP2BYNMjL1o6eW+9fESMOh53UiXc1P/QYt7QBKLZUX+/E6zHUyK5MQJ1E+yXXTvbV -etevzCB2ZFe8kxlc3EESvn6M0McB7aUHlsLC0Q43LW+yEyr14i9dFRl8hPZYjwtBjWuHdPaNHCZV -tuzzIJSNIpBc/Hj2Hnrfa29BjPuA4cY7+lRA0W3h8OzcMpz4h7aAJw4Sqo443ipjy47fjbZ15AJv -pmXbgzC4G2wkg0jHQxJB5S3aCMwqTyChTyq4GErCxs2IgO06B46U+7HYE3uwkrYPH1mr5n53snPi -dVzXllBaEXw72DFUB6JyGxqpEfK653asa5V8Fyi9XzDB7Es64YgNT2FS4lrezRNiJUVxK4hiH3YI -blxQjKuI+vQxtEP0+w/SeGbvE6Ekwt9T05kFQJVqsONzTxLmql6JKUDxUoatzLy7RyDaTReftdZJ -OQL629zXeIf91I1BGokrVLh8Ij51amAR52FwBtiFCLH5bnBtf33DCH1dH8HVxrUbuGcvTYySe2l4 -+5MciVpe73x5ts0IiL7h4uEdxNpQBK87kYlYkzH2xM2fK51foKdKZA50g88qaQ7hohNfqy2wdGO9 -N2SbvG32msW46fgdI0z1gTf9mMPGPbN7iKf35494U3LusfLmhRHFYllJ8Rkf1paOUqz5ALVjap1G -BI7HMkEpMYxjB7LVusMczyki26/eJX6qB/ZSfSgcJsjMScc8QWHgC9RQHGq3O44YCYadF8JLv++w -a1LvGPiosgW3Au59PLenIRXkmltQDulGhz4svfid9gGA3PKMkdwWBYXBT/iThDq+ocmylOztBQA3 -nNXyvAvS4+AosC+obnQpDSde1SY/SLZ6453JZAEtlejuZfl33B2gy6em5+OAejVuIt/vTRtFY2Hh -1oKLG/OW2lKPPZCCrXEwyn65gZDfIUkh+6v3LU47bPziBgEzZOhtx0WRK1FcSQCGoq4STdi+Xw/A -EHuAXag56llCgzgZBWRCPr3Bi/vA4wENgvHm+KsoI1wnKXdOHxnssULsMoGjXCYVZ4ca8c4nGI4m -EYtCBUDmGOcKO5O6luAfjBXD8z1I8slwpFAi3MUE9czuqAedHjcRxfFp/ArmYgibd1py7rsM6NTp -dkTnpQyCUhwp93DWEJJMJl3FrRuQ/9aA/Vf4oHuhNgk2t62ZPCA+POQT6i/PyM98KO5Cv2oYyog8 -cYu68qprQ/scQL0DgC7GQqNDVc2I76/X5Hj3S25FEOQpZSoxigMcZo+j6+BbZ55+HQ== - - - sGLrB+isvYf4Z/9azfjwzFCR7SWNgJ1RACczxyWNKsekG1AKDF5eOGV4L2nKh2Q3wnndEsNarCWQ -rEieH6hRZEIlaqvo887hihCYoF28BqHIwr6PwyCVFGZqNrLYEwMk8I5EhAMPWZXH3xH2QxLZuxG/ -a+oBGD+2/yj9UJmcBmF7Ym8HH631hEt2qBeUQQGsfxidxkm27BshbKWfsgxSHHmqfnk9OH5HQSZf -l4gZIV2+LdHIgZ3EULHkHt/LLoqDA3rmKzbtaa42s3ieXdZvM/Nt4yyWRsOCZGvUfh2yKRnnDgRw -sCVJzrlwa5wzEtFo5nKhJGP9RQtVWZmevqNTD30AkkqvWP3oVVESeWxss/BwFx5VAusKJR2m9JAM -+AKjguQ29jvUv6dInRz/IB7TYN5ce9759ThRv7iX2MI2qxKnYxWl3kmpVhC3gVzv7Mm9cLyelktW -Z12efp3Y+TqVZPcdtZxDtyPh7MTu5wxBinsKq9IlYMIy2eOy0CggGqpqDaEkwXuaztUvyLIGvwGl -/Z8AUJwCOz34cRKsrzv0WowOwXeJGY6gBbVjkC1Hq2w2UE8i34cw13CfGsSIjEWY/CT561islwYz -/BDvAE0yyrbCEQa1ZAFJGTM1jhFbM1MTEPGeIjh862Ul1z7sMSn9Phjnj5kaozUS5gjI5MTakhE8 -895VK9Uu7KY4pfAl7qEyAJEyhA6qACSAyI5vHsqQR6hkHsEpY0+0ZI2ehY086/Mwjg5qsFTbGz31 -cCpu6h32crC4gIceKkkFjJ0fMSOi0vVLqKOLmV0kihX3gStbFUh1IkbgUskYO6KdehmKzwd+2Cdg -sprOfocdZ76S6PsYLpOmGk/mnIoKCt1/4KrM3rzaJaQgoRh0ye0x2Skkei5n8+IB3RWkn4RAhcVs -ke+57H11frt+rEHInrCHdD1DiDTl5yMcJoUmOfn6TgHZ9JxSp1iKnogsdD7sThirP+cqUTuFJlQg -2qS3W7mcKKjFBUcQvfiMa6orxy+ijFyBxP94pb0WCiE9iq9k7RHlquC9aHckmflL52v4zS6Ccik/ -LUbgO4eFt79iCioBaYvFHVn5tcFmascZ9MkSniPmOAX9kn0ZTshvBaIDxH89ytKT3fZmH2SCgGzf -ZGXPju7vqqmEOsvIVc8+mH4ONZNO2koNq90ECO7MoBaxk7zGW0h6Zzf4IvuiyD85r8A0lhsfoCX/ -7kBvqLBwGzuhSD066mcazs8xEpcZYYF0TYixAcI3s2xbG/DinxXaAANRgesAWaxx6UFvCEDGCY1e -23aYxnS1DMI3kBScpJsTrEcDnRNYoNtmB8jKfnWLtSMOAfEfsii55QzmrFpknNJtq94BAi7PvLZF -JPi07mz/wXySPly9josvzmLvs21UMXjqyxd78Dt5j/xuk8qyky5Jm3QHRm4da+ow4t0NK7q3QZdk -xJ2NFGmNCL/bSAhHikPKmYgwF5+lQM4j1L95hMker+NAbxm2o9FnOMmxjECAcSIib9FaKGNVgNP7 -ZbOpn9Cv8HaO0KLHc4Us8uD6lhjXssqtM9H3MJYCqdggzlIVSh+5J3oNp2nSERaYqyD79CVw3i/2 -8Cs4A1pkF8iJZGKz3M4CKCP2hZ3ruI8pRrIOQPFBjJWsNpGD0ovx3K6Xw1pPVH146p2q42bj30dQ -rxkAMw4GOADoizhv4wAt1ndL2GsNp79v00AGm8yFqK1U2QBuTD2wrRAgN4wH3kZAFT+0oEQ8u0cm -JLa/QqzxIEdrDVK6SSjKw9jpD2vxzUaol7ec2YREz/zGF+zRdD3EZgzqalYpbqHZTbvJ3zBCreAT -U+CJj7z55J3EVIyLPVDdXI7vCnON7y/VDao7SYfcOGZEG8RgS3JVSgSMED5UlRMXXEyPYwirqw6L -V+yNOEapCgvt5Ode0UXwNeS98Z1O9DXXKKA4Cdh3lXPvC4jQFRFVDZfWW1R9aiURU2nyinZXRlaP -wdZ+lVIru268IbmWRJjQFwvhJRPUKCGpGYMjCgWpImcl21Eb2X/LSY3qmvR6x8hQzE49uOJ9wtND -dboEF5enXjhyvziGtafyHfbG2B7i0eVQgHwGB4LUphh7DG4g5ST3GtzRgjge8JjRubZZiR7GXusD -SL8P2oZPXDwmGIDg1Omo2C/Vh+QITuCPTwBGhmyLtiA0CAdQ7As93/A31YiFgIjtS0g2Lp68uPBi -vntQOMg9kEalRQetSmfhkziEaB/04qE16ye47ehPn4MXP9YZOwkWOglQUB1G9u8jPLHxLUtBlZWI -zzroDBZXwDS0IUdYbIZGFahQrO2EKFOR/AD6gRRY5SNMsohNgyyasQ10mwPxWOS/s9KyYg6ae8Uu -4yDGMpPbC1HKIsILcOk0OOcIdyMqrxIDvuPuWoGz5oUDeWYXA2tpw1+Fs70vpbT8FX9/HAslG7LT -aSxii5bTX6qtwffvJRbxIhRaSTOx25adHho5BeKbjihSgO6+ny2yD/vF7PQgMP8k/lPAGAo1aiTb -GphShduDtOKyOi44/oJWPe6JktO+kEd1uTAZuGHgtDtE06a0DdpTryJOvzwPDMmZWiDlcHaC+Mm+ -PsiR79GV7OukrIBYnOzrE7nhlCiQX1tIJENbyuxIvKw42gt7YxKtySWx68pRV7mMyxdzBbLZF5LG -nosR0DE6n5yDQhGrYXP2u3MC0WVs03YezuhurjxzaiVzorx6K6jp4Yn6DUSJa4WWhUY7dISlY6/i -uaE1VRrR/GChornaAEkohQAIOesnXhHUIGpjlkiqGt7eFgQR22rSHAFR0MEqqj1hc1UT2ow1UK5y -BvsAnRQI26CVPioBAqA0lMl6ZkFhATFAqGnD6dZuvAkFBD8VHsaAe6DP1C9uEA9Af2jtqRvKMQrS -ttehLjwpxlJ1h/enCIB8VYrs9ekpftX2ZHYkpzdIdNV4UZb0CmgNEtNFJj3zPsvExge2srqIktjY -NqskigF6SniIupSf2p+4E0ZV5VHw11A8HKmb8e0xBgCOsC5gVtd3+qrq9UCQ4XPeCaN9wo2r29Bv -JwO96ybrwWG3hVxZgIILb0IHKOjt8ULgw4t/PFDAWPaCy186sEMJTFPlgwVgqWNVb6NEsffr4Fb5 -rYk96NaaggYwxyxDz5kRHMKkktYAZQBCsINJp14WQBaUGqqAg6Y/dCRdazBKOAibU0AD1mUIJ6MG -5N5ZWySm6sCIJQiBBGCdD9Jdy0eoBtDtJb6mCTkd4GuPk5zRfyHkQSBwFPltsdUfajdIeOmRxKEO -lMSWk10G4eqI3esMh6DGVswzPhlsKcN21CcDuPMIRWNlw2l9xMKxQzLsbSxY37TeU4Ar75X1bbFP -tEEBvNsqu+MvXQQxzvqqIfL4Qc0Q2JtrJG5oUnADSgw4utboJVyWWVsjG+Y13g6OfBe6bSCQIsNG -Q0tntgC4CfnoVosRJpcM84k1kaNAV/thbAvhu644Prd9sTRKyIa1moqtO4B/rTFhJSVuZFNAjnPo -kUrWY7aVPraXNM9hYVbSPHViNXvPdErSuKJiToPB7oh5MRYg4IGxaDMhzU+IKEnmrwCQWEekwWZa -TRAUbxMSezJy5chg2r7W9MtE4wmcqm+4bSVYbHV0lVzZ0IKbIOiS7OxAaZfb+ELcFbWRtzmlcnEB -Irq47nejgK0cvuG5SR6Wvt/Bc1ssYR0j5fcrTeNPod6RiI0c8UF9Q/LDGzBlb4hpcj5AWuoeVrNb -Bu6DfqKpdBvOqAsbiDBvTu23zaDh0leWVPkB4BMN2D9s2tyEE57IMspJivo/97rNYs6CUu53y+yz -d6pWxkNi93UrZ0hDzaDy14rjVcQ44TNdYnxkgIV22Yd/bDQchbxshxjFXuVk4ZXMuvVKxPGx/p13 -2B/n/YDnVjDIOXS8mn1PvQmQxH8uAVvEXrBo0TDWGzNYcSD1ypjqmMRbFGUa3DT0FvXGRvZL4uou -aPrDjSPurhF/IuvQTvyeXV3Pa4htwhmKmKw3dlweVv+lhnThiHTUSDu/yJuBJr0bfYkvzYXKVtnc -M/AcB/frk4UruwTn7GOrzIt2yULj2EAzXB9krFJydrVNgtqOwbh+xQDBsQI8Wh8kEDxsvJKbaFhu -OyJDsVduJV52G2BM0ZeGBzah9pa6U7oSmQDo2zhbFNhFhcy5bSZzmU/eSV88iY75Y++wA2ZyjTWB -9uvsGccaeUk3U6Lt6aCIDNCErPpBihu2Dzx2lYZnuZnZOKyo6MVovHYAltg6ujb67amOrXA8u9bx -uKnofcjFKMYJGL1GIRyh3xCL81K0DLt930Bg+COmoMCkbEM6uZFxPpgw9fEistRtJ6rhHo2HDG1f -9DdUBNNt2KKWZdA5AABGiwzOYqTyAmqBPyYr2i7jvNiXI0an8lsm94efApeYJvb4GAdzV6EbrU8X -+A4chPJ0T/GnWzy9+CULktnBA79BNdVP2g7bXnhnnziRCJZ4fE7co5jbVnAFHT333Tp1Y7Vjw1Jl -/RLsfK1qQiAHgLM3YBj3F/rR3nXSvbHQZnZ4vvVLnUJ2mw9h392+eat4lhnjME/J9VGlUQSnU+MI -0VIBF0iuZMN8ImEdoYdwSVkixoK2ON0G3OgB/RPJwahk47iWwiayBx0Rl5B2gftUhkueGhk1kZKV -yX1O8EUFPSPEZDVytV2Kn4/+WLnoK09dikNE87rPGZmJxwiOKJFWBe9PeVzpPbqKzeKfowX00mET -iBQbfcYESit6jdJJLICsjd4UAo4GXoV2BB+M4LkKJcwiG4DYN85HJeV7hz1OMc8tKtiMzU0ue6kY -NnrPcTyOmTqbh4eRYyYeieEaeGOyFfBaq6ePsNInhRbBoYhDZy9QbIoZ/TGIemPEBjryAXNYsZBq -LESXMoLj7MdiDegqtTEHuKPkEPAdUzvc6/nilsB8/L0vx/COTepNOSF73BtSupdilsNDPPtOC2YL -dN3JFfaxwYcBJ4FUfThYDlFH+mvoEzruao9tS8/drRUDA2l9uKEO8QPAtwDi5le2QI6wSF3YS7AI -qsOa+ltp7mVwHVvGemTyFm8Elb+ns5HUnl+JDjlwpG+6VzD0YnThI2k+Lolmr6UYfGRJBmBf5XO7 -yBXIR+WH/ywG1dQRUpz2sGMDvayiiLGApw4FfIHldlJXUGlN7NB0lZaETmwvQVC3cG5C3tLZ/upe -gTK6KGoiBm1o9dVuqgbUsdcpbrEM8neFEDv/qWwm0Qfx+ksBOV4b+4mnwR7GR4xcsXE48vmFgOZN -SWqKE9TI8q/MqJBzbmgxCc/JszLDSaR9JycSFyjqa0wDsajJ38bzS1B8aIO9zv/9MoU38iYubmTH -yEo/YI/0bxvgbwxvkayQi5iRY7wEdJS6P7/k50ufACaH0PN1Cpyd1FSIaJHO9o/PRRnn04cRyCko -36qR4hwlmIglbwkUM6mThZYEFU/NEn41BZsc2IfKJduMg7s4xdYwFkoFUimjBhCIkg== - - - xDgJ+wnqArGzg4DM4te+XRsBegbBiussiBwBKdTJwqXgFABbK3hGn27h+fuVzvsFBKxmgT9gR3rU -9GaiZWmgMAWM+rrsqA+S89eB7Yf/9vf0iJm7V2waKT3RxxUpbjUic6Bx0/efRjJAAh1ixdWIEA19 -QK9zeMvknzxSE7ew2kGKqDU4N074V+j2+wGzUl7VXkqZgeG9IKH+miX0y4mRwUtqERVbVQ0F+Cqw -TNCTOK5GiACQxMCX9pNqSBl05oFIUhljEIt4oP06g1js0u4DaF+9QZi3EpxCgQhR7D9AMKHTXozE -4VD+4AXuzGp9JYiueztqib1y4MR+nVrMujGI2EbBEvoJFelGxwg+bMcT8aEQL8ZCptt6Eun8aQ1V -vWgbFpWDsb3qAzigKDB4S/xm0eZ1ajFrx9Mfw8pWMk9OxiJKF/PNbMjsLUsrmbEttF2C7Nztm4jd -SygJ2vGW/3sgXz/9MXs8KUYzmU6epn725fzzrQXyRXtVeWtAL0z2qAhEZbBdCPvpIPhhmr62T083 -bTY0DNybNs3bxWArfJnDWyJe7ASwhsrQYTZ0kI5YjBf9nAoFcWMHGk9Zp94wAjJM3aqjfjKwjwjs -GZ9nkJ8cSHr3k9qeND5Mz2SsHtIMk36pJF5amD8NkwlP5yogmw/VeoHHENehNdKAzXhMtqRUCXBU -rSDN35tYmqim7qh9CJKKZPoU/ZJpgB3VOw9ep/aWaVqfx+bG9Nif2b4IxBJaIOTag/1nN3DZQy0L -F6M0DFytjMomY0SLX95ebJsVxXtNTHB2aNvd1r3NbRMd1zvXmV7HiWOjMnxwlQGSToMhblnv/zvt -zkezWHkVYIhHQgkmJDl8lM1KtHjJL140JV0PeKuol+BxaRb6y7nxyQiOdOH418rOB+zoQdoUihTj -RbVPlTfMiHLqTqGlmDcS9O0ZuPr8g/kRAgXgydMP2i+YggDhr6GUdRjZ11zEUKeFjypqJiSZDr7n -A4qr1ynEk5LsLzKzemp+wN4PKuD9xgtWSAiQSptp7tdxgjxUUMmgKtPA0slDNfXvUSiB6CNFm+BK -lHrsDCq5kxH25CVT1hsboXb8XHOMoXQUVJRFyooKnvgVrOBVy5Iqc39ldMUuiE938ZZu8NPgvEF4 -eaWxV+DS1WTZ1JWkzZj6fjrduWOnkRsPMyyo/351ezY5pXWkJJjgEj9gHxUc50OKouQmCQ2YExSE -r+O8p5/YFaempjH5E/uGQB/0dDf6+nrgIm5JDdYPz13fg/w9PQq4ea8/xlsdA5AUTazxFBO7pWfk -pg/kTdVuzUuS03RPTbpcLRQUVheX15OG1ttAA7hz6vHTL4YqxjUhd8tWTi77ceEBKZsfk16X+IRr -tan3HwzDX5gdKR95eoVqFA+792rpU/WW8Iakhr4VpFqCS1Y7vd5+MkAHzjexEL5OwV7GI4z7V3/3 -787/+Le//env//Ev//d//ac//tM//fkff3Pr3//5f/7v357t//r3v/32x48//+mbmr897N/Gv/ld -+fZ3Eoz+4Z9/9zd/kX+o34r+7w//T/7t3z/+6f88bP/8bXz7D9/+238v3/70+Is//Gd8UE6IJzOb -M0ndFiuhwP7+yY7D5P3TOD+yp3F+k2n9x8f/iU/wOPskB3Wk91z+4RF2nm9FkR6LN/JHDLYW8Era -h/CRFsphoyc+32NAtwryoaD+gyi42zuOJOyDuqQQrgFs9/rz4WLIu8YhpdluyuiAZeGS8130zKIo -CaByN7of28SC4Fi08cpFORn+dycnw6H85DBfxU6/JEo1U76ymvixeYhBHOb9CiexUelqouOs8Ce7 -78Wsq/RpEKdDEn7ZxFEPpnjhZa1GfmOn/As66E+Qul/Kfp0IUKWkBG7Bh8N+0ghz81VPZJGiJj0K -U0vwRaXAPMJTKR25EuR4xRhPwvEFtVijgU2h1ATO7o3cgsEdWWNb9kDe3Y6Gs4pdU81kU46VG2dC -BAcCCclU8XIGyorIwak3iLmlm2sWW/nJ6JjiYcWbk+uS9fGVNHwlhaCtGsFq6L2IsX8mF6zxmRmk -gwN0J0uSz7diAHSRxpEvDV436NtOAsMiKj0kH6pKp8KCREff2COEB/7psLAv9soXUcmWU7cxOR+r -xRKhqhLKfscdqZ5RUErQOscbRpgLPJ5KQcqRVYL05IqYZHs6PiqwOtZjp9X5BBEU+wBoRZnEwq8k -MTCwbuJUUrsEvdtV4DKsKJOqsR6jhI0f9IEvkeb+8L47qDWKXxPwVzRy3awDKt2DzLbB46qeYj+5 -CCMA2hGvdKYOxskXUj2R0RSvBEduFjRyjgNqwVTn1aZPIEZuIyJxE+OVGkzRKncygEL8xk5ENVuO -NZlvNg+yxfudhOVeInIU51iA6XF2dekiJr1lGgGKOCezxzRvg/Xghv3Jt4IssqLDVLIoyISmvHbT -dn+8Nz9hxDiwlo/TMbeZGgyUBogjaB3FRlgEP66JigVhkjmbG8AkMRdyHvvOIejJHtBgTOtx9uPR -BDFFW/bd27j3APcY26fulGZUznC/2wDqCmifmCnCphUCg3PAJZ8EOtkWEAleaxaMZG3YE0vApiUQ -x41NQCfRNHB+cQZVscWuHqB0gWPegER46/dOqWpGQc6HHLPyEY4xOPs6IZ6yNywexW3/V1ysfKp+ -DPnIsiUMfrzHbZN6czWaI9sl/ZIHTX6xU17E0dTyEatQJ45wyakKEEgv1pDo34KjkgQ0QQZtxg+C -pwxoiQs3CD7ytk/PUUCWDVC9FgycXTpsL5ZPNXBKr2zsigK4wjGfTjeOsBo4PLU3kTBNZXk1X8H7 -ReTnLgB/UALtqiYFxyYEmHu1RuAnn0BhmqzMuB8lgM4NMGQimhOY5oRnCiLx3hIJbAWrSie01SGL -AbPs5D53fZbeTfLXn4+zqHTK4R3L3xGJ+Amk8w674jTMDrBnp3/tGipmvDyClNuKI99NwBFgrIKT -2YQYkJxEm8j93G18wiPJLABA1F08/Fj9ergNpYZrnBUcIGQ3iqN/BL3J078CmDfIhuN4Vx9BSOsa -8xHWhS3Gimo5OBQFfnkoUBMdwgrLxDMrof8rzC8bsO8yyBLTak+P0ozo/5EomkWFPlPjCd+Q9BtQ -piEYaYbzC16LtjiC1h3sUYI8Rvg1kR5a3kXYo+v2iTVZ+HYo5wHmLzF6r+gNBLRyMMH1m6wGS+V8 -ELLkfrHCJOH6kVdHQIF46orUYNGzGTpJkjjOgKa4VBwCs18KPXZKvfVYRdsgdTY3b6AT0CCPETjh -/VhTuo0QELjuGcqTQRVycQGhPzxHMVasikQrK1jEhRGgdSLGSUUSYA1k38XxkjrJxd6B9oH32i/d -cMGfGbxFAIreOewyJUQjKqubzc27YXomvYfKdHcKZ39rbcUIj3+BVI/D6oV0i5u4xjtmDMcokQqL -na0l3vOn0MWNZqFOYyeIL1jRxczz9ERm92EfIc3ieF5FKLKrqALlGKCUHYIfoyHivMVvQDV57XF7 -I/CoVgGwPw63aoQu72U7/ZAj6PodgKBajGRD37GbyrS49XL+jznVhREgE9ke6wGed4IIjwbxTk3x -+t026hxcksjrCPzswoMS5GXFu8HnKBhNbzGK/UKu5HmVmJoEedmwUe9DPiSx9+t5QIhqCaZz7Pwo -zFhqYCf5gLv1IcWyMqOzmckn4oGJEN9d7NOP8ylBPQcd4jEz8R0YKW4QC3Z7u89vo6fdOwEfRrfH -7fuAPyE5X/mde65CjHvwjvdJI9wLWaxiVKeKIo0P1SF4AwvwWfRrjFRjAGP0mCnpiRhNYKFcgDsl -g2eKQFGeH0rPPfBz/tiXRTqxcxJSif79a9GaGQs9PyzBRTLam/UKZACCfIFRFwgY1yHIcwUtevHp -nJB0GaFs4OJRZtyz503djHfiJrQBm9hJxUnYGvBqnxgrVLhO9893J4HoG+CMIf11PHUHoJ6ezVTx -tAm6zcc5igNv8dAdhwCqmwGgyvnk23QFtlUR6P5TNQYI/TbEFgrehBEUHILQPMDnKCchR1iXn1nN -CFKl3PFTDHDM1js+XmdOFvVs5pJO8AKNoLC/rLAKxyk9INSehwLm/ZElzjaBaZ6xnqYwK2A8WuCY -gDci4n86R2fooVxjODbjpqMDVLNALxd31FRhkD2cqmPVTsH5pCnr5W+dA5xZxQ+lOVSuAMtnyLAF -GF/E8WKsRFCFwoBMuOD7RfZEsJ+Du6TvRHIXhKgrl4SP0Kxs6V6jT1jq2oBQ123qtSI+aBkRhaCG -2NvDXjpX4cQIaxAx5vkMqdWwR1iLuVGruX7oZj37KeylFcVV722Ucs1Bkg30K2Kk+60nK0cejE4S -WfC61AieDWzB+6D6mCIn0TAM/ztUOZRd+PNKnCMlDCkhKOgXeEsrEgWvF5MiOT5tTVmbsV/sqkil -z0EmmpsBgnMkPxVnhkyDDajbk2fyW8xkAiCIOeMRqdidz4GHGXrHp2s4+ztKAwxqkVHUTwI4eOZw -lefIgMMIW0QPsiIvsb1jRYx98OAr4KWup8M/mjyJ1I7jgSXAaRKwAaQ2Y2/AvucPc7IL+5Iqbzq/ -lR98C2DISv9B99UYobJzYceEL+WsnUlJJtawi6dk38MeGiB4bRMuPLpIzLgL3cQeIHOJLCsP6oMp -LJ7SenT6T3W48OGsz0gC3l+iwjSD2933nrDHqcwJHwYH6UFG8kG/SP6eiy3kjfVxpvOkJoKfGUd1 -/eKWhd0SqAwE/3OllAIYWOYykKt/6CF+uiB2r986ISIzEpTpq9ymW+jRDNREXYFI41VmqtSOA1i/ -Hg6M+p70IF0fI7lzHlKFyKhGREn1M7R/3GERdvfJ7o0zIPoJYlPfEjkA1isyWqL52Z5cPDN66AJ3 -m39/meng470oBQg6wTH3M9o5nlymeeHHKE7D5yBLl9iBRrlOjwPEeNgntBRXgYcO1chqjr4hJzwn -LOT/B7ABFSzwEYS7AW99QGzgLuRVtCDixs0kwwxRzGayxf6ZUtsgtKSG19mWMA0hJRKLTcxMDOKd -x7V6XxVqmCXinmAaWmwLFzfI2ZJECrOhBSsZI+JOzpVIZHbgLCCBIhe35sY2IJupBTd7vVF6k2t5 -mCbhTQDuL9sHRB/zMq0Te/fqFhLw16jomT6R4C8URc0ZHsSFSKa3Jmh3zXabtiO6C871LvaDa2uo -bPbCbbbacb6cszqOII4w18XIfuiuge0lNSmtmR6vVqd9BCnrMDxmk5uIbw50GGFZiJ5mwy4AsR65 -sgPJ8jyw++uKv3GRkYUvVdMtlvcXlc3FvNVhA+oSICQ8MSQQ1kHxTzSiIIZ58h0/yUAei4d0bi1J -xDhPhH5r1Gu83DbQ5L4k4VoxcjQMqIQKolOwu4g+jDX7YHH4CB2wpaTR6vlL90A2FB/nBN6qeTFL -+kCMeB83wRHcsdUnwf4Qrz3oJ3WhA+lM3roZMe4WO1sn8Sx3QaVQYVzdbXEPASYQHQ== - - - yboPHvvF3+vbfroHMVYqsUZAtEtay3qcvcOO6mhattcKfB7mVIxcKooMqUSwfUWZ/ZIQVX5xsTSI -rU6FzU+uaW/HaqlRW6Xffvz3Mt1OYqAK4aPIByQ9il2shuQpAUgczcltzqNQkVO68EgSR1PIb+qK -xbWLnym+PPmlRle093QLISWNVl4ZdUS6ZUBRs5OyIR2aOoXNANmOBrm4dCw2D8l2TbtDEpnbFS1i -uolzBHyNqE7JdRWIukT/8qO/fzgx+DGv9W8qH8q3m96DqIVi94ajvrWzsabpmtHp+a4nZn2EhqKr -fv4Lc/DkW0oMygjpOdY0gnPu6Hv3xdYE88atBQOMg7A/i2E2OEsCRYPkVsvL3eOoLQQrcNRbEnNt -pg/vro5rl3VUoeTnoATmirr4RP3vO5gV5PESlfIwswve10NHZ4bc1qSxp+zjjUEd9qHmgUld/8Kn -lyPFNLDXRPv5TgPqvyJjp3tihV5aG/gsRsCTZMgL9wRSiemnkUHb2VdIKZ6nv3cUuUi6bvTRUu9W -ICT4slOVQuwDZ8Ais8weybVHtUVE36Jrd/LOFn8tlYFULhbASyjI75EUxLEbDeADdLcnTGErGtjf -SgI472mqVbaSsGiGHeX+Cbg8rpCLs/U4xMJF6c4I1PTMoYclOrQNybXuyU/5OX7ecJBkZB6GNaoC -W0j+sZjAbSTitJs3vTg31+PVu6MfsyeISuTuXAl1S3kzDsTpNk80yABRHJGBGTck+us9U0yD7L/+ -Wv/kEm62MuJQ95FX8ukAghCF3YPX7zU1vbD7G11PA7iqt3wSnvzfQlCGcwdR2V7AffmK5gDxAQ6I -IC+Df/umtCCoOLlP5M99GV2CryOXTF7J4wYedrPhSX3dleaQQv9FKVUREF4MkMUNpARxgALh58ng -ByhyznkbkszTlHH4SCcicvLN80w7ONWubWpmHJ0QiPTBS8ch0hgUed5Q7JLXjM1cpFvnidfpAxyk -mfKbO8kX0nyuGT1f9+njYVJe1pbXK7ZroPuG4tLeB2U4mUKQie2TXh0alPdBwVuWi3PUbMH8IfBN -SRcdgfuXg2L2hQpqXvQOx4l4hCNo45E7SQc/tyKT7MjFTYHX6+mZmMMan/INcnENKXEXwD62nYbR -R7gpWwuszb4J5+0Ut2KryLbd9DUIEpyenjctbmdb9NyKxe/7ggVTj7cdA4zKAZyLcWf0JepyW9EJ -cC6iOLIv4EkaZHRc3AfOOMfDqA27b6ojysAVR7Hm/99hd8Lu691itDu4V3eZwdlZLftCCNbLetd7 -BezHDhk39NRKSt1OE5mCMhnzIpJ1X+aiE1Ye1yFN+xF/+vmYPyqYl9wb//WD8zFp1Z0CUKjYvQp+ -qPSlcyq8r8sYKfg+5GImJrCh689tZn0MDXCKFVBtPQR+/hSkiOQr9+3/+IH2tB7EyJWTUDOi9ctw -c4QAsFcC4BJ8dwlhPNkVSTnVG45jydXKJR3FB3kwqpNs6nc/0hR2PZ+2f5EbZr1mVCoF+yKRx3Cy -inG5yK2jG/BxsfOp6g4QRpYzUglGHs8sLGecyWeGqpO75mI77FaLnUmmdghpdwSUzOsgYYoYR9Td -O15wi3KGqr6TRSl0mxOqwhOLYhxAVaSdSRTpSVEFBQh56kS0AUjzeD3haSlKwkd4vLZJLMFx5fBm -GFM/LAveT1AKJxzX4+JRSeHl2OXTLNP5/E0/goGJpxtH3cMc5zjFp3suDzjP8OnpQEr51YfdS6Fi -d1baIxEKz79TMULk2Wbk2WSEgy8SlTC5uLMLzgNeMV4mhkLBUYTrT+StDEsgEz730x5/esq1J1E6 -mcNEOswZew8zmScQCirfCtBB0qQ9w+rpT/mT04l4DSCtXMlFkbr2lDRi8ou0Y/Ew3jhETSqpShT3 -GfBK52HHxwBaJlVWY1UT61IYLADeSLlu7frGsYgrITt0Y1kvozmx1ceTVnurCar2xIXqHGBRIo6X -K7l0enjzJ3ubumf52bOT74V+WGWyJ7rdk0XSFn1ZFGdCUOntwRecelKU4pH8VAfjBgAbpXKlfcS9 -acKCAwe8C1qnZ6fG5uu+m5LxA8iujCsc4RKmvLwcIi1PJDFSjL0ZUfp2KLyPcNjW5RN+hz1Qct25 -2881rKVdbDGpfm90udMe6ZRP9oOH4iH6heOj1DRM2ImsRquTZmDZFuCQK3XRoHJwR3zCl/oYJ/d3 -y+9MdGFBLVfeMHvfXKfAUoRsPmEuRwYmxguIYn22fPsOEtaOAnTLgMkDDw6f23aWh/Q0o9FEVwpJ -qAvZFc4lfOeQv1v7PQ4aubzOoVlRNBdqYM03DajoQepHpjBI/N8a57UqL2R7o2L/8RFCi0/bNcBs -14ZRpGinIykF0tGtUgTo65yc76gAxIPLVT9XNGmmLkSxT9jTz0WXHPptzjb205c5SL8Xfg5wDyEE -aOiI004Q32j8RURPlipVkKf5ctU3EnUu71hQ1QT0mSTArRJSoNdEo2I3LixkMHuY4g0VI8jyJhNr -FBXBGb2MrdgmNmIDneiYTDSQujODzDgx+6r25wLbPfbKbOSX7iI/ZkzO1cQ5LY8NG+Bgv8GTUc9Q -MybXaBjvgT/hy2U82P25Gb/q0YvOFORpTzfqIZtb6Duclp/GaHR1D5RYQKJ7qtES25USxNN1nBe9 -YYAgSdBSOcKNeN3ZTZ/I5yVUndgW2yKzpwa7WJ0c2T8ce009hcvoNumBON15JaKst096yH5GSYbC -D+ptzT5MfaAJ5bAEuDfB/qIz48aVVJaSBp6kjByr5HI5TOxot7baHeCyg/R/AxciucJ585Nkoq7x -94BZkWxjQUseuvclXzmxfaaGDE29UkXHocLbhbjN6EtGUsJkjkn4LEkrV/ADMR3W2Xq3ybwnCfk2 -8cgGq6Y72FG2xalilMZ68D2vfVlt9ObKbcDcKE16T8c2996rpodomG1l3O9WpJ1g0QLJ9pqk2tiJ -uX0Fz+K2RAmLzR7Mnk18qBSmXdZFnkTDwI/dGANHRm6FFPq2AvB3rYMnlS53DKViXrBiki7sGgRY -ScuGHdVipHqXH+kCEljLn1ra8RXXAN6b7nR1goComEIPZMbjQggVnZpQGOge2lY5fYd9OVmr3LRj -CsR4QAzu8cRqVu83Y9S6VgiwbFJk/v/svQmYXFd5IGpZGNuyZWMEBmMM5UVGXrp1z7n3LNdis9oY -DO0ltiEOhCjtVstq3IvS6rYteDPJTF4Wv3nJfLz32CYbYUjiTBIyk2TCNyQh+/bxZRIgIcaQ8DHJ -JCEmM3gJhnh759/OPbfqVlf1IulKrnJbXf3XqXPP+u+LVck1kqLg4GLCgXkusagEsHZy2EQUtZko -ipFZJkptwKXP8XnIK+sDwJ2XDFdsvzAlJXHnp5GuwZSkk0cg5k6LPUgBYxctlIZ5LT5p7MLko0Dh -YsI8hmey8IqFxsQ1qd5DTJiPfCn34GLBcEdW5b7dArqRzUQvPe7BxmD9Kpt6AFZER46J4RrJ8fBx -B3m0H7vIRBhNrBIvJLmegWJC7kTFWpgqCsaRy0iVUzaXOoDikFCUopKGMyLu3FyAx0vyeOq4KEUc -RvTAeYKr5A9OCo4XnPyARpCEAfhIv6vM7eCR7uQ8ST0CSElcxDJsVdl4gJdyUyRZIKQkZldEjuRn -oHOSuavKfZZ41jvy4BpDh31REVfHFFNJm3i1Y8wYRBIYuUFodySgjtUWxDINwLiTpvLWg56N9Ix1 -T8cooa2JGeIkFsaQiZJ68Ek8BZaAZXAmaXJNUR3pmFFbnO24ZiH3UMSoW1eF42DxIjmQkn1XpeOq -CFmRJXigiPFPEpbtqIYHNZTiSY7MMbEDyRMAecSI8AJQS6U84YuTeC+X6jsg272W1JGSJ6zIYmyk -j+n9iizZCVdF+EJ4mZGScjEQK4vuy66KSgLvBEHHaHOnHvIyrYelJUYuqevDeqO8jFld0rRpOQZw -StsqXVxeGSgc2fvGMNhQRCcXBQTI0WqF/ajQAISqOrkqUpI0T4tIIZs3huHiVf2cJPAMwvE56Mum -BaDY7xGYfzYe6iqw21IMC/WgqtLCLkaUKgwBkpvJJT4gP4STIhZJFjJVxmzaLlZILGOImYt+uNhQ -MuklrDZWbpNqoOxFCwkxSjlkIkcAUMdEekm1aa5KSESQzcPKitcKTjiTEtQcMoaJGat0JiYyxDZG -TgDQS1Up4bWgBx8xbJZWvJbq3ZaUGpMRXkjWR7S2RLjOYvEOyWhiJHcVbKfUuzRiqEgyBY5Rtj2u -2WIjswMJoiktHWwSx3xBXiEnhTiTIBKswCsHO9Z5NlHXZaOqF1OLxh5UVUNVR02Diw4lmQJ2nZfI -kedImqKKsQb2APCqDJaI+b2Jm96WpGvTiRZCJ+nakKOhr3B+ItAfFRKZX3BW1zKLqn6f+vGgRleJ -usDGlFPaSzZ+ueO9Y5iQ4UG5cxu9vAG9zkd4lUiZ3e5RyxzdrHl4ADTit5NVll7oofIrF/V3VBKX -lc8ZAK24QGaV74iPkfxov7Wi6S5LUXlyzoneSVTz0yJ/4OicZFwEuBMjUByITtw2pPaI16LSQOV8 -xLK9PcdccWgHih5WoPucF7iLnsnOiCmoKjmBQVsELHNtBRhpC8Cjh61EnvlMohPEUaNxCBPJ6Fiu -E8eZODrmecroxAlaheiZnHMJMg9uG2I5TmIRySwmtg02pfpYTB49WcQC1zWCODhwfvDinIybMR/h -SgbHKfgdluWJXitOgOyDSabbqECoLKpRRHYU08I+SIX4TnQNoBpb6lIjaScBqJ3YpLOoQSizCGR+ -G3x9TOViHVkOUDdEhxoj/lRcQ5vdjbx4EJUxzAyrt0w0DazKiuiTkipJ0vzCp2VkvBWZGuAxFQVq -HSb79BMfAeUHjCTQKKuMppB0oYwhqCrmUaj85yTHhBUNaVmLEMa6BmLuzpgfzdNQXrEs9g4h7pgu -kwBD5O3nI7yMOTcgLCPW8fNeVISoUprs08/kpmVeNBvLvBjj9SVdcjgXicc9wie74FDH08VQBeyi -AZR8u0q1mJmCci1mUKPMJm8g26IuoUxW7xvOvIiOBkhUbJZWOECjNaJYW/mygPsABZ/bKp4GtJ9U -PtxmaR5jgFPZP5vFAoyVTwFoKFjtHLhVCsq0OlV+gsWZLO+Wk9/TeDnzvS2IGyUEwVZkDI+odGig -nkIiC1odZkxdjEECzUnmxDmjaxmmqRAEJKiC9AZw4EMPOb6x4I4Nq+sxmSW8kQ895bus8loqg1QQ -RJBI6cooo2OKiDwmeyUlCqaI4GSviv6WPK/UrrQ4KUgEwRFuZfQFByBnFwEgseAgsVWRTugTprkH -cVMH+kD6RhD5qlyz9fFPyKps6Mw5SRprCvJTmxd7AudbNEacfsFCQI7AGMVKHCoo/SmCAONdo8wO -CXupeLnJYyVa6IGBOtJQIzozjPv2iaUjI9eSooxZyIBlJus+ImkbbUDkHw7KiqpaPA== - - - aj4FLnH+aDtCHFb4GN+OniOOu0UFejRa8WGGx7lSjFaNQNkyT7LS9Co9lOIgDtH24HZJWcTAtElm -d9AccTkvEdVxfeJFh7o4eEdA96SjqzTqbnAYGMCeVWZllGFh1RUbcTB/mSIgxqxXUyZbLMRigzGe -F63ERYOYa+YPYMpUvMawgSf2kFGhBjgRlSUdZR0EarYa4MahVsZw8r+JPocyElQIEKCPhBOZFzgr -AFylcgQHfa8YKLlTwZWfNCbgMVqFmIPXPdlxnKt8WHNiGQPQx5qD6OeO/JnzaQIsMDTQ0oFPIWMD -h44yloBi29GULCHQyxpRh8AB7bkDr3KJMkg64KRNDgP7GZirNPSAZQVoLJwVOCKhygUMXoZdGZWE -ZoHbYFVdAsM1NI9NbMX4OJL2MqotxVthyTIDiqnoqwbwwnEPntOG4r6RNJbRrWSgIh1RRlLUdBwD -K1JUrCiFwSUkvissHkUww4mJFflZRION5TLCKgZqQtQKlz9QFMfJQFYKZKisiB1kBWmplBTzhNAs -b3kAThsJSRJbVncYl0f3B1fGQDIXaxA7HzW/vQc6UjsbXg7QeeHAJl+9QSyuIFt47xvG68AUKNkc -WMF5AessOY0ShctIw1WV0k0ppQ8cK2gnpIcgTvH1EfcoCIwl07yzsaAWGGooeBusiy4K5lZTFpMA -r9y2rI+MhyN5jRfXZsxgJCUs4eySlQTmYylEEyIQKGm5ARQmvuKWUtBAjHoVUOIlwAYCz9l2Axec -eCQI7S6sWGG5eJBx5E0de+BKoqYq+ozO5ohuwIRSeDHDZqW01NURt+LaBwHf4pgOSqyM20p+HjTN -ot3ZsPU89uC16m3M6j/s1pjVhuCE/zO+CjUE+ocqKzQDFcKmsYEagJVLaGXQhghz78V+zlWtDLuo -M9AitjEl7VjVgydMX2VBg8dRDCTYrZgrBPM72QhNmShzHRbCtcSwFhkH3hWitLWmCiYoSNAKwDKt -voQxPLiONhZ1dTG9rK18XQHtk0Edw0SVLKOSxQGkUUUfcjZFJDe8CNFTFezX1TLScwuClxxQY128 -fyam/gfLsEanf7hnNqIbYLWtUD+pW2qhVLdlHCRjgGBzLsWj0uwUEFzPuX4V5Rehuy76tiwm/7BV -3vda1Bow61x/Pot2BLifpINAVYmRbuu4STjZ3AI73wGlBzP6eUxQjwX0fM9vQneJ8IZZbDIrwl4u -mdzAMpShiGJ9rDwEpgBSNAVJjigQECzyK4wKTUM+VVDEuIzFoNgwjQWxuSqZkuwRUAq6yuKiMkqd -iY8TB83QOCMjBiZfY416TpsXgAUUc+QTpow4mYLBSXHmaUdJ1wMwF/MkJL+mUwfZxkxkhLUSBAAl -UTUXFolm6aJKGg3Jh5nZZKdc7oHrNhFfyZGdkC25wJMA6XnYrV0zz4dAUxW41FriDiBBSh5tEOzb -aGw0fylQWBTMgZbgSxaXgVPRmDy6/cOSFZp7QJ9MahnIjWGsDYIp70ROfouE8RjvKwjILbgxCqxj -lPGeqqBAopCqgmmWiwrUcDXtMdTHcy46wEGiYo+B/ZYLcnEPRiIarI35T7JC9sI6csAjIEedAS6o -HD+zTAwFgN1IgglClTifWiXKPjjTaOxnkmgY89tSU71XohLkP25LLN3AWLckcmB9DCkxJcWw0K1A -04VizM9cIlxC1lWFLUaBoPFmxtsORShAItVhK3K89hV/4/ji97xh/gYywJJ1nsqIC4ODCXJx67H2 -ekzSa8lEABoyX1SJghHB5iaNnoN8vBRQCfY8ogd5QQpQEOWiYzsUDKUKk7lOXfSgMDrx5rmOOwxJ -SwnH5+D3TnQVMIqiIRRJCpg8ptoO4CpTrKFMvLmR2KfeNYiavayUJACWM8bEAm8cYgsZIjIpuZCT -mAwnlRO+hFvBsc02J/TFV6gQ6yA4ExlCJcoKMrNxwGjEw7MHqh0eHMEt5SKFy8IyrsLEdZYaeydG -S64wBwxjGb3VVXR1AJqlOdF8JqQVFF8leTZBS0L+hishEyJpbKyVJBc3SdJzRTZf7MFXPis6l6zj -eLG4WlQmOjLAOpI8XolcamyaygNyrDthYa3hZMmZrBlI1JKN3UdklqX6Ee0ldw7oLJhHwRTreHih -7rRUrDJSth7Iis9lGYzw4QXEmWRSCIAlfUjHySoanUnRbKArKqUrrPspwEHTxQJSXjRdfFK1lkjr -ohYKA4tOnrZAdJ2JRIHIChxrDheH6gtO7nVVOkNriewGNXfJixBzwkOiX/as0qUE2EAx1Sjo6VJY -xbxy0cljETapNbwavmhELo1oqOm+9rvbTTigD7oALpxQSxFLLecxgDLndGIE5JwJyKFU6UY50Bg5 -FHEIgu8ZNFeBcwVTNXRxIEWoTnU+8D3SJkECVk6WX2TCRUOVcLaHQpJkRTRc11I6k1czJD/jghtg -+sg93yc2jcHEnRCpJO1KnqdXmtOuQNJ4yvEFbCjrPfJM8jKBJAK2YLoNOQfnstBhJcM8B5zZKn8N -ZJKnLFABAWU6jgFOmBHMJuI93NKSEHFVlzPwSI44RcgrFHCt9FBITC+kBWKhEKpakDcE+HoSPdLA -CpEy3ZIqcVrwkiWrOaYQ8lLUwtE6gjDDsQbh4pTkVGI5jqSbTXOwe6RthFtGVxrzGLD3RimZQSC9 -QmX4VRzehHCJjVEcBIJASeGhvORgB9Eqr2qcxLh2UJSJL0VBIUsoZpWct0ppsbyBJgjQ97Sw3Ezm -IP8JJ0yD6kYU2OWqZA0ZKzFwcSq2H8s0UVSwZRaIe8i0NOaUuL2kNjI4hQIlIDhCQSZ0+E0FuNIS -XN1vmL/pZR9jidIGXrORK+3Dwjbyu82ccX82upHnbuDO+7HyTXx/o4TQV5xolD0apZR+Ik2j/NMk -KfUTqxpFsEZZrY9c1ygD9pEWm0XLXPImAqGSUqQxJSQQJNYBqRiclnNBZO4hchFAV1nyUF7iRgCo -c+HRmihwI7VupOt9mYAmhqGRs+jHhjTyLI3cTR9OqJFrauSv+jBjzZxbE4/XlyFs5B4b+cx+TGkj -B7thxriR3+7LnDcx8o0cfz/xoEmUaJQ5+gkoTdJMk9jTiLf7IvlGctCPdlgi3qRp5Py6WD8cXbtA -1S1j6CfiN+oDGjUHTWoGpD5azOBh4sDHwRuiQyBMi9Wg+w2Tn17iPh/57R5OoB/P0MxgNHIjjXxL -PyaniSHqwzk1s1mNPFkT99aP1WvmCxs5yH7sZiNv2sjF9mN5m9jjRj66ieNu5s2buPi+LH+TfNAs -SfQRO5pllEZpplnyAZcJ4ntA05Jx/RKTdCBpDIpSjDtQXcdWyxAN/zA0l+WxMVGvQOE5fU0Ry8Pk -Lg3pRLd+9HYGxw4rUQgs6BWWas6NUXkLTVYNRck56SRAMQ3qALK1m6oyCbERYJ1nGyo431P+Rcyd -HlOc5KXEWQE35a1UbPGUXsSUMbkbsBTkpYCBUTp1qafHQSZYrvxlxbcFULxUkoGZFdLQJWchcNZy -fznMD2oF8Z30UfcPp4lSUwPWLSLO1KXwtWAl5dOknagkIQ5R6gSamIaAXf8iG8GejRh8ySXnMvFk -g3xdHL3SV+5oFFIaxZkm2QexrgIzLOgyFTvlhG6AMqCHUvyfsSwmoENTmYaM+qWvnCaZVsCkmHGA -HGLkVhqAkoIZbMZUlBKAMeEMBDKSFQ84HjFUM8Osq7zbQvlY15mJTctGgx/6JCpJlewoVloaj5E5 -mPLN5BA3m9qTVFawCxP7DwH5z+WqiDsGWHYpsRRwf1W2dCf2YIgUKmIu7jIr2IlEipoYKJVmjaBd -J2Si8FIjHdP/FlJwhV2YwEWAudUiF3diTImViYUV6rhkYvAXu1wBDhDkZJ9HjIFRIuStbtK886BT -4symJq0aximviphfGFRDnuJG8zSEMq/ib1XMRpg7ua0O65cwjFMDg6NdghuAYS2ZNkshxSLmSwV2 -iB25IciKcqACAfSxwCP4TtEOQ8SitlKxxVjDhnWjYlETk3t220EDA+8lZDEtmNZklut5xGrdBYdd -jlHqe/L2BnlGRyRrYqwX1pNzkkCdk/0D9lfcLcSU4DrkWWLxtxjPZAksxwnSY5d0yjOKpByjcFgy -b+a1hDfgAkP5WfMk4WQm8ZNaRaau91ZHlgzsHMCSKTBvInYAhUBBLJkYP7vfVBaPLswZXYkb0Gwf -hNyIvBsRfRNF6EM9+pGaRqLUSMGayV0jYexHRRtJbhNt7kfHG4l+X/agmZfI6E4gnGMMMe+IE2AR -uRFPZ8mkubVhbHSBsCikkZhTDuFMgUp0WQKkZQDpsxAzU2xspDJXrqPjE9wecrfPVRr+BYiXilHn -WVUVw0tBwbxK2NnnqjVfy8YL3O+2N6KGJiTSD+P0Q0+NiKwZ6zWhyEZk2hfzNmHpRnTeD/fnMVoz -sSPmVBIEgXFcOtarUxAiHdXqSrKGgscbe4AgWqPQak2J7WIJW6mopiPKwJK73sqAC1GtNHNgjexa -E2PXyAUS/2QsCFbg0s6CqULpCPknQZ/dbyr/ecyAgMwQhvtG/3nOIa8r6y1kZyPhA4pUq5iFjS0x -oJSq6iAgpcYZQ5VqzjSSS0IGRPM25ksxiOAkYiB2wIpq4OiSTFQF2cRpyIxgoBM6e6j/IU7PF1Tg -GXsuS5NkIOO911xqlcbG8Y9Qnlm8jnNA/9iDT3MQQ1gXFcfRvsoVpiRLDMSq2jyGFxjqoaT4D8I6 -mK0zZwEv3CbNrClHNkGtPh+z3nM978KRipDpsxOOt6gyFIG/lMaoU6ydw/n8UeUsPmfAwTHLaiSu -2FQZz0SUJyA7qKHGygq9U1XJDyU8AvTFKXSg9gSJxFDLgDEf4g/y7gI8EFM9oe6PPB5N1IFDdBCn -m9JpAL2ph5FEaZJLZJcSCi5us+BqHKX8Rm6zkTVtZmL7cbyN7HETI92P625m0ZuY+T6Mf7OQ0CRN -9Jc8GsSURoGmj/TTKCo1iVR9hC+f3GlRmGIaVs1AzXWBehFWRIMQ6lKAyy+bjGqRLgLrfiO+vz3H -k51/+5zlhlPf74o03qfGm9fnmjbf6cbb3wdVNOOVJgzUD10147ZGLNgPZebidarLNNElZy1FpZ8W -tM26UWCzjEsoSk4hc7muAlkUmYxIdaVjYluuCIKcfpWmGQtd4pRhDLnEbwV2hgOXJFgApubJvAHR -G9FgAFlAiZcuiuhBDDk8KUIaTCRZLpHNzGcVXOUt9sD0MsClShIAS2ksBeogLScxiwBUJsnhCZgJ -4IbXi88olDuVL8cYI1DuVelroFMSJwrOdEZAVuRCUVDmY8ALjYEuhq0SvCBFDsKtrBfHgxcu1gYD -foG/X1ZKWEDpPC0f86vA9wvRrcbskBAPTXiNk15NyGG0HHpU0mMJmDk8CCYbr7Iec8YUrG4pXwcz -s+cwnlhopZRFBbMrp25yUQsL5fuS5FZexL8AFz4GcBjZbADImQsgOoDlvCLNtoAZ0w== - - - JLZHXPcx7534Aka/bs4UChKJTXKPBcTCtmfOTQmVLdjOVpV2BAbSkVc2Z5+dEHzNRlvrKKfFGAr3 -HBFtq0p1QMCYQzU0a8ZrUGUYhT8g3JxkrEKCjtMNEbrMiZyCPs5Es5ONZUjBA5tVduCKSZFQzscA -NLSiUTKbrNJOSA1TFRPRgMMl0WJMJMxyFBinMpYV0jKqgAKo9FERoyvBz5oT7+QU9MZk09Pjc8pl -FscgaYly4lwJGCQkS0DJqQ/GTM7IBvdSVR0Y73kIMaVWlQq6iMVfYLqKOKKC7BVxEuh3So3Zbwv2 -X/Oz2DILWas40RsnDuPve3ERAcmGi1tJHASxXxw+BwwJJxZTlCA5yq4lxS54HdNDgEdEQRmBGDeu -NgY22tMqaCVbKQlN4DbFHqQEsCF8EWfB/Aukb+QMGjBgK1nI0Bw0hkI1F3KF3D9VKndTlVN2Vc1W -0H9LOkDH1MmYcUmIgvEq0/J9RfZLiNrzXHbUxvSQjmRMAkpNKZ+a9A074yFcsSEJXT0kl6TUFYA0 -XV5Slqa5zUwsWV+lKgJXj6zkdC9SzFTBIpS0jJmrSkeCPV1yhUiAfIFOgFJAypaSt6qZF29i3BtZ -/EZ5YJPi390mxL+LqjArhRGIAmgFAr0ZVK1LQtwbQPTFKuDdsZnDCisa/ZnKGLoBQ40x2Bh5g5oD -uD4xBtvkmqNEtIupWthdxGcxwy2m2Si5A9SyTkgPgZPmxmhKnow9e4kwFFIuOS7ocXwQS9ZnJKqw -MhqP6sOqvixGop55TcRsIuCMQ2TfkdV5XuAmM0UCn+yFo3Ixwh0VzILjyn5FIP5ZL34rXLGk94nV -YArJmFpwTrx5gYc/uB9JYdrbmICl93kyvMjOyjFi/irqN7hYsXQS4RhoWI18YPuMWPnCxQp4SBIi -p1eayHAbnx7oSgNDGZdBBs2ZpyoggSlrjcXT0IO6lJyEanmtEE1HgSTZskJyvULPPobFqwzVqDIM -Yk2ZSsvjIh8bcHqe9MxAZuWT9U+BqOKsBJLSCncpAdrptjJfVptE2kHPbs+RKMoXWOF/cI1BAs0T -DFRfc0ex5PO9u1k/FVmWl72HvKefSZaH14Bm6vhxjKIBOe18QT4J8wLXljJEFrEcBdhzFeWFNWRX -ISA7K9GNi2HGRczJamKJdwjXKyQnGesKobYVTQxgVTnOGlxXuXQtHR1CBtSrlQo+gAlskeS2cXQH -IF+C1lIYqsglY5wMwSZ56NIhpG3RwhUz/7IPvDxwjCJJjVNdT3OxGkV9aFVjnkcsImW04faYTK7n -iQUpkml+kii9IDeixh2dTpIQ8YqCGx1oIOJmO43TNC7qxrDWnWMXGsmuBpmBtWU3uqziTh3WzWUx -BR0sCciZgY2vJK14hoBLrAJNjagjGBxrlnENClOSFSzCSyWhvEkVQCNhrIZTTsTV4wMCHG8eq3jX -2is2pgDvQpQNcqqKL0HP0k0MjwIwYtayrzCa7eK6i2Mkd0pAjrWsPx7ToCEwSRhZbTZ6IXM1xcbt -TJ+VpJ4Hbw4jrpWO0x/CgCmHo6lSiiez8EkdieRpPsqSsIYsZENq+6riHe+xT0tsQA9OIqiTxg27 -0LOQE2vHgd1TwUzi1YYox2HyIqZh0cM8PVT15exajAqMU4zVFCVpR394co4xr3feAJbdrh/jnukw -aejB9op01CxrqZggRFH0bh27gJsxRT0bMlczkEpeCQmYkB6MLcqECoyRW0BedKNJTfXjejBiECpL -gYtXM/Tgozaf7aUY4yDYNx1D1ZhnMRl7LhS3j9VjUenFA9ZsdHXgKVfyGNCU2DO2ROMDOTc0Y1/J -Ad67whX2zaREA+SJAeX9vMALR7qrgupWTHZtSoAXkBIuVhQuy4LtuuIE7yjHEwOlPi3q6iX9UFKK -L+25/kTeRFOQD0OEBynTdHUedwGGF3PKZFR/RoYcH+iUz3unksJxgA0DwQdOdp1fyErtqxKLmkT0 -Hng1xjzmr6p1koPna+PmRMFAgfrcs1IIFX+0byq6u4MJmrO2KMhepFlTlHPuS/CIKzPWhyQZxpPG -OvplgE+dKoqeHmQM3DL2II111GLVuq2iFDEjfdWQzdLgYJtAJ6VbTqsB6jUvWWo5NMZXPsDKS1AJ -qOeT2AvwIiQ0oaLXiwIPDklSo0yMbpWWWerlrZwUCITcM5xPS0FOb5FwoxOiFVYVkvXESCsrWBvy -+mRZkou2LDwnfhCjIQSmECMFpqiilKAqCYkqyRrDPYM2P/pSKiPBVnwp025B245qJWnJPUANZMOP -Q29LAnKUkTxuDB35faYZmFSsU+xpwlmLJKbJxZWUir0JUKVVZrNYwRG0/FxZKytj8Z/kNGHKNFZz -JnmoVBYLGOSUBXVS4CxEgcdGHgN3WO6WO8Qty5J7wGpksWcW51GyzWUlUEFJyk7OwQGhP+wdUlBh -Eu6hglekS0EuEiY7nOQc1pxrhjDNiRuUK1E/SsFeVeW8r2hRDVhWLuQBbrTtkn6qfQcgu1EFIGNu -kDFcTFisIAeu50TP7FwC6ZEpugVgXPxCWeHgMK17tD9BhFopGY8xDdak3AzxcbHkRBPhzHh6tiJG -OEcKSD/80EwUseKJHoBSacqmxdeTHliMorusCsVzkew2EPlCwi9POiKDqi3u7KTApXnFdSgfi2gk -ixQrv3atcoXADJks6oiK2YAxCfUpe86azmIVQYZHHOrqZ7O7E5HYeglM5BmgpAjLVTWJDWM2HHPH -wrXXgMgd14E1iQ0jiMqiztWiD4PVCSM5hvafspQ4K2wZ7UK+EF4Xe56UnsMUVTLmnp5ZorBFTHxU -k2psIR51xklEks2ltJ20JaBEZtY70FQQO/LP1JbDaAzHK9XHmrLxtcXhFGsBxjmpgfkuqZIbmPep -PFXX6kLyEUmNhWXiCMiFr0QEa9zhyHiAoYC3rqQQyHlR/zsW2cqYRApsAhwlBLmYxWOY2VZw8S0r -KwpYjz0HouWcQtMYKWJsMQpPYMQkQyCaqpwQjSRKAocS9g9DVk1zelSJKwYLqaNQnpyidqIRhHXS -cLxYuWhi6egkGA4CezicSKWBSqgniEldwBd5UuCWNJRgeYi8LWwkcQk2q0q/uCi7luR017js6Y6w -fttyyZd5gWNdpwo+KXDW3UMImxjdLCWJRaDV4gKakXOD5dIRcZkUHUTrxLcHTEUOkZ51kTzWnoRO -FRPdI6YOGgbGFujqWNWeJeOyqY9HMi5LGVsmuw+GjaFmaBBHQQniz9hWTIw6u9YkdZ1NFRpXUJWm -MbRjMbtjOUiVgIot2yZ1Vgb3X7LLgncqVyU0WQx3q7LaGa43T0F0VbxNAgdLfRRXTCyxa6uKickw -qixtBhxMHBvYS2OTnlmChySINhbOYU0wSHEcyAohtORy7SijIzuhlbQNlDuNC6Zj5R3UT4NwzQaA -alwOS8FWq8PMFejsHOXaMVp4e25LME81r11ObufcQS51FCHmB/SCk7Jxgdjm1B7dosbYIoiSpqvc -vSBvKXFdpDZ0PVhF4NxzPD/gZ8bmHRQkKeUBDIgkjASoycmzG+EJPB5XRZWu0tUsRGisAxmPQcrR -yjqRjBll394h61h0LumDnazqi6EoDje6S1oWB6J7ftKritnek+HSQYnfDxw1fz8RrZM+uJB0jXik -J7CaWlazPkf8AxEeQBTiYpZKInvR8zLiGtwfCmrlkPsEiZUY9V9HSiUxbj04jKtNx44ZUdsSNfkM -BiOY41uKgdgRjvUD6VaL7z2o0HMOEBFOvxfNVxQgUkQseZqZiibz5LGyp3LJCYulOiRDnmhXkBmV -FNbgNGqlLAty1NNyr0TuTgp4KcJLyJOLUyCkiM7FZQGrb0ekI6VkPOVDrqEHKFGbMabGqtxcACHx -tofsZIVUvNBcx7PC4JC/m0XI6tiVWQ1rxGMeresVvyBm4+q8EM2BrN6VO3hFn3yV8xpSl5Iwjtn4 -+XD1bNIalPXJLeW7MN8LR11SnIbgnP5w7GeyT/+bYceD+mKRUUBl5HwvXOojgcExchAxCthJNR3r -yOI30dCDFLDywsomPAj4p5LCpM7EiNU1ZSxqvSo2GPVOIt67IqcS0xUnNS9wLqkDqXHBG3xS4OIO -x/AxDJqy0RcuLgYwHJTSwKUx4IrC1JE3EDUceF3nuKeJk3CO1Zx1fdQEZy8o66mgxRi5pztiDKpx -5U7UoNCti5l6cieXRjDqGMbZiDNdFt3xIIqcgr0hbXCVfxWy4ZOnYf0QApwU3aD45HJP6CLvGLEb -TlQMsT7EIkvPcX3CaUmfGHtmLUIyZsgywvq1asxh4bW2KmnJPWtJzFinI4UWI17CfUGNuEzoiHBf -yaFg/m1CDgU7I4DLu4pVwQFOojqkY+Cq4NAJoVhbFWvqPYbxhOpSNEGO3bNivRCupilGvDGMJ7Kc -xSkBlqKNQvtPrI2jMZu/TuDcAydfL6J5VvuYWTtPXZE1ltTVCS/HtUmwJEBOnbCgreEwCvuDqabG -MFsLRwmhBThPUp/yjYLGJa1cAPLGyijGMN+EJewNaeOrEoo6ppZ1lVs3ALXj/L7iJKwxmAdbWip7 -yT1A2VXJ7ytZeiAPBrPPtsqXZCRTEOTBB3f+6e5ZcLXaSYEzaoB80nwuYCnoXLiqdBYkyaETJD1M -yAKzkhRLfGQSN8Y16zBvvmaYJC92lL87dsCe985JoFwNJsV1a0DjqkxOoOBChwKZc/08VPGcyZl0 -lDmpOn5UOY2cFpKCN3JcwX0iOkdorCyYM5wvTg0odT9qQNykCemh0AXD8wqpw1XiTOqgw+3UB2co -QUHjVVwD/deQvdVxZnYsAT4f74rxKjl+8Q5llKxE2k/26Wfz6v74Tan7gzlOKBV0iQmhUvhkFxwd -UKIUUfYBJd9GN8hb18LX4ITu3U6sZcE1YxUpksMwbwtLtG/3tUvL181OL88uLkwtHelcA7BdY5gL -2zt1RWf3bctLswt3dXbt3Xvt9PTK/K2Ly1PQ+IrO1dB0D/wT5SD2ReZ8eJv7iLW7gCqaO9iuyXUC -1Nc2Shi+SNTaQgXQRFiKkUAK5QGb47gMIyJrQnOQvJxIOWjL2Y+hMk1WVaQBsRHyRJEjxq/BfRYV -OrPa4JLgYtlVMYLnFBKAQPTOp++D+GbERVkc1q2NKnETJSQbEyODtQYUVNH7OqtGJsWTM7E643Az -Id9SVNHFACvIrcYOU2VsCTknSHsCDrEcS5BriWgrqzpLjFrIpQyEEy6YF8iVITxWSl7bLNZIS6qG -Y3wsDbSMghggbnaJK6PPCUSSs4uDp9DiSC3Zog9u4i7aYmRZfUTxYPiLnvI+U0k+OGlciuYvrACj -Kqxg6iV8vIq7MJXmNs/FN0U8FcfI1kScFQyBPQQyHa02msJzaA2hZqbKOJCAyzpBKnkpy6uo6CAB -xcqUUWaxyrrGO17VKYLYbdLageGRtceQDC8vpAQJeO9Oi0ycmGDF395F9qkS1kE/TQ== - - - NbCg/APEn0QVP4d/AMWWuEsnjgtYAISrusdgFWACILaHr1MelWOO0jfSHWNpAkueFFL1i/PsS4Af -3+hcAomwQAc7PpdyoJyvqmbxNmTKxAi3Mp4lHcvVo8AsBn0p7gD2QCP1D1LXXC/ikK80/t5TuCgC -nfhdl+K/BtEnVYZhON2ZOBrIIkK35McL7hJcDNrz0pEHRGXIBrUKR1nkMRIIC06Rv06V6cHHempg -ZiuSMRAoxoc7UcdKgQQAAmaRswxluHkDoPCuHGXPgk3lpo7BlBx8aIR79ZrO6YT0wDwQLFcZJ5tx -7ew8YlUpzk22w9xG93IXYwGKWHaaLrI05ko0WHJYTJW5r2IdXfQiqZx9sVa0RE2JbRVXu2DDeqIT -lKr1CI8LDpkjrXhTccY2b8SaCEAQMnklwZUrZ9u42Fw9p6YjOz77nPgicbFCs8uEEMiSa2+YeO6B -0pXRZZkzLHis+shWZlWZtWAveNltFW5oRB6Cxhxs46NlDSuxRQwPD8ml0rGkZUHLsngRS24NAJLi -EUYDOsDYg7geWFCnyNnJpVRyjKgtyLBIwKoePMApsSbWQZNggLBO3GmMfIWGonCsisYgOBOWoqwe -xoTTRtMlAi0PSzJlXr+pXFojM7TJjzBQrpTdh7AkfQxCYeqFVcQyOXvsRgVkQ8Kg+baD5w8HPW7i -ENfNRBd5goYwKSoHOtkY9AfBpbiXDYz+PZvNcYOMbKKXvo+BEAry2BjxL2FNW6bEKdJztuLNHc9q -UpgB6L6bFhduCR0th77GxhiO0ln6yfZtNx3Cz1RGH972tjdePzsXutq+bXd8D4PcfceNkzct7p+B -97sOIDgZ633zcwvhwzH4B4Q9HGzzx/dMza3Q56qz+4aF5fqny0cO0YdhmZamjvQ+eeb2laU7V+Zm -FqZnjtHz91Qtpg/Ozu1fmqG92F3fxdowqZvdb12YnQ6gVUe5azmd0CrfqAauN3XgSbNwiJZm71xZ -njmMDcMnVbfp9O6cOjxz/dLMd62EUR8Zdp5gEG7hDLvm0j3VhZX5m6eXp+6BLww3T93CSaaz6J7h -4eXZ5emDt8/ODT/FhcXb8EstnGltNt1TXZo5vDK3vJaL2cIZyiSuXr1d99wPzEwszh9aPDy73GbE -uXhoZmlqeXFp2D2aXWjhDlWT6N6EQcOtJnbb4srS9Mwbl6YOHZydbuEcZxeaZjcA97X+buEMBl+s -9Zzs+4amkztbuDD39e728IS/jRNqIPXE1YaBLg9NCBfvfNfM9PLexZWF/aHV3sUBm3xcZlqbVs+V -3T+IGOy69oZ9184dOji1T7VwcjD+7jndO7t/eQBrUs1OZa08njyH7qkdnJm96+DQDExL5yaTGIBn -d183c6CzZySHHis59MDSVBAR5m5anD180kuibURlmy6IFi2c5EgQHQmiI0F0JIiOBNGRIDoSREeC -aHVl1yCItpGxGQmiJ97cTg5B9I1TK4cPz04t7J1bOVYjWA/ZOby8/7qZe2bZln3iSjD1eWyMsb+T -tqxtU0wY+w0xjYitWzg/ZhnXJrTcfODA4ZnlFl+wtR29RZzO3pP1ALb0ZjUJK/uHZsjbiA73NzDk -+4fmyFs5oyPrwA63HZqZXpmbWpoEjiLM4/iwArcszi4sTzJn1lY8NfRxHzNZlrXwgGxEBB0LrHAr -J9Ughr576Enplk7q3UdLh3J4ZenA1PTMbdNTc0PbV9ro/Fafx8aY2cMBBd68MqD5iJ042uzE4eUj -w5/JOSZXY9OLc4tL19x7kJTybZsnz6lnqkx033DfocWFmYU1qCPaOMeeyfSb7sTiwuHlqTVMt8Wz -reayZpZrZEU67lakEYFo/9w2aB9ruyZnKDPZcwBzTC3NLh+cn1lupY12szDI3OzyLVOzgyjfCYpC -TnJDe5sxpO6d2d0DuKbEqtzCKcHoe2Y09Fa1kWG8u2mP8hN6RnnDjAYoRFt+6op1EOMbZ5buOlaE -uOnR8HeL+YDh6UerLTrrMrmdXPvTYuZluN3ZhCcN38s6TQa6lY4mG7IYtHNKDfaC4Vy49s7cMzN3 -28Gp/Yv3nqQRRUUrN2xzHLnaObeTw5HrxsWlQwcX5xbvOtJierdWvcT+2bmpVtoUNksn0Ua1WF9l -xP4TeVZNmPkkc0mrn8Op/bMrQ3uAq/E2Jh6ROayd9x8hwxMQGbYxlGbjyLCNs9oYMhyhwBMABZ5k -Hsd3tlHa2wTk0MZpbQw7tBLfNXlOD69EOWkcjUdpJ47tJm1q2ok2kqdNTzvRRj/XUdqJ9RDsNh7X -TSDYbZzWBgl2K3mQ9Zj8rps9fGhuanpmfmZh+capQy2mcWu8SwNcFU7Uu9TGaZ28d2m9vlftRHlN -bleH1xJR08bDd7g5lObIxMGphYWZudtm5mam16AxvLaFU+ydS/ds71vnbG9t4Wx757JmojYBwTU3 -ToV297WYoK1FaJuXybRtt5rFtTXS6jYqCzaBVrdxWhuk1W2kAE20GgcytHgpdR6zTqfnraq9beH0 -ZaprxpLHIY9NbQRTC7PzU62OCTkwOzc3vFptZubdbVSo0SS6t/9OKD4z9P0YcJOOjx6NZtA9sf2D -3HETxVkbp4Xj757U8uKJrAyE0XfPKLa6CYc+ZEjL0Cmtjss8u+bUPeUgqEzfuLh/6NnOzS7MTLXR -t7yaSA9RWVqcP5FDRGj8PWcVyoKtDPYkSfXVC20kBelEeia5f//s8uw9Q09xaQb1hm2cZZxJg1iy -PLU0vKfh3L1TR9pII+I8BvN862F71pM5sI23eVMzB7aRsG6CjNrGaW1QRm2l2D3KgLhruo04YuNX -qJXTGvkj1Wc05ls4pZE/0tXPMX+k50ZB1lam+xp5JK2HZLfRjr0JJLuN09oYyW4nFzLySEp2qI22 -s024S22c1sl7l9brkdROlLdhj6Q2svUjj6SRR9LII+m47dZmeCRNt1GHuAm0uo3T2iCtbiX7sfke -SWrkkdQaNDmyjzXcwzZqtjYBY7ZxWhvEmK0kAuvRFIwyWW5SpsR2nvJ1H4mTZl/aaZ0f7UuLs7ls -jjvQUXAqWkMVsp0tXN4NZRRtZxLHUSHsihkcoIK7Yd/E4uLc3ra6+G88hapq5QkdpVAdpVDdBML+ -HMgaeHJXPVlbVro2agSHz0m3Ls51KBJ2HR7rfW1cHiZgI0Q4QoQjRLhpiNC2cFYtQoRtXJ4RImwl -IpxZWhwUtznCg63Fg23keNqAB98Ap3rED47Q4AgNPhfQYBv5nfagwTauzomPBk9ij542ulH0ePQc -t0uVbvy+Ni7V6G61+G65Fh6Ylt6tNi7ViX63Tq7MdEsz84uD8vq0KDPd2hylVUft0VlHmfBv1gn/ -7wnvw+894YNOC+ec+EaP8vF172Ur59WYkG9d6esIod066HCfDLnrDh+C7HUtnGf/3HWjLG+NtGOU -5e14zTDN8tY1wUMzU8vXDY9YZxf2zxyYXZhtpS03mc1gpnokArVKBNpIQr25Qef3BA0ZaunEngPF -nNeGOyYW5w8tHp5ttZi3VgPNoA1tmXVmjSGGsmE3rwz4Trswx/DhPi3FHBvLPNJ+w5oeYQ/ihJdm -lw/Ozyy3cpuOBhZpY1D2xtFIy/HkpqCT9nIiDRmN7h5wzhLFXgunBKPvmdHQ29XGK3Z30x4NcINq -+YzyhhkNsLq2/NQV6yDKo4QAm5UQoOVEsu3BzqM42sY5XnvDvltm75uZu2Vu6si+Vp6rBiXJkFab -pART1jFtRKnJRAbcnZFxfmSc3wzjPN4EMM/rbE9bb8XIID8yyLdxlptskD8Bi8mNDPKN9GJkkD9e -MxwZ5EcG+XZt1cggf+JObGSQ7yn8ceDAyuGZSciLFKYxki1HsuUqkzsyMze3eO+eu5ZmZhb2hCs+ -sycQ1dm7FvfcM7s4N7O8Z2lm/57FpamFu9q4ACOhcxWh88Qpy74uoXOO8dvYNNSEaOFUR5LnSPJs -pCYjyfN4zXAkeQ7BPYYlWlie5NSgbeXfZubCc9Yidto2Ks2TWfSglXfPzq+sIfOsb+P84iQGH8N1 -qR9Wlg4EVHrbWiqdtTEVfH0eG1M/7Cfpr4Wz3LgGor1z22Cd8JZqVhrQ0lq47m4hs4VT7J5O891b -E4Jpoz9GbRo9MiXdqonFBaT8J/I8e6ayZvbntkMz04HRXxppz0bas+HEANCVkfaMVWmoRBtpz0ba -s6OA30bas5H2bKQ9G2nPRtqzoznNk157NrzIPTe4RFBrBO6RTrCLhxnpBIdl9lq4TpupFDzMUm07 -I1mfUxH/shVvuO9Q4NbWoG5p5RntmUy/6a5Zu9RG1NU7me7prk89ujQzgB63RTe6NmboZMztcFJn -hjnZyUS75/ecSOgwwiDPiewwc7PLt0zNDiL3JygaaX8mpg0hkhZb2TeWFaaNFsuNZYVpI5O8saww -rZzRKCvMiBifBMS4jfhv49S4xazGBglx28WVUYq29rMXoxRt7T91oxRtm84HrJ18tPGsj9Kznfjp -2QaQj9YYz0fp2dqVnu2kzgHSxkvxXM4B8lxOl3HzgQOHZ9rsrLW2o7eI0wGcsTSzv60b9Zzy+Ng/ -gMepJtXG+r4w+p4ZHTmhZ3RkJG9tNo4a/uKeEPhpXYTk5NqnE8TIOZKNTzjZ+LaDU/sX721z3vKR -0NhGodG28MCMhMaR0NjKCzYSGk+cG7YxodG3cEYbExpbOaOR0DgSGkdC40hoPKpC49BIf0yZnS1c -3wa0PzTWb+mUjvROaSTaDxDt22jea7J53zu7fw3R1EXWxvPJc+ie2sGZwYH7ydzyVs5NJnFiq2Ru -X1m6c2VuZmH6uLCCo2Rixwp5NiQTW0PCqRMn39Ty4tDeluNtLPYC4++e07pyaN05dXjm+qWZ71oJ -d3sAmzNKoXUU59k/hdaBpcX54Q9rGzMs0Ax6jusoNVgTbRilBjteMxylBtscBQCNe7iVOrA0Nb08 -NXfT4mwrg3Wrbmu5RYenmW1HzV1T6Z7pwsr8zWF/7hneo7qVOW/SaXRP8fDy7PL0wdtn54af48Li -bfilFs60NpsGRL0Ge9p0GzUgG8+/38ppbSz1/nQr3VDWY0k4GcOzT7BsS2s4dq28SaMEROtVYI3s -Ab1zBHvA8lQ7Uyuc+I5+x1yrfLTFmeV0Qm07L5sqzGTPCWmmjV48I2FmHcIMXMwWzjARZ0aM8knL -KLf08I1Y5fWd66EdnFrpj7AR96ZWTmjk3LQGYabiuPcNyKDUGj3Y2hycslae0c1xcGrn3EYOTseM -oxpZ1pqF0TZG6G+6LNrGSY5k0ZEsOpJFR7LoSBYdyaIjWXQki65XFm2jMWEki554czs5ZNGTOP9J -G4WY53L+k6Hd5lo6sY05zp0cod6jtC7HfaM2fr8WZdfaNrWN3bC2oo0NZatpIxEbpThF5Hf93OLi -QOHjhMF9QxTgPUHRXVtn1oTtDi8fGb4M+wE4gFRt+Jo756am797TIdDioanp2eUj1w== - - - tNNawXNc830b6X6Pu+53xE41TPMwJnGZOIGwzBriLto6q43p8E+Ik7m+EtDXUTXaSS5Gf3y0StfN -Hl6eWlieZDVfW7H18Hd8DmbSyhikhus9MxfGsqZMwW0MpE1m0T3BqXfPzq+sQTmet1HnHycx+Iqv -S2O6snRganrmtump4RnKNi5TfR4bY0jaW6l741xIe+f2nNHqzDHNHRuCa6omeO/Bdubu6J5N89Vb -E35pY+x4bRrdc+RLNbG4gPzMiTzPnqmsmbO8jcsrj1jLzWctW3hgRpzliLMccZbD6oBaXXr+OVUJ -QrbiDfcdWlyYWQPRbiOq6p1Mv+meDDxK71yecyz2yCRyUptETnY60e75jTz/nxMYZGppdvng/Ew7 -MyltFiaZm12+ZWp2EL0/QdHICeIsuV5E0mJdLSOR2szuHsArtpuphNH3zGjorWqjUHB30x4NSLHQ -8hnlDTMa4LvU8hkVI2I8IsYtmOLGiXGLJ7dBOtx2aWVDtLiVWHFDtLiV3MWGaHErZzSixaNatAPZ -gFEt2pNtn9pP5jfHlrm2PR7l6jq2W72JubqeI3mjW2m3HCXrWocUeXubM7pvllL3pOIAMOjnxqnQ -7r6ThDzMy2Tatj/NhGFtF+yNYTyHB7stnaD3q9Xoo0lTgwMZns51Mv6v6V2EtHDuMs81o5dRcp4T -NjlPy+XdETVvuG4nWTabm08ExcvGD2LLJ7ix9C9jbcSTG8v/MtZGqXxdCWBORnPtSe19CRsG6OL2 -diYm3jgubLOAs0Fb7QlBzdbng3kSs/mqlZXIN5PRT3FKGxNEbByptBxrrr/4MwTU3L40tXD4wPHK -vnv9ysL0rS2+8mvRHC5P3dlKutOsOMTRvm1NCijdaaOlpTaRNd8DOIBvHB3AE+MAjrtO1mnjBDd8 -BPeOjuAJcgTVyYAEj77uDfiLOwJbcf3sUhvPw3NC0hwpc056ZQ4ZIU7aS9ZyLLIp4bRtDvJbjzJn -5De8Sf6orb/fm+qVugFEiNt+7dxcC5do+DqP61mm4a1oqpUVpDZSkq6lUxoVpSMsN1RRum9dXNx/ -19JUK7m0jVekM608oJtTka6dc1tjRbqd196gsn1vWNgfK9MhzABo302LC7eEaWF2ojGG7525a3Yh -/WT7tpsOST/44W1H5u9cnIPTvTR158zh71qZ6Yx1AnB+JowhSItXbM86127flnXuuHf7tpXt265c -AWbjOoDcHP4ZNyp32nayce11bnLw7xvPXGHgt9HK2BLeOG0KeKOzvNS6c8cUfF9cAe84An+9Obx7 -V4Dd2yk6N3be8c6ssz88+o5bt28bc7Ysx0undMcXpRk3VpvOPMBdlo2r3DIcwATLtfZJWwIWqiwI -WCjbmabvm/Esc9DW+wDONLRVejxTWQ7AbNy7HGA6dBXaAKwY91nmuQPtx73JFMDNuDOlh8a5CiOw -DoA2fOwKBBbjNrMFP6oMj5rAHnI9XnrsuVTj3mcl9+ByAtpxFd4ysFDOALAcN8bLJOpwnjCMIS8C -MIwON6LzbdJY4A6WhB6Xj9sSlsGEuRfW0+PceFlmDoBhakUYMD2u0GFRsAdVhsY0YBMeokoA6rAl -RYFjMGVYSNg1o9x42HjLU0Z4kcIbgTRgGwZsHQ7DjhvYLAC6cZ2XOQDz8bIwuEcurIN20ENWhjHE -xzk/nmcGe1DjylscW9iuXNE6hEXLDe58mYV1KGAdsoL2m6Yc4C7LlcBVR2A4Y4BVHTgVO9BhhBOx -A40r0dVYe9PTa24aRhCOgYIjRcNVMGGf5eNKG1ObmFfh/mW66F4Fr4pxVYbNSpfMh80KZ9LUFtdr -DTfEJDuBY/A6XMJw22p77HMfjqLVtdPgi3y8yHFt8OiEqdEYuuB43RqA30aNXVgKXdYOZZjUeFbS -HsfjmwL5rNPjAK5ynVyMSeq5DNusytrtCtdyvCjyonYPfRHurza6+9JCzyqDlU9ueFiWceMLU8MF -3pjQmepBHN6G1VLepEjGOxsOMI6rwkbhy3yJapgLwCqzZQ3LATCcOZOiQ4AVpjRJw9iBxcODcG4Z -ULkiSJ7L011WWIIFbM4HGnu1iDjLfDzgE8dLi/CsJLj2TskQiqxgYLiLAsxKTUDlc5mYcwFPlrgI -4fR57zq0WtoaGEa4zOEi0bbbQGloc9R4CUeIegj7UJoM0W9AVarEtQ17HnbE0j4o62l7w+aE7aZt -zOBW0ezCoaabWTgdNwIOvSmh24CSijy3eAVDDwElAjC09OHe8X0txwOO8QQvS77cQAI0PM4GulMS -Vo8oIxxx6oFQBhws7Wy9MWya0V3durB/xqmeMYRhaqdMbcCAvV2mbW1qAeOEk6tN9zoAsfA5nqdq -0YDkFQ4PVLW8QB9zg8exthdAS5XN6xsXUGmYWuZrWwwUOtM4htp5QNKPZCg5PCk/EI8ZAq0ukjM5 -IT3wbsoZnozweLbpvAMs7KkwGp1G1iOM6wBwLNcRVtS8T4BCtQ9rN0/4NtAzaxB9FXLBw7uyzDUh -7JKxV3gb/kcy4IHUCY4JS2eyvCC4yhSeN8DdGaAIwH8mrCQjy4Cpvc2RbpjQIisYkQuJCu/oHoTd -y1XpCSeGzePDErZ5PHeCOgINxCOfhzl4AmrALHhnivFwNvHOlGG4udyZgCpx8NjWMnUIODwsLPfq -Hd+6cMO1Qxzhw9oWQqEQ7osEPtkHPtcA95k80WUWSXXAq3lKTBgcxmloMcP1D3/mtB2ZtbhoBXAy -RtG+qayMC2QZuxtca0c4IWBxjfReBZSRMaLoORF0YuD2hZ0scOouHIUs58PiAirTpWckkHlkGG24 -iji6whZhbxTd9TAm5TUBw2kv+Z4BEVF018OkArfNPE6hEMUHfikMC29qIC1MpVwG6MhU+MZZmDcg -hnAvlHBJRQGXLyCZgAAQFkbjkecOqLoMxFmGAKhfFYREinAKsHEe8CByvaWLGCQcnLCLuBl5QF3a -yRBK4Jk8wYE558a58gVxRFmQHxiY+ZLZJEQrPIZwOI3mO6MDg9ehtdHESGTAY+aIblxAMjlxajYs -ZCljgH3xyFmGni1z3g7OjCvpIjGnFiYfNrmki1TkTjBegHtvcppxHhaDG5e5k3sQjloXsAh42+VV -DwLPApbgx4VRZoZxppduAbNY5jgCjhbMH3ZQKyS4AUXjJYfzFBZS50wukZoCMBxIjXIR8glh/5l2 -uEBziG0JJEeVOIZwtAifQLeOGP8A094KXXVhHbmDQtgLOA0l0RmgZB6lpcBjWRtHYJxj/gZ4B+4g -wH3kewJy48aBcAjTkpG4BcAcLwoAK6YbzrdHUhd2QuuMeRSAh6XSvEO0OA4QQFEyLFOegcqXDAw4 -IS6vYtkhwLldnmlH/KQjRItftrkioC0yW61sOGBMz0QUCHc9UANNuxuFnLDldH3D7trcuaoHVYjM -GQhYyRIRcESyD7msTKGUImDmtJwwC/KjxscFUlxmlgesDVIG6JZwULglmUcUC5S6iENwgFmQkykz -OigEDHeSeb9C89pUQGCKq+8L1BIeJGAQOj3h8yDt6I48SYTbdAThfCE3BldSG1qxMFziSgGBCGuC -u1XyTc9hsvF4ENsEcKVoDBaIYsF01DCugANWwNoCMEgagivCmoush+w+HS8bhOksNzQIpbEHE5Au -H6/AYIjEC/yMiCEo4NDUAK6dSuAAzAK7jDoF4CKLKB4D2SUWLVypEpQrxOQFOuCJRodeFMvzgTVW -tJdKyy0pAnZUOeMKS2yGCzJZEKOgA1cSQ9hIvISuAfcQCJ3rpmvASGuFV7PqByhskHJ07YkgBQYM -2z066KDsnkngSAJh7pqzD7xqliNTiQsk2wwMj87ydDUnI99m8YG4JRmxV44ZXto7IyyXQbEX99kw -giI46kdk/wkYniZHxRthz8IZLUjUh2OlHEvqQXYoawcQgCoTYHVafWD3SkvcYC60PBCSgG50WbsE -ACRFSv3GgKhiS1O/XgA0uXK1i5gCqzubQuP1poehlFIhAg98bG4Fa4B0zCuWEWZKMAxyvyRAVagI -VjH02oO3AG6LXNWQHDDV2pe2hg4BSFwc4c5CxG5liC1IES10G+TTsoaSYbDa0sxS/I2T0DjgCtkj -UJHYIFurc0LyKeUAeG6Nq5EZABovPBLRI4C5gggd064JWQGk5xWtmxR4OBOqRhgRWIjozyQUgU5Y -8oreegU8TkWbiVUPwLDFukbFAaiSXRCSD2vrtNYpewD7kOdlnY+AIeRZ5ruZDrgl4ewXNQ7FB56t -whbMy8CxMybjk4Tj4jNaEPPLXJKm45gD5rDCT2nVBUyZrxQeOTUAmsLIDhHzBc9SJvfdDCDck8wZ -lzKLMF5SLiVcJVwTXZqymwUNDFKYJ+kAK34VDqEjeldxtkHSygvVywYHeMAnvs4zA65k0bHirmGv -PeGrGise5mkNaiUSvh0F7NLVOfzweVYgaauLA2HAAf8VNdEBMLO1pi5jhMHChvcIJNCBwp2opBcf -hDqr87qcA7gxV7CQdaEIDmShEeNVEpTXYTS5rolaDeQrSmxhyMaRtFMGRBK+OC8LpAvL9CpnFgru -XmkZmxpPK1yphIEqR0EjHNxAyIpaW7hPHjUSSaeoYcQpywhY1gkITFteeBtYDJbuXEb6ljBPp8ra -EHDZQSiekGMSqIfpagwaBjzWSbe9qxAXKC+ZS43yMi1QINKlxjNYCddgaHA6FzE8zLwLSCI7K58C -PchIe5uqA8CQQtpevKF0x+FpQVZmTYUN80EgIG5ioFNVBbBHgupErQHMTWbEksT6D+DEFNlsQFni -o1YrsHNOeS+KFRKOwv2xpqhpYIBDNLl2ibpmQoQbWF+6zQHlaeY9EUNWiqC3UeNw2XJvalqjsBmh -CRoEonoJhmWzTIniIZeraHRAAr6upXBF+FphdE2f0bCZtM9XvjWx/JGyLqA+Jo4ZnIxoj+PjB5p1 -EFkIGAi7ShoTsPSWrXSljnIjcBlONO54gMZQixhEREuEISwFdqstcTFIA1AeY5NcpVkPq+BIvgNN -Zkm6eReVKcBHhmNG5wPRZDx5hs9CWOWSjFl5eEukEFAxW5FCt6XPSzq6iYDVBacpAzDLmFvLgM2I -JjmBg+RPetpwyrUlNRQYCDXNAowtXjbYRHxfCAMDRhi8KrzryFaAuSbw0HSig+TnvKVdLzPR5yMc -NcAR3giMJrkyp7EF5A/2FjrqSC7x6BSmZNGrLAtLlMxG6wFY5DwqG+FEy4H0aClBbVxYs4LEnhIY -Aa2Zn47WC4Q7xQx1ZphGApAIqqn14GMPRbTIAdjb3ral3OGk1xzxUPcQ4BgUSsZblMwp2BIVUNXM -kMGzpe9eBmCOwp3N0yUDK4PNaSvj2oKsEo5AmWwE6ySDUOhVfYtBaau02Gf5MIDWNsusl5MT+F7W -udbhZFjpBbISFQypWV47k2AKCyTd1E5vCuSjHu1mJpyS5F5MClybMq9dLjDZZIo0/w== - - - 8RqiRrlEpU7tzuITnSprF9wD/jbI2FeoAMxGyLt04Q2wMZXEsFRIBkxypA5M0BGYrugS1XEXmtTC -XawhOgB6m/kaSgxUK+ytLZKW3AOwuFrgxPoBsLLq+cJEYGTjkV2fkB5wvVGW8SZg8MkI1z5nOHDf -DMzEiod2Whpw6YgDRrcAOWzh0irnWEcVWG7kx8KisYXIgc2M2GVjSUOBLHsBOgc2zAW8qpDTC7Ie -CzpgSSWWMDA/qK4jWwIrfcK+GbharA0IskBJvKaOu6HBjE7mrzDywolkHBjuaCizMgRwDikQiwO9 -ZkUbmIUsKkEDU2kz8teIeAP4ROxhQtiuvDSm3hh2Q2dd3YazQ+b5+hgAffoclWrVgFHj4moTA/Yl -cG1l9yogJSrJQhqXDKhekH2LdG3BKBcOU969D0BOHQnB1aYFbhxMcfXtRTrvy6L7LCAcCVFycFKm -IB4xBOrSJ+dxQnogVBvP72SEx3PNhx1detg4Fa9FAw/SZZjLeaMAizk4NtEwF1jBnPwN5J4rsO0h -PQFnA8Zi4W3uPFsT0CUgCuGZyzzBrWJ9QQHcvWaWLnyLNT8+dEeeH3Co6YrpnG0HIHXlLGSCK5F3 -rLdxmSCVHLw1MkYJhSUMHS6CLUgzraOUWoANtmB214uWEi5TkJp5WxQTiYBGfVlyr+yCA/g9CJ45 -S7TAVE4I3lesOWL4ZB/4XAOcrNQARAyMThponY1EReBB0LG0mAXsaM4uOwZMBgDUbI0EcmmjUih3 -jOSBu83ZcQN8VUrNbila5WWn8USkhrnATuQkCoFIHe1y3lpGAqYkfjbw4wYHB2JlYopBuoVooQD8 -Ga1idIYKsOflpZjVkHxitwF9ik2LiRWYDoCzrRAOo1jQyhKnAj2QAj6M13rS2gZ0QJpKuNdhcFFS -RNzvCZGUdITQjEcYEmR9xiJBUhSvqIKEtui/JB42oG0jeQVOmTesADWgBSSgNdISccu0iLuihfdk -/RsjqyUxFODVpAkThlk69JJAFx8wak2IzSBw5cyxiRsX0GdtcrpJwrGBCa4QDWiikoBVMWIQ8iWb -OMLGBu6Nb4LYDCogeAgmHQgYfc3kaWFnFLEIufQaDkGhS2IyylJ2AuRvR/6GGu84iX2o90S+Q6xH -4VzY0rKKrKxsAxb0MlEblmsxFGknuF9rMjmEY+gZG4SjGQ39AFelMEqiGwJSVjgWn1GlR2MImJh1 -ishIRsucY7WiWPDCfIVp0Wxt9CC9syqkYrvRyEQbEC59IRY58GBUsjGFE6uazTUbUgOCFjudC5eP -T208XU6Jl1UAi9kGgI7kTidiPnyfSB8qZou8mhP70JRFFAcsmMTJtulTOaeguxsuU2IUBAMcmSY9 -0BJSr8H1NnnkbqwAS3agKVDzMy32KKVKtuAVYhNT4EvAemxBQOGGBOZabMFWRcOxkY7BkRH80+iG -BNTDjF9ZsLmzAoazY/NoVavgDtEgwXJ2ifEkm/CjfMGakfoQyHaFmhFWMgbcSDwp4ITK4ErUDpWf -WZ4cEPZpDHCr5YY4FgLhgDm5IYEQswRolUpUK1nhWIZDysJmOdBVZ2xAQX0+SbgKzxjII4WIvQEB -lSKMlJU/FcIFU4hIHvjXcDX4kqBzUbTL+VIodBhKyUxelpNKC7w02VvYi2ty2E2bW1EQFoGv9aTj -zsm6QD2gAg8VcYaEzl7ildrlPDm4pWQN/ds8Xs6qFzDLObbkx+eBKKgIMdQGBwyI6Z4J2OWsI1Yl -zhnscqb0cYEEEwC748g7TVZzUtg2VKbzlrBVLKxRFjePhSiw8WjRAWgb/f/Q9oNaEjkBxLO5qDAw -gRuvLHPhC6WcLSNGqaIgqiKHEIR46wRWHVjQ4ps883K4adKgkS6IsMVrgEYL8rKr3RmAhytYv2Bo -WCtLld7EFJbe2hQerzg9jbByRAYwWFbuAuYAKTnapcIh1jU0A0tTFIWvISRYR+42RV5ohGOPk4jo -AOjZKTCiROC02afMo3wTTWMm3C7va8gWeghyRF5Dy2g3zU0PDscdKsVmoaNhrvIBYcKAHWROdRER -AJd5rmoEB46CWHkiaQJgrmQESMeiYOBslhC9aJwL4qSILzoeyMC2lUJF8e9cIhQqagvWtpLUmUCa -fS526tyJOpOJOLR01R5Urn2oDC5tjT1AV8TM1RkJHIP14lEbuQ44CdYVvsaigGFO8IXwMnDu2T0N -HElhXJVNrNReeCQ6HshiFka4KeO7gAnnlYIjl4YWOKVlb4jz8sh2qh7mD83fRa5qnCKMl1RMCU8J -l8RbFBBrDCjY5ZzNdZ1bRTuQU3W+NuxUmZW9TDDYJnwMA2COudRRcKx4azBMGNXAiHvYQV3j2j1K -wk7V+Xv4XCH7VBcGQMFmdF6XHErDLnKpjAFmfkXWuppAUoonZCK9oNGSfKUqOQeW13tUsNeEIrjr -5AebSFBgmEOFaSpr9RKwyjBnKAbBgwO9Cg+oDHNeydUQ1yJQvdmCr3YGfuB1zTCoM/PE87rIhPGU -xtGzMO3WcwxCNQYWdgIGK1EAAaUZ2I1IMmJ3bFx4Y2pjwIUvreh4Svyr6Goc0DC5Iifd9q5DJdSi -erisBGa2zIEpX+macI32hlIZEcNpfglQJR7VAM8diiw1hQDGOFkjt7RkGwk4YIj3fzhF4owULiku -Z6KrAMtchezETzKwN1bL3WcNCDBjlmw3qC3JRbcVODrtxbc0PJYYyIJtvokSBh0MyVrGChvmH0t2 -mgWw8Y7Y3bC03plEE/Q2YfoDWyIxKmy6AW1fToaBqGBCTtP6qHuwYrqBoBFn64qKgNnBvc7WVBq9 -u1mZ5sLPzn27r11aroce7twHMYP7tm8rO7uu6NzxrWkAIYf9NUUC7l1ZXl5c6KjOjYsrh2c61y3e -u9AdBUjBfxjZN3QQHwi/Cr2VELV7ubAVGO6YIt2ph67JKQDwvRZTelmQNxkDpwUxsz9m1RjJkyJn -hdhtAuQhcAcCBjKADilprxAwhZrLdAgRyDdexps2jjNLuu1dhdDB3jtxNfG/vQfBzLrrrQsQvLq/ -c9fS1P7ZmYXljs6u6IwBrw10B37bTuHAZQOCL8twqTmekv7de1foJRytDrhMgX5dfo8hlDEPqawh -+gF8x/fOs/50797Rfm3KfmEoHkbE6oAALThThW1TgTUM6Ld7u8CdA0hB4O0MqEHl95h8QNPx1rEX -aK6DhFHbNLTQZ+CyR13DpcQ44Otn52auSWOCb506jNHDAS+EZm/cvu0dcTzIByJG67yzE5if8BOu -8MG+TQBEzejHdyAKWDlsHSCdLDx4Jz71uqnlqWs6JrBA27fdccO2Z9fyumzrdw/5s6ZuN/fRR2kw -GxnAxkeyKU/fyDA2dwDrGMbRGMCahnH0BjDkMI72AAYO49gMYJVhHMsB9BvGcR/DsR9A7zCO1xji -MI7jANozhuM7gNHP6Gf0M/oZ/Yx+Tqyf4046W8I/jPjJ485at1C+OPbDaBzAsRzGKgM4NsMYOICj -PYwhB3D0hrGmARyNYaxjAJs4ko08feMj2aynr28wa+rzlPpry5ZT42vLllP6v7acuvWMc3a8iF87 -zjlj66n9mp96+rkvedVr3nLzLfi6+S2vedVLzj19a1PrLVvPvvjVt86/94Ff5NcD752/7bU7d5x+ -akOnLxp7xw/9/G8/+HcP8+vvHvztX3jv7MQlZ2/tarr17EsmFj766b//2hNPPc2vp5742j88+In3 -vn1XV+OtZ+96+3t//a8ee/LpZ6oleiY0f/gzP/EdV9Qabz37iu/4ic88/I2nu1f0mace/dxP1hqf -evau7/jJzz361DPdTcPr6cdC411nywS3nH7J23/ic4/1dBob/8TbLzmdl27rjon3fubRPk1D40c/ -897rXkSj2HLG5bOfePip/sfgqYd/bWGMRrF1xxve//knmsYqE/zGX330HRfDKLDbr67SLQz50z/0 -mnNPHaLb8Hry73/+tpdsDYuwc1C3oeOv/fb8q87YsuWc17z3wQHdPvvMEw++9zXnbDl1x1se+LsB -3Yal+LsH3rLj1FNfdMvHHu67tnEQD3/slhdB218cpu0vnvxth1+zNezFWvZ4qLPz1Fc/MbszHOAh -zuQzT3z+/W/YsXWosw7dXn7GlqHukHQ71N2Uboe68xM7tq4dl6yGowihVTgq4r4nuls/04woA079 -xOe/GrDvM2nLr36+EQFfMjH7/l978B8qZA0tf+39TYj91NN3XH7d7Ht/oSICf/dgaHnd5U0EY8vW -M3bsfO1tFXF54L3Q8oxGQhRap0Qr0Kyd/Vpi65QY7jinmbglzbf0p7E7d75hYT/YlLdvu+NNVSZs -sW3fxAbxaw9Fh5U05XSTe8F8K9wLWuBkMZSHQ0EeKZBCIhsHZy43DsFeEGoP6YVpscMvZSGLpoHU -PMpqcD8zuUKfhzFKDRHGpstxi15n8iZ+QoFGOeVEKij/Vs3r4a2Jg1NfT6hdY/m4KpQp8yTP/969 -105Pr8zfurhcFZaNTlPg3oS+D5V7U/i9f/s2G72p9vU5Q/UP1neKoI8NniPsYoMniaayobO0c9/C -JuxKLa1+Z/dNi8u3zkwvLu0PJxI/H3Tcdt86MzV341R4xn3QvLNr4tob3shH+fYDi0vz9Jmcgc6u -a/cv3jmz79obyn1h4LctH5mb2VcNQQYVqwGEuVx7Q+faleXFDuGf2XcnRX16+7z5zsMzS/fM7N/3 -lpkj+6jV4fr5C1fu2gXww7Mup/O2Ij/ifsfuOPBfb0r9MQy6c9F/0+pMsQtXAodEVT46s5YFJ0ST -9HuQkrOkhFUMjPkXMBy41hgchjNMR5B0mwB5DNPi4SrwPM1RRN3CTedjGYcgsJj+hoabNq3mVfXZ -uwjkONkXVey+cerw3fQ26+zeu7g419l1w8I9M0vLM/t5iyJ8Ym720KGwd93w62YPQ4FDaa8EPjm7 -cHeE3vEa+HeFvghUaue+mrfWvl5/rXDMxGNr577UIcuVGBnpOu/sqIA8M/bZ6tsIQdiQ/vWJP5o4 -be2ruW2pvECvrVXJ9Smn/M5Xf1ve/v6TgXt58g8IzMwMfvgV/uMf009+B9v9IXznqd9f9REwsEj5 -wzK+Ka5VQv137rup3wbH3QgXsXP9yrvffaQDW16/fjv34QGpXGf33XEVIY7QY4Ilwl3u/TKto2YX -vNX8YnuISsuOZYpRTobjefWeMXl71Q3vWp59yzgdqbeu/ND7f/i+t10Of6h9//YDP/aB75su4I/L -blv63h/83sNvvZy+c+3kzZN7r3xOH8/uyi09XJFiZNaPAnV/sh4ahH1skApRHxuhQzSTDVGiVbij -Y8BdDMA5G4sfuClwVb0VhISFgTPS46282llclydzAUmwOGgC372zU7hO4Rt9mhsao3czfoH+Hejd -bAPHvXbvZnq93vzAwJ91dLvpYzgGo1rfMDZ9SBsfxmYNaXNHsu4hHaVhrGM8R3skww== - - - j+fYjGSY8RzLkQwcT3sGc+xHssp4jtdgesdzHEfStsGk4znuI2nnYI77MNLxHPcxjAZzYg1m9DP6 -Gf2clD+twjOjwawymJaMp4XMVUvG057BtFluOo6D6R3J8RpPv5Ecl/G0ZzCrj+RYjmeYkRyb8Qw/ -kqM6pHUM4yiNZyMj2cQhbcowNj6kTR/GOgZ2DMZwtF89dujKdXLV6ARqe+rW004/cxu+zjz9tP4B -CtT4tDO277jw4st2htdlF1+4Y/sZp/X9Qmh81o6LrvBvuv3b3vHt3/6Ob7v9Tf6Ki3ac9fx+vqRn -7rjEvPk7Dt//oZ/8j+H1kx+6//B3vNlc+uKzGp6w5dTnb79Q3TR//4d/5Q8+8+BD4fXgZ/7gVz58 -/8K3uEt6XVDRqfV10/f/3O997n88/Mjj/xxejz/y8P/43O//wr9fvCm7YFu9/Zat2y5Qt6z86G9+ -/iuPPvEv7F/71L888eg/fuEPf+7+7ywvPCttH1pf6L/z/o/9yd8++s3EdfeZp5/65uP/+PlPvu/A -NRem/Z96xgV+5n2ffOirX+/1Cv7mI1/+vQ8eKC84I7rlbjlth/rO9/3ulx/95tO9HsfPPPn43/ze -+75T7Xi+dL/1rEtvvv+TX37syUaX6meeevzLn7z/lp3beThbnv9it/hzn3+kuTX0/+hDH1t+3ct4 -OKHzW//9H/7jN/u6az/zzYf/5Eemdp/3vC3UuT/0C198fBWH7ae//je/8b1vfDl2f+pZl37Lqp3D -cL722Z945xXbt+KymPmf/8JqnUP3X/6Vw37HaVtO2XL6hW/8gd/9ymqdh+7/5eE/uP+Gl5++5ZRT -z7z49g9++pFBzvGPfPZHvvXSbaeecuq2y97+4QcfH+DQ//Q/f/GB/VefszU03/ntH3nonwc0f+Yb -f/OrK+WLTtsCzf/j4OYw+DddePqQzcPgP/PB2y8+89Rhmz/2uR+749Lhm6+p99rY17Qya1v3te3q -Gs/MGk/kGs+7XNXm2CjpvLpNhAh+62+/3ndxancVRq+nf/RPvtpvOF2YIKzlha9b+dhDfTDHM08+ -VsMzAUVu33nL/b/xpUcasNgzT3/z0S//bg2LhdnuUPv+n99oxJFf/+pDn3zfjE9wJOJUty9i4Gdi -z09989G//ZOP3f+dvoaBU/z+yD9/gxE84PevfP43f3TlFtVLD5h6/O5nv/g3RD6Qevzez90//bqG -gAehTT/4Iw98nIgT0ab5m9SF25/fQC2R8hVvfNv+FSJ9TPku2XFmH0oZ6OoLL7z0aiKska6uSohP -33bOi4hsD6La+IWEKRjIE8SvDMtxDHwNju5YJbQD3N4pMyuWuYXM/vNdcKiWwiU8fDZO1XSg/kEe -S6Fg2SIBxaiKwpSm1hSc3rnuStVnApQBxNyVUk7axNzHsVvwPMqdL5MRRFAMLcGxpi3jpJI+G1aA -4zqaYjmM5lgOD+s4Vob1HFeZSd0uKUslph7NsLBdATnCFf0uvOtUn2DCXEmMayiX/h3zPV/GctGr -f7uA4ihhtfZO176dF1QgbMC3oSId+PbtXc+zISMzZv/eO10PNoHAEG9Wc4Ec1j8fcj5TEdmuQxrh -UL2aS9N7JcVhsTAr1QiDM0IlPwQY65w5rKqUNIbE5k6pstZtAuw6pwLH1OhlqTtptxCPIvU94xgi -MJY+ogGnjePUkm4b1oGWXI0WbA0LNkzMlnJ0zxV0no3nBgolQiVFyB8Pdad93nPls3GT5VAGF2Kz -ylxVv6tPyGU2s3zrPBR9705Nm8WeJTwlG9dZxlVwrcmkdDAkMsfiR5CuW+eZeNSGaym1rou4mTpn -N10NIUgxx73PpC42Ju5mHOuMpV6leDwCvZEhxELtCMdVhwIQmhGy5YrEuD98yqoxQFk4HyMAoYCo -ErAphM5QgTWqiUwliXpXYWIVPG0xq3AJIXeIpgM1cLo3mXD4EGYOVQ3HtKFiwIzlKFzMx5Ho8Hit -ED9D+BTgNkiQvvq3tKak64Abw7egdsXgL4VJY7WLvWt6FGRnV4Wto+E2hUiQs/umhUp4rHOjbOed -ncBeKMm+3LcRgKgh/Ts4VCK3fphQiQGvN935rqnre6DX33XPDz7wa7/18f/0777nyNLMGyJ8YuHH -f/2/P/QPjzz2yMN//Zef/qP/9h/u2sNf+IGPf+FrT1YiyVf//D/d5+GD4r5f+usna4z/1z71wZux -rx/47YfrIsFjf/bBm+CT/L5f/qsn0g+e/Nv/9q+uoef8n//5U19+hIWbZ/7lf3/h4993A4/tdXf+ -q5/+zU995i/C688//ce/9mPz1yUTevNdh+898p73vOfIvYf2v+GUdb9OhKCQm7tx9OqRIQqZHsBU -ekOYCu5zPb4EGd9V+KvmsG414hI2i0somUuAyhPAJQQeeQxlm4B2oMqS6S02MB5QOTSD0kRYBqd6 -Ix9RLW1rmNYp3RXIfTIjdaUEXxcBLatmpF41wrATbEj/DhH/ZouhAuA2/lp434c/8v/Ord5m/gMP -/Ne/fuypp7/2pV994AOL/Vp91099/rEKVz/+0EcWGvv6yBe+UUf3j3/xpw/1NHvPnz7eq4b6+p8d -6Wr23X/RpK569tnP/ut6b32aPfvsX6Y9Lvxpv2bPPvun81W7n/p6/3aP/lRs9n98uX+zZ5/9m+/h -Znf/99WaPfvsp95F7T74yOrt/vcHqN1/Wb3Zs8/+F5rsXw1q96Xvgnbvf2xQuyfeB+0+OiBNTaDT -H4V2Dwxq9uyzPwPtfnZwu589Gu2GGN8Da5nvB1Y5LPR6HDdu4QuD2n2BjuAvD2pH+3vKjwx48Nc+ -RO0OPbR6u8/Jjf/w11Zr9k8/Luf+7j9bpdk3f79CIP/6s32bPfnH70ku5pG/7NfsU/+qdtG/57ON -u/LNP643O+WUez/9jd5m//T77zml+7X0Uw91reOjf/njjTjw0E99MeFWH/nzjy41tcI+P/RLX3gk -cMNPfu3zv/T+XtyXvubf95EHHvjo+1ZvtAmvk5qjTTmkJu52HRxSL6Ob17iLEZd7jLlc61BCgS3M -oF6o6aDaW0M93H5qb8guM26gOqf8TpUWYTKmxMqBcBRKp0b87QnI356yZcsQDjNbTt269XnP27p1 -VVPXlq3PO+20088Ir9NPO+15/VKdhVbPP+PMs87efs45288+68wznt/YcsupodVZ288974U7Xvzi -HS8879ztZ4WWPUY5cM4565zzdpz/0gteFl4XvPT8Heedc1aPqQ+anf2CHS+54MKLXvHKTueVr7jo -wgtesuMFZ3c3BFef886/4OWv6Fxy6WU7L7vs0ks6r3j5BeefB8bDtLvnnXH2eee/7KLOpTsv33Xl -lVfu2nX5zks7F73s/PPOPuN5VYdbtp5+1gvOf9krLr7sVVdePTYeXmNXXfmqyy5+xcvOf0HqEnTq -aWee++KXveKSnbuuGt+dKa1Vtnvsql07L77oghefe2Z8cnjq9he+9KKLd+66ercK99aYItfZ+NW7 -Lrv45S99YfXkLadtO/f8CzuXQbPCWOe9syZX41e96tJXvgw6lCyhp58durvk8qugkKYvr7nmmnCJ -bWh45eUXX/TS88II5bHnvPhlr7xs11iWh2Z79rz61XuuKZ3Js6uhwx3b+cFbnnfmuee//OKdV41r -48o9r35teL16D5QLH79yZ+fC8889Uyzv217wkvDYq3eH7q559WtfF16vffU13ujdV11+8ctf8oJt -p0m788LwXnV1eGzo7nWvf/3rX/e60M7mu6++/JKLXnJebHfWeS99xaXc7rXQ7vWve/We0C4M8JIw -kT79hYb43N7+aHxX7dYmjm/PNa5nfDjfzs4roWZ4ec2rXw3TDd31zFfWLzw4LMw1e8IL1k/vpvU7 -RzZO9iN0GBpCPfXSwzKPXbkT9uPs07fG/X3B+Re+8tJXhY3LCws1620RDkL3/tJ5eXnnstAQ6jCH -V66ysatedVnn5S9Jzks4f9vOffEFF4VjetVYoBAqHL/xq8NB7Vx0wY5zqvMXOjz97HCeQ8PLd111 -9Vh4XX3lrsvDwb/gxS84K01tCLctXKOLOpdcdvmrdoXXqy6/7JJXvjw0C09NLhJdyxe/9MJXdC6+ -9DK4bhe/8qILX7oDm6UXExuey/c3vF7x8gtfev4Lz22+6NvOeUHEBy958QtfsH3b6U2IA/HLC87b -Ifhl2xmn9TY7hfHVtrMCuiJ81dyKW572/DMQ/z0fWvVHlVu2ADoNCHWw68CWLcMg6A2/ntP87Vp4 -o2YNrl81deIg3hb4zpLq0QtbW4EUVJBHrjYft1jXFxhVLGfezdUicFpKZ+e6cLXGUC1cOywwH3tN -YTGZZgXy46UizjX2V2qqbl57uACZnY0jTRonU4q91ma+isMGatcVVB7POqhc97ldlW1V494FjjwH -FrVUKdvqwcpKs3NlYFsLVXPWCN8141lAqQO+awPLqyxbBKGGNBQ5Hu5xe9fxOM9lqp87ZkHKqhmY -51x3dHMGtaQRgKgh/TuYwz4qDLa96W1vmzSNH5mb3jG98H//6Ec/8qF/d2j/t99suz59+7/5Dz/7 -y5/81Oceeuhzf/Jb//XnfvR73+mTj/ce+fAnP/ulf/hfj3/zySe/+fj/+sqXP/c7P/O936r50123 -fP8vffYfUs/Hp594+MFf/8BByvZ2yuT3/+pDj3brfb7+pd/64W/Bj8cOPfDZBtXp4w/+wmHs4HX/ -9uNffrL386f+569/37Xw+esHfH714s809f+Y9H/KW77/4194rGt8Tz/217/5wzfT+K+85ft++c+/ -kjqaPvnY3//FJ/6//Vlcn/f85G999kt//0+PPfEvT37jsYf/5xf/7JMf/TdvVcn6vuN7PvTAL/36 -H/1FWN8/+sR//ukPfPe3p+sbXsWNb59e+L9+7KMf/dD3L9z59hubN9GH/X2La/xova+TmvA2Etuh -cHkvmS27Se5qFLeWlq9Rd9T1ybq0R5QueGP6I+pjYxokSsy3MR3Sapn5BgPGSpOX2qjMd3bfEDZ5 -1+1LUwuHIdHwNeHN/sX5zuGZSDjCSctVU6LjdXVl+/VEGQI78etJ0uX4+FXa2MFNVDZEGzO4jR5i -ONom9xRTH96wcHh5amF6Zh/gjn03XBeu7qFNW9U4tU3oymxaV3rzjk3vegL2e8N9M9Mr0HF9dY9t -AuziKOap1H3yVGbd8omOqLU/qgclpxsP/Dyy+pxvuwIBzrHkTejxbYnIDD525E2okr+5FIAdD+TA -1tsFZtsopWs9VkB4Mn25AkE3TpFvnfRY0MdOnhz/ZqlHRpi0S6ZX9Vif82pu5ld0KqfoOnkzOqBx -zgU6Zmz4w9ha10aVNH0QTrweL2xA092ty3LcGqeT1iAf5OPGKdPbOvStyqxMW4e+tQ70KSxPT+uc -4PXWQSAxmerpm3cyaagCjVdlb0PInm+GbOuKIEaS61/Sus9iJIckXYwBwtKu0DSItQ== - - - zg9bJ0F1rgMfYfDVz8CHUytjve2g3OYKg7/LDBcUHIGUgzc6C/POG73iIQ1rrpSpXaAIAr/QwpJD -L5REwKoENrAZvjTVMea/+QLBV0xXO3zr6z0mwOoCCcjg43LTSXvEIAyVmWrD+W++QDLCpF01l6TH -+pxpk9BeMm4LX9ZWIoKC8GyVIb5Ij+feoESNPt3xAMa/eSXgK7mvt8O3mar1mADjSkSQoccRNyU9 -GhyYS1aC/+aVkBEm7aq5JD3W57wKKuHiIwrPPQWsZFluBwSsFEXgB8cKM67LgFPSuA/rZXoqDEZn -vRErxXhpvRn0bePGAzoWz+hs2IexT3QRLh+mrF7Tt8y41xnQgNW/BY75ZTjp/C097soiG/issDeZ -s7pLSTMWsKHLQYm26rchMbf2Wssz8/HS+IFfChKCAU2jOIoDGRq48qDBM96U1VKqUg36DmigjKu+ -4gN6GPggZ8adNbYnrGioAwJXz7sud3bRhV4n1nNGbPHaRxAkNffhyrE4FOZbkjel4Ca8fPJ3ZUvP -S11v53JCe2mPFTC59hGE3XDAnPRYyMcR4fDf0ZOARpi0S6ZX9Vif85AG/Bx11BDgBb/Q1dQDHrAq -HLikHozxeZDqylyHt4HHQGMS4gbtxwOtwkAW2anqXfwQ3VazsHY4OIozGcauP3zBmsYBHpWCNUkB -gGFNAauu8gAlRda5tScQ7lrYzDeHDvI8cNDbiZxm1qZkLgH5nKKJSIke3pZEs6E6lOxNlpkEGNX9 -qshVrTEQ5DJHYNVtApRDn4KgG44IlR4NfWxrj4/AqPCnsaaNk4lW3dZmP2zpLdoTzUfXMgF0uj/9 -gzibwCeCNBjfdPmulIXhM66MIeKCGiuOe2w+5qTzrtTd3UGPAHszyErbuWRDIsjEUlkNolCz0NQt -aODXG0WSJuGlR8ghlVeTONQoOHULWPj1RlGsUWjrFu527jvQb1UzFtlBbO1ML84fWlxZ2N85fHDq -0ExnfnH/TLcyMyovb1DqlqnlIH4v3LyyPDe7MHP94tJth4KMPrNU/87aZOUbFg4sdgvIqQFv3DH3 -b4GvV/gmM0gDkfePLH4Pw7+6NI2GswyZ98AUlLkVc2ACDzJRQZXR8K1nrGE5MBG/50wCnJAgwcwW -KXyS4I7CImudgLGv7H5cAuSxTURLI8HxGdpIzxUcnpERAomPw48Zq8SxCXBCogp5LgKnnquJJ51U -S5Q8LgHy2Cb6rPMkUrgrE01I99Uew+opttCKutOBJ+DtCfAsV47gcRE18IIMxABNDgkpC5MAGX8H -IVFbVW/sAzdpTEFAo7nWig83KmclcuBvo83VjTuVFaLUJetwAIrVNtxbuJJ1IMR850XVQ2XiDfK/ -4cZFqRioQWtPQK+pRk1Aw94L9xTGFrA0sTXjgYXwPOC8dKWgWlUwsDQa8wVkVE6PeyjD96iHsCLQ -86TAXYmW0AAPO+d4fcrSYiaC8LEvxCCelYUmYGDEbLXCAc97gmtXSC1C5VGrDwOypajYVVl4Ahp4 -FvcQxBBYlQo+KXDeaHhinjnefVV6xwPOrWJgOBwlAXNXVYdT46VGBhNWIyuEDQNOidYtrKWU6sm0 -5sUMC1hUsysM2iygr4CWZGxhFa1Y6BUE7PIElZc9NV4MHFlWFAQMxDrZkXBrGe4yOZi2RMkaTr7y -tgeYmyo2OIU7qt8INS4zJE/hXCq5MLBQ3hKw0N4ld4PnUADm5bbWC6xUSlZM8yFhYFxe8KbqagxC -Zc5XwymuhKTDLTEMDDe6qLZeDnzgb2ADCFgaxUAD8V3dQNw17sGAdUYl8LkI1zlPJJwpQogBwWWO -77gpaX17kY9oTnAuhiiBoxs9LyuX5XijBT7ZDQ/Cm1HVQZZuCDzXp3dGlm/dbB31zUt3jXcmDk4t -LXfGOhOLCwsz08uLS92U+MqVo06H8TIoCZQvAjGZr8ML0EKWcssCPlAmz4mIhvNDl8RCug1NRDTn -NCVhYfPMcBnRIPRrIUieZEOW6wu5UAr9q52rVU8tyQ4Jt6yM9B2Os1NdjfM4BBV44FLi+m1Z5D1D -gLuRlTzeQHdK7sHnwh64vBAhxFureRVCw4l4zRAcF22yz2LS+elXJDUsgAun3w6rkt3wOURT+76b -FhduCU9bDg8cG9ue1NxKP9m+7aZD+Jmnz26ZWwn/3nznu8JJDQeYzHd7l1YOH+zcOLUwddfMUufm -IASHMzzgww59OjE1NzcbBJ9DB2enuenti4tzuzt559By51bgi3c3NL2iMwZ9pF+w8IXr56aWO01f -qDUNTHdoS330fUT9Gwa+cfM9U3P1ptRHwxd0JsMZ2Df1kWfdTwibe/NC4PYP1loHbDF3d+e26aXZ -O++cm5G21EffbyxNL07NBRRz/Uz4cGap+QnUR8O3bj84u7D6oK5f2U9DoT76trthIQz9EAow/Tuk -PqD5jVNLd4czMwbn4K6Dqw/hW6dCp9OLc4vQ/naey+z03Z3dV3TG+QSHU18/v0fhLvTZZjjydOzw -wOGfWP8Sfuj/SrDPVh/yeh8fzzA/3cCPyeDH4g8b7o7K06sLQU8Pf2uac5AOCiNzPyrPzrvXvWPi -qoOyS5P196g9PqsvvMbgmPCvy3gT1FGcfIUX6emYQgQP3hhMfGzo2W/2TWm6xzDEOobDsxNQuijJ -qG34Wkft7sh/cHh2d1h23YRbtMrQulBp/+Hp4zo8wNn9x1Yc+7EBieg7oGo4BrSWQT4A3R7Y/8DY -nOO7MkgInun+sRhwQqv6L6Q79gtZJ4r/P3vv2dY40iwMv5/3uvgPJhuclGUbMM4BDBgwOToIbHBC -tnfvOR+e3/52UFYrODA7uzt7n8OA1Kqqrq6u1F3dzqTFfz5pZhuMrK8zfbyRPhCgg4EGEeWsZM6j -lBL4nbqMMl75I3Y8GP41QH9BL1q5Ozd2CkiGnncsA3zsPyXtfSynpGyL3R7oMfoEzL3uIICb4Md4 -M0xMaRMmf3Y5aQAuuX+WbYy7LdM38vBTcv+IQX/0zmSlMYTjumIVww2VPgb8R4/qVtRYXnoL7AVW -/ggEDaBQ5LIXQLgDIDYJxGow2iV1C8S/7Wl34tQj949hZz3Y6N59CEDvvI8DHv1sCTbwRIOPOKIV -HlyNpcKf0uCs3XbmlHvP8ZQ0boSmzR0PoneFtzcwZ6KN7ggINp5GsE5BFfGgas8CsXp30vMhMHDR -lOXxOmYgeNFod6fjGUb77yKbgYk4Zm6ysz1p0F5I0BCE2aeZQZR0CK4yA77RdnfilwgEWdvBbZjs -b5X3X1d5syo4PzsMGGUvFwPPiosyIP4Qo7AsDCapecJ5pGhB+udpzrw8HAUuO4328C+7DjK8dNZB -Jgh+9ZCmOWGhmtoztCjcGsst9ADE5UqbNvD50CNaadPsDXC3Kbi7VGk1HDVaKiT8pDOU/0/9Dj9p -9qZ4pZgw+tFEPA5T8zCvTAMHnGbQLzyXQAUsyt5OCv4LR5JLxCk2sbhlHLd6skokHtpRoyv/nToZ -9I3z2Z+foJa532r5V1bLc47Ar6rNF61gW47idtDVhjohq6bWXgUKjVbHWVkbS418qmqN8on6rbIH -BzuOugIetxo9qfxSA0EwYJu517L01gOe7C3x6Z3lKarfQRU2SMVzJgTXL8UGWiY0Go3BtJ8bjrrS -2GwAZLiAJL3kpXdZUt7Bg5k5ShDUJv3hnwBkbTI204BQVbsDK0TYvKw15ygLaZa+m6iAznZDYZrR -bilsM/SKU3o16g5qw64C7teOK1jAVD7uN674V6sQL4dw9tMn/ntKBS5NfKtWoZepU6ilKBNrn/1r -E/pfpUroeDTB0L9OisJ22ssy3eH/rhqc+5DJ39rwtzb8D2nDaPwX0obWQ11/q8Kfc4PEt+o8AJuo -8AqDP6XecCQFgK4I3DTkkbNYw7f+ZVqdhhix3hlkDlRRxy/LQ7n7f04vrxu9Kf4SEZAMZORWx7CN -MN8dj3qNH/hPs95QAKjJRy33hrYczpIs+YXNhvDNVgNEL/wyDQcNz6nhxKWYDxtl/iNz4V9lP5io -yHDCr2I/KPvtN79NyFK86XmuvvvtTv92p/8md3rJeoX+OX7pf2nbzb9NgxKdcL8XA/93VaX4zaoy -asHwi2hLK1G+taW1P8v1H/+m3RpcVISHhXhu2dAjjsW3bERZgWH97duAh3LBxZSEul2aVqoB8b5p -WB/I8FQirh/NRUV5vMPjX7l9w3J/9/cYw5/fq2/r1Mx7Uvj/+p6UCyjv6EwVPK1wpYLpF0Y5Hk/b -SiXgJ7eZWWYcumvnX7nXEFa5g/9EdbO/Wtz8y++1ZvMBnKWwWyA272x4DJ/N4Jwg2yErdcA1WRpL -k2Pph9lmtAAKuQEs9Hgkoblo1sfjzvCvcrfdlgaXU/mt0dJNPwYu/Tns/Sll/tcdn6hnz1Aw6a5Z -qX5j8kJRahKShgcrGd/Q6huOYQTTG8biY8BnrNoaHoRneEETGtOsQg0HzzcxvGAo5QXLJUTTC1ql -n6FNVDIE8Axrf8ZShGcq0ATDmsigta5TdNzELpoEhjHHk+iZwg6d2WCsby0ZPPAI+5AR2uh63e94 -S1BEw9brvncmteH43kyY+vhWZWjC9CbflW/JHyhebZQ3vQASJQ3G3ckPFRxrBXensizOW1/dK/MS -P6CsXoXc6I/1zlYG3Um30QN+dFtxWTW3E0t5qTccW3OhTWwPpdHYDFyZAznlQGl1aoyBmyhpk4LV -vTt1zgzeFSXDql9g3Cbto3rwGXly0rBilv43kadtDTNn7cUJjEYsLjyiSoWk7xJWQOWl0aRj/mIE -9YYMbPRoONHwq2RVIbPNT9k8cHHH6sEDJuVNq8o7Ab1OTXkvQ22jfuVg+Zg1g41Zkek3u4YQBFEq -IY1qYrdphM7e3oDGNHOjO/izO+4CUShJQzUqUcRD+lPqlSXIkB1NZ2OuBrYCWfjWdXVFG1UECHwJ -yK0M/l5fWPx1vEbht9f422tcyGuEnImrIS2FWIOOOWQTDGPkHgOLSeEr4bc3+dub/O1N/vYmf3uT -v73J397kv8GbzPWkhhzITtHpMP9xh/JXK41zJ/8/sczqtVGF5dGSK81R6HhvhmU4eKdAgI5yFP2T -t3x/R+Xs377ygt1qdNxXIDeUB8D22F1r02tnL9sCxfeeZd1w/O37zX6BAybKDbk/HPwAfOz1Gu+/ -lfYvprT/IepYU6XKBo0AvRNQdwpR2v8eDAr0KTD6G7cP/QRly/yMde5/kESw/3mJYH8LhFEghN8C -8VtFmCRC/M9LxM+oPvjv+bhnI+DuVwa9xo/AyXA6lgJ58Pq3p/vb0/1lEg8chRIPCXgRBrwTMCEE -WD7KwqUGeAWj/c7ef1Lqgf5JBRD/MZkREqhIV4SXGMG7f1kRHhieiDIcnQjAS3zi9ivp/lFi8522 -Y/lig1NbeBUCejQde2LL8NI5rWWC4LuQUlkk+RhMRuaFjn6v2zc/Gb6NZ9nb/6tMBw== - - - 0hRQ6go5AW5NVW4iTgjfW6f+nxSe+L9WeGiehU43kh02zn2vyvzth6t++OlQ7jd6v33w3z74L+NP -wUu+fvvgv4Yz9U+Rmd8++C8lNr+6G/XbB//tg//2wX/74H+jD46vfrruDntARk+l33cpQSmNwgIF -jo/HGWWLVBTd4QuP+uTxvQvaE3utBw0GkgYDSUUF/jvLPX4mO9CNxbBHnEjxCZUnVCKOjj/llKfq -E2ee0P8ensCOx5UaFsQOgQXuHjytgYXXPKNyIDbB0IILO5jov4YbcFbAzqvnWyCW8PDWEcgJRq0D -4tHVgC4sYeFGmX8HT3htn7yx/Ek90oMWBRRYUvhqFvCEFRmed2ENt0y+LMlJ+pkiptbdQcXLiuqs -QxdQBow6Cs9DZz5+q3z9x/cJ/UR5YNVzcwR8NIVqlJSnnKqOlSfO8iD8w+ThH7WT8Gd6KaoNUnUC -FghV0XKqDlaeOAuE+E8TiJ8hD79ytfECefOlZR3+C8HiA3jemPYmT8Yg8bLbH/X0INHxZEg0R5Vz -g6ICCp/QL9Bbho6jorhxH2esoFf29dWAfpqYeoCuEC4M2hn9AmHvm4hrDRAMTyTcjVpzBrqDDxcA -wHgio6Ltpx3Egtv/g6wzv1j5o9YisSl40+lOJPW1b3Y1kMCBkFr7Uv9C9TgFdGq7UnOvRmzWXxAk -9W5yDZjitUOvFTipEBCvxjgMriNS/h99XkJ32xt6oWJlsUMMf1HgUert4bzSUfO/CFxm3NEgsWrI -qdONj2dAl3AojrT1FwTlsg90tQZHO3NPOdYKVrCrNiMaT3A0DakTsUeuBf0IUBWV5WqA1FP8OLUn -onZghMr4uNZJrf+4DWZ2YzRttbqDod5JdNyfcei0swK1bvMqFhY1godQ4F8QzJNhq9MwA+VUzqoh -vH4SIaf2V0PM4MAV8gkH9Fgqhq1hr2HoPIMtKlpKAv63GscoFKlyedKQx4ELqa3zDF8fjj+j1NMf -1BPFlK8ups0f+iwh1xwHa1NZMoKGzMZOgHpOmSoeSvP+6LM7MLQWcFyqtlauyVSaX35NG7rsuZ+B -FrycDsad7kBnjhryQeIhz4WAupMZtb+Ter3hXzorFTppLIF6OzgXJ7IEd6mWZEkaGCYVnuDKJxwO -f7TvirI07oBPGuOx5UNBTVroWVWdmeamcdWHpjAy2iDWen5DkxfMiJHUkPvwvFANiioZLLqfFOsf -NYQDYZ166iX+vjzs9X5Y6EhoPpzq4hmmknrXqXqwCKvcgor/xTRJDWvPVLHT5pGq0Ch1omjnvKhN -eCy3yqyVu82m1BgEsvBaB01fi+oVrGqakFZ0iBKsKppQhP1RNP6J1AbKUpYbAxu4BPoaZRK12YEi -Qvw5dGWRPsZ6sjfsNMzfx9W+iIasDBovpJFog+EIoMMALJ+r3BZxz3VzpQuM+QNVk5hCdJqF/xmz -IHGsbPHYNEbAWtggqaJlgsQoKVkypHpj8H+Ngcl8qsMRN+YtWWU6kqFkZeARdhvAjwD9A46NPm9U -ZsYNgxHlVYtEBIbz6rodVc1OXFMORptOBIGpCJzJrU63bVW90A/Acov1hnY0kfpawCOCVcJ0DGA0 -FBjKEgzyN8IB7DwY3BHSgou5sQ5GlQtlVdzYKoDOG+7NBFn9xkTn5USSeoHcj1530FZdaRd4luYm -UPoIXwB/sQnPFvaAZv8CA4TLGDJw3i8/f/gqVTG2VyCU5G47ANxu+K8/GKYvXCxksNQbNhu9gGYk -8cz9hCAsb7wNovqJYhNtoMy20sX4qR8o9s8GyGIXCcbKRD3W6uSemTX+jKpZhUTQzjZsLhrcWYWa -SCXDtSrYWlaPXrQYRQ9bjAHNyh+nIyX6Qe/U+KcKYhBTIEdcAlyx14GvWA7zIG7fc6ytsQdimBDv -WIzYKZrCLy9/9JvDHgT1/0EDKDea0vhrKgUiAfCmL03kbgtTlZ1OJvBcOzNN+mOdfuURY3hUGbwN -Udfk92gA+WMAQW44GEjorHSVItANEz0ufVMGJT9sTfvSYJJvTIB+3nyJqQ9gHA3/NKQH0IPbk+rp -sC3hv4LrbaW54WSa//V7A9AiAn/AtXUU92++kBv8qV1JtfmSUDYcGN9PfozU17EMEG8yGYDPjTbo -wTKooOem4k/gkDWAYF5K8ESjX4SSX4OQn82PPb0V8Dd6bVkaqK2sEm1oCsJq4FJPJ8oZdfBdmNSp -5LgB801wzvwNYz1D3yyEw8/GyT+96Q1uDcYvYOzGew5tjaQzf8Ow6F0hDRB66bOXrel4Muz/2v00 -/+kmqfPLRm/Y+oQOmifTBsOB9KtyS+0EcdY2ZDBZT1FPPDvZBD47aED/qh01dub7ZWOW+fTra41v -4djPhGb0ulTjgZeMgBNIB2rKeYuBuvS/SaAA4oJGs9tTT8XcfBF4HsQemPvADwwAZ7I5bMjtQAue -fgg8UVmbQp5N33HsgxrzDMfEnRszBrieTY1wPYlo6lLlCbhpl0DYDt3mNGqAIWn9AMhBdDtWb1za -fKFM7FV5H+iOUSo6gI71VA9hdGz9OQC6aTidAOjD6Uhvq5AAmmqeN5jZ01Gg2hi8T0HYE6gNR9oH -rH4tHST7bDoZTWFGYAxiv+7/oYUdEE0DwtBCmioevOmjE5iV9fgk+AADymnf8ObJMKlJ9BZ7cFVi -IMlYAEEYp2sG2nFI7ExLBEaNEYAy7vanvYaBJtssUW+HAoBUOKI6qO1RN2qdHePRcGLF1+h1x9Zn -fXTdKOa3duLuqNFu6zMwUwlkppOhxkaJMBOpwJvGklavOwIdhl7j/8A8eAfdUNGKumybPpER6Mif -KMILgFi8MWh5oVGPWg0M/5TkEQz4VCycYQSUkYIjeT5tQM0QqOLTRW0CbAQOhLfXHUiBCVArHnSo -Tcdoe4KuuWLGxWXLiNoWn73fq9MnpivJWKsfVbkwxDe/+mna6v9Qh1yXcmNbuS2Po9hX1PuOJcPW -TOXDeOyCHLUE1hNoj7Zllhib/W8UbQ0H8AYxzEsXiHpT4wjxDk2BhrXMeaRbSE1lW1Ni30e45x79 -Bq2AcrA0NKg1a1t4LK8seUDs0XBuNSburWAbAGqs98Oh4UB6b2gnmZNbvQ0m0XZvJL8N9TkmEPvw -3v+M9hvy53j49hadDEf+G/ekN0OPHFoD+ZHaZtA+GptB23kAGyP93GzIrryHDWXDtgKvtiNg0bow -maW0M1yJbWvblt6gFvmr21aOlAZ2CRihq0G3BTwj0ixFn30MmxNY4TLDJ3hiv3V7bkOOWppnLJHT -Lbkdhfq31xhF//TbUO0gTZ4JsCm6HlGD6NlQhciTNB9oNRqOux4jBpuBIRhplwwCp0l0ajiUYfmM -lxC0enJUM05NvOLj2ljVftrh4eSmYNCjCGZj3OxO+g2rh0VsO/QYca1hE+5C9ELflsbd94GFAYQR -Go3kaGeoHw3u1u4vn+06+rHhwKOIkzUAaNftg/kXbQ4nuh0THHSv1lo2gKYZ97a6EvJqadBAJLsH -G7bxAoG/eTye9JRpPBq5GVTYTgGsN/QBGzTug9c9Y7zr4ysoPZI8sLvCTjThDzQ33LDbzQUVUNXw -5lggeQan24eFMQkCQ3aRID1dLdT0AdQoL8HxRSkbqBRycFFPiDBR2qMj0CUzTksSu0Cjvnotguco -ID+rMRgM3ewddtumg5Z7+OOCBrhJhkF2cKSGo/bUq8XYE0Zr6MIb6AT2uuokFEnKHzQxRxOO7hLQ -kAO3gQCNml3onatunIN3Zg4uiL7UeNocu+kD7G8Zht2Hk2OSbh/tjYLr7BXJQzfbokP0sBbYStk8 -a0eYY20LrbOtnnR7eh6COHOQd9AajN26iRuNeq0fbqzDJv/dh4cDGnXc0AEj7386Q9Z5TDRkEFFC -x9u8zuBaguYGxGLC0b6ZbLHgbAeNyInDDtqYIs49LcEHtxhn1JYBGCWTciQ+I2bIUmhAGhMvX0iW -4M0sEoQnu0gZbDr+7I6Aezdwj6qRgwW8ATBY7xKxl0fDJlz7NvbRmDChA4XapZKFiV3jVEnWMVWC -W58pyYlLU3LC3i4HczY5JWdzYcrZ+EmPQRBuOTFjN5hApuK7F6ixdydQs4X6gCB4pPX0rN1gqGed -At0BynbBEENrbMxCKvwpqUX3jUFbyUsSsxLaZ4gkeB19ICPjr1CS2+MrjMzHZ2YaETIfJFKOIlY3 -pcrMQntmTdHRFnmwNaCchMCAJpbtqpM/c5mrVOJ8XoIqDb3lDs+2HkL7Nwfbe43b8BG7dRbJpuVS -v5N8H6weFVfDwe1ctxEdbwhX5YKwnkxflVIn3GGy+rh9kpanLbFYYE7imzTHrVPUOP+Rfw9TG+m9 -5+huej88GqfHx0wMoEnvVVdltdXRJPtePq+m9znpMtc9SLXy0ej2uw1XtX0HEIr54mZSvC9N8h9P -We4+Es70h9VxpnI56YRSwvq0mOc2brIfve0bgCb/Rh01idA2xMSbeH3+8Jip56LXzliN7ZJP6f3P -4lM6OY72Q/nw5rQYLLXfABrEr+Lry9k0//Z0I2Z76d5t8i3bmeQ64j1t4sjrVr5FV7/S+4fbNxgQ -IHqce35/HoLftr7ylXZlNRuJf2xkLiPrA0zEbaM9BWgSH8FQq9Diz4O5Dveyt5/ZZLdC2dPwayid -274q5qTpbur6aL2z12o1PuFv3VDhrdrBqGkq1hDl7sZrsvt81M72Ng+3I3LocZqpXm59wQ7spPeO -OixAI+xdP6Uzg9Z2P3RwshcT+48HXVGMjd/YjNyq0KHPJK2BbOWPxteAc+K2JN6wVDvZzcUaYJDp -k4NgJCxle2Ktj/twV91M5yr76zeFcIKH9S35yoOwnhJzw+fQ/nX7Ick0158Q3NRgE3QpJeyuw2F5 -EG6E8wFkVSr7uSNEFAG9blcp+mn9JB9r7G8VV0P3MkQjwBfPCApqAtBQzbUKh/4IpYr7ym/7N4Vj -3D4XLrxiaMwdUwESfEuFUqlCmMkfvh8ogG4O9vfaH6fPaDQ1igG8syyvogGtskcaCU86CXTw4AK2 -kjj0jF/N5l8Qu/PS+JAT7oWPVqae/wjl32LHX4VGY3sjKzSvzvfL+ZdUpt5pTTK1jdZJps6wUAQy -4tPdOviofV+4fT2calzCwmuS1udPHVq8F5XL6qDdDfNvl4U2YimA29gJpdb3bvAoQcgATeGFDl1n -udujYlqWO1dc8uTmEI1SnO/KAhjB3XAoO0w8W7lp7rqR9Sqr8OBCUADNXuhgGizme3SWOuKT4EeJ -6mJAKaE5TO/VJ2uZ+tFkauemZTQNrFdH/1Zehc/qUKd9RbsZK6umFwmpGNwa7eQ6wsV9oUnt7+Yl -WQ5TUu1gT6MEc0RjR7WUeTmikcztR16LcMoeR/OVD7GhaAE8qvHLr/5x5uw5e1LMvR3FKfq42S7m -2v07pEkJ41DK9oSDGx24MKke3GfL9fUDCxEADaBDOsuXPjclgOt8HyoblnpL3gzt9A== - - - Wtu1wG/hSWm01kwkQnvcqYUnexUQnOe6466AVGfkocbsrFfKer/2pORWH8zpix0oZCehg6P7IxXr -+yPQaSx4u1ndLU1e2v3M5etRPtbl1goYwNt2WcjUT4bv6et6pVEsxM9vAZrkSYgxwIDjwKum5qJY -fNlZ7SifU69UodXodZHW3A/fHZYy4vPqOEOlpqLertCMPWxkauHDB0wiVNMADdLUxgaR3sl+eL17 -m6nXt8IGa0RTzWlhe1B70MamHypc35ZVA7O7lX/LsJLBEGhvoQgoDQqd/JG8+2r/PPwk3ESPxfxb -XRbyRzu3l6XN0hFHFR/TLHw7BMbsfVrMtB7ewExvf4EmZ3dAhg4Nb2FvkpmLNfSkGEwIO4VI+I3L -Z0+2gprCCiVjcmFVvHhqJ4Asp9PgR64Af8BJVsyov4nwbS4Fn6VNz4pwD6/pI9xU+zKX075Ev9XQ -R9rnWrui9iNXhD8u4I+82kRMYDTFggkXamUAhNDk7TB08vUvDvIqOZimHIKioMlqPU1pZF2qzzC0 -nIamRuBSxgnrPuKOwjQIMndo+jwNGxzAZwc6jIzWOK0x8tzEBMzSUw1KxtAb42i5ja//wbCMhIJG -H4ySxq+c9iylscrMEdwv/c+s1uGihQgFjTaglt74GBb0Lf6taMZq+BOjIYuHt2xUTf3S5cUmuRiN -I78wz7Mmjhh6fenEV/P0zBUNAm3rqfoR/k2DYSGnaBHtlBkrgpdWxkbnSNE+tJh9OY3KAkWeMpj1 -8LMDbW5g2SwaRADD9c1un0KDuJ5S0NS0j7TRN3x0qX45hzAi0c4YRSCnYT/TfrvUOoLIP8X+yWFn -PMWGcFKtnKXlyeZF5nK6uWa2H0Pg5o+Dwn2/0IfuIPCOjmKd4aZAHRW+clDz7+ZbuVwfeOTZT2CL -u7wh+qATIPo4zgrbwIHbOTd4KvWjachoA43trqA72Gjw5DDK6EUd3BuMnhYeIWuEAgQYLYaBHb/f -N3WJWk9fbwehssm3e9WnYp6/ZyxoxL37ajHDp2NX+XJwsJ45vrscmN42HgX5vHyZ3o+Ia/mj0Dpv -CgpBAAr9LotzC0JHI2+yzfe8tFuomvpq8EBCIF6pf2VqlZ3HfBMEqgQAOEZG3jRAg2mDERc/Hlxk -sdc5GUXPQ4fXjS/VzY03FnFzkdepeLoQ2iXwSc5yk8J9e/Oded0/zGPPArrb+6/7xfycnjtCoznv -mqCizim+Ll1+zDcGpRvo8H4WCzQIFi+FBEPRwy1K7KxLgC2CsKu5TdbAA4ECaBRoVSFb3C88RjV/ -JuoegPmNviAoEaARr56k4/zb+fgy1q09ZJXJAzvOMmsP7jGlr4Dy5gNG0opkXG1+KukKcwQRGu29 -FoNvx9EMtV+/Z3Y2nvdVT9DIqvx7ZOcQS8k5+zXMHN/cboBpFEIyVIC9MYyDe8xhoATEBsVMXtLm -+YUyIoilNwcRfR4kPqXjHFI2J+1Q8fUllchnq3efBFJhGPP+UNyp9fbQNNazAckjJhK0wNXDDT3W -UMKomII4Qz0U2vHWM3X02Sgzr3u7NfiWSrb7PQqoTvoIKTG3+VUX03vHd2sg5umsaiIAtwbCDMhZ -5rLU/gCSdiAz6avVBJ4o4Y3Yw0FwykggANt6wy80mUeRNB0O5hLmyNAaKO9Rh3uvCM3gOke9JXIV -JcswvdvMnI2y5Sw3zVJ0+GqsTd6nKFCiD8+ZxN5xRHtRV4PX1Kso1ncl6qi8tgcYHg3tNZMdEaGG -+TQC9mWjRmYNY1ck05qQYe4/13Odx9VkKFV6eTEBjx5nP09CG6FU7U2fikk4CM/ZT+ZgVX8B0GhJ -EiYvrZ6fZntSls51Vx+2gYLNnuepjU6m0Pr82kIjkviQE8Xi81thu5g5rwB9XzrHcRDFCJktRVff -HmVBlNRMxE4uHjMQMmvKDtrlJkOrTT9YAjSf07gMC+k0q+gxkxO94vDNzzQ2UqKE+BCNlg2gS7nj -nhVkrdy7Ns64WLab6zzkwIwXTl7tcCc1fRqHUndPu+rYUEzqqw9Uwftq+uuwJaljuT3NxJuj9yhd -vg5DYajDcP4u/xZd5TFLD47kMVV5KO1rBquQPGo0o1idpoRTCuCqh8oHjxxjThAz5cftTD13dpmP -XrQj6eTpZ1c3Ybr04STsVvniFobOD4Xw8GSaScR6uzooJW0IQ/zg2aCOkin3kVdguJpXAHiL0e25 -0jQ7BFJyyb7mj4+He7nXzywHfAzhLF9phc7Bszqt+AcK/nDuvVOMALctuBa/3BvdF5ox6h26HM3Y -fTd5+r7XLTTrW19m90bAmbX61sZl8WV966L4UstMYJK7SSb/ffMTjGFiDWZMywBegc/2xPMQ1NC6 -14Ptk7AfTMuRxDRzvpt/y0aS0siCNUnHV0+LO3e1CXCq6Lb2oho6OD5t59v9RFhHDXoYDEJJK2a2 -bsB03udM77Y7sY70/KrCkAxvwRiWVsH0bD+nv7L0HtBM7HohshoVrJ3T2kERAE1TybfM8bFjK9ik -InzF9hhbk97lZvHlE0zK2sPRY6F1uMXnjyurl4na5kcxPT6qfqiZW0XZ2GUp113bEJT5mCwAIrKj -sFVAlCWOWCd9/XaVhcM8MrqNCig4NqUncSeW4ctPFbP7qox+vJttFJ6LzFXmfO9m0+AgKwOZCOaP -xrUBmORCtLRZenzJDEpXjeJ+iQ7qoBQ/7UGRvWg7neg8ZnpgOudPMhf19JfRLVdoiwG/trqbER/3 -cunkzVdXvGFZKVPPDG2Cx/CfX1l+R4A+dGZQ3v0q7hdbY4PIpA4EVoEL26uuJ/rz0SQqhxWylAjr -a4AI6T0tDDdgdjB7vjkUIoNwXXOzDthM/frwM72fGp1mrmLH+4XmNu/Y5BrYhZ0xNI4ZTTNBbpbX -i7nM0zv4EXmB+bR87YSxwxiH08NIvQwmz17HOj0c+6otKBihPOQfIZr9An8K2J3q6AMPNPRxPdfh -J7V87HW0m/2Y9LtG4DcHHDAYF+NCaFd4M3ru4Edk9JJ9Tl9vTCY6YVAEDo5fp4na0f2Toc9QJbOb -rb6gs1tLxVeR+s1cTh66+betci/Oy3s3aDlnT3qudQhCAyUNWrQKUD+7W8VcNrELfbhT4PBl5ELj -9SFoHNVWepp/X3+8A7FJsFVoCTv7GSp11LdMgT3pimnlj06uboFiLUeAaD+UINNMMymJ0rfY9Wrd -nr4ByksT4HXevuaPDlim8HzWfMq/1QdRHS5cSTlEASiwC8kjZf0MBBK6tlTjGwDtNZE5G7ZGiVP+ -5QgMy+ACOKGFekZMXLybJ+UH9o7Abx3N74IA1j8z7GQ3n7kYnOQLzbeXAyIa0IoLJs6AbaHOC62b -dNw62ahx+F64v9htAPdm+5ZkMIS14skWHIRc/mj1jXJCw99Pz5xhpK64QoY/fKsWg8elhCG0cpmy -ivRraNwmgJa3v5dy3YPDOFowMS56RVY/tca7wK0YR/KVCvSKYtleXtp/XoPKpnZ+fwqcpeyJ0Qwm -Mn3gMdxsKJGGsqh5n7lsjt7wmhJzIF2ZvjCkNQ6Z4l54TUt/wOyg5lJi3ojXvWod5t43Ci+j9hti -kNFCINE+f1yHL3pGrMLae7a/Fv8wxEvp8uhLm55qLKGguWH3G+Le+VkNuA1XjF3z83K+0u+Oizkp -2wck9oTi68lmwtnUnyX3YRIyd9KurudOc9e0owMRTwxjrUJzWI54tKtv3TyBWV1Zy/ZDW+R5o2Kv -HZ7egqHtis4E1tr5FlpeJlhKFrhX0Z3kSYh9yNTrm+d6lkO1QLnTwvm7EkukCtvI3TaZkMtd6Ehu -Z+LhSi6993TVQ94Rxe4Gd5RdCpPbx2Ihz4tZIVTVUXPI65xB1QI9W58As/owKm0W66Nsrz+Mmx2i -N5JDpPemthU6yJxtPxUigjjESi9zsSoj3uxJida1shwtHF2Fkpf36yojz9eg41+C2qWFFvKBin0B -5vrxqIDXj+DykGFszoEZOiluAyp3z4HrtR3ORvb2kmbG7wH8+wDu3VUabjN4sHBOmJ5f3AIX+Sqs -zrRU1+Ry7Jd6AynLfbbbe8XGVsHv55qGToFIbxQGU6B+DZQz9W4xOrqfpgUP7XarjvUXO2Hv98N3 -r9fZXoSul+5eRq9mdaZqMk2JaQYW6y0lZ4PMmvBFJ0pwsZLKfnwONiCa4/zR7WXalFKs3Yv110Jw -Pzx97Aqpr929Qut858CU10RNyrFifpqBa5sfiPWRfLaautZSQyYRAMaX2l1D6ix+Mc2dgN8ON4An -NlB8FzNINCnzx0dPX0hN6upRAdp9zqH0g5RNliZf7aatgdw+N7HvOAunzEH+nc2V9egasRl0M98u -hE7Pt0A8mmlreV0BMusWxTf1zE3+nRfWEp/pj2paXOvfqJN4a9WD+9oAqRs/ztcKrTvpYa9xM/zI -xwofm8VC/KJs1NAFmAhoZc6Oyw0UZyLVCTVpPN+epq8zw8ybYG1cWxeuVelLb8brGwNBB643hmvS -74NCG4b1H8WXVOYBZptKpOw3gBZ+B8puvAuIGCnZX6MI6BMP+KjXV0/X+423N8lmbwyAxPj58NJi -z02AIvfp/cPstLgjV0okAHHx8KAMDNFWFAhI5A5OT1MqXoAr7NuF1nStm5QO1vsaC5LGiBubtWzc -GDwdvb0rWjPM7Omo9956JRjigngsdweE7CmpPJ7WgAW8yj0AxfKVNSTKmelkHZjhwkbh+ZPuABVX -3M4XBy+3+af3J0qjbhtB2c8l7vog2A4Gtd6khJtgPnZbj2FoSK2DUf3IS0U2aO7rHVRFJ/qGqeJW -qToKZdZXO0y+wp4w+Y2HJCWt1g741dwuDVz18ri4uxOGhgDooNtQej/cHhQmW5UD6OE9cWNueFvM -T+43MhdDEHl+Bp9eN9j/l9J3pDJwL2rgbAQ3/o0DeXTaEN7IKSZY49Y9Y7NLvTRYucypli8qOylh -odoQt7PuuzSCMOy39NrdG3yo9HpTVIo3lAMLlI6QdvVCwgDxL7jmHLyWBpMartWAX9aGva62Y9zh -exp+rx5PYCAVnlEACMs1Rvikgq62oZVUk6ICyqGSz67pbANyuav6RUlu/IDnbuHje+y7P4kFsOrH -l9MmGLTicDC5gDVe/naNq0xTTl7+ceq+dV1tfjWW0PbnunFDP7k+VyXvZDgYtjrysC+59JBYDzJD -3TnDCx7sBRRLl6ajE+yb39X2WVgmkbUUabgMd28oZ/VyVM51qCE7dEZUYMlZpjn800PACVJy3B24 -lXKoHxUB126k5nVX+svHHMpLYyDwqFTQ97wpwdFpTKR6Z9pvDhrd3ng2EcTzFh6Zj8o+K20wf7tv -XW0nP7GyS0WOL5ArAvHKyJO/hvJnVS/fcxMJNGg5ve7aJzfrcrd/AuvJfMkdQmIRvNkruLSR1E5H -wGraWy36POjBTSAUPsFS17FVKGYbmAtzEZPH7Ku614H7OgHCsVOXk8ag3ZDbRt1Mog== - - - x9+RJG76si43RiPXkiur9cm05GGzMak2fkjy2IcgQ6VAlmPOl1Imf+vGPcW2VgYtYCetMuE9shce -xWw6Q1DBKZxtdf34Jae6fOsEvTHWOgXrHcDcQEMGEtKRAkpFawDIJ7Tu48BfHWkQGDf+hFOvMQgY -fRXY00BjDB/rbpJ6dk40AOwhBAl+moH9GE4DI2Cg4Fm0EnYiEGoM7h1eFdM1IQoHADLt0wHgU2Ay -hCBaUqCLylYagV7jBzynB0gUYDgWxvG01YHkVWCOvvs+0MFgbAPAJ3io5/BNR98dB6aDT3h5RdSf -ggagW3J3ZDyFZxbNbvPIfItKXatoZBgHB4FgGGeyqVY7oJw74+NLrQrGjxHGmqpuOMzJBwasCLta -qZo7E1BPXLgwm7auuxaTqp8B79FoDb2cF0c+z0abyTPzYfrIh/64e+QWbvpV+lhzAc5X9KNE5nOD -ZvvY4Ld5uwVakALHAvDUR2uSE+9tLkicNHzlcLiO//PDQPwLMGBvKKeFi5YRurwu5et5a6UaeAqL -084G+OphwktArF54ShtewfO1jiV5YDGz4A06vNlgrBj9FQzPDG9Y/Q0gv9U1HP1j+KgwAONOOMAN -vLrVDhIxklZ7fyO0RYYLigXQPS2rbCi0VYct4wEhovktDjEn9hpuSGS/CYw6GihtWOwZAMN1Q5aT -naGc4COf4YCoL+EFu+CF8RG5dDCU4vOf3OHZa4pqb12m0Z+pRHnjQ3/BZuNsXLgX+veofAatMRo+ -K79IOVlONQal3vVHM/t6cp7JhAf0094hfR3Pr7enMDuXz5Ufn+jdjDjgV3M759yY3YBbd5oxKhY6 -Ybj98+Qee1ifZPNvidJnef3ioJF/o+5S2lsmdHAhdFa3R/Wv1XDno7waakU3V8Mv7YfVMFO8XA2W -x7A38MldlK0mVkOpg9GmgqYzYRVSX48OAeWvcVRDk5fk7LQQOqneoE0G6tv8ZzQ25suJ13rpoHCT -lS7SsfH7fqycuT/O3xevL+GKQ2xCDUHToz74qJZDGGC/RuBZqaezD7/NRXYvuDFTFbQOW7qOOwfJ -3tCboEUn/Ed1LMt747r8eB+uUDHuMmjsEuD+OF6cXDPPw88tMIY04vCZDld+osb7AHh8GkqVVrcA -JeMTtZtxuFjLxb/2PhDl4PO7vBnro/x08nhOxloSn/lk5TlKxPq8cVrVsUI0ZsRC97i0Sca6vxqU -x/SmTMZaox+5NSa5S8IK0Iy3wycRh+7ynWBj+65AxsrtPIb2mXdyX9eKz9z61ahXRVjRwoYJMVXM -HqcdsArrGwN558AB690zVXw7vSRhhb1ZK63ub9FHL3VSd6lSmak7Di1zn6q/Iqxg4jUL5nG9kR+n -qSrEuoOXNy1DG7xjH3r0DsDKDW0CVaWyCtba9rYFK8/3X0YkrAANRPwiP3UGdYTYjjXdEMTiBk3E -+nz4euGEtbzBBvceEVaAxtbd/dWn8XpHuiBjPc/tpL42+lUS1tBeL5kiYQVokEDtHt6nz8hM5u7u -qGKCOiViXSu+ixsXfeaMhJUqPj0VEVakoa3dFdY3PwcnOSesDaoUfr0mYy1RmW0pKN5ZsKINKJDJ -k/XwodLdu0jQwuTDU+4AM5kuPH4WTVjv96mqGKEh1l0b1rI0FC4aEYgGIBZH1u5WH+4bDliFdfGz -XXx2wpqnTnafEhasEA1GXPlKpj/k0wsi1su9bdYR67F0yVIOWB8i1OXLDlysJXf3uCydPtzvBIlY -r7cH745YLzvnr00LVoRGQVyiro9H+2SsVXbzqpjePyBjHR2tOWK9fimtTbAvQOzuGXVzeJwnYz1J -FV6ez5+eiFifzj4rFqyqy4EQf9zx7aID1sc49TTuRchYTz9G/bNEnCVifalEoIZ27K68eRladcB6 -d0UVuv0jItb4aWRtNf0UKQCsyS+EFZo1XUNNX8QXBWuT3bFMnp37amMXYWW2U8Gyua9V6nU3mYFY -wxasAOjHF0Cj2oAD2aoXx8OtAwXr5DBk6evqY+NhB2PN3tEVs1IMy+Or1CrEGkVYV9BJErqGqkTU -7uYmNr1YXhMw1kP6OGKxsuERd4otD7uRzB2bsW4gQ9BoDiBiytpdOSMNVTE+WrVgHfMfScXyHCbP -oxYOrw2l1zq2slLjRbC6HHz5aut5mNQbWN7endP9a6e3HeDKbU5Jb1VDABRxfsvhczAOW6Gc4u1I -H5JoeSvE6ReV8vFn3Pp20Fm7VacnqYFYqyQfHd/Gmd2LV+e3ncbztv7WyjRhPcNs3bccP6+GBoeM -89vm2dse6S1mmrB+ttZqlh0+j5e3yvtXY/z2bfsrYQF+FeqqbusbvZa0vm0eXQ40phEaXOdDn7Lj -25vNZmzV+e1jPnmgv7Uz7XXzWdxw/PxjUhuVHd9+XjPZc9JblWn9z2Lq2elz0OHzPd7x7RHDpW4d -37YGzcuqC9O2Vrcqj7uObwuZk6bk+PaIOVyjXZiWWWU2dpIOn/NlqnCwq/Y6GdyzvA3Vz8eHyttc -dN82Pcv119JuxtiAC18YQlEQo+Wo9vZDTg0UN3s4oIPqiYoVX3ishbLD8Sn+zaDTmAnUaUBcPnOr -kaPEHYxQ6/BHFD4rrobzFzn448Yc4mFtoSBsXGRVLSivMdsHtYii3UEcZPbTUuvsNoo90RyBoZBh -ZsRO4oNtKnZwOwWqdm0LIHzb1xCux7oHzR2gqNYK8vQ1GjHpXiNWiAaGQgb1a8QqrMNQ6IGMlbu7 -d8QKbMoHbfXTjIhRKOSIFdrAlhPWthErd7mhY4VhVPw0e2nobntra13HimIDDStr4TCMDbS+lnom -rDt3OlbFHdwwMfmAccSKYgMHrCBkBLHBMwkrsp7c3ZNjdwGTx5wzVhgbOGKFsUHH4KeZu7u/GnXD -Wt12xIocDRJWVadBR6NuGlopoeJHvymDsZO7b/f9tHue9gcmLeDQVFj/ei7cnnmC5DuK9Cl6Iwt6 -/ZgJGdJgYAbrDtRrsA8ZdGFI+aSKU1nRMzjRZpz7se56Mqz/SI2C7boS06sprNRoZ39kiAhOgiFz -timVKD3kgRJrFCBq1qSmIP5UYVP5ET4ZKhiQd2xJkgFyQOcO4HV4uJUlWQV0YDbWkfKb2o8Lozet -5MLOtMZ5iKZk7LCaaAMUX9WQFnjdgqpguqsyCHv1ljwh6ENty8xDI/epI35zE/2AYnljocmcs8Fk -fSYqjmRBcraBBJ+Eme1DypB3UwhT5QVxnXmaZo8R17WIgMh4+EMhH4ffhB5ur/roIWQa/NdzGC/X -vMdwXevhrqWHarSGRQv4tVcu/PI/hkOLlBpynY78coFG72Y/ij5kXhNoF37tReaUL7ORxvJVevxa -hPsG1gOX597CesPYzMh9JnufOPKrIxAaE790RQQ/2rFroeeCeXxZH1oIuYOEESk8VmQjDzWiTTzc -wlqIzL7ngsZhdXraKQoXwviHwj6atFBxDklddVTijtMTJfCInbtbW6RzUKBh/9jD6/qJF7tLx1vK -UgSRknzIsV9qb/APqRbdUkTApuoLAM31cM4uGQQaSBCI+G4NdBgWp3Rxf93ZAgTe5h2GKjyiGxvb -ZQtjFOs5E28oZ8PdGYYOVPHRBLrJ7jgLdJ6eHZrT7NtV/TTTBGwUZ5+ARGIba7dTTQs4DOg6Hb6i -w/DHQ8iwxGGTDaAmg4Vj9yHVfQEwqnoG2z6nQQ8tKtEI7exco8lAGOjNpET2BYpABJpjJ7lNU9Lk -KmbzzlyGJRRSliJMw9IueXlnfrXQZ3ysy5B5FdenN6k3Bj7Ow8TVTOERhgsrnoNcgmwpL+JUGbRA -u6Qrbgd+nQx1W+xC0+TwyNl6nl0oFPnx9QBNmq9no0kbPm0M8YqH4zAmNty9Db9jCNAA8gfu/rrf -iQr4Var1SaAUz2ZmaJeDmQjDTHOEZjYw83cTiUDpbrQ0ppG9vXmZ9iIvk2lm7TZjN0eWnM2rKNui -20lqvLDnDMkpo2hRNWvekQbRZjPZ286qD9cbzRsw48nTc3K4trzpmb392pgtnMYruwQGHdLHVRJ3 -sDvok0F3a1t+YxMLd3QHCoy5Zqk9Ywmyx1h2VA/GeeNMjE6J3yjQQokWsCvEeOgDX5SYvE7G0ev0 -YIuHArAEjyGjZ2O2We9lmPN+mCF0x8ubem78QofHPI2bUWVsTjWKKlD2CzNgcEh/ABdx1c2zIWoS -h4lS8fJFVJogGk+y1hdWBUh1wrn3NNmcMbPm2EOCKvDICzj3cHsm/a3vsyEx3sGBcBJeJzf/owKZ -Jo0/b5YhWvkJXXgal0g6AjGNFBk5hO5gFnTY2ewdZJa61crOr9kmuzO/0Ez3bz1t/DJPdnYj8Rkz -z/QjfaZr6W5icO6diwLCu+YzmeKWdDiyJnKt5PhL5GLr2T8y+/cOobN3Ihd0bpvUOVUL+MyoHMGd -DiezJ0LMKdVD+mhqttRzJImAMHwxli7pKx6zdMlqZT2TRESz1j+C68R3CyWJIGN8ZActSsEU+ZsZ -JPgTaINe0LISFk8bUJ6eWt1sIAx+8o8IvGVHl20cjq3rMnOwL3m+abCeZIr8esns4ZW47SgWkGkz -mMZjq2mcb94kz3d8sNtV3I9t1tC4TOQ/jT06thpC7y7pSUiTBJnN4Dyq4PBqk7f6aXPy5sVHfh9b -PmWFPeTMIIub62WXyG6u6kDB9a5t46YQ2C/wzI9EeMe5ORxJR+fNlqv9ur10N394k4XfpUwAzTtL -S0rREjU0hDbXLDRKJJg8+vqGu4Z2E1k0cn5toKLTnAGZg815yYE+9KJTEZNDWOfQoejOrScgc8J1 -NkNo9gUQNNNC42zQzAl1MA+3wkHkiRpVZw7tiHWdQX5Tbjm0zVUHpSgb0nz0YX4gNCY6+0qhg2cB -N43u+uKmwTE1ajddoNXpCY3Jjk27jT+dlwF9aDfT2ADepL8W1W5XJO1mCXH9a7ermbSbIgJOwRaE -trh2u4bbEhZewEUj567d/GoBAGgR7abFNwjQ4toNQlnCKi4C5LKcFM4GtVWgmD6DdLNmHze3pJpx -BdKSolPgKfvIDPs6QVBky6rDZ0tZyoXr6VFPV93bY8uBGX82cde4ykqhH6ULoF2veusIosa1reLm -Ft5QAWf1tSXixgsrs09sMHKWDVOuUJwlDQKa1fEnQoFbKsyu+ryAPMNu7Nx6A7Jva/Ltr5u1AIK2 -tCQ37GFIN46aTgOPI8vx/kHYf2HdcDBHUHh7M3vyy5iEtEGbz/sngUJoFrePj1/uxtGnWYMjt0gG -zCQCi3v/CArBOM5s1hAgV++fBEUVAQug2e2j46B55KFntI8oTWA3juAZNI5GV31++5gMeuzyQr2x -7tBxZMGL7DIsRm5a+0+OpG+Xk+TGoOAk92MI/MzzW/dJTtzd7cI0X56wPWQkCXQyGPU1412WYXOA -pdcxXSzsiS4fO/YsNPkQC7ydx2GeWS1VLhqxhnHgWcxf7OnDUjHZ2/Z0ka1x5rwAoA== - - - zbqI4MBNH5OsOV7i1l4IzXvtR7M3Hvv4ATR2Y++4upRkCmCaOJvfYVpVMI/mS9RNoK3QHKb9netO -X5NYGJci3MjyKRYu6SJz2i7FnwyIgYcyDqh0b/y+Ghmcv6yGr58Lq5EM+wwr+ArutXwr8KCZZZTz -udfyraCipSWU87nX8gE0yynnc6/lW7GULs5dzudeywfQLKecz72WDweFSyjnc6/lg71ZSjmfDaup -lm/FVro4Zzmfey3fil6Ft1g5n3st34qxsniRcj7VbSXX8rklU2Yq53Ov5TPnoV03QLsU4NW2i+6R -sW3nvXMxkuMmGh80GUPcvPde8V1/tYpF6+ZWWyTtdxUgbw2AiUkEnwnih7zZA5h3+JBza94FMjer -9nZNQmupXtXsftBPUZr0IV37ZZUOihjfQGjuu7z89zBi2HnvtFnPZw8tiS4CTTgo9ENWzJHxPmgy -rHhAstwTXW6sci/eW1EKyrxyzr7ktuC4v8SyUdmrjGsy5/4Se1C4SLrZ2jkt7HXfBufRuci634w8 -ZJpL2Z1ti8msqUQw6nAKLpwghmV33ss+eMXDo+zOTxDrXo+I0ThvMbEGKq5RSsGxSkzZD+0DmnmD -F21f8WkyX56FmP70V6PotZrvEBkTdg0BS+1nq6afotfG2glt1dAL5AAAv1wWxsPZoJJMIebayLnG -z/jIklEB3155lOP4X/QqubljXsVoJucW1aN5umO+awqnnjvvZ6gpbLrX36yjBJfBh3aukXMuwvFb -56jtvN8+6Kw7kZUm1Yw6j6H3znvfGdl2yZz19BjDFfeawrjsVZPjTyJQbyC0pdWsyqQSHcuKxyzQ -ZhF8T6aRinbmZ5pn4ar/bpJOa5iFaZbyneztXcS8o7/sWLuj+2n+vG9bqthh2luMvinX6V26R4Rh -AvBJPPRkxViIybwyzzT88eo6z8szxYMoGHReXn0v+44HHQFYd0LOAcPrHBd9rJWUKokt3kW9nqP0 -QvmwN6qjQ54esNjObzbCMfaE5XGecd5M1XrK9HTkiJs/A7rkdT6L2iVyVt3sYSbHthKCYH4ySyjq -ItAfldlyQa6Fes6LxQQF5FyWNXE08LP6aZXZ576zn/ZRmS0X5FzxZ9ucNDerOr5TEh6Jro/KjNkb -B5qgqw7IYmYST2eanGa1o1lzYRU3O01EZaNU6M3JKnP2JqqmH0x54/7RUrI3R5bszXzb4ICXvDFT -9gYH7A4FQhvJ6NZSsjdHS9mcBAAE3TunBuye9XCLZW9WlNLFxfcJwno4x+yNvtXKV60gN2f2xpi8 -P5qpQMitOghWC5omnqNA+9siCK8rocyO9LHVkSbuVffjSLOHV6H1mSSCqGxGx3NmgAhFY9tuOZuZ -MkCgc8mge+dW/vBTD7fwrnm08/5q0/k8rhnK2FyOPVsxlC56lfgtcOyZFhSiEr+Z9wTaR2mTWMyq -7YScYdvJsev5WsRpbPE6rdsBa/Y1WfhsZstHjm9yy67Mc1wjWG5lnm0V13Hb3kKVeYZt18bivLlr -PBwq82av8ZirMo+wdxABWnJlnuP+tOVW5q384VlLtozKvBVDnbShOM8wg5ZSmafkbKzFecuuzFtx -OMZgyZV5hnS325bO+SrzbNGa07oQrKlbtPBfqVtbknsBoTluvbR4nX62Xl4PfW299NQC4092Udcg -Z9lz4bbh0htQ2C85LovFOfspx/OSs5yCMtveCLed955brFGdn3MCza2OQVv3tG6x3v7atW2x3v7y -GAzHRVprauj2ehnz8W5kmYxGDT3zfATQfB6DYxABx/l4N5p9PtpddcjzeWNKg7gt6ahjBGjeabRi -qpMGgPyUinuTY51GTmbNE5BzJYzTUWBOG7vt5x17HpTllM3Gzu1OOGTbKgmr4OyR9+xhN8wYxpai -07SDkx2dH/95aAjtxUdZq98i2cZac7bcAsGFuFlSkezj15KKZCGgxYtkYd3aUopkIaClFMlCQP4O -unbMgBkyUGiiOB8FO/P+pZ2IPhmNaG6XNR/tRXkmnTZrvZD/ojyjc+uUUlxCUZ6ZaUsrvrUW5c2c -6zQzzW9RnnfsuZSiPLyj69rDaVy4KE9PP3gXvy5QlGfKp/nb2Qerr5w9RvLp925G2rbnYtYSP4uf -lot6xE1+95FBULwn0/wmniE0P+fz+fNsbEcoz7sycXuHF738rAG5nTwMFLubDTw7N7oczubCKGSW -I8397Zw1plRHxmXLDr5KL0xCqIwDvkv+rFPqXTde0OXxxcRG+rlYT9UK6dhkO1esH4p3+fviHVwp -zN8X5HSmLNSPctloK5fLxo7hzQuXI9U+bfbMRCv5KXNFmEv12/4qrGF3LoCLnydrRiEzl93ttc5O -jElrA1a+s7u5vzp0LPbj7m7dyu7aMUesVLGedS322zzrnjScyu5eXbCWIgkDVmtFWDI01rCi2NNS -23jbqWqXNlpK0daCbmV3dNQRK0Szv9d3KvYT1te/hOmjU9ndg9tldn2PYr9hve6IdasqddpOWCWP -ewrPbx27SxVOH0oOWOPljQG79+KE9dxS7IeGFkxdhQT0myLxSZsIkNvtubdDaDD2x6ovkNxODbdT -jGlVILio6gwuTnvIwJpDXLecs4/9vWbnNjTQl5h0X6C2NVykkEfVrg95H5thrQlP5zu9vmaiyXlH -FyDLax+sz4pD4Bh77eia43o9Ek3mBLFHLsrzej3fabuHvOeubZ+sMll+P3sH/d+s5ydtt8DNep49 -XNHvwvO6aMVvD33cruCb8V53rFhockw/+LhUzzdN0mTF4+JN4tba2W/lIyaIFyvsmylnM39hn8Wv -R1V9FqYto7CP1Dl9hX1phX2kOFeZN8ss7CN1aWW2Qkw/hX2kNL7JSC+nsI+0L8yYVV9SYZ//w0wW -KuybzUjPXdhHqurzme6epbDPYWFl2YV9pKo+911DcxX2kTxG8q6hhQr7SFV9K+5nP8xT2EdyeeDY -LLmwj0STxd4so7CPVNVnc6AWL+wjua3GBPGSCvtIY6h7Nksr7CNV9S2yWOxQ2EcCZVA2yyrs82Ta -cgr7SFV9CzLN02P0YNoChX2kVZgV20XPhik2X2GfCYCSYTYybUmFfaSqPtVIL7Gwz3nn/VIL+xxD -3OUW9pF4gvPQSy3sc7Q3yy3sI7EDLUUst7DPoaBs2YV9JGfV6NyWUZi4aIxoWLtVeYLvW/Mq+PZZ -02eOEc2sX/ljc4bqqy+vaW/kvzHzY9bQS7nFT6HJdIUfOWez0C1+JCnVl1cdWTUJbc/JKvOGS1gk -6ult+JQDhwt7V0zl2N5kuV/g50gT2VWvOF7XOztNxj23i7LK65peq7JxI8s1aUrWn2q8pCmbQ/po -bAmZ2I3EyGNvk59UHrz8bwlbrfxc/reinQDj4a8vdvmfRdk43P83W+cIl/8Ztoz4r+nztefCYz80 -6f6/ecbLlAk2zxv/Sa0ZL/9zSkKa7//z2o/iefmfP4Fe+PI/w3Ye/9uOLZVmyzijiz28ot2zrjPU -eBwvbfvT6Ji03Zi0tubRuYOd2VOv1mUieG3fDDscyVIKC/rMdU2k2NNXTd9sO6jJ1vPYdWeI//sM -tZ2NK65H6HlvUoMM8lGEo4ZRfu4bK7+ub1tLmuBRsD4sn4+1tVKtv3B9GRRoIFoee9B9b7UCoLz3 -p/neagWg+dlE7iVpOaQIl1CIqS6j6PNmXkAuhb46lJU/Nj0BzXgHLgkK1tALT0VEjkuZ/YqhgNnH -frbL2at9jXvVCdW+lwOrLYTPlni8IYS2nHufEbFQsfnZRG4wZo7crLtzc8XhXly3Xdu79sgfFgW6 -bdkmBexOu7al8edyCmOsJzvP5V6ooKpTX16nnxJS8KLm4xhZTy1wNefl3iZxQ4WYi/sY6EY71xIK -v0vf6Gq8JRxjgAEtesM3hmK/Z8wQRvmqojBNFP8lFH4Wi3dChPl4PVMJhVvyHl39t4z6sjPrzpQF -5qPrQcxOOs1xPs5375/VVbdd/TdnIaZlMjoWX/gANENJk+M+G/Xqv8X7BSejH7O2hHv/NHvjVZ2y -2L1/K/Z7cc1X/2l1gZ5uts/j22EJ1uLVvo9fJOfHug3Ob7UvgDaL84MF2tH/efxauNoXMpxfQnYQ -FjueeNRpr/g5owsBWkiJKlkOBGjh7U8ICjG+IkRrnoCc63MdC6l0H9o2bpE5q+9Ji9poCS8ZDNvm -YzLoMRg+C6lu0WR01mnkWirnmqDtg2fnu9KQnzZDLRXspkfClRTdO45NMugr+eOnkCoZ9Dk9/VxJ -tn0wcHYrZow9b+cspLLk0+DVkq61VLN4jLd6jE5OP8xedxsleIx3usfomE/zW3ebizpv5dOVwop6 -LoeXkW6O57wMk+yn3S3vMsw7PevsnoT0VXd75+NULd+eTXM852WY9j23qPh04bpb+9YOt3Js77pb -QJNPsTCmVAnmQkkXwcGoimSEqk8Ia+/KB/X8J53Lxo5u8uvS0WX+MHRZPxi+huD6DfijVMOlhreP -xTaznVrLY8cQZYQNOWflN1M94OnhhRGrqTJvvH59VzNnOSxX0yVzD3cOlXk7zvWA8vQ1SZOwQqbh -ajVq16kQUVjfEC92npzqAR/dqhBHrEXSzIWIpfX7K0es25Vm9N3parodUo0cQKMwucYbivMsNXLj -bXFLv2GRtRRd7gQPH3pOl//ha/iMGtpcEthwLkSkisf7NQeswvomd5p/tWBd0cvj7l5cChFLG4Iz -1tL5+r0D1ngZ1XkaVKe1JPDC7YbF42tnrIXCVdE8rht4hb0WRn8YqkKnuwcxS1NyOzZH+WgHmfa6 -m8z4aBpKDicF3XCCXt9xVndUW+yxGFO8qe8255Jf1kIrn9cuAL7yO+blpDxO3vvfoOl2u9iBc3GV -UzLFuQTJbeOWI032/QKArNxs9VVkQ1jbGlvPT5t745YlD+y4a0vL3LrlnfJ+N245Dp8arQEMl7Ps -cXMrACTs2rLmBfyWonnsBLWKFNTQLtDI28Bm7yFCM/8VlCaaXPaAKSLgmyzPK1/INK1YD2jK+9gG -5psmLa2zQrguRKHItg/XoroKj6lds+oqOOamV2a8MQYAr/g43tsrc/tcWFZF1F1k1aps5q+I8pmW -9lgmei4sfMQuOvRuCfm0go/17xUfZ3c/FxbOSOMVj/Bo4YOu3c+7W7HUrXlFNfbz7nwdTGTK2Zi3 -ohAWaxvFOa94sCmb4tIi6UbRpmzmX0OHtX8uzpcSGdr2dTplYAC/LGsEs+T4FKaZMzDcl21Hwmdc -XmR3uSmSBv26dV5jnK2gzX6Px9xHMbRLfqupDMrG+Wa/mY5iINKE7U275JZz9nTVTTQRKwRUP23W -usTPjZlqS/HCilNdotcN0L7HkLBfYAFojtdAG32BGaB5XSRkBWXcOGaDZrEyCzLNs5rIfzedVkDn -Y5rnlUKzMa3KPTpAs1UxO7qNqB5RWfSatyTRbz0iuf7Gd0mit5vttCV+ppJEVzVdJg== - - - 5KHnK0n0W4+48sfmIiWJfusRLT70HAV4vuoR9YX8uUoS/dYjrvgr83HkiM96xBXn49v9lCT6rUeE -DtQCJYkmmpzrEdMratGSfaSXeVnhitcldcu5rBAJdJMt+1dic11WqKYfvvmywhXyJXXLvqzQUact -97LCFcOhDN94WaHqDn7zZYWGXUPfeVmhsy+w2GWFZppstVHm8z4QRV4Vy4RVb9tth6TCmLkuPJy3 -oGzGCw/dbzv02nDp+8LDpZxq5X3hoa89UItfeJhyve1wZYZTrVwvPJzhVKtFLjw0iqD7qVYLXXjo -XlyFY88lXHjo7s07bE6a/cJDe4eNtx06ZqBmvfDQfTTVvYPu6s/HhYfuncMCvYQLD91vO9R2Qi56 -4aF7lwxmbbELDw3Fka7Ht/vnzWyng9tOu17owkMjl+y3HSrO7eIXHpI29lDabYfk/WlzXHjonk1e -8XF4lq8LD93jYeO264UuPNQZTpqCs0qa44WHs1bkz3nhIRGKZgPdk/czXHjoDsUtYJ/pwkPv+wiW -cuGhvluEItx2iK3nEi48xPPR6bbDFcOZKQtdeOi+oqmEuItfeOi+xVudngtfeOi6xRtnoJZx4aG7 -RGqbYRe98NB9rWyFVL06z4WHtnE13XbouvQ9y4WH7lBcEl2zXXjoftvhUkpJ4IWHSyklsV146Kvk -wmlrrzT/hYckN1+/7dCyg3j+Cw/dbztcmbs8bqY9HxjNEi489D48aykXHrrfdujfVfe48HDGewqX -cG8o4bZDs6QtcOGhOxSyq0648HC2Kwqttx26JbpmPQTI5bZDs4aet9CDXvPgoU+d5n3hobvzY5a0 -BS48tBPWtB4OPE9oY73wcN5a3BkvPHSBMv68cReBGS48dIey4vOewgVL9VeUw0zcqoNnK7wi3nZo -lLSFLjzU0BBvO3RP3s9w4aGDa2Aya0u48NCdm8qW+MUvPDQwjRDdW5g2/4WHM+c6zUxbpPDKbKSX -cuGh+22Hs25RdLzw0N1jNOs0q9M4w4WH7h4jcUFyngsP3W87dFlYme3CQ/fbDlXr6fsULqcLD913 -Hztk1We/8ND9tkNfq1F+Ljx0XwBasZX8OkxUrwsP3W2FdRP53BceOtmKmOrcms3FvduqydmV+8ms -aka4Yz0dEltP8Ng5I+y+399SRGlay8A8gSkvzXqmDkYG9inenIIraOJDdKjzhoqdSYLG0lNTtFod -yzIz7qwCG3ME0KyGHk43qNjtp4hbwaqu1Y2TVCVKBQersdbDiGPlzlEx1XtKnF/tBiP9ne3sV5Yq -v59m1r+mWWH1sdFmwW+v6U0+vlfeOjn7uuC/Pu8eRD7KteInxdsoQHMclVf5aol7zV58Xh22L0+E -m05dOuW/roPS28ZNZofLf6zVL44/k73926F0GBx8vYhfm3J6IJyu3pwebm+wwZKw+fZQqexMO+sP -3PBRSqLBRaWmqav65TXsTTT0nF5l3r+uQ/vsdpoqZo+zVPGtdUSVhK0LWX5NBOVx/KYyXn8WmmO+ -+VDT6hLroYNY5CbKVhNragHeR0wePw7hDXyJdV2XWBLEppLXvfvqSyLTOh9BA6tdkAmv25y+0pu7 -mXT1iMQvxA7Q4cmWLLMbq6ivAI1TdzvBxrZUW7++r6c2BnKsuMVw79P1Xnu1DWtbj9Ry1dVpeMSd -wg1xJwNUIbmaKdbrm9SG1AbPakPbwc0ji9cFOlfLGer39CytoXNAxZE4kRI2c9P8fVG6RfeLAjS5 -p9LFrXCaa6+lY5P9o3Rs3N4rJtY/a4WbNPsMODdIlXrXt4+ZsrDaBICuv1S4+IrAIB36FGFh5d1z -eq+6KqN+ZfrD6jhzfHPzEipc38LD6OEfoM+1XhSWEodx+J/i72QUWlGxSRDub3iZYL0c46Pb6m90 -EOWcAVtEgJAXd1EtMtA9lRH48yCM/gRm7UFG8Q2fi6pfFmPFAnMSB3DLVCgf3pwWg+VKhQ5vd15U -Uo93jC8Sk4b2ImR8UQ21tBcon2Z49yxK2ruY8cUo9669oA0vStkvWNZwumt8dhVsqo1Pw+gFQKO8 -e+fa2rsouv2RLm+kKWghNuhypMjCFzRQSsNPunx4Af88NwJvvocgc8/DuElrQ0wqagzvtoNClqVb -kQMatorB4qk+3TqsQM6dK3Bb51cILiy03V+HPA/BkmdkpjaYnQgHY4N6RMfK7Bym9yCaPbh2fgHQ -ZHvR991MrfVWzR9XVi91ydTWFHLm6NZSGJRldsrFfR0kCZ6WVfcN8rx6oIHM8pPahlR6uo+/Zy6n -G93CfbtGwX7RSIyRDDNP02foque6jRhNxe6eWVXI6pyh9/lDUROZmwjiIZM/z0GreBNTpf8GAG8+ -wevqbmjFE+ZvkHEYwt9YZcVjaLGFZuO3ZVIA2vT0pwDQ7Adokk9nw2DhJntylN3pyfAIAKGcLVDR -ul0BWC8Y3sZyuJuJo3Njd9SO1IbKPLz9DKEmaMPl7fOYin2uRWPdtdoWu5HoTygqGB0XWp9fCTTt -0dZplGEEf3I7qHaYLjym4Z/JEBD8jxSc0GFtRB7BF9dwlMpRdcMl3I8SRfE2XXqOwnC6HHPInpQp -TUq2DIkQMGWDcJfoFx2mBq/aNN7FkyGc22wCNNpjZWIBXwvOs+OoUQu8H8TyR/tDioqlqhTFN48P -CfoAegXvZ1gppO6GSEfCrj9mLPvTFI8lZww3wDjoPgaSalvyzTbTTo1uReGZx6mhiwl0Qs5Nxx5g -xyQCnsWnuP1WK35scMfQs9VJcKwBqNkAcLulKFfOoIX89uYgL8sHw12juUZXX+9HTnTfgQkm7ljd -x8KWcjO7kVcBtLZsALIxbcMlgkEXb0u05txBtkT70FIFkZGKdVNXBSSosW44mUKirT67/lKf3cmx -7tkgxG7s778WX3Ye4D6U02N9JySeBbDj3A48MCKxikvw4dXM8MLvVfX8CuCs4Iuxe1enRm8WXeYL -uwR8D6hi1SuyH2S2CQQ/VARo8E3QkFT7SQ5qfT0+iiErnJHveFavxdSOvThVj3Z4pywLK/gCP9yl -Z2OXmHfT5droRjkC5ebLvfHdTBhAfny5op/9sGM5YCOiu1IQxqvqMR0wqrMEfKzmxsUjMP5cyXo7 -dvn1YKKTvYJuLtcoB4i1wbhzHozDZG3iPhiWkQBoLIOhrOdiGEnaACN9s9tQYUQjjqOprEFhAKjr -R31FoM0wXCRCOcdvViJMQaFy9tgiHUF5ETs3ARpHhtpgXCVjxBEBRIwMUwD4bruWdhrTzPIItXfI -F8grGpOP75zXz4y56DPPpuwg9PUoJ5B+xouObFRoBVepnNVxQT59mfYLQHOxo5L1QYfIB61AOWy8 -CMY/PyTRyNcqd2D88zl9aEYDvGTRyAxqEDf+WQ3tGf98Fg+Mf74fpBRPBbjvTPaOriA69chBWYrw -EzwsEjngXKev4GGRyAGJgL/gYZHIAaLxGTwsEjmY4xvX4GGRyEFJdPkJHhaJHNT4xkfwsEjkANH4 -DB4WiRxW1FtJvIMHPXKAlo9XE1dD2MPYFhyHoPrZyUAR/DNpV3PVtw8ud5UZB+QGVjFEoKNBIRjs -4U2hgdwhqGeggJSDaPSh+YPLtmXsKqM1ViYfeQih6abNqogae6JpB1N/t0q/SsfqdjwwC8lLIXyF -gqnEc2VFTV1WAx42F4TeRpRqB4NJhc2p5C7209aDdOE5ua8+TqO5D5P9F1/gz2JUUztPoK/Nz2xv -bVBEil11389orckzlhfM3NLBLgpLsOo8DSF/RvfX/x94IYoJOhDnwI/YxbQnyWdy9707CIRX/thb -+SOWqdD01aA9LMqSVJf+N8kPW9O+NJgEkoFY5jJXqcT5vNQatqVAWFXPttypmhxCL9hsnI0L90L/ -fj/yWhyKNyzVNoYDyFlONQYgrvtoZl9PzjOZ8IB+2jukr+NqXiifKz8+Ib1JyD8zuuDn3xKlz/L6 -xUEj/0bdpQyFXCgw2B7Vv1bDnY/yaqgV3VwNv7QfVsNM8XI1WB7D3sAnd9hTw0lV877DmTNapHQW -zKT4ymjhgHZeo4THZqkZLZJRwmiWmtEiGSWMZqkZLZJRQmiWm9EiGSVoOpec0VpO+mkuo7RiP9Vs -0YwWySiZHIHlZLRI6Szb7v7FM1qkdBbMQC45o0VKZ8HeLDmjRUpnOW+GnjujRUpnKVpgmRktUjoL -+enLzWiR0lm4VmGpGS1SOgv5G8vNaJHSWTi4WWpGi5TOWlEOUl1iRosUDJtyD8vJaJHSWZa80DIy -WqR0llIVs8yMFimdBVODS85okdJZWtp2eRktUnoEK5ulZrRI6SycT11qRovETTX9tMSMFkkYUe5h -uRktUjpr1gykj4xWiJDOwiKw1IxWiJDOQkxbZkaLHDng3iw1o0WKHGBvlpzRIkUO2oL08jJapMgB -ZbmWm9EiRQ5obJab0SJFDirTlpjRIkUOy1le94wcsEAvNaNFihwQmuVmtEjprBV0qPpSM1qkdNaK -dmTv0jJapHSWmuVaYkYr5JDOghkVmLR6KQzaxoQV3BwFHl1Kk+kINeFfstJ7d1Bt/JCAMqQD+H8U -+B/8KSYCNBMPMDwP/uDh02pz5Y8gah2gdwJVuEzwEsvIk3y3NekOBw35RyCJnt2eVK8q+UAygFu/ -gNZ7gSCgiXoBzcGrHZQre4GUvqz8QQUy8MftXyt/TNX/owJn4EdU4FiaFQNUlI2D3zjwCyWA/0RI -pf6M5xIUA/4VOZ5KxAO3Dfg97gkA+wP+dQR++wDP/gpwgZPAwxMVaK+Ad2BqclQ8ytEcHWB5Okol -ODHQhw8TUZah2QBL8dE4KyQCwAVmBS4qJOJMANAUFSmexg/B7wn4dTyaEBjU0A6yuvLH28ofV5AS -gLW98ocQCO4Ebm+MfeUx9VSUUzqNOsmJsJewkyz8D/yCO8hydJRJ8IkA+BHleEBMX3kYF2n0MEEn -EiqFcVrgENkcxbHaQ4aHfWGiCTbBo4c2mL7oFjiREiCpAgNGQKVb+SPK0yJoAEcnwdC8qBEvsnEW -IWJFUaPdhNyRTDAOjCAK6GFcoFmVdhNIV9J3DSIGRjIO2c4LcUCzQr4mWxzoHCAKkI+7qQsXowgX -HO04aIZGO8HGeVWA0EjAh3GB4rBcgIcJIEzwIegM6g1HU1GGoUUkaugX9SEkgKW4qAgoUz8X41DW -wMM468ILMteIYgC+SoBusXwiyotxWnsYpwCZAphvurywDMughgmBi6tybu45ZjodyJOmUJ80Mxym -EHGuEWdlEYzmlTLQ5jlPktj8ItPNNLOQyAJBgTxnOQb0ho27sJTEfBvM71ATVhJVunlaQA95Pi44 -jzqpLzaQ36TdBDgpwC+gEZpTvuUEEkYLcahKBDClKFHjtgmkL7JVuphEnGITyACpWk6nXqATAgU7 -J2K1gbtgJaJPImIW+beD/Db9bBWQ/gxqAX4tArWDvubiFK9JjQnkd9EeF6MMD0QNTA== - - - LYCRElS+m6yDk+ok6lgbxG9jO0lkfGtCNi5AaIDMOCCT4RcQGZ5JcFCwWZVKCs9eYw8EMB8Y5HbB -mcEozLeQ0HeajfP36hvlRqQYGo0yIyq024beQUaIBtsO8hs0JQAtJBgO4RBZkUVm1rdPYSexR4Lp -h+65fXXgOADFAnQH6BdQpcBX6pOHnSjMRL1uB/m9Yu/XXJH9Hxux85ureeSexH2/dspnh77Z1JK6 -QKTMv96crwszTl2CsiTJwgJd+UYHwWzi+75jBIVMluaBKmI5EOtxNNlt+CaWA6WE2JugaVqL1XyF -ZXbF2CPB/LaZauVa34G/xOgT0QmcePQ5KwrkgfgmnhvxzhQe27lL6spPC/aJY0AihygVRMNK9BKI -Picpx0HM2RAjS2LcRnTLiTPRSVRQsO9PlTlaa6JdJ6gy37H+lVNukgnEKoMJSkbSgcxg0s30uo1x -d/B+Ik06w7Y1MQkToszMSLTPARvdqNx8ATTAXOwLSkxsvnAUhfuciEcFXmQgGzdfWC4Bh0N/WjU+ -ZTnjUxUC+akZgnMC1/NBMNMeNqUAmw8U3t6k1mQnELucyICNgSCv8jCAG71UBuNJY9CSXvKNSeOl -kn85aYyUNja4dCCWHQ57geCoITf6Y/11ZdCddBu97v9J6hhFaNxFMRC7kBrgk3a3f/b2NpYmd0oL -SgUmSy1pMJFk9UuRAwIJfti+vNVpt3UjU0m8FP4ntaaQInOn1N5kZLmhMagSyEwnw8BFYwwwA7p1 -FtnhnjXHkvyn1H45ln684FZjsyjSoPlgOAjQIGDVFNvUoN6gHC1LyfEC0DMMmJssw0VpTkDTmRfg -JAWqBD6khDhS2eihmADTmWGhZ8zjh2yUpoD6ZkQxyjF0QntIA54zoqA5B/BzQQQNYEslpQAsX5Tm -KagjeYAnoTwEyoCKo2c0J/LqM0APgx4CE6I85KOiyEItBfoAonrtoQAMFcsyQKvS6tcMBd7DZ7TI -KERaO27QcZBYhgJ6jxHjgFgugblCKzYLMIClGFGjDChqRAQtUgm1X3ycphG5CZqlVQaYYS6Q0Zw7 -ALN3ou9AGrkTxO7aYC7fs+ASYJTEuBhgBGBXBRY7RPAhlFNGiEdphqJdZMUuVHaQ3+TMQUQCAzkp -QJbyGukm5I6iT5gjNojfxHATbxHVbJThYHRCA++HouMu85AwCnaY30G3lUSVbpbj0EPkqjmqCkJf -7CC/id0cYBNMkgFdjvWwb+XCAeeP4gFlkLMc8Ac1dptgfm9sbieiTyJiJq1ph/ltk9QqIv0ZrAn8 -GpjrOPpaiCuhmQ3kN9HOAyee5hghwMC1GyWHb9MRjgaXZJltEL81qcazCeilwVGGDKAUFUmQaf+m -1Q7z2ySHIPaL0PkzxR4ONBQOOM5qcsc++E5SQnT0rBCXrywRCpFnEI4EKypOq29X1EphjwTSD9nz -u2FsHGgEuCAFM3p0HKUxiRJLlASiZrfD/NY5O4PFIjrONmrnt1hziD2J/zOYKn89+gbJJylKIivJ -JC5RUc7tJpBlfwFy5+f9fG6C2dD3fQeYCvE0WgulgFgB80x2Hr5JcICAI8Q0naC1ON9fSG9Tjz0S -zG+brVau9Z34S8xcIDrh1hbwOa8/M4P8Lp4b8M6WWrFxl9QVV6qXmQ0njwGBHKJUEM0ryVMgOp6k -cJcYvRMDTGL4RvTNiRPRSVJwosifPnYy2UTjTtTH//Z0OM+zSqdFMH0ElsX5cJ6CXidveFw1PWYZ -+JhTH6tAHB6bgfzTc+Koj/wcOfF4AkxWXqR/58QdVB0NPV6owVigJBjVxtNgqibwIh8N9VtPeQiV -ODT8aLVPeZiAPjUTB3ymKSVuZaIUBTwD+JDiWVFtycPf4UOaZfAqugAasAm4oAc0Bg8UD3rIA8UH -1wjBQ0ZZjcYPKdxSSHDKQ/A7z8HFIiAfeOkXPeOASYcrcdBCKR+j/QVomQ7oHEyQtedGXQc7G8cL -mjTLxTFXgLoWRZwgYeO8qNEFBAyRwPBKhgR0i4M7SeA6JeJEjwCy93ekxG196JMpI3aB2Fk7yG/I -GHIsFkgY2ooQcV99CFPIwHlTvEuylJDkyQbxu5xogAh1FSJCwVWfhN1Z7kkzxAbzu1iucxdRDVw3 -hsdhS4IWnKcgaRQs8L6DYgt5Ksm0wKCHcS7uoiII/bAC/N5YEXIowbIC4hBwlARNUhi4w55hgBbH -NQ/+lY4d5jdJionE/mIk9kgwv216WgWkP4MNgV/zAtw8Ar7WRscG8rtojycAk4BfDz1qgVMSOzbd -4KRFyBbZBvN7E2vxOPiYg5ENCAR4SuuCXSL82lQ7yG/U7Pb5Oj+Z80/X+USHEyEiMMw8XBzpk8be -SUqIHp4d5jfoGohDQPFiAiuI/ixOqJ3EHgmmH7rndsE4uFkOLnjDwDtB0VjkSUJLlAaiZrTD/Da5 -IRHv20aRXej5iJ9b5fg2Vz6pnd9czSj6JFVJooZI9xJV5fweDkl6FqB2fuGZz1Ewm/q+78BSIZ6C -PjELl9k5vDXcDvObxAaafoiYoeJawOcrlLfrxh4B5LcpGyvP+g7cJSYsEJkM3EoKPhcEh2H4Jo4b -8c6UUbExl9QTV6KXWgROGgISOSSZIBpWopdA9DqJUS4xbifElqTYjeiYE+egk5jgCnB/itjRVhOt -OkkT/+vz4BTcmy9ATiSiCeCw4Tw4x9N4NUR7XDU9Zhn4GA+zAYjDYzOQf3weXOnM7HlwjgfTHaiY -33lwsqLjE8CaCErBSFw51gE8FGABB3pIxbEyhg/hYhyqN+GURIBA4cVMGPbFRYrWHtIsrmvhQAyi -PkRbK9DDOIuVPlyDw+uIis5THsKiEfQsLibUZ8oCHQdUHqNUwLK4wIblKdwJ9SFkBXwoJBQ/Va1K -Rg9pJbNh67pR2YGXIsXSqIaFi/NY2fFxYKf5BHJ7RI5lNNLgfgBYwSHQSt050J9YkQJ6WV4pKrbB -7P0NqXB7J/oOpJE7QeyuDeby3QtgzYD8iUpADmWurzwU4th3U1baHWTFLlR2iN/k0IEQDK9Uo5ic -wcJkx+4i+7ZJYgf5TRw3MhdRTeEBhr6byDGMy0QkDIMN5HdQbaVQJZtlFe+ZEl01ha0rdpDfxGyO -FfD+Ilyz4V+1QHHgkScIGAtUGa0x2wjye6NcOw19Ag0zaUw7yG+boRb56M9iSWAnabRVAUY5TDyh -CY0R5HdFiyz0wGnsVyeUk4xs2sHR2BLNshXk9yamADYahjnQZaUoWu2ATZ79G1UbyG9U7DaRX4DM -nynyHBvHqSY4xgyFHT/rwDtKCNHFs0L8hhwDRAE0MNotqq7jz+CEWinskUD6IXt+94sVgFZBcRsw -9dB575PllSgIJJVuh/it09W/oSJ6yzZi5zZU84g8gff+LZS//nyH0NsVJJGPZAqXpyDnT4AThX5+ -audn/Dw63mrc+74DSoVOISGygE4euPnKcTQ2mN8kNTClCBnEw9ShGtn7C+JtarFHgvmNM9XMtb4T -f4m5CkgnC9PO4HOUYCWOxDcx3YR4tnSKjb/EzrjSvdQ0OHEYSPQQJYNoWkleAsnhJEa4xJCdFFQS -QzaSS06cik6iglNDvtSxg7EmWnWSNv63J8FhWhv3OZ6IAk9ZVJLgFIWHQ3tcNT1mWdNjFYjDYzOQ -f3oSHPVRmCMJLiZgBobw5T8yCW5OgC/nRHGOEqMgbgtwFFDGcC+qchQXyzBACfBgqrJx7fRtdOYB -PPFSVA+/ZNBxPPDAqwSvHJNmgfcNCSO4YwNV03HAU+JF9SRgJh6n0bMEzavUxWmRQxSzCbwEix4y -cEkQ9AfEAcoZhRaI3+VRwfOo4LlXEA8r6ocvm5A7Uvn/t/c2O9brSpbY3IDf4Ux6YqAT4p8oDW2g -BwYSsAeeGwW4um1gq9uobtjw25srSGpvKhbzStrJvMgPZ1L13Ti5g8FgMP5ELs5oy+B7GECY54q9 -fGQ5SOGvyoXU2JMzbGR1H9NaxTYfC769AnRODgR3l+HIcpDU0sVKkUc+QG/UYqlhp8Q8X2TzMWVe -pYWrGA5uKyohNiYE34fnZnWq4L+eSwkAnpUGYsoOzLx7FbFzuAa35ltyQoQdgLiEUIjxY8FOAHF1 -NmO5pTmmHMOKba2FlMxtMUsimY/JRbMTGxssxBV/CMFiPoJwVNond17aGX4S7X7WPeszbbKLexJf -N/In28hlnAjIwAKC+VnUmOYsxFhyK63bAVtHbe6644FfB2Io5yv1EnTdgOI5yskqObeunK1JFSlT -0rDKj30MO7HlOEr0Bck0voK5V9jpo4fvxYKjCT0Yx2GhjTkr4oR4crGEjAEomvbhvrd6A/H7IMLW -CwO3JzXQaqRzhDVOf7fDTrcLzw1E+5gH4zjAxyyoNKUkTUJBgo05t/MiPhjPYSr3yZ3g9HD6xww8 -gQuhWUWZB2M4QuPEwtW4HQG/0cLvg5OfS+jO6fefkdAxq7mv7Otm870AwiGtII46LjhottgKlSs7 -VP5rrGDBoSAGJCEFLeqzs1Wp36EhjJY9rIxjhQZNRWiQ1+nAJ5v5S4/s3Cb77BgfjXlsl/344Snu -8YhkfAp0svd83r2MsF3FjSZ/uu54sNWmaeIgd91sG1pEPTpRU2+wR2crjnqMQXkHql9dAT6YH6Er -MUjpzcBbR0S6Elq/dDJ9uX9bT9uta4HpXhug76VAeq8N0Hehrg21cuDUhsNv72PLFGcN130C6Fsg -Yf4QoO8RfewwI/ajuIk+/6PirsnHart+TH4ut7WA5oTvY3unGEioGVpnAYBVgYQ8Mvx+bwNIpgyN -ZZZ6cUhoEj/N+mGsq/LFacoyp1TIP4mr0PYbUEeGg9y7YEkBswrjyHHdjQzeERILYALeb0rEKVpX -JW85/lgmi5XOWGsRyNT5oTWYjhz+AnGaK3qcKTOMzypC8M3wb2cBc/UEhDcGL1famLKLeak/n+Vw -jX1FIifa4GqjRuA/IhJlWG5cFrMTZ48oBHCwJ3a8HLBeYF9LNfF24i+JrN47G9sUvd1DNhnbjHfz -2Bv77HVPibXapBuIM6eoPFXMIqpOonjFcoR3OEpYxXa4vTYj1TDxiwUnc9E8B3k1AdMAqsZidrDp -E0bi13JkHmr1rnTXFMOxdboWYmNCnDd9zXCYXz5ax3beHeDHcsQEP56X3L/SHAeJLkiEcmYEHw12 -lOxjVOh6TOpbFc+hDSpAI2bgPkBozHZ/C0FZ9Fn3qDkOMxxi8vel/EmbxyKHBacp0yKnqnBHm25X -vmsjNFIrnt/vJmWMiE+DaED46Ymgei6ZUCI+GM+hBn/e09O8wcXkUfANC11O48Ibvv6O2RxHv+Tk -z01ohNUQH0M1yUX8Ri9zO75S1b8j7nXdfy948PJh5INgiLm+LzC5crATxBALUDCIuOsPorGmEMme -pQ6IhjNaOLE6kGWsNB2kIV8nB59s5q8FxDlL/ezseRo/qKX+PKYmtV8mGp0Ene5N3w== - - - cS9FbBdyO1kcPtiC07xxkNtrds5eo58qx9Uee3R246j3GLSDoPqlXQflSuhKDFJ6M/C1xojSL51M -X+7f1gxPeVRF6DYNzLepeN6mgfmu5GgaPO/KpENumPz2jnid4w2Yb+C3LX8IzPeQjvi0Fpi15NTd -MscKpjXB6cB9lPvawDQTBEvnw8e6lmfhZl/w8VYAnZVaSbEc0D3CrSDkNih2oo87ELHgPcGpJ0+y -7iLiFSrIPa+zexLTcCBaV9H6jjxHBVe555QLNbn4tLHBu2ICXM7Jw8YpJIRyt+7I8ufy2rTaGXht -ATx1ef7BTGUlFgDL1nb1VMDOFpyNCztxXeSdZcBe1QZrIq5IOB0e2lqWSpQTFyDWi6hUF1xr1AyS -IImnmO8SC54IiJJ4pYzFlXoTh3Vxow40i7evH2zmr3ntcQttbGP0thDdbGxX/lBnXG0ssViTodQi -DuyXPiBXKFO9YjnCSRwkrFIL2FsiLmGa+0vOpnLkOLYEhpIEEw5KCvUyOTQn6HHRfcQ4hWtWpHkO -8s6NiNsbIuavF5rnMO98tJDtglPAr9MkZ/m1PKP6YCxHyQ6AQitP+uAFIruD3Texoec4qYdVHAcG -xXbRtwt+EDiL2bgsUiMfuB2N7XMqGTa23a64d81yoOEkqRZZ5vT7UAE927XvGAmN15rlAEeDMQJe -nUv6mWzGKz+fUmgRH4znGbnfwP2eM7Qp+iLpv7oKG6rWnXpu6hY1z2Fmw4Q/HaB4QnRP+Pu432dj -1Ulp78eqi6bPvA2Thsr9jd7mfnrDrOcNae8bz70soY3z2+kyoQifQbDmOXmDmvcceQ4yGzc5Gdia -jGt1vjLTzvHBeA5zN0etbR390gJU5EzM5OflA6lmOUjnr+NeqpC1dtlUfqzep2vAxKFWQYMrzRRo -4snaHLRtQ8tLVr3R1JxuxJ6lFPDvU964G7BZpkrd8Z+Oe4J+d8btdlMD/j0VlO9K/mzJy9SgfFcm -HXLD5Ld3xzFHv955BNP7D+9SWP27O97DPQnJpawloMtNsgrMhie5cXcpbdKCz4rQjyZXCB+u3v4w -LgPtAJ9nnsozGYrn90ccIDll9CwUafUeFojyaR8BfXZPEfMXwpDqogqp7OSxBhdKJ/XBWA4K8oI4 -BWArqdRs1rkevSMmaOVzot/rbc1ykMob7crVtzW/E4GAvj/vYlJyiKDikz/aH4Jh66B5DpJbHmXG -V31bnnSkhstMHKoNeL4ZIi71DpbiOSohxFlr5AZy8mfaIZXbxe6ZBTDmTDTlJugOpnhgOdDMW8Vt -PS9C/Q3O/MuX3hTL6oNymufY0lnJsHUM4p15neq+3ECfAcDgBBv1QJoLtrr27GZw5zf9ieRhIMou -xJ1flLiFaG0sF4GXrGrs7MTfyM6ep/JeDbb77GImLgW8RvmASlxxBXBdP1Zb0mi1FT87MUTrs/5l -vQL9/Muj4RfibHz+Sz/l23RaSwM8kHKS1XPK4/So6ib79JyNKnvuVPMc28qgu1m7wW5OcM61jnJF -StfbF7puDbx4+8S1lLL1CvGR5bAIEHNnKBVOyUGtFW2yceFdX69M/kE4DmgIYIgoR7XX5KWm56sy -zVY7LeGDsTzlN+/DdIdkHVIhh/zC3dbx3dSKaYKgeQ6zGSL8hc2qnO/jtvD3UcZPZ23npL2ftV20 -fJIyUGm43N+XMryD1s1s/w1x75vPvZc8Gme9dby6TnCKmLM8WhPnj2mdI/X/g8xG3uZI6gmzCTxZ -e/S8qHKOD8ZzoLtplbb11KsyzV3Oac4/N+UZXcVzlNJfB956ItKVUPqlk/lS7u8F62bLwOShlkED -LMsVWAFJGxC0o0JLfprN0hSL7cWerRS07lMOuRezaXSnDvlPb1yjFZ2xtv30AtiNUi4jc1fyZ0te -pwaZuzLpkBsmv71xXed4A7AbD7vGv491fwnYDd/jQ3mvrQArrdMCFLwl+Tq341TaBbHfhIx9+iio -V3FZ//K4sVFunSiWAzoBbk0yiPnHjzk51YoouwAYek1Sr3FHvvZT+iWkDgVtCzSAKYEmn/QejOOo -vAo70yI8rDE/2bux0btyAgEY2GjyOpYrD9Qpnj8XLIE3COwtH3wWsiJzLWkWIFZ4XCEKjG+yi7Qn -d4xZQfPGf00L53ciUI6TQTXAXi5G+cOpHPXkyuipTduBSQaWMisYbyxnd4Q4J5WkRUx517TDEq8m -bSQQjbe7mbczf42WagttZGP0dhDda3RX/syxbrWzxGRTJgPUNO9TVlEOb3VUSnR/5DjCRxwFrFK7 -lL2BGF0+Yt5dcz0VxXNwtxC4lAFZWtKToMJVZ+HXZHje7bcmz9uR5jnIPzcibu+J+GA8R3loZSPb -Fb8g6JVI+/Hzebf3I89hLdqkRi+n4taPYKOrUMJtfOg5T+plFcuBoVFb+2lfmPEQ8TxUyhdDOXJ2 -09rfgf1uZdh65vzGvIbZzlwAb9NCv2LwtqvfMRMatTXLAc4GYwgmbxpjNX4H2TyXWGgRH4znMJ37 -NLGkQZTSAqt1ycPTlEKxHGvyp339OWHvu/rL0Kx6pzJpmNjfuVHfAgBXtvOGtNdt53sRwH0SEmki -+mLOLNX7CIw2iCE8EawFcBtEOUb92dmx1P3QeEZLKFoUstSV5oU05uvs4JNN/aWUOGmon51tQwMg -tdQrtcTrwr/XiHhd/hOO8NKMetYslRD3W3TA08T/eKkce+tJmsaGttM16oNZG81aB7ncZt9uV9oC -eoc/Or5gVKBW7onql3Y/tCOjKzFI6c3Alxo0Wr90Mn25f1uXHg13eQEC38aXCroCsPG8PQr1s6UG -UP2zZZ85cOorh1/embd5hvpg+CkIcpvc5t+d+S7gSkDy4dMetSHt7rxxBeZpNXJfwVqzo38aeRYZ -N2HK8WaB4Jol9LmPFDEzUbH8fn8D+K78ZsGcBnPlCVAQcc3NxakBerZLxGRiis7W7sScscX8oMaD -8Rzk5IGlJgfF0zhxtjtEcjt4T0xBdnZ47TgRbYVmObL8sYQatiIPXKRkIMtYDQj7DkRbDpoLMQYv -dhHjukPgxsnga3BKk8v3FCEaIb2CjOUHdRLRmjV2VdFVGrECn6KOcWK962zXnZijTkrr0m8qEQKD -5sK0o2m2E39Jp9UO2ti+6O4gutfIpvwpJPLjvhKDNclO8RLNjB+GLxRKVK9ZjvARBwmr1LYQV+/d -l0uu5qJYDvJsePlGOnbJu+xQh6esRJA15bn3OD3fRlM8x7YMtBAbE+KS+Wuew7zz0US2C15BcEjl -nLLBga+SqyuWg2QP8mwSUMbhQQGHupHY0PWc1McqlkP7fBhODELeyKtX3JhRn3aSmuXAuK7M/g0x -f9LqBaZ3wdoDb/wFD7hd+66VkIitOH6/s5Qx5Bi9oI07V8E+T6UUSsAH4zhM4TE/Pydt02VxF30k -TSYUzxEqJxtUD9wV8fs26H0A+NMR9pySb0fYt8CclfFQNVIHQqW9aTxvAarrOdy3mfv2f/PJkibK -1ydL/mGRUMSMyIilAelqfnNkOWjv+jkPbPEUZK3VzpVlyjc+GM+BDrPV2tbTL60+ISc+j8h70qHE -YcVzkNKbga8VyEq/dDI/V+/TZWDyUMug8ZWmCjTxZH0O2rehBSYr32huTjdjz1j2hwNOhMWe86Vu -mjr0P/3gOjDD86u7EZc3nmjkr8TPhujtC7H+nBJff/7bm+IyP3ujKQ4UcrlP8kc0xf8H1Rj/Dghp -A1xgfM/1U9hx4GY/ByE6m+t6IQKUFyF/mdcdODPjC6eCb15ieBIBAr0sDSpYvu2TiG4q7znjfQ1c -11nnV2TXfIsm0Xz5JCnEOQptxzXAaS9vrHwtnkJF90xE8TceV6RKtgEAWbziAeL+wJea+KuDw1zl -CYVlTSE333kLcPd4pQ0K2B/RgmT4NAwYKW/XJzS2X/MhB3l14cF4vtHTvJ9Mq0lsHdHoJOh0Nc8B -nUJvskm6iOtea6yA0jMOzSFxewX11rbCjEqxHJVAA3YMd78w0I5qrEb/yviPm0RxHKTxRrkidZoZ -IN2QtSWf47/YiWQZFMsRYh8lrGJPuMCQMtDJBv+lr9BzUTwHqRsporc+pWbZEZ/3LoLOZ4HEl1S7 -wxcqnmOrXC3ExoS45DY1z2G79Ggj25V4ImCGwJKEjay+Iu0feY4SfolJycAUTyn1YguijvIS3Jvw -0Kw4DvSO2mhOR1DIaeS5IrxUY6N5w2jeQR9vZdh6O/KNeQ0znaVgeMapARVtV79nJzTZ0zwHOEyM -IdefI54gdKZCu57LR7WID8ZzaGcTIKUW18i9wQX3ZVe9Wnhq0NS9a57D7IYJf9rZ82z6nvD38cfP -xtyT0t6PuZdBjbW/YdJQub/T37wFQK7M5x1x75vPzXdKmmBf3yn5x2VmkRP+AWKudlp5/jDIbmDZ -aVyHDw/blbJeO8cHYTnQ2zQq2zq6pb0LkRJnifHzxZebYUeWgxTejHupu6KUS6fypdjfCz+u14CK -w4yChlaaJ9DMk1a8tIKnRSat4Gh2Tvdhx1R2+PETzrgbr2lkZ874j2+GT6Y8noxXvG0+eVcAWGSR -K/mzIXv3St6ZdMgtk9/eFq9zvAU/Lk8A/BFt8VHw4w4X1RDNV5PLC0EOW+WW1Dy7HaZQnkPABdjg -ymUboLrJAYtgUtJTiIrj9wccXNWSOwBS6uD8aYFXtCHkU1n7YSuIuIR8cdfa0ugCER8vUzRNE5jz -bV7Fc1CMx0DRhFKnFSxUPXpPToEkx1PpIKYQF6rwLc9RCYqDGtcgBzekh7ux0XtyClxhWMq9yLlC -/h15DtN8co/yRTal2JMv18ioZZMdgJuAckZuwRvTyxP2veE4tpJTMmxMhu5+PTOrU62Ae2DYdsLD -Krj96pano4nIH3D71ZZn6UGU6ggXCiuadfIqi/HlSiw2ymfeH26acBnNfpQnvL0xOXECbYnTC3H1 -QvRTORCgXEMl4sDPhA/5pgxz1OUn832fHd931Hr9u3pnuF451nvjs6ptzZeLF/skvWpykINtfGkB -zHYON8RT8r6atRjaUbU9r6t5DpBbiVjltiYXUG4J8Sn3q530JqN5DtK3NODTGHLTZWMm1tvY1AVo -nmNbHdS7aiG6ecO5iY0KDcpGtq6NHJxOMZGYImD+9VQSCsVyYEye0WRH/KwoWzqodsPvwcU8GMcB -TQMZQ/pdU37uc2O+7ayAD8bxVCy7D1CO57xckLpa7o9snXhKrZhuT81zmM0Q4S9sVhXoHreFfweg -/KTPPCftfZ950fBJEkel4XJ/VxL3Djw5s/zbwt43nZsPUTSeeuMuXSecRXYreY2N6R/Gcec/yGak -mLLzjiyns+dHz4Mqz/hgPIf5mqPWtp5+Vepf5XRyhDr9HAfY6UoMUnoz8NYTka6E0i+dzJdyfzc6 -uV4GJg+1DBpdaaJA63naoaA9F1on0CScZlh0O/bMpQCUn3LIvZhNozt1yX96a9v7NQ== - - - B1+oxKfdXQHK8bnTvZA/GzI+GDzJO5MOuWXy21vbMkd3o7Wd/GtKXUlT/O/WdvV3qY5EIAxT2qtz -ifSAb5xSAokwOZlQkGRtAdSc54za9sioWAILFlJyYFz5KnlkOaAT4JYsg/cpZ3J+qeDD+DyNTTCt -ZhdQWhSQOsT5iUwtENaJaMwTQrZlOSq5AhQY2loYSJ7B3djoXTlnlNshCHHyO6Bjy3KQyl+1K1JH -PHmbxl0D0BALHvWU7xjBJmKIrr8OR5aDpBYYudXnhye3jtVy+06KFSw8iOjW1e9iNzwHt4yUEBsT -orcdT05sWCnhMxokPrDv8IrKXjuGLfh0+HF4BdY8chy4UbXemTa5w0xyZkQyvGJeW3X39H4fL/ko -w9Yz6jfmdaqBdONjCADzcLIUqy/vxJfYJOIhuoSYgUSFmBxJmAC1bXaaYAeCaKaSCyfftJqUFQY8 -0ebyeRBxWMAeB9Ea73di48UKUd5aCia5s5C/r+hN98ljoNamEHGyBK9mSslU//Jg9oW2GHx0STXh -6soHH6WiES706OSr5xfw1RWl0e73GzV2g4HiOGwHt1JuX0jZmkVx8w7A4fjxjFNAD81xmNdEJbYa -8XGpxrQVlLZ1fB0PqU3lwVgOMBUZY3Iyhpj6xmz0vIgPxvOUu7mbHeNRLYFfwKmtWA9NMJdHXTnN -DTTPYWbDhD+dKSin9bgt+32087P52jlh76drFw2fxVkmDRP7G8PsO2jnxHbekPa+7dx7UqR11VvH -qeusoMiZcYJx2tQWZ6R4DjIbgUlIA09JBJ7hPDpOVLvGB+M50Nm0Wts6+lXpWRUzOX/5dfDR84UY -pPNm4I1LSNdBa5fO5UuxvxfVnywClYfaBQ2uNFOgVRftO9BGCiv1aRpIEiy6E3umUjD9T7njbrym -kZ354z+9Vy1PAGSsbfOK1h0rLrd5ResuVJyvfsHlLhw4teHwy9vUbspzuQHWLXgu9s/oUg/AJQlr -wbqKERnlWgHTBOcIROvK1TwQgYjko+zxHUVtXlFiOySdZscni/jABqLBc7qFaAHiBOIT/9vnD2xe -YJ12rGT5sgXafm0HRDygAGKKPoXoCsA/HiOY/bITBVQ1uVpvra/EmFyfEJ33+4Taqb+4OZkt/KdL -9mNzfySEpeDIRUA9+XmXTLCBJDLYAv6bIjReOPbu2S9XLB//DFwSNYeNS8anQCerWH5/ZgFArWx8 -dtoveAlRzNQmudd1+cJSiE1pnoOyOQH+gku3U751ubHBvzL94x5RHAdpvFGuSD1lrLM5fvilAg7R -XUhWQbMcIfZBwiq1LcTVxfCln1BzUSzHFotevqvizGfS0jyVJBqqc/CzaCGHxV9zPZrnIINpRNze -E/HBeA7bokcT2a6EEsiZz+Skny/z5He7aXgOEl4wBAG3IGcPXdxRfxsX0Qu3NC4rjkM7UxguWwmU -69Z9AsogTgdWzXKgc1fb9Q0x7+/We8juBsUhlnmeJ1vxJdu171oJzfMUz+93NTLGLO9kuw+TRqvo -pydTUSXig/E8I/cbYMtz8i34Ipuyp3V9ql4ZLbUG6hg1z6G79oLDp4mzkvYnHT7V/4Uoe25GI0yf -uEqqSi7i97nKNzC6menfl/a+5u+949EG+u10dVnkjHISAJDkzvHcYZDVwJXL4ez1pco/V9Ar3/hg -PAdu1UZpW0+7tG0BMe0S5NcRp3rZOgxSeTPutcaK0i6dy5dyfy9CN1kEJg41CxpcaaZAM09W7tLq -nVaYrH6juTndiR1TKfjcp5xxL17TyE698Z/eCw9hR932L/jcU0P8bInxhbj/nBJffv7L2+BW5mfu -wHMDbC1oYO9f2QYf8mblFMvjCQuQi3yFSZvFQ+GJ7cnvgLsCLYlvcCa6Hao0g9mtwCSzBRTuwHJA -08VDAfj4Z9eP1bqlovAKMhNiuFuWHbHbyHe35Nui20mx0PxULrYqjqNSKW/yhR0MlGb3FL0ZvSsn -UOCwu/FpzD4xAVuWPxcfk6lkjLQl5a9TKUlhP7IQCx4SWXeowDxFPOY+5ykClixEeasY6FS5gSRE -vMyeaNbmwyCCxSYxA9+Hpycau9JFT2nKCFJs9dbKV9rJz34nyjWjRAymfLRPxHmx+XuuHER4sHm/ -xke1fzayK3rbh240uiV/6MVKta3kllZKfQzkSclbKB8WuEq16hXDAQ7iKF+VWfDYUhaaTMh9ueR6 -JkeWg9yaILhFHOGNS8V9PGckcowAWG9SxCxr2LXd8Bxb5mohNibEJevXPEc5Z2Ui2xWvgGmaINPE -+zcVMuHIc5TwQBEEsCHy6sVUaPpjbOg4Tu5hFcuxrbU0XEbXszbVKdMO8K6t+rST1DwHBnZt+G/I -+aOGjxNcQaqGVLH5cuVarX7PTljI1iwHOEyMgStgFpXybCr+6amcQgv4IBxHCM2sHCfk5NWvefmY -4soz6W828vvA1mfjFM162FRvhqk7ln4c/VJ8OjehM7K/AevOZsAsgLoPqul7U3gHnpvM4Q1zv70K -N1/yaKP8drpKKHIK2O8MUOHyFrpiOcjtoAeJgeWk5HalKlOO8cFYDty0jc62jnJp6SliTouXny/1 -7MKR5SCNN+NeKo61dulcfqzWZ4tAxaFmwQIrTRFozklbHLRnQ6tLVrvRvJxuxI6pFHjuU8G863ep -h2a+/I/vhU9TRdZ2r/Dcfq043O4VnnsnL67B4a5MOuSGyW/vissc5zuvVuLxMQDS/xFt8e8/He7D -nDwPwJlwYwS8CnCWlSscuDOyVNSvmIGU5MJJ+VaPL2BmNT5fTfFPCODJlHstS6g/lgd+5VrLDvht -64fE+Rl8TfKPa1iFKJ//KlHe0IUQtagxKfokN/NXmFLSOhXwKxABqgtiCi+u/hyXbkCLS8UBO078 -1dVhGLnDk7KCOZe36e8z9twKdKgCygW5AMEUJqDRuh36e4byEpPkX+2uvBeGb7Q07yfTagYbFYzK -TyeqGH5/YgHAK7k7KeUsPvIWtN1snwhstZzlNkKsSfMclMsJTtgU8zilZteD942ebA/FcpDKG+2K -2Cm/cSmaIuCnGa5f7ECyDprnCLmPIla5rc2ps1yD7jsJMhnNc5C+sWsll1rL2dizngV4ccYt+Wrh -5JdlV3fDcmyNq4XYmBBXPKZmOWyTHi1kuxBGBEBQEObw68k8raZhOapYBOagnKVxL2D9RxfRDbU0 -KCuWY/trzhcoPrzAglRj69j0ybCqGY6yG2bzt4X8SYvHEs8RTzhhiXECcGPr3rUQlt8plgN6DBhj -lpPeqZIMy1yxUk8loErAB+M4TOPJi8j18VSAp8HsRf9Ic0/Fc+hGvRClzkl7P0xdtBriYKg0XO5v -8zBvQGET47kv7HXT+V6EYF+Q0mLBMy5AuNGiuxVLo/OzEKWzFOPzqgTbsdT50DjGsmdaDdB8lSaD -NNbrrOCTTf214jxnpp+dTUNjBzXUHy87qeM4L+95HYxKDtuF3M62Ex5sxWnGOMjpNXunlvqnGjhq -kz0623HUsxHaQ1D10jaV8iV0IQbpvBn4WidN6ZdOpi/3b2uGIzkrINy2QfKOFbLbNkjelbzaBrK7 -MumQGya/vQsucyRIJyeQvPG8q/9DDod/fxfcIbYgcjm8J2vKe4mxgCmBWBp1QgPqEmhujoWY4Zks -Nml5rkFoyM8strNddmLa0rMQkzPIHYQU7OS1AYsPos5VmkeiAprIU4nzhD9EpjEVIvCU8Mxb+q8h -TMtOtLNPRPeR8qG5EteIhxAwYMFI0BN/yUlkssknycSWKRdcIKbI7+UX01ygxyCaw8uyGMSVByPT -4A7fLyHvHPMDSprn45+Rk6hJbB3R+CTodBXPAa3CaU3ml2Zl52RJNj/DJUSXsl4Ql/L2XMdYmFkp -nqNyKZMWZZ2ynM4Xc1Kjf2X+ap8oloN03qi3iD3BqTgT8qr39yJbCMVzjNytiCI3bqxFI8TgKsZn -x13oySieg/SNMy5p9374UHzxeQeDzi0e/bEz3rR3ftd3w3Nsya6F2JgQ1zyn4jlqnyob2S6FlDRN -j9fe8XMfXDH4I89RwofkMabgkpaSa7Dle6FyEz1/QgO0YjnQQWqrOR1FIadPWyz93CQ5MyLTTau5 -3SVUMmy9LfnGvIbZzpxBPrHQ1vvsedTqd+2EpnyK5wCPiTGAWGoFC6V0l08npUrCB2M5TOc+TQeO -AU+ylkeAzztKmoweWY61+PPh6pSw96PVRZthG5UJQ6X+xn16P8gy03lD2sum861QxAFwdHj6borP -zB4yGlRneKegpLeZuK4i47LYjAbM9iv1PTSa0TyaFgY0c6VpIQ35Ojn4ZHN/LUJPGepnZ9fQ8EcN -9edrUGa+TDI+BzrbW57vToZ4XMXtdH/hwdabpo2DnF6zdWrZf6qlo/bYo7MbRwVK7SCoelnfSnkS -ug6jVP468KXOmtIunUtf7N/WFHdpi3toxkaDe6vlaLgzyQOLK67kz4bsAC9jl0quTDrklskvb4ob -zNFN+oD3P26K40lmBMC/m+IdQKj05zHFojV+GGMK9FFBVQJxKv0jIQoNrxOGQis4TTZM+5MbQsSn -duvXPS0GMSXdXv6yvogil4Nw49biZabynB2w1/BcLmjGTk+ilJGJGGIsxBQTzZIchH9+LAQNbzS4 -KXxYvBJciILVBGJ95UTN+yUbkanivp4NJgsLpeBODlwXFLA/9ALB8EYThDAu7Lf5rIUCwrNVq3k+ -/hmw4WoSW0c0Ogk6Xc3z+wMMbiNmi5TbULk9K0RJmeQylV2+MBVmVIrnoKCOe7c2powOA9XXTPXo -Xxm/2iWK5SCdN+oVsU2yhgVN+7gfduA7ka2DYjlC7KOEVewobUB8dzf+S2eh5qJYjtF2zN8P6utM -552L4F+s8GZ4BnoyO/jPK8exdboWYdMiXPKYmuOw/Xk0ju1KJMEk5d4rfr5PU/EcJDxu2kYg9Ft8 -wSnv+Gj30PEjNCxrlgMdo7aZ09ETcrqU5iY53f6Z66bV3IY8VzJsve34xryG2c6a7wtgoWtXU69+ -105ooqd4fr+vlDHQ2MYY9Q3NC7moEvHBeJ6R+34ihmJcvkvFZX9KkC48NWji2zXHYVZDRD/r6WkW -fVP027dRTobak7LeDbUXTZ74GSoLl/ob/cztFIHb/Bvi3jedO7HqGOW3s5VlEVNQHmxcBamM5g2D -zEZifsQDFtOy13unKnnlEx+M5UA/0+hs6+iWdiuSlDi9jd8GX2C3jwzH6LsZ9lI3RWmWTeRLob8X -CpXon0hDDYKGU5ob0GyT1re0YKclJS3YaEpON2HHUHYs1BOOmMdoGsupG/7j0VGszRdqrHfPVzN9 -LB/EKvWzpa4v1CcHTn3l8Mu73zLBG73vZQ75AtQf0fsegRaOGxcLtj8CuCtw4cmv5Usa+AQoIE2P -fF3GGPloGz/WOTyv/kzxryRL8nb5sRPNckCzCDcyAfWEsiYCjrhcWJpXSJhCuFnsfg== - - - 4UQun0Dsed4vz1g8YgCanV6ANRqWoxIpPFAPRCoMJMBWGxu9K2ciCi4diMba/eJ7y/PHoiSMxeJy -LD5ephBgdwvCc1VCtCYLCaIxqxjGHMqdpaWge9mwfvjF+Z0IMCgrz8cXoJBEtFMwmbiG2NdGT2/a -EFx+2gP2u7h12Yl42hpnDpKo+1/Ki3MgShfjwab+Eif1JtrY3uhtIrrb2L78IcxwtbcKfERMZiNJ -nDwV0tcpUb5iOcJPHCWsYuO6P4iLNe7LVddzUTzH+Dc5LWpxEdCXV71O20nyBgAEgGLlWtiDcRxb -6yoZNibDJes/chzmn4/msV1yCrg2i4sQ6dfRl/aIYjlK9pC2V0z6Qn49m3qR9xgcuo6TuljFc2xz -CtdGcSnG24yuuHUM+rSHVBwHBnZl8vel/EmTx9dW4HtiiQXQZWPr3rUQGq4VzwFuEmMArBRjrGsB -MD2dUCgJH4TlGanfAquT571QfS8xf4Bg9sosgbp0zXGYzTDRT3t4mgjdk/22pzkdZM8Jez/KXjR6 -4iCpMFTs7/OQ93MDajr3pb1vOnec/CG8b6eLgyKmdN4s4DFMeZf9yHKMzUiKjXFNzKCBF+qxo1N8 -MJYDHU2rs62nXVp1Qk7gzePncz2yoHgO0nkz8LXCWOmXTubn6ny6DEweahksrtIUgaabtL9BOza0 -qqQlG03K2V7sGcsTQ/cfeuNutGZhnTjjP70bjg53SSvtx4oFyO1wO+X+7U7+bMhuasiVSYfcMvnl -LXEzyb3eO03xFKSmJW32P6IpPgAr3JZNKze6Qv7u523BP5N7O7GAGdmClCYXwsptcTxdIFhIcnXM -hZ0GKCS5eBbz11j8esHTz3LxrOBZLA5gWfjwNgMEp0Bp2Y8l2CDEMJuwExMLueSaflFgWpNvNSko -2pS8TpPdaXg2EcQ5lBsn6dcuRC/EEMtLi2rir34Oc4XnxPUsX84fpx94PKmAvCcW7ATIhTcekrb2 -Y6+YVgRGMoSVT30PwvGNZub9Auw4hY0LRmdA56o4fn9m4eROI77xoZrFuFshGpdL3OC8/cJKmD0p -noPSOQwUo81HvBEkNzZ43+zZBjmyHKTyRrtyj3FNxhWj5G2xHJHle5Atg2I5QGwlYRUbEI6Sg8b8 -YFHHTZC5KJZjtJ27rWmMaXUFaPusZ3GzSTVByOYken8wnmOL3CzE/CLExoS45DI1z1GbVJnIdiWS -4OcLnqHEz/dpKp6jCsbE3wKaH1n1GnMAVz6i50xYWNYchznHo+VuF6KnT0aVKh4rpwZFTLoZxjbV -Whlcvgmr7PZKUqBZDjSc3G1Ky7ysWXa99j0joUmeYjmg0YAxAJ8J/bgwZbFPp6FKwgdjOUzluNG9 -rHI2UL4QX/KSLAPVLEeo/LjTNjZwT8JTe3foNj0fYc+p+HaAvZ+5M8uhroJGTSrtTdO53winc7hn -MXXj3prBvQyhDfHb6bKyCO8kI16XpK7F87Rh0M6VmL/G532l04W88osPxnKYszwqbeupl7YrkLCv -+Rj2uhToZcVyjMqbca/1U5R22VS+lPpb2+B0DYg41CpoXKU5Ak05WaFLC3daW9LKjWbldCP2TCW3 -h84FxJ7jpUkqDYl/fCfcQPVYztnut7eBarKUo/+V/NmQHV4+iqECg1cmHXLL5Ld3wuscr0Oj4IIC -vgz83Qnv4IWnsng1ybrwwO00F+y5OeOlgbjUxgSIYcp/ucP/AjUyzikZWD/MsuxIWcvil0RcdhwC -wXdK6Y0QFzMVdFiTv9Y4NyP8rpXoIzpSibia/Q/9jK+nLtWRseIoIgzhE1GqMkNwcSfiXeCkgxRf -pp24xhRr8JeC+fZgM3/xdTLZ6PPERNyiFkHiwy+m+UU0YIQhMVpdHTAlSXhYGfLWxxI0z8c/BTD8 -OImtIxqfBJ2u4jmgXwgQPrFJA/Cn8rwaiHi3HcRlifELYyFWpViOSqSnFIBXk8V0wcy77M3oXxi/ -3iWK5SCVN9otYk8pNfjLzVj01X2xFdk6KJ5j5G5FFLmjpFOgCfzzV85Cz0WxHKRu6b3OIQMIbpfc -y7Tmh2WcnfbnYjTPsdWuFmJTQkR30W8qnsO26cFEtkvxZFry5PDzfZZHlsNKdZ9jWCqtUqkw24o+ -3PqIrjeh0VnxHOgetdGcDqGQ0/uQM2GR877R3EcLb2Qon2vpjrw7r4Gfa0PInScstPVFeLX6XTuh -+Z7iOcBhYgw/RxkjTn5/6+ZcRqokfDCWQxucLuA2NIpwvLIan5pX607tWTvWT8ZzmNkw4VXAOZ80 -Pm4Lfx/y/KjAbsg9J60OuXF4yKXLQMU952XuL8PF3Xt021tPo3cnMzJkHYP9drrMLPpdJTUOACQ3 -K08gBildon+IzyeATlf2yjs+GMuB/qZV2tZTL+1fQE6DznNYcpOTLsQgnTcDX2uxKP3SyXwp9/fi -+7NlYPJQy6ARlmYLNP+kdS8t5GmlSes4lqTTzdizlgLvf8qf6bBdYzkL8Myf/entcTS8C+i3bZDD -Y4UItw1yeCXHhrwz6ZAbJr+9PS5zXPRx7xPI4QV25u/2OMeHsvh0lozLL/mOb0Fzyx9kEnFyYUek -AxSP82mfl64Y8K8EXtdNSctrBQnHcAis07RnlwL5Jacy0l9O0/oEYp0cvuWt+zggyksoIIpElSg1 -ZSKGpUIqO4D8wlnhoeF8BDQTEWFwf8VUGGM8TL16IZokaJ1QM/MXVyeTlTMAk81NEVHLUoKXx6PA -a9gls5MTIeSZbCHiseCY5yBoVQ/G8/FPwQ4/TmLriEYnQaereX5/jiG4Xxk+Sh4RWHdAaIQUPLf7 -vMhNbYVZleI5KLHz9VMlBpIHGjc2+lfWr/eJ4jlI6Y1+Re70n5cVj8csabXLk+N8L7KVUDxHyH0U -cZdb+oLokVTQ84670JNRPMdWvVjfeV2s6CkF9FBtJuLYAVpr4cU3nvJAmuUYk2kk3N6T8EFYDtul -RwPZrsQTiJkKZCM/n0NwT6t55TlIeIAXxkUgEFNCbsOO3N76iJ43ofFZ8xzaq8JwDgd2bJKwfp7g -FnE6vmqeA1283q5vyHl/v96Dn7e4+4+Frh/m9Op37YRmfIrn9zsbGcPimST0o8pb8BeSUiXig/Ec -pvS5NA+SQcQl2EsOniajmuXQDXva158U9ravv2g0xM1QYbjY3+hm3gDhJrbzjrjXjed7QYrnjyV/ -MkzcbMZAgpB2dasQa4IrkkvwSsRpCZlGdix1PzSg0USalgY0daV5IQ37OkH4ZFN/LUPPmeon3zc0 -fFBL/fkqlNovE41Ogs32pu+799BMu4zb2Q7Dg603TR0Hub3XnVPr/lM9HbXFHnwvjgqU2jsw3dK+ -lXIjdBXGKLwd+FJr7ahcOpW+1L+tNR6sqUDg/hVKfKqg4f4VSrxQk2MVTO3PhgOnNhx+eztcZmjD -HTDxjAz3dze8gwOd/Fxw+YOeLQduQBSIJhCNzRUoaMau4u/n8ITumpHy4hlovxQILBA9asBEnOwU -KtGscj0OkHClgeDcx7w4fM9b9nEA2Yr8ATQBAKpEwPqBOM8lhjiLPBIf+fx+PTkT5TOcK4+pgxQd -HskBrrxd9+m0837JQmSuePABnynNVK7m4uu5hCx5uXz1u2CAWIAM8nCEEH36VcxTWOc17vpreD7+ -GcApahJbRzQ+CTpdxXNAkw2oXxkkKv3DTrEiQs+4foTAXR/C7pgKMyrFc1T+BLUiacNAs5+eKNzN -6F8Zv9oliuUYnbfqlcthKddEpYXQ7cLiv9iJbCEUzwFyKxF3uYE8mIhS5/SdhZ6K4jhI2xHZvAeG -XXHE591LMgi5ugvF+iX4XdsNz7FFuhZiY0Jc85uK56hdqixkuxBO0iRxmTf9NI1gnkbzym+U4AjI -s9zXBPRgaaspB9HzJDQwK5YDXaO2mNPxUyAbp0Xe0pnNbN8wmPvA5wcRtt5mvD+rYYaD18hKp0Hu -x2xs6btGQvM8xXOAq8QY0+pFQdO0P11yMhNVEj4IyzNSv4EfDneSVhjdjyWGHX30sOjUlKlTVxyH -mQyR/LyDpyn0LdHvo4efDrOnhL0fZS+aPPEyTBYq9Pc5mTfAw4nh3Bf2tt3ce1jkNbRvpyvKImSU -JNgB7LimNC3HMeZiJNCnYU2s4NunK/ijN3wwlgNdTKuyradc1qaAmNEt8us971EsB6m8GfhSI0Vp -l87lS7G/FzacLQKTh9oFC6c0M6CJJi1uabVOy0lardFsnGzEnqkU1PATXrgbomkwJ174Tz8MjmZ2 -Afx2r6jhZq3w4O4VB3wn46R+OaD/yqRDbpj89u63zNHeQg1P+2+Z/0YN76KGz2nT5ishITzxsfNn -FdwTiWEHBMRTB3LJpJ4XtghKccnXUdwTH1zeA5fbLPVNF6B9mVhuswTzxGDFA8QO0HFx3rHAl+CC -EEWiSsTDvrjsOtmKqTwBpukvF1NduebvgULDq8UgzvMTSlzcIIhhKY9XHCf+6uYwVzzlgnsr8t7x -ln/g8eA5cp5oph1+eJH356Pbz7tiXhHPqThBCysvUCmej38KbvhxEltHNDoJOl3N8/uTC4H7ku9/ -KGRjfsVdiHhCGsTgKgAxNRVmVIrnoIQOA0U8io0zgetqnyDczeh942fbRPEcpPRGvyJ3Mgc80Y3s -raqcbUS2DIrhCKFb+arIOIopSWiseOfUU5CZHBiO0XNJh+uJ+/N+Bbh0ZpVyES/Tz2tV9CvHsaWt -FmHTIlxylprjsK15NI3tShDBz/PjX+nn0zrH3WAanqMKxSkV30tJqNd53lHyW8fQcyE0JmueY7tR -k883heXtmznuaPPapE8HVc1zlPFQu39Dzp+0fMH+lDZTWugKhKVXv2snNMtTPAe0GTAGOpTQmkOs -3C4lokrEB+N5Ru43IKBxog4Owi/53b2tY7XUGohz1xyHWQ0R/ayrpxn0TdHvgxqdi7UnZb0bay+a -PHGSVBYu9Tc6yTcgw5nNvyHufdO5lyW0YX47W1UW2Z2kwh5Ix8bxzGGQ3UjUT/rZb1edruOVU3ww -lsMczVFpW0e7tFmRfh2xo/2y50Ca4xiNN+Ne6qYo3bKZfCn09yKGsxUg4lCboCGV5gc046TlLS3Y -aU1JajaaldNd2DOUAhh+yhfzME3DOfXEf3wL3CwV6dtlFOwCFx4rLrh7AQB/kmfX4IJXJh1yw+S3 -t8Blju4OHgquzuADwh/RAm/b33dT8HZXAZpKLhUhwFiXrdHJ830TIPOmj7WcYRKkUdwlTd7lYwFq -QyFONjmrfMIpFCy3I88BjaNpKUCuEdluPqAqRLzVl3wh0rOniD4Fdsgd/Lyjkk8BN2QTcVqWHbGt -5TkqqQJiVMTZpDRQvb6uR+/KmYhBUK9ALNeSNc9hGaH/8AhLbrYfu9rbobtC4jFFdERTOEqG5Qty -Z8NwmM5TBT8JsJTJYGxbz6ap9Schp2SX4nBD6d8ploPrt4MIGxOhu1NPzepU8X89vQ== - - - EqBZQVpLg6dl3p8lkLMAcBPJXeQKPxbQNqSJYjaFKO+kCBG7AERcC8Me9y7skEYgGrzALcQ134sS -ok3VFJyUvOXyydxCJSK7dvIOesmUlDrrXzaO77Pj+LTiy1+GVQZKFaUr8HBqdxRi0mPMxCnsf9nq -c5CHbZxpgV0Pq13+8iZ+WO9yi1Xpt+t2Fc8Rch9FrHKnghW0WL5fKlPpTkVxHNsw4K4KCIZwHN7M -H7M1ph9+6fa/56suW0wr4vaeiA/Gc1h8aC1k+8JCWrdTpJxQ1OLX9Z7zkePAeJzRMlP49OVIzSGm -dkOvci4PxnCAqWAMQfNMYywxrhXru/Vq50V8MJ6notk7YN/GpuVNax/DDvV9DKd041GrPjIcZi9a -7vO+RUW4x03B38H4Pulkzsh638W8hfB9XAAq6uk89I7+L29XlXoSTd6fxsBKq3XM9REQ5cJVjlk0 -G5DE4AjpPNU4deQ5SOGonjBwSqdnnjBX/6icpnKFD8ZzoIdptbb19Kuy/aL0dcVfAkjRPvdty3OE -0o8Dbz0R6Uoo/dLJfCn3dyN762Vg8lDLoOGU5ga6gKfNCNpeoRUBTbhJOkV3Ys9SdlTvf+TIuvGZ -RnLiyP70DjZQt6e8kC6DK+6I3pNdX8ifDdlbkH0lVyYdcsvkl3ewrcwx6j70CURvHMub/kb07iJ6 -40LtX25d85e0gjqUQUxBnHYYygxyu2KTP1FABRYp4O30aQeEkm92IErxV369TriUAuJqC5iuT4Ji -bDyPa+KOlbr6yWfiOu8o3bnaxWPAwe3EafJwU3P6rxXq0ybvjLgCQKTVPtGG8WqAEI0rSHftvF98 -nMzV4wMiHBvK1gKXl5/BW5f8X6tgeJIBMrh6RwmIqRgFL/zWJrrm+fgnwXm3k9g6otFJ0Olqnt+f -WADxLFukT6HU2B3dOIcT714A1jumoo1K8RyUzQkO4IzRPW4+Or8L34zeN362TRTPQUpv9FtgseUg -QhLpeaCPb0W2EornCLmPIla5Z/masubHRr7yFnoyiufYEldQJhfBLHP54luxGRdLzw/Ih5c8kGY5 -xmQaCbf3JHwQlsN26dFAtivhBD+XZ3iWNZ8KeTCWg2QX9D8UC+i5vzwX0DiIniuhsfnIcWhHCvCK -q/HAswy1dcxt4XRkVSyHWQ3bp/fFvL9P71mNXObFIstzOZte+K6B0DRPcfx+FyNjyLtQSWXTEtYK -4n0yE1UiPhjPM3K/BWebH91CQ6/MgBgstQTmDxXDYRajBT8dlGgCfU/ydxDIT4Wnc7Lejk4XDV57 -RyYKl/n7nONb6OPK3O8Le9tq7uUETVDfOtH/WEwWGX3+QmtyH4ZlCWPMJQOMp3Et3jDYK/oz1bvy -hA/GcqB/aXW2cd2yBgWElDO66bcCQkEXYZDCm4GvtFCUaulUvpT6u6H19QoweahR0EBKswKSYNKa -llbptIqkJRpNwdku7FlKAdY/4YI7sZkGceaB//Smd7ClEQtcF/kOlJFL5vVA/mzJa7nl8Nky6ZAb -Jr+86W3qHG/gdju5avF3z7uD2w3stHWRL1auHJ4RlKxVSIJh+6ikZZFYvyTfnYlrgeZNu73e8Bai -YA8nolnLa14Aq5Ktn4jyZgiIgMuL+Jo/447HugNvi8MA0ZXPzUKUu4WJ+AQGTu5pdbN8WJts5Ykr -MW4Rol/MjissT+QKcXJFpOPMX9wc5M13ZOSNk4Inis/jJooKgnM79G/EywLehOyIy8TiJC9PTx/G -u7mqoGX5+CcBdzdz2DqS8TnQ2R5ZDmimmamgKYey5gUv0i5y68g2ENLEUphNKZ6jUmcgjnl5CK+8 -grOx0fu2z3aJ4jlI6Y1+C/61m/HkxoqjuF/sQ7YOiuMAqQ8CVpnladB1yQcW+o6CTeXAcZCmZ/jR -NabgHXdsyHOOBTB3UZ4et08gwCPLUcYN4LnkZySnK9FD2WbXimlUOHActiuPWtuuOG7B91tFSi8v -KtGFGNtN4FM4Lvy1IHVvCvdQ0ts9uV0K3oBhxFdB+blZ1n2jNjzHttCOJrD19uJ9qxq4azMIJvZY -xS87bLzuBqUJnuI4wE0C5FNQOpN2KsrQhRxUifhgPM/I/QZit883tt2afuOXpUJ/HledbkTm1DXH -sUZ/OlSxZFnJ+qORiqn+tLc8NZ0RNk+cDFUjE/D7fMwbgN1M7beFva/2my95NEFmO11LFtmdQcqT -5NxTfMVzkM0IcPGKz37RP2v6UwW8cokPxnPYNj1qbevpl3QpRMopm8ZifODrMErlrwNfaaMo3dKp -fCn1d4N26yVg8lCroCGV5gck1aeFLS3VaTFJajWaFdJt2DOUgtl9xhH3gjSN5sQR/+mdb/Sy5f3Z -NPdpjbFCdi854u7kz4YMbJu0GednPxw8OLVl8cvb3lamOOsT2ycAu5Ohxfj3We8vALt9WPMNEFsO -dCaaAJbKFZAwZT8MosnXR/YTwYByFGgjXDQxZkfQWoIt91SC34mSJMg9FecKZi7KXMAqxWUvEkH0 -Dq5UHi5Zdvhfb9d8YbXWMfjKFpCEeRvkG1uh+TUYoS3ldjG+xnm5QZccDdzsg8z71cVhqt7mSymz -K699JKI8t4t8Z1n8PgOP+7Ppr/cDrYKOamy+FSwCPhjPxz8Jr7udxNYRjU6CTlfz/P60AsBaGUIX -9TcG3ipxysXtK4I6sRRmU4rnoExOIMXkZW8U4eXxGD36V7avd4niOUrpr/oVuVMwjymqS+JWjr/y -ncgWQrEcIfZRwir2HEri7Nf5K2ehpqI4jlH2EvMQ9WD9ee8i+HaLyzXjZNZQld2wHFvgahk2IsMl -p6lZDtuiR/PYLsQSTNKbkAucOIXdZhqOw4rzlHvHckDO5weItXfo+REalxXLsZ20NJo38qZTKpOQ -bWw9gz4dWDXPgb5dG/0bcv6k1aNwcIKDmBZ6wnG/jSx+10ponqdYDugxYAyB6MStZNxr3S6lokrE -B+N5Ru63ILsDMnOcBfTT5KvmldFSY2CeXbMcZjVE9tN+nqbRN2W/D/p2MtaeFPZ2rL0MBqz8JBWG -i/2NfvIt2G5t92+Ie9947sF2N2F+6+QDqrYsUq5Ih5OEH7Y8aa04jrGaDF2ctFMvUp0u5ZVXfBCO -A/1Mq7Gto1raroCUcVnl16bA2SuOg/T9Ou6lfspRtWwiX8r8vYjdbAGIOMwiaECl2QHLNml5Swt2 -WlHSgo1m5GwH9sxkB+w+4YU7QZpGc+qE//gGuFnKIf7Z72/iZWhub4x5kj8bsjev5J1Jh9wy+e09 -8DrHW4jddv1DWuBjALt9wViLaa+i71bw48IM3wBgzSUHP8Ccrc6kXe3WVFrEHb5SDkn6JXyYqSCD -KZ4DmkY4NpBhgMxHWEvbAkS4Fm+Tj5krpnj5MAe5QzkqIcSw5MlMpduoWI7Kpib46CXkgWrlrEbv -iQm4uSnm+Uy1XaRY/lishLFMuMmDD9vTUoB9YUECxwTiVDpIIOJdCdhF8POO55e0tYqtuTlfBBNi -xOkQh8+w6/6XkxWcvSlPtquNjtq0Gdj1w1gn1htdaV+BBhQ/v+A56rgTw+xCJu52fpz5S7TUe2hj -O6O7h+huY/vy7peUG/ut2Vpis3MyVRzWnFMO4WfT1SlVvmI5wk0cJaxiY6/M5Un1r1ZdTUVxHOPc -cvt1BhCdryiTJ60EaHMTxE56dXH3bQ3HscWuEmEjIlwz/SPHYb75YBvbJY+Q5picnZWf1zkeOQ6r -0V3Sr6AXuow2uLG40PWZ1LsqnoO7U6k2cjYntWGy644mfDTn095RsxxmOMTk3xDzJ20eIJ5oOMkq -mx3Ys135ro3QWH1kOcBJYggnF3Xcx7KUb7Tns4mjhA/G8ozYb0B+2w98K/Eojfcv+8RemSEwj64Y -DjMYIvhp905zoFuS3wf9PhtfT8n6T4ivbAGorOedza0VuLxjtXtn2nxjKgNrtiayb6eLgqJcSXsB -thBwYJElC4M0LpF+np6Prpwvw5Q/fDCeA71Mq7Stp15abQag1ZVf7+dCjixH6Pww7rVyWGmXTeXn -inu6BkQcahU0nrLUgOaZtKdBmzS0kqSFGkvG2T7sGUpB/T7jx3oxmsVy5sX+9DY4GtsFsds3sN9z -xff2Dex3JUff4HtXJh1yw+S3t8FljkYDmfzjNjjefsVHhL/74J0+ePLOxRl7wK7NroJtefnEPfm9 -8AfkmVx+8SY5m1C+rwIadYppBwfgoIWC4Hbk+f3RRiCdjAV0HDDhy1dXEOFj3Oo/bKwgs7YUo8Yk -DzUvOxEPMID4CtTasBwU4DHQMuOxgzSQvI62sdF7YqZ1cOuU52ONWavsLcsfC5VY7ZSppwwqTB8F -Hk8MSL7SgWYq8hmI6ypmEUvXFHBoLhnPX/JBNeb7kELEvSUQU4Lp68/l4SohLnPoK4NqrWMFE6YH -65Xvv0I05WkPvPe8PhHlxZKFaObdzpuZv8RKvYU2tjF6W4huNrotf6YNrneW3AFLs4hJNu+njIbY -1ynTvuI5wE0oEavceCYbRAkPXy27noziOca9yWVP6aA5Y3Yc6lOWAgg6uwQrqg0xv2akeY4t1rUQ -GxPi0hbQPEf5aGUj2xXXgJ/jAiR+vdQr80eOg0QHrKCTt46BKmhNVXsbH3rOk7pZxXJYZGQmc9oX -hikkzeKJtHn/wHXTYu6jmB9E2Hq78f6sBtrNYiFmWuXyuLBa+K6F0JB95Pj9jlKGwKaDduQ9tO1S -UnGU8MFYDlN4KL0DdPbw+NwlB0mzCc1zqLmfj1Mnpb0fpy6ajd6mVBgu9vdt0zfwtInxvCHtddv5 -XsTh8OHzF0O7l3MgSvdMYGlKUSNECVqJaOcMMkC3LPM+LJDR2okWgzRjpekgDfbHpOCTzfu1hjhl -pJ+dHUMDHzPSKxXE65q/14B4XfkTTvDKhHp2LOUP91h8vLPE/3ipBnsnJX01n+1sWfpgdkaS1DFu -tt2se3PgVB9AbetHxwGMCs/aJzHl0m6Hcl50GQbpvBn4WkNGqZdOpi/3b2vLB7NjjMcMtF0QyZcD -+bMhp5j2Cj1emXTILZPf3o/38oTvjX58Sg4yrM0f0Y8fAUmeHJ5PqR5csytne+QwAEB/QJumHfJO -oGTh+BcXnqhgNsxSUs5zqX9BBB4kiGaNO2hbXOT+HQDn5h1tVC7EeJuEnswTadwal4nlU7gQjcl/ -uaQ8JxPTvxd4cHyEKh+ehRgzzS9P0OQMIwfiVLqgauYviRDEFXw8fCa1sVz/xZd7RK9QHpEprAVX -ys9AqMtPkovPQq6HT6ep4g5VAy3LN3qpb0CSH+awdSTjc6CzPbIc0JycVrlcklLhFCrmUIGm7YJP -uSl+1/fTO3bCLOrIclSDCeBnyejzQCbuINnN4H27ZztEsRyk8Ua7InbK+QKeVE/hO7hgv9iFbB0U -zxFyH0Wscns4hZSLTJPp+wk2FcVxkLZn+FGPYL+uFaD5pGsx04eJgn7okhsuHRzFcw== - - - bHtAC7ExIa75TMVz2C49WMh2JZLg/XngG8JCEs91N5tXjqMkD+HDBrkWCmBDW7XeOoiuK6Fh+chy -lOjUYk7HTgBCZkjHNcirM/cN5j4q+UGErbcZ789qoN1kjFAssrFrxR1tVr5rIjTHUywHeMo0xrzO -TtRj4hJ37NdzaagS8cF4npH7DVhyl4FMvTfJ0MxS0U2Py06Nmbp1zXKY1TDZzzt5lkHfk/0+EtTp -UHtK2PuR9qLdE09DhWFSf5+jeQOanFnObWHvG8695KCN79vpmrLI7iQX9uaZ7Sueg2xGIn4a2AUz -P2v7U3W8cosPxnOYpzlqbevpl3UrICYAtOXX+xGJI8tBOm8GvtRPUdqlc/lS7O8FJ2eLwOShdkED -K80SWMpJy1xWttO6kpZtLCmnO7FnKgWd/Iwv7sZqGtWJM/7Tj6XLzQCBFp8ztHiBJ48Vh3x+RRzf -yaEhVx6c2rD47T1wmeJ6A5plMSa5Arf8GT3wEWfSAaw2If9AMJdtvGXiimfBcX9E3g9OzhgQfvL6 -NroDfi2gp1POAHwMz7OmiuX3BxtBm8rQRKk0q5jPiTaZXPZEU4Fvp/zKMKQ262J3Ykp20/ySo3Xl -IumR5aDwDuAqh0eXpTqblyfu8+vgXSmXKT8CDeIc3I773LL8uTiZFhtIYPA7zvrdemQuuOsUSnta -iAA2S8T9moNFUpMUJHeqTH5BXIjBlStZ9dkhW6oTEJOga18XPa1pKxD4WJQ+EQdVcsoBol9hvEi4 -3LxW4iov0CdiDHGf0evMX8Ok2j8b2xa9DcR2Gt2SPwTLovaVGOz8seLtb0niltzm5hqlulc8R/iI -o4hVbjxPLhlpqHc7O4uuJ6N4jq0dsbPlZTyUhJNxsToLSbjCst+8OW1ImuMY79wIuL0l4INwHOac -j+axXfEJENPjyEn6tY31jvCR5bBiPSXiMeSjh97NFTi4jQw9v0k9rGI5MCZqMz/rBiGlNz4/1WO8 -D2/Y+f3m2lGGrWfK96c1zHB86Tulf1TcLbX0XRuh8VqxHNBwwBg48Yx/BFfw1c+nFErEB+N5Ru43 -gMptijHiG6YMAb911p2aM/OKmuVQuz/t6mn6o4X9QV9PlX86tp6czwizJ56GKpKL+I2u5g2gcmb3 -b4h7X/f3XjNpgvx2skIoQq6SDAez13lHfmNsxknET7rZr0qdL8mUV3wwngP3aaOzjauWlp0QMuLY -bfqtKTv3yG+Qvl+HvVYWK82ymfxcic/0T6ShBkEDKs0OWK5JGxusUUPrSVqs0XScbcGOmRSY8lM+ -uBOiaSynLviPb4SbWBHGQwNTPlc88tDAlFfyHBo88sqkQ26Y/PZeuMwxWeUNmHIEan2Q/Pe3wqea -l5o9w5b/jy2D95ytQcBxR/9WzdR5fF5I3sGuMXnLNRefcu4++REb7Ie3+ROwmwCn5P+y87Q/tewm -kyJR2vUu7dsddlix/P4YY+OSvJtJY/gUqTMUq9BwCQs0eTKhSrgam6aSkpQ1v74NYpiM0JITlAiq -OA4K6Bb9FCtSmnzBZyODd4QEbQ1lNhUIQ3P8seiIpQ52Cmmpl/QPn9NBj8gzRSHKfdBiFClRDmIU -O7igR+BKtYXFm9PlspgQTRCa89NSaX5OYQhEX44aUWX0tEZsANNOfwDEMnk3uhJxRR7E/dtP+vk0 -iZHPqGN3I29n/hIh9f7Z2LbobiC209iW/JkWuN5XxWKlXrFLSugqZDPXKdG+5jnGR7QiVrkBXJYW -99ku48tOJqN5DvJtLrHGGE8k+JOGAm8gNQw0K7gPD8byjNi42uohl0lCW2mClHiSg0tp80Au/KN6 -t3b0jY1+xfI1y2Ge+Wga2xWPgJ/7KLNc0yyfUaXlOaoutz7pOKTRncv/2Fhk6LpN6mAVz4tWU8Sc -U5BJZWq1mjKLEl2q8B5vKlnn85NnW8eQT7tGzXJgPFcW/4aYP2nxDu8Q4+ILFngJcwHdPa561z5Y -pFYsv99BuvJ8MnTmbL6jdD6VUAI+CMdhCnfpZzCMZcoPV19zjzSFUDwH7dILcemcnLcD01VzId6F -SsPl/j73ciOgUot5R87rFvOtBUMaepVkcEkhdPc3HiCKkKbADYMkIUpeTC8Y/WSPUm9DwxYtkljN -RzNTmvbR2K6zgE8969dS4Zxxfna2Cg0V1Djv1gqX+wvcYplIVHg6zXs+7mYO2C7fdroAfOiFpmnh -IA/X7Jdahp+ruI8769HZgqNC4tEjUNXSrsLBd5AlGKTtl0GvtTyUXsk0+jL/tla3c0kfcwoZ1of9 -ndck7YJ0co1P8mdLXkGOtfW9M+mQGya/vMctU7Q3WtwzjtYk9/IrW9xhYIs7Oey0OZNK7ZzizVLu -jq7p3/iGZ9Pem+263wi1ISaLcnY/nYQrISExtrgQW88/KJbf72YESyEgjTF+//IIGsCrQKunvEVC -BBhn9yOmQsNz5xZXHOtH4iPHQS5drpyvKXPCOKY+DH4cvCtlIgqaHohzCDtMQMvyx1JWL+8uJ5du -4/Q85g3zwaEqEPfv2SDOLv/lE/8Qhz9WI4ZWzxQIcY2wPrdXTSA6v8xC3A+YUW309KbNwKUZrunX -KQtx0ZgnMYVGEOuJznyRKekVxBjKKTE19ZfMVe+gjW2M7hZie41tyh/CHVc7q0BByP0t9HHqOe+O -TonyFcsRTuIo4S52EszOZj/m3V11PRfFc5BzA+qNdMh2mPezdiLgcAt8SdiPlmqWg0pyPfrGRr9k -+IrlMNd8NI3tkkNw5ZYnfp6G8U97eeU5SvikxcWsScf4IlMOeevI0POa3L8qnoO6Z3K/XWxhihkK -ZOsZ8lnPqFkODOja4u+L+aMWjzvuM1Ydt0Lr+W616j37oJFa8xzgITFGSH+PMeoB7/PJhBbxwXie -M/aL6bfAO+DYqnQiJ+92tLmjsVIroI5c8xy1T89HJprrKDlvh6Zbxs40fz40nZvRCGNnzpGqkor4 -fd7xTj5Alf6GnPeVfse1HyP6jmrwD4uBIicKHZt+UYs6xXGQvRTt7Ce6T1df2g0+CMuBW/RVY1tP -s7TCrCA5+HU90X1gOEjbr6NeKoCVYtk8fq6aJ+onwjBroNGTpgI0qaQtDNaSoXUjLcpo5k33HzeS -J+btP/a93cBMQzjzvX/8ee61Kh6Hr/fz3AC9l7XZyZ8teW7IO5MOuWHyy3vdMsU7x7kXl3YL3nb9 -85rd3wVt4pMLmktNv8by9KNP4XyOs3wZj+Z5FdTI5z9vPsLyvPtsHb5dLfMTyFfx/P5Yg+uGEfCN -KGaci2u9UxmBUwaivDZbRUx6ELntsuwXheyM78x4aqJ+y1Q8B8V23G42i89yWgSqjY3ekxNE8fcg -Ru8KXMuR58/FymQuFld15EN9OWglNiTTicDpsrnVCSIEBnEH7/Prx4xrS7A2M5USFMQoH5ttSq1L -XpyIVmIIgomJS1cbXb0RS7ApqiPzk4+KFf0bxMkJcbXPK3FxXoMQF1+uXqq5v4ZMtY82tju6+4ju -OLY3fwjjRG2vcqkzAqgOBcway0MuXKlE/ZrnAF+hRNzlNpKCev+8eEqXXc1Fsxzk4iYcAUhjPDHW -zxoKHMI8LV5KGjO5/ZZ2y/OU3PehWZQQGxPi0g7QPEe56aOJbFc8A2aZvI+Vn7u5XNs/shxVPdr5 -Y17xwSZl29aV+9cqRHTdJ3W0iucp4W+DhaThrFuSps2ckZq3jlGf95Oa58AIr+z+HTl/0u69RTGG -gdJCm+B3zJB29bt2QkO34jmg/4Ax3DrLGLMtMP3nswsl4oPxPCP3fawTHLFzpVL3a3lUjVkttQbq -3jXPofv2QtiimZCS9n7YumP6RP8X4tW5GY0wfeIsqSq5iN/oLO/jnVDbf0Pc+7q/4/CP0X47XTEU -OaPkxrPdiz/Nc5DhSPifzQ5WfL5KU97xQVgO3KutzraedmktCjFxmB8/lw/UdB0GqbwZ+Fq5fFQv -ncvPFf90FZg8zC5oeKW5As0+adODtnFonUmrOJai053Ys5WCbnrKIfdiNo3u1CH/8d3yVJbIcluL -SxUF+2RZswIK8bMlxifx+XNKfPn5L++Ry/RSOnId8mRxOPZgf2ePfPwTmMk7fawAKpLrW7Fcmlnx -4cqV2zqry1d9QMQGlttfrsD9rktuduHykg/B7EQ8KiC3zJbsxPFzSRLkltm8FGBU9BtwQg1PEqyh -AL2aj2VxixBFpErE1acw7SWNS/9e/ZJ+vc75k1wlujXfN6lRBb+WFzNANGt+20lP/cXDyWzxgdF6 -9zHFxWS9zCnurflWlrOlxwfJPJx8+q/1qSVMTM6TWJEx7ipoeb7R3LxdhOlJbB3R+CTodBXPAQ1D -PFktRomqdllzs03esfb56mOtarmtMKNSLEflzwAil6/eGMgYt8vejN43frZNFM9BOm/UK3LjHWvk -yimXcBULme9FshCK5QixjxLuYk853SyZZcdZsKkoloO0Ld1XHDgqr9JdcC8uxX0757rRz+5p4g3P -sWWuFmJjQlzzm4rnsG16MJHtUjxJv17kdAJ+bQqy55HlKNFjSr5NSanD4qvaWw/R9SU0OB9Zjm2s -RZ+0B2eRyoiAnGPrmfTp2Kp5jtI+tfo35PxRqweksfSbsM547mAji9+1EprsKZYDvKUgMePiK5AO -cO14u5SPKhEfjOcZue+nYtF9rKEU3unvss0zo6XGQH275jnMbJjw5z09zaXvCX/b55wPuOekvR9w -L5o+85VUGir3N/rK+4kCNZ83xL1vPvcShTbUb6eLzCK8AGNFkxL/JfDsYZDdyMFkk1LvAhl1vqxX -zvFBWA7zNgeVbT3d0t4FpLTSakbncfV0FQYpvBn3WnflqFw6lS/F/lbYIrYGTBxmFDS00jyB5Z20 -2KXlO60vafXGUnO6CzuGkvtE51xxN1rTuM5c8Z/eCXdL1bwLHylHKy9iOhcP5M+WvDTknUmH3DD5 -5T1xmaK58SRmFLyE+ZeeG/+BnrhP2y5Fnr8cXs8FttmWieK2QPS1mgMR4EYOb9wu9onRBHQp4F+m -fGDeiWCJE3Y1OUs0P4dFiN7aHXBXXjtxZsK9gCdMc/onaCJQoXmktCCu5fyDXZPE85x+HVIOko+r -Ci0kJwva7PJL6SCu6yq0EMqzVGrer44OU12jScImT1Xf20zEaUnxEPOfl3yDWASbYyKGOfE2O0Y6 -oCGcMQJB/2AcHz8J0KZE3zoiMdHpFBXH708ocE81X6hcUy5cIWITbbar0MJUMYSJdTAzUhwHJXCC -AIfHLexqk7hh2UV/HfwLW1ebQnMcpO9Gt0XqycFK3JoW2sy9nce3qGI5RuxWwiq2XZJf8tgm+bBR -1zkoL6JYnhH7Fnj2Ui7+rTjLsOyyp0QoyOgTMBAvOBrNcZChNAJu7wj4YByH7cyjXWwXggZ+LWh1 -+PU0r7tTaVkOK8QBbjqjlvJZWxtzDN24SiOw4nnR0s8DfrtsHshWV59hH6ktnI2emg== - - - 40BvrjbpfSnvb9J7GPEZLXdBmVVqcrXmXetgiZxiOaCVgDHQe086k+83FXDzVKapBHwQjufM/DIQ -rk2rhBQ5VR3SFds6dkotgHpBzXOYrRDhL8Qimh7fEv4WUvnpkHROzttB6RbiNzMbKudpF3PTbC5u -U+LTqSrfmczATKCN5dvZSrGod5Vs19mazGuOg1QOMGFoFxBctZA7V5gfveCDcBzoXhqNbT3V0uYD -pAReMqSUioItwiB9N+Nea48clUun8qXY34vFT9aAicOMgoVQmgzQnJIWsKwepyUjLcho4k03YcdQ -Chz/KSfWC880kFMn9sc3tV2smN1zA/w9V4TvuQH+nus6zQ3Cd2HSIbdMfnlT28gcw3wH+RtQMl6f -Ef8VXe2x0N/FpTg74ZPBXKHZMgpbDCkdszsUdUazXJw8ICg0l1GM8G3sCYN2ZPn9cQa4ThkVagbG -pqmgvtm1JFo0YdklxEfEJLSdph3zOYNDL/YjurBjtDYcB8V1QETlA0dpnHqoXg3elRK4czYaIc4h -7jDKLcsfi5CwFAG/ctbkq0fVfHAvFMQ5zDssoUwRRLOaHcItLsDLioDGivNOlGtIHsd3dlzAjDya -iLOrGLZMGT21aSuwudcjH1+j32mIP6Atrjwnn4ipnFqEGOcp1um0E3+JkXr/bGxbdDcQ22lsS/4U -8PdxX8mFrpT527QOaOgsyzr3VUpUrzgO8BBKwCo14OCcsTkgfLXoaiqK5SDHhnPl0ih7QZs8ZyX4 -eiuoBPOc9lOBpFEsB9XlevSNjX7J7BXLUW5ZWcZ2yR24KR83wc9tnJ/20vAcJTywCwVNEd9k/Bwq -qG0bFboukzpXxXNQKwoDZSTEsOTLr1vPkM/6Rc1yYDDXFn9fzB+1+FSe2RlPu6UFlhPbG1v1rn2w -KK1YDnCQGEOuKET8J1uhVs+lEUrAB+F4ztAvQ36HjBcq3bsp2qpuZajUAqgT1zyH2QoT/rxnp6nP -LeFv4ZWfDqnn5LwdU69aOnOMVBoq9/d5xluQ38xi3pDztsXcfAOkjebb6TKgyAkoYTchEPD0YJC5 -2NmKdsqVlwtF19EJPgjHgd7lRV9bT620rISMJnr5cX2D/sBwkK6bUa+VvUfF0on8XBGv9c+EYebA -YifNAmg6SRsXrBFDC0ZajtGcm24/aiQF7/uU4+1GZRq/meP901vcKSWqUN3LK963Wyuw9/KK4F3J -zjTkyqRDbpn89ha3zNHdaHEvDqhsbv27xa1a3D65nhmbFL4Bj1kXdLUorxIsPu3WaQfMNsAiwEe3 -sE47xKl18sEPV1wLUfH8/iAjcExoYKKCcRi4gvfKpZBElDeNqogBN2CS3KmytztxBu7TavJTXw/G -c1BMB7aTWeDF00ASDTY2ek9OwY/DFRsQo593xOSW58+FyGQuyaXji2haHlMyQtgQpgNidBUtLhG9 -N0K0y7TDsM0zctxkbckE5p0YYW2L+5i92YnWyi1H92KXRBtdvRFLMB9JGoi07DflhSihKxEljBZi -jPIdF7mMWeqM2rm/xkq1jza2O7r7iO44tjd/COhbbS8x26SMaVqkbFnjGr5QKlG/5jnCVxxFLHKn -LSmpp3dz+HLZ1Vw0y0EubkqxG2O4EHeg6XOGAocg9wpRyZhp9ru6G56DSlw9+sZGv2T6muco/3y0 -je2KS8AsrTf553WWiuWochHog/iShPza4q7axmJD129SD6t4DoyLymjOe0HIaR3KqDlmOW8bzZ1O -Wjt63DWvNuHtGcWBoKl4aQpypiU2oXSk1Lp3LYRGa8VzQK9BxkDlmMaYjd+Bak8mFErEB+M5TOkp -0XNWCm+/FIM57yJpHnFkOULlx122kXG7Ap7buIP26IWQekq7b0TUq19IiK1Q70D9HRXylq3c6XYz -0e/aRgU7vmPm9576aEP5droOKGJGZLxpW32UvrFmOWiLIrRDP4L+UAuyc7WXcoAPxnOgV2yUtvW0 -SytMiDkFI79Odd9K12GQyptxr9XASrt0Lj9X0rNFYOJQs6ARlKYDNLWkrQzanKHVI63NWP5Nt2LH -Vgp695nQ1/O31DPT0PfHt77dWtG34wt491JxuuMLeHchOvsk7j+nxNef//aGN+bn1jtIJYtNEXr5 -pejdP/HCpVtDcub43I4vK85VXCwnX9vXFA1jCdghhXucWMIt3dWbgr+N73A4IYHIBJj+B2E5oCdk -l/zhTqqY1foK0Ot8uThjwmJ3CeULnkkqs/NOw/FfEE0MO9Bqy3JUMoXDJAJFhYHMExO5Hb0rJ1Di -0MkFsaYqmuePhUis9iqf4XFjKM47Dl/+mIobQ2sFhZvz6xBy4agcEQNumZWnxVdcj3rSEMXkXtNq -dyC8RQ6Y4V5TfAJEa2VwtRE78Cnp9oiGeEbH+PAk4jynvJXyBIUXaD4hlue39cxfYqTeQxvZGd0t -RDcb25Y/1PJWW0tsNn54PKaNDE5aw12dMuUrliPcxFHCXewpJ9FxLfDjdNXpXBTPQe7NlrIwlrfi -LxiKre/OJdX6ivZ+ZHlK7PvwnEcZNibDNfs/shzmoI8Gsl1yC7ZAy8nPy/NbmuewUj2l4XKGJmXX -YXlC17fhoes7qZdVPIdpnhjNaVcI1ESxJHnMZlnW+0ZzH4T8KMLW2Y5vzGqg5eS2E1Z5ijuYdLv0 -XSOhQVvxHOAsMUbAM1URV8j9XDFSz6UVSsIHYzlM57aAClmTVXrNTbJ8QrEcoXJm5mrgnoTfaOb3 -kbtPR9hTOv5nRFhqO/f1fd12vhcb2GXsJ2ufBR1ktN5moqk4wIkoItr9jCjdr9T30FBGqydaD9Kk -lWaENODr1OCTzPy1iji30T471seCH91oP/+IFDVeJhqdA53sPcd3Lz9sV3E7XSE+yHrTnHGQ037d -OLVOP1WSqx324Ftx1EsRyjdQ1dK2g/IibBEG6bsZ91pj5KhcOpW+2L+tJ+6WHZF7bvC75wP5syHL -P16AuuOX5JbJb2+LyxwJYMkJAO/yHe5XtsV/BsDbTNH/5afy+mwF1kopgxBna0u7KxHn8pdmfsI+ -Aqw3bfSPFXu4EAUqINGCnXfaBAg9EOcp5F8Dvtgt5i83m6czSMQJARHEaHYS7q66OTmI7EgAtWaS -G0rSLPk2biX6ZCYgLlPY/1JeBgBRPh0+2LRfUxHMdBVhfRZWdJL+jReGXeK3hHmHMpxCcuJ+ivlV -4UJcUxor4voY9gFbnm90NG8geB+F3zoiceHpNBXP7w8s8mUPhojBLAYuoJLBCxogJLBzz0aIKWmG -gwI5gL2maclC1hxEj943eLUxNMdB+m5UK1LPyQmk8LzitE8BK2Tbj+5TxXGE1AcBq9AGLb+1nOL9 -yj9oT3JkeUbqmwjeaU7ZItbV7KipwYY8ui1PwJ93NZrnIENpRNzeE/HBeA7bm0fb2K7EDgEOnNNf -4ud1eTTPYSUZoiquBaacyFhjKhJ26xu64ZUGYsVzoFdU5n4+XkLOaRY5Y5LTmvvmfhODvB1961jy -OzMaaDYZayAt8eR3aOl22bsGwlK6I8cBdSWGwP0bDOHXtQJ5n0s5j/I9CMNzpnIDx9uEkIrcxabS -25qqbLXS1HapH9Q8hxkKEf5CRKJp8i3hb+J4nwxL5+S8H5YuG7pyLVQaLvf3uZab+OPa3N+Q87bF -3H3RownmWyfs65KxyBnwzqYDnl/5gKV5DrIYCe5ALlvKk76nS/SjH3wwjgM9TKuzradd2oeAnILb -vOBSVYx8HQapvBn4UqtEqZfO5UuxvxvMW68Ck4caBoukLCOgqSUtZWlpTstHVpzR/JtuxJ6p7HDe -JxxxL0rTeE4d8Z9+4BtzncKcPzDLgzA7nPckbyVV8mdLXl/JTyYdcsPkt/e4ZY4p6FyH845rPljz -K3vco+G8xRHji6l4goqmhe96zs57pS9IoHgL17nkaKIvEKz2Y5VvZ9CwcwWg7cjz+0MNgJTyqwom -eVdbMIZBTDILcVmnHWM4l5rOy2e+SsMNu8TyY53WHaKvZTkovAsI1OIWGSjt0LjL3ozelTMthDfO -CHFxT3jBhuWPhUnB6IMjl2/u02p3C5ITkom42HXHNM0QyYnoSp9UsEJjNrVXwDmB5Ha4ClXqEfx6 -dQKMFpJR+tjVBVcat4LJzYhzML+4o/VOeCklxfOPybhYifnVhPSXIsSDTfwlTOottLGN0d1CdLOx -bflzkN7tzirg2GFK6YGbEb/d10pV2lcsB3gJJWERWxKmGYlXflquu+p6KorlIOc2LTZ3x3zZUuft -xKUEzyDxMimdWqf9uYKW56BiV4++sdGvWb7iOco1H21ju+QRcHHbrUF+Ls/7PAjLUaKn/N7nB6mW -/DZ8hdxt4kLPaVLvqlgO6koBK3H1qB+mdf/ewM34tGPUPIeFc2bwb8j5owYPmE2BG01LLBd/N7bu -XQuhkVrxHOAiMYZgp6IBVcFrz6YSSsAH4XjO1m+geudWQfAfYcm3kKipUhOgXlzzHGYsTPjzrp2m -P7eEv4nqfTKonpPzdlC9aunMN1JpqNzf5xtvonpri3lDztsWc/eJjyac1yc+/nEpUOT0kvSGsD+/ -qXkOshjBOU4KEqT0WpGdKr6OfvDBOA70MK3Otp52aYUJOQXMOf3c+acRtTwHqbwZ+FoRrPRLJ/Nz -NT1dBiYPtQwWSmlSQBNM2sugzRlaQtICjSXhdCv2jKXgfJ9yxd04TSM6c8V/eu/bL1X1Zv1IFrpW -nO/lQP5syXND3pl0yA2T3977znPUHewTON823xP4I3rf33++W2DXItxaKu/XdQepEnckNFP8MYgC -epuIPvgnOKhAI7mwXwUX4oxcJhV/iwk7NJf8W4hryXqmFAQEl8nb50CTKRcXQHyhAVfNeTx9W5IJ -nKLC3y24KlKKe9Dw2gC+upnJPUF8ccQAxMmW5zGOE3/1dJgrLrxhWsuaD3jjB/IaM36QZuJ2weTl -hyTEZN0OzhgtFJCkjbOfd/01PN9oX96+bKYnsXVE45Og01U8vz/DQIHirHVSyAYT5oqM6YzNNbk8 -jt03FW1TmuWgrE6gv6LJYnpXrguo0bumT/aIZjlI5Y12C0a2W5DbRJtPRXc3IlkGzXKE2EcJq9gx -BUMQU35jvvAVei6a5Rht2wVPkEeTEb62K95FAPJW5FGplqkQgprnKbnvX8NWQmxViOVFiEtuU/Mc -tkuPJrJdCScCMWhF1/Yj7KSW47BKHcEXX2pMuWm9MRfRDbg0NCuep4S/C0IAyER5lEFerTH51Do1 -6vPBVfMcZTrM7t+R8yftXvA2XSnLnMua16vftROa7CmeA3oO0tHGIZ80RoUmupCPKhEfjOcwpbsP -Oeeaam8B292Ytz6fcD0IyxEqJ5tUjdsV8Bv36H1QomMw6kbZUzp+MJZjgywznXf0fdl0vhtU2EmW -OPu0Ufxc4XPzLUG/57YitwSuGedK19wVY9uV+h4azWgSTasCmrfSrJDG/GNu8Mkm/g== - - - Wn6e2mefnQSPRg66z36++mS2yySjc6A77Z7bu5cevq5hff/lH7YVHmyxScI4yF03e6bW+ufaOGp3 -PTr7cNhLGMozMOXSZtXRhdBVGKPyZtxr3TSlXDaVvtS/rR2OzvYimgHqlMmnWAtESXbBy/566Ss5 -vpKfTDrkhslv74PLHAlayQn4b/MRJ6+Bw39FH/yIc/KdZ8ABn5TiV3bjYcpPYwimUnSo3pJH9AV2 -ATCh3uWrr8bMO6q2PB/g1qUe7tQsBzSMbMyH+qWqmeOOQ7zK/ZLkyOvzIhBwlm+Udn8pQ4j4Ngmi -vAv7YCxHBVOAluGDJQaKSGA2NnpXzjBlRwqiRQb3YDx/LIsVpEGYnNwtCtMOP5g/xOFuURUSRIFR -xtWkcsxDMERxCAdXmMqD7kKTr8C4AfXExvLOlwtQ9ZFPqoue1rQd+DUZllyLWlJmu+5gz3JLGncz -fQhPNGtcVXfl1MSDTfwli9VbaGM7o7eF6F5ju/KnYL8PO6tY7IQ5IIiHya99jVLdH1mOcRKthCL2 -nJKnWLKocsalu+Z6LornIOcmfddUCNQD9+fNJLkCwSiHYmdndtjpluUZsW9ddz6OvrHRL9m9YjnM -MR9NY7viDgD06eec6S5rDSpHlsMqNNx3KTVjrE8oq6jQ9ZnUuyqeF43m/DN0Lv1PYDhavHhmd+G1 -HZ92jJrnwHCuLf4NOX/U5AGDK22ytMQV0kqve9dCWKBWLAd4SHnzKs0BSjN+8RVS81QmoQR8MI7n -bP0q+AnaHU76HrhLbkLVtzJVagLUjWuew4yFCX/at9Pk55bst+CJzsbUc2L+aEyleqdynvcw94zm -MvCtdutUlW9MZphbP4bz+vjHPywDinpXSXmB1DvXRObIcpDOJboD2LgcUz1feCkv+GAsB/qXVmlb -T720vBQ55XS1e2Y7iuconb8OfK0CVvqlk/m5gp4uA5OHWgaNpDQpoPkl7WTQ3gyrIGl5RpNwuhl7 -1rJj8J9wZ90wTQM6c2d/+glw9LK9bA23JLefb9pmKO+W/NmSl4a8M+mQGya/vfMtc1xuoJ8A4dtZ -9/cJ8C7Cd6oL8AitD+UTQYFny7BViQhsokehzUv+QxPWHYLL4zCGn/zHusyuEsNkVyEGO4VKTK4u -ZuK67ojcwU3uL2/sR0nMQJuWFHxAE4F24pTGMamcLGcvbEpa5biDDykZ8TmCC9GHIMRlqsjfy0dY -kiJATBaxT6id+auvw2wx2Sn5qqkmPy7pe06S4XmcOd/JFslC8vY+AAxuCZW4ztCAwfHXNe4KfGX5 -RvfyBsT3UfaNS0RFp5PULL8/s5D3T2Y8rZ4SB2tzR0qIAR9KE3FeQvjCQpgtKZ6D0jnBF8NH3jSO -8U+07Hbwr0xe7Y0jx0Eab5QrUgdxA97jhPriv9h9bBUUyxFiHySsUqP15L1NMSBjvHb9g57LkeUg -ZaO3ijGsz873vEeBOXjc25a0eslHGzXPQVW5Hn1jo1/ykZrnsL15tI3tSuzANAVjDj+vzUDNc1hp -nnJunDhCsmcAa7gR39DxITQQa47D2lA2o+0ia00/20XXdnw2hmqWA/25Nvj7Yv6kvWP03EtKCyy4 -Thtb9a590JRO8RzQWsAYuD+EMcK0vz9zMulUEj4Iy3OmfhnlO2XJPiZf4px0yraOpVIToE5csRxm -K1r0836dZsm3RL+F8H02oJ4T83Y8vW7lyisyYbjU3+YUb33vJnZ+X8q7tnLznY82hm+nK8UipiAe -eOc/rKn5y5HnIGPB+WfoR9C1ail3rjo/ur8HYznQtbRK23rqZS0IiAkAC/za+NIKVywHqbwZ+FKT -RGmXzuVLsb8X25stApOH2gWLnzQVoEklK2FpRU6rRlaT0bSbbsSeqRRs7zM+uBebaRSnTviPb267 -uaJyLw20d6gY3ksD7V3IuITwiuG9Lx8lt0x+eXPb5jnegDeZ4/KxevLLv5vbBZ55zu/b53tCdkdg -EzQlEHcE0JhRkEBz5fSxl0MRuOGV3NdSwc5w7Qsf95IXqLmkYHTJMQrgPE1xh95Oo+Cjm0/DFHxg -U55ASDSRpxJRQSa+abxpJ074rAbcJHGDlYj3F7xLjsXMcyVK3Ym/XF2V6DjxF08nk02mIxNbpjVf -rlmLD/byHsHid9HAEpBOMsMysUW+byZ551gAHxTPxw92t7XwW0ckLjydpuL5/XmFIHNh/yaTSy6g -gKGDKKEkEZclxC+NRJuT4jkol8NAi6CLznE/965H75s92R+K5SCdN+oVsVPan4qfv5JE+SDOV3tQ -L4TiOULuo4hFbr9mWnDWfukl9FwUy0HqBmyh9MdCccHn/Qo+3+LEvJtTCrWW29mK56DiVo++sdGv -eUrFc9j+PNjGdimCWKQWqXTHz59XFA8sR4kOIEI5P4LvMTbft9DOoedFaDhWLAe1oHz0WYluxsmD -3JznZnw6kGqeoxRPDf4NOX/U4NMSy1tyWOLa/NPr3rUQmtopngNcJMYA1CfGqI+sn08+lYQPxvKc -tV8G9E5xI81Qmnrlqyu1VWoD1I0rlsOMhYh+3rPTbPmW6BdNhTmX47hdAb/Pt9xxjOczgVPa/dFE -gFnLGyq+bS330oA2kG+ni8Yippfvm9P8TOgVz0Gmbs0q+kmmtaNhn6vTlQN8MJYDnUurtK2nXtaM -iKVKwq/3d0sUy0Eqbwa+1C5R2qVz+VLs78XxZovA5KF2QUMoTQdoaklLWVqb0+KRlmYs/aZbsWcs -Bcj7TAzpxmcayVkM+dMb3X6pyNxxanC844H82ZC9BXnZAbuXL8ktk9/e6MYc3XQLx9tkmKg/otHd -Nrnvwmkd3rLEt16/WKns641QICY5eXdgwRsBJUWZTH4MyK0+ec9S/EwpCBh8uPRJAFOIiuf3xxyB -TMu4PrPE0h2w17hcn63RP0XE2UzI7cO60wKg2RJtKh+zFcdBUV4gh5BAYiAP97ex0TtiCn7YBECq -NaUD+Jb1YCx/LlymxV7wejEWe11LzwUGhNkI0VS8sDlD64FY3xnx8SPZ1CKWZuMT9d3OsLQlZOsq -vxYMDCGuMXSVwbWmjQB3EVcTxXTneioANLxBgY+00/qEh3ch5i+3abTdyNt5v8ZKtYE2ti26G4hu -NbYpfwjGRO0rsdiUoCxJQ0jmTCinRIhOqe4VxxEu4ihglTqmeAnibJzrrjo1D8VyjGdLAT0ZFs5B -FNCY82YCvMMMgphktSXx1jxPyX0f01uEmF+E2JgQl+xf8xzmn48msl1xDFLB28XIz4Pzdjechuew -st0XcM95yQ9Wbiw8dH0n9bKK5zDNH213u+IMpdK0s5XHbXyJOprnKeHvQ5IfZdiY6V5y8prnQOOJ -DnKmhXb1s9Vx8btmQsP2keWA9gOGsLiRn4aIBZ35fF5xFPBBOJ4R+g18W+RI8A2pFE8l+w7cq9ac -ukDq2zXPYSZDhL/g6WlOdE/4i0ZD9qkeuCviN+7T+7DkpxOFc1r+JyQK3Pbf0Ph987mXKLSRfjtd -LBTh5dKgN7iDsiw8exhk+4j8UJAcsKkl26nq7OgcH4zjMHdz1NnW0y4tQdPPo3Qm8VBhhUo+shyj -8WbcSzWyUi6byc8V/HQJiDjUKFhoZTkCzTlpm4P2bWh9Sas3mpnTXdgzlIK+fyqe9OI1zVJpPPnj -2+MuVmTutYH3niuO99rAexcyPjpEM+843oVJh9wy+fXt8TKZG/DeaXWm8IfAew8AOUHDCo8oyxWR -4AqAnM+YSnJFJOaKQYi41CI3TMoVcNzpWGK+21FvUYMkn/dwj6U+BrOWl0TkHssTEHiF2/OTS6M8 -cZd9RE8qEUWeSpzlK2RyF6YAZvkl+ZY5/Rx8Vmt3YpJY7tf5sFTaCoQc0FysAh3n/eLpZKry6Pia -koJdJXKSAbN/CgVkQIxfj7sCNtAlrcu9MlPe0zuye6OpebsCO8i/Uam49HSeLbsBLUIAcwmsFwrZ -eTU7QLM87LwgsYjmC+NgZqR4jsqaLaIwoEXTQMgZNjb4V9au98WR5SCVN9otYk94NhvZmnx27289 -tg6K5xi5WxFF7pAkyzS5fveFc9BTURwHaVu6rfiAWw7bX/Aptr45lxQ7u3pz8shzbGmrhdiYENec -peI5bJMeLGS7Ej5sQZ7Dr+sbYorjsKI88Z9Kj0xeUNyIg+h6EhaKFceBnlFbzMmgCSlTIVxetJnD -/Ia93O9IHWXYepvx9qyGmY0r7aW0yBW/Sq98z0RoWqdYDnCUGMPLc3LLDhZ2Ou9UAj4Yx2Eax3ts -UmOndKk+zn3eP+qcUzE8I/n9pJHKz+yV7kIaju5N4f5+PR9mT2j7foy9aPLMyVBptNDf5mHe+DTO -zOaepLeN/uYjH21Q305Xj0XOVRJgPFdXM3zFc5C52CkPXBHbTpfryhk+GMeBHrLV2dbTLu1JQE48 -jSn91aWoXPEcpPJm4GttE6VfOpkv5f5eXG+2DEweahk0oNLcgCaarLaltTqtJmmxxlJxuhV7xlJg -vU/54W6oo0GROeM/veMNBRRE7rWB9Q4H8mdDTv+nwe+evyS3TH57xxtzjOZOx9uueWP+5o735/+U -/sv/+D+H//0//Of/4/Nf/r9//bd//+8T4d/9r//yn/71f/u3f/m/Hv/6b//9f/ef/uu//D//+te/ -/Of//F/+27/8t3/9v9N/+us//du//tf/9l/+7V//+q//53/5f0HBj/Yf/Lt/9x/+l7TT/n/XwEN4 - - - diff --git a/branding/logo/logoguidelines.md b/branding/logo/logoguidelines.md new file mode 100644 index 000000000000..0c37e3dd455e --- /dev/null +++ b/branding/logo/logoguidelines.md @@ -0,0 +1,18 @@ +# NumPy Logo Guidelines +These guidelines are meant to help keep the NumPy logo consistent and recognizable across all its uses. They also provide a common language for referring to the logos and their components. + +The primary logo is the horizontal option (logomark and text next to each other) and the secondary logo is the stacked version (logomark over text). I’ve also provided the logomark on its own (meaning it doesn’t have text). When in doubt, it’s preferable to use primary or secondary options over the logomark alone. + +## Color +The full color options are a combo of two shades of blue, rgb(77, 171, 207) and rgb(77, 119, 207), while light options are rgb(255, 255, 255) and dark options are rgb(1, 50, 67). + +Whenever possible, use the full color logos. One color logos (light or dark) are to be used when full color will not have enough contrast, usually when logos must be on colored backgrounds. + +## Minimum Size +Please do not make the primary logo smaller than 50px wide, secondary logo smaller than 35px wide, or logomark smaller than 20px wide. + +## Logo Integrity +A few other notes to keep in mind when using the logo: +- Make sure to scale the logo proportionally. +- Maintain a good amount of space around the logo. Don’t let it overlap with text, images, or other elements. +- Do not try and recreate or modify the logo. For example, do not use the logomark and then try to write NumPy in another font. diff --git a/branding/logo/logomark/numpylogoicon.png b/branding/logo/logomark/numpylogoicon.png new file mode 100644 index 000000000000..4d663fe0a479 Binary files /dev/null and b/branding/logo/logomark/numpylogoicon.png differ diff --git a/branding/logo/logomark/numpylogoicon.svg b/branding/logo/logomark/numpylogoicon.svg new file mode 100644 index 000000000000..50810223b355 --- /dev/null +++ b/branding/logo/logomark/numpylogoicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/branding/logo/logomark/numpylogoicondark.png b/branding/logo/logomark/numpylogoicondark.png new file mode 100644 index 000000000000..eea3f32881a3 Binary files /dev/null and b/branding/logo/logomark/numpylogoicondark.png differ diff --git a/branding/logo/logomark/numpylogoiconlight.png b/branding/logo/logomark/numpylogoiconlight.png new file mode 100644 index 000000000000..a81b175a6649 Binary files /dev/null and b/branding/logo/logomark/numpylogoiconlight.png differ diff --git a/branding/logo/primary/numpylogo.png b/branding/logo/primary/numpylogo.png new file mode 100644 index 000000000000..8187b49c10ae Binary files /dev/null and b/branding/logo/primary/numpylogo.png differ diff --git a/branding/logo/primary/numpylogo.svg b/branding/logo/primary/numpylogo.svg new file mode 100644 index 000000000000..63d61c50f6a0 --- /dev/null +++ b/branding/logo/primary/numpylogo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/branding/logo/primary/numpylogodark.png b/branding/logo/primary/numpylogodark.png new file mode 100644 index 000000000000..e6d20af6be7d Binary files /dev/null and b/branding/logo/primary/numpylogodark.png differ diff --git a/branding/logo/primary/numpylogolight.png b/branding/logo/primary/numpylogolight.png new file mode 100644 index 000000000000..8500d1c10f27 Binary files /dev/null and b/branding/logo/primary/numpylogolight.png differ diff --git a/branding/logo/secondary/numpylogo2.png b/branding/logo/secondary/numpylogo2.png new file mode 100644 index 000000000000..000a197b3894 Binary files /dev/null and b/branding/logo/secondary/numpylogo2.png differ diff --git a/branding/logo/secondary/numpylogo2.svg b/branding/logo/secondary/numpylogo2.svg new file mode 100644 index 000000000000..20385487c94e --- /dev/null +++ b/branding/logo/secondary/numpylogo2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/branding/logo/secondary/numpylogo2dark.png b/branding/logo/secondary/numpylogo2dark.png new file mode 100644 index 000000000000..3c866703c416 Binary files /dev/null and b/branding/logo/secondary/numpylogo2dark.png differ diff --git a/branding/logo/secondary/numpylogo2light.png b/branding/logo/secondary/numpylogo2light.png new file mode 100644 index 000000000000..98f00bc42ec8 Binary files /dev/null and b/branding/logo/secondary/numpylogo2light.png differ diff --git a/doc/BRANCH_WALKTHROUGH.rst b/doc/BRANCH_WALKTHROUGH.rst new file mode 100644 index 000000000000..95de5464b376 --- /dev/null +++ b/doc/BRANCH_WALKTHROUGH.rst @@ -0,0 +1,77 @@ +This file contains a walkthrough of branching NumPy 1.21.x on Linux. The +commands can be copied into the command line, but be sure to replace 1.21 and +1.22 by the correct versions. It is good practice to make ``.mailmap`` as +current as possible before making the branch, that may take several weeks. + +This should be read together with the general directions in `releasing`. + +Branching +========= + +Make the branch +--------------- + +This is only needed when starting a new maintenance branch. Because +NumPy now depends on tags to determine the version, the start of a new +development cycle in the main branch needs an annotated tag. That is done +as follows:: + + $ git checkout main + $ git pull upstream main + $ git commit --allow-empty -m'REL: Begin NumPy 1.22.0 development' + $ git push upstream HEAD + +If the push fails because new PRs have been merged, do:: + + $ git pull --rebase upstream + +and repeat the push. Once the push succeeds, tag it:: + + $ git tag -a -s v1.22.0.dev0 -m'Begin NumPy 1.22.0 development' + $ git push upstream v1.22.0.dev0 + +then make the new branch and push it:: + + $ git branch maintenance/1.21.x HEAD^ + $ git push upstream maintenance/1.21.x + +Prepare the main branch for further development +----------------------------------------------- + +Make a PR branch to prepare main for further development:: + + $ git checkout -b 'prepare-main-for-1.22.0-development' v1.22.0.dev0 + +Delete the release note fragments:: + + $ git rm doc/release/upcoming_changes/[0-9]*.*.rst + +Create the new release notes skeleton and add to index:: + + $ cp doc/source/release/template.rst doc/source/release/1.22.0-notes.rst + $ gvim doc/source/release/1.22.0-notes.rst # put the correct version + $ git add doc/source/release/1.22.0-notes.rst + $ gvim doc/source/release.rst # add new notes to notes index + $ git add doc/source/release.rst + +Update ``pavement.py`` and update the ``RELEASE_NOTES`` variable to point to +the new notes:: + + $ gvim pavement.py + $ git add pavement.py + +Update ``cversions.txt`` to add current release. There should be no new hash +to worry about at this early point, just add a comment following previous +practice:: + + $ gvim numpy/core/code_generators/cversions.txt + $ git add numpy/core/code_generators/cversions.txt + +Check your work, commit it, and push:: + + $ git status # check work + $ git commit -m'REL: Prepare main for NumPy 1.22.0 development' + $ git push origin HEAD + +Now make a pull request. + diff --git a/doc/CAPI.rst.txt b/doc/CAPI.rst.txt deleted file mode 100644 index f38815e2a657..000000000000 --- a/doc/CAPI.rst.txt +++ /dev/null @@ -1,320 +0,0 @@ -=============== -C-API for NumPy -=============== - -:Author: Travis Oliphant -:Discussions to: `numpy-discussion@python.org`__ -:Created: October 2005 - -__ http://scipy.org/scipylib/mailing-lists.html - -The C API of NumPy is (mostly) backward compatible with Numeric. - -There are a few non-standard Numeric usages (that were not really part -of the API) that will need to be changed: - -* If you used any of the function pointers in the ``PyArray_Descr`` - structure you will have to modify your usage of those. First, - the pointers are all under the member named ``f``. So ``descr->cast`` - is now ``descr->f->cast``. In addition, the - casting functions have eliminated the strides argument (use - ``PyArray_CastTo`` if you need strided casting). All functions have - one or two ``PyArrayObject *`` arguments at the end. This allows the - flexible arrays and mis-behaved arrays to be handled. - -* The ``descr->zero`` and ``descr->one`` constants have been replaced with - function calls, ``PyArray_Zero``, and ``PyArray_One`` (be sure to read the - code and free the resulting memory if you use these calls). - -* If you passed ``array->dimensions`` and ``array->strides`` around - to functions, you will need to fix some code. These are now - ``npy_intp*`` pointers. On 32-bit systems there won't be a problem. - However, on 64-bit systems, you will need to make changes to avoid - errors and segfaults. - - -The header files ``arrayobject.h`` and ``ufuncobject.h`` contain many defines -that you may find useful. The files ``__ufunc_api.h`` and -``__multiarray_api.h`` contain the available C-API function calls with -their function signatures. - -All of these headers are installed to -``/site-packages/numpy/core/include`` - - -Getting arrays in C-code -========================= - -All new arrays can be created using ``PyArray_NewFromDescr``. A simple interface -equivalent to ``PyArray_FromDims`` is ``PyArray_SimpleNew(nd, dims, typenum)`` -and to ``PyArray_FromDimsAndData`` is -``PyArray_SimpleNewFromData(nd, dims, typenum, data)``. - -This is a very flexible function. - -:: - - PyObject * PyArray_NewFromDescr(PyTypeObject *subtype, PyArray_Descr *descr, - int nd, npy_intp *dims, - npy_intp *strides, char *data, - int flags, PyObject *obj); - -``subtype`` : ``PyTypeObject *`` - The subtype that should be created (either pass in - ``&PyArray_Type``, or ``obj->ob_type``, - where ``obj`` is an instance of a subtype (or subclass) of - ``PyArray_Type``). - -``descr`` : ``PyArray_Descr *`` - The type descriptor for the array. This is a Python object (this - function steals a reference to it). The easiest way to get one is - using ``PyArray_DescrFromType()``. If you want to use a - flexible size array, then you need to use - ``PyArray_DescrNewFromType()`` and set its ``elsize`` - parameter to the desired size. The typenum in both of these cases - is one of the ``PyArray_XXXX`` enumerated types. - -``nd`` : ``int`` - The number of dimensions (<``MAX_DIMS``) - -``*dims`` : ``npy_intp *`` - A pointer to the size in each dimension. Information will be - copied from here. - -``*strides`` : ``npy_intp *`` - The strides this array should have. For new arrays created by this - routine, this should be ``NULL``. If you pass in memory for this array - to use, then you can pass in the strides information as well - (otherwise it will be created for you and default to C-contiguous - or Fortran contiguous). Any strides will be copied into the array - structure. Do not pass in bad strides information!!!! - - ``PyArray_CheckStrides(...)`` can help but you must call it if you are - unsure. You cannot pass in strides information when data is ``NULL`` - and this routine is creating its own memory. - -``*data`` : ``char *`` - ``NULL`` for creating brand-new memory. If you want this array to wrap - another memory area, then pass the pointer here. You are - responsible for deleting the memory in that case, but do not do so - until the new array object has been deleted. The best way to - handle that is to get the memory from another Python object, - ``INCREF`` that Python object after passing it's data pointer to this - routine, and set the ``->base`` member of the returned array to the - Python object. *You are responsible for* setting ``PyArray_BASE(ret)`` - to the base object. Failure to do so will create a memory leak. - - If you pass in a data buffer, the ``flags`` argument will be the flags - of the new array. If you create a new array, a non-zero flags - argument indicates that you want the array to be in Fortran order. - -``flags`` : ``int`` - Either the flags showing how to interpret the data buffer passed - in, or if a new array is created, nonzero to indicate a Fortran - order array. See below for an explanation of the flags. - -``obj`` : ``PyObject *`` - If subtypes is ``&PyArray_Type``, this argument is - ignored. Otherwise, the ``__array_finalize__`` method of the subtype - is called (if present) and passed this object. This is usually an - array of the type to be created (so the ``__array_finalize__`` method - must handle an array argument. But, it can be anything...) - -Note: The returned array object will be uninitialized unless the type is -``PyArray_OBJECT`` in which case the memory will be set to ``NULL``. - -``PyArray_SimpleNew(nd, dims, typenum)`` is a drop-in replacement for -``PyArray_FromDims`` (except it takes ``npy_intp*`` dims instead of ``int*`` dims -which matters on 64-bit systems) and it does not initialize the memory -to zero. - -``PyArray_SimpleNew`` is just a macro for ``PyArray_New`` with default arguments. -Use ``PyArray_FILLWBYTE(arr, 0)`` to fill with zeros. - -The ``PyArray_FromDims`` and family of functions are still available and -are loose wrappers around this function. These functions still take -``int *`` arguments. This should be fine on 32-bit systems, but on 64-bit -systems you may run into trouble if you frequently passed -``PyArray_FromDims`` the dimensions member of the old ``PyArrayObject`` structure -because ``sizeof(npy_intp) != sizeof(int)``. - - -Getting an arrayobject from an arbitrary Python object -====================================================== - -``PyArray_FromAny(...)`` - -This function replaces ``PyArray_ContiguousFromObject`` and friends (those -function calls still remain but they are loose wrappers around the -``PyArray_FromAny`` call). - -:: - - static PyObject * - PyArray_FromAny(PyObject *op, PyArray_Descr *dtype, int min_depth, - int max_depth, int requires, PyObject *context) - - -``op`` : ``PyObject *`` - The Python object to "convert" to an array object - -``dtype`` : ``PyArray_Descr *`` - The desired data-type descriptor. This can be ``NULL``, if the - descriptor should be determined by the object. Unless ``FORCECAST`` is - present in ``flags``, this call will generate an error if the data - type cannot be safely obtained from the object. - -``min_depth`` : ``int`` - The minimum depth of array needed or 0 if doesn't matter - -``max_depth`` : ``int`` - The maximum depth of array allowed or 0 if doesn't matter - -``requires`` : ``int`` - A flag indicating the "requirements" of the returned array. These - are the usual ndarray flags (see `NDArray flags`_ below). In - addition, there are three flags used only for the ``FromAny`` - family of functions: - - - ``ENSURECOPY``: always copy the array. Returned arrays always - have ``CONTIGUOUS``, ``ALIGNED``, and ``WRITEABLE`` set. - - ``ENSUREARRAY``: ensure the returned array is an ndarray. - - ``FORCECAST``: cause a cast to occur regardless of whether or - not it is safe. - -``context`` : ``PyObject *`` - If the Python object ``op`` is not a numpy array, but has an - ``__array__`` method, context is passed as the second argument to - that method (the first is the typecode). Almost always this - parameter is ``NULL``. - - -``PyArray_ContiguousFromAny(op, typenum, min_depth, max_depth)`` is -equivalent to ``PyArray_ContiguousFromObject(...)`` (which is still -available), except it will return the subclass if op is already a -subclass of the ndarray. The ``ContiguousFromObject`` version will -always return an ndarray. - -Passing Data Type information to C-code -======================================= - -All datatypes are handled using the ``PyArray_Descr *`` structure. -This structure can be obtained from a Python object using -``PyArray_DescrConverter`` and ``PyArray_DescrConverter2``. The former -returns the default ``PyArray_LONG`` descriptor when the input object -is None, while the latter returns ``NULL`` when the input object is ``None``. - -See the ``arraymethods.c`` and ``multiarraymodule.c`` files for many -examples of usage. - -Getting at the structure of the array. --------------------------------------- - -You should use the ``#defines`` provided to access array structure portions: - -- ``PyArray_DATA(obj)`` : returns a ``void *`` to the array data -- ``PyArray_BYTES(obj)`` : return a ``char *`` to the array data -- ``PyArray_ITEMSIZE(obj)`` -- ``PyArray_NDIM(obj)`` -- ``PyArray_DIMS(obj)`` -- ``PyArray_DIM(obj, n)`` -- ``PyArray_STRIDES(obj)`` -- ``PyArray_STRIDE(obj,n)`` -- ``PyArray_DESCR(obj)`` -- ``PyArray_BASE(obj)`` - -see more in ``arrayobject.h`` - - -NDArray Flags -============= - -The ``flags`` attribute of the ``PyArrayObject`` structure contains important -information about the memory used by the array (pointed to by the data member) -This flags information must be kept accurate or strange results and even -segfaults may result. - -There are 6 (binary) flags that describe the memory area used by the -data buffer. These constants are defined in ``arrayobject.h`` and -determine the bit-position of the flag. Python exposes a nice attribute- -based interface as well as a dictionary-like interface for getting -(and, if appropriate, setting) these flags. - -Memory areas of all kinds can be pointed to by an ndarray, necessitating -these flags. If you get an arbitrary ``PyArrayObject`` in C-code, -you need to be aware of the flags that are set. -If you need to guarantee a certain kind of array -(like ``NPY_CONTIGUOUS`` and ``NPY_BEHAVED``), then pass these requirements into the -PyArray_FromAny function. - - -``NPY_CONTIGUOUS`` - True if the array is (C-style) contiguous in memory. -``NPY_FORTRAN`` - True if the array is (Fortran-style) contiguous in memory. - -Notice that contiguous 1-d arrays are always both ``NPY_FORTRAN`` contiguous -and C contiguous. Both of these flags can be checked and are convenience -flags only as whether or not an array is ``NPY_CONTIGUOUS`` or ``NPY_FORTRAN`` -can be determined by the ``strides``, ``dimensions``, and ``itemsize`` -attributes. - -``NPY_OWNDATA`` - True if the array owns the memory (it will try and free it using - ``PyDataMem_FREE()`` on deallocation --- so it better really own it). - -These three flags facilitate using a data pointer that is a memory-mapped -array, or part of some larger record array. But, they may have other uses... - -``NPY_ALIGNED`` - True if the data buffer is aligned for the type and the strides - are multiples of the alignment factor as well. This can be - checked. - -``NPY_WRITEABLE`` - True only if the data buffer can be "written" to. - -``NPY_WRITEBACKIFCOPY`` - This is a special flag that is set if this array represents a copy - made because a user required certain flags in ``PyArray_FromAny`` and - a copy had to be made of some other array (and the user asked for - this flag to be set in such a situation). The base attribute then - points to the "misbehaved" array (which is set read_only). If you use - this flag, you are must call ``PyArray_ResolveWritebackIfCopy`` before - deallocating this array (i.e. before calling ``Py_DECREF`` the last time) - which will write the data contents back to the "misbehaved" array (casting - if necessary) and will reset the "misbehaved" array to ``WRITEABLE``. If - the "misbehaved" array was not ``WRITEABLE`` to begin with then - ``PyArray_FromAny`` would have returned an error because ``WRITEBACKIFCOPY`` - would not have been possible. In error conditions, call - ``PyArray_DiscardWritebackIfCopy`` to throw away the scratch buffer, then - ``Py_DECREF`` or ``Py_XDECREF``. - -``NPY_UPDATEIFCOPY`` - Similar to ``NPY_WRITEBACKIFCOPY``, but deprecated since it copied the - contents back when the array is deallocated, which is not explicit and - relies on refcount semantics. Refcount semantics are unreliable on - alternative implementations of python such as PyPy. - -``PyArray_UpdateFlags(obj, flags)`` will update the ``obj->flags`` for -``flags`` which can be any of ``NPY_CONTIGUOUS``, ``NPY_FORTRAN``, ``NPY_ALIGNED``, or -``NPY_WRITEABLE``. - -Some useful combinations of these flags: - -- ``NPY_BEHAVED = NPY_ALIGNED | NPY_WRITEABLE`` -- ``NPY_CARRAY = NPY_DEFAULT = NPY_CONTIGUOUS | NPY_BEHAVED`` -- ``NPY_CARRAY_RO = NPY_CONTIGUOUS | NPY_ALIGNED`` -- ``NPY_FARRAY = NPY_FORTRAN | NPY_BEHAVED`` -- ``NPY_FARRAY_RO = NPY_FORTRAN | NPY_ALIGNED`` - -The macro ``PyArray_CHECKFLAGS(obj, flags)`` can test any combination of flags. -There are several default combinations defined as macros already -(see ``arrayobject.h``) - -In particular, there are ``ISBEHAVED``, ``ISBEHAVED_RO``, ``ISCARRAY`` -and ``ISFARRAY`` macros that also check to make sure the array is in -native byte order (as determined) by the data-type descriptor. - -There are more C-API enhancements which you can discover in the code, -or buy the book (http://www.trelgol.com) diff --git a/doc/C_STYLE_GUIDE.rst.txt b/doc/C_STYLE_GUIDE.rst.txt index a5726f16fa36..4e2f27fbb1b1 100644 --- a/doc/C_STYLE_GUIDE.rst.txt +++ b/doc/C_STYLE_GUIDE.rst.txt @@ -1,220 +1,3 @@ -=================== -NumPy C Style Guide -=================== -The NumPy C coding conventions are based on Python PEP-0007 by Guido van -Rossum with a few added strictures. There are many C coding conventions and -it must be emphasized that the primary goal of the NumPy conventions isn't -to choose the 'best', about which there is certain to be disagreement, but -to achieve uniformity. Because the NumPy conventions are very close to -those in PEP-0007, that PEP is used as a template below with the NumPy -additions and variations in the appropriate spots. - -NumPy modified PEP-0007 -======================= - -Introduction ------------- - -This document gives coding conventions for the C code comprising -the C implementation of NumPy. Note, rules are there to be broken. -Two good reasons to break a particular rule: - -1. When applying the rule would make the code less readable, even - for someone who is used to reading code that follows the rules. - -2. To be consistent with surrounding code that also breaks it - (maybe for historic reasons) -- although this is also an - opportunity to clean up someone else's mess. - - -C dialect ---------- - -* Use ANSI/ISO standard C (the 1989 version of the standard). - This means, amongst many other things, that all declarations - must be at the top of a block (not necessarily at the top of - function). - -* Don't use GCC extensions (e.g. don't write multi-line strings - without trailing backslashes). Preferably break long strings - up onto separate lines like so:: - - "blah blah" - "blah blah" - - This will work with MSVC, which otherwise chokes on very long - strings. - -* All function declarations and definitions must use full - prototypes (i.e. specify the types of all arguments). - -* Do not use C++ style // one line comments, they aren't portable. - Note: this will change with the proposed transition to C++. - -* No compiler warnings with major compilers (gcc, VC++, a few others). - Note: NumPy still produces compiler warnings that need to be addressed. - - -Code lay-out ------------- - -* Use 4-space indents and no tabs at all. - -* No line should be longer than 80 characters. If this and the - previous rule together don't give you enough room to code, your code is - too complicated, consider using subroutines. - -* No line should end in whitespace. If you think you need - significant trailing whitespace, think again, somebody's editor might - delete it as a matter of routine. - -* Function definition style: function name in column 1, outermost - curly braces in column 1, blank line after local variable declarations:: - - static int - extra_ivars(PyTypeObject *type, PyTypeObject *base) - { - int t_size = PyType_BASICSIZE(type); - int b_size = PyType_BASICSIZE(base); - - assert(t_size >= b_size); /* type smaller than base! */ - ... - return 1; - } - - If the transition to C++ goes through it is possible that this form will - be relaxed so that short class methods meant to be inlined can have the - return type on the same line as the function name. However, that is yet to - be determined. - -* Code structure: one space between keywords like ``if``, ``for`` and - the following left parenthesis; no spaces inside the parenthesis; braces - around all ``if`` branches and no statements on the same line as the - ``if``. They should be formatted as shown:: - - if (mro != NULL) { - one_line_statement; - } - else { - ... - } - - - for (i = 0; i < n; i++) { - one_line_statement; - } - - - while (isstuff) { - dostuff; - } - - - do { - stuff; - } while (isstuff); - - - switch (kind) { - /* Boolean kind */ - case 'b': - return 0; - /* Unsigned int kind */ - case 'u': - ... - /* Anything else */ - default: - return 3; - } - - -* The return statement should *not* get redundant parentheses:: - - return Py_None; /* correct */ - return(Py_None); /* incorrect */ - -* Function and macro call style: ``foo(a, b, c)``, no space before - the open paren, no spaces inside the parens, no spaces before - commas, one space after each comma. - -* Always put spaces around assignment, Boolean and comparison - operators. In expressions using a lot of operators, add spaces - around the outermost (lowest priority) operators. - -* Breaking long lines: if you can, break after commas in the - outermost argument list. Always indent continuation lines - appropriately, e.g., :: - - PyErr_SetString(PyExc_TypeError, - "Oh dear, you messed up."); - - Here appropriately means at least two tabs. It isn't necessary to - line everything up with the opening parenthesis of the function - call. - -* When you break a long expression at a binary operator, the - operator goes at the end of the previous line, e.g., :: - - if (type > tp_dictoffset != 0 && - base > tp_dictoffset == 0 && - type > tp_dictoffset == b_size && - (size_t)t_size == b_size + sizeof(PyObject *)) { - return 0; - } - - Note that the terms in the multi-line Boolean expression are indented so - as to make the beginning of the code block clearly visible. - -* Put blank lines around functions, structure definitions, and - major sections inside functions. - -* Comments go before the code they describe. Multi-line comments should - be like so:: - - /* - * This would be a long - * explanatory comment. - */ - - Trailing comments should be used sparingly. Instead of :: - - if (yes) {/* Success! */ - - do :: - - if (yes) { - /* Success! */ - -* All functions and global variables should be declared static - when they aren't needed outside the current compilation unit. - -* Declare external functions and variables in a header file. - - -Naming conventions ------------------- - -* There has been no consistent prefix for NumPy public functions, but - they all begin with a prefix of some sort, followed by an underscore, and - are in camel case: ``PyArray_DescrAlignConverter``, ``NpyIter_GetIterNext``. - In the future the names should be of the form ``Npy*_PublicFunction``, - where the star is something appropriate. - -* Public Macros should have a NPY_ prefix and then use upper case, - for example, ``NPY_DOUBLE``. - -* Private functions should be lower case with underscores, for example: - ``array_real_get``. Single leading underscores should not be used, but - some current function names violate that rule due to historical accident. - Those functions should be renamed at some point. - - -Function documentation ----------------------- - -NumPy doesn't have a C function documentation standard at this time, but -needs one. Most numpy functions are not documented in the code and that -should change. One possibility is Doxygen with a plugin so that the same -NumPy style used for Python functions can also be used for documenting -C functions, see the files in doc/cdoc/. +The "NumPy C Style Guide" at this page has been supserseded by +"NEP 45 — C Style Guide" at https://numpy.org/neps/nep-0045-c_style_guide.html diff --git a/doc/DISTUTILS.rst.txt b/doc/DISTUTILS.rst.txt index c027afff2378..539a3b9c121a 100644 --- a/doc/DISTUTILS.rst.txt +++ b/doc/DISTUTILS.rst.txt @@ -59,7 +59,7 @@ SciPy pure Python package example Below is an example of a minimal ``setup.py`` file for a pure SciPy package:: - #!/usr/bin/env python + #!/usr/bin/env python3 def configuration(parent_package='',top_path=None): from numpy.distutils.misc_util import Configuration config = Configuration('mypackage',parent_package,top_path) @@ -243,7 +243,7 @@ in writing setup scripts: after processing all source generators, no extension module will be built. This is the recommended way to conditionally define extension modules. Source generator functions are called by the - ``build_src`` command of ``numpy.distutils``. + ``build_src`` sub-command of ``numpy.distutils``. For example, here is a typical source generator function:: @@ -297,11 +297,182 @@ in writing setup scripts: + ``config.get_info(*names)`` --- -Template files --------------- -XXX: Describe how files with extensions ``.f.src``, ``.pyf.src``, -``.c.src``, etc. are pre-processed by the ``build_src`` command. +.. _templating: + +Conversion of ``.src`` files using Templates +-------------------------------------------- + +NumPy distutils supports automatic conversion of source files named +.src. This facility can be used to maintain very similar +code blocks requiring only simple changes between blocks. During the +build phase of setup, if a template file named .src is +encountered, a new file named is constructed from the +template and placed in the build directory to be used instead. Two +forms of template conversion are supported. The first form occurs for +files named .ext.src where ext is a recognized Fortran +extension (f, f90, f95, f77, for, ftn, pyf). The second form is used +for all other cases. + +.. index:: + single: code generation + +Fortran files +------------- + +This template converter will replicate all **function** and +**subroutine** blocks in the file with names that contain '<...>' +according to the rules in '<...>'. The number of comma-separated words +in '<...>' determines the number of times the block is repeated. What +these words are indicates what that repeat rule, '<...>', should be +replaced with in each block. All of the repeat rules in a block must +contain the same number of comma-separated words indicating the number +of times that block should be repeated. If the word in the repeat rule +needs a comma, leftarrow, or rightarrow, then prepend it with a +backslash ' \'. If a word in the repeat rule matches ' \\' then +it will be replaced with the -th word in the same repeat +specification. There are two forms for the repeat rule: named and +short. + +Named repeat rule +^^^^^^^^^^^^^^^^^ + +A named repeat rule is useful when the same set of repeats must be +used several times in a block. It is specified using , where N is the number of times the block +should be repeated. On each repeat of the block, the entire +expression, '<...>' will be replaced first with item1, and then with +item2, and so forth until N repeats are accomplished. Once a named +repeat specification has been introduced, the same repeat rule may be +used **in the current block** by referring only to the name +(i.e. ). + + +Short repeat rule +^^^^^^^^^^^^^^^^^ + +A short repeat rule looks like . The +rule specifies that the entire expression, '<...>' should be replaced +first with item1, and then with item2, and so forth until N repeats +are accomplished. + + +Pre-defined names +^^^^^^^^^^^^^^^^^ + +The following predefined named repeat rules are available: + +- + +- <_c=s,d,c,z> + +- <_t=real, double precision, complex, double complex> + +- + +- + +- + +- + + +Other files +------------ + +Non-Fortran files use a separate syntax for defining template blocks +that should be repeated using a variable expansion similar to the +named repeat rules of the Fortran-specific repeats. + +NumPy Distutils preprocesses C source files (extension: :file:`.c.src`) written +in a custom templating language to generate C code. The ``@`` symbol is +used to wrap macro-style variables to empower a string substitution mechanism +that might describe (for instance) a set of data types. + +The template language blocks are delimited by ``/**begin repeat`` +and ``/**end repeat**/`` lines, which may also be nested using +consecutively numbered delimiting lines such as ``/**begin repeat1`` +and ``/**end repeat1**/``: + +1. ``/**begin repeat`` on a line by itself marks the beginning of + a segment that should be repeated. + +2. Named variable expansions are defined using ``#name=item1, item2, item3, + ..., itemN#`` and placed on successive lines. These variables are + replaced in each repeat block with corresponding word. All named + variables in the same repeat block must define the same number of + words. + +3. In specifying the repeat rule for a named variable, ``item*N`` is short- + hand for ``item, item, ..., item`` repeated N times. In addition, + parenthesis in combination with ``*N`` can be used for grouping several + items that should be repeated. Thus, ``#name=(item1, item2)*4#`` is + equivalent to ``#name=item1, item2, item1, item2, item1, item2, item1, + item2#``. + +4. ``*/`` on a line by itself marks the end of the variable expansion + naming. The next line is the first line that will be repeated using + the named rules. + +5. Inside the block to be repeated, the variables that should be expanded + are specified as ``@name@``. + +6. ``/**end repeat**/`` on a line by itself marks the previous line + as the last line of the block to be repeated. + +7. A loop in the NumPy C source code may have a ``@TYPE@`` variable, targeted + for string substitution, which is preprocessed to a number of otherwise + identical loops with several strings such as ``INT``, ``LONG``, ``UINT``, + ``ULONG``. The ``@TYPE@`` style syntax thus reduces code duplication and + maintenance burden by mimicking languages that have generic type support. + +The above rules may be clearer in the following template source example: + +.. code-block:: NumPyC + :linenos: + :emphasize-lines: 3, 13, 29, 31 + + /* TIMEDELTA to non-float types */ + + /**begin repeat + * + * #TOTYPE = BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG, + * LONGLONG, ULONGLONG, DATETIME, + * TIMEDELTA# + * #totype = npy_byte, npy_ubyte, npy_short, npy_ushort, npy_int, npy_uint, + * npy_long, npy_ulong, npy_longlong, npy_ulonglong, + * npy_datetime, npy_timedelta# + */ + + /**begin repeat1 + * + * #FROMTYPE = TIMEDELTA# + * #fromtype = npy_timedelta# + */ + static void + @FROMTYPE@_to_@TOTYPE@(void *input, void *output, npy_intp n, + void *NPY_UNUSED(aip), void *NPY_UNUSED(aop)) + { + const @fromtype@ *ip = input; + @totype@ *op = output; + + while (n--) { + *op++ = (@totype@)*ip++; + } + } + /**end repeat1**/ + + /**end repeat**/ + +The preprocessing of generically-typed C source files (whether in NumPy +proper or in any third party package using NumPy Distutils) is performed +by `conv_template.py`_. +The type-specific C files generated (extension: ``.c``) +by these modules during the build process are ready to be compiled. This +form of generic typing is also supported for C header files (preprocessed +to produce ``.h`` files). + +.. _conv_template.py: https://github.com/numpy/numpy/blob/main/numpy/distutils/conv_template.py Useful functions in ``numpy.distutils.misc_util`` ------------------------------------------------- @@ -406,9 +577,6 @@ The header of a typical SciPy ``__init__.py`` is:: Package docstring, typically with a brief description and function listing. """ - # py3k related imports - from __future__ import division, print_function, absolute_import - # import functions into module namespace from .subpackage import * ... @@ -419,15 +587,11 @@ The header of a typical SciPy ``__init__.py`` is:: test = Tester().test bench = Tester().bench -Note that NumPy submodules still use a file named ``info.py`` in which the -module docstring and ``__all__`` dict are defined. These files will be removed -at some point. - Extra features in NumPy Distutils ''''''''''''''''''''''''''''''''' Specifying config_fc options for libraries in setup.py script ------------------------------------------------------------- +------------------------------------------------------------- It is possible to specify config_fc options in setup.py scripts. For example, using diff --git a/doc/HOWTO_DOCUMENT.rst.txt b/doc/HOWTO_DOCUMENT.rst.txt index 6b3640d8d5de..8f0d2fbae068 100644 --- a/doc/HOWTO_DOCUMENT.rst.txt +++ b/doc/HOWTO_DOCUMENT.rst.txt @@ -1 +1 @@ -This document has been replaced, see https://numpydoc.readthedocs.io/en/latest/ +This document has been replaced, see https://numpydoc.readthedocs.io/en/latest/format.html#docstring-standard diff --git a/doc/HOWTO_RELEASE.rst.txt b/doc/HOWTO_RELEASE.rst.txt index 3ed15e99cc51..37e047f9fbf0 100644 --- a/doc/HOWTO_RELEASE.rst.txt +++ b/doc/HOWTO_RELEASE.rst.txt @@ -3,143 +3,147 @@ NumPy. Current build and release info ============================== - The current info on building and releasing NumPy and SciPy is scattered in -several places. It should be summarized in one place, updated and where +several places. It should be summarized in one place, updated, and where necessary described in more detail. The sections below list all places where useful info can be found. + Source tree ----------- -* INSTALL.txt -* release.sh -* pavement.py +- INSTALL.rst.txt +- pavement.py + NumPy Docs ---------- -* https://github.com/numpy/numpy/blob/master/doc/HOWTO_RELEASE.rst.txt -* http://projects.scipy.org/numpy/wiki/MicrosoftToolchainSupport +- https://github.com/numpy/numpy/blob/main/doc/HOWTO_RELEASE.rst.txt + SciPy.org wiki -------------- -* http://www.scipy.org/Installing_SciPy and links on that page. -* http://new.scipy.org/building/windows.html +- https://www.scipy.org/Installing_SciPy and links on that page. -Doc wiki --------- -* http://docs.scipy.org/numpy/docs/numpy-docs/user/install.rst/ Release Scripts --------------- -* https://github.com/numpy/numpy-vendor +- https://github.com/numpy/numpy-vendor + Supported platforms and versions ================================ - -Python 2.7 and >=3.4 are the currently supported versions when building from -source. We test numpy against all these versions every time we merge code to -trunk. Binary installers may be available for a subset of these versions (see -below). +:ref:`NEP 29 ` outlines which Python versions +are supported; For the first half of 2020, this will be Python >= 3.6. We test +NumPy against all these versions every time we merge code to main. Binary +installers may be available for a subset of these versions (see below). OS X ---- - -Python 2.7 and >=3.4 are the versions for which we provide binary installers. -OS X versions >= 10.6 are supported. We build binary wheels for OSX that are -compatible with Python.org Python, system Python, homebrew and macports - see -this `OSX wheel building summary +OS X versions >= 10.9 are supported, for Python version support see +:ref:`NEP 29 `. We build binary wheels for +OSX that are compatible with Python.org Python, system Python, homebrew and +macports - see this `OSX wheel building summary `_ for details. + Windows ------- +We build 32- and 64-bit wheels on Windows. Windows 7, 8 and 10 are supported. +We build NumPy using the `mingw-w64 toolchain`_ on Appveyor. -We build 32- and 64-bit wheels for Python 2.7, 3.4, 3.5 on Windows. Windows -XP, Vista, 7, 8 and 10 are supported. We build numpy using the MSVC compilers -on Appveyor, but we are hoping to update to a `mingw-w64 toolchain -`_. The Windows wheels use ATLAS for BLAS / LAPACK. Linux ----- - We build and ship `manylinux1 `_ -wheels for numpy. Many Linux distributions include their own binary builds +wheels for NumPy. Many Linux distributions include their own binary builds of NumPy. + BSD / Solaris ------------- - No binaries are provided, but successful builds on Solaris and BSD have been reported. + Tool chain ========== - We build all our wheels on cloud infrastructure - so this list of compilers is -for information and debugging builds locally. See the ``.travis.yml`` and -``appveyor.yml`` scripts in the `numpy wheels`_ repo for the definitive source +for information and debugging builds locally. See the ``.travis.yml`` script +in the `numpy wheels`_ repo for the definitive source of the build recipes. Packages that are available using pip are noted. + Compilers --------- - The same gcc version is used as the one with which Python itself is built on each platform. At the moment this means: -* OS X builds on travis currently use `clang`. It appears that binary wheels +- OS X builds on travis currently use `clang`. It appears that binary wheels for OSX >= 10.6 can be safely built from the travis-ci OSX 10.9 VMs when building against the Python from the Python.org installers; -* Windows builds use the MSVC version corresponding to the Python being built - against; -* Manylinux1 wheels use the gcc provided on the Manylinux docker images. +- Windows builds use the `mingw-w64 toolchain`_; +- Manylinux1 wheels use the gcc provided on the Manylinux docker images. You will need Cython for building the binaries. Cython compiles the ``.pyx`` -files in the numpy distribution to ``.c`` files. +files in the NumPy distribution to ``.c`` files. + +.. _mingw-w64 toolchain : https://mingwpy.github.io + +OpenBLAS +------------ +All the wheels link to a version of OpenBLAS_ supplied via the openblas-libs_ repo. +The shared object (or DLL) is shipped with in the wheel, renamed to prevent name +collisions with other OpenBLAS shared objects that may exist in the filesystem. + +.. _OpenBLAS: https://github.com/xianyi/OpenBLAS +.. _openblas-libs: https://github.com/MacPython/openblas-libs + Building source archives and wheels ----------------------------------- - You will need write permission for numpy-wheels in order to trigger wheel builds. -* Python(s) from `python.org `_ or linux distro. -* cython -* virtualenv (pip) -* Paver (pip) -* numpy-wheels ``_ (clone) +- Python(s) from `python.org `_ or linux distro. +- cython (pip) +- virtualenv (pip) +- Paver (pip) +- pandoc `pandoc.org `_ or linux distro. +- numpy-wheels ``_ (clone) + Building docs ------------- - Building the documents requires a number of latex ``.sty`` files. Install them all to avoid aggravation. -* Sphinx (pip) -* numpydoc (pip) -* Matplotlib -* Texlive (or MikTeX on Windows) +- Sphinx (pip) +- numpydoc (pip) +- Matplotlib +- Texlive (or MikTeX on Windows) + Uploading to PyPI ----------------- +- terryfy ``_ (clone). +- beautifulsoup4 (pip) +- delocate (pip) +- auditwheel (pip) +- twine (pip) -* terryfy ``_ (clone). -* beautifulsoup4 (pip) -* delocate (pip) -* auditwheel (pip) -* twine (pip) Generating author/pr lists -------------------------- +You will need a personal access token +``_ +so that scripts can access the github NumPy repository. -You will need an personal access token -``_ -so that scripts can access the github numpy repository +- gitpython (pip) +- pygithub (pip) -* gitpython (pip) -* pygithub (pip) Virtualenv ---------- - Virtualenv is a very useful tool to keep several versions of packages around. It is also used in the Paver script to build the docs. @@ -149,27 +153,28 @@ What is released Wheels ------ +We currently support Python 3.6-3.8 on Windows, OSX, and Linux -* Windows wheels for Python 2.7, 3.4, 3.5, for 32- and 64-bit, built using - Appveyor; -* Dual architecture OSX wheels built via travis-ci; -* 32- and 64-bit Manylinux1 wheels built via travis-ci. +* Windows: 32-bit and 64-bit wheels built using Appveyor; +* OSX: x64_86 OSX wheels built using travis-ci; +* Linux: 32-bit and 64-bit Manylinux1 wheels built using travis-ci. See the `numpy wheels`_ building repository for more detail. .. _numpy wheels : https://github.com/MacPython/numpy-wheels + Other ----- +- Release Notes +- Changelog -* Release Notes -* Changelog Source distribution ------------------- - We build source releases in both .zip and .tar.gz formats. + Release process =============== @@ -179,34 +184,24 @@ A typical release schedule is one beta, two release candidates and a final release. It's best to discuss the timing on the mailing list first, in order for people to get their commits in on time, get doc wiki edits merged, etc. After a date is set, create a new maintenance/x.y.z branch, add new empty -release notes for the next version in the master branch and update the Trac +release notes for the next version in the main branch and update the Trac Milestones. -Make sure current trunk builds a package correctly --------------------------------------------------- + +Make sure current branch builds a package correctly +--------------------------------------------------- :: git clean -fxd - python setup.py bdist + python setup.py bdist_wheel python setup.py sdist -To actually build the binaries after everything is set up correctly, the -release.sh script can be used. For details of the build process itself it is +For details of the build process itself, it is best to read the pavement.py script. .. note:: The following steps are repeated for the beta(s), release candidates(s) and the final release. -Check that docs can be built ----------------------------- -Do:: - - cd doc/ - make dist - -to check that the documentation is in a buildable state. See -doc/HOWTO_BUILD_DOCS.rst.txt for more details and for how to update -http://docs.scipy.org. Check deprecations ------------------ @@ -233,7 +228,7 @@ There are three steps to the process. 2. If the C_API_VERSION in the first step has changed, or if the hash of the API has changed, the cversions.txt file needs to be updated. To check - the hash, run the script numpy/core/cversions.py and note the api hash that + the hash, run the script numpy/core/cversions.py and note the API hash that is printed. If that hash does not match the last hash in numpy/core/code_generators/cversions.txt the hash has changed. Using both the appropriate C_API_VERSION and hash, add a new entry to cversions.txt. @@ -244,7 +239,7 @@ There are three steps to the process. definitive. If steps 1 and 2 are done correctly, compiling the release should not give - a warning "API mismatch detect at the beginning of the build. + a warning "API mismatch detect at the beginning of the build". 3. The numpy/core/include/numpy/numpyconfig.h will need a new NPY_X_Y_API_VERSION macro, where X and Y are the major and minor version @@ -255,14 +250,20 @@ There are three steps to the process. The C ABI version number in numpy/core/setup_common.py should only be updated for a major release. + Check the release notes ----------------------- -Check that the release notes are up-to-date. +Use `towncrier`_ to build the release note and +commit the changes. This will remove all the fragments from +``doc/release/upcoming_changes`` and add ``doc/release/-note.rst``. -Write or update the release notes in a file named for the release, such as -``doc/release/1.11.0-notes.rst``. + towncrier build --version "" + git commit -m"Create release note" -Mention at least the following: +Check that the release notes are up-to-date. + +Update the release notes with a Highlights section. Mention some of the +following: - major new features - deprecated and removed features @@ -270,14 +271,13 @@ Mention at least the following: - for SciPy, supported NumPy version(s) - outlook for the near future -Also make sure that as soon as the branch is made, there is a new release -notes file in trunk for the next release. +.. _towncrier: https://pypi.org/project/towncrier/ + Update the release status and create a release "tag" ---------------------------------------------------- -Identify the commit hash of the release, e.g. 1b2e1d63ff. +Identify the commit hash of the release, e.g. 1b2e1d63ff:: -:: git co 1b2e1d63ff # gives warning about detached head First, change/check the following variables in ``pavement.py`` depending on the @@ -307,7 +307,7 @@ changes:: And make sure the ``VERSION`` variable is set properly. Now you can make the release commit and tag. We recommend you don't push -the commit or tag immediately, just in case you need to do more cleanup. We +the commit or tag immediately, just in case you need to do more cleanup. We prefer to defer the push of the tag until we're confident this is the exact form of the released code (see: :ref:`push-tag-and-commit`): @@ -318,11 +318,11 @@ The ``-s`` flag makes a PGP (usually GPG) signed tag. Please do sign the release tags. The release tag should have the release number in the annotation (tag -message). Unfortunately the name of a tag can be changed without breaking the +message). Unfortunately, the name of a tag can be changed without breaking the signature, the contents of the message cannot. -See : https://github.com/scipy/scipy/issues/4919 for a discussion of signing -release tags, and http://keyring.debian.org/creating-key.html for instructions +See: https://github.com/scipy/scipy/issues/4919 for a discussion of signing +release tags, and https://keyring.debian.org/creating-key.html for instructions on creating a GPG key if you do not have one. To make your key more readily identifiable as you, consider sending your key @@ -330,71 +330,73 @@ to public keyservers, with a command such as:: gpg --send-keys -Apply patch to fix bogus strides --------------------------------- -NPY_RELAXED_STRIDE_CHECKING was made the default in NumPy 1.10.0 and bogus -strides are used in the development branch to smoke out problems. The -`patch `_ should be updated if -necessary and applied to the release branch to rationalize the strides. -Update the version of the master branch ---------------------------------------- +Update the version of the main branch +------------------------------------- Increment the release number in setup.py. Release candidates should have "rc1" (or "rc2", "rcN") appended to the X.Y.Z format. Also create a new version hash in cversions.txt and a corresponding version define NPY_x_y_API_VERSION in numpyconfig.h -Trigger the wheel builds on travis-ci and Appveyor --------------------------------------------------- -See the `numpy wheels` repository. +Trigger the wheel builds +------------------------ +See the `numpy wheels`_ repository. In that repository edit the files: -* ``.travis.yml``; -* ``appveyor.yml``. +- ``azure/posix.yml`` +- ``azure/windows.yml``. In both cases, set the ``BUILD_COMMIT`` variable to the current release tag - -e.g. ``v1.11.1``. +e.g. ``v1.19.0``:: + + $ gvim azure/posix.yml azure/windows.yml + $ git commit -a + $ git push upstream HEAD Make sure that the release tag has been pushed. -Trigger a build by doing a commit of your edits to ``.travis.yml`` and -``appveyor.yml`` to the repository:: +Trigger a build by pushing a commit of your edits to the repository. Note that +you can do this on a branch, but it must be pushed upstream to the +``MacPython/numpy-wheels`` repository to trigger uploads since only +that repo has the appropriate tokens to allow uploads. - cd /path/to/numpy-wheels - # Edit .travis.yml, appveyor.yml - git commit - git push +The wheels, once built, appear at https://anaconda.org/multibuild-wheels-staging/numpy -The wheels, once built, appear at a Rackspace container pointed at by: +Make the release +---------------- +Build the changelog and notes for upload with:: -* http://wheels.scipy.org -* https://3f23b170c54c2533c070-1c8a9b3114517dc5fe17b7c3f8c63a43.ssl.cf2.rackcdn.com + paver write_release -The HTTP address may update first, and you should wait 15 minutes after the -build finishes before fetching the binaries. -Make the release ----------------- +Build and archive documentation +------------------------------- +Do:: -Build the changelog and notes for upload with:: + cd doc/ + make dist - paver write_release_and_log +to check that the documentation is in a buildable state. Then, after tagging, +create an archive of the documentation in the numpy/doc repo:: -The tar-files and binary releases for distribution should be uploaded to SourceForge, -together with the Release Notes and the Changelog. Uploading can be done -through a web interface or, more efficiently, through scp/sftp/rsync as -described in the SourceForge -`upload guide `_. -For example:: + # This checks out github.com/numpy/doc and adds (``git add``) the + # documentation to the checked out repo. + make merge-doc + # Now edit the ``index.html`` file in the repo to reflect the new content. + # If the documentation is for a non-patch release (e.g. 1.19 -> 1.20), + # make sure to update the ``stable`` symlink to point to the new directory. + ln -sfn stable + # Commit the changes + git -C build/merge commit -am "Add documentation for " + # Push to numpy/doc repo + git -C build/merge push - scp ,numpy@frs.sourceforge.net:/home/frs/project/n/nu/numpy/NumPy// Update PyPI ----------- - The wheels and source should be uploaded to PyPI. You should upload the wheels first, and the source formats last, to make sure @@ -403,44 +405,18 @@ expecting a binary wheel. You can do this automatically using the ``wheel-uploader`` script from https://github.com/MacPython/terryfy. Here is the recommended incantation for -downloading all the Windows, Manylinux, OSX wheels and uploading to PyPI. - -:: +downloading all the Windows, Manylinux, OSX wheels and uploading to PyPI. :: - cd ~/wheelhouse # local directory to cache wheel downloads - CDN_URL=https://3f23b170c54c2533c070-1c8a9b3114517dc5fe17b7c3f8c63a43.ssl.cf2.rackcdn.com - wheel-uploader -u $CDN_URL -w warehouse -v -s -t win numpy 1.11.1rc1 + NPY_WHLS=~/wheelhouse # local directory to cache wheel downloads + CDN_URL=https://anaconda.org/multibuild-wheels-staging/numpy/files + wheel-uploader -u $CDN_URL -w $NPY_WHLS -v -s -t win numpy 1.11.1rc1 wheel-uploader -u $CDN_URL -w warehouse -v -s -t macosx numpy 1.11.1rc1 wheel-uploader -u $CDN_URL -w warehouse -v -s -t manylinux1 numpy 1.11.1rc1 The ``-v`` flag gives verbose feedback, ``-s`` causes the script to sign the -wheels with your GPG key before upload. ``-r warehouse`` causes the upload to -use the Warehouse PyPI server. This is a good idea because the Warehouse -server seems to be a lot more reliable in receiving automated wheel uploads. -For this flag to work, you will need a ``warehouse`` section in your -``~/.pypirc`` file, of form: - - [distutils] - index-servers = - pypi - warehouse - - [pypi] - username:your_user_name - password:your_password - - [warehouse] - repository: https://upload.pypi.io/legacy/ - username: your_user_name - password: your_password - - [server-login] - username:your_user_name - password:your_password - -Don't forget to upload the wheels before the source tarball, so there is no -period for which people switch from an expected binary install to a source -install from PyPI. +wheels with your GPG key before upload. Don't forget to upload the wheels +before the source tarball, so there is no period for which people switch from +an expected binary install to a source install from PyPI. There are two ways to update the source release on PyPI, the first one is:: @@ -457,9 +433,9 @@ interface. .. _push-tag-and-commit: + Push the release tag and commit ------------------------------- - Finally, now you are confident this tag correctly defines the source code that you released you can push the tag and release commit up to github:: @@ -469,59 +445,47 @@ you released you can push the tag and release commit up to github:: where ``upstream`` points to the main https://github.com/numpy/numpy.git repository. -Update docs.scipy.org ---------------------- - -All documentation for a release can be updated on http://docs.scipy.org/ with: - - make dist - make upload USERNAME= RELEASE=1.11.0 - -Note that ```` must have SSH credentials on the server. If you don't -have those, ask someone who does (the list currently includes @rgommers, -@juliantaylor and @pv). - -Also rebuild and upload ``docs.scipy.org`` front page, if the release -series is a new one. The front page sources have their own repo: -https://github.com/scipy/docs.scipy.org. Do the following: - -- Update ``index.rst`` for the new version. -- ``make dist`` -- Check that the built documentation is OK. -- ``touch output-is-fine`` -- ``make upload USERNAME= RELEASE=1.x.y`` Update scipy.org ---------------- - A release announcement with a link to the download site should be placed in the sidebar of the front page of scipy.org. The scipy.org should be a PR at https://github.com/scipy/scipy.org. The file that needs modification is ``www/index.rst``. Search for ``News``. + +Update oldest-supported-numpy +----------------------------- +If this release is the first one to support a new Python version, or the first +to provide wheels for a new platform or PyPy version, the version pinnings +in https://github.com/scipy/oldest-supported-numpy should be updated. +Either submit a PR with changes to ``setup.cfg`` there, or open an issue with +info on needed changes. + + Announce to the lists --------------------- - The release should be announced on the mailing lists of NumPy and SciPy, to python-announce, and possibly also those of -Matplotlib,IPython and/or Pygame. +Matplotlib, IPython and/or Pygame. -During the beta/RC phase an explicit request for testing the binaries with +During the beta/RC phase, an explicit request for testing the binaries with several other libraries (SciPy/Matplotlib/Pygame) should be posted on the mailing list. + Announce to Linux Weekly News ----------------------------- - Email the editor of LWN to let them know of the release. Directions at: https://lwn.net/op/FAQ.lwn#contact + After the final release ----------------------- After the final release is announced, a few administrative tasks are left to be done: - Forward port changes in the release branch to release notes and release - scripts, if any, to trunk. + scripts, if any, to main branch. - Update the Milestones in Trac. diff --git a/doc/Makefile b/doc/Makefile index 667dbef29ec9..16fc3229d4c9 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -1,23 +1,36 @@ # Makefile for Sphinx documentation # -PYVER = 3.6 +# PYVER needs to be major.minor, just "3" doesn't work - it will result in +# issues with the amendments to PYTHONPATH and install paths (see DIST_VARS). + +# Use explicit "version_info" indexing since make cannot handle colon characters, and +# evaluate it now to allow easier debugging when printing the variable + +PYVER:=$(shell python3 -c 'from sys import version_info as v; print("{0}.{1}".format(v[0], v[1]))') PYTHON = python$(PYVER) # You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = LANG=C sphinx-build -PAPER = +SPHINXOPTS ?= +SPHINXBUILD ?= LANG=C sphinx-build +PAPER ?= +DOXYGEN ?= doxygen +# For merging a documentation archive into a git checkout of numpy/doc +# Turn a tag like v1.18.0 into 1.18 +# Use sed -n -e 's/patttern/match/p' to return a blank value if no match +TAG ?= $(shell git describe --tag | sed -n -e's,v\([1-9]\.[0-9]*\)\.[0-9].*,\1,p') FILES= # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source +ALLSPHINXOPTS = -WT --keep-going -d build/doctrees $(PAPEROPT_$(PAPER)) \ + $(SPHINXOPTS) source .PHONY: help clean html web pickle htmlhelp latex changes linkcheck \ - dist dist-build gitwash-update + dist dist-build gitwash-update version-check html-build latex-build \ + merge-doc show #------------------------------------------------------------------------------ @@ -33,9 +46,12 @@ help: @echo " dist PYVER=... to make a distribution-ready tree" @echo " gitwash-update GITWASH=path/to/gitwash update gitwash developer docs" @echo " upload USERNAME=... RELEASE=... to upload built docs to docs.scipy.org" + @echo " merge-doc TAG=... to clone numpy/doc and archive documentation into it" + @echo " show to show the html output in a browser" clean: - -rm -rf build/* source/reference/generated + -rm -rf build/* + find . -name generated -type d -prune -exec rm -rf "{}" ";" gitwash-update: rm -rf source/dev/gitwash @@ -51,28 +67,48 @@ gitwash-update: # Build the current numpy version, and extract docs from it. # We have to be careful of some issues: -# +# # - Everything must be done using the same Python version # - We must use eggs (otherwise they might override PYTHONPATH on import). # - Different versions of easy_install install to different directories (!) # -INSTALL_DIR = $(CURDIR)/build/inst-dist/ +INSTALL_DIR = $(CURDIR)/build/inst-dist INSTALL_PPH = $(INSTALL_DIR)/lib/python$(PYVER)/site-packages:$(INSTALL_DIR)/local/lib/python$(PYVER)/site-packages:$(INSTALL_DIR)/lib/python$(PYVER)/dist-packages:$(INSTALL_DIR)/local/lib/python$(PYVER)/dist-packages UPLOAD_DIR=/srv/docs_scipy_org/doc/numpy-$(RELEASE) -DIST_VARS=SPHINXBUILD="LANG=C PYTHONPATH=$(INSTALL_PPH) python$(PYVER) `which sphinx-build`" PYTHON="PYTHONPATH=$(INSTALL_PPH) python$(PYVER)" SPHINXOPTS="$(SPHINXOPTS)" +DIST_VARS=SPHINXBUILD="LANG=C PYTHONPATH=$(INSTALL_PPH) python$(PYVER) `which sphinx-build`" PYTHON="PYTHONPATH=$(INSTALL_PPH) python$(PYVER)" + +NUMPYVER:=$(shell $(PYTHON) -c "import numpy; print(numpy.version.git_revision[:10])" 2>/dev/null) +GITVER ?= $(shell cd ..; $(PYTHON) -c "import versioneer as v; print(v.get_versions()['full-revisionid'][:10])") + +version-check: +ifeq "$(GITVER)" "Unknown" + # @echo sdist build with unlabeled sources +else ifeq ("", "$(NUMPYVER)") + @echo numpy not found, cannot build documentation without successful \"import numpy\" + @exit 1 +else ifneq ($(NUMPYVER),$(GITVER)) + @echo installed numpy $(NUMPYVER) != current repo git version \'$(GITVER)\' + @echo use '"make dist"' or '"GITVER=$(NUMPYVER) make $(MAKECMDGOALS) ..."' + @exit 1 +else + # for testing + # @echo installed numpy $(NUMPYVER) matches git version $(GITVER); exit 1 +endif + + +dist: build/dist.tar.gz -dist: +build/dist.tar.gz: make $(DIST_VARS) real-dist -real-dist: dist-build html html-scipyorg - test -d build/latex || make latex +real-dist: dist-build html-build + test -d build/latex || make latex-build make -C build/latex all-pdf - -test -d build/htmlhelp || make htmlhelp-build -rm -rf build/dist - cp -r build/html-scipyorg build/dist + cp -r build/html build/dist cd build/html && zip -9r ../dist/numpy-html.zip . cp build/latex/numpy-ref.pdf build/dist cp build/latex/numpy-user.pdf build/dist @@ -86,7 +122,7 @@ dist-build: install -d $(subst :, ,$(INSTALL_PPH)) $(PYTHON) `which easy_install` --prefix=$(INSTALL_DIR) ../dist/*.egg -upload: +upload: build/dist.tar.gz # SSH must be correctly configured for this to work. # Assumes that ``make dist`` was already run # Example usage: ``make upload USERNAME=rgommers RELEASE=1.10.1`` @@ -103,6 +139,32 @@ upload: ssh $(USERNAME)@docs.scipy.org rm $(UPLOAD_DIR)/dist.tar.gz ssh $(USERNAME)@docs.scipy.org ln -snf numpy-$(RELEASE) /srv/docs_scipy_org/doc/numpy + +merge-doc: build/dist.tar.gz +ifeq "$(TAG)" "" + echo tag "$(TAG)" not of the form 1.18; + exit 1; +endif + @# Only clone if the directory does not exist + @if ! test -d build/merge; then \ + git clone https://github.com/numpy/doc build/merge; \ + fi; + @# Remove any old content and copy in the new, add it to git + -rm -rf build/merge/$(TAG)/* + -mkdir -p build/merge/$(TAG) + @# -C changes working directory + tar -C build/merge/$(TAG) -xf build/dist.tar.gz + git -C build/merge add $(TAG) + @# For now, the user must do this. If it is onerous, automate it and change + @# the instructions in doc/HOWTO_RELEASE.rst.txt + @echo " " + @echo New documentation archive added to ./build/merge. + @echo Now add/modify the appropriate section after + @echo " " + @echo in build/merge/index.html, + @echo then \"git commit\", \"git push\" + + #------------------------------------------------------------------------------ # Basic Sphinx generation rules for different formats #------------------------------------------------------------------------------ @@ -112,8 +174,15 @@ build/generate-stamp: $(wildcard source/reference/*.rst) mkdir -p build touch build/generate-stamp -html: generate +html: version-check html-build +html-build: generate mkdir -p build/html build/doctrees + $(PYTHON) preprocess.py +ifeq (, $(shell which $(DOXYGEN))) + @echo "Unable to find 'Doxygen:$(DOXYGEN)', skip generating C/C++ API from comment blocks." +else + $(DOXYGEN) build/doxygen/Doxyfile +endif $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) build/html $(FILES) $(PYTHON) postprocess.py html build/html/*.html @echo @@ -123,9 +192,9 @@ html-scipyorg: mkdir -p build/html build/doctrees $(SPHINXBUILD) -t scipyorg -b html $(ALLSPHINXOPTS) build/html-scipyorg $(FILES) @echo - @echo "Build finished. The HTML pages are in build/html." + @echo "Build finished. The HTML pages are in build/html-scipyorg." -pickle: generate +pickle: generate version-check mkdir -p build/pickle build/doctrees $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) build/pickle $(FILES) @echo @@ -135,7 +204,7 @@ pickle: generate web: pickle -htmlhelp: generate +htmlhelp: generate version-check mkdir -p build/htmlhelp build/doctrees $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) build/htmlhelp $(FILES) @echo @@ -146,32 +215,33 @@ htmlhelp-build: htmlhelp build/htmlhelp/numpy.chm %.chm: %.hhp -hhc.exe $^ -qthelp: generate +qthelp: generate version-check mkdir -p build/qthelp build/doctrees $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) build/qthelp $(FILES) -latex: generate +latex: version-check latex-build +latex-build: generate mkdir -p build/latex build/doctrees $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) build/latex $(FILES) $(PYTHON) postprocess.py tex build/latex/*.tex - perl -pi -e 's/\t(latex.*|pdflatex) (.*)/\t-$$1 -interaction batchmode $$2/' build/latex/Makefile + perl -pi -e 's/LATEXOPTS =/LATEXOPTS ?= --halt-on-error/' build/latex/Makefile @echo @echo "Build finished; the LaTeX files are in build/latex." @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ "run these through (pdf)latex." -coverage: build +coverage: build version-check mkdir -p build/coverage build/doctrees $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) build/coverage $(FILES) @echo "Coverage finished; see c.txt and python.txt in build/coverage" -changes: generate +changes: generate version-check mkdir -p build/changes build/doctrees $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) build/changes $(FILES) @echo @echo "The overview file is in build/changes." -linkcheck: generate +linkcheck: generate version-check mkdir -p build/linkcheck build/doctrees $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) build/linkcheck $(FILES) @echo @@ -190,3 +260,7 @@ info: @echo "Running Texinfo files through makeinfo..." make -C build/texinfo info @echo "makeinfo finished; the Info files are in build/texinfo." + +show: + @python -c "import webbrowser; webbrowser.open_new_tab('file://$(PWD)/build/html/index.html')" + diff --git a/doc/Py3K.rst.txt b/doc/Py3K.rst.txt index 44111eeb5710..cde0394ddab6 100644 --- a/doc/Py3K.rst.txt +++ b/doc/Py3K.rst.txt @@ -22,8 +22,8 @@ Resources Information on porting to 3K: -- http://wiki.python.org/moin/cporting -- http://wiki.python.org/moin/PortingExtensionModulesToPy3k +- https://wiki.python.org/moin/cporting +- https://wiki.python.org/moin/PortingExtensionModulesToPy3k Prerequisites @@ -355,9 +355,7 @@ The Py2/Py3 compatible structure definition looks like:: (binaryfunc)0, /*nb_true_divide*/ 0, /*nb_inplace_floor_divide*/ 0, /*nb_inplace_true_divide*/ - #if PY_VERSION_HEX >= 0x02050000 (unaryfunc)NULL, /*nb_index*/ - #endif }; @@ -394,14 +392,6 @@ There are a couple of places that need further attention: In some cases, this returns a buffer object on Python 2. On Python 3, there is no stand-alone buffer object, so we return a byte array instead. -- multiarray.int_asbuffer - - Converts an integer to a void* pointer -- in Python. - - Should we just remove this for Py3? It doesn't seem like it is used - anywhere, and it doesn't sound very useful. - - The Py2/Py3 compatible PyBufferMethods definition looks like:: NPY_NO_EXPORT PyBufferProcs array_as_buffer = { @@ -428,10 +418,6 @@ The Py2/Py3 compatible PyBufferMethods definition looks like:: Produce PEP 3118 format strings for array scalar objects. -.. todo:: - - Figure out what to do with int_asbuffer - .. todo:: There's stuff to clean up in numarray/_capi.c @@ -812,20 +798,20 @@ Types with tp_as_sequence defined PySequenceMethods in py3k are binary compatible with py2k, but some of the slots have gone away. I suspect this means some functions need redefining so -the semantics of the slots needs to be checked. - -PySequenceMethods foo_sequence_methods = { - (lenfunc)0, /* sq_length */ - (binaryfunc)0, /* sq_concat */ - (ssizeargfunc)0, /* sq_repeat */ - (ssizeargfunc)0, /* sq_item */ - (void *)0, /* nee sq_slice */ - (ssizeobjargproc)0, /* sq_ass_item */ - (void *)0, /* nee sq_ass_slice */ - (objobjproc)0, /* sq_contains */ - (binaryfunc)0, /* sq_inplace_concat */ - (ssizeargfunc)0 /* sq_inplace_repeat */ -}; +the semantics of the slots needs to be checked:: + + PySequenceMethods foo_sequence_methods = { + (lenfunc)0, /* sq_length */ + (binaryfunc)0, /* sq_concat */ + (ssizeargfunc)0, /* sq_repeat */ + (ssizeargfunc)0, /* sq_item */ + (void *)0, /* nee sq_slice */ + (ssizeobjargproc)0, /* sq_ass_item */ + (void *)0, /* nee sq_ass_slice */ + (objobjproc)0, /* sq_contains */ + (binaryfunc)0, /* sq_inplace_concat */ + (ssizeargfunc)0 /* sq_inplace_repeat */ + }; PyMappingMethods @@ -840,13 +826,13 @@ Types with tp_as_mapping defined * multiarray/arrayobject.c PyMappingMethods in py3k look to be the same as in py2k. The semantics -of the slots needs to be checked. +of the slots needs to be checked:: -PyMappingMethods foo_mapping_methods = { - (lenfunc)0, /* mp_length */ - (binaryfunc)0, /* mp_subscript */ - (objobjargproc)0 /* mp_ass_subscript */ -}; + PyMappingMethods foo_mapping_methods = { + (lenfunc)0, /* mp_length */ + (binaryfunc)0, /* mp_subscript */ + (objobjargproc)0 /* mp_ass_subscript */ + }; PyFile diff --git a/doc/RELEASE_WALKTHROUGH.rst.txt b/doc/RELEASE_WALKTHROUGH.rst.txt index ad14c16c1739..42d84e04b25a 100644 --- a/doc/RELEASE_WALKTHROUGH.rst.txt +++ b/doc/RELEASE_WALKTHROUGH.rst.txt @@ -1,23 +1,84 @@ -This file contains a walkthrough of the NumPy 1.14.4 release on Linux. +This file contains a walkthrough of the NumPy 1.21.0 release on Linux, modified +for building on azure and uploading to anaconda.org The commands can be copied into the command line, but be sure to -replace 1.14.4 by the correct version. +replace 1.21.0 by the correct version. + +This should be read together with the general directions in `releasing`. + + +Release Preparation +=================== + +Backport Pull Requests +---------------------- + +Changes that have been marked for this release must be backported to the +maintenance/1.21.x branch. -Release Walkthrough -==================== Update Release documentation ---------------------------- -The file ``doc/changelog/1.14.4-changelog.rst`` should be updated to reflect -the final list of changes and contributors. This text can be generated by:: +Four documents usually need to be updated or created before making a release: + +- The changelog +- The release-notes +- The ``.mailmap`` file +- The ``doc/source/release.rst`` file + +These changes should be made as an ordinary PR against the maintenance branch. +After release all files except ``doc/source/release.rst`` will need to be +forward ported to the main branch. + +Generate the changelog +~~~~~~~~~~~~~~~~~~~~~~ + +The changelog is generated using the changelog tool:: + + $ python tools/changelog.py $GITHUB v1.20.0..maintenance/1.21.x > doc/changelog/1.21.0-changelog.rst + +where ``GITHUB`` contains your GitHub access token. The text will need to be +checked for non-standard contributor names and dependabot entries removed. It +is also a good idea to remove any links that may be present in the PR titles +as they don't translate well to markdown, replace them with monospaced text. The +non-standard contributor names should be fixed by updating the ``.mailmap`` +file, which is a lot of work. It is best to make several trial runs before +reaching this point and ping the malefactors using a GitHub issue to get the +needed information. + +Finish the release notes +~~~~~~~~~~~~~~~~~~~~~~~~ - $ python tools/changelog.py $GITHUB v1.14.3..maintenance/1.14.x > doc/changelog/1.14.4-changelog.rst +If this is the first release in a series the release note is generated, see +the release note in ``doc/release/upcoming_changes/README.rst`` to see how to +do this. Generating the release notes will also delete all the news +fragment files in ``doc/release/upcoming_changes/``. -where ``GITHUB`` contains your github access token. This text may also be -appended to ``doc/release/1.14.4-notes.rst`` for release updates, though not -for new releases like ``1.14.0``, as the changelogs for latter tend to be -excessively long. The ``doc/source/release.rst`` file should also be -updated with a link to the new release notes. +The generated release note will always need some fixups, the introduction will +need to be written, and significant changes should be called out. For patch +releases the changelog text may also be appended, but not for the initial +release as it is too long. Check previous release notes to see how this is +done. Note that the ``:orphan:`` markup at the top, if present, will need +changing to ``.. currentmodule:: numpy`` and the ``doc/source/release.rst`` +index file will need updating. + +Check the pavement.py file +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Check that the pavement.py file points to the correct release notes. It should +have been updated after the last release, but if not, fix it now:: + + $gvim pavement.py + + +Release Walkthrough +==================== + +Note that in the code snippets below, ``upstream`` refers to the root repository on +GitHub and ``origin`` to its fork in your personal GitHub repositories. You may +need to make adjustments if you have not forked the repository but simply +cloned it locally. You can also edit ``.git/config`` and add ``upstream`` if it +isn't already present. Prepare the release commit -------------------------- @@ -25,184 +86,187 @@ Prepare the release commit Checkout the branch for the release, make sure it is up to date, and clean the repository:: - $ git checkout maintenance/1.14.x - $ git pull upstream maintenance/1.14.x + $ git checkout maintenance/1.21.x + $ git pull upstream maintenance/1.21.x $ git submodule update - $ git clean -xdf - -Edit pavement.py and setup.py as detailed in HOWTO_RELEASE:: - - $ gvim pavement.py setup.py - $ git commit -a -m"REL: NumPy 1.14.4 release." + $ git clean -xdfq Sanity check:: - $ python runtests.py -m "full" $ python3 runtests.py -m "full" -Push this release directly onto the end of the maintenance branch. This -requires write permission to the numpy repository:: +Tag the release and push the tag. This requires write permission for the numpy +repository:: - $ git push upstream maintenance/1.14.x + $ git tag -a -s v1.21.0 -m"NumPy 1.21.0 release" + $ git push upstream v1.21.0 -As an example, see the 1.14.3 REL commit: ``_. Build source releases --------------------- Paver is used to build the source releases. It will create the ``release`` and ``release/installers`` directories and put the ``*.zip`` and ``*.tar.gz`` -source releases in the latter. +source releases in the latter. :: + + $ paver sdist # sdist will do a git clean -xdfq, so we omit that - $ paver sdist # sdist will do a git clean -xdf, so we omit that Build wheels ------------ Trigger the wheels build by pointing the numpy-wheels repository at this -commit. This can take a while. The numpy-wheels repository is cloned from -``_. Start with a pull as the repo -may have been accessed and changed by someone else and a push will fail:: +commit. This can take up to an hour. The numpy-wheels repository is cloned from +``_. If this is the first release in +a series, start with a pull as the repo may have been accessed and changed by +someone else, then create a new branch for the series. If the branch already +exists skip this:: $ cd ../numpy-wheels - $ git pull origin master - $ git branch # only when starting new numpy version - $ git checkout v1.14.x # v1.14.x already existed for the 1.14.4 release - -Edit the ``.travis.yml`` and ``.appveyor.yml`` files to make sure they have the -correct version, and put in the commit hash for the ``REL`` commit created -above for ``BUILD_COMMIT``, see the _example from `v1.14.3`:: - - $ gvim .travis.yml appveyor.yml - $ git commit -a - $ git push origin HEAD + $ git checkout main + $ git pull upstream main + $ git branch v1.21.x + +Checkout the new branch and edit the ``azure-pipelines.yml`` and +``.travis.yml`` files to make sure they have the correct version, and put in +the commit hash for the ``REL`` commit created above for ``BUILD_COMMIT`` +variable. The ``azure/posix.yml`` and ``.travis.yml`` files may also need the +Cython versions updated to keep up with Python releases, but generally just +do:: + + $ git checkout v1.21.x + $ gvim azure-pipelines.yml .travis.yml + $ git commit -a -m"NumPy 1.21.0 release." + $ git push upstream HEAD Now wait. If you get nervous at the amount of time taken -- the builds can take -several hours-- you can check the build progress by following the links -provided at ``_ to check the travis -and appveyor build status. Check if all the needed wheels have been built and -uploaded before proceeding. There should currently be 22 of them at -``_, 4 for Mac, 8 for Windows, and 10 for Linux. -Note that sometimes builds, like tests, fail for unrelated reasons and you will -need to restart them. +a while -- you can check the build progress by following the links +provided at ``_ to check the +build status. Check if all the needed wheels have been built and +uploaded to the staging repository before proceeding. -.. example_: https://github.com/MacPython/numpy-wheels/commit/fed9c04629c155e7804282eb803d81097244598d +Note that sometimes builds, like tests, fail for unrelated reasons and you will +need to rerun them. You will need to be logged in under 'numpy' to do this +on azure. Download wheels --------------- -When the wheels have all been successfully built, download them using the ``wheel-uploader`` -in the ``terryfy`` repository. The terryfy repository may be cloned from -``_ if you don't already have it. The -wheels can also be uploaded using the ``wheel-uploader``, but we prefer to -download all the wheels to the ``../numpy/release/installers`` directory and -upload later using ``twine``:: - - $ cd ../terryfy - $ git pull origin master - $ CDN_URL=https://3f23b170c54c2533c070-1c8a9b3114517dc5fe17b7c3f8c63a43.ssl.cf2.rackcdn.com - $ NPY_WHLS=../numpy/release/installers - $ ./wheel-uploader -u $CDN_URL -n -v -w $NPY_WHLS -t win numpy 1.14.4 - $ ./wheel-uploader -u $CDN_URL -n -v -w $NPY_WHLS -t manylinux1 numpy 1.14.4 - $ ./wheel-uploader -u $CDN_URL -n -v -w $NPY_WHLS -t macosx numpy 1.14.4 - -If you do this often, consider making CDN_URL and NPY_WHLS part of your default -environment. Note that we need local copies of the files in order to generate -hashes to include in the README files generated later. - -Tag the release ---------------- +When the wheels have all been successfully built and staged, download them from the +Anaconda staging directory using the ``tools/download-wheels.py`` script:: -Once the wheels have been built and downloaded without errors, go back to your -numpy repository in the maintenance branch and tag the ``REL`` commit, signing -it with your gpg key, and build the source distribution archives:: + $ cd ../numpy + $ python3 tools/download-wheels.py 1.21.0 - $ git tag -s v1.14.4 -You should upload your public gpg key to github, so that the tag will appear -"verified" there. +Generate the README files +------------------------- -Check that the files in ``release/installers`` have the correct versions, then -push the tag upstream:: +This needs to be done after all installers are downloaded, but before the pavement +file is updated for continued development:: - $ git push upstream v1.14.4 + $ paver write_release -We wait until this point to push the tag because it is very difficult to change -the tag after it has been pushed. Reset the maintenance branch into a development state ----------------------------------------------------- -Add another ``REL`` commit to the numpy maintenance branch, which resets the -``ISREALEASED`` flag to ``False`` and increments the version counter:: +Create release notes for next release and edit them to set the version. These +notes will be a skeleton and have little content:: + + $ cp doc/source/release/template.rst doc/source/release/1.21.1-notes.rst + $ gvim doc/source/release/1.21.1-notes.rst + $ git add doc/source/release/1.21.1-notes.rst - $ gvim pavement.py setup.py - $ git commit -a -m"REL: prepare 1.14.x for further development" - $ git push upstream maintenance/1.14.x +Add new release notes to the documentation release list and update the +``RELEASE_NOTES`` variable in ``pavement.py``. + + $ gvim doc/source/release.rst pavement.py + +Commit the result:: + + $ git commit -a -m"REL: prepare 1.21.x for further development" + $ git push upstream HEAD -This strategy is copied from the scipy release procedure and was used in numpy -for the first time in 1.14.3. It needed to be modified a little since numpy -has more strict requirements for the version number. Upload to PyPI -------------- Upload to PyPI using ``twine``. A recent version of ``twine`` of is needed -after recent PyPI changes, version ``1.11.0`` was used here. :: +after recent PyPI changes, version ``3.4.1`` was used here:: $ cd ../numpy $ twine upload release/installers/*.whl - $ twine upload release/installers/numpy-1.14.4.zip # Upload last. - -If one of the commands breaks in the middle, which is not uncommon, you may -need to selectively upload the remaining files because PyPI does not allow the -same file to be uploaded twice. The source file should be uploaded last to -avoid synchronization problems if pip users access the files while this is in -process. Note that PyPI only allows a single source distribution, here we have + $ twine upload release/installers/numpy-1.21.0.zip # Upload last. + +If one of the commands breaks in the middle, you may need to selectively upload +the remaining files because PyPI does not allow the same file to be uploaded +twice. The source file should be uploaded last to avoid synchronization +problems that might occur if pip users access the files while this is in +process, causing pip to build from source rather than downloading a binary +wheel. PyPI only allows a single source distribution, here we have chosen the zip archive. + Upload files to github ---------------------- -Generate the ``release/README.*`` files:: - - $ paver write_release_and_log - -Go to ``_, there should be a ``v1.14.4 +Go to ``_, there should be a ``v1.21.0 tag``, click on it and hit the edit button for that tag. There are two ways to -add files, using an editable text window and as binary uploads. - -- Cut and paste the ``release/README.md`` file contents into the text window. -- Upload ``release/installers/numpy-1.14.4.tar.gz`` as a binary file. -- Upload ``release/installers/numpy-1.14.4.zip`` as a binary file. +add files, using an editable text window and as binary uploads. Start by +editing the ``release/README.md`` that is translated from the rst version using +pandoc. Things that will need fixing: PR lines from the changelog, if included, +are wrapped and need unwrapping, links should be changed to monospaced text. +Then copy the contents to the clipboard and paste them into the text window. It +may take several tries to get it look right. Then + +- Upload ``release/installers/numpy-1.21.0.tar.gz`` as a binary file. +- Upload ``release/installers/numpy-1.21.0.zip`` as a binary file. - Upload ``release/README.rst`` as a binary file. -- Upload ``doc/changelog/1.14.4-changelog.rst`` as a binary file. +- Upload ``doc/changelog/1.21.0-changelog.rst`` as a binary file. - Check the pre-release button if this is a pre-releases. - Hit the ``{Publish,Update} release`` button at the bottom. -Upload documents to docs.scipy.org ----------------------------------- +Upload documents to numpy.org +----------------------------- -This step is only needed for final releases and can be skipped for -pre-releases. You will also need upload permission for the document server, if -you do not have permission ping Pauli Virtanen or Ralf Gommers to generate and -upload the documentation. Otherwise:: +This step is only needed for final releases and can be skipped for pre-releases +and most patch releases. ``make merge-doc`` clones the ``numpy/doc`` repo into +``doc/build/merge`` and updates it with the new documentation:: +Note that if you have a `.local` numpy install, you should either remove it or +install the current version for the docs to pick up the correct NumPy version. $ pushd doc $ make dist - $ make upload USERNAME= RELEASE=v1.14.4 + $ make merge-doc $ popd -If the release series is a new one, you will need to rebuild and upload the -``docs.scipy.org`` front page:: +If the release series is a new one, you will need to add a new section to the +``doc/build/merge/index.html`` front page just after the "insert here" comment:: + + $ gvim doc/build/merge/index.html +/'insert here' + +Otherwise, only the ``zip`` and ``pdf`` links should be updated with the +new tag name:: + + $ gvim doc/build/merge/index.html +/'tag v1.21' + +You can "test run" the new documentation in a browser to make sure the links +work:: + + $ firefox doc/build/merge/index.html + +Update the stable link:: - $ cd ../docs.scipy.org - $ gvim index.rst + $ ln -sfn 1.21 stable -Note: there is discussion about moving the docs to github. This section will be -updated when/if that happens. +Once everything seems satisfactory, commit and upload the changes:: + $ pushd doc/build/merge + $ git commit -a -m"Add documentation for v1.21.0" + $ git push + $ popd Announce the release on scipy.org --------------------------------- @@ -212,7 +276,7 @@ This assumes that you have forked ``_:: $ cd ../scipy.org $ git checkout master $ git pull upstream master - $ git checkout -b numpy-1.14.4 + $ git checkout -b numpy-1.21.0 $ gvim www/index.rst # edit the News section $ git commit -a $ git push origin HEAD @@ -224,5 +288,24 @@ Announce to mailing lists The release should be announced on the numpy-discussion, scipy-devel, scipy-user, and python-announce-list mailing lists. Look at previous -announcements for the basic template. The contributor and PR lists -are the same as generated for the release notes above. +announcements for the basic template. The contributor and PR lists are the same +as generated for the release notes above. If you crosspost, make sure that +python-announce-list is BCC so that replies will not be sent to that list. + + +Post-Release Tasks +------------------ + +Checkout main and forward port the documentation changes:: + + $ git checkout -b post-1.21.0-release-update + $ git checkout maintenance/1.21.x doc/source/release/1.21.0-notes.rst + $ git checkout maintenance/1.21.x doc/changelog/1.21.0-changelog.rst + $ git checkout maintenance/1.21.x .mailmap # only if updated for release. + $ gvim doc/source/release.rst # Add link to new notes + $ git add doc/changelog/1.21.0-changelog.rst doc/source/release/1.21.0-notes.rst + $ git status # check status before commit + $ git commit -a -m"REL: Update main after 1.21.0 release." + $ git push origin HEAD + +Go to GitHub and make a PR. diff --git a/doc/TESTS.rst.txt b/doc/TESTS.rst.txt index 68b0eace4a78..0d8137f4a7e1 100644 --- a/doc/TESTS.rst.txt +++ b/doc/TESTS.rst.txt @@ -1,50 +1,51 @@ -.. -*- rest -*- - NumPy/SciPy Testing Guidelines ============================== .. contents:: + Introduction '''''''''''' -SciPy uses the `Nose testing system -`__, with some -minor convenience features added. Nose is an extension of the unit -testing framework offered by `unittest.py -`__. Our goal is that -every module and package in SciPy should have a thorough set of unit +Until the 1.15 release, NumPy used the `nose`_ testing framework, it now uses +the `pytest`_ framework. The older framework is still maintained in order to +support downstream projects that use the old numpy framework, but all tests +for NumPy should use pytest. + +Our goal is that every module and package in NumPy +should have a thorough set of unit tests. These tests should exercise the full functionality of a given routine as well as its robustness to erroneous or unexpected input -arguments. Long experience has shown that by far the best time to -write the tests is before you write or change the code - this is -`test-driven development -`__. The -arguments for this can sound rather abstract, but we can assure you -that you will find that writing the tests first leads to more robust -and better designed code. Well-designed tests with good coverage make +arguments. Well-designed tests with good coverage make an enormous difference to the ease of refactoring. Whenever a new bug is found in a routine, you should write a new test for that specific case and add it to the test suite to prevent that bug from creeping back in unnoticed. -To run SciPy's full test suite, use the following:: +.. note:: - >>> import scipy - >>> scipy.test() + SciPy uses the testing framework from :mod:`numpy.testing`, + so all of the NumPy examples shown below are also applicable to SciPy -SciPy uses the testing framework from NumPy (specifically -``numpy.testing``), so all the SciPy examples shown here are also -applicable to NumPy. So NumPy's full test suite can be run as -follows:: +Testing NumPy +''''''''''''' + +NumPy can be tested in a number of ways, choose any way you feel comfortable. + +Running tests from inside Python +-------------------------------- + +You can test an installed NumPy by `numpy.test`, for example, +To run NumPy's full test suite, use the following:: >>> import numpy - >>> numpy.test() + >>> numpy.test(label='slow') -The test method may take two or more arguments; the first, ``label`` is a -string specifying what should be tested and the second, ``verbose`` is an -integer giving the level of output verbosity. See the docstring for -numpy.test for details. The default value for ``label`` is 'fast' - which +The test method may take two or more arguments; the first ``label`` is a +string specifying what should be tested and the second ``verbose`` is an +integer giving the level of output verbosity. See the docstring +`numpy.test` +for details. The default value for ``label`` is 'fast' - which will run the standard tests. The string 'full' will run the full battery of tests, including those identified as being slow to run. If ``verbose`` is 1 or less, the tests will just show information messages about the tests @@ -52,170 +53,162 @@ that are run; but if it is greater than 1, then the tests will also provide warnings on missing tests. So if you want to run every test and get messages about which modules don't have tests:: - >>> scipy.test(label='full', verbose=2) # or scipy.test('full', 2) + >>> numpy.test(label='full', verbose=2) # or numpy.test('full', 2) -Finally, if you are only interested in testing a subset of SciPy, for -example, the ``integrate`` module, use the following:: +Finally, if you are only interested in testing a subset of NumPy, for +example, the ``core`` module, use the following:: ->>> scipy.integrate.test() + >>> numpy.core.test() -The rest of this page will give you a basic idea of how to add unit -tests to modules in SciPy. It is extremely important for us to have -extensive unit testing since this code is going to be used by -scientists and researchers and is being developed by a large number of -people spread across the world. So, if you are writing a package that -you'd like to become part of SciPy, please write the tests as you -develop the package. Also since much of SciPy is legacy code that was -originally written without unit tests, there are still several modules -that don't have tests yet. Please feel free to choose one of these -modules and develop tests for it as you read through -this introduction. +Running tests from the command line +----------------------------------- + +If you want to build NumPy in order to work on NumPy itself, use +``runtests.py``.To run NumPy's full test suite:: + + $ python runtests.py + +Testing a subset of NumPy:: + + $python runtests.py -t numpy/core/tests + +For detailed info on testing, see :ref:`testing-builds` + +Other methods of running tests +------------------------------ + +Run tests using your favourite IDE such as `vscode`_ or `pycharm`_ Writing your own tests '''''''''''''''''''''' -Every Python module, extension module, or subpackage in the SciPy +If you are writing a package that you'd like to become part of NumPy, +please write the tests as you develop the package. +Every Python module, extension module, or subpackage in the NumPy package directory should have a corresponding ``test_.py`` file. -Nose examines these files for test methods (named test*) and test -classes (named Test*). +Pytest examines these files for test methods (named ``test*``) and test +classes (named ``Test*``). -Suppose you have a SciPy module ``scipy/xxx/yyy.py`` containing a +Suppose you have a NumPy module ``numpy/xxx/yyy.py`` containing a function ``zzz()``. To test this function you would create a test module called ``test_yyy.py``. If you only need to test one aspect of ``zzz``, you can simply add a test function:: def test_zzz(): - assert_(zzz() == 'Hello from zzz') + assert zzz() == 'Hello from zzz' More often, we need to group a number of tests together, so we create a test class:: - from numpy.testing import assert_, assert_raises + import pytest # import xxx symbols - from scipy.xxx.yyy import zzz + from numpy.xxx.yyy import zzz + import pytest class TestZzz: def test_simple(self): - assert_(zzz() == 'Hello from zzz') + assert zzz() == 'Hello from zzz' def test_invalid_parameter(self): - assert_raises(...) + with pytest.raises(ValueError, match='.*some matching regex.*'): + ... -Within these test methods, ``assert_()`` and related functions are used to test +Within these test methods, ``assert`` and related functions are used to test whether a certain assumption is valid. If the assertion fails, the test fails. -Note that the Python builtin ``assert`` should not be used, because it is -stripped during compilation with ``-O``. +``pytest`` internally rewrites the ``assert`` statement to give informative +output when it fails, so should be preferred over the legacy variant +``numpy.testing.assert_``. Whereas plain ``assert`` statements are ignored +when running Python in optimized mode with ``-O``, this is not an issue when +running tests with pytest. + +Similarly, the pytest functions :func:`pytest.raises` and :func:`pytest.warns` +should be preferred over their legacy counterparts +:func:`numpy.testing.assert_raises` and :func:`numpy.testing.assert_warns`, +since the pytest variants are more broadly used and allow more explicit +targeting of warnings and errors when used with the ``match`` regex. + Note that ``test_`` functions or methods should not have a docstring, because that makes it hard to identify the test from the output of running the test suite with ``verbose=2`` (or similar verbosity setting). Use plain comments (``#``) if necessary. -Sometimes it is convenient to run ``test_yyy.py`` by itself, so we add +Also since much of NumPy is legacy code that was +originally written without unit tests, there are still several modules +that don't have tests yet. Please feel free to choose one of these +modules and develop tests for it. + +Using C code in tests +--------------------- -:: +NumPy exposes a rich :ref:`C-API` . These are tested using c-extension +modules written "as-if" they know nothing about the internals of NumPy, rather +using the official C-API interfaces only. Examples of such modules are tests +for a user-defined ``rational`` dtype in ``_rational_tests`` or the ufunc +machinery tests in ``_umath_tests`` which are part of the binary distribution. +Starting from version 1.21, you can also write snippets of C code in tests that +will be compiled locally into c-extension modules and loaded into python. - if __name__ == "__main__": - run_module_suite() +.. currentmodule:: numpy.testing.extbuild -at the bottom. +.. autofunction:: build_and_import_extension -Labeling tests with nose ------------------------- +Labeling tests +-------------- Unlabeled tests like the ones above are run in the default -``scipy.test()`` run. If you want to label your test as slow - and -therefore reserved for a full ``scipy.test(label='full')`` run, you -can label it with a nose decorator:: +``numpy.test()`` run. If you want to label your test as slow - and +therefore reserved for a full ``numpy.test(label='full')`` run, you +can label it with ``pytest.mark.slow``:: - # numpy.testing module includes 'import decorators as dec' - from numpy.testing import dec, assert_ + import pytest - @dec.slow + @pytest.mark.slow def test_big(self): - print 'Big, slow test' + print('Big, slow test') Similarly for methods:: class test_zzz: - @dec.slow + @pytest.mark.slow def test_simple(self): assert_(zzz() == 'Hello from zzz') Easier setup and teardown functions / methods --------------------------------------------- -Nose looks for module level setup and teardown functions by name; -thus:: +Testing looks for module-level or class-level setup and teardown functions by +name; thus:: def setup(): """Module-level setup""" - print 'doing setup' + print('doing setup') def teardown(): """Module-level teardown""" - print 'doing teardown' + print('doing teardown') -You can add setup and teardown functions to functions and methods with -nose decorators:: + class TestMe: + def setup(): + """Class-level setup""" + print('doing setup') - import nose - # import all functions from numpy.testing that are needed - from numpy.testing import assert_, assert_array_almost_equal + def teardown(): + """Class-level teardown""" + print('doing teardown') - def setup_func(): - """A trivial setup function.""" - global helpful_variable - helpful_variable = 'pleasant' - print "In setup_func" - def teardown_func(): - """A trivial teardown function.""" - global helpful_variable - del helpful_variable - print "In teardown_func" - - @nose.with_setup(setup_func, teardown_func) - def test_with_extras(): - # This test uses the setup/teardown functions. - global helpful_variable - print " In test_with_extras" - print " Helpful is %s" % helpful_variable +Setup and teardown functions to functions and methods are known as "fixtures", +and their use is not encouraged. Parametric tests ---------------- -One very nice feature of nose is allowing easy testing across a range -of parameters - a nasty problem for standard unit tests. It does this -with test generators:: - - def check_even(n, nn): - """A check function to be used in a test generator.""" - assert_(n % 2 == 0 or nn % 2 == 0) - - def test_evens(): - for i in range(0,4,2): - yield check_even, i, i*3 - -Note that ``check_even`` is not itself a test (no 'test' in the name), -but ``test_evens`` is a generator that returns a series of tests, using -``check_even``, across a range of inputs. - -A problem with generator tests can be that if a test is failing, it's -hard to see for which parameters. To avoid this problem, ensure that: - - - No computation related to the features tested is done in the - ``test_*`` generator function, but delegated to a corresponding - ``check_*`` function (can be inside the generator, to share namespace). - - The generators are used *solely* for loops over parameters. - - Those parameters are *not* arrays. - -.. warning:: - - Parametric tests cannot be implemented on classes derived from - TestCase. +One very nice feature of testing is allowing easy testing across a range +of parameters - a nasty problem for standard unit tests. Use the +``pytest.mark.parametrize`` decorator. Doctests -------- @@ -234,10 +227,10 @@ for numpy.lib:: >>> np.lib.test(doctests=True) The doctests are run as if they are in a fresh Python instance which -has executed ``import numpy as np``. Tests that are part of a SciPy +has executed ``import numpy as np``. Tests that are part of a NumPy subpackage will have that subpackage already imported. E.g. for a test -in ``scipy/linalg/tests/``, the namespace will be created such that -``from scipy import linalg`` has already executed. +in ``numpy/linalg/tests/``, the namespace will be created such that +``from numpy import linalg`` has already executed. ``tests/`` @@ -246,15 +239,15 @@ in ``scipy/linalg/tests/``, the namespace will be created such that Rather than keeping the code and the tests in the same directory, we put all the tests for a given subpackage in a ``tests/`` subdirectory. For our example, if it doesn't already exist you will -need to create a ``tests/`` directory in ``scipy/xxx/``. So the path -for ``test_yyy.py`` is ``scipy/xxx/tests/test_yyy.py``. +need to create a ``tests/`` directory in ``numpy/xxx/``. So the path +for ``test_yyy.py`` is ``numpy/xxx/tests/test_yyy.py``. -Once the ``scipy/xxx/tests/test_yyy.py`` is written, its possible to +Once the ``numpy/xxx/tests/test_yyy.py`` is written, its possible to run the tests by going to the ``tests/`` directory and typing:: python test_yyy.py -Or if you add ``scipy/xxx/tests/`` to the Python path, you could run +Or if you add ``numpy/xxx/tests/`` to the Python path, you could run the tests interactively in the interpreter like this:: >>> import test_yyy @@ -279,20 +272,20 @@ section of your setup.py:: ... def configuration(parent_package='', top_path=None): ... - config.add_data_dir('tests') + config.add_subpackage('tests') return config ... Now you can do the following to test your module:: - >>> import scipy - >>> scipy.xxx.test() + >>> import numpy + >>> numpy.xxx.test() -Also, when invoking the entire SciPy test suite, your tests will be +Also, when invoking the entire NumPy test suite, your tests will be found and run:: - >>> import scipy - >>> scipy.test() + >>> import numpy + >>> numpy.test() # your tests are included and run automatically! Tips & Tricks @@ -306,16 +299,16 @@ minor variations, it can be helpful to create a base class containing all the common tests, and then create a subclass for each variation. Several examples of this technique exist in NumPy; below are excerpts from one in `numpy/linalg/tests/test_linalg.py -`__:: +`__:: class LinalgTestCase: def test_single(self): - a = array([[1.,2.], [3.,4.]], dtype=single) + a = array([[1., 2.], [3., 4.]], dtype=single) b = array([2., 1.], dtype=single) self.do(a, b) def test_double(self): - a = array([[1.,2.], [3.,4.]], dtype=double) + a = array([[1., 2.], [3., 4.]], dtype=double) b = array([2., 1.], dtype=double) self.do(a, b) @@ -324,14 +317,14 @@ from one in `numpy/linalg/tests/test_linalg.py class TestSolve(LinalgTestCase): def do(self, a, b): x = linalg.solve(a, b) - assert_almost_equal(b, dot(a, x)) - assert_(imply(isinstance(b, matrix), isinstance(x, matrix))) + assert_allclose(b, dot(a, x)) + assert imply(isinstance(b, matrix), isinstance(x, matrix)) class TestInv(LinalgTestCase): def do(self, a, b): a_inv = linalg.inv(a) - assert_almost_equal(dot(a, a_inv), identity(asarray(a).shape[0])) - assert_(imply(isinstance(a, matrix), isinstance(a_inv, matrix))) + assert_allclose(dot(a, a_inv), identity(asarray(a).shape[0])) + assert imply(isinstance(a, matrix), isinstance(a_inv, matrix)) In this case, we wanted to test solving a linear algebra problem using matrices of several data types, using ``linalg.solve`` and @@ -344,35 +337,33 @@ Known failures & skipping tests Sometimes you might want to skip a test or mark it as a known failure, such as when the test suite is being written before the code it's meant to test, or if a test only fails on a particular architecture. -The decorators from numpy.testing.dec can be used to do this. To skip a test, simply use ``skipif``:: - from numpy.testing import dec + import pytest - @dec.skipif(SkipMyTest, "Skipping this test because...") + @pytest.mark.skipif(SkipMyTest, reason="Skipping this test because...") def test_something(foo): ... The test is marked as skipped if ``SkipMyTest`` evaluates to nonzero, and the message in verbose test output is the second argument given to ``skipif``. Similarly, a test can be marked as a known failure by -using ``knownfailureif``:: +using ``xfail``:: - from numpy.testing import dec + import pytest - @dec.knownfailureif(MyTestFails, "This test is known to fail because...") + @pytest.mark.xfail(MyTestFails, reason="This test is known to fail because...") def test_something_else(foo): ... Of course, a test can be unconditionally skipped or marked as a known -failure by passing ``True`` as the first argument to ``skipif`` or -``knownfailureif``, respectively. +failure by using ``skip`` or ``xfail`` without argument, respectively. A total of the number of skipped and known failing tests is displayed at the end of the test run. Skipped tests are marked as ``'S'`` in the test results (or ``'SKIPPED'`` for ``verbose > 1``), and known -failing tests are marked as ``'K'`` (or ``'KNOWN'`` if ``verbose > +failing tests are marked as ``'x'`` (or ``'XFAIL'`` if ``verbose > 1``). Tests on random data @@ -384,3 +375,25 @@ occasionally with no code changes is not helpful. Make the random data deterministic by setting the random number seed before generating it. Use either Python's ``random.seed(some_number)`` or NumPy's ``numpy.random.seed(some_number)``, depending on the source of random numbers. + +Alternatively, you can use `Hypothesis`_ to generate arbitrary data. +Hypothesis manages both Python's and Numpy's random seeds for you, and +provides a very concise and powerful way to describe data (including +``hypothesis.extra.numpy``, e.g. for a set of mutually-broadcastable shapes). + +The advantages over random generation include tools to replay and share +failures without requiring a fixed seed, reporting *minimal* examples for +each failure, and better-than-naive-random techniques for triggering bugs. + + +Documentation for ``numpy.test`` +-------------------------------- + +.. autofunction:: numpy.test + +.. _nose: https://nose.readthedocs.io/en/latest/ +.. _pytest: https://pytest.readthedocs.io +.. _parameterization: https://docs.pytest.org/en/latest/parametrize.html +.. _Hypothesis: https://hypothesis.readthedocs.io/en/latest/ +.. _vscode: https://code.visualstudio.com/docs/python/testing#_enable-a-test-framework +.. _pycharm: https://www.jetbrains.com/help/pycharm/testing-your-first-python-application.html diff --git a/doc/cdoc/Doxyfile b/doc/cdoc/Doxyfile deleted file mode 100644 index d80e985587b9..000000000000 --- a/doc/cdoc/Doxyfile +++ /dev/null @@ -1,1571 +0,0 @@ -# Doxyfile 1.6.3 - -# This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project -# -# All text after a hash (#) is considered a comment and will be ignored -# The format is: -# TAG = value [value, ...] -# For lists items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (" ") - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- - -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all -# text before the first occurrence of this tag. Doxygen uses libiconv (or the -# iconv built into libc) for the transcoding. See -# http://www.gnu.org/software/libiconv for the list of possible encodings. - -DOXYFILE_ENCODING = UTF-8 - -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded -# by quotes) that should identify the project. - -PROJECT_NAME = numpy - -# The PROJECT_NUMBER tag can be used to enter a project or revision number. -# This could be handy for archiving the generated documentation or -# if some version control system is used. - -PROJECT_NUMBER = 2.0.0 - -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) -# base path where the generated documentation will be put. -# If a relative path is entered, it will be relative to the location -# where doxygen was started. If left blank the current directory will be used. - -OUTPUT_DIRECTORY = build - -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create -# 4096 sub-directories (in 2 levels) under the output directory of each output -# format and will distribute the generated files over these directories. -# Enabling this option can be useful when feeding doxygen a huge amount of -# source files, where putting all generated files in the same directory would -# otherwise cause performance problems for the file system. - -CREATE_SUBDIRS = NO - -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# The default language is English, other supported languages are: -# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, -# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, -# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English -# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, -# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, -# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. - -OUTPUT_LANGUAGE = English - -# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will -# include brief member descriptions after the members that are listed in -# the file and class documentation (similar to JavaDoc). -# Set to NO to disable this. - -BRIEF_MEMBER_DESC = YES - -# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend -# the brief description of a member or function before the detailed description. -# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the -# brief descriptions will be completely suppressed. - -REPEAT_BRIEF = YES - -# This tag implements a quasi-intelligent brief description abbreviator -# that is used to form the text in various listings. Each string -# in this list, if found as the leading text of the brief description, will be -# stripped from the text and the result after processing the whole list, is -# used as the annotated text. Otherwise, the brief description is used as-is. -# If left blank, the following values are used ("$name" is automatically -# replaced with the name of the entity): "The $name class" "The $name widget" -# "The $name file" "is" "provides" "specifies" "contains" -# "represents" "a" "an" "the" - -ABBREVIATE_BRIEF = "The $name class" \ - "The $name widget" \ - "The $name file" \ - is \ - provides \ - specifies \ - contains \ - represents \ - a \ - an \ - the - -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# Doxygen will generate a detailed section even if there is only a brief -# description. - -ALWAYS_DETAILED_SEC = NO - -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment -# operators of the base classes will not be shown. - -INLINE_INHERITED_MEMB = NO - -# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full -# path before files name in the file list and in the header files. If set -# to NO the shortest path that makes the file name unique will be used. - -FULL_PATH_NAMES = YES - -# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag -# can be used to strip a user-defined part of the path. Stripping is -# only done if one of the specified strings matches the left-hand part of -# the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the -# path to strip. - -STRIP_FROM_PATH = ../../numpy/core - -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of -# the path mentioned in the documentation of a class, which tells -# the reader which header file to include in order to use a class. -# If left blank only the name of the header file containing the class -# definition is used. Otherwise one should specify the include paths that -# are normally passed to the compiler using the -I flag. - -STRIP_FROM_INC_PATH = - -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter -# (but less readable) file names. This can be useful is your file systems -# doesn't support long names like on DOS, Mac, or CD-ROM. - -SHORT_NAMES = NO - -# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen -# will interpret the first line (until the first dot) of a JavaDoc-style -# comment as the brief description. If set to NO, the JavaDoc -# comments will behave just like regular Qt-style comments -# (thus requiring an explicit @brief command for a brief description.) - -JAVADOC_AUTOBRIEF = NO - -# If the QT_AUTOBRIEF tag is set to YES then Doxygen will -# interpret the first line (until the first dot) of a Qt-style -# comment as the brief description. If set to NO, the comments -# will behave just like regular Qt-style comments (thus requiring -# an explicit \brief command for a brief description.) - -QT_AUTOBRIEF = NO - -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen -# treat a multi-line C++ special comment block (i.e. a block of //! or /// -# comments) as a brief description. This used to be the default behaviour. -# The new default is to treat a multi-line C++ comment block as a detailed -# description. Set this tag to YES if you prefer the old behaviour instead. - -MULTILINE_CPP_IS_BRIEF = NO - -# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented -# member inherits the documentation from any documented member that it -# re-implements. - -INHERIT_DOCS = YES - -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce -# a new page for each member. If set to NO, the documentation of a member will -# be part of the file/class/namespace that contains it. - -SEPARATE_MEMBER_PAGES = NO - -# The TAB_SIZE tag can be used to set the number of spaces in a tab. -# Doxygen uses this value to replace tabs by spaces in code fragments. - -TAB_SIZE = 8 - -# This tag can be used to specify a number of aliases that acts -# as commands in the documentation. An alias has the form "name=value". -# For example adding "sideeffect=\par Side Effects:\n" will allow you to -# put the command \sideeffect (or @sideeffect) in the documentation, which -# will result in a user-defined paragraph with heading "Side Effects:". -# You can put \n's in the value part of an alias to insert newlines. - -ALIASES = - -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C -# sources only. Doxygen will then generate output that is more tailored for C. -# For instance, some of the names that are used will be different. The list -# of all members will be omitted, etc. - -OPTIMIZE_OUTPUT_FOR_C = YES - -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java -# sources only. Doxygen will then generate output that is more tailored for -# Java. For instance, namespaces will be presented as packages, qualified -# scopes will look different, etc. - -OPTIMIZE_OUTPUT_JAVA = NO - -# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran -# sources only. Doxygen will then generate output that is more tailored for -# Fortran. - -OPTIMIZE_FOR_FORTRAN = NO - -# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL -# sources. Doxygen will then generate output that is tailored for -# VHDL. - -OPTIMIZE_OUTPUT_VHDL = NO - -# Doxygen selects the parser to use depending on the extension of the files it parses. -# With this tag you can assign which parser to use for a given extension. -# Doxygen has a built-in mapping, but you can override or extend it using this tag. -# The format is ext=language, where ext is a file extension, and language is one of -# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, -# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat -# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran), -# use: inc=Fortran f=C. Note that for custom extensions you also need to set -# FILE_PATTERNS otherwise the files are not read by doxygen. - -EXTENSION_MAPPING = - -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want -# to include (a tag file for) the STL sources as input, then you should -# set this tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. -# func(std::string) {}). This also make the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. - -BUILTIN_STL_SUPPORT = NO - -# If you use Microsoft's C++/CLI language, you should set this option to YES to -# enable parsing support. - -CPP_CLI_SUPPORT = NO - -# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. -# Doxygen will parse them like normal C++ but will assume all classes use public -# instead of private inheritance when no explicit protection keyword is present. - -SIP_SUPPORT = NO - -# For Microsoft's IDL there are propget and propput attributes to indicate getter -# and setter methods for a property. Setting this option to YES (the default) -# will make doxygen to replace the get and set methods by a property in the -# documentation. This will only work if the methods are indeed getting or -# setting a simple type. If this is not the case, or you want to show the -# methods anyway, you should set this option to NO. - -IDL_PROPERTY_SUPPORT = YES - -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default -# all members of a group must be documented explicitly. - -DISTRIBUTE_GROUP_DOC = NO - -# Set the SUBGROUPING tag to YES (the default) to allow class member groups of -# the same type (for instance a group of public functions) to be put as a -# subgroup of that type (e.g. under the Public Functions section). Set it to -# NO to prevent subgrouping. Alternatively, this can be done per class using -# the \nosubgrouping command. - -SUBGROUPING = YES - -# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum -# is documented as struct, union, or enum with the name of the typedef. So -# typedef struct TypeS {} TypeT, will appear in the documentation as a struct -# with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically -# be useful for C code in case the coding convention dictates that all compound -# types are typedef'ed and only the typedef is referenced, never the tag name. - -TYPEDEF_HIDES_STRUCT = NO - -# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to -# determine which symbols to keep in memory and which to flush to disk. -# When the cache is full, less often used symbols will be written to disk. -# For small to medium size projects (<1000 input files) the default value is -# probably good enough. For larger projects a too small cache size can cause -# doxygen to be busy swapping symbols to and from disk most of the time -# causing a significant performance penalty. -# If the system has enough physical memory increasing the cache will improve the -# performance by keeping more symbols in memory. Note that the value works on -# a logarithmic scale so increasing the size by one will roughly double the -# memory usage. The cache size is given by this formula: -# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, -# corresponding to a cache size of 2^16 = 65536 symbols - -SYMBOL_CACHE_SIZE = 0 - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. -# Private class members and static file members will be hidden unless -# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES - -EXTRACT_ALL = YES - -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class -# will be included in the documentation. - -EXTRACT_PRIVATE = YES - -# If the EXTRACT_STATIC tag is set to YES all static members of a file -# will be included in the documentation. - -EXTRACT_STATIC = YES - -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) -# defined locally in source files will be included in the documentation. -# If set to NO only classes defined in header files are included. - -EXTRACT_LOCAL_CLASSES = YES - -# This flag is only useful for Objective-C code. When set to YES local -# methods, which are defined in the implementation section but not in -# the interface are included in the documentation. -# If set to NO (the default) only methods in the interface are included. - -EXTRACT_LOCAL_METHODS = NO - -# If this flag is set to YES, the members of anonymous namespaces will be -# extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base -# name of the file that contains the anonymous namespace. By default -# anonymous namespace are hidden. - -EXTRACT_ANON_NSPACES = NO - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all -# undocumented members of documented classes, files or namespaces. -# If set to NO (the default) these members will be included in the -# various overviews, but no documentation section is generated. -# This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_MEMBERS = NO - -# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. -# If set to NO (the default) these classes will be included in the various -# overviews. This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_CLASSES = NO - -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all -# friend (class|struct|union) declarations. -# If set to NO (the default) these declarations will be included in the -# documentation. - -HIDE_FRIEND_COMPOUNDS = NO - -# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any -# documentation blocks found inside the body of a function. -# If set to NO (the default) these blocks will be appended to the -# function's detailed documentation block. - -HIDE_IN_BODY_DOCS = NO - -# The INTERNAL_DOCS tag determines if documentation -# that is typed after a \internal command is included. If the tag is set -# to NO (the default) then the documentation will be excluded. -# Set it to YES to include the internal documentation. - -INTERNAL_DOCS = NO - -# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate -# file names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. - -CASE_SENSE_NAMES = NO - -# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen -# will show members with their full class and namespace scopes in the -# documentation. If set to YES the scope will be hidden. - -HIDE_SCOPE_NAMES = NO - -# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen -# will put a list of the files that are included by a file in the documentation -# of that file. - -SHOW_INCLUDE_FILES = YES - -# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen -# will list include files with double quotes in the documentation -# rather than with sharp brackets. - -FORCE_LOCAL_INCLUDES = NO - -# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] -# is inserted in the documentation for inline members. - -INLINE_INFO = YES - -# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen -# will sort the (detailed) documentation of file and class members -# alphabetically by member name. If set to NO the members will appear in -# declaration order. - -SORT_MEMBER_DOCS = YES - -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the -# brief documentation of file, namespace and class members alphabetically -# by member name. If set to NO (the default) the members will appear in -# declaration order. - -SORT_BRIEF_DOCS = NO - -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen -# will sort the (brief and detailed) documentation of class members so that -# constructors and destructors are listed first. If set to NO (the default) -# the constructors will appear in the respective orders defined by -# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. -# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO -# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. - -SORT_MEMBERS_CTORS_1ST = NO - -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the -# hierarchy of group names into alphabetical order. If set to NO (the default) -# the group names will appear in their defined order. - -SORT_GROUP_NAMES = NO - -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be -# sorted by fully-qualified names, including namespaces. If set to -# NO (the default), the class list will be sorted only by class name, -# not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the -# alphabetical list. - -SORT_BY_SCOPE_NAME = NO - -# The GENERATE_TODOLIST tag can be used to enable (YES) or -# disable (NO) the todo list. This list is created by putting \todo -# commands in the documentation. - -GENERATE_TODOLIST = YES - -# The GENERATE_TESTLIST tag can be used to enable (YES) or -# disable (NO) the test list. This list is created by putting \test -# commands in the documentation. - -GENERATE_TESTLIST = YES - -# The GENERATE_BUGLIST tag can be used to enable (YES) or -# disable (NO) the bug list. This list is created by putting \bug -# commands in the documentation. - -GENERATE_BUGLIST = YES - -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or -# disable (NO) the deprecated list. This list is created by putting -# \deprecated commands in the documentation. - -GENERATE_DEPRECATEDLIST= YES - -# The ENABLED_SECTIONS tag can be used to enable conditional -# documentation sections, marked by \if sectionname ... \endif. - -ENABLED_SECTIONS = - -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines -# the initial value of a variable or define consists of for it to appear in -# the documentation. If the initializer consists of more lines than specified -# here it will be hidden. Use a value of 0 to hide initializers completely. -# The appearance of the initializer of individual variables and defines in the -# documentation can be controlled using \showinitializer or \hideinitializer -# command in the documentation regardless of this setting. - -MAX_INITIALIZER_LINES = 30 - -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated -# at the bottom of the documentation of classes and structs. If set to YES the -# list will mention the files that were used to generate the documentation. - -SHOW_USED_FILES = YES - -# If the sources in your project are distributed over multiple directories -# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy -# in the documentation. The default is NO. - -SHOW_DIRECTORIES = NO - -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. -# This will remove the Files entry from the Quick Index and from the -# Folder Tree View (if specified). The default is YES. - -SHOW_FILES = YES - -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the -# Namespaces page. This will remove the Namespaces entry from the Quick Index -# and from the Folder Tree View (if specified). The default is YES. - -SHOW_NAMESPACES = YES - -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from -# the version control system). Doxygen will invoke the program by executing (via -# popen()) the command , where is the value of -# the FILE_VERSION_FILTER tag, and is the name of an input file -# provided by doxygen. Whatever the program writes to standard output -# is used as the file version. See the manual for examples. - -FILE_VERSION_FILTER = - -# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by -# doxygen. The layout file controls the global structure of the generated output files -# in an output format independent way. The create the layout file that represents -# doxygen's defaults, run doxygen with the -l option. You can optionally specify a -# file name after the option, if omitted DoxygenLayout.xml will be used as the name -# of the layout file. - -LAYOUT_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -# The QUIET tag can be used to turn on/off the messages that are generated -# by doxygen. Possible values are YES and NO. If left blank NO is used. - -QUIET = NO - -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated by doxygen. Possible values are YES and NO. If left blank -# NO is used. - -WARNINGS = YES - -# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings -# for undocumented members. If EXTRACT_ALL is set to YES then this flag will -# automatically be disabled. - -WARN_IF_UNDOCUMENTED = YES - -# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some -# parameters in a documented function, or documenting parameters that -# don't exist or using markup commands wrongly. - -WARN_IF_DOC_ERROR = YES - -# This WARN_NO_PARAMDOC option can be abled to get warnings for -# functions that are documented, but have no documentation for their parameters -# or return value. If set to NO (the default) doxygen will only warn about -# wrong or incomplete parameter documentation, but not about the absence of -# documentation. - -WARN_NO_PARAMDOC = NO - -# The WARN_FORMAT tag determines the format of the warning messages that -# doxygen can produce. The string should contain the $file, $line, and $text -# tags, which will be replaced by the file and line number from which the -# warning originated and the warning text. Optionally the format may contain -# $version, which will be replaced by the version of the file (if it could -# be obtained via FILE_VERSION_FILTER) - -WARN_FORMAT = "$file:$line: $text" - -# The WARN_LOGFILE tag can be used to specify a file to which warning -# and error messages should be written. If left blank the output is written -# to stderr. - -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag can be used to specify the files and/or directories that contain -# documented source files. You may enter file names like "myfile.cpp" or -# directories like "/usr/src/myproject". Separate the files or directories -# with spaces. - -INPUT = ../../numpy/core/src \ - ../../numpy/core/include - -# This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is -# also the default input encoding. Doxygen uses libiconv (or the iconv built -# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for -# the list of possible encodings. - -INPUT_ENCODING = UTF-8 - -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank the following patterns are tested: -# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx -# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 - -FILE_PATTERNS = *.h *.c *.src - -# The RECURSIVE tag can be used to turn specify whether or not subdirectories -# should be searched for input files as well. Possible values are YES and NO. -# If left blank NO is used. - -RECURSIVE = YES - -# The EXCLUDE tag can be used to specify files and/or directories that should -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. - -EXCLUDE = - -# The EXCLUDE_SYMLINKS tag can be used select whether or not files or -# directories that are symbolic links (a Unix filesystem feature) are excluded -# from the input. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. Note that the wildcards are matched -# against the file with absolute path, so to exclude all test directories -# for example use the pattern */test/* - -EXCLUDE_PATTERNS = - -# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names -# (namespaces, classes, functions, etc.) that should be excluded from the -# output. The symbol name can be a fully qualified name, a word, or if the -# wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test - -EXCLUDE_SYMBOLS = - -# The EXAMPLE_PATH tag can be used to specify one or more files or -# directories that contain example code fragments that are included (see -# the \include command). - -EXAMPLE_PATH = - -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank all files are included. - -EXAMPLE_PATTERNS = * - -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude -# commands irrespective of the value of the RECURSIVE tag. -# Possible values are YES and NO. If left blank NO is used. - -EXAMPLE_RECURSIVE = NO - -# The IMAGE_PATH tag can be used to specify one or more files or -# directories that contain image that are included in the documentation (see -# the \image command). - -IMAGE_PATH = - -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command , where -# is the value of the INPUT_FILTER tag, and is the name of an -# input file. Doxygen will then use the output that the filter program writes -# to standard output. If FILTER_PATTERNS is specified, this tag will be -# ignored. - -INPUT_FILTER = ./numpyfilter.py - -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. The filters are a list of the form: -# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further -# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER -# is applied to all files. - -FILTER_PATTERNS = - -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will be used to filter the input files when producing source -# files to browse (i.e. when SOURCE_BROWSER is set to YES). - -FILTER_SOURCE_FILES = NO - -#--------------------------------------------------------------------------- -# configuration options related to source browsing -#--------------------------------------------------------------------------- - -# If the SOURCE_BROWSER tag is set to YES then a list of source files will -# be generated. Documented entities will be cross-referenced with these sources. -# Note: To get rid of all source code in the generated output, make sure also -# VERBATIM_HEADERS is set to NO. - -SOURCE_BROWSER = NO - -# Setting the INLINE_SOURCES tag to YES will include the body -# of functions and classes directly in the documentation. - -INLINE_SOURCES = NO - -# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct -# doxygen to hide any special comment blocks from generated source code -# fragments. Normal C and C++ comments will always remain visible. - -STRIP_CODE_COMMENTS = YES - -# If the REFERENCED_BY_RELATION tag is set to YES -# then for each documented function all documented -# functions referencing it will be listed. - -REFERENCED_BY_RELATION = YES - -# If the REFERENCES_RELATION tag is set to YES -# then for each documented function all documented entities -# called/used by that function will be listed. - -REFERENCES_RELATION = YES - -# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) -# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from -# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will -# link to the source code. Otherwise they will link to the documentation. - -REFERENCES_LINK_SOURCE = YES - -# If the USE_HTAGS tag is set to YES then the references to source code -# will point to the HTML generated by the htags(1) tool instead of doxygen -# built-in source browser. The htags tool is part of GNU's global source -# tagging system (see http://www.gnu.org/software/global/global.html). You -# will need version 4.8.6 or higher. - -USE_HTAGS = NO - -# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen -# will generate a verbatim copy of the header file for each class for -# which an include is specified. Set to NO to disable this. - -VERBATIM_HEADERS = YES - -#--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index -# of all compounds will be generated. Enable this if the project -# contains a lot of classes, structs, unions or interfaces. - -ALPHABETICAL_INDEX = NO - -# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then -# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns -# in which this list will be split (can be a number in the range [1..20]) - -COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all -# classes will be put under the same header in the alphabetical index. -# The IGNORE_PREFIX tag can be used to specify one or more prefixes that -# should be ignored while generating the index headers. - -IGNORE_PREFIX = - -#--------------------------------------------------------------------------- -# configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES (the default) Doxygen will -# generate HTML output. - -GENERATE_HTML = YES - -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `html' will be used as the default path. - -HTML_OUTPUT = html - -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for -# each generated HTML page (for example: .htm,.php,.asp). If it is left blank -# doxygen will generate files with .html extension. - -HTML_FILE_EXTENSION = .html - -# The HTML_HEADER tag can be used to specify a personal HTML header for -# each generated HTML page. If it is left blank doxygen will generate a -# standard header. - -HTML_HEADER = - -# The HTML_FOOTER tag can be used to specify a personal HTML footer for -# each generated HTML page. If it is left blank doxygen will generate a -# standard footer. - -HTML_FOOTER = - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading -# style sheet that is used by each HTML page. It can be used to -# fine-tune the look of the HTML output. If the tag is left blank doxygen -# will generate a default style sheet. Note that doxygen will try to copy -# the style sheet file to the HTML output directory, so don't put your own -# stylesheet in the HTML output directory as well, or it will be erased! - -HTML_STYLESHEET = - -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting -# this to NO can help when comparing the output of multiple runs. - -HTML_TIMESTAMP = YES - -# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, -# files or namespaces will be aligned in HTML using tables. If set to -# NO a bullet list will be used. - -HTML_ALIGN_MEMBERS = YES - -# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML -# documentation will contain sections that can be hidden and shown after the -# page has loaded. For this to work a browser that supports -# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox -# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). - -HTML_DYNAMIC_SECTIONS = NO - -# If the GENERATE_DOCSET tag is set to YES, additional index files -# will be generated that can be used as input for Apple's Xcode 3 -# integrated development environment, introduced with OSX 10.5 (Leopard). -# To create a documentation set, doxygen will generate a Makefile in the -# HTML output directory. Running make will produce the docset in that -# directory and running "make install" will install the docset in -# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find -# it at startup. -# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information. - -GENERATE_DOCSET = NO - -# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the -# feed. A documentation feed provides an umbrella under which multiple -# documentation sets from a single provider (such as a company or product suite) -# can be grouped. - -DOCSET_FEEDNAME = "Doxygen generated docs" - -# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that -# should uniquely identify the documentation set bundle. This should be a -# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen -# will append .docset to the name. - -DOCSET_BUNDLE_ID = org.doxygen.Project - -# If the GENERATE_HTMLHELP tag is set to YES, additional index files -# will be generated that can be used as input for tools like the -# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) -# of the generated HTML documentation. - -GENERATE_HTMLHELP = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can -# be used to specify the file name of the resulting .chm file. You -# can add a path in front of the file if the result should not be -# written to the html output directory. - -CHM_FILE = - -# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can -# be used to specify the location (absolute path including file name) of -# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run -# the HTML help compiler on the generated index.hhp. - -HHC_LOCATION = - -# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag -# controls if a separate .chi index file is generated (YES) or that -# it should be included in the master .chm file (NO). - -GENERATE_CHI = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING -# is used to encode HtmlHelp index (hhk), content (hhc) and project file -# content. - -CHM_INDEX_ENCODING = - -# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag -# controls whether a binary table of contents is generated (YES) or a -# normal table of contents (NO) in the .chm file. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members -# to the contents of the HTML help documentation and to the tree view. - -TOC_EXPAND = NO - -# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER -# are set, an additional index file will be generated that can be used as input for -# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated -# HTML documentation. - -GENERATE_QHP = NO - -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can -# be used to specify the file name of the resulting .qch file. -# The path specified is relative to the HTML output folder. - -QCH_FILE = - -# The QHP_NAMESPACE tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#namespace - -QHP_NAMESPACE = org.doxygen.Project - -# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#virtual-folders - -QHP_VIRTUAL_FOLDER = doc - -# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. -# For more information please see -# http://doc.trolltech.com/qthelpproject.html#custom-filters - -QHP_CUST_FILTER_NAME = - -# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see -# Qt Help Project / Custom Filters. - -QHP_CUST_FILTER_ATTRS = - -# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's -# filter section matches. -# Qt Help Project / Filter Attributes. - -QHP_SECT_FILTER_ATTRS = - -# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can -# be used to specify the location of Qt's qhelpgenerator. -# If non-empty doxygen will try to run qhelpgenerator on the generated -# .qhp file. - -QHG_LOCATION = - -# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files -# will be generated, which together with the HTML files, form an Eclipse help -# plugin. To install this plugin and make it available under the help contents -# menu in Eclipse, the contents of the directory containing the HTML and XML -# files needs to be copied into the plugins directory of eclipse. The name of -# the directory within the plugins directory should be the same as -# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before -# the help appears. - -GENERATE_ECLIPSEHELP = NO - -# A unique identifier for the eclipse help plugin. When installing the plugin -# the directory name containing the HTML and XML files should also have -# this name. - -ECLIPSE_DOC_ID = org.doxygen.Project - -# The DISABLE_INDEX tag can be used to turn on/off the condensed index at -# top of each HTML page. The value NO (the default) enables the index and -# the value YES disables it. - -DISABLE_INDEX = NO - -# This tag can be used to set the number of enum values (range [1..20]) -# that doxygen will group on one line in the generated HTML documentation. - -ENUM_VALUES_PER_LINE = 4 - -# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. -# If the tag value is set to YES, a side panel will be generated -# containing a tree-like index structure (just like the one that -# is generated for HTML Help). For this to work a browser that supports -# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). -# Windows users are probably better off using the HTML help feature. - -GENERATE_TREEVIEW = YES - -# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, -# and Class Hierarchy pages using a tree view instead of an ordered list. - -USE_INLINE_TREES = NO - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be -# used to set the initial width (in pixels) of the frame in which the tree -# is shown. - -TREEVIEW_WIDTH = 250 - -# Use this tag to change the font size of Latex formulas included -# as images in the HTML documentation. The default is 10. Note that -# when you change the font size after a successful doxygen run you need -# to manually remove any form_*.png images from the HTML output directory -# to force them to be regenerated. - -FORMULA_FONTSIZE = 10 - -# When the SEARCHENGINE tag is enabled doxygen will generate a search box -# for the HTML output. The underlying search engine uses javascript -# and DHTML and should work on any modern browser. Note that when using -# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets -# (GENERATE_DOCSET) there is already a search function so this one should -# typically be disabled. For large projects the javascript based search engine -# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. - -SEARCHENGINE = NO - -# When the SERVER_BASED_SEARCH tag is enabled the search engine will be -# implemented using a PHP enabled web server instead of at the web client -# using Javascript. Doxygen will generate the search PHP script and index -# file to put on the web server. The advantage of the server -# based approach is that it scales better to large projects and allows -# full text search. The disadvances is that it is more difficult to setup -# and does not have live searching capabilities. - -SERVER_BASED_SEARCH = NO - -#--------------------------------------------------------------------------- -# configuration options related to the LaTeX output -#--------------------------------------------------------------------------- - -# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will -# generate Latex output. - -GENERATE_LATEX = NO - -# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `latex' will be used as the default path. - -LATEX_OUTPUT = latex - -# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be -# invoked. If left blank `latex' will be used as the default command name. -# Note that when enabling USE_PDFLATEX this option is only used for -# generating bitmaps for formulas in the HTML output, but not in the -# Makefile that is written to the output directory. - -LATEX_CMD_NAME = latex - -# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to -# generate index for LaTeX. If left blank `makeindex' will be used as the -# default command name. - -MAKEINDEX_CMD_NAME = makeindex - -# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact -# LaTeX documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_LATEX = NO - -# The PAPER_TYPE tag can be used to set the paper type that is used -# by the printer. Possible values are: a4, a4wide, letter, legal and -# executive. If left blank a4wide will be used. - -PAPER_TYPE = a4wide - -# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX -# packages that should be included in the LaTeX output. - -EXTRA_PACKAGES = - -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for -# the generated latex document. The header should contain everything until -# the first chapter. If it is left blank doxygen will generate a -# standard header. Notice: only use this tag if you know what you are doing! - -LATEX_HEADER = - -# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated -# is prepared for conversion to pdf (using ps2pdf). The pdf file will -# contain links (just like the HTML output) instead of page references -# This makes the output suitable for online browsing using a pdf viewer. - -PDF_HYPERLINKS = YES - -# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of -# plain latex in the generated Makefile. Set this option to YES to get a -# higher quality PDF documentation. - -USE_PDFLATEX = YES - -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. -# command to the generated LaTeX files. This will instruct LaTeX to keep -# running if errors occur, instead of asking the user for help. -# This option is also used when generating formulas in HTML. - -LATEX_BATCHMODE = NO - -# If LATEX_HIDE_INDICES is set to YES then doxygen will not -# include the index chapters (such as File Index, Compound Index, etc.) -# in the output. - -LATEX_HIDE_INDICES = NO - -# If LATEX_SOURCE_CODE is set to YES then doxygen will include -# source code with syntax highlighting in the LaTeX output. -# Note that which sources are shown also depends on other settings -# such as SOURCE_BROWSER. - -LATEX_SOURCE_CODE = NO - -#--------------------------------------------------------------------------- -# configuration options related to the RTF output -#--------------------------------------------------------------------------- - -# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output -# The RTF output is optimized for Word 97 and may not look very pretty with -# other RTF readers or editors. - -GENERATE_RTF = NO - -# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `rtf' will be used as the default path. - -RTF_OUTPUT = rtf - -# If the COMPACT_RTF tag is set to YES Doxygen generates more compact -# RTF documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_RTF = NO - -# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated -# will contain hyperlink fields. The RTF file will -# contain links (just like the HTML output) instead of page references. -# This makes the output suitable for online browsing using WORD or other -# programs which support those fields. -# Note: wordpad (write) and others do not support links. - -RTF_HYPERLINKS = NO - -# Load stylesheet definitions from file. Syntax is similar to doxygen's -# config file, i.e. a series of assignments. You only have to provide -# replacements, missing definitions are set to their default value. - -RTF_STYLESHEET_FILE = - -# Set optional variables used in the generation of an rtf document. -# Syntax is similar to doxygen's config file. - -RTF_EXTENSIONS_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to the man page output -#--------------------------------------------------------------------------- - -# If the GENERATE_MAN tag is set to YES (the default) Doxygen will -# generate man pages - -GENERATE_MAN = NO - -# The MAN_OUTPUT tag is used to specify where the man pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `man' will be used as the default path. - -MAN_OUTPUT = man - -# The MAN_EXTENSION tag determines the extension that is added to -# the generated man pages (default is the subroutine's section .3) - -MAN_EXTENSION = .3 - -# If the MAN_LINKS tag is set to YES and Doxygen generates man output, -# then it will generate one additional man file for each entity -# documented in the real man page(s). These additional files -# only source the real man page, but without them the man command -# would be unable to find the correct page. The default is NO. - -MAN_LINKS = NO - -#--------------------------------------------------------------------------- -# configuration options related to the XML output -#--------------------------------------------------------------------------- - -# If the GENERATE_XML tag is set to YES Doxygen will -# generate an XML file that captures the structure of -# the code including all documentation. - -GENERATE_XML = NO - -# The XML_OUTPUT tag is used to specify where the XML pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `xml' will be used as the default path. - -XML_OUTPUT = xml - -# The XML_SCHEMA tag can be used to specify an XML schema, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_SCHEMA = - -# The XML_DTD tag can be used to specify an XML DTD, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_DTD = - -# If the XML_PROGRAMLISTING tag is set to YES Doxygen will -# dump the program listings (including syntax highlighting -# and cross-referencing information) to the XML output. Note that -# enabling this will significantly increase the size of the XML output. - -XML_PROGRAMLISTING = YES - -#--------------------------------------------------------------------------- -# configuration options for the AutoGen Definitions output -#--------------------------------------------------------------------------- - -# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will -# generate an AutoGen Definitions (see autogen.sf.net) file -# that captures the structure of the code including all -# documentation. Note that this feature is still experimental -# and incomplete at the moment. - -GENERATE_AUTOGEN_DEF = NO - -#--------------------------------------------------------------------------- -# configuration options related to the Perl module output -#--------------------------------------------------------------------------- - -# If the GENERATE_PERLMOD tag is set to YES Doxygen will -# generate a Perl module file that captures the structure of -# the code including all documentation. Note that this -# feature is still experimental and incomplete at the -# moment. - -GENERATE_PERLMOD = NO - -# If the PERLMOD_LATEX tag is set to YES Doxygen will generate -# the necessary Makefile rules, Perl scripts and LaTeX code to be able -# to generate PDF and DVI output from the Perl module output. - -PERLMOD_LATEX = NO - -# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be -# nicely formatted so it can be parsed by a human reader. This is useful -# if you want to understand what is going on. On the other hand, if this -# tag is set to NO the size of the Perl module output will be much smaller -# and Perl will parse it just the same. - -PERLMOD_PRETTY = YES - -# The names of the make variables in the generated doxyrules.make file -# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. -# This is useful so different doxyrules.make files included by the same -# Makefile don't overwrite each other's variables. - -PERLMOD_MAKEVAR_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the preprocessor -#--------------------------------------------------------------------------- - -# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will -# evaluate all C-preprocessor directives found in the sources and include -# files. - -ENABLE_PREPROCESSING = YES - -# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro -# names in the source code. If set to NO (the default) only conditional -# compilation will be performed. Macro expansion can be done in a controlled -# way by setting EXPAND_ONLY_PREDEF to YES. - -MACRO_EXPANSION = NO - -# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES -# then the macro expansion is limited to the macros specified with the -# PREDEFINED and EXPAND_AS_DEFINED tags. - -EXPAND_ONLY_PREDEF = NO - -# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files -# in the INCLUDE_PATH (see below) will be search if a #include is found. - -SEARCH_INCLUDES = YES - -# The INCLUDE_PATH tag can be used to specify one or more directories that -# contain include files that are not input files but should be processed by -# the preprocessor. - -INCLUDE_PATH = - -# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard -# patterns (like *.h and *.hpp) to filter out the header-files in the -# directories. If left blank, the patterns specified with FILE_PATTERNS will -# be used. - -INCLUDE_FILE_PATTERNS = - -# The PREDEFINED tag can be used to specify one or more macro names that -# are defined before the preprocessor is started (similar to the -D option of -# gcc). The argument of the tag is a list of macros of the form: name -# or name=definition (no spaces). If the definition and the = are -# omitted =1 is assumed. To prevent a macro definition from being -# undefined via #undef or recursively expanded use the := operator -# instead of the = operator. - -PREDEFINED = - -# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then -# this tag can be used to specify a list of macro names that should be expanded. -# The macro definition that is found in the sources will be used. -# Use the PREDEFINED tag if you want to use a different macro definition. - -EXPAND_AS_DEFINED = - -# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then -# doxygen's preprocessor will remove all function-like macros that are alone -# on a line, have an all uppercase name, and do not end with a semicolon. Such -# function macros are typically used for boiler-plate code, and will confuse -# the parser if not removed. - -SKIP_FUNCTION_MACROS = YES - -#--------------------------------------------------------------------------- -# Configuration::additions related to external references -#--------------------------------------------------------------------------- - -# The TAGFILES option can be used to specify one or more tagfiles. -# Optionally an initial location of the external documentation -# can be added for each tagfile. The format of a tag file without -# this location is as follows: -# TAGFILES = file1 file2 ... -# Adding location for the tag files is done as follows: -# TAGFILES = file1=loc1 "file2 = loc2" ... -# where "loc1" and "loc2" can be relative or absolute paths or -# URLs. If a location is present for each tag, the installdox tool -# does not have to be run to correct the links. -# Note that each tag file must have a unique name -# (where the name does NOT include the path) -# If a tag file is not located in the directory in which doxygen -# is run, you must also specify the path to the tagfile here. - -TAGFILES = - -# When a file name is specified after GENERATE_TAGFILE, doxygen will create -# a tag file that is based on the input files it reads. - -GENERATE_TAGFILE = - -# If the ALLEXTERNALS tag is set to YES all external classes will be listed -# in the class index. If set to NO only the inherited external classes -# will be listed. - -ALLEXTERNALS = NO - -# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed -# in the modules index. If set to NO, only the current project's groups will -# be listed. - -EXTERNAL_GROUPS = YES - -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of `which perl'). - -PERL_PATH = /usr/bin/perl - -#--------------------------------------------------------------------------- -# Configuration options related to the dot tool -#--------------------------------------------------------------------------- - -# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will -# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base -# or super classes. Setting the tag to NO turns the diagrams off. Note that -# this option is superseded by the HAVE_DOT option below. This is only a -# fallback. It is recommended to install and use dot, since it yields more -# powerful graphs. - -CLASS_DIAGRAMS = YES - -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see -# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the -# documentation. The MSCGEN_PATH tag allows you to specify the directory where -# the mscgen tool resides. If left empty the tool is assumed to be found in the -# default search path. - -MSCGEN_PATH = - -# If set to YES, the inheritance and collaboration graphs will hide -# inheritance and usage relations if the target is undocumented -# or is not a class. - -HIDE_UNDOC_RELATIONS = YES - -# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is -# available from the path. This tool is part of Graphviz, a graph visualization -# toolkit from AT&T and Lucent Bell Labs. The other options in this section -# have no effect if this option is set to NO (the default) - -HAVE_DOT = NO - -# By default doxygen will write a font called FreeSans.ttf to the output -# directory and reference it in all dot files that doxygen generates. This -# font does not include all possible unicode characters however, so when you need -# these (or just want a differently looking font) you can specify the font name -# using DOT_FONTNAME. You need need to make sure dot is able to find the font, -# which can be done by putting it in a standard location or by setting the -# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory -# containing the font. - -DOT_FONTNAME = FreeSans - -# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. -# The default size is 10pt. - -DOT_FONTSIZE = 10 - -# By default doxygen will tell dot to use the output directory to look for the -# FreeSans.ttf font (which doxygen will put there itself). If you specify a -# different font using DOT_FONTNAME you can set the path where dot -# can find it using this tag. - -DOT_FONTPATH = - -# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect inheritance relations. Setting this tag to YES will force the -# the CLASS_DIAGRAMS tag to NO. - -CLASS_GRAPH = YES - -# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect implementation dependencies (inheritance, containment, and -# class references variables) of the class with other documented classes. - -COLLABORATION_GRAPH = YES - -# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for groups, showing the direct groups dependencies - -GROUP_GRAPHS = YES - -# If the UML_LOOK tag is set to YES doxygen will generate inheritance and -# collaboration diagrams in a style similar to the OMG's Unified Modeling -# Language. - -UML_LOOK = NO - -# If set to YES, the inheritance and collaboration graphs will show the -# relations between templates and their instances. - -TEMPLATE_RELATIONS = NO - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT -# tags are set to YES then doxygen will generate a graph for each documented -# file showing the direct and indirect include dependencies of the file with -# other documented files. - -INCLUDE_GRAPH = YES - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and -# HAVE_DOT tags are set to YES then doxygen will generate a graph for each -# documented header file showing the documented files that directly or -# indirectly include this file. - -INCLUDED_BY_GRAPH = YES - -# If the CALL_GRAPH and HAVE_DOT options are set to YES then -# doxygen will generate a call dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable call graphs -# for selected functions only using the \callgraph command. - -CALL_GRAPH = NO - -# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then -# doxygen will generate a caller dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable caller -# graphs for selected functions only using the \callergraph command. - -CALLER_GRAPH = NO - -# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen -# will graphical hierarchy of all classes instead of a textual one. - -GRAPHICAL_HIERARCHY = YES - -# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES -# then doxygen will show the dependencies a directory has on other directories -# in a graphical way. The dependency relations are determined by the #include -# relations between the files in the directories. - -DIRECTORY_GRAPH = YES - -# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. Possible values are png, jpg, or gif -# If left blank png will be used. - -DOT_IMAGE_FORMAT = png - -# The tag DOT_PATH can be used to specify the path where the dot tool can be -# found. If left blank, it is assumed the dot tool can be found in the path. - -DOT_PATH = - -# The DOTFILE_DIRS tag can be used to specify one or more directories that -# contain dot files that are included in the documentation (see the -# \dotfile command). - -DOTFILE_DIRS = - -# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of -# nodes that will be shown in the graph. If the number of nodes in a graph -# becomes larger than this value, doxygen will truncate the graph, which is -# visualized by representing a node as a red box. Note that doxygen if the -# number of direct children of the root node in a graph is already larger than -# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note -# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. - -DOT_GRAPH_MAX_NODES = 50 - -# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the -# graphs generated by dot. A depth value of 3 means that only nodes reachable -# from the root by following a path via at most 3 edges will be shown. Nodes -# that lay further from the root node will be omitted. Note that setting this -# option to 1 or 2 may greatly reduce the computation time needed for large -# code bases. Also note that the size of a graph can be further restricted by -# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. - -MAX_DOT_GRAPH_DEPTH = 0 - -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, because dot on Windows does not -# seem to support this out of the box. Warning: Depending on the platform used, -# enabling this option may lead to badly anti-aliased labels on the edges of -# a graph (i.e. they become hard to read). - -DOT_TRANSPARENT = NO - -# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output -# files in one run (i.e. multiple -o and -T options on the command line). This -# makes dot run faster, but since only newer versions of dot (>1.8.10) -# support this, this feature is disabled by default. - -DOT_MULTI_TARGETS = NO - -# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will -# generate a legend page explaining the meaning of the various boxes and -# arrows in the dot generated graphs. - -GENERATE_LEGEND = YES - -# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will -# remove the intermediate dot files that are used to generate -# the various graphs. - -DOT_CLEANUP = YES diff --git a/doc/cdoc/Makefile b/doc/cdoc/Makefile deleted file mode 100644 index bc6225ec8ce5..000000000000 --- a/doc/cdoc/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -all: build - -build: - doxygen - -.PHONY: all build - diff --git a/doc/cdoc/README b/doc/cdoc/README deleted file mode 100644 index a5363cfa1b11..000000000000 --- a/doc/cdoc/README +++ /dev/null @@ -1,31 +0,0 @@ -cdoc -==== - -This is a simple Doxygen project for building NumPy C code documentation, -with docstrings extracted from the C sources themselves. - -The understood syntax for documentation in the C source is - - /* - * Some text in reStructuredText format - */ - int function_to_which_the_text_applies() - { - ... - } - - /* - * More text in reStructuredText format - */ - struct - { - int variable_1; /* Documentation for variable_1 */ - - /* - * Documentation for variable_2 - */ - int variable_2; - } struct_name_t; - -Please do not use JavaDoc or Doxygen-specific formatting at the moment. - diff --git a/doc/cdoc/numpyfilter.py b/doc/cdoc/numpyfilter.py deleted file mode 100755 index 614c50771b9e..000000000000 --- a/doc/cdoc/numpyfilter.py +++ /dev/null @@ -1,116 +0,0 @@ -#!/usr/bin/env python -""" -numpyfilter.py INPUTFILE - -Interpret C comments as ReStructuredText, and replace them by the HTML output. -Also, add Doxygen /** and /**< syntax automatically where appropriate. - -""" -from __future__ import division, absolute_import, print_function - -import sys -import re -import os -import textwrap -import optparse - -if sys.version_info[0] >= 3: - import pickle -else: - import cPickle as pickle - -CACHE_FILE = 'build/rst-cache.pck' - -def main(): - p = optparse.OptionParser(usage=__doc__.strip()) - options, args = p.parse_args() - - if len(args) != 1: - p.error("no input file given") - - comment_re = re.compile(r'(\n.*?)/\*(.*?)\*/', re.S) - - cache = load_cache() - - f = open(args[0], 'r') - try: - text = f.read() - text = comment_re.sub(lambda m: process_match(m, cache), text) - sys.stdout.write(text) - finally: - f.close() - save_cache(cache) - -def filter_comment(text): - if text.startswith('NUMPY_API'): - text = text[9:].strip() - if text.startswith('UFUNC_API'): - text = text[9:].strip() - - html = render_html(text) - return html - -def process_match(m, cache=None): - pre, rawtext = m.groups() - - preline = pre.split("\n")[-1] - - if cache is not None and rawtext in cache: - text = cache[rawtext] - else: - text = re.compile(r'^\s*\*', re.M).sub('', rawtext) - text = textwrap.dedent(text) - text = filter_comment(text) - - if cache is not None: - cache[rawtext] = text - - if preline.strip(): - return pre + "/**< " + text + " */" - else: - return pre + "/** " + text + " */" - -def load_cache(): - if os.path.exists(CACHE_FILE): - f = open(CACHE_FILE, 'rb') - try: - cache = pickle.load(f) - except Exception: - cache = {} - finally: - f.close() - else: - cache = {} - return cache - -def save_cache(cache): - f = open(CACHE_FILE + '.new', 'wb') - try: - pickle.dump(cache, f) - finally: - f.close() - os.rename(CACHE_FILE + '.new', CACHE_FILE) - -def render_html(text): - import docutils.parsers.rst - import docutils.writers.html4css1 - import docutils.core - - docutils.parsers.rst.roles.DEFAULT_INTERPRETED_ROLE = 'title-reference' - writer = docutils.writers.html4css1.Writer() - parts = docutils.core.publish_parts( - text, - writer=writer, - settings_overrides = dict(halt_level=5, - traceback=True, - default_reference_context='title-reference', - stylesheet_path='', - # security settings: - raw_enabled=0, - file_insertion_enabled=0, - _disable_config=1, - ) - ) - return parts['html_body'].encode('utf-8') - -if __name__ == "__main__": main() diff --git a/doc/changelog/1.12.0-changelog.rst b/doc/changelog/1.12.0-changelog.rst index 75d9964e3e7f..2e91f510f529 100644 --- a/doc/changelog/1.12.0-changelog.rst +++ b/doc/changelog/1.12.0-changelog.rst @@ -225,7 +225,7 @@ A total of 418 pull requests were merged for this release. * `#7240 `__: Change 'pubic' to 'public'. * `#7241 `__: MAINT: update doc/sphinxext to numpydoc 0.6.0, and fix up some... * `#7243 `__: ENH: Adding support to the range keyword for estimation of the... -* `#7246 `__: DOC: metion writeable keyword in as_strided in release notes +* `#7246 `__: DOC: mention writeable keyword in as_strided in release notes * `#7247 `__: TST: Fail quickly on AppVeyor for superseded PR builds * `#7248 `__: DOC: remove link to documentation wiki editor from HOWTO_DOCUMENT. * `#7250 `__: DOC,REL: Update 1.11.0 notes. @@ -333,7 +333,7 @@ A total of 418 pull requests were merged for this release. * `#7534 `__: MAINT: Update setup.py to reflect supported python versions. * `#7536 `__: MAINT: Always use PyCapsule instead of PyCObject in mtrand.pyx * `#7539 `__: MAINT: Cleanup of random stuff -* `#7549 `__: BUG: allow graceful recovery for no Liux compiler +* `#7549 `__: BUG: allow graceful recovery for no Linux compiler * `#7562 `__: BUG: Fix test_from_object_array_unicode (test_defchararray.TestBasic)… * `#7565 `__: BUG: Fix test_ctypeslib and test_indexing for debug interpreter * `#7566 `__: MAINT: use manylinux1 wheel for cython @@ -459,7 +459,7 @@ A total of 418 pull requests were merged for this release. * `#8016 `__: BUG: Fix numpy.ma.median. * `#8018 `__: BUG: Fixes return for np.ma.count if keepdims is True and axis... * `#8021 `__: DOC: change all non-code instances of Numpy to NumPy -* `#8027 `__: ENH: Add platform indepedent lib dir to PYTHONPATH +* `#8027 `__: ENH: Add platform independent lib dir to PYTHONPATH * `#8028 `__: DOC: Update 1.11.2 release notes. * `#8030 `__: BUG: fix np.ma.median with only one non-masked value and an axis... * `#8038 `__: MAINT: Update error message in rollaxis. diff --git a/doc/changelog/1.14.5-changelog.rst b/doc/changelog/1.14.5-changelog.rst new file mode 100644 index 000000000000..1769a8fc3d85 --- /dev/null +++ b/doc/changelog/1.14.5-changelog.rst @@ -0,0 +1,16 @@ + +Contributors +============ + +A total of 1 person contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Charles Harris + +Pull requests merged +==================== + +A total of 2 pull requests were merged for this release. + +* `#11274 `__: BUG: Correct use of NPY_UNUSED. +* `#11294 `__: BUG: Remove extra trailing parentheses. diff --git a/doc/changelog/1.14.6-changelog.rst b/doc/changelog/1.14.6-changelog.rst new file mode 100644 index 000000000000..be396208d4f4 --- /dev/null +++ b/doc/changelog/1.14.6-changelog.rst @@ -0,0 +1,21 @@ + +Contributors +============ + +A total of 4 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Charles Harris +* Eric Wieser +* Julian Taylor +* Matti Picus + +Pull requests merged +==================== + +A total of 4 pull requests were merged for this release. + +* `#11985 `__: BUG: fix cached allocations without the GIL +* `#11986 `__: BUG: Undo behavior change in ma.masked_values(shrink=True) +* `#11987 `__: BUG: fix refcount leak in PyArray_AdaptFlexibleDType +* `#11995 `__: TST: Add Python 3.7 testing to NumPy 1.14. diff --git a/doc/changelog/1.15.0-changelog.rst b/doc/changelog/1.15.0-changelog.rst new file mode 100644 index 000000000000..dd5544ac9fec --- /dev/null +++ b/doc/changelog/1.15.0-changelog.rst @@ -0,0 +1,584 @@ + +Contributors +============ + +A total of 133 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Aaron Critchley + +* Aarthi + +* Aarthi Agurusa + +* Alex Thomas + +* Alexander Belopolsky +* Allan Haldane +* Anas Khan + +* Andras Deak +* Andrey Portnoy + +* Anna Chiara +* Aurelien Jarno + +* Baurzhan Muftakhidinov +* Berend Kapelle + +* Bernhard M. Wiedemann +* Bjoern Thiel + +* Bob Eldering +* Cenny Wenner + +* Charles Harris +* ChloeColeongco + +* Chris Billington + +* Christopher + +* Chun-Wei Yuan + +* Claudio Freire + +* Daniel Smith +* Darcy Meyer + +* David Abdurachmanov + +* David Freese +* Deepak Kumar Gouda + +* Dennis Weyland + +* Derrick Williams + +* Dmitriy Shalyga + +* Eric Cousineau + +* Eric Larson +* Eric Wieser +* Evgeni Burovski +* Frederick Lefebvre + +* Gaspar Karm + +* Geoffrey Irving +* Gerhard Hobler + +* Gerrit Holl +* Guo Ci + +* Hameer Abbasi + +* Han Shen +* Hiroyuki V. Yamazaki + +* Hong Xu +* Ihor Melnyk + +* Jaime Fernandez +* Jake VanderPlas + +* James Tocknell + +* Jarrod Millman +* Jeff VanOss + +* John Kirkham +* Jonas Rauber + +* Jonathan March + +* Joseph Fox-Rabinovitz +* Julian Taylor +* Junjie Bai + +* Juris Bogusevs + +* Jörg Döpfert +* Kenichi Maehashi + +* Kevin Sheppard +* Kimikazu Kato + +* Kirit Thadaka + +* Kritika Jalan + +* Kyle Sunden + +* Lakshay Garg + +* Lars G + +* Licht Takeuchi +* Louis Potok + +* Luke Zoltan Kelley +* MSeifert04 + +* Mads R. B. Kristensen + +* Malcolm Smith + +* Mark Harfouche + +* Marten H. van Kerkwijk + +* Marten van Kerkwijk +* Matheus Vieira Portela + +* Mathieu Lamarre +* Mathieu Sornay + +* Matthew Brett +* Matthew Rocklin + +* Matthias Bussonnier +* Matti Picus +* Michael Droettboom +* Miguel Sánchez de León Peque + +* Mike Toews + +* Milo + +* Nathaniel J. Smith +* Nelle Varoquaux +* Nicholas Nadeau, P.Eng., AVS + +* Nick Minkyu Lee + +* Nikita + +* Nikita Kartashov + +* Nils Becker + +* Oleg Zabluda +* Orestis Floros + +* Pat Gunn + +* Paul van Mulbregt + +* Pauli Virtanen +* Pierre Chanial + +* Ralf Gommers +* Raunak Shah + +* Robert Kern +* Russell Keith-Magee + +* Ryan Soklaski + +* Samuel Jackson + +* Sebastian Berg +* Siavash Eliasi + +* Simon Conseil +* Simon Gibbons +* Stefan Krah + +* Stefan van der Walt +* Stephan Hoyer +* Subhendu + +* Subhendu Ranjan Mishra + +* Tai-Lin Wu + +* Tobias Fischer + +* Toshiki Kataoka + +* Tyler Reddy + +* Unknown + +* Varun Nayyar +* Victor Rodriguez + +* Warren Weckesser +* William D. Irons + +* Zane Bradley + +* cclauss + +* fo40225 + +* lapack_lite code generator + +* lumbric + +* luzpaz + +* mamrehn + +* tynn + +* xoviat + +Pull requests merged +==================== + +A total of 438 pull requests were merged for this release. + +* `#8157 `__: BUG: void .item() doesn't hold reference to original array +* `#8774 `__: ENH: Add gcd and lcm ufuncs +* `#8819 `__: ENH: Implement axes keyword argument for gufuncs. +* `#8952 `__: MAINT: Removed duplicated code around `ufunc->identity` +* `#9686 `__: DEP: Deprecate non-tuple nd-indices +* `#9980 `__: MAINT: Implement `lstsq` as a `gufunc` +* `#9998 `__: ENH: Nditer as context manager +* `#10073 `__: ENH: Implement fft.fftshift/ifftshift with np.roll for improved... +* `#10078 `__: DOC: document nested_iters +* `#10128 `__: BUG: Prefix library names with `lib` on windows. +* `#10142 `__: DEP: Pending deprecation warning for matrix +* `#10154 `__: MAINT: Use a StructSequence in place of the typeinfo tuples +* `#10158 `__: BUG: Fix a few smaller valgrind errors +* `#10178 `__: MAINT: Prepare master for 1.15 development. +* `#10186 `__: MAINT: Move histogram and histogramdd into their own module +* `#10187 `__: BUG: Extra space is inserted on first line for long elements +* `#10192 `__: DEP: Deprecate the pickle aliases +* `#10193 `__: BUG: Fix bugs found by testing in release mode. +* `#10194 `__: BUG, MAINT: Ufunc reduce reference leak +* `#10195 `__: DOC: Fixup percentile docstring, from review in gh-9213 +* `#10196 `__: BUG: Fix regression in np.ma.load in gh-10055 +* `#10199 `__: ENH: Quantile +* `#10203 `__: MAINT: Update development branch version to 1.15.0. +* `#10205 `__: BUG: Handle NaNs correctly in arange +* `#10207 `__: ENH: Allow `np.r_` to accept 0d arrays +* `#10208 `__: MAINT: Improve error message for void(-1) +* `#10210 `__: DOC: change 'a'->'prototype' in empty_like docs (addresses #10209) +* `#10211 `__: MAINT,ENH: remove MaskedArray.astype, as the base type does everything. +* `#10212 `__: DOC: fix minor typos +* `#10213 `__: ENH: Set up proposed NEP process +* `#10214 `__: DOC: add warning to isclose function +* `#10216 `__: BUG: Fix broken format string picked up by LGTM.com +* `#10220 `__: DOC: clarify that np.absolute == np.abs +* `#10223 `__: ENH: added masked version of 'numpy.stack' with tests. +* `#10225 `__: ENH: distutils: parallelize builds by default +* `#10226 `__: BUG: distutils: use correct top-level package name +* `#10229 `__: BUG: distutils: fix extra DLL loading in certain scenarios +* `#10231 `__: BUG: Fix sign-compare warnings in datetime.c and datetime_strings.c. +* `#10232 `__: BUG: Don't reimplement isclose in np.ma +* `#10237 `__: DOC: give correct version of np.nansum change +* `#10241 `__: MAINT: Avoid repeated validation of percentiles in nanpercentile +* `#10247 `__: MAINT: fix typo +* `#10248 `__: DOC: Add installation notes for Linux users +* `#10249 `__: MAINT: Fix tests failures on travis CI merge. +* `#10250 `__: MAINT: Check for `__array_ufunc__` before doing anything else. +* `#10251 `__: ENH: Enable AVX2/AVX512 support to numpy +* `#10252 `__: MAINT: Workaround for new travis sdist failures. +* `#10255 `__: MAINT: Fix loop and simd sign-compare warnings. +* `#10257 `__: BUG: duplicate message print if warning raises an exception +* `#10259 `__: BUG: Make sure einsum default value of `optimize` is True. +* `#10260 `__: ENH: Add pytest support +* `#10261 `__: MAINT: Extract helper functions from histogram +* `#10262 `__: DOC: Add missing release note for #10207 +* `#10263 `__: BUG: Fix strange behavior of infinite-step-size/underflow-case... +* `#10264 `__: MAINT: Fix (some) yield warnings +* `#10266 `__: BUG: distutils: fix locale decoding errors +* `#10268 `__: BUG: Fix misleading error when coercing to array +* `#10269 `__: MAINT: extract private helper function to compute histogram bin... +* `#10271 `__: BUG: Allow nan values in the data when the bins are explicit +* `#10278 `__: ENH: Add support for datetimes to histograms +* `#10282 `__: MAINT: Extract helper function for last-bound-inclusive search_sorted +* `#10283 `__: MAINT: Fallback on the default sequence multiplication behavior +* `#10284 `__: MAINT/BUG: Tidy gen_umath +* `#10286 `__: BUG: Fix memory leak (#10157). +* `#10287 `__: ENH: Allow ptp to take an axis tuple and keepdims +* `#10292 `__: BUG: Masked singleton can be reshaped to be non-scalar +* `#10293 `__: MAINT: Fix sign-compare warnings in mem_overlap.c. +* `#10294 `__: MAINT: pytest cleanups +* `#10298 `__: DOC: Explain np.digitize and np.searchsorted more clearly +* `#10300 `__: MAINT, DOC: Documentation and misc. typos +* `#10303 `__: MAINT: Array wrap/prepare identification cleanup +* `#10309 `__: MAINT: deduplicate check_nonreorderable_axes +* `#10314 `__: BUG: Ensure `__array_finalize__` cannot back-mangle shape +* `#10316 `__: DOC: add documentation about how to handle new array printing +* `#10320 `__: BUG: skip the extra-dll directory when there are no DLLS +* `#10323 `__: MAINT: Remove duplicated code for promoting dtype and array types. +* `#10324 `__: BUG: Fix crashes when using float32 values in uniform histograms +* `#10325 `__: MAINT: Replace manual expansion of PyArray_MinScalarType with... +* `#10327 `__: MAINT: Fix misc. typos +* `#10333 `__: DOC: typo fix in numpy.linalg.det docstring +* `#10334 `__: DOC: Fix typos in docs for partition method +* `#10336 `__: DOC: Post 1.14.0 release updates. +* `#10337 `__: ENH: Show the silenced error and traceback in warning `__cause__` +* `#10341 `__: BUG: fix config where PATH isn't set on win32 +* `#10342 `__: BUG: arrays not being flattened in `union1d` +* `#10346 `__: ENH: Check matching inputs/outputs in umath generation +* `#10352 `__: BUG: Fix einsum optimize logic for singleton dimensions +* `#10354 `__: BUG: fix error message not formatted in einsum +* `#10359 `__: BUG: do not optimize einsum with only 2 arguments. +* `#10361 `__: BUG: complex repr has extra spaces, missing + +* `#10362 `__: MAINT: Update download URL in setup.py. +* `#10367 `__: BUG: add missing paren and remove quotes from repr of fieldless... +* `#10371 `__: BUG: fix einsum issue with unicode input and py2 +* `#10381 `__: BUG/ENH: Improve output for structured non-void types +* `#10388 `__: ENH: Add types for int and uint of explicit sizes to swig. +* `#10390 `__: MAINT: Adjust type promotion in linalg.norm +* `#10391 `__: BUG: Make dtype.descr error for out-of-order fields +* `#10392 `__: DOC: Document behaviour of `np.concatenate` with `axis=None` +* `#10401 `__: BUG: Resize bytes_ columns in genfromtxt +* `#10402 `__: DOC: added "steals a reference" to PyArray_FromAny +* `#10406 `__: ENH: add `np.printoptions`, a context manager +* `#10411 `__: BUG: Revert multifield-indexing adds padding bytes for NumPy... +* `#10412 `__: ENH: Fix repr of np.record objects to match np.void types +* `#10414 `__: MAINT: Fix sign-compare warnings in umath_linalg. +* `#10415 `__: MAINT: Fix sign-compare warnings in npy_binsearch, npy_partition. +* `#10416 `__: MAINT: Fix sign-compare warnings in dragon4.c. +* `#10418 `__: MAINT: Remove repeated #ifdefs implementing `isinstance(x, basestring)`... +* `#10420 `__: DOC: Fix version added labels in numpy.unique docs +* `#10421 `__: DOC: Fix type of axis in nanfunctions +* `#10423 `__: MAINT: Update zesty to artful for i386 testing +* `#10426 `__: DOC: Add version when linalg.norm accepted axis +* `#10427 `__: DOC: Fix typo in docs for argpartition +* `#10430 `__: MAINT: Use ValueError for duplicate field names in lookup +* `#10433 `__: DOC: Add 1.14.1 release notes template (forward port) +* `#10434 `__: MAINT: Move `tools/announce.py` to `tools/changelog.py`. +* `#10441 `__: BUG: Fix nan_to_num return with integer input +* `#10443 `__: BUG: Fix various Big-Endian test failures (ppc64) +* `#10444 `__: MAINT: Implement float128 dragon4 for IBM double-double (ppc64) +* `#10451 `__: BUG: prevent the MSVC 14.1 compiler (Visual Studio 2017) from... +* `#10453 `__: Revert "BUG: prevent the MSVC 14.1 compiler (Visual Studio 2017)... +* `#10458 `__: BLD: Use zip_safe=False in setup() call +* `#10459 `__: MAINT: Remove duplicated logic between array_wrap and array_prepare +* `#10463 `__: ENH: Add entry_points for f2py, conv_template, and from_template. +* `#10465 `__: MAINT: Fix miscellaneous sign-compare warnings. +* `#10472 `__: DOC: Document A@B in Matlab/NumPy summary table +* `#10473 `__: BUG: Fixed polydiv for Complex Numbers +* `#10475 `__: DOC: Add CircleCI builder for devdocs +* `#10476 `__: DOC: fix formatting in interp example +* `#10477 `__: BUG: Align type definition with generated lapack +* `#10478 `__: DOC: Minor punctuation cleanups and improved explanation. +* `#10479 `__: BUG: Fix calling ufuncs with a positional output argument. +* `#10482 `__: BUG: Add missing DECREF in Py2 int() cast +* `#10484 `__: MAINT: Remove unused code path for applying maskedarray domains... +* `#10497 `__: DOC: Tell matlab users about np.block +* `#10498 `__: MAINT: Remove special cases in np.unique +* `#10501 `__: BUG: fromregex: asbytes called on regexp objects +* `#10502 `__: MAINT: Use AxisError in swapaxes, unique, and diagonal +* `#10503 `__: BUG: Fix unused-result warning. +* `#10506 `__: MAINT: Delete unused `_build_utils/common.py` +* `#10508 `__: BUG: Add missing `#define _MULTIARRAYMODULE` to vdot.c +* `#10509 `__: MAINT: Use new-style format strings for clarity +* `#10516 `__: MAINT: Allow errors to escape from InitOperators +* `#10518 `__: ENH: Add a repr to np._NoValue +* `#10522 `__: MAINT: Remove the unmaintained umath ``__version__`` constant. +* `#10524 `__: BUG: fix np.save issue with python 2.7.5 +* `#10529 `__: BUG: Provide a better error message for out-of-order fields +* `#10543 `__: DEP: Issue FutureWarning when malformed records detected. +* `#10544 `__: BUG: infinite recursion in str of 0d subclasses +* `#10546 `__: BUG: In numpy.i, clear CARRAY flag if wrapped buffer is not C_CONTIGUOUS. +* `#10547 `__: DOC: Fix incorrect formula in gradient docstring. +* `#10548 `__: BUG: Set missing exception after malloc +* `#10549 `__: ENH: Make NpzFile conform to the Mapping protocol +* `#10553 `__: MAINT: Cleanups to promote_types and result_types +* `#10554 `__: DOC: promote_types is not associative by design, +* `#10555 `__: BUG: Add missing PyErr_NoMemory() after malloc +* `#10564 `__: BUG: Provide correct format in Py_buffer for scalars +* `#10566 `__: BUG: Fix travis failure in previous commit +* `#10571 `__: BUG: Fix corner-case behavior of cond() and use SVD when possible +* `#10576 `__: MAINT: Fix misc. documentation typos +* `#10583 `__: MAINT: Fix typos in DISTUTILS.rst.txt. +* `#10588 `__: BUG: Revert sort optimization in np.unique. +* `#10589 `__: BUG: fix entry_points typo for from-template +* `#10591 `__: ENH: Add histogram_bin_edges function and test +* `#10592 `__: DOC: Corrected url for Guide to NumPy book; see part of #8520,... +* `#10596 `__: MAINT: Update sphinxext submodule hash. +* `#10599 `__: ENH: Make flatnonzero call asanyarray before ravel() +* `#10603 `__: MAINT: Improve error message in histogram. +* `#10604 `__: MAINT: Fix Misc. typos +* `#10606 `__: MAINT: Do not use random roots when testing roots. +* `#10618 `__: MAINT: Stop using non-tuple indices internally +* `#10619 `__: BUG: np.ma.flatnotmasked_contiguous behaves differently on mask=nomask... +* `#10621 `__: BUG: deallocate recursive closure in arrayprint.py +* `#10623 `__: BUG: Correctly identify comma separated dtype strings +* `#10625 `__: BUG: Improve the accuracy of the FFT implementation +* `#10635 `__: ENH: Implement initial kwarg for ufunc.add.reduce +* `#10641 `__: MAINT: Post 1.14.1 release updates for master branch +* `#10650 `__: BUG: Fix missing NPY_VISIBILITY_HIDDEN on npy_longdouble_to_PyLong +* `#10653 `__: MAINT: Remove duplicate implementation for aliased functions. +* `#10657 `__: BUG: f2py: fix f2py generated code to work on Pypy +* `#10658 `__: BUG: Make np.partition and np.sort work on np.matrix when axis=None +* `#10660 `__: BUG/MAINT: Remove special cases for 0d arrays in interp +* `#10661 `__: MAINT: Unify reductions in fromnumeric.py +* `#10665 `__: ENH: umath: don't make temporary copies for in-place accumulation +* `#10666 `__: BUG: fix complex casting error in cov with aweights +* `#10669 `__: MAINT: Covariance must be symmetric as well as positive-semidefinite. +* `#10670 `__: DEP: Deprecate np.sum(generator) +* `#10671 `__: DOC/MAINT: More misc. typos +* `#10672 `__: ENH: Allow dtype field names to be ascii encoded unicode in Python2 +* `#10676 `__: BUG: F2py mishandles quoted control characters +* `#10677 `__: STY: Minor stylistic cleanup of numeric.py +* `#10679 `__: DOC: zeros, empty, and ones now have consistent docstrings +* `#10684 `__: ENH: Modify intersect1d to return common indices +* `#10689 `__: BLD: Add configuration changes to allow cross platform builds... +* `#10691 `__: DOC: add versionadded for NDArrayOperatorsMixin. +* `#10694 `__: DOC: Improve docstring of memmap +* `#10698 `__: BUG: Further back-compat fix for subclassed array repr (forward... +* `#10699 `__: DOC: Grammar of np.gradient docstring +* `#10702 `__: TST, DOC: Upload devdocs and neps after circleci build +* `#10703 `__: MAINT: NEP process updates +* `#10708 `__: BUG: fix problem with modifying pyf lines containing ';' in f2py +* `#10710 `__: BUG: fix error message in numpy.select +* `#10711 `__: MAINT: Hard tab and whitespace cleanup. +* `#10715 `__: MAINT: Fixed C++ guard in f2py test. +* `#10716 `__: BUG: dragon4 fractional output mode adds too many trailing zeros +* `#10718 `__: BUG: Fix bug in asserting near equality of float16 arrays. +* `#10719 `__: DOC: add documentation for constants +* `#10720 `__: BUG: distutils: Remove named templates from the processed output... +* `#10722 `__: MAINT: Misc small fixes. +* `#10730 `__: DOC: Fix minor typo in how-to-document. +* `#10732 `__: BUG: Fix `setup.py build install egg_info`, which did not previously... +* `#10734 `__: DOC: Post 1.14.2 release update. +* `#10737 `__: MAINT: Fix low-hanging PyPy compatibility issues +* `#10739 `__: BUG: Fix histogram bins="auto" for data with little variance +* `#10740 `__: MAINT, TST: Fixes for Python 3.7 +* `#10743 `__: MAINT: Import abstract classes from collections.abc +* `#10745 `__: ENH: Add object loops to the comparison ufuncs +* `#10746 `__: MAINT: Fix typo in warning message +* `#10748 `__: DOC: a.size and np.prod(a.shape) are not equivalent +* `#10750 `__: DOC: Add graph showing different behaviors of np.percentile +* `#10755 `__: DOC: Move bin estimator documentation from `histogram` to `histogram_bin_edges` +* `#10758 `__: TST: Change most travisci tests to Python3.6. +* `#10763 `__: BUG: floating types should override tp_print +* `#10766 `__: MAINT: Remove the unused scalarmath getters for fmod and sqrt +* `#10773 `__: BUG: Use dummy_threading on platforms that don't support threading +* `#10774 `__: BUG: Fix SQRT_MIN for platforms with 8-byte long double +* `#10775 `__: BUG: Return NULL from PyInit_* when exception is raised +* `#10777 `__: MAINT: Remove use of unittest in NumPy tests. +* `#10778 `__: BUG: test, fix for missing flags['WRITEBACKIFCOPY'] key +* `#10781 `__: ENH: NEP index builder +* `#10785 `__: DOC: Fixed author name in reference to book +* `#10786 `__: ENH: Add "stable" option to np.sort as an alias for "mergesort". +* `#10790 `__: TST: Various fixes prior to switching to pytest +* `#10795 `__: BUG: Allow spaces in output string of einsum +* `#10796 `__: BUG: fix wrong inplace vectorization on overlapping arguments +* `#10798 `__: BUG: error checking before mapping of einsum axes. +* `#10800 `__: DOC: Add remarks about array vs scalar output to every ufunc +* `#10802 `__: BUG/DOC/MAINT: Tidy up histogramdd +* `#10807 `__: DOC: Update link to tox in development docs (#10806) +* `#10812 `__: MAINT: Rearrange `numpy/testing` files +* `#10814 `__: BUG: verify the OS supports avx instruction +* `#10822 `__: BUG: fixes exception in numpy.genfromtxt, see #10780 +* `#10824 `__: BUG: test, fix PyArray_DiscardWritebackIfCopy refcount issue... +* `#10826 `__: BUG: np.squeeze() now respects older API axis expectation +* `#10827 `__: ENH: Add tester for pytest. +* `#10828 `__: BUG: fix obvious mistake in testing/decorators warning. +* `#10829 `__: BLD: use Python 3.6 instead of 2.7 as default for doc build. +* `#10830 `__: BUG: Fix obvious warning bugs. +* `#10831 `__: DOC: Fix minor typos +* `#10832 `__: ENH: datetime64: support AC dates starting with '+' +* `#10833 `__: ENH: Add support for the 64-bit RISC-V architecture +* `#10834 `__: DOC: note that NDEBUG should be set when OPT should increase... +* `#10836 `__: MAINT: Fix script name for pushing NEP docs to repo +* `#10840 `__: MAINT: Fix typo in code example. +* `#10842 `__: TST: Switch to pytest +* `#10849 `__: DOC: fix examples in docstring for np.flip +* `#10850 `__: DEP: Issue deprecation warnings for some imports. +* `#10858 `__: MAINT: Post pytest switch cleanup +* `#10859 `__: MAINT: Remove yield tests +* `#10860 `__: BUG: core: fix NPY_TITLE_KEY macro on pypy +* `#10863 `__: MAINT: More Histogramdd cleanup +* `#10867 `__: DOC: Cross Link full/full_like in a few see-also sections. +* `#10869 `__: BUG: Fix encoding regression in ma/bench.py (Issue #10868) +* `#10871 `__: MAINT: Remove unnecessary special case in np.histogramdd for... +* `#10872 `__: ENH: Extend np.flip to work over multiple axes +* `#10874 `__: DOC: State in docstring that lexsort is stable (#10873). +* `#10875 `__: BUG: fix savetxt, loadtxt for '+-' in complex +* `#10878 `__: DOC: rework documents and silence warnings during sphinx build +* `#10882 `__: BUG: have `_array_from_buffer_3118` correctly handle errors +* `#10883 `__: DOC: Fix negative binomial documentation. +* `#10885 `__: TST: Re-enable test display on appveyor +* `#10890 `__: MAINT: lstsq: compute residuals inside the ufunc +* `#10891 `__: TST: Extract a helper function to test for reference cycles +* `#10898 `__: ENH: Have dtype transfer for equivalent user dtypes prefer user-defined... +* `#10901 `__: DOC, BUG : Bad link to `np.random.randint` +* `#10903 `__: DOC: Fix link in `See Also` section of `randn` docstring. +* `#10907 `__: TST: reactivate module docstring tests, fix float formatting +* `#10911 `__: BUG: Fix casting between npy_half and float in einsum +* `#10916 `__: BUG: Add missing underscore to prototype in check_embedded_lapack +* `#10919 `__: BUG: Pass non-None outputs to `__array_prepare__` and `__array_wrap__` +* `#10921 `__: DOC: clear up warnings, fix matplotlib plot +* `#10923 `__: BUG: fixed dtype alignment for array of structs in case of converting... +* `#10925 `__: DOC: Fix typos in 1.15.0 changelog +* `#10936 `__: DOC: Fix NumpyVersion example (closes gh-10935) +* `#10938 `__: MAINT: One step closer to vectorizing lstsq +* `#10940 `__: DOC: fix broken links for developer documentation +* `#10943 `__: ENH: Add a search box to the sidebar in the docs +* `#10945 `__: MAINT: Remove references to the 2008 documentation marathon +* `#10946 `__: BUG: 'style' arg to array2string broken in legacy mode +* `#10949 `__: DOC: cleanup documentation, continuation of nditer PR #9998 +* `#10951 `__: BUG: it.close() disallows access to iterator, fixes #10950 +* `#10953 `__: MAINT: address extraneous shape tuple checks in descriptor.c +* `#10958 `__: MAINT, DOC: Fix typos +* `#10967 `__: DOC: add quantile, nanquantile to toc +* `#10970 `__: WIP: Remove fragile use of `__array_interface__` in ctypeslib.as_array +* `#10971 `__: MAINT: Remove workaround for gh-10891 +* `#10973 `__: DOC: advise against use of matrix. +* `#10975 `__: MAINT: move linalg tests using matrix to matrixlib +* `#10980 `__: DOC: link to governance, convert external link to internal +* `#10984 `__: MAINT: Added pytest cache folder to .gitignore +* `#10985 `__: MAINT, ENH: Move matrix_power to linalg and allow higher dimensions. +* `#10986 `__: MAINT: move all masked array matrix tests to matrixlib. +* `#10987 `__: DOC: Correction to docstring example (result was correct) +* `#10988 `__: MAINT: Small tidy-ups to ufunc_object.c +* `#10991 `__: DOC: Update genfromtxt docs to use StringIO and u-strings +* `#10996 `__: DOC: Make doc examples using StringIO python2-3 compatible +* `#11003 `__: DOC: work around GH isaacs/github#316 to show SVG image +* `#11005 `__: MAINT: Misc. typos +* `#11006 `__: TST, BUILD: add latex to circleci doc build +* `#11008 `__: REL: Fwd port 1.14.3 changelog +* `#11009 `__: DOC: release walkthrough updates from 1.14.3 +* `#11010 `__: Move remaining Matrix tests to matrixlib +* `#11011 `__: MAINT: Simplify dimension-juggling in np.pad +* `#11012 `__: MAINT: np.pad: Add helper functions for producing slices along... +* `#11018 `__: ENH: Implement axis for generalized ufuncs. +* `#11023 `__: BUG: np.histogramdd loses precision on its inputs, leading to... +* `#11026 `__: MAINT: reduce code duplication in ufunc_frompyfunc +* `#11033 `__: BUG: Fix padding with large integers +* `#11036 `__: BUG: optimizing compilers can reorder call to npy_get_floatstatus +* `#11037 `__: BUG: initialize value before use +* `#11038 `__: ENH: Add `__deepcopy__` to MaskedConstant +* `#11043 `__: BUG: reduce using SSE only warns if inside SSE loop +* `#11050 `__: BUG: remove fast scalar power for arrays with object dtype +* `#11053 `__: DOC: bump scipy-sphinx-theme to current version +* `#11055 `__: DOC: Add explanation for comments=None in loadtxt. +* `#11056 `__: MAINT: Improve performance of random permutation +* `#11057 `__: BUG: use absolute imports in test files +* `#11066 `__: MAINT: `distutils.system_info`: handle Accelerate like any other... +* `#11073 `__: DOC: expand reasoning behind npy_*floatstatus_barrer() +* `#11076 `__: BUG: Ensure `PyArray_AssignRawScalar` respects `NPY_NEEDS_INIT` +* `#11082 `__: DOC: link to updated module docstring, not NEP +* `#11083 `__: ENH: remove nose from travis tests +* `#11085 `__: DOC: create label and ref, fixes broken link +* `#11086 `__: DOC: Mention we can return unitinitialized values +* `#11089 `__: BLD: cleanup `_configtest.o.d` during build +* `#11090 `__: BUG: Added support for index values 27-52 in C einsum +* `#11091 `__: BUG: Python2 doubles don't print correctly in interactive shell +* `#11094 `__: DOC: add numpy.lib.format to docs and link to it +* `#11095 `__: MAINT: Einsum argument parsing cleanup +* `#11097 `__: BUG: fix datetime.timedelta->timedelta64 unit detection logic +* `#11098 `__: ENH: Add keepdims argument for generalized ufuncs. +* `#11105 `__: ENH: Add (put|take)_along_axis +* `#11111 `__: BUG: fix case of ISA selector in ufunc selection +* `#11116 `__: BUG: Typo in variable name in binary_repr +* `#11120 `__: MAINT: remove redundant code in `MaskedArray.__new__` +* `#11122 `__: BUG,MAINT: Ensure masked elements can be tested against nan and... +* `#11124 `__: BUG: Ensure that fully masked arrays pass assert_array_equal. +* `#11134 `__: DOC: Clarify tofile requirements +* `#11137 `__: MAINT: move remaining MaskedArray matrix tests to matrixlib. +* `#11139 `__: TST: turn some build warnings into errors +* `#11140 `__: MAINT: Update artful to bionic for i386 testing +* `#11141 `__: MAINT: Extract a helper function for prepending and appending +* `#11145 `__: DOC: cleanup NEP creation +* `#11146 `__: DOC: add a NEP to split MaskedArray into a separate package +* `#11148 `__: TST: make build warning into an error in runtest.py +* `#11149 `__: BUG: guessing datetime, time precedence +* `#11152 `__: BENCH: Add basic benchmarks for numpy.pad +* `#11155 `__: BUG: Prevent stackoverflow in conversion to datetime types +* `#11158 `__: TST: disable gc in refcount test +* `#11159 `__: TST: Skip ctypes dependent test that fails on Python < 2.7.7. +* `#11160 `__: TST: windows builds now properly support floating error states +* `#11163 `__: MAINT: Work around non-deterministic Python readdir order in... +* `#11167 `__: MAINT: Cleanup dragon4 code in various ways +* `#11168 `__: TST: linalg: add regression test for gh-8577 +* `#11169 `__: MAINT: add sanity-checks to be run at import time +* `#11173 `__: MAINT: Ensure that parsing errors are passed on even in tests. +* `#11176 `__: MAINT: avoid setting non-existing gufunc strides for keepdims=True. +* `#11177 `__: DOC: improvement of the documentation for gufunc. +* `#11178 `__: TST: Test dimensions/indices found from parsed gufunc signatures. +* `#11180 `__: BUG: void dtype setup checked offset not actual pointer for alignment +* `#11182 `__: BUG: Avoid deprecated non-tuple indexing +* `#11184 `__: MAINT: Add bitmask helper functions +* `#11185 `__: MAINT: Add comments to long_double detection code +* `#11186 `__: TST: Add np.core._multiarray_tests.format_float_OSprintf_g +* `#11187 `__: MAINT: Use the more common -1 / 0 to indicate error / success +* `#11189 `__: NEP: Array function protocol +* `#11190 `__: DOC: Update NEP0 to clarify that discussion should happen on... +* `#11191 `__: MAINT: remove darwin hardcoded LDOUBLE detection +* `#11193 `__: BUG: Fix reference count/memory leak exposed by better testing +* `#11200 `__: BUG: Bytes delimiter/comments in genfromtxt should be decoded +* `#11209 `__: DOC: Fix doctest formatting in `rot90()` examples +* `#11218 `__: BUG: Fixes einsum broadcasting bug when optimize=True +* `#11222 `__: DOC: Make reference doc nditer examples python3 friendly +* `#11223 `__: BUG: Forcibly promote shape to uint64 in numpy.memmap. +* `#11225 `__: DOC: add existing recfunctions documentation to output +* `#11226 `__: MAINT: add 'rst' to nep filename, fixup urls +* `#11229 `__: NEP: New RNG policy +* `#11231 `__: MAINT: ensure we do not create unnecessary tuples for outputs +* `#11238 `__: MAINT: Don't update the flags a second time +* `#11239 `__: MAINT: Use PyArray_NewFromDescr where possible, remove unused... +* `#11240 `__: MAINT: Remove dead code backporting py2.6 warnings +* `#11246 `__: BUG: Set ndarray.base before `__array_finalize__` +* `#11247 `__: MAINT/BUG: Remove out-of-band reference count in PyArray_Newshape,... +* `#11248 `__: MAINT: Don't update the flags a second time +* `#11249 `__: BUG: Remove errant flag meddling in .real and .imag +* `#11252 `__: DOC: show how to generate release notes in release walkthrough +* `#11257 `__: BUG: ensure extobj and axes have their own references. +* `#11260 `__: MAINT: Do proper cleanup in get_ufunc_arguments. +* `#11263 `__: DOC: Update master after NumPy 1.14.4 release. +* `#11269 `__: BUG: Correct use of NPY_UNUSED. +* `#11273 `__: BUG: Remove invalid read in searchsorted if needle is empty +* `#11275 `__: TST: Do not use empty arrays in tests (unless they are not read) +* `#11277 `__: BUG: Work around past and present PEP3118 issues in ctypes +* `#11280 `__: DOC: make docstring of np.interp clearer +* `#11286 `__: BUG: einsum needs to check overlap on an out argument +* `#11287 `__: DOC: Minor documentation improvements +* `#11291 `__: BUG: Remove extra trailing parentheses. +* `#11293 `__: DOC: fix hierarchy of numericaltype +* `#11296 `__: BUG: Fix segfault on failing `__array_wrap__` +* `#11298 `__: BUG: Undo behavior change in ma.masked_values(shrink=True) +* `#11307 `__: BUG: Fix memmap regression when shape=None +* `#11314 `__: MAINT: remove unused "npy_import" +* `#11315 `__: MAINT: Package `tools/allocation_tracking` +* `#11319 `__: REL, REV: Revert f2py fixes that exposed SciPy bug. +* `#11327 `__: DOC: Update release notes for 1.15.0. +* `#11339 `__: BUG: decref in failure path; replace PyObject_Type by Py_TYPE +* `#11352 `__: DEP: Actually deprecate the normed argument to histogram +* `#11359 `__: DOC: document new functions +* `#11367 `__: BUG: add missing NpyIter_Close in einsum +* `#11368 `__: BUG/TST: String indexing should just fail, not emit a futurewarning +* `#11389 `__: ENH: Remove NpyIter_Close +* `#11392 `__: BUG: Make scalar.squeeze accept axis arg +* `#11393 `__: REL,MAINT: Update numpyconfig.h for 1.15. +* `#11394 `__: MAINT: Update mailmap +* `#11403 `__: DOC: Remove npyiter close from notes +* `#11427 `__: BUG: Fix incorrect deprecation logic for histogram(normed=...)... +* `#11489 `__: BUG: Ensure out is returned in einsum. +* `#11491 `__: BUG/ENH: Einsum optimization path updates and bug fixes. +* `#11493 `__: BUG: Revert #10229 to fix DLL loads on Windows. +* `#11494 `__: MAINT: add PyPI classifier for Python 3.7 +* `#11495 `__: BENCH: belated addition of lcm, gcd to ufunc benchmark. +* `#11496 `__: BUG: Advanced indexing assignment incorrectly took 1-D fastpath +* `#11511 `__: BUG: Fix #define for ppc64 and ppc64le +* `#11529 `__: ENH: Add density argument to histogramdd. +* `#11532 `__: BUG: Decref of field title caused segfault +* `#11540 `__: DOC: Update the 1.15.0 release notes. +* `#11577 `__: BLD: Modify cpu detection and printing to get working aarch64... +* `#11578 `__: DOC: link to TESTS.rst.txt testing guidelines, tweak testing... +* `#11602 `__: TST: Add Python 3.7 to CI testing diff --git a/doc/changelog/1.15.1-changelog.rst b/doc/changelog/1.15.1-changelog.rst new file mode 100644 index 000000000000..42ba67c2bbc5 --- /dev/null +++ b/doc/changelog/1.15.1-changelog.rst @@ -0,0 +1,44 @@ + +Contributors +============ + +A total of 7 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Charles Harris +* Chris Billington +* Elliott Sales de Andrade + +* Eric Wieser +* Jeremy Manning + +* Matti Picus +* Ralf Gommers + +Pull requests merged +==================== + +A total of 24 pull requests were merged for this release. + +* `#11647 `__: MAINT: Filter Cython warnings in ``__init__.py`` +* `#11648 `__: BUG: Fix doc source links to unwrap decorators +* `#11657 `__: BUG: Ensure singleton dimensions are not dropped when converting... +* `#11661 `__: BUG: Warn on Nan in minimum,maximum for scalars +* `#11665 `__: BUG: cython sometimes emits invalid gcc attribute +* `#11682 `__: BUG: Fix regression in void_getitem +* `#11698 `__: BUG: Make matrix_power again work for object arrays. +* `#11700 `__: BUG: Add missing PyErr_NoMemory after failing malloc +* `#11719 `__: BUG: Fix undefined functions on big-endian systems. +* `#11720 `__: MAINT: Make einsum optimize default to False. +* `#11746 `__: BUG: Fix regression in loadtxt for bz2 text files in Python 2. +* `#11757 `__: BUG: Revert use of `console_scripts`. +* `#11758 `__: BUG: Fix Fortran kind detection for aarch64 & s390x. +* `#11759 `__: BUG: Fix printing of longdouble on ppc64le. +* `#11760 `__: BUG: Fixes for unicode field names in Python 2 +* `#11761 `__: BUG: Increase required cython version on python 3.7 +* `#11763 `__: BUG: check return value of _buffer_format_string +* `#11775 `__: MAINT: Make assert_array_compare more generic. +* `#11776 `__: TST: Fix urlopen stubbing. +* `#11777 `__: BUG: Fix regression in intersect1d. +* `#11779 `__: BUG: Fix test sensitive to platform byte order. +* `#11781 `__: BUG: Avoid signed overflow in histogram +* `#11785 `__: BUG: Fix pickle and memoryview for datetime64, timedelta64 scalars +* `#11786 `__: BUG: Deprecation triggers segfault diff --git a/doc/changelog/1.15.2-changelog.rst b/doc/changelog/1.15.2-changelog.rst new file mode 100644 index 000000000000..b4589c56db9a --- /dev/null +++ b/doc/changelog/1.15.2-changelog.rst @@ -0,0 +1,21 @@ + +Contributors +============ + +A total of 4 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Charles Harris +* Julian Taylor +* Marten van Kerkwijk +* Matti Picus + +Pull requests merged +==================== + +A total of 4 pull requests were merged for this release. + +* `#11902 `__: BUG: Fix matrix PendingDeprecationWarning suppression for pytest... +* `#11981 `__: BUG: fix cached allocations without the GIL for 1.15.x +* `#11982 `__: BUG: fix refcount leak in PyArray_AdaptFlexibleDType +* `#11992 `__: BUG: Ensure boolean indexing of subclasses sets base correctly. diff --git a/doc/changelog/1.15.3-changelog.rst b/doc/changelog/1.15.3-changelog.rst new file mode 100644 index 000000000000..9e03df454049 --- /dev/null +++ b/doc/changelog/1.15.3-changelog.rst @@ -0,0 +1,32 @@ + +Contributors +============ + +A total of 7 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Allan Haldane +* Charles Harris +* Jeroen Demeyer +* Kevin Sheppard +* Matthew Bowden + +* Matti Picus +* Tyler Reddy + +Pull requests merged +==================== + +A total of 12 pull requests were merged for this release. + +* `#12080 `__: MAINT: Blacklist some MSVC complex functions. +* `#12083 `__: TST: Add azure CI testing to 1.15.x branch. +* `#12084 `__: BUG: test_path() now uses Path.resolve() +* `#12085 `__: TST, MAINT: Fix some failing tests on azure-pipelines mac and... +* `#12187 `__: BUG: Fix memory leak in mapping.c +* `#12188 `__: BUG: Allow boolean subtract in histogram +* `#12189 `__: BUG: Fix in-place permutation +* `#12190 `__: BUG: limit default for get_num_build_jobs() to 8 +* `#12191 `__: BUG: OBJECT_to_* should check for errors +* `#12192 `__: DOC: Prepare for NumPy 1.15.3 release. +* `#12237 `__: BUG: Fix MaskedArray fill_value type conversion. +* `#12238 `__: TST: Backport azure-pipeline testing fixes for Mac diff --git a/doc/changelog/1.15.4-changelog.rst b/doc/changelog/1.15.4-changelog.rst new file mode 100644 index 000000000000..fbe71f4ae38b --- /dev/null +++ b/doc/changelog/1.15.4-changelog.rst @@ -0,0 +1,21 @@ + +Contributors +============ + +A total of 4 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Charles Harris +* Matti Picus +* Sebastian Berg +* bbbbbbbbba + + +Pull requests merged +==================== + +A total of 4 pull requests were merged for this release. + +* `#12296 `__: BUG: Dealloc cached buffer info (#12249) +* `#12297 `__: BUG: Fix fill value in masked array '==' and '!=' ops. +* `#12307 `__: DOC: Correct the default value of `optimize` in `numpy.einsum` +* `#12320 `__: REL: Prepare for the NumPy 1.15.4 release diff --git a/doc/changelog/1.16.0-changelog.rst b/doc/changelog/1.16.0-changelog.rst new file mode 100644 index 000000000000..8aca5e643b1a --- /dev/null +++ b/doc/changelog/1.16.0-changelog.rst @@ -0,0 +1,616 @@ + +Contributors +============ + +A total of 113 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Alan Fontenot + +* Allan Haldane +* Alon Hershenhorn + +* Alyssa Quek + +* Andreas Nussbaumer + +* Anner + +* Anthony Sottile + +* Antony Lee +* Ayappan P + +* Bas van Schaik + +* C.A.M. Gerlach + +* Charles Harris +* Chris Billington +* Christian Clauss +* Christoph Gohlke +* Christopher Pezley + +* Daniel B Allan + +* Daniel Smith +* Dawid Zych + +* Derek Kim + +* Dima Pasechnik + +* Edgar Giovanni Lepe + +* Elena Mokeeva + +* Elliott Sales de Andrade + +* Emil Hessman + +* Eric Larson +* Eric Schles + +* Eric Wieser +* Giulio Benetti + +* Guillaume Gautier + +* Guo Ci +* Heath Henley + +* Isuru Fernando + +* J. Lewis Muir + +* Jack Vreeken + +* Jaime Fernandez +* James Bourbeau +* Jeff VanOss +* Jeffrey Yancey + +* Jeremy Chen + +* Jeremy Manning + +* Jeroen Demeyer +* John Darbyshire + +* John Kirkham +* John Zwinck +* Jonas Jensen + +* Joscha Reimer + +* Juan Azcarreta + +* Julian Taylor +* Kevin Sheppard +* Krzysztof Chomski + +* Kyle Sunden +* Lars Grüter +* Lilian Besson + +* MSeifert04 +* Mark Harfouche +* Marten van Kerkwijk +* Martin Thoma +* Matt Harrigan + +* Matthew Bowden + +* Matthew Brett +* Matthias Bussonnier +* Matti Picus +* Max Aifer + +* Michael Hirsch, Ph.D + +* Michael James Jamie Schnaitter + +* MichaelSaah + +* Mike Toews +* Minkyu Lee + +* Mircea Akos Bruma + +* Mircea-Akos Brumă + +* Moshe Looks + +* Muhammad Kasim + +* Nathaniel J. Smith +* Nikita Titov + +* Paul Müller + +* Paul van Mulbregt +* Pauli Virtanen +* Pierre Glaser + +* Pim de Haan +* Ralf Gommers +* Robert Kern +* Robin Aggleton + +* Rohit Pandey + +* Roman Yurchak + +* Ryan Soklaski +* Sebastian Berg +* Sho Nakamura + +* Simon Gibbons +* Stan Seibert + +* Stefan Otte +* Stefan van der Walt +* Stephan Hoyer +* Stuart Archibald +* Taylor Smith + +* Tim Felgentreff + +* Tim Swast + +* Tim Teichmann + +* Toshiki Kataoka +* Travis Oliphant +* Tyler Reddy +* Uddeshya Singh + +* Warren Weckesser +* Weitang Li + +* Wenjamin Petrenko + +* William D. Irons +* Yannick Jadoul + +* Yaroslav Halchenko +* Yug Khanna + +* Yuji Kanagawa + +* Yukun Guo + +* @ankokumoyashi + +* @lerbuke + + +Pull requests merged +==================== + +A total of 490 pull requests were merged for this release. + +* `#6256 `__: NEP: Add proposal for oindex and vindex. +* `#6377 `__: BUG: define "uint-alignment", fixes complex64 alignment +* `#8206 `__: ENH: add padding options to diff +* `#8923 `__: ENH: Add 'stone' estimator to np.histogram +* `#8955 `__: ENH: Allow ufunc.identity to be any python object +* `#9022 `__: BUG: don't silence `__array_wrap__` errors in `ufunc.reduce` +* `#10551 `__: BUG: memmap close files when it shouldn't, load leaves them open... +* `#10602 `__: MAINT: Move dtype string functions to python +* `#10704 `__: NEP 15: Merging multiarray and umath +* `#10797 `__: DEP: Updated `unravel_index()` to support `shape` kwarg +* `#10915 `__: ENH: implement nep 0015: merge multiarray and umath +* `#10998 `__: DOC: removed spurious FIXME comment in number.c +* `#11002 `__: MAINT: add clearer message to assist users with failed builds. +* `#11016 `__: ENH: Add AARCH32 support. +* `#11084 `__: DOC: link to TESTS.rst.txt testing guidelines, tweak testing... +* `#11119 `__: ENH: Chain exceptions to give better error messages for invalid... +* `#11175 `__: ENH: Generalized ufunc signature expansion for frozen and flexible... +* `#11197 `__: BUG/ENH: Removed non-standard scaling of the covariance matrix... +* `#11234 `__: DOC: Update einsum docs +* `#11282 `__: MAINT: move comparison operator special-handling out of ufunc... +* `#11297 `__: NEP: Expansion of gufunc signatures. +* `#11299 `__: BUG: Prevent crashes on 0-length structured void scalars +* `#11303 `__: DOC: revision of NEP-18 (`__array_function__`) +* `#11312 `__: WIP: DOC: slightly tweak the directions to create a release +* `#11318 `__: REL: Setup master for 1.16 development. +* `#11323 `__: DEP: Actually deprecate the normed argument to histogram +* `#11324 `__: MAINT: Don't use dtype strings when the dtypes themselves can... +* `#11326 `__: DOC: Update master after NumPy 1.14.5 release. +* `#11328 `__: MAINT: Misc numeric cleanup +* `#11335 `__: DOC: Change array lengths/entries in `broadcast_arrays` example... +* `#11336 `__: BUG: decref in failure path; replace `PyObject_Type` by `Py_TYPE` +* `#11338 `__: MAINT: Ensure ufunc override call each class only once, plus... +* `#11340 `__: BUG: sctypeDict['f8'] randomly points to double or longdouble... +* `#11345 `__: BUG/ENH: Einsum optimization path updates and bug fixes. +* `#11347 `__: DOC: Silence many sphinx warnings +* `#11348 `__: ENH: Improve support for pathlib.Path objects in load functions +* `#11349 `__: DOC: document new functions +* `#11351 `__: MAINT: Improve speed of ufunc kwargs parsing +* `#11353 `__: DOC, MAINT: HTTP -> HTTPS, and other linkrot fixes +* `#11356 `__: NEP: Update NEP 19: RNG Policy +* `#11357 `__: MAINT: Add new `_test.c` files and `benchmarks/html` to `gitignore` +* `#11365 `__: BUG: add missing NpyIter_Close in einsum +* `#11366 `__: BUG/TST: String indexing should just fail, not emit a futurewarning +* `#11371 `__: DOC: Clarify requirement that histogram bins are monotonic. +* `#11373 `__: TST: Show that histogramdd's normed argument is histogram's density +* `#11374 `__: WIP: additional revision for NEP-18 (`__array_function__`) +* `#11376 `__: ENH: Remove NpyIter_Close +* `#11379 `__: BUG: changed hardcoded axis to 0 for checking indices +* `#11382 `__: DEP: deprecate undocumented, unused dtype type dicts +* `#11383 `__: ENH: Allow size=0 in numpy.random.choice +* `#11385 `__: BUG: Make scalar.squeeze accept axis arg +* `#11390 `__: REL,MAINT: Update numpyconfig.h for 1.15. +* `#11391 `__: MAINT: Update mailmap +* `#11396 `__: TST: Added regression test for #11395 +* `#11405 `__: BUG: Ensure comparisons on scalar strings pass without warning. +* `#11406 `__: BUG: Ensure out is returned in einsum. +* `#11409 `__: DOC: Update testing section of README. +* `#11414 `__: DOC: major revision of NEP 21, advanced indexing +* `#11422 `__: BENCH: Add benchmarks for np.loadtxt reading from CSV format... +* `#11424 `__: ENH: Allow use of svd on empty arrays +* `#11425 `__: DOC: Clear up confusion between np.where(cond) and np.where(cond,... +* `#11428 `__: BUG: Fix incorrect deprecation logic for histogram(normed=...)... +* `#11429 `__: NEP: accept NEP 20 partially (frozen, flexible, but not broadcastable... +* `#11432 `__: MAINT: Refactor differences between cblas_matrixproduct and PyArray_MatrixProduct2 +* `#11434 `__: MAINT: add PyPI classifier for Python 3.7 +* `#11436 `__: DOC: Document average return type +* `#11440 `__: BUG: fix interpolation with inf and NaN present +* `#11444 `__: DOC: Fix documentation for fromfunction +* `#11449 `__: BUG: Revert #10229 to fix DLL loads on Windows. +* `#11450 `__: MAINT/DEP: properly implement `ndarray.__pos__` +* `#11453 `__: BENCH: add ufunc argument parsing benchmarks. +* `#11455 `__: BENCH: belated addition of lcm, gcd to ufunc benchmark. +* `#11459 `__: NEP: Add some text to NEP 0 to clarify how a NEP is accepted +* `#11461 `__: MAINT: Add discussion link to NEP 15 +* `#11462 `__: Add NEP 22, a high level overview for the duck array work +* `#11463 `__: MAINT: Produce a more readable repr of argument packs in benchmark +* `#11464 `__: BUG: Don't convert inputs to `np.float64` in digitize +* `#11468 `__: BUG: Advanced indexing assignment incorrectly took 1-D fastpath +* `#11470 `__: BLD: Don't leave the build task running if runtests.py is interrupted +* `#11471 `__: MAINT: Remove python-side docstrings from add_newdocs. +* `#11472 `__: DOC: include NEP number on each NEP page +* `#11473 `__: MAINT: Move pytesttester outside of np.testing, to avoid creating... +* `#11474 `__: MAINT: Move add_newdocs into core, since it only adds docs to... +* `#11479 `__: BUG: Fix #define for ppc64 and ppc64le +* `#11480 `__: MAINT: move ufunc override code to umath and multiarray as much... +* `#11482 `__: DOC: Include warning in np.resize() docs +* `#11484 `__: BUG: Increase required cython version on python 3.7 +* `#11487 `__: DOC: extend sanity check message +* `#11488 `__: NEP: clarify bugfix policy for legacy RandomState. +* `#11501 `__: MAINT: Tidy cython invocation +* `#11503 `__: MAINT: improve error message for isposinf and isneginf on complex... +* `#11512 `__: DOC: Add templates for issues and PRs +* `#11514 `__: Prefer the same-python cython to the on-PATH cython +* `#11515 `__: BUG: decref of field title caused segfault +* `#11518 `__: MAINT: Speed up normalize_axis_tuple by about 30% +* `#11522 `__: BUG: fix np.load() of empty .npz file +* `#11525 `__: MAINT: Append `*FLAGS` instead of overriding +* `#11526 `__: ENH: add multi-field assignment helpers in np.lib.recfunctions +* `#11527 `__: DOC: Note that method is the polar form of Box-Muller. +* `#11528 `__: ENH: Add support for ipython latex printing to polynomial +* `#11531 `__: ENH: Add density argument to histogramdd. +* `#11533 `__: DOC: Fixed example code for cheb2poly and poly2cheb (see #11519) +* `#11534 `__: DOC: Minor improvements to np.concatenate docstring +* `#11535 `__: MAINT: Improve memory usage in PEP3118 format parsing +* `#11553 `__: DOC: Tiny typo on numpy/reference/arrays.dtypes.html +* `#11556 `__: BUG: Make assert_string_equal check str equality simply without... +* `#11559 `__: NEP: accept nep 0015 +* `#11560 `__: NEP: accept nep 0019 +* `#11562 `__: DOC: update release notes for LDFLAGS append behavior (gh-11525). +* `#11565 `__: MAINT: convert the doctests for polynomial to regular tests +* `#11566 `__: BLD: Do not use gcc warnings flags when 'gcc' is actually clang. +* `#11567 `__: TST: Integrate codecov testing +* `#11568 `__: BLD: Modify cpu detection and printing to get working aarch64... +* `#11571 `__: DOC: Updated array2string description +* `#11572 `__: DOC: Updated Slice Description +* `#11573 `__: TST: add broadcast_arrays() kwarg unit test for TypeError +* `#11580 `__: MAINT: refactor ufunc iter operand flags handling +* `#11591 `__: MAINT: update runtests.py node id example for pytest usage +* `#11592 `__: DOC: add Stefan van der Walt to Steering Council +* `#11593 `__: ENH: handle empty matrices in qr decomposition +* `#11594 `__: ENH: support for empty matrices in linalg.lstsq +* `#11595 `__: BUG:warn on Nan in minimum,maximum for scalars, float16 +* `#11596 `__: NEP: backwards compatibility and deprecation policy +* `#11598 `__: TST: Add Python 3.7 to CI testing +* `#11601 `__: BUG: Make np.array([[1], 2]) and np.array([1, [2]]) behave in... +* `#11606 `__: DOC: Post 1.15.0 release updates for master. +* `#11607 `__: DOC: minor clarification and typo fix to NEP 21 (outer indexing). +* `#11610 `__: TST: including C source line coverage for CI / codecov +* `#11611 `__: NEP: Add roadmap section and subdocuments to NEPs +* `#11613 `__: BUG: have geometric() raise ValueError on p=0 +* `#11615 `__: BUG: Clip uses wrong memory order in output +* `#11616 `__: DOC: add a brief note on "Protocols for methods" to NEP 18 +* `#11621 `__: DOC: Use "real symmetric" rather than "symmetric" in ``eigh``... +* `#11626 `__: DOC: Show plot in meshgrid example. +* `#11630 `__: DOC: Include the versionadded to the isnat documentation. +* `#11634 `__: MAINT: Filter Cython warnings in `__init__.py` +* `#11637 `__: ENH: np.angle: Remove unnecessary multiplication, and allow subclasses... +* `#11638 `__: ENH: Make expand_dims work on subclasses +* `#11642 `__: BUG: Fixes for unicode field names in Python 2 +* `#11643 `__: DOC: Insert up to date link to Spyder website in Dev Env doc... +* `#11644 `__: BUG: Fix doc source links to unwrap decorators +* `#11652 `__: BUG: Ensure singleton dimensions are not dropped when converting... +* `#11660 `__: ENH: Add Nan warnings for maximum, minimum on more dtypes +* `#11669 `__: BUG: Fix regression in `void_getitem` +* `#11670 `__: MAINT: trivially refactor mapped indexing +* `#11673 `__: DOC: Add geomspace to "See also" of linspace +* `#11679 `__: TST: ignore setup.py files for codecov reports +* `#11688 `__: DOC: Update broadcasting doc with current exception details +* `#11691 `__: BUG: Make matrix_power again work for object arrays. +* `#11692 `__: MAINT: Remove duplicate code. +* `#11693 `__: NEP: Mark NEP 18 as accepted +* `#11694 `__: BUG: Fix pickle and memoryview for datetime64, timedelta64 scalars +* `#11695 `__: BUG: Add missing PyErr_NoMemory after failing malloc +* `#11703 `__: MAINT: Remove np.pkgload, which seems to be unusable anyway +* `#11708 `__: BUG: Fix regression in np.loadtxt for bz2 text files in Python... +* `#11710 `__: BUG: Check for compiler used in env['CC'], then config_vars['CC'] +* `#11711 `__: BUG: Fix undefined functions on big-endian systems. +* `#11715 `__: TST: Fix urlopen stubbing. +* `#11717 `__: MAINT: Make einsum optimize default to False. +* `#11718 `__: BUG: Revert use of `console_scripts`. +* `#11722 `__: MAINT: Remove duplicate docstring and correct location of `__all__`... +* `#11725 `__: BUG: Fix Fortran kind detection for aarch64 & s390x. +* `#11727 `__: BUG: Fix printing of longdouble on ppc64le. +* `#11729 `__: DOC: fix capitalization of kilojoules +* `#11731 `__: DOC: fix typo in vectorize docstring +* `#11733 `__: DOC: recommend polynomial.Polynomial over np.polyfit +* `#11735 `__: BUG: Fix test sensitive to platform byte order. +* `#11738 `__: TST, MAINT: add lgtm.yml to tweak LGTM.com analysis +* `#11739 `__: BUG: disallow setting flag to writeable after fromstring, frombuffer +* `#11740 `__: BUG: Deprecation triggers segfault +* `#11742 `__: DOC: Reduce warnings and cleanup redundant c-api documentation +* `#11745 `__: DOC: Small docstring fixes for old polyfit. +* `#11754 `__: BUG: check return value of `_buffer_format_string` +* `#11755 `__: MAINT: Fix typos in random.hypergeometric's notes +* `#11756 `__: MAINT: Make assert_array_compare more generic. +* `#11765 `__: DOC: Move documentation from `help(ndarray.ctypes)` to `help(some_array.ctypes)` +* `#11771 `__: BUG: Make `random.shuffle` work on 1-D instances of `ndarray`... +* `#11774 `__: BUG: Fix regression in intersect1d. +* `#11778 `__: BUG: Avoid signed overflow in histogram +* `#11783 `__: MAINT: check `_append_char` return value +* `#11784 `__: MAINT: reformat line spacing before test methods +* `#11797 `__: DOC: Update docs after 1.15.1 release. +* `#11800 `__: DOC: document use when f2py is not in the PATH +* `#11802 `__: ENH: Use entry_points to install the f2py scripts. +* `#11805 `__: BUG: add type cast check for ediff1d +* `#11806 `__: DOC: Polybase augmented assignment notes +* `#11812 `__: DOC: edit setup.py docstring that is displayed on PyPI. +* `#11813 `__: BUG: fix array_split incorrect behavior with array size bigger... +* `#11814 `__: DOC, MAINT: Fixes for errstate() and README.md documentation. +* `#11817 `__: DOC: add examples and extend existing dos for polynomial subclasses +* `#11818 `__: TST: add missing tests for all polynomial subclass pow fns. +* `#11823 `__: TST: add test for array2string unexpected kwarg +* `#11830 `__: MAINT: reduce void type repr code duplication +* `#11834 `__: MAINT, DOC: Replace 'an' by 'a' in some docstrings. +* `#11837 `__: DOC: Make clear the connection between numpy types and C types +* `#11840 `__: BUG: Let 0-D arrays of Python timedelta convert to np.timedelta64. +* `#11843 `__: MAINT: remove surviving, unused, list comprehension +* `#11849 `__: TST: reorder duplicate mem_overlap.c compile +* `#11850 `__: DOC: add comment to remove fn after python 2 support is dropped +* `#11852 `__: BUG: timedelta64 now accepts NumPy ints +* `#11858 `__: DOC: add docstrings for numeric types +* `#11862 `__: BUG: Re-add `_ones_like` to numpy.core.umath. +* `#11864 `__: TST: Update travis testing to use latest virtualenv. +* `#11865 `__: DOC: add a Code of Conduct document. +* `#11866 `__: TST: Drop Python 3.4 testing +* `#11868 `__: MAINT: include benchmarks, complete docs, dev tool files in sdist. +* `#11870 `__: MAINT: dtype(unicode) should raise TypeError on failure +* `#11874 `__: BENCH: split out slow setup method in bench_shape_base.Block +* `#11877 `__: BUG: Fix memory leak in pyfragments.swg +* `#11880 `__: BUG: The multiarray/ufunc merge broke old wheels. +* `#11882 `__: DOC: Recommend the use of `np.ndim` over `np.isscalar`, and explain... +* `#11889 `__: BENCH: Split bench_function_base.Sort into Sort and SortWorst. +* `#11891 `__: MAINT: remove exec_command() from build_ext +* `#11892 `__: TST: Parametrize PEP3118 scalar tests. +* `#11893 `__: TST: Fix duplicated test name. +* `#11894 `__: TST: Parametrize f2py tests. +* `#11895 `__: TST: Parametrize some linalg tests over types. +* `#11896 `__: BUG: Fix matrix PendingDeprecationWarning suppression for pytest... +* `#11898 `__: MAINT: remove exec_command usage from ccompiler.py +* `#11899 `__: MAINT: remove exec_command from system_info.py +* `#11900 `__: MAINT: remove exec_command from gnu.py +* `#11901 `__: MAINT: remove exec_command usage in ibm.py +* `#11904 `__: Use pytest for some already-parametrized core tests +* `#11905 `__: TST: Start testing with "-std=c99" on travisCI. +* `#11906 `__: TST: add shippable ARMv8 to CI +* `#11907 `__: Link HOWTO_DOCUMENT to specific section on docstrings +* `#11909 `__: MAINT: flake8 cleanups +* `#11910 `__: MAINT: test, refactor design of recursive closures +* `#11912 `__: DOC: dtype offset and itemsize is limited by range of C int +* `#11914 `__: DOC: Clarify difference between PySequence_GETITEM, PyArray_GETITEM +* `#11916 `__: DEP: deprecate np.set_numeric_ops and friends +* `#11920 `__: TST: Fix 'def' test_numerictypes.py::TestSctypeDict to 'class'... +* `#11921 `__: MAINT: Don't rely on `__name__` in bitname - use the information... +* `#11922 `__: TST: Add tests for maximum_sctype +* `#11929 `__: DOC: #defining -> #define / Added a short explanation for Numeric +* `#11930 `__: DOC: fix scipy-sphinx-theme license path +* `#11932 `__: MAINT: Move `np.dtype.name.__get__` to python +* `#11933 `__: TST: Fix unit tests that used to call unittest.TestCase.fail +* `#11934 `__: NEP: Revert "NEP: Mark NEP 18 as accepted" +* `#11935 `__: MAINT: remove usage of exec_command in config.py +* `#11937 `__: MAINT: remove exec_command() from f2py init +* `#11941 `__: BUG: Ensure einsum(optimize=True) dispatches tensordot deterministically +* `#11943 `__: DOC: Add warning/clarification about backwards compat in NEP-18 +* `#11948 `__: DEP: finish making all comparisons to NaT false +* `#11949 `__: MAINT: Small tidy-ups to `np.core._dtype` +* `#11950 `__: MAINT: Extract tangential improvements made in #11175 +* `#11952 `__: MAINT: test NPY_INTERNAL_BUILD only if defined +* `#11953 `__: TST: codecov.yml improvements +* `#11957 `__: ENH: mark that large allocations can use huge pages +* `#11958 `__: TST: Add a test for np.pad where constant_values is an object +* `#11959 `__: MAINT: Explicitely cause pagefaults to happen before starting... +* `#11961 `__: TST: Add more tests for np.pad +* `#11962 `__: ENH: maximum lines of content to be read from numpy.loadtxt +* `#11965 `__: BENCH: Add a benchmark comparing block to copy in the 3D case +* `#11966 `__: MAINT: Rewrite shape normalization in pad function +* `#11967 `__: BUG: fix refcount leak in PyArray_AdaptFlexibleDType +* `#11971 `__: MAINT: Block algorithm with a single copy per call to `block` +* `#11973 `__: BUG: fix cached allocations without the GIL +* `#11976 `__: MAINT/DOC: Show the location of an empty list in np.block +* `#11979 `__: MAINT: Ensure that a copy of the array is returned when calling... +* `#11989 `__: BUG: Ensure boolean indexing of subclasses sets base correctly. +* `#11991 `__: MAINT: speed up `_block` by avoiding a recursive closure +* `#11996 `__: TST: Parametrize and break apart dtype tests +* `#11997 `__: MAINT: Extract string helpers to a new private file +* `#12002 `__: Revert "NEP: Revert "NEP: Mark NEP 18 as accepted"" +* `#12004 `__: BUG: Fix f2py compile function testing. +* `#12005 `__: ENH: initial implementation of core `__array_function__` machinery +* `#12008 `__: MAINT: Reassociate `np.cast` with the comment describing it +* `#12009 `__: MAINT: Eliminate the private `numerictypes._typestr` +* `#12011 `__: ENH: implementation of array_reduce_ex +* `#12012 `__: MAINT: Extract the crazy number of type aliases to their own... +* `#12014 `__: TST: prefer pytest.skip() over SkipTest +* `#12015 `__: TST: improve warnings parallel test safety +* `#12017 `__: NEP: add 3 missing data NEPs rescued from 2011-2012 +* `#12018 `__: MAINT: Simplify parts of `_type_aliases` +* `#12019 `__: DOC: MAINT: address comments @eric-wieser on NEP 24-26 PR. +* `#12020 `__: TST: Add tests for np.sctype2char +* `#12021 `__: DOC: Post NumPy 1.15.2 release updates.[ci skip] +* `#12024 `__: MAINT: Normalize axes the normal way in fftpack.py +* `#12027 `__: DOC: Add docstrings for abstract types in scalar type hierarchy +* `#12030 `__: DOC: use "import numpy as np" style +* `#12032 `__: BUG: check return value from PyArray_PromoteTypes +* `#12033 `__: TST: Mark check for f2py script xfail. +* `#12034 `__: MAINT: Add version deprecated to some deprecation messages. +* `#12035 `__: BUG: Fix memory leak in PY3K buffer code. +* `#12041 `__: MAINT: remove duplicate imports +* `#12042 `__: MAINT: cleanup and better document core/overrides.py +* `#12045 `__: BUG: fix memory leak of buffer format string +* `#12048 `__: BLD: pin sphinx to 1.7.9 +* `#12051 `__: TST: add macos azure testing to CI +* `#12054 `__: MAINT: avoid modifying mutable default values +* `#12056 `__: MAINT: The crackfortran function is called with an extra argument +* `#12057 `__: MAINT: remove unused imports +* `#12058 `__: MAINT: remove redundant assignment +* `#12060 `__: MAINT: remove unused stdlib imports +* `#12061 `__: MAINT: remove redundant imports +* `#12062 `__: BUG: `OBJECT_to_*` should check for errors +* `#12064 `__: MAINT: delay initialization of getlimits (circular imports) +* `#12072 `__: BUG: test_path() now uses Path.resolve() +* `#12073 `__: MAINT Avoid some memory copies in numpy.polynomial.hermite +* `#12079 `__: MAINT: Blacklist some MSVC complex functions. +* `#12081 `__: TST: add Windows test matrix to Azure CI +* `#12082 `__: TST: Add Python 3.5 to Azure windows CI. +* `#12088 `__: BUG: limit default for get_num_build_jobs() to 8 +* `#12089 `__: BUG: Fix in-place permutation +* `#12090 `__: TST, MAINT: Update pickling tests by making them loop over all... +* `#12091 `__: TST: Install pickle5 for CI testing with python 3.6/7 +* `#12093 `__: Provide information about what kind is actually not integer kind +* `#12099 `__: ENH: Validate dispatcher functions in array_function_dispatch +* `#12102 `__: TST: improve coverage of nd_grid +* `#12103 `__: MAINT: Add azure-pipeline status badge to README.md +* `#12106 `__: TST, MAINT: Skip some f2py tests on Mac. +* `#12108 `__: BUG: Allow boolean subtract in histogram +* `#12109 `__: TST: add unit test for issctype +* `#12112 `__: ENH: check getfield arguments to prevent invalid memory access +* `#12115 `__: ENH: `__array_function__` support for most of `numpy.core` +* `#12116 `__: ENH: `__array_function__` support for `np.lib`, part 1/2 +* `#12117 `__: ENH: `__array_function__` support for `np.fft` and `np.linalg` +* `#12119 `__: ENH: `__array_function__` support for `np.lib`, part 2/2 +* `#12120 `__: ENH: add timedelta modulus operator support (mm) +* `#12121 `__: MAINT: Clarify the error message for resize failure +* `#12123 `__: DEP: deprecate asscalar +* `#12124 `__: BUG: refactor float error status to support Alpine linux +* `#12125 `__: TST: expand cases in test_issctype() +* `#12127 `__: BUG: Fix memory leak in mapping.c +* `#12131 `__: BUG: fix PyDataType_ISBOOL +* `#12133 `__: MAINT, TST refactor pickle imports and tests +* `#12134 `__: DOC: Remove duplicated sentence in numpy.multiply +* `#12137 `__: TST: error tests for fill_diagonal() +* `#12138 `__: TST: error tests for diag_indices_from() +* `#12140 `__: DOC: fixups for NEP-18 based on the implementation +* `#12141 `__: DOC: minor tweak to CoC (update NumFOCUS contact address). +* `#12145 `__: MAINT: Update ndarrayobject.h `__cplusplus` block. +* `#12146 `__: MAINT: Fix typo in comment +* `#12147 `__: MAINT: Move duplicated type_reso_error code into a helper function +* `#12148 `__: DOC: document NEP-18 overrides in release notes +* `#12151 `__: TST: byte_bounds contiguity handling +* `#12153 `__: DOC, TST: cover setdiff1d assume_unique +* `#12154 `__: ENH: `__array_function__` for `np.core.defchararray` +* `#12155 `__: MAINT: Define Py_SETREF for pre-3.5.2 python and use in code +* `#12157 `__: ENH: Add support for third-party path-like objects by backporting... +* `#12159 `__: MAINT: remove unused nd_grid `__len__`. +* `#12163 `__: ENH: `__array_function__` for `np.einsum` and `np.block` +* `#12165 `__: Mark NEP 22 as accepted, and add "Informational" NEPs to NEP... +* `#12166 `__: NEP: Add zero-rank arrays historical info NEP +* `#12173 `__: NEP: add notes about updates to NEP-18 +* `#12174 `__: NEP 16 abstract arrays: rebased and marked as "Withdrawn" +* `#12175 `__: ENH: `__array_function__` for multiarray functions +* `#12176 `__: TST: add test for weighted histogram mismatch +* `#12177 `__: MAINT: remove unused `_assertSquareness()` +* `#12179 `__: MAINT: Move `_kind_to_stem` to `np.core._dtype`, so that it can... +* `#12180 `__: NEP: change toc titles, cross reference, mark 16 superseded +* `#12181 `__: MAINT: fix depreciation message typo for np.sum +* `#12185 `__: TST: test multi_dot with 2 arrays +* `#12199 `__: TST: add Azure CI triggers +* `#12209 `__: Delay import of distutils.msvccompiler to avoid warning on non-Windows. +* `#12211 `__: DOC: Clarify the examples for argmax and argmin +* `#12212 `__: MAINT: `ndarray.__repr__` should not rely on `__array_function__` +* `#12214 `__: TST: add test for tensorinv() +* `#12215 `__: TST: test dims match on lstsq() +* `#12216 `__: TST: test invalid histogram range +* `#12217 `__: TST: test histogram bins dims +* `#12219 `__: ENH: make matmul into a ufunc +* `#12222 `__: TST: unit tests for column_stack. +* `#12224 `__: BUG: Fix MaskedArray fill_value type conversion. +* `#12229 `__: MAINT: Fix typo in comment +* `#12236 `__: BUG: maximum, minimum no longer emit warnings on NAN +* `#12240 `__: BUG: Fix crash in repr of void subclasses +* `#12241 `__: TST: arg handling tests in histogramdd +* `#12243 `__: BUG: Fix misleading assert message in assert_almost_equal #12200 +* `#12245 `__: TST: tests for sort_complex() +* `#12246 `__: DOC: Update docs after NumPy 1.15.3 release. +* `#12249 `__: BUG: Dealloc cached buffer info +* `#12250 `__: DOC: add missing docs +* `#12251 `__: MAINT: improved error message when no `__array_function__` implementation... +* `#12254 `__: MAINT: Move ctype -> dtype conversion to python +* `#12257 `__: BUG: Fix fill value in masked array '==' and '!=' ops. +* `#12259 `__: TST: simplify how the different code paths for block are tested. +* `#12265 `__: BUG: Revert linspace import for concatenation funcs +* `#12266 `__: BUG: Avoid SystemErrors by checking the return value of PyPrint +* `#12268 `__: DOC: add broadcasting article from scipy old-wiki +* `#12270 `__: MAINT: set `__module__` for more `array_function_dispatch` uses +* `#12276 `__: MAINT: remove unused parse_index() +* `#12279 `__: NEP: tweak and mark NEP 0027 as final +* `#12280 `__: DEP: deprecate passing a generator to stack functions +* `#12281 `__: NEP: revise note for NEP 27 +* `#12285 `__: ENH: array does not need to be writable to use as input to take +* `#12286 `__: ENH: Do not emit compiler warning if forcing old API +* `#12288 `__: BUILD: force LGTM to use cython>=0.29 +* `#12291 `__: MAINT: `_set_out_array()` syntax fix +* `#12292 `__: MAINT: removed unused vars in f2py test code +* `#12299 `__: BUILD: use system python3 in the chroot +* `#12302 `__: DOC: Update the docstring of asfortranarray and ascontiguousarray +* `#12306 `__: TST: add 32-bit linux Azure CI job +* `#12312 `__: MAINT, TST: unreachable Python code paths +* `#12321 `__: MAINT: Simple speed-ups for getting overloaded types +* `#12326 `__: DOC: NumPy 1.15.4 post release documentation update. +* `#12328 `__: MAINT: Allow subclasses in `ndarray.__array_function__`. +* `#12330 `__: TST: test_tofile_fromfile now uses initialized memory +* `#12331 `__: DEV: change ASV benchmarks to run on Python 3.6 by default +* `#12338 `__: DOC: add a docstring for the function 'compare_chararrays' (See... +* `#12342 `__: BUG: Fix for np.dtype(ctypes.Structure) does not respect _pack_... +* `#12347 `__: DOC: typo in docstring numpy.random.beta, shape parameters must... +* `#12349 `__: TST, DOC: store circleci doc artifacts +* `#12353 `__: BUG: test, fix for threshold='nan' +* `#12354 `__: BUG: Fix segfault when an error occurs in np.fromfile +* `#12355 `__: BUG: fix a bug in npy_PyFile_Dup2 where it didn't return immediately... +* `#12357 `__: MAINT: Cleanup pavement file +* `#12358 `__: BUG: test, fix loading structured dtypes with padding +* `#12362 `__: MAINT: disable `__array_function__` dispatch unless environment... +* `#12363 `__: MAINT: update gfortran RPATH for AIX/Windows non-support. +* `#12364 `__: NEP: clarify the purpose of "types" in `__array_function__`. +* `#12366 `__: MAINT: Refactor sorting header file +* `#12372 `__: BUG: random: Fix handling of a=0 for numpy.random.weibull. +* `#12373 `__: MAINT: Improve error message for legal but unsupported PEP3118... +* `#12376 `__: BUG: do not override exception on import failure +* `#12377 `__: NEP: move nep 15 from accepted to final +* `#12378 `__: TST: Update complex long double precision tests. +* `#12380 `__: BUG: Fix for #10533 np.dtype(ctype) does not respect endianness +* `#12381 `__: BUG: graceful DataSource __del__ when __init__ fails +* `#12382 `__: ENH: set correct __module__ for objects in numpy's public API +* `#12388 `__: ENH: allow arrays for start and stop in {lin,log,geom}space +* `#12390 `__: DEV: remove shim added in 1.4 +* `#12391 `__: DEP: raise on a call to deprecated numpy.lib.function_base.unique +* `#12392 `__: DOC: Add release notes for ctypes improvements +* `#12398 `__: BUG: fix possible overlap issues with avx enabled +* `#12399 `__: DOC: Fix typo in polyint. Fixes #12386. +* `#12405 `__: ENH: Add support for `np.dtype(ctypes.Union)` +* `#12407 `__: BUG: Fall back to 'ascii' locale in build (if needed) +* `#12408 `__: BUG: multifield-view of MaskedArray gets bad fill_value +* `#12409 `__: MAINT: correct the dtype.descr docstring +* `#12413 `__: BUG: Do not double-quote arguments to the command line +* `#12414 `__: MAINT: Update cversion hash. +* `#12417 `__: BUG: Fix regression on np.dtype(ctypes.c_void_p) +* `#12419 `__: Fix PyArray_FillFunc function definitions +* `#12420 `__: gfortran needs -lpthread & -maix64(64 build) in AIX +* `#12422 `__: MNT: Reword error message about loading pickled data. +* `#12424 `__: BUG: Fix inconsistent cache keying in ndpointer +* `#12429 `__: MAINT: Update mailmap for 1.16.0 release. +* `#12431 `__: BUG/ENH: Fix use of ndpointer in return values +* `#12437 `__: MAINT: refactor datetime.c_metadata creation +* `#12439 `__: BUG: test, fix NPY_VISIBILITY_HIDDEN on gcc, which becomes NPY_NO_EXPORT +* `#12440 `__: BUG: don't override original errors when casting inside np.dot()... +* `#12443 `__: MAINT Use set litterals +* `#12445 `__: MAINT: Use list and dict comprehension when possible +* `#12446 `__: MAINT: Fixups to new functions in np.lib.recfunctions +* `#12447 `__: ENH: add back the multifield copy->view change +* `#12448 `__: MAINT: Review F401,F841,F842 flake8 errors (unused variables... +* `#12455 `__: TST: use condition directive for Azure 2.7 check +* `#12458 `__: MAINT, DOC: fix Azure README badge +* `#12464 `__: BUG: IndexError for empty list on structured MaskedArray. +* `#12466 `__: TST: use openblas for Windows CI +* `#12470 `__: MAINT: remove wrapper functions from numpy.core.multiarray +* `#12471 `__: ENH: override support for np.linspace and friends +* `#12474 `__: TST: enable dispatcher test coverage +* `#12477 `__: DOC: fix example for __call__. See #12451 +* `#12486 `__: DOC: Update copyright year in the license +* `#12488 `__: ENH: implement matmul on NDArrayOperatorsMixin +* `#12493 `__: BUG: fix records.fromfile fails to read data >4 GB +* `#12494 `__: BUG: test, fix matmul, dot for vector array with stride[i]=0 +* `#12498 `__: TST: sync Azure Win openblas +* `#12501 `__: MAINT: removed word/typo from comment in site.cfg.example +* `#12556 `__: BUG: only override vector size for avx code for 1.16 +* `#12562 `__: DOC, MAINT: Make `PYVER = 3` in doc/Makefile. +* `#12563 `__: DOC: more doc updates for structured arrays +* `#12564 `__: BUG: fix an unsafe PyTuple_GET_ITEM call +* `#12565 `__: Fix lgtm.com C/C++ build +* `#12567 `__: BUG: reorder operations for VS2015 +* `#12568 `__: BUG: fix improper use of C-API +* `#12569 `__: BUG: Make new-lines in compiler error messages print to the console +* `#12570 `__: MAINT: don't check alignment size=0 arrays (RELAXED_STRIDES) +* `#12573 `__: BUG: fix refcount issue caused by #12524 +* `#12580 `__: BUG: fix segfault in ctypeslib with obj being collected +* `#12581 `__: TST: activate shippable maintenance branches +* `#12582 `__: BUG: fix f2py pep338 execution method +* `#12587 `__: BUG: Make `arr.ctypes.data` hold a reference to the underlying... +* `#12588 `__: BUG: check for errors after PyArray_DESCR_REPLACE +* `#12590 `__: DOC, MAINT: Prepare for 1.16.0rc1 release. +* `#12603 `__: DOC: Fix markup in 1.16.0 release notes. +* `#12621 `__: BUG: longdouble with elsize 12 is never uint alignable. +* `#12622 `__: BUG: Add missing free in ufunc dealloc +* `#12623 `__: MAINT: add test for 12-byte alignment +* `#12655 `__: BUG: fix uint alignment asserts in lowlevel loops +* `#12656 `__: BENCH: don't fail at import time with old Numpy +* `#12657 `__: DOC: update 2018 -> 2019 +* `#12705 `__: ENH: Better links in documentation +* `#12706 `__: MAINT: Further fixups to uint alignment checks +* `#12707 `__: BUG: Add 'sparc' to platforms implementing 16 byte reals. +* `#12708 `__: TST: Fix endianness in unstuctured_to_structured test +* `#12710 `__: TST: pin Azure brew version for stability. diff --git a/doc/changelog/1.16.1-changelog.rst b/doc/changelog/1.16.1-changelog.rst new file mode 100644 index 000000000000..30e0e3a2468b --- /dev/null +++ b/doc/changelog/1.16.1-changelog.rst @@ -0,0 +1,62 @@ + +Contributors +============ + +A total of 16 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Antoine Pitrou +* Arcesio Castaneda Medina + +* Charles Harris +* Chris Markiewicz + +* Christoph Gohlke +* Christopher J. Markiewicz + +* Daniel Hrisca + +* EelcoPeacs + +* Eric Wieser +* Kevin Sheppard +* Matti Picus +* OBATA Akio + +* Ralf Gommers +* Sebastian Berg +* Stephan Hoyer +* Tyler Reddy + +Pull requests merged +==================== + +A total of 33 pull requests were merged for this release. + +* `#12754 `__: BUG: Check paths are unicode, bytes or path-like +* `#12767 `__: ENH: add mm->q floordiv +* `#12768 `__: ENH: port np.core.overrides to C for speed +* `#12769 `__: ENH: Add np.ctypeslib.as_ctypes_type(dtype), improve `np.ctypeslib.as_ctypes` +* `#12771 `__: BUG: Ensure probabilities are not NaN in choice +* `#12772 `__: MAINT: add warning to numpy.distutils for LDFLAGS append behavior. +* `#12773 `__: ENH: add "max difference" messages to np.testing.assert_array_equal... +* `#12774 `__: BUG: Fix incorrect/missing reference cleanups found using valgrind +* `#12776 `__: BUG,TST: Remove the misguided `run_command` that wraps subprocess +* `#12777 `__: DOC, TST: Clean up matplotlib imports +* `#12781 `__: BUG: Fix reference counting for subarrays containing objects +* `#12782 `__: BUG: Ensure failing memory allocations are reported +* `#12784 `__: BUG: Fix leak of void scalar buffer info +* `#12788 `__: MAINT: Change the order of checking for local file. +* `#12808 `__: BUG: loosen kwargs requirements in ediff1d +* `#12809 `__: DOC: clarify the extend of __array_function__ support in NumPy... +* `#12810 `__: BUG: Check that dtype or formats arguments are not None. +* `#12811 `__: BUG: fix f2py problem to build wrappers using PGI's Fortran +* `#12812 `__: BUG: double decref of dtype in failure codepath. Test and fix +* `#12813 `__: BUG, DOC: test, fix that f2py.compile accepts str and bytes,... +* `#12816 `__: BUG: resolve writeback in arr_insert failure paths +* `#12820 `__: ENH: Add mm->qm divmod +* `#12843 `__: BUG: fix to check before apply `shlex.split` +* `#12844 `__: BUG: Fix SystemError when pickling datetime64 array with pickle5 +* `#12845 `__: BUG: Fix rounding of denormals in double and float to half casts. +* `#12868 `__: TEST: pin mingw version +* `#12869 `__: BUG: ndarrays pickled by 1.16 cannot be loaded by 1.15.4 and... +* `#12870 `__: BUG: do not Py_DECREF NULL pointer +* `#12890 `__: ENH: add _dtype_ctype to namespace for freeze analysis +* `#12891 `__: BUG: fail if old multiarray module detected +* `#12898 `__: BUG: Do not double-quote arguments passed on to the linker +* `#12899 `__: BUG: Do not insert extra double quote into preprocessor macros +* `#12902 `__: DOC: Prepare for 1.16.1 release. diff --git a/doc/changelog/1.16.2-changelog.rst b/doc/changelog/1.16.2-changelog.rst new file mode 100644 index 000000000000..3cf0cc566a58 --- /dev/null +++ b/doc/changelog/1.16.2-changelog.rst @@ -0,0 +1,25 @@ + +Contributors +============ + +A total of 5 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Charles Harris +* Eric Wieser +* Matti Picus +* Tyler Reddy +* Tony LaTorre + + +Pull requests merged +==================== + +A total of 7 pull requests were merged for this release. + +* `#12909 `__: TST: fix vmImage dispatch in Azure +* `#12923 `__: MAINT: remove complicated test of multiarray import failure mode +* `#13020 `__: BUG: fix signed zero behavior in npy_divmod +* `#13026 `__: MAINT: Add functions to parse shell-strings in the platform-native... +* `#13028 `__: BUG: Fix regression in parsing of F90 and F77 environment variables +* `#13038 `__: BUG: parse shell escaping in extra_compile_args and extra_link_args +* `#13041 `__: BLD: Windows absolute path DLL loading diff --git a/doc/changelog/1.16.3-changelog.rst b/doc/changelog/1.16.3-changelog.rst new file mode 100644 index 000000000000..96291c0aee30 --- /dev/null +++ b/doc/changelog/1.16.3-changelog.rst @@ -0,0 +1,55 @@ + +Contributors +============ + +A total of 16 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Andreas Schwab +* Bharat Raghunathan + +* Bran + +* Charles Harris +* Eric Wieser +* Jakub Wilk +* Kevin Sheppard +* Marten van Kerkwijk +* Matti Picus +* Paul Ivanov +* Ralf Gommers +* Sebastian Berg +* Tyler Reddy +* Warren Weckesser +* Yu Feng +* adeak + + +Pull requests merged +==================== + +A total of 26 pull requests were merged for this release. + +* `#13072 `__: BUG: Fixes to numpy.distutils.Configuration.get_version (#13056) +* `#13082 `__: BUG: Fix errors in string formatting while producing an error +* `#13083 `__: BUG: Convert fortran flags in environment variable +* `#13084 `__: BUG: Remove error-prone borrowed reference handling +* `#13085 `__: BUG: Add error checks when converting integers to datetime types +* `#13091 `__: BUG: Remove our patched version of `distutils.split_quoted` +* `#13141 `__: BUG: Fix testsuite failures on ppc and riscv +* `#13142 `__: BUG: Fix parameter validity checks in ``random.choice`` +* `#13143 `__: BUG: Ensure linspace works on object input. +* `#13144 `__: BLD: fix include list for sdist building. +* `#13145 `__: BUG: __array_interface__ offset was always ignored +* `#13274 `__: MAINT: f2py: Add a cast to avoid a compiler warning. +* `#13275 `__: BUG, MAINT: fix reference count error on invalid input to ndarray.flat +* `#13276 `__: ENH: Cast covariance to double in random mvnormal +* `#13278 `__: BUG: Fix null pointer dereference in PyArray_DTypeFromObjectHelper +* `#13339 `__: BUG: Use C call to sysctlbyname for AVX detection on MacOS. +* `#13340 `__: BUG: Fix crash when calling savetxt on a padded array +* `#13341 `__: BUG: ufunc.at iteration variable size fix +* `#13342 `__: DOC: Add as_ctypes_type to the documentation +* `#13350 `__: BUG: Return the coefficients array directly +* `#13351 `__: BUG/MAINT: Tidy typeinfo.h and .c +* `#13359 `__: BUG: Make allow_pickle=False the default for loading +* `#13360 `__: DOC: fix some doctest failures +* `#13363 `__: BUG/MAINT: Tidy typeinfo.h and .c +* `#13381 `__: BLD: address mingw-w64 issue. Follow-up to gh-9977 +* `#13382 `__: REL: Prepare for the NumPy release. diff --git a/doc/changelog/1.16.4-changelog.rst b/doc/changelog/1.16.4-changelog.rst new file mode 100644 index 000000000000..b32881c371c1 --- /dev/null +++ b/doc/changelog/1.16.4-changelog.rst @@ -0,0 +1,39 @@ + +Contributors +============ + +A total of 10 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Charles Harris +* Eric Wieser +* Dennis Zollo + +* Hunter Damron + +* Jingbei Li + +* Kevin Sheppard +* Matti Picus +* Nicola Soranzo + +* Sebastian Berg +* Tyler Reddy + +Pull requests merged +==================== + +A total of 16 pull requests were merged for this release. + +* `#13392 `__: BUG: Some PyPy versions lack PyStructSequence_InitType2. +* `#13394 `__: MAINT, DEP: Fix deprecated ``assertEquals()`` +* `#13396 `__: BUG: Fix structured_to_unstructured on single-field types (backport) +* `#13549 `__: BLD: Make CI pass again with pytest 4.5 +* `#13552 `__: TST: Register markers in conftest.py. +* `#13559 `__: BUG: Removes ValueError for empty kwargs in arraymultiter_new +* `#13560 `__: BUG: Add TypeError to accepted exceptions in crackfortran. +* `#13561 `__: BUG: Handle subarrays in descr_to_dtype +* `#13562 `__: BUG: Protect generators from log(0.0) +* `#13563 `__: BUG: Always return views from structured_to_unstructured when... +* `#13564 `__: BUG: Catch stderr when checking compiler version +* `#13565 `__: BUG: longdouble(int) does not work +* `#13587 `__: BUG: distutils/system_info.py fix missing subprocess import (#13523) +* `#13620 `__: BUG,DEP: Fix writeable flag setting for arrays without base +* `#13641 `__: MAINT: Prepare for the 1.16.4 release. +* `#13644 `__: BUG: special case object arrays when printing rel-, abs-error diff --git a/doc/changelog/1.16.5-changelog.rst b/doc/changelog/1.16.5-changelog.rst new file mode 100644 index 000000000000..c609d214c5ef --- /dev/null +++ b/doc/changelog/1.16.5-changelog.rst @@ -0,0 +1,54 @@ + +Contributors +============ + +A total of 18 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Alexander Shadchin +* Allan Haldane +* Bruce Merry + +* Charles Harris +* Colin Snyder + +* Dan Allan + +* Emile + +* Eric Wieser +* Grey Baker + +* Maksim Shabunin + +* Marten van Kerkwijk +* Matti Picus +* Peter Andreas Entschev + +* Ralf Gommers +* Richard Harris + +* Sebastian Berg +* Sergei Lebedev + +* Stephan Hoyer + +Pull requests merged +==================== + +A total of 23 pull requests were merged for this release. + +* `#13742 `__: ENH: Add project URLs to setup.py +* `#13823 `__: TEST, ENH: fix tests and ctypes code for PyPy +* `#13845 `__: BUG: use npy_intp instead of int for indexing array +* `#13867 `__: TST: Ignore DeprecationWarning during nose imports +* `#13905 `__: BUG: Fix use-after-free in boolean indexing +* `#13933 `__: MAINT/BUG/DOC: Fix errors in _add_newdocs +* `#13984 `__: BUG: fix byte order reversal for datetime64[ns] +* `#13994 `__: MAINT,BUG: Use nbytes to also catch empty descr during allocation +* `#14042 `__: BUG: np.array cleared errors occurred in PyMemoryView_FromObject +* `#14043 `__: BUG: Fixes for Undefined Behavior Sanitizer (UBSan) errors. +* `#14044 `__: BUG: ensure that casting to/from structured is properly checked. +* `#14045 `__: MAINT: fix histogram*d dispatchers +* `#14046 `__: BUG: further fixup to histogram2d dispatcher. +* `#14052 `__: BUG: Replace contextlib.suppress for Python 2.7 +* `#14056 `__: BUG: fix compilation of 3rd party modules with Py_LIMITED_API... +* `#14057 `__: BUG: Fix memory leak in dtype from dict constructor +* `#14058 `__: DOC: Document array_function at a higher level. +* `#14084 `__: BUG, DOC: add new recfunctions to `__all__` +* `#14162 `__: BUG: Remove stray print that causes a SystemError on python 3.7 +* `#14297 `__: TST: Pin pytest version to 5.0.1. +* `#14322 `__: ENH: Enable huge pages in all Linux builds +* `#14346 `__: BUG: fix behavior of structured_to_unstructured on non-trivial... +* `#14382 `__: REL: Prepare for the NumPy 1.16.5 release. diff --git a/doc/changelog/1.16.6-changelog.rst b/doc/changelog/1.16.6-changelog.rst new file mode 100644 index 000000000000..62ff46c34827 --- /dev/null +++ b/doc/changelog/1.16.6-changelog.rst @@ -0,0 +1,36 @@ + +Contributors +============ + +A total of 10 people contributed to this release. + +* CakeWithSteak +* Charles Harris +* Chris Burr +* Eric Wieser +* Fernando Saravia +* Lars Grueter +* Matti Picus +* Maxwell Aladago +* Qiming Sun +* Warren Weckesser + +Pull requests merged +==================== + +A total of 14 pull requests were merged for this release. + +* `#14211 `__: BUG: Fix uint-overflow if padding with linear_ramp and negative... +* `#14275 `__: BUG: fixing to allow unpickling of PY3 pickles from PY2 +* `#14340 `__: BUG: Fix misuse of .names and .fields in various places (backport... +* `#14423 `__: BUG: test, fix regression in converting to ctypes. +* `#14434 `__: BUG: Fixed maximum relative error reporting in assert_allclose +* `#14509 `__: BUG: Fix regression in boolean matmul. +* `#14686 `__: BUG: properly define PyArray_DescrCheck +* `#14853 `__: BLD: add 'apt update' to shippable +* `#14854 `__: BUG: Fix _ctypes class circular reference. (#13808) +* `#14856 `__: BUG: Fix `np.einsum` errors on Power9 Linux and z/Linux +* `#14863 `__: BLD: Prevent -flto from optimising long double representation... +* `#14864 `__: BUG: lib: Fix histogram problem with signed integer arrays. +* `#15172 `__: ENH: Backport improvements to testing functions. +* `#15191 `__: REL: Prepare for 1.16.6 release. diff --git a/doc/changelog/1.17.0-changelog.rst b/doc/changelog/1.17.0-changelog.rst new file mode 100644 index 000000000000..8179c180bad3 --- /dev/null +++ b/doc/changelog/1.17.0-changelog.rst @@ -0,0 +1,695 @@ + +Contributors +============ + +A total of 150 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Aaron Voelker + +* Abdur Rehman + +* Abdur-Rahmaan Janhangeer + +* Abhinav Sagar + +* Adam J. Stewart + +* Adam Orr + +* Albert Thomas + +* Alex Watt + +* Alexander Blinne + +* Alexander Shadchin +* Allan Haldane +* Ander Ustarroz + +* Andras Deak +* Andrea Pattori + +* Andreas Schwab +* Andrew Naguib + +* Andy Scholand + +* Ankit Shukla + +* Anthony Sottile +* Antoine Pitrou +* Antony Lee +* Arcesio Castaneda Medina + +* Assem + +* Bernardt Duvenhage + +* Bharat Raghunathan + +* Bharat123rox + +* Bran + +* Bruce Merry + +* Charles Harris +* Chirag Nighut + +* Christoph Gohlke +* Christopher Whelan + +* Chuanzhu Xu + +* Colin Snyder + +* Dan Allan + +* Daniel Hrisca +* Daniel Lawrence + +* Debsankha Manik + +* Dennis Zollo + +* Dieter Werthmüller + +* Dominic Jack + +* EelcoPeacs + +* Eric Larson +* Eric Wieser +* Fabrice Fontaine + +* Gary Gurlaskie + +* Gregory Lee + +* Gregory R. Lee +* Guillaume Horel + +* Hameer Abbasi +* Haoyu Sun + +* Harmon + +* He Jia + +* Hunter Damron + +* Ian Sanders + +* Ilja + +* Isaac Virshup + +* Isaiah Norton + +* Jackie Leng + +* Jaime Fernandez +* Jakub Wilk +* Jan S. (Milania1) + +* Jarrod Millman +* Javier Dehesa + +* Jeremy Lay + +* Jim Turner + +* Jingbei Li + +* Joachim Hereth + +* Johannes Hampp + +* John Belmonte + +* John Kirkham +* John Law + +* Jonas Jensen +* Joseph Fox-Rabinovitz +* Joseph Martinot-Lagarde +* Josh Wilson +* Juan Luis Cano Rodríguez +* Julian Taylor +* Jérémie du Boisberranger + +* Kai Striega + +* Katharine Hyatt + +* Kevin Sheppard +* Kexuan Sun +* Kiko Correoso + +* Kriti Singh + +* Lars Grueter + +* Luis Pedro Coelho +* Maksim Shabunin + +* Manvi07 + +* Mark Harfouche +* Marten van Kerkwijk +* Martin Reinecke + +* Matthew Brett +* Matthias Bussonnier +* Matti Picus +* Michel Fruchart + +* Mike Lui + +* Mike Taves + +* Min ho Kim + +* Mircea Akos Bruma +* Nick Minkyu Lee +* Nick Papior +* Nick R. Papior + +* Nicola Soranzo + +* Nimish Telang + +* OBATA Akio + +* Oleksandr Pavlyk +* Ori Broda + +* Paul Ivanov +* Pauli Virtanen +* Peter Andreas Entschev + +* Peter Bell + +* Pierre de Buyl +* Piyush Jaipuriayar + +* Prithvi MK + +* Raghuveer Devulapalli + +* Ralf Gommers +* Richard Harris + +* Rishabh Chakrabarti + +* Riya Sharma + +* Robert Kern +* Roman Yurchak +* Ryan Levy + +* Sebastian Berg +* Sergei Lebedev + +* Shekhar Prasad Rajak + +* Stefan van der Walt +* Stephan Hoyer +* Steve Stagg + +* SuryaChand P + +* Søren Rasmussen + +* Thibault Hallouin + +* Thomas A Caswell +* Tobias Uelwer + +* Tony LaTorre + +* Toshiki Kataoka +* Tyler Moncur + +* Tyler Reddy +* Valentin Haenel +* Vrinda Narayan + +* Warren Weckesser +* Weitang Li +* Wojtek Ruszczewski +* Yu Feng +* Yu Kobayashi + +* Yury Kirienko + +* aashuli + +* luzpaz +* parul + +* spacescientist + + +Pull requests merged +==================== + +A total of 532 pull requests were merged for this release. + +* `#4808 `__: ENH: Make the `mode` parameter of np.pad default to 'constant' +* `#8131 `__: BUG: Fix help() formatting for deprecated functions. +* `#8159 `__: ENH: Add import time benchmarks. +* `#8641 `__: BUG: Preserve types of empty arrays in ix_ when known +* `#8662 `__: ENH: preserve subclasses in ufunc.outer +* `#9330 `__: ENH: Make errstate a ContextDecorator in Python3 +* `#10308 `__: API: Make MaskedArray.mask return a view, rather than the underlying... +* `#10417 `__: ENH: Allow dtype objects to be indexed with multiple fields at... +* `#10723 `__: BUG: longdouble(int) does not work +* `#10741 `__: ENH: Implement `np.floating.as_integer_ratio` +* `#10855 `__: ENH: Adding a count parameter to np.unpackbits +* `#11230 `__: MAINT: More cleanup of einsum +* `#11233 `__: BUG: ensure i0 does not change the shape. +* `#11358 `__: MAINT: Rewrite numpy.pad without concatenate +* `#11684 `__: BUG: Raise when unravel_index, ravel_multi_index are given empty... +* `#11689 `__: DOC: Add ref docs for C generic types. +* `#11721 `__: BUG: Make `arr.ctypes.data` hold onto a reference to the underlying... +* `#11829 `__: MAINT: Use textwrap.dedent in f2py tests +* `#11859 `__: BUG: test and fix np.dtype('i,L') #5645 +* `#11888 `__: ENH: Add pocketfft sources to numpy for testing, benchmarks,... +* `#11977 `__: BUG: reference cycle in np.vectorize +* `#12025 `__: DOC: add detail for 'where' argument in ufunc +* `#12152 `__: TST: Added tests for np.tensordot() +* `#12201 `__: TST: coverage for _commonType() +* `#12234 `__: MAINT: refactor PyArray_AdaptFlexibleDType to return a meaningful... +* `#12239 `__: BUG: polyval returned non-masked arrays for masked input. +* `#12253 `__: DOC, TST: enable doctests +* `#12308 `__: ENH: add mm->q floordiv +* `#12317 `__: ENH: port np.core.overrides to C for speed +* `#12333 `__: DOC: update description of the Dirichlet distribution +* `#12418 `__: ENH: Add timsort to npysort +* `#12428 `__: ENH: always use zip64, upgrade pickle protocol to 3 +* `#12456 `__: ENH: Add np.ctypeslib.as_ctypes_type(dtype), improve `np.ctypeslib.as_ctypes` +* `#12457 `__: TST: openblas for Azure MacOS +* `#12463 `__: DOC: fix docstrings for broadcastable inputs in ufunc +* `#12502 `__: TST: Azure Python version fix +* `#12506 `__: MAINT: Prepare master for 1.17.0 development. +* `#12508 `__: DOC, MAINT: Make `PYVER = 3` in doc/Makefile. +* `#12511 `__: BUG: don't check alignment of size=0 arrays (RELAXED_STRIDES) +* `#12512 `__: added template-generated files to .gitignore +* `#12519 `__: ENH/DEP: Use a ufunc under the hood for ndarray.clip +* `#12522 `__: BUG: Make new-lines in compiler error messages print to the console +* `#12524 `__: BUG: fix improper use of C-API +* `#12526 `__: BUG: reorder operations for VS2015 +* `#12527 `__: DEV: Fix lgtm.com C/C++ build +* `#12528 `__: BUG: fix an unsafe PyTuple_GET_ITEM call +* `#12532 `__: DEV: add ctags option file +* `#12534 `__: DOC: Fix desc. of Ellipsis behavior in reference +* `#12537 `__: DOC: Change 'num' to 'np' +* `#12538 `__: MAINT: remove VC 9.0 from CI +* `#12539 `__: DEV: remove travis 32 bit job since it is running on azure +* `#12543 `__: TST: wheel-match Linux openblas in CI +* `#12544 `__: BUG: fix refcount issue caused by #12524 +* `#12545 `__: BUG: Ensure probabilities are not NaN in choice +* `#12546 `__: BUG: check for errors after PyArray_DESCR_REPLACE +* `#12547 `__: ENH: Cast covariance to double in random mvnormal +* `#12549 `__: TST: relax codecov project threshold +* `#12551 `__: MAINT: add warning to numpy.distutils for LDFLAGS append behavior. +* `#12552 `__: BENCH: Improve benchmarks for numpy.pad +* `#12554 `__: DOC: more doc updates for structured arrays +* `#12555 `__: BUG: only override vector size for avx code +* `#12560 `__: DOC: fix some doctest failures +* `#12566 `__: BUG: fix segfault in ctypeslib with obj being collected +* `#12571 `__: Revert "Merge pull request #11721 from eric-wieser/fix-9647" +* `#12572 `__: BUG: Make `arr.ctypes.data` hold a reference to the underlying... +* `#12575 `__: ENH: improve performance for numpy.core.records.find_duplicate +* `#12577 `__: BUG: fix f2py pep338 execution method +* `#12578 `__: TST: activate shippable maintenance branches +* `#12583 `__: TST: add test for 'python -mnumpy.f2py' +* `#12584 `__: Clarify skiprows in loadtxt +* `#12586 `__: ENH: Implement radix sort +* `#12589 `__: MAINT: Update changelog.py for Python 3. +* `#12591 `__: ENH: add "max difference" messages to np.testing.assert_array_equal +* `#12592 `__: BUG,TST: Remove the misguided `run_command` that wraps subprocess +* `#12593 `__: ENH,WIP: Use richer exception types for ufunc type resolution... +* `#12594 `__: DEV, BUILD: add pypy3 to azure CI +* `#12596 `__: ENH: improve performance of numpy.core.records.fromarrays +* `#12601 `__: DOC: Correct documentation of `numpy.delete` obj parameter. +* `#12602 `__: DOC: Update RELEASE_WALKTHROUGH.rst.txt. +* `#12604 `__: BUG: Check that dtype and formats arguments for None. +* `#12606 `__: DOC: Document NPY_SORTKIND parameter in PyArray_Sort +* `#12608 `__: MAINT: Use `*.format` for some strings. +* `#12609 `__: ENH: Deprecate writeable broadcast_array +* `#12610 `__: TST: Update runtests.py to specify C99 for gcc. +* `#12611 `__: BUG: longdouble with elsize 12 is never uint alignable +* `#12612 `__: TST: Update `travis-test.sh` for C99 +* `#12616 `__: BLD: Fix minimum Python version in setup.py +* `#12617 `__: BUG: Add missing free in ufunc dealloc +* `#12618 `__: MAINT: add test for 12-byte alignment +* `#12620 `__: BLD: move -std=c99 addition to CFLAGS to Azure config +* `#12624 `__: BUG: Fix incorrect/missing reference cleanups found using valgrind +* `#12626 `__: BUG: fix uint alignment asserts in lowlevel loops +* `#12631 `__: BUG: fix f2py problem to build wrappers using PGI's Fortran +* `#12634 `__: DOC, TST: remove "agg" setting from docs +* `#12639 `__: BENCH: don't fail at import time with old Numpy +* `#12641 `__: DOC: update 2018 -> 2019 +* `#12644 `__: ENH: where for ufunc reductions +* `#12645 `__: DOC: Minor fix to pocketfft release note +* `#12650 `__: BUG: Fix reference counting for subarrays containing objects +* `#12651 `__: DOC: SimpleNewFromDescr cannot be given NULL for descr +* `#12666 `__: BENCH: add asv nanfunction benchmarks +* `#12668 `__: ENH: Improve error messages for non-matching shapes in concatenate. +* `#12671 `__: TST: Fix endianness in unstuctured_to_structured test +* `#12672 `__: BUG: Add 'sparc' to platforms implementing 16 byte reals. +* `#12677 `__: MAINT: Further fixups to uint alignment checks +* `#12679 `__: ENH: remove "Invalid value" warnings from median, percentile +* `#12680 `__: BUG: Ensure failing memory allocations are reported +* `#12683 `__: ENH: add mm->qm divmod +* `#12684 `__: DEV: remove _arg from public API, add matmul to benchmark ufuncs +* `#12685 `__: BUG: Make pocketfft handle long doubles. +* `#12687 `__: ENH: Better links in documentation +* `#12690 `__: WIP, ENH: add _nan_mask function +* `#12693 `__: ENH: Add a hermitian argument to `pinv` and `svd`, matching `matrix_rank` +* `#12696 `__: BUG: Fix leak of void scalar buffer info +* `#12698 `__: DOC: improve comments in copycast_isaligned +* `#12700 `__: ENH: chain additional exception on ufunc method lookup error +* `#12702 `__: TST: Check FFT results for C/Fortran ordered and non contiguous... +* `#12704 `__: TST: pin Azure brew version for stability +* `#12709 `__: TST: add ppc64le to Travis CI matrix +* `#12713 `__: BUG: loosen kwargs requirements in ediff1d +* `#12722 `__: BUG: Fix rounding of denormals in double and float to half casts... +* `#12723 `__: BENCH: Include other sort benchmarks +* `#12724 `__: BENCH: quiet DeprecationWarning +* `#12727 `__: DOC: fix and doctest tutorial +* `#12728 `__: DOC: clarify the suffix of single/extended precision math constants +* `#12729 `__: DOC: Extend documentation of `ndarray.tolist` +* `#12731 `__: DOC: Update release notes and changelog after 1.16.0 release. +* `#12733 `__: DOC: clarify the extend of __array_function__ support in NumPy... +* `#12741 `__: DOC: fix generalized eigenproblem reference in "NumPy for MATLAB... +* `#12743 `__: BUG: Fix crash in error message formatting introduced by gh-11230 +* `#12748 `__: BUG: Fix SystemError when pickling datetime64 array with pickle5 +* `#12757 `__: BUG: Added parens to macro argument expansions +* `#12758 `__: DOC: Update docstring of diff() to use 'i' not 'n' +* `#12762 `__: MAINT: Change the order of checking for locale file and import... +* `#12783 `__: DOC: document C99 requirement in dev guide +* `#12787 `__: DOC: remove recommendation to add main for testing +* `#12805 `__: BUG: double decref of dtype in failure codepath. Test and fix +* `#12807 `__: BUG, DOC: test, fix that f2py.compile accepts str and bytes,... +* `#12814 `__: BUG: resolve writeback in arr_insert failure paths +* `#12815 `__: BUG: Fix testing of f2py.compile from strings. +* `#12818 `__: DOC: remove python2-only methods, small cleanups +* `#12824 `__: BUG: fix to check before apply `shlex.split` +* `#12830 `__: ENH: __array_function__ updates for NumPy 1.17.0 +* `#12831 `__: BUG: Catch stderr when checking compiler version +* `#12842 `__: BUG: ndarrays pickled by 1.16 cannot be loaded by 1.15.4 and... +* `#12846 `__: BUG: fix signed zero behavior in npy_divmod +* `#12850 `__: BUG: fail if old multiarray module detected +* `#12851 `__: TEST: use xenial by default for travis +* `#12854 `__: BUG: do not Py_DECREF NULL pointer +* `#12857 `__: STY: simplify code +* `#12863 `__: TEST: pin mingw version +* `#12866 `__: DOC: link to benchmarking info +* `#12867 `__: TST: Use same OpenBLAS build for testing as for current wheels. +* `#12871 `__: ENH: add c-imported modules to namespace for freeze analysis +* `#12877 `__: Remove deprecated ``sudo: false`` from .travis.yml +* `#12879 `__: DEP: deprecate exec_command +* `#12885 `__: DOC: fix math formatting of np.linalg.lstsq docs +* `#12886 `__: DOC: add missing character routines, fix #8578 +* `#12887 `__: BUG: Fix np.rec.fromarrays on arrays which are already structured +* `#12889 `__: BUG: Make allow_pickle=False the default for loading +* `#12892 `__: BUG: Do not double-quote arguments passed on to the linker +* `#12894 `__: MAINT: Removed unused and confusingly indirect imports from mingw32ccompiler +* `#12895 `__: BUG: Do not insert extra double quote into preprocessor macros +* `#12903 `__: TST: fix vmImage dispatch in Azure +* `#12905 `__: BUG: fix byte order reversal for datetime64[ns] +* `#12908 `__: DOC: Update master following 1.16.1 release. +* `#12911 `__: BLD: fix doc build for distribution. +* `#12915 `__: ENH: pathlib support for fromfile(), .tofile() and .dump() +* `#12920 `__: MAINT: remove complicated test of multiarray import failure mode +* `#12922 `__: DOC: Add note about arbitrary code execution to numpy.load +* `#12925 `__: BUG: parse shell escaping in extra_compile_args and extra_link_args +* `#12928 `__: MAINT: Merge together the unary and binary type resolvers +* `#12929 `__: DOC: fix documentation bug in np.argsort and extend examples +* `#12931 `__: MAINT: Remove recurring check +* `#12932 `__: BUG: do not dereference NULL pointer +* `#12937 `__: DOC: Correct negative_binomial docstring +* `#12944 `__: BUG: Make timsort deal with zero length elements. +* `#12945 `__: BUG: Add timsort without breaking the API. +* `#12949 `__: DOC: ndarray.max is missing +* `#12962 `__: ENH: Add 'bitorder' keyword to packbits, unpackbits +* `#12963 `__: DOC: Grammatical fix in numpy doc +* `#12964 `__: DOC: Document that ``scale==0`` is now allowed in many distributions. +* `#12965 `__: DOC: Properly format Return section of ogrid Docstring, +* `#12968 `__: BENCH: Re-write sorting benchmarks +* `#12971 `__: ENH: Add 'offset' keyword to 'numpy.fromfile()' +* `#12973 `__: DOC: Recommend adding dimension to switch between row and column... +* `#12983 `__: DOC: Randomstate docstring fixes +* `#12984 `__: DOC: Add examples of negative shifts in np.roll +* `#12986 `__: BENCH: set ones in any/all benchmarks to 1 instead of 0 +* `#12988 `__: ENH: Create boolean and integer ufuncs for isnan, isinf, and... +* `#12989 `__: ENH: Correct handling of infinities in np.interp (option B) +* `#12995 `__: BUG: Add missing PyErr_NoMemory() for reporting a failed malloc +* `#12996 `__: MAINT: Use the same multiplication order in interp for cached... +* `#13002 `__: DOC: reduce warnings when building, and rephrase slightly +* `#13004 `__: MAINT: minor changes for consistency to site.cfg.example +* `#13008 `__: MAINT: Move pickle import to numpy.compat +* `#13019 `__: BLD: Windows absolute path DLL loading +* `#13023 `__: BUG: Changes to string-to-shell parsing behavior broke paths... +* `#13027 `__: BUG: Fix regression in parsing of F90 and F77 environment variables +* `#13031 `__: MAINT: Replace if statement with a dictionary lookup for ease... +* `#13032 `__: MAINT: Extract the loop macros into their own header +* `#13033 `__: MAINT: Convert property to @property +* `#13035 `__: DOC: Draw more attention to which functions in random are convenience... +* `#13036 `__: BUG: __array_interface__ offset was always ignored +* `#13039 `__: BUG: Remove error-prone borrowed reference handling +* `#13044 `__: DOC: link to devdocs in README +* `#13046 `__: ENH: Add shape to *_like() array creation +* `#13049 `__: MAINT: remove undocumented __buffer__ attribute lookup +* `#13050 `__: BLD: make doc build work more robustly. +* `#13054 `__: DOC: Added maximum_sctype to documentation +* `#13055 `__: DOC: Post NumPy 1.16.2 release update. +* `#13056 `__: BUG: Fixes to numpy.distutils.Configuration.get_version +* `#13058 `__: DOC: update docstring in numpy.interp docstring +* `#13060 `__: BUG: Use C call to sysctlbyname for AVX detection on MacOS +* `#13063 `__: DOC: revert PR #13058 and fixup Makefile +* `#13067 `__: MAINT: Use with statements for opening files in distutils +* `#13068 `__: BUG: Add error checks when converting integers to datetime types +* `#13071 `__: DOC: Removed incorrect claim regarding shape constraints for... +* `#13073 `__: MAINT: Fix ABCPolyBase in various ways +* `#13075 `__: BUG: Convert fortran flags in environment variable +* `#13076 `__: BUG: Remove our patched version of `distutils.split_quoted` +* `#13077 `__: BUG: Fix errors in string formatting while producing an error +* `#13078 `__: MAINT: deduplicate fromroots in np.polynomial +* `#13079 `__: MAINT: Merge duplicate implementations of `*vander2d` and `*vander3d`... +* `#13086 `__: BLD: fix include list for sdist building +* `#13090 `__: BUILD: sphinx 1.8.3 can be used with our outdated templates +* `#13092 `__: BUG: ensure linspace works on object input. +* `#13093 `__: BUG: Fix parameter validity checks in ``random.choice``. +* `#13095 `__: BUG: Fix testsuite failures on ppc and riscv +* `#13096 `__: TEST: allow refcheck result to vary, increase discoverability... +* `#13097 `__: DOC: update doc of `ndarray.T` +* `#13099 `__: DOC: Add note about "copy and slicing" +* `#13104 `__: DOC: fix references in docs +* `#13107 `__: MAINT: Unify polynomial valnd functions +* `#13108 `__: MAINT: Merge duplicate implementations of `hermvander2d` and... +* `#13109 `__: Prevent traceback chaining in _wrapfunc. +* `#13111 `__: MAINT: Unify polydiv +* `#13115 `__: DOC: Fix #12050 by updating numpy.random.hypergeometric docs +* `#13116 `__: DOC: Add backticks in linalg docstrings. +* `#13117 `__: DOC: Fix arg type for np.pad, fix #9489 +* `#13118 `__: DOC: update scipy-sphinx-theme, fixes search +* `#13119 `__: DOC: Fix c-api function documentation duplication. +* `#13125 `__: BUG: Fix unhandled exception in CBLAS detection +* `#13126 `__: DEP: polynomial: Be stricter about integral arguments +* `#13127 `__: DOC: Tidy 1.17.0 release note newlines +* `#13128 `__: MAINT: Unify polynomial addition and subtraction functions +* `#13130 `__: MAINT: Unify polynomial fitting functions +* `#13131 `__: BUILD: use 'quiet' when building docs +* `#13132 `__: BLD: Allow users to specify BLAS and LAPACK library link order +* `#13134 `__: ENH: Use AVX for float32 implementation of np.exp & np.log +* `#13137 `__: BUG: Fix build for glibc on ARC and uclibc. +* `#13140 `__: DEV: cleanup imports and some assignments (from LGTM) +* `#13146 `__: MAINT: Unify polynomial power functions +* `#13147 `__: DOC: Add description of overflow errors +* `#13149 `__: DOC: correction to numpy.pad docstring +* `#13157 `__: BLD: streamlined library names in site.cfg sections +* `#13158 `__: BLD: Add libflame as a LAPACK back-end +* `#13161 `__: BLD: streamlined CBLAS linkage tries, default to try libraries... +* `#13162 `__: BUILD: update numpydoc to latest version +* `#13163 `__: ENH: randomgen +* `#13169 `__: STY: Fix weird indents to be multiples of 4 spaces +* `#13170 `__: DOC, BUILD: fail the devdoc build if there are warnings +* `#13174 `__: DOC: Removed some c-api duplication +* `#13176 `__: BUG: fix reference count error on invalid input to ndarray.flat +* `#13181 `__: BENCH, BUG: fix Savez suite, previously was actually calling... +* `#13182 `__: MAINT: add overlap checks to choose, take, put, putmask +* `#13188 `__: MAINT: Simplify logic in convert_datetime_to_datetimestruct +* `#13202 `__: ENH: use rotated companion matrix to reduce error +* `#13203 `__: DOC: Use std docstring for multivariate normal +* `#13205 `__: DOC : Fix C-API documentation references to items that don't... +* `#13206 `__: BUILD: pin sphinx to 1.8.5 +* `#13208 `__: MAINT: cleanup of fast_loop_macros.h +* `#13216 `__: Adding an example of successful execution of numpy.test() to... +* `#13217 `__: TST: always publish Azure tests +* `#13218 `__: ENH: `isfinite` support for `datetime64` and `timedelta64` +* `#13219 `__: ENH: nan_to_num keyword addition (was #9355) +* `#13222 `__: DOC: Document/ Deprecate functions exposed in "numpy" namespace +* `#13224 `__: Improve error message for negative valued argument +* `#13226 `__: DOC: Fix small issues in mtrand doc strings +* `#13231 `__: DOC: Change the required Sphinx version to build documentation +* `#13234 `__: DOC : PyArray_Descr.names undocumented +* `#13239 `__: DOC: Minor grammatical fixes in NumPy docs +* `#13242 `__: DOC: fix docstring for floor_divide +* `#13243 `__: MAINT: replace SETREF with assignment to ret array in ndarray.flat +* `#13244 `__: DOC: Improve mtrand docstrings +* `#13250 `__: MAINT: Improve efficiency of pad by avoiding use of apply_along_axis +* `#13253 `__: TST: fail Azure CI if test failures +* `#13259 `__: DOC: Small readability improvement +* `#13262 `__: DOC : Correcting bug on Documentation Page (Byteswapping) +* `#13264 `__: TST: use OpenBLAS v0.3.5 for POWER8 CI runs +* `#13269 `__: BUG, MAINT: f2py: Add a cast to avoid a compiler warning. +* `#13270 `__: TST: use OpenBLAS v0.3.5 for ARMv8 CI +* `#13271 `__: ENH: vectorize np.abs for unsigned ints and half, improving performance... +* `#13273 `__: BUG: Fix null pointer dereference in PyArray_DTypeFromObject +* `#13277 `__: DOC: Document caveat in random.uniform +* `#13287 `__: Add benchmark for sorting random array. +* `#13289 `__: DOC: add Quansight Labs as an Institutional Partner +* `#13291 `__: MAINT: fix unused variable warning in npy_math_complex.c.src +* `#13292 `__: DOC: update numpydoc to latest master +* `#13293 `__: DOC: add more info to failure message +* `#13298 `__: ENH: Added clearer exception for np.diff on 0-dimensional ndarray +* `#13301 `__: BUG: Fix crash when calling savetxt on a padded array +* `#13305 `__: NEP: Update NEP-18 to include the ``__skip_array_function__``... +* `#13306 `__: MAINT: better MemoryError message (#13225) +* `#13309 `__: DOC: list Quansight rather than Quansight Labs as Institutional... +* `#13310 `__: ENH: Add project_urls to setup +* `#13311 `__: BUG: Fix bad error message in np.memmap +* `#13312 `__: BUG: Close files if an error occurs in genfromtxt +* `#13313 `__: MAINT: fix typo in 'self' +* `#13314 `__: DOC: remove misplaced section at bottom of governance people... +* `#13316 `__: DOC: Added anti-diagonal examples to np.diagonal and np.fill_diagonal +* `#13320 `__: MAINT: remove unused file +* `#13321 `__: MAINT: Move exceptions from core._internal to core._exceptions +* `#13322 `__: MAINT: Move umath error helpers into their own module +* `#13323 `__: BUG: ufunc.at iteration variable size fix +* `#13324 `__: MAINT: Move asarray helpers into their own module +* `#13326 `__: DEP: Deprecate collapsing shape-1 dtype fields to scalars. +* `#13328 `__: MAINT: Tidy up error message for accumulate and reduceat +* `#13331 `__: DOC, BLD: fix doc build issues in preparation for the next numpydoc... +* `#13332 `__: BUG: Always return views from structured_to_unstructured when... +* `#13334 `__: BUG: Fix structured_to_unstructured on single-field types +* `#13335 `__: DOC: Add as_ctypes_type to the documentation +* `#13336 `__: BUILD: fail documentation build if numpy version does not match +* `#13337 `__: DOC: Add docstrings for consistency in aliases +* `#13346 `__: BUG/MAINT: Tidy typeinfo.h and .c +* `#13348 `__: BUG: Return the coefficients array directly +* `#13354 `__: TST: Added test_fftpocket.py::test_axes +* `#13367 `__: DOC: reorganize developer docs, use scikit-image as a base for... +* `#13371 `__: BUG/ENH: Make floor, ceil, and trunc call the matching special... +* `#13374 `__: DOC: Specify range for numpy.angle +* `#13377 `__: DOC: Add missing macros to C API documentation +* `#13379 `__: BLD: address mingw-w64 issue. Follow-up to gh-9977 +* `#13383 `__: MAINT, DOC: Post 1.16.3 release updates +* `#13388 `__: BUG: Some PyPy versions lack PyStructSequence_InitType2. +* `#13389 `__: ENH: implement ``__skip_array_function__`` attribute for NEP-18 +* `#13390 `__: ENH: Add support for Fraction to percentile and quantile +* `#13391 `__: MAINT, DEP: Fix deprecated ``assertEquals()`` +* `#13395 `__: DOC: note re defaults allclose to assert_allclose +* `#13397 `__: DOC: Resolve confusion regarding hashtag in header line of csv +* `#13399 `__: ENH: Improved performance of PyArray_FromAny for sequences of... +* `#13402 `__: DOC: Show the default value of deletechars in the signature of... +* `#13403 `__: DOC: fix typos in dev/index +* `#13404 `__: DOC: Add Sebastian Berg as sponsored by BIDS +* `#13406 `__: DOC: clarify array_{2string,str,repr} defaults +* `#13409 `__: BUG: (py2 only) fix unicode support for savetxt fmt string +* `#13413 `__: DOC: document existence of linalg backends +* `#13415 `__: BUG: fixing bugs in AVX exp/log while handling special value... +* `#13416 `__: BUG: Protect generators from log(0.0) +* `#13417 `__: DOC: dimension sizes are non-negative, not positive +* `#13425 `__: MAINT: fixed typo 'Mismacth' from numpy/core/setup_common.py +* `#13433 `__: BUG: Handle subarrays in descr_to_dtype +* `#13435 `__: BUG: Add TypeError to accepted exceptions in crackfortran. +* `#13436 `__: TST: Add file-not-closed check to LGTM analysis. +* `#13440 `__: MAINT: fixed typo 'wtihout' from numpy/core/shape_base.py +* `#13443 `__: BLD, TST: implicit func errors +* `#13445 `__: MAINT: refactor PyArrayMultiIterObject constructors +* `#13446 `__: MANT: refactor unravel_index for code repetition +* `#13449 `__: BUG: missing git raises an OSError +* `#13456 `__: TST: refine Azure fail reports +* `#13463 `__: BUG,DEP: Fix writeable flag setting for arrays without base +* `#13467 `__: ENH: err msg for too large sequences. See #13450 +* `#13469 `__: DOC: correct "version added" in npymath docs +* `#13471 `__: LICENSE: split license file in standard BSD 3-clause and bundled. +* `#13477 `__: DOC: have notes in histogram_bin_edges match parameter style +* `#13479 `__: DOC: Mention the handling of nan in the assert_equal docstring. +* `#13482 `__: TEST: add duration report to tests, speed up two outliers +* `#13483 `__: DOC: update mailmap for Bill Spotz +* `#13485 `__: DOC: add security vulnerability reporting and doc links to README +* `#13491 `__: BUG/ENH: Create npy format 3.0 to support extended unicode characters... +* `#13495 `__: BUG: test all ufunc.types for return type, fix for exp, log +* `#13496 `__: BUG: ma.tostring should respect the order parameter +* `#13498 `__: DOC: Clarify rcond normalization in linalg.pinv +* `#13499 `__: MAINT: Use with statement to open/close files to fix LGTM alerts +* `#13503 `__: ENH: Support object arrays in matmul +* `#13504 `__: DOC: Update links in PULL_REQUEST_TEMPLATE.md +* `#13506 `__: ENH: Add sparse option to np.core.numeric.indices +* `#13507 `__: BUG: np.array cleared errors occurred in PyMemoryView_FromObject +* `#13508 `__: BUG: Removes ValueError for empty kwargs in arraymultiter_new +* `#13518 `__: MAINT: implement assert_array_compare without converting array... +* `#13520 `__: BUG: exp, log AVX loops do not use steps +* `#13523 `__: BUG: distutils/system_info.py fix missing subprocess import +* `#13529 `__: MAINT: Use exec() instead array_function_dispatch to improve... +* `#13530 `__: BENCH: Modify benchmarks for radix sort. +* `#13534 `__: BLD: Make CI pass again with pytest 4.5 +* `#13541 `__: ENH: restore unpack bit lookup table +* `#13544 `__: ENH: Allow broadcast to be called with zero arguments +* `#13550 `__: TST: Register markers in conftest.py. +* `#13551 `__: DOC: Add note to ``nonzero`` docstring. +* `#13558 `__: MAINT: Fix errors seen on new python 3.8 +* `#13570 `__: DOC: Remove duplicate documentation of the PyArray_SimpleNew... +* `#13571 `__: DOC: Mention that expand_dims returns a view +* `#13574 `__: DOC: remove performance claim from searchsorted() +* `#13575 `__: TST: Apply ufunc signature and type test fixmes. +* `#13581 `__: ENH: AVX support for exp/log for strided float32 arrays +* `#13584 `__: DOC: roadmap update +* `#13589 `__: MAINT: Increment stacklevel for warnings to account for NEP-18... +* `#13590 `__: BUG: Fixes for Undefined Behavior Sanitizer (UBSan) errors. +* `#13595 `__: NEP: update NEP 19 with API terminology +* `#13599 `__: DOC: Fixed minor doc error in take_along_axis +* `#13603 `__: TST: bump / verify OpenBLAS in CI +* `#13619 `__: DOC: Add missing return value documentation in ndarray.require +* `#13621 `__: DOC: Update boolean indices in index arrays with slices example +* `#13623 `__: BUG: Workaround for bug in clang7.0 +* `#13624 `__: DOC: revert __skip_array_function__ from NEP-18 +* `#13626 `__: DOC: update isfortran docs with return value +* `#13627 `__: MAINT: revert __skip_array_function__ from NEP-18 +* `#13629 `__: BUG: setup.py install --skip-build fails +* `#13632 `__: MAINT: Collect together the special-casing of 0d nonzero into... +* `#13633 `__: DOC: caution against relying upon NumPy's implementation in subclasses +* `#13634 `__: MAINT: avoid nested dispatch in numpy.core.shape_base +* `#13636 `__: DOC: Add return section to linalg.matrix_rank & tensordot +* `#13639 `__: MAINT: Update mailmap for 1.17.0 +* `#13642 `__: BUG: special case object arrays when printing rel-, abs-error... +* `#13648 `__: BUG: ensure that casting to/from structured is properly checked. +* `#13649 `__: DOC: Mention PyArray_GetField steals a reference +* `#13652 `__: MAINT: remove superfluous setting in can_cast_safely_table. +* `#13655 `__: BUG/MAINT: Non-native byteorder in random ints +* `#13656 `__: PERF: Use intrinsic rotr on Windows +* `#13657 `__: BUG: Avoid leading underscores in C function names. +* `#13660 `__: DOC: Updates following NumPy 1.16.4 release. +* `#13663 `__: BUG: regression for array([pandas.DataFrame()]) +* `#13664 `__: MAINT: Misc. typo fixes +* `#13665 `__: MAINT: Use intrinsics in Win64-PCG64 +* `#13670 `__: BUG: Fix RandomState argument name +* `#13672 `__: DOC: Fix rst markup in RELEASE_WALKTHROUGH. +* `#13678 `__: BUG: fix benchmark suite importability on Numpy<1.17 +* `#13682 `__: ENH: Support __length_hint__ in PyArray_FromIter +* `#13684 `__: BUG: Move ndarray.dump to python and make it close the file it... +* `#13687 `__: DOC: Remove misleading statement +* `#13688 `__: MAINT: Correct masked aliases +* `#13690 `__: MAINT: Remove version added from Generator +* `#13691 `__: BUG: Prevent passing of size 0 to array alloc C functions +* `#13692 `__: DOC: Update C-API documentation of scanfunc, fromstr +* `#13693 `__: ENH: Pass input strides and dimensions by pointer to const +* `#13695 `__: BUG: Ensure Windows choice returns int32 +* `#13696 `__: DOC: Put the useful constants first +* `#13697 `__: MAINT: speed up hstack and vstack by eliminating list comprehension. +* `#13700 `__: Add links for GitHub Sponsors button. +* `#13703 `__: DOC: Adds documentation for numpy.dtype.base +* `#13704 `__: DOC: Mention PyArray_DIMS can be NULL +* `#13708 `__: DEP: Deprecate nonzero(0d) in favor of calling atleast_1d explicitly +* `#13715 `__: BUG: Fix use-after-free in boolean indexing +* `#13716 `__: BUG: Fix random.choice when probability is not C contiguous +* `#13720 `__: MAINT/BUG: Manage more files with with statements +* `#13721 `__: MAINT,BUG: More ufunc exception cleanup +* `#13724 `__: MAINT: fix use of cache_dim +* `#13725 `__: BUG: fix compilation of 3rd party modules with Py_LIMITED_API... +* `#13726 `__: MAINT: Update PCG jump sizes +* `#13729 `__: DOC: Merge together DISTUTILS.rst.txt#template-files" and distutils.r… +* `#13730 `__: MAINT: Change keyword from reserved word +* `#13737 `__: DOC: Mention and try to explain pairwise summation in sum +* `#13741 `__: MAINT: random: Remove unused empty file binomial.h. +* `#13743 `__: MAINT: random: Rename legacy distributions file. +* `#13744 `__: DOC: Update the C style guide for C99. +* `#13745 `__: BUG: fix segfault on side-effect in __bool__ function in array.nonzero() +* `#13746 `__: [WIP] DOC : Refactor C-API -- Python Types and C structures +* `#13757 `__: MAINT: fix histogram*d dispatchers +* `#13760 `__: DOC: update test guidelines document to use pytest for skipif +* `#13761 `__: MAINT: random: Rewrite the hypergeometric distribution. +* `#13762 `__: MAINT: Use textwrap.dedent for multiline strings +* `#13763 `__: MAINT: Use with statements and dedent in core/setup.py +* `#13767 `__: DOC: Adds examples for dtype attributes +* `#13770 `__: MAINT: random: Combine ziggurat.h and ziggurat_constants.h +* `#13771 `__: DOC: Change random to uninitialized and unpredictable in empty... +* `#13772 `__: BUILD: use numpy-wheels/openblas_support.py to create _distributor_init.py +* `#13773 `__: DOC: Update of reference to paper for Lemire's method +* `#13774 `__: BUG: Make ``Generator._masked`` flag default to ``False``. +* `#13777 `__: MAINT: Remove duplication of should_use_min_scalar_type function +* `#13780 `__: ENH: use SeedSequence instead of seed() +* `#13781 `__: DOC: Update TESTS.rst.txt for pytest +* `#13786 `__: MAINT: random: Fix a few compiler warnings. +* `#13787 `__: DOC: Fixed the problem of "versionadded" +* `#13788 `__: MAINT: fix 'in' -> 'is' typo +* `#13789 `__: MAINT: Fix warnings in radixsort.c.src: comparing integers of... +* `#13791 `__: MAINT: remove dSFMT +* `#13792 `__: LICENSE: update dragon4 license to MIT +* `#13793 `__: MAINT: remove xoshiro* BitGenerators +* `#13795 `__: DOC: Update description of sep in fromstring +* `#13803 `__: DOC: Improve documentation for ``defchararray`` +* `#13813 `__: BUG: further fixup to histogram2d dispatcher. +* `#13815 `__: MAINT: Correct intrinsic use on Windows +* `#13818 `__: TST: Add tests for ComplexWarning in astype +* `#13819 `__: DOC: Fix documented default value of ``__array_priority__`` for... +* `#13820 `__: MAINT, DOC: Fix misspelled words in documentation. +* `#13821 `__: MAINT: core: Fix a compiler warning. +* `#13830 `__: MAINT: Update tox for supported Python versions +* `#13832 `__: MAINT: remove pcg32 BitGenerator +* `#13833 `__: MAINT: remove ThreeFry BitGenerator +* `#13837 `__: MAINT, BUG: fixes from seedsequence +* `#13838 `__: ENH: SFC64 BitGenerator +* `#13839 `__: MAINT: Ignore some generated files. +* `#13840 `__: ENH: np.random.default_gen() +* `#13843 `__: DOC: remove note about `__array_ufunc__` being provisional for... +* `#13849 `__: DOC: np.random documentation cleanup and expansion. +* `#13850 `__: DOC: Update performance numbers +* `#13851 `__: MAINT: Update shippable.yml to remove Python 2 dependency +* `#13855 `__: BUG: Fix memory leak in dtype from dict constructor +* `#13856 `__: MAINT: move location of bitgen.h +* `#13858 `__: BUG: do not force emulation of 128-bit arithmetic. +* `#13859 `__: DOC: Update performance numbers for PCG64 +* `#13861 `__: BUG: Ensure consistent interpretation of uint64 states. +* `#13863 `__: DOC: Document the precise PCG variant. +* `#13864 `__: TST: Ignore DeprecationWarning during nose imports +* `#13869 `__: DOC: Prepare for 1.17.0rc1 release +* `#13870 `__: MAINT,BUG: Use nbytes to also catch empty descr during allocation +* `#13873 `__: ENH: Rename default_gen -> default_rng +* `#13893 `__: DOC: fix links in 1.17 release note +* `#13897 `__: DOC: Use Cython >= 0.29.11 for Python 3.8 support. +* `#13932 `__: MAINT,BUG,DOC: Fix errors in _add_newdocs +* `#13963 `__: ENH, BUILD: refactor all OpenBLAS downloads into a single, testable... +* `#13971 `__: DOC: emphasize random API changes +* `#13972 `__: MAINT: Rewrite Floyd algorithm +* `#13992 `__: BUG: Do not crash on recursive `.dtype` attribute lookup. +* `#13993 `__: DEP: Speed up WarnOnWrite deprecation in buffer interface +* `#13995 `__: BLD: Remove Trusty dist in Travis CI build +* `#13996 `__: BUG: Handle weird bytestrings in dtype() +* `#13997 `__: BUG: i0 Bessel function regression on array-likes supporting... +* `#13998 `__: BUG: Missing warnings import in polyutils. +* `#13999 `__: DOC: Document array_function at a higher level. +* `#14001 `__: DOC: Show workaround for Generator.integers backward compatibility +* `#14021 `__: DOC: Prepare 1.17.0rc2 release. +* `#14040 `__: DOC: Improve quickstart documentation of new random Generator. +* `#14041 `__: TST, MAINT: expand OpenBLAS version checking +* `#14080 `__: BUG, DOC: add new recfunctions to `__all__` +* `#14081 `__: BUG: fix build issue on icc 2016 +* `#14082 `__: BUG: Fix file-like object check when saving arrays +* `#14109 `__: REV: "ENH: Improved performance of PyArray_FromAny for sequences... +* `#14126 `__: BUG, TEST: Adding validation test suite to validate float32 exp +* `#14127 `__: DOC: Add blank line above doctest for intersect1d +* `#14128 `__: MAINT: adjustments to test_ufunc_noncontigous +* `#14129 `__: MAINT: Use equality instead of identity check with literal +* `#14133 `__: MAINT: Update mailmap and changelog for 1.17.0 diff --git a/doc/changelog/1.17.1-changelog.rst b/doc/changelog/1.17.1-changelog.rst new file mode 100644 index 000000000000..c7c8b6c8e68f --- /dev/null +++ b/doc/changelog/1.17.1-changelog.rst @@ -0,0 +1,55 @@ + +Contributors +============ + +A total of 17 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Alexander Jung + +* Allan Haldane +* Charles Harris +* Eric Wieser +* Giuseppe Cuccu + +* Hiroyuki V. Yamazaki +* Jérémie du Boisberranger +* Kmol Yuan + +* Matti Picus +* Max Bolingbroke + +* Maxwell Aladago + +* Oleksandr Pavlyk +* Peter Andreas Entschev +* Sergei Lebedev +* Seth Troisi + +* Vladimir Pershin + +* Warren Weckesser + +Pull requests merged +==================== + +A total of 24 pull requests were merged for this release. + +* `#14156 `__: TST: Allow fuss in testing strided/non-strided exp/log loops +* `#14157 `__: BUG: avx2_scalef_ps must be static +* `#14158 `__: BUG: Remove stray print that causes a SystemError on python 3.7. +* `#14159 `__: BUG: Fix DeprecationWarning in python 3.8. +* `#14160 `__: BLD: Add missing gcd/lcm definitions to npy_math.h +* `#14161 `__: DOC, BUILD: cleanups and fix (again) 'build dist' +* `#14166 `__: TST: Add 3.8-dev to travisCI testing. +* `#14194 `__: BUG: Remove the broken clip wrapper (Backport) +* `#14198 `__: DOC: Fix hermitian argument docs in svd. +* `#14199 `__: MAINT: Workaround for Intel compiler bug leading to failing test +* `#14200 `__: TST: Clean up of test_pocketfft.py +* `#14201 `__: BUG: Make advanced indexing result on read-only subclass writeable... +* `#14236 `__: BUG: Fixed default BitGenerator name +* `#14237 `__: ENH: add c-imported modules for freeze analysis in np.random +* `#14296 `__: TST: Pin pytest version to 5.0.1 +* `#14301 `__: BUG: Fix leak in the f2py-generated module init and `PyMem_Del`... +* `#14302 `__: BUG: Fix formatting error in exception message +* `#14307 `__: MAINT: random: Match type of SeedSequence.pool_size to DEFAULT_POOL_SIZE. +* `#14308 `__: BUG: Fix numpy.random bug in platform detection +* `#14309 `__: ENH: Enable huge pages in all Linux builds +* `#14330 `__: BUG: Fix segfault in `random.permutation(x)` when x is a string. +* `#14338 `__: BUG: don't fail when lexsorting some empty arrays (#14228) +* `#14339 `__: BUG: Fix misuse of .names and .fields in various places (backport... +* `#14345 `__: BUG: fix behavior of structured_to_unstructured on non-trivial... +* `#14350 `__: REL: Prepare 1.17.1 release diff --git a/doc/changelog/1.17.2-changelog.rst b/doc/changelog/1.17.2-changelog.rst new file mode 100644 index 000000000000..144f40038c3b --- /dev/null +++ b/doc/changelog/1.17.2-changelog.rst @@ -0,0 +1,28 @@ + +Contributors +============ + +A total of 7 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* CakeWithSteak + +* Charles Harris +* Dan Allan +* Hameer Abbasi +* Lars Grueter +* Matti Picus +* Sebastian Berg + +Pull requests merged +==================== + +A total of 8 pull requests were merged for this release. + +* `#14418 `__: BUG: Fix aradixsort indirect indexing. +* `#14420 `__: DOC: Fix a minor typo in dispatch documentation. +* `#14421 `__: BUG: test, fix regression in converting to ctypes +* `#14430 `__: BUG: Do not show Override module in private error classes. +* `#14432 `__: BUG: Fixed maximum relative error reporting in assert_allclose. +* `#14433 `__: BUG: Fix uint-overflow if padding with linear_ramp and negative... +* `#14436 `__: BUG: Update 1.17.x with 1.18.0-dev pocketfft.py. +* `#14446 `__: REL: Prepare for NumPy 1.17.2 release. diff --git a/doc/changelog/1.17.3-changelog.rst b/doc/changelog/1.17.3-changelog.rst new file mode 100644 index 000000000000..f911c8465d99 --- /dev/null +++ b/doc/changelog/1.17.3-changelog.rst @@ -0,0 +1,32 @@ + +Contributors +============ + +A total of 7 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Allan Haldane +* Charles Harris +* Kevin Sheppard +* Matti Picus +* Ralf Gommers +* Sebastian Berg +* Warren Weckesser + +Pull requests merged +==================== + +A total of 12 pull requests were merged for this release. + +* `#14456 `__: MAINT: clean up pocketfft modules inside numpy.fft namespace. +* `#14463 `__: BUG: random.hypergeometic assumes npy_long is npy_int64, hung... +* `#14502 `__: BUG: random: Revert gh-14458 and refix gh-14557. +* `#14504 `__: BUG: add a specialized loop for boolean matmul. +* `#14506 `__: MAINT: Update pytest version for Python 3.8 +* `#14512 `__: DOC: random: fix doc linking, was referencing private submodules. +* `#14513 `__: BUG,MAINT: Some fixes and minor cleanup based on clang analysis +* `#14515 `__: BUG: Fix randint when range is 2**32 +* `#14519 `__: MAINT: remove the entropy c-extension module +* `#14563 `__: DOC: remove note about Pocketfft license file (non-existing here). +* `#14578 `__: BUG: random: Create a legacy implementation of random.binomial. +* `#14687 `__: BUG: properly define PyArray_DescrCheck diff --git a/doc/changelog/1.17.4-changelog.rst b/doc/changelog/1.17.4-changelog.rst new file mode 100644 index 000000000000..96d9f3e9ebe8 --- /dev/null +++ b/doc/changelog/1.17.4-changelog.rst @@ -0,0 +1,26 @@ + +Contributors +============ + +A total of 5 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Charles Harris +* Chris Burr + +* Matti Picus +* Qiming Sun + +* Warren Weckesser + +Pull requests merged +==================== + +A total of 8 pull requests were merged for this release. + +* `#14758 `__: BLD: declare support for python 3.8 +* `#14781 `__: BUG: random: biased samples from integers() with 8 or 16 bit... +* `#14851 `__: BUG: Fix _ctypes class circular reference. (#13808) +* `#14852 `__: BLD: add 'apt update' to shippable +* `#14855 `__: BUG: Fix `np.einsum` errors on Power9 Linux and z/Linux +* `#14857 `__: BUG: lib: Fix histogram problem with signed integer arrays. +* `#14858 `__: BLD: Prevent -flto from optimising long double representation... +* `#14866 `__: MAINT: move buffer.h -> npy_buffer.h to avoid conflicts diff --git a/doc/changelog/1.17.5-changelog.rst b/doc/changelog/1.17.5-changelog.rst new file mode 100644 index 000000000000..7ac758075110 --- /dev/null +++ b/doc/changelog/1.17.5-changelog.rst @@ -0,0 +1,26 @@ + +Contributors +============ + +A total of 6 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Charles Harris +* Eric Wieser +* Ilhan Polat +* Matti Picus +* Michael Hudson-Doyle +* Ralf Gommers + +Pull requests merged +==================== + +A total of 7 pull requests were merged for this release. + +* `#14593 `__: MAINT: backport Cython API cleanup to 1.17.x, remove docs +* `#14937 `__: BUG: fix integer size confusion in handling array's ndmin argument +* `#14939 `__: BUILD: remove SSE2 flag from numpy.random builds +* `#14993 `__: MAINT: Added Python3.8 branch to dll lib discovery +* `#15038 `__: BUG: Fix refcounting in ufunc object loops +* `#15067 `__: BUG: Exceptions tracebacks are dropped +* `#15175 `__: ENH: Backport improvements to testing functions. diff --git a/doc/changelog/1.18.0-changelog.rst b/doc/changelog/1.18.0-changelog.rst new file mode 100644 index 000000000000..266ff08077ac --- /dev/null +++ b/doc/changelog/1.18.0-changelog.rst @@ -0,0 +1,540 @@ + +Contributors +============ + +A total of 114 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Abhinav Sagar +* Alex Henrie + +* Alexander Jung + +* Allan Haldane +* Andrea Pattori +* Andrew Liu + +* Anis Ladram + +* Anne Bonner + +* Antoine Dechaume + +* Aryan Naraghi + +* Bastian Eichenberger + +* Brian Wignall + +* Brigitta Sipocz +* CakeWithSteak + +* Charles Harris +* Chris Barker +* Chris Burr + +* Chris Markiewicz + +* Christoph Gohlke +* Christopher Whelan +* Colin Snyder +* Dan Allan +* Daniel Ching +* David Stansby + +* David Zwicker + +* Dieter Werthmüller +* Disconnect3d + +* Dmytro + +* Doug Davis + +* Eric Larson +* Eric Wieser +* Esben Haabendal + +* Eugene Prilepin + +* Felix Divo + +* Gary Gurlaskie +* Gina + +* Giuseppe Cuccu + +* Grzegorz Bokota + +* Guanqun Lu + +* Guilherme Leobas + +* Guillaume Horel +* Géraud Le Falher + +* Hameer Abbasi +* Harmon +* Hiroyuki V. Yamazaki +* Huang, Guangtai + +* Hugo + +* Hyeonguk Ryu + +* Ilhan Polat + +* Isaac Virshup +* Jack J. Woehr + +* Jack Woehr + +* Jackie Leng +* Jaime Fernandez +* Jeff Hale + +* Johann Faouzi + +* Jon Dufresne + +* Joseph Fox-Rabinovitz +* Joseph R. Fox-Rabinovitz + +* João Marcos Gris + +* Justus Magin + +* Jérémie du Boisberranger +* Kai Striega +* Kevin Sheppard +* Kexuan Sun +* Kmol Yuan + +* Kriti Singh +* Larry Bradley + +* Lars Grueter +* Luis Pedro Coelho +* MSeifert04 +* Magdalena Proszewska + +* Manny + +* Mark Harfouche +* Martin Reinecke +* Martin Thoma +* Matt Haberland + +* Matt McCormick + +* Matthias Bussonnier +* Matti Picus +* Max Bolingbroke + +* Maxwell Aladago + +* Michael Hudson-Doyle + +* Oleksandr Pavlyk +* Omar Merghany + +* Pauli Virtanen +* Peter Andreas Entschev +* Peter Bell +* Peter Cock + +* Pradeep Reddy Raamana + +* Qiming Sun + +* Raghuveer Devulapalli +* Ralf Gommers +* Samesh + +* Samesh Lakhotia + +* Sebastian Berg +* Sergei Lebedev +* Seth Troisi + +* Siddhesh Poyarekar + +* Simon + +* Simon Notley + +* Stefan van der Walt +* Stephan Hoyer +* Steve Stagg +* Thomas A Caswell +* Thomas Kluyver +* Tim Hoffmann + +* Tirth Patel + +* Tyler Reddy +* Vladimir Pershin + +* Warren Weckesser +* Yadong Zhang + +* Zieji Pohz + +* Zolisa Bleki + + +Pull requests merged +==================== + +A total of 413 pull requests were merged for this release. + +* `#9301 `__: DOC: added note to docstring of numpy.savez +* `#10151 `__: BUG: Numpy scalar types sometimes have the same name +* `#12129 `__: DOC: Improve axes shift description and example in np.tensordot +* `#12205 `__: MAINT: avoid relying on ``np.generic.__name__`` in ``np.dtype.name`` +* `#12284 `__: ENH: supply our version of numpy.pxd, requires cython>=0.29 +* `#12633 `__: BUG: General fixes to f2py reference counts (dereferencing) +* `#12658 `__: BUG: NaT now sorts to ends of arrays +* `#12828 `__: DOC: Updates to nditer usage instructions +* `#13003 `__: BUG: Do not crash on recursive ``.dtype`` attribute lookup. +* `#13368 `__: ENH: Use AVX for float32 implementation of np.sin & np.cos +* `#13605 `__: DEP: Deprecate silent ignoring of bad data in fromfile/fromstring +* `#13610 `__: ENH: Always produce a consistent shape in the result of ``argwhere`` +* `#13673 `__: DOC: array(obj, dtype=dt) can downcast +* `#13698 `__: DOC: Document ma.filled behavior with non-scalar fill_value +* `#13710 `__: DOC: Add note to irfft-like functions about the default sizes +* `#13739 `__: BUG: Don't produce undefined behavior for a << b if b >= bitsof(a) +* `#13766 `__: MAINT: Update NEP template. +* `#13794 `__: ENH: random: Add the multivariate hypergeometric distribution. +* `#13799 `__: DOC: Fix unrendered links +* `#13802 `__: BUG: Fixed maximum relative error reporting in assert_allclose +* `#13812 `__: MAINT: Rewrite Floyd algorithm +* `#13825 `__: DOC: Add missing macros to C-API documentation +* `#13829 `__: ENH: Add axis argument to random.permutation and random.shuffle +* `#13847 `__: DOC: Adds documentation of functions exposed in numpy namespace +* `#13860 `__: BUG: Refcount fixes +* `#13871 `__: MAINT: Ensure array_dealloc does not modify refcount of self +* `#13874 `__: MAINT: Prepare master for 1.18.0 development. +* `#13876 `__: MAINT,BUG,DOC: Fix errors in _add_newdocs +* `#13880 `__: MAINT: Remove an unnessary backslash between two string literals +* `#13881 `__: MAINT: Update pavement to use python3 in shell commands. +* `#13882 `__: MAINT: Remove unnecessary backslashes (and replace others by... +* `#13883 `__: MAINT: Replace integers in places where booleans are expected +* `#13884 `__: DOC: Add missing parameter description for keepdims in MaskedArray +* `#13885 `__: ENH: use AVX for float32 and float64 implementation of sqrt,... +* `#13886 `__: DOC: reformat top-level release index +* `#13892 `__: DOC : Refactor Array API documentation -- Array Structure and... +* `#13895 `__: DOC: Fix typo in "make_mask" documentation +* `#13896 `__: MAINT: Delete unused _aliased_types.py +* `#13899 `__: MAINT: Change the type of error raised in set_printoptions +* `#13901 `__: BLD: Remove Trusty dist in Travis CI build +* `#13907 `__: BUG: Handle weird bytestrings in dtype() +* `#13908 `__: ENH: use towncrier to build the release note +* `#13913 `__: ENH: improve error message for ragged-array creation failure +* `#13914 `__: DOC: Update the description of byteswap +* `#13916 `__: BUG: i0 Bessel function regression on array-likes supporting... +* `#13920 `__: ENH, BUILD: refactor all OpenBLAS downloads into a single, testable... +* `#13922 `__: MAINT: Remove unnecessary parenthesis in numpy.ma.core +* `#13925 `__: MAINT: Fix wrong spelling of ufunc +* `#13926 `__: DOC: Remove explicit .next method calls with built-in next function... +* `#13928 `__: DOC: Don't override MaskedArray.view documentation with the one... +* `#13930 `__: BUG: Fix incorrect GIL release in array.nonzero +* `#13935 `__: MAINT: Warn if ``_add_newdocs.py`` is used to add docstrings to... +* `#13943 `__: MAINT: Revert #13876, "MAINT,BUG,DOC: Fix errors in _add_newdocs" +* `#13944 `__: MAINT,BUG,DOC: Fix errors in _add_newdocs +* `#13945 `__: DOC, MAINT: emphasize random API changes, remove Generator.randint +* `#13946 `__: DOC: Add a numpy-doc docstring to add_newdoc +* `#13947 `__: DOC: Fix rst rendering in data types +* `#13948 `__: DOC:Update the description of set_printoptions in quickstart... +* `#13950 `__: Fixing failure on Python 2.7 on Windows 7 +* `#13952 `__: Fix a typo related to the range of indices +* `#13959 `__: DOC: add space between words across lines +* `#13964 `__: BUG, DOC: add new recfunctions to ``__all__`` +* `#13967 `__: DOC: Change (old) range() to np.arange() +* `#13968 `__: DOC: improve np.sort docstring +* `#13970 `__: DOC: spellcheck numpy/doc/broadcasting.py +* `#13976 `__: MAINT, TST: remove test-installed-numpy.py +* `#13979 `__: DOC: Document array_function at a higher level. +* `#13985 `__: DOC: show workaround for backward compatibility +* `#13988 `__: DOC: Add a call for contribution paragraph to the readme +* `#13989 `__: BUG: Missing warnings import in polyutils +* `#13990 `__: BUILD: adapt "make version-check" to "make dist" +* `#13991 `__: DOC: emphasize need for matching numpy, git versions +* `#14002 `__: TST, MAINT, BUG: expand OpenBLAS version checking +* `#14004 `__: ENH: Chain exception for typed item assignment +* `#14005 `__: MAINT: Fix spelling error in npy_tempita kwarg +* `#14010 `__: DOC: Array API : Directory restructure and code cleanup +* `#14011 `__: [DOC] Remove unused/deprecated functions +* `#14022 `__: Update system_info.py +* `#14025 `__: DOC:Link between the two indexing documentation pages +* `#14026 `__: DOC: Update NumFOCUS subcommittee replacing Nathaniel with Sebastian +* `#14027 `__: DOC: update "Contributing to NumPy" with more activities/roles +* `#14028 `__: DOC: Improve quickstart documentation of new random Generator +* `#14030 `__: DEP: Speed up WarnOnWrite deprecation in buffer interface +* `#14032 `__: NEP: numpy.org website redesign +* `#14035 `__: DOC: Fix docstring of numpy.allclose regarding NaNs +* `#14036 `__: DEP: Raise warnings for deprecated functions PyArray_As1D, PyArray_As2D +* `#14039 `__: DEP: Remove np.rank which has been deprecated for more than 5... +* `#14048 `__: BUG, TEST: Adding validation test suite to validate float32 exp +* `#14051 `__: ENH,DEP: Allow multiple axes in expand_dims +* `#14053 `__: ENH: add pyproject.toml +* `#14060 `__: DOC: Update cversions.py links and wording +* `#14062 `__: DOC, BUILD: cleanups and fix (again) 'make dist' +* `#14063 `__: BUG: Fix file-like object check when saving arrays +* `#14064 `__: DOC: Resolve bad references in Sphinx warnings +* `#14068 `__: MAINT: bump ARMv8 / POWER8 OpenBLAS in CI +* `#14069 `__: DOC: Emphasize the need to run tests when building from source +* `#14070 `__: DOC:Add example to clarify "numpy.save" behavior on already open... +* `#14072 `__: DEP: Deprecate full and economic modes for linalg.qr +* `#14073 `__: DOC: Doc release +* `#14074 `__: BUG: fix build issue on icc 2016 +* `#14076 `__: TST: Add 3.8-dev to travisCI testing. +* `#14085 `__: DOC: Add blank line above doctest for intersect1d +* `#14086 `__: ENH: Propose standard policy for dropping support of old Python... +* `#14089 `__: DOC: Use ``pip install .`` where possible instead of calling setup.py +* `#14091 `__: MAINT: adjustments to test_ufunc_noncontigous +* `#14092 `__: MAINT: Improve NEP template +* `#14096 `__: DOC: fix documentation of i and j for tri. +* `#14097 `__: MAINT: Lazy import testing on python >=3.7 +* `#14100 `__: DEP: Deprecate PyArray_FromDimsAndDataAndDescr, PyArray_FromDims +* `#14101 `__: MAINT: Clearer error message while padding with stat_length=0 +* `#14106 `__: MAINT: remove duplicate variable assignments +* `#14108 `__: BUG: initialize variable that is passed by pointer +* `#14110 `__: DOC: fix typo in c-api/array.rst doc +* `#14115 `__: DOC: fix markup of news fragment readme +* `#14121 `__: BUG: Add gcd/lcm definitions to npy_math.h +* `#14122 `__: MAINT: Mark umath accuracy test xfail. +* `#14124 `__: MAINT: Use equality instead of identity check with literal +* `#14130 `__: MAINT: Fix small typo in quickstart docs +* `#14134 `__: DOC, MAINT: Update master after 1.17.0 release. +* `#14141 `__: ENH: add c-imported modules for freeze analysis in np.random +* `#14143 `__: BUG: Fix DeprecationWarning in python 3.8 +* `#14144 `__: BUG: Remove stray print that causes a SystemError on python 3.7... +* `#14145 `__: BUG: Remove the broken clip wrapper +* `#14152 `__: BUG: avx2_scalef_ps must be static +* `#14153 `__: TST: Allow fuss in testing strided/non-strided exp/log loops +* `#14170 `__: NEP: Proposal for __duckarray__ protocol +* `#14171 `__: BUG: Make advanced indexing result on read-only subclass writeable +* `#14174 `__: BUG: Check for existence of ``fromstr`` which used in ``fromstr_next_element`` +* `#14178 `__: TST: Clean up of test_pocketfft.py +* `#14181 `__: DEP: Deprecate np.alen +* `#14183 `__: DOC: Fix misleading ``allclose`` docstring for ``equal_nan`` +* `#14185 `__: MAINT: Workaround for Intel compiler bug leading to failing test +* `#14190 `__: DOC: Fix hermitian argument docs in ``svd`` +* `#14195 `__: MAINT: Fix a docstring typo. +* `#14196 `__: DOC: Fix links in ``/.github/CONTRIBUTING.md``. +* `#14197 `__: ENH: Multivariate normal speedups +* `#14203 `__: MAINT: Improve mismatch message of np.testing.assert_array_equal +* `#14204 `__: DOC,MAINT: Move towncrier files and fixup categories +* `#14207 `__: BUG: Fixed default BitGenerator name +* `#14209 `__: BUG: Fix uint-overflow if padding with linear_ramp and negative... +* `#14216 `__: ENH: Enable huge pages in all Linux builds +* `#14217 `__: BUG: Fix leak in the f2py-generated module init and ``PyMem_Del``... +* `#14219 `__: DOC: new nan_to_num keywords are from 1.17 onwards +* `#14223 `__: TST: Add tests for deprecated C functions (PyArray_As1D, PyArray_As1D) +* `#14224 `__: DOC: mention ``take_along_axis`` in ``choose`` +* `#14227 `__: ENH: Parse complex number from string +* `#14231 `__: DOC: update or remove outdated sourceforge links +* `#14234 `__: MAINT: Better error message for norm +* `#14235 `__: DOC: add backlinks to numpy.org +* `#14240 `__: BUG: Don't fail when lexsorting some empty arrays. +* `#14241 `__: BUG: Fix segfault in ``random.permutation(x)`` when x is a string. +* `#14245 `__: Doc: fix a typo in NEP21 +* `#14249 `__: DOC: set status of NEP 28 (website redesign) to Accepted +* `#14250 `__: BLD: MAINT: change default behavior of build flag appending. +* `#14252 `__: BUG: Fixes StopIteration error from 'np.genfromtext' for empty... +* `#14255 `__: BUG: fix inconsistent axes ordering for axis in function ``unique`` +* `#14256 `__: DEP: Deprecate load/dump functions in favour of pickle methods +* `#14257 `__: MAINT: Update NEP-30 +* `#14259 `__: DEP: Deprecate arrayprint formatting functions +* `#14263 `__: DOC: Make Py3K docs C code snippets RST literal blocks +* `#14266 `__: DOC: remove scipy.org from the breadcrumb formattiong +* `#14270 `__: BUG: Fix formatting error in exception message +* `#14272 `__: DOC: Address typos in dispatch docs +* `#14279 `__: BUG: Fix ZeroDivisionError for zero length arrays in pocketfft. +* `#14290 `__: BUG: Fix misuse of .names and .fields in various places +* `#14291 `__: TST, BUG: Use python3.6-dbg. +* `#14295 `__: BUG: core: Handle large negative np.int64 args in binary_repr. +* `#14298 `__: BUG: Fix numpy.random bug in platform detection +* `#14303 `__: MAINT: random: Match type of SeedSequence.pool_size to DEFAULT_POOL_SIZE. +* `#14310 `__: Bug: Fix behavior of structured_to_unstructured on non-trivial... +* `#14311 `__: DOC: add two commas, move one word +* `#14313 `__: DOC: Clarify rules about broadcasting when empty arrays are involved. +* `#14321 `__: TST, MAINT: bump to OpenBLAS 0.3.7 stable +* `#14325 `__: DEP: numpy.testing.rand +* `#14335 `__: DEP: Deprecate class ``SafeEval`` +* `#14341 `__: BUG: revert detecting and raising error on ragged arrays +* `#14342 `__: DOC: Improve documentation of ``isscalar``. +* `#14349 `__: MAINT: Fix bloated mismatch error percentage in array comparisons. +* `#14351 `__: DOC: Fix a minor typo in dispatch documentation. +* `#14352 `__: MAINT: Remove redundant deprecation checks +* `#14353 `__: MAINT: polynomial: Add an N-d vander implementation used under... +* `#14355 `__: DOC: clarify that PytestTester is non-public +* `#14356 `__: DOC: support and require sphinx>=2.2 +* `#14360 `__: DOC: random: fix doc linking, was referencing private submodules. +* `#14364 `__: MAINT: Fixes for prospective Python 3.10 and 4.0 +* `#14365 `__: DOC: lib: Add more explanation of the weighted average calculation. +* `#14368 `__: MAINT: Avoid BytesWarning in PyArray_DescrConverter() +* `#14369 `__: MAINT: Post NumPy 1.17.1 update. +* `#14370 `__: DOC: Fixed dtype docs for var, nanvar. +* `#14372 `__: DOC: Document project as Python 3 only with a trove classifier +* `#14378 `__: BUILD: move all test dependencies to ./test_requirements.txt +* `#14381 `__: BUG: lib: Fix histogram problem with signed integer arrays. +* `#14385 `__: REL: Update master after NumPy 1.16.5 release. +* `#14387 `__: BUG: test, fix regression in converting to ctypes +* `#14389 `__: NEP: Add initial draft of NEP-31: Context-local and global overrides... +* `#14390 `__: DOC: document numpy/doc update process +* `#14392 `__: DOC: update np.around docstring with note about floating-point... +* `#14393 `__: BUG: view with fieldless dtype should raise if itemsize != 0 +* `#14395 `__: DOC: fix issue with __new__ usage in subclassing doc. +* `#14398 `__: DOC: Fix release notes table of contents +* `#14399 `__: NEP 32: Remove the financial functions from NumPy +* `#14404 `__: BLD: Update RELEASE_WALKTHROUGH and cythonize. +* `#14407 `__: Bump pytest from 5.1.1 to 5.1.2 +* `#14408 `__: TST: Remove build job since we now use Dependabot +* `#14410 `__: BLD: Only allow using Cython module when cythonizing. +* `#14411 `__: TST: Add dependabot config file. +* `#14416 `__: BUG: Fix format statement associated with AttributeError. +* `#14417 `__: BUG: Fix aradixsort indirect indexing. +* `#14426 `__: DOC: add the reference to 'printoptions' +* `#14429 `__: BUG: Do not show Override module in private error classes. +* `#14444 `__: DOC: Make implementation bullet points consistent in NEP 29 +* `#14447 `__: MAINT: Clarify policy language in NEP-29. +* `#14448 `__: REL: Update master after 1.17.2 release. +* `#14452 `__: MAINT: clean up pocketfft modules inside numpy.fft namespace +* `#14453 `__: BLD: remove generated Cython files from sdist +* `#14454 `__: MAINT: add test to prevent new public-looking modules being added +* `#14458 `__: BUG: random.hypergeometic assumes npy_long is npy_int64, hangs... +* `#14459 `__: ENH: Print the amount of memory that would be used by a failed... +* `#14460 `__: MAINT: use test_requirements.txt in tox and shippable, ship it... +* `#14464 `__: BUG: add a specialized loop for boolean matmul +* `#14469 `__: BUG: Fix _ctypes class circular reference. (#13808) +* `#14472 `__: BUG: core: Fix the str function of the rational dtype. +* `#14475 `__: DOC: add timedelta64 signature +* `#14477 `__: MAINT: Extract raising of MemoryError to a helper function +* `#14483 `__: BUG,MAINT: Some fixes and minor cleanup based on clang analysis +* `#14484 `__: MAINT: Add ``NPY_UNUSED`` and ``const`` qualified suggested by clang +* `#14485 `__: MAINT: Silence integer comparison build warnings in assert statements +* `#14486 `__: MAINT: distutils: Add newline at the end of printed warnings. +* `#14490 `__: BUG: random: Revert gh-14458 and refix gh-14557. +* `#14493 `__: DOC: Fix reference NPY_ARRAY_OWNDATA instead of NPY_OWNDATA. +* `#14495 `__: ENH: Allow NPY_PKG_CONFIG_PATH environment variable override +* `#14498 `__: MAINT: remove the entropy c-extension module +* `#14499 `__: DOC: Add backslashes so PyUFunc_FromFuncAndDataAndSignatureAndIdentity... +* `#14500 `__: DOC: Fix a minor typo in changelog readme +* `#14501 `__: BUG: Fix randint when range is 2**32 +* `#14503 `__: DOC: tweak np.round docstring to clarify floating-point error +* `#14508 `__: DOC: Add warning to NPV function +* `#14510 `__: API: Do not return None from recfunctions.drop_fields +* `#14511 `__: BUG: Fix flatten_dtype so that nested 0-field structs are flattened... +* `#14514 `__: DOC: Build release notes during CircleCI step +* `#14518 `__: BUILD: Hide platform configuration probe behind --debug-configure +* `#14520 `__: Mention that split() returns views into the original array +* `#14521 `__: MAINT: Simplify lookfor function +* `#14523 `__: MAINT: random: Remove a few duplicated C function prototypes. +* `#14525 `__: BUILD, MAINT: run tests with verbose for PyPY, also do not leak... +* `#14526 `__: BUG: fix release snippet failures caught only after merging +* `#14527 `__: BLD: add warn-error option, adds -Werror to compiler +* `#14531 `__: BUG: random: Create a legacy implementation of random.binomial. +* `#14534 `__: MAINT: remove unused functions, rearrange headers (from CC=clang) +* `#14535 `__: DOC: Fix a bit of code in 'Beyond the Basics' C API user guide. +* `#14536 `__: MAINT: Cleanup old_defines in DOC +* `#14540 `__: DOC: Added missing versionadded to diff(prepend) +* `#14543 `__: BUG: Avoid ctypes in Generators +* `#14545 `__: Changing ImportWarning to DeprecationWarning +* `#14548 `__: MAINT: handle case where GIT_VERSION is empty string +* `#14554 `__: MAINT: core: Remove duplicated inner loop ee->e from log, exp,... +* `#14555 `__: DOC: clarify input types in basics.io.genfromtxt.rst +* `#14557 `__: DOC: remove note about Pocketfft license file (non-existing here). +* `#14558 `__: DOC: Fix code that generates the table in the 'Casting Rules'... +* `#14562 `__: MAINT: don't install partial numpy.random C/Cython API. +* `#14564 `__: TST: ensure coercion tables aren't printed on failing public... +* `#14567 `__: DEP: remove deprecated (and private) numpy.testing submodules. +* `#14568 `__: BLD, DOC: fix gh-14518, add release note +* `#14570 `__: BUG: importing build_src breaks setuptools monkeypatch for msvc14 +* `#14572 `__: DOC: Note runtests.py ``-- -s`` method to use pytests ``-s`` +* `#14573 `__: DOC: update submodule docstrings, remove info.py files +* `#14576 `__: DOC: Document the NPY_SCALARKIND values as C variables. +* `#14582 `__: MAINT: Bump pytest from 5.1.2 to 5.1.3 +* `#14583 `__: DEP: remove deprecated select behaviour +* `#14585 `__: BUG: Add missing check for 0-sized array in ravel_multi_index +* `#14586 `__: BUG: dtype refcount cleanups +* `#14587 `__: DOC: Fix a minor typo in changelog entry +* `#14592 `__: MAINT: Fix typo: remoge → remove +* `#14595 `__: DOC: Change the promotion table checkmark to 'Y'. +* `#14596 `__: DEP: Complete deprecation of invalid array/memory order +* `#14598 `__: DOC: Add to doc that interp cannot contain NaN +* `#14600 `__: NEP: Accept NEP 32. +* `#14601 `__: NEP: Fix discrepancies in NEPs +* `#14603 `__: NEP: Only list "Active" NEPs under "Meta-NEPs" +* `#14604 `__: API: restructure and document numpy.random C-API +* `#14605 `__: BUG: properly define PyArray_DescrCheck{,Exact} +* `#14607 `__: MAINT: Remove duplicate files from .gitignore +* `#14608 `__: API: rearrange the cython files in numpy.random +* `#14614 `__: MAINT: Bump pytest from 5.1.3 to 5.2.0 +* `#14615 `__: MAINT: Add "MAINT" tag to dependabot commit msg +* `#14616 `__: DOC: Updated sphinx directive formatting +* `#14620 `__: DEP: Finish deprecation of non-integer ``num`` in linspace +* `#14621 `__: DOC: s/OR/AND/ in np.logical_and docstring +* `#14623 `__: DOC: misleading np.sinc() documentation +* `#14629 `__: DOC: clarify residual in np.polyfit +* `#14630 `__: BUILD: change to build_src --verbose-cfg, runtests.py --debug-info +* `#14631 `__: BUG: always free clean_sep +* `#14634 `__: DOC: Create ``class Extension`` docstring and add it to documentation. +* `#14636 `__: DOC: add ``printoptions`` as a context manager to ``set_printoptions`` +* `#14639 `__: DOC: Fix typo in NEP 29 +* `#14643 `__: MAINT: Use scalar math power function directly +* `#14649 `__: DOC: Add IPython to dependencies needed to build docs. +* `#14652 `__: MAINT: Bump pytest-cov from 2.7.1 to 2.8.1 +* `#14653 `__: MAINT: Bump pytest from 5.2.0 to 5.2.1 +* `#14654 `__: MAINT: Bump pytz from 2019.2 to 2019.3 +* `#14656 `__: MAINT: Use ``extract_unit`` throughout datetime +* `#14657 `__: BUG: fix fromfile behavior when reading sub-array dtypes +* `#14662 `__: BUG: random: Use correct length when axis is given to shuffle. +* `#14669 `__: BUG: Do not rely on undefined behaviour to cast from float to... +* `#14674 `__: NEP: add default-dtype-object-deprecation nep 34 +* `#14681 `__: MAINT: Remove unused boolean negative/subtract loops +* `#14682 `__: DEP: ufunc ``out`` argument must be a tuple for multiple outputs +* `#14693 `__: BUG: Fix ``np.einsum`` errors on Power9 Linux and z/Linux +* `#14696 `__: DOC: Note release notes process changes on devdocs start page +* `#14699 `__: Doc warnings +* `#14703 `__: TST: Adding CI stages, with one initial job to the Travis CI +* `#14705 `__: DOC: Switch Markdown link to RST in NEP 29 +* `#14709 `__: TST: Divide Azure CI Pipelines into stages. +* `#14710 `__: DEP: Finish the out kwarg deprecation for ufunc calls +* `#14711 `__: DOC: Removing mentions of appveyor +* `#14714 `__: BUG: Default start to 0 for timedelta arange +* `#14717 `__: API: NaT (arg)min/max behavior +* `#14718 `__: API: Forbid Q<->m safe casting +* `#14720 `__: DEP: deprecate financial functions. +* `#14721 `__: DOC: Move newsfragment to correct folder +* `#14723 `__: DOC: cleaning up examples in maskedarray.generic +* `#14725 `__: MAINT: umath: Change error message for unsupported bool subtraction. +* `#14730 `__: ENH: Add complex number support for fromfile +* `#14732 `__: TST: run refguide-check on rst files in doc/* +* `#14734 `__: DOC: Edit NEP procedure for better discussion +* `#14736 `__: DOC: Post 1.17.3 release update. +* `#14737 `__: NEP: Accept NEP 29 as final +* `#14738 `__: BUG: Don't narrow intp to int when producing error messages +* `#14742 `__: DOC: lib: Fix deprecation markup in financial function docstrings. +* `#14743 `__: DOC: Change from HTTP to HTTPS +* `#14745 `__: BUG: clear only attribute errors in get_attr_string.h::maybe_get_attr +* `#14762 `__: MAINT: doc: Remove doc/newdtype_example/ +* `#14763 `__: Reword cautionary note about dtype.descr +* `#14769 `__: BUG: fix integer size confusion in handling array's ndmin argument +* `#14771 `__: TST, BUILD: add a gcc 4.8 run on ubuntu 18.04 +* `#14775 `__: Update CLASSIFIERS with python 3.8 support +* `#14777 `__: BUG: random: biased samples from integers() with 8 or 16 bit... +* `#14782 `__: DOC: Add release note about changed random variate stream from... +* `#14786 `__: DOC: Make changes to NEP procedure +* `#14790 `__: DOC: random: Remove redundant 'See Also' entry in 'uniform' docstring. +* `#14791 `__: MAINT: Minor typo fix +* `#14792 `__: MAINT: Bump pytest from 5.2.1 to 5.2.2 +* `#14793 `__: DOC: Adjust NEP-31 to new template. +* `#14794 `__: DEP: issue deprecation warning when creating ragged array (NEP... +* `#14798 `__: NEP: move 'NEP 29 random' from Accepted to Final +* `#14799 `__: DOC: Add take_along_axis to the see also section in argmin, argmax... +* `#14800 `__: ENH: change object-array comparisons to prefer OO->O unfuncs +* `#14805 `__: TST: Don't construct Fraction instances from numpy scalars +* `#14814 `__: Rename helper functions to not use the word rank +* `#14820 `__: MAINT: Use templating to merge float loops +* `#14826 `__: BUILD: ignore more build.log warnings +* `#14827 `__: BLD: Prevent -flto from optimising long double representation... +* `#14829 `__: BUG: raise ValueError for empty arrays passed to _pyarray_correlate +* `#14830 `__: MAINT: move buffer.h -> npy_buffer.h to avoid conflicts +* `#14836 `__: MAINT: Bump cython from 0.29.13 to 0.29.14 +* `#14841 `__: ENH: add isinf, isnan, fmin, fmax loops for datetime64, timedelta64 +* `#14842 `__: BLD: add 'apt update' to shippable +* `#14845 `__: MAINT: revert gh-14800, which gave precedence to OO->O over OO->? +* `#14874 `__: REL: Update master after 1.17.4 release. +* `#14878 `__: BUILD: remove SSE2 flag from numpy.random builds +* `#14879 `__: DOC: Update NEP29 with Python3.8 informations. +* `#14881 `__: BUG: Remove builtins from __all__ +* `#14898 `__: MAINT: Delete and ignore generated files +* `#14899 `__: Update FUNDING.yml +* `#14901 `__: MAINT: Remove uses of scalar aliases +* `#14903 `__: NEP: move nep 34 to accepted +* `#14907 `__: TST: Add s390x to the TravisCI test matrix. +* `#14912 `__: DOC: Note FFT type promotion +* `#14914 `__: TST: Test with Python3.8 on Windows. +* `#14915 `__: TST: Update travis.yml +* `#14921 `__: TST: add no_tracing decorator to refcount-sensitive codepath... +* `#14926 `__: MAINT: Bump pytest from 5.2.2 to 5.2.4 +* `#14929 `__: BUG: Fix step returned by linspace when num=1 and endpoint=False +* `#14932 `__: DOC: Compare 'tolist' function to 'list' in example +* `#14935 `__: DOC: Clarify return type for default_rng +* `#14944 `__: MAINT: move numpy/random/examples -> numpy/random/_examples +* `#14947 `__: DOC: testing: Note handling of scalars in assert_array_equal... +* `#14948 `__: DOC, API: add random.__init__.pxd and document random.* functions +* `#14951 `__: DOC: Clean up examples of low-level random access +* `#14954 `__: TST. API: test using distributions.h via cffi +* `#14962 `__: TST: skip if cython is not available +* `#14967 `__: MAINT: Cleaned up mintypecode for Py3 +* `#14973 `__: DOC: fix docstring of np.linalg.norm +* `#14974 `__: MAINT: Added Python3.8 branch to dll lib discovery on Windows +* `#14976 `__: DEV: update asv.conf.json +* `#14978 `__: MAINT: Bump pytest from 5.2.4 to 5.3.0 +* `#14982 `__: MAINT: Fix typos +* `#14983 `__: REV: "ENH: Improved performance of PyArray_FromAny for sequences... +* `#14994 `__: BUG: warn when saving dtype with metadata +* `#14996 `__: DEP: Deprecate the axis argument to masked_rows and masked_cols +* `#15004 `__: MAINT: Fix long name of PCG64 +* `#15007 `__: DOC, API: improve the C-API/Cython documentation and interfaces... +* `#15009 `__: DOC: Fix typo in numpy.loadtxt and numpy.genfromtxt documentation +* `#15012 `__: ENH: allow using symbol-suffixed 64-bit BLAS/LAPACK for numpy.dot... +* `#15014 `__: DOC: add a more useful comment to compat.py3k.py +* `#15019 `__: DOC: lib: Use a clearer example of ddof in the notes of the cov... +* `#15021 `__: TST: machinery for tests requiring large memory + lapack64 smoketest +* `#15023 `__: MAINT: Only copy input array in _replace_nan() if there are nans... +* `#15025 `__: MAINT: Bump pytest from 5.3.0 to 5.3.1 +* `#15027 `__: REV: "ENH: Improved performance of PyArray_FromAny for sequences... +* `#15031 `__: REL: Prepare for 1.18 branch +* `#15032 `__: MAINT: Cleaned up mintypecode for Py3 (pt. 2) +* `#15036 `__: BUG: Fix refcounting in ufunc object loops +* `#15039 `__: BUG: Exceptions tracebacks are dropped +* `#15053 `__: REV: Revert "Merge pull request #14794 from mattip/nep-0034-impl" +* `#15058 `__: API, DOC: change names to multivariate_hypergeometric, improve docs +* `#15059 `__: REL: Prepare for NumPy 1.18.0 release. +* `#15109 `__: TST: Check requires_memory immediately before the test +* `#15111 `__: ENH: Add support to sort timedelta64 ``NaT`` to end of the array +* `#15112 `__: MAINT: follow-up cleanup for blas64 PR +* `#15113 `__: ENH: f2py: add --f2cmap option for specifying the name of .f2py_f2cmap +* `#15114 `__: ENH: add support for ILP64 OpenBLAS (without symbol suffix) +* `#15146 `__: REL: Prepare for 1.18.0 release. diff --git a/doc/changelog/1.18.1-changelog.rst b/doc/changelog/1.18.1-changelog.rst new file mode 100644 index 000000000000..d3df291981ef --- /dev/null +++ b/doc/changelog/1.18.1-changelog.rst @@ -0,0 +1,33 @@ + +Contributors +============ + +A total of 7 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Charles Harris +* Matti Picus +* Maxwell Aladago +* Pauli Virtanen +* Ralf Gommers +* Tyler Reddy +* Warren Weckesser + +Pull requests merged +==================== + +A total of 13 pull requests were merged for this release. + +* `#15158 `__: MAINT: Update pavement.py for towncrier. +* `#15159 `__: DOC: add moved modules to 1.18 release note +* `#15161 `__: MAINT, DOC: Minor backports and updates for 1.18.x +* `#15176 `__: TST: Add assert_array_equal test for big integer arrays +* `#15184 `__: BUG: use tmp dir and check version for cython test (#15170) +* `#15220 `__: BUG: distutils: fix msvc+gfortran openblas handling corner case +* `#15221 `__: BUG: remove -std=c99 for c++ compilation (#15194) +* `#15222 `__: MAINT: unskip test on win32 +* `#15223 `__: TST: add BLAS ILP64 run in Travis & Azure +* `#15245 `__: MAINT: only add --std=c99 where needed +* `#15246 `__: BUG: lib: Fix handling of integer arrays by gradient. +* `#15247 `__: MAINT: Do not use private Python function in testing +* `#15250 `__: REL: Prepare for the NumPy 1.18.1 release. diff --git a/doc/changelog/1.18.2-changelog.rst b/doc/changelog/1.18.2-changelog.rst new file mode 100644 index 000000000000..95008b897ff5 --- /dev/null +++ b/doc/changelog/1.18.2-changelog.rst @@ -0,0 +1,25 @@ + +Contributors +============ + +A total of 5 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Charles Harris +* Ganesh Kathiresan + +* Matti Picus +* Sebastian Berg +* przemb + + +Pull requests merged +==================== + +A total of 7 pull requests were merged for this release. + +* `#15675 `__: TST: move _no_tracing to testing._private +* `#15676 `__: MAINT: Large overhead in some random functions +* `#15677 `__: TST: Do not create gfortran link in azure Mac testing. +* `#15679 `__: BUG: Added missing error check in `ndarray.__contains__` +* `#15722 `__: MAINT: use list-based APIs to call subprocesses +* `#15729 `__: REL: Prepare for 1.18.2 release. +* `#15734 `__: BUG: fix logic error when nm fails on 32-bit diff --git a/doc/changelog/1.18.3-changelog.rst b/doc/changelog/1.18.3-changelog.rst new file mode 100644 index 000000000000..6ed2d4851d63 --- /dev/null +++ b/doc/changelog/1.18.3-changelog.rst @@ -0,0 +1,24 @@ + +Contributors +============ + +A total of 6 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Charles Harris +* Max Balandat + +* @Mibu287 + +* Pan Jan + +* Sebastian Berg +* @panpiort8 + + +Pull requests merged +==================== + +A total of 5 pull requests were merged for this release. + +* `#15916 `__: BUG: Fix eigh and cholesky methods of numpy.random.multivariate_normal +* `#15929 `__: BUG,MAINT: Remove incorrect special case in string to number... +* `#15930 `__: BUG: Guarantee array is in valid state after memory error occurs... +* `#15954 `__: BUG: Check that `pvals` is 1D in `_generator.multinomial`. +* `#16017 `__: BUG: Alpha parameter must be 1D in `generator.dirichlet` diff --git a/doc/changelog/1.18.4-changelog.rst b/doc/changelog/1.18.4-changelog.rst new file mode 100644 index 000000000000..f3524b5f59b3 --- /dev/null +++ b/doc/changelog/1.18.4-changelog.rst @@ -0,0 +1,23 @@ + +Contributors +============ + +A total of 4 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Charles Harris +* Matti Picus +* Sebastian Berg +* Warren Weckesser + +Pull requests merged +==================== + +A total of 6 pull requests were merged for this release. + +* `#16055 `__: BLD: add i686 for 1.18 builds +* `#16090 `__: BUG: random: ``Generator.integers(2**32)`` always returned 0. +* `#16091 `__: BLD: fix path to libgfortran on macOS +* `#16109 `__: REV: Reverts side-effect changes to casting +* `#16114 `__: BLD: put openblas library in local directory on windows +* `#16132 `__: DOC: Change import error "howto" to link to new troubleshooting... diff --git a/doc/changelog/1.18.5-changelog.rst b/doc/changelog/1.18.5-changelog.rst new file mode 100644 index 000000000000..f0bc51e6f2a7 --- /dev/null +++ b/doc/changelog/1.18.5-changelog.rst @@ -0,0 +1,18 @@ + +Contributors +============ + +A total of 3 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Charles Harris +* Matti Picus +* Siyuan + + +Pull requests merged +==================== + +A total of 2 pull requests were merged for this release. + +* `#16439 `__: ENH: enable pickle protocol 5 support for python3.5 +* `#16441 `__: BUG: relpath fails for different drives on windows diff --git a/doc/changelog/1.19.0-changelog.rst b/doc/changelog/1.19.0-changelog.rst new file mode 100644 index 000000000000..bde00249972a --- /dev/null +++ b/doc/changelog/1.19.0-changelog.rst @@ -0,0 +1,628 @@ + +Contributors +============ + +A total of 126 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Alex Henrie +* Alexandre de Siqueira + +* Andras Deak +* Andrea Sangalli + +* Andreas Klöckner + +* Andrei Shirobokov + +* Anirudh Subramanian + +* Anne Bonner +* Anton Ritter-Gogerly + +* Benjamin Trendelkamp-Schroer + +* Bharat Raghunathan +* Brandt Bucher + +* Brian Wignall +* Bui Duc Minh + +* Changqing Li + +* Charles Harris +* Chris Barker +* Chris Holland + +* Christian Kastner + +* Chunlin + +* Chunlin Fang + +* Damien Caliste + +* Dan Allan +* Daniel Hrisca +* Daniel Povey + +* Dustan Levenstein + +* Emmanuelle Gouillart + +* Eric Larson +* Eric M. Bray +* Eric Mariasis + +* Eric Wieser +* Erik Welch + +* Fabio Zeiser + +* Gabriel Gerlero + +* Ganesh Kathiresan + +* Gengxin Xie + +* Guilherme Leobas +* Guillaume Peillex + +* Hameer Abbasi +* Hao Jin + +* Harshal Prakash Patankar + +* Heshy Roskes + +* Himanshu Garg + +* Huon Wilson + +* John Han + +* John Kirkham +* Jon Dufresne +* Jon Morris + +* Josh Wilson +* Justus Magin +* Kai Striega +* Kerem Hallaç + +* Kevin Sheppard +* Kirill Zinovjev + +* Marcin Podhajski + +* Mark Harfouche +* Marten van Kerkwijk +* Martin Michlmayr + +* Masashi Kishimoto + +* Mathieu Lamarre +* Matt Hancock + +* MatteoRaso + +* Matthew Harrigan +* Matthias Bussonnier +* Matti Picus +* Max Balandat + +* Maximilian Konrad + +* Maxwell Aladago +* Maxwell Bileschi + +* Melissa Weber Mendonça + +* Michael Felt +* Michael Hirsch + +* Mike Taves +* Nico Schlömer +* Pan Jan + +* Paul Rougieux + +* Pauli Virtanen +* Peter Andreas Entschev +* Petre-Flaviu Gostin + +* Pierre de Buyl +* Piotr Gaiński + +* Przemyslaw Bartosik + +* Raghuveer Devulapalli +* Rakesh Vasudevan + +* Ralf Gommers +* RenaRuirui + +* Robert Kern +* Roman Yurchak +* Ross Barnowski + +* Ryan + +* Ryan Soklaski +* Sanjeev Kumar + +* SanthoshBala18 + +* Sayed Adel + +* Sebastian Berg +* Seth Troisi +* Sha Liu + +* Siba Smarak Panigrahi + +* Simon Gasse + +* Stephan Hoyer +* Steve Dower + +* Thomas A Caswell +* Till Hoffmann + +* Tim Hoffmann +* Tina Oberoi + +* Tirth Patel +* Tyler Reddy +* Warren Weckesser +* Wojciech Rzadkowski + +* Xavier Thomas + +* Yilin LI + +* Zac Hatfield-Dodds + +* Zé Vinícius + +* @Adam + +* @Anthony + +* @Jim + +* @bartosz-grabowski + +* @dojafrat + +* @gamboon + +* @jfbu + +* @keremh + +* @mayeut + +* @ndunnewind + +* @nglinh + +* @shreepads + +* @sslivkoff + + + +Pull requests merged +==================== + +A total of 488 pull requests were merged for this release. + +* `#8255 `__: ENH: add identity kwarg to frompyfunc +* `#10600 `__: DOC: Do not complain about contiguity when mutating ``ndarray.shape`` +* `#12646 `__: TST: check exception details in refguide_check.py +* `#13421 `__: ENH: improve runtime detection of CPU features +* `#14326 `__: TST: Add assert_array_equal test for big integer arrays. +* `#14376 `__: MAINT: Remove unnecessary 'from __future__ import ...' statements +* `#14530 `__: MAINT: Fix typos and copy edit NEP-0030. +* `#14546 `__: DOC: NumPy for absolute beginners tutorial +* `#14715 `__: NEP: Proposal for array creation dispatching with ``__array_function__`` +* `#14867 `__: ENH: Use AVX-512F for np.maximum and np.minimum +* `#14924 `__: BUG: Fix numpy.random.dirichlet returns NaN for small 'alpha'... +* `#14933 `__: API: Use ``ResultType`` in ``PyArray_ConvertToCommonType`` +* `#14940 `__: BUG: pickle the content of a scalar containing objects, not the... +* `#14942 `__: MAINT,API: ignore and NULL fasttake/fastputmask ArrFuncs slots +* `#14981 `__: BUG: Make ``ediff1d`` kwarg casting consistent +* `#14988 `__: DOC: linalg: Include information about scipy.linalg. +* `#14995 `__: BUG: Use ``__array__`` during dimension discovery +* `#15011 `__: MAINT: cleanup compat.py3k.py +* `#15022 `__: ENH: f2py: improve error messages +* `#15024 `__: DOC: clarify documentation for transpose() +* `#15028 `__: [DOC] LaTeX: fix preamble (closes #15026) +* `#15035 `__: BUG: add endfunction, endsubroutine to valid fortran end words +* `#15040 `__: TST: Add test for object method (and general unary) loops +* `#15042 `__: REL: Update master after 1.18.x branch. +* `#15043 `__: DOC: Update HOWTO_RELEASE.rst.txt +* `#15046 `__: API, DOC: change names to multivariate_hypergeometric, improve... +* `#15050 `__: DOC: Fix statement about norms +* `#15052 `__: MAINT: follow-up cleanup for blas64 PR +* `#15054 `__: DOC: add docstrings to refguide-check +* `#15066 `__: Revert "DEP: issue deprecation warning when creating ragged array... +* `#15068 `__: ENH: Add support to sort timedelta64 ``NaT`` to end of the array +* `#15069 `__: ENH: add support for ILP64 OpenBLAS (without symbol suffix) +* `#15070 `__: DOC: correct version for NaT sort +* `#15072 `__: TST: Check requires_memory immediately before the test +* `#15073 `__: MAINT: core: Fix a very long line in the ufunc docstrings. +* `#15076 `__: BUG: test, fix flexible dtype conversion on class with __array__ +* `#15082 `__: TST: add value to pytest.ini for pytest6 compatibility +* `#15085 `__: MAINT: Ragged cleanup +* `#15097 `__: DOC: bring the out parameter docstring into line with ufuncs +* `#15106 `__: ENH: f2py: add --f2cmap option for specifying the name of .f2py_f2cmap +* `#15107 `__: TST: add BLAS ILP64 run in Travis & Azure +* `#15110 `__: MAINT: Fix expm1 instability for small complex numbers. +* `#15115 `__: MAINT: random: Remove a few unused imports from test files. +* `#15116 `__: MAINT: Bump pytest from 5.3.1 to 5.3.2 +* `#15118 `__: API: remove undocumented use of __array__(dtype, context) +* `#15120 `__: MAINT,CI: fix signed-unsigned comparison warning +* `#15124 `__: DOC: Update documentation of np.clip +* `#15125 `__: DOC: Remove reference to basic RNG +* `#15126 `__: MAINT: Fix randint 0d limits and other 0d cleanups +* `#15129 `__: DOC: Fix typos, via a Levenshtein-style corrector +* `#15133 `__: MAINT: CI: Clean up .travis.yml +* `#15136 `__: DOC: Correct choice signature +* `#15138 `__: DOC: Correct documentation in choice +* `#15143 `__: TST: shippable build efficiency +* `#15144 `__: BUG: ensure reduction output matches input along non-reduction... +* `#15149 `__: REL: Update master after NumPy 1.18.0 release. +* `#15150 `__: MAINT: Update pavement.py for towncrier. +* `#15153 `__: DOC: update cholesky docstring regarding input checking +* `#15154 `__: DOC: update documentation on how to build NumPy +* `#15156 `__: DOC: add moved modules to 1.18 release note +* `#15160 `__: MAINT: Update required cython version to 0.29.14. +* `#15164 `__: BUG: searchsorted: passing the keys as a keyword argument +* `#15170 `__: BUG: use tmp dir and check version for cython test +* `#15178 `__: TST: improve assert message of assert_array_max_ulp +* `#15187 `__: MAINT: unskip test on win32 +* `#15189 `__: ENH: Add property-based tests using Hypothesis +* `#15194 `__: BUG: test, fix for c++ compilation +* `#15195 `__: MAINT: refactoring in np.core.records +* `#15196 `__: DOC: Adding instructions for building documentation to developer... +* `#15197 `__: DOC: NEP 37: A dispatch protocol for NumPy-like modules +* `#15203 `__: MAINT: Do not use private Python function in testing +* `#15205 `__: DOC: Improvements to Quickstart Tutorial. +* `#15211 `__: BUG: distutils: fix msvc+gfortran openblas handling corner case +* `#15212 `__: BUG: lib: Fix handling of integer arrays by gradient. +* `#15215 `__: MAINT: lib: A little bit of clean up for the new year. +* `#15216 `__: REL: Update master after NumPy 1.16.6 and 1.17.5 releases. +* `#15217 `__: DEP: records: Deprecate treating shape=0 as shape=None +* `#15218 `__: ENH: build fallback lapack_lite with 64-bit integers on 64-bit... +* `#15224 `__: MAINT: linalg: use symbol suffix in fallback lapack_lite +* `#15227 `__: DOC: typo in release.rst +* `#15228 `__: NEP: universal SIMD NEP 38 +* `#15229 `__: MAINT: Remove unused int_asbuffer +* `#15230 `__: BUG: do not emit warnings for np.sign, np.equal when using nan +* `#15231 `__: MAINT: Remove Python2 specific C module setup [part2] +* `#15232 `__: MAINT: Cleaning up PY_MAJOR_VERSION/PY_VERSION_HEX +* `#15233 `__: MAINT: Clean up more PY_VERSION_HEX +* `#15236 `__: MAINT: Remove implicit inheritance from object class +* `#15238 `__: MAINT: only add --std=c99 where needed +* `#15239 `__: MAINT: Remove Python2 newbuffer getbuffer +* `#15240 `__: MAINT: Py3K array_as_buffer and gentype_as_buffer +* `#15241 `__: MAINT: Remove references to non-existent sys.exc_clear() +* `#15242 `__: DOC: Update HOWTO_RELEASE.rst +* `#15248 `__: MAINT: cleanup use of sys.exc_info +* `#15249 `__: MAINT: Eliminate some calls to ``eval`` +* `#15251 `__: MAINT: Improve const-correctness of shapes and strides +* `#15253 `__: DOC: clarify the effect of None parameters passed to ndarray.view +* `#15254 `__: MAINT: Improve const-correctness of string arguments +* `#15255 `__: MAINT: Delete numpy.distutils.compat +* `#15256 `__: MAINT: Implement keyword-only arguments as syntax +* `#15260 `__: MAINT: Remove FIXME comments introduced in the previous commit +* `#15261 `__: MAINT: Work with unicode strings in ``dtype('i8,i8')`` +* `#15262 `__: BUG: Use PyDict_GetItemWithError() instead of PyDict_GetItem() +* `#15263 `__: MAINT: Remove python2 array_{get,set}slice +* `#15264 `__: DOC: Add some missing functions in the list of available ufuncs. +* `#15265 `__: MAINT: Tidy PyArray_DescrConverter +* `#15266 `__: MAINT: remove duplicated if statements between DescrConverters +* `#15267 `__: BUG: Fix PyArray_DescrAlignConverter2 on tuples +* `#15268 `__: MAINT: Remove Python2 ndarray.__unicode__ +* `#15272 `__: MAINT: Remove Python 2 divide +* `#15273 `__: MAINT: minor formatting fixups for NEP-37 +* `#15274 `__: MAINT: Post NumPy 1.18.1 update. +* `#15275 `__: MAINT: travis-ci: Update CI scripts. +* `#15278 `__: BENCH: Add benchmark for small array coercions +* `#15279 `__: BUILD: use standard build of OpenBLAS for aarch64, ppc64le, s390x +* `#15280 `__: BENCH: Add basic benchmarks for take and putmask +* `#15281 `__: MAINT: Cleanup most PY3K #ifdef guards +* `#15282 `__: DOC: BLD: add empty release notes for 1.19.0 to fix doc build... +* `#15283 `__: MAINT: Cleanup more NPY_PY3K +* `#15284 `__: MAINT: Use a simpler return convention for internal functions +* `#15285 `__: MAINT: Simplify ``np.int_`` inheritance +* `#15286 `__: DOC" Update np.full docstring. +* `#15287 `__: MAINT: Express PyArray_DescrAlignConverter in terms of _convert_from_any +* `#15288 `__: MAINT: Push down declarations in _convert_from_* +* `#15289 `__: MAINT: C code simplifications +* `#15291 `__: BUG: Add missing error handling to _convert_from_list +* `#15295 `__: DOC: Added tutorial about linear algebra on multidimensional... +* `#15300 `__: MAINT: Refactor dtype conversion functions to be more similar +* `#15303 `__: DOC: Updating f2py docs to python 3 and fixing some typos +* `#15304 `__: MAINT: Remove NPY_PY3K constant +* `#15305 `__: MAINT: Remove sys.version checks in tests +* `#15307 `__: MAINT: cleanup sys.version dependant code +* `#15310 `__: MAINT: Ensure ``_convert_from_*`` functions set errors +* `#15312 `__: MAINT: Avoid escaping unicode in error messages +* `#15315 `__: MAINT: Change file extension of ma README to rst. +* `#15319 `__: BUG: fix NameError in clip nan propagation tests +* `#15323 `__: NEP: document reimplementation of NEP 34 +* `#15324 `__: MAINT: fix typos +* `#15328 `__: TST: move pypy CI to ubuntu 18.04 +* `#15329 `__: TST: move _no_tracing to testing._private, remove testing.support +* `#15333 `__: BUG: Add some missing C error handling +* `#15335 `__: MAINT: Remove sys.version checks +* `#15336 `__: DEP: Deprecate ``->f->fastclip`` at registration time +* `#15338 `__: DOC: document site.cfg.example +* `#15350 `__: MAINT: Fix mistype in histogramdd docstring +* `#15351 `__: DOC, BLD: reword release note, upgrade sphinx version +* `#15353 `__: MAINT: Remove unnecessary calls to PyArray_DATA from binomial... +* `#15354 `__: MAINT: Bump pytest from 5.3.2 to 5.3.3 +* `#15355 `__: MAINT: Const qualify UFunc inner loops +* `#15358 `__: MAINT: Remove six +* `#15361 `__: MAINT: Revise imports from collections.abc module +* `#15362 `__: MAINT: remove internal functions required to handle Python2/3... +* `#15364 `__: MAINT: Remove other uses of six module +* `#15366 `__: MAINT: resolve pyflake F403 'from module import *' used +* `#15367 `__: DOC: Fix Multithreaded Generation example docs +* `#15368 `__: MAINT: Update tox for supported Python versions +* `#15369 `__: MAINT: simd: Avoid signed comparison warning +* `#15370 `__: DOC: Updating Chararry Buffer datatypes +* `#15373 `__: MAINT: Remove sys.version checks +* `#15374 `__: TST: Simplify unicode test +* `#15375 `__: MAINT: Use ``with open`` when possible +* `#15377 `__: MAINT: Cleanup python2 references +* `#15379 `__: MAINT: Python2 Cleanups +* `#15381 `__: DEP: add PendingDeprecation to matlib.py funky namespace +* `#15385 `__: BUG, MAINT: Stop using the error-prone deprecated Py_UNICODE... +* `#15386 `__: MAINT: clean up some macros in scalarapi.c +* `#15393 `__: MAINT/BUG: Fixups to scalar base classes +* `#15397 `__: BUG: np.load does not handle empty array with an empty descr +* `#15398 `__: MAINT: Revise imports from urllib modules +* `#15399 `__: MAINT: Remove Python3 DeprecationWarning from pytest.ini +* `#15400 `__: MAINT: cleanup _pytesttester.py +* `#15401 `__: BUG: Flags should not contain spaces +* `#15403 `__: MAINT: Clean up, mostly unused imports. +* `#15405 `__: BUG/TEST: core: Fix an undefined name in a test. +* `#15407 `__: MAINT: Replace basestring with str. +* `#15408 `__: ENH: Use AVX-512F for complex number arithmetic, absolute, square... +* `#15414 `__: MAINT: Remove Python2 workarounds +* `#15415 `__: MAINT: Revert f2py Python 2.6 workaround +* `#15417 `__: MAINT: Cleanup references to python2 +* `#15418 `__: MAINT, DOC: Remove use of old Python __builtin__, now known as... +* `#15421 `__: ENH: Make use of ExitStack in npyio.py +* `#15422 `__: MAINT: Inline gentype_getreadbuf +* `#15423 `__: MAINT: Use f-strings for clarity. +* `#15425 `__: MAINT: dir(numpy) returns duplicate "testing" +* `#15426 `__: MAINT: Use the PyArrayScalar_VAL macro where possible +* `#15427 `__: DEP: Schedule unused C-API functions for removal/disabling +* `#15428 `__: DOC: Improve ndarray.ctypes example +* `#15429 `__: DOC: distutils: Add a docstring to show_config(). +* `#15430 `__: MAINT: Use contextmanager in _run_doctests +* `#15434 `__: MAINT: Updated polynomial to use fstrings +* `#15435 `__: DOC: Fix Incorrect document in Beginner Docs +* `#15436 `__: MAINT: Update core.py with fstrings (issue #15420) +* `#15439 `__: DOC: fix docstrings so ``python tools/refguide-check --rst ``... +* `#15441 `__: MAINT: Tidy macros in scalar_new +* `#15444 `__: MAINT: use 'yield from ' for simple cases +* `#15445 `__: MAINT: Bump pytest from 5.3.3 to 5.3.4 +* `#15446 `__: BUG: Reject nonsense arguments to scalar constructors +* `#15449 `__: DOC: Update refguide_check note on how to skip code +* `#15451 `__: MAINT: Simplify ``np.object_.__new__`` +* `#15452 `__: STY,MAINT: avoid 'multiple imports on one line' +* `#15463 `__: ENH: expose ``bit_generator`` and random C-API to cython +* `#15464 `__: MAINT: Cleanup duplicate line in refguide_check +* `#15465 `__: MAINT: cleanup unused imports; avoid redefinition of imports +* `#15468 `__: BUG: Fix for SVD not always sorted with hermitian=True +* `#15469 `__: MAINT: Simplify scalar __new__ some more +* `#15474 `__: MAINT: Eliminate messy _WORK macro +* `#15476 `__: update result of rng.random(3) to current rng output +* `#15480 `__: DOC: Correct get_state doc +* `#15482 `__: MAINT: Use ``.identifier = val`` to fill type structs +* `#15483 `__: [DOC] Mention behaviour of np.squeeze with one element +* `#15484 `__: ENH: fixing generic error messages to be more specific in multiarray/descriptor.c +* `#15487 `__: BUG: Fixing result of np quantile edge case +* `#15491 `__: TST: mark the top 3 slowest tests to save ~10 seconds +* `#15493 `__: MAINT: Bump pytest from 5.3.4 to 5.3.5 +* `#15500 `__: MAINT: Use True/False instead of 1/0 in np.dtype.__reduce__ +* `#15503 `__: MAINT: Do not allow ``copyswap`` and friends to fail silently +* `#15504 `__: DOC: Remove duplicated code in true_divide docstring +* `#15505 `__: NEP 40: Informational NEP about current DTypes +* `#15506 `__: NEP 41: First steps towards improved Datatype Support +* `#15510 `__: DOC: Update unique docstring example +* `#15511 `__: MAINT: Large overhead in some random functions +* `#15516 `__: TST: Fix missing output in refguide-check +* `#15521 `__: MAINT: Simplify arraydescr_richcompare +* `#15522 `__: MAINT: Fix internal misuses of ``NPY_TITLE_KEY`` +* `#15524 `__: DOC: Update instructions for building/archiving docs. +* `#15526 `__: BUG: Fix inline assembly that detects cpu features on x86(32bit) +* `#15532 `__: update doctests, small bugs and changes of repr +* `#15534 `__: DEP: Do not allow "abstract" dtype conversion/creation +* `#15536 `__: DOC: Minor copyediting on NEP 37. +* `#15538 `__: MAINT: Extract repeated code to a helper function +* `#15543 `__: NEP: edit and move NEP 38 to accepted status +* `#15547 `__: MAINT: Refresh Doxyfile and modernize numpyfilter.py +* `#15549 `__: TST: Accuracy test float32 sin/cos/exp/log for AVX platforms +* `#15550 `__: DOC: Improve the ``numpy.linalg.eig`` docstring. +* `#15553 `__: BUG: Added missing error check in ``ndarray.__contains__`` +* `#15554 `__: NEP 44 - Restructuring the NumPy Documentation +* `#15556 `__: TST: (Travis CI) Use full python3-dbg path for virtual env creation +* `#15560 `__: BUG, DOC: restore missing import +* `#15566 `__: DOC: Removing bad practices from quick start + some PEP8 +* `#15574 `__: TST: Do not create symbolic link named gfortran. +* `#15575 `__: DOC: Document caveat in random.uniform +* `#15577 `__: TST: Test division by zero both with scalar and with array +* `#15579 `__: DOC: numpy.clip is equivalent to minimum(..., maximum(...)) +* `#15582 `__: MAINT: Bump cython from 0.29.14 to 0.29.15 +* `#15583 `__: MAINT: Bump hypothesis from 5.3.0 to 5.5.4 +* `#15585 `__: BLD: manylinux2010 docker reports machine=i686 +* `#15598 `__: BUG: Ignore differences in NAN for computing ULP differences +* `#15600 `__: TST: use manylinux2010 docker instead of ubuntu +* `#15610 `__: TST: mask DeprecationWarning in xfailed test +* `#15612 `__: BUG: Fix bug in AVX-512F np.maximum and np.minimum +* `#15614 `__: DOC: Reword docstring for assert_equal +* `#15615 `__: BUG: Remove check requiring natural alignment of float/double... +* `#15616 `__: DOC: Add missing imports, definitions and dummy file +* `#15619 `__: DOC: Fix documentation for apply_along_axis +* `#15624 `__: DOC: fix printing, np., deprecation for refguide +* `#15631 `__: MAINT: Pull identical line out of conditional. +* `#15633 `__: DOC: remove broken link in f2py tutorial +* `#15639 `__: BLD: update openblas download to new location, use manylinux2010-base +* `#15644 `__: DOC: Update to clarify actual behavior real_if_(all elements)_close +* `#15648 `__: MAINT: AVX512 implementation with intrinsic for float64 input... +* `#15653 `__: BLD: update OpenBLAS to pre-0.3.9 version +* `#15662 `__: DOC: Refactor ``np.polynomial`` docs using ``automodule`` +* `#15665 `__: BUG: fix doctest exception messages +* `#15672 `__: MAINT: Added comment pointing FIXME to relevant PR. +* `#15673 `__: DOC: Make extension module wording more clear +* `#15678 `__: DOC: Improve np.finfo docs +* `#15680 `__: DOC: Improve Benchmark README with environment setup and more... +* `#15682 `__: MAINT: Bump hypothesis from 5.5.4 to 5.6.0 +* `#15683 `__: NEP: move NEP 44 to accepted status +* `#15685 `__: ENH: Add ``subok`` parameter to np.copy function (cf. #6509) +* `#15694 `__: DOC: Fix indexing docs to pass refguide +* `#15695 `__: MAINT: Test during import to detect bugs with Accelerate(MacOS)... +* `#15696 `__: MAINT: Add a fast path to var for complex input +* `#15701 `__: MAINT: Convert shebang from python to python3 (#15687) +* `#15702 `__: MAINT: replace optparse with argparse for 'doc' and 'tools' scripts +* `#15703 `__: DOC: Fix quickstart doc to pass refguide +* `#15705 `__: DOC: Change list to tuple in example description. +* `#15706 `__: MAINT: Fixing typos in f2py comments and code. +* `#15710 `__: DOC: fix SVD tutorial to pass refguide +* `#15714 `__: MAINT: use list-based APIs to call subprocesses +* `#15715 `__: ENH: update numpy.linalg.multi_dot to accept an ``out`` argument +* `#15716 `__: TST: always use 'python -mpip' not 'pip' +* `#15717 `__: DOC: update datetime reference to pass refguide +* `#15718 `__: DOC: Fix coremath.rst to fix refguide_check +* `#15720 `__: DOC: fix remaining doc files for refguide_check +* `#15723 `__: BUG: fix logic error when nm fails on 32-bit +* `#15724 `__: TST: Remove nose from the test_requirements.txt file. +* `#15733 `__: DOC: Allow NEPs to link to python, numpy, scipy, and matplotlib... +* `#15735 `__: DOC: LICENSE 2019 -> 2020 +* `#15736 `__: BUG: Guarantee array is in valid state after memory error occurs... +* `#15738 `__: MAINT: Remove non-native byte order from _var check. +* `#15740 `__: MAINT: Add better error handling in linalg.norm for vectors and... +* `#15745 `__: MAINT: doc: Remove doc/summarize.py +* `#15747 `__: BUG: lib: Handle axes with length 0 in np.unique. +* `#15749 `__: DOC: document inconsistency between the shape of data and mask... +* `#15750 `__: BUG, TST: fix f2py for PyPy, skip one test for PyPy +* `#15752 `__: MAINT: Fix swig tests issue #15743 +* `#15757 `__: MAINT: CI: Add an explicit 'pr' section to azure-pipelines.yml +* `#15762 `__: MAINT: Bump pytest from 5.3.5 to 5.4.1 +* `#15766 `__: BUG,MAINT: Remove incorrect special case in string to number... +* `#15768 `__: REL: Update master after 1.18.2 release. +* `#15769 `__: ENH: Allow toggling madvise hugepage and fix default +* `#15771 `__: DOC: Fix runtests example in developer docs +* `#15773 `__: DEP: Make issubdtype consistent for types and dtypes +* `#15774 `__: MAINT: remove useless ``global`` statements +* `#15778 `__: BLD: Add requirements.txt file for building docs +* `#15781 `__: BUG: don't add 'public' or 'private' if the other one exists +* `#15784 `__: ENH: Use TypeError in ``np.array`` for python consistency +* `#15794 `__: BUG: Add basic __format__ for masked element to fix incorrect... +* `#15797 `__: TST: Add unit test for out=None of np.einsum +* `#15799 `__: MAINT: Cleanups to np.insert and np.delete +* `#15800 `__: BUG: Add error-checking versions of strided casts. +* `#15802 `__: DEP: Make ``np.insert`` and ``np.delete`` on 0d arrays with an axis... +* `#15803 `__: DOC: correct possible list lengths for ``extobj`` in ufunc calls +* `#15804 `__: DEP: Make np.delete on out-of-bounds indices an error +* `#15805 `__: DEP: Forbid passing non-integral index arrays to ``insert`` and... +* `#15806 `__: TST: Parametrize sort test +* `#15809 `__: TST: switch PyPy job with CPython +* `#15812 `__: TST: Remove code that is not supposed to warn out of warning... +* `#15815 `__: DEP: Do not cast boolean indices to integers in np.delete +* `#15816 `__: MAINT: simplify code that assumes str/unicode and int/long are... +* `#15827 `__: BUG: Break on all errors when performing strided casts. +* `#15830 `__: MAINT: pathlib and hashlib are in stdlib in Python 3.5+ +* `#15832 `__: ENH: improved error message ``IndexError: too many indices for``... +* `#15834 `__: NEP: Add paragraph to NEP 41 about no array-object use and fix... +* `#15836 `__: BUG: Fix IndexError for illegal axis in np.mean +* `#15839 `__: DOC: Minor fix to _hist_bin_fd documentation +* `#15840 `__: BUG,DEP: Make ``scalar.__round__()`` behave like pythons round +* `#15843 `__: DOC: First steps towards docs restructuring (NEP 44) +* `#15848 `__: DOC, TST: enable refguide_check in circleci +* `#15850 `__: DOC: fix typo in C-API reference +* `#15854 `__: DOC: Fix docstring for _hist_bin_auto. +* `#15866 `__: MAINT: Bump cython from 0.29.15 to 0.29.16 +* `#15867 `__: DEP: Deprecate ndarray.tostring() +* `#15868 `__: TST: use draft OpenBLAS build +* `#15870 `__: ENH: Add keepdims argument to count_nonzero +* `#15872 `__: BUG: Fix eigh and cholesky methods of numpy.random.multivariate_normal +* `#15876 `__: BUG: Check that ``pvals`` is 1D in ``_generator.multinomial``. +* `#15877 `__: DOC: Add missing signature from nditer docstring +* `#15881 `__: BUG: Fix empty_like to respect shape=() +* `#15882 `__: BUG: Do not ignore empty tuple of strides in ndarray.__new__ +* `#15883 `__: MAINT: Remove duplicated code in iotools.py +* `#15884 `__: BUG: Setting a 0d array's strides to themselves should be legal +* `#15885 `__: BUG: Respect itershape=() in nditer +* `#15887 `__: MAINT: Clean-up 'next = __next__' used for Python 2 compatibility +* `#15891 `__: DOC: Clarify docs on mixed advanced indexing and slicing +* `#15893 `__: TST: Run test_large_zip in a child process +* `#15894 `__: DOC: Add missing doc of numpy.ma.apply_over_axes in API list. +* `#15899 `__: DOC: Improve record module documentation +* `#15901 `__: DOC: Fixed order of items and link to mailing list in dev docs... +* `#15903 `__: BLD: report clang version on macOS +* `#15904 `__: MAINT: records: Remove private ``format_parser._descr`` attribute +* `#15907 `__: DOC: Update documentation w.r.t. NPY_RELAXED_STRIDES_CHECKING +* `#15914 `__: BUG: random: Disallow p=0 in negative_binomial +* `#15920 `__: DOC: Improve docstring for numpy.linalg.lstsq +* `#15921 `__: ENH: Use sysconfig instead of probing Makefile +* `#15928 `__: DOC: Update np.copy docstring to include ragged case +* `#15931 `__: DOC: Correct private function name to PyArray_AdaptFlexibleDType +* `#15936 `__: MAINT: Fix capitalization in error message in ``mtrand.pyx`` +* `#15938 `__: BUG: Add _LARGE_FILES to def_macros[] when platform is AIX. +* `#15939 `__: DOC: Update np.rollaxis docstring +* `#15949 `__: BUG: fix AttributeError on accessing object in nested MaskedArray. +* `#15951 `__: BUG: Alpha parameter must be 1D in ``generator.dirichlet`` +* `#15953 `__: NEP: minor maintenance, update filename and fix a cross-reference +* `#15964 `__: MAINT: Bump hypothesis from 5.8.0 to 5.8.3 +* `#15967 `__: TST: Add slow_pypy support +* `#15968 `__: DOC: Added note to angle function docstring about angle(0) being... +* `#15982 `__: MAINT/BUG: Cleanup and minor fixes to conform_reduce_result +* `#15985 `__: BUG: Avoid duplication in stack trace of ``linspace(a, b, num=1.5)`` +* `#15988 `__: BUG: Fix inf and NaN-warnings in half float ``nextafter`` +* `#15989 `__: MAINT: Remove 0d check for PyArray_ISONESEGMENT +* `#15990 `__: DEV: Pass additional runtests.py args to ASV +* `#15991 `__: BUG: max/min of a masked array dtype fix +* `#15993 `__: DOC: Fix method documentation of function sort in MaskedArray +* `#16000 `__: NEP: Improve Value Based Casting paragraph in NEP 40 +* `#16001 `__: DOC: add note on flatten ordering in matlab page +* `#16007 `__: TST: Add tests for the conversion utilities +* `#16008 `__: BUG: Unify handling of string enum converters +* `#16009 `__: MAINT: Replace npyiter_order_converter with PyArray_OrderConverter +* `#16010 `__: BUG: Fix lexsort axis check +* `#16011 `__: DOC: Clarify single-segment arrays in np reference +* `#16014 `__: DOC: Change import error "howto" to link to new troubleshooting... +* `#16015 `__: DOC: update first section of NEP 37 (``__array_function__`` downsides) +* `#16021 `__: REL: Update master after 1.18.3 release. +* `#16024 `__: MAINT: Bump hypothesis from 5.8.3 to 5.10.1 +* `#16025 `__: DOC: initialise random number generator before first use in quickstart +* `#16032 `__: ENH: Fix exception causes in build_clib.py +* `#16038 `__: MAINT,TST: Move _repr_latex tests to test_printing. +* `#16041 `__: BUG: missing 'f' prefix for fstring +* `#16042 `__: ENH: Fix exception causes in build_ext.py +* `#16043 `__: DOC: Add converters example to the loadtxt docstring +* `#16051 `__: DOC: Add missing bracket +* `#16053 `__: DOC: Small typo fixes to NEP 40. +* `#16054 `__: DOC, BLD: update release howto and walkthrough for ananconda.org... +* `#16061 `__: ENH: Chained exceptions in linalg.py and polyutils.py +* `#16064 `__: MAINT: Chain exceptions in several places. +* `#16067 `__: MAINT: Chain exceptions in memmap.py and core.py +* `#16068 `__: BUG: Fix string to bool cast regression +* `#16069 `__: DOC: Added page describing how to contribute to the docs team +* `#16075 `__: DOC: add a note on sampling 2-D arrays to random.choice docstring +* `#16076 `__: BUG: random: Generator.integers(2**32) always returned 0. +* `#16077 `__: BLD: fix path to libgfortran on macOS +* `#16078 `__: DOC: Add axis to random module "new or different" docs +* `#16079 `__: DOC,BLD: Limit timeit iterations in random docs. +* `#16080 `__: BUG: numpy.einsum indexing arrays now accept numpy int type +* `#16081 `__: DOC: add note on type casting to numpy.left_shift(). +* `#16083 `__: DOC: improve development debugging doc +* `#16084 `__: DOC: tweak neps/scope.rst +* `#16085 `__: MAINT: Bump cython from 0.29.16 to 0.29.17 +* `#16086 `__: MAINT: Bump hypothesis from 5.10.1 to 5.10.4 +* `#16094 `__: TST: use latest released PyPy instead of nightly builds +* `#16097 `__: MAINT, DOC: Improve grammar on a comment in the quickstart +* `#16100 `__: NEP 41: Accept NEP 41 and add DType<->scalar duplication paragraph +* `#16101 `__: BLD: put openblas library in local directory on windows +* `#16102 `__: ENH: correct identity for logaddexp2 ufunc: -inf +* `#16113 `__: MAINT: Fix random.PCG64 signature +* `#16119 `__: DOC: Move misplaced news fragment for gh-13421 +* `#16122 `__: DOC: Fix links for NEP 40 in NEP 41 +* `#16125 `__: BUG: lib: Fix a problem with vectorize with default parameters. +* `#16128 `__: ENH: Add equal_nan keyword argument to array_equal +* `#16129 `__: ENH: Better error message when ``bins`` has float value in ``histogramdd``. +* `#16133 `__: MAINT: Unify casting error creation (outside the iterator) +* `#16141 `__: BENCH: Default to building HEAD instead of master +* `#16144 `__: REL: Update master after NumPy 1.18.4 release +* `#16145 `__: DOC: Add VSCode help link to importerror troubleshooting +* `#16147 `__: CI: pin 32-bit manylinux2010 image tag +* `#16151 `__: MAINT: Bump pytz from 2019.3 to 2020.1 +* `#16153 `__: BUG: Correct loop order in MT19937 jump +* `#16155 `__: CI: unpin 32-bit manylinux2010 image tag +* `#16162 `__: BUG: add missing numpy/__init__.pxd to the wheel +* `#16168 `__: BUG:Umath remove unnecessary include of simd.inc in fast_loop_macro.h +* `#16169 `__: DOC,BLD: Add :doc: to whitelisted roles in refguide_check. +* `#16170 `__: ENH: resync numpy/__init__.pxd with upstream +* `#16171 `__: ENH: allow choosing which manylinux artifact to download +* `#16173 `__: MAINT: Mark tests as a subpackage rather than data. +* `#16182 `__: Update Docs : point users of np.outer to np.multiply.outer +* `#16183 `__: DOC: Fix link to numpy docs in README. +* `#16185 `__: ENH: Allow pickle with protocol 5 when higher is requested +* `#16188 `__: MAINT: cleanups to _iotools.StringConverter +* `#16197 `__: DOC: Unify cross-references between array joining methods +* `#16199 `__: DOC: Improve docstring of ``numpy.core.records`` +* `#16201 `__: DOC: update Code of Conduct committee +* `#16203 `__: MAINT: Bump hypothesis from 5.10.4 to 5.12.0 +* `#16204 `__: MAINT: Bump pytest from 5.4.1 to 5.4.2 +* `#16210 `__: DOC: warn about runtime of shares_memory +* `#16213 `__: ENH: backport scipy changes to openblas download script +* `#16214 `__: BUG: skip complex256 arcsinh precision test on glibc2.17 +* `#16215 `__: MAINT: Chain exceptions and use NameError in np.bmat +* `#16216 `__: DOC,BLD: pin sphinx to <3.0 in doc_requirements.txt +* `#16223 `__: BUG: fix signature of PyArray_SearchSorted in __init__.pxd +* `#16224 `__: ENH: add manylinux1 openblas hashes +* `#16226 `__: DOC: Fix Generator.choice docstring +* `#16227 `__: DOC: Add PyDev instructions to troubleshooting doc +* `#16228 `__: DOC: Add Clang and MSVC to supported compilers list +* `#16240 `__: DOC: Warn about behavior of ptp with signed integers. +* `#16258 `__: DOC: Update the f2py section of the "Using Python as Glue" page. +* `#16263 `__: BUG: Add missing decref in fromarray error path +* `#16265 `__: ENH: Add tool for downloading release wheels from Anaconda. +* `#16269 `__: DOC: Fix typos and cosmetic issues +* `#16280 `__: REL: Prepare for the 1.19.0 release +* `#16293 `__: BUG: Fix tools/download-wheels.py. +* `#16301 `__: BUG: Require Python >= 3.6 in setup.py +* `#16312 `__: BUG: relpath fails for different drives on windows +* `#16314 `__: DOC: Fix documentation rendering, +* `#16341 `__: BUG: Don't segfault on bad __len__ when assigning. (gh-16327) +* `#16342 `__: MAINT: Stop Using PyEval_Call* and simplify some uses +* `#16343 `__: BLD: Avoid "visibility attribute not supported" warning. +* `#16344 `__: BUG: Allow attaching documentation twice in add_docstring +* `#16355 `__: MAINT: Remove f-strings in setup.py. (gh-16346) +* `#16356 `__: BUG: Indentation for docstrings +* `#16358 `__: BUG: Fix dtype leak in ``PyArray_FromAny`` error path +* `#16383 `__: ENH: Optimize Cpu feature detect in X86, fix for GCC on macOS... +* `#16398 `__: MAINT: core: Use a raw string for the fromstring docstring. +* `#16399 `__: MAINT: Make ctypes optional on Windows +* `#16400 `__: BUG: Fix small leaks in error path and ``empty_like`` with shape +* `#16402 `__: TST, MAINT: Fix detecting and testing armhf features +* `#16412 `__: DOC,BLD: Update sphinx conf to use xelatex. +* `#16413 `__: DOC,BLD: Update make dist html target. +* `#16414 `__: MAINT, DOC: add index for user docs. +* `#16437 `__: MAINT: support python 3.10 +* `#16456 `__: DOC: Fix troubleshooting code snippet when env vars are empty +* `#16457 `__: REL: Prepare for the NumPy 1.19.0rc2 release. +* `#16526 `__: MAINT:ARMHF Fix detecting feature groups NEON_HALF and NEON_VFPV4 +* `#16527 `__: BUG:random: Error when ``size`` is smaller than broadcast input... +* `#16528 `__: BUG: fix GCC 10 major version comparison +* `#16563 `__: BUG: Ensure SeedSequence 0-padding does not collide with spawn... +* `#16586 `__: BUG: fix sin/cos bug when input is strided array +* `#16602 `__: MAINT: Move and improve ``test_ignore_nan_ulperror``. +* `#16645 `__: REL: Update 1.19.0-changelog.rst for 1.19.0 release. diff --git a/doc/changelog/1.19.1-changelog.rst b/doc/changelog/1.19.1-changelog.rst new file mode 100644 index 000000000000..3b46ffadfdd9 --- /dev/null +++ b/doc/changelog/1.19.1-changelog.rst @@ -0,0 +1,53 @@ + +Contributors +============ + +A total of 15 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Abhinav Reddy + +* Anirudh Subramanian +* Antonio Larrosa + +* Charles Harris +* Chunlin Fang +* Eric Wieser +* Etienne Guesnet + +* Kevin Sheppard +* Matti Picus +* Raghuveer Devulapalli +* Roman Yurchak +* Ross Barnowski +* Sayed Adel +* Sebastian Berg +* Tyler Reddy + +Pull requests merged +==================== + +A total of 25 pull requests were merged for this release. + +* `#16649 `__: MAINT, CI: disable Shippable cache +* `#16652 `__: MAINT: Replace `PyUString_GET_SIZE` with `PyUnicode_GetLength`. +* `#16654 `__: REL: Fix outdated docs link +* `#16656 `__: BUG: raise IEEE exception on AIX +* `#16672 `__: BUG: Fix bug in AVX complex absolute while processing array of... +* `#16693 `__: TST: Add extra debugging information to CPU features detection +* `#16703 `__: BLD: Add CPU entry for Emscripten / WebAssembly +* `#16705 `__: TST: Disable Python 3.9-dev testing. +* `#16714 `__: MAINT: Disable use_hugepages in case of ValueError +* `#16724 `__: BUG: Fix PyArray_SearchSorted signature. +* `#16768 `__: MAINT: Fixes for deprecated functions in scalartypes.c.src +* `#16772 `__: MAINT: Remove unneeded call to PyUnicode_READY +* `#16776 `__: MAINT: Fix deprecated functions in scalarapi.c +* `#16779 `__: BLD, ENH: Add RPATH support for AIX +* `#16780 `__: BUG: Fix default fallback in genfromtxt +* `#16784 `__: BUG: Added missing return after raising error in methods.c +* `#16795 `__: BLD: update cython to 0.29.21 +* `#16832 `__: MAINT: setuptools 49.2.0 emits a warning, avoid it +* `#16872 `__: BUG: Validate output size in bin- and multinomial +* `#16875 `__: BLD, MAINT: Pin setuptools +* `#16904 `__: DOC: Reconstruct Testing Guideline. +* `#16905 `__: TST, BUG: Re-raise MemoryError exception in test_large_zip's... +* `#16906 `__: BUG,DOC: Fix bad MPL kwarg. +* `#16916 `__: BUG: Fix string/bytes to complex assignment +* `#16922 `__: REL: Prepare for NumPy 1.19.1 release diff --git a/doc/changelog/1.19.2-changelog.rst b/doc/changelog/1.19.2-changelog.rst new file mode 100644 index 000000000000..47db1dd59cd7 --- /dev/null +++ b/doc/changelog/1.19.2-changelog.rst @@ -0,0 +1,30 @@ + +Contributors +============ + +A total of 8 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Charles Harris +* Matti Picus +* Pauli Virtanen +* Philippe Ombredanne + +* Sebastian Berg +* Stefan Behnel + +* Stephan Loyd + +* Zac Hatfield-Dodds + +Pull requests merged +==================== + +A total of 9 pull requests were merged for this release. + +* `#16959 `__: TST: Change aarch64 to arm64 in travis.yml. +* `#16998 `__: MAINT: Configure hypothesis in ``np.test()`` for determinism,... +* `#17000 `__: BLD: pin setuptools < 49.2.0 +* `#17015 `__: ENH: Add NumPy declarations to be used by Cython 3.0+ +* `#17125 `__: BUG: Remove non-threadsafe sigint handling from fft calculation +* `#17243 `__: BUG: core: fix ilp64 blas dot/vdot/... for strides > int32 max +* `#17244 `__: DOC: Use SPDX license expressions with correct license +* `#17245 `__: DOC: Fix the link to the quick-start in the old API functions +* `#17272 `__: BUG: fix pickling of arrays larger than 2GiB diff --git a/doc/changelog/1.19.3-changelog.rst b/doc/changelog/1.19.3-changelog.rst new file mode 100644 index 000000000000..5e8dfa10b6ba --- /dev/null +++ b/doc/changelog/1.19.3-changelog.rst @@ -0,0 +1,31 @@ + +Contributors +============ + +A total of 8 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Charles Harris +* Chris Brown + +* Daniel Vanzo + +* E. Madison Bray + +* Hugo van Kemenade + +* Ralf Gommers +* Sebastian Berg +* @danbeibei + + +Pull requests merged +==================== + +A total of 10 pull requests were merged for this release. + +* `#17298 `__: BLD: set upper versions for build dependencies +* `#17336 `__: BUG: Set deprecated fields to null in PyArray_InitArrFuncs +* `#17446 `__: ENH: Warn on unsupported Python 3.10+ +* `#17450 `__: MAINT: Update test_requirements.txt. +* `#17522 `__: ENH: Support for the NVIDIA HPC SDK nvfortran compiler +* `#17568 `__: BUG: Cygwin Workaround for #14787 on affected platforms +* `#17647 `__: BUG: Fix memory leak of buffer-info cache due to relaxed strides +* `#17652 `__: MAINT: Backport openblas_support from master. +* `#17653 `__: TST: Add Python 3.9 to the CI testing on Windows, Mac. +* `#17660 `__: TST: Simplify source path names in test_extending. diff --git a/doc/changelog/1.19.4-changelog.rst b/doc/changelog/1.19.4-changelog.rst new file mode 100644 index 000000000000..82632b990855 --- /dev/null +++ b/doc/changelog/1.19.4-changelog.rst @@ -0,0 +1,16 @@ + +Contributors +============ + +A total of 1 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Charles Harris + +Pull requests merged +==================== + +A total of 2 pull requests were merged for this release. + +* `#17679 `__: MAINT: Add check for Windows 10 version 2004 bug. +* `#17680 `__: REV: Revert OpenBLAS to 1.19.2 version for 1.19.4 diff --git a/doc/changelog/1.19.5-changelog.rst b/doc/changelog/1.19.5-changelog.rst new file mode 100644 index 000000000000..f7cbd5377190 --- /dev/null +++ b/doc/changelog/1.19.5-changelog.rst @@ -0,0 +1,32 @@ + +Contributors +============ + +A total of 8 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Charles Harris +* Christoph Gohlke +* Matti Picus +* Raghuveer Devulapalli +* Sebastian Berg +* Simon Graham + +* Veniamin Petrenko + +* Bernie Gray + + +Pull requests merged +==================== + +A total of 11 pull requests were merged for this release. + +* `#17756 `__: BUG: Fix segfault due to out of bound pointer in floatstatus... +* `#17774 `__: BUG: fix np.timedelta64('nat').__format__ throwing an exception +* `#17775 `__: BUG: Fixed file handle leak in array_tofile. +* `#17786 `__: BUG: Raise recursion error during dimension discovery +* `#17917 `__: BUG: Fix subarray dtype used with too large count in fromfile +* `#17918 `__: BUG: 'bool' object has no attribute 'ndim' +* `#17919 `__: BUG: ensure _UFuncNoLoopError can be pickled +* `#17924 `__: BLD: use BUFFERSIZE=20 in OpenBLAS +* `#18026 `__: BLD: update to OpenBLAS 0.3.13 +* `#18036 `__: BUG: make a variable volatile to work around clang compiler bug +* `#18114 `__: REL: Prepare for the NumPy 1.19.5 release. diff --git a/doc/changelog/1.20.0-changelog.rst b/doc/changelog/1.20.0-changelog.rst new file mode 100644 index 000000000000..f06bd8a8d22d --- /dev/null +++ b/doc/changelog/1.20.0-changelog.rst @@ -0,0 +1,913 @@ + +Contributors +============ + +A total of 184 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Aaron Meurer + +* Abhilash Barigidad + +* Abhinav Reddy + +* Abhishek Singh + +* Al-Baraa El-Hag + +* Albert Villanova del Moral + +* Alex Leontiev + +* Alex Rockhill + +* Alex Rogozhnikov +* Alexander Belopolsky +* Alexander Kuhn-Regnier + +* Allen Downey + +* Andras Deak +* Andrea Olivo + +* Andrew Eckart + +* Anirudh Subramanian +* Anthony Byuraev + +* Antonio Larrosa + +* Ashutosh Singh + +* Bangcheng Yang + +* Bas van Beek + +* Ben Derrett + +* Ben Elliston + +* Ben Nathanson + +* Bernie Gray + +* Bharat Medasani + +* Bharat Raghunathan +* Bijesh Mohan + +* Bradley Dice + +* Brandon David + +* Brandt Bucher +* Brian Soto + +* Brigitta Sipocz +* Cameron Blocker + +* Carl Leake + +* Charles Harris +* Chris Brown + +* Chris Vavaliaris + +* Christoph Gohlke +* Chunlin Fang +* CloseChoice + +* Daniel G. A. Smith + +* Daniel Hrisca +* Daniel Vanzo + +* David Pitchford + +* Davide Dal Bosco + +* Derek Homeier +* Dima Kogan + +* Dmitry Kutlenkov + +* Douglas Fenstermacher + +* Dustin Spicuzza + +* E. Madison Bray + +* Elia Franzella + +* Enrique Matías Sánchez + +* Erfan Nariman | Veneficus + +* Eric Larson +* Eric Moore +* Eric Wieser +* Erik M. Bray +* EthanCJ-git + +* Etienne Guesnet + +* FX Coudert + +* Felix Divo +* Frankie Robertson + +* Ganesh Kathiresan +* Gengxin Xie +* Gerry Manoim + +* Guilherme Leobas +* Hassan Kibirige +* Hugo Mendes + +* Hugo van Kemenade +* Ian Thomas + +* InessaPawson + +* Isabela Presedo-Floyd + +* Isuru Fernando +* Jakob Jakobson + +* Jakub Wilk +* James Myatt + +* Jesse Li + +* John Hagen + +* John Zwinck +* Joseph Fox-Rabinovitz +* Josh Wilson +* Jovial Joe Jayarson + +* Julia Signell + +* Jun Kudo + +* Karan Dhir + +* Kaspar Thommen + +* Kerem Hallaç +* Kevin Moore + +* Kevin Sheppard +* Klaus Zimmermann + +* LSchroefl + +* Laurie + +* Laurie Stephey + +* Levi Stovall + +* Lisa Schwetlick + +* Lukas Geiger + +* Madhulika Jain Chambers + +* Matthias Bussonnier +* Matti Picus +* Melissa Weber Mendonça +* Michael Hirsch +* Nick R. Papior +* Nikola Forró +* Noman Arshad + +* Paul YS Lee + +* Pauli Virtanen +* Paweł Redzyński + +* Peter Andreas Entschev +* Peter Bell +* Philippe Ombredanne + +* Phoenix Meadowlark + +* Piotr Gaiński +* Raghav Khanna + +* Raghuveer Devulapalli +* Rajas Rade + +* Rakesh Vasudevan +* Ralf Gommers +* Raphael Kruse + +* Rashmi K A + +* Robert Kern +* Rohit Sanjay + +* Roman Yurchak +* Ross Barnowski +* Royston E Tauro + +* Ryan C Cooper + +* Ryan Soklaski +* Safouane Chergui + +* Sahil Siddiq + +* Sarthak Vineet Kumar + +* Sayed Adel +* Sebastian Berg +* Sergei Vorfolomeev + +* Seth Troisi +* Sidhant Bansal + +* Simon Gasse +* Simon Graham + +* Stefan Appelhoff + +* Stefan Behnel + +* Stefan van der Walt +* Steve Dower +* Steve Joachim + +* Steven Pitman + +* Stuart Archibald +* Sturla Molden +* Susan Chang + +* Takanori H + +* Tapajyoti Bose + +* Thomas A Caswell +* Tina Oberoi +* Tirth Patel +* Tobias Pitters + +* Tomoki, Karatsu + +* Tyler Reddy +* Veniamin Petrenko + +* Wansoo Kim + +* Warren Weckesser +* Wei Yang + +* Wojciech Rzadkowski +* Yang Hau + +* Yogesh Raisinghani + +* Yu Feng +* Yuya Unno + +* Zac Hatfield-Dodds +* Zuhair Ali-Khan + +* @abhilash42 + +* @danbeibei + +* @dojafrat +* @dpitch40 + +* @forfun + +* @iamsoto + +* @jbrockmendel + +* @leeyspaul + +* @mitch + +* @prateek arora + +* @serge-sans-paille + +* @skywalker + +* @stphnlyd + +* @xoviat +* @谭九鼎 + +* @JMFT + +* @Jack + +* @Neal C + + +Pull requests merged +==================== + +A total of 716 pull requests were merged for this release. + +* `#13516 `__: ENH: enable multi-platform SIMD compiler optimizations +* `#14779 `__: NEP 36 (fair play) +* `#14882 `__: DEP: Deprecate aliases of builtin types in python 3.7+ +* `#15037 `__: BUG: ``np.resize`` negative shape and subclasses edge case fixes +* `#15121 `__: ENH: random: Add the method ``permuted`` to Generator. +* `#15162 `__: BUG,MAINT: Fix issues with non-reduce broadcasting axes +* `#15471 `__: BUG: Ensure PyArray_FromScalar always returns the requested dtype +* `#15507 `__: NEP 42: Technical decisions for new DTypes +* `#15508 `__: API: Create Preliminary DTypeMeta class and np.dtype subclasses +* `#15551 `__: DOC: Simd optimization documentation +* `#15604 `__: MAINT: Avoid exception in NpzFile destructor if constructor raises... +* `#15666 `__: ENH: Improved ``__str__`` for polynomials +* `#15759 `__: BUILD: Remove Accelerate support +* `#15791 `__: [DOC] Added tutorial about the numpy.ma module. +* `#15852 `__: ENH: Add where argument to np.mean +* `#15886 `__: DEP: Deprecate passing shape=None to mean shape=() +* `#15900 `__: DEP: Ensure indexing errors will be raised even on empty results +* `#15997 `__: ENH: improve printing of arrays with multi-line reprs +* `#16056 `__: DEP: Deprecate inexact matches for mode, searchside +* `#16130 `__: DOC: Correct documentation of ``__array__`` when used as output... +* `#16134 `__: ENH: Implement concatenate dtype and casting keyword arguments +* `#16156 `__: DEP: Deprecate ``numpy.dual``. +* `#16161 `__: BUG: Potential fix for divmod(1.0, 0.0) to raise divbyzero and... +* `#16167 `__: DOC: Increase guidance and detail of np.polynomial docstring +* `#16174 `__: DOC: Add transition note to all lib/poly functions +* `#16200 `__: ENH: Rewrite of array-coercion to support new dtypes +* `#16205 `__: ENH: Add ``full_output`` argument to ``f2py.compile``. +* `#16207 `__: DOC: Add PyArray_ContiguousFromObject C docs +* `#16232 `__: DEP: Deprecate ufunc.outer with matrix inputs +* `#16237 `__: MAINT: precompute ``log(2.0 * M_PI)`` in ``random_loggam`` +* `#16238 `__: MAINT: Unify cached (C-level static) imports +* `#16239 `__: BUG,DOC: Allow attach docs twice but error if wrong +* `#16242 `__: BUG: Fix default fallback in genfromtxt +* `#16247 `__: ENH:Umath Replace raw SIMD of unary float point(32-64) with NPYV... +* `#16248 `__: MRG, ENH: added edge keyword argument to digitize +* `#16253 `__: DOC: Clarify tiny/xmin in finfo and machar +* `#16254 `__: MAINT: Chain exceptions in generate_umath.py +* `#16257 `__: DOC: Update the f2py section of the "Using Python as Glue" page. +* `#16260 `__: DOC: Improve ``rec.array`` function documentation +* `#16266 `__: ENH: include dt64/td64 isinstance checks in ``__init__.pxd`` +* `#16267 `__: DOC: Clarifications for np.std +* `#16273 `__: BUG: Order percentile monotonically +* `#16274 `__: MAINT: cleanups to quantile +* `#16275 `__: REL: Update master after 1.19.x branch. +* `#16276 `__: BUG: Ensure out argument is returned by identity for 0d arrays +* `#16278 `__: DOC: Clarifications for ``np.var``. +* `#16283 `__: DOC: Add a note about performance of isclose compared to math.isclose +* `#16284 `__: MAINT: Clean up the implementation of quantile +* `#16285 `__: MAINT: Bump hypothesis from 5.12.0 to 5.14.0 +* `#16288 `__: BLD: Avoid "visibility attribute not supported" warning +* `#16291 `__: DOC: Improve "tobytes" docstring. +* `#16292 `__: BUG: Fix tools/download-wheels.py. +* `#16295 `__: BUG: Require Python >= 3.6 in setup.py +* `#16296 `__: DOC: Fix malformed docstrings in ma. +* `#16297 `__: ENH: Optimize Cpu feature detect in X86, fix for GCC on macOS +* `#16298 `__: BUG: np.info does not show keyword-only arguments +* `#16300 `__: DOC: Fix bad reference in ``numpy.ma`` +* `#16304 `__: TST, MAINT: Fix detecting and testing armhf features +* `#16305 `__: DOC: Fix packbits documentation rendering, +* `#16306 `__: DOC: Fix troubleshooting code snippet when env vars are empty +* `#16308 `__: BUG: relpath fails for different drives on windows +* `#16311 `__: DOC: Fix ``np.ma.core.doc_note`` +* `#16316 `__: MAINT: Bump numpydoc version +* `#16318 `__: MAINT: Stop Using PyEval_Call* and simplify some uses +* `#16321 `__: ENH: Improve the ARM cpu feature detection by parsing /proc/cpuinfo +* `#16323 `__: DOC: Reconstruct Testing Guideline. +* `#16327 `__: BUG: Don't segfault on bad __len__ when assigning. +* `#16329 `__: MAINT: Cleanup 'tools/download-wheels.py' +* `#16332 `__: DOC: link np.interp to SciPy's interpolation functions (closes... +* `#16333 `__: DOC: Fix spelling typo - homogenous to homogeneous. (#16324) +* `#16334 `__: ENH: Use AVX-512 for np.isnan, np.infinite, np.isinf and np.signbit +* `#16336 `__: BUG: Fix refcounting in add_newdoc +* `#16337 `__: CI: Create a link for the circleCI artifact +* `#16346 `__: MAINT: Remove f-strings in setup.py. +* `#16348 `__: BUG: Fix dtype leak in ``PyArray_FromAny`` error path +* `#16349 `__: BUG: Indentation for docstrings +* `#16350 `__: BUG: Set readonly flag in array interface +* `#16351 `__: BUG: Fix small leaks in error path and ``empty_like`` with shape +* `#16362 `__: MAINT: Streamline download-wheels. +* `#16365 `__: DOC: Fix an obvious mistake in a message printed in doc/Makefile. +* `#16367 `__: MAINT: Bump cython from 0.29.17 to 0.29.19 +* `#16368 `__: MAINT: Bump hypothesis from 5.14.0 to 5.15.1 +* `#16369 `__: MAINT: Bump pytest-cov from 2.8.1 to 2.9.0 +* `#16371 `__: ENH: Use AVX-512 for np.frexp and np.ldexp +* `#16373 `__: MAINT, DOC: add index for user docs. +* `#16375 `__: ENH: ARM Neon implementation with intrinsic for np.argmax. +* `#16385 `__: DOC: Tighten howto-docs guide #16259 +* `#16387 `__: MAINT: Make ctypes optional on Windows +* `#16389 `__: ENH: Hardcode buffer handling for simple scalars +* `#16392 `__: MAINT: Stop uploading wheels to Rackspace. +* `#16393 `__: MAINT: Use a raw string for the fromstring docstring. +* `#16395 `__: ENH: Validate and disable CPU features in runtime +* `#16397 `__: ENH: Implement the NumPy C SIMD vectorization interface +* `#16404 `__: DOC,BLD: Update make dist html target. +* `#16408 `__: DOC,BLD: Update sphinx conf to use xelatex. +* `#16409 `__: TST, CI: turn on codecov patch diffs +* `#16411 `__: BUG: endpoints of array returned by geomspace() should match... +* `#16417 `__: MAINT: support python 3.10 +* `#16418 `__: MAINT: Chain some exceptions. +* `#16420 `__: DOC: Improve intersect1d docstring +* `#16422 `__: DOC: Update assert_warns parameter list +* `#16423 `__: TST: Simplify assert_warns in test_io.py +* `#16427 `__: DOC: make NEP 18 status Final +* `#16428 `__: DOC: Add style guide to howto_document +* `#16430 `__: DOC: NEP for C style guide +* `#16433 `__: DOC: Fix description of dtype default in linspace +* `#16435 `__: BUG: Add extern to PyArrayDTypeMeta_Type declaration +* `#16436 `__: DOC: Add a reference into NEP 29, +* `#16438 `__: MAINT: Catch remaining cases of Py_SIZE and Py_TYPE as lvalues +* `#16442 `__: ENH: Fix deprecated warn for Intel/Apple/Clang Compiler +* `#16444 `__: DOC: make clearer that sinc is normalized by a factor pi +* `#16445 `__: DOC: update roadmap +* `#16446 `__: BUG: fixes einsum output order with optimization (#14615) +* `#16447 `__: DOC: add a "make show" command to doc/Makefile +* `#16450 `__: DOC: Add a NEP link to all neps. +* `#16452 `__: DOC,ENH: extend error message when Accelerate is detected +* `#16454 `__: TST: Add tests for PyArray_IntpConverter +* `#16463 `__: DOC: Improve assert_warns docstring with example +* `#16464 `__: MAINT: Bump hypothesis from 5.15.1 to 5.16.0 +* `#16465 `__: DOC: Fix development_workflow links +* `#16468 `__: BUG: fix GCC 10 major version comparison +* `#16471 `__: BLD: install mingw32 v7.3.0 for win32 +* `#16472 `__: DOC: Fixes for 18 broken links +* `#16474 `__: MAINT: use zip instead of range in piecewise +* `#16476 `__: ENH: add ``norm=forward,backward`` to numpy.fft functions +* `#16482 `__: SIMD: Optimize the performace of np.packbits in ARM-based machine. +* `#16485 `__: BUG: Fix result when a gufunc output broadcasts the inputs. +* `#16500 `__: DOC: Point Contributing page to new NEP 45 +* `#16501 `__: MAINT: make Py_SET_SIZE and Py_SET_TYPE macros a bit safer +* `#16503 `__: BUG:random: Error when ``size`` is smaller than broadcast input... +* `#16504 `__: DOC: Correct MV Normal sig +* `#16505 `__: BUG: raise IEEE exception on AIX +* `#16506 `__: DOC: only single-polynomial fitting in np.polynomial.Polynomial.fit() +* `#16510 `__: DOC: Minor rounding correction in Generator.binomial +* `#16514 `__: STY: trivial doc style fix in NEP 45. +* `#16515 `__: ENH: add type stubs from numpy-stubs +* `#16519 `__: BUG: f2py: make callbacks threadsafe +* `#16520 `__: STY: f2py: replace \t by whitespace for readability +* `#16522 `__: MAINT:ARMHF Fix detecting feature groups NEON_HALF and NEON_VFPV4 +* `#16523 `__: MAINT: Improve buffer speed +* `#16524 `__: MAINT: f2py: move thread-local declaration definition to common... +* `#16529 `__: BUG: Fix cython warning in random/_common.pyx. +* `#16530 `__: MAINT: Bump pytest from 5.4.2 to 5.4.3 +* `#16532 `__: BUG: Remove non-threadsafe sigint handling from fft calculation +* `#16540 `__: SIMD: SSE2 intrinsic implementation for float64 input of np.enisum +* `#16551 `__: BUG: Ensure SeedSequence 0-padding does not collide with spawn... +* `#16554 `__: DEP: Remove deprecated numeric types and deprecate remaining +* `#16555 `__: CI: drop win32 3.7, 3.6 builds +* `#16556 `__: MAINT: simplifying annotations for np.core.from_numeric +* `#16558 `__: ENH: make typing module available at runtime +* `#16570 `__: ENH: Throw TypeError on operator concat on Numpy Arrays +* `#16571 `__: TST: Add new tests for array coercion +* `#16572 `__: BUG: fix sin/cos bug when input is strided array +* `#16574 `__: MAINT: fix name of first parameter to dtype constructor in type... +* `#16581 `__: DOC: Added an example for np.transpose(4d_array) +* `#16583 `__: MAINT: changed np.generic arguments to positional-only +* `#16589 `__: MAINT: Remove nickname from polynomial classes. +* `#16590 `__: DOC: Clarify dtype default for logspace and geomspace +* `#16591 `__: DOC: Disallow complex args in arange +* `#16592 `__: BUG: Raise TypeError for float->timedelta promotion +* `#16594 `__: ENH: Add ``__f2py_numpy_version__`` attribute to Fortran modules. +* `#16596 `__: BUG: Fix reference count leak in mapping.c +* `#16601 `__: MAINT: Move and improve ``test_ignore_nan_ulperror``. +* `#16603 `__: DOC: make addition of types a "new feature" in release notes +* `#16605 `__: MAINT: Avx512 intrinsics implementation for float64 input np.log +* `#16606 `__: MAINT: Bump pytest-cov from 2.9.0 to 2.10.0 +* `#16607 `__: MAINT: Bump hypothesis from 5.16.0 to 5.16.1 +* `#16613 `__: MAINT: bump mypy version to 0.780 +* `#16617 `__: BLD: Openblas 0.3.10 +* `#16618 `__: ENH: add annotation for abs +* `#16619 `__: BLD: check if std=c99 is really required +* `#16620 `__: MAINT, CI: disable Shippable cache +* `#16621 `__: BENCH: Expand array-creation benchmarks +* `#16622 `__: MAINT: Implemented two dtype-related TODO's +* `#16623 `__: BUG: Initialize stop-reading in array_from_text +* `#16627 `__: DOC: Updated documentation for numpy.squeeze +* `#16629 `__: ENH: add tool to find functions missing types +* `#16630 `__: ENH,BUG:distutils Remove the origins from the implied features +* `#16633 `__: MAINT: lib: Some code clean up in loadtxt +* `#16635 `__: BENCH: remove obsolete goal_time param +* `#16639 `__: BUG: Fix uint->timedelta promotion to raise TypeError +* `#16642 `__: MAINT: Replace ``PyUString_GET_SIZE`` with ``PyUnicode_GetLength``. +* `#16643 `__: REL: Fix outdated docs link +* `#16644 `__: MAINT: Improve performance of np.full +* `#16646 `__: TST: add a static typing test for memoryviews as ArrayLikes +* `#16647 `__: ENH: Added annotations to 8 functions from np.core.fromnumeric +* `#16648 `__: REL: Update master after 1.19.0 release. +* `#16650 `__: ENH: Allow genfromtxt to unpack structured arrays +* `#16651 `__: MAINT: Prefer generator expressions over list comprehensions... +* `#16653 `__: DOC: cross-reference numpy.dot and numpy.linalg.multi_dot +* `#16658 `__: MAINT: Bump hypothesis from 5.16.1 to 5.16.3 +* `#16659 `__: MAINT: Bump mypy from 0.780 to 0.781 +* `#16664 `__: DOC: Add lib.format.open_memmap to autosummary. +* `#16666 `__: BUG: Fix bug in AVX complex absolute while processing array of... +* `#16669 `__: MAINT: remove blacklist/whitelist terms +* `#16671 `__: DOC: Simplify and update git setup page +* `#16674 `__: TST: Add extra debugging information to CPU features detection +* `#16675 `__: ENH: Add support for file like objects to np.core.records.fromfile +* `#16683 `__: DOC: updated gcc minimum recommend version to build from source +* `#16684 `__: MAINT: Allow None to be passed to certain generic subclasses +* `#16690 `__: DOC: fixed docstring for descr_to_dtype +* `#16691 `__: DOC: Remove "matrix" from ``triu`` docstring. +* `#16696 `__: MAINT: add py.typed sentinel to package manifest +* `#16699 `__: MAINT: Fixup quantile tests to not use ``np.float`` +* `#16702 `__: BLD: Add CPU entry for Emscripten / WebAssembly +* `#16704 `__: TST: Disable Python 3.9-dev testing. +* `#16706 `__: DOC: Add instruction about stable symlink +* `#16708 `__: MAINT: Disable use_hugepages in case of ValueError +* `#16709 `__: DOC: Add dep directive to alen docstring. +* `#16710 `__: ENH, BLD: Add RPATH support for AIX +* `#16718 `__: DOC: fix typo +* `#16720 `__: BUG: Fix PyArray_SearchSorted signature. +* `#16723 `__: NEP: Initial draft for NEP 43 for extensible ufuncs +* `#16729 `__: ENH: Add annotations to the last 8 functions in numpy.core.fromnumeric +* `#16730 `__: ENH: Use f90 compiler specified in f2py command line args for... +* `#16731 `__: DOC: reword random c-api introduction, cython is documented in... +* `#16735 `__: DOC: Tweak a sentence about broadcasting. +* `#16736 `__: DOC: Prepend ``ma.`` to references in ``numpy.ma`` +* `#16738 `__: DOC: Remove redundant word +* `#16742 `__: DOC: add unique() to See Also of repeat() +* `#16743 `__: DOC: add example to unique() and make connection to repeat() +* `#16747 `__: MAINT: Chaining exceptions in numpy/core/_internal.py +* `#16752 `__: BLD: add manylinux1 OpenBlAS 0.3.10 hashes and test for them +* `#16757 `__: DOC: Add Matti Picus to steering council page +* `#16759 `__: ENH: make dtype generic over scalar type +* `#16760 `__: DOC: Added a section in the 'Iterating over arrays' doc page... +* `#16761 `__: MAINT: Tidy exception chaining in _datasource.py +* `#16762 `__: MAINT: Fixes for deprecated functions in scalartypes.c.src +* `#16764 `__: MAINT: Bump mypy from 0.781 to 0.782 +* `#16765 `__: MAINT: Bump hypothesis from 5.16.3 to 5.19.0 +* `#16767 `__: ENH: Update NumPy logos +* `#16770 `__: MAINT: Remove unneeded call to PyUnicode_READY +* `#16771 `__: MAINT: Fix deprecated functions in scalarapi.c +* `#16775 `__: DOC: switch to logo with text +* `#16777 `__: BUG: Added missing return after raising error in methods.c +* `#16778 `__: NEP: Update NEP 42 to note the issue of circular references +* `#16782 `__: ENH, TST: Bring the NumPy C SIMD vectorization interface "NPYV"... +* `#16786 `__: BENCH: Add basic benchmarks for scalar indexing and assignment +* `#16789 `__: BUG: fix decode error when building and get rid of warn +* `#16792 `__: DOC: Minor RST formatting. +* `#16793 `__: BLD, MAINT: update cython to 0.29.21 +* `#16794 `__: TST: Upgrade to Python 3.8 for DEBUG testing. +* `#16798 `__: DOC: Fix RST/numpydoc standard. +* `#16800 `__: MAINT: Move typing tests +* `#16802 `__: MAINT: Explicitly disallow object user dtypes +* `#16805 `__: DOC: add example to corrcoef function +* `#16806 `__: DOC: adding docs on passing dimensions as tuple to ndindex +* `#16807 `__: BUG, MAINT: Remove overzealous automatic RST link +* `#16811 `__: DOC: Add explanation of 'K' and 'A' layout options to 'asarray*'... +* `#16814 `__: DOC: Add a reST label to /user/building.rst +* `#16815 `__: BUG: fix mgrid output for lower precision float inputs +* `#16816 `__: BLD: temporarily disable OpenBLAS hash checks +* `#16817 `__: BUG: Do not inherit flags from the structured part of a union... +* `#16819 `__: DOC: replace dec.slow with pytest.mark.slow +* `#16820 `__: MAINT: Make void scalar to array creation copy when dtype is... +* `#16821 `__: DOC: fix inconsistent parameter name in np.ndindex docstring +* `#16822 `__: MAINT: setuptools 49.2.0 emits a warning, avoid it +* `#16824 `__: DOC: add examples to random number generator pages +* `#16826 `__: DOC: describe ufunc copy behavior when input and output overlap +* `#16827 `__: MAINT: Fix ``runtest.py`` warning. +* `#16829 `__: DOC,BLD: Add pandas to doc_requirements.txt +* `#16831 `__: MAINT: fix sphinx deprecation +* `#16834 `__: Avoid using uninitialized bytes in getlimits.py. +* `#16835 `__: DOC: Explaining why datetime64 doesn't work for allclose + isclose +* `#16836 `__: DOC: improve SIMD features tables +* `#16837 `__: BLD: update openblas hashes, re-enable check +* `#16838 `__: MAINT: Remove code that will never run +* `#16840 `__: MAINT: Bump hypothesis from 5.19.0 to 5.19.1 +* `#16841 `__: BUG: linspace should round towards -infinity +* `#16845 `__: TST: Disable shippable until we can fix it. +* `#16847 `__: MAINT: Remove Duplicated Code (function extract rmap) +* `#16848 `__: MAINT: Remove Duplicated Code +* `#16849 `__: MAINT: Change for loop (range -> for each) +* `#16850 `__: DEP: Deprecate NumPy object scalars +* `#16854 `__: DOC: clarify whats required for new features see #13924 +* `#16857 `__: MAINT: fix new compiler warnings on clang +* `#16858 `__: BUG: fix the search dir of dispatch-able sources +* `#16860 `__: MAINT: Remove deprecated python function 'file()' +* `#16868 `__: BUG: Validate output size in bin- and multinomial +* `#16870 `__: BLD, MAINT: Pin setuptools +* `#16871 `__: BUG: Update compiler check for AVX-512F +* `#16874 `__: TST, MAINT: fix the test for ``np.ones`` +* `#16878 `__: DOC: edit to the documentation of lib/polynomial.py/polyfit +* `#16879 `__: MAINT: Configure hypothesis in ``np.test()`` for determinism,... +* `#16882 `__: BLD: Remove unused pip install +* `#16883 `__: BUG,DOC: Fix bad MPL kwarg in docs +* `#16886 `__: DOC: Fix types including curly braces +* `#16887 `__: DOC: Remove the links for ``True`` and ``False`` +* `#16888 `__: ENH: Integrate the new CPU dispatcher with umath generator +* `#16890 `__: TST, BUG: Re-raise MemoryError exception in test_large_zip's... +* `#16894 `__: DOC: Fix wrong markups in ``arrays.dtypes`` +* `#16896 `__: DOC: Remove links for C codes +* `#16897 `__: DOC: Fix the declarations of C fuctions +* `#16899 `__: MNT: also use Py_SET_REFCNT instead of Py_REFCNT +* `#16900 `__: MAINT: Chaining exceptions in numpy/__init__.py +* `#16907 `__: DOC: update val to be scalar or array like optional closes #16901 +* `#16910 `__: MAINT: Bump hypothesis from 5.19.1 to 5.20.2 +* `#16911 `__: ENH: Speed up trim_zeros +* `#16914 `__: BUG: Fix string/bytes to complex assignment +* `#16917 `__: DOC: Add correctness vs strictness consideration for np.dtype +* `#16919 `__: DOC: Add ufunc docstring to generated docs. +* `#16925 `__: REL: Update master after 1.19.1 release. +* `#16931 `__: Revert "Merge pull request #16248 from alexrockhill/edge" +* `#16935 `__: ENH: implement NEP-35's ``like=`` argument +* `#16936 `__: BUG: Fix memory leak of buffer-info cache due to relaxed strides +* `#16938 `__: ENH,API: Store exported buffer info on the array +* `#16940 `__: BLD: update OpenBLAS build +* `#16941 `__: BUG: Allow array-like types to be coerced as object array elements +* `#16943 `__: DEP: Deprecate size-one ragged array coercion +* `#16944 `__: Change the name of the folder "icons" to "logo". +* `#16949 `__: ENH: enable colors for ``runtests.py --ipython`` +* `#16950 `__: DOC: Clarify input to irfft/irfft2/irfftn +* `#16952 `__: MAINT: Bump hypothesis from 5.20.2 to 5.23.2 +* `#16953 `__: update numpy/lib/arraypad.py with appropriate chain exception +* `#16957 `__: MAINT: Use arm64 instead of aarch64 on travisCI. +* `#16962 `__: MAINT: Chain exception in ``distutils/fcompiler/environment.py``. +* `#16966 `__: MAINT: Added the ``order`` parameter to ``np.array()`` +* `#16969 `__: ENH: Add Neon SIMD implementations for add, sub, mul, and div +* `#16973 `__: DOC: Fixed typo in lib/recfunctions.py +* `#16974 `__: TST: Add pypy win32 CI testing. +* `#16982 `__: ENH: Increase the use of ``Literal`` types +* `#16986 `__: ENH: Add NumPy declarations to be used by Cython 3.0+ +* `#16988 `__: DOC: Add the new NumPy logo to Sphinx pages +* `#16991 `__: MAINT: Bump hypothesis from 5.23.2 to 5.23.9 +* `#16992 `__: MAINT: Bump pytest from 5.4.3 to 6.0.1 +* `#16993 `__: BLD: pin setuptools < 49.2.0 +* `#16996 `__: DOC: Revise glossary page +* `#17002 `__: DOC: clip() allows arguments. +* `#17009 `__: NEP: Updated NEP-35 with keyword-only instruction +* `#17010 `__: BUG: Raise correct errors in boolean indexing fast path +* `#17013 `__: MAINT: Simplify scalar power +* `#17014 `__: MAINT: Improve error handling in umathmodule setup +* `#17022 `__: DOC: Fix non-matching pronoun. +* `#17028 `__: DOC: Disclaimer for FFT library +* `#17029 `__: MAINT: Add error return to all casting functionality and NpyIter +* `#17033 `__: BUG: fix a compile and a test warning +* `#17036 `__: DOC: Clarify that ``np.char`` comparison functions always return... +* `#17039 `__: DOC: Use a less ambiguous example for array_split +* `#17041 `__: MAINT: Bump hypothesis from 5.23.9 to 5.23.12 +* `#17048 `__: STY: core._internal style fixups +* `#17050 `__: MAINT: Remove _EXTRAFLAGS variable +* `#17050 `__: MAINT: change ``for line in open()`` to ``with open() as f`` +* `#17052 `__: MAINT: Delete obsolete conversion to list +* `#17053 `__: BUG: fix typo in polydiv that prevented promotion to poly1d +* `#17055 `__: MAINT: Replace lambda function by list comprehension +* `#17058 `__: MAINT: Revert boolean casting back to elementwise comparisons... +* `#17059 `__: BUG: fix pickling of arrays larger than 2GiB +* `#17062 `__: API, BUG: Raise error on complex input to i0 +* `#17063 `__: MAINT: Remove obsolete conversion to set +* `#17067 `__: DEP: lib: Remove the deprecated financial functions. +* `#17068 `__: MAINT, BUG: Remove uses of PyString_FromString. +* `#17074 `__: DOC: use the pydata_sphinx_theme +* `#17078 `__: DOC: Fixes duplication of toctree content (Closes #17077) +* `#17091 `__: MAINT: Bump pytest-cov from 2.10.0 to 2.10.1 +* `#17092 `__: MAINT: Bump hypothesis from 5.23.12 to 5.26.0 +* `#17093 `__: NEP: Adjust NEP-35 to make it more user-accessible +* `#17104 `__: ENH: Add placeholder stubs for all sub-modules +* `#17109 `__: MAINT: Split einsum into multiple files +* `#17112 `__: BUG: Handle errors from the PyCapsule API +* `#17115 `__: DOC: Fix spacing in vectorize doc +* `#17116 `__: API: Remove ``np.ctypeslib.ctypes_load_library`` +* `#17119 `__: DOC: make spacing consistent in NEP 41 bullet points +* `#17121 `__: BUG: core: fix ilp64 blas dot/vdot/... for strides > int32 max +* `#17123 `__: ENH: allow running mypy through runtests.py +* `#17127 `__: MAINT: Remove duplicated symbols from link step +* `#17129 `__: BLD: Check for reduce intrinsics and AVX512BW mask operations +* `#17132 `__: MAINT: Chain some exceptions in arraysetops. +* `#17133 `__: MAINT: Chain ValueError in ma.timer_comparison +* `#17137 `__: API,MAINT: Rewrite promotion using common DType and common instance +* `#17141 `__: MAINT: Make arrayprint str and repr the ndarray defaults. +* `#17142 `__: DOC: NEP-42: Fix a few typos. +* `#17143 `__: MAINT: Change handling of the expired financial functions. +* `#17144 `__: ENH: Add annotations to 3 functions in ``np.core.function_base`` +* `#17145 `__: MAINT, BUG: Replace uses of PyString_AsString. +* `#17146 `__: MAINT: ``Replace PyUString_*`` by ``PyUnicode_*`` equivalents. +* `#17149 `__: MAINT: Replace PyInt macros with their PyLong replacement +* `#17150 `__: ENH: Add support for the abstract scalars to cython code +* `#17151 `__: BUG: Fix incorrect cython definition of npy_cfloat +* `#17152 `__: MAINT: Clean up some ``Npy_`` vs ``Py_`` macro usage +* `#17154 `__: DOC: Remove references to PyCObject +* `#17159 `__: DOC: Update numpy4matlab +* `#17160 `__: Clean up some more bytes vs unicode handling +* `#17161 `__: BUG: Remove Void special case for "safe casting" +* `#17163 `__: MAINT: Remove redundant headers +* `#17164 `__: MAINT: Remove NPY_COPY_PYOBJECT_PTR +* `#17167 `__: BLD: Merge the npysort library into multiarray +* `#17168 `__: TST: Add tests mapping out the rules for metadata in promotion +* `#17171 `__: BUG: revert trim_zeros changes from gh-16911 +* `#17172 `__: ENH: Make ``np.complexfloating`` generic w.r.t. ``np.floating`` +* `#17176 `__: MAINT/ENH: datetime: remove calls to PyUnicode_AsASCIIString,... +* `#17180 `__: ENH: Added missing methods to ``np.flatiter`` +* `#17181 `__: DOC: Correct error in description of ndarray.base +* `#17182 `__: DOC: Document ``dtype.metadata`` +* `#17186 `__: MAINT: Use utf8 strings in more of datetime +* `#17188 `__: MAINT: Add placeholder stubs for ``ndarray`` and ``generic`` +* `#17191 `__: MAINT: Bump hypothesis from 5.26.0 to 5.30.0 +* `#17193 `__: MAINT: Remove some callers of functions in numpy.compat +* `#17195 `__: ENH: Make the window functions exactly symmetric +* `#17197 `__: MAINT: Improve error handling in npy_cpu_init +* `#17199 `__: DOC: Fix the documented signatures of four ``ufunc`` methods +* `#17201 `__: MAINT: Make the ``NPY_CPU_DISPATCH_CALL`` macros expressions not... +* `#17204 `__: DOC: Fixed headings for tutorials so they appear at new theme... +* `#17210 `__: DOC: Canonical_urls +* `#17214 `__: MAINT: Fix various issues with the ``np.generic`` annotations +* `#17215 `__: DOC: Use official MATLAB spelling in numpy-for-matlab-users.rst +* `#17219 `__: BLD: enabled negation of library choices in NPY_*_ORDER +* `#17220 `__: BUG, DOC: comment out metadata added via javascript +* `#17222 `__: MAINT, DOC: move informational files from numpy.doc.*.py to their... +* `#17223 `__: MAINT: use sysconfig not distutils.sysconfig where possible +* `#17225 `__: BUG: Fix dimension discovery of within array ragged cases +* `#17227 `__: DOC: Added templates for different types of issues. +* `#17233 `__: DEP: Deprecated ndindex.ndincr +* `#17235 `__: MAINT: Remove old PY_VERSION_HEX and sys.version_info code +* `#17237 `__: BUG: Avoid using ``np.random`` in typing tests. +* `#17238 `__: DOC: Use SPDX license expressions with correct license +* `#17239 `__: DOC: Fix link quick-start in old random API functions +* `#17240 `__: MAINT: added exception chaining in shape_base.py +* `#17241 `__: MAINT: ``__array_interface__`` data address cannot be bytes +* `#17242 `__: MAINT: Run slow CI jobs earlier so builds finishes sooner +* `#17247 `__: ENH: Add tool to help speed up Travis CI +* `#17250 `__: DOC: Fix docstring cross-referencing +* `#17252 `__: DOC: Added a PR "Reviewer guidelines" document. +* `#17257 `__: DOC: work around a bug in the new theme +* `#17258 `__: SIMD: add fused multiply subtract/add intrinics for all supported... +* `#17259 `__: MAINT: Bump hypothesis from 5.30.0 to 5.33.0 +* `#17260 `__: MAINT: Bump pydata-sphinx-theme from 0.3.2 to 0.4.0 +* `#17263 `__: DOC: add new glossary terms +* `#17264 `__: DOC: remove some glosssary terms +* `#17267 `__: TST: Fix the path to ``mypy.ini`` in ``runtests.py`` +* `#17268 `__: BUG: sysconfig attributes/distutils issue +* `#17273 `__: ENH: Annotate the arithmetic operations of ``ndarray`` and ``generic`` +* `#17278 `__: MAINT: Merge together index page content into a single file +* `#17279 `__: DOC: Fix a typo in shape_base. +* `#17284 `__: ENH: Pass optimizations arguments to asv build +* `#17285 `__: DEP: Change the financial name access warning to DeprecationWarning +* `#17288 `__: REL: Update master after 1.19.2 release. +* `#17289 `__: MAINT: Simplify ufunc pickling +* `#17290 `__: MAINT: Cleanup some pystring macros +* `#17292 `__: MAINT: Replace remaining PyString macros. +* `#17293 `__: MAINT: Replace PyUString_Check by PyUnicode_Check. +* `#17295 `__: BUG,ENH: fix pickling user-scalars by allowing non-format buffer... +* `#17296 `__: MAINT: Replace some ``pyint_*`` macros defined in ``npy_3kcompat``. +* `#17297 `__: BLD: set upper versions for build dependencies +* `#17299 `__: MAINT: (dtype-transfer) make copyswapn and legacy cast wrapper... +* `#17300 `__: MAINT: Replace PyBaseString_Check by PyUnicode_Check +* `#17302 `__: MAINT: Replace a couple of missed npy_3kcompat macros +* `#17304 `__: BUILD: pin pygments to 2.6.1, 2.7.0 breaks custom NumPyC lexer +* `#17307 `__: MAINT: Bump hypothesis from 5.33.0 to 5.35.1 +* `#17308 `__: MAINT: Bump pytest from 6.0.1 to 6.0.2 +* `#17309 `__: MAINT: Move the ``fromnumeric`` annotations to their own stub file +* `#17312 `__: MAINT: Syntax-highlight .src files on github +* `#17313 `__: MAINT: Mark vendored/generated files in .gitattributes +* `#17315 `__: MAINT: Cleanup f2py/cfuncs.py +* `#17319 `__: BUG: Set deprecated fields to null in PyArray_InitArrFuncs +* `#17320 `__: BUG: allow registration of hard-coded structured dtypes +* `#17326 `__: ENH: Add annotations for five array construction functions +* `#17329 `__: DOC: Fix incorrect ``.. deprecated::`` syntax that led to this... +* `#17330 `__: DOC: improve ``issubdtype`` and scalar type docs +* `#17331 `__: DOC: Remove the tables of scalar types, and use ``..autoclass``... +* `#17332 `__: DOC, BLD: update lexer highlighting and make numpydocs a regular... +* `#17334 `__: MAINT: Chaining exceptions in npyio.py +* `#17337 `__: NEP: Regenerate table in NEP 29 (add numpy 1.18 and 1.19 to list) +* `#17338 `__: DOC: Fix syntax errors in docstrings for versionchanged, versionadded +* `#17340 `__: SIMD: Add partial/non-contig load and store intrinsics for 32/64-bit +* `#17344 `__: ENH, BLD: Support for the NVIDIA HPC SDK nvfortran compiler +* `#17346 `__: BLD,BUG: Fix a macOS build failure when ``NPY_BLAS_ORDER=""`` +* `#17350 `__: DEV: Add PR prefix labeler and numpy prefix mapping +* `#17352 `__: DOC: Guide to writing how-tos +* `#17353 `__: DOC: How-to guide for I/O +* `#17354 `__: DOC: clarify residuals return param +* `#17356 `__: ENH: Add Npy__PyLong_AsInt function. +* `#17357 `__: MAINT: Bump hypothesis from 5.35.1 to 5.35.3 +* `#17364 `__: MAINT: Finish replacing PyInt_Check +* `#17369 `__: DOC: distutils: Remove an obsolete paragraph. +* `#17370 `__: NEP: Edit nep-0042 for more clarity +* `#17372 `__: ENH: Add annotations for remaining ``ndarray`` / ``generic`` non-magic... +* `#17373 `__: BUG: Fixes module data docstrings. +* `#17375 `__: DOC: Fix default_rng docstring +* `#17377 `__: BUG: ensure _UFuncNoLoopError can be pickled +* `#17380 `__: Minor grammatical correction in quickstart doc. +* `#17382 `__: DOC: NumPy restyling for pydata theme +* `#17383 `__: MAINT: Fix docstring for np.matmul +* `#17386 `__: MAINT: Bump hypothesis from 5.35.3 to 5.36.1 +* `#17388 `__: MAINT: Remove old debug print statement. +* `#17391 `__: DOC: Replace "About NumPy" with "Document conventions" +* `#17392 `__: DOC: Update info on doc style rules +* `#17393 `__: BUG: Fix default void, datetime, and timedelta in array coercion +* `#17394 `__: ENH: Implement sliding window +* `#17396 `__: MAINT: Replace append_metastr_to_string function. +* `#17399 `__: BLD: Fixed ARGOUTVIEWM memory deallocation. Closes #17398. +* `#17400 `__: DOC: rm incorrect alias from recarray user article. +* `#17401 `__: MAINT: Rewrite can-cast logic in terms of NEP 42 +* `#17402 `__: DOC: Add arraysetops to an autosummary +* `#17404 `__: MAINT: Replace PyUString_ConcatAndDel in nditer_constr.c. +* `#17405 `__: MAINT: Replace PyUString_ConcatAndDel in mapping.c. +* `#17406 `__: ENH: Replace the module-level ``__getattr__`` with explicit type... +* `#17407 `__: DOC: in PR template, set expectations for PR review timeline +* `#17409 `__: MAINT: Cleanup remaining PyUString_ConcatAndDel use. +* `#17410 `__: API: Special case how numpy scalars are coerced to signed integer +* `#17411 `__: TST: Mark the typing tests as slow +* `#17412 `__: DOC: Fix a parameter type in the ``putmask`` docs +* `#17418 `__: DOC: adding operational form documentation for array ops +* `#17419 `__: DEP: Deprecate coercion to subarray dtypes +* `#17421 `__: BUG: Fix memory leak in array-coercion error paths +* `#17422 `__: MAINT: chains nested try-except in numpy/ma/core.py +* `#17423 `__: DOC: Remove bogus reference to _a_ +* `#17424 `__: DOC: Fix formatting issues in description of .c.src files +* `#17427 `__: NEP: nep-0029 typo correction +* `#17429 `__: MAINT: Move aliases for common scalar unions to ``numpy.typing`` +* `#17430 `__: BUG: Fix memoryleaks related to NEP 37 function overrides +* `#17431 `__: DOC: Fix the links for ``Ellipsis`` +* `#17432 `__: DOC: add references to einops and opt_einsum +* `#17433 `__: MAINT : Disable 32 bit PyPy CI testing on Windows. +* `#17435 `__: DOC: Security warning for issues template +* `#17436 `__: DOC: Fix "Feature request" spelling in issue templates +* `#17438 `__: MAINT: Chaining exception in numpy\numpy\ma\mrecords.py +* `#17440 `__: DOC: Cleaner template for PRs +* `#17442 `__: MAINT: fix exception chaining in format.py +* `#17443 `__: ENH: Warn on unsupported Python 3.10+ +* `#17444 `__: ENH: Add ``Typing :: Typed`` to the PyPi classifier +* `#17445 `__: DOC: Fix the references for macros +* `#17447 `__: NEP: update NEP 42 with discussion of type hinting applications +* `#17448 `__: DOC: Remove CoC pages from Sphinx +* `#17453 `__: MAINT: Chain exceptions in "_polybase.py" +* `#17455 `__: MAINT: Bump hypothesis from 5.36.1 to 5.37.0 +* `#17456 `__: ENH: add dtype option to numpy.lib.function_base.cov and corrcoef +* `#17457 `__: BUG: Fixes incorrect error message in numpy.ediff1d +* `#17459 `__: DOC: update code of conduct URL +* `#17464 `__: DOC: Add some entries for C types and macros +* `#17465 `__: ENH: Add annotations for bitwise operations +* `#17468 `__: DOC: add some missing scalar aliases +* `#17472 `__: TST: Fix doctest for full_like +* `#17473 `__: MAINT: py3k: remove os.fspath and os.PathLike backports +* `#17474 `__: MAINT: Move the ``np.core.numeric`` annotations to their own stub... +* `#17479 `__: ENH: type ``np.unicode_`` as ``np.str_`` +* `#17481 `__: DOC: Fix the entries for members of structures +* `#17483 `__: DOC: Fix the references for ``random.*`` +* `#17485 `__: BLD: circleCI- merge before build, add -n to sphinx +* `#17487 `__: MAINT: Remove duplicate placeholder annotations +* `#17493 `__: DOC: New round of NEP 42 edits +* `#17497 `__: DOC: Use consistent lowercase on docs landing page +* `#17498 `__: MAINT: fix incompatible type comparison in numpy.lib.utils.info +* `#17501 `__: BUG: Fix failures in master related to userdtype registeration +* `#17502 `__: BUG: remove ``sys`` from the type stubs +* `#17503 `__: DOC: Fix empty 'C style guide' page +* `#17504 `__: DOC: Rename 'Quickstart tutorial' +* `#17508 `__: ENH: Added the Final feature for all constants +* `#17510 `__: DOC: Fewer blank lines in PR template +* `#17520 `__: DOC: Display real license on license page +* `#17521 `__: DOC: Add docstrings for some scalar types +* `#17523 `__: DOC: Update top links in landing page +* `#17525 `__: CI: Make merge ref grabbing conditional on the PR being active +* `#17527 `__: DOC: Fix Bool types in C functions +* `#17528 `__: Doc: Fix some links and typos +* `#17529 `__: MAINT: Cleanup compatibility code for pathlib +* `#17534 `__: DOC: Fix a typo +* `#17535 `__: ENH: add function to get broadcast shape from a given set of... +* `#17536 `__: BUG: Fixed crash on self-referential dtypes +* `#17537 `__: MAINT: Bump hypothesis from 5.37.0 to 5.37.1 +* `#17538 `__: MAINT: Bump pydata-sphinx-theme from 0.4.0 to 0.4.1 +* `#17539 `__: MAINT: Bump mypy from 0.782 to 0.790 +* `#17540 `__: ENH: Make ``np.number`` generic with respect to its precision +* `#17541 `__: CI: fix conditional for PR merge command +* `#17546 `__: MAINT: explicit disabling ``CCompilerOpt`` in F2PY +* `#17548 `__: BUG: Cygwin Workaround for #14787 on affected platforms +* `#17549 `__: DOC: Fix the entries of C functions +* `#17555 `__: DOC: Fix wrong blockquotes +* `#17558 `__: DOC: MAINT: Add NEP 43 links to NEP 42 +* `#17559 `__: DOC: Remove directives for some constants +* `#17564 `__: MAINT: Update the annotations in ``np.core.numeric`` +* `#17570 `__: DOC: Add the entry for ``NPY_FEATURE_VERSION`` +* `#17571 `__: DOC: Fix typos +* `#17572 `__: ENH: Add annotations for three new constants +* `#17576 `__: DOC: Fix Boolean array indexing typo +* `#17577 `__: BUG: Respect dtype of all-zero argument to poly1d +* `#17578 `__: NEP36: include additional feedback +* `#17580 `__: MAINT: Cleanup swig for Python 3. +* `#17581 `__: MAINT: Move the ``np.core.numerictypes`` annotations to their own... +* `#17583 `__: MAINT: Bump hypothesis from 5.37.1 to 5.37.3 +* `#17584 `__: ENH: Add annotations for ``np.core._type_aliases`` +* `#17594 `__: DOC: Typo in lexsort docstring +* `#17596 `__: DEP,BUG: Coercion/cast of array to a subarray dtype will be fixed +* `#17597 `__: TST: Clean up the errors of the typing tests +* `#17598 `__: BUG: Fixed file handle leak in array_tofile. +* `#17601 `__: TST: Fix a broken ``np.core.numeric`` test +* `#17603 `__: MAINT: Mark dead code as intentional for clang. +* `#17607 `__: DOC: removed old references to submodule licenses +* `#17608 `__: DOC: Fix typos (general documentation) +* `#17610 `__: Fully qualify license trove classifier +* `#17611 `__: BUG: mac dylib treated as part of extra objects by f2py +* `#17613 `__: ENH: Add annotations for 9 ``ndarray``/``generic`` magic methods +* `#17614 `__: DOC: Fix the document for arrays interface +* `#17618 `__: MAINT: Conversion of some strings to f-strings +* `#17619 `__: DOC: Fix some references +* `#17621 `__: TST: Valid docstring for config_py function show() +* `#17622 `__: MAINT: Conversion of some strings to fstrings, part II +* `#17623 `__: MAINT: Conversion of some strings to fstrings, part III +* `#17624 `__: DOC: Tidy up references to ``str_`` / ``bytes_`` +* `#17625 `__: MAINT: Conversion of some strings to fstrings, part iv +* `#17627 `__: DOC: Fix the references for ``__array_*__`` +* `#17628 `__: DOC: Add entries for macros +* `#17629 `__: DOC: Add ``identity_value`` to ``PyUFuncObject`` +* `#17630 `__: DOC: Replace ``PyCObject`` with ``PyCapsule`` +* `#17633 `__: DOC: Don't use Python highlighting for non-python code +* `#17638 `__: DOC: Fix some references +* `#17639 `__: MAINT: Bump hypothesis from 5.37.3 to 5.38.0 +* `#17641 `__: MAINT, BLD: update to OpenBLAS v0.3.12 +* `#17642 `__: DOC: Fix reference to atleast_1d +* `#17643 `__: ENH: Add annotations for ``np.core._ufunc_config`` +* `#17644 `__: ENH: Add annotations for ``np.core.shape_base`` +* `#17645 `__: BUG: fix np.timedelta64('nat').__format__ throwing an exception +* `#17654 `__: BUG: f2py incorrectly translates dimension declarations. +* `#17655 `__: BLD: Fix installing Numpy on z/OS +* `#17657 `__: NEP: Ensure inner loop signature is complete everywhere +* `#17658 `__: TST: simplify source path names in compilation test +* `#17662 `__: TST: f2py: Add a doctest for ``getlincoef`` +* `#17666 `__: REL: Update master after 1.19.3 release. +* `#17668 `__: TST: Make test suite work in FIPS (140-2) Mode +* `#17670 `__: DOC: f2py: Add a docstring for getarrlen +* `#17672 `__: DOC: Update README badge for travis-ci.com +* `#17673 `__: MAINT: Refine a number of ``np.generic`` annotations +* `#17675 `__: MAINT: Update release documentation and software +* `#17681 `__: SIMD: Add sum intrinsics for float/double. +* `#17682 `__: BUG: (nditer_impl.h) Use ``intp`` instead of ``char *`` for offset... +* `#17689 `__: BUG: Fix small bug in ``make_lite.py``. +* `#17691 `__: DOC: Modify Templates +* `#17692 `__: MAINT: Bump hypothesis from 5.38.0 to 5.41.0 +* `#17693 `__: MAINT: Bump pytz from 2020.1 to 2020.4 +* `#17695 `__: TST: use a more standard workflow for PyPy +* `#17696 `__: REL: Update master after 1.19.4 release. +* `#17699 `__: MAINT: Rename ``DtypeLike`` to ``DTypeLike`` +* `#17700 `__: Fix small typos. +* `#17701 `__: BUG: Fixed an issue where ``.pyi`` files were ignored by numpy... +* `#17703 `__: Fix Doc Typos & Added Example +* `#17706 `__: BUG: Raise promotion error if a DType was provided in array coercion +* `#17708 `__: Improve the einsum bench by adding new bench cases and variable... +* `#17711 `__: ENH: adds type hints to numpy.version +* `#17715 `__: REV: Revert gh-17654 - f2py incorrectly translates dimension... +* `#17717 `__: MAINT: Add more files to ``.gitgnore`` +* `#17720 `__: API: Do not import sliding_window_view to main namespace +* `#17723 `__: MAINT: Do not override ``sliding_window_view`` module to ``numpy`` +* `#17725 `__: NEP: Add NEP-35 instructions on reading like= downstream +* `#17729 `__: BLD: Use importlib to find numpy root directory in distutils +* `#17733 `__: MAINT: ma: Remove unused ``**options`` from MaskedArray ``__new__``... +* `#17735 `__: TST: Remove Python 3.6 CI testing. +* `#17738 `__: BLD, TST: move linux jobs to github actions +* `#17740 `__: MAINT: Bump hypothesis from 5.41.0 to 5.41.2 +* `#17743 `__: BLD, BUG: Fix cblas detection on windows +* `#17745 `__: TST: add pypy3.7 +* `#17748 `__: BLD: compare platform.architecture() correctly +* `#17749 `__: DOC: Add "performance" category to the release notes +* `#17751 `__: BUG: Fix segfault due to out of bound pointer in floatstatus... +* `#17753 `__: BUG: Fix buffer export dtype references +* `#17755 `__: BUG: Fix memory leaks found using valgrind +* `#17758 `__: BLD: Lazy load f2py test utilities +* `#17759 `__: BLD: use BUFFERSIZE=20 in OpenBLAS +* `#17763 `__: SIMD, BUG: fix reuses the previous values during the fallback... +* `#17768 `__: MAINT: update link to website in FUNDING.yml +* `#17773 `__: MAINT: Add BLD and STY to labeler prefixes. +* `#17776 `__: MAINT: Simplify Hypothesis configuration +* `#17787 `__: NEP: Make like= argument added in NEP-35 strict +* `#17788 `__: DOC: Fix up links, code blocks of release note fragments +* `#17796 `__: MAINT: Minor touchups in npyio +* `#17802 `__: MAINT: Update mailmap. +* `#17805 `__: MAINT: Set the ufunc and ndarray ops return type to ``Any`` +* `#17812 `__: Update linalg.py +* `#17815 `__: DOC: Fix empty_like docstring +* `#17823 `__: DOC: Add missing release fragments to ``upcoming_changes``. +* `#17828 `__: BUG: Fix incorrectly passed size in masked processing +* `#17829 `__: MAINT: Bump hypothesis from 5.41.2 to 5.41.3 +* `#17830 `__: TST: Add back durations flag for DEBUG builds. +* `#17832 `__: BUG: Fix subarray dtype used with too large count in fromfile +* `#17833 `__: BUG: Fix pickling of scalars with NPY_LISTPICKLE +* `#17838 `__: DOC: Update the ``numpy.typing`` documentation +* `#17841 `__: DOC: Fixing boilerplate code example +* `#17844 `__: MAINT: Add ``__all__`` to ``numpy.typing`` +* `#17848 `__: DOC: Add release note for gh-16161. +* `#17855 `__: BUG: Fix incorrect C function prototypes/declarations. +* `#17857 `__: MAINT: Prepare for the NumPy 1.20.x branch. +* `#17869 `__: BUG, TST: use python-version not PYTHON_VERSION +* `#17879 `__: BUG: Fix buffer readflag errors and small leaks +* `#17893 `__: DOC: Prepare for 1.20.0 release +* `#17898 `__: MAINT: Remove remaining uses of Python 3.6. +* `#17899 `__: TST: use latest pypy37 not pypy36 +* `#17901 `__: MAINT: clean up a spurious warning in numpy/typing/setup.py +* `#17904 `__: ENH: Speed up default ``where`` in the reduce-like method +* `#17915 `__: TST: remove stray '+' from f-string upgrade +* `#17916 `__: ENH: add support for fujitsu compiler to numpy. +* `#17922 `__: BUG: 'bool' object has no attribute 'ndim' +* `#17931 `__: DOC: Update release notes to mention ``type(dtype) is not np.dtype`` +* `#17990 `__: BUG: Replace f-string in setup.py +* `#18015 `__: BUG: Ignore fewer errors during array-coercion +* `#18016 `__: BUG: Fix a MacOS build failure +* `#18017 `__: TST: Fix crosstalk issues with polynomial str tests. +* `#18018 `__: TST: Ensure tests are not sensitive to execution order +* `#18019 `__: BLD: update to OpenBLAS 0.3.13 +* `#18024 `__: DEP: Futurewarn on requiring __len__ on array-likes +* `#18035 `__: BUG: make a variable volatile to work around clang compiler bug +* `#18049 `__: TST: add back sdist test run +* `#18063 `__: BUG: Fix concatenation when the output is "S" or "U" +* `#18064 `__: BLD, BUG: Fix detecting aarch64 on macOS +* `#18068 `__: REL: Prepare for 1.20.0rc2 release. +* `#18108 `__: BUG, BLD: Generate the main dispatcher config header into the... +* `#18120 `__: BUG, SIMD: Fix _simd module build for 64bit ARM/NEON clang +* `#18127 `__: REL: Update 1.20.x after 1.19.5 release. +* `#18130 `__: BUG: Fix promotion of half and string +* `#18146 `__: BUG, MAINT: improve avx512 mask logical operations +* `#18154 `__: BUG: Promotion between strings and objects was assymetric +* `#18192 `__: MAINT: Use explicit reexports for numpy.typing objects +* `#18201 `__: BUG: Keep ignoring most errors during array-protocol lookup +* `#18219 `__: MAINT: random shuffle: warn on unrecognized objects, fix empty... +* `#18231 `__: BLD: update OpenBLAS to af2b0d02 +* `#18237 `__: DOC: Clarify the type alias deprecation message +* `#18257 `__: BUG: Ensure too many advanced indices raises an exception +* `#18258 `__: MAINT: add an 'apt update' +* `#18259 `__: DOC: Prepare for the NumPy 1.20.0 release. diff --git a/doc/changelog/1.20.1-changelog.rst b/doc/changelog/1.20.1-changelog.rst new file mode 100644 index 000000000000..215cdca3c5e0 --- /dev/null +++ b/doc/changelog/1.20.1-changelog.rst @@ -0,0 +1,36 @@ + +Contributors +============ + +A total of 8 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Bas van Beek +* Charles Harris +* Nicholas McKibben + +* Pearu Peterson +* Ralf Gommers +* Sebastian Berg +* Tyler Reddy +* @Aerysv + + +Pull requests merged +==================== + +A total of 15 pull requests were merged for this release. + +* `#18306 `__: MAINT: Add missing placeholder annotations +* `#18310 `__: BUG: Fix typo in ``numpy.__init__.py`` +* `#18326 `__: BUG: don't mutate list of fake libraries while iterating over... +* `#18327 `__: MAINT: gracefully shuffle memoryviews +* `#18328 `__: BUG: Use C linkage for random distributions +* `#18336 `__: CI: fix when GitHub Actions builds trigger, and allow ci skips +* `#18337 `__: BUG: Allow unmodified use of isclose, allclose, etc. with timedelta +* `#18345 `__: BUG: Allow pickling all relevant DType types/classes +* `#18351 `__: BUG: Fix missing signed_char dependency. Closes #18335. +* `#18352 `__: DOC: Change license date 2020 -> 2021 +* `#18353 `__: CI: CircleCI seems to occasionally time out, increase the limit +* `#18354 `__: BUG: Fix f2py bugs when wrapping F90 subroutines. +* `#18356 `__: MAINT: crackfortran regex simplify +* `#18357 `__: BUG: threads.h existence test requires GLIBC > 2.12. +* `#18359 `__: REL: Prepare for the NumPy 1.20.1 release. diff --git a/doc/changelog/1.20.2-changelog.rst b/doc/changelog/1.20.2-changelog.rst new file mode 100644 index 000000000000..831cf03324de --- /dev/null +++ b/doc/changelog/1.20.2-changelog.rst @@ -0,0 +1,40 @@ + +Contributors +============ + +A total of 7 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Allan Haldane +* Bas van Beek +* Charles Harris +* Christoph Gohlke +* Mateusz Sokół + +* Michael Lamparski +* Sebastian Berg + +Pull requests merged +==================== + +A total of 20 pull requests were merged for this release. + +* `#18382 `__: MAINT: Update f2py from master. +* `#18459 `__: BUG: ``diagflat`` could overflow on windows or 32-bit platforms +* `#18460 `__: BUG: Fix refcount leak in f2py ``complex_double_from_pyobj``. +* `#18461 `__: BUG: Fix tiny memory leaks when ``like=`` overrides are used +* `#18462 `__: BUG: Remove temporary change of descr/flags in VOID functions +* `#18469 `__: BUG: Segfault in nditer buffer dealloc for Object arrays +* `#18485 `__: BUG: Remove suspicious type casting +* `#18486 `__: BUG: remove nonsensical comparison of pointer < 0 +* `#18487 `__: BUG: verify pointer against NULL before using it +* `#18488 `__: BUG: check if PyArray_malloc succeeded +* `#18546 `__: BUG: incorrect error fallthrough in nditer +* `#18559 `__: CI: Backport CI fixes from main. +* `#18599 `__: MAINT: Add annotations for ``dtype.__getitem__``, ``__mul__`` and... +* `#18611 `__: BUG: NameError in numpy.distutils.fcompiler.compaq +* `#18612 `__: BUG: Fixed ``where`` keyword for ``np.mean`` & ``np.var`` methods +* `#18617 `__: CI: Update apt package list before Python install +* `#18636 `__: MAINT: Ensure that re-exported sub-modules are properly annotated +* `#18638 `__: BUG: Fix ma coercion list-of-ma-arrays if they do not cast to... +* `#18661 `__: BUG: Fix small valgrind-found issues +* `#18671 `__: BUG: Fix small issues found with pytest-leaks diff --git a/doc/changelog/1.20.3-changelog.rst b/doc/changelog/1.20.3-changelog.rst new file mode 100644 index 000000000000..df7f1056521a --- /dev/null +++ b/doc/changelog/1.20.3-changelog.rst @@ -0,0 +1,35 @@ + +Contributors +============ + +A total of 7 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Anne Archibald +* Bas van Beek +* Charles Harris +* Dong Keun Oh + +* Kamil Choudhury + +* Sayed Adel +* Sebastian Berg + +Pull requests merged +==================== + +A total of 15 pull requests were merged for this release. + +* `#18763 `__: BUG: Correct ``datetime64`` missing type overload for ``datetime.date``... +* `#18764 `__: MAINT: Remove ``__all__`` in favor of explicit re-exports +* `#18768 `__: BLD: Strip extra newline when dumping gfortran version on MacOS +* `#18769 `__: BUG: fix segfault in object/longdouble operations +* `#18794 `__: MAINT: Use towncrier build explicitly +* `#18887 `__: MAINT: Relax certain integer-type constraints +* `#18915 `__: MAINT: Remove unsafe unions and ABCs from return-annotations +* `#18921 `__: MAINT: Allow more recursion depth for scalar tests. +* `#18922 `__: BUG: Initialize the full nditer buffer in case of error +* `#18923 `__: BLD: remove unnecessary flag ``-faltivec`` on macOS +* `#18924 `__: MAINT, CI: treats _SIMD module build warnings as errors through... +* `#18925 `__: BUG: for MINGW, threads.h existence test requires GLIBC > 2.12 +* `#18941 `__: BUG: Make changelog recognize gh- as a PR number prefix. +* `#18948 `__: REL, DOC: Prepare for the NumPy 1.20.3 release. +* `#18953 `__: BUG: Fix failing mypy test in 1.20.x. diff --git a/doc/changelog/1.21.0-changelog.rst b/doc/changelog/1.21.0-changelog.rst new file mode 100644 index 000000000000..947da4da740e --- /dev/null +++ b/doc/changelog/1.21.0-changelog.rst @@ -0,0 +1,769 @@ + +Contributors +============ + +A total of 175 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* @8bitmp3 + +* @DWesl + +* @Endolith +* @Illviljan + +* @Lbogula + +* @Lisa + +* @Patrick + +* @Scian + +* @h-vetinari + +* @h6197627 + +* @jbCodeHub + +* @legoffant + +* @sfolje0 + +* @tautaus + +* @yetanothercheer + +* Abhay Raghuvanshi + +* Adrian Price-Whelan + +* Aerik Pawson + +* Agbonze Osazuwa + +* Aitik Gupta + +* Al-Baraa El-Hag +* Alex Henrie +* Alexander Hunt + +* Alizé Papp + +* Allan Haldane +* Amarnath1904 + +* Amrit Krishnan + +* Andras Deak +* AngelGris + +* Anne Archibald +* Anthony Vo + +* Antony Lee +* Atharva-Vidwans + +* Ayush Verma + +* Bas van Beek +* Bharat Raghunathan +* Bhargav V + +* Brian Soto +* Carl Michal + +* Charles Harris +* Charles Stern + +* Chiara Marmo + +* Chris Barnes + +* Chris Vavaliaris +* Christina Hedges + +* Christoph Gohlke +* Christopher Dahlin + +* Christos Efstathiou + +* Chunlin Fang +* Constanza Fierro + +* Daniel Evans + +* Daniel Montes + +* Dario Mory + +* David Carlier + +* David Stansby +* Deepyaman Datta + +* Derek Homeier +* Dong Keun Oh + +* Dylan Cutler + +* Eric Larson +* Eric Wieser +* Eva Jau + +* Evgeni Burovski +* FX Coudert + +* Faris A Chugthai + +* Filip Ter + +* Filip Trojan + +* François Le Lay + +* Ganesh Kathiresan +* Giannis Zapantis + +* Giulio Procopio + +* Greg Lucas + +* Hollow Man + +* Holly Corbett + +* I-Shen Leong + +* Inessa Pawson +* Isabela Presedo-Floyd +* Ismael Jimenez + +* Isuru Fernando +* Jakob Jakobson +* James Gerity + +* Jamie Macey + +* Jasmin Classen + +* Jody Klymak + +* Joseph Fox-Rabinovitz +* Jérome Eertmans + +* Jérôme Kieffer + +* Kamil Choudhury + +* Kasia Leszek + +* Keller Meier + +* Kenichi Maehashi +* Kevin Sheppard +* Kulin Seth + +* Kumud Lakara + +* Laura Kopf + +* Laura Martens + +* Leo Singer + +* Leonardus Chen + +* Lima Tango + +* Lumir Balhar + +* Maia Kaplan + +* Mainak Debnath + +* Marco Aurélio da Costa + +* Marta Lemanczyk + +* Marten van Kerkwijk +* Mary Conley + +* Marysia Winkels + +* Mateusz Sokół + +* Matt Haberland +* Matt Hall + +* Matt Ord + +* Matthew Badin + +* Matthias Bussonnier +* Matthias Geier +* Matti Picus +* Matías Ríos + +* Maxim Belkin + +* Melissa Weber Mendonça +* Meltem Eren Copur + +* Michael Dubravski + +* Michael Lamparski +* Michal W. Tarnowski + +* Michał Górny + +* Mike Boyle + +* Mike Toews +* Misal Raj + +* Mitchell Faas + +* Mukulikaa Parhari + +* Neil Girdhar + +* Nicholas McKibben + +* Nico Schlömer +* Nicolas Hug + +* Nilo Kruchelski + +* Nirjas Jakilim + +* Ohad Ravid + +* Olivier Grisel +* Pamphile ROY + +* Panos Mavrogiorgos + +* Patrick T. Komiske III + +* Pearu Peterson +* Peter Hawkins + +* Raghuveer Devulapalli +* Ralf Gommers +* Raúl Montón Pinillos + +* Rin Arakaki + +* Robert Kern +* Rohit Sanjay +* Roman Yurchak +* Ronan Lamy +* Ross Barnowski +* Ryan C Cooper +* Ryan Polley + +* Ryan Soklaski +* Sabrina Simao + +* Sayed Adel +* Sebastian Berg +* Shen Zhou + +* Stefan van der Walt +* Sylwester Arabas + +* Takanori Hirano +* Tania Allard + +* Thomas J. Fan + +* Thomas Orgis + +* Tim Hoffmann +* Tomoki, Karatsu + +* Tong Zou + +* Touqir Sajed + +* Tyler Reddy +* Wansoo Kim +* Warren Weckesser +* Weh Andreas + +* Yang Hau +* Yashasvi Misra + +* Zolboo Erdenebaatar + +* Zolisa Bleki + +Pull requests merged +==================== + +A total of 581 pull requests were merged for this release. + +* `#13578 `__: DEP: Deprecate `data_type.dtype` if attribute is not already... +* `#15269 `__: ENH: Implement faster keyword argument parsing capable of ``METH_FASTCALL`` +* `#15271 `__: ENH: Optimize and cleanup ufunc calls and ufunc CheckOverrides +* `#15392 `__: BUG: Remove temporary change of descr/flags in VOID functions +* `#16164 `__: DOC: Add more information about poly1d -> polynomial to reference... +* `#16241 `__: ENH: Warn when reloading numpy or using numpy in sub-interpreter +* `#16370 `__: DOC: Fix for building with sphinx 3 +* `#16588 `__: DOC: unify the docs for np.transpose and ndarray.transpose +* `#16818 `__: DOC: added examples section for rfft2 and irfft2 docstring +* `#16855 `__: DOC: Fix Typo (Wrong argument name) +* `#16987 `__: ENH: Phase unwrapping generalized to arbitrary interval size +* `#17102 `__: SIMD: Optimize the performance of np.packbits in AVX2/AVX512F/VSX. +* `#17122 `__: MAINT: Use numpy version for f2py version. +* `#17492 `__: DEP: Shift correlate mode parsing to C and deprecate inexact... +* `#17586 `__: DEP: Formally deprecate `np.typeDict` +* `#17587 `__: SIMD: Replace raw SIMD of sin/cos with NPYV(universal intrinsics) +* `#17636 `__: MAINT: Bump pydata-sphinx-theme and set logo link to index +* `#17637 `__: DOC: Add module template +* `#17719 `__: ENH: Make `ndarray` generic w.r.t. its shape and dtype +* `#17727 `__: ENH: Added libdivide for floor divide +* `#17736 `__: BUG, Benchmark: fix passing optimization build options to asv +* `#17737 `__: MAINT, Benchmark: print the supported CPU features during the... +* `#17778 `__: ENH: Add annotations for comparison operations +* `#17782 `__: SIMD: Optimize the performance of einsum's submodule multiply... +* `#17789 `__: ENH, SIMD: Add new NPYV intrinsics pack(0) +* `#17790 `__: ENH, SIMD: Add new NPYV intrinsics pack(1) +* `#17791 `__: BLD: Enable Werror=undef in travis +* `#17792 `__: ENH: add support for fujitsu compiler to numpy. +* `#17795 `__: ENH: Add two new `_Like` unions +* `#17817 `__: BUG: Ignore fewer errors during array-coercion +* `#17836 `__: MAINT: Add git rules to ignore all SIMD generated files +* `#17843 `__: ENH: Add a mypy plugin for inferring platform-specific `np.number`... +* `#17847 `__: TST: use latest pypy37 not pypy36 +* `#17852 `__: DOC: Doc for deprecate_with_doc +* `#17853 `__: DOC: Clarify docs of np.resize(). +* `#17861 `__: MAINT: Update master after 1.20.x branch. +* `#17862 `__: Make it clearer that np.interp input must be monotonically increasing +* `#17863 `__: MAINT: Implement new casting loops based on NEP 42 and 43 +* `#17866 `__: DOC: fix typo in glossary.rst +* `#17868 `__: BUG, TST: use python-version not PYTHON_VERSION +* `#17872 `__: DOC: update the release howto for oldest-supported-numpy +* `#17874 `__: MAINT: clean up a spurious warning in numpy/typing/setup.py +* `#17875 `__: DOC: Prepare for 1.20.0 release +* `#17876 `__: DOC: fixed typo in np-indexing.png explaining [-2:] slice in... +* `#17877 `__: BUG: Fix buffer readflag errors and small leaks +* `#17878 `__: BUG: np.arange: Allow `stop` not `start` as sole kwargs. +* `#17881 `__: MAINT: Bump hypothesis from 5.41.3 to 5.41.4 +* `#17883 `__: MAINT: Remove duplicate dictionary entry +* `#17884 `__: BUG: numpy.putmask not respecting writeable flag +* `#17886 `__: ENH: Timestamp development versions. +* `#17887 `__: DOC: Update arraycreation +* `#17888 `__: DOC: Correct sentence/statement composition +* `#17889 `__: DOC: Rename basics to fundamentals + added description +* `#17895 `__: MAINT: Remove remaining uses of Python 3.6. +* `#17896 `__: ENH: Speed up default `where` in the reduce-like method +* `#17897 `__: BUG: merging PR to use -Werror=undef broke another PR +* `#17900 `__: DEP: Finalize unravel_index `dims` alias for `shape` keyword +* `#17906 `__: BUG: Fix a MacOS build failure +* `#17907 `__: BUG: 'bool' object has no attribute 'ndim' +* `#17912 `__: BUG: remove stray '+' from f-string upgrade in numba/extending.py +* `#17914 `__: DOC: Update release notes to mention `type(dtype) is not np.dtype` +* `#17920 `__: NEP: Update NEP 42 and 43 according to the current implementation +* `#17921 `__: BUG: Enforce high >= low on uniform number generators +* `#17929 `__: MAINT: Replace `contextlib_nullcontext` with `contextlib.nullcontext` +* `#17934 `__: DOC: Add information about leak checking and valgrind +* `#17936 `__: TST: Fixed an issue where the typing tests would fail for comparison... +* `#17942 `__: DOC: Clarify savez documentation of naming arrays in output file +* `#17943 `__: [DOC]: Wrong length for underline in docstring. +* `#17945 `__: MAINT: Bump hypothesis from 5.41.4 to 5.41.5 +* `#17950 `__: BUG: Removed empty String from Nag Compiler's Flags +* `#17953 `__: NEP: Accept NEP 42 -- New and extensible DTypes +* `#17955 `__: DOC: Replace {var} in docstrings type annotation with `scalar... +* `#17956 `__: ENH: Use versioneer to manage numpy versions. +* `#17957 `__: TST: Fix crosstalk issues with polynomial str tests. +* `#17958 `__: MAINT: Optimize the performance of count_nonzero by using universal... +* `#17960 `__: TST, BUILD: Add a native x86 baseline build running on ubuntu-20.04 +* `#17962 `__: TST: Ensure tests are not sensitive to execution order +* `#17966 `__: BUG: Add missing decref to arange +* `#17968 `__: ENH: Use more typevars in `np.dtype` +* `#17971 `__: BUG, SIMD: Fix direactive check for AVX512BW of intrinsics npyv_tobits_* +* `#17973 `__: DEP: Futurewarn on requiring __len__ on array-likes +* `#17974 `__: BLD: Fixes for versioneer and setup.py sdist. +* `#17976 `__: DOC: Add/remove spaces in snippets and re-format here and there +* `#17978 `__: MAINT: Update test_requirements and release_requirements. +* `#17981 `__: ENH: Add proper dtype-support to `np.flatiter` +* `#17985 `__: ENH, SIMD: Ditching the old CPU dispatcher(Arithmetic) +* `#17992 `__: DOC: Replace verbatim with reference to local parameter +* `#17993 `__: [DOC] np.kron use double backticks for non-references +* `#17994 `__: SIMD: Optimize the performance of einsum's submodule dot . +* `#17995 `__: MAINT: Bump pytest from 6.0.2 to 6.2.0 +* `#17996 `__: MAINT: Update wheel requirement from <=0.35.1 to <0.36.3 +* `#17997 `__: MAINT: Bump hypothesis from 5.41.5 to 5.43.3 +* `#17998 `__: TST: ignore pytest warning +* `#17999 `__: Replace Numpy with NumPy +* `#18001 `__: BLD, BUG: Fix detecting aarch64 on macOS +* `#18002 `__: DOC: Fix and extend the docstring for np.inner +* `#18007 `__: DOC: Add a brief explanation of float printing +* `#18008 `__: DOC: fix for doctests +* `#18011 `__: BLD: update to OpenBLAS 0.3.13 +* `#18012 `__: SIMD: Optimize the performance of einsum's submodule sum. +* `#18014 `__: DOC: random: add some examples for SeedSequence +* `#18027 `__: DOC, MAINT: Minor fixes to refguide_check.py documentation. +* `#18030 `__: BUG: make a variable volatile to work around clang compiler bug +* `#18031 `__: DOC: Parameter name typo axes -> axis in numpy.fft._pocketfft. +* `#18032 `__: ENH: Add annotations for `np.core.arrayprint` +* `#18034 `__: DOC: Fix a couple of reference to verbatim and vice versa +* `#18042 `__: MAINT: Add dist_info to "other" setup.py commands. +* `#18045 `__: MAINT: Bump pytest from 6.2.0 to 6.2.1 +* `#18046 `__: TST: add back sdist test run +* `#18047 `__: BLD,DOC: pin sphinx to 3.3.1 +* `#18048 `__: DOC: Update TESTS.rst.txt +* `#18050 `__: MAINT: Add aliases for commonly used `ArrayLike` objects +* `#18051 `__: DEP: deprecate np.testing.dec +* `#18052 `__: BUG: Fix concatenation when the output is "S" or "U" +* `#18054 `__: DOC: Update stack docstrings +* `#18057 `__: BLD: ensure we give the right error message for old Python versions +* `#18062 `__: DOC: add missing details to linalg.lstsq docstring +* `#18065 `__: MAINT: CPUs that support unaligned access. +* `#18066 `__: TST: Allow mypy output types to be specified via aliases +* `#18067 `__: MAINT: Remove obsolete workaround to set ndarray.__hash__ = None +* `#18070 `__: BUG: Fix unique handling of nan entries. +* `#18072 `__: MAINT: crackfortran regex simplify +* `#18074 `__: MAINT: exprtype regex simplify +* `#18075 `__: ENH, SIMD: Dispatch for unsigned floor division +* `#18077 `__: NEP: mark NEP 28 on website redesign as final +* `#18078 `__: Fix build warnings in NEPs +* `#18079 `__: MAINT: Bump sphinx from 3.3.1 to 3.4.1 +* `#18080 `__: MAINT: Bump pytz from 2020.4 to 2020.5 +* `#18081 `__: MAINT: Bump hypothesis from 5.43.3 to 5.43.4 +* `#18082 `__: DOC: roadmap update +* `#18083 `__: MAINT: regex char class improve +* `#18084 `__: NEP: NumPy sponsorship guidelines (NEP 46) +* `#18085 `__: DOC: replace 'this platform' with the actual platform in the... +* `#18086 `__: BUG, SIMD: Fix _simd module build for 64bit Arm/neon clang +* `#18088 `__: DOC: Update reference to verbatim in a few location. +* `#18090 `__: MAINT: multiline regex class simplify +* `#18091 `__: DOC: Avoid using "set of" when talking about an ordered list. +* `#18097 `__: NEP: update backwards compatibility and deprecation policy NEP +* `#18100 `__: BUG, BLD: Generate the main dispatcher config header into the... +* `#18101 `__: ENH: move exp, log, frexp, ldexp to SIMD dispatching +* `#18103 `__: TST: Avoid changing odd tempfile names in tests' site.cfg +* `#18104 `__: TST: Turn some tests with loops into parametrized tests. +* `#18109 `__: MAINT: Fix exception cause in mingw32ccompiler.py +* `#18110 `__: API: make piecewise subclass safe using use zeros_like. +* `#18111 `__: MAINT: Bump hypothesis from 5.43.4 to 5.46.0 +* `#18115 `__: BUG: Fix promotion of half and string +* `#18116 `__: DEP: Deprecate promotion of numbers and bool to string +* `#18118 `__: BUG, MAINT: improve avx512 mask logical operations +* `#18126 `__: REL: Update master after 1.19.5 release. +* `#18128 `__: ENH: Add dtype support to the array comparison ops +* `#18136 `__: ENH: Adding keyboard interrupt support for array creation +* `#18144 `__: BLD: add found Cython version to check in cythonize.py +* `#18148 `__: MAINT: Bump sphinx from 3.4.1 to 3.4.3 +* `#18149 `__: MAINT: Bump hypothesis from 5.46.0 to 6.0.0 +* `#18150 `__: BUG: Ensure too many advanced indices raises an exception +* `#18152 `__: BUG: Promotion between strings and objects was assymetric +* `#18156 `__: MAINT: Remove redundant null check before free +* `#18157 `__: BUG: Initialize value of no_castable_output used in ufunc_loop_matches +* `#18161 `__: MAINT: Make keyword arrays static +* `#18164 `__: TST: add a pypy37 windows 64-bit build +* `#18166 `__: Use sinus based formula for ``chebpts1`` +* `#18169 `__: ENH: cpu features detection implementation on FreeBSD ARM +* `#18173 `__: TST: Clear the mypy cache before running any typing tests +* `#18174 `__: MAINT: Changed the `NBitBase` variancy in `number` from co- to... +* `#18176 `__: ENH: Improve performance of tril_indices and triu_indices +* `#18178 `__: SIMD: add fast integer division intrinsics for all supported... +* `#18180 `__: BUG: threads.h existence test requires GLIBC > 2.12. +* `#18181 `__: ENH: [f2py] Add external attribute support. +* `#18182 `__: MAINT: Bump hypothesis from 6.0.0 to 6.0.2 +* `#18183 `__: MAINT: Optimize numpy.count_nonzero for int types using SIMD... +* `#18184 `__: BUG: Fix f2py bugs when wrapping F90 subroutines. +* `#18185 `__: MAINT: Give the `_Like` and `_ArrayLike` type aliases a... +* `#18187 `__: STY: unify imports in __init__.py +* `#18191 `__: STY: Use explicit reexports for numpy.typing objects +* `#18193 `__: MAINT: Fix typo in docstring example +* `#18194 `__: MAINT: einsum: Optimize the sub function two-operands by using... +* `#18196 `__: BLD: update OpenBLAS to af2b0d02 +* `#18197 `__: BUG: Keep ignoring most errors during array-protocol lookup +* `#18200 `__: ENH: Add new intrinsics sum_u8/u16/u64. +* `#18204 `__: TST: Speed up the typing tests +* `#18205 `__: MAINT: Update pavement.py to work with versioneer. +* `#18208 `__: TST: raise memory limit for test +* `#18210 `__: DOC: typo in post-loop return +* `#18211 `__: MAINT: random shuffle: warn on unrecognized objects, fix empty... +* `#18213 `__: DOC: Formatting consistency. +* `#18214 `__: DOC: Double backticks for inline code example. +* `#18217 `__: MAINT: Ignore ComplexWarning in ``test_iter_copy_casts``. +* `#18221 `__: DOC: Misc single to double backticks fixes. +* `#18223 `__: DOC: Improve doc for numpy.random.Generator.choice +* `#18224 `__: MAINT: Bump pydata-sphinx-theme from 0.4.1 to 0.4.2 +* `#18225 `__: MAINT: Bump mypy from 0.790 to 0.800 +* `#18226 `__: MAINT: Bump hypothesis from 6.0.2 to 6.0.3 +* `#18227 `__: MAINT: Bump pytest-cov from 2.10.1 to 2.11.1 +* `#18228 `__: ENH: Add dtype-support to the ufunc-based `ndarray` magic methods... +* `#18229 `__: MAINT: Clean up all module-level dunders +* `#18230 `__: DOC: Clarify the type alias deprecation message +* `#18232 `__: DOC: lib/shape_base numpydoc formatting. +* `#18233 `__: NEP: accept NEP 23 (backwards compatibility policy) +* `#18234 `__: NEP: accept NEP 46 (sponsorship guidelines) +* `#18235 `__: DOC: Fix command in "Writing custom array containers" guide +* `#18236 `__: ENH: Add aliases for commonly used dtype-like objects +* `#18238 `__: DOC: __array__ accepts a dtype argument +* `#18245 `__: BLD: fix issue with `bdist_egg`, which made `make dist` in doc/... +* `#18247 `__: DOC: Misc numpydoc format fixes +* `#18248 `__: DOC: See also -> See Also (casing) +* `#18251 `__: DOC: more misc fixes of syntax +* `#18252 `__: DOC: cleanup of numpy/polynomial. +* `#18253 `__: DOC: improve description of `_NoValue` +* `#18255 `__: MAINT: add an 'apt update' +* `#18262 `__: REL: Update master after 1.20.0 release. +* `#18263 `__: ENH: Added sanity check to printoptions +* `#18264 `__: BUG: Use C linkage for random distributions +* `#18269 `__: DOC: Numpydoc format space before `:` in Parameters +* `#18272 `__: DOC: Numpydoc warning incorrect underline length. +* `#18274 `__: MAINT: Chain exceptions in linalg +* `#18275 `__: MAINT: Bump hypothesis from 6.0.3 to 6.1.1 +* `#18276 `__: MAINT: Bump pytest from 6.2.1 to 6.2.2 +* `#18277 `__: MAINT: Bump pydata-sphinx-theme from 0.4.2 to 0.4.3 +* `#18278 `__: MAINT: defer the import of shutil +* `#18282 `__: MAINT: gracefully shuffle memoryviews +* `#18284 `__: ENH: Add annotations for the remaining `np.generic` aliases +* `#18285 `__: TST: Pin `typing_extensions` to the latest version +* `#18289 `__: MAINT: Move transferdata into buffer-wise struct +* `#18293 `__: BUG: Fix typo in ``numpy.__init__.py`` +* `#18295 `__: BUG: don't mutate list of fake libraries while iterating over... +* `#18301 `__: MAINT: avoid chaining exceptions in conv_template.py +* `#18302 `__: MAINT: Add missing placeholder annotations +* `#18303 `__: MAINT: Fix typo in PyArray_RegisterDataType error +* `#18307 `__: DOC: Corrected numpy.power example. +* `#18313 `__: Numpy logo fix on README +* `#18315 `__: CI: rearrange Azure build jobs +* `#18317 `__: MAINT: Fixed chain exception for array_split func +* `#18320 `__: DOC: add links to polynomial function/class listing +* `#18322 `__: ENH: Add a mypy plugin for exposing platform-specific extended-precision... +* `#18323 `__: ENH: Add dtype-support to the ufunc-based `ndarray` magic methods... +* `#18324 `__: MAINT: Avoid moveaxis overhead in median. +* `#18329 `__: BUG: Allow unmodified use of isclose, allclose, etc. with timedelta +* `#18331 `__: MAINT: Update openblas_support for macosx-arm64 +* `#18332 `__: BUG: Allow pickling all relevant DType types/classes +* `#18333 `__: CI: fix when GitHub Actions builds trigger, and allow ci skips +* `#18334 `__: TST: use setup-python action for pypy, disable win64 pypy +* `#18338 `__: DOC: Fix whitespace before "last updated" on overview page +* `#18339 `__: DOC: Discussion on the @ operator and the matrix class +* `#18340 `__: DOC: remove pygments_style from conf.py +* `#18342 `__: DOC: Specified all possible return types for trapz function #18140 +* `#18344 `__: DOC: Added sentence to docstring of histogram_bin_edges to explain... +* `#18346 `__: DOC: Change license date 2020 -> 2021 +* `#18347 `__: MAINT: Delete unused "dst" clearing functions +* `#18348 `__: DEP: doc-deprecate BLAS_SRC/LAPACK_SRC +* `#18349 `__: CI: CircleCI seems to occasionally time out, increase the limit +* `#18350 `__: BUG: Fix missing signed_char dependency. +* `#18361 `__: ENH: Share memory of read-only intent(in) arrays. +* `#18362 `__: REL: Update master after 1.20.1 release. +* `#18364 `__: DOC: Update landing page to match table of contents +* `#18366 `__: MAINT: Disable TravisCI git clone depth. +* `#18367 `__: MAINT: Bump pytz from 2020.5 to 2021.1 +* `#18369 `__: BUG: np.in1d bug on the object array (issue 17923) +* `#18372 `__: DOC: improve standard_t example in numpy.random. +* `#18374 `__: TST: Add a test for nditer write masked with references +* `#18375 `__: BUG: fix regression in a hidden callback use case in f2py. +* `#18377 `__: ENH: Add annotations for `np.lib.ufunclike` +* `#18379 `__: DOC: Fix docstring of _median_nancheck. +* `#18384 `__: BUG: improve the interface of `tofile` method +* `#18389 `__: MAINT: Fix version of wheel to support Python 3.10 +* `#18390 `__: ENH: Add annotations for `np.core.einsumfunc` +* `#18392 `__: BUG: Remove check in shuffle for non-ndarrays +* `#18394 `__: MAINT: Added Chain exceptions where appropriate +* `#18395 `__: ENH: Initial typing of random +* `#18396 `__: MAINT: Threading and Unicode strings +* `#18397 `__: ENH: Add annotations for `np.lib.index_tricks` +* `#18398 `__: MAINT: Fix casting signatures to align with NEP 43 signature +* `#18400 `__: MAINT: Added Chain exceptions where appropriate +* `#18402 `__: BUG: Fix typo in char_codes +* `#18404 `__: BUG: Fix iterator shape in advanced index assignment broadcast... +* `#18405 `__: DOC: Mention `scipy.signal.correlate` and FFT method in `np.correlate`closes... +* `#18413 `__: MAINT: Bump sphinx from 3.4.3 to 3.5.0 +* `#18414 `__: MAINT: Bump hypothesis from 6.1.1 to 6.2.0 +* `#18415 `__: MAINT: Update END statements parsing for recent Fortran standards. +* `#18416 `__: BUG: Fix f2py parsing continued lines that follow comment lines. +* `#18417 `__: ENH: Add dtype-support to the ufunc-based `ndarray` magic methods... +* `#18418 `__: DOC: remove layout overrides for headers +* `#18420 `__: BUG: Fix tiny memory leaks when ``like=`` overrides are used +* `#18423 `__: ENH: Lint checks for PR diffs +* `#18428 `__: DOC: remove explanations.rst +* `#18429 `__: DOC: point intersphinx to matplotlib/stable... +* `#18432 `__: MAINT: Correct code producing warnings +* `#18433 `__: ENH: Add typing for RandomState +* `#18436 `__: BUG: Fix refcount leak in f2py `complex_double_from_pyobj` +* `#18437 `__: TST: Fix some uninitialized memory in the tests +* `#18438 `__: BUG: Correct shuffling of objects in 1-d array likes +* `#18439 `__: MAINT: random: Use 'from exc' when raising a ValueError in choice. +* `#18443 `__: BUG: fix stacklevel in warning within random.shuffle +* `#18448 `__: DOC: Remove unfinished Linear Algebra section from Quickstart... +* `#18450 `__: BUG: Segfault in nditer buffer dealloc for Object arrays +* `#18454 `__: NEP: add Spending NumPy Project Funds (NEP 48) +* `#18455 `__: BUG: ``diagflat`` could overflow on windows or 32-bit platforms +* `#18456 `__: NEP: array API standard adoption (NEP 47) +* `#18458 `__: DOC: update NEP status for accepted/finished NEPs +* `#18463 `__: MAINT: Bump mypy from 0.800 to 0.812 +* `#18464 `__: MAINT: Bump sphinx from 3.5.0 to 3.5.1 +* `#18465 `__: MAINT: Bump cython from 0.29.21 to 0.29.22 +* `#18466 `__: MAINT: Bump hypothesis from 6.2.0 to 6.3.0 +* `#18475 `__: ENH: Added type annotations to eye() function +* `#18476 `__: BUG: Remove suspicious type casting +* `#18477 `__: BUG: remove nonsensical comparison of pointer < 0 +* `#18478 `__: BUG: verify pointer against NULL before using it +* `#18479 `__: BUG: check if PyArray_malloc succeeded +* `#18481 `__: DOC: Generator and RandomState doc improvements +* `#18482 `__: ENH: Improve error message in multinomial +* `#18489 `__: DOC: Rename "Ones and zeros" section in array-creation documentation. +* `#18493 `__: BUG: Fix non-versioneer uses of numpy.distutils +* `#18497 `__: TST: Remove the `einsum` typing tests reliance on issuing a `ComplexWarning` +* `#18498 `__: BUG: Fixed Von Mises distribution for big values of kappa +* `#18499 `__: TST: Branch coverage improvement for `np.polynomial` +* `#18502 `__: DOC: Fix links to landing page +* `#18505 `__: DOC: add guide for downstream package authors +* `#18509 `__: DOC: trunc, floor, ceil, rint, fix should all link to each other +* `#18513 `__: BLD: add _2_24 to valid manylinux names +* `#18515 `__: MAINT: Improve error message when common type not found. +* `#18517 `__: MAINT: Bump hypothesis from 6.3.0 to 6.3.4 +* `#18518 `__: DOC Improve formatting in the depending_on_numpy documentation +* `#18522 `__: BUG: remove extraneous ARGOUTVIEWM dim. 4 typemaps +* `#18526 `__: MAINT: Specify color in RGB in the docs about the new NumPy logo +* `#18530 `__: BUG: incorrect error fallthrough in nditer +* `#18531 `__: CI: Use Ubuntu 18.04 to run "full" test. +* `#18537 `__: [BLD] use the new openblas lib +* `#18538 `__: Fix the numpy Apple M1 build +* `#18539 `__: BUG: NameError in numpy.distutils.fcompiler.compaq +* `#18544 `__: MAINT: Update master to main after branch rename +* `#18545 `__: ENH: Add annotations for `np.lib.arrayterator` +* `#18554 `__: CI: Pin docker image for Linux_Python_38_32bit_full_with_asserts... +* `#18560 `__: BUG: Fixed ``where`` keyword for ``np.mean`` & ``np.var`` methods +* `#18566 `__: CI: another master -> main fix +* `#18567 `__: CI: skip lint check on merges with main +* `#18569 `__: CI: Ensure that doc-build uses "main" as branch name +* `#18570 `__: CI: Use `git branch -m` instead of `--initial-branch=main` +* `#18571 `__: BUG: Fix overflow warning on apple silicon +* `#18572 `__: CI: Set git default branch to "main" in CircleCI. +* `#18574 `__: MAINT: Update the Call for Contributions section +* `#18575 `__: MAINT: Bump sphinx from 3.5.1 to 3.5.2 +* `#18576 `__: MAINT: Bump hypothesis from 6.3.4 to 6.6.0 +* `#18578 `__: MAINT: Bump pycodestyle from 2.5.0 to 2.6.0 +* `#18579 `__: MAINT: OrderedDict is no longer necessary from Python 3.7 +* `#18582 `__: BLD, TST: use pypy nightly to work around bug +* `#18583 `__: DOC: Clarify docs for fliplr() / flipud() +* `#18584 `__: DOC: Added documentation for linter (#18423) +* `#18593 `__: MAINT: Do not claim input to binops is `self` (array object) +* `#18594 `__: MAINT: Remove strange `op == NULL` check +* `#18596 `__: MAINT: Chain exceptions in index_tricks.py and mrecords.py +* `#18598 `__: MAINT: Add annotations for `dtype.__getitem__`, `__mul__` and... +* `#18602 `__: CI: Do not fail CI on lint error +* `#18605 `__: BUG: Fix ma coercion list-of-ma-arrays if they do not cast to... +* `#18614 `__: MAINT: Bump pycodestyle from 2.6.0 to 2.7.0 +* `#18615 `__: MAINT: Bump hypothesis from 6.6.0 to 6.8.1 +* `#18616 `__: CI: Update apt package list before Python install +* `#18618 `__: MAINT: Ensure that re-exported sub-modules are properly annotated +* `#18622 `__: DOC: Consistently use rng as variable name for random generators +* `#18629 `__: BUG, ENH: fix array2string rounding bug by adding min_digits... +* `#18630 `__: DOC: add note to numpy.rint() docstrings +* `#18634 `__: BUG: Use npy_log1p where appropriate in random generation +* `#18635 `__: ENH: Improve the exception for default low in Generator.integers +* `#18641 `__: MAINT: Remove useless declarations in `bad_commands` +* `#18642 `__: ENH: Use new argument parsing for array creation functions +* `#18643 `__: DOC: Remove mention of nose from README +* `#18645 `__: DOC: Minor fix in inline code example of ufunc reference +* `#18648 `__: MAINT: use super() as described by PEP 3135 +* `#18649 `__: MAINT: Add missing type to cdef statement +* `#18651 `__: BUG: Fix small valgrind-found issues +* `#18652 `__: DOC: Update some plotting code to current Matplotlib idioms +* `#18657 `__: ENH: Improve performance of `np.save` for small arrays +* `#18658 `__: BLD: remove /usr/include from default include dirs +* `#18659 `__: DEV: add a conda environment.yml with all development dependencies +* `#18660 `__: DOC: add release note for removal of /usr/include from include... +* `#18664 `__: MAINT: Bump sphinx from 3.5.2 to 3.5.3 +* `#18666 `__: ENH: Use exponentials in place of inversion in Rayleigh and geometric +* `#18670 `__: BUG: Fix small issues found with pytest-leaks +* `#18676 `__: MAINT: Implement new style promotion for `np.result_type`, etc. +* `#18679 `__: BUG: Changed METH_VARARGS to METH_NOARGS +* `#18680 `__: Docs: simd-optimizations.rst: fix typo (basline ~> baseline) +* `#18685 `__: REL: Update main after 1.20.2 release. +* `#18686 `__: BUG: Fix test_ccompiler_opt when path contains dots +* `#18689 `__: DOC: Change matrix size in absolute beginners doc. +* `#18690 `__: BUG: Correct datetime64 missing type overload for datetime.date... +* `#18691 `__: BUG: fix segfault in object/longdouble operations +* `#18692 `__: MAINT: Bump pydata-sphinx-theme from 0.5.0 to 0.5.2 +* `#18693 `__: MAINT: Bump hypothesis from 6.8.1 to 6.8.3 +* `#18694 `__: TST: pin pypy version to 7.3.4rc1 +* `#18695 `__: ENH: Support parsing Fortran abstract interface blocks. +* `#18697 `__: DEP: Disable PyUFunc_GenericFunction and PyUFunc_SetUsesArraysAsData +* `#18698 `__: MAINT: Specify the color space in all new NumPy logo files +* `#18701 `__: BLD: Strip extra newline when dumping gfortran version on MacOS +* `#18705 `__: DOC: update Steering Council membership and people on governance... +* `#18706 `__: DOC: Add release notes to upcoming_changes +* `#18708 `__: TST: add tests for using np.meshgrid for higher dimensional grids. +* `#18712 `__: DOC: Simplifies Mandelbrot set plot in Quickstart guide +* `#18718 `__: API, DEP: Move ufunc signature parsing to the start +* `#18722 `__: DOC: deduplicate dtype basic types (2) +* `#18725 `__: MAINT: Bump pytest from 6.2.2 to 6.2.3 +* `#18726 `__: MAINT: Bump hypothesis from 6.8.3 to 6.8.4 +* `#18728 `__: MAINT: Add exception chaining where appropriate +* `#18731 `__: BUG: Check out requirements and raise when not satisfied +* `#18733 `__: DEV: Adds gitpod to numpy +* `#18737 `__: BLD: introduce use of BLAS_LIBS and LAPACK_LIBS in distutils/system_info +* `#18739 `__: MAINT: Add exception chaining where appropriate +* `#18741 `__: DOC: Emphasize distinctions between np.copy and ndarray.copy +* `#18745 `__: CI: remove shippable CI +* `#18750 `__: MAINT: Allow more recursion depth for scalar tests. +* `#18751 `__: BUG: Regression #18075 | Fixing Ufunc TD generation order +* `#18753 `__: BLD: Negative zero handling with ifort +* `#18755 `__: MAINT: Bump sphinx from 3.5.3 to 3.5.4 +* `#18757 `__: MAINT: Bump hypothesis from 6.8.4 to 6.9.1 +* `#18758 `__: DOC: Update howto-docs with link to NumPy tutorials. +* `#18761 `__: DOC: Small fixes (including formatting) for NEP 43 +* `#18765 `__: ENH: Improve the placeholder annotations for the main numpy namespace +* `#18766 `__: ENH, SIMD: Replace libdivide functions of signed integer division... +* `#18770 `__: DOC: More concise "How to import NumPy" description +* `#18771 `__: DOC: Use: from numpy.testing import ... +* `#18772 `__: CI: Use informational mode for codecov +* `#18773 `__: CI: Fixing typo in Azure job run +* `#18777 `__: DOC: update random and asserts in test guidelines +* `#18778 `__: MAINT: Relax the integer-type-constraint of `npt._ShapeLike` +* `#18779 `__: DOC: fix spelling of "reccomended" ("recommended") +* `#18780 `__: ENH: Improve the placeholder annotations for the main numpy namespace... +* `#18781 `__: ENH: Add `__all__` to a number of public modules +* `#18785 `__: DOC: change `dec.parametrize` to `pytest.mark.parametrize` +* `#18786 `__: DOC: add note for clip() special case a_min > a_max See #18782 +* `#18787 `__: DOC: Document newer pytest conventions +* `#18789 `__: DEV: Pin pydata-sphinx-theme to 0.5.2. +* `#18790 `__: CI: Use `towncrier build` explicitly +* `#18791 `__: DOC: Fixes small things in the genfromtext docstring +* `#18792 `__: MAINT: Use recent towncrier releases on PyPI. +* `#18795 `__: SIMD, TEST: Workaround for misaligned stack GCC BUG ABI on WIN64 +* `#18796 `__: DOC: Misc Numpydoc and formatting for proper parsing. +* `#18797 `__: DOC: Update random c-api documentation +* `#18799 `__: MAINT: Improve the placeholder annotations for the main numpy... +* `#18800 `__: MAINT: Relax miscellaneous integer-type constraints +* `#18801 `__: DOC: fix typo in frexp docstring +* `#18802 `__: DOC: Improve random.choice() documentation +* `#18805 `__: NEP: propose new nep for allocator policies +* `#18806 `__: MAINT: Bump hypothesis from 6.9.1 to 6.10.0 +* `#18807 `__: MAINT: Bump cython from 0.29.22 to 0.29.23 +* `#18809 `__: MAINT: runtests help text cleanup +* `#18812 `__: DOC: Document howto build documentation in a virtual environment +* `#18813 `__: BUG: Initialize the full nditer buffer in case of error +* `#18818 `__: ENH: Add annotations for 4 objects in `np.core.numerictypes` +* `#18820 `__: MAINT: Remove incorrect inline +* `#18822 `__: DEV: general Gitpod enhancements +* `#18823 `__: MAINT: Minor fix to add reference link to numpy.fill_diagonal... +* `#18825 `__: MAINT: Update README.md +* `#18831 `__: BUG: Prevent nan being used in percentile +* `#18834 `__: DOC: Fix typo in random docs +* `#18836 `__: MAINT: Generalize and shorten the ufunc "trivially iterable"... +* `#18837 `__: ENH, SIMD: Add support for dispatching C++ sources +* `#18839 `__: DOC: Add Gitpod development documentation +* `#18841 `__: DOC: Add favicon +* `#18842 `__: ENH: Improve the placeholder annotations within sub-modules +* `#18843 `__: DOC: Clarify isreal docstring +* `#18845 `__: DOC: Move Sphinx numpy target in reference index. +* `#18851 `__: MAINT: Disable pip version check for azure lint check. +* `#18853 `__: ENH: Improve the placeholder annotations within sub-modules (part... +* `#18855 `__: STY: change CRLF line terminators to Unix +* `#18856 `__: MAINT: Fix the typo "implment" +* `#18862 `__: TST: Skip f2py TestSharedMemory for LONGDOUBLE on macos/arm64 +* `#18863 `__: ENH: Add max values comparison for floating point +* `#18864 `__: MAINT: Remove dead codepath in generalized ufuncs +* `#18868 `__: Upgrade to GitHub-native Dependabot +* `#18869 `__: MAINT: Fix azure linter problems with pip 21.1 +* `#18871 `__: MAINT: Bump hypothesis from 6.10.0 to 6.10.1 +* `#18874 `__: BLD, ENH: Enable Accelerate Framework +* `#18877 `__: MAINT: Update PyPy version used by CI +* `#18880 `__: API: Ensure that casting does not affect ufunc loop +* `#18882 `__: ENH: Add min values comparison for floating point +* `#18885 `__: MAINT: Remove unsafe unions and ABCs from return-annotations +* `#18889 `__: ENH: Add SIMD operations for min and max value comparision +* `#18890 `__: MAINT: ssize_t -> Py_ssize_t and other fixes for Python v3.10.0 +* `#18891 `__: MAINT: Bump typing-extensions from 3.7.4.3 to 3.10.0.0 +* `#18893 `__: DOC: Add a set of standard replies. +* `#18895 `__: DOC: Improve cumsum documentation +* `#18896 `__: MAINT: Explicitly mark text files in .gitattributes. +* `#18897 `__: MAINT: Add ".csv" some data file names. +* `#18899 `__: BLD, BUG: Fix compiler optimization log AttributeError +* `#18900 `__: BLD: remove unnecessary flag `-faltivec` on macOS +* `#18903 `__: MAINT, CI: treats _SIMD module build warnings as errors through... +* `#18906 `__: ENH: Add PCG64DXSM BitGenerator +* `#18908 `__: MAINT: Adjust NumPy float hashing to Python's slightly changed... +* `#18909 `__: ENH: Improve the placeholder annotations within sub-modules (part... +* `#18910 `__: BUG : for MINGW, threads.h existence test requires GLIBC > 2.12 +* `#18911 `__: BLD, BUG: Fix bdist_wheel duplicate building +* `#18912 `__: CI: fix the GitHub Actions trigger in docker.yml +* `#18918 `__: DOC: fix documentation of cloning over ssh +* `#18919 `__: ENH: Add placeholder annotations for two missing `np.testing`... +* `#18920 `__: BUG: Report underflow condition in AVX implementation of np.exp +* `#18927 `__: NEP: add mailing list thread, fixes from review +* `#18930 `__: BUG: Make changelog recognize ``gh-`` as a PR number prefix. +* `#18931 `__: BUG: Fix refcounting in string-promotion deprecation code path +* `#18933 `__: BUG: Fix underflow error in AVX512 implementation of ufunc exp/f64 +* `#18934 `__: DOC: Add a release note for the improved placeholder annotations +* `#18935 `__: API: Add `npt.NDArray`, a runtime-subscriptable alias for `np.ndarray` +* `#18936 `__: DOC: Update performance for new PRNG +* `#18940 `__: ENH: manually inline PCG64DXSM code for performance. +* `#18943 `__: TST: xfail `TestCond.test_nan` unconditionally +* `#18944 `__: ENH: Add annotations for `np.lib.utils` +* `#18954 `__: DOC: Update beginners docu for sum function with axis +* `#18955 `__: DOC: add an extra example in runtests.py help test +* `#18956 `__: DOC: change copyright SciPy to NumPy +* `#18957 `__: DOC: Improve datetime64 docs. +* `#18958 `__: MAINT: Do not use deprecated ``mktemp()`` +* `#18959 `__: DOC: improve numpy.histogram2d() documentation +* `#18960 `__: BUG: fixed ma.average ignoring masked weights +* `#18961 `__: DOC: add note and examples to `isrealobj` docstring +* `#18962 `__: DOC: Update a page title with proper case +* `#18963 `__: DEP: remove PolyBase from np.polynomial.polyutils +* `#18965 `__: DOC: Improve description of array scalar in glossary +* `#18967 `__: BUG: fix np.ma.masked_where(copy=False) when input has no mask +* `#18970 `__: MAINT, SIMD: Hardened the AVX compile-time tests +* `#18972 `__: ENH: Include co-authors in changelog. +* `#18973 `__: MAINT: Bump sphinx from 3.5.4 to 4.0.0 +* `#18974 `__: MAINT: Bump hypothesis from 6.10.1 to 6.12.0 +* `#18976 `__: MAINT: Bump pytest from 6.2.3 to 6.2.4 +* `#18980 `__: DOC: Gitpod documentation enhancements +* `#18982 `__: MAINT: Cleanup tools/changelog.py +* `#18983 `__: REL: Update main after 1.20.3 release. +* `#18985 `__: MAINT: Remove usage of the PEP 604 pipe operator +* `#18987 `__: BUG: Update coordinates in PyArray_ITER_GOTO1D +* `#18989 `__: BUG: fix potential buffer overflow(#18939) +* `#18990 `__: ENH: Add annotations for `np.lib.NumpyVersion` +* `#18996 `__: MAINT: Remove warning when checking AVX512f on MSVC +* `#18998 `__: ENH: Improve annotations of the `item`, `tolist`, `take` and... +* `#18999 `__: DEP: Ensure the string promotion FutureWarning is raised +* `#19001 `__: DEP: Deprecate error clearing for special method in array-coercion +* `#19002 `__: ENH: Add annotations for `np.broadcast` and `np.DataSource` +* `#19005 `__: ENH: Add dtype-support to 11 `ndarray` / `generic` methods +* `#19007 `__: BUG: fix potential use of null pointer in nditer buffers +* `#19008 `__: BUG: fix variable misprint in multiarray test code +* `#19009 `__: BUG: fix variable misprint checking wrong variable in umath tests +* `#19011 `__: BUG: fix ValueError in PyArray_Std on win_amd64 +* `#19012 `__: MAINT: Small cleanups in `PyArray_NewFromDescr_int` +* `#19014 `__: Revert "BUG: Update coordinates in PyArray_ITER_GOTO1D" +* `#19018 `__: DOC: "NumPy" <- "numpy" in NumPy Fundamentals - Indexing +* `#19021 `__: DOC: Add comment for ifdef macro guard +* `#19024 `__: MAINT: Bump pytest-cov from 2.11.1 to 2.12.0 +* `#19025 `__: MAINT: Bump sphinx from 4.0.0 to 4.0.1 +* `#19026 `__: DOC: Clarify minimum numpy version needed to use random c-api +* `#19029 `__: ENH: Improve the annotations of `np.core._internal` +* `#19031 `__: DEP: Deprecate 4 `ndarray.ctypes` methods +* `#19035 `__: MAINT: Python3 classes do not need to inherit from object +* `#19037 `__: BUG: do not use PyLong_FromLong for intp +* `#19041 `__: DOC: Improve trapz docstring +* `#19043 `__: DOC: Fix typo in release notes for v1.21 +* `#19046 `__: BUG, SIMD: Fix unexpected result of uint8 division on X86 +* `#19047 `__: BUG, SIMD: Fix NumPy build on ppc64le(IBM/Power) for old versions... +* `#19048 `__: BUG: Fix duplicate variable names in compiler check for AVX512_SKX +* `#19049 `__: BLD,API: (distutils) Force strict floating point error model... +* `#19052 `__: ENH: Improve the `np.ufunc` annotations +* `#19055 `__: DOC: Forward port missing 1.18.5 release note. +* `#19063 `__: ENH: Stubs for array_equal appear out of date. +* `#19066 `__: BUG: Fixed an issue wherein `nanmedian` could return an array... +* `#19068 `__: MAINT: Update mailmap +* `#19073 `__: REL: Prepare 1.21.0 release +* `#19074 `__: BUG: Fix compile-time test of POPCNT +* `#19075 `__: BUG: Fix test_numpy_version. +* `#19094 `__: BUG: Fixed an issue wherein `_GenericAlias.__getitem__` would... +* `#19100 `__: BUG: Linter should only run on pull requests. +* `#19120 `__: BUG: Fix setup.py to work in maintenance branches. +* `#19144 `__: BUG: expose short_version as previously in version.py +* `#19175 `__: API: Delay string and number promotion deprecation/future warning +* `#19178 `__: BUG, SIMD: Fix detect host/native CPU features on ICC at compile-time +* `#19180 `__: BUG: Add -std=c99 to intel icc compiler flags on linux +* `#19193 `__: NEP: Accept NEP 35 as final +* `#19194 `__: MAINT, BUG: Adapt `castingimpl.casting` to denote a minimal level +* `#19197 `__: REL: Prepare for NumPy 1.20.0rc2 release. +* `#19213 `__: MAINT: Add annotations for the missing `period` parameter to... +* `#19219 `__: MAINT: Add `complex` as allowed type for the `np.complexfloating`... +* `#19233 `__: TST: Ignore exp FP exceptions test for glibc ver < 2.17 +* `#19238 `__: MAINT: replace imgmath with mathjax for docs +* `#19239 `__: BUG: Fix out-of-bounds access in convert_datetime_divisor_to_multiple +* `#19240 `__: ENH: Support major version larger than 9 in NumpyVersion +* `#19268 `__: DOC: fix duplicate navbar in development documentation index +* `#19269 `__: BUG: Invalid dtypes comparison should not raise TypeError +* `#19280 `__: BUG: Add missing DECREF in new path +* `#19283 `__: REL: Prepare for 1.21.0 release diff --git a/doc/changelog/1.21.1-changelog.rst b/doc/changelog/1.21.1-changelog.rst new file mode 100644 index 000000000000..f219c5012323 --- /dev/null +++ b/doc/changelog/1.21.1-changelog.rst @@ -0,0 +1,51 @@ + +Contributors +============ + +A total of 11 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Bas van Beek +* Charles Harris +* Ganesh Kathiresan +* Gregory R. Lee +* Hugo Defois + +* Kevin Sheppard +* Matti Picus +* Ralf Gommers +* Sayed Adel +* Sebastian Berg +* Thomas J. Fan + +Pull requests merged +==================== + +A total of 26 pull requests were merged for this release. + +* `#19311 `__: REV,BUG: Replace ``NotImplemented`` with ``typing.Any`` +* `#19324 `__: MAINT: Fixed the return-dtype of ``ndarray.real`` and ``imag`` +* `#19330 `__: MAINT: Replace ``"dtype[Any]"`` with ``dtype`` in the definiton of... +* `#19342 `__: DOC: Fix some docstrings that crash pdf generation. +* `#19343 `__: MAINT: bump scipy-mathjax +* `#19347 `__: BUG: Fix arr.flat.index for large arrays and big-endian machines +* `#19348 `__: ENH: add ``numpy.f2py.get_include`` function +* `#19349 `__: BUG: Fix reference count leak in ufunc dtype handling +* `#19350 `__: MAINT: Annotate missing attributes of ``np.number`` subclasses +* `#19351 `__: BUG: Fix cast safety and comparisons for zero sized voids +* `#19352 `__: BUG: Correct Cython declaration in random +* `#19353 `__: BUG: protect against accessing base attribute of a NULL subarray +* `#19365 `__: BUG, SIMD: Fix detecting AVX512 features on Darwin +* `#19366 `__: MAINT: remove ``print()``'s in distutils template handling +* `#19390 `__: ENH: SIMD architectures to show_config +* `#19391 `__: BUG: Do not raise deprecation warning for all nans in unique... +* `#19392 `__: BUG: Fix NULL special case in object-to-any cast code +* `#19430 `__: MAINT: Use arm64-graviton2 for testing on travis +* `#19495 `__: BUILD: update OpenBLAS to v0.3.17 +* `#19496 `__: MAINT: Avoid unicode characters in division SIMD code comments +* `#19499 `__: BUG, SIMD: Fix infinite loop during count non-zero on GCC-11 +* `#19500 `__: BUG: fix a numpy.npiter leak in npyiter_multi_index_set +* `#19501 `__: TST: Fix a ``GenericAlias`` test failure for python 3.9.0 +* `#19502 `__: MAINT: Start testing with Python 3.10.0b3. +* `#19503 `__: MAINT: Add missing dtype overloads for object- and ctypes-based... +* `#19510 `__: REL: Prepare for NumPy 1.21.1 release. + diff --git a/doc/changelog/1.21.2-changelog.rst b/doc/changelog/1.21.2-changelog.rst new file mode 100644 index 000000000000..f23fb0cfdac3 --- /dev/null +++ b/doc/changelog/1.21.2-changelog.rst @@ -0,0 +1,41 @@ + +Contributors +============ + +A total of 10 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Bas van Beek +* Carl Johnsen + +* Charles Harris +* Gwyn Ciesla + +* Matthieu Dartiailh +* Matti Picus +* Niyas Sait + +* Ralf Gommers +* Sayed Adel +* Sebastian Berg + +Pull requests merged +==================== + +A total of 18 pull requests were merged for this release. + +* `#19497 `__: MAINT: set Python version for 1.21.x to ``<3.11`` +* `#19533 `__: BUG: Fix an issue wherein importing ``numpy.typing`` could raise +* `#19646 `__: MAINT: Update Cython version for Python 3.10. +* `#19648 `__: TST: Bump the python 3.10 test version from beta4 to rc1 +* `#19651 `__: TST: avoid distutils.sysconfig in runtests.py +* `#19652 `__: MAINT: add missing dunder method to nditer type hints +* `#19656 `__: BLD, SIMD: Fix testing extra checks when ``-Werror`` isn't applicable... +* `#19657 `__: BUG: Remove logical object ufuncs with bool output +* `#19658 `__: MAINT: Include .coveragerc in source distributions to support... +* `#19659 `__: BUG: Fix bad write in masked iterator output copy paths +* `#19660 `__: ENH: Add support for windows on arm targets +* `#19661 `__: BUG: add base to templated arguments for platlib +* `#19662 `__: BUG,DEP: Non-default UFunc signature/dtype usage should be deprecated +* `#19666 `__: MAINT: Add Python 3.10 to supported versions. +* `#19668 `__: TST,BUG: Sanitize path-separators when running ``runtest.py`` +* `#19671 `__: BLD: load extra flags when checking for libflame +* `#19676 `__: BLD: update circleCI docker image +* `#19677 `__: REL: Prepare for 1.21.2 release. diff --git a/doc/changelog/1.21.3-changelog.rst b/doc/changelog/1.21.3-changelog.rst new file mode 100644 index 000000000000..7677947218d5 --- /dev/null +++ b/doc/changelog/1.21.3-changelog.rst @@ -0,0 +1,28 @@ + +Contributors +============ + +A total of 7 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Aaron Meurer +* Bas van Beek +* Charles Harris +* Developer-Ecosystem-Engineering + +* Kevin Sheppard +* Sebastian Berg +* Warren Weckesser + +Pull requests merged +==================== + +A total of 8 pull requests were merged for this release. + +* `#19745 `__: ENH: Add dtype-support to 3 `generic`/`ndarray` methods +* `#19955 `__: BUG: Resolve Divide by Zero on Apple silicon + test failures... +* `#19958 `__: MAINT: Mark type-check-only ufunc subclasses as ufunc aliases... +* `#19994 `__: BUG: np.tan(np.inf) test failure +* `#20080 `__: BUG: Correct incorrect advance in PCG with emulated int128 +* `#20081 `__: BUG: Fix NaT handling in the PyArray_CompareFunc for datetime... +* `#20082 `__: DOC: Ensure that we add documentation also as to the dict for... +* `#20106 `__: BUG: core: result_type(0, np.timedelta64(4)) would seg. fault. diff --git a/doc/changelog/1.21.4-changelog.rst b/doc/changelog/1.21.4-changelog.rst new file mode 100644 index 000000000000..3452627c0ab6 --- /dev/null +++ b/doc/changelog/1.21.4-changelog.rst @@ -0,0 +1,29 @@ + +Contributors +============ + +A total of 7 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Bas van Beek +* Charles Harris +* Isuru Fernando +* Matthew Brett +* Sayed Adel +* Sebastian Berg +* 傅立业(Chris Fu) + + +Pull requests merged +==================== + +A total of 9 pull requests were merged for this release. + +* `#20278 `__: BUG: Fix shadowed reference of ``dtype`` in type stub +* `#20293 `__: BUG: Fix headers for universal2 builds +* `#20294 `__: BUG: ``VOID_nonzero`` could sometimes mutate alignment flag +* `#20295 `__: BUG: Do not use nonzero fastpath on unaligned arrays +* `#20296 `__: BUG: Distutils patch to allow for 2 as a minor version (!) +* `#20297 `__: BUG, SIMD: Fix 64-bit/8-bit integer division by a scalar +* `#20298 `__: BUG, SIMD: Workaround broadcasting SIMD 64-bit integers on MSVC... +* `#20300 `__: REL: Prepare for the NumPy 1.21.4 release. +* `#20302 `__: TST: Fix a ``Arrayterator`` typing test failure diff --git a/doc/changelog/1.22.0-changelog.rst b/doc/changelog/1.22.0-changelog.rst new file mode 100644 index 000000000000..c7a49bc13376 --- /dev/null +++ b/doc/changelog/1.22.0-changelog.rst @@ -0,0 +1,775 @@ + +Contributors +============ + +A total of 153 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* @DWesl +* @Illviljan +* @h-vetinari +* @yan-wyb + +* Aaron Meurer +* Abel Aoun + +* Adrian Gao + +* Ahmet Can Solak + +* Ajay DS + +* Alban Colley + +* Alberto Rubiales + +* Alessia Marcolini + +* Amit Kumar + +* Andrei Batomunkuev + +* Andrew Watson + +* Anirudh Dagar + +* Ankit Dwivedi + +* Antony Lee +* Arfy Slowy + +* Arryan Singh + +* Arun Palaniappen + +* Arushi Sharma + +* Bas van Beek +* Brent Brewington + +* Carl Johnsen + +* Carl Michal + +* Charles Harris +* Chiara Marmo +* Chris Fu (傅立业) + +* Christoph Buchner + +* Christoph Reiter + +* Chunlin Fang +* Clément Robert + +* Constanza Fierro +* Damien Caliste +* Daniel Ching +* David Badnar + +* David Cortes + +* David Okpare + +* Derek Huang + +* Developer-Ecosystem-Engineering + +* Dima Pasechnik +* Dimitri Papadopoulos + +* Dmitriy Fishman + +* Eero Vaher + +* Elias Koromilas + +* Eliaz Bobadilla + +* Elisha Hollander + +* Eric Wieser +* Eskild Eriksen + +* Evan Miller + +* Fayas Noushad + +* Gagandeep Singh + +* Ganesh Kathiresan +* Ghiles Meddour + +* Greg Lucas +* Gregory R. Lee +* Guo Shuai + +* Gwyn Ciesla + +* Hameer Abbasi +* Hector Martin + +* Henry Schreiner + +* Himanshu + +* Hood Chatham + +* Hugo Defois + +* Hugo van Kemenade +* I-Shen Leong + +* Imen Rajhi + +* Irina Maria Mocan + +* Irit Katriel + +* Isuru Fernando +* Jakob Jakobson +* Jerry Morrison + +* Jessi J Zhao + +* Joe Marshall + +* Johan von Forstner + +* Jonas I. Liechti + +* Jonathan Reichelt Gjertsen + +* Joshua Himmens + +* Jérome Eertmans +* Jérôme Kieffer + +* KIU Shueng Chuan + +* Kazuki Sakamoto + +* Kenichi Maehashi +* Kenny Huynh + +* Kent R. Spillner + +* Kevin Granados + +* Kevin Modzelewski + +* Kevin Sheppard +* Lalit Musmade + +* Malik Idrees Hasan Khan + +* Marco Aurelio da Costa + +* Margret Pax + +* Mars Lee + +* Marten van Kerkwijk +* Matthew Barber + +* Matthew Brett +* Matthias Bussonnier +* Matthieu Dartiailh +* Matti Picus +* Melissa Weber Mendonça +* Michael McCann + +* Mike Jarvis + +* Mike McCann + +* Mike Toews +* Mukulika Pahari +* Nick Pope + +* Nick Wogan + +* Niels Dunnewind + +* Niko Savola + +* Nikola Forró +* Niyas Sait + +* Pamphile ROY +* Paul Ganssle + +* Pauli Virtanen +* Pearu Peterson +* Peter Hawkins + +* Peter Tillema + +* Prathmesh Shirsat + +* Raghuveer Devulapalli +* Ralf Gommers +* Robert Kern +* Rohit Goswami + +* Ronan Lamy +* Ross Barnowski +* Roy Jacobson + +* Samyak S Sarnayak + +* Sayantika Banik + +* Sayed Adel +* Sebastian Berg +* Sebastian Schleehauf + +* Serge Guelton +* Shriraj Hegde + +* Shubham Gupta + +* Sista Seetaram + +* Stefan van der Walt +* Stephannie Jimenez Gacha + +* Tania Allard +* Theodoros Nikolaou + +* Thomas Green + +* Thomas J. Fan +* Thomas Li + +* Tim Hoffmann +* Tom Tan + +* Tyler Reddy +* Vijay Arora + +* Vinith Kishore + +* Warren Weckesser +* Yang Hau +* Yashasvi Misra +* Yuval Ofek + +* Zac Hatfield-Dodds +* Zhang Na + + +Pull requests merged +==================== + +A total of 609 pull requests were merged for this release. + +* `#15847 `__: BUG: avoid infinite recurrence on dependencies in crackfortran +* `#16740 `__: ENH: Add broadcast support to Generator.multinomial +* `#16796 `__: DOC: Added a warning about fractional steps in np.arange +* `#17530 `__: ENH: Allow ``ctypeslib.load_library`` to take any path-like object +* `#17582 `__: ENH: Configurable allocator +* `#18203 `__: MAINT: Speedup np.quantile. +* `#18330 `__: TST: Add cygwin build to CI +* `#18421 `__: DOC: Adjust polyfit doc to clarify the meaning of w +* `#18536 `__: ENH: Add smallest_normal and smallest_subnormal attributes to... +* `#18585 `__: ENH: Implementation of the NEP 47 (adopting the array API standard) +* `#18759 `__: BUG: revise string_from_pyobj/try_pyarr_from_string with respect... +* `#18762 `__: MAINT: Remove unused imports and unreachable code +* `#18775 `__: DOC: Ensure that we add documentation also as to the dict for... +* `#18884 `__: DOC: Add support for documenting C/C++ via Doxygen & Breathe +* `#18905 `__: MAINT: Refactor reductions to use NEP 43 style dispatching/promotion +* `#18964 `__: DOC: replace np.ma functions' return types with ``MaskedArray`` +* `#18984 `__: DOC: add example showing how to convert POSIX timestamps to datetime64 +* `#19003 `__: DOC: Remove misleading info about Fortran compiler in Building... +* `#19016 `__: BUG: Update coordinates on PyArray_ITER_GOTO1D +* `#19022 `__: SIMD: Add new universal intrinsic for ceil +* `#19023 `__: BUG: fix np.ma.MaskedArray.anom when input is masked +* `#19036 `__: MAINT: replace imgmath with mathjax for docs +* `#19058 `__: BUG: Fixes to getter signatures +* `#19060 `__: ENH: Add initial annotations to ``np.core.multiarray`` +* `#19062 `__: ENH: Add a mypy plugin for inferring the precision of ``np.ctypeslib.c_intp`` +* `#19070 `__: REL: Prepare for NumPy 1.22.0 development +* `#19071 `__: BUG: Fix compile-time test of POPCNT +* `#19072 `__: BUG, TST: Fix test_numpy_version. +* `#19082 `__: MAINT: Bump hypothesis from 6.12.0 to 6.13.4 +* `#19083 `__: ENH: Implement the DLPack Array API protocols for ndarray. +* `#19086 `__: BUG: Linter should only run on pull requests. +* `#19087 `__: DOC: Add note to savez about naming variables with keyword ``file``. +* `#19089 `__: DOC: Add example to histogram2d docstring +* `#19090 `__: MAINT: removed unused imports listed in LGTM +* `#19092 `__: BUG: Fixed an issue wherein ``_GenericAlias.__getitem__`` would... +* `#19093 `__: DOC: add a "Returns" section for ``np.frombuffer`` +* `#19096 `__: BUG: Fix setup.py to work in maintenance branches. +* `#19098 `__: BUG, SIMD: Fix detect host/native CPU features on ICC during... +* `#19099 `__: DOC: fixed unsigned integer alias links. +* `#19102 `__: MAINT: Removed suitable unused variables shown in LGTM +* `#19110 `__: DOC: Fix the documented default value of the ``order`` parameter... +* `#19115 `__: DOC: Misc fixes to ``absolute_beginners.html`` +* `#19118 `__: MAINT: Misc cleaning of ``numpy.typing`` +* `#19119 `__: BUG: Adjust shallow clone in the gitpod container +* `#19121 `__: DOC: Fix missing files and deprecated commands. +* `#19124 `__: BUG: Fixed an issue wherein ``poly1d.__getitem__`` could return... +* `#19128 `__: DOC:``Building the NumPy API and reference docs`` rewrite +* `#19130 `__: ENH: SIMD architectures to show_config +* `#19131 `__: DOC: added explanation about tril/triu n-dimensional functionality. +* `#19132 `__: BUG: Use larger fetch depth in gitpod.yml +* `#19135 `__: BUG: Remove complex floor divide +* `#19139 `__: MAINT: Bump hypothesis from 6.13.4 to 6.13.10 +* `#19140 `__: ENH: Add dtype-support to 3 ``generic``/``ndarray`` methods +* `#19142 `__: BUG: expose ``short_version`` as previously in version.py +* `#19151 `__: ENH: Vectorising np.linalg.qr +* `#19165 `__: DOC: Explicitly mention that ``searchsorted`` returns an integer... +* `#19167 `__: ENH: Improve readibility of error message in terminal. +* `#19170 `__: API: Delay string and number promotion deprecation/future warning +* `#19172 `__: BUG: Fixed an issue wherein ``_GenericAlias`` could raise for non-iterable... +* `#19173 `__: ENH: Add support for copy modes to NumPy +* `#19174 `__: MAINT, BUG: Adapt ``castingimpl.casting`` to denote a minimal level +* `#19176 `__: REV,BUG: Replace ``NotImplemented`` with ``typing.Any`` +* `#19177 `__: BUG: Add ``-std=c99`` to intel icc compiler flags on linux +* `#19179 `__: ENH: Add annotations for ``np.testing`` +* `#19181 `__: MAINT: Bump pytest-cov from 2.12.0 to 2.12.1 +* `#19182 `__: MAINT: Bump hypothesis from 6.13.10 to 6.13.14 +* `#19185 `__: DOC: Crosslinking to Gitpod guide +* `#19186 `__: DOC: ndindex class docstrings fix +* `#19188 `__: NEP: Accept NEP 35 (``like=`` keyword for array creation) as final +* `#19195 `__: DOC: Link issue label +* `#19196 `__: DOC: update references to other repos head branch to 'main' +* `#19200 `__: DOC: NeighborhoodIterator position on creation +* `#19202 `__: BUG: Fix out-of-bounds access in convert_datetime_divisor_to_multiple +* `#19209 `__: TST: Ignore exp FP exceptions test for glibc ver < 2.17 +* `#19211 `__: ENH: Adding keepdims to np.argmin,np.argmax +* `#19212 `__: MAINT: Add annotations for the missing ``period`` parameter to... +* `#19214 `__: ENH: Support major version larger than 9 in ``NumpyVersion`` +* `#19218 `__: MAINT: Add ``complex`` as allowed type for the ``np.complexfloating``... +* `#19223 `__: ENH: Add annotations for ``np.pad`` +* `#19224 `__: MAINT: Remove python 2 specific string comparison code +* `#19225 `__: DOC: Fix some inconsistencies in the docstring of matrix_rank +* `#19227 `__: ENH: Add annotations to ``np.core.multiarray`` part 2/4 +* `#19228 `__: BUG: Invalid dtypes comparison should not raise ``TypeError`` +* `#19235 `__: Revert "BUG: revise string_from_pyobj/try_pyarr_from_string with... +* `#19237 `__: ENH: Add annotations to ``np.core.multiarray`` part 3/4 +* `#19241 `__: MAINT: Bump hypothesis from 6.13.14 to 6.14.0 +* `#19242 `__: MAINT: Bump mypy from 0.812 to 0.902 +* `#19244 `__: BUG: Fix an issue wherein assigment to ``np.ma.masked_array`` ignores... +* `#19245 `__: ENH: Add dtype-support to the ``np.core.shape_base`` annotations +* `#19251 `__: BUG: revise string_from_pyobj/try_pyarr_from_string with respect... +* `#19254 `__: MAINT: Refactor output ufunc wrapping logic +* `#19256 `__: DOC: Fix formatting in rot90() docstring +* `#19257 `__: MAINT: Move array-prep and type resolution to earlier +* `#19258 `__: MAINT: Refactor and simplify the main ufunc iterator loop code +* `#19259 `__: MAINT: Align masked with normal ufunc loops +* `#19261 `__: ENH: Add annotations for ``np.lib.twodim_base`` +* `#19262 `__: MAINT: Some tiny fixes and style changes in ``ufunc_object.c`` +* `#19263 `__: STY: Small changes to the ``PyUFunc_ReduceWrapper`` +* `#19264 `__: DOC: fix duplicate navbar in development documentation index +* `#19275 `__: MAINT: Misc typing maintenance for ``np.dtype`` +* `#19276 `__: BUG: Fix ``arr.flat.index`` for large arrays and big-endian machines +* `#19277 `__: BUG: Add missing DECREF in new path +* `#19278 `__: MAINT: Remove accidentally created directory. +* `#19281 `__: ENH: add ``numpy.f2py.get_include`` function +* `#19284 `__: NEP: Fixes from NEP36 feedback +* `#19285 `__: MAINT: Use Ubuntu focal for travis-ci builds. +* `#19286 `__: ENH: Add annotations for ``np.lib.type_check`` +* `#19289 `__: BUG: Fix reference count leak in ufunc dtype handling +* `#19290 `__: DOC: Unpin pydata sphinx theme and update config to avoid long... +* `#19292 `__: MAINT: Add lightweight identity-hash map +* `#19293 `__: MAINT: Add simple tuple creation helper and use it +* `#19295 `__: DOC: Add ``versionadded`` directives to ``numpy.typing`` +* `#19298 `__: DOC: Add documentation for ``np.ctypeslib.c_intp`` +* `#19301 `__: BUG: Do not raise deprecation warning for all nans in unique +* `#19306 `__: DOC: Fix some docstrings that crash pdf generation. +* `#19314 `__: MAINT: bump scipy-mathjax +* `#19316 `__: BUG: Fix warning problems of the mod operator +* `#19317 `__: MAINT: Clean up multiarray interned strings +* `#19320 `__: REL: Update main after 1.21.0 release. +* `#19322 `__: BUG: Fix cast safety and comparisons for zero sized voids +* `#19323 `__: BUG: Correct Cython declaration in random +* `#19326 `__: BUG: protect against accessing base attribute of a NULL subarray +* `#19328 `__: MAINT: Replace ``"dtype[Any]"`` with ``dtype`` in the definiton of... +* `#19329 `__: ENH Add a conda-based CI job on azure. +* `#19338 `__: DOC: Removed duplicate instructions for building docs from ``dev/index``... +* `#19344 `__: MAINT: Annotate missing attributes of ``np.number`` subclasses +* `#19355 `__: ENH: Adding ``bit_count`` (popcount) +* `#19356 `__: API: Ensure np.vectorize outputs can be subclasses. +* `#19359 `__: ENH: Add annotations for ``np.f2py`` +* `#19360 `__: MAINT: remove ``print()``'s in distutils template handling +* `#19361 `__: ENH: Use literals for annotating ``int``- & ``str``-based constants +* `#19362 `__: BUG, SIMD: Fix detecting AVX512 features on Darwin +* `#19368 `__: MAINT: Bump mypy from 0.902 to 0.910 +* `#19369 `__: DOC: Moved VQ example & target from duplicate array broadcasting... +* `#19370 `__: MAINT: Move masked strided/inner-loop code to its "final" place +* `#19371 `__: MAINT: Use cast-is-view flag for the ufunc trivial-loop check +* `#19378 `__: DOC: fix remaining np.min/np.max usages +* `#19380 `__: BUG: Fix NULL special case in object-to-any cast code +* `#19381 `__: MAINT: Modify initialization order during multiarray import +* `#19393 `__: MAINT: fix overly broad exception handling listed in LGTM +* `#19394 `__: BUG, SIMD: Fix infinite loop during count non-zero on GCC-11 +* `#19396 `__: BUG: fix a numpy.npiter leak in npyiter_multi_index_set +* `#19402 `__: DOC: typo fix +* `#19403 `__: BUG: Fix memory leak in function npyiter_multi_index_set +* `#19404 `__: NEP: update NEP with the PyDataMem_Handler struct as implemented... +* `#19407 `__: DOC: Rearranged parts of the Indexing docs to consolidate content +* `#19408 `__: ENH: Add annotations for misc python-based functions +* `#19409 `__: BUG: fix some memory leaks in ufunc_object +* `#19412 `__: MAINT: Bump sphinx from 4.0.1 to 4.0.3 +* `#19413 `__: MAINT: Bump hypothesis from 6.14.0 to 6.14.1 +* `#19416 `__: DOC: Remove duplicate information about governance +* `#19418 `__: DOC: Removing tutorials from sphinx documentation +* `#19419 `__: BUG: fix f2py markinnerspace for multiple quotations +* `#19421 `__: ENH: Add annotations for ``np.core.getlimits`` +* `#19422 `__: DOC: Additional ideas related to numpy-tutorials integration +* `#19423 `__: Skip finite recursion and refcounting tests for pyston +* `#19426 `__: MAINT: Use arm64-graviton2 for testing on travis +* `#19429 `__: BUG: Fix some multiarray leaks +* `#19431 `__: MAINT: Delete old SSE2 ``absolute`` implementation +* `#19434 `__: MAINT: Fix the module of ``flagsobj`` +* `#19436 `__: ENH: Improve the annotations of ``flagsobj`` +* `#19440 `__: MAINT: factored out _PyArray_ArgMinMaxCommon +* `#19442 `__: MAINT: Use "with open(...)" +* `#19444 `__: ENH: Add annotations for ``np.lib.shape_base`` +* `#19445 `__: DOC: broadcast_to() supports int as shape parameter +* `#19446 `__: MAINT: Start testing with Python 3.10.0b3. +* `#19447 `__: DOC: BLAS/LAPACK linking rules +* `#19450 `__: TST: Simplify property-based test +* `#19451 `__: BUG: Make openblas_support support ILP64 on Windows. +* `#19456 `__: TST: Fix a ``GenericAlias`` test failure for python 3.9.0 +* `#19458 `__: MAINT: Avoid unicode characters in division SIMD code comments +* `#19459 `__: ENH: Add the ``axis`` and ``ndim`` attributes to ``np.AxisError`` +* `#19460 `__: MAINT: Bump sphinx from 4.0.3 to 4.1.0 +* `#19461 `__: MAINT: Bump hypothesis from 6.14.1 to 6.14.2 +* `#19462 `__: BUILD: move to OpenBLAS 0.3.16 +* `#19463 `__: MAINT: Use straight arm64 in TravisCI. +* `#19468 `__: MAINT: Add missing ``dtype`` overloads for object- and ctypes-based... +* `#19475 `__: DOC: Fix see also references in ``numpy.resize`` +* `#19478 `__: ENH: Vectorizing umath module using AVX-512 (open sourced from... +* `#19479 `__: BLD: Add clang ``-ftrapping-math`` also for ``compiler_so`` +* `#19483 `__: MAINT: Update for using ``openblas64_``. +* `#19485 `__: TST/BENCH: Adding test coverage and benchmarks for floating point... +* `#19486 `__: DOC: Add link to NumPy PDF docs +* `#19491 `__: MAINT: Disable test_blas64_dot. +* `#19492 `__: BUILD: update OpenBLAS to v0.3.17 +* `#19493 `__: TST: generalise ``clip`` test +* `#19498 `__: MAINT: Update manylinux ci test to manylinux2014 +* `#19506 `__: DOC: Fix typos +* `#19512 `__: REL: Update main after 1.21.1 release. +* `#19513 `__: ENH: Add support for windows on arm targets +* `#19516 `__: DOC: Created fundamentals doc for explanations in ``ufunc`` reference... +* `#19517 `__: MAINT: Bump sphinx from 4.1.0 to 4.1.1 +* `#19518 `__: MAINT: Bump hypothesis from 6.14.2 to 6.14.3 +* `#19519 `__: MAINT: Bump cython from 0.29.23 to 0.29.24 +* `#19525 `__: TST: Test that ``numpy.typing`` can be imported in the absence... +* `#19526 `__: MAINT: bump Sphinx in environment.yml file +* `#19527 `__: BLD: Add LoongArch support +* `#19529 `__: SIMD: Force inlining all functions that accept AVX registers +* `#19534 `__: BLD: Tell fortran compiler Cygwin doesn't support rpath. +* `#19535 `__: TST: Add Cygwin to the x86 feature tests. +* `#19538 `__: DOC: Fix typo in PCG64 +* `#19539 `__: DEP: Remove deprecated numeric style dtype strings +* `#19540 `__: MAINT: Update the ``np.finfo`` annotations +* `#19542 `__: TST: Parametrize a few more tests. +* `#19543 `__: MAINT: Improve the ``np.core.numerictypes`` stubs +* `#19545 `__: DOC: Add clarification +* `#19546 `__: DOC: Add link and explanation of ``_add_newdocs`` to developer... +* `#19547 `__: BLD: Use cygpath utility for path conversion in cyg2win32 +* `#19554 `__: MAINT: add missing dunder method to nditer type hints +* `#19557 `__: DOC: clarify doc re: unsupported keys in savez. +* `#19559 `__: ENH: Add annotations for ``__path__`` and ``PytestTester`` +* `#19560 `__: TST: Bump the GitHub actions python 3.10 version +* `#19561 `__: DOC: Remove explicit parameter sparse=False in meshgrid() indexing... +* `#19563 `__: MAINT: Bump hypothesis from 6.14.3 to 6.14.4 +* `#19564 `__: TST: Add "Scaled float" custom DType for testng +* `#19565 `__: DOC: Fix sphinx warnings in c-info.beyond-basics.rst +* `#19566 `__: DOC: Remove ``dot`` docstring in numpy/core/_add_newdocs.py +* `#19567 `__: DOC: Fix Unknown section warning when building docs +* `#19568 `__: BUG: Seed random state in test_vonmises_large_kappa_range. +* `#19571 `__: MAINT: Refactor UFunc core to use NEP 43 style dispatching +* `#19572 `__: MAINT: Cleanup unused function _move_axis_to_0 +* `#19576 `__: MAINT: Make Python3.8 the default for CI testing. +* `#19578 `__: TST: Add basic tests for custom DType (scaled float) ufuncs +* `#19580 `__: ENH: Add basic promoter capability to ufunc dispatching +* `#19582 `__: BLD: load extra flags when checking for libflame +* `#19587 `__: MAINT: Refactor DType slots into an opaque, allocated struct +* `#19590 `__: DOC Fix sphinx warnings related to scope of c:macro. +* `#19593 `__: DOC,MAINT: Update wording surrounding ``fname`` parameter for loadtxt/genfromtxt +* `#19595 `__: MAINT: Bump sphinx from 4.1.1 to 4.1.2 +* `#19596 `__: MAINT: Bump hypothesis from 6.14.4 to 6.14.5 +* `#19598 `__: PERF: Speed-up common case of loadtxt()ing non-hex floats. +* `#19599 `__: PERF: Avoid using ``@recursive``. +* `#19600 `__: BUG: Fix bad write in masked iterator output copy paths +* `#19601 `__: PERF: Speedup comments handling in loadtxt. +* `#19605 `__: DEV: Update default Python in benchmark config. +* `#19607 `__: BUG: Fix NaT handling in the PyArray_CompareFunc for datetime... +* `#19608 `__: PERF: Specialize loadtxt packer for uniform-dtype data. +* `#19609 `__: PERF: In loadtxt, decide once and for all whether decoding is... +* `#19610 `__: PERF: Special-case single-converter in loadtxt. +* `#19612 `__: TST: Bump the python 3.10 test version from beta4 to rc1 +* `#19613 `__: DOC: isclose accepts boolean input +* `#19615 `__: MAINT: Proposal to expire three deprecated functions in numpy.lib.npyio +* `#19616 `__: MAINT: In loadtxt, refactor detection of the number of columns. +* `#19618 `__: MAINT: Optimize loadtxt usecols. +* `#19619 `__: MAINT: Include .coveragerc in source distributions to support... +* `#19620 `__: PERF: Simplify some of loadtxt's standard converters. +* `#19621 `__: BUG: The normal cast-safety for ufunc loops is "no" casting +* `#19622 `__: MAINT: Skip a type check in loadtxt when using user converters. +* `#19627 `__: BUG: Ignore whitespaces while parsing gufunc signatures +* `#19628 `__: TST: avoid distutils.sysconfig in runtests.py +* `#19632 `__: BUG,DEP: Non-default UFunc signature/dtype usage should be deprecated +* `#19633 `__: MAINT: Bump hypothesis from 6.14.5 to 6.14.6 +* `#19638 `__: MAINT: Remove import time compile +* `#19639 `__: MAINT: Update Cython version for Python 3.10. +* `#19640 `__: BUG: Remove logical object ufuncs with bool output +* `#19642 `__: BLD, SIMD: Fix testing extra checks when ``-Werror`` isn't applicable... +* `#19645 `__: DOC: Reorganized the documentation contribution docs +* `#19654 `__: BUG: add base to templated arguments for platlib +* `#19663 `__: NEP: add qualifier for free(), mention ContextVar +* `#19665 `__: MAINT: Drop Python3.7 from supported versions. +* `#19667 `__: ENH: Add annotations for ``np.lib.npyio`` +* `#19672 `__: BLD: update circleCI docker image +* `#19678 `__: REL: Update main after 1.21.2 release. +* `#19680 `__: ENH: Allow ``np.fromregex`` to accept ``os.PathLike`` implementations +* `#19681 `__: MAINT: Update wheel requirement from <0.36.3 to <0.37.1 +* `#19682 `__: MAINT: Bump hypothesis from 6.14.6 to 6.14.7 +* `#19683 `__: ENH: Add annotations for ``np.lib.stride_tricks`` +* `#19686 `__: ENH: Add spaces after punctuation in dtype repr/str. +* `#19692 `__: DOC: Fix trivial doc typo. +* `#19693 `__: MAINT: In loadtxt, inline read_data. +* `#19695 `__: DOC: Fix typo in ``unwrap`` docstring. +* `#19698 `__: DOC: fix typo in example +* `#19702 `__: MAINT: Replace deprecated unittest aliases +* `#19713 `__: MAINT: Replace numpy custom generation engine by raw C++ +* `#19714 `__: MAINT: Remove redundant Python2 float/int conversions +* `#19715 `__: BUG: Casting ``bool_`` to float16 +* `#19725 `__: MAINT: Use a contextmanager to ensure loadtxt closes the input... +* `#19727 `__: DOC: fix basics.creation.rst to address issue 19726 +* `#19730 `__: BUG: Fix reference leak of capi_tmp in f2py/cb_rules.py +* `#19731 `__: BUG: fix time cast-safety for ``factor*unit`` e.g. in ``10**6*ms``... +* `#19732 `__: MAINT: Spelling fixes in documentation +* `#19733 `__: DOC: add citation file for GitHub support +* `#19736 `__: BUG: Fix passing a MaskedArray instance to ``MaskedArray.__setitem__`` +* `#19738 `__: MAINT: Bump hypothesis from 6.14.7 to 6.15.0 +* `#19739 `__: NEP: Update NEP 47: Adopting the array API standard +* `#19742 `__: MAINT: Remove redundant test. +* `#19743 `__: MAINT: Avoid use of confusing compat aliases. +* `#19747 `__: MAINT: Update README.md with badges +* `#19754 `__: ENH: Add clang-format file +* `#19758 `__: MAINT: Remove redundant semicolon +* `#19764 `__: BUG: np.around fails when using doctest +* `#19766 `__: BUG: Remove np.around's footnote [2] +* `#19775 `__: MAINT,DOC: Readability improvements and cleanup for f2py +* `#19776 `__: DOC: Add explanation of a sparse mesh grid +* `#19781 `__: MAINT: refactor "for ... in range(len(" statements +* `#19784 `__: MAINT: Remove typing code-paths specific to Python 3.7 +* `#19789 `__: MAINT: Bump hypothesis from 6.15.0 to 6.17.3 +* `#19791 `__: DOC: Created an explanation document for copies and views +* `#19799 `__: TST: Drop typing-extensions from test_requirements.txt +* `#19800 `__: ENH: Add entry point for Array API implementation +* `#19802 `__: STY: Use the new PEP 457 positional-only syntax for typing +* `#19803 `__: ENH: Add ``is_integer`` to ``np.floating`` & ``np.integer`` +* `#19805 `__: ENH: Symbolic solver for dimension specifications. +* `#19809 `__: MAINT: Fix compiler warnings generated by convert_datatype.h. +* `#19810 `__: MAINT: Minor include rationalizations. +* `#19811 `__: DEP: Deprecate quote_args (from numpy.distutils.misc_util) +* `#19813 `__: DOC: Fix import of default_rng +* `#19814 `__: ENH: Replaced markdown issue templates with issue forms +* `#19815 `__: MAINT: revise OSError aliases (IOError, EnvironmentError) +* `#19817 `__: ENH: Use custom file-like protocols instead of ``typing.IO`` +* `#19818 `__: MAINT: fix unhashable instance and potential exception identified... +* `#19819 `__: MAINT: mark _version.py as generated +* `#19821 `__: BUG: Fixed an issue wherein certain ``nan`` functions could... +* `#19824 `__: MAINT: Small cleanups of includes in *.c files. +* `#19826 `__: MAINT: Standardize guards in numpy/core/include +* `#19827 `__: MAINT: Standardize guards in numpy/core/src/common. +* `#19829 `__: MAINT: Standardize guards in numpy/core/src/multiarray. +* `#19837 `__: MAINT: Bump hypothesis from 6.17.3 to 6.18.0 +* `#19838 `__: MAINT: Bump pytest from 6.2.4 to 6.2.5 +* `#19843 `__: TST: Fix/Improve cast nonstandard bool to numeric test +* `#19844 `__: DOC: Added missing C-API functions +* `#19845 `__: TST: Make nanfunc test ignore overflow instead of xfailing test +* `#19846 `__: MAINT: Update testing to 3.10rc2 +* `#19849 `__: DOC: Fix sentence casing in page titles +* `#19850 `__: Replace posix specific ssize_t with py_ssize_t to compile on... +* `#19854 `__: BUG: Fixed an issue wherein ``var`` would raise for 0d object arrays +* `#19856 `__: MAINT: Mark type-check-only ufunc subclasses as ufunc aliases... +* `#19857 `__: MAINT, ENH: Refactor percentile and quantile methods +* `#19862 `__: DOC: Add BRANCH_WALKTHROUGH +* `#19863 `__: BUG: Fix ``nanpercentile`` ignoring the dtype of all-nan arrays +* `#19864 `__: DOC: Update RELEASE_WALKTHROUGH +* `#19865 `__: DOC: Moved NumPy Internals to Under-the-hood documentation for... +* `#19867 `__: MAINT: Bump hypothesis from 6.18.0 to 6.21.1 +* `#19868 `__: MAINT: Bump sphinx from 4.1.2 to 4.2.0 +* `#19869 `__: BUG: ensure np.median does not drop subclass for NaN result. +* `#19870 `__: DOC: Small fixups for the release walkthrough +* `#19874 `__: DOC: Fix typo in upcoming changes filename +* `#19879 `__: ENH: Add ``__class_getitem__`` to ``ndarray``, ``dtype`` and ``number`` +* `#19882 `__: MAINT: Use SHA-256 instead of SHA-1 +* `#19883 `__: DOC: Fix the reported module names of objects in the ``numpy.typing``... +* `#19884 `__: TST: Make this sysconfig handling a bit more portable +* `#19887 `__: ENH: Add annotations for ``np.linalg`` +* `#19888 `__: BUG: core: Fix *_like strides for str and bytes dtype. +* `#19890 `__: DOC: Added hyperlink on numpy logo in README.md +* `#19893 `__: MAINT,DOC: f2py restructring +* `#19894 `__: ENH: Add a typing protocol for representing nested sequences +* `#19899 `__: DOC: replace return type in np.ma.* docstring +* `#19900 `__: DOC:Fixed refguide errors for basics.creation.rst +* `#19902 `__: BUG,DOC: Ignore upcoming_changes from refguide +* `#19903 `__: DOC: Fixed refguide errors for basics.broadcasting.rst +* `#19905 `__: DOC: fix docstring formatting of polynomial fit method return... +* `#19907 `__: MAINT: Bump hypothesis from 6.21.1 to 6.21.6 +* `#19908 `__: BUG: Check whether an error is already set for invalid casting +* `#19909 `__: MAINT: Re-export ``LinAlgError`` to the ``np.linalg.linalg`` stubs +* `#19911 `__: DOC: Typos found by codespell +* `#19913 `__: MAINT: Fix LGTM.com error: Unmatchable caret in regular expression +* `#19914 `__: MAINT: Fix LGTM.com warning in nditer_imp.h +* `#19915 `__: ENH: Add annotations for ``np.char`` +* `#19916 `__: MAINT: Repair ``make_lite.py`` +* `#19917 `__: ENH: Add annotations for ``np.lib.arraysetops`` +* `#19918 `__: MAINT: Override the modules of ``np.char`` and ``np.rec`` functions +* `#19919 `__: ENH: Create an experimental export of the new DType API +* `#19920 `__: DOC: Fix typos in NEPs, found by codespell +* `#19921 `__: DEP: Use ``delimiter`` rather than ``delimitor`` as kwarg in mrecords +* `#19925 `__: BUG: ufunc: Fix potential memory leak. +* `#19926 `__: BUG: Resolve Divide by Zero on Apple silicon + test failures +* `#19927 `__: BUG: Only call the get_versions() function once on import +* `#19928 `__: MAINT: lib: Check that the dtype given to fromregex is structured. +* `#19929 `__: duplicate item in see also. +* `#19933 `__: MAINT: random: Use expm1 where appropriate. +* `#19934 `__: BUG: core: Fix memory leak in the C function boundarraymethod_repr. +* `#19936 `__: BUG: Make sure __version__ is defined in setup mode +* `#19937 `__: ENH: Updates to numpy.array_api +* `#19939 `__: MAINT: Fix LGTM.com warning: Constant in conditional expression... +* `#19940 `__: MAINT: Fix LGTM.com warning: Variable ``isrec`` defined multiple... +* `#19942 `__: MAINT: Fix LGTM.com warning: Unreachable code +* `#19943 `__: MAINT: Fix LGTM.com warning: Variable ``f`` defined multiple times +* `#19944 `__: MAINT: Fix LGTM.com warning: Comparison result is always the... +* `#19946 `__: MAINT: Fix LGTM.com warning: Comparison result is always the... +* `#19948 `__: MAINT: Add annotations for three missing ``ndarray`` methods +* `#19949 `__: ENH: Add annotations for ``np.rec`` +* `#19951 `__: MAINT: Fix LGTM.com warning: Comparison is always false because... +* `#19953 `__: ENH: Add annotations to ``np.core.multiarray`` part 4/4 +* `#19957 `__: DOC: Add syntax highlighting, update pronouns +* `#19960 `__: DOC: Minor syntax fix for numpydoc warnings +* `#19961 `__: MAINT: Minor cleanups after merging gh-19805 +* `#19962 `__: DOC: Remove overstated TDD evangelism. +* `#19963 `__: DOC: rename ``np.lib.scimath`` to ``np.emath`` +* `#19965 `__: MAINT: Update funding link in FUNDING.yml +* `#19967 `__: DOC: Update basics.io.genfromtxt.rst +* `#19968 `__: ENH: nagfor from NAG is available on Darwin +* `#19969 `__: MAINT: Misc ``np.array_api`` annotation fixes +* `#19972 `__: MAINT: Bump hypothesis from 6.21.6 to 6.23.0 +* `#19974 `__: BUG: np.tan(np.inf) test failure in Apple silicon +* `#19976 `__: DOC Remove reference to ``PyArray_MultiIter_SIZE`` +* `#19977 `__: MAINT: clang-format for f2py +* `#19978 `__: MAINT: Reduce DepreciationWarnings, use more data API types for... +* `#19979 `__: ENH: Add annotations for ``np.memmap`` +* `#19980 `__: ENH: Add the linalg extension to the array_api submodule +* `#19981 `__: DOC: Deindent some sphinx declarations to avoid warnings. +* `#19983 `__: DOC: Specifically mention the C99 requirement in 'Building from... +* `#19984 `__: MAINT: Configure pytest to ignore array_api warnings. +* `#19986 `__: MAINT: Fix LGTM.com warning: Comparison result is always the... +* `#19987 `__: BUG: Remove double cast to char in favor of PyArray_BYTES +* `#19988 `__: DOC: Update links to online copy of Abramowitz and Stegun. +* `#19992 `__: ENH: nagfor - get_flags_linker_so() on darwin +* `#19995 `__: DOC: for new_order parameter, add alias for 'native' order +* `#19997 `__: STY: Harmonize rules with cb_rules for f2py +* `#19999 `__: DOC: Copy-edit and fix typos. +* `#20000 `__: BUG,DEP: Allow (arg-)partition to accept ``uint64`` indices +* `#20002 `__: MAINT: Introduce various linting and misc fixes to ``numpy.typing`` +* `#20003 `__: BLD: updated mypy version from 0.902 to 0.910 +* `#20004 `__: DOC: Fix typos in the random and f2py documentation. +* `#20006 `__: ENH: Add annotations for ``np.lib.function_base`` part 1 +* `#20007 `__: MAINT: Removed the ``cdoc`` directory +* `#20008 `__: BUG: Fix the ``lib.function_base`` window functions ignoring extended... +* `#20010 `__: MAINT: correct linker flags for NAG Fortran compiler +* `#20015 `__: DOC: np.select: use an example that also shows default value +* `#20016 `__: BUG: Add a warning for user dtypes modifying casts after use +* `#20018 `__: ENH: core: More informative error message for broadcast(*args) +* `#20019 `__: MAINT:redundant 'else' statement with 'for' loop#19077 +* `#20026 `__: MAINT: Test PyPy3.8 +* `#20027 `__: ENH: Add missing parameters to the ``nan`` functions +* `#20029 `__: MAINT: Bump pytz from 2021.1 to 2021.3 +* `#20031 `__: MAINT: Bump hypothesis from 6.23.0 to 6.23.1 +* `#20032 `__: MAINT: Bump pytest-cov from 2.12.1 to 3.0.0 +* `#20034 `__: ENH: Add annotations for ``np.lib.function_base`` part 2/3 +* `#20036 `__: ENH: Add annotations for ``np.lib.function_base`` part 3/3 +* `#20037 `__: MAINT: Fixed an issue wherein ``npt._NestedSequence`` was not a... +* `#20040 `__: TST: Add python 3.10 to the CI +* `#20047 `__: DOC:add an example to show flag writeable cleared upon copy related... +* `#20049 `__: BUG: Correct advance in PCG with emulated int128 +* `#20051 `__: DOC:add-html-reference-to-some-ma-methods +* `#20057 `__: MAINT: LGTM.com warnings +* `#20058 `__: MAINT: update OpenBLAS to 0.3.18 +* `#20059 `__: MAINT: LGTM.com recommendations +* `#20060 `__: MAINT: Remove encoding declarations: ``# -*- coding: utf-8 -*-`` +* `#20061 `__: DOC: Remove references to Python 2 +* `#20063 `__: ENH: Add annotations for ``np.lib.histograms`` +* `#20065 `__: ENH: Add annotations for ``np.lib.polynomial`` +* `#20066 `__: MAINT: A few updates to the array_api +* `#20067 `__: MAINT: Use ``Py_SET_TYPE`` macro instead of assigning to ``Py_TYPE`` +* `#20069 `__: BUG: Add workaround for missing ufunc error propagation +* `#20071 `__: MAINT: Remove unused imports and remove duplicated tests +* `#20076 `__: DOC: Document the dtype comparison operations +* `#20084 `__: MAINT: move "git submodule update" earlier in docker creation +* `#20087 `__: BLD: fix submodule update in gitpod.Dockerfile +* `#20088 `__: BUG: core: result_type(0, np.timedelta64(4)) would seg. fault. +* `#20091 `__: DOC: fix typo in docstring of bitwise_or +* `#20094 `__: BUG: AVX-512F log() overflows +* `#20096 `__: MAINT: Bump hypothesis from 6.23.1 to 6.23.2 +* `#20097 `__: MAINT: Bump pycodestyle from 2.7.0 to 2.8.0 +* `#20102 `__: BLD Uses cibuildwheel for linux + osx wheels [cd build] +* `#20104 `__: MAINT: Update F2PY documentation URL +* `#20105 `__: ENH: Add annotations for ``np.matrix`` +* `#20111 `__: DOC: fix minor typo in comment +* `#20115 `__: DOC: Modify code in absolute beginners tutorial to match image +* `#20116 `__: MAINT: Fix issue with C compiler args containing spaces +* `#20119 `__: DOC: Remove double property ctypes from ndarray +* `#20123 `__: DOC: Add note to iterable docstring about 0d arrays. +* `#20129 `__: ENH: Misc typing improvements to ``np.array_api`` +* `#20130 `__: MAINT: Bump hypothesis from 6.23.2 to 6.23.3 +* `#20134 `__: BUG: fix win32 np.clip slowness +* `#20136 `__: BUG: core: Fix incorrect check of NpyIter_Deallocate return value. +* `#20137 `__: DOC:Reword array has one axis +* `#20139 `__: MAINT,BUG: Fix ``ufunc.at`` to use new ufunc API +* `#20142 `__: MAINT: core: Update the comment about _parse_signature with more... +* `#20146 `__: DOC: Updated docstring for floating point rounding +* `#20149 `__: REL: Update main after 1.21.3 release. +* `#20150 `__: BUG: lib: Fix error raised by insert. +* `#20153 `__: BUG, SIMD: Fix 64-bit/8-bit integer division by a scalar +* `#20154 `__: MAINT: Add breathe to environment.yml +* `#20155 `__: BUG: Distutils patch to allow for 2 as a minor version (!) +* `#20156 `__: DOC: Fixed docstring for parameters 2 and -2 on linalg.cond +* `#20159 `__: BUG: Relax homogeneous signature fallback in type resolution +* `#20162 `__: BUG: fixes for MSVC version checks +* `#20163 `__: ENH: Expose promoters and Common-DType API experimentally +* `#20164 `__: MAINT: Remove useless custom tp_alloc and tp_free on ndarray +* `#20165 `__: ENH: Add annotations for ``np.chararray`` +* `#20166 `__: MAINT, STY: Run clang-format on cpp files and headers. +* `#20170 `__: More informative error for unparsable version +* `#20172 `__: Allow clib callable build flags +* `#20173 `__: TST: Disable test_partial_iteration_cleanup on 32 bit Windows. +* `#20174 `__: TST: xfail ``test_overrides`` when numpy is built with MKL support +* `#20179 `__: BUG: Do not use nonzero fastpath on unaligned arrays +* `#20182 `__: DOC, MAINT: Update build systems for f2py +* `#20183 `__: Thin compatibility layer for C/C++ math header +* `#20184 `__: MAINT: Miscellaneous typing cleanups +* `#20187 `__: BUG,DOC: Resolve a refguide failure for ``ndarray.__class_getitem__`` +* `#20188 `__: MAINT: Bump hypothesis from 6.23.3 to 6.24.0 +* `#20190 `__: BUG: Don't pass /arch:SSE2 to MSVC when targeting x64 +* `#20194 `__: DOC: add release note and move NEP 49 to Final +* `#20195 `__: DOC: Two small changes in array.rst: +* `#20196 `__: Fix minor grammar issues in docs +* `#20197 `__: DOC, MAINT : fixing typo in numpy doc +* `#20199 `__: ENH: Add dtype typing support to ``np.core.numeric`` +* `#20200 `__: MAINT: Only warn for transferred ownership if env variable is... +* `#20201 `__: DEP: Deprecate ``np.MachAr`` +* `#20205 `__: BUG,DOC: Fix ``random.power``'s error description +* `#20206 `__: CI: Add new workflow/action for testing universal intrinsics... +* `#20207 `__: ENH: Add prompt for title in issue forms +* `#20213 `__: DOC: Mention ``nan`` results in ``power`` and ``float_power``. +* `#20214 `__: BUG: fix test c-extension compilation inside a venv +* `#20217 `__: DOC: Add a release note for fully annotating the main numpy namespace +* `#20219 `__: BUG, SIMD: Workaround broadcasting SIMD 64-bit integers on MSVC... +* `#20222 `__: Run rebase on Cygwin CI +* `#20224 `__: BUG: Fix shadowed reference of ``dtype`` in type stubs +* `#20228 `__: MAINT: Better error message from histogram2d +* `#20230 `__: ENH: Add annotations for ``np.ctypeslib`` +* `#20232 `__: CI: Add new workflow for Intel SDE +* `#20234 `__: MAINT: Update vs2017 to vs2019. +* `#20235 `__: DOC: fix typo in example, put the return statement inside the... +* `#20237 `__: BUG: ``VOID_nonzero`` could sometimes mutate alignment flag +* `#20238 `__: BUG: Fix environment checking logic for ``NUMPY_WARN_IF_NO_MEM_POLICY`` +* `#20242 `__: DOC: centralized min-max documentation +* `#20243 `__: DOC: Fixes wording for fmod and remainder functions. +* `#20255 `__: DOC: fix missing link in "What is NumPy?" to broadcasting +* `#20256 `__: The module name in the reshape section of the absolute_beginners.html... +* `#20261 `__: [DOC] Fix math block in hermmulx, lagmulx +* `#20267 `__: Adding Examples to numpy.roll() +* `#20268 `__: MAINT: remove Dependabot +* `#20269 `__: MAINT: Bump hypothesis from 6.24.0 to 6.24.1 +* `#20270 `__: BUG: Fix headers for universal2 builds +* `#20271 `__: TST: Add a test for device property +* `#20274 `__: TST: Some fixes & refactoring around glibc-dependent skips in... +* `#20279 `__: ENH: Add annotations for ``np.fft`` +* `#20281 `__: DOC: Correct grammar in isfinite docstring +* `#20282 `__: MAINT: Fix runtests.py overriding $PYTHONPATH environment variable +* `#20283 `__: MAINT Fix typo for event name in wheels.yml +* `#20284 `__: BUG: Fix duplication of names in 'numpy.__all__'. +* `#20287 `__: TST, MAINT: module name excluded in typing tests +* `#20290 `__: DOC: Do not leave space between directive name and double colon. +* `#20292 `__: SIMD: replace raw SIMD of ceil with universal intrinsics +* `#20299 `__: BLD: in conda, pin setuptools to a known working version +* `#20303 `__: BUG: Fix requirement that user DTypes had to be heaptypes +* `#20307 `__: REL: Update main after 1.21.4 release. +* `#20308 `__: MAINT: Add ``IS_PYSTON`` to ``np.testing.__all__`` +* `#20309 `__: MAINT: Add annotations for a missing ``percentile`` interpolation... +* `#20310 `__: BUG: Fix float16 einsum fastpaths using wrong tempvar +* `#20314 `__: BUG: Get full precision for 32 bit floating point random values. +* `#20315 `__: MAINT: Remove Python <3.8 support from C +* `#20318 `__: MAINT: Remove codeql-analysis.yml. +* `#20325 `__: DOC: Remove non-existent quantile ``interpolation="inclusive"``... +* `#20327 `__: BUG,DEP: Fixup quantile/percentile and rename interpolation->method +* `#20331 `__: MAINT: Update quantile default lerp method +* `#20333 `__: DEP: remove code for supporting GCC <4 in Mingw32CCompiler +* `#20334 `__: MAINT: Rename commit trigger to "wheel build" for building wheels +* `#20342 `__: CI: Bump azure MacOS version to macOS-1015 +* `#20343 `__: ENH: add a 'version' field to PyDataMem_Handler +* `#20344 `__: BLD: do not position 'cxx=-std=c++11' as a default compiler flag +* `#20345 `__: ENH: Avoid re-encapsulation of the default handler +* `#20347 `__: MAINT: Do not forward ``__(deep)copy__`` calls of ``_GenericAlias``... +* `#20350 `__: DOC: random: Fix a mistake in the zipf example. +* `#20352 `__: ENH: Prefix log messages with their levels +* `#20353 `__: BUG, DIST: Print os error message when the executable not exist +* `#20354 `__: BLD: Verify the ability to compile C++ sources before initiating... +* `#20360 `__: BUG: Revert from ``long double`` changes, and force ``npymath`` to... +* `#20361 `__: MAINT: Update SVML sources to prevent an executable stack +* `#20364 `__: BUG: Relax unary ufunc (sqrt, etc.) stride assert +* `#20365 `__: BUG: Fix failure to create aligned, empty structured dtype +* `#20366 `__: MAINT,TST: Avoid small positive integers in refcount test +* `#20367 `__: ENH, SIMD: add new universal intrinsics for trunc +* `#20369 `__: MAINT: Fix newlines in diagnostics output of numpy.f2py. +* `#20373 `__: MAINT: Prepare for branching maintenance/1.22.x +* `#20379 `__: DOC: Fix formatting of a code example in ``numpy.random.Generator.multivariate_normal()``... +* `#20386 `__: REV: Add MaskedArray creation from non nd-array back in +* `#20402 `__: BLD: Fix Macos Builds [wheel build] +* `#20410 `__: BUG, SIMD: Fix ``exp`` FP stack overflow when ``AVX512_SKX`` is enabled +* `#20411 `__: ENH: provide a convenience function to replace npy_load_module... +* `#20415 `__: CI: CircleCI: Install numpy after processing doc_requirements.txt +* `#20419 `__: MAINT: import setuptools before distutils in one np.random test +* `#20420 `__: BUG: Clear errors correctly in F2PY conversions +* `#20429 `__: MAINT: Fix longdouble precision check in test_umath.py +* `#20430 `__: MAINT: Fix METH_NOARGS function signatures +* `#20434 `__: REL: Prepare for the NumPy 1.22.0r1 release. +* `#20436 `__: BUG: Fix an incorrect protocol used in ``np.lib.shape_base`` +* `#20473 `__: BUG: Fix two overload-related problems +* `#20474 `__: TST: remove obsolete TestF77Mismatch +* `#20475 `__: MAINT: Update the required setuptools version. +* `#20476 `__: BUG: Restore support for i386 and PowerPC (OS X) +* `#20487 `__: MAINT: update wheel to version that supports python3.10 +* `#20496 `__: TST: use pypy3.8-v7.3.7 final versions +* `#20502 `__: BUG: Fix the .T attribute in the array_api namespace +* `#20503 `__: BUG: Protect divide by 0 in multinomial distribution. +* `#20535 `__: BUG: Fix reduce promotion with out argument +* `#20538 `__: BUG: Fix handling of the dtype parameter to numpy.array_api.prod() +* `#20539 `__: PERF: Fix performance bug in dispatching cache +* `#20541 `__: REL: Prepare for NumPy 1.22.0rc2 release. +* `#20548 `__: REV: Revert adding a default ufunc promoter +* `#20576 `__: BUG: Fix small issues found using valgrind +* `#20577 `__: BUG: Fix sorting of int8/int16 +* `#20578 `__: ENH: Add ``__array__`` to the array_api Array object +* `#20579 `__: MAINT: make sure CI stays on VS2019 unless changed explicitly +* `#20585 `__: DOC: Update front page of documentation with Sphinx-Panels +* `#20598 `__: BUG: Fix issues (mainly) found using pytest-leaks +* `#20599 `__: MAINT: Fix two minor typing-related problems +* `#20600 `__: BUG: Fix leaks found using pytest-leaks +* `#20601 `__: MAINT: Check for buffer interface support rather than try/except +* `#20602 `__: BUG: Fix PyInit__umath_linalg type +* `#20605 `__: DEV: add a warningfilter to fix pytest workflow. +* `#20614 `__: TST: Bump mypy: 0.910 -> 0.920 +* `#20617 `__: MAINT: Help boost::python libraries at least not crash +* `#20632 `__: DOC: Document implementation of NEP 43 and experimental new DType... +* `#20649 `__: DOC: Modify SVG to be visible on Chrome +* `#20650 `__: BUG: Support env argument in CCompiler.spawn +* `#20651 `__: BUG: f2py: Simplify creation of an exception message. +* `#20680 `__: TYP,TST: Bump mypy to 0.930 +* `#20681 `__: BUG: Fix setstate logic for empty arrays +* `#20682 `__: ENH: Add ARM Compiler with ARM Performance Library support diff --git a/doc/example.py b/doc/example.py index 560775038652..5e3d79807d8c 100644 --- a/doc/example.py +++ b/doc/example.py @@ -8,8 +8,6 @@ a line by itself, preferably preceded by a blank line. """ -from __future__ import division, absolute_import, print_function - import os # standard library imports first # Do NOT import using *, e.g. from numpy import * @@ -112,9 +110,9 @@ def foo(var1, var2, long_var_name='hi'): use the function. >>> a = [1, 2, 3] - >>> print [x + 3 for x in a] + >>> print([x + 3 for x in a]) [4, 5, 6] - >>> print "a\n\nb" + >>> print("a\n\nb") a b diff --git a/doc/neps/Makefile b/doc/neps/Makefile index 3c023ae9bc9b..4bbe4b42aaa3 100644 --- a/doc/neps/Makefile +++ b/doc/neps/Makefile @@ -2,20 +2,23 @@ # # You can set these variables from the command line. -SPHINXOPTS = -W -SPHINXBUILD = sphinx-build +SPHINXOPTS ?= +SPHINXBUILD ?= LANG=C sphinx-build + +# Internal variables SPHINXPROJ = NumPyEnhancementProposals SOURCEDIR = . BUILDDIR = _build +ALLSPHINXOPTS = -WT --keep-going -n -d $(SPHINXOPTS) # Put it first so that "make" without argument is like "make help". help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(ALLSPHINXOPTS) $(O) .PHONY: help Makefile index index: - python tools/build_index.py + python3 tools/build_index.py # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). diff --git a/doc/neps/_static/casting_flow.svg b/doc/neps/_static/casting_flow.svg new file mode 100644 index 000000000000..8b4b96477f61 --- /dev/null +++ b/doc/neps/_static/casting_flow.svg @@ -0,0 +1,2212 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + cast not required + only implements S8 + + diff --git a/doc/neps/_static/dtype_hierarchy.svg b/doc/neps/_static/dtype_hierarchy.svg new file mode 100644 index 000000000000..3bade3d0f2f5 --- /dev/null +++ b/doc/neps/_static/dtype_hierarchy.svg @@ -0,0 +1,935 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + DType + + + Float64 + + + + Int64 + + + + + + + >int64 + + + + <int64 + + + + + + + + >float64 + + + + <float64 + + + Instances + Concrete Types: + Instances + + + datetime64 + + + + + + + + <M8[s] + + + + >M8[ns] + + + + + + + + + Concept: + + DTypeMeta + + DType + + + + + + AbstractDtypes:• type hierarchy• UFunc resolution• may promote + + + + Concrete DTypes:• casting/promotion• UFunc signature + + + + + + DType Instances• Describe data• `arr.dtype` + (Cannot be instantiated) + Concrete Types formleaves of the tree;the inheritance is abstractsimilar to Python's abc.ABC. + + + + + x + + + + + + + Int64 + + + + Float64 + + + + Inexact + + + + Numeric + + + + DType + + + + Floating + + + + Integral + + Abstract Types (Hierarchy): + + + datetime64 + + + + diff --git a/doc/neps/_static/nep-0000.png b/doc/neps/_static/nep-0000.png index 51eb2b25826c..0fc8176d242e 100644 Binary files a/doc/neps/_static/nep-0000.png and b/doc/neps/_static/nep-0000.png differ diff --git a/doc/neps/_static/nep-0040_dtype-hierarchy.png b/doc/neps/_static/nep-0040_dtype-hierarchy.png new file mode 100644 index 000000000000..6c45758b1615 Binary files /dev/null and b/doc/neps/_static/nep-0040_dtype-hierarchy.png differ diff --git a/doc/neps/_static/nep-0041-mindmap.svg b/doc/neps/_static/nep-0041-mindmap.svg new file mode 100644 index 000000000000..2b396f385a0a --- /dev/null +++ b/doc/neps/_static/nep-0041-mindmap.svg @@ -0,0 +1,3640 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Phase I + + diff --git a/doc/neps/_static/nep-0041-type-sketch-no-fonts.svg b/doc/neps/_static/nep-0041-type-sketch-no-fonts.svg new file mode 100644 index 000000000000..3250396c530a --- /dev/null +++ b/doc/neps/_static/nep-0041-type-sketch-no-fonts.svg @@ -0,0 +1,1110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/neps/_static/nep-0041-type-sketch.svg b/doc/neps/_static/nep-0041-type-sketch.svg new file mode 100644 index 000000000000..9e597db9d9b2 --- /dev/null +++ b/doc/neps/_static/nep-0041-type-sketch.svg @@ -0,0 +1,523 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + Value Storage + Parameters andStorage options + Value Space andBehaviour + type + instance + + ABC + instance + + type + + DType + + + base dtype + element + + dtype + + element + + dtype + + Python type + + Python typewith ABC + NEP 41 Proposal + Alternative + + + diff --git a/doc/neps/_static/nep-0047-casting-rules-lattice.png b/doc/neps/_static/nep-0047-casting-rules-lattice.png new file mode 100644 index 000000000000..669d3047683d Binary files /dev/null and b/doc/neps/_static/nep-0047-casting-rules-lattice.png differ diff --git a/doc/neps/_static/nep-0047-library-dependencies.png b/doc/neps/_static/nep-0047-library-dependencies.png new file mode 100644 index 000000000000..4eab600a5382 Binary files /dev/null and b/doc/neps/_static/nep-0047-library-dependencies.png differ diff --git a/doc/neps/_static/nep-0047-scope-of-array-API.png b/doc/neps/_static/nep-0047-scope-of-array-API.png new file mode 100644 index 000000000000..55253288c3a2 Binary files /dev/null and b/doc/neps/_static/nep-0047-scope-of-array-API.png differ diff --git a/doc/neps/_static/nep0013_image1.png b/doc/neps/_static/nep0013_image1.png new file mode 100644 index 000000000000..e1b35b738324 Binary files /dev/null and b/doc/neps/_static/nep0013_image1.png differ diff --git a/doc/neps/_static/nep0013_image2.png b/doc/neps/_static/nep0013_image2.png new file mode 100644 index 000000000000..99f51b2fa54d Binary files /dev/null and b/doc/neps/_static/nep0013_image2.png differ diff --git a/doc/neps/_static/nep0013_image3.png b/doc/neps/_static/nep0013_image3.png new file mode 100644 index 000000000000..87a354ad1093 Binary files /dev/null and b/doc/neps/_static/nep0013_image3.png differ diff --git a/doc/neps/_static/nep43-sketch-with-text.svg b/doc/neps/_static/nep43-sketch-with-text.svg new file mode 100644 index 000000000000..212cfe89cc5b --- /dev/null +++ b/doc/neps/_static/nep43-sketch-with-text.svg @@ -0,0 +1,1304 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + Loopdescriptors + + ResolverInput + + + + + + + + + + + + + >U5 + <U8 + + set descriptorsfor inner-loop… + <U5 + <U8 + <U13 + + User Input + + + + + + Input/OutputOperands + + + + + + + + DType classesof the ArrayMethod + + + + + + Promotion (if necessary) + + If provided + + + + + + + + >U5 + S8 + >U5 + <S8 + Unicode + Unicode + Unicode + If not provided + Cast descriptorsto Loop DTypes + <U13 + + + + + + + + Registered + + resolve_descriptors + + + Perform operation with these descriptors(setup, inner-loop function, teardown) + NumPy + Registered or default + ArrayMethod + + Casting, Result Allocation and Outer Iterationdone by UFunc Machinery (within ArrayMethod) + Promoter + + Inputs + Output + … including correctoutput descriptor + + + ArrayMethod lookup + + diff --git a/doc/neps/_static/nep43-sketch.svg b/doc/neps/_static/nep43-sketch.svg new file mode 100644 index 000000000000..372c0ee46fc0 --- /dev/null +++ b/doc/neps/_static/nep43-sketch.svg @@ -0,0 +1,3009 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/neps/conf.py b/doc/neps/conf.py index aa11d37b3cfb..68805e50faf0 100644 --- a/doc/neps/conf.py +++ b/doc/neps/conf.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # NumPy Enhancement Proposals documentation build configuration file, created by # sphinx-quickstart on Mon Dec 11 12:45:09 2017. @@ -30,8 +29,10 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = ['sphinx.ext.imgmath', - 'sphinx.ext.graphviz'] +extensions = [ + 'sphinx.ext.imgmath', + 'sphinx.ext.intersphinx', +] # Add any paths that contain templates here, relative to this directory. templates_path = ['../source/_templates/'] @@ -43,11 +44,11 @@ source_suffix = '.rst' # The master toctree document. -master_doc = 'index' +master_doc = 'content' # General information about the project. project = u'NumPy Enhancement Proposals' -copyright = u'2017, NumPy Developers' +copyright = u'2017-2018, NumPy Developers' author = u'NumPy Developers' # The version info for the project you're documenting, acts as replacement for @@ -80,69 +81,21 @@ ## -- Options for HTML output ---------------------------------------------- # -## The theme to use for HTML and HTML Help pages. See the documentation for -## a list of builtin themes. -## -#html_theme = 'alabaster' -# -## Theme options are theme-specific and customize the look and feel of a theme -## further. For a list of options available for each theme, see the -## documentation. -## -## html_theme_options = {} -# -## Add any paths that contain custom static files (such as style sheets) here, -## relative to this directory. They are copied after the builtin static files, -## so a file named "default.css" will overwrite the builtin "default.css". -#html_static_path = ['_static'] -# -## Custom sidebar templates, must be a dictionary that maps document names -## to template names. -## -## This is required for the alabaster theme -## refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars -#html_sidebars = { -# '**': [ -# 'relations.html', # needs 'show_related': True theme option to display -# 'searchbox.html', -# ] -#} - -## ----------------------------------------------------------------------------- -# HTML output -# ----------------------------------------------------------------------------- -themedir = os.path.join(os.pardir, 'scipy-sphinx-theme', '_theme') -if not os.path.isdir(themedir): - raise RuntimeError("Get the scipy-sphinx-theme first, " - "via git submodule init && git submodule update") - -html_theme = 'scipy' -html_theme_path = [themedir] - -#if 'scipyorg' in tags: -if True: - # Build for the scipy.org website - html_theme_options = { - "edit_link": True, - "sidebar": "right", - "scipy_org_logo": True, - "rootlinks": [("http://scipy.org/", "Scipy.org"), - ("http://docs.scipy.org/", "Docs")] - } -else: - # Default build - html_theme_options = { - "edit_link": False, - "sidebar": "left", - "scipy_org_logo": False, - "rootlinks": [] - } - html_sidebars = {'index': 'indexsidebar.html'} - -#html_additional_pages = { -# 'index': 'indexcontent.html', -#} +html_theme = 'pydata_sphinx_theme' + +html_logo = '../source/_static/numpylogo.svg' + +html_theme_options = { + "github_url": "https://github.com/numpy/numpy", + "twitter_url": "https://twitter.com/numpy_team", + "external_links": [ + {"name": "Wishlist", + "url": "https://github.com/numpy/numpy/issues?q=is%3Aopen+is%3Aissue+label%3A%2223+-+Wish+List%22", + }, + ], + "show_prev_next": False, +} html_title = "%s" % (project) html_static_path = ['../source/_static'] @@ -219,3 +172,14 @@ author, 'NumPyEnhancementProposals', 'One line description of project.', 'Miscellaneous'), ] + +# ----------------------------------------------------------------------------- +# Intersphinx configuration +# ----------------------------------------------------------------------------- +intersphinx_mapping = { + 'python': ('https://docs.python.org/dev', None), + 'numpy': ('https://numpy.org/devdocs', None), + 'scipy': ('https://docs.scipy.org/doc/scipy/reference', None), + 'matplotlib': ('https://matplotlib.org', None) +} + diff --git a/doc/neps/content.rst b/doc/neps/content.rst new file mode 100644 index 000000000000..f5d8347c4a0c --- /dev/null +++ b/doc/neps/content.rst @@ -0,0 +1,25 @@ +===================================== +Roadmap & NumPy Enhancement Proposals +===================================== + +This page provides an overview of development priorities for NumPy. +Specifically, it contains a roadmap with a higher-level overview, as +well as NumPy Enhancement Proposals (NEPs)—suggested changes +to the library—in various stages of discussion or completion (see `NEP +0 `__). + +Roadmap +------- +.. toctree:: + :maxdepth: 1 + + Index + The Scope of NumPy + Current roadmap + Wishlist (opens new window) |wishlist_link| + +.. |wishlist_link| raw:: html + + WishList + + diff --git a/doc/neps/index.rst.tmpl b/doc/neps/index.rst.tmpl index 6c988014fa6a..0299f8671f10 100644 --- a/doc/neps/index.rst.tmpl +++ b/doc/neps/index.rst.tmpl @@ -1,12 +1,21 @@ -=========================== -NumPy Enhancement Proposals -=========================== - -NumPy Enhancement Proposals (NEPs) describe proposed changes to NumPy. -NEPs are modeled on Python Enhancement Proposals (PEPs), and are typically -written up when large changes to NumPy are proposed. +===================================== +Roadmap & NumPy Enhancement Proposals +===================================== + +This page provides an overview of development priorities for NumPy. +Specifically, it contains a roadmap with a higher-level overview, as +well as NumPy Enhancement Proposals (NEPs)—suggested changes +to the library—in various stages of discussion or completion (see `NEP +0 `__). + +Roadmap +------- +.. toctree:: + :maxdepth: 1 -This page provides an overview of all NEPs. + The Scope of NumPy + Current roadmap + Wish list Meta-NEPs (NEPs about NEPs or Processes) ---------------------------------------- @@ -14,20 +23,36 @@ Meta-NEPs (NEPs about NEPs or Processes) .. toctree:: :maxdepth: 1 -{% for nep, tags in neps.items() if tags['Type'] == 'Process' %} - NEP {{ nep }} — {{ tags['Title'] }} <{{ tags['Filename'] }}> +{% for nep, tags in neps.items() if tags['Status'] == 'Active' %} + {{ tags['Title'] }} <{{ tags['Filename'] }}> {% endfor %} nep-template -Accepted NEPs, implementation in progress ------------------------------------------ + +{% if has_provisional %} + +Provisional NEPs (provisionally accepted; interface may change) +--------------------------------------------------------------- + +.. toctree:: + :maxdepth: 1 + +{% for nep, tags in neps.items() if tags['Status'] == 'Provisional' %} + {{ tags['Title'] }} <{{ tags['Filename'] }}> +{% endfor %} + +{% endif %} + + +Accepted NEPs (implementation in progress) +------------------------------------------ .. toctree:: :maxdepth: 1 {% for nep, tags in neps.items() if tags['Status'] == 'Accepted' %} - NEP {{ nep }} — {{ tags['Title'] }} <{{ tags['Filename'] }}> + {{ tags['Title'] }} <{{ tags['Filename'] }}> {% endfor %} @@ -38,37 +63,38 @@ Open NEPs (under consideration) :maxdepth: 1 {% for nep, tags in neps.items() if tags['Status'] == 'Draft' %} - NEP {{ nep }} — {{ tags['Title'] }} <{{ tags['Filename'] }}> + {{ tags['Title'] }} <{{ tags['Filename'] }}> {% endfor %} -Implemented NEPs +Finished NEPs ---------------- .. toctree:: :maxdepth: 1 {% for nep, tags in neps.items() if tags['Status'] == 'Final' %} - NEP {{ nep }} — {{ tags['Title'] }} <{{ tags['Filename'] }}> + {{ tags['Title'] }} <{{ tags['Filename'] }}> {% endfor %} -Deferred NEPs -------------- +Deferred and Superseded NEPs +---------------------------- .. toctree:: :maxdepth: 1 -{% for nep, tags in neps.items() if tags['Status'] == 'Deferred' %} - NEP {{ nep }} — {{ tags['Title'] }} <{{ tags['Filename'] }}> +{% for nep, tags in neps.items() if tags['Status'] in ('Deferred', 'Superseded') %} + {{ tags['Title'] }} <{{ tags['Filename'] }}> {% endfor %} -Rejected NEPs -------------- +Rejected and Withdrawn NEPs +--------------------------- .. toctree:: :maxdepth: 1 -{% for nep, tags in neps.items() if tags['Status'] == 'Rejected' %} - NEP {{ nep }} — {{ tags['Title'] }} <{{ tags['Filename'] }}> +{% for nep, tags in neps.items() if tags['Status'] in ('Rejected', 'Withdrawn') %} + {{ tags['Title'] }} <{{ tags['Filename'] }}> {% endfor %} + diff --git a/doc/neps/nep-0000.rst b/doc/neps/nep-0000.rst index 9c6646db23b7..044edebc79ed 100644 --- a/doc/neps/nep-0000.rst +++ b/doc/neps/nep-0000.rst @@ -1,6 +1,8 @@ -=================== -Purpose and Process -=================== +.. _NEP00: + +=========================== +NEP 0 — Purpose and process +=========================== :Author: Jarrod Millman :Status: Active @@ -31,12 +33,18 @@ feature proposal [1]_. Types ^^^^^ -There are two kinds of NEP: +There are three kinds of NEPs: 1. A **Standards Track** NEP describes a new feature or implementation for NumPy. -2. A **Process** NEP describes a process surrounding NumPy, or +2. An **Informational** NEP describes a NumPy design issue, or provides + general guidelines or information to the Python community, but does not + propose a new feature. Informational NEPs do not necessarily represent a + NumPy community consensus or recommendation, so users and implementers are + free to ignore Informational NEPs or follow their advice. + +3. A **Process** NEP describes a process surrounding NumPy, or proposes a change to (or an event in) a process. Process NEPs are like Standards Track NEPs but apply to areas other than the NumPy language itself. They may propose an implementation, but not to @@ -69,9 +77,11 @@ request`_ to the ``doc/neps`` directory with the name ``nep-.rst`` where ```` is an appropriately assigned four-digit number (e.g., ``nep-0000.rst``). The draft must use the :doc:`nep-template` file. -Once the PR is in place, the NEP should be announced on the mailing -list for discussion (comments on the PR itself should be restricted to -minor editorial and technical fixes). +Once the PR for the NEP is in place, a post should be made to the +mailing list containing the sections up to "Backward compatibility", +with the purpose of limiting discussion there to usage and impact. +Discussion on the pull request will have a broader scope, also including +details of implementation. At the earliest convenience, the PR should be merged (regardless of whether it is accepted during discussion). Additional PRs may be made @@ -97,21 +107,28 @@ status of NEPs are as follows: All NEPs should be created with the ``Draft`` status. -Normally, a NEP is ``Accepted`` by consensus of all interested -Contributors. To verify that consensus has been reached, the NEP -author or another interested party should make a post on the -numpy-discussion mailing list proposing it for acceptance; if there -are no substantive objections after one week, the NEP can officially -be marked ``Accepted``, and a link to this post should be added to the -NEP for reference. - -In unusual cases, the `NumPy Steering Council`_ may be asked to decide whether -a controversial NEP is ``Accepted``. +Eventually, after discussion, there may be a consensus that the NEP +should be accepted – see the next section for details. At this point +the status becomes ``Accepted``. Once a NEP has been ``Accepted``, the reference implementation must be completed. When the reference implementation is complete and incorporated into the main source code repository, the status will be changed to ``Final``. +To allow gathering of additional design and interface feedback before +committing to long term stability for a language feature or standard library +API, a NEP may also be marked as "Provisional". This is short for +"Provisionally Accepted", and indicates that the proposal has been accepted for +inclusion in the reference implementation, but additional user feedback is +needed before the full design can be considered "Final". Unlike regular +accepted NEPs, provisionally accepted NEPs may still be Rejected or Withdrawn +even after the related changes have been included in a Python release. + +Wherever possible, it is considered preferable to reduce the scope of a +proposal to avoid the need to rely on the "Provisional" status (e.g. by +deferring some features to later NEPs), as this status can lead to version +compatibility challenges in the wider NumPy ecosystem. + A NEP can also be assigned status ``Deferred``. The NEP author or a core developer can assign the NEP this status when no progress is being made on the NEP. @@ -125,7 +142,7 @@ accepted that a competing proposal is a better alternative. When a NEP is ``Accepted``, ``Rejected``, or ``Withdrawn``, the NEP should be updated accordingly. In addition to updating the status field, at the very least the ``Resolution`` header should be added with a link to the relevant -post in the mailing list archives. +thread in the mailing list archives. NEPs can also be ``Superseded`` by a different NEP, rendering the original obsolete. The ``Replaced-By`` and ``Replaces`` headers @@ -135,6 +152,61 @@ Process NEPs may also have a status of ``Active`` if they are never meant to be completed, e.g. NEP 0 (this NEP). +How a NEP becomes Accepted +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A NEP is ``Accepted`` by consensus of all interested contributors. We +need a concrete way to tell whether consensus has been reached. When +you think a NEP is ready to accept, send an email to the +numpy-discussion mailing list with a subject like: + + Proposal to accept NEP #: + +In the body of your email, you should: + +* link to the latest version of the NEP, + +* briefly describe any major points of contention and how they were + resolved, + +* include a sentence like: "If there are no substantive objections + within 7 days from this email, then the NEP will be accepted; see + NEP 0 for more details." + +For an example, see: https://mail.python.org/pipermail/numpy-discussion/2018-June/078345.html + +After you send the email, you should make sure to link to the email +thread from the ``Discussion`` section of the NEP, so that people can +find it later. + +Generally the NEP author will be the one to send this email, but +anyone can do it – the important thing is to make sure that everyone +knows when a NEP is on the verge of acceptance, and give them a final +chance to respond. If there's some special reason to extend this final +comment period beyond 7 days, then that's fine, just say so in the +email. You shouldn't do less than 7 days, because sometimes people are +travelling or similar and need some time to respond. + +In general, the goal is to make sure that the community has consensus, +not provide a rigid policy for people to try to game. When in doubt, +err on the side of asking for more feedback and looking for +opportunities to compromise. + +If the final comment period passes without any substantive objections, +then the NEP can officially be marked ``Accepted``. You should send a +followup email notifying the list (celebratory emoji optional but +encouraged 🎉✨), and then update the NEP by setting its ``:Status:`` +to ``Accepted``, and its ``:Resolution:`` header to a link to your +followup email. + +If there *are* substantive objections, then the NEP remains in +``Draft`` state, discussion continues as normal, and it can be +proposed for acceptance again later once the objections are resolved. + +In unusual cases, the `NumPy Steering Council`_ may be asked to decide +whether a controversial NEP is ``Accepted``. + + Maintenance ^^^^^^^^^^^ @@ -162,7 +234,9 @@ Header Preamble Each NEP must begin with a header preamble. The headers must appear in the following order. Headers marked with ``*`` are -optional. All other headers are required. :: +optional. All other headers are required. + +.. code-block:: rst :Author: <list of authors' real names and optionally, email addresses> :Status: <Draft | Active | Accepted | Deferred | Rejected | @@ -179,10 +253,14 @@ The Author header lists the names, and optionally the email addresses of all the authors of the NEP. The format of the Author header value must be +.. code-block:: rst + Random J. User <address@dom.ain> if the email address is included, and just +.. code-block:: rst + Random J. User if the address is not given. If there are multiple authors, each should be on @@ -200,10 +278,10 @@ References and Footnotes .. [1] This historical record is available by the normal git commands for retrieving older revisions, and can also be browsed on - `GitHub <https://github.com/numpy/numpy/tree/master/doc/neps>`_. + `GitHub <https://github.com/numpy/numpy/tree/main/doc/neps>`_. .. [2] The URL for viewing NEPs on the web is - http://numpy.github.io/neps/. + https://www.numpy.org/neps/. .. _repo: https://github.com/numpy/numpy @@ -220,7 +298,7 @@ References and Footnotes .. _reStructuredTextPrimer: http://www.sphinx-doc.org/en/stable/rest.html -.. _Sphinx: www.sphinx-doc.org/en/stable +.. _Sphinx: http://www.sphinx-doc.org/en/stable/ Copyright diff --git a/doc/neps/nep-0001-npy-format.rst b/doc/neps/nep-0001-npy-format.rst index 2057aed83d4b..fb6754f5c095 100644 --- a/doc/neps/nep-0001-npy-format.rst +++ b/doc/neps/nep-0001-npy-format.rst @@ -1,6 +1,8 @@ -===================================== -A Simple File Format for NumPy Arrays -===================================== +.. _NEP01: + +============================================= +NEP 1 — A simple file format for NumPy arrays +============================================= :Author: Robert Kern <robert.kern@gmail.com> :Status: Final @@ -290,15 +292,15 @@ included in the 1.9.0 release of numpy. Specifically, the file format.py in this directory implements the format as described here. - http://github.com/numpy/numpy/blob/master/numpy/lib/format.py + https://github.com/numpy/numpy/blob/main/numpy/lib/format.py References ---------- -[1] http://docs.python.org/lib/module-pickle.html +[1] https://docs.python.org/library/pickle.html -[2] http://hdf.ncsa.uiuc.edu/products/hdf5/index.html +[2] https://support.hdfgroup.org/HDF5/ Copyright diff --git a/doc/neps/nep-0002-warnfix.rst b/doc/neps/nep-0002-warnfix.rst index 60dc885b24ca..a1138b2f1b83 100644 --- a/doc/neps/nep-0002-warnfix.rst +++ b/doc/neps/nep-0002-warnfix.rst @@ -1,12 +1,16 @@ -========================================================================= -A proposal to build numpy without warning with a big set of warning flags -========================================================================= +.. _NEP02: + +================================================================================= +NEP 2 — A proposal to build numpy without warning with a big set of warning flags +================================================================================= :Author: David Cournapeau :Contact: david@ar.media.kyoto-u.ac.jp :Date: 2008-09-04 :Status: Deferred +.. highlight:: c + Executive summary ================= @@ -21,7 +25,9 @@ Warning flags ============= Each compiler detects a different set of potential errors. The baseline will -be gcc -Wall -W -Wextra. Ideally, a complete set would be nice:: +be gcc -Wall -W -Wextra. Ideally, a complete set would be nice: + +.. code-block:: bash -W -Wall -Wextra -Wstrict-prototypes -Wmissing-prototypes -Waggregate-return -Wcast-align -Wcast-qual -Wnested-externs -Wshadow -Wbad-function-cast @@ -65,9 +71,7 @@ When applied to a variable, one would get:: int foo(int * NPY_UNUSED(dummy)) -expanded to - -:: +expanded to:: int foo(int * __NPY_UNUSED_TAGGEDdummy __COMP_NPY_UNUSED) diff --git a/doc/neps/nep-0003-math_config_clean.rst b/doc/neps/nep-0003-math_config_clean.rst index 5af90743780b..ff5a325fc2a5 100644 --- a/doc/neps/nep-0003-math_config_clean.rst +++ b/doc/neps/nep-0003-math_config_clean.rst @@ -1,6 +1,8 @@ -=========================================================== -Cleaning the math configuration of numpy.core -=========================================================== +.. _NEP03: + +===================================================== +NEP 3 — Cleaning the math configuration of numpy.core +===================================================== :Author: David Cournapeau :Contact: david@ar.media.kyoto-u.ac.jp diff --git a/doc/neps/nep-0004-datetime-proposal3.rst b/doc/neps/nep-0004-datetime-proposal3.rst index 46d8e314bca0..78b139dc5a57 100644 --- a/doc/neps/nep-0004-datetime-proposal3.rst +++ b/doc/neps/nep-0004-datetime-proposal3.rst @@ -1,6 +1,8 @@ -==================================================================== - A (third) proposal for implementing some date/time types in NumPy -==================================================================== +.. _NEP04: + +========================================================================= +NEP 4 — A (third) proposal for implementing some date/time types in NumPy +========================================================================= :Author: Francesc Alted i Abad :Contact: faltet@pytables.com @@ -562,9 +564,9 @@ examples of other derived units, and we find this a bit too overwhelming for this proposal purposes. -.. [1] http://docs.python.org/lib/module-datetime.html -.. [2] http://www.egenix.com/products/python/mxBase/mxDateTime -.. [3] http://en.wikipedia.org/wiki/Unix_time +.. [1] https://docs.python.org/library/datetime.html +.. [2] https://www.egenix.com/products/python/mxBase/mxDateTime +.. [3] https://en.wikipedia.org/wiki/Unix_time .. Local Variables: diff --git a/doc/neps/nep-0005-generalized-ufuncs.rst b/doc/neps/nep-0005-generalized-ufuncs.rst index 54b2b370efdf..43459a555a58 100644 --- a/doc/neps/nep-0005-generalized-ufuncs.rst +++ b/doc/neps/nep-0005-generalized-ufuncs.rst @@ -1,6 +1,8 @@ -=============================== -Generalized Universal Functions -=============================== +.. _NEP05: + +======================================= +NEP 5 — Generalized Universal Functions +======================================= :Status: Final diff --git a/doc/neps/nep-0006-newbugtracker.rst b/doc/neps/nep-0006-newbugtracker.rst index 2b9344ed0dd5..cb13f78828d1 100644 --- a/doc/neps/nep-0006-newbugtracker.rst +++ b/doc/neps/nep-0006-newbugtracker.rst @@ -1,6 +1,8 @@ -=========================================== -Replacing Trac with a different bug tracker -=========================================== +.. _NEP06: + +=================================================== +NEP 6 — Replacing Trac with a different bug tracker +=================================================== :Author: David Cournapeau, Stefan van der Walt :Status: Deferred diff --git a/doc/neps/nep-0007-datetime-proposal.rst b/doc/neps/nep-0007-datetime-proposal.rst index 72d48d244782..8f6c5273713e 100644 --- a/doc/neps/nep-0007-datetime-proposal.rst +++ b/doc/neps/nep-0007-datetime-proposal.rst @@ -1,6 +1,8 @@ -==================================================================== - A proposal for implementing some date/time types in NumPy -==================================================================== +.. _NEP07: + +================================================================== +NEP 7 — A proposal for implementing some date/time types in NumPy +================================================================== :Author: Travis Oliphant :Contact: oliphant@enthought.com @@ -662,9 +664,9 @@ operations mixing business days with other time units will not be allowed. -.. [1] http://docs.python.org/lib/module-datetime.html -.. [2] http://www.egenix.com/products/python/mxBase/mxDateTime -.. [3] http://en.wikipedia.org/wiki/Unix_time +.. [1] https://docs.python.org/library/datetime.html +.. [2] https://www.egenix.com/products/python/mxBase/mxDateTime +.. [3] https://en.wikipedia.org/wiki/Unix_time .. Local Variables: diff --git a/doc/neps/nep-0008-groupby_additions.rst b/doc/neps/nep-0008-groupby_additions.rst index fa02f2f9c4f5..89d454914d96 100644 --- a/doc/neps/nep-0008-groupby_additions.rst +++ b/doc/neps/nep-0008-groupby_additions.rst @@ -1,6 +1,8 @@ -==================================================================== - A proposal for adding groupby functionality to NumPy -==================================================================== +.. _NEP08: + +============================================================= +NEP 8 — A proposal for adding groupby functionality to NumPy +============================================================= :Author: Travis Oliphant :Contact: oliphant@enthought.com diff --git a/doc/neps/nep-0009-structured_array_extensions.rst b/doc/neps/nep-0009-structured_array_extensions.rst index 695d0d516d53..cd6c3f6c380c 100644 --- a/doc/neps/nep-0009-structured_array_extensions.rst +++ b/doc/neps/nep-0009-structured_array_extensions.rst @@ -1,6 +1,8 @@ -=========================== -Structured array extensions -=========================== +.. _NEP09: + +=================================== +NEP 9 — Structured array extensions +=================================== :Status: Deferred diff --git a/doc/neps/nep-0010-new-iterator-ufunc.rst b/doc/neps/nep-0010-new-iterator-ufunc.rst index 7b388a9744e8..67177d30ba31 100644 --- a/doc/neps/nep-0010-new-iterator-ufunc.rst +++ b/doc/neps/nep-0010-new-iterator-ufunc.rst @@ -1,6 +1,8 @@ -===================================== -Optimizing Iterator/UFunc Performance -===================================== +.. _NEP10: + +============================================== +NEP 10 — Optimizing Iterator/UFunc performance +============================================== :Author: Mark Wiebe <mwwiebe@gmail.com> :Content-Type: text/x-rst @@ -8,7 +10,7 @@ Optimizing Iterator/UFunc Performance :Status: Final ***************** -Table of Contents +Table of contents ***************** .. contents:: @@ -391,7 +393,9 @@ The proposed ‘order=’ flags become as follows: ‘K’ a layout equivalent to ‘C’ followed by some permutation of the axes, as close to the layout of the input(s) as possible (“Keep Layout”) === ===================================================================================== -Or as an enum:: +Or as an enum: + +.. code-block:: c /* For specifying array memory layout or iteration order */ typedef enum { @@ -414,7 +418,9 @@ parameter to control the layout of their output(s). The iterator can do automatic casting, and I have created a sequence of progressively more permissive casting rules. Perhaps for 2.0, NumPy -could adopt this enum as its preferred way of dealing with casting.:: +could adopt this enum as its preferred way of dealing with casting. + +.. code-block:: c /* For specifying allowed casting in operations which support it */ typedef enum { @@ -494,7 +500,9 @@ Proposed Iterator Memory Layout The following struct describes the iterator memory. All items are packed together, which means that different values of the flags, -ndim, and niter will produce slightly different layouts. :: +ndim, and niter will produce slightly different layouts. + +.. code-block:: c struct { /* Flags indicate what optimizations have been applied, and @@ -567,12 +575,16 @@ ndim, and niter will produce slightly different layouts. :: intp shape; /* The current coordinate along this axis */ intp coord; - /* The operand and index strides for this axis + /* The operand and index strides for this axis */ intp stride[niter]; - {intp indexstride;} #if (flags&FLAGS_HASINDEX); + #if (flags&FLAGS_HASINDEX) + intp indexstride; + #endif /* The operand pointers and index values for this axis */ char* ptr[niter]; - {intp index;} #if (flags&FLAGS_HASINDEX); + #if (flags&FLAGS_HASINDEX) + intp index; + #endif }[ndim]; }; @@ -708,13 +720,17 @@ Construction and Destruction Returns NULL if there is an error, otherwise returns the allocated iterator. - To make an iterator similar to the old iterator, this should work.:: + To make an iterator similar to the old iterator, this should work. + + .. code-block:: c iter = NpyIter_New(op, NPY_ITER_READWRITE, NPY_CORDER, NPY_NO_CASTING, NULL, 0, NULL); If you want to edit an array with aligned ``double`` code, - but the order doesn't matter, you would use this.:: + but the order doesn't matter, you would use this. + + .. code-block:: c dtype = PyArray_DescrFromType(NPY_DOUBLE); iter = NpyIter_New(op, NPY_ITER_READWRITE | @@ -762,7 +778,9 @@ Construction and Destruction In ``op_axes[j][i]`` is stored either a valid axis of ``op[j]``, or -1 which means ``newaxis``. Within each ``op_axes[j]`` array, axes may not be repeated. The following example is how normal broadcasting - applies to a 3-D array, a 2-D array, a 1-D array and a scalar.:: + applies to a 3-D array, a 2-D array, a 1-D array and a scalar. + + .. code-block:: c npy_intp oa_ndim = 3; /* # iteration axes */ npy_intp op0_axes[] = {0, 1, 2}; /* 3-D operand */ @@ -1137,7 +1155,9 @@ Construction and Destruction If you want to reset both the ``iterindex`` range and the base pointers at the same time, you can do the following to avoid extra buffer copying (be sure to add the return code error checks - when you copy this code).:: + when you copy this code). + + .. code-block:: c /* Set to a trivial empty range */ NpyIter_ResetToIterIndexRange(iter, 0, 0); @@ -1188,7 +1208,9 @@ Construction and Destruction is used as the source for ``baseptrs``, it will point into a small buffer instead of the array and the inner iteration will be invalid. - The pattern for using nested iterators is as follows.:: + The pattern for using nested iterators is as follows: + + .. code-block:: c NpyIter *iter1, *iter1; NpyIter_IterNext_Fn iternext1, iternext2; @@ -1291,7 +1313,7 @@ Construction and Destruction Returns ``NPY_SUCCEED`` or ``NPY_FAIL``. -``int NpyIter_HasInnerLoop(NpyIter *iter`` +``int NpyIter_HasInnerLoop(NpyIter *iter)`` Returns 1 if the iterator handles the inner loop, or 0 if the caller needs to handle it. This is controlled @@ -1410,7 +1432,9 @@ Functions For Iteration non-NULL, the function may be safely called without holding the Python GIL. - The typical looping construct is as follows.:: + The typical looping construct is as follows: + + .. code-block:: c NpyIter_IterNext_Fn iternext = NpyIter_GetIterNext(iter, NULL); char **dataptr = NpyIter_GetDataPtrArray(iter); @@ -1420,7 +1444,9 @@ Functions For Iteration } while(iternext(iter)); When ``NPY_ITER_NO_INNER_ITERATION`` is specified, the typical - inner loop construct is as follows.:: + inner loop construct is as follows: + + .. code-block:: c NpyIter_IterNext_Fn iternext = NpyIter_GetIterNext(iter, NULL); char **dataptr = NpyIter_GetDataPtrArray(iter); @@ -1449,7 +1475,9 @@ Functions For Iteration to become zero when ``iternext()`` returns false, enabling the following loop construct. Note that if you use this construct, you should not pass ``NPY_ITER_GROWINNER`` as a flag, because it - will cause larger sizes under some circumstances.:: + will cause larger sizes under some circumstances: + + .. code-block:: c /* The constructor should have buffersize passed as this value */ #define FIXED_BUFFER_SIZE 1024 @@ -1517,7 +1545,7 @@ Functions For Iteration ``npy_intp *NpyIter_GetIndexPtr(NpyIter *iter)`` This gives back a pointer to the index being tracked, or NULL - if no index is being tracked. It is only useable if one of + if no index is being tracked. It is only usable if one of the flags ``NPY_ITER_C_INDEX`` or ``NPY_ITER_F_INDEX`` were specified during construction. @@ -1569,7 +1597,9 @@ result. If the input is a reference type, this function will fail. To fix this, the code must be changed to specially handle writeable -references, and add ``NPY_ITER_WRITEABLE_REFERENCES`` to the flags.:: +references, and add ``NPY_ITER_WRITEABLE_REFERENCES`` to the flags: + +.. code-block:: c /* NOTE: This code has not been compiled/tested */ PyObject *CopyArray(PyObject *arr, NPY_ORDER order) @@ -1877,8 +1907,8 @@ the new iterator. Here is one of the original functions, for reference, and some random image data.:: - In [5]: rand1 = np.random.random_sample(1080*1920*4).astype(np.float32) - In [6]: rand2 = np.random.random_sample(1080*1920*4).astype(np.float32) + In [5]: rand1 = np.random.random(1080*1920*4).astype(np.float32) + In [6]: rand2 = np.random.random(1080*1920*4).astype(np.float32) In [7]: image1 = rand1.reshape(1080,1920,4).swapaxes(0,1) In [8]: image2 = rand2.reshape(1080,1920,4).swapaxes(0,1) diff --git a/doc/neps/nep-0011-deferred-ufunc-evaluation.rst b/doc/neps/nep-0011-deferred-ufunc-evaluation.rst index 5f5de3518848..fde03437881c 100644 --- a/doc/neps/nep-0011-deferred-ufunc-evaluation.rst +++ b/doc/neps/nep-0011-deferred-ufunc-evaluation.rst @@ -1,6 +1,8 @@ -========================= -Deferred UFunc Evaluation -========================= +.. _NEP11: + +================================== +NEP 11 — Deferred UFunc evaluation +================================== :Author: Mark Wiebe <mwwiebe@gmail.com> :Content-Type: text/x-rst @@ -39,11 +41,15 @@ For an idea of how to get this kind of behavior in NumPy without changing the Python code, consider the C++ technique of expression templates. These can be used to quite arbitrarily rearrange expressions using -vectors or other data structures, example,:: +vectors or other data structures, example: + +.. code-block:: cpp A = B + C + D; -can be transformed into something equivalent to:: +can be transformed into something equivalent to: + +.. code-block:: cpp for(i = 0; i < A.size; ++i) { A[i] = B[i] + C[i] + D[i]; diff --git a/doc/neps/nep-0012-missing-data.rst b/doc/neps/nep-0012-missing-data.rst index 1553339f4ccd..4775ea18bc1e 100644 --- a/doc/neps/nep-0012-missing-data.rst +++ b/doc/neps/nep-0012-missing-data.rst @@ -1,10 +1,12 @@ -=================================== -Missing Data Functionality in NumPy -=================================== +.. _NEP12: + +============================================ +NEP 12 — Missing data functionality in NumPy +============================================ :Author: Mark Wiebe <mwwiebe@gmail.com> :Copyright: Copyright 2011 by Enthought, Inc -:License: CC By-SA 3.0 (http://creativecommons.org/licenses/by-sa/3.0/) +:License: CC By-SA 3.0 (https://creativecommons.org/licenses/by-sa/3.0/) :Date: 2011-06-23 :Status: Deferred @@ -224,7 +226,7 @@ but with semantics reflecting its status as a missing value. In particular, trying to treat it as a boolean will raise an exception, and comparisons with it will produce numpy.NA instead of True or False. These basics are adopted from the behavior of the NA value in the R project. To dig -deeper into the ideas, http://en.wikipedia.org/wiki/Ternary_logic#Kleene_logic +deeper into the ideas, https://en.wikipedia.org/wiki/Ternary_logic#Kleene_logic provides a starting point. For example,:: @@ -313,7 +315,7 @@ The following works in the current draft implementation:: For floating point numbers, Inf and NaN are separate concepts from missing values. If a division by zero occurs in an array with default missing value support, an unmasked Inf or NaN will be produced. To -mask those values, a further 'a[np.logical_not(a.isfinite(a)] = np.NA' +mask those values, a further 'a[np.logical_not(a.isfinite(a))] = np.NA' can achieve that. For the bitpattern approach, the parameterized dtype('NA[f8,InfNan]') described in a later section can be used to get these semantics without the extra manipulation. @@ -857,7 +859,7 @@ Shared Masks One feature of numpy.ma is called 'shared masks'. -http://docs.scipy.org/doc/numpy/reference/maskedarray.baseclass.html#numpy.ma.MaskedArray.sharedmask +https://docs.scipy.org/doc/numpy/reference/maskedarray.baseclass.html#numpy.ma.MaskedArray.sharedmask This feature cannot be supported by a masked implementation of missing values without directly violating the missing value abstraction. @@ -888,7 +890,7 @@ found from doing google searches of numpy C API array access. NumPy Documentation - How to extend NumPy ----------------------------------------- -http://docs.scipy.org/doc/numpy/user/c-info.how-to-extend.html#dealing-with-array-objects +https://docs.scipy.org/doc/numpy/user/c-info.how-to-extend.html#dealing-with-array-objects This page has a section "Dealing with array objects" which has some advice for how to access numpy arrays from C. When accepting arrays, the first step it suggests is @@ -898,10 +900,10 @@ advice will properly fail when given an NA-masked array it doesn't know how to h The way this is handled is that PyArray_FromAny requires a special flag, NPY_ARRAY_ALLOWNA, before it will allow NA-masked arrays to flow through. -http://docs.scipy.org/doc/numpy/reference/c-api.array.html#NPY_ARRAY_ALLOWNA +https://docs.scipy.org/doc/numpy/reference/c-api.array.html#NPY_ARRAY_ALLOWNA Code which does not follow this advice, and instead just calls PyArray_Check() to verify -its an ndarray and checks some flags, will silently produce incorrect results. This style +it is an ndarray and checks some flags, will silently produce incorrect results. This style of code does not provide any opportunity for numpy to say "hey, this array is special", so also is not compatible with future ideas of lazy evaluation, derived dtypes, etc. @@ -926,7 +928,7 @@ to access the array elements. This python indexing still goes through the Python API, so the NA handling and error checking in numpy still can work like normal and fail if the inputs have NAs which cannot fit in the output array. In this case it fails when trying to convert the NA into an integer -to set in in the output. +to set in the output. The next version of the code introduces more efficient indexing. This operates based on Python's buffer protocol. This causes Cython to call @@ -955,11 +957,13 @@ so the later code will raise exceptions as desired. C Implementation Details ************************ +.. highlight:: c + The first version to implement is the array masks, because it is the more general approach. The mask itself is an array, but since it is intended to never be directly accessible from Python, it won't be a full ndarray itself. The mask always has the same shape as -the array it's attached to, so it doesn't need its own shape. For +the array it is attached to, so it doesn't need its own shape. For an array with a struct dtype, however, the mask will have a different dtype than just a straight bool, so it does need its own dtype. This gives us the following additions to the PyArrayObject:: @@ -1157,32 +1161,32 @@ Acknowledgments In addition to feedback from Travis Oliphant and others at Enthought, this NEP has been revised based on a great deal of feedback from the NumPy-Discussion mailing list. The people participating in -the discussion are:: - - Nathaniel Smith - Robert Kern - Charles Harris - Gael Varoquaux - Eric Firing - Keith Goodman - Pierre GM - Christopher Barker - Josef Perktold - Ben Root - Laurent Gautier - Neal Becker - Bruce Southey - Matthew Brett - Wes McKinney - Lluís - Olivier Delalleau - Alan G Isaac - E. Antero Tammi - Jason Grout - Dag Sverre Seljebotn - Joe Harrington - Gary Strangman - Chris Jordan-Squire - Peter +the discussion are: + +- Nathaniel Smith +- Robert Kern +- Charles Harris +- Gael Varoquaux +- Eric Firing +- Keith Goodman +- Pierre GM +- Christopher Barker +- Josef Perktold +- Ben Root +- Laurent Gautier +- Neal Becker +- Bruce Southey +- Matthew Brett +- Wes McKinney +- Lluís +- Olivier Delalleau +- Alan G Isaac +- E. Antero Tammi +- Jason Grout +- Dag Sverre Seljebotn +- Joe Harrington +- Gary Strangman +- Chris Jordan-Squire +- Peter I apologize if I missed anyone. diff --git a/doc/neps/nep-0013-ufunc-overrides.rst b/doc/neps/nep-0013-ufunc-overrides.rst index c97b69023d19..c132113db7b8 100644 --- a/doc/neps/nep-0013-ufunc-overrides.rst +++ b/doc/neps/nep-0013-ufunc-overrides.rst @@ -1,6 +1,8 @@ -================================= -A Mechanism for Overriding Ufuncs -================================= +.. _NEP13: + +========================================== +NEP 13 — A mechanism for overriding Ufuncs +========================================== .. currentmodule:: numpy @@ -44,8 +46,8 @@ right behaviour, hence the change in name.) The ``__array_ufunc__`` as described below requires that any corresponding Python binary operations (``__mul__`` et al.) should be -implemented in a specific way and be compatible with Numpy's ndarray -semantics. Objects that do not satisfy this cannot override any Numpy +implemented in a specific way and be compatible with NumPy's ndarray +semantics. Objects that do not satisfy this cannot override any NumPy ufuncs. We do not specify a future-compatible path by which this requirement can be relaxed --- any changes here require corresponding changes in 3rd party code. @@ -53,7 +55,7 @@ changes in 3rd party code. .. [1] http://docs.python.org/doc/numpy/user/basics.subclassing.html .. [2] https://github.com/scipy/scipy/issues/2123 .. [3] https://github.com/scipy/scipy/issues/1569 -.. [4] http://technicaldiscovery.blogspot.com/2013/07/thoughts-after-scipy-2013-and-specific.html +.. [4] https://technicaldiscovery.blogspot.com/2013/07/thoughts-after-scipy-2013-and-specific.html Motivation @@ -100,7 +102,7 @@ Take this example of ufuncs interoperability with sparse matrices.:: [ 4, 1, 4]], dtype=int64) In [5]: np.multiply(a, bsp) # Returns NotImplemented to user, bad! - Out[5]: NotImplemted + Out[5]: NotImplemented Returning :obj:`NotImplemented` to user should not happen. Moreover:: @@ -130,11 +132,11 @@ However, this behavior is more confusing than useful, and having a :exc:`TypeError` would be preferable. This proposal will *not* resolve the issue with scipy.sparse matrices, -which have multiplication semantics incompatible with numpy arrays. +which have multiplication semantics incompatible with NumPy arrays. However, the aim is to enable writing other custom array types that have strictly ndarray compatible semantics. -.. [5] http://mail.python.org/pipermail/numpy-discussion/2011-June/056945.html +.. [5] https://mail.python.org/pipermail/numpy-discussion/2011-June/056945.html .. [6] https://github.com/numpy/numpy/issues/5844 @@ -244,7 +246,7 @@ three groups: - *Incompatible*: neither above nor below A; types for which no (indirect) upcasting is possible. -Note that the legacy behaviour of numpy ufuncs is to try to convert +Note that the legacy behaviour of NumPy ufuncs is to try to convert unknown objects to :class:`ndarray` via :func:`np.asarray`. This is equivalent to placing :class:`ndarray` above these objects in the graph. Since we above defined :class:`ndarray` to return `NotImplemented` for @@ -261,16 +263,7 @@ consider carefully if any surprising behavior results. Type casting hierarchy. - .. graphviz:: - - digraph array_ufuncs { - rankdir=BT; - A -> C [label="C"]; - B -> C [label="C"]; - D -> B [label="B"]; - ndarray -> C [label="A"]; - ndarray -> B [label="B"]; - } + .. image:: _static/nep0013_image1.png The ``__array_ufunc__`` of type A can handle ndarrays returning C, B can handle ndarray and D returning B, and C can handle A and B returning C, @@ -286,14 +279,7 @@ consider carefully if any surprising behavior results. One-cycle in the ``__array_ufunc__`` graph. - .. graphviz:: - - digraph array_ufuncs { - rankdir=BT; - A -> B [label="B"]; - B -> A [label="A"]; - } - + .. image:: _static/nep0013_image2.png In this case, the ``__array_ufunc__`` relations have a cycle of length 1, and a type casting hierarchy does not exist. Binary operations are not @@ -303,15 +289,7 @@ consider carefully if any surprising behavior results. Longer cycle in the ``__array_ufunc__`` graph. - .. graphviz:: - - digraph array_ufuncs { - rankdir=BT; - A -> B [label="B"]; - B -> C [label="C"]; - C -> A [label="A"]; - } - + .. image:: _static/nep0013_image3.png In this case, the ``__array_ufunc__`` relations have a longer cycle, and a type casting hierarchy does not exist. Binary operations are still @@ -476,7 +454,7 @@ implements the following behavior: A class wishing to modify the interaction with :class:`ndarray` in binary operations therefore has two options: -1. Implement ``__array_ufunc__`` and follow Numpy semantics for Python +1. Implement ``__array_ufunc__`` and follow NumPy semantics for Python binary operations (see below). 2. Set ``__array_ufunc__ = None``, and implement Python binary @@ -500,7 +478,7 @@ are not compatible, i.e., implementations should be something like:: except AttributeError: return False - class ArrayLike(object): + class ArrayLike: ... def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): ... @@ -538,7 +516,7 @@ does not know how to deal with arrays and ufuncs, and thus has set ``__array_ufunc__`` to :obj:`None`, but does know how to do multiplication:: - class MyObject(object): + class MyObject: __array_ufunc__ = None def __init__(self, value): self.value = value @@ -578,7 +556,7 @@ in turn immediately raises :exc:`TypeError`, because one of its operands ``arr.__array_ufunc__``, which will return :obj:`NotImplemented`, which we catch. -.. note :: the reason for not allowing in-place operations to return +.. note:: the reason for not allowing in-place operations to return :obj:`NotImplemented` is that these cannot generically be replaced by a simple reverse operation: most array operations assume the contents of the instance are changed in-place, and do not expect a new @@ -635,7 +613,7 @@ simplify the dispatch logic for binary operations with NumPy arrays as much as possible, by making it possible to use Python's dispatch rules or NumPy's dispatch rules, but not some mixture of both at the same time. -.. [9] http://bugs.python.org/issue30140 +.. [9] https://bugs.python.org/issue30140 .. _neps.ufunc-overrides.list-of-operators: @@ -700,7 +678,7 @@ NA ``abs`` :func:`absolute` Future extensions to other functions ------------------------------------ -Some numpy functions could be implemented as (generalized) Ufunc, in +Some NumPy functions could be implemented as (generalized) Ufunc, in which case it would be possible for them to be overridden by the ``__array_ufunc__`` method. A prime candidate is :func:`~numpy.matmul`, which currently is not a Ufunc, but could be relatively easily be diff --git a/doc/neps/nep-0014-dropping-python2.7-proposal.rst b/doc/neps/nep-0014-dropping-python2.7-proposal.rst index 6cfd4707fc6c..e14a173e2032 100644 --- a/doc/neps/nep-0014-dropping-python2.7-proposal.rst +++ b/doc/neps/nep-0014-dropping-python2.7-proposal.rst @@ -1,8 +1,10 @@ -==================================== -Plan for dropping Python 2.7 support -==================================== +.. _NEP14: -:Status: Accepted +============================================= +NEP 14 — Plan for dropping Python 2.7 support +============================================= + +:Status: Final :Resolution: https://mail.python.org/pipermail/numpy-discussion/2017-November/077419.html The Python core team plans to stop supporting Python 2 in 2020. The NumPy @@ -50,6 +52,6 @@ to Python3 only, see the python3-statement_. For more information on porting your code to run on Python 3, see the python3-howto_. -.. _python3-statement: http://www.python3statement.org/ +.. _python3-statement: https://python3statement.org/ .. _python3-howto: https://docs.python.org/3/howto/pyporting.html diff --git a/doc/neps/nep-0015-merge-multiarray-umath.rst b/doc/neps/nep-0015-merge-multiarray-umath.rst new file mode 100644 index 000000000000..1efceb957693 --- /dev/null +++ b/doc/neps/nep-0015-merge-multiarray-umath.rst @@ -0,0 +1,159 @@ +.. _NEP15: + +===================================== +NEP 15 — Merging multiarray and umath +===================================== + +:Author: Nathaniel J. Smith <njs@pobox.com> +:Status: Final +:Type: Standards Track +:Created: 2018-02-22 +:Resolution: https://mail.python.org/pipermail/numpy-discussion/2018-June/078345.html + +Abstract +-------- + +Let's merge ``numpy.core.multiarray`` and ``numpy.core.umath`` into a +single extension module, and deprecate ``np.set_numeric_ops``. + + +Background +---------- + +Currently, numpy's core C code is split between two separate extension +modules. + +``numpy.core.multiarray`` is built from +``numpy/core/src/multiarray/*.c``, and contains the core array +functionality (in particular, the ``ndarray`` object). + +``numpy.core.umath`` is built from ``numpy/core/src/umath/*.c``, and +contains the ufunc machinery. + +These two modules each expose their own separate C API, accessed via +``import_multiarray()`` and ``import_umath()`` respectively. The idea +is that they're supposed to be independent modules, with +``multiarray`` as a lower-level layer with ``umath`` built on top. In +practice this has turned out to be problematic. + +First, the layering isn't perfect: when you write ``ndarray + +ndarray``, this invokes ``ndarray.__add__``, which then calls the +ufunc ``np.add``. This means that ``ndarray`` needs to know about +ufuncs – so instead of a clean layering, we have a circular +dependency. To solve this, ``multiarray`` exports a somewhat +terrifying function called ``set_numeric_ops``. The bootstrap +procedure each time you ``import numpy`` is: + +1. ``multiarray`` and its ``ndarray`` object are loaded, but + arithmetic operations on ndarrays are broken. + +2. ``umath`` is loaded. + +3. ``set_numeric_ops`` is used to monkeypatch all the methods like + ``ndarray.__add__`` with objects from ``umath``. + +In addition, ``set_numeric_ops`` is exposed as a public API, +``np.set_numeric_ops``. + +Furthermore, even when this layering does work, it ends up distorting +the shape of our public ABI. In recent years, the most common reason +for adding new functions to ``multiarray``\'s "public" ABI is not that +they really need to be public or that we expect other projects to use +them, but rather just that we need to call them from ``umath``. This +is extremely unfortunate, because it makes our public ABI +unnecessarily large, and since we can never remove things from it then +this creates an ongoing maintenance burden. The way C works, you can +have internal API that's visible to everything inside the same +extension module, or you can have a public API that everyone can use; +you can't (easily) have an API that's visible to multiple extension +modules inside numpy, but not to external users. + +We've also increasingly been putting utility code into +``numpy/core/src/private/``, which now contains a bunch of files which +are ``#include``\d twice, once into ``multiarray`` and once into +``umath``. This is pretty gross, and is purely a workaround for these +being separate C extensions. The ``npymath`` library is also +included in both extension modules. + + +Proposed changes +---------------- + +This NEP proposes three changes: + +1. We should start building ``numpy/core/src/multiarray/*.c`` and + ``numpy/core/src/umath/*.c`` together into a single extension + module. + +2. Instead of ``set_numeric_ops``, we should use some new, private API + to set up ``ndarray.__add__`` and friends. + +3. We should deprecate, and eventually remove, ``np.set_numeric_ops``. + + +Non-proposed changes +-------------------- + +We don't necessarily propose to throw away the distinction between +multiarray/ and umath/ in terms of our source code organization: +internal organization is useful! We just want to build them together +into a single extension module. Of course, this does open the door for +potential future refactorings, which we can then evaluate based on +their merits as they come up. + +It also doesn't propose that we break the public C ABI. We should +continue to provide ``import_multiarray()`` and ``import_umath()`` +functions – it's just that now both ABIs will ultimately be loaded +from the same C library. Due to how ``import_multiarray()`` and +``import_umath()`` are written, we'll also still need to have modules +called ``numpy.core.multiarray`` and ``numpy.core.umath``, and they'll +need to continue to export ``_ARRAY_API`` and ``_UFUNC_API`` objects – +but we can make one or both of these modules be tiny shims that simply +re-export the magic API object from where-ever it's actually defined. +(See ``numpy/core/code_generators/generate_{numpy,ufunc}_api.py`` for +details of how these imports work.) + + +Backward compatibility +---------------------- + +The only compatibility break is the deprecation of ``np.set_numeric_ops``. + + +Rejected alternatives +--------------------- + +Preserve ``set_numeric_ops`` for monkeypatching +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In discussing this NEP, one additional use case was raised for +``set_numeric_ops``: if you have an optimized vector math library +(e.g. Intel's MKL VML, Sleef, or Yeppp), then ``set_numeric_ops`` can +be used to monkeypatch numpy to use these operations instead of +numpy's built-in vector operations. But, even if we grant that this is +a great idea, using ``set_numeric_ops`` isn't actually the best way to +do it. All ``set_numeric_ops`` allows you to do is take over Python's +syntactic operators (``+``, ``*``, etc.) on ndarrays; it doesn't let +you affect operations called via other APIs (e.g., ``np.add``), or +operations that don't have built-in syntax (e.g., ``np.exp``). Also, +you have to reimplement the whole ufunc machinery, instead of just the +core loop. On the other hand, the `PyUFunc_ReplaceLoopBySignature +<https://docs.scipy.org/doc/numpy/reference/c-api.ufunc.html#c.PyUFunc_ReplaceLoopBySignature>`__ +API – which was added in 2006 – allows replacement of the inner loops +of arbitrary ufuncs. This is both simpler and more powerful – e.g. +replacing the inner loop of ``np.add`` means your code will +automatically be used for both ``ndarray + ndarray`` as well as direct +calls to ``np.add``. So this doesn't seem like a good reason to not +deprecate ``set_numeric_ops``. + + +Discussion +---------- + +* https://mail.python.org/pipermail/numpy-discussion/2018-March/077764.html +* https://mail.python.org/pipermail/numpy-discussion/2018-June/078345.html + +Copyright +--------- + +This document has been placed in the public domain. diff --git a/doc/neps/nep-0016-abstract-array.rst b/doc/neps/nep-0016-abstract-array.rst new file mode 100644 index 000000000000..9d21abe6f4bb --- /dev/null +++ b/doc/neps/nep-0016-abstract-array.rst @@ -0,0 +1,330 @@ +.. _NEP16: + +============================================================= +NEP 16 — An abstract base class for identifying "duck arrays" +============================================================= + +:Author: Nathaniel J. Smith <njs@pobox.com> +:Status: Withdrawn +:Type: Standards Track +:Created: 2018-03-06 +:Resolution: https://github.com/numpy/numpy/pull/12174 + +.. note:: + + This NEP has been withdrawn in favor of the protocol based approach + described in + `NEP 22 <nep-0022-ndarray-duck-typing-overview.html>`__ + +Abstract +-------- + +We propose to add an abstract base class ``AbstractArray`` so that +third-party classes can declare their ability to "quack like" an +``ndarray``, and an ``asabstractarray`` function that performs +similarly to ``asarray`` except that it passes through +``AbstractArray`` instances unchanged. + + +Detailed description +-------------------- + +Many functions, in NumPy and in third-party packages, start with some +code like:: + + def myfunc(a, b): + a = np.asarray(a) + b = np.asarray(b) + ... + +This ensures that ``a`` and ``b`` are ``np.ndarray`` objects, so +``myfunc`` can carry on assuming that they'll act like ndarrays both +semantically (at the Python level), and also in terms of how they're +stored in memory (at the C level). But many of these functions only +work with arrays at the Python level, which means that they don't +actually need ``ndarray`` objects *per se*: they could work just as +well with any Python object that "quacks like" an ndarray, such as +sparse arrays, dask's lazy arrays, or xarray's labeled arrays. + +However, currently, there's no way for these libraries to express that +their objects can quack like an ndarray, and there's no way for +functions like ``myfunc`` to express that they'd be happy with +anything that quacks like an ndarray. The purpose of this NEP is to +provide those two features. + +Sometimes people suggest using ``np.asanyarray`` for this purpose, but +unfortunately its semantics are exactly backwards: it guarantees that +the object it returns uses the same memory layout as an ``ndarray``, +but tells you nothing at all about its semantics, which makes it +essentially impossible to use safely in practice. Indeed, the two +``ndarray`` subclasses distributed with NumPy – ``np.matrix`` and +``np.ma.masked_array`` – do have incompatible semantics, and if they +were passed to a function like ``myfunc`` that doesn't check for them +as a special-case, then it may silently return incorrect results. + + +Declaring that an object can quack like an array +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +There are two basic approaches we could use for checking whether an +object quacks like an array. We could check for a special attribute on +the class:: + + def quacks_like_array(obj): + return bool(getattr(type(obj), "__quacks_like_array__", False)) + +Or, we could define an `abstract base class (ABC) +<https://docs.python.org/3/library/collections.abc.html>`__:: + + def quacks_like_array(obj): + return isinstance(obj, AbstractArray) + +If you look at how ABCs work, this is essentially equivalent to +keeping a global set of types that have been declared to implement the +``AbstractArray`` interface, and then checking it for membership. + +Between these, the ABC approach seems to have a number of advantages: + +* It's Python's standard, "one obvious way" of doing this. + +* ABCs can be introspected (e.g. ``help(np.AbstractArray)`` does + something useful). + +* ABCs can provide useful mixin methods. + +* ABCs integrate with other features like mypy type-checking, + ``functools.singledispatch``, etc. + +One obvious thing to check is whether this choice affects speed. Using +the attached benchmark script on a CPython 3.7 prerelease (revision +c4d77a661138d, self-compiled, no PGO), on a Thinkpad T450s running +Linux, we find:: + + np.asarray(ndarray_obj) 330 ns + np.asarray([]) 1400 ns + + Attribute check, success 80 ns + Attribute check, failure 80 ns + + ABC, success via subclass 340 ns + ABC, success via register() 700 ns + ABC, failure 370 ns + +Notes: + +* The first two lines are included to put the other lines in context. + +* This used 3.7 because both ``getattr`` and ABCs are receiving + substantial optimizations in this release, and it's more + representative of the long-term future of Python. (Failed + ``getattr`` doesn't necessarily construct an exception object + anymore, and ABCs were reimplemented in C.) + +* The "success" lines refer to cases where ``quacks_like_array`` would + return True. The "failure" lines are cases where it would return + False. + +* The first measurement for ABCs is subclasses defined like:: + + class MyArray(AbstractArray): + ... + + The second is for subclasses defined like:: + + class MyArray: + ... + + AbstractArray.register(MyArray) + + I don't know why there's such a large difference between these. + +In practice, either way we'd only do the full test after first +checking for well-known types like ``ndarray``, ``list``, etc. `This +is how NumPy currently checks for other double-underscore attributes +<https://github.com/numpy/numpy/blob/main/numpy/core/src/private/get_attr_string.h>`__ +and the same idea applies here to either approach. So these numbers +won't affect the common case, just the case where we actually have an +``AbstractArray``, or else another third-party object that will end up +going through ``__array__`` or ``__array_interface__`` or end up as an +object array. + +So in summary, using an ABC will be slightly slower than using an +attribute, but this doesn't affect the most common paths, and the +magnitude of slowdown is fairly small (~250 ns on an operation that +already takes longer than that). Furthermore, we can potentially +optimize this further (e.g. by keeping a tiny LRU cache of types that +are known to be AbstractArray subclasses, on the assumption that most +code will only use one or two of these types at a time), and it's very +unclear that this even matters – if the speed of ``asarray`` no-op +pass-throughs were a bottleneck that showed up in profiles, then +probably we would have made them faster already! (It would be trivial +to fast-path this, but we don't.) + +Given the semantic and usability advantages of ABCs, this seems like +an acceptable trade-off. + +.. + CPython 3.6 (from Debian):: + + Attribute check, success 110 ns + Attribute check, failure 370 ns + + ABC, success via subclass 690 ns + ABC, success via register() 690 ns + ABC, failure 1220 ns + + +Specification of ``asabstractarray`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Given ``AbstractArray``, the definition of ``asabstractarray`` is simple:: + + def asabstractarray(a, dtype=None): + if isinstance(a, AbstractArray): + if dtype is not None and dtype != a.dtype: + return a.astype(dtype) + return a + return asarray(a, dtype=dtype) + +Things to note: + +* ``asarray`` also accepts an ``order=`` argument, but we don't + include that here because it's about details of memory + representation, and the whole point of this function is that you use + it to declare that you don't care about details of memory + representation. + +* Using the ``astype`` method allows the ``a`` object to decide how to + implement casting for its particular type. + +* For strict compatibility with ``asarray``, we skip calling + ``astype`` when the dtype is already correct. Compare:: + + >>> a = np.arange(10) + + # astype() always returns a view: + >>> a.astype(a.dtype) is a + False + + # asarray() returns the original object if possible: + >>> np.asarray(a, dtype=a.dtype) is a + True + + +What exactly are you promising if you inherit from ``AbstractArray``? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This will presumably be refined over time. The ideal of course is that +your class should be indistinguishable from a real ``ndarray``, but +nothing enforces that except the expectations of users. In practice, +declaring that your class implements the ``AbstractArray`` interface +simply means that it will start passing through ``asabstractarray``, +and so by subclassing it you're saying that if some code works for +``ndarray``\s but breaks for your class, then you're willing to accept +bug reports on that. + +To start with, we should declare ``__array_ufunc__`` to be an abstract +method, and add the ``NDArrayOperatorsMixin`` methods as mixin +methods. + +Declaring ``astype`` as an ``@abstractmethod`` probably makes sense as +well, since it's used by ``asabstractarray``. We might also want to go +ahead and add some basic attributes like ``ndim``, ``shape``, +``dtype``. + +Adding new abstract methods will be a bit tricky, because ABCs enforce +these at subclass time; therefore, simply adding a new +`@abstractmethod` will be a backwards compatibility break. If this +becomes a problem then we can use some hacks to implement an +`@upcoming_abstractmethod` decorator that only issues a warning if the +method is missing, and treat it like a regular deprecation cycle. (In +this case, the thing we'd be deprecating is "support for abstract +arrays that are missing feature X".) + + +Naming +~~~~~~ + +The name of the ABC doesn't matter too much, because it will only be +referenced rarely and in relatively specialized situations. The name +of the function matters a lot, because most existing instances of +``asarray`` should be replaced by this, and in the future it's what +everyone should be reaching for by default unless they have a specific +reason to use ``asarray`` instead. This suggests that its name really +should be *shorter* and *more memorable* than ``asarray``... which +is difficult. I've used ``asabstractarray`` in this draft, but I'm not +really happy with it, because it's too long and people are unlikely to +start using it by habit without endless exhortations. + +One option would be to actually change ``asarray``\'s semantics so +that *it* passes through ``AbstractArray`` objects unchanged. But I'm +worried that there may be a lot of code out there that calls +``asarray`` and then passes the result into some C function that +doesn't do any further type checking (because it knows that its caller +has already used ``asarray``). If we allow ``asarray`` to return +``AbstractArray`` objects, and then someone calls one of these C +wrappers and passes it an ``AbstractArray`` object like a sparse +array, then they'll get a segfault. Right now, in the same situation, +``asarray`` will instead invoke the object's ``__array__`` method, or +use the buffer interface to make a view, or pass through an array with +object dtype, or raise an error, or similar. Probably none of these +outcomes are actually desirable in most cases, so maybe making it a +segfault instead would be OK? But it's dangerous given that we don't +know how common such code is. OTOH, if we were starting from scratch +then this would probably be the ideal solution. + +We can't use ``asanyarray`` or ``array``, since those are already +taken. + +Any other ideas? ``np.cast``, ``np.coerce``? + + +Implementation +-------------- + +1. Rename ``NDArrayOperatorsMixin`` to ``AbstractArray`` (leaving + behind an alias for backwards compatibility) and make it an ABC. + +2. Add ``asabstractarray`` (or whatever we end up calling it), and + probably a C API equivalent. + +3. Begin migrating NumPy internal functions to using + ``asabstractarray`` where appropriate. + + +Backward compatibility +---------------------- + +This is purely a new feature, so there are no compatibility issues. +(Unless we decide to change the semantics of ``asarray`` itself.) + + +Rejected alternatives +--------------------- + +One suggestion that has come up is to define multiple abstract classes +for different subsets of the array interface. Nothing in this proposal +stops either NumPy or third-parties from doing this in the future, but +it's very difficult to guess ahead of time which subsets would be +useful. Also, "the full ndarray interface" is something that existing +libraries are written to expect (because they work with actual +ndarrays) and test (because they test with actual ndarrays), so it's +by far the easiest place to start. + + +Links to discussion +------------------- + +* https://mail.python.org/pipermail/numpy-discussion/2018-March/077767.html + + +Appendix: Benchmark script +-------------------------- + +.. literalinclude:: nep-0016-benchmark.py + + +Copyright +--------- + +This document has been placed in the public domain. diff --git a/doc/neps/nep-0016-benchmark.py b/doc/neps/nep-0016-benchmark.py new file mode 100644 index 000000000000..ec8e44726876 --- /dev/null +++ b/doc/neps/nep-0016-benchmark.py @@ -0,0 +1,48 @@ +import perf +import abc +import numpy as np + +class NotArray: + pass + +class AttrArray: + __array_implementer__ = True + +class ArrayBase(abc.ABC): + pass + +class ABCArray1(ArrayBase): + pass + +class ABCArray2: + pass + +ArrayBase.register(ABCArray2) + +not_array = NotArray() +attr_array = AttrArray() +abc_array_1 = ABCArray1() +abc_array_2 = ABCArray2() + +# Make sure ABC cache is primed +isinstance(not_array, ArrayBase) +isinstance(abc_array_1, ArrayBase) +isinstance(abc_array_2, ArrayBase) + +runner = perf.Runner() +def t(name, statement): + runner.timeit(name, statement, globals=globals()) + +t("np.asarray([])", "np.asarray([])") +arrobj = np.array([]) +t("np.asarray(arrobj)", "np.asarray(arrobj)") + +t("attr, False", + "getattr(not_array, '__array_implementer__', False)") +t("attr, True", + "getattr(attr_array, '__array_implementer__', False)") + +t("ABC, False", "isinstance(not_array, ArrayBase)") +t("ABC, True, via inheritance", "isinstance(abc_array_1, ArrayBase)") +t("ABC, True, via register", "isinstance(abc_array_2, ArrayBase)") + diff --git a/doc/neps/nep-0017-split-out-maskedarray.rst b/doc/neps/nep-0017-split-out-maskedarray.rst index d6dcc1def1be..5cb1c0c399e5 100644 --- a/doc/neps/nep-0017-split-out-maskedarray.rst +++ b/doc/neps/nep-0017-split-out-maskedarray.rst @@ -1,6 +1,8 @@ -======================= -Split Out Masked Arrays -======================= +.. _NEP17: + +================================ +NEP 17 — Split out masked arrays +================================ :Author: Stéfan van der Walt <stefanv@berkeley.edu> :Status: Rejected diff --git a/doc/neps/nep-0018-array-function-protocol.rst b/doc/neps/nep-0018-array-function-protocol.rst index 943ca4cbf4c5..f4c21446bbe6 100644 --- a/doc/neps/nep-0018-array-function-protocol.rst +++ b/doc/neps/nep-0018-array-function-protocol.rst @@ -1,35 +1,44 @@ -================================================== -NEP: Dispatch Mechanism for NumPy's high level API -================================================== +.. _NEP18: + +==================================================================== +NEP 18 — A dispatch mechanism for NumPy's high level array functions +==================================================================== :Author: Stephan Hoyer <shoyer@google.com> :Author: Matthew Rocklin <mrocklin@gmail.com> -:Status: Draft +:Author: Marten van Kerkwijk <mhvk@astro.utoronto.ca> +:Author: Hameer Abbasi <hameerabbasi@yahoo.com> +:Author: Eric Wieser <wieser.eric@gmail.com> +:Status: Final :Type: Standards Track :Created: 2018-05-29 +:Updated: 2019-05-25 +:Resolution: https://mail.python.org/pipermail/numpy-discussion/2018-August/078493.html -Abstact -------- +Abstract +-------- -We propose a protocol to allow arguments of numpy functions to define -how that function operates on them. This allows other libraries that -implement NumPy's high level API to reuse Numpy functions. This allows -libraries that extend NumPy's high level API to apply to more NumPy-like -libraries. +We propose the ``__array_function__`` protocol, to allow arguments of NumPy +functions to define how that function operates on them. This will allow +using NumPy as a high level API for efficient multi-dimensional array +operations, even with array implementations that differ greatly from +``numpy.ndarray``. Detailed description -------------------- -Numpy's high level ndarray API has been implemented several times +NumPy's high level ndarray API has been implemented several times outside of NumPy itself for different architectures, such as for GPU arrays (CuPy), Sparse arrays (scipy.sparse, pydata/sparse) and parallel -arrays (Dask array) as well as various Numpy-like implementations in the +arrays (Dask array) as well as various NumPy-like implementations in the deep learning frameworks, like TensorFlow and PyTorch. -Similarly there are several projects that build on top of the Numpy API -for labeled and indexed arrays (XArray), automatic differentation -(Autograd, Tangent), higher order array factorizations (TensorLy), etc. -that add additional functionality on top of the Numpy API. +Similarly there are many projects that build on top of the NumPy API +for labeled and indexed arrays (XArray), automatic differentiation +(Autograd, Tangent), masked arrays (numpy.ma), physical units (astropy.units, +pint, unyt), etc. that add additional functionality on top of the NumPy API. +Most of these project also implement a close variation of NumPy's level high +API. We would like to be able to use these libraries together, for example we would like to be able to place a CuPy array within XArray, or perform @@ -38,7 +47,7 @@ accomplish if code written for NumPy ndarrays could also be used by other NumPy-like projects. For example, we would like for the following code example to work -equally well with any Numpy-like array object: +equally well with any NumPy-like array object: .. code:: python @@ -47,7 +56,7 @@ equally well with any Numpy-like array object: return np.mean(np.exp(y)) Some of this is possible today with various protocol mechanisms within -Numpy. +NumPy. - The ``np.exp`` function checks the ``__array_ufunc__`` protocol - The ``.T`` method works using Python's method dispatch @@ -55,10 +64,10 @@ Numpy. the argument However other functions, like ``np.tensordot`` do not dispatch, and -instead are likely to coerce to a Numpy array (using the ``__array__``) +instead are likely to coerce to a NumPy array (using the ``__array__``) protocol, or err outright. To achieve enough coverage of the NumPy API to support downstream projects like XArray and autograd we want to -support *almost all* functions within Numpy, which calls for a more +support *almost all* functions within NumPy, which calls for a more reaching protocol than just ``__array_ufunc__``. We would like a protocol that allows arguments of a NumPy function to take control and divert execution to another function (for example a GPU or parallel @@ -71,10 +80,35 @@ We propose adding support for a new protocol in NumPy, ``__array_function__``. This protocol is intended to be a catch-all for NumPy functionality that -is not covered by existing protocols, like reductions (like ``np.sum``) -or universal functions (like ``np.exp``). The semantics are very similar -to ``__array_ufunc__``, except the operation is specified by an -arbitrary callable object rather than a ufunc instance and method. +is not covered by the ``__array_ufunc__`` protocol for universal functions +(like ``np.exp``). The semantics are very similar to ``__array_ufunc__``, except +the operation is specified by an arbitrary callable object rather than a ufunc +instance and method. + +A prototype implementation can be found in +`this notebook <https://nbviewer.jupyter.org/gist/shoyer/1f0a308a06cd96df20879a1ddb8f0006>`_. + +.. warning:: + + The ``__array_function__`` protocol, and its use on particular functions, + is *experimental*. We plan to retain an interface that makes it possible + to override NumPy functions, but the way to do so for particular functions + **can and will change** with little warning. If such reduced backwards + compatibility guarantees are not accepted to you, do not rely upon overrides + of NumPy functions for non-NumPy arrays. See "Non-goals" below for more + details. + +.. note:: + + Dispatch with the ``__array_function__`` protocol has been implemented but is + not yet enabled by default: + + - In NumPy 1.16, you need to set the environment variable + ``NUMPY_EXPERIMENTAL_ARRAY_FUNCTION=1`` before importing NumPy to test + NumPy function overrides. + - In NumPy 1.17, the protocol will be enabled by default, but can be disabled + with ``NUMPY_EXPERIMENTAL_ARRAY_FUNCTION=0``. + - Eventually, expect to ``__array_function__`` to always be enabled. The interface ~~~~~~~~~~~~~ @@ -88,23 +122,26 @@ We propose the following signature for implementations of - ``func`` is an arbitrary callable exposed by NumPy's public API, which was called in the form ``func(*args, **kwargs)``. -- ``types`` is a list of types for all arguments to the original NumPy - function call that will be checked for an ``__array_function__`` - implementation. -- The tuple ``args`` and dict ``**kwargs`` are directly passed on from the +- ``types`` is a `collection <https://docs.python.org/3/library/collections.abc.html#collections.abc.Collection>`_ + of unique argument types from the original NumPy function call that + implement ``__array_function__``. +- The tuple ``args`` and dict ``kwargs`` are directly passed on from the original call. Unlike ``__array_ufunc__``, there are no high-level guarantees about the type of ``func``, or about which of ``args`` and ``kwargs`` may contain objects -implementing the array API. As a convenience for ``__array_function__`` -implementors of the NumPy API, the ``types`` keyword contains a list of all -types that implement the ``__array_function__`` protocol. This allows -downstream implementations to quickly determine if they are likely able to -support the operation. - -Still be determined: what guarantees can we offer for ``types``? Should -we promise that types are unique, and appear in the order in which they -are checked? +implementing the array API. + +As a convenience for ``__array_function__`` implementors, ``types`` provides all +argument types with an ``'__array_function__'`` attribute. This +allows implementors to quickly identify cases where they should defer to +``__array_function__`` implementations on other arguments. +The type of ``types`` is intentionally vague: +``frozenset`` would most closely match intended use, but we may use ``tuple`` +instead for performance reasons. In any case, ``__array_function__`` +implementations should not rely on the iteration order of ``types``, which +would violate a well-defined "Type casting hierarchy" (as described in +`NEP-13 <https://www.numpy.org/neps/nep-0013-ufunc-overrides.html>`_). Example for a project implementing the NumPy API ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -118,45 +155,86 @@ checks: If these conditions hold, ``__array_function__`` should return the result from calling its implementation for ``func(*args, **kwargs)``. Otherwise, it should return the sentinel value ``NotImplemented``, indicating -that the function is not implemented by these types. +that the function is not implemented by these types. This is preferable to +raising ``TypeError`` directly, because it gives *other* arguments the +opportunity to define the operations. + +There are no general requirements on the return value from +``__array_function__``, although most sensible implementations should probably +return array(s) with the same type as one of the function's arguments. +If/when Python gains +`typing support for protocols <https://www.python.org/dev/peps/pep-0544/>`_ +and NumPy adds static type annotations, the ``@overload`` implementation +for ``SupportsArrayFunction`` will indicate a return type of ``Any``. + +It may also be convenient to define a custom decorators (``implements`` below) +for registering ``__array_function__`` implementations. .. code:: python + HANDLED_FUNCTIONS = {} + class MyArray: def __array_function__(self, func, types, args, kwargs): if func not in HANDLED_FUNCTIONS: return NotImplemented + # Note: this allows subclasses that don't override + # __array_function__ to handle MyArray objects if not all(issubclass(t, MyArray) for t in types): return NotImplemented return HANDLED_FUNCTIONS[func](*args, **kwargs) - HANDLED_FUNCTIONS = { - np.concatenate: my_concatenate, - np.broadcast_to: my_broadcast_to, - np.sum: my_sum, - ... - } + def implements(numpy_function): + """Register an __array_function__ implementation for MyArray objects.""" + def decorator(func): + HANDLED_FUNCTIONS[numpy_function] = func + return func + return decorator + + @implements(np.concatenate) + def concatenate(arrays, axis=0, out=None): + ... # implementation of concatenate for MyArray objects + + @implements(np.broadcast_to) + def broadcast_to(array, shape): + ... # implementation of broadcast_to for MyArray objects + +Note that it is not required for ``__array_function__`` implementations to +include *all* of the corresponding NumPy function's optional arguments +(e.g., ``broadcast_to`` above omits the irrelevant ``subok`` argument). +Optional arguments are only passed in to ``__array_function__`` if they +were explicitly used in the NumPy function call. + +.. note:: -Necessary changes within the Numpy codebase itself + Just like the case for builtin special methods like ``__add__``, properly + written ``__array_function__`` methods should always return + ``NotImplemented`` when an unknown type is encountered. Otherwise, it will + be impossible to correctly override NumPy functions from another object + if the operation also includes one of your objects. + +Necessary changes within the NumPy codebase itself ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -This will require two changes within the Numpy codebase: +This will require two changes within the NumPy codebase: 1. A function to inspect available inputs, look for the ``__array_function__`` attribute on those inputs, and call those methods appropriately until one succeeds. This needs to be fast in the - common all-NumPy case. + common all-NumPy case, and have acceptable performance (no worse than + linear time) even if the number of overloaded inputs is large (e.g., + as might be the case for `np.concatenate`). This is one additional function of moderate complexity. -2. Calling this function within all relevant Numpy functions. +2. Calling this function within all relevant NumPy functions. - This affects many parts of the Numpy codebase, although with very low + This affects many parts of the NumPy codebase, although with very low complexity. Finding and calling the right ``__array_function__`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Given a Numpy function, ``*args`` and ``**kwargs`` inputs, we need to +Given a NumPy function, ``*args`` and ``**kwargs`` inputs, we need to search through ``*args`` and ``**kwargs`` for all appropriate inputs that might have the ``__array_function__`` attribute. Then we need to select among those possible methods and execute the right one. @@ -171,13 +249,46 @@ be nested within lists or dictionaries, such as in the case of ``np.concatenate([x, y, z])``. This can be problematic for two reasons: 1. Some functions are given long lists of values, and traversing them - might be prohibitively expensive -2. Some function may have arguments that we don't want to inspect, even - if they have the ``__array_function__`` method + might be prohibitively expensive. +2. Some functions may have arguments that we don't want to inspect, even + if they have the ``__array_function__`` method. + +To resolve these issues, NumPy functions should explicitly indicate which +of their arguments may be overloaded, and how these arguments should be +checked. As a rule, this should include all arguments documented as either +``array_like`` or ``ndarray``. + +We propose to do so by writing "dispatcher" functions for each overloaded +NumPy function: + +- These functions will be called with the exact same arguments that were passed + into the NumPy function (i.e., ``dispatcher(*args, **kwargs)``), and should + return an iterable of arguments to check for overrides. +- Dispatcher functions are required to share the exact same positional, + optional and keyword-only arguments as their corresponding NumPy functions. + Otherwise, valid invocations of a NumPy function could result in an error when + calling its dispatcher. +- Because default *values* for keyword arguments do not have + ``__array_function__`` attributes, by convention we set all default argument + values to ``None``. This reduces the likelihood of signatures falling out + of sync, and minimizes extraneous information in the dispatcher. + The only exception should be cases where the argument value in some way + effects dispatching, which should be rare. + +An example of the dispatcher for ``np.concatenate`` may be instructive: + +.. code:: python + + def _concatenate_dispatcher(arrays, axis=None, out=None): + for array in arrays: + yield array + if out is not None: + yield out -To resolve these we ask the functions to provide an explicit list of -arguments that should be traversed. This is the ``relevant_arguments=`` -keyword in the examples below. +The concatenate dispatcher is written as generator function, which allows it +to potentially include the value of the optional ``out`` argument without +needing to create a new sequence with the (potentially long) list of objects +to be concatenated. Trying ``__array_function__`` methods until the right one works ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' @@ -187,15 +298,15 @@ of these may decide that, given the available inputs, they are unable to determine the correct result. How do we call the right one? If several are valid then which has precedence? -The rules for dispatch with ``__array_function__`` match those for -``__array_ufunc__`` (see -`NEP-13 <http://www.numpy.org/neps/nep-0013-ufunc-overrides.html>`_). +For the most part, the rules for dispatch with ``__array_function__`` +match those for ``__array_ufunc__`` (see +`NEP-13 <https://www.numpy.org/neps/nep-0013-ufunc-overrides.html>`_). In particular: - NumPy will gather implementations of ``__array_function__`` from all specified inputs and call them in order: subclasses before - superclasses, and otherwise left to right. Note that in some edge cases, - this differs slightly from the + superclasses, and otherwise left to right. Note that in some edge cases + involving subclasses, this differs slightly from the `current behavior <https://bugs.python.org/issue30140>`_ of Python. - Implementations of ``__array_function__`` indicate that they can handle the operation by returning any value other than @@ -203,69 +314,232 @@ In particular: - If all ``__array_function__`` methods return ``NotImplemented``, NumPy will raise ``TypeError``. -Changes within Numpy functions +If no ``__array_function__`` methods exist, NumPy will default to calling its +own implementation, intended for use on NumPy arrays. This case arises, for +example, when all array-like arguments are Python numbers or lists. +(NumPy arrays do have a ``__array_function__`` method, given below, but it +always returns ``NotImplemented`` if any argument other than a NumPy array +subclass implements ``__array_function__``.) + +One deviation from the current behavior of ``__array_ufunc__`` is that NumPy +will only call ``__array_function__`` on the *first* argument of each unique +type. This matches Python's +`rule for calling reflected methods <https://docs.python.org/3/reference/datamodel.html#object.__ror__>`_, +and this ensures that checking overloads has acceptable performance even when +there are a large number of overloaded arguments. To avoid long-term divergence +between these two dispatch protocols, we should +`also update <https://github.com/numpy/numpy/issues/11306>`_ +``__array_ufunc__`` to match this behavior. + +The ``__array_function__`` method on ``numpy.ndarray`` +'''''''''''''''''''''''''''''''''''''''''''''''''''''' + +The use cases for subclasses with ``__array_function__`` are the same as those +with ``__array_ufunc__``, so ``numpy.ndarray`` also defines a +``__array_function__`` method: + +.. code:: python + + def __array_function__(self, func, types, args, kwargs): + if not all(issubclass(t, ndarray) for t in types): + # Defer to any non-subclasses that implement __array_function__ + return NotImplemented + + # Use NumPy's private implementation without __array_function__ + # dispatching + return func._implementation(*args, **kwargs) + +This method matches NumPy's dispatching rules, so for most part it is +possible to pretend that ``ndarray.__array_function__`` does not exist. +The private ``_implementation`` attribute, defined below in the +``array_function_dispatch`` decorator, allows us to avoid the special cases for +NumPy arrays that were needed in the ``__array_ufunc__`` protocol. + +The ``__array_function__`` protocol always calls subclasses before +superclasses, so if any ``ndarray`` subclasses are involved in an operation, +they will get the chance to override it, just as if any other argument +overrides ``__array_function__``. But the default behavior in an operation +that combines a base NumPy array and a subclass is different: if the subclass +returns ``NotImplemented``, NumPy's implementation of the function will be +called instead of raising an exception. This is appropriate since subclasses +are `expected to be substitutable <https://en.wikipedia.org/wiki/Liskov_substitution_principle>`_. + +We still caution authors of subclasses to exercise caution when relying +upon details of NumPy's internal implementations. It is not always possible to +write a perfectly substitutable ndarray subclass, e.g., in cases involving the +creation of new arrays, not least because NumPy makes use of internal +optimizations specialized to base NumPy arrays, e.g., code written in C. Even +if NumPy's implementation happens to work today, it may not work in the future. +In these cases, your recourse is to re-implement top-level NumPy functions via +``__array_function__`` on your subclass. + +Changes within NumPy functions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Given a function defined above, for now call it -``do_array_function_dance``, we now need to call that function from -within every relevant Numpy function. This is a pervasive change, but of -fairly simple and innocuous code that should complete quickly and +Given a function defining the above behavior, for now call it +``implement_array_function``, we now need to call that +function from within every relevant NumPy function. This is a pervasive change, +but of fairly simple and innocuous code that should complete quickly and without effect if no arguments implement the ``__array_function__`` -protocol. Let us consider a few examples of NumPy functions and how they -might be affected by this change: +protocol. + +To achieve this, we define a ``array_function_dispatch`` decorator to rewrite +NumPy functions. The basic implementation is as follows: .. code:: python + def array_function_dispatch(dispatcher, module=None): + """Wrap a function for dispatch with the __array_function__ protocol.""" + def decorator(implementation): + @functools.wraps(implementation) + def public_api(*args, **kwargs): + relevant_args = dispatcher(*args, **kwargs) + return implement_array_function( + implementation, public_api, relevant_args, args, kwargs) + if module is not None: + public_api.__module__ = module + # for ndarray.__array_function__ + public_api._implementation = implementation + return public_api + return decorator + + # example usage + def _broadcast_to_dispatcher(array, shape, subok=None): + return (array,) + + @array_function_dispatch(_broadcast_to_dispatcher, module='numpy') def broadcast_to(array, shape, subok=False): - success, value = do_array_function_dance( - func=broadcast_to, - relevant_arguments=[array], - args=(array,), - kwargs=dict(shape=shape, subok=subok)) - if success: - return value + ... # existing definition of np.broadcast_to + +Using a decorator is great! We don't need to change the definitions of +existing NumPy functions, and only need to write a few additional lines +for the dispatcher function. We could even reuse a single dispatcher for +families of functions with the same signature (e.g., ``sum`` and ``prod``). +For such functions, the largest change could be adding a few lines to the +docstring to note which arguments are checked for overloads. + +It's particularly worth calling out the decorator's use of +``functools.wraps``: + +- This ensures that the wrapped function has the same name and docstring as + the wrapped NumPy function. +- On Python 3, it also ensures that the decorator function copies the original + function signature, which is important for introspection based tools such as + auto-complete. +- Finally, it ensures that the wrapped function + `can be pickled <http://gael-varoquaux.info/programming/decoration-in-python-done-right-decorating-and-pickling.html>`_. + +The example usage illustrates several best practices for writing dispatchers +relevant to NumPy contributors: + +- We passed the ``module`` argument, which in turn sets the ``__module__`` + attribute on the generated function. This is for the benefit of better error + messages, here for errors raised internally by NumPy when no implementation + is found, e.g., + ``TypeError: no implementation found for 'numpy.broadcast_to'``. Setting + ``__module__`` to the canonical location in NumPy's public API encourages + users to use NumPy's public API for identifying functions in + ``__array_function__``. + +- The dispatcher is a function that returns a tuple, rather than an equivalent + (and equally valid) generator using ``yield``: + + .. code:: python + + # example usage + def broadcast_to(array, shape, subok=None): + yield array + + This is no accident: NumPy's implementation of dispatch for + ``__array_function__`` is fastest when dispatcher functions return a builtin + sequence type (``tuple`` or ``list``). + + On a related note, it's perfectly fine for dispatchers to return arguments + even if in some cases you *know* that they cannot have an + ``__array_function__`` method. This can arise for functions with default + arguments (e.g., ``None``) or complex signatures. NumPy's dispatching logic + sorts out these cases very quickly, so it generally is not worth the trouble + of parsing them on your own. + +.. note:: + + The code for ``array_function_dispatch`` above has been updated from the + original version of this NEP to match the actual + `implementation in NumPy <https://github.com/numpy/numpy/blob/e104f03ac8f65ae5b92a9b413b0fa639f39e6de2/numpy/core/overrides.py>`_. + +Extensibility +~~~~~~~~~~~~~ - ... # continue with the definition of broadcast_to +An important virtue of this approach is that it allows for adding new +optional arguments to NumPy functions without breaking code that already +relies on ``__array_function__``. - def concatenate(arrays, axis=0, out=None) - success, value = do_array_function_dance( - func=concatenate, - relevant_arguments=[arrays, out], - args=(arrays,), - kwargs=dict(axis=axis, out=out)) - if success: - return value +This is not a theoretical concern. NumPy's older, haphazard implementation of +overrides *within* functions like ``np.sum()`` necessitated some awkward +gymnastics when we decided to add new optional arguments, e.g., the new +``keepdims`` argument is only passed in cases where it is used: - ... # continue with the definition of concatenate +.. code:: python -The list of objects passed to ``relevant_arguments`` are those that should -be inspected for ``__array_function__`` implementations. + def sum(array, ..., keepdims=np._NoValue): + kwargs = {} + if keepdims is not np._NoValue: + kwargs['keepdims'] = keepdims + return array.sum(..., **kwargs) -Alternatively, we could write these overloads with a decorator, e.g., +For ``__array_function__`` implementors, this also means that it is possible +to implement even existing optional arguments incrementally, and only in cases +where it makes sense. For example, a library implementing immutable arrays +would not be required to explicitly include an unsupported ``out`` argument in +the function signature. This can be somewhat onerous to implement properly, +e.g., .. code:: python - @overload_for_array_function(['array']) - def broadcast_to(array, shape, subok=False): - ... # continue with the definition of broadcast_to - - @overload_for_array_function(['arrays', 'out']) - def concatenate(arrays, axis=0, out=None): - ... # continue with the definition of concatenate - -The decorator ``overload_for_array_function`` would be written in terms -of ``do_array_function_dance``. + def my_sum(array, ..., out=None): + if out is not None: + raise TypeError('out argument is not supported') + ... -The downside of this approach would be a loss of introspection capability -for NumPy functions on Python 2, since this requires the use of -``inspect.Signature`` (only available on Python 3). However, NumPy won't -be supporting Python 2 for `very much longer <http://www.numpy.org/neps/nep-0014-dropping-python2.7-proposal.html>`_. +We thus avoid encouraging the tempting shortcut of adding catch-all +``**ignored_kwargs`` to the signatures of functions called by NumPy, which fails +silently for misspelled or ignored arguments. + +Performance +~~~~~~~~~~~ + +Performance is always a concern with NumPy, even though NumPy users have +already prioritized usability over pure speed with their choice of the Python +language itself. It's important that this new ``__array_function__`` protocol +not impose a significant cost in the typical case of NumPy functions acting +on NumPy arrays. + +Our `microbenchmark results <https://nbviewer.jupyter.org/gist/shoyer/1f0a308a06cd96df20879a1ddb8f0006>`_ +show that a pure Python implementation of the override machinery described +above adds roughly 2-3 microseconds of overhead to each NumPy function call +without any overloaded arguments. For context, typical NumPy functions on small +arrays have a runtime of 1-10 microseconds, mostly determined by what fraction +of the function's logic is written in C. For example, one microsecond is about +the difference in speed between the ``ndarray.sum()`` method (1.6 us) and +``numpy.sum()`` function (2.6 us). + +Fortunately, we expect significantly less overhead with a C implementation of +``implement_array_function``, which is where the bulk of the +runtime is. This would leave the ``array_function_dispatch`` decorator and +dispatcher function on their own adding about 0.5 microseconds of overhead, +for perhaps ~1 microsecond of overhead in the typical case. + +In our view, this level of overhead is reasonable to accept for code written +in Python. We're pretty sure that the vast majority of NumPy users aren't +concerned about performance differences measured in microsecond(s) on NumPy +functions, because it's difficult to do *anything* in Python in less than a +microsecond. Use outside of NumPy ~~~~~~~~~~~~~~~~~~~~ Nothing about this protocol that is particular to NumPy itself. Should -we enourage use of the same ``__array_function__`` protocol third-party +we encourage use of the same ``__array_function__`` protocol third-party libraries for overloading non-NumPy functions, e.g., for making array-implementation generic functionality in SciPy? @@ -276,8 +550,9 @@ to be explicitly recognized. Libraries like Dask, CuPy, and Autograd already wrap a limited subset of SciPy functionality (e.g., ``scipy.linalg``) similarly to how they wrap NumPy. -If we want to do this, we should consider exposing the helper function -``do_array_function_dance()`` above as a public API. +If we want to do this, we should expose at least the decorator +``array_function_dispatch()`` and possibly also the lower level +``implement_array_function()`` as part of NumPy's public API. Non-goals --------- @@ -293,7 +568,10 @@ wait for an optimal implementation. The price of moving fast is that for now **this protocol should be considered strictly experimental**. We reserve the right to change the details of this protocol and how specific NumPy functions use it at any time in the future -- even in -otherwise bug-fix only releases of NumPy. +otherwise bug-fix only releases of NumPy. In practice, once initial +issues with ``__array_function__`` are worked out, we will use abbreviated +deprecation cycles as short as a single major NumPy release (e.g., as +little as four months). In particular, we don't plan to write additional NEPs that list all specific functions to overload, with exactly how they should be @@ -311,18 +589,20 @@ own protocols: - dispatch for methods of any kind, e.g., methods on ``np.random.RandomState`` objects. -As a concrete example of how we expect to break behavior in the future, -some functions such as ``np.where`` are currently not NumPy universal -functions, but conceivably could become universal functions in the -future. When/if this happens, we will change such overloads from using -``__array_function__`` to the more specialized ``__array_ufunc__``. +We also expect that the mechanism for overriding specific functions +that will initially use the ``__array_function__`` protocol can and will +change in the future. As a concrete example of how we expect to break +behavior in the future, some functions such as ``np.where`` are currently +not NumPy universal functions, but conceivably could become universal +functions in the future. When/if this happens, we will change such overloads +from using ``__array_function__`` to the more specialized ``__array_ufunc__``. Backward compatibility ---------------------- This proposal does not change existing semantics, except for those arguments -that currently have ``__array_function__`` methods, which should be rare. +that currently have ``__array_function__`` attributes, which should be rare. Alternatives @@ -332,13 +612,24 @@ Specialized protocols ~~~~~~~~~~~~~~~~~~~~~ We could (and should) continue to develop protocols like -``__array_ufunc__`` for cohesive subsets of Numpy functionality. +``__array_ufunc__`` for cohesive subsets of NumPy functionality. As mentioned above, if this means that some functions that we overload with ``__array_function__`` should switch to a new protocol instead, that is explicitly OK for as long as ``__array_function__`` retains its experimental status. +Switching to a new protocol should use an abbreviated version of NumPy's +normal deprecation cycle: + +- For a single major release, after checking for any new protocols, NumPy + should still check for ``__array_function__`` methods that implement the + given function. If any argument returns a value other than + ``NotImplemented`` from ``__array_function__``, a descriptive + ``FutureWarning`` should be issued. +- In the next major release, the checks for ``__array_function__`` will be + removed. + Separate namespace ~~~~~~~~~~~~~~~~~~ @@ -347,17 +638,22 @@ either inside or outside of NumPy. This has the advantage of alleviating any possible concerns about backwards compatibility and would provide the maximum freedom for quick -experimentation. In the long term, it would provide a clean abstration +experimentation. In the long term, it would provide a clean abstraction layer, separating NumPy's high level API from default implementations on ``numpy.ndarray`` objects. The downsides are that this would require an explicit opt-in from all existing code, e.g., ``import numpy.api as np``, and in the long term -would result in the maintainence of two separate NumPy APIs. Also, many +would result in the maintenance of two separate NumPy APIs. Also, many functions from ``numpy`` itself are already overloaded (but inadequately), so confusion about high vs. low level APIs in NumPy would still persist. +Alternatively, a separate namespace, e.g., ``numpy.array_only``, could be +created for a non-overloaded version of NumPy's high level API, for cases +where performance with NumPy arrays is a critical concern. This has most +of the same downsides as the separate namespace. + Multiple dispatch ~~~~~~~~~~~~~~~~~ @@ -370,7 +666,7 @@ don't think this approach makes sense for NumPy in the near term. The main reason is that NumPy already has a well-proven dispatching mechanism with ``__array_ufunc__``, based on Python's own dispatching -system for arithemtic, and it would be confusing to add another +system for arithmetic, and it would be confusing to add another mechanism that works in a very different way. This would also be more invasive change to NumPy itself, which would need to gain a multiple dispatch implementation. @@ -384,37 +680,46 @@ would be straightforward to write a shim for a default Implementations in terms of a limited core API ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The internal implemenations of some NumPy functions is extremely simple. -For example: - ``np.stack()`` is implemented in only a few lines of code -by combining indexing with ``np.newaxis``, ``np.concatenate`` and the -``shape`` attribute. - ``np.mean()`` is implemented internally in terms -of ``np.sum()``, ``np.divide()``, ``.astype()`` and ``.shape``. +The internal implementation of some NumPy functions is extremely simple. +For example: + +- ``np.stack()`` is implemented in only a few lines of code by combining + indexing with ``np.newaxis``, ``np.concatenate`` and the ``shape`` attribute. +- ``np.mean()`` is implemented internally in terms of ``np.sum()``, + ``np.divide()``, ``.astype()`` and ``.shape``. This suggests the possibility of defining a minimal "core" ndarray interface, and relying upon it internally in NumPy to implement the full API. This is an attractive option, because it could significantly reduce the work required for new array implementations. -However, this also comes with several downsides: 1. The details of how -NumPy implements a high-level function in terms of overloaded functions -now becomes an implicit part of NumPy's public API. For example, -refactoring ``stack`` to use ``np.block()`` instead of -``np.concatenate()`` internally would now become a breaking change. 2. -Array libraries may prefer to implement high level functions differently -than NumPy. For example, a library might prefer to implement a -fundamental operations like ``mean()`` directly rather than relying on -``sum()`` followed by division. More generally, it's not clear yet what -exactly qualifies as core functionality, and figuring this out could be -a large project. 3. We don't yet have an overloading system for -attributes and methods on array objects, e.g., for accessing ``.dtype`` -and ``.shape``. This should be the subject of a future NEP, but until -then we should be reluctant to rely on these properties. - -Given these concerns, we encourage relying on this approach only in -limited cases. - -Coersion to a NumPy array as a catch-all fallback -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +However, this also comes with several downsides: + +1. The details of how NumPy implements a high-level function in terms of + overloaded functions now becomes an implicit part of NumPy's public API. For + example, refactoring ``stack`` to use ``np.block()`` instead of + ``np.concatenate()`` internally would now become a breaking change. +2. Array libraries may prefer to implement high level functions differently than + NumPy. For example, a library might prefer to implement a fundamental + operations like ``mean()`` directly rather than relying on ``sum()`` followed + by division. More generally, it's not clear yet what exactly qualifies as + core functionality, and figuring this out could be a large project. +3. We don't yet have an overloading system for attributes and methods on array + objects, e.g., for accessing ``.dtype`` and ``.shape``. This should be the + subject of a future NEP, but until then we should be reluctant to rely on + these properties. + +Given these concerns, we think it's valuable to support explicit overloading of +nearly every public function in NumPy's API. This does not preclude the future +possibility of rewriting NumPy functions in terms of simplified core +functionality with ``__array_function__`` and a protocol and/or base class for +ensuring that arrays expose methods and properties like ``numpy.ndarray``. +However, to work well this would require the possibility of implementing +*some* but not all functions with ``__array_function__``, e.g., as described +in the next section. + +Partial implementation of NumPy's API +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ With the current design, classes that implement ``__array_function__`` to overload at least one function implicitly declare an intent to @@ -431,113 +736,222 @@ that assuredly many pandas users rely on. If pandas implemented functions like ``np.nanmean`` would suddenly break on pandas objects by raising TypeError. +Even libraries that reimplement most of NumPy's public API sometimes rely upon +using utility functions from NumPy without a wrapper. For example, both CuPy +and JAX simply `use an alias <https://github.com/numpy/numpy/issues/12974>`_ to +``np.result_type``, which already supports duck-types with a ``dtype`` +attribute. + With ``__array_ufunc__``, it's possible to alleviate this concern by casting all arguments to numpy arrays and re-calling the ufunc, but the heterogeneous function signatures supported by ``__array_function__`` make it impossible to implement this generic fallback behavior for ``__array_function__``. -We could resolve this issue by change the handling of return values in -``__array_function__`` in either of two possible ways: 1. Change the -meaning of all arguments returning ``NotImplemented`` to indicate that -all arguments should be coerced to NumPy arrays instead. However, many -array libraries (e.g., scipy.sparse) really don't want implicit -conversions to NumPy arrays, and often avoid implementing ``__array__`` -for exactly this reason. Implicit conversions can result in silent bugs -and performance degradation. 2. Use another sentinel value of some sort -to indicate that a class implementing part of the higher level array API -is coercible as a fallback, e.g., a return value of -``np.NotImplementedButCoercible`` from ``__array_function__``. - -If we take this second approach, we would need to define additional -rules for how coercible array arguments are coerced, e.g., - Would we -try for ``__array_function__`` overloads again after coercing coercible -arguments? - If so, would we coerce coercible arguments one-at-a-time, -or all-at-once? - -These are slightly tricky design questions, so for now we propose to -defer this issue. We can always implement -``np.NotImplementedButCoercible`` at some later time if it proves -critical to the numpy community in the future. Importantly, we don't -think this will stop critical libraries that desire to implement most of -the high level NumPy API from adopting this proposal. - -NOTE: If you are reading this NEP in its draft state and disagree, -please speak up on the mailing list! - -Drawbacks of this approach --------------------------- - -Future difficulty extending NumPy's API -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +We considered three possible ways to resolve this issue, but none were +entirely satisfactory: + +1. Change the meaning of all arguments returning ``NotImplemented`` from + ``__array_function__`` to indicate that all arguments should be coerced to + NumPy arrays and the operation should be retried. However, many array + libraries (e.g., scipy.sparse) really don't want implicit conversions to + NumPy arrays, and often avoid implementing ``__array__`` for exactly this + reason. Implicit conversions can result in silent bugs and performance + degradation. + + Potentially, we could enable this behavior only for types that implement + ``__array__``, which would resolve the most problematic cases like + scipy.sparse. But in practice, a large fraction of classes that present a + high level API like NumPy arrays already implement ``__array__``. This would + preclude reliable use of NumPy's high level API on these objects. + +2. Use another sentinel value of some sort, e.g., + ``np.NotImplementedButCoercible``, to indicate that a class implementing + part of NumPy's higher level array API is coercible as a fallback. If all + arguments return ``NotImplementedButCoercible``, arguments would be coerced + and the operation would be retried. + + Unfortunately, correct behavior after encountering + ``NotImplementedButCoercible`` is not always obvious. Particularly + challenging is the "mixed" case where some arguments return + ``NotImplementedButCoercible`` and others return ``NotImplemented``. + Would dispatching be retried after only coercing the "coercible" arguments? + If so, then conceivably we could end up looping through the dispatching + logic an arbitrary number of times. Either way, the dispatching rules would + definitely get more complex and harder to reason about. + +3. Allow access to NumPy's implementation of functions, e.g., in the form of + a publicly exposed ``__skip_array_function__`` attribute on the NumPy + functions. This would allow for falling back to NumPy's implementation by + using ``func.__skip_array_function__`` inside ``__array_function__`` + methods, and could also potentially be used to be used to avoid the + overhead of dispatching. However, it runs the risk of potentially exposing + details of NumPy's implementations for NumPy functions that do not call + ``np.asarray()`` internally. See + `this note <https://mail.python.org/pipermail/numpy-discussion/2019-May/079541.html>`_ + for a summary of the full discussion. + +These solutions would solve real use cases, but at the cost of additional +complexity. We would like to gain experience with how ``__array_function__`` is +actually used before making decisions that would be difficult to roll back. + +A magic decorator that inspects type annotations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In principle, Python 3 type annotations contain sufficient information to +automatically create most ``dispatcher`` functions. It would be convenient to +use these annotations to dispense with the need for manually writing +dispatchers, e.g., + +.. code:: python + + @array_function_dispatch + def broadcast_to(array: ArrayLike + shape: Tuple[int, ...], + subok: bool = False): + ... # existing definition of np.broadcast_to + +This would require some form of automatic code generation, either at compile or +import time. -One downside of passing on all arguments directly on to -``__array_function__`` is that it makes it hard to extend the signatures -of overloaded NumPy functions with new arguments, because adding even an -optional keyword argument would break existing overloads. +We think this is an interesting possible extension to consider in the future. We +don't think it makes sense to do so now, because code generation involves +tradeoffs and NumPy's experience with type annotations is still +`quite limited <https://github.com/numpy/numpy-stubs>`_. Even if NumPy +was Python 3 only (which will happen +`sometime in 2019 <http://www.numpy.org/neps/nep-0014-dropping-python2.7-proposal.html>`_), +we aren't ready to annotate NumPy's codebase directly yet. -This is not a new problem for NumPy. NumPy has occasionally changed the -signature for functions in the past, including functions like -``numpy.sum`` which support overloads. +Support for implementation-specific arguments +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -For adding new keyword arguments that do not change default behavior, we -would only include these as keyword arguments when they have changed -from default values. This is similar to `what NumPy already has -done <https://github.com/numpy/numpy/blob/v1.14.2/numpy/core/fromnumeric.py#L1865-L1867>`_, -e.g., for the optional ``keepdims`` argument in ``sum``: +We could allow ``__array_function__`` implementations to add their own +optional keyword arguments by including ``**ignored_kwargs`` in dispatcher +functions, e.g., .. code:: python - def sum(array, ..., keepdims=np._NoValue): - kwargs = {} - if keepdims is not np._NoValue: - kwargs['keepdims'] = keepdims - return array.sum(..., **kwargs) + def _concatenate_dispatcher(arrays, axis=None, out=None, **ignored_kwargs): + ... # same implementation of _concatenate_dispatcher as above + +Implementation-specific arguments are somewhat common in libraries that +otherwise emulate NumPy's higher level API (e.g., ``dask.array.sum()`` adds +``split_every`` and ``tensorflow.reduce_sum()`` adds ``name``). Supporting +them in NumPy would be particularly useful for libraries that implement new +high-level array functions on top of NumPy functions, e.g., + +.. code:: python -In other cases, such as deprecated arguments, preserving the existing -behavior of overloaded functions may not be possible. Libraries that use -``__array_function__`` should be aware of this risk: we don't propose to -freeze NumPy's API in stone any more than it already is. + def mean_squared_error(x, y, **kwargs): + return np.mean((x - y) ** 2, **kwargs) -Difficulty adding implementation specific arguments -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Otherwise, we would need separate versions of ``mean_squared_error`` for each +array implementation in order to pass implementation-specific arguments to +``mean()``. -Some array implementations generally follow NumPy's API, but have -additional optional keyword arguments (e.g., ``dask.array.sum()`` has -``split_every`` and ``tensorflow.reduce_sum()`` has ``name``). A generic -dispatching library could potentially pass on all unrecognized keyword -argument directly to the implementation, but extending ``np.sum()`` to -pass on ``**kwargs`` would entail public facing changes in NumPy. -Customizing the detailed behavior of array libraries will require using -library specific functions, which could be limiting in the case of -libraries that consume the NumPy API such as xarray. +We wouldn't allow adding optional positional arguments, because these are +reserved for future use by NumPy itself, but conflicts between keyword arguments +should be relatively rare. +However, this flexibility would come with a cost. In particular, it implicitly +adds ``**kwargs`` to the signature for all wrapped NumPy functions without +actually including it (because we use ``functools.wraps``). This means it is +unlikely to work well with static analysis tools, which could report invalid +arguments. Likewise, there is a price in readability: these optional arguments +won't be included in the docstrings for NumPy functions. + +It's not clear that this tradeoff is worth it, so we propose to leave this out +for now. Adding implementation-specific arguments will require using those +libraries directly. + +Other possible choices for the protocol +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The array function ``__array_function__`` includes only two arguments, ``func`` +and ``types``, that provide information about the context of the function call. + +``func`` is part of the protocol because there is no way to avoid it: +implementations need to be able to dispatch by matching a function to NumPy's +public API. + +``types`` is included because we can compute it almost for free as part of +collecting ``__array_function__`` implementations to call in +``implement_array_function``. We also think it will be used +by many ``__array_function__`` methods, which otherwise would need to extract +this information themselves. It would be equivalently easy to provide single +instances of each type, but providing only types seemed cleaner. + +Taking this even further, it was suggested that ``__array_function__`` should be +a ``classmethod``. We agree that it would be a little cleaner to remove the +redundant ``self`` argument, but feel that this minor clean-up would not be +worth breaking from the precedence of ``__array_ufunc__``. + +There are two other arguments that we think *might* be important to pass to +``__array_ufunc__`` implementations: + +- Access to the non-dispatched implementation (i.e., before wrapping with + ``array_function_dispatch``) in ``ndarray.__array_function__`` would allow + us to drop special case logic for that method from + ``implement_array_function``. +- Access to the ``dispatcher`` function passed into + ``array_function_dispatch()`` would allow ``__array_function__`` + implementations to determine the list of "array-like" arguments in a generic + way by calling ``dispatcher(*args, **kwargs)``. This *could* be useful for + ``__array_function__`` implementations that dispatch based on the value of an + array attribute (e.g., ``dtype`` or ``units``) rather than directly on the + array type. + +We have left these out for now, because we don't know that they are necessary. +If we want to include them in the future, the easiest way to do so would be to +update the ``array_function_dispatch`` decorator to add them as function +attributes. + +Callable objects generated at runtime +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +NumPy has some APIs that define callable objects *dynamically*, such as +``vectorize`` and methods on ``random.RandomState`` object. Examples can +also be found in other core libraries in the scientific Python stack, e.g., +distribution objects in scipy.stats and model objects in scikit-learn. It would +be nice to be able to write overloads for such callables, too. This presents a +challenge for the ``__array_function__`` protocol, because unlike the case for +functions there is no public object in the ``numpy`` namespace to pass into +the ``func`` argument. + +We could potentially handle this by establishing an alternative convention +for how the ``func`` argument could be inspected, e.g., by using +``func.__self__`` to obtain the class object and ``func.__func__`` to return +the unbound function object. However, some caution is in order, because +this would immesh what are currently implementation details as a permanent +features of the interface, such as the fact that ``vectorize`` is implemented as a +class rather than closure, or whether a method is implemented directly or using +a descriptor. + +Given the complexity and the limited use cases, we are also deferring on this +issue for now, but we are confident that ``__array_function__`` could be +expanded to accommodate these use cases in the future if need be. Discussion ---------- -Various alternatives to this proposal were discussed in a few Github issues: +Various alternatives to this proposal were discussed in a few GitHub issues: -1. `pydata/sparse #1 <https://github.com/pydata/sparse/issues/1>`_ -2. `numpy/numpy #11129 <https://github.com/numpy/numpy/issues/11129>`_ +1. `pydata/sparse #1 <https://github.com/pydata/sparse/issues/1>`_ +2. `numpy/numpy #11129 <https://github.com/numpy/numpy/issues/11129>`_ Additionally it was the subject of `a blogpost -<http://matthewrocklin.com/blog/work/2018/05/27/beyond-numpy>`_ Following this +<http://matthewrocklin.com/blog/work/2018/05/27/beyond-numpy>`_. Following this it was discussed at a `NumPy developer sprint <https://scisprints.github.io/#may-numpy-developer-sprint>`_ at the `UC Berkeley Institute for Data Science (BIDS) <https://bids.berkeley.edu/>`_. - -References and Footnotes ------------------------- - -.. [1] Each NEP must either be explicitly labeled as placed in the public domain (see - this NEP as an example) or licensed under the `Open Publication License`_. - -.. _Open Publication License: http://www.opencontent.org/openpub/ - +Detailed discussion of this proposal itself can be found on the +`the mailing list <https://mail.python.org/pipermail/numpy-discussion/2018-June/078127.html>`_ and relevant pull requests +(`1 <https://github.com/numpy/numpy/pull/11189>`_, +`2 <https://github.com/numpy/numpy/pull/11303#issuecomment-396638175>`_, +`3 <https://github.com/numpy/numpy/pull/11374>`_) Copyright --------- -This document has been placed in the public domain. [1]_ +This document has been placed in the public domain. diff --git a/doc/neps/nep-0019-rng-policy.rst b/doc/neps/nep-0019-rng-policy.rst index de9164bbae3b..c5c46603bbc6 100644 --- a/doc/neps/nep-0019-rng-policy.rst +++ b/doc/neps/nep-0019-rng-policy.rst @@ -1,12 +1,15 @@ -============================== -Random Number Generator Policy -============================== +.. _NEP19: + +======================================= +NEP 19 — Random number generator policy +======================================= :Author: Robert Kern <robert.kern@gmail.com> -:Status: Draft +:Status: Final :Type: Standards Track :Created: 2018-05-24 - +:Updated: 2019-05-21 +:Resolution: https://mail.python.org/pipermail/numpy-discussion/2018-July/078380.html Abstract -------- @@ -91,23 +94,13 @@ those contributors simply walked away. Implementation -------------- -We propose first freezing ``RandomState`` as it is and developing a new RNG -subsystem alongside it. This allows anyone who has been relying on our old -stream-compatibility guarantee to have plenty of time to migrate. -``RandomState`` will be considered deprecated, but with a long deprecation -cycle, at least a few years. Deprecation warnings will start silent but become -increasingly noisy over time. Bugs in the current state of the code will *not* -be fixed if fixing them would impact the stream. However, if changes in the -rest of ``numpy`` would break something in the ``RandomState`` code, we will -fix ``RandomState`` to continue working (for example, some change in the -C API). No new features will be added to ``RandomState``. Users should -migrate to the new subsystem as they are able to. - -Work on a proposed `new PRNG subsystem -<https://github.com/bashtage/randomgen>`_ is already underway. The specifics -of the new design are out of scope for this NEP and up for much discussion, but -we will discuss general policies that will guide the evolution of whatever code -is adopted. +Work on a proposed new Pseudo Random Number Generator (PRNG) subsystem is +already underway in the randomgen_ +project. The specifics of the new design are out of scope for this NEP and up +for much discussion, but we will discuss general policies that will guide the +evolution of whatever code is adopted. We will also outline just a few of the +requirements that such a new system must have to support the policy proposed in +this NEP. First, we will maintain API source compatibility just as we do with the rest of ``numpy``. If we *must* make a breaking change, we will only do so with an @@ -116,66 +109,160 @@ appropriate deprecation period and warnings. Second, breaking stream-compatibility in order to introduce new features or improve performance will be *allowed* with *caution*. Such changes will be considered features, and as such will be no faster than the standard release -cadence of features (i.e. on ``X.Y`` releases, never ``X.Y.Z``). Slowness is -not a bug. Correctness bug fixes that break stream-compatibility can happen on -bugfix releases, per usual, but developers should consider if they can wait -until the next feature release. We encourage developers to strongly weight -user’s pain from the break in stream-compatibility against the improvements. -One example of a worthwhile improvement would be to change algorithms for -a significant increase in performance, for example, moving from the `Box-Muller -transform <https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform>`_ method -of Gaussian variate generation to the faster `Ziggurat algorithm -<https://en.wikipedia.org/wiki/Ziggurat_algorithm>`_. An example of an -unworthy improvement would be tweaking the Ziggurat tables just a little bit. - -Any new design for the RNG subsystem will provide a choice of different core -uniform PRNG algorithms. We will be more strict about a select subset of -methods on these core PRNG objects. They MUST guarantee stream-compatibility -for a minimal, specified set of methods which are chosen to make it easier to -compose them to build other distributions. Namely, +cadence of features (i.e. on ``X.Y`` releases, never ``X.Y.Z``). Slowness will +not be considered a bug for this purpose. Correctness bug fixes that break +stream-compatibility can happen on bugfix releases, per usual, but developers +should consider if they can wait until the next feature release. We encourage +developers to strongly weight user’s pain from the break in +stream-compatibility against the improvements. One example of a worthwhile +improvement would be to change algorithms for a significant increase in +performance, for example, moving from the `Box-Muller transform +<https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform>`_ method of +Gaussian variate generation to the faster `Ziggurat algorithm +<https://en.wikipedia.org/wiki/Ziggurat_algorithm>`_. An example of a +discouraged improvement would be tweaking the Ziggurat tables just a little bit +for a small performance improvement. + +Any new design for the random subsystem will provide a choice of different core +uniform PRNG algorithms. A promising design choice is to make these core +uniform PRNGs their own lightweight objects with a minimal set of methods +(randomgen_ calls them “BitGenerators”). The broader set of non-uniform +distributions will be its own class that holds a reference to one of these core +uniform PRNG objects and simply delegates to the core uniform PRNG object when +it needs uniform random numbers (randomgen_ calls this the Generator). To +borrow an example from randomgen_, the +class ``MT19937`` is a BitGenerator that implements the classic Mersenne Twister +algorithm. The class ``Generator`` wraps around the BitGenerator to provide +all of the non-uniform distribution methods:: + + # This is not the only way to instantiate this object. + # This is just handy for demonstrating the delegation. + >>> bg = MT19937(seed) + >>> rg = Generator(bg) + >>> x = rg.standard_normal(10) + +We will be more strict about a select subset of methods on these BitGenerator +objects. They MUST guarantee stream-compatibility for a specified set +of methods which are chosen to make it easier to compose them to build other +distributions and which are needed to abstract over the implementation details +of the variety of BitGenerator algorithms. Namely, * ``.bytes()`` - * ``.random_uintegers()`` - * ``.random_sample()`` - -Furthermore, the new design should also provide one generator class (we shall -call it ``StableRandom`` for discussion purposes) that provides a slightly -broader subset of distribution methods for which stream-compatibility is -*guaranteed*. The point of ``StableRandom`` is to provide something that can -be used in unit tests so projects that currently have tests which rely on the -precise stream can be migrated off of ``RandomState``. For the best -transition, ``StableRandom`` should use as its core uniform PRNG the current -MT19937 algorithm. As best as possible, the API for the distribution methods -that are provided on ``StableRandom`` should match their counterparts on -``RandomState``. They should provide the same stream that the current version -of ``RandomState`` does. Because their intended use is for unit tests, we do -not need the performance improvements from the new algorithms that will be -introduced by the new subsystem. - -The list of ``StableRandom`` methods should be chosen to support unit tests: - - * ``.randint()`` - * ``.uniform()`` - * ``.normal()`` - * ``.standard_normal()`` - * ``.choice()`` - * ``.shuffle()`` - * ``.permutation()`` - - -Not Versioning --------------- + * ``integers()`` (formerly ``.random_integers()``) + * ``random()`` (formerly ``.random_sample()``) + +The distributions class (``Generator``) SHOULD have all of the same +distribution methods as ``RandomState`` with close-enough function signatures +such that almost all code that currently works with ``RandomState`` instances +will work with ``Generator`` instances (ignoring the precise stream +values). Some variance will be allowed for integer distributions: in order to +avoid some of the cross-platform problems described above, these SHOULD be +rewritten to work with ``uint64`` numbers on all platforms. + +.. _randomgen: https://github.com/bashtage/randomgen + + +Supporting Unit Tests +::::::::::::::::::::: + +Because we did make a strong stream-compatibility guarantee early in numpy’s +life, reliance on stream-compatibility has grown beyond reproducible +simulations. One use case that remains for stream-compatibility across numpy +versions is to use pseudorandom streams to generate test data in unit tests. +With care, many of the cross-platform instabilities can be avoided in the +context of small unit tests. + +The new PRNG subsystem MUST provide a second, legacy distributions class that +uses the same implementations of the distribution methods as the current +version of ``numpy.random.RandomState``. The methods of this class will have +strict stream-compatibility guarantees, even stricter than the current policy. +It is intended that this class will no longer be modified, except to keep it +working when numpy internals change. All new development should go into the +primary distributions class. Bug fixes that change the stream SHALL NOT be +made to ``RandomState``; instead, buggy distributions should be made to warn +when they are buggy. The purpose of ``RandomState`` will be documented as +providing certain fixed functionality for backwards compatibility and stable +numbers for the limited purpose of unit testing, and not making whole programs +reproducible across numpy versions. + +This legacy distributions class MUST be accessible under the name +``numpy.random.RandomState`` for backwards compatibility. All current ways of +instantiating ``numpy.random.RandomState`` with a given state should +instantiate the Mersenne Twister BitGenerator with the same state. The legacy +distributions class MUST be capable of accepting other BitGenerators. The +purpose +here is to ensure that one can write a program with a consistent BitGenerator +state with a mixture of libraries that may or may not have upgraded from +``RandomState``. Instances of the legacy distributions class MUST respond +``True`` to ``isinstance(rg, numpy.random.RandomState)`` because there is +current utility code that relies on that check. Similarly, old pickles of +``numpy.random.RandomState`` instances MUST unpickle correctly. + + +``numpy.random.*`` +:::::::::::::::::: + +The preferred best practice for getting reproducible pseudorandom numbers is to +instantiate a generator object with a seed and pass it around. The implicit +global ``RandomState`` behind the ``numpy.random.*`` convenience functions can +cause problems, especially when threads or other forms of concurrency are +involved. Global state is always problematic. We categorically recommend +avoiding using the convenience functions when reproducibility is involved. + +That said, people do use them and use ``numpy.random.seed()`` to control the +state underneath them. It can be hard to categorize and count API usages +consistently and usefully, but a very common usage is in unit tests where many +of the problems of global state are less likely. + +This NEP does not propose removing these functions or changing them to use the +less-stable ``Generator`` distribution implementations. Future NEPs +might. + +Specifically, the initial release of the new PRNG subsystem SHALL leave these +convenience functions as aliases to the methods on a global ``RandomState`` +that is initialized with a Mersenne Twister BitGenerator object. A call to +``numpy.random.seed()`` will be forwarded to that BitGenerator object. In +addition, the global ``RandomState`` instance MUST be accessible in this +initial release by the name ``numpy.random.mtrand._rand``: Robert Kern long ago +promised ``scikit-learn`` that this name would be stable. Whoops. + +In order to allow certain workarounds, it MUST be possible to replace the +BitGenerator underneath the global ``RandomState`` with any other BitGenerator +object (we leave the precise API details up to the new subsystem). Calling +``numpy.random.seed()`` thereafter SHOULD just pass the given seed to the +current BitGenerator object and not attempt to reset the BitGenerator to the +Mersenne Twister. The set of ``numpy.random.*`` convenience functions SHALL +remain the same as they currently are. They SHALL be aliases to the +``RandomState`` methods and not the new less-stable distributions class +(``Generator``, in the examples above). Users who want to get the fastest, best +distributions can follow best practices and instantiate generator objects explicitly. + +This NEP does not propose that these requirements remain in perpetuity. After +we have experience with the new PRNG subsystem, we can and should revisit these +issues in future NEPs. + + +Alternatives +------------ + +Versioning +:::::::::: For a long time, we considered that the way to allow algorithmic improvements while maintaining the stream was to apply some form of versioning. That is, every time we make a stream change in one of the distributions, we increment some version number somewhere. ``numpy.random`` would keep all past versions -of the code, and there would be a way to get the old versions. Proposals of -how to do this exactly varied widely, but we will not exhaustively list them -here. We spent years going back and forth on these designs and were not able -to find one that sufficed. Let that time lost, and more importantly, the -contributors that we lost while we dithered, serve as evidence against the -notion. +of the code, and there would be a way to get the old versions. + +We will not be doing this. If one needs to get the exact bit-for-bit results +from a given version of ``numpy``, whether one uses random numbers or not, one +should use the exact version of ``numpy``. + +Proposals of how to do RNG versioning varied widely, and we will not +exhaustively list them here. We spent years going back and forth on these +designs and were not able to find one that sufficed. Let that time lost, and +more importantly, the contributors that we lost while we dithered, serve as +evidence against the notion. Concretely, adding in versioning makes maintenance of ``numpy.random`` difficult. Necessarily, we would be keeping lots of versions of the same code @@ -195,11 +282,49 @@ is to pin the release of ``numpy`` as a whole, versioning ``RandomState`` alone is superfluous. +``StableRandom`` +:::::::::::::::: + +A previous version of this NEP proposed to leave ``RandomState`` completely +alone for a deprecation period and build the new subsystem alongside with new +names. To satisfy the unit testing use case, it proposed introducing a small +distributions class nominally called ``StableRandom``. It would have provided +a small subset of distribution methods that were considered most useful in unit +testing, but not the full set such that it would be too likely to be used +outside of the testing context. + +During discussion about this proposal, it became apparent that there was no +satisfactory subset. At least some projects used a fairly broad selection of +the ``RandomState`` methods in unit tests. + +Downstream project owners would have been forced to modify their code to +accommodate the new PRNG subsystem. Some modifications might be simply +mechanical, but the bulk of the work would have been tedious churn for no +positive improvement to the downstream project, just avoiding being broken. + +Furthermore, under this old proposal, we would have had a quite lengthy +deprecation period where ``RandomState`` existed alongside the new system of +BitGenerator and Generator classes. Leaving the implementation of +``RandomState`` fixed meant that it could not use the new BitGenerator state +objects. Developing programs that use a mixture of libraries that have and +have not upgraded would require managing two sets of PRNG states. This would +notionally have been time-limited, but we intended the deprecation to be very +long. + +The current proposal solves all of these problems. All current usages of +``RandomState`` will continue to work in perpetuity, though some may be +discouraged through documentation. Unit tests can continue to use the full +complement of ``RandomState`` methods. Mixed ``RandomState/Generator`` +code can safely share the common BitGenerator state. Unmodified ``RandomState`` +code can make use of the new features of alternative BitGenerator-like settable +streams. + + Discussion ---------- -- https://mail.python.org/pipermail/numpy-discussion/2018-January/077608.html -- https://github.com/numpy/numpy/pull/10124#issuecomment-350876221 +- `NEP discussion <https://mail.python.org/pipermail/numpy-discussion/2018-June/078126.html>`_ +- `Earlier discussion <https://mail.python.org/pipermail/numpy-discussion/2018-January/077608.html>`_ Copyright diff --git a/doc/neps/nep-0020-gufunc-signature-enhancement.rst b/doc/neps/nep-0020-gufunc-signature-enhancement.rst new file mode 100644 index 000000000000..80ee75f5f71e --- /dev/null +++ b/doc/neps/nep-0020-gufunc-signature-enhancement.rst @@ -0,0 +1,259 @@ +.. _NEP20: + +=============================================================== +NEP 20 — Expansion of generalized universal function signatures +=============================================================== + +:Author: Marten van Kerkwijk <mhvk@astro.utoronto.ca> +:Status: Final +:Type: Standards Track +:Created: 2018-06-10 +:Resolution: https://mail.python.org/pipermail/numpy-discussion/2018-April/077959.html, + https://mail.python.org/pipermail/numpy-discussion/2018-May/078078.html + +.. note:: The proposal to add fixed (i) and flexible (ii) dimensions + was accepted, while that to add broadcastable (iii) ones was deferred. + +Abstract +-------- + +Generalized universal functions are, as their name indicates, generalization +of universal functions: they operate on non-scalar elements. Their signature +describes the structure of the elements they operate on, with names linking +dimensions of the operands that should be the same. Here, it is proposed to +extend the signature to allow the signature to indicate that a dimension (i) +has fixed size; (ii) can be absent; and (iii) can be broadcast. + +Detailed description +-------------------- + +Each part of the proposal is driven by specific needs [1]_. + +1. Fixed-size dimensions. Code working with spatial vectors often explicitly + is for 2 or 3-dimensional space (e.g., the code from the `Standards Of + Fundamental Astronomy <http://www.iausofa.org/>`_, which the author hopes + to wrap using gufuncs for astropy [2]_). The signature should be able to + indicate that. E.g., the signature of a function that converts a polar + angle to a two-dimensional cartesian unit vector would currently have to be + ``()->(n)``, with there being no way to indicate that ``n`` has to equal 2. + Indeed, this signature is particularly annoying since without putting in an + output argument, the current gufunc wrapper code fails because it cannot + determine ``n``. Similarly, the signature for an cross product of two + 3-dimensional vectors has to be ``(n),(n)->(n)``, with again no way to + indicate that ``n`` has to equal 3. Hence, the proposal here to allow one + to give numerical values in addition to variable names. Thus, angle to + two-dimensional unit vector would be ``()->(2)``; two angles to + three-dimensional unit vector ``(),()->(3)``; and that for the cross + product of two three-dimensional vectors would be ``(3),(3)->(3)``. + +2. Possibly missing dimensions. This part is almost entirely driven by the + wish to wrap ``matmul`` in a gufunc. ``matmul`` stands for matrix + multiplication, and if it did only that, it could be covered with the + signature ``(m,n),(n,p)->(m,p)``. However, it has special cases for when a + dimension is missing, allowing either argument to be treated as a single + vector, with the function thus becoming, effectively, vector-matrix, + matrix-vector, or vector-vector multiplication (but with no + broadcasting). To support this, it is suggested to allow postfixing a + dimension name with a question mark to indicate that the dimension does not + necessarily have to be present. + + With this addition, the signature for ``matmul`` can be expressed as + ``(m?,n),(n,p?)->(m?,p?)``. This indicates that if, e.g., the second + operand has only one dimension, for the purposes of the elementary function + it will be treated as if that input has core shape ``(n, 1)``, and the + output has the corresponding core shape of ``(m, 1)``. The actual output + array, however, has the flexible dimension removed, i.e., it will have + shape ``(..., m)``. Similarly, if both arguments have only a single + dimension, the inputs will be presented as having shapes ``(1, n)`` and + ``(n, 1)`` to the elementary function, and the output as ``(1, 1)``, while + the actual output array returned will have shape ``()``. In this way, the + signature allows one to use a single elementary function for four related + but different signatures, ``(m,n),(n,p)->(m,p)``, ``(n),(n,p)->(p)``, + ``(m,n),(n)->(m)`` and ``(n),(n)->()``. + +3. Dimensions that can be broadcast. For some applications, broadcasting + between operands makes sense. For instance, an ``all_equal`` function that + compares vectors in arrays could have a signature ``(n),(n)->()``, but this + forces both operands to be arrays, while it would be useful also to check + that, e.g., all parts of a vector are constant (maybe zero). The proposal + is to allow the implementer of a gufunc to indicate that a dimension can be + broadcast by post-fixing the dimension name with ``|1``. Hence, the + signature for ``all_equal`` would become ``(n|1),(n|1)->()``. The + signature seems handy more generally for "chained ufuncs"; e.g., another + application might be in a putative ufunc implementing ``sumproduct``. + + Another example that arose in the discussion, is of a weighted mean, which + might look like ``weighted_mean(y, sigma[, axis, ...])``, returning the + mean and its uncertainty. With a signature of ``(n),(n)->(),()``, one + would be forced to always give as many sigmas as there are data points, + while broadcasting would allow one to give a single sigma for all points + (which is still useful to calculate the uncertainty on the mean). + +Implementation +-------------- + +The proposed changes have all been implemented [3]_, [4]_, [5]_. These PRs +extend the ufunc structure with two new fields, each of size equal to the +number of distinct dimensions, with ``core_dim_sizes`` holding possibly fixed +sizes, and ``core_dim_flags`` holding flags indicating whether a dimension can +be missing or broadcast. To ensure we can distinguish between this new +version and previous versions, an unused entry ``reserved1`` is repurposed as +a version number. + +In the implementation, care is taken that to the elementary function flagged +dimensions are not treated any differently than non-flagged ones: for +instance, sizes of fixed-size dimensions are still passed on to the elementary +function (but the loop can now count on that size being equal to the fixed one +given in the signature). + +An implementation detail to be decided upon is whether it might be handy to +have a summary of all flags. This could possibly be stored in ``core_enabled`` +(which currently is a bool), with non-zero continuing to indicate a gufunc, +but specific flags indicating whether or not a gufunc uses fixed, flexible, or +broadcastable dimensions. + +With the above, the formal definition of the syntax would become [4]_:: + + <Signature> ::= <Input arguments> "->" <Output arguments> + <Input arguments> ::= <Argument list> + <Output arguments> ::= <Argument list> + <Argument list> ::= nil | <Argument> | <Argument> "," <Argument list> + <Argument> ::= "(" <Core dimension list> ")" + <Core dimension list> ::= nil | <Core dimension> | + <Core dimension> "," <Core dimension list> + <Core dimension> ::= <Dimension name> <Dimension modifier> + <Dimension name> ::= valid Python variable name | valid integer + <Dimension modifier> ::= nil | "|1" | "?" + +#. All quotes are for clarity. +#. Unmodified core dimensions that share the same name must have the same size. + Each dimension name typically corresponds to one level of looping in the + elementary function's implementation. +#. White spaces are ignored. +#. An integer as a dimension name freezes that dimension to the value. +#. If a name if suffixed with the ``|1`` modifier, it is allowed to broadcast + against other dimensions with the same name. All input dimensions + must share this modifier, while no output dimensions should have it. +#. If the name is suffixed with the ``?`` modifier, the dimension is a core + dimension only if it exists on all inputs and outputs that share it; + otherwise it is ignored (and replaced by a dimension of size 1 for the + elementary function). + +Examples of signatures [4]_: + ++----------------------------+-----------------------------------+ +| Signature | Possible use | ++----------------------------+-----------------------------------+ +| ``(),()->()`` | Addition | ++----------------------------+-----------------------------------+ +| ``(i)->()`` | Sum over last axis | ++----------------------------+-----------------------------------+ +| ``(i|1),(i|1)->()`` | Test for equality along axis, | +| | allowing comparison with a scalar | ++----------------------------+-----------------------------------+ +| ``(i),(i)->()`` | inner vector product | ++----------------------------+-----------------------------------+ +| ``(m,n),(n,p)->(m,p)`` | matrix multiplication | ++----------------------------+-----------------------------------+ +| ``(n),(n,p)->(p)`` | vector-matrix multiplication | ++----------------------------+-----------------------------------+ +| ``(m,n),(n)->(m)`` | matrix-vector multiplication | ++----------------------------+-----------------------------------+ +| ``(m?,n),(n,p?)->(m?,p?)`` | all four of the above at once, | +| | except vectors cannot have loop | +| | dimensions (ie, like ``matmul``) | ++----------------------------+-----------------------------------+ +| ``(3),(3)->(3)`` | cross product for 3-vectors | ++----------------------------+-----------------------------------+ +| ``(i,t),(j,t)->(i,j)`` | inner over the last dimension, | +| | outer over the second to last, | +| | and loop/broadcast over the rest. | ++----------------------------+-----------------------------------+ + +Backward compatibility +---------------------- + +One possible worry is the change in ufunc structure. For most applications, +which call ``PyUFunc_FromDataAndSignature``, this is entirely transparent. +Furthermore, by repurposing ``reserved1`` as a version number, code compiled +against older versions of numpy will continue to work (though one will get a +warning upon import of that code with a newer version of numpy), except if +code explicitly changes the ``reserved1`` entry. + +Alternatives +------------ + +It was suggested instead of extending the signature, to have multiple +dispatch, so that, e.g., ``matmul`` would simply have the multiple signatures +it supports, i.e., instead of ``(m?,n),(n,p?)->(m?,p?)`` one would have +``(m,n),(n,p)->(m,p) | (n),(n,p)->(p) | (m,n),(n)->(m) | (n),(n)->()``. A +disadvantage of this is that the developer now has to make sure that the +elementary function can deal with these different signatures. Furthermore, +the expansion quickly becomes cumbersome. For instance, for the ``all_equal`` +signature of ``(n|1),(n|1)->()``, one would have to have five entries: +``(n),(n)->() | (n),(1)->() | (1),(n)->() | (n),()->() | (),(n)->()``. For +signatures like ``(m|1,n|1,o|1),(m|1,n|1,o|1)->()`` (from the ``cube_equal`` +test case in [4]_), it is not even worth writing out the expansion. + +For broadcasting, the alternative suffix of ``^`` was suggested (as +broadcasting can be thought of as increasing the size of the array). This +seems less clear. Furthermore, it was wondered whether it should not just be +an all-or-nothing flag. This could be the case, though given the postfix +for flexible dimensions, arguably another postfix is clearer (as is the +implementation). + +Discussion +---------- + +The proposals here were discussed at fair length on the mailing list [6]_, +[7]_. The main points of contention were whether the use cases were +sufficiently strong. In particular, for frozen dimensions, it was argued that +checks on the right number could be put in loop selection code. This seems +much less clear for no benefit. + +For broadcasting, the lack of examples of elementary functions that might need +it was noted, with it being questioned whether something like ``all_equal`` +was best done with a gufunc rather than as a special method on ``np.equal``. +One counter-argument to this would be that there is an actual PR for +``all_equal`` [8]_. Another that even if one were to use a method, it would +be good to be able to express their signature (just as is possible at least +for ``reduce`` and ``accumulate``). + +A final argument was that we were making the gufuncs too complex. This +arguably holds for the dimensions that can be omitted, but that also has the +strongest use case. The frozen dimensions has a very simple implementation and +its meaning is obvious. The ability to broadcast is simple too, once the +flexible dimensions are supported. + +References and Footnotes +------------------------ + +.. [1] Identified needs and suggestions for the implementation are not all by + the author. In particular, the suggestion for fixed dimensions and + initial implementation was by Jaime Frio (`gh-5015 + <https://github.com/numpy/numpy/pull/5015>`_), the suggestion of ``?`` + to indicate dimensions can be omitted was by Nathaniel Smith, and the + initial implementation of that by Matti Picus (`gh-11132 + <https://github.com/numpy/numpy/pull/11132>`_). +.. [2] `wrap ERFA functions in gufuncs + <https://github.com/astropy/astropy/pull/7502>`_ (`ERFA + <https://github.com/liberfa/erfa>`_) is the less stringently licensed + version of `Standards Of Fundamental Astronomy + <http://www.iausofa.org/>`_ +.. [3] `fixed-size and flexible dimensions + <https://github.com/numpy/numpy/pull/11175>`_ +.. [4] `broadcastable dimensions + <https://github.com/numpy/numpy/pull/11179>`_ +.. [5] `use in matmul <https://github.com/numpy/numpy/pull/11133>`_ +.. [6] Discusses implementations for ``matmul``: + https://mail.python.org/pipermail/numpy-discussion/2018-May/077972.html, + https://mail.python.org/pipermail/numpy-discussion/2018-May/078021.html +.. [7] Broadcasting: + https://mail.python.org/pipermail/numpy-discussion/2018-May/078078.html +.. [8] `Logical gufuncs <https://github.com/numpy/numpy/pull/8528>`_ (includes + ``all_equal``) + +Copyright +--------- + +This document has been placed in the public domain. diff --git a/doc/neps/nep-0021-advanced-indexing.rst b/doc/neps/nep-0021-advanced-indexing.rst new file mode 100644 index 000000000000..7751d309bdb9 --- /dev/null +++ b/doc/neps/nep-0021-advanced-indexing.rst @@ -0,0 +1,663 @@ +.. _NEP21: + +================================================== +NEP 21 — Simplified and explicit advanced indexing +================================================== + +:Author: Sebastian Berg +:Author: Stephan Hoyer <shoyer@google.com> +:Status: Draft +:Type: Standards Track +:Created: 2015-08-27 + + +Abstract +-------- + +NumPy's "advanced" indexing support for indexing array with other arrays is +one of its most powerful and popular features. Unfortunately, the existing +rules for advanced indexing with multiple array indices are typically confusing +to both new, and in many cases even old, users of NumPy. Here we propose an +overhaul and simplification of advanced indexing, including two new "indexer" +attributes ``oindex`` and ``vindex`` to facilitate explicit indexing. + +Background +---------- + +Existing indexing operations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +NumPy arrays currently support a flexible range of indexing operations: + +- "Basic" indexing involving only slices, integers, ``np.newaxis`` and ellipsis + (``...``), e.g., ``x[0, :3, np.newaxis]`` for selecting the first element + from the 0th axis, the first three elements from the 1st axis and inserting a + new axis of size 1 at the end. Basic indexing always return a view of the + indexed array's data. +- "Advanced" indexing, also called "fancy" indexing, includes all cases where + arrays are indexed by other arrays. Advanced indexing always makes a copy: + + - "Boolean" indexing by boolean arrays, e.g., ``x[x > 0]`` for + selecting positive elements. + - "Vectorized" indexing by one or more integer arrays, e.g., ``x[[0, 1]]`` + for selecting the first two elements along the first axis. With multiple + arrays, vectorized indexing uses broadcasting rules to combine indices along + multiple dimensions. This allows for producing a result of arbitrary shape + with arbitrary elements from the original arrays. + - "Mixed" indexing involving any combinations of the other advancing types. + This is no more powerful than vectorized indexing, but is sometimes more + convenient. + +For clarity, we will refer to these existing rules as "legacy indexing". +This is only a high-level summary; for more details, see NumPy's documentation +and `Examples` below. + +Outer indexing +~~~~~~~~~~~~~~ + +One broadly useful class of indexing operations is not supported: + +- "Outer" or orthogonal indexing treats one-dimensional arrays equivalently to + slices for determining output shapes. The rule for outer indexing is that the + result should be equivalent to independently indexing along each dimension + with integer or boolean arrays as if both the indexed and indexing arrays + were one-dimensional. This form of indexing is familiar to many users of other + programming languages such as MATLAB, Fortran and R. + +The reason why NumPy omits support for outer indexing is that the rules for +outer and vectorized conflict. Consider indexing a 2D array by two 1D integer +arrays, e.g., ``x[[0, 1], [0, 1]]``: + +- Outer indexing is equivalent to combining multiple integer indices with + ``itertools.product()``. The result in this case is another 2D array with + all combinations of indexed elements, e.g., + ``np.array([[x[0, 0], x[0, 1]], [x[1, 0], x[1, 1]]])`` +- Vectorized indexing is equivalent to combining multiple integer indices with + ``zip()``. The result in this case is a 1D array containing the diagonal + elements, e.g., ``np.array([x[0, 0], x[1, 1]])``. + +This difference is a frequent stumbling block for new NumPy users. The outer +indexing model is easier to understand, and is a natural generalization of +slicing rules. But NumPy instead chose to support vectorized indexing, because +it is strictly more powerful. + +It is always possible to emulate outer indexing by vectorized indexing with +the right indices. To make this easier, NumPy includes utility objects and +functions such as ``np.ogrid`` and ``np.ix_``, e.g., +``x[np.ix_([0, 1], [0, 1])]``. However, there are no utilities for emulating +fully general/mixed outer indexing, which could unambiguously allow for slices, +integers, and 1D boolean and integer arrays. + +Mixed indexing +~~~~~~~~~~~~~~ + +NumPy's existing rules for combining multiple types of indexing in the same +operation are quite complex, involving a number of edge cases. + +One reason why mixed indexing is particularly confusing is that at first glance +the result works deceptively like outer indexing. Returning to our example of a +2D array, both ``x[:2, [0, 1]]`` and ``x[[0, 1], :2]`` return 2D arrays with +axes in the same order as the original array. + +However, as soon as two or more non-slice objects (including integers) are +introduced, vectorized indexing rules apply. The axes introduced by the array +indices are at the front, unless all array indices are consecutive, in which +case NumPy deduces where the user "expects" them to be. Consider indexing a 3D +array ``arr`` with shape ``(X, Y, Z)``: + +1. ``arr[:, [0, 1], 0]`` has shape ``(X, 2)``. +2. ``arr[[0, 1], 0, :]`` has shape ``(2, Z)``. +3. ``arr[0, :, [0, 1]]`` has shape ``(2, Y)``, not ``(Y, 2)``! + +These first two cases are intuitive and consistent with outer indexing, but +this last case is quite surprising, even to many highly experienced NumPy users. + +Mixed cases involving multiple array indices are also surprising, and only +less problematic because the current behavior is so useless that it is rarely +encountered in practice. When a boolean array index is mixed with another boolean or +integer array, boolean array is converted to integer array indices (equivalent +to ``np.nonzero()``) and then broadcast. For example, indexing a 2D array of +size ``(2, 2)`` like ``x[[True, False], [True, False]]`` produces a 1D vector +with shape ``(1,)``, not a 2D sub-matrix with shape ``(1, 1)``. + +Mixed indexing seems so tricky that it is tempting to say that it never should +be used. However, it is not easy to avoid, because NumPy implicitly adds full +slices if there are fewer indices than the full dimensionality of the indexed +array. This means that indexing a 2D array like `x[[0, 1]]`` is equivalent to +``x[[0, 1], :]``. These cases are not surprising, but they constrain the +behavior of mixed indexing. + +Indexing in other Python array libraries +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Indexing is a useful and widely recognized mechanism for accessing +multi-dimensional array data, so it is no surprise that many other libraries in +the scientific Python ecosystem also support array indexing. + +Unfortunately, the full complexity of NumPy's indexing rules mean that it is +both challenging and undesirable for other libraries to copy its behavior in all +of its nuance. The only full implementation of NumPy-style indexing is NumPy +itself. This includes projects like dask.array and h5py, which support *most* +types of array indexing in some form, and otherwise attempt to copy NumPy's API +exactly. + +Vectorized indexing in particular can be challenging to implement with array +storage backends not based on NumPy. In contrast, indexing by 1D arrays along +at least one dimension in the style of outer indexing is much more acheivable. +This has led many libraries (including dask and h5py) to attempt to define a +safe subset of NumPy-style indexing that is equivalent to outer indexing, e.g., +by only allowing indexing with an array along at most one dimension. However, +this is quite challenging to do correctly in a general enough way to be useful. +For example, the current versions of dask and h5py both handle mixed indexing +in case 3 above inconsistently with NumPy. This is quite likely to lead to +bugs. + +These inconsistencies, in addition to the broader challenge of implementing +every type of indexing logic, make it challenging to write high-level array +libraries like xarray or dask.array that can interchangeably index many types of +array storage. In contrast, explicit APIs for outer and vectorized indexing in +NumPy would provide a model that external libraries could reliably emulate, even +if they don't support every type of indexing. + +High level changes +------------------ + +Inspired by multiple "indexer" attributes for controlling different types +of indexing behavior in pandas, we propose to: + +1. Introduce ``arr.oindex[indices]`` which allows array indices, but + uses outer indexing logic. +2. Introduce ``arr.vindex[indices]`` which use the current + "vectorized"/broadcasted logic but with two differences from + legacy indexing: + + * Boolean indices are not supported. All indices must be integers, + integer arrays or slices. + * The integer index result dimensions are always the first axes + of the result array. No transpose is done, even for a single + integer array index. + +3. Plain indexing on arrays will start to give warnings and eventually + errors in cases where one of the explicit indexers should be preferred: + + * First, in all cases where legacy and outer indexing would give + different results. + * Later, potentially in all cases involving an integer array. + +These constraints are sufficient for making indexing generally consistent +with expectations and providing a less surprising learning curve with +``oindex``. + +Note that all things mentioned here apply both for assignment as well as +subscription. + +Understanding these details is *not* easy. The `Examples` section in the +discussion gives code examples. +And the hopefully easier `Motivational Example` provides some +motivational use-cases for the general ideas and is likely a good start for +anyone not intimately familiar with advanced indexing. + + +Detailed Description +-------------------- + +Proposed rules +~~~~~~~~~~~~~~ + +From the three problems noted above some expectations for NumPy can +be deduced: + +1. There should be a prominent outer/orthogonal indexing method such as + ``arr.oindex[indices]``. + +2. Considering how confusing vectorized/fancy indexing can be, it should + be possible to be made more explicitly (e.g. ``arr.vindex[indices]``). + +3. A new ``arr.vindex[indices]`` method, would not be tied to the + confusing transpose rules of fancy indexing, which is for example + needed for the simple case of a single advanced index. Thus, + no transposing should be done. The axes created by the integer array + indices are always inserted at the front, even for a single index. + +4. Boolean indexing is conceptionally outer indexing. Broadcasting + together with other advanced indices in the manner of legacy + indexing is generally not helpful or well defined. + A user who wishes the "``nonzero``" plus broadcast behaviour can thus + be expected to do this manually. Thus, ``vindex`` does not need to + support boolean index arrays. + +5. An ``arr.legacy_index`` attribute should be implemented to support + legacy indexing. This gives a simple way to update existing codebases + using legacy indexing, which will make the deprecation of plain indexing + behavior easier. The longer name ``legacy_index`` is intentionally chosen + to be explicit and discourage its use in new code. + +6. Plain indexing ``arr[...]`` should return an error for ambiguous cases. + For the beginning, this probably means cases where ``arr[ind]`` and + ``arr.oindex[ind]`` return different results give deprecation warnings. + This includes every use of vectorized indexing with multiple integer arrays. + Due to the transposing behaviour, this means that``arr[0, :, index_arr]`` + will be deprecated, but ``arr[:, 0, index_arr]`` will not for the time being. + +7. To ensure that existing subclasses of `ndarray` that override indexing + do not inadvertently revert to default behavior for indexing attributes, + these attribute should have explicit checks that disable them if + ``__getitem__`` or ``__setitem__`` has been overridden. + +Unlike plain indexing, the new indexing attributes are explicitly aimed +at higher dimensional indexing, several additional changes should be implemented: + +* The indexing attributes will enforce exact dimension and indexing match. + This means that no implicit ellipsis (``...``) will be added. Unless + an ellipsis is present the indexing expression will thus only work for + an array with a specific number of dimensions. + This makes the expression more explicit and safeguards against wrong + dimensionality of arrays. + There should be no implications for "duck typing" compatibility with + builtin Python sequences, because Python sequences only support a limited + form of "basic indexing" with integers and slices. + +* The current plain indexing allows for the use of non-tuples for + multi-dimensional indexing such as ``arr[[slice(None), 2]]``. + This creates some inconsistencies and thus the indexing attributes + should only allow plain python tuples for this purpose. + (Whether or not this should be the case for plain indexing is a + different issue.) + +* The new attributes should not use getitem to implement setitem, + since it is a cludge and not useful for vectorized + indexing. (not implemented yet) + + +Open Questions +~~~~~~~~~~~~~~ + +* The names ``oindex``, ``vindex`` and ``legacy_index`` are just suggestions at + the time of writing this, another name NumPy has used for something like + ``oindex`` is ``np.ix_``. See also below. + +* ``oindex`` and ``vindex`` could always return copies, even when no array + operation occurs. One argument for allowing a view return is that this way + ``oindex`` can be used as a general index replacement. + However, there is one argument for returning copies. It is possible for + ``arr.vindex[array_scalar, ...]``, where ``array_scalar`` should be + a 0-D array but is not, since 0-D arrays tend to be converted. + Copying always "fixes" this possible inconsistency. + +* The final state to morph plain indexing in is not fixed in this PEP. + It is for example possible that `arr[index]`` will be equivalent to + ``arr.oindex`` at some point in the future. + Since such a change will take years, it seems unnecessary to make + specific decisions at this time. + +* The proposed changes to plain indexing could be postponed indefinitely or + not taken in order to not break or force major fixes to existing code bases. + + +Alternative Names +~~~~~~~~~~~~~~~~~ + +Possible names suggested (more suggestions will be added). + +============== ============ ======== +**Orthogonal** oindex oix +**Vectorized** vindex vix +**Legacy** legacy_index l/findex +============== ============ ======== + + +Subclasses +~~~~~~~~~~ + +Subclasses are a bit problematic in the light of these changes. There are +some possible solutions for this. For most subclasses (those which do not +provide ``__getitem__`` or ``__setitem__``) the special attributes should +just work. Subclasses that *do* provide it must be updated accordingly +and should preferably not subclass ``oindex`` and ``vindex``. + +All subclasses will inherit the attributes, however, the implementation +of ``__getitem__`` on these attributes should test +``subclass.__getitem__ is ndarray.__getitem__``. If not, the +subclass has special handling for indexing and ``NotImplementedError`` +should be raised, requiring that the indexing attributes is also explicitly +overwritten. Likewise, implementations of ``__setitem__`` should check to see +if ``__setitem__`` is overridden. + +A further question is how to facilitate implementing the special attributes. +Also there is the weird functionality where ``__setitem__`` calls +``__getitem__`` for non-advanced indices. It might be good to avoid it for +the new attributes, but on the other hand, that may make it even more +confusing. + +To facilitate implementations we could provide functions similar to +``operator.itemgetter`` and ``operator.setitem`` for the attributes. +Possibly a mixin could be provided to help implementation. These improvements +are not essential to the initial implementation, so they are saved for +future work. + +Implementation +-------------- + +Implementation would start with writing special indexing objects available +through ``arr.oindex``, ``arr.vindex``, and ``arr.legacy_index`` to allow these +indexing operations. Also, we would need to start to deprecate those plain index +operations which are not ambiguous. +Furthermore, the NumPy code base will need to use the new attributes and +tests will have to be adapted. + + +Backward compatibility +---------------------- + +As a new feature, no backward compatibility issues with the new ``vindex`` +and ``oindex`` attributes would arise. + +To facilitate backwards compatibility as much as possible, we expect a long +deprecation cycle for legacy indexing behavior and propose the new +``legacy_index`` attribute. + +Some forward compatibility issues with subclasses that do not specifically +implement the new methods may arise. + + +Alternatives +------------ + +NumPy may not choose to offer these different type of indexing methods, or +choose to only offer them through specific functions instead of the proposed +notation above. + +We don't think that new functions are a good alternative, because indexing +notation ``[]`` offer some syntactic advantages in Python (i.e., direct +creation of slice objects) compared to functions. + +A more reasonable alternative would be write new wrapper objects for alternative +indexing with functions rather than methods (e.g., ``np.oindex(arr)[indices]`` +instead of ``arr.oindex[indices]``). Functionally, this would be equivalent, +but indexing is such a common operation that we think it is important to +minimize syntax and worth implementing it directly on `ndarray` objects +themselves. Indexing attributes also define a clear interface that is easier +for alternative array implementations to copy, nonwithstanding ongoing +efforts to make it easier to override NumPy functions [2]_. + +Discussion +---------- + +The original discussion about vectorized vs outer/orthogonal indexing arose +on the NumPy mailing list: + + * https://mail.python.org/pipermail/numpy-discussion/2015-April/072550.html + +Some discussion can be found on the original pull request for this NEP: + + * https://github.com/numpy/numpy/pull/6256 + +Python implementations of the indexing operations can be found at: + + * https://github.com/numpy/numpy/pull/5749 + * https://gist.github.com/shoyer/c700193625347eb68fee4d1f0dc8c0c8 + + +Examples +~~~~~~~~ + +Since the various kinds of indexing is hard to grasp in many cases, these +examples hopefully give some more insights. Note that they are all in terms +of shape. +In the examples, all original dimensions have 5 or more elements, +advanced indexing inserts smaller dimensions. +These examples may be hard to grasp without working knowledge of advanced +indexing as of NumPy 1.9. + +Example array:: + + >>> arr = np.ones((5, 6, 7, 8)) + + +Legacy fancy indexing +--------------------- + +Note that the same result can be achieved with ``arr.legacy_index``, but the +"future error" will still work in this case. + +Single index is transposed (this is the same for all indexing types):: + + >>> arr[[0], ...].shape + (1, 6, 7, 8) + >>> arr[:, [0], ...].shape + (5, 1, 7, 8) + + +Multiple indices are transposed *if* consecutive:: + + >>> arr[:, [0], [0], :].shape # future error + (5, 1, 8) + >>> arr[:, [0], :, [0]].shape # future error + (1, 5, 7) + + +It is important to note that a scalar *is* integer array index in this sense +(and gets broadcasted with the other advanced index):: + + >>> arr[:, [0], 0, :].shape + (5, 1, 8) + >>> arr[:, [0], :, 0].shape # future error (scalar is "fancy") + (1, 5, 7) + + +Single boolean index can act on multiple dimensions (especially the whole +array). It has to match (as of 1.10. a deprecation warning) the dimensions. +The boolean index is otherwise identical to (multiple consecutive) integer +array indices:: + + >>> # Create boolean index with one True value for the last two dimensions: + >>> bindx = np.zeros((7, 8), dtype=np.bool_) + >>> bindx[0, 0] = True + >>> arr[:, 0, bindx].shape + (5, 1) + >>> arr[0, :, bindx].shape + (1, 6) + + +The combination with anything that is not a scalar is confusing, e.g.:: + + >>> arr[[0], :, bindx].shape # bindx result broadcasts with [0] + (1, 6) + >>> arr[:, [0, 1], bindx].shape # IndexError + + +Outer indexing +-------------- + +Multiple indices are "orthogonal" and their result axes are inserted +at the same place (they are not broadcasted):: + + >>> arr.oindex[:, [0], [0, 1], :].shape + (5, 1, 2, 8) + >>> arr.oindex[:, [0], :, [0, 1]].shape + (5, 1, 7, 2) + >>> arr.oindex[:, [0], 0, :].shape + (5, 1, 8) + >>> arr.oindex[:, [0], :, 0].shape + (5, 1, 7) + + +Boolean indices results are always inserted where the index is:: + + >>> # Create boolean index with one True value for the last two dimensions: + >>> bindx = np.zeros((7, 8), dtype=np.bool_) + >>> bindx[0, 0] = True + >>> arr.oindex[:, 0, bindx].shape + (5, 1) + >>> arr.oindex[0, :, bindx].shape + (6, 1) + + +Nothing changed in the presence of other advanced indices since:: + + >>> arr.oindex[[0], :, bindx].shape + (1, 6, 1) + >>> arr.oindex[:, [0, 1], bindx].shape + (5, 2, 1) + + +Vectorized/inner indexing +------------------------- + +Multiple indices are broadcasted and iterated as one like fancy indexing, +but the new axes are always inserted at the front:: + + >>> arr.vindex[:, [0], [0, 1], :].shape + (2, 5, 8) + >>> arr.vindex[:, [0], :, [0, 1]].shape + (2, 5, 7) + >>> arr.vindex[:, [0], 0, :].shape + (1, 5, 8) + >>> arr.vindex[:, [0], :, 0].shape + (1, 5, 7) + + +Boolean indices results are always inserted where the index is, exactly +as in ``oindex`` given how specific they are to the axes they operate on:: + + >>> # Create boolean index with one True value for the last two dimensions: + >>> bindx = np.zeros((7, 8), dtype=np.bool_) + >>> bindx[0, 0] = True + >>> arr.vindex[:, 0, bindx].shape + (5, 1) + >>> arr.vindex[0, :, bindx].shape + (6, 1) + + +But other advanced indices are again transposed to the front:: + + >>> arr.vindex[[0], :, bindx].shape + (1, 6, 1) + >>> arr.vindex[:, [0, 1], bindx].shape + (2, 5, 1) + + +Motivational Example +~~~~~~~~~~~~~~~~~~~~ + +Imagine having a data acquisition software storing ``D`` channels and +``N`` datapoints along the time. She stores this into an ``(N, D)`` shaped +array. During data analysis, we needs to fetch a pool of channels, for example +to calculate a mean over them. + +This data can be faked using:: + + >>> arr = np.random.random((100, 10)) + +Now one may remember indexing with an integer array and find the correct code:: + + >>> group = arr[:, [2, 5]] + >>> mean_value = arr.mean() + +However, assume that there were some specific time points (first dimension +of the data) that need to be specially considered. These time points are +already known and given by:: + + >>> interesting_times = np.array([1, 5, 8, 10], dtype=np.intp) + +Now to fetch them, we may try to modify the previous code:: + + >>> group_at_it = arr[interesting_times, [2, 5]] + IndexError: Ambiguous index, use `.oindex` or `.vindex` + +An error such as this will point to read up the indexing documentation. +This should make it clear, that ``oindex`` behaves more like slicing. +So, out of the different methods it is the obvious choice +(for now, this is a shape mismatch, but that could possibly also mention +``oindex``):: + + >>> group_at_it = arr.oindex[interesting_times, [2, 5]] + +Now of course one could also have used ``vindex``, but it is much less +obvious how to achieve the right thing!:: + + >>> reshaped_times = interesting_times[:, np.newaxis] + >>> group_at_it = arr.vindex[reshaped_times, [2, 5]] + + +One may find, that for example our data is corrupt in some places. +So, we need to replace these values by zero (or anything else) for these +times. The first column may for example give the necessary information, +so that changing the values becomes easy remembering boolean indexing:: + + >>> bad_data = arr[:, 0] > 0.5 + >>> arr[bad_data, :] = 0 # (corrupts further examples) + +Again, however, the columns may need to be handled more individually (but in +groups), and the ``oindex`` attribute works well:: + + >>> arr.oindex[bad_data, [2, 5]] = 0 + +Note that it would be very hard to do this using legacy fancy indexing. +The only way would be to create an integer array first:: + + >>> bad_data_indx = np.nonzero(bad_data)[0] + >>> bad_data_indx_reshaped = bad_data_indx[:, np.newaxis] + >>> arr[bad_data_indx_reshaped, [2, 5]] + +In any case we can use only ``oindex`` to do all of this without getting +into any trouble or confused by the whole complexity of advanced indexing. + +But, some new features are added to the data acquisition. Different sensors +have to be used depending on the times. Let us assume we already have +created an array of indices:: + + >>> correct_sensors = np.random.randint(10, size=(100, 2)) + +Which lists for each time the two correct sensors in an ``(N, 2)`` array. + +A first try to achieve this may be ``arr[:, correct_sensors]`` and this does +not work. It should be clear quickly that slicing cannot achieve the desired +thing. But hopefully users will remember that there is ``vindex`` as a more +powerful and flexible approach to advanced indexing. +One may, if trying ``vindex`` randomly, be confused about:: + + >>> new_arr = arr.vindex[:, correct_sensors] + +which is neither the same, nor the correct result (see transposing rules)! +This is because slicing works still the same in ``vindex``. However, reading +the documentation and examples, one can hopefully quickly find the desired +solution:: + + >>> rows = np.arange(len(arr)) + >>> rows = rows[:, np.newaxis] # make shape fit with correct_sensors + >>> new_arr = arr.vindex[rows, correct_sensors] + +At this point we have left the straight forward world of ``oindex`` but can +do random picking of any element from the array. Note that in the last example +a method such as mentioned in the ``Related Questions`` section could be more +straight forward. But this approach is even more flexible, since ``rows`` +does not have to be a simple ``arange``, but could be ``interesting_times``:: + + >>> interesting_times = np.array([0, 4, 8, 9, 10]) + >>> correct_sensors_at_it = correct_sensors[interesting_times, :] + >>> interesting_times_reshaped = interesting_times[:, np.newaxis] + >>> new_arr_it = arr[interesting_times_reshaped, correct_sensors_at_it] + +Truly complex situation would arise now if you would for example pool ``L`` +experiments into an array shaped ``(L, N, D)``. But for ``oindex`` this should +not result into surprises. ``vindex``, being more powerful, will quite +certainly create some confusion in this case but also cover pretty much all +eventualities. + + +Copyright +--------- + +This document is placed under the CC0 1.0 Universell (CC0 1.0) Public Domain Dedication [1]_. + + +References and Footnotes +------------------------ + +.. [1] To the extent possible under law, the person who associated CC0 + with this work has waived all copyright and related or neighboring + rights to this work. The CC0 license may be found at + https://creativecommons.org/publicdomain/zero/1.0/ +.. [2] e.g., see NEP 18, + http://www.numpy.org/neps/nep-0018-array-function-protocol.html diff --git a/doc/neps/nep-0022-ndarray-duck-typing-overview.rst b/doc/neps/nep-0022-ndarray-duck-typing-overview.rst new file mode 100644 index 000000000000..47b81d9e76ec --- /dev/null +++ b/doc/neps/nep-0022-ndarray-duck-typing-overview.rst @@ -0,0 +1,354 @@ +.. _NEP22: + +=========================================================== +NEP 22 — Duck typing for NumPy arrays – high level overview +=========================================================== + +:Author: Stephan Hoyer <shoyer@google.com>, Nathaniel J. Smith <njs@pobox.com> +:Status: Final +:Type: Informational +:Created: 2018-03-22 +:Resolution: https://mail.python.org/pipermail/numpy-discussion/2018-September/078752.html + +Abstract +-------- + +We outline a high-level vision for how NumPy will approach handling +“duck arrays”. This is an Informational-class NEP; it doesn’t +prescribe full details for any particular implementation. In brief, we +propose developing a number of new protocols for defining +implementations of multi-dimensional arrays with high-level APIs +matching NumPy. + + +Detailed description +-------------------- + +Traditionally, NumPy’s ``ndarray`` objects have provided two things: a +high level API for expression operations on homogeneously-typed, +arbitrary-dimensional, array-structured data, and a concrete +implementation of the API based on strided in-RAM storage. The API is +powerful, fairly general, and used ubiquitously across the scientific +Python stack. The concrete implementation, on the other hand, is +suitable for a wide range of uses, but has limitations: as data sets +grow and NumPy becomes used in a variety of new environments, there +are increasingly cases where the strided in-RAM storage strategy is +inappropriate, and users find they need sparse arrays, lazily +evaluated arrays (as in dask), compressed arrays (as in blosc), arrays +stored in GPU memory, arrays stored in alternative formats such as +Arrow, and so forth – yet users still want to work with these arrays +using the familiar NumPy APIs, and re-use existing code with minimal +(ideally zero) porting overhead. As a working shorthand, we call these +“duck arrays”, by analogy with Python’s “duck typing”: a “duck array” +is a Python object which “quacks like” a numpy array in the sense that +it has the same or similar Python API, but doesn’t share the C-level +implementation. + +This NEP doesn’t propose any specific changes to NumPy or other +projects; instead, it gives an overview of how we hope to extend NumPy +to support a robust ecosystem of projects implementing and relying +upon its high level API. + +Terminology +~~~~~~~~~~~ + +“Duck array” works fine as a placeholder for now, but it’s pretty +jargony and may confuse new users, so we may want to pick something +else for the actual API functions. Unfortunately, “array-like” is +already taken for the concept of “anything that can be coerced into an +array” (including e.g. list objects), and “anyarray” is already taken +for the concept of “something that shares ndarray’s implementation, +but has different semantics”, which is the opposite of a duck array +(e.g., np.matrix is an “anyarray”, but is not a “duck array”). This is +a classic bike-shed so for now we’re just using “duck array”. Some +possible options though include: arrayish, pseudoarray, nominalarray, +ersatzarray, arraymimic, ... + + +General approach +~~~~~~~~~~~~~~~~ + +At a high level, duck array support requires working through each of +the API functions provided by NumPy, and figuring out how it can be +extended to work with duck array objects. In some cases this is easy +(e.g., methods/attributes on ndarray itself); in other cases it’s more +difficult. Here are some principles we’ve found useful so far: + + +Principle 1: Focus on “full” duck arrays, but don’t rule out “partial” duck arrays +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +We can distinguish between two classes: + +* “full” duck arrays, which aspire to fully implement np.ndarray’s + Python-level APIs and work essentially anywhere that np.ndarray + works + +* “partial” duck arrays, which intentionally implement only a subset + of np.ndarray’s API. + +Full duck arrays are, well, kind of boring. They have exactly the same +semantics as ndarray, with differences being restricted to +under-the-hood decisions about how the data is actually stored. The +kind of people that are excited about making numpy more extensible are +also, unsurprisingly, excited about changing or extending numpy’s +semantics. So there’s been a lot of discussion of how to best support +partial duck arrays. We've been guilty of this ourself. + +At this point though, we think the best general strategy is to focus +our efforts primarily on supporting full duck arrays, and only worry +about partial duck arrays as much as we need to to make sure we don't +accidentally rule them out for no reason. + +Why focus on full duck arrays? Several reasons: + +First, there are lots of very clear use cases. Potential consumers of +the full duck array interface include almost every package that uses +numpy (scipy, sklearn, astropy, ...), and in particular packages that +provide array-wrapping-classes that handle multiple types of arrays, +such as xarray and dask.array. Potential implementers of the full duck +array interface include: distributed arrays, sparse arrays, masked +arrays, arrays with units (unless they switch to using dtypes), +labeled arrays, and so forth. Clear use cases lead to good and +relevant APIs. + +Second, the Anna Karenina principle applies here: full duck arrays are +all alike, but every partial duck array is partial in its own way: + +* ``xarray.DataArray`` is mostly a duck array, but has incompatible + broadcasting semantics. +* ``xarray.Dataset`` wraps multiple arrays in one object; it still + implements some array interfaces like ``__array_ufunc__``, but + certainly not all of them. +* ``pandas.Series`` has methods with similar behavior to numpy, but + unique null-skipping behavior. +* scipy’s ``LinearOperator``\s support matrix multiplication and nothing else +* h5py and similar libraries for accessing array storage have objects + that support numpy-like slicing and conversion into a full array, + but not computation. +* Some classes may be similar to ndarray, but without supporting the + full indexing semantics. + +And so forth. + +Despite our best attempts, we haven't found any clear, unique way of +slicing up the ndarray API into a hierarchy of related types that +captures these distinctions; in fact, it’s unlikely that any single +person even understands all the distinctions. And this is important, +because we have a *lot* of APIs that we need to add duck array support +to (both in numpy and in all the projects that depend on numpy!). By +definition, these already work for ``ndarray``, so hopefully getting +them to work for full duck arrays shouldn’t be so hard, since by +definition full duck arrays act like ``ndarray``. It’d be very +cumbersome to have to go through each function and identify the exact +subset of the ndarray API that it needs, then figure out which partial +array types can/should support it. Once we have things working for +full duck arrays, we can go back later and refine the APIs needed +further as needed. Focusing on full duck arrays allows us to start +making progress immediately. + +In the future, it might be useful to identify specific use cases for +duck arrays and standardize narrower interfaces targeted just at those +use cases. For example, it might make sense to have a standard “array +loader” interface that file access libraries like h5py, netcdf, pydap, +zarr, ... all implement, to make it easy to switch between these +libraries. But that’s something that we can do as we go, and it +doesn’t necessarily have to involve the NumPy devs at all. For an +example of what this might look like, see the documentation for +`dask.array.from_array +<http://dask.pydata.org/en/latest/array-api.html#dask.array.from_array>`__. + + +Principle 2: Take advantage of duck typing +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``ndarray`` has a very large API surface area:: + + In [1]: len(set(dir(np.ndarray)) - set(dir(object))) + Out[1]: 138 + +And this is a huge **under**\estimate, because there are also many +free-standing functions in NumPy and other libraries which currently +use the NumPy C API and thus only work on ``ndarray`` objects. In type +theory, a type is defined by the operations you can perform on an +object; thus, the actual type of ``ndarray`` includes not just its +methods and attributes, but *all* of these functions. For duck arrays +to be successful, they’ll need to implement a large proportion of the +``ndarray`` API – but not all of it. (For example, +``dask.array.Array`` does not provide an equivalent to the +``ndarray.ptp`` method, presumably because no-one has ever noticed or +cared about its absence. But this doesn’t seem to have stopped people +from using dask.) + +This means that realistically, we can’t hope to define the whole duck +array API up front, or that anyone will be able to implement it all in +one go; this will be an incremental process. It also means that even +the so-called “full” duck array interface is somewhat fuzzily defined +at the borders; there are parts of the ``np.ndarray`` API that duck +arrays won’t have to implement, but we aren’t entirely sure what those +are. + +And ultimately, it isn’t really up to the NumPy developers to define +what does or doesn’t qualify as a duck array. If we want scikit-learn +functions to work on dask arrays (for example), then that’s going to +require negotiation between those two projects to discover +incompatibilities, and when an incompatibility is discovered it will +be up to them to negotiate who should change and how. The NumPy +project can provide technical tools and general advice to help resolve +these disagreements, but we can’t force one group or another to take +responsibility for any given bug. + +Therefore, even though we’re focusing on “full” duck arrays, we +*don’t* attempt to define a normative “array ABC” – maybe this will be +useful someday, but right now, it’s not. And as a convenient +side-effect, the lack of a normative definition leaves partial duck +arrays room to experiment. + +But, we do provide some more detailed advice for duck array +implementers and consumers below. + +Principle 3: Focus on protocols +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Historically, numpy has had lots of success at interoperating with +third-party objects by defining *protocols*, like ``__array__`` (asks +an arbitrary object to convert itself into an array), +``__array_interface__`` (a precursor to Python’s buffer protocol), and +``__array_ufunc__`` (allows third-party objects to support ufuncs like +``np.exp``). + +`NEP 16 <https://github.com/numpy/numpy/pull/10706>`_ took a +different approach: we need a duck-array equivalent of +``asarray``, and it proposed to do this by defining a version of +``asarray`` that would let through objects which implemented a new +AbstractArray ABC. As noted above, we now think that trying to define +an ABC is a bad idea for other reasons. But when this NEP was +discussed on the mailing list, we realized that even on its own +merits, this idea is not so great. A better approach is to define a +*method* that can be called on an arbitrary object to ask it to +convert itself into a duck array, and then define a version of +``asarray`` that calls this method. + +This is strictly more powerful: if an object is already a duck array, +it can simply ``return self``. It allows more correct semantics: NEP +16 assumed that ``asarray(obj, dtype=X)`` is the same as +``asarray(obj).astype(X)``, but this isn’t true. And it supports more +use cases: if h5py supported sparse arrays, it might want to provide +an object which is not itself a sparse array, but which can be +automatically converted into a sparse array. See NEP <XX, to be +written> for full details. + +The protocol approach is also more consistent with core Python +conventions: for example, see the ``__iter__`` method for coercing +objects to iterators, or the ``__index__`` protocol for safe integer +coercion. And finally, focusing on protocols leaves the door open for +partial duck arrays, which can pick and choose which subset of the +protocols they want to participate in, each of which have well-defined +semantics. + +Conclusion: protocols are one honking great idea – let’s do more of +those. + +Principle 4: Reuse existing methods when possible +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +It’s tempting to try to define cleaned up versions of ndarray methods +with a more minimal interface to allow for easier implementation. For +example, ``__array_reshape__`` could drop some of the strange +arguments accepted by ``reshape`` and ``__array_basic_getitem__`` +could drop all the `strange edge cases +<http://www.numpy.org/neps/nep-0021-advanced-indexing.html>`__ of +NumPy’s advanced indexing. + +But as discussed above, we don’t really know what APIs we need for +duck-typing ndarray. We would inevitably end up with a very long list +of new special methods. In contrast, existing methods like ``reshape`` +and ``__getitem__`` have the advantage of already being widely +used/exercised by libraries that use duck arrays, and in practice, any +serious duck array type is going to have to implement them anyway. + +Principle 5: Make it easy to do the right thing +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Making duck arrays work well is going to be a community effort. +Documentation helps, but only goes so far. We want to make it easy to +implement duck arrays that do the right thing. + +One way NumPy can help is by providing mixin classes for implementing +large groups of related functionality at once. +``NDArrayOperatorsMixin`` is a good example: it allows for +implementing arithmetic operators implicitly via the +``__array_ufunc__`` method. It’s not complete, and we’ll want more +helpers like that (e.g. for reductions). + +(We initially thought that the importance of these mixins might be an +argument for providing an array ABC, since that’s the standard way to +do mixins in modern Python. But in discussion around NEP 16 we +realized that partial duck arrays also wanted to take advantage of +these mixins in some cases, so even if we did have an array ABC then +the mixins would still need some sort of separate existence. So never +mind that argument.) + +Tentative duck array guidelines +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +As a general rule, libraries using duck arrays should insist upon the +minimum possible requirements, and libraries implementing duck arrays +should provide as complete of an API as possible. This will ensure +maximum compatibility. For example, users should prefer to rely on +``.transpose()`` rather than ``.swapaxes()`` (which can be implemented +in terms of transpose), but duck array authors should ideally +implement both. + +If you are trying to implement a duck array, then you should strive to +implement everything. You certainly need ``.shape``, ``.ndim`` and +``.dtype``, but also your dtype attribute should actually be a +``numpy.dtype`` object, weird fancy indexing edge cases should ideally +work, etc. Only details related to NumPy’s specific ``np.ndarray`` +implementation (e.g., ``strides``, ``data``, ``view``) are explicitly +out of scope. + +A (very) rough sketch of future plans +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The proposals discussed so far – ``__array_ufunc__`` and some kind of +``asarray`` protocol – are clearly necessary but not sufficient for +full duck typing support. We expect the need for additional protocols +to support (at least) these features: + +* **Concatenating** duck arrays, which would be used internally by other + array combining methods like stack/vstack/hstack. The implementation + of concatenate will need to be negotiated among the list of array + arguments. We expect to use an ``__array_concatenate__`` protocol + like ``__array_ufunc__`` instead of multiple dispatch. +* **Ufunc-like functions** that currently aren’t ufuncs. Many NumPy + functions like median, percentile, sort, where and clip could be + written as generalized ufuncs but currently aren’t. Either these + functions should be written as ufuncs, or we should consider adding + another generic wrapper mechanism that works similarly to ufuncs but + makes fewer guarantees about how the implementation is done. +* **Random number generation** with duck arrays, e.g., + ``np.random.randn()``. For example, we might want to add new APIs + like ``random_like()`` for generating new arrays with a matching + shape *and* type – though we'll need to look at some real examples + of how these functions are used to figure out what would be helpful. +* **Miscellaneous other functions** such as ``np.einsum``, + ``np.zeros_like``, and ``np.broadcast_to`` that don’t fall into any + of the above categories. +* **Checking mutability** on duck arrays, which would imply that they + support assignment with ``__setitem__`` and the out argument to + ufuncs. Many otherwise fine duck arrays are not easily mutable (for + example, because they use some kinds of sparse or compressed + storage, or are in read-only shared memory), and it turns out that + frequently-used code like the default implementation of ``np.mean`` + needs to check this (to decide whether it can re-use temporary + arrays). + +We intentionally do not describe exactly how to add support for these +types of duck arrays here. These will be the subject of future NEPs. + + +Copyright +--------- + +This document has been placed in the public domain. diff --git a/doc/neps/nep-0023-backwards-compatibility.rst b/doc/neps/nep-0023-backwards-compatibility.rst new file mode 100644 index 000000000000..8b6f4cd1186a --- /dev/null +++ b/doc/neps/nep-0023-backwards-compatibility.rst @@ -0,0 +1,351 @@ +.. _NEP23: + +======================================================= +NEP 23 — Backwards compatibility and deprecation policy +======================================================= + +:Author: Ralf Gommers <ralf.gommers@gmail.com> +:Status: Final +:Type: Process +:Created: 2018-07-14 +:Resolution: https://mail.python.org/pipermail/numpy-discussion/2021-January/081423.html + + +Abstract +-------- + +In this NEP we describe NumPy's approach to backwards compatibility, +its deprecation and removal policy, and the trade-offs and decision +processes for individual cases where breaking backwards compatibility +is considered. + + +Motivation and Scope +-------------------- + +NumPy has a very large user base. Those users rely on NumPy being stable +and the code they write that uses NumPy functionality to keep working. +NumPy is also actively maintained and improved -- and sometimes improvements +require, or are made easier, by breaking backwards compatibility. +Finally, there are trade-offs in stability for existing users vs. avoiding +errors or having a better user experience for new users. These competing +needs often give rise to long debates and delay accepting or rejecting +contributions. This NEP tries to address that by providing a policy as well +as examples and rationales for when it is or isn't a good idea to break +backwards compatibility. + +In addition, this NEP can serve as documentation for users about how the NumPy +project treats backwards compatibility, and the speed at which they can expect +changes to be made. + +In scope for this NEP are: + +- Principles of NumPy's approach to backwards compatibility. +- How to deprecate functionality, and when to remove already deprecated + functionality. +- Decision making process for deprecations and removals. +- How to ensure that users are well informed about any change. + +Out of scope are: + +- Making concrete decisions about deprecations of particular functionality. +- NumPy's versioning scheme. + + +General principles +------------------ + +When considering proposed changes that are backwards incompatible, the +main principles the NumPy developers use when making a decision are: + +1. Changes need to benefit more than they harm users. +2. NumPy is widely used, so breaking changes should be assumed by default to be + harmful. +3. Decisions should be based on how they affect users and downstream packages + and should be based on usage data where possible. It does not matter whether + this use contradicts the documentation or best practices. +4. The possibility of an incorrect result is worse than an error or even crash. + +When assessing the costs of proposed changes, keep in mind that most users do +not read the mailing list, do not notice deprecation warnings, and sometimes +wait more than one or two years before upgrading from their old version. And +that NumPy has millions of users, so "no one will do or use this" is likely +incorrect. + +Benefits of proposed changes can include improved functionality, usability and +performance, as well as lower maintenance cost and improved future +extensibility. + +Fixes for clear bugs are exempt from this backwards compatibility policy. +However, in case of serious impact on users even bug fixes may have to be +delayed for one or more releases. For example, if a downstream library would no +longer build or would give incorrect results. + + +Strategies related to deprecations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Impact assessment +````````````````` + +Getting hard data on the impact of a deprecation of often difficult. Strategies +that can be used to assess such impact include: + +- Use a code search engine ([1]_, [2]_) or static ([3]_) or dynamic ([4]_) code + analysis tools to determine where and how the functionality is used. +- Test prominent downstream libraries against a development build of NumPy + containing the proposed change to get real-world data on its impact. +- Make a change on the main branch and revert it before release if it + causes problems. We encourage other packages to test against + NumPy's main branch and if that's too burdensome, then at least to + test pre-releases. This often turns up issues quickly. + +Alternatives to deprecations +```````````````````````````` + +If the impact is unclear or significant, it is often good to consider +alternatives to deprecations. For example, discouraging use in documentation +only, or moving the documentation for the functionality to a less prominent +place or even removing it completely. Commenting on open issues related to it +that they are low-prio or labeling them as "wontfix" will also be a signal to +users, and reduce the maintenance effort needing to be spent. + + +Implementing deprecations and removals +-------------------------------------- + +Deprecation warnings are necessary in all cases where functionality +will eventually be removed. If there is no intent to remove functionality, +then it should not be deprecated. A "please don't use this for new code" +in the documentation or other type of warning should be used instead, and the +documentation can be organized such that the preferred alternative is more +prominently shown. + +Deprecations: + +- shall include the version number of the release in which the functionality + was deprecated. +- shall include information on alternatives to the deprecated functionality, or a + reason for the deprecation if no clear alternative is available. Note that + release notes can include longer messages if needed. +- shall use ``DeprecationWarning`` by default, and ``VisibleDeprecation`` + for changes that need attention again after already having been deprecated or + needing extra attention for some reason. +- shall be listed in the release notes of the release where the deprecation is + first present. +- shall not be introduced in micro (bug fix) releases. +- shall set a ``stacklevel``, so the warning appears to come from the correct + place. +- shall be mentioned in the documentation for the functionality. A + ``.. deprecated::`` directive can be used for this. + +Examples of good deprecation warnings (also note standard form of the comments +above the warning, helps when grepping): + +.. code-block:: python + + # NumPy 1.15.0, 2018-09-02 + warnings.warn('np.asscalar(a) is deprecated since NumPy 1.16.0, use ' + 'a.item() instead', DeprecationWarning, stacklevel=3) + + # NumPy 1.15.0, 2018-02-10 + warnings.warn("Importing from numpy.testing.utils is deprecated " + "since 1.15.0, import from numpy.testing instead.", + DeprecationWarning, stacklevel=2) + + # NumPy 1.14.0, 2017-07-14 + warnings.warn( + "Reading unicode strings without specifying the encoding " + "argument is deprecated since NumPy 1.14.0. Set the encoding, " + "use None for the system default.", + np.VisibleDeprecationWarning, stacklevel=2) + +.. code-block:: C + + /* DEPRECATED 2020-05-13, NumPy 1.20 */ + if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, + matrix_deprecation_msg, ufunc->name, "first") < 0) { + return NULL; + } + +Removal of deprecated functionality: + +- shall be done after at least 2 releases assuming the current 6-monthly + release cycle; if that changes, there shall be at least 1 year between + deprecation and removal. +- shall be listed in the release notes of the release where the removal happened. +- can be done in any minor, but not bugfix, release. + +For backwards incompatible changes that aren't "deprecate and remove" but for +which code will start behaving differently, a ``FutureWarning`` should be +used. Release notes, mentioning version number and using ``stacklevel`` should +be done in the same way as for deprecation warnings. A ``.. versionchanged::`` +directive shall be used in the documentation after the behaviour change was +made to indicate when the behavior changed: + +.. code-block:: python + + def argsort(self, axis=np._NoValue, ...): + """ + Parameters + ---------- + axis : int, optional + Axis along which to sort. If None, the default, the flattened array + is used. + + .. versionchanged:: 1.13.0 + Previously, the default was documented to be -1, but that was + in error. At some future date, the default will change to -1, as + originally intended. + Until then, the axis should be given explicitly when + ``arr.ndim > 1``, to avoid a FutureWarning. + """ + ... + warnings.warn( + "In the future the default for argsort will be axis=-1, not the " + "current None, to match its documentation and np.argsort. " + "Explicitly pass -1 or None to silence this warning.", + MaskedArrayFutureWarning, stacklevel=3) + + +Decision making +--------------- + +In concrete cases where this policy needs to be applied, decisions are made according +to the `NumPy governance model +<https://docs.scipy.org/doc/numpy/dev/governance/index.html>`_. + +All deprecations must be proposed on the mailing list in order to give everyone +with an interest in NumPy development a chance to comment. Removal of +deprecated functionality does not need discussion on the mailing list. + + +Functionality with more strict deprecation policies +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- ``numpy.random`` has its own backwards compatibility policy with additional + requirements on top of the ones in this NEP, see + `NEP 19 <http://www.numpy.org/neps/nep-0019-rng-policy.html>`_. +- The file format of ``.npy`` and ``.npz`` files is strictly versioned + independent of the NumPy version; existing format versions must remain + backwards compatible even if a newer format version is introduced. + + +Example cases +------------- + +We now discuss a few concrete examples from NumPy's history to illustrate +typical issues and trade-offs. + +**Changing the behavior of a function** + +``np.histogram`` is probably the most infamous example. +First, a new keyword ``new=False`` was introduced, this was then switched +over to None one release later, and finally it was removed again. +Also, it has a ``normed`` keyword that had behavior that could be considered +either suboptimal or broken (depending on ones opinion on the statistics). +A new keyword ``density`` was introduced to replace it; ``normed`` started giving +``DeprecationWarning`` only in v.1.15.0. Evolution of ``histogram``:: + + def histogram(a, bins=10, range=None, normed=False): # v1.0.0 + + def histogram(a, bins=10, range=None, normed=False, weights=None, new=False): #v1.1.0 + + def histogram(a, bins=10, range=None, normed=False, weights=None, new=None): #v1.2.0 + + def histogram(a, bins=10, range=None, normed=False, weights=None): #v1.5.0 + + def histogram(a, bins=10, range=None, normed=False, weights=None, density=None): #v1.6.0 + + def histogram(a, bins=10, range=None, normed=None, weights=None, density=None): #v1.15.0 + # v1.15.0 was the first release where `normed` started emitting + # DeprecationWarnings + +The ``new`` keyword was planned from the start to be temporary. Such a plan +forces users to change their code more than once, which is almost never the +right thing to do. Instead, a better approach here would have been to +deprecate ``histogram`` and introduce a new function ``hist`` in its place. + + +**Disallowing indexing with floats** + +Indexing an array with floats is asking for something ambiguous, and can be a +sign of a bug in user code. After some discussion, it was deemed a good idea +to deprecate indexing with floats. This was first tried for the v1.8.0 +release, however in pre-release testing it became clear that this would break +many libraries that depend on NumPy. Therefore it was reverted before release, +to give those libraries time to fix their code first. It was finally +introduced for v1.11.0 and turned into a hard error for v1.12.0. + +This change was disruptive, however it did catch real bugs in, e.g., SciPy and +scikit-learn. Overall the change was worth the cost, and introducing it in +the main branch first to allow testing, then removing it again before +a release, is a useful strategy. + +Similar deprecations that also look like good examples of +cleanups/improvements: + +- removing deprecated boolean indexing (in 2016, see `gh-8312 <https://github.com/numpy/numpy/pull/8312>`__) +- deprecating truth testing on empty arrays (in 2017, see `gh-9718 <https://github.com/numpy/numpy/pull/9718>`__) + + +**Removing the financial functions** + +The financial functions (e.g. ``np.pmt``) had short non-descriptive names, were +present in the main NumPy namespace, and didn't really fit well within NumPy's +scope. They were added in 2008 after +`a discussion <https://mail.python.org/pipermail/numpy-discussion/2008-April/032353.html>`_ +on the mailing list where opinion was divided (but a majority in favor). +The financial functions didn't cause a lot of overhead, however there were +still multiple issues and PRs a year for them which cost maintainer time to +deal with. And they cluttered up the ``numpy`` namespace. Discussion on +removing them was discussed in 2013 (gh-2880, rejected) and in 2019 +(:ref:`NEP32`, accepted without significant complaints). + +Given that they were clearly outside of NumPy's scope, moving them to a +separate ``numpy-financial`` package and removing them from NumPy after a +deprecation period made sense. That also gave users an easy way to update +their code by doing `pip install numpy-financial`. + + +Alternatives +------------ + +**Being more aggressive with deprecations.** + +The goal of being more aggressive is to allow NumPy to move forward faster. +This would avoid others inventing their own solutions (often in multiple +places), as well as be a benefit to users without a legacy code base. We +reject this alternative because of the place NumPy has in the scientific Python +ecosystem - being fairly conservative is required in order to not increase the +extra maintenance for downstream libraries and end users to an unacceptable +level. + + +Discussion +---------- + +- `Mailing list discussion on the first version of this NEP in 2018 <https://mail.python.org/pipermail/numpy-discussion/2018-July/078432.html>`__ +- `Mailing list discussion on the Dec 2020 update of this NEP <https://mail.python.org/pipermail/numpy-discussion/2020-December/081358.html>`__ +- `PR with review comments on the the Dec 2020 update of this NEP <https://github.com/numpy/numpy/pull/18097>`__ + + +References and Footnotes +------------------------ + +- `Issue requesting semantic versioning <https://github.com/numpy/numpy/issues/10156>`__ + +- `PEP 387 - Backwards Compatibility Policy <https://www.python.org/dev/peps/pep-0387/>`__ + +.. [1] https://searchcode.com/ + +.. [2] https://sourcegraph.com/search + +.. [3] https://github.com/Quansight-Labs/python-api-inspect + +.. [4] https://github.com/data-apis/python-record-api + +Copyright +--------- + +This document has been placed in the public domain. diff --git a/doc/neps/nep-0024-missing-data-2.rst b/doc/neps/nep-0024-missing-data-2.rst new file mode 100644 index 000000000000..c0e2d2ce7771 --- /dev/null +++ b/doc/neps/nep-0024-missing-data-2.rst @@ -0,0 +1,212 @@ +.. _NEP24: + +============================================================= +NEP 24 — Missing data functionality - Alternative 1 to NEP 12 +============================================================= + +:Author: Nathaniel J. Smith <njs@pobox.com>, Matthew Brett <matthew.brett@gmail.com> +:Status: Deferred +:Type: Standards Track +:Created: 2011-06-30 + + +Abstract +-------- + +*Context: this NEP was written as an alternative to NEP 12, which at the time of writing +had an implementation that was merged into the NumPy main branch.* + +The principle of this NEP is to separate the APIs for masking and for missing values, according to + +* The current implementation of masked arrays (NEP 12) +* This proposal. + +This discussion is only of the API, and not of the implementation. + +Detailed description +-------------------- + + +Rationale +^^^^^^^^^ + +The purpose of this NEP is to define two interfaces -- one for handling +'missing values', and one for handling 'masked arrays'. + +An ordinary value is something like an integer or a floating point number. A +*missing* value is a placeholder for an ordinary value that is for some +reason unavailable. For example, in working with statistical data, we often +build tables in which each row represents one item, and each column +represents properties of that item. For instance, we might take a group of +people and for each one record height, age, education level, and income, and +then stick these values into a table. But then we discover that our research +assistant screwed up and forgot to record the age of one of our individuals. +We could throw out the rest of their data as well, but this would be +wasteful; even such an incomplete row is still perfectly usable for some +analyses (e.g., we can compute the correlation of height and income). The +traditional way to handle this would be to stick some particular meaningless +value in for the missing data, e.g., recording this person's age as 0. But +this is very error prone; we may later forget about these special values +while running other analyses, and discover to our surprise that babies have +higher incomes than teenagers. (In this case, the solution would be to just +leave out all the items where we have no age recorded, but this isn't a +general solution; many analyses require something more clever to handle +missing values.) So instead of using an ordinary value like 0, we define a +special "missing" value, written "NA" for "not available". + +Therefore, missing values have the following properties: Like any other +value, they must be supported by your array's dtype -- you can't store a +floating point number in an array with dtype=int32, and you can't store an NA +in it either. You need an array with dtype=NAint32 or something (exact syntax +to be determined). Otherwise, they act exactly like any other values. In +particular, you can apply arithmetic functions and so forth to them. By +default, any function which takes an NA as an argument always returns an NA +as well, regardless of the values of the other arguments. This ensures that +if we try to compute the correlation of income with age, we will get "NA", +meaning "given that some of the entries could be anything, the answer could +be anything as well". This reminds us to spend a moment thinking about how we +should rephrase our question to be more meaningful. And as a convenience for +those times when you do decide that you just want the correlation between the +known ages and income, then you can enable this behavior by adding a single +argument to your function call. + +For floating point computations, NAs and NaNs have (almost?) identical +behavior. But they represent different things -- NaN an invalid computation +like 0/0, NA a value that is not available -- and distinguishing between +these things is useful because in some situations they should be treated +differently. (For example, an imputation procedure should replace NAs with +imputed values, but probably should leave NaNs alone.) And anyway, we can't +use NaNs for integers, or strings, or booleans, so we need NA anyway, and +once we have NA support for all these types, we might as well support it for +floating point too for consistency. + +A masked array is, conceptually, an ordinary rectangular numpy array, which +has had an arbitrarily-shaped mask placed over it. The result is, +essentially, a non-rectangular view of a rectangular array. In principle, +anything you can accomplish with a masked array could also be accomplished by +explicitly keeping a regular array and a boolean mask array and using numpy +indexing to combine them for each operation, but combining them into a single +structure is much more convenient when you need to perform complex operations +on the masked view of an array, while still being able to manipulate the mask +in the usual ways. Therefore, masks are preserved through indexing, and +functions generally treat masked-out values as if they were not even part of +the array in the first place. (Maybe this is a good heuristic: a length-4 +array in which the last value has been masked out behaves just like an +ordinary length-3 array, so long as you don't change the mask.) Except, of +course, that you are free to manipulate the mask in arbitrary ways whenever +you like; it's just a standard numpy array. + +There are some simple situations where one could use either of these tools to +get the job done -- or other tools entirely, like using designated surrogate +values (age=0), separate mask arrays, etc. But missing values are designed to +be particularly helpful in situations where the missingness is an intrinsic +feature of the data -- where there's a specific value that **should** exist, +if it did exist we'd it'd mean something specific, but it **doesn't**. Masked +arrays are designed to be particularly helpful in situations where we just +want to temporarily ignore some data that does exist, or generally when we +need to work with data that has a non-rectangular shape (e.g., if you make +some measurement at each point on a grid laid over a circular agar dish, then +the points that fall outside the dish aren't missing measurements, they're +just meaningless). + +Initialization +^^^^^^^^^^^^^^ + +First, missing values can be set and be displayed as ``np.NA, NA``:: + + >>> np.array([1.0, 2.0, np.NA, 7.0], dtype='NA[f8]') + array([1., 2., NA, 7.], dtype='NA[<f8]') + +As the initialization is not ambiguous, this can be written without the NA +dtype:: + + >>> np.array([1.0, 2.0, np.NA, 7.0]) + array([1., 2., NA, 7.], dtype='NA[<f8]') + +Masked values can be set and be displayed as ``np.IGNORE, IGNORE``:: + + >>> np.array([1.0, 2.0, np.IGNORE, 7.0], masked=True) + array([1., 2., IGNORE, 7.], masked=True) + +As the initialization is not ambiguous, this can be written without +``masked=True``:: + + >>> np.array([1.0, 2.0, np.IGNORE, 7.0]) + array([1., 2., IGNORE, 7.], masked=True) + +Ufuncs +^^^^^^ + +By default, NA values propagate:: + + >>> na_arr = np.array([1.0, 2.0, np.NA, 7.0]) + >>> np.sum(na_arr) + NA('float64') + +unless the ``skipna`` flag is set:: + + >>> np.sum(na_arr, skipna=True) + 10.0 + +By default, masking does not propagate:: + + >>> masked_arr = np.array([1.0, 2.0, np.IGNORE, 7.0]) + >>> np.sum(masked_arr) + 10.0 + +unless the ``propmask`` flag is set:: + + >>> np.sum(masked_arr, propmask=True) + IGNORE + +An array can be masked, and contain NA values:: + + >>> both_arr = np.array([1.0, 2.0, np.IGNORE, np.NA, 7.0]) + +In the default case, the behavior is obvious:: + + >>> np.sum(both_arr) + NA('float64') + +It's also obvious what to do with ``skipna=True``:: + + >>> np.sum(both_arr, skipna=True) + 10.0 + >>> np.sum(both_arr, skipna=True, propmask=True) + IGNORE + +To break the tie between NA and MSK, NAs propagate harder:: + + >>> np.sum(both_arr, propmask=True) + NA('float64') + +Assignment +^^^^^^^^^^ + +is obvious in the NA case:: + + >>> arr = np.array([1.0, 2.0, 7.0]) + >>> arr[2] = np.NA + TypeError('dtype does not support NA') + >>> na_arr = np.array([1.0, 2.0, 7.0], dtype='NA[f8]') + >>> na_arr[2] = np.NA + >>> na_arr + array([1., 2., NA], dtype='NA[<f8]') + +Direct assignnent in the masked case is magic and confusing, and so happens only +via the mask:: + + >>> masked_array = np.array([1.0, 2.0, 7.0], masked=True) + >>> masked_arr[2] = np.NA + TypeError('dtype does not support NA') + >>> masked_arr[2] = np.IGNORE + TypeError('float() argument must be a string or a number') + >>> masked_arr.visible[2] = False + >>> masked_arr + array([1., 2., IGNORE], masked=True) + + +Copyright +--------- + +This document has been placed in the public domain. diff --git a/doc/neps/nep-0025-missing-data-3.rst b/doc/neps/nep-0025-missing-data-3.rst new file mode 100644 index 000000000000..1756ce491188 --- /dev/null +++ b/doc/neps/nep-0025-missing-data-3.rst @@ -0,0 +1,475 @@ +.. _NEP25: + +====================================== +NEP 25 — NA support via special dtypes +====================================== + +:Author: Nathaniel J. Smith <njs@pobox.com> +:Status: Deferred +:Type: Standards Track +:Created: 2011-07-08 + +Abstract +======== + +*Context: this NEP was written as an additional alternative to NEP 12 (NEP 24 +is another alternative), which at the time of writing had an implementation +that was merged into the NumPy main branch.* + +To try and make more progress on the whole missing values/masked arrays/... +debate, it seems useful to have a more technical discussion of the pieces +which we *can* agree on. This is the second, which attempts to nail down the +details of how NAs can be implemented using special dtype's. + +Rationale +--------- + +An ordinary value is something like an integer or a floating point number. A +missing value is a placeholder for an ordinary value that is for some reason +unavailable. For example, in working with statistical data, we often build +tables in which each row represents one item, and each column represents +properties of that item. For instance, we might take a group of people and +for each one record height, age, education level, and income, and then stick +these values into a table. But then we discover that our research assistant +screwed up and forgot to record the age of one of our individuals. We could +throw out the rest of their data as well, but this would be wasteful; even +such an incomplete row is still perfectly usable for some analyses (e.g., we +can compute the correlation of height and income). The traditional way to +handle this would be to stick some particular meaningless value in for the +missing data,e.g., recording this person's age as 0. But this is very error +prone; we may later forget about these special values while running other +analyses, and discover to our surprise that babies have higher incomes than +teenagers. (In this case, the solution would be to just leave out all the +items where we have no age recorded, but this isn't a general solution; many +analyses require something more clever to handle missing values.) So instead +of using an ordinary value like 0, we define a special "missing" value, +written "NA" for "not available". + +There are several possible ways to represent such a value in memory. For +instance, we could reserve a specific value (like 0, or a particular NaN, or +the smallest negative integer) and then ensure that this value is treated +specially by all arithmetic and other operations on our array. Another option +would be to add an additional mask array next to our main array, use this to +indicate which values should be treated as NA, and then extend our array +operations to check this mask array whenever performing computations. Each +implementation approach has various strengths and weaknesses, but here we focus +on the former (value-based) approach exclusively and leave the possible +addition of the latter to future discussion. The core advantages of this +approach are (1) it adds no additional memory overhead, (2) it is +straightforward to store and retrieve such arrays to disk using existing file +storage formats, (3) it allows binary compatibility with R arrays including NA +values, (4) it is compatible with the common practice of using NaN to indicate +missingness when working with floating point numbers, (5) the dtype is already +a place where "weird things can happen" -- there are a wide variety of dtypes +that don't act like ordinary numbers (including structs, Python objects, +fixed-length strings, ...), so code that accepts arbitrary NumPy arrays already +has to be prepared to handle these (even if only by checking for them and +raising an error). Therefore adding yet more new dtypes has less impact on +extension authors than if we change the ndarray object itself. + +The basic semantics of NA values are as follows. Like any other value, they +must be supported by your array's dtype -- you can't store a floating point +number in an array with dtype=int32, and you can't store an NA in it either. +You need an array with dtype=NAint32 or something (exact syntax to be +determined). Otherwise, NA values act exactly like any other values. In +particular, you can apply arithmetic functions and so forth to them. By +default, any function which takes an NA as an argument always returns an NA as +well, regardless of the values of the other arguments. This ensures that if we +try to compute the correlation of income with age, we will get "NA", meaning +"given that some of the entries could be anything, the answer could be anything +as well". This reminds us to spend a moment thinking about how we should +rephrase our question to be more meaningful. And as a convenience for those +times when you do decide that you just want the correlation between the known +ages and income, then you can enable this behavior by adding a single argument +to your function call. + +For floating point computations, NAs and NaNs have (almost?) identical +behavior. But they represent different things -- NaN an invalid computation +like 0/0, NA a value that is not available -- and distinguishing between these +things is useful because in some situations they should be treated differently. +(For example, an imputation procedure should replace NAs with imputed values, +but probably should leave NaNs alone.) And anyway, we can't use NaNs for +integers, or strings, or booleans, so we need NA anyway, and once we have NA +support for all these types, we might as well support it for floating point too +for consistency. + +General strategy +================ + +NumPy already has a general mechanism for defining new dtypes and slotting them +in so that they're supported by ndarrays, by the casting machinery, by ufuncs, +and so on. In principle, we could implement NA-dtypes just using these existing +interfaces. But we don't want to do that, because defining all those new ufunc +loops etc. from scratch would be a huge hassle, especially since the basic +functionality needed is the same in all cases. So we need some generic +functionality for NAs -- but it would be better not to bake this in as a single +set of special "NA types", since users may well want to define new custom +dtypes that have their own NA values, and have them integrate well the rest of +the NA machinery. Our strategy, therefore, is to avoid the `mid-layer mistake`_ +by exposing some code for generic NA handling in different situations, which +dtypes can selectively use or not as they choose. + +.. _mid-layer mistake: https://lwn.net/Articles/336262/ + +Some example use cases: + 1. We want to define a dtype that acts exactly like an int32, except that the + most negative value is treated as NA. + 2. We want to define a parametrized dtype to represent `categorical data`_, + and the bit-pattern to be used for NA depends on the number of categories + defined, so our code needs to play an active role handling it rather than + simply deferring to the standard machinery. + 3. We want to define a dtype that acts like an length-10 string and supports + NAs. Since our string may hold arbitrary binary values, we want to actually + allocate 11 bytes for it, with the first byte a flag indicating whether this + string is NA and the rest containing the string content. + 4. We want to define a dtype that allows multiple different types of NA data, + which print differently and can be distinguished by the new ufunc that we + define called ``is_na_of_type(...)``, but otherwise takes advantage of the + generic NA machinery for most operations. + +.. _categorical data: http://mail.scipy.org/pipermail/numpy-discussion/2010-August/052401.html + +dtype C-level API extensions +============================ + +.. highlight:: c + +The `PyArray_Descr`_ struct gains the following new fields:: + + void * NA_value; + PyArray_Descr * NA_extends; + int NA_extends_offset; + +.. _PyArray_Descr: http://docs.scipy.org/doc/numpy/reference/c-api.types-and-structures.html#PyArray_Descr + +The following new flag values are defined:: + + NPY_NA_AUTO_ARRFUNCS + NPY_NA_AUTO_CAST + NPY_NA_AUTO_UFUNC + NPY_NA_AUTO_UFUNC_CHECKED + NPY_NA_AUTO_ALL /* the above flags OR'ed together */ + +The `PyArray_ArrFuncs`_ struct gains the following new fields:: + + void (*isna)(void * src, void * dst, npy_intp n, void * arr); + void (*clearna)(void * data, npy_intp n, void * arr); + +.. _PyArray_ArrFuncs: http://docs.scipy.org/doc/numpy/reference/c-api.types-and-structures.html#PyArray_ArrFuncs + +We add at least one new convenience macro:: + + #define NPY_NA_SUPPORTED(dtype) ((dtype)->f->isna != NULL) + +The general idea is that anywhere where we used to call a dtype-specific +function pointer, the code will be modified to instead: + + 1. Check for whether the relevant ``NPY_NA_AUTO_...`` bit is enabled, the + NA_extends field is non-NULL, and the function pointer we wanted to call + is NULL. + 2. If these conditions are met, then use ``isna`` to identify which entries + in the array are NA, and handle them appropriately. Then look up whatever + function we were *going* to call using this dtype on the ``NA_extends`` + dtype instead, and use that to handle the non-NA elements. + +For more specifics, see following sections. + +Note that if ``NA_extends`` points to a parametrized dtype, then the dtype +object it points to must be fully specified. For example, if it is a string +dtype, it must have a non-zero ``elsize`` field. + +In order to handle the case where the NA information is stored in a field next +to the `real' data, the ``NA_extends_offset`` field is set to a non-zero value; +it must point to the location within each element of this dtype where some data +of the ``NA_extends`` dtype is found. For example, if we have are storing +10-byte strings with an NA indicator byte at the beginning, then we have:: + + elsize == 11 + NA_extends_offset == 1 + NA_extends->elsize == 10 + +When delegating to the ``NA_extends`` dtype, we offset our data pointer by +``NA_extends_offset`` (while keeping our strides the same) so that it sees an +array of data of the expected type (plus some superfluous padding). This is +basically the same mechanism that record dtypes use, IIUC, so it should be +pretty well-tested. + +When delegating to a function that cannot handle "misbehaved" source data (see +the ``PyArray_ArrFuncs`` documentation for details), then we need to check for +alignment issues before delegating (especially with a non-zero +``NA_extends_offset``). If there's a problem, when we need to "clean up" the +source data first, using the usual mechanisms for handling misaligned data. (Of +course, we should usually set up our dtypes so that there aren't any alignment +issues, but someone screws that up, or decides that reduced memory usage is +more important to them then fast inner loops, then we should still handle that +gracefully, as we do now.) + +The ``NA_value`` and ``clearna`` fields are used for various sorts of casting. +``NA_value`` is a bit-pattern to be used when, for example, assigning from +np.NA. ``clearna`` can be a no-op if ``elsize`` and ``NA_extends->elsize`` are +the same, but if they aren't then it should clear whatever auxiliary NA storage +this dtype uses, so that none of the specified array elements are NA. + +Core dtype functions +-------------------- + +The following functions are defined in ``PyArray_ArrFuncs``. The special +behavior described here is enabled by the NPY_NA_AUTO_ARRFUNCS bit in the dtype +flags, and only enabled if the given function field is *not* filled in. + +``getitem``: Calls ``isna``. If ``isna`` returns true, returns np.NA. +Otherwise, delegates to the ``NA_extends`` dtype. + +``setitem``: If the input object is ``np.NA``, then runs +``memcpy(self->NA_value, data, arr->dtype->elsize);``. Otherwise, calls +``clearna``, and then delegates to the ``NA_extends`` dtype. + +``copyswapn``, ``copyswap``: FIXME: Not sure whether there's any special +handling to use for these? + +``compare``: FIXME: how should this handle NAs? R's sort function *discards* +NAs, which doesn't seem like a good option. + +``argmax``: FIXME: what is this used for? If it's the underlying implementation +for np.max, then it really needs some way to get a skipna argument. If not, +then the appropriate semantics depends on what it's supposed to accomplish... + +``dotfunc``: QUESTION: is it actually guaranteed that everything has the same +dtype? FIXME: same issues as for ``argmax``. + +``scanfunc``: This one's ugly. We may have to explicitly override it in all of +our special dtypes, because assuming that we want the option of, say, having +the token "NA" represent an NA value in a text file, we need some way to check +whether that's there before delegating. But ``ungetc`` is only guaranteed to +let us put back 1 character, and we need 2 (or maybe 3 if we actually check for +"NA "). The other option would be to read to the next delimiter, check whether +we have an NA, and if not then delegate to ``fromstr`` instead of ``scanfunc``, +but according to the current API, each dtype might in principle use a totally +different rule for defining "the next delimiter". So... any ideas? (FIXME) + +``fromstr``: Easy -- check for "NA ", if present then assign ``NA_value``, +otherwise call ``clearna`` and delegate. + +``nonzero``: FIXME: again, what is this used for? (It seems redundant with +using the casting machinery to cast to bool.) Probably it needs to be modified +so that it can return NA, though... + +``fill``: Use ``isna`` to check if either of the first two values is NA. If so, +then fill the rest of the array with ``NA_value``. Otherwise, call ``clearna`` +and then delegate. + +``fillwithvalue``: Guess this can just delegate? + +``sort``, ``argsort``: These should probably arrange to sort NAs to a +particular place in the array (either the front or the back -- any opinions?) + +``scalarkind``: FIXME: I have no idea what this does. + +``castdict``, ``cancastscalarkindto``, ``cancastto``: See section on casting +below. + +Casting +------- + +FIXME: this really needs attention from an expert on NumPy's casting rules. But +I can't seem to find the docs that explain how casting loops are looked up and +decided between (e.g., if you're casting from dtype A to dtype B, which dtype's +loops are used?), so I can't go into details. But those details are tricky and +they matter... + +But the general idea is, if you have a dtype with ``NPY_NA_AUTO_CAST`` set, +then the following conversions are automatically allowed: + + * Casting from the underlying type to the NA-type: this is performed by the + * usual ``clearna`` + potentially-strided copy dance. Also, ``isna`` is + * called to check that none of the regular values have been accidentally + * converted into NA; if so, then an error is raised. + * Casting from the NA-type to the underlying type: allowed in principle, but + if ``isna`` returns true for any of the values that are to be converted, + then again, an error is raised. (If you want to get around this, use + ``np.view(array_with_NAs, dtype=float)``.) + * Casting between the NA-type and other types that do not support NA: this is + allowed if the underlying type is allowed to cast to the other type, and is + performed by combining a cast to or from the underlying type (using the + above rules) with a cast to or from the other type (using the underlying + type's rules). + * Casting between the NA-type and other types that do support NA: if the + other type has NPY_NA_AUTO_CAST set, then we use the above rules plus the + usual dance with ``isna`` on one array being converted to ``NA_value`` + elements in the other. If only one of the arrays has NPY_NA_AUTO_CAST set, + then it's assumed that that dtype knows what it's doing, and we don't do + any magic. (But this is one of the things that I'm not sure makes sense, as + per my caveat above.) + +Ufuncs +------ + +All ufuncs gain an additional optional keyword argument, ``skipNA=``, which +defaults to False. + +If ``skipNA == True``, then the ufunc machinery *unconditionally* calls +``isna`` for any dtype where NPY_NA_SUPPORTED(dtype) is true, and then acts as +if any values for which isna returns True were masked out in the ``where=`` +argument (see miniNEP 1 for the behavior of ``where=``). If a ``where=`` +argument is also given, then it acts as if the ``isna`` values had be ANDed out +of the ``where=`` mask, though it does not actually modify the mask. Unlike the +other changes below, this is performed *unconditionally* for any dtype which +has an ``isna`` function defined; the NPY_NA_AUTO_UFUNC flag is *not* checked. + +If NPY_NA_AUTO_UFUNC is set, then ufunc loop lookup is modified so that +whenever it checks for the existence of a loop on the current dtype, and does +not find one, then it also checks for a loop on the ``NA_extends`` dtype. If +that loop is found, then it uses it in the normal way, with the exceptions that +(1) it is only called for values which are not NA according to ``isna``, (2) if +the output array has NPY_NA_AUTO_UFUNC set, then ``clearna`` is called on it +before calling the ufunc loop, (3) pointer offsets are adjusted by +``NA_extends_offset`` before calling the ufunc loop. In addition, if +NPY_NA_AUTO_UFUNC_CHECK is set, then after evaluating the ufunc loop we call +``isna`` on the *output* array, and if there are any NAs in the output which +were not in the input, then we raise an error. (The intention of this is to +catch cases where, say, we represent NA using the most-negative integer, and +then someone's arithmetic overflows to create such a value by accident.) + +FIXME: We should go into more detail here about how NPY_NA_AUTO_UFUNC works +when there are multiple input arrays, of which potentially some have the flag +set and some do not. + +Printing +-------- + +FIXME: There should be some sort of mechanism by which values which are NA are +automatically repr'ed as NA, but I don't really understand how NumPy printing +works, so I'll let someone else fill in this section. + +Indexing +-------- + +Scalar indexing like ``a[12]`` goes via the ``getitem`` function, so according +to the proposal as described above, if a dtype delegates ``getitem``, then +scalar indexing on NAs will return the object ``np.NA``. (If it doesn't +delegate ``getitem``, of course, then it can return whatever it wants.) + +This seems like the simplest approach, but an alternative would be to add a +special case to scalar indexing, where if an ``NPY_NA_AUTO_INDEX`` flag were +set, then it would call ``isna`` on the specified element. If this returned +false, it would call ``getitem`` as usual; otherwise, it would return a 0-d +array containing the specified element. The problem with this is that it breaks +expressions like ``if a[i] is np.NA: ...``. (Of course, there is nothing nearly +so convenient as that for NaN values now, but then, NaN values don't have their +own global singleton.) So for now we stick to scalar indexing just returning +``np.NA``, but this can be revisited if anyone objects. + +.. highlight:: python + +Python API for generic NA support +================================= + +NumPy will gain a global singleton called ``numpy.NA``, similar to None, but with +semantics reflecting its status as a missing value. In particular, trying to +treat it as a boolean will raise an exception, and comparisons with it will +produce ``numpy.NA`` instead of True or False. These basics are adopted from the +behavior of the NA value in the R project. To dig deeper into the ideas, +http://en.wikipedia.org/wiki/Ternary_logic#Kleene_logic provides a starting +point. + +Most operations on ``np.NA`` (e.g., ``__add__``, ``__mul__``) are overridden to +unconditionally return ``np.NA``. + +The automagic dtype detection used for expressions like ``np.asarray([1, 2, +3])``, ``np.asarray([1.0, 2.0. 3.0])`` will be extended to recognize the +``np.NA`` value, and use it to automatically switch to a built-in NA-enabled +dtype (which one being determined by the other elements in the array). A simple +``np.asarray([np.NA])`` will use an NA-enabled float64 dtype (which is +analogous to what you get from ``np.asarray([])``). Note that this means that +expressions like ``np.log(np.NA)`` will work: first ``np.NA`` will be coerced +to a 0-d NA-float array, and then ``np.log`` will be called on that. + +Python-level dtype objects gain the following new fields:: + + NA_supported + NA_value + +``NA_supported`` is a boolean which simply exposes the value of the +``NPY_NA_SUPPORTED`` flag; it should be true if this dtype allows for NAs, +false otherwise. [FIXME: would it be better to just key this off the existence +of the ``isna`` function? Even if a dtype decides to implement all other NA +handling itself, it still has to define ``isna`` in order to make ``skipNA=`` +work correctly.] + +``NA_value`` is a 0-d array of the given dtype, and its sole element contains +the same bit-pattern as the dtype's underlying ``NA_value`` field. This makes +it possible to determine the default bit-pattern for NA values for this type +(e.g., with ``np.view(mydtype.NA_value, dtype=int8)``). + +We *do not* expose the ``NA_extends`` and ``NA_extends_offset`` values at the +Python level, at least for now; they're considered an implementation detail +(and it's easier to expose them later if they're needed then unexpose them if +they aren't). + +Two new ufuncs are defined: ``np.isNA`` returns a logical array, with true +values where-ever the dtype's ``isna`` function returned true. ``np.isnumber`` +is only defined for numeric dtypes, and returns True for all elements which are +not NA, and for which ``np.isfinite`` would return True. + +Builtin NA dtypes +================= + +The above describes the generic machinery for NA support in dtypes. It's +flexible enough to handle all sorts of situations, but we also want to define a +few generally useful NA-supporting dtypes that are available by default. + +For each built-in dtype, we define an associated NA-supporting dtype, as +follows: + +* floats: the associated dtype uses a specific NaN bit-pattern to indicate NA + (chosen for R compatibility) +* complex: we do whatever R does (FIXME: look this up -- two NA floats, + probably?) +* signed integers: the most-negative signed value is used as NA (chosen for R + compatibility) +* unsigned integers: the most-positive value is used as NA (no R compatibility + possible). +* strings: the first byte (or, in the case of unicode strings, first 4 bytes) + is used as a flag to indicate NA, and the rest of the data gives the actual + string. (no R compatibility possible) +* objects: Two options (FIXME): either we don't include an NA-ful version, or + we use np.NA as the NA bit pattern. +* boolean: we do whatever R does (FIXME: look this up -- 0 == FALSE, 1 == TRUE, + 2 == NA?) + +Each of these dtypes is trivially defined using the above machinery, and are +what are automatically used by the automagic type inference machinery (for +``np.asarray([True, np.NA, False])``, etc.). + +They can also be accessed via a new function ``np.withNA``, which takes a +regular dtype (or an object that can be coerced to a dtype, like 'float') and +returns one of the above dtypes. Ideally ``withNA`` should also take some +optional arguments that let you describe which values you want to count as NA, +etc., but I'll leave that for a future draft (FIXME). + +FIXME: If ``d`` is one of the above dtypes, then should ``d.type`` return? + +The NEP also contains a proposal for a somewhat elaborate +domain-specific-language for describing NA dtypes. I'm not sure how great an +idea that is. (I have a bias against using strings as data structures, and find +the already existing strings confusing enough as it is -- also, apparently the +NEP version of NumPy uses strings like 'f8' when printing dtypes, while my +NumPy uses object names like 'float64', so I'm not sure what's going on there. +``withNA(float64, arg1=value1)`` seems like a more pleasant way to print a +dtype than "NA[f8,value1]", at least to me.) But if people want it, then cool. + +Type hierarchy +-------------- + +FIXME: how should we do subtype checks, etc., for NA dtypes? What does +``issubdtype(withNA(float), float)`` return? How about +``issubdtype(withNA(float), np.floating)``? + +Serialization +------------- + + +Copyright +--------- + +This document has been placed in the public domain. diff --git a/doc/neps/nep-0026-missing-data-summary.rst b/doc/neps/nep-0026-missing-data-summary.rst new file mode 100644 index 000000000000..08dbf36d4829 --- /dev/null +++ b/doc/neps/nep-0026-missing-data-summary.rst @@ -0,0 +1,726 @@ +.. _NEP26: + +==================================================== +NEP 26 — Summary of missing data NEPs and discussion +==================================================== + +:Author: Mark Wiebe <mwwiebe@gmail.com>, Nathaniel J. Smith <njs@pobox.com> +:Status: Deferred +:Type: Standards Track +:Created: 2012-04-22 + +*Context*: this NEP was written as summary of the large number of discussions +and proposals (:ref:`NEP12`, :ref:`NEP24`, :ref:`NEP25`), regarding missing data +functionality. + +The debate about how NumPy should handle missing data, a subject with +many preexisting approaches, requirements, and conventions, has been long and +contentious. There has been more than one proposal for how to implement +support into NumPy, and there is a testable implementation which is +merged into NumPy's current main. The vast number of emails and differing +points of view has made it difficult for interested parties to understand +the issues and be comfortable with the direction NumPy is going. + +Here is our (Mark and Nathaniel's) attempt to summarize the +problem, proposals, and points of agreement/disagreement in a single +place, to help the community move towards consensus. + +The NumPy developers' problem +============================= + +For this discussion, "missing data" means array elements +which can be indexed (e.g. A[3] in an array A with shape (5,)), +but have, in some sense, no value. + +It does not refer to compressed or sparse storage techniques where +the value for A[3] is not actually stored in memory, but still has a +well-defined value like 0. + +This is still vague, and to create an actual implementation, +it is necessary to answer such questions as: + +* What values are computed when doing element-wise ufuncs. +* What values are computed when doing reductions. +* Whether the storage for an element gets overwritten when marking + that value missing. +* Whether computations resulting in NaN automatically treat in the + same way as a missing value. +* Whether one interacts with missing values using a placeholder object + (e.g. called "NA" or "masked"), or through a separate boolean array. +* Whether there is such a thing as an array object that cannot hold + missing array elements. +* How the (C and Python) API is expressed, in terms of dtypes, + masks, and other constructs. +* If we decide to answer some of these questions in multiple ways, + then that creates the question of whether that requires multiple + systems, and if so how they should interact. + +There's clearly a very large space of missing-data APIs that *could* +be implemented. There is likely at least one user, somewhere, who +would find any possible implementation to be just the thing they +need to solve some problem. On the other hand, much of NumPy's power +and clarity comes from having a small number of orthogonal concepts, +such as strided arrays, flexible indexing, broadcasting, and ufuncs, +and we'd like to preserve that simplicity. + +There has been dissatisfaction among several major groups of NumPy users +about the existing status quo of missing data support. In particular, +neither the numpy.ma component nor use of floating-point NaNs as a +missing data signal fully satisfy the performance requirements and +ease of use for these users. The example of R, where missing data +is treated via an NA placeholder and is deeply integrated into all +computation, is where many of these users point to indicate what +functionality they would like. Doing a deep integration of missing +data like in R must be considered carefully, it must be clear it +is not being done in a way which sacrifices existing performance +or functionality. + +Our problem is, how can we choose some incremental additions to +NumPy that will make a large class of users happy, be +reasonably elegant, complement the existing design, and that we're +comfortable we won't regret being stuck with in the long term. + +Prior art +========= + +So a major (maybe *the* major) problem is figuring out how ambitious +the project to add missing data support to NumPy should be, and which +kinds of problems are in scope. Let's start with the +best understood situation where "missing data" comes into play: + +"Statistical missing data" +-------------------------- + +In statistics, social science, etc., "missing data" is a term of art +referring to a specific (but extremely common and important) +situation: we have tried to gather some measurements according to some +scheme, but some of these measurements are missing. For example, if we +have a table listing the height, age, and income of a number of +individuals, but one person did not provide their income, then we need +some way to represent this:: + + Person | Height | Age | Income + ------------------------------ + 1 | 63 | 25 | 15000 + 2 | 58 | 32 | <missing> + 3 | 71 | 45 | 30000 + +The traditional way is to record that income as, say, "-99", and +document this in the README along with the data set. Then, you have to +remember to check for and handle such incomes specially; if you +forget, you'll get superficially reasonable but completely incorrect +results, like calculating the average income on this data set as +14967. If you're in one of these fields, then such missing-ness is +routine and inescapable, and if you use the "-99" approach then it's a +pitfall you have to remember to check for explicitly on literally +*every* calculation you ever do. This is, obviously, an unpleasant way +to live. + +Let's call this situation the "statistical missing data" situation, +just to have a convenient handle for it. (As mentioned, practitioners +just call this "missing data", and what to do about it is literally an +entire sub-field of statistics; if you google "missing data" then +every reference is on how to handle it.) NumPy isn't going to do +automatic imputation or anything like that, but it could help a great +deal by providing some standard way to at least represent data which +is missing in this sense. + +The main prior art for how this could be done comes from the S/S+/R +family of languages. Their strategy is, for each type they support, +to define a special value called "NA". (For ints this is INT_MAX, +for floats it's a special NaN value that's distinguishable from +other NaNs, ...) Then, they arrange that in computations, this +value has a special semantics that we will call "NA semantics". + +NA Semantics +------------ + +The idea of NA semantics is that any computations involving NA +values should be consistent with what would have happened if we +had known the correct value. + +For example, let's say we want to compute the mean income, how might +we do this? One way would be to just ignore the missing entry, and +compute the mean of the remaining entries. This gives us (15000 + +30000)/2, or 22500. + +Is this result consistent with discovering the income of person 2? +Let's say we find out that person 2's income is 50000. This means +the correct answer is (15000 + 50000 + 30000)/3, or 31666.67, +indicating clearly that it is not consistent. Therefore, the mean +income is NA, i.e. a specific number whose value we are unable +to compute. + +This motivates the following rules, which are how R implements NA: + +Assignment: + NA values are understood to represent specific + unknown values, and thus should have value-like semantics with + respect to assignment and other basic data manipulation + operations. Code which does not actually look at the values involved + should work the same regardless of whether some of them are + missing. For example, one might write:: + + income[:] = income[np.argsort(height)] + + to perform an in-place sort of the ``income`` array, and know that + the shortest person's income would end up being first. It turns out + that the shortest person's income is not known, so the array should + end up being ``[NA, 15000, 30000]``, but there's nothing + special about NAness here. + +Propagation: + In the example above, we concluded that an operation like ``mean`` + should produce NA when one of its data values was NA. + If you ask me, "what is 3 plus x?", then my only possible answer is + "I don't know what x is, so I don't know what 3 + x is either". NA + means "I don't know", so 3 + NA is NA. + + This is important for safety when analyzing data: missing data often + requires special handling for correctness -- the fact that you are + missing information might mean that something you wanted to compute + cannot actually be computed, and there are whole books written on + how to compensate in various situations. Plus, it's easy to not + realize that you have missing data, and write code that assumes you + have all the data. Such code should not silently produce the wrong + answer. + + There is an important exception to characterizing this as propagation, + in the case of boolean values. Consider the calculation:: + + v = np.any([False, False, NA, True]) + + If we strictly propagate, ``v`` will become NA. However, no + matter whether we place True or False into the third array position, + ``v`` will then get the value True. The answer to the question + "Is the result True consistent with later discovering the value + that was missing?" is yes, so it is reasonable to not propagate here, + and instead return the value True. This is what R does:: + + > any(c(F, F, NA, T)) + [1] TRUE + > any(c(F, F, NA, F)) + [1] NA + +Other: + NaN and NA are conceptually distinct. 0.0/0.0 is not a mysterious, + unknown value -- it's defined to be NaN by IEEE floating point, Not + a Number. NAs are numbers (or strings, or whatever), just unknown + ones. Another small but important difference is that in Python, ``if + NaN: ...`` treats NaN as True (NaN is "truthy"); but ``if NA: ...`` + would be an error. + + In R, all reduction operations implement an alternative semantics, + activated by passing a special argument (``na.rm=TRUE`` in R). + ``sum(a)`` means "give me the sum of all the + values" (which is NA if some of the values are NA); + ``sum(a, na.rm=True)`` means "give me the sum of all the non-NA + values". + +Other prior art +--------------- + +Once we move beyond the "statistical missing data" case, the correct +behavior for missing data becomes less clearly defined. There are many +cases where specific elements are singled out to be treated specially +or excluded from computations, and these could often be conceptualized +as involving 'missing data' in some sense. + +In image processing, it's common to use a single image together with +one or more boolean masks to e.g. composite subsets of an image. As +Joe Harrington pointed out on the list, in the context of processing +astronomical images, it's also common to generalize to a +floating-point valued mask, or alpha channel, to indicate degrees of +"missingness". We think this is out of scope for the present design, +but it is an important use case, and ideally NumPy should support +natural ways of manipulating such data. + +After R, numpy.ma is probably the most mature source of +experience on missing-data-related APIs. Its design is quite different +from R; it uses different semantics -- reductions skip masked values +by default and NaNs convert to masked -- and it uses a different +storage strategy via a separate mask. While it seems to be generally +considered sub-optimal for general use, it's hard to pin down whether +this is because the API is immature but basically good, or the API +is fundamentally broken, or the API is great but the code should be +faster, or what. We looked at some of those users to try and get a +better idea. + +Matplotlib is perhaps the best known package to rely on numpy.ma. It +seems to use it in two ways. One is as a way for users to indicate +what data is missing when passing it to be graphed. (Other ways are +also supported, e.g., passing in NaN values gives the same result.) In +this regard, matplotlib treats np.ma.masked and NaN values in the same way +that R's plotting routines handle NA and NaN values. For these purposes, +matplotlib doesn't really care what semantics or storage strategy is +used for missing data. + +Internally, matplotlib uses numpy.ma arrays to store and pass around +separately computed boolean masks containing 'validity' information +for each input array in a cheap and non-destructive fashion. Mark's +impression from some shallow code review is that mostly it works +directly with the data and mask attributes of the masked arrays, +not extensively using the particular computational semantics of +numpy.ma. So, for this usage they do rely on the non-destructive +mask-based storage, but this doesn't say much about what semantics +are needed. + +Paul Hobson `posted some code`__ on the list that uses numpy.ma for +storing arrays of contaminant concentration measurements. Here the +mask indicates whether the corresponding number represents an actual +measurement, or just the estimated detection limit for a concentration +which was too small to detect. Nathaniel's impression from reading +through this code is that it also mostly uses the .data and .mask +attributes in preference to performing operations on the MaskedArray +directly. + +__ https://mail.scipy.org/pipermail/numpy-discussion/2012-April/061743.html + +So, these examples make it clear that there is demand for a convenient +way to keep a data array and a mask array (or even a floating point +array) bundled up together and "aligned". But they don't tell us much +about what semantics the resulting object should have with respect to +ufuncs and friends. + +Semantics, storage, API, oh my! +=============================== + +We think it's useful to draw a clear line between use cases, +semantics, and storage. Use cases are situations that users encounter, +regardless of what NumPy does; they're the focus of the previous +section. When we say *semantics*, we mean the result of different +operations as viewed from the Python level without regard to the +underlying implementation. + +*NA semantics* are the ones described above and used by R:: + + 1 + NA = NA + sum([1, 2, NA]) = NA + NA | False = NA + NA | True = True + +With ``na.rm=TRUE`` or ``skipNA=True``, this switches to:: + + 1 + NA = illegal # in R, only reductions take na.rm argument + sum([1, 2, NA], skipNA=True) = 3 + +There's also been discussion of what we'll call *ignore +semantics*. These are somewhat underdefined:: + + sum([1, 2, IGNORED]) = 3 + # Several options here: + 1 + IGNORED = 1 + # or + 1 + IGNORED = <leaves output array untouched> + # or + 1 + IGNORED = IGNORED + +The numpy.ma semantics are:: + + sum([1, 2, masked]) = 3 + 1 + masked = masked + +If either NA or ignore semantics are implemented with masks, then there +is a choice of what should be done to the value in the storage +for an array element which gets assigned a missing value. Three +possibilities are: + +* Leave that memory untouched (the choice made in the NEP). +* Do the calculation with the values independently of the mask + (perhaps the most useful option for Paul Hobson's use-case above). +* Copy whatever value is stored behind the input missing value into + the output (this is what numpy.ma does. Even that is ambiguous in + the case of ``masked + masked`` -- in this case numpy.ma copies the + value stored behind the leftmost masked value). + +When we talk about *storage*, we mean the debate about whether missing +values should be represented by designating a particular value of the +underlying data-type (the *bitpattern dtype* option, as used in R), or +by using a separate *mask* stored alongside the data itself. + +For mask-based storage, there is also an important question about what +the API looks like for accessing the mask, modifying the mask, and +"peeking behind" the mask. + +Designs that have been proposed +=============================== + +One option is to just copy R, by implementing a mechanism whereby +dtypes can arrange for certain bitpatterns to be given NA semantics. + +One option is to copy numpy.ma closely, but with a more optimized +implementation. (Or to simply optimize the existing implementation.) + +One option is that described in `NEP12`, for which an implementation +of mask-based missing data exists. This system is roughly: + +* There is both bitpattern and mask-based missing data, and both + have identical interoperable NA semantics. +* Masks are modified by assigning np.NA or values to array elements. + The way to peek behind the mask or to unmask values is to keep a + view of the array that shares the data pointer but not the mask pointer. +* Mark would like to add a way to access and manipulate the mask more + directly, to be used in addition to this view-based API. +* If an array has both a bitpattern dtype and a mask, then assigning + np.NA writes to the mask, rather than to the array itself. Writing + a bitpattern NA to an array which supports both requires accessing + the data by "peeking under the mask". + +Another option is that described in `NEP24`, which is to implement +bitpattern dtypes with NA semantics for the "statistical missing data" +use case, and to also implement a totally independent API for masked +arrays with ignore semantics and all mask manipulation done explicitly +through a .mask attribute. + +Another option would be to define a minimalist aligned array container +that holds multiple arrays and that can be used to pass them around +together. It would support indexing (to help with the common problem +of wanting to subset several arrays together without their becoming +unaligned), but all arithmetic etc. would be done by accessing the +underlying arrays directly via attributes. The "prior art" discussion +above suggests that something like this holding a .data and a .mask +array might actually be solve a number of people's problems without +requiring any major architectural changes to NumPy. This is similar to +a structured array, but with each field in a separately stored array +instead of packed together. + +Several people have suggested that there should be a single system +that has multiple missing values that each have different semantics, +e.g., a MISSING value that has NA semantics, and a separate IGNORED +value that has ignored semantics. + +None of these options are necessarily exclusive. + +The debate +========== + +We both are dubious of using ignored semantics as a default missing +data behavior. **Nathaniel** likes NA semantics because he is most +interested in the "statistical missing data" use case, and NA semantics +are exactly right for that. **Mark** isn't as interested in that use +case in particular, but he likes the NA computational abstraction +because it is unambiguous and well-defined in all cases, and has a +lot of existing experience to draw from. + +What **Nathaniel** thinks, overall: + +* The "statistical missing data" use case is clear and compelling; the + other use cases certainly deserve our attention, but it's hard to say what + they *are* exactly yet, or even if the best way to support them is + by extending the ndarray object. +* The "statistical missing data" use case is best served by an R-style + system that uses bitpattern storage to implement NA semantics. The + main advantage of bitpattern storage for this use case is that it + avoids the extra memory and speed overhead of storing and checking a + mask (especially for the common case of floating point data, where + some tricks with NaNs allow us to effectively hardware-accelerate + most NA operations). These concerns alone appears to make a + mask-based implementation unacceptable to many NA users, + particularly in areas like neuroscience (where memory is tight) or + financial modeling (where milliseconds are critical). In addition, + the bit-pattern approach is less confusing conceptually (e.g., + assignment really is just assignment, no magic going on behind the + curtain), and it's possible to have in-memory compatibility with R + for inter-language calls via rpy2. The main disadvantage of the + bitpattern approach is the need to give up a value to represent NA, + but this is not an issue for the most important data types (float, + bool, strings, enums, objects); really, only integers are + affected. And even for integers, giving up a value doesn't really + matter for statistical problems. (Occupy Wall Street + notwithstanding, no-one's income is 2**63 - 1. And if it were, we'd + be switching to floats anyway to avoid overflow.) +* Adding new dtypes requires some cooperation with the ufunc and + casting machinery, but doesn't require any architectural changes or + violations of NumPy's current orthogonality. +* His impression from the mailing list discussion, esp. the `"what can + we agree on?" thread`__, is that many numpy.ma users specifically + like the combination of masked storage, the mask being easily + accessible through the API, and ignored semantics. He could be + wrong, of course. But he cannot remember seeing anybody besides Mark + advocate for the specific combination of masked storage and NA + semantics, which makes him nervous. + + __ http://thread.gmane.org/gmane.comp.python.numeric.general/46704 +* Also, he personally is not very happy with the idea of having two + storage implementations that are almost-but-not-quite identical at + the Python level. While there likely are people who would like to + temporarily pretend that certain data is "statistically missing + data" without making a copy of their array, it's not at all clear + that they outnumber the people who would like to use bitpatterns and + masks simultaneously for distinct purposes. And honestly he'd like + to be able to just ignore masks if he wants and stick to + bitpatterns, which isn't possible if they're coupled together + tightly in the API. So he would say the jury is still very much out + on whether this aspect of the NEP design is an advantage or a + disadvantage. (Certainly he's never heard of any R users complaining + that they really wish they had an option of making a different + trade-off here.) +* R's NA support is a `headline feature`__ and its target audience + consider it a compelling advantage over other platforms like Matlab + or Python. Working with statistical missing data is very painful + without platform support. + + __ http://www.sr.bham.ac.uk/~ajrs/R/why_R.html +* By comparison, we clearly have much more uncertainty about the use + cases that require a mask-based implementation, and it doesn't seem + like people will suffer too badly if they are forced for now to + settle for using NumPy's excellent mask-based indexing, the new + where= support, and even numpy.ma. +* Therefore, bitpatterns with NA semantics seem to meet the criteria + of making a large class of users happy, in an elegant way, that fits + into the original design, and where we can have reasonable certainty + that we understand the problem and use cases well enough that we'll + be happy with them in the long run. But no mask-based storage + proposal does, yet. + +What **Mark** thinks, overall: + +* The idea of using NA semantics by default for missing data, inspired + by the "statistical missing data" problem, is better than all the + other default behaviors which were considered. This applies equally + to the bitpattern and the masked approach. + +* For NA-style functionality to get proper support by all NumPy + features and eventually all third-party libraries, it needs to be + in the core. How to correctly and efficiently handle missing data + differs by algorithm, and if thinking about it is required to fully + support NumPy, NA support will be broader and higher quality. + +* At the same time, providing two different missing data interfaces, + one for masks and one for bitpatterns, requires NumPy developers + and third-party NumPy plugin developers to separately consider the + question of what to do in either case, and do two additional + implementations of their code. This complicates their job, + and could lead to inconsistent support for missing data. + +* Providing the ability to work with both masks and bitpatterns through + the same C and Python programming interface makes missing data support + cleanly orthogonal with all other NumPy features. + +* There are many trade-offs of memory usage, performance, correctness, and + flexibility between masks and bitpatterns. Providing support for both + approaches allows users of NumPy to choose the approach which is + most compatible with their way of thinking, or has characteristics + which best match their use-case. Providing them through the same + interface further allows them to try both with minimal effort, and + choose the one which performs better or uses the least memory for + their programs. + +* Memory Usage + + * With bitpatterns, less memory is used for storing a single array + containing some NAs. + + * With masks, less memory is used for storing multiple arrays that + are identical except for the location of their NAs. (In this case a + single data array can be re-used with multiple mask arrays; + bitpattern NAs would need to copy the whole data array.) + +* Performance + + * With bitpatterns, the floating point type can use native hardware + operations, with nearly correct behavior. For fully correct floating + point behavior and with other types, code must be written which + specially tests for equality with the missing-data bitpattern. + + * With masks, there is always the overhead of accessing mask memory + and testing its truth value. The implementation that currently exists + has no performance tuning, so it is only good to judge a minimum + performance level. Optimal mask-based code is in general going to + be slower than optimal bitpattern-based code. + +* Correctness + + * Bitpattern integer types must sacrifice a valid value to represent NA. + For larger integer types, there are arguments that this is ok, but for + 8-bit types there is no reasonable choice. In the floating point case, + if the performance of native floating point operations is chosen, + there is a small inconsistency that NaN+NA and NA+NaN are different. + * With masks, it works correctly in all cases. + +* Generality + + * The bitpattern approach can work in a fully general way only when + there is a specific value which can be given up from the + data type. For IEEE floating point, a NaN is an obvious choice, + and for booleans represented as a byte, there are plenty of choices. + For integers, a valid value must be sacrificed to use this approach. + Third-party dtypes which plug into NumPy will also have to + make a bitpattern choice to support this system, something which + may not always be possible. + + * The mask approach works universally with all data types. + +Recommendations for Moving Forward +================================== + +**Nathaniel** thinks we should: + +* Go ahead and implement bitpattern NAs. +* *Don't* implement masked arrays in the core -- or at least, not + yet. Instead, we should focus on figuring out how to implement them + out-of-core, so that people can try out different approaches without + us committing to any one approach. And so new prototypes can be + released more quickly than the NumPy release cycle. And anyway, + we're going to have to figure out how to experiment with such + changes out-of-core if NumPy is to continue to evolve without + forking -- might as well do it now. The existing code can live in + the main branch, be disabled, or live its own branch -- it'll still be there + once we know what we're doing. + +**Mark** thinks we should: + +* The existing code should remain as is, with a global run-time experimental + flag added which disables NA support by default. + +A more detailed rationale for this recommendation is: + +* A solid preliminary NA-mask implementation is currently in NumPy + main. This implementation has been extensively tested + against scipy and other third-party packages, and has been in main + in a stable state for a significant amount of time. +* This implementation integrates deeply with the core, providing an + interface which is usable in the same way R's NA support is. It + provides a compelling, user-friendly answer to R's NA support. +* The missing data NEP provides a plan for adding bitpattern-based + dtype support of NAs, which will operate through the same interface + but allow for the same performance/correctness tradeoffs that R has made. +* Making it very easy for users to try out this implementation, which + has reasonable feature coverage and performance characteristics, is + the best way to get more concrete feedback about how NumPy's missing + data support should look. + +Because of its preliminary state, the existing implementation is marked +as experimental in the NumPy documentation. It would be good for this +to remain marked as experimental until it is more fleshed out, for +example supporting struct and array dtypes and with a fuller set of +NumPy operations. + +I think the code should stay as it is, except to add a run-time global +NumPy flag, perhaps numpy.experimental.maskna, which defaults to +False and can be toggled to True. In its default state, any NA feature +usage would raise an "ExperimentalError" exception, a measure which +would prevent it from being accidentally used and communicate its +experimental status very clearly. + +The `ABI issues`__ seem very tricky to deal with effectively in the 1.x +series of releases, but I believe that with proper implementation-hiding +in a 2.0 release, evolving the software to support various other +ABI ideas that have been discussed is feasible. This is the approach +I like best. + +__ http://thread.gmane.org/gmane.comp.python.numeric.general/49485> + +**Nathaniel** notes in response that he doesn't really have any +objection to shipping experimental APIs in the main numpy distribution +*if* we're careful to make sure that they don't "leak out" in a way +that leaves us stuck with them. And in principle some sort of "this +violates your warranty" global flag could be a way to do that. (In +fact, this might also be a useful strategy for the kinds of changes +that he favors, of adding minimal hooks to enable us to build +prototypes more easily -- we could have some "rapid prototyping only" +hooks that let prototype hacks get deeper access to NumPy's internals +than we were otherwise ready to support.) + +But, he wants to point out two things. First, it seems like we still +have fundamental questions to answer about the NEP design, like +whether masks should have NA semantics or ignore semantics, and there +are already plans to majorly change how NEP masks are exposed and +accessed. So he isn't sure what we'll learn by asking for feedback on +the NEP code in its current state. + +And second, given the concerns about their causing (minor) ABI issues, +it's not clear that we could really prevent them from leaking out. (He +looks forward to 2.0 too, but we're not there yet.) So maybe it would +be better if they weren't present in the C API at all, and the hoops +required for testers were instead something like, 'we have included a +hacky pure-Python prototype accessible by typing "import +numpy.experimental.donttrythisathome.NEP" and would welcome feedback'? + +If so, then he should mention that he did implement a horribly klugy, +pure Python implementation of the NEP API that works with NumPy +1.6.1. This was mostly as an experiment to see how possible such +prototyping was and to test out a possible ufunc override mechanism, +but if there's interest, the module is available here: +https://github.com/njsmith/numpyNEP + +It passes the maskna test-suite, with some minor issues described +in a big comment at the top. + +**Mark** responds: + +I agree that it's important to be careful when adding new +features to NumPy, but I also believe it is essential that the project +have forward development momentum. A project like NumPy requires +developers to write code for advancement to occur, and obstacles +that impede the writing of code discourage existing developers +from contributing more, and potentially scare away developers +who are thinking about joining in. + +All software projects, both open source and closed source, must +balance between short-term practicality and long-term planning. +In the case of the missing data development, there was a short-term +resource commitment to tackle this problem, which is quite immense +in scope. If there isn't a high likelihood of getting a contribution +into NumPy that concretely advances towards a solution, I expect +that individuals and companies interested in doing such work will +have a much harder time justifying a commitment of their resources. +For a project which is core to so many other libraries, only +relying on the good will of selfless volunteers would mean that +NumPy could more easily be overtaken by another project. + +In the case of the existing NA contribution at issue, how we resolve +this disagreement represents a decision about how NumPy's +developers, contributors, and users should interact. If we create +a document describing a dispute resolution process, how do we +design it so that it doesn't introduce a large burden and excessive +uncertainty on developers that could prevent them from productively +contributing code? + +If we go this route of writing up a decision process which includes +such a dispute resolution mechanism, I think the meat of it should +be a roadmap that potential contributors and developers can follow +to gain influence over NumPy. NumPy development needs broad support +beyond code contributions, and tying influence in the project to +contributions seems to me like it would be a good way to encourage +people to take on tasks like bug triaging/management, continuous +integration/build server administration, and the myriad other +tasks that help satisfy the project's needs. No specific meritocratic, +democratic, consensus-striving system will satisfy everyone, but the +vigour of the discussions around governance and process indicate that +something at least a little bit more formal than the current status +quo is necessary. + +In conclusion, I would like the NumPy project to prioritize movement +towards a more flexible and modular ABI/API, balanced with strong +backwards-compatibility constraints and feature additions that +individuals, universities, and companies want to contribute. +I do not believe keeping the NA code in 1.7 as it is, with the small +additional measure of requiring it to be enabled by an experimental +flag, poses a risk of long-term ABI troubles. The greater risk I see +is a continuing lack of developers contributing to the project, +and I believe backing out this code because these worries would create a +risk of reducing developer contribution. + + +References and Footnotes +------------------------ + +:ref:`NEP12` describes Mark's NA-semantics/mask implementation/view based mask +handling API. + +:ref:`NEP24` ("the alterNEP") was Nathaniel's initial attempt at separating MISSING +and IGNORED handling into bit-patterns versus masks, though there's a bunch +he would change about the proposal at this point. + +:ref:`NEP25` ("miniNEP 2") was a later attempt by Nathaniel to sketch out an +implementation strategy for NA dtypes. + +A further discussion overview page can be found at: +https://github.com/njsmith/numpy/wiki/NA-discussion-status + + +Copyright +--------- + +This document has been placed in the public domain. diff --git a/doc/neps/nep-0027-zero-rank-arrarys.rst b/doc/neps/nep-0027-zero-rank-arrarys.rst new file mode 100644 index 000000000000..eef4bcacc4cd --- /dev/null +++ b/doc/neps/nep-0027-zero-rank-arrarys.rst @@ -0,0 +1,256 @@ +.. _NEP27: + +========================= +NEP 27 — Zero rank arrays +========================= + +:Author: Alexander Belopolsky (sasha), transcribed Matt Picus <matti.picus@gmail.com> +:Status: Final +:Type: Informational +:Created: 2006-06-10 +:Resolution: https://mail.python.org/pipermail/numpy-discussion/2018-October/078824.html + +.. note:: + + NumPy has both zero rank arrays and scalars. This design document, adapted + from a `2006 wiki entry`_, describes what zero rank arrays are and why they + exist. It was transcribed 2018-10-13 into a NEP and links were updated. + The pull request sparked `a lively discussion`_ about the continued need + for zero rank arrays and scalars in NumPy. + + Some of the information here is dated, for instance indexing of 0-D arrays + now is now implemented and does not error. + +Zero-Rank Arrays +---------------- + +Zero-rank arrays are arrays with shape=(). For example: + + >>> x = array(1) + >>> x.shape + () + + +Zero-Rank Arrays and Array Scalars +---------------------------------- + +Array scalars are similar to zero-rank arrays in many aspects:: + + + >>> int_(1).shape + () + +They even print the same:: + + + >>> print int_(1) + 1 + >>> print array(1) + 1 + + +However there are some important differences: + +* Array scalars are immutable +* Array scalars have different python type for different data types + +Motivation for Array Scalars +---------------------------- + +NumPy's design decision to provide 0-d arrays and array scalars in addition to +native python types goes against one of the fundamental python design +principles that there should be only one obvious way to do it. In this section +we will try to explain why it is necessary to have three different ways to +represent a number. + +There were several numpy-discussion threads: + + +* `rank-0 arrays`_ in a 2002 mailing list thread. +* Thoughts about zero dimensional arrays vs Python scalars in a `2005 mailing list thread`_] + +It has been suggested several times that NumPy just use rank-0 arrays to +represent scalar quantities in all case. Pros and cons of converting rank-0 +arrays to scalars were summarized as follows: + +- Pros: + + - Some cases when Python expects an integer (the most + dramatic is when slicing and indexing a sequence: + _PyEval_SliceIndex in ceval.c) it will not try to + convert it to an integer first before raising an error. + Therefore it is convenient to have 0-dim arrays that + are integers converted for you by the array object. + + - No risk of user confusion by having two types that + are nearly but not exactly the same and whose separate + existence can only be explained by the history of + Python and NumPy development. + + - No problems with code that does explicit typechecks + ``(isinstance(x, float)`` or ``type(x) == types.FloatType)``. Although + explicit typechecks are considered bad practice in general, there are a + couple of valid reasons to use them. + + - No creation of a dependency on Numeric in pickle + files (though this could also be done by a special case + in the pickling code for arrays) + +- Cons: + + - It is difficult to write generic code because scalars + do not have the same methods and attributes as arrays. + (such as ``.type`` or ``.shape``). Also Python scalars have + different numeric behavior as well. + + - This results in a special-case checking that is not + pleasant. Fundamentally it lets the user believe that + somehow multidimensional homoegeneous arrays + are something like Python lists (which except for + Object arrays they are not). + +NumPy implements a solution that is designed to have all the pros and none of the cons above. + + Create Python scalar types for all of the 21 types and also + inherit from the three that already exist. Define equivalent + methods and attributes for these Python scalar types. + +The Need for Zero-Rank Arrays +----------------------------- + +Once the idea to use zero-rank arrays to represent scalars was rejected, it was +natural to consider whether zero-rank arrays can be eliminated altogether. +However there are some important use cases where zero-rank arrays cannot be +replaced by array scalars. See also `A case for rank-0 arrays`_ from February +2006. + +* Output arguments:: + + >>> y = int_(5) + >>> add(5,5,x) + array(10) + >>> x + array(10) + >>> add(5,5,y) + Traceback (most recent call last): + File "<stdin>", line 1, in ? + TypeError: return arrays must be of ArrayType + +* Shared data:: + + >>> x = array([1,2]) + >>> y = x[1:2] + >>> y.shape = () + >>> y + array(2) + >>> x[1] = 20 + >>> y + array(20) + +Indexing of Zero-Rank Arrays +---------------------------- + +As of NumPy release 0.9.3, zero-rank arrays do not support any indexing:: + + >>> x[...] + Traceback (most recent call last): + File "<stdin>", line 1, in ? + IndexError: 0-d arrays can't be indexed. + +On the other hand there are several cases that make sense for rank-zero arrays. + +Ellipsis and empty tuple +~~~~~~~~~~~~~~~~~~~~~~~~ + +Alexander started a `Jan 2006 discussion`_ on scipy-dev +with the following proposal: + + ... it may be reasonable to allow ``a[...]``. This way + ellipsis can be interpereted as any number of ``:`` s including zero. + Another subscript operation that makes sense for scalars would be + ``a[...,newaxis]`` or even ``a[{newaxis, }* ..., {newaxis,}*]``, where + ``{newaxis,}*`` stands for any number of comma-separated newaxis tokens. + This will allow one to use ellipsis in generic code that would work on + any numpy type. + +Francesc Altet supported the idea of ``[...]`` on zero-rank arrays and +`suggested`_ that ``[()]`` be supported as well. + +Francesc's proposal was:: + + In [65]: type(numpy.array(0)[...]) + Out[65]: <type 'numpy.ndarray'> + + In [66]: type(numpy.array(0)[()]) # Indexing a la numarray + Out[66]: <type 'int32_arrtype'> + + In [67]: type(numpy.array(0).item()) # already works + Out[67]: <type 'int'> + +There is a consensus that for a zero-rank array ``x``, both ``x[...]`` and ``x[()]`` should be valid, but the question +remains on what should be the type of the result - zero rank ndarray or ``x.dtype``? + +(Alexander) + First, whatever choice is made for ``x[...]`` and ``x[()]`` they should be + the same because ``...`` is just syntactic sugar for "as many `:` as + necessary", which in the case of zero rank leads to ``... = (:,)*0 = ()``. + Second, rank zero arrays and numpy scalar types are interchangeable within + numpy, but numpy scalars can be use in some python constructs where ndarrays + can't. For example:: + + >>> (1,)[array(0)] + Traceback (most recent call last): + File "<stdin>", line 1, in ? + TypeError: tuple indices must be integers + >>> (1,)[int32(0)] + 1 + +Since most if not all numpy function automatically convert zero-rank arrays to scalars on return, there is no reason for +``[...]`` and ``[()]`` operations to be different. + +See SVN changeset 1864 (which became git commit `9024ff0`_) for +implementation of ``x[...]`` and ``x[()]`` returning numpy scalars. + +See SVN changeset 1866 (which became git commit `743d922`_) for +implementation of ``x[...] = v`` and ``x[()] = v`` + +Increasing rank with newaxis +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Everyone who commented liked this feature, so as of SVN changeset 1871 (which became git commit `b32744e`_) any number of ellipses and +newaxis tokens can be placed as a subscript argument for a zero-rank array. For +example:: + + >>> x = array(1) + >>> x[newaxis,...,newaxis,...] + array([[1]]) + +It is not clear why more than one ellipsis should be allowed, but this is the +behavior of higher rank arrays that we are trying to preserve. + +Refactoring +~~~~~~~~~~~ + +Currently all indexing on zero-rank arrays is implemented in a special ``if (nd +== 0)`` branch of code that used to always raise an index error. This ensures +that the changes do not affect any existing usage (except, the usage that +relies on exceptions). On the other hand part of motivation for these changes +was to make behavior of ndarrays more uniform and this should allow to +eliminate ``if (nd == 0)`` checks altogether. + +Copyright +--------- + +The original document appeared on the scipy.org wiki, with no Copyright notice, and its `history`_ attributes it to sasha. + +.. _`2006 wiki entry`: https://web.archive.org/web/20100503065506/http://projects.scipy.org:80/numpy/wiki/ZeroRankArray +.. _`history`: https://web.archive.org/web/20100503065506/http://projects.scipy.org:80/numpy/wiki/ZeroRankArray?action=history +.. _`2005 mailing list thread`: https://sourceforge.net/p/numpy/mailman/message/11299166 +.. _`suggested`: https://mail.python.org/pipermail/numpy-discussion/2006-January/005572.html +.. _`Jan 2006 discussion`: https://mail.python.org/pipermail/numpy-discussion/2006-January/005579.html +.. _`A case for rank-0 arrays`: https://mail.python.org/pipermail/numpy-discussion/2006-February/006384.html +.. _`rank-0 arrays`: https://mail.python.org/pipermail/numpy-discussion/2002-September/001600.html +.. _`9024ff0`: https://github.com/numpy/numpy/commit/9024ff0dc052888b5922dde0f3e615607a9e99d7 +.. _`743d922`: https://github.com/numpy/numpy/commit/743d922bf5893acf00ac92e823fe12f460726f90 +.. _`b32744e`: https://github.com/numpy/numpy/commit/b32744e3fc5b40bdfbd626dcc1f72907d77c01c4 +.. _`a lively discussion`: https://github.com/numpy/numpy/pull/12166 diff --git a/doc/neps/nep-0028-website-redesign.rst b/doc/neps/nep-0028-website-redesign.rst new file mode 100644 index 000000000000..592209a5ff89 --- /dev/null +++ b/doc/neps/nep-0028-website-redesign.rst @@ -0,0 +1,338 @@ +.. _NEP28: + +=================================== +NEP 28 — numpy.org website redesign +=================================== + +:Author: Ralf Gommers <ralf.gommers@gmail.com> +:Author: Joe LaChance <joe@boldmetrics.com> +:Author: Shekhar Rajak <shekharrajak.1994@gmail.com> +:Status: Final +:Type: Informational +:Created: 2019-07-16 +:Resolution: https://mail.python.org/pipermail/numpy-discussion/2019-August/079889.html + + +Abstract +-------- + +NumPy is the fundamental library for numerical and scientific computing with +Python. It is used by millions and has a large team of maintainers and +contributors. Despite that, its `numpy.org <http://numpy.org>`_ website has +never received the attention it needed and deserved. We hope and intend to +change that soon. This document describes ideas and requirements for how to +design a replacement for the current website, to better serve the needs of +our diverse community. + +At a high level, what we're aiming for is: + +- a modern, clean look +- an easy to deploy static site +- a structure that's easy to navigate +- content that addresses all types of stakeholders +- Possible multilingual translations / i18n + +This website serves a couple of roles: + +- it's the entry point to the project for new users +- it should link to the documentation (which is hosted separately, now on + http://docs.scipy.org/ and in the near future on http://numpy.org/doc). +- it should address various aspects of the project (e.g. what NumPy is and + why you'd want to use it, community, project organization, funding, + relationship with NumFOCUS and possibly other organizations) +- it should link out to other places, so every type of stakeholder + (beginning and advanced user, educators, packagers, funders, etc.) + can find their way + + +Motivation and Scope +-------------------- + +The current numpy.org website has almost no content and its design is poor. +This affects many users, who come there looking for information. It also +affects many other aspects of the NumPy project, from finding new contributors +to fundraising. + +The scope of the proposed redesign is the top-level numpy.org site, which +now contains only a couple of pages and may contain on the order of ten +pages after the redesign. Changing the documentation (user guide, reference +guide, and some other pages in the NumPy Manual) is out of scope for +this proposal. + + +Detailed description +-------------------- + +User Experience +~~~~~~~~~~~~~~~ + +Besides the NumPy logo, there is little that can or needs to be kept from the +current website. We will rely to a large extent on ideas and proposals by the +designer(s) of the new website. + +As reference points we can use the `Jupyter website <https://jupyter.org/>`_, +which is probably the best designed site in our ecosystem, and the +`QuantEcon <https://quantecon.org>`_ and `Julia <https://julialang.org>`_ +sites which are well-designed too. + +The Website +~~~~~~~~~~~ + +A static site is a must. There are many high-quality static site generators. +The current website uses Sphinx, however that is not the best choice - it's +hard to theme and results in sites that are too text-heavy due to Sphinx' +primary aim being documentation. + +The following should be considered when choosing a static site generator: + +1. *How widely used is it?* This is important when looking for help maintaining + or improving the site. More popular frameworks are usually also better + maintained, so less chance of bugs or obsolescence. +2. *Ease of deployment.* Most generators meet this criterion, however things + like built-in support for GitHub Pages helps. +3. *Preferences of who implements the new site.* Everyone has their own + preferences. And it's a significant amount of work to build a new site. + So we should take the opinion of those doing the work into account. + +Traffic +``````` + +The current site receives on the order of 500,000 unique visitors per month. +With a redesigned site and relevant content, there is potential for visitor +counts to reach 5-6 million -- a similar level as +`scipy.org <http://scipy.org>`_ or `matplotlib.org <http://matplotlib.org>`_ -- +or more. + +Possible options for static site generators +``````````````````````````````````````````` + +1. *Jekyll.* This is a well maintained option with 855 Github contributors, + with contributions within the last month. Jekyll is written in Ruby, and + has a simple CLI interface. Jekyll also has a large directory of + `themes <https://jekyllthemes.io>`__, although a majority cost money. + There are several themes (`serif <https://jekyllthemes.io/theme/serif>`_, + `uBuild <https://jekyllthemes.io/theme/ubuild-jekyll-theme>`_, + `Just The Docs <https://jekyllthemes.io/theme/just-the-docs>`_) that are + appropriate and free. Most themes are likely responsive for mobile, and + that should be a requirement. Jekyll uses a combination of liquid templating + and YAML to render HTML, and content is written in Markdown. i18n + functionality is not native to Jekyll, but can be added easily. + One nice benefit of Jekyll is that it can be run automatically by GitHub + Pages, so deployment via a CI system doesn't need to be implemented. +2. *Hugo.* This is another well maintained option with 554 contributors, with + contributions within the last month. Hugo is written in Go, and similar to + Jekyll, has a simple to use CLI interface to generate static sites. Again, + similar to Jekyll, Hugo has a large directory of + `themes <https://themes.gohugo.io>`_. These themes appear to be free, + unlike some of Jekyll's themes. + (`Sample landing page theme <https://themes.gohugo.io/hugo-hero-theme>`_, + `docs theme <https://themes.gohugo.io/hugo-whisper-theme>`_). Hugo uses Jade + as its templating language, and content is also written in Markdown. i18n + functionality is native to Hugo. +3. *Docusaurus.* Docusaurus is a responsive static site generator made by Facebook. + Unlike the previous options, Docusaurus doesn't come with themes, and thus we + would not want to use this for our landing page. This is an excellent docs + option written in React. Docusaurus natively has support for i18n (via + Crowdin_), document versioning, and document search. + +Both Jekyll and Hugo are excellent options that should be supported into the +future and are good choices for NumPy. Docusaurus has several bonus features +such as versioning and search that Jekyll and Hugo don't have, but is likely +a poor candidate for a landing page - it could be a good option for a +high-level docs site later on though. + +Deployment +~~~~~~~~~~ + +There is no need for running a server, and doing so is in our experience a +significant drain on the time of maintainers. + +1. *Netlify.* Using netlify is free until 100GB of bandwidth is used. Additional + bandwidth costs $20/100GB. They support a global CDN system, which will keep + load times quick for users in other regions. Netlify also has Github integration, + which will allow for easy deployment. When a pull request is merged, Netlify + will automatically deploy the changes. DNS is simple, and HTTPS is also supported. +2. *Github Pages.* Github Pages also has a 100GB bandwidth limit, and is unclear if + additional bandwidth can be purchased. It is also unclear where sites are deployed, + and should be assumed sites aren't deployed globally. Github Pages has an easy to + use CI & DNS, similar to Netlify. HTTPS is supported. +3. *Cloudflare.* An excellent option, additional CI is likely needed for the same + ease of deployment. + +All of the above options are appropriate for the NumPy site based on current +traffic. Updating to a new deployment strategy, if needed, is a minor amount of +work compared to developing the website itself. If a provider such as +Cloudflare is chosen, additional CI may be required, such as CircleCI, to +have a similar deployment to GitHub Pages or Netlify. + +Analytics +~~~~~~~~~ + +It's beneficial to maintainers to know how many visitors are coming to +numpy.org. Google Analytics offers visitor counts and locations. This will +help to support and deploy more strategically, and help maintainers +understand where traffic is coming from. + +Google Analytics is free. A script, provided by Google, must be added to the home page. + +Website Structure +~~~~~~~~~~~~~~~~~ + +We aim to keep the first version of the new website small in terms of amount +of content. New pages can be added later on, it's more important right now to +get the site design right and get some essential information up. Note that in +the second half of 2019 we expect to get 1 or 2 tech writers involved in the +project via Google Season of Docs. They will likely help improve the content +and organization of that content. + +We propose the following structure: + +0. Front page: essentials of what NumPy is (compare e.g. jupyter.org), one or + a couple key user stories (compare e.g. julialang.org) +1. Install +2. Documentation +3. Array computing +4. Community +5. Learning +6. About Us +7. Contribute +8. Donate + +There may be a few other pages, e.g. a page on performance, that are linked +from one of the main pages. + +Stakeholder Content +~~~~~~~~~~~~~~~~~~~ + +This should have as little content as possible *within the site*. Somewhere +on the site we should link out to content that's specific to: + +- beginning users (quickstart, tutorial) +- advanced users +- educators +- packagers +- package authors that depend on NumPy +- funders (governance, roadmap) + +Translation (multilingual / i18n) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +NumPy has users all over the world. Most of those users are not native +English speakers, and many don't speak English well or at all. Therefore +having content in multiple languages is potentially addressing a large unmet +need. It would likely also help make the NumPy project more diverse and +welcoming. + +On the other hand, there are good reasons why few projects have a +multi-lingual site. It's potentially a lot of extra work. Extra work for +maintainers is costly - they're already struggling to keep up with the work +load. Therefore we have to very carefully consider whether a multi-lingual +site is feasible and weight costs and benefits. + +We start with an assertion: maintaining translations of all documentation, or +even the whole user guide, as part of the NumPy project is not feasible. One +simply has to look at the volume of our documentation and the frequency with +which we change it to realize that that's the case. Perhaps it will be +feasible though to translate just the top-level pages of the website. Those +do not change very often, and it will be a limited amount of content (order +of magnitude 5-10 pages of text). + +We propose the following requirements for adding a language: + +- The language must have a dedicated maintainer +- There must be a way to validate content changes (e.g. a second + maintainer/reviewer, or high quality language support in a freely + available machine translation tool) +- The language must have a reasonable size target audience (to be + assessed by the NumPy maintainers) + +Furthermore we propose a policy for when to remove support for a language again +(preferably by hiding it rather than deleting content). This may be done when +the language no longer has a maintainer, and coverage of translations falls +below an acceptable threshold (say 80%). + +Benefits of having translations include: + +- Better serve many existing and potential users +- Potentially attract a culturally and geographically more diverse set of contributors + +The tradeoffs are: + +- Cost of maintaining a more complex code base +- Cost of making decisions about whether or not to add a new language +- Higher cost to making content changes, creates work for language maintainers +- Any content change should be rolled out with enough delay to have translations in place + +Can we define a small enough set of pages and content that it makes sense to do this? +Probably yes. + +Is there an easy to use tool to maintain translations and add them to the website? +To be discussed - it needs investigating, and may depend on the choice of static site +generator. One potential option is Crowdin_, which is free for open source projects. + + +Style and graphic design +~~~~~~~~~~~~~~~~~~~~~~~~ + +Beyond the "a modern, clean look" goal we choose to not specify too much. A +designer may have much better ideas than the authors of this proposal, hence we +will work with the designer(s) during the implementation phase. + +The NumPy logo could use a touch-up. The logo widely recognized and its colors and +design are good, however the look-and-feel is perhaps a little dated. + + +Other aspects +~~~~~~~~~~~~~ + +A search box would be nice to have. The Sphinx documentation already has a +search box, however a search box on the main site which provides search results +for the docs, the website, and perhaps other domains that are relevant for +NumPy would make sense. + + +Backward compatibility +---------------------- + +Given a static site generator is chosen, we will migrate away from Sphinx for +numpy.org (the website, *not including the docs*). The current deployment can +be preserved until a future deprecation date is decided (potentially based on +the comfort level of our new site). + +All site generators listed above have visibility into the HTML and Javascript +that is generated, and can continue to be maintained in the event a given +project ceases to be maintained. + + +Alternatives +------------ + +Alternatives we considered for the overall design of the website: + +1. *Update current site.* A new Sphinx theme could be chosen. This would likely + take the least amount of resources initially, however, Sphinx does not have + the features we are looking for moving forward such as i18n, responsive design, + and a clean, modern look. + Note that updating the docs Sphinx theme is likely still a good idea - it's + orthogonal to this NEP though. +2. *Create custom site.* This would take the most amount of resources, and is + likely to have additional benefit in comparison to a static site generator. + All features would be able to be added at the cost of developer time. + + +Discussion +---------- + +- Pull request for this NEP (with a good amount of discussion): https://github.com/numpy/numpy/pull/14032 +- Email about NEP for review: https://mail.python.org/pipermail/numpy-discussion/2019-July/079856.html +- Proposal to accept this NEP: https://mail.python.org/pipermail/numpy-discussion/2019-August/079889.html + + +References and Footnotes +------------------------ +.. _Crowdin: https://crowdin.com/pricing#annual + +Copyright +--------- + +This document has been placed in the public domain. diff --git a/doc/neps/nep-0029-deprecation_policy.rst b/doc/neps/nep-0029-deprecation_policy.rst new file mode 100644 index 000000000000..a50afcb98f9d --- /dev/null +++ b/doc/neps/nep-0029-deprecation_policy.rst @@ -0,0 +1,317 @@ +.. _NEP29: + +================================================================================== +NEP 29 — Recommend Python and NumPy version support as a community policy standard +================================================================================== + + +:Author: Thomas A Caswell <tcaswell@gmail.com>, Andreas Mueller, Brian Granger, Madicken Munk, Ralf Gommers, Matt Haberland <mhaberla@calpoly.edu>, Matthias Bussonnier <bussonniermatthias@gmail.com>, Stefan van der Walt <stefanv@berkeley.edu> +:Status: Final +:Type: Informational +:Created: 2019-07-13 +:Resolution: https://mail.python.org/pipermail/numpy-discussion/2019-October/080128.html + + +Abstract +-------- + +This NEP recommends that all projects across the Scientific +Python ecosystem adopt a common "time window-based" policy for +support of Python and NumPy versions. Standardizing a recommendation +for project support of minimum Python and NumPy versions will improve +downstream project planning. + +This is an unusual NEP in that it offers recommendations for +community-wide policy and not for changes to NumPy itself. Since a +common place for SPEEPs (Scientific Python Ecosystem Enhancement +Proposals) does not exist and given NumPy's central role in the +ecosystem, a NEP provides a visible place to document the proposed +policy. + +This NEP is being put forward by maintainers of Matplotlib, scikit-learn, +IPython, Jupyter, yt, SciPy, NumPy, and scikit-image. + + + +Detailed description +-------------------- + +For the purposes of this NEP we assume semantic versioning and define: + +*major version* + A release that changes the first number (e.g. X.0.0) + +*minor version* + A release that changes the second number (e.g 1.Y.0) + +*patch version* + A release that changes the third number (e.g. 1.1.Z) + + +When a project releases a new major or minor version, we recommend that +they support at least all minor versions of Python +introduced and released in the prior 42 months *from the +anticipated release date* with a minimum of 2 minor versions of +Python, and all minor versions of NumPy released in the prior 24 +months *from the anticipated release date* with a minimum of 3 +minor versions of NumPy. + + +Consider the following timeline:: + + Jan 16 Jan 17 Jan 18 Jan 19 Jan 20 + | | | | | + +++++|+++++++++++|+++++++++++|+++++++++++|+++++++++++|++++++++++++ + | | | | + py 3.5.0 py 3.6.0 py 3.7.0 py 3.8.0 + |-----------------------------------------> Feb19 + |-----------------------------------------> Dec19 + |-----------------------------------------> Nov20 + +It shows the 42 month support windows for Python. A project with a +major or minor version release in February 2019 should support Python 3.5 and newer, +a project with a major or minor version released in December 2019 should +support Python 3.6 and newer, and a project with a major or minor version +release in November 2020 should support Python 3.7 and newer. + +The current Python release cadence is 18 months so a 42 month window +ensures that there will always be at least two minor versions of Python +in the window. The window is extended 6 months beyond the anticipated two-release +interval for Python to provide resilience against small fluctuations / +delays in its release schedule. + +Because Python minor version support is based only on historical +release dates, a 42 month time window, and a planned project release +date, one can predict with high confidence when a project will be able +to drop any given minor version of Python. This, in turn, could save +months of unnecessary maintenance burden. + +If a project releases immediately after a minor version of Python +drops out of the support window, there will inevitably be some +mismatch in supported versions—but this situation should only last +until other projects in the ecosystem make releases. + +Otherwise, once a project does a minor or major release, it is +guaranteed that there will be a stable release of all other projects +that, at the source level, support the same set of Python versions +supported by the new release. + +If there is a Python 4 or a NumPy 2 this policy will have to be +reviewed in light of the community's and projects' best interests. + + +Support Table +~~~~~~~~~~~~~ + +============ ====== ===== +Date Python NumPy +------------ ------ ----- +Jan 07, 2020 3.6+ 1.15+ +Jun 23, 2020 3.7+ 1.15+ +Jul 23, 2020 3.7+ 1.16+ +Jan 13, 2021 3.7+ 1.17+ +Jul 26, 2021 3.7+ 1.18+ +Dec 22, 2021 3.7+ 1.19+ +Dec 26, 2021 3.8+ 1.19+ +Jun 21, 2022 3.8+ 1.20+ +Apr 14, 2023 3.9+ 1.20+ +============ ====== ===== + + +Drop Schedule +~~~~~~~~~~~~~ + +:: + + On next release, drop support for Python 3.5 (initially released on Sep 13, 2015) + On Jan 07, 2020 drop support for NumPy 1.14 (initially released on Jan 06, 2018) + On Jun 23, 2020 drop support for Python 3.6 (initially released on Dec 23, 2016) + On Jul 23, 2020 drop support for NumPy 1.15 (initially released on Jul 23, 2018) + On Jan 13, 2021 drop support for NumPy 1.16 (initially released on Jan 13, 2019) + On Jul 26, 2021 drop support for NumPy 1.17 (initially released on Jul 26, 2019) + On Dec 22, 2021 drop support for NumPy 1.18 (initially released on Dec 22, 2019) + On Dec 26, 2021 drop support for Python 3.7 (initially released on Jun 27, 2018) + On Jun 21, 2022 drop support for NumPy 1.19 (initially released on Jun 20, 2020) + On Apr 14, 2023 drop support for Python 3.8 (initially released on Oct 14, 2019) + + +Implementation +-------------- + +We suggest that all projects adopt the following language into their +development guidelines: + + This project supports: + + - All minor versions of Python released 42 months prior to the + project, and at minimum the two latest minor versions. + - All minor versions of ``numpy`` released in the 24 months prior + to the project, and at minimum the last three minor versions. + + In ``setup.py``, the ``python_requires`` variable should be set to + the minimum supported version of Python. All supported minor + versions of Python should be in the test matrix and have binary + artifacts built for the release. + + Minimum Python and NumPy version support should be adjusted upward + on every major and minor release, but never on a patch release. + + +Backward compatibility +---------------------- + +No backward compatibility issues. + +Alternatives +------------ + +Ad-Hoc version support +~~~~~~~~~~~~~~~~~~~~~~ + +A project could, on every release, evaluate whether to increase +the minimum version of Python supported. +As a major downside, an ad-hoc approach makes it hard for downstream users to predict what +the future minimum versions will be. As there is no objective threshold +to when the minimum version should be dropped, it is easy for these +version support discussions to devolve into `bike shedding <https://en.wikipedia.org/wiki/Wikipedia:Avoid_Parkinson%27s_bicycle-shed_effect>`_ and acrimony. + + +All CPython supported versions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The CPython supported versions of Python are listed in the Python +Developers Guide and the Python PEPs. Supporting these is a very clear +and conservative approach. However, it means that there exists a four +year lag between when a new features is introduced into the language +and when a project is able to use it. Additionally, for projects with +compiled extensions this requires building many binary artifacts for +each release. + +For the case of NumPy, many projects carry workarounds to bugs that +are fixed in subsequent versions of NumPy. Being proactive about +increasing the minimum version of NumPy allows downstream +packages to carry fewer version-specific patches. + + + +Default version on Linux distribution +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The policy could be to support the version of Python that ships by +default in the latest Ubuntu LTS or CentOS/RHEL release. However, we +would still have to standardize across the community which +distribution to follow. + +By following the versions supported by major Linux distributions, we +are giving up technical control of our projects to external +organizations that may have different motivations and concerns than we +do. + + +N minor versions of Python +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Given the current release cadence of the Python, the proposed time (42 +months) is roughly equivalent to "the last two" Python minor versions. +However, if Python changes their release cadence substantially, any +rule based solely on the number of minor releases may need to be +changed to remain sensible. + +A more fundamental problem with a policy based on number of Python +releases is that it is hard to predict when support for a given minor +version of Python will be dropped as that requires correctly +predicting the release schedule of Python for the next 3-4 years. A +time-based rule, in contrast, only depends on past events +and the length of the support window. + + +Time window from the X.Y.1 Python release +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This is equivalent to a few month longer support window from the X.Y.0 +release. This is because X.Y.1 bug-fix release is typically a few +months after the X.Y.0 release, thus a N month window from X.Y.1 is +roughly equivalent to a N+3 month from X.Y.0. + +The X.Y.0 release is naturally a special release. If we were to +anchor the window on X.Y.1 we would then have the discussion of why +not X.Y.M? + + +Discussion +---------- + + +References and Footnotes +------------------------ + +Code to generate support and drop schedule tables :: + + from datetime import datetime, timedelta + + data = """Jan 15, 2017: NumPy 1.12 + Sep 13, 2015: Python 3.5 + Dec 23, 2016: Python 3.6 + Jun 27, 2018: Python 3.7 + Jun 07, 2017: NumPy 1.13 + Jan 06, 2018: NumPy 1.14 + Jul 23, 2018: NumPy 1.15 + Jan 13, 2019: NumPy 1.16 + Jul 26, 2019: NumPy 1.17 + Oct 14, 2019: Python 3.8 + Dec 22, 2019: NumPy 1.18 + Jun 20, 2020: NumPy 1.19 + """ + + releases = [] + + plus42 = timedelta(days=int(365*3.5 + 1)) + plus24 = timedelta(days=int(365*2 + 1)) + + for line in data.splitlines(): + date, project_version = line.split(':') + project, version = project_version.strip().split(' ') + release = datetime.strptime(date, '%b %d, %Y') + if project.lower() == 'numpy': + drop = release + plus24 + else: + drop = release + plus42 + releases.append((drop, project, version, release)) + + releases = sorted(releases, key=lambda x: x[0]) + + + py_major,py_minor = sorted([int(x) for x in r[2].split('.')] for r in releases if r[1] == 'Python')[-1] + minpy = f"{py_major}.{py_minor+1}+" + + num_major,num_minor = sorted([int(x) for x in r[2].split('.')] for r in releases if r[1] == 'NumPy')[-1] + minnum = f"{num_major}.{num_minor+1}+" + + toprint_drop_dates = [''] + toprint_support_table = [] + for d, p, v, r in releases[::-1]: + df = d.strftime('%b %d, %Y') + toprint_drop_dates.append( + f'On {df} drop support for {p} {v} ' + f'(initially released on {r.strftime("%b %d, %Y")})') + toprint_support_table.append(f'{df} {minpy:<6} {minnum:<5}') + if p.lower() == 'numpy': + minnum = v+'+' + else: + minpy = v+'+' + print("On next release, drop support for Python 3.5 (initially released on Sep 13, 2015)") + for e in toprint_drop_dates[-4::-1]: + print(e) + + print('============ ====== =====') + print('Date Python NumPy') + print('------------ ------ -----') + for e in toprint_support_table[-4::-1]: + print(e) + print('============ ====== =====') + + +Copyright +--------- + +This document has been placed in the public domain. diff --git a/doc/neps/nep-0030-duck-array-protocol.rst b/doc/neps/nep-0030-duck-array-protocol.rst new file mode 100644 index 000000000000..1e12b546ab86 --- /dev/null +++ b/doc/neps/nep-0030-duck-array-protocol.rst @@ -0,0 +1,187 @@ +.. _NEP30: + +====================================================== +NEP 30 — Duck typing for NumPy arrays - Implementation +====================================================== + +:Author: Peter Andreas Entschev <pentschev@nvidia.com> +:Author: Stephan Hoyer <shoyer@google.com> +:Status: Draft +:Type: Standards Track +:Created: 2019-07-31 +:Updated: 2019-07-31 +:Resolution: + +Abstract +-------- + +We propose the ``__duckarray__`` protocol, following the high-level overview +described in NEP 22, allowing downstream libraries to return arrays of their +defined types, in contrast to ``np.asarray``, that coerces those ``array_like`` +objects to NumPy arrays. + +Detailed description +-------------------- + +NumPy's API, including array definitions, is implemented and mimicked in +countless other projects. By definition, many of those arrays are fairly +similar in how they operate to the NumPy standard. The introduction of +``__array_function__`` allowed dispatching of functions implemented by several +of these projects directly via NumPy's API. This introduces a new requirement, +returning the NumPy-like array itself, rather than forcing a coercion into a +pure NumPy array. + +For the purpose above, NEP 22 introduced the concept of duck typing to NumPy +arrays. The suggested solution described in the NEP allows libraries to avoid +coercion of a NumPy-like array to a pure NumPy array where necessary, while +still allowing that NumPy-like array libraries that do not wish to implement +the protocol to coerce arrays to a pure NumPy array via ``np.asarray``. + +Usage Guidance +~~~~~~~~~~~~~~ + +Code that uses ``np.duckarray`` is meant for supporting other ndarray-like objects +that "follow the NumPy API". That is an ill-defined concept at the moment -- +every known library implements the NumPy API only partly, and many deviate +intentionally in at least some minor ways. This cannot be easily remedied, so +for users of ``np.duckarray`` we recommend the following strategy: check if the +NumPy functionality used by the code that follows your use of ``np.duckarray`` +is present in Dask, CuPy and Sparse. If so, it's reasonable to expect any duck +array to work here. If not, we suggest you indicate in your docstring what kinds +of duck arrays are accepted, or what properties they need to have. + +To exemplify the usage of duck arrays, suppose one wants to take the ``mean()`` +of an array-like object ``arr``. Using NumPy to achieve that, one could write +``np.asarray(arr).mean()`` to achieve the intended result. If ``arr`` is not +a NumPy array, this would create an actual NumPy array in order to call +``.mean()``. However, if the array is an object that is compliant with the NumPy +API (either in full or partially) such as a CuPy, Sparse or a Dask array, then +that copy would have been unnecessary. On the other hand, if one were to use the new +``__duckarray__`` protocol: ``np.duckarray(arr).mean()``, and ``arr`` is an object +compliant with the NumPy API, it would simply be returned rather than coerced +into a pure NumPy array, avoiding unnecessary copies and potential loss of +performance. + +Implementation +-------------- + +The implementation idea is fairly straightforward, requiring a new function +``duckarray`` to be introduced in NumPy, and a new method ``__duckarray__`` in +NumPy-like array classes. The new ``__duckarray__`` method shall return the +downstream array-like object itself, such as the ``self`` object, while the +``__array__`` method raises ``TypeError``. Alternatively, the ``__array__`` +method could create an actual NumPy array and return that. + +The new NumPy ``duckarray`` function can be implemented as follows: + +.. code:: python + + def duckarray(array_like): + if hasattr(array_like, '__duckarray__'): + return array_like.__duckarray__() + return np.asarray(array_like) + +Example for a project implementing NumPy-like arrays +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Now consider a library that implements a NumPy-compatible array class called +``NumPyLikeArray``, this class shall implement the methods described above, and +a complete implementation would look like the following: + +.. code:: python + + class NumPyLikeArray: + def __duckarray__(self): + return self + + def __array__(self): + raise TypeError("NumPyLikeArray can not be converted to a NumPy " + "array. You may want to use np.duckarray() instead.") + +The implementation above exemplifies the simplest case, but the overall idea +is that libraries will implement a ``__duckarray__`` method that returns the +original object, and an ``__array__`` method that either creates and returns an +appropriate NumPy array, or raises a``TypeError`` to prevent unintentional use +as an object in a NumPy array (if ``np.asarray`` is called on an arbitrary +object that does not implement ``__array__``, it will create a NumPy array +scalar). + +In case of existing libraries that don't already implement ``__array__`` but +would like to use duck array typing, it is advised that they introduce +both ``__array__`` and``__duckarray__`` methods. + +Usage +----- + +An example of how the ``__duckarray__`` protocol could be used to write a +``stack`` function based on ``concatenate``, and its produced outcome, can be +seen below. The example here was chosen not only to demonstrate the usage of +the ``duckarray`` function, but also to demonstrate its dependency on the NumPy +API, demonstrated by checks on the array's ``shape`` attribute. Note that the +example is merely a simplified version of NumPy's actual implementation of +``stack`` working on the first axis, and it is assumed that Dask has implemented +the ``__duckarray__`` method. + +.. code:: python + + def duckarray_stack(arrays): + arrays = [np.duckarray(arr) for arr in arrays] + + shapes = {arr.shape for arr in arrays} + if len(shapes) != 1: + raise ValueError('all input arrays must have the same shape') + + expanded_arrays = [arr[np.newaxis, ...] for arr in arrays] + return np.concatenate(expanded_arrays, axis=0) + + dask_arr = dask.array.arange(10) + np_arr = np.arange(10) + np_like = list(range(10)) + + duckarray_stack((dask_arr, dask_arr)) # Returns dask.array + duckarray_stack((dask_arr, np_arr)) # Returns dask.array + duckarray_stack((dask_arr, np_like)) # Returns dask.array + +In contrast, using only ``np.asarray`` (at the time of writing of this NEP, this +is the usual method employed by library developers to ensure arrays are +NumPy-like) has a different outcome: + +.. code:: python + + def asarray_stack(arrays): + arrays = [np.asanyarray(arr) for arr in arrays] + + # The remaining implementation is the same as that of + # ``duckarray_stack`` above + + asarray_stack((dask_arr, dask_arr)) # Returns np.ndarray + asarray_stack((dask_arr, np_arr)) # Returns np.ndarray + asarray_stack((dask_arr, np_like)) # Returns np.ndarray + +Backward compatibility +---------------------- + +This proposal does not raise any backward compatibility issues within NumPy, +given that it only introduces a new function. However, downstream libraries +that opt to introduce the ``__duckarray__`` protocol may choose to remove the +ability of coercing arrays back to a NumPy array via ``np.array`` or +``np.asarray`` functions, preventing unintended effects of coercion of such +arrays back to a pure NumPy array (as some libraries already do, such as CuPy +and Sparse), but still leaving libraries not implementing the protocol with the +choice of utilizing ``np.duckarray`` to promote ``array_like`` objects to pure +NumPy arrays. + +Previous proposals and discussion +--------------------------------- + +The duck typing protocol proposed here was described in a high level in +`NEP 22 <https://numpy.org/neps/nep-0022-ndarray-duck-typing-overview.html>`_. + +Additionally, longer discussions about the protocol and related proposals +took place in +`numpy/numpy #13831 <https://github.com/numpy/numpy/issues/13831>`_ + +Copyright +--------- + +This document has been placed in the public domain. diff --git a/doc/neps/nep-0031-uarray.rst b/doc/neps/nep-0031-uarray.rst new file mode 100644 index 000000000000..b4ec94077f80 --- /dev/null +++ b/doc/neps/nep-0031-uarray.rst @@ -0,0 +1,659 @@ +.. _NEP31: + +============================================================ +NEP 31 — Context-local and global overrides of the NumPy API +============================================================ + +:Author: Hameer Abbasi <habbasi@quansight.com> +:Author: Ralf Gommers <rgommers@quansight.com> +:Author: Peter Bell <pbell@quansight.com> +:Status: Draft +:Type: Standards Track +:Created: 2019-08-22 + + +Abstract +-------- + +This NEP proposes to make all of NumPy's public API overridable via an +extensible backend mechanism. + +Acceptance of this NEP means NumPy would provide global and context-local +overrides in a separate namespace, as well as a dispatch mechanism similar +to NEP-18 [2]_. First experiences with ``__array_function__`` show that it +is necessary to be able to override NumPy functions that *do not take an +array-like argument*, and hence aren't overridable via +``__array_function__``. The most pressing need is array creation and coercion +functions, such as ``numpy.zeros`` or ``numpy.asarray``; see e.g. NEP-30 [9]_. + +This NEP proposes to allow, in an opt-in fashion, overriding any part of the +NumPy API. It is intended as a comprehensive resolution to NEP-22 [3]_, and +obviates the need to add an ever-growing list of new protocols for each new +type of function or object that needs to become overridable. + +Motivation and Scope +-------------------- + +The primary end-goal of this NEP is to make the following possible: + +.. code:: python + + # On the library side + import numpy.overridable as unp + + def library_function(array): + array = unp.asarray(array) + # Code using unumpy as usual + return array + + # On the user side: + import numpy.overridable as unp + import uarray as ua + import dask.array as da + + ua.register_backend(da) # Can be done within Dask itself + + library_function(dask_array) # works and returns dask_array + + with unp.set_backend(da): + library_function([1, 2, 3, 4]) # actually returns a Dask array. + +Here, ``backend`` can be any compatible object defined either by NumPy or an +external library, such as Dask or CuPy. Ideally, it should be the module +``dask.array`` or ``cupy`` itself. + +These kinds of overrides are useful for both the end-user as well as library +authors. End-users may have written or wish to write code that they then later +speed up or move to a different implementation, say PyData/Sparse. They can do +this simply by setting a backend. Library authors may also wish to write code +that is portable across array implementations, for example ``sklearn`` may wish +to write code for a machine learning algorithm that is portable across array +implementations while also using array creation functions. + +This NEP takes a holistic approach: It assumes that there are parts of +the API that need to be overridable, and that these will grow over time. It +provides a general framework and a mechanism to avoid a design of a new +protocol each time this is required. This was the goal of ``uarray``: to +allow for overrides in an API without needing the design of a new protocol. + +This NEP proposes the following: That ``unumpy`` [8]_ becomes the +recommended override mechanism for the parts of the NumPy API not yet covered +by ``__array_function__`` or ``__array_ufunc__``, and that ``uarray`` is +vendored into a new namespace within NumPy to give users and downstream +dependencies access to these overrides. This vendoring mechanism is similar +to what SciPy decided to do for making ``scipy.fft`` overridable (see [10]_). + +The motivation behind ``uarray`` is manyfold: First, there have been several +attempts to allow dispatch of parts of the NumPy API, including (most +prominently), the ``__array_ufunc__`` protocol in NEP-13 [4]_, and the +``__array_function__`` protocol in NEP-18 [2]_, but this has shown the need +for further protocols to be developed, including a protocol for coercion (see +[5]_, [9]_). The reasons these overrides are needed have been extensively +discussed in the references, and this NEP will not attempt to go into the +details of why these are needed; but in short: It is necessary for library +authors to be able to coerce arbitrary objects into arrays of their own types, +such as CuPy needing to coerce to a CuPy array, for example, instead of +a NumPy array. In simpler words, one needs things like ``np.asarray(...)`` or +an alternative to "just work" and return duck-arrays. + +Usage and Impact +---------------- + +This NEP allows for global and context-local overrides, as well as +automatic overrides a-la ``__array_function__``. + +Here are some use-cases this NEP would enable, besides the +first one stated in the motivation section: + +The first is allowing alternate dtypes to return their +respective arrays. + +.. code:: python + + # Returns an XND array + x = unp.ones((5, 5), dtype=xnd_dtype) # Or torch dtype + +The second is allowing overrides for parts of the API. +This is to allow alternate and/or optimised implementations +for ``np.linalg``, BLAS, and ``np.random``. + +.. code:: python + + import numpy as np + import pyfftw # Or mkl_fft + + # Makes pyfftw the default for FFT + np.set_global_backend(pyfftw) + + # Uses pyfftw without monkeypatching + np.fft.fft(numpy_array) + + with np.set_backend(pyfftw) # Or mkl_fft, or numpy + # Uses the backend you specified + np.fft.fft(numpy_array) + +This will allow an official way for overrides to work with NumPy without +monkeypatching or distributing a modified version of NumPy. + +Here are a few other use-cases, implied but not already +stated: + +.. code:: python + + data = da.from_zarr('myfile.zarr') + # result should still be dask, all things being equal + result = library_function(data) + result.to_zarr('output.zarr') + +This second one would work if ``magic_library`` was built +on top of ``unumpy``. + +.. code:: python + + from dask import array as da + from magic_library import pytorch_predict + + data = da.from_zarr('myfile.zarr') + # normally here one would use e.g. data.map_overlap + result = pytorch_predict(data) + result.to_zarr('output.zarr') + +There are some backends which may depend on other backends, for example xarray +depending on `numpy.fft`, and transforming a time axis into a frequency axis, +or Dask/xarray holding an array other than a NumPy array inside it. This would +be handled in the following manner inside code:: + + with ua.set_backend(cupy), ua.set_backend(dask.array): + # Code that has distributed GPU arrays here + +Backward compatibility +---------------------- + +There are no backward incompatible changes proposed in this NEP. + +Detailed description +-------------------- + +Proposals +~~~~~~~~~ + +The only change this NEP proposes at its acceptance, is to make ``unumpy`` the +officially recommended way to override NumPy, along with making some submodules +overridable by default via ``uarray``. ``unumpy`` will remain a separate +repository/package (which we propose to vendor to avoid a hard dependency, and +use the separate ``unumpy`` package only if it is installed, rather than depend +on for the time being). In concrete terms, ``numpy.overridable`` becomes an +alias for ``unumpy``, if available with a fallback to the a vendored version if +not. ``uarray`` and ``unumpy`` and will be developed primarily with the input +of duck-array authors and secondarily, custom dtype authors, via the usual +GitHub workflow. There are a few reasons for this: + +* Faster iteration in the case of bugs or issues. +* Faster design changes, in the case of needed functionality. +* ``unumpy`` will work with older versions of NumPy as well. +* The user and library author opt-in to the override process, + rather than breakages happening when it is least expected. + In simple terms, bugs in ``unumpy`` mean that ``numpy`` remains + unaffected. +* For ``numpy.fft``, ``numpy.linalg`` and ``numpy.random``, the functions in + the main namespace will mirror those in the ``numpy.overridable`` namespace. + The reason for this is that there may exist functions in the in these + submodules that need backends, even for ``numpy.ndarray`` inputs. + +Advantanges of ``unumpy`` over other solutions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``unumpy`` offers a number of advantanges over the approach of defining a new +protocol for every problem encountered: Whenever there is something requiring +an override, ``unumpy`` will be able to offer a unified API with very minor +changes. For example: + +* ``ufunc`` objects can be overridden via their ``__call__``, ``reduce`` and + other methods. +* Other functions can be overridden in a similar fashion. +* ``np.asduckarray`` goes away, and becomes ``np.overridable.asarray`` with a + backend set. +* The same holds for array creation functions such as ``np.zeros``, + ``np.empty`` and so on. + +This also holds for the future: Making something overridable would require only +minor changes to ``unumpy``. + +Another promise ``unumpy`` holds is one of default implementations. Default +implementations can be provided for any multimethod, in terms of others. This +allows one to override a large part of the NumPy API by defining only a small +part of it. This is to ease the creation of new duck-arrays, by providing +default implementations of many functions that can be easily expressed in +terms of others, as well as a repository of utility functions that help in the +implementation of duck-arrays that most duck-arrays would require. This would +allow us to avoid designing entire protocols, e.g., a protocol for stacking +and concatenating would be replaced by simply implementing ``stack`` and/or +``concatenate`` and then providing default implementations for everything else +in that class. The same applies for transposing, and many other functions for +which protocols haven't been proposed, such as ``isin`` in terms of ``in1d``, +``setdiff1d`` in terms of ``unique``, and so on. + +It also allows one to override functions in a manner which +``__array_function__`` simply cannot, such as overriding ``np.einsum`` with the +version from the ``opt_einsum`` package, or Intel MKL overriding FFT, BLAS +or ``ufunc`` objects. They would define a backend with the appropriate +multimethods, and the user would select them via a ``with`` statement, or +registering them as a backend. + +The last benefit is a clear way to coerce to a given backend (via the +``coerce`` keyword in ``ua.set_backend``), and a protocol +for coercing not only arrays, but also ``dtype`` objects and ``ufunc`` objects +with similar ones from other libraries. This is due to the existence of actual, +third party dtype packages, and their desire to blend into the NumPy ecosystem +(see [6]_). This is a separate issue compared to the C-level dtype redesign +proposed in [7]_, it's about allowing third-party dtype implementations to +work with NumPy, much like third-party array implementations. These can provide +features such as, for example, units, jagged arrays or other such features that +are outside the scope of NumPy. + +Mixing NumPy and ``unumpy`` in the same file +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Normally, one would only want to import only one of ``unumpy`` or ``numpy``, +you would import it as ``np`` for familiarity. However, there may be situations +where one wishes to mix NumPy and the overrides, and there are a few ways to do +this, depending on the user's style:: + + from numpy import overridable as unp + import numpy as np + +or:: + + import numpy as np + + # Use unumpy via np.overridable + +Duck-array coercion +~~~~~~~~~~~~~~~~~~~ + +There are inherent problems about returning objects that are not NumPy arrays +from ``numpy.array`` or ``numpy.asarray``, particularly in the context of C/C++ +or Cython code that may get an object with a different memory layout than the +one it expects. However, we believe this problem may apply not only to these +two functions but all functions that return NumPy arrays. For this reason, +overrides are opt-in for the user, by using the submodule ``numpy.overridable`` +rather than ``numpy``. NumPy will continue to work unaffected by anything in +``numpy.overridable``. + +If the user wishes to obtain a NumPy array, there are two ways of doing it: + +1. Use ``numpy.asarray`` (the non-overridable version). +2. Use ``numpy.overridable.asarray`` with the NumPy backend set and coercion + enabled + +Aliases outside of the ``numpy.overridable`` namespace +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +All functionality in ``numpy.random``, ``numpy.linalg`` and ``numpy.fft`` +will be aliased to their respective overridable versions inside +``numpy.overridable``. The reason for this is that there are alternative +implementations of RNGs (``mkl-random``), linear algebra routines (``eigen``, +``blis``) and FFT routines (``mkl-fft``, ``pyFFTW``) that need to operate on +``numpy.ndarray`` inputs, but still need the ability to switch behaviour. + +This is different from monkeypatching in a few different ways: + +* The caller-facing signature of the function is always the same, + so there is at least the loose sense of an API contract. Monkeypatching + does not provide this ability. +* There is the ability of locally switching the backend. +* It has been `suggested <http://numpy-discussion.10968.n7.nabble.com/NEP-31-Context-local-and-global-overrides-of-the-NumPy-API-tp47452p47472.html>`_ + that the reason that 1.17 hasn't landed in the Anaconda defaults channel is + due to the incompatibility between monkeypatching and ``__array_function__``, + as monkeypatching would bypass the protocol completely. +* Statements of the form ``from numpy import x; x`` and ``np.x`` would have + different results depending on whether the import was made before or + after monkeypatching happened. + +All this isn't possible at all with ``__array_function__`` or +``__array_ufunc__``. + +It has been formally realised (at least in part) that a backend system is +needed for this, in the `NumPy roadmap <https://numpy.org/neps/roadmap.html#other-functionality>`_. + +For ``numpy.random``, it's still necessary to make the C-API fit the one +proposed in `NEP-19 <https://numpy.org/neps/nep-0019-rng-policy.html>`_. +This is impossible for `mkl-random`, because then it would need to be +rewritten to fit that framework. The guarantees on stream +compatibility will be the same as before, but if there's a backend that affects +``numpy.random`` set, we make no guarantees about stream compatibility, and it +is up to the backend author to provide their own guarantees. + +Providing a way for implicit dispatch +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It has been suggested that the ability to dispatch methods which do not take +a dispatchable is needed, while guessing that backend from another dispatchable. + +As a concrete example, consider the following: + +.. code:: python + + with unumpy.determine_backend(array_like, np.ndarray): + unumpy.arange(len(array_like)) + +While this does not exist yet in ``uarray``, it is trivial to add it. The need for +this kind of code exists because one might want to have an alternative for the +proposed ``*_like`` functions, or the ``like=`` keyword argument. The need for these +exists because there are functions in the NumPy API that do not take a dispatchable +argument, but there is still the need to select a backend based on a different +dispatchable. + +The need for an opt-in module +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The need for an opt-in module is realised because of a few reasons: + +* There are parts of the API (like `numpy.asarray`) that simply cannot be + overridden due to incompatibility concerns with C/Cython extensions, however, + one may want to coerce to a duck-array using ``asarray`` with a backend set. +* There are possible issues around an implicit option and monkeypatching, such + as those mentioned above. + +NEP 18 notes that this may require maintenance of two separate APIs. However, +this burden may be lessened by, for example, parametrizing all tests over +``numpy.overridable`` separately via a fixture. This also has the side-effect +of thoroughly testing it, unlike ``__array_function__``. We also feel that it +provides an opportunity to separate the NumPy API contract properly from the +implementation. + +Benefits to end-users and mixing backends +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Mixing backends is easy in ``uarray``, one only has to do: + +.. code:: python + + # Explicitly say which backends you want to mix + ua.register_backend(backend1) + ua.register_backend(backend2) + ua.register_backend(backend3) + + # Freely use code that mixes backends here. + +The benefits to end-users extend beyond just writing new code. Old code +(usually in the form of scripts) can be easily ported to different backends +by a simple import switch and a line adding the preferred backend. This way, +users may find it easier to port existing code to GPU or distributed computing. + +Related Work +------------ + +Other override mechanisms +~~~~~~~~~~~~~~~~~~~~~~~~~ + +* NEP-18, the ``__array_function__`` protocol. [2]_ +* NEP-13, the ``__array_ufunc__`` protocol. [3]_ +* NEP-30, the ``__duck_array__`` protocol. [9]_ + +Existing NumPy-like array implementations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* Dask: https://dask.org/ +* CuPy: https://cupy.chainer.org/ +* PyData/Sparse: https://sparse.pydata.org/ +* Xnd: https://xnd.readthedocs.io/ +* Astropy's Quantity: https://docs.astropy.org/en/stable/units/ + +Existing and potential consumers of alternative arrays +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* Dask: https://dask.org/ +* scikit-learn: https://scikit-learn.org/ +* xarray: https://xarray.pydata.org/ +* TensorLy: http://tensorly.org/ + +Existing alternate dtype implementations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* ``ndtypes``: https://ndtypes.readthedocs.io/en/latest/ +* Datashape: https://datashape.readthedocs.io +* Plum: https://plum-py.readthedocs.io/ + +Alternate implementations of parts of the NumPy API +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* ``mkl_random``: https://github.com/IntelPython/mkl_random +* ``mkl_fft``: https://github.com/IntelPython/mkl_fft +* ``bottleneck``: https://github.com/pydata/bottleneck +* ``opt_einsum``: https://github.com/dgasmith/opt_einsum + +Implementation +-------------- + +The implementation of this NEP will require the following steps: + +* Implementation of ``uarray`` multimethods corresponding to the + NumPy API, including classes for overriding ``dtype``, ``ufunc`` + and ``array`` objects, in the ``unumpy`` repository, which are usually + very easy to create. +* Moving backends from ``unumpy`` into the respective array libraries. + +Maintenance can be eased by testing over ``{numpy, unumpy}`` via parameterized +tests. If a new argument is added to a method, the corresponding argument +extractor and replacer will need to be updated within ``unumpy``. + +A lot of argument extractors can be re-used from the existing implementation +of the ``__array_function__`` protocol, and the replacers can be usually +re-used across many methods. + +For the parts of the namespace which are going to be overridable by default, +the main method will need to be renamed and hidden behind a ``uarray`` multimethod. + +Default implementations are usually seen in the documentation using the words +"equivalent to", and thus, are easily available. + +``uarray`` Primer +~~~~~~~~~~~~~~~~~ + +**Note:** *This section will not attempt to go into too much detail about +uarray, that is the purpose of the uarray documentation.* [1]_ +*However, the NumPy community will have input into the design of +uarray, via the issue tracker.* + +``unumpy`` is the interface that defines a set of overridable functions +(multimethods) compatible with the numpy API. To do this, it uses the +``uarray`` library. ``uarray`` is a general purpose tool for creating +multimethods that dispatch to one of multiple different possible backend +implementations. In this sense, it is similar to the ``__array_function__`` +protocol but with the key difference that the backend is explicitly installed +by the end-user and not coupled into the array type. + +Decoupling the backend from the array type gives much more flexibility to +end-users and backend authors. For example, it is possible to: + +* override functions not taking arrays as arguments +* create backends out of source from the array type +* install multiple backends for the same array type + +This decoupling also means that ``uarray`` is not constrained to dispatching +over array-like types. The backend is free to inspect the entire set of +function arguments to determine if it can implement the function e.g. ``dtype`` +parameter dispatching. + +Defining backends +^^^^^^^^^^^^^^^^^ + +``uarray`` consists of two main protocols: ``__ua_convert__`` and +``__ua_function__``, called in that order, along with ``__ua_domain__``. +``__ua_convert__`` is for conversion and coercion. It has the signature +``(dispatchables, coerce)``, where ``dispatchables`` is an iterable of +``ua.Dispatchable`` objects and ``coerce`` is a boolean indicating whether or +not to force the conversion. ``ua.Dispatchable`` is a simple class consisting +of three simple values: ``type``, ``value``, and ``coercible``. +``__ua_convert__`` returns an iterable of the converted values, or +``NotImplemented`` in the case of failure. + +``__ua_function__`` has the signature ``(func, args, kwargs)`` and defines +the actual implementation of the function. It receives the function and its +arguments. Returning ``NotImplemented`` will cause a move to the default +implementation of the function if one exists, and failing that, the next +backend. + +Here is what will happen assuming a ``uarray`` multimethod is called: + +1. We canonicalise the arguments so any arguments without a default + are placed in ``*args`` and those with one are placed in ``**kwargs``. +2. We check the list of backends. + + a. If it is empty, we try the default implementation. + +3. We check if the backend's ``__ua_convert__`` method exists. If it exists: + + a. We pass it the output of the dispatcher, + which is an iterable of ``ua.Dispatchable`` objects. + b. We feed this output, along with the arguments, + to the argument replacer. ``NotImplemented`` means we move to 3 + with the next backend. + c. We store the replaced arguments as the new arguments. + +4. We feed the arguments into ``__ua_function__``, and return the output, and + exit if it isn't ``NotImplemented``. +5. If the default implementation exists, we try it with the current backend. +6. On failure, we move to 3 with the next backend. If there are no more + backends, we move to 7. +7. We raise a ``ua.BackendNotImplementedError``. + +Defining overridable multimethods +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +To define an overridable function (a multimethod), one needs a few things: + +1. A dispatcher that returns an iterable of ``ua.Dispatchable`` objects. +2. A reverse dispatcher that replaces dispatchable values with the supplied + ones. +3. A domain. +4. Optionally, a default implementation, which can be provided in terms of + other multimethods. + +As an example, consider the following:: + + import uarray as ua + + def full_argreplacer(args, kwargs, dispatchables): + def full(shape, fill_value, dtype=None, order='C'): + return (shape, fill_value), dict( + dtype=dispatchables[0], + order=order + ) + + return full(*args, **kwargs) + + @ua.create_multimethod(full_argreplacer, domain="numpy") + def full(shape, fill_value, dtype=None, order='C'): + return (ua.Dispatchable(dtype, np.dtype),) + +A large set of examples can be found in the ``unumpy`` repository, [8]_. +This simple act of overriding callables allows us to override: + +* Methods +* Properties, via ``fget`` and ``fset`` +* Entire objects, via ``__get__``. + +Examples for NumPy +^^^^^^^^^^^^^^^^^^ + +A library that implements a NumPy-like API will use it in the following +manner (as an example):: + + import numpy.overridable as unp + _ua_implementations = {} + + __ua_domain__ = "numpy" + + def __ua_function__(func, args, kwargs): + fn = _ua_implementations.get(func, None) + return fn(*args, **kwargs) if fn is not None else NotImplemented + + def implements(ua_func): + def inner(func): + _ua_implementations[ua_func] = func + return func + + return inner + + @implements(unp.asarray) + def asarray(a, dtype=None, order=None): + # Code here + # Either this method or __ua_convert__ must + # return NotImplemented for unsupported types, + # Or they shouldn't be marked as dispatchable. + + # Provides a default implementation for ones and zeros. + @implements(unp.full) + def full(shape, fill_value, dtype=None, order='C'): + # Code here + +Alternatives +------------ + +The current alternative to this problem is a combination of NEP-18 [2]_, +NEP-13 [4]_ and NEP-30 [9]_ plus adding more protocols (not yet specified) +in addition to it. Even then, some parts of the NumPy API will remain +non-overridable, so it's a partial alternative. + +The main alternative to vendoring ``unumpy`` is to simply move it into NumPy +completely and not distribute it as a separate package. This would also achieve +the proposed goals, however we prefer to keep it a separate package for now, +for reasons already stated above. + +The third alternative is to move ``unumpy`` into the NumPy organisation and +develop it as a NumPy project. This will also achieve the said goals, and is +also a possibility that can be considered by this NEP. However, the act of +doing an extra ``pip install`` or ``conda install`` may discourage some users +from adopting this method. + +An alternative to requiring opt-in is mainly to *not* override ``np.asarray`` +and ``np.array``, and making the rest of the NumPy API surface overridable, +instead providing ``np.duckarray`` and ``np.asduckarray`` +as duck-array friendly alternatives that used the respective overrides. However, +this has the downside of adding a minor overhead to NumPy calls. + +Discussion +---------- + +* ``uarray`` blogpost: https://labs.quansight.org/blog/2019/07/uarray-update-api-changes-overhead-and-comparison-to-__array_function__/ +* The discussion section of NEP-18: https://numpy.org/neps/nep-0018-array-function-protocol.html#discussion +* NEP-22: https://numpy.org/neps/nep-0022-ndarray-duck-typing-overview.html +* Dask issue #4462: https://github.com/dask/dask/issues/4462 +* PR #13046: https://github.com/numpy/numpy/pull/13046 +* Dask issue #4883: https://github.com/dask/dask/issues/4883 +* Issue #13831: https://github.com/numpy/numpy/issues/13831 +* Discussion PR 1: https://github.com/hameerabbasi/numpy/pull/3 +* Discussion PR 2: https://github.com/hameerabbasi/numpy/pull/4 +* Discussion PR 3: https://github.com/numpy/numpy/pull/14389 + + +References and Footnotes +------------------------ + +.. [1] uarray, A general dispatch mechanism for Python: https://uarray.readthedocs.io + +.. [2] NEP 18 — A dispatch mechanism for NumPy’s high level array functions: https://numpy.org/neps/nep-0018-array-function-protocol.html + +.. [3] NEP 22 — Duck typing for NumPy arrays – high level overview: https://numpy.org/neps/nep-0022-ndarray-duck-typing-overview.html + +.. [4] NEP 13 — A Mechanism for Overriding Ufuncs: https://numpy.org/neps/nep-0013-ufunc-overrides.html + +.. [5] Reply to Adding to the non-dispatched implementation of NumPy methods: http://numpy-discussion.10968.n7.nabble.com/Adding-to-the-non-dispatched-implementation-of-NumPy-methods-tp46816p46874.html + +.. [6] Custom Dtype/Units discussion: http://numpy-discussion.10968.n7.nabble.com/Custom-Dtype-Units-discussion-td43262.html + +.. [7] The epic dtype cleanup plan: https://github.com/numpy/numpy/issues/2899 + +.. [8] unumpy: NumPy, but implementation-independent: https://unumpy.readthedocs.io + +.. [9] NEP 30 — Duck Typing for NumPy Arrays - Implementation: https://www.numpy.org/neps/nep-0030-duck-array-protocol.html + +.. [10] http://scipy.github.io/devdocs/fft.html#backend-control + + +Copyright +--------- + +This document has been placed in the public domain. diff --git a/doc/neps/nep-0032-remove-financial-functions.rst b/doc/neps/nep-0032-remove-financial-functions.rst new file mode 100644 index 000000000000..b57ae943fa96 --- /dev/null +++ b/doc/neps/nep-0032-remove-financial-functions.rst @@ -0,0 +1,216 @@ +.. _NEP32: + +================================================== +NEP 32 — Remove the financial functions from NumPy +================================================== + +:Author: Warren Weckesser <warren.weckesser@gmail.com> +:Status: Final +:Type: Standards Track +:Created: 2019-08-30 +:Resolution: https://mail.python.org/pipermail/numpy-discussion/2019-September/080074.html + + +Abstract +-------- + +We propose deprecating and ultimately removing the financial functions [1]_ +from NumPy. The functions will be moved to an independent repository, +and provided to the community as a separate package with the name +``numpy_financial``. + + +Motivation and scope +-------------------- + +The NumPy financial functions [1]_ are the 10 functions ``fv``, ``ipmt``, +``irr``, ``mirr``, ``nper``, ``npv``, ``pmt``, ``ppmt``, ``pv`` and ``rate``. +The functions provide elementary financial calculations such as future value, +net present value, etc. These functions were added to NumPy in 2008 [2]_. + +In May, 2009, a request by Joe Harrington to add a function called ``xirr`` to +the financial functions triggered a long thread about these functions [3]_. +One important point that came up in that thread is that a "real" financial +library must be able to handle real dates. The NumPy financial functions do +not work with actual dates or calendars. The preference for a more capable +library independent of NumPy was expressed several times in that thread. + +In June, 2009, D. L. Goldsmith expressed concerns about the correctness of the +implementations of some of the financial functions [4]_. It was suggested then +to move the financial functions out of NumPy to an independent package. + +In a GitHub issue in 2013 [5]_, Nathaniel Smith suggested moving the financial +functions from the top-level namespace to ``numpy.financial``. He also +suggested giving the functions better names. Responses at that time included +the suggestion to deprecate them and move them from NumPy to a separate +package. This issue is still open. + +Later in 2013 [6]_, it was suggested on the mailing list that these functions +be removed from NumPy. + +The arguments for the removal of these functions from NumPy: + +* They are too specialized for NumPy. +* They are not actually useful for "real world" financial calculations, because + they do not handle real dates and calendars. +* The definition of "correctness" for some of these functions seems to be a + matter of convention, and the current NumPy developers do not have the + background to judge their correctness. +* There has been little interest among past and present NumPy developers + in maintaining these functions. + +The main arguments for keeping the functions in NumPy are: + +* Removing these functions will be disruptive for some users. Current users + will have to add the new ``numpy_financial`` package to their dependencies, + and then modify their code to use the new package. +* The functions provided, while not "industrial strength", are apparently + similar to functions provided by spreadsheets and some calculators. Having + them available in NumPy makes it easier for some developers to migrate their + software to Python and NumPy. + +It is clear from comments in the mailing list discussions and in the GitHub +issues that many current NumPy developers believe the benefits of removing +the functions outweigh the costs. For example, from [5]_:: + + The financial functions should probably be part of a separate package + -- Charles Harris + + If there's a better package we can point people to we could just deprecate + them and then remove them entirely... I'd be fine with that too... + -- Nathaniel Smith + + +1 to deprecate them. If no other package exists, it can be created if + someone feels the need for that. + -- Ralf Gommers + + I feel pretty strongly that we should deprecate these. If nobody on numpy’s + core team is interested in maintaining them, then it is purely a drag on + development for NumPy. + -- Stephan Hoyer + +And from the 2013 mailing list discussion, about removing the functions from +NumPy:: + + I am +1 as well, I don't think they should have been included in the first + place. + -- David Cournapeau + +But not everyone was in favor of removal:: + + The fin routines are tiny and don't require much maintenance once + written. If we made an effort (putting up pages with examples of common + financial calculations and collecting those under a topical web page, + then linking to that page from various places and talking it up), I + would think they could attract users looking for a free way to play with + financial scenarios. [...] + So, I would say we keep them. If ours are not the best, we should bring + them up to snuff. + -- Joe Harrington + +For an idea of the maintenance burden of the financial functions, one can +look for all the GitHub issues [7]_ and pull requests [8]_ that have the tag +``component: numpy.lib.financial``. + +One method for measuring the effect of removing these functions is to find +all the packages on GitHub that use them. Such a search can be performed +with the ``python-api-inspect`` service [9]_. A search for all uses of the +NumPy financial functions finds just eight repositories. (See the comments +in [5]_ for the actual SQL query.) + + +Implementation +-------------- + +* Create a new Python package, ``numpy_financial``, to be maintained in the + top-level NumPy github organization. This repository will contain the + definitions and unit tests for the financial functions. The package will + be added to PyPI so it can be installed with ``pip``. +* Deprecate the financial functions in the ``numpy`` namespace, beginning in + NumPy version 1.18. Remove the financial functions from NumPy version 1.20. + + +Backward compatibility +---------------------- + +The removal of these functions breaks backward compatibility, as explained +earlier. The effects are mitigated by providing the ``numpy_financial`` +library. + + +Alternatives +------------ + +The following alternatives were mentioned in [5]_: + +* *Maintain the functions as they are (i.e. do nothing).* + A review of the history makes clear that this is not the preference of many + NumPy developers. A recurring comment is that the functions simply do not + belong in NumPy. When that sentiment is combined with the history of bug + reports and the ongoing questions about the correctness of the functions, the + conclusion is that the cleanest solution is deprecation and removal. +* *Move the functions from the ``numpy`` namespace to ``numpy.financial``.* + This was the initial suggestion in [5]_. Such a change does not address the + maintenance issues, and doesn't change the misfit that many developers see + between these functions and NumPy. It causes disruption for the current + users of these functions without addressing what many developers see as the + fundamental problem. + + +Discussion +---------- + +Links to past mailing list discussions, and to relevant GitHub issues and pull +requests, have already been given. The announcement of this NEP was made on +the NumPy-Discussion mailing list on 3 September 2019 [10]_, and on the +PyData mailing list on 8 September 2019 [11]_. The formal proposal to accept +the NEP was made on 19 September 2019 [12]_; a notification was also sent to +PyData (same thread as [11]_). There have been no substantive objections. + + +References and footnotes +------------------------ + +.. [1] Financial functions, + https://numpy.org/doc/1.17/reference/routines.financial.html + +.. [2] NumPy-Discussion mailing list, "Simple financial functions for NumPy", + https://mail.python.org/pipermail/numpy-discussion/2008-April/032353.html + +.. [3] NumPy-Discussion mailing list, "add xirr to numpy financial functions?", + https://mail.python.org/pipermail/numpy-discussion/2009-May/042645.html + +.. [4] NumPy-Discussion mailing list, "Definitions of pv, fv, nper, pmt, and rate", + https://mail.python.org/pipermail/numpy-discussion/2009-June/043188.html + +.. [5] Get financial functions out of main namespace, + https://github.com/numpy/numpy/issues/2880 + +.. [6] NumPy-Discussion mailing list, "Deprecation of financial routines", + https://mail.python.org/pipermail/numpy-discussion/2013-August/067409.html + +.. [7] ``component: numpy.lib.financial`` issues, + https://github.com/numpy/numpy/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22component%3A+numpy.lib.financial%22+ + +.. [8] ``component: numpy.lib.financial`` pull requests, + https://github.com/numpy/numpy/pulls?utf8=%E2%9C%93&q=is%3Apr+label%3A%22component%3A+numpy.lib.financial%22+ + +.. [9] Quansight-Labs/python-api-inspect, + https://github.com/Quansight-Labs/python-api-inspect/ + +.. [10] NumPy-Discussion mailing list, "NEP 32: Remove the financial functions + from NumPy" + https://mail.python.org/pipermail/numpy-discussion/2019-September/079965.html + +.. [11] PyData mailing list (pydata@googlegroups.com), "NumPy proposal to + remove the financial functions. + https://mail.google.com/mail/u/0/h/1w0mjgixc4rpe/?&th=16d5c38be45f77c4&q=nep+32&v=c&s=q + +.. [12] NumPy-Discussion mailing list, "Proposal to accept NEP 32: Remove the + financial functions from NumPy" + https://mail.python.org/pipermail/numpy-discussion/2019-September/080074.html + +Copyright +--------- + +This document has been placed in the public domain. diff --git a/doc/neps/nep-0034-infer-dtype-is-object.rst b/doc/neps/nep-0034-infer-dtype-is-object.rst new file mode 100644 index 000000000000..a424ab4a38af --- /dev/null +++ b/doc/neps/nep-0034-infer-dtype-is-object.rst @@ -0,0 +1,148 @@ +.. _NEP34: + +=========================================================== +NEP 34 — Disallow inferring ``dtype=object`` from sequences +=========================================================== + +:Author: Matti Picus +:Status: Accepted +:Type: Standards Track +:Created: 2019-10-10 +:Resolution: https://mail.python.org/pipermail/numpy-discussion/2019-October/080200.html + +Abstract +-------- + +When users create arrays with sequences-of-sequences, they sometimes err in +matching the lengths of the nested sequences_, commonly called "ragged +arrays". Here we will refer to them as ragged nested sequences. Creating such +arrays via ``np.array([<ragged_nested_sequence>])`` with no ``dtype`` keyword +argument will today default to an ``object``-dtype array. Change the behaviour to +raise a ``ValueError`` instead. + +Motivation and Scope +-------------------- + +Users who specify lists-of-lists when creating a `numpy.ndarray` via +``np.array`` may mistakenly pass in lists of different lengths. Currently we +accept this input and automatically create an array with ``dtype=object``. This +can be confusing, since it is rarely what is desired. Changing the automatic +dtype detection to never return ``object`` for ragged nested sequences (defined as a +recursive sequence of sequences, where not all the sequences on the same +level have the same length) will force users who actually wish to create +``object`` arrays to specify that explicitly. Note that ``lists``, ``tuples``, +and ``nd.ndarrays`` are all sequences [0]_. See for instance `issue 5303`_. + +Usage and Impact +---------------- + +After this change, array creation with ragged nested sequences must explicitly +define a dtype: + + >>> np.array([[1, 2], [1]]) + ValueError: cannot guess the desired dtype from the input + + >>> np.array([[1, 2], [1]], dtype=object) + # succeeds, with no change from current behaviour + +The deprecation will affect any call that internally calls ``np.asarray``. For +instance, the ``assert_equal`` family of functions calls ``np.asarray``, so +users will have to change code like:: + + np.assert_equal(a, [[1, 2], 3]) + +to:: + + np.assert_equal(a, np.array([[1, 2], 3], dtype=object)) + +Detailed description +-------------------- + +To explicitly set the shape of the object array, since it is sometimes hard to +determine what shape is desired, one could use: + + >>> arr = np.empty(correct_shape, dtype=object) + >>> arr[...] = values + +We will also reject mixed sequences of non-sequence and sequence, for instance +all of these will be rejected: + + >>> arr = np.array([np.arange(10), [10]]) + >>> arr = np.array([[range(3), range(3), range(3)], [range(3), 0, 0]]) + +Related Work +------------ + +`PR 14341`_ tried to raise an error when ragged nested sequences were specified +with a numeric dtype ``np.array, [[1], [2, 3]], dtype=int)`` but failed due to +false-positives, for instance ``np.array([1, np.array([5])], dtype=int)``. + +.. _`PR 14341`: https://github.com/numpy/numpy/pull/14341 + +Implementation +-------------- + +The code to be changed is inside ``PyArray_GetArrayParamsFromObject`` and the +internal ``discover_dimensions`` function. The first implementation in `PR +14794`_ caused a number of downstream library failures and was reverted before +the release of 1.18. Subsequently downstream libraries fixed the places they +were using ragged arrays. The reimplementation became `PR 15119`_ which was +merged for the 1.19 release. + +Backward compatibility +---------------------- + +Anyone depending on creating object arrays from ragged nested sequences will +need to modify their code. There will be a deprecation period during which the +current behaviour will emit a ``DeprecationWarning``. + +Alternatives +------------ + +- We could continue with the current situation. + +- It was also suggested to add a kwarg ``depth`` to array creation, or perhaps + to add another array creation API function ``ragged_array_object``. The goal + was to eliminate the ambiguity in creating an object array from ``array([[1, + 2], [1]], dtype=object)``: should the returned array have a shape of + ``(1,)``, or ``(2,)``? This NEP does not deal with that issue, and only + deprecates the use of ``array`` with no ``dtype=object`` for ragged nested + sequences. Users of ragged nested sequences may face another deprecation + cycle in the future. Rationale: we expect that there are very few users who + intend to use ragged arrays like that, this was never intended as a use case + of NumPy arrays. Users are likely better off with `another library`_ or just + using list of lists. + +- It was also suggested to deprecate all automatic creation of ``object``-dtype + arrays, which would require adding an explicit ``dtype=object`` for something + like ``np.array([Decimal(10), Decimal(10)])``. This too is out of scope for + the current NEP. Rationale: it's harder to asses the impact of this larger + change, we're not sure how many users this may impact. + +Discussion +---------- + +Comments to `issue 5303`_ indicate this is unintended behaviour as far back as +2014. Suggestions to change it have been made in the ensuing years, but none +have stuck. The WIP implementation in `PR 14794`_ seems to point to the +viability of this approach. + +References and Footnotes +------------------------ + +.. _`issue 5303`: https://github.com/numpy/numpy/issues/5303 +.. _sequences: https://docs.python.org/3.7/glossary.html#term-sequence +.. _`PR 14794`: https://github.com/numpy/numpy/pull/14794 +.. _`PR 15119`: https://github.com/numpy/numpy/pull/15119 +.. _`another library`: https://github.com/scikit-hep/awkward-array + +.. [0] ``np.ndarrays`` are not recursed into, rather their shape is used + directly. This will not emit warnings:: + + ragged = np.array([[1], [1, 2, 3]], dtype=object) + np.array([ragged, ragged]) # no dtype needed + +Copyright +--------- + +This document has been placed in the public domain. diff --git a/doc/neps/nep-0035-array-creation-dispatch-with-array-function.rst b/doc/neps/nep-0035-array-creation-dispatch-with-array-function.rst new file mode 100644 index 000000000000..f6a77f75470d --- /dev/null +++ b/doc/neps/nep-0035-array-creation-dispatch-with-array-function.rst @@ -0,0 +1,450 @@ +.. _NEP35: + +=========================================================== +NEP 35 — Array creation dispatching with __array_function__ +=========================================================== + +:Author: Peter Andreas Entschev <pentschev@nvidia.com> +:Status: Final +:Type: Standards Track +:Created: 2019-10-15 +:Updated: 2020-11-06 +:Resolution: https://mail.python.org/pipermail/numpy-discussion/2021-May/081761.html + +Abstract +-------- + +We propose the introduction of a new keyword argument ``like=`` to all array +creation functions to address one of the shortcomings of ``__array_function__``, +as described by NEP 18 [1]_. The ``like=`` keyword argument will create an +instance of the argument's type, enabling direct creation of non-NumPy arrays. +The target array type must implement the ``__array_function__`` protocol. + +Motivation and Scope +-------------------- + +Many libraries implement the NumPy API, such as Dask for graph +computing, CuPy for GPGPU computing, xarray for N-D labeled arrays, etc. Underneath, +they have adopted the ``__array_function__`` protocol which allows NumPy to understand +and treat downstream objects as if they are the native ``numpy.ndarray`` object. +Hence the community while using various libraries still benefits from a unified +NumPy API. This not only brings great convenience for standardization but also +removes the burden of learning a new API and rewriting code for every new +object. In more technical terms, this mechanism of the protocol is called a +"dispatcher", which is the terminology we use from here onwards when referring +to that. + + +.. code:: python + + x = dask.array.arange(5) # Creates dask.array + np.diff(x) # Returns dask.array + +Note above how we called Dask's implementation of ``diff`` via the NumPy +namespace by calling ``np.diff``, and the same would apply if we had a CuPy +array or any other array from a library that adopts ``__array_function__``. +This allows writing code that is agnostic to the implementation library, thus +users can write their code once and still be able to use different array +implementations according to their needs. + +Obviously, having a protocol in-place is useful if the arrays are created +elsewhere and let NumPy handle them. But still these arrays have to be started +in their native library and brought back. Instead if it was possible to create +these objects through NumPy API then there would be an almost complete +experience, all using NumPy syntax. For example, say we have some CuPy array +``cp_arr``, and want a similar CuPy array with identity matrix. We could still +write the following: + +.. code:: python + + x = cupy.identity(3) + +Instead, the better way would be using to only use the NumPy API, this could now +be achieved with: + +.. code:: python + + x = np.identity(3, like=cp_arr) + +As if by magic, ``x`` will also be a CuPy array, as NumPy was capable to infer +that from the type of ``cp_arr``. Note that this last step would not be possible +without ``like=``, as it would be impossible for the NumPy to know the user +expects a CuPy array based only on the integer input. + +The new ``like=`` keyword proposed is solely intended to identify the downstream +library where to dispatch and the object is used only as reference, meaning that +no modifications, copies or processing will be performed on that object. + +We expect that this functionality will be mostly useful to library developers, +allowing them to create new arrays for internal usage based on arrays passed +by the user, preventing unnecessary creation of NumPy arrays that will +ultimately lead to an additional conversion into a downstream array type. + +Support for Python 2.7 has been dropped since NumPy 1.17, therefore we make use +of the keyword-only argument standard described in PEP-3102 [2]_ to implement +``like=``, thus preventing it from being passed by position. + +.. _neps.like-kwarg.usage-and-impact: + +Usage and Impact +---------------- + +NumPy users who don't use other arrays from downstream libraries can continue +to use array creation routines without a ``like=`` argument. Using +``like=np.ndarray`` will work as if no array was passed via that argument. +However, this will incur additional checks that will negatively impact +performance. + +To understand the intended use for ``like=``, and before we move to more complex +cases, consider the following illustrative example consisting only of NumPy and +CuPy arrays: + +.. code:: python + + import numpy as np + import cupy + + def my_pad(arr, padding): + padding = np.array(padding, like=arr) + return np.concatenate((padding, arr, padding)) + + my_pad(np.arange(5), [-1, -1]) # Returns np.ndarray + my_pad(cupy.arange(5), [-1, -1]) # Returns cupy.core.core.ndarray + +Note in the ``my_pad`` function above how ``arr`` is used as a reference to +dictate what array type padding should have, before concatenating the arrays to +produce the result. On the other hand, if ``like=`` wasn't used, the NumPy case +would still work, but CuPy wouldn't allow this kind of automatic +conversion, ultimately raising a +``TypeError: Only cupy arrays can be concatenated`` exception. + +Now we should look at how a library like Dask could benefit from ``like=``. +Before we understand that, it's important to understand a bit about Dask basics +and how it ensures correctness with ``__array_function__``. Note that Dask can +perform computations on different sorts of objects, like dataframes, bags and +arrays, here we will focus strictly on arrays, which are the objects we can use +``__array_function__`` with. + +Dask uses a graph computing model, meaning it breaks down a large problem in +many smaller problems and merges their results to reach the final result. To +break the problem down into smaller ones, Dask also breaks arrays into smaller +arrays that it calls "chunks". A Dask array can thus consist of one or more +chunks and they may be of different types. However, in the context of +``__array_function__``, Dask only allows chunks of the same type; for example, +a Dask array can be formed of several NumPy arrays or several CuPy arrays, but +not a mix of both. + +To avoid mismatched types during computation, Dask keeps an attribute ``_meta`` as +part of its array throughout computation: this attribute is used to both predict +the output type at graph creation time, and to create any intermediary arrays +that are necessary within some function's computation. Going back to our +previous example, we can use ``_meta`` information to identify what kind of +array we would use for padding, as seen below: + +.. code:: python + + import numpy as np + import cupy + import dask.array as da + from dask.array.utils import meta_from_array + + def my_dask_pad(arr, padding): + padding = np.array(padding, like=meta_from_array(arr)) + return np.concatenate((padding, arr, padding)) + + # Returns dask.array<concatenate, shape=(9,), dtype=int64, chunksize=(5,), chunktype=numpy.ndarray> + my_dask_pad(da.arange(5), [-1, -1]) + + # Returns dask.array<concatenate, shape=(9,), dtype=int64, chunksize=(5,), chunktype=cupy.ndarray> + my_dask_pad(da.from_array(cupy.arange(5)), [-1, -1]) + +Note how ``chunktype`` in the return value above changes from +``numpy.ndarray`` in the first ``my_dask_pad`` call to ``cupy.ndarray`` in the +second. We have also renamed the function to ``my_dask_pad`` in this example +with the intent to make it clear that this is how Dask would implement such +functionality, should it need to do so, as it requires Dask's internal tools +that are not of much use elsewhere. + +To enable proper identification of the array type we use Dask's utility function +``meta_from_array``, which was introduced as part of the work to support +``__array_function__``, allowing Dask to handle ``_meta`` appropriately. Readers +can think of ``meta_from_array`` as a special function that just returns the +type of the underlying Dask array, for example: + +.. code:: python + + np_arr = da.arange(5) + cp_arr = da.from_array(cupy.arange(5)) + + meta_from_array(np_arr) # Returns a numpy.ndarray + meta_from_array(cp_arr) # Returns a cupy.ndarray + +Since the value returned by ``meta_from_array`` is a NumPy-like array, we can +just pass that directly into the ``like=`` argument. + +The ``meta_from_array`` function is primarily targeted at the library's internal +usage to ensure chunks are created with correct types. Without the ``like=`` +argument, it would be impossible to ensure ``my_pad`` creates a padding array +with a type matching that of the input array, which would cause a ``TypeError`` +exception to be raised by CuPy, as discussed above would happen to the CuPy case +alone. Combining Dask's internal handling of meta arrays and the proposed +``like=`` argument, it now becomes possible to handle cases involving creation +of non-NumPy arrays, which is likely the heaviest limitation Dask currently +faces from the ``__array_function__`` protocol. + +Backward Compatibility +---------------------- + +This proposal does not raise any backward compatibility issues within NumPy, +given that it only introduces a new keyword argument to existing array creation +functions with a default ``None`` value, thus not changing current behavior. + +Detailed description +-------------------- + +The introduction of the ``__array_function__`` protocol allowed downstream +library developers to use NumPy as a dispatching API. However, the protocol +did not -- and did not intend to -- address the creation of arrays by downstream +libraries, preventing those libraries from using such important functionality in +that context. + +The purpose of this NEP is to address that shortcoming in a simple and +straightforward way: introduce a new ``like=`` keyword argument, similar to how +the ``empty_like`` family of functions work. When array creation functions +receive such an argument, they will trigger the ``__array_function__`` protocol, +and call the downstream library's own array creation function implementation. +The ``like=`` argument, as its own name suggests, shall be used solely for the +purpose of identifying where to dispatch. In contrast to the way +``__array_function__`` has been used so far (the first argument identifies the +target downstream library), and to avoid breaking NumPy's API with regards to +array creation, the new ``like=`` keyword shall be used for the purpose of +dispatching. + +Downstream libraries will benefit from the ``like=`` argument without any +changes to their API, given the argument only needs to be implemented by NumPy. +It's still allowed that downstream libraries include the ``like=`` argument, +as it can be useful in some cases, please refer to +:ref:`neps.like-kwarg.implementation` for details on those cases. It will still +be required that downstream libraries implement the ``__array_function__`` +protocol, as described by NEP 18 [1]_, and appropriately introduce the argument +to their calls to NumPy array creation functions, as exemplified in +:ref:`neps.like-kwarg.usage-and-impact`. + +Related work +------------ + +Other NEPs have been written to address parts of ``__array_function__`` +protocol's limitation, such as the introduction of the ``__duckarray__`` +protocol in NEP 30 [3]_, and the introduction of an overriding mechanism called +``uarray`` by NEP 31 [4]_. + +.. _neps.like-kwarg.implementation: + +Implementation +-------------- + +The implementation requires introducing a new ``like=`` keyword to all existing +array creation functions of NumPy. As examples of functions that would add this +new argument (but not limited to) we can cite those taking array-like objects +such as ``array`` and ``asarray``, functions that create arrays based on +numerical inputs such as ``range`` and ``identity``, as well as the ``empty`` +family of functions, even though that may be redundant, since specializations +for those already exist with the naming format ``empty_like``. As of the +writing of this NEP, a complete list of array creation functions can be +found in [5]_. + +This newly proposed keyword shall be removed by the ``__array_function__`` +mechanism from the keyword dictionary before dispatching. The purpose for this +is twofold: + +1. Simplifies adoption of array creation by those libraries already opting-in + to implement the ``__array_function__`` protocol, thus removing the + requirement to explicitly opt-in for all array creation functions; and +2. Most downstream libraries will have no use for the keyword argument, and + those that do may accomplish so by capturing ``self`` from + ``__array_function__``. + +Downstream libraries thus do not require to include the ``like=`` keyword to +their array creation APIs. In some cases (e.g., Dask), having the ``like=`` +keyword can be useful, as it would allow the implementation to identify +array internals. As an example, Dask could benefit from the reference array +to identify its chunk type (e.g., NumPy, CuPy, Sparse), and thus create a new +Dask array backed by the same chunk type, something that's not possible unless +Dask can read the reference array's attributes. + +Function Dispatching +~~~~~~~~~~~~~~~~~~~~ + +There are two different cases to dispatch: Python functions, and C functions. +To permit ``__array_function__`` dispatching, one possible implementation is to +decorate Python functions with ``overrides.array_function_dispatch``, but C +functions have a different requirement, which we shall describe shortly. + +The example below shows a suggestion on how the ``asarray`` could be decorated +with ``overrides.array_function_dispatch``: + +.. code:: python + + def _asarray_decorator(a, dtype=None, order=None, *, like=None): + return (like,) + + @set_module('numpy') + @array_function_dispatch(_asarray_decorator) + def asarray(a, dtype=None, order=None, *, like=None): + return array(a, dtype, copy=False, order=order) + +Note in the example above that the implementation remains unchanged, the only +difference is the decoration, which uses the new ``_asarray_decorator`` function +to instruct the ``__array_function__`` protocol to dispatch if ``like`` is not +``None``. + +We will now look at a C function example, and since ``asarray`` is anyway a +specialization of ``array``, we will use the latter as an example now. As +``array`` is a C function, currently all NumPy does regarding its Python source +is to import the function and adjust its ``__module__`` to ``numpy``. The +function will now be decorated with a specialization of +``overrides.array_function_from_dispatcher``, which shall take care of adjusting +the module too. + +.. code:: python + + array_function_nodocs_from_c_func_and_dispatcher = functools.partial( + overrides.array_function_from_dispatcher, + module='numpy', docs_from_dispatcher=False, verify=False) + + @array_function_nodocs_from_c_func_and_dispatcher(_multiarray_umath.array) + def array(a, dtype=None, *, copy=True, order='K', subok=False, ndmin=0, + like=None): + return (like,) + +There are two downsides to the implementation above for C functions: + +1. It creates another Python function call; and +2. To follow current implementation standards, documentation should be attached + directly to the Python source code. + +The first version of this proposal suggested the implementation above as one +viable solution for NumPy functions implemented in C. However, due to the +downsides pointed out above we have decided to discard any changes on the Python +side and resolve those issues with a pure-C implementation. Please refer to +[7]_ for details. + +Reading the Reference Array Downstream +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +As stated in the beginning of :ref:`neps.like-kwarg.implementation` section, +``like=`` is not propagated to the downstream library, nevertheless, it's still +possible to access it. This requires some changes in the downstream library's +``__array_function__`` definition, where the ``self`` attribute is in practice +that passed via ``like=``. This is the case because we use ``like=`` as the +dispatching array, unlike other compute functions covered by NEP-18 that usually +dispatch on the first positional argument. + +An example of such use is to create a new Dask array while preserving its +backend type: + +.. code:: python + + # Returns dask.array<array, shape=(3,), dtype=int64, chunksize=(3,), chunktype=cupy.ndarray> + np.asarray([1, 2, 3], like=da.array(cp.array(()))) + + # Returns a cupy.ndarray + type(np.asarray([1, 2, 3], like=da.array(cp.array(()))).compute()) + +Note how above the array is backed by ``chunktype=cupy.ndarray``, and the +resulting array after computing it is also a ``cupy.ndarray``. If Dask did +not use the ``like=`` argument via the ``self`` attribute from +``__array_function__``, the example above would be backed by ``numpy.ndarray`` +instead: + +.. code:: python + + # Returns dask.array<array, shape=(3,), dtype=int64, chunksize=(3,), chunktype=numpy.ndarray> + np.asarray([1, 2, 3], like=da.array(cp.array(()))) + + # Returns a numpy.ndarray + type(np.asarray([1, 2, 3], like=da.array(cp.array(()))).compute()) + +Given the library would need to rely on ``self`` attribute from +``__array_function__`` to dispatch the function with the correct reference +array, we suggest one of two alternatives: + +1. Introduce a list of functions in the downstream library that do support the + ``like=`` argument and pass ``like=self`` when calling the function; or +2. Inspect whether the function's signature and verify whether it includes the + ``like=`` argument. Note that this may incur in a higher performance penalty + and assumes introspection is possible, which may not be if the function is + a C function. + +To make things clearer, let's take a look at how suggestion 2 could be +implemented in Dask. The current relevant part of ``__array_function__`` +definition in Dask is seen below: + +.. code:: python + + def __array_function__(self, func, types, args, kwargs): + # Code not relevant for this example here + + # Dispatch ``da_func`` (da.asarray, for example) with *args and **kwargs + da_func(*args, **kwargs) + +And this is how the updated code would look like: + +.. code:: python + + def __array_function__(self, func, types, args, kwargs): + # Code not relevant for this example here + + # Inspect ``da_func``'s signature and store keyword-only arguments + import inspect + kwonlyargs = inspect.getfullargspec(da_func).kwonlyargs + + # If ``like`` is contained in ``da_func``'s signature, add ``like=self`` + # to the kwargs dictionary. + if 'like' in kwonlyargs: + kwargs['like'] = self + + # Dispatch ``da_func`` (da.asarray, for example) with args and kwargs. + # Here, kwargs contain ``like=self`` if the function's signature does too. + da_func(*args, **kwargs) + +Alternatives +------------ + +Recently a new protocol to replace ``__array_function__`` entirely was proposed +by NEP 37 [6]_, which would require considerable rework by downstream libraries +that adopt ``__array_function__`` already, because of that we still believe the +``like=`` argument is beneficial for NumPy and downstream libraries. However, +that proposal wouldn't necessarily be considered a direct alternative to the +present NEP, as it would replace NEP 18 entirely, upon which this builds. +Discussion on details about this new proposal and why that would require rework +by downstream libraries is beyond the scope of the present proposal. + +Discussion +---------- + +- `Further discussion on implementation and the NEP's content <https://mail.python.org/pipermail/numpy-discussion/2020-August/080919.html>`_ +- `Decision to release an experimental implementation in NumPy 1.20.0 <https://mail.python.org/pipermail/numpy-discussion/2020-November/081193.html>`__ + + +References +---------- + +.. [1] `NEP 18 - A dispatch mechanism for NumPy's high level array functions <https://numpy.org/neps/nep-0018-array-function-protocol.html>`_. + +.. [2] `PEP 3102 — Keyword-Only Arguments <https://www.python.org/dev/peps/pep-3102/>`_. + +.. [3] `NEP 30 — Duck Typing for NumPy Arrays - Implementation <https://numpy.org/neps/nep-0030-duck-array-protocol.html>`_. + +.. [4] `NEP 31 — Context-local and global overrides of the NumPy API <https://github.com/numpy/numpy/pull/14389>`_. + +.. [5] `Array creation routines <https://docs.scipy.org/doc/numpy-1.17.0/reference/routines.array-creation.html>`_. + +.. [6] `NEP 37 — A dispatch protocol for NumPy-like modules <https://numpy.org/neps/nep-0037-array-module.html>`_. + +.. [7] `Implementation's pull request on GitHub <https://github.com/numpy/numpy/pull/16935>`_ + +Copyright +--------- + +This document has been placed in the public domain. diff --git a/doc/neps/nep-0036-fair-play.rst b/doc/neps/nep-0036-fair-play.rst new file mode 100644 index 000000000000..2acdcc70459a --- /dev/null +++ b/doc/neps/nep-0036-fair-play.rst @@ -0,0 +1,181 @@ +================== +NEP 36 — Fair play +================== + +:Author: Stéfan van der Walt <stefanv@berkeley.edu> +:Status: Accepted +:Type: Informational +:Created: 2019-10-24 +:Resolution: https://mail.python.org/pipermail/numpy-discussion/2021-June/081890.html + + +Abstract +-------- + +This document sets out Rules of Play for companies and outside +developers that engage with the NumPy project. It covers: + +- Restrictions on use of the NumPy name +- How and whether to publish a modified distribution +- How to make us aware of patched versions + +Companies and developers will know after reading this NEP what kinds +of behavior the community would like to see, and which we consider +troublesome, bothersome, and unacceptable. + +Motivation +---------- + +Every so often, we learn of NumPy versions modified and circulated by outsiders. +These patched versions can cause problems for the NumPy community +(see, e.g., [#erf]_ and [#CVE-2019-6446]_). +When issues like these arise, our developers waste time identifying +the problematic release, locating alterations, and determining an +appropriate course of action. + +In addition, packages on the Python Packaging Index are sometimes +named such that users assume they are sanctioned or maintained by +NumPy. We wish to reduce the number of such incidents. + +During a community call on `October 16th, 2019 +<https://github.com/numpy/archive/blob/main/status_meetings/status-2019-10-16.md>`__ +the community resolved to draft guidelines to address these matters. + +.. [#erf] In December 2018, a + `bug report <https://github.com/numpy/numpy/issues/12515>`__ + was filed against `np.erf` -- a function that didn't exist in the + NumPy distribution. It came to light that a company had published + a NumPy version with an extended API footprint. After several + months of discussion, the company agreed to make its patches + public, and we added a label to the NumPy issue tracker to identify + issues pertaining to that distribution. + +.. [#CVE-2019-6446] After a security issue (CVE-2019-6446) was filed + against NumPy, distributions put in their own fixes, most often by + changing a default keyword value. As a result the NumPy API was + inconsistent across distributions. + +Scope +----- + +This document aims to define a minimal set of rules that, when +followed, will be considered good-faith efforts in line with the +expectations of the NumPy developers. + +Our hope is that developers who feel they need to modify NumPy will +first consider contributing to the project, or use one of several existing +mechanisms for extending our APIs and for operating on +externally defined array objects. + +When in doubt, please `talk to us first +<https://numpy.org/community/>`__. We may suggest an alternative; at +minimum, we'll be prepared. + +Fair play rules +--------------- + +1. Do not reuse the NumPy name for projects not developed by the NumPy + community. + + At time of writing, there are only a handful of ``numpy``-named + packages developed by the community, including ``numpy``, + ``numpy-financial``, and ``unumpy``. We ask that external packages not + include the phrase ``numpy``, i.e., avoid names such as + ``mycompany_numpy``. + + To be clear, this rule only applies to modules (package names); it + is perfectly acceptable to have a *submodule* of your own library + named ``mylibrary.numpy``. + + NumPy is a trademark owned by NumFOCUS. + +2. Do not republish modified versions of NumPy. + + Modified versions of NumPy make it very difficult for the + developers to address bug reports, since we typically do not know + which parts of NumPy have been modified. + + If you have to break this rule (and we implore you not + to!), then make it clear in the ``__version__`` tag that + you have modified NumPy, e.g.:: + + >>> print(np.__version__) + '1.17.2+mycompany.15` + + We understand that minor patches are often required to make a + library work inside of a distribution. E.g., Debian may patch + NumPy so that it searches for optimized BLAS libraries in the + correct locations. This is acceptable, but we ask that no + substantive changes are made. + +3. Do not extend or modify NumPy's API. + + If you absolutely have to break rule two, please do not add + additional functions to the namespace, or modify the API of + existing functions. NumPy's API is already + quite large, and we are working hard to reduce it where feasible. + Having additional functions exposed in distributed versions is + confusing for users and developers alike. + +4. *DO* use official mechanism to engage with the API. + + Protocols such as `__array_ufunc__ + <https://numpy.org/neps/nep-0013-ufunc-overrides.html>`__ and + `__array_function__ + <https://numpy.org/neps/nep-0018-array-function-protocol.html>`__ + were designed to help external packages interact more easily with + NumPy. E.g., the latter allows objects from foreign libraries to + pass through NumPy. We actively encourage using any of + these "officially sanctioned" mechanisms for overriding or + interacting with NumPy. + + If these mechanisms are deemed insufficient, please start a + discussion on the mailing list before monkeypatching NumPy. + +Questions and answers +--------------------- + +**Q:** We would like to distribute an optimized version of NumPy that +utilizes special instructions for our company's CPU. You recommend +against that, so what are we to do? + +**A:** Please consider including the patches required in the official +NumPy repository. Not only do we encourage such contributions, but we +already have optimized loops for some platforms available. + +**Q:** We would like to ship a much faster version of FFT than NumPy +provides, but NumPy has no mechanism for overriding its FFT routines. +How do we proceed? + +**A:** There are two solutions that we approve of: let the users +install your optimizations using a piece of code, such as:: + + from my_company_accel import patch_numpy_fft + patch_numpy_fft() + +or have your distribution automatically perform the above, but print a +message to the terminal clearly stating what is happening:: + + We are now patching NumPy for optimal performance under MyComp + Special Platform. Please direct all bug reports to + https://mycomp.com/numpy-bugs + +If you require additional mechanisms for overriding code, please +discuss this with the development team on the mailing list. + +**Q:** We would like to distribute NumPy with faster linear algebra +routines. Are we allowed to do this? + +**A:** Yes, this is explicitly supported by linking to a different +version of BLAS. + +Discussion +---------- + +References and footnotes +------------------------ + +Copyright +--------- + +This document has been placed in the public domain. diff --git a/doc/neps/nep-0037-array-module.rst b/doc/neps/nep-0037-array-module.rst new file mode 100644 index 000000000000..1e868324d007 --- /dev/null +++ b/doc/neps/nep-0037-array-module.rst @@ -0,0 +1,570 @@ +.. _NEP37: + +=================================================== +NEP 37 — A dispatch protocol for NumPy-like modules +=================================================== + +:Author: Stephan Hoyer <shoyer@google.com> +:Author: Hameer Abbasi +:Author: Sebastian Berg +:Status: Draft +:Type: Standards Track +:Created: 2019-12-29 + +Abstract +-------- + +NEP-18's ``__array_function__`` has been a mixed success. Some projects (e.g., +dask, CuPy, xarray, sparse, Pint, MXNet) have enthusiastically adopted it. +Others (e.g., JAX) have been more reluctant. Here we propose a new +protocol, ``__array_module__``, that we expect could eventually subsume most +use-cases for ``__array_function__``. The protocol requires explicit adoption +by both users and library authors, which ensures backwards compatibility, and +is also significantly simpler than ``__array_function__``, both of which we +expect will make it easier to adopt. + +Why ``__array_function__`` hasn't been enough +--------------------------------------------- + +There are two broad ways in which NEP-18 has fallen short of its goals: + +1. **Backwards compatibility concerns**. `__array_function__` has significant + implications for libraries that use it: + + - `JAX <https://github.com/google/jax/issues/1565>`_ has been reluctant + to implement ``__array_function__`` in part because it is concerned about + breaking existing code: users expect NumPy functions like + ``np.concatenate`` to return NumPy arrays. This is a fundamental + limitation of the ``__array_function__`` design, which we chose to allow + overriding the existing ``numpy`` namespace. + Libraries like Dask and CuPy have looked at and accepted the backwards + incompatibility impact of ``__array_function__``; it would still have been + better for them if that impact didn't exist. + + Note that projects like `PyTorch + <https://github.com/pytorch/pytorch/issues/22402>`_ and `scipy.sparse + <https://github.com/scipy/scipy/issues/10362>`_ have also not + adopted ``__array_function__`` yet, because they don't have a + NumPy-compatible API or semantics. In the case of PyTorch, that is likely + to be added in the future. ``scipy.sparse`` is in the same situation as + ``numpy.matrix``: its semantics are not compatible with ``numpy.ndarray`` + and therefore adding ``__array_function__`` (except to return ``NotImplemented`` + perhaps) is not a healthy idea. + - ``__array_function__`` currently requires an "all or nothing" approach to + implementing NumPy's API. There is no good pathway for **incremental + adoption**, which is particularly problematic for established projects + for which adopting ``__array_function__`` would result in breaking + changes. + +2. **Limitations on what can be overridden.** ``__array_function__`` has some + important gaps, most notably array creation and coercion functions: + + - **Array creation** routines (e.g., ``np.arange`` and those in + ``np.random``) need some other mechanism for indicating what type of + arrays to create. `NEP 35 <https://numpy.org/neps/nep-0035-array-creation-dispatch-with-array-function.html>`_ + proposed adding optional ``like=`` arguments to functions without + existing array arguments. However, we still lack any mechanism to + override methods on objects, such as those needed by + ``np.random.RandomState``. + - **Array conversion** can't reuse the existing coercion functions like + ``np.asarray``, because ``np.asarray`` sometimes means "convert to an + exact ``np.ndarray``" and other times means "convert to something _like_ + a NumPy array." This led to the `NEP 30 + <https://numpy.org/neps/nep-0030-duck-array-protocol.html>`_ proposal for + a separate ``np.duckarray`` function, but this still does not resolve how + to cast one duck array into a type matching another duck array. + +Other maintainability concerns that were raised include: + +- It is no longer possible to use **aliases to NumPy functions** within + modules that support overrides. For example, both CuPy and JAX set + ``result_type = np.result_type`` and now have to wrap use of + ``np.result_type`` in their own ``result_type`` function instead. +- Implementing **fall-back mechanisms** for unimplemented NumPy functions + by using NumPy's implementation is hard to get right (but see the + `version from dask <https://github.com/dask/dask/pull/5043>`_), because + ``__array_function__`` does not present a consistent interface. + Converting all arguments of array type requires recursing into generic + arguments of the form ``*args, **kwargs``. + +``get_array_module`` and the ``__array_module__`` protocol +---------------------------------------------------------- + +We propose a new user-facing mechanism for dispatching to a duck-array +implementation, ``numpy.get_array_module``. ``get_array_module`` performs the +same type resolution as ``__array_function__`` and returns a module with an API +promised to match the standard interface of ``numpy`` that can implement +operations on all provided array types. + +The protocol itself is both simpler and more powerful than +``__array_function__``, because it doesn't need to worry about actually +implementing functions. We believe it resolves most of the maintainability and +functionality limitations of ``__array_function__``. + +The new protocol is opt-in, explicit and with local control; see +:ref:`appendix-design-choices` for discussion on the importance of these design +features. + +The array module contract +========================= + +Modules returned by ``get_array_module``/``__array_module__`` should make a +best effort to implement NumPy's core functionality on new array types(s). +Unimplemented functionality should simply be omitted (e.g., accessing an +unimplemented function should raise ``AttributeError``). In the future, we +anticipate codifying a protocol for requesting restricted subsets of ``numpy``; +see :ref:`requesting-restricted-subsets` for more details. + +How to use ``get_array_module`` +=============================== + +Code that wants to support generic duck arrays should explicitly call +``get_array_module`` to determine an appropriate array module from which to +call functions, rather than using the ``numpy`` namespace directly. For +example: + +.. code:: python + + # calls the appropriate version of np.something for x and y + module = np.get_array_module(x, y) + module.something(x, y) + +Both array creation and array conversion are supported, because dispatching is +handled by ``get_array_module`` rather than via the types of function +arguments. For example, to use random number generation functions or methods, +we can simply pull out the appropriate submodule: + +.. code:: python + + def duckarray_add_random(array): + module = np.get_array_module(array) + noise = module.random.randn(*array.shape) + return array + noise + +We can also write the duck-array ``stack`` function from `NEP 30 +<https://numpy.org/neps/nep-0030-duck-array-protocol.html>`_, without the need +for a new ``np.duckarray`` function: + +.. code:: python + + def duckarray_stack(arrays): + module = np.get_array_module(*arrays) + arrays = [module.asarray(arr) for arr in arrays] + shapes = {arr.shape for arr in arrays} + if len(shapes) != 1: + raise ValueError('all input arrays must have the same shape') + expanded_arrays = [arr[module.newaxis, ...] for arr in arrays] + return module.concatenate(expanded_arrays, axis=0) + +By default, ``get_array_module`` will return the ``numpy`` module if no +arguments are arrays. This fall-back can be explicitly controlled by providing +the ``module`` keyword-only argument. It is also possible to indicate that an +exception should be raised instead of returning a default array module by +setting ``module=None``. + +How to implement ``__array_module__`` +===================================== + +Libraries implementing a duck array type that want to support +``get_array_module`` need to implement the corresponding protocol, +``__array_module__``. This new protocol is based on Python's dispatch protocol +for arithmetic, and is essentially a simpler version of ``__array_function__``. + +Only one argument is passed into ``__array_module__``, a Python collection of +unique array types passed into ``get_array_module``, i.e., all arguments with +an ``__array_module__`` attribute. + +The special method should either return a namespace with an API matching +``numpy``, or ``NotImplemented``, indicating that it does not know how to +handle the operation: + +.. code:: python + + class MyArray: + def __array_module__(self, types): + if not all(issubclass(t, MyArray) for t in types): + return NotImplemented + return my_array_module + +Returning custom objects from ``__array_module__`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``my_array_module`` will typically, but need not always, be a Python module. +Returning a custom objects (e.g., with functions implemented via +``__getattr__``) may be useful for some advanced use cases. + +For example, custom objects could allow for partial implementations of duck +array modules that fall-back to NumPy (although this is not recommended in +general because such fall-back behavior can be error prone): + +.. code:: python + + class MyArray: + def __array_module__(self, types): + if all(issubclass(t, MyArray) for t in types): + return ArrayModule() + else: + return NotImplemented + + class ArrayModule: + def __getattr__(self, name): + import base_module + return getattr(base_module, name, getattr(numpy, name)) + +Subclassing from ``numpy.ndarray`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +All of the same guidance about well-defined type casting hierarchies from +NEP-18 still applies. ``numpy.ndarray`` itself contains a matching +implementation of ``__array_module__``, which is convenient for subclasses: + +.. code:: python + + class ndarray: + def __array_module__(self, types): + if all(issubclass(t, ndarray) for t in types): + return numpy + else: + return NotImplemented + +NumPy's internal machinery +========================== + +The type resolution rules of ``get_array_module`` follow the same model as +Python and NumPy's existing dispatch protocols: subclasses are called before +super-classes, and otherwise left to right. ``__array_module__`` is guaranteed +to be called only a single time on each unique type. + +The actual implementation of `get_array_module` will be in C, but should be +equivalent to this Python code: + +.. code:: python + + def get_array_module(*arrays, default=numpy): + implementing_arrays, types = _implementing_arrays_and_types(arrays) + if not implementing_arrays and default is not None: + return default + for array in implementing_arrays: + module = array.__array_module__(types) + if module is not NotImplemented: + return module + raise TypeError("no common array module found") + + def _implementing_arrays_and_types(relevant_arrays): + types = [] + implementing_arrays = [] + for array in relevant_arrays: + t = type(array) + if t not in types and hasattr(t, '__array_module__'): + types.append(t) + # Subclasses before superclasses, otherwise left to right + index = len(implementing_arrays) + for i, old_array in enumerate(implementing_arrays): + if issubclass(t, type(old_array)): + index = i + break + implementing_arrays.insert(index, array) + return implementing_arrays, types + +Relationship with ``__array_ufunc__`` and ``__array_function__`` +---------------------------------------------------------------- + +These older protocols have distinct use-cases and should remain +=============================================================== + +``__array_module__`` is intended to resolve limitations of +``__array_function__``, so it is natural to consider whether it could entirely +replace ``__array_function__``. This would offer dual benefits: (1) simplifying +the user-story about how to override NumPy and (2) removing the slowdown +associated with checking for dispatch when calling every NumPy function. + +However, ``__array_module__`` and ``__array_function__`` are pretty different +from a user perspective: it requires explicit calls to ``get_array_function``, +rather than simply reusing original ``numpy`` functions. This is probably fine +for *libraries* that rely on duck-arrays, but may be frustratingly verbose for +interactive use. + +Some of the dispatching use-cases for ``__array_ufunc__`` are also solved by +``__array_module__``, but not all of them. For example, it is still useful to +be able to define non-NumPy ufuncs (e.g., from Numba or SciPy) in a generic way +on non-NumPy arrays (e.g., with dask.array). + +Given their existing adoption and distinct use cases, we don't think it makes +sense to remove or deprecate ``__array_function__`` and ``__array_ufunc__`` at +this time. + +Mixin classes to implement ``__array_function__`` and ``__array_ufunc__`` +========================================================================= + +Despite the user-facing differences, ``__array_module__`` and a module +implementing NumPy's API still contain sufficient functionality needed to +implement dispatching with the existing duck array protocols. + +For example, the following mixin classes would provide sensible defaults for +these special methods in terms of ``get_array_module`` and +``__array_module__``: + +.. code:: python + + class ArrayUfuncFromModuleMixin: + + def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): + arrays = inputs + kwargs.get('out', ()) + try: + array_module = np.get_array_module(*arrays) + except TypeError: + return NotImplemented + + try: + # Note this may have false positive matches, if ufunc.__name__ + # matches the name of a ufunc defined by NumPy. Unfortunately + # there is no way to determine in which module a ufunc was + # defined. + new_ufunc = getattr(array_module, ufunc.__name__) + except AttributeError: + return NotImplemented + + try: + callable = getattr(new_ufunc, method) + except AttributeError: + return NotImplemented + + return callable(*inputs, **kwargs) + + class ArrayFunctionFromModuleMixin: + + def __array_function__(self, func, types, args, kwargs): + array_module = self.__array_module__(types) + if array_module is NotImplemented: + return NotImplemented + + # Traverse submodules to find the appropriate function + modules = func.__module__.split('.') + assert modules[0] == 'numpy' + for submodule in modules[1:]: + module = getattr(module, submodule, None) + new_func = getattr(module, func.__name__, None) + if new_func is None: + return NotImplemented + + return new_func(*args, **kwargs) + +To make it easier to write duck arrays, we could also add these mixin classes +into ``numpy.lib.mixins`` (but the examples above may suffice). + +Alternatives considered +----------------------- + +Naming +====== + +We like the name ``__array_module__`` because it mirrors the existing +``__array_function__`` and ``__array_ufunc__`` protocols. Another reasonable +choice could be ``__array_namespace__``. + +It is less clear what the NumPy function that calls this protocol should be +called (``get_array_module`` in this proposal). Some possible alternatives: +``array_module``, ``common_array_module``, ``resolve_array_module``, +``get_namespace``, ``get_numpy``, ``get_numpylike_module``, +``get_duck_array_module``. + +.. _requesting-restricted-subsets: + +Requesting restricted subsets of NumPy's API +============================================ + +Over time, NumPy has accumulated a very large API surface, with over 600 +attributes in the top level ``numpy`` module alone. It is unlikely that any +duck array library could or would want to implement all of these functions and +classes, because the frequently used subset of NumPy is much smaller. + +We think it would be useful exercise to define "minimal" subset(s) of NumPy's +API, omitting rarely used or non-recommended functionality. For example, +minimal NumPy might include ``stack``, but not the other stacking functions +``column_stack``, ``dstack``, ``hstack`` and ``vstack``. This could clearly +indicate to duck array authors and users what functionality is core and what +functionality they can skip. + +Support for requesting a restricted subset of NumPy's API would be a natural +feature to include in ``get_array_function`` and ``__array_module__``, e.g., + +.. code:: python + + # array_module is only guaranteed to contain "minimal" NumPy + array_module = np.get_array_module(*arrays, request='minimal') + +To facilitate testing with NumPy and use with any valid duck array library, +NumPy itself would return restricted versions of the ``numpy`` module when +``get_array_module`` is called only on NumPy arrays. Omitted functions would +simply not exist. + +Unfortunately, we have not yet figured out what these restricted subsets should +be, so it doesn't make sense to do this yet. When/if we do, we could either add +new keyword arguments to ``get_array_module`` or add new top level functions, +e.g., ``get_minimal_array_module``. We would also need to add either a new +protocol patterned off of ``__array_module__`` (e.g., +``__array_module_minimal__``), or could add an optional second argument to +``__array_module__`` (catching errors with ``try``/``except``). + +A new namespace for implicit dispatch +===================================== + +Instead of supporting overrides in the main `numpy` namespace with +``__array_function__``, we could create a new opt-in namespace, e.g., +``numpy.api``, with versions of NumPy functions that support dispatching. These +overrides would need new opt-in protocols, e.g., ``__array_function_api__`` +patterned off of ``__array_function__``. + +This would resolve the biggest limitations of ``__array_function__`` by being +opt-in and would also allow for unambiguously overriding functions like +``asarray``, because ``np.api.asarray`` would always mean "convert an +array-like object." But it wouldn't solve all the dispatching needs met by +``__array_module__``, and would leave us with supporting a considerably more +complex protocol both for array users and implementors. + +We could potentially implement such a new namespace *via* the +``__array_module__`` protocol. Certainly some users would find this convenient, +because it is slightly less boilerplate. But this would leave users with a +confusing choice: when should they use `get_array_module` vs. +`np.api.something`. Also, we would have to add and maintain a whole new module, +which is considerably more expensive than merely adding a function. + +Dispatching on both types and arrays instead of only types +========================================================== + +Instead of supporting dispatch only via unique array types, we could also +support dispatch via array objects, e.g., by passing an ``arrays`` argument as +part of the ``__array_module__`` protocol. This could potentially be useful for +dispatch for arrays with metadata, such provided by Dask and Pint, but would +impose costs in terms of type safety and complexity. + +For example, a library that supports arrays on both CPUs and GPUs might decide +on which device to create a new arrays from functions like ``ones`` based on +input arguments: + +.. code:: python + + class Array: + def __array_module__(self, types, arrays): + useful_arrays = tuple(a in arrays if isinstance(a, Array)) + if not useful_arrays: + return NotImplemented + prefer_gpu = any(a.prefer_gpu for a in useful_arrays) + return ArrayModule(prefer_gpu) + + class ArrayModule: + def __init__(self, prefer_gpu): + self.prefer_gpu = prefer_gpu + + def __getattr__(self, name): + import base_module + base_func = getattr(base_module, name) + return functools.partial(base_func, prefer_gpu=self.prefer_gpu) + +This might be useful, but it's not clear if we really need it. Pint seems to +get along OK without any explicit array creation routines (favoring +multiplication by units, e.g., ``np.ones(5) * ureg.m``), and for the most part +Dask is also OK with existing ``__array_function__`` style overrides (e.g., +favoring ``np.ones_like`` over ``np.ones``). Choosing whether to place an array +on the CPU or GPU could be solved by `making array creation lazy +<https://github.com/google/jax/pull/1668>`_. + +.. _appendix-design-choices: + +Appendix: design choices for API overrides +------------------------------------------ + +There is a large range of possible design choices for overriding NumPy's API. +Here we discuss three major axes of the design decision that guided our design +for ``__array_module__``. + +Opt-in vs. opt-out for users +============================ + +The ``__array_ufunc__`` and ``__array_function__`` protocols provide a +mechanism for overriding NumPy functions *within NumPy's existing namespace*. +This means that users need to explicitly opt-out if they do not want any +overridden behavior, e.g., by casting arrays with ``np.asarray()``. + +In theory, this approach lowers the barrier for adopting these protocols in +user code and libraries, because code that uses the standard NumPy namespace is +automatically compatible. But in practice, this hasn't worked out. For example, +most well-maintained libraries that use NumPy follow the best practice of +casting all inputs with ``np.asarray()``, which they would have to explicitly +relax to use ``__array_function__``. Our experience has been that making a +library compatible with a new duck array type typically requires at least a +small amount of work to accommodate differences in the data model and operations +that can be implemented efficiently. + +These opt-out approaches also considerably complicate backwards compatibility +for libraries that adopt these protocols, because by opting in as a library +they also opt-in their users, whether they expect it or not. For winning over +libraries that have been unable to adopt ``__array_function__``, an opt-in +approach seems like a must. + +Explicit vs. implicit choice of implementation +============================================== + +Both ``__array_ufunc__`` and ``__array_function__`` have implicit control over +dispatching: the dispatched functions are determined via the appropriate +protocols in every function call. This generalizes well to handling many +different types of objects, as evidenced by its use for implementing arithmetic +operators in Python, but it has an important downside for **readability**: +it is not longer immediately evident to readers of code what happens when a +function is called, because the function's implementation could be overridden +by any of its arguments. + +The **speed** implications are: + +- When using a *duck-array type*, ``get_array_module`` means type checking only + needs to happen once inside each function that supports duck typing, whereas + with ``__array_function__`` it happens every time a NumPy function is called. + Obvious it's going to depend on the function, but if a typical duck-array + supporting function calls into other NumPy functions 3-5 times this is a factor + of 3-5x more overhead. +- When using *NumPy arrays*, ``get_array_module`` is one extra call per + function (``__array_function__`` overhead remains the same), which means a + small amount of extra overhead. + +Explicit and implicit choice of implementations are not mutually exclusive +options. Indeed, most implementations of NumPy API overrides via +``__array_function__`` that we are familiar with (namely, Dask, CuPy and +Sparse, but not Pint) also include an explicit way to use their version of +NumPy's API by importing a module directly (``dask.array``, ``cupy`` or +``sparse``, respectively). + +Local vs. non-local vs. global control +====================================== + +The final design axis is how users control the choice of API: + +- **Local control**, as exemplified by multiple dispatch and Python protocols for + arithmetic, determines which implementation to use either by checking types + or calling methods on the direct arguments of a function. +- **Non-local control** such as `np.errstate + <https://docs.scipy.org/doc/numpy/reference/generated/numpy.errstate.html>`_ + overrides behavior with global-state via function decorators or + context-managers. Control is determined hierarchically, via the inner-most + context. +- **Global control** provides a mechanism for users to set default behavior, + either via function calls or configuration files. For example, matplotlib + allows setting a global choice of plotting backend. + +Local control is generally considered a best practice for API design, because +control flow is entirely explicit, which makes it the easiest to understand. +Non-local and global control are occasionally used, but generally either due to +ignorance or a lack of better alternatives. + +In the case of duck typing for NumPy's public API, we think non-local or global +control would be mistakes, mostly because they **don't compose well**. If one +library sets/needs one set of overrides and then internally calls a routine +that expects another set of overrides, the resulting behavior may be very +surprising. Higher order functions are especially problematic, because the +context in which functions are evaluated may not be the context in which they +are defined. + +One class of override use cases where we think non-local and global control are +appropriate is for choosing a backend system that is guaranteed to have an +entirely consistent interface, such as a faster alternative implementation of +``numpy.fft`` on NumPy arrays. However, these are out of scope for the current +proposal, which is focused on duck arrays. diff --git a/doc/neps/nep-0038-SIMD-optimizations.rst b/doc/neps/nep-0038-SIMD-optimizations.rst new file mode 100644 index 000000000000..9272284474a6 --- /dev/null +++ b/doc/neps/nep-0038-SIMD-optimizations.rst @@ -0,0 +1,336 @@ +.. _NEP38: + +============================================================= +NEP 38 — Using SIMD optimization instructions for performance +============================================================= + +:Author: Sayed Adel, Matti Picus, Ralf Gommers +:Status: Accepted +:Type: Standards +:Created: 2019-11-25 +:Resolution: http://numpy-discussion.10968.n7.nabble.com/NEP-38-Universal-SIMD-intrinsics-td47854.html + + +Abstract +-------- + +While compilers are getting better at using hardware-specific routines to +optimize code, they sometimes do not produce optimal results. Also, we would +like to be able to copy binary optimized C-extension modules from one machine +to another with the same base architecture (x86, ARM, or PowerPC) but with +different capabilities without recompiling. + +We have a mechanism in the ufunc machinery to `build alternative loops`_ +indexed by CPU feature name. At import (in ``InitOperators``), the loop +function that matches the run-time CPU info `is chosen`_ from the candidates.This +NEP proposes a mechanism to build on that for many more features and +architectures. The steps proposed are to: + +- Establish a set of well-defined, architecture-agnostic, universal intrisics + which capture features available across architectures. +- Capture these universal intrisics in a set of C macros and use the macros + to build code paths for sets of features from the baseline up to the maximum + set of features available on that architecture. Offer these as a limited + number of compiled alternative code paths. +- At runtime, discover which CPU features are available, and choose from among + the possible code paths accordingly. + + +Motivation and Scope +-------------------- + +Traditionally NumPy has depended on compilers to generate optimal code +specifically for the target architecture. +However few users today compile NumPy locally for their machines. Most use the +binary packages which must provide run-time support for the lowest-common +denominator CPU architecture. Thus NumPy cannot take advantage of +more advanced features of their CPU processors, since they may not be available +on all users' systems. + +Traditionally, CPU features have been exposed through `intrinsics`_ which are +compiler-specific instructions that map directly to assembly instructions. +Recently there were discussions about the effectiveness of adding more +intrinsics (e.g., `gh-11113`_ for AVX optimizations for floats). In the past, +architecture-specific code was added to NumPy for `fast avx512 routines`_ in +various ufuncs, using the mechanism described above to choose the best loop +for the architecture. However the code is not generic and does not generalize +to other architectures. + +Recently, OpenCV moved to using `universal intrinsics`_ in the Hardware +Abstraction Layer (HAL) which provided a nice abstraction for common shared +Single Instruction Multiple Data (SIMD) constructs. This NEP proposes a similar +mechanism for NumPy. There are three stages to using the mechanism: + +- Infrastructure is provided in the code for abstract intrinsics. The ufunc + machinery will be extended using sets of these abstract intrinsics, so that + a single ufunc will be expressed as a set of loops, going from a minimal to + a maximal set of possibly available intrinsics. +- At compile time, compiler macros and CPU detection are used to turn the + abstract intrinsics into concrete intrinsic calls. Any intrinsics not + available on the platform, either because the CPU does not support them + (and so cannot be tested) or because the abstract intrinsic does not have a + parallel concrete intrinsic on the platform will not error, rather the + corresponding loop will not be produced and added to the set of + possibilities. +- At runtime, the CPU detection code will further limit the set of loops + available, and the optimal one will be chosen for the ufunc. + +The current NEP proposes only to use the runtime feature detection and optimal +loop selection mechanism for ufuncs. Future NEPS may propose other uses for the +proposed solution. + +The ufunc machinery already has the ability to select an optimal loop for +specifically available CPU features at runtime, currently used for ``avx2``, +``fma`` and ``avx512f`` loops (in the generated ``__umath_generated.c`` file); +universal intrinsics would extend the generated code to include more loop +variants. + +Usage and Impact +---------------- + +The end user will be able to get a list of intrinsics available for their +platform and compiler. Optionally, +the user may be able to specify which of the loops available at runtime will be +used, perhaps via an environment variable to enable benchmarking the impact of +the different loops. There should be no direct impact to naive end users, the +results of all the loops should be identical to within a small number (1-3?) +ULPs. On the other hand, users with more powerful machines should notice a +significant performance boost. + +Binary releases - wheels on PyPI and conda packages +``````````````````````````````````````````````````` + +The binaries released by this process will be larger since they include all +possible loops for the architecture. Some packagers may prefer to limit the +number of loops in order to limit the size of the binaries, we would hope they +would still support a wide range of families of architectures. Note this +problem already exists in the Intel MKL offering, where the binary package +includes an extensive set of alternative shared objects (DLLs) for various CPU +alternatives. + +Source builds +````````````` + +See "Detailed Description" below. A source build where the packager knows +details of the target machine could theoretically produce a smaller binary by +choosing to compile only the loops needed by the target via command line +arguments. + +How to run benchmarks to assess performance benefits +```````````````````````````````````````````````````` + +Adding more code which use intrinsics will make the code harder to maintain. +Therefore, such code should only be added if it yields a significant +performance benefit. Assessing this performance benefit can be nontrivial. +To aid with this, the implementation for this NEP will add a way to select +which instruction sets can be used at *runtime* via environment variables. +(name TBD). This ablility is critical for CI code verification. + + +Diagnostics +``````````` + +A new dictionary ``__cpu_features__`` will be available to python. The keys are +the available features, the value is a boolean whether the feature is available +or not. Various new private +C functions will be used internally to query available features. These +might be exposed via specific c-extension modules for testing. + + +Workflow for adding a new CPU architecture-specific optimization +```````````````````````````````````````````````````````````````` + +NumPy will always have a baseline C implementation for any code that may be +a candidate for SIMD vectorization. If a contributor wants to add SIMD +support for some architecture (typically the one of most interest to them), +this comment is the beginning of a tutorial on how to do so: +https://github.com/numpy/numpy/pull/13516#issuecomment-558859638 + +.. _tradeoffs: + +As of this moment, NumPy has a number of ``avx512f`` and ``avx2`` and ``fma`` +SIMD loops for many ufuncs. These would likely be the first candidates +to be ported to universal intrinsics. The expectation is that the new +implementation may cause a regression in benchmarks, but not increase the +size of the binary. If the regression is not minimal, we may choose to keep +the X86-specific code for that platform and use the universal intrisic code +for other platforms. + +Any new PRs to implement ufuncs using intrinsics will be expected to use the +universal intrinsics. If it can be demonstrated that the use of universal +intrinsics is too awkward or is not performant enough, platform specific code +may be accepted as well. In rare cases, a single-platform only PR may be +accepted, but it would have to be examined within the framework of preferring +a solution using universal intrinsics. + +The subjective criteria for accepting new loops are: + +- correctness: the new code must not decrease accuracy by more than 1-3 ULPs + even at edge points in the algorithm. +- code bloat: both source code size and especially binary size of the compiled + wheel. +- maintainability: how readable is the code +- performance: benchmarks must show a significant performance boost + +.. _new-intrinsics: + +Adding a new intrinsic +~~~~~~~~~~~~~~~~~~~~~~ + +If a contributor wants to use a platform-specific SIMD instruction that is not +yet supported as a universal intrinsic, then: + +1. It should be added as a universal intrinsic for all platforms +2. If it does not have an equivalent instruction on other platforms (e.g. + ``_mm512_mask_i32gather_ps`` in ``AVX512``), then no universal intrinsic + should be added and a platform-specific ``ufunc`` or a short helper function + should be written instead. If such a helper function is used, it must be + wrapped with the feature macros, and a reasonable non-intrinsic fallback to + be used by default. + +We expect (2) to be the exception. The contributor and maintainers should +consider whether that single-platform intrinsic is worth it compared to using +the best available universal intrinsic based implementation. + +Reuse by other projects +``````````````````````` + +It would be nice if the universal intrinsics would be available to other +libraries like SciPy or Astropy that also build ufuncs, but that is not an +explicit goal of the first implementation of this NEP. + +Backward compatibility +---------------------- + +There should be no impact on backwards compatibility. + + +Detailed description +-------------------- + +The CPU-specific are mapped to unversal intrinsics which are +similar for all x86 SIMD variants, ARM SIMD variants etc. For example, the +NumPy universal intrinsic ``npyv_load_u32`` maps to: + +* ``vld1q_u32`` for ARM based NEON +* ``_mm256_loadu_si256`` for x86 based AVX2 +* ``_mm512_loadu_si512`` for x86 based AVX-512 + +Anyone writing a SIMD loop will use the ``npyv_load_u32`` macro instead of the +architecture specific intrinsic. The code also supplies guard macros for +compilation and runtime, so that the proper loops can be chosen. + +Two new build options are available to ``runtests.py`` and ``setup.py``: +``--cpu-baseline`` and ``--cpu-dispatch``. +The absolute minimum required features to compile are defined by +``--cpu-baseline``. For instance, on ``x86_64`` this defaults to ``SSE3``. The +minimum features will be enabled if the compiler support it. The +set of additional intrinsics that can be detected and used as sets of +requirements to dispatch on are set by ``--cpu-dispatch``. For instance, on +``x86_64`` this defaults to ``[SSSE3, SSE41, POPCNT, SSE42, AVX, F16C, XOP, +FMA4, FMA3, AVX2, AVX512F, AVX512CD, AVX512_KNL, AVX512_KNM, AVX512_SKX, +AVX512_CLX, AVX512_CNL, AVX512_ICL]``. These features are all mapped to a +c-level boolean array ``npy__cpu_have``, and a c-level convenience function +``npy_cpu_have(int feature_id)`` queries this array, and the results are stored +in ``__cpu_features__`` at runtime. + +When importing the ufuncs, the available compiled loops' required features are +matched to the ones discovered. The loop with the best match is marked to be +called by the ufunc. + +Related Work +------------ + +- `Pixman`_ is the library used by Cairo and X to manipulate pixels. It uses + a technique like the one described here to fill a structure with function + pointers at runtime. These functions are similar to ufunc loops. +- `Eigen`_ is a C++ template library for linear algebra: matrices, vectors, + numerical solvers, and related algorithms. It is a higher level-abstraction + than the intrinsics discussed here. +- `xsimd`_ is a header-only C++ library for x86 and ARM that implements the + mathematical functions used in the algorithms of ``boost.SIMD``. +- `Simd`_ is a high-level image processing and machine learning library with + optimizations for different platforms. +- OpenCV used to have the one-implementation-per-architecture design, but more + recently moved to a design that is quite similar to what is proposed in this + NEP. The top-level `dispatch code`_ includes a `generic header`_ that is + `specialized at compile time`_ by the CMakefile system. +- `VOLK`_ is a GPL3 library used by gnuradio and others to abstract SIMD + intrinsics. They offer a set of high-level operations which have been + optimized for each architecture. +- The C++ Standards Committee has proposed `class templates`_ for portable + SIMD programming via vector types, and `namespaces`_ for the templates. + +Implementation +-------------- + +Current PRs: + +- `gh-13421 improve runtime detection of CPU features <https://github.com/numpy/numpy/pull/13421>`_ +- `gh-13516: enable multi-platform SIMD compiler optimizations <https://github.com/numpy/numpy/pull/13516>`_ + +The compile-time and runtime code infrastructure are supplied by the first PR. +The second adds a demonstration of use of the infrastructure for a loop. Once +the NEP is approved, more work is needed to write loops using the machnisms +provided by the NEP. + +Alternatives +------------ + +A proposed alternative in gh-13516_ is to implement loops for each CPU +architecture separately by hand, without trying to abstract common patterns in +the SIMD intrinsics (e.g., have `loops.avx512.c.src`, `loops.avx2.c.src`, +`loops.sse.c.src`, `loops.vsx.c.src`, `loops.neon.c.src`, etc.). This is more +similar to what PIXMAX does. There's a lot of duplication here though, and the +manual code duplication requires a champion who will be dedicated to +implementing and maintaining that platform's loop code. + + +Discussion +---------- + +Most of the discussion took place on the PR `gh-15228`_ to accept this NEP. +Discussion on the mailing list mentioned `VOLK`_ which was added to +the section on related work. The question of maintainability also was raised +both on the mailing list and in `gh-15228`_ and resolved as follows: + +- If contributors want to leverage a specific SIMD instruction, will they be + expected to add software implementation of this instruction for all other + architectures too? (see the `new-intrinsics`_ part of the workflow). +- On whom does the burden lie to verify the code and benchmarks for all + architectures? What happens if adding a universal ufunc in place of + architecture-specific code helps one architecture but harms performance + on another? (answered in the tradeoffs_ part of the workflow). + +References and Footnotes +------------------------ + +.. _`build alternative loops`: https://github.com/numpy/numpy/blob/v1.17.4/numpy/core/code_generators/generate_umath.py#L50 +.. _`is chosen`: https://github.com/numpy/numpy/blob/v1.17.4/numpy/core/code_generators/generate_umath.py#L1038 +.. _`gh-11113`: https://github.com/numpy/numpy/pull/11113 +.. _`gh-15228`: https://github.com/numpy/numpy/pull/15228 +.. _`gh-13516`: https://github.com/numpy/numpy/pull/13516 +.. _`fast avx512 routines`: https://github.com/numpy/numpy/pulls?q=is%3Apr+avx512+is%3Aclosed + +.. [1] Each NEP must either be explicitly labeled as placed in the public domain (see + this NEP as an example) or licensed under the `Open Publication License`_. + +.. _Open Publication License: https://www.opencontent.org/openpub/ + +.. _`xsimd`: https://xsimd.readthedocs.io/en/latest/ +.. _`Pixman`: https://gitlab.freedesktop.org/pixman +.. _`VOLK`: https://www.libvolk.org/doxygen/index.html +.. _`Eigen`: http://eigen.tuxfamily.org/index.php?title=Main_Page +.. _`Simd`: https://github.com/ermig1979/Simd +.. _`dispatch code`: https://github.com/opencv/opencv/blob/4.1.2/modules/core/src/arithm.dispatch.cpp +.. _`generic header`: https://github.com/opencv/opencv/blob/4.1.2/modules/core/src/arithm.simd.hpp +.. _`specialized at compile time`: https://github.com/opencv/opencv/blob/4.1.2/modules/core/CMakeLists.txt#L3-#L13 +.. _`intrinsics`: https://software.intel.com/en-us/cpp-compiler-developer-guide-and-reference-intrinsics +.. _`universal intrinsics`: https://docs.opencv.org/master/df/d91/group__core__hal__intrin.html +.. _`class templates`: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0214r8.pdf +.. _`namespaces`: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/n4808.pdf + +Copyright +--------- + +This document has been placed in the public domain. [1]_ diff --git a/doc/neps/nep-0040-legacy-datatype-impl.rst b/doc/neps/nep-0040-legacy-datatype-impl.rst new file mode 100644 index 000000000000..a6e74d7a0c1f --- /dev/null +++ b/doc/neps/nep-0040-legacy-datatype-impl.rst @@ -0,0 +1,649 @@ +.. _NEP40: + +================================================ +NEP 40 — Legacy datatype implementation in NumPy +================================================ + +:title: Legacy Datatype Implementation in NumPy +:Author: Sebastian Berg +:Status: Final +:Type: Informational +:Created: 2019-07-17 + + +.. note:: + + This NEP is first in a series: + + - NEP 40 (this document) explains the shortcomings of NumPy's dtype implementation. + + - :ref:`NEP 41 <NEP41>` gives an overview of our proposed replacement. + + - :ref:`NEP 42 <NEP42>` describes the new design's datatype-related APIs. + + - NEP 43 describes the new design's API for universal functions. + + + +Abstract +-------- + +As a preparation to further NumPy enhancement proposals 41, 42, and 43. This +NEP details the current status of NumPy datatypes as of NumPy 1.18. +It describes some of the technical aspects and concepts that +motivated the other proposals. +For more general information most readers should begin by reading :ref:`NEP 41 <NEP41>` +and use this document only as a reference or for additional details. + + +Detailed Description +-------------------- + +This section describes some central concepts and provides a brief overview +of the current implementation of dtypes as well as a discussion. +In many cases subsections will be split roughly to first describe the +current implementation and then follow with an "Issues and Discussion" section. + +.. _parametric-datatype-discussion: + +Parametric Datatypes +^^^^^^^^^^^^^^^^^^^^ + +Some datatypes are inherently *parametric*. All ``np.flexible`` scalar +types are attached to parametric datatypes (string, bytes, and void). +The class ``np.flexible`` for scalars is a superclass for the data types of +variable length (string, bytes, and void). +This distinction is similarly exposed by the C-Macros +``PyDataType_ISFLEXIBLE`` and ``PyTypeNum_ISFLEXIBLE``. +This flexibility generalizes to the set of values which can be represented +inside the array. +For instance, ``"S8"`` can represent longer strings than ``"S4"``. +The parametric string datatype thus also limits the values inside the array +to a subset (or subtype) of all values which can be represented by string +scalars. + +The basic numerical datatypes are not flexible (do not inherit from +``np.flexible``). ``float64``, ``float32``, etc. do have a byte order, but the described +values are unaffected by it, and it is always possible to cast them to the +native, canonical representation without any loss of information. + +The concept of flexibility can be generalized to parametric datatypes. +For example the private ``PyArray_AdaptFlexibleDType`` function also accepts the +naive datetime dtype as input to find the correct time unit. +The datetime dtype is thus parametric not in the size of its storage, +but instead in what the stored value represents. +Currently ``np.can_cast("datetime64[s]", "datetime64[ms]", casting="safe")`` +returns true, although it is unclear that this is desired or generalizes +to possible future data types such as physical units. + +Thus we have data types (mainly strings) with the properties that: + +1. Casting is not always safe (``np.can_cast("S8", "S4")``) +2. Array coercion should be able to discover the exact dtype, such as for + ``np.array(["str1", 12.34], dtype="S")`` where NumPy discovers the + resulting dtype as ``"S5"``. + (If the dtype argument is omitted the behaviour is currently ill defined [gh-15327]_.) + A form similar to ``dtype="S"`` is ``dtype="datetime64"`` which can + discover the unit: ``np.array(["2017-02"], dtype="datetime64")``. + +This notion highlights that some datatypes are more complex than the basic +numerical ones, which is evident in the complicated output type discovery +of universal functions. + + +Value Based Casting +^^^^^^^^^^^^^^^^^^^ + +Casting is typically defined between two types: +A type is considered to cast safely to a second type when the second type +can represent all values of the first without loss of information. +NumPy may inspect the actual value to decide +whether casting is safe or not. + +This is useful for example in expressions such as:: + + arr = np.array([1, 2, 3], dtype="int8") + result = arr + 5 + assert result.dtype == np.dtype("int8") + # If the value is larger, the result will change however: + result = arr + 500 + assert result.dtype == np.dtype("int16") + +In this expression, the python value (which originally has no datatype) is +represented as an ``int8`` or ``int16`` (the smallest possible data type). + +NumPy currently does this even for NumPy scalars and zero-dimensional arrays, +so that replacing ``5`` with ``np.int64(5)`` or ``np.array(5, dtype="int64")`` +in the above expression will lead to the same results, and thus ignores the +existing datatype. The same logic also applies to floating-point scalars, +which are allowed to lose precision. +The behavior is not used when both inputs are scalars, so that +``5 + np.int8(5)`` returns the default integer size (32 or 64-bit) and not +an ``np.int8``. + +While the behaviour is defined in terms of casting and exposed by +``np.result_type`` it is mainly important for universal functions +(such as ``np.add`` in the above examples). +Universal functions currently rely on safe casting semantics to decide which +loop should be used, and thus what the output datatype will be. + + +Issues and Discussion +""""""""""""""""""""" + +There appears to be some agreement that the current method is +not desirable for values that have a datatype, +but may be useful for pure python integers or floats as in the first +example. +However, any change of the datatype system and universal function dispatching +must initially fully support the current behavior. +A main difficulty is that for example the value ``156`` can be represented +by ``np.uint8`` and ``np.int16``. +The result depends on the "minimal" representation in the context of the +conversion (for ufuncs the context may depend on the loop order). + + +The Object Datatype +^^^^^^^^^^^^^^^^^^^ + +The object datatype currently serves as a generic fallback for any value +which is not otherwise representable. +However, due to not having a well-defined type, it has some issues, +for example when an array is filled with Python sequences:: + + >>> l = [1, [2]] + >>> np.array(l, dtype=np.object_) + array([1, list([2])], dtype=object) # a 1d array + + >>> a = np.empty((), dtype=np.object_) + >>> a[...] = l + ValueError: assignment to 0-d array # ??? + >>> a[()] = l + >>> a + array(list([1, [2]]), dtype=object) + +Without a well-defined type, functions such as ``isnan()`` or ``conjugate()`` +do not necessarily work, but can work for a :class:`decimal.Decimal`. +To improve this situation it seems desirable to make it easy to create +``object`` dtypes that represent a specific Python datatype and stores its object +inside the array in the form of pointer to python ``PyObject``. +Unlike most datatypes, Python objects require garbage collection. +This means that additional methods to handle references and +visit all objects must be defined. +In practice, for most use-cases it is sufficient to limit the creation of such +datatypes so that all functionality related to Python C-level references is +private to NumPy. + +Creating NumPy datatypes that match builtin Python objects also creates a few problems +that require more thoughts and discussion. +These issues do not need to solved right away: + +* NumPy currently returns *scalars* even for array input in some cases, in most + cases this works seamlessly. However, this is only true because the NumPy + scalars behave much like NumPy arrays, a feature that general Python objects + do not have. +* Seamless integration probably requires that ``np.array(scalar)`` finds the + correct DType automatically since some operations (such as indexing) return + the scalar instead of a 0D array. + This is problematic if multiple users independently decide to implement + for example a DType for ``decimal.Decimal``. + + +Current ``dtype`` Implementation +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Currently ``np.dtype`` is a Python class with its instances being the +``np.dtype(">float64")``, etc. instances. +To set the actual behaviour of these instances, a prototype instance is stored +globally and looked up based on the ``dtype.typenum``. The singleton is used +where possible. Where required it is copied and modified, for instance to change +endianness. + +Parametric datatypes (strings, void, datetime, and timedelta) must store +additional information such as string lengths, fields, or datetime units -- +new instances of these types are created instead of relying on a singleton. +All current datatypes within NumPy further support setting a metadata field +during creation which can be set to an arbitrary dictionary value, but seems +rarely used in practice (one recent and prominent user is h5py). + +Many datatype-specific functions are defined within a C structure called +:c:type:`PyArray_ArrFuncs`, which is part of each ``dtype`` instance and +has a similarity to Python's ``PyNumberMethods``. +For user-defined datatypes this structure is exposed to the user, making +ABI-compatible changes impossible. +This structure holds important information such as how to copy or cast, +and provides space for pointers to functions, such as comparing elements, +converting to bool, or sorting. +Since some of these functions are vectorized operations, operating on more than +one element, they fit the model of ufuncs and do not need to be defined on the +datatype in the future. +For example the ``np.clip`` function was previously implemented using +``PyArray_ArrFuncs`` and is now implemented as a ufunc. + +Discussion and Issues +""""""""""""""""""""" + +A further issue with the current implementation of the functions on the dtype +is that, unlike methods, +they are not passed an instance of the dtype when called. +Instead, in many cases, the array which is being operated on is passed in +and typically only used to extract the datatype again. +A future API should likely stop passing in the full array object. +Since it will be necessary to fall back to the old definitions for +backward compatibility, the array object may not be available. +However, passing a "fake" array in which mainly the datatype is defined +is probably a sufficient workaround +(see backward compatibility; alignment information may sometimes also be desired). + +Although not extensively used outside of NumPy itself, the currently +``PyArray_Descr`` is a public structure. +This is especially also true for the ``PyArray_ArrFuncs`` structure stored in +the ``f`` field. +Due to compatibility they may need to remain supported for a very long time, +with the possibility of replacing them by functions that dispatch to a newer API. + +However, in the long run access to these structures will probably have to +be deprecated. + + +NumPy Scalars and Type Hierarchy +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +As a side note to the above datatype implementation: unlike the datatypes, +the NumPy scalars currently **do** provide a type hierarchy, consisting of abstract +types such as ``np.inexact`` (see figure below). +In fact, some control flow within NumPy currently uses +``issubclass(a.dtype.type, np.inexact)``. + +.. _nep-0040_dtype-hierarchy: + +.. figure:: _static/nep-0040_dtype-hierarchy.png + + **Figure:** Hierarchy of NumPy scalar types reproduced from the reference + documentation. Some aliases such as ``np.intp`` are excluded. Datetime + and timedelta are not shown. + +NumPy scalars try to mimic zero-dimensional arrays with a fixed datatype. +For the numerical (and unicode) datatypes, they are further limited to +native byte order. + + +Current Implementation of Casting +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +One of the main features which datatypes need to support is casting between one +another using ``arr.astype(new_dtype, casting="unsafe")``, or during execution +of ufuncs with different types (such as adding integer and floating point numbers). + +Casting tables determine whether it is possible to cast from one specific type to another. +However, generic casting rules cannot handle the parametric dtypes such as strings. +The logic for parametric datatypes is defined mainly in ``PyArray_CanCastTo`` +and currently cannot be customized for user defined datatypes. + +The actual casting has two distinct parts: + +1. ``copyswap``/``copyswapn`` are defined for each dtype and can handle + byte-swapping for non-native byte orders as well as unaligned memory. +2. The generic casting code is provided by C functions which know how to + cast aligned and contiguous memory from one dtype to another + (both in native byte order). + These C-level functions can be registered to cast aligned and contiguous memory + from one dtype to another. + The function may be provided with both arrays (although the parameter + is sometimes ``NULL`` for scalars). + NumPy will ensure that these functions receive native byte order input. + The current implementation stores the functions either in a C-array + on the datatype which is cast, or in a dictionary when casting to a user + defined datatype. + +Generally NumPy will thus perform casting as chain of the three functions +``in_copyswapn -> castfunc -> out_copyswapn`` using (small) buffers between +these steps. + +The above multiple functions are wrapped into a single function (with metadata) +that handles the cast and is used for example during the buffered iteration used +by ufuncs. +This is the mechanism that is always used for user defined datatypes. +For most dtypes defined within NumPy itself, more specialized code is used to +find a function to do the actual cast +(defined by the private ``PyArray_GetDTypeTransferFunction``). +This mechanism replaces most of the above mechanism and provides much faster +casts for example when the inputs are not contiguous in memory. +However, it cannot be extended by user defined datatypes. + +Related to casting, we currently have a ``PyArray_EquivTypes`` function which +indicate that a *view* is sufficient (and thus no cast is necessary). +This function is used multiple places and should probably be part of +a redesigned casting API. + + +DType handling in Universal functions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Universal functions are implemented as instances of the ``numpy.UFunc`` class +with an ordered-list of datatype-specific +(based on the dtype typecode character, not datatype instances) implementations, +each with a signature and a function pointer. +This list of implementations can be seen with ``ufunc.types`` where +all implementations are listed with their C-style typecode signatures. +For example:: + + >>> np.add.types + [..., + 'll->l', + ..., + 'dd->d', + ...] + +Each of these signatures is associated with a single inner-loop function defined +in C, which does the actual calculation, and may be called multiple times. + +The main step in finding the correct inner-loop function is to call a +:c:type:`PyUFunc_TypeResolutionFunc` which retrieves the input dtypes from +the provided input arrays +and will determine the full type signature (including output dtype) to be executed. + +By default the ``TypeResolver`` is implemented by searching all of the implementations +listed in ``ufunc.types`` in order and stopping if all inputs can be safely +cast to fit the signature. +This means that if long (``l``) and double (``d``) arrays are added, +numpy will find that the ``'dd->d'`` definition works +(long can safely cast to double) and uses that. + +In some cases this is not desirable. For example the ``np.isnat`` universal +function has a ``TypeResolver`` which rejects integer inputs instead of +allowing them to be cast to float. +In principle, downstream projects can currently use their own non-default +``TypeResolver``, since the corresponding C-structure necessary to do this +is public. +The only project known to do this is Astropy, which is willing to switch to +a new API if NumPy were to remove the possibility to replace the TypeResolver. + +For user defined datatypes, the dispatching logic is similar, +although separately implemented and limited (see discussion below). + + +Issues and Discussion +""""""""""""""""""""" + +It is currently only possible for user defined functions to be found/resolved +if any of the inputs (or the outputs) has the user datatype, since it uses the +`OO->O` signature. +For example, given that a ufunc loop to implement ``fraction_divide(int, int) +-> Fraction`` has been implemented, +the call ``fraction_divide(4, 5)`` (with no specific output dtype) will fail +because the loop that +includes the user datatype ``Fraction`` (as output) can only be found if any of +the inputs is already a ``Fraction``. +``fraction_divide(4, 5, dtype=Fraction)`` can be made to work, but is inconvenient. + +Typically, dispatching is done by finding the first loop that matches. A match +is defined as: all inputs (and possibly outputs) can +be cast safely to the signature typechars (see also the current implementation +section). +However, in some cases safe casting is problematic and thus explicitly not +allowed. +For example the ``np.isnat`` function is currently only defined for +datetime and timedelta, +even though integers are defined to be safely castable to timedelta. +If this was not the case, calling +``np.isnat(np.array("NaT", "timedelta64").astype("int64"))`` would currently +return true, although the integer input array has no notion of "not a time". +If a universal function, such as most functions in ``scipy.special``, is only +defined for ``float32`` and ``float64`` it will currently automatically +cast a ``float16`` silently to ``float32`` (similarly for any integer input). +This ensures successful execution, but may lead to a change in the output dtype +when support for new data types is added to a ufunc. +When a ``float16`` loop is added, the output datatype will currently change +from ``float32`` to ``float16`` without a warning. + +In general the order in which loops are registered is important. +However, this is only reliable if all loops are added when the ufunc is first defined. +Additional loops added when a new user datatypes is imported +must not be sensitive to the order in which imports occur. + +There are two main approaches to better define the type resolution for user +defined types: + +1. Allow for user dtypes to directly influence the loop selection. + For example they may provide a function which return/select a loop + when there is no exact matching loop available. +2. Define a total ordering of all implementations/loops, probably based on + "safe casting" semantics, or semantics similar to that. + +While option 2 may be less complex to reason about it remains to be seen +whether it is sufficient for all (or most) use cases. + + +Adjustment of Parametric output DTypes in UFuncs +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A second step necessary for parametric dtypes is currently performed within +the ``TypeResolver``: +the datetime and timedelta datatypes have to decide on the correct parameter +for the operation and output array. +This step also needs to double check that all casts can be performed safely, +which by default means that they are "same kind" casts. + +Issues and Discussion +""""""""""""""""""""" + +Fixing the correct output dtype is currently part of the type resolution. +However, it is a distinct step and should probably be handled as such after +the actual type/loop resolution has occurred. + +As such this step may move from the dispatching step (described above) to +the implementation-specific code described below. + + +DType-specific Implementation of the UFunc +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Once the correct implementation/loop is found, UFuncs currently call +a single *inner-loop function* which is written in C. +This may be called multiple times to do the full calculation and it has +little or no information about the current context. It also has a void +return value. + +Issues and Discussion +""""""""""""""""""""" + +Parametric datatypes may require passing +additional information to the inner-loop function to decide how to interpret +the data. +This is the reason why currently no universal functions for ``string`` dtypes +exist (although technically possible within NumPy itself). +Note that it is currently possible to pass in the input array objects +(which in turn hold the datatypes when no casting is necessary). +However, the full array information should not be required and currently the +arrays are passed in before any casting occurs. +The feature is unused within NumPy and no known user exists. + +Another issue is the error reporting from within the inner-loop function. +There exist currently two ways to do this: + +1. by setting a Python exception +2. using the CPU floating point error flags. + +Both of these are checked before returning to the user. +However, many integer functions currently can set neither of these errors, +so that checking the floating point error flags is unnecessary overhead. +On the other hand, there is no way to stop the iteration or pass out error +information which does not use the floating point flags or requires to hold +the Python global interpreter lock (GIL). + +It seems necessary to provide more control to authors of inner loop functions. +This means allowing users to pass in and out information from the inner-loop +function more easily, while *not* providing the input array objects. +Most likely this will involve: + +* Allowing the execution of additional code before the first and after + the last inner-loop call. +* Returning an integer value from the inner-loop to allow stopping the + iteration early and possibly propagate error information. +* Possibly, to allow specialized inner-loop selections. For example currently + ``matmul`` and many reductions will execute optimized code for certain inputs. + It may make sense to allow selecting such optimized loops beforehand. + Allowing this may also help to bring casting (which uses this heavily) and + ufunc implementations closer. + +The issues surrounding the inner-loop functions have been discussed in some +detail in the github issue gh-12518_ . + +Reductions use an "identity" value. +This is currently defined once per ufunc, regardless of the ufunc dtype signature. +For example ``0`` is used for ``sum``, or ``math.inf`` for ``min``. +This works well for numerical datatypes, but is not always appropriate for other dtypes. +In general it should be possible to provide a dtype-specific identity to the +ufunc reduction. + + +Datatype Discovery during Array Coercion +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When calling ``np.array(...)`` to coerce a general Python object to a NumPy array, +all objects need to be inspected to find the correct dtype. +The input to ``np.array()`` are potentially nested Python sequences which hold +the final elements as generic Python objects. +NumPy has to unpack all the nested sequences and then inspect the elements. +The final datatype is found by iterating over all elements which will end up +in the array and: + +1. discovering the dtype of the single element: + + * from array (or array like) or NumPy scalar using ``element.dtype`` + * using ``isinstance(..., float)`` for known Python types + (note that these rules mean that subclasses are *currently* valid). + * special rule for void datatypes to coerce tuples. + +2. Promoting the current dtype with the next elements dtype using + ``np.promote_types``. +3. If strings are found, the whole process is restarted (see also [gh-15327]_), + in a similar manner as if ``dtype="S"`` was given (see below). + +If ``dtype=...`` is given, this dtype is used unmodified, unless +it is an unspecific *parametric dtype instance* which means "S0", "V0", "U0", +"datetime64", and "timdelta64". +These are thus flexible datatypes without length 0 – considered to be unsized – +and datetimes or timedelta without a unit attached ("generic unit"). + +In future DType class hierarchy, these may be represented by the class rather +than a special instance, since these special instances should not normally be +attached to an array. + +If such a *parametric dtype instance* is provided for example using ``dtype="S"`` +``PyArray_AdaptFlexibleDType`` is called and effectively inspects all values +using DType specific logic. +That is: + +* Strings will use ``str(element)`` to find the length of most elements +* Datetime64 is capable of coercing from strings and guessing the correct unit. + + +Discussion and Issues +""""""""""""""""""""" + +It seems probable that during normal discovery, the ``isinstance`` should rather +be strict ``type(element) is desired_type`` checks. +Further, the current ``AdaptFlexibleDType`` logic should be made available to +user DTypes and not be a secondary step, but instead replace, or be part of, +the normal discovery. + + + +Related Issues +-------------- + +``np.save`` currently translates all user-defined dtypes to void dtypes. +This means they cannot be stored using the ``npy`` format. +This is not an issue for the python pickle protocol, although it may require +some thought if we wish to ensure that such files can be loaded securely +without the possibility of executing malicious code +(i.e. without the ``allow_pickle=True`` keyword argument). + +The additional existence of masked arrays and especially masked datatypes +within Pandas has interesting implications for interoperability. +Since mask information is often stored separately, its handling requires +support by the container (array) object. +NumPy itself does not provide such support, and is not expected to add it +in the foreseeable future. +However, if such additions to the datatypes within NumPy would improve +interoperability they could be considered even if +they are not used by NumPy itself. + + +Related Work +------------ + +* Julia types are an interesting blueprint for a type hierarchy, and define + abstract and concrete types [julia-types]_. + +* In Julia promotion can occur based on abstract types. If a promoter is + defined, it will cast the inputs and then Julia can then retry to find + an implementation with the new values [julia-promotion]_. + +* ``xnd-project`` (https://github.com/xnd-project) with ndtypes and gumath + + * The ``xnd-project`` is similar to NumPy and defines data types as well + as the possibility to extend them. A major difference is that it does + not use promotion/casting within the ufuncs, but instead requires explicit + definition of ``int32 + float64 -> float64`` loops. + + + +Discussion +---------- + +There have been many discussions about the current state and what a future +datatype system may look like. +The full list of these discussion is long and some are lost to time, +the following provides a subset for more recent ones: + +* Draft NEP by Stephan Hoyer after a developer meeting (was updated on the next developer meeting) https://hackmd.io/6YmDt_PgSVORRNRxHyPaNQ + +* List of related documents gathered previously here + https://hackmd.io/UVOtgj1wRZSsoNQCjkhq1g (TODO: Reduce to the most important + ones): + + * https://github.com/numpy/numpy/pull/12630 + Matti Picus draft NEP, discusses the technical side of subclassing more from + the side of ``ArrFunctions`` + + * https://hackmd.io/ok21UoAQQmOtSVk6keaJhw and https://hackmd.io/s/ryTFaOPHE + (2019-04-30) Proposals for subclassing implementation approach. + + * Discussion about the calling convention of ufuncs and need for more + powerful UFuncs: https://github.com/numpy/numpy/issues/12518 + + * 2018-11-30 developer meeting notes: + https://github.com/BIDS-numpy/docs/blob/master/meetings/2018-11-30-dev-meeting.md + and subsequent draft for an NEP: https://hackmd.io/6YmDt_PgSVORRNRxHyPaNQ + + BIDS Meeting on November 30, 2018 and document by Stephan Hoyer about + what numpy should provide and thoughts of how to get there. Meeting with + Eric Wieser, Matti Picus, Charles Harris, Tyler Reddy, Stéfan van der + Walt, and Travis Oliphant. + + * SciPy 2018 brainstorming session with summaries of use cases: + https://github.com/numpy/numpy/wiki/Dtype-Brainstorming + + Also lists some requirements and some ideas on implementations + + + +References +---------- + +.. _gh-12518: https://github.com/numpy/numpy/issues/12518 +.. [gh-15327] https://github.com/numpy/numpy/issues/12518 + +.. [julia-types] https://docs.julialang.org/en/v1/manual/types/index.html#Abstract-Types-1 + +.. [julia-promotion] https://docs.julialang.org/en/v1/manual/conversion-and-promotion/ + + + +Copyright +--------- + +This document has been placed in the public domain. diff --git a/doc/neps/nep-0041-improved-dtype-support.rst b/doc/neps/nep-0041-improved-dtype-support.rst new file mode 100644 index 000000000000..2fb907073b7f --- /dev/null +++ b/doc/neps/nep-0041-improved-dtype-support.rst @@ -0,0 +1,806 @@ +.. _NEP41: + +================================================= +NEP 41 — First step towards a new datatype system +================================================= + +:title: First step towards a new Datatype System +:Author: Sebastian Berg +:Author: Stéfan van der Walt +:Author: Matti Picus +:Status: Accepted +:Type: Standard Track +:Created: 2020-02-03 +:Resolution: https://mail.python.org/pipermail/numpy-discussion/2020-April/080573.html and https://mail.python.org/pipermail/numpy-discussion/2020-March/080495.html + +.. note:: + + This NEP is second in a series: + + - :ref:`NEP 40 <NEP40>` explains the shortcomings of NumPy's dtype implementation. + + - NEP 41 (this document) gives an overview of our proposed replacement. + + - :ref:`NEP 42 <NEP42>` describes the new design's datatype-related APIs. + + - NEP 43 describes the new design's API for universal functions. + + +Abstract +-------- + +:ref:`Datatypes <arrays.dtypes>` in NumPy describe how to interpret each +element in arrays. NumPy provides ``int``, ``float``, and ``complex`` numerical +types, as well as string, datetime, and structured datatype capabilities. +The growing Python community, however, has need for more diverse datatypes. +Examples are datatypes with unit information attached (such as meters) or +categorical datatypes (fixed set of possible values). +However, the current NumPy datatype API is too limited to allow the creation +of these. + +This NEP is the first step to enable such growth; it will lead to +a simpler development path for new datatypes. +In the long run the new datatype system will also support the creation +of datatypes directly from Python rather than C. +Refactoring the datatype API will improve maintainability and facilitate +development of both user-defined external datatypes, +as well as new features for existing datatypes internal to NumPy. + + +Motivation and Scope +-------------------- + +.. seealso:: + + The user impact section includes examples of what kind of new datatypes + will be enabled by the proposed changes in the long run. + It may thus help to read these section out of order. + +Motivation +^^^^^^^^^^ + +One of the main issues with the current API is the definition of typical +functions such as addition and multiplication for parametric datatypes +(see also :ref:`NEP 40 <NEP40>`) +which require additional steps to determine the output type. +For example when adding two strings of length 4, the result is a string +of length 8, which is different from the input. +Similarly, a datatype which embeds a physical unit must calculate the new unit +information: dividing a distance by a time results in a speed. +A related difficulty is that the :ref:`current casting rules <ufuncs.casting>` +-- the conversion between different datatypes -- +cannot describe casting for such parametric datatypes implemented outside of NumPy. + +This additional functionality for supporting parametric datatypes introduces +increased complexity within NumPy itself, +and furthermore is not available to external user-defined datatypes. +In general the concerns of different datatypes are not well well-encapsulated. +This burden is exacerbated by the exposure of internal C structures, +limiting the addition of new fields +(for example to support new sorting methods [new_sort]_). + +Currently there are many factors which limit the creation of new user-defined +datatypes: + +* Creating casting rules for parametric user-defined dtypes is either impossible + or so complex that it has never been attempted. +* Type promotion, e.g. the operation deciding that adding float and integer + values should return a float value, is very valuable for numeric datatypes + but is limited in scope for user-defined and especially parametric datatypes. +* Much of the logic (e.g. promotion) is written in single functions + instead of being split as methods on the datatype itself. +* In the current design datatypes cannot have methods that do not generalize + to other datatypes. For example a unit datatype cannot have a ``.to_si()`` method to + easily find the datatype which would represent the same values in SI units. + +The large need to solve these issues has driven the scientific community +to create work-arounds in multiple projects implementing physical units as an +array-like class instead of a datatype, which would generalize better across +multiple array-likes (Dask, pandas, etc.). +Already, Pandas has made a push into the same direction with its +extension arrays [pandas_extension_arrays]_ and undoubtedly +the community would be best served if such new features could be common +between NumPy, Pandas, and other projects. + +Scope +^^^^^ + +The proposed refactoring of the datatype system is a large undertaking and +thus is proposed to be split into various phases, roughly: + +* Phase I: Restructure and extend the datatype infrastructure (This NEP 41) +* Phase II: Incrementally define or rework API (Detailed largely in NEPs 42/43) +* Phase III: Growth of NumPy and Scientific Python Ecosystem capabilities. + +For a more detailed accounting of the various phases, see +"Plan to Approach the Full Refactor" in the Implementation section below. +This NEP proposes to move ahead with the necessary creation of new dtype +subclasses (Phase I), +and start working on implementing current functionality. +Within the context of this NEP all development will be fully private API or +use preliminary underscored names which must be changed in the future. +Most of the internal and public API choices are part of a second Phase +and will be discussed in more detail in the following NEPs 42 and 43. +The initial implementation of this NEP will have little or no effect on users, +but provides the necessary ground work for incrementally addressing the +full rework. + +The implementation of this NEP and the following, implied large rework of how +datatypes are defined in NumPy is expected to create small incompatibilities +(see backward compatibility section). +However, a transition requiring large code adaption is not anticipated and not +within scope. + +Specifically, this NEP makes the following design choices which are discussed +in more details in the detailed description section: + +1. Each datatype will be an instance of a subclass of ``np.dtype``, with most of the + datatype-specific logic being implemented + as special methods on the class. In the C-API, these correspond to specific + slots. In short, for ``f = np.dtype("f8")``, ``isinstance(f, np.dtype)`` will remain true, + but ``type(f)`` will be a subclass of ``np.dtype`` rather than just ``np.dtype`` itself. + The ``PyArray_ArrFuncs`` which are currently stored as a pointer on the instance (as ``PyArray_Descr->f``), + should instead be stored on the class as typically done in Python. + In the future these may correspond to python side dunder methods. + Storage information such as itemsize and byteorder can differ between + different dtype instances (e.g. "S3" vs. "S8") and will remain part of the instance. + This means that in the long run the current lowlevel access to dtype methods + will be removed (see ``PyArray_ArrFuncs`` in + :ref:`NEP 40 <NEP40>`). + +2. The current NumPy scalars will *not* change, they will not be instances of + datatypes. This will also be true for new datatypes, scalars will not be + instances of a dtype (although ``isinstance(scalar, dtype)`` may be made + to return ``True`` when appropriate). + +Detailed technical decisions to follow in NEP 42. + +Further, the public API will be designed in a way that is extensible in the future: + +3. All new C-API functions provided to the user will hide implementation details + as much as possible. The public API should be an identical, but limited, + version of the C-API used for the internal NumPy datatypes. + +The datatype system may be targeted to work with NumPy arrays, +for example by providing strided-loops, but should avoid direct +interactions with the array-object (typically `np.ndarray` instances). +Instead, the design principle will be that the array-object is a consumer +of the datatype. +While only a guiding principle, this may allow splitting the datatype system +or even the NumPy datatypes into their own project which NumPy depends on. + +The changes to the datatype system in Phase II must include a large refactor of the +UFunc machinery, which will be further defined in NEP 43: + +4. To enable all of the desired functionality for new user-defined datatypes, + the UFunc machinery will be changed to replace the current dispatching + and type resolution system. + The old system should be *mostly* supported as a legacy version for some time. + +Additionally, as a general design principle, the addition of new user-defined +datatypes will *not* change the behaviour of programs. +For example ``common_dtype(a, b)`` must not be ``c`` unless ``a`` or ``b`` know +that ``c`` exists. + + +User Impact +----------- + +The current ecosystem has very few user-defined datatypes using NumPy, the +two most prominent being: ``rational`` and ``quaternion``. +These represent fairly simple datatypes which are not strongly impacted +by the current limitations. +However, we have identified a need for datatypes such as: + +* bfloat16, used in deep learning +* categorical types +* physical units (such as meters) +* datatypes for tracing/automatic differentiation +* high, fixed precision math +* specialized integer types such as int2, int24 +* new, better datetime representations +* extending e.g. integer dtypes to have a sentinel NA value +* geometrical objects [pygeos]_ + +Some of these are partially solved; for example unit capability is provided +in ``astropy.units``, ``unyt``, or ``pint``, as `numpy.ndarray` subclasses. +Most of these datatypes, however, simply cannot be reasonably defined +right now. +An advantage of having such datatypes in NumPy is that they should integrate +seamlessly with other array or array-like packages such as Pandas, +``xarray`` [xarray_dtype_issue]_, or ``Dask``. + +The long term user impact of implementing this NEP will be to allow both +the growth of the whole ecosystem by having such new datatypes, as well as +consolidating implementation of such datatypes within NumPy to achieve +better interoperability. + + +Examples +^^^^^^^^ + +The following examples represent future user-defined datatypes we wish to enable. +These datatypes are not part the NEP and choices (e.g. choice of casting rules) +are possibilities we wish to enable and do not represent recommendations. + +Simple Numerical Types +"""""""""""""""""""""" + +Mainly used where memory is a consideration, lower-precision numeric types +such as `bfloat16 <https://en.wikipedia.org/wiki/Bfloat16_floating-point_format>`_ +are common in other computational frameworks. +For these types the definitions of things such as ``np.common_type`` and +``np.can_cast`` are some of the most important interfaces. Once they +support ``np.common_type``, it is (for the most part) possible to find +the correct ufunc loop to call, since most ufuncs -- such as add -- effectively +only require ``np.result_type``:: + + >>> np.add(arr1, arr2).dtype == np.result_type(arr1, arr2) + +and `~numpy.result_type` is largely identical to `~numpy.common_type`. + + +Fixed, high precision math +"""""""""""""""""""""""""" + +Allowing arbitrary precision or higher precision math is important in +simulations. For instance ``mpmath`` defines a precision:: + + >>> import mpmath as mp + >>> print(mp.dps) # the current (default) precision + 15 + +NumPy should be able to construct a native, memory-efficient array from +a list of ``mpmath.mpf`` floating point objects:: + + >>> arr_15_dps = np.array(mp.arange(3)) # (mp.arange returns a list) + >>> print(arr_15_dps) # Must find the correct precision from the objects: + array(['0.0', '1.0', '2.0'], dtype=mpf[dps=15]) + +We should also be able to specify the desired precision when +creating the datatype for an array. Here, we use ``np.dtype[mp.mpf]`` +to find the DType class (the notation is not part of this NEP), +which is then instantiated with the desired parameter. +This could also be written as ``MpfDType`` class:: + + >>> arr_100_dps = np.array([1, 2, 3], dtype=np.dtype[mp.mpf](dps=100)) + >>> print(arr_15_dps + arr_100_dps) + array(['0.0', '2.0', '4.0'], dtype=mpf[dps=100]) + +The ``mpf`` datatype can decide that the result of the operation should be the +higher precision one of the two, so uses a precision of 100. +Furthermore, we should be able to define casting, for example as in:: + + >>> np.can_cast(arr_15_dps.dtype, arr_100_dps.dtype, casting="safe") + True + >>> np.can_cast(arr_100_dps.dtype, arr_15_dps.dtype, casting="safe") + False # loses precision + >>> np.can_cast(arr_100_dps.dtype, arr_100_dps.dtype, casting="same_kind") + True + +Casting from float is a probably always at least a ``same_kind`` cast, but +in general, it is not safe:: + + >>> np.can_cast(np.float64, np.dtype[mp.mpf](dps=4), casting="safe") + False + +since a float64 has a higher precision than the ``mpf`` datatype with +``dps=4``. + +Alternatively, we can say that:: + + >>> np.common_type(np.dtype[mp.mpf](dps=5), np.dtype[mp.mpf](dps=10)) + np.dtype[mp.mpf](dps=10) + +And possibly even:: + + >>> np.common_type(np.dtype[mp.mpf](dps=5), np.float64) + np.dtype[mp.mpf](dps=16) # equivalent precision to float64 (I believe) + +since ``np.float64`` can be cast to a ``np.dtype[mp.mpf](dps=16)`` safely. + + +Categoricals +"""""""""""" + +Categoricals are interesting in that they can have fixed, predefined values, +or can be dynamic with the ability to modify categories when necessary. +The fixed categories (defined ahead of time) is the most straight forward +categorical definition. +Categoricals are *hard*, since there are many strategies to implement them, +suggesting NumPy should only provide the scaffolding for user-defined +categorical types. For instance:: + + >>> cat = Categorical(["eggs", "spam", "toast"]) + >>> breakfast = array(["eggs", "spam", "eggs", "toast"], dtype=cat) + +could store the array very efficiently, since it knows that there are only 3 +categories. +Since a categorical in this sense knows almost nothing about the data stored +in it, few operations makes, sense, although equality does: + + >>> breakfast2 = array(["eggs", "eggs", "eggs", "eggs"], dtype=cat) + >>> breakfast == breakfast2 + array[True, False, True, False]) + +The categorical datatype could work like a dictionary: no two +items names can be equal (checked on dtype creation), so that the equality +operation above can be performed very efficiently. +If the values define an order, the category labels (internally integers) could +be ordered the same way to allow efficient sorting and comparison. + +Whether or not casting is defined from one categorical with less to one with +strictly more values defined, is something that the Categorical datatype would +need to decide. Both options should be available. + + +Unit on the Datatype +"""""""""""""""""""" + +There are different ways to define Units, depending on how the internal +machinery would be organized, one way is to have a single Unit datatype +for every existing numerical type. +This will be written as ``Unit[float64]``, the unit itself is part of the +DType instance ``Unit[float64]("m")`` is a ``float64`` with meters attached:: + + >>> from astropy import units + >>> meters = np.array([1, 2, 3], dtype=np.float64) * units.m # meters + >>> print(meters) + array([1.0, 2.0, 3.0], dtype=Unit[float64]("m")) + +Note that units are a bit tricky. It is debatable, whether:: + + >>> np.array([1.0, 2.0, 3.0], dtype=Unit[float64]("m")) + +should be valid syntax (coercing the float scalars without a unit to meters). +Once the array is created, math will work without any issue:: + + >>> meters / (2 * unit.seconds) + array([0.5, 1.0, 1.5], dtype=Unit[float64]("m/s")) + +Casting is not valid from one unit to the other, but can be valid between +different scales of the same dimensionality (although this may be "unsafe"):: + + >>> meters.astype(Unit[float64]("s")) + TypeError: Cannot cast meters to seconds. + >>> meters.astype(Unit[float64]("km")) + >>> # Convert to centimeter-gram-second (cgs) units: + >>> meters.astype(meters.dtype.to_cgs()) + +The above notation is somewhat clumsy. Functions +could be used instead to convert between units. +There may be ways to make these more convenient, but those must be left +for future discussions:: + + >>> units.convert(meters, "km") + >>> units.to_cgs(meters) + +There are some open questions. For example, whether additional methods +on the array object could exist to simplify some of the notions, and how these +would percolate from the datatype to the ``ndarray``. + +The interaction with other scalars would likely be defined through:: + + >>> np.common_type(np.float64, Unit) + Unit[np.float64](dimensionless) + +Ufunc output datatype determination can be more involved than for simple +numerical dtypes since there is no "universal" output type:: + + >>> np.multiply(meters, seconds).dtype != np.result_type(meters, seconds) + +In fact ``np.result_type(meters, seconds)`` must error without context +of the operation being done. +This example highlights how the specific ufunc loop +(loop with known, specific DTypes as inputs), has to be able to make +certain decisions before the actual calculation can start. + + + +Implementation +-------------- + +Plan to Approach the Full Refactor +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +To address these issues in NumPy and enable new datatypes, +multiple development stages are required: + +* Phase I: Restructure and extend the datatype infrastructure (This NEP) + + * Organize Datatypes like normal Python classes [`PR 15508`]_ + +* Phase II: Incrementally define or rework API + + * Incrementally define all necessary functionality through methods and + properties on the DType (NEP 42): + + * The properties of the class hierarchy and DType class itself, + including methods not covered by the following, most central, points. + * The functionality that will support dtype casting using ``arr.astype()`` + and casting related operations such as ``np.common_type``. + * The implementation of item access and storage, and the way shape and + dtype are determined when creating an array with ``np.array()`` + * Create a public C-API to define new DTypes. + + * Restructure how universal functions work (NEP 43), to allow extending + a `~numpy.ufunc` such as ``np.add`` for user-defined datatypes + such as Units: + + * Refactor how the low-level C functions are organized to make it + extensible and flexible enough for complicated DTypes such as Units. + * Implement registration and efficient lookup for these low-level C + functions as defined by the user. + * Define how promotion will be used to implement behaviour when casting + is required. For example ``np.float64(3) + np.int32(3)`` promotes the + ``int32`` to a ``float64``. + +* Phase III: Growth of NumPy and Scientific Python Ecosystem capabilities: + + * Cleanup of legacy behaviour where it is considered buggy or undesirable. + * Provide a path to define new datatypes from Python. + * Assist the community in creating types such as Units or Categoricals + * Allow strings to be used in functions such as ``np.equal`` or ``np.add``. + * Remove legacy code paths within NumPy to improve long term maintainability + +This document serves as a basis for phase I and provides the vision and +motivation for the full project. +Phase I does not introduce any new user-facing features, +but is concerned with the necessary conceptual cleanup of the current datatype system. +It provides a more "pythonic" datatype Python type object, with a clear class hierarchy. + +The second phase is the incremental creation of all APIs necessary to define +fully featured datatypes and reorganization of the NumPy datatype system. +This phase will thus be primarily concerned with defining an, +initially preliminary, stable public API. + +Some of the benefits of a large refactor may only become evident after the full +deprecation of the current legacy implementation (i.e. larger code removals). +However, these steps are necessary for improvements to many parts of the +core NumPy API, and are expected to make the implementation generally +easier to understand. + +The following figure illustrates the proposed design at a high level, +and roughly delineates the components of the overall design. +Note that this NEP only regards Phase I (shaded area), +the rest encompasses Phase II and the design choices are up for discussion, +however, it highlights that the DType datatype class is the central, necessary +concept: + +.. image:: _static/nep-0041-mindmap.svg + + +First steps directly related to this NEP +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The required changes necessary to NumPy are large and touch many areas +of the code base +but many of these changes can be addressed incrementally. + +To enable an incremental approach we will start by creating a C defined +``PyArray_DTypeMeta`` class with its instances being the ``DType`` classes, +subclasses of ``np.dtype``. +This is necessary to add the ability of storing custom slots on the DType in C. +This ``DTypeMeta`` will be implemented first to then enable incremental +restructuring of current code. + +The addition of ``DType`` will then enable addressing other changes +incrementally, some of which may begin before the settling the full internal +API: + +1. New machinery for array coercion, with the goal of enabling user DTypes + with appropriate class methods. +2. The replacement or wrapping of the current casting machinery. +3. Incremental redefinition of the current ``PyArray_ArrFuncs`` slots into + DType method slots. + +At this point, no or only very limited new public API will be added and +the internal API is considered to be in flux. +Any new public API may be set up give warnings and will have leading underscores +to indicate that it is not finalized and can be changed without warning. + + +Backward compatibility +---------------------- + +While the actual backward compatibility impact of implementing Phase I and II +are not yet fully clear, we anticipate, and accept the following changes: + +* **Python API**: + + * ``type(np.dtype("f8"))`` will be a subclass of ``np.dtype``, while right + now ``type(np.dtype("f8")) is np.dtype``. + Code should use ``isinstance`` checks, and in very rare cases may have to + be adapted to use it. + +* **C-API**: + + * In old versions of NumPy ``PyArray_DescrCheck`` is a macro which uses + ``type(dtype) is np.dtype``. When compiling against an old NumPy version, + the macro may have to be replaced with the corresponding + ``PyObject_IsInstance`` call. (If this is a problem, we could backport + fixing the macro) + + * The UFunc machinery changes will break *limited* parts of the current + implementation. Replacing e.g. the default ``TypeResolver`` is expected + to remain supported for a time, although optimized masked inner loop iteration + (which is not even used *within* NumPy) will no longer be supported. + + * All functions currently defined on the dtypes, such as + ``PyArray_Descr->f->nonzero``, will be defined and accessed differently. + This means that in the long run lowlevel access code will + have to be changed to use the new API. Such changes are expected to be + necessary in very few project. + +* **dtype implementors (C-API)**: + + * The array which is currently provided to some functions (such as cast functions), + will no longer be provided. + For example ``PyArray_Descr->f->nonzero`` or ``PyArray_Descr->f->copyswapn``, + may instead receive a dummy array object with only some fields (mainly the + dtype), being valid. + At least in some code paths, a similar mechanism is already used. + + * The ``scalarkind`` slot and registration of scalar casting will be + removed/ignored without replacement. + It currently allows partial value-based casting. + The ``PyArray_ScalarKind`` function will continue to work for builtin types, + but will not be used internally and be deprecated. + + * Currently user dtypes are defined as instances of ``np.dtype``. + The creation works by the user providing a prototype instance. + NumPy will need to modify at least the type during registration. + This has no effect for either ``rational`` or ``quaternion`` and mutation + of the structure seems unlikely after registration. + +Since there is a fairly large API surface concerning datatypes, further changes +or the limitation certain function to currently existing datatypes is +likely to occur. +For example functions which use the type number as input +should be replaced with functions taking DType classes instead. +Although public, large parts of this C-API seem to be used rarely, +possibly never, by downstream projects. + + + +Detailed Description +-------------------- + +This section details the design decisions covered by this NEP. +The subsections correspond to the list of design choices presented +in the Scope section. + +Datatypes as Python Classes (1) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The current NumPy datatypes are not full scale python classes. +They are instead (prototype) instances of a single ``np.dtype`` class. +Changing this means that any special handling, e.g. for ``datetime`` +can be moved to the Datetime DType class instead, away from monolithic general +code (e.g. current ``PyArray_AdjustFlexibleDType``). + +The main consequence of this change with respect to the API is that +special methods move from the dtype instances to methods on the new DType class. +This is the typical design pattern used in Python. +Organizing these methods and information in a more Pythonic way provides a +solid foundation for refining and extending the API in the future. +The current API cannot be extended due to how it is exposed publicly. +This means for example that the methods currently stored in ``PyArray_ArrFuncs`` +on each datatype (see :ref:`NEP 40 <NEP40>`) +will be defined differently in the future and +deprecated in the long run. + +The most prominent visible side effect of this will be that +``type(np.dtype(np.float64))`` will not be ``np.dtype`` anymore. +Instead it will be a subclass of ``np.dtype`` meaning that +``isinstance(np.dtype(np.float64), np.dtype)`` will remain true. +This will also add the ability to use ``isinstance(dtype, np.dtype[float64])`` +thus removing the need to use ``dtype.kind``, ``dtype.char``, or ``dtype.type`` +to do this check. + +With the design decision of DTypes as full-scale Python classes, +the question of subclassing arises. +Inheritance, however, appears problematic and a complexity best avoided +(at least initially) for container datatypes. +Further, subclasses may be more interesting for interoperability for +example with GPU backends (CuPy) storing additional methods related to the +GPU rather than as a mechanism to define new datatypes. +A class hierarchy does provides value, and one can be achieved by +allowing the creation of *abstract* datatypes. +An example for an abstract datatype would be the datatype equivalent of +``np.floating``, representing any floating point number. +These can serve the same purpose as Python's abstract base classes. + +This NEP chooses to duplicate the scalar hierarchy fully or in part. +The main reason is to uncouple the implementation of the DType and scalar. +To add a DType to NumPy, in theory the scalar will not need to be +modified or know about NumPy. Also note that the categorical DType as +currently implemented in pandas does not have a scalar correspondence +making it less straight forward to rely on scalars to implement behaviour. +While DType and Scalar describe the same concept/type (e.g. an `int64`), +it seems practical to split out the information and functionality necessary +for numpy into the DType class. + +The dtype instances provide parameters and storage options +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +From a computer science point of view a type defines the *value space* +(all possible values its instances can take) and their *behaviour*. +As proposed in this NEP, the DType class defines value space and behaviour. +The ``dtype`` instance can be seen as part of the value, so that the typical +Python ``instance`` corresponds to ``dtype + element`` (where *element* is the +data stored in the array). +An alternative view would be to define value space and behaviour on the +``dtype`` instances directly. +These two options are presented in the following figure and compared to +similar Python implementation patterns: + +.. image:: _static/nep-0041-type-sketch-no-fonts.svg + +The difference is in how parameters, such as string length or the datetime +units (``ms``, ``ns``, ...), and storage options, such as byte-order, are handled. +When implementing a Python (scalar) ``type`` parameters, for example the datetimes +unit, will be stored in the instance. +This is the design NEP 42 tries to mimic, however, the parameters are now part +of the dtype instance, meaning that part of the data stored in the instance +is shared by all array elements. +As mentioned previously, this means that the Python ``instance`` corresponds +to the ``dtype + element`` stored in a NumPy array. + +An more advanced approach in Python is to use a class factory and an abstract +base class (ABC). +This allows moving the parameter into the dynamically created ``type`` and +behaviour implementation may be specific to those parameters. +An alternative approach might use this model and implemented behaviour +directly on the ``dtype`` instance. + +We believe that the version as proposed here is easier to work with and understand. +Python class factories are not commonly used and NumPy does not use code +specialized for dtype parameters or byte-orders. +Making such specialization easier to implement such specialization does not +seem to be a priority. +One result of this choice is that some DTypes may only have a singleton instance +if they have no parameters or storage variation. +However, all of the NumPy dtypes require dynamically created instances due +to allowing metadata to be attached. + + +Scalars should not be instances of the datatypes (2) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +For simple datatypes such as ``float64`` (see also below), it seems +tempting that the instance of a ``np.dtype("float64")`` can be the scalar. +This idea may be even more appealing due to the fact that scalars, +rather than datatypes, currently define a useful type hierarchy. + +However, we have specifically decided against this for a number of reasons. +First, the new datatypes described herein would be instances of DType classes. +Making these instances themselves classes, while possible, adds additional +complexity that users need to understand. +It would also mean that scalars must have storage information (such as byteorder) +which is generally unnecessary and currently is not used. +Second, while the simple NumPy scalars such as ``float64`` may be such instances, +it should be possible to create datatypes for Python objects without enforcing +NumPy as a dependency. +However, Python objects that do not depend on NumPy cannot be instances of a NumPy DType. +Third, there is a mismatch between the methods and attributes which are useful +for scalars and datatypes. For instance ``to_float()`` makes sense for a scalar +but not for a datatype and ``newbyteorder`` is not useful on a scalar (or has +a different meaning). + +Overall, it seem rather than reducing the complexity, i.e. by merging +the two distinct type hierarchies, making scalars instances of DTypes would +increase the complexity of both the design and implementation. + +A possible future path may be to instead simplify the current NumPy scalars to +be much simpler objects which largely derive their behaviour from the datatypes. + +C-API for creating new Datatypes (3) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The current C-API with which users can create new datatypes +is limited in scope, and requires use of "private" structures. This means +the API is not extensible: no new members can be added to the structure +without losing binary compatibility. +This has already limited the inclusion of new sorting methods into +NumPy [new_sort]_. + +The new version shall thus replace the current ``PyArray_ArrFuncs`` structure used +to define new datatypes. +Datatypes that currently exist and are defined using these slots will be +supported during a deprecation period. + +The most likely solution is to hide the implementation from the user and thus make +it extensible in the future is to model the API after Python's stable +API [PEP-384]_: + +.. code-block:: C + + static struct PyArrayMethodDef slots[] = { + {NPY_dt_method, method_implementation}, + ..., + {0, NULL} + } + + typedef struct{ + PyTypeObject *typeobj; /* type of python scalar */ + ...; + PyType_Slot *slots; + } PyArrayDTypeMeta_Spec; + + PyObject* PyArray_InitDTypeMetaFromSpec( + PyArray_DTypeMeta *user_dtype, PyArrayDTypeMeta_Spec *dtype_spec); + +The C-side slots should be designed to mirror Python side methods +such as ``dtype.__dtype_method__``, although the exposure to Python is +a later step in the implementation to reduce the complexity of the initial +implementation. + + +C-API Changes to the UFunc Machinery (4) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Proposed changes to the UFunc machinery will be part of NEP 43. +However, the following changes will be necessary +(see :ref:`NEP 40 <NEP40>` +for a detailed description of the current implementation and its issues): + +* The current UFunc type resolution must be adapted to allow better control + for user-defined dtypes as well as resolve current inconsistencies. +* The inner-loop used in UFuncs must be expanded to include a return value. + Further, error reporting must be improved, and passing in dtype-specific + information enabled. + This requires the modification of the inner-loop function signature and + addition of new hooks called before and after the inner-loop is used. + +An important goal for any changes to the universal functions will be to +allow the reuse of existing loops. +It should be easy for a new units datatype to fall back to existing math +functions after handling the unit related computations. + + +Discussion +---------- + +See :ref:`NEP 40 <NEP40>` +for a list of previous meetings and discussions. + +Additional discussion around this specific NEP has occurred on both +the mailing list and the pull request: + +* `Mailing list discussion <https://mail.python.org/pipermail/numpy-discussion/2020-March/080481.html>`_ +* `NEP 41 pull request <https://github.com/numpy/numpy/pull/15506>`_ +* `Pull request thread on Dtype hierarchy and Scalars <https://github.com/numpy/numpy/pull/15506#discussion_r390016298>`_ + + +References +---------- + +.. [pandas_extension_arrays] https://pandas.pydata.org/pandas-docs/stable/development/extending.html#extension-types + +.. [xarray_dtype_issue] https://github.com/pydata/xarray/issues/1262 + +.. [pygeos] https://github.com/caspervdw/pygeos + +.. [new_sort] https://github.com/numpy/numpy/pull/12945 + +.. [PEP-384] https://www.python.org/dev/peps/pep-0384/ + +.. [PR 15508] https://github.com/numpy/numpy/pull/15508 + + +Copyright +--------- + +This document has been placed in the public domain. + + +Acknowledgments +--------------- + +The effort to create new datatypes for NumPy has been discussed for several +years in many different contexts and settings, making it impossible to list everyone involved. +We would like to thank especially Stephan Hoyer, Nathaniel Smith, and Eric Wieser +for repeated in-depth discussion about datatype design. +We are very grateful for the community input in reviewing and revising this +NEP and would like to thank especially Ross Barnowski and Ralf Gommers. diff --git a/doc/neps/nep-0042-new-dtypes.rst b/doc/neps/nep-0042-new-dtypes.rst new file mode 100644 index 000000000000..c29172a281c5 --- /dev/null +++ b/doc/neps/nep-0042-new-dtypes.rst @@ -0,0 +1,1430 @@ +.. _NEP42: + +============================================================================== +NEP 42 — New and extensible DTypes +============================================================================== + +:title: New and extensible DTypes +:Author: Sebastian Berg +:Author: Ben Nathanson +:Author: Marten van Kerkwijk +:Status: Accepted +:Type: Standard +:Created: 2019-07-17 +:Resolution: https://mail.python.org/pipermail/numpy-discussion/2020-October/081038.html + +.. note:: + + This NEP is third in a series: + + - :ref:`NEP40` explains the shortcomings of NumPy's dtype implementation. + + - :ref:`NEP41` gives an overview of our proposed replacement. + + - NEP 42 (this document) describes the new design's datatype-related APIs. + + - :ref:`NEP43` describes the new design's API for universal functions. + + +****************************************************************************** +Abstract +****************************************************************************** + +NumPy's dtype architecture is monolithic -- each dtype is an instance of a +single class. There's no principled way to expand it for new dtypes, and the +code is difficult to read and maintain. + +As :ref:`NEP 41 <NEP41>` explains, we are proposing a new architecture that is +modular and open to user additions. dtypes will derive from a new ``DType`` +class serving as the extension point for new types. ``np.dtype("float64")`` +will return an instance of a ``Float64`` class, a subclass of root class +``np.dtype``. + +This NEP is one of two that lay out the design and API of this new +architecture. This NEP addresses dtype implementation; :ref:`NEP 43 <NEP43>` addresses +universal functions. + +.. note:: + + Details of the private and external APIs may change to reflect user + comments and implementation constraints. The underlying principles and + choices should not change significantly. + + +****************************************************************************** +Motivation and scope +****************************************************************************** + +Our goal is to allow user code to create fully featured dtypes for a broad +variety of uses, from physical units (such as meters) to domain-specific +representations of geometric objects. :ref:`NEP 41 <NEP41>` describes a number +of these new dtypes and their benefits. + +Any design supporting dtypes must consider: + +- How shape and dtype are determined when an array is created +- How array elements are stored and accessed +- The rules for casting dtypes to other dtypes + +In addition: + +- We want dtypes to comprise a class hierarchy open to new types and to + subhierarchies, as motivated in :ref:`NEP 41 <NEP41>`. + +And to provide this, + +- We need to define a user API. + +All these are the subjects of this NEP. + +- The class hierarchy, its relation to the Python scalar types, and its + important attributes are described in `nep42_DType class`_. + +- The functionality that will support dtype casting is described in `Casting`_. + +- The implementation of item access and storage, and the way shape and dtype + are determined when creating an array, are described in :ref:`nep42_array_coercion`. + +- The functionality for users to define their own DTypes is described in + `Public C-API`_. + +The API here and in :ref:`NEP 43 <NEP43>` is entirely on the C side. A Python-side version +will be proposed in a future NEP. A future Python API is expected to be +similar, but provide a more convenient API to reuse the functionality of +existing DTypes. It could also provide shorthands to create structured DTypes +similar to Python's +`dataclasses <https://docs.python.org/3.8/library/dataclasses.html>`_. + + +****************************************************************************** +Backward compatibility +****************************************************************************** + +The disruption is expected to be no greater than that of a typical NumPy +release. + +- The main issues are noted in :ref:`NEP 41 <NEP41>` and will mostly affect + heavy users of the NumPy C-API. + +- Eventually we will want to deprecate the API currently used for creating + user-defined dtypes. + +- Small, rarely noticed inconsistencies are likely to change. Examples: + + - ``np.array(np.nan, dtype=np.int64)`` behaves differently from + ``np.array([np.nan], dtype=np.int64)`` with the latter raising an error. + This may require identical results (either both error or both succeed). + - ``np.array([array_like])`` sometimes behaves differently from + ``np.array([np.array(array_like)])`` + - array operations may or may not preserve dtype metadata + +- Documentation that describes the internal structure of dtypes will need + to be updated. + +The new code must pass NumPy's regular test suite, giving some assurance that +the changes are compatible with existing code. + +****************************************************************************** +Usage and impact +****************************************************************************** + +We believe the few structures in this section are sufficient to consolidate +NumPy's present functionality and also to support complex user-defined DTypes. + +The rest of the NEP fills in details and provides support for the claim. + +Again, though Python is used for illustration, the implementation is a C API only; a +future NEP will tackle the Python API. + +After implementing this NEP, creating a DType will be possible by implementing +the following outlined DType base class, +that is further described in `nep42_DType class`_: + +.. code-block:: python + :dedent: 0 + + class DType(np.dtype): + type : type # Python scalar type + parametric : bool # (may be indicated by superclass) + + @property + def canonical(self) -> bool: + raise NotImplementedError + + def ensure_canonical(self : DType) -> DType: + raise NotImplementedError + +For casting, a large part of the functionality is provided by the "methods" stored +in ``_castingimpl`` + +.. code-block:: python + :dedent: 0 + + @classmethod + def common_dtype(cls : DTypeMeta, other : DTypeMeta) -> DTypeMeta: + raise NotImplementedError + + def common_instance(self : DType, other : DType) -> DType: + raise NotImplementedError + + # A mapping of "methods" each detailing how to cast to another DType + # (further specified at the end of the section) + _castingimpl = {} + +For array-coercion, also part of casting: + +.. code-block:: python + :dedent: 0 + + def __dtype_setitem__(self, item_pointer, value): + raise NotImplementedError + + def __dtype_getitem__(self, item_pointer, base_obj) -> object: + raise NotImplementedError + + @classmethod + def __discover_descr_from_pyobject__(cls, obj : object) -> DType: + raise NotImplementedError + + # initially private: + @classmethod + def _known_scalar_type(cls, obj : object) -> bool: + raise NotImplementedError + + +Other elements of the casting implementation is the ``CastingImpl``: + +.. code-block:: python + :dedent: 0 + + casting = Union["safe", "same_kind", "unsafe"] + + class CastingImpl: + # Object describing and performing the cast + casting : casting + + def resolve_descriptors(self, Tuple[DTypeMeta], Tuple[DType|None] : input) -> (casting, Tuple[DType]): + raise NotImplementedError + + # initially private: + def _get_loop(...) -> lowlevel_C_loop: + raise NotImplementedError + +which describes the casting from one DType to another. In +:ref:`NEP 43 <NEP43>` this ``CastingImpl`` object is used unchanged to +support universal functions. +Note that the name ``CastingImpl`` here will be generically called +``ArrayMethod`` to accommodate both casting and universal functions. + + +****************************************************************************** +Definitions +****************************************************************************** +.. glossary:: + + dtype + The dtype *instance*; this is the object attached to a numpy array. + + DType + Any subclass of the base type ``np.dtype``. + + coercion + Conversion of Python types to NumPy arrays and values stored in a NumPy + array. + + cast + Conversion of an array to a different dtype. + + parametric type + A dtype whose representation can change based on a parameter value, + like a string dtype with a length parameter. All members of the current + ``flexible`` dtype class are parametric. See + :ref:`NEP 40 <parametric-datatype-discussion>`. + + promotion + Finding a dtype that can perform an operation on a mix of dtypes without + loss of information. + + safe cast + A cast is safe if no information is lost when changing type. + +On the C level we use ``descriptor`` or ``descr`` to mean +*dtype instance*. In the proposed C-API, these terms will distinguish +dtype instances from DType classes. + +.. note:: + NumPy has an existing class hierarchy for scalar types, as + seen :ref:`in the figure <nep-0040_dtype-hierarchy>` of + :ref:`NEP 40 <NEP40>`, and the new DType hierarchy will resemble it. The + types are used as an attribute of the single dtype class in the current + NumPy; they're not dtype classes. They neither harm nor help this work. + +.. _nep42_DType class: + +****************************************************************************** +The DType class +****************************************************************************** + +This section reviews the structure underlying the proposed DType class, +including the type hierarchy and the use of abstract DTypes. + +Class getter +============================================================================== + +To create a DType instance from a scalar type users now call +``np.dtype`` (for instance, ``np.dtype(np.int64)``). Sometimes it is +also necessary to access the underlying DType class; this comes up in +particular with type hinting because the "type" of a DType instance is +the DType class. Taking inspiration from type hinting, we propose the +following getter syntax:: + + np.dtype[np.int64] + +to get the DType class corresponding to a scalar type. The notation +works equally well with built-in and user-defined DTypes. + +This getter eliminates the need to create an explicit name for every +DType, crowding the ``np`` namespace; the getter itself signifies the +type. It also opens the possibility of making ``np.ndarray`` generic +over DType class using annotations like:: + + np.ndarray[np.dtype[np.float64]] + +The above is fairly verbose, so it is possible that we will include +aliases like:: + + Float64 = np.dtype[np.float64] + +in ``numpy.typing``, thus keeping annotations concise but still +avoiding crowding the ``np`` namespace as discussed above. For a +user-defined DType:: + + class UserDtype(dtype): ... + +one can do ``np.ndarray[UserDtype]``, keeping annotations concise in +that case without introducing boilerplate in NumPy itself. For a user +user-defined scalar type:: + + class UserScalar(generic): ... + +we would need to add a typing overload to ``dtype``:: + + @overload + __new__(cls, dtype: Type[UserScalar], ...) -> UserDtype + +to allow ``np.dtype[UserScalar]``. + +The initial implementation probably will return only concrete (not abstract) +DTypes. + +*This item is still under review.* + + +Hierarchy and abstract classes +============================================================================== + +We will use abstract classes as building blocks of our extensible DType class +hierarchy. + +1. Abstract classes are inherited cleanly, in principle allowing checks like + ``isinstance(np.dtype("float64"), np.inexact)``. + +2. Abstract classes allow a single piece of code to handle a multiplicity of + input types. Code written to accept Complex objects can work with numbers + of any precision; the precision of the results is determined by the + precision of the arguments. + +3. There's room for user-created families of DTypes. We can envision an + abstract ``Unit`` class for physical units, with a concrete subclass like + ``Float64Unit``. Calling ``Unit(np.float64, "m")`` (``m`` for meters) would + be equivalent to ``Float64Unit("m")``. + +4. The implementation of universal functions in :ref:`NEP 43 <NEP43>` may require + a class hierarchy. + +**Example:** A NumPy ``Categorical`` class would be a match for pandas +``Categorical`` objects, which can contain integers or general Python objects. +NumPy needs a DType that it can assign a Categorical to, but it also needs +DTypes like ``CategoricalInt64`` and ``CategoricalObject`` such that +``common_dtype(CategoricalInt64, String)`` raises an error, but +``common_dtype(CategoricalObject, String)`` returns an ``object`` DType. In +our scheme, ``Categorical`` is an abstract type with ``CategoricalInt64`` and +``CategoricalObject`` subclasses. + + +Rules for the class structure, illustrated :ref:`below <nep42_hierarchy_figure>`: + +1. Abstract DTypes cannot be instantiated. Instantiating an abstract DType + raises an error, or perhaps returns an instance of a concrete subclass. + Raising an error will be the default behavior and may be required initially. + +2. While abstract DTypes may be superclasses, they may also act like Python's + abstract base classes (ABC) allowing registration instead of subclassing. + It may be possible to simply use or inherit from Python ABCs. + +3. Concrete DTypes may not be subclassed. In the future this might be relaxed + to allow specialized implementations such as a GPU float64 subclassing a + NumPy float64. + +The +`Julia language <https://docs.julialang.org/en/v1/manual/types/#man-abstract-types-1>`_ +has a similar prohibition against subclassing concrete types. +For example methods such as the later ``__common_instance__`` or +``__common_dtype__`` cannot work for a subclass unless they were designed +very carefully. +It helps avoid unintended vulnerabilities to implementation changes that +result from subclassing types that were not written to be subclassed. +We believe that the DType API should rather be extended to simplify wrapping +of existing functionality. + +The DType class requires C-side storage of methods and additional information, +to be implemented by a ``DTypeMeta`` class. Each ``DType`` class is an +instance of ``DTypeMeta`` with a well-defined and extensible interface; +end users ignore it. + +.. _nep42_hierarchy_figure: +.. figure:: _static/dtype_hierarchy.svg + :figclass: align-center + + +Miscellaneous methods and attributes +============================================================================== + +This section collects definitions in the DType class that are not used in +casting and array coercion, which are described in detail below. + +* Existing dtype methods (:class:`numpy.dtype`) and C-side fields are preserved. + +* ``DType.type`` replaces ``dtype.type``. Unless a use case arises, + ``dtype.type`` will be deprecated. + This indicates a Python scalar type which represents the same values as + the DType. This is the same type as used in the proposed `Class getter`_ + and for `DType discovery during array coercion`_. + (This can may also be set for abstract DTypes, this is necessary + for array coercion.) + +* A new ``self.canonical`` property generalizes the notion of byte order to + indicate whether data has been stored in a default/canonical way. For + existing code, "canonical" will just signify native byte order, but it can + take on new meanings in new DTypes -- for instance, to distinguish a + complex-conjugated instance of Complex which stores ``real - imag`` instead + of ``real + imag``. The ISNBO ("is + native byte order") flag might be repurposed as the canonical flag. + +* Support is included for parametric DTypes. A DType will be deemed parametric + if it inherits from ParametricDType. + +* DType methods may resemble or even reuse existing Python slots. Thus Python + special slots are off-limits for user-defined DTypes (for instance, defining + ``Unit("m") > Unit("cm")``), since we may want to develop a meaning for these + operators that is common to all DTypes. + +* Sorting functions are moved to the DType class. They may be implemented by + defining a method ``dtype_get_sort_function(self, sortkind="stable") -> + sortfunction`` that must return ``NotImplemented`` if the given ``sortkind`` + is not known. + +* Functions that cannot be removed are implemented as special methods. + Many of these were previously defined part of the :c:type:`PyArray_ArrFuncs` + slot of the dtype instance (``PyArray_Descr *``) and include functions + such as ``nonzero``, ``fill`` (used for ``np.arange``), and + ``fromstr`` (used to parse text files). + These old methods will be deprecated and replacements + following the new design principles added. + The API is not defined here. Since these methods can be deprecated and renamed + replacements added, it is acceptable if these new methods have to be modified. + +* Use of ``kind`` for non-built-in types is discouraged in favor of + ``isinstance`` checks. ``kind`` will return the ``__qualname__`` of the + object to ensure uniqueness for all DTypes. On the C side, ``kind`` and + ``char`` are set to ``\0`` (NULL character). + While ``kind`` will be discouraged, the current ``np.issubdtype`` + may remain the preferred method for this type of check. + +* A method ``ensure_canonical(self) -> dtype`` returns a new dtype (or + ``self``) with the ``canonical`` flag set. + +* Since NumPy's approach is to provide functionality through unfuncs, + functions like sorting that will be implemented in DTypes might eventually be + reimplemented as generalized ufuncs. + +.. _nep_42_casting: + +****************************************************************************** +Casting +****************************************************************************** + +We review here the operations related to casting arrays: + +- Finding the "common dtype," returned by :func:`numpy.promote_types` and + :func:`numpy.result_type` + +- The result of calling :func:`numpy.can_cast` + +We show how casting arrays with ``astype(new_dtype)`` will be implemented. + +`Common DType` operations +============================================================================== + +When input types are mixed, a first step is to find a DType that can hold +the result without loss of information -- a "common DType." + +Array coercion and concatenation both return a common dtype instance. Most +universal functions use the common DType for dispatching, though they might +not use it for a result (for instance, the result of a comparison is always +bool). + +We propose the following implementation: + +- For two DType classes:: + + __common_dtype__(cls, other : DTypeMeta) -> DTypeMeta + + Returns a new DType, often one of the inputs, which can represent values + of both input DTypes. This should usually be minimal: + the common DType of ``Int16`` and ``Uint16`` is ``Int32`` and not ``Int64``. + ``__common_dtype__`` may return NotImplemented to defer to other and, + like Python operators, subclasses take precedence (their + ``__common_dtype__`` method is tried first). + +- For two instances of the same DType:: + + __common_instance__(self: SelfT, other : SelfT) -> SelfT + + For nonparametric built-in dtypes, this returns a canonicalized copy of + ``self``, preserving metadata. For nonparametric user types, this provides + a default implementation. + +- For instances of different DTypes, for example ``>float64`` and ``S8``, + the operation is done in three steps: + + 1. ``Float64.__common_dtype__(type(>float64), type(S8))`` + returns ``String`` (or defers to ``String.__common_dtype__``). + + 2. The casting machinery (explained in detail below) provides the + information that ``">float64"`` casts to ``"S32"`` + + 3. ``String.__common_instance__("S8", "S32")`` returns the final ``"S32"``. + +The benefit of this handoff is to reduce duplicated code and keep concerns +separate. DType implementations don't need to know how to cast, and the +results of casting can be extended to new types, such as a new string encoding. + +This means the implementation will work like this:: + + def common_dtype(DType1, DType2): + common_dtype = type(dtype1).__common_dtype__(type(dtype2)) + if common_dtype is NotImplemented: + common_dtype = type(dtype2).__common_dtype__(type(dtype1)) + if common_dtype is NotImplemented: + raise TypeError("no common dtype") + return common_dtype + + def promote_types(dtype1, dtype2): + common = common_dtype(type(dtype1), type(dtype2)) + + if type(dtype1) is not common: + # Find what dtype1 is cast to when cast to the common DType + # by using the CastingImpl as described below: + castingimpl = get_castingimpl(type(dtype1), common) + safety, (_, dtype1) = castingimpl.resolve_descriptors( + (common, common), (dtype1, None)) + assert safety == "safe" # promotion should normally be a safe cast + + if type(dtype2) is not common: + # Same as above branch for dtype1. + + if dtype1 is not dtype2: + return common.__common_instance__(dtype1, dtype2) + +Some of these steps may be optimized for nonparametric DTypes. + +Since the type returned by ``__common_dtype__`` is not necessarily one of the +two arguments, it's not equivalent to NumPy's "safe" casting. +Safe casting works for ``np.promote_types(int16, int64)``, which returns +``int64``, but fails for:: + + np.promote_types("int64", "float32") -> np.dtype("float64") + +It is the responsibility of the DType author to ensure that the inputs +can be safely cast to the ``__common_dtype__``. + +Exceptions may apply. For example, casting ``int32`` to +a (long enough) string is at least at this time considered "safe". +However ``np.promote_types(int32, String)`` will *not* be defined. + +**Example:** + +``object`` always chooses ``object`` as the common DType. For +``datetime64`` type promotion is defined with no other datatype, but if +someone were to implement a new higher precision datetime, then:: + + HighPrecisionDatetime.__common_dtype__(np.dtype[np.datetime64]) + +would return ``HighPrecisionDatetime``, and the casting implementation, +as described below, may need to decide how to handle the datetime unit. + + +**Alternatives:** + +- We're pushing the decision on common DTypes to the DType classes. Suppose + instead we could turn to a universal algorithm based on safe casting, + imposing a total order on DTypes and returning the first type that both + arguments could cast to safely. + + It would be difficult to devise a reasonable total order, and it would have + to accept new entries. Beyond that, the approach is flawed because + importing a type can change the behavior of a program. For example, a + program requiring the common DType of ``int16`` and ``uint16`` would + ordinarily get the built-in type ``int32`` as the first match; if the + program adds ``import int24``, the first match becomes ``int24`` and the + smaller type might make the program overflow for the first time. [1]_ + +- A more flexible common DType could be implemented in the future where + ``__common_dtype__`` relies on information from the casting logic. + Since ``__commond_dtype__`` is a method a such a default implementation + could be added at a later time. + +- The three-step handling of differing dtypes could, of course, be coalesced. + It would lose the value of splitting in return for a possibly faster + execution. But few cases would benefit. Most cases, such as array coercion, + involve a single Python type (and thus dtype). + + +The cast operation +============================================================================== + +Casting is perhaps the most complex and interesting DType operation. It +is much like a typical universal function on arrays, converting one input to a +new output, with two distinctions: + +- Casting always requires an explicit output datatype. +- The NumPy iterator API requires access to functions that are lower-level + than what universal functions currently need. + +Casting can be complex and may not implement all details of each input +datatype (such as non-native byte order or unaligned access). So a complex +type conversion might entail 3 steps: + +1. The input datatype is normalized and prepared for the cast. +2. The cast is performed. +3. The result, which is in a normalized form, is cast to the requested + form (non-native byte order). + +Further, NumPy provides different casting kinds or safety specifiers: + +* ``equivalent``, allowing only byte-order changes +* ``safe``, requiring a type large enough to preserve value +* ``same_kind``, requiring a safe cast or one within a kind, like float64 to float32 +* ``unsafe``, allowing any data conversion + +and in some cases a cast may be just a view. + +We need to support the two current signatures of ``arr.astype``: + +- For DTypes: ``arr.astype(np.String)`` + + - current spelling ``arr.astype("S")`` + - ``np.String`` can be an abstract DType + +- For dtypes: ``arr.astype(np.dtype("S8"))`` + + +We also have two signatures of ``np.can_cast``: + +- Instance to class: ``np.can_cast(dtype, DType, "safe")`` +- Instance to instance: ``np.can_cast(dtype, other_dtype, "safe")`` + +On the Python level ``dtype`` is overloaded to mean class or instance. + +A third ``can_cast`` signature, ``np.can_cast(DType, OtherDType, "safe")``,may be used +internally but need not be exposed to Python. + +During DType creation, DTypes will be able to pass a list of ``CastingImpl`` +objects, which can define casting to and from the DType. + +One of them should define the cast between instances of that DType. It can be +omitted if the DType has only a single implementation and is nonparametric. + +Each ``CastingImpl`` has a distinct DType signature: + + ``CastingImpl[InputDtype, RequestedDtype]`` + +and implements the following methods and attributes: + + +* To report safeness, + + ``resolve_descriptors(self, Tuple[DTypeMeta], Tuple[DType|None] : input) -> casting, Tuple[DType]``. + + The ``casting`` output reports safeness (safe, unsafe, or same-kind), and + the tuple is used for more multistep casting, as in the example below. + +* To get a casting function, + + ``get_loop(...) -> function_to_handle_cast (signature to be decided)`` + + returns a low-level implementation of a strided casting function + ("transfer function") capable of performing the + cast. + + Initially the implementation will be *private*, and users will only be + able to provide strided loops with the signature. + +* For performance, a ``casting`` attribute taking a value of ``equivalent``, ``safe``, + ``unsafe``, or ``same-kind``. + + +**Performing a cast** + +.. _nep42_cast_figure: + +.. figure:: _static/casting_flow.svg + :figclass: align-center + +The above figure illustrates a multistep +cast of an ``int24`` with a value of ``42`` to a string of length 20 +(``"S20"``). + +We've picked an example where the implementer has only provided limited +functionality: a function to cast an ``int24`` to an ``S8`` string (which can +hold all 24-bit integers). This means multiple conversions are needed. + +The full process is: + +1. Call + + ``CastingImpl[Int24, String].resolve_descriptors((Int24, String), (int24, "S20"))``. + + This provides the information that ``CastingImpl[Int24, String]`` only + implements the cast of ``int24`` to ``"S8"``. + +2. Since ``"S8"`` does not match ``"S20"``, use + + ``CastingImpl[String, String].get_loop()`` + + to find the transfer (casting) function to convert an ``"S8"`` into an ``"S20"`` + +3. Fetch the transfer function to convert an ``int24`` to an ``"S8"`` using + + ``CastingImpl[Int24, String].get_loop()`` + +4. Perform the actual cast using the two transfer functions: + + ``int24(42) -> S8("42") -> S20("42")``. + + ``resolve_descriptors`` allows the implementation for + + ``np.array(42, dtype=int24).astype(String)`` + + to call + + ``CastingImpl[Int24, String].resolve_descriptors((Int24, String), (int24, None))``. + + In this case the result of ``(int24, "S8")`` defines the correct cast: + + ``np.array(42, dtype=int24).astype(String) == np.array("42", dtype="S8")``. + +**Casting safety** + +To compute ``np.can_cast(int24, "S20", casting="safe")``, only the +``resolve_descriptors`` function is required and +is called in the same way as in :ref:`the figure describing a cast <nep42_cast_figure>`. + +In this case, the calls to ``resolve_descriptors``, will also provide the +information that ``int24 -> "S8"`` as well as ``"S8" -> "S20"`` are safe +casts, and thus also the ``int24 -> "S20"`` is a safe cast. + +In some cases, no cast is necessary. For example, on most Linux systems +``np.dtype("long")`` and ``np.dtype("longlong")`` are different dtypes but are +both 64-bit integers. In this case, the cast can be performed using +``long_arr.view("longlong")``. The information that a cast is a view will be +handled by an additional flag. Thus the ``casting`` can have the 8 values in +total: the original 4 of ``equivalent``, ``safe``, ``unsafe``, and ``same-kind``, +plus ``equivalent+view``, ``safe+view``, ``unsafe+view``, and +``same-kind+view``. NumPy currently defines ``dtype1 == dtype2`` to be True +only if byte order matches. This functionality can be replaced with the +combination of "equivalent" casting and the "view" flag. + +(For more information on the ``resolve_descriptors`` signature see the +:ref:`nep42_C-API` section below and :ref:`NEP 43 <NEP43>`.) + + +**Casting between instances of the same DType** + +To keep down the number of casting +steps, CastingImpl must be capable of any conversion between all instances +of this DType. + +In general the DType implementer must include ``CastingImpl[DType, DType]`` +unless there is only a singleton instance. + +**General multistep casting** + +We could implement certain casts, such as ``int8`` to ``int24``, +even if the user provides only an ``int16 -> int24`` cast. This proposal does +not provide that, but future work might find such casts dynamically, or at least +allow ``resolve_descriptors`` to return arbitrary ``dtypes``. + +If ``CastingImpl[Int8, Int24].resolve_descriptors((Int8, Int24), (int8, int24))`` +returns ``(int16, int24)``, the actual casting process could be extended to include +the ``int8 -> int16`` cast. This adds a step. + + +**Example:** + +The implementation for casting integers to datetime would generally +say that this cast is unsafe (because it is always an unsafe cast). +Its ``resolve_descriptors`` function may look like:: + + def resolve_descriptors(self, DTypes, given_dtypes): + from_dtype, to_dtype = given_dtypes + from_dtype = from_dtype.ensure_canonical() # ensure not byte-swapped + if to_dtype is None: + raise TypeError("Cannot convert to a NumPy datetime without a unit") + to_dtype = to_dtype.ensure_canonical() # ensure not byte-swapped + + # This is always an "unsafe" cast, but for int64, we can represent + # it by a simple view (if the dtypes are both canonical). + # (represented as C-side flags here). + safety_and_view = NPY_UNSAFE_CASTING | _NPY_CAST_IS_VIEW + return safety_and_view, (from_dtype, to_dtype) + +.. note:: + + While NumPy currently defines integer-to-datetime casts, with the possible + exception of the unit-less ``timedelta64`` it may be better to not define + these casts at all. In general we expect that user defined DTypes will be + using custom methods such as ``unit.drop_unit(arr)`` or ``arr * + unit.seconds``. + + +**Alternatives:** + +- Our design objectives are: + - Minimize the number of DType methods and avoid code duplication. + - Mirror the implementation of universal functions. + +- The decision to use only the DType classes in the first step of finding the + correct ``CastingImpl`` in addition to defining ``CastingImpl.casting``, + allows to retain the current default implementation of + ``__common_dtype__`` for existing user defined dtypes, which could be + expanded in the future. + +- The split into multiple steps may seem to add complexity rather than reduce + it, but it consolidates the signatures of ``np.can_cast(dtype, DTypeClass)`` + and ``np.can_cast(dtype, other_dtype)``. + + Further, the API guarantees separation of concerns for user DTypes. The user + ``Int24`` dtype does not have to handle all string lengths if it does not + wish to do so. Further, an encoding added to the ``String`` DType would + not affect the overall cast. The ``resolve_descriptors`` function + can keep returning the default encoding and the ``CastingImpl[String, + String]`` can take care of any necessary encoding changes. + +- The main alternative is moving most of the information that is here pushed + into the ``CastingImpl`` directly into methods on the DTypes. But this + obscures the similarity between casting and universal functions. It does + reduce indirection, as noted below. + +- An earlier proposal defined two methods ``__can_cast_to__(self, other)`` to + dynamically return ``CastingImpl``. This + removes the requirement to define all possible casts at DType creation + (of one of the involved DTypes). + + Such an API could be added later. It resembles Python's ``__getattr__`` in + providing additional control over attribute lookup. + + +**Notes:** + +``CastingImpl`` is used as a name in this NEP to clarify that it implements +all functionality related to a cast. It is meant to be identical to the +``ArrayMethod`` proposed in NEP 43 as part of restructuring ufuncs to handle +new DTypes. All type definitions are expected to be named ``ArrayMethod``. + +The way dispatching works for ``CastingImpl`` is planned to be limited +initially and fully opaque. In the future, it may or may not be moved into a +special UFunc, or behave more like a universal function. + + +.. _nep42_array_coercion: + + +Coercion to and from Python objects +============================================================================== + +When storing a single value in an array or taking it out, it is necessary to +coerce it -- that is, convert it -- to and from the low-level representation +inside the array. + +Coercion is slightly more complex than typical casts. One reason is that a +Python object could itself be a 0-dimensional array or scalar with an +associated DType. + +Coercing to and from Python scalars requires two to three +methods that largely correspond to the current definitions: + +1. ``__dtype_setitem__(self, item_pointer, value)`` + +2. ``__dtype_getitem__(self, item_pointer, base_obj) -> object``; + ``base_obj`` is for memory management and usually ignored; it points to + an object owning the data. Its only role is to support structured datatypes + with subarrays within NumPy, which currently return views into the array. + The function returns an equivalent Python scalar (i.e. typically a NumPy + scalar). + +3. ``__dtype_get_pyitem__(self, item_pointer, base_obj) -> object`` (initially + hidden for new-style user-defined datatypes, may be exposed on user + request). This corresponds to the ``arr.item()`` method also used by + ``arr.tolist()`` and returns Python floats, for example, instead of NumPy + floats. + +(The above is meant for C-API. A Python-side API would have to use byte +buffers or similar to implement this, which may be useful for prototyping.) + +When a certain scalar +has a known (different) dtype, NumPy may in the future use casting instead of +``__dtype_setitem__``. + +A user datatype is (initially) expected to implement +``__dtype_setitem__`` for its own ``DType.type`` and all basic Python scalars +it wishes to support (e.g. ``int`` and ``float``). In the future a +function ``known_scalar_type`` may be made public to allow a user dtype to signal +which Python scalars it can store directly. + + +**Implementation:** The pseudocode implementation for setting a single item in +an array from an arbitrary Python object ``value`` is (some +functions here are defined later):: + + def PyArray_Pack(dtype, item_pointer, value): + DType = type(dtype) + if DType.type is type(value) or DType.known_scalartype(type(value)): + return dtype.__dtype_setitem__(item_pointer, value) + + # The dtype cannot handle the value, so try casting: + arr = np.array(value) + if arr.dtype is object or arr.ndim != 0: + # not a numpy or user scalar; try using the dtype after all: + return dtype.__dtype_setitem__(item_pointer, value) + + arr.astype(dtype) + item_pointer.write(arr[()]) + +where the call to ``np.array()`` represents the dtype discovery and is +not actually performed. + +**Example:** Current ``datetime64`` returns ``np.datetime64`` scalars and can +be assigned from ``np.datetime64``. However, the datetime +``__dtype_setitem__`` also allows assignment from date strings ("2016-05-01") +or Python integers. Additionally the datetime ``__dtype_get_pyitem__`` +function actually returns a Python ``datetime.datetime`` object (most of the +time). + + +**Alternatives:** This functionality could also be implemented as a cast to and +from the ``object`` dtype. +However, coercion is slightly more complex than typical casts. +One reason is that in general a Python object could itself be a +zero-dimensional array or scalar with an associated DType. +Such an object has a DType, and the correct cast to another DType is already +defined:: + + np.array(np.float32(4), dtype=object).astype(np.float64) + +is identical to:: + + np.array(4, dtype=np.float32).astype(np.float64) + +Implementing the first ``object`` to ``np.float64`` cast explicitly, +would require the user to take to duplicate or fall back to existing +casting functionality. + +It is certainly possible to describe the coercion to and from Python objects +using the general casting machinery, but the ``object`` dtype is special and +important enough to be handled by NumPy using the presented methods. + +**Further issues and discussion:** + +- The ``__dtype_setitem__`` function duplicates some code, such as coercion + from a string. + + ``datetime64`` allows assignment from string, but the same conversion also + occurs for casting from the string dtype to ``datetime64``. + + We may in the future expose the ``known_scalartype`` function to allow the + user to implement such duplication. + + For example, NumPy would normally use + + ``np.array(np.string_("2019")).astype(datetime64)`` + + but ``datetime64`` could choose to use its ``__dtype_setitem__`` instead + for performance reasons. + +- There is an issue about how subclasses of scalars should be handled. We + anticipate to stop automatically detecting the dtype for + ``np.array(float64_subclass)`` to be float64. The user can still provide + ``dtype=np.float64``. However, the above automatic casting using + ``np.array(scalar_subclass).astype(requested_dtype)`` will fail. In many + cases, this is not an issue, since the Python ``__float__`` protocol can be + used instead. But in some cases, this will mean that subclasses of Python + scalars will behave differently. + +.. note:: + + *Example:* ``np.complex256`` should not use ``__float__`` in its + ``__dtype_setitem__`` method in the future unless it is a known floating + point type. If the scalar is a subclass of a different high precision + floating point type (e.g. ``np.float128``) then this currently loses + precision without notifying the user. + In that case ``np.array(float128_subclass(3), dtype=np.complex256)`` + may fail unless the ``float128_subclass`` is first converted to the + ``np.float128`` base class. + + +DType discovery during array coercion +============================================================================== + +An important step in the use of NumPy arrays is creation of the array from +collections of generic Python objects. + +**Motivation:** Although the distinction is not clear currently, there are two main needs:: + + np.array([1, 2, 3, 4.]) + +needs to guess the correct dtype based on the Python objects inside. +Such an array may include a mix of datatypes, as long as they can be +promoted. +A second use case is when users provide the output DType class, but not the +specific DType instance:: + + np.array([object(), None], dtype=np.dtype[np.string_]) # (or `dtype="S"`) + +In this case the user indicates that ``object()`` and ``None`` should be +interpreted as strings. +The need to consider the user provided DType also arises for a future +``Categorical``:: + + np.array([1, 2, 1, 1, 2], dtype=Categorical) + +which must interpret the numbers as unique categorical values rather than +integers. + +There are three further issues to consider: + +1. It may be desirable to create datatypes associated + with normal Python scalars (such as ``datetime.datetime``) that do not + have a ``dtype`` attribute already. + +2. In general, a datatype could represent a sequence, however, NumPy currently + assumes that sequences are always collections of elements + (the sequence cannot be an element itself). + An example would be a ``vector`` DType. + +3. An array may itself contain arrays with a specific dtype (even + general Python objects). For example: + ``np.array([np.array(None, dtype=object)], dtype=np.String)`` + poses the issue of how to handle the included array. + +Some of these difficulties arise because finding the correct shape +of the output array and finding the correct datatype are closely related. + +**Implementation:** There are two distinct cases above: + +1. The user has provided no dtype information. + +2. The user provided a DType class -- as represented, for example, by ``"S"`` + representing a string of any length. + +In the first case, it is necessary to establish a mapping from the Python type(s) +of the constituent elements to the DType class. +Once the DType class is known, the correct dtype instance needs to be found. +In the case of strings, this requires to find the string length. + +These two cases shall be implemented by leveraging two pieces of information: + +1. ``DType.type``: The current type attribute to indicate which Python scalar + type is associated with the DType class (this is a *class* attribute that always + exists for any datatype and is not limited to array coercion). + +2. ``__discover_descr_from_pyobject__(cls, obj) -> dtype``: A classmethod that + returns the correct descriptor given the input object. + Note that only parametric DTypes have to implement this. + For nonparametric DTypes using the default instance will always be acceptable. + +The Python scalar type which is already associated with a DType through the +``DType.type`` attribute maps from the DType to the Python scalar type. +At registration time, a DType may choose to allow automatically discover for +this Python scalar type. +This requires a lookup in the opposite direction, which will be implemented +using global a mapping (dictionary-like) of:: + + known_python_types[type] = DType + +Correct garbage collection requires additional care. +If both the Python scalar type (``pytype``) and ``DType`` are created dynamically, +they will potentially be deleted again. +To allow this, it must be possible to make the above mapping weak. +This requires that the ``pytype`` holds a reference of ``DType`` explicitly. +Thus, in addition to building the global mapping, NumPy will store the ``DType`` as +``pytype.__associated_array_dtype__`` in the Python type. +This does *not* define the mapping and should *not* be accessed directly. +In particular potential inheritance of the attribute does not mean that NumPy will use the +superclasses ``DType`` automatically. A new ``DType`` must be created for the +subclass. + +.. note:: + + Python integers do not have a clear/concrete NumPy type associated right + now. This is because during array coercion NumPy currently finds the first + type capable of representing their value in the list of `long`, `unsigned + long`, `int64`, `unsigned int64`, and `object` (on many machines `long` is + 64 bit). + + Instead they will need to be implemented using an ``AbstractPyInt``. This + DType class can then provide ``__discover_descr_from_pyobject__`` and + return the actual dtype which is e.g. ``np.dtype("int64")``. For + dispatching/promotion in ufuncs, it will also be necessary to dynamically + create ``AbstractPyInt[value]`` classes (creation can be cached), so that + they can provide the current value based promotion functionality provided + by ``np.result_type(python_integer, array)`` [2]_ . + +To allow for a DType to accept inputs as scalars that are not basic Python +types or instances of ``DType.type``, we use ``known_scalar_type`` method. +This can allow discovery of a ``vector`` as a scalar (element) instead of a sequence +(for the command ``np.array(vector, dtype=VectorDType)``) even when ``vector`` is itself a +sequence or even an array subclass. This will *not* be public API initially, +but may be made public at a later time. + +**Example:** The current datetime DType requires a +``__discover_descr_from_pyobject__`` which returns the correct unit for string +inputs. This allows it to support:: + + np.array(["2020-01-02", "2020-01-02 11:24"], dtype="M8") + +By inspecting the date strings. Together with the common dtype +operation, this allows it to automatically find that the datetime64 unit +should be "minutes". + + +**NumPy internal implementation:** The implementation to find the correct dtype +will work similar to the following pseudocode:: + + def find_dtype(array_like): + common_dtype = None + for element in array_like: + # default to object dtype, if unknown + DType = known_python_types.get(type(element), np.dtype[object]) + dtype = DType.__discover_descr_from_pyobject__(element) + + if common_dtype is None: + common_dtype = dtype + else: + common_dtype = np.promote_types(common_dtype, dtype) + +In practice, the input to ``np.array()`` is a mix of sequences and array-like +objects, so that deciding what is an element requires to check whether it +is a sequence. +The full algorithm (without user provided dtypes) thus looks more like:: + + def find_dtype_recursive(array_like, dtype=None): + """ + Recursively find the dtype for a nested sequences (arrays are not + supported here). + """ + DType = known_python_types.get(type(element), None) + + if DType is None and is_array_like(array_like): + # Code for a sequence, an array_like may have a DType we + # can use directly: + for element in array_like: + dtype = find_dtype_recursive(element, dtype=dtype) + return dtype + + elif DType is None: + DType = np.dtype[object] + + # dtype discovery and promotion as in `find_dtype` above + +If the user provides ``DType``, then this DType will be tried first, and the +``dtype`` may need to be cast before the promotion is performed. + +**Limitations:** The motivational point 3. of a nested array +``np.array([np.array(None, dtype=object)], dtype=np.String)`` is currently +(sometimes) supported by inspecting all elements of the nested array. +User DTypes will implicitly handle these correctly if the nested array +is of ``object`` dtype. +In some other cases NumPy will retain backward compatibility for existing +functionality only. +NumPy uses such functionality to allow code such as:: + + >>> np.array([np.array(["2020-05-05"], dtype="S")], dtype=np.datetime64) + array([['2020-05-05']], dtype='datetime64[D]') + +which discovers the datetime unit ``D`` (days). +This possibility will not be accessible to user DTypes without an +intermediate cast to ``object`` or a custom function. + +The use of a global type map means that an error or warning has to be given if +two DTypes wish to map to the same Python type. In most cases user DTypes +should only be implemented for types defined within the same library to avoid +the potential for conflicts. It will be the DType implementor's responsibility +to be careful about this and use avoid registration when in doubt. + +**Alternatives:** + +- Instead of a global mapping, we could rely on the scalar attribute + ``scalar.__associated_array_dtype__``. This only creates a difference in + behavior for subclasses, and the exact implementation can be undefined + initially. Scalars will be expected to derive from a NumPy scalar. In + principle NumPy could, for a time, still choose to rely on the attribute. + +- An earlier proposal for the ``dtype`` discovery algorithm used a two-pass + approach, first finding the correct ``DType`` class and only then + discovering the parametric ``dtype`` instance. It was rejected as + needlessly complex. But it would have enabled value-based promotion + in universal functions, allowing:: + + np.add(np.array([8], dtype="uint8"), [4]) + + to return a ``uint8`` result (instead of ``int16``), which currently happens for:: + + np.add(np.array([8], dtype="uint8"), 4) + + (note the list ``[4]`` instead of scalar ``4``). + This is not a feature NumPy currently has or desires to support. + +**Further issues and discussion:** It is possible to create a DType +such as Categorical, array, or vector which can only be used if ``dtype=DType`` +is provided. Such DTypes cannot roundtrip correctly. For example:: + + np.array(np.array(1, dtype=Categorical)[()]) + +will result in an integer array. To get the original ``Categorical`` array +``dtype=Categorical`` will need to be passed explicitly. +This is a general limitation, but round-tripping is always possible if +``dtype=original_arr.dtype`` is passed. + + +.. _nep42_c-api: + +****************************************************************************** +Public C-API +****************************************************************************** + +DType creation +============================================================================== + +To create a new DType the user will need to define the methods and attributes +outlined in the `Usage and impact`_ section and detailed throughout this +proposal. + +In addition, some methods similar to those in :c:type:`PyArray_ArrFuncs` will +be needed for the slots struct below. + +As mentioned in :ref:`NEP 41 <NEP41>`, the interface to define this DType +class in C is modeled after :PEP:`384`: Slots and some additional information +will be passed in a slots struct and identified by ``ssize_t`` integers:: + + static struct PyArrayMethodDef slots[] = { + {NPY_dt_method, method_implementation}, + ..., + {0, NULL} + } + + typedef struct{ + PyTypeObject *typeobj; /* type of python scalar or NULL */ + int flags /* flags, including parametric and abstract */ + /* NULL terminated CastingImpl; is copied and references are stolen */ + CastingImpl *castingimpls[]; + PyType_Slot *slots; + PyTypeObject *baseclass; /* Baseclass or NULL */ + } PyArrayDTypeMeta_Spec; + + PyObject* PyArray_InitDTypeMetaFromSpec(PyArrayDTypeMeta_Spec *dtype_spec); + +All of this is passed by copying. + +**TODO:** The DType author should be able to define new methods for the +DType, up to defining a full object, and, in the future, possibly even +extending the ``PyArrayDTypeMeta_Type`` struct. We have to decide what to make +available initially. A solution may be to allow inheriting only from an +existing class: ``class MyDType(np.dtype, MyBaseclass)``. If ``np.dtype`` is +first in the method resolution order, this also prevents an undesirable +override of slots like ``==``. + +The ``slots`` will be identified by names which are prefixed with ``NPY_dt_`` +and are: + +* ``is_canonical(self) -> {0, 1}`` +* ``ensure_canonical(self) -> dtype`` +* ``default_descr(self) -> dtype`` (return must be native and should normally be a singleton) +* ``setitem(self, char *item_ptr, PyObject *value) -> {-1, 0}`` +* ``getitem(self, char *item_ptr, PyObject (base_obj) -> object or NULL`` +* ``discover_descr_from_pyobject(cls, PyObject) -> dtype or NULL`` +* ``common_dtype(cls, other) -> DType, NotImplemented, or NULL`` +* ``common_instance(self, other) -> dtype or NULL`` + +Where possible, a default implementation will be provided if the slot is +omitted or set to ``NULL``. Nonparametric dtypes do not have to implement: + +* ``discover_descr_from_pyobject`` (uses ``default_descr`` instead) +* ``common_instance`` (uses ``default_descr`` instead) +* ``ensure_canonical`` (uses ``default_descr`` instead). + +Sorting is expected to be implemented using: + +* ``get_sort_function(self, NPY_SORTKIND sort_kind) -> {out_sortfunction, NotImplemented, NULL}``. + +For convenience, it will be sufficient if the user implements only: + +* ``compare(self, char *item_ptr1, char *item_ptr2, int *res) -> {-1, 0, 1}`` + + +**Limitations:** The ``PyArrayDTypeMeta_Spec`` struct is clumsy to extend (for +instance, by adding a version tag to the ``slots`` to indicate a new, longer +version). We could use a function to provide the struct; it would require +memory management but would allow ABI-compatible extension (the struct is +freed again when the DType is created). + + +CastingImpl +============================================================================== + +The external API for ``CastingImpl`` will be limited initially to defining: + +* ``casting`` attribute, which can be one of the supported casting kinds. + This is the safest cast possible. For example, casting between two NumPy + strings is of course "safe" in general, but may be "same kind" in a specific + instance if the second string is shorter. If neither type is parametric the + ``resolve_descriptors`` must use it. + +* ``resolve_descriptors(PyArrayMethodObject *self, PyArray_DTypeMeta *DTypes[2], + PyArray_Descr *dtypes_in[2], PyArray_Descr *dtypes_out[2], NPY_CASTING *casting_out) + -> int {0, -1}`` The out + dtypes must be set correctly to dtypes which the strided loop + (transfer function) can handle. Initially the result must have instances + of the same DType class as the ``CastingImpl`` is defined for. The + ``casting`` will be set to ``NPY_EQUIV_CASTING``, ``NPY_SAFE_CASTING``, + ``NPY_UNSAFE_CASTING``, or ``NPY_SAME_KIND_CASTING``. + A new, additional flag, + ``_NPY_CAST_IS_VIEW``, can be set to indicate that no cast is necessary and a + view is sufficient to perform the cast. The cast should return + ``-1`` when an error occurred. If a cast is not possible (but no error + occurred), a ``-1`` result should be returned *without* an error set. + *This point is under consideration, we may use ``-1`` to indicate + a general error, and use a different return value for an impossible cast.* + This means that it is *not* possible to inform the user about why a cast is + impossible. + +* ``strided_loop(char **args, npy_intp *dimensions, npy_intp *strides, + ...) -> int {0, -1}`` (signature will be fully defined in :ref:`NEP 43 <NEP43>`) + +This is identical to the proposed API for ufuncs. The additional ``...`` +part of the signature will include information such as the two ``dtype``\s. +More optimized loops are in use internally, and +will be made available to users in the future (see notes). + +Although verbose, the API will mimic the one for creating a new DType: + +.. code-block:: C + + typedef struct{ + int flags; /* e.g. whether the cast requires the API */ + int nin, nout; /* Number of Input and outputs (always 1) */ + NPY_CASTING casting; /* The "minimal casting level" */ + PyArray_DTypeMeta *dtypes; /* input and output DType class */ + /* NULL terminated slots defining the methods */ + PyType_Slot *slots; + } PyArrayMethod_Spec; + +The focus differs between casting and general ufuncs. For example, for casts +``nin == nout == 1`` is always correct, while for ufuncs ``casting`` is +expected to be usually `"no"`. + +**Notes:** We may initially allow users to define only a single loop. +Internally NumPy optimizes far more, and this should be made public +incrementally in one of two ways: + +* Allow multiple versions, such as: + + * contiguous inner loop + * strided inner loop + * scalar inner loop + +* Or, more likely, expose the ``get_loop`` function which is passed additional + information, such as the fixed strides (similar to our internal API). + +* The casting level denotes the minimal guaranteed casting level and can be + ``-1`` if the cast may be impossible. For most non-parametric casts, this + value will be the casting level. NumPy may skip the ``resolve_descriptors`` + call for ``np.can_cast()`` when the result is ``True`` based on this level. + +The example does not yet include setup and error handling. Since these are +similar to the UFunc machinery, they will be defined in :ref:`NEP 43 <NEP43>` and then +incorporated identically into casting. + +The slots/methods used will be prefixed with ``NPY_meth_``. + + +**Alternatives:** + +- Aside from name changes and signature tweaks, there seem to be few + alternatives to the above structure. The proposed API using ``*_FromSpec`` + function is a good way to achieve a stable and extensible API. The slots + design is extensible and can be changed without breaking binary + compatibility. Convenience functions can still be provided to allow creation + with less code. + +- One downside is that compilers cannot warn about function-pointer + incompatibilities. + + +****************************************************************************** +Implementation +****************************************************************************** + +Steps for implementation are outlined in the Implementation section of +:ref:`NEP 41 <NEP41>`. In brief, we first will rewrite the internals of +casting and array coercion. After that, the new public API will be added +incrementally. We plan to expose it in a preliminary state initially to gain +experience. All functionality currently implemented on the dtypes will be +replaced systematically as new features are added. + + +****************************************************************************** +Alternatives +****************************************************************************** + +The space of possible implementations is large, so there have been many +discussions, conceptions, and design documents. These are listed in +:ref:`NEP 40 <NEP40>`. Alternatives were also been discussed in the +relevant sections above. + + +****************************************************************************** +References +****************************************************************************** + +.. [1] To be clear, the program is broken: It should not have stored a value + in the common DType that was below the lowest int16 or above the highest + uint16. It avoided overflow earlier by an accident of implementation. + Nonetheless, we insist that program behavior not be altered just by + importing a type. + +.. [2] NumPy currently inspects the value to allow the operations:: + + np.array([1], dtype=np.uint8) + 1 + np.array([1.2], dtype=np.float32) + 1. + + to return a ``uint8`` or ``float32`` array respectively. This is + further described in the documentation for :func:`numpy.result_type`. + + +****************************************************************************** +Copyright +****************************************************************************** + +This document has been placed in the public domain. diff --git a/doc/neps/nep-0043-extensible-ufuncs.rst b/doc/neps/nep-0043-extensible-ufuncs.rst new file mode 100644 index 000000000000..3312eb12cc89 --- /dev/null +++ b/doc/neps/nep-0043-extensible-ufuncs.rst @@ -0,0 +1,1330 @@ +.. _NEP43: + +============================================================================== +NEP 43 — Enhancing the extensibility of UFuncs +============================================================================== + +:title: Enhancing the Extensibility of UFuncs +:Author: Sebastian Berg +:Status: Draft +:Type: Standard +:Created: 2020-06-20 + + +.. note:: + + This NEP is fourth in a series: + + - :ref:`NEP 40 <NEP40>` explains the shortcomings of NumPy's dtype implementation. + + - :ref:`NEP 41 <NEP41>` gives an overview of our proposed replacement. + + - :ref:`NEP 42 <NEP42>` describes the new design's datatype-related APIs. + + - NEP 43 (this document) describes the new design's API for universal functions. + + +****************************************************************************** +Abstract +****************************************************************************** + +The previous NEP 42 proposes the creation of new DTypes which can +be defined by users outside of NumPy itself. +The implementation of NEP 42 will enable users to create arrays with a custom dtype +and stored values. +This NEP outlines how NumPy will operate on arrays with custom dtypes in the future. +The most important functions operating on NumPy arrays are the so called +"universal functions" (ufunc) which include all math functions, such as +``np.add``, ``np.multiply``, and even ``np.matmul``. +These ufuncs must operate efficiently on multiple arrays with +different datatypes. + +This NEP proposes to expand the design of ufuncs. +It makes a new distinction between the ufunc which can operate +on many different dtypes such as floats or integers, +and a new ``ArrayMethod`` which defines the efficient operation for +specific dtypes. + +.. note:: + + Details of the private and external APIs may change to reflect user + comments and implementation constraints. The underlying principles and + choices should not change significantly. + + +****************************************************************************** +Motivation and scope +****************************************************************************** + +The goal of this NEP is to extend universal +functions support the new DType system detailed in NEPs 41 and 42. +While the main motivation is enabling new user-defined DTypes, this will +also significantly simplify defining universal functions for NumPy strings or +structured DTypes. +Until now, these DTypes are not supported by any of NumPy's functions +(such as ``np.add`` or ``np.equal``), due to difficulties arising from +their parametric nature (compare NEP 41 and 42), e.g. the string length. + +Functions on arrays must handle a number of distinct steps which are +described in more detail in section "`Steps involved in a UFunc call`_". +The most important ones are: + +- Organizing all functionality required to define a ufunc call for specific + DTypes. This is often called the "inner-loop". +- Deal with input for which no exact matching implementation is found. + For example when ``int32`` and ``float64`` are added, the ``int32`` + is cast to ``float64``. This requires a distinct "promotion" step. + +After organizing and defining these, we need to: + +- Define the user API for customizing both of the above points. +- Allow convenient reuse of existing functionality. + For example a DType representing physical units, such as meters, + should be able to fall back to NumPy's existing math implementations. + +This NEP details how these requirements will be achieved in NumPy: + +- All DTyper-specific functionality currently part of the ufunc + definition will be defined as part of a new `ArrayMethod`_ object. + This ``ArrayMethod`` object will be the new, preferred, way to describe any + function operating on arrays. + +- Ufuncs will dispatch to the ``ArrayMethod`` and potentially use promotion + to find the correct ``ArrayMethod`` to use. + This will be described in the `Promotion and dispatching`_ section. + +A new C-API will be outlined in each section. A future Python API is +expected to be very similar and the C-API is presented in terms of Python +code for readability. + +The NEP proposes a large, but necessary, refactor of the NumPy ufunc internals. +This modernization will not affect end users directly and is not only a necessary +step for new DTypes, but in itself a maintenance effort which is expected to +help with future improvements to the ufunc machinery. + +While the most important restructure proposed is the new ``ArrayMethod`` +object, the largest long-term consideration is the API choice for +promotion and dispatching. + + +*********************** +Backwards Compatibility +*********************** + +The general backwards compatibility issues have also been listed +previously in NEP 41. + +The vast majority of users should not see any changes beyond those typical +for NumPy releases. +There are three main users or use-cases impacted by the proposed changes: + +1. The Numba package uses direct access to the NumPy C-loops and modifies + the NumPy ufunc struct directly for its own purposes. +2. Astropy uses its own "type resolver", meaning that a default switch over + from the existing type resolution to a new default Promoter requires care. +3. It is currently possible to register loops for dtype *instances*. + This is theoretically useful for structured dtypes and is a resolution + step happening *after* the DType resolution step proposed here. + +This NEP will try hard to maintain backward compatibility as much as +possible. However, both of these projects have signaled willingness to adapt +to breaking changes. + +The main reason why NumPy will be able to provide backward compatibility +is that: + +* Existing inner-loops can be wrapped, adding an indirection to the call but + maintaining full backwards compatibility. + The ``get_loop`` function can, in this case, search the existing + inner-loop functions (which are stored on the ufunc directly) in order + to maintain full compatibility even with potential direct structure access. +* Legacy type resolvers can be called as a fallback (potentially caching + the result). The resolver may need to be called twice (once for the DType + resolution and once for the ``resolve_descriptor`` implementation). +* The fallback to the legacy type resolver should in most cases handle loops + defined for such structured dtype instances. This is because if there is no + other ``np.Void`` implementation, the legacy fallback will retain the old + behaviour at least initially. + +The masked type resolvers specifically will *not* remain supported, but +has no known users (including NumPy itself, which only uses the default +version). + +Further, no compatibility attempt will be made for *calling* as opposed +to providing either the normal or the masked type resolver. As NumPy +will use it only as a fallback. There are no known users of this +(undocumented) possibility. + +While the above changes potentially break some workflows, +we believe that the long-term improvements vastly outweigh this. +Further, packages such as astropy and Numba are capable of adapting so that +end-users may need to update their libraries but not their code. + + +****************************************************************************** +Usage and impact +****************************************************************************** + +This NEP restructures how operations on NumPy arrays are defined both +within NumPy and for external implementers. +The NEP mainly concerns those who either extend ufuncs for custom DTypes +or create custom ufuncs. It does not aim to finalize all +potential use-cases, but rather restructure NumPy to be extensible and allow +addressing new issues or feature requests as they arise. + + +Overview and end user API +========================= + +To give an overview of how this NEP proposes to structure ufuncs, +the following describes the potential exposure of the proposed restructure +to the end user. + +Universal functions are much like a Python method defined on the DType of +the array when considering a ufunc with only a single input:: + + res = np.positive(arr) + +could be implemented (conceptually) as:: + + positive_impl = arr.dtype.positive + res = positive_impl(arr) + +However, unlike methods, ``positive_impl`` is not stored on the dtype itself. +It is rather the implementation of ``np.positive`` for a specific DType. +Current NumPy partially exposes this "choice of implementation" using +the ``dtype`` (or more exact ``signature``) attribute in universal functions, +although these are rarely used:: + + np.positive(arr, dtype=np.float64) + +forces NumPy to use the ``positive_impl`` written specifically for the Float64 +DType. + +This NEP makes the distinction more explicit, by creating a new object to +represent ``positive_impl``:: + + positive_impl = np.positive.resolve_impl((type(arr.dtype), None)) + # The `None` represents the output DType which is automatically chosen. + +While the creation of a ``positive_impl`` object and the ``resolve_impl`` +method is part of this NEP, the following code:: + + res = positive_impl(arr) + +may not be implemented initially and is not central to the redesign. + +In general NumPy universal functions can take many inputs. +This requires looking up the implementation by considering all of them +and makes ufuncs "multi-methods" with respect to the input DTypes:: + + add_impl = np.add.resolve_impl((type(arr1.dtype), type(arr2.dtype), None)) + +This NEP defines how ``positive_impl`` and ``add_impl`` will be represented +as a new ``ArrayMethod`` which can be implemented outside of NumPy. +Further, it defines how ``resolve_impl`` will implement and solve dispatching +and promotion. + +The reasons for this split may be more clear after reviewing the +`Steps involved in a UFunc call`_ section. + + +Defining a new ufunc implementation +=================================== + +The following is a mock-up of how a new implementation, in this case +to define string equality, will be added to a ufunc. + +.. code-block:: python + + class StringEquality(BoundArrayMethod): + nin = 1 + nout = 1 + # DTypes are stored on the BoundArrayMethod and not on the internal + # ArrayMethod, to reference cyles. + DTypes = (String, String, Bool) + + def resolve_descriptors(self: ArrayMethod, DTypes, given_descrs): + """The strided loop supports all input string dtype instances + and always returns a boolean. (String is always native byte order.) + + Defining this function is not necessary, since NumPy can provide + it by default. + + The `self` argument here refers to the unbound array method, so + that DTypes are passed in explicitly. + """ + assert isinstance(given_descrs[0], DTypes[0]) + assert isinstance(given_descrs[1], DTypes[1]) + assert given_descrs[2] is None or isinstance(given_descrs[2], DTypes[2]) + + out_descr = given_descrs[2] # preserve input (e.g. metadata) + if given_descrs[2] is None: + out_descr = DTypes[2]() + + # The operation is always "no" casting (most ufuncs are) + return (given_descrs[0], given_descrs[1], out_descr), "no" + + def strided_loop(context, dimensions, data, strides, innerloop_data): + """The 1-D strided loop, similar to those used in current ufuncs""" + # dimensions: Number of loop items and core dimensions + # data: Pointers to the array data. + # strides: strides to iterate all elements + n = dimensions[0] # number of items to loop over + num_chars1 = context.descriptors[0].itemsize + num_chars2 = context.descriptors[1].itemsize + + # C code using the above information to compare the strings in + # both arrays. In particular, this loop requires the `num_chars1` + # and `num_chars2`. Information which is currently not easily + # available. + + np.equal.register_impl(StringEquality) + del StringEquality # may be deleted. + + +This definition will be sufficient to create a new loop, and the +structure allows for expansion in the future; something that is already +required to implement casting within NumPy itself. +We use ``BoundArrayMethod`` and a ``context`` structure here. These +are described and motivated in details later. Briefly: + +* ``context`` is a generalization of the ``self`` that Python passes to its + methods. +* ``BoundArrayMethod`` is equivalent to the Python distinction that + ``class.method`` is a method, while ``class().method`` returns a "bound" method. + + +Customizing Dispatching and Promotion +===================================== + +Finding the correct implementation when ``np.positive.resolve_impl()`` is +called is largely an implementation detail. +But, in some cases it may be necessary to influence this process when no +implementation matches the requested DTypes exactly: + +.. code-block:: python + + np.multiple.resolve_impl((Timedelta64, Int8, None)) + +will not have an exact match, because NumPy only has an implementation for +multiplying ``Timedelta64`` with ``Int64``. +In simple cases, NumPy will use a default promotion step to attempt to find +the correct implementation, but to implement the above step, we will allow +the following: + +.. code-block:: python + + def promote_timedelta_integer(ufunc, dtypes): + new_dtypes = (Timdelta64, Int64, dtypes[-1]) + # Resolve again, using Int64: + return ufunc.resolve_impl(new_dtypes) + + np.multiple.register_promoter( + (Timedelta64, Integer, None), promote_timedelta_integer) + +Where ``Integer`` is an abstract DType (compare NEP 42). + + +.. _steps_of_a_ufunc_call: + +**************************************************************************** +Steps involved in a UFunc call +**************************************************************************** + +Before going into more detailed API choices, it is helpful to review the +steps involved in a call to a universal function in NumPy. + +A UFunc call is split into the following steps: + +1. Handle ``__array_ufunc__`` protocol: + + * For array-likes such as a Dask arrays, NumPy can defer the operation. + This step is performed first, and unaffected by this NEP (compare :ref:`NEP18`). + +2. Promotion and dispatching + + * Given the DTypes of all inputs, find the correct implementation. + E.g. an implementation for ``float64``, ``int64`` or a user-defined DType. + + * When no exact implementation exists, *promotion* has to be performed. + For example, adding a ``float32`` and a ``float64`` is implemented by + first casting the ``float32`` to ``float64``. + +3. Parametric ``dtype`` resolution: + + * In general, whenever an output DType is parametric the parameters have + to be found (resolved). + * For example, if a loop adds two strings, it is necessary to define the + correct output (and possibly input) dtypes. ``S5 + S4 -> S9``, while + an ``upper`` function has the signature ``S5 -> S5``. + * When they are not parametric, a default implementation is provided + which fills in the default dtype instances (ensuring for example native + byte order). + +4. Preparing the iteration: + + * This step is largely handled by ``NpyIter`` internally (the iterator). + * Allocate all outputs and temporary buffers necessary to perform casts. + This *requires* the dtypes as resolved in step 3. + * Find the best iteration order, which includes information to efficiently + implement broadcasting. For example, adding a single value to an array + repeats the same value. + +5. Setup and fetch the C-level function: + + * If necessary, allocate temporary working space. + * Find the C-implemented, light weight, inner-loop function. + Finding the inner-loop function can allow specialized implementations + in the future. + For example casting currently optimizes contiguous casts and + reductions have optimizations that are currently handled + inside the inner-loop function itself. + * Signal whether the inner-loop requires the Python API or whether + the GIL may be released (to allow threading). + * Clear floating point exception flags. + +6. Perform the actual calculation: + + * Run the DType specific inner-loop function. + * The inner-loop may require access to additional data, such as dtypes or + additional data set in the previous step. + * The inner-loop function may be called an undefined number of times. + +7. Finalize: + + * Free any temporary working space allocated in step 5. + * Check for floating point exception flags. + * Return the result. + +The ``ArrayMethod`` provides a concept to group steps 3 to 6 and partially 7. +However, implementers of a new ufunc or ``ArrayMethod`` usually do not need to +customize the behaviour in steps 4 or 6 which NumPy can and does provide. +For the ``ArrayMethod`` implementer, the central steps to customize +are step 3 and step 5. These provide the custom inner-loop function and +potentially inner-loop specific setup. +Further customization is possible and anticipated as future extensions. + +Step 2. is promotion and dispatching and will be restructured +with new API to allow customization of the process where necessary. + +Step 1 is listed for completeness and is unaffected by this NEP. + +The following sketch provides an overview of step 2 to 6 with an emphasize +of how dtypes are handled and which parts are customizable ("Registered") +and which are handled by NumPy: + +.. figure:: _static/nep43-sketch.svg + :figclass: align-center + + +***************************************************************************** +ArrayMethod +***************************************************************************** + +A central proposal of this NEP is the creation of the ``ArrayMethod`` as an object +describing each implementation specific to a given set of DTypes. +We use the ``class`` syntax to describe the information required to create +a new ``ArrayMethod`` object: + +.. code-block:: python + + class ArrayMethod: + name: str # Name, mainly useful for debugging + + # Casting safety information (almost always "safe", necessary to + # unify casting and universal functions) + casting: Casting = "no" + + # More general flags: + flags: int + + def resolve_descriptors(self, + Tuple[DTypeMeta], Tuple[DType|None]: given_descrs) -> Casting, Tuple[DType]: + """Returns the safety of the operation (casting safety) and the + """ + # A default implementation can be provided for non-parametric + # output dtypes. + raise NotImplementedError + + @staticmethod + def get_loop(Context : context, strides, ...) -> strided_loop_function, flags: + """Returns the low-level C (strided inner-loop) function which + performs the actual operation. + + This method may initially private, users will be able to provide + a set of optimized inner-loop functions instead: + + * `strided_inner_loop` + * `contiguous_inner_loop` + * `unaligned_strided_loop` + * ... + """ + raise NotImplementedError + + @staticmethod + def strided_inner_loop( + Context : context, data, dimensions, strides, innerloop_data): + """The inner-loop (equivalent to the current ufunc loop) + which is returned by the default `get_loop()` implementation.""" + raise NotImplementedError + +With ``Context`` providing mostly static information about the function call: + +.. code-block:: python + + class Context: + # The ArrayMethod object itself: + ArrayMethod : method + + # Information about the caller, e.g. the ufunc, such as `np.add`: + callable : caller = None + # The number of input arguments: + int : nin = 1 + # The number of output arguments: + int : nout = 1 + # The actual dtypes instances the inner-loop operates on: + Tuple[DType] : descriptors + + # Any additional information required. In the future, this will + # generalize or duplicate things currently stored on the ufunc: + # - The ufunc signature of generalized ufuncs + # - The identity used for reductions + +And ``flags`` stored properties, for whether: + +* the ``ArrayMethod`` supports unaligned input and output arrays +* the inner-loop function requires the Python API (GIL) +* NumPy has to check the floating point error CPU flags. + +*Note: More information is expected to be added as necessary.* + + +The call ``Context`` +==================== + +The "context" object is analogous to Python's ``self`` that is +passed to all methods. +To understand why the "context" object is necessary and its +internal structure, it is helpful to remember +that a Python method can be written in the following way +(see also the `documentation of __get__ +<https://docs.python.org/3.8/reference/datamodel.html#object.__get__>`_): + +.. code-block:: python + + class BoundMethod: + def __init__(self, instance, method): + self.instance = instance + self.method = method + + def __call__(self, *args, **kwargs): + return self.method.function(self.instance, *args, **kwargs) + + + class Method: + def __init__(self, function): + self.function = function + + def __get__(self, instance, owner=None): + assert instance is not None # unsupported here + return BoundMethod(instance, self) + + +With which the following ``method1`` and ``method2`` below, behave identically: + +.. code-block:: python + + def function(self): + print(self) + + class MyClass: + def method1(self): + print(self) + + method2 = Method(function) + +And both will print the same result: + +.. code-block:: python + + >>> myinstance = MyClass() + >>> myinstance.method1() + <__main__.MyClass object at 0x7eff65436d00> + >>> myinstance.method2() + <__main__.MyClass object at 0x7eff65436d00> + +Here ``self.instance`` would be all information passed on by ``Context``. +The ``Context`` is a generalization and has to pass additional information: + +* Unlike a method which operates on a single class instance, the ``ArrayMethod`` + operates on many input arrays and thus multiple dtypes. +* The ``__call__`` of the ``BoundMethod`` above contains only a single call + to a function. But an ``ArrayMethod`` has to call ``resolve_descriptors`` + and later pass on that information to the inner-loop function. +* A Python function has no state except that defined by its outer scope. + Within C, ``Context`` is able to provide additional state if necessary. + +Just as Python requires the distinction of a method and a bound method, +NumPy will have a ``BoundArrayMethod``. +This stores all of the constant information that is part of the ``Context``, +such as: + +* the ``DTypes`` +* the number of input and output arguments +* the ufunc signature (specific to generalized ufuncs, compare :ref:`NEP20`). + +Fortunately, most users and even ufunc implementers will not have to worry +about these internal details; just like few Python users need to know +about the ``__get__`` dunder method. +The ``Context`` object or C-structure provides all necessary data to the +fast C-functions and NumPy API creates the new ``ArrayMethod`` or +``BoundArrayMethod`` as required. + + +.. _ArrayMethod_specs: + +ArrayMethod Specifications +========================== + +.. highlight:: c + +These specifications provide a minimal initial C-API, which shall be expanded +in the future, for example to allow specialized inner-loops. + +Briefly, NumPy currently relies on strided inner-loops and this +will be the only allowed method of defining a ufunc initially. +We expect the addition of a ``setup`` function or exposure of ``get_loop`` +in the future. + +UFuncs require the same information as casting, giving the following +definitions (see also :ref:`NEP 42 <NEP42>` ``CastingImpl``): + +* A new structure to be passed to the resolve function and inner-loop:: + + typedef struct { + PyObject *caller; /* The ufunc object */ + PyArrayMethodObject *method; + + int nin, nout; + + PyArray_DTypeMeta **dtypes; + /* Operand descriptors, filled in by resolve_desciptors */ + PyArray_Descr **descriptors; + + void *reserved; // For Potential in threading (Interpreter state) + } PyArrayMethod_Context + + This structure may be appended to include additional information in future + versions of NumPy and includes all constant loop metadata. + + We could version this structure, although it may be simpler to version + the ``ArrayMethod`` itself. + +* Similar to casting, ufuncs may need to find the correct loop dtype + or indicate that a loop is only capable of handling certain instances of + the involved DTypes (e.g. only native byteorder). This is handled by + a ``resolve_descriptors`` function (identical to the ``resolve_descriptors`` + of ``CastingImpl``):: + + NPY_CASTING + resolve_descriptors( + PyArrayMethodObject *self, + PyArray_DTypeMeta *dtypes, + PyArray_Descr *given_dtypes[nin+nout], + PyArray_Descr *loop_dtypes[nin+nout]); + + The function fills ``loop_dtypes`` based on the given ``given_dtypes``. + This requires filling in the descriptor of the output(s). + Often also the input descriptor(s) have to be found, e.g. to ensure native + byteorder when needed by the inner-loop. + + In most cases an ``ArrayMethod`` will have non-parametric output DTypes + so that a default implementation can be provided. + +* An additional ``void *user_data`` will usually be typed to extend + the existing ``NpyAuxData *`` struct:: + + struct { + NpyAuxData_FreeFunc *free; + NpyAuxData_CloneFunc *clone; + /* To allow for a bit of expansion without breaking the ABI */ + void *reserved[2]; + } NpyAuxData; + + This struct is currently mainly used for the NumPy internal casting + machinery and as of now both ``free`` and ``clone`` must be provided, + although this could be relaxed. + + Unlike NumPy casts, the vast majority of ufuncs currently do not require + this additional scratch-space, but may need simple flagging capability + for example for implementing warnings (see Error and Warning Handling below). + To simplify this NumPy will pass a single zero initialized ``npy_intp *`` + when ``user_data`` is not set. + *Note that it would be possible to pass this as part of Context.* + +* The optional ``get_loop`` function will not be public initially, to avoid + finalizing the API which requires design choices also with casting: + + .. code-block:: C + + innerloop * + get_loop( + PyArrayMethod_Context *context, + int aligned, int move_references, + npy_intp *strides, + PyArray_StridedUnaryOp **out_loop, + NpyAuxData **innerloop_data, + NPY_ARRAYMETHOD_FLAGS *flags); + + ``NPY_ARRAYMETHOD_FLAGS`` can indicate whether the Python API is required + and floating point errors must be checked. ``move_references`` is used + internally for NumPy casting at this time. + +* The inner-loop function:: + + int inner_loop(PyArrayMethod_Context *context, ..., void *innerloop_data); + + Will have the identical signature to current inner-loops with the following + changes: + + * A return value to indicate an error when returning ``-1`` instead of ``0``. + When returning ``-1`` a Python error must be set. + * The new first argument ``PyArrayMethod_Context *`` is used to pass in + potentially required information about the ufunc or descriptors in a + convenient way. + * The ``void *innerloop_data`` will be the ``NpyAuxData **innerloop_data`` as set by + ``get_loop``. If ``get_loop`` does not set ``innerloop_data`` an ``npy_intp *`` + is passed instead (see `Error Handling`_ below for the motivation). + + *Note:* Since ``get_loop`` is expected to be private, the exact implementation + of ``innerloop_data`` can be modified until final exposure. + +Creation of a new ``BoundArrayMethod`` will use a ``PyArrayMethod_FromSpec()`` +function. A shorthand will allow direct registration to a ufunc using +``PyUFunc_AddImplementationFromSpec()``. The specification is expected +to contain the following (this may extend in the future):: + + typedef struct { + const char *name; /* Generic name, mainly for debugging */ + int nin, nout; + NPY_CASTING casting; + NPY_ARRAYMETHOD_FLAGS flags; + PyArray_DTypeMeta **dtypes; + PyType_Slot *slots; + } PyArrayMethod_Spec; + +.. highlight:: python + +Discussion and alternatives +=========================== + +The above split into an ``ArrayMethod`` and ``Context`` and the additional +requirement of a ``BoundArrayMethod`` is a necessary split mirroring the +implementation of methods and bound methods in Python. + +One reason for this requirement is that it allows storing the ``ArrayMethod`` +object in many cases without holding references to the ``DTypes`` which may +be important if DTypes are created (and deleted) dynamically. +(This is a complex topic, which does not have a complete solution in current +Python, but the approach solves the issue with respect to casting.) + +There seem to be no alternatives to this structure. Separating the +DType-specific steps from the general ufunc dispatching and promotion is +absolutely necessary to allow future extension and flexibility. +Furthermore, it allows unifying casting and ufuncs. + +Since the structure of ``ArrayMethod`` and ``BoundArrayMethod`` will be +opaque and can be extended, there are few long-term design implications aside +from the choice of making them Python objects. + + +``resolve_descriptors`` +----------------------- + +The ``resolve_descriptors`` method is possibly the main innovation of this +NEP and it is central also in the implementation of casting in NEP 42. + +By ensuring that every ``ArrayMethod`` provides ``resolve_descriptors`` we +define a unified, clear API for step 3 in `Steps involved in a UFunc call`_. +This step is required to allocate output arrays and has to happen before +casting can be prepared. + +While the returned casting-safety (``NPY_CASTING``) will almost always be +"no" for universal functions, including it has two big advantages: + +* ``-1`` indicates that an error occurred. If a Python error is set, it will + be raised. If no Python error is set this will be considered an "impossible" + cast and a custom error will be set. (This distinction is important for the + ``np.can_cast()`` function, which should raise the first one and return + ``False`` in the second case, it is not noteworthy for typical ufuncs). + *This point is under consideration, we may use -1 to indicate + a general error, and use a different return value for an impossible cast.* +* Returning the casting safety is central to NEP 42 for casting and + allows the unmodified use of ``ArrayMethod`` there. +* There may be a future desire to implement fast but unsafe implementations. + For example for ``int64 + int64 -> int32`` which is unsafe from a casting + perspective. Currently, this would use ``int64 + int64 -> int64`` and then + cast to ``int32``. An implementation that skips the cast would + have to signal that it effectively includes the "same-kind" cast and is + thus not considered "no". + + +``get_loop`` method +------------------- + +Currently, NumPy ufuncs typically only provide a single strided loop, so that +the ``get_loop`` method may seem unnecessary. +For this reason we plan for ``get_loop`` to be a private function initially. + +However, ``get_loop`` is required for casting where specialized loops are +used even beyond strided and contiguous loops. +Thus, the ``get_loop`` function must be a full replacement for +the internal ``PyArray_GetDTypeTransferFunction``. + +In the future, ``get_loop`` may be made public or a new ``setup`` function +be exposed to allow more control, for example to allow allocating +working memory. +Further, we could expand ``get_loop`` and allow the ``ArrayMethod`` implementer +to also control the outer iteration and not only the 1-D inner-loop. + + +Extending the inner-loop signature +---------------------------------- + +Extending the inner-loop signature is another central and necessary part of +the NEP. + +**Passing in the Context:** + +Passing in the ``Context`` potentially allows for the future extension of +the signature by adding new fields to the context struct. +Furthermore it provides direct access to the dtype instances which +the inner-loop operates on. +This is necessary information for parametric dtypes since for example comparing +two strings requires knowing the length of both strings. +The ``Context`` can also hold potentially useful information such as the +the original ``ufunc``, which can be helpful when reporting errors. + +In principle passing in Context is not necessary, as all information could be +included in ``innerloop_data`` and set up in the ``get_loop`` function. +In this NEP we propose passing the struct to simplify creation of loops for +parametric DTypes. + +**Passing in user data:** + +The current casting implementation uses the existing ``NpyAuxData *`` to pass +in additional data as defined by ``get_loop``. +There are certainly alternatives to the use of this structure, but it +provides a simple solution, which is already used in NumPy and public API. + +``NpyAyxData *`` is a light weight, allocated structure and since it already +exists in NumPy for this purpose, it seems a natural choice. +To simplify some use-cases (see "Error Handling" below), we will pass a +``npy_intp *innerloop_data = 0`` instead when ``innerloop_data`` is not provided. + +*Note:* Since ``get_loop`` is expected to be private initially we can gain +experience with ``innerloop_data`` before exposing it as public API. + +**Return value:** + +The return value to indicate an error is an important, but currently missing +feature in NumPy. The error handling is further complicated by the way +CPUs signal floating point errors. +Both are discussed in the next section. + +Error Handling +"""""""""""""" + +.. highlight:: c + +We expect that future inner-loops will generally set Python errors as soon +as an error is found. This is complicated when the inner-loop is run without +locking the GIL. In this case the function will have to lock the GIL, +set the Python error and return ``-1`` to indicate an error occurred::: + + int + inner_loop(PyArrayMethod_Context *context, ..., void *innerloop_data) + { + NPY_ALLOW_C_API_DEF + + for (npy_intp i = 0; i < N; i++) { + /* calculation */ + + if (error_occurred) { + NPY_ALLOW_C_API; + PyErr_SetString(PyExc_ValueError, + "Error occurred inside inner_loop."); + NPY_DISABLE_C_API + return -1; + } + } + return 0; + } + +Floating point errors are special, since they require checking the hardware +state which is too expensive if done within the inner-loop function itself. +Thus, NumPy will handle these if flagged by the ``ArrayMethod``. +An ``ArrayMethod`` should never cause floating point error flags to be set +if it flags that these should not be checked. This could interfere when +calling multiple functions; in particular when casting is necessary. + +An alternative solution would be to allow setting the error only at the later +finalization step when NumPy will also check the floating point error flags. + +We decided against this pattern at this time. It seems more complex and +generally unnecessary. +While safely grabbing the GIL in the loop may require passing in an additional +``PyThreadState`` or ``PyInterpreterState`` in the future (for subinterpreter +support), this is acceptable and can be anticipated. +Setting the error at a later point would add complexity: for instance +if an operation is paused (which can currently happen for casting in particular), +the error check needs to run explicitly ever time this happens. + +We expect that setting errors immediately is the easiest and most convenient +solution and more complex solution may be possible future extensions. + +Handling *warnings* is slightly more complex: A warning should be +given exactly once for each function call (i.e. for the whole array) even +if naively it would be given many times. +To simplify such a use case, we will pass in ``npy_intp *innerloop_data = 0`` +by default which can be used to store flags (or other simple persistent data). +For instance, we could imagine an integer multiplication loop which warns +when an overflow occurred:: + + int + integer_multiply(PyArrayMethod_Context *context, ..., npy_intp *innerloop_data) + { + int overflow; + NPY_ALLOW_C_API_DEF + + for (npy_intp i = 0; i < N; i++) { + *out = multiply_integers(*in1, *in2, &overflow); + + if (overflow && !*innerloop_data) { + NPY_ALLOW_C_API; + if (PyErr_Warn(PyExc_UserWarning, + "Integer overflow detected.") < 0) { + NPY_DISABLE_C_API + return -1; + } + *innerloop_data = 1; + NPY_DISABLE_C_API + } + return 0; + } + +*TODO:* The idea of passing an ``npy_intp`` scratch space when ``innerloop_data`` +is not set seems convenient, but I am uncertain about it, since I am not +aware of any similar prior art. This "scratch space" could also be part of +the ``context`` in principle. + +.. highlight:: python + +Reusing existing Loops/Implementations +====================================== + +For many DTypes the above definition for adding additional C-level loops will be +sufficient and require no more than a single strided loop implementation +and if the loop works with parametric DTypes, the +``resolve_descriptors`` function *must* additionally be provided. + +However, in some use-cases it is desirable to call back to an existing implementation. +In Python, this could be achieved by simply calling into the original ufunc. + +For better performance in C, and for large arrays, it is desirable to reuse +an existing ``ArrayMethod`` as directly as possible, so that its inner-loop function +can be used directly without additional overhead. +We will thus allow to create a new, wrapping, ``ArrayMethod`` from an existing +``ArrayMethod``. + +This wrapped ``ArrayMethod`` will have two additional methods: + +* ``view_inputs(Tuple[DType]: input_descr) -> Tuple[DType]`` replacing the + user input descriptors with descriptors matching the wrapped loop. + It must be possible to *view* the inputs as the output. + For example for ``Unit[Float64]("m") + Unit[Float32]("km")`` this will + return ``float64 + int32``. The original ``resolve_descriptors`` will + convert this to ``float64 + float64``. + +* ``wrap_outputs(Tuple[DType]: input_descr) -> Tuple[DType]`` replacing the + resolved descriptors with with the desired actual loop descriptors. + The original ``resolve_descriptors`` function will be called between these + two calls, so that the output descriptors may not be set in the first call. + In the above example it will use the ``float64`` as returned (which might + have changed the byte-order), and further resolve the physical unit making + the final signature:: + + Unit[Float64]("m") + Unit[Float64]("m") -> Unit[Float64]("m") + + the UFunc machinery will take care of casting the "km" input to "m". + + +The ``view_inputs`` method allows passing the correct inputs into the +original ``resolve_descriptors`` function, while ``wrap_outputs`` ensures +the correct descriptors are used for output allocation and input buffering casts. + +An important use-case for this is that of an abstract Unit DType +with subclasses for each numeric dtype (which could be dynamically created):: + + Unit[Float64]("m") + # with Unit[Float64] being the concrete DType: + isinstance(Unit[Float64], Unit) # is True + +Such a ``Unit[Float64]("m")`` instance has a well-defined signature with +respect to type promotion. +The author of the ``Unit`` DType can implement most necessary logic by +wrapping the existing math functions and using the two additional methods +above. +Using the *promotion* step, this will allow to create a register a single +promoter for the abstract ``Unit`` DType with the ``ufunc``. +The promoter can then add the wrapped concrete ``ArrayMethod`` dynamically +at promotion time, and NumPy can cache (or store it) after the first call. + +**Alternative use-case:** + +A different use-case is that of a ``Unit(float64, "m")`` DType, where +the numerical type is part of the DType parameter. +This approach is possible, but will require a custom ``ArrayMethod`` +which wraps existing loops. +It must also always require require two steps of dispatching +(one to the ``Unit`` DType and a second one for the numerical type). + +Furthermore, the efficient implementation will require the ability to +fetch and reuse the inner-loop function from another ``ArrayMethod``. +(Which is probably necessary for users like Numba, but it is uncertain +whether it should be a common pattern and it cannot be accessible from +Python itself.) + + +.. _promotion_and_dispatching: + +************************* +Promotion and dispatching +************************* + +NumPy ufuncs are multi-methods in the sense that they operate on (or with) +multiple DTypes at once. +While the input (and output) dtypes are attached to NumPy arrays, +the ``ndarray`` type itself does not carry the information of which +function to apply to the data. + +For example, given the input:: + + int_arr = np.array([1, 2, 3], dtype=np.int64) + float_arr = np.array([1, 2, 3], dtype=np.float64) + np.add(int_arr, float_arr) + +has to find the correct ``ArrayMethod`` to perform the operation. +Ideally, there is an exact match defined, e.g. for ``np.add(int_arr, int_arr)`` +the ``ArrayMethod[Int64, Int64, out=Int64]`` matches exactly and can be used. +However, for ``np.add(int_arr, float_arr)`` there is no direct match, +requiring a promotion step. + +Promotion and dispatching process +================================= + +In general the ``ArrayMethod`` is found by searching for an exact match of +all input DTypes. +The output dtypes should *not* affect calculation, but if multiple registered +``ArrayMethod``\ s match exactly, the output DType will be used to find the +better match. +This will allow the current distinction for ``np.equal`` loops which define +both ``Object, Object -> Bool`` (default) and ``Object, Object -> Object``. + +Initially, an ``ArrayMethod`` will be defined for *concrete* DTypes only +and since these cannot be subclassed an exact match is guaranteed. +In the future we expect that ``ArrayMethod``\ s can also be defined for +*abstract* DTypes. In which case the best match is found as detailed below. + +**Promotion:** + +If a matching ``ArrayMethod`` exists, dispatching is straight forward. +However, when it does not, additional definitions are required to implement +this "promotion": + +* By default any UFunc has a promotion which uses the common DType of all + inputs and dispatches a second time. This is well-defined for most + mathematical functions, but can be disabled or customized if necessary. + For instances ``int32 + float64`` tries again using ``float64 + float64`` + which is the common DType. + +* Users can *register* new Promoters just as they can register a + new ``ArrayMethod``. These will use abstract DTypes to allow matching + a large variety of signatures. + The return value of a promotion function shall be a new ``ArrayMethod`` + or ``NotImplemented``. It must be consistent over multiple calls with + the same input to allow caching of the result. + +The signature of a promotion function would be:: + + promoter(np.ufunc: ufunc, Tuple[DTypeMeta]: DTypes): -> Union[ArrayMethod, NotImplemented] + +Note that DTypes may include the output's DType, however, normally the +output DType will *not* affect which ``ArrayMethod`` is chosen. + +In most cases, it should not be necessary to add a custom promotion function. +An example which requires this is multiplication with a unit: +in NumPy ``timedelta64`` can be multiplied with most integers, +but NumPy only defines a loop (``ArrayMethod``) for ``timedelta64 * int64`` +so that multiplying with ``int32`` would fail. + +To allow this, the following promoter can be registered for +``(Timedelta64, Integral, None)``:: + + def promote(ufunc, DTypes): + res = list(DTypes) + try: + res[1] = np.common_dtype(DTypes[1], Int64) + except TypeError: + return NotImplemented + + # Could check that res[1] is actually Int64 + return ufunc.resolve_impl(tuple(res)) + +In this case, just as a ``Timedelta64 * int64`` and ``int64 * timedelta64`` +``ArrayMethod`` is necessary, a second promoter will have to be registered to +handle the case where the integer is passed first. + +**Dispatching rules for ArrayMethod and Promoters:** + +Promoter and ``ArrayMethod`` are discovered by finding the best match as +defined by the DType class hierarchy. +The best match is defined if: + +* The signature matches for all input DTypes, so that + ``issubclass(input_DType, registered_DType)`` returns true. +* No other promoter or ``ArrayMethod`` is more precise in any input: + ``issubclass(other_DType, this_DType)`` is true (this may include if both + are identical). +* This promoter or ``ArrayMethod`` is more precise in at least one input or + output DType. + +It will be an error if ``NotImplemented`` is returned or if two +promoters match the input equally well. +When an existing promoter is not precise enough for new functionality, a +new promoter has to be added. +To ensure that this promoter takes precedence it may be necessary to define +new abstract DTypes as more precise subclasses of existing ones. + +The above rules enable specialization if an output is supplied +or the full loop is specified. This should not typically be necessary, +but allows resolving ``np.logic_or``, etc. which have both +``Object, Object -> Bool`` and ``Object, Object -> Object`` loops (using the +first by default). + + +Discussion and alternatives +=========================== + +Instead of resolving and returning a new implementation, we could also +return a new set of DTypes to use for dispatching. This works, however, +it has the disadvantage that it is impossible to dispatch to a loop +defined on a different ufunc or to dynamically create a new ``ArrayMethod``. + + +**Rejected Alternatives:** + +In the above the promoters use a multiple dispatching style type resolution +while the current UFunc machinery uses the first +"safe" loop (see also :ref:`NEP 40 <NEP40>`) in an ordered hierarchy. + +While the "safe" casting rule is not restrictive enough, we could imagine +using a new "promote" casting rule, or the common-DType logic to find the +best matching loop by upcasting the inputs as necessary. + +One downside to this approach is that upcasting alone allows upcasting the +result beyond what is expected by users: +Currently (which will remain supported as a fallback) any ufunc which defines +only a float64 loop will also work for float16 and float32 by *upcasting*:: + + >>> from scipy.special import erf + >>> erf(np.array([4.], dtype=np.float16)) # float16 + array([1.], dtype=float32) + +with a float32 result. It is impossible to change the ``erf`` function to +return a float16 result without changing the result of following code. +In general, we argue that automatic upcasting should not occur in cases +where a less precise loop can be defined, *unless* the ufunc +author does this intentionally using a promotion. + +This consideration means that upcasting has to be limited by some additional +method. + +*Alternative 1:* + +Assuming general upcasting is not intended, a rule must be defined to +limit upcasting the input from ``float16 -> float32`` either using generic +logic on the DTypes or the UFunc itself (or a combination of both). +The UFunc cannot do this easily on its own, since it cannot know all possible +DTypes which register loops. +Consider the two examples: + +First (should be rejected): + +* Input: ``float16 * float16`` +* Existing loop: ``float32 * float32`` + +Second (should be accepted): + +* Input: ``timedelta64 * int32`` +* Existing loop: ``timedelta64 * int16`` + + +This requires either: + +1. The ``timedelta64`` to somehow signal that the ``int64`` upcast is + always supported if it is involved in the operation. +2. The ``float32 * float32`` loop to reject upcasting. + +Implementing the first approach requires signaling that upcasts are +acceptable in the specific context. This would require additional hooks +and may not be simple for complex DTypes. + +For the second approach in most cases a simple ``np.common_dtype`` rule will +work for initial dispatching, however, even this is only clearly the case +for homogeneous loops. +This option will require adding a function to check whether the input +is a valid upcast to each loop individually, which seems problematic. +In many cases a default could be provided (homogeneous signature). + +*Alternative 2:* + +An alternative "promotion" step is to ensure that the *output* DType matches +with the loop after first finding the correct output DType. +If the output DTypes are known, finding a safe loop becomes easy. +In the majority of cases this works, the correct output dtype is just:: + + np.common_dtype(*input_DTypes) + +or some fixed DType (e.g. Bool for logical functions). + +However, it fails for example in the ``timedelta64 * int32`` case above since +there is a-priori no way to know that the "expected" result type of this +output is indeed ``timedelta64`` (``np.common_dtype(Datetime64, Int32)`` fails). +This requires some additional knowledge of the timedelta64 precision being +int64. Since a ufunc can have an arbitrary number of (relevant) inputs +it would thus at least require an additional ``__promoted_dtypes__`` method +on ``Datetime64`` (and all DTypes). + +A further limitation is shown by masked DTypes. Logical functions do not +have a boolean result when masked are involved, which would thus require the +original ufunc author to anticipate masked DTypes in this scheme. +Similarly, some functions defined for complex values will return real numbers +while others return complex numbers. If the original author did not anticipate +complex numbers, the promotion may be incorrect for a later added complex loop. + + +We believe that promoters, while allowing for an huge theoretical complexity, +are the best solution: + +1. Promotion allows for dynamically adding new loops. E.g. it is possible + to define an abstract Unit DType, which dynamically creates classes to + wrap other existing DTypes. Using a single promoter, this DType can + dynamically wrap existing ``ArrayMethod`` enabling it to find the correct + loop in a single lookup instead of two. +2. The promotion logic will usually err on the safe side: A newly-added + loop cannot be misused unless a promoter is added as well. +3. They put the burden of carefully thinking of whether the logic is correct + on the programmer adding new loops to a UFunc. (Compared to Alternative 2) +4. In case of incorrect existing promotion, writing a promoter to restrict + or refine a generic rule is possible. In general a promotion rule should + never return an *incorrect* promotion, but if it the existing promotion + logic fails or is incorrect for a newly-added loop, the loop can add a + new promoter to refine the logic. + +The option of having each loop verify that no upcast occurred is probably +the best alternative, but does not include the ability to dynamically +adding new loops. + +The main downsides of general promoters is that they allow a possible +very large complexity. +A third-party library *could* add incorrect promotions to NumPy, however, +this is already possible by adding new incorrect loops. +In general we believe we can rely on downstream projects to use this +power and complexity carefully and responsibly. + + +*************** +User Guidelines +*************** + +In general adding a promoter to a UFunc must be done very carefully. +A promoter should never affect loops which can be reasonably defined +by other datatypes. Defining a hypothetical ``erf(UnitFloat16)`` loop +must not lead to ``erf(float16)``. +In general a promoter should fulfill the following requirements: + +* Be conservative when defining a new promotion rule. An incorrect result + is a much more dangerous error than an unexpected error. +* One of the (abstract) DTypes added should typically match specifically with a + DType (or family of DTypes) defined by your project. + Never add promotion rules which go beyond normal common DType rules! + It is *not* reasonable to add a loop for ``int16 + uint16 -> int24`` if + you write an ``int24`` dtype. The result of this operation was already + defined previously as ``int32`` and will be used with this assumption. +* A promoter (or loop) should never affect existing loop results. + This includes adding faster but less precise loops/promoters to replace + existing ones. +* Try to stay within a clear, linear hierarchy for all promotion (and casting) + related logic. NumPy itself breaks this logic for integers and floats + (they are not strictly linear, since int64 cannot promote to float32). +* Loops and promoters can be added by any project, which could be: + + * The project defining the ufunc + * The project defining the DType + * A third-party project + + Try to find out which is the best project to add the loop. If neither + the project defining the ufunc nor the project defining the DType add the + loop, issues with multiple definitions (which are rejected) may arise + and care should be taken that the loop behaviour is always more desirable + than an error. + +In some cases exceptions to these rules may make sense, however, in general +we ask you to use extreme caution and when in doubt create a new UFunc +instead. This clearly notifies the users of differing rules. +When in doubt, ask on the NumPy mailing list or issue tracker! + + +************** +Implementation +************** + +Implementation of this NEP will entail a large refactor and restructuring +of the current ufunc machinery (as well as casting). + +The implementation unfortunately will require large maintenance of the +UFunc machinery, since both the actual UFunc loop calls, as well as the +the initial dispatching steps have to be modified. + +In general, the correct ``ArrayMethod``, also those returned by a promoter, +will be cached (or stored) inside a hashtable for efficient lookup. + + +********** +Discussion +********** + +There is a large space of possible implementations with many discussions +in various places, as well as initial thoughts and design documents. +These are listed in the discussion of :ref:`NEP 40 <NEP40>` and not repeated here for +brevity. + +A long discussion which touches many of these points and points towards +similar solutions can be found in +`the github issue 12518 "What should be the calling convention for ufunc inner loop signatures?" <https://github.com/numpy/numpy/issues/12518>`_ + + +********** +References +********** + +Please see NEP 40 and 41 for more discussion and references. + + +********* +Copyright +********* + +This document has been placed in the public domain. diff --git a/doc/neps/nep-0044-restructuring-numpy-docs.rst b/doc/neps/nep-0044-restructuring-numpy-docs.rst new file mode 100644 index 000000000000..fd41e0c2a41b --- /dev/null +++ b/doc/neps/nep-0044-restructuring-numpy-docs.rst @@ -0,0 +1,245 @@ +.. _NEP44: + +=================================================== +NEP 44 — Restructuring the NumPy documentation +=================================================== + +:Author: Ralf Gommers +:Author: Melissa Mendonça +:Author: Mars Lee +:Status: Accepted +:Type: Process +:Created: 2020-02-11 +:Resolution: https://mail.python.org/pipermail/numpy-discussion/2020-March/080467.html + +Abstract +======== + +This document proposes a restructuring of the NumPy Documentation, both in form +and content, with the goal of making it more organized and discoverable for +beginners and experienced users. + +Motivation and Scope +==================== + +See `here <https://numpy.org/devdocs/>`_ for the front page of the latest docs. +The organization is quite confusing and illogical (e.g. user and developer docs +are mixed). We propose the following: + +- Reorganizing the docs into the four categories mentioned in [1]_, namely *Tutorials*, *How Tos*, *Reference Guide* and *Explanations* (more about this below). +- Creating dedicated sections for Tutorials and How-Tos, including orientation + on how to create new content; +- Adding an Explanations section for key concepts and techniques that require + deeper descriptions, some of which will be rearranged from the Reference Guide. + +Usage and Impact +================ + +The documentation is a fundamental part of any software project, especially +open source projects. In the case of NumPy, many beginners might feel demotivated +by the current structure of the documentation, since it is difficult to discover +what to learn (unless the user has a clear view of what to look for in the +Reference docs, which is not always the case). + +Looking at the results of a "NumPy Tutorial" search on any search engine also +gives an idea of the demand for this kind of content. Having official high-level +documentation written using up-to-date content and techniques will certainly +mean more users (and developers/contributors) are involved in the NumPy +community. + +Backward compatibility +====================== + +The restructuring will effectively demand a complete rewrite of links and some +of the current content. Input from the community will be useful for identifying +key links and pages that should not be broken. + +Detailed description +==================== + +As discussed in the article [1]_, there are four categories of doc content: + +- Tutorials +- How-to guides +- Explanations +- Reference guide + +We propose to use those categories as the ones we use (for writing and +reviewing) whenever we add a new documentation section. + +The reasoning for this is that it is clearer both for +developers/documentation writers and to users where each piece of +information should go, and the scope and tone of each document. For +example, if explanations are mixed with basic tutorials, beginners +might be overwhelmed and alienated. On the other hand, if the reference +guide contains basic how-tos, it might be difficult for experienced +users to find the information they need, quickly. + +Currently, there are many blogs and tutorials on the internet about NumPy or +using NumPy. One of the issues with this is that if users search for this +information they may end up in an outdated (unofficial) tutorial before +they find the current official documentation. This can be especially +confusing, especially for beginners. Having a better infrastructure for the +documentation also aims to solve this problem by giving users high-level, +up-to-date official documentation that can be easily updated. + +Status and ideas of each type of doc content +-------------------------------------------- + +**Reference guide** + +NumPy has a quite complete reference guide. All functions are documented, most +have examples, and most are cross-linked well with *See Also* sections. Further +improving the reference guide is incremental work that can be done (and is being +done) by many people. There are, however, many explanations in the reference +guide. These can be moved to a more dedicated Explanations section on the docs. + +**How-to guides** + +NumPy does not have many how-to's. The subclassing and array ducktyping section +may be an example of a how-to. Others that could be added are: + +- Parallelization (controlling BLAS multithreading with ``threadpoolctl``, using + multiprocessing, random number generation, etc.) +- Storing and loading data (``.npy``/``.npz`` format, text formats, Zarr, HDF5, + Bloscpack, etc.) +- Performance (memory layout, profiling, use with Numba, Cython, or Pythran) +- Writing generic code that works with NumPy, Dask, CuPy, pydata/sparse, etc. + +**Explanations** + +There is a reasonable amount of content on fundamental NumPy concepts such as +indexing, vectorization, broadcasting, (g)ufuncs, and dtypes. This could be +organized better and clarified to ensure it's really about explaining the concepts +and not mixed with tutorial or how-to like content. + +There are few explanations about anything other than those fundamental NumPy +concepts. + +Some examples of concepts that could be expanded: + +- Copies vs. Views; +- BLAS and other linear algebra libraries; +- Fancy indexing. + +In addition, there are many explanations in the Reference Guide, which should be +moved to this new dedicated Explanations section. + +**Tutorials** + +There's a lot of scope for writing better tutorials. We have a new *NumPy for +absolute beginners tutorial* [3]_ (GSoD project of Anne Bonner). In addition we +need a number of tutorials addressing different levels of experience with Python +and NumPy. This could be done using engaging data sets, ideas or stories. For +example, curve fitting with polynomials and functions in ``numpy.linalg`` could +be done with the Keeling curve (decades worth of CO2 concentration in air +measurements) rather than with synthetic random data. + +Ideas for tutorials (these capture the types of things that make sense, they're +not necessarily the exact topics we propose to implement): + +- Conway's game of life with only NumPy (note: already in `Nicolas Rougier's book + <https://www.labri.fr/perso/nrougier/from-python-to-numpy/#the-game-of-life>`_) +- Using masked arrays to deal with missing data in time series measurements +- Using Fourier transforms to analyze the Keeling curve data, and extrapolate it. +- Geospatial data (e.g. lat/lon/time to create maps for every year via a stacked + array, like `gridMet data <http://www.climatologylab.org/gridmet.html>`_) +- Using text data and dtypes (e.g. use speeches from different people, shape + ``(n_speech, n_sentences, n_words)``) + +The *Preparing to Teach* document [2]_ from the Software Carpentry Instructor +Training materials is a nice summary of how to write effective lesson plans (and +tutorials would be very similar). In addition to adding new tutorials, we also +propose a *How to write a tutorial* document, which would help users contribute +new high-quality content to the documentation. + +Data sets +--------- + +Using interesting data in the NumPy docs requires giving all users access to +that data, either inside NumPy or in a separate package. The former is not the +best idea, since it's hard to do without increasing the size of NumPy +significantly. Even for SciPy there has so far been no consensus on this (see +`scipy PR 8707 <https://github.com/scipy/scipy/pull/8707>`_ on adding a new +``scipy.datasets`` subpackage). + +So we'll aim for a new (pure Python) package, named ``numpy-datasets`` or +``scipy-datasets`` or something similar. That package can take some lessons from +how, e.g., scikit-learn ships data sets. Small data sets can be included in the +repo, large data sets can be accessed via a downloader class or function. + +Related Work +============ + +Some examples of documentation organization in other projects: + +- `Documentation for Jupyter <https://jupyter.org/documentation>`_ +- `Documentation for Python <https://docs.python.org/3/>`_ +- `Documentation for TensorFlow <https://www.tensorflow.org/learn>`_ + +These projects make the intended audience for each part of the documentation +more explicit, as well as previewing some of the content in each section. + +Implementation +============== + +Currently, the `documentation for NumPy <https://numpy.org/devdocs/>`_ can be +confusing, especially for beginners. Our proposal is to reorganize the docs in +the following structure: + +- For users: + - Absolute Beginners Tutorial + - main Tutorials section + - How Tos for common tasks with NumPy + - Reference Guide (API Reference) + - Explanations + - F2Py Guide + - Glossary +- For developers/contributors: + - Contributor's Guide + - Under-the-hood docs + - Building and extending the documentation + - Benchmarking + - NumPy Enhancement Proposals +- Meta information + - Reporting bugs + - Release Notes + - About NumPy + - License + +Ideas for follow-up +------------------- + +Besides rewriting the current documentation to some extent, it would be ideal +to have a technical infrastructure that would allow more contributions from the +community. For example, if Jupyter Notebooks could be submitted as-is as +tutorials or How-Tos, this might create more contributors and broaden the NumPy +community. + +Similarly, if people could download some of the documentation in Notebook +format, this would certainly mean people would use less outdated material for +learning NumPy. + +It would also be interesting if the new structure for the documentation makes +translations easier. + +Discussion +========== + +Discussion around this NEP can be found on the NumPy mailing list: + +- https://mail.python.org/pipermail/numpy-discussion/2020-February/080419.html + +References and Footnotes +======================== + +.. [1] `What nobody tells you about documentation <https://www.divio.com/blog/documentation/>`_ + +.. [2] `Preparing to Teach <https://carpentries.github.io/instructor-training/15-lesson-study/index.html>`_ (from the `Software Carpentry <https://software-carpentry.org/>`_ Instructor Training materials) + +.. [3] `NumPy for absolute beginners Tutorial <https://numpy.org/devdocs/user/absolute_beginners.html>`_ by Anne Bonner + +Copyright +========= + +This document has been placed in the public domain. diff --git a/doc/neps/nep-0045-c_style_guide.rst b/doc/neps/nep-0045-c_style_guide.rst new file mode 100644 index 000000000000..9a6323873300 --- /dev/null +++ b/doc/neps/nep-0045-c_style_guide.rst @@ -0,0 +1,266 @@ +.. _NEP45: + +================================= +NEP 45 — C style guide +================================= + +:Author: Charles Harris <charlesr.harris@gmail.com> +:Status: Accepted +:Type: Process +:Created: 2012-02-26 +:Resolution: https://github.com/numpy/numpy/issues/11911 + +.. highlight:: c + +Abstract +-------- + +This document gives coding conventions for the C code comprising +the C implementation of NumPy. + +Motivation and Scope +-------------------- + +The NumPy C coding conventions are based on Python +`PEP 7 -- Style Guide for C Code <https://www.python.org/dev/peps/pep-0007>`_ +by Guido van Rossum with a few added strictures. + +Because the NumPy conventions are very close to those in PEP 7, that PEP is +used as a template with the NumPy additions and variations in the appropriate +spots. + +Usage and Impact +---------------- + +There are many C coding conventions and it must be emphasized that the primary +goal of the NumPy conventions isn't to choose the "best," about which there is +certain to be disagreement, but to achieve uniformity. + +Two good reasons to break a particular rule: + +1. When applying the rule would make the code less readable, even + for someone who is used to reading code that follows the rules. + +2. To be consistent with surrounding code that also breaks it + (maybe for historic reasons) -- although this is also an + opportunity to clean up someone else's mess. + + +Backward compatibility +---------------------- + +No impact. + + +Detailed description +-------------------- + +C dialect +========= + +* Use C99 (that is, the standard defined by ISO/IEC 9899:1999). + +* Don't use GCC extensions (for instance, don't write multi-line strings + without trailing backslashes). Preferably break long strings + up onto separate lines like so:: + + "blah blah" + "blah blah" + + This will work with MSVC, which otherwise chokes on very long + strings. + +* All function declarations and definitions must use full prototypes (that is, + specify the types of all arguments). + +* No compiler warnings with major compilers (gcc, VC++, a few others). + +.. note:: + NumPy still produces compiler warnings that need to be addressed. + +Code layout +============ + +* Use 4-space indents and no tabs at all. + +* No line should be longer than 80 characters. If this and the + previous rule together don't give you enough room to code, your code is + too complicated -- consider using subroutines. + +* No line should end in whitespace. If you think you need + significant trailing whitespace, think again; somebody's editor might + delete it as a matter of routine. + +* Function definition style: function name in column 1, outermost + curly braces in column 1, blank line after local variable declarations:: + + static int + extra_ivars(PyTypeObject *type, PyTypeObject *base) + { + int t_size = PyType_BASICSIZE(type); + int b_size = PyType_BASICSIZE(base); + + assert(t_size >= b_size); /* type smaller than base! */ + ... + return 1; + } + + If the transition to C++ goes through it is possible that this form will + be relaxed so that short class methods meant to be inlined can have the + return type on the same line as the function name. However, that is yet to + be determined. + +* Code structure: one space between keywords like ``if``, ``for`` and + the following left parenthesis; no spaces inside the parenthesis; braces + around all ``if`` branches, and no statements on the same line as the + ``if``. They should be formatted as shown:: + + if (mro != NULL) { + one_line_statement; + } + else { + ... + } + + + for (i = 0; i < n; i++) { + one_line_statement; + } + + + while (isstuff) { + dostuff; + } + + + do { + stuff; + } while (isstuff); + + + switch (kind) { + /* Boolean kind */ + case 'b': + return 0; + /* Unsigned int kind */ + case 'u': + ... + /* Anything else */ + default: + return 3; + } + + +* The return statement should *not* get redundant parentheses:: + + return Py_None; /* correct */ + return(Py_None); /* incorrect */ + +* Function and macro call style: ``foo(a, b, c)``, no space before + the open paren, no spaces inside the parens, no spaces before + commas, one space after each comma. + +* Always put spaces around the assignment, Boolean, and comparison + operators. In expressions using a lot of operators, add spaces + around the outermost (lowest priority) operators. + +* Breaking long lines: If you can, break after commas in the + outermost argument list. Always indent continuation lines + appropriately: :: + + PyErr_SetString(PyExc_TypeError, + "Oh dear, you messed up."); + + Here appropriately means at least a double indent (8 spaces). It isn't + necessary to line everything up with the opening parenthesis of the function + call. + +* When you break a long expression at a binary operator, the + operator goes at the end of the previous line, for example: :: + + if (type > tp_dictoffset != 0 && + base > tp_dictoffset == 0 && + type > tp_dictoffset == b_size && + (size_t)t_size == b_size + sizeof(PyObject *)) { + return 0; + } + + Note that the terms in the multi-line Boolean expression are indented so + as to make the beginning of the code block clearly visible. + +* Put blank lines around functions, structure definitions, and + major sections inside functions. + +* Comments go before the code they describe. Multi-line comments should + be like so: :: + + /* + * This would be a long + * explanatory comment. + */ + + Trailing comments should be used sparingly. Instead of :: + + if (yes) { // Success! + + do :: + + if (yes) { + // Success! + +* All functions and global variables should be declared static + when they aren't needed outside the current compilation unit. + +* Declare external functions and variables in a header file. + + +Naming conventions +================== + +* There has been no consistent prefix for NumPy public functions, but + they all begin with a prefix of some sort, followed by an underscore, and + are in camel case: ``PyArray_DescrAlignConverter``, ``NpyIter_GetIterNext``. + In the future the names should be of the form ``Npy*_PublicFunction``, + where the star is something appropriate. + +* Public Macros should have a ``NPY_`` prefix and then use upper case, + for example, ``NPY_DOUBLE``. + +* Private functions should be lower case with underscores, for example: + ``array_real_get``. Single leading underscores should not be used, but + some current function names violate that rule due to historical accident. + +.. note:: + + Functions whose names begin with a single underscore should be renamed at + some point. + + +Function documentation +====================== + +NumPy doesn't have a C function documentation standard at this time, but +needs one. Most NumPy functions are not documented in the code, and that +should change. One possibility is Doxygen with a plugin so that the same +NumPy style used for Python functions can also be used for documenting +C functions, see the files in ``doc/cdoc/``. + + +Related Work +------------ + +Based on Van Rossum and Warsaw, :pep:`7` + + +Discussion +---------- + +https://github.com/numpy/numpy/issues/11911 +recommended that this proposal, which originated as ``doc/C_STYLE_GUIDE.rst.txt``, +be turned into an NEP. + + +Copyright +--------- + +This document has been placed in the public domain. diff --git a/doc/neps/nep-0046-sponsorship-guidelines.rst b/doc/neps/nep-0046-sponsorship-guidelines.rst new file mode 100644 index 000000000000..8535cb554703 --- /dev/null +++ b/doc/neps/nep-0046-sponsorship-guidelines.rst @@ -0,0 +1,256 @@ +.. _NEP46: + +===================================== +NEP 46 — NumPy sponsorship guidelines +===================================== + +:Author: Ralf Gommers <ralf.gommers@gmail.com> +:Status: Accepted +:Type: Process +:Created: 2020-12-27 +:Resolution: https://mail.python.org/pipermail/numpy-discussion/2021-January/081424.html + + +Abstract +-------- + +This NEP provides guidelines on how the NumPy project will acknowledge +financial and in-kind support. + + +Motivation and Scope +-------------------- + +In the past few years, the NumPy project has gotten significant financial +support, as well as dedicated work time for maintainers to work on NumPy. There +is a need to acknowledge that support - it's the right thing to do, it's +helpful when looking for new funding, and funders and organizations expect or +require it, Furthermore, having a clear policy for how NumPy acknowledges +support is helpful when searching for new support. Finally, this policy may +help set reasonable expectations for potential funders. + +This NEP is aimed at both the NumPy community - who can use it as a guideline +when looking for support on behalf of the project and when acknowledging +existing support - and at past, current and prospective sponsors, who often +want or need to know what they get in return for their support other than a +healthier NumPy. + +The scope of this proposal includes: + +- direct financial support, employers providing paid time for NumPy maintainers + and regular contributors, and in-kind support such as free hardware resources or + services, +- where and how NumPy acknowledges support (e.g., logo placement on the website), +- the amount and duration of support which leads to acknowledgement, and +- who in the NumPy project is responsible for sponsorship related topics, and + how to contact them. + + +How NumPy will acknowledge support +---------------------------------- + +There will be two different ways to acknowledge financial and in-kind support: +one to recognize significant active support, and another one to recognize +support received in the past and smaller amounts of support. + +Entities who fall under "significant active supporter" we'll call Sponsor. +The minimum level of support given to NumPy to be considered a Sponsor are: + +- $30,000/yr for unrestricted financial contributions (e.g., donations) +- $60,000/yr for financial contributions for a particular purpose (e.g., grants) +- $100,000/yr for in-kind contributions (e.g., time for employees to contribute) + +We define support being active as: + +- for a one-off donation: it was received within the previous 12 months, +- for recurring or financial or in-kind contributions: they should be ongoing. + +After support moves from "active" to "inactive" status, the acknowledgement +will be left in its place for at least another 6 months. If appropriate, the +funding team can discuss opportunities for renewal with the sponsor. After +those 6 months, acknowledgement may be moved to the historical overview. The +exact timing of this move is at the discretion of the funding team, because +there may be reasons to keep it in the more prominent place for longer. + +The rationale for the above funding levels is that unrestricted financial +contributions are typically the most valuable for the project, and the hardest +to obtain. The opposite is true for in-kind contributions. The dollar value of +the levels also reflect that NumPy's needs have grown to the point where we +need multiple paid developers in order to effectively support our user base and +continue to move the project forward. Financial support at or above these +levels is needed to be able to make a significant difference. + +Sponsors will get acknowledged through: + +- a small logo displayed on the front page of the NumPy website +- prominent logo placement on https://numpy.org/about/ +- logos displayed in talks about NumPy by maintainers +- announcements of the sponsorship on the NumPy mailing list and the numpy-team + Twitter account + +In addition to Sponsors, we already have the concept of Institutional Partner +(defined in NumPy's +`governance document <https://numpy.org/devdocs/dev/governance/index.html>`__), +for entities who employ a NumPy maintainer and let them work on NumPy as part +of their official duties. The governance document doesn't currently define a +minimum amount of paid maintainer time needed to be considered for partnership. +Therefore we propose that level here, roughly in line with the sponsorship +levels: + +- 6 person-months/yr of paid work time for one or more NumPy maintainers or + regular contributors to any NumPy team or activity + +Institutional Partners get the same benefits as Sponsors, in addition to what +is specified in the NumPy governance document. + +Finally, a new page on the website (https://numpy.org/funding/, linked from the +About page) will be added to acknowledge all current and previous sponsors, +partners, and any other entities and individuals who provided $5,000 or more of +financial or in-kind support. This page will include relevant details of +support (dates, amounts, names, and purpose); no logos will be used on this +page. The rationale for the $5,000 minimum level is to keep the amount of work +maintaining the page reasonable; the level is the equivalent of, e.g., one GSoC +or a person-week's worth of engineering time in a Western country, which seems +like a reasonable lower limit. + + +Implementation +-------------- + +The following content changes need to be made: + +- Add a section with small logos towards the bottom of the `numpy.org + <https://numpy.org/>`__ website. +- Create a full list of historical and current support and deploy it to + https://numpy.org/funding. +- Update the NumPy governance document for changes to Institutional Partner + eligibility requirements and benefits. +- Update https://numpy.org/about with details on how to get in touch with the + NumPy project about sponsorship related matters (see next section). + + +NumPy Funding Team +~~~~~~~~~~~~~~~~~~ + +At the moment NumPy has only one official body, the Steering Council, and no +good way to get in touch with either that body or any person or group +responsible for funding and sponsorship related matters. The way this is +typically done now is to somehow find the personal email of a maintainer, and +email them in private. There is a need to organize this more transparently - a +potential sponsor isn't likely to inquire through the mailing list, nor is it +easy for a potential sponsor to know if they're reaching out to the right +person in private. + +https://numpy.org/about/ already says that NumPy has a "funding and grants" +team. However that is not the case. We propose to organize this team, name team +members on it, and add the names of those team members plus a dedicated email +address for the team to the About page. + + +Status before this proposal +--------------------------- + +Acknowledgement of support +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +At the time of writing (Dec 2020), the logos of the four largest financial +sponsors and two institutional partners are displayed on +https://numpy.org/about/. The `Nature paper about NumPy <https://www.nature.com/articles/s41586-020-2649-2>`__ +mentions some early funding. No comprehensive list of received funding and +in-kind support is published anywhere. + +Decisions on which logos to list on the website have been made mostly by the +website team. Decisions on which entities to recognize as Institutional Partner +have been made by the NumPy Steering Council. + + +NumPy governance, decision-making, and financial oversight +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +*This section is meant as context for the reader, to help put the rest of this +NEP in perspective, and perhaps answer questions the reader has when reading +this as a potential sponsor.* + +NumPy has a formal governance structure defined in +`this governance document <https://numpy.org/devdocs/dev/governance/index.html>`__). +Decisions are made by consensus among all active participants in a discussion +(typically on the mailing list), and if consensus cannot be reached then the +Steering Council takes the decision (also by consensus). + +NumPy is a sponsored project of NumFOCUS, a US-based 501(c)3 nonprofit. +NumFOCUS administers NumPy funds, and ensures they are spent in accordance with +its mission and nonprofit status. In practice, NumPy has a NumFOCUS +subcommittee (with its members named in the NumPy governance document) who can +authorize financial transactions. Those transactions, for example paying a +contractor for a particular activity or deliverable, are decided on by the +NumPy Steering Council. + + +Alternatives +------------ + +*Tiered sponsorship levels.* We considered using tiered sponsorship levels, and +rejected this alternative because it would be more complex, and not necessarily +communicate the right intent - the minimum levels are for us to determine how +to acknowledge support that we receive, not a commercial value proposition. +Entities typically will support NumPy because they rely on the project or want +to help advance it, and not to get brand awareness through logo placement. + +*Listing all donations*. Note that in the past we have received many smaller +donations, mostly from individuals through NumFOCUS. It would be great to list +all of those contributions, but given the way we receive information on those +donations right now, that would be quite labor-intensive. If we manage to move +to a more suitable platform, such as `Open Collective <https://opencollective.com/>`__, +in the future, we should reconsider listing all individual donations. + + +Related Work +------------ + +Here we provide a few examples of how other projects handle sponsorship +guidelines and acknowledgements. + +*Scikit-learn* has a narrow banner with logos at the bottom of +https://scikit-learn.org, and a list of present funding and past sponsors at +https://scikit-learn.org/stable/about.html#funding. Plus a separate section +"Infrastructure support" at the bottom of that same About page. + +*Jupyter* has logos of sponsors and institutional partners in two sections on +https://jupyter.org/about. Some subprojects have separate approaches, for +example sponsors are listed (by using the `all-contributors +<https://github.com/all-contributors/all-contributors>`__ bot) in the README for +`jupyterlab-git <https://github.com/jupyterlab/jupyterlab-git>`__. For a recent +discussion on that, see `here <jupyterlab-git acknowledgements discussion>`_. + +*NumFOCUS* has a large banner with sponsor logos on its front page at +https://numfocus.org, and a full page with sponsors at different sponsorship +levels listed at https://numfocus.org/sponsors. They also have a +`Corporate Sponsorship Prospectus <https://numfocus.org/blog/introducing-our-newest-corporate-sponsorship-prospectus>`__, +which includes a lot of detail on both sponsorship levels and benefits, as well +as how that helps NumFOCUS-affiliated projects (including NumPy). + + +Discussion +---------- + +- `Mailing list thread discussing this NEP <https://mail.python.org/pipermail/numpy-discussion/2020-December/081353.html>`__ +- `PR with review of the NEP draft <https://github.com/numpy/numpy/pull/18084>`__ + + +References and Footnotes +------------------------ + +- `Inside NumPy: preparing for the next decade <https://github.com/numpy/archive/blob/main/content/inside_numpy_presentation_SciPy2019.pdf>`__ presentation at SciPy'19 discussing the impact of the first NumPy grant. +- `Issue <https://github.com/numpy/numpy/issues/13393>`__ and + `email <https://mail.python.org/pipermail/numpy-discussion/2019-April/079371.html>`__ + where IBM offered a $5,000 bounty for VSX SIMD support +- `JupyterLab Corporate Engagement and Contribution Guide <https://github.com/jupyterlab/jupyterlab/blob/master/CORPORATE.md>`__ + + +.. _jupyterlab-git acknowledgements discussion: https://github.com/jupyterlab/jupyterlab-git/pull/530 + + +Copyright +--------- + +This document has been placed in the public domain. diff --git a/doc/neps/nep-0047-array-api-standard.rst b/doc/neps/nep-0047-array-api-standard.rst new file mode 100644 index 000000000000..53b8e35b001f --- /dev/null +++ b/doc/neps/nep-0047-array-api-standard.rst @@ -0,0 +1,694 @@ +.. _NEP47: + +======================================== +NEP 47 — Adopting the array API standard +======================================== + +:Author: Ralf Gommers <ralf.gommers@gmail.com> +:Author: Stephan Hoyer <shoyer@gmail.com> +:Author: Aaron Meurer <asmeurer@gmail.com> +:Status: Draft +:Type: Standards Track +:Created: 2021-01-21 +:Resolution: + + +Abstract +-------- + +We propose to adopt the `Python array API standard`_, developed by the +`Consortium for Python Data API Standards`_. Implementing this as a separate +new namespace in NumPy will allow authors of libraries which depend on NumPy +as well as end users to write code that is portable between NumPy and all +other array/tensor libraries that adopt this standard. + +.. note:: + + We expect that this NEP will remain in a draft state for quite a while. + Given the large scope we don't expect to propose it for acceptance any + time soon; instead, we want to solicit feedback on both the high-level + design and implementation, and learn what needs describing better in this + NEP or changing in either the implementation or the array API standard + itself. + + +Motivation and Scope +-------------------- + +Python users have a wealth of choice for libraries and frameworks for +numerical computing, data science, machine learning, and deep learning. New +frameworks pushing forward the state of the art in these fields are appearing +every year. One unintended consequence of all this activity and creativity +has been fragmentation in multidimensional array (a.k.a. tensor) libraries - +which are the fundamental data structure for these fields. Choices include +NumPy, Tensorflow, PyTorch, Dask, JAX, CuPy, MXNet, and others. + +The APIs of each of these libraries are largely similar, but with enough +differences that it’s quite difficult to write code that works with multiple +(or all) of these libraries. The array API standard aims to address that +issue, by specifying an API for the most common ways arrays are constructed +and used. The proposed API is quite similar to NumPy's API, and deviates mainly +in places where (a) NumPy made design choices that are inherently not portable +to other implementations, and (b) where other libraries consistently deviated +from NumPy on purpose because NumPy's design turned out to have issues or +unnecessary complexity. + +For a longer discussion on the purpose of the array API standard we refer to +the `Purpose and Scope section of the array API standard <https://data-apis.github.io/array-api/latest/purpose_and_scope.html>`__ +and the two blog posts announcing the formation of the Consortium [1]_ and +the release of the first draft version of the standard for community review [2]_. + +The scope of this NEP includes: + +- Adopting the 2021 version of the array API standard +- Adding a separate namespace, tentatively named ``numpy.array_api`` +- Changes needed/desired outside of the new namespace, for example new dunder + methods on the ``ndarray`` object +- Implementation choices, and differences between functions in the new + namespace with those in the main ``numpy`` namespace +- A new array object conforming to the array API standard +- Maintenance effort and testing strategy +- Impact on NumPy's total exposed API surface and on other future and + under-discussion design choices +- Relation to existing and proposed NumPy array protocols + (``__array_ufunc__``, ``__array_function__``, ``__array_module__``). +- Required improvements to existing NumPy functionality + +Out of scope for this NEP are: + +- Changes in the array API standard itself. Those are likely to come up + during review of this NEP, but should be upstreamed as needed and this NEP + subsequently updated. + + +Usage and Impact +---------------- + +*This section will be fleshed out later, for now we refer to the use cases given +in* `the array API standard Use Cases section <https://data-apis.github.io/array-api/latest/use_cases.html>`__ + +In addition to those use cases, the new namespace contains functionality that +is widely used and supported by many array libraries. As such, it is a good +set of functions to teach to newcomers to NumPy and recommend as "best +practice". That contrasts with NumPy's main namespace, which contains many +functions and objects that have been superseded or we consider mistakes - but +that we can't remove because of backwards compatibility reasons. + +The usage of the ``numpy.array_api`` namespace by downstream libraries is +intended to enable them to consume multiple kinds of arrays, *without having +to have a hard dependency on all of those array libraries*: + +.. image:: _static/nep-0047-library-dependencies.png + +Adoption in downstream libraries +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The prototype implementation of the ``array_api`` namespace will be used with +SciPy, scikit-learn, and other libraries of interest that depend on NumPy, in +order to get more experience with the design and find out if any important +parts are missing. + +The pattern to support multiple array libraries is intended to be something +like:: + + def somefunc(x, y): + # Retrieves standard namespace. Raises if x and y have different + # namespaces. See Appendix for possible get_namespace implementation + xp = get_namespace(x, y) + out = xp.mean(x, axis=0) + 2*xp.std(y, axis=0) + return out + +The ``get_namespace`` call is effectively the library author opting in to +using the standard API namespace, and thereby explicitly supporting +all conforming array libraries. + + +The ``asarray`` / ``asanyarray`` pattern +```````````````````````````````````````` + +Many existing libraries use the same ``asarray`` (or ``asanyarray``) pattern +as NumPy itself does; accepting any object that can be coerced into a ``np.ndarray``. +We consider this design pattern problematic - keeping in mind the Zen of +Python, *"explicit is better than implicit"*, as well as the pattern being +historically problematic in the SciPy ecosystem for ``ndarray`` subclasses +and with over-eager object creation. All other array/tensor libraries are +more strict, and that works out fine in practice. We would advise authors of +new libraries to avoid the ``asarray`` pattern. Instead they should either +accept just NumPy arrays or, if they want to support multiple kinds of +arrays, check if the incoming array object supports the array API standard +by checking for ``__array_namespace__`` as shown in the example above. + +Existing libraries can do such a check as well, and only call ``asarray`` if +the check fails. This is very similar to the ``__duckarray__`` idea in +:ref:`NEP30`. + + +.. _adoption-application-code: + +Adoption in application code +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The new namespace can be seen by end users as a cleaned up and slimmed down +version of NumPy's main namespace. Encouraging end users to use this +namespace like:: + + import numpy.array_api as xp + + x = xp.linspace(0, 2*xp.pi, num=100) + y = xp.cos(x) + +seems perfectly reasonable, and potentially beneficial - users get offered only +one function for each purpose (the one we consider best-practice), and they +then write code that is more easily portable to other libraries. + + +Backward compatibility +---------------------- + +No deprecations or removals of existing NumPy APIs or other backwards +incompatible changes are proposed. + + +High-level design +----------------- + +The array API standard consists of approximately 120 objects, all of which +have a direct NumPy equivalent. This figure shows what is included at a high level: + +.. image:: _static/nep-0047-scope-of-array-API.png + +The most important changes compared to what NumPy currently offers are: + +- A new array object, ``numpy.array_api.Array`` which: + + - is a thin pure Python (non-subclass) wrapper around ``np.ndarray``, + - conforms to the casting rules and indexing behavior specified by the + standard, + - does not have methods other than dunder methods, + - does not support the full range of NumPy indexing behavior (see + :ref:`indexing` below), + - does not have distinct scalar objects, only 0-D arrays, + - cannot be constructed directly. Instead array construction functions + like ``asarray()`` should be used. + +- Functions in the ``array_api`` namespace: + + - do not accept ``array_like`` inputs, only ``numpy.array_api`` array + objects, with Python scalars only being supported in dunder operators on + the array object, + - do not support ``__array_ufunc__`` and ``__array_function__``, + - use positional-only and keyword-only parameters in their signatures, + - have inline type annotations, + - may have minor changes to signatures and semantics of individual + functions compared to their equivalents already present in NumPy, + - only support dtype literals, not format strings or other ways of + specifying dtypes, + - generally may only support a restricted set of dtypes compared to their + NumPy counterparts. + +- DLPack_ support will be added to NumPy, +- New syntax for "device support" will be added, through a ``.device`` + attribute on the new array object, and ``device=`` keywords in array creation + functions in the ``array_api`` namespace, +- Casting rules will differ from those NumPy currently has. Output dtypes can + be derived from input dtypes (i.e. no value-based casting), and 0-D arrays + are treated like >=1-D arrays. Cross-kind casting (e.g., int to float) is + not allowed. +- Not all dtypes NumPy has are part of the standard. Only boolean, signed and + unsigned integers, and floating-point dtypes up to ``float64`` are supported. + Complex dtypes are expected to be added in the next version of the standard. + Extended precision, string, void, object and datetime dtypes, as well as + structured dtypes, are not included. + +Improvements to existing NumPy functionality that are needed include: + +- Add support for stacks of matrices to some functions in ``numpy.linalg`` + that are currently missing such support. +- Add the ``keepdims`` keyword to ``np.argmin`` and ``np.argmax``. +- Add a "never copy" mode to ``np.asarray``. +- Add smallest_normal to ``np.finfo()``. +- DLPack_ support. + +Additionally, the ``numpy.array_api`` implementation was chosen to be a +*minimal* implementation of the array API standard. This means that it not +only conforms to all the requirements of the array API, but it explicitly does +not include any APIs or behaviors not explicitly required by it. The standard +itself does not require implementations to be so restrictive, but doing this +with the NumPy array API implementation will allow it to become a canonical +implementation of the array API standard. Anyone who wants to make use of the +array API standard can use the NumPy implementation and be sure that their +code is not making use of behaviors that will not be in other conforming +implementations. + +In particular, this means + +- ``numpy.array_api`` will only include those functions that are listed in the + standard. This also applies to methods on the ``Array`` object, +- Functions will only accept input dtypes that are required by the standard + (e.g., transcendental functions like ``cos`` will not accept integer dtypes + because the standard only requires them to accept floating-point dtypes), +- Type promotion will only occur for combinations of dtypes required by the + standard (see the :ref:`dtypes-and-casting-rules` section below), +- Indexing is limited to a subset of possible index types (see :ref:`indexing` + below). + + +Functions in the ``array_api`` namespace +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Let's start with an example of a function implementation that shows the most +important differences with the equivalent function in the main namespace:: + + def matmul(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.matmul <numpy.matmul>`. + See its docstring for more information. + """ + if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: + raise TypeError("Only numeric dtypes are allowed in matmul") + + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + + return Array._new(np.matmul(x1._array, x2._array)) + +This function does not accept ``array_like`` inputs, only +``numpy.array_api.Array``. There are multiple reasons for this. Other array +libraries all work like this. Requiring the user to do coercion of Python +scalars, lists, generators, or other foreign objects explicitly results in a +cleaner design with less unexpected behavior. It is higher-performance---less +overhead from ``asarray`` calls. Static typing is easier. Subclasses will work +as expected. And the slight increase in verbosity because users have to +explicitly coerce to ``ndarray`` on rare occasions seems like a small price to +pay. + +This function does not support ``__array_ufunc__`` nor ``__array_function__``. +These protocols serve a similar purpose as the array API standard module itself, +but through a different mechanisms. Because only ``Array`` instances are accepted, +dispatching via one of these protocols isn't useful anymore. + +This function uses positional-only parameters in its signature. This makes +code more portable---writing, for instance, ``max(a=a, ...)`` is no longer +valid, hence if other libraries call the first parameter ``input`` rather than +``a``, that is fine. Note that NumPy already uses positional-only arguments +for functions that are ufuncs. The rationale for keyword-only parameters (not +shown in the above example) is two-fold: clarity of end user code, and it +being easier to extend the signature in the future without worrying about the +order of keywords. + +This function has inline type annotations. Inline annotations are far easier to +maintain than separate stub files. And because the types are simple, this will +not result in a large amount of clutter with type aliases or unions like in the +current stub files NumPy has. + +This function only accepts numeric dtypes (i.e., not ``bool``). It also does +not allow the input dtypes to be of different kinds (the internal +``_result_type()`` function will raise ``TypeError`` on cross-kind type +combinations like ``_result_type(int32, float64)``). This allows the +implementation to be minimal. Preventing combinations that work in NumPy but +are not required by the array API specification lets users of the submodule +know they are not relying on NumPy specific behavior that may not be present +in array API conforming implementations from other libraries. + +DLPack support for zero-copy data interchange +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ability to convert one kind of array into another kind is valuable, and +indeed necessary when downstream libraries want to support multiple kinds of +arrays. This requires a well-specified data exchange protocol. NumPy already +supports two of these, namely the buffer protocol (i.e., PEP 3118), and +the ``__array_interface__`` (Python side) / ``__array_struct__`` (C side) +protocol. Both work similarly, letting the "producer" describe how the data +is laid out in memory so the "consumer" can construct its own kind of array +with a view on that data. + +DLPack works in a very similar way. The main reasons to prefer DLPack over +the options already present in NumPy are: + +1. DLPack is the only protocol with device support (e.g., GPUs using CUDA or + ROCm drivers, or OpenCL devices). NumPy is CPU-only, but other array + libraries are not. Having one protocol per device isn't tenable, hence + device support is a must. +2. Widespread support. DLPack has the widest adoption of all protocols. Only + NumPy is missing support, and the experiences of other libraries with it + are positive. This contrasts with the protocols NumPy does support, which + are used very little---when other libraries want to interoperate with + NumPy, they typically use the (more limited, and NumPy-specific) + ``__array__`` protocol. + +Adding support for DLPack to NumPy entails: + +- Adding a ``ndarray.__dlpack__()`` method which returns a ``dlpack`` C + structure wrapped in a ``PyCapsule``. +- Adding a ``np._from_dlpack(obj)`` function, where ``obj`` supports + ``__dlpack__()``, and returns an ``ndarray``. + +DLPack is currently a ~200 LoC header, and is meant to be included directly, so +no external dependency is needed. Implementation should be straightforward. + + +Syntax for device support +~~~~~~~~~~~~~~~~~~~~~~~~~ + +NumPy itself is CPU-only, so it clearly doesn't have a need for device support. +However, other libraries (e.g. TensorFlow, PyTorch, JAX, MXNet) support +multiple types of devices: CPU, GPU, TPU, and more exotic hardware. +To write portable code on systems with multiple devices, it's often necessary +to create new arrays on the same device as some other array, or to check that +two arrays live on the same device. Hence syntax for that is needed. + +The array object will have a ``.device`` attribute which enables comparing +devices of different arrays (they only should compare equal if both arrays are +from the same library and it's the same hardware device). Furthermore, +``device=`` keywords in array creation functions are needed. For example:: + + def empty(shape: Union[int, Tuple[int, ...]], /, *, + dtype: Optional[dtype] = None, + device: Optional[device] = None) -> Array: + """ + Array API compatible wrapper for :py:func:`np.empty <numpy.empty>`. + """ + if device not in ["cpu", None]: + raise ValueError(f"Unsupported device {device!r}") + return Array._new(np.empty(shape, dtype=dtype)) + +The implementation for NumPy is as simple as setting the device attribute to +the string ``"cpu"`` and raising an exception if array creation functions +encounter any other value. + +.. _dtypes-and-casting-rules: + +Dtypes and casting rules +~~~~~~~~~~~~~~~~~~~~~~~~ + +The supported dtypes in this namespace are boolean, 8/16/32/64-bit signed and +unsigned integer, and 32/64-bit floating-point dtypes. These will be added to +the namespace as dtype literals with the expected names (e.g., ``bool``, +``uint16``, ``float64``). + +The most obvious omissions are the complex dtypes. The rationale for the lack +of complex support in the first version of the array API standard is that several +libraries (PyTorch, MXNet) are still in the process of adding support for +complex dtypes. The next version of the standard is expected to include ``complex64`` +and ``complex128`` (see `this issue <https://github.com/data-apis/array-api/issues/102>`__ +for more details). + +Specifying dtypes to functions, e.g. via the ``dtype=`` keyword, is expected +to only use the dtype literals. Format strings, Python builtin dtypes, or +string representations of the dtype literals are not accepted. This will +improve readability and portability of code at little cost. Furthermore, no +behavior is expected of these dtype literals themselves other than basic +equality comparison. In particular, since the array API does not have scalar +objects, syntax like ``float32(0.0)`` is not allowed (a 0-D array can be +created with ``asarray(0.0, dtype=float32)``). + +Casting rules are only defined between different dtypes of the same kind +(i.e., boolean to boolean, integer to integer, or floating-point to +floating-point). This also means omitting integer-uint64 combinations that +would upcast to float64 in NumPy. The rationale for this is that mixed-kind +(e.g., integer to floating-point) casting behaviors differ between libraries. + +.. image:: _static/nep-0047-casting-rules-lattice.png + +*Type promotion diagram. Promotion between any two types is given by their +join on this lattice. Only the types of participating arrays matter, not their +values. Dashed lines indicate that behavior for Python scalars is undefined on +overflow. The Python scalars themselves are only allowed in operators on the +array object, not inside of functions. Boolean, integer and floating-point +dtypes are not connected, indicating mixed-kind promotion is undefined (for +the NumPy implementation, these raise an exception).* + +The most important difference between the casting rules in NumPy and in the +array API standard is how scalars and 0-dimensional arrays are handled. In the +standard, array scalars do not exist and 0-dimensional arrays follow the same +casting rules as higher-dimensional arrays. Furthermore, there is no +value-based casting in the standard. The result type of an operation can be +predicted entirely from its input arrays' dtypes, regardless of their shapes +or values. Python scalars are only allowed in dunder operations (like +``__add__``), and only if they are of the same kind as the array dtype. They +always cast to the dtype of the array, regardless of value. Overflow behavior +is undefined. + +See the `Type Promotion Rules section of the array API standard <https://data-apis.github.io/array-api/latest/API_specification/type_promotion.html>`__ +for more details. + +In the implementation, this means + +- Ensuring any operation that would produce an scalar object in NumPy is + converted to a 0-D array in the ``Array`` constructor, +- Checking for combinations that would apply value-based casting and + ensuring they promote to the correct type. This can be achieved, e.g., by + manually broadcasting 0-D inputs (preventing them from participating in + value-based casting), or by explicitly passing the ``signature`` argument + to the underlying ufunc, +- In dunder operator methods, manually converting Python scalar inputs to 0-D + arrays of the matching dtype if they are the same kind, and raising otherwise. For scalars out of + bounds of the given dtype (for which the behavior is undefined by the spec), + the behavior of ``np.array(scalar, dtype=dtype)`` is used (either cast or + raise OverflowError). + +.. _indexing: + +Indexing +~~~~~~~~ + +An indexing expression that would return a scalar with ``ndarray``, e.g. +``arr_2d[0, 0]``, will return a 0-D array with the new ``Array`` object. There are +several reasons for this: array scalars are largely considered a design mistake +which no other array library copied; it works better for non-CPU libraries +(typically arrays can live on the device, scalars live on the host); and it's +simply a more consistent design. To get a Python scalar out of a 0-D array, one can +use the builtin for the type, e.g. ``float(arr_0d)``. + +The other `indexing modes in the standard <https://data-apis.github.io/array-api/latest/API_specification/indexing.html>`__ +do work largely the same as they do for ``numpy.ndarray``. One noteworthy +difference is that clipping in slice indexing (e.g., ``a[:n]`` where ``n`` is +larger than the size of the first axis) is unspecified behavior, because +that kind of check can be expensive on accelerators. + +The standard omits advanced indexing (indexing by an integer array), and boolean indexing is limited to a +single n-D boolean array. This is due to those indexing modes not being +suitable for all types of arrays or JIT compilation. Furthermore, some +advanced NumPy indexing semantics, such as the semantics for mixing advanced +and non-advanced indices in a single index, are considered design mistakes in +NumPy. The absence of these more advanced index types does not seem to be +problematic; if a user or library author wants to use them, they can do so +through zero-copy conversion to ``numpy.ndarray``. This will signal correctly +to whomever reads the code that it is then NumPy-specific rather than portable +to all conforming array types. + +Being a minimal implementation, ``numpy.array_api`` will explicitly disallow +slices with clipped bounds, advanced indexing, and boolean indices mixed with +other indices. + +The array object +~~~~~~~~~~~~~~~~ + +The array object in the standard does not have methods other than dunder +methods. It also does not allow direct construction, preferring instead array +construction methods like ``asarray``. The rationale for that is that not all +array libraries have methods on their array object (e.g., TensorFlow does +not). It also provides only a single way of doing something, rather than have +functions and methods that are effectively duplicate. + +Mixing operations that may produce views (e.g., indexing, ``nonzero``) +in combination with mutation (e.g., item or slice assignment) is +`explicitly documented in the standard to not be supported <https://data-apis.github.io/array-api/latest/design_topics/copies_views_and_mutation.html>`__. +This cannot easily be prohibited in the array object itself; instead this will +be guidance to the user via documentation. + +The standard current does not prescribe a name for the array object itself. We +propose to name it ``Array``. This uses proper PEP 8 capitalization for a +class, and does not conflict with any existing NumPy class names. [3]_ Note +that the actual name of the array class does not actually matter that much as +it is not itself included in the top-level namespace, and cannot be directly +constructed. + +Implementation +-------------- + +A prototype of the ``array_api`` namespace can be found in +https://github.com/numpy/numpy/pull/18585. The docstring in its +``__init__.py`` has several important notes about implementation details. The +code for the wrapper functions also contains ``# Note:`` comments everywhere +there is a difference with the NumPy API. The +implementation is entirely in pure Python, and consists primarily of wrapper +classes/functions that pass through to the corresponding NumPy functions after +applying input validation and any changed behavior. One important part that is not +implemented yet is DLPack_ support, as its implementation in ``np.ndarray`` is +still in progress (https://github.com/numpy/numpy/pull/19083). + +The ``numpy.array_api`` module is considered experimental. This means that +importing it will issue a ``UserWarning``. The alternative to this was naming +the module ``numpy._array_api``, but the warning was chosen instead so that it +does not become necessary to rename the module in the future, potentially +breaking user code. The module also requires Python 3.8 or greater due to +extensive use of the positional-only argument syntax. + +The experimental nature of the module also means that it is not yet mentioned +anywhere in the NumPy documentation, outside of its module docstring and this +NEP. Documentation for the implementation is itself a challenging problem. +Presently every docstring in the implementation simply references the +underlying NumPy function it implements. However, this is not ideal, as the +underlying NumPy function may have different behavior from the corresponding +function in the array API, for instance, additional keyword arguments that are +not present in the array API. It has been suggested that documentation may be +pulled directly from the spec itself, but support for this would require +making some technical changes to the way the spec is written, and so the +current implementation does not yet make any attempt to do this. + +The array API specification is accompanied by an in-progress `official test +suite <https://github.com/data-apis/array-api-tests>`_, which is designed to +test conformance of any library to the array API specification. The tests +included with the implementation will therefore be minimal, as the majority of +the behavior will be verified by this test suite. The tests in NumPy itself +for the ``array_api`` submodule will only include testing for behavior not +covered by the array API test suite, for instance, tests that the +implementation is minimal and properly rejects things like disallowed type +combinations. A CI job will be added to the array API test suite repository to +regularly test it against the NumPy implementation. The array API test suite +is designed to be vendored if libraries wish to do that, but this idea was +rejected for NumPy because the time taken by it is significant relative to the +existing NumPy test suite, and because the test suite is itself still +a work in progress. + +The dtype objects +~~~~~~~~~~~~~~~~~ + +We must be able to compare dtypes for equality, and expressions like these must +be possible:: + + np.array_api.some_func(..., dtype=x.dtype) + +The above implies it would be nice to have ``np.array_api.float32 == +np.array_api.ndarray(...).dtype``. + +Dtypes should not be assumed to have a class hierarchy by users, however we are +free to implement it with a class hierarchy if that's convenient. We considered +the following options to implement dtype objects: + +1. Alias dtypes to those in the main namespace, e.g., ``np.array_api.float32 = + np.float32``. +2. Make the dtypes instances of ``np.dtype``, e.g., ``np.array_api.float32 = + np.dtype(np.float32)``. +3. Create new singleton classes with only the required methods/attributes + (currently just ``__eq__``). + +It seems like (2) would be easiest from the perspective of interacting with +functions outside the main namespace and (3) would adhere best to the +standard. (2) does not prevent users from accessing NumPy-specific attributes +of the dtype objects like (3) would, although unlike (1), it does disallow +creating scalar objects like ``float32(0.0)``. (2) also keeps only one object +per dtype---with (1), ``arr.dtype`` would be still be a dtype instance. The +implementation currently uses (2). + +TBD: the standard does not yet have a good way to inspect properties of a +dtype, to ask questions like "is this an integer dtype?". Perhaps this is easy +enough to do for users, like so:: + + def _get_dtype(dt_or_arr): + return dt_or_arr.dtype if hasattr(dt_or_arr, 'dtype') else dt_or_arr + + def is_floating(dtype_or_array): + dtype = _get_dtype(dtype_or_array) + return dtype in (float32, float64) + + def is_integer(dtype_or_array): + dtype = _get_dtype(dtype_or_array) + return dtype in (uint8, uint16, uint32, uint64, int8, int16, int32, int64) + +However it could make sense to add to the standard. Note that NumPy itself +currently does not have a great for asking such questions, see +`gh-17325 <https://github.com/numpy/numpy/issues/17325>`__. + + +Feedback from downstream library authors +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +TODO - this can only be done after trying out some use cases + +Leo Fang (CuPy): *"My impression is for CuPy we could simply take this new array object and s/numpy/cupy"* + + +Related Work +------------ + +:ref:`NEP37` contains a similar mechanism to retrieve a NumPy-like namespace. +In fact, NEP 37 inspired the (slightly simpler) mechanism in the array API +standard. + +Other libraries have adopted large parts of NumPy's API, made changes where +necessary, and documented deviations. See for example +`the jax.numpy documentation <https://jax.readthedocs.io/en/latest/jax.numpy.html>`__ +and `Difference between CuPy and NumPy <https://docs.cupy.dev/en/stable/reference/difference.html>`__. +The array API standard was constructed with the help of such comparisons, only +between many array libraries rather than only between NumPy and one other library. + + +Alternatives +------------ + +It was proposed to have the NumPy array API implementation as a separate +library from NumPy. This was rejected because keeping it separate will make it +less likely for people to review it, and including it in NumPy itself as an +experimental submodule will make it easier for end users and library authors +who already depend on NumPy to access the implementation. + +Appendix - a possible ``get_namespace`` implementation +------------------------------------------------------ + +The ``get_namespace`` function mentioned in the +:ref:`adoption-application-code` section can be implemented like:: + + def get_namespace(*xs): + # `xs` contains one or more arrays, or possibly Python scalars (accepting + # those is a matter of taste, but doesn't seem unreasonable). + namespaces = { + x.__array_namespace__() if hasattr(x, '__array_namespace__') else None for x in xs if not isinstance(x, (bool, int, float, complex)) + } + + if not namespaces: + # one could special-case np.ndarray above or use np.asarray here if + # older numpy versions need to be supported. + raise ValueError("Unrecognized array input") + + if len(namespaces) != 1: + raise ValueError(f"Multiple namespaces for array inputs: {namespaces}") + + xp, = namespaces + if xp is None: + raise ValueError("The input is not a supported array type") + + return xp + + +Discussion +---------- + +- `First discussion on the mailing list about the array API standard <https://mail.python.org/pipermail/numpy-discussion/2020-November/081181.html>`__ + +- `Discussion of NEP 47 on the mailing list + <https://mail.python.org/pipermail/numpy-discussion/2021-February/081530.html>`_ + +- `PR #18585 implementing numpy.array_api + <https://github.com/numpy/numpy/pull/18585>`_ + +References and Footnotes +------------------------ + +.. _Python array API standard: https://data-apis.github.io/array-api/latest + +.. _Consortium for Python Data API Standards: https://data-apis.org/ + +.. _DLPack: https://github.com/dmlc/dlpack + +.. [1] https://data-apis.org/blog/announcing_the_consortium/ + +.. [2] https://data-apis.org/blog/array_api_standard_release/ + +.. [3] https://github.com/numpy/numpy/pull/18585#discussion_r641370294 + +Copyright +--------- + +This document has been placed in the public domain. [1]_ diff --git a/doc/neps/nep-0048-spending-project-funds.rst b/doc/neps/nep-0048-spending-project-funds.rst new file mode 100644 index 000000000000..d2924d4a9e35 --- /dev/null +++ b/doc/neps/nep-0048-spending-project-funds.rst @@ -0,0 +1,458 @@ +.. _NEP48: + +===================================== +NEP 48 — Spending NumPy project funds +===================================== + +:Author: Ralf Gommers <ralf.gommers@gmail.com> +:Author: Inessa Pawson <inessa@albuscode.org> +:Author: Stefan van der Walt <stefanv@berkeley.edu> +:Status: Draft +:Type: Informational +:Created: 2021-02-07 +:Resolution: + + +Abstract +-------- + +The NumPy project has historically never received significant **unrestricted** +funding. However, that is starting to change. This NEP aims to provide +guidance about spending NumPy project unrestricted funds by formulating a set +of principles about *what* to pay for and *who* to pay. It will also touch on +how decisions regarding spending funds get made, how funds get administered, +and transparency around these topics. + + +Motivation and Scope +-------------------- + +NumPy is a fiscally sponsored project of NumFOCUS, a 501(c)(3) nonprofit +organization headquartered in Austin, TX. Therefore, for all legal and +accounting matters the NumPy project has to follow the rules and regulations +for US nonprofits. All nonprofit donations are classified into two categories: +**unrestricted funds** which may be used for any legal purpose appropriate +to the organization and **restricted funds**, monies set aside for a +particular purpose (e.g., project, educational program, etc.). + +For the detailed timeline of NumPy funding refer to +:ref:`numpy-funding-history`. + +Since its inception and until 2020, the NumPy project has only spent on the order of +$10,000 USD of funds that were not restricted to a particular program. Project +income of this type has been relying on donations from individuals and, from +mid 2019, recurring monthly contributions from Tidelift. By the end of 2020, +the Tidelift contributions increased to $3,000/month, and there's also a +potential for an increase of donations and grants going directly to the +project. Having a clear set of principles around how to use these funds will +facilitate spending them fairly and effectively. Additionally, it will make it +easier to solicit donations and other contributions. + +A key assumption this NEP makes is that NumPy remains a largely +volunteer-driven project, and that the project funds are not enough to employ +maintainers full-time. If funding increases to the point where that assumption +is no longer true, this NEP should be updated. + +In scope for this NEP are: + +- Principles of spending project funds: what to pay for, and who to pay. +- Describing how NumPy's funds get administered. +- Describing how decisions to spend funds get proposed and made. + +Out of scope for this NEP are: + +- Making any decisions about spending project funds on a specific project or + activity. +- Principles for spending funds that are intended for NumPy development, but + don't fall in the category of NumPy unrestricted funds. This includes most of + the grant funding, which is usually earmarked for certain + activities/deliverables and goes to an Institutional Partner rather than + directly to the NumPy project, and companies or institutions funding specific + features. + *Rationale: As a project, we have no direct control over how this work gets + executed (at least formally, until issues or PRs show up). In some cases, we + may not even know the contributions were funded or done by an employee on + work time. (Whether that's the case or not should not change how we approach + a contribution). For grants though, we do expect the research/project leader + and funded team to align their work with the needs of NumPy and be + receptive to feedback from other NumPy maintainers and contributors.* + + +Principles of spending project funds +------------------------------------ + +NumPy will likely always be a project with many times more volunteer +contributors than funded people. Therefore having those funded people operate +in ways that attract more volunteers and enhance their participation experience +is critical. That key principle motivates many of the more detailed principles +given below for what to pay for and whom to pay. + +The approach for spending funds will be: + +- first figure out what we want to fund, +- then look for a great candidate, +- after that's settled, determine a fair compensation level. + +The next sections go into detail on each of these three points. + +.. _section-what-to-pay-for: + +What to pay for +``````````````` + +1. Pay for things that are important *and* otherwise won't get done. + *Rationale: there is way more to be done than there are funds to do all + those things. So count on interested volunteers or external sponsored work + to do many of those things.* +2. Plan for sustainability. Don't rely on money always being there. +3. Consider potential positive benefits for NumPy maintainers and contributors, + maintainers of other projects, end users, and other stakeholders like + packagers and educators. +4. Think broadly. There's more to a project than code: websites, documentation, + community building, governance - it's all important. +5. For proposed funded work, include paid time for others to review your work + if such review is expected to be significant effort - do not just increase + the load on volunteer maintainers. + *Rationale: we want the effect of spending funds to be positive for + everyone, not just for the people getting paid. This is also a matter of + fairness.* + +When considering development work, principle (1) implies that priority should +be giving to (a) the most boring/painful tasks that no one likes doing, and to +necessary structural changes to the code base that are too large to be done by +a volunteer in a reasonable amount of time. + +There are also many tasks, activities, and projects outside of +development work that are important and could enhance the project or community +- think of, for example, user surveys, translations, outreach, dedicated +mentoring of newcomers, community organizating, website improvements, and +administrative tasks. + +Time of people to perform tasks is also not the only thing that funds can be +used for: expenses for in-person developer meetings or sprints, hosted hardware +for benchmarking or development work, and CI or other software services could +all be good candidates to spend funds on. + +Whom to pay +``````````` + +1. All else being equal, give preference to existing maintainers/contributors. +2. When looking outside of the current team, consider this an opportunity to + make the project more diverse. +3. Pay attention to the following when considering paying someone: + + - the necessary technical or domain-specific skills to execute the tasks, + - communication and self-management skills, + - experience contributing to and working with open source projects. + +It will likely depend on the project/tasks whether there's already a clear best +candidate within the NumPy team, or whether we look for new people to get +involved. Before making any decisions, the decision makers (according to the +NumPy governance document - currently that's the Steering Council) should think +about whether an opportunity should be advertised to give a wider group of +people a chance to apply for it. + +Compensating fairly +``````````````````` + +.. note:: + + This section on compensating fairly will be considered *Draft* even if this + NEP as a whole is accepted. Once we have applied the approach outlined here + at least 2-3 times and we are happy with it, will we remove this note and + consider this section *Accepted*. + +Paying people fairly is a difficult topic, especially when it comes to +distributed teams. Therefore, we will only offer some guidance here. Final +decisions will always have to be considered and approved by the group of people +that bears this responsibility (according to the current NumPy governance +structure, this would be the NumPy Steering Council). + +Discussions on remote employee compensation tend to be dominated by two +narratives: "pay local market rates" and "same work -- same pay". + +We consider them both extreme: + +- "Same work -- same pay" is unfair to people living in locations with a higher + cost of living. For example, the average rent for a single family apartment + can differ by a large factor (from a few hundred dollars to thousands of + dollars per month). +- "Pay local market rates" bakes in existing inequalities between countries + and makes fixed-cost items like a development machine or a holiday trip + abroad relatively harder to afford in locations where market rates are lower. + +We seek to find a middle ground between these two extremes. + +Useful points of reference include companies like GitLab and +Buffer who are transparent about their remuneration policies ([3]_, [4]_), +Google Summer of Code stipends ([5]_), other open source projects that manage +their budget in a transparent manner (e.g., Babel and Webpack on Open +Collective ([6]_, [7]_)), and standard salary comparison sites. + +Since NumPy is a not-for-profit project, we also looked to the nonprofit sector +for guidelines on remuneration policies and compensation levels. Our findings +show that most smaller non-profits tend to pay a median salary/wage. We +recognize merit in this approach: applying candidates are likely to have a +genuine interest in open source, rather than to be motivated purely by +financial incentives. + +Considering all of the above, we will use the following guidelines for +determining compensation: + +1. Aim to compensate people appropriately, up to a level that's expected for + senior engineers or other professionals as applicable. +2. Establish a compensation cap of $125,000 USD that cannot be exceeded even + for the residents from the most expensive/competitive locations ([#f-pay]_). +3. For equivalent work and seniority, a pay differential between locations + should never be more than 2x. + For example, if we pay $110,000 USD to a senior-level developer from New + York, for equivalent work a senior-level developer from South-East Asia + should be paid at least $55,000 USD. To compare locations, we will use + `Numbeo Cost of Living calculator <https://www.numbeo.com/cost-of-living/>`__ + (or its equivalent). + +Some other considerations: + +- Often, compensated work is offered for a limited amount of hours or fixed + term. In those cases, consider compensation equivalent to a remuneration + package that comes with permanent employment (e.g., one month of work should + be compensated by at most 1/12th of a full-year salary + benefits). +- When comparing rates, an individual contractor should typically make 20% more + than someone who is employed since they have to take care of their benefits + and accounting on their own. +- Some people may be happy with one-off payments towards a particular + deliverable (e.g., "triage all open issues for label X for $x,xxx"). + This should be compensated at a lower rate compared to an individual + contractor. Or they may motivate lower amounts for another reason (e.g., "I + want to receive $x,xxx to hire a cleaner or pay for childcare, to free up + time for work on open source). +- When funding someone's time through their employer, that employer may want to + set the compensation level based on its internal rules (e.g., overhead rates). + Small deviations from the guidelines in this NEP may be needed in such cases, + however they should be within reason. +- It's entirely possible that another strategy rather than paying people for + their time on certain tasks may turn out to be more effective. Anything that + helps the project and community grow and improve is worth considering. +- Transparency helps. If everyone involved is comfortable sharing their + compensation levels with the rest of the team (or better make it public), + it's least likely to be way off the mark for fairness. + +We highly recommend that the individuals involved in decision-making about +hiring and compensation peruse the content of the References section of this +NEP. It offers a lot of helpful advice on this topic. + + +Defining fundable activities and projects +----------------------------------------- + +We'd like to have a broader set of fundable ideas that we will prioritize with +input from NumPy team members and the wider community. All ideas will be +documented on a single wiki page. Anyone may propose an idea. Only members of a +NumPy team may edit the wiki page. + +Each listed idea must meet the following requirements: + +1. It must be clearly scoped: its description must explain the importance to + the project, referencing the NumPy Roadmap if possible, the items to pay for + or activities and deliverables, and why it should be a funded activity (see + :ref:`section-what-to-pay-for`). +2. It must contain the following metadata: title, cost, time duration or effort + estimate, and (if known) names of the team member(s) to execute or coordinate. +3. It must have an assigned priority (low, medium, or high). This discussion + can originate at a NumPy community meeting or on the mailing list. However, + it must be finalized on the mailing list allowing everyone to weigh in. + +If a proposed idea has been assigned a high priority level, a decision on +allocating funding for it will be made on the private NumPy Steering Council +mailing list. *Rationale: these will often involve decisions about individuals, +which is typically hard to do in public. This is the current practice that +seems to be working well.* + +Sometimes, it may be practical to make a single funding decision ad-hoc (e.g., +"Here's a great opportunity plus the right person to execute it right now”). +However, this approach to decision-making should be used rarely. + + +Strategy for spending/saving funds +---------------------------------- + +There is an expectation from NumPy individual, corporate, and institutional +donors that the funds will be used for the benefit of the project and the +community. Therefore, we should spend available funds, thoughtfully, +strategically, and fairly, as they come in. For emergencies, we should keep a +$10,000 - $15,000 USD reserve which could cover, for example, a year of CI and +hosting services, 1-2 months of full-time maintenance work, or contracting a +consultant for a specific need. + + +How project funds get administered +---------------------------------- + +We will first summarize how administering of funds works today, and then +discuss how to make this process more efficient and transparent. + +Currently, the project funds are held by NumFOCUS in a dedicated account. +NumFOCUS has a small accounting team, which produces an account overview as a +set of spreadsheets on a monthly basis. These land in a shared drive, typically +with about a one month delay (e.g., the balance and transactions for February +are available at the end of March), where a few NumPy team members can access +them. Expense claims and invoices are submitted through the NumFOCUS website. +Those then show up in another spreadsheet, where a NumPy team member must +review and approve each of them before payments are made. Following NumPy +bylaws, the NumFOCUS finance subcommittee, consisting of five people, meets +every six months to review all the project related transactions. (In practice, +there have been so few transactions that we skipped some of these meetings.) + +The existing process is time-consuming and error-prone. More transparency and +automation are desirable. + + +Transparency about project funds and in decision making +``````````````````````````````````````````````````````` + +**To discuss: do we want full transparency by publishing our accounts, +transparency to everyone on a NumPy team, or some other level?** + +Ralf: I'd personally like it to be fully transparent, like through Open +Collective, so the whole community can see current balance, income and expenses +paid out at any moment in time. Moving to Open Collective is nontrivial, +however we can publish the data elsewhere for now if we'd want to. +*Note: Google Season of Docs this year requires having an Open Collective +account, so this is likely to happen soon enough.* + +Stefan/Inessa: at least a summary overview should be fully public, and all +transactions should be visible to the Steering Council. Full transparency of +all transactions is probably fine, but not necessary. + +*The options here may be determined by the accounting system and amount of +effort required.* + + +.. _numpy-funding-history: + +NumPy funding – history and current status +------------------------------------------ + +The NumPy project received its first major funding in 2017. For an overview of +the early history of NumPy (and SciPy), including some institutions sponsoring +time for their employees or contractors to work on NumPy, see [1]_ and [2]_. To +date, NumPy has received four grants: + +- Two grants, from the Alfred P. Sloan Foundation and the Gordon and Betty + Moore Foundation respectively, of about $1.3M combined to the Berkeley + Institute of Data Science. Work performed during the period 2017-2020; + PI Stéfan van der Walt. +- Two grants from the Chan Zuckerberg Foundation to NumFOCUS, for a combined + amount of $335k. Work performed during the period 2020-2021; PI's Ralf + Gommers (first grant) and Melissa Mendonça (second grant). + +From 2012 onwards NumPy has been a fiscally sponsored project of NumFOCUS. +Note that fiscal sponsorship doesn't mean NumPy gets funding, rather that it +can receive funds under the umbrella of a nonprofit. See `NumFOCUS Project +Support <https://numfocus.org/projects-overview>`__ for more details. + +Only since 2017 has the NumPy website displayed a "Donate" button, and since +2019 the NumPy repositories have had the GitHub Sponsors button. Before that, +it was possible to donate to NumPy on the NumFOCUS website. The sum total of +donations from individuals to NumPy for 2017-2020 was about $6,100. + +From May 2019 onwards, Tidelift has supported NumPy financially as part of +its "managed open source" business model. From May 2019 till July 2020 this was +$1,000/month, and it started steadily growing after that to about $3,000/month +(as of Feb 2021). + +Finally, there has been other incidental project income, for example, some book +royalties from Packt Publishing, GSoC mentoring fees from Google, and +merchandise sales revenue through the NumFOCUS web shop. All of these were +small (two or three figure) amounts. + +This brings the total amount of project income which did not already have a +spending target to about $35,000. Most of that is recent, from Tidelift. +Over the past 1.5 years we spent about $10,000 for work on the new NumPy +website and Sphinx theme. Those spending decisions were made by the NumPy +Steering Council and announced on the mailing list. + +That leaves about $25,000 in available funds at the time of writing, and +that amount is currently growing at a rate of about $3,000/month. + + +Related Work +------------ + +See references. We assume that other open source projects have also developed +guidelines on spending project funds. However, we were unable to find any +examples at the time of writing. + + +Alternatives +------------ + +*Alternative spending strategy*: not having cash reserves. The rationale +being that NumPy is important enough that in a real emergency some person or +entity will likely jump in to help out. This is not a responsible approach to +financial stewardship of the project though. Hence, we decided against it. + + +Discussion +---------- + + + +References and Footnotes +------------------------ + +.. [1] Pauli Virtanen et al., "SciPy 1.0: fundamental algorithms for scientific + computing in Python", https://www.nature.com/articles/s41592-019-0686-2, + 2020 + +.. [2] Charles Harris et al., "Array programming with NumPy", https://www.nature.com/articles/s41586-020-2649-2, 2020 + +.. [3] https://remote.com/blog/remote-compensation + +.. [4] https://about.gitlab.com/company/culture/all-remote/compensation/#how-do-you-decide-how-much-to-pay-people + +.. [5] https://developers.google.com/open-source/gsoc/help/student-stipends + +.. [6] Jurgen Appelo, "Compensation: what is fair?", https://blog.agilityscales.com/compensation-what-is-fair-38a65a822c29, 2016 + +.. [7] Project Include, "Compensating fairly", https://projectinclude.org/compensating_fairly + +.. [#f-pay] This cap is derived from comparing with compensation levels at + other open source projects (e.g., Babel, Webpack, Drupal - all in + the $100,000 -- $125,000 range) and Partner Institutions. + +- Nadia Eghbal, "Roads and Bridges: The Unseen Labor Behind Our Digital + Infrastructure", 2016 +- Nadia Eghbal, "Working in Public: The Making and Maintenance of Open + Source", 2020 +- https://github.com/nayafia/lemonade-stand +- Daniel Oberhaus, `"The Internet Was Built on the Free Labor of Open Source + Developers. Is That Sustainable?" + <https://www.vice.com/en/article/43zak3/the-internet-was-built-on-the-free-labor-of-open-source-developers-is-that-sustainable>`_, 2019 +- David Heinemeier Hansson, `"The perils of mixing open source and money" <https://dhh.dk/2013/the-perils-of-mixing-open-source-and-money.html>`_, 2013 +- Danny Crichton, `"Open source sustainability" <https://techcrunch.com/2018/06/23/open-source-sustainability/?guccounter=1>`_, 2018 +- Nadia Eghbal, "Rebuilding the Cathedral", https://www.youtube.com/watch?v=VS6IpvTWwkQ, 2017 +- Nadia Eghbal, "Where money meets open source", https://www.youtube.com/watch?v=bjAinwgvQqc&t=246s, 2017 +- Eileen Uchitelle, ""The unbearable vulnerability of open source", https://www.youtube.com/watch?v=VdwO3LQ56oM, 2017 (the inverted triangle, open source is a funnel) +- Dries Buytaert, "Balancing Makers and Takers to scale and sustain Open Source", https://dri.es/balancing-makers-and-takers-to-scale-and-sustain-open-source, 2019 +- Safia Abdalla, "Beyond Maintenance", https://increment.com/open-source/beyond-maintenance/, 2019 +- Xavier Damman, "Money and Open Source Communities", https://blog.opencollective.com/money-and-open-source-communities/, 2016 +- Aseem Sood, "Let's talk about money", https://blog.opencollective.com/lets-talk-about-money/, 2017 +- Alanna Irving, "Has your open source community raised money? Here's how to spend it.", https://blog.opencollective.com/has-your-open-source-community-raised-money-heres-how-to-spend-it/, 2017 +- Alanna Irving, "Funding open source, how Webpack reached $400k+/year", https://blog.opencollective.com/funding-open-source-how-webpack-reached-400k-year/, 2017 +- Alanna Irving, "Babel's rise to financial sustainability", https://blog.opencollective.com/babels-rise-to-financial-sustainability/, 2019 +- Devon Zuegel, "The city guide to open source", https://www.youtube.com/watch?v=80KTVu6GGSE, 2020 + blog: https://increment.com/open-source/the-city-guide-to-open-source/ + +GitHub Sponsors: + +- https://github.blog/2019-05-23-announcing-github-sponsors-a-new-way-to-contribute-to-open-source/ +- https://github.blog/2020-05-12-github-sponsors-is-out-of-beta-for-sponsored-organizations/ +- https://blog.opencollective.com/on-github-sponsors/, 2019 +- https://blog.opencollective.com/double-the-love/, 2020 +- https://blog.opencollective.com/github-sponsors-for-companies-open-source-collective-for-people/ + + +Copyright +--------- + +This document has been placed in the public domain. diff --git a/doc/neps/nep-0049.rst b/doc/neps/nep-0049.rst new file mode 100644 index 000000000000..3bd1d102c62d --- /dev/null +++ b/doc/neps/nep-0049.rst @@ -0,0 +1,354 @@ +=================================== +NEP 49 — Data allocation strategies +=================================== + +:Author: Matti Picus +:Status: Final +:Type: Standards Track +:Created: 2021-04-18 +:Resolution: https://mail.python.org/archives/list/numpy-discussion@python.org/thread/YZ3PNTXZUT27B6ITFAD3WRSM3T3SRVK4/#PKYXCTG4R5Q6LIRZC4SEWLNBM6GLRF26 + + +Abstract +-------- + +The ``numpy.ndarray`` requires additional memory allocations +to hold ``numpy.ndarray.strides``, ``numpy.ndarray.shape`` and +``numpy.ndarray.data`` attributes. These attributes are specially allocated +after creating the python object in ``__new__`` method. + +This NEP proposes a mechanism to override the memory management strategy used +for ``ndarray->data`` with user-provided alternatives. This allocation holds +the data and can be very large. As accessing this data often becomes +a performance bottleneck, custom allocation strategies to guarantee data +alignment or pinning allocations to specialized memory hardware can enable +hardware-specific optimizations. The other allocations remain unchanged. + +Motivation and Scope +-------------------- + +Users may wish to override the internal data memory routines with ones of their +own. Two such use-cases are to ensure data alignment and to pin certain +allocations to certain NUMA cores. This desire for alignment was discussed +multiple times on the mailing list `in 2005`_, and in `issue 5312`_ in 2014, +which led to `PR 5457`_ and more mailing list discussions here_ `and here`_. In +a comment on the issue `from 2017`_, a user described how 64-byte alignment +improved performance by 40x. + +Also related is `issue 14177`_ around the use of ``madvise`` and huge pages on +Linux. + +Various tracing and profiling libraries like filprofiler_ or `electric fence`_ +override ``malloc``. + +The long CPython discussion of `BPO 18835`_ began with discussing the need for +``PyMem_Alloc32`` and ``PyMem_Alloc64``. The early conclusion was that the +cost (of wasted padding) vs. the benefit of aligned memory is best left to the +user, but then evolves into a discussion of various proposals to deal with +memory allocations, including `PEP 445`_ `memory interfaces`_ to +``PyTraceMalloc_Track`` which apparently was explicitly added for NumPy. + +Allowing users to implement different strategies via the NumPy C-API will +enable exploration of this rich area of possible optimizations. The intention +is to create a flexible enough interface without burdening normative users. + +.. _`issue 5312`: https://github.com/numpy/numpy/issues/5312 +.. _`from 2017`: https://github.com/numpy/numpy/issues/5312#issuecomment-315234656 +.. _`in 2005`: https://numpy-discussion.scipy.narkive.com/MvmMkJcK/numpy-arrays-data-allocation-and-simd-alignement +.. _`here`: http://numpy-discussion.10968.n7.nabble.com/Aligned-configurable-memory-allocation-td39712.html +.. _`and here`: http://numpy-discussion.10968.n7.nabble.com/Numpy-s-policy-for-releasing-memory-td1533.html +.. _`issue 14177`: https://github.com/numpy/numpy/issues/14177 +.. _`filprofiler`: https://github.com/pythonspeed/filprofiler/blob/master/design/allocator-overrides.md +.. _`electric fence`: https://github.com/boundarydevices/efence +.. _`memory interfaces`: https://docs.python.org/3/c-api/memory.html#customize-memory-allocators +.. _`BPO 18835`: https://bugs.python.org/issue18835 +.. _`PEP 445`: https://www.python.org/dev/peps/pep-0445/ + +Usage and Impact +---------------- + +The new functions can only be accessed via the NumPy C-API. An example is +included later in this NEP. The added ``struct`` will increase the size of the +``ndarray`` object. It is a necessary price to pay for this approach. We +can be reasonably sure that the change in size will have a minimal impact on +end-user code because NumPy version 1.20 already changed the object size. + +The implementation preserves the use of ``PyTraceMalloc_Track`` to track +allocations already present in NumPy. + +Backward compatibility +---------------------- + +The design will not break backward compatibility. Projects that were assigning +to the ``ndarray->data`` pointer were already breaking the current memory +management strategy and should restore +``ndarray->data`` before calling ``Py_DECREF``. As mentioned above, the change +in size should not impact end-users. + +Detailed description +-------------------- + +High level design +================= + +Users who wish to change the NumPy data memory management routines will use +:c:func:`PyDataMem_SetHandler`, which uses a :c:type:`PyDataMem_Handler` +structure to hold pointers to functions used to manage the data memory. In +order to allow lifetime management of the ``context``, the structure is wrapped +in a ``PyCapsule``. + +Since a call to ``PyDataMem_SetHandler`` will change the default functions, but +that function may be called during the lifetime of an ``ndarray`` object, each +``ndarray`` will carry with it the ``PyDataMem_Handler``-wrapped PyCapsule used +at the time of its instantiation, and these will be used to reallocate or free +the data memory of the instance. Internally NumPy may use ``memcpy`` or +``memset`` on the pointer to the data memory. + +The name of the handler will be exposed on the python level via a +``numpy.core.multiarray.get_handler_name(arr)`` function. If called as +``numpy.core.multiarray.get_handler_name()`` it will return the name of the +handler that will be used to allocate data for the next new `ndarrray`. + +The version of the handler will be exposed on the python level via a +``numpy.core.multiarray.get_handler_version(arr)`` function. If called as +``numpy.core.multiarray.get_handler_version()`` it will return the version of the +handler that will be used to allocate data for the next new `ndarrray`. + +The version, currently 1, allows for future enhancements to the +``PyDataMemAllocator``. If fields are added, they must be added to the end. + + +NumPy C-API functions +===================== + +.. c:type:: PyDataMem_Handler + + A struct to hold function pointers used to manipulate memory + + .. code-block:: c + + typedef struct { + char name[127]; /* multiple of 64 to keep the struct aligned */ + uint8_t version; /* currently 1 */ + PyDataMemAllocator allocator; + } PyDataMem_Handler; + + where the allocator structure is + + .. code-block:: c + + /* The declaration of free differs from PyMemAllocatorEx */ + typedef struct { + void *ctx; + void* (*malloc) (void *ctx, size_t size); + void* (*calloc) (void *ctx, size_t nelem, size_t elsize); + void* (*realloc) (void *ctx, void *ptr, size_t new_size); + void (*free) (void *ctx, void *ptr, size_t size); + } PyDataMemAllocator; + + The use of a ``size`` parameter in ``free`` differentiates this struct from + the :c:type:`PyMemAllocatorEx` struct in Python. This call signature is + used internally in NumPy currently, and also in other places for instance + `C++98 <https://en.cppreference.com/w/cpp/memory/allocator/deallocate>`, + `C++11 <https://en.cppreference.com/w/cpp/memory/allocator_traits/deallocate>`, and + `Rust (allocator_api) <https://doc.rust-lang.org/std/alloc/trait.Allocator.html#tymethod.deallocate>`. + + The consumer of the `PyDataMemAllocator` interface must keep track of ``size`` and make sure it is + consistent with the parameter passed to the ``(m|c|re)alloc`` functions. + + NumPy itself may violate this requirement when the shape of the requested + array contains a ``0``, so authors of PyDataMemAllocators should relate to + the ``size`` parameter as a best-guess. Work to fix this is ongoing in PRs + 15780_ and 15788_ but has not yet been resolved. When it is this NEP should + be revisited. + +.. c:function:: PyObject * PyDataMem_SetHandler(PyObject *handler) + + Sets a new allocation policy. If the input value is ``NULL``, will reset + the policy to the default. Return the previous policy, or + return NULL if an error has occurred. We wrap the user-provided + so they will still call the Python and NumPy memory management callback + hooks. All the function pointers must be filled in, ``NULL`` is not + accepted. + +.. c:function:: const PyObject * PyDataMem_GetHandler() + + Return the current policy that will be used to allocate data for the + next ``PyArrayObject``. On failure, return ``NULL``. + +``PyDataMem_Handler`` thread safety and lifetime +================================================ +The active handler is stored in the current :py:class:`~contextvars.Context` +via a :py:class:`~contextvars.ContextVar`. This ensures it can be configured both +per-thread and per-async-coroutine. + +There is currently no lifetime management of ``PyDataMem_Handler``. +The user of `PyDataMem_SetHandler` must ensure that the argument remains alive +for as long as any objects allocated with it, and while it is the active handler. +In practice, this means the handler must be immortal. + +As an implementation detail, currently this ``ContextVar`` contains a ``PyCapsule`` +object storing a pointer to a ``PyDataMem_Handler`` with no destructor, +but this should not be relied upon. + +Sample code +=========== + +This code adds a 64-byte header to each ``data`` pointer and stores information +about the allocation in the header. Before calling ``free``, a check ensures +the ``sz`` argument is correct. + +.. code-block:: c + + #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION + #include <numpy/arrayobject.h> + NPY_NO_EXPORT void * + + typedef struct { + void *(*malloc)(size_t); + void *(*calloc)(size_t, size_t); + void *(*realloc)(void *, size_t); + void (*free)(void *); + } Allocator; + + NPY_NO_EXPORT void * + shift_alloc(Allocator *ctx, size_t sz) { + char *real = (char *)ctx->malloc(sz + 64); + if (real == NULL) { + return NULL; + } + snprintf(real, 64, "originally allocated %ld", (unsigned long)sz); + return (void *)(real + 64); + } + + NPY_NO_EXPORT void * + shift_zero(Allocator *ctx, size_t sz, size_t cnt) { + char *real = (char *)ctx->calloc(sz + 64, cnt); + if (real == NULL) { + return NULL; + } + snprintf(real, 64, "originally allocated %ld via zero", + (unsigned long)sz); + return (void *)(real + 64); + } + + NPY_NO_EXPORT void + shift_free(Allocator *ctx, void * p, npy_uintp sz) { + if (p == NULL) { + return ; + } + char *real = (char *)p - 64; + if (strncmp(real, "originally allocated", 20) != 0) { + fprintf(stdout, "uh-oh, unmatched shift_free, " + "no appropriate prefix\\n"); + /* Make C runtime crash by calling free on the wrong address */ + ctx->free((char *)p + 10); + /* ctx->free(real); */ + } + else { + npy_uintp i = (npy_uintp)atoi(real +20); + if (i != sz) { + fprintf(stderr, "uh-oh, unmatched shift_free" + "(ptr, %ld) but allocated %ld\\n", sz, i); + /* This happens when the shape has a 0, only print */ + ctx->free(real); + } + else { + ctx->free(real); + } + } + } + + NPY_NO_EXPORT void * + shift_realloc(Allocator *ctx, void * p, npy_uintp sz) { + if (p != NULL) { + char *real = (char *)p - 64; + if (strncmp(real, "originally allocated", 20) != 0) { + fprintf(stdout, "uh-oh, unmatched shift_realloc\\n"); + return realloc(p, sz); + } + return (void *)((char *)ctx->realloc(real, sz + 64) + 64); + } + else { + char *real = (char *)ctx->realloc(p, sz + 64); + if (real == NULL) { + return NULL; + } + snprintf(real, 64, "originally allocated " + "%ld via realloc", (unsigned long)sz); + return (void *)(real + 64); + } + } + + static Allocator new_handler_ctx = { + malloc, + calloc, + realloc, + free + }; + + static PyDataMem_Handler new_handler = { + "secret_data_allocator", + 1, + { + &new_handler_ctx, + shift_alloc, /* malloc */ + shift_zero, /* calloc */ + shift_realloc, /* realloc */ + shift_free /* free */ + } + }; + +Related Work +------------ + +This NEP is being tracked by the pnumpy_ project and a `comment in the PR`_ +mentions use in orchestrating FPGA DMAs. + +Implementation +-------------- + +This NEP has been implemented in `PR 17582`_. + +Alternatives +------------ + +These were discussed in `issue 17467`_. `PR 5457`_ and `PR 5470`_ proposed a +global interface for specifying aligned allocations. + +``PyArray_malloc_aligned`` and friends were added to NumPy with the +`numpy.random` module API refactor. and are used there for performance. + +`PR 390`_ had two parts: expose ``PyDataMem_*`` via the NumPy C-API, and a hook +mechanism. The PR was merged with no example code for using these features. + +Discussion +---------- + +The discussion on the mailing list led to the ``PyDataMemAllocator`` struct +with a ``context`` field like :c:type:`PyMemAllocatorEx` but with a different +signature for ``free``. + + +References and Footnotes +------------------------ + +.. [1] Each NEP must either be explicitly labeled as placed in the public domain (see + this NEP as an example) or licensed under the `Open Publication License`_. + +.. _Open Publication License: https://www.opencontent.org/openpub/ + +.. _`PR 17582`: https://github.com/numpy/numpy/pull/17582 +.. _`PR 5457`: https://github.com/numpy/numpy/pull/5457 +.. _`PR 5470`: https://github.com/numpy/numpy/pull/5470 +.. _`15780`: https://github.com/numpy/numpy/pull/15780 +.. _`15788`: https://github.com/numpy/numpy/pull/15788 +.. _`PR 390`: https://github.com/numpy/numpy/pull/390 +.. _`issue 17467`: https://github.com/numpy/numpy/issues/17467 +.. _`comment in the PR`: https://github.com/numpy/numpy/pull/17582#issuecomment-809145547 +.. _pnumpy: https://quansight.github.io/pnumpy/stable/index.html + +Copyright +--------- + +This document has been placed in the public domain. [1]_ diff --git a/doc/neps/nep-template.rst b/doc/neps/nep-template.rst index 26515127dd48..bbb48eaae5ba 100644 --- a/doc/neps/nep-template.rst +++ b/doc/neps/nep-template.rst @@ -1,6 +1,6 @@ -============================= -NEP Template and Instructions -============================= +================================= +NEP X — Template and instructions +================================= :Author: <list of authors' real names and optionally, email addresses> :Status: <Draft | Active | Accepted | Deferred | Rejected | Withdrawn | Final | Superseded> @@ -8,19 +8,57 @@ NEP Template and Instructions :Created: <date created on, in yyyy-mm-dd format> :Resolution: <url> (required for Accepted | Rejected | Withdrawn) + Abstract -------- The abstract should be a short description of what the NEP will achieve. +Note that the — in the title is an elongated dash, not -. + +Motivation and Scope +-------------------- + +This section describes the need for the proposed change. It should describe +the existing problem, who it affects, what it is trying to solve, and why. +This section should explicitly address the scope of and key requirements for +the proposed change. + +Usage and Impact +---------------- + +This section describes how users of NumPy will use features described in this +NEP. It should be comprised mainly of code examples that wouldn't be possible +without acceptance and implementation of this NEP, as well as the impact the +proposed changes would have on the ecosystem. This section should be written +from the perspective of the users of NumPy, and the benefits it will provide +them; and as such, it should include implementation details only if +necessary to explain the functionality. + +Backward compatibility +---------------------- + +This section describes the ways in which the NEP breaks backward compatibility. + +The mailing list post will contain the NEP up to and including this section. +Its purpose is to provide a high-level summary to users who are not interested +in detailed technical discussion, but may have opinions around, e.g., usage and +impact. Detailed description -------------------- -This section describes the need for the NEP. It should describe the existing -problem that it is trying to solve and why this NEP makes the situation better. -It should include examples of how the new functionality would be used and -perhaps some use cases. +This section should provide a detailed description of the proposed change. +It should include examples of how the new functionality would be used, +intended use-cases and pseudo-code illustrating its use. + + +Related Work +------------ + +This section should list relevant and/or similar technologies, possibly in other +libraries. It does not need to be comprehensive, just list the major examples of +prior and relevant art. Implementation @@ -28,20 +66,14 @@ Implementation This section lists the major steps required to implement the NEP. Where possible, it should be noted where one step is dependent on another, and which -steps may be optionally omitted. Where it makes sense, each step should -include a link related pull requests as the implementation progresses. +steps may be optionally omitted. Where it makes sense, each step should +include a link to related pull requests as the implementation progresses. Any pull requests or development branches containing work on this NEP should be linked to from here. (A NEP does not need to be implemented in a single pull request if it makes sense to implement it in discrete phases). -Backward compatibility ----------------------- - -This section describes the ways in which the NEP breaks backward compatibility. - - Alternatives ------------ @@ -64,7 +96,7 @@ References and Footnotes .. [1] Each NEP must either be explicitly labeled as placed in the public domain (see this NEP as an example) or licensed under the `Open Publication License`_. -.. _Open Publication License: http://www.opencontent.org/openpub/ +.. _Open Publication License: https://www.opencontent.org/openpub/ Copyright diff --git a/doc/neps/roadmap.rst b/doc/neps/roadmap.rst new file mode 100644 index 000000000000..7e5d1a03b0a8 --- /dev/null +++ b/doc/neps/roadmap.rst @@ -0,0 +1,136 @@ +============= +NumPy Roadmap +============= + +This is a live snapshot of tasks and features we will be investing resources +in. It may be used to encourage and inspire developers and to search for +funding. + + +Interoperability +---------------- + +We aim to make it easier to interoperate with NumPy. There are many NumPy-like +packages that add interesting new capabilities to the Python ecosystem, as well +as many libraries that extend NumPy's model in various ways. Work in NumPy to +facilitate interoperability with all such packages, and the code that uses them, +may include (among other things) interoperability protocols, better duck typing +support and ndarray subclass handling. + +The key goal is: *make it easy for code written for NumPy to also work with +other NumPy-like projects.* This will enable GPU support via, e.g, CuPy or JAX, +distributed array support via Dask, and writing special-purpose arrays (either +from scratch, or as a ``numpy.ndarray`` subclass) that work well with SciPy, +scikit-learn and other such packages. + +The ``__array_ufunc__`` and ``__array_function__`` protocols are stable, but +do not cover the whole API. New protocols for overriding other functionality +in NumPy are needed. Work in this area aims to bring to completion one or more +of the following proposals: + +- :ref:`NEP30` +- :ref:`NEP31` +- :ref:`NEP35` +- :ref:`NEP37` + +In addition we aim to provide ways to make it easier for other libraries to +implement a NumPy-compatible API. This may include defining consistent subsets +of the API, as discussed in `this section of NEP 37 +<https://numpy.org/neps/nep-0037-array-module.html#requesting-restricted-subsets-of-numpy-s-api>`__. + + +Performance +----------- + +Improvements to NumPy's performance are important to many users. We have +focused this effort on Universal SIMD (see :ref:`NEP38`) intrinsics which +provide nice improvements across various hardware platforms via an abstraction +layer. The infrastructure is in place, and we welcome follow-on PRs to add +SIMD support across all relevant NumPy functions. + +Other performance improvement ideas include: + +- A better story around parallel execution. +- Optimizations in individual functions. +- Reducing ufunc and ``__array_function__`` overhead. + +Furthermore we would like to improve the benchmarking system, in terms of coverage, +easy of use, and publication of the results (now +`here <https://pv.github.io/numpy-bench>`__) as part of the docs or website. + + +Documentation and website +------------------------- + +The NumPy `documentation <https://www.numpy.org/devdocs>`__ is of varying +quality. The API documentation is in good shape; tutorials and high-level +documentation on many topics are missing or outdated. See :ref:`NEP44` for +planned improvements. Adding more tutorials is underway in the +`numpy-tutorials repo <https://github.com/numpy/numpy-tutorials>`__. + +Our website (https://numpy.org) was completely redesigned recently. We aim to +further improve it by adding translations, more case studies and other +high-level content, and more (see `this tracking issue <https://github.com/numpy/numpy.org/issues/266>`__). + + +Extensibility +------------- + +We aim to make it much easier to extend NumPy. The primary topic here is to +improve the dtype system - see :ref:`NEP41` and related NEPs linked from it. +Concrete goals for the dtype system rewrite are: + +- Easier custom dtypes: + + - Simplify and/or wrap the current C-API + - More consistent support for dtype metadata + - Support for writing a dtype in Python + +- Allow adding (a) new string dtype(s). This could be encoded strings with + fixed-width storage (e.g., ``utf8`` or ``latin1``), and/or a variable length + string dtype. The latter could share an implementation with ``dtype=object``, + but be explicitly type-checked. + One of these should probably be the default for text data. The current + string dtype support is neither efficient nor user friendly. + + +User experience +--------------- + +Type annotations +```````````````` +NumPy 1.20 adds type annotations for most NumPy functionality, so users can use +tools like `mypy`_ to type check their code and IDEs can improve their support +for NumPy. Improving those type annotations, for example to support annotating +array shapes and dtypes, is ongoing. + +Platform support +```````````````` +We aim to increase our support for different hardware architectures. This +includes adding CI coverage when CI services are available, providing wheels on +PyPI for POWER8/9 (``ppc64le``), providing better build and install +documentation, and resolving build issues on other platforms like AIX. + + +Maintenance +----------- + +- ``MaskedArray`` needs to be improved, ideas include: + + - Rewrite masked arrays to not be a ndarray subclass -- maybe in a separate project? + - MaskedArray as a duck-array type, and/or + - dtypes that support missing values + +- Fortran integration via ``numpy.f2py`` requires a number of improvements, see + `this tracking issue <https://github.com/numpy/numpy/issues/14938>`__. +- A backend system for ``numpy.fft`` (so that e.g. ``fft-mkl`` doesn't need to monkeypatch numpy). +- Write a strategy on how to deal with overlap between NumPy and SciPy for ``linalg``. +- Deprecate ``np.matrix`` (very slowly). +- Add new indexing modes for "vectorized indexing" and "outer indexing" (see :ref:`NEP21`). +- Make the polynomial API easier to use. +- Integrate an improved text file loader. +- Ufunc and gufunc improvements, see `gh-8892 <https://github.com/numpy/numpy/issues/8892>`__ + and `gh-11492 <https://github.com/numpy/numpy/issues/11492>`__. + + +.. _`mypy`: https://mypy.readthedocs.io diff --git a/doc/neps/scope.rst b/doc/neps/scope.rst new file mode 100644 index 000000000000..93887c4b12ff --- /dev/null +++ b/doc/neps/scope.rst @@ -0,0 +1,48 @@ +============== +Scope of NumPy +============== + +Here, we describe aspects of N-d array computation that are within scope for NumPy development. This is *not* an aspirational definition of where NumPy should aim, but instead captures the status quo—areas which we have decided to continue supporting, at least for the time being. + +- **In-memory, N-dimensional, homogeneously typed (single pointer + strided) arrays on CPUs** + + - Support for a wide range of data types + - Not specialized hardware such as GPUs + - But, do support wide range of CPUs (e.g. ARM, PowerX) + +- **Higher level APIs for N-dimensional arrays** + + - NumPy is a *de facto* standard for array APIs in Python + - Indexing and fast iteration over elements (ufunc) + - Interoperability protocols with other data container implementations (like + :ref:`__array_ufunc__ and __array_function__ <basics.dispatch>`. + +- **Python API and a C API** to the ndarray's methods and attributes. + +- Other **specialized types or uses of N-dimensional arrays**: + + - Masked arrays + - Structured arrays (informally known as record arrays) + - Memory mapped arrays + +- Historically, NumPy has included the following **basic functionality + in support of scientific computation**. We intend to keep supporting + (but not to expand) what is currently included: + + - Linear algebra + - Fast Fourier transforms and windowing + - Pseudo-random number generators + - Polynomial fitting + +- NumPy provides some **infrastructure for other packages in the scientific Python ecosystem**: + + - numpy.distutils (build support for C++, Fortran, BLAS/LAPACK, and other + relevant libraries for scientific computing) + - f2py (generating bindings for Fortran code) + - testing utilities + +- **Speed**: we take performance concerns seriously and aim to execute + operations on large arrays with similar performance as native C + code. That said, where conflict arises, maintenance and portability take + precedence over performance. We aim to prevent regressions where + possible (e.g., through asv). diff --git a/doc/neps/tools/build_index.py b/doc/neps/tools/build_index.py index 65225c995744..51227a6f1273 100644 --- a/doc/neps/tools/build_index.py +++ b/doc/neps/tools/build_index.py @@ -4,7 +4,6 @@ """ import os -import sys import jinja2 import glob import re @@ -23,6 +22,7 @@ def nep_metadata(): meta_re = r':([a-zA-Z\-]*): (.*)' + has_provisional = False neps = {} print('Loading metadata for:') for source in sources: @@ -35,11 +35,23 @@ def nep_metadata(): tags = [match.groups() for match in tags if match is not None] tags = {tag[0]: tag[1] for tag in tags} - # We could do a clever regexp, but for now just assume the title is - # the second line of the document - tags['Title'] = lines[1].strip() + # The title should be the first line after a line containing only + # * or = signs. + for i, line in enumerate(lines[:-1]): + chars = set(line.rstrip()) + if len(chars) == 1 and ("=" in chars or "*" in chars): + break + else: + raise RuntimeError("Unable to find NEP title.") + + tags['Title'] = lines[i+1].strip() tags['Filename'] = source + if not tags['Title'].startswith(f'NEP {nr} — '): + raise RuntimeError( + f'Title for NEP {nr} does not start with "NEP {nr} — " ' + '(note that — here is a special, enlongated dash). Got: ' + f' {tags["Title"]!r}') if tags['Status'] in ('Accepted', 'Rejected', 'Withdrawn'): if not 'Resolution' in tags: @@ -47,6 +59,8 @@ def nep_metadata(): f'NEP {nr} is Accepted/Rejected/Withdrawn but ' 'has no Resolution tag' ) + if tags['Status'] == 'Provisional': + has_provisional = True neps[nr] = tags @@ -84,7 +98,7 @@ def nep_metadata(): f'been set to Superseded' ) - return {'neps': neps} + return {'neps': neps, 'has_provisional': has_provisional} infile = 'index.rst.tmpl' diff --git a/doc/newdtype_example/example.py b/doc/newdtype_example/example.py deleted file mode 100644 index 6be9caa756b6..000000000000 --- a/doc/newdtype_example/example.py +++ /dev/null @@ -1,18 +0,0 @@ -from __future__ import division, absolute_import, print_function - -import floatint.floatint as ff -import numpy as np - -# Setting using array is hard because -# The parser doesn't stop at tuples always -# So, the setitem code will be called with scalars on the -# wrong shaped array. -# But we can get a view as an ndarray of the given type: -g = np.array([1, 2, 3, 4, 5, 6, 7, 8]).view(ff.floatint_type) - -# Now, the elements will be the scalar type associated -# with the ndarray. -print(g[0]) -print(type(g[1])) - -# Now, you need to register ufuncs and more arrfuncs to do useful things... diff --git a/doc/newdtype_example/floatint.c b/doc/newdtype_example/floatint.c deleted file mode 100644 index 0cc198388f97..000000000000 --- a/doc/newdtype_example/floatint.c +++ /dev/null @@ -1,152 +0,0 @@ - -#include "Python.h" -#include "structmember.h" /* for offset of macro if needed */ -#include "numpy/arrayobject.h" - - -/* Use a Python float as the canonical type being added -*/ - -typedef struct _floatint { - PyObject_HEAD - npy_int32 first; - npy_int32 last; -} PyFloatIntObject; - -static PyTypeObject PyFloatInt_Type = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ - "floatint.floatint", /*tp_name*/ - sizeof(PyFloatIntObject), /*tp_basicsize*/ -}; - -static PyArray_ArrFuncs _PyFloatInt_Funcs; - -#define _ALIGN(type) offsetof(struct {char c; type v;},v) - -/* The scalar-type */ - -static PyArray_Descr _PyFloatInt_Dtype = { - PyObject_HEAD_INIT(NULL) - &PyFloatInt_Type, - 'f', - '0', - '=', - 0, - 0, - sizeof(double), - _ALIGN(double), - NULL, - NULL, - NULL, - &_PyFloatInt_Funcs -}; - -static void -twoint_copyswap(void *dst, void *src, int swap, void *arr) -{ - if (src != NULL) { - memcpy(dst, src, sizeof(double)); - } - - if (swap) { - register char *a, *b, c; - a = (char *)dst; - b = a + 7; - c = *a; *a++ = *b; *b-- = c; - c = *a; *a++ = *b; *b-- = c; - c = *a; *a++ = *b; *b-- = c; - c = *a; *a++ = *b; *b = c; - } -} - -static PyObject * -twoint_getitem(char *ip, PyArrayObject *ap) { - npy_int32 a[2]; - - if ((ap==NULL) || PyArray_ISBEHAVED_RO(ap)) { - a[0] = *((npy_int32 *)ip); - a[1] = *((npy_int32 *)ip + 1); - } - else { - ap->descr->f->copyswap(a, ip, !PyArray_ISNOTSWAPPED(ap), ap); - } - return Py_BuildValue("(ii)", a[0], a[1]); -} - -static int -twoint_setitem(PyObject *op, char *ov, PyArrayObject *ap) { - npy_int32 a[2]; - - if (!PyTuple_Check(op)) { - PyErr_SetString(PyExc_TypeError, "must be a tuple"); - return -1; - } - if (!PyArg_ParseTuple(op, "ii", a, a+1)) return -1; - - if (ap == NULL || PyArray_ISBEHAVED(ap)) { - memcpy(ov, a, sizeof(double)); - } - else { - ap->descr->f->copyswap(ov, a, !PyArray_ISNOTSWAPPED(ap), ap); - } - return 0; -} - -static PyArray_Descr * _register_dtype(void) -{ - int userval; - PyArray_InitArrFuncs(&_PyFloatInt_Funcs); - /* Add copyswap, - nonzero, getitem, setitem*/ - _PyFloatInt_Funcs.copyswap = twoint_copyswap; - _PyFloatInt_Funcs.getitem = (PyArray_GetItemFunc *)twoint_getitem; - _PyFloatInt_Funcs.setitem = (PyArray_SetItemFunc *)twoint_setitem; - _PyFloatInt_Dtype.ob_type = &PyArrayDescr_Type; - - userval = PyArray_RegisterDataType(&_PyFloatInt_Dtype); - return PyArray_DescrFromType(userval); -} - - -/* Initialization function for the module (*must* be called init<name>) */ - -PyMODINIT_FUNC initfloatint(void) { - PyObject *m, *d; - PyArray_Descr *dtype; - - /* Create the module and add the functions */ - m = Py_InitModule("floatint", NULL); - - /* Import the array objects */ - import_array(); - - - /* Initialize the new float type */ - - /* Add some symbolic constants to the module */ - d = PyModule_GetDict(m); - - if (PyType_Ready(&PyFloat_Type) < 0) return; - PyFloatInt_Type.tp_base = &PyFloat_Type; - /* This is only needed because we are sub-typing the - Float type and must pre-set some function pointers - to get PyType_Ready to fill in the rest. - */ - PyFloatInt_Type.tp_alloc = PyType_GenericAlloc; - PyFloatInt_Type.tp_new = PyFloat_Type.tp_new; - PyFloatInt_Type.tp_dealloc = PyFloat_Type.tp_dealloc; - PyFloatInt_Type.tp_free = PyObject_Del; - if (PyType_Ready(&PyFloatInt_Type) < 0) return; - /* End specific code */ - - - dtype = _register_dtype(); - Py_XINCREF(dtype); - if (dtype != NULL) { - PyDict_SetItemString(d, "floatint_type", (PyObject *)dtype); - } - Py_INCREF(&PyFloatInt_Type); - PyDict_SetItemString(d, "floatint", (PyObject *)&PyFloatInt_Type); - return; -} diff --git a/doc/newdtype_example/floatint/__init__.py b/doc/newdtype_example/floatint/__init__.py deleted file mode 100644 index 1d0f69b67d8f..000000000000 --- a/doc/newdtype_example/floatint/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import division, absolute_import, print_function diff --git a/doc/newdtype_example/setup.py b/doc/newdtype_example/setup.py deleted file mode 100644 index d7ab040a1723..000000000000 --- a/doc/newdtype_example/setup.py +++ /dev/null @@ -1,13 +0,0 @@ -from __future__ import division, print_function - -from numpy.distutils.core import setup - -def configuration(parent_package = '', top_path=None): - from numpy.distutils.misc_util import Configuration - config = Configuration('floatint', parent_package, top_path) - - config.add_extension('floatint', - sources = ['floatint.c']) - return config - -setup(configuration=configuration) diff --git a/doc/postprocess.py b/doc/postprocess.py index 2e50c115edbe..3e066d22eb9e 100755 --- a/doc/postprocess.py +++ b/doc/postprocess.py @@ -1,42 +1,28 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """ -%prog MODE FILES... - Post-processes HTML and Latex files output by Sphinx. -MODE is either 'html' or 'tex'. - """ -from __future__ import division, absolute_import, print_function - -import re -import optparse import io def main(): - p = optparse.OptionParser(__doc__) - options, args = p.parse_args() - - if len(args) < 1: - p.error('no mode given') + import argparse - mode = args.pop(0) + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('mode', help='file mode', choices=('html', 'tex')) + parser.add_argument('file', nargs='+', help='input file(s)') + args = parser.parse_args() - if mode not in ('html', 'tex'): - p.error('unknown mode %s' % mode) + mode = args.mode - for fn in args: - f = io.open(fn, 'r', encoding="utf-8") - try: + for fn in args.file: + with io.open(fn, 'r', encoding="utf-8") as f: if mode == 'html': lines = process_html(fn, f.readlines()) elif mode == 'tex': lines = process_tex(f.readlines()) - finally: - f.close() - f = io.open(fn, 'w', encoding="utf-8") - f.write("".join(lines)) - f.close() + with io.open(fn, 'w', encoding="utf-8") as f: + f.write("".join(lines)) def process_html(fn, lines): return lines diff --git a/doc/preprocess.py b/doc/preprocess.py new file mode 100755 index 000000000000..870d3e12301c --- /dev/null +++ b/doc/preprocess.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +import subprocess +import os +import sys +from string import Template + +def main(): + doxy_gen(os.path.abspath(os.path.join('..'))) + +def doxy_gen(root_path): + """ + Generate Doxygen configuration file. + """ + confs = doxy_config(root_path) + build_path = os.path.join(root_path, "doc", "build", "doxygen") + gen_path = os.path.join(build_path, "Doxyfile") + if not os.path.exists(build_path): + os.makedirs(build_path) + with open(gen_path, 'w') as fd: + fd.write("#Please Don't Edit! This config file was autogenerated by ") + fd.write(f"doxy_gen({root_path}) in doc/preprocess.py.\n") + for c in confs: + fd.write(c) + +class DoxyTpl(Template): + delimiter = '@' + +def doxy_config(root_path): + """ + Fetch all Doxygen sub-config files and gather it with the main config file. + """ + confs = [] + dsrc_path = os.path.join(root_path, "doc", "source") + sub = dict(ROOT_DIR=root_path) + with open(os.path.join(dsrc_path, "doxyfile"), "r") as fd: + conf = DoxyTpl(fd.read()) + confs.append(conf.substitute(CUR_DIR=dsrc_path, **sub)) + + for dpath, _, files in os.walk(root_path): + if ".doxyfile" not in files: + continue + conf_path = os.path.join(dpath, ".doxyfile") + with open(conf_path, "r") as fd: + conf = DoxyTpl(fd.read()) + confs.append(conf.substitute(CUR_DIR=dpath, **sub)) + return confs + + +if __name__ == "__main__": + main() + diff --git a/doc/records.rst.txt b/doc/records.rst.txt index a608880d7f6f..3c0d5521626d 100644 --- a/doc/records.rst.txt +++ b/doc/records.rst.txt @@ -50,7 +50,7 @@ New possibilities for the "data-type" **Dictionary (keys "names", "titles", and "formats")** - This will be converted to a ``PyArray_VOID`` type with corresponding + This will be converted to a ``NPY_VOID`` type with corresponding fields parameter (the formats list will be converted to actual ``PyArray_Descr *`` objects). @@ -58,10 +58,10 @@ New possibilities for the "data-type" **Objects (anything with an .itemsize and .fields attribute)** If its an instance of (a sub-class of) void type, then a new ``PyArray_Descr*`` structure is created corresponding to its - typeobject (and ``PyArray_VOID``) typenumber. If the type is + typeobject (and ``NPY_VOID``) typenumber. If the type is registered, then the registered type-number is used. - Otherwise a new ``PyArray_VOID PyArray_Descr*`` structure is created + Otherwise a new ``NPY_VOID PyArray_Descr*`` structure is created and filled ->elsize and ->fields filled in appropriately. The itemsize attribute must return a number > 0. The fields diff --git a/doc/release/time_based_proposal.rst b/doc/release/time_based_proposal.rst deleted file mode 100644 index 555be68633ad..000000000000 --- a/doc/release/time_based_proposal.rst +++ /dev/null @@ -1,129 +0,0 @@ -.. vim:syntax=rst - -Introduction -============ - -This document proposes some enhancements for numpy and scipy releases. -Successive numpy and scipy releases are too far apart from a time point of -view - some people who are in the numpy release team feel that it cannot -improve without a bit more formal release process. The main proposal is to -follow a time-based release, with expected dates for code freeze, beta and rc. -The goal is two folds: make release more predictable, and move the code forward. - -Rationale -========= - -Right now, the release process of numpy is relatively organic. When some -features are there, we may decide to make a new release. Because there is not -fixed schedule, people don't really know when new features and bug fixes will -go into a release. More significantly, having an expected release schedule -helps to *coordinate* efforts: at the beginning of a cycle, everybody can jump -in and put new code, even break things if needed. But after some point, only -bug fixes are accepted: this makes beta and RC releases much easier; calming -things down toward the release date helps focusing on bugs and regressions - -Proposal -======== - -Time schedule -------------- - -The proposed schedule is to release numpy every 9 weeks - the exact period can -be tweaked if it ends up not working as expected. There will be several stages -for the cycle: - - * Development: anything can happen (by anything, we mean as currently - done). The focus is on new features, refactoring, etc... - - * Beta: no new features. No bug fixing which requires heavy changes. - regression fixes which appear on supported platforms and were not - caught earlier. - - * Polish/RC: only docstring changes and blocker regressions are allowed. - -The schedule would be as follows: - - +------+-----------------+-----------------+------------------+ - | Week | 1.3.0 | 1.4.0 | Release time | - +======+=================+=================+==================+ - | 1 | Development | | | - +------+-----------------+-----------------+------------------+ - | 2 | Development | | | - +------+-----------------+-----------------+------------------+ - | 3 | Development | | | - +------+-----------------+-----------------+------------------+ - | 4 | Development | | | - +------+-----------------+-----------------+------------------+ - | 5 | Development | | | - +------+-----------------+-----------------+------------------+ - | 6 | Development | | | - +------+-----------------+-----------------+------------------+ - | 7 | Beta | | | - +------+-----------------+-----------------+------------------+ - | 8 | Beta | | | - +------+-----------------+-----------------+------------------+ - | 9 | Beta | | 1.3.0 released | - +------+-----------------+-----------------+------------------+ - | 10 | Polish | Development | | - +------+-----------------+-----------------+------------------+ - | 11 | Polish | Development | | - +------+-----------------+-----------------+------------------+ - | 12 | Polish | Development | | - +------+-----------------+-----------------+------------------+ - | 13 | Polish | Development | | - +------+-----------------+-----------------+------------------+ - | 14 | | Development | | - +------+-----------------+-----------------+------------------+ - | 15 | | Development | | - +------+-----------------+-----------------+------------------+ - | 16 | | Beta | | - +------+-----------------+-----------------+------------------+ - | 17 | | Beta | | - +------+-----------------+-----------------+------------------+ - | 18 | | Beta | 1.4.0 released | - +------+-----------------+-----------------+------------------+ - -Each stage can be defined as follows: - - +------------------+-------------+----------------+----------------+ - | | Development | Beta | Polish | - +==================+=============+================+================+ - | Python Frozen | | slushy | Y | - +------------------+-------------+----------------+----------------+ - | Docstring Frozen | | slushy | thicker slush | - +------------------+-------------+----------------+----------------+ - | C code Frozen | | thicker slush | thicker slush | - +------------------+-------------+----------------+----------------+ - -Terminology: - - * slushy: you can change it if you beg the release team and it's really - important and you coordinate with docs/translations; no "big" - changes. - - * thicker slush: you can change it if it's an open bug marked - showstopper for the Polish release, you beg the release team, the - change is very very small yet very very important, and you feel - extremely guilty about your transgressions. - -The different frozen states are intended to be gradients. The exact meaning is -decided by the release manager: he has the last word on what's go in, what -doesn't. The proposed schedule means that there would be at most 12 weeks -between putting code into the source code repository and being released. - -Release team ------------- - -For every release, there would be at least one release manager. We propose to -rotate the release manager: rotation means it is not always the same person -doing the dirty job, and it should also keep the release manager honest. - -References -========== - - * Proposed schedule for Gnome from Havoc Pennington (one of the core - GTK and Gnome manager): - http://mail.gnome.org/archives/gnome-hackers/2002-June/msg00041.html - The proposed schedule is heavily based on this email - - * http://live.gnome.org/ReleasePlanning/Freezes diff --git a/doc/release/upcoming_changes/README.rst b/doc/release/upcoming_changes/README.rst new file mode 100644 index 000000000000..436535ecddbc --- /dev/null +++ b/doc/release/upcoming_changes/README.rst @@ -0,0 +1,61 @@ +:orphan: + +Changelog +========= + +This directory contains "news fragments" which are short files that contain a +small **ReST**-formatted text that will be added to the next what's new page. + +Make sure to use full sentences with correct case and punctuation, and please +try to use Sphinx intersphinx using backticks. The fragment should have a +header line and an underline using ``------`` + +Each file should be named like ``<PULL REQUEST>.<TYPE>.rst``, where +``<PULL REQUEST>`` is a pull request number, and ``<TYPE>`` is one of: + +* ``new_function``: New user facing functions. +* ``deprecation``: Changes existing code to emit a DeprecationWarning. +* ``future``: Changes existing code to emit a FutureWarning. +* ``expired``: Removal of a deprecated part of the API. +* ``compatibility``: A change which requires users to change code and is not + backwards compatible. (Not to be used for removal of deprecated features.) +* ``c_api``: Changes in the Numpy C-API exported functions +* ``new_feature``: New user facing features like ``kwargs``. +* ``improvement``: General improvements and edge-case changes which are + not new features or compatibility related. +* ``performance``: Performance changes that should not affect other behaviour. +* ``change``: Other changes +* ``highlight``: Adds a highlight bullet point to use as a possibly highlight + of the release. + +It is possible to add two files with different categories (and text) if both +are relevant. For example a change may improve performance but have some +compatibility concerns. + +Most categories should be formatted as paragraphs with a heading. +So for example: ``123.new_feature.rst`` would have the content:: + + ``my_new_feature`` option for `my_favorite_function` + ---------------------------------------------------- + The ``my_new_feature`` option is now available for `my_favorite_function`. + To use it, write ``np.my_favorite_function(..., my_new_feature=True)``. + +``highlight`` is usually formatted as bulled points making the fragment +``* This is a highlight``. + +Note the use of single-backticks to get an internal link (assuming +``my_favorite_function`` is exported from the ``numpy`` namespace), +and double-backticks for code. + +If you are unsure what pull request type to use, don't hesitate to ask in your +PR. + +You can install ``towncrier`` and run ``towncrier build --draft --version 1.18`` +if you want to get a preview of how your change will look in the final release +notes. + +.. note:: + + This README was adapted from the pytest changelog readme under the terms of + the MIT licence. + diff --git a/doc/release/upcoming_changes/template.rst b/doc/release/upcoming_changes/template.rst new file mode 100644 index 000000000000..997b4850ea58 --- /dev/null +++ b/doc/release/upcoming_changes/template.rst @@ -0,0 +1,39 @@ +{% set title = "NumPy {} Release Notes".format(versiondata.version) %} +{{ "=" * title|length }} +{{ title }} +{{ "=" * title|length }} + +{% for section, _ in sections.items() %} +{% set underline = underlines[0] %}{% if section %}{{ section }} +{{ underline * section|length }}{% set underline = underlines[1] %} + +{% endif %} +{% if sections[section] %} +{% for category, val in definitions.items() if category in sections[section] %} + +{{ definitions[category]['name'] }} +{{ underline * definitions[category]['name']|length }} + +{% if definitions[category]['showcontent'] %} +{% for text, values in sections[section][category].items() %} +{{ text }} + +{{ get_indent(text) }}({{values|join(', ') }}) + +{% endfor %} +{% else %} +- {{ sections[section][category]['']|join(', ') }} + +{% endif %} +{% if sections[section][category]|length == 0 %} +No significant changes. + +{% else %} +{% endif %} +{% endfor %} +{% else %} +No significant changes. + + +{% endif %} +{% endfor %} diff --git a/doc/scipy-sphinx-theme b/doc/scipy-sphinx-theme deleted file mode 160000 index d990ab913419..000000000000 --- a/doc/scipy-sphinx-theme +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d990ab9134199f6496b9ac8567f10791f04a720a diff --git a/doc/source/_static/favicon/apple-touch-icon.png b/doc/source/_static/favicon/apple-touch-icon.png new file mode 100644 index 000000000000..e6cd574260aa Binary files /dev/null and b/doc/source/_static/favicon/apple-touch-icon.png differ diff --git a/doc/source/_static/favicon/favicon-16x16.png b/doc/source/_static/favicon/favicon-16x16.png new file mode 100644 index 000000000000..95beb08342d6 Binary files /dev/null and b/doc/source/_static/favicon/favicon-16x16.png differ diff --git a/doc/source/_static/favicon/favicon-32x32.png b/doc/source/_static/favicon/favicon-32x32.png new file mode 100644 index 000000000000..cc06622fa04b Binary files /dev/null and b/doc/source/_static/favicon/favicon-32x32.png differ diff --git a/doc/source/_static/favicon/favicon.ico b/doc/source/_static/favicon/favicon.ico new file mode 100644 index 000000000000..4ed63bf67ed4 Binary files /dev/null and b/doc/source/_static/favicon/favicon.ico differ diff --git a/doc/source/_static/index-images/api.svg b/doc/source/_static/index-images/api.svg new file mode 100644 index 000000000000..e637525cc0b6 --- /dev/null +++ b/doc/source/_static/index-images/api.svg @@ -0,0 +1,31 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100" height="100" viewBox="0 0 1170 1063"> + <metadata><?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> +<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c138 79.159824, 2016/09/14-01:09:01 "> + <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> + <rdf:Description rdf:about=""/> + </rdf:RDF> +</x:xmpmeta> + + + + + + + + + + + + + + + + + + + + + +<?xpacket end="w"?></metadata> +<image width="1170" height="1063" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABJIAAAQnCAMAAACXNLbrAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAANlBMVEUBMkMBMkMBMkMBMkMBMkMBMkMBMkMBMkMBMkMBMkMBMkMBMkMBMkMBMkMBMkMBMkMBMkMAAACj98WdAAAAEHRSTlMAQL+AIBBgn1CP36/vcDDPTJL3MQAAAAFiS0dEEeK1PboAAAAJcEhZcwAALiMAAC4jAXilP3YAAAAHdElNRQflCgkWEi2YZYTeAAAjTUlEQVR42u3dDVoUyRKG0QRERfFn/6u903J11FHpbjIrPjPOWUE8mPFaVV0N4zPL3Ay2dFN9snY2qgfYmSRtSpIWGtUD7EySNiVJC43qAXYmSZuSpIVG9QA7k6RNSdJCo3qAnUnSpiRpoVE9wM4kaVOStNCoHmBnkrQpSVpoVA+wM0nalCQtNKoH2JkkbUqSFhrVA+xMkjYlSQuN6gF2JkmbkqSFRvUAO5OkTUnSQqN6gJ1J0qYkaaFRPcDOJGlTkrTQqB5gZ5K0KUlaaFQPsDNJ2pQkLTSqB9iZJG1KkhYa1QPsTJI2JUkLjeoBdiZJm5KkhUb1ADuTpE1J0kKjeoCdSdKmJGmhUT3AziRpU5K00KgeYGeStClJWmhUD7AzSdqUJC00qgfYmSRtSpIWGtUD7EySNiVJC43qAXYmSZuSpIVG9QA7k6RNSdJCo3qAnUnSpiRpoVE9wM4kaVOStNCoHmBnkrQpSVpoVA+wM0nalCQtNKoH2JkkbUqSFhrVA+xMkjYlSQuN6gF2JkmbkqSFRvUAO5OkTUnSQqN6gJ1J0qYkaaFRPcDOJGlTkrTQqB5gZ5K0KUlaaFQPsDNJ2pQkLTSqB9iZJG1KkhYa1QPsTJI2JUkLjeoBdiZJm5KkhUb1ADuTpE1J0kKjeoCdSdKmJGmhUT3AziRpU5K00KgeYGeStClJWmhUD7AzSdqUJC00qgfYmSRtSpIWGtUD7EySNiVJC43qAXYmSZuSpIVG9QA7k6RNSdJCo3qAnUnSpiRpoVE9wM4kaVOStNCoHmBnkrQpSVpoVA+wM0nalCQtNKoH2JkkbUqSFhrVA+xMkjYlSQuN6gF2JkmbkqSFRvUAO5OkTUnSQqN6gJ1J0qYkaaFRPcDOJGlTkrTQqB5gZ5K0KUlaaFQPsDNJ2pQkLTSqB9iZJG1KkhYa1QPsTJI2JUkLjeoBdiZJm5KkhUb1ADuTpE1J0kKjeoCdSdKmJGmhUT3AziRpU5K00KgeYGeStClJWmhUD7AzSdqUJC00qgfYmSRtSpIWGtUD7EySNiVJC43qAXYmSZuSpIVG9QA7k6RNSdJCo3qAnUnSpiRpoVE9wM4kaVOStNCoHmBnkrQpSVpoVA+wM0nalCQtNKoH2JkkbUqSFhrVA+xMkjYlSQuN6gF2JkmbkqSFRvUAO5OkTUnSQqN6gJ1J0qYkaaFRPcDOJGlTkrTQqB5gZ5K0KUlaaFQPsDNJ2pQkLTSqB9iZJG1KkhYa1QPsTJI2JUkLjeoBdiZJm5KkhUb1ADuTpE1J0kKjeoCdSdKmJGmhUT3AziRpU5K00KgeYGeStClJWmhUD7AzSdqUJC00qgfYmSRtSpIWGtUD7EySNiVJC43qAXYmSZuSpIVG9QA7k6RNSdJCo3qAnUnSpiRpoVE9wM4kaVOStNCoHmBnkrQpSVpoVA+wM0nalCQtNKoH2JkkbUqSFhrVA+xMkjYlSQuN6gF2JkmbkqSFRvUAO5OkTUnSQqN6gJ1J0qYkaaFRPcDOJGlTkrTQqB5gZ5K0KUlaaFQPsDNJ2pQkLTSqB9iZJG1KkhYa1QPsTJI2JUkLjeoBdiZJm5KkhUb1ADuTpE1J0kKjeoCdSdKmJGmhUT3AziRpU5K00KgeYGeStClJWmhUD7AzSdqUJC00qgfYmSRtSpIWGtUD7EySNiVJC43qAXYmSZuSpIVG9QA7k6RNSdJCo3qAnUnSpiRpoVE9wM4kaVOStNCoHmBnkrQpSVpoVA+wM0nalCQtNKoH+Hx7t61X1avDGq+qT9Y6t9U9CEjSXfX5Ar66q+6BJAH/kiRJgiCSJEkQRJIkCYJIkiRBEEmSJAgiSZIEQSRJkiCIJEkSBJEkSYIgkiRJEESSJAmCSJIkQRBJkiQIIkmSBEEkSZIgiCRJEgSRJEmCIJIkSRBEkiQJgkiSJEEQSZIkCCJJkgRBJEmSIIgkSRIEkSRJgiCSJEkQRJIkCYJIkiRBEEmSJAgiSZIEQSRJkiCIJEkSBJEkSYIgkiRJEESSJAmCSJIkQRBJkiQIIkmSBEEkSZIgiCRJEgSRJEmCIJIkSRBEkiQJgkiSJEEQSZIkCCJJkgRBJEmSIIgkSRIEkSRJgiCSJEkQRJIkCYJIkiRBEEmSJAgiSZIEQSRJkiCIJEkSBJEkSYIgkiRJEESSJAmCSJIkQRBJkiQIIkmSBEEkSZIgiCRJEgSRJEmCIJIkSRBEkiQJgkiSJEEQSZIkCCJJkgRBJEmSIIgkSRIEkSRJgiCSJEkQRJIkCYJIkiRBEEmSJAgiSZIEQSRJkiCIJEkSBJEkSYIgkiRJEESSJAmCSJIkQRBJkiQIIkmSBEEkSZIgiCRJEgSRJEmCIJIkSRBEkiQJgkiSJEEQSZIkCCJJkgRBJEmSIIgkSRIEkSRJgiCSJEkQRJIkCYJIkiRBEEmSJAgiSZIEQSRJkiCIJEkSBJEkSYIgkiRJEESSJAmCSJIkQRBJkiQIIkmSBEEkSZIgiCRJEgSRJEmCIJIkSRBEkiQJgkiSJEEQSZIkCCJJkgRBJEmSIIgkSRIEkSRJgiCSJEkQRJIkCYJIkiRBEEmSJAgiSZIEQSRJkiCIJEkSBJEkSYIgkiRJEESSJAmCSJIkQRBJkiQIIkmSBEEkSZIgiCRJEgSRJEmCIJIkSRBEkiQJgkiSJEEQSZIkCCJJkgRBJEmSIIgkSRIEkSRJgiCSJEkQRJIkCYJIkiRBEEmSJAgiSZIEQSRJkiCIJEkSBJEkSYIgkiRJEESSJAmCSJIkQRBJkiQIIkmSBEEkSZIgiCRJEgSRJEmCIJIkSRBEkiQJgkiSJEEQSZIkCCJJkgRBJEmSIIgkSRIEkSRJgiCSJEkQRJIkCYJIkiRBEEmSJAgiSZIEQSRJkiCIJEkSBJEkSYIgkiRJEESSJAmCSJIkQRBJkiQIIkmSBEEkSZIgiCRJEgSRJEmCIJIkSRBEkiQJgkiSJEEQSZIkCCJJkgRBJEmSIIgkSRIEkSRJgiCSJEkQRJIkCYJIkiRBEEmSJAgiSZIEQSRJkiCIJEkSBJEkSYIgkiRJEESSJAmCSJIkQRBJkiQIIkmSBEEkSZIgiCRJEgSRJEmCIJIkSRBEkiQJgkiSJEEQSZIkCCJJkgRBJEmSIIgkSRIEkSRJgiCSJEkQRJIkCYJIkiRBEEmSJAgiSZIEQSRJkiCIJEkSBJEkSYIgkiRJEESSJAmCSJIkQRBJkiQIIkmSBEEkSZIgiCRJEgSRJEmCIJIkSRBEkiQJgkiSJEEQSZIkCCJJkgRBJEmSIIgkSRIEkSRJgiCSJEkQRJIkCYJIkiRBEEmSJAgiSZIEQSRJkiCIJEkSBJEkSYIgkiRJEESSJAmCSJIkQRBJkiQIIkmSBEEkSZIgiCRJEgSRJEmCIJIkSRBEkiQJgkiSJEEQSZIkCCJJkgRBJEmSIIgkSRIEkSRJgiCSJEkQRJIkCYJIkiRBEEmSJAgiSZIEQSRJkiCIJEkSBJEkSYIgkiRJEESSJAmCSJIkQRBJkiQIIkmSBEEkSZIgiCRJEgSRJEmCIJIkSRBEkiQJgkiSJEEQSZIkCCJJkgRBJEmSIIgkSRIEkSRJgiCSJEkQRJIkCYJIkiRBEEmSJAgiSZIEQSRJkiCIJEkSBJEkSYIgkiRJEESSJAmCSJIkQRBJkiQIIkmSBEEkSZIgiCRJEgSRJEmCIJIkSRBEkiQJgkiSJEEQSZIkCCJJkgRBJEmSIIgkSRIEkSRJgiCSJEkQRJIkCYJIkiRBEEmSJAgiSZIEQSRJkiCIJEkSBJEkSYIgkiRJEESSJAmCSJIkQRBJkiQIIkmSBEEkSZIgiCRJEgSRJEmCIJIkSRBEkiQJgkiSJEEQSZIkCCJJkgRBJEmSIIgkSRIEkSRJgiCSJEkQRJIkCYJIkiRBEEmSJAgiSZIEQSRJkiCIJEkSBJEkSYIgkiRJEESSJAmCSJIkQRBJkiQIIkmSBEEkSZIgiCRJEgSRJEmCIJIkSRBEkiQJgkiSJEEQSZIkCCJJkgRBJEmSIIgkSRIEkSRJgiCSJEkQRJIkCYJIkiRBEEmSJAgiSZIEQSRJkiCIJEkSBJEkSYIgkiRJEESSJAmCSJIkQRBJkiQIIkmSBEEkSZIgiCRJEgSRJEmCIJIkSRBEkiQJgkiSJEEQSZIkCCJJkgRBJEmSIIgkSRIEkSRJgiCSJEkQRJIkCYJIkiRBEEmSJAgiSZIEQSRJkiCIJEkSBJEkSYIgkiRJEESSJAmCSJIkQRBJkiQIIkmSBEEkSZIgiCRJEgSRJEmCIJIkSRBEkiQJgkiSJEEQSZIkCCJJkgRBJEmSIIgkSRIEkSRJgiCSJEkQRJIkCYJIkiRBEEmSJAgiSZIEQSRJkiCIJEkSBJEkSYIgkiRJEESSJAmCSJIkQRBJkiQIIkmSBEEkSZIgiCRJEgSRJEmCIJIkSRBEkiQJgkiSJEEQSZIkCCJJkgRBJEmSIIgkSRIEkSRJgiCSJEkQRJIkCYJIkiRBEEmSJAgiSZIEQSRJkiCIJEkSBJEkSYIgkiRJEESSJAmCSJIkQRBJkiQIIkmSBEEkSZIgiCRJEgSRJEmCIJIkSRBEkiQJgkiSJEEQSZIkCCJJkgRBJEmSIIgkSRIEkSRJgiCSJEkQRJIkCYJIkiRBEEmSJAgiSZIEQSRJkiCIJEkSBJEkSYIgkiRJEESSJAmCSJIkQRBJkiQIIkmSBEEkSZIgiCRJEgSRJEmCIJIkSRBEkiQJgkiSJEEQSZIkCCJJkgRBJEmSIIgkSRIEkSRJgiCSJEkQRJIkCYJIkiRBEEmSJAgiSZIEQSRJkiCIJEkSBJEkSYIgkiRJEESSJAmCSJIkQRBJkiQIIkmSBEEkSZIgiCRJEgSRJEmCIJIkSRBEkiQJgkiSJEEQSZIkCCJJkgRBJEmSIIgkSRIEkSRJgiCSJEkQRJIkCYJIkiSd5/7m9d3dm9snd3d3NzfVE51mevvvTA8ZM43TTA//zvQ2Yaa/iSRJ0rNevX64/dUP7t37u7J9e/X24fbxVzPd3t3cF810//bulzM93t69rZrp7yNJkvRnb9+8++NP7/bDx+NnevjzTJ/ujp/p5uHTn2d6cLl0FkmSpD94+/6cH+C7h1dHzvTm8ayZjqzSzVkzPb5RpedJkiT9zquHc/bsyafXB8109+7smd59OOZu6T5wpr+YJEnSr93cXvZTfLxbv2w3by6c6YDLt1cXzvT5zZGXlH8hSZKkX7k0SCero3TNTKsDcHGQDpjpLydJkvRfH69Z/s9ro/TqrMdav/CwcKZrgnQiSr8nSZL0s/trF+0f794umukFB/Xxw6If1N35z9r+M5ND9zuSJEk/eXv9op3crrgAuDn/AfIvZ1rx6dvNpxfN9Mmnb78mSZL0g/tr74++eZz+4dv9w4v/jedfKL18JufulyTJ0fjezcsukZ68n/v05uPLLpGe3E6e6WWXSE8+Hf9C519AkiTpO5OOw7uZu/Z6zkyPM2+UXs8o9z8zLXry9leTJEn65uU3bV9NvHl7wbP2n8yb6eU3bc7eb0mSY/HV/Yybkck/1KkzvZk007xK/jOTt7l/IkmS9H8f59yMfDVl/6cWKXOmT5r0I0mSpCeTizRl/6c82P7ehP2fXCRN+pkkSdIX04s0oUkLZnrx/k8vkib9RJIk6WTB9r+4Sfezr5FOPr1wpvlF0qQfSZIk/ePViiK9sElLtj9zJk36jiRJ0rJNe9lL06tmekmTZn7WNmum3UiSJI1x5Rf/z3D9u4Crtv8l7yet2xZn8BtJchwmvvn3H4/Xvsf9YeE/+LUzvV04k/e4v5IkSVq5adc+Jvm4cqbH62Za9MDt/zP5DUr/J0ntk7R00z5/frhmpiUftv3r/VU/qFUPt5688KPAfUhS+ySte5D05JpbkmlftvuNax67r16V5sfwG0nqfhZWPrT54orbpKW3kl9muvw2aemt5Bd+VckXktQ8SYtv204uvnVbfNt2cnvxD2rtbduJW7cvJKl5klbfIp1c+puKFn4C+M2lt5NHLMqq3xH+d5Gk3km6OeIHfOF//6+OmOndZbeT9+svJq/+JHAzktQ7SetvR04uezlx9fP2a/7V1724+T0vcQ9JOmmcpEm/RPY57y6Z6ZALtwsvSQ65cPuHl5Mk6aRxktY/R35yyWXSMRdJl/2zH3OR5DLpRJI6J+mgi6SLLpMOuki66DLpqIskl0lDkk76JumYJ0kn518mHfER4KX/7kddJLlMGpJ00jZJh12QXPCh23EXJOdfuh3ycdsTH7pJ0ufGSTruP//zX00+4p2kr869dFv+gvt3vJskSX2TdH/kD/ncO5LjLkjO//btUR8CnFz04eSWJKlvko78z//z43kzLf922w/Oe5i8/ttt32v/TTdJ6puk4x5un5x3l3Tcw+2T8+6SjryXvPKXuexEktom6cAHySdn3SUdei957kP3I+/b3LlJ0ue2STr0vu3MO7dj79vOu3M79r7NnZsktU3SsfdI5333/sjPAE/OuZs8ekWaHseyn/d/jeoBup6Bo3/M5zwkOfYe6by7yWMfuV3zq5z2Ikldk3Tge5JPznhwc/DjrbPuJg9+vPWP6pNRTJK6Jun4f/nnX0w+7Dt33zz/4ObwdF/8G+82I0ldk3TUF+7/9fyqHftx+8nzD5OO35Ce57HwB/6zUT1A0yNw5GvS5/6cj8/k8w+4jv4U4No/6bQNSWqapOMfkZzxnZLjZ3r+WfLRT9zbv5kkSU2TdPwjkufX//Cn2+c83z5+ps/VZ6OWJDVN0sEvSj79Uz+jIJPPznT0i5InvV+WlKSmSar4h39upopMPvfMvSKTvT9yk6SmSTr6PemT51at4jAmztT7dyZJUtMkHf/h1vPrf/w7AM+/BVCxIC0PZOlP/EejeoCeJyAxSRUzPfePL0lHk6SmJyBx/RNnqrjB7f03ASSpaZISf9CJSaqYqfcXbyVJkmJ+0JL0RJJqjeoBJCnlBy1JTySp1qgeQJJSftCS9ESSao3qASQp5QctSU8kqdaoHqBnkqz/eTP5xO1okiRJh/Fe0pyZ9iZJTU9AYpK8vf2k5YEs/Yn/aFQP0PME+I7beTNVfBXYd9xqjeoBeibJbwJ44jcBpJGkpkk6/jfv+31J5/L7kmqN6gF6JslvlfzCb5WMI0lNk+R3b3+R+Lu3z/iDdzuTpKZJ8hdKvvAXSuJIUtckHb9q/o7beXqex8If+M9G9QBNj8Dx//LPz3T8M/dXz87kr90eTJK6JunwVTvjEcnhz7fP+ZNpR8/0ufpkFJOkrkk6/Of+/GOb458ln/NtsqMfcPX+0q0knTRN0tEPk865Hzn6nfLnHyUdvyJNj2PZz/u/RvUAXc/Awe9KP/8G0D/eHvxPf3/GTEe/LNn7RUlJOmmapIMf3Jz3GzeOfTXhvDeAjr2bPOfx1tYkqW2SxqdDf8pvz5rp2Du3877feuyrCec8ctuaJPVN0qEfuZ9133b0ndvzrwCcHHvn1v2+TZI+903Sod8pOfc3JR5553bua9JHXk42/zbJkKSTrkk69C7p3P/8j7xLOu9e8tgPAs75DHBvktQ4SQe+LXn2yzYHPnQ/+0Hy/XGXbo/nfAa4N0lqnKQD3wI8/z//4y7dzv/ljcddurV/uC1JJ32TdNgD7gs+2T7s0u2CC5LjLt3Oe+C+NUnqnKTDXrm55AnJUZdul/yzH3Xp1vvPJT2RpNZJOugy6aLX/w66TLroqc1Rl0kukiTppHGSDrokuexjpGO+fHfZv/oxT5M8SRqSdNI5SYdcklz43fZDLkku/N7GIR+6+bjtRJJ6J+mQpySXvpB8xKE8952kr454N6n332/7SpKaJ+mA//4vvh25X//U/fLfb73+FW4vbn8hSc2TtP6//3eX344sv518vPw58vpvurX/dtsTSeqepOVPk6/5VdKrnyZf87WN1avS/Bh+I0ntz8LiW7erPkW6X3ubdN2fJVo7U/ffb/uNJLVP0trbpCsfkCy9TbriVvLk1cp2X3EruSlJkqSVh+DqTVv4EufjtQ9tVv4yp+Z/Kek7kiRJKx8nXb9p695OuP73f6z7KMDn/99IkiQtfHTzkt/+s+rF8pf8c6/qpO+2/UuSJGksexPoRV+QWNTJl23/mk56tP0dSZKkk48rHt2+8P/+JU1KnOmTL5J8R5Ik6YsFTXrx3ciC/U+cSZF+IEmS9GR6kyY8H5m+/4kzKdKPJEmS/u/j3F2b8sR28v7PmWnu86RbRfqRJEnSV1P3f9Jf2rif+X7CrF9HNPNzN5+1/UySJOmb+2m79njp7/74vXlfd5v394jmvZ/kfaT/kCRJ+s6kXfs080vtr+c85Ho3c6a3c2Z69M72f0mSJH3v44wXlN7MfTwy5SHX+7kzvZox063vtf2CJEnSD17+8GbiTdvXmV588/Y4/wbp5Zvj3P2SJDkaP3n7sgulyZcjT25eNtOSy5EXXrxNvbndiSRJ0s9eclHybtXTkbvrn968m37Z9n8frp9pwWXbLiRJkv7r1ZV3b48Lf5Svrvw48PFu3Ys/V8f7wctIvyVJkvQrN1e8D7hy+U+uiVLiTJ/feKz9B5IkSb92c+GyvVu8/CeXBmB1kL7M9HDZ7dvjgyD9kSRJ0u+8ujv/ofLtvPcQ/+j+w/kzfTpqptfnP+j+9Not2zMkSZL+4O2bcy4BPn048j/+j2fN9O7Qi5GPD+eU8t2DT9meJ0mS9Gc3D3+8Bnh8f2iPnnz880yfbytmuvvz47fbOz06iyRJ0rPu397d/uoq4NObD2Vrdn9z9/43M9V9S+OfmX7Vyk/v73xz5GySJElnurm5u7t7f3vycHf3+ibhKe3Hmw93d2/+nSnhQuS7md7c3X2ImOlvIkmSBEEkSZIgiCRJEgSRJEmCIJIkSRBEkiQJgkiSJEEQSZIkCCJJkgRBJEmSIIgkSRIEkSRJgiCSJEkQRJIkCYJIkiRBEEmSJAgiSZIEQSRJkiCIJEkSBJEkSYIgkiRJEESSJAmCSJIkQRBJkiQIIkmSBEEkSZIgiCRJEgSRJEmCIJIkSRBEkiQJgkiSJEEQSZIkCCJJkgRBJEmSIIgkSRIEkSRJgiCSJEkQRJIkCYJIkiRBEEmSJAgiSZIEQSRJkiCIJEkSBJEkSYIgkiRJEESSJAmCSJIkQRBJkiQIIkmSBEEkSZIgiCRJEgSRJEmCIJIkSRBEkiQJgkiSJEEQSZIkCCJJkgRBJEmSIIgkSRIEkSRJgiCSJEkQRJIkCYJIkiRBEEmSJAgiSZIEQSRJkiCIJEkSBJEkSYIgkiRJEESSJAmCSJIkQRBJkiQIIkmSBEEkSZIgiCRJEgSRJEmCIJIkSRBEkiQJgkiSJEEQSZIkCCJJkgRBJEmSIIgkSRIEkSRJgiCSJEkQRJIkCYJIkiRBEEmSJAgiSZIEQSRJkiCIJEkSBJEkSYIgkiRJEESSJAmCSJIkQRBJkiQIIkmSBEEkSZIgiCRJEgSRJEmCIJIkSRBEkiQJgkiSJEEQSZIkCCJJkgRBJEmSIIgkSRIEkSRJgiCSJEkQRJIkCYJIkiRBEEmSJAgiSZIEQSRJkiCIJEkSBJEkSYIgkiRJEESSJAmCSJIkQRBJkiQIIkmSBEEkSZIgiCRJEgSRJEmCIJIkSRBEkiQJgkiSJEEQSZIkCCJJkgRBJEmSIIgkSRIEkSRJgiCSJEkQRJIkCYJIkiRBEEmSJAgiSZIEQSRJkiCIJEkSBJEkSYIgkiRJEESSJAmCSJIkQRBJkiQIIkmSBEEkSZIgiCRJEgSRJEmCIJIkSRBEkiQJgkiSJEEQSZIkCCJJkgRBJEmSIIgkSRIEkSRJgiCSJEkQRJIkCYJIkiRBEEmSJAgiSZIEQSRJkiCIJEkSBJEkSYIgkiRJEESSJAmCSJIkQRBJkiQIIkmSBEEkSZIgiCRJEgSRJEmCIJIkSRBEkiQJgkiSJEEQSZIkCCJJkgRBJOnz59u7BqoPGjNUn6Ij3Fb3ICBJLVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6IeRvUAPVQvEzNUn6Ie/gcLRxxOKlYQdAAAAABJRU5ErkJggg=="/> +</svg> diff --git a/doc/source/_static/index-images/contributor.svg b/doc/source/_static/index-images/contributor.svg new file mode 100644 index 000000000000..3a689e0e4cb2 --- /dev/null +++ b/doc/source/_static/index-images/contributor.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#013243"><path d="M0 0h24v24H0z" fill="none"/><path d="M15 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm-9-2V7H4v3H1v2h3v3h2v-3h3v-2H6zm9 4c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/></svg> \ No newline at end of file diff --git a/doc/source/_static/index-images/getting_started.svg b/doc/source/_static/index-images/getting_started.svg new file mode 100644 index 000000000000..04db7e615671 --- /dev/null +++ b/doc/source/_static/index-images/getting_started.svg @@ -0,0 +1,31 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100" height="100" viewBox="0 0 1194 1063"> + <metadata><?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> +<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c138 79.159824, 2016/09/14-01:09:01 "> + <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> + <rdf:Description rdf:about=""/> + </rdf:RDF> +</x:xmpmeta> + + + + + + + + + + + + + + + + + + + + + +<?xpacket end="w"?></metadata> +<image width="1194" height="1063" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABKoAAAQnCAMAAADsEHQnAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAABelBMVEUAAAAAAgIABggADxQBHCUBJTIBLDoBMUIBMkMAAAEAAgMAAwQACg0AFx8BJjMBLz8AAQEABQYADhMBGiMBJDABKzoBMUEABwkAEhgBHigBJzQBLj0ADBABKTcBMEAAAQIABQcBGyUBLDsACAsAFR0BLj4ACw4AGCABIy8BKjgBMEEAEBUBICsBLTwBMkIAExkBHyoBJzUABAYADRIBJTEABgkAFBsBISwBHCYBKzkADxUAERcACQwBKDYADhIAExoACw8ABAUBGyQACAoAFh0BHScAEhkBIS0AGSEAFBoBLT0BIi0BL0ABGSIBHykAAwMAERYBIy4AFRwADxMBGiIBIi4ABwoACg4BJDEBIzAADBEAFh4ADREAEBYAFx4BKjkBICoBHikBKDUAGCEBKTYACQ0BHSYBJjIAAwUAGiIAEBQAGiMAHykBMkMBMkMBMkMBMkMBMkMBMkMBMkMBMkMBMkMBMkMBMkMBMkMBMkMBMkMBMkMBMkMAAAASK7oTAAAAfHRSTlMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIEBQgJ+/z++PEHDfr2AwdXqxVQAAAAFiS0dEAIgFHUgAAAAJcEhZcwAALiMAAC4jAXilP3YAAAAHdElNRQflCgkWEyD/z8kiAAA7K0lEQVR42u3d6X8cxdXo8ZFsRyONl/Fu8CIhBxsbL2DAhtgYCzAhEIfEBHCCQ3J5kueuGo1Go9Ey/uOvFkuakWbp6a6qU+fU7/vufu7zIrR6fu6uPl1dKmGgsfFDh4/8ZqIMxGy+tlCvLzYaS6/2aTYajeV6vVaT/l9YlHQJ4jY5VTl67PgJ6T8S0FertrLcWH2VRWOtvq63WNIxiFr15KnTZ86ek/4bAT3NL9QbzUyR6rS6Vq+1pP+n5yBdg4iNnb/wxpsXpf9AQC+1+trolero1eL6vPR/woikexCtauXS6ctXpP88wEHteqNApbTmSroIkZqeOfXWkVkW0xGb1vpikaup/ZaW1SxeSTchTlNXf3vkbek/DbDP/IqTy6luzcUF6f+uTKSjEKFq5dr1d27clP7LAF3mV5aKd0lxraS7EJ/qzLu3bt+R/rsAnVrra746tVOrtvR/4xDSYYjN2Pm7773PIBWi0na6PtXP6krUMwzSaYjLZOXa6cvc+iEmrXVvN34HLEa8yC4dh5hU731w+kMe+yEm88shLqg6Lq3WY720ks5DRKY/unuGx36ISW0xaKe2NOtxxkq6D9GYunr/ASOfiEnNw2hCJosxjoZKFyIS1cqljz/h1g8REQtVpLGSbkQcqid/9+C29J8C2CMaqihjJR2JGEyPP2RAATERD9VWrOJas5LORASmHl3/lAEFxGM+hlC9im2BXboT4iYrlz4+yyoVotFalk5UR6zWpY/GHulSCKvee3z4CKtUiMdK2DmqYZaiGQqVboWs6Y8++5BFKsSjFm4yPatYlqykYyFq7OoxVqkQj5ju/fY0V6SPyxbpWkiqPDnMKhXisRDXvd+eRgyDC9K5EFOd++BzVqkQj5bnbV4KqUsfnXRTVR2/wAt/iEi0l1TblsS3s5JOhpDpL758nxf+EI2oL6m2Sa9YSTdDxtTR0zdYpUI0anFfUm0TXrGSjoaEycoHb8yyJTGiEeWDv4OaojuwS2dDQPX80+MsUyEa8/HNUvWzLHiYpLsRPlTjX/2eVSrEI/L19G5LcjeB0uUIbezR15dZpUI8lNz87ZC7CZROR2CVS09ZpUI8WpFsojACqREr6XYEVb136PcXpU9OYFd7VTo8OazJvBQoXY+gpRr/wxFeTkY8VC1T7ZFZsJLOR0BjV69fPid9cgK7VqSbk1dTYnRduh/hVC59/BuWqRAPgQ9nOSOw5Z50QEKpzh168I30uQnsUvAqzSDhX7ORTkioUo1/xjIVItLSM/fZ22LoIybdkDDGHrFMhZi0tZcqfKukIxJE5dJTlqkQkbbOR3/dGmGHFqQrEkD13rvfMk2FiJgo1atXS0FbJd2RAKX66P7xP0qfm8AeI6UK3CrpkPgv1dFnbE2FmJgpVdhWSZfEt8nxP81Kn5pAB0OlCtoq6ZR4Vp25z9ZUiImpUoVslXRL/Bq7euyy9KkJdDBWqoCtko6JV99denpW+tQEOpgrVbhWSdfEo+rJP/+Fuz/ExGCpXr1aC3PspHvisVR85R2RaWncnmq4MHPr0kHx1ampe0evf8OEOmKi/r0/0VZJN8W9ycmxyvi1Pz/7kNeTERWzpQqzJ4x0WByrTlXGH516/vHxi1xRITKa96eKoFXSbXFocroyd/X7C3/94SybKCA+lksVYl9Q6b64Uh2b+/GnC8/5GCkitS5dE8+t8r7funRi3HRq6uSjS79979YNnvghUgvSLfHN+3iVdGVcdGrm6OPr377D9/0QL5MDVd18PwaUDo2DTp36+p0XbJuOmBkdqOrm+Vum0q0p3Klnf/uQ5SlETt83lPOoeT2G0rnJbXLs3vjjr8/QKcRvWToiYfhdWpcuTs5OTVdm/v7zszN0CgoYf/i3Z8nnUZSOTi7T967dv/7DbekzEMgigSX1HcseD6N0dUZXrYy//O0tvkADJQy/T3PQgr/jKB2eEU2Ozfz01VO+6Qc9TE+p79f0N10l3Z6RVCtX//HXI3x+BoqYn/3s1vB2IKXrM0Knpsb//tkDBqigynw6C1XbVnwdSekAZTY2/v3Hn17mM1nQJY2Jqg7eXlyWLlA21cr5U9dfsAEVtKlLlyM8XxML0hHKYvreT5/9ie85QJ+2dDckeHrBRjpDQ1WnKkd/PnJF+pwDckhpTmGPn6F16RINMzZ+6i7fR4ZOK9LRkOHnKaB0igaqTn106jpXVFAquad/O7w8BZSu0aBQTc+c4pOj0Cu5p387vAyCSveor8mpH1/+8jfGPaFWYsOfnXxssyddpD4259LfusF7ftArie30+vGwdZV0k3obG//zg4vSpxpQRIIjVXs8DFdJR6mHyanxU18f541kqDYvXQtZ7j8MKN2lg6r3Xn7MYjq0W5OOhSz3K+vSYdpv+uST59/yBg20q0m3QprzmXXpNHWbnHr0y/u8kgz9kh1U2OH8sko6Tp2qlasPGaSCBclsp96f64EF6Tx1mB5/+Htu/WBCyoMKOxy/Cijdp11jMy+v89gPNnBR9cr5ZZV0oV6brDw5/U/p8wtwhIuqTW4vq6QbtaVaeXT3X4x8wgouqra4vaySrtSmsY/unrkpfXYBznBRtc3pZZV0pjZMPTr2qfS5BbiT/EzVDqeXVdKdmpz68R9PfyN9bgEOJT9TtcPpbJVwqaonD91iQAGmJLmhem8uR9ZlSzX20d0XDCjAlqQ+pzxY0+FhFQ3V+ONnrFLBmJZ0H2LicIMFuVBNVp68xfcdYE7S+1Tt53DfKrFSVe+xSgWLmFTo5G47UKlSTY9fOMMqFexhUqGLu3kFoVDNfPA1q1SwiEX1bs7mFSRCNVn56TR7vcAkFtX3cfZNQIlSzX3wP3jhDzbx+t8+q66ObPhSVWf+8Rfe+INRTKrv13Z0ZIOXavr8/eNsSQyjEv9OTS/Ljg5t6FJVvj99Q/p0AnxZkQ5DfFxNrIcN1dTVu99In0yAP0vSYYjQgptDGzJU1bnffXtF+lwC/OH5Xw+ORqsClmp6/O4RVqlgGc//enB0BxiuVGNXj12WPpMArxL/pHIfbu4AQ4VqsvLk8G3pEwnwqyldhSi5uQMMVCpeTkYCeP+vp1UnBzdQqca/+pBlKli3LB2FSDmZAg1SqrGrv7BMBfsYVejNyb7FAULFHnpIA6MKfTjZYM9/qar3vr/1R+mTCPBvQToJ0XKxE4z/Uo3fZZkKSWCpqh8X4wq+S8U0FZLBrgr9uBhX8Buqycqlj5mmQiKkgxCvVQdH12+qTr77e176QyKYqupvvvjh9Vqqyv3j0qcPEAobwPTnYLHKZ6mmLrGDOtLBByD6c7C/nr9QVece35I+eYBwGADtr1H88Por1fhXL6TPHSAg6RxErfjh9VWqqUfX2ZgYKWlL1yBqxb+y7KlUlZdv8P0sJIVZ9UGKfw7QT6nGXv6Nr7wjLXXpGkSt+Lq6p7u/r6XPGyAwdgAdpPi6uodQTc5x94f08FrNQIWPr4dUVS684P1kJEe6BZErPK/u4e7vCY/+kB42qxqs8CNA16Gqzp36l/RJA4THG4CDFd4J1HWq5u4fvyN90gDhkarBCj8CdBuqycoHbE2MJDGrMFjhR4BuSzXz8C/SZwwgglQNVnh/dZelqp68y46fSBRjVUMUPcAuU3XvOVspIFWMVQ1R9FMQDu/+pg5xTYVkkaohik4ruLv7mzn0g/TZAoiRLkH0oknV3F2e/SFh0iWIXtG9FVyVauwl26gjZdIliF7RGVBX11Qf3GLyEwmbly5B9OJIVeUh7/0haQyrD7NW8Ai7ufu7dEb6TAFEkaphio6rOwjV5NQXT9n1BWkjVcNEkKrKoWfspIfEkaph5FM1dulv0qcJII1UDVP0JcDCpape5e4P4G3loQoe4cILVTPHuPsDSNVQBY9w4YUqxhQAUpVBwSNcsFRTL19InyNABEjVUAWPcLG7v7lTbPsClElVBgWPcKFU3fvDWekzBIgCqRqq4BEuUqrvDvGKMrCFVA1V8AgXKNXYtQfS5wcQCVI1VMEjnL9U1S8OX5E+P4BIkKqhCh7h3KWanPn5tvTpAcSCVA1V8AjnTtXcBXZSB3aQqqEKHuG8pfru8YfSJwcQjxXpEERP6B3AsZ9u8eYfsIvXlYeR2Vmh+uPnJ6TPDSAipGoYkVRVZ77k8zRAB1I1jEiqWFIHupGqYST2VmdJHdiHVA0j8MUaltSB/VrSJYhe+FRVr37+R+nzAoiNdAmiF/zrypPjvzClDuwnXYLo1Qoe4JFTVfmKJXXggKZ0CmIXOlXTh45InxNAhBrSKYhdq+ABHjVVMw9YUgcOIlVDFD3AI5bqu6/Y+AXoYVE6BZFbLXqAR7z9e/KN9BkBRImtFQYrOqw+WqqqH30tfUIAcWJrhcGWix7gUUo1efIPfPQP6Ilx9cGKToCOlKrKu3z2AeitLd2CyBWdVRglVVMvf+DpH9CHdAsi1y56fEdYqDr6xk3pswGIFjOgAxU+vtlTNffXi9InAxAvBqsGKbpd8Qipqh7lS8pAfwxWDbJY+PhmLtXMz9KnAhAzBqsGKfwAMHOq5h7+Kn0qADFjWmGQwg8As6aqcuqM9JkARG1eugZRK/qyctZUVa+9d076TADixiPA/laLH96Mt398SwsYgkeA/RVfVc+Wqumf+JYWMATr6v0V3a24nC1V1fH/kj4LgOgtSPcgYoVn1bOl6uT9T6TPAiB6rKv35+DwZrimOv85Y+rAcKyr91N4s6pyhlRNPTrNkjqQwZp0EaJVfAB0WKomK5eezjKmAGTB7nr9OFiqGpyq6syf32EvdSAbtqzqo+ni6A4I1diPX75/hR2qgKxYrOptzcXB7V+q7376fJZQAdmxWNWbg6mqvqmqzp3618Vzd6T/9IAmLFb1Nu/i4PZbpLp7hNV0YDQsVvVUfFu9Tb1Xqa4eu8y9HzCqVekqRKnwd7W29FqkuvbW7E1KBYxsWboKUXIxqtAjVdW57x+cYJFKXrv22nq9w3KjD7v/njcbQy3W91up7XGyUpINrwH24GRU4WCqqjNfsUoV3PzGD2rzB7ZmOzrCtpu3tnmgF3wFjHGFgxxsALOpO1TTP/7yb0apQpnfuGBaJk1ylhrLK8U30u3CtyAOWnBzaLvf97t2+DahCmB+ob62JH0GYduay1pxB3iAo/u/zlRNVh7f+iOrVL61V9a4S4hLw+HNIH/b/Rzd/5U7V6kufMgqlWftZW73YuRknHoLd4D7uXn+t5eq6fNf/soqlWfr3PTFqlH8iyrbuAPcZ9XVr2dnleo/p3nhz7N1Lqgi1nS1YsVfuZuLraq2bJeqculPzFL51eZ7JpFzdBPIFGg3Z+uAW+vp995956bUTzgRvMgav0UnN4HssN7FxVbF2zbX08fvv8/Nn1ctdgfRYMlJq7h87rTu7EdUKo19cIM3/vxqs5yuQ9PF06p16f+KmLgaqtpQevL0G1ap/Koxa6NF08VFAAvre9xsqrCl9C2bp3vGv7KaOGgVX1ne43C4tsS9n2c8EdKl+HB1S/o/IR7uFtU3UiX3G04D08vaFG8Vf/Mdjt5U3kKqvGqxoK5P4QeBzCu8turyt0SqfKJUKhVuFZdV29y9WlkmVV5RKqVWCw4tcFm1penqxcotpMoj/nHVquiAFWOgm5y9/reFVPlDqfQq+PYyX9na5PSiilT5wzyVasUGrPhnyt2eeq+RKl9q0mcKiinUKlarnI5/biJVnrR4m0a7Qq3issrxRRWp8oWFVf2K/Njmk/+nyvXXy0iVH+xPZUGRVqX+JqDbx39lUuUJSxU2FGhVK+0NFtzOVG0iVV5w+2dEgVal/UEIp4PqW0iVD8wpmFHgJZuU/71acv+jIlUe8PTPkPytSnkVwNXH/zqQKg9SX1G1JX+r0j0PHG7+uYtUuZfyv6YW5W9Vqivrq87X1Mukygem/4xZyjsilOobC64+/9qFVDnHRZU5uTdaSHO3ah+3f6TKgzRPT9vytirJ4So3n1M8gFS5xuM/i/K2KsVbQA9P/zaRKtd4pcakvK1K7ymg++HPbaTKtRQv+VOQt1WpbVq95uuHRaocY/9Hq3JuDNpOa0HAy5zCFlLlGJMKduXbwCqtt6w8LVSVSZVzaf0bmph8rUrpXy9fC1VlUuVa2q/Tm5erVQl9Y831zp+dSJVbKf0LmqJcrUpmucrTRNU2UuUWz/+My9WqRK613W+n14lUOcXzP/NytSqN6Sp/S+qbSJVTzH/al6tVa9L/q2M9MNmRKqdSOCOTl+cpVwJL686/+7APqXIqlfXTtOV5zmX+Y1s+H/5tIVUusf9LGvL8LI0/BvSwmfo+pMqlRJ70IE+rTE+tex1T2EaqXErjQQ9o1T5+xxS2kSqXWFVPRp5WmZ0Pzr1L6ihIlUv2H/NgB63aFaRUpMop6XMGAdGqHQtBflykyqGW9DmDkHK0yuR4lefRzx2kyqEUN9JOGa3aFKhUpMolUpUYWhWuVKTKJcaqUkOrgpWKVLnEWFVycow+mmpVuFKRKpdIVXrSblXAUpEql0hVglJuVchSkSqXSFWK0m1V0FKRKpdIVZJSbVXYUpEql0hVmvK0Sv/rooFLRapcIlWJyrMFivJ3bMK899eJVDlEqlKVXKvCl4pUuUSqkpWnVYq/GbIavlSkyiVerElXnlap3WsvwJ6fB5Eqh0hVwvL8fGs691tfkygVqXKJVKUsT6vaGr/G7f3bNL2RKpekTyJIytMqhQNWeT6D6AKpckn6LIKoXEs4yh4ENsNs+dkDqXKpIX0iQVSuVqlaXF8SePT3GqlySdm/kHAtV6sULVjJLKhvI1UuMViVulytamm5Gq9L/rhIlUs8AkxevpEjFf/GyS1TbSFVLvHJGuR75UTBTeDSvOyPi1Q5Ff8JB9/ytaoV+zqn6M3fJlLlVOznGwLI+SrvQsyj66s16Z8WqXJL1YNneJKzVRHvYbUo+ORvB6lyal76nEIM8m6REumFlfB6+mukyi1970nAg7ytai1L/y/vYTmCS6oyqXItxlMN4eXeeq4d24zVkvwq1TZS5VZb+sxCHPJvk7ke02PkZugd1PsjVY7FdJ5BUP5WteqxLFk163Hc+20hVY6pmDtGAAW2H49jyCqqUJEq53gGiNeKfCphXjxWkYWKVLkX27IoxBT6rItsrFZjCxWpco8pUOwo9gmqebE1q6V4FtP3kCrnWFjHjoKfy2utSJxMi7GMJ3QjVc5xWYVdhT/tuRD4bZvVuvAGCn2RKudasTxqRgSKf4Z4vh7u0moxildoeiNV7jGvgD0uPpleWwzxz19jPbql9E6kyr0Wq1XY46JV5fK65xvBpZVYb/x2kCoPWK1CBzetKrfWvV1bra3H3qkyqfKD/RXQwVGrNiwsO79iX11ciPq+bxep8oHPQaCTu1aVy/Mra84urppr0d/27SFVXrAXDDq5bNWGtoNcrS6uyH1+NA9S5QUr6+jiuFUb2uvLed/hWl2r13Tc9HUiVX5wC4gu7lu1qb1QXxtlYXSpUV+PcxZ9OFLlCcNV6OKnVVvma+v15Uaj/z3haqOxvNEoPetSvZAqX9hhAV08tmrXfG3DQv21lc3/l8Jbvd5IlS+8X4NuIVplGKnypk2r0IVWFUGq/GFoHd1oVQGkyiNahW60Kj9S5ROtQjdalRup8opWoRutyotU+UWr0I1W5USqPKNV6Ear8iFVvtWYWUAXWpULqfKO+Sp0o1V5kCr/5tlpD11oVQ6kKoAW7wOiC60aHakKgq320IVWjYxUhbHAghU60apRkapAWoG/kovI0aoRkapgamxijA60ajSkKqB1YoU9tGokpCqoBW4DsYtWjYJUBdby/UVv6EGrRkCqBCzk/iwSbKFV2ZEqIe31eoOlq+TRqsxIlajXn0WiWcmiVVmRqkhsfhZppV7fDFeDdiWkqfUToqGRqqhtfdfttfV6P9t504kov1qXPst0IFWQxA45r2hVNqQKgijVFlqVAamCHEr1Gq0ajlRBDKXaRauGIlWQQqk60KphSBWEUKoutGoIUgUZlGofWjUYqYIISnUArRqIVEECpeqBVg1CqiCAUvVEqwYgVQiPUvVBq/ojVQiOUvVFq/oiVQiNUg1Aq/ohVQiMUg1Eq/ogVQiLUg1Bq3ojVQiKUg1Fq3oiVQiJUmVAq3ohVQiIUmVCq3ogVQiHUmVEqw4iVQiGUmVGqw4gVQiFUo2AVu1HqhAIpRoJrdqHVCEMSjUiWtWNVCEISjUyWtWFVCEESpUDrepEqhAApcqFVnUgVfCPUuVEq/aQKnhHqXKjVbtIFXyjVAXQqh2kCp5RqkJo1WukCn5RqoJo1TZSBa8oVWG0agupgk+UygFatYlUwSNK5QStKpMq+ESpHKFVpAoeUSpnaBWpgjeUyiFaRargCaVyKvlWkSr4QakcS71VpApeUCrnEm8VqYIPlMqDtFtFquABpfIi6VaRKrhHqTxJuVWkCs5RKm8SbhWpgmuUyqN0W0Wq4Bil8irZVpEquEWpPEu1VaQKTlEq7xJtFamCS5QqgDRbRargEKUKIslWkSq4Q6kCSbFVpArOUKpgEmwVqYIrlCqg9FpFquAIpQoquVaRKrhBqQJLrVWkCk5QquASaxWpgguUSkBarSJVcIBSiUiqVaQKxVEqISm1ilShMEolJqFWkSoURakEpdMqUoWCKJWoZFpFqlAMpRKWSqtIFQqhVOISaRWpQhGUKgJptIpUoQBKFYUkWkWqkB+likQKrSJVyI1SRSOBVpEq5EWpImK/VaQKOVGqqJhvFalCPpQqMtZbRaqQC6WKjvFWkSrkQakiZLtVpAo5UKoomW4VqcLoKFWkLLeKVGFklCpaK9Lnhj+kCqOiVBFblD47vCFVGBGliprZVpEqjIZSRc5qq0gVRkKpome0VaQKo6BUCthsFanCCCiVCiZbRaqQHaVSwmKrSBUyo1RqLDXk1Dut1F6bL3r2kSpkRalQVHOzZYsbCVuo1VqjnX6kChlRKji22lheqWU9/0gVsqFU8GNtIdMJSKqQCaWCN6vrGW4GSRWyoFTwaXX4nhCkChlQKni2OmzRilRhOEoF/9YG3wWSKgxFqRBCc+BuW6QKw1AqBDLowopUYQhKhWCa/VesSBUGo1QIqd7vRCRVGIhSIaxGn5tAUoVBKBVCW2r3PBVJFQagVAiv2bNVpAr9USqI6DW7TqrQF6WCkB6tIlXoh1JBzMFWkSr0Qakg6ECrSBV6ay1Jn6xI2v5WkSr0RKkga/9zQFKFXigVpDW7vxxBqtADpYK8pa65dVKFgygVYtD1OUNShQMoFeLQubROqrAfpUIkOperSBX2oVSIRmPvvCRV6EapEJG9PYxJFbpQKsSkufsUkFShE6VCXHafApIqdKBUiM3ObuukCnsoFaKzs7JOqrCLUiFCry+rSBV2UCrE6PVlFanCa5QKcdq+rCJV2EapEKm1rROUVGELpUK0tl6vIVXYRKkQr+XNU5RUoUypELXVzXOUVIFSIXILZVKFMqVC7DbfriFVoFSIXLNMqkCpEL8FUgVKhfgtk6rkUSoosEqqUkepoMI8qUobpYIO66QqaZQKSiySqpRRKmixRKoSRqmgB6lKF6WCIm1SlSpKBU0WSFWiKBVUqZOqNFEq6LJIqpJEqYJocpidaZCqFFGqIJptDrQzpCpF/ICCaLY51A6RqvTw8wlis1QcbHdIVXL48QSxXSoOtzOkKjX8dILYKRUH3BVSlRh+OEHslYpD7gipSgs/myA6S8VBd4NUJYUfTRDdpeKwO0GqUsJPJoj9peLAu0CqEsIPJoiDpdqwKP2/Sj1SlQ5KFUTPUtGqwkhVMihVEH1KRauKIlWpoFRB9C0VrSqmSaoSQamCGFAqWlUIrysnglIFMbBUtKoIUpUGShXEkFLRqgLYBTQJlCqIoaWiVfmRqhRQqiAylIpW5VYjVfZRqiAylYpW5TVPqsyjVEFkLBWtyqfJJ0vNo1RBZC4VrcqlQaqso1RBjFAqWpVHnVQZR6mCGKlUtCqHGqmyjVIFMWKpaNXoyqTKNEoVxMilolWjapAq0yhVEDlKRatGtEKqLKNUQeQqFa0aTZtUGUapgshZKlo1itUyqbKLUgWRu1S0agTLZVJlFqUKokCpaFV282VSZRWlCqJQqWhVVkubB4tUmUSpgihYqnJ5Wfq/QIf1zWNFqiyiVEEULlW5vC7936BBs7V5qEiVQZQqCAelolVZLG8dKVJlD6UKwkmpaFUG81sHilSZQ6mCcFQqWjXU4vZxIlXWUKognJWKVg2zfVFFqqyhVEE4LBWtGuz1RRWpMoZSBeG0VLRqkO3Hf2VSZQylCsJxqWjVAPWdY0SqLKFUQTgvFa3qa3X3EJEqQyhVEB5KRav6qe0eIVJlB6UKwkupaFVvy3sHiFSZQamC8FSqcrnWlP5Pi89qa+/4kCorKFUQ3kpVLrdp1X6dB5tUGUGpgvBYKlp1wErn0SFVNlCqILyWilbts9h1cEiVCZQqCM+lolVdllpdx4ZUWUCpgvBeKv6SHZrdpSJVFnB+BxGgVPwtdx042qRKP87uIIKUir/mawePNqlSj3M7iEClKvNxiE09jjap0o5SBRGuVLSq99EmVcpRqiBClqpcXpH+zxW21OtokyrdKFUQYUuV+guBjVavY0KqVKNUQYQuVdoDVsu9Dwmp0oxSBRG+VOXyfKp/2uZCnyNCqhSjVEFIlGrjj5vm4npjvt8BIVV6UaogZEq1YT29m8DmSv/DQarUolRBLM0X/1Pl1E7tL7zYGnA0SJVWlCqIpVbxP1V+den//JAatYHHglQpRamCkC3VxoVVQ/oIhLK6MORQkCqdKFUQ0qXasL4qfRBCWF0feiBIlUqUKogISrVh3fwfuzHsimoTqdKIUgURR6k2tBctPwxcrGU6CKRKIUoVRDSl2rRgtFar9axPWEmVPpQqiKhKtam9smZs3aqZ8YJqC6lSh1IFEV2ptv/4tZXlho2ngkvLWVao9pAqbShVEHGWale7Vlup1+uNTdKHKuMBbWxb3PifXV+p1UY+vqRKGUoVROSl6qW2Z73e33Ijp+X6EOu1/ZweRFKlC6UKQmGpzCNVqlCqIChVhEiVJpQqCEoVI1KlCKUKglJFiVTpQamCoFRxIlVqUKogKFWkSJUWlCoIShUrUqUEpQqCUkWLVOlAqYKgVPEiVSpQqiAoVcRIlQaUKghKFTNSpQClCoJSRY1UxY9SBUGp4kaqokepgqBUkSNVsaNUQVCq2JGqyFGqIChV9EhV3ChVEJQqfqQqapQqCEqlAKmKGaUKglJpQKoiRqmCoFQqkKp4UaogKJUOpCpalCoISqUEqYoVpQqCUmlBqiJFqYKgVGqQqjhRqiAolR6kKkqUKghKpQipihGlCoJSaUKqIkSpgqBUqpCq+FCqICiVLqQqOpQqCEqlDKmKDaUKglJpQ6oiQ6mCoFTqkKq4UKogKJU+pCoqlCoISqUQqYoJpQqCUmlEqiJCqYKgVCqRqnhQqiAolU6kKhqUKghKpRSpigWlCoJSaUWqIkGpgqBUapGqOFCqICiVXqQqCpQqCEqlGKmKAaUKglJpRqoiQKmCoFSqkSp5lCoISqUbqRJHqYKgVMqRKmmUKghKpR2pEkapgqBU6pEqWZQqjHnpPzSKIlWiKFUgXFWpR6okUapgaJV2pEoQpQqIVilHquRQqqBolW6kSgylCoxWqUaqpFCq4GiVZqRKCKUSQKsUI1UyKJUIWqUXqRJBqYTQKrVIlQRKJYZWaUWqBFAqQbRKKVIVHqUSRat0IlXBUSphtEolUhUapRJHqzQiVWG1a5RKHq1SKIZUtWu5rdRzW2vk1pT+qaEgWqVPzlTV1vj5QjFapU6uVK2vSp9pQDG0SpscqSJUMIBWKTNyqggVbKBVuoyYKkIFM2iVKiOlilDBElqlyQipIlQwhlYpkjlVhAr20Co9MqaKUMEkWqVGplTVGtJnFODHovQvEBllSFVrUfp0AryhVUoMT9UCr8zAMlqlw7BUtdakzyTAL1qlwpBUcUkF+2iVBoNTtSx9EgEB0CoFBqWK/SqRCFoVvwGpanPzh1TQquj1T9U6pUI6aFXs+qZqXfrcAUKiVZHrl6oV6TMHCItWxa1PqhhQR3JoVdR6p4pSIUG0KmY9U8U6FZJEqyLWK1WUComiVfHqkaoF6fMFkEKronUwVUx+YgjLZwititWBVLXY7xODNduWlwhoVaQOpIoNPzFYs217OZNWxWl/qurSJwoit1kqWoXg9qWqJn2aIHLbpaJVCK07VSxUYbCdUtEqBNadKrbSw0B7paJVCKsrVdz+YaDOUtEqBNWVKm7/MEh3qWgVQupMFU//MMj+UtEqBNSRqnnLQ8go7GCpaBXC6UgVO79ggF6lolUIZi9V89JnBmK21O59AtEqhLGXKt6oQX9LrX5nEK1CELupYlAB/fUvle1W1aV/n9i1m6o16bMC8RpUKtutWpf+gWLHTqpYqUJfg0tFqxDCTqp4/Id+hpWKViGA16lqSZ8QiNbwUtEq+Pc6VXyhFH1kKZXtF91pVRRep4q3/9BbtlLZXkGgVTHYThWTCugta6loFTzbTpXl0wwFZC+V7ZOoJv07xXaqWryojF5GKZXpVjXboxwI+LCVKsvPb5DfaKWiVfBpK1VMqqOHUUtFq+DRVqqkTwPEaPRS0Sr4s5mqBemzABHKUypaBW82U2X49EJe+Upl+mRazXlI4MRmqpj/xH55S2W6VfkPCoorsakCDiryo6RV8KHE+384oNhPklbBgxKjCtiv6A/ScKsa0j/YdJVYqsI+xS8dDLeK7dallFiqQjcXNzm0Cq6VmKpCFzfLMbQKjpX4/Ds6uVo4NtyqFekfbZpKfP8PHdw94jLcKravklBiVR17XD6Mp1VwqcS7yti15nRsyHCreB0wvBJ7FWOH6wVju63i1eXwSjwAxGvuH23RKjhT4gEgtvl4CG+3VbxiE1rJ8gfcMAI/40K0Co6UmFXAJl+DjXZbxeuAYZEqbPI3gm23VYytB1VirAp+f3V2W7Us/etNSkn6z40I+L0+sNsqRkEDIlXwfidjt1UL0r/fhJAq+F9zMdsqxqvCIVXJC7E6bLdVjCyEQqpSF+Y5ltlWMV4VCqlKXKgn7mZbtSb9E04FqUpbuNkgs61iZCEMUpW0kFOMZlvFyEIQpCplYeetzbaKx4AhkKqEhX4zxGqreAwYAqlKV/h32Ky2ijeXAyBVyZJ429Zqq+rSv+MElJrSf2XIkNkXwGqratI/ZPvYBCZRUjuYGG3VKstVvpGqNMnttWS0VUxX+VYyeuZgIMld4YyecfPSP2Xr+AxEimT3r7TZKvYE9ay0Lv0nRnDSvyqbreKyyi8+WZoe+RdBTLZK+h8A60ot6b8wApMvldFWcVnlVanMYFVaYiiVzVYxB+pVqcy0QlLiKJXJVq1KH1PbSmUeAaYkllKZbBU7LPhUKi9I/4ERTjylstgqxkB9KpXnpf/ACCamUhls1ar0ETWtVC7zfeVUxFUqg63iGaBHJYMnDHqLrVT2Tj2+YOrRRqqYV09DfKUy1yrGFTzaSBWLVUmIsVTWWsVuoB5tpKq8JP0Xhn9xlspYq1alj6Zlm6lalv4Lw7dmrKUy1irpg2nZZqra0n9geNaMeTjRUqt4BOjPZqoYVzAu6lKZahVbrPuzlSruAE2LvFSWWkWq/NlKFc8ALYu+VIZaxbSCP1up4hmgYQpKZadVpMqf7VQxBWqWilKZaRWp8mc7VS321zNKSamstIpU+bOdKhbWjVrVUiojrSJV/rxOFQvrJi1p+uavhVbxZo0/r1Nl4jzBPqpKZeIcJFX+7KSKyyp7lJXKQqtIlT87qeJrEOaoK5WBVpEqf3ZTxWWVMQpLpb9VpMqf3VSpP0vQZVFjqdSfhaTKn71UcVllidqvkutuFanyZy9VfBDQELWlUt4qUuVPR6pa7AVjheJS6W4VqfKnI1V8vNQK5Z/OVNwqUuVPZ6rKa9J/abgQ7+bE2ZAq9NCVKt5atoBSySFV/nSliltAAyiVIFLlT3equAXUrqn9A7+qS0WqPNqXKp4C6qZme6p+dJeKVHm0L1V8aEs1SiWMVPmzP1XsXazYEqUSRqr8OZAq/adLslS+oGzr1CNV/hxMFdvBKEWp5JEqf3qkqsWntjRS/TLNJgtLD6TKnx6p4jGgRpQqBqTKn16pKreZWtdmRfpEKspEqUiVRz1TRau00T6ibqRUpMqj3qmiVao0a9KnUVFGSqV9T4uo9UlVuc16lRrqBz/NlIpPlnrUL1U8B1RD/ZCCnVLpXzKMWN9UlVvMV6mwRqniof5OPGKlAf9/Bkby7NO/OmKoVK/mpQ+mYYNSZeokMkr9oz9TJ1lT+mBaNjBVLK5HTv+CuqlSMavg0+BUlVvstRcx/QvqtkrFA0CfSsP+D9aZsIqV/mUqY6V6pf8iN2JDU1We50lglNRvTVw2VyqWqnwanqpyeYELq/g0DDxsMlYq/a+MRy1LqsotvhEfmaaFWUNrpeL+z6tMqdq4C2TGKiZrBi6p7JVqSfqI2pYxVeVyjSWrWDRMzESbK5WBGbeoZU4VsYqEjVAZLBWL6n6NkKqNWDFlJc1IqAyWiqEqz0ZKFWtWwhatLNwaLFVT/zxu3EZMFbEStGhhMX2LwVKxUuXbyKkiVkLshMpkqValD6p5OVK1Eas6QrMTKpOlYqcq73KlCsjPZKnWpI+qfaQKYZksFWvq/pEqBGWyVK8MvDoePVKFkGyWitu/AEgVArJZKm7/QiBVCMdmqbj9C4JUIRijpTKwG6sGpAqhGC2VgR3uVSBVCMRoqQx8NUgHUoUwjJaKhapQSBWCsFoqFqpCIVUIwWqp+EhpMKQKAVgt1SpL6sGQKvhntVQsqQdEquCd1VKxnV5IpAq+mS0Vu6mHRKrgmdlS8THloEgV/DJbKqbUwyJV8MpsqXj4Fxipgk9mS8XDv9BIFTwyWyo++xAcqYI/dkvFmEJwpAre2C0VYwrhkSr4YrdUjCkIIFXwxG6p+OqDBFIFP+yWioEqEaQKXlAquEWq4IPdUjXnpY9tokgVPDBcKkY/hZAquEep4BypgnN2S8XopxxSBdcoFTwgVXCMUsEHUgW3DJeKD2lJIlVwynCpeJ1GFKmCS5QKnpAqOESp4AupgjuGS8XrNNJIFZyhVPCHVMEVSgWPSBUcoVTwiVTBDcOlalKqCJAqOGG5VLyiHANSBRcoFTwjVXCAUsE3UoXiKBW8I1UojFLBP1KFogyX6tWC9MHFDlKFgiyXig2q4kGqUAylQhCkCoVQKoRBqlAEpUIgpAoFUCqEQqqQH6VCMKQKuVEqhEOqkJflUrE9cXRIFXKiVAiJVCEfSoWgSBVyoVQIi1QhD0qFwEgVcqBUCI1UYXSUCsGRKoyMUiE8UoVRUSoIIFUYEaWCBFKF0bSb0j2hVEkiVRgJpYIMUoVRWC4Vn3uPGqnCCCgVpJAqZEepIIZUITNKBTmkCllRKggiVciIUkESqUI2lAqiSBUyoVSQRaqQBaWCMFKFDCgVpJEqDEepII5UYShKBXmkCsNQKkSAVGEISoUYkCoMRqkQBVKFgSgV4kCqMAilQiRIFQagVIgFqUJ/lArRIFXoi1IhHqQK/VAqRIRUoQ9KhZiQKvRGqRAVUoWeKBXiQqrQC6VCZEgVeqBUiA2pwkGUCtEhVTiAUiE+pAr7USpEiFRhH0qFGJEqdKNUiBKpQhdKhTiRKnSiVIgUqUIHSoVYkSrsoVSIFqnCLkqFeJEq7KBUiBipwmuUCjEjVdhGqRA1UoUtlApxI1XYRKkQOVKFMqVC/EgVjJdqXvrowglSBdul4prKCFIFSgUFSFXyWqvSPfGnyd2fGaQqda0l6Z74wzqVIaQqcaZLxd2fIaQqbZQKSpCqpFEqaEGqUkapoAapShilgh6kKl2UCoqQqmRRKmhCqlJFqaAKqUoUpYIupCpNlArKkKokUSpoQ6pSRKmgDqlKEKWCPqQqPZQKCpGq5FAqaESqUkOpoBKpSk1DuieUCnmQqsQsSveEUiEXUpUWSgWlSFVSKBW0IlUpoVRQi1QlhFJBL1KVDkoFxUhVMigV9Dr7MalKBaWCXr/+fJVUJYJSQa2b71wYnyZVaaBU0Orie3cfVaolUpUESgWtbpy+VpkslUhVEigVdJo4fv3lzHSpRKrSUJfuCaVCLice/GOmWiqRqkSsS/eEUiGPs//98Gplt1SkyjxKBY0m3vz50VSpg/T/IHhGqaDQiW+fP7k3XSJV6aBUUOjs4Zdz1VKJVKWDUkGdc8e/fjw+ViqRqoRQKqjz9oN/jE+XSqQqJZQK2kzc6nrsR6qSQKmgzcSRo2Ol3qT/p8EbSgVtJo4fmi6RqsRQKqhz+UKlRKoS025KB4VSYUSzX56cJFWJoVRQ5+3T56slUpUWSgV9/vVoukSq0kKpoM+VD8ZKpCotlAr6TLxXKZGqtFAqKPTpTIlUpYVSQaGzz6ukKi2UCgpd/Hq8RKqSMk+poM+JN66WSFVSWkvSQaFUGNm5vzwZI1VJoVRQ6M6bh6aGlYpUmUKpoNHZz+aGlopUWUKpoNE3/zUzSapSQqmg0YmnX1SHl4pU2UGpoNG5H65NZygVqTKDUkGjO+9/P3xJnVRZ0pAOCqVCDpmW1EmVIYvSQaFUyOFipiV1UmUHpYJGV964mmVJnVSZQamg0cQ7TzItqZMqKygVVPo0w5Q6qTKEUkGl2fsZl9RJlQ2UCiq9fXo845I6qTLB7gf/KJVplw9nXlInVRZQKqh09kJlhFCRKvUoFVR6+/rJkUpFqpSjVFDpyp8yvaNMqqygVFBp4sPsA1WkyoAF6aBQKuRy4x/ZB6pIlX52P05DqUy7cvyXe6OWilQpRqmg0p0Pn4wyUEWqtKNU0OmT0e/+SJVilAo6vX199Ls/UqWX3U+TUirbLn/+Y5VUpcPu9sSUyrbZC1O5SkWqdKJU0OnEX0ccUidVqlEq6PTJ06ujP/sjVWpRKuh08Xll1CF1UqXZmnRRKBXyuPw0xzwVqdLL7FZ6lMq2i8//U81dKlKlD6WCSp+8dTT/NRWp0odSQaUT92cKXFORKnXMbvtCqWybfW+ETz6QKv0oFVS6fP3H3M/+SJVClAoqvf3wXpF1KlKlTVu6KJQKOdw5+3S0Tz6QKuXMbqZAqUy78uuX56ukKiGUCir95XdzRe/+SJUmZl+noVSm3Tn7QZ6d9EiVWpQKGl359fm4i1KRKjWsjn5SKtPe/su7Lu7+SJUey9JJoVQY3dmn15zc/ZEqNawOVFEq0y7/fL7g4CepUsbqTuqUyjQHg5+kShmjC1WUyrTbfyo++EmqdJmXbgqlwqgmbnyde3NiUqWVzTV1SmXZH49/VmzTF1KlUMvkShWlsuzi7793efdHqnQw+fiPUhl2Z/ZjZ0MKpEoRi3PqlMqwc5ePFd2dilRpZHHrF0pl2IkjF9wuU5EqJQwuqlMqw7558Lji8tEfqVJjVToslAqZ3Zk9/JPrZSpSpYO9+z9KZZeXZSpSpYO5+z9KZZefZSpSpYO153+L0gcU3lx8cMrHMhWpUsHaSzWUyq5Zd1u+kCp9jM1/UiqzJm4U/tYfqdLM1qYKlMqsK99emPEXKlIVP1NLVZTKqrfffOOkr1UqUqWDdF0oFYabfePSd15DRaqiV5POC6XCMBP/fPZozHOpSFXsVqT7QqkwxJX3n497XE8nVTrYGQClVEadOPPQ8zIVqdKgIV0YSoVBbh8//NLb2CepUsTKu8qUyqSJs5/7eTmZVKkjnRhKhf5ufvqzz6lPUqVIS7oxlAp9nXjh7eVkUqWNjVkFSmXRN8f/++VciFUqUqWBiVRRKoMmzh5+4nvqk1QpYiFVlMqgm/8+dtX71CepUsTABCilsufEp7cehlulIlUa1KVDQ6lwwO1b3wdcpCJVKqhPFaUyZ+LsW2FXqUiVBtpTRamsuXL5h/tfhF2lIlUaKE8VpbLm7TMBR6lIlSK6U0WpbLkze/ytkKNUpEoR1amiVLbcvPzs6JRQqEhV7DTPVVEqU058euvCR6Fe+CNV6ihOFaWy5M7tW4dOVuVCRapipzdVK9KHDu6c++eZ6z8F2ZWKVGnVli5OXuvSRw7unDhy/0eJ+QRSpYl0cihV6iZmjx8+JXvvR6o0kG4OpUrclcvPrn0ne+tHqlRQubc6pbLi4vvvXfhI/N6PVGmgMVWUyoZzZ4+c/mCuKt0oUqWDwhlQSmXDieM/P5oK9I0HUqXfunR4KFWSJmaPf3xoRnDik1Rpo26wilIZcOLyD+JzVKRKG+n0UKrkTMze+vP4TByL6aRKjyXp+FCqtGxcUf18NLIrKlKlwbJ0fShVQs6dPf7x78Zju6AiVSpoWlenVKrduXjxyC9HK9X4rqhIlQbz0v2hVIm4cuPp6VNRPfQjVbqsSheIUiXg3Oybf7pwVW7jPFJlgJLFqmZb+kAhr4mLn37785No5tJJlVIL0hGiVLaduPzi9KnxsYgvqEiVEtIVolSGXfnkf7534ep3k7GHilRpsCbdIUpl1MTtN799fvRRpSqdIVJlQ/zjCpRKo4kTR449+Wku2kd+pEqdlnSJKJVF515cf3Iy+hUqUqVK5HeAlEqjE//6j6JOkSod4n4GSKkUunP5q4+k20OqDIp5CpRSaXTjYTx75pEqQyLeCpRSaTT7/H9Jl2dkE9IHDRnE+x7g0rz0scHIJmb/94x0eEb3jfRhQxaxLqwvtaSPDEZ259f/M1WVDs/oXkgfN2QR6bbFlEqjs88r0tnJ4/M/Sh84ZBHlN7YolUITs6fHpauTy7vHb0ofO2QQ42UVpVJo4tc/a7z72zB+7J/SBw9ZxDevQKkUOnfj+knp5uQ0ffQBDwE1iO6yilIpNPHmhauqRtQ7TR27fUf6ACKDyFarGpRKn3M3nv0oHZz8Ji89OCF9BJFBXJdVi9KHA6Ob+PXCSbXXVBvmHr4pfQiRRUyzVZRKoYnZZ+repulSHX/KQ0ANIhpZp1QKTXz65XhVujYFW/XVZVbWNYjmTUBKpdGNd1VOfnb54vNZ6cOIDFqRDCxQKo1O/N/vpENT3Nild3gIqEEcK+uUSqMr/++jqnRoHKh8/bb0kUQWi9KZesWHSXW68uAn6co4MfnynSvSxxIZRHALSKlUevOx7od/u+599uY56YOJDMRvASmVSjc/vqd5oKpD9fx1XgVUQfir8JRKpYnjh6rSjXFl+hGvAuqwJBiqJqXS6cYvOvd96Wnql1meAmow35QrFduo63Tn9//R8l3SLH66xVNAFcS+tEWptLp9zMBI1Z7K7/7GU0AVhIbWKZVWJx5cMrKmvm3y5GefSh9TZCIyXbVKqbR68+GcdF3cqp5/g4EFFVoCS+tspKfWzTe0v6V8wPRnN3gKqEL4VlEqtSYuf2VpTX3b1bd4CqhD6MeAlEqv2c+vSofFvbFL7/EUUId20FbxgrJiZ/5u5JWaLpVDvAuoRMhWUSrF3n5mbE192+S9u79KH1pkE65Vy9L/qcjvypnHVemseFEdf4PNi5UI1SpeptHs3/e1fvZvaKvYvFiNdpAdYSiVZhP/+qIq3RRffjz9G54CKhFgZqFZk/6PRBGf3B+TLoo3Y9feuCh9fJFRy/f3tniZRrdvnj6SDopHU4+/5ROmavjdvmppXvq/D0WcePDY1HvK+809/JCldTXWPS6uM/ipnLmX//aZnHl+WfoYIzN/i+uMUyl386m5l//2qf74Hu8t6+Frwaou/R+GYiY+fWjv5b99pi/8m4kFRbzcBDKkoN3ZZ+elS+Lf1LFvpI8zRjDfcB0qHv2p983Ta3YHFfY8ecBTQFUcX1ixoK7fi8cV6YyEUHn3Be8tq9JyuTUoC+r6nTht++nfrnv332dpXZeaq7tAPqFlwM0PT1WlIxJG9aPrbAmqzbqTuQXmPg2Y+PT5jHRDgrXq0eHb0scboyoeqyYzChac/drua8oHjF26xdK6PgVjtcgllQW3//toCk//dlQOnWFpXaGF3GtWzTqhsuHFS4ubFPc1ee/umyytazS/nGd0YXFB+n83HEnm6d9uq8aPsbSu1MJob9s0FxeYpDLD7CbF/VWvsrSuVmt9Ldu1VXNthdF0Sybe/4PVTYr7Y2ldt/bK2sBV9mZjeZ1MGXPnxrHzVelyhPcdS+vatWrr9cVG91L7aqOxXF+vcc9n0eznj0xuqPD/ARoJN/JSZKiDAAAAAElFTkSuQmCC"/> +</svg> diff --git a/doc/source/_static/index-images/user_guide.svg b/doc/source/_static/index-images/user_guide.svg new file mode 100644 index 000000000000..d61b0937da75 --- /dev/null +++ b/doc/source/_static/index-images/user_guide.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#013243"><g><rect fill="none" height="24" width="24"/></g><g><g/><g><path d="M21,5c-1.11-0.35-2.33-0.5-3.5-0.5c-1.95,0-4.05,0.4-5.5,1.5c-1.45-1.1-3.55-1.5-5.5-1.5S2.45,4.9,1,6v14.65 c0,0.25,0.25,0.5,0.5,0.5c0.1,0,0.15-0.05,0.25-0.05C3.1,20.45,5.05,20,6.5,20c1.95,0,4.05,0.4,5.5,1.5c1.35-0.85,3.8-1.5,5.5-1.5 c1.65,0,3.35,0.3,4.75,1.05c0.1,0.05,0.15,0.05,0.25,0.05c0.25,0,0.5-0.25,0.5-0.5V6C22.4,5.55,21.75,5.25,21,5z M21,18.5 c-1.1-0.35-2.3-0.5-3.5-0.5c-1.7,0-4.15,0.65-5.5,1.5V8c1.35-0.85,3.8-1.5,5.5-1.5c1.2,0,2.4,0.15,3.5,0.5V18.5z"/><g><path d="M17.5,10.5c0.88,0,1.73,0.09,2.5,0.26V9.24C19.21,9.09,18.36,9,17.5,9c-1.7,0-3.24,0.29-4.5,0.83v1.66 C14.13,10.85,15.7,10.5,17.5,10.5z"/><path d="M13,12.49v1.66c1.13-0.64,2.7-0.99,4.5-0.99c0.88,0,1.73,0.09,2.5,0.26V11.9c-0.79-0.15-1.64-0.24-2.5-0.24 C15.8,11.66,14.26,11.96,13,12.49z"/><path d="M17.5,14.33c-1.7,0-3.24,0.29-4.5,0.83v1.66c1.13-0.64,2.7-0.99,4.5-0.99c0.88,0,1.73,0.09,2.5,0.26v-1.52 C19.21,14.41,18.36,14.33,17.5,14.33z"/></g></g></g></svg> \ No newline at end of file diff --git a/doc/source/_static/numpy.css b/doc/source/_static/numpy.css new file mode 100644 index 000000000000..53b610bf13d9 --- /dev/null +++ b/doc/source/_static/numpy.css @@ -0,0 +1,34 @@ +@import url('https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,400;0,700;0,900;1,400;1,700;1,900&family=Open+Sans:ital,wght@0,400;0,600;1,400;1,600&display=swap'); + +.navbar-brand img { + height: 75px; +} +.navbar-brand { + height: 75px; +} + +body { + font-family: 'Open Sans', sans-serif; + color:#4A4A4A; /* numpy.org body color */ +} + +pre, code { + font-size: 100%; + line-height: 155%; +} + +h1 { + font-family: "Lato", sans-serif; + color: #013243; /* warm black */ +} + + +h2 { + color: #4d77cf; /* han blue */ + letter-spacing: -.03em; +} + +h3 { + color: #013243; /* warm black */ + letter-spacing: -.03em; +} diff --git a/doc/source/_static/numpylogo.svg b/doc/source/_static/numpylogo.svg new file mode 100644 index 000000000000..a566851b8699 --- /dev/null +++ b/doc/source/_static/numpylogo.svg @@ -0,0 +1,23 @@ +<?xml version="1.0" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<!--Generator: Xara Designer (www.xara.com), SVG filter version: 6.4.0.3--> +<svg fill="none" fill-rule="evenodd" stroke="black" stroke-width="0.501" stroke-linejoin="bevel" stroke-miterlimit="10" font-family="Times New Roman" font-size="16" style="font-variant-ligatures:none" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" version="1.1" overflow="visible" width="255.845pt" height="123.322pt" viewBox="0 -123.322 255.845 123.322"> + <defs> + </defs> + <g id="Layer 1" transform="scale(1 -1)"> + <path d="M 107.188,79.018 C 107.386,78.994 107.58,78.94 107.762,78.859 C 107.941,78.774 108.106,78.663 108.252,78.529 C 108.44,78.349 108.616,78.158 108.78,77.955 L 123.492,59.358 C 123.432,59.95 123.393,60.531 123.364,61.088 C 123.336,61.644 123.322,62.176 123.322,62.672 L 123.322,79.079 L 129.655,79.079 L 129.655,48.109 L 125.913,48.109 C 125.433,48.095 124.956,48.182 124.513,48.364 C 124.073,48.581 123.693,48.902 123.407,49.3 L 108.801,67.73 C 108.847,67.195 108.879,66.667 108.907,66.149 C 108.936,65.632 108.953,65.146 108.953,64.692 L 108.953,48.091 L 102.616,48.091 L 102.616,79.079 L 106.398,79.079 C 106.662,79.076 106.926,79.056 107.188,79.018 Z" fill="#013243" stroke="none" stroke-width="0.354" fill-rule="nonzero" stroke-linejoin="miter" marker-start="none" marker-end="none"/> + <path d="M 138.934,70.158 L 138.934,56.172 C 138.934,55.08 139.182,54.237 139.679,53.641 C 140.233,53.023 141.04,52.693 141.869,52.748 C 142.571,52.744 143.265,52.896 143.9,53.195 C 144.571,53.52 145.191,53.943 145.739,54.45 L 145.739,70.158 L 152.328,70.158 L 152.328,48.116 L 148.249,48.116 C 147.515,48.055 146.839,48.516 146.629,49.222 L 146.228,50.498 C 145.814,50.096 145.373,49.722 144.91,49.378 C 144.455,49.046 143.966,48.763 143.453,48.531 C 142.913,48.287 142.349,48.099 141.77,47.971 C 141.128,47.831 140.473,47.763 139.817,47.769 C 138.721,47.749 137.634,47.962 136.627,48.396 C 135.723,48.797 134.92,49.395 134.277,50.147 C 133.624,50.928 133.132,51.832 132.831,52.805 C 132.495,53.893 132.33,55.026 132.342,56.165 L 132.342,70.158 Z" fill="#013243" stroke="none" stroke-width="0.354" fill-rule="nonzero" stroke-linejoin="miter" marker-start="none" marker-end="none"/> + <path d="M 156.578,48.109 L 156.578,70.158 L 160.661,70.158 C 161.024,70.171 161.384,70.075 161.692,69.881 C 161.978,69.682 162.185,69.388 162.277,69.052 L 162.631,67.861 C 162.989,68.24 163.371,68.596 163.776,68.924 C 164.175,69.245 164.606,69.522 165.063,69.754 C 166.067,70.263 167.18,70.522 168.306,70.509 C 169.494,70.555 170.661,70.191 171.612,69.477 C 172.508,68.755 173.194,67.805 173.597,66.727 C 173.947,67.379 174.403,67.969 174.948,68.471 C 175.463,68.94 176.043,69.333 176.67,69.637 C 177.291,69.936 177.947,70.157 178.623,70.296 C 179.299,70.437 179.988,70.508 180.679,70.509 C 181.822,70.528 182.96,70.337 184.035,69.945 C 184.97,69.598 185.811,69.037 186.491,68.308 C 187.174,67.546 187.685,66.647 187.99,65.671 C 188.347,64.524 188.519,63.327 188.501,62.126 L 188.501,48.119 L 181.908,48.119 L 181.908,62.116 C 181.908,64.398 180.931,65.538 178.977,65.536 C 178.146,65.563 177.341,65.243 176.755,64.653 C 176.167,64.07 175.873,63.224 175.873,62.116 L 175.873,48.109 L 169.291,48.109 L 169.291,62.116 C 169.291,63.378 169.043,64.264 168.547,64.774 C 168.05,65.284 167.32,65.536 166.356,65.536 C 165.769,65.537 165.19,65.4 164.666,65.135 C 164.115,64.85 163.61,64.484 163.166,64.051 L 163.166,48.102 Z" fill="#013243" stroke="none" stroke-width="0.354" fill-rule="nonzero" stroke-linejoin="miter" marker-start="none" marker-end="none"/> + <path d="M 199.516,58.462 L 199.516,48.109 L 192.332,48.109 L 192.332,79.079 L 203.255,79.079 C 205.159,79.121 207.058,78.861 208.88,78.309 C 210.302,77.874 211.618,77.15 212.747,76.183 C 213.741,75.307 214.51,74.206 214.991,72.972 C 215.476,71.697 215.716,70.342 215.699,68.977 C 215.716,67.526 215.464,66.084 214.955,64.724 C 214.472,63.453 213.692,62.316 212.68,61.407 C 211.553,60.424 210.232,59.69 208.802,59.252 C 207.007,58.695 205.135,58.429 203.255,58.462 Z M 199.516,63.881 L 203.255,63.881 C 205.127,63.881 206.474,64.324 207.296,65.221 C 208.118,66.117 208.529,67.347 208.529,68.96 C 208.538,69.619 208.43,70.274 208.21,70.895 C 208.007,71.462 207.676,71.975 207.243,72.394 C 206.774,72.832 206.215,73.162 205.605,73.362 C 204.847,73.607 204.053,73.726 203.255,73.716 L 199.516,73.716 Z" fill="#013243" stroke="none" stroke-width="0.354" fill-rule="nonzero" stroke-linejoin="miter" marker-start="none" marker-end="none"/> + <path d="M 228.466,42.388 C 228.316,42.012 228.072,41.68 227.757,41.424 C 227.345,41.186 226.87,41.078 226.396,41.116 L 221.452,41.116 L 225.705,50.04 L 216.908,70.158 L 222.731,70.158 C 223.157,70.179 223.577,70.054 223.922,69.803 C 224.192,69.595 224.398,69.315 224.517,68.995 L 228.129,59.493 C 228.463,58.637 228.74,57.759 228.958,56.867 C 229.1,57.32 229.256,57.767 229.426,58.203 C 229.596,58.639 229.759,59.089 229.915,59.543 L 233.19,69.002 C 233.314,69.343 233.55,69.632 233.86,69.821 C 234.174,70.034 234.544,70.148 234.923,70.151 L 240.24,70.151 Z" fill="#013243" stroke="none" stroke-width="0.354" fill-rule="nonzero" stroke-linejoin="miter" marker-start="none" marker-end="none"/> + <path d="M 46.918,89.155 L 33.759,95.797 L 19.312,88.588 L 32.83,81.801 L 46.918,89.155 Z" fill="#4dabcf" stroke="none" stroke-width="0.354" fill-rule="nonzero" stroke-linejoin="miter" marker-start="none" marker-end="none"/> + <path d="M 52.954,86.11 L 66.752,79.142 L 52.437,71.955 L 38.898,78.752 L 52.954,86.11 Z" fill="#4dabcf" stroke="none" stroke-width="0.354" fill-rule="nonzero" stroke-linejoin="miter" marker-start="none" marker-end="none"/> + <path d="M 71.384,95.698 L 85.561,88.588 L 72.88,82.222 L 59.054,89.197 L 71.384,95.698 Z" fill="#4dabcf" stroke="none" stroke-width="0.354" fill-rule="nonzero" stroke-linejoin="miter" marker-start="none" marker-end="none"/> + <path d="M 65.281,98.76 L 52.518,105.161 L 39.894,98.859 L 53.046,92.228 L 65.281,98.76 Z" fill="#4dabcf" stroke="none" stroke-width="0.354" fill-rule="nonzero" stroke-linejoin="miter" marker-start="none" marker-end="none"/> + <path d="M 55.304,43.803 L 55.304,26.386 L 70.764,34.102 L 70.75,51.526 L 55.304,43.803 Z" fill="#4dabcf" stroke="none" stroke-width="0.354" fill-rule="nonzero" stroke-linejoin="miter" marker-start="none" marker-end="none"/> + <path d="M 70.743,57.607 L 70.725,74.847 L 55.304,67.18 L 55.304,49.934 L 70.743,57.607 Z" fill="#4dabcf" stroke="none" stroke-width="0.354" fill-rule="nonzero" stroke-linejoin="miter" marker-start="none" marker-end="none"/> + <path d="M 89.304,60.836 L 89.304,43.352 L 76.116,36.774 L 76.105,54.177 L 89.304,60.836 Z" fill="#4dabcf" stroke="none" stroke-width="0.354" fill-rule="nonzero" stroke-linejoin="miter" marker-start="none" marker-end="none"/> + <path d="M 89.304,66.95 L 89.304,84.083 L 76.091,77.516 L 76.102,60.241 L 89.304,66.95 Z" fill="#4dabcf" stroke="none" stroke-width="0.354" fill-rule="nonzero" stroke-linejoin="miter" marker-start="none" marker-end="none"/> + <path d="M 49.846,67.18 L 39.433,72.419 L 39.433,49.792 C 39.433,49.792 26.695,76.892 25.518,79.327 C 25.366,79.642 24.742,79.986 24.582,80.071 C 22.286,81.269 15.594,84.657 15.594,84.657 L 15.594,44.667 L 24.852,39.705 L 24.852,60.617 C 24.852,60.617 37.452,36.402 37.583,36.136 C 37.714,35.871 38.972,33.322 40.326,32.426 C 42.123,31.231 49.839,26.592 49.839,26.592 Z" fill="#4d77cf" stroke="none" stroke-width="0.354" fill-rule="nonzero" stroke-linejoin="miter" marker-start="none" marker-end="none"/> + </g> +</svg> diff --git a/doc/source/_static/scipy-mathjax b/doc/source/_static/scipy-mathjax new file mode 160000 index 000000000000..36f4c898f225 --- /dev/null +++ b/doc/source/_static/scipy-mathjax @@ -0,0 +1 @@ +Subproject commit 36f4c898f2255e0c98eb6949cd67381552d5ffea diff --git a/doc/source/_templates/autosummary/attribute.rst b/doc/source/_templates/autosummary/attribute.rst new file mode 100644 index 000000000000..9e0eaa25fdfb --- /dev/null +++ b/doc/source/_templates/autosummary/attribute.rst @@ -0,0 +1,13 @@ +:orphan: + +{{ fullname | escape | underline}} + +.. currentmodule:: {{ module }} + +attribute + +.. auto{{ objtype }}:: {{ fullname | replace("numpy.", "numpy::") }} + +{# In the fullname (e.g. `numpy.ma.MaskedArray.methodname`), the module name +is ambiguous. Using a `::` separator (e.g. `numpy::ma.MaskedArray.methodname`) +specifies `numpy` as the module name. #} diff --git a/doc/source/_templates/autosummary/base.rst b/doc/source/_templates/autosummary/base.rst new file mode 100644 index 000000000000..91bfff9ba870 --- /dev/null +++ b/doc/source/_templates/autosummary/base.rst @@ -0,0 +1,17 @@ +{% if objtype == 'property' %} +:orphan: +{% endif %} + +{{ fullname | escape | underline}} + +.. currentmodule:: {{ module }} + +{% if objtype == 'property' %} +property +{% endif %} + +.. auto{{ objtype }}:: {{ fullname | replace("numpy.", "numpy::") }} + +{# In the fullname (e.g. `numpy.ma.MaskedArray.methodname`), the module name +is ambiguous. Using a `::` separator (e.g. `numpy::ma.MaskedArray.methodname`) +specifies `numpy` as the module name. #} diff --git a/doc/source/_templates/autosummary/member.rst b/doc/source/_templates/autosummary/member.rst new file mode 100644 index 000000000000..c0dcd5ed2676 --- /dev/null +++ b/doc/source/_templates/autosummary/member.rst @@ -0,0 +1,13 @@ +:orphan: + +{{ fullname | escape | underline}} + +.. currentmodule:: {{ module }} + +member + +.. auto{{ objtype }}:: {{ fullname | replace("numpy.", "numpy::") }} + +{# In the fullname (e.g. `numpy.ma.MaskedArray.methodname`), the module name +is ambiguous. Using a `::` separator (e.g. `numpy::ma.MaskedArray.methodname`) +specifies `numpy` as the module name. #} diff --git a/doc/source/_templates/autosummary/method.rst b/doc/source/_templates/autosummary/method.rst new file mode 100644 index 000000000000..0dd2263932c6 --- /dev/null +++ b/doc/source/_templates/autosummary/method.rst @@ -0,0 +1,13 @@ +:orphan: + +{{ fullname | escape | underline}} + +.. currentmodule:: {{ module }} + +method + +.. auto{{ objtype }}:: {{ fullname | replace("numpy.", "numpy::") }} + +{# In the fullname (e.g. `numpy.ma.MaskedArray.methodname`), the module name +is ambiguous. Using a `::` separator (e.g. `numpy::ma.MaskedArray.methodname`) +specifies `numpy` as the module name. #} diff --git a/doc/source/_templates/autosummary/module.rst b/doc/source/_templates/autosummary/module.rst new file mode 100644 index 000000000000..e1f428d6598e --- /dev/null +++ b/doc/source/_templates/autosummary/module.rst @@ -0,0 +1,40 @@ +{% extends "!autosummary/module.rst" %} + +{# This file is almost the same as the default, but adds :toctree: to the autosummary directives. + The original can be found at `sphinx/ext/autosummary/templates/autosummary/module.rst`. #} + +{% block attributes %} +{% if attributes %} + .. rubric:: Module Attributes + + .. autosummary:: + :toctree: + {% for item in attributes %} + {{ item }} + {%- endfor %} +{% endif %} +{% endblock %} + +{% block functions %} +{% if functions %} + .. rubric:: Functions + + .. autosummary:: + :toctree: + {% for item in functions %} + {{ item }} + {%- endfor %} +{% endif %} +{% endblock %} + +{% block classes %} +{% if classes %} + .. rubric:: Classes + + .. autosummary:: + :toctree: + {% for item in classes %} + {{ item }} + {%- endfor %} +{% endif %} +{% endblock %} diff --git a/doc/source/_templates/defindex.html b/doc/source/_templates/defindex.html deleted file mode 100644 index 8eaadecb9283..000000000000 --- a/doc/source/_templates/defindex.html +++ /dev/null @@ -1,35 +0,0 @@ -{# - basic/defindex.html - ~~~~~~~~~~~~~~~~~~~ - - Default template for the "index" page. - - :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. - :license: BSD, see LICENSE for details. -#} -{%- extends "layout.html" %} -{% set title = _('Overview') %} -{% block body %} - <h1>{{ docstitle|e }}</h1> - <p> - {{ _('Welcome! This is') }} - {% block description %}{{ _('the documentation for') }} {{ project|e }} - {{ release|e }}{% if last_updated %}, {{ _('last updated') }} {{ last_updated|e }}{% endif %}{% endblock %}. - </p> - {% block tables %} - <p><strong>{{ _('Indices and tables:') }}</strong></p> - <table class="contentstable"><tr> - <td style="width: 50%"> - <p class="biglink"><a class="biglink" href="{{ pathto("contents") }}">{{ _('Complete Table of Contents') }}</a><br> - <span class="linkdescr">{{ _('lists all sections and subsections') }}</span></p> - <p class="biglink"><a class="biglink" href="{{ pathto("search") }}">{{ _('Search Page') }}</a><br> - <span class="linkdescr">{{ _('search this documentation') }}</span></p> - </td><td style="width: 50%"> - <p class="biglink"><a class="biglink" href="{{ pathto("modindex") }}">{{ _('Global Module Index') }}</a><br> - <span class="linkdescr">{{ _('quick access to all modules') }}</span></p> - <p class="biglink"><a class="biglink" href="{{ pathto("genindex") }}">{{ _('General Index') }}</a><br> - <span class="linkdescr">{{ _('all functions, classes, terms') }}</span></p> - </td></tr> - </table> - {% endblock %} -{% endblock %} diff --git a/doc/source/_templates/indexcontent.html b/doc/source/_templates/indexcontent.html deleted file mode 100644 index fbd8930ae39e..000000000000 --- a/doc/source/_templates/indexcontent.html +++ /dev/null @@ -1,60 +0,0 @@ -{% extends "defindex.html" %} -{% block tables %} - <p><strong>Parts of the documentation:</strong></p> - <table class="contentstable" align="center"><tr> - <td width="50%"> - <p class="biglink"><a class="biglink" href="{{ pathto("user/index") }}">NumPy User Guide</a><br/> - <span class="linkdescr">start here</span></p> - <p class="biglink"><a class="biglink" href="{{ pathto("reference/index") }}">NumPy Reference</a><br/> - <span class="linkdescr">reference documentation</span></p> - <p class="biglink"><a class="biglink" href="{{ pathto("f2py/index") }}">F2Py Guide</a><br/> - <span class="linkdescr">f2py documentation</span></p> - <p class="biglink"><a class="biglink" href="{{ pathto("dev/index") }}">NumPy Developer Guide</a><br/> - <span class="linkdescr">contributing to NumPy</span></p> - <p class="biglink"><a class="biglink" href="{{ pathto("docs/index") }}">Building and Extending the Documentation</a><br/> - <span class="linkdescr">about this documentation</span></p> - </td></tr> - </table> - - <p><strong>Indices and tables:</strong></p> - <table class="contentstable" align="center"><tr> - <td width="50%"> - <p class="biglink"><a class="biglink" href="{{ pathto("genindex") }}">General Index</a><br/> - <span class="linkdescr">all functions, classes, terms</span></p> - <p class="biglink"><a class="biglink" href="{{ pathto("glossary") }}">Glossary</a><br/> - <span class="linkdescr">the most important terms explained</span></p> - </td><td width="50%"> - <p class="biglink"><a class="biglink" href="{{ pathto("search") }}">Search page</a><br/> - <span class="linkdescr">search this documentation</span></p> - <p class="biglink"><a class="biglink" href="{{ pathto("contents") }}">Complete Table of Contents</a><br/> - <span class="linkdescr">lists all sections and subsections</span></p> - </td></tr> - </table> - - <p><strong>Meta information:</strong></p> - <table class="contentstable" align="center"><tr> - <td width="50%"> - <p class="biglink"><a class="biglink" href="{{ pathto("bugs") }}">Reporting bugs</a></p> - <p class="biglink"><a class="biglink" href="{{ pathto("about") }}">About NumPy</a></p> - <p class="biglink"><a class="biglink" href="http://www.numpy.org/neps/index.html"> - NumPy Enhancement Proposals</a><br/> - </td><td width="50%"> - <p class="biglink"><a class="biglink" href="{{ pathto("release") }}">Release Notes</a></p> - <p class="biglink"><a class="biglink" href="{{ pathto("license") }}">License of NumPy</a></p> - </td></tr> - </table> - - <h2>Acknowledgements</h2> - <p> - Large parts of this manual originate from Travis E. Oliphant's book - <a href="https://archive.org/details/NumPyBook">"Guide to NumPy"</a> - (which generously entered Public Domain in August 2008). The reference - documentation for many of the functions are written by numerous - contributors and developers of NumPy. - </p> - <p> - The preferred way to update the documentation is by submitting a pull - request on Github (see the <a href="{{ pathto("docs/index") }}">Documentation Index</a>. - Please help us to further improve the NumPy documentation! - </p> -{% endblock %} diff --git a/doc/source/_templates/indexsidebar.html b/doc/source/_templates/indexsidebar.html deleted file mode 100644 index 9edb003affc8..000000000000 --- a/doc/source/_templates/indexsidebar.html +++ /dev/null @@ -1,4 +0,0 @@ - <h3>Resources</h3> - <ul> - <li><a href="http://scipy.org/">Scipy.org website</a></li> - </ul> diff --git a/doc/source/_templates/layout.html b/doc/source/_templates/layout.html index 77da54a003e1..e2812fdd5ff3 100644 --- a/doc/source/_templates/layout.html +++ b/doc/source/_templates/layout.html @@ -1,20 +1,10 @@ {% extends "!layout.html" %} -{% block rootrellink %} - {% if pagename != 'index' %} - <li class="active"><a href="{{ pathto('index') }}">{{ shorttitle|e }}</a></li> - {% endif %} -{% endblock %} - -{% block sidebarsearch %} -{%- if sourcename %} -<ul class="this-page-menu"> -{%- if 'reference/generated' in sourcename %} - <li><a href="/numpy/docs/{{ sourcename.replace('reference/generated/', '').replace('.txt', '') |e }}">{{_('Edit page')}}</a></li> -{%- else %} - <li><a href="/numpy/docs/numpy-docs/{{ sourcename.replace('.txt', '.rst') |e }}">{{_('Edit page')}}</a></li> -{%- endif %} -</ul> -{%- endif %} +{%- block extrahead %} {{ super() }} +<link rel="stylesheet" href="{{ pathto('_static/numpy.css', 1) }}" type="text/css" /> + + <!-- PR #17220: This is added via javascript in versionwarning.js --> + <!-- link rel="canonical" href="http://numpy.org/doc/stable/{{ pagename }}{{ file_suffix }}" / --> + {% endblock %} diff --git a/doc/source/about.rst b/doc/source/about.rst deleted file mode 100644 index 776488ea4a89..000000000000 --- a/doc/source/about.rst +++ /dev/null @@ -1,64 +0,0 @@ -About NumPy -=========== - -NumPy is the fundamental package -needed for scientific computing with Python. This package contains: - -- a powerful N-dimensional :ref:`array object <arrays>` -- sophisticated :ref:`(broadcasting) functions <ufuncs>` -- basic :ref:`linear algebra functions <routines.linalg>` -- basic :ref:`Fourier transforms <routines.fft>` -- sophisticated :ref:`random number capabilities <routines.random>` -- tools for integrating Fortran code -- tools for integrating C/C++ code - -Besides its obvious scientific uses, *NumPy* can also be used as an -efficient multi-dimensional container of generic data. Arbitrary -data types can be defined. This allows *NumPy* to seamlessly and -speedily integrate with a wide variety of databases. - -NumPy is a successor for two earlier scientific Python libraries: -NumPy derives from the old *Numeric* code base and can be used -as a replacement for *Numeric*. It also adds the features introduced -by *Numarray* and can also be used to replace *Numarray*. - -NumPy community ---------------- - -NumPy is a distributed, volunteer, open-source project. *You* can help -us make it better; if you believe something should be improved either -in functionality or in documentation, don't hesitate to contact us --- or -even better, contact us and participate in fixing the problem. - -Our main means of communication are: - -- `scipy.org website <http://scipy.org/>`__ - -- `Mailing lists <http://scipy.org/Mailing_Lists>`__ - -- `NumPy Issues <https://github.com/numpy/numpy/issues>`__ (bug reports go here) - -- `Old NumPy Trac <http://projects.scipy.org/numpy>`__ (no longer used) - -More information about the development of NumPy can be found at our `Developer Zone <https://scipy.scipy.org/scipylib/dev-zone.html>`__. - -The project management structure can be found at our :doc:`governance page <dev/governance/index>` - - -About this documentation -======================== - -Conventions ------------ - -Names of classes, objects, constants, etc. are given in **boldface** font. -Often they are also links to a more detailed documentation of the -referred object. - -This manual contains many examples of use, usually prefixed with the -Python prompt ``>>>`` (which is not a part of the example code). The -examples assume that you have first entered:: - ->>> import numpy as np - -before running the examples. diff --git a/doc/source/benchmarking.rst b/doc/source/benchmarking.rst new file mode 100644 index 000000000000..9f0eeb03aa37 --- /dev/null +++ b/doc/source/benchmarking.rst @@ -0,0 +1 @@ +.. include:: ../../benchmarks/README.rst diff --git a/doc/source/bugs.rst b/doc/source/bugs.rst index 950934b14dcf..304a4136a4a1 100644 --- a/doc/source/bugs.rst +++ b/doc/source/bugs.rst @@ -5,7 +5,7 @@ Reporting bugs File bug reports or feature requests, and make contributions (e.g. code patches), by opening a "new issue" on GitHub: -- NumPy Issues: http://github.com/numpy/numpy/issues +- NumPy Issues: https://github.com/numpy/numpy/issues Please give as much information as you can in the ticket. It is extremely useful if you can supply a small self-contained code snippet that reproduces @@ -15,5 +15,5 @@ the milestone. Report bugs to the appropriate GitHub project (there is one for NumPy and a different one for SciPy). -More information can be found on the http://scipy.org/Developer_Zone -website. +More information can be found on the +https://www.scipy.org/scipylib/dev-zone.html website. diff --git a/doc/source/conf.py b/doc/source/conf.py index 1472f5155ca8..cd5aadd81a9b 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -1,14 +1,66 @@ -# -*- coding: utf-8 -*- -from __future__ import division, absolute_import, print_function +import os +import re +import sys +import importlib -import sys, os, re +# Minimum version, enforced by sphinx +needs_sphinx = '3.2.0' -# Check Sphinx version -import sphinx -if sphinx.__version__ < "1.2.1": - raise RuntimeError("Sphinx 1.2.1 or newer required") -needs_sphinx = '1.0' +# This is a nasty hack to use platform-agnostic names for types in the +# documentation. + +# must be kept alive to hold the patched names +_name_cache = {} + +def replace_scalar_type_names(): + """ Rename numpy types to use the canonical names to make sphinx behave """ + import ctypes + + Py_ssize_t = ctypes.c_int64 if ctypes.sizeof(ctypes.c_void_p) == 8 else ctypes.c_int32 + + class PyObject(ctypes.Structure): + pass + + class PyTypeObject(ctypes.Structure): + pass + + PyObject._fields_ = [ + ('ob_refcnt', Py_ssize_t), + ('ob_type', ctypes.POINTER(PyTypeObject)), + ] + + + PyTypeObject._fields_ = [ + # varhead + ('ob_base', PyObject), + ('ob_size', Py_ssize_t), + # declaration + ('tp_name', ctypes.c_char_p), + ] + + # prevent numpy attaching docstrings to the scalar types + assert 'numpy.core._add_newdocs_scalars' not in sys.modules + sys.modules['numpy.core._add_newdocs_scalars'] = object() + + import numpy + + # change the __name__ of the scalar types + for name in [ + 'byte', 'short', 'intc', 'int_', 'longlong', + 'ubyte', 'ushort', 'uintc', 'uint', 'ulonglong', + 'half', 'single', 'double', 'longdouble', + 'half', 'csingle', 'cdouble', 'clongdouble', + ]: + typ = getattr(numpy, name) + c_typ = PyTypeObject.from_address(id(typ)) + c_typ.tp_name = _name_cache[typ] = b"numpy." + name.encode('utf8') + + # now generate the docstrings as usual + del sys.modules['numpy.core._add_newdocs_scalars'] + import numpy.core._add_newdocs_scalars + +replace_scalar_type_names() # ----------------------------------------------------------------------------- # General configuration @@ -19,17 +71,31 @@ sys.path.insert(0, os.path.abspath('../sphinxext')) -extensions = ['sphinx.ext.autodoc', 'numpydoc', - 'sphinx.ext.intersphinx', 'sphinx.ext.coverage', - 'sphinx.ext.doctest', 'sphinx.ext.autosummary', - 'sphinx.ext.graphviz', 'sphinx.ext.ifconfig', - 'matplotlib.sphinxext.plot_directive'] +extensions = [ + 'sphinx.ext.autodoc', + 'numpydoc', + 'sphinx.ext.intersphinx', + 'sphinx.ext.coverage', + 'sphinx.ext.doctest', + 'sphinx.ext.autosummary', + 'sphinx.ext.graphviz', + 'sphinx.ext.ifconfig', + 'matplotlib.sphinxext.plot_directive', + 'IPython.sphinxext.ipython_console_highlighting', + 'IPython.sphinxext.ipython_directive', + 'sphinx.ext.mathjax', + 'sphinx_panels', +] -if sphinx.__version__ >= "1.4": - extensions.append('sphinx.ext.imgmath') - imgmath_image_format = 'svg' -else: - extensions.append('sphinx.ext.pngmath') +skippable_extensions = [ + ('breathe', 'skip generating C/C++ API from comment blocks.'), +] +for ext, warn in skippable_extensions: + ext_exist = importlib.util.find_spec(ext) is not None + if ext_exist: + extensions.append(ext) + else: + print(f"Unable to find Sphinx extension '{ext}', {warn}.") # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -37,9 +103,12 @@ # The suffix of source filenames. source_suffix = '.rst' +# Will change to `root_doc` in Sphinx 4 +master_doc = 'index' + # General substitutions. project = 'NumPy' -copyright = '2008-2018, The SciPy community' +copyright = '2008-2021, The NumPy community' # The default replacements for |version| and |release|, also used in various # other places throughout the built documents. @@ -79,46 +148,37 @@ # output. They are ignored by default. #show_authors = False -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - def setup(app): # add a config value for `ifconfig` directives app.add_config_value('python_version_major', str(sys.version_info.major), 'env') + app.add_lexer('NumPyC', NumPyLexer) + +# While these objects do have type `module`, the names are aliases for modules +# elsewhere. Sphinx does not support referring to modules by an aliases name, +# so we make the alias look like a "real" module for it. +# If we deemed it desirable, we could in future make these real modules, which +# would make `from numpy.char import split` work. +sys.modules['numpy.char'] = numpy.char +sys.modules['numpy.testing.dec'] = numpy.testing.dec # ----------------------------------------------------------------------------- # HTML output # ----------------------------------------------------------------------------- -themedir = os.path.join(os.pardir, 'scipy-sphinx-theme', '_theme') -if not os.path.isdir(themedir): - raise RuntimeError("Get the scipy-sphinx-theme first, " - "via git submodule init && git submodule update") - -html_theme = 'scipy' -html_theme_path = [themedir] - -if 'scipyorg' in tags: - # Build for the scipy.org website - html_theme_options = { - "edit_link": True, - "sidebar": "right", - "scipy_org_logo": True, - "rootlinks": [("http://scipy.org/", "Scipy.org"), - ("http://docs.scipy.org/", "Docs")] - } -else: - # Default build - html_theme_options = { - "edit_link": False, - "sidebar": "left", - "scipy_org_logo": False, - "rootlinks": [] - } - html_sidebars = {'index': ['indexsidebar.html', 'searchbox.html']} +html_theme = 'pydata_sphinx_theme' -html_additional_pages = { - 'index': 'indexcontent.html', +html_logo = '_static/numpylogo.svg' + +html_favicon = '_static/favicon/favicon.ico' + +html_theme_options = { + "logo_link": "index", + "github_url": "https://github.com/numpy/numpy", + "twitter_url": "https://twitter.com/numpy_team", + "collapse_navigation": True, + "external_links": [ + {"name": "Learn", "url": "https://numpy.org/numpy-tutorials/"} + ], } html_title = "%s v%s Manual" % (project, version) @@ -136,6 +196,8 @@ def setup(app): pngmath_use_preview = True pngmath_dvipng_args = ['-gamma', '1.5', '-D', '96', '-bg', 'Transparent'] +mathjax_path = "scipy-mathjax/MathJax.js?config=scipy-mathjax" + plot_html_show_formats = False plot_html_show_source_link = False @@ -149,6 +211,9 @@ def setup(app): # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' +# XeLaTeX for better support of unicode characters +latex_engine = 'xelatex' + # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, document class [howto/manual]). _stdauthor = 'Written by the NumPy community' @@ -167,16 +232,34 @@ def setup(app): # not chapters. #latex_use_parts = False -# Additional stuff for the LaTeX preamble. -latex_preamble = r''' -\usepackage{amsmath} -\DeclareUnicodeCharacter{00A0}{\nobreakspace} +latex_elements = { + 'fontenc': r'\usepackage[LGR,T1]{fontenc}' +} +# Additional stuff for the LaTeX preamble. +latex_elements['preamble'] = r''' % In the parameters section, place a newline after the Parameters % header +\usepackage{xcolor} \usepackage{expdlist} \let\latexdescription=\description \def\description{\latexdescription{}{} \breaklabel} +% but expdlist old LaTeX package requires fixes: +% 1) remove extra space +\usepackage{etoolbox} +\makeatletter +\patchcmd\@item{{\@breaklabel} }{{\@breaklabel}}{}{} +\makeatother +% 2) fix bug in expdlist's way of breaking the line after long item label +\makeatletter +\def\breaklabel{% + \def\@breaklabel{% + \leavevmode\par + % now a hack because Sphinx inserts \leavevmode after term node + \def\leavevmode{\def\leavevmode{\unhbox\voidb@x}}% + }% +} +\makeatother % Make Examples/etc section headers smaller and more compact \makeatletter @@ -213,9 +296,17 @@ def setup(app): # Intersphinx configuration # ----------------------------------------------------------------------------- intersphinx_mapping = { + 'neps': ('https://numpy.org/neps', None), 'python': ('https://docs.python.org/dev', None), 'scipy': ('https://docs.scipy.org/doc/scipy/reference', None), - 'matplotlib': ('https://matplotlib.org', None) + 'matplotlib': ('https://matplotlib.org/stable', None), + 'imageio': ('https://imageio.readthedocs.io/en/stable', None), + 'skimage': ('https://scikit-image.org/docs/stable', None), + 'pandas': ('https://pandas.pydata.org/pandas-docs/stable', None), + 'scipy-lecture-notes': ('https://scipy-lectures.org', None), + 'pytest': ('https://docs.pytest.org/en/stable', None), + 'numpy-tutorials': ('https://numpy.org/numpy-tutorials', None), + 'numpydoc': ('https://numpydoc.readthedocs.io/en/latest', None), } @@ -233,8 +324,7 @@ def setup(app): # Autosummary # ----------------------------------------------------------------------------- -import glob -autosummary_generate = glob.glob("reference/*.rst") +autosummary_generate = True # ----------------------------------------------------------------------------- # Coverage checker @@ -300,6 +390,17 @@ def setup(app): else: print("NOTE: linkcode extension not found -- no links to source generated") + +def _get_c_source_file(obj): + if issubclass(obj, numpy.generic): + return r"core/src/multiarray/scalartypes.c.src" + elif obj is numpy.ndarray: + return r"core/src/multiarray/arrayobject.c" + else: + # todo: come up with a better way to generate these + return None + + def linkcode_resolve(domain, info): """ Determine the URL corresponding to Python object @@ -321,28 +422,72 @@ def linkcode_resolve(domain, info): except Exception: return None + # strip decorators, which would resolve to the source of the decorator + # possibly an upstream bug in getsourcefile, bpo-1764286 try: - fn = inspect.getsourcefile(obj) - except Exception: - fn = None - if not fn: - return None + unwrap = inspect.unwrap + except AttributeError: + pass + else: + obj = unwrap(obj) - try: - source, lineno = inspect.getsourcelines(obj) - except Exception: - lineno = None + fn = None + lineno = None + + # Make a poor effort at linking C extension types + if isinstance(obj, type) and obj.__module__ == 'numpy': + fn = _get_c_source_file(obj) + + if fn is None: + try: + fn = inspect.getsourcefile(obj) + except Exception: + fn = None + if not fn: + return None + + # Ignore re-exports as their source files are not within the numpy repo + module = inspect.getmodule(obj) + if module is not None and not module.__name__.startswith("numpy"): + return None + + try: + source, lineno = inspect.getsourcelines(obj) + except Exception: + lineno = None + + fn = relpath(fn, start=dirname(numpy.__file__)) if lineno: linespec = "#L%d-L%d" % (lineno, lineno + len(source) - 1) else: linespec = "" - fn = relpath(fn, start=dirname(numpy.__file__)) - if 'dev' in numpy.__version__: - return "http://github.com/numpy/numpy/blob/master/numpy/%s%s" % ( + return "https://github.com/numpy/numpy/blob/main/numpy/%s%s" % ( fn, linespec) else: - return "http://github.com/numpy/numpy/blob/v%s/numpy/%s%s" % ( + return "https://github.com/numpy/numpy/blob/v%s/numpy/%s%s" % ( numpy.__version__, fn, linespec) + +from pygments.lexers import CLexer +from pygments.lexer import inherit, bygroups +from pygments.token import Comment + +class NumPyLexer(CLexer): + name = 'NUMPYLEXER' + + tokens = { + 'statements': [ + (r'@[a-zA-Z_]*@', Comment.Preproc, 'macro'), + inherit, + ], + } + + +# ----------------------------------------------------------------------------- +# Breathe & Doxygen +# ----------------------------------------------------------------------------- +breathe_projects = dict(numpy=os.path.join("..", "build", "doxygen", "xml")) +breathe_default_project = "numpy" +breathe_default_members = ("members", "undoc-members", "protected-members") diff --git a/doc/source/contents.rst b/doc/source/contents.rst deleted file mode 100644 index fad9be76e498..000000000000 --- a/doc/source/contents.rst +++ /dev/null @@ -1,15 +0,0 @@ -##################### -NumPy manual contents -##################### - -.. toctree:: - - user/index - reference/index - f2py/index - dev/index - release - about - bugs - license - glossary diff --git a/doc/source/dev/alignment.rst b/doc/source/dev/alignment.rst new file mode 100644 index 000000000000..bb1198ebfc0e --- /dev/null +++ b/doc/source/dev/alignment.rst @@ -0,0 +1,113 @@ +.. currentmodule:: numpy + +.. _alignment: + +**************** +Memory Alignment +**************** + +NumPy alignment goals +===================== + +There are three use-cases related to memory alignment in NumPy (as of 1.14): + + 1. Creating :term:`structured datatypes <structured data type>` with + :term:`fields <field>` aligned like in a C-struct. + 2. Speeding up copy operations by using :class:`uint` assignment in instead of + ``memcpy``. + 3. Guaranteeing safe aligned access for ufuncs/setitem/casting code. + +NumPy uses two different forms of alignment to achieve these goals: +"True alignment" and "Uint alignment". + +"True" alignment refers to the architecture-dependent alignment of an +equivalent C-type in C. For example, in x64 systems :attr:`float64` is +equivalent to ``double`` in C. On most systems, this has either an alignment of +4 or 8 bytes (and this can be controlled in GCC by the option +``malign-double``). A variable is aligned in memory if its memory offset is a +multiple of its alignment. On some systems (eg. sparc) memory alignment is +required; on others, it gives a speedup. + +"Uint" alignment depends on the size of a datatype. It is defined to be the +"True alignment" of the uint used by NumPy's copy-code to copy the datatype, or +undefined/unaligned if there is no equivalent uint. Currently, NumPy uses +``uint8``, ``uint16``, ``uint32``, ``uint64``, and ``uint64`` to copy data of +size 1, 2, 4, 8, 16 bytes respectively, and all other sized datatypes cannot +be uint-aligned. + +For example, on a (typical Linux x64 GCC) system, the NumPy :attr:`complex64` +datatype is implemented as ``struct { float real, imag; }``. This has "true" +alignment of 4 and "uint" alignment of 8 (equal to the true alignment of +``uint64``). + +Some cases where uint and true alignment are different (default GCC Linux): + ====== ========= ======== ======== + arch type true-aln uint-aln + ====== ========= ======== ======== + x86_64 complex64 4 8 + x86_64 float128 16 8 + x86 float96 4 \- + ====== ========= ======== ======== + + +Variables in NumPy which control and describe alignment +======================================================= + +There are 4 relevant uses of the word ``align`` used in NumPy: + + * The :attr:`dtype.alignment` attribute (``descr->alignment`` in C). This is + meant to reflect the "true alignment" of the type. It has arch-dependent + default values for all datatypes, except for the structured types created + with ``align=True`` as described below. + * The ``ALIGNED`` flag of an ndarray, computed in ``IsAligned`` and checked + by :c:func:`PyArray_ISALIGNED`. This is computed from + :attr:`dtype.alignment`. + It is set to ``True`` if every item in the array is at a memory location + consistent with :attr:`dtype.alignment`, which is the case if the + ``data ptr`` and all strides of the array are multiples of that alignment. + * The ``align`` keyword of the dtype constructor, which only affects + :ref:`structured_arrays`. If the structure's field offsets are not manually + provided, NumPy determines offsets automatically. In that case, + ``align=True`` pads the structure so that each field is "true" aligned in + memory and sets :attr:`dtype.alignment` to be the largest of the field + "true" alignments. This is like what C-structs usually do. Otherwise if + offsets or itemsize were manually provided ``align=True`` simply checks that + all the fields are "true" aligned and that the total itemsize is a multiple + of the largest field alignment. In either case :attr:`dtype.isalignedstruct` + is also set to True. + * ``IsUintAligned`` is used to determine if an ndarray is "uint aligned" in + an analogous way to how ``IsAligned`` checks for true alignment. + +Consequences of alignment +========================= + +Here is how the variables above are used: + + 1. Creating aligned structs: To know how to offset a field when + ``align=True``, NumPy looks up ``field.dtype.alignment``. This includes + fields that are nested structured arrays. + 2. Ufuncs: If the ``ALIGNED`` flag of an array is False, ufuncs will + buffer/cast the array before evaluation. This is needed since ufunc inner + loops access raw elements directly, which might fail on some archs if the + elements are not true-aligned. + 3. Getitem/setitem/copyswap function: Similar to ufuncs, these functions + generally have two code paths. If ``ALIGNED`` is False they will + use a code path that buffers the arguments so they are true-aligned. + 4. Strided copy code: Here, "uint alignment" is used instead. If the itemsize + of an array is equal to 1, 2, 4, 8 or 16 bytes and the array is uint + aligned then instead NumPy will do ``*(uintN*)dst) = *(uintN*)src)`` for + appropriate N. Otherwise, NumPy copies by doing ``memcpy(dst, src, N)``. + 5. Nditer code: Since this often calls the strided copy code, it must + check for "uint alignment". + 6. Cast code: This checks for "true" alignment, as it does + ``*dst = CASTFUNC(*src)`` if aligned. Otherwise, it does + ``memmove(srcval, src); dstval = CASTFUNC(srcval); memmove(dst, dstval)`` + where dstval/srcval are aligned. + +Note that the strided-copy and strided-cast code are deeply intertwined and so +any arrays being processed by them must be both uint and true aligned, even +though the copy-code only needs uint alignment and the cast code only true +alignment. If there is ever a big rewrite of this code it would be good to +allow them to use different alignments. + + diff --git a/doc/source/dev/development_advanced_debugging.rst b/doc/source/dev/development_advanced_debugging.rst new file mode 100644 index 000000000000..18a7f6ae9ad1 --- /dev/null +++ b/doc/source/dev/development_advanced_debugging.rst @@ -0,0 +1,190 @@ +======================== +Advanced debugging tools +======================== + +If you reached here, you want to dive into, or use, more advanced tooling. +This is usually not necessary for first time contributors and most +day-to-day development. +These are used more rarely, for example close to a new NumPy release, +or when a large or particular complex change was made. + +Since not all of these tools are used on a regular bases and only available +on some systems, please expect differences, issues, or quirks; +we will be happy to help if you get stuck and appreciate any improvements +or suggestions to these workflows. + + +Finding C errors with additional tooling +######################################## + +Most development will not require more than a typical debugging toolchain +as shown in :ref:`Debugging <debugging>`. +But for example memory leaks can be particularly subtle or difficult to +narrow down. + +We do not expect any of these tools to be run by most contributors. +However, you can ensure that we can track down such issues more easily easier: + +* Tests should cover all code paths, including error paths. +* Try to write short and simple tests. If you have a very complicated test + consider creating an additional simpler test as well. + This can be helpful, because often it is only easy to find which test + triggers an issue and not which line of the test. +* Never use ``np.empty`` if data is read/used. ``valgrind`` will notice this + and report an error. When you do not care about values, you can generate + random values instead. + +This will help us catch any oversights before your change is released +and means you do not have to worry about making reference counting errors, +which can be intimidating. + + +Python debug build for finding memory leaks +=========================================== + +Debug builds of Python are easily available for example on ``debian`` systems, +and can be used on all platforms. +Running a test or terminal is usually as easy as:: + + python3.8d runtests.py + # or + python3.8d runtests.py --ipython + +and were already mentioned in :ref:`Debugging <debugging>`. + +A Python debug build will help: + +- Find bugs which may otherwise cause random behaviour. + One example is when an object is still used after it has been deleted. + +- Python debug builds allows to check correct reference counting. + This works using the additional commands:: + + sys.gettotalrefcount() + sys.getallocatedblocks() + + +Use together with ``pytest`` +---------------------------- + +Running the test suite only with a debug python build will not find many +errors on its own. An additional advantage of a debug build of Python is that +it allows detecting memory leaks. + +A tool to make this easier is `pytest-leaks`_, which can be installed using ``pip``. +Unfortunately, ``pytest`` itself may leak memory, but good results can usually +(currently) be achieved by removing:: + + @pytest.fixture(autouse=True) + def add_np(doctest_namespace): + doctest_namespace['np'] = numpy + + @pytest.fixture(autouse=True) + def env_setup(monkeypatch): + monkeypatch.setenv('PYTHONHASHSEED', '0') + +from ``numpy/conftest.py`` (This may change with new ``pytest-leaks`` versions +or ``pytest`` updates). + +This allows to run the test suite, or part of it, conveniently:: + + python3.8d runtests.py -t numpy/core/tests/test_multiarray.py -- -R2:3 -s + +where ``-R2:3`` is the ``pytest-leaks`` command (see its documentation), the +``-s`` causes output to print and may be necessary (in some versions captured +output was detected as a leak). + +Note that some tests are known (or even designed) to leak references, we try +to mark them, but expect some false positives. + +.. _pytest-leaks: https://github.com/abalkin/pytest-leaks + +``valgrind`` +============ + +Valgrind is a powerful tool to find certain memory access problems and should +be run on complicated C code. +Basic use of ``valgrind`` usually requires no more than:: + + PYTHONMALLOC=malloc python runtests.py + +where ``PYTHONMALLOC=malloc`` is necessary to avoid false positives from python +itself. +Depending on the system and valgrind version, you may see more false positives. +``valgrind`` supports "suppressions" to ignore some of these, and Python does +have a suppression file (and even a compile time option) which may help if you +find it necessary. + +Valgrind helps: + +- Find use of uninitialized variables/memory. + +- Detect memory access violations (reading or writing outside of allocated + memory). + +- Find *many* memory leaks. Note that for *most* leaks the python + debug build approach (and ``pytest-leaks``) is much more sensitive. + The reason is that ``valgrind`` can only detect if memory is definitely + lost. If:: + + dtype = np.dtype(np.int64) + arr.astype(dtype=dtype) + + Has incorrect reference counting for ``dtype``, this is a bug, but valgrind + cannot see it because ``np.dtype(np.int64)`` always returns the same object. + However, not all dtypes are singletons, so this might leak memory for + different input. + In rare cases NumPy uses ``malloc`` and not the Python memory allocators + which are invisible to the Python debug build. + ``malloc`` should normally be avoided, but there are some exceptions + (e.g. the ``PyArray_Dims`` structure is public API and cannot use the + Python allocators.) + +Even though using valgrind for memory leak detection is slow and less sensitive +it can be a convenient: you can run most programs with valgrind without +modification. + +Things to be aware of: + +- Valgrind does not support the numpy ``longdouble``, this means that tests + will fail or be flagged errors that are completely fine. + +- Expect some errors before and after running your NumPy code. + +- Caches can mean that errors (specifically memory leaks) may not be detected + or are only detect at a later, unrelated time. + +A big advantage of valgrind is that it has no requirements aside from valgrind +itself (although you probably want to use debug builds for better tracebacks). + + +Use together with ``pytest`` +---------------------------- +You can run the test suite with valgrind which may be sufficient +when you are only interested in a few tests:: + + PYTHOMMALLOC=malloc valgrind python runtests.py \ + -t numpy/core/tests/test_multiarray.py -- --continue-on-collection-errors + +Note the ``--continue-on-collection-errors``, which is currently necessary due to +missing ``longdouble`` support causing failures (this will usually not be +necessary if you do not run the full test suite). + +If you wish to detect memory leaks you will also require ``--show-leak-kinds=definite`` +and possibly more valgrind options. Just as for ``pytest-leaks`` certain +tests are known to leak cause errors in valgrind and may or may not be marked +as such. + +We have developed `pytest-valgrind`_ which: + +- Reports errors for each test individually + +- Narrows down memory leaks to individual tests (by default valgrind + only checks for memory leaks after a program stops, which is very + cumbersome). + +Please refer to its ``README`` for more information (it includes an example +command for NumPy). + +.. _pytest-valgrind: https://github.com/seberg/pytest-valgrind + diff --git a/doc/source/dev/development_environment.rst b/doc/source/dev/development_environment.rst index f4c6f3ec75d3..37cf6f7afb50 100644 --- a/doc/source/dev/development_environment.rst +++ b/doc/source/dev/development_environment.rst @@ -3,26 +3,33 @@ Setting up and using your development environment ================================================= +.. _recommended-development-setup: Recommended development setup ----------------------------- Since NumPy contains parts written in C and Cython that need to be compiled before use, make sure you have the necessary compilers and Python -development headers installed - see :ref:`building-from-source`. +development headers installed - see :ref:`building-from-source`. Building +NumPy as of version ``1.17`` requires a C99 compliant compiler. Having compiled code also means that importing NumPy from the development sources needs some additional steps, which are explained below. For the rest of this chapter we assume that you have set up your git repo as described in :ref:`using-git`. +.. _testing-builds: + +Testing builds +-------------- + To build the development version of NumPy and run tests, spawn interactive shells with the Python import paths properly set up etc., do one of:: $ python runtests.py -v $ python runtests.py -v -s random - $ python runtests.py -v -t numpy/core/tests/test_iter.py:test_iter_c_order + $ python runtests.py -v -t numpy/core/tests/test_nditer.py::test_iter_c_order $ python runtests.py --ipython $ python runtests.py --python somescript.py $ python runtests.py --bench @@ -35,13 +42,33 @@ any) found on current PYTHONPATH. When specifying a target using ``-s``, ``-t``, or ``--python``, additional arguments may be forwarded to the target embedded by ``runtests.py`` by passing the extra arguments after a bare ``--``. For example, to run a test method with -the ``--pdb`` flag forwarded to nose, run the following:: +the ``--pdb`` flag forwarded to the target, run the following:: + + $ python runtests.py -t numpy/tests/test_scripts.py::test_f2py -- --pdb + +When using pytest as a target (the default), you can +`match test names using python operators`_ by passing the ``-k`` argument to pytest:: + + $ python runtests.py -v -t numpy/core/tests/test_multiarray.py -- -k "MatMul and not vector" - $ python runtests.py -t numpy/tests/test_scripts.py:test_f2py -- --pdb +.. note:: + + Remember that all tests of NumPy should pass before committing your changes. Using ``runtests.py`` is the recommended approach to running tests. There are also a number of alternatives to it, for example in-place -build or installing to a virtualenv. See the FAQ below for details. +build or installing to a virtualenv or a conda environment. See the FAQ below +for details. + +.. note:: + + Some of the tests in the test suite require a large amount of + memory, and are skipped if your system does not have enough. + + To override the automatic detection of available memory, set the + environment variable ``NPY_AVAILABLE_MEM``, for example + ``NPY_AVAILABLE_MEM=32GB``, or using pytest ``--available-memory=32GB`` + target option. Building in-place @@ -55,7 +82,7 @@ For development, you can set up an in-place build so that changes made to This allows you to import the in-place built NumPy *from the repo base directory only*. If you want the in-place build to be visible outside that base dir, you need to point your ``PYTHONPATH`` environment variable to this -directory. Some IDEs (Spyder for example) have utilities to manage +directory. Some IDEs (`Spyder`_ for example) have utilities to manage ``PYTHONPATH``. On Linux and OSX, you can run the command:: $ export PYTHONPATH=$PWD @@ -74,33 +101,57 @@ installs a ``.egg-link`` file into your site-packages as well as adjusts the ``easy-install.pth`` there, so its a more permanent (and magical) operation. +.. _Spyder: https://www.spyder-ide.org/ + Other build options ------------------- +Build options can be discovered by running any of:: + + $ python setup.py --help + $ python setup.py --help-commands + It's possible to do a parallel build with ``numpy.distutils`` with the ``-j`` option; see :ref:`parallel-builds` for more details. -In order to install the development version of NumPy in ``site-packages``, use -``python setup.py install --user``. - A similar approach to in-place builds and use of ``PYTHONPATH`` but outside the source tree is to use:: - $ python setup.py install --prefix /some/owned/folder + $ pip install . --prefix /some/owned/folder $ export PYTHONPATH=/some/owned/folder/lib/python3.4/site-packages -Using virtualenvs ------------------ +NumPy uses a series of tests to probe the compiler and libc libraries for +functions. The results are stored in ``_numpyconfig.h`` and ``config.h`` files +using ``HAVE_XXX`` definitions. These tests are run during the ``build_src`` +phase of the ``_multiarray_umath`` module in the ``generate_config_h`` and +``generate_numpyconfig_h`` functions. Since the output of these calls includes +many compiler warnings and errors, by default it is run quietly. If you wish +to see this output, you can run the ``build_src`` stage verbosely:: + + $ python build build_src -v + +Using virtual environments +-------------------------- A frequently asked question is "How do I set up a development version of NumPy in parallel to a released version that I use to do my job/research?". One simple way to achieve this is to install the released version in -site-packages, by using a binary installer or pip for example, and set -up the development version in a virtualenv. First install +site-packages, by using pip or conda for example, and set +up the development version in a virtual environment. + +If you use conda, we recommend creating a separate virtual environment for +numpy development using the ``environment.yml`` file in the root of the repo +(this will create the environment and install all development dependencies at +once):: + + $ conda env create -f environment.yml # `mamba` works too for this command + $ conda activate numpy-dev + +If you installed Python some other way than conda, first install `virtualenv`_ (optionally use `virtualenvwrapper`_), then create your -virtualenv (named numpy-dev here) with:: +virtualenv (named ``numpy-dev`` here) with:: $ virtualenv numpy-dev @@ -115,17 +166,19 @@ Running tests Besides using ``runtests.py``, there are various ways to run the tests. Inside the interpreter, tests can be run like this:: - >>> np.test() + >>> np.test() # doctest: +SKIPBLOCK >>> np.test('full') # Also run tests marked as slow >>> np.test('full', verbose=2) # Additionally print test name/file + An example of a successful test : + ``4686 passed, 362 skipped, 9 xfailed, 5 warnings in 213.99 seconds`` + Or a similar way from the command line:: $ python -c "import numpy as np; np.test()" -Tests can also be run with ``nosetests numpy``, however then the NumPy-specific -``nose`` plugin is not found which causes tests marked as ``KnownFailure`` to -be reported as errors. +Tests can also be run with ``pytest numpy``, however then the NumPy-specific +plugin is not found which causes strange side effects Running individual test files can be useful; it's much faster than running the whole test suite or that of a whole module (example: ``np.random.test()``). @@ -137,16 +190,44 @@ That also takes extra arguments, like ``--pdb`` which drops you into the Python debugger when a test fails or an exception is raised. Running tests with `tox`_ is also supported. For example, to build NumPy and -run the test suite with Python 3.4, use:: +run the test suite with Python 3.7, use:: - $ tox -e py34 + $ tox -e py37 -For more extensive info on running and writing tests, see -https://github.com/numpy/numpy/blob/master/doc/TESTS.rst.txt . +For more extensive information, see :ref:`testing-guidelines` -*Note: do not run the tests from the root directory of your numpy git repo, +*Note: do not run the tests from the root directory of your numpy git repo without ``runtests.py``, that will result in strange test errors.* +Running Linting +--------------- +Lint checks can be performed on newly added lines of Python code. + +Install all dependent packages using pip:: + + $ python -m pip install -r linter_requirements.txt + +To run lint checks before committing new code, run:: + + $ python runtests.py --lint uncommitted + +To check all changes in newly added Python code of current branch with target branch, run:: + + $ python runtests.py --lint main + +If there are no errors, the script exits with no message. In case of errors:: + + $ python runtests.py --lint main + ./numpy/core/tests/test_scalarmath.py:34:5: E303 too many blank lines (3) + 1 E303 too many blank lines (3) + +It is advisable to run lint checks before pushing commits to a remote branch +since the linter runs as part of the CI pipeline. + +For more details on Style Guidelines: + + - `Python Style Guide`_ + - `C Style Guide`_ Rebuilding & cleaning the workspace ----------------------------------- @@ -166,14 +247,26 @@ repo, use one of:: $ git reset --hard +.. _debugging: + Debugging --------- Another frequently asked question is "How do I debug C code inside NumPy?". -The easiest way to do this is to first write a Python script that invokes the C -code whose execution you want to debug. For instance ``mytest.py``:: +First, ensure that you have gdb installed on your system with the Python +extensions (often the default on Linux). You can see which version of +Python is running inside gdb to verify your setup:: + + (gdb) python + >import sys + >print(sys.version_info) + >end + sys.version_info(major=3, minor=7, micro=0, releaselevel='final', serial=0) - from numpy import linspace +Next you need to write a Python script that invokes the C code whose execution +you want to debug. For instance ``mytest.py``:: + + import numpy as np x = np.arange(5) np.empty_like(x) @@ -187,10 +280,14 @@ And then in the debugger:: (gdb) run The execution will now stop at the corresponding C function and you can step -through it as usual. With the Python extensions for gdb installed (often the -default on Linux), a number of useful Python-specific commands are available. +through it as usual. A number of useful Python-specific commands are available. For example to see where in the Python code you are, use ``py-list``. For more -details, see `DebuggingWithGdb`_. +details, see `DebuggingWithGdb`_. Here are some commonly used commands: + + - ``list``: List specified function or line. + - ``next``: Step program, proceeding through subroutine calls. + - ``step``: Continue program being debugged, after signal or breakpoint. + - ``print``: Print value of expression EXP. Instead of plain ``gdb`` you can of course use your favourite alternative debugger; run it on the python binary with arguments @@ -202,26 +299,25 @@ typically packaged as ``python-dbg``) is highly recommended. .. _DebuggingWithGdb: https://wiki.python.org/moin/DebuggingWithGdb - .. _tox: https://tox.readthedocs.io/ - .. _virtualenv: http://www.virtualenv.org/ - .. _virtualenvwrapper: http://www.doughellmann.com/projects/virtualenvwrapper/ - .. _Waf: https://code.google.com/p/waf/ +.. _`match test names using python operators`: https://docs.pytest.org/en/latest/usage.html#specifying-tests-selecting-tests +.. _`Python Style Guide`: https://www.python.org/dev/peps/pep-0008/ +.. _`C Style Guide`: https://numpy.org/neps/nep-0045-c_style_guide.html Understanding the code & getting started ---------------------------------------- The best strategy to better understand the code base is to pick something you -want to change and start reading the code to figure out how it works. When in +want to change and start reading the code to figure out how it works. When in doubt, you can ask questions on the mailing list. It is perfectly okay if your -pull requests aren't perfect, the community is always happy to help. As a -volunteer project, things do sometimes get dropped and it's totally fine to +pull requests aren't perfect, the community is always happy to help. As a +volunteer project, things do sometimes get dropped and it's totally fine to ping us if something has sat without a response for about two to four weeks. -So go ahead and pick something that annoys or confuses you about numpy, -experiment with the code, hang around for discussions or go through the -reference documents to try to fix it. Things will fall in place and soon +So go ahead and pick something that annoys or confuses you about NumPy, +experiment with the code, hang around for discussions or go through the +reference documents to try to fix it. Things will fall in place and soon you'll have a pretty good understanding of the project as a whole. Good Luck! diff --git a/doc/source/dev/development_gitpod.rst b/doc/source/dev/development_gitpod.rst new file mode 100644 index 000000000000..92cca81fca69 --- /dev/null +++ b/doc/source/dev/development_gitpod.rst @@ -0,0 +1,271 @@ +.. _development-gitpod: + + +Using Gitpod for NumPy development +======================================================= + +This section of the documentation will guide you through: + +* using GitPod for your NumPy development environment +* creating a personal fork of the NumPy repository on GitHub +* a quick tour of Gitpod and VSCode +* working on the NumPy documentation in Gitpod + +Gitpod +------- + +`Gitpod`_ is an open-source platform for automated and ready-to-code +development environments. It enables developers to describe their dev +environment as code and start instant and fresh development environments for +each new task directly from your browser. This reduces the need to install local +development environments and deal with incompatible dependencies. + +Gitpod GitHub integration +-------------------------- + +To be able to use Gitpod, you will need to have the Gitpod app installed on your +GitHub account, so if +you do not have an account yet, you will need to create one first. + +Head over to the `Gitpod`_ website and click on the **Continue with GitHub** +button. You will be redirected to the GitHub authentication page. +You will then be asked to install the `Gitpod GitHub app <https://github.com/marketplace/gitpod-io>`_. + +Make sure to select **All repositories** access option to avoid issues with +permissions later on. Click on the green **Install** button + +.. image:: ./gitpod-imgs/installing-gitpod-io.png + :alt: Gitpod repository access and installation screenshot + +This will install the necessary hooks for the integration. + +Forking the NumPy repository +----------------------------- + +The best way to work on NumPy as a contributor is by making a fork of the +repository first. + +#. Browse to the `NumPy repository on GitHub`_ and `create your own fork`_. +#. Browse to your fork. Your fork will have a URL like + https://github.com/melissawm/NumPy, except with your GitHub username in place of ``melissawm``. + +Starting Gitpod +---------------- +Once you have authenticated to Gitpod through GitHub, you can install the +`Gitpod browser extension <https://www.gitpod.io/docs/browser-extension>`_ +which will add a **Gitpod** button next to the **Code** button in the +repository: + +.. image:: ./gitpod-imgs/NumPy-github.png + :alt: NumPy repository with Gitpod button screenshot + +#. If you install the extension - you can click the **Gitpod** button to start + a new workspace. + +#. Alternatively, if you do not want to install the browser extension, you can + visit https://gitpod.io/#https://github.com/USERNAME/NumPy replacing + ``USERNAME`` with your GitHub username. + +#. In both cases, this will open a new tab on your web browser and start + building your development environment. Please note this can take a few + minutes. + +#. Once the build is complete, you will be directed to your workspace, + including the VSCode editor and all the dependencies you need to work on + NumPy. The first time you start your workspace, you will notice that there + might be some actions running. This will ensure that you have a development + version of NumPy installed and that the docs are being pre-built for you. + +#. When your workspace is ready, you can :ref:`test the build<testing-builds>` by + entering:: + + $ python runtests.py -v + +``runtests.py`` is another script in the NumPy root directory. It runs a suite +of tests that make sure NumPy is working as it should, and ``-v`` activates the +``--verbose`` option to show all the test output. + +Quick workspace tour +--------------------- +Gitpod uses VSCode as the editor. If you have not used this editor before, you +can check the Getting started `VSCode docs`_ to familiarize yourself with it. + +Your workspace will look similar to the image below: + +.. image:: ./gitpod-imgs/gitpod-workspace.png + :alt: Gitpod workspace screenshot + +.. note:: By default, VSCode initializes with a light theme. You can change to + a dark theme by with the keyboard shortcut :kbd:`Cmd-K Cmd-T` in Mac or + :kbd:`Ctrl-K Ctrl-T` in Linux and Windows. + +We have marked some important sections in the editor: + +#. Your current Python interpreter - by default, this is ``numpy-dev`` and + should be displayed in the status bar and on your terminal. You do not need + to activate the conda environment as this will always be activated for you. +#. Your current branch is always displayed in the status bar. You can also use + this button to change or create branches. +#. GitHub Pull Requests extension - you can use this to work with Pull Requests + from your workspace. +#. Marketplace extensions - we have added some essential extensions to the NumPy + Gitpod. Still, you can also install other extensions or syntax highlighting + themes for your user, and these will be preserved for you. +#. Your workspace directory - by default, it is ``/workspace/numpy``. **Do not + change this** as this is the only directory preserved in Gitpod. + +We have also pre-installed a few tools and VSCode extensions to help with the +development experience: + +* `GitHub CLI <https://cli.github.com/>`_ +* `VSCode rst extension <https://marketplace.visualstudio.com/items?itemName=lextudio.restructuredtext>`_ +* `VSCode Live server extension <https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer>`_ +* `VSCode Gitlens extension <https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens>`_ +* `VSCode autodocstrings extension <https://marketplace.visualstudio.com/items?itemName=njpwerner.autodocstring>`_ +* `VSCode Git Graph extension <https://marketplace.visualstudio.com/items?itemName=mhutchie.git-graph>`_ + +Development workflow with Gitpod +--------------------------------- +The :ref:`development-workflow` section of this documentation contains +information regarding the NumPy development workflow. Make sure to check this +before working on your contributions. + +When using Gitpod, git is pre configured for you: + +#. You do not need to configure your git username, and email as this should be + done for you as you authenticated through GitHub. You can check the git + configuration with the command ``git config --list`` in your terminal. +#. As you started your workspace from your own NumPy fork, you will by default + have both ``upstream`` and ``origin`` added as remotes. You can verify this by + typing ``git remote`` on your terminal or by clicking on the **branch name** + on the status bar (see image below). + + .. image:: ./gitpod-imgs/NumPy-gitpod-branches.png + :alt: Gitpod workspace branches plugin screenshot + +Rendering the NumPy documentation +---------------------------------- +You can find the detailed documentation on how rendering the documentation with +Sphinx works in the :ref:`howto-build-docs` section. + +The documentation is pre-built during your workspace initialization. So once +this task is completed, you have two main options to render the documentation +in Gitpod. + +Option 1: Using Liveserve +*************************** + +#. View the documentation in ``NumPy/doc/build/html``. You can start with + ``index.html`` and browse, or you can jump straight to the file you're + interested in. +#. To see the rendered version of a page, you can right-click on the ``.html`` + file and click on **Open with Live Serve**. Alternatively, you can open the + file in the editor and click on the **Go live** button on the status bar. + + .. image:: ./gitpod-imgs/vscode-statusbar.png + :alt: Gitpod workspace VSCode start live serve screenshot + +#. A simple browser will open to the right-hand side of the editor. We recommend + closing it and click on the **Open in browser** button in the pop-up. +#. To stop the server click on the **Port: 5500** button on the status bar. + +Option 2: Using the rst extension +*********************************** + +A quick and easy way to see live changes in a ``.rst`` file as you work on it +uses the rst extension with docutils. + +.. note:: This will generate a simple live preview of the document without the + ``html`` theme, and some backlinks might not be added correctly. But it is an + easy and lightweight way to get instant feedback on your work. + +#. Open any of the source documentation files located in ``doc/source`` in the + editor. +#. Open VSCode Command Palette with :kbd:`Cmd-Shift-P` in Mac or + :kbd:`Ctrl-Shift-P` in Linux and Windows. Start typing "restructured" + and choose either "Open preview" or "Open preview to the Side". + + .. image:: ./gitpod-imgs/vscode-rst.png + :alt: Gitpod workspace VSCode open rst screenshot + +#. As you work on the document, you will see a live rendering of it on the editor. + + .. image:: ./gitpod-imgs/rst-rendering.png + :alt: Gitpod workspace VSCode rst rendering screenshot + +If you want to see the final output with the ``html`` theme you will need to +rebuild the docs with ``make html`` and use Live Serve as described in option 1. + +FAQ's and troubleshooting +------------------------- + +How long is my Gitpod workspace kept for? +***************************************** + +Your stopped workspace will be kept for 14 days and deleted afterwards if you do +not use them. + +Can I come back to a previous workspace? +***************************************** + +Yes, let's say you stepped away for a while and you want to carry on working on +your NumPy contributions. You need to visit https://gitpod.io/workspaces and +click on the workspace you want to spin up again. All your changes will be there +as you last left them. + +Can I install additional VSCode extensions? +******************************************* + +Absolutely! Any extensions you installed will be installed in your own workspace +and preserved. + +I registered on Gitpod but I still cannot see a ``Gitpod`` button in my repositories. +************************************************************************************* + +Head to https://gitpod.io/integrations and make sure you are logged in. +Hover over GitHub and click on the three buttons that appear on the right. +Click on edit permissions and make sure you have ``user:email``, +``read:user``, and ``public_repo`` checked. Click on **Update Permissions** +and confirm the changes in the GitHub application page. + +.. image:: ./gitpod-imgs/gitpod-edit-permissions-gh.png + :alt: Gitpod integrations - edit GH permissions screenshot + +How long does my workspace stay active if I'm not using it? +*********************************************************** + +If you keep your workspace open in a browser tab but don't interact with it, +it will shut down after 30 minutes. If you close the browser tab, it will +shut down after 3 minutes. + +My terminal is blank - there is no cursor and it's completely unresponsive +************************************************************************** + +Unfortunately this is a known-issue on Gitpod's side. You can sort this +issue in two ways: + +#. Create a new Gitpod workspace altogether. +#. Head to your `Gitpod dashboard <https://gitpod.io/workspaces>`_ and locate + the running workspace. Hover on it and click on the **three dots menu** + and then click on **Stop**. When the workspace is completely stopped you + can click on its name to restart it again. + +.. image:: ./gitpod-imgs/gitpod-dashboard-stop.png + :alt: Gitpod dashboard and workspace menu screenshot + +I authenticated through GitHub but I still cannot commit to the repository through Gitpod. +****************************************************************************************** + +Head to https://gitpod.io/integrations and make sure you are logged in. +Hover over GitHub and click on the three buttons that appear on the right. +Click on edit permissions and make sure you have ``public_repo`` checked. +Click on **Update Permissions** and confirm the changes in the +GitHub application page. + +.. image:: ./gitpod-imgs/gitpod-edit-permissions-repo.png + :alt: Gitpod integrations - edit GH repository permissions screenshot + +.. _Gitpod: https://www.gitpod.io/ +.. _NumPy repository on GitHub: https://github.com/NumPy/NumPy +.. _create your own fork: https://help.github.com/en/articles/fork-a-repo +.. _VSCode docs: https://code.visualstudio.com/docs/getstarted/tips-and-tricks diff --git a/doc/source/dev/gitwash/development_workflow.rst b/doc/source/dev/development_workflow.rst similarity index 84% rename from doc/source/dev/gitwash/development_workflow.rst rename to doc/source/dev/development_workflow.rst index c6884a7cfb37..8c56f6fb2cbc 100644 --- a/doc/source/dev/gitwash/development_workflow.rst +++ b/doc/source/dev/development_workflow.rst @@ -26,9 +26,9 @@ In short: - *Contributors*: push your feature branch to your own Github repo, and :ref:`create a pull request <asking-for-merging>`. - - *Core developers* If you want to push changes without + - *Core developers*: If you want to push changes without further review, see the notes :ref:`below <pushing-to-main>`. - + This way of working helps to keep work well organized and the history as clear as possible. @@ -49,10 +49,10 @@ First, fetch new commits from the ``upstream`` repository: git fetch upstream -Then, create a new branch based on the master branch of the upstream +Then, create a new branch based on the main branch of the upstream repository:: - git checkout -b my-new-feature upstream/master + git checkout -b my-new-feature upstream/main .. _editing-workflow: @@ -69,7 +69,7 @@ Overview git status # Optional git diff # Optional git add modified_file - git commit + git commit # push the branch to your own Github repo git push origin my-new-feature @@ -112,42 +112,42 @@ In more detail properly formatted and sufficiently detailed commit message. After saving your message and closing the editor, your commit will be saved. For trivial commits, a short commit message can be passed in through the command line - using the ``-m`` flag. For example, ``git commit -am "ENH: Some message"``. - + using the ``-m`` flag. For example, ``git commit -am "ENH: Some message"``. + In some cases, you will see this form of the commit command: ``git commit -a``. The extra ``-a`` flag automatically commits all modified files and removes all deleted files. This can save you some typing of numerous ``git add`` commands; however, it can add unwanted changes to a commit if you're not careful. For more information, see `why the -a flag?`_ - and the - helpful use-case description in the `tangled working copy problem`_. + helpful use-case description in the `tangled working copy problem`_. #. Push the changes to your forked repo on github_:: git push origin my-new-feature For more information, see `git push`_. - + .. note:: - + Assuming you have followed the instructions in these pages, git will create a default link to your github_ repo called ``origin``. In git >= 1.7 you can ensure that the link to origin is permanently set by using the ``--set-upstream`` option:: - + git push --set-upstream origin my-new-feature - + From now on git_ will know that ``my-new-feature`` is related to the ``my-new-feature`` branch in your own github_ repo. Subsequent push calls are then simplified to the following:: git push - + You have to use ``--set-upstream`` for each new branch that you create. - + It may be the case that while you were working on your edits, new commits have been added to ``upstream`` that affect your work. In this case, follow the -:ref:`rebasing-on-master` section of this document to apply those changes to +:ref:`rebasing-on-main` section of this document to apply those changes to your branch. .. _writing-the-commit-message: @@ -188,23 +188,48 @@ Standard acronyms to start the commit message with are:: REL: related to releasing numpy +.. _workflow_mailing_list: + +Get the mailing list's opinion +======================================================= + +If you plan a new feature or API change, it's wisest to first email the +NumPy `mailing list <https://mail.python.org/mailman/listinfo/numpy-discussion>`_ +asking for comment. If you haven't heard back in a week, it's +OK to ping the list again. + .. _asking-for-merging: Asking for your changes to be merged with the main repo ======================================================= When you feel your work is finished, you can create a pull request (PR). Github -has a nice help page that outlines the process for `filing pull requests`_. +has a nice help page that outlines the process for `filing pull requests`_. If your changes involve modifications to the API or addition/modification of a -function, you should initiate a code review. This involves sending an email to -the `NumPy mailing list`_ with a link to your PR along with a description of -and a motivation for your changes. +function, add a release note to the ``doc/release/upcoming_changes/`` +directory, following the instructions and format in the +``doc/release/upcoming_changes/README.rst`` file. + + +.. _workflow_PR_timeline: + +Getting your PR reviewed +======================== -.. _rebasing-on-master: +We review pull requests as soon as we can, typically within a week. If you get +no review comments within two weeks, feel free to ask for feedback by +adding a comment on your PR (this will notify maintainers). -Rebasing on master -================== +If your PR is large or complicated, asking for input on the numpy-discussion +mailing list may also be useful. + + + +.. _rebasing-on-main: + +Rebasing on main +================ This updates your feature branch with changes from the upstream `NumPy github`_ repo. If you do not absolutely need to do this, try to avoid doing @@ -219,8 +244,8 @@ Next, you need to update the feature branch:: git checkout my-new-feature # make a backup in case you mess up git branch tmp my-new-feature - # rebase on upstream master branch - git rebase upstream/master + # rebase on upstream main branch + git rebase upstream/main If you have made changes to files that have changed also upstream, this may generate merge conflicts that you need to resolve. See @@ -233,7 +258,7 @@ Finally, remove the backup branch upon a successful rebase:: .. note:: - Rebasing on master is preferred over merging upstream back to your + Rebasing on main is preferred over merging upstream back to your branch. Using ``git merge`` and ``git pull`` is discouraged when working on feature branches. @@ -284,7 +309,7 @@ Rewriting commit history Do this only for your own feature branches. -There's an embarrassing typo in a commit you made? Or perhaps the you +There's an embarrassing typo in a commit you made? Or perhaps you made several false starts you would like the posterity not to see. This can be done via *interactive rebasing*. @@ -297,10 +322,10 @@ Suppose that the commit history looks like this:: 2dec1ac Fix a few bugs + disable 13d7934 First implementation 6ad92e5 * masked is now an instance of a new object, MaskedConstant - 29001ed Add pre-nep for a copule of structured_array_extensions. + 29001ed Add pre-nep for a couple of structured_array_extensions. ... -and ``6ad92e5`` is the last commit in the ``master`` branch. Suppose we +and ``6ad92e5`` is the last commit in the ``main`` branch. Suppose we want to make the following changes: * Rewrite the commit message for ``13d7934`` to something more sensible. @@ -367,14 +392,14 @@ Deleting a branch on github_ :: - git checkout master + git checkout main # delete branch locally git branch -D my-unwanted-branch # delete branch on github - git push origin :my-unwanted-branch + git push origin --delete my-unwanted-branch -(Note the colon ``:`` before ``test-branch``. See also: -http://github.com/guides/remove-a-remote-branch +See also: +https://stackoverflow.com/questions/2003505/how-do-i-delete-a-git-branch-locally-and-remotely Several people sharing a single repository @@ -387,7 +412,7 @@ share it via github_. First fork NumPy into your account, as from :ref:`forking`. Then, go to your forked repository github page, say -``http://github.com/your-user-name/numpy`` +``https://github.com/your-user-name/numpy`` Click on the 'Admin' button, and add anyone else to the repo as a collaborator: @@ -426,25 +451,25 @@ Backporting =========== Backporting is the process of copying new feature/fixes committed in -`numpy/master`_ back to stable release branches. To do this you make a branch +`numpy/main`_ back to stable release branches. To do this you make a branch off the branch you are backporting to, cherry pick the commits you want from -``numpy/master``, and then submit a pull request for the branch containing the +``numpy/main``, and then submit a pull request for the branch containing the backport. 1. First, you need to make the branch you will work on. This needs to be - based on the older version of NumPy (not master):: + based on the older version of NumPy (not main):: # Make a new branch based on numpy/maintenance/1.8.x, # backport-3324 is our new name for the branch. git checkout -b backport-3324 upstream/maintenance/1.8.x -2. Now you need to apply the changes from master to this branch using +2. Now you need to apply the changes from main to this branch using `git cherry-pick`_:: # Update remote git fetch upstream # Check the commit log for commits to cherry pick - git log upstream/master + git log upstream/main # This pull request included commits aa7a047 to c098283 (inclusive) # so you use the .. syntax (for a range of commits), the ^ makes the # range inclusive. @@ -455,7 +480,7 @@ backport. 3. You might run into some conflicts cherry picking here. These are resolved the same way as merge/rebase conflicts. Except here you can - use `git blame`_ to see the difference between master and the + use `git blame`_ to see the difference between main and the backported branch to make sure nothing gets screwed up. 4. Push the new branch to your Github repository:: @@ -463,18 +488,18 @@ backport. git push -u origin backport-3324 5. Finally make a pull request using Github. Make sure it is against the - maintenance branch and not master, Github will usually suggest you - make the pull request against master. + maintenance branch and not main, Github will usually suggest you + make the pull request against main. .. _pushing-to-main: Pushing changes to the main repo ================================ -*This is only relevant if you have commit rights to the main NumPy repo.* +*Requires commit rights to the main NumPy repo.* When you have a set of "ready" changes in a feature branch ready for -NumPy's ``master`` or ``maintenance`` branches, you can push +NumPy's ``main`` or ``maintenance`` branches, you can push them to ``upstream`` as follows: 1. First, merge or rebase on the target branch. @@ -482,29 +507,29 @@ them to ``upstream`` as follows: a) Only a few, unrelated commits then prefer rebasing:: git fetch upstream - git rebase upstream/master + git rebase upstream/main - See :ref:`rebasing-on-master`. + See :ref:`rebasing-on-main`. b) If all of the commits are related, create a merge commit:: git fetch upstream - git merge --no-ff upstream/master + git merge --no-ff upstream/main 2. Check that what you are going to push looks sensible:: - git log -p upstream/master.. + git log -p upstream/main.. git log --oneline --graph 3. Push to upstream:: - git push upstream my-feature-branch:master + git push upstream my-feature-branch:main -.. note:: +.. note:: It's usually a good idea to use the ``-n`` flag to ``git push`` to check first that you're about to push the changes you want to the place you want. -.. include:: git_links.inc +.. include:: gitwash/git_links.inc diff --git a/doc/source/dev/examples/.doxyfile b/doc/source/dev/examples/.doxyfile new file mode 100644 index 000000000000..966c1b636f81 --- /dev/null +++ b/doc/source/dev/examples/.doxyfile @@ -0,0 +1,2 @@ +INPUT += @CUR_DIR +INCLUDE_PATH += @CUR_DIR diff --git a/doc/source/dev/examples/doxy_class.hpp b/doc/source/dev/examples/doxy_class.hpp new file mode 100644 index 000000000000..ceba63487867 --- /dev/null +++ b/doc/source/dev/examples/doxy_class.hpp @@ -0,0 +1,21 @@ +/** + * Template to represent limbo numbers. + * + * Specializations for integer types that are part of nowhere. + * It doesn't support with any real types. + * + * @param Tp Type of the integer. Required to be an integer type. + * @param N Number of elements. +*/ +template<typename Tp, std::size_t N> +class DoxyLimbo { + public: + /// Default constructor. Initialize nothing. + DoxyLimbo(); + /// Set Default behavior for copy the limbo. + DoxyLimbo(const DoxyLimbo<Tp, N> &l); + /// Returns the raw data for the limbo. + const Tp *data(); + protected: + Tp p_data[N]; ///< Example for inline comment. +}; diff --git a/doc/source/dev/examples/doxy_func.h b/doc/source/dev/examples/doxy_func.h new file mode 100644 index 000000000000..792a9d1b7696 --- /dev/null +++ b/doc/source/dev/examples/doxy_func.h @@ -0,0 +1,11 @@ +/** + * This a simple brief. + * + * And the details goes here. + * Multi lines are welcome. + * + * @param num leave a comment for parameter num. + * @param str leave a comment for the second parameter. + * @return leave a comment for the returned value. + */ +int doxy_javadoc_example(int num, const char *str); diff --git a/doc/source/dev/examples/doxy_rst.h b/doc/source/dev/examples/doxy_rst.h new file mode 100644 index 000000000000..6ab4a07758ab --- /dev/null +++ b/doc/source/dev/examples/doxy_rst.h @@ -0,0 +1,15 @@ +/** + * A comment block contains reST markup. + * @rst + * .. note:: + * + * Thanks to Breathe_, we were able to bring it to Doxygen_ + * + * Some code example:: + * + * int example(int x) { + * return x * 2; + * } + * @endrst + */ +void doxy_reST_example(void); diff --git a/doc/source/dev/gitpod-imgs/NumPy-github.png b/doc/source/dev/gitpod-imgs/NumPy-github.png new file mode 100644 index 000000000000..010b0fc5ea33 Binary files /dev/null and b/doc/source/dev/gitpod-imgs/NumPy-github.png differ diff --git a/doc/source/dev/gitpod-imgs/NumPy-gitpod-branches.png b/doc/source/dev/gitpod-imgs/NumPy-gitpod-branches.png new file mode 100644 index 000000000000..3ee6c5f2022f Binary files /dev/null and b/doc/source/dev/gitpod-imgs/NumPy-gitpod-branches.png differ diff --git a/doc/source/dev/gitpod-imgs/gitpod-dashboard-stop.png b/doc/source/dev/gitpod-imgs/gitpod-dashboard-stop.png new file mode 100644 index 000000000000..40f137745941 Binary files /dev/null and b/doc/source/dev/gitpod-imgs/gitpod-dashboard-stop.png differ diff --git a/doc/source/dev/gitpod-imgs/gitpod-edit-permissions-gh.png b/doc/source/dev/gitpod-imgs/gitpod-edit-permissions-gh.png new file mode 100644 index 000000000000..8955e907a96d Binary files /dev/null and b/doc/source/dev/gitpod-imgs/gitpod-edit-permissions-gh.png differ diff --git a/doc/source/dev/gitpod-imgs/gitpod-edit-permissions-repo.png b/doc/source/dev/gitpod-imgs/gitpod-edit-permissions-repo.png new file mode 100644 index 000000000000..8bfaff81cfb6 Binary files /dev/null and b/doc/source/dev/gitpod-imgs/gitpod-edit-permissions-repo.png differ diff --git a/doc/source/dev/gitpod-imgs/gitpod-workspace.png b/doc/source/dev/gitpod-imgs/gitpod-workspace.png new file mode 100644 index 000000000000..a65c9bd7e152 Binary files /dev/null and b/doc/source/dev/gitpod-imgs/gitpod-workspace.png differ diff --git a/doc/source/dev/gitpod-imgs/installing-gitpod-io.png b/doc/source/dev/gitpod-imgs/installing-gitpod-io.png new file mode 100644 index 000000000000..97319a7293ce Binary files /dev/null and b/doc/source/dev/gitpod-imgs/installing-gitpod-io.png differ diff --git a/doc/source/dev/gitpod-imgs/rst-rendering.png b/doc/source/dev/gitpod-imgs/rst-rendering.png new file mode 100644 index 000000000000..41cc305f3a33 Binary files /dev/null and b/doc/source/dev/gitpod-imgs/rst-rendering.png differ diff --git a/doc/source/dev/gitpod-imgs/vscode-rst.png b/doc/source/dev/gitpod-imgs/vscode-rst.png new file mode 100644 index 000000000000..5b574c115a2b Binary files /dev/null and b/doc/source/dev/gitpod-imgs/vscode-rst.png differ diff --git a/doc/source/dev/gitpod-imgs/vscode-statusbar.png b/doc/source/dev/gitpod-imgs/vscode-statusbar.png new file mode 100644 index 000000000000..3febbcee0ee5 Binary files /dev/null and b/doc/source/dev/gitpod-imgs/vscode-statusbar.png differ diff --git a/doc/source/dev/gitwash/development_setup.rst b/doc/source/dev/gitwash/development_setup.rst index 1ebd4b486021..2be7125da032 100644 --- a/doc/source/dev/gitwash/development_setup.rst +++ b/doc/source/dev/gitwash/development_setup.rst @@ -1,143 +1,178 @@ -==================================== -Getting started with Git development -==================================== +.. _development-setup: -This section and the next describe in detail how to set up git for working -with the NumPy source code. If you have git already set up, skip to -:ref:`development-workflow`. +############################################################################## +Setting up git for NumPy development +############################################################################## -Basic Git setup -############### +To contribute code or documentation, you first need -* :ref:`install-git`. -* Introduce yourself to Git:: +#. git installed on your machine +#. a GitHub account +#. a fork of NumPy - git config --global user.email you@yourdomain.example.com - git config --global user.name "Your Name Comes Here" -.. _forking: +****************************************************************************** +Install git +****************************************************************************** -Making your own copy (fork) of NumPy -#################################### +You may already have git; check by typing ``git --version``. If it's +installed you'll see some variation of ``git version 2.11.0``. +If instead you see ``command is not recognized``, ``command not +found``, etc., +`install git <https://git-scm.com/book/en/v2/Getting-Started-Installing-Git>`_. -You need to do this only once. The instructions here are very similar -to the instructions at http://help.github.com/forking/ - please see that -page for more detail. We're repeating some of it here just to give the -specifics for the NumPy_ project, and to suggest some default names. +Then set your name and email: :: -Set up and configure a github_ account -====================================== + git config --global user.email you@yourdomain.example.com + git config --global user.name "Your Name" -If you don't have a github_ account, go to the github_ page, and make one. +.. _set-up-and-configure-a-github-account: -You then need to configure your account to allow write access - see the -``Generating SSH keys`` help on `github help`_. +****************************************************************************** +Create a GitHub account +****************************************************************************** -Create your own forked copy of NumPy_ -========================================= +If you don't have a GitHub account, visit https://github.com/join to create +one. -#. Log into your github_ account. -#. Go to the NumPy_ github home at `NumPy github`_. -#. Click on the *fork* button: +.. _forking: - .. image:: forking_button.png +****************************************************************************** +Create a NumPy fork +****************************************************************************** - After a short pause, you should find yourself at the home page for - your own forked copy of NumPy_. +``Forking`` has two steps -- visit GitHub to create a fork repo in your +account, then make a copy of it on your own machine. -.. include:: git_links.inc +Create the fork repo +============================================================================== +#. Log into your GitHub account. +#. Go to the `NumPy GitHub home <https://github.com/numpy/numpy>`_. +#. At the upper right of the page, click ``Fork``: -.. _set-up-fork: + .. image:: forking_button.png -Set up your fork -################ + You'll see -First you follow the instructions for :ref:`forking`. + .. image:: forking_message.png -Overview -======== + and then you'll be taken to the home page of your forked copy: -:: + .. image:: forked_page.png - git clone https://github.com/your-user-name/numpy.git - cd numpy - git remote add upstream https://github.com/numpy/numpy.git -In detail -========= +.. _set-up-fork: + +Make the local copy +============================================================================== + +#. In the directory where you want the copy created, run :: -Clone your fork ---------------- + git clone https://github.com/your-user-name/numpy.git -#. Clone your fork to the local computer with ``git clone - https://github.com/your-user-name/numpy.git`` -#. Investigate. Change directory to your new repo: ``cd numpy``. Then - ``git branch -a`` to show you all branches. You'll get something - like:: + You'll see something like: :: - * master - remotes/origin/master + $ git clone https://github.com/your-user-name/numpy.git + Cloning into 'numpy'... + remote: Enumerating objects: 12, done. + remote: Counting objects: 100% (12/12), done. + remote: Compressing objects: 100% (12/12), done. + remote: Total 175837 (delta 0), reused 0 (delta 0), pack-reused 175825 + Receiving objects: 100% (175837/175837), 78.16 MiB | 9.87 MiB/s, done. + Resolving deltas: 100% (139317/139317), done. - This tells you that you are currently on the ``master`` branch, and - that you also have a ``remote`` connection to ``origin/master``. - What remote repository is ``remote/origin``? Try ``git remote -v`` to - see the URLs for the remote. They will point to your github_ fork. + A directory ``numpy`` is created on your machine. (If you already have + a numpy directory, GitHub will choose a different name like ``numpy-1``.) + :: - Now you want to connect to the upstream `NumPy github`_ repository, so - you can merge in changes from trunk. + $ ls -l + total 0 + drwxrwxrwx 1 bjn bjn 4096 Jun 20 07:20 numpy .. _linking-to-upstream: -Linking your repository to the upstream repo --------------------------------------------- +#. Give the name ``upstream`` to the main NumPy repo: :: + + cd numpy + git remote add upstream https://github.com/numpy/numpy.git + +#. Set up your repository so ``git pull`` pulls from ``upstream`` by + default: :: + + git config branch.main.remote upstream + git config branch.main.merge refs/heads/main -:: +****************************************************************************** +Look it over +****************************************************************************** - cd numpy - git remote add upstream https://github.com/numpy/numpy.git +#. The branches shown by ``git branch -a`` will include -``upstream`` here is just the arbitrary name we're using to refer to the -main NumPy_ repository at `NumPy github`_. + - the ``main`` branch you just cloned on your own machine + - the ``main`` branch from your fork on GitHub, which git named + ``origin`` by default + - the ``main`` branch on the the main NumPy repo, which you named + ``upstream``. -Just for your own satisfaction, show yourself that you now have a new -'remote', with ``git remote -v show``, giving you something like:: + :: - upstream https://github.com/numpy/numpy.git (fetch) - upstream https://github.com/numpy/numpy.git (push) - origin https://github.com/your-user-name/numpy.git (fetch) - origin https://github.com/your-user-name/numpy.git (push) + main + remotes/origin/main + remotes/upstream/main -To keep in sync with changes in NumPy, you want to set up your repository -so it pulls from ``upstream`` by default. This can be done with:: + If ``upstream`` isn't there, it will be added after you access the + NumPy repo with a command like ``git fetch`` or ``git pull``. - git config branch.master.remote upstream - git config branch.master.merge refs/heads/master -You may also want to have easy access to all pull requests sent to the -NumPy repository:: +#. The repos shown by ``git remote -v show`` will include your fork on GitHub + and the main repo: :: - git config --add remote.upstream.fetch '+refs/pull/*/head:refs/remotes/upstream/pr/*' + upstream https://github.com/numpy/numpy.git (fetch) + upstream https://github.com/numpy/numpy.git (push) + origin https://github.com/your-user-name/numpy.git (fetch) + origin https://github.com/your-user-name/numpy.git (push) -Your config file should now look something like (from -``$ cat .git/config``):: +#. ``git config --list`` will include :: - [core] - repositoryformatversion = 0 - filemode = true - bare = false - logallrefupdates = true - ignorecase = true - precomposeunicode = false - [remote "origin"] - url = https://github.com/your-user-name/numpy.git - fetch = +refs/heads/*:refs/remotes/origin/* - [remote "upstream"] - url = https://github.com/numpy/numpy.git - fetch = +refs/heads/*:refs/remotes/upstream/* - fetch = +refs/pull/*/head:refs/remotes/upstream/pr/* - [branch "master"] - remote = upstream - merge = refs/heads/master + user.email=your_email@example.com + user.name=Your Name + remote.origin.url=git@github.com:your-github-id/numpy.git + remote.origin.fetch=+refs/heads/*:refs/remotes/origin/* + branch.main.remote=upstream + branch.main.merge=refs/heads/main + remote.upstream.url=https://github.com/numpy/numpy.git + remote.upstream.fetch=+refs/heads/*:refs/remotes/upstream/* .. include:: git_links.inc + + +****************************************************************************** +Optional: set up SSH keys to avoid passwords +****************************************************************************** + +Cloning your NumPy fork repo required no password, because it read the remote +repo without changing it. Later, though, submitting your pull requests will +write to it, and GitHub will ask for your username and password -- even though +it's your own repo. You can eliminate this authentication without compromising +security by `setting up SSH keys \ +<https://help.github.com/en/github/authenticating-to-github/connecting-to-github-with-ssh>`_. + +**If you set up the keys before cloning**, the instructions above change +slightly. Instead of :: + + git clone https://github.com/your-user-name/numpy.git + +run :: + + git clone git@github.com:your-user-name/numpy.git + +and instead of showing an ``https`` URL, ``git remote -v`` will show :: + + origin git@github.com:your-user-name/numpy.git (fetch) + origin git@github.com:your-user-name/numpy.git (push) + + +**If you have cloned already** and want to start using SSH, see +`Switching remote URLs from HTTPS to SSH \ +<https://help.github.com/en/github/using-git/changing-a-remotes-url#switching-remote-urls-from-https-to-ssh>`_. diff --git a/doc/source/dev/gitwash/dot2_dot3.rst b/doc/source/dev/gitwash/dot2_dot3.rst index 7759e2e60d68..30852b5ad387 100644 --- a/doc/source/dev/gitwash/dot2_dot3.rst +++ b/doc/source/dev/gitwash/dot2_dot3.rst @@ -7,22 +7,22 @@ Thanks to Yarik Halchenko for this explanation. Imagine a series of commits A, B, C, D... Imagine that there are two -branches, *topic* and *master*. You branched *topic* off *master* when -*master* was at commit 'E'. The graph of the commits looks like this:: +branches, *topic* and *main*. You branched *topic* off *main* when +*main* was at commit 'E'. The graph of the commits looks like this:: A---B---C topic / - D---E---F---G master + D---E---F---G main Then:: - git diff master..topic + git diff main..topic will output the difference from G to C (i.e. with effects of F and G), while:: - git diff master...topic + git diff main...topic would output just differences in the topic branch (i.e. only A, B, and C). diff --git a/doc/source/dev/gitwash/following_latest.rst b/doc/source/dev/gitwash/following_latest.rst index ad497bf9a418..0e98b4ec41d6 100644 --- a/doc/source/dev/gitwash/following_latest.rst +++ b/doc/source/dev/gitwash/following_latest.rst @@ -1,9 +1,5 @@ .. _following-latest: -============================= - Following the latest source -============================= - These are the instructions if you just want to follow the latest *NumPy* source, but you don't need to do any development for now. If you do want to contribute a patch (excellent!) or do more extensive diff --git a/doc/source/dev/gitwash/forked_page.png b/doc/source/dev/gitwash/forked_page.png new file mode 100644 index 000000000000..f369cab3afc6 Binary files /dev/null and b/doc/source/dev/gitwash/forked_page.png differ diff --git a/doc/source/dev/gitwash/forking_button.png b/doc/source/dev/gitwash/forking_button.png old mode 100644 new mode 100755 index d0e04134d4d0..9750c0947bb1 Binary files a/doc/source/dev/gitwash/forking_button.png and b/doc/source/dev/gitwash/forking_button.png differ diff --git a/doc/source/dev/gitwash/forking_message.png b/doc/source/dev/gitwash/forking_message.png new file mode 100644 index 000000000000..63129601737a Binary files /dev/null and b/doc/source/dev/gitwash/forking_message.png differ diff --git a/doc/source/dev/gitwash/git_development.rst b/doc/source/dev/gitwash/git_development.rst deleted file mode 100644 index 5d7d47f89615..000000000000 --- a/doc/source/dev/gitwash/git_development.rst +++ /dev/null @@ -1,14 +0,0 @@ -.. _git-development: - -===================== - Git for development -===================== - -Contents: - -.. toctree:: - :maxdepth: 2 - - development_setup - configure_git - dot2_dot3 diff --git a/doc/source/dev/gitwash/git_intro.rst b/doc/source/dev/gitwash/git_intro.rst index 3ce322f8fd35..9d596d4d4610 100644 --- a/doc/source/dev/gitwash/git_intro.rst +++ b/doc/source/dev/gitwash/git_intro.rst @@ -1,42 +1,8 @@ -============ -Introduction -============ - -These pages describe a git_ and github_ workflow for the NumPy_ -project. - -There are several different workflows here, for different ways of -working with *NumPy*. - -This is not a comprehensive git_ reference, it's just a workflow for our -own project. It's tailored to the github_ hosting service. You may well -find better or quicker ways of getting stuff done with git_, but these -should get you started. - -For general resources for learning git_ see :ref:`git-resources`. - -.. _install-git: - Install git =========== -Overview --------- - -================ ============= -Debian / Ubuntu ``sudo apt-get install git-core`` -Fedora ``sudo yum install git-core`` -Windows Download and install msysGit_ -OS X Use the git-osx-installer_ -================ ============= - -In detail ---------- - -See the git_ page for the most recent information. - -Have a look at the github_ install help pages available from `github help`_ - -There are good instructions here: http://book.git-scm.com/2_installing_git.html +Developing with git can be done entirely without github. Git is a distributed +version control system. In order to use git on your machine you must `install +it`_. .. include:: git_links.inc diff --git a/doc/source/dev/gitwash/git_links.inc b/doc/source/dev/gitwash/git_links.inc index 30532da99b43..8126cf9ac6cf 100644 --- a/doc/source/dev/gitwash/git_links.inc +++ b/doc/source/dev/gitwash/git_links.inc @@ -2,64 +2,63 @@ and name substitutions. It may be included in many files, therefore it should only contain link targets and name substitutions. Try grepping for "^\.\. _" to find plausible - candidates for this list. + candidates for this list. .. NOTE: reST targets are __not_case_sensitive__, so only one target definition is needed for nipy, NIPY, Nipy, etc... .. git stuff -.. _git: http://git-scm.com/ -.. _github: http://github.com -.. _github help: http://help.github.com -.. _msysgit: http://code.google.com/p/msysgit/downloads/list -.. _git-osx-installer: http://code.google.com/p/git-osx-installer/downloads/list +.. _git: https://git-scm.com/ +.. _github: https://github.com/numpy/numpy +.. _github help: https://help.github.com +.. _`install it`: https://git-scm.com/downloads .. _subversion: http://subversion.tigris.org/ -.. _git cheat sheet: http://github.com/guides/git-cheat-sheet -.. _pro git book: http://progit.org/ -.. _git svn crash course: http://git-scm.com/course/svn.html -.. _learn.github: http://learn.github.com/ -.. _network graph visualizer: http://github.com/blog/39-say-hello-to-the-network-graph-visualizer -.. _git user manual: http://www.kernel.org/pub/software/scm/git/docs/user-manual.html -.. _git tutorial: http://www.kernel.org/pub/software/scm/git/docs/gittutorial.html -.. _git community book: http://book.git-scm.com/ +.. _git cheat sheet: http://cheat.errtheblog.com/s/git +.. _pro git book: https://git-scm.com/book/ +.. _git svn crash course: https://git.wiki.kernel.org/index.php/GitSvnCrashCourse +.. _learn.github: https://learn.github.com/ +.. _network graph visualizer: https://github.blog/2008-04-10-say-hello-to-the-network-graph-visualizer/ +.. _git user manual: https://www.kernel.org/pub/software/scm/git/docs/user-manual.html +.. _git tutorial: https://www.kernel.org/pub/software/scm/git/docs/gittutorial.html +.. _git community book: https://book.git-scm.com/ .. _git ready: http://www.gitready.com/ .. _git casts: http://www.gitcasts.com/ .. _Fernando's git page: http://www.fperez.org/py4science/git.html .. _git magic: http://www-cs-students.stanford.edu/~blynn/gitmagic/index.html .. _git concepts: http://www.eecs.harvard.edu/~cduan/technical/git/ -.. _git clone: http://www.kernel.org/pub/software/scm/git/docs/git-clone.html -.. _git checkout: http://www.kernel.org/pub/software/scm/git/docs/git-checkout.html -.. _git commit: http://www.kernel.org/pub/software/scm/git/docs/git-commit.html -.. _git push: http://www.kernel.org/pub/software/scm/git/docs/git-push.html -.. _git pull: http://www.kernel.org/pub/software/scm/git/docs/git-pull.html -.. _git add: http://www.kernel.org/pub/software/scm/git/docs/git-add.html -.. _git status: http://www.kernel.org/pub/software/scm/git/docs/git-status.html -.. _git diff: http://www.kernel.org/pub/software/scm/git/docs/git-diff.html -.. _git log: http://www.kernel.org/pub/software/scm/git/docs/git-log.html -.. _git branch: http://www.kernel.org/pub/software/scm/git/docs/git-branch.html -.. _git remote: http://www.kernel.org/pub/software/scm/git/docs/git-remote.html -.. _git config: http://www.kernel.org/pub/software/scm/git/docs/git-config.html +.. _git clone: https://www.kernel.org/pub/software/scm/git/docs/git-clone.html +.. _git checkout: https://www.kernel.org/pub/software/scm/git/docs/git-checkout.html +.. _git commit: https://www.kernel.org/pub/software/scm/git/docs/git-commit.html +.. _git push: https://www.kernel.org/pub/software/scm/git/docs/git-push.html +.. _git pull: https://www.kernel.org/pub/software/scm/git/docs/git-pull.html +.. _git add: https://www.kernel.org/pub/software/scm/git/docs/git-add.html +.. _git status: https://www.kernel.org/pub/software/scm/git/docs/git-status.html +.. _git diff: https://www.kernel.org/pub/software/scm/git/docs/git-diff.html +.. _git log: https://www.kernel.org/pub/software/scm/git/docs/git-log.html +.. _git branch: https://www.kernel.org/pub/software/scm/git/docs/git-branch.html +.. _git remote: https://www.kernel.org/pub/software/scm/git/docs/git-remote.html +.. _git config: https://www.kernel.org/pub/software/scm/git/docs/git-config.html .. _why the -a flag?: http://www.gitready.com/beginner/2009/01/18/the-staging-area.html .. _git staging area: http://www.gitready.com/beginner/2009/01/18/the-staging-area.html -.. _tangled working copy problem: http://tomayko.com/writings/the-thing-about-git -.. _git management: http://kerneltrap.org/Linux/Git_Management -.. _linux git workflow: http://www.mail-archive.com/dri-devel@lists.sourceforge.net/msg39091.html -.. _ipython git workflow: http://mail.python.org/pipermail/ipython-dev/2010-October/006746.html +.. _tangled working copy problem: https://tomayko.com/writings/the-thing-about-git +.. _git management: https://web.archive.org/web/20090328043540/http://kerneltrap.org/Linux/Git_Management +.. _linux git workflow: https://www.mail-archive.com/dri-devel@lists.sourceforge.net/msg39091.html +.. _ipython git workflow: https://mail.python.org/pipermail/ipython-dev/2010-October/005632.html .. _git parable: http://tom.preston-werner.com/2009/05/19/the-git-parable.html .. _git foundation: http://matthew-brett.github.com/pydagogue/foundation.html -.. _numpy/master: https://github.com/numpy/numpy +.. _numpy/main: https://github.com/numpy/numpy .. _git cherry-pick: https://www.kernel.org/pub/software/scm/git/docs/git-cherry-pick.html .. _git blame: https://www.kernel.org/pub/software/scm/git/docs/git-blame.html -.. _this blog post: http://github.com/blog/612-introducing-github-compare-view -.. _this article on merging conflicts: http://git-scm.com/book/en/Git-Branching-Basic-Branching-and-Merging#Basic-Merge-Conflicts -.. _learn git: https://www.atlassian.com/git/tutorials/ +.. _this blog post: https://github.com/blog/612-introducing-github-compare-view +.. _this article on merging conflicts: https://git-scm.com/book/en/Git-Branching-Basic-Branching-and-Merging#Basic-Merge-Conflicts +.. _learn git: https://try.github.io/ .. _filing pull requests: https://help.github.com/articles/using-pull-requests/#initiating-the-pull-request .. _pull request review: https://help.github.com/articles/using-pull-requests/#reviewing-the-pull-request .. other stuff -.. _python: http://www.python.org -.. _NumPy: http://www.numpy.org -.. _`NumPy github`: http://github.com/numpy/numpy -.. _`NumPy mailing list`: http://scipy.org/Mailing_Lists +.. _python: https://www.python.org +.. _NumPy: https://www.numpy.org +.. _`NumPy github`: https://github.com/numpy/numpy +.. _`NumPy mailing list`: https://scipy.org/scipylib/mailing-lists.html diff --git a/doc/source/dev/gitwash/index.rst b/doc/source/dev/gitwash/index.rst index b867bbd97eb2..afbb5e019ca1 100644 --- a/doc/source/dev/gitwash/index.rst +++ b/doc/source/dev/gitwash/index.rst @@ -1,7 +1,22 @@ .. _using-git: +.. _git-development: + +===================== + Git for development +===================== + +These pages describe a general git_ and github_ workflow. + +This is not a comprehensive git_ reference. It's tailored to the github_ +hosting service. You may well find better or quicker ways of getting stuff done +with git_, but these should get you started. + +For general resources for learning git_ see :ref:`git-resources`. + +Have a look at the github_ install help pages available from `github help`_ + +.. _install-git: -Working with *NumPy* source code -================================ Contents: @@ -10,6 +25,9 @@ Contents: git_intro following_latest - git_development - development_workflow + development_setup + configure_git + dot2_dot3 git_resources + +.. include:: git_links.inc diff --git a/doc/source/dev/gitwash_links.txt b/doc/source/dev/gitwash_links.txt index f9536828c604..36ca0b65f440 100644 --- a/doc/source/dev/gitwash_links.txt +++ b/doc/source/dev/gitwash_links.txt @@ -1,3 +1,3 @@ -.. _NumPy: http://www.numpy.org -.. _`NumPy github`: http://github.com/numpy/numpy -.. _`NumPy mailing list`: http://scipy.org/Mailing_Lists +.. _NumPy: https://www.numpy.org +.. _`NumPy github`: https://github.com/numpy/numpy +.. _`NumPy mailing list`: https://scipy.org/scipylib/mailing-lists.html diff --git a/doc/source/dev/governance/governance.rst b/doc/source/dev/governance/governance.rst index 54e52363c00d..3ed39c4c1df9 100644 --- a/doc/source/dev/governance/governance.rst +++ b/doc/source/dev/governance/governance.rst @@ -203,7 +203,7 @@ Council membership ~~~~~~~~~~~~~~~~~~ A list of current Steering Council Members is maintained at the -page :ref:`governance-people`. +page `About Us <https://numpy.org/about/>`_. To become eligible to join the Steering Council, an individual must be a Project Contributor who has produced contributions that are @@ -301,13 +301,13 @@ its interactions with NumFOCUS. or technical direction of the Project. - This Subcommittee will have 5 members, 4 of whom will be current Council Members and 1 of whom will be external to the Steering - Council. No more than 2 Subcommitee Members can report to one person + Council. No more than 2 Subcommittee Members can report to one person through employment or contracting work (including the reportee, i.e. the reportee + 1 is the max). This avoids effective majorities resting on one person. The current membership of the NumFOCUS Subcommittee is listed at the -page :ref:`governance-people`. +page `About Us <https://numpy.org/about/>`_. Institutional Partners and Funding @@ -375,20 +375,19 @@ Institutional Partner benefits are: - Council Members invited to NumPy Developer Meetings. A list of current Institutional Partners is maintained at the page -:ref:`governance-people`. +`About Us <https://numpy.org/about/>`_. Document history ================ -https://github.com/numpy/numpy/commits/master/doc/source/dev/governance/governance.rst +https://github.com/numpy/numpy/commits/main/doc/source/dev/governance/governance.rst Acknowledgements ================ Substantial portions of this document were adapted from the -`Jupyter/IPython project's governance document -<https://github.com/jupyter/governance/blob/master/governance.md>`_. +`Jupyter/IPython project's governance document <https://github.com/jupyter/governance>`_ License ======= diff --git a/doc/source/dev/governance/index.rst b/doc/source/dev/governance/index.rst index 3919e5e66a5e..4dcc3002bad9 100644 --- a/doc/source/dev/governance/index.rst +++ b/doc/source/dev/governance/index.rst @@ -6,4 +6,3 @@ NumPy governance :maxdepth: 3 governance - people diff --git a/doc/source/dev/governance/people.rst b/doc/source/dev/governance/people.rst deleted file mode 100644 index b22852a5a4e4..000000000000 --- a/doc/source/dev/governance/people.rst +++ /dev/null @@ -1,63 +0,0 @@ -.. _governance-people: - -Current steering council and institutional partners -=================================================== - -Steering council ----------------- - -* Sebastian Berg - -* Jaime Fernández del Río - -* Ralf Gommers - -* Charles Harris - -* Nathaniel Smith - -* Julian Taylor - -* Pauli Virtanen - -* Eric Wieser - -* Marten van Kerkwijk - -* Stephan Hoyer - -* Allan Haldane - - -Emeritus members ----------------- - -* Travis Oliphant - Project Founder / Emeritus Leader (served: 2005-2012) - -* Alex Griffing (served: 2015-2017) - - -NumFOCUS Subcommittee ---------------------- - -* Chuck Harris - -* Ralf Gommers - -* Jaime Fernández del Río - -* Nathaniel Smith - -* External member: Thomas Caswell - - -Institutional Partners ----------------------- - -* UC Berkeley (Nathaniel Smith) - - -Document history ----------------- - -https://github.com/numpy/numpy/commits/master/doc/source/dev/governance/governance.rst diff --git a/doc/source/dev/howto-docs.rst b/doc/source/dev/howto-docs.rst new file mode 100644 index 000000000000..93fec509c237 --- /dev/null +++ b/doc/source/dev/howto-docs.rst @@ -0,0 +1,447 @@ +.. _howto-docs: + +############################################ +How to contribute to the NumPy documentation +############################################ + +This guide will help you decide what to contribute and how to submit it to the +official NumPy documentation. + +*************************** +Documentation team meetings +*************************** + +The NumPy community has set a firm goal of improving its documentation. We +hold regular documentation meetings on Zoom (dates are announced on the +`numpy-discussion mailing list +<https://mail.python.org/mailman/listinfo/numpy-discussion>`__), and everyone +is welcome. Reach out if you have questions or need +someone to guide you through your first steps -- we're happy to help. +Minutes are taken `on hackmd.io <https://hackmd.io/oB_boakvRqKR-_2jRV-Qjg>`__ +and stored in the `NumPy Archive repository +<https://github.com/numpy/archive>`__. + +************* +What's needed +************* + +The :ref:`NumPy Documentation <numpy_docs_mainpage>` has the details covered. +API reference documentation is generated directly from +`docstrings <https://www.python.org/dev/peps/pep-0257/>`_ in the code when the +documentation is :ref:`built<howto-build-docs>`. Although we have mostly +complete reference documentation for each function and class exposed to users, +there is a lack of usage examples for some of them. + +What we lack are docs with broader scope -- tutorials, how-tos, and +explanations. Reporting defects is another way to contribute. We discuss both. + +****************** +Contributing fixes +****************** + +We're eager to hear about and fix doc defects. But to attack the biggest +problems we end up having to defer or overlook some bug reports. Here are the +best defects to go after. + +Top priority goes to **technical inaccuracies** -- a docstring missing a +parameter, a faulty description of a function/parameter/method, and so on. +Other "structural" defects like broken links also get priority. All these fixes +are easy to confirm and put in place. You can submit +a `pull request (PR) <https://numpy.org/devdocs/dev/index.html#devindex>`__ +with the fix, if you know how to do that; otherwise please `open an issue +<https://github.com/numpy/numpy/issues>`__. + +**Typos and misspellings** fall on a lower rung; we welcome hearing about them +but may not be able to fix them promptly. These too can be handled as pull +requests or issues. + +Obvious **wording** mistakes (like leaving out a "not") fall into the typo +category, but other rewordings -- even for grammar -- require a judgment call, +which raises the bar. Test the waters by first presenting the fix as an issue. + +Some functions/objects like numpy.ndarray.transpose, numpy.array etc. defined in +C-extension modules have their docstrings defined separately in `_add_newdocs.py +<https://github.com/numpy/numpy/blob/main/numpy/core/_add_newdocs.py>`__ + +********************** +Contributing new pages +********************** + +Your frustrations using our documents are our best guide to what needs fixing. + +If you write a missing doc you join the front line of open source, but it's +a meaningful contribution just to let us know what's missing. If you want to +compose a doc, run your thoughts by the `mailing list +<https://mail.python.org/mailman/listinfo/numpy-discussion>`__ for further +ideas and feedback. If you want to alert us to a gap, +`open an issue <https://github.com/numpy/numpy/issues>`__. See +`this issue <https://github.com/numpy/numpy/issues/15760>`__ for an example. + +If you're looking for subjects, our formal roadmap for documentation is a +*NumPy Enhancement Proposal (NEP)*, +`NEP 44 - Restructuring the NumPy Documentation <https://www.numpy.org/neps/nep-0044-restructuring-numpy-docs>`__. +It identifies areas where our docs need help and lists several +additions we'd like to see, including :ref:`Jupyter notebooks <numpy_tutorials>`. + +.. _tutorials_howtos_explanations: + +Documentation framework +======================= + +There are formulas for writing useful documents, and four formulas +cover nearly everything. There are four formulas because there are four +categories of document -- ``tutorial``, ``how-to guide``, ``explanation``, +and ``reference``. The insight that docs divide up this way belongs to +Daniele Procida and his `Diátaxis Framework <https://diataxis.fr/>`__. When you +begin a document or propose one, have in mind which of these types it will be. + +.. _numpy_tutorials: + +NumPy tutorials +=============== + +In addition to the documentation that is part of the NumPy source tree, you can +submit content in Jupyter Notebook format to the +`NumPy Tutorials <https://numpy.org/numpy-tutorials>`__ page. This +set of tutorials and educational materials is meant to provide high-quality +resources by the NumPy project, both for self-learning and for teaching classes +with. These resources are developed in a separate GitHub repository, +`numpy-tutorials <https://github.com/numpy/numpy-tutorials>`__, where you can +check out existing notebooks, open issues to suggest new topics or submit your +own tutorials as pull requests. + +.. _contributing: + +More on contributing +==================== + +Don't worry if English is not your first language, or if you can only come up +with a rough draft. Open source is a community effort. Do your best -- we'll +help fix issues. + +Images and real-life data make text more engaging and powerful, but be sure +what you use is appropriately licensed and available. Here again, even a rough +idea for artwork can be polished by others. + +For now, the only data formats accepted by NumPy are those also used by other +Python scientific libraries like pandas, SciPy, or Matplotlib. We're +developing a package to accept more formats; contact us for details. + +NumPy documentation is kept in the source code tree. To get your document +into the docbase you must download the tree, :ref:`build it +<howto-build-docs>`, and submit a pull request. If GitHub and pull requests +are new to you, check our :ref:`Contributor Guide <devindex>`. + +Our markup language is reStructuredText (rST), which is more elaborate than +Markdown. Sphinx, the tool many Python projects use to build and link project +documentation, converts the rST into HTML and other formats. For more on +rST, see the `Quick reStructuredText Guide +<https://docutils.sourceforge.io/docs/user/rst/quickref.html>`__ or the +`reStructuredText Primer +<http://www.sphinx-doc.org/en/stable/usage/restructuredtext/basics.html>`__ + + +*********************** +Contributing indirectly +*********************** + +If you run across outside material that would be a useful addition to the +NumPy docs, let us know by `opening an issue <https://github.com/numpy/numpy/issues>`__. + +You don't have to contribute here to contribute to NumPy. You've contributed +if you write a tutorial on your blog, create a YouTube video, or answer questions +on Stack Overflow and other sites. + + +.. _howto-document: + +******************* +Documentation style +******************* + +.. _userdoc_guide: + +User documentation +================== + +- In general, we follow the + `Google developer documentation style guide <https://developers.google.com/style>`_ + for the User Guide. + +- NumPy style governs cases where: + + - Google has no guidance, or + - We prefer not to use the Google style + + Our current rules: + + - We pluralize *index* as *indices* rather than + `indexes <https://developers.google.com/style/word-list#letter-i>`_, + following the precedent of :func:`numpy.indices`. + + - For consistency we also pluralize *matrix* as *matrices*. + +- Grammatical issues inadequately addressed by the NumPy or Google rules are + decided by the section on "Grammar and Usage" in the most recent edition of + the `Chicago Manual of Style + <https://en.wikipedia.org/wiki/The_Chicago_Manual_of_Style>`_. + +- We welcome being + `alerted <https://github.com/numpy/numpy/issues>`_ to cases + we should add to the NumPy style rules. + +.. _docstring_intro: + +Docstrings +========== + +When using `Sphinx <http://www.sphinx-doc.org/>`_ in combination with the +NumPy conventions, you should use the ``numpydoc`` extension so that your +docstrings will be handled correctly. For example, Sphinx will extract the +``Parameters`` section from your docstring and convert it into a field +list. Using ``numpydoc`` will also avoid the reStructuredText errors produced +by plain Sphinx when it encounters NumPy docstring conventions like +section headers (e.g. ``-------------``) that sphinx does not expect to +find in docstrings. + +It is available from: + +* `numpydoc on PyPI <https://pypi.python.org/pypi/numpydoc>`_ +* `numpydoc on GitHub <https://github.com/numpy/numpydoc/>`_ + +Note that for documentation within NumPy, it is not necessary to do +``import numpy as np`` at the beginning of an example. + +Please use the ``numpydoc`` :ref:`formatting standard <numpydoc:format>` as +shown in their :ref:`example <numpydoc:example>`. + +.. _doc_c_code: + +Documenting C/C++ Code +====================== + +NumPy uses Doxygen_ to parse specially-formatted C/C++ comment blocks. This generates +XML files, which are converted by Breathe_ into RST, which is used by Sphinx. + +**It takes three steps to complete the documentation process**: + +1. Writing the comment blocks +----------------------------- + +Although there is still no commenting style set to follow, the Javadoc +is more preferable than the others due to the similarities with the current +existing non-indexed comment blocks. + +.. note:: + Please see `"Documenting the code" <https://www.doxygen.nl/manual/docblocks.html>`__. + +**This is what Javadoc style looks like**: + +.. literalinclude:: examples/doxy_func.h + +**And here is how it is rendered**: + +.. doxygenfunction:: doxy_javadoc_example + +**For line comment, you can use a triple forward slash. For example**: + +.. literalinclude:: examples/doxy_class.hpp + +**And here is how it is rendered**: + +.. doxygenclass:: DoxyLimbo + +Common Doxygen Tags: +++++++++++++++++++++ + +.. note:: + For more tags/commands, please take a look at https://www.doxygen.nl/manual/commands.html + +``@brief`` + +Starts a paragraph that serves as a brief description. By default the first sentence +of the documentation block is automatically treated as a brief description, since +option `JAVADOC_AUTOBRIEF <https://www.doxygen.nl/manual/config.html#cfg_javadoc_autobrief>`__ +is enabled within doxygen configurations. + +``@details`` + +Just like ``@brief`` starts a brief description, ``@details`` starts the detailed description. +You can also start a new paragraph (blank line) then the ``@details`` command is not needed. + +``@param`` + +Starts a parameter description for a function parameter with name <parameter-name>, +followed by a description of the parameter. The existence of the parameter is checked +and a warning is given if the documentation of this (or any other) parameter is missing +or not present in the function declaration or definition. + +``@return`` + +Starts a return value description for a function. +Multiple adjacent ``@return`` commands will be joined into a single paragraph. +The ``@return`` description ends when a blank line or some other sectioning command is encountered. + +``@code/@endcode`` + +Starts/Ends a block of code. A code block is treated differently from ordinary text. +It is interpreted as source code. + +``@rst/@endrst`` + +Starts/Ends a block of reST markup. + +Example +~~~~~~~ +**Take a look at the following example**: + +.. literalinclude:: examples/doxy_rst.h + +**And here is how it is rendered**: + +.. doxygenfunction:: doxy_reST_example + +2. Feeding Doxygen +------------------ + +Not all headers files are collected automatically. You have to add the desired +C/C++ header paths within the sub-config files of Doxygen. + +Sub-config files have the unique name ``.doxyfile``, which you can usually find near +directories that contain documented headers. You need to create a new config file if +there's not one located in a path close(2-depth) to the headers you want to add. + +Sub-config files can accept any of Doxygen_ `configuration options <https://www.doxygen.nl/manual/config.html>`__, +but do not override or re-initialize any configuration option, +rather only use the concatenation operator "+=". For example:: + + # to specfiy certain headers + INPUT += @CUR_DIR/header1.h \ + @CUR_DIR/header2.h + # to add all headers in certain path + INPUT += @CUR_DIR/to/headers + # to define certain macros + PREDEFINED += C_MACRO(X)=X + # to enable certain branches + PREDEFINED += NPY_HAVE_FEATURE \ + NPY_HAVE_FEATURE2 + +.. note:: + @CUR_DIR is a template constant returns the current + dir path of the sub-config file. + +3. Inclusion directives +----------------------- + +Breathe_ provides a wide range of custom directives to allow +converting the documents generated by Doxygen_ into reST files. + +.. note:: + For more information, please check out "`Directives & Config Variables <https://breathe.readthedocs.io/en/latest/directives.html>`__" + +Common directives: +++++++++++++++++++ + +``doxygenfunction`` + +This directive generates the appropriate output for a single function. +The function name is required to be unique in the project. + +.. code:: + + .. doxygenfunction:: <function name> + :outline: + :no-link: + +Checkout the `example <https://breathe.readthedocs.io/en/latest/function.html#function-example>`__ +to see it in action. + + +``doxygenclass`` + +This directive generates the appropriate output for a single class. +It takes the standard project, path, outline and no-link options and +additionally the members, protected-members, private-members, undoc-members, +membergroups and members-only options: + +.. code:: + + .. doxygenclass:: <class name> + :members: [...] + :protected-members: + :private-members: + :undoc-members: + :membergroups: ... + :members-only: + :outline: + :no-link: + +Checkout the `doxygenclass documentation <https://breathe.readthedocs.io/en/latest/class.html#class-example>_` +for more details and to see it in action. + +``doxygennamespace`` + +This directive generates the appropriate output for the contents of a namespace. +It takes the standard project, path, outline and no-link options and additionally the content-only, +members, protected-members, private-members and undoc-members options. +To reference a nested namespace, the full namespaced path must be provided, +e.g. foo::bar for the bar namespace inside the foo namespace. + +.. code:: + + .. doxygennamespace:: <namespace> + :content-only: + :outline: + :members: + :protected-members: + :private-members: + :undoc-members: + :no-link: + +Checkout the `doxygennamespace documentation <https://breathe.readthedocs.io/en/latest/namespace.html#namespace-example>`__ +for more details and to see it in action. + +``doxygengroup`` + +This directive generates the appropriate output for the contents of a doxygen group. +A doxygen group can be declared with specific doxygen markup in the source comments +as covered in the doxygen `grouping documentation <https://www.doxygen.nl/manual/grouping.html>`__. + +It takes the standard project, path, outline and no-link options and additionally the +content-only, members, protected-members, private-members and undoc-members options. + +.. code:: + + .. doxygengroup:: <group name> + :content-only: + :outline: + :members: + :protected-members: + :private-members: + :undoc-members: + :no-link: + :inner: + +Checkout the `doxygengroup documentation <https://breathe.readthedocs.io/en/latest/group.html#group-example>`__ +for more details and to see it in action. + +.. _`Doxygen`: https://www.doxygen.nl/index.html +.. _`Breathe`: https://breathe.readthedocs.io/en/latest/ + + +********************* +Documentation reading +********************* + +- The leading organization of technical writers, + `Write the Docs <https://www.writethedocs.org/>`__, + holds conferences, hosts learning resources, and runs a Slack channel. + +- "Every engineer is also a writer," says Google's + `collection of technical writing resources <https://developers.google.com/tech-writing>`__, + which includes free online courses for developers in planning and writing + documents. + +- `Software Carpentry's <https://software-carpentry.org/lessons>`__ mission is + teaching software to researchers. In addition to hosting the curriculum, the + website explains how to present ideas effectively. diff --git a/doc/source/dev/howto_build_docs.rst b/doc/source/dev/howto_build_docs.rst new file mode 100644 index 000000000000..b175926da075 --- /dev/null +++ b/doc/source/dev/howto_build_docs.rst @@ -0,0 +1,137 @@ +.. _howto-build-docs: + +========================================= +Building the NumPy API and reference docs +========================================= + +If you only want to get the documentation, note that pre-built +versions can be found at + + https://numpy.org/doc/ + +in several different formats. + +Development environments +------------------------ + +Before proceeding further it should be noted that the documentation is built with the ``make`` tool, +which is not natively available on Windows. MacOS or Linux users can jump +to :ref:`how-todoc.prerequisites`. It is recommended for Windows users to set up their development +environment on :ref:`Gitpod <development-gitpod>` or `Windows Subsystem +for Linux (WSL) <https://docs.microsoft.com/en-us/windows/wsl/install-win10>`_. WSL is a good option +for a persistent local set-up. + +Gitpod +^^^^^^ +Gitpod is an open-source platform that automatically creates the correct development environment right +in your browser, reducing the need to install local development environments and deal with +incompatible dependencies. + +If you have good internet connectivity and want a temporary set-up, +it is often faster to build with Gitpod. Here are the in-depth instructions for +:ref:`building NumPy with Gitpod <development-gitpod>`. + + +.. _how-todoc.prerequisites: + +Prerequisites +------------- + +Building the NumPy documentation and API reference requires the following: + +NumPy +^^^^^ + +Since large parts of the main documentation are obtained from NumPy via +``import numpy`` and examining the docstrings, you will need to first +:ref:`build <building-from-source>` and install it so that the correct version is imported. +NumPy has to be re-built and re-installed every time you fetch the latest version of the +repository, before generating the documentation. This ensures that the NumPy version and +the git repository version are in sync. + +Note that you can e.g. install NumPy to a temporary location and set +the PYTHONPATH environment variable appropriately. +Alternatively, if using Python virtual environments (via e.g. ``conda``, +``virtualenv`` or the ``venv`` module), installing NumPy into a +new virtual environment is recommended. + +Dependencies +^^^^^^^^^^^^ + +All of the necessary dependencies for building the NumPy docs except for +Doxygen_ can be installed with:: + + pip install -r doc_requirements.txt + +We currently use Sphinx_ along with Doxygen_ for generating the API and +reference documentation for NumPy. In addition, building the documentation +requires the Sphinx extension `plot_directive`, which is shipped with +:doc:`Matplotlib <matplotlib:index>`. We also use numpydoc_ to render docstrings in +the generated API documentation. :doc:`SciPy <scipy:index>` +is installed since some parts of the documentation require SciPy functions. + +For installing Doxygen_, please check the official +`download <https://www.doxygen.nl/download.html#srcbin>`_ and +`installation <https://www.doxygen.nl/manual/install.html>`_ pages, or if you +are using Linux then you can install it through your distribution package manager. + +.. note:: + + Try to install a newer version of Doxygen_ > 1.8.10 otherwise you may get some + warnings during the build. + +Submodules +^^^^^^^^^^ + +If you obtained NumPy via git, also get the git submodules that contain +additional parts required for building the documentation:: + + git submodule update --init + +.. _Sphinx: http://www.sphinx-doc.org/ +.. _numpydoc: https://numpydoc.readthedocs.io/en/latest/index.html +.. _Doxygen: https://www.doxygen.nl/index.html + +Instructions +------------ + +Now you are ready to generate the docs, so write:: + + cd doc + make html + +If all goes well, this will generate a +``build/html`` subdirectory in the ``/doc`` directory, containing the built documentation. If +you get a message about ``installed numpy != current repo git version``, you must +either override the check by setting ``GITVER`` or re-install NumPy. + +If you have built NumPy into a virtual environment and get an error +that says ``numpy not found, cannot build documentation without...``, +you need to override the makefile ``PYTHON`` variable at the command +line, so instead of writing ``make html`` write:: + + make PYTHON=python html + +To build the PDF documentation, do instead:: + + make latex + make -C build/latex all-pdf + +You will need to have LaTeX_ installed for this, inclusive of support for +Greek letters. For example, on Ubuntu xenial ``texlive-lang-greek`` and +``cm-super`` are needed. Also, ``latexmk`` is needed on non-Windows systems. + +Instead of the above, you can also do:: + + make dist + +which will rebuild NumPy, install it to a temporary location, and +build the documentation in all formats. This will most likely again +only work on Unix platforms. + +The documentation for NumPy distributed at https://numpy.org/doc in html and +pdf format is also built with ``make dist``. See `HOWTO RELEASE`_ for details +on how to update https://numpy.org/doc. + +.. _LaTeX: https://www.latex-project.org/ +.. _HOWTO RELEASE: https://github.com/numpy/numpy/blob/main/doc/HOWTO_RELEASE.rst.txt diff --git a/doc/source/dev/index.rst b/doc/source/dev/index.rst index 543194119541..a8c9692679b3 100644 --- a/doc/source/dev/index.rst +++ b/doc/source/dev/index.rst @@ -1,12 +1,273 @@ +.. _devindex: + ##################### Contributing to NumPy ##################### +Not a coder? Not a problem! NumPy is multi-faceted, and we can use a lot of help. +These are all activities we'd like to get help with (they're all important, so +we list them in alphabetical order): + +- Code maintenance and development +- Community coordination +- DevOps +- Developing educational content & narrative documentation +- Fundraising +- Marketing +- Project management +- Translating content +- Website design and development +- Writing technical documentation + +The rest of this document discusses working on the NumPy code base and documentation. +We're in the process of updating our descriptions of other activities and roles. +If you are interested in these other activities, please contact us! +You can do this via +the `numpy-discussion mailing list <https://mail.python.org/mailman/listinfo/numpy-discussion>`__, +or on `GitHub <https://github.com/numpy/numpy>`__ (open an issue or comment on a +relevant issue). These are our preferred communication channels (open source is open +by nature!), however if you prefer to discuss in private first, please reach out to +our community coordinators at `numpy-team@googlegroups.com` or `numpy-team.slack.com` +(send an email to `numpy-team@googlegroups.com` for an invite the first time). + +Development process - summary +============================= + +Here's the short summary, complete TOC links are below: + +1. If you are a first-time contributor: + + * Go to `https://github.com/numpy/numpy + <https://github.com/numpy/numpy>`_ and click the + "fork" button to create your own copy of the project. + + * Clone the project to your local computer:: + + git clone https://github.com/your-username/numpy.git + + * Change the directory:: + + cd numpy + + * Add the upstream repository:: + + git remote add upstream https://github.com/numpy/numpy.git + + * Now, `git remote -v` will show two remote repositories named: + + - ``upstream``, which refers to the ``numpy`` repository + - ``origin``, which refers to your personal fork + +2. Develop your contribution: + + * Pull the latest changes from upstream:: + + git checkout main + git pull upstream main + + * Create a branch for the feature you want to work on. Since the + branch name will appear in the merge message, use a sensible name + such as 'linspace-speedups':: + + git checkout -b linspace-speedups + + * Commit locally as you progress (``git add`` and ``git commit``) + Use a :ref:`properly formatted<writing-the-commit-message>` commit message, + write tests that fail before your change and pass afterward, run all the + :ref:`tests locally<development-environment>`. Be sure to document any + changed behavior in docstrings, keeping to the NumPy docstring + :ref:`standard<howto-document>`. + +3. To submit your contribution: + + * Push your changes back to your fork on GitHub:: + + git push origin linspace-speedups + + * Enter your GitHub username and password (repeat contributors or advanced + users can remove this step by connecting to GitHub with + :ref:`SSH<set-up-and-configure-a-github-account>`). + + * Go to GitHub. The new branch will show up with a green Pull Request + button. Make sure the title and message are clear, concise, and self- + explanatory. Then click the button to submit it. + + * If your commit introduces a new feature or changes functionality, post on + the `mailing list`_ to explain your changes. For bug fixes, documentation + updates, etc., this is generally not necessary, though if you do not get + any reaction, do feel free to ask for review. + +4. Review process: + + * Reviewers (the other developers and interested community members) will + write inline and/or general comments on your Pull Request (PR) to help + you improve its implementation, documentation and style. Every single + developer working on the project has their code reviewed, and we've come + to see it as friendly conversation from which we all learn and the + overall code quality benefits. Therefore, please don't let the review + discourage you from contributing: its only aim is to improve the quality + of project, not to criticize (we are, after all, very grateful for the + time you're donating!). See our :ref:`Reviewer Guidelines + <reviewer-guidelines>` for more information. + + * To update your PR, make your changes on your local repository, commit, + **run tests, and only if they succeed** push to your fork. As soon as + those changes are pushed up (to the same branch as before) the PR will + update automatically. If you have no idea how to fix the test failures, + you may push your changes anyway and ask for help in a PR comment. + + * Various continuous integration (CI) services are triggered after each PR + update to build the code, run unit tests, measure code coverage and check + coding style of your branch. The CI tests must pass before your PR can be + merged. If CI fails, you can find out why by clicking on the "failed" + icon (red cross) and inspecting the build and test log. To avoid overuse + and waste of this resource, + :ref:`test your work<recommended-development-setup>` locally before + committing. + + * A PR must be **approved** by at least one core team member before merging. + Approval means the core team member has carefully reviewed the changes, + and the PR is ready for merging. + +5. Document changes + + Beyond changes to a functions docstring and possible description in the + general documentation, if your change introduces any user-facing + modifications they may need to be mentioned in the release notes. + To add your change to the release notes, you need to create a short file + with a summary and place it in ``doc/release/upcoming_changes``. + The file ``doc/release/upcoming_changes/README.rst`` details the format and + filename conventions. + + If your change introduces a deprecation, make sure to discuss this first on + GitHub or the mailing list first. If agreement on the deprecation is + reached, follow :ref:`NEP 23 deprecation policy <NEP23>` to add the deprecation. + +6. Cross referencing issues + + If the PR relates to any issues, you can add the text ``xref gh-xxxx`` where + ``xxxx`` is the number of the issue to github comments. Likewise, if the PR + solves an issue, replace the ``xref`` with ``closes``, ``fixes`` or any of + the other flavors `github accepts <https://help.github.com/en/articles/ + closing-issues-using-keywords>`_. + + In the source code, be sure to preface any issue or PR reference with + ``gh-xxxx``. + +For a more detailed discussion, read on and follow the links at the bottom of +this page. + +Divergence between ``upstream/main`` and your feature branch +------------------------------------------------------------ + +If GitHub indicates that the branch of your Pull Request can no longer +be merged automatically, you have to incorporate changes that have been made +since you started into your branch. Our recommended way to do this is to +:ref:`rebase on main <rebasing-on-main>`. + +.. _guidelines: + +Guidelines +---------- + +* All code should have tests (see `test coverage`_ below for more details). +* All code should be `documented <https://numpydoc.readthedocs.io/ + en/latest/format.html#docstring-standard>`_. +* No changes are ever committed without review and approval by a core + team member. Please ask politely on the PR or on the `mailing list`_ if you + get no response to your pull request within a week. + +.. _stylistic-guidelines: + +Stylistic Guidelines +-------------------- + +* Set up your editor to follow `PEP 8 <https://www.python.org/dev/peps/ + pep-0008/>`_ (remove trailing white space, no tabs, etc.). Check code with + pyflakes / flake8. + +* Use NumPy data types instead of strings (``np.uint8`` instead of + ``"uint8"``). + +* Use the following import conventions:: + + import numpy as np + +* For C code, see :ref:`NEP 45 <NEP45>`. + + +Test coverage +------------- + +Pull requests (PRs) that modify code should either have new tests, or modify existing +tests to fail before the PR and pass afterwards. You should :ref:`run the tests +<development-environment>` before pushing a PR. + +Running NumPy's test suite locally requires some additional packages, such as +``pytest`` and ``hypothesis``. The additional testing dependencies are listed +in ``test_requirements.txt`` in the top-level directory, and can conveniently +be installed with:: + + pip install -r test_requirements.txt + +Tests for a module should ideally cover all code in that module, +i.e., statement coverage should be at 100%. + +To measure the test coverage, install +`pytest-cov <https://pytest-cov.readthedocs.io/en/latest/>`__ +and then run:: + + $ python runtests.py --coverage + +This will create a report in ``build/coverage``, which can be viewed with:: + + $ firefox build/coverage/index.html + +.. _building-docs: + +Building docs +------------- + +To build docs, run ``make`` from the ``doc`` directory. ``make help`` lists +all targets. For example, to build the HTML documentation, you can run:: + + make html + +To get the appropriate dependencies and other requirements, +see :ref:`howto-build-docs`. + +Fixing Warnings +~~~~~~~~~~~~~~~ + +- "citation not found: R###" There is probably an underscore after a + reference in the first line of a docstring (e.g. [1]\_). Use this + method to find the source file: $ cd doc/build; grep -rin R#### + +- "Duplicate citation R###, other instance in..."" There is probably a + [2] without a [1] in one of the docstrings + +Development process - details +============================= + +The rest of the story + .. toctree:: - :maxdepth: 3 + :maxdepth: 2 - gitwash/index + Git Basics <gitwash/index> development_environment + development_gitpod + howto_build_docs + development_workflow + development_advanced_debugging + reviewer_guidelines + ../benchmarking + NumPy C style guide <https://numpy.org/neps/nep-0045-c_style_guide.html> + releasing governance/index + howto-docs + +NumPy-specific workflow is in :ref:`numpy-development-workflow +<development-workflow>`. -For core developers: see :ref:`development-workflow`. +.. _`mailing list`: https://mail.python.org/mailman/listinfo/numpy-discussion diff --git a/doc/source/dev/internals.code-explanations.rst b/doc/source/dev/internals.code-explanations.rst new file mode 100644 index 000000000000..b6edd61b1efa --- /dev/null +++ b/doc/source/dev/internals.code-explanations.rst @@ -0,0 +1,646 @@ +.. currentmodule:: numpy + +.. _c-code-explanations: + +************************* +NumPy C code explanations +************************* + + Fanaticism consists of redoubling your efforts when you have forgotten + your aim. + --- *George Santayana* + + An authority is a person who can tell you more about something than + you really care to know. + --- *Unknown* + +This page attempts to explain the logic behind some of the new +pieces of code. The purpose behind these explanations is to enable +somebody to be able to understand the ideas behind the implementation +somewhat more easily than just staring at the code. Perhaps in this +way, the algorithms can be improved on, borrowed from, and/or +optimized by more people. + + +Memory model +============ + +.. index:: + pair: ndarray; memory model + +One fundamental aspect of the :class:`ndarray` is that an array is seen as a +"chunk" of memory starting at some location. The interpretation of +this memory depends on the :term:`stride` information. For each dimension in +an :math:`N`-dimensional array, an integer (:term:`stride`) dictates how many +bytes must be skipped to get to the next element in that dimension. +Unless you have a single-segment array, this :term:`stride` information must +be consulted when traversing through an array. It is not difficult to +write code that accepts strides, you just have to use ``char*`` +pointers because strides are in units of bytes. Keep in mind also that +strides do not have to be unit-multiples of the element size. Also, +remember that if the number of dimensions of the array is 0 (sometimes +called a ``rank-0`` array), then the :term:`strides <stride>` and +:term:`dimensions <dimension>` variables are ``NULL``. + +Besides the structural information contained in the strides and +dimensions members of the :c:type:`PyArrayObject`, the flags contain +important information about how the data may be accessed. In particular, +the :c:data:`NPY_ARRAY_ALIGNED` flag is set when the memory is on a +suitable boundary according to the datatype array. Even if you have +a :term:`contiguous` chunk of memory, you cannot just assume it is safe to +dereference a datatype-specific pointer to an element. Only if the +:c:data:`NPY_ARRAY_ALIGNED` flag is set, this is a safe operation. On +some platforms it will work but on others, like Solaris, it will cause +a bus error. The :c:data:`NPY_ARRAY_WRITEABLE` should also be ensured +if you plan on writing to the memory area of the array. It is also +possible to obtain a pointer to an unwritable memory area. Sometimes, +writing to the memory area when the :c:data:`NPY_ARRAY_WRITEABLE` flag is not +set will just be rude. Other times it can cause program crashes (*e.g.* +a data-area that is a read-only memory-mapped file). + + +Data-type encapsulation +======================= + +.. seealso:: :ref:`arrays.dtypes` + +.. index:: + single: dtype + +The :ref:`datatype <arrays.dtypes>` is an important abstraction of the +:class:`ndarray`. Operations +will look to the datatype to provide the key functionality that is +needed to operate on the array. This functionality is provided in the +list of function pointers pointed to by the ``f`` member of the +:c:type:`PyArray_Descr` structure. In this way, the number of datatypes can be +extended simply by providing a :c:type:`PyArray_Descr` structure with suitable +function pointers in the ``f`` member. For built-in types, there are some +optimizations that bypass this mechanism, but the point of the datatype +abstraction is to allow new datatypes to be added. + +One of the built-in datatypes, the :class:`void` datatype allows for +arbitrary :term:`structured types <structured data type>` containing 1 or more +fields as elements of the array. A :term:`field` is simply another datatype +object along with an offset into the current structured type. In order to +support arbitrarily nested fields, several recursive implementations of +datatype access are implemented for the void type. A common idiom is to cycle +through the elements of the dictionary and perform a specific operation based on +the datatype object stored at the given offset. These offsets can be +arbitrary numbers. Therefore, the possibility of encountering misaligned +data must be recognized and taken into account if necessary. + + +N-D Iterators +============= + +.. seealso:: :ref:`arrays.nditer` + +.. index:: + single: array iterator + +A very common operation in much of NumPy code is the need to iterate +over all the elements of a general, strided, N-dimensional array. This +operation of a general-purpose N-dimensional loop is abstracted in the +notion of an iterator object. To write an N-dimensional loop, you only +have to create an iterator object from an ndarray, work with the +:c:member:`dataptr <PyArrayIterObject.dataptr>` member of the iterator object +structure and call the macro :c:func:`PyArray_ITER_NEXT` on the iterator +object to move to the next element. The ``next`` element is always in +C-contiguous order. The macro works by first special-casing the C-contiguous, +1-D, and 2-D cases which work very simply. + +For the general case, the iteration works by keeping track of a list +of coordinate counters in the iterator object. At each iteration, the +last coordinate counter is increased (starting from 0). If this +counter is smaller than one less than the size of the array in that +dimension (a pre-computed and stored value), then the counter is +increased and the :c:member:`dataptr <PyArrayIterObject.dataptr>` member is +increased by the strides in that +dimension and the macro ends. If the end of a dimension is reached, +the counter for the last dimension is reset to zero and the +:c:member:`dataptr <PyArrayIterObject.dataptr>` is +moved back to the beginning of that dimension by subtracting the +strides value times one less than the number of elements in that +dimension (this is also pre-computed and stored in the +:c:member:`backstrides <PyArrayIterObject.backstrides>` +member of the iterator object). In this case, the macro does not end, +but a local dimension counter is decremented so that the next-to-last +dimension replaces the role that the last dimension played and the +previously-described tests are executed again on the next-to-last +dimension. In this way, the :c:member:`dataptr <PyArrayIterObject.dataptr>` +is adjusted appropriately for arbitrary striding. + +The :c:member:`coordinates <PyArrayIterObject.coordinates>` member of the +:c:type:`PyArrayIterObject` structure maintains +the current N-d counter unless the underlying array is C-contiguous in +which case the coordinate counting is bypassed. The +:c:member:`index <PyArrayIterObject.index>` member of +the :c:type:`PyArrayIterObject` keeps track of the current flat index of the +iterator. It is updated by the :c:func:`PyArray_ITER_NEXT` macro. + + +Broadcasting +============ + +.. seealso:: :ref:`basics.broadcasting` + +.. index:: + single: broadcasting + +In Numeric, the ancestor of NumPy, broadcasting was implemented in several +lines of code buried deep in ``ufuncobject.c``. In NumPy, the notion of +broadcasting has been abstracted so that it can be performed in multiple places. +Broadcasting is handled by the function :c:func:`PyArray_Broadcast`. This +function requires a :c:type:`PyArrayMultiIterObject` (or something that is a +binary equivalent) to be passed in. The :c:type:`PyArrayMultiIterObject` keeps +track of the broadcast number of dimensions and size in each +dimension along with the total size of the broadcast result. It also +keeps track of the number of arrays being broadcast and a pointer to +an iterator for each of the arrays being broadcast. + +The :c:func:`PyArray_Broadcast` function takes the iterators that have already +been defined and uses them to determine the broadcast shape in each +dimension (to create the iterators at the same time that broadcasting +occurs then use the :c:func:`PyArray_MultiIterNew` function). +Then, the iterators are +adjusted so that each iterator thinks it is iterating over an array +with the broadcast size. This is done by adjusting the iterators +number of dimensions, and the :term:`shape` in each dimension. This works +because the iterator strides are also adjusted. Broadcasting only +adjusts (or adds) length-1 dimensions. For these dimensions, the +strides variable is simply set to 0 so that the data-pointer for the +iterator over that array doesn't move as the broadcasting operation +operates over the extended dimension. + +Broadcasting was always implemented in Numeric using 0-valued strides +for the extended dimensions. It is done in exactly the same way in +NumPy. The big difference is that now the array of strides is kept +track of in a :c:type:`PyArrayIterObject`, the iterators involved in a +broadcast result are kept track of in a :c:type:`PyArrayMultiIterObject`, +and the :c:func:`PyArray_Broadcast` call implements the +:ref:`general-broadcasting-rules`. + + +Array Scalars +============= + +.. seealso:: :ref:`arrays.scalars` + +.. index:: + single: array scalars + +The array scalars offer a hierarchy of Python types that allow a one-to-one +correspondence between the datatype stored in an array and the +Python-type that is returned when an element is extracted from the +array. An exception to this rule was made with object arrays. Object +arrays are heterogeneous collections of arbitrary Python objects. When +you select an item from an object array, you get back the original +Python object (and not an object array scalar which does exist but is +rarely used for practical purposes). + +The array scalars also offer the same methods and attributes as arrays +with the intent that the same code can be used to support arbitrary +dimensions (including 0-dimensions). The array scalars are read-only +(immutable) with the exception of the void scalar which can also be +written to so that structured array field setting works more naturally +(``a[0]['f1'] = value``). + + +Indexing +======== + +.. seealso:: :ref:`basics.indexing`, :ref:`arrays.indexing` + +.. index:: + single: indexing + +All Python indexing operations ``arr[index]`` are organized by first preparing +the index and finding the index type. The supported index types are: + +* integer +* :const:`newaxis` +* :term:`python:slice` +* :py:data:`Ellipsis` +* integer arrays/array-likes (advanced) +* boolean (single boolean array); if there is more than one boolean array as + the index or the shape does not match exactly, the boolean array will be + converted to an integer array instead. +* 0-d boolean (and also integer); 0-d boolean arrays are a special + case that has to be handled in the advanced indexing code. They signal + that a 0-d boolean array had to be interpreted as an integer array. + +As well as the scalar array special case signaling that an integer array +was interpreted as an integer index, which is important because an integer +array index forces a copy but is ignored if a scalar is returned (full integer +index). The prepared index is guaranteed to be valid with the exception of +out of bound values and broadcasting errors for advanced indexing. This +includes that an :py:data:`Ellipsis` is added for incomplete indices for +example when a two-dimensional array is indexed with a single integer. + +The next step depends on the type of index which was found. If all +dimensions are indexed with an integer a scalar is returned or set. A +single boolean indexing array will call specialized boolean functions. +Indices containing an :py:data:`Ellipsis` or :term:`python:slice` but no +advanced indexing will always create a view into the old array by calculating +the new strides and memory offset. This view can then either be returned or, +for assignments, filled using ``PyArray_CopyObject``. Note that +``PyArray_CopyObject`` may also be called on temporary arrays in other branches +to support complicated assignments when the array is of object :class:`dtype`. + +Advanced indexing +----------------- + +By far the most complex case is advanced indexing, which may or may not be +combined with typical view-based indexing. Here integer indices are +interpreted as view-based. Before trying to understand this, you may want +to make yourself familiar with its subtleties. The advanced indexing code +has three different branches and one special case: + +* There is one indexing array and it, as well as the assignment array, can + be iterated trivially. For example, they may be contiguous. Also, the + indexing array must be of :class:`intp` type and the value array in + assignments should be of the correct type. This is purely a fast path. +* There are only integer array indices so that no subarray exists. +* View-based and advanced indexing is mixed. In this case, the view-based + indexing defines a collection of subarrays that are combined by the + advanced indexing. For example, ``arr[[1, 2, 3], :]`` is created by + vertically stacking the subarrays ``arr[1, :]``, ``arr[2, :]``, and + ``arr[3, :]``. +* There is a subarray but it has exactly one element. This case can be handled + as if there is no subarray but needs some care during setup. + +Deciding what case applies, checking broadcasting, and determining the kind +of transposition needed are all done in :c:func:`PyArray_MapIterNew`. After +setting up, there are two cases. If there is no subarray or it only has one +element, no subarray iteration is necessary and an iterator is prepared +which iterates all indexing arrays *as well as* the result or value array. +If there is a subarray, there are three iterators prepared. One for the +indexing arrays, one for the result or value array (minus its subarray), +and one for the subarrays of the original and the result/assignment array. +The first two iterators give (or allow calculation) of the pointers into +the start of the subarray, which then allows restarting the subarray +iteration. + +When advanced indices are next to each other transposing may be necessary. +All necessary transposing is handled by :c:func:`PyArray_MapIterSwapAxes` and +has to be handled by the caller unless :c:func:`PyArray_MapIterNew` is asked to +allocate the result. + +After preparation, getting and setting are relatively straightforward, +although the different modes of iteration need to be considered. Unless +there is only a single indexing array during item getting, the validity of +the indices is checked beforehand. Otherwise, it is handled in the inner +loop itself for optimization. + +.. _ufuncs-internals: + +Universal functions +=================== + +.. seealso:: :ref:`ufuncs`, :ref:`ufuncs-basics` + +.. index:: + single: ufunc + +Universal functions are callable objects that take :math:`N` inputs +and produce :math:`M` outputs by wrapping basic 1-D loops that work +element-by-element into full easy-to-use functions that seamlessly +implement :ref:`broadcasting <basics.broadcasting>`, +:ref:`type-checking <ufuncs.casting>`, +:ref:`buffered coercion <use-of-internal-buffers>`, and +:ref:`output-argument handling <ufuncs-output-type>`. New universal functions +are normally created in C, although there is a mechanism for creating ufuncs +from Python functions (:func:`frompyfunc`). The user must supply a 1-D loop that +implements the basic function taking the input scalar values and +placing the resulting scalars into the appropriate output slots as +explained in implementation. + + +Setup +----- + +Every :class:`ufunc` calculation involves some overhead related to setting up +the calculation. The practical significance of this overhead is that +even though the actual calculation of the ufunc is very fast, you will +be able to write array and type-specific code that will work faster +for small arrays than the ufunc. In particular, using ufuncs to +perform many calculations on 0-D arrays will be slower than other +Python-based solutions (the silently-imported ``scalarmath`` module exists +precisely to give array scalars the look-and-feel of ufunc based +calculations with significantly reduced overhead). + +When a :class:`ufunc` is called, many things must be done. The information +collected from these setup operations is stored in a loop object. This +loop object is a C-structure (that could become a Python object but is +not initialized as such because it is only used internally). This loop +object has the layout needed to be used with :c:func:`PyArray_Broadcast` +so that the broadcasting can be handled in the same way as it is handled in +other sections of code. + +The first thing done is to look up in the thread-specific global +dictionary the current values for the buffer-size, the error mask, and +the associated error object. The state of the error mask controls what +happens when an error condition is found. It should be noted that +checking of the hardware error flags is only performed after each 1-D +loop is executed. This means that if the input and output arrays are +contiguous and of the correct type so that a single 1-D loop is +performed, then the flags may not be checked until all elements of the +array have been calculated. Looking up these values in a thread-specific +dictionary takes time which is easily ignored for all but +very small arrays. + +After checking, the thread-specific global variables, the inputs are +evaluated to determine how the ufunc should proceed and the input and +output arrays are constructed if necessary. Any inputs which are not +arrays are converted to arrays (using context if necessary). Which of +the inputs are scalars (and therefore converted to 0-D arrays) is +noted. + +Next, an appropriate 1-D loop is selected from the 1-D loops available +to the :class:`ufunc` based on the input array types. This 1-D loop is selected +by trying to match the signature of the datatypes of the inputs +against the available signatures. The signatures corresponding to +built-in types are stored in the :attr:`ufunc.types` member of the ufunc +structure. The signatures corresponding to user-defined types are stored in a +linked list of function information with the head element stored as a +``CObject`` in the ``userloops`` dictionary keyed by the datatype number +(the first user-defined type in the argument list is used as the key). +The signatures are searched until a signature is found to which the +input arrays can all be cast safely (ignoring any scalar arguments +which are not allowed to determine the type of the result). The +implication of this search procedure is that "lesser types" should be +placed below "larger types" when the signatures are stored. If no 1-D +loop is found, then an error is reported. Otherwise, the ``argument_list`` +is updated with the stored signature --- in case casting is necessary +and to fix the output types assumed by the 1-D loop. + +If the ufunc has 2 inputs and 1 output and the second input is an +``Object`` array then a special-case check is performed so that +``NotImplemented`` is returned if the second input is not an ndarray, has +the :obj:`~numpy.class.__array_priority__` attribute, and has an ``__r{op}__`` +special method. In this way, Python is signaled to give the other object a +chance to complete the operation instead of using generic object-array +calculations. This allows (for example) sparse matrices to override +the multiplication operator 1-D loop. + +For input arrays that are smaller than the specified buffer size, +copies are made of all non-contiguous, misaligned, or out-of-byteorder +arrays to ensure that for small arrays, a single loop is +used. Then, array iterators are created for all the input arrays and +the resulting collection of iterators is broadcast to a single shape. + +The output arguments (if any) are then processed and any missing +return arrays are constructed. If any provided output array doesn't +have the correct type (or is misaligned) and is smaller than the +buffer size, then a new output array is constructed with the special +:c:data:`NPY_ARRAY_WRITEBACKIFCOPY` flag set. At the end of the function, +:c:func:`PyArray_ResolveWritebackIfCopy` is called so that +its contents will be copied back into the output array. +Iterators for the output arguments are then processed. + +Finally, the decision is made about how to execute the looping +mechanism to ensure that all elements of the input arrays are combined +to produce the output arrays of the correct type. The options for loop +execution are one-loop (for :term`contiguous`, aligned, and correct data +type), strided-loop (for non-contiguous but still aligned and correct +data type), and a buffered loop (for misaligned or incorrect data +type situations). Depending on which execution method is called for, +the loop is then set up and computed. + + +Function call +------------- + +This section describes how the basic universal function computation loop is +set up and executed for each of the three different kinds of execution. If +:c:data:`NPY_ALLOW_THREADS` is defined during compilation, then as long as +no object arrays are involved, the Python Global Interpreter Lock (GIL) is +released prior to calling the loops. It is re-acquired if necessary to +handle error conditions. The hardware error flags are checked only after +the 1-D loop is completed. + + +One loop +^^^^^^^^ + +This is the simplest case of all. The ufunc is executed by calling the +underlying 1-D loop exactly once. This is possible only when we have +aligned data of the correct type (including byteorder) for both input +and output and all arrays have uniform strides (either :term:`contiguous`, +0-D, or 1-D). In this case, the 1-D computational loop is called once +to compute the calculation for the entire array. Note that the +hardware error flags are only checked after the entire calculation is +complete. + + +Strided loop +^^^^^^^^^^^^ + +When the input and output arrays are aligned and of the correct type, +but the striding is not uniform (non-contiguous and 2-D or larger), +then a second looping structure is employed for the calculation. This +approach converts all of the iterators for the input and output +arguments to iterate over all but the largest dimension. The inner +loop is then handled by the underlying 1-D computational loop. The +outer loop is a standard iterator loop on the converted iterators. The +hardware error flags are checked after each 1-D loop is completed. + + +Buffered loop +^^^^^^^^^^^^^ + +This is the code that handles the situation whenever the input and/or +output arrays are either misaligned or of the wrong datatype +(including being byteswapped) from what the underlying 1-D loop +expects. The arrays are also assumed to be non-contiguous. The code +works very much like the strided-loop except for the inner 1-D loop is +modified so that pre-processing is performed on the inputs and post-processing +is performed on the outputs in ``bufsize`` chunks (where +``bufsize`` is a user-settable parameter). The underlying 1-D +computational loop is called on data that is copied over (if it needs +to be). The setup code and the loop code is considerably more +complicated in this case because it has to handle: + +- memory allocation of the temporary buffers + +- deciding whether or not to use buffers on the input and output data + (misaligned and/or wrong datatype) + +- copying and possibly casting data for any inputs or outputs for which + buffers are necessary. + +- special-casing ``Object`` arrays so that reference counts are properly + handled when copies and/or casts are necessary. + +- breaking up the inner 1-D loop into ``bufsize`` chunks (with a possible + remainder). + +Again, the hardware error flags are checked at the end of each 1-D +loop. + + +Final output manipulation +------------------------- + +Ufuncs allow other array-like classes to be passed seamlessly through +the interface in that inputs of a particular class will induce the +outputs to be of that same class. The mechanism by which this works is +the following. If any of the inputs are not ndarrays and define the +:obj:`~numpy.class.__array_wrap__` method, then the class with the largest +:obj:`~numpy.class.__array_priority__` attribute determines the type of all the +outputs (with the exception of any output arrays passed in). The +:obj:`~numpy.class.__array_wrap__` method of the input array will be called +with the ndarray being returned from the ufunc as its input. There are two +calling styles of the :obj:`~numpy.class.__array_wrap__` function supported. +The first takes the ndarray as the first argument and a tuple of "context" as +the second argument. The context is (ufunc, arguments, output argument +number). This is the first call tried. If a ``TypeError`` occurs, then the +function is called with just the ndarray as the first argument. + + +Methods +------- + +There are three methods of ufuncs that require calculation similar to +the general-purpose ufuncs. These are :meth:`ufunc.reduce`, +:meth:`ufunc.accumulate`, and :meth:`ufunc.reduceat`. Each of these +methods requires a setup command followed by a +loop. There are four loop styles possible for the methods +corresponding to no-elements, one-element, strided-loop, and buffered-loop. +These are the same basic loop styles as implemented for the +general-purpose function call except for the no-element and one-element +cases which are special-cases occurring when the input array +objects have 0 and 1 elements respectively. + + +Setup +^^^^^ + +The setup function for all three methods is ``construct_reduce``. +This function creates a reducing loop object and fills it with the +parameters needed to complete the loop. All of the methods only work +on ufuncs that take 2-inputs and return 1 output. Therefore, the +underlying 1-D loop is selected assuming a signature of ``[otype, +otype, otype]`` where ``otype`` is the requested reduction +datatype. The buffer size and error handling are then retrieved from +(per-thread) global storage. For small arrays that are misaligned or +have incorrect datatype, a copy is made so that the un-buffered +section of code is used. Then, the looping strategy is selected. If +there is 1 element or 0 elements in the array, then a simple looping +method is selected. If the array is not misaligned and has the +correct datatype, then strided looping is selected. Otherwise, +buffered looping must be performed. Looping parameters are then +established, and the return array is constructed. The output array is +of a different :term:`shape` depending on whether the method is +:meth:`reduce <ufunc.reduce>`, :meth:`accumulate <ufunc.accumulate>`, or +:meth:`reduceat <ufunc.reduceat>`. If an output array is already provided, then +its shape is checked. If the output array is not C-contiguous, +aligned, and of the correct data type, then a temporary copy is made +with the :c:data:`NPY_ARRAY_WRITEBACKIFCOPY` flag set. In this way, the methods +will be able to work with a well-behaved output array but the result will be +copied back into the true output array when +:c:func:`PyArray_ResolveWritebackIfCopy` is called at function completion. +Finally, iterators are set up to loop over the correct :term:`axis` +(depending on the value of axis provided to the method) and the setup +routine returns to the actual computation routine. + + +:meth:`Reduce <ufunc.reduce>` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: + triple: ufunc; methods; reduce + +All of the ufunc methods use the same underlying 1-D computational +loops with input and output arguments adjusted so that the appropriate +reduction takes place. For example, the key to the functioning of +:meth:`reduce <ufunc.reduce>` is that the 1-D loop is called with the output +and the second input pointing to the same position in memory and both having +a step-size of 0. The first input is pointing to the input array with a +step-size given by the appropriate stride for the selected axis. In this +way, the operation performed is + +.. math:: + :nowrap: + + \begin{align*} + o & = & i[0] \\ + o & = & i[k]\textrm{<op>}o\quad k=1\ldots N + \end{align*} + +where :math:`N+1` is the number of elements in the input, :math:`i`, +:math:`o` is the output, and :math:`i[k]` is the +:math:`k^{\textrm{th}}` element of :math:`i` along the selected axis. +This basic operation is repeated for arrays with greater than 1 +dimension so that the reduction takes place for every 1-D sub-array +along the selected axis. An iterator with the selected dimension +removed handles this looping. + +For buffered loops, care must be taken to copy and cast data before +the loop function is called because the underlying loop expects +aligned data of the correct datatype (including byteorder). The +buffered loop must handle this copying and casting prior to calling +the loop function on chunks no greater than the user-specified +``bufsize``. + + +:meth:`Accumulate <ufunc.accumulate>` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: + triple: ufunc; methods; accumulate + +The :meth:`accumulate <ufunc.accumulate>` method is very similar to +the :meth:`reduce <ufunc.reduce>` method in that +the output and the second input both point to the output. The +difference is that the second input points to memory one stride behind +the current output pointer. Thus, the operation performed is + +.. math:: + :nowrap: + + \begin{align*} + o[0] & = & i[0] \\ + o[k] & = & i[k]\textrm{<op>}o[k-1]\quad k=1\ldots N. + \end{align*} + +The output has the same shape as the input and each 1-D loop operates +over :math:`N` elements when the shape in the selected axis is :math:`N+1`. +Again, buffered loops take care to copy and cast the data before +calling the underlying 1-D computational loop. + + +:meth:`Reduceat <ufunc.reduceat>` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: + triple: ufunc; methods; reduceat + single: ufunc + +The :meth:`reduceat <ufunc.reduceat>` function is a generalization of both the +:meth:`reduce <ufunc.reduce>` and :meth:`accumulate <ufunc.accumulate>` +functions. It implements a :meth:`reduce <ufunc.reduce>` over ranges of +the input array specified by indices. The extra indices argument is checked to +be sure that every input is not too large for the input array along +the selected dimension before the loop calculations take place. The +loop implementation is handled using code that is very similar to the +:meth:`reduce <ufunc.reduce>` code repeated as many times as there are elements +in the indices input. In particular: the first input pointer passed to the +underlying 1-D computational loop points to the input array at the +correct location indicated by the index array. In addition, the output +pointer and the second input pointer passed to the underlying 1-D loop +point to the same position in memory. The size of the 1-D +computational loop is fixed to be the difference between the current +index and the next index (when the current index is the last index, +then the next index is assumed to be the length of the array along the +selected dimension). In this way, the 1-D loop will implement a +:meth:`reduce <ufunc.reduce>` over the specified indices. + +Misaligned or a loop datatype that does not match the input and/or +output datatype is handled using buffered code wherein data is +copied to a temporary buffer and cast to the correct datatype if +necessary prior to calling the underlying 1-D function. The temporary +buffers are created in (element) sizes no bigger than the user +settable buffer-size value. Thus, the loop must be flexible enough to +call the underlying 1-D computational loop enough times to complete +the total calculation in chunks no bigger than the buffer-size. diff --git a/doc/source/dev/internals.rst b/doc/source/dev/internals.rst new file mode 100644 index 000000000000..14e5f3141e00 --- /dev/null +++ b/doc/source/dev/internals.rst @@ -0,0 +1,175 @@ +.. currentmodule:: numpy + +.. _numpy-internals: + +************************************* +Internal organization of NumPy arrays +************************************* + +It helps to understand a bit about how NumPy arrays are handled under the covers +to help understand NumPy better. This section will not go into great detail. +Those wishing to understand the full details are requested to refer to Travis +Oliphant's book `Guide to NumPy <http://web.mit.edu/dvp/Public/numpybook.pdf>`_. + +NumPy arrays consist of two major components: the raw array data (from now on, +referred to as the data buffer), and the information about the raw array data. +The data buffer is typically what people think of as arrays in C or Fortran, +a :term:`contiguous` (and fixed) block of memory containing fixed-sized data +items. NumPy also contains a significant set of data that describes how to +interpret the data in the data buffer. This extra information contains (among +other things): + + 1) The basic data element's size in bytes. + 2) The start of the data within the data buffer (an offset relative to the + beginning of the data buffer). + 3) The number of :term:`dimensions <dimension>` and the size of each dimension. + 4) The separation between elements for each dimension (the :term:`stride`). + This does not have to be a multiple of the element size. + 5) The byte order of the data (which may not be the native byte order). + 6) Whether the buffer is read-only. + 7) Information (via the :class:`dtype` object) about the interpretation of the + basic data element. The basic data element may be as simple as an int or a + float, or it may be a compound object (e.g., + :term:`struct-like <structured data type>`), a fixed character field, + or Python object pointers. + 8) Whether the array is to be interpreted as :term:`C-order <C order>` + or :term:`Fortran-order <Fortran order>`. + +This arrangement allows for the very flexible use of arrays. One thing that it +allows is simple changes to the metadata to change the interpretation of the +array buffer. Changing the byteorder of the array is a simple change involving +no rearrangement of the data. The :term:`shape` of the array can be changed very +easily without changing anything in the data buffer or any data copying at all. + +Among other things that are made possible is one can create a new array metadata +object that uses the same data buffer +to create a new :term:`view` of that data buffer that has a different +interpretation of the buffer (e.g., different shape, offset, byte order, +strides, etc) but shares the same data bytes. Many operations in NumPy do just +this such as :term:`slicing <python:slice>`. Other operations, such as +transpose, don't move data elements around in the array, but rather change the +information about the shape and strides so that the indexing of the array +changes, but the data in the doesn't move. + +Typically these new versions of the array metadata but the same data buffer are +new views into the data buffer. There is a different :class:`ndarray` object, +but it uses the same data buffer. This is why it is necessary to force copies +through the use of the :func:`copy` method if one really wants to make a new +and independent copy of the data buffer. + +New views into arrays mean the object reference counts for the data buffer +increase. Simply doing away with the original array object will not remove the +data buffer if other views of it still exist. + +Multidimensional array indexing order issues +============================================ + +.. seealso:: :ref:`basics.indexing` + +What is the right way to index +multi-dimensional arrays? Before you jump to conclusions about the one and +true way to index multi-dimensional arrays, it pays to understand why this is +a confusing issue. This section will try to explain in detail how NumPy +indexing works and why we adopt the convention we do for images, and when it +may be appropriate to adopt other conventions. + +The first thing to understand is +that there are two conflicting conventions for indexing 2-dimensional arrays. +Matrix notation uses the first index to indicate which row is being selected and +the second index to indicate which column is selected. This is opposite the +geometrically oriented-convention for images where people generally think the +first index represents x position (i.e., column) and the second represents y +position (i.e., row). This alone is the source of much confusion; +matrix-oriented users and image-oriented users expect two different things with +regard to indexing. + +The second issue to understand is how indices correspond +to the order in which the array is stored in memory. In Fortran, the first index +is the most rapidly varying index when moving through the elements of a +two-dimensional array as it is stored in memory. If you adopt the matrix +convention for indexing, then this means the matrix is stored one column at a +time (since the first index moves to the next row as it changes). Thus Fortran +is considered a Column-major language. C has just the opposite convention. In +C, the last index changes most rapidly as one moves through the array as +stored in memory. Thus C is a Row-major language. The matrix is stored by +rows. Note that in both cases it presumes that the matrix convention for +indexing is being used, i.e., for both Fortran and C, the first index is the +row. Note this convention implies that the indexing convention is invariant +and that the data order changes to keep that so. + +But that's not the only way +to look at it. Suppose one has large two-dimensional arrays (images or +matrices) stored in data files. Suppose the data are stored by rows rather than +by columns. If we are to preserve our index convention (whether matrix or +image) that means that depending on the language we use, we may be forced to +reorder the data if it is read into memory to preserve our indexing +convention. For example, if we read row-ordered data into memory without +reordering, it will match the matrix indexing convention for C, but not for +Fortran. Conversely, it will match the image indexing convention for Fortran, +but not for C. For C, if one is using data stored in row order, and one wants +to preserve the image index convention, the data must be reordered when +reading into memory. + +In the end, what you do for Fortran or C depends on +which is more important, not reordering data or preserving the indexing +convention. For large images, reordering data is potentially expensive, and +often the indexing convention is inverted to avoid that. + +The situation with +NumPy makes this issue yet more complicated. The internal machinery of NumPy +arrays is flexible enough to accept any ordering of indices. One can simply +reorder indices by manipulating the internal :term:`stride` information for +arrays without reordering the data at all. NumPy will know how to map the new +index order to the data without moving the data. + +So if this is true, why not choose +the index order that matches what you most expect? In particular, why not define +row-ordered images to use the image convention? (This is sometimes referred +to as the Fortran convention vs the C convention, thus the 'C' and 'FORTRAN' +order options for array ordering in NumPy.) The drawback of doing this is +potential performance penalties. It's common to access the data sequentially, +either implicitly in array operations or explicitly by looping over rows of an +image. When that is done, then the data will be accessed in non-optimal order. +As the first index is incremented, what is actually happening is that elements +spaced far apart in memory are being sequentially accessed, with usually poor +memory access speeds. For example, for a two-dimensional image ``im`` defined so +that ``im[0, 10]`` represents the value at ``x = 0``, ``y = 10``. To be +consistent with usual Python behavior then ``im[0]`` would represent a column +at ``x = 0``. Yet that data would be spread over the whole array since the data +are stored in row order. Despite the flexibility of NumPy's indexing, it can't +really paper over the fact basic operations are rendered inefficient because of +data order or that getting contiguous subarrays is still awkward (e.g., +``im[:, 0]`` for the first row, vs ``im[0]``). Thus one can't use an idiom such +as for row in ``im``; for col in ``im`` does work, but doesn't yield contiguous +column data. + +As it turns out, NumPy is +smart enough when dealing with :ref:`ufuncs <ufuncs-internals>` to determine +which index is the most rapidly varying one in memory and uses that for the +innermost loop. Thus for ufuncs, there is no large intrinsic advantage to +either approach in most cases. On the other hand, use of :attr:`ndarray.flat` +with a FORTRAN ordered array will lead to non-optimal memory access as adjacent +elements in the flattened array (iterator, actually) are not contiguous in +memory. + +Indeed, the fact is that Python +indexing on lists and other sequences naturally leads to an outside-to-inside +ordering (the first index gets the largest grouping, the next largest, +and the last gets the smallest element). Since image data are normally stored +in rows, this corresponds to the position within rows being the last item +indexed. + +If you do want to use Fortran ordering realize that +there are two approaches to consider: 1) accept that the first index is just not +the most rapidly changing in memory and have all your I/O routines reorder +your data when going from memory to disk or visa versa, or use NumPy's +mechanism for mapping the first index to the most rapidly varying data. We +recommend the former if possible. The disadvantage of the latter is that many +of NumPy's functions will yield arrays without Fortran ordering unless you are +careful to use the ``order`` keyword. Doing this would be highly inconvenient. + +Otherwise, we recommend simply learning to reverse the usual order of indices +when accessing elements of an array. Granted, it goes against the grain, but +it is more in line with Python semantics and the natural order of the data. + + diff --git a/doc/source/dev/gitwash/pull_button.png b/doc/source/dev/pull_button.png similarity index 100% rename from doc/source/dev/gitwash/pull_button.png rename to doc/source/dev/pull_button.png diff --git a/doc/source/dev/releasing.rst b/doc/source/dev/releasing.rst new file mode 100644 index 000000000000..75081aec1675 --- /dev/null +++ b/doc/source/dev/releasing.rst @@ -0,0 +1,16 @@ +=================== +Releasing a version +=================== + +------------------------ +How to Prepare a Release +------------------------ + +.. include:: ../../HOWTO_RELEASE.rst.txt + +----------------------- +Step-by-Step Directions +----------------------- + +.. include:: ../../RELEASE_WALKTHROUGH.rst.txt + diff --git a/doc/source/dev/reviewer_guidelines.rst b/doc/source/dev/reviewer_guidelines.rst new file mode 100644 index 000000000000..ffac85f7720a --- /dev/null +++ b/doc/source/dev/reviewer_guidelines.rst @@ -0,0 +1,181 @@ +.. _reviewer-guidelines: + +=================== +Reviewer Guidelines +=================== + +Reviewing open pull requests (PRs) helps move the project forward. We encourage +people outside the project to get involved as well; it's a great way to get +familiar with the codebase. + +Who can be a reviewer? +====================== + +Reviews can come from outside the NumPy team -- we welcome contributions from +domain experts (for instance, `linalg` or `fft`) or maintainers of other +projects. You do not need to be a NumPy maintainer (a NumPy team member with +permission to merge a PR) to review. + +If we do not know you yet, consider introducing yourself in `the mailing list or +Slack <https://numpy.org/community/>`_ before you start reviewing pull requests. + +Communication Guidelines +======================== + +- Every PR, good or bad, is an act of generosity. Opening with a positive + comment will help the author feel rewarded, and your subsequent remarks may be + heard more clearly. You may feel good also. +- Begin if possible with the large issues, so the author knows they've been + understood. Resist the temptation to immediately go line by line, or to open + with small pervasive issues. +- You are the face of the project, and NumPy some time ago decided `the kind of + project it will be <https://numpy.org/code-of-conduct/>`_: open, empathetic, + welcoming, friendly and patient. Be `kind + <https://youtu.be/tzFWz5fiVKU?t=49m30s>`_ to contributors. +- Do not let perfect be the enemy of the good, particularly for documentation. + If you find yourself making many small suggestions, or being too nitpicky on + style or grammar, consider merging the current PR when all important concerns + are addressed. Then, either push a commit directly (if you are a maintainer) + or open a follow-up PR yourself. +- If you need help writing replies in reviews, check out some + :ref:`standard replies for reviewing<saved-replies>`. + +Reviewer Checklist +================== + +- Is the intended behavior clear under all conditions? Some things to watch: + - What happens with unexpected inputs like empty arrays or nan/inf values? + - Are axis or shape arguments tested to be `int` or `tuples`? + - Are unusual `dtypes` tested if a function supports those? +- Should variable names be improved for clarity or consistency? +- Should comments be added, or rather removed as unhelpful or extraneous? +- Does the documentation follow the :ref:`NumPy guidelines<howto-document>`? Are + the docstrings properly formatted? +- Does the code follow NumPy's :ref:`Stylistic Guidelines<stylistic-guidelines>`? +- If you are a maintainer, and it is not obvious from the PR description, add a + short explanation of what a branch did to the merge message and, if closing an + issue, also add "Closes gh-123" where 123 is the issue number. +- For code changes, at least one maintainer (i.e. someone with commit rights) + should review and approve a pull request. If you are the first to review a + PR and approve of the changes use the GitHub `approve review + <https://help.github.com/articles/reviewing-changes-in-pull-requests/>`_ tool + to mark it as such. If a PR is straightforward, for example it's a clearly + correct bug fix, it can be merged straight away. If it's more complex or + changes public API, please leave it open for at least a couple of days so + other maintainers get a chance to review. +- If you are a subsequent reviewer on an already approved PR, please use the + same review method as for a new PR (focus on the larger issues, resist the + temptation to add only a few nitpicks). If you have commit rights and think + no more review is needed, merge the PR. + +For maintainers +--------------- + +- Make sure all automated CI tests pass before merging a PR, and that the + :ref:`documentation builds <building-docs>` without any errors. +- In case of merge conflicts, ask the PR submitter to :ref:`rebase on main + <rebasing-on-main>`. +- For PRs that add new features or are in some way complex, wait at least a day + or two before merging it. That way, others get a chance to comment before the + code goes in. Consider adding it to the release notes. +- When merging contributions, a committer is responsible for ensuring that those + meet the requirements outlined in the :ref:`Development process guidelines + <guidelines>` for NumPy. Also, check that new features and backwards + compatibility breaks were discussed on the `numpy-discussion mailing list + <https://mail.python.org/mailman/listinfo/numpy-discussion>`_. +- Squashing commits or cleaning up commit messages of a PR that you consider too + messy is OK. Remember to retain the original author's name when doing this. + Make sure commit messages follow the :ref:`rules for NumPy + <writing-the-commit-message>`. +- When you want to reject a PR: if it's very obvious, you can just close it and + explain why. If it's not, then it's a good idea to first explain why you + think the PR is not suitable for inclusion in NumPy and then let a second + committer comment or close. + +GitHub Workflow +--------------- + +When reviewing pull requests, please use workflow tracking features on GitHub as +appropriate: + +- After you have finished reviewing, if you want to ask for the submitter to + make changes, change your review status to "Changes requested." This can be + done on GitHub, PR page, Files changed tab, Review changes (button on the top + right). +- If you're happy about the current status, mark the pull request as Approved + (same way as Changes requested). Alternatively (for maintainers): merge + the pull request, if you think it is ready to be merged. + +It may be helpful to have a copy of the pull request code checked out on your +own machine so that you can play with it locally. You can use the `GitHub CLI +<https://docs.github.com/en/github/getting-started-with-github/github-cli>`_ to +do this by clicking the ``Open with`` button in the upper right-hand corner of +the PR page. + +Assuming you have your :ref:`development environment<development-environment>` +set up, you can now build the code and test it. + +.. _saved-replies: + +Standard replies for reviewing +============================== + +It may be helpful to store some of these in GitHub's `saved +replies <https://github.com/settings/replies/>`_ for reviewing: + +**Usage question** + .. code-block:: md + + You are asking a usage question. The issue tracker is for bugs and new features. + I'm going to close this issue, feel free to ask for help via our [help channels](https://numpy.org/gethelp/). + +**You’re welcome to update the docs** + .. code-block:: md + + Please feel free to offer a pull request updating the documentation if you feel it could be improved. + +**Self-contained example for bug** + .. code-block:: md + + Please provide a [self-contained example code](https://stackoverflow.com/help/mcve), including imports and data (if possible), so that other contributors can just run it and reproduce your issue. + Ideally your example code should be minimal. + +**Software versions** + .. code-block:: md + + To help diagnose your issue, please paste the output of: + ``` + python -c 'import numpy; print(numpy.version.version)' + ``` + Thanks. + +**Code blocks** + .. code-block:: md + + Readability can be greatly improved if you [format](https://help.github.com/articles/creating-and-highlighting-code-blocks/) your code snippets and complete error messages appropriately. + You can edit your issue descriptions and comments at any time to improve readability. + This helps maintainers a lot. Thanks! + +**Linking to code** + .. code-block:: md + + For clarity's sake, you can link to code like [this](https://help.github.com/articles/creating-a-permanent-link-to-a-code-snippet/). + +**Better description and title** + .. code-block:: md + + Please make the title of the PR more descriptive. + The title will become the commit message when this is merged. + You should state what issue (or PR) it fixes/resolves in the description using the syntax described [here](https://docs.github.com/en/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword). + +**Regression test needed** + .. code-block:: md + + Please add a [non-regression test](https://en.wikipedia.org/wiki/Non-regression_testing) that would fail at main but pass in this PR. + +**Don’t change unrelated** + .. code-block:: md + + Please do not change unrelated lines. It makes your contribution harder to review and may introduce merge conflicts to other pull requests. + +.. include:: gitwash/git_links.inc diff --git a/doc/source/dev/underthehood.rst b/doc/source/dev/underthehood.rst new file mode 100644 index 000000000000..c0f37fd5b493 --- /dev/null +++ b/doc/source/dev/underthehood.rst @@ -0,0 +1,15 @@ +.. _underthehood: + +=========================================== +Under-the-hood Documentation for developers +=========================================== + +These documents are intended as a low-level look into NumPy; focused +towards developers. + +.. toctree:: + :maxdepth: 1 + + internals + internals.code-explanations + alignment diff --git a/doc/source/docs/howto_build_docs.rst b/doc/source/docs/howto_build_docs.rst deleted file mode 100644 index 383bed96d1c2..000000000000 --- a/doc/source/docs/howto_build_docs.rst +++ /dev/null @@ -1,87 +0,0 @@ -.. _howto-build-docs: - -========================================= -Building the NumPy API and reference docs -========================================= - -We currently use Sphinx_ for generating the API and reference -documentation for NumPy. You will need Sphinx 1.0.1 or newer. - -If you only want to get the documentation, note that pre-built -versions can be found at - - http://docs.scipy.org/ - -in several different formats. - -.. _Sphinx: http://sphinx.pocoo.org - - -Instructions ------------- - -If you obtained NumPy via git, get also the git submodules that contain -additional parts required for building the documentation:: - - git submodule init - git submodule update - -In addition, building the documentation requires the Sphinx extension -`plot_directive`, which is shipped with Matplotlib_. This Sphinx extension can -be installed by installing Matplotlib. You will also need python3.6. - -Since large parts of the main documentation are stored in -docstrings, you will need to first build NumPy, and install it so -that the correct version is imported by - - >>> import numpy - -Note that you can eg. install NumPy to a temporary location and set -the PYTHONPATH environment variable appropriately. - -After NumPy is installed, install SciPy since some of the plots in the random -module require `scipy.special` to display properly. Now you are ready to -generate the docs, so write:: - - make html - -in the ``doc/`` directory. If all goes well, this will generate a -``build/html`` subdirectory containing the built documentation. Note -that building the documentation on Windows is currently not actively -supported, though it should be possible. (See Sphinx_ documentation -for more information.) - -To build the PDF documentation, do instead:: - - make latex - make -C build/latex all-pdf - -You will need to have Latex installed for this. - -Instead of the above, you can also do:: - - make dist - -which will rebuild NumPy, install it to a temporary location, and -build the documentation in all formats. This will most likely again -only work on Unix platforms. - -The documentation for NumPy distributed at http://docs.scipy.org in html and -pdf format is also built with ``make dist``. See `HOWTO RELEASE`_ for details on -how to update http://docs.scipy.org. - -.. _Matplotlib: http://matplotlib.org/ -.. _HOWTO RELEASE: https://github.com/numpy/numpy/blob/master/doc/HOWTO_RELEASE.rst.txt - -Sphinx extensions ------------------ - -NumPy's documentation uses several custom extensions to Sphinx. These -are shipped in the ``sphinxext/`` directory (as git submodules, as discussed -above), and are automatically enabled when building NumPy's documentation. - -If you want to make use of these extensions in third-party -projects, they are available on PyPi_ as the numpydoc_ package. - -.. _PyPi: http://python.org/pypi -.. _numpydoc: http://python.org/pypi/numpydoc diff --git a/doc/source/docs/howto_document.rst b/doc/source/docs/howto_document.rst deleted file mode 100644 index de7d06cf8ece..000000000000 --- a/doc/source/docs/howto_document.rst +++ /dev/null @@ -1,61 +0,0 @@ -.. _howto-document: - - -A Guide to NumPy/SciPy Documentation -==================================== - -When using `Sphinx <http://sphinx.pocoo.org/>`__ in combination with the -numpy conventions, you should use the ``numpydoc`` extension so that your -docstrings will be handled correctly. For example, Sphinx will extract the -``Parameters`` section from your docstring and convert it into a field -list. Using ``numpydoc`` will also avoid the reStructuredText errors produced -by plain Sphinx when it encounters numpy docstring conventions like -section headers (e.g. ``-------------``) that sphinx does not expect to -find in docstrings. - -Some features described in this document require a recent version of -``numpydoc``. For example, the **Yields** section was added in -``numpydoc`` 0.6. - -It is available from: - -* `numpydoc on PyPI <http://pypi.python.org/pypi/numpydoc>`_ -* `numpydoc on GitHub <https://github.com/numpy/numpydoc/>`_ - -Note that for documentation within numpy, it is not necessary to do -``import numpy as np`` at the beginning of an example. However, some -sub-modules, such as ``fft``, are not imported by default, and you have to -include them explicitly:: - - import numpy.fft - -after which you may use it:: - - np.fft.fft2(...) - -.. rubric:: - **For convenience the** `formatting standard`_ **is included below with an - example** - -.. include:: ../../sphinxext/doc/format.rst - -.. _example: - -Example Source -============== - -.. literalinclude:: ../../sphinxext/doc/example.py - - - -Example Rendered -================ - -.. ifconfig:: python_version_major < '3' - - The example is rendered only when sphinx is run with python3 and above - -.. automodule:: doc.example - :members: - -.. _`formatting standard`: https://numpydoc.readthedocs.io/en/latest/format.html diff --git a/doc/source/docs/index.rst b/doc/source/docs/index.rst deleted file mode 100644 index 7d8b1bcb405c..000000000000 --- a/doc/source/docs/index.rst +++ /dev/null @@ -1,11 +0,0 @@ -.. _documentation: - -NumPy's Documentation -===================== - -.. toctree:: - :maxdepth: 2 - - howto_document - howto_build_docs - diff --git a/doc/source/doxyfile b/doc/source/doxyfile new file mode 100644 index 000000000000..ea45b9578309 --- /dev/null +++ b/doc/source/doxyfile @@ -0,0 +1,340 @@ +# Doxyfile 1.8.18 +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = NumPy +PROJECT_NUMBER = +PROJECT_BRIEF = "NumPy is the fundamental package for scientific computing in Python" +PROJECT_LOGO = +OUTPUT_DIRECTORY = @ROOT_DIR/doc/build/doxygen +CREATE_SUBDIRS = NO +ALLOW_UNICODE_NAMES = NO +OUTPUT_LANGUAGE = English +OUTPUT_TEXT_DIRECTION = None +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = @ROOT_DIR +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = YES +JAVADOC_BANNER = NO +QT_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 4 +ALIASES = +ALIASES += "rst=\verbatim embed:rst:leading-asterisk" +ALIASES += "endrst=\endverbatim" +OPTIMIZE_OUTPUT_FOR_C = NO +OPTIMIZE_OUTPUT_JAVA = NO +OPTIMIZE_FOR_FORTRAN = NO +OPTIMIZE_OUTPUT_VHDL = NO +OPTIMIZE_OUTPUT_SLICE = NO +EXTENSION_MAPPING = +MARKDOWN_SUPPORT = YES +TOC_INCLUDE_HEADINGS = 5 +AUTOLINK_SUPPORT = YES +BUILTIN_STL_SUPPORT = NO +CPP_CLI_SUPPORT = NO +SIP_SUPPORT = NO +IDL_PROPERTY_SUPPORT = YES +DISTRIBUTE_GROUP_DOC = NO +GROUP_NESTED_COMPOUNDS = NO +SUBGROUPING = YES +INLINE_GROUPED_CLASSES = NO +INLINE_SIMPLE_STRUCTS = NO +TYPEDEF_HIDES_STRUCT = NO +LOOKUP_CACHE_SIZE = 0 +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = NO +EXTRACT_PRIVATE = NO +EXTRACT_PRIV_VIRTUAL = NO +EXTRACT_PACKAGE = NO +EXTRACT_STATIC = NO +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +EXTRACT_ANON_NSPACES = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = NO +HIDE_COMPOUND_REFERENCE= NO +SHOW_INCLUDE_FILES = YES +SHOW_GROUPED_MEMB_INC = NO +FORCE_LOCAL_INCLUDES = NO +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_MEMBERS_CTORS_1ST = NO +SORT_GROUP_NAMES = NO +SORT_BY_SCOPE_NAME = NO +STRICT_PROTO_MATCHING = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_FILES = YES +SHOW_NAMESPACES = YES +FILE_VERSION_FILTER = +LAYOUT_FILE = +CITE_BIB_FILES = +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = no +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = NO +WARN_AS_ERROR = NO +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = +INPUT_ENCODING = UTF-8 +FILE_PATTERNS = *.h, *.hpp +RECURSIVE = YES +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXCLUDE_SYMBOLS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO +FILTER_SOURCE_PATTERNS = +USE_MDFILE_AS_MAINPAGE = +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = NO +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +REFERENCES_LINK_SOURCE = YES +SOURCE_TOOLTIPS = YES +USE_HTAGS = NO +VERBATIM_HEADERS = YES +CLANG_ASSISTED_PARSING = NO +CLANG_OPTIONS = +CLANG_DATABASE_PATH = +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = YES +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = NO +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_EXTRA_STYLESHEET = +HTML_EXTRA_FILES = +HTML_COLORSTYLE_HUE = 220 +HTML_COLORSTYLE_SAT = 100 +HTML_COLORSTYLE_GAMMA = 80 +HTML_TIMESTAMP = NO +HTML_DYNAMIC_MENUS = YES +HTML_DYNAMIC_SECTIONS = NO +HTML_INDEX_NUM_ENTRIES = 100 +GENERATE_DOCSET = NO +DOCSET_FEEDNAME = "Doxygen generated docs" +DOCSET_BUNDLE_ID = org.doxygen.Project +DOCSET_PUBLISHER_ID = org.doxygen.Publisher +DOCSET_PUBLISHER_NAME = Publisher +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +CHM_INDEX_ENCODING = +BINARY_TOC = NO +TOC_EXPAND = NO +GENERATE_QHP = NO +QCH_FILE = +QHP_NAMESPACE = org.doxygen.Project +QHP_VIRTUAL_FOLDER = doc +QHP_CUST_FILTER_NAME = +QHP_CUST_FILTER_ATTRS = +QHP_SECT_FILTER_ATTRS = +QHG_LOCATION = +GENERATE_ECLIPSEHELP = NO +ECLIPSE_DOC_ID = org.doxygen.Project +DISABLE_INDEX = NO +GENERATE_TREEVIEW = NO +ENUM_VALUES_PER_LINE = 4 +TREEVIEW_WIDTH = 250 +EXT_LINKS_IN_WINDOW = NO +HTML_FORMULA_FORMAT = png +FORMULA_FONTSIZE = 10 +FORMULA_TRANSPARENT = YES +FORMULA_MACROFILE = +USE_MATHJAX = NO +MATHJAX_FORMAT = HTML-CSS +MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@@2 +MATHJAX_EXTENSIONS = +MATHJAX_CODEFILE = +SEARCHENGINE = YES +SERVER_BASED_SEARCH = NO +EXTERNAL_SEARCH = NO +SEARCHENGINE_URL = +SEARCHDATA_FILE = searchdata.xml +EXTERNAL_SEARCH_ID = +EXTRA_SEARCH_MAPPINGS = +#--------------------------------------------------------------------------- +# Configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = +MAKEINDEX_CMD_NAME = makeindex +LATEX_MAKEINDEX_CMD = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4 +EXTRA_PACKAGES = +LATEX_HEADER = +LATEX_FOOTER = +LATEX_EXTRA_STYLESHEET = +LATEX_EXTRA_FILES = +PDF_HYPERLINKS = YES +USE_PDFLATEX = YES +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +LATEX_SOURCE_CODE = NO +LATEX_BIB_STYLE = plain +LATEX_TIMESTAMP = NO +LATEX_EMOJI_DIRECTORY = +#--------------------------------------------------------------------------- +# Configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +RTF_SOURCE_CODE = NO +#--------------------------------------------------------------------------- +# Configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_SUBDIR = +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# Configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = YES +XML_OUTPUT = xml +XML_PROGRAMLISTING = YES +XML_NS_MEMB_FILE_SCOPE = NO +#--------------------------------------------------------------------------- +# Configuration options related to the DOCBOOK output +#--------------------------------------------------------------------------- +GENERATE_DOCBOOK = NO +DOCBOOK_OUTPUT = docbook +DOCBOOK_PROGRAMLISTING = NO +#--------------------------------------------------------------------------- +# Configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# Configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = YES +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration options related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +EXTERNAL_PAGES = YES +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = YES +DIA_PATH = +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = NO +DOT_NUM_THREADS = 0 +DOT_FONTNAME = Helvetica +DOT_FONTSIZE = 10 +DOT_FONTPATH = +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +UML_LIMIT_NUM_FIELDS = 10 +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = NO +CALLER_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = png +INTERACTIVE_SVG = NO +DOT_PATH = +DOTFILE_DIRS = +MSCFILE_DIRS = +DIAFILE_DIRS = +PLANTUML_JAR_PATH = +PLANTUML_CFG_FILE = +PLANTUML_INCLUDE_PATH = +DOT_GRAPH_MAX_NODES = 50 +MAX_DOT_GRAPH_DEPTH = 0 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES diff --git a/doc/source/f2py/advanced.rst b/doc/source/f2py/advanced.rst index c9f3862e62c6..c8efbaadb426 100644 --- a/doc/source/f2py/advanced.rst +++ b/doc/source/f2py/advanced.rst @@ -1,45 +1,95 @@ -====================== -Advanced F2PY usages -====================== +======================== +Advanced F2PY use cases +======================== -Adding self-written functions to F2PY generated modules -======================================================= +Adding user-defined functions to F2PY generated modules +========================================================= -Self-written Python C/API functions can be defined inside +User-defined Python C/API functions can be defined inside signature files using ``usercode`` and ``pymethoddef`` statements (they must be used inside the ``python module`` block). For example, the following signature file ``spam.pyf`` -.. include:: spam.pyf +.. include:: ./code/spam.pyf :literal: wraps the C library function ``system()``:: f2py -c spam.pyf -In Python: +In Python this can then be used as: -.. include:: spam_session.dat - :literal: - -Modifying the dictionary of a F2PY generated module -=================================================== +.. literalinclude:: ./code/results/spam_session.dat + :language: python -The following example illustrates how to add a user-defined -variables to a F2PY generated extension module. Given the following -signature file +Adding user-defined variables +============================== -.. include:: var.pyf - :literal: +The following example illustrates how to add user-defined variables to a F2PY +generated extension module by modifying the dictionary of a F2PY generated +module. Consider the following signature file (compiled with ``f2py -c var.pyf``): -compile it as ``f2py -c var.pyf``. +.. literalinclude:: ./code/var.pyf + :language: fortran Notice that the second ``usercode`` statement must be defined inside -an ``interface`` block and where the module dictionary is available through -the variable ``d`` (see ``f2py var.pyf``-generated ``varmodule.c`` for +an ``interface`` block and the module dictionary is available through +the variable ``d`` (see ``varmodule.c`` generated by ``f2py var.pyf`` for additional details). -In Python: +Usage in Python: + +.. literalinclude:: ./code/results/var_session.dat + :language: python + + +Dealing with KIND specifiers +============================ + +Currently, F2PY can handle only ``<type spec>(kind=<kindselector>)`` +declarations where ``<kindselector>`` is a numeric integer (e.g. 1, 2, +4,...), but not a function call ``KIND(..)`` or any other +expression. F2PY needs to know what would be the corresponding C type +and a general solution for that would be too complicated to implement. + +However, F2PY provides a hook to overcome this difficulty, namely, +users can define their own <Fortran type> to <C type> maps. For +example, if Fortran 90 code contains:: + + REAL(kind=KIND(0.0D0)) ... + +then create a mapping file containing a Python dictionary:: + + {'real': {'KIND(0.0D0)': 'double'}} + +for instance. + +Use the ``--f2cmap`` command-line option to pass the file name to F2PY. +By default, F2PY assumes file name is ``.f2py_f2cmap`` in the current +working directory. + +More generally, the f2cmap file must contain a dictionary +with items:: + + <Fortran typespec> : {<selector_expr>:<C type>} + +that defines mapping between Fortran type:: + + <Fortran typespec>([kind=]<selector_expr>) + +and the corresponding <C type>. The <C type> can be one of the following:: + + char + signed_char + short + int + long_long + float + double + long_double + complex_float + complex_double + complex_long_double + string -.. include:: var_session.dat - :literal: +For more information, see the F2Py source code ``numpy/f2py/capi_maps.py``. diff --git a/doc/source/f2py/allocarr_session.dat b/doc/source/f2py/allocarr_session.dat deleted file mode 100644 index fc91959b738d..000000000000 --- a/doc/source/f2py/allocarr_session.dat +++ /dev/null @@ -1,27 +0,0 @@ ->>> import allocarr ->>> print allocarr.mod.__doc__ -b - 'f'-array(-1,-1), not allocated -foo - Function signature: - foo() - ->>> allocarr.mod.foo() - b is not allocated ->>> allocarr.mod.b = [[1,2,3],[4,5,6]] # allocate/initialize b ->>> allocarr.mod.foo() - b=[ - 1.000000 2.000000 3.000000 - 4.000000 5.000000 6.000000 - ] ->>> allocarr.mod.b # b is Fortran-contiguous -array([[ 1., 2., 3.], - [ 4., 5., 6.]],'f') ->>> allocarr.mod.b = [[1,2,3],[4,5,6],[7,8,9]] # reallocate/initialize b ->>> allocarr.mod.foo() - b=[ - 1.000000 2.000000 3.000000 - 4.000000 5.000000 6.000000 - 7.000000 8.000000 9.000000 - ] ->>> allocarr.mod.b = None # deallocate array ->>> allocarr.mod.foo() - b is not allocated diff --git a/doc/source/f2py/array_session.dat b/doc/source/f2py/array_session.dat deleted file mode 100644 index 069530d032ee..000000000000 --- a/doc/source/f2py/array_session.dat +++ /dev/null @@ -1,65 +0,0 @@ ->>> import arr ->>> from numpy import array ->>> print arr.foo.__doc__ -foo - Function signature: - a = foo(a,[overwrite_a]) -Required arguments: - a : input rank-2 array('d') with bounds (n,m) -Optional arguments: - overwrite_a := 0 input int -Return objects: - a : rank-2 array('d') with bounds (n,m) - ->>> a=arr.foo([[1,2,3], -... [4,5,6]]) -copied an array using PyArray_CopyFromObject: size=6, elsize=8 ->>> print a -[[ 1. 3. 4.] - [ 3. 5. 6.]] ->>> a.iscontiguous(), arr.has_column_major_storage(a) -(0, 1) ->>> b=arr.foo(a) # even if a is proper-contiguous -... # and has proper type, a copy is made -... # forced by intent(copy) attribute -... # to preserve its original contents -... -copied an array using copy_ND_array: size=6, elsize=8 ->>> print a -[[ 1. 3. 4.] - [ 3. 5. 6.]] ->>> print b -[[ 1. 4. 5.] - [ 2. 5. 6.]] ->>> b=arr.foo(a,overwrite_a=1) # a is passed directly to Fortran -... # routine and its contents is discarded -... ->>> print a -[[ 1. 4. 5.] - [ 2. 5. 6.]] ->>> print b -[[ 1. 4. 5.] - [ 2. 5. 6.]] ->>> a is b # a and b are actually the same objects -1 ->>> print arr.foo([1,2,3]) # different rank arrays are allowed -copied an array using PyArray_CopyFromObject: size=3, elsize=8 -[ 1. 1. 2.] ->>> print arr.foo([[[1],[2],[3]]]) -copied an array using PyArray_CopyFromObject: size=3, elsize=8 -[ [[ 1.] - [ 3.] - [ 4.]]] ->>> ->>> # Creating arrays with column major data storage order: -... ->>> s = arr.as_column_major_storage(array([[1,2,3],[4,5,6]])) -copied an array using copy_ND_array: size=6, elsize=4 ->>> arr.has_column_major_storage(s) -1 ->>> print s -[[1 2 3] - [4 5 6]] ->>> s2 = arr.as_column_major_storage(s) ->>> s2 is s # an array with column major storage order - # is returned immediately -1 diff --git a/doc/source/f2py/buildtools/cmake.rst b/doc/source/f2py/buildtools/cmake.rst new file mode 100644 index 000000000000..3ed5a2beea14 --- /dev/null +++ b/doc/source/f2py/buildtools/cmake.rst @@ -0,0 +1,60 @@ +.. _f2py-cmake: + +=================== +Using via ``cmake`` +=================== + +In terms of complexity, ``cmake`` falls between ``make`` and ``meson``. The +learning curve is steeper since CMake syntax is not pythonic and is closer to +``make`` with environment variables. + +However, the trade-off is enhanced flexibility and support for most architectures +and compilers. An introduction to the syntax is out of scope for this document, +but this `extensive CMake collection`_ of resources is great. + +.. note:: + + ``cmake`` is very popular for mixed-language systems, however support for + ``f2py`` is not particularly native or pleasant; and a more natural approach + is to consider :ref:`f2py-skbuild` + +Fibonacci Walkthrough (F77) +=========================== + +Returning to the ``fib`` example from :ref:`f2py-getting-started` section. + +.. literalinclude:: ./../code/fib1.f + :language: fortran + +We do not need to explicitly generate the ``python -m numpy.f2py fib1.f`` +output, which is ``fib1module.c``, which is beneficial. With this; we can now +initialize a ``CMakeLists.txt`` file as follows: + +.. literalinclude:: ./../code/CMakeLists.txt + :language: cmake + +A key element of the ``CMakeLists.txt`` file defined above is that the +``add_custom_command`` is used to generate the wrapper ``C`` files and then +added as a dependency of the actual shared library target via a +``add_custom_target`` directive which prevents the command from running every +time. Additionally, the method used for obtaining the ``fortranobject.c`` file +can also be used to grab the ``numpy`` headers on older ``cmake`` versions. + +This then works in the same manner as the other modules, although the naming +conventions are different and the output library is not automatically prefixed +with the ``cython`` information. + +.. code:: bash + + ls . + # CMakeLists.txt fib1.f + mkdir build && cd build + cmake .. + make + python -c "import numpy as np; import fibby; a = np.zeros(9); fibby.fib(a); print (a)" + # [ 0. 1. 1. 2. 3. 5. 8. 13. 21.] + +This is particularly useful where an existing toolchain already exists and +``scikit-build`` or other additional ``python`` dependencies are discouraged. + +.. _extensive CMake collection: https://cliutils.gitlab.io/modern-cmake/ diff --git a/doc/source/f2py/buildtools/distutils.rst b/doc/source/f2py/buildtools/distutils.rst new file mode 100644 index 000000000000..9abeee8b84a7 --- /dev/null +++ b/doc/source/f2py/buildtools/distutils.rst @@ -0,0 +1,78 @@ +.. _f2py-distutils: + +============================= +Using via `numpy.distutils` +============================= + +.. currentmodule:: numpy.distutils.core + +:mod:`numpy.distutils` is part of NumPy, and extends the standard Python +``distutils`` module to deal with Fortran sources and F2PY signature files, e.g. +compile Fortran sources, call F2PY to construct extension modules, etc. + +.. topic:: Example + + Consider the following ``setup_file.py`` for the ``fib`` and ``scalar`` + examples from :ref:`f2py-getting-started` section: + + .. literalinclude:: ./../code/setup_example.py + :language: python + + Running + + .. code-block:: bash + + python setup_example.py build + + will build two extension modules ``scalar`` and ``fib2`` to the + build directory. + +Extensions to ``distutils`` +=========================== + +:mod:`numpy.distutils` extends ``distutils`` with the following features: + +* :class:`Extension` class argument ``sources`` may contain Fortran source + files. In addition, the list ``sources`` may contain at most one + F2PY signature file, and in this case, the name of an Extension module must + match with the ``<modulename>`` used in signature file. It is + assumed that an F2PY signature file contains exactly one ``python + module`` block. + + If ``sources`` do not contain a signature file, then F2PY is used to scan + Fortran source files to construct wrappers to the Fortran codes. + + Additional options to the F2PY executable can be given using the + :class:`Extension` class argument ``f2py_options``. + +* The following new ``distutils`` commands are defined: + + ``build_src`` + to construct Fortran wrapper extension modules, among many other things. + ``config_fc`` + to change Fortran compiler options. + + Additionally, the ``build_ext`` and ``build_clib`` commands are also enhanced + to support Fortran sources. + + Run + + .. code-block:: bash + + python <setup.py file> config_fc build_src build_ext --help + + to see available options for these commands. + +* When building Python packages containing Fortran sources, one + can choose different Fortran compilers by using the ``build_ext`` + command option ``--fcompiler=<Vendor>``. Here ``<Vendor>`` can be one of the + following names (on ``linux`` systems):: + + absoft compaq fujitsu g95 gnu gnu95 intel intele intelem lahey nag nagfor nv pathf95 pg vast + + See ``numpy_distutils/fcompiler.py`` for an up-to-date list of + supported compilers for different platforms, or run + + .. code-block:: bash + + python -m numpy.f2py -c --help-fcompiler diff --git a/doc/source/f2py/buildtools/index.rst b/doc/source/f2py/buildtools/index.rst new file mode 100644 index 000000000000..aa41fd37f01a --- /dev/null +++ b/doc/source/f2py/buildtools/index.rst @@ -0,0 +1,102 @@ +.. _f2py-bldsys: + +======================= +F2PY and Build Systems +======================= + +In this section we will cover the various popular build systems and their usage +with ``f2py``. + +.. note:: + **As of November 2021** + + The default build system for ``F2PY`` has traditionally been the through the + enhanced ``numpy.distutils`` module. This module is based on ``distutils`` which + will be removed in ``Python 3.12.0`` in **October 2023**; ``setuptools`` does not + have support for Fortran or ``F2PY`` and it is unclear if it will be supported + in the future. Alternative methods are thus increasingly more important. + + +Basic Concepts +=============== + +Building an extension module which includes Python and Fortran consists of: + +- Fortran source(s) +- One or more generated files from ``f2py`` + + + A ``C`` wrapper file is always created + + Code with modules require an additional ``.f90`` wrapper + +- ``fortranobject.{c,h}`` + + + Distributed with ``numpy`` + + Can be queried via ``python -c "import numpy.f2py; print(numpy.f2py.get_include())"`` + +- NumPy headers + + + Can be queried via ``python -c "import numpy; print(numpy.get_include())"`` + +- Python libraries and development headers + +Broadly speaking there are three cases which arise when considering the outputs of ``f2py``: + +Fortran 77 programs + - Input file ``blah.f`` + - Generates + + + ``blahmodule.c`` + + ``f2pywrappers.f`` + + When no ``COMMON`` blocks are present only a ``C`` wrapper file is generated. + Wrappers are also generated to rewrite assumed shape arrays as automatic + arrays. + +Fortran 90 programs + - Input file ``blah.f90`` + - Generates: + + + ``blahmodule.c`` + + ``blah-f2pywrappers2.f90`` + + The secondary wrapper is used to handle code which is subdivided into + modules. It rewrites assumed shape arrays as automatic arrays. + +Signature files + - Input file ``blah.pyf`` + - Generates: + + + ``blahmodule.c`` + + ``blah-f2pywrappers2.f90`` (occasionally) + + ``f2pywrappers.f`` (occasionally) + + Signature files ``.pyf`` do not signal their language standard via the file + extension, they may generate the F90 and F77 specific wrappers depending on + their contents; which shifts the burden of checking for generated files onto + the build system. + +.. note:: + + The signature file output situation is being reconsidered in `issue 20385`_ . + + +In theory keeping the above requirements in hand, any build system can be +adapted to generate ``f2py`` extension modules. Here we will cover a subset of +the more popular systems. + +.. note:: + ``make`` has no place in a modern multi-language setup, and so is not + discussed further. + +Build Systems +============== + +.. toctree:: + :maxdepth: 2 + + distutils + meson + cmake + skbuild + +.. _`issue 20385`: https://github.com/numpy/numpy/issues/20385 diff --git a/doc/source/f2py/buildtools/meson.rst b/doc/source/f2py/buildtools/meson.rst new file mode 100644 index 000000000000..d98752e65f80 --- /dev/null +++ b/doc/source/f2py/buildtools/meson.rst @@ -0,0 +1,114 @@ +.. _f2py-meson: + +=================== +Using via ``meson`` +=================== + +The key advantage gained by leveraging ``meson`` over the techniques described +in :ref:`f2py-distutils` is that this feeds into existing systems and larger +projects with ease. ``meson`` has a rather pythonic syntax which makes it more +comfortable and amenable to extension for ``python`` users. + +.. note:: + + Meson needs to be at-least ``0.46.0`` in order to resolve the ``python`` include directories. + + +Fibonacci Walkthrough (F77) +=========================== + + +We will need the generated ``C`` wrapper before we can use a general purpose +build system like ``meson``. We will acquire this by: + +.. code-block:: bash + + python -n numpy.f2py fib1.f -m fib2 + +Now, consider the following ``meson.build`` file for the ``fib`` and ``scalar`` +examples from :ref:`f2py-getting-started` section: + +.. literalinclude:: ./../code/meson.build + :language: meson + +At this point the build will complete, but the import will fail: + +.. code-block:: bash + + meson setup builddir + meson compile -C builddir + cd builddir + python -c 'import fib2' + Traceback (most recent call last): + File "<string>", line 1, in <module> + ImportError: fib2.cpython-39-x86_64-linux-gnu.so: undefined symbol: FIB_ + # Check this isn't a false positive + nm -A fib2.cpython-39-x86_64-linux-gnu.so | grep FIB_ + fib2.cpython-39-x86_64-linux-gnu.so: U FIB_ + +Recall that the original example, as reproduced below, was in SCREAMCASE: + +.. literalinclude:: ./../code/fib1.f + :language: fortran + +With the standard approach, the subroutine exposed to ``python`` is ``fib`` and +not ``FIB``. This means we have a few options. One approach (where possible) is +to lowercase the original Fortran file with say: + +.. code-block:: bash + + tr "[:upper:]" "[:lower:]" < fib1.f > fib1.f + python -n numpy.f2py fib1.f -m fib2 + meson --wipe builddir + meson compile -C builddir + cd builddir + python -c 'import fib2' + +However this requires the ability to modify the source which is not always +possible. The easiest way to solve this is to let ``f2py`` deal with it: + +.. code-block:: bash + + python -n numpy.f2py fib1.f -m fib2 --lower + meson --wipe builddir + meson compile -C builddir + cd builddir + python -c 'import fib2' + + +Automating wrapper generation +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A major pain point in the workflow defined above, is the manual tracking of +inputs. Although it would require more effort to figure out the actual outputs +for reasons discussed in :ref:`f2py-bldsys`. + +However, we can augment our workflow in a straightforward to take into account +files for which the outputs are known when the build system is set up. + +.. literalinclude:: ./../code/meson_upd.build + :language: meson + +This can be compiled and run as before. + +.. code-block:: bash + + rm -rf builddir + meson setup builddir + meson compile -C builddir + cd builddir + python -c "import numpy as np; import fibby; a = np.zeros(9); fibby.fib(a); print (a)" + # [ 0. 1. 1. 2. 3. 5. 8. 13. 21.] + +Salient points +=============== + +It is worth keeping in mind the following: + +* ``meson`` will default to passing ``-fimplicit-none`` under ``gfortran`` by + default, which differs from that of the standard ``np.distutils`` behaviour + +* It is not possible to use SCREAMCASE in this context, so either the contents + of the ``.f`` file or the generated wrapper ``.c`` needs to be lowered to + regular letters; which can be facilitated by the ``--lower`` option of + ``F2PY`` diff --git a/doc/source/f2py/buildtools/skbuild.rst b/doc/source/f2py/buildtools/skbuild.rst new file mode 100644 index 000000000000..af18ea43bfd0 --- /dev/null +++ b/doc/source/f2py/buildtools/skbuild.rst @@ -0,0 +1,94 @@ +.. _f2py-skbuild: + +============================ +Using via ``scikit-build`` +============================ + +``scikit-build`` provides two separate concepts geared towards the users of Python extension modules. + +1. A ``setuptools`` replacement (legacy behaviour) +2. A series of ``cmake`` modules with definitions which help building Python extensions + +.. note:: + + It is possible to use ``scikit-build``'s ``cmake`` modules to `bypass the + cmake setup mechanism`_ completely, and to write targets which call ``f2py + -c``. This usage is **not recommended** since the point of these build system + documents are to move away from the internal ``numpy.distutils`` methods. + +For situations where no ``setuptools`` replacements are required or wanted (i.e. +if ``wheels`` are not needed), it is recommended to instead use the vanilla +``cmake`` setup described in :ref:`f2py-cmake`. + +Fibonacci Walkthrough (F77) +=========================== + +We will consider the ``fib`` example from :ref:`f2py-getting-started` section. + +.. literalinclude:: ./../code/fib1.f + :language: fortran + +``CMake`` modules only +^^^^^^^^^^^^^^^^^^^^^^^ + +Consider using the following ``CMakeLists.txt``. + +.. literalinclude:: ./../code/CMakeLists_skbuild.txt + :language: cmake + +Much of the logic is the same as in :ref:`f2py-cmake`, however notably here the +appropriate module suffix is generated via ``sysconfig.get_config_var("SO")``. +The resulting extension can be built and loaded in the standard workflow. + +.. code:: bash + + ls . + # CMakeLists.txt fib1.f + mkdir build && cd build + cmake .. + make + python -c "import numpy as np; import fibby; a = np.zeros(9); fibby.fib(a); print (a)" + # [ 0. 1. 1. 2. 3. 5. 8. 13. 21.] + + +``setuptools`` replacement +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. note:: + + **As of November 2021** + + The behavior described here of driving the ``cmake`` build of a module is + considered to be legacy behaviour and should not be depended on. + +The utility of ``scikit-build`` lies in being able to drive the generation of +more than extension modules, in particular a common usage pattern is the +generation of Python distributables (for example for PyPI). + +The workflow with ``scikit-build`` straightforwardly supports such packaging requirements. Consider augmenting the project with a ``setup.py`` as defined: + +.. literalinclude:: ./../code/setup_skbuild.py + :language: python + +Along with a commensurate ``pyproject.toml`` + +.. literalinclude:: ./../code/pyproj_skbuild.toml + :language: toml + +Together these can build the extension using ``cmake`` in tandem with other +standard ``setuptools`` outputs. Running ``cmake`` through ``setup.py`` is +mostly used when it is necessary to integrate with extension modules not built +with ``cmake``. + +.. code:: bash + + ls . + # CMakeLists.txt fib1.f pyproject.toml setup.py + python setup.py build_ext --inplace + python -c "import numpy as np; import fibby.fibby; a = np.zeros(9); fibby.fibby.fib(a); print (a)" + # [ 0. 1. 1. 2. 3. 5. 8. 13. 21.] + +Where we have modified the path to the module as ``--inplace`` places the +extension module in a subfolder. + +.. _bypass the cmake setup mechanism: https://scikit-build.readthedocs.io/en/latest/cmake-modules/F2PY.html diff --git a/doc/source/f2py/callback_session.dat b/doc/source/f2py/callback_session.dat deleted file mode 100644 index cd2f26084990..000000000000 --- a/doc/source/f2py/callback_session.dat +++ /dev/null @@ -1,23 +0,0 @@ ->>> import callback ->>> print callback.foo.__doc__ -foo - Function signature: - r = foo(fun,[fun_extra_args]) -Required arguments: - fun : call-back function -Optional arguments: - fun_extra_args := () input tuple -Return objects: - r : float -Call-back functions: - def fun(i): return r - Required arguments: - i : input int - Return objects: - r : float - ->>> def f(i): return i*i -... ->>> print callback.foo(f) -110.0 ->>> print callback.foo(lambda i:1) -11.0 diff --git a/doc/source/f2py/code/CMakeLists.txt b/doc/source/f2py/code/CMakeLists.txt new file mode 100644 index 000000000000..62ff193bbb2d --- /dev/null +++ b/doc/source/f2py/code/CMakeLists.txt @@ -0,0 +1,80 @@ +### setup project ### +cmake_minimum_required(VERSION 3.17.3) # 3.17 > for Python3_SOABI +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +project(fibby + VERSION 1.0 + DESCRIPTION "FIB module" + LANGUAGES C Fortran + ) + +# Safety net +if(PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR) + message( + FATAL_ERROR + "In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there.\n" + ) +endif() + +# Grab Python +find_package(Python3 3.9 REQUIRED + COMPONENTS Interpreter Development NumPy) + +# Grab the variables from a local Python installation +# F2PY headers +execute_process( + COMMAND "${Python3_EXECUTABLE}" + -c "import numpy.f2py; print(numpy.f2py.get_include())" + OUTPUT_VARIABLE F2PY_INCLUDE_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +# Project scope; consider using target_include_directories instead +include_directories( + BEFORE + ${Python3_INCLUDE_DIRS} + ${Python3_NumPy_INCLUDE_DIRS} + ${F2PY_INCLUDE_DIR} + ) + +message(STATUS ${Python3_INCLUDE_DIRS}) +message(STATUS ${F2PY_INCLUDE_DIR}) +message(STATUS ${Python3_NumPy_INCLUDE_DIRS}) + +# Vars +set(f2py_module_name "fibby") +set(fortran_src_file "${CMAKE_SOURCE_DIR}/fib1.f") +set(f2py_module_c "${f2py_module_name}module.c") +set(generated_module_file "${f2py_module_name}${Python3_SOABI}") + +# Generate sources +add_custom_target( + genpyf + DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${f2py_module_c}" + ) +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${f2py_module_c}" + COMMAND ${Python3_EXECUTABLE} -m "numpy.f2py" + "${fortran_src_file}" + -m "fibby" + --lower # Important + DEPENDS fib1.f # Fortran source + ) + +# Set up target +add_library(${CMAKE_PROJECT_NAME} SHARED + "${CMAKE_CURRENT_BINARY_DIR}/${f2py_module_c}" # Generated + "${F2PY_INCLUDE_DIR}/fortranobject.c" # From NumPy + "${fortran_src_file}" # Fortran source(s) + ) + +# Depend on sources +add_dependencies(${CMAKE_PROJECT_NAME} genpyf) + +set_target_properties( + ${CMAKE_PROJECT_NAME} + PROPERTIES + PREFIX "" + OUTPUT_NAME "${CMAKE_PROJECT_NAME}" + LINKER_LANGUAGE C + ) diff --git a/doc/source/f2py/code/CMakeLists_skbuild.txt b/doc/source/f2py/code/CMakeLists_skbuild.txt new file mode 100644 index 000000000000..97bc5c744d41 --- /dev/null +++ b/doc/source/f2py/code/CMakeLists_skbuild.txt @@ -0,0 +1,89 @@ +### setup project ### +cmake_minimum_required(VERSION 3.17.3) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +project(fibby + VERSION 1.0 + DESCRIPTION "FIB module" + LANGUAGES C Fortran + ) + +# Safety net +if(PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR) + message( + FATAL_ERROR + "In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there.\n" + ) +endif() + +# Grab Python +find_package(Python3 3.9 REQUIRED + COMPONENTS Interpreter Development) + +# Ensure scikit-build modules +if (NOT SKBUILD) + # Kanged -->https://github.com/Kitware/torch_liberator/blob/master/CMakeLists.txt + # If skbuild is not the driver; include its utilities in CMAKE_MODULE_PATH + execute_process( + COMMAND "${Python3_EXECUTABLE}" + -c "import os, skbuild; print(os.path.dirname(skbuild.__file__))" + OUTPUT_VARIABLE SKBLD_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + set(SKBLD_CMAKE_DIR "${SKBLD_DIR}/resources/cmake") + list(APPEND CMAKE_MODULE_PATH ${SKBLD_CMAKE_DIR}) +endif() + +# scikit-build style includes +find_package(PythonExtensions REQUIRED) # for ${PYTHON_EXTENSION_MODULE_SUFFIX} +find_package(NumPy REQUIRED) # for ${NumPy_INCLUDE_DIRS} +find_package(F2PY REQUIRED) # for ${F2PY_INCLUDE_DIR} + +# Prepping the module +set(f2py_module_name "fibby") +set(fortran_src_file "${CMAKE_SOURCE_DIR}/fib1.f") +set(generated_module_file ${f2py_module_name}${PYTHON_EXTENSION_MODULE_SUFFIX}) + +# Target for enforcing dependencies +add_custom_target(${f2py_module_name} ALL + DEPENDS "${fortran_src_file}" + ) + +# Custom command for generating .c +add_custom_command( + OUTPUT "${f2py_module_name}module.c" + COMMAND ${F2PY_EXECUTABLE} + -m ${f2py_module_name} + ${fortran_src_file} + --lower + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS ${fortran_src_file} + ) + +add_library(${generated_module_file} MODULE + "${f2py_module_name}module.c" + "${F2PY_INCLUDE_DIR}/fortranobject.c" + "${fortran_src_file}") + +target_include_directories(${generated_module_file} PUBLIC + ${F2PY_INCLUDE_DIRS} + ${PYTHON_INCLUDE_DIRS}) +set_target_properties(${generated_module_file} PROPERTIES SUFFIX "") +set_target_properties(${generated_module_file} PROPERTIES PREFIX "") + +# Linker fixes +if (UNIX) + if (APPLE) + set_target_properties(${generated_module_file} PROPERTIES + LINK_FLAGS '-Wl,-dylib,-undefined,dynamic_lookup') + else() + set_target_properties(${generated_module_file} PROPERTIES + LINK_FLAGS '-Wl,--allow-shlib-undefined') + endif() +endif() + +if (SKBUILD) + install(TARGETS ${generated_module_file} DESTINATION fibby) +else() + install(TARGETS ${generated_module_file} DESTINATION ${CMAKE_SOURCE_DIR}/fibby) +endif() diff --git a/doc/source/f2py/allocarr.f90 b/doc/source/f2py/code/allocarr.f90 similarity index 100% rename from doc/source/f2py/allocarr.f90 rename to doc/source/f2py/code/allocarr.f90 diff --git a/doc/source/f2py/array.f b/doc/source/f2py/code/array.f similarity index 100% rename from doc/source/f2py/array.f rename to doc/source/f2py/code/array.f diff --git a/doc/source/f2py/calculate.f b/doc/source/f2py/code/calculate.f similarity index 92% rename from doc/source/f2py/calculate.f rename to doc/source/f2py/code/calculate.f index 1cda1c8ddd3d..4ff570d28ff5 100644 --- a/doc/source/f2py/calculate.f +++ b/doc/source/f2py/code/calculate.f @@ -7,7 +7,7 @@ subroutine calculate(x,n) c cf2py intent(in,out,copy) x integer n,i - real*8 x(n) + real*8 x(n), func do i=1,n x(i) = func(x(i)) end do diff --git a/doc/source/f2py/callback.f b/doc/source/f2py/code/callback.f similarity index 91% rename from doc/source/f2py/callback.f rename to doc/source/f2py/code/callback.f index 6e9bfb920cdf..d5cfc757411d 100644 --- a/doc/source/f2py/callback.f +++ b/doc/source/f2py/code/callback.f @@ -2,7 +2,7 @@ SUBROUTINE FOO(FUN,R) EXTERNAL FUN INTEGER I - REAL*8 R + REAL*8 R, FUN Cf2py intent(out) r R = 0D0 DO I=-5,5 diff --git a/doc/source/f2py/callback2.pyf b/doc/source/f2py/code/callback2.pyf similarity index 100% rename from doc/source/f2py/callback2.pyf rename to doc/source/f2py/code/callback2.pyf diff --git a/doc/source/f2py/common.f b/doc/source/f2py/code/common.f similarity index 100% rename from doc/source/f2py/common.f rename to doc/source/f2py/code/common.f diff --git a/doc/source/f2py/extcallback.f b/doc/source/f2py/code/extcallback.f similarity index 100% rename from doc/source/f2py/extcallback.f rename to doc/source/f2py/code/extcallback.f diff --git a/doc/source/f2py/fib1.f b/doc/source/f2py/code/fib1.f similarity index 100% rename from doc/source/f2py/fib1.f rename to doc/source/f2py/code/fib1.f diff --git a/doc/source/f2py/fib1.pyf b/doc/source/f2py/code/fib1.pyf similarity index 100% rename from doc/source/f2py/fib1.pyf rename to doc/source/f2py/code/fib1.pyf diff --git a/doc/source/f2py/fib2.pyf b/doc/source/f2py/code/fib2.pyf similarity index 100% rename from doc/source/f2py/fib2.pyf rename to doc/source/f2py/code/fib2.pyf diff --git a/doc/source/f2py/fib3.f b/doc/source/f2py/code/fib3.f similarity index 100% rename from doc/source/f2py/fib3.f rename to doc/source/f2py/code/fib3.f diff --git a/doc/source/f2py/ftype.f b/doc/source/f2py/code/ftype.f similarity index 100% rename from doc/source/f2py/ftype.f rename to doc/source/f2py/code/ftype.f diff --git a/doc/source/f2py/code/meson.build b/doc/source/f2py/code/meson.build new file mode 100644 index 000000000000..b756abf8f59a --- /dev/null +++ b/doc/source/f2py/code/meson.build @@ -0,0 +1,38 @@ +project('f2py_examples', 'c', + version : '0.1', + default_options : ['warning_level=2']) + +add_languages('fortran') + +py_mod = import('python') +py3 = py_mod.find_installation('python3') +py3_dep = py3.dependency() +message(py3.path()) +message(py3.get_install_dir()) + +incdir_numpy = run_command(py3, + ['-c', 'import os; os.chdir(".."); import numpy; print(numpy.get_include())'], + check : true +).stdout().strip() + +incdir_f2py = run_command(py3, + ['-c', 'import os; os.chdir(".."); import numpy.f2py; print(numpy.f2py.get_include())'], + check : true +).stdout().strip() + +fibby_source = custom_target('fibbymodule.c', + input : ['fib1.f'], + output : ['fibbymodule.c'], + command : [ py3, '-m', 'numpy.f2py', '@INPUT@', + '-m', 'fibby', '--lower' ] + ) + +inc_np = include_directories(incdir_numpy, incdir_f2py) + +py3.extension_module('fibby', + 'fib1.f', + fibby_source, + incdir_f2py+'/fortranobject.c', + include_directories: inc_np, + dependencies : py3_dep, + install : true) diff --git a/doc/source/f2py/code/meson_upd.build b/doc/source/f2py/code/meson_upd.build new file mode 100644 index 000000000000..97bd8d175c7c --- /dev/null +++ b/doc/source/f2py/code/meson_upd.build @@ -0,0 +1,37 @@ +project('f2py_examples', 'c', + version : '0.1', + default_options : ['warning_level=2']) + +add_languages('fortran') + +py_mod = import('python') +py3 = py_mod.find_installation('python3') +py3_dep = py3.dependency() +message(py3.path()) +message(py3.get_install_dir()) + +incdir_numpy = run_command(py3, + ['-c', 'import os; os.chdir(".."); import numpy; print(numpy.get_include())'], + check : true +).stdout().strip() + +incdir_f2py = run_command(py3, + ['-c', 'import os; os.chdir(".."); import numpy.f2py; print(numpy.f2py.get_include())'], + check : true +).stdout().strip() + +fibby_source = custom_target('fibbymodule.c', + input : ['fib1.f'], + output : ['fibbymodule.c'], + command : [ py3, '-m', 'numpy.f2py', '@INPUT@', + '-m', 'fibby', '--lower' ]) + +inc_np = include_directories(incdir_numpy, incdir_f2py) + +py3.extension_module('fibby', + 'fib1.f', + fibby_source, + incdir_f2py+'/fortranobject.c', + include_directories: inc_np, + dependencies : py3_dep, + install : true) diff --git a/doc/source/f2py/moddata.f90 b/doc/source/f2py/code/moddata.f90 similarity index 100% rename from doc/source/f2py/moddata.f90 rename to doc/source/f2py/code/moddata.f90 diff --git a/doc/source/f2py/code/pyproj_skbuild.toml b/doc/source/f2py/code/pyproj_skbuild.toml new file mode 100644 index 000000000000..6686d1736015 --- /dev/null +++ b/doc/source/f2py/code/pyproj_skbuild.toml @@ -0,0 +1,5 @@ +[project] +requires-python = ">=3.7" + +[build-system] +requires = ["setuptools>=42", "wheel", "scikit-build", "cmake>=3.18", "numpy>=1.21"] diff --git a/doc/source/f2py/code/results/allocarr_session.dat b/doc/source/f2py/code/results/allocarr_session.dat new file mode 100644 index 000000000000..ba168c22aa12 --- /dev/null +++ b/doc/source/f2py/code/results/allocarr_session.dat @@ -0,0 +1,32 @@ +>>> import allocarr +>>> print(allocarr.mod.__doc__) +b : 'f'-array(-1,-1), not allocated +foo() + +Wrapper for ``foo``. + + + +>>> allocarr.mod.foo() + b is not allocated +>>> allocarr.mod.b = [[1, 2, 3], [4, 5, 6]] # allocate/initialize b +>>> allocarr.mod.foo() + b=[ + 1.000000 2.000000 3.000000 + 4.000000 5.000000 6.000000 + ] +>>> allocarr.mod.b # b is Fortran-contiguous +array([[ 1., 2., 3.], + [ 4., 5., 6.]], dtype=float32) +>>> allocarr.mod.b.flags.f_contiguous +True +>>> allocarr.mod.b = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] # reallocate/initialize b +>>> allocarr.mod.foo() + b=[ + 1.000000 2.000000 3.000000 + 4.000000 5.000000 6.000000 + 7.000000 8.000000 9.000000 + ] +>>> allocarr.mod.b = None # deallocate array +>>> allocarr.mod.foo() + b is not allocated diff --git a/doc/source/f2py/code/results/array_session.dat b/doc/source/f2py/code/results/array_session.dat new file mode 100644 index 000000000000..714c03651f84 --- /dev/null +++ b/doc/source/f2py/code/results/array_session.dat @@ -0,0 +1,87 @@ +>>> import arr +>>> from numpy import asfortranarray +>>> print(arr.foo.__doc__) +a = foo(a,[overwrite_a]) + +Wrapper for ``foo``. + +Parameters +---------- +a : input rank-2 array('d') with bounds (n,m) + +Other Parameters +---------------- +overwrite_a : input int, optional + Default: 0 + +Returns +------- +a : rank-2 array('d') with bounds (n,m) + +>>> a = arr.foo([[1, 2, 3], +... [4, 5, 6]]) +created an array from object +>>> print(a) +[[ 1. 3. 4.] + [ 3. 5. 6.]] +>>> a.flags.c_contiguous +False +>>> a.flags.f_contiguous +True +# even if a is proper-contiguous and has proper type, +# a copy is made forced by intent(copy) attribute +# to preserve its original contents +>>> b = arr.foo(a) +copied an array: size=6, elsize=8 +>>> print(a) +[[ 1. 3. 4.] + [ 3. 5. 6.]] +>>> print(b) +[[ 1. 4. 5.] + [ 2. 5. 6.]] +>>> b = arr.foo(a, overwrite_a = 1) # a is passed directly to Fortran +... # routine and its contents is discarded +... +>>> print(a) +[[ 1. 4. 5.] + [ 2. 5. 6.]] +>>> print(b) +[[ 1. 4. 5.] + [ 2. 5. 6.]] +>>> a is b # a and b are actually the same objects +True +>>> print(arr.foo([1, 2, 3])) # different rank arrays are allowed +created an array from object +[ 1. 1. 2.] +>>> print(arr.foo([[[1], [2], [3]]])) +created an array from object +[[[ 1.] + [ 1.] + [ 2.]]] +>>> +>>> # Creating arrays with column major data storage order: + ... +>>> s = asfortranarray([[1, 2, 3], [4, 5, 6]]) +>>> s.flags.f_contiguous +True +>>> print(s) +[[1 2 3] + [4 5 6]] +>>> print(arr.foo(s)) +>>> s2 = asfortranarray(s) +>>> s2 is s # an array with column major storage order + # is returned immediately +True +>>> # Note that arr.foo returns a column major data storage order array: + ... +>>> s3 = ascontiguousarray(s) +>>> s3.flags.f_contiguous +False +>>> s3.flags.c_contiguous +True +>>> s3 = arr.foo(s3) +copied an array: size=6, elsize=8 +>>> s3.flags.f_contiguous +True +>>> s3.flags.c_contiguous +False diff --git a/doc/source/f2py/calculate_session.dat b/doc/source/f2py/code/results/calculate_session.dat similarity index 65% rename from doc/source/f2py/calculate_session.dat rename to doc/source/f2py/code/results/calculate_session.dat index 2fe64f522463..c4c38070099c 100644 --- a/doc/source/f2py/calculate_session.dat +++ b/doc/source/f2py/code/results/calculate_session.dat @@ -3,4 +3,4 @@ array([ 0., 1., 4., 9., 16.]) >>> import math >>> foo.calculate(range(5), math.exp) -array([ 1. , 2.71828175, 7.38905621, 20.08553696, 54.59814835]) +array([ 1. , 2.71828183, 7.3890561, 20.08553692, 54.59815003]) diff --git a/doc/source/f2py/code/results/callback_session.dat b/doc/source/f2py/code/results/callback_session.dat new file mode 100644 index 000000000000..460c9ce28873 --- /dev/null +++ b/doc/source/f2py/code/results/callback_session.dat @@ -0,0 +1,35 @@ +>>> import callback +>>> print(callback.foo.__doc__) +r = foo(fun,[fun_extra_args]) + +Wrapper for ``foo``. + +Parameters +---------- +fun : call-back function + +Other Parameters +---------------- +fun_extra_args : input tuple, optional + Default: () + +Returns +------- +r : float + +Notes +----- +Call-back functions:: + + def fun(i): return r + Required arguments: + i : input int + Return objects: + r : float + +>>> def f(i): return i*i +... +>>> print(callback.foo(f)) +110.0 +>>> print(callback.foo(lambda i:1)) +11.0 diff --git a/doc/source/f2py/code/results/common_session.dat b/doc/source/f2py/code/results/common_session.dat new file mode 100644 index 000000000000..2595bfbd5b20 --- /dev/null +++ b/doc/source/f2py/code/results/common_session.dat @@ -0,0 +1,30 @@ +>>> import common +>>> print(common.data.__doc__) +i : 'i'-scalar +x : 'i'-array(4) +a : 'f'-array(2,3) + +>>> common.data.i = 5 +>>> common.data.x[1] = 2 +>>> common.data.a = [[1,2,3],[4,5,6]] +>>> common.foo() +>>> common.foo() + I= 5 + X=[ 0 2 0 0 ] + A=[ + [ 1.00000000 , 2.00000000 , 3.00000000 ] + [ 4.00000000 , 5.00000000 , 6.00000000 ] + ] +>>> common.data.a[1] = 45 +>>> common.foo() + I= 5 + X=[ 0 2 0 0 ] + A=[ + [ 1.00000000 , 2.00000000 , 3.00000000 ] + [ 45.0000000 , 45.0000000 , 45.0000000 ] + ] +>>> common.data.a # a is Fortran-contiguous +array([[ 1., 2., 3.], + [ 45., 45., 45.]], dtype=float32) +>>> common.data.a.flags.f_contiguous +True \ No newline at end of file diff --git a/doc/source/f2py/compile_session.dat b/doc/source/f2py/code/results/compile_session.dat similarity index 63% rename from doc/source/f2py/compile_session.dat rename to doc/source/f2py/code/results/compile_session.dat index 0d8408198845..5c42742bea38 100644 --- a/doc/source/f2py/compile_session.dat +++ b/doc/source/f2py/code/results/compile_session.dat @@ -1,10 +1,10 @@ ->>> import f2py2e +>>> import numpy.f2py >>> fsource = ''' ... subroutine foo ... print*, "Hello world!" ... end ... ''' ->>> f2py2e.compile(fsource,modulename='hello',verbose=0) +>>> numpy.f2py.compile(fsource, modulename='hello', verbose=0) 0 >>> import hello >>> hello.foo() diff --git a/doc/source/f2py/extcallback_session.dat b/doc/source/f2py/code/results/extcallback_session.dat similarity index 83% rename from doc/source/f2py/extcallback_session.dat rename to doc/source/f2py/code/results/extcallback_session.dat index c22935ea0f7d..5b97ab7cfff2 100644 --- a/doc/source/f2py/extcallback_session.dat +++ b/doc/source/f2py/code/results/extcallback_session.dat @@ -1,10 +1,10 @@ >>> import pfromf >>> pfromf.f2() Traceback (most recent call last): - File "<stdin>", line 1, in ? + File "<stdin>", line 1, in <module> pfromf.error: Callback fpy not defined (as an argument or module pfromf attribute). ->>> def f(): print "python f" +>>> def f(): print("python f") ... >>> pfromf.fpy = f >>> pfromf.f2() diff --git a/doc/source/f2py/ftype_session.dat b/doc/source/f2py/code/results/ftype_session.dat similarity index 62% rename from doc/source/f2py/ftype_session.dat rename to doc/source/f2py/code/results/ftype_session.dat index 01f9febaf403..e39cc128d5bc 100644 --- a/doc/source/f2py/ftype_session.dat +++ b/doc/source/f2py/code/results/ftype_session.dat @@ -1,13 +1,13 @@ >>> import ftype ->>> print ftype.__doc__ -This module 'ftype' is auto-generated with f2py (version:2.28.198-1366). +>>> print(ftype.__doc__) +This module 'ftype' is auto-generated with f2py (version:2). Functions: foo(n=13) COMMON blocks: /data/ a,x(3) . ->>> type(ftype.foo),type(ftype.data) -(<type 'fortran'>, <type 'fortran'>) +>>> type(ftype.foo), type(ftype.data) +(<class 'fortran'>, <class 'fortran'>) >>> ftype.foo() IN FOO: N= 13 A= 0. X=[ 0. 0. 0.] >>> ftype.data.a = 3 @@ -18,4 +18,4 @@ COMMON blocks: >>> ftype.foo(24) IN FOO: N= 24 A= 3. X=[ 1. 45. 3.] >>> ftype.data.x -array([ 1., 45., 3.],'f') +array([ 1., 45., 3.], dtype=float32) diff --git a/doc/source/f2py/moddata_session.dat b/doc/source/f2py/code/results/moddata_session.dat similarity index 66% rename from doc/source/f2py/moddata_session.dat rename to doc/source/f2py/code/results/moddata_session.dat index 1ec212f8bd22..824bd86fc464 100644 --- a/doc/source/f2py/moddata_session.dat +++ b/doc/source/f2py/code/results/moddata_session.dat @@ -1,10 +1,13 @@ >>> import moddata ->>> print moddata.mod.__doc__ -i - 'i'-scalar -x - 'i'-array(4) -a - 'f'-array(2,3) -foo - Function signature: - foo() +>>> print(moddata.mod.__doc__) +i : 'i'-scalar +x : 'i'-array(4) +a : 'f'-array(2,3) +b : 'f'-array(-1,-1), not allocated +foo() + +Wrapper for ``foo``. + >>> moddata.mod.i = 5 @@ -20,4 +23,6 @@ foo - Function signature: Setting a(1,2)=a(1,2)+3 >>> moddata.mod.a # a is Fortran-contiguous array([[ 1., 5., 3.], - [ 4., 5., 6.]],'f') + [ 4., 5., 6.]], dtype=float32) +>>> moddata.mod.a.flags.f_contiguous +True diff --git a/doc/source/f2py/code/results/run_main_session.dat b/doc/source/f2py/code/results/run_main_session.dat new file mode 100644 index 000000000000..be6cacd22634 --- /dev/null +++ b/doc/source/f2py/code/results/run_main_session.dat @@ -0,0 +1,14 @@ +>>> import numpy.f2py +>>> r = numpy.f2py.run_main(['-m','scalar','doc/source/f2py/scalar.f']) +Reading fortran codes... + Reading file 'doc/source/f2py/scalar.f' (format:fix,strict) +Post-processing... + Block: scalar + Block: FOO +Building modules... + Building module "scalar"... + Wrote C/API module "scalar" to file "./scalarmodule.c" +>>> print(r) +{'scalar': {'h': ['/home/users/pearu/src_cvs/f2py/src/fortranobject.h'], + 'csrc': ['./scalarmodule.c', + '/home/users/pearu/src_cvs/f2py/src/fortranobject.c']}} diff --git a/doc/source/f2py/code/results/scalar_session.dat b/doc/source/f2py/code/results/scalar_session.dat new file mode 100644 index 000000000000..3bb45ed686eb --- /dev/null +++ b/doc/source/f2py/code/results/scalar_session.dat @@ -0,0 +1,24 @@ +>>> import scalar +>>> print(scalar.foo.__doc__) +foo(a,b) + +Wrapper for ``foo``. + +Parameters +---------- +a : input float +b : in/output rank-0 array(float,'d') + +>>> scalar.foo(2, 3) + A= 2. B= 3. + INCREMENT A AND B + NEW A= 3. B= 4. +>>> import numpy +>>> a = numpy.array(2) # these are integer rank-0 arrays +>>> b = numpy.array(3) +>>> scalar.foo(a, b) + A= 2. B= 3. + INCREMENT A AND B + NEW A= 3. B= 4. +>>> print(a, b) # note that only b is changed in situ +2 4 diff --git a/doc/source/f2py/spam_session.dat b/doc/source/f2py/code/results/spam_session.dat similarity index 73% rename from doc/source/f2py/spam_session.dat rename to doc/source/f2py/code/results/spam_session.dat index 7f99d13f9a62..bd5832d88072 100644 --- a/doc/source/f2py/spam_session.dat +++ b/doc/source/f2py/code/results/spam_session.dat @@ -1,5 +1,5 @@ >>> import spam >>> status = spam.system('whoami') pearu ->> status = spam.system('blah') +>>> status = spam.system('blah') sh: line 1: blah: command not found \ No newline at end of file diff --git a/doc/source/f2py/code/results/string_session.dat b/doc/source/f2py/code/results/string_session.dat new file mode 100644 index 000000000000..e8f7854d9341 --- /dev/null +++ b/doc/source/f2py/code/results/string_session.dat @@ -0,0 +1,30 @@ +>>> import mystring +>>> print(mystring.foo.__doc__) +foo(a,b,c,d) + +Wrapper for ``foo``. + +Parameters +---------- +a : input string(len=5) +b : in/output rank-0 array(string(len=5),'c') +c : input string(len=-1) +d : in/output rank-0 array(string(len=-1),'c') + +>>> from numpy import array +>>> a = array(b'123\0\0') +>>> b = array(b'123\0\0') +>>> c = array(b'123') +>>> d = array(b'123') +>>> mystring.foo(a, b, c, d) + A=123 + B=123 + C=123 + D=123 + CHANGE A,B,C,D + A=A23 + B=B23 + C=C23 + D=D23 +>>> a[()], b[()], c[()], d[()] +(b'123', b'B23', b'123', b'D2') diff --git a/doc/source/f2py/var_session.dat b/doc/source/f2py/code/results/var_session.dat similarity index 100% rename from doc/source/f2py/var_session.dat rename to doc/source/f2py/code/results/var_session.dat diff --git a/doc/source/f2py/scalar.f b/doc/source/f2py/code/scalar.f similarity index 100% rename from doc/source/f2py/scalar.f rename to doc/source/f2py/code/scalar.f diff --git a/doc/source/f2py/setup_example.py b/doc/source/f2py/code/setup_example.py similarity index 89% rename from doc/source/f2py/setup_example.py rename to doc/source/f2py/code/setup_example.py index 54af7729988e..479acc004d60 100644 --- a/doc/source/f2py/setup_example.py +++ b/doc/source/f2py/code/setup_example.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - from numpy.distutils.core import Extension ext1 = Extension(name = 'scalar', diff --git a/doc/source/f2py/code/setup_skbuild.py b/doc/source/f2py/code/setup_skbuild.py new file mode 100644 index 000000000000..4dfc6af8b76d --- /dev/null +++ b/doc/source/f2py/code/setup_skbuild.py @@ -0,0 +1,10 @@ +from skbuild import setup + +setup( + name="fibby", + version="0.0.1", + description="a minimal example package (fortran version)", + license="MIT", + packages=['fibby'], + cmake_args=['-DSKBUILD=ON'] +) diff --git a/doc/source/f2py/spam.pyf b/doc/source/f2py/code/spam.pyf similarity index 100% rename from doc/source/f2py/spam.pyf rename to doc/source/f2py/code/spam.pyf diff --git a/doc/source/f2py/string.f b/doc/source/f2py/code/string.f similarity index 100% rename from doc/source/f2py/string.f rename to doc/source/f2py/code/string.f diff --git a/doc/source/f2py/var.pyf b/doc/source/f2py/code/var.pyf similarity index 100% rename from doc/source/f2py/var.pyf rename to doc/source/f2py/code/var.pyf diff --git a/doc/source/f2py/common_session.dat b/doc/source/f2py/common_session.dat deleted file mode 100644 index 846fdaa07621..000000000000 --- a/doc/source/f2py/common_session.dat +++ /dev/null @@ -1,27 +0,0 @@ ->>> import common ->>> print common.data.__doc__ -i - 'i'-scalar -x - 'i'-array(4) -a - 'f'-array(2,3) - ->>> common.data.i = 5 ->>> common.data.x[1] = 2 ->>> common.data.a = [[1,2,3],[4,5,6]] ->>> common.foo() - I= 5 - X=[ 0 2 0 0] - A=[ - [ 1., 2., 3.] - [ 4., 5., 6.] - ] ->>> common.data.a[1] = 45 ->>> common.foo() - I= 5 - X=[ 0 2 0 0] - A=[ - [ 1., 2., 3.] - [ 45., 45., 45.] - ] ->>> common.data.a # a is Fortran-contiguous -array([[ 1., 2., 3.], - [ 45., 45., 45.]],'f') diff --git a/doc/source/f2py/distutils.rst b/doc/source/f2py/distutils.rst deleted file mode 100644 index fdcd38468d01..000000000000 --- a/doc/source/f2py/distutils.rst +++ /dev/null @@ -1,73 +0,0 @@ -============================= -Using via `numpy.distutils` -============================= - -:mod:`numpy.distutils` is part of NumPy extending standard Python ``distutils`` -to deal with Fortran sources and F2PY signature files, e.g. compile Fortran -sources, call F2PY to construct extension modules, etc. - -.. topic:: Example - - Consider the following `setup file`__: - - .. include:: setup_example.py - :literal: - - Running - - :: - - python setup_example.py build - - will build two extension modules ``scalar`` and ``fib2`` to the - build directory. - - __ setup_example.py - -:mod:`numpy.distutils` extends ``distutils`` with the following features: - -* ``Extension`` class argument ``sources`` may contain Fortran source - files. In addition, the list ``sources`` may contain at most one - F2PY signature file, and then the name of an Extension module must - match with the ``<modulename>`` used in signature file. It is - assumed that an F2PY signature file contains exactly one ``python - module`` block. - - If ``sources`` does not contain a signature files, then F2PY is used - to scan Fortran source files for routine signatures to construct the - wrappers to Fortran codes. - - Additional options to F2PY process can be given using ``Extension`` - class argument ``f2py_options``. - -* The following new ``distutils`` commands are defined: - - ``build_src`` - to construct Fortran wrapper extension modules, among many other things. - ``config_fc`` - to change Fortran compiler options - - as well as ``build_ext`` and ``build_clib`` commands are enhanced - to support Fortran sources. - - Run - - :: - - python <setup.py file> config_fc build_src build_ext --help - - to see available options for these commands. - -* When building Python packages containing Fortran sources, then one - can choose different Fortran compilers by using ``build_ext`` - command option ``--fcompiler=<Vendor>``. Here ``<Vendor>`` can be one of the - following names:: - - absoft sun mips intel intelv intele intelev nag compaq compaqv gnu vast pg hpux - - See ``numpy_distutils/fcompiler.py`` for up-to-date list of - supported compilers or run - - :: - - f2py -c --help-fcompiler diff --git a/doc/source/f2py/f2py.getting-started.rst b/doc/source/f2py/f2py.getting-started.rst new file mode 100644 index 000000000000..c1a006f6f2eb --- /dev/null +++ b/doc/source/f2py/f2py.getting-started.rst @@ -0,0 +1,270 @@ +.. _f2py-getting-started: + +====================================== + Three ways to wrap - getting started +====================================== + +Wrapping Fortran or C functions to Python using F2PY consists of the +following steps: + +* Creating the so-called signature file that contains descriptions of + wrappers to Fortran or C functions, also called the signatures of the + functions. For Fortran routines, F2PY can create an initial + signature file by scanning Fortran source codes and + tracking all relevant information needed to create wrapper + functions. + + * Optionally, F2PY created signature files can be edited to optimize + wrapper functions, to make them "smarter" and more "Pythonic". + +* F2PY reads a signature file and writes a Python C/API module containing + Fortran/C/Python bindings. +* F2PY compiles all sources and builds an extension module containing + the wrappers. + + * In building the extension modules, F2PY uses ``numpy_distutils`` which + supports a number of Fortran 77/90/95 compilers, including Gnu, Intel, Sun + Fortran, SGI MIPSpro, Absoft, NAG, Compaq etc. + +Depending on the situation, these steps can be carried out in a single composite +command or step-by-step; in which case some steps can be omitted or combined +with others. + +Below, we describe three typical approaches of using F2PY. These can be read in +order of increasing effort, but also cater to different access levels depending +on whether the Fortran code can be freely modified. + +The following example Fortran 77 code will be used for +illustration, save it as ``fib1.f``: + +.. literalinclude:: ./code/fib1.f + :language: fortran + + +The quick way +============== + +The quickest way to wrap the Fortran subroutine ``FIB`` for use in Python is to +run + +:: + + python -m numpy.f2py -c fib1.f -m fib1 + +This command compiles and wraps ``fib1.f`` (``-c``) to create the extension +module ``fib1.so`` (``-m``) in the current directory. A list of command line +options can be seen by executing ``python -m numpy.f2py``. Now, in Python the +Fortran subroutine ``FIB`` is accessible via ``fib1.fib``:: + + >>> import numpy as np + >>> import fib1 + >>> print(fib1.fib.__doc__) + fib(a,[n]) + + Wrapper for ``fib``. + + Parameters + ---------- + a : input rank-1 array('d') with bounds (n) + + Other Parameters + ---------------- + n : input int, optional + Default: len(a) + + >>> a = np.zeros(8, 'd') + >>> fib1.fib(a) + >>> print(a) + [ 0. 1. 1. 2. 3. 5. 8. 13.] + +.. note:: + + * Note that F2PY recognized that the second argument ``n`` is the + dimension of the first array argument ``a``. Since by default all + arguments are input-only arguments, F2PY concludes that ``n`` can + be optional with the default value ``len(a)``. + + * One can use different values for optional ``n``:: + + >>> a1 = np.zeros(8, 'd') + >>> fib1.fib(a1, 6) + >>> print(a1) + [ 0. 1. 1. 2. 3. 5. 0. 0.] + + but an exception is raised when it is incompatible with the input + array ``a``:: + + >>> fib1.fib(a, 10) + Traceback (most recent call last): + File "<stdin>", line 1, in <module> + fib.error: (len(a)>=n) failed for 1st keyword n: fib:n=10 + >>> + + F2PY implements basic compatibility checks between related + arguments in order to avoid unexpected crashes. + + * When a NumPy array, that is Fortran contiguous and has a ``dtype`` + corresponding to a presumed Fortran type, is used as an input array + argument, then its C pointer is directly passed to Fortran. + + Otherwise F2PY makes a contiguous copy (with the proper ``dtype``) of + the input array and passes a C pointer of the copy to the Fortran + subroutine. As a result, any possible changes to the (copy of) + input array have no effect to the original argument, as + demonstrated below:: + + >>> a = np.ones(8, 'i') + >>> fib1.fib(a) + >>> print(a) + [1 1 1 1 1 1 1 1] + + Clearly, this is unexpected, as Fortran typically passes by reference. That + the above example worked with ``dtype=float`` is considered accidental. + + F2PY provides an ``intent(inplace)`` attribute that modifies + the attributes of an input array so that any changes made by + Fortran routine will be reflected in the input argument. For example, + if one specifies the ``intent(inplace) a`` directive (see subsequent + sections on how), then the example above would read:: + + >>> a = np.ones(8, 'i') + >>> fib1.fib(a) + >>> print(a) + [ 0. 1. 1. 2. 3. 5. 8. 13.] + + However, the recommended way to have changes made by Fortran subroutine + propagate to Python is to use the ``intent(out)`` attribute. That approach is + more efficient and also cleaner. + + * The usage of ``fib1.fib`` in Python is very similar to using ``FIB`` in + Fortran. However, using *in situ* output arguments in Python is poor style, + as there are no safety mechanisms in Python to protect against wrong + argument types. When using Fortran or C, compilers discover any type + mismatches during the compilation process, but in Python the types must be + checked at runtime. Consequently, using *in situ* output arguments in Python + may lead to difficult to find bugs, not to mention the fact that the + codes will be less readable when all required type checks are implemented. + + Though the approach to wrapping Fortran routines for Python discussed so far is + very straightforward, it has several drawbacks (see the comments above). + The drawbacks are due to the fact that there is no way for F2PY to determine + the actual intention of the arguments; that is there is ambiguity in + distinguishing between input and output arguments. Consequently, F2PY assumes + that all arguments are input arguments by default. + + However, there are ways (see below) to remove this ambiguity by "teaching" + F2PY about the true intentions of function arguments, and F2PY is then able to + generate more explicit, easier to use, and less error prone wrappers for + Fortran functions. + +The smart way +============== + +Let us apply the steps for wrapping Fortran functions to Python one by +one. + +* First, we create a signature file from ``fib1.f`` by running: + + :: + + python -m numpy.f2py fib1.f -m fib2 -h fib1.pyf + + The signature file is saved to ``fib1.pyf`` (see the ``-h`` flag) and + its contents are shown below. + + .. literalinclude:: ./code/fib1.pyf + :language: fortran + +* Next, we'll teach F2PY that the argument ``n`` is an input argument (using the + ``intent(in)`` attribute) and that the result, i.e., the contents of ``a`` + after calling the Fortran function ``FIB``, should be returned to Python (using + the ``intent(out)`` attribute). In addition, an array ``a`` should be created + dynamically using the size determined by the input argument ``n`` (using the + ``depend(n)`` attribute to indicate this dependence relation). + + The contents of a suitably modified version of ``fib1.pyf`` (saved as + ``fib2.pyf``) is as follows: + + .. literalinclude:: ./code/fib2.pyf + :language: fortran + +* Finally, we build the extension module with ``numpy.distutils`` by running: + + :: + + python -m numpy.f2py -c fib2.pyf fib1.f + +In Python:: + + >>> import fib2 + >>> print(fib2.fib.__doc__) + a = fib(n) + + Wrapper for ``fib``. + + Parameters + ---------- + n : input int + + Returns + ------- + a : rank-1 array('d') with bounds (n) + + >>> print(fib2.fib(8)) + [ 0. 1. 1. 2. 3. 5. 8. 13.] + +.. note:: + + * The signature of ``fib2.fib`` now more closely corresponds to the + intention of Fortran subroutine ``FIB``: given the number ``n``, + ``fib2.fib`` returns the first ``n`` Fibonacci numbers as a NumPy array. + The new Python signature ``fib2.fib`` also rules out the unexpected behaviour in ``fib1.fib``. + + * Note that by default, using a single ``intent(out)`` also implies + ``intent(hide)``. Arguments that have the ``intent(hide)`` attribute + specified will not be listed in the argument list of a wrapper function. + +The quick and smart way +======================== + +The "smart way" of wrapping Fortran functions, as explained above, is +suitable for wrapping (e.g. third party) Fortran codes for which +modifications to their source codes are not desirable nor even +possible. + +However, if editing Fortran codes is acceptable, then the generation of an +intermediate signature file can be skipped in most cases. F2PY specific +attributes can be inserted directly into Fortran source codes using F2PY +directives. A F2PY directive consists of special comment lines (starting with +``Cf2py`` or ``!f2py``, for example) which are ignored by Fortran compilers but +interpreted by F2PY as normal lines. + +Consider a modified version of the previous Fortran code with F2PY directives, +saved as ``fib3.f``: + +.. literalinclude:: ./code/fib3.f + :language: fortran + +Building the extension module can be now carried out in one command:: + + python -m numpy.f2py -c -m fib3 fib3.f + +Notice that the resulting wrapper to ``FIB`` is as "smart" (unambiguous) as in +the previous case:: + + >>> import fib3 + >>> print(fib3.fib.__doc__) + a = fib(n) + + Wrapper for ``fib``. + + Parameters + ---------- + n : input int + + Returns + ------- + a : rank-1 array('d') with bounds (n) + + >>> print(fib3.fib(8)) + [ 0. 1. 1. 2. 3. 5. 8. 13.] diff --git a/doc/source/f2py/getting-started.rst b/doc/source/f2py/getting-started.rst deleted file mode 100644 index fffd61c45a24..000000000000 --- a/doc/source/f2py/getting-started.rst +++ /dev/null @@ -1,261 +0,0 @@ -====================================== - Three ways to wrap - getting started -====================================== - -Wrapping Fortran or C functions to Python using F2PY consists of the -following steps: - -* Creating the so-called signature file that contains descriptions of - wrappers to Fortran or C functions, also called as signatures of the - functions. In the case of Fortran routines, F2PY can create initial - signature file by scanning Fortran source codes and - catching all relevant information needed to create wrapper - functions. - -* Optionally, F2PY created signature files can be edited to optimize - wrappers functions, make them "smarter" and more "Pythonic". - -* F2PY reads a signature file and writes a Python C/API module containing - Fortran/C/Python bindings. - -* F2PY compiles all sources and builds an extension module containing - the wrappers. In building extension modules, F2PY uses - ``numpy_distutils`` that supports a number of Fortran 77/90/95 - compilers, including Gnu, Intel, - Sun Fortre, SGI MIPSpro, Absoft, NAG, Compaq etc. compilers. - -Depending on a particular situation, these steps can be carried out -either by just in one command or step-by-step, some steps can be -omitted or combined with others. - -Below I'll describe three typical approaches of using F2PY. -The following `example Fortran 77 code`__ will be used for -illustration: - -.. include:: fib1.f - :literal: - -__ fib1.f - -The quick way -============== - -The quickest way to wrap the Fortran subroutine ``FIB`` to Python is -to run - -:: - - f2py -c fib1.f -m fib1 - -This command builds (see ``-c`` flag, execute ``f2py`` without -arguments to see the explanation of command line options) an extension -module ``fib1.so`` (see ``-m`` flag) to the current directory. Now, in -Python the Fortran subroutine ``FIB`` is accessible via ``fib1.fib``:: - - >>> import numpy - >>> import fib1 - >>> print fib1.fib.__doc__ - fib - Function signature: - fib(a,[n]) - Required arguments: - a : input rank-1 array('d') with bounds (n) - Optional arguments: - n := len(a) input int - - >>> a = numpy.zeros(8,'d') - >>> fib1.fib(a) - >>> print a - [ 0. 1. 1. 2. 3. 5. 8. 13.] - -.. note:: - - * Note that F2PY found that the second argument ``n`` is the - dimension of the first array argument ``a``. Since by default all - arguments are input-only arguments, F2PY concludes that ``n`` can - be optional with the default value ``len(a)``. - - * One can use different values for optional ``n``:: - - >>> a1 = numpy.zeros(8,'d') - >>> fib1.fib(a1,6) - >>> print a1 - [ 0. 1. 1. 2. 3. 5. 0. 0.] - - but an exception is raised when it is incompatible with the input - array ``a``:: - - >>> fib1.fib(a,10) - fib:n=10 - Traceback (most recent call last): - File "<stdin>", line 1, in ? - fib.error: (len(a)>=n) failed for 1st keyword n - >>> - - This demonstrates one of the useful features in F2PY, that it, - F2PY implements basic compatibility checks between related - arguments in order to avoid any unexpected crashes. - - * When a NumPy array, that is Fortran contiguous and has a dtype - corresponding to presumed Fortran type, is used as an input array - argument, then its C pointer is directly passed to Fortran. - - Otherwise F2PY makes a contiguous copy (with a proper dtype) of - the input array and passes C pointer of the copy to Fortran - subroutine. As a result, any possible changes to the (copy of) - input array have no effect to the original argument, as - demonstrated below:: - - >>> a = numpy.ones(8,'i') - >>> fib1.fib(a) - >>> print a - [1 1 1 1 1 1 1 1] - - Clearly, this is not an expected behaviour. The fact that the - above example worked with ``dtype=float`` is considered - accidental. - - F2PY provides ``intent(inplace)`` attribute that would modify - the attributes of an input array so that any changes made by - Fortran routine will be effective also in input argument. For example, - if one specifies ``intent(inplace) a`` (see below, how), then - the example above would read: - - >>> a = numpy.ones(8,'i') - >>> fib1.fib(a) - >>> print a - [ 0. 1. 1. 2. 3. 5. 8. 13.] - - However, the recommended way to get changes made by Fortran - subroutine back to python is to use ``intent(out)`` attribute. It - is more efficient and a cleaner solution. - - * The usage of ``fib1.fib`` in Python is very similar to using - ``FIB`` in Fortran. However, using *in situ* output arguments in - Python indicates a poor style as there is no safety mechanism - in Python with respect to wrong argument types. When using Fortran - or C, compilers naturally discover any type mismatches during - compile time but in Python the types must be checked in - runtime. So, using *in situ* output arguments in Python may cause - difficult to find bugs, not to mention that the codes will be less - readable when all required type checks are implemented. - - Though the demonstrated way of wrapping Fortran routines to Python - is very straightforward, it has several drawbacks (see the comments - above). These drawbacks are due to the fact that there is no way - that F2PY can determine what is the actual intention of one or the - other argument, is it input or output argument, or both, or - something else. So, F2PY conservatively assumes that all arguments - are input arguments by default. - - However, there are ways (see below) how to "teach" F2PY about the - true intentions (among other things) of function arguments; and then - F2PY is able to generate more Pythonic (more explicit, easier to - use, and less error prone) wrappers to Fortran functions. - -The smart way -============== - -Let's apply the steps of wrapping Fortran functions to Python one by -one. - -* First, we create a signature file from ``fib1.f`` by running - - :: - - f2py fib1.f -m fib2 -h fib1.pyf - - The signature file is saved to ``fib1.pyf`` (see ``-h`` flag) and - its contents is shown below. - - .. include:: fib1.pyf - :literal: - -* Next, we'll teach F2PY that the argument ``n`` is an input argument - (use ``intent(in)`` attribute) and that the result, i.e. the - contents of ``a`` after calling Fortran function ``FIB``, should be - returned to Python (use ``intent(out)`` attribute). In addition, an - array ``a`` should be created dynamically using the size given by - the input argument ``n`` (use ``depend(n)`` attribute to indicate - dependence relation). - - The content of a modified version of ``fib1.pyf`` (saved as - ``fib2.pyf``) is as follows: - - .. include:: fib2.pyf - :literal: - -* And finally, we build the extension module by running - - :: - - f2py -c fib2.pyf fib1.f - -In Python:: - - >>> import fib2 - >>> print fib2.fib.__doc__ - fib - Function signature: - a = fib(n) - Required arguments: - n : input int - Return objects: - a : rank-1 array('d') with bounds (n) - - >>> print fib2.fib(8) - [ 0. 1. 1. 2. 3. 5. 8. 13.] - -.. note:: - - * Clearly, the signature of ``fib2.fib`` now corresponds to the - intention of Fortran subroutine ``FIB`` more closely: given the - number ``n``, ``fib2.fib`` returns the first ``n`` Fibonacci numbers - as a NumPy array. Also, the new Python signature ``fib2.fib`` - rules out any surprises that we experienced with ``fib1.fib``. - - * Note that by default using single ``intent(out)`` also implies - ``intent(hide)``. Argument that has ``intent(hide)`` attribute - specified, will not be listed in the argument list of a wrapper - function. - -The quick and smart way -======================== - -The "smart way" of wrapping Fortran functions, as explained above, is -suitable for wrapping (e.g. third party) Fortran codes for which -modifications to their source codes are not desirable nor even -possible. - -However, if editing Fortran codes is acceptable, then the generation -of an intermediate signature file can be skipped in most -cases. Namely, F2PY specific attributes can be inserted directly to -Fortran source codes using the so-called F2PY directive. A F2PY -directive defines special comment lines (starting with ``Cf2py``, for -example) which are ignored by Fortran compilers but F2PY interprets -them as normal lines. - -Here is shown a `modified version of the example Fortran code`__, saved -as ``fib3.f``: - -.. include:: fib3.f - :literal: - -__ fib3.f - -Building the extension module can be now carried out in one command:: - - f2py -c -m fib3 fib3.f - -Notice that the resulting wrapper to ``FIB`` is as "smart" as in -previous case:: - - >>> import fib3 - >>> print fib3.fib.__doc__ - fib - Function signature: - a = fib(n) - Required arguments: - n : input int - Return objects: - a : rank-1 array('d') with bounds (n) - - >>> print fib3.fib(8) - [ 0. 1. 1. 2. 3. 5. 8. 13.] diff --git a/doc/source/f2py/index.rst b/doc/source/f2py/index.rst index 8b7d1453a041..56df31b4e752 100644 --- a/doc/source/f2py/index.rst +++ b/doc/source/f2py/index.rst @@ -1,8 +1,10 @@ -##################################### -F2PY Users Guide and Reference Manual -##################################### +.. _f2py: -The purpose of the ``F2PY`` --*Fortran to Python interface generator*-- +===================================== +F2PY user guide and reference manual +===================================== + +The purpose of the ``F2PY`` --*Fortran to Python interface generator*-- utility is to provide a connection between Python and Fortran languages. F2PY is a part of NumPy_ (``numpy.f2py``) and also available as a standalone command line tool ``f2py`` when ``numpy`` is installed that @@ -19,13 +21,12 @@ from Python. .. toctree:: :maxdepth: 2 - getting-started - signature-file - python-usage usage - distutils + f2py.getting-started + python-usage + signature-file + buildtools/index advanced -.. _Python: http://www.python.org/ -.. _NumPy: http://www.numpy.org/ -.. _SciPy: http://www.numpy.org/ +.. _Python: https://www.python.org/ +.. _NumPy: https://www.numpy.org/ diff --git a/doc/source/f2py/python-usage.rst b/doc/source/f2py/python-usage.rst index 60167d01a1dc..ef8ccd7dd657 100644 --- a/doc/source/f2py/python-usage.rst +++ b/doc/source/f2py/python-usage.rst @@ -4,119 +4,120 @@ Using F2PY bindings in Python All wrappers for Fortran/C routines, common blocks, or for Fortran 90 module data generated by F2PY are exposed to Python as ``fortran`` -type objects. Routine wrappers are callable ``fortran`` type objects +type objects. Routine wrappers are callable ``fortran`` type objects while wrappers to Fortran data have attributes referring to data objects. -All ``fortran`` type object have attribute ``_cpointer`` that contains -CObject referring to the C pointer of the corresponding Fortran/C -function or variable in C level. Such CObjects can be used as a -callback argument of F2PY generated functions to bypass Python C/API -layer of calling Python functions from Fortran or C when the -computational part of such functions is implemented in C or Fortran -and wrapped with F2PY (or any other tool capable of providing CObject -of a function). +All ``fortran`` type objects have an attribute ``_cpointer`` that contains a +``CObject`` referring to the C pointer of the corresponding Fortran/C function +or variable at the C level. Such ``CObjects`` can be used as a callback argument +for F2PY generated functions to bypass the Python C/API layer for calling Python +functions from Fortran or C when the computational aspects of such functions are +implemented in C or Fortran and wrapped with F2PY (or any other tool capable of +providing the ``CObject`` of a function). -Consider a Fortran 77 file ``ftype.f``: +Consider a Fortran 77 file ```ftype.f``: - .. include:: ftype.f - :literal: + .. literalinclude:: ./code/ftype.f + :language: fortran -and build a wrapper using ``f2py -c ftype.f -m ftype``. +and a wrapper built using ``f2py -c ftype.f -m ftype``. In Python: - .. include:: ftype_session.dat - :literal: + .. literalinclude:: ./code/results/ftype_session.dat + :language: python Scalar arguments ================= -In general, a scalar argument of a F2PY generated wrapper function can -be ordinary Python scalar (integer, float, complex number) as well as +In general, a scalar argument for a F2PY generated wrapper function can +be an ordinary Python scalar (integer, float, complex number) as well as an arbitrary sequence object (list, tuple, array, string) of scalars. In the latter case, the first element of the sequence object is passed to Fortran routine as a scalar argument. -Note that when type-casting is required and there is possible loss of -information (e.g. when type-casting float to integer or complex to -float), F2PY does not raise any exception. In complex to real -type-casting only the real part of a complex number is used. +.. note:: + + * When type-casting is required and there is possible loss of information via + narrowing e.g. when type-casting float to integer or complex to float, F2PY + *does not* raise an exception. + + * For complex to real type-casting only the real part of a complex number is used. -``intent(inout)`` scalar arguments are assumed to be array objects in -order to *in situ* changes to be effective. It is recommended to use -arrays with proper type but also other types work. + * ``intent(inout)`` scalar arguments are assumed to be array objects in + order to have *in situ* changes be effective. It is recommended to use + arrays with proper type but also other types work. Consider the following Fortran 77 code: - .. include:: scalar.f - :literal: + .. literalinclude:: ./code/scalar.f + :language: fortran and wrap it using ``f2py -c -m scalar scalar.f``. In Python: - .. include:: scalar_session.dat - :literal: + .. literalinclude:: ./code/results/scalar_session.dat + :language: python String arguments ================= -F2PY generated wrapper functions accept (almost) any Python object as -a string argument, ``str`` is applied for non-string objects. +F2PY generated wrapper functions accept almost any Python object as +a string argument, since ``str`` is applied for non-string objects. Exceptions are NumPy arrays that must have type code ``'c'`` or ``'1'`` when used as string arguments. -A string can have arbitrary length when using it as a string argument -to F2PY generated wrapper function. If the length is greater than -expected, the string is truncated. If the length is smaller that +A string can have an arbitrary length when used as a string argument +for an F2PY generated wrapper function. If the length is greater than +expected, the string is truncated silently. If the length is smaller than expected, additional memory is allocated and filled with ``\0``. Because Python strings are immutable, an ``intent(inout)`` argument -expects an array version of a string in order to *in situ* changes to -be effective. +expects an array version of a string in order to have *in situ* changes be effective. Consider the following Fortran 77 code: - .. include:: string.f - :literal: + .. literalinclude:: ./code/string.f + :language: fortran and wrap it using ``f2py -c -m mystring string.f``. Python session: - .. include:: string_session.dat - :literal: + .. literalinclude:: ./code/results/string_session.dat + :language: python Array arguments ================ -In general, array arguments of F2PY generated wrapper functions accept -arbitrary sequences that can be transformed to NumPy array objects. -An exception is ``intent(inout)`` array arguments that always must be -proper-contiguous and have proper type, otherwise an exception is -raised. Another exception is ``intent(inplace)`` array arguments that -attributes will be changed in-situ if the argument has different type -than expected (see ``intent(inplace)`` attribute for more -information). - -In general, if a NumPy array is proper-contiguous and has a proper -type then it is directly passed to wrapped Fortran/C function. -Otherwise, an element-wise copy of an input array is made and the -copy, being proper-contiguous and with proper type, is used as an -array argument. +In general, array arguments for F2PY generated wrapper functions accept +arbitrary sequences that can be transformed to NumPy array objects. There are +two notable exceptions: + +* ``intent(inout)`` array arguments must always be proper-contiguous (defined below) and have a + compatible ``dtype``, otherwise an exception is raised. +* ``intent(inplace)`` array arguments will be changed *in situ* if the argument + has a different type than expected (see the ``intent(inplace)`` attribute for + more information). + +In general, if a NumPy array is proper-contiguous and has a proper type then it +is directly passed to the wrapped Fortran/C function. Otherwise, an element-wise +copy of the input array is made and the copy, being proper-contiguous and with +proper type, is used as the array argument. There are two types of proper-contiguous NumPy arrays: -* Fortran-contiguous arrays when data is stored column-wise, - i.e. indexing of data as stored in memory starts from the lowest +* Fortran-contiguous arrays refer to data that is stored columnwise, + i.e. the indexing of data as stored in memory starts from the lowest dimension; -* C-contiguous or simply contiguous arrays when data is stored - row-wise, i.e. indexing of data as stored in memory starts from the - highest dimension. +* C-contiguous, or simply contiguous arrays, refer to data that is stored + rowwise, i.e. the indexing of data as stored in memory starts from the highest + dimension. For one-dimensional arrays these notions coincide. @@ -129,38 +130,33 @@ and C-contiguous if the order is as follows:: A[0,0] A[0,1] A[1,0] A[1,1] -To test whether an array is C-contiguous, use ``.iscontiguous()`` -method of NumPy arrays. To test for Fortran contiguity, all -F2PY generated extension modules provide a function -``has_column_major_storage(<array>)``. This function is equivalent to -``<array>.flags.f_contiguous`` but more efficient. - -Usually there is no need to worry about how the arrays are stored in -memory and whether the wrapped functions, being either Fortran or C -functions, assume one or another storage order. F2PY automatically -ensures that wrapped functions get arguments with proper storage -order; the corresponding algorithm is designed to make copies of -arrays only when absolutely necessary. However, when dealing with very -large multidimensional input arrays with sizes close to the size of -the physical memory in your computer, then a care must be taken to use -always proper-contiguous and proper type arguments. +To test whether an array is C-contiguous, use the ``.flags.c_contiguous`` +attribute of NumPy arrays. To test for Fortran contiguity, use the +``.flags.f_contiguous`` attribute. + +Usually there is no need to worry about how the arrays are stored in memory and +whether the wrapped functions, being either Fortran or C functions, assume one +or another storage order. F2PY automatically ensures that wrapped functions get +arguments with the proper storage order; the underlying algorithm is designed to +make copies of arrays only when absolutely necessary. However, when dealing with +very large multidimensional input arrays with sizes close to the size of the +physical memory in your computer, then care must be taken to ensure the usage of +proper-contiguous and proper type arguments. To transform input arrays to column major storage order before passing -them to Fortran routines, use a function -``as_column_major_storage(<array>)`` that is provided by all F2PY -generated extension modules. +them to Fortran routines, use the function ``numpy.asfortranarray(<array>)``. -Consider Fortran 77 code: +Consider the following Fortran 77 code: - .. include:: array.f - :literal: + .. literalinclude:: ./code/array.f + :language: fortran and wrap it using ``f2py -c -m arr array.f -DF2PY_REPORT_ON_ARRAY_COPY=1``. In Python: - .. include:: array_session.dat - :literal: + .. literalinclude:: ./code/results/array_session.dat + :language: python .. _Call-back arguments: @@ -171,31 +167,32 @@ F2PY supports calling Python functions from Fortran or C codes. Consider the following Fortran 77 code: - .. include:: callback.f - :literal: + .. literalinclude:: ./code/callback.f + :language: fortran and wrap it using ``f2py -c -m callback callback.f``. In Python: - .. include:: callback_session.dat - :literal: + .. literalinclude:: ./code/results/callback_session.dat + :language: python In the above example F2PY was able to guess accurately the signature -of a call-back function. However, sometimes F2PY cannot establish the -signature as one would wish and then the signature of a call-back -function must be modified in the signature file manually. Namely, -signature files may contain special modules (the names of such modules -contain a substring ``__user__``) that collect various signatures of -call-back functions. Callback arguments in routine signatures have -attribute ``external`` (see also ``intent(callback)`` attribute). To -relate a callback argument and its signature in ``__user__`` module -block, use ``use`` statement as illustrated below. The same signature -of a callback argument can be referred in different routine +of the call-back function. However, sometimes F2PY cannot establish the +appropriate signature; in these cases the signature of the call-back +function must be explicitly defined in the signature file. + +To facilitate this, signature files may contain special modules (the names of +these modules contain the special ``__user__`` sub-string) that defines the +various signatures for call-back functions. Callback arguments in routine +signatures have the ``external`` attribute (see also the ``intent(callback)`` +attribute). To relate a callback argument with its signature in a ``__user__`` +module block, a ``use`` statement can be utilized as illustrated below. The same +signature for a callback argument can be referred to in different routine signatures. -We use the same Fortran 77 code as in previous example but now -we'll pretend that F2PY was not able to guess the signatures of +We use the same Fortran 77 code as in the previous example but now +we will pretend that F2PY was not able to guess the signatures of call-back arguments correctly. First, we create an initial signature file ``callback2.pyf`` using F2PY:: @@ -203,40 +200,40 @@ file ``callback2.pyf`` using F2PY:: Then modify it as follows - .. include:: callback2.pyf + .. include:: ./code/callback2.pyf :literal: -Finally, build the extension module using ``f2py -c callback2.pyf callback.f``. +Finally, we build the extension module using ``f2py -c callback2.pyf callback.f``. -An example Python session would be identical to the previous example -except that argument names would differ. +An example Python session for this snippet would be identical to the previous +example except that the argument names would differ. Sometimes a Fortran package may require that users provide routines that the package will use. F2PY can construct an interface to such -routines so that Python functions could be called from Fortran. +routines so that Python functions can be called from Fortran. -Consider the following Fortran 77 subroutine that takes an array +Consider the following Fortran 77 subroutine that takes an array as its input and applies a function ``func`` to its elements. - .. include:: calculate.f - :literal: + .. literalinclude:: ./code/calculate.f + :language: fortran -It is expected that function ``func`` has been defined -externally. In order to use a Python function as ``func``, it must -have an attribute ``intent(callback)`` (it must be specified before -the ``external`` statement). +The Fortran code expects that the function ``func`` has been defined externally. +In order to use a Python function for ``func``, it must have an attribute +``intent(callback)`` and, it must be specified before the ``external`` statement. Finally, build an extension module using ``f2py -c -m foo calculate.f`` In Python: - .. include:: calculate_session.dat - :literal: + .. literalinclude:: ./code/results/calculate_session.dat + :language: python -The function is included as an argument to the python function call to -the Fortran subroutine even though it was *not* in the Fortran subroutine argument -list. The "external" refers to the C function generated by f2py, not the python -function itself. The python function must be supplied to the C function. +The function is included as an argument to the python function call to the +Fortran subroutine even though it was *not* in the Fortran subroutine argument +list. The "external" keyword refers to the C function generated by f2py, not the +python function itself. The python function is essentially being supplied to the +C function. The callback function may also be explicitly set in the module. Then it is not necessary to pass the function in the argument list to @@ -245,24 +242,24 @@ the python callback function is itself called by another Fortran function. Consider the following Fortran 77 subroutine: - .. include:: extcallback.f - :literal: + .. literalinclude:: ./code/extcallback.f + :language: fortran and wrap it using ``f2py -c -m pfromf extcallback.f``. In Python: - .. include:: extcallback_session.dat - :literal: + .. literalinclude:: ./code/results/extcallback_session.dat + :language: python Resolving arguments to call-back functions ------------------------------------------- +=========================================== -F2PY generated interface is very flexible with respect to call-back +F2PY generated interfaces are very flexible with respect to call-back arguments. For each call-back argument an additional optional argument ``<name>_extra_args`` is introduced by F2PY. This argument can be used to pass extra arguments to user provided call-back -arguments. +functions. If a F2PY generated wrapper function expects the following call-back argument:: @@ -286,7 +283,7 @@ is provided by a user, and in addition, fun_extra_args = (e_1,...,e_p) is used, then the following rules are applied when a Fortran or C -function calls the call-back argument ``gun``: +function evaluates the call-back argument ``gun``: * If ``p == 0`` then ``gun(a_1, ..., a_q)`` is called, here ``q = min(m, n)``. @@ -297,8 +294,8 @@ function calls the call-back argument ``gun``: * If ``n + p`` is less than the number of required arguments to ``gun`` then an exception is raised. -The function ``gun`` may return any number of objects as a tuple. Then -following rules are applied: +If the function ``gun`` may return any number of objects as a tuple; then +the following rules are applied: * If ``k < l``, then ``y_{k + 1}, ..., y_l`` are ignored. * If ``k > l``, then only ``x_1, ..., x_l`` are set. @@ -308,62 +305,62 @@ Common blocks ============== F2PY generates wrappers to ``common`` blocks defined in a routine -signature block. Common blocks are visible by all Fortran codes linked -with the current extension module, but not to other extension modules -(this restriction is due to how Python imports shared libraries). In +signature block. Common blocks are visible to all Fortran codes linked +to the current extension module, but not to other extension modules +(this restriction is due to the way Python imports shared libraries). In Python, the F2PY wrappers to ``common`` blocks are ``fortran`` type -objects that have (dynamic) attributes related to data members of -common blocks. When accessed, these attributes return as NumPy array -objects (multidimensional arrays are Fortran-contiguous) that +objects that have (dynamic) attributes related to the data members of +the common blocks. When accessed, these attributes return as NumPy array +objects (multidimensional arrays are Fortran-contiguous) which directly link to data members in common blocks. Data members can be changed by direct assignment or by in-place changes to the corresponding array objects. Consider the following Fortran 77 code: - .. include:: common.f - :literal: + .. literalinclude:: ./code/common.f + :language: fortran and wrap it using ``f2py -c -m common common.f``. In Python: - .. include:: common_session.dat - :literal: + .. literalinclude:: ./code/results/common_session.dat + :language: python Fortran 90 module data ======================= -The F2PY interface to Fortran 90 module data is similar to Fortran 77 +The F2PY interface to Fortran 90 module data is similar to the handling of Fortran 77 common blocks. Consider the following Fortran 90 code: - .. include:: moddata.f90 - :literal: + .. literalinclude:: ./code/moddata.f90 + :language: fortran and wrap it using ``f2py -c -m moddata moddata.f90``. In Python: - .. include:: moddata_session.dat - :literal: + .. literalinclude:: ./code/results/moddata_session.dat + :language: python Allocatable arrays -------------------- +=================== F2PY has basic support for Fortran 90 module allocatable arrays. Consider the following Fortran 90 code: - .. include:: allocarr.f90 - :literal: + .. literalinclude:: ./code/allocarr.f90 + :language: fortran and wrap it using ``f2py -c -m allocarr allocarr.f90``. In Python: - .. include:: allocarr_session.dat - :literal: + .. literalinclude:: ./code/results/allocarr_session.dat + :language: python diff --git a/doc/source/f2py/run_main_session.dat b/doc/source/f2py/run_main_session.dat deleted file mode 100644 index 29ecc3dfe402..000000000000 --- a/doc/source/f2py/run_main_session.dat +++ /dev/null @@ -1,14 +0,0 @@ ->>> import f2py2e ->>> r=f2py2e.run_main(['-m','scalar','docs/usersguide/scalar.f']) -Reading fortran codes... - Reading file 'docs/usersguide/scalar.f' -Post-processing... - Block: scalar - Block: FOO -Building modules... - Building module "scalar"... - Wrote C/API module "scalar" to file "./scalarmodule.c" ->>> print r -{'scalar': {'h': ['/home/users/pearu/src_cvs/f2py2e/src/fortranobject.h'], - 'csrc': ['./scalarmodule.c', - '/home/users/pearu/src_cvs/f2py2e/src/fortranobject.c']}} diff --git a/doc/source/f2py/scalar_session.dat b/doc/source/f2py/scalar_session.dat deleted file mode 100644 index 8aff097c2f1e..000000000000 --- a/doc/source/f2py/scalar_session.dat +++ /dev/null @@ -1,21 +0,0 @@ ->>> import scalar ->>> print scalar.foo.__doc__ -foo - Function signature: - foo(a,b) -Required arguments: - a : input float - b : in/output rank-0 array(float,'d') - ->>> scalar.foo(2,3) - A= 2. B= 3. - INCREMENT A AND B - NEW A= 3. B= 4. ->>> import numpy ->>> a=numpy.array(2) # these are integer rank-0 arrays ->>> b=numpy.array(3) ->>> scalar.foo(a,b) - A= 2. B= 3. - INCREMENT A AND B - NEW A= 3. B= 4. ->>> print a,b # note that only b is changed in situ -2 4 diff --git a/doc/source/f2py/signature-file.rst b/doc/source/f2py/signature-file.rst index bd926f33c705..b80b31509661 100644 --- a/doc/source/f2py/signature-file.rst +++ b/doc/source/f2py/signature-file.rst @@ -2,23 +2,22 @@ Signature file ================== -The syntax specification for signature files (.pyf files) is borrowed -from the Fortran 90/95 language specification. Almost all Fortran -90/95 standard constructs are understood, both in free and fixed -format (recall that Fortran 77 is a subset of Fortran 90/95). F2PY -introduces also some extensions to Fortran 90/95 language -specification that help designing Fortran to Python interface, make it -more "Pythonic". - -Signature files may contain arbitrary Fortran code (so that Fortran -codes can be considered as signature files). F2PY silently ignores +The syntax specification for signature files (.pyf files) is modeled on the +Fortran 90/95 language specification. Almost all Fortran 90/95 standard +constructs are understood, both in free and fixed format (recall that Fortran 77 +is a subset of Fortran 90/95). F2PY introduces some extensions to the Fortran +90/95 language specification that help in the design of the Fortran to Python +interface, making it more "Pythonic". + +Signature files may contain arbitrary Fortran code so that any Fortran 90/95 +codes can be treated as signature files. F2PY silently ignores Fortran constructs that are irrelevant for creating the interface. -However, this includes also syntax errors. So, be careful not making -ones;-). +However, this also means that syntax errors are not caught by F2PY and will only +be caught when the library is built. -In general, the contents of signature files is case-sensitive. When -scanning Fortran codes and writing a signature file, F2PY lowers all -cases automatically except in multiline blocks or when ``--no-lower`` +In general, the contents of the signature files are case-sensitive. When +scanning Fortran codes to generate a signature file, F2PY lowers all +cases automatically except in multi-line blocks or when the ``--no-lower`` option is used. The syntax of signature files is presented below. @@ -27,13 +26,15 @@ Python module block ===================== A signature file may contain one (recommended) or more ``python -module`` blocks. ``python module`` block describes the contents of +module`` blocks. The ``python module`` block describes the contents of a Python/C extension module ``<modulename>module.c`` that F2PY generates. -Exception: if ``<modulename>`` contains a substring ``__user__``, then -the corresponding ``python module`` block describes the signatures of -so-called call-back functions (see :ref:`Call-back arguments`). +.. warning:: + + Exception: if ``<modulename>`` contains a substring ``__user__``, then the + corresponding ``python module`` block describes the signatures of call-back + functions (see :ref:`Call-back arguments`). A ``python module`` block has the following structure:: @@ -56,9 +57,9 @@ A ``python module`` block has the following structure:: ]... end [python module [<modulename>]] -Here brackets ``[]`` indicate an optional part, dots ``...`` indicate -one or more of a previous part. So, ``[]...`` reads zero or more of a -previous part. +Here brackets ``[]`` indicate an optional section, dots ``...`` indicate one or +more of a previous section. So, ``[]...`` is to be read as zero or more of a +previous section. Fortran/C routine signatures @@ -93,7 +94,7 @@ The signature of a Fortran block data has the following structure:: end [ block data [<block data name>] ] Type declarations ------------------ +================= The definition of the ``<argument/variable type declaration>`` part is @@ -123,33 +124,36 @@ where and -+ ``<attrspec>`` is a comma separated list of attributes_; +* ``<attrspec>`` is a comma separated list of attributes_; -+ ``<arrayspec>`` is a comma separated list of dimension bounds; +* ``<arrayspec>`` is a comma separated list of dimension bounds; -+ ``<init_expr>`` is a `C expression`__. +* ``<init_expr>`` is a `C expression`__; -+ ``<intlen>`` may be negative integer for ``integer`` type +* ``<intlen>`` may be negative integer for ``integer`` type specifications. In such cases ``integer*<negintlen>`` represents - unsigned C integers. + unsigned C integers; __ `C expressions`_ If an argument has no ``<argument type declaration>``, its type is determined by applying ``implicit`` rules to its name. - Statements ----------- +========== + +Attribute statements +^^^^^^^^^^^^^^^^^^^^^ -Attribute statements: - The ``<argument/variable attribute statement>`` is +* The ``<argument/variable attribute statement>`` is ``<argument/variable type declaration>`` without ``<typespec>``. - In addition, in an attribute statement one cannot use other +* In addition, in an attribute statement one cannot use other attributes, also ``<entitydecl>`` can be only a list of names. -Use statements: - The definition of the ``<use statement>`` part is +Use statements +^^^^^^^^^^^^^^^ + +* The definition of the ``<use statement>`` part is :: @@ -161,12 +165,14 @@ Use statements: <rename_list> := <local_name> => <use_name> [ , <rename_list> ] - Currently F2PY uses ``use`` statement only for linking call-back +* Currently F2PY uses ``use`` statement only for linking call-back modules and ``external`` arguments (call-back functions), see :ref:`Call-back arguments`. -Common block statements: - The definition of the ``<common block statement>`` part is +Common block statements +^^^^^^^^^^^^^^^^^^^^^^^ + +* The definition of the ``<common block statement>`` part is :: @@ -178,18 +184,19 @@ Common block statements: <shortentitydecl> := <name> [ ( <arrayspec> ) ] [ , <shortentitydecl> ] - If a ``python module`` block contains two or more ``common`` blocks +* If a ``python module`` block contains two or more ``common`` blocks with the same name, the variables from the additional declarations are appended. The types of variables in ``<shortentitydecl>`` are defined using ``<argument type declarations>``. Note that the corresponding ``<argument type declarations>`` may contain array - specifications; then you don't need to specify these in - ``<shortentitydecl>``. + specifications; then these need not be specified in ``<shortentitydecl>``. -Other statements: - The ``<other statement>`` part refers to any other Fortran language +Other statements +^^^^^^^^^^^^^^^^^ + +* The ``<other statement>`` part refers to any other Fortran language constructs that are not described above. F2PY ignores most of them - except + except the following: + ``call`` statements and function calls of ``external`` arguments (`more details`__?); @@ -223,7 +230,7 @@ Other statements: Implicit rules are used to determine the type specification of a variable (from the first-letter of its name) if the variable is not defined using ``<variable type declaration>``. Default - implicit rule is given by + implicit rules are given by: :: @@ -234,153 +241,170 @@ Other statements: entry <entry name> [([<arguments>])] - F2PY generates wrappers to all entry names using the signature + F2PY generates wrappers for all entry names using the signature of the routine block. - Tip: ``entry`` statement can be used to describe the signature - of an arbitrary routine allowing F2PY to generate a number of - wrappers from only one routine block signature. There are few - restrictions while doing this: ``fortranname`` cannot be used, - ``callstatement`` and ``callprotoargument`` can be used only if - they are valid for all entry routines, etc. + .. note:: + + The ``entry`` statement can be used to describe the signature of an + arbitrary subroutine or function allowing F2PY to generate a number of + wrappers from only one routine block signature. There are few + restrictions while doing this: ``fortranname`` cannot be used, + ``callstatement`` and ``callprotoargument`` can be used only if they are + valid for all entry routines, etc. + +F2PY statements +^^^^^^^^^^^^^^^^ In addition, F2PY introduces the following statements: - + ``threadsafe`` - Use ``Py_BEGIN_ALLOW_THREADS .. Py_END_ALLOW_THREADS`` block - around the call to Fortran/C function. - - + ``callstatement <C-expr|multi-line block>`` - Replace F2PY generated call statement to Fortran/C function with - ``<C-expr|multi-line block>``. The wrapped Fortran/C function - is available as ``(*f2py_func)``. To raise an exception, set - ``f2py_success = 0`` in ``<C-expr|multi-line block>``. - - + ``callprotoargument <C-typespecs>`` - When ``callstatement`` statement is used then F2PY may not - generate proper prototypes for Fortran/C functions (because - ``<C-expr>`` may contain any function calls and F2PY has no way - to determine what should be the proper prototype). With this - statement you can explicitly specify the arguments of the - corresponding prototype:: - - extern <return type> FUNC_F(<routine name>,<ROUTINE NAME>)(<callprotoargument>); - - + ``fortranname [<actual Fortran/C routine name>]`` - You can use arbitrary ``<routine name>`` for a given Fortran/C - function. Then you have to specify - ``<actual Fortran/C routine name>`` with this statement. - - If ``fortranname`` statement is used without - ``<actual Fortran/C routine name>`` then a dummy wrapper is - generated. - - + ``usercode <multi-line block>`` - When used inside ``python module`` block, then given C code - will be inserted to generated C/API source just before - wrapper function definitions. Here you can define arbitrary - C functions to be used in initialization of optional arguments, - for example. If ``usercode`` is used twice inside ``python - module`` block then the second multiline block is inserted - after the definition of external routines. - - When used inside ``<routine signature>``, then given C code will - be inserted to the corresponding wrapper function just after - declaring variables but before any C statements. So, ``usercode`` - follow-up can contain both declarations and C statements. - - When used inside the first ``interface`` block, then given C - code will be inserted at the end of the initialization - function of the extension module. Here you can modify extension - modules dictionary. For example, for defining additional - variables etc. - - + ``pymethoddef <multiline block>`` - Multiline block will be inserted to the definition of - module methods ``PyMethodDef``-array. It must be a - comma-separated list of C arrays (see `Extending and Embedding`__ - Python documentation for details). - ``pymethoddef`` statement can be used only inside - ``python module`` block. - - __ http://www.python.org/doc/current/ext/ext.html +``threadsafe`` + Uses a ``Py_BEGIN_ALLOW_THREADS .. Py_END_ALLOW_THREADS`` block + around the call to Fortran/C function. + +``callstatement <C-expr|multi-line block>`` + Replaces the F2PY generated call statement to Fortran/C function with + ``<C-expr|multi-line block>``. The wrapped Fortran/C function is available + as ``(*f2py_func)``. + + To raise an exception, set ``f2py_success = 0`` in ``<C-expr|multi-line + block>``. + +``callprotoargument <C-typespecs>`` + When the ``callstatement`` statement is used then F2PY may not + generate proper prototypes for Fortran/C functions (because + ``<C-expr>`` may contain any function calls and F2PY has no way + to determine what should be the proper prototype). + + With this statement you can explicitly specify the arguments of the + corresponding prototype:: + + extern <return type> FUNC_F(<routine name>,<ROUTINE NAME>)(<callprotoargument>); + +``fortranname [<actual Fortran/C routine name>]`` + F2PY allows for the use of an arbitrary ``<routine name>`` for a given + Fortran/C function. Then this statement is used for the ``<actual + Fortran/C routine name>``. + + If ``fortranname`` statement is used without + ``<actual Fortran/C routine name>`` then a dummy wrapper is + generated. + +``usercode <multi-line block>`` + When this is used inside a ``python module`` block, the given C code will + be inserted to generated C/API source just before wrapper function + definitions. + + Here you can define arbitrary C functions to be used for the + initialization of optional arguments. + + For example, if ``usercode`` is used twice inside ``python module`` block + then the second multi-line block is inserted after the definition of + the external routines. + + When used inside ``<routine signature>``, then the given C code will be + inserted into the corresponding wrapper function just after the + declaration of variables but before any C statements. So, the + ``usercode`` follow-up can contain both declarations and C statements. + + When used inside the first ``interface`` block, then the given C code will + be inserted at the end of the initialization function of the extension + module. This is how the extension modules dictionary can be modified and + has many use-cases; for example, to define additional variables. + +``pymethoddef <multiline block>`` + This is a multi-line block which will be inserted into the definition of a + module methods ``PyMethodDef``-array. It must be a comma-separated list of + C arrays (see `Extending and Embedding`__ Python documentation for + details). ``pymethoddef`` statement can be used only inside ``python + module`` block. + + __ https://docs.python.org/extending/index.html Attributes ------------- +============ The following attributes are used by F2PY: ``optional`` The corresponding argument is moved to the end of ``<optional arguments>`` list. A default value for an optional argument can be - specified ``<init_expr>``, see ``entitydecl`` definition. Note that - the default value must be given as a valid C expression. + specified via ``<init_expr>``, see the ``entitydecl`` definition. + - Note that whenever ``<init_expr>`` is used, ``optional`` attribute - is set automatically by F2PY. + .. note:: - For an optional array argument, all its dimensions must be bounded. + * The default value must be given as a valid C expression. + * Whenever ``<init_expr>`` is used, ``optional`` attribute + is set automatically by F2PY. + * For an optional array argument, all its dimensions must be bounded. ``required`` - The corresponding argument is considered as a required one. This is - default. You need to specify ``required`` only if there is a need to - disable automatic ``optional`` setting when ``<init_expr>`` is used. + The corresponding argument with this attribute considered mandatory. This is + the default. ``required`` should only be specified if there is a need to + disable the automatic ``optional`` setting when ``<init_expr>`` is used. - If Python ``None`` object is used as a required argument, the + If a Python ``None`` object is used as a required argument, the argument is treated as optional. That is, in the case of array - argument, the memory is allocated. And if ``<init_expr>`` is given, - the corresponding initialization is carried out. + argument, the memory is allocated. If ``<init_expr>`` is given, then the + corresponding initialization is carried out. ``dimension(<arrayspec>)`` - The corresponding variable is considered as an array with given - dimensions in ``<arrayspec>``. + The corresponding variable is considered as an array with dimensions given in + ``<arrayspec>``. ``intent(<intentspec>)`` This specifies the "intention" of the corresponding argument. ``<intentspec>`` is a comma separated list of the following keys: - + ``in`` - The argument is considered as an input-only argument. It means - that the value of the argument is passed to Fortran/C function and - that function is expected not to change the value of an argument. - - + ``inout`` - The argument is considered as an input/output or *in situ* - output argument. ``intent(inout)`` arguments can be only - "contiguous" NumPy arrays with proper type and size. Here - "contiguous" can be either in Fortran or C sense. The latter one - coincides with the contiguous concept used in NumPy and is - effective only if ``intent(c)`` is used. Fortran contiguity - is assumed by default. - - Using ``intent(inout)`` is generally not recommended, use - ``intent(in,out)`` instead. See also ``intent(inplace)`` attribute. - - + ``inplace`` - The argument is considered as an input/output or *in situ* - output argument. ``intent(inplace)`` arguments must be - NumPy arrays with proper size. If the type of an array is - not "proper" or the array is non-contiguous then the array - will be changed in-place to fix the type and make it contiguous. - - Using ``intent(inplace)`` is generally not recommended either. - For example, when slices have been taken from an - ``intent(inplace)`` argument then after in-place changes, - slices data pointers may point to unallocated memory area. - - + ``out`` - The argument is considered as a return variable. It is appended - to the ``<returned variables>`` list. Using ``intent(out)`` - sets ``intent(hide)`` automatically, unless also - ``intent(in)`` or ``intent(inout)`` were used. - - By default, returned multidimensional arrays are - Fortran-contiguous. If ``intent(c)`` is used, then returned - multidimensional arrays are C-contiguous. - - + ``hide`` - The argument is removed from the list of required or optional + * ``in`` + The corresponding argument is considered to be input-only. This means that the value of + the argument is passed to a Fortran/C function and that the function is + expected to not change the value of this argument. + + * ``inout`` + The corresponding argument is marked for input/output or as an *in situ* output + argument. ``intent(inout)`` arguments can be only "contiguous" NumPy + arrays with proper type and size. Here "contiguous" can be either in the + Fortran or C sense. The latter coincides with the default contiguous + concept used in NumPy and is effective only if ``intent(c)`` is used. F2PY + assumes Fortran contiguous arguments by default. + + .. note:: + + Using ``intent(inout)`` is generally not recommended, use ``intent(in,out)`` instead. + + See also the ``intent(inplace)`` attribute. + + * ``inplace`` + The corresponding argument is considered to be an input/output or *in situ* output + argument. ``intent(inplace)`` arguments must be NumPy arrays of a proper + size. If the type of an array is not "proper" or the array is + non-contiguous then the array will be modified in-place to fix the type and + make it contiguous. + + .. note:: + + Using ``intent(inplace)`` is generally not recommended either. + + For example, when slices have been taken from an ``intent(inplace)`` argument + then after in-place changes, the data pointers for the slices may point to + an unallocated memory area. + + + * ``out`` + The corresponding argument is considered to be a return variable. It is appended to the + ``<returned variables>`` list. Using ``intent(out)`` sets ``intent(hide)`` + automatically, unless ``intent(in)`` or ``intent(inout)`` are specified + as well. + + By default, returned multidimensional arrays are Fortran-contiguous. If + ``intent(c)`` attribute is used, then the returned multidimensional arrays + are C-contiguous. + + * ``hide`` + The corresponding argument is removed from the list of required or optional arguments. Typically ``intent(hide)`` is used with ``intent(out)`` or when ``<init_expr>`` completely determines the value of the argument like in the following example:: @@ -388,18 +412,17 @@ The following attributes are used by F2PY: integer intent(hide),depend(a) :: n = len(a) real intent(in),dimension(n) :: a - + ``c`` - The argument is treated as a C scalar or C array argument. In - the case of a scalar argument, its value is passed to C function - as a C scalar argument (recall that Fortran scalar arguments are - actually C pointer arguments). In the case of an array - argument, the wrapper function is assumed to treat + * ``c`` + The corresponding argument is treated as a C scalar or C array argument. For the case + of a scalar argument, its value is passed to a C function as a C scalar + argument (recall that Fortran scalar arguments are actually C pointer + arguments). For array arguments, the wrapper function is assumed to treat multidimensional arrays as C-contiguous arrays. There is no need to use ``intent(c)`` for one-dimensional - arrays, no matter if the wrapped function is either a Fortran or - a C function. This is because the concepts of Fortran- and - C contiguity overlap in one-dimensional cases. + arrays, irrespective of whether the wrapped function is in Fortran or C. + This is because the concepts of Fortran- and C contiguity overlap in + one-dimensional cases. If ``intent(c)`` is used as a statement but without an entity declaration list, then F2PY adds the ``intent(c)`` attribute to all @@ -409,110 +432,121 @@ The following attributes are used by F2PY: attribute for ``<routine name>`` in order to disable Fortran specific ``F_FUNC(..,..)`` macros. - + ``cache`` - The argument is treated as a junk of memory. No Fortran nor C - contiguity checks are carried out. Using ``intent(cache)`` - makes sense only for array arguments, also in connection with - ``intent(hide)`` or ``optional`` attributes. - - + ``copy`` - Ensure that the original contents of ``intent(in)`` argument is - preserved. Typically used in connection with ``intent(in,out)`` - attribute. F2PY creates an optional argument - ``overwrite_<argument name>`` with the default value ``0``. - - + ``overwrite`` - The original contents of the ``intent(in)`` argument may be - altered by the Fortran/C function. F2PY creates an optional - argument ``overwrite_<argument name>`` with the default value - ``1``. - - + ``out=<new name>`` - Replace the return name with ``<new name>`` in the ``__doc__`` - string of a wrapper function. - - + ``callback`` - Construct an external function suitable for calling Python function + * ``cache`` + The corresponding argument is treated as junk memory. No Fortran nor C contiguity + checks are carried out. Using ``intent(cache)`` makes sense only for array + arguments, also in conjunction with ``intent(hide)`` or ``optional`` + attributes. + + * ``copy`` + Ensures that the original contents of ``intent(in)`` argument is + preserved. Typically used with the ``intent(in,out)`` attribute. F2PY + creates an optional argument ``overwrite_<argument name>`` with the + default value ``0``. + + * ``overwrite`` + This indicates that the original contents of the ``intent(in)`` argument + may be altered by the Fortran/C function. F2PY creates an optional + argument ``overwrite_<argument name>`` with the default value ``1``. + + * ``out=<new name>`` + Replaces the returned name with ``<new name>`` in the ``__doc__`` string + of the wrapper function. + + * ``callback`` + Constructs an external function suitable for calling Python functions from Fortran. ``intent(callback)`` must be specified before the - corresponding ``external`` statement. If 'argument' is not in - argument list then it will be added to Python wrapper but only - initializing external function. - - Use ``intent(callback)`` in situations where a Fortran/C code - assumes that a user implements a function with given prototype - and links it to an executable. Don't use ``intent(callback)`` - if function appears in the argument list of a Fortran routine. - - With ``intent(hide)`` or ``optional`` attributes specified and - using a wrapper function without specifying the callback argument - in argument list then call-back function is looked in the - namespace of F2PY generated extension module where it can be - set as a module attribute by a user. - - + ``aux`` - Define auxiliary C variable in F2PY generated wrapper function. - Useful to save parameter values so that they can be accessed - in initialization expression of other variables. Note that - ``intent(aux)`` silently implies ``intent(c)``. + corresponding ``external`` statement. If the 'argument' is not in + the argument list then it will be added to Python wrapper but only + by initializing an external function. + + .. note:: + + Use ``intent(callback)`` in situations where the Fortran/C code assumes + that the user implemented a function with a given prototype and linked + it to an executable. Don't use ``intent(callback)`` if the function + appears in the argument list of a Fortran routine. + + With ``intent(hide)`` or ``optional`` attributes specified and using a + wrapper function without specifying the callback argument in the argument + list; then the call-back function is assumed to be found in the namespace + of the F2PY generated extension module where it can be set as a module + attribute by a user. + + * ``aux`` + Defines an auxiliary C variable in the F2PY generated wrapper function. + Useful to save parameter values so that they can be accessed in + initialization expressions for other variables. + + .. note:: + + ``intent(aux)`` silently implies ``intent(c)``. The following rules apply: - + If no ``intent(in | inout | out | hide)`` is specified, + * If none of ``intent(in | inout | out | hide)`` are specified, ``intent(in)`` is assumed. - + ``intent(in,inout)`` is ``intent(in)``. - + ``intent(in,hide)`` or ``intent(inout,hide)`` is - ``intent(hide)``. - + ``intent(out)`` is ``intent(out,hide)`` unless ``intent(in)`` or - ``intent(inout)`` is specified. - + If ``intent(copy)`` or ``intent(overwrite)`` is used, then an - additional optional argument is introduced with a name - ``overwrite_<argument name>`` and a default value 0 or 1, respectively. - + ``intent(inout,inplace)`` is ``intent(inplace)``. - + ``intent(in,inplace)`` is ``intent(inplace)``. - + ``intent(hide)`` disables ``optional`` and ``required``. + + * ``intent(in,inout)`` is ``intent(in)``; + + * ``intent(in,hide)`` or ``intent(inout,hide)`` is ``intent(hide)``; + + * ``intent(out)`` is ``intent(out,hide)`` unless ``intent(in)`` or + ``intent(inout)`` is specified. + + * If ``intent(copy)`` or ``intent(overwrite)`` is used, then an additional + optional argument is introduced with a name ``overwrite_<argument name>`` + and a default value 0 or 1, respectively. + + * ``intent(inout,inplace)`` is ``intent(inplace)``; + + * ``intent(in,inplace)`` is ``intent(inplace)``; + + * ``intent(hide)`` disables ``optional`` and ``required``. ``check([<C-booleanexpr>])`` - Perform consistency check of arguments by evaluating - ``<C-booleanexpr>``; if ``<C-booleanexpr>`` returns 0, an exception - is raised. + Performs a consistency check on the arguments by evaluating + ``<C-booleanexpr>``; if ``<C-booleanexpr>`` returns 0, an exception is raised. + + .. note:: - If ``check(..)`` is not used then F2PY generates few standard checks - (e.g. in a case of an array argument, check for the proper shape - and size) automatically. Use ``check()`` to disable checks generated - by F2PY. + If ``check(..)`` is not used then F2PY automatically generates a few + standard checks (e.g. in a case of an array argument, it checks for the + proper shape and size). Use ``check()`` to disable checks + generated by F2PY. ``depend([<names>])`` This declares that the corresponding argument depends on the values - of variables in the list ``<names>``. For example, ``<init_expr>`` + of variables in the ``<names>`` list. For example, ``<init_expr>`` may use the values of other arguments. Using information given by ``depend(..)`` attributes, F2PY ensures that arguments are - initialized in a proper order. If ``depend(..)`` attribute is not + initialized in a proper order. If the ``depend(..)`` attribute is not used then F2PY determines dependence relations automatically. Use - ``depend()`` to disable dependence relations generated by F2PY. + ``depend()`` to disable the dependence relations generated by F2PY. When you edit dependence relations that were initially generated by F2PY, be careful not to break the dependence relations of other - relevant variables. Another thing to watch out is cyclic + relevant variables. Another thing to watch out for is cyclic dependencies. F2PY is able to detect cyclic dependencies when constructing wrappers and it complains if any are found. ``allocatable`` - The corresponding variable is Fortran 90 allocatable array defined - as Fortran 90 module data. + The corresponding variable is a Fortran 90 allocatable array defined as + Fortran 90 module data. .. _external: ``external`` The corresponding argument is a function provided by user. The - signature of this so-called call-back function can be defined + signature of this call-back function can be defined - in ``__user__`` module block, - or by demonstrative (or real, if the signature file is a real Fortran code) call in the ``<other statements>`` block. - For example, F2PY generates from + For example, F2PY generates from: - :: + .. code-block:: fortran external cb_sub, cb_fun integer n @@ -520,7 +554,9 @@ The following attributes are used by F2PY: call cb_sub(a,n) r = cb_fun(4) - the following call-back signatures:: + the following call-back signatures: + + .. code-block:: fortran subroutine cb_sub(a,n) real dimension(n) :: a @@ -531,7 +567,9 @@ The following attributes are used by F2PY: real :: r end function cb_fun - The corresponding user-provided Python function are then:: + The corresponding user-provided Python function are then: + + .. code-block:: python def cb_sub(a,[n]): ... @@ -540,49 +578,50 @@ The following attributes are used by F2PY: ... return r - See also ``intent(callback)`` attribute. + See also the ``intent(callback)`` attribute. ``parameter`` - The corresponding variable is a parameter and it must have a fixed - value. F2PY replaces all parameter occurrences by their - corresponding values. + This indicates that the corresponding variable is a parameter and it must have + a fixed value. F2PY replaces all parameter occurrences by their corresponding + values. Extensions ============ F2PY directives ------------------ +^^^^^^^^^^^^^^^^ -The so-called F2PY directives allow using F2PY signature file -constructs also in Fortran 77/90 source codes. With this feature you -can skip (almost) completely intermediate signature file generations -and apply F2PY directly to Fortran source codes. +The F2PY directives allow using F2PY signature file constructs in +Fortran 77/90 source codes. With this feature one can (almost) completely skip +the intermediate signature file generation and apply F2PY directly to Fortran +source codes. -F2PY directive has the following form:: +F2PY directives have the following form:: <comment char>f2py ... where allowed comment characters for fixed and free format Fortran codes are ``cC*!#`` and ``!``, respectively. Everything that follows ``<comment char>f2py`` is ignored by a compiler but read by F2PY as a -normal Fortran, non-comment line: +normal non-comment Fortran line: +.. note:: When F2PY finds a line with F2PY directive, the directive is first replaced by 5 spaces and then the line is reread. For fixed format Fortran codes, ``<comment char>`` must be at the first column of a file, of course. For free format Fortran codes, -F2PY directives can appear anywhere in a file. +the F2PY directives can appear anywhere in a file. C expressions --------------- +^^^^^^^^^^^^^^ C expressions are used in the following parts of signature files: -* ``<init_expr>`` of variable initialization; +* ``<init_expr>`` for variable initialization; * ``<C-booleanexpr>`` of the ``check`` attribute; -* ``<arrayspec> of the ``dimension`` attribute; -* ``callstatement`` statement, here also a C multiline block can be used. +* ``<arrayspec>`` of the ``dimension`` attribute; +* ``callstatement`` statement, here also a C multi-line block can be used. A C expression may contain: @@ -592,15 +631,19 @@ A C expression may contain: according to given dependence relations; * the following CPP macros: - ``rank(<name>)`` + * ``rank(<name>)`` Returns the rank of an array ``<name>``. - ``shape(<name>,<n>)`` + + * ``shape(<name>,<n>)`` Returns the ``<n>``-th dimension of an array ``<name>``. - ``len(<name>)`` + + * ``len(<name>)`` Returns the length of an array ``<name>``. - ``size(<name>)`` + + * ``size(<name>)`` Returns the size of an array ``<name>``. - ``slen(<name>)`` + + * ``slen(<name>)`` Returns the length of a string ``<name>``. For initializing an array ``<array name>``, F2PY generates a loop over @@ -615,7 +658,7 @@ from ``0`` to ``shape(<array name>,<i>)-1``. For example, a function ``myrange(n)`` generated from the following signature -:: +.. code-block:: subroutine myrange(a,n) fortranname ! myrange is a dummy wrapper @@ -630,23 +673,23 @@ is equivalent to ``numpy.arange(n,dtype=float)``. F2PY may lower cases also in C expressions when scanning Fortran codes (see ``--[no]-lower`` option). -Multiline blocks ------------------- +Multi-line blocks +^^^^^^^^^^^^^^^^^^ -A multiline block starts with ``'''`` (triple single-quotes) and ends -with ``'''`` in some *strictly* subsequent line. Multiline blocks can -be used only within .pyf files. The contents of a multiline block can +A multi-line block starts with ``'''`` (triple single-quotes) and ends +with ``'''`` in some *strictly* subsequent line. Multi-line blocks can +be used only within .pyf files. The contents of a multi-line block can be arbitrary (except that it cannot contain ``'''``) and no transformations (e.g. lowering cases) are applied to it. -Currently, multiline blocks can be used in the following constructs: +Currently, multi-line blocks can be used in the following constructs: -+ as a C expression of the ``callstatement`` statement; +* as a C expression of the ``callstatement`` statement; -+ as a C type specification of the ``callprotoargument`` statement; +* as a C type specification of the ``callprotoargument`` statement; -+ as a C code block of the ``usercode`` statement; +* as a C code block of the ``usercode`` statement; -+ as a list of C arrays of the ``pymethoddef`` statement; +* as a list of C arrays of the ``pymethoddef`` statement; -+ as documentation string. +* as a documentation string. diff --git a/doc/source/f2py/string_session.dat b/doc/source/f2py/string_session.dat deleted file mode 100644 index cbae6b784987..000000000000 --- a/doc/source/f2py/string_session.dat +++ /dev/null @@ -1,27 +0,0 @@ ->>> import mystring ->>> print mystring.foo.__doc__ -foo - Function signature: - foo(a,b,c,d) -Required arguments: - a : input string(len=5) - b : in/output rank-0 array(string(len=5),'c') - c : input string(len=-1) - d : in/output rank-0 array(string(len=-1),'c') - ->>> import numpy ->>> a=numpy.array('123') ->>> b=numpy.array('123') ->>> c=numpy.array('123') ->>> d=numpy.array('123') ->>> mystring.foo(a,b,c,d) - A=123 - B=123 - C=123 - D=123 - CHANGE A,B,C,D - A=A23 - B=B23 - C=C23 - D=D23 ->>> a.tostring(),b.tostring(),c.tostring(),d.tostring() -('123', 'B23', '123', 'D23') diff --git a/doc/source/f2py/usage.rst b/doc/source/f2py/usage.rst index a6f093154d95..596148799ba9 100644 --- a/doc/source/f2py/usage.rst +++ b/doc/source/f2py/usage.rst @@ -3,7 +3,17 @@ Using F2PY =========== F2PY can be used either as a command line tool ``f2py`` or as a Python -module ``f2py2e``. +module ``numpy.f2py``. While we try to provide the command line tool as part +of the numpy setup, some platforms like Windows make it difficult to +reliably put the executables on the ``PATH``. We will refer to ``f2py`` +in this document but you may have to run it as a module:: + + python -m numpy.f2py + +If you run ``f2py`` with no arguments, and the line ``numpy Version`` at the +end matches the NumPy version printed from ``python -m numpy.f2py``, then you +can use the shorter version. If not, or if you cannot run ``f2py``, you should +replace all calls to ``f2py`` here with the longer version. Command ``f2py`` ================= @@ -11,36 +21,44 @@ Command ``f2py`` When used as a command line tool, ``f2py`` has three major modes, distinguished by the usage of ``-c`` and ``-h`` switches: +Signature file generation +^^^^^^^^^^^^^^^^^^^^^^^^^^ + 1. To scan Fortran sources and generate a signature file, use - :: + .. code-block:: sh f2py -h <filename.pyf> <options> <fortran files> \ [[ only: <fortran functions> : ] \ [ skip: <fortran functions> : ]]... \ [<fortran files> ...] - Note that a Fortran source file can contain many routines, and not - necessarily all routines are needed to be used from Python. So, you - can either specify which routines should be wrapped (in ``only: .. :`` - part) or which routines F2PY should ignored (in ``skip: .. :`` part). + .. note:: + + A Fortran source file can contain many routines, and it is often + not necessary to allow all routines be usable from Python. In such cases, + either specify which routines should be wrapped (in the ``only: .. :`` part) + or which routines F2PY should ignored (in the ``skip: .. :`` part). If ``<filename.pyf>`` is specified as ``stdout`` then signatures - are send to standard output instead of a file. + are written to standard output instead of a file. - Among other options (see below), the following options can be used + Among other options (see below), the following can be used in this mode: ``--overwrite-signature`` - Overwrite existing signature file. + Overwrites an existing signature file. + +Extension module construction +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2. To construct an extension module, use - :: + .. code-block:: sh - f2py <options> <fortran files> \ - [[ only: <fortran functions> : ] \ - [ skip: <fortran functions> : ]]... \ + f2py -m <modulename> <options> <fortran files> \ + [[ only: <fortran functions> : ] \ + [ skip: <fortran functions> : ]]... \ [<fortran files> ...] The constructed extension module is saved as @@ -51,27 +69,27 @@ distinguished by the usage of ``-c`` and ``-h`` switches: in this mode: ``--debug-capi`` - Add debugging hooks to the extension module. When using this - extension module, various information about the wrapper is printed - to standard output, for example, the values of variables, the - steps taken, etc. + Adds debugging hooks to the extension module. When using this extension + module, various diagnostic information about the wrapper is written to + the standard output, for example, the values of variables, the steps taken, + etc. ``-include'<includefile>'`` Add a CPP ``#include`` statement to the extension module source. - ``<includefile>`` should be given in one of the following forms:: + ``<includefile>`` should be given in one of the following forms + + .. code-block:: cpp - "filename.ext" - <filename.ext> + "filename.ext" + <filename.ext> The include statement is inserted just before the wrapper functions. This feature enables using arbitrary C functions (defined in ``<includefile>``) in F2PY generated wrappers. - This option is deprecated. Use ``usercode`` statement to specify - C code snippets directly in signature files + .. note:: This option is deprecated. Use ``usercode`` statement to specify C code snippets directly in signature files. ``--[no-]wrap-functions`` - Create Fortran subroutine wrappers to Fortran functions. ``--wrap-functions`` is default because it ensures maximum portability and compiler independence. @@ -83,16 +101,19 @@ distinguished by the usage of ``-c`` and ``-h`` switches: List system resources found by ``numpy_distutils/system_info.py``. For example, try ``f2py --help-link lapack_opt``. +Building a module +^^^^^^^^^^^^^^^^^ + 3. To build an extension module, use - :: + .. code-block:: sh f2py -c <options> <fortran files> \ [[ only: <fortran functions> : ] \ [ skip: <fortran functions> : ]]... \ [ <fortran/c source files> ] [ <.o, .a, .so files> ] - If ``<fortran files>`` contains a signature file, then a source for + If ``<fortran files>`` contains a signature file, then the source for an extension module is constructed, all Fortran and C sources are compiled, and finally all object and library files are linked to the extension module ``<modulename>.so`` which is saved into the current @@ -100,26 +121,25 @@ distinguished by the usage of ``-c`` and ``-h`` switches: If ``<fortran files>`` does not contain a signature file, then an extension module is constructed by scanning all Fortran source codes - for routine signatures. + for routine signatures, before proceeding to build the extension module. - Among other options (see below) and options described in previous - mode, the following options can be used in this mode: + Among other options (see below) and options described for previous + modes, the following options can be used in this mode: ``--help-fcompiler`` - List available Fortran compilers. - ``--help-compiler`` [depreciated] - List available Fortran compilers. + List the available Fortran compilers. + ``--help-compiler`` **[depreciated]** + List the available Fortran compilers. ``--fcompiler=<Vendor>`` - Specify Fortran compiler type by vendor. + Specify a Fortran compiler type by vendor. ``--f77exec=<path>`` - Specify the path to F77 compiler - ``--fcompiler-exec=<path>`` [depreciated] - Specify the path to F77 compiler + Specify the path to a F77 compiler + ``--fcompiler-exec=<path>`` **[depreciated]** + Specify the path to a F77 compiler ``--f90exec=<path>`` - Specify the path to F90 compiler - ``--f90compiler-exec=<path>`` [depreciated] - Specify the path to F90 compiler - + Specify the path to a F90 compiler + ``--f90compiler-exec=<path>`` **[depreciated]** + Specify the path to a F90 compiler ``--f77flags=<string>`` Specify F77 compiler flags ``--f90flags=<string>`` @@ -129,12 +149,11 @@ distinguished by the usage of ``-c`` and ``-h`` switches: ``--arch=<string>`` Specify architecture specific optimization flags ``--noopt`` - Compile without optimization + Compile without optimization flags ``--noarch`` - Compile without arch-dependent optimization + Compile without arch-dependent optimization flags ``--debug`` Compile with debugging information - ``-l<libname>`` Use the library ``<libname>`` when linking. ``-D<macro>[=<defn=1>]`` @@ -147,17 +166,35 @@ distinguished by the usage of ``-c`` and ``-h`` switches: ``-L<dir>`` Add directory ``<dir>`` to the list of directories to be searched for ``-l``. - ``link-<resource>`` - - Link extension module with <resource> as defined by + Link the extension module with <resource> as defined by ``numpy_distutils/system_info.py``. E.g. to link with optimized LAPACK libraries (vecLib on MacOSX, ATLAS elsewhere), use ``--link-lapack_opt``. See also ``--help-link`` switch. - + + .. note:: The ``f2py -c`` option must be applied either to an existing ``.pyf`` file (plus the source/object/library files) or one must specify the ``-m <modulename>`` option (plus the sources/object/library files). Use one of the following options: + + .. code-block:: sh + + f2py -c -m fib1 fib1.f + + or + + .. code-block:: sh + + f2py -m fib1 fib1.f -h fib1.pyf + f2py -c fib1.pyf fib1.f + + For more information, see the `Building C and C++ Extensions`__ Python documentation for details. + + __ https://docs.python.org/3/extending/building.html + + When building an extension module, a combination of the following - macros may be required for non-gcc Fortran compilers:: - + macros may be required for non-gcc Fortran compilers: + + .. code-block:: sh + -DPREPEND_FORTRAN -DNO_APPEND_FORTRAN -DUPPERCASE_FORTRAN @@ -172,11 +209,13 @@ distinguished by the usage of ``-c`` and ``-h`` switches: of an array argument is larger than ``<int>``, a message about the coping is sent to ``stderr``. -Other options: +Other options +^^^^^^^^^^^^^ ``-m <modulename>`` - Name of an extension module. Default is ``untitled``. Don't use this option - if a signature file (\*.pyf) is used. + Name of an extension module. Default is ``untitled``. + + .. warning:: Don't use this option if a signature file (\*.pyf) is used. ``--[no-]lower`` Do [not] lower the cases in ``<fortran files>``. By default, ``--lower`` is assumed with ``-h`` switch, and ``--no-lower`` @@ -189,45 +228,20 @@ Other options: ``--verbose`` Run with extra verbosity. ``-v`` - Print f2py version ID and exit. + Print the F2PY version and exit. Execute ``f2py`` without any options to get an up-to-date list of available options. -Python module ``f2py2e`` -========================= +Python module ``numpy.f2py`` +============================ .. warning:: - The current Python interface to ``f2py2e`` module is not mature and - may change in future depending on users needs. - -The following functions are provided by the ``f2py2e`` module: - -``run_main(<list>)`` - Equivalent to running:: - - f2py <args> - - where ``<args>=string.join(<list>,' ')``, but in Python. Unless - ``-h`` is used, this function returns a dictionary containing - information on generated modules and their dependencies on source - files. For example, the command ``f2py -m scalar scalar.f`` can be - executed from Python as follows - - .. include:: run_main_session.dat - :literal: + The current Python interface to the ``f2py`` module is not mature and + may change in the future. - You cannot build extension modules with this function, that is, - using ``-c`` is not allowed. Use ``compile`` command instead, see - below. -``compile(source, modulename='untitled', extra_args='', verbose=1, source_fn=None)`` - Build extension module from Fortran 77 source string ``source``. - Return 0 if successful. - Note that this function actually calls ``f2py -c ..`` from shell to - ensure safety of the current Python process. - For example, +.. automodule:: numpy.f2py + :members: - .. include:: compile_session.dat - :literal: diff --git a/doc/source/getting_started.rst b/doc/source/getting_started.rst new file mode 100644 index 000000000000..cd7a66317ad8 --- /dev/null +++ b/doc/source/getting_started.rst @@ -0,0 +1,4 @@ +:orphan: + +Getting started +=============== \ No newline at end of file diff --git a/doc/source/glossary.rst b/doc/source/glossary.rst index b6ea42909794..aa2dc13dff1f 100644 --- a/doc/source/glossary.rst +++ b/doc/source/glossary.rst @@ -2,6 +2,521 @@ Glossary ******** -.. toctree:: +.. glossary:: + + + (`n`,) + A parenthesized number followed by a comma denotes a tuple with one + element. The trailing comma distinguishes a one-element tuple from a + parenthesized ``n``. + + + -1 + - **In a dimension entry**, instructs NumPy to choose the length + that will keep the total number of array elements the same. + + >>> np.arange(12).reshape(4, -1).shape + (4, 3) + + - **In an index**, any negative value + `denotes <https://docs.python.org/dev/faq/programming.html#what-s-a-negative-index>`_ + indexing from the right. + + . . . + An :py:data:`Ellipsis`. + + - **When indexing an array**, shorthand that the missing axes, if they + exist, are full slices. + + >>> a = np.arange(24).reshape(2,3,4) + + >>> a[...].shape + (2, 3, 4) + + >>> a[...,0].shape + (2, 3) + + >>> a[0,...].shape + (3, 4) + + >>> a[0,...,0].shape + (3,) + + It can be used at most once; ``a[...,0,...]`` raises an :exc:`IndexError`. + + - **In printouts**, NumPy substitutes ``...`` for the middle elements of + large arrays. To see the entire array, use `numpy.printoptions` + + + : + The Python :term:`python:slice` + operator. In ndarrays, slicing can be applied to every + axis: + + >>> a = np.arange(24).reshape(2,3,4) + >>> a + array([[[ 0, 1, 2, 3], + [ 4, 5, 6, 7], + [ 8, 9, 10, 11]], + <BLANKLINE> + [[12, 13, 14, 15], + [16, 17, 18, 19], + [20, 21, 22, 23]]]) + <BLANKLINE> + >>> a[1:,-2:,:-1] + array([[[16, 17, 18], + [20, 21, 22]]]) + + Trailing slices can be omitted: :: + + >>> a[1] == a[1,:,:] + array([[ True, True, True, True], + [ True, True, True, True], + [ True, True, True, True]]) + + In contrast to Python, where slicing creates a copy, in NumPy slicing + creates a :term:`view`. + + For details, see :ref:`combining-advanced-and-basic-indexing`. + + + < + In a dtype declaration, indicates that the data is + :term:`little-endian` (the bracket is big on the right). :: + + >>> dt = np.dtype('<f') # little-endian single-precision float + + + > + In a dtype declaration, indicates that the data is + :term:`big-endian` (the bracket is big on the left). :: + + >>> dt = np.dtype('>H') # big-endian unsigned short + + + advanced indexing + Rather than using a :doc:`scalar <reference/arrays.scalars>` or slice as + an index, an axis can be indexed with an array, providing fine-grained + selection. This is known as :ref:`advanced indexing<advanced-indexing>` + or "fancy indexing". + + + along an axis + An operation `along axis n` of array ``a`` behaves as if its argument + were an array of slices of ``a`` where each slice has a successive + index of axis `n`. + + For example, if ``a`` is a 3 x `N` array, an operation along axis 0 + behaves as if its argument were an array containing slices of each row: + + >>> np.array((a[0,:], a[1,:], a[2,:])) #doctest: +SKIP + + To make it concrete, we can pick the operation to be the array-reversal + function :func:`numpy.flip`, which accepts an ``axis`` argument. We + construct a 3 x 4 array ``a``: + + >>> a = np.arange(12).reshape(3,4) + >>> a + array([[ 0, 1, 2, 3], + [ 4, 5, 6, 7], + [ 8, 9, 10, 11]]) + + Reversing along axis 0 (the row axis) yields + + >>> np.flip(a,axis=0) + array([[ 8, 9, 10, 11], + [ 4, 5, 6, 7], + [ 0, 1, 2, 3]]) + + Recalling the definition of `along an axis`, ``flip`` along axis 0 is + treating its argument as if it were + + >>> np.array((a[0,:], a[1,:], a[2,:])) + array([[ 0, 1, 2, 3], + [ 4, 5, 6, 7], + [ 8, 9, 10, 11]]) + + and the result of ``np.flip(a,axis=0)`` is to reverse the slices: + + >>> np.array((a[2,:],a[1,:],a[0,:])) + array([[ 8, 9, 10, 11], + [ 4, 5, 6, 7], + [ 0, 1, 2, 3]]) + + + array + Used synonymously in the NumPy docs with :term:`ndarray`. + + + array_like + Any :doc:`scalar <reference/arrays.scalars>` or + :term:`python:sequence` + that can be interpreted as an ndarray. In addition to ndarrays + and scalars this category includes lists (possibly nested and with + different element types) and tuples. Any argument accepted by + :doc:`numpy.array <reference/generated/numpy.array>` + is array_like. :: + + >>> a = np.array([[1, 2.0], [0, 0], (1+1j, 3.)]) + + >>> a + array([[1.+0.j, 2.+0.j], + [0.+0.j, 0.+0.j], + [1.+1.j, 3.+0.j]]) + + + array scalar + An :doc:`array scalar <reference/arrays.scalars>` is an instance of the types/classes float32, float64, + etc.. For uniformity in handling operands, NumPy treats a scalar as + an array of zero dimension. In contrast, a 0-dimensional array is an :doc:`ndarray <reference/arrays.ndarray>` instance + containing precisely one value. + + + axis + Another term for an array dimension. Axes are numbered left to right; + axis 0 is the first element in the shape tuple. + + In a two-dimensional vector, the elements of axis 0 are rows and the + elements of axis 1 are columns. + + In higher dimensions, the picture changes. NumPy prints + higher-dimensional vectors as replications of row-by-column building + blocks, as in this three-dimensional vector: + + >>> a = np.arange(12).reshape(2,2,3) + >>> a + array([[[ 0, 1, 2], + [ 3, 4, 5]], + [[ 6, 7, 8], + [ 9, 10, 11]]]) + + ``a`` is depicted as a two-element array whose elements are 2x3 vectors. + From this point of view, rows and columns are the final two axes, + respectively, in any shape. + + This rule helps you anticipate how a vector will be printed, and + conversely how to find the index of any of the printed elements. For + instance, in the example, the last two values of 8's index must be 0 and + 2. Since 8 appears in the second of the two 2x3's, the first index must + be 1: + + >>> a[1,0,2] + 8 + + A convenient way to count dimensions in a printed vector is to + count ``[`` symbols after the open-parenthesis. This is + useful in distinguishing, say, a (1,2,3) shape from a (2,3) shape: + + >>> a = np.arange(6).reshape(2,3) + >>> a.ndim + 2 + >>> a + array([[0, 1, 2], + [3, 4, 5]]) + + >>> a = np.arange(6).reshape(1,2,3) + >>> a.ndim + 3 + >>> a + array([[[0, 1, 2], + [3, 4, 5]]]) + + + .base + + If an array does not own its memory, then its + :doc:`base <reference/generated/numpy.ndarray.base>` attribute returns + the object whose memory the array is referencing. That object may be + referencing the memory from still another object, so the owning object + may be ``a.base.base.base...``. Some writers erroneously claim that + testing ``base`` determines if arrays are :term:`view`\ s. For the + correct way, see :func:`numpy.shares_memory`. + + + big-endian + See `Endianness <https://en.wikipedia.org/wiki/Endianness>`_. + + + BLAS + `Basic Linear Algebra Subprograms <https://en.wikipedia.org/wiki/Basic_Linear_Algebra_Subprograms>`_ + + + broadcast + *broadcasting* is NumPy's ability to process ndarrays of + different sizes as if all were the same size. + + It permits an elegant do-what-I-mean behavior where, for instance, + adding a scalar to a vector adds the scalar value to every element. + + >>> a = np.arange(3) + >>> a + array([0, 1, 2]) + + >>> a + [3, 3, 3] + array([3, 4, 5]) + + >>> a + 3 + array([3, 4, 5]) + + Ordinarly, vector operands must all be the same size, because NumPy + works element by element -- for instance, ``c = a * b`` is :: + + c[0,0,0] = a[0,0,0] * b[0,0,0] + c[0,0,1] = a[0,0,1] * b[0,0,1] + ... + + But in certain useful cases, NumPy can duplicate data along "missing" + axes or "too-short" dimensions so shapes will match. The duplication + costs no memory or time. For details, see + :doc:`Broadcasting. <user/basics.broadcasting>` + + + C order + Same as :term:`row-major`. + + + column-major + See `Row- and column-major order <https://en.wikipedia.org/wiki/Row-_and_column-major_order>`_. + + + contiguous + An array is contiguous if + * it occupies an unbroken block of memory, and + * array elements with higher indexes occupy higher addresses (that + is, no :term:`stride` is negative). + + + copy + See :term:`view`. + + + dimension + See :term:`axis`. + + + dtype + The datatype describing the (identically typed) elements in an ndarray. + It can be changed to reinterpret the array contents. For details, see + :doc:`Data type objects (dtype). <reference/arrays.dtypes>` + + + fancy indexing + Another term for :term:`advanced indexing`. + + + field + In a :term:`structured data type`, each subtype is called a `field`. + The `field` has a name (a string), a type (any valid dtype), and + an optional `title`. See :ref:`arrays.dtypes`. + + + Fortran order + Same as :term:`column-major`. + + + flattened + See :term:`ravel`. + + + homogeneous + All elements of a homogeneous array have the same type. ndarrays, in + contrast to Python lists, are homogeneous. The type can be complicated, + as in a :term:`structured array`, but all elements have that type. + + NumPy `object arrays <#term-object-array>`_, which contain references to + Python objects, fill the role of heterogeneous arrays. + + + itemsize + The size of the dtype element in bytes. + + + little-endian + See `Endianness <https://en.wikipedia.org/wiki/Endianness>`_. + + + mask + A boolean array used to select only certain elements for an operation: + + >>> x = np.arange(5) + >>> x + array([0, 1, 2, 3, 4]) + + >>> mask = (x > 2) + >>> mask + array([False, False, False, True, True]) + + >>> x[mask] = -1 + >>> x + array([ 0, 1, 2, -1, -1]) + + + masked array + Bad or missing data can be cleanly ignored by putting it in a masked + array, which has an internal boolean array indicating invalid + entries. Operations with masked arrays ignore these entries. :: + + >>> a = np.ma.masked_array([np.nan, 2, np.nan], [True, False, True]) + >>> a + masked_array(data=[--, 2.0, --], + mask=[ True, False, True], + fill_value=1e+20) + + >>> a + [1, 2, 3] + masked_array(data=[--, 4.0, --], + mask=[ True, False, True], + fill_value=1e+20) + + For details, see :doc:`Masked arrays. <reference/maskedarray>` + + + matrix + NumPy's two-dimensional + :doc:`matrix class <reference/generated/numpy.matrix>` + should no longer be used; use regular ndarrays. + + + ndarray + :doc:`NumPy's basic structure <reference/arrays>`. + + + object array + An array whose dtype is ``object``; that is, it contains references to + Python objects. Indexing the array dereferences the Python objects, so + unlike other ndarrays, an object array has the ability to hold + heterogeneous objects. + + + ravel + :doc:`numpy.ravel \ + <reference/generated/numpy.ravel>` + and :doc:`numpy.flatten \ + <reference/generated/numpy.ndarray.flatten>` + both flatten an ndarray. ``ravel`` will return a view if possible; + ``flatten`` always returns a copy. + + Flattening collapses a multimdimensional array to a single dimension; + details of how this is done (for instance, whether ``a[n+1]`` should be + the next row or next column) are parameters. + + + record array + A :term:`structured array` with allowing access in an attribute style + (``a.field``) in addition to ``a['field']``. For details, see + :doc:`numpy.recarray. <reference/generated/numpy.recarray>` + + + row-major + See `Row- and column-major order <https://en.wikipedia.org/wiki/Row-_and_column-major_order>`_. + NumPy creates arrays in row-major order by default. + + + scalar + In NumPy, usually a synonym for :term:`array scalar`. + + + shape + A tuple showing the length of each dimension of an ndarray. The + length of the tuple itself is the number of dimensions + (:doc:`numpy.ndim <reference/generated/numpy.ndarray.ndim>`). + The product of the tuple elements is the number of elements in the + array. For details, see + :doc:`numpy.ndarray.shape <reference/generated/numpy.ndarray.shape>`. + + + stride + Physical memory is one-dimensional; strides provide a mechanism to map + a given index to an address in memory. For an N-dimensional array, its + ``strides`` attribute is an N-element tuple; advancing from index + ``i`` to index ``i+1`` on axis ``n`` means adding ``a.strides[n]`` bytes + to the address. + + Strides are computed automatically from an array's dtype and + shape, but can be directly specified using + :doc:`as_strided. <reference/generated/numpy.lib.stride_tricks.as_strided>` + + For details, see + :doc:`numpy.ndarray.strides <reference/generated/numpy.ndarray.strides>`. + + To see how striding underlies the power of NumPy views, see + `The NumPy array: a structure for efficient numerical computation. \ + <https://arxiv.org/pdf/1102.1523.pdf>`_ + + + structured array + Array whose :term:`dtype` is a :term:`structured data type`. + + + structured data type + Users can create arbitrarily complex :term:`dtypes <dtype>` + that can include other arrays and dtypes. These composite dtypes are called + :doc:`structured data types. <user/basics.rec>` + + + subarray + An array nested in a :term:`structured data type`, as ``b`` is here: + + >>> dt = np.dtype([('a', np.int32), ('b', np.float32, (3,))]) + >>> np.zeros(3, dtype=dt) + array([(0, [0., 0., 0.]), (0, [0., 0., 0.]), (0, [0., 0., 0.])], + dtype=[('a', '<i4'), ('b', '<f4', (3,))]) + + + subarray data type + An element of a structured datatype that behaves like an ndarray. + + + title + An alias for a field name in a structured datatype. + + + type + In NumPy, usually a synonym for :term:`dtype`. For the more general + Python meaning, :term:`see here. <python:type>` + + + ufunc + NumPy's fast element-by-element computation (:term:`vectorization`) + gives a choice which function gets applied. The general term for the + function is ``ufunc``, short for ``universal function``. NumPy routines + have built-in ufuncs, but users can also + :doc:`write their own. <reference/ufuncs>` + + + vectorization + NumPy hands off array processing to C, where looping and computation are + much faster than in Python. To exploit this, programmers using NumPy + eliminate Python loops in favor of array-to-array operations. + :term:`vectorization` can refer both to the C offloading and to + structuring NumPy code to leverage it. + + view + Without touching underlying data, NumPy can make one array appear + to change its datatype and shape. + + An array created this way is a `view`, and NumPy often exploits the + performance gain of using a view versus making a new array. + + A potential drawback is that writing to a view can alter the original + as well. If this is a problem, NumPy instead needs to create a + physically distinct array -- a `copy`. + + Some NumPy routines always return views, some always return copies, some + may return one or the other, and for some the choice can be specified. + Responsibility for managing views and copies falls to the programmer. + :func:`numpy.shares_memory` will check whether ``b`` is a view of + ``a``, but an exact answer isn't always feasible, as the documentation + page explains. + + >>> x = np.arange(5) + >>> x + array([0, 1, 2, 3, 4]) + + >>> y = x[::2] + >>> y + array([0, 2, 4]) + + >>> x[0] = 3 # changing x changes y as well, since y is a view on x + >>> y + array([3, 2, 4]) -.. automodule:: numpy.doc.glossary diff --git a/doc/source/index.rst b/doc/source/index.rst new file mode 100644 index 000000000000..7fa7d8aa9a0a --- /dev/null +++ b/doc/source/index.rst @@ -0,0 +1,101 @@ +.. _numpy_docs_mainpage: + +################### +NumPy documentation +################### + +.. toctree:: + :maxdepth: 1 + :hidden: + + User Guide <user/index> + API reference <reference/index> + Development <dev/index> + + +**Version**: |version| + +**Download documentation**: +`PDF Version <https://numpy.org/doc/stable/numpy-user.pdf>`_ | +`Historical versions of documentation <https://numpy.org/doc/>`_ + +**Useful links**: +`Installation <https://numpy.org/install/>`_ | +`Source Repository <https://github.com/numpy/numpy>`_ | +`Issue Tracker <https://github.com/numpy/numpy/issues>`_ | +`Q&A Support <https://numpy.org/gethelp/>`_ | +`Mailing List <https://mail.python.org/mailman/listinfo/numpy-discussion>`_ + +NumPy is the fundamental package for scientific computing in Python. It is a +Python library that provides a multidimensional array object, various derived +objects (such as masked arrays and matrices), and an assortment of routines for +fast operations on arrays, including mathematical, logical, shape manipulation, +sorting, selecting, I/O, discrete Fourier transforms, basic linear algebra, +basic statistical operations, random simulation and much more. + +.. panels:: + :card: + intro-card text-center + :column: col-lg-6 col-md-6 col-sm-6 col-xs-12 p-2 + + --- + :img-top: ../source/_static/index-images/getting_started.svg + + Getting Started + ^^^^^^^^^^^^^^^ + + New to NumPy? Check out the Absolute Beginner's Guide. It contains an + introduction to NumPy's main concepts and links to additional tutorials. + + .. link-button:: user/absolute_beginners + :type: ref + :text: + :classes: stretched-link + + --- + :img-top: ../source/_static/index-images/user_guide.svg + + User Guide + ^^^^^^^^^^ + + The user guide provides in-depth information on the + key concepts of pandas with useful background information and explanation. + + .. link-button:: user + :type: ref + :text: + :classes: stretched-link + + --- + :img-top: ../source/_static/index-images/api.svg + + API Reference + ^^^^^^^^^^^^^ + + The reference guide contains a detailed description of the functions, + modules, and objects included in NumPy. The reference describes how the + methods work and which parameters can be used. It assumes that you have an + understanding of the key concepts. + + .. link-button:: reference + :type: ref + :text: + :classes: stretched-link + + --- + :img-top: ../source/_static/index-images/contributor.svg + + Contributor's Guide + ^^^^^^^^^^^^^^^ + + Want to add to the codebase? Can help add translation or a flowchart to the + documentation? The contributing guidelines will guide you through the + process of improving NumPy. + + .. link-button:: devindex + :type: ref + :text: + :classes: stretched-link + +.. This is not really the index page, that is found in + _templates/indexcontent.html The toctree content here will be added to the + top of the template header diff --git a/doc/source/license.rst b/doc/source/license.rst index 8f360af8830e..beea023ce05a 100644 --- a/doc/source/license.rst +++ b/doc/source/license.rst @@ -1,35 +1,6 @@ ************* -NumPy License +NumPy license ************* -Copyright (c) 2005, NumPy Developers - -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - -* Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - -* Neither the name of the NumPy Developers nor the names of any - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.. include:: ../../LICENSE.txt + :literal: diff --git a/doc/source/reference/alignment.rst b/doc/source/reference/alignment.rst new file mode 100644 index 000000000000..70ded916a76d --- /dev/null +++ b/doc/source/reference/alignment.rst @@ -0,0 +1,13 @@ +:orphan: + +**************** +Memory Alignment +**************** + +.. This document has been moved to ../dev/alignment.rst. + +This document has been moved to :ref:`alignment`. + + + + diff --git a/doc/source/reference/arrays.classes.rst b/doc/source/reference/arrays.classes.rst index f17cb932ae4b..92c271f6b964 100644 --- a/doc/source/reference/arrays.classes.rst +++ b/doc/source/reference/arrays.classes.rst @@ -6,8 +6,19 @@ Standard array subclasses .. currentmodule:: numpy -The :class:`ndarray` in NumPy is a "new-style" Python -built-in-type. Therefore, it can be inherited from (in Python or in C) +.. for doctests + >>> import numpy as np + >>> np.random.seed(1) + +.. note:: + + Subclassing a ``numpy.ndarray`` is possible but if your goal is to create + an array with *modified* behavior, as do dask arrays for distributed + computation and cupy arrays for GPU-based computation, subclassing is + discouraged. Instead, using numpy's + :ref:`dispatch mechanism <basics.dispatch>` is recommended. + +The :class:`ndarray` can be inherited from (in Python or in C) if desired. Therefore, it can form a foundation for many useful classes. Often whether to sub-class the array object or to simply use the core array component as an internal part of a new class is a @@ -43,12 +54,8 @@ NumPy provides several hooks that classes can customize: .. versionadded:: 1.13 - .. note:: The API is `provisional - <https://docs.python.org/3/glossary.html#term-provisional-api>`_, - i.e., we do not yet guarantee backward compatibility. - Any class, ndarray subclass or not, can define this method or set it to - :obj:`None` in order to override the behavior of NumPy's ufuncs. This works + None in order to override the behavior of NumPy's ufuncs. This works quite similarly to Python's ``__mul__`` and other binary operation routines. - *ufunc* is the ufunc object that was called. @@ -79,7 +86,7 @@ NumPy provides several hooks that classes can customize: :func:`~numpy.matmul`, which currently is not a Ufunc, but could be relatively easily be rewritten as a (set of) generalized Ufuncs. The same may happen with functions such as :func:`~numpy.median`, - :func:`~numpy.min`, and :func:`~numpy.argsort`. + :func:`~numpy.amin`, and :func:`~numpy.argsort`. Like with some other special methods in python, such as ``__hash__`` and ``__iter__``, it is possible to indicate that your class does *not* @@ -91,13 +98,13 @@ NumPy provides several hooks that classes can customize: :class:`ndarray` handles binary operations like ``arr + obj`` and ``arr < obj`` when ``arr`` is an :class:`ndarray` and ``obj`` is an instance of a custom class. There are two possibilities. If - ``obj.__array_ufunc__`` is present and not :obj:`None`, then + ``obj.__array_ufunc__`` is present and not None, then ``ndarray.__add__`` and friends will delegate to the ufunc machinery, meaning that ``arr + obj`` becomes ``np.add(arr, obj)``, and then :func:`~numpy.add` invokes ``obj.__array_ufunc__``. This is useful if you want to define an object that acts like an array. - Alternatively, if ``obj.__array_ufunc__`` is set to :obj:`None`, then as a + Alternatively, if ``obj.__array_ufunc__`` is set to None, then as a special case, special methods like ``ndarray.__add__`` will notice this and *unconditionally* raise :exc:`TypeError`. This is useful if you want to create objects that interact with arrays via binary operations, but @@ -132,7 +139,7 @@ NumPy provides several hooks that classes can customize: place rather than separately by the ufunc machinery and by the binary operation rules (which gives preference to special methods of subclasses; the alternative way to enforce a one-place only hierarchy, - of setting :func:`__array_ufunc__` to :obj:`None`, would seem very + of setting :func:`__array_ufunc__` to None, would seem very unexpected and thus confusing, as then the subclass would not work at all with ufuncs). - :class:`ndarray` defines its own :func:`__array_ufunc__`, which, @@ -151,6 +158,121 @@ NumPy provides several hooks that classes can customize: :func:`__array_prepare__`, :data:`__array_priority__` mechanism described below for ufuncs (which may eventually be deprecated). +.. py:method:: class.__array_function__(func, types, args, kwargs) + + .. versionadded:: 1.16 + + .. note:: + + - In NumPy 1.17, the protocol is enabled by default, but can be disabled + with ``NUMPY_EXPERIMENTAL_ARRAY_FUNCTION=0``. + - In NumPy 1.16, you need to set the environment variable + ``NUMPY_EXPERIMENTAL_ARRAY_FUNCTION=1`` before importing NumPy to use + NumPy function overrides. + - Eventually, expect to ``__array_function__`` to always be enabled. + + - ``func`` is an arbitrary callable exposed by NumPy's public API, + which was called in the form ``func(*args, **kwargs)``. + - ``types`` is a collection :py:class:`collections.abc.Collection` + of unique argument types from the original NumPy function call that + implement ``__array_function__``. + - The tuple ``args`` and dict ``kwargs`` are directly passed on from the + original call. + + As a convenience for ``__array_function__`` implementors, ``types`` + provides all argument types with an ``'__array_function__'`` attribute. + This allows implementors to quickly identify cases where they should defer + to ``__array_function__`` implementations on other arguments. + Implementations should not rely on the iteration order of ``types``. + + Most implementations of ``__array_function__`` will start with two + checks: + + 1. Is the given function something that we know how to overload? + 2. Are all arguments of a type that we know how to handle? + + If these conditions hold, ``__array_function__`` should return the result + from calling its implementation for ``func(*args, **kwargs)``. Otherwise, + it should return the sentinel value ``NotImplemented``, indicating that the + function is not implemented by these types. + + There are no general requirements on the return value from + ``__array_function__``, although most sensible implementations should + probably return array(s) with the same type as one of the function's + arguments. + + It may also be convenient to define a custom decorators (``implements`` + below) for registering ``__array_function__`` implementations. + + .. code:: python + + HANDLED_FUNCTIONS = {} + + class MyArray: + def __array_function__(self, func, types, args, kwargs): + if func not in HANDLED_FUNCTIONS: + return NotImplemented + # Note: this allows subclasses that don't override + # __array_function__ to handle MyArray objects + if not all(issubclass(t, MyArray) for t in types): + return NotImplemented + return HANDLED_FUNCTIONS[func](*args, **kwargs) + + def implements(numpy_function): + """Register an __array_function__ implementation for MyArray objects.""" + def decorator(func): + HANDLED_FUNCTIONS[numpy_function] = func + return func + return decorator + + @implements(np.concatenate) + def concatenate(arrays, axis=0, out=None): + ... # implementation of concatenate for MyArray objects + + @implements(np.broadcast_to) + def broadcast_to(array, shape): + ... # implementation of broadcast_to for MyArray objects + + Note that it is not required for ``__array_function__`` implementations to + include *all* of the corresponding NumPy function's optional arguments + (e.g., ``broadcast_to`` above omits the irrelevant ``subok`` argument). + Optional arguments are only passed in to ``__array_function__`` if they + were explicitly used in the NumPy function call. + + Just like the case for builtin special methods like ``__add__``, properly + written ``__array_function__`` methods should always return + ``NotImplemented`` when an unknown type is encountered. Otherwise, it will + be impossible to correctly override NumPy functions from another object + if the operation also includes one of your objects. + + For the most part, the rules for dispatch with ``__array_function__`` + match those for ``__array_ufunc__``. In particular: + + - NumPy will gather implementations of ``__array_function__`` from all + specified inputs and call them in order: subclasses before + superclasses, and otherwise left to right. Note that in some edge cases + involving subclasses, this differs slightly from the + `current behavior <https://bugs.python.org/issue30140>`_ of Python. + - Implementations of ``__array_function__`` indicate that they can + handle the operation by returning any value other than + ``NotImplemented``. + - If all ``__array_function__`` methods return ``NotImplemented``, + NumPy will raise ``TypeError``. + + If no ``__array_function__`` methods exists, NumPy will default to calling + its own implementation, intended for use on NumPy arrays. This case arises, + for example, when all array-like arguments are Python numbers or lists. + (NumPy arrays do have a ``__array_function__`` method, given below, but it + always returns ``NotImplemented`` if any argument other than a NumPy array + subclass implements ``__array_function__``.) + + One deviation from the current behavior of ``__array_ufunc__`` is that + NumPy will only call ``__array_function__`` on the *first* argument of each + unique type. This matches Python's `rule for calling reflected methods + <https://docs.python.org/3/reference/datamodel.html#object.__ror__>`_, and + this ensures that checking overloads has acceptable performance even when + there are a large number of overloaded arguments. + .. py:method:: class.__array_finalize__(obj) This method is called whenever the system internally allocates a @@ -162,7 +284,7 @@ NumPy provides several hooks that classes can customize: .. py:method:: class.__array_prepare__(array, context=None) - At the beginning of every :ref:`ufunc <ufuncs.output-type>`, this + At the beginning of every :ref:`ufunc <ufuncs-output-type>`, this method is called on the input object with the highest array priority, or the output object if one was specified. The output array is passed in and whatever is returned is passed to the ufunc. @@ -177,7 +299,7 @@ NumPy provides several hooks that classes can customize: .. py:method:: class.__array_wrap__(array, context=None) - At the end of every :ref:`ufunc <ufuncs.output-type>`, this method + At the end of every :ref:`ufunc <ufuncs-output-type>`, this method is called on the input object with the highest array priority, or the output object if one was specified. The ufunc-computed array is passed in and whatever is returned is passed to the user. @@ -204,10 +326,11 @@ NumPy provides several hooks that classes can customize: If a class (ndarray subclass or not) having the :func:`__array__` method is used as the output object of an :ref:`ufunc - <ufuncs.output-type>`, results will be written to the object - returned by :func:`__array__`. Similar conversion is done on - input arrays. + <ufuncs-output-type>`, results will *not* be written to the object + returned by :func:`__array__`. This practice will return ``TypeError``. + +.. _matrix-objects: Matrix objects ============== @@ -286,23 +409,25 @@ alias for "matrix "in NumPy. Example 1: Matrix creation from a string ->>> a=mat('1 2 3; 4 5 3') ->>> print (a*a.T).I -[[ 0.2924 -0.1345] - [-0.1345 0.0819]] +>>> a = np.mat('1 2 3; 4 5 3') +>>> print((a*a.T).I) + [[ 0.29239766 -0.13450292] + [-0.13450292 0.08187135]] + Example 2: Matrix creation from nested sequence ->>> mat([[1,5,10],[1.0,3,4j]]) +>>> np.mat([[1,5,10],[1.0,3,4j]]) matrix([[ 1.+0.j, 5.+0.j, 10.+0.j], [ 1.+0.j, 3.+0.j, 0.+4.j]]) Example 3: Matrix creation from an array ->>> mat(random.rand(3,3)).T -matrix([[ 0.7699, 0.7922, 0.3294], - [ 0.2792, 0.0101, 0.9219], - [ 0.3398, 0.7571, 0.8197]]) +>>> np.mat(np.random.rand(3,3)).T +matrix([[4.17022005e-01, 3.02332573e-01, 1.86260211e-01], + [7.20324493e-01, 1.46755891e-01, 3.45560727e-01], + [1.14374817e-04, 9.23385948e-02, 3.96767474e-01]]) + Memory-mapped file arrays ========================= @@ -333,15 +458,15 @@ array actually get written to disk. Example: ->>> a = memmap('newfile.dat', dtype=float, mode='w+', shape=1000) +>>> a = np.memmap('newfile.dat', dtype=float, mode='w+', shape=1000) >>> a[10] = 10.0 >>> a[30] = 30.0 >>> del a ->>> b = fromfile('newfile.dat', dtype=float) ->>> print b[10], b[30] +>>> b = np.fromfile('newfile.dat', dtype=float) +>>> print(b[10], b[30]) 10.0 30.0 ->>> a = memmap('newfile.dat', dtype=float) ->>> print a[10], a[30] +>>> a = np.memmap('newfile.dat', dtype=float) +>>> print(a[10], a[30]) 10.0 30.0 @@ -357,16 +482,16 @@ Character arrays (:mod:`numpy.char`) The `chararray` class exists for backwards compatibility with Numarray, it is not recommended for new development. Starting from numpy 1.4, if one needs arrays of strings, it is recommended to use arrays of - `dtype` `object_`, `string_` or `unicode_`, and use the free functions + `dtype` `object_`, `bytes_` or `str_`, and use the free functions in the `numpy.char` module for fast vectorized string operations. -These are enhanced arrays of either :class:`string_` type or -:class:`unicode_` type. These arrays inherit from the +These are enhanced arrays of either :class:`str_` type or +:class:`bytes_` type. These arrays inherit from the :class:`ndarray`, but specially-define the operations ``+``, ``*``, and ``%`` on a (broadcasting) element-by-element basis. These operations are not available on the standard :class:`ndarray` of character type. In addition, the :class:`chararray` has all of the -standard :class:`string <str>` (and :class:`unicode`) methods, +standard :class:`str` (and :class:`bytes`) methods, executing them on an element-by-element basis. Perhaps the easiest way to create a chararray is to use :meth:`self.view(chararray) <ndarray.view>` where *self* is an ndarray of str or unicode @@ -452,7 +577,7 @@ object, then the Python code:: some code involving val ... -calls ``val = myiter.next()`` repeatedly until :exc:`StopIteration` is +calls ``val = next(myiter)`` repeatedly until :exc:`StopIteration` is raised by the iterator. There are several ways to iterate over an array that may be useful: default iteration, flat iteration, and :math:`N`-dimensional enumeration. @@ -472,9 +597,9 @@ This default iterator selects a sub-array of dimension :math:`N-1` from the array. This can be a useful construct for defining recursive algorithms. To loop over the entire array requires :math:`N` for-loops. ->>> a = arange(24).reshape(3,2,4)+10 +>>> a = np.arange(24).reshape(3,2,4)+10 >>> for val in a: -... print 'item:', val +... print('item:', val) item: [[10 11 12 13] [14 15 16 17]] item: [[18 19 20 21] @@ -496,7 +621,7 @@ an iterator that will cycle over the entire array in C-style contiguous order. >>> for i, val in enumerate(a.flat): -... if i%5 == 0: print i, val +... if i%5 == 0: print(i, val) 0 10 5 15 10 20 @@ -518,8 +643,8 @@ N-dimensional enumeration Sometimes it may be useful to get the N-dimensional index while iterating. The ndenumerate iterator can achieve this. ->>> for i, val in ndenumerate(a): -... if sum(i)%5 == 0: print i, val +>>> for i, val in np.ndenumerate(a): +... if sum(i)%5 == 0: print(i, val) (0, 0, 0) 10 (1, 1, 3) 25 (2, 0, 3) 29 @@ -540,8 +665,8 @@ objects as inputs and returns an iterator that returns tuples providing each of the input sequence elements in the broadcasted result. ->>> for val in broadcast([[1,0],[2,3]],[0,1]): -... print val +>>> for val in np.broadcast([[1,0],[2,3]],[0,1]): +... print(val) (1, 0) (0, 1) (2, 0) diff --git a/doc/source/reference/arrays.datetime.rst b/doc/source/reference/arrays.datetime.rst index e64d0c17e53a..63c93821b6b4 100644 --- a/doc/source/reference/arrays.datetime.rst +++ b/doc/source/reference/arrays.datetime.rst @@ -13,20 +13,21 @@ support datetime functionality. The data type is called "datetime64", so named because "datetime" is already taken by the datetime library included in Python. -.. note:: The datetime API is *experimental* in 1.7.0, and may undergo changes - in future versions of NumPy. Basic Datetimes =============== -The most basic way to create datetimes is from strings in -ISO 8601 date or datetime format. The unit for internal storage -is automatically selected from the form of the string, and can -be either a :ref:`date unit <arrays.dtypes.dateunits>` or a +The most basic way to create datetimes is from strings in ISO 8601 date +or datetime format. It is also possible to create datetimes from an integer by +offset relative to the Unix epoch (00:00:00 UTC on 1 January 1970). +The unit for internal storage is automatically selected from the +form of the string, and can be either a :ref:`date unit <arrays.dtypes.dateunits>` or a :ref:`time unit <arrays.dtypes.timeunits>`. The date units are years ('Y'), months ('M'), weeks ('W'), and days ('D'), while the time units are hours ('h'), minutes ('m'), seconds ('s'), milliseconds ('ms'), and -some additional SI-prefix seconds-based units. +some additional SI-prefix seconds-based units. The datetime64 data type +also accepts the string "NAT", in any combination of lowercase/uppercase +letters, for a "Not A Time" value. .. admonition:: Example @@ -34,6 +35,11 @@ some additional SI-prefix seconds-based units. >>> np.datetime64('2005-02-25') numpy.datetime64('2005-02-25') + + From an integer and a date unit, 1 year since the UNIX epoch: + + >>> np.datetime64(1, 'Y') + numpy.datetime64('1971') Using months for the unit: @@ -50,6 +56,11 @@ some additional SI-prefix seconds-based units. >>> np.datetime64('2005-02-25T03:30') numpy.datetime64('2005-02-25T03:30') + NAT (not a time): + + >>> np.datetime64('nat') + numpy.datetime64('NaT') + When creating an array of datetimes from a string, it is still possible to automatically select the unit from the inputs, by using the datetime type with generic units. @@ -60,8 +71,21 @@ datetime type with generic units. array(['2007-07-13', '2006-01-13', '2010-08-13'], dtype='datetime64[D]') >>> np.array(['2001-01-01T12:00', '2002-02-03T13:56:03.172'], dtype='datetime64') - array(['2001-01-01T12:00:00.000-0600', '2002-02-03T13:56:03.172-0600'], dtype='datetime64[ms]') + array(['2001-01-01T12:00:00.000', '2002-02-03T13:56:03.172'], + dtype='datetime64[ms]') + +An array of datetimes can be constructed from integers representing +POSIX timestamps with the given unit. + +.. admonition:: Example + + >>> np.array([0, 1577836800], dtype='datetime64[s]') + array(['1970-01-01T00:00:00', '2020-01-01T00:00:00'], + dtype='datetime64[s]') + >>> np.array([0, 1577836800000]).astype('datetime64[ms]') + array(['1970-01-01T00:00:00.000', '2020-01-01T00:00:00.000'], + dtype='datetime64[ms]') The datetime type works with many common NumPy functions, for example :func:`arange` can be used to generate ranges of dates. @@ -78,7 +102,7 @@ example :func:`arange` can be used to generate ranges of dates. '2005-02-17', '2005-02-18', '2005-02-19', '2005-02-20', '2005-02-21', '2005-02-22', '2005-02-23', '2005-02-24', '2005-02-25', '2005-02-26', '2005-02-27', '2005-02-28'], - dtype='datetime64[D]') + dtype='datetime64[D]') The datetime object represents a single moment in time. If two datetimes have different units, they may still be representing @@ -91,16 +115,38 @@ because the moment of time is still being represented exactly. >>> np.datetime64('2005') == np.datetime64('2005-01-01') True - >>> np.datetime64('2010-03-14T15Z') == np.datetime64('2010-03-14T15:00:00.00Z') + >>> np.datetime64('2010-03-14T15') == np.datetime64('2010-03-14T15:00:00.00') True +.. deprecated:: 1.11.0 + + NumPy does not store timezone information. For backwards compatibility, datetime64 + still parses timezone offsets, which it handles by converting to + UTC. This behaviour is deprecated and will raise an error in the + future. + + Datetime and Timedelta Arithmetic ================================= NumPy allows the subtraction of two Datetime values, an operation which produces a number with a time unit. Because NumPy doesn't have a physical quantities system in its core, the timedelta64 data type was created -to complement datetime64. +to complement datetime64. The arguments for timedelta64 are a number, +to represent the number of units, and a date/time unit, such as +(D)ay, (M)onth, (Y)ear, (h)ours, (m)inutes, or (s)econds. The timedelta64 +data type also accepts the string "NAT" in place of the number for a "Not A Time" value. + +.. admonition:: Example + + >>> np.timedelta64(1, 'D') + numpy.timedelta64(1,'D') + + >>> np.timedelta64(4, 'h') + numpy.timedelta64(4,'h') + + >>> np.timedelta64('nAt') + numpy.timedelta64('NaT') Datetimes and Timedeltas work together to provide ways for simple datetime calculations. @@ -114,11 +160,20 @@ simple datetime calculations. numpy.datetime64('2009-01-21') >>> np.datetime64('2011-06-15T00:00') + np.timedelta64(12, 'h') - numpy.datetime64('2011-06-15T12:00-0500') + numpy.datetime64('2011-06-15T12:00') >>> np.timedelta64(1,'W') / np.timedelta64(1,'D') 7.0 + >>> np.timedelta64(1,'W') % np.timedelta64(10,'D') + numpy.timedelta64(7,'D') + + >>> np.datetime64('nat') - np.datetime64('2009-01-01') + numpy.timedelta64('NaT','D') + + >>> np.datetime64('2009-01-01') + np.timedelta64('nat') + numpy.datetime64('NaT') + There are two Timedelta units ('Y', years and 'M', months) which are treated specially, because how much time they represent changes depending on when they are used. While a timedelta day unit is equivalent to @@ -179,7 +234,7 @@ And here are the time units: m minute +/- 1.7e13 years [1.7e13 BC, 1.7e13 AD] s second +/- 2.9e11 years [2.9e11 BC, 2.9e11 AD] ms millisecond +/- 2.9e8 years [ 2.9e8 BC, 2.9e8 AD] - us microsecond +/- 2.9e5 years [290301 BC, 294241 AD] +us / μs microsecond +/- 2.9e5 years [290301 BC, 294241 AD] ns nanosecond +/- 292 years [ 1678 AD, 2262 AD] ps picosecond +/- 106 days [ 1969 AD, 1970 AD] fs femtosecond +/- 2.6 hours [ 1969 AD, 1970 AD] @@ -245,16 +300,16 @@ is necessary to get a desired answer. The first business day on or after a date: >>> np.busday_offset('2011-03-20', 0, roll='forward') - numpy.datetime64('2011-03-21','D') + numpy.datetime64('2011-03-21') >>> np.busday_offset('2011-03-22', 0, roll='forward') - numpy.datetime64('2011-03-22','D') + numpy.datetime64('2011-03-22') The first business day strictly after a date: >>> np.busday_offset('2011-03-20', 1, roll='backward') - numpy.datetime64('2011-03-21','D') + numpy.datetime64('2011-03-21') >>> np.busday_offset('2011-03-22', 1, roll='backward') - numpy.datetime64('2011-03-23','D') + numpy.datetime64('2011-03-23') The function is also useful for computing some kinds of days like holidays. In Canada and the U.S., Mother's day is on @@ -264,7 +319,7 @@ weekmask. .. admonition:: Example >>> np.busday_offset('2012-05', 1, roll='forward', weekmask='Sun') - numpy.datetime64('2012-05-13','D') + numpy.datetime64('2012-05-13') When performance is important for manipulating many business dates with one particular choice of weekmask and holidays, there is @@ -285,7 +340,7 @@ To test a datetime64 value to see if it is a valid day, use :func:`is_busday`. True >>> a = np.arange(np.datetime64('2011-07-11'), np.datetime64('2011-07-18')) >>> np.is_busday(a) - array([ True, True, True, True, True, False, False], dtype='bool') + array([ True, True, True, True, True, False, False]) np.busday_count(): `````````````````` @@ -329,166 +384,3 @@ Some examples:: weekmask = "Mon Tue Wed Thu Fri" # any amount of whitespace is allowed; abbreviations are case-sensitive. weekmask = "MonTue Wed Thu\tFri" - -Changes with NumPy 1.11 -======================= - -In prior versions of NumPy, the datetime64 type always stored -times in UTC. By default, creating a datetime64 object from a string or -printing it would convert from or to local time:: - - # old behavior - >>>> np.datetime64('2000-01-01T00:00:00') - numpy.datetime64('2000-01-01T00:00:00-0800') # note the timezone offset -08:00 - -A consensus of datetime64 users agreed that this behavior is undesirable -and at odds with how datetime64 is usually used (e.g., by pandas_). For -most use cases, a timezone naive datetime type is preferred, similar to the -``datetime.datetime`` type in the Python standard library. Accordingly, -datetime64 no longer assumes that input is in local time, nor does it print -local times:: - - >>>> np.datetime64('2000-01-01T00:00:00') - numpy.datetime64('2000-01-01T00:00:00') - -For backwards compatibility, datetime64 still parses timezone offsets, which -it handles by converting to UTC. However, the resulting datetime is timezone -naive:: - - >>> np.datetime64('2000-01-01T00:00:00-08') - DeprecationWarning: parsing timezone aware datetimes is deprecated; this will raise an error in the future - numpy.datetime64('2000-01-01T08:00:00') - -As a corollary to this change, we no longer prohibit casting between datetimes -with date units and datetimes with timeunits. With timezone naive datetimes, -the rule for casting from dates to times is no longer ambiguous. - -.. _pandas: http://pandas.pydata.org - - -Differences Between 1.6 and 1.7 Datetimes -========================================= - -The NumPy 1.6 release includes a more primitive datetime data type -than 1.7. This section documents many of the changes that have taken -place. - -String Parsing -`````````````` - -The datetime string parser in NumPy 1.6 is very liberal in what it accepts, -and silently allows invalid input without raising errors. The parser in -NumPy 1.7 is quite strict about only accepting ISO 8601 dates, with a few -convenience extensions. 1.6 always creates microsecond (us) units by -default, whereas 1.7 detects a unit based on the format of the string. -Here is a comparison.:: - - # NumPy 1.6.1 - >>> np.datetime64('1979-03-22') - 1979-03-22 00:00:00 - # NumPy 1.7.0 - >>> np.datetime64('1979-03-22') - numpy.datetime64('1979-03-22') - - # NumPy 1.6.1, unit default microseconds - >>> np.datetime64('1979-03-22').dtype - dtype('datetime64[us]') - # NumPy 1.7.0, unit of days detected from string - >>> np.datetime64('1979-03-22').dtype - dtype('<M8[D]') - - # NumPy 1.6.1, ignores invalid part of string - >>> np.datetime64('1979-03-2corruptedstring') - 1979-03-02 00:00:00 - # NumPy 1.7.0, raises error for invalid input - >>> np.datetime64('1979-03-2corruptedstring') - Traceback (most recent call last): - File "<stdin>", line 1, in <module> - ValueError: Error parsing datetime string "1979-03-2corruptedstring" at position 8 - - # NumPy 1.6.1, 'nat' produces today's date - >>> np.datetime64('nat') - 2012-04-30 00:00:00 - # NumPy 1.7.0, 'nat' produces not-a-time - >>> np.datetime64('nat') - numpy.datetime64('NaT') - - # NumPy 1.6.1, 'garbage' produces today's date - >>> np.datetime64('garbage') - 2012-04-30 00:00:00 - # NumPy 1.7.0, 'garbage' raises an exception - >>> np.datetime64('garbage') - Traceback (most recent call last): - File "<stdin>", line 1, in <module> - ValueError: Error parsing datetime string "garbage" at position 0 - - # NumPy 1.6.1, can't specify unit in scalar constructor - >>> np.datetime64('1979-03-22T19:00', 'h') - Traceback (most recent call last): - File "<stdin>", line 1, in <module> - TypeError: function takes at most 1 argument (2 given) - # NumPy 1.7.0, unit in scalar constructor - >>> np.datetime64('1979-03-22T19:00', 'h') - numpy.datetime64('1979-03-22T19:00-0500','h') - - # NumPy 1.6.1, reads ISO 8601 strings w/o TZ as UTC - >>> np.array(['1979-03-22T19:00'], dtype='M8[h]') - array([1979-03-22 19:00:00], dtype=datetime64[h]) - # NumPy 1.7.0, reads ISO 8601 strings w/o TZ as local (ISO specifies this) - >>> np.array(['1979-03-22T19:00'], dtype='M8[h]') - array(['1979-03-22T19-0500'], dtype='datetime64[h]') - - # NumPy 1.6.1, doesn't parse all ISO 8601 strings correctly - >>> np.array(['1979-03-22T12'], dtype='M8[h]') - array([1979-03-22 00:00:00], dtype=datetime64[h]) - >>> np.array(['1979-03-22T12:00'], dtype='M8[h]') - array([1979-03-22 12:00:00], dtype=datetime64[h]) - # NumPy 1.7.0, handles this case correctly - >>> np.array(['1979-03-22T12'], dtype='M8[h]') - array(['1979-03-22T12-0500'], dtype='datetime64[h]') - >>> np.array(['1979-03-22T12:00'], dtype='M8[h]') - array(['1979-03-22T12-0500'], dtype='datetime64[h]') - -Unit Conversion -``````````````` - -The 1.6 implementation of datetime does not convert between units correctly.:: - - # NumPy 1.6.1, the representation value is untouched - >>> np.array(['1979-03-22'], dtype='M8[D]') - array([1979-03-22 00:00:00], dtype=datetime64[D]) - >>> np.array(['1979-03-22'], dtype='M8[D]').astype('M8[M]') - array([2250-08-01 00:00:00], dtype=datetime64[M]) - # NumPy 1.7.0, the representation is scaled accordingly - >>> np.array(['1979-03-22'], dtype='M8[D]') - array(['1979-03-22'], dtype='datetime64[D]') - >>> np.array(['1979-03-22'], dtype='M8[D]').astype('M8[M]') - array(['1979-03'], dtype='datetime64[M]') - -Datetime Arithmetic -``````````````````` - -The 1.6 implementation of datetime only works correctly for a small subset of -arithmetic operations. Here we show some simple cases.:: - - # NumPy 1.6.1, produces invalid results if units are incompatible - >>> a = np.array(['1979-03-22T12'], dtype='M8[h]') - >>> b = np.array([3*60], dtype='m8[m]') - >>> a + b - array([1970-01-01 00:00:00.080988], dtype=datetime64[us]) - # NumPy 1.7.0, promotes to higher-resolution unit - >>> a = np.array(['1979-03-22T12'], dtype='M8[h]') - >>> b = np.array([3*60], dtype='m8[m]') - >>> a + b - array(['1979-03-22T15:00-0500'], dtype='datetime64[m]') - - # NumPy 1.6.1, arithmetic works if everything is microseconds - >>> a = np.array(['1979-03-22T12:00'], dtype='M8[us]') - >>> b = np.array([3*60*60*1000000], dtype='m8[us]') - >>> a + b - array([1979-03-22 15:00:00], dtype=datetime64[us]) - # NumPy 1.7.0 - >>> a = np.array(['1979-03-22T12:00'], dtype='M8[us]') - >>> b = np.array([3*60*60*1000000], dtype='m8[us]') - >>> a + b - array(['1979-03-22T15:00:00.000000-0500'], dtype='datetime64[us]') diff --git a/doc/source/reference/arrays.dtypes.rst b/doc/source/reference/arrays.dtypes.rst index dcf04b453406..8606bc8f1a3a 100644 --- a/doc/source/reference/arrays.dtypes.rst +++ b/doc/source/reference/arrays.dtypes.rst @@ -14,7 +14,7 @@ following aspects of the data: 1. Type of the data (integer, float, Python object, etc.) 2. Size of the data (how many bytes is in *e.g.* the integer) 3. Byte order of the data (:term:`little-endian` or :term:`big-endian`) -4. If the data type is :term:`structured`, an aggregate of other +4. If the data type is :term:`structured data type`, an aggregate of other data types, (*e.g.*, describing an array item consisting of an integer and a float), @@ -42,7 +42,7 @@ needed in NumPy. pair: dtype; field Structured data types are formed by creating a data type whose -:term:`fields` contain other data types. Each field has a name by +:term:`field` contain other data types. Each field has a name by which it can be :ref:`accessed <arrays.indexing.fields>`. The parent data type should be of sufficient size to contain all its fields; the parent is nearly always based on the :class:`void` type which allows @@ -87,22 +87,22 @@ Sub-arrays always have a C-contiguous memory layout. >>> dt = np.dtype([('name', np.unicode_, 16), ('grades', np.float64, (2,))]) >>> dt['name'] - dtype('|U16') + dtype('<U16') >>> dt['grades'] - dtype(('float64',(2,))) + dtype(('<f8', (2,))) Items of an array of this data type are wrapped in an :ref:`array scalar <arrays.scalars>` type that also has two fields: >>> x = np.array([('Sarah', (8.0, 7.0)), ('John', (6.0, 7.0))], dtype=dt) >>> x[1] - ('John', [6.0, 7.0]) + ('John', [6., 7.]) >>> x[1]['grades'] - array([ 6., 7.]) + array([6., 7.]) >>> type(x[1]) - <type 'numpy.void'> + <class 'numpy.void'> >>> type(x[1]['grades']) - <type 'numpy.ndarray'> + <class 'numpy.ndarray'> .. _arrays.dtypes.constructing: @@ -122,14 +122,12 @@ constructor: What can be converted to a data-type object is described below: :class:`dtype` object - .. index:: triple: dtype; construction; from dtype Used as-is. -:const:`None` - +None .. index:: triple: dtype; construction; from None @@ -139,13 +137,12 @@ What can be converted to a data-type object is described below: triple: dtype; construction; from type Array-scalar types - The 24 built-in :ref:`array scalar type objects <arrays.scalars.built-in>` all convert to an associated data-type object. This is true for their sub-classes as well. Note that not all data-type information can be supplied with a - type-object: for example, :term:`flexible` data-types have + type-object: for example, `flexible` data-types have a default *itemsize* of 0, and require an explicitly given size to be useful. @@ -155,7 +152,6 @@ Array-scalar types >>> dt = np.dtype(np.complex128) # 128-bit complex floating-point number Generic types - The generic hierarchical type objects convert to corresponding type objects according to the associations: @@ -168,8 +164,16 @@ Generic types :class:`generic`, :class:`flexible` :class:`void` ===================================================== =============== -Built-in Python types + .. deprecated:: 1.19 + This conversion of generic scalar types is deprecated. + This is because it can be unexpected in a context such as + ``arr.astype(dtype=np.floating)``, which casts an array of ``float32`` + to an array of ``float64``, even though ``float32`` is a subdtype of + ``np.floating``. + + +Built-in Python types Several python types are equivalent to a corresponding array scalar when used to generate a :class:`dtype` object: @@ -179,8 +183,7 @@ Built-in Python types :class:`float` :class:`float\_` :class:`complex` :class:`cfloat` :class:`bytes` :class:`bytes\_` - :class:`str` :class:`bytes\_` (Python2) or :class:`unicode\_` (Python3) - :class:`unicode` :class:`unicode\_` + :class:`str` :class:`str\_` :class:`buffer` :class:`void` (all others) :class:`object_` ================ =============== @@ -196,8 +199,12 @@ Built-in Python types >>> dt = np.dtype(int) # Python-compatible integer >>> dt = np.dtype(object) # Python object -Types with ``.dtype`` + .. note:: + + All other types map to ``object_`` for convenience. Code should expect + that such types may map to a specific (new) dtype in the future. +Types with ``.dtype`` Any type object with a ``dtype`` attribute: The attribute will be accessed and used directly. The attribute must return something that is convertible into a dtype object. @@ -211,7 +218,6 @@ prepended with ``'>'`` (:term:`big-endian`), ``'<'`` specify the byte order. One-character strings - Each built-in data-type has a character code (the updated Numeric typecodes), that uniquely identifies it. @@ -223,7 +229,6 @@ One-character strings >>> dt = np.dtype('d') # double-precision floating-point number Array-protocol type strings (see :ref:`arrays.interface`) - The first character specifies the kind of data and the remaining characters specify the number of bytes per item, except for Unicode, where it is interpreted as the number of characters. The item size @@ -259,14 +264,12 @@ Array-protocol type strings (see :ref:`arrays.interface`) .. admonition:: Note on string types For backward compatibility with Python 2 the ``S`` and ``a`` typestrings - remain zero-terminated bytes and ``np.string_`` continues to map to - ``np.bytes_``. - To use actual strings in Python 3 use ``U`` or ``np.unicode_``. + remain zero-terminated bytes and `numpy.string_` continues to alias + `numpy.bytes_`. To use actual strings in Python 3 use ``U`` or `numpy.str_`. For signed bytes that do not need zero-termination ``b`` or ``i1`` can be used. String with comma-separated fields - A short-hand notation for specifying the format of a structured data type is a comma-separated string of basic formats. @@ -298,19 +301,17 @@ String with comma-separated fields >>> dt = np.dtype("a3, 3u8, (3,4)a10") Type strings - Any string in :obj:`numpy.sctypeDict`.keys(): .. admonition:: Example >>> dt = np.dtype('uint32') # 32-bit unsigned integer - >>> dt = np.dtype('Float64') # 64-bit floating-point number + >>> dt = np.dtype('float64') # 64-bit floating-point number .. index:: triple: dtype; construction; from tuple ``(flexible_dtype, itemsize)`` - The first argument must be an object that is converted to a zero-sized flexible data-type object, the second argument is an integer providing the desired itemsize. @@ -321,30 +322,29 @@ Type strings >>> dt = np.dtype(('U', 10)) # 10-character unicode string ``(fixed_dtype, shape)`` - .. index:: pair: dtype; sub-array The first argument is any object that can be converted into a fixed-size data-type object. The second argument is the desired shape of this type. If the shape parameter is 1, then the - data-type object is equivalent to fixed dtype. If *shape* is a - tuple, then the new dtype defines a sub-array of the given shape. + data-type object used to be equivalent to fixed dtype. This behaviour is + deprecated since NumPy 1.17 and will raise an error in the future. + If *shape* is a tuple, then the new dtype defines a sub-array of the given + shape. .. admonition:: Example >>> dt = np.dtype((np.int32, (2,2))) # 2 x 2 integer sub-array - >>> dt = np.dtype(('U10', 1)) # 10-character string >>> dt = np.dtype(('i4, (2,3)f8, f4', (2,3))) # 2 x 3 structured sub-array .. index:: triple: dtype; construction; from list ``[(field_name, field_dtype, field_shape), ...]`` - *obj* should be a list of fields where each field is described by a tuple of length 2 or 3. (Equivalent to the ``descr`` item in the - :obj:`__array_interface__` attribute.) + :obj:`~object.__array_interface__` attribute.) The first element, *field_name*, is the field name (if this is ``''`` then a standard field name, ``'f#'``, is assigned). The @@ -381,7 +381,6 @@ Type strings triple: dtype; construction; from dict ``{'names': ..., 'formats': ..., 'offsets': ..., 'titles': ..., 'itemsize': ...}`` - This style has two required and three optional keys. The *names* and *formats* keys are required. Their respective values are equal-length lists with the field names and the field formats. @@ -391,10 +390,10 @@ Type strings When the optional keys *offsets* and *titles* are provided, their values must each be lists of the same length as the *names* and *formats* lists. The *offsets* value is a list of byte offsets - (integers) for each field, while the *titles* value is a list of - titles for each field (:const:`None` can be used if no title is - desired for that field). The *titles* can be any :class:`string` - or :class:`unicode` object and will add another entry to the + (limited to `ctypes.c_int`) for each field, while the *titles* value is a + list of titles for each field (``None`` can be used if no title is + desired for that field). The *titles* can be any object, but when a + :class:`str` object will add another entry to the fields dictionary keyed by the title and referencing the same field tuple which will contain the title as an additional tuple member. @@ -402,7 +401,8 @@ Type strings The *itemsize* key allows the total size of the dtype to be set, and must be an integer large enough so all the fields are within the dtype. If the dtype being constructed is aligned, - the *itemsize* must also be divisible by the struct alignment. + the *itemsize* must also be divisible by the struct alignment. Total dtype + *itemsize* is limited to `ctypes.c_int`. .. admonition:: Example @@ -410,7 +410,7 @@ Type strings an 8-bit unsigned integer: >>> dt = np.dtype({'names': ['r','g','b','a'], - ... 'formats': [uint8, uint8, uint8, uint8]}) + ... 'formats': [np.uint8, np.uint8, np.uint8, np.uint8]}) Data type with fields ``r`` and ``b`` (with the given titles), both being 8-bit unsigned integers, the first at byte position @@ -422,7 +422,6 @@ Type strings ``{'field1': ..., 'field2': ..., ...}`` - This usage is discouraged, because it is ambiguous with the other dict-based construction method. If you have a field called 'names' and a field called 'formats' there will be @@ -440,11 +439,10 @@ Type strings byte position 0), ``col2`` (32-bit float at byte position 10), and ``col3`` (integers at byte position 14): - >>> dt = np.dtype({'col1': ('U10', 0), 'col2': (float32, 10), - 'col3': (int, 14)}) + >>> dt = np.dtype({'col1': ('U10', 0), 'col2': (np.float32, 10), + ... 'col3': (int, 14)}) ``(base_dtype, new_dtype)`` - In NumPy 1.7 and later, this form allows `base_dtype` to be interpreted as a structured dtype. Arrays created with this dtype will have underlying dtype `base_dtype` but will have fields and flags taken from `new_dtype`. @@ -457,12 +455,13 @@ Type strings Both arguments must be convertible to data-type objects with the same total size. + .. admonition:: Example 32-bit integer, whose first two bytes are interpreted as an integer via field ``real``, and the following two bytes via field ``imag``. - >>> dt = np.dtype((np.int32,{'real':(np.int16, 0),'imag':(np.int16, 2)}) + >>> dt = np.dtype((np.int32,{'real':(np.int16, 0),'imag':(np.int16, 2)})) 32-bit integer, which is interpreted as consisting of a sub-array of shape ``(4,)`` containing 8-bit integers: @@ -509,7 +508,7 @@ Endianness of this data: dtype.byteorder -Information about sub-data-types in a :term:`structured` data type: +Information about sub-data-types in a :term:`structured data type`: .. autosummary:: :toctree: generated/ @@ -536,6 +535,14 @@ Attributes providing additional information: dtype.isnative dtype.descr dtype.alignment + dtype.base + +Metadata attached by the user: + +.. autosummary:: + :toctree: generated/ + + dtype.metadata Methods @@ -555,3 +562,20 @@ The following methods implement the pickle protocol: dtype.__reduce__ dtype.__setstate__ + +Utility method for typing: + +.. autosummary:: + :toctree: generated/ + + dtype.__class_getitem__ + +Comparison operations: + +.. autosummary:: + :toctree: generated/ + + dtype.__ge__ + dtype.__gt__ + dtype.__le__ + dtype.__lt__ diff --git a/doc/source/reference/arrays.indexing.rst b/doc/source/reference/arrays.indexing.rst index ba1bfd31210b..100d22e029d0 100644 --- a/doc/source/reference/arrays.indexing.rst +++ b/doc/source/reference/arrays.indexing.rst @@ -1,554 +1,70 @@ +.. _routines.indexing: .. _arrays.indexing: -Indexing -======== +Indexing routines +================= -.. sectionauthor:: adapted from "Guide to NumPy" by Travis E. Oliphant +.. seealso:: :ref:`basics.indexing` .. currentmodule:: numpy -.. index:: indexing, slicing - -:class:`ndarrays <ndarray>` can be indexed using the standard Python -``x[obj]`` syntax, where *x* is the array and *obj* the selection. -There are three kinds of indexing available: field access, basic -slicing, advanced indexing. Which one occurs depends on *obj*. - -.. note:: - - In Python, ``x[(exp1, exp2, ..., expN)]`` is equivalent to - ``x[exp1, exp2, ..., expN]``; the latter is just syntactic sugar - for the former. - - -Basic Slicing and Indexing +Generating index arrays +----------------------- +.. autosummary:: + :toctree: generated/ + + c_ + r_ + s_ + nonzero + where + indices + ix_ + ogrid + ravel_multi_index + unravel_index + diag_indices + diag_indices_from + mask_indices + tril_indices + tril_indices_from + triu_indices + triu_indices_from + +Indexing-like operations +------------------------ +.. autosummary:: + :toctree: generated/ + + take + take_along_axis + choose + compress + diag + diagonal + select + lib.stride_tricks.sliding_window_view + lib.stride_tricks.as_strided + +Inserting data into arrays -------------------------- - -Basic slicing extends Python's basic concept of slicing to N -dimensions. Basic slicing occurs when *obj* is a :class:`slice` object -(constructed by ``start:stop:step`` notation inside of brackets), an -integer, or a tuple of slice objects and integers. :const:`Ellipsis` -and :const:`newaxis` objects can be interspersed with these as -well. - -.. deprecated:: 1.15.0 - - In order to remain backward compatible with a common usage in - Numeric, basic slicing is also initiated if the selection object is - any non-ndarray and non-tuple sequence (such as a :class:`list`) containing - :class:`slice` objects, the :const:`Ellipsis` object, or the :const:`newaxis` - object, but not for integer arrays or other embedded sequences. - -.. index:: - triple: ndarray; special methods; getitem - triple: ndarray; special methods; setitem - single: ellipsis - single: newaxis - -The simplest case of indexing with *N* integers returns an :ref:`array -scalar <arrays.scalars>` representing the corresponding item. As in -Python, all indices are zero-based: for the *i*-th index :math:`n_i`, -the valid range is :math:`0 \le n_i < d_i` where :math:`d_i` is the -*i*-th element of the shape of the array. Negative indices are -interpreted as counting from the end of the array (*i.e.*, if -:math:`n_i < 0`, it means :math:`n_i + d_i`). - - -All arrays generated by basic slicing are always :term:`views <view>` -of the original array. - -The standard rules of sequence slicing apply to basic slicing on a -per-dimension basis (including using a step index). Some useful -concepts to remember include: - -- The basic slice syntax is ``i:j:k`` where *i* is the starting index, - *j* is the stopping index, and *k* is the step (:math:`k\neq0`). - This selects the *m* elements (in the corresponding dimension) with - index values *i*, *i + k*, ..., *i + (m - 1) k* where - :math:`m = q + (r\neq0)` and *q* and *r* are the quotient and remainder - obtained by dividing *j - i* by *k*: *j - i = q k + r*, so that - *i + (m - 1) k < j*. - - .. admonition:: Example - - >>> x = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) - >>> x[1:7:2] - array([1, 3, 5]) - -- Negative *i* and *j* are interpreted as *n + i* and *n + j* where - *n* is the number of elements in the corresponding dimension. - Negative *k* makes stepping go towards smaller indices. - - .. admonition:: Example - - >>> x[-2:10] - array([8, 9]) - >>> x[-3:3:-1] - array([7, 6, 5, 4]) - -- Assume *n* is the number of elements in the dimension being - sliced. Then, if *i* is not given it defaults to 0 for *k > 0* and - *n - 1* for *k < 0* . If *j* is not given it defaults to *n* for *k > 0* - and *-n-1* for *k < 0* . If *k* is not given it defaults to 1. Note that - ``::`` is the same as ``:`` and means select all indices along this - axis. - - .. admonition:: Example - - >>> x[5:] - array([5, 6, 7, 8, 9]) - -- If the number of objects in the selection tuple is less than - *N* , then ``:`` is assumed for any subsequent dimensions. - - .. admonition:: Example - - >>> x = np.array([[[1],[2],[3]], [[4],[5],[6]]]) - >>> x.shape - (2, 3, 1) - >>> x[1:2] - array([[[4], - [5], - [6]]]) - -- :const:`Ellipsis` expand to the number of ``:`` objects needed to - make a selection tuple of the same length as ``x.ndim``. There may - only be a single ellipsis present. - - .. admonition:: Example - - >>> x[...,0] - array([[1, 2, 3], - [4, 5, 6]]) - -- Each :const:`newaxis` object in the selection tuple serves to expand - the dimensions of the resulting selection by one unit-length - dimension. The added dimension is the position of the :const:`newaxis` - object in the selection tuple. - - .. admonition:: Example - - >>> x[:,np.newaxis,:,:].shape - (2, 1, 3, 1) - -- An integer, *i*, returns the same values as ``i:i+1`` - **except** the dimensionality of the returned object is reduced by - 1. In particular, a selection tuple with the *p*-th - element an integer (and all other entries ``:``) returns the - corresponding sub-array with dimension *N - 1*. If *N = 1* - then the returned object is an array scalar. These objects are - explained in :ref:`arrays.scalars`. - -- If the selection tuple has all entries ``:`` except the - *p*-th entry which is a slice object ``i:j:k``, - then the returned array has dimension *N* formed by - concatenating the sub-arrays returned by integer indexing of - elements *i*, *i+k*, ..., *i + (m - 1) k < j*, - -- Basic slicing with more than one non-``:`` entry in the slicing - tuple, acts like repeated application of slicing using a single - non-``:`` entry, where the non-``:`` entries are successively taken - (with all other non-``:`` entries replaced by ``:``). Thus, - ``x[ind1,...,ind2,:]`` acts like ``x[ind1][...,ind2,:]`` under basic - slicing. - - .. warning:: The above is **not** true for advanced indexing. - -- You may use slicing to set values in the array, but (unlike lists) you - can never grow the array. The size of the value to be set in - ``x[obj] = value`` must be (broadcastable) to the same shape as - ``x[obj]``. - -.. index:: - pair: ndarray; view - -.. note:: - - Remember that a slicing tuple can always be constructed as *obj* - and used in the ``x[obj]`` notation. Slice objects can be used in - the construction in place of the ``[start:stop:step]`` - notation. For example, ``x[1:10:5,::-1]`` can also be implemented - as ``obj = (slice(1,10,5), slice(None,None,-1)); x[obj]`` . This - can be useful for constructing generic code that works on arrays - of arbitrary dimension. - -.. data:: newaxis - - The :const:`newaxis` object can be used in all slicing operations to - create an axis of length one. :const:`newaxis` is an alias for - 'None', and 'None' can be used in place of this with the same result. - - -Advanced Indexing ------------------ - -Advanced indexing is triggered when the selection object, *obj*, is a -non-tuple sequence object, an :class:`ndarray` (of data type integer or bool), -or a tuple with at least one sequence object or ndarray (of data type -integer or bool). There are two types of advanced indexing: integer -and Boolean. - -Advanced indexing always returns a *copy* of the data (contrast with -basic slicing that returns a :term:`view`). - -.. warning:: - - The definition of advanced indexing means that ``x[(1,2,3),]`` is - fundamentally different than ``x[(1,2,3)]``. The latter is - equivalent to ``x[1,2,3]`` which will trigger basic selection while - the former will trigger advanced indexing. Be sure to understand - why this occurs. - - Also recognize that ``x[[1,2,3]]`` will trigger advanced indexing, - whereas due to the deprecated Numeric compatibility mentioned above, - ``x[[1,2,slice(None)]]`` will trigger basic slicing. - -Integer array indexing -^^^^^^^^^^^^^^^^^^^^^^ - -Integer array indexing allows selection of arbitrary items in the array -based on their *N*-dimensional index. Each integer array represents a number -of indexes into that dimension. - -Purely integer array indexing -""""""""""""""""""""""""""""" - -When the index consists of as many integer arrays as the array being indexed -has dimensions, the indexing is straight forward, but different from slicing. - -Advanced indexes always are :ref:`broadcast<ufuncs.broadcasting>` and -iterated as *one*:: - - result[i_1, ..., i_M] == x[ind_1[i_1, ..., i_M], ind_2[i_1, ..., i_M], - ..., ind_N[i_1, ..., i_M]] - -Note that the result shape is identical to the (broadcast) indexing array -shapes ``ind_1, ..., ind_N``. - -.. admonition:: Example - - From each row, a specific element should be selected. The row index is just - ``[0, 1, 2]`` and the column index specifies the element to choose for the - corresponding row, here ``[0, 1, 0]``. Using both together the task - can be solved using advanced indexing: - - >>> x = np.array([[1, 2], [3, 4], [5, 6]]) - >>> x[[0, 1, 2], [0, 1, 0]] - array([1, 4, 5]) - -To achieve a behaviour similar to the basic slicing above, broadcasting can be -used. The function :func:`ix_` can help with this broadcasting. This is best -understood with an example. - -.. admonition:: Example - - From a 4x3 array the corner elements should be selected using advanced - indexing. Thus all elements for which the column is one of ``[0, 2]`` and - the row is one of ``[0, 3]`` need to be selected. To use advanced indexing - one needs to select all elements *explicitly*. Using the method explained - previously one could write: - - >>> x = array([[ 0, 1, 2], - ... [ 3, 4, 5], - ... [ 6, 7, 8], - ... [ 9, 10, 11]]) - >>> rows = np.array([[0, 0], - ... [3, 3]], dtype=np.intp) - >>> columns = np.array([[0, 2], - ... [0, 2]], dtype=np.intp) - >>> x[rows, columns] - array([[ 0, 2], - [ 9, 11]]) - - However, since the indexing arrays above just repeat themselves, - broadcasting can be used (compare operations such as - ``rows[:, np.newaxis] + columns``) to simplify this: - - >>> rows = np.array([0, 3], dtype=np.intp) - >>> columns = np.array([0, 2], dtype=np.intp) - >>> rows[:, np.newaxis] - array([[0], - [3]]) - >>> x[rows[:, np.newaxis], columns] - array([[ 0, 2], - [ 9, 11]]) - - This broadcasting can also be achieved using the function :func:`ix_`: - - >>> x[np.ix_(rows, columns)] - array([[ 0, 2], - [ 9, 11]]) - - Note that without the ``np.ix_`` call, only the diagonal elements would - be selected, as was used in the previous example. This difference is the - most important thing to remember about indexing with multiple advanced - indexes. - -Combining advanced and basic indexing -""""""""""""""""""""""""""""""""""""" - -When there is at least one slice (``:``), ellipsis (``...``) or ``np.newaxis`` -in the index (or the array has more dimensions than there are advanced indexes), -then the behaviour can be more complicated. It is like concatenating the -indexing result for each advanced index element - -In the simplest case, there is only a *single* advanced index. A single -advanced index can for example replace a slice and the result array will be -the same, however, it is a copy and may have a different memory layout. -A slice is preferable when it is possible. - -.. admonition:: Example - - >>> x[1:2, 1:3] - array([[4, 5]]) - >>> x[1:2, [1, 2]] - array([[4, 5]]) - -The easiest way to understand the situation may be to think in -terms of the result shape. There are two parts to the indexing operation, -the subspace defined by the basic indexing (excluding integers) and the -subspace from the advanced indexing part. Two cases of index combination -need to be distinguished: - -* The advanced indexes are separated by a slice, ellipsis or newaxis. - For example ``x[arr1, :, arr2]``. -* The advanced indexes are all next to each other. - For example ``x[..., arr1, arr2, :]`` but *not* ``x[arr1, :, 1]`` - since ``1`` is an advanced index in this regard. - -In the first case, the dimensions resulting from the advanced indexing -operation come first in the result array, and the subspace dimensions after -that. -In the second case, the dimensions from the advanced indexing operations -are inserted into the result array at the same spot as they were in the -initial array (the latter logic is what makes simple advanced indexing -behave just like slicing). - -.. admonition:: Example - - Suppose ``x.shape`` is (10,20,30) and ``ind`` is a (2,3,4)-shaped - indexing :class:`intp` array, then ``result = x[...,ind,:]`` has - shape (10,2,3,4,30) because the (20,)-shaped subspace has been - replaced with a (2,3,4)-shaped broadcasted indexing subspace. If - we let *i, j, k* loop over the (2,3,4)-shaped subspace then - ``result[...,i,j,k,:] = x[...,ind[i,j,k],:]``. This example - produces the same result as :meth:`x.take(ind, axis=-2) <ndarray.take>`. - -.. admonition:: Example - - Let ``x.shape`` be (10,20,30,40,50) and suppose ``ind_1`` - and ``ind_2`` can be broadcast to the shape (2,3,4). Then - ``x[:,ind_1,ind_2]`` has shape (10,2,3,4,40,50) because the - (20,30)-shaped subspace from X has been replaced with the - (2,3,4) subspace from the indices. However, - ``x[:,ind_1,:,ind_2]`` has shape (2,3,4,10,30,50) because there - is no unambiguous place to drop in the indexing subspace, thus - it is tacked-on to the beginning. It is always possible to use - :meth:`.transpose() <ndarray.transpose>` to move the subspace - anywhere desired. Note that this example cannot be replicated - using :func:`take`. - - -Boolean array indexing -^^^^^^^^^^^^^^^^^^^^^^ - -This advanced indexing occurs when obj is an array object of Boolean -type, such as may be returned from comparison operators. A single -boolean index array is practically identical to ``x[obj.nonzero()]`` where, -as described above, :meth:`obj.nonzero() <ndarray.nonzero>` returns a -tuple (of length :attr:`obj.ndim <ndarray.ndim>`) of integer index -arrays showing the :const:`True` elements of *obj*. However, it is -faster when ``obj.shape == x.shape``. - -If ``obj.ndim == x.ndim``, ``x[obj]`` returns a 1-dimensional array -filled with the elements of *x* corresponding to the :const:`True` -values of *obj*. The search order will be :term:`row-major`, -C-style. If *obj* has :const:`True` values at entries that are outside -of the bounds of *x*, then an index error will be raised. If *obj* is -smaller than *x* it is identical to filling it with :const:`False`. - -.. admonition:: Example - - A common use case for this is filtering for desired element values. - For example one may wish to select all entries from an array which - are not NaN: - - >>> x = np.array([[1., 2.], [np.nan, 3.], [np.nan, np.nan]]) - >>> x[~np.isnan(x)] - array([ 1., 2., 3.]) - - Or wish to add a constant to all negative elements: - - >>> x = np.array([1., -1., -2., 3]) - >>> x[x < 0] += 20 - >>> x - array([ 1., 19., 18., 3.]) - -In general if an index includes a Boolean array, the result will be -identical to inserting ``obj.nonzero()`` into the same position -and using the integer array indexing mechanism described above. -``x[ind_1, boolean_array, ind_2]`` is equivalent to -``x[(ind_1,) + boolean_array.nonzero() + (ind_2,)]``. - -If there is only one Boolean array and no integer indexing array present, -this is straight forward. Care must only be taken to make sure that the -boolean index has *exactly* as many dimensions as it is supposed to work -with. - -.. admonition:: Example - - From an array, select all rows which sum up to less or equal two: - - >>> x = np.array([[0, 1], [1, 1], [2, 2]]) - >>> rowsum = x.sum(-1) - >>> x[rowsum <= 2, :] - array([[0, 1], - [1, 1]]) - - But if ``rowsum`` would have two dimensions as well: - - >>> rowsum = x.sum(-1, keepdims=True) - >>> rowsum.shape - (3, 1) - >>> x[rowsum <= 2, :] # fails - IndexError: too many indices - >>> x[rowsum <= 2] - array([0, 1]) - - The last one giving only the first elements because of the extra dimension. - Compare ``rowsum.nonzero()`` to understand this example. - -Combining multiple Boolean indexing arrays or a Boolean with an integer -indexing array can best be understood with the -:meth:`obj.nonzero() <ndarray.nonzero>` analogy. The function :func:`ix_` -also supports boolean arrays and will work without any surprises. - -.. admonition:: Example - - Use boolean indexing to select all rows adding up to an even - number. At the same time columns 0 and 2 should be selected with an - advanced integer index. Using the :func:`ix_` function this can be done - with: - - >>> x = array([[ 0, 1, 2], - ... [ 3, 4, 5], - ... [ 6, 7, 8], - ... [ 9, 10, 11]]) - >>> rows = (x.sum(-1) % 2) == 0 - >>> rows - array([False, True, False, True]) - >>> columns = [0, 2] - >>> x[np.ix_(rows, columns)] - array([[ 3, 5], - [ 9, 11]]) - - Without the ``np.ix_`` call or only the diagonal elements would be - selected. - - Or without ``np.ix_`` (compare the integer array examples): - - >>> rows = rows.nonzero()[0] - >>> x[rows[:, np.newaxis], columns] - array([[ 3, 5], - [ 9, 11]]) - -Detailed notes --------------- - -These are some detailed notes, which are not of importance for day to day -indexing (in no particular order): - -* The native NumPy indexing type is ``intp`` and may differ from the - default integer array type. ``intp`` is the smallest data type - sufficient to safely index any array; for advanced indexing it may be - faster than other types. -* For advanced assignments, there is in general no guarantee for the - iteration order. This means that if an element is set more than once, - it is not possible to predict the final result. -* An empty (tuple) index is a full scalar index into a zero dimensional array. - ``x[()]`` returns a *scalar* if ``x`` is zero dimensional and a view - otherwise. On the other hand ``x[...]`` always returns a view. -* If a zero dimensional array is present in the index *and* it is a full - integer index the result will be a *scalar* and not a zero dimensional array. - (Advanced indexing is not triggered.) -* When an ellipsis (``...``) is present but has no size (i.e. replaces zero - ``:``) the result will still always be an array. A view if no advanced index - is present, otherwise a copy. -* the ``nonzero`` equivalence for Boolean arrays does not hold for zero - dimensional boolean arrays. -* When the result of an advanced indexing operation has no elements but an - individual index is out of bounds, whether or not an ``IndexError`` is - raised is undefined (e.g. ``x[[], [123]]`` with ``123`` being out of bounds). -* When a *casting* error occurs during assignment (for example updating a - numerical array using a sequence of strings), the array being assigned - to may end up in an unpredictable partially updated state. - However, if any other error (such as an out of bounds index) occurs, the - array will remain unchanged. -* The memory layout of an advanced indexing result is optimized for each - indexing operation and no particular memory order can be assumed. -* When using a subclass (especially one which manipulates its shape), the - default ``ndarray.__setitem__`` behaviour will call ``__getitem__`` for - *basic* indexing but not for *advanced* indexing. For such a subclass it may - be preferable to call ``ndarray.__setitem__`` with a *base class* ndarray - view on the data. This *must* be done if the subclasses ``__getitem__`` does - not return views. - -.. _arrays.indexing.fields: - - -Field Access -------------- - -.. seealso:: :ref:`arrays.dtypes`, :ref:`arrays.scalars` - -If the :class:`ndarray` object is a structured array the :term:`fields <field>` -of the array can be accessed by indexing the array with strings, -dictionary-like. - -Indexing ``x['field-name']`` returns a new :term:`view` to the array, -which is of the same shape as *x* (except when the field is a -sub-array) but of data type ``x.dtype['field-name']`` and contains -only the part of the data in the specified field. Also -:ref:`record array <arrays.classes.rec>` scalars can be "indexed" this way. - -Indexing into a structured array can also be done with a list of field names, -*e.g.* ``x[['field-name1','field-name2']]``. Currently this returns a new -array containing a copy of the values in the fields specified in the list. -As of NumPy 1.7, returning a copy is being deprecated in favor of returning -a view. A copy will continue to be returned for now, but a FutureWarning -will be issued when writing to the copy. If you depend on the current -behavior, then we suggest copying the returned array explicitly, i.e. use -x[['field-name1','field-name2']].copy(). This will work with both past and -future versions of NumPy. - -If the accessed field is a sub-array, the dimensions of the sub-array -are appended to the shape of the result. - -.. admonition:: Example - - >>> x = np.zeros((2,2), dtype=[('a', np.int32), ('b', np.float64, (3,3))]) - >>> x['a'].shape - (2, 2) - >>> x['a'].dtype - dtype('int32') - >>> x['b'].shape - (2, 2, 3, 3) - >>> x['b'].dtype - dtype('float64') - - -Flat Iterator indexing ----------------------- - -:attr:`x.flat <ndarray.flat>` returns an iterator that will iterate -over the entire array (in C-contiguous style with the last index -varying the fastest). This iterator object can also be indexed using -basic slicing or advanced indexing as long as the selection object is -not a tuple. This should be clear from the fact that :attr:`x.flat -<ndarray.flat>` is a 1-dimensional view. It can be used for integer -indexing with 1-dimensional C-style-flat indices. The shape of any -returned array is therefore the shape of the integer indexing object. - -.. index:: - single: indexing - single: ndarray +.. autosummary:: + :toctree: generated/ + + place + put + put_along_axis + putmask + fill_diagonal + +Iterating over arrays +--------------------- +.. autosummary:: + :toctree: generated/ + + nditer + ndenumerate + ndindex + nested_iters + flatiter + lib.Arrayterator diff --git a/doc/source/reference/arrays.interface.rst b/doc/source/reference/arrays.interface.rst index 4a5fe62bf3e3..6a8c5f9c4d09 100644 --- a/doc/source/reference/arrays.interface.rst +++ b/doc/source/reference/arrays.interface.rst @@ -22,7 +22,7 @@ The Array Interface described here. __ http://cython.org/ -__ http://wiki.cython.org/tutorials/numpy +__ https://github.com/cython/cython/wiki/tutorials-numpy :version: 3 @@ -49,9 +49,9 @@ Python side =========== This approach to the interface consists of the object having an -:data:`__array_interface__` attribute. +:data:`~object.__array_interface__` attribute. -.. data:: __array_interface__ +.. data:: object.__array_interface__ A dictionary of items (3 required and 5 optional). The optional keys in the dictionary have implied defaults if they are not @@ -60,18 +60,16 @@ This approach to the interface consists of the object having an The keys are: **shape** (required) - Tuple whose elements are the array size in each dimension. Each - entry is an integer (a Python int or long). Note that these - integers could be larger than the platform "int" or "long" - could hold (a Python int is a C long). It is up to the code + entry is an integer (a Python :py:class:`int`). Note that these + integers could be larger than the platform ``int`` or ``long`` + could hold (a Python :py:class:`int` is a C ``long``). It is up to the code using this attribute to handle this appropriately; either by raising an error when overflow is possible, or by using - :c:data:`Py_LONG_LONG` as the C type for the shapes. + ``long long`` as the C type for the shapes. **typestr** (required) - - A string providing the basic type of the homogenous array The + A string providing the basic type of the homogeneous array The basic string format consists of 3 parts: a character describing the byteorder of the data (``<``: little-endian, ``>``: big-endian, ``|``: not-relevant), a character code giving the @@ -97,7 +95,6 @@ This approach to the interface consists of the object having an ===== ================================================================ **descr** (optional) - A list of tuples providing a more detailed description of the memory layout for each item in the homogeneous array. Each tuple in the list has two or three elements. Normally, this @@ -127,7 +124,6 @@ This approach to the interface consists of the object having an **Default**: ``[('', typestr)]`` **data** (optional) - A 2-tuple whose first argument is an integer (a long integer if necessary) that points to the data-area storing the array contents. This pointer must point to the first element of @@ -136,56 +132,52 @@ This approach to the interface consists of the object having an means the data area is read-only). This attribute can also be an object exposing the - :c:func:`buffer interface <PyObject_AsCharBuffer>` which + :ref:`buffer interface <bufferobjects>` which will be used to share the data. If this key is not present (or - returns :class:`None`), then memory sharing will be done + returns None), then memory sharing will be done through the buffer interface of the object itself. In this case, the offset key can be used to indicate the start of the buffer. A reference to the object exposing the array interface must be stored by the new object if the memory area is to be secured. - **Default**: :const:`None` + **Default**: None **strides** (optional) - - Either :const:`None` to indicate a C-style contiguous array or + Either ``None`` to indicate a C-style contiguous array or a Tuple of strides which provides the number of bytes needed to jump to the next array element in the corresponding dimension. Each entry must be an integer (a Python - :const:`int` or :const:`long`). As with shape, the values may - be larger than can be represented by a C "int" or "long"; the + :py:class:`int`). As with shape, the values may + be larger than can be represented by a C ``int`` or ``long``; the calling code should handle this appropriately, either by - raising an error, or by using :c:type:`Py_LONG_LONG` in C. The - default is :const:`None` which implies a C-style contiguous - memory buffer. In this model, the last dimension of the array + raising an error, or by using ``long long`` in C. The + default is ``None`` which implies a C-style contiguous + memory buffer. In this model, the last dimension of the array varies the fastest. For example, the default strides tuple for an object whose array entries are 8 bytes long and whose - shape is (10,20,30) would be (4800, 240, 8) + shape is ``(10, 20, 30)`` would be ``(4800, 240, 8)`` - **Default**: :const:`None` (C-style contiguous) + **Default**: ``None`` (C-style contiguous) **mask** (optional) - - :const:`None` or an object exposing the array interface. All + None or an object exposing the array interface. All elements of the mask array should be interpreted only as true or not true indicating which elements of this array are valid. The shape of this object should be `"broadcastable" <arrays.broadcasting.broadcastable>` to the shape of the original array. - **Default**: :const:`None` (All array values are valid) + **Default**: None (All array values are valid) **offset** (optional) - An integer offset into the array data region. This can only be - used when data is :const:`None` or returns a :class:`buffer` + used when data is ``None`` or returns a :class:`buffer` object. **Default**: 0. **version** (required) - An integer showing the version of the interface (i.e. 3 for this version). Be careful not to use this to invalidate objects exposing future versions of the interface. @@ -197,11 +189,11 @@ C-struct access This approach to the array interface allows for faster access to an array using only one attribute lookup and a well-defined C-structure. -.. c:var:: __array_struct__ +.. data:: object.__array_struct__ - A :c:type: `PyCObject` whose :c:data:`voidptr` member contains a + A :c:type:`PyCapsule` whose ``pointer`` member contains a pointer to a filled :c:type:`PyArrayInterface` structure. Memory - for the structure is dynamically created and the :c:type:`PyCObject` + for the structure is dynamically created and the :c:type:`PyCapsule` is also created with an appropriate destructor so the retriever of this attribute simply has to apply :c:func:`Py_DECREF()` to the object returned by this attribute when it is finished. Also, @@ -211,7 +203,7 @@ array using only one attribute lookup and a well-defined C-structure. must also not reallocate their memory if other objects are referencing them. -The PyArrayInterface structure is defined in ``numpy/ndarrayobject.h`` +The :c:type:`PyArrayInterface` structure is defined in ``numpy/ndarrayobject.h`` as:: typedef struct { @@ -231,29 +223,32 @@ as:: The flags member may consist of 5 bits showing how the data should be interpreted and one bit showing how the Interface should be -interpreted. The data-bits are :const:`CONTIGUOUS` (0x1), -:const:`FORTRAN` (0x2), :const:`ALIGNED` (0x100), :const:`NOTSWAPPED` -(0x200), and :const:`WRITEABLE` (0x400). A final flag -:const:`ARR_HAS_DESCR` (0x800) indicates whether or not this structure +interpreted. The data-bits are :c:macro:`NPY_ARRAY_C_CONTIGUOUS` (0x1), +:c:macro:`NPY_ARRAY_F_CONTIGUOUS` (0x2), :c:macro:`NPY_ARRAY_ALIGNED` (0x100), +:c:macro:`NPY_ARRAY_NOTSWAPPED` (0x200), and :c:macro:`NPY_ARRAY_WRITEABLE` (0x400). A final flag +:c:macro:`NPY_ARR_HAS_DESCR` (0x800) indicates whether or not this structure has the arrdescr field. The field should not be accessed unless this flag is present. + .. c:macro:: NPY_ARR_HAS_DESCR + .. admonition:: New since June 16, 2006: - In the past most implementations used the "desc" member of the - :c:type:`PyCObject` itself (do not confuse this with the "descr" member of + In the past most implementations used the ``desc`` member of the ``PyCObject`` + (now :c:type:`PyCapsule`) itself (do not confuse this with the "descr" member of the :c:type:`PyArrayInterface` structure above --- they are two separate things) to hold the pointer to the object exposing the interface. - This is now an explicit part of the interface. Be sure to own a - reference to the object when the :c:type:`PyCObject` is created using - :c:type:`PyCObject_FromVoidPtrAndDesc`. + This is now an explicit part of the interface. Be sure to take a + reference to the object and call :c:func:`PyCapsule_SetContext` before + returning the :c:type:`PyCapsule`, and configure a destructor to decref this + reference. Type description examples ========================= For clarity it is useful to provide some examples of the type -description and corresponding :data:`__array_interface__` 'descr' +description and corresponding :data:`~object.__array_interface__` 'descr' entries. Thanks to Scott Gilbert for these examples: In every case, the 'descr' key is optional, but of course provides @@ -315,25 +310,39 @@ largely aesthetic. In particular: 1. The PyArrayInterface structure had no descr member at the end (and therefore no flag ARR_HAS_DESCR) -2. The desc member of the PyCObject returned from __array_struct__ was +2. The ``context`` member of the :c:type:`PyCapsule` (formally the ``desc`` + member of the ``PyCObject``) returned from ``__array_struct__`` was not specified. Usually, it was the object exposing the array (so that a reference to it could be kept and destroyed when the - C-object was destroyed). Now it must be a tuple whose first - element is a string with "PyArrayInterface Version #" and whose - second element is the object exposing the array. + C-object was destroyed). It is now an explicit requirement that this field + be used in some way to hold a reference to the owning object. + + .. note:: + + Until August 2020, this said: + + Now it must be a tuple whose first element is a string with + "PyArrayInterface Version #" and whose second element is the object + exposing the array. + + This design was retracted almost immediately after it was proposed, in + <https://mail.python.org/pipermail/numpy-discussion/2006-June/020995.html>. + Despite 14 years of documentation to the contrary, at no point was it + valid to assume that ``__array_interface__`` capsules held this tuple + content. -3. The tuple returned from __array_interface__['data'] used to be a +3. The tuple returned from ``__array_interface__['data']`` used to be a hex-string (now it is an integer or a long integer). -4. There was no __array_interface__ attribute instead all of the keys - (except for version) in the __array_interface__ dictionary were +4. There was no ``__array_interface__`` attribute instead all of the keys + (except for version) in the ``__array_interface__`` dictionary were their own attribute: Thus to obtain the Python-side information you had to access separately the attributes: - * __array_data__ - * __array_shape__ - * __array_strides__ - * __array_typestr__ - * __array_descr__ - * __array_offset__ - * __array_mask__ + * ``__array_data__`` + * ``__array_shape__`` + * ``__array_strides__`` + * ``__array_typestr__`` + * ``__array_descr__`` + * ``__array_offset__`` + * ``__array_mask__`` diff --git a/doc/source/reference/arrays.ndarray.rst b/doc/source/reference/arrays.ndarray.rst index 4c8bbf66d5bd..0f703b4754de 100644 --- a/doc/source/reference/arrays.ndarray.rst +++ b/doc/source/reference/arrays.ndarray.rst @@ -1,15 +1,15 @@ +.. currentmodule:: numpy + .. _arrays.ndarray: ****************************************** The N-dimensional array (:class:`ndarray`) ****************************************** -.. currentmodule:: numpy - An :class:`ndarray` is a (usually fixed-size) multidimensional container of items of the same type and size. The number of dimensions and items in an array is defined by its :attr:`shape <ndarray.shape>`, -which is a :class:`tuple` of *N* positive integers that specify the +which is a :class:`tuple` of *N* non-negative integers that specify the sizes of each dimension. The type of items in the array is specified by a separate :ref:`data-type object (dtype) <arrays.dtypes>`, one of which is associated with each ndarray. @@ -37,7 +37,7 @@ objects implementing the :class:`buffer` or :ref:`array >>> x = np.array([[1, 2, 3], [4, 5, 6]], np.int32) >>> type(x) - <type 'numpy.ndarray'> + <class 'numpy.ndarray'> >>> x.shape (2, 3) >>> x.dtype @@ -47,6 +47,7 @@ objects implementing the :class:`buffer` or :ref:`array >>> # The element of x in the *second* row, *third* column, namely, 6. >>> x[1, 2] + 6 For example :ref:`slicing <arrays.indexing>` can produce views of the array: @@ -82,10 +83,12 @@ Indexing arrays Arrays can be indexed using an extended Python slicing syntax, ``array[selection]``. Similar syntax is also used for accessing -fields in a :ref:`structured array <arrays.dtypes.field>`. +fields in a :term:`structured data type`. .. seealso:: :ref:`Array Indexing <arrays.indexing>`. +.. _memory-layout: + Internal memory layout of an ndarray ==================================== @@ -127,9 +130,14 @@ strided scheme, and correspond to memory that can be *addressed* by the strides: where :math:`d_j` `= self.shape[j]`. Both the C and Fortran orders are :term:`contiguous`, *i.e.,* -:term:`single-segment`, memory layouts, in which every part of the +single-segment, memory layouts, in which every part of the memory block can be accessed by some combination of the indices. +.. note:: + + `Contiguous arrays` and `single-segment arrays` are synonymous + and are used interchangeably throughout the documentation. + While a C-style and Fortran-style contiguous array, which has the corresponding flags set, can be addressed with the above strides, the actual strides may be different. This can happen in two cases: @@ -143,21 +151,24 @@ different. This can happen in two cases: considered C-style and Fortran-style contiguous. Point 1. means that ``self`` and ``self.squeeze()`` always have the same -contiguity and :term:`aligned` flags value. This also means that even a high -dimensional array could be C-style and Fortran-style contiguous at the same -time. +contiguity and ``aligned`` flags value. This also means +that even a high dimensional array could be C-style and Fortran-style +contiguous at the same time. .. index:: aligned An array is considered aligned if the memory offsets for all elements and the -base offset itself is a multiple of `self.itemsize`. +base offset itself is a multiple of `self.itemsize`. Understanding +`memory-alignment` leads to better performance on most hardware. .. note:: - Points (1) and (2) are not yet applied by default. Beginning with - NumPy 1.8.0, they are applied consistently only if the environment - variable ``NPY_RELAXED_STRIDES_CHECKING=1`` was defined when NumPy - was built. Eventually this will become the default. + Points (1) and (2) can currently be disabled by the compile time + environmental variable ``NPY_RELAXED_STRIDES_CHECKING=0``, + which was the default before NumPy 1.10. + No users should have to do this. ``NPY_RELAXED_STRIDES_DEBUG=1`` + can be used to help find errors when incorrectly relying on the strides + in C-extension code (see below warning). You can check whether this option was enabled when your NumPy was built by looking at the value of ``np.ones((10,1), @@ -238,7 +249,6 @@ Other attributes ndarray.real ndarray.imag ndarray.flat - ndarray.ctypes .. _arrays.ndarray.array-interface: @@ -248,10 +258,10 @@ Array interface .. seealso:: :ref:`arrays.interface`. -========================== =================================== -:obj:`__array_interface__` Python-side of the array interface -:obj:`__array_struct__` C-side of the array interface -========================== =================================== +================================== =================================== +:obj:`~object.__array_interface__` Python-side of the array interface +:obj:`~object.__array_struct__` C-side of the array interface +================================== =================================== :mod:`ctypes` foreign function interface ---------------------------------------- @@ -326,7 +336,7 @@ Item selection and manipulation ------------------------------- For array methods that take an *axis* keyword, it defaults to -:const:`None`. If axis is *None*, then the array is treated as a 1-D +*None*. If axis is *None*, then the array is treated as a 1-D array. Any other value for *axis* represents the dimension along which the operation should proceed. @@ -368,6 +378,7 @@ Many of these methods take an argument named *axis*. In such cases, A 3-dimensional array of size 3 x 3 x 3, summed over each of its three axes + >>> x = np.arange(27).reshape((3,3,3)) >>> x array([[[ 0, 1, 2], [ 3, 4, 5], @@ -409,6 +420,7 @@ be performed. .. autosummary:: :toctree: generated/ + ndarray.max ndarray.argmax ndarray.min ndarray.argmin @@ -440,7 +452,7 @@ Each of the arithmetic operations (``+``, ``-``, ``*``, ``/``, ``//``, ``%``, ``divmod()``, ``**`` or ``pow()``, ``<<``, ``>>``, ``&``, ``^``, ``|``, ``~``) and the comparisons (``==``, ``<``, ``>``, ``<=``, ``>=``, ``!=``) is equivalent to the corresponding -:term:`universal function` (or :term:`ufunc` for short) in NumPy. For +universal function (or :term:`ufunc` for short) in NumPy. For more information, see the section on :ref:`Universal Functions <ufuncs>`. @@ -456,17 +468,17 @@ Comparison operators: ndarray.__eq__ ndarray.__ne__ -Truth value of an array (:func:`bool()`): +Truth value of an array (:class:`bool() <bool>`): .. autosummary:: :toctree: generated/ - ndarray.__nonzero__ + ndarray.__bool__ .. note:: Truth-value testing of an array invokes - :meth:`ndarray.__nonzero__`, which raises an error if the number of + :meth:`ndarray.__bool__`, which raises an error if the number of elements in the array is larger than 1, because the truth value of such arrays is ambiguous. Use :meth:`.any() <ndarray.any>` and :meth:`.all() <ndarray.all>` instead to be clear about what is meant @@ -492,7 +504,6 @@ Arithmetic: ndarray.__add__ ndarray.__sub__ ndarray.__mul__ - ndarray.__div__ ndarray.__truediv__ ndarray.__floordiv__ ndarray.__mod__ @@ -509,15 +520,11 @@ Arithmetic: - Any third argument to :func:`pow()` is silently ignored, as the underlying :func:`ufunc <power>` takes only two arguments. - - The three division operators are all defined; :obj:`div` is active - by default, :obj:`truediv` is active when - :obj:`__future__` division is in effect. - - Because :class:`ndarray` is a built-in type (written in C), the ``__r{op}__`` special methods are not directly defined. - The functions called to implement many arithmetic special methods - for arrays can be modified using :func:`set_numeric_ops`. + for arrays can be modified using :class:`__array_ufunc__ <numpy.class.__array_ufunc__>`. Arithmetic, in-place: @@ -527,7 +534,6 @@ Arithmetic, in-place: ndarray.__iadd__ ndarray.__isub__ ndarray.__imul__ - ndarray.__idiv__ ndarray.__itruediv__ ndarray.__ifloordiv__ ndarray.__imod__ @@ -560,10 +566,8 @@ Matrix Multiplication: .. note:: Matrix operators ``@`` and ``@=`` were introduced in Python 3.5 - following PEP465. NumPy 1.10.0 has a preliminary implementation of ``@`` - for testing purposes. Further documentation can be found in the - :func:`matmul` documentation. - + following :pep:`465`, and the ``@`` operator has been introduced in NumPy + 1.10.0. Further information can be found in the :func:`matmul` documentation. Special methods =============== @@ -597,19 +601,17 @@ Container customization: (see :ref:`Indexing <arrays.indexing>`) ndarray.__setitem__ ndarray.__contains__ -Conversion; the operations :func:`complex()`, :func:`int()`, -:func:`long()`, :func:`float()`, :func:`oct()`, and -:func:`hex()`. They work only on arrays that have one element in them +Conversion; the operations :class:`int() <int>`, +:class:`float() <float>` and :class:`complex() <complex>`. +They work only on arrays that have one element in them and return the appropriate scalar. .. autosummary:: :toctree: generated/ ndarray.__int__ - ndarray.__long__ ndarray.__float__ - ndarray.__oct__ - ndarray.__hex__ + ndarray.__complex__ String representations: @@ -618,3 +620,10 @@ String representations: ndarray.__str__ ndarray.__repr__ + +Utility method for typing: + +.. autosummary:: + :toctree: generated/ + + ndarray.__class_getitem__ diff --git a/doc/source/reference/arrays.nditer.cython.rst b/doc/source/reference/arrays.nditer.cython.rst new file mode 100644 index 000000000000..43aad99275c7 --- /dev/null +++ b/doc/source/reference/arrays.nditer.cython.rst @@ -0,0 +1,147 @@ +Putting the Inner Loop in Cython +================================ + +Those who want really good performance out of their low level operations +should strongly consider directly using the iteration API provided +in C, but for those who are not comfortable with C or C++, Cython +is a good middle ground with reasonable performance tradeoffs. For +the :class:`~numpy.nditer` object, this means letting the iterator take care +of broadcasting, dtype conversion, and buffering, while giving the inner +loop to Cython. + +For our example, we'll create a sum of squares function. To start, +let's implement this function in straightforward Python. We want to +support an 'axis' parameter similar to the numpy :func:`sum` function, +so we will need to construct a list for the `op_axes` parameter. +Here's how this looks. + +.. admonition:: Example + + >>> def axis_to_axeslist(axis, ndim): + ... if axis is None: + ... return [-1] * ndim + ... else: + ... if type(axis) is not tuple: + ... axis = (axis,) + ... axeslist = [1] * ndim + ... for i in axis: + ... axeslist[i] = -1 + ... ax = 0 + ... for i in range(ndim): + ... if axeslist[i] != -1: + ... axeslist[i] = ax + ... ax += 1 + ... return axeslist + ... + >>> def sum_squares_py(arr, axis=None, out=None): + ... axeslist = axis_to_axeslist(axis, arr.ndim) + ... it = np.nditer([arr, out], flags=['reduce_ok', + ... 'buffered', 'delay_bufalloc'], + ... op_flags=[['readonly'], ['readwrite', 'allocate']], + ... op_axes=[None, axeslist], + ... op_dtypes=['float64', 'float64']) + ... with it: + ... it.operands[1][...] = 0 + ... it.reset() + ... for x, y in it: + ... y[...] += x*x + ... return it.operands[1] + ... + >>> a = np.arange(6).reshape(2,3) + >>> sum_squares_py(a) + array(55.0) + >>> sum_squares_py(a, axis=-1) + array([ 5., 50.]) + +To Cython-ize this function, we replace the inner loop (y[...] += x*x) with +Cython code that's specialized for the float64 dtype. With the +'external_loop' flag enabled, the arrays provided to the inner loop will +always be one-dimensional, so very little checking needs to be done. + +Here's the listing of sum_squares.pyx:: + + import numpy as np + cimport numpy as np + cimport cython + + def axis_to_axeslist(axis, ndim): + if axis is None: + return [-1] * ndim + else: + if type(axis) is not tuple: + axis = (axis,) + axeslist = [1] * ndim + for i in axis: + axeslist[i] = -1 + ax = 0 + for i in range(ndim): + if axeslist[i] != -1: + axeslist[i] = ax + ax += 1 + return axeslist + + @cython.boundscheck(False) + def sum_squares_cy(arr, axis=None, out=None): + cdef np.ndarray[double] x + cdef np.ndarray[double] y + cdef int size + cdef double value + + axeslist = axis_to_axeslist(axis, arr.ndim) + it = np.nditer([arr, out], flags=['reduce_ok', 'external_loop', + 'buffered', 'delay_bufalloc'], + op_flags=[['readonly'], ['readwrite', 'allocate']], + op_axes=[None, axeslist], + op_dtypes=['float64', 'float64']) + with it: + it.operands[1][...] = 0 + it.reset() + for xarr, yarr in it: + x = xarr + y = yarr + size = x.shape[0] + for i in range(size): + value = x[i] + y[i] = y[i] + value * value + return it.operands[1] + +On this machine, building the .pyx file into a module looked like the +following, but you may have to find some Cython tutorials to tell you +the specifics for your system configuration.:: + + $ cython sum_squares.pyx + $ gcc -shared -pthread -fPIC -fwrapv -O2 -Wall -I/usr/include/python2.7 -fno-strict-aliasing -o sum_squares.so sum_squares.c + +Running this from the Python interpreter produces the same answers +as our native Python/NumPy code did. + +.. admonition:: Example + + >>> from sum_squares import sum_squares_cy + >>> a = np.arange(6).reshape(2,3) + >>> sum_squares_cy(a) + array(55.0) + >>> sum_squares_cy(a, axis=-1) + array([ 5., 50.]) + +Doing a little timing in IPython shows that the reduced overhead and +memory allocation of the Cython inner loop is providing a very nice +speedup over both the straightforward Python code and an expression +using NumPy's built-in sum function.:: + + >>> a = np.random.rand(1000,1000) + + >>> timeit sum_squares_py(a, axis=-1) + 10 loops, best of 3: 37.1 ms per loop + + >>> timeit np.sum(a*a, axis=-1) + 10 loops, best of 3: 20.9 ms per loop + + >>> timeit sum_squares_cy(a, axis=-1) + 100 loops, best of 3: 11.8 ms per loop + + >>> np.all(sum_squares_cy(a, axis=-1) == np.sum(a*a, axis=-1)) + True + + >>> np.all(sum_squares_py(a, axis=-1) == np.sum(a*a, axis=-1)) + True diff --git a/doc/source/reference/arrays.nditer.rst b/doc/source/reference/arrays.nditer.rst index fa8183f7541a..72a04f73e8d1 100644 --- a/doc/source/reference/arrays.nditer.rst +++ b/doc/source/reference/arrays.nditer.rst @@ -1,11 +1,23 @@ .. currentmodule:: numpy +.. for doctests + The last section on Cython is 'included' at the end of this file. The tests + for that section are disabled. + .. _arrays.nditer: ********************* Iterating Over Arrays ********************* +.. note:: + + Arrays support the iterator protocol and can be iterated over like Python + lists. See the :ref:`quickstart.indexing-slicing-and-iterating` section in + the Quickstart guide for basic usage and examples. The remainder of + this document presents the :class:`nditer` object and covers more + advanced usage. + The iterator object :class:`nditer`, introduced in NumPy 1.6, provides many flexible ways to visit all the elements of one or more arrays in a systematic fashion. This page introduces some basic ways to use the @@ -115,13 +127,18 @@ context is exited. array([[ 0, 2, 4], [ 6, 8, 10]]) +If you are writing code that needs to support older versions of numpy, +note that prior to 1.15, :class:`nditer` was not a context manager and +did not have a `close` method. Instead it relied on the destructor to +initiate the writeback of the buffer. + Using an External Loop ---------------------- In all the examples so far, the elements of `a` are provided by the iterator one at a time, because all the looping logic is internal to the -iterator. While this is simple and convenient, it is not very efficient. A -better approach is to move the one-dimensional innermost loop into your +iterator. While this is simple and convenient, it is not very efficient. +A better approach is to move the one-dimensional innermost loop into your code, external to the iterator. This way, NumPy's vectorized operations can be used on larger chunks of the elements being visited. @@ -156,41 +173,29 @@ element in a computation. For example, you may want to visit the elements of an array in memory order, but use a C-order, Fortran-order, or multidimensional index to look up values in a different array. -The Python iterator protocol doesn't have a natural way to query these -additional values from the iterator, so we introduce an alternate syntax -for iterating with an :class:`nditer`. This syntax explicitly works -with the iterator object itself, so its properties are readily accessible -during iteration. With this looping construct, the current value is -accessible by indexing into the iterator, and the index being tracked -is the property `index` or `multi_index` depending on what was requested. - -The Python interactive interpreter unfortunately prints out the -values of expressions inside the while loop during each iteration of the -loop. We have modified the output in the examples using this looping -construct in order to be more readable. +The index is tracked by the iterator object itself, and accessible +through the `index` or `multi_index` properties, depending on what was +requested. The examples below show printouts demonstrating the +progression of the index: .. admonition:: Example >>> a = np.arange(6).reshape(2,3) >>> it = np.nditer(a, flags=['f_index']) - >>> while not it.finished: - ... print("%d <%d>" % (it[0], it.index), end=' ') - ... it.iternext() + >>> for x in it: + ... print("%d <%d>" % (x, it.index), end=' ') ... 0 <0> 1 <2> 2 <4> 3 <1> 4 <3> 5 <5> >>> it = np.nditer(a, flags=['multi_index']) - >>> while not it.finished: - ... print("%d <%s>" % (it[0], it.multi_index), end=' ') - ... it.iternext() + >>> for x in it: + ... print("%d <%s>" % (x, it.multi_index), end=' ') ... 0 <(0, 0)> 1 <(0, 1)> 2 <(0, 2)> 3 <(1, 0)> 4 <(1, 1)> 5 <(1, 2)> - >>> it = np.nditer(a, flags=['multi_index'], op_flags=['writeonly']) - >>> with it: - .... while not it.finished: - ... it[0] = it.multi_index[1] - it.multi_index[0] - ... it.iternext() + >>> with np.nditer(a, flags=['multi_index'], op_flags=['writeonly']) as it: + ... for x in it: + ... x[...] = it.multi_index[1] - it.multi_index[0] ... >>> a array([[ 0, 1, 2], @@ -199,7 +204,7 @@ construct in order to be more readable. Tracking an index or multi-index is incompatible with using an external loop, because it requires a different index value per element. If you try to combine these flags, the :class:`nditer` object will -raise an exception +raise an exception. .. admonition:: Example @@ -209,6 +214,42 @@ raise an exception File "<stdin>", line 1, in <module> ValueError: Iterator flag EXTERNAL_LOOP cannot be used if an index or multi-index is being tracked +Alternative Looping and Element Access +-------------------------------------- + +To make its properties more readily accessible during iteration, +:class:`nditer` has an alternative syntax for iterating, which works +explicitly with the iterator object itself. With this looping construct, +the current value is accessible by indexing into the iterator. Other +properties, such as tracked indices remain as before. The examples below +produce identical results to the ones in the previous section. + +.. admonition:: Example + + >>> a = np.arange(6).reshape(2,3) + >>> it = np.nditer(a, flags=['f_index']) + >>> while not it.finished: + ... print("%d <%d>" % (it[0], it.index), end=' ') + ... is_not_finished = it.iternext() + ... + 0 <0> 1 <2> 2 <4> 3 <1> 4 <3> 5 <5> + + >>> it = np.nditer(a, flags=['multi_index']) + >>> while not it.finished: + ... print("%d <%s>" % (it[0], it.multi_index), end=' ') + ... is_not_finished = it.iternext() + ... + 0 <(0, 0)> 1 <(0, 1)> 2 <(0, 2)> 3 <(1, 0)> 4 <(1, 1)> 5 <(1, 2)> + + >>> with np.nditer(a, flags=['multi_index'], op_flags=['writeonly']) as it: + ... while not it.finished: + ... it[0] = it.multi_index[1] - it.multi_index[0] + ... is_not_finished = it.iternext() + ... + >>> a + array([[ 0, 1, 2], + [-1, 0, 1]]) + Buffering the Array Elements ---------------------------- @@ -287,12 +328,13 @@ specified as an iterator flag. ... op_dtypes=['complex128']): ... print(np.sqrt(x), end=' ') ... - 1.73205080757j 1.41421356237j 1j 0j (1+0j) (1.41421356237+0j) + 1.7320508075688772j 1.4142135623730951j 1j 0j (1+0j) (1.4142135623730951+0j) >>> for x in np.nditer(a, flags=['buffered'], op_dtypes=['complex128']): ... print(np.sqrt(x), end=' ') ... - 1.73205080757j 1.41421356237j 1j 0j (1+0j) (1.41421356237+0j) + 1.7320508075688772j 1.4142135623730951j 1j 0j (1+0j) (1.4142135623730951+0j) + The iterator uses NumPy's casting rules to determine whether a specific conversion is permitted. By default, it enforces 'safe' casting. This means, @@ -376,8 +418,8 @@ which includes the input shapes to help diagnose the problem. ... print("%d:%d" % (x,y), end=' ') ... Traceback (most recent call last): - File "<stdin>", line 1, in <module> - ValueError: operands could not be broadcast together with shapes (2) (2,3) + ... + ValueError: operands could not be broadcast together with shapes (2,) (2,3) Iterator-Allocated Output Arrays -------------------------------- @@ -453,9 +495,9 @@ reasons. >>> square(np.arange(6).reshape(2,3), out=b) Traceback (most recent call last): - File "<stdin>", line 1, in <module> - File "<stdin>", line 4, in square - ValueError: non-broadcastable output operand with shape (3) doesn't match the broadcast shape (2,3) + ... + ValueError: non-broadcastable output operand with shape (3,) doesn't + match the broadcast shape (2,3) Outer Product Iteration ----------------------- @@ -521,7 +563,7 @@ For a simple example, consider taking the sum of all elements in an array. >>> a = np.arange(24).reshape(2,3,4) >>> b = np.array(0) - >>> with np.nditer([a, b], flags=['reduce_ok', 'external_loop'], + >>> with np.nditer([a, b], flags=['reduce_ok'], ... op_flags=[['readonly'], ['readwrite']]) as it: ... for x,y in it: ... y[...] += x @@ -539,7 +581,7 @@ sums along the last axis of `a`. .. admonition:: Example >>> a = np.arange(24).reshape(2,3,4) - >>> it = np.nditer([a, None], flags=['reduce_ok', 'external_loop'], + >>> it = np.nditer([a, None], flags=['reduce_ok'], ... op_flags=[['readonly'], ['readwrite', 'allocate']], ... op_axes=[None, [0,1,-1]]) >>> with it: @@ -573,7 +615,7 @@ buffering. .. admonition:: Example >>> a = np.arange(24).reshape(2,3,4) - >>> it = np.nditer([a, None], flags=['reduce_ok', 'external_loop', + >>> it = np.nditer([a, None], flags=['reduce_ok', ... 'buffered', 'delay_bufalloc'], ... op_flags=[['readonly'], ['readwrite', 'allocate']], ... op_axes=[None, [0,1,-1]]) @@ -588,150 +630,8 @@ buffering. array([[ 6, 22, 38], [54, 70, 86]]) -Putting the Inner Loop in Cython -================================ - -Those who want really good performance out of their low level operations -should strongly consider directly using the iteration API provided -in C, but for those who are not comfortable with C or C++, Cython -is a good middle ground with reasonable performance tradeoffs. For -the :class:`nditer` object, this means letting the iterator take care -of broadcasting, dtype conversion, and buffering, while giving the inner -loop to Cython. - -For our example, we'll create a sum of squares function. To start, -let's implement this function in straightforward Python. We want to -support an 'axis' parameter similar to the numpy :func:`sum` function, -so we will need to construct a list for the `op_axes` parameter. -Here's how this looks. - -.. admonition:: Example - - >>> def axis_to_axeslist(axis, ndim): - ... if axis is None: - ... return [-1] * ndim - ... else: - ... if type(axis) is not tuple: - ... axis = (axis,) - ... axeslist = [1] * ndim - ... for i in axis: - ... axeslist[i] = -1 - ... ax = 0 - ... for i in range(ndim): - ... if axeslist[i] != -1: - ... axeslist[i] = ax - ... ax += 1 - ... return axeslist - ... - >>> def sum_squares_py(arr, axis=None, out=None): - ... axeslist = axis_to_axeslist(axis, arr.ndim) - ... it = np.nditer([arr, out], flags=['reduce_ok', 'external_loop', - ... 'buffered', 'delay_bufalloc'], - ... op_flags=[['readonly'], ['readwrite', 'allocate']], - ... op_axes=[None, axeslist], - ... op_dtypes=['float64', 'float64']) - ... with it: - ... it.operands[1][...] = 0 - ... it.reset() - ... for x, y in it: - ... y[...] += x*x - ... return it.operands[1] - ... - >>> a = np.arange(6).reshape(2,3) - >>> sum_squares_py(a) - array(55.0) - >>> sum_squares_py(a, axis=-1) - array([ 5., 50.]) - -To Cython-ize this function, we replace the inner loop (y[...] += x*x) with -Cython code that's specialized for the float64 dtype. With the -'external_loop' flag enabled, the arrays provided to the inner loop will -always be one-dimensional, so very little checking needs to be done. - -Here's the listing of sum_squares.pyx:: - - import numpy as np - cimport numpy as np - cimport cython - - def axis_to_axeslist(axis, ndim): - if axis is None: - return [-1] * ndim - else: - if type(axis) is not tuple: - axis = (axis,) - axeslist = [1] * ndim - for i in axis: - axeslist[i] = -1 - ax = 0 - for i in range(ndim): - if axeslist[i] != -1: - axeslist[i] = ax - ax += 1 - return axeslist - - @cython.boundscheck(False) - def sum_squares_cy(arr, axis=None, out=None): - cdef np.ndarray[double] x - cdef np.ndarray[double] y - cdef int size - cdef double value - - axeslist = axis_to_axeslist(axis, arr.ndim) - it = np.nditer([arr, out], flags=['reduce_ok', 'external_loop', - 'buffered', 'delay_bufalloc'], - op_flags=[['readonly'], ['readwrite', 'allocate']], - op_axes=[None, axeslist], - op_dtypes=['float64', 'float64']) - with it: - it.operands[1][...] = 0 - it.reset() - for xarr, yarr in it: - x = xarr - y = yarr - size = x.shape[0] - for i in range(size): - value = x[i] - y[i] = y[i] + value * value - return it.operands[1] - -On this machine, building the .pyx file into a module looked like the -following, but you may have to find some Cython tutorials to tell you -the specifics for your system configuration.:: - - $ cython sum_squares.pyx - $ gcc -shared -pthread -fPIC -fwrapv -O2 -Wall -I/usr/include/python2.7 -fno-strict-aliasing -o sum_squares.so sum_squares.c - -Running this from the Python interpreter produces the same answers -as our native Python/NumPy code did. - -.. admonition:: Example - - >>> from sum_squares import sum_squares_cy - >>> a = np.arange(6).reshape(2,3) - >>> sum_squares_cy(a) - array(55.0) - >>> sum_squares_cy(a, axis=-1) - array([ 5., 50.]) - -Doing a little timing in IPython shows that the reduced overhead and -memory allocation of the Cython inner loop is providing a very nice -speedup over both the straightforward Python code and an expression -using NumPy's built-in sum function.:: - - >>> a = np.random.rand(1000,1000) - - >>> timeit sum_squares_py(a, axis=-1) - 10 loops, best of 3: 37.1 ms per loop - - >>> timeit np.sum(a*a, axis=-1) - 10 loops, best of 3: 20.9 ms per loop - - >>> timeit sum_squares_cy(a, axis=-1) - 100 loops, best of 3: 11.8 ms per loop - - >>> np.all(sum_squares_cy(a, axis=-1) == np.sum(a*a, axis=-1)) - True +.. for doctests + Include Cython section separately. Those tests are skipped entirely via an + entry in RST_SKIPLIST - >>> np.all(sum_squares_py(a, axis=-1) == np.sum(a*a, axis=-1)) - True +.. include:: arrays.nditer.cython.rst diff --git a/doc/source/reference/arrays.rst b/doc/source/reference/arrays.rst index faa91a389562..497dd9cd6f51 100644 --- a/doc/source/reference/arrays.rst +++ b/doc/source/reference/arrays.rst @@ -11,7 +11,7 @@ NumPy provides an N-dimensional array type, the :ref:`ndarray type. The items can be :ref:`indexed <arrays.indexing>` using for example N integers. -All ndarrays are :term:`homogenous`: every item takes up the same size +All ndarrays are :term:`homogeneous`: every item takes up the same size block of memory, and all blocks are interpreted in exactly the same way. How each item in the array is to be interpreted is specified by a separate :ref:`data-type object <arrays.dtypes>`, one of which is associated diff --git a/doc/source/reference/arrays.scalars.rst b/doc/source/reference/arrays.scalars.rst index 9c4f05f75b39..c691e802ffd0 100644 --- a/doc/source/reference/arrays.scalars.rst +++ b/doc/source/reference/arrays.scalars.rst @@ -24,14 +24,14 @@ mixing scalar and array operations. Array scalars live in a hierarchy (see the Figure below) of data types. They can be detected using the hierarchy: For example, -``isinstance(val, np.generic)`` will return :const:`True` if *val* is +``isinstance(val, np.generic)`` will return :py:data:`True` if *val* is an array scalar object. Alternatively, what kind of array scalar is present can be determined using other members of the data type hierarchy. Thus, for example ``isinstance(val, np.complexfloating)`` -will return :const:`True` if *val* is a complex valued type, while -:const:`isinstance(val, np.flexible)` will return true if *val* is one -of the flexible itemsize array types (:class:`string`, -:class:`unicode`, :class:`void`). +will return :py:data:`True` if *val* is a complex valued type, while +``isinstance(val, np.flexible)`` will return true if *val* is one +of the flexible itemsize array types (:class:`str_`, +:class:`bytes_`, :class:`void`). .. figure:: figures/dtype-hierarchy.png @@ -41,6 +41,13 @@ of the flexible itemsize array types (:class:`string`, pointer for the platform. All the number types can be obtained using bit-width names as well. + +.. TODO - use something like this instead of the diagram above, as it generates + links to the classes and is a vector graphic. Unfortunately it looks worse + and the html <map> element providing the linked regions is misaligned. + + .. inheritance-diagram:: byte short intc int_ longlong ubyte ushort uintc uint ulonglong half single double longdouble csingle cdouble clongdouble bool_ datetime64 timedelta64 object_ bytes_ str_ void + .. [#] However, array scalars are immutable, so none of the array scalar attributes are settable. @@ -51,43 +58,33 @@ of the flexible itemsize array types (:class:`string`, Built-in scalar types ===================== -The built-in scalar types are shown below. Along with their (mostly) -C-derived names, the integer, float, and complex data-types are also -available using a bit-width convention so that an array of the right -size can always be ensured (e.g. :class:`int8`, :class:`float64`, -:class:`complex128`). Two aliases (:class:`intp` and :class:`uintp`) -pointing to the integer type that is sufficiently large to hold a C pointer -are also provided. The C-like names are associated with character codes, -which are shown in the table. Use of the character codes, however, +The built-in scalar types are shown below. The C-like names are associated with character codes, +which are shown in their descriptions. Use of the character codes, however, is discouraged. Some of the scalar types are essentially equivalent to fundamental Python types and therefore inherit from them as well as from the generic array scalar type: -==================== ================================ -Array scalar type Related Python type -==================== ================================ -:class:`int_` :class:`IntType` (Python 2 only) -:class:`float_` :class:`FloatType` -:class:`complex_` :class:`ComplexType` -:class:`bytes_` :class:`BytesType` -:class:`unicode_` :class:`UnicodeType` -==================== ================================ +==================== =========================== ============= +Array scalar type Related Python type Inherits? +==================== =========================== ============= +:class:`int_` :class:`int` Python 2 only +:class:`float_` :class:`float` yes +:class:`complex_` :class:`complex` yes +:class:`bytes_` :class:`bytes` yes +:class:`str_` :class:`str` yes +:class:`bool_` :class:`bool` no +:class:`datetime64` :class:`datetime.datetime` no +:class:`timedelta64` :class:`datetime.timedelta` no +==================== =========================== ============= The :class:`bool_` data type is very similar to the Python -:class:`BooleanType` but does not inherit from it because Python's -:class:`BooleanType` does not allow itself to be inherited from, and +:class:`bool` but does not inherit from it because Python's +:class:`bool` does not allow itself to be inherited from, and on the C-level the size of the actual bool data is not the same as a Python Boolean scalar. -.. warning:: - - The :class:`bool_` type is not a subclass of the :class:`int_` type - (the :class:`bool_` is not even a number type). This is different - than Python's default implementation of :class:`bool` as a - sub-class of int. - .. warning:: The :class:`int_` type does **not** inherit from the @@ -96,88 +93,185 @@ Python Boolean scalar. .. tip:: The default data type in NumPy is :class:`float_`. -In the tables below, ``platform?`` means that the type may not be -available on all platforms. Compatibility with different C or Python -types is indicated: two types are compatible if their data is of the -same size and interpreted in the same way. - -Booleans: - -=================== ============================= =============== -Type Remarks Character code -=================== ============================= =============== -:class:`bool_` compatible: Python bool ``'?'`` -:class:`bool8` 8 bits -=================== ============================= =============== - -Integers: - -=================== ============================= =============== -:class:`byte` compatible: C char ``'b'`` -:class:`short` compatible: C short ``'h'`` -:class:`intc` compatible: C int ``'i'`` -:class:`int_` compatible: Python int ``'l'`` -:class:`longlong` compatible: C long long ``'q'`` -:class:`intp` large enough to fit a pointer ``'p'`` -:class:`int8` 8 bits -:class:`int16` 16 bits -:class:`int32` 32 bits -:class:`int64` 64 bits -=================== ============================= =============== - -Unsigned integers: - -=================== ============================= =============== -:class:`ubyte` compatible: C unsigned char ``'B'`` -:class:`ushort` compatible: C unsigned short ``'H'`` -:class:`uintc` compatible: C unsigned int ``'I'`` -:class:`uint` compatible: Python int ``'L'`` -:class:`ulonglong` compatible: C long long ``'Q'`` -:class:`uintp` large enough to fit a pointer ``'P'`` -:class:`uint8` 8 bits -:class:`uint16` 16 bits -:class:`uint32` 32 bits -:class:`uint64` 64 bits -=================== ============================= =============== - -Floating-point numbers: - -=================== ============================= =============== -:class:`half` ``'e'`` -:class:`single` compatible: C float ``'f'`` -:class:`double` compatible: C double -:class:`float_` compatible: Python float ``'d'`` -:class:`longfloat` compatible: C long float ``'g'`` -:class:`float16` 16 bits -:class:`float32` 32 bits -:class:`float64` 64 bits -:class:`float96` 96 bits, platform? -:class:`float128` 128 bits, platform? -=================== ============================= =============== - -Complex floating-point numbers: - -=================== ============================= =============== -:class:`csingle` ``'F'`` -:class:`complex_` compatible: Python complex ``'D'`` -:class:`clongfloat` ``'G'`` -:class:`complex64` two 32-bit floats -:class:`complex128` two 64-bit floats -:class:`complex192` two 96-bit floats, - platform? -:class:`complex256` two 128-bit floats, - platform? -=================== ============================= =============== - -Any Python object: - -=================== ============================= =============== -:class:`object_` any Python object ``'O'`` -=================== ============================= =============== +.. autoclass:: numpy.generic + :members: __init__ + :exclude-members: __init__ + +.. autoclass:: numpy.number + :members: __init__ + :exclude-members: __init__ + +Integer types +~~~~~~~~~~~~~ + +.. autoclass:: numpy.integer + :members: __init__ + :exclude-members: __init__ + +.. note:: + + The numpy integer types mirror the behavior of C integers, and can therefore + be subject to :ref:`overflow-errors`. + +Signed integer types +++++++++++++++++++++ + +.. autoclass:: numpy.signedinteger + :members: __init__ + :exclude-members: __init__ + +.. autoclass:: numpy.byte + :members: __init__ + :exclude-members: __init__ + +.. autoclass:: numpy.short + :members: __init__ + :exclude-members: __init__ + +.. autoclass:: numpy.intc + :members: __init__ + :exclude-members: __init__ + +.. autoclass:: numpy.int_ + :members: __init__ + :exclude-members: __init__ + +.. autoclass:: numpy.longlong + :members: __init__ + :exclude-members: __init__ + +Unsigned integer types +++++++++++++++++++++++ + +.. autoclass:: numpy.unsignedinteger + :members: __init__ + :exclude-members: __init__ + +.. autoclass:: numpy.ubyte + :members: __init__ + :exclude-members: __init__ + +.. autoclass:: numpy.ushort + :members: __init__ + :exclude-members: __init__ + +.. autoclass:: numpy.uintc + :members: __init__ + :exclude-members: __init__ + +.. autoclass:: numpy.uint + :members: __init__ + :exclude-members: __init__ + +.. autoclass:: numpy.ulonglong + :members: __init__ + :exclude-members: __init__ + +Inexact types +~~~~~~~~~~~~~ + +.. autoclass:: numpy.inexact + :members: __init__ + :exclude-members: __init__ .. note:: - The data actually stored in :term:`object arrays <object array>` + Inexact scalars are printed using the fewest decimal digits needed to + distinguish their value from other values of the same datatype, + by judicious rounding. See the ``unique`` parameter of + `format_float_positional` and `format_float_scientific`. + + This means that variables with equal binary values but whose datatypes are of + different precisions may display differently:: + + >>> f16 = np.float16("0.1") + >>> f32 = np.float32(f16) + >>> f64 = np.float64(f32) + >>> f16 == f32 == f64 + True + >>> f16, f32, f64 + (0.1, 0.099975586, 0.0999755859375) + + Note that none of these floats hold the exact value :math:`\frac{1}{10}`; + ``f16`` prints as ``0.1`` because it is as close to that value as possible, + whereas the other types do not as they have more precision and therefore have + closer values. + + Conversely, floating-point scalars of different precisions which approximate + the same decimal value may compare unequal despite printing identically: + + >>> f16 = np.float16("0.1") + >>> f32 = np.float32("0.1") + >>> f64 = np.float64("0.1") + >>> f16 == f32 == f64 + False + >>> f16, f32, f64 + (0.1, 0.1, 0.1) + +Floating-point types +++++++++++++++++++++ + +.. autoclass:: numpy.floating + :members: __init__ + :exclude-members: __init__ + +.. autoclass:: numpy.half + :members: __init__ + :exclude-members: __init__ + +.. autoclass:: numpy.single + :members: __init__ + :exclude-members: __init__ + +.. autoclass:: numpy.double + :members: __init__ + :exclude-members: __init__ + +.. autoclass:: numpy.longdouble + :members: __init__ + :exclude-members: __init__ + +Complex floating-point types +++++++++++++++++++++++++++++ + +.. autoclass:: numpy.complexfloating + :members: __init__ + :exclude-members: __init__ + +.. autoclass:: numpy.csingle + :members: __init__ + :exclude-members: __init__ + +.. autoclass:: numpy.cdouble + :members: __init__ + :exclude-members: __init__ + +.. autoclass:: numpy.clongdouble + :members: __init__ + :exclude-members: __init__ + +Other types +~~~~~~~~~~~ + +.. autoclass:: numpy.bool_ + :members: __init__ + :exclude-members: __init__ + +.. autoclass:: numpy.datetime64 + :members: __init__ + :exclude-members: __init__ + +.. autoclass:: numpy.timedelta64 + :members: __init__ + :exclude-members: __init__ + +.. autoclass:: numpy.object_ + :members: __init__ + :exclude-members: __init__ + +.. note:: + + The data actually stored in object arrays (*i.e.*, arrays having dtype :class:`object_`) are references to Python objects, not the objects themselves. Hence, object arrays behave more like usual Python :class:`lists <list>`, in the sense @@ -188,16 +282,28 @@ Any Python object: on item access, but instead returns the actual object that the array item refers to. -The following data types are :term:`flexible`. They have no predefined -size: the data they describe can be of different length in different +.. index:: flexible + +The following data types are **flexible**: they have no predefined +size and the data they describe can be of different length in different arrays. (In the character codes ``#`` is an integer denoting how many elements the data type consists of.) -=================== ============================== ======== -:class:`bytes_` compatible: Python bytes ``'S#'`` -:class:`unicode_` compatible: Python unicode/str ``'U#'`` -:class:`void` ``'V#'`` -=================== ============================== ======== +.. autoclass:: numpy.flexible + :members: __init__ + :exclude-members: __init__ + +.. autoclass:: numpy.bytes_ + :members: __init__ + :exclude-members: __init__ + +.. autoclass:: numpy.str_ + :members: __init__ + :exclude-members: __init__ + +.. autoclass:: numpy.void + :members: __init__ + :exclude-members: __init__ .. warning:: @@ -212,12 +318,125 @@ elements the data type consists of.) convention more consistent with other Python modules such as the :mod:`struct` module. +.. _sized-aliases: + +Sized aliases +~~~~~~~~~~~~~ + +Along with their (mostly) +C-derived names, the integer, float, and complex data-types are also +available using a bit-width convention so that an array of the right +size can always be ensured. Two aliases (:class:`numpy.intp` and :class:`numpy.uintp`) +pointing to the integer type that is sufficiently large to hold a C pointer +are also provided. + +.. note that these are documented with ..attribute because that is what + autoclass does for aliases under the hood. + +.. autoclass:: numpy.bool8 + +.. attribute:: int8 + int16 + int32 + int64 + + Aliases for the signed integer types (one of `numpy.byte`, `numpy.short`, + `numpy.intc`, `numpy.int_` and `numpy.longlong`) with the specified number + of bits. + + Compatible with the C99 ``int8_t``, ``int16_t``, ``int32_t``, and + ``int64_t``, respectively. + +.. attribute:: uint8 + uint16 + uint32 + uint64 + + Alias for the unsigned integer types (one of `numpy.ubyte`, `numpy.ushort`, + `numpy.uintc`, `numpy.uint` and `numpy.ulonglong`) with the specified number + of bits. + + Compatible with the C99 ``uint8_t``, ``uint16_t``, ``uint32_t``, and + ``uint64_t``, respectively. + +.. attribute:: intp + + Alias for the signed integer type (one of `numpy.byte`, `numpy.short`, + `numpy.intc`, `numpy.int_` and `np.longlong`) that is the same size as a + pointer. + + Compatible with the C ``intptr_t``. + + :Character code: ``'p'`` + +.. attribute:: uintp + + Alias for the unsigned integer type (one of `numpy.ubyte`, `numpy.ushort`, + `numpy.uintc`, `numpy.uint` and `np.ulonglong`) that is the same size as a + pointer. + + Compatible with the C ``uintptr_t``. + + :Character code: ``'P'`` + +.. autoclass:: numpy.float16 + +.. autoclass:: numpy.float32 + +.. autoclass:: numpy.float64 + +.. attribute:: float96 + float128 + + Alias for `numpy.longdouble`, named after its size in bits. + The existence of these aliases depends on the platform. + +.. autoclass:: numpy.complex64 + +.. autoclass:: numpy.complex128 + +.. attribute:: complex192 + complex256 + + Alias for `numpy.clongdouble`, named after its size in bits. + The existence of these aliases depends on the platform. + +Other aliases +~~~~~~~~~~~~~ + +The first two of these are conveniences which resemble the names of the +builtin types, in the same style as `bool_`, `int_`, `str_`, `bytes_`, and +`object_`: + +.. autoclass:: numpy.float_ + +.. autoclass:: numpy.complex_ + +Some more use alternate naming conventions for extended-precision floats and +complex numbers: + +.. autoclass:: numpy.longfloat + +.. autoclass:: numpy.singlecomplex + +.. autoclass:: numpy.cfloat + +.. autoclass:: numpy.longcomplex + +.. autoclass:: numpy.clongfloat + +The following aliases originate from Python 2, and it is recommended that they +not be used in new code. + +.. autoclass:: numpy.string_ + +.. autoclass:: numpy.unicode_ Attributes ========== The array scalar objects have an :obj:`array priority -<__array_priority__>` of :c:data:`NPY_SCALAR_PRIORITY` +<class.__array_priority__>` of :c:data:`NPY_SCALAR_PRIORITY` (-1,000,000.0). They also do not (yet) have a :attr:`ctypes <ndarray.ctypes>` attribute. Otherwise, they share the same attributes as arrays: @@ -271,7 +490,6 @@ The exceptions to the above rules are given below: .. autosummary:: :toctree: generated/ - generic generic.__array__ generic.__array_wrap__ generic.squeeze @@ -280,6 +498,13 @@ The exceptions to the above rules are given below: generic.__setstate__ generic.setflags +Utility method for typing: + +.. autosummary:: + :toctree: generated/ + + number.__class_getitem__ + Defining new types ================== diff --git a/doc/source/reference/c-api.array.rst b/doc/source/reference/c-api/array.rst similarity index 82% rename from doc/source/reference/c-api.array.rst rename to doc/source/reference/c-api/array.rst index 5ea7bfcfc0ae..bb440582548c 100644 --- a/doc/source/reference/c-api.array.rst +++ b/doc/source/reference/c-api/array.rst @@ -20,27 +20,44 @@ Array API Array structure and data access ------------------------------- -These macros all access the :c:type:`PyArrayObject` structure members. The input -argument, arr, can be any :c:type:`PyObject *<PyObject>` that is directly interpretable -as a :c:type:`PyArrayObject *` (any instance of the :c:data:`PyArray_Type` and its -sub-types). +These macros access the :c:type:`PyArrayObject` structure members and are +defined in ``ndarraytypes.h``. The input argument, *arr*, can be any +:c:expr:`PyObject *` that is directly interpretable as a +:c:expr:`PyArrayObject *` (any instance of the :c:data:`PyArray_Type` +and its sub-types). .. c:function:: int PyArray_NDIM(PyArrayObject *arr) The number of dimensions in the array. -.. c:function:: npy_intp *PyArray_DIMS(PyArrayObject *arr) +.. c:function:: int PyArray_FLAGS(PyArrayObject* arr) - Returns a pointer to the dimensions/shape of the array. The - number of elements matches the number of dimensions - of the array. + Returns an integer representing the :ref:`array-flags<array-flags>`. -.. c:function:: npy_intp *PyArray_SHAPE(PyArrayObject *arr) +.. c:function:: int PyArray_TYPE(PyArrayObject* arr) + + Return the (builtin) typenumber for the elements of this array. + +.. c:function:: int PyArray_SETITEM( \ + PyArrayObject* arr, void* itemptr, PyObject* obj) + + Convert obj and place it in the ndarray, *arr*, at the place + pointed to by itemptr. Return -1 if an error occurs or 0 on + success. + +.. c:function:: void PyArray_ENABLEFLAGS(PyArrayObject* arr, int flags) + + .. versionadded:: 1.7 + + Enables the specified array flags. This function does no validation, + and assumes that you know what you're doing. + +.. c:function:: void PyArray_CLEARFLAGS(PyArrayObject* arr, int flags) .. versionadded:: 1.7 - A synonym for PyArray_DIMS, named to be consistent with the - 'shape' usage within Python. + Clears the specified array flags. This function does no validation, + and assumes that you know what you're doing. .. c:function:: void *PyArray_DATA(PyArrayObject *arr) @@ -53,6 +70,19 @@ sub-types). array then be sure you understand how to access the data in the array to avoid memory and/or alignment problems. +.. c:function:: npy_intp *PyArray_DIMS(PyArrayObject *arr) + + Returns a pointer to the dimensions/shape of the array. The + number of elements matches the number of dimensions + of the array. Can return ``NULL`` for 0-dimensional arrays. + +.. c:function:: npy_intp *PyArray_SHAPE(PyArrayObject *arr) + + .. versionadded:: 1.7 + + A synonym for :c:func:`PyArray_DIMS`, named to be consistent with the + `shape <numpy.ndarray.shape>` usage within Python. + .. c:function:: npy_intp *PyArray_STRIDES(PyArrayObject* arr) Returns a pointer to the strides of the array. The @@ -67,6 +97,27 @@ sub-types). Return the stride in the *n* :math:`^{\textrm{th}}` dimension. +.. c:function:: npy_intp PyArray_ITEMSIZE(PyArrayObject* arr) + + Return the itemsize for the elements of this array. + + Note that, in the old API that was deprecated in version 1.7, this function + had the return type ``int``. + +.. c:function:: npy_intp PyArray_SIZE(PyArrayObject* arr) + + Returns the total size (in number of elements) of the array. + +.. c:function:: npy_intp PyArray_Size(PyArrayObject* obj) + + Returns 0 if *obj* is not a sub-class of ndarray. Otherwise, + returns the total number of elements in the array. Safer version + of :c:func:`PyArray_SIZE` (*obj*). + +.. c:function:: npy_intp PyArray_NBYTES(PyArrayObject* arr) + + Returns the total number of bytes consumed by the array. + .. c:function:: PyObject *PyArray_BASE(PyArrayObject* arr) This returns the base object of the array. In most cases, this @@ -93,58 +144,22 @@ sub-types). A synonym for PyArray_DESCR, named to be consistent with the 'dtype' usage within Python. -.. c:function:: void PyArray_ENABLEFLAGS(PyArrayObject* arr, int flags) - - .. versionadded:: 1.7 - - Enables the specified array flags. This function does no validation, - and assumes that you know what you're doing. - -.. c:function:: void PyArray_CLEARFLAGS(PyArrayObject* arr, int flags) - - .. versionadded:: 1.7 - - Clears the specified array flags. This function does no validation, - and assumes that you know what you're doing. - -.. c:function:: int PyArray_FLAGS(PyArrayObject* arr) - -.. c:function:: npy_intp PyArray_ITEMSIZE(PyArrayObject* arr) - - Return the itemsize for the elements of this array. - - Note that, in the old API that was deprecated in version 1.7, this function - had the return type ``int``. - -.. c:function:: int PyArray_TYPE(PyArrayObject* arr) - - Return the (builtin) typenumber for the elements of this array. - .. c:function:: PyObject *PyArray_GETITEM(PyArrayObject* arr, void* itemptr) - Get a Python object from the ndarray, *arr*, at the location - pointed to by itemptr. Return ``NULL`` on failure. + Get a Python object of a builtin type from the ndarray, *arr*, + at the location pointed to by itemptr. Return ``NULL`` on failure. -.. c:function:: int PyArray_SETITEM( \ - PyArrayObject* arr, void* itemptr, PyObject* obj) - - Convert obj and place it in the ndarray, *arr*, at the place - pointed to by itemptr. Return -1 if an error occurs or 0 on - success. - -.. c:function:: npy_intp PyArray_SIZE(PyArrayObject* arr) - - Returns the total size (in number of elements) of the array. - -.. c:function:: npy_intp PyArray_Size(PyArrayObject* obj) - - Returns 0 if *obj* is not a sub-class of ndarray. Otherwise, - returns the total number of elements in the array. Safer version - of :c:func:`PyArray_SIZE` (*obj*). + `numpy.ndarray.item` is identical to PyArray_GETITEM. -.. c:function:: npy_intp PyArray_NBYTES(PyArrayObject* arr) +.. c:function:: int PyArray_FinalizeFunc(PyArrayObject* arr, PyObject* obj) - Returns the total number of bytes consumed by the array. + The function pointed to by the CObject + :obj:`~numpy.class.__array_finalize__`. + The first argument is the newly created sub-type. The second argument + (if not NULL) is the "parent" array (if the array was created using + slicing or some other operation where a clearly-distinguishable parent + is present). This routine can do anything it wants to. It should + return a -1 on error and 0 otherwise. Data access @@ -197,10 +212,11 @@ From scratch ^^^^^^^^^^^^ .. c:function:: PyObject* PyArray_NewFromDescr( \ - PyTypeObject* subtype, PyArray_Descr* descr, int nd, npy_intp* dims, \ - npy_intp* strides, void* data, int flags, PyObject* obj) + PyTypeObject* subtype, PyArray_Descr* descr, int nd, npy_intp const* dims, \ + npy_intp const* strides, void* data, int flags, PyObject* obj) - This function steals a reference to *descr*. + This function steals a reference to *descr*. The easiest way to get one + is using :c:func:`PyArray_DescrFromType`. This is the main array creation function. Most new arrays are created with this flexible function. @@ -214,11 +230,13 @@ From scratch :c:data:`&PyArray_Type<PyArray_Type>`, then *obj* is the object to pass to the :obj:`~numpy.class.__array_finalize__` method of the subclass. - If *data* is ``NULL``, then new memory will be allocated and *flags* - can be non-zero to indicate a Fortran-style contiguous array. If - *data* is not ``NULL``, then it is assumed to point to the memory + If *data* is ``NULL``, then new unitinialized memory will be allocated and + *flags* can be non-zero to indicate a Fortran-style contiguous array. Use + :c:func:`PyArray_FILLWBYTE` to initialize the memory. + + If *data* is not ``NULL``, then it is assumed to point to the memory to be used for the array and the *flags* argument is used as the - new flags for the array (except the state of :c:data:`NPY_OWNDATA`, + new flags for the array (except the state of :c:data:`NPY_ARRAY_OWNDATA`, :c:data:`NPY_ARRAY_WRITEBACKIFCOPY` and :c:data:`NPY_ARRAY_UPDATEIFCOPY` flags of the new array will be reset). @@ -230,6 +248,12 @@ From scratch provided *dims* and *strides* are copied into newly allocated dimension and strides arrays for the new array object. + :c:func:`PyArray_CheckStrides` can help verify non- ``NULL`` stride + information. + + If ``data`` is provided, it must stay alive for the life of the array. One + way to manage this is through :c:func:`PyArray_SetBaseObject` + .. c:function:: PyObject* PyArray_NewLikeArray( \ PyArrayObject* prototype, NPY_ORDER order, PyArray_Descr* descr, \ int subok) @@ -237,7 +261,6 @@ From scratch .. versionadded:: 1.6 This function steals a reference to *descr* if it is not NULL. - This array creation routine allows for the convenient creation of a new array matching an existing array's shapes and memory layout, possibly changing the layout and/or data type. @@ -255,8 +278,9 @@ From scratch base-class array. .. c:function:: PyObject* PyArray_New( \ - PyTypeObject* subtype, int nd, npy_intp* dims, int type_num, \ - npy_intp* strides, void* data, int itemsize, int flags, PyObject* obj) + PyTypeObject* subtype, int nd, npy_intp const* dims, int type_num, \ + npy_intp const* strides, void* data, int itemsize, int flags, \ + PyObject* obj) This is similar to :c:func:`PyArray_NewFromDescr` (...) except you specify the data-type descriptor with *type_num* and *itemsize*, @@ -277,38 +301,48 @@ From scratch are passed in they must be consistent with the dimensions, the itemsize, and the data of the array. -.. c:function:: PyObject* PyArray_SimpleNew(int nd, npy_intp* dims, int typenum) +.. c:function:: PyObject* PyArray_SimpleNew(int nd, npy_intp const* dims, int typenum) Create a new uninitialized array of type, *typenum*, whose size in - each of *nd* dimensions is given by the integer array, *dims*. - This function cannot be used to create a flexible-type array (no - itemsize given). + each of *nd* dimensions is given by the integer array, *dims*.The memory + for the array is uninitialized (unless typenum is :c:data:`NPY_OBJECT` + in which case each element in the array is set to NULL). The + *typenum* argument allows specification of any of the builtin + data-types such as :c:data:`NPY_FLOAT` or :c:data:`NPY_LONG`. The + memory for the array can be set to zero if desired using + :c:func:`PyArray_FILLWBYTE` (return_object, 0).This function cannot be + used to create a flexible-type array (no itemsize given). .. c:function:: PyObject* PyArray_SimpleNewFromData( \ - int nd, npy_intp* dims, int typenum, void* data) + int nd, npy_intp const* dims, int typenum, void* data) Create an array wrapper around *data* pointed to by the given pointer. The array flags will have a default that the data area is well-behaved and C-style contiguous. The shape of the array is given by the *dims* c-array of length *nd*. The data-type of the - array is indicated by *typenum*. + array is indicated by *typenum*. If data comes from another + reference-counted Python object, the reference count on this object + should be increased after the pointer is passed in, and the base member + of the returned ndarray should point to the Python object that owns + the data. This will ensure that the provided memory is not + freed while the returned array is in existence. .. c:function:: PyObject* PyArray_SimpleNewFromDescr( \ - int nd, npy_intp* dims, PyArray_Descr* descr) + int nd, npy_int const* dims, PyArray_Descr* descr) - This function steals a reference to *descr* if it is not NULL. + This function steals a reference to *descr*. - Create a new array with the provided data-type descriptor, *descr* - , of the shape determined by *nd* and *dims*. + Create a new array with the provided data-type descriptor, *descr*, + of the shape determined by *nd* and *dims*. -.. c:function:: PyArray_FILLWBYTE(PyObject* obj, int val) +.. c:function:: void PyArray_FILLWBYTE(PyObject* obj, int val) Fill the array pointed to by *obj* ---which must be a (subclass of) ndarray---with the contents of *val* (evaluated as a byte). This macro calls memset, so obj must be contiguous. .. c:function:: PyObject* PyArray_Zeros( \ - int nd, npy_intp* dims, PyArray_Descr* dtype, int fortran) + int nd, npy_intp const* dims, PyArray_Descr* dtype, int fortran) Construct a new *nd* -dimensional array with shape given by *dims* and data type given by *dtype*. If *fortran* is non-zero, then a @@ -317,13 +351,13 @@ From scratch corresponds to :c:type:`NPY_OBJECT` ). .. c:function:: PyObject* PyArray_ZEROS( \ - int nd, npy_intp* dims, int type_num, int fortran) + int nd, npy_intp const* dims, int type_num, int fortran) Macro form of :c:func:`PyArray_Zeros` which takes a type-number instead of a data-type object. .. c:function:: PyObject* PyArray_Empty( \ - int nd, npy_intp* dims, PyArray_Descr* dtype, int fortran) + int nd, npy_intp const* dims, PyArray_Descr* dtype, int fortran) Construct a new *nd* -dimensional array with shape given by *dims* and data type given by *dtype*. If *fortran* is non-zero, then a @@ -333,7 +367,7 @@ From scratch filled with :c:data:`Py_None`. .. c:function:: PyObject* PyArray_EMPTY( \ - int nd, npy_intp* dims, int typenum, int fortran) + int nd, npy_intp const* dims, int typenum, int fortran) Macro form of :c:func:`PyArray_Empty` which takes a type-number, *typenum*, instead of a data-type object. @@ -400,53 +434,47 @@ From other objects may be 0. Also, if *op* is not already an array (or does not expose the array interface), then a new array will be created (and filled from *op* using the sequence protocol). The new array will - have :c:data:`NPY_ARRAY_DEFAULT` as its flags member. The *context* argument - is passed to the :obj:`~numpy.class.__array__` method of *op* and is only used if - the array is constructed that way. Almost always this - parameter is ``NULL``. - - In versions 1.6 and earlier of NumPy, the following flags - did not have the ``_ARRAY_`` macro namespace in them. That form - of the constant names is deprecated in 1.7. + have :c:data:`NPY_ARRAY_DEFAULT` as its flags member. The *context* + argument is unused. - .. c:var:: NPY_ARRAY_C_CONTIGUOUS + .. c:macro:: NPY_ARRAY_C_CONTIGUOUS Make sure the returned array is C-style contiguous - .. c:var:: NPY_ARRAY_F_CONTIGUOUS + .. c:macro:: NPY_ARRAY_F_CONTIGUOUS Make sure the returned array is Fortran-style contiguous. - .. c:var:: NPY_ARRAY_ALIGNED + .. c:macro:: NPY_ARRAY_ALIGNED Make sure the returned array is aligned on proper boundaries for its data type. An aligned array has the data pointer and every strides factor as a multiple of the alignment factor for the data-type- descriptor. - .. c:var:: NPY_ARRAY_WRITEABLE + .. c:macro:: NPY_ARRAY_WRITEABLE Make sure the returned array can be written to. - .. c:var:: NPY_ARRAY_ENSURECOPY + .. c:macro:: NPY_ARRAY_ENSURECOPY Make sure a copy is made of *op*. If this flag is not present, data is not copied if it can be avoided. - .. c:var:: NPY_ARRAY_ENSUREARRAY + .. c:macro:: NPY_ARRAY_ENSUREARRAY Make sure the result is a base-class ndarray. By default, if *op* is an instance of a subclass of ndarray, an instance of that same subclass is returned. If this flag is set, an ndarray object will be returned instead. - .. c:var:: NPY_ARRAY_FORCECAST + .. c:macro:: NPY_ARRAY_FORCECAST Force a cast to the output type even if it cannot be done safely. Without this flag, a data cast will occur only if it can be done safely, otherwise an error is raised. - .. c:var:: NPY_ARRAY_WRITEBACKIFCOPY + .. c:macro:: NPY_ARRAY_WRITEBACKIFCOPY If *op* is already an array, but does not satisfy the requirements, then a copy is made (which will satisfy the @@ -459,62 +487,73 @@ From other objects will be made writeable again. If *op* is not writeable to begin with, or if it is not already an array, then an error is raised. - .. c:var:: NPY_ARRAY_UPDATEIFCOPY + .. c:macro:: NPY_ARRAY_UPDATEIFCOPY Deprecated. Use :c:data:`NPY_ARRAY_WRITEBACKIFCOPY`, which is similar. This flag "automatically" copies the data back when the returned array is deallocated, which is not supported in all python implementations. - .. c:var:: NPY_ARRAY_BEHAVED + .. c:macro:: NPY_ARRAY_BEHAVED :c:data:`NPY_ARRAY_ALIGNED` \| :c:data:`NPY_ARRAY_WRITEABLE` - .. c:var:: NPY_ARRAY_CARRAY + .. c:macro:: NPY_ARRAY_CARRAY :c:data:`NPY_ARRAY_C_CONTIGUOUS` \| :c:data:`NPY_ARRAY_BEHAVED` - .. c:var:: NPY_ARRAY_CARRAY_RO + .. c:macro:: NPY_ARRAY_CARRAY_RO :c:data:`NPY_ARRAY_C_CONTIGUOUS` \| :c:data:`NPY_ARRAY_ALIGNED` - .. c:var:: NPY_ARRAY_FARRAY + .. c:macro:: NPY_ARRAY_FARRAY :c:data:`NPY_ARRAY_F_CONTIGUOUS` \| :c:data:`NPY_ARRAY_BEHAVED` - .. c:var:: NPY_ARRAY_FARRAY_RO + .. c:macro:: NPY_ARRAY_FARRAY_RO :c:data:`NPY_ARRAY_F_CONTIGUOUS` \| :c:data:`NPY_ARRAY_ALIGNED` - .. c:var:: NPY_ARRAY_DEFAULT + .. c:macro:: NPY_ARRAY_DEFAULT :c:data:`NPY_ARRAY_CARRAY` - .. c:var:: NPY_ARRAY_IN_ARRAY +.. + dedented to allow internal linking, pending a refactoring - :c:data:`NPY_ARRAY_C_CONTIGUOUS` \| :c:data:`NPY_ARRAY_ALIGNED` +.. c:macro:: NPY_ARRAY_IN_ARRAY + + :c:data:`NPY_ARRAY_C_CONTIGUOUS` \| :c:data:`NPY_ARRAY_ALIGNED` - .. c:var:: NPY_ARRAY_IN_FARRAY + .. c:macro:: NPY_ARRAY_IN_FARRAY :c:data:`NPY_ARRAY_F_CONTIGUOUS` \| :c:data:`NPY_ARRAY_ALIGNED` - .. c:var:: NPY_OUT_ARRAY +.. c:macro:: NPY_OUT_ARRAY - :c:data:`NPY_ARRAY_C_CONTIGUOUS` \| :c:data:`NPY_ARRAY_WRITEABLE` \| - :c:data:`NPY_ARRAY_ALIGNED` + :c:data:`NPY_ARRAY_C_CONTIGUOUS` \| :c:data:`NPY_ARRAY_WRITEABLE` \| + :c:data:`NPY_ARRAY_ALIGNED` - .. c:var:: NPY_ARRAY_OUT_FARRAY +.. c:macro:: NPY_ARRAY_OUT_ARRAY + + :c:data:`NPY_ARRAY_C_CONTIGUOUS` \| :c:data:`NPY_ARRAY_ALIGNED` \| + :c:data:`NPY_ARRAY_WRITEABLE` + + .. c:macro:: NPY_ARRAY_OUT_FARRAY :c:data:`NPY_ARRAY_F_CONTIGUOUS` \| :c:data:`NPY_ARRAY_WRITEABLE` \| :c:data:`NPY_ARRAY_ALIGNED` - .. c:var:: NPY_ARRAY_INOUT_ARRAY +.. + dedented to allow internal linking, pending a refactoring - :c:data:`NPY_ARRAY_C_CONTIGUOUS` \| :c:data:`NPY_ARRAY_WRITEABLE` \| - :c:data:`NPY_ARRAY_ALIGNED` \| :c:data:`NPY_ARRAY_WRITEBACKIFCOPY` \| - :c:data:`NPY_ARRAY_UPDATEIFCOPY` +.. c:macro:: NPY_ARRAY_INOUT_ARRAY - .. c:var:: NPY_ARRAY_INOUT_FARRAY + :c:data:`NPY_ARRAY_C_CONTIGUOUS` \| :c:data:`NPY_ARRAY_WRITEABLE` \| + :c:data:`NPY_ARRAY_ALIGNED` \| :c:data:`NPY_ARRAY_WRITEBACKIFCOPY` \| + :c:data:`NPY_ARRAY_UPDATEIFCOPY` + + .. c:macro:: NPY_ARRAY_INOUT_FARRAY :c:data:`NPY_ARRAY_F_CONTIGUOUS` \| :c:data:`NPY_ARRAY_WRITEABLE` \| :c:data:`NPY_ARRAY_ALIGNED` \| :c:data:`NPY_ARRAY_WRITEBACKIFCOPY` \| @@ -525,66 +564,16 @@ From other objects PyArray_Descr** out_dtype, int* out_ndim, npy_intp* out_dims, \ PyArrayObject** out_arr, PyObject* context) - .. versionadded:: 1.6 + .. deprecated:: NumPy 1.19 - Retrieves the array parameters for viewing/converting an arbitrary - PyObject* to a NumPy array. This allows the "innate type and shape" - of Python list-of-lists to be discovered without - actually converting to an array. PyArray_FromAny calls this function - to analyze its input. - - In some cases, such as structured arrays and the :obj:`~numpy.class.__array__` interface, - a data type needs to be used to make sense of the object. When - this is needed, provide a Descr for 'requested_dtype', otherwise - provide NULL. This reference is not stolen. Also, if the requested - dtype doesn't modify the interpretation of the input, out_dtype will - still get the "innate" dtype of the object, not the dtype passed - in 'requested_dtype'. - - If writing to the value in 'op' is desired, set the boolean - 'writeable' to 1. This raises an error when 'op' is a scalar, list - of lists, or other non-writeable 'op'. This differs from passing - :c:data:`NPY_ARRAY_WRITEABLE` to PyArray_FromAny, where the writeable array may - be a copy of the input. - - When success (0 return value) is returned, either out_arr - is filled with a non-NULL PyArrayObject and - the rest of the parameters are untouched, or out_arr is - filled with NULL, and the rest of the parameters are filled. - - Typical usage: + Unless NumPy is made aware of an issue with this, this function + is scheduled for rapid removal without replacement. - .. code-block:: c + .. versionchanged:: NumPy 1.19 - PyArrayObject *arr = NULL; - PyArray_Descr *dtype = NULL; - int ndim = 0; - npy_intp dims[NPY_MAXDIMS]; + `context` is never used. Its use results in an error. - if (PyArray_GetArrayParamsFromObject(op, NULL, 1, &dtype, - &ndim, &dims, &arr, NULL) < 0) { - return NULL; - } - if (arr == NULL) { - ... validate/change dtype, validate flags, ndim, etc ... - // Could make custom strides here too - arr = PyArray_NewFromDescr(&PyArray_Type, dtype, ndim, - dims, NULL, - fortran ? NPY_ARRAY_F_CONTIGUOUS : 0, - NULL); - if (arr == NULL) { - return NULL; - } - if (PyArray_CopyObject(arr, op) < 0) { - Py_DECREF(arr); - return NULL; - } - } - else { - ... in this case the other parameters weren't filled, just - validate and possibly copy arr itself ... - } - ... use arr ... + .. versionadded:: 1.6 .. c:function:: PyObject* PyArray_CheckFromAny( \ PyObject* op, PyArray_Descr* dtype, int min_depth, int max_depth, \ @@ -600,7 +589,10 @@ From other objects did not have the _ARRAY_ macro namespace in them. That form of the constant names is deprecated in 1.7. -.. c:var:: NPY_ARRAY_NOTSWAPPED +.. + dedented to allow internal linking, pending a refactoring + +.. c:macro:: NPY_ARRAY_NOTSWAPPED Make sure the returned array has a data-type descriptor that is in machine byte-order, over-riding any specification in the *dtype* @@ -611,11 +603,15 @@ From other objects not in machine byte- order), then a new data-type descriptor is created and used with its byte-order field set to native. -.. c:var:: NPY_ARRAY_BEHAVED_NS + .. c:macro:: NPY_ARRAY_BEHAVED_NS - :c:data:`NPY_ARRAY_ALIGNED` \| :c:data:`NPY_ARRAY_WRITEABLE` \| :c:data:`NPY_ARRAY_NOTSWAPPED` + :c:data:`NPY_ARRAY_ALIGNED` \| :c:data:`NPY_ARRAY_WRITEABLE` \| + :c:data:`NPY_ARRAY_NOTSWAPPED` -.. c:var:: NPY_ARRAY_ELEMENTSTRIDES +.. + dedented to allow internal linking, pending a refactoring + +.. c:macro:: NPY_ARRAY_ELEMENTSTRIDES Make sure the returned array has strides that are multiples of the element size. @@ -630,14 +626,14 @@ From other objects .. c:function:: PyObject* PyArray_FromStructInterface(PyObject* op) Returns an ndarray object from a Python object that exposes the - :obj:`__array_struct__` attribute and follows the array interface + :obj:`~object.__array_struct__` attribute and follows the array interface protocol. If the object does not contain this attribute then a borrowed reference to :c:data:`Py_NotImplemented` is returned. .. c:function:: PyObject* PyArray_FromInterface(PyObject* op) Returns an ndarray object from a Python object that exposes the - :obj:`__array_interface__` attribute following the array interface + :obj:`~object.__array_interface__` attribute following the array interface protocol. If the object does not contain this attribute then a borrowed reference to :c:data:`Py_NotImplemented` is returned. @@ -645,10 +641,8 @@ From other objects PyObject* op, PyArray_Descr* dtype, PyObject* context) Return an ndarray object from a Python object that exposes the - :obj:`~numpy.class.__array__` method. The :obj:`~numpy.class.__array__` method can take 0, 1, or 2 - arguments ([dtype, context]) where *context* is used to pass - information about where the :obj:`~numpy.class.__array__` method is being called - from (currently only used in ufuncs). + :obj:`~numpy.class.__array__` method. The :obj:`~numpy.class.__array__` + method can take 0, or 1 argument ``([dtype])``. ``context`` is unused. .. c:function:: PyObject* PyArray_ContiguousFromAny( \ PyObject* op, int typenum, int min_depth, int max_depth) @@ -661,8 +655,17 @@ From other objects requirements set to :c:data:`NPY_ARRAY_DEFAULT` and the type_num member of the type argument set to *typenum*. -.. c:function:: PyObject *PyArray_FromObject( \ - PyObject *op, int typenum, int min_depth, int max_depth) +.. c:function:: PyObject* PyArray_ContiguousFromObject( \ + PyObject* op, int typenum, int min_depth, int max_depth) + + This function returns a well-behaved C-style contiguous array from any nested + sequence or array-interface exporting object. The minimum number of dimensions + the array can have is given by `min_depth` while the maximum is `max_depth`. + This is equivalent to call :c:func:`PyArray_FromAny` with requirements + :c:data:`NPY_ARRAY_DEFAULT` and :c:data:`NPY_ARRAY_ENSUREARRAY`. + +.. c:function:: PyObject* PyArray_FromObject( \ + PyObject* op, int typenum, int min_depth, int max_depth) Return an aligned and in native-byteorder array from any nested sequence or array-interface exporting object, op, of a type given by @@ -732,6 +735,13 @@ From other objects broadcastable to the shape of ``dest``. The data areas of dest and src must not overlap. +.. c:function:: int PyArray_CopyObject(PyArrayObject* dest, PyObject* src) + + Assign an object ``src`` to a NumPy array ``dest`` according to + array-coercion rules. This is basically identical to + :c:func:`PyArray_FromAny`, but assigns directly to the output array. + Returns 0 on success and -1 on failures. + .. c:function:: int PyArray_MoveInto(PyArrayObject* dest, PyArrayObject* src) Move data from the source array, ``src``, into the destination @@ -777,7 +787,7 @@ From other objects PyObject* obj, int typenum, int requirements) Combination of :c:func:`PyArray_FROM_OF` and :c:func:`PyArray_FROM_OT` - allowing both a *typenum* and a *flags* argument to be provided.. + allowing both a *typenum* and a *flags* argument to be provided. .. c:function:: PyObject* PyArray_FROMANY( \ PyObject* obj, int typenum, int min, int max, int requirements) @@ -809,17 +819,17 @@ Dealing with types General check of Python Type ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. c:function:: PyArray_Check(op) +.. c:function:: int PyArray_Check(PyObject *op) Evaluates true if *op* is a Python object whose type is a sub-type of :c:data:`PyArray_Type`. -.. c:function:: PyArray_CheckExact(op) +.. c:function:: int PyArray_CheckExact(PyObject *op) Evaluates true if *op* is a Python object with type :c:data:`PyArray_Type`. -.. c:function:: PyArray_HasArrayInterface(op, out) +.. c:function:: int PyArray_HasArrayInterface(PyObject *op, PyObject *out) If ``op`` implements any part of the array interface, then ``out`` will contain a new reference to the newly created ndarray using @@ -827,48 +837,50 @@ General check of Python Type conversion occurs. Otherwise, out will contain a borrowed reference to :c:data:`Py_NotImplemented` and no error condition is set. -.. c:function:: PyArray_HasArrayInterfaceType(op, type, context, out) +.. c:function:: int PyArray_HasArrayInterfaceType(\ + PyObject *op, PyArray_Descr *dtype, PyObject *context, PyObject *out) If ``op`` implements any part of the array interface, then ``out`` will contain a new reference to the newly created ndarray using the interface or ``out`` will contain ``NULL`` if an error during conversion occurs. Otherwise, out will contain a borrowed reference to Py_NotImplemented and no error condition is set. - This version allows setting of the type and context in the part of - the array interface that looks for the :obj:`~numpy.class.__array__` attribute. + This version allows setting of the dtype in the part of the array interface + that looks for the :obj:`~numpy.class.__array__` attribute. `context` is + unused. -.. c:function:: PyArray_IsZeroDim(op) +.. c:function:: int PyArray_IsZeroDim(PyObject *op) Evaluates true if *op* is an instance of (a subclass of) :c:data:`PyArray_Type` and has 0 dimensions. -.. c:function:: PyArray_IsScalar(op, cls) +.. c:macro:: PyArray_IsScalar(op, cls) - Evaluates true if *op* is an instance of :c:data:`Py{cls}ArrType_Type`. + Evaluates true if *op* is an instance of ``Py{cls}ArrType_Type``. -.. c:function:: PyArray_CheckScalar(op) +.. c:function:: int PyArray_CheckScalar(PyObject *op) Evaluates true if *op* is either an array scalar (an instance of a sub-type of :c:data:`PyGenericArr_Type` ), or an instance of (a sub-class of) :c:data:`PyArray_Type` whose dimensionality is 0. -.. c:function:: PyArray_IsPythonNumber(op) +.. c:function:: int PyArray_IsPythonNumber(PyObject *op) Evaluates true if *op* is an instance of a builtin numeric type (int, float, complex, long, bool) -.. c:function:: PyArray_IsPythonScalar(op) +.. c:function:: int PyArray_IsPythonScalar(PyObject *op) Evaluates true if *op* is a builtin Python scalar object (int, - float, complex, str, unicode, long, bool). + float, complex, bytes, str, long, bool). -.. c:function:: PyArray_IsAnyScalar(op) +.. c:function:: int PyArray_IsAnyScalar(PyObject *op) Evaluates true if *op* is either a Python scalar object (see :c:func:`PyArray_IsPythonScalar`) or an array scalar (an instance of a sub- type of :c:data:`PyGenericArr_Type` ). -.. c:function:: PyArray_CheckAnyScalar(op) +.. c:function:: int PyArray_CheckAnyScalar(PyObject *op) Evaluates true if *op* is a Python scalar object (see :c:func:`PyArray_IsPythonScalar`), an array scalar (an instance of a @@ -881,139 +893,143 @@ Data-type checking For the typenum macros, the argument is an integer representing an enumerated array data type. For the array type checking macros the -argument must be a :c:type:`PyObject *<PyObject>` that can be directly interpreted as a -:c:type:`PyArrayObject *`. +argument must be a :c:expr:`PyObject *` that can be directly interpreted as a +:c:expr:`PyArrayObject *`. -.. c:function:: PyTypeNum_ISUNSIGNED(num) +.. c:function:: int PyTypeNum_ISUNSIGNED(int num) -.. c:function:: PyDataType_ISUNSIGNED(descr) +.. c:function:: int PyDataType_ISUNSIGNED(PyArray_Descr *descr) -.. c:function:: PyArray_ISUNSIGNED(obj) +.. c:function:: int PyArray_ISUNSIGNED(PyArrayObject *obj) Type represents an unsigned integer. -.. c:function:: PyTypeNum_ISSIGNED(num) +.. c:function:: int PyTypeNum_ISSIGNED(int num) -.. c:function:: PyDataType_ISSIGNED(descr) +.. c:function:: int PyDataType_ISSIGNED(PyArray_Descr *descr) -.. c:function:: PyArray_ISSIGNED(obj) +.. c:function:: int PyArray_ISSIGNED(PyArrayObject *obj) Type represents a signed integer. -.. c:function:: PyTypeNum_ISINTEGER(num) +.. c:function:: int PyTypeNum_ISINTEGER(int num) -.. c:function:: PyDataType_ISINTEGER(descr) +.. c:function:: int PyDataType_ISINTEGER(PyArray_Descr* descr) -.. c:function:: PyArray_ISINTEGER(obj) +.. c:function:: int PyArray_ISINTEGER(PyArrayObject *obj) Type represents any integer. -.. c:function:: PyTypeNum_ISFLOAT(num) +.. c:function:: int PyTypeNum_ISFLOAT(int num) -.. c:function:: PyDataType_ISFLOAT(descr) +.. c:function:: int PyDataType_ISFLOAT(PyArray_Descr* descr) -.. c:function:: PyArray_ISFLOAT(obj) +.. c:function:: int PyArray_ISFLOAT(PyArrayObject *obj) Type represents any floating point number. -.. c:function:: PyTypeNum_ISCOMPLEX(num) +.. c:function:: int PyTypeNum_ISCOMPLEX(int num) -.. c:function:: PyDataType_ISCOMPLEX(descr) +.. c:function:: int PyDataType_ISCOMPLEX(PyArray_Descr* descr) -.. c:function:: PyArray_ISCOMPLEX(obj) +.. c:function:: int PyArray_ISCOMPLEX(PyArrayObject *obj) Type represents any complex floating point number. -.. c:function:: PyTypeNum_ISNUMBER(num) +.. c:function:: int PyTypeNum_ISNUMBER(int num) -.. c:function:: PyDataType_ISNUMBER(descr) +.. c:function:: int PyDataType_ISNUMBER(PyArray_Descr* descr) -.. c:function:: PyArray_ISNUMBER(obj) +.. c:function:: int PyArray_ISNUMBER(PyArrayObject *obj) Type represents any integer, floating point, or complex floating point number. -.. c:function:: PyTypeNum_ISSTRING(num) +.. c:function:: int PyTypeNum_ISSTRING(int num) -.. c:function:: PyDataType_ISSTRING(descr) +.. c:function:: int PyDataType_ISSTRING(PyArray_Descr* descr) -.. c:function:: PyArray_ISSTRING(obj) +.. c:function:: int PyArray_ISSTRING(PyArrayObject *obj) Type represents a string data type. -.. c:function:: PyTypeNum_ISPYTHON(num) +.. c:function:: int PyTypeNum_ISPYTHON(int num) -.. c:function:: PyDataType_ISPYTHON(descr) +.. c:function:: int PyDataType_ISPYTHON(PyArray_Descr* descr) -.. c:function:: PyArray_ISPYTHON(obj) +.. c:function:: int PyArray_ISPYTHON(PyArrayObject *obj) Type represents an enumerated type corresponding to one of the standard Python scalar (bool, int, float, or complex). -.. c:function:: PyTypeNum_ISFLEXIBLE(num) +.. c:function:: int PyTypeNum_ISFLEXIBLE(int num) -.. c:function:: PyDataType_ISFLEXIBLE(descr) +.. c:function:: int PyDataType_ISFLEXIBLE(PyArray_Descr* descr) -.. c:function:: PyArray_ISFLEXIBLE(obj) +.. c:function:: int PyArray_ISFLEXIBLE(PyArrayObject *obj) Type represents one of the flexible array types ( :c:data:`NPY_STRING`, :c:data:`NPY_UNICODE`, or :c:data:`NPY_VOID` ). -.. c:function:: PyDataType_ISUNSIZED(descr): +.. c:function:: int PyDataType_ISUNSIZED(PyArray_Descr* descr) Type has no size information attached, and can be resized. Should only be called on flexible dtypes. Types that are attached to an array will always be sized, hence the array form of this macro not existing. -.. c:function:: PyTypeNum_ISUSERDEF(num) + .. versionchanged:: 1.18 -.. c:function:: PyDataType_ISUSERDEF(descr) + For structured datatypes with no fields this function now returns False. -.. c:function:: PyArray_ISUSERDEF(obj) +.. c:function:: int PyTypeNum_ISUSERDEF(int num) + +.. c:function:: int PyDataType_ISUSERDEF(PyArray_Descr* descr) + +.. c:function:: int PyArray_ISUSERDEF(PyArrayObject *obj) Type represents a user-defined type. -.. c:function:: PyTypeNum_ISEXTENDED(num) +.. c:function:: int PyTypeNum_ISEXTENDED(int num) -.. c:function:: PyDataType_ISEXTENDED(descr) +.. c:function:: int PyDataType_ISEXTENDED(PyArray_Descr* descr) -.. c:function:: PyArray_ISEXTENDED(obj) +.. c:function:: int PyArray_ISEXTENDED(PyArrayObject *obj) Type is either flexible or user-defined. -.. c:function:: PyTypeNum_ISOBJECT(num) +.. c:function:: int PyTypeNum_ISOBJECT(int num) -.. c:function:: PyDataType_ISOBJECT(descr) +.. c:function:: int PyDataType_ISOBJECT(PyArray_Descr* descr) -.. c:function:: PyArray_ISOBJECT(obj) +.. c:function:: int PyArray_ISOBJECT(PyArrayObject *obj) Type represents object data type. -.. c:function:: PyTypeNum_ISBOOL(num) +.. c:function:: int PyTypeNum_ISBOOL(int num) -.. c:function:: PyDataType_ISBOOL(descr) +.. c:function:: int PyDataType_ISBOOL(PyArray_Descr* descr) -.. c:function:: PyArray_ISBOOL(obj) +.. c:function:: int PyArray_ISBOOL(PyArrayObject *obj) Type represents Boolean data type. -.. c:function:: PyDataType_HASFIELDS(descr) +.. c:function:: int PyDataType_HASFIELDS(PyArray_Descr* descr) -.. c:function:: PyArray_HASFIELDS(obj) +.. c:function:: int PyArray_HASFIELDS(PyArrayObject *obj) Type has fields associated with it. -.. c:function:: PyArray_ISNOTSWAPPED(m) +.. c:function:: int PyArray_ISNOTSWAPPED(PyArrayObject *m) Evaluates true if the data area of the ndarray *m* is in machine byte-order according to the array's data-type descriptor. -.. c:function:: PyArray_ISBYTESWAPPED(m) +.. c:function:: int PyArray_ISBYTESWAPPED(PyArrayObject *m) Evaluates true if the data area of the ndarray *m* is **not** in machine byte-order according to the array's data-type descriptor. -.. c:function:: Bool PyArray_EquivTypes( \ +.. c:function:: npy_bool PyArray_EquivTypes( \ PyArray_Descr* type1, PyArray_Descr* type2) Return :c:data:`NPY_TRUE` if *type1* and *type2* actually represent @@ -1022,20 +1038,20 @@ argument must be a :c:type:`PyObject *<PyObject>` that can be directly interpret :c:data:`NPY_LONG` and :c:data:`NPY_INT` are equivalent. Otherwise return :c:data:`NPY_FALSE`. -.. c:function:: Bool PyArray_EquivArrTypes( \ +.. c:function:: npy_bool PyArray_EquivArrTypes( \ PyArrayObject* a1, PyArrayObject * a2) Return :c:data:`NPY_TRUE` if *a1* and *a2* are arrays with equivalent types for this platform. -.. c:function:: Bool PyArray_EquivTypenums(int typenum1, int typenum2) +.. c:function:: npy_bool PyArray_EquivTypenums(int typenum1, int typenum2) Special case of :c:func:`PyArray_EquivTypes` (...) that does not accept flexible data types but may be easier to call. -.. c:function:: int PyArray_EquivByteorders({byteorder} b1, {byteorder} b2) +.. c:function:: int PyArray_EquivByteorders(int b1, int b2) - True if byteorder characters ( :c:data:`NPY_LITTLE`, + True if byteorder characters *b1* and *b2* ( :c:data:`NPY_LITTLE`, :c:data:`NPY_BIG`, :c:data:`NPY_NATIVE`, :c:data:`NPY_IGNORE` ) are either equal or equivalent as to their specification of a native byte order. Thus, on a little-endian machine :c:data:`NPY_LITTLE` @@ -1156,8 +1172,8 @@ Converting data types storing the max value of the input types converted to a string or unicode. .. c:function:: PyArray_Descr* PyArray_ResultType( \ - npy_intp narrs, PyArrayObject**arrs, npy_intp ndtypes, \ - PyArray_Descr**dtypes) + npy_intp narrs, PyArrayObject **arrs, npy_intp ndtypes, \ + PyArray_Descr **dtypes) .. versionadded:: 1.6 @@ -1187,7 +1203,7 @@ Converting data types .. c:function:: int PyArray_ObjectType(PyObject* op, int mintype) - This function is superceded by :c:func:`PyArray_MinScalarType` and/or + This function is superseded by :c:func:`PyArray_MinScalarType` and/or :c:func:`PyArray_ResultType`. This function is useful for determining a common type that two or @@ -1201,7 +1217,7 @@ Converting data types .. c:function:: void PyArray_ArrayType( \ PyObject* op, PyArray_Descr* mintype, PyArray_Descr* outtype) - This function is superceded by :c:func:`PyArray_ResultType`. + This function is superseded by :c:func:`PyArray_ResultType`. This function works similarly to :c:func:`PyArray_ObjectType` (...) except it handles flexible arrays. The *mintype* argument can have @@ -1212,21 +1228,25 @@ Converting data types .. c:function:: PyArrayObject** PyArray_ConvertToCommonType( \ PyObject* op, int* n) - The functionality this provides is largely superceded by iterator + The functionality this provides is largely superseded by iterator :c:type:`NpyIter` introduced in 1.6, with flag :c:data:`NPY_ITER_COMMON_DTYPE` or with the same dtype parameter for all operands. Convert a sequence of Python objects contained in *op* to an array of ndarrays each having the same data type. The type is selected - based on the typenumber (larger type number is chosen over a - smaller one) ignoring objects that are only scalars. The length of - the sequence is returned in *n*, and an *n* -length array of - :c:type:`PyArrayObject` pointers is the return value (or ``NULL`` if an - error occurs). The returned array must be freed by the caller of - this routine (using :c:func:`PyDataMem_FREE` ) and all the array objects - in it ``DECREF`` 'd or a memory-leak will occur. The example - template-code below shows a typically usage: + in the same way as `PyArray_ResultType`. The length of the sequence is + returned in *n*, and an *n* -length array of :c:type:`PyArrayObject` + pointers is the return value (or ``NULL`` if an error occurs). + The returned array must be freed by the caller of this routine + (using :c:func:`PyDataMem_FREE` ) and all the array objects in it + ``DECREF`` 'd or a memory-leak will occur. The example template-code + below shows a typically usage: + + .. versionchanged:: 1.18.0 + A mix of scalars and zero-dimensional arrays now produces a type + capable of holding the scalar value. + Previously priority was given to the dtype of the arrays. .. code-block:: c @@ -1259,8 +1279,8 @@ Converting data types function returns :c:data:`NPY_FALSE`. -New data types -^^^^^^^^^^^^^^ +User-defined data types +^^^^^^^^^^^^^^^^^^^^^^^ .. c:function:: void PyArray_InitArrFuncs(PyArray_ArrFuncs* f) @@ -1302,8 +1322,15 @@ New data types data-type object, *descr*, of the given *scalar* kind. Use *scalar* = :c:data:`NPY_NOSCALAR` to register that an array of data-type *descr* can be cast safely to a data-type whose type_number is - *totype*. + *totype*. The return value is 0 on success or -1 on failure. + +.. c:function:: int PyArray_TypeNumFromName( \ + char const *str) + Given a string return the type-number for the data-type with that string as + the type-object name. + Returns ``NPY_NOTYPE`` without setting an error if no type can be found. + Only works for user-defined data-types. Special functions for NPY_OBJECT ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -1344,7 +1371,7 @@ Special functions for NPY_OBJECT locations in the structure with object data-types. No checking is performed but *arr* must be of data-type :c:type:`NPY_OBJECT` and be single-segment and uninitialized (no previous objects in - position). Use :c:func:`PyArray_DECREF` (*arr*) if you need to + position). Use :c:func:`PyArray_XDECREF` (*arr*) if you need to decrement all the items in the object array prior to calling this function. @@ -1353,7 +1380,7 @@ Special functions for NPY_OBJECT Precondition: ``arr`` is a copy of ``base`` (though possibly with different strides, ordering, etc.) Set the UPDATEIFCOPY flag and ``arr->base`` so that when ``arr`` is destructed, it will copy any changes back to ``base``. - DEPRECATED, use :c:func:`PyArray_SetWritebackIfCopyBase``. + DEPRECATED, use :c:func:`PyArray_SetWritebackIfCopyBase`. Returns 0 for success, -1 for failure. @@ -1363,11 +1390,12 @@ Special functions for NPY_OBJECT strides, ordering, etc.) Sets the :c:data:`NPY_ARRAY_WRITEBACKIFCOPY` flag and ``arr->base``, and set ``base`` to READONLY. Call :c:func:`PyArray_ResolveWritebackIfCopy` before calling - `Py_DECREF`` in order copy any changes back to ``base`` and + `Py_DECREF` in order copy any changes back to ``base`` and reset the READONLY flag. Returns 0 for success, -1 for failure. +.. _array-flags: Array flags ----------- @@ -1398,7 +1426,7 @@ An ndarray can have a data segment that is not a simple contiguous chunk of well-behaved memory you can manipulate. It may not be aligned with word boundaries (very important on some platforms). It might have its data in a different byte-order than the machine recognizes. It -might not be writeable. It might be in Fortan-contiguous order. The +might not be writeable. It might be in Fortran-contiguous order. The array flags are used to indicate what can be said about data associated with an array. @@ -1406,12 +1434,12 @@ In versions 1.6 and earlier of NumPy, the following flags did not have the _ARRAY_ macro namespace in them. That form of the constant names is deprecated in 1.7. -.. c:var:: NPY_ARRAY_C_CONTIGUOUS +.. c:macro:: NPY_ARRAY_C_CONTIGUOUS The data area is in C-style contiguous order (last index varies the fastest). -.. c:var:: NPY_ARRAY_F_CONTIGUOUS +.. c:macro:: NPY_ARRAY_F_CONTIGUOUS The data area is in Fortran-style contiguous order (first index varies the fastest). @@ -1432,22 +1460,24 @@ of the constant names is deprecated in 1.7. .. seealso:: :ref:`Internal memory layout of an ndarray <arrays.ndarray>` -.. c:var:: NPY_ARRAY_OWNDATA +.. c:macro:: NPY_ARRAY_OWNDATA - The data area is owned by this array. + The data area is owned by this array. Should never be set manually, instead + create a ``PyObject`` wrapping the data and set the array's base to that + object. For an example, see the test in ``test_mem_policy``. -.. c:var:: NPY_ARRAY_ALIGNED +.. c:macro:: NPY_ARRAY_ALIGNED The data area and all array elements are aligned appropriately. -.. c:var:: NPY_ARRAY_WRITEABLE +.. c:macro:: NPY_ARRAY_WRITEABLE The data area can be written to. Notice that the above 3 flags are defined so that a new, well- behaved array has these flags defined as true. -.. c:var:: NPY_ARRAY_WRITEBACKIFCOPY +.. c:macro:: NPY_ARRAY_WRITEBACKIFCOPY The data area represents a (well-behaved) copy whose information should be transferred back to the original when @@ -1466,7 +1496,7 @@ of the constant names is deprecated in 1.7. would have returned an error because :c:data:`NPY_ARRAY_WRITEBACKIFCOPY` would not have been possible. -.. c:var:: NPY_ARRAY_UPDATEIFCOPY +.. c:macro:: NPY_ARRAY_UPDATEIFCOPY A deprecated version of :c:data:`NPY_ARRAY_WRITEBACKIFCOPY` which depends upon ``dealloc`` to trigger the writeback. For backwards @@ -1483,31 +1513,31 @@ for ``flags`` which can be any of :c:data:`NPY_ARRAY_C_CONTIGUOUS`, Combinations of array flags ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. c:var:: NPY_ARRAY_BEHAVED +.. c:macro:: NPY_ARRAY_BEHAVED :c:data:`NPY_ARRAY_ALIGNED` \| :c:data:`NPY_ARRAY_WRITEABLE` -.. c:var:: NPY_ARRAY_CARRAY +.. c:macro:: NPY_ARRAY_CARRAY :c:data:`NPY_ARRAY_C_CONTIGUOUS` \| :c:data:`NPY_ARRAY_BEHAVED` -.. c:var:: NPY_ARRAY_CARRAY_RO +.. c:macro:: NPY_ARRAY_CARRAY_RO :c:data:`NPY_ARRAY_C_CONTIGUOUS` \| :c:data:`NPY_ARRAY_ALIGNED` -.. c:var:: NPY_ARRAY_FARRAY +.. c:macro:: NPY_ARRAY_FARRAY :c:data:`NPY_ARRAY_F_CONTIGUOUS` \| :c:data:`NPY_ARRAY_BEHAVED` -.. c:var:: NPY_ARRAY_FARRAY_RO +.. c:macro:: NPY_ARRAY_FARRAY_RO :c:data:`NPY_ARRAY_F_CONTIGUOUS` \| :c:data:`NPY_ARRAY_ALIGNED` -.. c:var:: NPY_ARRAY_DEFAULT +.. c:macro:: NPY_ARRAY_DEFAULT :c:data:`NPY_ARRAY_CARRAY` -.. c:var:: NPY_ARRAY_UPDATE_ALL +.. c:macro:: NPY_ARRAY_UPDATE_ALL :c:data:`NPY_ARRAY_C_CONTIGUOUS` \| :c:data:`NPY_ARRAY_F_CONTIGUOUS` \| :c:data:`NPY_ARRAY_ALIGNED` @@ -1518,36 +1548,27 @@ Flag-like constants These constants are used in :c:func:`PyArray_FromAny` (and its macro forms) to specify desired properties of the new array. -.. c:var:: NPY_ARRAY_FORCECAST +.. c:macro:: NPY_ARRAY_FORCECAST Cast to the desired type, even if it can't be done without losing information. -.. c:var:: NPY_ARRAY_ENSURECOPY +.. c:macro:: NPY_ARRAY_ENSURECOPY Make sure the resulting array is a copy of the original. -.. c:var:: NPY_ARRAY_ENSUREARRAY +.. c:macro:: NPY_ARRAY_ENSUREARRAY Make sure the resulting object is an actual ndarray, and not a sub-class. -.. c:var:: NPY_ARRAY_NOTSWAPPED - - Only used in :c:func:`PyArray_CheckFromAny` to over-ride the byteorder - of the data-type object passed in. - -.. c:var:: NPY_ARRAY_BEHAVED_NS - - :c:data:`NPY_ARRAY_ALIGNED` \| :c:data:`NPY_ARRAY_WRITEABLE` \| :c:data:`NPY_ARRAY_NOTSWAPPED` - Flag checking ^^^^^^^^^^^^^ For all of these macros *arr* must be an instance of a (subclass of) -:c:data:`PyArray_Type`, but no checking is done. +:c:data:`PyArray_Type`. -.. c:function:: PyArray_CHKFLAGS(arr, flags) +.. c:function:: int PyArray_CHKFLAGS(PyObject *arr, int flags) The first parameter, arr, must be an ndarray or subclass. The parameter, *flags*, should be an integer consisting of bitwise @@ -1557,60 +1578,60 @@ For all of these macros *arr* must be an instance of a (subclass of) :c:data:`NPY_ARRAY_WRITEABLE`, :c:data:`NPY_ARRAY_WRITEBACKIFCOPY`, :c:data:`NPY_ARRAY_UPDATEIFCOPY`. -.. c:function:: PyArray_IS_C_CONTIGUOUS(arr) +.. c:function:: int PyArray_IS_C_CONTIGUOUS(PyObject *arr) Evaluates true if *arr* is C-style contiguous. -.. c:function:: PyArray_IS_F_CONTIGUOUS(arr) +.. c:function:: int PyArray_IS_F_CONTIGUOUS(PyObject *arr) Evaluates true if *arr* is Fortran-style contiguous. -.. c:function:: PyArray_ISFORTRAN(arr) +.. c:function:: int PyArray_ISFORTRAN(PyObject *arr) Evaluates true if *arr* is Fortran-style contiguous and *not* C-style contiguous. :c:func:`PyArray_IS_F_CONTIGUOUS` is the correct way to test for Fortran-style contiguity. -.. c:function:: PyArray_ISWRITEABLE(arr) +.. c:function:: int PyArray_ISWRITEABLE(PyObject *arr) Evaluates true if the data area of *arr* can be written to -.. c:function:: PyArray_ISALIGNED(arr) +.. c:function:: int PyArray_ISALIGNED(PyObject *arr) Evaluates true if the data area of *arr* is properly aligned on the machine. -.. c:function:: PyArray_ISBEHAVED(arr) +.. c:function:: int PyArray_ISBEHAVED(PyObject *arr) Evaluates true if the data area of *arr* is aligned and writeable and in machine byte-order according to its descriptor. -.. c:function:: PyArray_ISBEHAVED_RO(arr) +.. c:function:: int PyArray_ISBEHAVED_RO(PyObject *arr) Evaluates true if the data area of *arr* is aligned and in machine byte-order. -.. c:function:: PyArray_ISCARRAY(arr) +.. c:function:: int PyArray_ISCARRAY(PyObject *arr) Evaluates true if the data area of *arr* is C-style contiguous, and :c:func:`PyArray_ISBEHAVED` (*arr*) is true. -.. c:function:: PyArray_ISFARRAY(arr) +.. c:function:: int PyArray_ISFARRAY(PyObject *arr) Evaluates true if the data area of *arr* is Fortran-style contiguous and :c:func:`PyArray_ISBEHAVED` (*arr*) is true. -.. c:function:: PyArray_ISCARRAY_RO(arr) +.. c:function:: int PyArray_ISCARRAY_RO(PyObject *arr) Evaluates true if the data area of *arr* is C-style contiguous, aligned, and in machine byte-order. -.. c:function:: PyArray_ISFARRAY_RO(arr) +.. c:function:: int PyArray_ISFARRAY_RO(PyObject *arr) Evaluates true if the data area of *arr* is Fortran-style contiguous, aligned, and in machine byte-order **.** -.. c:function:: PyArray_ISONESEGMENT(arr) +.. c:function:: int PyArray_ISONESEGMENT(PyObject *arr) Evaluates true if the data area of *arr* consists of a single (C-style or Fortran-style) contiguous segment. @@ -1643,11 +1664,13 @@ Conversion .. c:function:: PyObject* PyArray_GetField( \ PyArrayObject* self, PyArray_Descr* dtype, int offset) - Equivalent to :meth:`ndarray.getfield<numpy.ndarray.getfield>` (*self*, *dtype*, *offset*). Return - a new array of the given *dtype* using the data in the current - array at a specified *offset* in bytes. The *offset* plus the - itemsize of the new array type must be less than *self* - ->descr->elsize or an error is raised. The same shape and strides + Equivalent to :meth:`ndarray.getfield<numpy.ndarray.getfield>` + (*self*, *dtype*, *offset*). This function `steals a reference + <https://docs.python.org/3/c-api/intro.html?reference-count-details>`_ + to `PyArray_Descr` and returns a new array of the given `dtype` using + the data in the current array at a specified `offset` in bytes. The + `offset` plus the itemsize of the new array type must be less than ``self + ->descr->elsize`` or an error is raised. The same shape and strides as the original array are used. Therefore, this function has the effect of returning a field from a structured array. But, it can also be used to select specific bytes or groups of bytes from any array @@ -1666,7 +1689,7 @@ Conversion destination must be an integer multiple of the number of elements in *val*. -.. c:function:: PyObject* PyArray_Byteswap(PyArrayObject* self, Bool inplace) +.. c:function:: PyObject* PyArray_Byteswap(PyArrayObject* self, npy_bool inplace) Equivalent to :meth:`ndarray.byteswap<numpy.ndarray.byteswap>` (*self*, *inplace*). Return an array whose data area is byteswapped. If *inplace* is non-zero, then do @@ -1883,36 +1906,37 @@ Item selection and manipulation created. The *clipmode* argument determines behavior for when entries in *self* are not between 0 and len(*op*). - .. c:var:: NPY_RAISE + .. c:macro:: NPY_RAISE raise a ValueError; - .. c:var:: NPY_WRAP + .. c:macro:: NPY_WRAP wrap values < 0 by adding len(*op*) and values >=len(*op*) by subtracting len(*op*) until they are in range; - .. c:var:: NPY_CLIP + .. c:macro:: NPY_CLIP all values are clipped to the region [0, len(*op*) ). -.. c:function:: PyObject* PyArray_Sort(PyArrayObject* self, int axis) +.. c:function:: PyObject* PyArray_Sort(PyArrayObject* self, int axis, NPY_SORTKIND kind) - Equivalent to :meth:`ndarray.sort<numpy.ndarray.sort>` (*self*, *axis*). Return an array with - the items of *self* sorted along *axis*. + Equivalent to :meth:`ndarray.sort<numpy.ndarray.sort>` (*self*, *axis*, *kind*). + Return an array with the items of *self* sorted along *axis*. The array + is sorted using the algorithm denoted by *kind*, which is an integer/enum pointing + to the type of sorting algorithms used. .. c:function:: PyObject* PyArray_ArgSort(PyArrayObject* self, int axis) - Equivalent to :meth:`ndarray.argsort<numpy.ndarray.argsort>` (*self*, *axis*). Return an array of - indices such that selection of these indices along the given - ``axis`` would return a sorted version of *self*. If *self* - ->descr is a data-type with fields defined, then - self->descr->names is used to determine the sort order. A - comparison where the first field is equal will use the second - field and so on. To alter the sort order of a structured array, create - a new data-type with a different order of names and construct a - view of the array with that new data-type. + Equivalent to :meth:`ndarray.argsort<numpy.ndarray.argsort>` (*self*, *axis*). + Return an array of indices such that selection of these indices + along the given ``axis`` would return a sorted version of *self*. If *self* ->descr + is a data-type with fields defined, then self->descr->names is used + to determine the sort order. A comparison where the first field is equal + will use the second field and so on. To alter the sort order of a + structured array, create a new data-type with a different order of names + and construct a view of the array with that new data-type. .. c:function:: PyObject* PyArray_LexSort(PyObject* sort_keys, int axis) @@ -2013,9 +2037,20 @@ Calculation .. tip:: Pass in :c:data:`NPY_MAXDIMS` for axis in order to achieve the same - effect that is obtained by passing in *axis* = :const:`None` in Python + effect that is obtained by passing in ``axis=None`` in Python (treating the array as a 1-d array). + +.. note:: + + The out argument specifies where to place the result. If out is + NULL, then the output array is created, otherwise the output is + placed in out which must be the correct size and type. A new + reference to the output array is always returned even when out + is not NULL. The caller of the routine has the responsibility + to ``Py_DECREF`` out if not NULL or a memory-leak will occur. + + .. c:function:: PyObject* PyArray_ArgMax( \ PyArrayObject* self, int axis, PyArrayObject* out) @@ -2028,18 +2063,6 @@ Calculation Equivalent to :meth:`ndarray.argmin<numpy.ndarray.argmin>` (*self*, *axis*). Return the index of the smallest element of *self* along *axis*. - - - -.. note:: - - The out argument specifies where to place the result. If out is - NULL, then the output array is created, otherwise the output is - placed in out which must be the correct size and type. A new - reference to the output array is always returned even when out - is not NULL. The caller of the routine has the responsibility - to ``DECREF`` out if not NULL or a memory-leak will occur. - .. c:function:: PyObject* PyArray_Max( \ PyArrayObject* self, int axis, PyArrayObject* out) @@ -2270,7 +2293,7 @@ Array Functions See the :func:`~numpy.einsum` function for more details. -.. c:function:: PyObject* PyArray_CopyAndTranspose(PyObject \* op) +.. c:function:: PyObject* PyArray_CopyAndTranspose(PyObject * op) A specialized copy and transpose function that works only for 2-d arrays. The returned array is a transposed copy of *op*. @@ -2325,9 +2348,9 @@ Array Functions Other functions ^^^^^^^^^^^^^^^ -.. c:function:: Bool PyArray_CheckStrides( \ - int elsize, int nd, npy_intp numbytes, npy_intp* dims, \ - npy_intp* newstrides) +.. c:function:: npy_bool PyArray_CheckStrides( \ + int elsize, int nd, npy_intp numbytes, npy_intp const* dims, \ + npy_intp const* newstrides) Determine if *newstrides* is a strides array consistent with the memory of an *nd* -dimensional array with shape ``dims`` and @@ -2339,14 +2362,14 @@ Other functions *elsize* refer to a single-segment array. Return :c:data:`NPY_TRUE` if *newstrides* is acceptable, otherwise return :c:data:`NPY_FALSE`. -.. c:function:: npy_intp PyArray_MultiplyList(npy_intp* seq, int n) +.. c:function:: npy_intp PyArray_MultiplyList(npy_intp const* seq, int n) -.. c:function:: int PyArray_MultiplyIntList(int* seq, int n) +.. c:function:: int PyArray_MultiplyIntList(int const* seq, int n) Both of these routines multiply an *n* -length array, *seq*, of integers and return the result. No overflow checking is performed. -.. c:function:: int PyArray_CompareLists(npy_intp* l1, npy_intp* l2, int n) +.. c:function:: int PyArray_CompareLists(npy_intp const* l1, npy_intp const* l2, int n) Given two *n* -length arrays of integers, *l1*, and *l2*, return 1 if the lists are identical; otherwise, return 0. @@ -2368,7 +2391,9 @@ it is possible to do this. Defining an :c:type:`NpyAuxData` is similar to defining a class in C++, but the object semantics have to be tracked manually since the API is in C. Here's an example for a function which doubles up an element using -an element copier function as a primitive.:: +an element copier function as a primitive. + +.. code-block:: c typedef struct { NpyAuxData base; @@ -2432,12 +2457,12 @@ an element copier function as a primitive.:: functions should never set the Python exception on error, because they may be called from a multi-threaded context. -.. c:function:: NPY_AUXDATA_FREE(auxdata) +.. c:function:: void NPY_AUXDATA_FREE(NpyAuxData *auxdata) A macro which calls the auxdata's free function appropriately, does nothing if auxdata is NULL. -.. c:function:: NPY_AUXDATA_CLONE(auxdata) +.. c:function:: NpyAuxData *NPY_AUXDATA_CLONE(NpyAuxData *auxdata) A macro which calls the auxdata's clone function appropriately, returning a deep copy of the auxiliary data. @@ -2445,7 +2470,7 @@ an element copier function as a primitive.:: Array Iterators --------------- -As of NumPy 1.6.0, these array iterators are superceded by +As of NumPy 1.6.0, these array iterators are superseded by the new array iterator, :c:type:`NpyIter`. An array iterator is a simple way to access the elements of an @@ -2460,7 +2485,7 @@ this useful approach to looping over an array. it easy to loop over an N-dimensional non-contiguous array in C-style contiguous fashion. -.. c:function:: PyObject* PyArray_IterAllButAxis(PyObject* arr, int \*axis) +.. c:function:: PyObject* PyArray_IterAllButAxis(PyObject* arr, int* axis) Return an array iterator that will iterate over all axes but the one provided in *\*axis*. The returned iterator cannot be used @@ -2471,7 +2496,7 @@ this useful approach to looping over an array. stride and that axis will be used. .. c:function:: PyObject *PyArray_BroadcastToShape( \ - PyObject* arr, npy_intp *dimensions, int nd) + PyObject* arr, npy_intp const *dimensions, int nd) Return an array iterator that is broadcast to iterate as an array of the shape provided by *dimensions* and *nd*. @@ -2504,7 +2529,7 @@ this useful approach to looping over an array. *destination*, which must have size at least *iterator* ->nd_m1+1. -.. c:function:: PyArray_ITER_GOTO1D(PyObject* iterator, npy_intp index) +.. c:function:: void PyArray_ITER_GOTO1D(PyObject* iterator, npy_intp index) Set the *iterator* index and dataptr to the location in the array indicated by the integer *index* which points to an element in the @@ -2619,22 +2644,33 @@ cost of a slight overhead. The mode should be one of: - * NPY_NEIGHBORHOOD_ITER_ZERO_PADDING: zero padding. Outside bounds values - will be 0. - * NPY_NEIGHBORHOOD_ITER_ONE_PADDING: one padding, Outside bounds values - will be 1. - * NPY_NEIGHBORHOOD_ITER_CONSTANT_PADDING: constant padding. Outside bounds - values will be the same as the first item in fill_value. - * NPY_NEIGHBORHOOD_ITER_MIRROR_PADDING: mirror padding. Outside bounds - values will be as if the array items were mirrored. For example, for the - array [1, 2, 3, 4], x[-2] will be 2, x[-2] will be 1, x[4] will be 4, - x[5] will be 1, etc... - * NPY_NEIGHBORHOOD_ITER_CIRCULAR_PADDING: circular padding. Outside bounds - values will be as if the array was repeated. For example, for the - array [1, 2, 3, 4], x[-2] will be 3, x[-2] will be 4, x[4] will be 1, - x[5] will be 2, etc... - - If the mode is constant filling (NPY_NEIGHBORHOOD_ITER_CONSTANT_PADDING), + .. c:macro:: NPY_NEIGHBORHOOD_ITER_ZERO_PADDING + + Zero padding. Outside bounds values will be 0. + + .. c:macro:: NPY_NEIGHBORHOOD_ITER_ONE_PADDING + + One padding, Outside bounds values will be 1. + + .. c:macro:: NPY_NEIGHBORHOOD_ITER_CONSTANT_PADDING + + Constant padding. Outside bounds values will be the + same as the first item in fill_value. + + .. c:macro:: NPY_NEIGHBORHOOD_ITER_MIRROR_PADDING + + Mirror padding. Outside bounds values will be as if the + array items were mirrored. For example, for the array [1, 2, 3, 4], + x[-2] will be 2, x[-2] will be 1, x[4] will be 4, x[5] will be 1, + etc... + + .. c:macro:: NPY_NEIGHBORHOOD_ITER_CIRCULAR_PADDING + + Circular padding. Outside bounds values will be as if the array + was repeated. For example, for the array [1, 2, 3, 4], x[-2] will + be 3, x[-2] will be 4, x[4] will be 1, x[5] will be 2, etc... + + If the mode is constant filling (`NPY_NEIGHBORHOOD_ITER_CONSTANT_PADDING`), fill_value should point to an array object which holds the filling value (the first item will be the filling value if the array contains more than one item). For other cases, fill_value may be NULL. @@ -2649,25 +2685,31 @@ cost of a slight overhead. - If the position of iter is changed, any subsequent call to PyArrayNeighborhoodIter_Next is undefined behavior, and PyArrayNeighborhoodIter_Reset must be called. + - If the position of iter is not the beginning of the data and the + underlying data for iter is contiguous, the iterator will point to the + start of the data instead of position pointed by iter. + To avoid this situation, iter should be moved to the required position + only after the creation of iterator, and PyArrayNeighborhoodIter_Reset + must be called. .. code-block:: c - PyArrayIterObject \*iter; - PyArrayNeighborhoodIterObject \*neigh_iter; + PyArrayIterObject *iter; + PyArrayNeighborhoodIterObject *neigh_iter; iter = PyArray_IterNew(x); - //For a 3x3 kernel + /*For a 3x3 kernel */ bounds = {-1, 1, -1, 1}; - neigh_iter = (PyArrayNeighborhoodIterObject*)PyArrayNeighborhoodIter_New( + neigh_iter = (PyArrayNeighborhoodIterObject*)PyArray_NeighborhoodIterNew( iter, bounds, NPY_NEIGHBORHOOD_ITER_ZERO_PADDING, NULL); for(i = 0; i < iter->size; ++i) { for (j = 0; j < neigh_iter->size; ++j) { - // Walk around the item currently pointed by iter->dataptr + /* Walk around the item currently pointed by iter->dataptr */ PyArrayNeighborhoodIter_Next(neigh_iter); } - // Move to the next point of iter + /* Move to the next point of iter */ PyArrayIter_Next(iter); PyArrayNeighborhoodIter_Reset(neigh_iter); } @@ -2686,6 +2728,45 @@ cost of a slight overhead. neighborhood. Calling this function after every point of the neighborhood has been visited is undefined. +Array mapping +------------- + +Array mapping is the machinery behind advanced indexing. + +.. c:function:: PyObject* PyArray_MapIterArray(PyArrayObject *a, \ + PyObject *index) + + Use advanced indexing to iterate an array. + +.. c:function:: void PyArray_MapIterSwapAxes(PyArrayMapIterObject *mit, \ + PyArrayObject **ret, int getmap) + + Swap the axes to or from their inserted form. ``MapIter`` always puts the + advanced (array) indices first in the iteration. But if they are + consecutive, it will insert/transpose them back before returning. + This is stored as ``mit->consec != 0`` (the place where they are inserted). + For assignments, the opposite happens: the values to be assigned are + transposed (``getmap=1`` instead of ``getmap=0``). ``getmap=0`` and + ``getmap=1`` undo the other operation. + +.. c:function:: void PyArray_MapIterNext(PyArrayMapIterObject *mit) + + This function needs to update the state of the map iterator + and point ``mit->dataptr`` to the memory-location of the next object. + + Note that this function never handles an extra operand but provides + compatibility for an old (exposed) API. + +.. c:function:: PyObject* PyArray_MapIterArrayCopyIfOverlap(PyArrayObject *a, \ + PyObject *index, int copy_if_overlap, PyArrayObject *extra_op) + + Similar to :c:func:`PyArray_MapIterArray` but with an additional + ``copy_if_overlap`` argument. If ``copy_if_overlap != 0``, checks if ``a`` + has memory overlap with any of the arrays in ``index`` and with + ``extra_op``, and make copies as appropriate to avoid problems if the + input is modified during the iteration. ``iter->array`` may contain a + copied array (UPDATEIFCOPY/WRITEBACKIFCOPY set). + Array Scalars ------------- @@ -2698,13 +2779,19 @@ Array Scalars whenever 0-dimensional arrays could be returned to Python. .. c:function:: PyObject* PyArray_Scalar( \ - void* data, PyArray_Descr* dtype, PyObject* itemsize) + void* data, PyArray_Descr* dtype, PyObject* base) + + Return an array scalar object of the given *dtype* by **copying** + from memory pointed to by *data*. *base* is expected to be the + array object that is the owner of the data. *base* is required + if `dtype` is a ``void`` scalar, or if the ``NPY_USE_GETITEM`` + flag is set and it is known that the ``getitem`` method uses + the ``arr`` argument without checking if it is ``NULL``. Otherwise + `base` may be ``NULL``. - Return an array scalar object of the given enumerated *typenum* - and *itemsize* by **copying** from memory pointed to by *data* - . If *swap* is nonzero then this function will byteswap the data - if appropriate to the data-type because array scalars are always - in correct machine-byte order. + If the data is not in native byte order (as indicated by + ``dtype->byteorder``) then this function will byteswap the data, + because array scalars are always in correct machine-byte order. .. c:function:: PyObject* PyArray_ToScalar(void* data, PyArrayObject* arr) @@ -2756,10 +2843,7 @@ Array Scalars *arr* is not ``NULL`` and the first element is negative then :c:data:`NPY_INTNEG_SCALAR` is returned, otherwise :c:data:`NPY_INTPOS_SCALAR` is returned. The possible return values - are :c:data:`NPY_{kind}_SCALAR` where ``{kind}`` can be **INTPOS**, - **INTNEG**, **FLOAT**, **COMPLEX**, **BOOL**, or **OBJECT**. - :c:data:`NPY_NOSCALAR` is also an enumerated value - :c:type:`NPY_SCALARKIND` variables can take on. + are the enumerated values in :c:type:`NPY_SCALARKIND`. .. c:function:: int PyArray_CanCoerceScalar( \ char thistype, char neededtype, NPY_SCALARKIND scalar) @@ -2786,14 +2870,14 @@ Data-type descriptors Data-type objects must be reference counted so be aware of the action on the data-type reference of different C-API calls. The standard rule is that when a data-type object is returned it is a - new reference. Functions that take :c:type:`PyArray_Descr *` objects and + new reference. Functions that take :c:expr:`PyArray_Descr *` objects and return arrays steal references to the data-type their inputs unless otherwise noted. Therefore, you must own a reference to any data-type object used as input to such a function. .. c:function:: int PyArray_DescrCheck(PyObject* obj) - Evaluates as true if *obj* is a data-type object ( :c:type:`PyArray_Descr *` ). + Evaluates as true if *obj* is a data-type object ( :c:expr:`PyArray_Descr *` ). .. c:function:: PyArray_Descr* PyArray_DescrNew(PyArray_Descr* obj) @@ -2817,11 +2901,23 @@ Data-type descriptors Create a new data-type object with the byteorder set according to *newendian*. All referenced data-type objects (in subdescr and fields members of the data-type object) are also changed - (recursively). If a byteorder of :c:data:`NPY_IGNORE` is encountered it + (recursively). + + The value of *newendian* is one of these macros: +.. + dedent the enumeration of flags to avoid missing references sphinx warnings + +.. c:macro:: NPY_IGNORE + NPY_SWAP + NPY_NATIVE + NPY_LITTLE + NPY_BIG + + If a byteorder of :c:data:`NPY_IGNORE` is encountered it is left alone. If newendian is :c:data:`NPY_SWAP`, then all byte-orders are swapped. Other valid newendian values are :c:data:`NPY_NATIVE`, - :c:data:`NPY_LITTLE`, and :c:data:`NPY_BIG` which all cause the returned - data-typed descriptor (and all it's + :c:data:`NPY_LITTLE`, and :c:data:`NPY_BIG` which all cause + the returned data-typed descriptor (and all it's referenced data-type descriptors) to have the corresponding byte- order. @@ -2847,7 +2943,10 @@ Data-type descriptors Returns a data-type object corresponding to *typenum*. The *typenum* can be one of the enumerated types, a character code for - one of the enumerated types, or a user-defined type. + one of the enumerated types, or a user-defined type. If you want to use a + flexible size array, then you need to ``flexible typenum`` and set the + results ``elsize`` parameter to the desired size. The typenum is one of the + :c:data:`NPY_TYPES`. .. c:function:: int PyArray_DescrConverter(PyObject* obj, PyArray_Descr** dtype) @@ -2952,11 +3051,11 @@ to. already a buffer object pointing to another object). If you need to hold on to the memory be sure to INCREF the base member. The chunk of memory is pointed to by *buf* ->ptr member and has length - *buf* ->len. The flags member of *buf* is :c:data:`NPY_BEHAVED_RO` with - the :c:data:`NPY_ARRAY_WRITEABLE` flag set if *obj* has a writeable buffer - interface. + *buf* ->len. The flags member of *buf* is :c:data:`NPY_ARRAY_ALIGNED` + with the :c:data:`NPY_ARRAY_WRITEABLE` flag set if *obj* has + a writeable buffer interface. -.. c:function:: int PyArray_AxisConverter(PyObject \* obj, int* axis) +.. c:function:: int PyArray_AxisConverter(PyObject* obj, int* axis) Convert a Python object, *obj*, representing an axis argument to the proper value for passing to the functions that take an integer @@ -2964,7 +3063,7 @@ to. :c:data:`NPY_MAXDIMS` which is interpreted correctly by the C-API functions that take axis arguments. -.. c:function:: int PyArray_BoolConverter(PyObject* obj, Bool* value) +.. c:function:: int PyArray_BoolConverter(PyObject* obj, npy_bool* value) Convert any Python object, *obj*, to :c:data:`NPY_TRUE` or :c:data:`NPY_FALSE`, and place the result in *value*. @@ -2978,8 +3077,11 @@ to. .. c:function:: int PyArray_SortkindConverter(PyObject* obj, NPY_SORTKIND* sort) Convert Python strings into one of :c:data:`NPY_QUICKSORT` (starts - with 'q' or 'Q') , :c:data:`NPY_HEAPSORT` (starts with 'h' or 'H'), - or :c:data:`NPY_MERGESORT` (starts with 'm' or 'M'). + with 'q' or 'Q'), :c:data:`NPY_HEAPSORT` (starts with 'h' or 'H'), + :c:data:`NPY_MERGESORT` (starts with 'm' or 'M') or :c:data:`NPY_STABLESORT` + (starts with 't' or 'T'). :c:data:`NPY_MERGESORT` and :c:data:`NPY_STABLESORT` + are aliased to each other for backwards compatibility and may refer to one + of several stable sorting algorithms depending on the data type. .. c:function:: int PyArray_SearchsideConverter( \ PyObject* obj, NPY_SEARCHSIDE* side) @@ -3027,7 +3129,7 @@ Other conversions .. code-block:: c - #define error_converting(x) (((x) == -1) && PyErr_Occurred() + #define error_converting(x) (((x) == -1) && PyErr_Occurred()) .. c:function:: npy_intp PyArray_PyIntAsIntp(PyObject* op) @@ -3113,19 +3215,19 @@ the C-API is needed then some additional steps must be taken. Internally, these #defines work as follows: * If neither is defined, the C-API is declared to be - :c:type:`static void**`, so it is only visible within the + ``static void**``, so it is only visible within the compilation unit that #includes numpy/arrayobject.h. * If :c:macro:`PY_ARRAY_UNIQUE_SYMBOL` is #defined, but :c:macro:`NO_IMPORT_ARRAY` is not, the C-API is declared to - be :c:type:`void**`, so that it will also be visible to other + be ``void**``, so that it will also be visible to other compilation units. * If :c:macro:`NO_IMPORT_ARRAY` is #defined, regardless of whether :c:macro:`PY_ARRAY_UNIQUE_SYMBOL` is, the C-API is - declared to be :c:type:`extern void**`, so it is expected to + declared to be ``extern void**``, so it is expected to be defined in another compilation unit. * Whenever :c:macro:`PY_ARRAY_UNIQUE_SYMBOL` is #defined, it also changes the name of the variable holding the C-API, which - defaults to :c:data:`PyArray_API`, to whatever the macro is + defaults to ``PyArray_API``, to whatever the macro is #defined to. Checking the API Version @@ -3140,21 +3242,31 @@ calling the function). That's why several functions are provided to check for numpy versions. The macros :c:data:`NPY_VERSION` and :c:data:`NPY_FEATURE_VERSION` corresponds to the numpy version used to build the extension, whereas the versions returned by the functions -PyArray_GetNDArrayCVersion and PyArray_GetNDArrayCFeatureVersion corresponds to -the runtime numpy's version. +:c:func:`PyArray_GetNDArrayCVersion` and :c:func:`PyArray_GetNDArrayCFeatureVersion` +corresponds to the runtime numpy's version. The rules for ABI and API compatibilities can be summarized as follows: - * Whenever :c:data:`NPY_VERSION` != PyArray_GetNDArrayCVersion, the + * Whenever :c:data:`NPY_VERSION` != ``PyArray_GetNDArrayCVersion()``, the extension has to be recompiled (ABI incompatibility). - * :c:data:`NPY_VERSION` == PyArray_GetNDArrayCVersion and - :c:data:`NPY_FEATURE_VERSION` <= PyArray_GetNDArrayCFeatureVersion means + * :c:data:`NPY_VERSION` == ``PyArray_GetNDArrayCVersion()`` and + :c:data:`NPY_FEATURE_VERSION` <= ``PyArray_GetNDArrayCFeatureVersion()`` means backward compatible changes. ABI incompatibility is automatically detected in every numpy's version. API incompatibility detection was added in numpy 1.4.0. If you want to supported many different numpy versions with one extension binary, you have to build your -extension with the lowest NPY_FEATURE_VERSION as possible. +extension with the lowest :c:data:`NPY_FEATURE_VERSION` as possible. + +.. c:macro:: NPY_VERSION + + The current version of the ndarray object (check to see if this + variable is defined to guarantee the ``numpy/arrayobject.h`` header is + being used). + +.. c:macro:: NPY_FEATURE_VERSION + + The current version of the C-API. .. c:function:: unsigned int PyArray_GetNDArrayCVersion(void) @@ -3207,12 +3319,16 @@ Internal Flexibility setting a Python Error) if one of the objects being assigned is not callable. + .. deprecated:: 1.16 + .. c:function:: PyObject* PyArray_GetNumericOps(void) Return a Python dictionary containing the callable Python objects stored in the internal arithmetic operation table. The keys of this dictionary are given in the explanation for :c:func:`PyArray_SetNumericOps`. + .. deprecated:: 1.16 + .. c:function:: void PyArray_SetStringFunction(PyObject* op, int repr) This function allows you to alter the tp_str and tp_repr methods @@ -3231,32 +3347,34 @@ Memory management .. c:function:: char* PyDataMem_NEW(size_t nbytes) -.. c:function:: PyDataMem_FREE(char* ptr) +.. c:function:: void PyDataMem_FREE(char* ptr) .. c:function:: char* PyDataMem_RENEW(void * ptr, size_t newbytes) Macros to allocate, free, and reallocate memory. These macros are used internally to create arrays. -.. c:function:: npy_intp* PyDimMem_NEW(nd) +.. c:function:: npy_intp* PyDimMem_NEW(int nd) -.. c:function:: PyDimMem_FREE(npy_intp* ptr) +.. c:function:: void PyDimMem_FREE(char* ptr) -.. c:function:: npy_intp* PyDimMem_RENEW(npy_intp* ptr, npy_intp newnd) +.. c:function:: npy_intp* PyDimMem_RENEW(void* ptr, size_t newnd) Macros to allocate, free, and reallocate dimension and strides memory. -.. c:function:: PyArray_malloc(nbytes) +.. c:function:: void* PyArray_malloc(size_t nbytes) -.. c:function:: PyArray_free(ptr) +.. c:function:: void PyArray_free(void* ptr) -.. c:function:: PyArray_realloc(ptr, nbytes) +.. c:function:: void* PyArray_realloc(npy_intp* ptr, size_t nbytes) These macros use different memory allocators, depending on the constant :c:data:`NPY_USE_PYMEM`. The system malloc is used when :c:data:`NPY_USE_PYMEM` is 0, if :c:data:`NPY_USE_PYMEM` is 1, then the Python memory allocator is used. + .. c:macro:: NPY_USE_PYMEM + .. c:function:: int PyArray_ResolveWritebackIfCopy(PyArrayObject* obj) If ``obj.flags`` has :c:data:`NPY_ARRAY_WRITEBACKIFCOPY` or (deprecated) @@ -3287,9 +3405,13 @@ be accomplished using two groups of macros. Typically, if one macro in a group is used in a code block, all of them must be used in the same code block. Currently, :c:data:`NPY_ALLOW_THREADS` is defined to the python-defined :c:data:`WITH_THREADS` constant unless the environment -variable :c:data:`NPY_NOSMP` is set in which case +variable ``NPY_NOSMP`` is set in which case :c:data:`NPY_ALLOW_THREADS` is defined to be 0. +.. c:macro:: NPY_ALLOW_THREADS + +.. c:macro:: WITH_THREADS + Group 1 """"""" @@ -3326,18 +3448,18 @@ Group 1 interpreter. This macro acquires the GIL and restores the Python state from the saved variable. - .. c:function:: NPY_BEGIN_THREADS_DESCR(PyArray_Descr *dtype) + .. c:function:: void NPY_BEGIN_THREADS_DESCR(PyArray_Descr *dtype) Useful to release the GIL only if *dtype* does not contain arbitrary Python objects which may need the Python interpreter - during execution of the loop. Equivalent to + during execution of the loop. - .. c:function:: NPY_END_THREADS_DESCR(PyArray_Descr *dtype) + .. c:function:: void NPY_END_THREADS_DESCR(PyArray_Descr *dtype) Useful to regain the GIL in situations where it was released using the BEGIN form of this macro. - .. c:function:: NPY_BEGIN_THREADS_THRESHOLDED(int loop_size) + .. c:function:: void NPY_BEGIN_THREADS_THRESHOLDED(int loop_size) Useful to release the GIL only if *loop_size* exceeds a minimum threshold, currently set to 500. Should be matched @@ -3377,15 +3499,15 @@ Group 2 Priority ^^^^^^^^ -.. c:var:: NPY_PRIORITY +.. c:macro:: NPY_PRIORITY Default priority for arrays. -.. c:var:: NPY_SUBTYPE_PRIORITY +.. c:macro:: NPY_SUBTYPE_PRIORITY Default subtype priority. -.. c:var:: NPY_SCALAR_PRIORITY +.. c:macro:: NPY_SCALAR_PRIORITY Default scalar priority (very small) @@ -3400,15 +3522,15 @@ Priority Default buffers ^^^^^^^^^^^^^^^ -.. c:var:: NPY_BUFSIZE +.. c:macro:: NPY_BUFSIZE Default size of the user-settable internal buffers. -.. c:var:: NPY_MIN_BUFSIZE +.. c:macro:: NPY_MIN_BUFSIZE Smallest size of user-settable internal buffers. -.. c:var:: NPY_MAX_BUFSIZE +.. c:macro:: NPY_MAX_BUFSIZE Largest size allowed for the user-settable buffers. @@ -3416,34 +3538,32 @@ Default buffers Other constants ^^^^^^^^^^^^^^^ -.. c:var:: NPY_NUM_FLOATTYPE +.. c:macro:: NPY_NUM_FLOATTYPE The number of floating-point types -.. c:var:: NPY_MAXDIMS +.. c:macro:: NPY_MAXDIMS The maximum number of dimensions allowed in arrays. -.. c:var:: NPY_VERSION +.. c:macro:: NPY_MAXARGS - The current version of the ndarray object (check to see if this - variable is defined to guarantee the numpy/arrayobject.h header is - being used). + The maximum number of array arguments that can be used in functions. -.. c:var:: NPY_FALSE +.. c:macro:: NPY_FALSE Defined as 0 for use with Bool. -.. c:var:: NPY_TRUE +.. c:macro:: NPY_TRUE Defined as 1 for use with Bool. -.. c:var:: NPY_FAIL +.. c:macro:: NPY_FAIL The return value of failed converter functions which are called using the "O&" syntax in :c:func:`PyArg_ParseTuple`-like functions. -.. c:var:: NPY_SUCCEED +.. c:macro:: NPY_SUCCEED The return value of successful converter functions which are called using the "O&" syntax in :c:func:`PyArg_ParseTuple`-like functions. @@ -3452,42 +3572,42 @@ Other constants Miscellaneous Macros ^^^^^^^^^^^^^^^^^^^^ -.. c:function:: PyArray_SAMESHAPE(a1, a2) +.. c:function:: int PyArray_SAMESHAPE(PyArrayObject *a1, PyArrayObject *a2) Evaluates as True if arrays *a1* and *a2* have the same shape. -.. c:function:: PyArray_MAX(a,b) +.. c:macro:: PyArray_MAX(a,b) Returns the maximum of *a* and *b*. If (*a*) or (*b*) are expressions they are evaluated twice. -.. c:function:: PyArray_MIN(a,b) +.. c:macro:: PyArray_MIN(a,b) Returns the minimum of *a* and *b*. If (*a*) or (*b*) are expressions they are evaluated twice. -.. c:function:: PyArray_CLT(a,b) +.. c:macro:: PyArray_CLT(a,b) -.. c:function:: PyArray_CGT(a,b) +.. c:macro:: PyArray_CGT(a,b) -.. c:function:: PyArray_CLE(a,b) +.. c:macro:: PyArray_CLE(a,b) -.. c:function:: PyArray_CGE(a,b) +.. c:macro:: PyArray_CGE(a,b) -.. c:function:: PyArray_CEQ(a,b) +.. c:macro:: PyArray_CEQ(a,b) -.. c:function:: PyArray_CNE(a,b) +.. c:macro:: PyArray_CNE(a,b) Implements the complex comparisons between two complex numbers (structures with a real and imag member) using NumPy's definition of the ordering which is lexicographic: comparing the real parts first and then the complex parts if the real parts are equal. -.. c:function:: PyArray_REFCOUNT(PyObject* op) +.. c:function:: npy_intp PyArray_REFCOUNT(PyObject* op) Returns the reference count of any Python object. -.. c:function:: PyArray_DiscardWritebackIfCopy(PyObject* obj) +.. c:function:: void PyArray_DiscardWritebackIfCopy(PyObject* obj) If ``obj.flags`` has :c:data:`NPY_ARRAY_WRITEBACKIFCOPY` or (deprecated) :c:data:`NPY_ARRAY_UPDATEIFCOPY`, this function clears the flags, `DECREF` s @@ -3498,7 +3618,7 @@ Miscellaneous Macros error when you are finished with ``obj``, just before ``Py_DECREF(obj)``. It may be called multiple times, or with ``NULL`` input. -.. c:function:: PyArray_XDECREF_ERR(PyObject* obj) +.. c:function:: void PyArray_XDECREF_ERR(PyObject* obj) Deprecated in 1.14, use :c:func:`PyArray_DiscardWritebackIfCopy` followed by ``Py_XDECREF`` @@ -3514,75 +3634,113 @@ Miscellaneous Macros Enumerated Types ^^^^^^^^^^^^^^^^ -.. c:type:: NPY_SORTKIND +.. c:enum:: NPY_SORTKIND + + A special variable-type which can take on different values to indicate + the sorting algorithm being used. + + .. c:enumerator:: NPY_QUICKSORT + + .. c:enumerator:: NPY_HEAPSORT - A special variable-type which can take on the values :c:data:`NPY_{KIND}` - where ``{KIND}`` is + .. c:enumerator:: NPY_MERGESORT - **QUICKSORT**, **HEAPSORT**, **MERGESORT** + .. c:enumerator:: NPY_STABLESORT - .. c:var:: NPY_NSORTS + Used as an alias of :c:data:`NPY_MERGESORT` and vica versa. - Defined to be the number of sorts. + .. c:enumerator:: NPY_NSORTS -.. c:type:: NPY_SCALARKIND + Defined to be the number of sorts. It is fixed at three by the need for + backwards compatibility, and consequently :c:data:`NPY_MERGESORT` and + :c:data:`NPY_STABLESORT` are aliased to each other and may refer to one + of several stable sorting algorithms depending on the data type. + + +.. c:enum:: NPY_SCALARKIND A special variable type indicating the number of "kinds" of scalars distinguished in determining scalar-coercion rules. This - variable can take on the values :c:data:`NPY_{KIND}` where ``{KIND}`` can be + variable can take on the values: + + .. c:enumerator:: NPY_NOSCALAR - **NOSCALAR**, **BOOL_SCALAR**, **INTPOS_SCALAR**, - **INTNEG_SCALAR**, **FLOAT_SCALAR**, **COMPLEX_SCALAR**, - **OBJECT_SCALAR** + .. c:enumerator:: NPY_BOOL_SCALAR - .. c:var:: NPY_NSCALARKINDS + .. c:enumerator:: NPY_INTPOS_SCALAR + + .. c:enumerator:: NPY_INTNEG_SCALAR + + .. c:enumerator:: NPY_FLOAT_SCALAR + + .. c:enumerator:: NPY_COMPLEX_SCALAR + + .. c:enumerator:: NPY_OBJECT_SCALAR + + .. c:enumerator:: NPY_NSCALARKINDS Defined to be the number of scalar kinds (not including :c:data:`NPY_NOSCALAR`). -.. c:type:: NPY_ORDER +.. c:enum:: NPY_ORDER An enumeration type indicating the element order that an array should be interpreted in. When a brand new array is created, generally only **NPY_CORDER** and **NPY_FORTRANORDER** are used, whereas when one or more inputs are provided, the order can be based on them. - .. c:var:: NPY_ANYORDER + .. c:enumerator:: NPY_ANYORDER Fortran order if all the inputs are Fortran, C otherwise. - .. c:var:: NPY_CORDER + .. c:enumerator:: NPY_CORDER C order. - .. c:var:: NPY_FORTRANORDER + .. c:enumerator:: NPY_FORTRANORDER Fortran order. - .. c:var:: NPY_KEEPORDER + .. c:enumerator:: NPY_KEEPORDER An order as close to the order of the inputs as possible, even if the input is in neither C nor Fortran order. -.. c:type:: NPY_CLIPMODE +.. c:enum:: NPY_CLIPMODE A variable type indicating the kind of clipping that should be applied in certain functions. - .. c:var:: NPY_RAISE + .. c:enumerator:: NPY_RAISE The default for most operations, raises an exception if an index is out of bounds. - .. c:var:: NPY_CLIP + .. c:enumerator:: NPY_CLIP Clips an index to the valid range if it is out of bounds. - .. c:var:: NPY_WRAP + .. c:enumerator:: NPY_WRAP Wraps an index to the valid range if it is out of bounds. -.. c:type:: NPY_CASTING +.. c:enum:: NPY_SEARCHSIDE + + A variable type indicating whether the index returned should be that of + the first suitable location (if :c:data:`NPY_SEARCHLEFT`) or of the last + (if :c:data:`NPY_SEARCHRIGHT`). + + .. c:enumerator:: NPY_SEARCHLEFT + + .. c:enumerator:: NPY_SEARCHRIGHT + +.. c:enum:: NPY_SELECTKIND + + A variable type indicating the selection algorithm being used. + + .. c:enumerator:: NPY_INTROSELECT + +.. c:enum:: NPY_CASTING .. versionadded:: 1.6 @@ -3590,25 +3748,25 @@ Enumerated Types be. This is used by the iterator added in NumPy 1.6, and is intended to be used more broadly in a future version. - .. c:var:: NPY_NO_CASTING + .. c:enumerator:: NPY_NO_CASTING Only allow identical types. - .. c:var:: NPY_EQUIV_CASTING + .. c:enumerator:: NPY_EQUIV_CASTING Allow identical and casts involving byte swapping. - .. c:var:: NPY_SAFE_CASTING + .. c:enumerator:: NPY_SAFE_CASTING Only allow casts which will not cause values to be rounded, truncated, or otherwise changed. - .. c:var:: NPY_SAME_KIND_CASTING + .. c:enumerator:: NPY_SAME_KIND_CASTING Allow any safe casts, and casts between types of the same kind. For example, float64 -> float32 is permitted with this rule. - .. c:var:: NPY_UNSAFE_CASTING + .. c:enumerator:: NPY_UNSAFE_CASTING Allow any cast, no matter what kind of data loss may occur. diff --git a/doc/source/reference/c-api.config.rst b/doc/source/reference/c-api/config.rst similarity index 54% rename from doc/source/reference/c-api.config.rst rename to doc/source/reference/c-api/config.rst index 60bf61a32666..87130699bbf8 100644 --- a/doc/source/reference/c-api.config.rst +++ b/doc/source/reference/c-api/config.rst @@ -19,59 +19,62 @@ avoid namespace pollution. Data type sizes --------------- -The :c:data:`NPY_SIZEOF_{CTYPE}` constants are defined so that sizeof +The ``NPY_SIZEOF_{CTYPE}`` constants are defined so that sizeof information is available to the pre-processor. -.. c:var:: NPY_SIZEOF_SHORT +.. c:macro:: NPY_SIZEOF_SHORT sizeof(short) -.. c:var:: NPY_SIZEOF_INT +.. c:macro:: NPY_SIZEOF_INT sizeof(int) -.. c:var:: NPY_SIZEOF_LONG +.. c:macro:: NPY_SIZEOF_LONG sizeof(long) -.. c:var:: NPY_SIZEOF_LONGLONG +.. c:macro:: NPY_SIZEOF_LONGLONG sizeof(longlong) where longlong is defined appropriately on the platform. -.. c:var:: NPY_SIZEOF_PY_LONG_LONG +.. c:macro:: NPY_SIZEOF_PY_LONG_LONG -.. c:var:: NPY_SIZEOF_FLOAT +.. c:macro:: NPY_SIZEOF_FLOAT sizeof(float) -.. c:var:: NPY_SIZEOF_DOUBLE +.. c:macro:: NPY_SIZEOF_DOUBLE sizeof(double) -.. c:var:: NPY_SIZEOF_LONG_DOUBLE +.. c:macro:: NPY_SIZEOF_LONG_DOUBLE - sizeof(longdouble) (A macro defines **NPY_SIZEOF_LONGDOUBLE** as well.) +.. c:macro:: NPY_SIZEOF_LONGDOUBLE -.. c:var:: NPY_SIZEOF_PY_INTPTR_T + sizeof(longdouble) - Size of a pointer on this platform (sizeof(void \*)) (A macro defines - NPY_SIZEOF_INTP as well.) +.. c:macro:: NPY_SIZEOF_PY_INTPTR_T + +.. c:macro:: NPY_SIZEOF_INTP + + Size of a pointer on this platform (sizeof(void \*)) Platform information -------------------- -.. c:var:: NPY_CPU_X86 -.. c:var:: NPY_CPU_AMD64 -.. c:var:: NPY_CPU_IA64 -.. c:var:: NPY_CPU_PPC -.. c:var:: NPY_CPU_PPC64 -.. c:var:: NPY_CPU_SPARC -.. c:var:: NPY_CPU_SPARC64 -.. c:var:: NPY_CPU_S390 -.. c:var:: NPY_CPU_PARISC +.. c:macro:: NPY_CPU_X86 +.. c:macro:: NPY_CPU_AMD64 +.. c:macro:: NPY_CPU_IA64 +.. c:macro:: NPY_CPU_PPC +.. c:macro:: NPY_CPU_PPC64 +.. c:macro:: NPY_CPU_SPARC +.. c:macro:: NPY_CPU_SPARC64 +.. c:macro:: NPY_CPU_S390 +.. c:macro:: NPY_CPU_PARISC .. versionadded:: 1.3.0 @@ -80,11 +83,11 @@ Platform information Defined in ``numpy/npy_cpu.h`` -.. c:var:: NPY_LITTLE_ENDIAN +.. c:macro:: NPY_LITTLE_ENDIAN -.. c:var:: NPY_BIG_ENDIAN +.. c:macro:: NPY_BIG_ENDIAN -.. c:var:: NPY_BYTE_ORDER +.. c:macro:: NPY_BYTE_ORDER .. versionadded:: 1.3.0 @@ -94,10 +97,35 @@ Platform information Defined in ``numpy/npy_endian.h``. -.. c:function:: PyArray_GetEndianness() +.. c:function:: int PyArray_GetEndianness() .. versionadded:: 1.3.0 Returns the endianness of the current platform. One of :c:data:`NPY_CPU_BIG`, :c:data:`NPY_CPU_LITTLE`, or :c:data:`NPY_CPU_UNKNOWN_ENDIAN`. + + .. c:macro:: NPY_CPU_BIG + + .. c:macro:: NPY_CPU_LITTLE + + .. c:macro:: NPY_CPU_UNKNOWN_ENDIAN + + +Compiler directives +------------------- + +.. c:macro:: NPY_LIKELY +.. c:macro:: NPY_UNLIKELY +.. c:macro:: NPY_UNUSED + + +Interrupt Handling +------------------ + +.. c:macro:: NPY_INTERRUPT_H +.. c:macro:: NPY_SIGSETJMP +.. c:macro:: NPY_SIGLONGJMP +.. c:macro:: NPY_SIGJMP_BUF +.. c:macro:: NPY_SIGINT_ON +.. c:macro:: NPY_SIGINT_OFF diff --git a/doc/source/reference/c-api.coremath.rst b/doc/source/reference/c-api/coremath.rst similarity index 85% rename from doc/source/reference/c-api.coremath.rst rename to doc/source/reference/c-api/coremath.rst index ad92235da623..e129fdd7769b 100644 --- a/doc/source/reference/c-api.coremath.rst +++ b/doc/source/reference/c-api/coremath.rst @@ -24,52 +24,52 @@ in doubt. Floating point classification ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. c:var:: NPY_NAN +.. c:macro:: NPY_NAN This macro is defined to a NaN (Not a Number), and is guaranteed to have the signbit unset ('positive' NaN). The corresponding single and extension precision macro are available with the suffix F and L. -.. c:var:: NPY_INFINITY +.. c:macro:: NPY_INFINITY This macro is defined to a positive inf. The corresponding single and extension precision macro are available with the suffix F and L. -.. c:var:: NPY_PZERO +.. c:macro:: NPY_PZERO This macro is defined to positive zero. The corresponding single and extension precision macro are available with the suffix F and L. -.. c:var:: NPY_NZERO +.. c:macro:: NPY_NZERO This macro is defined to negative zero (that is with the sign bit set). The corresponding single and extension precision macro are available with the suffix F and L. -.. c:function:: int npy_isnan(x) +.. c:macro:: npy_isnan(x) This is a macro, and is equivalent to C99 isnan: works for single, double - and extended precision, and return a non 0 value is x is a NaN. + and extended precision, and return a non 0 value if x is a NaN. -.. c:function:: int npy_isfinite(x) +.. c:macro:: npy_isfinite(x) This is a macro, and is equivalent to C99 isfinite: works for single, - double and extended precision, and return a non 0 value is x is neither a + double and extended precision, and return a non 0 value if x is neither a NaN nor an infinity. -.. c:function:: int npy_isinf(x) +.. c:macro:: npy_isinf(x) This is a macro, and is equivalent to C99 isinf: works for single, double - and extended precision, and return a non 0 value is x is infinite (positive + and extended precision, and return a non 0 value if x is infinite (positive and negative). -.. c:function:: int npy_signbit(x) +.. c:macro:: npy_signbit(x) This is a macro, and is equivalent to C99 signbit: works for single, double - and extended precision, and return a non 0 value is x has the signbit set + and extended precision, and return a non 0 value if x has the signbit set (that is the number is negative). -.. c:function:: double npy_copysign(double x, double y) +.. c:macro:: npy_copysign(x, y) This is a function equivalent to C99 copysign: return x with the same sign as y. Works for any value, including inf and nan. Single and extended @@ -80,50 +80,51 @@ Floating point classification Useful math constants ~~~~~~~~~~~~~~~~~~~~~ -The following math constants are available in npy_math.h. Single and extended -precision are also available by adding the F and L suffixes respectively. +The following math constants are available in ``npy_math.h``. Single +and extended precision are also available by adding the ``f`` and +``l`` suffixes respectively. -.. c:var:: NPY_E +.. c:macro:: NPY_E Base of natural logarithm (:math:`e`) -.. c:var:: NPY_LOG2E +.. c:macro:: NPY_LOG2E Logarithm to base 2 of the Euler constant (:math:`\frac{\ln(e)}{\ln(2)}`) -.. c:var:: NPY_LOG10E +.. c:macro:: NPY_LOG10E Logarithm to base 10 of the Euler constant (:math:`\frac{\ln(e)}{\ln(10)}`) -.. c:var:: NPY_LOGE2 +.. c:macro:: NPY_LOGE2 Natural logarithm of 2 (:math:`\ln(2)`) -.. c:var:: NPY_LOGE10 +.. c:macro:: NPY_LOGE10 Natural logarithm of 10 (:math:`\ln(10)`) -.. c:var:: NPY_PI +.. c:macro:: NPY_PI Pi (:math:`\pi`) -.. c:var:: NPY_PI_2 +.. c:macro:: NPY_PI_2 Pi divided by 2 (:math:`\frac{\pi}{2}`) -.. c:var:: NPY_PI_4 +.. c:macro:: NPY_PI_4 Pi divided by 4 (:math:`\frac{\pi}{4}`) -.. c:var:: NPY_1_PI +.. c:macro:: NPY_1_PI Reciprocal of pi (:math:`\frac{1}{\pi}`) -.. c:var:: NPY_2_PI +.. c:macro:: NPY_2_PI Two times the reciprocal of pi (:math:`\frac{2}{\pi}`) -.. c:var:: NPY_EULER +.. c:macro:: NPY_EULER The Euler constant :math:`\lim_{n\rightarrow\infty}({\sum_{k=1}^n{\frac{1}{k}}-\ln n})` @@ -184,7 +185,7 @@ Those can be useful for precise floating point comparison. * NPY_FPE_INVALID Note that :c:func:`npy_get_floatstatus_barrier` is preferable as it prevents - agressive compiler optimizations reordering the call relative to + aggressive compiler optimizations reordering the call relative to the code setting the status, which could lead to incorrect results. .. versionadded:: 1.9.0 @@ -192,7 +193,7 @@ Those can be useful for precise floating point comparison. .. c:function:: int npy_get_floatstatus_barrier(char*) Get floating point status. A pointer to a local variable is passed in to - prevent aggresive compiler optimizations from reodering this function call + prevent aggressive compiler optimizations from reordering this function call relative to the code setting the status, which could lead to incorrect results. @@ -210,7 +211,7 @@ Those can be useful for precise floating point comparison. Clears the floating point status. Returns the previous status mask. Note that :c:func:`npy_clear_floatstatus_barrier` is preferable as it - prevents agressive compiler optimizations reordering the call relative to + prevents aggressive compiler optimizations reordering the call relative to the code setting the status, which could lead to incorrect results. .. versionadded:: 1.9.0 @@ -218,11 +219,11 @@ Those can be useful for precise floating point comparison. .. c:function:: int npy_clear_floatstatus_barrier(char*) Clears the floating point status. A pointer to a local variable is passed in to - prevent aggresive compiler optimizations from reodering this function call. + prevent aggressive compiler optimizations from reordering this function call. Returns the previous status mask. .. versionadded:: 1.15.0 -n + Complex functions ~~~~~~~~~~~~~~~~~ @@ -248,9 +249,14 @@ Linking against the core math library in an extension To use the core math library in your own extension, you need to add the npymath compile and link options to your extension in your setup.py: + .. hidden in a comment so as to be included in refguide but not rendered documentation + >>> import numpy.distutils.misc_util + >>> config = np.distutils.misc_util.Configuration(None, '', '.') + >>> with open('foo.c', 'w') as f: pass + >>> from numpy.distutils.misc_util import get_info >>> info = get_info('npymath') - >>> config.add_extension('foo', sources=['foo.c'], extra_info=info) + >>> _ = config.add_extension('foo', sources=['foo.c'], extra_info=info) In other words, the usage of info is exactly the same as when using blas_info and co. @@ -258,7 +264,7 @@ and co. Half-precision functions ~~~~~~~~~~~~~~~~~~~~~~~~ -.. versionadded:: 2.0.0 +.. versionadded:: 1.6.0 The header file <numpy/halffloat.h> provides functions to work with IEEE 754-2008 16-bit floating point values. While this format is @@ -297,40 +303,40 @@ External Links: * `OpenGL Half Float Pixel Support`__ * `The OpenEXR image format`__. -__ http://ieeexplore.ieee.org/servlet/opac?punumber=4610933 -__ http://en.wikipedia.org/wiki/Half_precision_floating-point_format -__ http://www.opengl.org/registry/specs/ARB/half_float_pixel.txt -__ http://www.openexr.com/about.html +__ https://ieeexplore.ieee.org/document/4610935/ +__ https://en.wikipedia.org/wiki/Half-precision_floating-point_format +__ https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_half_float_pixel.txt +__ https://www.openexr.com/about.html -.. c:var:: NPY_HALF_ZERO +.. c:macro:: NPY_HALF_ZERO This macro is defined to positive zero. -.. c:var:: NPY_HALF_PZERO +.. c:macro:: NPY_HALF_PZERO This macro is defined to positive zero. -.. c:var:: NPY_HALF_NZERO +.. c:macro:: NPY_HALF_NZERO This macro is defined to negative zero. -.. c:var:: NPY_HALF_ONE +.. c:macro:: NPY_HALF_ONE This macro is defined to 1.0. -.. c:var:: NPY_HALF_NEGONE +.. c:macro:: NPY_HALF_NEGONE This macro is defined to -1.0. -.. c:var:: NPY_HALF_PINF +.. c:macro:: NPY_HALF_PINF This macro is defined to +inf. -.. c:var:: NPY_HALF_NINF +.. c:macro:: NPY_HALF_NINF This macro is defined to -inf. -.. c:var:: NPY_HALF_NAN +.. c:macro:: NPY_HALF_NAN This macro is defined to a NaN value, guaranteed to have its sign bit unset. diff --git a/doc/source/reference/c-api/data_memory.rst b/doc/source/reference/c-api/data_memory.rst new file mode 100644 index 000000000000..b779026b4551 --- /dev/null +++ b/doc/source/reference/c-api/data_memory.rst @@ -0,0 +1,159 @@ +.. _data_memory: + +Memory management in NumPy +========================== + +The `numpy.ndarray` is a python class. It requires additional memory allocations +to hold `numpy.ndarray.strides`, `numpy.ndarray.shape` and +`numpy.ndarray.data` attributes. These attributes are specially allocated +after creating the python object in `__new__`. The ``strides`` and +``shape`` are stored in a piece of memory allocated internally. + +The ``data`` allocation used to store the actual array values (which could be +pointers in the case of ``object`` arrays) can be very large, so NumPy has +provided interfaces to manage its allocation and release. This document details +how those interfaces work. + +Historical overview +------------------- + +Since version 1.7.0, NumPy has exposed a set of ``PyDataMem_*`` functions +(:c:func:`PyDataMem_NEW`, :c:func:`PyDataMem_FREE`, :c:func:`PyDataMem_RENEW`) +which are backed by `alloc`, `free`, `realloc` respectively. In that version +NumPy also exposed the `PyDataMem_EventHook` function described below, which +wrap the OS-level calls. + +Since those early days, Python also improved its memory management +capabilities, and began providing +various :ref:`management policies <memoryoverview>` beginning in version +3.4. These routines are divided into a set of domains, each domain has a +:c:type:`PyMemAllocatorEx` structure of routines for memory management. Python also +added a `tracemalloc` module to trace calls to the various routines. These +tracking hooks were added to the NumPy ``PyDataMem_*`` routines. + +NumPy added a small cache of allocated memory in its internal +``npy_alloc_cache``, ``npy_alloc_cache_zero``, and ``npy_free_cache`` +functions. These wrap ``alloc``, ``alloc-and-memset(0)`` and ``free`` +respectively, but when ``npy_free_cache`` is called, it adds the pointer to a +short list of available blocks marked by size. These blocks can be re-used by +subsequent calls to ``npy_alloc*``, avoiding memory thrashing. + +Configurable memory routines in NumPy (NEP 49) +---------------------------------------------- + +Users may wish to override the internal data memory routines with ones of their +own. Since NumPy does not use the Python domain strategy to manage data memory, +it provides an alternative set of C-APIs to change memory routines. There are +no Python domain-wide strategies for large chunks of object data, so those are +less suited to NumPy's needs. User who wish to change the NumPy data memory +management routines can use :c:func:`PyDataMem_SetHandler`, which uses a +:c:type:`PyDataMem_Handler` structure to hold pointers to functions used to +manage the data memory. The calls are still wrapped by internal routines to +call :c:func:`PyTraceMalloc_Track`, :c:func:`PyTraceMalloc_Untrack`, and will +use the :c:func:`PyDataMem_EventHookFunc` mechanism. Since the functions may +change during the lifetime of the process, each ``ndarray`` carries with it the +functions used at the time of its instantiation, and these will be used to +reallocate or free the data memory of the instance. + +.. c:type:: PyDataMem_Handler + + A struct to hold function pointers used to manipulate memory + + .. code-block:: c + + typedef struct { + char name[127]; /* multiple of 64 to keep the struct aligned */ + uint8_t version; /* currently 1 */ + PyDataMemAllocator allocator; + } PyDataMem_Handler; + + where the allocator structure is + + .. code-block:: c + + /* The declaration of free differs from PyMemAllocatorEx */ + typedef struct { + void *ctx; + void* (*malloc) (void *ctx, size_t size); + void* (*calloc) (void *ctx, size_t nelem, size_t elsize); + void* (*realloc) (void *ctx, void *ptr, size_t new_size); + void (*free) (void *ctx, void *ptr, size_t size); + } PyDataMemAllocator; + +.. c:function:: PyObject * PyDataMem_SetHandler(PyObject *handler) + + Set a new allocation policy. If the input value is ``NULL``, will reset the + policy to the default. Return the previous policy, or + return ``NULL`` if an error has occurred. We wrap the user-provided functions + so they will still call the python and numpy memory management callback + hooks. + +.. c:function:: PyObject * PyDataMem_GetHandler() + + Return the current policy that will be used to allocate data for the + next ``PyArrayObject``. On failure, return ``NULL``. + +For an example of setting up and using the PyDataMem_Handler, see the test in +:file:`numpy/core/tests/test_mem_policy.py` + +.. c:function:: void PyDataMem_EventHookFunc(void *inp, void *outp, size_t size, void *user_data); + + This function will be called during data memory manipulation + +.. c:function:: PyDataMem_EventHookFunc * PyDataMem_SetEventHook(PyDataMem_EventHookFunc *newhook, void *user_data, void **old_data) + + Sets the allocation event hook for numpy array data. + + Returns a pointer to the previous hook or ``NULL``. If old_data is + non-``NULL``, the previous user_data pointer will be copied to it. + + If not ``NULL``, hook will be called at the end of each ``PyDataMem_NEW/FREE/RENEW``: + + .. code-block:: c + + result = PyDataMem_NEW(size) -> (*hook)(NULL, result, size, user_data) + PyDataMem_FREE(ptr) -> (*hook)(ptr, NULL, 0, user_data) + result = PyDataMem_RENEW(ptr, size) -> (*hook)(ptr, result, size, user_data) + + When the hook is called, the GIL will be held by the calling + thread. The hook should be written to be reentrant, if it performs + operations that might cause new allocation events (such as the + creation/destruction numpy objects, or creating/destroying Python + objects which might cause a gc) + +What happens when deallocating if there is no policy set +-------------------------------------------------------- + +A rare but useful technique is to allocate a buffer outside NumPy, use +:c:func:`PyArray_NewFromDescr` to wrap the buffer in a ``ndarray``, then switch +the ``OWNDATA`` flag to true. When the ``ndarray`` is released, the +appropriate function from the ``ndarray``'s ``PyDataMem_Handler`` should be +called to free the buffer. But the ``PyDataMem_Handler`` field was never set, +it will be ``NULL``. For backward compatibility, NumPy will call ``free()`` to +release the buffer. If ``NUMPY_WARN_IF_NO_MEM_POLICY`` is set to ``1``, a +warning will be emitted. The current default is not to emit a warning, this may +change in a future version of NumPy. + +A better technique would be to use a ``PyCapsule`` as a base object: + +.. code-block:: c + + /* define a PyCapsule_Destructor, using the correct deallocator for buff */ + void free_wrap(void *capsule){ + void * obj = PyCapsule_GetPointer(capsule, PyCapsule_GetName(capsule)); + free(obj); + }; + + /* then inside the function that creates arr from buff */ + ... + arr = PyArray_NewFromDescr(... buf, ...); + if (arr == NULL) { + return NULL; + } + capsule = PyCapsule_New(buf, "my_wrapped_buffer", + (PyCapsule_Destructor)&free_wrap); + if (PyArray_SetBaseObject(arr, capsule) == -1) { + Py_DECREF(arr); + return NULL; + } + ... diff --git a/doc/source/reference/c-api.deprecations.rst b/doc/source/reference/c-api/deprecations.rst similarity index 99% rename from doc/source/reference/c-api.deprecations.rst rename to doc/source/reference/c-api/deprecations.rst index a382017a2ad7..5b1abc6f2add 100644 --- a/doc/source/reference/c-api.deprecations.rst +++ b/doc/source/reference/c-api/deprecations.rst @@ -48,7 +48,9 @@ warnings). To use the NPY_NO_DEPRECATED_API mechanism, you need to #define it to the target API version of NumPy before #including any NumPy headers. -If you want to confirm that your code is clean against 1.7, use:: +If you want to confirm that your code is clean against 1.7, use: + +.. code-block:: c #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION diff --git a/doc/source/reference/c-api.dtype.rst b/doc/source/reference/c-api/dtype.rst similarity index 73% rename from doc/source/reference/c-api.dtype.rst rename to doc/source/reference/c-api/dtype.rst index 8af3a9080567..382e45dc0aa7 100644 --- a/doc/source/reference/c-api.dtype.rst +++ b/doc/source/reference/c-api/dtype.rst @@ -25,155 +25,157 @@ select the precision desired. Enumerated Types ---------------- +.. c:enumerator:: NPY_TYPES + There is a list of enumerated types defined providing the basic 24 data types plus some useful generic names. Whenever the code requires a type number, one of these enumerated types is requested. The types -are all called :c:data:`NPY_{NAME}`: +are all called ``NPY_{NAME}``: -.. c:var:: NPY_BOOL +.. c:enumerator:: NPY_BOOL The enumeration value for the boolean type, stored as one byte. It may only be set to the values 0 and 1. -.. c:var:: NPY_BYTE -.. c:var:: NPY_INT8 +.. c:enumerator:: NPY_BYTE +.. c:enumerator:: NPY_INT8 The enumeration value for an 8-bit/1-byte signed integer. -.. c:var:: NPY_SHORT -.. c:var:: NPY_INT16 +.. c:enumerator:: NPY_SHORT +.. c:enumerator:: NPY_INT16 The enumeration value for a 16-bit/2-byte signed integer. -.. c:var:: NPY_INT -.. c:var:: NPY_INT32 +.. c:enumerator:: NPY_INT +.. c:enumerator:: NPY_INT32 The enumeration value for a 32-bit/4-byte signed integer. -.. c:var:: NPY_LONG +.. c:enumerator:: NPY_LONG Equivalent to either NPY_INT or NPY_LONGLONG, depending on the platform. -.. c:var:: NPY_LONGLONG -.. c:var:: NPY_INT64 +.. c:enumerator:: NPY_LONGLONG +.. c:enumerator:: NPY_INT64 The enumeration value for a 64-bit/8-byte signed integer. -.. c:var:: NPY_UBYTE -.. c:var:: NPY_UINT8 +.. c:enumerator:: NPY_UBYTE +.. c:enumerator:: NPY_UINT8 The enumeration value for an 8-bit/1-byte unsigned integer. -.. c:var:: NPY_USHORT -.. c:var:: NPY_UINT16 +.. c:enumerator:: NPY_USHORT +.. c:enumerator:: NPY_UINT16 The enumeration value for a 16-bit/2-byte unsigned integer. -.. c:var:: NPY_UINT -.. c:var:: NPY_UINT32 +.. c:enumerator:: NPY_UINT +.. c:enumerator:: NPY_UINT32 The enumeration value for a 32-bit/4-byte unsigned integer. -.. c:var:: NPY_ULONG +.. c:enumerator:: NPY_ULONG Equivalent to either NPY_UINT or NPY_ULONGLONG, depending on the platform. -.. c:var:: NPY_ULONGLONG -.. c:var:: NPY_UINT64 +.. c:enumerator:: NPY_ULONGLONG +.. c:enumerator:: NPY_UINT64 The enumeration value for a 64-bit/8-byte unsigned integer. -.. c:var:: NPY_HALF -.. c:var:: NPY_FLOAT16 +.. c:enumerator:: NPY_HALF +.. c:enumerator:: NPY_FLOAT16 The enumeration value for a 16-bit/2-byte IEEE 754-2008 compatible floating point type. -.. c:var:: NPY_FLOAT -.. c:var:: NPY_FLOAT32 +.. c:enumerator:: NPY_FLOAT +.. c:enumerator:: NPY_FLOAT32 The enumeration value for a 32-bit/4-byte IEEE 754 compatible floating point type. -.. c:var:: NPY_DOUBLE -.. c:var:: NPY_FLOAT64 +.. c:enumerator:: NPY_DOUBLE +.. c:enumerator:: NPY_FLOAT64 The enumeration value for a 64-bit/8-byte IEEE 754 compatible floating point type. -.. c:var:: NPY_LONGDOUBLE +.. c:enumerator:: NPY_LONGDOUBLE The enumeration value for a platform-specific floating point type which is at least as large as NPY_DOUBLE, but larger on many platforms. -.. c:var:: NPY_CFLOAT -.. c:var:: NPY_COMPLEX64 +.. c:enumerator:: NPY_CFLOAT +.. c:enumerator:: NPY_COMPLEX64 The enumeration value for a 64-bit/8-byte complex type made up of two NPY_FLOAT values. -.. c:var:: NPY_CDOUBLE -.. c:var:: NPY_COMPLEX128 +.. c:enumerator:: NPY_CDOUBLE +.. c:enumerator:: NPY_COMPLEX128 The enumeration value for a 128-bit/16-byte complex type made up of two NPY_DOUBLE values. -.. c:var:: NPY_CLONGDOUBLE +.. c:enumerator:: NPY_CLONGDOUBLE The enumeration value for a platform-specific complex floating point type which is made up of two NPY_LONGDOUBLE values. -.. c:var:: NPY_DATETIME +.. c:enumerator:: NPY_DATETIME The enumeration value for a data type which holds dates or datetimes with a precision based on selectable date or time units. -.. c:var:: NPY_TIMEDELTA +.. c:enumerator:: NPY_TIMEDELTA The enumeration value for a data type which holds lengths of times in integers of selectable date or time units. -.. c:var:: NPY_STRING +.. c:enumerator:: NPY_STRING The enumeration value for ASCII strings of a selectable size. The strings have a fixed maximum size within a given array. -.. c:var:: NPY_UNICODE +.. c:enumerator:: NPY_UNICODE The enumeration value for UCS4 strings of a selectable size. The strings have a fixed maximum size within a given array. -.. c:var:: NPY_OBJECT +.. c:enumerator:: NPY_OBJECT The enumeration value for references to arbitrary Python objects. -.. c:var:: NPY_VOID +.. c:enumerator:: NPY_VOID Primarily used to hold struct dtypes, but can contain arbitrary binary data. Some useful aliases of the above types are -.. c:var:: NPY_INTP +.. c:enumerator:: NPY_INTP The enumeration value for a signed integer type which is the same size as a (void \*) pointer. This is the type used by all arrays of indices. -.. c:var:: NPY_UINTP +.. c:enumerator:: NPY_UINTP The enumeration value for an unsigned integer type which is the same size as a (void \*) pointer. -.. c:var:: NPY_MASK +.. c:enumerator:: NPY_MASK The enumeration value of the type used for masks, such as with the :c:data:`NPY_ITER_ARRAYMASK` iterator flag. This is equivalent to :c:data:`NPY_UINT8`. -.. c:var:: NPY_DEFAULT_TYPE +.. c:enumerator:: NPY_DEFAULT_TYPE The default type to use when no dtype is explicitly specified, for example when calling np.zero(shape). This is equivalent to @@ -181,23 +183,23 @@ Some useful aliases of the above types are Other useful related constants are -.. c:var:: NPY_NTYPES +.. c:macro:: NPY_NTYPES The total number of built-in NumPy types. The enumeration covers the range from 0 to NPY_NTYPES-1. -.. c:var:: NPY_NOTYPE +.. c:macro:: NPY_NOTYPE A signal value guaranteed not to be a valid type enumeration number. -.. c:var:: NPY_USERDEF +.. c:macro:: NPY_USERDEF The start of type numbers used for Custom Data types. The various character codes indicating certain types are also part of an enumerated list. References to type characters (should they be needed at all) should always use these enumerations. The form of them -is :c:data:`NPY_{NAME}LTR` where ``{NAME}`` can be +is ``NPY_{NAME}LTR`` where ``{NAME}`` can be **BOOL**, **BYTE**, **UBYTE**, **SHORT**, **USHORT**, **INT**, **UINT**, **LONG**, **ULONG**, **LONGLONG**, **ULONGLONG**, @@ -219,24 +221,17 @@ Defines Max and min values for integers ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. c:var:: NPY_MAX_INT{bits} - -.. c:var:: NPY_MAX_UINT{bits} - -.. c:var:: NPY_MIN_INT{bits} - +``NPY_MAX_INT{bits}``, ``NPY_MAX_UINT{bits}``, ``NPY_MIN_INT{bits}`` These are defined for ``{bits}`` = 8, 16, 32, 64, 128, and 256 and provide the maximum (minimum) value of the corresponding (unsigned) integer type. Note: the actual integer type may not be available on all platforms (i.e. 128-bit and 256-bit integers are rare). -.. c:var:: NPY_MIN_{type} - +``NPY_MIN_{type}`` This is defined for ``{type}`` = **BYTE**, **SHORT**, **INT**, **LONG**, **LONGLONG**, **INTP** -.. c:var:: NPY_MAX_{type} - +``NPY_MAX_{type}`` This is defined for all defined for ``{type}`` = **BYTE**, **UBYTE**, **SHORT**, **USHORT**, **INT**, **UINT**, **LONG**, **ULONG**, **LONGLONG**, **ULONGLONG**, **INTP**, **UINTP** @@ -245,8 +240,8 @@ Max and min values for integers Number of bits in data types ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -All :c:data:`NPY_SIZEOF_{CTYPE}` constants have corresponding -:c:data:`NPY_BITSOF_{CTYPE}` constants defined. The :c:data:`NPY_BITSOF_{CTYPE}` +All ``NPY_SIZEOF_{CTYPE}`` constants have corresponding +``NPY_BITSOF_{CTYPE}`` constants defined. The ``NPY_BITSOF_{CTYPE}`` constants provide the number of bits in the data type. Specifically, the available ``{CTYPE}s`` are @@ -261,7 +256,7 @@ All of the numeric data types (integer, floating point, and complex) have constants that are defined to be a specific enumerated type number. Exactly which enumerated type a bit-width type refers to is platform dependent. In particular, the constants available are -:c:data:`PyArray_{NAME}{BITS}` where ``{NAME}`` is **INT**, **UINT**, +``PyArray_{NAME}{BITS}`` where ``{NAME}`` is **INT**, **UINT**, **FLOAT**, **COMPLEX** and ``{BITS}`` can be 8, 16, 32, 64, 80, 96, 128, 160, 192, 256, and 512. Obviously not all bit-widths are available on all platforms for all the kinds of numeric types. Commonly 8-, 16-, @@ -302,47 +297,112 @@ Boolean Unsigned versions of the integers can be defined by pre-pending a 'u' to the front of the integer name. -.. c:type:: npy_(u)byte +.. c:type:: npy_byte + + char + +.. c:type:: npy_ubyte + + unsigned char + +.. c:type:: npy_short + + short + +.. c:type:: npy_ushort + + unsigned short + +.. c:type:: npy_int + + int + +.. c:type:: npy_uint + + unsigned int + +.. c:type:: npy_int16 + + 16-bit integer + +.. c:type:: npy_uint16 + + 16-bit unsigned integer + +.. c:type:: npy_int32 - (unsigned) char + 32-bit integer -.. c:type:: npy_(u)short +.. c:type:: npy_uint32 - (unsigned) short + 32-bit unsigned integer -.. c:type:: npy_(u)int +.. c:type:: npy_int64 - (unsigned) int + 64-bit integer -.. c:type:: npy_(u)long +.. c:type:: npy_uint64 - (unsigned) long int + 64-bit unsigned integer -.. c:type:: npy_(u)longlong +.. c:type:: npy_long - (unsigned long long int) + long int -.. c:type:: npy_(u)intp +.. c:type:: npy_ulong - (unsigned) Py_intptr_t (an integer that is the size of a pointer on + unsigned long int + +.. c:type:: npy_longlong + + long long int + +.. c:type:: npy_ulonglong + + unsigned long long int + +.. c:type:: npy_intp + + Py_intptr_t (an integer that is the size of a pointer on + the platform). + +.. c:type:: npy_uintp + + unsigned Py_intptr_t (an integer that is the size of a pointer on the platform). (Complex) Floating point ^^^^^^^^^^^^^^^^^^^^^^^^ -.. c:type:: npy_(c)float +.. c:type:: npy_half + + 16-bit float + +.. c:type:: npy_float - float + 32-bit float -.. c:type:: npy_(c)double +.. c:type:: npy_cfloat - double + 32-bit complex float -.. c:type:: npy_(c)longdouble +.. c:type:: npy_double + + 64-bit double + +.. c:type:: npy_cdouble + + 64-bit complex double + +.. c:type:: npy_longdouble long double +.. c:type:: npy_clongdouble + + long complex double + complex types are structures with **.real** and **.imag** members (in that order). @@ -354,8 +414,8 @@ There are also typedefs for signed integers, unsigned integers, floating point, and complex floating point types of specific bit- widths. The available type names are - :c:type:`npy_int{bits}`, :c:type:`npy_uint{bits}`, :c:type:`npy_float{bits}`, - and :c:type:`npy_complex{bits}` + ``npy_int{bits}``, ``npy_uint{bits}``, ``npy_float{bits}``, + and ``npy_complex{bits}`` where ``{bits}`` is the number of bits in the type and can be **8**, **16**, **32**, **64**, 128, and 256 for integer types; 16, **32** @@ -371,6 +431,12 @@ Printf Formatting For help in printing, the following strings are defined as the correct format specifier in printf and related commands. - :c:data:`NPY_LONGLONG_FMT`, :c:data:`NPY_ULONGLONG_FMT`, - :c:data:`NPY_INTP_FMT`, :c:data:`NPY_UINTP_FMT`, - :c:data:`NPY_LONGDOUBLE_FMT` +.. c:macro:: NPY_LONGLONG_FMT + +.. c:macro:: NPY_ULONGLONG_FMT + +.. c:macro:: NPY_INTP_FMT + +.. c:macro:: NPY_UINTP_FMT + +.. c:macro:: NPY_LONGDOUBLE_FMT diff --git a/doc/source/reference/c-api.generalized-ufuncs.rst b/doc/source/reference/c-api/generalized-ufuncs.rst similarity index 74% rename from doc/source/reference/c-api.generalized-ufuncs.rst rename to doc/source/reference/c-api/generalized-ufuncs.rst index dd8cf6558751..b59f077ad67d 100644 --- a/doc/source/reference/c-api.generalized-ufuncs.rst +++ b/doc/source/reference/c-api/generalized-ufuncs.rst @@ -127,34 +127,56 @@ The formal syntax of signatures is as follows:: <Output arguments> ::= <Argument list> <Argument list> ::= nil | <Argument> | <Argument> "," <Argument list> <Argument> ::= "(" <Core dimension list> ")" - <Core dimension list> ::= nil | <Core dimension name> | - <Core dimension name> "," <Core dimension list> - <Core dimension name> ::= valid Python variable name - + <Core dimension list> ::= nil | <Core dimension> | + <Core dimension> "," <Core dimension list> + <Core dimension> ::= <Dimension name> <Dimension modifier> + <Dimension name> ::= valid Python variable name | valid integer + <Dimension modifier> ::= nil | "?" Notes: #. All quotes are for clarity. -#. Core dimensions that share the same name must have the exact same size. +#. Unmodified core dimensions that share the same name must have the same size. Each dimension name typically corresponds to one level of looping in the elementary function's implementation. #. White spaces are ignored. +#. An integer as a dimension name freezes that dimension to the value. +#. If the name is suffixed with the "?" modifier, the dimension is a core + dimension only if it exists on all inputs and outputs that share it; + otherwise it is ignored (and replaced by a dimension of size 1 for the + elementary function). Here are some examples of signatures: -+-------------+------------------------+-----------------------------------+ -| add | ``(),()->()`` | | -+-------------+------------------------+-----------------------------------+ -| inner1d | ``(i),(i)->()`` | | -+-------------+------------------------+-----------------------------------+ -| sum1d | ``(i)->()`` | | -+-------------+------------------------+-----------------------------------+ -| dot2d | ``(m,n),(n,p)->(m,p)`` | matrix multiplication | -+-------------+------------------------+-----------------------------------+ -| outer_inner | ``(i,t),(j,t)->(i,j)`` | inner over the last dimension, | -| | | outer over the second to last, | -| | | and loop/broadcast over the rest. | -+-------------+------------------------+-----------------------------------+ ++-------------+----------------------------+-----------------------------------+ +| name | signature | common usage | ++=============+============================+===================================+ +| add | ``(),()->()`` | binary ufunc | ++-------------+----------------------------+-----------------------------------+ +| sum1d | ``(i)->()`` | reduction | ++-------------+----------------------------+-----------------------------------+ +| inner1d | ``(i),(i)->()`` | vector-vector multiplication | ++-------------+----------------------------+-----------------------------------+ +| matmat | ``(m,n),(n,p)->(m,p)`` | matrix multiplication | ++-------------+----------------------------+-----------------------------------+ +| vecmat | ``(n),(n,p)->(p)`` | vector-matrix multiplication | ++-------------+----------------------------+-----------------------------------+ +| matvec | ``(m,n),(n)->(m)`` | matrix-vector multiplication | ++-------------+----------------------------+-----------------------------------+ +| matmul | ``(m?,n),(n,p?)->(m?,p?)`` | combination of the four above | ++-------------+----------------------------+-----------------------------------+ +| outer_inner | ``(i,t),(j,t)->(i,j)`` | inner over the last dimension, | +| | | outer over the second to last, | +| | | and loop/broadcast over the rest. | ++-------------+----------------------------+-----------------------------------+ +| cross1d | ``(3),(3)->(3)`` | cross product where the last | +| | | dimension is frozen and must be 3 | ++-------------+----------------------------+-----------------------------------+ + +.. _frozen: + +The last is an instance of freezing a core dimension and can be used to +improve ufunc performance C-API for implementing Elementary Functions ------------------------------------------- diff --git a/doc/source/reference/c-api.rst b/doc/source/reference/c-api/index.rst similarity index 88% rename from doc/source/reference/c-api.rst rename to doc/source/reference/c-api/index.rst index b8cbe97b2162..6288ff33bc17 100644 --- a/doc/source/reference/c-api.rst +++ b/doc/source/reference/c-api/index.rst @@ -21,7 +21,7 @@ experience at first. Be assured that the task becomes easier with practice, and you may be surprised at how simple the C-code can be to understand. Even if you don't think you can write C-code from scratch, it is much easier to understand and modify already-written source code -then create it *de novo*. +than create it *de novo*. Python extensions are especially straightforward to understand because they all have a very similar structure. Admittedly, NumPy is not a @@ -40,12 +40,13 @@ code. .. toctree:: :maxdepth: 2 - c-api.types-and-structures - c-api.config - c-api.dtype - c-api.array - c-api.iterator - c-api.ufunc - c-api.generalized-ufuncs - c-api.coremath - c-api.deprecations + types-and-structures + config + dtype + array + iterator + ufunc + generalized-ufuncs + coremath + deprecations + data_memory diff --git a/doc/source/reference/c-api.iterator.rst b/doc/source/reference/c-api/iterator.rst similarity index 70% rename from doc/source/reference/c-api.iterator.rst rename to doc/source/reference/c-api/iterator.rst index 392dcb730a07..83644d8b240b 100644 --- a/doc/source/reference/c-api.iterator.rst +++ b/doc/source/reference/c-api/iterator.rst @@ -110,7 +110,6 @@ number of non-zero elements in an array. /* Increment the iterator to the next inner loop */ } while(iternext(iter)); - NpyIter_Close(iter) /* best practice, not strictly required in this case */ NpyIter_Deallocate(iter); return nonzero_count; @@ -195,7 +194,6 @@ is used to control the memory layout of the allocated result, typically ret = NpyIter_GetOperandArray(iter)[1]; Py_INCREF(ret); - NpyIter_Close(iter); if (NpyIter_Deallocate(iter) != NPY_SUCCEED) { Py_DECREF(ret); return NULL; @@ -314,325 +312,327 @@ Construction and Destruction Flags that may be passed in ``flags``, applying to the whole iterator, are: +.. + dedent the enumeration of flags to avoid missing references sphinx warnings - .. c:var:: NPY_ITER_C_INDEX +.. c:macro:: NPY_ITER_C_INDEX - Causes the iterator to track a raveled flat index matching C - order. This option cannot be used with :c:data:`NPY_ITER_F_INDEX`. + Causes the iterator to track a raveled flat index matching C + order. This option cannot be used with :c:data:`NPY_ITER_F_INDEX`. - .. c:var:: NPY_ITER_F_INDEX +.. c:macro:: NPY_ITER_F_INDEX - Causes the iterator to track a raveled flat index matching Fortran - order. This option cannot be used with :c:data:`NPY_ITER_C_INDEX`. + Causes the iterator to track a raveled flat index matching Fortran + order. This option cannot be used with :c:data:`NPY_ITER_C_INDEX`. + +.. c:macro:: NPY_ITER_MULTI_INDEX - .. c:var:: NPY_ITER_MULTI_INDEX - - Causes the iterator to track a multi-index. - This prevents the iterator from coalescing axes to - produce bigger inner loops. If the loop is also not buffered - and no index is being tracked (`NpyIter_RemoveAxis` can be called), - then the iterator size can be ``-1`` to indicate that the iterator - is too large. This can happen due to complex broadcasting and - will result in errors being created when the setting the iterator - range, removing the multi index, or getting the next function. - However, it is possible to remove axes again and use the iterator - normally if the size is small enough after removal. - - .. c:var:: NPY_ITER_EXTERNAL_LOOP - - Causes the iterator to skip iteration of the innermost - loop, requiring the user of the iterator to handle it. - - This flag is incompatible with :c:data:`NPY_ITER_C_INDEX`, - :c:data:`NPY_ITER_F_INDEX`, and :c:data:`NPY_ITER_MULTI_INDEX`. - - .. c:var:: NPY_ITER_DONT_NEGATE_STRIDES - - This only affects the iterator when :c:type:`NPY_KEEPORDER` is - specified for the order parameter. By default with - :c:type:`NPY_KEEPORDER`, the iterator reverses axes which have - negative strides, so that memory is traversed in a forward - direction. This disables this step. Use this flag if you - want to use the underlying memory-ordering of the axes, - but don't want an axis reversed. This is the behavior of - ``numpy.ravel(a, order='K')``, for instance. - - .. c:var:: NPY_ITER_COMMON_DTYPE - - Causes the iterator to convert all the operands to a common - data type, calculated based on the ufunc type promotion rules. - Copying or buffering must be enabled. - - If the common data type is known ahead of time, don't use this - flag. Instead, set the requested dtype for all the operands. - - .. c:var:: NPY_ITER_REFS_OK - - Indicates that arrays with reference types (object - arrays or structured arrays containing an object type) - may be accepted and used in the iterator. If this flag - is enabled, the caller must be sure to check whether - :c:func:`NpyIter_IterationNeedsAPI(iter)` is true, in which case - it may not release the GIL during iteration. - - .. c:var:: NPY_ITER_ZEROSIZE_OK - - Indicates that arrays with a size of zero should be permitted. - Since the typical iteration loop does not naturally work with - zero-sized arrays, you must check that the IterSize is larger - than zero before entering the iteration loop. - Currently only the operands are checked, not a forced shape. - - .. c:var:: NPY_ITER_REDUCE_OK - - Permits writeable operands with a dimension with zero - stride and size greater than one. Note that such operands - must be read/write. - - When buffering is enabled, this also switches to a special - buffering mode which reduces the loop length as necessary to - not trample on values being reduced. - - Note that if you want to do a reduction on an automatically - allocated output, you must use :c:func:`NpyIter_GetOperandArray` - to get its reference, then set every value to the reduction - unit before doing the iteration loop. In the case of a - buffered reduction, this means you must also specify the - flag :c:data:`NPY_ITER_DELAY_BUFALLOC`, then reset the iterator - after initializing the allocated operand to prepare the - buffers. - - .. c:var:: NPY_ITER_RANGED - - Enables support for iteration of sub-ranges of the full - ``iterindex`` range ``[0, NpyIter_IterSize(iter))``. Use - the function :c:func:`NpyIter_ResetToIterIndexRange` to specify - a range for iteration. - - This flag can only be used with :c:data:`NPY_ITER_EXTERNAL_LOOP` - when :c:data:`NPY_ITER_BUFFERED` is enabled. This is because - without buffering, the inner loop is always the size of the - innermost iteration dimension, and allowing it to get cut up - would require special handling, effectively making it more - like the buffered version. - - .. c:var:: NPY_ITER_BUFFERED - - Causes the iterator to store buffering data, and use buffering - to satisfy data type, alignment, and byte-order requirements. - To buffer an operand, do not specify the :c:data:`NPY_ITER_COPY` - or :c:data:`NPY_ITER_UPDATEIFCOPY` flags, because they will - override buffering. Buffering is especially useful for Python - code using the iterator, allowing for larger chunks - of data at once to amortize the Python interpreter overhead. - - If used with :c:data:`NPY_ITER_EXTERNAL_LOOP`, the inner loop - for the caller may get larger chunks than would be possible - without buffering, because of how the strides are laid out. - - Note that if an operand is given the flag :c:data:`NPY_ITER_COPY` - or :c:data:`NPY_ITER_UPDATEIFCOPY`, a copy will be made in preference - to buffering. Buffering will still occur when the array was - broadcast so elements need to be duplicated to get a constant - stride. - - In normal buffering, the size of each inner loop is equal - to the buffer size, or possibly larger if - :c:data:`NPY_ITER_GROWINNER` is specified. If - :c:data:`NPY_ITER_REDUCE_OK` is enabled and a reduction occurs, - the inner loops may become smaller depending - on the structure of the reduction. - - .. c:var:: NPY_ITER_GROWINNER - - When buffering is enabled, this allows the size of the inner - loop to grow when buffering isn't necessary. This option - is best used if you're doing a straight pass through all the - data, rather than anything with small cache-friendly arrays - of temporary values for each inner loop. - - .. c:var:: NPY_ITER_DELAY_BUFALLOC - - When buffering is enabled, this delays allocation of the - buffers until :c:func:`NpyIter_Reset` or another reset function is - called. This flag exists to avoid wasteful copying of - buffer data when making multiple copies of a buffered - iterator for multi-threaded iteration. - - Another use of this flag is for setting up reduction operations. - After the iterator is created, and a reduction output - is allocated automatically by the iterator (be sure to use - READWRITE access), its value may be initialized to the reduction - unit. Use :c:func:`NpyIter_GetOperandArray` to get the object. - Then, call :c:func:`NpyIter_Reset` to allocate and fill the buffers - with their initial values. - - .. c:var:: NPY_ITER_COPY_IF_OVERLAP - - If any write operand has overlap with any read operand, eliminate all - overlap by making temporary copies (enabling UPDATEIFCOPY for write - operands, if necessary). A pair of operands has overlap if there is - a memory address that contains data common to both arrays. - - Because exact overlap detection has exponential runtime - in the number of dimensions, the decision is made based - on heuristics, which has false positives (needless copies in unusual - cases) but has no false negatives. - - If any read/write overlap exists, this flag ensures the result of the - operation is the same as if all operands were copied. - In cases where copies would need to be made, **the result of the - computation may be undefined without this flag!** + Causes the iterator to track a multi-index. + This prevents the iterator from coalescing axes to + produce bigger inner loops. If the loop is also not buffered + and no index is being tracked (`NpyIter_RemoveAxis` can be called), + then the iterator size can be ``-1`` to indicate that the iterator + is too large. This can happen due to complex broadcasting and + will result in errors being created when the setting the iterator + range, removing the multi index, or getting the next function. + However, it is possible to remove axes again and use the iterator + normally if the size is small enough after removal. + +.. c:macro:: NPY_ITER_EXTERNAL_LOOP + + Causes the iterator to skip iteration of the innermost + loop, requiring the user of the iterator to handle it. + + This flag is incompatible with :c:data:`NPY_ITER_C_INDEX`, + :c:data:`NPY_ITER_F_INDEX`, and :c:data:`NPY_ITER_MULTI_INDEX`. + +.. c:macro:: NPY_ITER_DONT_NEGATE_STRIDES + + This only affects the iterator when :c:type:`NPY_KEEPORDER` is + specified for the order parameter. By default with + :c:type:`NPY_KEEPORDER`, the iterator reverses axes which have + negative strides, so that memory is traversed in a forward + direction. This disables this step. Use this flag if you + want to use the underlying memory-ordering of the axes, + but don't want an axis reversed. This is the behavior of + ``numpy.ravel(a, order='K')``, for instance. + +.. c:macro:: NPY_ITER_COMMON_DTYPE + + Causes the iterator to convert all the operands to a common + data type, calculated based on the ufunc type promotion rules. + Copying or buffering must be enabled. + + If the common data type is known ahead of time, don't use this + flag. Instead, set the requested dtype for all the operands. + +.. c:macro:: NPY_ITER_REFS_OK + + Indicates that arrays with reference types (object + arrays or structured arrays containing an object type) + may be accepted and used in the iterator. If this flag + is enabled, the caller must be sure to check whether + :c:expr:`NpyIter_IterationNeedsAPI(iter)` is true, in which case + it may not release the GIL during iteration. + +.. c:macro:: NPY_ITER_ZEROSIZE_OK + + Indicates that arrays with a size of zero should be permitted. + Since the typical iteration loop does not naturally work with + zero-sized arrays, you must check that the IterSize is larger + than zero before entering the iteration loop. + Currently only the operands are checked, not a forced shape. + +.. c:macro:: NPY_ITER_REDUCE_OK + + Permits writeable operands with a dimension with zero + stride and size greater than one. Note that such operands + must be read/write. + + When buffering is enabled, this also switches to a special + buffering mode which reduces the loop length as necessary to + not trample on values being reduced. + + Note that if you want to do a reduction on an automatically + allocated output, you must use :c:func:`NpyIter_GetOperandArray` + to get its reference, then set every value to the reduction + unit before doing the iteration loop. In the case of a + buffered reduction, this means you must also specify the + flag :c:data:`NPY_ITER_DELAY_BUFALLOC`, then reset the iterator + after initializing the allocated operand to prepare the + buffers. + +.. c:macro:: NPY_ITER_RANGED + + Enables support for iteration of sub-ranges of the full + ``iterindex`` range ``[0, NpyIter_IterSize(iter))``. Use + the function :c:func:`NpyIter_ResetToIterIndexRange` to specify + a range for iteration. + + This flag can only be used with :c:data:`NPY_ITER_EXTERNAL_LOOP` + when :c:data:`NPY_ITER_BUFFERED` is enabled. This is because + without buffering, the inner loop is always the size of the + innermost iteration dimension, and allowing it to get cut up + would require special handling, effectively making it more + like the buffered version. + +.. c:macro:: NPY_ITER_BUFFERED + + Causes the iterator to store buffering data, and use buffering + to satisfy data type, alignment, and byte-order requirements. + To buffer an operand, do not specify the :c:data:`NPY_ITER_COPY` + or :c:data:`NPY_ITER_UPDATEIFCOPY` flags, because they will + override buffering. Buffering is especially useful for Python + code using the iterator, allowing for larger chunks + of data at once to amortize the Python interpreter overhead. + + If used with :c:data:`NPY_ITER_EXTERNAL_LOOP`, the inner loop + for the caller may get larger chunks than would be possible + without buffering, because of how the strides are laid out. + + Note that if an operand is given the flag :c:data:`NPY_ITER_COPY` + or :c:data:`NPY_ITER_UPDATEIFCOPY`, a copy will be made in preference + to buffering. Buffering will still occur when the array was + broadcast so elements need to be duplicated to get a constant + stride. + + In normal buffering, the size of each inner loop is equal + to the buffer size, or possibly larger if + :c:data:`NPY_ITER_GROWINNER` is specified. If + :c:data:`NPY_ITER_REDUCE_OK` is enabled and a reduction occurs, + the inner loops may become smaller depending + on the structure of the reduction. + +.. c:macro:: NPY_ITER_GROWINNER + + When buffering is enabled, this allows the size of the inner + loop to grow when buffering isn't necessary. This option + is best used if you're doing a straight pass through all the + data, rather than anything with small cache-friendly arrays + of temporary values for each inner loop. + +.. c:macro:: NPY_ITER_DELAY_BUFALLOC + + When buffering is enabled, this delays allocation of the + buffers until :c:func:`NpyIter_Reset` or another reset function is + called. This flag exists to avoid wasteful copying of + buffer data when making multiple copies of a buffered + iterator for multi-threaded iteration. + + Another use of this flag is for setting up reduction operations. + After the iterator is created, and a reduction output + is allocated automatically by the iterator (be sure to use + READWRITE access), its value may be initialized to the reduction + unit. Use :c:func:`NpyIter_GetOperandArray` to get the object. + Then, call :c:func:`NpyIter_Reset` to allocate and fill the buffers + with their initial values. + +.. c:macro:: NPY_ITER_COPY_IF_OVERLAP + + If any write operand has overlap with any read operand, eliminate all + overlap by making temporary copies (enabling UPDATEIFCOPY for write + operands, if necessary). A pair of operands has overlap if there is + a memory address that contains data common to both arrays. + + Because exact overlap detection has exponential runtime + in the number of dimensions, the decision is made based + on heuristics, which has false positives (needless copies in unusual + cases) but has no false negatives. + + If any read/write overlap exists, this flag ensures the result of the + operation is the same as if all operands were copied. + In cases where copies would need to be made, **the result of the + computation may be undefined without this flag!** Flags that may be passed in ``op_flags[i]``, where ``0 <= i < nop``: +.. + dedent the enumeration of flags to avoid missing references sphinx warnings + +.. c:macro:: NPY_ITER_READWRITE +.. c:macro:: NPY_ITER_READONLY +.. c:macro:: NPY_ITER_WRITEONLY + + Indicate how the user of the iterator will read or write + to ``op[i]``. Exactly one of these flags must be specified + per operand. Using ``NPY_ITER_READWRITE`` or ``NPY_ITER_WRITEONLY`` + for a user-provided operand may trigger `WRITEBACKIFCOPY`` + semantics. The data will be written back to the original array + when ``NpyIter_Deallocate`` is called. + +.. c:macro:: NPY_ITER_COPY + + Allow a copy of ``op[i]`` to be made if it does not + meet the data type or alignment requirements as specified + by the constructor flags and parameters. + +.. c:macro:: NPY_ITER_UPDATEIFCOPY + + Triggers :c:data:`NPY_ITER_COPY`, and when an array operand + is flagged for writing and is copied, causes the data + in a copy to be copied back to ``op[i]`` when + ``NpyIter_Deallocate`` is called. + + If the operand is flagged as write-only and a copy is needed, + an uninitialized temporary array will be created and then copied + to back to ``op[i]`` on calling ``NpyIter_Deallocate``, instead of + doing the unnecessary copy operation. + +.. c:macro:: NPY_ITER_NBO +.. c:macro:: NPY_ITER_ALIGNED +.. c:macro:: NPY_ITER_CONTIG + + Causes the iterator to provide data for ``op[i]`` + that is in native byte order, aligned according to + the dtype requirements, contiguous, or any combination. + + By default, the iterator produces pointers into the + arrays provided, which may be aligned or unaligned, and + with any byte order. If copying or buffering is not + enabled and the operand data doesn't satisfy the constraints, + an error will be raised. + + The contiguous constraint applies only to the inner loop, + successive inner loops may have arbitrary pointer changes. + + If the requested data type is in non-native byte order, + the NBO flag overrides it and the requested data type is + converted to be in native byte order. + +.. c:macro:: NPY_ITER_ALLOCATE + + This is for output arrays, and requires that the flag + :c:data:`NPY_ITER_WRITEONLY` or :c:data:`NPY_ITER_READWRITE` + be set. If ``op[i]`` is NULL, creates a new array with + the final broadcast dimensions, and a layout matching + the iteration order of the iterator. + + When ``op[i]`` is NULL, the requested data type + ``op_dtypes[i]`` may be NULL as well, in which case it is + automatically generated from the dtypes of the arrays which + are flagged as readable. The rules for generating the dtype + are the same is for UFuncs. Of special note is handling + of byte order in the selected dtype. If there is exactly + one input, the input's dtype is used as is. Otherwise, + if more than one input dtypes are combined together, the + output will be in native byte order. + + After being allocated with this flag, the caller may retrieve + the new array by calling :c:func:`NpyIter_GetOperandArray` and + getting the i-th object in the returned C array. The caller + must call Py_INCREF on it to claim a reference to the array. + +.. c:macro:: NPY_ITER_NO_SUBTYPE + + For use with :c:data:`NPY_ITER_ALLOCATE`, this flag disables + allocating an array subtype for the output, forcing + it to be a straight ndarray. + + TODO: Maybe it would be better to introduce a function + ``NpyIter_GetWrappedOutput`` and remove this flag? + +.. c:macro:: NPY_ITER_NO_BROADCAST + + Ensures that the input or output matches the iteration + dimensions exactly. - .. c:var:: NPY_ITER_READWRITE - .. c:var:: NPY_ITER_READONLY - .. c:var:: NPY_ITER_WRITEONLY +.. c:macro:: NPY_ITER_ARRAYMASK - Indicate how the user of the iterator will read or write - to ``op[i]``. Exactly one of these flags must be specified - per operand. Using ``NPY_ITER_READWRITE`` or ``NPY_ITER_WRITEONLY`` - for a user-provided operand may trigger `WRITEBACKIFCOPY`` - semantics. The data will be written back to the original array - when ``NpyIter_Close`` is called. - - .. c:var:: NPY_ITER_COPY - - Allow a copy of ``op[i]`` to be made if it does not - meet the data type or alignment requirements as specified - by the constructor flags and parameters. + .. versionadded:: 1.7 + + Indicates that this operand is the mask to use for + selecting elements when writing to operands which have + the :c:data:`NPY_ITER_WRITEMASKED` flag applied to them. + Only one operand may have :c:data:`NPY_ITER_ARRAYMASK` flag + applied to it. + + The data type of an operand with this flag should be either + :c:data:`NPY_BOOL`, :c:data:`NPY_MASK`, or a struct dtype + whose fields are all valid mask dtypes. In the latter case, + it must match up with a struct operand being WRITEMASKED, + as it is specifying a mask for each field of that array. + + This flag only affects writing from the buffer back to + the array. This means that if the operand is also + :c:data:`NPY_ITER_READWRITE` or :c:data:`NPY_ITER_WRITEONLY`, + code doing iteration can write to this operand to + control which elements will be untouched and which ones will be + modified. This is useful when the mask should be a combination + of input masks. + +.. c:macro:: NPY_ITER_WRITEMASKED + + .. versionadded:: 1.7 + + This array is the mask for all `writemasked <numpy.nditer>` + operands. Code uses the ``writemasked`` flag which indicates + that only elements where the chosen ARRAYMASK operand is True + will be written to. In general, the iterator does not enforce + this, it is up to the code doing the iteration to follow that + promise. + + When ``writemasked`` flag is used, and this operand is buffered, + this changes how data is copied from the buffer into the array. + A masked copying routine is used, which only copies the + elements in the buffer for which ``writemasked`` + returns true from the corresponding element in the ARRAYMASK + operand. - .. c:var:: NPY_ITER_UPDATEIFCOPY - - Triggers :c:data:`NPY_ITER_COPY`, and when an array operand - is flagged for writing and is copied, causes the data - in a copy to be copied back to ``op[i]`` when ``NpyIter_Close`` is - called. - - If the operand is flagged as write-only and a copy is needed, - an uninitialized temporary array will be created and then copied - to back to ``op[i]`` on calling ``NpyIter_Close``, instead of doing - the unnecessary copy operation. - - .. c:var:: NPY_ITER_NBO - .. c:var:: NPY_ITER_ALIGNED - .. c:var:: NPY_ITER_CONTIG - - Causes the iterator to provide data for ``op[i]`` - that is in native byte order, aligned according to - the dtype requirements, contiguous, or any combination. - - By default, the iterator produces pointers into the - arrays provided, which may be aligned or unaligned, and - with any byte order. If copying or buffering is not - enabled and the operand data doesn't satisfy the constraints, - an error will be raised. - - The contiguous constraint applies only to the inner loop, - successive inner loops may have arbitrary pointer changes. - - If the requested data type is in non-native byte order, - the NBO flag overrides it and the requested data type is - converted to be in native byte order. - - .. c:var:: NPY_ITER_ALLOCATE - - This is for output arrays, and requires that the flag - :c:data:`NPY_ITER_WRITEONLY` or :c:data:`NPY_ITER_READWRITE` - be set. If ``op[i]`` is NULL, creates a new array with - the final broadcast dimensions, and a layout matching - the iteration order of the iterator. - - When ``op[i]`` is NULL, the requested data type - ``op_dtypes[i]`` may be NULL as well, in which case it is - automatically generated from the dtypes of the arrays which - are flagged as readable. The rules for generating the dtype - are the same is for UFuncs. Of special note is handling - of byte order in the selected dtype. If there is exactly - one input, the input's dtype is used as is. Otherwise, - if more than one input dtypes are combined together, the - output will be in native byte order. - - After being allocated with this flag, the caller may retrieve - the new array by calling :c:func:`NpyIter_GetOperandArray` and - getting the i-th object in the returned C array. The caller - must call Py_INCREF on it to claim a reference to the array. - - .. c:var:: NPY_ITER_NO_SUBTYPE - - For use with :c:data:`NPY_ITER_ALLOCATE`, this flag disables - allocating an array subtype for the output, forcing - it to be a straight ndarray. - - TODO: Maybe it would be better to introduce a function - ``NpyIter_GetWrappedOutput`` and remove this flag? - - .. c:var:: NPY_ITER_NO_BROADCAST - - Ensures that the input or output matches the iteration - dimensions exactly. - - .. c:var:: NPY_ITER_ARRAYMASK - - .. versionadded:: 1.7 - - Indicates that this operand is the mask to use for - selecting elements when writing to operands which have - the :c:data:`NPY_ITER_WRITEMASKED` flag applied to them. - Only one operand may have :c:data:`NPY_ITER_ARRAYMASK` flag - applied to it. - - The data type of an operand with this flag should be either - :c:data:`NPY_BOOL`, :c:data:`NPY_MASK`, or a struct dtype - whose fields are all valid mask dtypes. In the latter case, - it must match up with a struct operand being WRITEMASKED, - as it is specifying a mask for each field of that array. - - This flag only affects writing from the buffer back to - the array. This means that if the operand is also - :c:data:`NPY_ITER_READWRITE` or :c:data:`NPY_ITER_WRITEONLY`, - code doing iteration can write to this operand to - control which elements will be untouched and which ones will be - modified. This is useful when the mask should be a combination - of input masks, for example. Mask values can be created - with the :c:func:`NpyMask_Create` function. - - .. c:var:: NPY_ITER_WRITEMASKED - - .. versionadded:: 1.7 - - Indicates that only elements which the operand with - the ARRAYMASK flag indicates are intended to be modified - by the iteration. In general, the iterator does not enforce - this, it is up to the code doing the iteration to follow - that promise. Code can use the :c:func:`NpyMask_IsExposed` - inline function to test whether the mask at a particular - element allows writing. - - When this flag is used, and this operand is buffered, this - changes how data is copied from the buffer into the array. - A masked copying routine is used, which only copies the - elements in the buffer for which :c:func:`NpyMask_IsExposed` - returns true from the corresponding element in the ARRAYMASK - operand. - - .. c:var:: NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE +.. c:macro:: NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE - In memory overlap checks, assume that operands with - ``NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE`` enabled are accessed only - in the iterator order. + In memory overlap checks, assume that operands with + ``NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE`` enabled are accessed only + in the iterator order. - This enables the iterator to reason about data dependency, - possibly avoiding unnecessary copies. - - This flag has effect only if ``NPY_ITER_COPY_IF_OVERLAP`` is enabled - on the iterator. + This enables the iterator to reason about data dependency, + possibly avoiding unnecessary copies. + + This flag has effect only if ``NPY_ITER_COPY_IF_OVERLAP`` is enabled + on the iterator. .. c:function:: NpyIter* NpyIter_AdvancedNew( \ npy_intp nop, PyArrayObject** op, npy_uint32 flags, NPY_ORDER order, \ NPY_CASTING casting, npy_uint32* op_flags, PyArray_Descr** op_dtypes, \ - int oa_ndim, int** op_axes, npy_intp* itershape, npy_intp buffersize) + int oa_ndim, int** op_axes, npy_intp const* itershape, npy_intp buffersize) Extends :c:func:`NpyIter_MultiNew` with several advanced options providing more control over broadcasting and buffering. @@ -709,11 +709,9 @@ Construction and Destruction the functions will pass back errors through it instead of setting a Python exception. - :c:func:`NpyIter_Deallocate` must be called for each copy. One call to - :c:func:`NpyIter_Close` is sufficient to trigger writeback resolution for - all copies since they share buffers. + :c:func:`NpyIter_Deallocate` must be called for each copy. -.. c:function:: int NpyIter_RemoveAxis(NpyIter* iter, int axis)`` +.. c:function:: int NpyIter_RemoveAxis(NpyIter* iter, int axis) Removes an axis from iteration. This requires that :c:data:`NPY_ITER_MULTI_INDEX` was set for iterator creation, and does @@ -744,7 +742,7 @@ Construction and Destruction the iterator. Any cached functions or pointers from the iterator must be retrieved again! - After calling this function, :c:func:`NpyIter_HasMultiIndex(iter)` will + After calling this function, :c:expr:`NpyIter_HasMultiIndex(iter)` will return false. Returns ``NPY_SUCCEED`` or ``NPY_FAIL``. @@ -763,23 +761,9 @@ Construction and Destruction Returns ``NPY_SUCCEED`` or ``NPY_FAIL``. -.. c:function:: int NpyIter_Close(NpyIter* iter) - - Resolves any needed writeback resolution. Should be called before - :c:func::`NpyIter_Deallocate`. After this call it is not safe to use the operands. - When using :c:func:`NpyIter_Copy`, only one call to :c:func:`NpyIter_Close` - is sufficient to resolve any writebacks, since the copies share buffers. - - Returns ``0`` or ``-1`` if unsuccessful. - .. c:function:: int NpyIter_Deallocate(NpyIter* iter) - Deallocates the iterator object. - - :c:func:`NpyIter_Close` should be called before this. If not, and if - writeback is needed, it will be performed at this point in order to maintain - backward-compatibility with older code, and a deprecation warning will be - emitted. Old code should be updated to call `NpyIter_Close` beforehand. + Deallocates the iterator object and resolves any needed writebacks. Returns ``NPY_SUCCEED`` or ``NPY_FAIL``. @@ -885,7 +869,7 @@ Construction and Destruction } while (iternext2(iter2)); } while (iternext1(iter1)); -.. c:function:: int NpyIter_GotoMultiIndex(NpyIter* iter, npy_intp* multi_index) +.. c:function:: int NpyIter_GotoMultiIndex(NpyIter* iter, npy_intp const* multi_index) Adjusts the iterator to point to the ``ndim`` indices pointed to by ``multi_index``. Returns an error if a multi-index @@ -992,19 +976,6 @@ Construction and Destruction Returns the number of operands in the iterator. - When :c:data:`NPY_ITER_USE_MASKNA` is used on an operand, a new - operand is added to the end of the operand list in the iterator - to track that operand's NA mask. Thus, this equals the number - of construction operands plus the number of operands for - which the flag :c:data:`NPY_ITER_USE_MASKNA` was specified. - -.. c:function:: int NpyIter_GetFirstMaskNAOp(NpyIter* iter) - - .. versionadded:: 1.7 - - Returns the index of the first NA mask operand in the array. This - value is equal to the number of operands passed into the constructor. - .. c:function:: npy_intp* NpyIter_GetAxisStrideArray(NpyIter* iter, int axis) Gets the array of strides for the specified axis. Requires that @@ -1041,16 +1012,6 @@ Construction and Destruction that are being iterated. The result points into ``iter``, so the caller does not gain any references to the PyObjects. -.. c:function:: npy_int8* NpyIter_GetMaskNAIndexArray(NpyIter* iter) - - .. versionadded:: 1.7 - - This gives back a pointer to the ``nop`` indices which map - construction operands with :c:data:`NPY_ITER_USE_MASKNA` flagged - to their corresponding NA mask operands and vice versa. For - operands which were not flagged with :c:data:`NPY_ITER_USE_MASKNA`, - this array contains negative values. - .. c:function:: PyObject* NpyIter_GetIterView(NpyIter* iter, npy_intp i) This gives back a reference to a new ndarray view, which is a view @@ -1269,7 +1230,7 @@ Functions For Iteration .. c:function:: npy_intp* NpyIter_GetIndexPtr(NpyIter* iter) This gives back a pointer to the index being tracked, or NULL - if no index is being tracked. It is only useable if one of + if no index is being tracked. It is only usable if one of the flags :c:data:`NPY_ITER_C_INDEX` or :c:data:`NPY_ITER_F_INDEX` were specified during construction. @@ -1307,7 +1268,7 @@ functions provide that information. NPY_MAX_INTP is placed in the stride. Once the iterator is prepared for iteration (after a reset if - :c:data:`NPY_DELAY_BUFALLOC` was used), call this to get the strides + :c:data:`NPY_ITER_DELAY_BUFALLOC` was used), call this to get the strides which may be used to select a fast inner loop function. For example, if the stride is 0, that means the inner loop can always load its value into a variable once, then use the variable throughout the loop, diff --git a/doc/source/reference/c-api.types-and-structures.rst b/doc/source/reference/c-api/types-and-structures.rst similarity index 60% rename from doc/source/reference/c-api.types-and-structures.rst rename to doc/source/reference/c-api/types-and-structures.rst index dcebd1ede4ed..605a4ae718fb 100644 --- a/doc/source/reference/c-api.types-and-structures.rst +++ b/doc/source/reference/c-api/types-and-structures.rst @@ -1,3 +1,4 @@ + ***************************** Python Types and C-Structures ***************************** @@ -6,7 +7,7 @@ Python Types and C-Structures Several new types are defined in the C-code. Most of these are accessible from Python, but a few are not exposed due to their limited -use. Every new Python type has an associated :c:type:`PyObject *<PyObject>` with an +use. Every new Python type has an associated :c:expr:`PyObject *` with an internal structure that includes a pointer to a "method table" that defines how the new object behaves in Python. When you receive a Python object into C code, you always get a pointer to a @@ -25,7 +26,7 @@ By constructing a new Python type you make available a new object for Python. The ndarray object is an example of a new type defined in C. New types are defined in C by two basic steps: -1. creating a C-structure (usually named :c:type:`Py{Name}Object`) that is +1. creating a C-structure (usually named ``Py{Name}Object``) that is binary- compatible with the :c:type:`PyObject` structure itself but holds the additional information needed for that particular object; @@ -57,10 +58,10 @@ types are place holders that allow the array scalars to fit into a hierarchy of actual Python types. -PyArray_Type ------------- +PyArray_Type and PyArrayObject +------------------------------ -.. c:var:: PyArray_Type +.. c:var:: PyTypeObject PyArray_Type The Python type of the ndarray is :c:data:`PyArray_Type`. In C, every ndarray is a pointer to a :c:type:`PyArrayObject` structure. The ob_type @@ -68,14 +69,18 @@ PyArray_Type typeobject. .. c:type:: PyArrayObject + NPY_AO The :c:type:`PyArrayObject` C-structure contains all of the required information for an array. All instances of an ndarray (and its subclasses) will have this structure. For future compatibility, these structure members should normally be accessed using the provided macros. If you need a shorter name, then you can make use - of :c:type:`NPY_AO` which is defined to be equivalent to - :c:type:`PyArrayObject`. + of :c:type:`NPY_AO` (deprecated) which is defined to be equivalent to + :c:type:`PyArrayObject`. Direct access to the struct fields are + deprecated. Use the ``PyArray_*(arr)`` form instead. + As of NumPy 1.20, the size of this struct is not considered part of + the NumPy ABI (see note at the end of the member list). .. code-block:: c @@ -89,84 +94,114 @@ PyArray_Type PyArray_Descr *descr; int flags; PyObject *weakreflist; + /* version dependent private members */ } PyArrayObject; -.. c:macro: PyArrayObject.PyObject_HEAD + .. c:macro:: PyObject_HEAD + + This is needed by all Python objects. It consists of (at least) + a reference count member ( ``ob_refcnt`` ) and a pointer to the + typeobject ( ``ob_type`` ). (Other elements may also be present + if Python was compiled with special options see + Include/object.h in the Python source tree for more + information). The ob_type member points to a Python type + object. + + .. c:member:: char *data + + Accessible via :c:data:`PyArray_DATA`, this data member is a + pointer to the first element of the array. This pointer can + (and normally should) be recast to the data type of the array. - This is needed by all Python objects. It consists of (at least) - a reference count member ( ``ob_refcnt`` ) and a pointer to the - typeobject ( ``ob_type`` ). (Other elements may also be present - if Python was compiled with special options see - Include/object.h in the Python source tree for more - information). The ob_type member points to a Python type - object. + .. c:member:: int nd -.. c:member:: char *PyArrayObject.data + An integer providing the number of dimensions for this + array. When nd is 0, the array is sometimes called a rank-0 + array. Such arrays have undefined dimensions and strides and + cannot be accessed. Macro :c:data:`PyArray_NDIM` defined in + ``ndarraytypes.h`` points to this data member. :c:data:`NPY_MAXDIMS` + is the largest number of dimensions for any array. - A pointer to the first element of the array. This pointer can - (and normally should) be recast to the data type of the array. + .. c:member:: npy_intp dimensions -.. c:member:: int PyArrayObject.nd + An array of integers providing the shape in each dimension as + long as nd :math:`\geq` 1. The integer is always large enough + to hold a pointer on the platform, so the dimension size is + only limited by memory. :c:data:`PyArray_DIMS` is the macro + associated with this data member. - An integer providing the number of dimensions for this - array. When nd is 0, the array is sometimes called a rank-0 - array. Such arrays have undefined dimensions and strides and - cannot be accessed. :c:data:`NPY_MAXDIMS` is the largest number of - dimensions for any array. + .. c:member:: npy_intp *strides -.. c:member:: npy_intp PyArrayObject.dimensions + An array of integers providing for each dimension the number of + bytes that must be skipped to get to the next element in that + dimension. Associated with macro :c:data:`PyArray_STRIDES`. - An array of integers providing the shape in each dimension as - long as nd :math:`\geq` 1. The integer is always large enough - to hold a pointer on the platform, so the dimension size is - only limited by memory. + .. c:member:: PyObject *base -.. c:member:: npy_intp *PyArrayObject.strides + Pointed to by :c:data:`PyArray_BASE`, this member is used to hold a + pointer to another Python object that is related to this array. + There are two use cases: - An array of integers providing for each dimension the number of - bytes that must be skipped to get to the next element in that - dimension. + - If this array does not own its own memory, then base points to the + Python object that owns it (perhaps another array object) + - If this array has the (deprecated) :c:data:`NPY_ARRAY_UPDATEIFCOPY` or + :c:data:`NPY_ARRAY_WRITEBACKIFCOPY` flag set, then this array is a working + copy of a "misbehaved" array. -.. c:member:: PyObject *PyArrayObject.base + When ``PyArray_ResolveWritebackIfCopy`` is called, the array pointed to + by base will be updated with the contents of this array. - This member is used to hold a pointer to another Python object that - is related to this array. There are two use cases: 1) If this array - does not own its own memory, then base points to the Python object - that owns it (perhaps another array object), 2) If this array has - the (deprecated) :c:data:`NPY_ARRAY_UPDATEIFCOPY` or - :c:data:NPY_ARRAY_WRITEBACKIFCOPY`: flag set, then this array is - a working copy of a "misbehaved" array. When - ``PyArray_ResolveWritebackIfCopy`` is called, the array pointed to by base - will be updated with the contents of this array. + .. c:member:: PyArray_Descr *descr -.. c:member:: PyArray_Descr *PyArrayObject.descr + A pointer to a data-type descriptor object (see below). The + data-type descriptor object is an instance of a new built-in + type which allows a generic description of memory. There is a + descriptor structure for each data type supported. This + descriptor structure contains useful information about the type + as well as a pointer to a table of function pointers to + implement specific functionality. As the name suggests, it is + associated with the macro :c:data:`PyArray_DESCR`. - A pointer to a data-type descriptor object (see below). The - data-type descriptor object is an instance of a new built-in - type which allows a generic description of memory. There is a - descriptor structure for each data type supported. This - descriptor structure contains useful information about the type - as well as a pointer to a table of function pointers to - implement specific functionality. + .. c:member:: int flags -.. c:member:: int PyArrayObject.flags + Pointed to by the macro :c:data:`PyArray_FLAGS`, this data member represents + the flags indicating how the memory pointed to by data is to be + interpreted. Possible flags are :c:data:`NPY_ARRAY_C_CONTIGUOUS`, + :c:data:`NPY_ARRAY_F_CONTIGUOUS`, :c:data:`NPY_ARRAY_OWNDATA`, + :c:data:`NPY_ARRAY_ALIGNED`, :c:data:`NPY_ARRAY_WRITEABLE`, + :c:data:`NPY_ARRAY_WRITEBACKIFCOPY`, and :c:data:`NPY_ARRAY_UPDATEIFCOPY`. - Flags indicating how the memory pointed to by data is to be - interpreted. Possible flags are :c:data:`NPY_ARRAY_C_CONTIGUOUS`, - :c:data:`NPY_ARRAY_F_CONTIGUOUS`, :c:data:`NPY_ARRAY_OWNDATA`, - :c:data:`NPY_ARRAY_ALIGNED`, :c:data:`NPY_ARRAY_WRITEABLE`, - :c:data:`NPY_ARRAY_WRITEBACKIFCOPY`, and :c:data:`NPY_ARRAY_UPDATEIFCOPY`. + .. c:member:: PyObject *weakreflist -.. c:member:: PyObject *PyArrayObject.weakreflist + This member allows array objects to have weak references (using the + weakref module). - This member allows array objects to have weak references (using the - weakref module). + .. note:: + Further members are considered private and version dependent. If the size + of the struct is important for your code, special care must be taken. + A possible use-case when this is relevant is subclassing in C. + If your code relies on ``sizeof(PyArrayObject)`` to be constant, + you must add the following check at import time: -PyArrayDescr_Type ------------------ + .. code-block:: c -.. c:var:: PyArrayDescr_Type + if (sizeof(PyArrayObject) < PyArray_Type.tp_basicsize) { + PyErr_SetString(PyExc_ImportError, + "Binary incompatibility with NumPy, must recompile/update X."); + return NULL; + } + + To ensure that your code does not have to be compiled for a specific + NumPy version, you may add a constant, leaving room for changes in NumPy. + A solution guaranteed to be compatible with any future NumPy version + requires the use of a runtime calculate offset and allocation size. + + +PyArrayDescr_Type and PyArray_Descr +----------------------------------- + +.. c:var:: PyTypeObject PyArrayDescr_Type The :c:data:`PyArrayDescr_Type` is the built-in type of the data-type-descriptor objects used to describe how the bytes comprising @@ -182,8 +217,18 @@ PyArrayDescr_Type .. c:type:: PyArray_Descr - The format of the :c:type:`PyArray_Descr` structure that lies at the - heart of the :c:data:`PyArrayDescr_Type` is + The :c:type:`PyArray_Descr` structure lies at the heart of the + :c:data:`PyArrayDescr_Type`. While it is described here for + completeness, it should be considered internal to NumPy and manipulated via + ``PyArrayDescr_*`` or ``PyDataType*`` functions and macros. The size of this + structure is subject to change across versions of NumPy. To ensure + compatibility: + + - Never declare a non-pointer instance of the struct + - Never perform pointer arithmetic + - Never use ``sizof(PyArray_Descr)`` + + It has the following structure: .. code-block:: c @@ -193,186 +238,215 @@ PyArrayDescr_Type char kind; char type; char byteorder; - char unused; - int flags; + char flags; int type_num; int elsize; int alignment; PyArray_ArrayDescr *subarray; PyObject *fields; + PyObject *names; PyArray_ArrFuncs *f; + PyObject *metadata; + NpyAuxData *c_metadata; + npy_hash_t hash; } PyArray_Descr; -.. c:member:: PyTypeObject *PyArray_Descr.typeobj + .. c:member:: PyTypeObject *typeobj - Pointer to a typeobject that is the corresponding Python type for - the elements of this array. For the builtin types, this points to - the corresponding array scalar. For user-defined types, this - should point to a user-defined typeobject. This typeobject can - either inherit from array scalars or not. If it does not inherit - from array scalars, then the :c:data:`NPY_USE_GETITEM` and - :c:data:`NPY_USE_SETITEM` flags should be set in the ``flags`` member. + Pointer to a typeobject that is the corresponding Python type for + the elements of this array. For the builtin types, this points to + the corresponding array scalar. For user-defined types, this + should point to a user-defined typeobject. This typeobject can + either inherit from array scalars or not. If it does not inherit + from array scalars, then the :c:data:`NPY_USE_GETITEM` and + :c:data:`NPY_USE_SETITEM` flags should be set in the ``flags`` member. -.. c:member:: char PyArray_Descr.kind + .. c:member:: char kind - A character code indicating the kind of array (using the array - interface typestring notation). A 'b' represents Boolean, a 'i' - represents signed integer, a 'u' represents unsigned integer, 'f' - represents floating point, 'c' represents complex floating point, 'S' - represents 8-bit zero-terminated bytes, 'U' represents 32-bit/character - unicode string, and 'V' represents arbitrary. + A character code indicating the kind of array (using the array + interface typestring notation). A 'b' represents Boolean, a 'i' + represents signed integer, a 'u' represents unsigned integer, 'f' + represents floating point, 'c' represents complex floating point, 'S' + represents 8-bit zero-terminated bytes, 'U' represents 32-bit/character + unicode string, and 'V' represents arbitrary. -.. c:member:: char PyArray_Descr.type + .. c:member:: char type - A traditional character code indicating the data type. + A traditional character code indicating the data type. -.. c:member:: char PyArray_Descr.byteorder + .. c:member:: char byteorder - A character indicating the byte-order: '>' (big-endian), '<' (little- - endian), '=' (native), '\|' (irrelevant, ignore). All builtin data- - types have byteorder '='. + A character indicating the byte-order: '>' (big-endian), '<' (little- + endian), '=' (native), '\|' (irrelevant, ignore). All builtin data- + types have byteorder '='. -.. c:member:: int PyArray_Descr.flags + .. c:member:: char flags - A data-type bit-flag that determines if the data-type exhibits object- - array like behavior. Each bit in this member is a flag which are named - as: + A data-type bit-flag that determines if the data-type exhibits object- + array like behavior. Each bit in this member is a flag which are named + as: - .. c:var:: NPY_ITEM_REFCOUNT +.. + dedented to allow internal linking, pending a refactoring - .. c:var:: NPY_ITEM_HASOBJECT +.. c:macro:: NPY_ITEM_REFCOUNT - Indicates that items of this data-type must be reference - counted (using :c:func:`Py_INCREF` and :c:func:`Py_DECREF` ). + Indicates that items of this data-type must be reference + counted (using :c:func:`Py_INCREF` and :c:func:`Py_DECREF` ). - .. c:var:: NPY_LIST_PICKLE + .. c:macro:: NPY_ITEM_HASOBJECT - Indicates arrays of this data-type must be converted to a list - before pickling. + Same as :c:data:`NPY_ITEM_REFCOUNT`. - .. c:var:: NPY_ITEM_IS_POINTER +.. + dedented to allow internal linking, pending a refactoring - Indicates the item is a pointer to some other data-type +.. c:macro:: NPY_LIST_PICKLE - .. c:var:: NPY_NEEDS_INIT + Indicates arrays of this data-type must be converted to a list + before pickling. - Indicates memory for this data-type must be initialized (set - to 0) on creation. +.. c:macro:: NPY_ITEM_IS_POINTER - .. c:var:: NPY_NEEDS_PYAPI + Indicates the item is a pointer to some other data-type - Indicates this data-type requires the Python C-API during - access (so don't give up the GIL if array access is going to - be needed). +.. c:macro:: NPY_NEEDS_INIT - .. c:var:: NPY_USE_GETITEM + Indicates memory for this data-type must be initialized (set + to 0) on creation. - On array access use the ``f->getitem`` function pointer - instead of the standard conversion to an array scalar. Must - use if you don't define an array scalar to go along with - the data-type. +.. c:macro:: NPY_NEEDS_PYAPI - .. c:var:: NPY_USE_SETITEM + Indicates this data-type requires the Python C-API during + access (so don't give up the GIL if array access is going to + be needed). - When creating a 0-d array from an array scalar use - ``f->setitem`` instead of the standard copy from an array - scalar. Must use if you don't define an array scalar to go - along with the data-type. +.. c:macro:: NPY_USE_GETITEM - .. c:var:: NPY_FROM_FIELDS + On array access use the ``f->getitem`` function pointer + instead of the standard conversion to an array scalar. Must + use if you don't define an array scalar to go along with + the data-type. - The bits that are inherited for the parent data-type if these - bits are set in any field of the data-type. Currently ( - :c:data:`NPY_NEEDS_INIT` \| :c:data:`NPY_LIST_PICKLE` \| - :c:data:`NPY_ITEM_REFCOUNT` \| :c:data:`NPY_NEEDS_PYAPI` ). +.. c:macro:: NPY_USE_SETITEM - .. c:var:: NPY_OBJECT_DTYPE_FLAGS + When creating a 0-d array from an array scalar use + ``f->setitem`` instead of the standard copy from an array + scalar. Must use if you don't define an array scalar to go + along with the data-type. - Bits set for the object data-type: ( :c:data:`NPY_LIST_PICKLE` - \| :c:data:`NPY_USE_GETITEM` \| :c:data:`NPY_ITEM_IS_POINTER` \| - :c:data:`NPY_REFCOUNT` \| :c:data:`NPY_NEEDS_INIT` \| - :c:data:`NPY_NEEDS_PYAPI`). + .. c:macro:: NPY_FROM_FIELDS - .. c:function:: PyDataType_FLAGCHK(PyArray_Descr *dtype, int flags) + The bits that are inherited for the parent data-type if these + bits are set in any field of the data-type. Currently ( + :c:data:`NPY_NEEDS_INIT` \| :c:data:`NPY_LIST_PICKLE` \| + :c:data:`NPY_ITEM_REFCOUNT` \| :c:data:`NPY_NEEDS_PYAPI` ). - Return true if all the given flags are set for the data-type - object. + .. c:macro:: NPY_OBJECT_DTYPE_FLAGS - .. c:function:: PyDataType_REFCHK(PyArray_Descr *dtype) + Bits set for the object data-type: ( :c:data:`NPY_LIST_PICKLE` + \| :c:data:`NPY_USE_GETITEM` \| :c:data:`NPY_ITEM_IS_POINTER` \| + :c:data:`NPY_ITEM_REFCOUNT` \| :c:data:`NPY_NEEDS_INIT` \| + :c:data:`NPY_NEEDS_PYAPI`). - Equivalent to :c:func:`PyDataType_FLAGCHK` (*dtype*, - :c:data:`NPY_ITEM_REFCOUNT`). + .. c:function:: int PyDataType_FLAGCHK(PyArray_Descr *dtype, int flags) -.. c:member:: int PyArray_Descr.type_num + Return true if all the given flags are set for the data-type + object. - A number that uniquely identifies the data type. For new data-types, - this number is assigned when the data-type is registered. + .. c:function:: int PyDataType_REFCHK(PyArray_Descr *dtype) -.. c:member:: int PyArray_Descr.elsize + Equivalent to :c:func:`PyDataType_FLAGCHK` (*dtype*, + :c:data:`NPY_ITEM_REFCOUNT`). - For data types that are always the same size (such as long), this - holds the size of the data type. For flexible data types where - different arrays can have a different elementsize, this should be - 0. + .. c:member:: int type_num -.. c:member:: int PyArray_Descr.alignment + A number that uniquely identifies the data type. For new data-types, + this number is assigned when the data-type is registered. - A number providing alignment information for this data type. - Specifically, it shows how far from the start of a 2-element - structure (whose first element is a ``char`` ), the compiler - places an item of this type: ``offsetof(struct {char c; type v;}, - v)`` + .. c:member:: int elsize -.. c:member:: PyArray_ArrayDescr *PyArray_Descr.subarray + For data types that are always the same size (such as long), this + holds the size of the data type. For flexible data types where + different arrays can have a different elementsize, this should be + 0. - If this is non- ``NULL``, then this data-type descriptor is a - C-style contiguous array of another data-type descriptor. In - other-words, each element that this descriptor describes is - actually an array of some other base descriptor. This is most - useful as the data-type descriptor for a field in another - data-type descriptor. The fields member should be ``NULL`` if this - is non- ``NULL`` (the fields member of the base descriptor can be - non- ``NULL`` however). The :c:type:`PyArray_ArrayDescr` structure is - defined using + .. c:member:: int alignment - .. code-block:: c + A number providing alignment information for this data type. + Specifically, it shows how far from the start of a 2-element + structure (whose first element is a ``char`` ), the compiler + places an item of this type: ``offsetof(struct {char c; type v;}, + v)`` - typedef struct { - PyArray_Descr *base; - PyObject *shape; - } PyArray_ArrayDescr; + .. c:member:: PyArray_ArrayDescr *subarray + + If this is non- ``NULL``, then this data-type descriptor is a + C-style contiguous array of another data-type descriptor. In + other-words, each element that this descriptor describes is + actually an array of some other base descriptor. This is most + useful as the data-type descriptor for a field in another + data-type descriptor. The fields member should be ``NULL`` if this + is non- ``NULL`` (the fields member of the base descriptor can be + non- ``NULL`` however). + + .. c:type:: PyArray_ArrayDescr + + .. code-block:: c + + typedef struct { + PyArray_Descr *base; + PyObject *shape; + } PyArray_ArrayDescr; + + .. c:member:: PyArray_Descr *base - The elements of this structure are: + The data-type-descriptor object of the base-type. - .. c:member:: PyArray_Descr *PyArray_ArrayDescr.base + .. c:member:: PyObject *shape - The data-type-descriptor object of the base-type. + The shape (always C-style contiguous) of the sub-array as a Python + tuple. - .. c:member:: PyObject *PyArray_ArrayDescr.shape + .. c:member:: PyObject *fields - The shape (always C-style contiguous) of the sub-array as a Python - tuple. + If this is non-NULL, then this data-type-descriptor has fields + described by a Python dictionary whose keys are names (and also + titles if given) and whose values are tuples that describe the + fields. Recall that a data-type-descriptor always describes a + fixed-length set of bytes. A field is a named sub-region of that + total, fixed-length collection. A field is described by a tuple + composed of another data- type-descriptor and a byte + offset. Optionally, the tuple may contain a title which is + normally a Python string. These tuples are placed in this + dictionary keyed by name (and also title if given). + .. c:member:: PyObject *names -.. c:member:: PyObject *PyArray_Descr.fields + An ordered tuple of field names. It is NULL if no field is + defined. - If this is non-NULL, then this data-type-descriptor has fields - described by a Python dictionary whose keys are names (and also - titles if given) and whose values are tuples that describe the - fields. Recall that a data-type-descriptor always describes a - fixed-length set of bytes. A field is a named sub-region of that - total, fixed-length collection. A field is described by a tuple - composed of another data- type-descriptor and a byte - offset. Optionally, the tuple may contain a title which is - normally a Python string. These tuples are placed in this - dictionary keyed by name (and also title if given). + .. c:member:: PyArray_ArrFuncs *f -.. c:member:: PyArray_ArrFuncs *PyArray_Descr.f + A pointer to a structure containing functions that the type needs + to implement internal features. These functions are not the same + thing as the universal functions (ufuncs) described later. Their + signatures can vary arbitrarily. - A pointer to a structure containing functions that the type needs - to implement internal features. These functions are not the same - thing as the universal functions (ufuncs) described later. Their - signatures can vary arbitrarily. + .. c:member:: PyObject *metadata + + Metadata about this dtype. + + .. c:member:: NpyAuxData *c_metadata + + Metadata specific to the C implementation + of the particular dtype. Added for NumPy 1.7.0. + + .. c:type:: npy_hash_t + .. c:member:: npy_hash_t *hash + + Currently unused. Reserved for future use in caching + hash values. .. c:type:: PyArray_ArrFuncs @@ -408,9 +482,9 @@ PyArrayDescr_Type PyArray_ScalarKindFunc *scalarkind; int **cancastscalarkindto; int *cancastto; - PyArray_FastClipFunc *fastclip; - PyArray_FastPutmaskFunc *fastputmask; - PyArray_FastTakeFunc *fasttake; + PyArray_FastClipFunc *fastclip; /* deprecated */ + PyArray_FastPutmaskFunc *fastputmask; /* deprecated */ + PyArray_FastTakeFunc *fasttake; /* deprecated */ PyArray_ArgFunc *argmin; } PyArray_ArrFuncs; @@ -498,20 +572,19 @@ PyArrayDescr_Type and ``is2`` *bytes*, respectively. This function requires behaved (though not necessarily contiguous) memory. - .. c:member:: int scanfunc(FILE* fd, void* ip , void* sep , void* arr) + .. c:member:: int scanfunc(FILE* fd, void* ip, void* arr) A pointer to a function that scans (scanf style) one element of the corresponding type from the file descriptor ``fd`` into the array memory pointed to by ``ip``. The array is assumed - to be behaved. If ``sep`` is not NULL, then a separator string - is also scanned from the file before returning. The last - argument ``arr`` is the array to be scanned into. A 0 is - returned if the scan is successful. A negative number - indicates something went wrong: -1 means the end of file was - reached before the separator string could be scanned, -4 means - that the end of file was reached before the element could be - scanned, and -3 means that the element could not be - interpreted from the format string. Requires a behaved array. + to be behaved. + The last argument ``arr`` is the array to be scanned into. + Returns number of receiving arguments successfully assigned (which + may be zero in case a matching failure occurred before the first + receiving argument was assigned), or EOF if input failure occurs + before the first receiving argument was assigned. + This function should be called without holding the Python GIL, and + has to grab it for error reporting. .. c:member:: int fromstr(char* str, void* ip, char** endptr, void* arr) @@ -522,8 +595,10 @@ PyArrayDescr_Type string. The last argument ``arr`` is the array into which ip points (needed for variable-size data- types). Returns 0 on success or -1 on failure. Requires a behaved array. + This function should be called without holding the Python GIL, and + has to grab it for error reporting. - .. c:member:: Bool nonzero(void* data, void* arr) + .. c:member:: npy_bool nonzero(void* data, void* arr) A pointer to a function that returns TRUE if the item of ``arr`` pointed to by ``data`` is nonzero. This function can @@ -567,7 +642,8 @@ PyArrayDescr_Type Either ``NULL`` or a dictionary containing low-level casting functions for user- defined data-types. Each function is - wrapped in a :c:type:`PyCObject *` and keyed by the data-type number. + wrapped in a :c:expr:`PyCapsule *` and keyed by + the data-type number. .. c:member:: NPY_SCALARKIND scalarkind(PyArrayObject* arr) @@ -596,6 +672,16 @@ PyArrayDescr_Type .. c:member:: void fastclip( \ void *in, npy_intp n_in, void *min, void *max, void *out) + .. deprecated:: 1.17 + The use of this function will give a deprecation warning when + ``np.clip``. Instead of this function, the datatype must + instead use ``PyUFunc_RegisterLoopForDescr`` to attach a custom + loop to ``np.core.umath.clip``, ``np.minimum``, and ``np.maximum``. + + .. deprecated:: 1.19 + Setting this function is deprecated and should always be ``NULL``, + if set, it will be ignored. + A function that reads ``n_in`` items from ``in``, and writes to ``out`` the read value if it is within the limits pointed to by ``min`` and ``max``, or the corresponding limit if outside. The @@ -605,6 +691,10 @@ PyArrayDescr_Type .. c:member:: void fastputmask( \ void *in, void *mask, npy_intp n_in, void *values, npy_intp nv) + .. deprecated:: 1.19 + Setting this function is deprecated and should always be ``NULL``, + if set, it will be ignored. + A function that takes a pointer ``in`` to an array of ``n_in`` items, a pointer ``mask`` to an array of ``n_in`` boolean values, and a pointer ``vals`` to an array of ``nv`` items. @@ -617,6 +707,10 @@ PyArrayDescr_Type npy_intp n_outer, npy_intp m_middle, npy_intp nelem, \ NPY_CLIPMODE clipmode) + .. deprecated:: 1.19 + Setting this function is deprecated and should always be ``NULL``, + if set, it will be ignored. + A function that takes a pointer ``src`` to a C contiguous, behaved segment, interpreted as a 3-dimensional array of shape ``(n_outer, nindarray, nelem)``, a pointer ``indarray`` to a @@ -643,27 +737,30 @@ PyArrayDescr_Type The :c:data:`PyArray_Type` typeobject implements many of the features of -Python objects including the tp_as_number, tp_as_sequence, -tp_as_mapping, and tp_as_buffer interfaces. The rich comparison -(tp_richcompare) is also used along with new-style attribute lookup -for methods (tp_methods) and properties (tp_getset). The -:c:data:`PyArray_Type` can also be sub-typed. +:c:type:`Python objects <PyTypeObject>` including the :c:member:`tp_as_number +<PyTypeObject.tp_as_number>`, :c:member:`tp_as_sequence +<PyTypeObject.tp_as_sequence>`, :c:member:`tp_as_mapping +<PyTypeObject.tp_as_mapping>`, and :c:member:`tp_as_buffer +<PyTypeObject.tp_as_buffer>` interfaces. The :c:type:`rich comparison +<richcmpfunc>`) is also used along with new-style attribute lookup for +member (:c:member:`tp_members <PyTypeObject.tp_members>`) and properties +(:c:member:`tp_getset <PyTypeObject.tp_getset>`). +The :c:data:`PyArray_Type` can also be sub-typed. .. tip:: - The tp_as_number methods use a generic approach to call whatever - function has been registered for handling the operation. The - function PyNumeric_SetOps(..) can be used to register functions to - handle particular mathematical operations (for all arrays). When - the umath module is imported, it sets the numeric operations for - all arrays to the corresponding ufuncs. The tp_str and tp_repr - methods can also be altered using PyString_SetStringFunction(...). + The ``tp_as_number`` methods use a generic approach to call whatever + function has been registered for handling the operation. When the + ``_multiarray_umath module`` is imported, it sets the numeric operations + for all arrays to the corresponding ufuncs. This choice can be changed with + :c:func:`PyUFunc_ReplaceLoopBySignature` The ``tp_str`` and ``tp_repr`` + methods can also be altered using :c:func:`PyArray_SetStringFunction`. -PyUFunc_Type ------------- +PyUFunc_Type and PyUFuncObject +------------------------------ -.. c:var:: PyUFunc_Type +.. c:var:: PyTypeObject PyUFunc_Type The ufunc object is implemented by creation of the :c:data:`PyUFunc_Type`. It is a very simple type that implements only @@ -683,7 +780,16 @@ PyUFunc_Type The core of the ufunc is the :c:type:`PyUFuncObject` which contains all the information needed to call the underlying C-code loops that - perform the actual work. It has the following structure: + perform the actual work. While it is described here for completeness, it + should be considered internal to NumPy and manipulated via ``PyUFunc_*`` + functions. The size of this structure is subject to change across versions + of NumPy. To ensure compatibility: + + - Never declare a non-pointer instance of the struct + - Never perform pointer arithmetic + - Never use ``sizeof(PyUFuncObject)`` + + It has the following structure: .. code-block:: c @@ -703,36 +809,52 @@ PyUFunc_Type void *ptr; PyObject *obj; PyObject *userloops; + int core_enabled; + int core_num_dim_ix; + int *core_num_dims; + int *core_dim_ixs; + int *core_offsets; + char *core_signature; + PyUFunc_TypeResolutionFunc *type_resolver; + PyUFunc_LegacyInnerLoopSelectionFunc *legacy_inner_loop_selector; + void *reserved2; npy_uint32 *op_flags; npy_uint32 *iter_flags; + /* new in API version 0x0000000D */ + npy_intp *core_dim_sizes; + npy_uint32 *core_dim_flags; + PyObject *identity_value; + /* Further private slots (size depends on the NumPy version) */ } PyUFuncObject; - .. c:macro: PyUFuncObject.PyObject_HEAD + .. c:macro: PyObject_HEAD required for all Python objects. - .. c:member:: int PyUFuncObject.nin + .. c:member:: int nin The number of input arguments. - .. c:member:: int PyUFuncObject.nout + .. c:member:: int nout The number of output arguments. - .. c:member:: int PyUFuncObject.nargs + .. c:member:: int nargs The total number of arguments (*nin* + *nout*). This must be less than :c:data:`NPY_MAXARGS`. - .. c:member:: int PyUFuncObject.identity + .. c:member:: int identity Either :c:data:`PyUFunc_One`, :c:data:`PyUFunc_Zero`, - :c:data:`PyUFunc_None` or :c:data:`PyUFunc_AllOnes` to indicate + :c:data:`PyUFunc_MinusOne`, :c:data:`PyUFunc_None`, + :c:data:`PyUFunc_ReorderableNone`, or + :c:data:`PyUFunc_IdentityValue` to indicate the identity for this operation. It is only used for a reduce-like call on an empty array. - .. c:member:: void PyUFuncObject.functions(char** args, npy_intp* dims, - npy_intp* steps, void* extradata) + .. c:member:: void functions( \ + char** args, npy_intp* dims, npy_intp* steps, void* extradata) An array of function pointers --- one for each data type supported by the ufunc. This is the vector loop that is called @@ -749,7 +871,7 @@ PyUFunc_Type passed in as *extradata*. The size of this function pointer array is ntypes. - .. c:member:: void **PyUFuncObject.data + .. c:member:: void **data Extra data to be passed to the 1-d vector loops or ``NULL`` if no extra-data is needed. This C-array must be the same size ( @@ -758,18 +880,22 @@ PyUFunc_Type just 1-d vector loops that make use of this extra data to receive a pointer to the actual function to call. - .. c:member:: int PyUFuncObject.ntypes + .. c:member:: int ntypes The number of supported data types for the ufunc. This number specifies how many different 1-d loops (of the builtin data types) are available. - .. c:member:: char *PyUFuncObject.name + .. c:member:: int reserved1 + + Unused. + + .. c:member:: char *name A string name for the ufunc. This is used dynamically to build the __doc\__ attribute of ufuncs. - .. c:member:: char *PyUFuncObject.types + .. c:member:: char *types An array of :math:`nargs \times ntypes` 8-bit type_numbers which contains the type signature for the function for each of @@ -779,24 +905,24 @@ PyUFunc_Type vector loop. These type numbers do not have to be the same type and mixed-type ufuncs are supported. - .. c:member:: char *PyUFuncObject.doc + .. c:member:: char *doc Documentation for the ufunc. Should not contain the function signature as this is generated dynamically when __doc\__ is retrieved. - .. c:member:: void *PyUFuncObject.ptr + .. c:member:: void *ptr Any dynamically allocated memory. Currently, this is used for dynamic ufuncs created from a python function to store room for the types, data, and name members. - .. c:member:: PyObject *PyUFuncObject.obj + .. c:member:: PyObject *obj For ufuncs dynamically created from python functions, this member holds a reference to the underlying Python function. - .. c:member:: PyObject *PyUFuncObject.userloops + .. c:member:: PyObject *userloops A dictionary of user-defined 1-d vector loops (stored as CObject ptrs) for user-defined types. A loop may be registered by the @@ -804,19 +930,92 @@ PyUFunc_Type User defined type numbers are always larger than :c:data:`NPY_USERDEF`. + .. c:member:: int core_enabled + + 0 for scalar ufuncs; 1 for generalized ufuncs + + .. c:member:: int core_num_dim_ix + + Number of distinct core dimension names in the signature + + .. c:member:: int *core_num_dims + + Number of core dimensions of each argument - .. c:member:: npy_uint32 PyUFuncObject.op_flags + .. c:member:: int *core_dim_ixs + + Dimension indices in a flattened form; indices of argument ``k`` are + stored in ``core_dim_ixs[core_offsets[k] : core_offsets[k] + + core_numdims[k]]`` + + .. c:member:: int *core_offsets + + Position of 1st core dimension of each argument in ``core_dim_ixs``, + equivalent to cumsum(``core_num_dims``) + + .. c:member:: char *core_signature + + Core signature string + + .. c:member:: PyUFunc_TypeResolutionFunc *type_resolver + + A function which resolves the types and fills an array with the dtypes + for the inputs and outputs + + .. c:member:: PyUFunc_LegacyInnerLoopSelectionFunc *legacy_inner_loop_selector + + .. deprecated:: 1.22 + + Some fallback support for this slot exists, but will be removed + eventually. A universal function that relied on this will + have to be ported eventually. + See ref:`NEP 41 <NEP41>` and ref:`NEP 43 <NEP43>` + + .. c:member:: void *reserved2 + + For a possible future loop selector with a different signature. + + .. c:member:: npy_uint32 op_flags Override the default operand flags for each ufunc operand. - .. c:member:: npy_uint32 PyUFuncObject.iter_flags + .. c:member:: npy_uint32 iter_flags Override the default nditer flags for the ufunc. -PyArrayIter_Type ----------------- + Added in API version 0x0000000D + + .. c:member:: npy_intp *core_dim_sizes + + For each distinct core dimension, the possible + :ref:`frozen <frozen>` size if + :c:data:`UFUNC_CORE_DIM_SIZE_INFERRED` is ``0`` + + .. c:member:: npy_uint32 *core_dim_flags + + For each distinct core dimension, a set of ``UFUNC_CORE_DIM*`` flags + +.. + dedented to allow internal linking, pending a refactoring -.. c:var:: PyArrayIter_Type +.. c:macro:: UFUNC_CORE_DIM_CAN_IGNORE + + if the dim name ends in ``?`` + +.. c:macro:: UFUNC_CORE_DIM_SIZE_INFERRED + + if the dim size will be determined from the operands + and not from a :ref:`frozen <frozen>` signature + + .. c:member:: PyObject *identity_value + + Identity for reduction, when :c:member:`PyUFuncObject.identity` + is equal to :c:data:`PyUFunc_IdentityValue`. + +PyArrayIter_Type and PyArrayIterObject +-------------------------------------- + +.. c:var:: PyTypeObject PyArrayIter_Type This is an iterator object that makes it easy to loop over an N-dimensional array. It is the object returned from the flat @@ -857,57 +1056,57 @@ PyArrayIter_Type npy_intp factors[NPY_MAXDIMS]; PyArrayObject *ao; char *dataptr; - Bool contiguous; + npy_bool contiguous; } PyArrayIterObject; - .. c:member:: int PyArrayIterObject.nd_m1 + .. c:member:: int nd_m1 :math:`N-1` where :math:`N` is the number of dimensions in the underlying array. - .. c:member:: npy_intp PyArrayIterObject.index + .. c:member:: npy_intp index The current 1-d index into the array. - .. c:member:: npy_intp PyArrayIterObject.size + .. c:member:: npy_intp size The total size of the underlying array. - .. c:member:: npy_intp *PyArrayIterObject.coordinates + .. c:member:: npy_intp *coordinates An :math:`N` -dimensional index into the array. - .. c:member:: npy_intp *PyArrayIterObject.dims_m1 + .. c:member:: npy_intp *dims_m1 The size of the array minus 1 in each dimension. - .. c:member:: npy_intp *PyArrayIterObject.strides + .. c:member:: npy_intp *strides The strides of the array. How many bytes needed to jump to the next element in each dimension. - .. c:member:: npy_intp *PyArrayIterObject.backstrides + .. c:member:: npy_intp *backstrides How many bytes needed to jump from the end of a dimension back to its beginning. Note that ``backstrides[k] == strides[k] * dims_m1[k]``, but it is stored here as an optimization. - .. c:member:: npy_intp *PyArrayIterObject.factors + .. c:member:: npy_intp *factors This array is used in computing an N-d index from a 1-d index. It contains needed products of the dimensions. - .. c:member:: PyArrayObject *PyArrayIterObject.ao + .. c:member:: PyArrayObject *ao A pointer to the underlying ndarray this iterator was created to represent. - .. c:member:: char *PyArrayIterObject.dataptr + .. c:member:: char *dataptr This member points to an element in the ndarray indicated by the index. - .. c:member:: Bool PyArrayIterObject.contiguous + .. c:member:: npy_bool contiguous This flag is true if the underlying array is :c:data:`NPY_ARRAY_C_CONTIGUOUS`. It is used to simplify @@ -920,13 +1119,13 @@ the internal structure of the iterator object, and merely interact with it through the use of the macros :c:func:`PyArray_ITER_NEXT` (it), :c:func:`PyArray_ITER_GOTO` (it, dest), or :c:func:`PyArray_ITER_GOTO1D` (it, index). All of these macros require the argument *it* to be a -:c:type:`PyArrayIterObject *`. +:c:expr:`PyArrayIterObject *`. -PyArrayMultiIter_Type ---------------------- +PyArrayMultiIter_Type and PyArrayMultiIterObject +------------------------------------------------ -.. c:var:: PyArrayMultiIter_Type +.. c:var:: PyTypeObject PyArrayMultiIter_Type This type provides an iterator that encapsulates the concept of broadcasting. It allows :math:`N` arrays to be broadcast together @@ -954,41 +1153,41 @@ PyArrayMultiIter_Type PyArrayIterObject *iters[NPY_MAXDIMS]; } PyArrayMultiIterObject; - .. c:macro: PyArrayMultiIterObject.PyObject_HEAD + .. c:macro: PyObject_HEAD Needed at the start of every Python object (holds reference count and type identification). - .. c:member:: int PyArrayMultiIterObject.numiter + .. c:member:: int numiter The number of arrays that need to be broadcast to the same shape. - .. c:member:: npy_intp PyArrayMultiIterObject.size + .. c:member:: npy_intp size The total broadcasted size. - .. c:member:: npy_intp PyArrayMultiIterObject.index + .. c:member:: npy_intp index The current (1-d) index into the broadcasted result. - .. c:member:: int PyArrayMultiIterObject.nd + .. c:member:: int nd The number of dimensions in the broadcasted result. - .. c:member:: npy_intp *PyArrayMultiIterObject.dimensions + .. c:member:: npy_intp *dimensions The shape of the broadcasted result (only ``nd`` slots are used). - .. c:member:: PyArrayIterObject **PyArrayMultiIterObject.iters + .. c:member:: PyArrayIterObject **iters An array of iterator objects that holds the iterators for the arrays to be broadcast together. On return, the iterators are adjusted for broadcasting. -PyArrayNeighborhoodIter_Type ----------------------------- +PyArrayNeighborhoodIter_Type and PyArrayNeighborhoodIterObject +-------------------------------------------------------------- -.. c:var:: PyArrayNeighborhoodIter_Type +.. c:var:: PyTypeObject PyArrayNeighborhoodIter_Type This is an iterator object that makes it easy to loop over an N-dimensional neighborhood. @@ -999,10 +1198,35 @@ PyArrayNeighborhoodIter_Type :c:data:`PyArrayNeighborhoodIter_Type` is the :c:type:`PyArrayNeighborhoodIterObject`. -PyArrayFlags_Type ------------------ + .. code-block:: c + + typedef struct { + PyObject_HEAD + int nd_m1; + npy_intp index, size; + npy_intp coordinates[NPY_MAXDIMS] + npy_intp dims_m1[NPY_MAXDIMS]; + npy_intp strides[NPY_MAXDIMS]; + npy_intp backstrides[NPY_MAXDIMS]; + npy_intp factors[NPY_MAXDIMS]; + PyArrayObject *ao; + char *dataptr; + npy_bool contiguous; + npy_intp bounds[NPY_MAXDIMS][2]; + npy_intp limits[NPY_MAXDIMS][2]; + npy_intp limits_sizes[NPY_MAXDIMS]; + npy_iter_get_dataptr_t translate; + npy_intp nd; + npy_intp dimensions[NPY_MAXDIMS]; + PyArrayIterObject* _internal_iter; + char* constant; + int mode; + } PyArrayNeighborhoodIterObject; + +PyArrayFlags_Type and PyArrayFlagsObject +---------------------------------------- -.. c:var:: PyArrayFlags_Type +.. c:var:: PyTypeObject PyArrayFlags_Type When the flags attribute is retrieved from Python, a special builtin object of this type is constructed. This special type makes @@ -1010,6 +1234,16 @@ PyArrayFlags_Type attributes or by accessing them as if the object were a dictionary with the flag names as entries. +.. c:type:: PyArrayFlagsObject + + .. code-block:: c + + typedef struct PyArrayFlagsObject { + PyObject_HEAD + PyObject *arr; + int flags; + } PyArrayFlagsObject; + ScalarArrayTypes ---------------- @@ -1017,7 +1251,7 @@ ScalarArrayTypes There is a Python type for each of the different built-in data types that can be present in the array Most of these are simple wrappers around the corresponding data type in C. The C-names for these types -are :c:data:`Py{TYPE}ArrType_Type` where ``{TYPE}`` can be +are ``Py{TYPE}ArrType_Type`` where ``{TYPE}`` can be **Bool**, **Byte**, **Short**, **Int**, **Long**, **LongLong**, **UByte**, **UShort**, **UInt**, **ULong**, **ULongLong**, @@ -1026,8 +1260,8 @@ are :c:data:`Py{TYPE}ArrType_Type` where ``{TYPE}`` can be **Object**. These type names are part of the C-API and can therefore be created in -extension C-code. There is also a :c:data:`PyIntpArrType_Type` and a -:c:data:`PyUIntpArrType_Type` that are simple substitutes for one of the +extension C-code. There is also a ``PyIntpArrType_Type`` and a +``PyUIntpArrType_Type`` that are simple substitutes for one of the integer types that can hold a pointer on the platform. The structure of these scalar objects is not exposed to C-code. The function :c:func:`PyArray_ScalarAsCtype` (..) can be used to extract the C-type @@ -1062,12 +1296,12 @@ PyArray_Dims The members of this structure are - .. c:member:: npy_intp *PyArray_Dims.ptr + .. c:member:: npy_intp *ptr A pointer to a list of (:c:type:`npy_intp`) integers which usually represent array shape or array strides. - .. c:member:: int PyArray_Dims.len + .. c:member:: int len The length of the list of integers. It is assumed safe to access *ptr* [0] to *ptr* [len-1]. @@ -1096,26 +1330,26 @@ PyArray_Chunk The members are - .. c:macro: PyArray_Chunk.PyObject_HEAD + .. c:macro: PyObject_HEAD Necessary for all Python objects. Included here so that the :c:type:`PyArray_Chunk` structure matches that of the buffer object (at least to the len member). - .. c:member:: PyObject *PyArray_Chunk.base + .. c:member:: PyObject *base The Python object this chunk of memory comes from. Needed so that memory can be accounted for properly. - .. c:member:: void *PyArray_Chunk.ptr + .. c:member:: void *ptr A pointer to the start of the single-segment chunk of memory. - .. c:member:: npy_intp PyArray_Chunk.len + .. c:member:: npy_intp len The length of the segment in bytes. - .. c:member:: int PyArray_Chunk.flags + .. c:member:: int flags Any data flags (*e.g.* :c:data:`NPY_ARRAY_WRITEABLE` ) that should be used to interpret the memory. @@ -1130,13 +1364,13 @@ PyArrayInterface The :c:type:`PyArrayInterface` structure is defined so that NumPy and other extension modules can use the rapid array interface - protocol. The :obj:`__array_struct__` method of an object that + protocol. The :obj:`~object.__array_struct__` method of an object that supports the rapid array interface protocol should return a - :c:type:`PyCObject` that contains a pointer to a :c:type:`PyArrayInterface` + :c:type:`PyCapsule` that contains a pointer to a :c:type:`PyArrayInterface` structure with the relevant details of the array. After the new array is created, the attribute should be ``DECREF``'d which will free the :c:type:`PyArrayInterface` structure. Remember to ``INCREF`` the - object (whose :obj:`__array_struct__` attribute was retrieved) and + object (whose :obj:`~object.__array_struct__` attribute was retrieved) and point the base member of the new :c:type:`PyArrayObject` to this same object. In this way the memory for the array will be managed correctly. @@ -1155,15 +1389,15 @@ PyArrayInterface PyObject *descr; } PyArrayInterface; - .. c:member:: int PyArrayInterface.two + .. c:member:: int two the integer 2 as a sanity check. - .. c:member:: int PyArrayInterface.nd + .. c:member:: int nd the number of dimensions in the array. - .. c:member:: char PyArrayInterface.typekind + .. c:member:: char typekind A character indicating what kind of array is present according to the typestring convention with 't' -> bitfield, 'b' -> Boolean, 'i' -> @@ -1171,11 +1405,11 @@ PyArrayInterface complex floating point, 'O' -> object, 'S' -> (byte-)string, 'U' -> unicode, 'V' -> void. - .. c:member:: int PyArrayInterface.itemsize + .. c:member:: int itemsize The number of bytes each item in the array requires. - .. c:member:: int PyArrayInterface.flags + .. c:member:: int flags Any of the bits :c:data:`NPY_ARRAY_C_CONTIGUOUS` (1), :c:data:`NPY_ARRAY_F_CONTIGUOUS` (2), :c:data:`NPY_ARRAY_ALIGNED` (0x100), @@ -1189,26 +1423,26 @@ PyArrayInterface structure is present (it will be ignored by objects consuming version 2 of the array interface). - .. c:member:: npy_intp *PyArrayInterface.shape + .. c:member:: npy_intp *shape An array containing the size of the array in each dimension. - .. c:member:: npy_intp *PyArrayInterface.strides + .. c:member:: npy_intp *strides An array containing the number of bytes to jump to get to the next element in each dimension. - .. c:member:: void *PyArrayInterface.data + .. c:member:: void *data A pointer *to* the first element of the array. - .. c:member:: PyObject *PyArrayInterface.descr + .. c:member:: PyObject *descr A Python object describing the data-type in more detail (same - as the *descr* key in :obj:`__array_interface__`). This can be + as the *descr* key in :obj:`~object.__array_interface__`). This can be ``NULL`` if *typekind* and *itemsize* provide enough information. This field is also ignored unless - :c:data:`ARR_HAS_DESCR` flag is on in *flags*. + :c:data:`NPY_ARR_HAS_DESCR` flag is on in *flags*. Internally used structures @@ -1241,12 +1475,12 @@ for completeness and assistance in understanding the code. to define a 1-d loop for a ufunc for every defined signature of a user-defined data-type. -.. c:var:: PyArrayMapIter_Type +.. c:var:: PyTypeObject PyArrayMapIter_Type Advanced indexing is handled with this Python type. It is simply a loose wrapper around the C-structure containing the variables needed for advanced array indexing. The associated C-structure, - :c:type:`PyArrayMapIterObject`, is useful if you are trying to + ``PyArrayMapIterObject``, is useful if you are trying to understand the advanced-index mapping code. It is defined in the ``arrayobject.h`` header. This type is not exposed to Python and could be replaced with a C-structure. As a Python type it takes diff --git a/doc/source/reference/c-api.ufunc.rst b/doc/source/reference/c-api/ufunc.rst similarity index 64% rename from doc/source/reference/c-api.ufunc.rst rename to doc/source/reference/c-api/ufunc.rst index 02a35cf56f01..95dc47839e4b 100644 --- a/doc/source/reference/c-api.ufunc.rst +++ b/doc/source/reference/c-api/ufunc.rst @@ -10,18 +10,52 @@ UFunc API Constants --------- -.. c:var:: UFUNC_ERR_{HANDLER} +``UFUNC_ERR_{HANDLER}`` + .. c:macro:: UFUNC_ERR_IGNORE - ``{HANDLER}`` can be **IGNORE**, **WARN**, **RAISE**, or **CALL** + .. c:macro:: UFUNC_ERR_WARN -.. c:var:: UFUNC_{THING}_{ERR} + .. c:macro:: UFUNC_ERR_RAISE - ``{THING}`` can be **MASK**, **SHIFT**, or **FPE**, and ``{ERR}`` can - be **DIVIDEBYZERO**, **OVERFLOW**, **UNDERFLOW**, and **INVALID**. + .. c:macro:: UFUNC_ERR_CALL -.. c:var:: PyUFunc_{VALUE} +``UFUNC_{THING}_{ERR}`` + .. c:macro:: UFUNC_MASK_DIVIDEBYZERO - ``{VALUE}`` can be **One** (1), **Zero** (0), or **None** (-1) + .. c:macro:: UFUNC_MASK_OVERFLOW + + .. c:macro:: UFUNC_MASK_UNDERFLOW + + .. c:macro:: UFUNC_MASK_INVALID + + .. c:macro:: UFUNC_SHIFT_DIVIDEBYZERO + + .. c:macro:: UFUNC_SHIFT_OVERFLOW + + .. c:macro:: UFUNC_SHIFT_UNDERFLOW + + .. c:macro:: UFUNC_SHIFT_INVALID + + .. c:macro:: UFUNC_FPE_DIVIDEBYZERO + + .. c:macro:: UFUNC_FPE_OVERFLOW + + .. c:macro:: UFUNC_FPE_UNDERFLOW + + .. c:macro:: UFUNC_FPE_INVALID + +``PyUFunc_{VALUE}`` + .. c:macro:: PyUFunc_One + + .. c:macro:: PyUFunc_Zero + + .. c:macro:: PyUFunc_MinusOne + + .. c:macro:: PyUFunc_ReorderableNone + + .. c:macro:: PyUFunc_None + + .. c:macro:: PyUFunc_IdentityValue Macros @@ -39,27 +73,65 @@ Macros Used in universal function code to re-acquire the Python GIL if it was released (because loop->obj was not true). -.. c:function:: UFUNC_CHECK_ERROR(loop) - A macro used internally to check for errors and goto fail if - found. This macro requires a fail label in the current code - block. The *loop* variable must have at least members (obj, - errormask, and errorobj). If *loop* ->obj is nonzero, then - :c:func:`PyErr_Occurred` () is called (meaning the GIL must be held). If - *loop* ->obj is zero, then if *loop* ->errormask is nonzero, - :c:func:`PyUFunc_checkfperr` is called with arguments *loop* ->errormask - and *loop* ->errobj. If the result of this check of the IEEE - floating point registers is true then the code redirects to the - fail label which must be defined. +Types +----- + +.. c:type:: PyUFuncGenericFunction + + pointers to functions that actually implement the underlying + (element-by-element) function :math:`N` times with the following + signature: + + .. c:function:: void loopfunc(\ + char** args, npy_intp const *dimensions, npy_intp const *steps, void* data) -.. c:function:: UFUNC_CHECK_STATUS(ret) + *args* - Deprecated: use npy_clear_floatstatus from npy_math.h instead. + An array of pointers to the actual data for the input and output + arrays. The input arguments are given first followed by the output + arguments. - A macro that expands to platform-dependent code. The *ret* - variable can be any integer. The :c:data:`UFUNC_FPE_{ERR}` bits are - set in *ret* according to the status of the corresponding error - flags of the floating point processor. + *dimensions* + + A pointer to the size of the dimension over which this function is + looping. + + *steps* + + A pointer to the number of bytes to jump to get to the + next element in this dimension for each of the input and + output arguments. + + *data* + + Arbitrary data (extra arguments, function names, *etc.* ) + that can be stored with the ufunc and will be passed in + when it is called. + + This is an example of a func specialized for addition of doubles + returning doubles. + + .. code-block:: c + + static void + double_add(char **args, + npy_intp const *dimensions, + npy_intp const *steps, + void *extra) + { + npy_intp i; + npy_intp is1 = steps[0], is2 = steps[1]; + npy_intp os = steps[2], n = dimensions[0]; + char *i1 = args[0], *i2 = args[1], *op = args[2]; + for (i = 0; i < n; i++) { + *((double *)op) = *((double *)i1) + + *((double *)i2); + i1 += is1; + i2 += is2; + op += os; + } + } Functions @@ -83,30 +155,28 @@ Functions :param func: Must to an array of length *ntypes* containing - :c:type:`PyUFuncGenericFunction` items. These items are pointers to - functions that actually implement the underlying - (element-by-element) function :math:`N` times. + :c:type:`PyUFuncGenericFunction` items. :param data: Should be ``NULL`` or a pointer to an array of size *ntypes* . This array may contain arbitrary extra-data to be passed to - the corresponding 1-d loop function in the func array. + the corresponding loop function in the func array. :param types: Length ``(nin + nout) * ntypes`` array of ``char`` encoding the - :ref:`PyArray_Descr.type_num` (built-in only) that the corresponding + `numpy.dtype.num` (built-in only) that the corresponding function in the ``func`` array accepts. For instance, for a comparison ufunc with three ``ntypes``, two ``nin`` and one ``nout``, where the - first function accepts :ref:`npy_int32` and the the second - :ref:`npy_int64`, with both returning :ref:`npy_bool`, ``types`` would + first function accepts `numpy.int32` and the the second + `numpy.int64`, with both returning `numpy.bool_`, ``types`` would be ``(char[]) {5, 5, 0, 7, 7, 0}`` since ``NPY_INT32`` is 5, - ``NPY_INT64`` is 7, and ``NPY_BOOL`` is 0 (on the python side, these - are exposed via :ref:`dtype.num`, i.e., for the example here, - ``dtype(np.int32).num``, ``dtype(np.int64).num``, and - ``dtype(np.bool_).num``, resp.). + ``NPY_INT64`` is 7, and ``NPY_BOOL`` is 0. + + The bit-width names can also be used (e.g. :c:data:`NPY_INT32`, + :c:data:`NPY_COMPLEX128` ) if desired. - :ref:`casting-rules` will be used at runtime to find the first - ``func`` callable by the input/output provided. + :ref:`ufuncs.casting` will be used at runtime to find the first + ``func`` callable by the input/output provided. :param ntypes: How many different data-type-specific functions the ufunc has implemented. @@ -117,13 +187,23 @@ Functions :param nout: The number of outputs + :param identity: + + Either :c:data:`PyUFunc_One`, :c:data:`PyUFunc_Zero`, + :c:data:`PyUFunc_MinusOne`, or :c:data:`PyUFunc_None`. + This specifies what should be returned when + an empty array is passed to the reduce method of the ufunc. + The special value :c:data:`PyUFunc_IdentityValue` may only be used with + the :c:func:`PyUFunc_FromFuncAndDataAndSignatureAndIdentity` method, to + allow an arbitrary python object to be used as the identity. + :param name: - The name for the ufunc. Specifying a name of 'add' or - 'multiply' enables a special behavior for integer-typed - reductions when no dtype is given. If the input type is an - integer (or boolean) data type smaller than the size of the int_ - data type, it will be internally upcast to the int_ (or uint) - data type. + The name for the ufunc as a ``NULL`` terminated string. Specifying + a name of 'add' or 'multiply' enables a special behavior for + integer-typed reductions when no dtype is given. If the input type is an + integer (or boolean) data type smaller than the size of the `numpy.int_` + data type, it will be internally upcast to the `numpy.int_` (or + `numpy.uint`) data type. :param doc: Allows passing in a documentation string to be stored with the @@ -151,6 +231,21 @@ Functions to calling PyUFunc_FromFuncAndData. A copy of the string is made, so the passed in buffer can be freed. +.. c:function:: PyObject* PyUFunc_FromFuncAndDataAndSignatureAndIdentity( \ + PyUFuncGenericFunction *func, void **data, char *types, int ntypes, \ + int nin, int nout, int identity, char *name, char *doc, int unused, \ + char *signature, PyObject *identity_value) + + This function is very similar to `PyUFunc_FromFuncAndDataAndSignature` above, + but has an extra *identity_value* argument, to define an arbitrary identity + for the ufunc when ``identity`` is passed as ``PyUFunc_IdentityValue``. + + :param identity_value: + The identity for the new gufunc. Must be passed as ``NULL`` unless the + ``identity`` argument is ``PyUFunc_IdentityValue``. Setting it to NULL + is equivalent to calling PyUFunc_FromFuncAndDataAndSignature. + + .. c:function:: int PyUFunc_RegisterLoopForType( \ PyUFuncObject* ufunc, int usertype, PyUFuncGenericFunction function, \ int* arg_types, void* data) @@ -188,23 +283,10 @@ Functions signature is an array of data-type numbers indicating the inputs followed by the outputs assumed by the 1-d loop. -.. c:function:: int PyUFunc_GenericFunction( \ - PyUFuncObject* self, PyObject* args, PyObject* kwds, PyArrayObject** mps) - - A generic ufunc call. The ufunc is passed in as *self*, the arguments - to the ufunc as *args* and *kwds*. The *mps* argument is an array of - :c:type:`PyArrayObject` pointers whose values are discarded and which - receive the converted input arguments as well as the ufunc outputs - when success is returned. The user is responsible for managing this - array and receives a new reference for each array in *mps*. The total - number of arrays in *mps* is given by *self* ->nin + *self* ->nout. - - Returns 0 on success, -1 on error. - .. c:function:: int PyUFunc_checkfperr(int errmask, PyObject* errobj) A simple interface to the IEEE error-flag checking support. The - *errmask* argument is a mask of :c:data:`UFUNC_MASK_{ERR}` bitmasks + *errmask* argument is a mask of ``UFUNC_MASK_{ERR}`` bitmasks indicating which errors to check for (and how to check for them). The *errobj* must be a Python tuple with two elements: a string containing the name which will be used in any communication @@ -249,37 +331,37 @@ functions stored in the functions member of the PyUFuncObject structure. .. c:function:: void PyUFunc_f_f_As_d_d( \ - char** args, npy_intp* dimensions, npy_intp* steps, void* func) + char** args, npy_intp const *dimensions, npy_intp const *steps, void* func) .. c:function:: void PyUFunc_d_d( \ - char** args, npy_intp* dimensions, npy_intp* steps, void* func) + char** args, npy_intp const *dimensions, npy_intp const *steps, void* func) .. c:function:: void PyUFunc_f_f( \ - char** args, npy_intp* dimensions, npy_intp* steps, void* func) + char** args, npy_intp const *dimensions, npy_intp const *steps, void* func) .. c:function:: void PyUFunc_g_g( \ - char** args, npy_intp* dimensions, npy_intp* steps, void* func) + char** args, npy_intp const *dimensions, npy_intp const *steps, void* func) .. c:function:: void PyUFunc_F_F_As_D_D( \ - char** args, npy_intp* dimensions, npy_intp* steps, void* func) + char** args, npy_intp const *dimensions, npy_intp const *steps, void* func) .. c:function:: void PyUFunc_F_F( \ - char** args, npy_intp* dimensions, npy_intp* steps, void* func) + char** args, npy_intp const *dimensions, npy_intp const *steps, void* func) .. c:function:: void PyUFunc_D_D( \ - char** args, npy_intp* dimensions, npy_intp* steps, void* func) + char** args, npy_intp const *dimensions, npy_intp const *steps, void* func) .. c:function:: void PyUFunc_G_G( \ - char** args, npy_intp* dimensions, npy_intp* steps, void* func) + char** args, npy_intp const *dimensions, npy_intp const *steps, void* func) .. c:function:: void PyUFunc_e_e( \ - char** args, npy_intp* dimensions, npy_intp* steps, void* func) + char** args, npy_intp const *dimensions, npy_intp const *steps, void* func) .. c:function:: void PyUFunc_e_e_As_f_f( \ - char** args, npy_intp* dimensions, npy_intp* steps, void* func) + char** args, npy_intp const *dimensions, npy_intp const *steps, void* func) .. c:function:: void PyUFunc_e_e_As_d_d( \ - char** args, npy_intp* dimensions, npy_intp* steps, void* func) + char** args, npy_intp const *dimensions, npy_intp const *steps, void* func) Type specific, core 1-d functions for ufuncs where each calculation is obtained by calling a function taking one input @@ -295,37 +377,37 @@ structure. C-function that takes double and returns double. .. c:function:: void PyUFunc_ff_f_As_dd_d( \ - char** args, npy_intp* dimensions, npy_intp* steps, void* func) + char** args, npy_intp const *dimensions, npy_intp const *steps, void* func) .. c:function:: void PyUFunc_ff_f( \ - char** args, npy_intp* dimensions, npy_intp* steps, void* func) + char** args, npy_intp const *dimensions, npy_intp const *steps, void* func) .. c:function:: void PyUFunc_dd_d( \ - char** args, npy_intp* dimensions, npy_intp* steps, void* func) + char** args, npy_intp const *dimensions, npy_intp const *steps, void* func) .. c:function:: void PyUFunc_gg_g( \ - char** args, npy_intp* dimensions, npy_intp* steps, void* func) + char** args, npy_intp const *dimensions, npy_intp const *steps, void* func) .. c:function:: void PyUFunc_FF_F_As_DD_D( \ - char** args, npy_intp* dimensions, npy_intp* steps, void* func) + char** args, npy_intp const *dimensions, npy_intp const *steps, void* func) .. c:function:: void PyUFunc_DD_D( \ - char** args, npy_intp* dimensions, npy_intp* steps, void* func) + char** args, npy_intp const *dimensions, npy_intp const *steps, void* func) .. c:function:: void PyUFunc_FF_F( \ - char** args, npy_intp* dimensions, npy_intp* steps, void* func) + char** args, npy_intp const *dimensions, npy_intp const *steps, void* func) .. c:function:: void PyUFunc_GG_G( \ - char** args, npy_intp* dimensions, npy_intp* steps, void* func) + char** args, npy_intp const *dimensions, npy_intp const *steps, void* func) .. c:function:: void PyUFunc_ee_e( \ - char** args, npy_intp* dimensions, npy_intp* steps, void* func) + char** args, npy_intp const *dimensions, npy_intp const *steps, void* func) .. c:function:: void PyUFunc_ee_e_As_ff_f( \ - char** args, npy_intp* dimensions, npy_intp* steps, void* func) + char** args, npy_intp const *dimensions, npy_intp const *steps, void* func) .. c:function:: void PyUFunc_ee_e_As_dd_d( \ - char** args, npy_intp* dimensions, npy_intp* steps, void* func) + char** args, npy_intp const *dimensions, npy_intp const *steps, void* func) Type specific, core 1-d functions for ufuncs where each calculation is obtained by calling a function taking two input @@ -338,10 +420,10 @@ structure. to use the underlying function that takes a different data type. .. c:function:: void PyUFunc_O_O( \ - char** args, npy_intp* dimensions, npy_intp* steps, void* func) + char** args, npy_intp const *dimensions, npy_intp const *steps, void* func) .. c:function:: void PyUFunc_OO_O( \ - char** args, npy_intp* dimensions, npy_intp* steps, void* func) + char** args, npy_intp const *dimensions, npy_intp const *steps, void* func) One-input, one-output, and two-input, one-output core 1-d functions for the :c:data:`NPY_OBJECT` data type. These functions handle reference @@ -351,7 +433,7 @@ structure. PyObject *)`` for :c:func:`PyUFunc_OO_O`. .. c:function:: void PyUFunc_O_O_method( \ - char** args, npy_intp* dimensions, npy_intp* steps, void* func) + char** args, npy_intp const *dimensions, npy_intp const *steps, void* func) This general purpose 1-d core function assumes that *func* is a string representing a method of the input object. For each @@ -359,7 +441,7 @@ structure. and its *func* method is called returning the result to the output array. .. c:function:: void PyUFunc_OO_O_method( \ - char** args, npy_intp* dimensions, npy_intp* steps, void* func) + char** args, npy_intp const *dimensions, npy_intp const *steps, void* func) This general purpose 1-d core function assumes that *func* is a string representing a method of the input object that takes one @@ -369,7 +451,7 @@ structure. of *args*. .. c:function:: void PyUFunc_On_Om( \ - char** args, npy_intp* dimensions, npy_intp* steps, void* func) + char** args, npy_intp const *dimensions, npy_intp const *steps, void* func) This is the 1-d core function used by the dynamic ufuncs created by umath.frompyfunc(function, nin, nout). In this case *func* is a @@ -394,9 +476,9 @@ structure. Importing the API ----------------- -.. c:var:: PY_UFUNC_UNIQUE_SYMBOL +.. c:macro:: PY_UFUNC_UNIQUE_SYMBOL -.. c:var:: NO_IMPORT_UFUNC +.. c:macro:: NO_IMPORT_UFUNC .. c:function:: void import_ufunc(void) diff --git a/doc/source/reference/distutils.rst b/doc/source/reference/distutils.rst index 289822909896..f201ba66865b 100644 --- a/doc/source/reference/distutils.rst +++ b/doc/source/reference/distutils.rst @@ -13,9 +13,13 @@ distutils, use the :func:`setup <core.setup>` command from :mod:`numpy.distutils.misc_util` that can make it easier to construct keyword arguments to pass to the setup function (by passing the dictionary obtained from the todict() method of the class). More -information is available in the NumPy Distutils Users Guide in -``<site-packages>/numpy/doc/DISTUTILS.txt``. +information is available in the :ref:`distutils-user-guide`. +The choice and location of linked libraries such as BLAS and LAPACK as well as +include paths and other such build options can be specified in a ``site.cfg`` +file located in the NumPy root repository or a ``.numpy-site.cfg`` file in your +home directory. See the ``site.cfg.example`` example file included in the NumPy +repository or sdist for documentation. .. index:: single: distutils @@ -23,39 +27,32 @@ information is available in the NumPy Distutils Users Guide in Modules in :mod:`numpy.distutils` ================================= +.. toctree:: + :maxdepth: 2 -misc_util ---------- + distutils/misc_util -.. module:: numpy.distutils.misc_util + +.. currentmodule:: numpy.distutils .. autosummary:: :toctree: generated/ - get_numpy_include_dirs - dict_append - appendpath - allpath - dot_join - generate_config_py - get_cmd - terminal_has_colors - red_text - green_text - yellow_text - blue_text - cyan_text - cyg2win32 - all_strings - has_f_sources - has_cxx_sources - filter_sources - get_dependencies - is_local_src_dir - get_ext_source_files - get_script_files + ccompiler + ccompiler_opt + cpuinfo.cpu + core.Extension + exec_command + log.set_verbosity + system_info.get_info + system_info.get_standard_file +Configuration class +=================== + +.. currentmodule:: numpy.distutils.misc_util + .. class:: Configuration(package_name=None, parent_name=None, top_path=None, package_path=None, **attrs) Construct a configuration instance for the given package name. If @@ -110,20 +107,6 @@ misc_util .. automethod:: get_info -Other modules -------------- - -.. currentmodule:: numpy.distutils - -.. autosummary:: - :toctree: generated/ - - system_info.get_info - system_info.get_standard_file - cpuinfo.cpu - log.set_verbosity - exec_command - Building Installable C libraries ================================ @@ -135,6 +118,11 @@ install the C library, you just use the method `add_installed_library` instead o `add_library`, which takes the same arguments except for an additional ``install_dir`` argument:: + .. hidden in a comment so as to be included in refguide but not rendered documentation + >>> import numpy.distutils.misc_util + >>> config = np.distutils.misc_util.Configuration(None, '', '.') + >>> with open('foo.c', 'w') as f: pass + >>> config.add_installed_library('foo', sources=['foo.c'], install_dir='lib') npy-pkg-config files @@ -198,8 +186,8 @@ Reusing a C library from another package Info are easily retrieved from the `get_info` function in `numpy.distutils.misc_util`:: - >>> info = get_info('npymath') - >>> config.add_extension('foo', sources=['foo.c'], extra_info=**info) + >>> info = np.distutils.misc_util.get_info('npymath') + >>> config.add_extension('foo', sources=['foo.c'], extra_info=info) An additional list of paths to look for .ini files can be given to `get_info`. @@ -215,102 +203,4 @@ template and placed in the build directory to be used instead. Two forms of template conversion are supported. The first form occurs for files named <file>.ext.src where ext is a recognized Fortran extension (f, f90, f95, f77, for, ftn, pyf). The second form is used -for all other cases. - -.. index:: - single: code generation - -Fortran files -------------- - -This template converter will replicate all **function** and -**subroutine** blocks in the file with names that contain '<...>' -according to the rules in '<...>'. The number of comma-separated words -in '<...>' determines the number of times the block is repeated. What -these words are indicates what that repeat rule, '<...>', should be -replaced with in each block. All of the repeat rules in a block must -contain the same number of comma-separated words indicating the number -of times that block should be repeated. If the word in the repeat rule -needs a comma, leftarrow, or rightarrow, then prepend it with a -backslash ' \'. If a word in the repeat rule matches ' \\<index>' then -it will be replaced with the <index>-th word in the same repeat -specification. There are two forms for the repeat rule: named and -short. - - -Named repeat rule -^^^^^^^^^^^^^^^^^ - -A named repeat rule is useful when the same set of repeats must be -used several times in a block. It is specified using <rule1=item1, -item2, item3,..., itemN>, where N is the number of times the block -should be repeated. On each repeat of the block, the entire -expression, '<...>' will be replaced first with item1, and then with -item2, and so forth until N repeats are accomplished. Once a named -repeat specification has been introduced, the same repeat rule may be -used **in the current block** by referring only to the name -(i.e. <rule1>. - - -Short repeat rule -^^^^^^^^^^^^^^^^^ - -A short repeat rule looks like <item1, item2, item3, ..., itemN>. The -rule specifies that the entire expression, '<...>' should be replaced -first with item1, and then with item2, and so forth until N repeats -are accomplished. - - -Pre-defined names -^^^^^^^^^^^^^^^^^ - -The following predefined named repeat rules are available: - -- <prefix=s,d,c,z> - -- <_c=s,d,c,z> - -- <_t=real, double precision, complex, double complex> - -- <ftype=real, double precision, complex, double complex> - -- <ctype=float, double, complex_float, complex_double> - -- <ftypereal=float, double precision, \\0, \\1> - -- <ctypereal=float, double, \\0, \\1> - - -Other files ------------ - -Non-Fortran files use a separate syntax for defining template blocks -that should be repeated using a variable expansion similar to the -named repeat rules of the Fortran-specific repeats. The template rules -for these files are: - -1. "/\**begin repeat "on a line by itself marks the beginning of - a segment that should be repeated. - -2. Named variable expansions are defined using #name=item1, item2, item3, - ..., itemN# and placed on successive lines. These variables are - replaced in each repeat block with corresponding word. All named - variables in the same repeat block must define the same number of - words. - -3. In specifying the repeat rule for a named variable, item*N is short- - hand for item, item, ..., item repeated N times. In addition, - parenthesis in combination with \*N can be used for grouping several - items that should be repeated. Thus, #name=(item1, item2)*4# is - equivalent to #name=item1, item2, item1, item2, item1, item2, item1, - item2# - -4. "\*/ "on a line by itself marks the end of the variable expansion - naming. The next line is the first line that will be repeated using - the named rules. - -5. Inside the block to be repeated, the variables that should be expanded - are specified as @name@. - -6. "/\**end repeat**/ "on a line by itself marks the previous line - as the last line of the block to be repeated. +for all other cases. See :ref:`templating`. diff --git a/doc/source/reference/distutils/misc_util.rst b/doc/source/reference/distutils/misc_util.rst new file mode 100644 index 000000000000..bbb83a5ab061 --- /dev/null +++ b/doc/source/reference/distutils/misc_util.rst @@ -0,0 +1,7 @@ +distutils.misc_util +=================== + +.. automodule:: numpy.distutils.misc_util + :members: + :undoc-members: + :exclude-members: Configuration diff --git a/doc/source/reference/distutils_guide.rst b/doc/source/reference/distutils_guide.rst new file mode 100644 index 000000000000..081719d16428 --- /dev/null +++ b/doc/source/reference/distutils_guide.rst @@ -0,0 +1,7 @@ +.. _distutils-user-guide: + +NumPy Distutils - Users Guide +============================= + +.. include:: ../../DISTUTILS.rst.txt + :start-line: 6 diff --git a/doc/source/reference/figures/opt-infra.odg b/doc/source/reference/figures/opt-infra.odg new file mode 100644 index 000000000000..a7b36f4076d5 Binary files /dev/null and b/doc/source/reference/figures/opt-infra.odg differ diff --git a/doc/source/reference/figures/opt-infra.png b/doc/source/reference/figures/opt-infra.png new file mode 100644 index 000000000000..e0b6f23169e6 Binary files /dev/null and b/doc/source/reference/figures/opt-infra.png differ diff --git a/doc/source/reference/global_state.rst b/doc/source/reference/global_state.rst new file mode 100644 index 000000000000..20874ceaae49 --- /dev/null +++ b/doc/source/reference/global_state.rst @@ -0,0 +1,96 @@ +.. _global_state: + +************ +Global State +************ + +NumPy has a few import-time, compile-time, or runtime options +which change the global behaviour. +Most of these are related to performance or for debugging +purposes and will not be interesting to the vast majority +of users. + + +Performance-Related Options +=========================== + +Number of Threads used for Linear Algebra +----------------------------------------- + +NumPy itself is normally intentionally limited to a single thread +during function calls, however it does support multiple Python +threads running at the same time. +Note that for performant linear algebra NumPy uses a BLAS backend +such as OpenBLAS or MKL, which may use multiple threads that may +be controlled by environment variables such as ``OMP_NUM_THREADS`` +depending on what is used. +One way to control the number of threads is the package +`threadpoolctl <https://pypi.org/project/threadpoolctl/>`_ + + +Madvise Hugepage on Linux +------------------------- + +When working with very large arrays on modern Linux kernels, +you can experience a significant speedup when +`transparent hugepage <https://www.kernel.org/doc/html/latest/admin-guide/mm/transhuge.html>`_ +is used. +The current system policy for transparent hugepages can be seen by:: + + cat /sys/kernel/mm/transparent_hugepage/enabled + +When set to ``madvise`` NumPy will typically use hugepages for a performance +boost. This behaviour can be modified by setting the environment variable:: + + NUMPY_MADVISE_HUGEPAGE=0 + +or setting it to ``1`` to always enable it. When not set, the default +is to use madvise on Kernels 4.6 and newer. These kernels presumably +experience a large speedup with hugepage support. +This flag is checked at import time. + + +Interoperability-Related Options +================================ + +The array function protocol which allows array-like objects to +hook into the NumPy API is currently enabled by default. +This option exists since NumPy 1.16 and is enabled by default since +NumPy 1.17. It can be disabled using:: + + NUMPY_EXPERIMENTAL_ARRAY_FUNCTION=0 + +See also :py:meth:`numpy.class.__array_function__` for more information. +This flag is checked at import time. + + +Debugging-Related Options +========================= + +Relaxed Strides Checking +------------------------ + +The *compile-time* environment variables:: + + NPY_RELAXED_STRIDES_DEBUG=0 + NPY_RELAXED_STRIDES_CHECKING=1 + +control how NumPy reports contiguity for arrays. +The default that it is enabled and the debug mode is disabled. +This setting should always be enabled. Setting the +debug option can be interesting for testing code written +in C which iterates through arrays that may or may not be +contiguous in memory. +Most users will have no reason to change these; for details +see the :ref:`memory layout <memory-layout>` documentation. + + +Warn if no memory allocation policy when deallocating data +---------------------------------------------------------- + +Some users might pass ownership of the data pointer to the ``ndarray`` by +setting the ``OWNDATA`` flag. If they do this without setting (manually) a +memory allocation policy, the default will be to call ``free``. If +``NUMPY_WARN_IF_NO_MEM_POLICY`` is set to ``"1"``, a ``RuntimeWarning`` will +be emitted. A better alternative is to use a ``PyCapsule`` with a deallocator +and set the ``ndarray.base``. diff --git a/doc/source/reference/index.rst b/doc/source/reference/index.rst index 2140c57f75cc..a18211cca782 100644 --- a/doc/source/reference/index.rst +++ b/doc/source/reference/index.rst @@ -1,5 +1,7 @@ .. _reference: +.. module:: numpy + ############### NumPy Reference ############### @@ -7,12 +9,9 @@ NumPy Reference :Release: |version| :Date: |today| - -.. module:: numpy - This reference manual details functions, modules, and objects included in NumPy, describing what they are and what they do. -For learning how to use NumPy, see also :ref:`user`. +For learning how to use NumPy, see the :ref:`complete documentation <numpy_docs_mainpage>`. .. toctree:: @@ -22,9 +21,12 @@ For learning how to use NumPy, see also :ref:`user`. constants ufuncs routines + typing + global_state distutils - c-api - internals + distutils_guide + c-api/index + simd/simd-optimizations swig @@ -35,4 +37,4 @@ Large parts of this manual originate from Travis E. Oliphant's book `Guide to NumPy <https://archive.org/details/NumPyBook>`__ (which generously entered Public Domain in August 2008). The reference documentation for many of the functions are written by numerous contributors and developers of -NumPy. \ No newline at end of file +NumPy. diff --git a/doc/source/reference/internals.code-explanations.rst b/doc/source/reference/internals.code-explanations.rst index ca81e167659e..d34314610f22 100644 --- a/doc/source/reference/internals.code-explanations.rst +++ b/doc/source/reference/internals.code-explanations.rst @@ -1,617 +1,9 @@ -.. currentmodule:: numpy +:orphan: ************************* NumPy C Code Explanations ************************* - Fanaticism consists of redoubling your efforts when you have forgotten - your aim. - --- *George Santayana* +.. This document has been moved to ../dev/internals.code-explanations.rst. - An authority is a person who can tell you more about something than - you really care to know. - --- *Unknown* - -This Chapter attempts to explain the logic behind some of the new -pieces of code. The purpose behind these explanations is to enable -somebody to be able to understand the ideas behind the implementation -somewhat more easily than just staring at the code. Perhaps in this -way, the algorithms can be improved on, borrowed from, and/or -optimized. - - -Memory model -============ - -.. index:: - pair: ndarray; memory model - -One fundamental aspect of the ndarray is that an array is seen as a -"chunk" of memory starting at some location. The interpretation of -this memory depends on the stride information. For each dimension in -an :math:`N` -dimensional array, an integer (stride) dictates how many -bytes must be skipped to get to the next element in that dimension. -Unless you have a single-segment array, this stride information must -be consulted when traversing through an array. It is not difficult to -write code that accepts strides, you just have to use (char \*) -pointers because strides are in units of bytes. Keep in mind also that -strides do not have to be unit-multiples of the element size. Also, -remember that if the number of dimensions of the array is 0 (sometimes -called a rank-0 array), then the strides and dimensions variables are -NULL. - -Besides the structural information contained in the strides and -dimensions members of the :c:type:`PyArrayObject`, the flags contain -important information about how the data may be accessed. In particular, -the :c:data:`NPY_ARRAY_ALIGNED` flag is set when the memory is on a -suitable boundary according to the data-type array. Even if you have -a contiguous chunk of memory, you cannot just assume it is safe to -dereference a data- type-specific pointer to an element. Only if the -:c:data:`NPY_ARRAY_ALIGNED` flag is set is this a safe operation (on -some platforms it will work but on others, like Solaris, it will cause -a bus error). The :c:data:`NPY_ARRAY_WRITEABLE` should also be ensured -if you plan on writing to the memory area of the array. It is also -possible to obtain a pointer to an unwritable memory area. Sometimes, -writing to the memory area when the :c:data:`NPY_ARRAY_WRITEABLE` flag is not -set will just be rude. Other times it can cause program crashes ( *e.g.* -a data-area that is a read-only memory-mapped file). - - -Data-type encapsulation -======================= - -.. index:: - single: dtype - -The data-type is an important abstraction of the ndarray. Operations -will look to the data-type to provide the key functionality that is -needed to operate on the array. This functionality is provided in the -list of function pointers pointed to by the 'f' member of the -:c:type:`PyArray_Descr` structure. In this way, the number of data-types can be -extended simply by providing a :c:type:`PyArray_Descr` structure with suitable -function pointers in the 'f' member. For built-in types there are some -optimizations that by-pass this mechanism, but the point of the data- -type abstraction is to allow new data-types to be added. - -One of the built-in data-types, the void data-type allows for -arbitrary structured types containing 1 or more fields as elements of the -array. A field is simply another data-type object along with an offset -into the current structured type. In order to support arbitrarily nested -fields, several recursive implementations of data-type access are -implemented for the void type. A common idiom is to cycle through the -elements of the dictionary and perform a specific operation based on -the data-type object stored at the given offset. These offsets can be -arbitrary numbers. Therefore, the possibility of encountering mis- -aligned data must be recognized and taken into account if necessary. - - -N-D Iterators -============= - -.. index:: - single: array iterator - -A very common operation in much of NumPy code is the need to iterate -over all the elements of a general, strided, N-dimensional array. This -operation of a general-purpose N-dimensional loop is abstracted in the -notion of an iterator object. To write an N-dimensional loop, you only -have to create an iterator object from an ndarray, work with the -dataptr member of the iterator object structure and call the macro -:c:func:`PyArray_ITER_NEXT` (it) on the iterator object to move to the next -element. The "next" element is always in C-contiguous order. The macro -works by first special casing the C-contiguous, 1-D, and 2-D cases -which work very simply. - -For the general case, the iteration works by keeping track of a list -of coordinate counters in the iterator object. At each iteration, the -last coordinate counter is increased (starting from 0). If this -counter is smaller than one less than the size of the array in that -dimension (a pre-computed and stored value), then the counter is -increased and the dataptr member is increased by the strides in that -dimension and the macro ends. If the end of a dimension is reached, -the counter for the last dimension is reset to zero and the dataptr is -moved back to the beginning of that dimension by subtracting the -strides value times one less than the number of elements in that -dimension (this is also pre-computed and stored in the backstrides -member of the iterator object). In this case, the macro does not end, -but a local dimension counter is decremented so that the next-to-last -dimension replaces the role that the last dimension played and the -previously-described tests are executed again on the next-to-last -dimension. In this way, the dataptr is adjusted appropriately for -arbitrary striding. - -The coordinates member of the :c:type:`PyArrayIterObject` structure maintains -the current N-d counter unless the underlying array is C-contiguous in -which case the coordinate counting is by-passed. The index member of -the :c:type:`PyArrayIterObject` keeps track of the current flat index of the -iterator. It is updated by the :c:func:`PyArray_ITER_NEXT` macro. - - -Broadcasting -============ - -.. index:: - single: broadcasting - -In Numeric, broadcasting was implemented in several lines of code -buried deep in ufuncobject.c. In NumPy, the notion of broadcasting has -been abstracted so that it can be performed in multiple places. -Broadcasting is handled by the function :c:func:`PyArray_Broadcast`. This -function requires a :c:type:`PyArrayMultiIterObject` (or something that is a -binary equivalent) to be passed in. The :c:type:`PyArrayMultiIterObject` keeps -track of the broadcast number of dimensions and size in each -dimension along with the total size of the broadcast result. It also -keeps track of the number of arrays being broadcast and a pointer to -an iterator for each of the arrays being broadcast. - -The :c:func:`PyArray_Broadcast` function takes the iterators that have already -been defined and uses them to determine the broadcast shape in each -dimension (to create the iterators at the same time that broadcasting -occurs then use the :c:func:`PyMultiIter_New` function). Then, the iterators are -adjusted so that each iterator thinks it is iterating over an array -with the broadcast size. This is done by adjusting the iterators -number of dimensions, and the shape in each dimension. This works -because the iterator strides are also adjusted. Broadcasting only -adjusts (or adds) length-1 dimensions. For these dimensions, the -strides variable is simply set to 0 so that the data-pointer for the -iterator over that array doesn't move as the broadcasting operation -operates over the extended dimension. - -Broadcasting was always implemented in Numeric using 0-valued strides -for the extended dimensions. It is done in exactly the same way in -NumPy. The big difference is that now the array of strides is kept -track of in a :c:type:`PyArrayIterObject`, the iterators involved in a -broadcast result are kept track of in a :c:type:`PyArrayMultiIterObject`, -and the :c:func:`PyArray_BroadCast` call implements the broad-casting rules. - - -Array Scalars -============= - -.. index:: - single: array scalars - -The array scalars offer a hierarchy of Python types that allow a one- -to-one correspondence between the data-type stored in an array and the -Python-type that is returned when an element is extracted from the -array. An exception to this rule was made with object arrays. Object -arrays are heterogeneous collections of arbitrary Python objects. When -you select an item from an object array, you get back the original -Python object (and not an object array scalar which does exist but is -rarely used for practical purposes). - -The array scalars also offer the same methods and attributes as arrays -with the intent that the same code can be used to support arbitrary -dimensions (including 0-dimensions). The array scalars are read-only -(immutable) with the exception of the void scalar which can also be -written to so that structured array field setting works more naturally -(a[0]['f1'] = ``value`` ). - - -Indexing -======== - -.. index:: - single: indexing - -All python indexing operations ``arr[index]`` are organized by first preparing -the index and finding the index type. The supported index types are: - -* integer -* newaxis -* slice -* ellipsis -* integer arrays/array-likes (fancy) -* boolean (single boolean array); if there is more than one boolean array as - index or the shape does not match exactly, the boolean array will be - converted to an integer array instead. -* 0-d boolean (and also integer); 0-d boolean arrays are a special - case which has to be handled in the advanced indexing code. They signal - that a 0-d boolean array had to be interpreted as an integer array. - -As well as the scalar array special case signaling that an integer array -was interpreted as an integer index, which is important because an integer -array index forces a copy but is ignored if a scalar is returned (full integer -index). The prepared index is guaranteed to be valid with the exception of -out of bound values and broadcasting errors for advanced indexing. This -includes that an ellipsis is added for incomplete indices for example when -a two dimensional array is indexed with a single integer. - -The next step depends on the type of index which was found. If all -dimensions are indexed with an integer a scalar is returned or set. A -single boolean indexing array will call specialized boolean functions. -Indices containing an ellipsis or slice but no advanced indexing will -always create a view into the old array by calculating the new strides and -memory offset. This view can then either be returned or, for assignments, -filled using :c:func:`PyArray_CopyObject`. Note that `PyArray_CopyObject` -may also be called on temporary arrays in other branches to support -complicated assignments when the array is of object dtype. - -Advanced indexing ------------------ - -By far the most complex case is advanced indexing, which may or may not be -combined with typical view based indexing. Here integer indices are -interpreted as view based. Before trying to understand this, you may want -to make yourself familiar with its subtleties. The advanced indexing code -has three different branches and one special case: - -* There is one indexing array and it, as well as the assignment array, can - be iterated trivially. For example they may be contiguous. Also the - indexing array must be of `intp` type and the value array in assignments - should be of the correct type. This is purely a fast path. -* There are only integer array indices so that no subarray exists. -* View based and advanced indexing is mixed. In this case the view based - indexing defines a collection of subarrays that are combined by the - advanced indexing. For example, ``arr[[1, 2, 3], :]`` is created by - vertically stacking the subarrays ``arr[1, :]``, ``arr[2,:]``, and - ``arr[3, :]``. -* There is a subarray but it has exactly one element. This case can be handled - as if there is no subarray, but needs some care during setup. - -Deciding what case applies, checking broadcasting, and determining the kind -of transposition needed are all done in `PyArray_MapIterNew`. After setting -up, there are two cases. If there is no subarray or it only has one -element, no subarray iteration is necessary and an iterator is prepared -which iterates all indexing arrays *as well as* the result or value array. -If there is a subarray, there are three iterators prepared. One for the -indexing arrays, one for the result or value array (minus its subarray), -and one for the subarrays of the original and the result/assignment array. -The first two iterators give (or allow calculation) of the pointers into -the start of the subarray, which then allows to restart the subarray -iteration. - -When advanced indices are next to each other transposing may be necessary. -All necessary transposing is handled by :c:func:`PyArray_MapIterSwapAxes` and -has to be handled by the caller unless `PyArray_MapIterNew` is asked to -allocate the result. - -After preparation, getting and setting is relatively straight forward, -although the different modes of iteration need to be considered. Unless -there is only a single indexing array during item getting, the validity of -the indices is checked beforehand. Otherwise it is handled in the inner -loop itself for optimization. - - -Universal Functions -=================== - -.. index:: - single: ufunc - -Universal functions are callable objects that take :math:`N` inputs -and produce :math:`M` outputs by wrapping basic 1-D loops that work -element-by-element into full easy-to use functions that seamlessly -implement broadcasting, type-checking and buffered coercion, and -output-argument handling. New universal functions are normally created -in C, although there is a mechanism for creating ufuncs from Python -functions (:func:`frompyfunc`). The user must supply a 1-D loop that -implements the basic function taking the input scalar values and -placing the resulting scalars into the appropriate output slots as -explained in implementation. - - -Setup ------ - -Every ufunc calculation involves some overhead related to setting up -the calculation. The practical significance of this overhead is that -even though the actual calculation of the ufunc is very fast, you will -be able to write array and type-specific code that will work faster -for small arrays than the ufunc. In particular, using ufuncs to -perform many calculations on 0-D arrays will be slower than other -Python-based solutions (the silently-imported scalarmath module exists -precisely to give array scalars the look-and-feel of ufunc based -calculations with significantly reduced overhead). - -When a ufunc is called, many things must be done. The information -collected from these setup operations is stored in a loop-object. This -loop object is a C-structure (that could become a Python object but is -not initialized as such because it is only used internally). This loop -object has the layout needed to be used with PyArray_Broadcast so that -the broadcasting can be handled in the same way as it is handled in -other sections of code. - -The first thing done is to look-up in the thread-specific global -dictionary the current values for the buffer-size, the error mask, and -the associated error object. The state of the error mask controls what -happens when an error condition is found. It should be noted that -checking of the hardware error flags is only performed after each 1-D -loop is executed. This means that if the input and output arrays are -contiguous and of the correct type so that a single 1-D loop is -performed, then the flags may not be checked until all elements of the -array have been calculated. Looking up these values in a thread- -specific dictionary takes time which is easily ignored for all but -very small arrays. - -After checking, the thread-specific global variables, the inputs are -evaluated to determine how the ufunc should proceed and the input and -output arrays are constructed if necessary. Any inputs which are not -arrays are converted to arrays (using context if necessary). Which of -the inputs are scalars (and therefore converted to 0-D arrays) is -noted. - -Next, an appropriate 1-D loop is selected from the 1-D loops available -to the ufunc based on the input array types. This 1-D loop is selected -by trying to match the signature of the data-types of the inputs -against the available signatures. The signatures corresponding to -built-in types are stored in the types member of the ufunc structure. -The signatures corresponding to user-defined types are stored in a -linked-list of function-information with the head element stored as a -``CObject`` in the userloops dictionary keyed by the data-type number -(the first user-defined type in the argument list is used as the key). -The signatures are searched until a signature is found to which the -input arrays can all be cast safely (ignoring any scalar arguments -which are not allowed to determine the type of the result). The -implication of this search procedure is that "lesser types" should be -placed below "larger types" when the signatures are stored. If no 1-D -loop is found, then an error is reported. Otherwise, the argument_list -is updated with the stored signature --- in case casting is necessary -and to fix the output types assumed by the 1-D loop. - -If the ufunc has 2 inputs and 1 output and the second input is an -Object array then a special-case check is performed so that -NotImplemented is returned if the second input is not an ndarray, has -the __array_priority\__ attribute, and has an __r{op}\__ special -method. In this way, Python is signaled to give the other object a -chance to complete the operation instead of using generic object-array -calculations. This allows (for example) sparse matrices to override -the multiplication operator 1-D loop. - -For input arrays that are smaller than the specified buffer size, -copies are made of all non-contiguous, mis-aligned, or out-of- -byteorder arrays to ensure that for small arrays, a single loop is -used. Then, array iterators are created for all the input arrays and -the resulting collection of iterators is broadcast to a single shape. - -The output arguments (if any) are then processed and any missing -return arrays are constructed. If any provided output array doesn't -have the correct type (or is mis-aligned) and is smaller than the -buffer size, then a new output array is constructed with the special -:c:data:`WRITEBACKIFCOPY` flag set. At the end of the function, -:c:func:`PyArray_ResolveWritebackIfCopy` is called so that -its contents will be copied back into the output array. -Iterators for the output arguments are then processed. - -Finally, the decision is made about how to execute the looping -mechanism to ensure that all elements of the input arrays are combined -to produce the output arrays of the correct type. The options for loop -execution are one-loop (for contiguous, aligned, and correct data -type), strided-loop (for non-contiguous but still aligned and correct -data type), and a buffered loop (for mis-aligned or incorrect data -type situations). Depending on which execution method is called for, -the loop is then setup and computed. - - -Function call -------------- - -This section describes how the basic universal function computation loop is -setup and executed for each of the three different kinds of execution. If -:c:data:`NPY_ALLOW_THREADS` is defined during compilation, then as long as -no object arrays are involved, the Python Global Interpreter Lock (GIL) is -released prior to calling the loops. It is re-acquired if necessary to -handle error conditions. The hardware error flags are checked only after -the 1-D loop is completed. - - -One Loop -^^^^^^^^ - -This is the simplest case of all. The ufunc is executed by calling the -underlying 1-D loop exactly once. This is possible only when we have -aligned data of the correct type (including byte-order) for both input -and output and all arrays have uniform strides (either contiguous, -0-D, or 1-D). In this case, the 1-D computational loop is called once -to compute the calculation for the entire array. Note that the -hardware error flags are only checked after the entire calculation is -complete. - - -Strided Loop -^^^^^^^^^^^^ - -When the input and output arrays are aligned and of the correct type, -but the striding is not uniform (non-contiguous and 2-D or larger), -then a second looping structure is employed for the calculation. This -approach converts all of the iterators for the input and output -arguments to iterate over all but the largest dimension. The inner -loop is then handled by the underlying 1-D computational loop. The -outer loop is a standard iterator loop on the converted iterators. The -hardware error flags are checked after each 1-D loop is completed. - - -Buffered Loop -^^^^^^^^^^^^^ - -This is the code that handles the situation whenever the input and/or -output arrays are either misaligned or of the wrong data-type -(including being byte-swapped) from what the underlying 1-D loop -expects. The arrays are also assumed to be non-contiguous. The code -works very much like the strided-loop except for the inner 1-D loop is -modified so that pre-processing is performed on the inputs and post- -processing is performed on the outputs in bufsize chunks (where -bufsize is a user-settable parameter). The underlying 1-D -computational loop is called on data that is copied over (if it needs -to be). The setup code and the loop code is considerably more -complicated in this case because it has to handle: - -- memory allocation of the temporary buffers - -- deciding whether or not to use buffers on the input and output data - (mis-aligned and/or wrong data-type) - -- copying and possibly casting data for any inputs or outputs for which - buffers are necessary. - -- special-casing Object arrays so that reference counts are properly - handled when copies and/or casts are necessary. - -- breaking up the inner 1-D loop into bufsize chunks (with a possible - remainder). - -Again, the hardware error flags are checked at the end of each 1-D -loop. - - -Final output manipulation -------------------------- - -Ufuncs allow other array-like classes to be passed seamlessly through -the interface in that inputs of a particular class will induce the -outputs to be of that same class. The mechanism by which this works is -the following. If any of the inputs are not ndarrays and define the -:obj:`~numpy.class.__array_wrap__` method, then the class with the largest -:obj:`~numpy.class.__array_priority__` attribute determines the type of all the -outputs (with the exception of any output arrays passed in). The -:obj:`~numpy.class.__array_wrap__` method of the input array will be called with the -ndarray being returned from the ufunc as it's input. There are two -calling styles of the :obj:`~numpy.class.__array_wrap__` function supported. The first -takes the ndarray as the first argument and a tuple of "context" as -the second argument. The context is (ufunc, arguments, output argument -number). This is the first call tried. If a TypeError occurs, then the -function is called with just the ndarray as the first argument. - - -Methods -------- - -There are three methods of ufuncs that require calculation similar to -the general-purpose ufuncs. These are reduce, accumulate, and -reduceat. Each of these methods requires a setup command followed by a -loop. There are four loop styles possible for the methods -corresponding to no-elements, one-element, strided-loop, and buffered- -loop. These are the same basic loop styles as implemented for the -general purpose function call except for the no-element and one- -element cases which are special-cases occurring when the input array -objects have 0 and 1 elements respectively. - - -Setup -^^^^^ - -The setup function for all three methods is ``construct_reduce``. -This function creates a reducing loop object and fills it with -parameters needed to complete the loop. All of the methods only work -on ufuncs that take 2-inputs and return 1 output. Therefore, the -underlying 1-D loop is selected assuming a signature of [ ``otype``, -``otype``, ``otype`` ] where ``otype`` is the requested reduction -data-type. The buffer size and error handling is then retrieved from -(per-thread) global storage. For small arrays that are mis-aligned or -have incorrect data-type, a copy is made so that the un-buffered -section of code is used. Then, the looping strategy is selected. If -there is 1 element or 0 elements in the array, then a simple looping -method is selected. If the array is not mis-aligned and has the -correct data-type, then strided looping is selected. Otherwise, -buffered looping must be performed. Looping parameters are then -established, and the return array is constructed. The output array is -of a different shape depending on whether the method is reduce, -accumulate, or reduceat. If an output array is already provided, then -it's shape is checked. If the output array is not C-contiguous, -aligned, and of the correct data type, then a temporary copy is made -with the WRITEBACKIFCOPY flag set. In this way, the methods will be able -to work with a well-behaved output array but the result will be copied -back into the true output array when :c:func:`PyArray_ResolveWritebackIfCopy` -is called at function completion. -Finally, iterators are set up to loop over the correct axis -(depending on the value of axis provided to the method) and the setup -routine returns to the actual computation routine. - - -Reduce -^^^^^^ - -.. index:: - triple: ufunc; methods; reduce - -All of the ufunc methods use the same underlying 1-D computational -loops with input and output arguments adjusted so that the appropriate -reduction takes place. For example, the key to the functioning of -reduce is that the 1-D loop is called with the output and the second -input pointing to the same position in memory and both having a step- -size of 0. The first input is pointing to the input array with a step- -size given by the appropriate stride for the selected axis. In this -way, the operation performed is - -.. math:: - :nowrap: - - \begin{align*} - o & = & i[0] \\ - o & = & i[k]\textrm{<op>}o\quad k=1\ldots N - \end{align*} - -where :math:`N+1` is the number of elements in the input, :math:`i`, -:math:`o` is the output, and :math:`i[k]` is the -:math:`k^{\textrm{th}}` element of :math:`i` along the selected axis. -This basic operations is repeated for arrays with greater than 1 -dimension so that the reduction takes place for every 1-D sub-array -along the selected axis. An iterator with the selected dimension -removed handles this looping. - -For buffered loops, care must be taken to copy and cast data before -the loop function is called because the underlying loop expects -aligned data of the correct data-type (including byte-order). The -buffered loop must handle this copying and casting prior to calling -the loop function on chunks no greater than the user-specified -bufsize. - - -Accumulate -^^^^^^^^^^ - -.. index:: - triple: ufunc; methods; accumulate - -The accumulate function is very similar to the reduce function in that -the output and the second input both point to the output. The -difference is that the second input points to memory one stride behind -the current output pointer. Thus, the operation performed is - -.. math:: - :nowrap: - - \begin{align*} - o[0] & = & i[0] \\ - o[k] & = & i[k]\textrm{<op>}o[k-1]\quad k=1\ldots N. - \end{align*} - -The output has the same shape as the input and each 1-D loop operates -over :math:`N` elements when the shape in the selected axis is :math:`N+1`. -Again, buffered loops take care to copy and cast the data before -calling the underlying 1-D computational loop. - - -Reduceat -^^^^^^^^ - -.. index:: - triple: ufunc; methods; reduceat - single: ufunc - -The reduceat function is a generalization of both the reduce and -accumulate functions. It implements a reduce over ranges of the input -array specified by indices. The extra indices argument is checked to -be sure that every input is not too large for the input array along -the selected dimension before the loop calculations take place. The -loop implementation is handled using code that is very similar to the -reduce code repeated as many times as there are elements in the -indices input. In particular: the first input pointer passed to the -underlying 1-D computational loop points to the input array at the -correct location indicated by the index array. In addition, the output -pointer and the second input pointer passed to the underlying 1-D loop -point to the same position in memory. The size of the 1-D -computational loop is fixed to be the difference between the current -index and the next index (when the current index is the last index, -then the next index is assumed to be the length of the array along the -selected dimension). In this way, the 1-D loop will implement a reduce -over the specified indices. - -Mis-aligned or a loop data-type that does not match the input and/or -output data-type is handled using buffered code where-in data is -copied to a temporary buffer and cast to the correct data-type if -necessary prior to calling the underlying 1-D function. The temporary -buffers are created in (element) sizes no bigger than the user -settable buffer-size value. Thus, the loop must be flexible enough to -call the underlying 1-D computational loop enough times to complete -the total calculation in chunks no bigger than the buffer-size. +This document has been moved to :ref:`c-code-explanations`. \ No newline at end of file diff --git a/doc/source/reference/internals.rst b/doc/source/reference/internals.rst index e1d6644a6cf4..7a5e6374c29b 100644 --- a/doc/source/reference/internals.rst +++ b/doc/source/reference/internals.rst @@ -1,9 +1,10 @@ +:orphan: + *************** NumPy internals *************** -.. toctree:: +.. This document has been moved to ../dev/internals.rst. - internals.code-explanations +This document has been moved to :ref:`numpy-internals`. -.. automodule:: numpy.doc.internals diff --git a/doc/source/reference/maskedarray.baseclass.rst b/doc/source/reference/maskedarray.baseclass.rst index 427ad15368f0..5a0f99651c3f 100644 --- a/doc/source/reference/maskedarray.baseclass.rst +++ b/doc/source/reference/maskedarray.baseclass.rst @@ -1,5 +1,8 @@ .. currentmodule:: numpy.ma +.. for doctests + >>> import numpy as np + >>> from numpy import ma .. _numpy.ma.constants: @@ -21,16 +24,16 @@ defines several constants. True >>> x[-1] = ma.masked >>> x - masked_array(data = [1 -- --], - mask = [False True True], - fill_value = 999999) + masked_array(data=[1, --, --], + mask=[False, True, True], + fill_value=999999) .. data:: nomask Value indicating that a masked array has no invalid entry. :attr:`nomask` is used internally to speed up computations when the mask - is not needed. + is not needed. It is represented internally as ``np.False_``. .. data:: masked_print_options @@ -49,11 +52,11 @@ The :class:`MaskedArray` class .. class:: MaskedArray - A subclass of :class:`~numpy.ndarray` designed to manipulate numerical arrays with missing data. +A subclass of :class:`~numpy.ndarray` designed to manipulate numerical arrays with missing data. - An instance of :class:`MaskedArray` can be thought as the combination of several elements: +An instance of :class:`MaskedArray` can be thought as the combination of several elements: * The :attr:`~MaskedArray.data`, as a regular :class:`numpy.ndarray` of any shape or datatype (the data). * A boolean :attr:`~numpy.ma.MaskedArray.mask` with the same shape as the data, where a ``True`` value indicates that the corresponding element of the data is invalid. @@ -62,89 +65,26 @@ The :class:`MaskedArray` class +.. _ma-attributes: + Attributes and properties of masked arrays ------------------------------------------ .. seealso:: :ref:`Array Attributes <arrays.ndarray.attributes>` +.. autoattribute:: MaskedArray.data -.. attribute:: MaskedArray.data - - Returns the underlying data, as a view of the masked array. - If the underlying data is a subclass of :class:`numpy.ndarray`, it is - returned as such. - - >>> x = ma.array(np.matrix([[1, 2], [3, 4]]), mask=[[0, 1], [1, 0]]) - >>> x.data - matrix([[1, 2], - [3, 4]]) - - The type of the data can be accessed through the :attr:`baseclass` - attribute. - -.. attribute:: MaskedArray.mask - - Returns the underlying mask, as an array with the same shape and structure - as the data, but where all fields are atomically booleans. - A value of ``True`` indicates an invalid entry. - - -.. attribute:: MaskedArray.recordmask - - Returns the mask of the array if it has no named fields. For structured - arrays, returns a ndarray of booleans where entries are ``True`` if **all** - the fields are masked, ``False`` otherwise:: - - >>> x = ma.array([(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)], - ... mask=[(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)], - ... dtype=[('a', int), ('b', int)]) - >>> x.recordmask - array([False, False, True, False, False]) - - -.. attribute:: MaskedArray.fill_value - - Returns the value used to fill the invalid entries of a masked array. - The value is either a scalar (if the masked array has no named fields), - or a 0-D ndarray with the same :attr:`dtype` as the masked array if it has - named fields. - - The default filling value depends on the datatype of the array: - - ======== ======== - datatype default - ======== ======== - bool True - int 999999 - float 1.e20 - complex 1.e20+0j - object '?' - string 'N/A' - ======== ======== - - - -.. attribute:: MaskedArray.baseclass - - Returns the class of the underlying data. - - >>> x = ma.array(np.matrix([[1, 2], [3, 4]]), mask=[[0, 0], [1, 0]]) - >>> x.baseclass - <class 'numpy.matrixlib.defmatrix.matrix'> - - -.. attribute:: MaskedArray.sharedmask +.. autoattribute:: MaskedArray.mask - Returns whether the mask of the array is shared between several masked arrays. - If this is the case, any modification to the mask of one array will be - propagated to the others. +.. autoattribute:: MaskedArray.recordmask +.. autoattribute:: MaskedArray.fill_value -.. attribute:: MaskedArray.hardmask +.. autoattribute:: MaskedArray.baseclass - Returns whether the mask is hard (``True``) or soft (``False``). - When the mask is hard, masked entries cannot be unmasked. +.. autoattribute:: MaskedArray.sharedmask +.. autoattribute:: MaskedArray.hardmask As :class:`MaskedArray` is a subclass of :class:`~numpy.ndarray`, a masked array also inherits all the attributes and properties of a :class:`~numpy.ndarray` instance. @@ -184,10 +124,7 @@ Conversion :toctree: generated/ MaskedArray.__float__ - MaskedArray.__hex__ MaskedArray.__int__ - MaskedArray.__long__ - MaskedArray.__oct__ MaskedArray.view MaskedArray.astype @@ -225,9 +162,9 @@ replaced with ``n`` integers which will be interpreted as an n-tuple. Item selection and manipulation ------------------------------- -For array methods that take an *axis* keyword, it defaults to `None`. -If axis is *None*, then the array is treated as a 1-D array. -Any other value for *axis* represents the dimension along which +For array methods that take an ``axis`` keyword, it defaults to None. +If axis is None, then the array is treated as a 1-D array. +Any other value for ``axis`` represents the dimension along which the operation should proceed. .. autosummary:: @@ -305,13 +242,13 @@ Comparison operators: MaskedArray.__eq__ MaskedArray.__ne__ -Truth value of an array (:func:`bool()`): -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Truth value of an array (:class:`bool() <bool>`): +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. autosummary:: :toctree: generated/ - MaskedArray.__nonzero__ + MaskedArray.__bool__ Arithmetic: @@ -328,7 +265,6 @@ Arithmetic: MaskedArray.__mul__ MaskedArray.__rmul__ MaskedArray.__div__ - MaskedArray.__rdiv__ MaskedArray.__truediv__ MaskedArray.__rtruediv__ MaskedArray.__floordiv__ diff --git a/doc/source/reference/maskedarray.generic.rst b/doc/source/reference/maskedarray.generic.rst index 07ad6c292a19..d3849c50deec 100644 --- a/doc/source/reference/maskedarray.generic.rst +++ b/doc/source/reference/maskedarray.generic.rst @@ -2,7 +2,7 @@ .. _maskedarray.generic: - +.. module:: numpy.ma The :mod:`numpy.ma` module ========================== @@ -74,7 +74,7 @@ To create an array with the second element invalid, we would do:: To create a masked array where all values close to 1.e20 are invalid, we would do:: - >>> z = masked_values([1.0, 1.e20, 3.0, 4.0], 1.e20) + >>> z = ma.masked_values([1.0, 1.e20, 3.0, 4.0], 1.e20) For a complete discussion of creation methods for masked arrays please see section :ref:`Constructing masked arrays <maskedarray.generic.constructing>`. @@ -110,15 +110,15 @@ There are several ways to construct a masked array. >>> x = np.array([1, 2, 3]) >>> x.view(ma.MaskedArray) - masked_array(data = [1 2 3], - mask = False, - fill_value = 999999) + masked_array(data=[1, 2, 3], + mask=False, + fill_value=999999) >>> x = np.array([(1, 1.), (2, 2.)], dtype=[('a',int), ('b', float)]) >>> x.view(ma.MaskedArray) - masked_array(data = [(1, 1.0) (2, 2.0)], - mask = [(False, False) (False, False)], - fill_value = (999999, 1e+20), - dtype = [('a', '<i4'), ('b', '<f8')]) + masked_array(data=[(1, 1.0), (2, 2.0)], + mask=[(False, False), (False, False)], + fill_value=(999999, 1.e+20), + dtype=[('a', '<i8'), ('b', '<f8')]) * Yet another possibility is to use any of the following functions: @@ -177,8 +177,8 @@ attribute. We must keep in mind that a ``True`` entry in the mask indicates an *invalid* data. Another possibility is to use the :func:`getmask` and :func:`getmaskarray` -functions. :func:`getmask(x)` outputs the mask of ``x`` if ``x`` is a masked -array, and the special value :data:`nomask` otherwise. :func:`getmaskarray(x)` +functions. ``getmask(x)`` outputs the mask of ``x`` if ``x`` is a masked +array, and the special value :data:`nomask` otherwise. ``getmaskarray(x)`` outputs the mask of ``x`` if ``x`` is a masked array. If ``x`` has no invalid entry or is not a masked array, the function outputs a boolean array of ``False`` with as many elements as ``x``. @@ -195,9 +195,9 @@ index. The inverse of the mask can be calculated with the >>> x = ma.array([[1, 2], [3, 4]], mask=[[0, 1], [1, 0]]) >>> x[~x.mask] - masked_array(data = [1 4], - mask = [False False], - fill_value = 999999) + masked_array(data=[1, 4], + mask=[False, False], + fill_value=999999) Another way to retrieve the valid data is to use the :meth:`compressed` method, which returns a one-dimensional :class:`~numpy.ndarray` (or one of its @@ -223,27 +223,26 @@ as invalid is to assign the special value :attr:`masked` to them:: >>> x = ma.array([1, 2, 3]) >>> x[0] = ma.masked >>> x - masked_array(data = [-- 2 3], - mask = [ True False False], - fill_value = 999999) + masked_array(data=[--, 2, 3], + mask=[ True, False, False], + fill_value=999999) >>> y = ma.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) >>> y[(0, 1, 2), (1, 2, 0)] = ma.masked >>> y - masked_array(data = - [[1 -- 3] - [4 5 --] - [-- 8 9]], - mask = - [[False True False] - [False False True] - [ True False False]], - fill_value = 999999) + masked_array( + data=[[1, --, 3], + [4, 5, --], + [--, 8, 9]], + mask=[[False, True, False], + [False, False, True], + [ True, False, False]], + fill_value=999999) >>> z = ma.array([1, 2, 3, 4]) >>> z[:-2] = ma.masked >>> z - masked_array(data = [-- -- 3 4], - mask = [ True True False False], - fill_value = 999999) + masked_array(data=[--, --, 3, 4], + mask=[ True, True, False, False], + fill_value=999999) A second possibility is to modify the :attr:`~MaskedArray.mask` directly, @@ -263,9 +262,10 @@ mask:: >>> x = ma.array([1, 2, 3], mask=[0, 0, 1]) >>> x.mask = True >>> x - masked_array(data = [-- -- --], - mask = [ True True True], - fill_value = 999999) + masked_array(data=[--, --, --], + mask=[ True, True, True], + fill_value=999999, + dtype=int64) Finally, specific entries can be masked and/or unmasked by assigning to the mask a sequence of booleans:: @@ -273,9 +273,9 @@ mask a sequence of booleans:: >>> x = ma.array([1, 2, 3]) >>> x.mask = [0, 1, 0] >>> x - masked_array(data = [1 -- 3], - mask = [False True False], - fill_value = 999999) + masked_array(data=[1, --, 3], + mask=[False, True, False], + fill_value=999999) Unmasking an entry ~~~~~~~~~~~~~~~~~~ @@ -285,40 +285,46 @@ new valid values to them:: >>> x = ma.array([1, 2, 3], mask=[0, 0, 1]) >>> x - masked_array(data = [1 2 --], - mask = [False False True], - fill_value = 999999) + masked_array(data=[1, 2, --], + mask=[False, False, True], + fill_value=999999) >>> x[-1] = 5 >>> x - masked_array(data = [1 2 5], - mask = [False False False], - fill_value = 999999) + masked_array(data=[1, 2, 5], + mask=[False, False, False], + fill_value=999999) .. note:: Unmasking an entry by direct assignment will silently fail if the masked - array has a *hard* mask, as shown by the :attr:`hardmask` attribute. This - feature was introduced to prevent overwriting the mask. To force the - unmasking of an entry where the array has a hard mask, the mask must first - to be softened using the :meth:`soften_mask` method before the allocation. - It can be re-hardened with :meth:`harden_mask`:: + array has a *hard* mask, as shown by the :attr:`~MaskedArray.hardmask` + attribute. This feature was introduced to prevent overwriting the mask. + To force the unmasking of an entry where the array has a hard mask, + the mask must first to be softened using the :meth:`soften_mask` method + before the allocation. It can be re-hardened with :meth:`harden_mask`:: >>> x = ma.array([1, 2, 3], mask=[0, 0, 1], hard_mask=True) >>> x - masked_array(data = [1 2 --], - mask = [False False True], - fill_value = 999999) + masked_array(data=[1, 2, --], + mask=[False, False, True], + fill_value=999999) >>> x[-1] = 5 >>> x - masked_array(data = [1 2 --], - mask = [False False True], - fill_value = 999999) + masked_array(data=[1, 2, --], + mask=[False, False, True], + fill_value=999999) >>> x.soften_mask() + masked_array(data=[1, 2, --], + mask=[False, False, True], + fill_value=999999) >>> x[-1] = 5 >>> x - masked_array(data = [1 2 5], - mask = [False False False], - fill_value = 999999) + masked_array(data=[1, 2, 5], + mask=[False, False, False], + fill_value=999999) >>> x.harden_mask() + masked_array(data=[1, 2, 5], + mask=[False, False, False], + fill_value=999999) To unmask all masked entries of a masked array (provided the mask isn't a hard @@ -327,15 +333,14 @@ mask:: >>> x = ma.array([1, 2, 3], mask=[0, 0, 1]) >>> x - masked_array(data = [1 2 --], - mask = [False False True], - fill_value = 999999) + masked_array(data=[1, 2, --], + mask=[False, False, True], + fill_value=999999) >>> x.mask = ma.nomask >>> x - masked_array(data = [1 2 3], - mask = [False False False], - fill_value = 999999) - + masked_array(data=[1, 2, 3], + mask=[False, False, False], + fill_value=999999) Indexing and slicing @@ -353,9 +358,7 @@ the mask is ``True``):: >>> x[0] 1 >>> x[-1] - masked_array(data = --, - mask = True, - fill_value = 1e+20) + masked >>> x[-1] is ma.masked True @@ -370,10 +373,7 @@ is masked. >>> y[0] (1, 2) >>> y[-1] - masked_array(data = (3, --), - mask = (False, True), - fill_value = (999999, 999999), - dtype = [('a', '<i4'), ('b', '<i4')]) + (3, --) When accessing a slice, the output is a masked array whose @@ -385,20 +385,19 @@ required to ensure propagation of any modification of the mask to the original. >>> x = ma.array([1, 2, 3, 4, 5], mask=[0, 1, 0, 0, 1]) >>> mx = x[:3] >>> mx - masked_array(data = [1 -- 3], - mask = [False True False], - fill_value = 999999) + masked_array(data=[1, --, 3], + mask=[False, True, False], + fill_value=999999) >>> mx[1] = -1 >>> mx - masked_array(data = [1 -1 3], - mask = [False False False], - fill_value = 999999) + masked_array(data=[1, -1, 3], + mask=[False, False, False], + fill_value=999999) >>> x.mask - array([False, True, False, False, True]) + array([False, False, False, False, True]) >>> x.data array([ 1, -1, 3, 4, 5]) - Accessing a field of a masked array with structured datatype returns a :class:`MaskedArray`. @@ -407,8 +406,8 @@ Operations on masked arrays Arithmetic and comparison operations are supported by masked arrays. As much as possible, invalid entries of a masked array are not processed, -meaning that the corresponding :attr:`data` entries *should* be the same -before and after the operation. +meaning that the corresponding :attr:`~MaskedArray.data` entries +*should* be the same before and after the operation. .. warning:: We need to stress that this behavior may not be systematic, that masked @@ -421,9 +420,9 @@ ufuncs. Unary and binary functions that have a validity domain (such as constant whenever the input is masked or falls outside the validity domain:: >>> ma.log([-1, 0, 1, 2]) - masked_array(data = [-- -- 0.0 0.69314718056], - mask = [ True True False False], - fill_value = 1e+20) + masked_array(data=[--, --, 0.0, 0.6931471805599453], + mask=[ True, True, False, False], + fill_value=1e+20) Masked arrays also support standard numpy ufuncs. The output is then a masked array. The result of a unary ufunc is masked wherever the input is masked. The @@ -435,10 +434,9 @@ input fall outside the validity domain:: >>> x = ma.array([-1, 1, 0, 2, 3], mask=[0, 0, 0, 0, 1]) >>> np.log(x) - masked_array(data = [-- -- 0.0 0.69314718056 --], - mask = [ True True False False True], - fill_value = 1e+20) - + masked_array(data=[--, 0.0, --, 0.6931471805599453, --], + mask=[ True, False, True, False, True], + fill_value=1e+20) Examples @@ -454,11 +452,11 @@ of anomalies (deviations from the average):: >>> import numpy.ma as ma >>> x = [0.,1.,-9999.,3.,4.] >>> mx = ma.masked_values (x, -9999.) - >>> print mx.mean() + >>> print(mx.mean()) 2.0 - >>> print mx - mx.mean() + >>> print(mx - mx.mean()) [-2.0 -1.0 -- 1.0 2.0] - >>> print mx.anom() + >>> print(mx.anom()) [-2.0 -1.0 -- 1.0 2.0] @@ -468,7 +466,7 @@ Filling in the missing data Suppose now that we wish to print that same data, but with the missing values replaced by the average value. - >>> print mx.filled(mx.mean()) + >>> print(mx.filled(mx.mean())) [ 0. 1. 2. 3. 4.] @@ -478,10 +476,10 @@ Numerical operations Numerical operations can be easily performed without worrying about missing values, dividing by zero, square roots of negative numbers, etc.:: - >>> import numpy as np, numpy.ma as ma + >>> import numpy.ma as ma >>> x = ma.array([1., -1., 3., 4., 5., 6.], mask=[0,0,0,0,1,0]) >>> y = ma.array([1., 2., 0., 4., 5., 6.], mask=[0,0,0,0,0,1]) - >>> print np.sqrt(x/y) + >>> print(ma.sqrt(x/y)) [1.0 -- -- 1.0 -- --] Four values of the output are invalid: the first one comes from taking the @@ -492,8 +490,10 @@ the last two where the inputs were masked. Ignoring extreme values ----------------------- -Let's consider an array ``d`` of random floats between 0 and 1. We wish to +Let's consider an array ``d`` of floats between 0 and 1. We wish to compute the average of the values of ``d`` while ignoring any data outside -the range ``[0.1, 0.9]``:: +the range ``[0.2, 0.9]``:: - >>> print ma.masked_outside(d, 0.1, 0.9).mean() + >>> d = np.linspace(0, 1, 20) + >>> print(d.mean() - ma.masked_outside(d, 0.2, 0.9).mean()) + -0.05263157894736836 diff --git a/doc/source/reference/random/bit_generators/index.rst b/doc/source/reference/random/bit_generators/index.rst new file mode 100644 index 000000000000..211f0d60e2b8 --- /dev/null +++ b/doc/source/reference/random/bit_generators/index.rst @@ -0,0 +1,153 @@ +.. currentmodule:: numpy.random + +Bit Generators +-------------- + +The random values produced by :class:`~Generator` +originate in a BitGenerator. The BitGenerators do not directly provide +random numbers and only contains methods used for seeding, getting or +setting the state, jumping or advancing the state, and for accessing +low-level wrappers for consumption by code that can efficiently +access the functions provided, e.g., `numba <https://numba.pydata.org>`_. + +Supported BitGenerators +======================= + +The included BitGenerators are: + +* PCG-64 - The default. A fast generator that can be advanced by an arbitrary + amount. See the documentation for :meth:`~.PCG64.advance`. PCG-64 has + a period of :math:`2^{128}`. See the `PCG author's page`_ for more details + about this class of PRNG. +* PCG-64 DXSM - An upgraded version of PCG-64 with better statistical + properties in parallel contexts. See :ref:`upgrading-pcg64` for more + information on these improvements. +* MT19937 - The standard Python BitGenerator. Adds a `MT19937.jumped` + function that returns a new generator with state as-if :math:`2^{128}` draws have + been made. +* Philox - A counter-based generator capable of being advanced an + arbitrary number of steps or generating independent streams. See the + `Random123`_ page for more details about this class of bit generators. +* SFC64 - A fast generator based on random invertible mappings. Usually the + fastest generator of the four. See the `SFC author's page`_ for (a little) + more detail. + +.. _`PCG author's page`: http://www.pcg-random.org/ +.. _`Random123`: https://www.deshawresearch.com/resources_random123.html +.. _`SFC author's page`: http://pracrand.sourceforge.net/RNG_engines.txt + +.. autosummary:: + :toctree: generated/ + + BitGenerator + +.. toctree:: + :maxdepth: 1 + + MT19937 <mt19937> + PCG64 <pcg64> + PCG64DXSM <pcg64dxsm> + Philox <philox> + SFC64 <sfc64> + +Seeding and Entropy +------------------- + +A BitGenerator provides a stream of random values. In order to generate +reproducible streams, BitGenerators support setting their initial state via a +seed. All of the provided BitGenerators will take an arbitrary-sized +non-negative integer, or a list of such integers, as a seed. BitGenerators +need to take those inputs and process them into a high-quality internal state +for the BitGenerator. All of the BitGenerators in numpy delegate that task to +`SeedSequence`, which uses hashing techniques to ensure that even low-quality +seeds generate high-quality initial states. + +.. code-block:: python + + from numpy.random import PCG64 + + bg = PCG64(12345678903141592653589793) + +.. end_block + +`~SeedSequence` is designed to be convenient for implementing best practices. +We recommend that a stochastic program defaults to using entropy from the OS so +that each run is different. The program should print out or log that entropy. +In order to reproduce a past value, the program should allow the user to +provide that value through some mechanism, a command-line argument is common, +so that the user can then re-enter that entropy to reproduce the result. +`~SeedSequence` can take care of everything except for communicating with the +user, which is up to you. + +.. code-block:: python + + from numpy.random import PCG64, SeedSequence + + # Get the user's seed somehow, maybe through `argparse`. + # If the user did not provide a seed, it should return `None`. + seed = get_user_seed() + ss = SeedSequence(seed) + print('seed = {}'.format(ss.entropy)) + bg = PCG64(ss) + +.. end_block + +We default to using a 128-bit integer using entropy gathered from the OS. This +is a good amount of entropy to initialize all of the generators that we have in +numpy. We do not recommend using small seeds below 32 bits for general use. +Using just a small set of seeds to instantiate larger state spaces means that +there are some initial states that are impossible to reach. This creates some +biases if everyone uses such values. + +There will not be anything *wrong* with the results, per se; even a seed of +0 is perfectly fine thanks to the processing that `~SeedSequence` does. If you +just need *some* fixed value for unit tests or debugging, feel free to use +whatever seed you like. But if you want to make inferences from the results or +publish them, drawing from a larger set of seeds is good practice. + +If you need to generate a good seed "offline", then ``SeedSequence().entropy`` +or using ``secrets.randbits(128)`` from the standard library are both +convenient ways. + +If you need to run several stochastic simulations in parallel, best practice +is to construct a random generator instance for each simulation. +To make sure that the random streams have distinct initial states, you can use +the `spawn` method of `~SeedSequence`. For instance, here we construct a list +of 12 instances: + +.. code-block:: python + + from numpy.random import PCG64, SeedSequence + + # High quality initial entropy + entropy = 0x87351080e25cb0fad77a44a3be03b491 + base_seq = SeedSequence(entropy) + child_seqs = base_seq.spawn(12) # a list of 12 SeedSequences + generators = [PCG64(seq) for seq in child_seqs] + +.. end_block + + +An alternative way is to use the fact that a `~SeedSequence` can be initialized +by a tuple of elements. Here we use a base entropy value and an integer +``worker_id`` + +.. code-block:: python + + from numpy.random import PCG64, SeedSequence + + # High quality initial entropy + entropy = 0x87351080e25cb0fad77a44a3be03b491 + sequences = [SeedSequence((entropy, worker_id)) for worker_id in range(12)] + generators = [PCG64(seq) for seq in sequences] + +.. end_block + +Note that the sequences produced by the latter method will be distinct from +those constructed via `~SeedSequence.spawn`. + + +.. autosummary:: + :toctree: generated/ + + SeedSequence diff --git a/doc/source/reference/random/bit_generators/mt19937.rst b/doc/source/reference/random/bit_generators/mt19937.rst new file mode 100644 index 000000000000..d05ea7c6f09a --- /dev/null +++ b/doc/source/reference/random/bit_generators/mt19937.rst @@ -0,0 +1,33 @@ +Mersenne Twister (MT19937) +-------------------------- + +.. currentmodule:: numpy.random + +.. autoclass:: MT19937 + :members: __init__ + :exclude-members: __init__ + +State +===== + +.. autosummary:: + :toctree: generated/ + + ~MT19937.state + +Parallel generation +=================== +.. autosummary:: + :toctree: generated/ + + ~MT19937.jumped + +Extending +========= +.. autosummary:: + :toctree: generated/ + + ~MT19937.cffi + ~MT19937.ctypes + + diff --git a/doc/source/reference/random/bit_generators/pcg64.rst b/doc/source/reference/random/bit_generators/pcg64.rst new file mode 100644 index 000000000000..889965f77bc2 --- /dev/null +++ b/doc/source/reference/random/bit_generators/pcg64.rst @@ -0,0 +1,32 @@ +Permuted Congruential Generator (64-bit, PCG64) +----------------------------------------------- + +.. currentmodule:: numpy.random + +.. autoclass:: PCG64 + :members: __init__ + :exclude-members: __init__ + +State +===== + +.. autosummary:: + :toctree: generated/ + + ~PCG64.state + +Parallel generation +=================== +.. autosummary:: + :toctree: generated/ + + ~PCG64.advance + ~PCG64.jumped + +Extending +========= +.. autosummary:: + :toctree: generated/ + + ~PCG64.cffi + ~PCG64.ctypes diff --git a/doc/source/reference/random/bit_generators/pcg64dxsm.rst b/doc/source/reference/random/bit_generators/pcg64dxsm.rst new file mode 100644 index 000000000000..e37efa5d39da --- /dev/null +++ b/doc/source/reference/random/bit_generators/pcg64dxsm.rst @@ -0,0 +1,32 @@ +Permuted Congruential Generator (64-bit, PCG64 DXSM) +---------------------------------------------------- + +.. currentmodule:: numpy.random + +.. autoclass:: PCG64DXSM + :members: __init__ + :exclude-members: __init__ + +State +===== + +.. autosummary:: + :toctree: generated/ + + ~PCG64DXSM.state + +Parallel generation +=================== +.. autosummary:: + :toctree: generated/ + + ~PCG64DXSM.advance + ~PCG64DXSM.jumped + +Extending +========= +.. autosummary:: + :toctree: generated/ + + ~PCG64DXSM.cffi + ~PCG64DXSM.ctypes diff --git a/doc/source/reference/random/bit_generators/philox.rst b/doc/source/reference/random/bit_generators/philox.rst new file mode 100644 index 000000000000..3c2fa4cc5aa1 --- /dev/null +++ b/doc/source/reference/random/bit_generators/philox.rst @@ -0,0 +1,34 @@ +Philox Counter-based RNG +------------------------ + +.. currentmodule:: numpy.random + +.. autoclass:: Philox + :members: __init__ + :exclude-members: __init__ + +State +===== + +.. autosummary:: + :toctree: generated/ + + ~Philox.state + +Parallel generation +=================== +.. autosummary:: + :toctree: generated/ + + ~Philox.advance + ~Philox.jumped + +Extending +========= +.. autosummary:: + :toctree: generated/ + + ~Philox.cffi + ~Philox.ctypes + + diff --git a/doc/source/reference/random/bit_generators/sfc64.rst b/doc/source/reference/random/bit_generators/sfc64.rst new file mode 100644 index 000000000000..8cb255bc154d --- /dev/null +++ b/doc/source/reference/random/bit_generators/sfc64.rst @@ -0,0 +1,27 @@ +SFC64 Small Fast Chaotic PRNG +----------------------------- + +.. currentmodule:: numpy.random + +.. autoclass:: SFC64 + :members: __init__ + :exclude-members: __init__ + +State +===== + +.. autosummary:: + :toctree: generated/ + + ~SFC64.state + +Extending +========= +.. autosummary:: + :toctree: generated/ + + ~SFC64.cffi + ~SFC64.ctypes + + + diff --git a/doc/source/reference/random/c-api.rst b/doc/source/reference/random/c-api.rst new file mode 100644 index 000000000000..de403ce98673 --- /dev/null +++ b/doc/source/reference/random/c-api.rst @@ -0,0 +1,191 @@ +C API for random +---------------- + +.. currentmodule:: numpy.random + +.. versionadded:: 1.19.0 + +Access to various distributions below is available via Cython or C-wrapper +libraries like CFFI. All the functions accept a :c:type:`bitgen_t` as their +first argument. To access these from Cython or C, you must link with the +``npyrandom`` library which is part of the NumPy distribution, located in +``numpy/random/lib``. + + +.. c:type:: bitgen_t + + The :c:type:`bitgen_t` holds the current state of the BitGenerator and + pointers to functions that return standard C types while advancing the + state. + + .. code-block:: c + + struct bitgen: + void *state + npy_uint64 (*next_uint64)(void *st) nogil + uint32_t (*next_uint32)(void *st) nogil + double (*next_double)(void *st) nogil + npy_uint64 (*next_raw)(void *st) nogil + + ctypedef bitgen bitgen_t + +See :doc:`extending` for examples of using these functions. + +The functions are named with the following conventions: + +- "standard" refers to the reference values for any parameters. For instance + "standard_uniform" means a uniform distribution on the interval ``0.0`` to + ``1.0`` + +- "fill" functions will fill the provided ``out`` with ``cnt`` values. + +- The functions without "standard" in their name require additional parameters + to describe the distributions. + +- Functions with ``inv`` in their name are based on the slower inverse method + instead of a ziggurat lookup algorithm, which is significantly faster. The + non-ziggurat variants are used in corner cases and for legacy compatibility. + + +.. c:function:: double random_standard_uniform(bitgen_t *bitgen_state) + +.. c:function:: void random_standard_uniform_fill(bitgen_t* bitgen_state, npy_intp cnt, double *out) + +.. c:function:: double random_standard_exponential(bitgen_t *bitgen_state) + +.. c:function:: void random_standard_exponential_fill(bitgen_t *bitgen_state, npy_intp cnt, double *out) + +.. c:function:: void random_standard_exponential_inv_fill(bitgen_t *bitgen_state, npy_intp cnt, double *out) + +.. c:function:: double random_standard_normal(bitgen_t* bitgen_state) + +.. c:function:: void random_standard_normal_fill(bitgen_t *bitgen_state, npy_intp count, double *out) + +.. c:function:: void random_standard_normal_fill_f(bitgen_t *bitgen_state, npy_intp count, float *out) + +.. c:function:: double random_standard_gamma(bitgen_t *bitgen_state, double shape) + +.. c:function:: float random_standard_uniform_f(bitgen_t *bitgen_state) + +.. c:function:: void random_standard_uniform_fill_f(bitgen_t* bitgen_state, npy_intp cnt, float *out) + +.. c:function:: float random_standard_exponential_f(bitgen_t *bitgen_state) + +.. c:function:: void random_standard_exponential_fill_f(bitgen_t *bitgen_state, npy_intp cnt, float *out) + +.. c:function:: void random_standard_exponential_inv_fill_f(bitgen_t *bitgen_state, npy_intp cnt, float *out) + +.. c:function:: float random_standard_normal_f(bitgen_t* bitgen_state) + +.. c:function:: float random_standard_gamma_f(bitgen_t *bitgen_state, float shape) + +.. c:function:: double random_normal(bitgen_t *bitgen_state, double loc, double scale) + +.. c:function:: double random_gamma(bitgen_t *bitgen_state, double shape, double scale) + +.. c:function:: float random_gamma_f(bitgen_t *bitgen_state, float shape, float scale) + +.. c:function:: double random_exponential(bitgen_t *bitgen_state, double scale) + +.. c:function:: double random_uniform(bitgen_t *bitgen_state, double lower, double range) + +.. c:function:: double random_beta(bitgen_t *bitgen_state, double a, double b) + +.. c:function:: double random_chisquare(bitgen_t *bitgen_state, double df) + +.. c:function:: double random_f(bitgen_t *bitgen_state, double dfnum, double dfden) + +.. c:function:: double random_standard_cauchy(bitgen_t *bitgen_state) + +.. c:function:: double random_pareto(bitgen_t *bitgen_state, double a) + +.. c:function:: double random_weibull(bitgen_t *bitgen_state, double a) + +.. c:function:: double random_power(bitgen_t *bitgen_state, double a) + +.. c:function:: double random_laplace(bitgen_t *bitgen_state, double loc, double scale) + +.. c:function:: double random_gumbel(bitgen_t *bitgen_state, double loc, double scale) + +.. c:function:: double random_logistic(bitgen_t *bitgen_state, double loc, double scale) + +.. c:function:: double random_lognormal(bitgen_t *bitgen_state, double mean, double sigma) + +.. c:function:: double random_rayleigh(bitgen_t *bitgen_state, double mode) + +.. c:function:: double random_standard_t(bitgen_t *bitgen_state, double df) + +.. c:function:: double random_noncentral_chisquare(bitgen_t *bitgen_state, double df, double nonc) +.. c:function:: double random_noncentral_f(bitgen_t *bitgen_state, double dfnum, double dfden, double nonc) +.. c:function:: double random_wald(bitgen_t *bitgen_state, double mean, double scale) + +.. c:function:: double random_vonmises(bitgen_t *bitgen_state, double mu, double kappa) + +.. c:function:: double random_triangular(bitgen_t *bitgen_state, double left, double mode, double right) + +.. c:function:: npy_int64 random_poisson(bitgen_t *bitgen_state, double lam) + +.. c:function:: npy_int64 random_negative_binomial(bitgen_t *bitgen_state, double n, double p) + +.. c:type:: binomial_t + + .. code-block:: c + + typedef struct s_binomial_t { + int has_binomial; /* !=0: following parameters initialized for binomial */ + double psave; + RAND_INT_TYPE nsave; + double r; + double q; + double fm; + RAND_INT_TYPE m; + double p1; + double xm; + double xl; + double xr; + double c; + double laml; + double lamr; + double p2; + double p3; + double p4; + } binomial_t; + + +.. c:function:: npy_int64 random_binomial(bitgen_t *bitgen_state, double p, npy_int64 n, binomial_t *binomial) + +.. c:function:: npy_int64 random_logseries(bitgen_t *bitgen_state, double p) + +.. c:function:: npy_int64 random_geometric_search(bitgen_t *bitgen_state, double p) + +.. c:function:: npy_int64 random_geometric_inversion(bitgen_t *bitgen_state, double p) + +.. c:function:: npy_int64 random_geometric(bitgen_t *bitgen_state, double p) + +.. c:function:: npy_int64 random_zipf(bitgen_t *bitgen_state, double a) + +.. c:function:: npy_int64 random_hypergeometric(bitgen_t *bitgen_state, npy_int64 good, npy_int64 bad, npy_int64 sample) + +.. c:function:: npy_uint64 random_interval(bitgen_t *bitgen_state, npy_uint64 max) + +.. c:function:: void random_multinomial(bitgen_t *bitgen_state, npy_int64 n, npy_int64 *mnix, double *pix, npy_intp d, binomial_t *binomial) + +.. c:function:: int random_multivariate_hypergeometric_count(bitgen_t *bitgen_state, npy_int64 total, size_t num_colors, npy_int64 *colors, npy_int64 nsample, size_t num_variates, npy_int64 *variates) + +.. c:function:: void random_multivariate_hypergeometric_marginals(bitgen_t *bitgen_state, npy_int64 total, size_t num_colors, npy_int64 *colors, npy_int64 nsample, size_t num_variates, npy_int64 *variates) + +Generate a single integer + +.. c:function:: npy_int64 random_positive_int64(bitgen_t *bitgen_state) + +.. c:function:: npy_int32 random_positive_int32(bitgen_t *bitgen_state) + +.. c:function:: npy_int64 random_positive_int(bitgen_t *bitgen_state) + +.. c:function:: npy_uint64 random_uint(bitgen_t *bitgen_state) + + +Generate random uint64 numbers in closed interval [off, off + rng]. + +.. c:function:: npy_uint64 random_bounded_uint64(bitgen_t *bitgen_state, npy_uint64 off, npy_uint64 rng, npy_uint64 mask, bool use_masked) + diff --git a/doc/source/reference/random/examples/cffi.rst b/doc/source/reference/random/examples/cffi.rst new file mode 100644 index 000000000000..04d52203b954 --- /dev/null +++ b/doc/source/reference/random/examples/cffi.rst @@ -0,0 +1,5 @@ +Extending via CFFI +------------------ + +.. literalinclude:: ../../../../../numpy/random/_examples/cffi/extending.py + :language: python diff --git a/doc/source/reference/random/examples/cython/extending.pyx b/doc/source/reference/random/examples/cython/extending.pyx new file mode 100644 index 000000000000..0cfbc146f4aa --- /dev/null +++ b/doc/source/reference/random/examples/cython/extending.pyx @@ -0,0 +1,4 @@ +extending.pyx +------------- + +.. include:: ../../../../../../numpy/random/examples/extending.pyx diff --git a/doc/source/reference/random/examples/cython/extending.pyx.rst b/doc/source/reference/random/examples/cython/extending.pyx.rst new file mode 100644 index 000000000000..e2bba5aa4400 --- /dev/null +++ b/doc/source/reference/random/examples/cython/extending.pyx.rst @@ -0,0 +1,5 @@ +extending.pyx +------------- + +.. literalinclude:: ../../../../../../numpy/random/_examples/cython/extending.pyx + :language: cython diff --git a/doc/source/reference/random/examples/cython/extending_distributions.pyx.rst b/doc/source/reference/random/examples/cython/extending_distributions.pyx.rst new file mode 100644 index 000000000000..f64921c677dc --- /dev/null +++ b/doc/source/reference/random/examples/cython/extending_distributions.pyx.rst @@ -0,0 +1,5 @@ +extending_distributions.pyx +--------------------------- + +.. literalinclude:: ../../../../../../numpy/random/_examples/cython/extending_distributions.pyx + :language: cython diff --git a/doc/source/reference/random/examples/cython/index.rst b/doc/source/reference/random/examples/cython/index.rst new file mode 100644 index 000000000000..368f5fcd5676 --- /dev/null +++ b/doc/source/reference/random/examples/cython/index.rst @@ -0,0 +1,11 @@ + +.. _extending_cython_example: + +Extending `numpy.random` via Cython +----------------------------------- + + +.. toctree:: + setup.py.rst + extending.pyx + extending_distributions.pyx diff --git a/doc/source/reference/random/examples/cython/setup.py.rst b/doc/source/reference/random/examples/cython/setup.py.rst new file mode 100644 index 000000000000..bc7a74c59382 --- /dev/null +++ b/doc/source/reference/random/examples/cython/setup.py.rst @@ -0,0 +1,5 @@ +setup.py +-------- + +.. literalinclude:: ../../../../../../numpy/random/_examples/cython/setup.py + :language: python diff --git a/doc/source/reference/random/examples/numba.rst b/doc/source/reference/random/examples/numba.rst new file mode 100644 index 000000000000..b41a02568c0f --- /dev/null +++ b/doc/source/reference/random/examples/numba.rst @@ -0,0 +1,5 @@ +Extending via Numba +------------------- + +.. literalinclude:: ../../../../../numpy/random/_examples/numba/extending.py + :language: python diff --git a/doc/source/reference/random/examples/numba_cffi.rst b/doc/source/reference/random/examples/numba_cffi.rst new file mode 100644 index 000000000000..fb2f85cceec9 --- /dev/null +++ b/doc/source/reference/random/examples/numba_cffi.rst @@ -0,0 +1,5 @@ +Extending via Numba and CFFI +---------------------------- + +.. literalinclude:: ../../../../../numpy/random/_examples/numba/extending_distributions.py + :language: python diff --git a/doc/source/reference/random/extending.rst b/doc/source/reference/random/extending.rst new file mode 100644 index 000000000000..2c506e94343f --- /dev/null +++ b/doc/source/reference/random/extending.rst @@ -0,0 +1,118 @@ +.. currentmodule:: numpy.random + +.. _extending: + +Extending +--------- +The BitGenerators have been designed to be extendable using standard tools for +high-performance Python -- numba and Cython. The `~Generator` object can also +be used with user-provided BitGenerators as long as these export a small set of +required functions. + +Numba +===== +Numba can be used with either CTypes or CFFI. The current iteration of the +BitGenerators all export a small set of functions through both interfaces. + +This example shows how numba can be used to produce gaussian samples using +a pure Python implementation which is then compiled. The random numbers are +provided by ``ctypes.next_double``. + +.. literalinclude:: ../../../../numpy/random/_examples/numba/extending.py + :language: python + :end-before: example 2 + +Both CTypes and CFFI allow the more complicated distributions to be used +directly in Numba after compiling the file distributions.c into a ``DLL`` or +``so``. An example showing the use of a more complicated distribution is in +the `examples` section below. + +.. _random_cython: + +Cython +====== + +Cython can be used to unpack the ``PyCapsule`` provided by a BitGenerator. +This example uses `PCG64` and the example from above. The usual caveats +for writing high-performance code using Cython -- removing bounds checks and +wrap around, providing array alignment information -- still apply. + +.. literalinclude:: ../../../../numpy/random/_examples/cython/extending_distributions.pyx + :language: cython + :end-before: example 2 + +The BitGenerator can also be directly accessed using the members of the ``bitgen_t`` +struct. + +.. literalinclude:: ../../../../numpy/random/_examples/cython/extending_distributions.pyx + :language: cython + :start-after: example 2 + :end-before: example 3 + +Cython can be used to directly access the functions in +``numpy/random/c_distributions.pxd``. This requires linking with the +``npyrandom`` library located in ``numpy/random/lib``. + +.. literalinclude:: ../../../../numpy/random/_examples/cython/extending_distributions.pyx + :language: cython + :start-after: example 3 + +See :ref:`extending_cython_example` for the complete listings of these examples +and a minimal ``setup.py`` to build the c-extension modules. + +CFFI +==== + +CFFI can be used to directly access the functions in +``include/numpy/random/distributions.h``. Some "massaging" of the header +file is required: + +.. literalinclude:: ../../../../numpy/random/_examples/cffi/extending.py + :language: python + :end-before: dlopen + +Once the header is parsed by ``ffi.cdef``, the functions can be accessed +directly from the ``_generator`` shared object, using the `BitGenerator.cffi` interface. + +.. literalinclude:: ../../../../numpy/random/_examples/cffi/extending.py + :language: python + :start-after: dlopen + + +New Bit Generators +================== +`~Generator` can be used with user-provided `~BitGenerator`\ s. The simplest +way to write a new BitGenerator is to examine the pyx file of one of the +existing BitGenerators. The key structure that must be provided is the +``capsule`` which contains a ``PyCapsule`` to a struct pointer of type +``bitgen_t``, + +.. code-block:: c + + typedef struct bitgen { + void *state; + uint64_t (*next_uint64)(void *st); + uint32_t (*next_uint32)(void *st); + double (*next_double)(void *st); + uint64_t (*next_raw)(void *st); + } bitgen_t; + +which provides 5 pointers. The first is an opaque pointer to the data structure +used by the BitGenerators. The next three are function pointers which return +the next 64- and 32-bit unsigned integers, the next random double and the next +raw value. This final function is used for testing and so can be set to +the next 64-bit unsigned integer function if not needed. Functions inside +``Generator`` use this structure as in + +.. code-block:: c + + bitgen_state->next_uint64(bitgen_state->state) + +Examples +======== + +.. toctree:: + Numba <examples/numba> + CFFI + Numba <examples/numba_cffi> + Cython <examples/cython/index> + CFFI <examples/cffi> diff --git a/doc/source/reference/random/generator.rst b/doc/source/reference/random/generator.rst new file mode 100644 index 000000000000..7934be98a6d4 --- /dev/null +++ b/doc/source/reference/random/generator.rst @@ -0,0 +1,180 @@ +.. currentmodule:: numpy.random + +Random Generator +---------------- +The `~Generator` provides access to +a wide range of distributions, and served as a replacement for +:class:`~numpy.random.RandomState`. The main difference between +the two is that ``Generator`` relies on an additional BitGenerator to +manage state and generate the random bits, which are then transformed into +random values from useful distributions. The default BitGenerator used by +``Generator`` is `~PCG64`. The BitGenerator +can be changed by passing an instantized BitGenerator to ``Generator``. + + +.. autofunction:: default_rng + +.. autoclass:: Generator + :members: __init__ + :exclude-members: __init__ + +Accessing the BitGenerator +========================== +.. autosummary:: + :toctree: generated/ + + ~numpy.random.Generator.bit_generator + +Simple random data +================== +.. autosummary:: + :toctree: generated/ + + ~numpy.random.Generator.integers + ~numpy.random.Generator.random + ~numpy.random.Generator.choice + ~numpy.random.Generator.bytes + +Permutations +============ +The methods for randomly permuting a sequence are + +.. autosummary:: + :toctree: generated/ + + ~numpy.random.Generator.shuffle + ~numpy.random.Generator.permutation + ~numpy.random.Generator.permuted + +The following table summarizes the behaviors of the methods. + ++--------------+-------------------+------------------+ +| method | copy/in-place | axis handling | ++==============+===================+==================+ +| shuffle | in-place | as if 1d | ++--------------+-------------------+------------------+ +| permutation | copy | as if 1d | ++--------------+-------------------+------------------+ +| permuted | either (use 'out' | axis independent | +| | for in-place) | | ++--------------+-------------------+------------------+ + +The following subsections provide more details about the differences. + +In-place vs. copy +~~~~~~~~~~~~~~~~~ +The main difference between `Generator.shuffle` and `Generator.permutation` +is that `Generator.shuffle` operates in-place, while `Generator.permutation` +returns a copy. + +By default, `Generator.permuted` returns a copy. To operate in-place with +`Generator.permuted`, pass the same array as the first argument *and* as +the value of the ``out`` parameter. For example, + + >>> rng = np.random.default_rng() + >>> x = np.arange(0, 15).reshape(3, 5) + >>> x + array([[ 0, 1, 2, 3, 4], + [ 5, 6, 7, 8, 9], + [10, 11, 12, 13, 14]]) + >>> y = rng.permuted(x, axis=1, out=x) + >>> x + array([[ 1, 0, 2, 4, 3], # random + [ 6, 7, 8, 9, 5], + [10, 14, 11, 13, 12]]) + +Note that when ``out`` is given, the return value is ``out``: + + >>> y is x + True + +Handling the ``axis`` parameter +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +An important distinction for these methods is how they handle the ``axis`` +parameter. Both `Generator.shuffle` and `Generator.permutation` treat the +input as a one-dimensional sequence, and the ``axis`` parameter determines +which dimension of the input array to use as the sequence. In the case of a +two-dimensional array, ``axis=0`` will, in effect, rearrange the rows of the +array, and ``axis=1`` will rearrange the columns. For example + + >>> rng = np.random.default_rng() + >>> x = np.arange(0, 15).reshape(3, 5) + >>> x + array([[ 0, 1, 2, 3, 4], + [ 5, 6, 7, 8, 9], + [10, 11, 12, 13, 14]]) + >>> rng.permutation(x, axis=1) + array([[ 1, 3, 2, 0, 4], # random + [ 6, 8, 7, 5, 9], + [11, 13, 12, 10, 14]]) + +Note that the columns have been rearranged "in bulk": the values within +each column have not changed. + +The method `Generator.permuted` treats the ``axis`` parameter similar to +how `numpy.sort` treats it. Each slice along the given axis is shuffled +independently of the others. Compare the following example of the use of +`Generator.permuted` to the above example of `Generator.permutation`: + + >>> rng.permuted(x, axis=1) + array([[ 1, 0, 2, 4, 3], # random + [ 5, 7, 6, 9, 8], + [10, 14, 12, 13, 11]]) + +In this example, the values within each row (i.e. the values along +``axis=1``) have been shuffled independently. This is not a "bulk" +shuffle of the columns. + +Shuffling non-NumPy sequences +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`Generator.shuffle` works on non-NumPy sequences. That is, if it is given +a sequence that is not a NumPy array, it shuffles that sequence in-place. +For example, + + >>> rng = np.random.default_rng() + >>> a = ['A', 'B', 'C', 'D', 'E'] + >>> rng.shuffle(a) # shuffle the list in-place + >>> a + ['B', 'D', 'A', 'E', 'C'] # random + +Distributions +============= +.. autosummary:: + :toctree: generated/ + + ~numpy.random.Generator.beta + ~numpy.random.Generator.binomial + ~numpy.random.Generator.chisquare + ~numpy.random.Generator.dirichlet + ~numpy.random.Generator.exponential + ~numpy.random.Generator.f + ~numpy.random.Generator.gamma + ~numpy.random.Generator.geometric + ~numpy.random.Generator.gumbel + ~numpy.random.Generator.hypergeometric + ~numpy.random.Generator.laplace + ~numpy.random.Generator.logistic + ~numpy.random.Generator.lognormal + ~numpy.random.Generator.logseries + ~numpy.random.Generator.multinomial + ~numpy.random.Generator.multivariate_hypergeometric + ~numpy.random.Generator.multivariate_normal + ~numpy.random.Generator.negative_binomial + ~numpy.random.Generator.noncentral_chisquare + ~numpy.random.Generator.noncentral_f + ~numpy.random.Generator.normal + ~numpy.random.Generator.pareto + ~numpy.random.Generator.poisson + ~numpy.random.Generator.power + ~numpy.random.Generator.rayleigh + ~numpy.random.Generator.standard_cauchy + ~numpy.random.Generator.standard_exponential + ~numpy.random.Generator.standard_gamma + ~numpy.random.Generator.standard_normal + ~numpy.random.Generator.standard_t + ~numpy.random.Generator.triangular + ~numpy.random.Generator.uniform + ~numpy.random.Generator.vonmises + ~numpy.random.Generator.wald + ~numpy.random.Generator.weibull + ~numpy.random.Generator.zipf diff --git a/doc/source/reference/random/index.rst b/doc/source/reference/random/index.rst new file mode 100644 index 000000000000..aaabc9b39278 --- /dev/null +++ b/doc/source/reference/random/index.rst @@ -0,0 +1,254 @@ +.. _numpyrandom: + +.. py:module:: numpy.random + +.. currentmodule:: numpy.random + +Random sampling (:mod:`numpy.random`) +===================================== + +Numpy's random number routines produce pseudo random numbers using +combinations of a `BitGenerator` to create sequences and a `Generator` +to use those sequences to sample from different statistical distributions: + +* BitGenerators: Objects that generate random numbers. These are typically + unsigned integer words filled with sequences of either 32 or 64 random bits. +* Generators: Objects that transform sequences of random bits from a + BitGenerator into sequences of numbers that follow a specific probability + distribution (such as uniform, Normal or Binomial) within a specified + interval. + +Since Numpy version 1.17.0 the Generator can be initialized with a +number of different BitGenerators. It exposes many different probability +distributions. See `NEP 19 <https://www.numpy.org/neps/ +nep-0019-rng-policy.html>`_ for context on the updated random Numpy number +routines. The legacy `RandomState` random number routines are still +available, but limited to a single BitGenerator. See :ref:`new-or-different` +for a complete list of improvements and differences from the legacy +``RandomState``. + +For convenience and backward compatibility, a single `RandomState` +instance's methods are imported into the numpy.random namespace, see +:ref:`legacy` for the complete list. + +.. _random-quick-start: + +Quick Start +----------- + +Call `default_rng` to get a new instance of a `Generator`, then call its +methods to obtain samples from different distributions. By default, +`Generator` uses bits provided by `PCG64` which has better statistical +properties than the legacy `MT19937` used in `RandomState`. + +.. code-block:: python + + # Do this (new version) + from numpy.random import default_rng + rng = default_rng() + vals = rng.standard_normal(10) + more_vals = rng.standard_normal(10) + + # instead of this (legacy version) + from numpy import random + vals = random.standard_normal(10) + more_vals = random.standard_normal(10) + +`Generator` can be used as a replacement for `RandomState`. Both class +instances hold an internal `BitGenerator` instance to provide the bit +stream, it is accessible as ``gen.bit_generator``. Some long-overdue API +cleanup means that legacy and compatibility methods have been removed from +`Generator` + +=================== ============== ============ +`RandomState` `Generator` Notes +------------------- -------------- ------------ +``random_sample``, ``random`` Compatible with `random.random` +``rand`` +------------------- -------------- ------------ +``randint``, ``integers`` Add an ``endpoint`` kwarg +``random_integers`` +------------------- -------------- ------------ +``tomaxint`` removed Use ``integers(0, np.iinfo(np.int_).max,`` + ``endpoint=False)`` +------------------- -------------- ------------ +``seed`` removed Use `SeedSequence.spawn` +=================== ============== ============ + +See :ref:`new-or-different` for more information. + +Something like the following code can be used to support both ``RandomState`` +and ``Generator``, with the understanding that the interfaces are slightly +different + +.. code-block:: python + + try: + rng_integers = rng.integers + except AttributeError: + rng_integers = rng.randint + a = rng_integers(1000) + +Seeds can be passed to any of the BitGenerators. The provided value is mixed +via `SeedSequence` to spread a possible sequence of seeds across a wider +range of initialization states for the BitGenerator. Here `PCG64` is used and +is wrapped with a `Generator`. + +.. code-block:: python + + from numpy.random import Generator, PCG64 + rng = Generator(PCG64(12345)) + rng.standard_normal() + +Here we use `default_rng` to create an instance of `Generator` to generate a +random float: + +>>> import numpy as np +>>> rng = np.random.default_rng(12345) +>>> print(rng) +Generator(PCG64) +>>> rfloat = rng.random() +>>> rfloat +0.22733602246716966 +>>> type(rfloat) +<class 'float'> + +Here we use `default_rng` to create an instance of `Generator` to generate 3 +random integers between 0 (inclusive) and 10 (exclusive): + +>>> import numpy as np +>>> rng = np.random.default_rng(12345) +>>> rints = rng.integers(low=0, high=10, size=3) +>>> rints +array([6, 2, 7]) +>>> type(rints[0]) +<class 'numpy.int64'> + +Introduction +------------ +The new infrastructure takes a different approach to producing random numbers +from the `RandomState` object. Random number generation is separated into +two components, a bit generator and a random generator. + +The `BitGenerator` has a limited set of responsibilities. It manages state +and provides functions to produce random doubles and random unsigned 32- and +64-bit values. + +The `random generator <Generator>` takes the +bit generator-provided stream and transforms them into more useful +distributions, e.g., simulated normal random values. This structure allows +alternative bit generators to be used with little code duplication. + +The `Generator` is the user-facing object that is nearly identical to the +legacy `RandomState`. It accepts a bit generator instance as an argument. +The default is currently `PCG64` but this may change in future versions. +As a convenience NumPy provides the `default_rng` function to hide these +details: + +>>> from numpy.random import default_rng +>>> rng = default_rng(12345) +>>> print(rng) +Generator(PCG64) +>>> print(rng.random()) +0.22733602246716966 + +One can also instantiate `Generator` directly with a `BitGenerator` instance. + +To use the default `PCG64` bit generator, one can instantiate it directly and +pass it to `Generator`: + +>>> from numpy.random import Generator, PCG64 +>>> rng = Generator(PCG64(12345)) +>>> print(rng) +Generator(PCG64) + +Similarly to use the older `MT19937` bit generator (not recommended), one can +instantiate it directly and pass it to `Generator`: + +>>> from numpy.random import Generator, MT19937 +>>> rng = Generator(MT19937(12345)) +>>> print(rng) +Generator(MT19937) + +What's New or Different +~~~~~~~~~~~~~~~~~~~~~~~ +.. warning:: + + The Box-Muller method used to produce NumPy's normals is no longer available + in `Generator`. It is not possible to reproduce the exact random + values using Generator for the normal distribution or any other + distribution that relies on the normal such as the `RandomState.gamma` or + `RandomState.standard_t`. If you require bitwise backward compatible + streams, use `RandomState`. + +* The Generator's normal, exponential and gamma functions use 256-step Ziggurat + methods which are 2-10 times faster than NumPy's Box-Muller or inverse CDF + implementations. +* Optional ``dtype`` argument that accepts ``np.float32`` or ``np.float64`` + to produce either single or double prevision uniform random variables for + select distributions +* Optional ``out`` argument that allows existing arrays to be filled for + select distributions +* All BitGenerators can produce doubles, uint64s and uint32s via CTypes + (`PCG64.ctypes`) and CFFI (`PCG64.cffi`). This allows the bit generators + to be used in numba. +* The bit generators can be used in downstream projects via + :ref:`Cython <random_cython>`. +* `Generator.integers` is now the canonical way to generate integer + random numbers from a discrete uniform distribution. The ``rand`` and + ``randn`` methods are only available through the legacy `RandomState`. + The ``endpoint`` keyword can be used to specify open or closed intervals. + This replaces both ``randint`` and the deprecated ``random_integers``. +* `Generator.random` is now the canonical way to generate floating-point + random numbers, which replaces `RandomState.random_sample`, + `RandomState.sample`, and `RandomState.ranf`. This is consistent with + Python's `random.random`. +* All BitGenerators in numpy use `SeedSequence` to convert seeds into + initialized states. +* The addition of an ``axis`` keyword argument to methods such as + `Generator.choice`, `Generator.permutation`, and `Generator.shuffle` + improves support for sampling from and shuffling multi-dimensional arrays. + +See :ref:`new-or-different` for a complete list of improvements and +differences from the traditional ``Randomstate``. + +Parallel Generation +~~~~~~~~~~~~~~~~~~~ + +The included generators can be used in parallel, distributed applications in +one of three ways: + +* :ref:`seedsequence-spawn` +* :ref:`independent-streams` +* :ref:`parallel-jumped` + +Users with a very large amount of parallelism will want to consult +:ref:`upgrading-pcg64`. + +Concepts +-------- +.. toctree:: + :maxdepth: 1 + + generator + Legacy Generator (RandomState) <legacy> + BitGenerators, SeedSequences <bit_generators/index> + Upgrading PCG64 with PCG64DXSM <upgrading-pcg64> + +Features +-------- +.. toctree:: + :maxdepth: 2 + + Parallel Applications <parallel> + Multithreaded Generation <multithreading> + new-or-different + Comparing Performance <performance> + c-api + Examples of using Numba, Cython, CFFI <extending> + +Original Source of the Generator and BitGenerators +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This package was developed independently of NumPy and was integrated in version +1.17.0. The original repo is at https://github.com/bashtage/randomgen. diff --git a/doc/source/reference/random/legacy.rst b/doc/source/reference/random/legacy.rst new file mode 100644 index 000000000000..42437dbb6173 --- /dev/null +++ b/doc/source/reference/random/legacy.rst @@ -0,0 +1,192 @@ +.. currentmodule:: numpy.random + +.. _legacy: + +Legacy Random Generation +------------------------ +The `RandomState` provides access to +legacy generators. This generator is considered frozen and will have +no further improvements. It is guaranteed to produce the same values +as the final point release of NumPy v1.16. These all depend on Box-Muller +normals or inverse CDF exponentials or gammas. This class should only be used +if it is essential to have randoms that are identical to what +would have been produced by previous versions of NumPy. + +`RandomState` adds additional information +to the state which is required when using Box-Muller normals since these +are produced in pairs. It is important to use +`RandomState.get_state`, and not the underlying bit generators +`state`, when accessing the state so that these extra values are saved. + +Although we provide the `MT19937` BitGenerator for use independent of +`RandomState`, note that its default seeding uses `SeedSequence` +rather than the legacy seeding algorithm. `RandomState` will use the +legacy seeding algorithm. The methods to use the legacy seeding algorithm are +currently private as the main reason to use them is just to implement +`RandomState`. However, one can reset the state of `MT19937` +using the state of the `RandomState`: + +.. code-block:: python + + from numpy.random import MT19937 + from numpy.random import RandomState + + rs = RandomState(12345) + mt19937 = MT19937() + mt19937.state = rs.get_state() + rs2 = RandomState(mt19937) + + # Same output + rs.standard_normal() + rs2.standard_normal() + + rs.random() + rs2.random() + + rs.standard_exponential() + rs2.standard_exponential() + + +.. autoclass:: RandomState + :members: __init__ + :exclude-members: __init__ + +Seeding and State +================= + +.. autosummary:: + :toctree: generated/ + + ~RandomState.get_state + ~RandomState.set_state + ~RandomState.seed + +Simple random data +================== +.. autosummary:: + :toctree: generated/ + + ~RandomState.rand + ~RandomState.randn + ~RandomState.randint + ~RandomState.random_integers + ~RandomState.random_sample + ~RandomState.choice + ~RandomState.bytes + +Permutations +============ +.. autosummary:: + :toctree: generated/ + + ~RandomState.shuffle + ~RandomState.permutation + +Distributions +============= +.. autosummary:: + :toctree: generated/ + + ~RandomState.beta + ~RandomState.binomial + ~RandomState.chisquare + ~RandomState.dirichlet + ~RandomState.exponential + ~RandomState.f + ~RandomState.gamma + ~RandomState.geometric + ~RandomState.gumbel + ~RandomState.hypergeometric + ~RandomState.laplace + ~RandomState.logistic + ~RandomState.lognormal + ~RandomState.logseries + ~RandomState.multinomial + ~RandomState.multivariate_normal + ~RandomState.negative_binomial + ~RandomState.noncentral_chisquare + ~RandomState.noncentral_f + ~RandomState.normal + ~RandomState.pareto + ~RandomState.poisson + ~RandomState.power + ~RandomState.rayleigh + ~RandomState.standard_cauchy + ~RandomState.standard_exponential + ~RandomState.standard_gamma + ~RandomState.standard_normal + ~RandomState.standard_t + ~RandomState.triangular + ~RandomState.uniform + ~RandomState.vonmises + ~RandomState.wald + ~RandomState.weibull + ~RandomState.zipf + +Functions in `numpy.random` +=========================== +Many of the RandomState methods above are exported as functions in +`numpy.random` This usage is discouraged, as it is implemented via a global +`RandomState` instance which is not advised on two counts: + +- It uses global state, which means results will change as the code changes + +- It uses a `RandomState` rather than the more modern `Generator`. + +For backward compatible legacy reasons, we cannot change this. See +:ref:`random-quick-start`. + +.. autosummary:: + :toctree: generated/ + + beta + binomial + bytes + chisquare + choice + dirichlet + exponential + f + gamma + geometric + get_state + gumbel + hypergeometric + laplace + logistic + lognormal + logseries + multinomial + multivariate_normal + negative_binomial + noncentral_chisquare + noncentral_f + normal + pareto + permutation + poisson + power + rand + randint + randn + random + random_integers + random_sample + ranf + rayleigh + sample + seed + set_state + shuffle + standard_cauchy + standard_exponential + standard_gamma + standard_normal + standard_t + triangular + uniform + vonmises + wald + weibull + zipf + diff --git a/doc/source/reference/random/multithreading.rst b/doc/source/reference/random/multithreading.rst new file mode 100644 index 000000000000..4b221d9aa6c1 --- /dev/null +++ b/doc/source/reference/random/multithreading.rst @@ -0,0 +1,115 @@ +Multithreaded Generation +======================== + +The four core distributions (:meth:`~.Generator.random`, +:meth:`~.Generator.standard_normal`, :meth:`~.Generator.standard_exponential`, +and :meth:`~.Generator.standard_gamma`) all allow existing arrays to be filled +using the ``out`` keyword argument. Existing arrays need to be contiguous and +well-behaved (writable and aligned). Under normal circumstances, arrays +created using the common constructors such as :meth:`numpy.empty` will satisfy +these requirements. + +This example makes use of Python 3 :mod:`concurrent.futures` to fill an array +using multiple threads. Threads are long-lived so that repeated calls do not +require any additional overheads from thread creation. + +The random numbers generated are reproducible in the sense that the same +seed will produce the same outputs, given that the number of threads does not +change. + +.. code-block:: ipython + + from numpy.random import default_rng, SeedSequence + import multiprocessing + import concurrent.futures + import numpy as np + + class MultithreadedRNG: + def __init__(self, n, seed=None, threads=None): + if threads is None: + threads = multiprocessing.cpu_count() + self.threads = threads + + seq = SeedSequence(seed) + self._random_generators = [default_rng(s) + for s in seq.spawn(threads)] + + self.n = n + self.executor = concurrent.futures.ThreadPoolExecutor(threads) + self.values = np.empty(n) + self.step = np.ceil(n / threads).astype(np.int_) + + def fill(self): + def _fill(random_state, out, first, last): + random_state.standard_normal(out=out[first:last]) + + futures = {} + for i in range(self.threads): + args = (_fill, + self._random_generators[i], + self.values, + i * self.step, + (i + 1) * self.step) + futures[self.executor.submit(*args)] = i + concurrent.futures.wait(futures) + + def __del__(self): + self.executor.shutdown(False) + + + +The multithreaded random number generator can be used to fill an array. +The ``values`` attributes shows the zero-value before the fill and the +random value after. + +.. code-block:: ipython + + In [2]: mrng = MultithreadedRNG(10000000, seed=12345) + ...: print(mrng.values[-1]) + Out[2]: 0.0 + + In [3]: mrng.fill() + ...: print(mrng.values[-1]) + Out[3]: 2.4545724517479104 + +The time required to produce using multiple threads can be compared to +the time required to generate using a single thread. + +.. code-block:: ipython + + In [4]: print(mrng.threads) + ...: %timeit mrng.fill() + + Out[4]: 4 + ...: 32.8 ms ± 2.71 ms per loop (mean ± std. dev. of 7 runs, 10 loops each) + +The single threaded call directly uses the BitGenerator. + +.. code-block:: ipython + + In [5]: values = np.empty(10000000) + ...: rg = default_rng() + ...: %timeit rg.standard_normal(out=values) + + Out[5]: 99.6 ms ± 222 µs per loop (mean ± std. dev. of 7 runs, 10 loops each) + +The gains are substantial and the scaling is reasonable even for arrays that +are only moderately large. The gains are even larger when compared to a call +that does not use an existing array due to array creation overhead. + +.. code-block:: ipython + + In [6]: rg = default_rng() + ...: %timeit rg.standard_normal(10000000) + + Out[6]: 125 ms ± 309 µs per loop (mean ± std. dev. of 7 runs, 10 loops each) + +Note that if `threads` is not set by the user, it will be determined by +`multiprocessing.cpu_count()`. + +.. code-block:: ipython + + In [7]: # simulate the behavior for `threads=None`, if the machine had only one thread + ...: mrng = MultithreadedRNG(10000000, seed=12345, threads=1) + ...: print(mrng.values[-1]) + Out[7]: 1.1800150052158556 diff --git a/doc/source/reference/random/new-or-different.rst b/doc/source/reference/random/new-or-different.rst new file mode 100644 index 000000000000..a815439267fc --- /dev/null +++ b/doc/source/reference/random/new-or-different.rst @@ -0,0 +1,129 @@ +.. _new-or-different: + +.. currentmodule:: numpy.random + +What's New or Different +----------------------- + +.. warning:: + + The Box-Muller method used to produce NumPy's normals is no longer available + in `Generator`. It is not possible to reproduce the exact random + values using ``Generator`` for the normal distribution or any other + distribution that relies on the normal such as the `Generator.gamma` or + `Generator.standard_t`. If you require bitwise backward compatible + streams, use `RandomState`, i.e., `RandomState.gamma` or + `RandomState.standard_t`. + +Quick comparison of legacy :ref:`mtrand <legacy>` to the new `Generator` + +================== ==================== ============= +Feature Older Equivalent Notes +------------------ -------------------- ------------- +`~.Generator` `~.RandomState` ``Generator`` requires a stream + source, called a `BitGenerator` + A number of these are provided. + ``RandomState`` uses + the Mersenne Twister `~.MT19937` by + default, but can also be instantiated + with any BitGenerator. +------------------ -------------------- ------------- +``random`` ``random_sample``, Access the values in a BitGenerator, + ``rand`` convert them to ``float64`` in the + interval ``[0.0.,`` `` 1.0)``. + In addition to the ``size`` kwarg, now + supports ``dtype='d'`` or ``dtype='f'``, + and an ``out`` kwarg to fill a user- + supplied array. + + Many other distributions are also + supported. +------------------ -------------------- ------------- +``integers`` ``randint``, Use the ``endpoint`` kwarg to adjust + ``random_integers`` the inclusion or exclution of the + ``high`` interval endpoint +================== ==================== ============= + +And in more detail: + +* Simulate from the complex normal distribution + (`~.Generator.complex_normal`) +* The normal, exponential and gamma generators use 256-step Ziggurat + methods which are 2-10 times faster than NumPy's default implementation in + `~.Generator.standard_normal`, `~.Generator.standard_exponential` or + `~.Generator.standard_gamma`. + + +.. ipython:: python + + from numpy.random import Generator, PCG64 + import numpy.random + rng = Generator(PCG64()) + %timeit -n 1 rng.standard_normal(100000) + %timeit -n 1 numpy.random.standard_normal(100000) + +.. ipython:: python + + %timeit -n 1 rng.standard_exponential(100000) + %timeit -n 1 numpy.random.standard_exponential(100000) + +.. ipython:: python + + %timeit -n 1 rng.standard_gamma(3.0, 100000) + %timeit -n 1 numpy.random.standard_gamma(3.0, 100000) + + +* `~.Generator.integers` is now the canonical way to generate integer + random numbers from a discrete uniform distribution. The ``rand`` and + ``randn`` methods are only available through the legacy `~.RandomState`. + This replaces both ``randint`` and the deprecated ``random_integers``. +* The Box-Muller method used to produce NumPy's normals is no longer available. +* All bit generators can produce doubles, uint64s and + uint32s via CTypes (`~PCG64.ctypes`) and CFFI (`~PCG64.cffi`). + This allows these bit generators to be used in numba. +* The bit generators can be used in downstream projects via + Cython. +* Optional ``dtype`` argument that accepts ``np.float32`` or ``np.float64`` + to produce either single or double prevision uniform random variables for + select distributions + + * Uniforms (`~.Generator.random` and `~.Generator.integers`) + * Normals (`~.Generator.standard_normal`) + * Standard Gammas (`~.Generator.standard_gamma`) + * Standard Exponentials (`~.Generator.standard_exponential`) + +.. ipython:: python + + rng = Generator(PCG64(0)) + rng.random(3, dtype='d') + rng.random(3, dtype='f') + +* Optional ``out`` argument that allows existing arrays to be filled for + select distributions + + * Uniforms (`~.Generator.random`) + * Normals (`~.Generator.standard_normal`) + * Standard Gammas (`~.Generator.standard_gamma`) + * Standard Exponentials (`~.Generator.standard_exponential`) + + This allows multithreading to fill large arrays in chunks using suitable + BitGenerators in parallel. + +.. ipython:: python + + existing = np.zeros(4) + rng.random(out=existing[:2]) + print(existing) + +* Optional ``axis`` argument for methods like `~.Generator.choice`, + `~.Generator.permutation` and `~.Generator.shuffle` that controls which + axis an operation is performed over for multi-dimensional arrays. + +.. ipython:: python + + rng = Generator(PCG64(123456789)) + a = np.arange(12).reshape((3, 4)) + a + rng.choice(a, axis=1, size=5) + rng.shuffle(a, axis=1) # Shuffle in-place + a diff --git a/doc/source/reference/random/parallel.rst b/doc/source/reference/random/parallel.rst new file mode 100644 index 000000000000..7f0207bdebb2 --- /dev/null +++ b/doc/source/reference/random/parallel.rst @@ -0,0 +1,196 @@ +Parallel Random Number Generation +================================= + +There are three strategies implemented that can be used to produce +repeatable pseudo-random numbers across multiple processes (local +or distributed). + +.. currentmodule:: numpy.random + +.. _seedsequence-spawn: + +`~SeedSequence` spawning +------------------------ + +`~SeedSequence` `implements an algorithm`_ to process a user-provided seed, +typically as an integer of some size, and to convert it into an initial state for +a `~BitGenerator`. It uses hashing techniques to ensure that low-quality seeds +are turned into high quality initial states (at least, with very high +probability). + +For example, `MT19937` has a state consisting of 624 +`uint32` integers. A naive way to take a 32-bit integer seed would be to just set +the last element of the state to the 32-bit seed and leave the rest 0s. This is +a valid state for `MT19937`, but not a good one. The Mersenne Twister +algorithm `suffers if there are too many 0s`_. Similarly, two adjacent 32-bit +integer seeds (i.e. ``12345`` and ``12346``) would produce very similar +streams. + +`~SeedSequence` avoids these problems by using successions of integer hashes +with good `avalanche properties`_ to ensure that flipping any bit in the input +input has about a 50% chance of flipping any bit in the output. Two input seeds +that are very close to each other will produce initial states that are very far +from each other (with very high probability). It is also constructed in such +a way that you can provide arbitrary-sized integers or lists of integers. +`~SeedSequence` will take all of the bits that you provide and mix them +together to produce however many bits the consuming `~BitGenerator` needs to +initialize itself. + +These properties together mean that we can safely mix together the usual +user-provided seed with simple incrementing counters to get `~BitGenerator` +states that are (to very high probability) independent of each other. We can +wrap this together into an API that is easy to use and difficult to misuse. + +.. code-block:: python + + from numpy.random import SeedSequence, default_rng + + ss = SeedSequence(12345) + + # Spawn off 10 child SeedSequences to pass to child processes. + child_seeds = ss.spawn(10) + streams = [default_rng(s) for s in child_seeds] + +.. end_block + +Child `~SeedSequence` objects can also spawn to make grandchildren, and so on. +Each `~SeedSequence` has its position in the tree of spawned `~SeedSequence` +objects mixed in with the user-provided seed to generate independent (with very +high probability) streams. + +.. code-block:: python + + grandchildren = child_seeds[0].spawn(4) + grand_streams = [default_rng(s) for s in grandchildren] + +.. end_block + +This feature lets you make local decisions about when and how to split up +streams without coordination between processes. You do not have to preallocate +space to avoid overlapping or request streams from a common global service. This +general "tree-hashing" scheme is `not unique to numpy`_ but not yet widespread. +Python has increasingly-flexible mechanisms for parallelization available, and +this scheme fits in very well with that kind of use. + +Using this scheme, an upper bound on the probability of a collision can be +estimated if one knows the number of streams that you derive. `~SeedSequence` +hashes its inputs, both the seed and the spawn-tree-path, down to a 128-bit +pool by default. The probability that there is a collision in +that pool, pessimistically-estimated ([1]_), will be about :math:`n^2*2^{-128}` where +`n` is the number of streams spawned. If a program uses an aggressive million +streams, about :math:`2^{20}`, then the probability that at least one pair of +them are identical is about :math:`2^{-88}`, which is in solidly-ignorable +territory ([2]_). + +.. [1] The algorithm is carefully designed to eliminate a number of possible + ways to collide. For example, if one only does one level of spawning, it + is guaranteed that all states will be unique. But it's easier to + estimate the naive upper bound on a napkin and take comfort knowing + that the probability is actually lower. + +.. [2] In this calculation, we can mostly ignore the amount of numbers drawn from each + stream. See :ref:`upgrading-pcg64` for the technical details about + `PCG64`. The other PRNGs we provide have some extra protection built in + that avoids overlaps if the `~SeedSequence` pools differ in the + slightest bit. `PCG64DXSM` has :math:`2^{127}` separate cycles + determined by the seed in addition to the position in the + :math:`2^{128}` long period for each cycle, so one has to both get on or + near the same cycle *and* seed a nearby position in the cycle. + `Philox` has completely independent cycles determined by the seed. + `SFC64` incorporates a 64-bit counter so every unique seed is at + least :math:`2^{64}` iterations away from any other seed. And + finally, `MT19937` has just an unimaginably huge period. Getting + a collision internal to `SeedSequence` is the way a failure would be + observed. + +.. _`implements an algorithm`: http://www.pcg-random.org/posts/developing-a-seed_seq-alternative.html +.. _`suffers if there are too many 0s`: http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/MT2002/emt19937ar.html +.. _`avalanche properties`: https://en.wikipedia.org/wiki/Avalanche_effect +.. _`not unique to numpy`: https://www.iro.umontreal.ca/~lecuyer/myftp/papers/parallel-rng-imacs.pdf + + +.. _independent-streams: + +Independent Streams +------------------- + +`Philox` is a counter-based RNG based which generates values by +encrypting an incrementing counter using weak cryptographic primitives. The +seed determines the key that is used for the encryption. Unique keys create +unique, independent streams. `Philox` lets you bypass the +seeding algorithm to directly set the 128-bit key. Similar, but different, keys +will still create independent streams. + +.. code-block:: python + + import secrets + from numpy.random import Philox + + # 128-bit number as a seed + root_seed = secrets.getrandbits(128) + streams = [Philox(key=root_seed + stream_id) for stream_id in range(10)] + +.. end_block + +This scheme does require that you avoid reusing stream IDs. This may require +coordination between the parallel processes. + + +.. _parallel-jumped: + +Jumping the BitGenerator state +------------------------------ + +``jumped`` advances the state of the BitGenerator *as-if* a large number of +random numbers have been drawn, and returns a new instance with this state. +The specific number of draws varies by BitGenerator, and ranges from +:math:`2^{64}` to :math:`2^{128}`. Additionally, the *as-if* draws also depend +on the size of the default random number produced by the specific BitGenerator. +The BitGenerators that support ``jumped``, along with the period of the +BitGenerator, the size of the jump and the bits in the default unsigned random +are listed below. + ++-----------------+-------------------------+-------------------------+-------------------------+ +| BitGenerator | Period | Jump Size | Bits per Draw | ++=================+=========================+=========================+=========================+ +| MT19937 | :math:`2^{19937}-1` | :math:`2^{128}` | 32 | ++-----------------+-------------------------+-------------------------+-------------------------+ +| PCG64 | :math:`2^{128}` | :math:`~2^{127}` ([3]_) | 64 | ++-----------------+-------------------------+-------------------------+-------------------------+ +| PCG64DXSM | :math:`2^{128}` | :math:`~2^{127}` ([3]_) | 64 | ++-----------------+-------------------------+-------------------------+-------------------------+ +| Philox | :math:`2^{256}` | :math:`2^{128}` | 64 | ++-----------------+-------------------------+-------------------------+-------------------------+ + +.. [3] The jump size is :math:`(\phi-1)*2^{128}` where :math:`\phi` is the + golden ratio. As the jumps wrap around the period, the actual distances + between neighboring streams will slowly grow smaller than the jump size, + but using the golden ratio this way is a classic method of constructing + a low-discrepancy sequence that spreads out the states around the period + optimally. You will not be able to jump enough to make those distances + small enough to overlap in your lifetime. + +``jumped`` can be used to produce long blocks which should be long enough to not +overlap. + +.. code-block:: python + + import secrets + from numpy.random import PCG64 + + seed = secrets.getrandbits(128) + blocked_rng = [] + rng = PCG64(seed) + for i in range(10): + blocked_rng.append(rng.jumped(i)) + +.. end_block + +When using ``jumped``, one does have to take care not to jump to a stream that +was already used. In the above example, one could not later use +``blocked_rng[0].jumped()`` as it would overlap with ``blocked_rng[1]``. Like +with the independent streams, if the main process here wants to split off 10 +more streams by jumping, then it needs to start with ``range(10, 20)``, +otherwise it would recreate the same streams. On the other hand, if you +carefully construct the streams, then you are guaranteed to have streams that +do not overlap. diff --git a/doc/source/reference/random/performance.py b/doc/source/reference/random/performance.py new file mode 100644 index 000000000000..794142836652 --- /dev/null +++ b/doc/source/reference/random/performance.py @@ -0,0 +1,86 @@ +from timeit import repeat + +import pandas as pd + +import numpy as np +from numpy.random import MT19937, PCG64, PCG64DXSM, Philox, SFC64 + +PRNGS = [MT19937, PCG64, PCG64DXSM, Philox, SFC64] + +funcs = {} +integers = 'integers(0, 2**{bits},size=1000000, dtype="uint{bits}")' +funcs['32-bit Unsigned Ints'] = integers.format(bits=32) +funcs['64-bit Unsigned Ints'] = integers.format(bits=64) +funcs['Uniforms'] = 'random(size=1000000)' +funcs['Normals'] = 'standard_normal(size=1000000)' +funcs['Exponentials'] = 'standard_exponential(size=1000000)' +funcs['Gammas'] = 'standard_gamma(3.0,size=1000000)' +funcs['Binomials'] = 'binomial(9, .1, size=1000000)' +funcs['Laplaces'] = 'laplace(size=1000000)' +funcs['Poissons'] = 'poisson(3.0, size=1000000)' + +setup = """ +from numpy.random import {prng}, Generator +rg = Generator({prng}()) +""" + +test = "rg.{func}" +table = {} +for prng in PRNGS: + print(prng) + col = {} + for key in funcs: + t = repeat(test.format(func=funcs[key]), + setup.format(prng=prng().__class__.__name__), + number=1, repeat=3) + col[key] = 1000 * min(t) + col = pd.Series(col) + table[prng().__class__.__name__] = col + +npfuncs = {} +npfuncs.update(funcs) +npfuncs['32-bit Unsigned Ints'] = 'randint(2**32,dtype="uint32",size=1000000)' +npfuncs['64-bit Unsigned Ints'] = 'randint(2**64,dtype="uint64",size=1000000)' +setup = """ +from numpy.random import RandomState +rg = RandomState() +""" +col = {} +for key in npfuncs: + t = repeat(test.format(func=npfuncs[key]), + setup.format(prng=prng().__class__.__name__), + number=1, repeat=3) + col[key] = 1000 * min(t) +table['RandomState'] = pd.Series(col) + +columns = ['MT19937', 'PCG64', 'PCG64DXSM', 'Philox', 'SFC64', 'RandomState'] +table = pd.DataFrame(table) +order = np.log(table).mean().sort_values().index +table = table.T +table = table.reindex(columns) +table = table.T +table = table.reindex([k for k in funcs], axis=0) +print(table.to_csv(float_format='%0.1f')) + + +rel = table.loc[:, ['RandomState']].values @ np.ones( + (1, table.shape[1])) / table +rel.pop('RandomState') +rel = rel.T +rel['Overall'] = np.exp(np.log(rel).mean(1)) +rel *= 100 +rel = np.round(rel) +rel = rel.T +print(rel.to_csv(float_format='%0d')) + +# Cross-platform table +rows = ['32-bit Unsigned Ints','64-bit Unsigned Ints','Uniforms','Normals','Exponentials'] +xplat = rel.reindex(rows, axis=0) +xplat = 100 * (xplat / xplat.MT19937.values[:,None]) +overall = np.exp(np.log(xplat).mean(0)) +xplat = xplat.T.copy() +xplat['Overall']=overall +print(xplat.T.round(1)) + + + diff --git a/doc/source/reference/random/performance.rst b/doc/source/reference/random/performance.rst new file mode 100644 index 000000000000..cb9b94113c2f --- /dev/null +++ b/doc/source/reference/random/performance.rst @@ -0,0 +1,155 @@ +Performance +----------- + +.. currentmodule:: numpy.random + +Recommendation +************** + +The recommended generator for general use is `PCG64` or its upgraded variant +`PCG64DXSM` for heavily-parallel use cases. They are statistically high quality, +full-featured, and fast on most platforms, but somewhat slow when compiled for +32-bit processes. See :ref:`upgrading-pcg64` for details on when heavy +parallelism would indicate using `PCG64DXSM`. + +`Philox` is fairly slow, but its statistical properties have +very high quality, and it is easy to get an assuredly-independent stream by using +unique keys. If that is the style you wish to use for parallel streams, or you +are porting from another system that uses that style, then +`Philox` is your choice. + +`SFC64` is statistically high quality and very fast. However, it +lacks jumpability. If you are not using that capability and want lots of speed, +even on 32-bit processes, this is your choice. + +`MT19937` `fails some statistical tests`_ and is not especially +fast compared to modern PRNGs. For these reasons, we mostly do not recommend +using it on its own, only through the legacy `~.RandomState` for +reproducing old results. That said, it has a very long history as a default in +many systems. + +.. _`fails some statistical tests`: https://www.iro.umontreal.ca/~lecuyer/myftp/papers/testu01.pdf + +Timings +******* + +The timings below are the time in ns to produce 1 random value from a +specific distribution. The original `MT19937` generator is +much slower since it requires 2 32-bit values to equal the output of the +faster generators. + +Integer performance has a similar ordering. + +The pattern is similar for other, more complex generators. The normal +performance of the legacy `RandomState` generator is much +lower than the other since it uses the Box-Muller transform rather +than the Ziggurat method. The performance gap for Exponentials is also +large due to the cost of computing the log function to invert the CDF. +The column labeled MT19973 uses the same 32-bit generator as +`RandomState` but produces random variates using `Generator`. + +.. csv-table:: + :header: ,MT19937,PCG64,PCG64DXSM,Philox,SFC64,RandomState + :widths: 14,14,14,14,14,14,14 + + 32-bit Unsigned Ints,3.3,1.9,2.0,3.3,1.8,3.1 + 64-bit Unsigned Ints,5.6,3.2,2.9,4.9,2.5,5.5 + Uniforms,5.9,3.1,2.9,5.0,2.6,6.0 + Normals,13.9,10.8,10.5,12.0,8.3,56.8 + Exponentials,9.1,6.0,5.8,8.1,5.4,63.9 + Gammas,37.2,30.8,28.9,34.0,27.5,77.0 + Binomials,21.3,17.4,17.6,19.3,15.6,21.4 + Laplaces,73.2,72.3,76.1,73.0,72.3,82.5 + Poissons,111.7,103.4,100.5,109.4,90.7,115.2 + +The next table presents the performance in percentage relative to values +generated by the legacy generator, ``RandomState(MT19937())``. The overall +performance was computed using a geometric mean. + +.. csv-table:: + :header: ,MT19937,PCG64,PCG64DXSM,Philox,SFC64 + :widths: 14,14,14,14,14,14 + + 32-bit Unsigned Ints,96,162,160,96,175 + 64-bit Unsigned Ints,97,171,188,113,218 + Uniforms,102,192,206,121,233 + Normals,409,526,541,471,684 + Exponentials,701,1071,1101,784,1179 + Gammas,207,250,266,227,281 + Binomials,100,123,122,111,138 + Laplaces,113,114,108,113,114 + Poissons,103,111,115,105,127 + Overall,159,219,225,174,251 + +.. note:: + + All timings were taken using Linux on an AMD Ryzen 9 3900X processor. + +Performance on different Operating Systems +****************************************** +Performance differs across platforms due to compiler and hardware availability +(e.g., register width) differences. The default bit generator has been chosen +to perform well on 64-bit platforms. Performance on 32-bit operating systems +is very different. + +The values reported are normalized relative to the speed of MT19937 in +each table. A value of 100 indicates that the performance matches the MT19937. +Higher values indicate improved performance. These values cannot be compared +across tables. + +64-bit Linux +~~~~~~~~~~~~ + +===================== ========= ======= =========== ======== ======= +Distribution MT19937 PCG64 PCG64DXSM Philox SFC64 +===================== ========= ======= =========== ======== ======= +32-bit Unsigned Ints 100 168 166 100 182 +64-bit Unsigned Ints 100 176 193 116 224 +Uniforms 100 188 202 118 228 +Normals 100 128 132 115 167 +Exponentials 100 152 157 111 168 +Overall 100 161 168 112 192 +===================== ========= ======= =========== ======== ======= + + +64-bit Windows +~~~~~~~~~~~~~~ +The relative performance on 64-bit Linux and 64-bit Windows is broadly similar +with the notable exception of the Philox generator. + +===================== ========= ======= =========== ======== ======= +Distribution MT19937 PCG64 PCG64DXSM Philox SFC64 +===================== ========= ======= =========== ======== ======= +32-bit Unsigned Ints 100 155 131 29 150 +64-bit Unsigned Ints 100 157 143 25 154 +Uniforms 100 151 144 24 155 +Normals 100 129 128 37 150 +Exponentials 100 150 145 28 159 +**Overall** 100 148 138 28 154 +===================== ========= ======= =========== ======== ======= + + +32-bit Windows +~~~~~~~~~~~~~~ + +The performance of 64-bit generators on 32-bit Windows is much lower than on 64-bit +operating systems due to register width. MT19937, the generator that has been +in NumPy since 2005, operates on 32-bit integers. + +===================== ========= ======= =========== ======== ======= +Distribution MT19937 PCG64 PCG64DXSM Philox SFC64 +===================== ========= ======= =========== ======== ======= +32-bit Unsigned Ints 100 24 34 14 57 +64-bit Unsigned Ints 100 21 32 14 74 +Uniforms 100 21 34 16 73 +Normals 100 36 57 28 101 +Exponentials 100 28 44 20 88 +**Overall** 100 25 39 18 77 +===================== ========= ======= =========== ======== ======= + + +.. note:: + + Linux timings used Ubuntu 20.04 and GCC 9.3.0. Windows timings were made on + Windows 10 using Microsoft C/C++ Optimizing Compiler Version 19 (Visual + Studio 2019). All timings were produced on an AMD Ryzen 9 3900X processor. diff --git a/doc/source/reference/random/upgrading-pcg64.rst b/doc/source/reference/random/upgrading-pcg64.rst new file mode 100644 index 000000000000..9e540ace9331 --- /dev/null +++ b/doc/source/reference/random/upgrading-pcg64.rst @@ -0,0 +1,152 @@ +.. _upgrading-pcg64: + +.. currentmodule:: numpy.random + +Upgrading ``PCG64`` with ``PCG64DXSM`` +-------------------------------------- + +Uses of the `PCG64` `BitGenerator` in a massively-parallel context have been +shown to have statistical weaknesses that were not apparent at the first +release in numpy 1.17. Most users will never observe this weakness and are +safe to continue to use `PCG64`. We have introduced a new `PCG64DXSM` +`BitGenerator` that will eventually become the new default `BitGenerator` +implementation used by `default_rng` in future releases. `PCG64DXSM` solves +the statistical weakness while preserving the performance and the features of +`PCG64`. + +Does this affect me? +==================== + +If you + + 1. only use a single `Generator` instance, + 2. only use `RandomState` or the functions in `numpy.random`, + 3. only use the `PCG64.jumped` method to generate parallel streams, + 4. explicitly use a `BitGenerator` other than `PCG64`, + +then this weakness does not affect you at all. Carry on. + +If you use moderate numbers of parallel streams created with `default_rng` or +`SeedSequence.spawn`, in the 1000s, then the chance of observing this weakness +is negligibly small. You can continue to use `PCG64` comfortably. + +If you use very large numbers of parallel streams, in the millions, and draw +large amounts of numbers from each, then the chance of observing this weakness +can become non-negligible, if still small. An example of such a use case would +be a very large distributed reinforcement learning problem with millions of +long Monte Carlo playouts each generating billions of random number draws. Such +use cases should consider using `PCG64DXSM` explicitly or another +modern `BitGenerator` like `SFC64` or `Philox`, but it is unlikely that any +old results you may have calculated are invalid. In any case, the weakness is +a kind of `Birthday Paradox <https://en.wikipedia.org/wiki/Birthday_problem>`_ +collision. That is, a single pair of parallel streams out of the millions, +considered together, might fail a stringent set of statistical tests of +randomness. The remaining millions of streams would all be perfectly fine, and +the effect of the bad pair in the whole calculation is very likely to be +swamped by the remaining streams in most applications. + +.. _upgrading-pcg64-details: + +Technical Details +================= + +Like many PRNG algorithms, `PCG64` is constructed from a transition function, +which advances a 128-bit state, and an output function, that mixes the 128-bit +state into a 64-bit integer to be output. One of the guiding design principles +of the PCG family of PRNGs is to balance the computational cost (and +pseudorandomness strength) between the transition function and the output +function. The transition function is a 128-bit linear congruential generator +(LCG), which consists of multiplying the 128-bit state with a fixed +multiplication constant and then adding a user-chosen increment, in 128-bit +modular arithmetic. LCGs are well-analyzed PRNGs with known weaknesses, though +128-bit LCGs are large enough to pass stringent statistical tests on their own, +with only the trivial output function. The output function of `PCG64` is +intended to patch up some of those known weaknesses by doing "just enough" +scrambling of the bits to assist in the statistical properties without adding +too much computational cost. + +One of these known weaknesses is that advancing the state of the LCG by steps +numbering a power of two (``bg.advance(2**N)``) will leave the lower ``N`` bits +identical to the state that was just left. For a single stream drawn from +sequentially, this is of little consequence. The remaining :math:`128-N` bits provide +plenty of pseudorandomness that will be mixed in for any practical ``N`` that can +be observed in a single stream, which is why one does not need to worry about +this if you only use a single stream in your application. Similarly, the +`PCG64.jumped` method uses a carefully chosen number of steps to avoid creating +these collisions. However, once you start creating "randomly-initialized" +parallel streams, either using OS entropy by calling `default_rng` repeatedly +or using `SeedSequence.spawn`, then we need to consider how many lower bits +need to "collide" in order to create a bad pair of streams, and then evaluate +the probability of creating such a collision. +`Empirically <https://github.com/numpy/numpy/issues/16313>`_, it has been +determined that if one shares the lower 58 bits of state and shares an +increment, then the pair of streams, when interleaved, will fail +`PractRand <http://pracrand.sourceforge.net/>`_ in +a reasonable amount of time, after drawing a few gigabytes of data. Following +the standard Birthday Paradox calculations for a collision of 58 bits, we can +see that we can create :math:`2^{29}`, or about half a billion, streams which is when +the probability of such a collision becomes high. Half a billion streams is +quite high, and the amount of data each stream needs to draw before the +statistical correlations become apparent to even the strict ``PractRand`` tests +is in the gigabytes. But this is on the horizon for very large applications +like distributed reinforcement learning. There are reasons to expect that even +in these applications a collision probably will not have a practical effect in +the total result, since the statistical problem is constrained to just the +colliding pair. + +Now, let us consider the case when the increment is not constrained to be the +same. Our implementation of `PCG64` seeds both the state and the increment; +that is, two calls to `default_rng` (almost certainly) have different states +and increments. Upon our first release, we believed that having the seeded +increment would provide a certain amount of extra protection, that one would +have to be "close" in both the state space and increment space in order to +observe correlations (``PractRand`` failures) in a pair of streams. If that were +true, then the "bottleneck" for collisions would be the 128-bit entropy pool +size inside of `SeedSequence` (and 128-bit collisions are in the +"preposterously unlikely" category). Unfortunately, this is not true. + +One of the known properties of an LCG is that different increments create +*distinct* streams, but with a known relationship. Each LCG has an orbit that +traverses all :math:`2^{128}` different 128-bit states. Two LCGs with different +increments are related in that one can "rotate" the orbit of the first LCG +(advance it by a number of steps that we can compute from the two increments) +such that then both LCGs will always then have the same state, up to an +additive constant and maybe an inversion of the bits. If you then iterate both +streams in lockstep, then the states will *always* remain related by that same +additive constant (and the inversion, if present). Recall that `PCG64` is +constructed from both a transition function (the LCG) and an output function. +It was expected that the scrambling effect of the output function would have +been strong enough to make the distinct streams practically independent (i.e. +"passing the ``PractRand`` tests") unless the two increments were +pathologically related to each other (e.g. 1 and 3). The output function XSL-RR +of the then-standard PCG algorithm that we implemented in `PCG64` turns out to +be too weak to cover up for the 58-bit collision of the underlying LCG that we +described above. For any given pair of increments, the size of the "colliding" +space of states is the same, so for this weakness, the extra distinctness +provided by the increments does not translate into extra protection from +statistical correlations that ``PractRand`` can detect. + +Fortunately, strengthening the output function is able to correct this weakness +and *does* turn the extra distinctness provided by differing increments into +additional protection from these low-bit collisions. To the `PCG author's +credit <https://github.com/numpy/numpy/issues/13635#issuecomment-506088698>`_, +she had developed a stronger output function in response to related discussions +during the long birth of the new `BitGenerator` system. We NumPy developers +chose to be "conservative" and use the XSL-RR variant that had undergone +a longer period of testing at that time. The DXSM output function adopts +a "xorshift-multiply" construction used in strong integer hashes that has much +better avalanche properties than the XSL-RR output function. While there are +"pathological" pairs of increments that induce "bad" additive constants that +relate the two streams, the vast majority of pairs induce "good" additive +constants that make the merely-distinct streams of LCG states into +practically-independent output streams. Indeed, now the claim we once made +about `PCG64` is actually true of `PCG64DXSM`: collisions are possible, but +both streams have to simultaneously be both "close" in the 128 bit state space +*and* "close" in the 127-bit increment space, so that would be less likely than +the negligible chance of colliding in the 128-bit internal `SeedSequence` pool. +The DXSM output function is more computationally intensive than XSL-RR, but +some optimizations in the LCG more than make up for the performance hit on most +machines, so `PCG64DXSM` is a good, safe upgrade. There are, of course, an +infinite number of stronger output functions that one could consider, but most +will have a greater computational cost, and the DXSM output function has now +received many CPU cycles of testing via ``PractRand`` at this time. diff --git a/doc/source/reference/routines.array-creation.rst b/doc/source/reference/routines.array-creation.rst index e718f0052872..30780c286c41 100644 --- a/doc/source/reference/routines.array-creation.rst +++ b/doc/source/reference/routines.array-creation.rst @@ -7,8 +7,8 @@ Array creation routines .. currentmodule:: numpy -Ones and zeros --------------- +From shape or value +------------------- .. autosummary:: :toctree: generated/ diff --git a/doc/source/reference/routines.array-manipulation.rst b/doc/source/reference/routines.array-manipulation.rst index cc93d1029f52..1c96495d96f7 100644 --- a/doc/source/reference/routines.array-manipulation.rst +++ b/doc/source/reference/routines.array-manipulation.rst @@ -9,6 +9,7 @@ Basic operations :toctree: generated/ copyto + shape Changing array shape ==================== @@ -68,11 +69,12 @@ Joining arrays concatenate stack - column_stack - dstack - hstack - vstack block + vstack + hstack + dstack + column_stack + row_stack Splitting arrays ================ diff --git a/doc/source/reference/routines.char.rst b/doc/source/reference/routines.char.rst index 7413e361507d..90df14125b45 100644 --- a/doc/source/reference/routines.char.rst +++ b/doc/source/reference/routines.char.rst @@ -1,11 +1,13 @@ String operations ***************** -.. currentmodule:: numpy.core.defchararray +.. currentmodule:: numpy.char -This module provides a set of vectorized string operations for arrays -of type `numpy.string_` or `numpy.unicode_`. All of them are based on -the string methods in the Python standard library. +.. module:: numpy.char + +The `numpy.char` module provides a set of vectorized string +operations for arrays of type `numpy.str_` or `numpy.bytes_`. +All of them are based on the string methods in the Python standard library. String operations ----------------- @@ -20,6 +22,7 @@ String operations center decode encode + expandtabs join ljust lower @@ -55,6 +58,7 @@ comparison. less_equal greater less + compare_chararrays String information ------------------ @@ -63,9 +67,11 @@ String information :toctree: generated/ count + endswith find index isalpha + isalnum isdecimal isdigit islower @@ -76,6 +82,7 @@ String information rfind rindex startswith + str_len Convenience class ----------------- @@ -83,4 +90,6 @@ Convenience class .. autosummary:: :toctree: generated/ + array + asarray chararray diff --git a/doc/source/reference/routines.ctypeslib.rst b/doc/source/reference/routines.ctypeslib.rst index b04713b61b7d..c6127ca6428f 100644 --- a/doc/source/reference/routines.ctypeslib.rst +++ b/doc/source/reference/routines.ctypeslib.rst @@ -1,3 +1,5 @@ +.. module:: numpy.ctypeslib + *********************************************************** C-Types Foreign Function Interface (:mod:`numpy.ctypeslib`) *********************************************************** @@ -6,6 +8,13 @@ C-Types Foreign Function Interface (:mod:`numpy.ctypeslib`) .. autofunction:: as_array .. autofunction:: as_ctypes -.. autofunction:: ctypes_load_library +.. autofunction:: as_ctypes_type .. autofunction:: load_library .. autofunction:: ndpointer + +.. class:: c_intp + + A `ctypes` signed integer type of the same size as `numpy.intp`. + + Depending on the platform, it can be an alias for either `~ctypes.c_int`, + `~ctypes.c_long` or `~ctypes.c_longlong`. diff --git a/doc/source/reference/routines.dtype.rst b/doc/source/reference/routines.dtype.rst index ec8d2981d6c8..e9189ca07737 100644 --- a/doc/source/reference/routines.dtype.rst +++ b/doc/source/reference/routines.dtype.rst @@ -17,11 +17,9 @@ Data type routines Creating data types ------------------- - .. autosummary:: :toctree: generated/ - dtype format_parser @@ -53,3 +51,4 @@ Miscellaneous typename sctype2char mintypecode + maximum_sctype diff --git a/doc/source/reference/routines.dual.rst b/doc/source/reference/routines.dual.rst index 4ed7098d6c3a..01814e9a71cb 100644 --- a/doc/source/reference/routines.dual.rst +++ b/doc/source/reference/routines.dual.rst @@ -1,4 +1,4 @@ -Optionally Scipy-accelerated routines (:mod:`numpy.dual`) +Optionally SciPy-accelerated routines (:mod:`numpy.dual`) ********************************************************* .. automodule:: numpy.dual diff --git a/doc/source/reference/routines.financial.rst b/doc/source/reference/routines.financial.rst deleted file mode 100644 index 5f426d7abf79..000000000000 --- a/doc/source/reference/routines.financial.rst +++ /dev/null @@ -1,21 +0,0 @@ -Financial functions -******************* - -.. currentmodule:: numpy - -Simple financial functions --------------------------- - -.. autosummary:: - :toctree: generated/ - - fv - pv - npv - pmt - ppmt - ipmt - irr - mirr - nper - rate diff --git a/doc/source/reference/routines.indexing.rst b/doc/source/reference/routines.indexing.rst deleted file mode 100644 index aeec1a1bba55..000000000000 --- a/doc/source/reference/routines.indexing.rst +++ /dev/null @@ -1,68 +0,0 @@ -.. _routines.indexing: - -Indexing routines -================= - -.. seealso:: :ref:`Indexing <arrays.indexing>` - -.. currentmodule:: numpy - -Generating index arrays ------------------------ -.. autosummary:: - :toctree: generated/ - - c_ - r_ - s_ - nonzero - where - indices - ix_ - ogrid - ravel_multi_index - unravel_index - diag_indices - diag_indices_from - mask_indices - tril_indices - tril_indices_from - triu_indices - triu_indices_from - -Indexing-like operations ------------------------- -.. autosummary:: - :toctree: generated/ - - take - take_along_axis - choose - compress - diag - diagonal - select - lib.stride_tricks.as_strided - -Inserting data into arrays --------------------------- -.. autosummary:: - :toctree: generated/ - - place - put - put_along_axis - putmask - fill_diagonal - -Iterating over arrays ---------------------- -.. autosummary:: - :toctree: generated/ - - nditer - ndenumerate - ndindex - nested_iters - flatiter - lib.Arrayterator diff --git a/doc/source/reference/routines.io.rst b/doc/source/reference/routines.io.rst index 55489951fe8d..3052ee1fbee8 100644 --- a/doc/source/reference/routines.io.rst +++ b/doc/source/reference/routines.io.rst @@ -1,3 +1,5 @@ +.. _routines.io: + Input and output **************** @@ -54,6 +56,7 @@ Memory mapping files :toctree: generated/ memmap + lib.format.open_memmap Text formatting options ----------------------- @@ -63,6 +66,7 @@ Text formatting options set_printoptions get_printoptions set_string_function + printoptions Base-n representations ---------------------- @@ -82,7 +86,6 @@ Data sources Binary Format Description ------------------------- .. autosummary:: - :template: autosummary/minimal_module.rst :toctree: generated/ - lib.format + lib.format diff --git a/doc/source/reference/routines.linalg.rst b/doc/source/reference/routines.linalg.rst index 0520df413566..76b7ab82c940 100644 --- a/doc/source/reference/routines.linalg.rst +++ b/doc/source/reference/routines.linalg.rst @@ -1,12 +1,55 @@ .. _routines.linalg: +.. module:: numpy.linalg + Linear algebra (:mod:`numpy.linalg`) ************************************ +The NumPy linear algebra functions rely on BLAS and LAPACK to provide efficient +low level implementations of standard linear algebra algorithms. Those +libraries may be provided by NumPy itself using C versions of a subset of their +reference implementations but, when possible, highly optimized libraries that +take advantage of specialized processor functionality are preferred. Examples +of such libraries are OpenBLAS_, MKL (TM), and ATLAS. Because those libraries +are multithreaded and processor dependent, environmental variables and external +packages such as threadpoolctl_ may be needed to control the number of threads +or specify the processor architecture. + +.. _OpenBLAS: https://www.openblas.net/ +.. _threadpoolctl: https://github.com/joblib/threadpoolctl + +The SciPy library also contains a `~scipy.linalg` submodule, and there is +overlap in the functionality provided by the SciPy and NumPy submodules. SciPy +contains functions not found in `numpy.linalg`, such as functions related to +LU decomposition and the Schur decomposition, multiple ways of calculating the +pseudoinverse, and matrix transcendentals such as the matrix logarithm. Some +functions that exist in both have augmented functionality in `scipy.linalg`. +For example, `scipy.linalg.eig` can take a second matrix argument for solving +generalized eigenvalue problems. Some functions in NumPy, however, have more +flexible broadcasting options. For example, `numpy.linalg.solve` can handle +"stacked" arrays, while `scipy.linalg.solve` accepts only a single square +array as its first argument. + +.. note:: + + The term *matrix* as it is used on this page indicates a 2d `numpy.array` + object, and *not* a `numpy.matrix` object. The latter is no longer + recommended, even for linear algebra. See + :ref:`the matrix object documentation<matrix-objects>` for + more information. + +The ``@`` operator +------------------ + +Introduced in NumPy 1.10.0, the ``@`` operator is preferable to +other methods when computing the matrix product between 2d arrays. The +:func:`numpy.matmul` function implements the ``@`` operator. + .. currentmodule:: numpy Matrix and vector products -------------------------- + .. autosummary:: :toctree: generated/ diff --git a/doc/source/reference/routines.ma.rst b/doc/source/reference/routines.ma.rst index 2408899b35fa..5404c43d8fee 100644 --- a/doc/source/reference/routines.ma.rst +++ b/doc/source/reference/routines.ma.rst @@ -44,7 +44,9 @@ Ones and zeros ma.masked_all ma.masked_all_like ma.ones + ma.ones_like ma.zeros + ma.zeros_like _____ @@ -67,10 +69,10 @@ Inspecting the array ma.size ma.is_masked ma.is_mask + ma.isMaskedArray + ma.isMA + ma.isarray - ma.MaskedArray.data - ma.MaskedArray.mask - ma.MaskedArray.recordmask ma.MaskedArray.all ma.MaskedArray.any @@ -80,6 +82,12 @@ Inspecting the array ma.size +.. autosummary:: + + ma.MaskedArray.data + ma.MaskedArray.mask + ma.MaskedArray.recordmask + _____ Manipulating a MaskedArray @@ -126,6 +134,7 @@ Changing the number of dimensions ma.MaskedArray.squeeze + ma.stack ma.column_stack ma.concatenate ma.dstack @@ -141,12 +150,13 @@ Joining arrays .. autosummary:: :toctree: generated/ - ma.column_stack ma.concatenate - ma.append - ma.dstack - ma.hstack + ma.stack ma.vstack + ma.hstack + ma.dstack + ma.column_stack + ma.append _____ @@ -259,17 +269,6 @@ Conversion operations ma.MaskedArray.tobytes -Pickling and unpickling -~~~~~~~~~~~~~~~~~~~~~~~ -.. autosummary:: - :toctree: generated/ - - ma.dump - ma.dumps - ma.load - ma.loads - - Filling a masked array ~~~~~~~~~~~~~~~~~~~~~~ .. autosummary:: @@ -278,21 +277,23 @@ Filling a masked array ma.common_fill_value ma.default_fill_value ma.maximum_fill_value - ma.maximum_fill_value + ma.minimum_fill_value ma.set_fill_value ma.MaskedArray.get_fill_value ma.MaskedArray.set_fill_value - ma.MaskedArray.fill_value +.. autosummary:: + + ma.MaskedArray.fill_value _____ -Masked arrays arithmetics -========================= +Masked arrays arithmetic +======================== -Arithmetics -~~~~~~~~~~~ +Arithmetic +~~~~~~~~~~ .. autosummary:: :toctree: generated/ @@ -332,6 +333,7 @@ Minimum/maximum ma.max ma.min ma.ptp + ma.diff ma.MaskedArray.argmax ma.MaskedArray.argmin @@ -400,6 +402,7 @@ Miscellanea ma.allequal ma.allclose ma.apply_along_axis + ma.apply_over_axes ma.arange ma.choose ma.ediff1d diff --git a/doc/source/reference/routines.math.rst b/doc/source/reference/routines.math.rst index 821363987acc..2a09b8d204a1 100644 --- a/doc/source/reference/routines.math.rst +++ b/doc/source/reference/routines.math.rst @@ -141,7 +141,23 @@ Handling complex numbers real imag conj + conjugate +Extrema Finding +--------------- +.. autosummary:: + :toctree: generated/ + + maximum + fmax + amax + nanmax + + minimum + fmin + amin + nanmin + Miscellaneous ------------- @@ -159,11 +175,7 @@ Miscellaneous fabs sign heaviside - maximum - minimum - fmax - fmin - + nan_to_num real_if_close diff --git a/doc/source/reference/routines.matlib.rst b/doc/source/reference/routines.matlib.rst index a35eaec785ca..c7f675425a0f 100644 --- a/doc/source/reference/routines.matlib.rst +++ b/doc/source/reference/routines.matlib.rst @@ -1,3 +1,5 @@ +.. module:: numpy.matlib + Matrix library (:mod:`numpy.matlib`) ************************************ diff --git a/doc/source/reference/routines.numarray.rst b/doc/source/reference/routines.numarray.rst deleted file mode 100644 index 9e84f49b98bd..000000000000 --- a/doc/source/reference/routines.numarray.rst +++ /dev/null @@ -1,5 +0,0 @@ -********************** -Numarray compatibility -********************** - -The numarray module was removed in NumPy 1.9.0. diff --git a/doc/source/reference/routines.oldnumeric.rst b/doc/source/reference/routines.oldnumeric.rst deleted file mode 100644 index 2120fc69e1d7..000000000000 --- a/doc/source/reference/routines.oldnumeric.rst +++ /dev/null @@ -1,7 +0,0 @@ -************************* -Old Numeric compatibility -************************* - -.. currentmodule:: numpy - -The oldnumeric module was removed in NumPy 1.9.0. diff --git a/doc/source/reference/routines.other.rst b/doc/source/reference/routines.other.rst index 45b9ac3d94da..339857409157 100644 --- a/doc/source/reference/routines.other.rst +++ b/doc/source/reference/routines.other.rst @@ -5,14 +5,6 @@ Miscellaneous routines .. currentmodule:: numpy -Buffer objects --------------- -.. autosummary:: - :toctree: generated/ - - getbuffer - newbuffer - Performance tuning ------------------ .. autosummary:: @@ -29,6 +21,7 @@ Memory ranges shares_memory may_share_memory + byte_bounds Array mixins ------------ @@ -43,3 +36,30 @@ NumPy version comparison :toctree: generated/ lib.NumpyVersion + +Utility +------- + +.. autosummary:: + :toctree: generated/ + + get_include + show_config + deprecate + deprecate_with_doc + broadcast_shapes + +Matlab-like Functions +--------------------- +.. autosummary:: + :toctree: generated/ + + who + disp + +Exceptions +---------- +.. autosummary:: + :toctree: generated/ + + AxisError diff --git a/doc/source/reference/routines.polynomials.chebyshev.rst b/doc/source/reference/routines.polynomials.chebyshev.rst index 60c816f03d1d..087b7beb9f06 100644 --- a/doc/source/reference/routines.polynomials.chebyshev.rst +++ b/doc/source/reference/routines.polynomials.chebyshev.rst @@ -1,92 +1,6 @@ -Chebyshev Module (:mod:`numpy.polynomial.chebyshev`) -==================================================== - .. versionadded:: 1.4.0 -.. currentmodule:: numpy.polynomial.chebyshev - -This module provides a number of objects (mostly functions) useful for -dealing with Chebyshev series, including a `Chebyshev` class that -encapsulates the usual arithmetic operations. (General information -on how this module represents and works with such polynomials is in the -docstring for its "parent" sub-package, `numpy.polynomial`). - -Chebyshev Class ---------------- - -.. autosummary:: - :toctree: generated/ - - Chebyshev - -Basics ------- - -.. autosummary:: - :toctree: generated/ - - chebval - chebval2d - chebval3d - chebgrid2d - chebgrid3d - chebroots - chebfromroots - -Fitting -------- - -.. autosummary:: - :toctree: generated/ - - chebfit - chebvander - chebvander2d - chebvander3d - -Calculus --------- - -.. autosummary:: - :toctree: generated/ - - chebder - chebint - -Algebra -------- - -.. autosummary:: - :toctree: generated/ - - chebadd - chebsub - chebmul - chebmulx - chebdiv - chebpow - -Quadrature ----------- - -.. autosummary:: - :toctree: generated/ - - chebgauss - chebweight - -Miscellaneous -------------- - -.. autosummary:: - :toctree: generated/ - - chebcompanion - chebdomain - chebzero - chebone - chebx - chebtrim - chebline - cheb2poly - poly2cheb +.. automodule:: numpy.polynomial.chebyshev + :no-members: + :no-inherited-members: + :no-special-members: diff --git a/doc/source/reference/routines.polynomials.classes.rst b/doc/source/reference/routines.polynomials.classes.rst index f44ddd46c126..5f575bed13d4 100644 --- a/doc/source/reference/routines.polynomials.classes.rst +++ b/doc/source/reference/routines.polynomials.classes.rst @@ -35,11 +35,11 @@ degree :math:`n`, but could just as easily be the basis functions of any of the other classes. The convention for all the classes is that the coefficient :math:`c[i]` goes with the basis function of degree i. -All of the classes have the same methods, and especially they implement the -Python numeric operators +, -, \*, //, %, divmod, \*\*, ==, -and !=. The last two can be a bit problematic due to floating point -roundoff errors. We now give a quick demonstration of the various -operations using NumPy version 1.7.0. +All of the classes are immutable and have the same methods, and +especially they implement the Python numeric operators +, -, \*, //, %, +divmod, \*\*, ==, and !=. The last two can be a bit problematic due to +floating point roundoff errors. We now give a quick demonstration of the +various operations using NumPy version 1.7.0. Basics ------ @@ -52,7 +52,7 @@ the conventional Polynomial class because of its familiarity:: >>> from numpy.polynomial import Polynomial as P >>> p = P([1,2,3]) >>> p - Polynomial([ 1., 2., 3.], domain=[-1, 1], window=[-1, 1]) + Polynomial([1., 2., 3.], domain=[-1, 1], window=[-1, 1]) Note that there are three parts to the long version of the printout. The first is the coefficients, the second is the domain, and the third is the @@ -65,11 +65,26 @@ window:: >>> p.window array([-1., 1.]) -Printing a polynomial yields a shorter form without the domain -and window:: +Printing a polynomial yields the polynomial expression in a more familiar +format:: - >>> print p - poly([ 1. 2. 3.]) + >>> print(p) + 1.0 + 2.0·x¹ + 3.0·x² + +Note that the string representation of polynomials uses Unicode characters +by default (except on Windows) to express powers and subscripts. An ASCII-based +representation is also available (default on Windows). The polynomial string +format can be toggled at the package-level with the +`~numpy.polynomial.set_default_printstyle` function:: + + >>> numpy.polynomial.set_default_printstyle('ascii') + >>> print(p) + 1.0 + 2.0 x**1 + 3.0 x**2 + +or controlled for individual polynomial instances with string formatting:: + + >>> print(f"{p:unicode}") + 1.0 + 2.0·x¹ + 3.0·x² We will deal with the domain and window when we get to fitting, for the moment we ignore them and run through the basic algebraic and arithmetic operations. @@ -77,19 +92,19 @@ we ignore them and run through the basic algebraic and arithmetic operations. Addition and Subtraction:: >>> p + p - Polynomial([ 2., 4., 6.], domain=[-1, 1], window=[-1, 1]) + Polynomial([2., 4., 6.], domain=[-1., 1.], window=[-1., 1.]) >>> p - p - Polynomial([ 0.], domain=[-1, 1], window=[-1, 1]) + Polynomial([0.], domain=[-1., 1.], window=[-1., 1.]) Multiplication:: >>> p * p - Polynomial([ 1., 4., 10., 12., 9.], domain=[-1, 1], window=[-1, 1]) + Polynomial([ 1., 4., 10., 12., 9.], domain=[-1., 1.], window=[-1., 1.]) Powers:: >>> p**2 - Polynomial([ 1., 4., 10., 12., 9.], domain=[-1, 1], window=[-1, 1]) + Polynomial([ 1., 4., 10., 12., 9.], domain=[-1., 1.], window=[-1., 1.]) Division: @@ -100,20 +115,20 @@ versions the '/' will only work for division by scalars. At some point it will be deprecated:: >>> p // P([-1, 1]) - Polynomial([ 5., 3.], domain=[-1, 1], window=[-1, 1]) + Polynomial([5., 3.], domain=[-1., 1.], window=[-1., 1.]) Remainder:: >>> p % P([-1, 1]) - Polynomial([ 6.], domain=[-1, 1], window=[-1, 1]) + Polynomial([6.], domain=[-1., 1.], window=[-1., 1.]) Divmod:: >>> quo, rem = divmod(p, P([-1, 1])) >>> quo - Polynomial([ 5., 3.], domain=[-1, 1], window=[-1, 1]) + Polynomial([5., 3.], domain=[-1., 1.], window=[-1., 1.]) >>> rem - Polynomial([ 6.], domain=[-1, 1], window=[-1, 1]) + Polynomial([6.], domain=[-1., 1.], window=[-1., 1.]) Evaluation:: @@ -134,7 +149,7 @@ the polynomials are regarded as functions this is composition of functions:: >>> p(p) - Polynomial([ 6., 16., 36., 36., 27.], domain=[-1, 1], window=[-1, 1]) + Polynomial([ 6., 16., 36., 36., 27.], domain=[-1., 1.], window=[-1., 1.]) Roots:: @@ -148,11 +163,11 @@ tuples, lists, arrays, and scalars are automatically cast in the arithmetic operations:: >>> p + [1, 2, 3] - Polynomial([ 2., 4., 6.], domain=[-1, 1], window=[-1, 1]) + Polynomial([2., 4., 6.], domain=[-1., 1.], window=[-1., 1.]) >>> [1, 2, 3] * p - Polynomial([ 1., 4., 10., 12., 9.], domain=[-1, 1], window=[-1, 1]) + Polynomial([ 1., 4., 10., 12., 9.], domain=[-1., 1.], window=[-1., 1.]) >>> p / 2 - Polynomial([ 0.5, 1. , 1.5], domain=[-1, 1], window=[-1, 1]) + Polynomial([0.5, 1. , 1.5], domain=[-1., 1.], window=[-1., 1.]) Polynomials that differ in domain, window, or class can't be mixed in arithmetic:: @@ -180,13 +195,18 @@ conversion of Polynomial classes among themselves is done for type, domain, and window casting:: >>> p(T([0, 1])) - Chebyshev([ 2.5, 2. , 1.5], domain=[-1, 1], window=[-1, 1]) + Chebyshev([2.5, 2. , 1.5], domain=[-1., 1.], window=[-1., 1.]) Which gives the polynomial `p` in Chebyshev form. This works because :math:`T_1(x) = x` and substituting :math:`x` for :math:`x` doesn't change the original polynomial. However, all the multiplications and divisions will be done using Chebyshev series, hence the type of the result. +It is intended that all polynomial instances are immutable, therefore +augmented operations (``+=``, ``-=``, etc.) and any other functionality that +would violate the immutablity of a polynomial instance are intentionally +unimplemented. + Calculus -------- @@ -195,18 +215,18 @@ Polynomial instances can be integrated and differentiated.:: >>> from numpy.polynomial import Polynomial as P >>> p = P([2, 6]) >>> p.integ() - Polynomial([ 0., 2., 3.], domain=[-1, 1], window=[-1, 1]) + Polynomial([0., 2., 3.], domain=[-1., 1.], window=[-1., 1.]) >>> p.integ(2) - Polynomial([ 0., 0., 1., 1.], domain=[-1, 1], window=[-1, 1]) + Polynomial([0., 0., 1., 1.], domain=[-1., 1.], window=[-1., 1.]) The first example integrates `p` once, the second example integrates it twice. By default, the lower bound of the integration and the integration constant are 0, but both can be specified.:: >>> p.integ(lbnd=-1) - Polynomial([-1., 2., 3.], domain=[-1, 1], window=[-1, 1]) + Polynomial([-1., 2., 3.], domain=[-1., 1.], window=[-1., 1.]) >>> p.integ(lbnd=-1, k=1) - Polynomial([ 0., 2., 3.], domain=[-1, 1], window=[-1, 1]) + Polynomial([0., 2., 3.], domain=[-1., 1.], window=[-1., 1.]) In the first case the lower bound of the integration is set to -1 and the integration constant is 0. In the second the constant of integration is set @@ -215,9 +235,9 @@ number of times the polynomial is differentiated:: >>> p = P([1, 2, 3]) >>> p.deriv(1) - Polynomial([ 2., 6.], domain=[-1, 1], window=[-1, 1]) + Polynomial([2., 6.], domain=[-1., 1.], window=[-1., 1.]) >>> p.deriv(2) - Polynomial([ 6.], domain=[-1, 1], window=[-1, 1]) + Polynomial([6.], domain=[-1., 1.], window=[-1., 1.]) Other Polynomial Constructors @@ -233,25 +253,25 @@ are demonstrated below:: >>> from numpy.polynomial import Chebyshev as T >>> p = P.fromroots([1, 2, 3]) >>> p - Polynomial([ -6., 11., -6., 1.], domain=[-1, 1], window=[-1, 1]) + Polynomial([-6., 11., -6., 1.], domain=[-1., 1.], window=[-1., 1.]) >>> p.convert(kind=T) - Chebyshev([ -9. , 11.75, -3. , 0.25], domain=[-1, 1], window=[-1, 1]) + Chebyshev([-9. , 11.75, -3. , 0.25], domain=[-1., 1.], window=[-1., 1.]) The convert method can also convert domain and window:: >>> p.convert(kind=T, domain=[0, 1]) - Chebyshev([-2.4375 , 2.96875, -0.5625 , 0.03125], [ 0., 1.], [-1., 1.]) + Chebyshev([-2.4375 , 2.96875, -0.5625 , 0.03125], domain=[0., 1.], window=[-1., 1.]) >>> p.convert(kind=P, domain=[0, 1]) - Polynomial([-1.875, 2.875, -1.125, 0.125], [ 0., 1.], [-1., 1.]) + Polynomial([-1.875, 2.875, -1.125, 0.125], domain=[0., 1.], window=[-1., 1.]) In numpy versions >= 1.7.0 the `basis` and `cast` class methods are also available. The cast method works like the convert method while the basis method returns the basis polynomial of given degree:: >>> P.basis(3) - Polynomial([ 0., 0., 0., 1.], domain=[-1, 1], window=[-1, 1]) + Polynomial([0., 0., 0., 1.], domain=[-1., 1.], window=[-1., 1.]) >>> T.cast(p) - Chebyshev([ -9. , 11.75, -3. , 0.25], domain=[-1, 1], window=[-1, 1]) + Chebyshev([-9. , 11.75, -3. , 0.25], domain=[-1., 1.], window=[-1., 1.]) Conversions between types can be useful, but it is *not* recommended for routine use. The loss of numerical precision in passing from a @@ -270,7 +290,8 @@ polynomials up to degree 5 are plotted below. >>> import matplotlib.pyplot as plt >>> from numpy.polynomial import Chebyshev as T >>> x = np.linspace(-1, 1, 100) - >>> for i in range(6): ax = plt.plot(x, T.basis(i)(x), lw=2, label="$T_%d$"%i) + >>> for i in range(6): + ... ax = plt.plot(x, T.basis(i)(x), lw=2, label=f"$T_{i}$") ... >>> plt.legend(loc="upper left") <matplotlib.legend.Legend object at 0x3b3ee10> @@ -284,7 +305,8 @@ The same plots over the range -2 <= `x` <= 2 look very different: >>> import matplotlib.pyplot as plt >>> from numpy.polynomial import Chebyshev as T >>> x = np.linspace(-2, 2, 100) - >>> for i in range(6): ax = plt.plot(x, T.basis(i)(x), lw=2, label="$T_%d$"%i) + >>> for i in range(6): + ... ax = plt.plot(x, T.basis(i)(x), lw=2, label=f"$T_{i}$") ... >>> plt.legend(loc="lower right") <matplotlib.legend.Legend object at 0x3b3ee10> diff --git a/doc/source/reference/routines.polynomials.hermite.rst b/doc/source/reference/routines.polynomials.hermite.rst index 8ee72e97c3f6..c881d9aaf1ea 100644 --- a/doc/source/reference/routines.polynomials.hermite.rst +++ b/doc/source/reference/routines.polynomials.hermite.rst @@ -1,92 +1,6 @@ -Hermite Module, "Physicists'" (:mod:`numpy.polynomial.hermite`) -=============================================================== - .. versionadded:: 1.6.0 -.. currentmodule:: numpy.polynomial.hermite - -This module provides a number of objects (mostly functions) useful for -dealing with Hermite series, including a `Hermite` class that -encapsulates the usual arithmetic operations. (General information -on how this module represents and works with such polynomials is in the -docstring for its "parent" sub-package, `numpy.polynomial`). - -Hermite Class -------------- - -.. autosummary:: - :toctree: generated/ - - Hermite - -Basics ------- - -.. autosummary:: - :toctree: generated/ - - hermval - hermval2d - hermval3d - hermgrid2d - hermgrid3d - hermroots - hermfromroots - -Fitting -------- - -.. autosummary:: - :toctree: generated/ - - hermfit - hermvander - hermvander2d - hermvander3d - -Calculus --------- - -.. autosummary:: - :toctree: generated/ - - hermder - hermint - -Algebra -------- - -.. autosummary:: - :toctree: generated/ - - hermadd - hermsub - hermmul - hermmulx - hermdiv - hermpow - -Quadrature ----------- - -.. autosummary:: - :toctree: generated/ - - hermgauss - hermweight - -Miscellaneous -------------- - -.. autosummary:: - :toctree: generated/ - - hermcompanion - hermdomain - hermzero - hermone - hermx - hermtrim - hermline - herm2poly - poly2herm +.. automodule:: numpy.polynomial.hermite + :no-members: + :no-inherited-members: + :no-special-members: diff --git a/doc/source/reference/routines.polynomials.hermite_e.rst b/doc/source/reference/routines.polynomials.hermite_e.rst index 33a15bb44451..bfcb900c8782 100644 --- a/doc/source/reference/routines.polynomials.hermite_e.rst +++ b/doc/source/reference/routines.polynomials.hermite_e.rst @@ -1,92 +1,6 @@ -HermiteE Module, "Probabilists'" (:mod:`numpy.polynomial.hermite_e`) -==================================================================== - .. versionadded:: 1.6.0 -.. currentmodule:: numpy.polynomial.hermite_e - -This module provides a number of objects (mostly functions) useful for -dealing with HermiteE series, including a `HermiteE` class that -encapsulates the usual arithmetic operations. (General information -on how this module represents and works with such polynomials is in the -docstring for its "parent" sub-package, `numpy.polynomial`). - -HermiteE Class --------------- - -.. autosummary:: - :toctree: generated/ - - HermiteE - -Basics ------- - -.. autosummary:: - :toctree: generated/ - - hermeval - hermeval2d - hermeval3d - hermegrid2d - hermegrid3d - hermeroots - hermefromroots - -Fitting -------- - -.. autosummary:: - :toctree: generated/ - - hermefit - hermevander - hermevander2d - hermevander3d - -Calculus --------- - -.. autosummary:: - :toctree: generated/ - - hermeder - hermeint - -Algebra -------- - -.. autosummary:: - :toctree: generated/ - - hermeadd - hermesub - hermemul - hermemulx - hermediv - hermepow - -Quadrature ----------- - -.. autosummary:: - :toctree: generated/ - - hermegauss - hermeweight - -Miscellaneous -------------- - -.. autosummary:: - :toctree: generated/ - - hermecompanion - hermedomain - hermezero - hermeone - hermex - hermetrim - hermeline - herme2poly - poly2herme +.. automodule:: numpy.polynomial.hermite_e + :no-members: + :no-inherited-members: + :no-special-members: diff --git a/doc/source/reference/routines.polynomials.laguerre.rst b/doc/source/reference/routines.polynomials.laguerre.rst index 45e288cb9c93..68c44630077c 100644 --- a/doc/source/reference/routines.polynomials.laguerre.rst +++ b/doc/source/reference/routines.polynomials.laguerre.rst @@ -1,92 +1,6 @@ -Laguerre Module (:mod:`numpy.polynomial.laguerre`) -================================================== - .. versionadded:: 1.6.0 -.. currentmodule:: numpy.polynomial.laguerre - -This module provides a number of objects (mostly functions) useful for -dealing with Laguerre series, including a `Laguerre` class that -encapsulates the usual arithmetic operations. (General information -on how this module represents and works with such polynomials is in the -docstring for its "parent" sub-package, `numpy.polynomial`). - -Laguerre Class --------------- - -.. autosummary:: - :toctree: generated/ - - Laguerre - -Basics ------- - -.. autosummary:: - :toctree: generated/ - - lagval - lagval2d - lagval3d - laggrid2d - laggrid3d - lagroots - lagfromroots - -Fitting -------- - -.. autosummary:: - :toctree: generated/ - - lagfit - lagvander - lagvander2d - lagvander3d - -Calculus --------- - -.. autosummary:: - :toctree: generated/ - - lagder - lagint - -Algebra -------- - -.. autosummary:: - :toctree: generated/ - - lagadd - lagsub - lagmul - lagmulx - lagdiv - lagpow - -Quadrature ----------- - -.. autosummary:: - :toctree: generated/ - - laggauss - lagweight - -Miscellaneous -------------- - -.. autosummary:: - :toctree: generated/ - - lagcompanion - lagdomain - lagzero - lagone - lagx - lagtrim - lagline - lag2poly - poly2lag +.. automodule:: numpy.polynomial.laguerre + :no-members: + :no-inherited-members: + :no-special-members: diff --git a/doc/source/reference/routines.polynomials.legendre.rst b/doc/source/reference/routines.polynomials.legendre.rst index fe6edc216f84..e10065b4d5fe 100644 --- a/doc/source/reference/routines.polynomials.legendre.rst +++ b/doc/source/reference/routines.polynomials.legendre.rst @@ -1,92 +1,6 @@ -Legendre Module (:mod:`numpy.polynomial.legendre`) -================================================== - .. versionadded:: 1.6.0 -.. currentmodule:: numpy.polynomial.legendre - -This module provides a number of objects (mostly functions) useful for -dealing with Legendre series, including a `Legendre` class that -encapsulates the usual arithmetic operations. (General information -on how this module represents and works with such polynomials is in the -docstring for its "parent" sub-package, `numpy.polynomial`). - -Legendre Class --------------- - -.. autosummary:: - :toctree: generated/ - - Legendre - -Basics ------- - -.. autosummary:: - :toctree: generated/ - - legval - legval2d - legval3d - leggrid2d - leggrid3d - legroots - legfromroots - -Fitting -------- - -.. autosummary:: - :toctree: generated/ - - legfit - legvander - legvander2d - legvander3d - -Calculus --------- - -.. autosummary:: - :toctree: generated/ - - legder - legint - -Algebra -------- - -.. autosummary:: - :toctree: generated/ - - legadd - legsub - legmul - legmulx - legdiv - legpow - -Quadrature ----------- - -.. autosummary:: - :toctree: generated/ - - leggauss - legweight - -Miscellaneous -------------- - -.. autosummary:: - :toctree: generated/ - - legcompanion - legdomain - legzero - legone - legx - legtrim - legline - leg2poly - poly2leg +.. automodule:: numpy.polynomial.legendre + :no-members: + :no-inherited-members: + :no-special-members: diff --git a/doc/source/reference/routines.polynomials.package.rst b/doc/source/reference/routines.polynomials.package.rst index 61cb57fbb8ef..1bc528c59495 100644 --- a/doc/source/reference/routines.polynomials.package.rst +++ b/doc/source/reference/routines.polynomials.package.rst @@ -1,18 +1,14 @@ -Polynomial Package -================== +:orphan: -.. versionadded:: 1.4.0 +.. automodule:: numpy.polynomial + :no-members: + :no-inherited-members: + :no-special-members: -.. currentmodule:: numpy.polynomial +Configuration +------------- -.. toctree:: - :maxdepth: 2 +.. autosummary:: + :toctree: generated/ - routines.polynomials.classes - routines.polynomials.polynomial - routines.polynomials.chebyshev - routines.polynomials.legendre - routines.polynomials.laguerre - routines.polynomials.hermite - routines.polynomials.hermite_e - routines.polynomials.polyutils + numpy.polynomial.set_default_printstyle diff --git a/doc/source/reference/routines.polynomials.polynomial.rst b/doc/source/reference/routines.polynomials.polynomial.rst index 8194ca867fc1..71000a60db2c 100644 --- a/doc/source/reference/routines.polynomials.polynomial.rst +++ b/doc/source/reference/routines.polynomials.polynomial.rst @@ -1,82 +1,6 @@ -Polynomial Module (:mod:`numpy.polynomial.polynomial`) -====================================================== - .. versionadded:: 1.4.0 -.. currentmodule:: numpy.polynomial.polynomial - -This module provides a number of objects (mostly functions) useful for -dealing with Polynomial series, including a `Polynomial` class that -encapsulates the usual arithmetic operations. (General information -on how this module represents and works with such polynomials is in the -docstring for its "parent" sub-package, `numpy.polynomial`). - -Polynomial Class ----------------- - -.. autosummary:: - :toctree: generated/ - - Polynomial - -Basics ------- - -.. autosummary:: - :toctree: generated/ - - polyval - polyval2d - polyval3d - polygrid2d - polygrid3d - polyroots - polyfromroots - polyvalfromroots - -Fitting -------- - -.. autosummary:: - :toctree: generated/ - - polyfit - polyvander - polyvander2d - polyvander3d - -Calculus --------- - -.. autosummary:: - :toctree: generated/ - - polyder - polyint - -Algebra -------- - -.. autosummary:: - :toctree: generated/ - - polyadd - polysub - polymul - polymulx - polydiv - polypow - -Miscellaneous -------------- - -.. autosummary:: - :toctree: generated/ - - polycompanion - polydomain - polyzero - polyone - polyx - polytrim - polyline +.. automodule:: numpy.polynomial.polynomial + :no-members: + :no-inherited-members: + :no-special-members: diff --git a/doc/source/reference/routines.polynomials.rst b/doc/source/reference/routines.polynomials.rst index e85d0549b0ae..4aea963c0116 100644 --- a/doc/source/reference/routines.polynomials.rst +++ b/doc/source/reference/routines.polynomials.rst @@ -1,30 +1,184 @@ +.. _routines.polynomial: + Polynomials *********** Polynomials in NumPy can be *created*, *manipulated*, and even *fitted* using -the :doc:`routines.polynomials.classes` +the :doc:`convenience classes <routines.polynomials.classes>` of the `numpy.polynomial` package, introduced in NumPy 1.4. Prior to NumPy 1.4, `numpy.poly1d` was the class of choice and it is still available in order to maintain backward compatibility. -However, the newer Polynomial package is more complete than `numpy.poly1d` -and its convenience classes are better behaved in the numpy environment. -Therefore Polynomial is recommended for new coding. +However, the newer `polynomial package <numpy.polynomial>` is more complete +and its `convenience classes <routines.polynomials.classes>` provide a +more consistent, better-behaved interface for working with polynomial +expressions. +Therefore :mod:`numpy.polynomial` is recommended for new coding. + +.. note:: **Terminology** + + The term *polynomial module* refers to the old API defined in + `numpy.lib.polynomial`, which includes the :class:`numpy.poly1d` class and + the polynomial functions prefixed with *poly* accessible from the `numpy` + namespace (e.g. `numpy.polyadd`, `numpy.polyval`, `numpy.polyfit`, etc.). + + The term *polynomial package* refers to the new API defined in + `numpy.polynomial`, which includes the convenience classes for the + different kinds of polynomials (`numpy.polynomial.Polynomial`, + `numpy.polynomial.Chebyshev`, etc.). + +Transitioning from `numpy.poly1d` to `numpy.polynomial` +------------------------------------------------------- + +As noted above, the :class:`poly1d class <numpy.poly1d>` and associated +functions defined in ``numpy.lib.polynomial``, such as `numpy.polyfit` +and `numpy.poly`, are considered legacy and should **not** be used in new +code. +Since NumPy version 1.4, the `numpy.polynomial` package is preferred for +working with polynomials. + +Quick Reference +~~~~~~~~~~~~~~~ + +The following table highlights some of the main differences between the +legacy polynomial module and the polynomial package for common tasks. +The `~numpy.polynomial.polynomial.Polynomial` class is imported for brevity:: + + from numpy.polynomial import Polynomial + + ++------------------------+------------------------------+---------------------------------------+ +| **How to...** | Legacy (`numpy.poly1d`) | `numpy.polynomial` | ++------------------------+------------------------------+---------------------------------------+ +| Create a | ``p = np.poly1d([1, 2, 3])`` | ``p = Polynomial([3, 2, 1])`` | +| polynomial object | | | +| from coefficients [1]_ | | | ++------------------------+------------------------------+---------------------------------------+ +| Create a polynomial | ``r = np.poly([-1, 1])`` | ``p = Polynomial.fromroots([-1, 1])`` | +| object from roots | ``p = np.poly1d(r)`` | | ++------------------------+------------------------------+---------------------------------------+ +| Fit a polynomial of | | | +| degree ``deg`` to data | ``np.polyfit(x, y, deg)`` | ``Polynomial.fit(x, y, deg)`` | ++------------------------+------------------------------+---------------------------------------+ + + +.. [1] Note the reversed ordering of the coefficients + +Transition Guide +~~~~~~~~~~~~~~~~ + +There are significant differences between ``numpy.lib.polynomial`` and +`numpy.polynomial`. +The most significant difference is the ordering of the coefficients for the +polynomial expressions. +The various routines in `numpy.polynomial` all +deal with series whose coefficients go from degree zero upward, +which is the *reverse order* of the poly1d convention. +The easy way to remember this is that indices +correspond to degree, i.e., ``coef[i]`` is the coefficient of the term of +degree *i*. + +Though the difference in convention may be confusing, it is straightforward to +convert from the legacy polynomial API to the new. +For example, the following demonstrates how you would convert a `numpy.poly1d` +instance representing the expression :math:`x^{2} + 2x + 3` to a +`~numpy.polynomial.polynomial.Polynomial` instance representing the same +expression:: + + >>> p1d = np.poly1d([1, 2, 3]) + >>> p = np.polynomial.Polynomial(p1d.coef[::-1]) + +In addition to the ``coef`` attribute, polynomials from the polynomial +package also have ``domain`` and ``window`` attributes. +These attributes are most relevant when fitting +polynomials to data, though it should be noted that polynomials with +different ``domain`` and ``window`` attributes are not considered equal, and +can't be mixed in arithmetic:: + + >>> p1 = np.polynomial.Polynomial([1, 2, 3]) + >>> p1 + Polynomial([1., 2., 3.], domain=[-1, 1], window=[-1, 1]) + >>> p2 = np.polynomial.Polynomial([1, 2, 3], domain=[-2, 2]) + >>> p1 == p2 + False + >>> p1 + p2 + Traceback (most recent call last): + ... + TypeError: Domains differ + +See the documentation for the +`convenience classes <routines.polynomials.classes>`_ for further details on +the ``domain`` and ``window`` attributes. -Transition notice ------------------ -The various routines in the Polynomial package all deal with -series whose coefficients go from degree zero upward, -which is the *reverse order* of the Poly1d convention. -The easy way to remember this is that indexes -correspond to degree, i.e., coef[i] is the coefficient of the term of -degree i. +Another major difference between the legacy polynomial module and the +polynomial package is polynomial fitting. In the old module, fitting was +done via the `~numpy.polyfit` function. In the polynomial package, the +`~numpy.polynomial.polynomial.Polynomial.fit` class method is preferred. For +example, consider a simple linear fit to the following data: +.. ipython:: python + + rng = np.random.default_rng() + x = np.arange(10) + y = np.arange(10) + rng.standard_normal(10) + +With the legacy polynomial module, a linear fit (i.e. polynomial of degree 1) +could be applied to these data with `~numpy.polyfit`: + +.. ipython:: python + + np.polyfit(x, y, deg=1) + +With the new polynomial API, the `~numpy.polynomial.polynomial.Polynomial.fit` +class method is preferred: + +.. ipython:: python + + p_fitted = np.polynomial.Polynomial.fit(x, y, deg=1) + p_fitted + +Note that the coefficients are given *in the scaled domain* defined by the +linear mapping between the ``window`` and ``domain``. +`~numpy.polynomial.polynomial.Polynomial.convert` can be used to get the +coefficients in the unscaled data domain. + +.. ipython:: python + + p_fitted.convert() + +Documentation for the `~numpy.polynomial` Package +------------------------------------------------- + +In addition to standard power series polynomials, the polynomial package +provides several additional kinds of polynomials including Chebyshev, +Hermite (two subtypes), Laguerre, and Legendre polynomials. +Each of these has an associated +`convenience class <routines.polynomials.classes>` available from the +`numpy.polynomial` namespace that provides a consistent interface for working +with polynomials regardless of their type. .. toctree:: - :maxdepth: 2 + :maxdepth: 1 + + routines.polynomials.classes + +Documentation pertaining to specific functions defined for each kind of +polynomial individually can be found in the corresponding module documentation: + +.. toctree:: + :maxdepth: 1 + + routines.polynomials.polynomial + routines.polynomials.chebyshev + routines.polynomials.hermite + routines.polynomials.hermite_e + routines.polynomials.laguerre + routines.polynomials.legendre + routines.polynomials.polyutils + - routines.polynomials.package +Documentation for Legacy Polynomials +------------------------------------ .. toctree:: :maxdepth: 2 diff --git a/doc/source/reference/routines.random.rst b/doc/source/reference/routines.random.rst deleted file mode 100644 index c8b097d7d14c..000000000000 --- a/doc/source/reference/routines.random.rst +++ /dev/null @@ -1,81 +0,0 @@ -.. _routines.random: - -Random sampling (:mod:`numpy.random`) -************************************* - -.. currentmodule:: numpy.random - -Simple random data -================== -.. autosummary:: - :toctree: generated/ - - rand - randn - randint - random_integers - random_sample - random - ranf - sample - choice - bytes - -Permutations -============ -.. autosummary:: - :toctree: generated/ - - shuffle - permutation - -Distributions -============= -.. autosummary:: - :toctree: generated/ - - beta - binomial - chisquare - dirichlet - exponential - f - gamma - geometric - gumbel - hypergeometric - laplace - logistic - lognormal - logseries - multinomial - multivariate_normal - negative_binomial - noncentral_chisquare - noncentral_f - normal - pareto - poisson - power - rayleigh - standard_cauchy - standard_exponential - standard_gamma - standard_normal - standard_t - triangular - uniform - vonmises - wald - weibull - zipf - -Random generator -================ -.. autosummary:: - :toctree: generated/ - - RandomState - seed - get_state - set_state diff --git a/doc/source/reference/routines.rst b/doc/source/reference/routines.rst index a9e80480b870..593d017ccefc 100644 --- a/doc/source/reference/routines.rst +++ b/doc/source/reference/routines.rst @@ -28,10 +28,8 @@ indentation. routines.emath routines.err routines.fft - routines.financial routines.functional routines.help - routines.indexing routines.io routines.linalg routines.logic @@ -41,7 +39,7 @@ indentation. routines.other routines.padding routines.polynomials - routines.random + random/index routines.set routines.sort routines.statistics diff --git a/doc/source/reference/routines.set.rst b/doc/source/reference/routines.set.rst index b12d3d5f5e79..149c33a8b610 100644 --- a/doc/source/reference/routines.set.rst +++ b/doc/source/reference/routines.set.rst @@ -3,6 +3,11 @@ Set routines .. currentmodule:: numpy +.. autosummary:: + :toctree: generated/ + + lib.arraysetops + Making proper sets ------------------ .. autosummary:: diff --git a/doc/source/reference/routines.statistics.rst b/doc/source/reference/routines.statistics.rst index e287fe9c8453..cd93e60253fb 100644 --- a/doc/source/reference/routines.statistics.rst +++ b/doc/source/reference/routines.statistics.rst @@ -9,11 +9,7 @@ Order statistics .. autosummary:: :toctree: generated/ - - amin - amax - nanmin - nanmax + ptp percentile nanpercentile @@ -56,4 +52,5 @@ Histograms histogram2d histogramdd bincount + histogram_bin_edges digitize diff --git a/doc/source/reference/routines.testing.rst b/doc/source/reference/routines.testing.rst index ad95bb39922b..d9e98e94188d 100644 --- a/doc/source/reference/routines.testing.rst +++ b/doc/source/reference/routines.testing.rst @@ -1,3 +1,5 @@ +.. module:: numpy.testing + Test Support (:mod:`numpy.testing`) =================================== @@ -6,8 +8,9 @@ Test Support (:mod:`numpy.testing`) Common test support for all numpy test scripts. This single module should provide all the common functionality for numpy -tests in a single location, so that test scripts can just import it and -work right away. +tests in a single location, so that :ref:`test scripts +<development-environment>` can just import it and work right away. For +background, see the :ref:`testing-guidelines` Asserts @@ -15,9 +18,6 @@ Asserts .. autosummary:: :toctree: generated/ - assert_almost_equal - assert_approx_equal - assert_array_almost_equal assert_allclose assert_array_almost_equal_nulp assert_array_max_ulp @@ -29,16 +29,29 @@ Asserts assert_warns assert_string_equal +Asserts (not recommended) +------------------------- +It is recommended to use one of `assert_allclose`, +`assert_array_almost_equal_nulp` or `assert_array_max_ulp` instead of these +functions for more consistent floating point comparisons. + +.. autosummary:: + :toctree: generated/ + + assert_almost_equal + assert_approx_equal + assert_array_almost_equal + Decorators ---------- .. autosummary:: :toctree: generated/ - decorators.deprecated - decorators.knownfailureif - decorators.setastest - decorators.skipif - decorators.slow + dec.deprecated + dec.knownfailureif + dec.setastest + dec.skipif + dec.slow decorate_methods Test Running @@ -50,3 +63,10 @@ Test Running run_module_suite rundocs suppress_warnings + +Guidelines +---------- + +.. toctree:: + + testing diff --git a/doc/source/reference/simd/simd-optimizations-tables-diff.inc b/doc/source/reference/simd/simd-optimizations-tables-diff.inc new file mode 100644 index 000000000000..41fa96703251 --- /dev/null +++ b/doc/source/reference/simd/simd-optimizations-tables-diff.inc @@ -0,0 +1,37 @@ +.. generated via source/reference/simd/simd-optimizations.py + +x86::Intel Compiler - CPU feature names +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. table:: + :align: left + + =========== ================================================================================================================== + Name Implies + =========== ================================================================================================================== + ``FMA3`` ``SSE`` ``SSE2`` ``SSE3`` ``SSSE3`` ``SSE41`` ``POPCNT`` ``SSE42`` ``AVX`` ``F16C`` **AVX2** + ``AVX2`` ``SSE`` ``SSE2`` ``SSE3`` ``SSSE3`` ``SSE41`` ``POPCNT`` ``SSE42`` ``AVX`` ``F16C`` **FMA3** + ``AVX512F`` ``SSE`` ``SSE2`` ``SSE3`` ``SSSE3`` ``SSE41`` ``POPCNT`` ``SSE42`` ``AVX`` ``F16C`` ``FMA3`` ``AVX2`` **AVX512CD** + =========== ================================================================================================================== + +.. note:: + The following features aren't supported by x86::Intel Compiler: + **XOP FMA4** + +x86::Microsoft Visual C/C++ - CPU feature names +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. table:: + :align: left + + ============ ================================================================================================================================= + Name Implies + ============ ================================================================================================================================= + ``FMA3`` ``SSE`` ``SSE2`` ``SSE3`` ``SSSE3`` ``SSE41`` ``POPCNT`` ``SSE42`` ``AVX`` ``F16C`` **AVX2** + ``AVX2`` ``SSE`` ``SSE2`` ``SSE3`` ``SSSE3`` ``SSE41`` ``POPCNT`` ``SSE42`` ``AVX`` ``F16C`` **FMA3** + ``AVX512F`` ``SSE`` ``SSE2`` ``SSE3`` ``SSSE3`` ``SSE41`` ``POPCNT`` ``SSE42`` ``AVX`` ``F16C`` ``FMA3`` ``AVX2`` **AVX512CD** **AVX512_SKX** + ``AVX512CD`` ``SSE`` ``SSE2`` ``SSE3`` ``SSSE3`` ``SSE41`` ``POPCNT`` ``SSE42`` ``AVX`` ``F16C`` ``FMA3`` ``AVX2`` ``AVX512F`` **AVX512_SKX** + ============ ================================================================================================================================= + +.. note:: + The following features aren't supported by x86::Microsoft Visual C/C++: + **AVX512_KNL AVX512_KNM** + diff --git a/doc/source/reference/simd/simd-optimizations-tables.inc b/doc/source/reference/simd/simd-optimizations-tables.inc new file mode 100644 index 000000000000..f038a91e1fd1 --- /dev/null +++ b/doc/source/reference/simd/simd-optimizations-tables.inc @@ -0,0 +1,103 @@ +.. generated via source/reference/simd/simd-optimizations.py + +x86 - CPU feature names +~~~~~~~~~~~~~~~~~~~~~~~ +.. table:: + :align: left + + ============ ================================================================================================================= + Name Implies + ============ ================================================================================================================= + ``SSE`` ``SSE2`` + ``SSE2`` ``SSE`` + ``SSE3`` ``SSE`` ``SSE2`` + ``SSSE3`` ``SSE`` ``SSE2`` ``SSE3`` + ``SSE41`` ``SSE`` ``SSE2`` ``SSE3`` ``SSSE3`` + ``POPCNT`` ``SSE`` ``SSE2`` ``SSE3`` ``SSSE3`` ``SSE41`` + ``SSE42`` ``SSE`` ``SSE2`` ``SSE3`` ``SSSE3`` ``SSE41`` ``POPCNT`` + ``AVX`` ``SSE`` ``SSE2`` ``SSE3`` ``SSSE3`` ``SSE41`` ``POPCNT`` ``SSE42`` + ``XOP`` ``SSE`` ``SSE2`` ``SSE3`` ``SSSE3`` ``SSE41`` ``POPCNT`` ``SSE42`` ``AVX`` + ``FMA4`` ``SSE`` ``SSE2`` ``SSE3`` ``SSSE3`` ``SSE41`` ``POPCNT`` ``SSE42`` ``AVX`` + ``F16C`` ``SSE`` ``SSE2`` ``SSE3`` ``SSSE3`` ``SSE41`` ``POPCNT`` ``SSE42`` ``AVX`` + ``FMA3`` ``SSE`` ``SSE2`` ``SSE3`` ``SSSE3`` ``SSE41`` ``POPCNT`` ``SSE42`` ``AVX`` ``F16C`` + ``AVX2`` ``SSE`` ``SSE2`` ``SSE3`` ``SSSE3`` ``SSE41`` ``POPCNT`` ``SSE42`` ``AVX`` ``F16C`` + ``AVX512F`` ``SSE`` ``SSE2`` ``SSE3`` ``SSSE3`` ``SSE41`` ``POPCNT`` ``SSE42`` ``AVX`` ``F16C`` ``FMA3`` ``AVX2`` + ``AVX512CD`` ``SSE`` ``SSE2`` ``SSE3`` ``SSSE3`` ``SSE41`` ``POPCNT`` ``SSE42`` ``AVX`` ``F16C`` ``FMA3`` ``AVX2`` ``AVX512F`` + ============ ================================================================================================================= + +x86 - Group names +~~~~~~~~~~~~~~~~~ +.. table:: + :align: left + + ============== ===================================================== =========================================================================================================================================================================== + Name Gather Implies + ============== ===================================================== =========================================================================================================================================================================== + ``AVX512_KNL`` ``AVX512ER`` ``AVX512PF`` ``SSE`` ``SSE2`` ``SSE3`` ``SSSE3`` ``SSE41`` ``POPCNT`` ``SSE42`` ``AVX`` ``F16C`` ``FMA3`` ``AVX2`` ``AVX512F`` ``AVX512CD`` + ``AVX512_KNM`` ``AVX5124FMAPS`` ``AVX5124VNNIW`` ``AVX512VPOPCNTDQ`` ``SSE`` ``SSE2`` ``SSE3`` ``SSSE3`` ``SSE41`` ``POPCNT`` ``SSE42`` ``AVX`` ``F16C`` ``FMA3`` ``AVX2`` ``AVX512F`` ``AVX512CD`` ``AVX512_KNL`` + ``AVX512_SKX`` ``AVX512VL`` ``AVX512BW`` ``AVX512DQ`` ``SSE`` ``SSE2`` ``SSE3`` ``SSSE3`` ``SSE41`` ``POPCNT`` ``SSE42`` ``AVX`` ``F16C`` ``FMA3`` ``AVX2`` ``AVX512F`` ``AVX512CD`` + ``AVX512_CLX`` ``AVX512VNNI`` ``SSE`` ``SSE2`` ``SSE3`` ``SSSE3`` ``SSE41`` ``POPCNT`` ``SSE42`` ``AVX`` ``F16C`` ``FMA3`` ``AVX2`` ``AVX512F`` ``AVX512CD`` ``AVX512_SKX`` + ``AVX512_CNL`` ``AVX512IFMA`` ``AVX512VBMI`` ``SSE`` ``SSE2`` ``SSE3`` ``SSSE3`` ``SSE41`` ``POPCNT`` ``SSE42`` ``AVX`` ``F16C`` ``FMA3`` ``AVX2`` ``AVX512F`` ``AVX512CD`` ``AVX512_SKX`` + ``AVX512_ICL`` ``AVX512VBMI2`` ``AVX512BITALG`` ``AVX512VPOPCNTDQ`` ``SSE`` ``SSE2`` ``SSE3`` ``SSSE3`` ``SSE41`` ``POPCNT`` ``SSE42`` ``AVX`` ``F16C`` ``FMA3`` ``AVX2`` ``AVX512F`` ``AVX512CD`` ``AVX512_SKX`` ``AVX512_CLX`` ``AVX512_CNL`` + ============== ===================================================== =========================================================================================================================================================================== + +IBM/POWER big-endian - CPU feature names +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. table:: + :align: left + + ======== ================ + Name Implies + ======== ================ + ``VSX`` + ``VSX2`` ``VSX`` + ``VSX3`` ``VSX`` ``VSX2`` + ======== ================ + +IBM/POWER little-endian - CPU feature names +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. table:: + :align: left + + ======== ================ + Name Implies + ======== ================ + ``VSX`` ``VSX2`` + ``VSX2`` ``VSX`` + ``VSX3`` ``VSX`` ``VSX2`` + ======== ================ + +ARMv7/A32 - CPU feature names +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. table:: + :align: left + + ============== =========================================================== + Name Implies + ============== =========================================================== + ``NEON`` + ``NEON_FP16`` ``NEON`` + ``NEON_VFPV4`` ``NEON`` ``NEON_FP16`` + ``ASIMD`` ``NEON`` ``NEON_FP16`` ``NEON_VFPV4`` + ``ASIMDHP`` ``NEON`` ``NEON_FP16`` ``NEON_VFPV4`` ``ASIMD`` + ``ASIMDDP`` ``NEON`` ``NEON_FP16`` ``NEON_VFPV4`` ``ASIMD`` + ``ASIMDFHM`` ``NEON`` ``NEON_FP16`` ``NEON_VFPV4`` ``ASIMD`` ``ASIMDHP`` + ============== =========================================================== + +ARMv8/A64 - CPU feature names +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. table:: + :align: left + + ============== =========================================================== + Name Implies + ============== =========================================================== + ``NEON`` ``NEON_FP16`` ``NEON_VFPV4`` ``ASIMD`` + ``NEON_FP16`` ``NEON`` ``NEON_VFPV4`` ``ASIMD`` + ``NEON_VFPV4`` ``NEON`` ``NEON_FP16`` ``ASIMD`` + ``ASIMD`` ``NEON`` ``NEON_FP16`` ``NEON_VFPV4`` + ``ASIMDHP`` ``NEON`` ``NEON_FP16`` ``NEON_VFPV4`` ``ASIMD`` + ``ASIMDDP`` ``NEON`` ``NEON_FP16`` ``NEON_VFPV4`` ``ASIMD`` + ``ASIMDFHM`` ``NEON`` ``NEON_FP16`` ``NEON_VFPV4`` ``ASIMD`` ``ASIMDHP`` + ============== =========================================================== + diff --git a/doc/source/reference/simd/simd-optimizations.py b/doc/source/reference/simd/simd-optimizations.py new file mode 100644 index 000000000000..a78302db5e89 --- /dev/null +++ b/doc/source/reference/simd/simd-optimizations.py @@ -0,0 +1,190 @@ +""" +Generate CPU features tables from CCompilerOpt +""" +from os import sys, path +gen_path = path.dirname(path.realpath(__file__)) +#sys.path.append(path.abspath(path.join(gen_path, *([".."]*4), "numpy", "distutils"))) +#from ccompiler_opt import CCompilerOpt +from numpy.distutils.ccompiler_opt import CCompilerOpt + +class FakeCCompilerOpt(CCompilerOpt): + fake_info = ("arch", "compiler", "extra_args") + # disable caching no need for it + conf_nocache = True + def __init__(self, *args, **kwargs): + no_cc = None + CCompilerOpt.__init__(self, no_cc, **kwargs) + def dist_compile(self, sources, flags, **kwargs): + return sources + def dist_info(self): + return FakeCCompilerOpt.fake_info + @staticmethod + def dist_log(*args, stderr=False): + # avoid printing + pass + def feature_test(self, name, force_flags=None): + # To speed up + return True + + def gen_features_table(self, features, ignore_groups=True, + field_names=["Name", "Implies"], + fstyle=None, fstyle_implies=None, **kwargs): + rows = [] + if fstyle is None: + fstyle = lambda ft: f'``{ft}``' + if fstyle_implies is None: + fstyle_implies = lambda origin, ft: fstyle(ft) + for f in self.feature_sorted(features): + is_group = "group" in self.feature_supported.get(f, {}) + if ignore_groups and is_group: + continue + implies = self.feature_sorted(self.feature_implies(f)) + implies = ' '.join([fstyle_implies(f, i) for i in implies]) + rows.append([fstyle(f), implies]) + if rows: + return self.gen_rst_table(field_names, rows, **kwargs) + + def gen_gfeatures_table(self, features, + field_names=["Name", "Gather", "Implies"], + fstyle=None, fstyle_implies=None, **kwargs): + rows = [] + if fstyle is None: + fstyle = lambda ft: f'``{ft}``' + if fstyle_implies is None: + fstyle_implies = lambda origin, ft: fstyle(ft) + for f in self.feature_sorted(features): + gather = self.feature_supported.get(f, {}).get("group", None) + if not gather: + continue + implies = self.feature_sorted(self.feature_implies(f)) + implies = ' '.join([fstyle_implies(f, i) for i in implies]) + gather = ' '.join([fstyle_implies(f, i) for i in gather]) + rows.append([fstyle(f), gather, implies]) + if rows: + return self.gen_rst_table(field_names, rows, **kwargs) + + def gen_rst_table(self, field_names, rows, tab_size=4): + assert(not rows or len(field_names) == len(rows[0])) + rows.append(field_names) + fld_len = len(field_names) + cls_len = [max(len(c[i]) for c in rows) for i in range(fld_len)] + del rows[-1] + cformat = ' '.join('{:<%d}' % i for i in cls_len) + border = cformat.format(*['='*i for i in cls_len]) + + rows = [cformat.format(*row) for row in rows] + # header + rows = [border, cformat.format(*field_names), border] + rows + # footer + rows += [border] + # add left margin + rows = [(' ' * tab_size) + r for r in rows] + return '\n'.join(rows) + +def features_table_sections(name, ftable=None, gtable=None, tab_size=4): + tab = ' '*tab_size + content = '' + if ftable: + title = f"{name} - CPU feature names" + content = ( + f"{title}\n{'~'*len(title)}" + f"\n.. table::\n{tab}:align: left\n\n" + f"{ftable}\n\n" + ) + if gtable: + title = f"{name} - Group names" + content += ( + f"{title}\n{'~'*len(title)}" + f"\n.. table::\n{tab}:align: left\n\n" + f"{gtable}\n\n" + ) + return content + +def features_table(arch, cc="gcc", pretty_name=None, **kwargs): + FakeCCompilerOpt.fake_info = (arch, cc, '') + ccopt = FakeCCompilerOpt(cpu_baseline="max") + features = ccopt.cpu_baseline_names() + ftable = ccopt.gen_features_table(features, **kwargs) + gtable = ccopt.gen_gfeatures_table(features, **kwargs) + + if not pretty_name: + pretty_name = arch + '/' + cc + return features_table_sections(pretty_name, ftable, gtable, **kwargs) + +def features_table_diff(arch, cc, cc_vs="gcc", pretty_name=None, **kwargs): + FakeCCompilerOpt.fake_info = (arch, cc, '') + ccopt = FakeCCompilerOpt(cpu_baseline="max") + fnames = ccopt.cpu_baseline_names() + features = {f:ccopt.feature_implies(f) for f in fnames} + + FakeCCompilerOpt.fake_info = (arch, cc_vs, '') + ccopt_vs = FakeCCompilerOpt(cpu_baseline="max") + fnames_vs = ccopt_vs.cpu_baseline_names() + features_vs = {f:ccopt_vs.feature_implies(f) for f in fnames_vs} + + common = set(fnames).intersection(fnames_vs) + extra_avl = set(fnames).difference(fnames_vs) + not_avl = set(fnames_vs).difference(fnames) + diff_impl_f = {f:features[f].difference(features_vs[f]) for f in common} + diff_impl = {k for k, v in diff_impl_f.items() if v} + + fbold = lambda ft: f'**{ft}**' if ft in extra_avl else f'``{ft}``' + fbold_implies = lambda origin, ft: ( + f'**{ft}**' if ft in diff_impl_f.get(origin, {}) else f'``{ft}``' + ) + diff_all = diff_impl.union(extra_avl) + ftable = ccopt.gen_features_table( + diff_all, fstyle=fbold, fstyle_implies=fbold_implies, **kwargs + ) + gtable = ccopt.gen_gfeatures_table( + diff_all, fstyle=fbold, fstyle_implies=fbold_implies, **kwargs + ) + if not pretty_name: + pretty_name = arch + '/' + cc + content = features_table_sections(pretty_name, ftable, gtable, **kwargs) + + if not_avl: + not_avl = ccopt_vs.feature_sorted(not_avl) + not_avl = ' '.join(not_avl) + content += ( + ".. note::\n" + f" The following features aren't supported by {pretty_name}:\n" + f" **{not_avl}**\n\n" + ) + return content + +if __name__ == '__main__': + pretty_names = { + "PPC64": "IBM/POWER big-endian", + "PPC64LE": "IBM/POWER little-endian", + "ARMHF": "ARMv7/A32", + "AARCH64": "ARMv8/A64", + "ICC": "Intel Compiler", + # "ICCW": "Intel Compiler msvc-like", + "MSVC": "Microsoft Visual C/C++" + } + with open(path.join(gen_path, 'simd-optimizations-tables.inc'), 'wt') as fd: + fd.write(f'.. generated via {__file__}\n\n') + for arch in ( + ("x86", "PPC64", "PPC64LE", "ARMHF", "AARCH64") + ): + pretty_name = pretty_names.get(arch, arch) + table = features_table(arch=arch, pretty_name=pretty_name) + assert(table) + fd.write(table) + + with open(path.join(gen_path, 'simd-optimizations-tables-diff.inc'), 'wt') as fd: + fd.write(f'.. generated via {__file__}\n\n') + for arch, cc_names in ( + ("x86", ("clang", "ICC", "MSVC")), + ("PPC64", ("clang",)), + ("PPC64LE", ("clang",)), + ("ARMHF", ("clang",)), + ("AARCH64", ("clang",)) + ): + arch_pname = pretty_names.get(arch, arch) + for cc in cc_names: + pretty_name = f"{arch_pname}::{pretty_names.get(cc, cc)}" + table = features_table_diff(arch=arch, cc=cc, pretty_name=pretty_name) + if table: + fd.write(table) diff --git a/doc/source/reference/simd/simd-optimizations.rst b/doc/source/reference/simd/simd-optimizations.rst new file mode 100644 index 000000000000..9de6d1734079 --- /dev/null +++ b/doc/source/reference/simd/simd-optimizations.rst @@ -0,0 +1,527 @@ +****************** +SIMD Optimizations +****************** + +NumPy provides a set of macros that define `Universal Intrinsics`_ to +abstract out typical platform-specific intrinsics so SIMD code needs to be +written only once. There are three layers: + +- Code is *written* using the universal intrinsic macros, with guards that + will enable use of the macros only when the compiler recognizes them. + In NumPy, these are used to construct multiple ufunc loops. Current policy is + to create three loops: One loop is the default and uses no intrinsics. One + uses the minimum intrinsics required on the architecture. And the third is + written using the maximum set of intrinsics possible. +- At *compile* time, a distutils command is used to define the minimum and + maximum features to support, based on user choice and compiler support. The + appropriate macros are overlaid with the platform / architecture intrinsics, + and the three loops are compiled. +- At *runtime import*, the CPU is probed for the set of supported intrinsic + features. A mechanism is used to grab the pointer to the most appropriate + function, and this will be the one called for the function. + + +Build options for compilation +============================= + +- ``--cpu-baseline``: minimal set of required optimizations. Default + value is ``min`` which provides the minimum CPU features that can + safely run on a wide range of platforms within the processor family. + +- ``--cpu-dispatch``: dispatched set of additional optimizations. + The default value is ``max -xop -fma4`` which enables all CPU + features, except for AMD legacy features(in case of X86). + +The command arguments are available in ``build``, ``build_clib``, and +``build_ext``. +if ``build_clib`` or ``build_ext`` are not specified by the user, the arguments of +``build`` will be used instead, which also holds the default values. + +Optimization names can be CPU features or groups of features that gather +several features or :ref:`special options <special-options>` to perform a series of procedures. + + +The following tables show the current supported optimizations sorted from the lowest to the highest interest. + +.. include:: simd-optimizations-tables.inc + +---- + +.. _tables-diff: + +While the above tables are based on the GCC Compiler, the following tables showing the differences in the +other compilers: + +.. include:: simd-optimizations-tables-diff.inc + +.. _special-options: + +Special options +~~~~~~~~~~~~~~~ + +- ``NONE``: enable no features + +- ``NATIVE``: Enables all CPU features that supported by the current + machine, this operation is based on the compiler flags (``-march=native, -xHost, /QxHost``) + +- ``MIN``: Enables the minimum CPU features that can safely run on a wide range of platforms: + + .. table:: + :align: left + + ====================================== ======================================= + For Arch Returns + ====================================== ======================================= + ``x86`` ``SSE`` ``SSE2`` + ``x86`` ``64-bit mode`` ``SSE`` ``SSE2`` ``SSE3`` + ``IBM/POWER`` ``big-endian mode`` ``NONE`` + ``IBM/POWER`` ``little-endian mode`` ``VSX`` ``VSX2`` + ``ARMHF`` ``NONE`` + ``ARM64`` ``AARCH64`` ``NEON`` ``NEON_FP16`` ``NEON_VFPV4`` + ``ASIMD`` + ====================================== ======================================= + +- ``MAX``: Enables all supported CPU features by the Compiler and platform. + +- ``Operators-/+``: remove or add features, useful with options ``MAX``, ``MIN`` and ``NATIVE``. + +NOTES +~~~~~~~~~~~~~ +- CPU features and other options are case-insensitive. + +- The order of the requested optimizations doesn't matter. + +- Either commas or spaces can be used as a separator, e.g. ``--cpu-dispatch``\ = + "avx2 avx512f" or ``--cpu-dispatch``\ = "avx2, avx512f" both work, but the + arguments must be enclosed in quotes. + +- The operand ``+`` is only added for nominal reasons, For example: + ``--cpu-baseline= "min avx2"`` is equivalent to ``--cpu-baseline="min + avx2"``. + ``--cpu-baseline="min,avx2"`` is equivalent to ``--cpu-baseline`="min,+avx2"`` + +- If the CPU feature is not supported by the user platform or + compiler, it will be skipped rather than raising a fatal error. + +- Any specified CPU feature to ``--cpu-dispatch`` will be skipped if + it's part of CPU baseline features + +- The ``--cpu-baseline`` argument force-enables implied features, + e.g. ``--cpu-baseline``\ ="sse42" is equivalent to + ``--cpu-baseline``\ ="sse sse2 sse3 ssse3 sse41 popcnt sse42" + +- The value of ``--cpu-baseline`` will be treated as "native" if + compiler native flag ``-march=native`` or ``-xHost`` or ``QxHost`` is + enabled through environment variable ``CFLAGS`` + +- The validation process for the requested optimizations when it comes to + ``--cpu-baseline`` isn't strict. For example, if the user requested + ``AVX2`` but the compiler doesn't support it then we just skip it and return + the maximum optimization that the compiler can handle depending on the + implied features of ``AVX2``, let us assume ``AVX``. + +- The user should always check the final report through the build log + to verify the enabled features. + +Special cases +~~~~~~~~~~~~~ + +**Interrelated CPU features**: Some exceptional conditions force us to link some features together when it come to certain compilers or architectures, resulting in the impossibility of building them separately. +These conditions can be divided into two parts, as follows: + +- **Architectural compatibility**: The need to align certain CPU features that are assured + to be supported by successive generations of the same architecture, for example: + + - On ppc64le `VSX(ISA 2.06)` and `VSX2(ISA 2.07)` both imply one another since the + first generation that supports little-endian mode is Power-8`(ISA 2.07)` + - On AArch64 `NEON` `FP16` `VFPV4` `ASIMD` implies each other since they are part of the + hardware baseline. + +- **Compilation compatibility**: Not all **C/C++** compilers provide independent support for all CPU + features. For example, **Intel**'s compiler doesn't provide separated flags for `AVX2` and `FMA3`, + it makes sense since all Intel CPUs that comes with `AVX2` also support `FMA3` and vice versa, + but this approach is incompatible with other **x86** CPUs from **AMD** or **VIA**. + Therefore, there are differences in the depiction of CPU features between the C/C++ compilers, + as shown in the :ref:`tables above <tables-diff>`. + + +Behaviors and Errors +~~~~~~~~~~~~~~~~~~~~ + + + +Usage and Examples +~~~~~~~~~~~~~~~~~~ + +Report and Trace +~~~~~~~~~~~~~~~~ + +Understanding CPU Dispatching, How the NumPy dispatcher works? +============================================================== + +NumPy dispatcher is based on multi-source compiling, which means taking +a certain source and compiling it multiple times with different compiler +flags and also with different **C** definitions that affect the code +paths to enable certain instruction-sets for each compiled object +depending on the required optimizations, then combining the returned +objects together. + +.. figure:: ../figures/opt-infra.png + +This mechanism should support all compilers and it doesn't require any +compiler-specific extension, but at the same time it is adds a few steps to +normal compilation that are explained as follows: + +1- Configuration +~~~~~~~~~~~~~~~~ + +Configuring the required optimization by the user before starting to build the +source files via the two command arguments as explained above: + +- ``--cpu-baseline``: minimal set of required optimizations. + +- ``--cpu-dispatch``: dispatched set of additional optimizations. + + +2- Discovering the environment +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In this part, we check the compiler and platform architecture +and cache some of the intermediary results to speed up rebuilding. + +3- Validating the requested optimizations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +By testing them against the compiler, and seeing what the compiler can +support according to the requested optimizations. + +4- Generating the main configuration header +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The generated header ``_cpu_dispatch.h`` contains all the definitions and +headers of instruction-sets for the required optimizations that have been +validated during the previous step. + +It also contains extra C definitions that are used for defining NumPy's +Python-level module attributes ``__cpu_baseline__`` and ``__cpu_dispaٍtch__``. + +**What is in this header?** + +The example header was dynamically generated by gcc on an X86 machine. +The compiler supports ``--cpu-baseline="sse sse2 sse3"`` and +``--cpu-dispatch="ssse3 sse41"``, and the result is below. + +.. code:: c + + // The header should be located at numpy/numpy/core/src/common/_cpu_dispatch.h + /**NOTE + ** C definitions prefixed with "NPY_HAVE_" represent + ** the required optimzations. + ** + ** C definitions prefixed with 'NPY__CPU_TARGET_' are protected and + ** shouldn't be used by any NumPy C sources. + */ + /******* baseline features *******/ + /** SSE **/ + #define NPY_HAVE_SSE 1 + #include <xmmintrin.h> + /** SSE2 **/ + #define NPY_HAVE_SSE2 1 + #include <emmintrin.h> + /** SSE3 **/ + #define NPY_HAVE_SSE3 1 + #include <pmmintrin.h> + + /******* dispatch-able features *******/ + #ifdef NPY__CPU_TARGET_SSSE3 + /** SSSE3 **/ + #define NPY_HAVE_SSSE3 1 + #include <tmmintrin.h> + #endif + #ifdef NPY__CPU_TARGET_SSE41 + /** SSE41 **/ + #define NPY_HAVE_SSE41 1 + #include <smmintrin.h> + #endif + +**Baseline features** are the minimal set of required optimizations configured +via ``--cpu-baseline``. They have no preprocessor guards and they're +always on, which means they can be used in any source. + +Does this mean NumPy's infrastructure passes the compiler's flags of +baseline features to all sources? + +Definitely, yes. But the :ref:`dispatch-able sources <dispatchable-sources>` are +treated differently. + +What if the user specifies certain **baseline features** during the +build but at runtime the machine doesn't support even these +features? Will the compiled code be called via one of these definitions, or +maybe the compiler itself auto-generated/vectorized certain piece of code +based on the provided command line compiler flags? + +During the loading of the NumPy module, there's a validation step +which detects this behavior. It will raise a Python runtime error to inform the +user. This is to prevent the CPU reaching an illegal instruction error causing +a segfault. + +**Dispatch-able features** are our dispatched set of additional optimizations +that were configured via ``--cpu-dispatch``. They are not activated by +default and are always guarded by other C definitions prefixed with +``NPY__CPU_TARGET_``. C definitions ``NPY__CPU_TARGET_`` are only +enabled within **dispatch-able sources**. + +.. _dispatchable-sources: + +5- Dispatch-able sources and configuration statements +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Dispatch-able sources are special **C** files that can be compiled multiple +times with different compiler flags and also with different **C** +definitions. These affect code paths to enable certain +instruction-sets for each compiled object according to "**the +configuration statements**" that must be declared between a **C** +comment\ ``(/**/)`` and start with a special mark **@targets** at the +top of each dispatch-able source. At the same time, dispatch-able +sources will be treated as normal **C** sources if the optimization was +disabled by the command argument ``--disable-optimization`` . + +**What are configuration statements?** + +Configuration statements are sort of keywords combined together to +determine the required optimization for the dispatch-able source. + +Example: + +.. code:: c + + /*@targets avx2 avx512f vsx2 vsx3 asimd asimdhp */ + // C code + +The keywords mainly represent the additional optimizations configured +through ``--cpu-dispatch``, but it can also represent other options such as: + +- Target groups: pre-configured configuration statements used for + managing the required optimizations from outside the dispatch-able source. + +- Policies: collections of options used for changing the default + behaviors or forcing the compilers to perform certain things. + +- "baseline": a unique keyword represents the minimal optimizations + that configured through ``--cpu-baseline`` + +**Numpy's infrastructure handles dispatch-able sources in four steps**: + +- **(A) Recognition**: Just like source templates and F2PY, the + dispatch-able sources requires a special extension ``*.dispatch.c`` + to mark C dispatch-able source files, and for C++ + ``*.dispatch.cpp`` or ``*.dispatch.cxx`` + **NOTE**: C++ not supported yet. + +- **(B) Parsing and validating**: In this step, the + dispatch-able sources that had been filtered by the previous step + are parsed and validated by the configuration statements for each one + of them one by one in order to determine the required optimizations. + +- **(C) Wrapping**: This is the approach taken by NumPy's + infrastructure, which has proved to be sufficiently flexible in order + to compile a single source multiple times with different **C** + definitions and flags that affect the code paths. The process is + achieved by creating a temporary **C** source for each required + optimization that related to the additional optimization, which + contains the declarations of the **C** definitions and includes the + involved source via the **C** directive **#include**. For more + clarification take a look at the following code for AVX512F : + + .. code:: c + + /* + * this definition is used by NumPy utilities as suffixes for the + * exported symbols + */ + #define NPY__CPU_TARGET_CURRENT AVX512F + /* + * The following definitions enable + * definitions of the dispatch-able features that are defined within the main + * configuration header. These are definitions for the implied features. + */ + #define NPY__CPU_TARGET_SSE + #define NPY__CPU_TARGET_SSE2 + #define NPY__CPU_TARGET_SSE3 + #define NPY__CPU_TARGET_SSSE3 + #define NPY__CPU_TARGET_SSE41 + #define NPY__CPU_TARGET_POPCNT + #define NPY__CPU_TARGET_SSE42 + #define NPY__CPU_TARGET_AVX + #define NPY__CPU_TARGET_F16C + #define NPY__CPU_TARGET_FMA3 + #define NPY__CPU_TARGET_AVX2 + #define NPY__CPU_TARGET_AVX512F + // our dispatch-able source + #include "/the/absuolate/path/of/hello.dispatch.c" + +- **(D) Dispatch-able configuration header**: The infrastructure + generates a config header for each dispatch-able source, this header + mainly contains two abstract **C** macros used for identifying the + generated objects, so they can be used for runtime dispatching + certain symbols from the generated objects by any **C** source. It is + also used for forward declarations. + + The generated header takes the name of the dispatch-able source after + excluding the extension and replace it with '**.h**', for example + assume we have a dispatch-able source called **hello.dispatch.c** and + contains the following: + + .. code:: c + + // hello.dispatch.c + /*@targets baseline sse42 avx512f */ + #include <stdio.h> + #include "numpy/utils.h" // NPY_CAT, NPY_TOSTR + + #ifndef NPY__CPU_TARGET_CURRENT + // wrapping the dispatch-able source only happens to the additional optimizations + // but if the keyword 'baseline' provided within the configuration statements, + // the infrastructure will add extra compiling for the dispatch-able source by + // passing it as-is to the compiler without any changes. + #define CURRENT_TARGET(X) X + #define NPY__CPU_TARGET_CURRENT baseline // for printing only + #else + // since we reach to this point, that's mean we're dealing with + // the additional optimizations, so it could be SSE42 or AVX512F + #define CURRENT_TARGET(X) NPY_CAT(NPY_CAT(X, _), NPY__CPU_TARGET_CURRENT) + #endif + // Macro 'CURRENT_TARGET' adding the current target as suffux to the exported symbols, + // to avoid linking duplications, NumPy already has a macro called + // 'NPY_CPU_DISPATCH_CURFX' similar to it, located at + // numpy/numpy/core/src/common/npy_cpu_dispatch.h + // NOTE: we tend to not adding suffixes to the baseline exported symbols + void CURRENT_TARGET(simd_whoami)(const char *extra_info) + { + printf("I'm " NPY_TOSTR(NPY__CPU_TARGET_CURRENT) ", %s\n", extra_info); + } + + Now assume you attached **hello.dispatch.c** to the source tree, then + the infrastructure should generate a temporary config header called + **hello.dispatch.h** that can be reached by any source in the source + tree, and it should contain the following code : + + .. code:: c + + #ifndef NPY__CPU_DISPATCH_EXPAND_ + // To expand the macro calls in this header + #define NPY__CPU_DISPATCH_EXPAND_(X) X + #endif + // Undefining the following macros, due to the possibility of including config headers + // multiple times within the same source and since each config header represents + // different required optimizations according to the specified configuration + // statements in the dispatch-able source that derived from it. + #undef NPY__CPU_DISPATCH_BASELINE_CALL + #undef NPY__CPU_DISPATCH_CALL + // nothing strange here, just a normal preprocessor callback + // enabled only if 'baseline' specified within the configuration statements + #define NPY__CPU_DISPATCH_BASELINE_CALL(CB, ...) \ + NPY__CPU_DISPATCH_EXPAND_(CB(__VA_ARGS__)) + // 'NPY__CPU_DISPATCH_CALL' is an abstract macro is used for dispatching + // the required optimizations that specified within the configuration statements. + // + // @param CHK, Expected a macro that can be used to detect CPU features + // in runtime, which takes a CPU feature name without string quotes and + // returns the testing result in a shape of boolean value. + // NumPy already has macro called "NPY_CPU_HAVE", which fits this requirement. + // + // @param CB, a callback macro that expected to be called multiple times depending + // on the required optimizations, the callback should receive the following arguments: + // 1- The pending calls of @param CHK filled up with the required CPU features, + // that need to be tested first in runtime before executing call belong to + // the compiled object. + // 2- The required optimization name, same as in 'NPY__CPU_TARGET_CURRENT' + // 3- Extra arguments in the macro itself + // + // By default the callback calls are sorted depending on the highest interest + // unless the policy "$keep_sort" was in place within the configuration statements + // see "Dive into the CPU dispatcher" for more clarification. + #define NPY__CPU_DISPATCH_CALL(CHK, CB, ...) \ + NPY__CPU_DISPATCH_EXPAND_(CB((CHK(AVX512F)), AVX512F, __VA_ARGS__)) \ + NPY__CPU_DISPATCH_EXPAND_(CB((CHK(SSE)&&CHK(SSE2)&&CHK(SSE3)&&CHK(SSSE3)&&CHK(SSE41)), SSE41, __VA_ARGS__)) + + An example of using the config header in light of the above: + + .. code:: c + + // NOTE: The following macros are only defined for demonstration purposes only. + // NumPy already has a collections of macros located at + // numpy/numpy/core/src/common/npy_cpu_dispatch.h, that covers all dispatching + // and declarations scenarios. + + #include "numpy/npy_cpu_features.h" // NPY_CPU_HAVE + #include "numpy/utils.h" // NPY_CAT, NPY_EXPAND + + // An example for setting a macro that calls all the exported symbols at once + // after checking if they're supported by the running machine. + #define DISPATCH_CALL_ALL(FN, ARGS) \ + NPY__CPU_DISPATCH_CALL(NPY_CPU_HAVE, DISPATCH_CALL_ALL_CB, FN, ARGS) \ + NPY__CPU_DISPATCH_BASELINE_CALL(DISPATCH_CALL_BASELINE_ALL_CB, FN, ARGS) + // The preprocessor callbacks. + // The same suffixes as we define it in the dispatch-able source. + #define DISPATCH_CALL_ALL_CB(CHECK, TARGET_NAME, FN, ARGS) \ + if (CHECK) { NPY_CAT(NPY_CAT(FN, _), TARGET_NAME) ARGS; } + #define DISPATCH_CALL_BASELINE_ALL_CB(FN, ARGS) \ + FN NPY_EXPAND(ARGS); + + // An example for setting a macro that calls the exported symbols of highest + // interest optimization, after checking if they're supported by the running machine. + #define DISPATCH_CALL_HIGH(FN, ARGS) \ + if (0) {} \ + NPY__CPU_DISPATCH_CALL(NPY_CPU_HAVE, DISPATCH_CALL_HIGH_CB, FN, ARGS) \ + NPY__CPU_DISPATCH_BASELINE_CALL(DISPATCH_CALL_BASELINE_HIGH_CB, FN, ARGS) + // The preprocessor callbacks + // The same suffixes as we define it in the dispatch-able source. + #define DISPATCH_CALL_HIGH_CB(CHECK, TARGET_NAME, FN, ARGS) \ + else if (CHECK) { NPY_CAT(NPY_CAT(FN, _), TARGET_NAME) ARGS; } + #define DISPATCH_CALL_BASELINE_HIGH_CB(FN, ARGS) \ + else { FN NPY_EXPAND(ARGS); } + + // NumPy has a macro called 'NPY_CPU_DISPATCH_DECLARE' can be used + // for forward declrations any kind of prototypes based on + // 'NPY__CPU_DISPATCH_CALL' and 'NPY__CPU_DISPATCH_BASELINE_CALL'. + // However in this example, we just handle it manually. + void simd_whoami(const char *extra_info); + void simd_whoami_AVX512F(const char *extra_info); + void simd_whoami_SSE41(const char *extra_info); + + void trigger_me(void) + { + // bring the auto-gernreated config header + // which contains config macros 'NPY__CPU_DISPATCH_CALL' and + // 'NPY__CPU_DISPATCH_BASELINE_CALL'. + // it highely recomaned to include the config header before exectuing + // the dispatching macros in case if there's another header in the scope. + #include "hello.dispatch.h" + DISPATCH_CALL_ALL(simd_whoami, ("all")) + DISPATCH_CALL_HIGH(simd_whoami, ("the highest interest")) + // An example of including multiple config headers in the same source + // #include "hello2.dispatch.h" + // DISPATCH_CALL_HIGH(another_function, ("the highest interest")) + } + + +Dive into the CPU dispatcher +============================ + +The baseline +~~~~~~~~~~~~ + +Dispatcher +~~~~~~~~~~ + +Groups and Policies +~~~~~~~~~~~~~~~~~~~ + +Examples +~~~~~~~~ + +Report and Trace +~~~~~~~~~~~~~~~~ + + +.. _`Universal Intrinsics`: https://numpy.org/neps/nep-0038-SIMD-optimizations.html diff --git a/doc/source/reference/testing.rst b/doc/source/reference/testing.rst new file mode 100644 index 000000000000..72780dd44aac --- /dev/null +++ b/doc/source/reference/testing.rst @@ -0,0 +1,7 @@ +.. _testing-guidelines: + +Testing Guidelines +================== + +.. include:: ../../TESTS.rst.txt + :start-line: 6 diff --git a/doc/source/reference/typing.rst b/doc/source/reference/typing.rst new file mode 100644 index 000000000000..c948bc4be946 --- /dev/null +++ b/doc/source/reference/typing.rst @@ -0,0 +1,2 @@ +.. _typing: +.. automodule:: numpy.typing diff --git a/doc/source/reference/ufuncs.rst b/doc/source/reference/ufuncs.rst index 3cc956887569..6ace5b233279 100644 --- a/doc/source/reference/ufuncs.rst +++ b/doc/source/reference/ufuncs.rst @@ -1,301 +1,31 @@ .. sectionauthor:: adapted from "Guide to NumPy" by Travis E. Oliphant +.. currentmodule:: numpy + .. _ufuncs: ************************************ Universal functions (:class:`ufunc`) ************************************ -.. note: XXX: section might need to be made more reference-guideish... - -.. currentmodule:: numpy - -.. index: ufunc, universal function, arithmetic, operation +.. seealso:: :ref:`ufuncs-basics` A universal function (or :term:`ufunc` for short) is a function that -operates on :class:`ndarrays <ndarray>` in an element-by-element fashion, +operates on :class:`ndarrays <numpy.ndarray>` in an element-by-element fashion, supporting :ref:`array broadcasting <ufuncs.broadcasting>`, :ref:`type casting <ufuncs.casting>`, and several other standard features. That -is, a ufunc is a ":term:`vectorized`" wrapper for a function that -takes a fixed number of specific inputs and produces a fixed number of -specific outputs. - -In NumPy, universal functions are instances of the -:class:`numpy.ufunc` class. Many of the built-in functions are -implemented in compiled C code. The basic ufuncs operate on scalars, but -there is also a generalized kind for which the basic elements are sub-arrays -(vectors, matrices, etc.), and broadcasting is done over other dimensions. -One can also produce custom :class:`ufunc` instances using the -:func:`frompyfunc` factory function. - - -.. _ufuncs.broadcasting: - -Broadcasting -============ - -.. index:: broadcasting - -Each universal function takes array inputs and produces array outputs -by performing the core function element-wise on the inputs (where an -element is generally a scalar, but can be a vector or higher-order -sub-array for generalized ufuncs). Standard -broadcasting rules are applied so that inputs not sharing exactly the -same shapes can still be usefully operated on. Broadcasting can be -understood by four rules: +is, a ufunc is a ":term:`vectorized <vectorization>`" wrapper for a function +that takes a fixed number of specific inputs and produces a fixed number of +specific outputs. For detailed information on universal functions, see +:ref:`ufuncs-basics`. -1. All input arrays with :attr:`ndim <ndarray.ndim>` smaller than the - input array of largest :attr:`ndim <ndarray.ndim>`, have 1's - prepended to their shapes. - -2. The size in each dimension of the output shape is the maximum of all - the input sizes in that dimension. - -3. An input can be used in the calculation if its size in a particular - dimension either matches the output size in that dimension, or has - value exactly 1. - -4. If an input has a dimension size of 1 in its shape, the first data - entry in that dimension will be used for all calculations along - that dimension. In other words, the stepping machinery of the - :term:`ufunc` will simply not step along that dimension (the - :term:`stride` will be 0 for that dimension). - -Broadcasting is used throughout NumPy to decide how to handle -disparately shaped arrays; for example, all arithmetic operations (``+``, -``-``, ``*``, ...) between :class:`ndarrays <ndarray>` broadcast the -arrays before operation. - -.. _arrays.broadcasting.broadcastable: - -.. index:: broadcastable - -A set of arrays is called ":term:`broadcastable`" to the same shape if -the above rules produce a valid result, *i.e.*, one of the following -is true: - -1. The arrays all have exactly the same shape. - -2. The arrays all have the same number of dimensions and the length of - each dimensions is either a common length or 1. - -3. The arrays that have too few dimensions can have their shapes prepended - with a dimension of length 1 to satisfy property 2. - -.. admonition:: Example - - If ``a.shape`` is (5,1), ``b.shape`` is (1,6), ``c.shape`` is (6,) - and ``d.shape`` is () so that *d* is a scalar, then *a*, *b*, *c*, - and *d* are all broadcastable to dimension (5,6); and - - - *a* acts like a (5,6) array where ``a[:,0]`` is broadcast to the other - columns, - - - *b* acts like a (5,6) array where ``b[0,:]`` is broadcast - to the other rows, - - - *c* acts like a (1,6) array and therefore like a (5,6) array - where ``c[:]`` is broadcast to every row, and finally, - - - *d* acts like a (5,6) array where the single value is repeated. - - -.. _ufuncs.output-type: - -Output type determination -========================= - -The output of the ufunc (and its methods) is not necessarily an -:class:`ndarray`, if all input arguments are not :class:`ndarrays <ndarray>`. -Indeed, if any input defines an :obj:`~class.__array_ufunc__` method, -control will be passed completely to that function, i.e., the ufunc is -`overridden <ufuncs.overrides>`_. - -If none of the inputs overrides the ufunc, then -all output arrays will be passed to the :obj:`~class.__array_prepare__` and -:obj:`~class.__array_wrap__` methods of the input (besides -:class:`ndarrays <ndarray>`, and scalars) that defines it **and** has -the highest :obj:`~class.__array_priority__` of any other input to the -universal function. The default :obj:`~class.__array_priority__` of the -ndarray is 0.0, and the default :obj:`~class.__array_priority__` of a subtype -is 1.0. Matrices have :obj:`~class.__array_priority__` equal to 10.0. - -All ufuncs can also take output arguments. If necessary, output will -be cast to the data-type(s) of the provided output array(s). If a class -with an :obj:`~class.__array__` method is used for the output, results will be -written to the object returned by :obj:`~class.__array__`. Then, if the class -also has an :obj:`~class.__array_prepare__` method, it is called so metadata -may be determined based on the context of the ufunc (the context -consisting of the ufunc itself, the arguments passed to the ufunc, and -the ufunc domain.) The array object returned by -:obj:`~class.__array_prepare__` is passed to the ufunc for computation. -Finally, if the class also has an :obj:`~class.__array_wrap__` method, the returned -:class:`ndarray` result will be passed to that method just before -passing control back to the caller. - -Use of internal buffers -======================= - -.. index:: buffers - -Internally, buffers are used for misaligned data, swapped data, and -data that has to be converted from one data type to another. The size -of internal buffers is settable on a per-thread basis. There can -be up to :math:`2 (n_{\mathrm{inputs}} + n_{\mathrm{outputs}})` -buffers of the specified size created to handle the data from all the -inputs and outputs of a ufunc. The default size of a buffer is -10,000 elements. Whenever buffer-based calculation would be needed, -but all input arrays are smaller than the buffer size, those -misbehaved or incorrectly-typed arrays will be copied before the -calculation proceeds. Adjusting the size of the buffer may therefore -alter the speed at which ufunc calculations of various sorts are -completed. A simple interface for setting this variable is accessible -using the function - -.. autosummary:: - :toctree: generated/ - - setbufsize - - -Error handling +:class:`ufunc` ============== -.. index:: error handling - -Universal functions can trip special floating-point status registers -in your hardware (such as divide-by-zero). If available on your -platform, these registers will be regularly checked during -calculation. Error handling is controlled on a per-thread basis, -and can be configured using the functions - .. autosummary:: :toctree: generated/ - seterr - seterrcall - -.. _ufuncs.casting: - -Casting Rules -============= - -.. index:: - pair: ufunc; casting rules - -.. note:: - - In NumPy 1.6.0, a type promotion API was created to encapsulate the - mechanism for determining output types. See the functions - :func:`result_type`, :func:`promote_types`, and - :func:`min_scalar_type` for more details. - -At the core of every ufunc is a one-dimensional strided loop that -implements the actual function for a specific type combination. When a -ufunc is created, it is given a static list of inner loops and a -corresponding list of type signatures over which the ufunc operates. -The ufunc machinery uses this list to determine which inner loop to -use for a particular case. You can inspect the :attr:`.types -<ufunc.types>` attribute for a particular ufunc to see which type -combinations have a defined inner loop and which output type they -produce (:ref:`character codes <arrays.scalars.character-codes>` are used -in said output for brevity). - -Casting must be done on one or more of the inputs whenever the ufunc -does not have a core loop implementation for the input types provided. -If an implementation for the input types cannot be found, then the -algorithm searches for an implementation with a type signature to -which all of the inputs can be cast "safely." The first one it finds -in its internal list of loops is selected and performed, after all -necessary type casting. Recall that internal copies during ufuncs (even -for casting) are limited to the size of an internal buffer (which is user -settable). - -.. note:: - - Universal functions in NumPy are flexible enough to have mixed type - signatures. Thus, for example, a universal function could be defined - that works with floating-point and integer values. See :func:`ldexp` - for an example. - -By the above description, the casting rules are essentially -implemented by the question of when a data type can be cast "safely" -to another data type. The answer to this question can be determined in -Python with a function call: :func:`can_cast(fromtype, totype) -<can_cast>`. The Figure below shows the results of this call for -the 24 internally supported types on the author's 64-bit system. You -can generate this table for your system with the code given in the Figure. - -.. admonition:: Figure - - Code segment showing the "can cast safely" table for a 32-bit system. - - >>> def print_table(ntypes): - ... print 'X', - ... for char in ntypes: print char, - ... print - ... for row in ntypes: - ... print row, - ... for col in ntypes: - ... print int(np.can_cast(row, col)), - ... print - >>> print_table(np.typecodes['All']) - X ? b h i l q p B H I L Q P e f d g F D G S U V O M m - ? 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - b 0 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 0 0 - h 0 0 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 0 0 - i 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 1 1 0 1 1 1 1 1 1 0 0 - l 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 1 1 0 1 1 1 1 1 1 0 0 - q 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 1 1 0 1 1 1 1 1 1 0 0 - p 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 1 1 0 1 1 1 1 1 1 0 0 - B 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 - H 0 0 0 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 0 0 - I 0 0 0 0 1 1 1 0 0 1 1 1 1 0 0 1 1 0 1 1 1 1 1 1 0 0 - L 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 0 0 - Q 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 0 0 - P 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 1 1 0 1 1 1 1 1 1 0 0 - e 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 0 0 - f 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 0 0 - d 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 1 1 1 1 1 1 0 0 - g 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 1 1 1 1 0 0 - F 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 - D 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 - G 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 0 0 - S 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 0 0 - U 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 - V 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 - O 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 - M 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 - m 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 - - -You should note that, while included in the table for completeness, -the 'S', 'U', and 'V' types cannot be operated on by ufuncs. Also, -note that on a 32-bit system the integer types may have different -sizes, resulting in a slightly altered table. - -Mixed scalar-array operations use a different set of casting rules -that ensure that a scalar cannot "upcast" an array unless the scalar is -of a fundamentally different kind of data (*i.e.*, under a different -hierarchy in the data-type hierarchy) than the array. This rule -enables you to use scalar constants in your code (which, as Python -types, are interpreted accordingly in ufuncs) without worrying about -whether the precision of the scalar constant will cause upcasting on -your large (small precision) array. - - -.. _ufuncs.overrides: - -Overriding Ufunc behavior -========================= - -Classes (including ndarray subclasses) can override how ufuncs act on -them by defining certain special methods. For details, see -:ref:`arrays.classes`. - - -:class:`ufunc` -============== + numpy.ufunc .. _ufuncs.kwargs: @@ -308,145 +38,171 @@ advanced usage and will not typically be used. .. index:: pair: ufunc; keyword arguments -*out* +.. rubric:: *out* + +.. versionadded:: 1.6 + +The first output can be provided as either a positional or a keyword +parameter. Keyword 'out' arguments are incompatible with positional +ones. - .. versionadded:: 1.6 +.. versionadded:: 1.10 - The first output can be provided as either a positional or a keyword - parameter. Keyword 'out' arguments are incompatible with positional - ones. +The 'out' keyword argument is expected to be a tuple with one entry per +output (which can be None for arrays to be allocated by the ufunc). +For ufuncs with a single output, passing a single array (instead of a +tuple holding a single array) is also valid. - .. versionadded:: 1.10 +Passing a single array in the 'out' keyword argument to a ufunc with +multiple outputs is deprecated, and will raise a warning in numpy 1.10, +and an error in a future release. - The 'out' keyword argument is expected to be a tuple with one entry per - output (which can be `None` for arrays to be allocated by the ufunc). - For ufuncs with a single output, passing a single array (instead of a - tuple holding a single array) is also valid. +If 'out' is None (the default), a uninitialized return array is created. +The output array is then filled with the results of the ufunc in the places +that the broadcast 'where' is True. If 'where' is the scalar True (the +default), then this corresponds to the entire output being filled. +Note that outputs not explicitly filled are left with their +uninitialized values. - Passing a single array in the 'out' keyword argument to a ufunc with - multiple outputs is deprecated, and will raise a warning in numpy 1.10, - and an error in a future release. +.. versionadded:: 1.13 - If 'out' is None (the default), a uninitialized return array is created. - The output array is then filled with the results of the ufunc in the places - that the broadcast 'where' is True. If 'where' is the scalar True (the - default), then this corresponds to the entire output being filled. - Note that outputs not explicitly filled are left with their - uninitialized values. +Operations where ufunc input and output operands have memory overlap are +defined to be the same as for equivalent operations where there +is no memory overlap. Operations affected make temporary copies +as needed to eliminate data dependency. As detecting these cases +is computationally expensive, a heuristic is used, which may in rare +cases result in needless temporary copies. For operations where the +data dependency is simple enough for the heuristic to analyze, +temporary copies will not be made even if the arrays overlap, if it +can be deduced copies are not necessary. As an example, +``np.add(a, b, out=a)`` will not involve copies. -*where* +.. rubric:: *where* - .. versionadded:: 1.7 +.. versionadded:: 1.7 - Accepts a boolean array which is broadcast together with the operands. - Values of True indicate to calculate the ufunc at that position, values - of False indicate to leave the value in the output alone. This argument - cannot be used for generalized ufuncs as those take non-scalar input. +Accepts a boolean array which is broadcast together with the operands. +Values of True indicate to calculate the ufunc at that position, values +of False indicate to leave the value in the output alone. This argument +cannot be used for generalized ufuncs as those take non-scalar input. - Note that if an uninitialized return array is created, values of False - will leave those values **uninitialized**. +Note that if an uninitialized return array is created, values of False +will leave those values **uninitialized**. -*axes* +.. rubric:: *axes* - .. versionadded:: 1.15 +.. versionadded:: 1.15 - A list of tuples with indices of axes a generalized ufunc should operate - on. For instance, for a signature of ``(i,j),(j,k)->(i,k)`` appropriate - for matrix multiplication, the base elements are two-dimensional matrices - and these are taken to be stored in the two last axes of each argument. - The corresponding axes keyword would be ``[(-2, -1), (-2, -1), (-2, -1)]``. - For simplicity, for generalized ufuncs that operate on 1-dimensional arrays - (vectors), a single integer is accepted instead of a single-element tuple, - and for generalized ufuncs for which all outputs are scalars, the output - tuples can be omitted. +A list of tuples with indices of axes a generalized ufunc should operate +on. For instance, for a signature of ``(i,j),(j,k)->(i,k)`` appropriate +for matrix multiplication, the base elements are two-dimensional matrices +and these are taken to be stored in the two last axes of each argument. +The corresponding axes keyword would be ``[(-2, -1), (-2, -1), (-2, -1)]``. +For simplicity, for generalized ufuncs that operate on 1-dimensional arrays +(vectors), a single integer is accepted instead of a single-element tuple, +and for generalized ufuncs for which all outputs are scalars, the output +tuples can be omitted. -*axis* +.. rubric:: *axis* - .. versionadded:: 1.15 +.. versionadded:: 1.15 - A single axis over which a generalized ufunc should operate. This is a - short-cut for ufuncs that operate over a single, shared core dimension, - equivalent to passing in ``axes`` with entries of ``(axis,)`` for each - single-core-dimension argument and ``()`` for all others. For instance, - for a signature ``(i),(i)->()``, it is equivalent to passing in - ``axes=[(axis,), (axis,), ()]``. +A single axis over which a generalized ufunc should operate. This is a +short-cut for ufuncs that operate over a single, shared core dimension, +equivalent to passing in ``axes`` with entries of ``(axis,)`` for each +single-core-dimension argument and ``()`` for all others. For instance, +for a signature ``(i),(i)->()``, it is equivalent to passing in +``axes=[(axis,), (axis,), ()]``. -*keepdims* +.. rubric:: *keepdims* - .. versionadded:: 1.15 +.. versionadded:: 1.15 - If this is set to `True`, axes which are reduced over will be left in the - result as a dimension with size one, so that the result will broadcast - correctly against the inputs. This option can only be used for generalized - ufuncs that operate on inputs that all have the same number of core - dimensions and with outputs that have no core dimensions , i.e., with - signatures like ``(i),(i)->()`` or ``(m,m)->()``. If used, the location of - the dimensions in the output can be controlled with ``axes`` and ``axis``. +If this is set to `True`, axes which are reduced over will be left in the +result as a dimension with size one, so that the result will broadcast +correctly against the inputs. This option can only be used for generalized +ufuncs that operate on inputs that all have the same number of core +dimensions and with outputs that have no core dimensions, i.e., with +signatures like ``(i),(i)->()`` or ``(m,m)->()``. If used, the location of +the dimensions in the output can be controlled with ``axes`` and ``axis``. -*casting* +.. rubric:: *casting* - .. versionadded:: 1.6 +.. versionadded:: 1.6 - May be 'no', 'equiv', 'safe', 'same_kind', or 'unsafe'. - See :func:`can_cast` for explanations of the parameter values. +May be 'no', 'equiv', 'safe', 'same_kind', or 'unsafe'. +See :func:`can_cast` for explanations of the parameter values. - Provides a policy for what kind of casting is permitted. For compatibility - with previous versions of NumPy, this defaults to 'unsafe' for numpy < 1.7. - In numpy 1.7 a transition to 'same_kind' was begun where ufuncs produce a - DeprecationWarning for calls which are allowed under the 'unsafe' - rules, but not under the 'same_kind' rules. From numpy 1.10 and - onwards, the default is 'same_kind'. +Provides a policy for what kind of casting is permitted. For compatibility +with previous versions of NumPy, this defaults to 'unsafe' for numpy < 1.7. +In numpy 1.7 a transition to 'same_kind' was begun where ufuncs produce a +DeprecationWarning for calls which are allowed under the 'unsafe' +rules, but not under the 'same_kind' rules. From numpy 1.10 and +onwards, the default is 'same_kind'. -*order* +.. rubric:: *order* - .. versionadded:: 1.6 +.. versionadded:: 1.6 - Specifies the calculation iteration order/memory layout of the output array. - Defaults to 'K'. 'C' means the output should be C-contiguous, 'F' means - F-contiguous, 'A' means F-contiguous if the inputs are F-contiguous and - not also not C-contiguous, C-contiguous otherwise, and 'K' means to match - the element ordering of the inputs as closely as possible. +Specifies the calculation iteration order/memory layout of the output array. +Defaults to 'K'. 'C' means the output should be C-contiguous, 'F' means +F-contiguous, 'A' means F-contiguous if the inputs are F-contiguous and +not also not C-contiguous, C-contiguous otherwise, and 'K' means to match +the element ordering of the inputs as closely as possible. -*dtype* +.. rubric:: *dtype* - .. versionadded:: 1.6 +.. versionadded:: 1.6 - Overrides the dtype of the calculation and output arrays. Similar to - *signature*. +Overrides the DType of the output arrays the same way as the *signature*. +This should ensure a matching precision of the calculation. The exact +calculation DTypes chosen may depend on the ufunc and the inputs may be +cast to this DType to perform the calculation. -*subok* +.. rubric:: *subok* - .. versionadded:: 1.6 +.. versionadded:: 1.6 - Defaults to true. If set to false, the output will always be a strict - array, not a subtype. +Defaults to true. If set to false, the output will always be a strict +array, not a subtype. -*signature* +.. rubric:: *signature* - Either a data-type, a tuple of data-types, or a special signature - string indicating the input and output types of a ufunc. This argument - allows you to provide a specific signature for the 1-d loop to use - in the underlying calculation. If the loop specified does not exist - for the ufunc, then a TypeError is raised. Normally, a suitable loop is - found automatically by comparing the input types with what is - available and searching for a loop with data-types to which all inputs - can be cast safely. This keyword argument lets you bypass that - search and choose a particular loop. A list of available signatures is - provided by the **types** attribute of the ufunc object. For backwards - compatibility this argument can also be provided as *sig*, although - the long form is preferred. Note that this should not be confused with - the generalized ufunc :ref:`signature <details-of-signature>` that is - stored in the **signature** attribute of the of the ufunc object. +Either a Dtype, a tuple of DTypes, or a special signature string +indicating the input and output types of a ufunc. -*extobj* +This argument allows the user to specify exact DTypes to be used for the +calculation. Casting will be used as necessary. The actual DType of the +input arrays is not considered unless ``signature`` is ``None`` for +that array. - a list of length 1, 2, or 3 specifying the ufunc buffer-size, the - error mode integer, and the error call-back function. Normally, these - values are looked up in a thread-specific dictionary. Passing them - here circumvents that look up and uses the low-level specification - provided for the error mode. This may be useful, for example, as an - optimization for calculations requiring many ufunc calls on small arrays - in a loop. +When all DTypes are fixed, a specific loop is chosen or an error raised +if no matching loop exists. +If some DTypes are not specified and left ``None``, the behaviour may +depend on the ufunc. +At this time, a list of available signatures is provided by the **types** +attribute of the ufunc. (This list may be missing DTypes not defined +by NumPy.) + +The ``signature`` only specifies the DType class/type. For example, it +can specify that the operation should be ``datetime64`` or ``float64`` +operation. It does not specify the ``datetime64`` time-unit or the +``float64`` byte-order. + +For backwards compatibility this argument can also be provided as *sig*, +although the long form is preferred. Note that this should not be +confused with the generalized ufunc :ref:`signature <details-of-signature>` +that is stored in the **signature** attribute of the of the ufunc object. + +.. rubric:: *extobj* + +A list of length 3 specifying the ufunc buffer-size, the error +mode integer, and the error call-back function. Normally, these +values are looked up in a thread-specific dictionary. Passing them +here circumvents that look up and uses the low-level specification +provided for the error mode. This may be useful, for example, as +an optimization for calculations requiring many ufunc calls on +small arrays in a loop. @@ -485,39 +241,6 @@ possess. None of the attributes can be set. Methods ------- -All ufuncs have four methods. However, these methods only make sense on scalar -ufuncs that take two input arguments and return one output argument. -Attempting to call these methods on other ufuncs will cause a -:exc:`ValueError`. The reduce-like methods all take an *axis* keyword, a *dtype* -keyword, and an *out* keyword, and the arrays must all have dimension >= 1. -The *axis* keyword specifies the axis of the array over which the reduction -will take place (with negative values counting backwards). Generally, it is an -integer, though for :meth:`ufunc.reduce`, it can also be a tuple of `int` to -reduce over several axes at once, or `None`, to reduce over all axes. -The *dtype* keyword allows you to manage a very common problem that arises -when naively using :meth:`ufunc.reduce`. Sometimes you may -have an array of a certain data type and wish to add up all of its -elements, but the result does not fit into the data type of the -array. This commonly happens if you have an array of single-byte -integers. The *dtype* keyword allows you to alter the data type over which -the reduction takes place (and therefore the type of the output). Thus, -you can ensure that the output is a data type with precision large enough -to handle your output. The responsibility of altering the reduce type is -mostly up to you. There is one exception: if no *dtype* is given for a -reduction on the "add" or "multiply" operations, then if the input type is -an integer (or Boolean) data-type and smaller than the size of the -:class:`int_` data type, it will be internally upcast to the :class:`int_` -(or :class:`uint`) data-type. Finally, the *out* keyword allows you to provide -an output array (for single-output ufuncs, which are currently the only ones -supported; for future extension, however, a tuple with a single argument -can be passed in). If *out* is given, the *dtype* argument is ignored. - -Ufuncs also have a fifth method that allows in place operations to be -performed using fancy indexing. No buffering is used on the dimensions where -fancy indexing is used, so the fancy index can list an item more than once and -the operation will be performed on the result of the previous operation for -that item. - .. index:: pair: ufunc; methods @@ -568,6 +291,7 @@ Math operations add subtract multiply + matmul divide logaddexp logaddexp2 @@ -576,6 +300,7 @@ Math operations negative positive power + float_power remainder mod fmod @@ -586,6 +311,7 @@ Math operations sign heaviside conj + conjugate exp exp2 log @@ -606,8 +332,8 @@ Math operations for large calculations. If your arrays are large, complicated expressions can take longer than absolutely necessary due to the creation and (later) destruction of temporary calculation - spaces. For example, the expression ``G = a * b + c`` is equivalent to - ``t1 = A * B; G = T1 + C; del t1``. It will be more quickly executed + spaces. For example, the expression ``G = A * B + C`` is equivalent to + ``T1 = A * B; G = T1 + C; del T1``. It will be more quickly executed as ``G = A * B; add(G, C, G)`` which is the same as ``G = A * B; G += C``. @@ -633,6 +359,8 @@ The ratio of degrees to radians is :math:`180^{\circ}/\pi.` arcsinh arccosh arctanh + degrees + radians deg2rad rad2deg diff --git a/doc/source/release.rst b/doc/source/release.rst index 913db1fab55a..a4a5bde63aeb 100644 --- a/doc/source/release.rst +++ b/doc/source/release.rst @@ -1,40 +1,84 @@ ************* -Release Notes +Release notes ************* -.. include:: ../release/1.15.0-notes.rst -.. include:: ../release/1.14.4-notes.rst -.. include:: ../release/1.14.3-notes.rst -.. include:: ../release/1.14.2-notes.rst -.. include:: ../release/1.14.1-notes.rst -.. include:: ../release/1.14.0-notes.rst -.. include:: ../release/1.13.3-notes.rst -.. include:: ../release/1.13.2-notes.rst -.. include:: ../release/1.13.1-notes.rst -.. include:: ../release/1.13.0-notes.rst -.. include:: ../release/1.12.1-notes.rst -.. include:: ../release/1.12.0-notes.rst -.. include:: ../release/1.11.3-notes.rst -.. include:: ../release/1.11.2-notes.rst -.. include:: ../release/1.11.1-notes.rst -.. include:: ../release/1.11.0-notes.rst -.. include:: ../release/1.10.4-notes.rst -.. include:: ../release/1.10.3-notes.rst -.. include:: ../release/1.10.2-notes.rst -.. include:: ../release/1.10.1-notes.rst -.. include:: ../release/1.10.0-notes.rst -.. include:: ../release/1.9.2-notes.rst -.. include:: ../release/1.9.1-notes.rst -.. include:: ../release/1.9.0-notes.rst -.. include:: ../release/1.8.2-notes.rst -.. include:: ../release/1.8.1-notes.rst -.. include:: ../release/1.8.0-notes.rst -.. include:: ../release/1.7.2-notes.rst -.. include:: ../release/1.7.1-notes.rst -.. include:: ../release/1.7.0-notes.rst -.. include:: ../release/1.6.2-notes.rst -.. include:: ../release/1.6.1-notes.rst -.. include:: ../release/1.6.0-notes.rst -.. include:: ../release/1.5.0-notes.rst -.. include:: ../release/1.4.0-notes.rst -.. include:: ../release/1.3.0-notes.rst +.. toctree:: + :maxdepth: 3 + + 1.22.0 <release/1.22.0-notes> + 1.21.4 <release/1.21.4-notes> + 1.21.3 <release/1.21.3-notes> + 1.21.2 <release/1.21.2-notes> + 1.21.1 <release/1.21.1-notes> + 1.21.0 <release/1.21.0-notes> + 1.20.3 <release/1.20.3-notes> + 1.20.2 <release/1.20.2-notes> + 1.20.1 <release/1.20.1-notes> + 1.20.0 <release/1.20.0-notes> + 1.19.5 <release/1.19.5-notes> + 1.19.4 <release/1.19.4-notes> + 1.19.3 <release/1.19.3-notes> + 1.19.2 <release/1.19.2-notes> + 1.19.1 <release/1.19.1-notes> + 1.19.0 <release/1.19.0-notes> + 1.18.5 <release/1.18.5-notes> + 1.18.4 <release/1.18.4-notes> + 1.18.3 <release/1.18.3-notes> + 1.18.2 <release/1.18.2-notes> + 1.18.1 <release/1.18.1-notes> + 1.18.0 <release/1.18.0-notes> + 1.17.5 <release/1.17.5-notes> + 1.17.4 <release/1.17.4-notes> + 1.17.3 <release/1.17.3-notes> + 1.17.2 <release/1.17.2-notes> + 1.17.1 <release/1.17.1-notes> + 1.17.0 <release/1.17.0-notes> + 1.16.6 <release/1.16.6-notes> + 1.16.5 <release/1.16.5-notes> + 1.16.4 <release/1.16.4-notes> + 1.16.3 <release/1.16.3-notes> + 1.16.2 <release/1.16.2-notes> + 1.16.1 <release/1.16.1-notes> + 1.16.0 <release/1.16.0-notes> + 1.15.4 <release/1.15.4-notes> + 1.15.3 <release/1.15.3-notes> + 1.15.2 <release/1.15.2-notes> + 1.15.1 <release/1.15.1-notes> + 1.15.0 <release/1.15.0-notes> + 1.14.6 <release/1.14.6-notes> + 1.14.5 <release/1.14.5-notes> + 1.14.4 <release/1.14.4-notes> + 1.14.3 <release/1.14.3-notes> + 1.14.2 <release/1.14.2-notes> + 1.14.1 <release/1.14.1-notes> + 1.14.0 <release/1.14.0-notes> + 1.13.3 <release/1.13.3-notes> + 1.13.2 <release/1.13.2-notes> + 1.13.1 <release/1.13.1-notes> + 1.13.0 <release/1.13.0-notes> + 1.12.1 <release/1.12.1-notes> + 1.12.0 <release/1.12.0-notes> + 1.11.3 <release/1.11.3-notes> + 1.11.2 <release/1.11.2-notes> + 1.11.1 <release/1.11.1-notes> + 1.11.0 <release/1.11.0-notes> + 1.10.4 <release/1.10.4-notes> + 1.10.3 <release/1.10.3-notes> + 1.10.2 <release/1.10.2-notes> + 1.10.1 <release/1.10.1-notes> + 1.10.0 <release/1.10.0-notes> + 1.9.2 <release/1.9.2-notes> + 1.9.1 <release/1.9.1-notes> + 1.9.0 <release/1.9.0-notes> + 1.8.2 <release/1.8.2-notes> + 1.8.1 <release/1.8.1-notes> + 1.8.0 <release/1.8.0-notes> + 1.7.2 <release/1.7.2-notes> + 1.7.1 <release/1.7.1-notes> + 1.7.0 <release/1.7.0-notes> + 1.6.2 <release/1.6.2-notes> + 1.6.1 <release/1.6.1-notes> + 1.6.0 <release/1.6.0-notes> + 1.5.0 <release/1.5.0-notes> + 1.4.0 <release/1.4.0-notes> + 1.3.0 <release/1.3.0-notes> diff --git a/doc/release/1.10.0-notes.rst b/doc/source/release/1.10.0-notes.rst similarity index 100% rename from doc/release/1.10.0-notes.rst rename to doc/source/release/1.10.0-notes.rst diff --git a/doc/release/1.10.1-notes.rst b/doc/source/release/1.10.1-notes.rst similarity index 100% rename from doc/release/1.10.1-notes.rst rename to doc/source/release/1.10.1-notes.rst diff --git a/doc/release/1.10.2-notes.rst b/doc/source/release/1.10.2-notes.rst similarity index 100% rename from doc/release/1.10.2-notes.rst rename to doc/source/release/1.10.2-notes.rst diff --git a/doc/release/1.10.3-notes.rst b/doc/source/release/1.10.3-notes.rst similarity index 100% rename from doc/release/1.10.3-notes.rst rename to doc/source/release/1.10.3-notes.rst diff --git a/doc/release/1.10.4-notes.rst b/doc/source/release/1.10.4-notes.rst similarity index 100% rename from doc/release/1.10.4-notes.rst rename to doc/source/release/1.10.4-notes.rst diff --git a/doc/release/1.11.0-notes.rst b/doc/source/release/1.11.0-notes.rst similarity index 99% rename from doc/release/1.11.0-notes.rst rename to doc/source/release/1.11.0-notes.rst index 166502ac5e70..36cd1d65a266 100644 --- a/doc/release/1.11.0-notes.rst +++ b/doc/source/release/1.11.0-notes.rst @@ -85,7 +85,7 @@ times in UTC. By default, creating a datetime64 object from a string or printing it would convert from or to local time:: # old behavior - >>>> np.datetime64('2000-01-01T00:00:00') + >>> np.datetime64('2000-01-01T00:00:00') numpy.datetime64('2000-01-01T00:00:00-0800') # note the timezone offset -08:00 @@ -96,7 +96,7 @@ type is preferred, similar to the ``datetime.datetime`` type in the Python standard library. Accordingly, datetime64 no longer assumes that input is in local time, nor does it print local times:: - >>>> np.datetime64('2000-01-01T00:00:00') + >>> np.datetime64('2000-01-01T00:00:00') numpy.datetime64('2000-01-01T00:00:00') For backwards compatibility, datetime64 still parses timezone offsets, which @@ -200,7 +200,7 @@ New Features * A ``dtype`` parameter has been added to ``np.random.randint`` Random ndarrays of the following types can now be generated: - - ``np.bool``, + - ``np.bool_``, - ``np.int8``, ``np.uint8``, - ``np.int16``, ``np.uint16``, - ``np.int32``, ``np.uint32``, diff --git a/doc/release/1.11.1-notes.rst b/doc/source/release/1.11.1-notes.rst similarity index 100% rename from doc/release/1.11.1-notes.rst rename to doc/source/release/1.11.1-notes.rst diff --git a/doc/release/1.11.2-notes.rst b/doc/source/release/1.11.2-notes.rst similarity index 100% rename from doc/release/1.11.2-notes.rst rename to doc/source/release/1.11.2-notes.rst diff --git a/doc/release/1.11.3-notes.rst b/doc/source/release/1.11.3-notes.rst similarity index 100% rename from doc/release/1.11.3-notes.rst rename to doc/source/release/1.11.3-notes.rst diff --git a/doc/release/1.12.0-notes.rst b/doc/source/release/1.12.0-notes.rst similarity index 100% rename from doc/release/1.12.0-notes.rst rename to doc/source/release/1.12.0-notes.rst diff --git a/doc/release/1.12.1-notes.rst b/doc/source/release/1.12.1-notes.rst similarity index 100% rename from doc/release/1.12.1-notes.rst rename to doc/source/release/1.12.1-notes.rst diff --git a/doc/release/1.13.0-notes.rst b/doc/source/release/1.13.0-notes.rst similarity index 98% rename from doc/release/1.13.0-notes.rst rename to doc/source/release/1.13.0-notes.rst index 4554e53ea371..9da9a99d7585 100644 --- a/doc/release/1.13.0-notes.rst +++ b/doc/source/release/1.13.0-notes.rst @@ -183,11 +183,11 @@ override the behavior of NumPy's ufuncs. This works quite similarly to Python's ``__mul__`` and other binary operation routines. See the documentation for a more detailed description of the implementation and behavior of this new option. The API is provisional, we do not yet guarantee backward compatibility -as modifications may be made pending feedback. See the NEP_ and +as modifications may be made pending feedback. See `NEP 13`_ and documentation_ for more details. -.. _NEP: https://github.com/numpy/numpy/blob/master/doc/neps/ufunc-overrides.rst -.. _documentation: https://github.com/charris/numpy/blob/master/doc/source/reference/arrays.classes.rst +.. _`NEP 13`: http://www.numpy.org/neps/nep-0013-ufunc-overrides.html +.. _documentation: https://github.com/numpy/numpy/blob/master/doc/source/reference/arrays.classes.rst New ``positive`` ufunc ---------------------- @@ -243,7 +243,7 @@ It is similar to Matlab's square bracket notation for creating block matrices. ``isin`` function, improving on ``in1d`` ---------------------------------------- -The new function ``isin`` tests whether each element of an N-dimensonal +The new function ``isin`` tests whether each element of an N-dimensional array is present anywhere within a second array. It is an enhancement of ``in1d`` that preserves the shape of the first array. @@ -275,7 +275,7 @@ In particular ``np.gradient`` can now take: This means that, e.g., it is now possible to do the following:: - >>> f = np.array([[1, 2, 6], [3, 4, 5]], dtype=np.float) + >>> f = np.array([[1, 2, 6], [3, 4, 5]], dtype=np.float_) >>> dx = 2. >>> y = [1., 1.5, 3.5] >>> np.gradient(f, dx, y) diff --git a/doc/release/1.13.1-notes.rst b/doc/source/release/1.13.1-notes.rst similarity index 95% rename from doc/release/1.13.1-notes.rst rename to doc/source/release/1.13.1-notes.rst index 807296a85a2f..88a4bc3dd1d4 100644 --- a/doc/release/1.13.1-notes.rst +++ b/doc/source/release/1.13.1-notes.rst @@ -13,7 +13,7 @@ used with 3.6.0 due to Python bug 29943_. NumPy 1.13.2 will be released shortly after Python 3.6.2 is out to fix that problem. If you are using 3.6.0 the workaround is to upgrade to 3.6.1 or use an earlier Python version. -.. _#29943: https://bugs.python.org/issue29943 +.. _29943: https://bugs.python.org/issue29943 Pull requests merged @@ -21,7 +21,7 @@ Pull requests merged A total of 19 pull requests were merged for this release. * #9240 DOC: BLD: fix lots of Sphinx warnings/errors. -* #9255 Revert "DEP: Raise TypeError for subtract(bool_, bool_)." +* #9255 Revert "DEP: Raise TypeError for subtract(bool, bool)." * #9261 BUG: don't elide into readonly and updateifcopy temporaries for... * #9262 BUG: fix missing keyword rename for common block in numpy.f2py * #9263 BUG: handle resize of 0d array diff --git a/doc/release/1.13.2-notes.rst b/doc/source/release/1.13.2-notes.rst similarity index 100% rename from doc/release/1.13.2-notes.rst rename to doc/source/release/1.13.2-notes.rst diff --git a/doc/release/1.13.3-notes.rst b/doc/source/release/1.13.3-notes.rst similarity index 100% rename from doc/release/1.13.3-notes.rst rename to doc/source/release/1.13.3-notes.rst diff --git a/doc/release/1.14.0-notes.rst b/doc/source/release/1.14.0-notes.rst similarity index 98% rename from doc/release/1.14.0-notes.rst rename to doc/source/release/1.14.0-notes.rst index 0f14f7703d36..346b5af99f89 100644 --- a/doc/release/1.14.0-notes.rst +++ b/doc/source/release/1.14.0-notes.rst @@ -14,11 +14,11 @@ dropping Python 2.7 support in the runup to 2020. The decision has been made to support 2.7 for all releases made in 2018, with the last release being designated a long term release with support for bug fixes extending through 2019. In 2019 support for 2.7 will be dropped in all new releases. More details -can be found in the relevant NEP_. +can be found in `NEP 12`_. This release supports Python 2.7 and 3.4 - 3.6. -.. _NEP: https://github.com/numpy/numpy/blob/master/doc/neps/dropping-python2.7-proposal.rst +.. _`NEP 12`: http://www.numpy.org/neps/nep-0014-dropping-python2.7-proposal.html Highlights @@ -43,7 +43,7 @@ New functions floating-point scalars unambiguously with control of rounding and padding. * ``PyArray_ResolveWritebackIfCopy`` and ``PyArray_SetWritebackIfCopyBase``, - new C-API functions useful in achieving PyPy compatibity. + new C-API functions useful in achieving PyPy compatibility. Deprecations @@ -134,8 +134,8 @@ are marked readonly. In the past, it was possible to get away with:: var_arr = np.asarray(val) val_arr += 1 # now errors, previously changed np.ma.masked.data -``np.ma`` functions producing ``fill_value``s have changed ----------------------------------------------------------- +``np.ma`` functions producing ``fill_value`` s have changed +----------------------------------------------------------- Previously, ``np.ma.default_fill_value`` would return a 0d array, but ``np.ma.minimum_fill_value`` and ``np.ma.maximum_fill_value`` would return a tuple of the fields. Instead, all three methods return a structured ``np.void`` @@ -332,7 +332,7 @@ eliminating their use internally and two new C-API functions, * ``PyArray_SetWritebackIfCopyBase`` * ``PyArray_ResolveWritebackIfCopy``, -have been added together with a complimentary flag, +have been added together with a complementary flag, ``NPY_ARRAY_WRITEBACKIFCOPY``. Using the new functionality also requires that some flags be changed when new arrays are created, to wit: ``NPY_ARRAY_INOUT_ARRAY`` should be replaced by ``NPY_ARRAY_INOUT_ARRAY2`` and diff --git a/doc/release/1.14.1-notes.rst b/doc/source/release/1.14.1-notes.rst similarity index 99% rename from doc/release/1.14.1-notes.rst rename to doc/source/release/1.14.1-notes.rst index 2ed4c3e14b7a..7b95c2e285b9 100644 --- a/doc/release/1.14.1-notes.rst +++ b/doc/source/release/1.14.1-notes.rst @@ -67,7 +67,7 @@ A total of 36 pull requests were merged for this release. * `#10431 <https://github.com/numpy/numpy/pull/10431>`__: REL: Add 1.14.1 release notes template * `#10435 <https://github.com/numpy/numpy/pull/10435>`__: MAINT: Use ValueError for duplicate field names in lookup (backport) * `#10534 <https://github.com/numpy/numpy/pull/10534>`__: BUG: Provide a better error message for out-of-order fields -* `#10536 <https://github.com/numpy/numpy/pull/10536>`__: BUG: Resize bytes_ columns in genfromtxt (backport of #10401) +* `#10536 <https://github.com/numpy/numpy/pull/10536>`__: BUG: Resize bytes columns in genfromtxt (backport of #10401) * `#10537 <https://github.com/numpy/numpy/pull/10537>`__: BUG: multifield-indexing adds padding bytes: revert for 1.14.1 * `#10539 <https://github.com/numpy/numpy/pull/10539>`__: BUG: fix np.save issue with python 2.7.5 * `#10540 <https://github.com/numpy/numpy/pull/10540>`__: BUG: Add missing DECREF in Py2 int() cast diff --git a/doc/release/1.14.2-notes.rst b/doc/source/release/1.14.2-notes.rst similarity index 100% rename from doc/release/1.14.2-notes.rst rename to doc/source/release/1.14.2-notes.rst diff --git a/doc/release/1.14.3-notes.rst b/doc/source/release/1.14.3-notes.rst similarity index 100% rename from doc/release/1.14.3-notes.rst rename to doc/source/release/1.14.3-notes.rst diff --git a/doc/release/1.14.4-notes.rst b/doc/source/release/1.14.4-notes.rst similarity index 97% rename from doc/release/1.14.4-notes.rst rename to doc/source/release/1.14.4-notes.rst index 174094c1cb38..3fb94383b449 100644 --- a/doc/release/1.14.4-notes.rst +++ b/doc/source/release/1.14.4-notes.rst @@ -19,7 +19,7 @@ values are now correct. Note that NumPy will error on import if it detects incorrect float32 `dot` results. This problem has been seen on the Mac when working in the Anaconda -enviroment and is due to a subtle interaction between MKL and PyQt5. It is not +environment and is due to a subtle interaction between MKL and PyQt5. It is not strictly a NumPy problem, but it is best that users be aware of it. See the gh-8577 NumPy issue for more information. diff --git a/doc/source/release/1.14.5-notes.rst b/doc/source/release/1.14.5-notes.rst new file mode 100644 index 000000000000..9a97cc033f50 --- /dev/null +++ b/doc/source/release/1.14.5-notes.rst @@ -0,0 +1,30 @@ +========================== +NumPy 1.14.5 Release Notes +========================== + +This is a bugfix release for bugs reported following the 1.14.4 release. The +most significant fixes are: + +* fixes for compilation errors on alpine and NetBSD + +The Python versions supported in this release are 2.7 and 3.4 - 3.6. The Python +3.6 wheels available from PIP are built with Python 3.6.2 and should be +compatible with all previous versions of Python 3.6. The source releases were +cythonized with Cython 0.28.2 and should work for the upcoming Python 3.7. + +Contributors +============ + +A total of 1 person contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Charles Harris + +Pull requests merged +==================== + +A total of 2 pull requests were merged for this release. + +* `#11274 <https://github.com/numpy/numpy/pull/11274>`__: BUG: Correct use of NPY_UNUSED. +* `#11294 <https://github.com/numpy/numpy/pull/11294>`__: BUG: Remove extra trailing parentheses. + diff --git a/doc/source/release/1.14.6-notes.rst b/doc/source/release/1.14.6-notes.rst new file mode 100644 index 000000000000..ac6a782723b7 --- /dev/null +++ b/doc/source/release/1.14.6-notes.rst @@ -0,0 +1,33 @@ +========================== +NumPy 1.14.6 Release Notes +========================== + +This is a bugfix release for bugs reported following the 1.14.5 release. The +most significant fixes are: + +* Fix for behavior change in ``ma.masked_values(shrink=True)`` +* Fix the new cached allocations machinery to be thread safe. + +The Python versions supported in this release are 2.7 and 3.4 - 3.7. The Python +3.6 wheels on PyPI should be compatible with all Python 3.6 versions. + +Contributors +============ + +A total of 4 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Charles Harris +* Eric Wieser +* Julian Taylor +* Matti Picus + +Pull requests merged +==================== + +A total of 4 pull requests were merged for this release. + +* `#11985 <https://github.com/numpy/numpy/pull/11985>`__: BUG: fix cached allocations without the GIL +* `#11986 <https://github.com/numpy/numpy/pull/11986>`__: BUG: Undo behavior change in ma.masked_values(shrink=True) +* `#11987 <https://github.com/numpy/numpy/pull/11987>`__: BUG: fix refcount leak in PyArray_AdaptFlexibleDType +* `#11995 <https://github.com/numpy/numpy/pull/11995>`__: TST: Add Python 3.7 testing to NumPy 1.14. diff --git a/doc/release/1.15.0-notes.rst b/doc/source/release/1.15.0-notes.rst similarity index 64% rename from doc/release/1.15.0-notes.rst rename to doc/source/release/1.15.0-notes.rst index cc193530c706..2d9d068e5c37 100644 --- a/doc/release/1.15.0-notes.rst +++ b/doc/source/release/1.15.0-notes.rst @@ -2,36 +2,59 @@ NumPy 1.15.0 Release Notes ========================== +NumPy 1.15.0 is a release with an unusual number of cleanups, many deprecations +of old functions, and improvements to many existing functions. Please read the +detailed descriptions below to see if you are affected. + +For testing, we have switched to pytest as a replacement for the no longer +maintained nose framework. The old nose based interface remains for downstream +projects who may still be using it. + +The Python versions supported by this release are 2.7, 3.4-3.7. The wheels are +linked with OpenBLAS v0.3.0, which should fix some of the linalg problems +reported for NumPy 1.14. + Highlights ========== * NumPy has switched to pytest for testing. +* A new `numpy.printoptions` context manager. +* Many improvements to the histogram functions. +* Support for unicode field names in python 2.7. +* Improved support for PyPy. +* Fixes and improvements to `numpy.einsum`. New functions ============= -* `np.gcd` and `np.lcm`, to compute the greatest common divisor and least +* `numpy.gcd` and `numpy.lcm`, to compute the greatest common divisor and least common multiple. -* `np.ma.stack`, the `np.stack` array-joining function generalized to masked - arrays. -* ``quantile`` function, an interface to ``percentile`` without factors of 100 -* ``nanquantile`` function, an interface to ``nanpercentile`` without factors - of 100 -* `np.printoptions`, a context manager that sets print options temporarily +* `numpy.ma.stack`, the `numpy.stack` array-joining function generalized to + masked arrays. + +* `numpy.quantile` function, an interface to ``percentile`` without factors of + 100 + +* `numpy.nanquantile` function, an interface to ``nanpercentile`` without + factors of 100 + +* `numpy.printoptions`, a context manager that sets print options temporarily for the scope of the ``with`` block:: >>> with np.printoptions(precision=2): ... print(np.array([2.0]) / 3) [0.67] -* `np.histogram_bin_edges`, a function to get the edges of the bins used by a histogram - without needing to calculate the histogram. +* `numpy.histogram_bin_edges`, a function to get the edges of the bins used by a + histogram without needing to calculate the histogram. + +* C functions `npy_get_floatstatus_barrier` and `npy_clear_floatstatus_barrier` + have been added to deal with compiler optimization changing the order of + operations. See below for details. -* `npy_get_floatstatus_barrier`` and ``npy_clear_floatstatus_barrier`` have been added to - deal with compiler optimization changing the order of operations. See below for details. Deprecations ============ @@ -39,72 +62,92 @@ Deprecations * Aliases of builtin `pickle` functions are deprecated, in favor of their unaliased ``pickle.<func>`` names: - * `np.loads` - * `np.core.numeric.load` - * `np.core.numeric.loads` - * `np.ma.loads`, `np.ma.dumps` - * `np.ma.load`, `np.ma.dump` - these functions already failed on python 3, - when called with a string. - -* Multidimensional indexing with anything but a tuple is - deprecated. This means that code such as ``ind = [slice(None), 0]``, - ``arr[[slice(None), 0]]`` should be changed to ``arr[tuple(ind)]``. This is - necessary to avoid ambiguity in expressions such as ``arr[[[0, 1], [0, 1]]]`` - which currently is interpreted as ``arr[array([0, 1]), array([0, 1])]``. - In future, this will be interpreted as ``arr[array([[0, 1], [0, 1]])]``. - -* Direct imports from the following modules is deprecated. All testing related - imports should come from `numpy.testing`. - * `np.testing.utils` - * `np.testing.decorators` - * `np.testing.nosetester` - * `np.testing.noseclasses` - * `np.core.umath_tests` - -* Giving a generator to `np.sum` is now deprecated. This was undocumented, but - worked. Previously, it would calculate the sum of the generator expression. - In the future, it might return a different result. Use `np.sum(np.from_iter(generator))` - or the built-in Python `sum` instead. + * `numpy.loads` + * `numpy.core.numeric.load` + * `numpy.core.numeric.loads` + * `numpy.ma.loads`, `numpy.ma.dumps` + * `numpy.ma.load`, `numpy.ma.dump` - these functions already failed on + python 3 when called with a string. + +* Multidimensional indexing with anything but a tuple is deprecated. This means + that the index list in ``ind = [slice(None), 0]; arr[ind]`` should be changed + to a tuple, e.g., ``ind = [slice(None), 0]; arr[tuple(ind)]`` or + ``arr[(slice(None), 0)]``. That change is necessary to avoid ambiguity in + expressions such as ``arr[[[0, 1], [0, 1]]]``, currently interpreted as + ``arr[array([0, 1]), array([0, 1])]``, that will be interpreted + as ``arr[array([[0, 1], [0, 1]])]`` in the future. + +* Imports from the following sub-modules are deprecated, they will be removed + at some future date. + + * `numpy.testing.utils` + * `numpy.testing.decorators` + * `numpy.testing.nosetester` + * `numpy.testing.noseclasses` + * `numpy.core.umath_tests` + +* Giving a generator to `numpy.sum` is now deprecated. This was undocumented + behavior, but worked. Previously, it would calculate the sum of the generator + expression. In the future, it might return a different result. Use + ``np.sum(np.from_iter(generator))`` or the built-in Python ``sum`` instead. * Users of the C-API should call ``PyArrayResolveWriteBackIfCopy`` or ``PyArray_DiscardWritbackIfCopy`` on any array with the ``WRITEBACKIFCOPY`` - flag set, before the array is deallocated. A deprecation warning will be + flag set, before deallocating the array. A deprecation warning will be emitted if those calls are not used when needed. * Users of ``nditer`` should use the nditer object as a context manager anytime one of the iterator operands is writeable, so that numpy can manage writeback semantics, or should call ``it.close()``. A - `RuntimeWarning` will be emitted otherwise in these cases. Users of the C-API - should call ``NpyIter_Close`` before ``NpyIter_Deallocate``. + `RuntimeWarning` may be emitted otherwise in these cases. + +* The ``normed`` argument of ``np.histogram``, deprecated long ago in 1.6.0, + now emits a ``DeprecationWarning``. Future Changes ============== +* NumPy 1.16 will drop support for Python 3.4. +* NumPy 1.17 will drop support for Python 2.7. + Compatibility notes =================== -The ``NpzFile`` returned by ``np.savez`` is now a `collections.abc.Mapping` ---------------------------------------------------------------------------- +Compiled testing modules renamed and made private +------------------------------------------------- +The following compiled modules have been renamed and made private: + +* ``umath_tests`` -> ``_umath_tests`` +* ``test_rational`` -> ``_rational_tests`` +* ``multiarray_tests`` -> ``_multiarray_tests`` +* ``struct_ufunc_test`` -> ``_struct_ufunc_tests`` +* ``operand_flag_tests`` -> ``_operand_flag_tests`` + +The ``umath_tests`` module is still available for backwards compatibility, but +will be removed in the future. + +The ``NpzFile`` returned by ``np.savez`` is now a ``collections.abc.Mapping`` +----------------------------------------------------------------------------- This means it behaves like a readonly dictionary, and has a new ``.values()`` method and ``len()`` implementation. -On python 3, this means that ``.iteritems()``, ``.iterkeys()`` have been +For python 3, this means that ``.iteritems()``, ``.iterkeys()`` have been deprecated, and ``.keys()`` and ``.items()`` now return views and not lists. This is consistent with how the builtin ``dict`` type changed between python 2 and python 3. -Under certain conditions, nditer must be used in a context manager ------------------------------------------------------------------- -When using an nditer with the ``"writeonly"`` or ``"readwrite"`` flags, there -are some circumstances where nditer doesn't actually give you a view onto the +Under certain conditions, ``nditer`` must be used in a context manager +---------------------------------------------------------------------- +When using an `numpy.nditer` with the ``"writeonly"`` or ``"readwrite"`` flags, there +are some circumstances where nditer doesn't actually give you a view of the writable array. Instead, it gives you a copy, and if you make changes to the copy, nditer later writes those changes back into your actual array. Currently, this writeback occurs when the array objects are garbage collected, which makes this API error-prone on CPython and entirely broken on PyPy. Therefore, -``nditer`` should now be used as a context manager whenever using ``nditer`` -with writeable arrays (``with np.nditer(...) as it: ...``). You may also +``nditer`` should now be used as a context manager whenever it is used +with writeable arrays, e.g., ``with np.nditer(...) as it: ...``. You may also explicitly call ``it.close()`` for cases where a context manager is unusable, for instance in generator expressions. @@ -114,8 +157,8 @@ The last nose release was 1.3.7 in June, 2015, and development of that tool has ended, consequently NumPy has now switched to using pytest. The old decorators and nose tools that were previously used by some downstream projects remain available, but will not be maintained. The standard testing utilities, -`assert_almost_equal` and such, are not be affected by this change except for -the nose specific functions `import_nose` and `raises`. Those functions are +``assert_almost_equal`` and such, are not be affected by this change except for +the nose specific functions ``import_nose`` and ``raises``. Those functions are not used in numpy, but are kept for downstream compatibility. Numpy no longer monkey-patches ``ctypes`` with ``__array_interface__`` @@ -125,22 +168,22 @@ types from ``ctypes``. ``np.ma.notmasked_contiguous`` and ``np.ma.flatnotmasked_contiguous`` always return lists ----------------------------------------------------------------------------------------- -This was always the documented behavior, but in reality the result used to be -any of slice, None, or list. - -All downstream users seem to use detect the `None` result from -``flatnotmasked_contiguous`` and replace it with ``[]``. -These callers will continue to work as before. - -``np.squeeze`` now respects the API expectation of objects that do not handle an ``axis`` argument --------------------------------------------------------------------------------------------------- -Prior to version ``1.7.0`` ``np.squeeze`` did not have an ``axis`` argument and all empty axes were removed -by default. After incorporation of an ``axis`` argument, it was possible to selectively squeeze single -or multiple empty axes, but the old API expectation was not respected because the axes could still be -selectively removed (silent success) in an object depending on the old API. The silent success is no -longer possible, and objects expecting the old API are respected. The silent success was prevented -by removing the interception of an otherwise-normal Exception when ``axis`` was provided to an object -using the old API. +This is the documented behavior, but previously the result could be any of +slice, None, or list. + +All downstream users seem to check for the ``None`` result from +``flatnotmasked_contiguous`` and replace it with ``[]``. Those callers will +continue to work as before. + +``np.squeeze`` restores old behavior of objects that cannot handle an ``axis`` argument +--------------------------------------------------------------------------------------- +Prior to version ``1.7.0``, `numpy.squeeze` did not have an ``axis`` argument and +all empty axes were removed by default. The incorporation of an ``axis`` +argument made it possible to selectively squeeze single or multiple empty axes, +but the old API expectation was not respected because axes could still be +selectively removed (silent success) from an object expecting all empty axes to +be removed. That silent, selective removal of empty axes for objects expecting +the old behavior has been fixed and the old behavior restored. unstructured void array's ``.item`` method now returns a bytes object --------------------------------------------------------------------- @@ -149,11 +192,10 @@ This may affect code which assumed the return value was mutable, which is no longer the case. ``copy.copy`` and ``copy.deepcopy`` no longer turn ``masked`` into an array ----------------------------------------------------------------------------- +--------------------------------------------------------------------------- Since ``np.ma.masked`` is a readonly scalar, copying should be a no-op. These functions now behave consistently with ``np.copy()``. - Multifield Indexing of Structured Arrays will still return a copy ----------------------------------------------------------------- The change that multi-field indexing of structured arrays returns a view @@ -161,28 +203,31 @@ instead of a copy is pushed back to 1.16. A new method ``numpy.lib.recfunctions.repack_fields`` has been introduced to help mitigate the effects of this change, which can be used to write code compatible with both numpy 1.15 and 1.16. For more information on how to update code to account -for this future change see "basics/structured arrays/accessing multiple fields" -in the user guide. +for this future change see the "accessing multiple fields" section of the +`user guide <https://docs.scipy.org/doc/numpy/user/basics.rec.html>`__. + C API changes ============= -* ``NpyIter_Close`` has been added and should be called before - ``NpyIter_Deallocate`` to resolve possible writeback-enabled arrays. - -* Functions ``npy_get_floatstatus_barrier`` and ``npy_clear_floatstatus_barrier`` - have been added and should be used in place of the ``npy_get_floatstatus``and - ``npy_clear_status`` functions. Optimizing compilers like GCC 8.1 and Clang - were rearranging the order of operations when the previous functions were - used in the ufunc SIMD functions, resulting in the floatstatus flags being ' - checked before the operation whose status we wanted to check was run. - See `#10339 <https://github.com/numpy/numpy/issues/10370>`__. +New functions ``npy_get_floatstatus_barrier`` and ``npy_clear_floatstatus_barrier`` +----------------------------------------------------------------------------------- +Functions ``npy_get_floatstatus_barrier`` and ``npy_clear_floatstatus_barrier`` +have been added and should be used in place of the ``npy_get_floatstatus``and +``npy_clear_status`` functions. Optimizing compilers like GCC 8.1 and Clang +were rearranging the order of operations when the previous functions were used +in the ufunc SIMD functions, resulting in the floatstatus flags being checked +before the operation whose status we wanted to check was run. See `#10339 +<https://github.com/numpy/numpy/issues/10370>`__. + +Changes to ``PyArray_GetDTypeTransferFunction`` +----------------------------------------------- +``PyArray_GetDTypeTransferFunction`` now defaults to using user-defined +``copyswapn`` / ``copyswap`` for user-defined dtypes. If this causes a +significant performance hit, consider implementing ``copyswapn`` to reflect the +implementation of ``PyArray_GetStridedCopyFn``. See `#10898 +<https://github.com/numpy/numpy/pull/10898>`__. -* ``PyArray_GetDTypeTransferFunction`` now defaults to using user-defined - ``copyswapn`` / ``copyswap`` for user-defined dtypes. If this causes a - significant performance hit, consider implementing ``copyswapn`` to reflect - the implementation of ``PyArray_GetStridedCopyFn``. - See `#10898 <https://github.com/numpy/numpy/pull/10898>`__. New Features ============ @@ -191,7 +236,7 @@ New Features -------------------------------------------------------------------- These compute the greatest common divisor, and lowest common multiple, respectively. These work on all the numpy integer types, as well as the -builtin arbitrary-precision `Decimal` and `long` types. +builtin arbitrary-precision ``Decimal`` and ``long`` types. Support for cross-platform builds for iOS ----------------------------------------- @@ -225,6 +270,22 @@ Added experimental support for the 64-bit RISC-V architecture. Improvements ============ +``np.einsum`` updates +--------------------- +Syncs einsum path optimization tech between `numpy` and `opt_einsum`. In +particular, the `greedy` path has received many enhancements by @jcmgray. A +full list of issues fixed are: + +* Arbitrary memory can be passed into the `greedy` path. Fixes gh-11210. +* The greedy path has been updated to contain more dynamic programming ideas + preventing a large number of duplicate (and expensive) calls that figure out + the actual pair contraction that takes place. Now takes a few seconds on + several hundred input tensors. Useful for matrix product state theories. +* Reworks the broadcasting dot error catching found in gh-11218 gh-10352 to be + a bit earlier in the process. +* Enhances the `can_dot` functionality that previous missed an edge case (part + of gh-11308). + ``np.ufunc.reduce`` and related functions now accept an initial value --------------------------------------------------------------------- ``np.ufunc.reduce``, ``np.sum``, ``np.prod``, ``np.min`` and ``np.max`` all @@ -237,7 +298,7 @@ the reduction with. axis is None, it will flip over all the axes. ``histogram`` and ``histogramdd`` functions have moved to ``np.lib.histograms`` ------------------------------------------------------------------------------- +------------------------------------------------------------------------------- These were originally found in ``np.lib.function_base``. They are still available under their un-scoped ``np.histogram(dd)`` names, and to maintain compatibility, aliased at ``np.lib.function_base.histogram(dd)``. @@ -251,9 +312,9 @@ Previously it would fail when trying to compute a finite range for the data. Since the range is ignored anyway when the bins are given explicitly, this error was needless. -Note that calling `histogram` on NaN values continues to raise the -`RuntimeWarning`s typical of working with nan values, which can be silenced -as usual with `errstate`. +Note that calling ``histogram`` on NaN values continues to raise the +``RuntimeWarning`` s typical of working with nan values, which can be silenced +as usual with ``errstate``. ``histogram`` works on datetime types, when explicit bin edges are given ------------------------------------------------------------------------ @@ -261,33 +322,39 @@ Dates, times, and timedeltas can now be histogrammed. The bin edges must be passed explicitly, and are not yet computed automatically. ``histogram`` "auto" estimator handles limited variance better ------------------------------------------------------------------------- -No longer does an IQR of 0 result in `n_bins=1`, rather the number of bins +-------------------------------------------------------------- +No longer does an IQR of 0 result in ``n_bins=1``, rather the number of bins chosen is related to the data size in this situation. -``histogram`` and ``histogramdd`` return edges matching the float type of the data ----------------------------------------------------------------------------------- -When passed ``float16``, ``np.float32``, or ``np.longdouble`` data, the +The edges returned by `histogram`` and ``histogramdd`` now match the data float type +------------------------------------------------------------------------------------ +When passed ``np.float16``, ``np.float32``, or ``np.longdouble`` data, the returned edges are now of the same dtype. Previously, ``histogram`` would only return the same type if explicit bins were given, and ``histogram`` would produce ``float64`` bins no matter what the inputs. ``histogramdd`` allows explicit ranges to be given in a subset of axes ---------------------------------------------------------------------- -The ``range`` argument of `histogramdd` can now contain ``None`` values to +The ``range`` argument of `numpy.histogramdd` can now contain ``None`` values to indicate that the range for the corresponding axis should be computed from the data. Previously, this could not be specified on a per-axis basis. +The normed arguments of ``histogramdd`` and ``histogram2d`` have been renamed +----------------------------------------------------------------------------- +These arguments are now called ``density``, which is consistent with +``histogram``. The old argument continues to work, but the new name should be +preferred. + ``np.r_`` works with 0d arrays, and ``np.ma.mr_`` works with ``np.ma.masked`` ----------------------------------------------------------------------------- +----------------------------------------------------------------------------- 0d arrays passed to the `r_` and `mr_` concatenation helpers are now treated as though they are arrays of length 1. Previously, passing these was an error. -As a result, ``np.ma.mr_`` now works correctly on the ``masked`` constant. +As a result, `numpy.ma.mr_` now works correctly on the ``masked`` constant. ``np.ptp`` accepts a ``keepdims`` argument, and extended axis tuples -------------------------------------------------------------------- -``np.ptp`` (peak-to-peak) can now work over multiple axes, just like `max` and -`min`. +``np.ptp`` (peak-to-peak) can now work over multiple axes, just like ``np.max`` +and ``np.min``. ``MaskedArray.astype`` now is identical to ``ndarray.astype`` ------------------------------------------------------------- @@ -295,10 +362,10 @@ This means it takes all the same arguments, making more code written for ndarray work for masked array too. Enable AVX2/AVX512 at compile time -------------------------------------------------------------- -Change to simd.inc.src to use AVX2 or AVX512 at compile time. Solving the gap -that if compile numpy for avx2 (or 512) with -march=native, still get the SSE -code for the simd functions even though rest of the code gets AVX2. +---------------------------------- +Change to simd.inc.src to allow use of AVX2 or AVX512 at compile time. Previously +compilation for avx2 (or 512) with -march=native would still use the SSE +code for the simd functions even when the rest of the code got AVX2. ``nan_to_num`` always returns scalars when receiving scalar or 0d inputs ------------------------------------------------------------------------ @@ -324,7 +391,7 @@ As a result of this change, the ``period`` argument can now be used on 0d arrays. Allow dtype field names to be unicode in Python 2 ---------------------------------------------------------------- +------------------------------------------------- Previously ``np.dtype([(u'name', float)])`` would raise a ``TypeError`` in Python 2, as only bytestrings were allowed in field names. Now any unicode string field names will be encoded with the ``ascii`` codec, raising a @@ -433,9 +500,3 @@ is the same as:: ``np.put_along_axis`` acts as the dual operation for writing to these indices within an array. -.. note:: Implementations of ``__array_ufunc__`` should ensure that they can - handle either ``axis`` or ``axes``. In future, we may convert - ``axis`` to ``axes`` before passing it on. - -Changes -======= diff --git a/doc/source/release/1.15.1-notes.rst b/doc/source/release/1.15.1-notes.rst new file mode 100644 index 000000000000..ddb83303ceb6 --- /dev/null +++ b/doc/source/release/1.15.1-notes.rst @@ -0,0 +1,74 @@ +========================== +NumPy 1.15.1 Release Notes +========================== + +This is a bugfix release for bugs and regressions reported following the 1.15.0 +release. + +* The annoying but harmless RuntimeWarning that "numpy.dtype size changed" has + been suppressed. The long standing suppression was lost in the transition to + pytest. +* The update to Cython 0.28.3 exposed a problematic use of a gcc attribute used + to prefer code size over speed in module initialization, possibly resulting in + incorrect compiled code. This has been fixed in latest Cython but has been + disabled here for safety. +* Support for big-endian and ARMv8 architectures has been improved. + +The Python versions supported by this release are 2.7, 3.4-3.7. The wheels are +linked with OpenBLAS v0.3.0, which should fix some of the linalg problems +reported for NumPy 1.14. + + +Compatibility Note +================== + +The NumPy 1.15.x OS X wheels released on PyPI no longer contain 32-bit +binaries. That will also be the case in future releases. See +`#11625 <https://github.com/numpy/numpy/issues/11625>`__ for the related +discussion. Those needing 32-bit support should look elsewhere or build +from source. + + +Contributors +============ + +A total of 7 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Charles Harris +* Chris Billington +* Elliott Sales de Andrade + +* Eric Wieser +* Jeremy Manning + +* Matti Picus +* Ralf Gommers + +Pull requests merged +==================== + +A total of 24 pull requests were merged for this release. + +* `#11647 <https://github.com/numpy/numpy/pull/11647>`__: MAINT: Filter Cython warnings in ``__init__.py`` +* `#11648 <https://github.com/numpy/numpy/pull/11648>`__: BUG: Fix doc source links to unwrap decorators +* `#11657 <https://github.com/numpy/numpy/pull/11657>`__: BUG: Ensure singleton dimensions are not dropped when converting... +* `#11661 <https://github.com/numpy/numpy/pull/11661>`__: BUG: Warn on Nan in minimum,maximum for scalars +* `#11665 <https://github.com/numpy/numpy/pull/11665>`__: BUG: cython sometimes emits invalid gcc attribute +* `#11682 <https://github.com/numpy/numpy/pull/11682>`__: BUG: Fix regression in void_getitem +* `#11698 <https://github.com/numpy/numpy/pull/11698>`__: BUG: Make matrix_power again work for object arrays. +* `#11700 <https://github.com/numpy/numpy/pull/11700>`__: BUG: Add missing PyErr_NoMemory after failing malloc +* `#11719 <https://github.com/numpy/numpy/pull/11719>`__: BUG: Fix undefined functions on big-endian systems. +* `#11720 <https://github.com/numpy/numpy/pull/11720>`__: MAINT: Make einsum optimize default to False. +* `#11746 <https://github.com/numpy/numpy/pull/11746>`__: BUG: Fix regression in loadtxt for bz2 text files in Python 2. +* `#11757 <https://github.com/numpy/numpy/pull/11757>`__: BUG: Revert use of `console_scripts`. +* `#11758 <https://github.com/numpy/numpy/pull/11758>`__: BUG: Fix Fortran kind detection for aarch64 & s390x. +* `#11759 <https://github.com/numpy/numpy/pull/11759>`__: BUG: Fix printing of longdouble on ppc64le. +* `#11760 <https://github.com/numpy/numpy/pull/11760>`__: BUG: Fixes for unicode field names in Python 2 +* `#11761 <https://github.com/numpy/numpy/pull/11761>`__: BUG: Increase required cython version on python 3.7 +* `#11763 <https://github.com/numpy/numpy/pull/11763>`__: BUG: check return value of _buffer_format_string +* `#11775 <https://github.com/numpy/numpy/pull/11775>`__: MAINT: Make assert_array_compare more generic. +* `#11776 <https://github.com/numpy/numpy/pull/11776>`__: TST: Fix urlopen stubbing. +* `#11777 <https://github.com/numpy/numpy/pull/11777>`__: BUG: Fix regression in intersect1d. +* `#11779 <https://github.com/numpy/numpy/pull/11779>`__: BUG: Fix test sensitive to platform byte order. +* `#11781 <https://github.com/numpy/numpy/pull/11781>`__: BUG: Avoid signed overflow in histogram +* `#11785 <https://github.com/numpy/numpy/pull/11785>`__: BUG: Fix pickle and memoryview for datetime64, timedelta64 scalars +* `#11786 <https://github.com/numpy/numpy/pull/11786>`__: BUG: Deprecation triggers segfault diff --git a/doc/source/release/1.15.2-notes.rst b/doc/source/release/1.15.2-notes.rst new file mode 100644 index 000000000000..a3e61fccd4d6 --- /dev/null +++ b/doc/source/release/1.15.2-notes.rst @@ -0,0 +1,45 @@ +========================== +NumPy 1.15.2 Release Notes +========================== + +This is a bugfix release for bugs and regressions reported following the 1.15.1 +release. + +* The matrix PendingDeprecationWarning is now suppressed in pytest 3.8. +* The new cached allocations machinery has been fixed to be thread safe. +* The boolean indexing of subclasses now works correctly. +* A small memory leak in PyArray_AdaptFlexibleDType has been fixed. + +The Python versions supported by this release are 2.7, 3.4-3.7. The wheels are +linked with OpenBLAS v0.3.0, which should fix some of the linalg problems +reported for NumPy 1.14. + +Compatibility Note +================== + +The NumPy 1.15.x OS X wheels released on PyPI no longer contain 32-bit +binaries. That will also be the case in future releases. See +`#11625 <https://github.com/numpy/numpy/issues/11625>`__ for the related +discussion. Those needing 32-bit support should look elsewhere or build +from source. + +Contributors +============ + +A total of 4 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Charles Harris +* Julian Taylor +* Marten van Kerkwijk +* Matti Picus + +Pull requests merged +==================== + +A total of 4 pull requests were merged for this release. + +* `#11902 <https://github.com/numpy/numpy/pull/11902>`__: BUG: Fix matrix PendingDeprecationWarning suppression for pytest... +* `#11981 <https://github.com/numpy/numpy/pull/11981>`__: BUG: fix cached allocations without the GIL for 1.15.x +* `#11982 <https://github.com/numpy/numpy/pull/11982>`__: BUG: fix refcount leak in PyArray_AdaptFlexibleDType +* `#11992 <https://github.com/numpy/numpy/pull/11992>`__: BUG: Ensure boolean indexing of subclasses sets base correctly. diff --git a/doc/source/release/1.15.3-notes.rst b/doc/source/release/1.15.3-notes.rst new file mode 100644 index 000000000000..753eecec98db --- /dev/null +++ b/doc/source/release/1.15.3-notes.rst @@ -0,0 +1,49 @@ +========================== +NumPy 1.15.3 Release Notes +========================== + +This is a bugfix release for bugs and regressions reported following the 1.15.2 +release. The Python versions supported by this release are 2.7, 3.4-3.7. The +wheels are linked with OpenBLAS v0.3.0, which should fix some of the linalg +problems reported for NumPy 1.14. + +Compatibility Note +================== + +The NumPy 1.15.x OS X wheels released on PyPI no longer contain 32-bit +binaries. That will also be the case in future releases. See +`#11625 <https://github.com/numpy/numpy/issues/11625>`__ for the related +discussion. Those needing 32-bit support should look elsewhere or build +from source. + +Contributors +============ + +A total of 7 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Allan Haldane +* Charles Harris +* Jeroen Demeyer +* Kevin Sheppard +* Matthew Bowden + +* Matti Picus +* Tyler Reddy + +Pull requests merged +==================== + +A total of 12 pull requests were merged for this release. + +* `#12080 <https://github.com/numpy/numpy/pull/12080>`__: MAINT: Blacklist some MSVC complex functions. +* `#12083 <https://github.com/numpy/numpy/pull/12083>`__: TST: Add azure CI testing to 1.15.x branch. +* `#12084 <https://github.com/numpy/numpy/pull/12084>`__: BUG: test_path() now uses Path.resolve() +* `#12085 <https://github.com/numpy/numpy/pull/12085>`__: TST, MAINT: Fix some failing tests on azure-pipelines mac and... +* `#12187 <https://github.com/numpy/numpy/pull/12187>`__: BUG: Fix memory leak in mapping.c +* `#12188 <https://github.com/numpy/numpy/pull/12188>`__: BUG: Allow boolean subtract in histogram +* `#12189 <https://github.com/numpy/numpy/pull/12189>`__: BUG: Fix in-place permutation +* `#12190 <https://github.com/numpy/numpy/pull/12190>`__: BUG: limit default for get_num_build_jobs() to 8 +* `#12191 <https://github.com/numpy/numpy/pull/12191>`__: BUG: OBJECT_to_* should check for errors +* `#12192 <https://github.com/numpy/numpy/pull/12192>`__: DOC: Prepare for NumPy 1.15.3 release. +* `#12237 <https://github.com/numpy/numpy/pull/12237>`__: BUG: Fix MaskedArray fill_value type conversion. +* `#12238 <https://github.com/numpy/numpy/pull/12238>`__: TST: Backport azure-pipeline testing fixes for Mac diff --git a/doc/source/release/1.15.4-notes.rst b/doc/source/release/1.15.4-notes.rst new file mode 100644 index 000000000000..033bd58287ba --- /dev/null +++ b/doc/source/release/1.15.4-notes.rst @@ -0,0 +1,38 @@ +========================== +NumPy 1.15.4 Release Notes +========================== + +This is a bugfix release for bugs and regressions reported following the 1.15.3 +release. The Python versions supported by this release are 2.7, 3.4-3.7. The +wheels are linked with OpenBLAS v0.3.0, which should fix some of the linalg +problems reported for NumPy 1.14. + +Compatibility Note +================== + +The NumPy 1.15.x OS X wheels released on PyPI no longer contain 32-bit +binaries. That will also be the case in future releases. See +`#11625 <https://github.com/numpy/numpy/issues/11625>`__ for the related +discussion. Those needing 32-bit support should look elsewhere or build +from source. + +Contributors +============ + +A total of 4 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Charles Harris +* Matti Picus +* Sebastian Berg +* bbbbbbbbba + + +Pull requests merged +==================== + +A total of 4 pull requests were merged for this release. + +* `#12296 <https://github.com/numpy/numpy/pull/12296>`__: BUG: Dealloc cached buffer info +* `#12297 <https://github.com/numpy/numpy/pull/12297>`__: BUG: Fix fill value in masked array '==' and '!=' ops. +* `#12307 <https://github.com/numpy/numpy/pull/12307>`__: DOC: Correct the default value of `optimize` in `numpy.einsum` +* `#12320 <https://github.com/numpy/numpy/pull/12320>`__: REL: Prepare for the NumPy 1.15.4 release diff --git a/doc/source/release/1.16.0-notes.rst b/doc/source/release/1.16.0-notes.rst new file mode 100644 index 000000000000..122f20eba48b --- /dev/null +++ b/doc/source/release/1.16.0-notes.rst @@ -0,0 +1,536 @@ +========================== +NumPy 1.16.0 Release Notes +========================== + +This NumPy release is the last one to support Python 2.7 and will be maintained +as a long term release with bug fixes until 2020. Support for Python 3.4 been +dropped, the supported Python versions are 2.7 and 3.5-3.7. The wheels on PyPI +are linked with OpenBLAS v0.3.4+, which should fix the known threading issues +found in previous OpenBLAS versions. + +Downstream developers building this release should use Cython >= 0.29 and, if +using OpenBLAS, OpenBLAS > v0.3.4. + +This release has seen a lot of refactoring and features many bug fixes, improved +code organization, and better cross platform compatibility. Not all of these +improvements will be visible to users, but they should help make maintenance +easier going forward. + + +Highlights +========== + +* Experimental (opt-in only) support for overriding numpy functions, + see ``__array_function__`` below. + +* The ``matmul`` function is now a ufunc. This provides better + performance and allows overriding with ``__array_ufunc__``. + +* Improved support for the ARM and POWER architectures. + +* Improved support for AIX and PyPy. + +* Improved interop with ctypes. + +* Improved support for PEP 3118. + + + +New functions +============= + +* New functions added to the `numpy.lib.recfuntions` module to ease the + structured assignment changes: + + * ``assign_fields_by_name`` + * ``structured_to_unstructured`` + * ``unstructured_to_structured`` + * ``apply_along_fields`` + * ``require_fields`` + + See the user guide at <https://docs.scipy.org/doc/numpy/user/basics.rec.html> + for more info. + + +New deprecations +================ + +* The type dictionaries `numpy.core.typeNA` and `numpy.core.sctypeNA` are + deprecated. They were buggy and not documented and will be removed in the + 1.18 release. Use`numpy.sctypeDict` instead. + +* The `numpy.asscalar` function is deprecated. It is an alias to the more + powerful `numpy.ndarray.item`, not tested, and fails for scalars. + +* The `numpy.set_array_ops` and `numpy.get_array_ops` functions are deprecated. + As part of `NEP 15`, they have been deprecated along with the C-API functions + :c:func:`PyArray_SetNumericOps` and :c:func:`PyArray_GetNumericOps`. Users + who wish to override the inner loop functions in built-in ufuncs should use + :c:func:`PyUFunc_ReplaceLoopBySignature`. + +* The `numpy.unravel_index` keyword argument ``dims`` is deprecated, use + ``shape`` instead. + +* The `numpy.histogram` ``normed`` argument is deprecated. It was deprecated + previously, but no warning was issued. + +* The ``positive`` operator (``+``) applied to non-numerical arrays is + deprecated. See below for details. + +* Passing an iterator to the stack functions is deprecated + + +Expired deprecations +==================== + +* NaT comparisons now return ``False`` without a warning, finishing a + deprecation cycle begun in NumPy 1.11. + +* ``np.lib.function_base.unique`` was removed, finishing a deprecation cycle + begun in NumPy 1.4. Use `numpy.unique` instead. + +* multi-field indexing now returns views instead of copies, finishing a + deprecation cycle begun in NumPy 1.7. The change was previously attempted in + NumPy 1.14 but reverted until now. + +* ``np.PackageLoader`` and ``np.pkgload`` have been removed. These were + deprecated in 1.10, had no tests, and seem to no longer work in 1.15. + + +Future changes +============== + +* NumPy 1.17 will drop support for Python 2.7. + + +Compatibility notes +=================== + +f2py script on Windows +---------------------- +On Windows, the installed script for running f2py is now an ``.exe`` file +rather than a ``*.py`` file and should be run from the command line as ``f2py`` +whenever the ``Scripts`` directory is in the path. Running ``f2py`` as a module +``python -m numpy.f2py [...]`` will work without path modification in any +version of NumPy. + +NaT comparisons +--------------- +Consistent with the behavior of NaN, all comparisons other than inequality +checks with datetime64 or timedelta64 NaT ("not-a-time") values now always +return ``False``, and inequality checks with NaT now always return ``True``. +This includes comparisons between NaT values. For compatibility with the +old behavior, use ``np.isnat`` to explicitly check for NaT or convert +datetime64/timedelta64 arrays with ``.astype(np.int64)`` before making +comparisons. + +complex64/128 alignment has changed +----------------------------------- +The memory alignment of complex types is now the same as a C-struct composed of +two floating point values, while before it was equal to the size of the type. +For many users (for instance on x64/unix/gcc) this means that complex64 is now +4-byte aligned instead of 8-byte aligned. An important consequence is that +aligned structured dtypes may now have a different size. For instance, +``np.dtype('c8,u1', align=True)`` used to have an itemsize of 16 (on x64/gcc) +but now it is 12. + +More in detail, the complex64 type now has the same alignment as a C-struct +``struct {float r, i;}``, according to the compiler used to compile numpy, and +similarly for the complex128 and complex256 types. + +nd_grid __len__ removal +----------------------- +``len(np.mgrid)`` and ``len(np.ogrid)`` are now considered nonsensical +and raise a ``TypeError``. + +``np.unravel_index`` now accepts ``shape`` keyword argument +----------------------------------------------------------- +Previously, only the ``dims`` keyword argument was accepted +for specification of the shape of the array to be used +for unraveling. ``dims`` remains supported, but is now deprecated. + +multi-field views return a view instead of a copy +------------------------------------------------- +Indexing a structured array with multiple fields, e.g., ``arr[['f1', 'f3']]``, +returns a view into the original array instead of a copy. The returned view +will often have extra padding bytes corresponding to intervening fields in the +original array, unlike before, which will affect code such as +``arr[['f1', 'f3']].view('float64')``. This change has been planned since numpy +1.7. Operations hitting this path have emitted ``FutureWarnings`` since then. +Additional ``FutureWarnings`` about this change were added in 1.12. + +To help users update their code to account for these changes, a number of +functions have been added to the ``numpy.lib.recfunctions`` module which +safely allow such operations. For instance, the code above can be replaced +with ``structured_to_unstructured(arr[['f1', 'f3']], dtype='float64')``. +See the "accessing multiple fields" section of the +`user guide <https://docs.scipy.org/doc/numpy/user/basics.rec.html#accessing-multiple-fields>`__. + + +C API changes +============= + +The :c:data:`NPY_FEATURE_VERSION` was incremented to 0x0000D, due to +the addition of: + +* :c:member:`PyUFuncObject.core_dim_flags` +* :c:member:`PyUFuncObject.core_dim_sizes` +* :c:member:`PyUFuncObject.identity_value` +* :c:func:`PyUFunc_FromFuncAndDataAndSignatureAndIdentity` + + +New Features +============ + +Integrated squared error (ISE) estimator added to ``histogram`` +--------------------------------------------------------------- +This method (``bins='stone'``) for optimizing the bin number is a +generalization of the Scott's rule. The Scott's rule assumes the distribution +is approximately Normal, while the ISE_ is a non-parametric method based on +cross-validation. + +.. _ISE: https://en.wikipedia.org/wiki/Histogram#Minimizing_cross-validation_estimated_squared_error + +``max_rows`` keyword added for ``np.loadtxt`` +--------------------------------------------- +New keyword ``max_rows`` in `numpy.loadtxt` sets the maximum rows of the +content to be read after ``skiprows``, as in `numpy.genfromtxt`. + +modulus operator support added for ``np.timedelta64`` operands +-------------------------------------------------------------- +The modulus (remainder) operator is now supported for two operands +of type ``np.timedelta64``. The operands may have different units +and the return value will match the type of the operands. + + +Improvements +============ + +no-copy pickling of numpy arrays +-------------------------------- +Up to protocol 4, numpy array pickling created 2 spurious copies of the data +being serialized. With pickle protocol 5, and the ``PickleBuffer`` API, a +large variety of numpy arrays can now be serialized without any copy using +out-of-band buffers, and with one less copy using in-band buffers. This +results, for large arrays, in an up to 66% drop in peak memory usage. + +build shell independence +------------------------ +NumPy builds should no longer interact with the host machine +shell directly. ``exec_command`` has been replaced with +``subprocess.check_output`` where appropriate. + +`np.polynomial.Polynomial` classes render in LaTeX in Jupyter notebooks +----------------------------------------------------------------------- +When used in a front-end that supports it, `Polynomial` instances are now +rendered through LaTeX. The current format is experimental, and is subject to +change. + +``randint`` and ``choice`` now work on empty distributions +---------------------------------------------------------- +Even when no elements needed to be drawn, ``np.random.randint`` and +``np.random.choice`` raised an error when the arguments described an empty +distribution. This has been fixed so that e.g. +``np.random.choice([], 0) == np.array([], dtype=float64)``. + +``linalg.lstsq``, ``linalg.qr``, and ``linalg.svd`` now work with empty arrays +------------------------------------------------------------------------------ +Previously, a ``LinAlgError`` would be raised when an empty matrix/empty +matrices (with zero rows and/or columns) is/are passed in. Now outputs of +appropriate shapes are returned. + +Chain exceptions to give better error messages for invalid PEP3118 format strings +--------------------------------------------------------------------------------- +This should help track down problems. + +Einsum optimization path updates and efficiency improvements +------------------------------------------------------------ +Einsum was synchronized with the current upstream work. + +`numpy.angle` and `numpy.expand_dims` now work on ``ndarray`` subclasses +------------------------------------------------------------------------ +In particular, they now work for masked arrays. + +``NPY_NO_DEPRECATED_API`` compiler warning suppression +------------------------------------------------------ +Setting ``NPY_NO_DEPRECATED_API`` to a value of 0 will suppress the current compiler +warnings when the deprecated numpy API is used. + +``np.diff`` Added kwargs prepend and append +------------------------------------------- +New kwargs ``prepend`` and ``append``, allow for values to be inserted on +either end of the differences. Similar to options for `ediff1d`. Now the +inverse of `cumsum` can be obtained easily via ``prepend=0``. + +ARM support updated +------------------- +Support for ARM CPUs has been updated to accommodate 32 and 64 bit targets, +and also big and little endian byte ordering. AARCH32 memory alignment issues +have been addressed. CI testing has been expanded to include AARCH64 targets +via the services of shippable.com. + +Appending to build flags +------------------------ +`numpy.distutils` has always overridden rather than appended to `LDFLAGS` and +other similar such environment variables for compiling Fortran extensions. +Now, if the `NPY_DISTUTILS_APPEND_FLAGS` environment variable is set to 1, the +behavior will be appending. This applied to: `LDFLAGS`, `F77FLAGS`, +`F90FLAGS`, `FREEFLAGS`, `FOPT`, `FDEBUG`, and `FFLAGS`. See gh-11525 for more +details. + +Generalized ufunc signatures now allow fixed-size dimensions +------------------------------------------------------------ +By using a numerical value in the signature of a generalized ufunc, one can +indicate that the given function requires input or output to have dimensions +with the given size. E.g., the signature of a function that converts a polar +angle to a two-dimensional cartesian unit vector would be ``()->(2)``; that +for one that converts two spherical angles to a three-dimensional unit vector +would be ``(),()->(3)``; and that for the cross product of two +three-dimensional vectors would be ``(3),(3)->(3)``. + +Note that to the elementary function these dimensions are not treated any +differently from variable ones indicated with a name starting with a letter; +the loop still is passed the corresponding size, but it can now count on that +size being equal to the fixed one given in the signature. + +Generalized ufunc signatures now allow flexible dimensions +---------------------------------------------------------- +Some functions, in particular numpy's implementation of ``@`` as ``matmul``, +are very similar to generalized ufuncs in that they operate over core +dimensions, but one could not present them as such because they were able to +deal with inputs in which a dimension is missing. To support this, it is now +allowed to postfix a dimension name with a question mark to indicate that the +dimension does not necessarily have to be present. + +With this addition, the signature for ``matmul`` can be expressed as +``(m?,n),(n,p?)->(m?,p?)``. This indicates that if, e.g., the second operand +has only one dimension, for the purposes of the elementary function it will be +treated as if that input has core shape ``(n, 1)``, and the output has the +corresponding core shape of ``(m, 1)``. The actual output array, however, has +the flexible dimension removed, i.e., it will have shape ``(..., m)``. +Similarly, if both arguments have only a single dimension, the inputs will be +presented as having shapes ``(1, n)`` and ``(n, 1)`` to the elementary +function, and the output as ``(1, 1)``, while the actual output array returned +will have shape ``()``. In this way, the signature allows one to use a +single elementary function for four related but different signatures, +``(m,n),(n,p)->(m,p)``, ``(n),(n,p)->(p)``, ``(m,n),(n)->(m)`` and +``(n),(n)->()``. + +``np.clip`` and the ``clip`` method check for memory overlap +------------------------------------------------------------ +The ``out`` argument to these functions is now always tested for memory overlap +to avoid corrupted results when memory overlap occurs. + +New value ``unscaled`` for option ``cov`` in ``np.polyfit`` +----------------------------------------------------------- +A further possible value has been added to the ``cov`` parameter of the +``np.polyfit`` function. With ``cov='unscaled'`` the scaling of the covariance +matrix is disabled completely (similar to setting ``absolute_sigma=True`` in +``scipy.optimize.curve_fit``). This would be useful in occasions, where the +weights are given by 1/sigma with sigma being the (known) standard errors of +(Gaussian distributed) data points, in which case the unscaled matrix is +already a correct estimate for the covariance matrix. + +Detailed docstrings for scalar numeric types +-------------------------------------------- +The ``help`` function, when applied to numeric types such as `numpy.intc`, +`numpy.int_`, and `numpy.longlong`, now lists all of the aliased names for that +type, distinguishing between platform -dependent and -independent aliases. + +``__module__`` attribute now points to public modules +----------------------------------------------------- +The ``__module__`` attribute on most NumPy functions has been updated to refer +to the preferred public module from which to access a function, rather than +the module in which the function happens to be defined. This produces more +informative displays for functions in tools such as IPython, e.g., instead of +``<function 'numpy.core.fromnumeric.sum'>`` you now see +``<function 'numpy.sum'>``. + +Large allocations marked as suitable for transparent hugepages +-------------------------------------------------------------- +On systems that support transparent hugepages over the madvise system call +numpy now marks that large memory allocations can be backed by hugepages which +reduces page fault overhead and can in some fault heavy cases improve +performance significantly. On Linux the setting for huge pages to be used, +`/sys/kernel/mm/transparent_hugepage/enabled`, must be at least `madvise`. +Systems which already have it set to `always` will not see much difference as +the kernel will automatically use huge pages where appropriate. + +Users of very old Linux kernels (~3.x and older) should make sure that +`/sys/kernel/mm/transparent_hugepage/defrag` is not set to `always` to avoid +performance problems due concurrency issues in the memory defragmentation. + +Alpine Linux (and other musl c library distros) support +------------------------------------------------------- +We now default to use `fenv.h` for floating point status error reporting. +Previously we had a broken default that sometimes would not report underflow, +overflow, and invalid floating point operations. Now we can support non-glibc +distributions like Alpine Linux as long as they ship `fenv.h`. + +Speedup ``np.block`` for large arrays +------------------------------------- +Large arrays (greater than ``512 * 512``) now use a blocking algorithm based on +copying the data directly into the appropriate slice of the resulting array. +This results in significant speedups for these large arrays, particularly for +arrays being blocked along more than 2 dimensions. + +``arr.ctypes.data_as(...)`` holds a reference to arr +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Previously the caller was responsible for keeping the array alive for the +lifetime of the pointer. + +Speedup ``np.take`` for read-only arrays +---------------------------------------- +The implementation of ``np.take`` no longer makes an unnecessary copy of the +source array when its ``writeable`` flag is set to ``False``. + +Support path-like objects for more functions +-------------------------------------------- +The ``np.core.records.fromfile`` function now supports ``pathlib.Path`` +and other path-like objects in addition to a file object. Furthermore, the +``np.load`` function now also supports path-like objects when using memory +mapping (``mmap_mode`` keyword argument). + +Better behaviour of ufunc identities during reductions +------------------------------------------------------ +Universal functions have an ``.identity`` which is used when ``.reduce`` is +called on an empty axis. + +As of this release, the logical binary ufuncs, `logical_and`, `logical_or`, +and `logical_xor`, now have ``identity`` s of type `bool`, where previously they +were of type `int`. This restores the 1.14 behavior of getting ``bool`` s when +reducing empty object arrays with these ufuncs, while also keeping the 1.15 +behavior of getting ``int`` s when reducing empty object arrays with arithmetic +ufuncs like ``add`` and ``multiply``. + +Additionally, `logaddexp` now has an identity of ``-inf``, allowing it to be +called on empty sequences, where previously it could not be. + +This is possible thanks to the new +:c:func:`PyUFunc_FromFuncAndDataAndSignatureAndIdentity`, which allows +arbitrary values to be used as identities now. + +Improved conversion from ctypes objects +--------------------------------------- +Numpy has always supported taking a value or type from ``ctypes`` and +converting it into an array or dtype, but only behaved correctly for simpler +types. As of this release, this caveat is lifted - now: + +* The ``_pack_`` attribute of ``ctypes.Structure``, used to emulate C's + ``__attribute__((packed))``, is respected. +* Endianness of all ctypes objects is preserved +* ``ctypes.Union`` is supported +* Non-representable constructs raise exceptions, rather than producing + dangerously incorrect results: + + * Bitfields are no longer interpreted as sub-arrays + * Pointers are no longer replaced with the type that they point to + +A new ``ndpointer.contents`` member +----------------------------------- +This matches the ``.contents`` member of normal ctypes arrays, and can be used +to construct an ``np.array`` around the pointers contents. This replaces +``np.array(some_nd_pointer)``, which stopped working in 1.15. As a side effect +of this change, ``ndpointer`` now supports dtypes with overlapping fields and +padding. + +``matmul`` is now a ``ufunc`` +----------------------------- +`numpy.matmul` is now a ufunc which means that both the function and the +``__matmul__`` operator can now be overridden by ``__array_ufunc__``. Its +implementation has also changed. It uses the same BLAS routines as +`numpy.dot`, ensuring its performance is similar for large matrices. + +Start and stop arrays for ``linspace``, ``logspace`` and ``geomspace`` +---------------------------------------------------------------------- +These functions used to be limited to scalar stop and start values, but can +now take arrays, which will be properly broadcast and result in an output +which has one axis prepended. This can be used, e.g., to obtain linearly +interpolated points between sets of points. + +CI extended with additional services +------------------------------------ +We now use additional free CI services, thanks to the companies that provide: + +* Codecoverage testing via codecov.io +* Arm testing via shippable.com +* Additional test runs on azure pipelines + +These are in addition to our continued use of travis, appveyor (for wheels) and +LGTM + + +Changes +======= + +Comparison ufuncs will now error rather than return NotImplemented +------------------------------------------------------------------ +Previously, comparison ufuncs such as ``np.equal`` would return +`NotImplemented` if their arguments had structured dtypes, to help comparison +operators such as ``__eq__`` deal with those. This is no longer needed, as the +relevant logic has moved to the comparison operators proper (which thus do +continue to return `NotImplemented` as needed). Hence, like all other ufuncs, +the comparison ufuncs will now error on structured dtypes. + +Positive will now raise a deprecation warning for non-numerical arrays +---------------------------------------------------------------------- +Previously, ``+array`` unconditionally returned a copy. Now, it will +raise a ``DeprecationWarning`` if the array is not numerical (i.e., +if ``np.positive(array)`` raises a ``TypeError``. For ``ndarray`` +subclasses that override the default ``__array_ufunc__`` implementation, +the ``TypeError`` is passed on. + +``NDArrayOperatorsMixin`` now implements matrix multiplication +-------------------------------------------------------------- +Previously, ``np.lib.mixins.NDArrayOperatorsMixin`` did not implement the +special methods for Python's matrix multiplication operator (``@``). This has +changed now that ``matmul`` is a ufunc and can be overridden using +``__array_ufunc__``. + +The scaling of the covariance matrix in ``np.polyfit`` is different +------------------------------------------------------------------- +So far, ``np.polyfit`` used a non-standard factor in the scaling of the the +covariance matrix. Namely, rather than using the standard ``chisq/(M-N)``, it +scaled it with ``chisq/(M-N-2)`` where M is the number of data points and N is the +number of parameters. This scaling is inconsistent with other fitting programs +such as e.g. ``scipy.optimize.curve_fit`` and was changed to ``chisq/(M-N)``. + +``maximum`` and ``minimum`` no longer emit warnings +--------------------------------------------------- +As part of code introduced in 1.10, ``float32`` and ``float64`` set invalid +float status when a Nan is encountered in `numpy.maximum` and `numpy.minimum`, +when using SSE2 semantics. This caused a `RuntimeWarning` to sometimes be +emitted. In 1.15 we fixed the inconsistencies which caused the warnings to +become more conspicuous. Now no warnings will be emitted. + +Umath and multiarray c-extension modules merged into a single module +-------------------------------------------------------------------- +The two modules were merged, according to `NEP 15`_. Previously `np.core.umath` +and `np.core.multiarray` were separate c-extension modules. They are now python +wrappers to the single `np.core/_multiarray_math` c-extension module. + +.. _`NEP 15` : http://www.numpy.org/neps/nep-0015-merge-multiarray-umath.html + +``getfield`` validity checks extended +------------------------------------- +`numpy.ndarray.getfield` now checks the dtype and offset arguments to prevent +accessing invalid memory locations. + +NumPy functions now support overrides with ``__array_function__`` +----------------------------------------------------------------- +NumPy has a new experimental mechanism for overriding the implementation of +almost all NumPy functions on non-NumPy arrays by defining an +``__array_function__`` method, as described in `NEP 18`_. + +This feature is not yet been enabled by default, but has been released to +facilitate experimentation by potential users. See the NEP for details on +setting the appropriate environment variable. We expect the NumPy 1.17 release +will enable overrides by default, which will also be more performant due to a +new implementation written in C. + +.. _`NEP 18` : http://www.numpy.org/neps/nep-0018-array-function-protocol.html + +Arrays based off readonly buffers cannot be set ``writeable`` +------------------------------------------------------------- +We now disallow setting the ``writeable`` flag True on arrays created +from ``fromstring(readonly-buffer)``. diff --git a/doc/source/release/1.16.1-notes.rst b/doc/source/release/1.16.1-notes.rst new file mode 100644 index 000000000000..d6fc25b44bb3 --- /dev/null +++ b/doc/source/release/1.16.1-notes.rst @@ -0,0 +1,107 @@ +========================== +NumPy 1.16.1 Release Notes +========================== + +The NumPy 1.16.1 release fixes bugs reported against the 1.16.0 release, and +also backports several enhancements from master that seem appropriate for a +release series that is the last to support Python 2.7. The wheels on PyPI are +linked with OpenBLAS v0.3.4+, which should fix the known threading issues +found in previous OpenBLAS versions. + +Downstream developers building this release should use Cython >= 0.29.2 and, if +using OpenBLAS, OpenBLAS > v0.3.4. + +If you are installing using pip, you may encounter a problem with older +installed versions of NumPy that pip did not delete becoming mixed with the +current version, resulting in an ``ImportError``. That problem is particularly +common on Debian derived distributions due to a modified pip. The fix is to +make sure all previous NumPy versions installed by pip have been removed. See +`#12736 <https://github.com/numpy/numpy/issues/12736>`__ for discussion of the +issue. Note that previously this problem resulted in an ``AttributeError``. + + +Contributors +============ + +A total of 16 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Antoine Pitrou +* Arcesio Castaneda Medina + +* Charles Harris +* Chris Markiewicz + +* Christoph Gohlke +* Christopher J. Markiewicz + +* Daniel Hrisca + +* EelcoPeacs + +* Eric Wieser +* Kevin Sheppard +* Matti Picus +* OBATA Akio + +* Ralf Gommers +* Sebastian Berg +* Stephan Hoyer +* Tyler Reddy + + +Enhancements +============ + +* `#12767 <https://github.com/numpy/numpy/pull/12767>`__: ENH: add mm->q floordiv +* `#12768 <https://github.com/numpy/numpy/pull/12768>`__: ENH: port np.core.overrides to C for speed +* `#12769 <https://github.com/numpy/numpy/pull/12769>`__: ENH: Add np.ctypeslib.as_ctypes_type(dtype), improve `np.ctypeslib.as_ctypes` +* `#12773 <https://github.com/numpy/numpy/pull/12773>`__: ENH: add "max difference" messages to np.testing.assert_array_equal... +* `#12820 <https://github.com/numpy/numpy/pull/12820>`__: ENH: Add mm->qm divmod +* `#12890 <https://github.com/numpy/numpy/pull/12890>`__: ENH: add _dtype_ctype to namespace for freeze analysis + + +Compatibility notes +=================== + +* The changed error message emitted by array comparison testing functions may + affect doctests. See below for detail. + +* Casting from double and single denormals to float16 has been corrected. In + some rare cases, this may result in results being rounded up instead of down, + changing the last bit (ULP) of the result. + + +New Features +============ + +divmod operation is now supported for two ``timedelta64`` operands +------------------------------------------------------------------ +The divmod operator now handles two ``np.timedelta64`` operands, with +type signature ``mm->qm``. + + +Improvements +============ + +Further improvements to ``ctypes`` support in ``np.ctypeslib`` +-------------------------------------------------------------- +A new `numpy.ctypeslib.as_ctypes_type` function has been added, which can be +used to converts a `dtype` into a best-guess `ctypes` type. Thanks to this +new function, `numpy.ctypeslib.as_ctypes` now supports a much wider range of +array types, including structures, booleans, and integers of non-native +endianness. + +Array comparison assertions include maximum differences +------------------------------------------------------- +Error messages from array comparison tests such as +`np.testing.assert_allclose` now include "max absolute difference" and +"max relative difference," in addition to the previous "mismatch" percentage. +This information makes it easier to update absolute and relative error +tolerances. + + +Changes +======= + +``timedelta64 % 0`` behavior adjusted to return ``NaT`` +------------------------------------------------------- +The modulus operation with two ``np.timedelta64`` operands now returns +``NaT`` in the case of division by zero, rather than returning zero + + + diff --git a/doc/source/release/1.16.2-notes.rst b/doc/source/release/1.16.2-notes.rst new file mode 100644 index 000000000000..62b90dc405b0 --- /dev/null +++ b/doc/source/release/1.16.2-notes.rst @@ -0,0 +1,70 @@ +========================== +NumPy 1.16.2 Release Notes +========================== + +NumPy 1.16.2 is a quick release fixing several problems encountered on Windows. +The Python versions supported are 2.7 and 3.5-3.7. The Windows problems +addressed are: + +- DLL load problems for NumPy wheels on Windows, +- distutils command line parsing on Windows. + +There is also a regression fix correcting signed zeros produced by divmod, see +below for details. + +Downstream developers building this release should use Cython >= 0.29.2 and, if +using OpenBLAS, OpenBLAS > v0.3.4. + +If you are installing using pip, you may encounter a problem with older +installed versions of NumPy that pip did not delete becoming mixed with the +current version, resulting in an ``ImportError``. That problem is particularly +common on Debian derived distributions due to a modified pip. The fix is to +make sure all previous NumPy versions installed by pip have been removed. See +`#12736 <https://github.com/numpy/numpy/issues/12736>`__ for discussion of the +issue. + + +Compatibility notes +=================== + +Signed zero when using divmod +----------------------------- +Starting in version 1.12.0, numpy incorrectly returned a negatively signed zero +when using the ``divmod`` and ``floor_divide`` functions when the result was +zero. For example:: + + >>> np.zeros(10)//1 + array([-0., -0., -0., -0., -0., -0., -0., -0., -0., -0.]) + +With this release, the result is correctly returned as a positively signed +zero:: + + >>> np.zeros(10)//1 + array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]) + + +Contributors +============ + +A total of 5 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Charles Harris +* Eric Wieser +* Matti Picus +* Tyler Reddy +* Tony LaTorre + + + +Pull requests merged +==================== + +A total of 7 pull requests were merged for this release. + +* `#12909 <https://github.com/numpy/numpy/pull/12909>`__: TST: fix vmImage dispatch in Azure +* `#12923 <https://github.com/numpy/numpy/pull/12923>`__: MAINT: remove complicated test of multiarray import failure mode +* `#13020 <https://github.com/numpy/numpy/pull/13020>`__: BUG: fix signed zero behavior in npy_divmod +* `#13026 <https://github.com/numpy/numpy/pull/13026>`__: MAINT: Add functions to parse shell-strings in the platform-native... +* `#13028 <https://github.com/numpy/numpy/pull/13028>`__: BUG: Fix regression in parsing of F90 and F77 environment variables +* `#13038 <https://github.com/numpy/numpy/pull/13038>`__: BUG: parse shell escaping in extra_compile_args and extra_link_args +* `#13041 <https://github.com/numpy/numpy/pull/13041>`__: BLD: Windows absolute path DLL loading diff --git a/doc/source/release/1.16.3-notes.rst b/doc/source/release/1.16.3-notes.rst new file mode 100644 index 000000000000..181a7264da75 --- /dev/null +++ b/doc/source/release/1.16.3-notes.rst @@ -0,0 +1,46 @@ +========================== +NumPy 1.16.3 Release Notes +========================== + +The NumPy 1.16.3 release fixes bugs reported against the 1.16.2 release, and +also backports several enhancements from master that seem appropriate for a +release series that is the last to support Python 2.7. The wheels on PyPI are +linked with OpenBLAS v0.3.4+, which should fix the known threading issues +found in previous OpenBLAS versions. + +Downstream developers building this release should use Cython >= 0.29.2 and, +if using OpenBLAS, OpenBLAS > v0.3.4. + +The most noticeable change in this release is that unpickling object arrays +when loading ``*.npy`` or ``*.npz`` files now requires an explicit opt-in. +This backwards incompatible change was made in response to +`CVE-2019-6446 <https://nvd.nist.gov/vuln/detail/CVE-2019-6446>`_. + + +Compatibility notes +=================== + +Unpickling while loading requires explicit opt-in +------------------------------------------------- +The functions ``np.load``, and ``np.lib.format.read_array`` take an +`allow_pickle` keyword which now defaults to ``False`` in response to +`CVE-2019-6446 <https://nvd.nist.gov/vuln/detail/CVE-2019-6446>`_. + + +Improvements +============ + +Covariance in `random.mvnormal` cast to double +---------------------------------------------- +This should make the tolerance used when checking the singular values of the +covariance matrix more meaningful. + + +Changes +======= + +``__array_interface__`` offset now works as documented +------------------------------------------------------ +The interface may use an ``offset`` value that was previously mistakenly +ignored. + diff --git a/doc/source/release/1.16.4-notes.rst b/doc/source/release/1.16.4-notes.rst new file mode 100644 index 000000000000..a236b05c86ae --- /dev/null +++ b/doc/source/release/1.16.4-notes.rst @@ -0,0 +1,94 @@ +========================== +NumPy 1.16.4 Release Notes +========================== + +The NumPy 1.16.4 release fixes bugs reported against the 1.16.3 release, and +also backports several enhancements from master that seem appropriate for a +release series that is the last to support Python 2.7. The wheels on PyPI are +linked with OpenBLAS v0.3.7-dev, which should fix issues on Skylake series +cpus. + +Downstream developers building this release should use Cython >= 0.29.2 and, +if using OpenBLAS, OpenBLAS > v0.3.7. The supported Python versions are 2.7 and +3.5-3.7. + + +New deprecations +================ +Writeable flag of C-API wrapped arrays +-------------------------------------- +When an array is created from the C-API to wrap a pointer to data, the only +indication we have of the read-write nature of the data is the ``writeable`` +flag set during creation. It is dangerous to force the flag to writeable. In +the future it will not be possible to switch the writeable flag to ``True`` +from python. This deprecation should not affect many users since arrays +created in such a manner are very rare in practice and only available through +the NumPy C-API. + + +Compatibility notes +=================== + +Potential changes to the random stream +-------------------------------------- +Due to bugs in the application of log to random floating point numbers, +the stream may change when sampling from ``np.random.beta``, ``np.random.binomial``, +``np.random.laplace``, ``np.random.logistic``, ``np.random.logseries`` or +``np.random.multinomial`` if a 0 is generated in the underlying MT19937 random stream. +There is a 1 in :math:`10^{53}` chance of this occurring, and so the probability that +the stream changes for any given seed is extremely small. If a 0 is encountered in the +underlying generator, then the incorrect value produced (either ``np.inf`` +or ``np.nan``) is now dropped. + + +Changes +======= + +`numpy.lib.recfunctions.structured_to_unstructured` does not squeeze single-field views +--------------------------------------------------------------------------------------- +Previously ``structured_to_unstructured(arr[['a']])`` would produce a squeezed +result inconsistent with ``structured_to_unstructured(arr[['a', b']])``. This +was accidental. The old behavior can be retained with +``structured_to_unstructured(arr[['a']]).squeeze(axis=-1)`` or far more simply, +``arr['a']``. + + +Contributors +============ + +A total of 10 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Charles Harris +* Eric Wieser +* Dennis Zollo + +* Hunter Damron + +* Jingbei Li + +* Kevin Sheppard +* Matti Picus +* Nicola Soranzo + +* Sebastian Berg +* Tyler Reddy + + +Pull requests merged +==================== + +A total of 16 pull requests were merged for this release. + +* `#13392 <https://github.com/numpy/numpy/pull/13392>`__: BUG: Some PyPy versions lack PyStructSequence_InitType2. +* `#13394 <https://github.com/numpy/numpy/pull/13394>`__: MAINT, DEP: Fix deprecated ``assertEquals()`` +* `#13396 <https://github.com/numpy/numpy/pull/13396>`__: BUG: Fix structured_to_unstructured on single-field types (backport) +* `#13549 <https://github.com/numpy/numpy/pull/13549>`__: BLD: Make CI pass again with pytest 4.5 +* `#13552 <https://github.com/numpy/numpy/pull/13552>`__: TST: Register markers in conftest.py. +* `#13559 <https://github.com/numpy/numpy/pull/13559>`__: BUG: Removes ValueError for empty kwargs in arraymultiter_new +* `#13560 <https://github.com/numpy/numpy/pull/13560>`__: BUG: Add TypeError to accepted exceptions in crackfortran. +* `#13561 <https://github.com/numpy/numpy/pull/13561>`__: BUG: Handle subarrays in descr_to_dtype +* `#13562 <https://github.com/numpy/numpy/pull/13562>`__: BUG: Protect generators from log(0.0) +* `#13563 <https://github.com/numpy/numpy/pull/13563>`__: BUG: Always return views from structured_to_unstructured when... +* `#13564 <https://github.com/numpy/numpy/pull/13564>`__: BUG: Catch stderr when checking compiler version +* `#13565 <https://github.com/numpy/numpy/pull/13565>`__: BUG: longdouble(int) does not work +* `#13587 <https://github.com/numpy/numpy/pull/13587>`__: BUG: distutils/system_info.py fix missing subprocess import (#13523) +* `#13620 <https://github.com/numpy/numpy/pull/13620>`__: BUG,DEP: Fix writeable flag setting for arrays without base +* `#13641 <https://github.com/numpy/numpy/pull/13641>`__: MAINT: Prepare for the 1.16.4 release. +* `#13644 <https://github.com/numpy/numpy/pull/13644>`__: BUG: special case object arrays when printing rel-, abs-error diff --git a/doc/source/release/1.16.5-notes.rst b/doc/source/release/1.16.5-notes.rst new file mode 100644 index 000000000000..5bf576fd04f1 --- /dev/null +++ b/doc/source/release/1.16.5-notes.rst @@ -0,0 +1,68 @@ +========================== +NumPy 1.16.5 Release Notes +========================== + +The NumPy 1.16.5 release fixes bugs reported against the 1.16.4 release, and +also backports several enhancements from master that seem appropriate for a +release series that is the last to support Python 2.7. The wheels on PyPI are +linked with OpenBLAS v0.3.7-dev, which should fix errors on Skylake series +cpus. + +Downstream developers building this release should use Cython >= 0.29.2 and, if +using OpenBLAS, OpenBLAS >= v0.3.7. The supported Python versions are 2.7 and +3.5-3.7. + + +Contributors +============ + +A total of 18 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Alexander Shadchin +* Allan Haldane +* Bruce Merry + +* Charles Harris +* Colin Snyder + +* Dan Allan + +* Emile + +* Eric Wieser +* Grey Baker + +* Maksim Shabunin + +* Marten van Kerkwijk +* Matti Picus +* Peter Andreas Entschev + +* Ralf Gommers +* Richard Harris + +* Sebastian Berg +* Sergei Lebedev + +* Stephan Hoyer + +Pull requests merged +==================== + +A total of 23 pull requests were merged for this release. + +* `#13742 <https://github.com/numpy/numpy/pull/13742>`__: ENH: Add project URLs to setup.py +* `#13823 <https://github.com/numpy/numpy/pull/13823>`__: TEST, ENH: fix tests and ctypes code for PyPy +* `#13845 <https://github.com/numpy/numpy/pull/13845>`__: BUG: use npy_intp instead of int for indexing array +* `#13867 <https://github.com/numpy/numpy/pull/13867>`__: TST: Ignore DeprecationWarning during nose imports +* `#13905 <https://github.com/numpy/numpy/pull/13905>`__: BUG: Fix use-after-free in boolean indexing +* `#13933 <https://github.com/numpy/numpy/pull/13933>`__: MAINT/BUG/DOC: Fix errors in _add_newdocs +* `#13984 <https://github.com/numpy/numpy/pull/13984>`__: BUG: fix byte order reversal for datetime64[ns] +* `#13994 <https://github.com/numpy/numpy/pull/13994>`__: MAINT,BUG: Use nbytes to also catch empty descr during allocation +* `#14042 <https://github.com/numpy/numpy/pull/14042>`__: BUG: np.array cleared errors occurred in PyMemoryView_FromObject +* `#14043 <https://github.com/numpy/numpy/pull/14043>`__: BUG: Fixes for Undefined Behavior Sanitizer (UBSan) errors. +* `#14044 <https://github.com/numpy/numpy/pull/14044>`__: BUG: ensure that casting to/from structured is properly checked. +* `#14045 <https://github.com/numpy/numpy/pull/14045>`__: MAINT: fix histogram*d dispatchers +* `#14046 <https://github.com/numpy/numpy/pull/14046>`__: BUG: further fixup to histogram2d dispatcher. +* `#14052 <https://github.com/numpy/numpy/pull/14052>`__: BUG: Replace contextlib.suppress for Python 2.7 +* `#14056 <https://github.com/numpy/numpy/pull/14056>`__: BUG: fix compilation of 3rd party modules with Py_LIMITED_API... +* `#14057 <https://github.com/numpy/numpy/pull/14057>`__: BUG: Fix memory leak in dtype from dict constructor +* `#14058 <https://github.com/numpy/numpy/pull/14058>`__: DOC: Document array_function at a higher level. +* `#14084 <https://github.com/numpy/numpy/pull/14084>`__: BUG, DOC: add new recfunctions to `__all__` +* `#14162 <https://github.com/numpy/numpy/pull/14162>`__: BUG: Remove stray print that causes a SystemError on python 3.7 +* `#14297 <https://github.com/numpy/numpy/pull/14297>`__: TST: Pin pytest version to 5.0.1. +* `#14322 <https://github.com/numpy/numpy/pull/14322>`__: ENH: Enable huge pages in all Linux builds +* `#14346 <https://github.com/numpy/numpy/pull/14346>`__: BUG: fix behavior of structured_to_unstructured on non-trivial... +* `#14382 <https://github.com/numpy/numpy/pull/14382>`__: REL: Prepare for the NumPy 1.16.5 release. diff --git a/doc/source/release/1.16.6-notes.rst b/doc/source/release/1.16.6-notes.rst new file mode 100644 index 000000000000..0aeba3cd3dd9 --- /dev/null +++ b/doc/source/release/1.16.6-notes.rst @@ -0,0 +1,87 @@ +.. currentmodule:: numpy + +========================== +NumPy 1.16.6 Release Notes +========================== + +The NumPy 1.16.6 release fixes bugs reported against the 1.16.5 release, and +also backports several enhancements from master that seem appropriate for a +release series that is the last to support Python 2.7. The wheels on PyPI are +linked with OpenBLAS v0.3.7, which should fix errors on Skylake series +cpus. + +Downstream developers building this release should use Cython >= 0.29.2 and, if +using OpenBLAS, OpenBLAS >= v0.3.7. The supported Python versions are 2.7 and +3.5-3.7. + +Highlights +========== + +- The ``np.testing.utils`` functions have been updated from 1.19.0-dev0. + This improves the function documentation and error messages as well + extending the ``assert_array_compare`` function to additional types. + + +New functions +============= + +Allow matmul (`@` operator) to work with object arrays. +------------------------------------------------------- +This is an enhancement that was added in NumPy 1.17 and seems reasonable to +include in the LTS 1.16 release series. + + +Compatibility notes +=================== + +Fix regression in matmul (`@` operator) for boolean types +--------------------------------------------------------- +Booleans were being treated as integers rather than booleans, +which was a regression from previous behavior. + + +Improvements +============ + +Array comparison assertions include maximum differences +------------------------------------------------------- +Error messages from array comparison tests such as ``testing.assert_allclose`` +now include "max absolute difference" and "max relative difference," in +addition to the previous "mismatch" percentage. This information makes it +easier to update absolute and relative error tolerances. + +Contributors +============ + +A total of 10 people contributed to this release. + +* CakeWithSteak +* Charles Harris +* Chris Burr +* Eric Wieser +* Fernando Saravia +* Lars Grueter +* Matti Picus +* Maxwell Aladago +* Qiming Sun +* Warren Weckesser + +Pull requests merged +==================== + +A total of 14 pull requests were merged for this release. + +* `#14211 <https://github.com/numpy/numpy/pull/14211>`__: BUG: Fix uint-overflow if padding with linear_ramp and negative... +* `#14275 <https://github.com/numpy/numpy/pull/14275>`__: BUG: fixing to allow unpickling of PY3 pickles from PY2 +* `#14340 <https://github.com/numpy/numpy/pull/14340>`__: BUG: Fix misuse of .names and .fields in various places (backport... +* `#14423 <https://github.com/numpy/numpy/pull/14423>`__: BUG: test, fix regression in converting to ctypes. +* `#14434 <https://github.com/numpy/numpy/pull/14434>`__: BUG: Fixed maximum relative error reporting in assert_allclose +* `#14509 <https://github.com/numpy/numpy/pull/14509>`__: BUG: Fix regression in boolean matmul. +* `#14686 <https://github.com/numpy/numpy/pull/14686>`__: BUG: properly define PyArray_DescrCheck +* `#14853 <https://github.com/numpy/numpy/pull/14853>`__: BLD: add 'apt update' to shippable +* `#14854 <https://github.com/numpy/numpy/pull/14854>`__: BUG: Fix _ctypes class circular reference. (#13808) +* `#14856 <https://github.com/numpy/numpy/pull/14856>`__: BUG: Fix `np.einsum` errors on Power9 Linux and z/Linux +* `#14863 <https://github.com/numpy/numpy/pull/14863>`__: BLD: Prevent -flto from optimising long double representation... +* `#14864 <https://github.com/numpy/numpy/pull/14864>`__: BUG: lib: Fix histogram problem with signed integer arrays. +* `#15172 <https://github.com/numpy/numpy/pull/15172>`__: ENH: Backport improvements to testing functions. +* `#15191 <https://github.com/numpy/numpy/pull/15191>`__: REL: Prepare for 1.16.6 release. diff --git a/doc/source/release/1.17.0-notes.rst b/doc/source/release/1.17.0-notes.rst new file mode 100644 index 000000000000..4bdc6105fc1b --- /dev/null +++ b/doc/source/release/1.17.0-notes.rst @@ -0,0 +1,561 @@ +.. currentmodule:: numpy + +========================== +NumPy 1.17.0 Release Notes +========================== + +This NumPy release contains a number of new features that should substantially +improve its performance and usefulness, see Highlights below for a summary. The +Python versions supported are 3.5-3.7, note that Python 2.7 has been dropped. +Python 3.8b2 should work with the released source packages, but there are no +future guarantees. + +Downstream developers should use Cython >= 0.29.11 for Python 3.8 support and +OpenBLAS >= 3.7 (not currently out) to avoid problems on the Skylake +architecture. The NumPy wheels on PyPI are built from the OpenBLAS development +branch in order to avoid those problems. + + +Highlights +========== + +* A new extensible `random` module along with four selectable `random number + generators <random.BitGenerators>` and improved seeding designed for use in parallel + processes has been added. The currently available bit generators are `MT19937 + <random.mt19937.MT19937>`, `PCG64 <random.pcg64.PCG64>`, `Philox + <random.philox.Philox>`, and `SFC64 <random.sfc64.SFC64>`. See below under + New Features. + +* NumPy's `FFT <fft>` implementation was changed from fftpack to pocketfft, + resulting in faster, more accurate transforms and better handling of datasets + of prime length. See below under Improvements. + +* New radix sort and timsort sorting methods. It is currently not possible to + choose which will be used. They are hardwired to the datatype and used + when either ``stable`` or ``mergesort`` is passed as the method. See below + under Improvements. + +* Overriding numpy functions is now possible by default, + see ``__array_function__`` below. + + +New functions +============= + +* `numpy.errstate` is now also a function decorator + + +Deprecations +============ + +`numpy.polynomial` functions warn when passed ``float`` in place of ``int`` +--------------------------------------------------------------------------- +Previously functions in this module would accept ``float`` values provided they +were integral (``1.0``, ``2.0``, etc). For consistency with the rest of numpy, +doing so is now deprecated, and in future will raise a ``TypeError``. + +Similarly, passing a float like ``0.5`` in place of an integer will now raise a +``TypeError`` instead of the previous ``ValueError``. + +Deprecate `numpy.distutils.exec_command` and ``temp_file_name`` +--------------------------------------------------------------- +The internal use of these functions has been refactored and there are better +alternatives. Replace ``exec_command`` with `subprocess.Popen` and +`temp_file_name <numpy.distutils.exec_command>` with `tempfile.mkstemp`. + +Writeable flag of C-API wrapped arrays +-------------------------------------- +When an array is created from the C-API to wrap a pointer to data, the only +indication we have of the read-write nature of the data is the ``writeable`` +flag set during creation. It is dangerous to force the flag to writeable. +In the future it will not be possible to switch the writeable flag to ``True`` +from python. +This deprecation should not affect many users since arrays created in such +a manner are very rare in practice and only available through the NumPy C-API. + +`numpy.nonzero` should no longer be called on 0d arrays +------------------------------------------------------- +The behavior of `numpy.nonzero` on 0d arrays was surprising, making uses of it +almost always incorrect. If the old behavior was intended, it can be preserved +without a warning by using ``nonzero(atleast_1d(arr))`` instead of +``nonzero(arr)``. In a future release, it is most likely this will raise a +``ValueError``. + +Writing to the result of `numpy.broadcast_arrays` will warn +----------------------------------------------------------- + +Commonly `numpy.broadcast_arrays` returns a writeable array with internal +overlap, making it unsafe to write to. A future version will set the +``writeable`` flag to ``False``, and require users to manually set it to +``True`` if they are sure that is what they want to do. Now writing to it will +emit a deprecation warning with instructions to set the ``writeable`` flag +``True``. Note that if one were to inspect the flag before setting it, one +would find it would already be ``True``. Explicitly setting it, though, as one +will need to do in future versions, clears an internal flag that is used to +produce the deprecation warning. To help alleviate confusion, an additional +`FutureWarning` will be emitted when accessing the ``writeable`` flag state to +clarify the contradiction. + +Note that for the C-side buffer protocol such an array will return a +readonly buffer immediately unless a writable buffer is requested. If +a writeable buffer is requested a warning will be given. When using +cython, the ``const`` qualifier should be used with such arrays to avoid +the warning (e.g. ``cdef const double[::1] view``). + + +Future Changes +============== + +Shape-1 fields in dtypes won't be collapsed to scalars in a future version +-------------------------------------------------------------------------- + +Currently, a field specified as ``[(name, dtype, 1)]`` or ``"1type"`` is +interpreted as a scalar field (i.e., the same as ``[(name, dtype)]`` or +``[(name, dtype, ()]``). This now raises a FutureWarning; in a future version, +it will be interpreted as a shape-(1,) field, i.e. the same as ``[(name, +dtype, (1,))]`` or ``"(1,)type"`` (consistently with ``[(name, dtype, n)]`` +/ ``"ntype"`` with ``n>1``, which is already equivalent to ``[(name, dtype, +(n,)]`` / ``"(n,)type"``). + + +Compatibility notes +=================== + +``float16`` subnormal rounding +------------------------------ +Casting from a different floating point precision to ``float16`` used incorrect +rounding in some edge cases. This means in rare cases, subnormal results will +now be rounded up instead of down, changing the last bit (ULP) of the result. + +Signed zero when using divmod +----------------------------- +Starting in version `1.12.0`, numpy incorrectly returned a negatively signed zero +when using the ``divmod`` and ``floor_divide`` functions when the result was +zero. For example:: + + >>> np.zeros(10)//1 + array([-0., -0., -0., -0., -0., -0., -0., -0., -0., -0.]) + +With this release, the result is correctly returned as a positively signed +zero:: + + >>> np.zeros(10)//1 + array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]) + +``MaskedArray.mask`` now returns a view of the mask, not the mask itself +------------------------------------------------------------------------ +Returning the mask itself was unsafe, as it could be reshaped in place which +would violate expectations of the masked array code. The behavior of `mask +<ma.MaskedArray.mask>` is now consistent with `data <ma.MaskedArray.data>`, +which also returns a view. + +The underlying mask can still be accessed with ``._mask`` if it is needed. +Tests that contain ``assert x.mask is not y.mask`` or similar will need to be +updated. + +Do not lookup ``__buffer__`` attribute in `numpy.frombuffer` +------------------------------------------------------------ +Looking up ``__buffer__`` attribute in `numpy.frombuffer` was undocumented and +non-functional. This code was removed. If needed, use +``frombuffer(memoryview(obj), ...)`` instead. + +``out`` is buffered for memory overlaps in `take`, `choose`, `put` +------------------------------------------------------------------ +If the out argument to these functions is provided and has memory overlap with +the other arguments, it is now buffered to avoid order-dependent behavior. + +Unpickling while loading requires explicit opt-in +------------------------------------------------- +The functions `load`, and ``lib.format.read_array`` take an +``allow_pickle`` keyword which now defaults to ``False`` in response to +`CVE-2019-6446 <https://nvd.nist.gov/vuln/detail/CVE-2019-6446>`_. + + +.. currentmodule:: numpy.random + +Potential changes to the random stream in old random module +----------------------------------------------------------- +Due to bugs in the application of ``log`` to random floating point numbers, +the stream may change when sampling from `~RandomState.beta`, `~RandomState.binomial`, +`~RandomState.laplace`, `~RandomState.logistic`, `~RandomState.logseries` or +`~RandomState.multinomial` if a ``0`` is generated in the underlying `MT19937` +random stream. There is a ``1`` in +:math:`10^{53}` chance of this occurring, so the probability that the stream +changes for any given seed is extremely small. If a ``0`` is encountered in the +underlying generator, then the incorrect value produced (either `numpy.inf` or +`numpy.nan`) is now dropped. + +.. currentmodule:: numpy + +`i0` now always returns a result with the same shape as the input +----------------------------------------------------------------- +Previously, the output was squeezed, such that, e.g., input with just a single +element would lead to an array scalar being returned, and inputs with shapes +such as ``(10, 1)`` would yield results that would not broadcast against the +input. + +Note that we generally recommend the SciPy implementation over the numpy one: +it is a proper ufunc written in C, and more than an order of magnitude faster. + +`can_cast` no longer assumes all unsafe casting is allowed +---------------------------------------------------------- +Previously, `can_cast` returned `True` for almost all inputs for +``casting='unsafe'``, even for cases where casting was not possible, such as +from a structured dtype to a regular one. This has been fixed, making it +more consistent with actual casting using, e.g., the `.astype <ndarray.astype>` +method. + +``ndarray.flags.writeable`` can be switched to true slightly more often +----------------------------------------------------------------------- + +In rare cases, it was not possible to switch an array from not writeable +to writeable, although a base array is writeable. This can happen if an +intermediate `ndarray.base` object is writeable. Previously, only the deepest +base object was considered for this decision. However, in rare cases this +object does not have the necessary information. In that case switching to +writeable was never allowed. This has now been fixed. + + +C API changes +============= + +dimension or stride input arguments are now passed by ``npy_intp const*`` +------------------------------------------------------------------------- +Previously these function arguments were declared as the more strict +``npy_intp*``, which prevented the caller passing constant data. +This change is backwards compatible, but now allows code like:: + + npy_intp const fixed_dims[] = {1, 2, 3}; + // no longer complains that the const-qualifier is discarded + npy_intp size = PyArray_MultiplyList(fixed_dims, 3); + + +New Features +============ + +.. currentmodule:: numpy.random + +New extensible `numpy.random` module with selectable random number generators +----------------------------------------------------------------------------- +A new extensible `numpy.random` module along with four selectable random number +generators and improved seeding designed for use in parallel processes has been +added. The currently available `Bit Generators` are +`~mt19937.MT19937`, `~pcg64.PCG64`, `~philox.Philox`, and `~sfc64.SFC64`. +``PCG64`` is the new default while ``MT19937`` is retained for backwards +compatibility. Note that the legacy random module is unchanged and is now +frozen, your current results will not change. More information is available in +the :ref:`API change description <new-or-different>` and in the `top-level view +<numpy.random>` documentation. + +.. currentmodule:: numpy + +libFLAME +-------- +Support for building NumPy with the libFLAME linear algebra package as the LAPACK, +implementation, see +`libFLAME <https://www.cs.utexas.edu/~flame/web/libFLAME.html>`_ for details. + +User-defined BLAS detection order +--------------------------------- +`distutils` now uses an environment variable, comma-separated and case +insensitive, to determine the detection order for BLAS libraries. +By default ``NPY_BLAS_ORDER=mkl,blis,openblas,atlas,accelerate,blas``. +However, to force the use of OpenBLAS simply do:: + + NPY_BLAS_ORDER=openblas python setup.py build + +which forces the use of OpenBLAS. +This may be helpful for users which have a MKL installation but wishes to try +out different implementations. + +User-defined LAPACK detection order +----------------------------------- +``numpy.distutils`` now uses an environment variable, comma-separated and case +insensitive, to determine the detection order for LAPACK libraries. +By default ``NPY_LAPACK_ORDER=mkl,openblas,flame,atlas,accelerate,lapack``. +However, to force the use of OpenBLAS simply do:: + + NPY_LAPACK_ORDER=openblas python setup.py build + +which forces the use of OpenBLAS. +This may be helpful for users which have a MKL installation but wishes to try +out different implementations. + +`ufunc.reduce` and related functions now accept a ``where`` mask +---------------------------------------------------------------- +`ufunc.reduce`, `sum`, `prod`, `min`, `max` all +now accept a ``where`` keyword argument, which can be used to tell which +elements to include in the reduction. For reductions that do not have an +identity, it is necessary to also pass in an initial value (e.g., +``initial=np.inf`` for `min`). For instance, the equivalent of +`nansum` would be ``np.sum(a, where=~np.isnan(a))``. + +Timsort and radix sort have replaced mergesort for stable sorting +----------------------------------------------------------------- +Both radix sort and timsort have been implemented and are now used in place of +mergesort. Due to the need to maintain backward compatibility, the sorting +``kind`` options ``"stable"`` and ``"mergesort"`` have been made aliases of +each other with the actual sort implementation depending on the array type. +Radix sort is used for small integer types of 16 bits or less and timsort for +the remaining types. Timsort features improved performance on data containing +already or nearly sorted data and performs like mergesort on random data and +requires :math:`O(n/2)` working space. Details of the timsort algorithm can be +found at `CPython listsort.txt +<https://github.com/python/cpython/blob/3.7/Objects/listsort.txt>`_. + +`packbits` and `unpackbits` accept an ``order`` keyword +------------------------------------------------------- +The ``order`` keyword defaults to ``big``, and will order the **bits** +accordingly. For ``'order=big'`` 3 will become ``[0, 0, 0, 0, 0, 0, 1, 1]``, +and ``[1, 1, 0, 0, 0, 0, 0, 0]`` for ``order=little`` + +`unpackbits` now accepts a ``count`` parameter +---------------------------------------------- +``count`` allows subsetting the number of bits that will be unpacked up-front, +rather than reshaping and subsetting later, making the `packbits` operation +invertible, and the unpacking less wasteful. Counts larger than the number of +available bits add zero padding. Negative counts trim bits off the end instead +of counting from the beginning. None counts implement the existing behavior of +unpacking everything. + +`linalg.svd` and `linalg.pinv` can be faster on hermitian inputs +---------------------------------------------------------------- +These functions now accept a ``hermitian`` argument, matching the one added +to `linalg.matrix_rank` in 1.14.0. + +divmod operation is now supported for two ``timedelta64`` operands +------------------------------------------------------------------ +The divmod operator now handles two ``timedelta64`` operands, with +type signature ``mm->qm``. + +`fromfile` now takes an ``offset`` argument +------------------------------------------- +This function now takes an ``offset`` keyword argument for binary files, +which specifics the offset (in bytes) from the file's current position. +Defaults to ``0``. + +New mode "empty" for `pad` +-------------------------- +This mode pads an array to a desired shape without initializing the new +entries. + +`empty_like` and related functions now accept a ``shape`` argument +------------------------------------------------------------------ +`empty_like`, `full_like`, `ones_like` and `zeros_like` now accept a ``shape`` +keyword argument, which can be used to create a new array +as the prototype, overriding its shape as well. This is particularly useful +when combined with the ``__array_function__`` protocol, allowing the creation +of new arbitrary-shape arrays from NumPy-like libraries when such an array +is used as the prototype. + +Floating point scalars implement ``as_integer_ratio`` to match the builtin float +-------------------------------------------------------------------------------- +This returns a (numerator, denominator) pair, which can be used to construct a +`fractions.Fraction`. + +Structured ``dtype`` objects can be indexed with multiple fields names +---------------------------------------------------------------------- +``arr.dtype[['a', 'b']]`` now returns a dtype that is equivalent to +``arr[['a', 'b']].dtype``, for consistency with +``arr.dtype['a'] == arr['a'].dtype``. + +Like the dtype of structured arrays indexed with a list of fields, this dtype +has the same ``itemsize`` as the original, but only keeps a subset of the fields. + +This means that ``arr[['a', 'b']]`` and ``arr.view(arr.dtype[['a', 'b']])`` are +equivalent. + +``.npy`` files support unicode field names +------------------------------------------ +A new format version of 3.0 has been introduced, which enables structured types +with non-latin1 field names. This is used automatically when needed. + + +Improvements +============ + +Array comparison assertions include maximum differences +------------------------------------------------------- +Error messages from array comparison tests such as +`testing.assert_allclose` now include "max absolute difference" and +"max relative difference," in addition to the previous "mismatch" percentage. +This information makes it easier to update absolute and relative error +tolerances. + +Replacement of the fftpack based `fft` module by the pocketfft library +---------------------------------------------------------------------- +Both implementations have the same ancestor (Fortran77 FFTPACK by Paul N. +Swarztrauber), but pocketfft contains additional modifications which improve +both accuracy and performance in some circumstances. For FFT lengths containing +large prime factors, pocketfft uses Bluestein's algorithm, which maintains +:math:`O(N log N)` run time complexity instead of deteriorating towards +:math:`O(N*N)` for prime lengths. Also, accuracy for real valued FFTs with near +prime lengths has improved and is on par with complex valued FFTs. + +Further improvements to ``ctypes`` support in `numpy.ctypeslib` +--------------------------------------------------------------- +A new `numpy.ctypeslib.as_ctypes_type` function has been added, which can be +used to converts a `dtype` into a best-guess `ctypes` type. Thanks to this +new function, `numpy.ctypeslib.as_ctypes` now supports a much wider range of +array types, including structures, booleans, and integers of non-native +endianness. + +`numpy.errstate` is now also a function decorator +------------------------------------------------- +Currently, if you have a function like:: + + def foo(): + pass + +and you want to wrap the whole thing in `errstate`, you have to rewrite it +like so:: + + def foo(): + with np.errstate(...): + pass + +but with this change, you can do:: + + @np.errstate(...) + def foo(): + pass + +thereby saving a level of indentation + +`numpy.exp` and `numpy.log` speed up for float32 implementation +--------------------------------------------------------------- +float32 implementation of `exp` and `log` now benefit from AVX2/AVX512 +instruction set which are detected during runtime. `exp` has a max ulp +error of 2.52 and `log` has a max ulp error or 3.83. + +Improve performance of `numpy.pad` +---------------------------------- +The performance of the function has been improved for most cases by filling in +a preallocated array with the desired padded shape instead of using +concatenation. + +`numpy.interp` handles infinities more robustly +----------------------------------------------- +In some cases where `interp` would previously return `nan`, it now +returns an appropriate infinity. + +Pathlib support for `fromfile`, `tofile` and `ndarray.dump` +----------------------------------------------------------- +`fromfile`, `ndarray.ndarray.tofile` and `ndarray.dump` now support +the `pathlib.Path` type for the ``file``/``fid`` parameter. + +Specialized `isnan`, `isinf`, and `isfinite` ufuncs for bool and int types +-------------------------------------------------------------------------- +The boolean and integer types are incapable of storing `nan` and `inf` values, +which allows us to provide specialized ufuncs that are up to 250x faster than +the previous approach. + +`isfinite` supports ``datetime64`` and ``timedelta64`` types +----------------------------------------------------------------- +Previously, `isfinite` used to raise a `TypeError` on being used on these +two types. + +New keywords added to `nan_to_num` +---------------------------------- +`nan_to_num` now accepts keywords ``nan``, ``posinf`` and ``neginf`` +allowing the user to define the value to replace the ``nan``, positive and +negative ``np.inf`` values respectively. + +MemoryErrors caused by allocated overly large arrays are more descriptive +------------------------------------------------------------------------- +Often the cause of a MemoryError is incorrect broadcasting, which results in a +very large and incorrect shape. The message of the error now includes this +shape to help diagnose the cause of failure. + +`floor`, `ceil`, and `trunc` now respect builtin magic methods +-------------------------------------------------------------- +These ufuncs now call the ``__floor__``, ``__ceil__``, and ``__trunc__`` +methods when called on object arrays, making them compatible with +`decimal.Decimal` and `fractions.Fraction` objects. + +`quantile` now works on `fraction.Fraction` and `decimal.Decimal` objects +------------------------------------------------------------------------- +In general, this handles object arrays more gracefully, and avoids floating- +point operations if exact arithmetic types are used. + +Support of object arrays in `matmul` +------------------------------------ +It is now possible to use `matmul` (or the ``@`` operator) with object arrays. +For instance, it is now possible to do:: + + from fractions import Fraction + a = np.array([[Fraction(1, 2), Fraction(1, 3)], [Fraction(1, 3), Fraction(1, 2)]]) + b = a @ a + + +Changes +======= + +`median` and `percentile` family of functions no longer warn about ``nan`` +-------------------------------------------------------------------------- +`numpy.median`, `numpy.percentile`, and `numpy.quantile` used to emit a +``RuntimeWarning`` when encountering an `nan`. Since they return the +``nan`` value, the warning is redundant and has been removed. + +``timedelta64 % 0`` behavior adjusted to return ``NaT`` +------------------------------------------------------- +The modulus operation with two ``np.timedelta64`` operands now returns +``NaT`` in the case of division by zero, rather than returning zero + +NumPy functions now always support overrides with ``__array_function__`` +------------------------------------------------------------------------ +NumPy now always checks the ``__array_function__`` method to implement overrides +of NumPy functions on non-NumPy arrays, as described in `NEP 18`_. The feature +was available for testing with NumPy 1.16 if appropriate environment variables +are set, but is now always enabled. + +.. _`NEP 18` : http://www.numpy.org/neps/nep-0018-array-function-protocol.html + +``lib.recfunctions.structured_to_unstructured`` does not squeeze single-field views +----------------------------------------------------------------------------------- +Previously ``structured_to_unstructured(arr[['a']])`` would produce a squeezed +result inconsistent with ``structured_to_unstructured(arr[['a', b']])``. This +was accidental. The old behavior can be retained with +``structured_to_unstructured(arr[['a']]).squeeze(axis=-1)`` or far more simply, +``arr['a']``. + +`clip` now uses a ufunc under the hood +-------------------------------------- +This means that registering clip functions for custom dtypes in C via +``descr->f->fastclip`` is deprecated - they should use the ufunc registration +mechanism instead, attaching to the ``np.core.umath.clip`` ufunc. + +It also means that ``clip`` accepts ``where`` and ``casting`` arguments, +and can be override with ``__array_ufunc__``. + +A consequence of this change is that some behaviors of the old ``clip`` have +been deprecated: + +* Passing ``nan`` to mean "do not clip" as one or both bounds. This didn't work + in all cases anyway, and can be better handled by passing infinities of the + appropriate sign. +* Using "unsafe" casting by default when an ``out`` argument is passed. Using + ``casting="unsafe"`` explicitly will silence this warning. + +Additionally, there are some corner cases with behavior changes: + +* Padding ``max < min`` has changed to be more consistent across dtypes, but + should not be relied upon. +* Scalar ``min`` and ``max`` take part in promotion rules like they do in all + other ufuncs. + +``__array_interface__`` offset now works as documented +------------------------------------------------------ +The interface may use an ``offset`` value that was mistakenly ignored. + +Pickle protocol in `savez` set to 3 for ``force zip64`` flag +----------------------------------------------------------------- +`savez` was not using the ``force_zip64`` flag, which limited the size of +the archive to 2GB. But using the flag requires us to use pickle protocol 3 to +write ``object`` arrays. The protocol used was bumped to 3, meaning the archive +will be unreadable by Python2. + +Structured arrays indexed with non-existent fields raise ``KeyError`` not ``ValueError`` +---------------------------------------------------------------------------------------- +``arr['bad_field']`` on a structured type raises ``KeyError``, for consistency +with ``dict['bad_field']``. diff --git a/doc/source/release/1.17.1-notes.rst b/doc/source/release/1.17.1-notes.rst new file mode 100644 index 000000000000..bd837ee5bd12 --- /dev/null +++ b/doc/source/release/1.17.1-notes.rst @@ -0,0 +1,73 @@ +.. currentmodule:: numpy + +========================== +NumPy 1.17.1 Release Notes +========================== + +This release contains a number of fixes for bugs reported against NumPy 1.17.0 +along with a few documentation and build improvements. The Python versions +supported are 3.5-3.7, note that Python 2.7 has been dropped. Python 3.8b3 +should work with the released source packages, but there are no future +guarantees. + +Downstream developers should use Cython >= 0.29.13 for Python 3.8 support and +OpenBLAS >= 3.7 to avoid problems on the Skylake architecture. The NumPy wheels +on PyPI are built from the OpenBLAS development branch in order to avoid those +problems. + + +Contributors +============ + +A total of 17 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Alexander Jung + +* Allan Haldane +* Charles Harris +* Eric Wieser +* Giuseppe Cuccu + +* Hiroyuki V. Yamazaki +* Jérémie du Boisberranger +* Kmol Yuan + +* Matti Picus +* Max Bolingbroke + +* Maxwell Aladago + +* Oleksandr Pavlyk +* Peter Andreas Entschev +* Sergei Lebedev +* Seth Troisi + +* Vladimir Pershin + +* Warren Weckesser + + +Pull requests merged +==================== + +A total of 24 pull requests were merged for this release. + +* `#14156 <https://github.com/numpy/numpy/pull/14156>`__: TST: Allow fuss in testing strided/non-strided exp/log loops +* `#14157 <https://github.com/numpy/numpy/pull/14157>`__: BUG: avx2_scalef_ps must be static +* `#14158 <https://github.com/numpy/numpy/pull/14158>`__: BUG: Remove stray print that causes a SystemError on python 3.7. +* `#14159 <https://github.com/numpy/numpy/pull/14159>`__: BUG: Fix DeprecationWarning in python 3.8. +* `#14160 <https://github.com/numpy/numpy/pull/14160>`__: BLD: Add missing gcd/lcm definitions to npy_math.h +* `#14161 <https://github.com/numpy/numpy/pull/14161>`__: DOC, BUILD: cleanups and fix (again) 'build dist' +* `#14166 <https://github.com/numpy/numpy/pull/14166>`__: TST: Add 3.8-dev to travisCI testing. +* `#14194 <https://github.com/numpy/numpy/pull/14194>`__: BUG: Remove the broken clip wrapper (Backport) +* `#14198 <https://github.com/numpy/numpy/pull/14198>`__: DOC: Fix hermitian argument docs in svd. +* `#14199 <https://github.com/numpy/numpy/pull/14199>`__: MAINT: Workaround for Intel compiler bug leading to failing test +* `#14200 <https://github.com/numpy/numpy/pull/14200>`__: TST: Clean up of test_pocketfft.py +* `#14201 <https://github.com/numpy/numpy/pull/14201>`__: BUG: Make advanced indexing result on read-only subclass writeable... +* `#14236 <https://github.com/numpy/numpy/pull/14236>`__: BUG: Fixed default BitGenerator name +* `#14237 <https://github.com/numpy/numpy/pull/14237>`__: ENH: add c-imported modules for freeze analysis in np.random +* `#14296 <https://github.com/numpy/numpy/pull/14296>`__: TST: Pin pytest version to 5.0.1 +* `#14301 <https://github.com/numpy/numpy/pull/14301>`__: BUG: Fix leak in the f2py-generated module init and `PyMem_Del`... +* `#14302 <https://github.com/numpy/numpy/pull/14302>`__: BUG: Fix formatting error in exception message +* `#14307 <https://github.com/numpy/numpy/pull/14307>`__: MAINT: random: Match type of SeedSequence.pool_size to DEFAULT_POOL_SIZE. +* `#14308 <https://github.com/numpy/numpy/pull/14308>`__: BUG: Fix numpy.random bug in platform detection +* `#14309 <https://github.com/numpy/numpy/pull/14309>`__: ENH: Enable huge pages in all Linux builds +* `#14330 <https://github.com/numpy/numpy/pull/14330>`__: BUG: Fix segfault in `random.permutation(x)` when x is a string. +* `#14338 <https://github.com/numpy/numpy/pull/14338>`__: BUG: don't fail when lexsorting some empty arrays (#14228) +* `#14339 <https://github.com/numpy/numpy/pull/14339>`__: BUG: Fix misuse of .names and .fields in various places (backport... +* `#14345 <https://github.com/numpy/numpy/pull/14345>`__: BUG: fix behavior of structured_to_unstructured on non-trivial... +* `#14350 <https://github.com/numpy/numpy/pull/14350>`__: REL: Prepare 1.17.1 release diff --git a/doc/source/release/1.17.2-notes.rst b/doc/source/release/1.17.2-notes.rst new file mode 100644 index 000000000000..65cdaf903deb --- /dev/null +++ b/doc/source/release/1.17.2-notes.rst @@ -0,0 +1,49 @@ +.. currentmodule:: numpy + +========================== +NumPy 1.17.2 Release Notes +========================== + +This release contains fixes for bugs reported against NumPy 1.17.1 along with a +some documentation improvements. The most important fix is for lexsort when the +keys are of type (u)int8 or (u)int16. If you are currently using 1.17 you +should upgrade. + +The Python versions supported in this release are 3.5-3.7, Python 2.7 has been +dropped. Python 3.8b4 should work with the released source packages, but there +are no future guarantees. + +Downstream developers should use Cython >= 0.29.13 for Python 3.8 support and +OpenBLAS >= 3.7 to avoid errors on the Skylake architecture. The NumPy wheels +on PyPI are built from the OpenBLAS development branch in order to avoid those +errors. + + +Contributors +============ + +A total of 7 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* CakeWithSteak + +* Charles Harris +* Dan Allan +* Hameer Abbasi +* Lars Grueter +* Matti Picus +* Sebastian Berg + + +Pull requests merged +==================== + +A total of 8 pull requests were merged for this release. + +* `#14418 <https://github.com/numpy/numpy/pull/14418>`__: BUG: Fix aradixsort indirect indexing. +* `#14420 <https://github.com/numpy/numpy/pull/14420>`__: DOC: Fix a minor typo in dispatch documentation. +* `#14421 <https://github.com/numpy/numpy/pull/14421>`__: BUG: test, fix regression in converting to ctypes +* `#14430 <https://github.com/numpy/numpy/pull/14430>`__: BUG: Do not show Override module in private error classes. +* `#14432 <https://github.com/numpy/numpy/pull/14432>`__: BUG: Fixed maximum relative error reporting in assert_allclose. +* `#14433 <https://github.com/numpy/numpy/pull/14433>`__: BUG: Fix uint-overflow if padding with linear_ramp and negative... +* `#14436 <https://github.com/numpy/numpy/pull/14436>`__: BUG: Update 1.17.x with 1.18.0-dev pocketfft.py. +* `#14446 <https://github.com/numpy/numpy/pull/14446>`__: REL: Prepare for NumPy 1.17.2 release. diff --git a/doc/source/release/1.17.3-notes.rst b/doc/source/release/1.17.3-notes.rst new file mode 100644 index 000000000000..e33ca19174b7 --- /dev/null +++ b/doc/source/release/1.17.3-notes.rst @@ -0,0 +1,59 @@ +.. currentmodule:: numpy + +========================== +NumPy 1.17.3 Release Notes +========================== + +This release contains fixes for bugs reported against NumPy 1.17.2 along with a +some documentation improvements. The Python versions supported in this release +are 3.5-3.8. + +Downstream developers should use Cython >= 0.29.13 for Python 3.8 support and +OpenBLAS >= 3.7 to avoid errors on the Skylake architecture. + + +Highlights +========== + +- Wheels for Python 3.8 +- Boolean ``matmul`` fixed to use booleans instead of integers. + + +Compatibility notes +=================== + +- The seldom used ``PyArray_DescrCheck`` macro has been changed/fixed. + + +Contributors +============ + +A total of 7 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Allan Haldane +* Charles Harris +* Kevin Sheppard +* Matti Picus +* Ralf Gommers +* Sebastian Berg +* Warren Weckesser + + +Pull requests merged +==================== + +A total of 12 pull requests were merged for this release. + +* `#14456 <https://github.com/numpy/numpy/pull/14456>`__: MAINT: clean up pocketfft modules inside numpy.fft namespace. +* `#14463 <https://github.com/numpy/numpy/pull/14463>`__: BUG: random.hypergeometic assumes npy_long is npy_int64, hung... +* `#14502 <https://github.com/numpy/numpy/pull/14502>`__: BUG: random: Revert gh-14458 and refix gh-14557. +* `#14504 <https://github.com/numpy/numpy/pull/14504>`__: BUG: add a specialized loop for boolean matmul. +* `#14506 <https://github.com/numpy/numpy/pull/14506>`__: MAINT: Update pytest version for Python 3.8 +* `#14512 <https://github.com/numpy/numpy/pull/14512>`__: DOC: random: fix doc linking, was referencing private submodules. +* `#14513 <https://github.com/numpy/numpy/pull/14513>`__: BUG,MAINT: Some fixes and minor cleanup based on clang analysis +* `#14515 <https://github.com/numpy/numpy/pull/14515>`__: BUG: Fix randint when range is 2**32 +* `#14519 <https://github.com/numpy/numpy/pull/14519>`__: MAINT: remove the entropy c-extension module +* `#14563 <https://github.com/numpy/numpy/pull/14563>`__: DOC: remove note about Pocketfft license file (non-existing here). +* `#14578 <https://github.com/numpy/numpy/pull/14578>`__: BUG: random: Create a legacy implementation of random.binomial. +* `#14687 <https://github.com/numpy/numpy/pull/14687>`__: BUG: properly define PyArray_DescrCheck diff --git a/doc/source/release/1.17.4-notes.rst b/doc/source/release/1.17.4-notes.rst new file mode 100644 index 000000000000..47f4725f95a6 --- /dev/null +++ b/doc/source/release/1.17.4-notes.rst @@ -0,0 +1,49 @@ +.. currentmodule:: numpy + +========================== +NumPy 1.17.4 Release Notes +========================== + +This release contains fixes for bugs reported against NumPy 1.17.3 along with +some build improvements. The Python versions supported in this release +are 3.5-3.8. + +Downstream developers should use Cython >= 0.29.13 for Python 3.8 support and +OpenBLAS >= 3.7 to avoid errors on the Skylake architecture. + + +Highlights +========== + +- Fixed `random.random_integers` biased generation of 8 and 16 bit integers. +- Fixed `np.einsum` regression on Power9 and z/Linux. +- Fixed histogram problem with signed integer arrays. + + +Contributors +============ + +A total of 5 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Charles Harris +* Chris Burr + +* Matti Picus +* Qiming Sun + +* Warren Weckesser + + +Pull requests merged +==================== + +A total of 8 pull requests were merged for this release. + +* `#14758 <https://github.com/numpy/numpy/pull/14758>`__: BLD: declare support for python 3.8 +* `#14781 <https://github.com/numpy/numpy/pull/14781>`__: BUG: random: biased samples from integers() with 8 or 16 bit... +* `#14851 <https://github.com/numpy/numpy/pull/14851>`__: BUG: Fix _ctypes class circular reference. (#13808) +* `#14852 <https://github.com/numpy/numpy/pull/14852>`__: BLD: add 'apt update' to shippable +* `#14855 <https://github.com/numpy/numpy/pull/14855>`__: BUG: Fix `np.einsum` errors on Power9 Linux and z/Linux +* `#14857 <https://github.com/numpy/numpy/pull/14857>`__: BUG: lib: Fix histogram problem with signed integer arrays. +* `#14858 <https://github.com/numpy/numpy/pull/14858>`__: BLD: Prevent -flto from optimising long double representation... +* `#14866 <https://github.com/numpy/numpy/pull/14866>`__: MAINT: move buffer.h -> npy_buffer.h to avoid conflicts + diff --git a/doc/source/release/1.17.5-notes.rst b/doc/source/release/1.17.5-notes.rst new file mode 100644 index 000000000000..0f1d3e1a59d5 --- /dev/null +++ b/doc/source/release/1.17.5-notes.rst @@ -0,0 +1,45 @@ +.. currentmodule:: numpy + +========================== +NumPy 1.17.5 Release Notes +========================== + +This release contains fixes for bugs reported against NumPy 1.17.4 along with +some build improvements. The Python versions supported in this release +are 3.5-3.8. + +Downstream developers should use Cython >= 0.29.14 for Python 3.8 support and +OpenBLAS >= 3.7 to avoid errors on the Skylake architecture. + +It is recommended that developers interested in the new random bit generators +upgrade to the NumPy 1.18.x series, as it has updated documentation and +many small improvements. + + +Contributors +============ + +A total of 6 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Charles Harris +* Eric Wieser +* Ilhan Polat +* Matti Picus +* Michael Hudson-Doyle +* Ralf Gommers + + +Pull requests merged +==================== + +A total of 8 pull requests were merged for this release. + +* `#14593 <https://github.com/numpy/numpy/pull/14593>`__: MAINT: backport Cython API cleanup to 1.17.x, remove docs +* `#14937 <https://github.com/numpy/numpy/pull/14937>`__: BUG: fix integer size confusion in handling array's ndmin argument +* `#14939 <https://github.com/numpy/numpy/pull/14939>`__: BUILD: remove SSE2 flag from numpy.random builds +* `#14993 <https://github.com/numpy/numpy/pull/14993>`__: MAINT: Added Python3.8 branch to dll lib discovery +* `#15038 <https://github.com/numpy/numpy/pull/15038>`__: BUG: Fix refcounting in ufunc object loops +* `#15067 <https://github.com/numpy/numpy/pull/15067>`__: BUG: Exceptions tracebacks are dropped +* `#15175 <https://github.com/numpy/numpy/pull/15175>`__: ENH: Backport improvements to testing functions. +* `#15213 <https://github.com/numpy/numpy/pull/15213>`__: REL: Prepare for the NumPy 1.17.5 release. diff --git a/doc/source/release/1.18.0-notes.rst b/doc/source/release/1.18.0-notes.rst new file mode 100644 index 000000000000..15e0ad77f5d1 --- /dev/null +++ b/doc/source/release/1.18.0-notes.rst @@ -0,0 +1,390 @@ +.. currentmodule:: numpy + +========================== +NumPy 1.18.0 Release Notes +========================== + +In addition to the usual bug fixes, this NumPy release cleans up and documents +the new random C-API, expires a large number of old deprecations, and improves +the appearance of the documentation. The Python versions supported are 3.5-3.8. +This is the last NumPy release series that will support Python 3.5. + +Downstream developers should use Cython >= 0.29.14 for Python 3.8 support and +OpenBLAS >= 3.7 to avoid problems on the Skylake +architecture. + + +Highlights +========== + +* The C-API for ``numpy.random`` has been defined and documented. +* Basic infrastructure for linking with 64 bit BLAS and LAPACK libraries. +* Many documentation improvements. + + +New functions +============= + +Multivariate hypergeometric distribution added to ``numpy.random`` +------------------------------------------------------------------ +The method ``multivariate_hypergeometric`` has been added to the class +`numpy.random.Generator`. This method generates random variates from +the multivariate hypergeometric probability distribution. +(`gh-13794 <https://github.com/numpy/numpy/pull/13794>`__) + + +Deprecations +============ + +``np.fromfile`` and ``np.fromstring`` will error on bad data +------------------------------------------------------------ + +In future numpy releases, the functions ``np.fromfile`` and ``np.fromstring`` +will throw an error when parsing bad data. +This will now give a ``DeprecationWarning`` where previously partial or +even invalid data was silently returned. This deprecation also affects +the C defined functions ``PyArray_FromString`` and ``PyArray_FromFile`` +(`gh-13605 <https://github.com/numpy/numpy/pull/13605>`__) + +Deprecate non-scalar arrays as fill values in ``ma.fill_value`` +--------------------------------------------------------------- +Setting a ``MaskedArray.fill_value`` to a non-scalar array is deprecated +since the logic to broadcast the fill value to the array is fragile, +especially when slicing. +(`gh-13698 <https://github.com/numpy/numpy/pull/13698>`__) + +Deprecate ``PyArray_As1D``, ``PyArray_As2D`` +-------------------------------------------- +``PyArray_As1D``, ``PyArray_As2D`` are deprecated, use +``PyArray_AsCArray`` instead +(`gh-14036 <https://github.com/numpy/numpy/pull/14036>`__) + +Deprecate ``np.alen`` +--------------------- +``np.alen`` was deprecated. Use ``len`` instead. +(`gh-14181 <https://github.com/numpy/numpy/pull/14181>`__) + +Deprecate the financial functions +--------------------------------- +In accordance with +`NEP-32 <https://numpy.org/neps/nep-0032-remove-financial-functions.html>`_, +the financial functions ``fv`` ``ipmt``, ``irr``, ``mirr``, ``nper``, +``npv``, ``pmt``, ``ppmt``, ``pv`` and ``rate`` are deprecated, and will be +removed from NumPy 1.20.The replacement for these functions is the Python package +`numpy-financial <https://pypi.org/project/numpy-financial>`_. +(`gh-14720 <https://github.com/numpy/numpy/pull/14720>`__) + +The ``axis`` argument to ``numpy.ma.mask_cols`` and ``numpy.ma.mask_row`` is deprecated +--------------------------------------------------------------------------------------- +This argument was always ignored. +(`gh-14996 <https://github.com/numpy/numpy/pull/14996>`__) + + +Expired deprecations +==================== + +* ``PyArray_As1D`` and ``PyArray_As2D`` have been removed in favor of + ``PyArray_AsCArray`` + (`gh-14036 <https://github.com/numpy/numpy/pull/14036>`__) + +* ``np.rank`` has been removed. This was deprecated in NumPy 1.10 + and has been replaced by ``np.ndim``. + (`gh-14039 <https://github.com/numpy/numpy/pull/14039>`__) + +* The deprecation of ``expand_dims`` out-of-range axes in 1.13.0 has + expired. + (`gh-14051 <https://github.com/numpy/numpy/pull/14051>`__) + +* ``PyArray_FromDimsAndDataAndDescr`` and ``PyArray_FromDims`` have been + removed (they will always raise an error). Use ``PyArray_NewFromDescr`` + and ``PyArray_SimpleNew`` instead. + (`gh-14100 <https://github.com/numpy/numpy/pull/14100>`__) + +* ``numeric.loads``, ``numeric.load``, ``np.ma.dump``, + ``np.ma.dumps``, ``np.ma.load``, ``np.ma.loads`` are removed, + use ``pickle`` methods instead + (`gh-14256 <https://github.com/numpy/numpy/pull/14256>`__) + +* ``arrayprint.FloatFormat``, ``arrayprint.LongFloatFormat`` has been removed, + use ``FloatingFormat`` instead + +* ``arrayprint.ComplexFormat``, ``arrayprint.LongComplexFormat`` has been + removed, use ``ComplexFloatingFormat`` instead + +* ``arrayprint.StructureFormat`` has been removed, use ``StructureVoidFormat`` + instead + (`gh-14259 <https://github.com/numpy/numpy/pull/14259>`__) + +* ``np.testing.rand`` has been removed. This was deprecated in NumPy 1.11 + and has been replaced by ``np.random.rand``. + (`gh-14325 <https://github.com/numpy/numpy/pull/14325>`__) + +* Class ``SafeEval`` in ``numpy/lib/utils.py`` has been removed. + This was deprecated in NumPy 1.10. Use ``np.safe_eval`` instead. + (`gh-14335 <https://github.com/numpy/numpy/pull/14335>`__) + +* Remove deprecated support for boolean and empty condition lists in + ``np.select`` + (`gh-14583 <https://github.com/numpy/numpy/pull/14583>`__) + +* Array order only accepts 'C', 'F', 'A', and 'K'. More permissive options + were deprecated in NumPy 1.11. + (`gh-14596 <https://github.com/numpy/numpy/pull/14596>`__) + +* np.linspace parameter ``num`` must be an integer. Deprecated in NumPy 1.12. + (`gh-14620 <https://github.com/numpy/numpy/pull/14620>`__) + +* UFuncs with multiple outputs must use a tuple for the ``out`` kwarg. This + finishes a deprecation started in NumPy 1.10. + (`gh-14682 <https://github.com/numpy/numpy/pull/14682>`__) + +The files ``numpy/testing/decorators.py``, ``numpy/testing/noseclasses.py`` +and ``numpy/testing/nosetester.py`` have been removed. They were never +meant to be public (all relevant objects are present in the +``numpy.testing`` namespace), and importing them has given a deprecation +warning since NumPy 1.15.0 +(`gh-14567 <https://github.com/numpy/numpy/pull/14567>`__) + + +Compatibility notes +=================== + +`numpy.lib.recfunctions.drop_fields` can no longer return None +-------------------------------------------------------------- +If ``drop_fields`` is used to drop all fields, previously the array would +be completely discarded and None returned. Now it returns an array of the +same shape as the input, but with no fields. The old behavior can be retained +with:: + + dropped_arr = drop_fields(arr, ['a', 'b']) + if dropped_arr.dtype.names == (): + dropped_arr = None + +converting the empty recarray to None +(`gh-14510 <https://github.com/numpy/numpy/pull/14510>`__) + +``numpy.argmin/argmax/min/max`` returns ``NaT`` if it exists in array +--------------------------------------------------------------------- +``numpy.argmin``, ``numpy.argmax``, ``numpy.min``, and ``numpy.max`` will return +``NaT`` if it exists in the array. +(`gh-14717 <https://github.com/numpy/numpy/pull/14717>`__) + +``np.can_cast(np.uint64, np.timedelta64, casting='safe')`` is now ``False`` +--------------------------------------------------------------------------- +Previously this was ``True`` - however, this was inconsistent with ``uint64`` +not being safely castable to ``int64``, and resulting in strange type +resolution. + +If this impacts your code, cast ``uint64`` to ``int64`` first. +(`gh-14718 <https://github.com/numpy/numpy/pull/14718>`__) + +Changed random variate stream from ``numpy.random.Generator.integers`` +---------------------------------------------------------------------- +There was a bug in ``numpy.random.Generator.integers`` that caused biased +sampling of 8 and 16 bit integer types. Fixing that bug has changed the +output stream from what it was in previous releases. +(`gh-14777 <https://github.com/numpy/numpy/pull/14777>`__) + +Add more ufunc loops for ``datetime64``, ``timedelta64`` +-------------------------------------------------------- +``np.datetime('NaT')`` should behave more like ``float('Nan')``. Add needed +infrastructure so ``np.isinf(a)`` and ``np.isnan(a)`` will run on +``datetime64`` and ``timedelta64`` dtypes. Also added specific loops for +``numpy.fmin`` and ``numpy.fmax`` that mask ``NaT``. This may require +adjustment to user- facing code. Specifically, code that either disallowed the +calls to ``numpy.isinf`` or ``numpy.isnan`` or checked that they raised an +exception will require adaptation, and code that mistakenly called +``numpy.fmax`` and ``numpy.fmin`` instead of ``numpy.maximum`` or +``numpy.minimum`` respectively will require adjustment. This also affects +``numpy.nanmax`` and ``numpy.nanmin``. +(`gh-14841 <https://github.com/numpy/numpy/pull/14841>`__) + +Moved modules in ``numpy.random`` +--------------------------------- +As part of the API cleanup, the submodules in ``numpy.random`` +``bit_generator``, ``philox``, ``pcg64``, ``sfc64, ``common``, ``generator``, +and ``bounded_integers`` were moved to ``_bit_generator``, ``_philox``, +``_pcg64``, ``_sfc64, ``_common``, ``_generator``, and ``_bounded_integers`` +respectively to indicate that they are not part of the public interface. +(`gh-14608 <https://github.com/numpy/numpy/pull/14608>`__) + + +C API changes +============= + +``PyDataType_ISUNSIZED(descr)`` now returns False for structured datatypes +-------------------------------------------------------------------------- +Previously this returned True for any datatype of itemsize 0, but now this +returns false for the non-flexible datatype with itemsize 0, ``np.dtype([])``. +(`gh-14393 <https://github.com/numpy/numpy/pull/14393>`__) + + +New Features +============ + +Add our own ``*.pxd`` cython import file +---------------------------------------- +Added a ``numpy/__init__.pxd`` file. It will be used for ``cimport numpy`` +(`gh-12284 <https://github.com/numpy/numpy/pull/12284>`__) + +A tuple of axes can now be input to ``expand_dims`` +--------------------------------------------------- +The ``numpy.expand_dims`` ``axis`` keyword can now accept a tuple of +axes. Previously, ``axis`` was required to be an integer. +(`gh-14051 <https://github.com/numpy/numpy/pull/14051>`__) + +Support for 64-bit OpenBLAS +--------------------------- +Added support for 64-bit (ILP64) OpenBLAS. See ``site.cfg.example`` +for details. +(`gh-15012 <https://github.com/numpy/numpy/pull/15012>`__) + +Add ``--f2cmap`` option to F2PY +------------------------------- +Allow specifying a file to load Fortran-to-C type map +customizations from. +(`gh-15113 <https://github.com/numpy/numpy/pull/15113>`__) + + +Improvements +============ + +Different C numeric types of the same size have unique names +------------------------------------------------------------ +On any given platform, two of ``np.intc``, ``np.int_``, and ``np.longlong`` +would previously appear indistinguishable through their ``repr``, despite +their corresponding ``dtype`` having different properties. +A similar problem existed for the unsigned counterparts to these types, and on +some platforms for ``np.double`` and ``np.longdouble`` + +These types now always print with a unique ``__name__``. +(`gh-10151 <https://github.com/numpy/numpy/pull/10151>`__) + +``argwhere`` now produces a consistent result on 0d arrays +---------------------------------------------------------- +On N-d arrays, ``numpy.argwhere`` now always produces an array of shape +``(n_non_zero, arr.ndim)``, even when ``arr.ndim == 0``. Previously, the +last axis would have a dimension of 1 in this case. +(`gh-13610 <https://github.com/numpy/numpy/pull/13610>`__) + +Add ``axis`` argument for ``random.permutation`` and ``random.shuffle`` +----------------------------------------------------------------------- + +Previously the ``random.permutation`` and ``random.shuffle`` functions +can only shuffle an array along the first axis; they now have a +new argument ``axis`` which allows shuffle along a specified axis. +(`gh-13829 <https://github.com/numpy/numpy/pull/13829>`__) + +``method`` keyword argument for ``np.random.multivariate_normal`` +----------------------------------------------------------------- +A ``method`` keyword argument is now available for +``np.random.multivariate_normal`` with possible values +``{'svd', 'eigh', 'cholesky'}``. To use it, write +``np.random.multivariate_normal(..., method=<method>)``. +(`gh-14197 <https://github.com/numpy/numpy/pull/14197>`__) + +Add complex number support for ``numpy.fromstring`` +--------------------------------------------------- +Now ``numpy.fromstring`` can read complex numbers. +(`gh-14227 <https://github.com/numpy/numpy/pull/14227>`__) + +``numpy.unique`` has consistent axes order when ``axis`` is not None +-------------------------------------------------------------------- +Using ``moveaxis`` instead of ``swapaxes`` in ``numpy.unique``, so that the ordering of axes +except the axis in arguments will not be broken. +(`gh-14255 <https://github.com/numpy/numpy/pull/14255>`__) + +``numpy.matmul`` with boolean output now converts to boolean values +------------------------------------------------------------------- +Calling ``numpy.matmul`` where the output is a boolean array would fill the array +with uint8 equivalents of the result, rather than 0/1. Now it forces the output +to 0 or 1 (``NPY_TRUE`` or ``NPY_FALSE``). +(`gh-14464 <https://github.com/numpy/numpy/pull/14464>`__) + +``numpy.random.randint`` produced incorrect value when the range was ``2**32`` +------------------------------------------------------------------------------ +The implementation introduced in 1.17.0 had an incorrect check when +determining whether to use the 32-bit path or the full 64-bit +path that incorrectly redirected random integer generation with a high - low +range of ``2**32`` to the 64-bit generator. +(`gh-14501 <https://github.com/numpy/numpy/pull/14501>`__) + +Add complex number support for ``numpy.fromfile`` +------------------------------------------------- +Now ``numpy.fromfile`` can read complex numbers. +(`gh-14730 <https://github.com/numpy/numpy/pull/14730>`__) + +``std=c99`` added if compiler is named ``gcc`` +---------------------------------------------- +GCC before version 5 requires the ``-std=c99`` command line argument. Newer +compilers automatically turn on C99 mode. The compiler setup code will +automatically add the code if the compiler name has ``gcc`` in it. +(`gh-14771 <https://github.com/numpy/numpy/pull/14771>`__) + + +Changes +======= + + +``NaT`` now sorts to the end of arrays +-------------------------------------- +``NaT`` is now effectively treated as the largest integer for sorting +purposes, so that it sorts to the end of arrays. This change is for consistency +with ``NaN`` sorting behavior. +(`gh-12658 <https://github.com/numpy/numpy/pull/12658>`__) +(`gh-15068 <https://github.com/numpy/numpy/pull/15068>`__) + +Incorrect ``threshold`` in ``np.set_printoptions`` raises ``TypeError`` or ``ValueError`` +----------------------------------------------------------------------------------------- +Previously an incorrect ``threshold`` raised ``ValueError``; it now raises ``TypeError`` +for non-numeric types and ``ValueError`` for ``nan`` values. +(`gh-13899 <https://github.com/numpy/numpy/pull/13899>`__) + +Warn when saving a dtype with metadata +-------------------------------------- +A ``UserWarning`` will be emitted when saving an array via ``numpy.save`` with +``metadata``. Saving such an array may not preserve metadata, and if metadata +is preserved, loading it will cause a ``ValueError``. This shortcoming in save +and load will be addressed in a future release. +(`gh-14142 <https://github.com/numpy/numpy/pull/14142>`__) + +``numpy.distutils`` append behavior changed for LDFLAGS and similar +------------------------------------------------------------------- +`numpy.distutils` has always overridden rather than appended to ``LDFLAGS`` and +other similar such environment variables for compiling Fortran extensions. Now +the default behavior has changed to appending - which is the expected behavior +in most situations. To preserve the old (overwriting) behavior, set the +``NPY_DISTUTILS_APPEND_FLAGS`` environment variable to 0. This applies to: +``LDFLAGS``, ``F77FLAGS``, ``F90FLAGS``, ``FREEFLAGS``, ``FOPT``, ``FDEBUG``, +and ``FFLAGS``. NumPy 1.16 and 1.17 gave build warnings in situations where this +change in behavior would have affected the compile flags used. +(`gh-14248 <https://github.com/numpy/numpy/pull/14248>`__) + +Remove ``numpy.random.entropy`` without a deprecation +----------------------------------------------------- + +``numpy.random.entropy`` was added to the ``numpy.random`` namespace in 1.17.0. +It was meant to be a private c-extension module, but was exposed as public. +It has been replaced by ``numpy.random.SeedSequence`` so the module was +completely removed. +(`gh-14498 <https://github.com/numpy/numpy/pull/14498>`__) + +Add options to quiet build configuration and build with ``-Werror`` +------------------------------------------------------------------- +Added two new configuration options. During the ``build_src`` subcommand, as +part of configuring NumPy, the files ``_numpyconfig.h`` and ``config.h`` are +created by probing support for various runtime functions and routines. +Previously, the very verbose compiler output during this stage clouded more +important information. By default the output is silenced. Running +``runtests.py --debug-info`` will add ``--verbose-cfg`` to the ``build_src`` +subcommand,which will restore the previous behaviour. + +Adding ``CFLAGS=-Werror`` to turn warnings into errors would trigger errors +during the configuration. Now ``runtests.py --warn-error`` will add +``--warn-error`` to the ``build`` subcommand, which will percolate to the +``build_ext`` and ``build_lib`` subcommands. This will add the compiler flag +to those stages and turn compiler warnings into errors while actually building +NumPy itself, avoiding the ``build_src`` subcommand compiler calls. + +(`gh-14527 <https://github.com/numpy/numpy/pull/14527>`__) +(`gh-14518 <https://github.com/numpy/numpy/pull/14518>`__) diff --git a/doc/source/release/1.18.1-notes.rst b/doc/source/release/1.18.1-notes.rst new file mode 100644 index 000000000000..8bc502ecbc74 --- /dev/null +++ b/doc/source/release/1.18.1-notes.rst @@ -0,0 +1,52 @@ +.. currentmodule:: numpy + +========================== +NumPy 1.18.1 Release Notes +========================== + +This release contains fixes for bugs reported against NumPy 1.18.0. Two bugs +in particular that caused widespread problems downstream were: + +- The cython random extension test was not using a temporary directory for + building, resulting in a permission violation. Fixed. + +- Numpy distutils was appending `-std=c99` to all C compiler runs, leading to + changed behavior and compile problems downstream. That flag is now only + applied when building numpy C code. + +The Python versions supported in this release are 3.5-3.8. Downstream +developers should use Cython >= 0.29.14 for Python 3.8 support and OpenBLAS >= +3.7 to avoid errors on the Skylake architecture. + +Contributors +============ + +A total of 7 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Charles Harris +* Matti Picus +* Maxwell Aladago +* Pauli Virtanen +* Ralf Gommers +* Tyler Reddy +* Warren Weckesser + +Pull requests merged +==================== + +A total of 13 pull requests were merged for this release. + +* `#15158 <https://github.com/numpy/numpy/pull/15158>`__: MAINT: Update pavement.py for towncrier. +* `#15159 <https://github.com/numpy/numpy/pull/15159>`__: DOC: add moved modules to 1.18 release note +* `#15161 <https://github.com/numpy/numpy/pull/15161>`__: MAINT, DOC: Minor backports and updates for 1.18.x +* `#15176 <https://github.com/numpy/numpy/pull/15176>`__: TST: Add assert_array_equal test for big integer arrays +* `#15184 <https://github.com/numpy/numpy/pull/15184>`__: BUG: use tmp dir and check version for cython test (#15170) +* `#15220 <https://github.com/numpy/numpy/pull/15220>`__: BUG: distutils: fix msvc+gfortran openblas handling corner case +* `#15221 <https://github.com/numpy/numpy/pull/15221>`__: BUG: remove -std=c99 for c++ compilation (#15194) +* `#15222 <https://github.com/numpy/numpy/pull/15222>`__: MAINT: unskip test on win32 +* `#15223 <https://github.com/numpy/numpy/pull/15223>`__: TST: add BLAS ILP64 run in Travis & Azure +* `#15245 <https://github.com/numpy/numpy/pull/15245>`__: MAINT: only add --std=c99 where needed +* `#15246 <https://github.com/numpy/numpy/pull/15246>`__: BUG: lib: Fix handling of integer arrays by gradient. +* `#15247 <https://github.com/numpy/numpy/pull/15247>`__: MAINT: Do not use private Python function in testing +* `#15250 <https://github.com/numpy/numpy/pull/15250>`__: REL: Prepare for the NumPy 1.18.1 release. diff --git a/doc/source/release/1.18.2-notes.rst b/doc/source/release/1.18.2-notes.rst new file mode 100644 index 000000000000..2681a907f48b --- /dev/null +++ b/doc/source/release/1.18.2-notes.rst @@ -0,0 +1,39 @@ +.. currentmodule:: numpy + +========================== +NumPy 1.18.2 Release Notes +========================== + +This small release contains a fix for a performance regression in numpy/random +and several bug/maintenance updates. + +The Python versions supported in this release are 3.5-3.8. Downstream +developers should use Cython >= 0.29.15 for Python 3.8 support and OpenBLAS >= +3.7 to avoid errors on the Skylake architecture. + + +Contributors +============ + +A total of 5 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Charles Harris +* Ganesh Kathiresan + +* Matti Picus +* Sebastian Berg +* przemb + + + +Pull requests merged +==================== + +A total of 7 pull requests were merged for this release. + +* `#15675 <https://github.com/numpy/numpy/pull/15675>`__: TST: move _no_tracing to testing._private +* `#15676 <https://github.com/numpy/numpy/pull/15676>`__: MAINT: Large overhead in some random functions +* `#15677 <https://github.com/numpy/numpy/pull/15677>`__: TST: Do not create gfortran link in azure Mac testing. +* `#15679 <https://github.com/numpy/numpy/pull/15679>`__: BUG: Added missing error check in `ndarray.__contains__` +* `#15722 <https://github.com/numpy/numpy/pull/15722>`__: MAINT: use list-based APIs to call subprocesses +* `#15729 <https://github.com/numpy/numpy/pull/15729>`__: REL: Prepare for 1.18.2 release. +* `#15734 <https://github.com/numpy/numpy/pull/15734>`__: BUG: fix logic error when nm fails on 32-bit diff --git a/doc/source/release/1.18.3-notes.rst b/doc/source/release/1.18.3-notes.rst new file mode 100644 index 000000000000..1ebad52b80aa --- /dev/null +++ b/doc/source/release/1.18.3-notes.rst @@ -0,0 +1,45 @@ +.. currentmodule:: numpy + +========================== +NumPy 1.18.3 Release Notes +========================== + +This release contains various bug/regression fixes. + +The Python versions supported in this release are 3.5-3.8. Downstream +developers should use Cython >= 0.29.15 for Python 3.8 support and OpenBLAS >= +3.7 to avoid errors on the Skylake architecture. + + +Highlights +========== + +* Fix for the `method='eigh'` and `method='cholesky'` methods in + `numpy.random.multivariate_normal`. Those were producing samples from the + wrong distribution. + + +Contributors +============ + +A total of 6 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Charles Harris +* Max Balandat + +* @Mibu287 + +* Pan Jan + +* Sebastian Berg +* @panpiort8 + + + +Pull requests merged +==================== + +A total of 5 pull requests were merged for this release. + +* `#15916 <https://github.com/numpy/numpy/pull/15916>`__: BUG: Fix eigh and cholesky methods of numpy.random.multivariate_normal +* `#15929 <https://github.com/numpy/numpy/pull/15929>`__: BUG,MAINT: Remove incorrect special case in string to number... +* `#15930 <https://github.com/numpy/numpy/pull/15930>`__: BUG: Guarantee array is in valid state after memory error occurs... +* `#15954 <https://github.com/numpy/numpy/pull/15954>`__: BUG: Check that `pvals` is 1D in `_generator.multinomial`. +* `#16017 <https://github.com/numpy/numpy/pull/16017>`__: BUG: Alpha parameter must be 1D in `generator.dirichlet` diff --git a/doc/source/release/1.18.4-notes.rst b/doc/source/release/1.18.4-notes.rst new file mode 100644 index 000000000000..25ef1d127c68 --- /dev/null +++ b/doc/source/release/1.18.4-notes.rst @@ -0,0 +1,38 @@ +.. currentmodule:: numpy + +========================== +NumPy 1.18.4 Release Notes +========================== + +This is the last planned release in the 1.18.x series. It reverts the +``bool("0")`` behavior introduced in 1.18.3 and fixes a bug in +``Generator.integers``. There is also a link to a new troubleshooting section +in the documentation included in the error message emitted when numpy import +fails. + +The Python versions supported in this release are 3.5-3.8. Downstream +developers should use Cython >= 0.29.15 for Python 3.8 support and +OpenBLAS >= 3.7 to avoid errors on the Skylake architecture. + +Contributors +============ + +A total of 4 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Charles Harris +* Matti Picus +* Sebastian Berg +* Warren Weckesser + +Pull requests merged +==================== + +A total of 6 pull requests were merged for this release. + +* `#16055 <https://github.com/numpy/numpy/pull/16055>`__: BLD: add i686 for 1.18 builds +* `#16090 <https://github.com/numpy/numpy/pull/16090>`__: BUG: random: ``Generator.integers(2**32)`` always returned 0. +* `#16091 <https://github.com/numpy/numpy/pull/16091>`__: BLD: fix path to libgfortran on macOS +* `#16109 <https://github.com/numpy/numpy/pull/16109>`__: REV: Reverts side-effect changes to casting +* `#16114 <https://github.com/numpy/numpy/pull/16114>`__: BLD: put openblas library in local directory on windows +* `#16132 <https://github.com/numpy/numpy/pull/16132>`__: DOC: Change import error "howto" to link to new troubleshooting... diff --git a/doc/source/release/1.18.5-notes.rst b/doc/source/release/1.18.5-notes.rst new file mode 100644 index 000000000000..e704c001a452 --- /dev/null +++ b/doc/source/release/1.18.5-notes.rst @@ -0,0 +1,31 @@ +.. currentmodule:: numpy + +========================== +NumPy 1.18.5 Release Notes +========================== + +This is a short release to allow pickle ``protocol=5`` to be used in +Python3.5. It is motivated by the recent backport of pickle5 to Python3.5. + +The Python versions supported in this release are 3.5-3.8. Downstream +developers should use Cython >= 0.29.15 for Python 3.8 support and +OpenBLAS >= 3.7 to avoid errors on the Skylake architecture. + +Contributors +============ + +A total of 3 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Charles Harris +* Matti Picus +* Siyuan Zhuang + + +Pull requests merged +==================== + +A total of 2 pull requests were merged for this release. + +* `#16439 <https://github.com/numpy/numpy/pull/16439>`__: ENH: enable pickle protocol 5 support for python3.5 +* `#16441 <https://github.com/numpy/numpy/pull/16441>`__: BUG: relpath fails for different drives on windows + diff --git a/doc/source/release/1.19.0-notes.rst b/doc/source/release/1.19.0-notes.rst new file mode 100644 index 000000000000..410890697b5c --- /dev/null +++ b/doc/source/release/1.19.0-notes.rst @@ -0,0 +1,477 @@ +.. currentmodule:: numpy + +========================== +NumPy 1.19.0 Release Notes +========================== +This NumPy release is marked by the removal of much technical debt: support for +Python 2 has been removed, many deprecations have been expired, and +documentation has been improved. The polishing of the random module continues +apace with bug fixes and better usability from Cython. + +The Python versions supported for this release are 3.6-3.8. Downstream +developers should use Cython >= 0.29.16 for Python 3.8 support and +OpenBLAS >= 3.7 to avoid problems on the Skylake architecture. + + +Highlights +========== + +* Code compatibility with Python versions < 3.6 (including Python 2) was + dropped from both the python and C code. The shims in ``numpy.compat`` will + remain to support third-party packages, but they may be deprecated in a + future release. Note that 1.19.x will *not* compile with earlier versions of + Python due to the use of f-strings. + + (`gh-15233 <https://github.com/numpy/numpy/pull/15233>`__) + + +Expired deprecations +==================== + +``numpy.insert`` and ``numpy.delete`` can no longer be passed an axis on 0d arrays +---------------------------------------------------------------------------------- +This concludes a deprecation from 1.9, where when an ``axis`` argument was +passed to a call to ``~numpy.insert`` and ``~numpy.delete`` on a 0d array, the +``axis`` and ``obj`` argument and indices would be completely ignored. +In these cases, ``insert(arr, "nonsense", 42, axis=0)`` would actually overwrite the +entire array, while ``delete(arr, "nonsense", axis=0)`` would be ``arr.copy()`` + +Now passing ``axis`` on a 0d array raises ``~numpy.AxisError``. + +(`gh-15802 <https://github.com/numpy/numpy/pull/15802>`__) + +``numpy.delete`` no longer ignores out-of-bounds indices +-------------------------------------------------------- +This concludes deprecations from 1.8 and 1.9, where ``np.delete`` would ignore +both negative and out-of-bounds items in a sequence of indices. This was at +odds with its behavior when passed a single index. + +Now out-of-bounds items throw ``IndexError``, and negative items index from the +end. + +(`gh-15804 <https://github.com/numpy/numpy/pull/15804>`__) + +``numpy.insert`` and ``numpy.delete`` no longer accept non-integral indices +--------------------------------------------------------------------------- +This concludes a deprecation from 1.9, where sequences of non-integers indices +were allowed and cast to integers. Now passing sequences of non-integral +indices raises ``IndexError``, just like it does when passing a single +non-integral scalar. + +(`gh-15805 <https://github.com/numpy/numpy/pull/15805>`__) + +``numpy.delete`` no longer casts boolean indices to integers +------------------------------------------------------------ +This concludes a deprecation from 1.8, where ``np.delete`` would cast boolean +arrays and scalars passed as an index argument into integer indices. The +behavior now is to treat boolean arrays as a mask, and to raise an error +on boolean scalars. + +(`gh-15815 <https://github.com/numpy/numpy/pull/15815>`__) + + +Compatibility notes +=================== + +Changed random variate stream from ``numpy.random.Generator.dirichlet`` +----------------------------------------------------------------------- +A bug in the generation of random variates for the Dirichlet distribution +with small 'alpha' values was fixed by using a different algorithm when +``max(alpha) < 0.1``. Because of the change, the stream of variates +generated by ``dirichlet`` in this case will be different from previous +releases. + +(`gh-14924 <https://github.com/numpy/numpy/pull/14924>`__) + +Scalar promotion in ``PyArray_ConvertToCommonType`` +--------------------------------------------------- +The promotion of mixed scalars and arrays in ``PyArray_ConvertToCommonType`` +has been changed to adhere to those used by ``np.result_type``. +This means that input such as ``(1000, np.array([1], dtype=np.uint8)))`` +will now return ``uint16`` dtypes. In most cases the behaviour is unchanged. +Note that the use of this C-API function is generally discouraged. +This also fixes ``np.choose`` to behave the same way as the rest of NumPy +in this respect. + +(`gh-14933 <https://github.com/numpy/numpy/pull/14933>`__) + +Fasttake and fastputmask slots are deprecated and NULL'ed +--------------------------------------------------------- +The fasttake and fastputmask slots are now never used and +must always be set to NULL. This will result in no change in behaviour. +However, if a user dtype should set one of these a DeprecationWarning +will be given. + +(`gh-14942 <https://github.com/numpy/numpy/pull/14942>`__) + +``np.ediff1d`` casting behaviour with ``to_end`` and ``to_begin`` +----------------------------------------------------------------- +``np.ediff1d`` now uses the ``"same_kind"`` casting rule for +its additional ``to_end`` and ``to_begin`` arguments. This +ensures type safety except when the input array has a smaller +integer type than ``to_begin`` or ``to_end``. +In rare cases, the behaviour will be more strict than it was +previously in 1.16 and 1.17. This is necessary to solve issues +with floating point NaN. + +(`gh-14981 <https://github.com/numpy/numpy/pull/14981>`__) + +Converting of empty array-like objects to NumPy arrays +------------------------------------------------------ +Objects with ``len(obj) == 0`` which implement an "array-like" interface, +meaning an object implementing ``obj.__array__()``, +``obj.__array_interface__``, ``obj.__array_struct__``, or the python +buffer interface and which are also sequences (i.e. Pandas objects) +will now always retain there shape correctly when converted to an array. +If such an object has a shape of ``(0, 1)`` previously, it could +be converted into an array of shape ``(0,)`` (losing all dimensions +after the first 0). + +(`gh-14995 <https://github.com/numpy/numpy/pull/14995>`__) + +Removed ``multiarray.int_asbuffer`` +----------------------------------- +As part of the continued removal of Python 2 compatibility, +``multiarray.int_asbuffer`` was removed. On Python 3, it threw a +``NotImplementedError`` and was unused internally. It is expected that there +are no downstream use cases for this method with Python 3. + +(`gh-15229 <https://github.com/numpy/numpy/pull/15229>`__) + +``numpy.distutils.compat`` has been removed +------------------------------------------- +This module contained only the function ``get_exception()``, which was used as:: + + try: + ... + except Exception: + e = get_exception() + +Its purpose was to handle the change in syntax introduced in Python 2.6, from +``except Exception, e:`` to ``except Exception as e:``, meaning it was only +necessary for codebases supporting Python 2.5 and older. + +(`gh-15255 <https://github.com/numpy/numpy/pull/15255>`__) + +``issubdtype`` no longer interprets ``float`` as ``np.floating`` +---------------------------------------------------------------- +``numpy.issubdtype`` had a FutureWarning since NumPy 1.14 which +has expired now. This means that certain input where the second +argument was neither a datatype nor a NumPy scalar type +(such as a string or a python type like ``int`` or ``float``) +will now be consistent with passing in ``np.dtype(arg2).type``. +This makes the result consistent with expectations and leads to +a false result in some cases which previously returned true. + +(`gh-15773 <https://github.com/numpy/numpy/pull/15773>`__) + +Change output of ``round`` on scalars to be consistent with Python +------------------------------------------------------------------ + +Output of the ``__round__`` dunder method and consequently the Python +built-in ``round`` has been changed to be a Python ``int`` to be consistent +with calling it on Python ``float`` objects when called with no arguments. +Previously, it would return a scalar of the ``np.dtype`` that was passed in. + +(`gh-15840 <https://github.com/numpy/numpy/pull/15840>`__) + +The ``numpy.ndarray`` constructor no longer interprets ``strides=()`` as ``strides=None`` +----------------------------------------------------------------------------------------- +The former has changed to have the expected meaning of setting +``numpy.ndarray.strides`` to ``()``, while the latter continues to result in +strides being chosen automatically. + +(`gh-15882 <https://github.com/numpy/numpy/pull/15882>`__) + +C-Level string to datetime casts changed +---------------------------------------- +The C-level casts from strings were simplified. This changed +also fixes string to datetime and timedelta casts to behave +correctly (i.e. like Python casts using ``string_arr.astype("M8")`` +while previously the cast would behave like +``string_arr.astype(np.int_).astype("M8")``. +This only affects code using low-level C-API to do manual casts +(not full array casts) of single scalar values or using e.g. +``PyArray_GetCastFunc``, and should thus not affect the vast majority +of users. + +(`gh-16068 <https://github.com/numpy/numpy/pull/16068>`__) + +``SeedSequence`` with small seeds no longer conflicts with spawning +------------------------------------------------------------------- +Small seeds (less than ``2**96``) were previously implicitly 0-padded out to +128 bits, the size of the internal entropy pool. When spawned, the spawn key +was concatenated before the 0-padding. Since the first spawn key is ``(0,)``, +small seeds before the spawn created the same states as the first spawned +``SeedSequence``. Now, the seed is explicitly 0-padded out to the internal +pool size before concatenating the spawn key. Spawned ``SeedSequences`` will +produce different results than in the previous release. Unspawned +``SeedSequences`` will still produce the same results. + +(`gh-16551 <https://github.com/numpy/numpy/pull/16551>`__) + + +Deprecations +============ + +Deprecate automatic ``dtype=object`` for ragged input +----------------------------------------------------- +Calling ``np.array([[1, [1, 2, 3]])`` will issue a ``DeprecationWarning`` as +per `NEP 34`_. Users should explicitly use ``dtype=object`` to avoid the +warning. + +.. _`NEP 34`: https://numpy.org/neps/nep-0034.html + +(`gh-15119 <https://github.com/numpy/numpy/pull/15119>`__) + +Passing ``shape=0`` to factory functions in ``numpy.rec`` is deprecated +----------------------------------------------------------------------- +``0`` is treated as a special case and is aliased to ``None`` in the functions: + +* ``numpy.core.records.fromarrays`` +* ``numpy.core.records.fromrecords`` +* ``numpy.core.records.fromstring`` +* ``numpy.core.records.fromfile`` + +In future, ``0`` will not be special cased, and will be treated as an array +length like any other integer. + +(`gh-15217 <https://github.com/numpy/numpy/pull/15217>`__) + +Deprecation of probably unused C-API functions +---------------------------------------------- +The following C-API functions are probably unused and have been +deprecated: + +* ``PyArray_GetArrayParamsFromObject`` +* ``PyUFunc_GenericFunction`` +* ``PyUFunc_SetUsesArraysAsData`` + +In most cases ``PyArray_GetArrayParamsFromObject`` should be replaced +by converting to an array, while ``PyUFunc_GenericFunction`` can be +replaced with ``PyObject_Call`` (see documentation for details). + +(`gh-15427 <https://github.com/numpy/numpy/pull/15427>`__) + +Converting certain types to dtypes is Deprecated +------------------------------------------------ +The super classes of scalar types, such as ``np.integer``, ``np.generic``, +or ``np.inexact`` will now give a deprecation warning when converted +to a dtype (or used in a dtype keyword argument). +The reason for this is that ``np.integer`` is converted to ``np.int_``, +while it would be expected to represent *any* integer (e.g. also +``int8``, ``int16``, etc. +For example, ``dtype=np.floating`` is currently identical to +``dtype=np.float64``, even though also ``np.float32`` is a subclass of +``np.floating``. + +(`gh-15534 <https://github.com/numpy/numpy/pull/15534>`__) + +Deprecation of ``round`` for ``np.complexfloating`` scalars +----------------------------------------------------------- +Output of the ``__round__`` dunder method and consequently the Python built-in +``round`` has been deprecated on complex scalars. This does not affect +``np.round``. + +(`gh-15840 <https://github.com/numpy/numpy/pull/15840>`__) + +``numpy.ndarray.tostring()`` is deprecated in favor of ``tobytes()`` +-------------------------------------------------------------------- +``~numpy.ndarray.tobytes`` has existed since the 1.9 release, but until this +release ``~numpy.ndarray.tostring`` emitted no warning. The change to emit a +warning brings NumPy in line with the builtin ``array.array`` methods of the +same name. + +(`gh-15867 <https://github.com/numpy/numpy/pull/15867>`__) + + +C API changes +============= + +Better support for ``const`` dimensions in API functions +-------------------------------------------------------- +The following functions now accept a constant array of ``npy_intp``: + +* ``PyArray_BroadcastToShape`` +* ``PyArray_IntTupleFromIntp`` +* ``PyArray_OverflowMultiplyList`` + +Previously the caller would have to cast away the const-ness to call these +functions. + +(`gh-15251 <https://github.com/numpy/numpy/pull/15251>`__) + +Const qualify UFunc inner loops +------------------------------- +``UFuncGenericFunction`` now expects pointers to const ``dimension`` and +``strides`` as arguments. This means inner loops may no longer modify +either ``dimension`` or ``strides``. This change leads to an +``incompatible-pointer-types`` warning forcing users to either ignore +the compiler warnings or to const qualify their own loop signatures. + +(`gh-15355 <https://github.com/numpy/numpy/pull/15355>`__) + + +New Features +============ + +``numpy.frompyfunc`` now accepts an identity argument +----------------------------------------------------- +This allows the :attr:``numpy.ufunc.identity`` attribute to be set on the +resulting ufunc, meaning it can be used for empty and multi-dimensional +calls to :meth:``numpy.ufunc.reduce``. + +(`gh-8255 <https://github.com/numpy/numpy/pull/8255>`__) + +``np.str_`` scalars now support the buffer protocol +--------------------------------------------------- +``np.str_`` arrays are always stored as UCS4, so the corresponding scalars +now expose this through the buffer interface, meaning +``memoryview(np.str_('test'))`` now works. + +(`gh-15385 <https://github.com/numpy/numpy/pull/15385>`__) + +``subok`` option for ``numpy.copy`` +----------------------------------- +A new kwarg, ``subok``, was added to ``numpy.copy`` to allow users to toggle +the behavior of ``numpy.copy`` with respect to array subclasses. The default +value is ``False`` which is consistent with the behavior of ``numpy.copy`` for +previous numpy versions. To create a copy that preserves an array subclass with +``numpy.copy``, call ``np.copy(arr, subok=True)``. This addition better +documents that the default behavior of ``numpy.copy`` differs from the +``numpy.ndarray.copy`` method which respects array subclasses by default. + +(`gh-15685 <https://github.com/numpy/numpy/pull/15685>`__) + +``numpy.linalg.multi_dot`` now accepts an ``out`` argument +---------------------------------------------------------- + +``out`` can be used to avoid creating unnecessary copies of the final product +computed by ``numpy.linalg.multidot``. + +(`gh-15715 <https://github.com/numpy/numpy/pull/15715>`__) + +``keepdims`` parameter for ``numpy.count_nonzero`` +-------------------------------------------------- +The parameter ``keepdims`` was added to ``numpy.count_nonzero``. The +parameter has the same meaning as it does in reduction functions such +as ``numpy.sum`` or ``numpy.mean``. + +(`gh-15870 <https://github.com/numpy/numpy/pull/15870>`__) + +``equal_nan`` parameter for ``numpy.array_equal`` +------------------------------------------------- +The keyword argument ``equal_nan`` was added to ``numpy.array_equal``. +``equal_nan`` is a boolean value that toggles whether or not ``nan`` values are +considered equal in comparison (default is ``False``). This matches API used in +related functions such as ``numpy.isclose`` and ``numpy.allclose``. + +(`gh-16128 <https://github.com/numpy/numpy/pull/16128>`__) + + +Improvements +============ + +Improve detection of CPU features +================================= +Replace ``npy_cpu_supports`` which was a gcc specific mechanism to test support +of AVX with more general functions ``npy_cpu_init`` and ``npy_cpu_have``, and +expose the results via a ``NPY_CPU_HAVE`` c-macro as well as a python-level +``__cpu_features__`` dictionary. + +(`gh-13421 <https://github.com/numpy/numpy/pull/13421>`__) + +Use 64-bit integer size on 64-bit platforms in fallback lapack_lite +------------------------------------------------------------------- +Use 64-bit integer size on 64-bit platforms in the fallback LAPACK library, +which is used when the system has no LAPACK installed, allowing it to deal with +linear algebra for large arrays. + +(`gh-15218 <https://github.com/numpy/numpy/pull/15218>`__) + +Use AVX512 intrinsic to implement ``np.exp`` when input is ``np.float64`` +------------------------------------------------------------------------- +Use AVX512 intrinsic to implement ``np.exp`` when input is ``np.float64``, +which can improve the performance of ``np.exp`` with ``np.float64`` input 5-7x +faster than before. The ``_multiarray_umath.so`` module has grown about 63 KB +on linux64. + +(`gh-15648 <https://github.com/numpy/numpy/pull/15648>`__) + +Ability to disable madvise hugepages +------------------------------------ +On Linux NumPy has previously added support for madavise hugepages which can +improve performance for very large arrays. Unfortunately, on older Kernel +versions this led to performance regressions, thus by default the support has +been disabled on kernels before version 4.6. To override the default, you can +use the environment variable:: + + NUMPY_MADVISE_HUGEPAGE=0 + +or set it to 1 to force enabling support. Note that this only makes +a difference if the operating system is set up to use madvise +transparent hugepage. + +(`gh-15769 <https://github.com/numpy/numpy/pull/15769>`__) + +``numpy.einsum`` accepts NumPy ``int64`` type in subscript list +--------------------------------------------------------------- +There is no longer a type error thrown when ``numpy.einsum`` is passed +a NumPy ``int64`` array as its subscript list. + +(`gh-16080 <https://github.com/numpy/numpy/pull/16080>`__) + +``np.logaddexp2.identity`` changed to ``-inf`` +---------------------------------------------- +The ufunc ``~numpy.logaddexp2`` now has an identity of ``-inf``, allowing it to +be called on empty sequences. This matches the identity of ``~numpy.logaddexp``. + +(`gh-16102 <https://github.com/numpy/numpy/pull/16102>`__) + + +Changes +======= + +Remove handling of extra argument to ``__array__`` +-------------------------------------------------- +A code path and test have been in the code since NumPy 0.4 for a two-argument +variant of ``__array__(dtype=None, context=None)``. It was activated when +calling ``ufunc(op)`` or ``ufunc.reduce(op)`` if ``op.__array__`` existed. +However that variant is not documented, and it is not clear what the intention +was for its use. It has been removed. + +(`gh-15118 <https://github.com/numpy/numpy/pull/15118>`__) + +``numpy.random._bit_generator`` moved to ``numpy.random.bit_generator`` +----------------------------------------------------------------------- +In order to expose ``numpy.random.BitGenerator`` and +``numpy.random.SeedSequence`` to Cython, the ``_bitgenerator`` module is now +public as ``numpy.random.bit_generator`` + +Cython access to the random distributions is provided via a ``pxd`` file +------------------------------------------------------------------------ +``c_distributions.pxd`` provides access to the c functions behind many of the +random distributions from Cython, making it convenient to use and extend them. + +(`gh-15463 <https://github.com/numpy/numpy/pull/15463>`__) + +Fixed ``eigh`` and ``cholesky`` methods in ``numpy.random.multivariate_normal`` +------------------------------------------------------------------------------- +Previously, when passing ``method='eigh'`` or ``method='cholesky'``, +``numpy.random.multivariate_normal`` produced samples from the wrong +distribution. This is now fixed. + +(`gh-15872 <https://github.com/numpy/numpy/pull/15872>`__) + +Fixed the jumping implementation in ``MT19937.jumped`` +------------------------------------------------------ +This fix changes the stream produced from jumped MT19937 generators. It does +not affect the stream produced using ``RandomState`` or ``MT19937`` that +are directly seeded. + +The translation of the jumping code for the MT19937 contained a reversed loop +ordering. ``MT19937.jumped`` matches the Makoto Matsumoto's original +implementation of the Horner and Sliding Window jump methods. + +(`gh-16153 <https://github.com/numpy/numpy/pull/16153>`__) + diff --git a/doc/source/release/1.19.1-notes.rst b/doc/source/release/1.19.1-notes.rst new file mode 100644 index 000000000000..4fc5528f5916 --- /dev/null +++ b/doc/source/release/1.19.1-notes.rst @@ -0,0 +1,68 @@ +.. currentmodule:: numpy + +========================== +NumPy 1.19.1 Release Notes +========================== + +NumPy 1.19.1 fixes several bugs found in the 1.19.0 release, replaces several +functions deprecated in the upcoming Python-3.9 release, has improved support +for AIX, and has a number of development related updates to keep CI working +with recent upstream changes. + +This release supports Python 3.6-3.8. Cython >= 0.29.21 needs to be used when +building with Python 3.9 for testing purposes. + + +Contributors +============ + +A total of 15 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Abhinav Reddy + +* Anirudh Subramanian +* Antonio Larrosa + +* Charles Harris +* Chunlin Fang +* Eric Wieser +* Etienne Guesnet + +* Kevin Sheppard +* Matti Picus +* Raghuveer Devulapalli +* Roman Yurchak +* Ross Barnowski +* Sayed Adel +* Sebastian Berg +* Tyler Reddy + + +Pull requests merged +==================== + +A total of 25 pull requests were merged for this release. + +* `#16649 <https://github.com/numpy/numpy/pull/16649>`__: MAINT, CI: disable Shippable cache +* `#16652 <https://github.com/numpy/numpy/pull/16652>`__: MAINT: Replace `PyUString_GET_SIZE` with `PyUnicode_GetLength`. +* `#16654 <https://github.com/numpy/numpy/pull/16654>`__: REL: Fix outdated docs link +* `#16656 <https://github.com/numpy/numpy/pull/16656>`__: BUG: raise IEEE exception on AIX +* `#16672 <https://github.com/numpy/numpy/pull/16672>`__: BUG: Fix bug in AVX complex absolute while processing array of... +* `#16693 <https://github.com/numpy/numpy/pull/16693>`__: TST: Add extra debugging information to CPU features detection +* `#16703 <https://github.com/numpy/numpy/pull/16703>`__: BLD: Add CPU entry for Emscripten / WebAssembly +* `#16705 <https://github.com/numpy/numpy/pull/16705>`__: TST: Disable Python 3.9-dev testing. +* `#16714 <https://github.com/numpy/numpy/pull/16714>`__: MAINT: Disable use_hugepages in case of ValueError +* `#16724 <https://github.com/numpy/numpy/pull/16724>`__: BUG: Fix PyArray_SearchSorted signature. +* `#16768 <https://github.com/numpy/numpy/pull/16768>`__: MAINT: Fixes for deprecated functions in scalartypes.c.src +* `#16772 <https://github.com/numpy/numpy/pull/16772>`__: MAINT: Remove unneeded call to PyUnicode_READY +* `#16776 <https://github.com/numpy/numpy/pull/16776>`__: MAINT: Fix deprecated functions in scalarapi.c +* `#16779 <https://github.com/numpy/numpy/pull/16779>`__: BLD, ENH: Add RPATH support for AIX +* `#16780 <https://github.com/numpy/numpy/pull/16780>`__: BUG: Fix default fallback in genfromtxt +* `#16784 <https://github.com/numpy/numpy/pull/16784>`__: BUG: Added missing return after raising error in methods.c +* `#16795 <https://github.com/numpy/numpy/pull/16795>`__: BLD: update cython to 0.29.21 +* `#16832 <https://github.com/numpy/numpy/pull/16832>`__: MAINT: setuptools 49.2.0 emits a warning, avoid it +* `#16872 <https://github.com/numpy/numpy/pull/16872>`__: BUG: Validate output size in bin- and multinomial +* `#16875 <https://github.com/numpy/numpy/pull/16875>`__: BLD, MAINT: Pin setuptools +* `#16904 <https://github.com/numpy/numpy/pull/16904>`__: DOC: Reconstruct Testing Guideline. +* `#16905 <https://github.com/numpy/numpy/pull/16905>`__: TST, BUG: Re-raise MemoryError exception in test_large_zip's... +* `#16906 <https://github.com/numpy/numpy/pull/16906>`__: BUG,DOC: Fix bad MPL kwarg. +* `#16916 <https://github.com/numpy/numpy/pull/16916>`__: BUG: Fix string/bytes to complex assignment +* `#16922 <https://github.com/numpy/numpy/pull/16922>`__: REL: Prepare for NumPy 1.19.1 release diff --git a/doc/source/release/1.19.2-notes.rst b/doc/source/release/1.19.2-notes.rst new file mode 100644 index 000000000000..1267d5eb1e11 --- /dev/null +++ b/doc/source/release/1.19.2-notes.rst @@ -0,0 +1,57 @@ +.. currentmodule:: numpy + +========================== +NumPy 1.19.2 Release Notes +========================== + +NumPy 1.19.2 fixes several bugs, prepares for the upcoming Cython 3.x release. +and pins setuptools to keep distutils working while upstream modifications are +ongoing. The aarch64 wheels are built with the latest manylinux2014 release +that fixes the problem of differing page sizes used by different linux distros. + +This release supports Python 3.6-3.8. Cython >= 0.29.21 needs to be used when +building with Python 3.9 for testing purposes. + +There is a known problem with Windows 10 version=2004 and OpenBLAS svd that we +are trying to debug. If you are running that Windows version you should use a +NumPy version that links to the MKL library, earlier Windows versions are fine. + +Improvements +============ + +Add NumPy declarations for Cython 3.0 and later +----------------------------------------------- +The pxd declarations for Cython 3.0 were improved to avoid using deprecated +NumPy C-API features. Extension modules built with Cython 3.0+ that use NumPy +can now set the C macro ``NPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION`` to avoid +C compiler warnings about deprecated API usage. + +Contributors +============ + +A total of 8 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Charles Harris +* Matti Picus +* Pauli Virtanen +* Philippe Ombredanne + +* Sebastian Berg +* Stefan Behnel + +* Stephan Loyd + +* Zac Hatfield-Dodds + +Pull requests merged +==================== + +A total of 9 pull requests were merged for this release. + +* `#16959 <https://github.com/numpy/numpy/pull/16959>`__: TST: Change aarch64 to arm64 in travis.yml. +* `#16998 <https://github.com/numpy/numpy/pull/16998>`__: MAINT: Configure hypothesis in ``np.test()`` for determinism,... +* `#17000 <https://github.com/numpy/numpy/pull/17000>`__: BLD: pin setuptools < 49.2.0 +* `#17015 <https://github.com/numpy/numpy/pull/17015>`__: ENH: Add NumPy declarations to be used by Cython 3.0+ +* `#17125 <https://github.com/numpy/numpy/pull/17125>`__: BUG: Remove non-threadsafe sigint handling from fft calculation +* `#17243 <https://github.com/numpy/numpy/pull/17243>`__: BUG: core: fix ilp64 blas dot/vdot/... for strides > int32 max +* `#17244 <https://github.com/numpy/numpy/pull/17244>`__: DOC: Use SPDX license expressions with correct license +* `#17245 <https://github.com/numpy/numpy/pull/17245>`__: DOC: Fix the link to the quick-start in the old API functions +* `#17272 <https://github.com/numpy/numpy/pull/17272>`__: BUG: fix pickling of arrays larger than 2GiB diff --git a/doc/source/release/1.19.3-notes.rst b/doc/source/release/1.19.3-notes.rst new file mode 100644 index 000000000000..f1f1fd2b3e2f --- /dev/null +++ b/doc/source/release/1.19.3-notes.rst @@ -0,0 +1,46 @@ +.. currentmodule:: numpy + +========================== +NumPy 1.19.3 Release Notes +========================== + +NumPy 1.19.3 is a small maintenance release with two major improvements: + +- Python 3.9 binary wheels on all supported platforms. +- OpenBLAS fixes for Windows 10 version 2004 fmod bug. + +This release supports Python 3.6-3.9 and is linked with OpenBLAS 0.3.12 to avoid +some of the fmod problems on Windows 10 version 2004. Microsoft is aware of the +problem and users should upgrade when the fix becomes available, the fix here +is limited in scope. + +Contributors +============ + +A total of 8 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Charles Harris +* Chris Brown + +* Daniel Vanzo + +* E. Madison Bray + +* Hugo van Kemenade + +* Ralf Gommers +* Sebastian Berg +* @danbeibei + + +Pull requests merged +==================== + +A total of 10 pull requests were merged for this release. + +* `#17298 <https://github.com/numpy/numpy/pull/17298>`__: BLD: set upper versions for build dependencies +* `#17336 <https://github.com/numpy/numpy/pull/17336>`__: BUG: Set deprecated fields to null in PyArray_InitArrFuncs +* `#17446 <https://github.com/numpy/numpy/pull/17446>`__: ENH: Warn on unsupported Python 3.10+ +* `#17450 <https://github.com/numpy/numpy/pull/17450>`__: MAINT: Update test_requirements.txt. +* `#17522 <https://github.com/numpy/numpy/pull/17522>`__: ENH: Support for the NVIDIA HPC SDK nvfortran compiler +* `#17568 <https://github.com/numpy/numpy/pull/17568>`__: BUG: Cygwin Workaround for #14787 on affected platforms +* `#17647 <https://github.com/numpy/numpy/pull/17647>`__: BUG: Fix memory leak of buffer-info cache due to relaxed strides +* `#17652 <https://github.com/numpy/numpy/pull/17652>`__: MAINT: Backport openblas_support from master. +* `#17653 <https://github.com/numpy/numpy/pull/17653>`__: TST: Add Python 3.9 to the CI testing on Windows, Mac. +* `#17660 <https://github.com/numpy/numpy/pull/17660>`__: TST: Simplify source path names in test_extending. diff --git a/doc/source/release/1.19.4-notes.rst b/doc/source/release/1.19.4-notes.rst new file mode 100644 index 000000000000..e7c0863f4510 --- /dev/null +++ b/doc/source/release/1.19.4-notes.rst @@ -0,0 +1,30 @@ +.. currentmodule:: numpy + +========================== +NumPy 1.19.4 Release Notes +========================== + +NumPy 1.19.4 is a quick release to revert the OpenBLAS library version. It was +hoped that the 0.3.12 OpenBLAS version used in 1.19.3 would work around the +Microsoft fmod bug, but problems in some docker environments turned up. Instead, +1.19.4 will use the older library and run a sanity check on import, raising an +error if the problem is detected. Microsoft is aware of the problem and has +promised a fix, users should upgrade when it becomes available. + +This release supports Python 3.6-3.9 + +Contributors +============ + +A total of 1 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Charles Harris + +Pull requests merged +==================== + +A total of 2 pull requests were merged for this release. + +* `#17679 <https://github.com/numpy/numpy/pull/17679>`__: MAINT: Add check for Windows 10 version 2004 bug. +* `#17680 <https://github.com/numpy/numpy/pull/17680>`__: REV: Revert OpenBLAS to 1.19.2 version for 1.19.4 diff --git a/doc/source/release/1.19.5-notes.rst b/doc/source/release/1.19.5-notes.rst new file mode 100644 index 000000000000..048f2718cddf --- /dev/null +++ b/doc/source/release/1.19.5-notes.rst @@ -0,0 +1,42 @@ +.. currentmodule:: numpy + +========================== +NumPy 1.19.5 Release Notes +========================== + +NumPy 1.19.5 is a short bugfix release. Apart from fixing several bugs, the +main improvement is the update to OpenBLAS 0.3.13 that works around the windows +2004 bug while not breaking execution on other platforms. This release supports +Python 3.6-3.9 and is planned to be the last release in the 1.19.x cycle. + +Contributors +============ + +A total of 8 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Charles Harris +* Christoph Gohlke +* Matti Picus +* Raghuveer Devulapalli +* Sebastian Berg +* Simon Graham + +* Veniamin Petrenko + +* Bernie Gray + + +Pull requests merged +==================== + +A total of 11 pull requests were merged for this release. + +* `#17756 <https://github.com/numpy/numpy/pull/17756>`__: BUG: Fix segfault due to out of bound pointer in floatstatus... +* `#17774 <https://github.com/numpy/numpy/pull/17774>`__: BUG: fix np.timedelta64('nat').__format__ throwing an exception +* `#17775 <https://github.com/numpy/numpy/pull/17775>`__: BUG: Fixed file handle leak in array_tofile. +* `#17786 <https://github.com/numpy/numpy/pull/17786>`__: BUG: Raise recursion error during dimension discovery +* `#17917 <https://github.com/numpy/numpy/pull/17917>`__: BUG: Fix subarray dtype used with too large count in fromfile +* `#17918 <https://github.com/numpy/numpy/pull/17918>`__: BUG: 'bool' object has no attribute 'ndim' +* `#17919 <https://github.com/numpy/numpy/pull/17919>`__: BUG: ensure _UFuncNoLoopError can be pickled +* `#17924 <https://github.com/numpy/numpy/pull/17924>`__: BLD: use BUFFERSIZE=20 in OpenBLAS +* `#18026 <https://github.com/numpy/numpy/pull/18026>`__: BLD: update to OpenBLAS 0.3.13 +* `#18036 <https://github.com/numpy/numpy/pull/18036>`__: BUG: make a variable volatile to work around clang compiler bug +* `#18114 <https://github.com/numpy/numpy/pull/18114>`__: REL: Prepare for the NumPy 1.19.5 release. diff --git a/doc/source/release/1.20.0-notes.rst b/doc/source/release/1.20.0-notes.rst new file mode 100644 index 000000000000..494e4f19ee5b --- /dev/null +++ b/doc/source/release/1.20.0-notes.rst @@ -0,0 +1,1002 @@ +.. currentmodule:: numpy + +========================== +NumPy 1.20.0 Release Notes +========================== +This NumPy release is the largest so made to date, some 684 PRs contributed by +184 people have been merged. See the list of highlights below for more details. +The Python versions supported for this release are 3.7-3.9, support for Python +3.6 has been dropped. Highlights are + +- Annotations for NumPy functions. This work is ongoing and improvements can + be expected pending feedback from users. + +- Wider use of SIMD to increase execution speed of ufuncs. Much work has been + done in introducing universal functions that will ease use of modern + features across different hardware platforms. This work is ongoing. + +- Preliminary work in changing the dtype and casting implementations in order to + provide an easier path to extending dtypes. This work is ongoing but enough + has been done to allow experimentation and feedback. + +- Extensive documentation improvements comprising some 185 PR merges. This work + is ongoing and part of the larger project to improve NumPy's online presence + and usefulness to new users. + +- Further cleanups related to removing Python 2.7. This improves code + readability and removes technical debt. + +- Preliminary support for the upcoming Cython 3.0. + + +New functions +============= + +The random.Generator class has a new ``permuted`` function. +----------------------------------------------------------- +The new function differs from ``shuffle`` and ``permutation`` in that the +subarrays indexed by an axis are permuted rather than the axis being treated as +a separate 1-D array for every combination of the other indexes. For example, +it is now possible to permute the rows or columns of a 2-D array. + +(`gh-15121 <https://github.com/numpy/numpy/pull/15121>`__) + +``sliding_window_view`` provides a sliding window view for numpy arrays +----------------------------------------------------------------------- +`numpy.lib.stride_tricks.sliding_window_view` constructs views on numpy +arrays that offer a sliding or moving window access to the array. This allows +for the simple implementation of certain algorithms, such as running means. + +(`gh-17394 <https://github.com/numpy/numpy/pull/17394>`__) + +`numpy.broadcast_shapes` is a new user-facing function +------------------------------------------------------ +`~numpy.broadcast_shapes` gets the resulting shape from +broadcasting the given shape tuples against each other. + +.. code:: python + + >>> np.broadcast_shapes((1, 2), (3, 1)) + (3, 2) + + >>> np.broadcast_shapes(2, (3, 1)) + (3, 2) + + >>> np.broadcast_shapes((6, 7), (5, 6, 1), (7,), (5, 1, 7)) + (5, 6, 7) + +(`gh-17535 <https://github.com/numpy/numpy/pull/17535>`__) + + +Deprecations +============ + +Using the aliases of builtin types like ``np.int`` is deprecated +---------------------------------------------------------------- + +For a long time, ``np.int`` has been an alias of the builtin ``int``. This is +repeatedly a cause of confusion for newcomers, and existed mainly for historic +reasons. + +These aliases have been deprecated. The table below shows the full list of +deprecated aliases, along with their exact meaning. Replacing uses of items in +the first column with the contents of the second column will work identically +and silence the deprecation warning. + +The third column lists alternative NumPy names which may occasionally be +preferential. See also :ref:`basics.types` for additional details. + +================= ============ ================================================================== +Deprecated name Identical to NumPy scalar type names +================= ============ ================================================================== +``numpy.bool`` ``bool`` `numpy.bool_` +``numpy.int`` ``int`` `numpy.int_` (default), ``numpy.int64``, or ``numpy.int32`` +``numpy.float`` ``float`` `numpy.float64`, `numpy.float_`, `numpy.double` (equivalent) +``numpy.complex`` ``complex`` `numpy.complex128`, `numpy.complex_`, `numpy.cdouble` (equivalent) +``numpy.object`` ``object`` `numpy.object_` +``numpy.str`` ``str`` `numpy.str_` +``numpy.long`` ``int`` `numpy.int_` (C ``long``), `numpy.longlong` (largest integer type) +``numpy.unicode`` ``str`` `numpy.unicode_` +================= ============ ================================================================== + +To give a clear guideline for the vast majority of cases, for the types +``bool``, ``object``, ``str`` (and ``unicode``) using the plain version +is shorter and clear, and generally a good replacement. +For ``float`` and ``complex`` you can use ``float64`` and ``complex128`` +if you wish to be more explicit about the precision. + +For ``np.int`` a direct replacement with ``np.int_`` or ``int`` is also +good and will not change behavior, but the precision will continue to depend +on the computer and operating system. +If you want to be more explicit and review the current use, you have the +following alternatives: + +* ``np.int64`` or ``np.int32`` to specify the precision exactly. + This ensures that results cannot depend on the computer or operating system. +* ``np.int_`` or ``int`` (the default), but be aware that it depends on + the computer and operating system. +* The C types: ``np.cint`` (int), ``np.int_`` (long), ``np.longlong``. +* ``np.intp`` which is 32bit on 32bit machines 64bit on 64bit machines. + This can be the best type to use for indexing. + +When used with ``np.dtype(...)`` or ``dtype=...`` changing it to the +NumPy name as mentioned above will have no effect on the output. +If used as a scalar with:: + + np.float(123) + +changing it can subtly change the result. In this case, the Python version +``float(123)`` or ``int(12.)`` is normally preferable, although the NumPy +version may be useful for consistency with NumPy arrays (for example, +NumPy behaves differently for things like division by zero). + +(`gh-14882 <https://github.com/numpy/numpy/pull/14882>`__) + +Passing ``shape=None`` to functions with a non-optional shape argument is deprecated +------------------------------------------------------------------------------------ +Previously, this was an alias for passing ``shape=()``. +This deprecation is emitted by `PyArray_IntpConverter` in the C API. If your +API is intended to support passing ``None``, then you should check for ``None`` +prior to invoking the converter, so as to be able to distinguish ``None`` and +``()``. + +(`gh-15886 <https://github.com/numpy/numpy/pull/15886>`__) + +Indexing errors will be reported even when index result is empty +---------------------------------------------------------------- +In the future, NumPy will raise an IndexError when an +integer array index contains out of bound values even if a non-indexed +dimension is of length 0. This will now emit a DeprecationWarning. +This can happen when the array is previously empty, or an empty +slice is involved:: + + arr1 = np.zeros((5, 0)) + arr1[[20]] + arr2 = np.zeros((5, 5)) + arr2[[20], :0] + +Previously the non-empty index ``[20]`` was not checked for correctness. +It will now be checked causing a deprecation warning which will be turned +into an error. This also applies to assignments. + +(`gh-15900 <https://github.com/numpy/numpy/pull/15900>`__) + +Inexact matches for ``mode`` and ``searchside`` are deprecated +-------------------------------------------------------------- +Inexact and case insensitive matches for ``mode`` and ``searchside`` were valid +inputs earlier and will give a DeprecationWarning now. For example, below are +some example usages which are now deprecated and will give a +DeprecationWarning:: + + import numpy as np + arr = np.array([[3, 6, 6], [4, 5, 1]]) + # mode: inexact match + np.ravel_multi_index(arr, (7, 6), mode="clap") # should be "clip" + # searchside: inexact match + np.searchsorted(arr[0], 4, side='random') # should be "right" + +(`gh-16056 <https://github.com/numpy/numpy/pull/16056>`__) + +Deprecation of `numpy.dual` +--------------------------- +The module `numpy.dual` is deprecated. Instead of importing functions +from `numpy.dual`, the functions should be imported directly from NumPy +or SciPy. + +(`gh-16156 <https://github.com/numpy/numpy/pull/16156>`__) + +``outer`` and ``ufunc.outer`` deprecated for matrix +--------------------------------------------------- +``np.matrix`` use with `~numpy.outer` or generic ufunc outer +calls such as ``numpy.add.outer``. Previously, matrix was +converted to an array here. This will not be done in the future +requiring a manual conversion to arrays. + +(`gh-16232 <https://github.com/numpy/numpy/pull/16232>`__) + +Further Numeric Style types Deprecated +-------------------------------------- + +The remaining numeric-style type codes ``Bytes0``, ``Str0``, +``Uint32``, ``Uint64``, and ``Datetime64`` +have been deprecated. The lower-case variants should be used +instead. For bytes and string ``"S"`` and ``"U"`` +are further alternatives. + +(`gh-16554 <https://github.com/numpy/numpy/pull/16554>`__) + +The ``ndincr`` method of ``ndindex`` is deprecated +-------------------------------------------------- +The documentation has warned against using this function since NumPy 1.8. +Use ``next(it)`` instead of ``it.ndincr()``. + +(`gh-17233 <https://github.com/numpy/numpy/pull/17233>`__) + +ArrayLike objects which do not define ``__len__`` and ``__getitem__`` +--------------------------------------------------------------------- +Objects which define one of the protocols ``__array__``, +``__array_interface__``, or ``__array_struct__`` but are not sequences +(usually defined by having a ``__len__`` and ``__getitem__``) will behave +differently during array-coercion in the future. + +When nested inside sequences, such as ``np.array([array_like])``, these +were handled as a single Python object rather than an array. +In the future they will behave identically to:: + + np.array([np.array(array_like)]) + +This change should only have an effect if ``np.array(array_like)`` is not 0-D. +The solution to this warning may depend on the object: + +* Some array-likes may expect the new behaviour, and users can ignore the + warning. The object can choose to expose the sequence protocol to opt-in + to the new behaviour. +* For example, ``shapely`` will allow conversion to an array-like using + ``line.coords`` rather than ``np.asarray(line)``. Users may work around + the warning, or use the new convention when it becomes available. + +Unfortunately, using the new behaviour can only be achieved by +calling ``np.array(array_like)``. + +If you wish to ensure that the old behaviour remains unchanged, please create +an object array and then fill it explicitly, for example:: + + arr = np.empty(3, dtype=object) + arr[:] = [array_like1, array_like2, array_like3] + +This will ensure NumPy knows to not enter the array-like and use it as +a object instead. + +(`gh-17973 <https://github.com/numpy/numpy/pull/17973>`__) + + +Future Changes +============== + +Arrays cannot be using subarray dtypes +-------------------------------------- +Array creation and casting using ``np.array(arr, dtype)`` +and ``arr.astype(dtype)`` will use different logic when ``dtype`` +is a subarray dtype such as ``np.dtype("(2)i,")``. + +For such a ``dtype`` the following behaviour is true:: + + res = np.array(arr, dtype) + + res.dtype is not dtype + res.dtype is dtype.base + res.shape == arr.shape + dtype.shape + +But ``res`` is filled using the logic:: + + res = np.empty(arr.shape + dtype.shape, dtype=dtype.base) + res[...] = arr + +which uses incorrect broadcasting (and often leads to an error). +In the future, this will instead cast each element individually, +leading to the same result as:: + + res = np.array(arr, dtype=np.dtype(["f", dtype]))["f"] + +Which can normally be used to opt-in to the new behaviour. + +This change does not affect ``np.array(list, dtype="(2)i,")`` unless the +``list`` itself includes at least one array. In particular, the behaviour +is unchanged for a list of tuples. + +(`gh-17596 <https://github.com/numpy/numpy/pull/17596>`__) + + +Expired deprecations +==================== + +* The deprecation of numeric style type-codes ``np.dtype("Complex64")`` + (with upper case spelling), is expired. ``"Complex64"`` corresponded to + ``"complex128"`` and ``"Complex32"`` corresponded to ``"complex64"``. +* The deprecation of ``np.sctypeNA`` and ``np.typeNA`` is expired. Both + have been removed from the public API. Use ``np.typeDict`` instead. + + (`gh-16554 <https://github.com/numpy/numpy/pull/16554>`__) + +* The 14-year deprecation of ``np.ctypeslib.ctypes_load_library`` is expired. + Use :func:`~numpy.ctypeslib.load_library` instead, which is identical. + + (`gh-17116 <https://github.com/numpy/numpy/pull/17116>`__) + +Financial functions removed +--------------------------- +In accordance with NEP 32, the financial functions are removed +from NumPy 1.20. The functions that have been removed are ``fv``, +``ipmt``, ``irr``, ``mirr``, ``nper``, ``npv``, ``pmt``, ``ppmt``, +``pv``, and ``rate``. These functions are available in the +`numpy_financial <https://pypi.org/project/numpy-financial>`_ +library. + +(`gh-17067 <https://github.com/numpy/numpy/pull/17067>`__) + + +Compatibility notes +=================== + +``isinstance(dtype, np.dtype)`` and not ``type(dtype) is not np.dtype`` +----------------------------------------------------------------------- +NumPy dtypes are not direct instances of ``np.dtype`` anymore. Code that +may have used ``type(dtype) is np.dtype`` will always return ``False`` and +must be updated to use the correct version ``isinstance(dtype, np.dtype)``. + +This change also affects the C-side macro ``PyArray_DescrCheck`` if compiled +against a NumPy older than 1.16.6. If code uses this macro and wishes to +compile against an older version of NumPy, it must replace the macro +(see also `C API changes`_ section). + + +Same kind casting in concatenate with ``axis=None`` +--------------------------------------------------- +When `~numpy.concatenate` is called with ``axis=None``, +the flattened arrays were cast with ``unsafe``. Any other axis +choice uses "same kind". That different default +has been deprecated and "same kind" casting will be used +instead. The new ``casting`` keyword argument +can be used to retain the old behaviour. + +(`gh-16134 <https://github.com/numpy/numpy/pull/16134>`__) + +NumPy Scalars are cast when assigned to arrays +---------------------------------------------- + +When creating or assigning to arrays, in all relevant cases NumPy +scalars will now be cast identically to NumPy arrays. In particular +this changes the behaviour in some cases which previously raised an +error:: + + np.array([np.float64(np.nan)], dtype=np.int64) + +will succeed and return an undefined result (usually the smallest possible +integer). This also affects assignments:: + + arr[0] = np.float64(np.nan) + +At this time, NumPy retains the behaviour for:: + + np.array(np.float64(np.nan), dtype=np.int64) + +The above changes do not affect Python scalars:: + + np.array([float("NaN")], dtype=np.int64) + +remains unaffected (``np.nan`` is a Python ``float``, not a NumPy one). +Unlike signed integers, unsigned integers do not retain this special case, +since they always behaved more like casting. +The following code stops raising an error:: + + np.array([np.float64(np.nan)], dtype=np.uint64) + +To avoid backward compatibility issues, at this time assignment from +``datetime64`` scalar to strings of too short length remains supported. +This means that ``np.asarray(np.datetime64("2020-10-10"), dtype="S5")`` +succeeds now, when it failed before. In the long term this may be +deprecated or the unsafe cast may be allowed generally to make assignment +of arrays and scalars behave consistently. + + +Array coercion changes when Strings and other types are mixed +------------------------------------------------------------- + +When strings and other types are mixed, such as:: + + np.array(["string", np.float64(3.)], dtype="S") + +The results will change, which may lead to string dtypes with longer strings +in some cases. In particularly, if ``dtype="S"`` is not provided any numerical +value will lead to a string results long enough to hold all possible numerical +values. (e.g. "S32" for floats). Note that you should always provide +``dtype="S"`` when converting non-strings to strings. + +If ``dtype="S"`` is provided the results will be largely identical to before, +but NumPy scalars (not a Python float like ``1.0``), will still enforce +a uniform string length:: + + np.array([np.float64(3.)], dtype="S") # gives "S32" + np.array([3.0], dtype="S") # gives "S3" + +Previously the first version gave the same result as the second. + + +Array coercion restructure +-------------------------- + +Array coercion has been restructured. In general, this should not affect +users. In extremely rare corner cases where array-likes are nested:: + + np.array([array_like1]) + +Things will now be more consistent with:: + + np.array([np.array(array_like1)]) + +This can subtly change output for some badly defined array-likes. +One example for this are array-like objects which are not also sequences +of matching shape. +In NumPy 1.20, a warning will be given when an array-like is not also a +sequence (but behaviour remains identical, see deprecations). +If an array like is also a sequence (defines ``__getitem__`` and ``__len__``) +NumPy will now only use the result given by ``__array__``, +``__array_interface__``, or ``__array_struct__``. This will result in +differences when the (nested) sequence describes a different shape. + +(`gh-16200 <https://github.com/numpy/numpy/pull/16200>`__) + +Writing to the result of `numpy.broadcast_arrays` will export readonly buffers +------------------------------------------------------------------------------ + +In NumPy 1.17 `numpy.broadcast_arrays` started warning when the resulting array +was written to. This warning was skipped when the array was used through the +buffer interface (e.g. ``memoryview(arr)``). The same thing will now occur for the +two protocols ``__array_interface__``, and ``__array_struct__`` returning read-only +buffers instead of giving a warning. + +(`gh-16350 <https://github.com/numpy/numpy/pull/16350>`__) + +Numeric-style type names have been removed from type dictionaries +----------------------------------------------------------------- + +To stay in sync with the deprecation for ``np.dtype("Complex64")`` +and other numeric-style (capital case) types. These were removed +from ``np.sctypeDict`` and ``np.typeDict``. You should use +the lower case versions instead. Note that ``"Complex64"`` +corresponds to ``"complex128"`` and ``"Complex32"`` corresponds +to ``"complex64"``. The numpy style (new) versions, denote the full +size and not the size of the real/imaginary part. + +(`gh-16554 <https://github.com/numpy/numpy/pull/16554>`__) + +The ``operator.concat`` function now raises TypeError for array arguments +------------------------------------------------------------------------- +The previous behavior was to fall back to addition and add the two arrays, +which was thought to be unexpected behavior for a concatenation function. + +(`gh-16570 <https://github.com/numpy/numpy/pull/16570>`__) + +``nickname`` attribute removed from ABCPolyBase +----------------------------------------------- + +An abstract property ``nickname`` has been removed from ``ABCPolyBase`` as it +was no longer used in the derived convenience classes. +This may affect users who have derived classes from ``ABCPolyBase`` and +overridden the methods for representation and display, e.g. ``__str__``, +``__repr__``, ``_repr_latex``, etc. + +(`gh-16589 <https://github.com/numpy/numpy/pull/16589>`__) + +``float->timedelta`` and ``uint64->timedelta`` promotion will raise a TypeError +------------------------------------------------------------------------------- +Float and timedelta promotion consistently raises a TypeError. +``np.promote_types("float32", "m8")`` aligns with +``np.promote_types("m8", "float32")`` now and both raise a TypeError. +Previously, ``np.promote_types("float32", "m8")`` returned ``"m8"`` which +was considered a bug. + +Uint64 and timedelta promotion consistently raises a TypeError. +``np.promote_types("uint64", "m8")`` aligns with +``np.promote_types("m8", "uint64")`` now and both raise a TypeError. +Previously, ``np.promote_types("uint64", "m8")`` returned ``"m8"`` which +was considered a bug. + +(`gh-16592 <https://github.com/numpy/numpy/pull/16592>`__) + +``numpy.genfromtxt`` now correctly unpacks structured arrays +------------------------------------------------------------ +Previously, `numpy.genfromtxt` failed to unpack if it was called with +``unpack=True`` and a structured datatype was passed to the ``dtype`` argument +(or ``dtype=None`` was passed and a structured datatype was inferred). +For example:: + + >>> data = StringIO("21 58.0\n35 72.0") + >>> np.genfromtxt(data, dtype=None, unpack=True) + array([(21, 58.), (35, 72.)], dtype=[('f0', '<i8'), ('f1', '<f8')]) + +Structured arrays will now correctly unpack into a list of arrays, +one for each column:: + + >>> np.genfromtxt(data, dtype=None, unpack=True) + [array([21, 35]), array([58., 72.])] + +(`gh-16650 <https://github.com/numpy/numpy/pull/16650>`__) + +``mgrid``, ``r_``, etc. consistently return correct outputs for non-default precision input +------------------------------------------------------------------------------------------- +Previously, ``np.mgrid[np.float32(0.1):np.float32(0.35):np.float32(0.1),]`` +and ``np.r_[0:10:np.complex64(3j)]`` failed to return meaningful output. +This bug potentially affects `~numpy.mgrid`, `~numpy.ogrid`, `~numpy.r_`, +and `~numpy.c_` when an input with dtype other than the default +``float64`` and ``complex128`` and equivalent Python types were used. +The methods have been fixed to handle varying precision correctly. + +(`gh-16815 <https://github.com/numpy/numpy/pull/16815>`__) + +Boolean array indices with mismatching shapes now properly give ``IndexError`` +------------------------------------------------------------------------------ + +Previously, if a boolean array index matched the size of the indexed array but +not the shape, it was incorrectly allowed in some cases. In other cases, it +gave an error, but the error was incorrectly a ``ValueError`` with a message +about broadcasting instead of the correct ``IndexError``. + +For example, the following used to incorrectly give ``ValueError: operands +could not be broadcast together with shapes (2,2) (1,4)``: + +.. code:: python + + np.empty((2, 2))[np.array([[True, False, False, False]])] + +And the following used to incorrectly return ``array([], dtype=float64)``: + +.. code:: python + + np.empty((2, 2))[np.array([[False, False, False, False]])] + +Both now correctly give ``IndexError: boolean index did not match indexed +array along dimension 0; dimension is 2 but corresponding boolean dimension is +1``. + +(`gh-17010 <https://github.com/numpy/numpy/pull/17010>`__) + +Casting errors interrupt Iteration +---------------------------------- +When iterating while casting values, an error may stop the iteration +earlier than before. In any case, a failed casting operation always +returned undefined, partial results. Those may now be even more +undefined and partial. +For users of the ``NpyIter`` C-API such cast errors will now +cause the `iternext()` function to return 0 and thus abort +iteration. +Currently, there is no API to detect such an error directly. +It is necessary to check ``PyErr_Occurred()``, which +may be problematic in combination with ``NpyIter_Reset``. +These issues always existed, but new API could be added +if required by users. + +(`gh-17029 <https://github.com/numpy/numpy/pull/17029>`__) + +f2py generated code may return unicode instead of byte strings +-------------------------------------------------------------- +Some byte strings previously returned by f2py generated code may now be unicode +strings. This results from the ongoing Python2 -> Python3 cleanup. + +(`gh-17068 <https://github.com/numpy/numpy/pull/17068>`__) + +The first element of the ``__array_interface__["data"]`` tuple must be an integer +---------------------------------------------------------------------------------- +This has been the documented interface for many years, but there was still +code that would accept a byte string representation of the pointer address. +That code has been removed, passing the address as a byte string will now +raise an error. + +(`gh-17241 <https://github.com/numpy/numpy/pull/17241>`__) + +poly1d respects the dtype of all-zero argument +---------------------------------------------- +Previously, constructing an instance of ``poly1d`` with all-zero +coefficients would cast the coefficients to ``np.float64``. +This affected the output dtype of methods which construct +``poly1d`` instances internally, such as ``np.polymul``. + +(`gh-17577 <https://github.com/numpy/numpy/pull/17577>`__) + +The numpy.i file for swig is Python 3 only. +------------------------------------------- +Uses of Python 2.7 C-API functions have been updated to Python 3 only. Users +who need the old version should take it from an older version of NumPy. + +(`gh-17580 <https://github.com/numpy/numpy/pull/17580>`__) + +Void dtype discovery in ``np.array`` +------------------------------------ +In calls using ``np.array(..., dtype="V")``, ``arr.astype("V")``, +and similar a TypeError will now be correctly raised unless all +elements have the identical void length. An example for this is:: + + np.array([b"1", b"12"], dtype="V") + +Which previously returned an array with dtype ``"V2"`` which +cannot represent ``b"1"`` faithfully. + +(`gh-17706 <https://github.com/numpy/numpy/pull/17706>`__) + + +C API changes +============= + +The ``PyArray_DescrCheck`` macro is modified +-------------------------------------------- +The ``PyArray_DescrCheck`` macro has been updated since NumPy 1.16.6 to be:: + + #define PyArray_DescrCheck(op) PyObject_TypeCheck(op, &PyArrayDescr_Type) + +Starting with NumPy 1.20 code that is compiled against an earlier version +will be API incompatible with NumPy 1.20. +The fix is to either compile against 1.16.6 (if the NumPy 1.16 release is +the oldest release you wish to support), or manually inline the macro by +replacing it with the new definition:: + + PyObject_TypeCheck(op, &PyArrayDescr_Type) + +which is compatible with all NumPy versions. + + +Size of ``np.ndarray`` and ``np.void_`` changed +----------------------------------------------- +The size of the ``PyArrayObject`` and ``PyVoidScalarObject`` +structures have changed. The following header definition has been +removed:: + + #define NPY_SIZEOF_PYARRAYOBJECT (sizeof(PyArrayObject_fields)) + +since the size must not be considered a compile time constant: it will +change for different runtime versions of NumPy. + +The most likely relevant use are potential subclasses written in C which +will have to be recompiled and should be updated. Please see the +documentation for :c:type:`PyArrayObject` for more details and contact +the NumPy developers if you are affected by this change. + +NumPy will attempt to give a graceful error but a program expecting a +fixed structure size may have undefined behaviour and likely crash. + +(`gh-16938 <https://github.com/numpy/numpy/pull/16938>`__) + + +New Features +============ + +``where`` keyword argument for ``numpy.all`` and ``numpy.any`` functions +------------------------------------------------------------------------ +The keyword argument ``where`` is added and allows to only consider specified +elements or subaxes from an array in the Boolean evaluation of ``all`` and +``any``. This new keyword is available to the functions ``all`` and ``any`` +both via ``numpy`` directly or in the methods of ``numpy.ndarray``. + +Any broadcastable Boolean array or a scalar can be set as ``where``. It +defaults to ``True`` to evaluate the functions for all elements in an array if +``where`` is not set by the user. Examples are given in the documentation of +the functions. + + +``where`` keyword argument for ``numpy`` functions ``mean``, ``std``, ``var`` +----------------------------------------------------------------------------- +The keyword argument ``where`` is added and allows to limit the scope in the +calculation of ``mean``, ``std`` and ``var`` to only a subset of elements. It +is available both via ``numpy`` directly or in the methods of +``numpy.ndarray``. + +Any broadcastable Boolean array or a scalar can be set as ``where``. It +defaults to ``True`` to evaluate the functions for all elements in an array if +``where`` is not set by the user. Examples are given in the documentation of +the functions. + +(`gh-15852 <https://github.com/numpy/numpy/pull/15852>`__) + +``norm=backward``, ``forward`` keyword options for ``numpy.fft`` functions +-------------------------------------------------------------------------- +The keyword argument option ``norm=backward`` is added as an alias for ``None`` +and acts as the default option; using it has the direct transforms unscaled +and the inverse transforms scaled by ``1/n``. + +Using the new keyword argument option ``norm=forward`` has the direct +transforms scaled by ``1/n`` and the inverse transforms unscaled (i.e. exactly +opposite to the default option ``norm=backward``). + +(`gh-16476 <https://github.com/numpy/numpy/pull/16476>`__) + +NumPy is now typed +------------------ +Type annotations have been added for large parts of NumPy. There is +also a new `numpy.typing` module that contains useful types for +end-users. The currently available types are + +- ``ArrayLike``: for objects that can be coerced to an array +- ``DtypeLike``: for objects that can be coerced to a dtype + +(`gh-16515 <https://github.com/numpy/numpy/pull/16515>`__) + +``numpy.typing`` is accessible at runtime +----------------------------------------- +The types in ``numpy.typing`` can now be imported at runtime. Code +like the following will now work: + +.. code:: python + + from numpy.typing import ArrayLike + x: ArrayLike = [1, 2, 3, 4] + +(`gh-16558 <https://github.com/numpy/numpy/pull/16558>`__) + +New ``__f2py_numpy_version__`` attribute for f2py generated modules. +-------------------------------------------------------------------- +Because f2py is released together with NumPy, ``__f2py_numpy_version__`` +provides a way to track the version f2py used to generate the module. + +(`gh-16594 <https://github.com/numpy/numpy/pull/16594>`__) + +``mypy`` tests can be run via runtests.py +----------------------------------------- +Currently running mypy with the NumPy stubs configured requires +either: + +* Installing NumPy +* Adding the source directory to MYPYPATH and linking to the ``mypy.ini`` + +Both options are somewhat inconvenient, so add a ``--mypy`` option to runtests +that handles setting things up for you. This will also be useful in the future +for any typing codegen since it will ensure the project is built before type +checking. + +(`gh-17123 <https://github.com/numpy/numpy/pull/17123>`__) + +Negation of user defined BLAS/LAPACK detection order +---------------------------------------------------- +`~numpy.distutils` allows negation of libraries when determining BLAS/LAPACK +libraries. +This may be used to remove an item from the library resolution phase, i.e. +to disallow NetLIB libraries one could do: + +.. code:: bash + + NPY_BLAS_ORDER='^blas' NPY_LAPACK_ORDER='^lapack' python setup.py build + +That will use any of the accelerated libraries instead. + +(`gh-17219 <https://github.com/numpy/numpy/pull/17219>`__) + +Allow passing optimizations arguments to asv build +-------------------------------------------------- +It is now possible to pass ``-j``, ``--cpu-baseline``, ``--cpu-dispatch`` and +``--disable-optimization`` flags to ASV build when the ``--bench-compare`` +argument is used. + +(`gh-17284 <https://github.com/numpy/numpy/pull/17284>`__) + +The NVIDIA HPC SDK nvfortran compiler is now supported +------------------------------------------------------ +Support for the nvfortran compiler, a version of pgfortran, has been added. + +(`gh-17344 <https://github.com/numpy/numpy/pull/17344>`__) + +``dtype`` option for ``cov`` and ``corrcoef`` +--------------------------------------------- +The ``dtype`` option is now available for `numpy.cov` and `numpy.corrcoef`. +It specifies which data-type the returned result should have. +By default the functions still return a `numpy.float64` result. + +(`gh-17456 <https://github.com/numpy/numpy/pull/17456>`__) + + +Improvements +============ + +Improved string representation for polynomials (``__str__``) +------------------------------------------------------------ + +The string representation (``__str__``) of all six polynomial types in +`numpy.polynomial` has been updated to give the polynomial as a mathematical +expression instead of an array of coefficients. Two package-wide formats for +the polynomial expressions are available - one using Unicode characters for +superscripts and subscripts, and another using only ASCII characters. + +(`gh-15666 <https://github.com/numpy/numpy/pull/15666>`__) + +Remove the Accelerate library as a candidate LAPACK library +----------------------------------------------------------- +Apple no longer supports Accelerate. Remove it. + +(`gh-15759 <https://github.com/numpy/numpy/pull/15759>`__) + +Object arrays containing multi-line objects have a more readable ``repr`` +------------------------------------------------------------------------- +If elements of an object array have a ``repr`` containing new lines, then the +wrapped lines will be aligned by column. Notably, this improves the ``repr`` of +nested arrays:: + + >>> np.array([np.eye(2), np.eye(3)], dtype=object) + array([array([[1., 0.], + [0., 1.]]), + array([[1., 0., 0.], + [0., 1., 0.], + [0., 0., 1.]])], dtype=object) + +(`gh-15997 <https://github.com/numpy/numpy/pull/15997>`__) + +Concatenate supports providing an output dtype +---------------------------------------------- +Support was added to `~numpy.concatenate` to provide +an output ``dtype`` and ``casting`` using keyword +arguments. The ``dtype`` argument cannot be provided +in conjunction with the ``out`` one. + +(`gh-16134 <https://github.com/numpy/numpy/pull/16134>`__) + +Thread safe f2py callback functions +----------------------------------- + +Callback functions in f2py are now thread safe. + +(`gh-16519 <https://github.com/numpy/numpy/pull/16519>`__) + +`numpy.core.records.fromfile` now supports file-like objects +------------------------------------------------------------ +`numpy.rec.fromfile` can now use file-like objects, for instance +:py:class:`io.BytesIO` + +(`gh-16675 <https://github.com/numpy/numpy/pull/16675>`__) + +RPATH support on AIX added to distutils +--------------------------------------- +This allows SciPy to be built on AIX. + +(`gh-16710 <https://github.com/numpy/numpy/pull/16710>`__) + +Use f90 compiler specified by the command line args +--------------------------------------------------- + +The compiler command selection for Fortran Portland Group Compiler is changed +in `numpy.distutils.fcompiler`. This only affects the linking command. This +forces the use of the executable provided by the command line option (if +provided) instead of the pgfortran executable. If no executable is provided to +the command line option it defaults to the pgf90 executable, which is an alias +for pgfortran according to the PGI documentation. + +(`gh-16730 <https://github.com/numpy/numpy/pull/16730>`__) + +Add NumPy declarations for Cython 3.0 and later +----------------------------------------------- + +The pxd declarations for Cython 3.0 were improved to avoid using deprecated +NumPy C-API features. Extension modules built with Cython 3.0+ that use NumPy +can now set the C macro ``NPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION`` to avoid +C compiler warnings about deprecated API usage. + +(`gh-16986 <https://github.com/numpy/numpy/pull/16986>`__) + +Make the window functions exactly symmetric +------------------------------------------- +Make sure the window functions provided by NumPy are symmetric. There were +previously small deviations from symmetry due to numerical precision that are +now avoided by better arrangement of the computation. + +(`gh-17195 <https://github.com/numpy/numpy/pull/17195>`__) + + +Performance improvements and changes +==================================== + +Enable multi-platform SIMD compiler optimizations +------------------------------------------------- + +A series of improvements for NumPy infrastructure to pave the way to +**NEP-38**, that can be summarized as follow: + +- **New Build Arguments** + + - ``--cpu-baseline`` to specify the minimal set of required + optimizations, default value is ``min`` which provides the minimum + CPU features that can safely run on a wide range of users + platforms. + + - ``--cpu-dispatch`` to specify the dispatched set of additional + optimizations, default value is ``max -xop -fma4`` which enables + all CPU features, except for AMD legacy features. + + - ``--disable-optimization`` to explicitly disable the whole new + improvements, It also adds a new **C** compiler #definition + called ``NPY_DISABLE_OPTIMIZATION`` which it can be used as + guard for any SIMD code. + +- **Advanced CPU dispatcher** + + A flexible cross-architecture CPU dispatcher built on the top of + Python/Numpy distutils, support all common compilers with a wide range of + CPU features. + + The new dispatcher requires a special file extension ``*.dispatch.c`` to + mark the dispatch-able **C** sources. These sources have the ability to be + compiled multiple times so that each compilation process represents certain + CPU features and provides different #definitions and flags that affect the + code paths. + +- **New auto-generated C header ``core/src/common/_cpu_dispatch.h``** + + This header is generated by the distutils module ``ccompiler_opt``, and + contains all the #definitions and headers of instruction sets, that had been + configured through command arguments '--cpu-baseline' and '--cpu-dispatch'. + +- **New C header ``core/src/common/npy_cpu_dispatch.h``** + + This header contains all utilities that required for the whole CPU + dispatching process, it also can be considered as a bridge linking the new + infrastructure work with NumPy CPU runtime detection. + +- **Add new attributes to NumPy umath module(Python level)** + + - ``__cpu_baseline__`` a list contains the minimal set of required + optimizations that supported by the compiler and platform according to the + specified values to command argument '--cpu-baseline'. + + - ``__cpu_dispatch__`` a list contains the dispatched set of additional + optimizations that supported by the compiler and platform according to the + specified values to command argument '--cpu-dispatch'. + +- **Print the supported CPU features during the run of PytestTester** + +(`gh-13516 <https://github.com/numpy/numpy/pull/13516>`__) + + +Changes +======= + +Changed behavior of ``divmod(1., 0.)`` and related functions +------------------------------------------------------------ +The changes also assure that different compiler versions have the same behavior +for nan or inf usages in these operations. This was previously compiler +dependent, we now force the invalid and divide by zero flags, making the +results the same across compilers. For example, gcc-5, gcc-8, or gcc-9 now +result in the same behavior. The changes are tabulated below: + +.. list-table:: Summary of New Behavior + :widths: auto + :header-rows: 1 + + * - Operator + - Old Warning + - New Warning + - Old Result + - New Result + - Works on MacOS + * - np.divmod(1.0, 0.0) + - Invalid + - Invalid and Dividebyzero + - nan, nan + - inf, nan + - Yes + * - np.fmod(1.0, 0.0) + - Invalid + - Invalid + - nan + - nan + - No? Yes + * - np.floor_divide(1.0, 0.0) + - Invalid + - Dividebyzero + - nan + - inf + - Yes + * - np.remainder(1.0, 0.0) + - Invalid + - Invalid + - nan + - nan + - Yes + +(`gh-16161 <https://github.com/numpy/numpy/pull/16161>`__) + +``np.linspace`` on integers now uses floor +------------------------------------------ +When using a ``int`` dtype in `numpy.linspace`, previously float values would +be rounded towards zero. Now `numpy.floor` is used instead, which rounds toward +``-inf``. This changes the results for negative values. For example, the +following would previously give:: + + >>> np.linspace(-3, 1, 8, dtype=int) + array([-3, -2, -1, -1, 0, 0, 0, 1]) + +and now results in:: + + >>> np.linspace(-3, 1, 8, dtype=int) + array([-3, -3, -2, -2, -1, -1, 0, 1]) + +The former result can still be obtained with:: + + >>> np.linspace(-3, 1, 8).astype(int) + array([-3, -2, -1, -1, 0, 0, 0, 1]) + +(`gh-16841 <https://github.com/numpy/numpy/pull/16841>`__) + diff --git a/doc/source/release/1.20.1-notes.rst b/doc/source/release/1.20.1-notes.rst new file mode 100644 index 000000000000..f95b5847ddcd --- /dev/null +++ b/doc/source/release/1.20.1-notes.rst @@ -0,0 +1,53 @@ +.. currentmodule:: numpy + +========================== +NumPy 1.20.1 Release Notes +========================== + +NumPy 1,20.1 is a rapid bugfix release fixing several bugs and regressions +reported after the 1.20.0 release. + + +Highlights +========== + +- The distutils bug that caused problems with downstream projects is fixed. +- The ``random.shuffle`` regression is fixed. + + +Contributors +============ + +A total of 8 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Bas van Beek +* Charles Harris +* Nicholas McKibben + +* Pearu Peterson +* Ralf Gommers +* Sebastian Berg +* Tyler Reddy +* @Aerysv + + + +Pull requests merged +==================== + +A total of 15 pull requests were merged for this release. + +* `#18306 <https://github.com/numpy/numpy/pull/18306>`__: MAINT: Add missing placeholder annotations +* `#18310 <https://github.com/numpy/numpy/pull/18310>`__: BUG: Fix typo in ``numpy.__init__.py`` +* `#18326 <https://github.com/numpy/numpy/pull/18326>`__: BUG: don't mutate list of fake libraries while iterating over... +* `#18327 <https://github.com/numpy/numpy/pull/18327>`__: MAINT: gracefully shuffle memoryviews +* `#18328 <https://github.com/numpy/numpy/pull/18328>`__: BUG: Use C linkage for random distributions +* `#18336 <https://github.com/numpy/numpy/pull/18336>`__: CI: fix when GitHub Actions builds trigger, and allow ci skips +* `#18337 <https://github.com/numpy/numpy/pull/18337>`__: BUG: Allow unmodified use of isclose, allclose, etc. with timedelta +* `#18345 <https://github.com/numpy/numpy/pull/18345>`__: BUG: Allow pickling all relevant DType types/classes +* `#18351 <https://github.com/numpy/numpy/pull/18351>`__: BUG: Fix missing signed_char dependency. Closes #18335. +* `#18352 <https://github.com/numpy/numpy/pull/18352>`__: DOC: Change license date 2020 -> 2021 +* `#18353 <https://github.com/numpy/numpy/pull/18353>`__: CI: CircleCI seems to occasionally time out, increase the limit +* `#18354 <https://github.com/numpy/numpy/pull/18354>`__: BUG: Fix f2py bugs when wrapping F90 subroutines. +* `#18356 <https://github.com/numpy/numpy/pull/18356>`__: MAINT: crackfortran regex simplify +* `#18357 <https://github.com/numpy/numpy/pull/18357>`__: BUG: threads.h existence test requires GLIBC > 2.12. +* `#18359 <https://github.com/numpy/numpy/pull/18359>`__: REL: Prepare for the NumPy 1.20.1 release. diff --git a/doc/source/release/1.20.2-notes.rst b/doc/source/release/1.20.2-notes.rst new file mode 100644 index 000000000000..10d39f7f6389 --- /dev/null +++ b/doc/source/release/1.20.2-notes.rst @@ -0,0 +1,48 @@ +.. currentmodule:: numpy + +========================== +NumPy 1.20.2 Release Notes +========================== + +NumPy 1.20.2 is a bugfix release containing several fixes merged to the main +branch after the NumPy 1.20.1 release. + +Contributors +============ + +A total of 7 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Allan Haldane +* Bas van Beek +* Charles Harris +* Christoph Gohlke +* Mateusz Sokół + +* Michael Lamparski +* Sebastian Berg + +Pull requests merged +==================== + +A total of 20 pull requests were merged for this release. + +* `#18382 <https://github.com/numpy/numpy/pull/18382>`__: MAINT: Update f2py from master. +* `#18459 <https://github.com/numpy/numpy/pull/18459>`__: BUG: ``diagflat`` could overflow on windows or 32-bit platforms +* `#18460 <https://github.com/numpy/numpy/pull/18460>`__: BUG: Fix refcount leak in f2py ``complex_double_from_pyobj``. +* `#18461 <https://github.com/numpy/numpy/pull/18461>`__: BUG: Fix tiny memory leaks when ``like=`` overrides are used +* `#18462 <https://github.com/numpy/numpy/pull/18462>`__: BUG: Remove temporary change of descr/flags in VOID functions +* `#18469 <https://github.com/numpy/numpy/pull/18469>`__: BUG: Segfault in nditer buffer dealloc for Object arrays +* `#18485 <https://github.com/numpy/numpy/pull/18485>`__: BUG: Remove suspicious type casting +* `#18486 <https://github.com/numpy/numpy/pull/18486>`__: BUG: remove nonsensical comparison of pointer < 0 +* `#18487 <https://github.com/numpy/numpy/pull/18487>`__: BUG: verify pointer against NULL before using it +* `#18488 <https://github.com/numpy/numpy/pull/18488>`__: BUG: check if PyArray_malloc succeeded +* `#18546 <https://github.com/numpy/numpy/pull/18546>`__: BUG: incorrect error fallthrough in nditer +* `#18559 <https://github.com/numpy/numpy/pull/18559>`__: CI: Backport CI fixes from main. +* `#18599 <https://github.com/numpy/numpy/pull/18599>`__: MAINT: Add annotations for `dtype.__getitem__`, `__mul__` and... +* `#18611 <https://github.com/numpy/numpy/pull/18611>`__: BUG: NameError in numpy.distutils.fcompiler.compaq +* `#18612 <https://github.com/numpy/numpy/pull/18612>`__: BUG: Fixed ``where`` keyword for ``np.mean`` & ``np.var`` methods +* `#18617 <https://github.com/numpy/numpy/pull/18617>`__: CI: Update apt package list before Python install +* `#18636 <https://github.com/numpy/numpy/pull/18636>`__: MAINT: Ensure that re-exported sub-modules are properly annotated +* `#18638 <https://github.com/numpy/numpy/pull/18638>`__: BUG: Fix ma coercion list-of-ma-arrays if they do not cast to... +* `#18661 <https://github.com/numpy/numpy/pull/18661>`__: BUG: Fix small valgrind-found issues +* `#18671 <https://github.com/numpy/numpy/pull/18671>`__: BUG: Fix small issues found with pytest-leaks diff --git a/doc/source/release/1.20.3-notes.rst b/doc/source/release/1.20.3-notes.rst new file mode 100644 index 000000000000..8c25b3cc3215 --- /dev/null +++ b/doc/source/release/1.20.3-notes.rst @@ -0,0 +1,43 @@ +.. currentmodule:: numpy + +========================== +NumPy 1.20.3 Release Notes +========================== + +NumPy 1.20.3 is a bugfix release containing several fixes merged to the main +branch after the NumPy 1.20.2 release. + +Contributors +============ + +A total of 7 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Anne Archibald +* Bas van Beek +* Charles Harris +* Dong Keun Oh + +* Kamil Choudhury + +* Sayed Adel +* Sebastian Berg + +Pull requests merged +==================== + +A total of 15 pull requests were merged for this release. + +* `#18763 <https://github.com/numpy/numpy/pull/18763>`__: BUG: Correct ``datetime64`` missing type overload for ``datetime.date``... +* `#18764 <https://github.com/numpy/numpy/pull/18764>`__: MAINT: Remove ``__all__`` in favor of explicit re-exports +* `#18768 <https://github.com/numpy/numpy/pull/18768>`__: BLD: Strip extra newline when dumping gfortran version on MacOS +* `#18769 <https://github.com/numpy/numpy/pull/18769>`__: BUG: fix segfault in object/longdouble operations +* `#18794 <https://github.com/numpy/numpy/pull/18794>`__: MAINT: Use towncrier build explicitly +* `#18887 <https://github.com/numpy/numpy/pull/18887>`__: MAINT: Relax certain integer-type constraints +* `#18915 <https://github.com/numpy/numpy/pull/18915>`__: MAINT: Remove unsafe unions and ABCs from return-annotations +* `#18921 <https://github.com/numpy/numpy/pull/18921>`__: MAINT: Allow more recursion depth for scalar tests. +* `#18922 <https://github.com/numpy/numpy/pull/18922>`__: BUG: Initialize the full nditer buffer in case of error +* `#18923 <https://github.com/numpy/numpy/pull/18923>`__: BLD: remove unnecessary flag ``-faltivec`` on macOS +* `#18924 <https://github.com/numpy/numpy/pull/18924>`__: MAINT, CI: treats _SIMD module build warnings as errors through... +* `#18925 <https://github.com/numpy/numpy/pull/18925>`__: BUG: for MINGW, threads.h existence test requires GLIBC > 2.12 +* `#18941 <https://github.com/numpy/numpy/pull/18941>`__: BUG: Make changelog recognize gh- as a PR number prefix. +* `#18948 <https://github.com/numpy/numpy/pull/18948>`__: REL, DOC: Prepare for the NumPy 1.20.3 release. +* `#18953 <https://github.com/numpy/numpy/pull/18953>`__: BUG: Fix failing mypy test in 1.20.x. diff --git a/doc/source/release/1.21.0-notes.rst b/doc/source/release/1.21.0-notes.rst new file mode 100644 index 000000000000..88a4503de281 --- /dev/null +++ b/doc/source/release/1.21.0-notes.rst @@ -0,0 +1,579 @@ +.. currentmodule:: numpy + +========================== +NumPy 1.21.0 Release Notes +========================== +The NumPy 1.21.0 release highlights are + +* continued SIMD work covering more functions and platforms, +* initial work on the new dtype infrastructure and casting, +* universal2 wheels for Python 3.8 and Python 3.9 on Mac, +* improved documentation, +* improved annotations, +* new ``PCG64DXSM`` bitgenerator for random numbers. + +In addition there are the usual large number of bug fixes and other improvements. + +The Python versions supported for this release are 3.7-3.9. Official support +for Python 3.10 will be added when it is released. + +.. warning:: + There are unresolved problems compiling NumPy 1.20.0 with gcc-11.1. + + * Optimization level `-O3` results in many incorrect warnings when + running the tests. + * On some hardware NumPY will hang in an infinite loop. + + + + + +New functions +============= + +.. currentmodule:: numpy.random + +Add `PCG64DXSM` `BitGenerator` +------------------------------ + +Uses of the ``PCG64`` ``BitGenerator`` in a massively-parallel context have been +shown to have statistical weaknesses that were not apparent at the first +release in numpy 1.17. Most users will never observe this weakness and are +safe to continue to use ``PCG64``. We have introduced a new ``PCG64DXSM`` +``BitGenerator`` that will eventually become the new default ``BitGenerator`` +implementation used by ``default_rng`` in future releases. ``PCG64DXSM`` solves +the statistical weakness while preserving the performance and the features of +``PCG64``. + +See :ref:`upgrading-pcg64` for more details. + +.. currentmodule:: numpy + +(`gh-18906 <https://github.com/numpy/numpy/pull/18906>`__) + + +Expired deprecations +==================== + +* The ``shape`` argument `~numpy.unravel_index` cannot be passed + as ``dims`` keyword argument anymore. (Was deprecated in NumPy 1.16.) + + (`gh-17900 <https://github.com/numpy/numpy/pull/17900>`__) + +* The function ``PyUFunc_GenericFunction`` has been disabled. + It was deprecated in NumPy 1.19. Users should call the ufunc + directly using the Python API. + + (`gh-18697 <https://github.com/numpy/numpy/pull/18697>`__) + +* The function ``PyUFunc_SetUsesArraysAsData`` has been disabled. + It was deprecated in NumPy 1.19. + + (`gh-18697 <https://github.com/numpy/numpy/pull/18697>`__) + +* The class ``PolyBase`` has been removed (deprecated in numpy 1.9.0). Please + use the abstract ``ABCPolyBase`` class instead. + + (`gh-18963 <https://github.com/numpy/numpy/pull/18963>`__) + +* The unused ``PolyError`` and ``PolyDomainError`` exceptions are + removed. + + (`gh-18963 <https://github.com/numpy/numpy/pull/18963>`__) + + +Deprecations +============ + +The ``.dtype`` attribute must return a ``dtype`` +------------------------------------------------ + +A ``DeprecationWarning`` is now given if the ``.dtype`` attribute +of an object passed into ``np.dtype`` or as a ``dtype=obj`` argument +is not a dtype. NumPy will stop attempting to recursively coerce the +result of ``.dtype``. + +(`gh-13578 <https://github.com/numpy/numpy/pull/13578>`__) + +Inexact matches for ``numpy.convolve`` and ``numpy.correlate`` are deprecated +----------------------------------------------------------------------------- + +`~numpy.convolve` and `~numpy.correlate` now emit a warning when there are case +insensitive and/or inexact matches found for ``mode`` argument in the functions. +Pass full ``"same"``, ``"valid"``, ``"full"`` strings instead of +``"s"``, ``"v"``, ``"f"`` for the ``mode`` argument. + +(`gh-17492 <https://github.com/numpy/numpy/pull/17492>`__) + +``np.typeDict`` has been formally deprecated +-------------------------------------------- +``np.typeDict`` is a deprecated alias for ``np.sctypeDict`` and +has been so for over 14 years (6689502_). +A deprecation warning will now be issued whenever getting ``np.typeDict``. + +.. _6689502: https://github.com/numpy/numpy/commit/668950285c407593a368336ff2e737c5da84af7d + +(`gh-17586 <https://github.com/numpy/numpy/pull/17586>`__) + +Exceptions will be raised during array-like creation +---------------------------------------------------- +When an object raised an exception during access of the special +attributes ``__array__`` or ``__array_interface__``, this exception +was usually ignored. +A warning is now given when the exception is anything but AttributeError. +To silence the warning, the type raising the exception has to be adapted +to raise an ``AttributeError``. + +(`gh-19001 <https://github.com/numpy/numpy/pull/19001>`__) + +Four ``ndarray.ctypes`` methods have been deprecated +---------------------------------------------------- +Four methods of the `ndarray.ctypes` object have been deprecated, +as they are (undocumentated) implementation artifacts of their respective +properties. + +The methods in question are: + +* ``_ctypes.get_data`` (use ``_ctypes.data`` instead) +* ``_ctypes.get_shape`` (use ``_ctypes.shape`` instead) +* ``_ctypes.get_strides`` (use ``_ctypes.strides`` instead) +* ``_ctypes.get_as_parameter`` (use ``_ctypes._as_parameter_`` instead) + +(`gh-19031 <https://github.com/numpy/numpy/pull/19031>`__) + + +Expired deprecations +==================== + +* The ``shape`` argument `numpy.unravel_index` cannot be passed + as ``dims`` keyword argument anymore. (Was deprecated in NumPy 1.16.) + + (`gh-17900 <https://github.com/numpy/numpy/pull/17900>`__) + +* The function ``PyUFunc_GenericFunction`` has been disabled. + It was deprecated in NumPy 1.19. Users should call the ufunc + directly using the Python API. + + (`gh-18697 <https://github.com/numpy/numpy/pull/18697>`__) + +* The function ``PyUFunc_SetUsesArraysAsData`` has been disabled. + It was deprecated in NumPy 1.19. + + (`gh-18697 <https://github.com/numpy/numpy/pull/18697>`__) + +Remove deprecated ``PolyBase`` and unused ``PolyError`` and ``PolyDomainError`` +------------------------------------------------------------------------------- + +The class ``PolyBase`` has been removed (deprecated in numpy 1.9.0). Please use +the abstract ``ABCPolyBase`` class instead. + +Furthermore, the unused ``PolyError`` and ``PolyDomainError`` exceptions are +removed from the `numpy.polynomial`. + +(`gh-18963 <https://github.com/numpy/numpy/pull/18963>`__) + + +Compatibility notes +=================== + +Error type changes in universal functions +----------------------------------------- +The universal functions may now raise different errors on invalid input in some +cases. The main changes should be that a ``RuntimeError`` was replaced with a +more fitting ``TypeError``. When multiple errors were present in the same +call, NumPy may now raise a different one. + +(`gh-15271 <https://github.com/numpy/numpy/pull/15271>`__) + +``__array_ufunc__`` argument validation +--------------------------------------- +NumPy will now partially validate arguments before calling ``__array_ufunc__``. +Previously, it was possible to pass on invalid arguments (such as a +non-existing keyword argument) when dispatch was known to occur. + +(`gh-15271 <https://github.com/numpy/numpy/pull/15271>`__) + +``__array_ufunc__`` and additional positional arguments +------------------------------------------------------- +Previously, all positionally passed arguments were checked for +``__array_ufunc__`` support. In the case of ``reduce``, ``accumulate``, and +``reduceat`` all arguments may be passed by position. This means that when +they were passed by position, they could previously have been asked to handle +the ufunc call via ``__array_ufunc__``. Since this depended on the way the +arguments were passed (by position or by keyword), NumPy will now only dispatch +on the input and output array. For example, NumPy will never dispatch on the +``where`` array in a reduction such as ``np.add.reduce``. + +(`gh-15271 <https://github.com/numpy/numpy/pull/15271>`__) + +Validate input values in ``Generator.uniform`` +---------------------------------------------- +Checked that ``high - low >= 0`` in ``np.random.Generator.uniform``. Raises +``ValueError`` if ``low > high``. Previously out-of-order inputs were accepted +and silently swapped, so that if ``low > high``, the value generated was +``high + (low - high) * random()``. + +(`gh-17921 <https://github.com/numpy/numpy/pull/17921>`__) + +``/usr/include`` removed from default include paths +--------------------------------------------------- +The default include paths when building a package with ``numpy.distutils`` no +longer include ``/usr/include``. This path is normally added by the compiler, +and hardcoding it can be problematic. In case this causes a problem, please +open an issue. A workaround is documented in PR 18658. + +(`gh-18658 <https://github.com/numpy/numpy/pull/18658>`__) + +Changes to comparisons with ``dtype=...`` +----------------------------------------- +When the ``dtype=`` (or ``signature``) arguments to comparison +ufuncs (``equal``, ``less``, etc.) is used, this will denote +the desired output dtype in the future. +This means that: + + np.equal(2, 3, dtype=object) + +will give a ``FutureWarning`` that it will return an ``object`` +array in the future, which currently happens for: + + np.equal(None, None, dtype=object) + +due to the fact that ``np.array(None)`` is already an object +array. (This also happens for some other dtypes.) + +Since comparisons normally only return boolean arrays, providing +any other dtype will always raise an error in the future and +give a ``DeprecationWarning`` now. + +(`gh-18718 <https://github.com/numpy/numpy/pull/18718>`__) + +Changes to ``dtype`` and ``signature`` arguments in ufuncs +---------------------------------------------------------- +The universal function arguments ``dtype`` and ``signature`` +which are also valid for reduction such as ``np.add.reduce`` +(which is the implementation for ``np.sum``) will now issue +a warning when the ``dtype`` provided is not a "basic" dtype. + +NumPy almost always ignored metadata, byteorder or time units +on these inputs. NumPy will now always ignore it and raise an +error if byteorder or time unit changed. +The following are the most important examples of changes which +will give the error. In some cases previously the information +stored was not ignored, in all of these an error is now raised:: + + # Previously ignored the byte-order (affect if non-native) + np.add(3, 5, dtype=">i32") + + # The biggest impact is for timedelta or datetimes: + arr = np.arange(10, dtype="m8[s]") + # The examples always ignored the time unit "ns": + np.add(arr, arr, dtype="m8[ns]") + np.maximum.reduce(arr, dtype="m8[ns]") + + # The following previously did use "ns" (as opposed to `arr.dtype`) + np.add(3, 5, dtype="m8[ns]") # Now return generic time units + np.maximum(arr, arr, dtype="m8[ns]") # Now returns "s" (from `arr`) + +The same applies for functions like ``np.sum`` which use these internally. +This change is necessary to achieve consistent handling within NumPy. + +If you run into these, in most cases pass for example ``dtype=np.timedelta64`` +which clearly denotes a general ``timedelta64`` without any unit or byte-order +defined. If you need to specify the output dtype precisely, you may do so +by either casting the inputs or providing an output array using `out=`. + +NumPy may choose to allow providing an exact output ``dtype`` here in the +future, which would be preceded by a ``FutureWarning``. + +(`gh-18718 <https://github.com/numpy/numpy/pull/18718>`__) + +Ufunc ``signature=...`` and ``dtype=`` generalization and ``casting`` +--------------------------------------------------------------------- +The behaviour for ``np.ufunc(1.0, 1.0, signature=...)`` or +``np.ufunc(1.0, 1.0, dtype=...)`` can now yield different loops in 1.21 +compared to 1.20 because of changes in promotion. +When ``signature`` was previously used, the casting check on inputs +was relaxed, which could lead to downcasting inputs unsafely especially +if combined with ``casting="unsafe"``. + +Casting is now guaranteed to be safe. If a signature is only +partially provided, for example using ``signature=("float64", None, None)``, +this could lead to no loop being found (an error). +In that case, it is necessary to provide the complete signature +to enforce casting the inputs. +If ``dtype="float64"`` is used or only outputs are set (e.g. +``signature=(None, None, "float64")`` the is unchanged. +We expect that very few users are affected by this change. + +Further, the meaning of ``dtype="float64"`` has been slightly modified and +now strictly enforces only the correct output (and not input) DTypes. +This means it is now always equivalent to:: + + signature=(None, None, "float64") + +(If the ufunc has two inputs and one output). Since this could lead +to no loop being found in some cases, NumPy will normally also search +for the loop:: + + signature=("float64", "float64", "float64") + +if the first search failed. +In the future, this behaviour may be customized to achieve the expected +results for more complex ufuncs. (For some universal functions such as +``np.ldexp`` inputs can have different DTypes.) + +(`gh-18880 <https://github.com/numpy/numpy/pull/18880>`__) + +Distutils forces strict floating point model on clang +----------------------------------------------------- +NumPy distutils will now always add the ``-ffp-exception-behavior=strict`` +compiler flag when compiling with clang. Clang defaults to a non-strict +version, which allows the compiler to generate code that does not set +floating point warnings/errors correctly. + +(`gh-19049 <https://github.com/numpy/numpy/pull/19049>`__) + + +C API changes +============= + +Use of ``ufunc->type_resolver`` and "type tuple" +------------------------------------------------ +NumPy now normalizes the "type tuple" argument to the type resolver functions +before calling it. Note that in the use of this type resolver is legacy +behaviour and NumPy will not do so when possible. Calling +``ufunc->type_resolver`` or ``PyUFunc_DefaultTypeResolver`` is strongly +discouraged and will now enforce a normalized type tuple if done. Note that +this does not affect providing a type resolver, which is expected to keep +working in most circumstances. If you have an unexpected use-case for calling +the type resolver, please inform the NumPy developers so that a solution can be +found. + +(`gh-18718 <https://github.com/numpy/numpy/pull/18718>`__) + + +New Features +============ + +Added a mypy plugin for handling platform-specific ``numpy.number`` precisions +------------------------------------------------------------------------------ +A mypy_ plugin is now available for automatically assigning the (platform-dependent) +precisions of certain `~numpy.number` subclasses, including the likes of +`~numpy.int_`, `~numpy.intp` and `~numpy.longlong`. See the documentation on +:ref:`scalar types <arrays.scalars.built-in>` for a comprehensive overview +of the affected classes. + +Note that while usage of the plugin is completely optional, without it the +precision of above-mentioned classes will be inferred as `~typing.Any`. + +To enable the plugin, one must add it to their mypy `configuration file`_: + +.. code-block:: ini + + [mypy] + plugins = numpy.typing.mypy_plugin + + +.. _mypy: http://mypy-lang.org/ +.. _configuration file: https://mypy.readthedocs.io/en/stable/config_file.html + +(`gh-17843 <https://github.com/numpy/numpy/pull/17843>`__) + +Let the mypy plugin manage extended-precision ``numpy.number`` subclasses +------------------------------------------------------------------------- +The mypy_ plugin, introduced in `numpy/numpy#17843`_, has been expanded: +the plugin now removes annotations for platform-specific extended-precision +types that are not available to the platform in question. +For example, it will remove `~numpy.float128` when not available. + +Without the plugin *all* extended-precision types will, as far as mypy is concerned, +be available on all platforms. + +To enable the plugin, one must add it to their mypy `configuration file`_: + +.. code-block:: ini + + [mypy] + plugins = numpy.typing.mypy_plugin + + +.. _mypy: http://mypy-lang.org/ +.. _configuration file: https://mypy.readthedocs.io/en/stable/config_file.html +.. _`numpy/numpy#17843`: https://github.com/numpy/numpy/pull/17843 + +(`gh-18322 <https://github.com/numpy/numpy/pull/18322>`__) + +New ``min_digits`` argument for printing float values +----------------------------------------------------- +A new ``min_digits`` argument has been added to the dragon4 float printing +functions `~numpy.format_float_positional` and `~numpy.format_float_scientific` +. This kwd guarantees that at least the given number of digits will be printed +when printing in unique=True mode, even if the extra digits are unnecessary to +uniquely specify the value. It is the counterpart to the precision argument +which sets the maximum number of digits to be printed. When unique=False in +fixed precision mode, it has no effect and the precision argument fixes the +number of digits. + +(`gh-18629 <https://github.com/numpy/numpy/pull/18629>`__) + +f2py now recognizes Fortran abstract interface blocks +----------------------------------------------------- +`~numpy.f2py` can now parse abstract interface blocks. + +(`gh-18695 <https://github.com/numpy/numpy/pull/18695>`__) + +BLAS and LAPACK configuration via environment variables +------------------------------------------------------- +Autodetection of installed BLAS and LAPACK libraries can be bypassed by using +the ``NPY_BLAS_LIBS`` and ``NPY_LAPACK_LIBS`` environment variables. Instead, +the link flags in these environment variables will be used directly, and the +language is assumed to be F77. This is especially useful in automated builds +where the BLAS and LAPACK that are installed are known exactly. A use case is +replacing the actual implementation at runtime via stub library links. + +If ``NPY_CBLAS_LIBS`` is set (optional in addition to ``NPY_BLAS_LIBS``), this +will be used as well, by defining ``HAVE_CBLAS`` and appending the environment +variable content to the link flags. + +(`gh-18737 <https://github.com/numpy/numpy/pull/18737>`__) + +A runtime-subcriptable alias has been added for ``ndarray`` +----------------------------------------------------------- +``numpy.typing.NDArray`` has been added, a runtime-subscriptable alias for +``np.ndarray[Any, np.dtype[~Scalar]]``. The new type alias can be used +for annotating arrays with a given dtype and unspecified shape. :sup:`1` + +:sup:`1` NumPy does not support the annotating of array shapes as of 1.21, +this is expected to change in the future though (see :pep:`646`). + +Examples +~~~~~~~~ + +.. code-block:: python + + >>> import numpy as np + >>> import numpy.typing as npt + + >>> print(npt.NDArray) + numpy.ndarray[typing.Any, numpy.dtype[~ScalarType]] + + >>> print(npt.NDArray[np.float64]) + numpy.ndarray[typing.Any, numpy.dtype[numpy.float64]] + + >>> NDArrayInt = npt.NDArray[np.int_] + >>> a: NDArrayInt = np.arange(10) + + >>> def func(a: npt.ArrayLike) -> npt.NDArray[Any]: + ... return np.array(a) + +(`gh-18935 <https://github.com/numpy/numpy/pull/18935>`__) + + +Improvements +============ + +Arbitrary ``period`` option for ``numpy.unwrap`` +------------------------------------------------ +The size of the interval over which phases are unwrapped is no longer restricted to ``2 * pi``. +This is especially useful for unwrapping degrees, but can also be used for other intervals. + +.. code:: python + + >>> phase_deg = np.mod(np.linspace(0,720,19), 360) - 180 + >>> phase_deg + array([-180., -140., -100., -60., -20., 20., 60., 100., 140., + -180., -140., -100., -60., -20., 20., 60., 100., 140., + -180.]) + + >>> unwrap(phase_deg, period=360) + array([-180., -140., -100., -60., -20., 20., 60., 100., 140., + 180., 220., 260., 300., 340., 380., 420., 460., 500., + 540.]) + +(`gh-16987 <https://github.com/numpy/numpy/pull/16987>`__) + +``np.unique`` now returns single ``NaN`` +---------------------------------------- +When ``np.unique`` operated on an array with multiple ``NaN`` entries, +its return included a ``NaN`` for each entry that was ``NaN`` in the original array. +This is now improved such that the returned array contains just one ``NaN`` as the +last element. + +Also for complex arrays all ``NaN`` values are considered equivalent +(no matter whether the ``NaN`` is in the real or imaginary part). As the +representant for the returned array the smallest one in the +lexicographical order is chosen - see ``np.sort`` for how the lexicographical +order is defined for complex arrays. + +(`gh-18070 <https://github.com/numpy/numpy/pull/18070>`__) + +``Generator.rayleigh`` and ``Generator.geometric`` performance improved +----------------------------------------------------------------------- +The performance of Rayleigh and geometric random variate generation +in ``Generator`` has improved. These are both transformation of exponential +random variables and the slow log-based inverse cdf transformation has +been replaced with the Ziggurat-based exponential variate generator. + +This change breaks the stream of variates generated when variates from +either of these distributions are produced. + +(`gh-18666 <https://github.com/numpy/numpy/pull/18666>`__) + +Placeholder annotations have been improved +------------------------------------------ +All placeholder annotations, that were previously annotated as ``typing.Any``, +have been improved. Where appropriate they have been replaced with explicit +function definitions, classes or other miscellaneous objects. + +(`gh-18934 <https://github.com/numpy/numpy/pull/18934>`__) + + +Performance improvements +======================== + +Improved performance in integer division of NumPy arrays +-------------------------------------------------------- +Integer division of NumPy arrays now uses +`libdivide <https://libdivide.com/>`__ when the divisor is a constant. With the +usage of libdivide and other minor optimizations, there is a large speedup. +The ``//`` operator and ``np.floor_divide`` makes use of the new changes. + +(`gh-17727 <https://github.com/numpy/numpy/pull/17727>`__) + +Improve performance of ``np.save`` and ``np.load`` for small arrays +------------------------------------------------------------------- +``np.save`` is now a lot faster for small arrays. + +``np.load`` is also faster for small arrays, +but only when serializing with a version >= ``(3, 0)``. + +Both are done by removing checks that are only relevant for Python 2, +while still maintaining compatibility with arrays +which might have been created by Python 2. + +(`gh-18657 <https://github.com/numpy/numpy/pull/18657>`__) + + +Changes +======= + +`numpy.piecewise` output class now matches the input class +---------------------------------------------------------- +When `~numpy.ndarray` subclasses are used on input to `~numpy.piecewise`, +they are passed on to the functions. The output will now be of the +same subclass as well. + +(`gh-18110 <https://github.com/numpy/numpy/pull/18110>`__) + +Enable Accelerate Framework +---------------------------- +With the release of macOS 11.3, several different issues that numpy was +encountering when using Accelerate Framework's implementation of BLAS and +LAPACK should be resolved. This change enables the Accelerate Framework as an +option on macOS. If additional issues are found, please file a bug report +against Accelerate using the developer feedback assistant tool +(https://developer.apple.com/bug-reporting/). We intend to address issues +promptly and plan to continue supporting and updating our BLAS and LAPACK +libraries. + +(`gh-18874 <https://github.com/numpy/numpy/pull/18874>`__) diff --git a/doc/source/release/1.21.1-notes.rst b/doc/source/release/1.21.1-notes.rst new file mode 100644 index 000000000000..0194327f8159 --- /dev/null +++ b/doc/source/release/1.21.1-notes.rst @@ -0,0 +1,69 @@ +.. currentmodule:: numpy + +========================== +NumPy 1.21.1 Release Notes +========================== +The NumPy 1.21.1 is maintenance release that fixes bugs discovered after the +1.21.0 release and updates OpenBLAS to v0.3.17 to deal with problems on arm64. + +The Python versions supported for this release are 3.7-3.9. The 1.21.x series +is compatible with development Python 3.10. Python 3.10 will be officially +supported after it is released. + +.. warning:: + There are unresolved problems compiling NumPy 1.20.0 with gcc-11.1. + + * Optimization level `-O3` results in many incorrect warnings when + running the tests. + * On some hardware NumPY will hang in an infinite loop. + +Contributors +============ + +A total of 11 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Bas van Beek +* Charles Harris +* Ganesh Kathiresan +* Gregory R. Lee +* Hugo Defois + +* Kevin Sheppard +* Matti Picus +* Ralf Gommers +* Sayed Adel +* Sebastian Berg +* Thomas J. Fan + +Pull requests merged +==================== + +A total of 26 pull requests were merged for this release. + +* `#19311 <https://github.com/numpy/numpy/pull/19311>`__: REV,BUG: Replace ``NotImplemented`` with ``typing.Any`` +* `#19324 <https://github.com/numpy/numpy/pull/19324>`__: MAINT: Fixed the return-dtype of ``ndarray.real`` and ``imag`` +* `#19330 <https://github.com/numpy/numpy/pull/19330>`__: MAINT: Replace ``"dtype[Any]"`` with ``dtype`` in the definiton of... +* `#19342 <https://github.com/numpy/numpy/pull/19342>`__: DOC: Fix some docstrings that crash pdf generation. +* `#19343 <https://github.com/numpy/numpy/pull/19343>`__: MAINT: bump scipy-mathjax +* `#19347 <https://github.com/numpy/numpy/pull/19347>`__: BUG: Fix arr.flat.index for large arrays and big-endian machines +* `#19348 <https://github.com/numpy/numpy/pull/19348>`__: ENH: add ``numpy.f2py.get_include`` function +* `#19349 <https://github.com/numpy/numpy/pull/19349>`__: BUG: Fix reference count leak in ufunc dtype handling +* `#19350 <https://github.com/numpy/numpy/pull/19350>`__: MAINT: Annotate missing attributes of ``np.number`` subclasses +* `#19351 <https://github.com/numpy/numpy/pull/19351>`__: BUG: Fix cast safety and comparisons for zero sized voids +* `#19352 <https://github.com/numpy/numpy/pull/19352>`__: BUG: Correct Cython declaration in random +* `#19353 <https://github.com/numpy/numpy/pull/19353>`__: BUG: protect against accessing base attribute of a NULL subarray +* `#19365 <https://github.com/numpy/numpy/pull/19365>`__: BUG, SIMD: Fix detecting AVX512 features on Darwin +* `#19366 <https://github.com/numpy/numpy/pull/19366>`__: MAINT: remove ``print()``'s in distutils template handling +* `#19390 <https://github.com/numpy/numpy/pull/19390>`__: ENH: SIMD architectures to show_config +* `#19391 <https://github.com/numpy/numpy/pull/19391>`__: BUG: Do not raise deprecation warning for all nans in unique... +* `#19392 <https://github.com/numpy/numpy/pull/19392>`__: BUG: Fix NULL special case in object-to-any cast code +* `#19430 <https://github.com/numpy/numpy/pull/19430>`__: MAINT: Use arm64-graviton2 for testing on travis +* `#19495 <https://github.com/numpy/numpy/pull/19495>`__: BUILD: update OpenBLAS to v0.3.17 +* `#19496 <https://github.com/numpy/numpy/pull/19496>`__: MAINT: Avoid unicode characters in division SIMD code comments +* `#19499 <https://github.com/numpy/numpy/pull/19499>`__: BUG, SIMD: Fix infinite loop during count non-zero on GCC-11 +* `#19500 <https://github.com/numpy/numpy/pull/19500>`__: BUG: fix a numpy.npiter leak in npyiter_multi_index_set +* `#19501 <https://github.com/numpy/numpy/pull/19501>`__: TST: Fix a ``GenericAlias`` test failure for python 3.9.0 +* `#19502 <https://github.com/numpy/numpy/pull/19502>`__: MAINT: Start testing with Python 3.10.0b3. +* `#19503 <https://github.com/numpy/numpy/pull/19503>`__: MAINT: Add missing dtype overloads for object- and ctypes-based... +* `#19510 <https://github.com/numpy/numpy/pull/19510>`__: REL: Prepare for NumPy 1.21.1 release. + diff --git a/doc/source/release/1.21.2-notes.rst b/doc/source/release/1.21.2-notes.rst new file mode 100644 index 000000000000..bc17c069ee19 --- /dev/null +++ b/doc/source/release/1.21.2-notes.rst @@ -0,0 +1,59 @@ +.. currentmodule:: numpy + +========================== +NumPy 1.21.2 Release Notes +========================== + +The NumPy 1.21.2 is a maintenance release that fixes bugs discovered after +1.21.1. It also provides 64 bit manylinux Python 3.10.0rc1 wheels for +downstream testing. Note that Python 3.10 is not yet final. It also has +preliminary support for Windows on ARM64, but there is no OpenBLAS for that +platform and no wheels are available. + +The Python versions supported for this release are 3.7-3.9. The 1.21.x series +is compatible with Python 3.10.0rc1 and Python 3.10 will be officially +supported after it is released. The previous problems with gcc-11.1 have been +fixed by gcc-11.2, check your version if you are using gcc-11. + + +Contributors +============ + +A total of 10 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Bas van Beek +* Carl Johnsen + +* Charles Harris +* Gwyn Ciesla + +* Matthieu Dartiailh +* Matti Picus +* Niyas Sait + +* Ralf Gommers +* Sayed Adel +* Sebastian Berg + + +Pull requests merged +==================== + +A total of 18 pull requests were merged for this release. + +* `#19497 <https://github.com/numpy/numpy/pull/19497>`__: MAINT: set Python version for 1.21.x to ``<3.11`` +* `#19533 <https://github.com/numpy/numpy/pull/19533>`__: BUG: Fix an issue wherein importing ``numpy.typing`` could raise +* `#19646 <https://github.com/numpy/numpy/pull/19646>`__: MAINT: Update Cython version for Python 3.10. +* `#19648 <https://github.com/numpy/numpy/pull/19648>`__: TST: Bump the python 3.10 test version from beta4 to rc1 +* `#19651 <https://github.com/numpy/numpy/pull/19651>`__: TST: avoid distutils.sysconfig in runtests.py +* `#19652 <https://github.com/numpy/numpy/pull/19652>`__: MAINT: add missing dunder method to nditer type hints +* `#19656 <https://github.com/numpy/numpy/pull/19656>`__: BLD, SIMD: Fix testing extra checks when ``-Werror`` isn't applicable... +* `#19657 <https://github.com/numpy/numpy/pull/19657>`__: BUG: Remove logical object ufuncs with bool output +* `#19658 <https://github.com/numpy/numpy/pull/19658>`__: MAINT: Include .coveragerc in source distributions to support... +* `#19659 <https://github.com/numpy/numpy/pull/19659>`__: BUG: Fix bad write in masked iterator output copy paths +* `#19660 <https://github.com/numpy/numpy/pull/19660>`__: ENH: Add support for windows on arm targets +* `#19661 <https://github.com/numpy/numpy/pull/19661>`__: BUG: add base to templated arguments for platlib +* `#19662 <https://github.com/numpy/numpy/pull/19662>`__: BUG,DEP: Non-default UFunc signature/dtype usage should be deprecated +* `#19666 <https://github.com/numpy/numpy/pull/19666>`__: MAINT: Add Python 3.10 to supported versions. +* `#19668 <https://github.com/numpy/numpy/pull/19668>`__: TST,BUG: Sanitize path-separators when running ``runtest.py`` +* `#19671 <https://github.com/numpy/numpy/pull/19671>`__: BLD: load extra flags when checking for libflame +* `#19676 <https://github.com/numpy/numpy/pull/19676>`__: BLD: update circleCI docker image +* `#19677 <https://github.com/numpy/numpy/pull/19677>`__: REL: Prepare for 1.21.2 release. diff --git a/doc/source/release/1.21.3-notes.rst b/doc/source/release/1.21.3-notes.rst new file mode 100644 index 000000000000..4058452ef7dc --- /dev/null +++ b/doc/source/release/1.21.3-notes.rst @@ -0,0 +1,44 @@ +.. currentmodule:: numpy + +========================== +NumPy 1.21.3 Release Notes +========================== + +NumPy 1.21.3 is a maintenance release that fixes a few bugs discovered after +1.21.2. It also provides 64 bit Python 3.10.0 wheels. Note a few oddities about +Python 3.10: + +* There are no 32 bit wheels for Windows, Mac, or Linux. +* The Mac Intel builds are only available in universal2 wheels. + +The Python versions supported in this release are 3.7-3.10. If you want to +compile your own version using gcc-11, you will need to use gcc-11.2+ to avoid +problems. + +Contributors +============ + +A total of 7 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Aaron Meurer +* Bas van Beek +* Charles Harris +* Developer-Ecosystem-Engineering + +* Kevin Sheppard +* Sebastian Berg +* Warren Weckesser + +Pull requests merged +==================== + +A total of 8 pull requests were merged for this release. + +* `#19745 <https://github.com/numpy/numpy/pull/19745>`__: ENH: Add dtype-support to 3 ```generic``/``ndarray`` methods +* `#19955 <https://github.com/numpy/numpy/pull/19955>`__: BUG: Resolve Divide by Zero on Apple silicon + test failures... +* `#19958 <https://github.com/numpy/numpy/pull/19958>`__: MAINT: Mark type-check-only ufunc subclasses as ufunc aliases... +* `#19994 <https://github.com/numpy/numpy/pull/19994>`__: BUG: np.tan(np.inf) test failure +* `#20080 <https://github.com/numpy/numpy/pull/20080>`__: BUG: Correct incorrect advance in PCG with emulated int128 +* `#20081 <https://github.com/numpy/numpy/pull/20081>`__: BUG: Fix NaT handling in the PyArray_CompareFunc for datetime... +* `#20082 <https://github.com/numpy/numpy/pull/20082>`__: DOC: Ensure that we add documentation also as to the dict for... +* `#20106 <https://github.com/numpy/numpy/pull/20106>`__: BUG: core: result_type(0, np.timedelta64(4)) would seg. fault. diff --git a/doc/source/release/1.21.4-notes.rst b/doc/source/release/1.21.4-notes.rst new file mode 100644 index 000000000000..e35d8c88027e --- /dev/null +++ b/doc/source/release/1.21.4-notes.rst @@ -0,0 +1,46 @@ +.. currentmodule:: numpy + +========================== +NumPy 1.21.4 Release Notes +========================== + +The NumPy 1.21.4 is a maintenance release that fixes a few bugs discovered +after 1.21.3. The most important fix here is a fix for the NumPy header files +to make them work for both x86_64 and M1 hardware when included in the Mac +universal2 wheels. Previously, the header files only worked for M1 and this +caused problems for folks building x86_64 extensions. This problem was not seen +before Python 3.10 because there were thin wheels for x86_64 that had +precedence. This release also provides thin x86_64 Mac wheels for Python 3.10. + +The Python versions supported in this release are 3.7-3.10. If you want to +compile your own version using gcc-11, you will need to use gcc-11.2+ to avoid +problems. + +Contributors +============ + +A total of 7 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Bas van Beek +* Charles Harris +* Isuru Fernando +* Matthew Brett +* Sayed Adel +* Sebastian Berg +* 傅立业(Chris Fu) + + +Pull requests merged +==================== + +A total of 9 pull requests were merged for this release. + +* `#20278 <https://github.com/numpy/numpy/pull/20278>`__: BUG: Fix shadowed reference of ``dtype`` in type stub +* `#20293 <https://github.com/numpy/numpy/pull/20293>`__: BUG: Fix headers for universal2 builds +* `#20294 <https://github.com/numpy/numpy/pull/20294>`__: BUG: ``VOID_nonzero`` could sometimes mutate alignment flag +* `#20295 <https://github.com/numpy/numpy/pull/20295>`__: BUG: Do not use nonzero fastpath on unaligned arrays +* `#20296 <https://github.com/numpy/numpy/pull/20296>`__: BUG: Distutils patch to allow for 2 as a minor version (!) +* `#20297 <https://github.com/numpy/numpy/pull/20297>`__: BUG, SIMD: Fix 64-bit/8-bit integer division by a scalar +* `#20298 <https://github.com/numpy/numpy/pull/20298>`__: BUG, SIMD: Workaround broadcasting SIMD 64-bit integers on MSVC... +* `#20300 <https://github.com/numpy/numpy/pull/20300>`__: REL: Prepare for the NumPy 1.21.4 release. +* `#20302 <https://github.com/numpy/numpy/pull/20302>`__: TST: Fix a ``Arrayterator`` typing test failure diff --git a/doc/source/release/1.22.0-notes.rst b/doc/source/release/1.22.0-notes.rst new file mode 100644 index 000000000000..08c74d998666 --- /dev/null +++ b/doc/source/release/1.22.0-notes.rst @@ -0,0 +1,458 @@ +.. currentmodule:: numpy + +========================== +NumPy 1.22.0 Release Notes +========================== +NumPy 1.22.0 is a big release featuring the work of 153 contributers spread +over 609 pull requests. There have been many improvements, highlights are: + +* Annotations of the main namespace are essentially complete. Upstream is a + moving target, so there will likely be further improvements, but the major + work is done. This is probably the most user visible enhancement in this + release. +* A preliminary version of the proposed Array-API is provided. This is a step + in creating a standard collection of functions that can be used across + applications such as CuPy and JAX. +* NumPy now has a DLPack backend. DLPack provides a common interchange format + for array (tensor) data. +* New methods for ``quantile``, ``percentile``, and related functions. The new + methods provide a complete set of the methods commonly found in the + literature. +* A new configurable allocator for use by downstream projects. +* The universal functions have been refactored to implement most of + :ref:`NEP 43 <NEP43>`. This also unlocks the ability to experiment with the + future DType API. + +These are in addition to the ongoing work to provide SIMD support for commonly +used functions, improvements to F2PY, and better documentation. + +The Python versions supported in this release are 3.8-3.10, Python 3.7 has been +dropped. Note that 32 bit wheels are only provided for Python 3.8 and 3.9 on +Windows, all other wheels are 64 bits on account of Ubuntu, Fedora, and other +Linux distributions dropping 32 bit support. All 64 bit wheels are also linked +with 64 bit integer OpenBLAS, which should fix the occasional problems +encountered by folks using truly huge arrays. + + +Expired deprecations +==================== + +Deprecated numeric style dtype strings have been removed +-------------------------------------------------------- +Using the strings ``"Bytes0"``, ``"Datetime64"``, ``"Str0"``, ``"Uint32"``, +and ``"Uint64"`` as a dtype will now raise a ``TypeError``. + +(`gh-19539 <https://github.com/numpy/numpy/pull/19539>`__) + +Expired deprecations for ``loads``, ``ndfromtxt``, and ``mafromtxt`` in npyio +----------------------------------------------------------------------------- +``numpy.loads`` was deprecated in v1.15, with the recommendation that users use +``pickle.loads`` instead. ``ndfromtxt`` and ``mafromtxt`` were both deprecated +in v1.17 - users should use ``numpy.genfromtxt`` instead with the appropriate +value for the ``usemask`` parameter. + +(`gh-19615 <https://github.com/numpy/numpy/pull/19615>`__) + + +Deprecations +============ + +Use delimiter rather than delimitor as kwarg in mrecords +-------------------------------------------------------- +The misspelled keyword argument ``delimitor`` of +``numpy.ma.mrecords.fromtextfile()`` has been changed to ``delimiter``, using +it will emit a deprecation warning. + +(`gh-19921 <https://github.com/numpy/numpy/pull/19921>`__) + +Passing boolean ``kth`` values to (arg-)partition has been deprecated +--------------------------------------------------------------------- +``numpy.partition`` and ``numpy.argpartition`` would previously accept boolean +values for the ``kth`` parameter, which would subsequently be converted into +integers. This behavior has now been deprecated. + +(`gh-20000 <https://github.com/numpy/numpy/pull/20000>`__) + +The ``np.MachAr`` class has been deprecated +------------------------------------------- +The ``numpy.MachAr`` class and ``finfo.machar <numpy.finfo>`` attribute have +been deprecated. Users are encouraged to access the property if interest +directly from the corresponding ``numpy.finfo`` attribute. + +(`gh-20201 <https://github.com/numpy/numpy/pull/20201>`__) + + +Compatibility notes +=================== + +Distutils forces strict floating point model on clang +----------------------------------------------------- +NumPy now sets the ``-ftrapping-math`` option on clang to enforce correct +floating point error handling for universal functions. Clang defaults to +non-IEEE and C99 conform behaviour otherwise. This change (using the +equivalent but newer ``-ffp-exception-behavior=strict``) was attempted in NumPy +1.21, but was effectively never used. + +(`gh-19479 <https://github.com/numpy/numpy/pull/19479>`__) + +Removed floor division support for complex types +------------------------------------------------ +Floor division of complex types will now result in a ``TypeError`` + +.. code-block:: python + + >>> a = np.arange(10) + 1j* np.arange(10) + >>> a // 1 + TypeError: ufunc 'floor_divide' not supported for the input types... + +(`gh-19135 <https://github.com/numpy/numpy/pull/19135>`__) + +``numpy.vectorize`` functions now produce the same output class as the base function +------------------------------------------------------------------------------------ +When a function that respects ``numpy.ndarray`` subclasses is vectorized using +``numpy.vectorize``, the vectorized function will now be subclass-safe also for +cases that a signature is given (i.e., when creating a ``gufunc``): the output +class will be the same as that returned by the first call to the underlying +function. + +(`gh-19356 <https://github.com/numpy/numpy/pull/19356>`__) + +Python 3.7 is no longer supported +--------------------------------- +Python support has been dropped. This is rather strict, there are changes that +require Python >= 3.8. + +(`gh-19665 <https://github.com/numpy/numpy/pull/19665>`__) + +str/repr of complex dtypes now include space after punctuation +-------------------------------------------------------------- +The repr of ``np.dtype({"names": ["a"], "formats": [int], "offsets": [2]})`` is +now ``dtype({'names': ['a'], 'formats': ['<i8'], 'offsets': [2], 'itemsize': +10})``, whereas spaces where previously omitted after colons and between +fields. + +The old behavior can be restored via ``np.set_printoptions(legacy="1.21")``. + +(`gh-19687 <https://github.com/numpy/numpy/pull/19687>`__) + +Corrected ``advance`` in ``PCG64DSXM`` and ``PCG64`` +---------------------------------------------------- +Fixed a bug in the ``advance`` method of ``PCG64DSXM`` and ``PCG64``. The bug +only affects results when the step was larger than :math:`2^{64}` on platforms +that do not support 128-bit integers(e.g., Windows and 32-bit Linux). + +(`gh-20049 <https://github.com/numpy/numpy/pull/20049>`__) + +Change in generation of random 32 bit floating point variates +------------------------------------------------------------- +There was bug in the generation of 32 bit floating point values from the +uniform distribution that would result in the least significant bit of the +random variate always being 0. This has been fixed. + +This change affects the variates produced by the ``random.Generator`` methods +``random``, ``standard_normal``, ``standard_exponential``, and +``standard_gamma``, but only when the dtype is specified as ``numpy.float32``. + +(`gh-20314 <https://github.com/numpy/numpy/pull/20314>`__) + + +C API changes +============= + +Masked inner-loops cannot be customized anymore +----------------------------------------------- +The masked inner-loop selector is now never used. A warning will be given in +the unlikely event that it was customized. + +We do not expect that any code uses this. If you do use it, you must unset the +selector on newer NumPy version. Please also contact the NumPy developers, we +do anticipate providing a new, more specific, mechanism. + +The customization was part of a never-implemented feature to allow for faster +masked operations. + +(`gh-19259 <https://github.com/numpy/numpy/pull/19259>`__) + +Experimental exposure of future DType and UFunc API +--------------------------------------------------- +The new header ``experimental_public_dtype_api.h`` allows to experiment with +future API for improved universal function and especially user DType support. +At this time it is advisable to experiment using the development version +of NumPy since some changes are expected and new features will be unlocked. + +(`gh-19919 <https://github.com/numpy/numpy/pull/19919>`__) + + +New Features +============ + +NEP 49 configurable allocators +------------------------------ +As detailed in `NEP 49`_, the function used for allocation of the data segment +of a ndarray can be changed. The policy can be set globally or in a context. +For more information see the NEP and the :ref:`data_memory` reference docs. +Also add a ``NUMPY_WARN_IF_NO_MEM_POLICY`` override to warn on dangerous use +of transfering ownership by setting ``NPY_ARRAY_OWNDATA``. + +.. _`NEP 49`: https://numpy.org/neps/nep-0049.html + +(`gh-17582 <https://github.com/numpy/numpy/pull/17582>`__) + +Implementation of the NEP 47 (adopting the array API standard) +-------------------------------------------------------------- +An initial implementation of `NEP 47`_ (adoption the array API standard) has +been added as ``numpy.array_api``. The implementation is experimental and will +issue a UserWarning on import, as the `array API standard +<https://data-apis.org/array-api/latest/index.html>`_ is still in draft state. +``numpy.array_api`` is a conforming implementation of the array API standard, +which is also minimal, meaning that only those functions and behaviors that are +required by the standard are implemented (see the NEP for more info). +Libraries wishing to make use of the array API standard are encouraged to use +``numpy.array_api`` to check that they are only using functionality that is +guaranteed to be present in standard conforming implementations. + +.. _`NEP 47`: https://numpy.org/neps/nep-0047-array-api-standard.html + +(`gh-18585 <https://github.com/numpy/numpy/pull/18585>`__) + +Generate C/C++ API reference documentation from comments blocks is now possible +------------------------------------------------------------------------------- +This feature depends on Doxygen_ in the generation process and on Breathe_ to +integrate it with Sphinx. + +.. _`Doxygen`: https://www.doxygen.nl/index.html +.. _`Breathe`: https://breathe.readthedocs.io/en/latest/ + +(`gh-18884 <https://github.com/numpy/numpy/pull/18884>`__) + +Assign the platform-specific ``c_intp`` precision via a mypy plugin +------------------------------------------------------------------- +The mypy_ plugin, introduced in `numpy/numpy#17843`_, has again been expanded: +the plugin now is now responsible for setting the platform-specific precision +of ``numpy.ctypeslib.c_intp``, the latter being used as data type for various +``numpy.ndarray.ctypes`` attributes. + +Without the plugin, aforementioned type will default to ``ctypes.c_int64``. + +To enable the plugin, one must add it to their mypy `configuration file`_: + +.. code-block:: ini + + [mypy] + plugins = numpy.typing.mypy_plugin + + +.. _mypy: http://mypy-lang.org/ +.. _configuration file: https://mypy.readthedocs.io/en/stable/config_file.html +.. _`numpy/numpy#17843`: https://github.com/numpy/numpy/pull/17843 + +(`gh-19062 <https://github.com/numpy/numpy/pull/19062>`__) + +Add NEP 47-compatible dlpack support +------------------------------------ +Add a ``ndarray.__dlpack__()`` method which returns a ``dlpack`` C structure +wrapped in a ``PyCapsule``. Also add a ``np._from_dlpack(obj)`` function, where +``obj`` supports ``__dlpack__()``, and returns an ``ndarray``. + +(`gh-19083 <https://github.com/numpy/numpy/pull/19083>`__) + +``keepdims`` optional argument added to ``numpy.argmin``, ``numpy.argmax`` +-------------------------------------------------------------------------- +``keepdims`` argument is added to ``numpy.argmin``, ``numpy.argmax``. If set +to ``True``, the axes which are reduced are left in the result as dimensions +with size one. The resulting array has the same number of dimensions and will +broadcast with the input array. + +(`gh-19211 <https://github.com/numpy/numpy/pull/19211>`__) + +``bit_count`` to compute the number of 1-bits in an integer +----------------------------------------------------------- +Computes the number of 1-bits in the absolute value of the input. +This works on all the numpy integer types. Analogous to the builtin +``int.bit_count`` or ``popcount`` in C++. + +.. code-block:: python + + >>> np.uint32(1023).bit_count() + 10 + >>> np.int32(-127).bit_count() + 7 + +(`gh-19355 <https://github.com/numpy/numpy/pull/19355>`__) + +The ``ndim`` and ``axis`` attributes have been added to ``numpy.AxisError`` +--------------------------------------------------------------------------- +The ``ndim`` and ``axis`` parameters are now also stored as attributes +within each ``numpy.AxisError`` instance. + +(`gh-19459 <https://github.com/numpy/numpy/pull/19459>`__) + +Preliminary support for ``windows/arm64`` target +------------------------------------------------ +``numpy`` added support for windows/arm64 target. Please note ``OpenBLAS`` +support is not yet available for windows/arm64 target. + +(`gh-19513 <https://github.com/numpy/numpy/pull/19513>`__) + +Added support for LoongArch +--------------------------- +LoongArch is a new instruction set, numpy compilation failure on LoongArch +architecture, so add the commit. + +(`gh-19527 <https://github.com/numpy/numpy/pull/19527>`__) + +A ``.clang-format`` file has been added +--------------------------------------- +Clang-format is a C/C++ code formatter, together with the added +``.clang-format`` file, it produces code close enough to the NumPy +C_STYLE_GUIDE for general use. Clang-format version 12+ is required due to the +use of several new features, it is available in Fedora 34 and Ubuntu Focal +among other distributions. + +(`gh-19754 <https://github.com/numpy/numpy/pull/19754>`__) + +``is_integer`` is now available to ``numpy.floating`` and ``numpy.integer`` +--------------------------------------------------------------------------- +Based on its counterpart in Python ``float`` and ``int``, the numpy floating +point and integer types now support ``float.is_integer``. Returns ``True`` if +the number is finite with integral value, and ``False`` otherwise. + +.. code-block:: python + + >>> np.float32(-2.0).is_integer() + True + >>> np.float64(3.2).is_integer() + False + >>> np.int32(-2).is_integer() + True + +(`gh-19803 <https://github.com/numpy/numpy/pull/19803>`__) + +Symbolic parser for Fortran dimension specifications +---------------------------------------------------- +A new symbolic parser has been added to f2py in order to correctly parse +dimension specifications. The parser is the basis for future improvements and +provides compatibility with Draft Fortran 202x. + +(`gh-19805 <https://github.com/numpy/numpy/pull/19805>`__) + +``ndarray``, ``dtype`` and ``number`` are now runtime-subscriptable +------------------------------------------------------------------- +Mimicking :pep:`585`, the ``numpy.ndarray``, ``numpy.dtype`` and +``numpy.number`` classes are now subscriptable for python 3.9 and later. +Consequently, expressions that were previously only allowed in .pyi stub files +or with the help of ``from __future__ import annotations`` are now also legal +during runtime. + +.. code-block:: python + + >>> import numpy as np + >>> from typing import Any + + >>> np.ndarray[Any, np.dtype[np.float64]] + numpy.ndarray[typing.Any, numpy.dtype[numpy.float64]] + +(`gh-19879 <https://github.com/numpy/numpy/pull/19879>`__) + + +Improvements +============ + +``ctypeslib.load_library`` can now take any path-like object +------------------------------------------------------------ +All parameters in the can now take any :term:`python:path-like object`. +This includes the likes of strings, bytes and objects implementing the +:meth:`__fspath__<os.PathLike.__fspath__>` protocol. + +(`gh-17530 <https://github.com/numpy/numpy/pull/17530>`__) + +Add ``smallest_normal`` and ``smallest_subnormal`` attributes to ``finfo`` +-------------------------------------------------------------------------- +The attributes ``smallest_normal`` and ``smallest_subnormal`` are available as +an extension of ``finfo`` class for any floating-point data type. To use these +new attributes, write ``np.finfo(np.float64).smallest_normal`` or +``np.finfo(np.float64).smallest_subnormal``. + +(`gh-18536 <https://github.com/numpy/numpy/pull/18536>`__) + +``numpy.linalg.qr`` accepts stacked matrices as inputs +------------------------------------------------------ +``numpy.linalg.qr`` is able to produce results for stacked matrices as inputs. +Moreover, the implementation of QR decomposition has been shifted to C from +Python. + +(`gh-19151 <https://github.com/numpy/numpy/pull/19151>`__) + +``numpy.fromregex`` now accepts ``os.PathLike`` implementations +--------------------------------------------------------------- +``numpy.fromregex`` now accepts objects implementing the ``__fspath__<os.PathLike>`` +protocol, *e.g.* ``pathlib.Path``. + +(`gh-19680 <https://github.com/numpy/numpy/pull/19680>`__) + +Add new methods for ``quantile`` and ``percentile`` +--------------------------------------------------- +``quantile`` and ``percentile`` now have have a ``method=`` keyword argument +supporting 13 different methods. This replaces the ``interpolation=`` keyword +argument. + +The methods are now aligned with nine methods which can be found in scientific +literature and the R language. The remaining methods are the previous +discontinuous variations of the default "linear" one. + +Please see the documentation of ``numpy.percentile`` for more information. + +(`gh-19857 <https://github.com/numpy/numpy/pull/19857>`__) + +Missing parameters have been added to the ``nan<x>`` functions +-------------------------------------------------------------- +A number of the ``nan<x>`` functions previously lacked parameters that were +present in their ``<x>``-based counterpart, *e.g.* the ``where`` parameter was +present in ``numpy.mean`` but absent from ``numpy.nanmean``. + +The following parameters have now been added to the ``nan<x>`` functions: + +* nanmin: ``initial`` & ``where`` +* nanmax: ``initial`` & ``where`` +* nanargmin: ``keepdims`` & ``out`` +* nanargmax: ``keepdims`` & ``out`` +* nansum: ``initial`` & ``where`` +* nanprod: ``initial`` & ``where`` +* nanmean: ``where`` +* nanvar: ``where`` +* nanstd: ``where`` + +(`gh-20027 <https://github.com/numpy/numpy/pull/20027>`__) + +Annotating the main Numpy namespace +----------------------------------- +Starting from the 1.20 release, PEP 484 type annotations have been included for +parts of the NumPy library; annotating the remaining functions being a work in +progress. With the release of 1.22 this process has been completed for the main +NumPy namespace, which is now fully annotated. + +Besides the main namespace, a limited number of sub-packages contain +annotations as well. This includes, among others, ``numpy.testing``, +``numpy.linalg`` and ``numpy.random`` (available since 1.21). + +(`gh-20217 <https://github.com/numpy/numpy/pull/20217>`__) + +Vectorize umath module using AVX-512 +------------------------------------- +By leveraging Intel Short Vector Math Library (SVML), 18 umath functions +(``exp2``, ``log2``, ``log10``, ``expm1``, ``log1p``, ``cbrt``, ``sin``, +``cos``, ``tan``, ``arcsin``, ``arccos``, ``arctan``, ``sinh``, ``cosh``, +``tanh``, ``arcsinh``, ``arccosh``, ``arctanh``) are vectorized using AVX-512 +instruction set for both single and double precision implementations. This +change is currently enabled only for Linux users and on processors with AVX-512 +instruction set. It provides an average speed up of 32x and 14x for single and +double precision functions respectively. + +(`gh-19478 <https://github.com/numpy/numpy/pull/19478>`__) + +OpenBLAS v0.3.17 +---------------- +Update the OpenBLAS used in testing and in wheels to v0.3.17 + +(`gh-19462 <https://github.com/numpy/numpy/pull/19462>`__) + diff --git a/doc/release/1.3.0-notes.rst b/doc/source/release/1.3.0-notes.rst similarity index 98% rename from doc/release/1.3.0-notes.rst rename to doc/source/release/1.3.0-notes.rst index 3ec93e0b0e39..2397142460d2 100644 --- a/doc/release/1.3.0-notes.rst +++ b/doc/source/release/1.3.0-notes.rst @@ -14,7 +14,7 @@ Python 2.6 support Python 2.6 is now supported on all previously supported platforms, including windows. -http://www.python.org/dev/peps/pep-0361/ +https://www.python.org/dev/peps/pep-0361/ Generalized ufuncs ------------------ @@ -235,7 +235,7 @@ This should make the porting to new platforms easier, and more robust. In particular, the configuration stage does not need to execute any code on the target platform, which is a first step toward cross-compilation. -http://numpy.github.io/neps/math_config_clean.html +https://www.numpy.org/neps/nep-0003-math_config_clean.html umath refactor -------------- @@ -247,7 +247,7 @@ Improvements to build warnings Numpy can now build with -W -Wall without warnings -http://numpy.github.io/neps/warnfix.html +https://www.numpy.org/neps/nep-0002-warnfix.html Separate core math library -------------------------- diff --git a/doc/release/1.4.0-notes.rst b/doc/source/release/1.4.0-notes.rst similarity index 100% rename from doc/release/1.4.0-notes.rst rename to doc/source/release/1.4.0-notes.rst diff --git a/doc/release/1.5.0-notes.rst b/doc/source/release/1.5.0-notes.rst similarity index 95% rename from doc/release/1.5.0-notes.rst rename to doc/source/release/1.5.0-notes.rst index a2184ab1335e..2b0c32f3e948 100644 --- a/doc/release/1.5.0-notes.rst +++ b/doc/source/release/1.5.0-notes.rst @@ -12,11 +12,11 @@ Python 3 compatibility This is the first NumPy release which is compatible with Python 3. Support for Python 3 and Python 2 is done from a single code base. Extensive notes on changes can be found at -`<http://projects.scipy.org/numpy/browser/trunk/doc/Py3K.txt>`_. +`<https://web.archive.org/web/20100814160313/http://projects.scipy.org/numpy/browser/trunk/doc/Py3K.txt>`_. Note that the Numpy testing framework relies on nose, which does not have a Python 3 compatible release yet. A working Python 3 branch of nose can be found -at `<http://bitbucket.org/jpellerin/nose3/>`_ however. +at `<https://web.archive.org/web/20100817112505/http://bitbucket.org/jpellerin/nose3/>`_ however. Porting of SciPy to Python 3 is expected to be completed soon. diff --git a/doc/release/1.6.0-notes.rst b/doc/source/release/1.6.0-notes.rst similarity index 100% rename from doc/release/1.6.0-notes.rst rename to doc/source/release/1.6.0-notes.rst diff --git a/doc/release/1.6.1-notes.rst b/doc/source/release/1.6.1-notes.rst similarity index 100% rename from doc/release/1.6.1-notes.rst rename to doc/source/release/1.6.1-notes.rst diff --git a/doc/release/1.6.2-notes.rst b/doc/source/release/1.6.2-notes.rst similarity index 100% rename from doc/release/1.6.2-notes.rst rename to doc/source/release/1.6.2-notes.rst diff --git a/doc/release/1.7.0-notes.rst b/doc/source/release/1.7.0-notes.rst similarity index 97% rename from doc/release/1.7.0-notes.rst rename to doc/source/release/1.7.0-notes.rst index 72aab4d4f174..f111f80dc97a 100644 --- a/doc/release/1.7.0-notes.rst +++ b/doc/source/release/1.7.0-notes.rst @@ -101,7 +101,7 @@ to NumPy 1.6: The notes in `doc/source/reference/arrays.datetime.rst <https://github.com/numpy/numpy/blob/maintenance/1.7.x/doc/source/reference/arrays.datetime.rst>`_ (also available in the online docs at `arrays.datetime.html -<http://docs.scipy.org/doc/numpy/reference/arrays.datetime.html>`_) should be +<https://docs.scipy.org/doc/numpy/reference/arrays.datetime.html>`_) should be consulted for more details. Custom formatter for printing arrays @@ -280,9 +280,9 @@ The macros in old_defines.h are deprecated and will be removed in the next major release (>= 2.0). The sed script tools/replace_old_macros.sed can be used to replace these macros with the newer versions. -You can test your code against the deprecated C API by #defining -NPY_NO_DEPRECATED_API to the target version number, for example -NPY_1_7_API_VERSION, before including any NumPy headers. +You can test your code against the deprecated C API by adding a line +composed of ``#define NPY_NO_DEPRECATED_API`` and the target version number, +such as ``NPY_1_7_API_VERSION``, before including any NumPy headers. The ``NPY_CHAR`` member of the ``NPY_TYPES`` enum is deprecated and will be removed in NumPy 1.8. See the discussion at diff --git a/doc/release/1.7.1-notes.rst b/doc/source/release/1.7.1-notes.rst similarity index 100% rename from doc/release/1.7.1-notes.rst rename to doc/source/release/1.7.1-notes.rst diff --git a/doc/release/1.7.2-notes.rst b/doc/source/release/1.7.2-notes.rst similarity index 100% rename from doc/release/1.7.2-notes.rst rename to doc/source/release/1.7.2-notes.rst diff --git a/doc/release/1.8.0-notes.rst b/doc/source/release/1.8.0-notes.rst similarity index 99% rename from doc/release/1.8.0-notes.rst rename to doc/source/release/1.8.0-notes.rst index 80c39f8bc692..65a471b92125 100644 --- a/doc/release/1.8.0-notes.rst +++ b/doc/source/release/1.8.0-notes.rst @@ -33,7 +33,7 @@ Future Changes The Datetime64 type remains experimental in this release. In 1.9 there will -probably be some changes to make it more useable. +probably be some changes to make it more usable. The diagonal method currently returns a new array and raises a FutureWarning. In 1.9 it will return a readonly view. @@ -315,8 +315,8 @@ If used with the `overwrite_input` option the array will now only be partially sorted instead of fully sorted. -Overrideable operand flags in ufunc C-API ------------------------------------------ +Overridable operand flags in ufunc C-API +---------------------------------------- When creating a ufunc, the default ufunc operand flags can be overridden via the new op_flags attribute of the ufunc object. For example, to set the operand flag for the first input to read/write: diff --git a/doc/release/1.8.1-notes.rst b/doc/source/release/1.8.1-notes.rst similarity index 100% rename from doc/release/1.8.1-notes.rst rename to doc/source/release/1.8.1-notes.rst diff --git a/doc/release/1.8.2-notes.rst b/doc/source/release/1.8.2-notes.rst similarity index 100% rename from doc/release/1.8.2-notes.rst rename to doc/source/release/1.8.2-notes.rst diff --git a/doc/release/1.9.0-notes.rst b/doc/source/release/1.9.0-notes.rst similarity index 99% rename from doc/release/1.9.0-notes.rst rename to doc/source/release/1.9.0-notes.rst index 7ea29e354f93..a19a05cb70b3 100644 --- a/doc/release/1.9.0-notes.rst +++ b/doc/source/release/1.9.0-notes.rst @@ -389,7 +389,7 @@ uses a per-state lock instead of the GIL. MaskedArray support for more complicated base classes ----------------------------------------------------- Built-in assumptions that the baseclass behaved like a plain array are being -removed. In particalur, ``repr`` and ``str`` should now work more reliably. +removed. In particular, ``repr`` and ``str`` should now work more reliably. C-API diff --git a/doc/release/1.9.1-notes.rst b/doc/source/release/1.9.1-notes.rst similarity index 100% rename from doc/release/1.9.1-notes.rst rename to doc/source/release/1.9.1-notes.rst diff --git a/doc/release/1.9.2-notes.rst b/doc/source/release/1.9.2-notes.rst similarity index 100% rename from doc/release/1.9.2-notes.rst rename to doc/source/release/1.9.2-notes.rst diff --git a/doc/source/release/template.rst b/doc/source/release/template.rst new file mode 100644 index 000000000000..cde7646df702 --- /dev/null +++ b/doc/source/release/template.rst @@ -0,0 +1,45 @@ +:orphan: + +========================== +NumPy 1.xx.x Release Notes +========================== + + +Highlights +========== + + +New functions +============= + + +Deprecations +============ + + +Future Changes +============== + + +Expired deprecations +==================== + + +Compatibility notes +=================== + + +C API changes +============= + + +New Features +============ + + +Improvements +============ + + +Changes +======= diff --git a/doc/source/user/absolute_beginners.rst b/doc/source/user/absolute_beginners.rst new file mode 100644 index 000000000000..27e9e1f638c3 --- /dev/null +++ b/doc/source/user/absolute_beginners.rst @@ -0,0 +1,1702 @@ + +**************************************** +NumPy: the absolute basics for beginners +**************************************** + +.. currentmodule:: numpy + +Welcome to the absolute beginner's guide to NumPy! If you have comments or +suggestions, please don’t hesitate to `reach out +<https://numpy.org/community/>`_! + + +Welcome to NumPy! +----------------- + +NumPy (**Numerical Python**) is an open source Python library that's used in +almost every field of science and engineering. It's the universal standard for +working with numerical data in Python, and it's at the core of the scientific +Python and PyData ecosystems. NumPy users include everyone from beginning coders +to experienced researchers doing state-of-the-art scientific and industrial +research and development. The NumPy API is used extensively in Pandas, SciPy, +Matplotlib, scikit-learn, scikit-image and most other data science and +scientific Python packages. + +The NumPy library contains multidimensional array and matrix data structures +(you'll find more information about this in later sections). It provides +**ndarray**, a homogeneous n-dimensional array object, with methods to +efficiently operate on it. NumPy can be used to perform a wide variety of +mathematical operations on arrays. It adds powerful data structures to Python +that guarantee efficient calculations with arrays and matrices and it supplies +an enormous library of high-level mathematical functions that operate on these +arrays and matrices. + +Learn more about :ref:`NumPy here <whatisnumpy>`! + +Installing NumPy +---------------- + +To install NumPy, we strongly recommend using a scientific Python distribution. +If you're looking for the full instructions for installing NumPy on your +operating system, see `Installing NumPy <https://numpy.org/install/>`_. + + + +If you already have Python, you can install NumPy with:: + + conda install numpy + +or :: + + pip install numpy + +If you don't have Python yet, you might want to consider using `Anaconda +<https://www.anaconda.com/>`_. It's the easiest way to get started. The good +thing about getting this distribution is the fact that you don’t need to worry +too much about separately installing NumPy or any of the major packages that +you’ll be using for your data analyses, like pandas, Scikit-Learn, etc. + +How to import NumPy +------------------- + +To access NumPy and its functions import it in your Python code like this:: + + import numpy as np + +We shorten the imported name to ``np`` for better readability of code using +NumPy. This is a widely adopted convention that you should follow so that +anyone working with your code can easily understand it. + +Reading the example code +------------------------ + +If you aren't already comfortable with reading tutorials that contain a lot of code, +you might not know how to interpret a code block that looks +like this:: + + >>> a = np.arange(6) + >>> a2 = a[np.newaxis, :] + >>> a2.shape + (1, 6) + +If you aren't familiar with this style, it's very easy to understand. +If you see ``>>>``, you're looking at **input**, or the code that +you would enter. Everything that doesn't have ``>>>`` in front of it +is **output**, or the results of running your code. This is the style +you see when you run ``python`` on the command line, but if you're using +IPython, you might see a different style. Note that it is not part of the +code and will cause an error if typed or pasted into the Python +shell. It can be safely typed or pasted into the IPython shell; the ``>>>`` +is ignored. + + +What’s the difference between a Python list and a NumPy array? +-------------------------------------------------------------- + +NumPy gives you an enormous range of fast and efficient ways of creating arrays +and manipulating numerical data inside them. While a Python list can contain +different data types within a single list, all of the elements in a NumPy array +should be homogeneous. The mathematical operations that are meant to be performed +on arrays would be extremely inefficient if the arrays weren't homogeneous. + +**Why use NumPy?** + +NumPy arrays are faster and more compact than Python lists. An array consumes +less memory and is convenient to use. NumPy uses much less memory to store data +and it provides a mechanism of specifying the data types. This allows the code +to be optimized even further. + +What is an array? +----------------- + +An array is a central data structure of the NumPy library. An array is a grid of +values and it contains information about the raw data, how to locate an element, +and how to interpret an element. It has a grid of elements that can be indexed +in :ref:`various ways <quickstart.indexing-slicing-and-iterating>`. +The elements are all of the same type, referred to as the array ``dtype``. + +An array can be indexed by a tuple of nonnegative integers, by booleans, by +another array, or by integers. The ``rank`` of the array is the number of +dimensions. The ``shape`` of the array is a tuple of integers giving the size of +the array along each dimension. + +One way we can initialize NumPy arrays is from Python lists, using nested lists +for two- or higher-dimensional data. + +For example:: + + >>> a = np.array([1, 2, 3, 4, 5, 6]) + +or:: + + >>> a = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]) + +We can access the elements in the array using square brackets. When you're +accessing elements, remember that indexing in NumPy starts at 0. That means that +if you want to access the first element in your array, you'll be accessing +element "0". + +:: + + >>> print(a[0]) + [1 2 3 4] + + +More information about arrays +----------------------------- + +*This section covers* ``1D array``, ``2D array``, ``ndarray``, ``vector``, ``matrix`` + +------ + +You might occasionally hear an array referred to as a "ndarray," which is +shorthand for "N-dimensional array." An N-dimensional array is simply an array +with any number of dimensions. You might also hear **1-D**, or one-dimensional +array, **2-D**, or two-dimensional array, and so on. The NumPy ``ndarray`` class +is used to represent both matrices and vectors. A **vector** is an array with a +single dimension (there's no difference +between row and column vectors), while a **matrix** refers to an +array with two dimensions. For **3-D** or higher dimensional arrays, the term +**tensor** is also commonly used. + +**What are the attributes of an array?** + +An array is usually a fixed-size container of items of the same type and size. +The number of dimensions and items in an array is defined by its shape. The +shape of an array is a tuple of non-negative integers that specify the sizes of +each dimension. + +In NumPy, dimensions are called **axes**. This means that if you have a 2D array +that looks like this:: + + [[0., 0., 0.], + [1., 1., 1.]] + +Your array has 2 axes. The first axis has a length of 2 and the second axis has +a length of 3. + +Just like in other Python container objects, the contents of an array can be +accessed and modified by indexing or slicing the array. Unlike the typical container +objects, different arrays can share the same data, so changes made on one array might +be visible in another. + +Array **attributes** reflect information intrinsic to the array itself. If you +need to get, or even set, properties of an array without creating a new array, +you can often access an array through its attributes. + +:ref:`Read more about array attributes here <arrays.ndarray>` and learn about +:ref:`array objects here <arrays>`. + + +How to create a basic array +--------------------------- + + +*This section covers* ``np.array()``, ``np.zeros()``, ``np.ones()``, +``np.empty()``, ``np.arange()``, ``np.linspace()``, ``dtype`` + +----- + +To create a NumPy array, you can use the function ``np.array()``. + +All you need to do to create a simple array is pass a list to it. If you choose +to, you can also specify the type of data in your list. +:ref:`You can find more information about data types here <arrays.dtypes>`. :: + + >>> import numpy as np + >>> a = np.array([1, 2, 3]) + +You can visualize your array this way: + +.. image:: images/np_array.png + +*Be aware that these visualizations are meant to simplify ideas and give you a basic understanding of NumPy concepts and mechanics. Arrays and array operations are much more complicated than are captured here!* + +Besides creating an array from a sequence of elements, you can easily create an +array filled with ``0``'s:: + + >>> np.zeros(2) + array([0., 0.]) + +Or an array filled with ``1``'s:: + + >>> np.ones(2) + array([1., 1.]) + +Or even an empty array! The function ``empty`` creates an array whose initial +content is random and depends on the state of the memory. The reason to use +``empty`` over ``zeros`` (or something similar) is speed - just make sure to +fill every element afterwards! :: + + >>> # Create an empty array with 2 elements + >>> np.empty(2) + array([ 3.14, 42. ]) # may vary + +You can create an array with a range of elements:: + + >>> np.arange(4) + array([0, 1, 2, 3]) + +And even an array that contains a range of evenly spaced intervals. To do this, +you will specify the **first number**, **last number**, and the **step size**. :: + + >>> np.arange(2, 9, 2) + array([2, 4, 6, 8]) + +You can also use ``np.linspace()`` to create an array with values that are +spaced linearly in a specified interval:: + + >>> np.linspace(0, 10, num=5) + array([ 0. , 2.5, 5. , 7.5, 10. ]) + +**Specifying your data type** + +While the default data type is floating point (``np.float64``), you can explicitly +specify which data type you want using the ``dtype`` keyword. :: + + >>> x = np.ones(2, dtype=np.int64) + >>> x + array([1, 1]) + +:ref:`Learn more about creating arrays here <quickstart.array-creation>` + +Adding, removing, and sorting elements +-------------------------------------- + +*This section covers* ``np.sort()``, ``np.concatenate()`` + +----- + +Sorting an element is simple with ``np.sort()``. You can specify the axis, kind, +and order when you call the function. + +If you start with this array:: + + >>> arr = np.array([2, 1, 5, 3, 7, 4, 6, 8]) + +You can quickly sort the numbers in ascending order with:: + + >>> np.sort(arr) + array([1, 2, 3, 4, 5, 6, 7, 8]) + +In addition to sort, which returns a sorted copy of an array, you can use: + +- `argsort`, which is an indirect sort along a specified axis, +- `lexsort`, which is an indirect stable sort on multiple keys, +- `searchsorted`, which will find elements in a sorted array, and +- `partition`, which is a partial sort. + +To read more about sorting an array, see: `sort`. + +If you start with these arrays:: + + >>> a = np.array([1, 2, 3, 4]) + >>> b = np.array([5, 6, 7, 8]) + +You can concatenate them with ``np.concatenate()``. :: + + >>> np.concatenate((a, b)) + array([1, 2, 3, 4, 5, 6, 7, 8]) + +Or, if you start with these arrays:: + + >>> x = np.array([[1, 2], [3, 4]]) + >>> y = np.array([[5, 6]]) + +You can concatenate them with:: + + >>> np.concatenate((x, y), axis=0) + array([[1, 2], + [3, 4], + [5, 6]]) + +In order to remove elements from an array, it's simple to use indexing to select +the elements that you want to keep. + +To read more about concatenate, see: `concatenate`. + + +How do you know the shape and size of an array? +----------------------------------------------- + +*This section covers* ``ndarray.ndim``, ``ndarray.size``, ``ndarray.shape`` + +----- + +``ndarray.ndim`` will tell you the number of axes, or dimensions, of the array. + +``ndarray.size`` will tell you the total number of elements of the array. This +is the *product* of the elements of the array's shape. + +``ndarray.shape`` will display a tuple of integers that indicate the number of +elements stored along each dimension of the array. If, for example, you have a +2-D array with 2 rows and 3 columns, the shape of your array is ``(2, 3)``. + +For example, if you create this array:: + + >>> array_example = np.array([[[0, 1, 2, 3], + ... [4, 5, 6, 7]], + ... + ... [[0, 1, 2, 3], + ... [4, 5, 6, 7]], + ... + ... [[0 ,1 ,2, 3], + ... [4, 5, 6, 7]]]) + +To find the number of dimensions of the array, run:: + + >>> array_example.ndim + 3 + +To find the total number of elements in the array, run:: + + >>> array_example.size + 24 + +And to find the shape of your array, run:: + + >>> array_example.shape + (3, 2, 4) + + +Can you reshape an array? +------------------------- + +*This section covers* ``arr.reshape()`` + +----- + +**Yes!** + +Using ``arr.reshape()`` will give a new shape to an array without changing the +data. Just remember that when you use the reshape method, the array you want to +produce needs to have the same number of elements as the original array. If you +start with an array with 12 elements, you'll need to make sure that your new +array also has a total of 12 elements. + +If you start with this array:: + + >>> a = np.arange(6) + >>> print(a) + [0 1 2 3 4 5] + +You can use ``reshape()`` to reshape your array. For example, you can reshape +this array to an array with three rows and two columns:: + + >>> b = a.reshape(3, 2) + >>> print(b) + [[0 1] + [2 3] + [4 5]] + +With ``np.reshape``, you can specify a few optional parameters:: + + >>> np.reshape(a, newshape=(1, 6), order='C') + array([[0, 1, 2, 3, 4, 5]]) + +``a`` is the array to be reshaped. + +``newshape`` is the new shape you want. You can specify an integer or a tuple of +integers. If you specify an integer, the result will be an array of that length. +The shape should be compatible with the original shape. + +``order:`` ``C`` means to read/write the elements using C-like index order, +``F`` means to read/write the elements using Fortran-like index order, ``A`` +means to read/write the elements in Fortran-like index order if a is Fortran +contiguous in memory, C-like order otherwise. (This is an optional parameter and +doesn't need to be specified.) + +If you want to learn more about C and Fortran order, you can +:ref:`read more about the internal organization of NumPy arrays here <numpy-internals>`. +Essentially, C and Fortran orders have to do with how indices correspond +to the order the array is stored in memory. In Fortran, when moving through +the elements of a two-dimensional array as it is stored in memory, the **first** +index is the most rapidly varying index. As the first index moves to the next +row as it changes, the matrix is stored one column at a time. +This is why Fortran is thought of as a **Column-major language**. +In C on the other hand, the **last** index changes +the most rapidly. The matrix is stored by rows, making it a **Row-major +language**. What you do for C or Fortran depends on whether it's more important +to preserve the indexing convention or not reorder the data. + +:ref:`Learn more about shape manipulation here <quickstart.shape-manipulation>`. + + +How to convert a 1D array into a 2D array (how to add a new axis to an array) +----------------------------------------------------------------------------- + +*This section covers* ``np.newaxis``, ``np.expand_dims`` + +----- + +You can use ``np.newaxis`` and ``np.expand_dims`` to increase the dimensions of +your existing array. + +Using ``np.newaxis`` will increase the dimensions of your array by one dimension +when used once. This means that a **1D** array will become a **2D** array, a +**2D** array will become a **3D** array, and so on. + +For example, if you start with this array:: + + >>> a = np.array([1, 2, 3, 4, 5, 6]) + >>> a.shape + (6,) + +You can use ``np.newaxis`` to add a new axis:: + + >>> a2 = a[np.newaxis, :] + >>> a2.shape + (1, 6) + +You can explicitly convert a 1D array with either a row vector or a column +vector using ``np.newaxis``. For example, you can convert a 1D array to a row +vector by inserting an axis along the first dimension:: + + >>> row_vector = a[np.newaxis, :] + >>> row_vector.shape + (1, 6) + +Or, for a column vector, you can insert an axis along the second dimension:: + + >>> col_vector = a[:, np.newaxis] + >>> col_vector.shape + (6, 1) + +You can also expand an array by inserting a new axis at a specified position +with ``np.expand_dims``. + +For example, if you start with this array:: + + >>> a = np.array([1, 2, 3, 4, 5, 6]) + >>> a.shape + (6,) + +You can use ``np.expand_dims`` to add an axis at index position 1 with:: + + >>> b = np.expand_dims(a, axis=1) + >>> b.shape + (6, 1) + +You can add an axis at index position 0 with:: + + >>> c = np.expand_dims(a, axis=0) + >>> c.shape + (1, 6) + +Find more information about :ref:`newaxis here <arrays.indexing>` and +``expand_dims`` at `expand_dims`. + + +Indexing and slicing +-------------------- + +You can index and slice NumPy arrays in the same ways you can slice Python +lists. :: + + >>> data = np.array([1, 2, 3]) + + >>> data[1] + 2 + >>> data[0:2] + array([1, 2]) + >>> data[1:] + array([2, 3]) + >>> data[-2:] + array([2, 3]) + +You can visualize it this way: + +.. image:: images/np_indexing.png + + +You may want to take a section of your array or specific array elements to use +in further analysis or additional operations. To do that, you'll need to subset, +slice, and/or index your arrays. + +If you want to select values from your array that fulfill certain conditions, +it's straightforward with NumPy. + +For example, if you start with this array:: + + >>> a = np.array([[1 , 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]) + +You can easily print all of the values in the array that are less than 5. :: + + >>> print(a[a < 5]) + [1 2 3 4] + +You can also select, for example, numbers that are equal to or greater than 5, +and use that condition to index an array. :: + + >>> five_up = (a >= 5) + >>> print(a[five_up]) + [ 5 6 7 8 9 10 11 12] + +You can select elements that are divisible by 2:: + + >>> divisible_by_2 = a[a%2==0] + >>> print(divisible_by_2) + [ 2 4 6 8 10 12] + +Or you can select elements that satisfy two conditions using the ``&`` and ``|`` +operators:: + + >>> c = a[(a > 2) & (a < 11)] + >>> print(c) + [ 3 4 5 6 7 8 9 10] + +You can also make use of the logical operators **&** and **|** in order to +return boolean values that specify whether or not the values in an array fulfill +a certain condition. This can be useful with arrays that contain names or other +categorical values. :: + + >>> five_up = (a > 5) | (a == 5) + >>> print(five_up) + [[False False False False] + [ True True True True] + [ True True True True]] + +You can also use ``np.nonzero()`` to select elements or indices from an array. + +Starting with this array:: + + >>> a = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]) + +You can use ``np.nonzero()`` to print the indices of elements that are, for +example, less than 5:: + + >>> b = np.nonzero(a < 5) + >>> print(b) + (array([0, 0, 0, 0]), array([0, 1, 2, 3])) + +In this example, a tuple of arrays was returned: one for each dimension. The +first array represents the row indices where these values are found, and the +second array represents the column indices where the values are found. + +If you want to generate a list of coordinates where the elements exist, you can +zip the arrays, iterate over the list of coordinates, and print them. For +example:: + + >>> list_of_coordinates= list(zip(b[0], b[1])) + + >>> for coord in list_of_coordinates: + ... print(coord) + (0, 0) + (0, 1) + (0, 2) + (0, 3) + +You can also use ``np.nonzero()`` to print the elements in an array that are less +than 5 with:: + + >>> print(a[b]) + [1 2 3 4] + +If the element you're looking for doesn't exist in the array, then the returned +array of indices will be empty. For example:: + + >>> not_there = np.nonzero(a == 42) + >>> print(not_there) + (array([], dtype=int64), array([], dtype=int64)) + +Learn more about :ref:`indexing and slicing here <quickstart.indexing-slicing-and-iterating>` +and :ref:`here <basics.indexing>`. + +Read more about using the nonzero function at: `nonzero`. + + +How to create an array from existing data +----------------------------------------- + +*This section covers* ``slicing and indexing``, ``np.vstack()``, ``np.hstack()``, +``np.hsplit()``, ``.view()``, ``copy()`` + +----- + +You can easily create a new array from a section of an existing array. + +Let's say you have this array: + +:: + + >>> a = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) + +You can create a new array from a section of your array any time by specifying +where you want to slice your array. :: + + >>> arr1 = a[3:8] + >>> arr1 + array([4, 5, 6, 7, 8]) + +Here, you grabbed a section of your array from index position 3 through index +position 8. + +You can also stack two existing arrays, both vertically and horizontally. Let's +say you have two arrays, ``a1`` and ``a2``:: + + >>> a1 = np.array([[1, 1], + ... [2, 2]]) + + >>> a2 = np.array([[3, 3], + ... [4, 4]]) + +You can stack them vertically with ``vstack``:: + + >>> np.vstack((a1, a2)) + array([[1, 1], + [2, 2], + [3, 3], + [4, 4]]) + +Or stack them horizontally with ``hstack``:: + + >>> np.hstack((a1, a2)) + array([[1, 1, 3, 3], + [2, 2, 4, 4]]) + +You can split an array into several smaller arrays using ``hsplit``. You can +specify either the number of equally shaped arrays to return or the columns +*after* which the division should occur. + +Let's say you have this array:: + + >>> x = np.arange(1, 25).reshape(2, 12) + >>> x + array([[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], + [13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]]) + +If you wanted to split this array into three equally shaped arrays, you would +run:: + + >>> np.hsplit(x, 3) + [array([[1, 2, 3, 4], + [13, 14, 15, 16]]), array([[ 5, 6, 7, 8], + [17, 18, 19, 20]]), array([[ 9, 10, 11, 12], + [21, 22, 23, 24]])] + +If you wanted to split your array after the third and fourth column, you'd run:: + + >>> np.hsplit(x, (3, 4)) + [array([[1, 2, 3], + [13, 14, 15]]), array([[ 4], + [16]]), array([[ 5, 6, 7, 8, 9, 10, 11, 12], + [17, 18, 19, 20, 21, 22, 23, 24]])] + +:ref:`Learn more about stacking and splitting arrays here <quickstart.stacking-arrays>`. + +You can use the ``view`` method to create a new array object that looks at the +same data as the original array (a *shallow copy*). + +Views are an important NumPy concept! NumPy functions, as well as operations +like indexing and slicing, will return views whenever possible. This saves +memory and is faster (no copy of the data has to be made). However it's +important to be aware of this - modifying data in a view also modifies the +original array! + +Let's say you create this array:: + + >>> a = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]) + +Now we create an array ``b1`` by slicing ``a`` and modify the first element of +``b1``. This will modify the corresponding element in ``a`` as well! :: + + >>> b1 = a[0, :] + >>> b1 + array([1, 2, 3, 4]) + >>> b1[0] = 99 + >>> b1 + array([99, 2, 3, 4]) + >>> a + array([[99, 2, 3, 4], + [ 5, 6, 7, 8], + [ 9, 10, 11, 12]]) + +Using the ``copy`` method will make a complete copy of the array and its data (a +*deep copy*). To use this on your array, you could run:: + + >>> b2 = a.copy() + +:ref:`Learn more about copies and views here <quickstart.copies-and-views>`. + + +Basic array operations +---------------------- + +*This section covers addition, subtraction, multiplication, division, and more* + +----- + +Once you've created your arrays, you can start to work with them. Let's say, +for example, that you've created two arrays, one called "data" and one called +"ones" + +.. image:: images/np_array_dataones.png + +You can add the arrays together with the plus sign. + +:: + + >>> data = np.array([1, 2]) + >>> ones = np.ones(2, dtype=int) + >>> data + ones + array([2, 3]) + +.. image:: images/np_data_plus_ones.png + +You can, of course, do more than just addition! + +:: + + >>> data - ones + array([0, 1]) + >>> data * data + array([1, 4]) + >>> data / data + array([1., 1.]) + +.. image:: images/np_sub_mult_divide.png + +Basic operations are simple with NumPy. If you want to find the sum of the +elements in an array, you'd use ``sum()``. This works for 1D arrays, 2D arrays, +and arrays in higher dimensions. :: + + >>> a = np.array([1, 2, 3, 4]) + + >>> a.sum() + 10 + +To add the rows or the columns in a 2D array, you would specify the axis. + +If you start with this array:: + + >>> b = np.array([[1, 1], [2, 2]]) + +You can sum over the axis of rows with:: + + >>> b.sum(axis=0) + array([3, 3]) + +You can sum over the axis of columns with:: + + >>> b.sum(axis=1) + array([2, 4]) + +:ref:`Learn more about basic operations here <quickstart.basic-operations>`. + + +Broadcasting +------------ + +There are times when you might want to carry out an operation between an array +and a single number (also called *an operation between a vector and a scalar*) +or between arrays of two different sizes. For example, your array (we'll call it +"data") might contain information about distance in miles but you want to +convert the information to kilometers. You can perform this operation with:: + + >>> data = np.array([1.0, 2.0]) + >>> data * 1.6 + array([1.6, 3.2]) + +.. image:: images/np_multiply_broadcasting.png + +NumPy understands that the multiplication should happen with each cell. That +concept is called **broadcasting**. Broadcasting is a mechanism that allows +NumPy to perform operations on arrays of different shapes. The dimensions of +your array must be compatible, for example, when the dimensions of both arrays +are equal or when one of them is 1. If the dimensions are not compatible, you +will get a ``ValueError``. + +:ref:`Learn more about broadcasting here <basics.broadcasting>`. + + +More useful array operations +---------------------------- + +*This section covers maximum, minimum, sum, mean, product, standard deviation, and more* + +----- + +NumPy also performs aggregation functions. In addition to ``min``, ``max``, and +``sum``, you can easily run ``mean`` to get the average, ``prod`` to get the +result of multiplying the elements together, ``std`` to get the standard +deviation, and more. :: + + >>> data.max() + 2.0 + >>> data.min() + 1.0 + >>> data.sum() + 3.0 + +.. image:: images/np_aggregation.png + +Let's start with this array, called "a" :: + + >>> a = np.array([[0.45053314, 0.17296777, 0.34376245, 0.5510652], + ... [0.54627315, 0.05093587, 0.40067661, 0.55645993], + ... [0.12697628, 0.82485143, 0.26590556, 0.56917101]]) + +It's very common to want to aggregate along a row or column. By default, every +NumPy aggregation function will return the aggregate of the entire array. To +find the sum or the minimum of the elements in your array, run:: + + >>> a.sum() + 4.8595784 + +Or:: + + >>> a.min() + 0.05093587 + +You can specify on which axis you want the aggregation function to be computed. +For example, you can find the minimum value within each column by specifying +``axis=0``. :: + + >>> a.min(axis=0) + array([0.12697628, 0.05093587, 0.26590556, 0.5510652 ]) + +The four values listed above correspond to the number of columns in your array. +With a four-column array, you will get four values as your result. + +Read more about :ref:`array methods here <array.ndarray.methods>`. + + +Creating matrices +----------------- + +You can pass Python lists of lists to create a 2-D array (or "matrix") to +represent them in NumPy. :: + + >>> data = np.array([[1, 2], [3, 4], [5, 6]]) + >>> data + array([[1, 2], + [3, 4], + [5, 6]]) + +.. image:: images/np_create_matrix.png + +Indexing and slicing operations are useful when you're manipulating matrices:: + + >>> data[0, 1] + 2 + >>> data[1:3] + array([[3, 4], + [5, 6]]) + >>> data[0:2, 0] + array([1, 3]) + +.. image:: images/np_matrix_indexing.png + +You can aggregate matrices the same way you aggregated vectors:: + + >>> data.max() + 6 + >>> data.min() + 1 + >>> data.sum() + 21 + +.. image:: images/np_matrix_aggregation.png + +You can aggregate all the values in a matrix and you can aggregate them across +columns or rows using the ``axis`` parameter. To illustrate this point, let's +look at a slightly modified dataset:: + + >>> data = np.array([[1, 2], [5, 3], [4, 6]]) + >>> data + array([[1, 2], + [5, 3], + [4, 6]]) + >>> data.max(axis=0) + array([5, 6]) + >>> data.max(axis=1) + array([2, 5, 6]) + +.. image:: images/np_matrix_aggregation_row.png + +Once you've created your matrices, you can add and multiply them using +arithmetic operators if you have two matrices that are the same size. :: + + >>> data = np.array([[1, 2], [3, 4]]) + >>> ones = np.array([[1, 1], [1, 1]]) + >>> data + ones + array([[2, 3], + [4, 5]]) + +.. image:: images/np_matrix_arithmetic.png + +You can do these arithmetic operations on matrices of different sizes, but only +if one matrix has only one column or one row. In this case, NumPy will use its +broadcast rules for the operation. :: + + >>> data = np.array([[1, 2], [3, 4], [5, 6]]) + >>> ones_row = np.array([[1, 1]]) + >>> data + ones_row + array([[2, 3], + [4, 5], + [6, 7]]) + +.. image:: images/np_matrix_broadcasting.png + +Be aware that when NumPy prints N-dimensional arrays, the last axis is looped +over the fastest while the first axis is the slowest. For instance:: + + >>> np.ones((4, 3, 2)) + array([[[1., 1.], + [1., 1.], + [1., 1.]], + <BLANKLINE> + [[1., 1.], + [1., 1.], + [1., 1.]], + <BLANKLINE> + [[1., 1.], + [1., 1.], + [1., 1.]], + <BLANKLINE> + [[1., 1.], + [1., 1.], + [1., 1.]]]) + +There are often instances where we want NumPy to initialize the values of an +array. NumPy offers functions like ``ones()`` and ``zeros()``, and the +``random.Generator`` class for random number generation for that. +All you need to do is pass in the number of elements you want it to generate:: + + >>> np.ones(3) + array([1., 1., 1.]) + >>> np.zeros(3) + array([0., 0., 0.]) + # the simplest way to generate random numbers + >>> rng = np.random.default_rng(0) + >>> rng.random(3) + array([0.63696169, 0.26978671, 0.04097352]) + +.. image:: images/np_ones_zeros_random.png + +You can also use ``ones()``, ``zeros()``, and ``random()`` to create +a 2D array if you give them a tuple describing the dimensions of the matrix:: + + >>> np.ones((3, 2)) + array([[1., 1.], + [1., 1.], + [1., 1.]]) + >>> np.zeros((3, 2)) + array([[0., 0.], + [0., 0.], + [0., 0.]]) + >>> rng.random((3, 2)) + array([[0.01652764, 0.81327024], + [0.91275558, 0.60663578], + [0.72949656, 0.54362499]]) # may vary + +.. image:: images/np_ones_zeros_matrix.png + +Read more about creating arrays, filled with ``0``'s, ``1``'s, other values or +uninitialized, at :ref:`array creation routines <routines.array-creation>`. + + +Generating random numbers +------------------------- + +The use of random number generation is an important part of the configuration +and evaluation of many numerical and machine learning algorithms. Whether you +need to randomly initialize weights in an artificial neural network, split data +into random sets, or randomly shuffle your dataset, being able to generate +random numbers (actually, repeatable pseudo-random numbers) is essential. + +With ``Generator.integers``, you can generate random integers from low (remember +that this is inclusive with NumPy) to high (exclusive). You can set +``endpoint=True`` to make the high number inclusive. + +You can generate a 2 x 4 array of random integers between 0 and 4 with:: + + >>> rng.integers(5, size=(2, 4)) + array([[2, 1, 1, 0], + [0, 0, 0, 4]]) # may vary + +:ref:`Read more about random number generation here <numpyrandom>`. + + +How to get unique items and counts +---------------------------------- + +*This section covers* ``np.unique()`` + +----- + +You can find the unique elements in an array easily with ``np.unique``. + +For example, if you start with this array:: + + >>> a = np.array([11, 11, 12, 13, 14, 15, 16, 17, 12, 13, 11, 14, 18, 19, 20]) + +you can use ``np.unique`` to print the unique values in your array:: + + >>> unique_values = np.unique(a) + >>> print(unique_values) + [11 12 13 14 15 16 17 18 19 20] + +To get the indices of unique values in a NumPy array (an array of first index +positions of unique values in the array), just pass the ``return_index`` +argument in ``np.unique()`` as well as your array. :: + + >>> unique_values, indices_list = np.unique(a, return_index=True) + >>> print(indices_list) + [ 0 2 3 4 5 6 7 12 13 14] + +You can pass the ``return_counts`` argument in ``np.unique()`` along with your +array to get the frequency count of unique values in a NumPy array. :: + + >>> unique_values, occurrence_count = np.unique(a, return_counts=True) + >>> print(occurrence_count) + [3 2 2 2 1 1 1 1 1 1] + +This also works with 2D arrays! +If you start with this array:: + + >>> a_2d = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [1, 2, 3, 4]]) + +You can find unique values with:: + + >>> unique_values = np.unique(a_2d) + >>> print(unique_values) + [ 1 2 3 4 5 6 7 8 9 10 11 12] + +If the axis argument isn't passed, your 2D array will be flattened. + +If you want to get the unique rows or columns, make sure to pass the ``axis`` +argument. To find the unique rows, specify ``axis=0`` and for columns, specify +``axis=1``. :: + + >>> unique_rows = np.unique(a_2d, axis=0) + >>> print(unique_rows) + [[ 1 2 3 4] + [ 5 6 7 8] + [ 9 10 11 12]] + +To get the unique rows, index position, and occurrence count, you can use:: + + >>> unique_rows, indices, occurrence_count = np.unique( + ... a_2d, axis=0, return_counts=True, return_index=True) + >>> print(unique_rows) + [[ 1 2 3 4] + [ 5 6 7 8] + [ 9 10 11 12]] + >>> print(indices) + [0 1 2] + >>> print(occurrence_count) + [2 1 1] + +To learn more about finding the unique elements in an array, see `unique`. + + +Transposing and reshaping a matrix +---------------------------------- + +*This section covers* ``arr.reshape()``, ``arr.transpose()``, ``arr.T`` + +----- + +It's common to need to transpose your matrices. NumPy arrays have the property +``T`` that allows you to transpose a matrix. + +.. image:: images/np_transposing_reshaping.png + +You may also need to switch the dimensions of a matrix. This can happen when, +for example, you have a model that expects a certain input shape that is +different from your dataset. This is where the ``reshape`` method can be useful. +You simply need to pass in the new dimensions that you want for the matrix. :: + + >>> data.reshape(2, 3) + array([[1, 2, 3], + [4, 5, 6]]) + >>> data.reshape(3, 2) + array([[1, 2], + [3, 4], + [5, 6]]) + +.. image:: images/np_reshape.png + +You can also use ``.transpose()`` to reverse or change the axes of an array +according to the values you specify. + +If you start with this array:: + + >>> arr = np.arange(6).reshape((2, 3)) + >>> arr + array([[0, 1, 2], + [3, 4, 5]]) + +You can transpose your array with ``arr.transpose()``. :: + + >>> arr.transpose() + array([[0, 3], + [1, 4], + [2, 5]]) + +You can also use ``arr.T``:: + + >>> arr.T + array([[0, 3], + [1, 4], + [2, 5]]) + +To learn more about transposing and reshaping arrays, see `transpose` and +`reshape`. + + +How to reverse an array +----------------------- + +*This section covers* ``np.flip()`` + +----- + +NumPy's ``np.flip()`` function allows you to flip, or reverse, the contents of +an array along an axis. When using ``np.flip()``, specify the array you would like +to reverse and the axis. If you don't specify the axis, NumPy will reverse the +contents along all of the axes of your input array. + +**Reversing a 1D array** + +If you begin with a 1D array like this one:: + + >>> arr = np.array([1, 2, 3, 4, 5, 6, 7, 8]) + +You can reverse it with:: + + >>> reversed_arr = np.flip(arr) + +If you want to print your reversed array, you can run:: + + >>> print('Reversed Array: ', reversed_arr) + Reversed Array: [8 7 6 5 4 3 2 1] + +**Reversing a 2D array** + +A 2D array works much the same way. + +If you start with this array:: + + >>> arr_2d = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]) + +You can reverse the content in all of the rows and all of the columns with:: + + >>> reversed_arr = np.flip(arr_2d) + >>> print(reversed_arr) + [[12 11 10 9] + [ 8 7 6 5] + [ 4 3 2 1]] + +You can easily reverse only the *rows* with:: + + >>> reversed_arr_rows = np.flip(arr_2d, axis=0) + >>> print(reversed_arr_rows) + [[ 9 10 11 12] + [ 5 6 7 8] + [ 1 2 3 4]] + +Or reverse only the *columns* with:: + + >>> reversed_arr_columns = np.flip(arr_2d, axis=1) + >>> print(reversed_arr_columns) + [[ 4 3 2 1] + [ 8 7 6 5] + [12 11 10 9]] + +You can also reverse the contents of only one column or row. For example, you +can reverse the contents of the row at index position 1 (the second row):: + + >>> arr_2d[1] = np.flip(arr_2d[1]) + >>> print(arr_2d) + [[ 1 2 3 4] + [ 8 7 6 5] + [ 9 10 11 12]] + +You can also reverse the column at index position 1 (the second column):: + + >>> arr_2d[:,1] = np.flip(arr_2d[:,1]) + >>> print(arr_2d) + [[ 1 10 3 4] + [ 8 7 6 5] + [ 9 2 11 12]] + +Read more about reversing arrays at `flip`. + + +Reshaping and flattening multidimensional arrays +------------------------------------------------ + +*This section covers* ``.flatten()``, ``ravel()`` + +----- + +There are two popular ways to flatten an array: ``.flatten()`` and ``.ravel()``. +The primary difference between the two is that the new array created using +``ravel()`` is actually a reference to the parent array (i.e., a "view"). This +means that any changes to the new array will affect the parent array as well. +Since ``ravel`` does not create a copy, it's memory efficient. + +If you start with this array:: + + >>> x = np.array([[1 , 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]) + +You can use ``flatten`` to flatten your array into a 1D array. :: + + >>> x.flatten() + array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]) + +When you use ``flatten``, changes to your new array won't change the parent +array. + +For example:: + + >>> a1 = x.flatten() + >>> a1[0] = 99 + >>> print(x) # Original array + [[ 1 2 3 4] + [ 5 6 7 8] + [ 9 10 11 12]] + >>> print(a1) # New array + [99 2 3 4 5 6 7 8 9 10 11 12] + +But when you use ``ravel``, the changes you make to the new array will affect +the parent array. + +For example:: + + >>> a2 = x.ravel() + >>> a2[0] = 98 + >>> print(x) # Original array + [[98 2 3 4] + [ 5 6 7 8] + [ 9 10 11 12]] + >>> print(a2) # New array + [98 2 3 4 5 6 7 8 9 10 11 12] + +Read more about ``flatten`` at `ndarray.flatten` and ``ravel`` at `ravel`. + + +How to access the docstring for more information +------------------------------------------------ + +*This section covers* ``help()``, ``?``, ``??`` + +----- + +When it comes to the data science ecosystem, Python and NumPy are built with the +user in mind. One of the best examples of this is the built-in access to +documentation. Every object contains the reference to a string, which is known +as the **docstring**. In most cases, this docstring contains a quick and concise +summary of the object and how to use it. Python has a built-in ``help()`` +function that can help you access this information. This means that nearly any +time you need more information, you can use ``help()`` to quickly find the +information that you need. + +For example:: + + >>> help(max) + Help on built-in function max in module builtins: + <BLANKLINE> + max(...) + max(iterable, *[, default=obj, key=func]) -> value + max(arg1, arg2, *args, *[, key=func]) -> value + <BLANKLINE> + With a single iterable argument, return its biggest item. The + default keyword-only argument specifies an object to return if + the provided iterable is empty. + With two or more arguments, return the largest argument. + <BLANKLINE> + + +Because access to additional information is so useful, IPython uses the ``?`` +character as a shorthand for accessing this documentation along with other +relevant information. IPython is a command shell for interactive computing in +multiple languages. +`You can find more information about IPython here <https://ipython.org/>`_. + +For example: + +.. code-block:: ipython + + In [0]: max? + max(iterable, *[, default=obj, key=func]) -> value + max(arg1, arg2, *args, *[, key=func]) -> value + + With a single iterable argument, return its biggest item. The + default keyword-only argument specifies an object to return if + the provided iterable is empty. + With two or more arguments, return the largest argument. + Type: builtin_function_or_method + +You can even use this notation for object methods and objects themselves. + +Let's say you create this array:: + + >>> a = np.array([1, 2, 3, 4, 5, 6]) + +Then you can obtain a lot of useful information (first details about ``a`` itself, +followed by the docstring of ``ndarray`` of which ``a`` is an instance): + +.. code-block:: ipython + + In [1]: a? + Type: ndarray + String form: [1 2 3 4 5 6] + Length: 6 + File: ~/anaconda3/lib/python3.7/site-packages/numpy/__init__.py + Docstring: <no docstring> + Class docstring: + ndarray(shape, dtype=float, buffer=None, offset=0, + strides=None, order=None) + + An array object represents a multidimensional, homogeneous array + of fixed-size items. An associated data-type object describes the + format of each element in the array (its byte-order, how many bytes it + occupies in memory, whether it is an integer, a floating point number, + or something else, etc.) + + Arrays should be constructed using `array`, `zeros` or `empty` (refer + to the See Also section below). The parameters given here refer to + a low-level method (`ndarray(...)`) for instantiating an array. + + For more information, refer to the `numpy` module and examine the + methods and attributes of an array. + + Parameters + ---------- + (for the __new__ method; see Notes below) + + shape : tuple of ints + Shape of created array. + ... + +This also works for functions and other objects that **you** create. Just +remember to include a docstring with your function using a string literal +(``""" """`` or ``''' '''`` around your documentation). + +For example, if you create this function:: + + >>> def double(a): + ... '''Return a * 2''' + ... return a * 2 + +You can obtain information about the function: + +.. code-block:: ipython + + In [2]: double? + Signature: double(a) + Docstring: Return a * 2 + File: ~/Desktop/<ipython-input-23-b5adf20be596> + Type: function + +You can reach another level of information by reading the source code of the +object you're interested in. Using a double question mark (``??``) allows you to +access the source code. + +For example: + +.. code-block:: ipython + + In [3]: double?? + Signature: double(a) + Source: + def double(a): + '''Return a * 2''' + return a * 2 + File: ~/Desktop/<ipython-input-23-b5adf20be596> + Type: function + +If the object in question is compiled in a language other than Python, using +``??`` will return the same information as ``?``. You'll find this with a lot of +built-in objects and types, for example: + +.. code-block:: ipython + + In [4]: len? + Signature: len(obj, /) + Docstring: Return the number of items in a container. + Type: builtin_function_or_method + +and : + +.. code-block:: ipython + + In [5]: len?? + Signature: len(obj, /) + Docstring: Return the number of items in a container. + Type: builtin_function_or_method + +have the same output because they were compiled in a programming language other +than Python. + + +Working with mathematical formulas +---------------------------------- + +The ease of implementing mathematical formulas that work on arrays is one of +the things that make NumPy so widely used in the scientific Python community. + +For example, this is the mean square error formula (a central formula used in +supervised machine learning models that deal with regression): + +.. image:: images/np_MSE_formula.png + +Implementing this formula is simple and straightforward in NumPy: + +.. image:: images/np_MSE_implementation.png + +What makes this work so well is that ``predictions`` and ``labels`` can contain +one or a thousand values. They only need to be the same size. + +You can visualize it this way: + +.. image:: images/np_mse_viz1.png + +In this example, both the predictions and labels vectors contain three values, +meaning ``n`` has a value of three. After we carry out subtractions the values +in the vector are squared. Then NumPy sums the values, and your result is the +error value for that prediction and a score for the quality of the model. + +.. image:: images/np_mse_viz2.png + +.. image:: images/np_MSE_explanation2.png + + +How to save and load NumPy objects +---------------------------------- + +*This section covers* ``np.save``, ``np.savez``, ``np.savetxt``, +``np.load``, ``np.loadtxt`` + +----- + +You will, at some point, want to save your arrays to disk and load them back +without having to re-run the code. Fortunately, there are several ways to save +and load objects with NumPy. The ndarray objects can be saved to and loaded from +the disk files with ``loadtxt`` and ``savetxt`` functions that handle normal +text files, ``load`` and ``save`` functions that handle NumPy binary files with +a **.npy** file extension, and a ``savez`` function that handles NumPy files +with a **.npz** file extension. + +The **.npy** and **.npz** files store data, shape, dtype, and other information +required to reconstruct the ndarray in a way that allows the array to be +correctly retrieved, even when the file is on another machine with different +architecture. + +If you want to store a single ndarray object, store it as a .npy file using +``np.save``. If you want to store more than one ndarray object in a single file, +save it as a .npz file using ``np.savez``. You can also save several arrays +into a single file in compressed npz format with `savez_compressed`. + +It's easy to save and load and array with ``np.save()``. Just make sure to +specify the array you want to save and a file name. For example, if you create +this array:: + + >>> a = np.array([1, 2, 3, 4, 5, 6]) + +You can save it as "filename.npy" with:: + + >>> np.save('filename', a) + +You can use ``np.load()`` to reconstruct your array. :: + + >>> b = np.load('filename.npy') + +If you want to check your array, you can run::: + + >>> print(b) + [1 2 3 4 5 6] + +You can save a NumPy array as a plain text file like a **.csv** or **.txt** file +with ``np.savetxt``. + +For example, if you create this array:: + + >>> csv_arr = np.array([1, 2, 3, 4, 5, 6, 7, 8]) + +You can easily save it as a .csv file with the name "new_file.csv" like this:: + + >>> np.savetxt('new_file.csv', csv_arr) + +You can quickly and easily load your saved text file using ``loadtxt()``:: + + >>> np.loadtxt('new_file.csv') + array([1., 2., 3., 4., 5., 6., 7., 8.]) + +The ``savetxt()`` and ``loadtxt()`` functions accept additional optional +parameters such as header, footer, and delimiter. While text files can be easier +for sharing, .npy and .npz files are smaller and faster to read. If you need more +sophisticated handling of your text file (for example, if you need to work with +lines that contain missing values), you will want to use the `genfromtxt` +function. + +With `savetxt`, you can specify headers, footers, comments, and more. + +Learn more about :ref:`input and output routines here <routines.io>`. + + +Importing and exporting a CSV +----------------------------- + +.. save a csv + + >>> with open('music.csv', 'w') as fid: + ... n = fid.write('Artist,Genre,Listeners,Plays\n') + ... n = fid.write('Billie Holiday,Jazz,1300000,27000000\n') + ... n = fid.write('Jimmie Hendrix,Rock,2700000,70000000\n') + ... n = fid.write('Miles Davis,Jazz,1500000,48000000\n') + ... n = fid.write('SIA,Pop,2000000,74000000\n') + + + +It's simple to read in a CSV that contains existing information. The best and +easiest way to do this is to use +`Pandas <https://pandas.pydata.org>`_. :: + + >>> import pandas as pd + + >>> # If all of your columns are the same type: + >>> x = pd.read_csv('music.csv', header=0).values + >>> print(x) + [['Billie Holiday' 'Jazz' 1300000 27000000] + ['Jimmie Hendrix' 'Rock' 2700000 70000000] + ['Miles Davis' 'Jazz' 1500000 48000000] + ['SIA' 'Pop' 2000000 74000000]] + + >>> # You can also simply select the columns you need: + >>> x = pd.read_csv('music.csv', usecols=['Artist', 'Plays']).values + >>> print(x) + [['Billie Holiday' 27000000] + ['Jimmie Hendrix' 70000000] + ['Miles Davis' 48000000] + ['SIA' 74000000]] + +.. image:: images/np_pandas.png + +It's simple to use Pandas in order to export your array as well. If you are new +to NumPy, you may want to create a Pandas dataframe from the values in your +array and then write the data frame to a CSV file with Pandas. + +If you created this array "a" :: + + >>> a = np.array([[-2.58289208, 0.43014843, -1.24082018, 1.59572603], + ... [ 0.99027828, 1.17150989, 0.94125714, -0.14692469], + ... [ 0.76989341, 0.81299683, -0.95068423, 0.11769564], + ... [ 0.20484034, 0.34784527, 1.96979195, 0.51992837]]) + +.. for doctests + The continuous integration truncates dataframe display without this setting. + >>> pd.set_option('max_columns', 10) + +You could create a Pandas dataframe :: + + >>> df = pd.DataFrame(a) + >>> print(df) + 0 1 2 3 + 0 -2.582892 0.430148 -1.240820 1.595726 + 1 0.990278 1.171510 0.941257 -0.146925 + 2 0.769893 0.812997 -0.950684 0.117696 + 3 0.204840 0.347845 1.969792 0.519928 + +You can easily save your dataframe with:: + + >>> df.to_csv('pd.csv') + +And read your CSV with:: + + >>> data = pd.read_csv('pd.csv') + +.. image:: images/np_readcsv.png + +You can also save your array with the NumPy ``savetxt`` method. :: + + >>> np.savetxt('np.csv', a, fmt='%.2f', delimiter=',', header='1, 2, 3, 4') + +If you're using the command line, you can read your saved CSV any time with a +command such as:: + + $ cat np.csv + # 1, 2, 3, 4 + -2.58,0.43,-1.24,1.60 + 0.99,1.17,0.94,-0.15 + 0.77,0.81,-0.95,0.12 + 0.20,0.35,1.97,0.52 + +Or you can open the file any time with a text editor! + +If you're interested in learning more about Pandas, take a look at the +`official Pandas documentation <https://pandas.pydata.org/index.html>`_. +Learn how to install Pandas with the +`official Pandas installation information <https://pandas.pydata.org/pandas-docs/stable/install.html>`_. + + +Plotting arrays with Matplotlib +------------------------------- + +If you need to generate a plot for your values, it's very simple with +`Matplotlib <https://matplotlib.org/>`_. + +For example, you may have an array like this one:: + + >>> a = np.array([2, 1, 5, 7, 4, 6, 8, 14, 10, 9, 18, 20, 22]) + +If you already have Matplotlib installed, you can import it with:: + + >>> import matplotlib.pyplot as plt + + # If you're using Jupyter Notebook, you may also want to run the following + # line of code to display your code in the notebook: + + %matplotlib inline + +All you need to do to plot your values is run:: + + >>> plt.plot(a) + + # If you are running from a command line, you may need to do this: + # >>> plt.show() + +.. plot:: user/plots/matplotlib1.py + :align: center + :include-source: 0 + +For example, you can plot a 1D array like this:: + + >>> x = np.linspace(0, 5, 20) + >>> y = np.linspace(0, 10, 20) + >>> plt.plot(x, y, 'purple') # line + >>> plt.plot(x, y, 'o') # dots + +.. plot:: user/plots/matplotlib2.py + :align: center + :include-source: 0 + +With Matplotlib, you have access to an enormous number of visualization options. :: + + >>> fig = plt.figure() + >>> ax = fig.add_subplot(projection='3d') + >>> X = np.arange(-5, 5, 0.15) + >>> Y = np.arange(-5, 5, 0.15) + >>> X, Y = np.meshgrid(X, Y) + >>> R = np.sqrt(X**2 + Y**2) + >>> Z = np.sin(R) + + >>> ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap='viridis') + +.. plot:: user/plots/matplotlib3.py + :align: center + :include-source: 0 + + +To read more about Matplotlib and what it can do, take a look at +`the official documentation <https://matplotlib.org/>`_. +For directions regarding installing Matplotlib, see the official +`installation section <https://matplotlib.org/users/installing.html>`_. + + +------------------------------------------------------- + +*Image credits: Jay Alammar http://jalammar.github.io/* diff --git a/doc/source/user/basics.broadcasting.rst b/doc/source/user/basics.broadcasting.rst index 65584b1fd39f..ca299085aeed 100644 --- a/doc/source/user/basics.broadcasting.rst +++ b/doc/source/user/basics.broadcasting.rst @@ -1,7 +1,306 @@ +.. _basics.broadcasting: +.. _array-broadcasting-in-numpy: + ************ Broadcasting ************ -.. seealso:: :class:`numpy.broadcast` +.. seealso:: + :class:`numpy.broadcast` + + +The term broadcasting describes how NumPy treats arrays with different +shapes during arithmetic operations. Subject to certain constraints, +the smaller array is "broadcast" across the larger array so that they +have compatible shapes. Broadcasting provides a means of vectorizing +array operations so that looping occurs in C instead of Python. It does +this without making needless copies of data and usually leads to +efficient algorithm implementations. There are, however, cases where +broadcasting is a bad idea because it leads to inefficient use of memory +that slows computation. + +NumPy operations are usually done on pairs of arrays on an +element-by-element basis. In the simplest case, the two arrays must +have exactly the same shape, as in the following example: + + >>> a = np.array([1.0, 2.0, 3.0]) + >>> b = np.array([2.0, 2.0, 2.0]) + >>> a * b + array([ 2., 4., 6.]) + +NumPy's broadcasting rule relaxes this constraint when the arrays' +shapes meet certain constraints. The simplest broadcasting example occurs +when an array and a scalar value are combined in an operation: + +>>> a = np.array([1.0, 2.0, 3.0]) +>>> b = 2.0 +>>> a * b +array([ 2., 4., 6.]) + +The result is equivalent to the previous example where ``b`` was an array. +We can think of the scalar ``b`` being *stretched* during the arithmetic +operation into an array with the same shape as ``a``. The new elements in +``b``, as shown in :ref:`broadcasting.figure-1`, are simply copies of the +original scalar. The stretching analogy is +only conceptual. NumPy is smart enough to use the original scalar value +without actually making copies so that broadcasting operations are as +memory and computationally efficient as possible. + +.. figure:: broadcasting_1.svg + :alt: A scalar is broadcast to match the shape of the 1-d array it + is being multiplied to. + :name: broadcasting.figure-1 + + *Figure 1* + + *In the simplest example of broadcasting, the scalar* ``b`` *is + stretched to become an array of same shape as* ``a`` *so the shapes + are compatible for element-by-element multiplication.* + +The code in the second example is more efficient than that in the first +because broadcasting moves less memory around during the multiplication +(``b`` is a scalar rather than an array). + +.. _general-broadcasting-rules: + +General Broadcasting Rules +========================== +When operating on two arrays, NumPy compares their shapes element-wise. +It starts with the trailing (i.e. rightmost) dimensions and works its +way left. Two dimensions are compatible when + +1) they are equal, or +2) one of them is 1 + +If these conditions are not met, a +``ValueError: operands could not be broadcast together`` exception is +thrown, indicating that the arrays have incompatible shapes. The size of +the resulting array is the size that is not 1 along each axis of the inputs. + +Arrays do not need to have the same *number* of dimensions. For example, +if you have a ``256x256x3`` array of RGB values, and you want to scale +each color in the image by a different value, you can multiply the image +by a one-dimensional array with 3 values. Lining up the sizes of the +trailing axes of these arrays according to the broadcast rules, shows that +they are compatible:: + + Image (3d array): 256 x 256 x 3 + Scale (1d array): 3 + Result (3d array): 256 x 256 x 3 + +When either of the dimensions compared is one, the other is +used. In other words, dimensions with size 1 are stretched or "copied" +to match the other. + +In the following example, both the ``A`` and ``B`` arrays have axes with +length one that are expanded to a larger size during the broadcast +operation:: + + A (4d array): 8 x 1 x 6 x 1 + B (3d array): 7 x 1 x 5 + Result (4d array): 8 x 7 x 6 x 5 + + +.. _arrays.broadcasting.broadcastable: + +Broadcastable arrays +==================== + +.. index:: broadcastable + +A set of arrays is called "broadcastable" to the same shape if +the above rules produce a valid result. + +For example, if ``a.shape`` is (5,1), ``b.shape`` is (1,6), ``c.shape`` is (6,) +and ``d.shape`` is () so that *d* is a scalar, then *a*, *b*, *c*, +and *d* are all broadcastable to dimension (5,6); and + +- *a* acts like a (5,6) array where ``a[:,0]`` is broadcast to the other + columns, + +- *b* acts like a (5,6) array where ``b[0,:]`` is broadcast + to the other rows, + +- *c* acts like a (1,6) array and therefore like a (5,6) array + where ``c[:]`` is broadcast to every row, and finally, + +- *d* acts like a (5,6) array where the single value is repeated. + +Here are some more examples:: + + A (2d array): 5 x 4 + B (1d array): 1 + Result (2d array): 5 x 4 + + A (2d array): 5 x 4 + B (1d array): 4 + Result (2d array): 5 x 4 + + A (3d array): 15 x 3 x 5 + B (3d array): 15 x 1 x 5 + Result (3d array): 15 x 3 x 5 + + A (3d array): 15 x 3 x 5 + B (2d array): 3 x 5 + Result (3d array): 15 x 3 x 5 + + A (3d array): 15 x 3 x 5 + B (2d array): 3 x 1 + Result (3d array): 15 x 3 x 5 + +Here are examples of shapes that do not broadcast:: + + A (1d array): 3 + B (1d array): 4 # trailing dimensions do not match + + A (2d array): 2 x 1 + B (3d array): 8 x 4 x 3 # second from last dimensions mismatched + +An example of broadcasting when a 1-d array is added to a 2-d array:: + + >>> a = array([[ 0.0, 0.0, 0.0], + ... [10.0, 10.0, 10.0], + ... [20.0, 20.0, 20.0], + ... [30.0, 30.0, 30.0]]) + >>> b = array([1.0, 2.0, 3.0]) + >>> a + b + array([[ 1., 2., 3.], + [ 11., 12., 13.], + [ 21., 22., 23.], + [ 31., 32., 33.]]) + >>> b = array([1.0, 2.0, 3.0, 4.0]) + >>> a + b + Traceback (most recent call last): + ValueError: operands could not be broadcast together with shapes (4,3) (4,) + +As shown in :ref:`broadcasting.figure-2`, ``b`` is added to each row of ``a``. +In :ref:`broadcasting.figure-3`, an exception is raised because of the +incompatible shapes. + +.. figure:: broadcasting_2.svg + :alt: A 1-d array with shape (3) is strectched to match the 2-d array of + shape (4, 3) it is being added to, and the result is a 2-d array of shape + (4, 3). + :name: broadcasting.figure-2 + + *Figure 2* + + *A one dimensional array added to a two dimensional array results in + broadcasting if number of 1-d array elements matches the number of 2-d + array columns.* + +.. figure:: broadcasting_3.svg + :alt: A huge cross over the 2-d array of shape (4, 3) and the 1-d array + of shape (4) shows that they can not be broadcast due to mismatch + of shapes and thus produce no result. + :name: broadcasting.figure-3 + + *Figure 3* + + *When the trailing dimensions of the arrays are unequal, broadcasting fails + because it is impossible to align the values in the rows of the 1st array + with the elements of the 2nd arrays for element-by-element addition.* + +Broadcasting provides a convenient way of taking the outer product (or +any other outer operation) of two arrays. The following example shows an +outer addition operation of two 1-d arrays:: + + >>> a = np.array([0.0, 10.0, 20.0, 30.0]) + >>> b = np.array([1.0, 2.0, 3.0]) + >>> a[:, np.newaxis] + b + array([[ 1., 2., 3.], + [ 11., 12., 13.], + [ 21., 22., 23.], + [ 31., 32., 33.]]) + +.. figure:: broadcasting_4.svg + :alt: A 2-d array of shape (4, 1) and a 1-d array of shape (3) are + stretched to match their shapes and produce a resultant array + of shape (4, 3). + :name: broadcasting.figure-4 + + *Figure 4* + + *In some cases, broadcasting stretches both arrays to form an output array + larger than either of the initial arrays.* + +Here the ``newaxis`` index operator inserts a new axis into ``a``, +making it a two-dimensional ``4x1`` array. Combining the ``4x1`` array +with ``b``, which has shape ``(3,)``, yields a ``4x3`` array. + +A Practical Example: Vector Quantization +======================================== + +Broadcasting comes up quite often in real world problems. A typical example +occurs in the vector quantization (VQ) algorithm used in information theory, +classification, and other related areas. The basic operation in VQ finds +the closest point in a set of points, called ``codes`` in VQ jargon, to a given +point, called the ``observation``. In the very simple, two-dimensional case +shown below, the values in ``observation`` describe the weight and height of an +athlete to be classified. The ``codes`` represent different classes of +athletes. [#f1]_ Finding the closest point requires calculating the distance +between observation and each of the codes. The shortest distance provides the +best match. In this example, ``codes[0]`` is the closest class indicating that +the athlete is likely a basketball player. + + >>> from numpy import array, argmin, sqrt, sum + >>> observation = array([111.0, 188.0]) + >>> codes = array([[102.0, 203.0], + ... [132.0, 193.0], + ... [45.0, 155.0], + ... [57.0, 173.0]]) + >>> diff = codes - observation # the broadcast happens here + >>> dist = sqrt(sum(diff**2,axis=-1)) + >>> argmin(dist) + 0 + +In this example, the ``observation`` array is stretched to match +the shape of the ``codes`` array:: + + Observation (1d array): 2 + Codes (2d array): 4 x 2 + Diff (2d array): 4 x 2 + +.. figure:: broadcasting_5.svg + :alt: A height versus weight graph that shows data of a female + gymnast, marathon runner, basketball player, football + lineman and the athlete to be classified. Shortest distance + is found between the basketball player and the athlete + to be classified. + :name: broadcasting.figure-5 + + *Figure 5* + + *The basic operation of vector quantization calculates the distance between + an object to be classified, the dark square, and multiple known codes, the + gray circles. In this simple case, the codes represent individual classes. + More complex cases use multiple codes per class.* + +Typically, a large number of ``observations``, perhaps read from a database, +are compared to a set of ``codes``. Consider this scenario:: + + Observation (2d array): 10 x 3 + Codes (2d array): 5 x 3 + Diff (3d array): 5 x 10 x 3 + +The three-dimensional array, ``diff``, is a consequence of broadcasting, not a +necessity for the calculation. Large data sets will generate a large +intermediate array that is computationally inefficient. Instead, if each +observation is calculated individually using a Python loop around the code +in the two-dimensional example above, a much smaller array is used. + +Broadcasting is a powerful tool for writing short and usually intuitive code +that does its computations very efficiently in C. However, there are cases +when broadcasting uses unnecessarily large amounts of memory for a particular +algorithm. In these cases, it is better to write the algorithm's outer loop in +Python. This may also produce more readable code, as algorithms that use +broadcasting tend to become more difficult to interpret as the number of +dimensions in the broadcast increases. + +.. rubric:: Footnotes -.. automodule:: numpy.doc.broadcasting +.. [#f1] + In this example, weight has more impact on the distance calculation + than height because of the larger values. In practice, it is important to + normalize the height and weight, often by their standard deviation across the + data set, so that both have equal influence on the distance calculation. diff --git a/doc/source/user/basics.byteswapping.rst b/doc/source/user/basics.byteswapping.rst index 4b1008df3aa5..fecdb9ee8543 100644 --- a/doc/source/user/basics.byteswapping.rst +++ b/doc/source/user/basics.byteswapping.rst @@ -2,4 +2,152 @@ Byte-swapping ************* -.. automodule:: numpy.doc.byteswapping +Introduction to byte ordering and ndarrays +========================================== + +The ``ndarray`` is an object that provide a python array interface to data +in memory. + +It often happens that the memory that you want to view with an array is +not of the same byte ordering as the computer on which you are running +Python. + +For example, I might be working on a computer with a little-endian CPU - +such as an Intel Pentium, but I have loaded some data from a file +written by a computer that is big-endian. Let's say I have loaded 4 +bytes from a file written by a Sun (big-endian) computer. I know that +these 4 bytes represent two 16-bit integers. On a big-endian machine, a +two-byte integer is stored with the Most Significant Byte (MSB) first, +and then the Least Significant Byte (LSB). Thus the bytes are, in memory order: + +#. MSB integer 1 +#. LSB integer 1 +#. MSB integer 2 +#. LSB integer 2 + +Let's say the two integers were in fact 1 and 770. Because 770 = 256 * +3 + 2, the 4 bytes in memory would contain respectively: 0, 1, 3, 2. +The bytes I have loaded from the file would have these contents: + +>>> big_end_buffer = bytearray([0,1,3,2]) +>>> big_end_buffer +bytearray(b'\\x00\\x01\\x03\\x02') + +We might want to use an ``ndarray`` to access these integers. In that +case, we can create an array around this memory, and tell numpy that +there are two integers, and that they are 16 bit and big-endian: + +>>> import numpy as np +>>> big_end_arr = np.ndarray(shape=(2,),dtype='>i2', buffer=big_end_buffer) +>>> big_end_arr[0] +1 +>>> big_end_arr[1] +770 + +Note the array ``dtype`` above of ``>i2``. The ``>`` means 'big-endian' +(``<`` is little-endian) and ``i2`` means 'signed 2-byte integer'. For +example, if our data represented a single unsigned 4-byte little-endian +integer, the dtype string would be ``<u4``. + +In fact, why don't we try that? + +>>> little_end_u4 = np.ndarray(shape=(1,),dtype='<u4', buffer=big_end_buffer) +>>> little_end_u4[0] == 1 * 256**1 + 3 * 256**2 + 2 * 256**3 +True + +Returning to our ``big_end_arr`` - in this case our underlying data is +big-endian (data endianness) and we've set the dtype to match (the dtype +is also big-endian). However, sometimes you need to flip these around. + +.. warning:: + + Scalars currently do not include byte order information, so extracting + a scalar from an array will return an integer in native byte order. + Hence: + + >>> big_end_arr[0].dtype.byteorder == little_end_u4[0].dtype.byteorder + True + +Changing byte ordering +====================== + +As you can imagine from the introduction, there are two ways you can +affect the relationship between the byte ordering of the array and the +underlying memory it is looking at: + +* Change the byte-ordering information in the array dtype so that it + interprets the underlying data as being in a different byte order. + This is the role of ``arr.newbyteorder()`` +* Change the byte-ordering of the underlying data, leaving the dtype + interpretation as it was. This is what ``arr.byteswap()`` does. + +The common situations in which you need to change byte ordering are: + +#. Your data and dtype endianness don't match, and you want to change + the dtype so that it matches the data. +#. Your data and dtype endianness don't match, and you want to swap the + data so that they match the dtype +#. Your data and dtype endianness match, but you want the data swapped + and the dtype to reflect this + +Data and dtype endianness don't match, change dtype to match data +----------------------------------------------------------------- + +We make something where they don't match: + +>>> wrong_end_dtype_arr = np.ndarray(shape=(2,),dtype='<i2', buffer=big_end_buffer) +>>> wrong_end_dtype_arr[0] +256 + +The obvious fix for this situation is to change the dtype so it gives +the correct endianness: + +>>> fixed_end_dtype_arr = wrong_end_dtype_arr.newbyteorder() +>>> fixed_end_dtype_arr[0] +1 + +Note the array has not changed in memory: + +>>> fixed_end_dtype_arr.tobytes() == big_end_buffer +True + +Data and type endianness don't match, change data to match dtype +---------------------------------------------------------------- + +You might want to do this if you need the data in memory to be a certain +ordering. For example you might be writing the memory out to a file +that needs a certain byte ordering. + +>>> fixed_end_mem_arr = wrong_end_dtype_arr.byteswap() +>>> fixed_end_mem_arr[0] +1 + +Now the array *has* changed in memory: + +>>> fixed_end_mem_arr.tobytes() == big_end_buffer +False + +Data and dtype endianness match, swap data and dtype +---------------------------------------------------- + +You may have a correctly specified array dtype, but you need the array +to have the opposite byte order in memory, and you want the dtype to +match so the array values make sense. In this case you just do both of +the previous operations: + +>>> swapped_end_arr = big_end_arr.byteswap().newbyteorder() +>>> swapped_end_arr[0] +1 +>>> swapped_end_arr.tobytes() == big_end_buffer +False + +An easier way of casting the data to a specific dtype and byte ordering +can be achieved with the ndarray astype method: + +>>> swapped_end_arr = big_end_arr.astype('<i2') +>>> swapped_end_arr[0] +1 +>>> swapped_end_arr.tobytes() == big_end_buffer +False + + diff --git a/doc/source/user/basics.copies.rst b/doc/source/user/basics.copies.rst new file mode 100644 index 000000000000..583a59b9563a --- /dev/null +++ b/doc/source/user/basics.copies.rst @@ -0,0 +1,152 @@ +.. _basics.copies-and-views: + +**************** +Copies and views +**************** + +When operating on NumPy arrays, it is possible to access the internal data +buffer directly using a :ref:`view <view>` without copying data around. This +ensures good performance but can also cause unwanted problems if the user is +not aware of how this works. Hence, it is important to know the difference +between these two terms and to know which operations return copies and +which return views. + +The NumPy array is a data structure consisting of two parts: +the :term:`contiguous` data buffer with the actual data elements and the +metadata that contains information about the data buffer. The metadata +includes data type, strides, and other important information that helps +manipulate the :class:`.ndarray` easily. See the :ref:`numpy-internals` +section for a detailed look. + +.. _view: + +View +==== + +It is possible to access the array differently by just changing certain +metadata like :term:`stride` and :term:`dtype` without changing the +data buffer. This creates a new way of looking at the data and these new +arrays are called views. The data buffer remains the same, so any changes made +to a view reflects in the original copy. A view can be forced through the +:meth:`.ndarray.view` method. + +Copy +==== + +When a new array is created by duplicating the data buffer as well as the +metadata, it is called a copy. Changes made to the copy +do not reflect on the original array. Making a copy is slower and +memory-consuming but sometimes necessary. A copy can be forced by using +:meth:`.ndarray.copy`. + +Indexing operations +=================== + +.. seealso:: :ref:`basics.indexing` + +Views are created when elements can be addressed with offsets and strides +in the original array. Hence, basic indexing always creates views. +For example:: + + >>> x = np.arange(10) + >>> x + array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) + >>> y = x[1:3] # creates a view + >>> y + array([1, 2]) + >>> x[1:3] = [10, 11] + >>> x + array([ 0, 10, 11, 3, 4, 5, 6, 7, 8, 9]) + >>> y + array([10, 11]) + +Here, ``y`` gets changed when ``x`` is changed because it is a view. + +:ref:`advanced-indexing`, on the other hand, always creates copies. +For example:: + + >>> x = np.arange(9).reshape(3, 3) + >>> x + array([[0, 1, 2], + [3, 4, 5], + [6, 7, 8]]) + >>> y = x[[1, 2]] + >>> y + array([[3, 4, 5], + [6, 7, 8]]) + >>> y.base is None + True + +Here, ``y`` is a copy, as signified by the :attr:`base <.ndarray.base>` +attribute. We can also confirm this by assigning new values to ``x[[1, 2]]`` +which in turn will not affect ``y`` at all:: + + >>> x[[1, 2]] = [[10, 11, 12], [13, 14, 15]] + >>> x + array([[ 0, 1, 2], + [10, 11, 12], + [13, 14, 15]]) + >>> y + array([[3, 4, 5], + [6, 7, 8]]) + +It must be noted here that during the assignment of ``x[[1, 2]]`` no view +or copy is created as the assignment happens in-place. + + +Other operations +================ + +The :func:`numpy.reshape` function creates a view where possible or a copy +otherwise. In most cases, the strides can be modified to reshape the +array with a view. However, in some cases where the array becomes +non-contiguous (perhaps after a :meth:`.ndarray.transpose` operation), +the reshaping cannot be done by modifying strides and requires a copy. +In these cases, we can raise an error by assigning the new shape to the +shape attribute of the array. For example:: + + >>> x = np.ones((2, 3)) + >>> y = x.T # makes the array non-contiguous + >>> y + array([[1., 1.], + [1., 1.], + [1., 1.]]) + >>> z = y.view() + >>> z.shape = 6 + Traceback (most recent call last): + ... + AttributeError: Incompatible shape for in-place modification. Use + `.reshape()` to make a copy with the desired shape. + +Taking the example of another operation, :func:`.ravel` returns a contiguous +flattened view of the array wherever possible. On the other hand, +:meth:`.ndarray.flatten` always returns a flattened copy of the array. +However, to guarantee a view in most cases, ``x.reshape(-1)`` may be preferable. + +How to tell if the array is a view or a copy +============================================ + +The :attr:`base <.ndarray.base>` attribute of the ndarray makes it easy +to tell if an array is a view or a copy. The base attribute of a view returns +the original array while it returns ``None`` for a copy. + + >>> x = np.arange(9) + >>> x + array([0, 1, 2, 3, 4, 5, 6, 7, 8]) + >>> y = x.reshape(3, 3) + >>> y + array([[0, 1, 2], + [3, 4, 5], + [6, 7, 8]]) + >>> y.base # .reshape() creates a view + array([0, 1, 2, 3, 4, 5, 6, 7, 8]) + >>> z = y[[2, 1]] + >>> z + array([[6, 7, 8], + [3, 4, 5]]) + >>> z.base is None # advanced indexing creates a copy + True + +Note that the ``base`` attribute should not be used to determine +if an ndarray object is *new*; only if it is a view or a copy +of another ndarray. \ No newline at end of file diff --git a/doc/source/user/basics.creation.rst b/doc/source/user/basics.creation.rst index b3fa810177c7..84ff1c30e1f0 100644 --- a/doc/source/user/basics.creation.rst +++ b/doc/source/user/basics.creation.rst @@ -6,4 +6,377 @@ Array creation .. seealso:: :ref:`Array creation routines <routines.array-creation>` -.. automodule:: numpy.doc.creation +Introduction +============ + +There are 6 general mechanisms for creating arrays: + +1) Conversion from other Python structures (i.e. lists and tuples) +2) Intrinsic NumPy array creation functions (e.g. arange, ones, zeros, + etc.) +3) Replicating, joining, or mutating existing arrays +4) Reading arrays from disk, either from standard or custom formats +5) Creating arrays from raw bytes through the use of strings or buffers +6) Use of special library functions (e.g., random) + +You can use these methods to create ndarrays or :ref:`structured_arrays`. +This document will cover general methods for ndarray creation. + +1) Converting Python sequences to NumPy Arrays +============================================== + +NumPy arrays can be defined using Python sequences such as lists and +tuples. Lists and tuples are defined using ``[...]`` and ``(...)``, +respectively. Lists and tuples can define ndarray creation: + +* a list of numbers will create a 1D array, +* a list of lists will create a 2D array, +* further nested lists will create higher-dimensional arrays. In general, any array object is called an **ndarray** in NumPy. + +:: + + >>> a1D = np.array([1, 2, 3, 4]) + >>> a2D = np.array([[1, 2], [3, 4]]) + >>> a3D = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]) + +When you use :func:`numpy.array` to define a new array, you should +consider the :doc:`dtype <basics.types>` of the elements in the array, +which can be specified explicitly. This feature gives you +more control over the underlying data structures and how the elements +are handled in C/C++ functions. If you are not careful with ``dtype`` +assignments, you can get unwanted overflow, as such + +:: + + >>> a = np.array([127, 128, 129], dtype=np.int8) + >>> a + array([ 127, -128, -127], dtype=int8) + +An 8-bit signed integer represents integers from -128 to 127. +Assigning the ``int8`` array to integers outside of this range results +in overflow. This feature can often be misunderstood. If you +perform calculations with mismatching ``dtypes``, you can get unwanted +results, for example:: + + >>> a = np.array([2, 3, 4], dtype=np.uint32) + >>> b = np.array([5, 6, 7], dtype=np.uint32) + >>> c_unsigned32 = a - b + >>> print('unsigned c:', c_unsigned32, c_unsigned32.dtype) + unsigned c: [4294967293 4294967293 4294967293] uint32 + >>> c_signed32 = a - b.astype(np.int32) + >>> print('signed c:', c_signed32, c_signed32.dtype) + signed c: [-3 -3 -3] int64 + +Notice when you perform operations with two arrays of the same +``dtype``: ``uint32``, the resulting array is the same type. When you +perform operations with different ``dtype``, NumPy will +assign a new type that satisfies all of the array elements involved in +the computation, here ``uint32`` and ``int32`` can both be represented in +as ``int64``. + +The default NumPy behavior is to create arrays in either 64-bit signed +integers or double precision floating point numbers, ``int64`` and +``float``, respectively. If you expect your arrays to be a certain type, +then you need to specify the ``dtype`` while you create the array. + +2) Intrinsic NumPy array creation functions +=========================================== +.. + 40 functions seems like a small number, but the routies.array-creation + has ~47. I'm sure there are more. + +NumPy has over 40 built-in functions for creating arrays as laid +out in the :ref:`Array creation routines <routines.array-creation>`. +These functions can be split into roughly three categories, based on the +dimension of the array they create: + +1) 1D arrays +2) 2D arrays +3) ndarrays + +1 - 1D array creation functions +------------------------------- + +The 1D array creation functions e.g. :func:`numpy.linspace` and +:func:`numpy.arange` generally need at least two inputs, ``start`` and +``stop``. + +:func:`numpy.arange` creates arrays with regularly incrementing values. +Check the documentation for complete information and examples. A few +examples are shown:: + + >>> np.arange(10) + array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) + >>> np.arange(2, 10, dtype=float) + array([ 2., 3., 4., 5., 6., 7., 8., 9.]) + >>> np.arange(2, 3, 0.1) + array([ 2. , 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9]) + +Note: best practice for :func:`numpy.arange` is to use integer start, end, and +step values. There are some subtleties regarding ``dtype``. In the second +example, the ``dtype`` is defined. In the third example, the array is +``dtype=float`` to accommodate the step size of ``0.1``. Due to roundoff error, +the ``stop`` value is sometimes included. + +:func:`numpy.linspace` will create arrays with a specified number of elements, and +spaced equally between the specified beginning and end values. For +example: :: + + >>> np.linspace(1., 4., 6) + array([ 1. , 1.6, 2.2, 2.8, 3.4, 4. ]) + +The advantage of this creation function is that you guarantee the +number of elements and the starting and end point. The previous +``arange(start, stop, step)`` will not include the value ``stop``. + +2 - 2D array creation functions +------------------------------- + +The 2D array creation functions e.g. :func:`numpy.eye`, :func:`numpy.diag`, and :func:`numpy.vander` +define properties of special matrices represented as 2D arrays. + +``np.eye(n, m)`` defines a 2D identity matrix. The elements where i=j (row index and column index are equal) are 1 +and the rest are 0, as such:: + + >>> np.eye(3) + array([[1., 0., 0.], + [0., 1., 0.], + [0., 0., 1.]]) + >>> np.eye(3, 5) + array([[1., 0., 0., 0., 0.], + [0., 1., 0., 0., 0.], + [0., 0., 1., 0., 0.]]) + +:func:`numpy.diag` can define either a square 2D array with given values along +the diagonal *or* if given a 2D array returns a 1D array that is +only the diagonal elements. The two array creation functions can be helpful while +doing linear algebra, as such:: + + >>> np.diag([1, 2, 3]) + array([[1, 0, 0], + [0, 2, 0], + [0, 0, 3]]) + >>> np.diag([1, 2, 3], 1) + array([[0, 1, 0, 0], + [0, 0, 2, 0], + [0, 0, 0, 3], + [0, 0, 0, 0]]) + >>> a = np.array([[1, 2], [3, 4]]) + >>> np.diag(a) + array([1, 4]) + +``vander(x, n)`` defines a Vandermonde matrix as a 2D NumPy array. Each column +of the Vandermonde matrix is a decreasing power of the input 1D array or +list or tuple, +``x`` where the highest polynomial order is ``n-1``. This array creation +routine is helpful in generating linear least squares models, as such:: + + >>> np.vander(np.linspace(0, 2, 5), 2) + array([[0. , 1. ], + [0.5, 1. ], + [1. , 1. ], + [1.5, 1. ], + [2. , 1. ]]) + >>> np.vander([1, 2, 3, 4], 2) + array([[1, 1], + [2, 1], + [3, 1], + [4, 1]]) + >>> np.vander((1, 2, 3, 4), 4) + array([[ 1, 1, 1, 1], + [ 8, 4, 2, 1], + [27, 9, 3, 1], + [64, 16, 4, 1]]) + +3 - general ndarray creation functions +-------------------------------------- + +The ndarray creation functions e.g. :func:`numpy.ones`, +:func:`numpy.zeros`, and :meth:`~numpy.random.Generator.random` define +arrays based upon the desired shape. The ndarray creation functions +can create arrays with any dimension by specifying how many dimensions +and length along that dimension in a tuple or list. + +:func:`numpy.zeros` will create an array filled with 0 values with the +specified shape. The default dtype is ``float64``:: + + >>> np.zeros((2, 3)) + array([[0., 0., 0.], + [0., 0., 0.]]) + >>> np.zeros((2, 3, 2)) + array([[[0., 0.], + [0., 0.], + [0., 0.]], + <BLANKLINE> + [[0., 0.], + [0., 0.], + [0., 0.]]]) + +:func:`numpy.ones` will create an array filled with 1 values. It is identical to +``zeros`` in all other respects as such:: + + >>> np.ones((2, 3)) + array([[ 1., 1., 1.], + [ 1., 1., 1.]]) + >>> np.ones((2, 3, 2)) + array([[[1., 1.], + [1., 1.], + [1., 1.]], + <BLANKLINE> + [[1., 1.], + [1., 1.], + [1., 1.]]]) + +The :meth:`~numpy.random.Generator.random` method of the result of +``default_rng`` will create an array filled with random +values between 0 and 1. It is included with the :func:`numpy.random` +library. Below, two arrays are created with shapes (2,3) and (2,3,2), +respectively. The seed is set to 42 so you can reproduce these +pseudorandom numbers:: + + >>> from numpy.random import default_rng + >>> default_rng(42).random((2,3)) + array([[0.77395605, 0.43887844, 0.85859792], + [0.69736803, 0.09417735, 0.97562235]]) + >>> default_rng(42).random((2,3,2)) + array([[[0.77395605, 0.43887844], + [0.85859792, 0.69736803], + [0.09417735, 0.97562235]], + [[0.7611397 , 0.78606431], + [0.12811363, 0.45038594], + [0.37079802, 0.92676499]]]) + +:func:`numpy.indices` will create a set of arrays (stacked as a one-higher +dimensioned array), one per dimension with each representing variation in that +dimension: :: + + >>> np.indices((3,3)) + array([[[0, 0, 0], + [1, 1, 1], + [2, 2, 2]], + [[0, 1, 2], + [0, 1, 2], + [0, 1, 2]]]) + +This is particularly useful for evaluating functions of multiple dimensions on +a regular grid. + +3) Replicating, joining, or mutating existing arrays +==================================================== + +Once you have created arrays, you can replicate, join, or mutate those +existing arrays to create new arrays. When you assign an array or its +elements to a new variable, you have to explicitly :func:`numpy.copy` the array, +otherwise the variable is a view into the original array. Consider the +following example:: + + >>> a = np.array([1, 2, 3, 4, 5, 6]) + >>> b = a[:2] + >>> b += 1 + >>> print('a =', a, '; b =', b) + a = [2 3 3 4 5 6] ; b = [2 3] + +In this example, you did not create a new array. You created a variable, +``b`` that viewed the first 2 elements of ``a``. When you added 1 to ``b`` you +would get the same result by adding 1 to ``a[:2]``. If you want to create a +*new* array, use the :func:`numpy.copy` array creation routine as such:: + + >>> a = np.array([1, 2, 3, 4]) + >>> b = a[:2].copy() + >>> b += 1 + >>> print('a = ', a, 'b = ', b) + a = [1 2 3 4] b = [2 3] + +For more information and examples look at :ref:`Copies and Views +<quickstart.copies-and-views>`. + +There are a number of routines to join existing arrays e.g. :func:`numpy.vstack`, +:func:`numpy.hstack`, and :func:`numpy.block`. Here is an example of joining four 2-by-2 +arrays into a 4-by-4 array using ``block``:: + + >>> A = np.ones((2, 2)) + >>> B = np.eye(2, 2) + >>> C = np.zeros((2, 2)) + >>> D = np.diag((-3, -4)) + >>> np.block([[A, B], [C, D]]) + array([[ 1., 1., 1., 0. ], + [ 1., 1., 0., 1. ], + [ 0., 0., -3., 0. ], + [ 0., 0., 0., -4. ]]) + +Other routines use similar syntax to join ndarrays. Check the +routine's documentation for further examples and syntax. + +4) Reading arrays from disk, either from standard or custom formats +=================================================================== + +This is the most common case of large array creation. The details depend +greatly on the format of data on disk. This section gives general pointers on +how to handle various formats. For more detailed examples of IO look at +:ref:`How to Read and Write files <how-to-io>`. + +Standard Binary Formats +----------------------- + +Various fields have standard formats for array data. The following lists the +ones with known Python libraries to read them and return NumPy arrays (there +may be others for which it is possible to read and convert to NumPy arrays so +check the last section as well) +:: + + HDF5: h5py + FITS: Astropy + +Examples of formats that cannot be read directly but for which it is not hard to +convert are those formats supported by libraries like PIL (able to read and +write many image formats such as jpg, png, etc). + +Common ASCII Formats +-------------------- + +Delimited files such as comma separated value (csv) and tab separated +value (tsv) files are used for programs like Excel and LabView. Python +functions can read and parse these files line-by-line. NumPy has two +standard routines for importing a file with delimited data :func:`numpy.loadtxt` +and :func:`numpy.genfromtxt`. These functions have more involved use cases in +:doc:`how-to-io`. A simple example given a ``simple.csv``: + +.. code-block:: bash + + $ cat simple.csv + x, y + 0, 0 + 1, 1 + 2, 4 + 3, 9 + +Importing ``simple.csv`` is accomplished using :func:`loadtxt`:: + + >>> np.loadtxt('simple.csv', delimiter = ',', skiprows = 1) # doctest: +SKIP + array([[0., 0.], + [1., 1.], + [2., 4.], + [3., 9.]]) + + +More generic ASCII files can be read using `scipy.io` and `Pandas +<https://pandas.pydata.org/>`_. + +5) Creating arrays from raw bytes through the use of strings or buffers +======================================================================= + +There are a variety of approaches one can use. If the file has a relatively +simple format then one can write a simple I/O library and use the NumPy +``fromfile()`` function and ``.tofile()`` method to read and write NumPy arrays +directly (mind your byteorder though!) If a good C or C++ library exists that +read the data, one can wrap that library with a variety of techniques though +that certainly is much more work and requires significantly more advanced +knowledge to interface with C or C++. + +6) Use of special library functions (e.g., SciPy, Pandas, and OpenCV) +===================================================================== + +NumPy is the fundamental library for array containers in the Python Scientific Computing +stack. Many Python libraries, including SciPy, Pandas, and OpenCV, use NumPy ndarrays +as the common format for data exchange, These libraries can create, +operate on, and work with NumPy arrays. diff --git a/doc/source/user/basics.dispatch.rst b/doc/source/user/basics.dispatch.rst new file mode 100644 index 000000000000..089a7df17063 --- /dev/null +++ b/doc/source/user/basics.dispatch.rst @@ -0,0 +1,272 @@ +.. _basics.dispatch: + +******************************* +Writing custom array containers +******************************* + +Numpy's dispatch mechanism, introduced in numpy version v1.16 is the +recommended approach for writing custom N-dimensional array containers that are +compatible with the numpy API and provide custom implementations of numpy +functionality. Applications include `dask <http://dask.pydata.org>`_ arrays, an +N-dimensional array distributed across multiple nodes, and `cupy +<https://docs-cupy.chainer.org/en/stable/>`_ arrays, an N-dimensional array on +a GPU. + +To get a feel for writing custom array containers, we'll begin with a simple +example that has rather narrow utility but illustrates the concepts involved. + +>>> import numpy as np +>>> class DiagonalArray: +... def __init__(self, N, value): +... self._N = N +... self._i = value +... def __repr__(self): +... return f"{self.__class__.__name__}(N={self._N}, value={self._i})" +... def __array__(self, dtype=None): +... return self._i * np.eye(self._N, dtype=dtype) + +Our custom array can be instantiated like: + +>>> arr = DiagonalArray(5, 1) +>>> arr +DiagonalArray(N=5, value=1) + +We can convert to a numpy array using :func:`numpy.array` or +:func:`numpy.asarray`, which will call its ``__array__`` method to obtain a +standard ``numpy.ndarray``. + +>>> np.asarray(arr) +array([[1., 0., 0., 0., 0.], + [0., 1., 0., 0., 0.], + [0., 0., 1., 0., 0.], + [0., 0., 0., 1., 0.], + [0., 0., 0., 0., 1.]]) + +If we operate on ``arr`` with a numpy function, numpy will again use the +``__array__`` interface to convert it to an array and then apply the function +in the usual way. + +>>> np.multiply(arr, 2) +array([[2., 0., 0., 0., 0.], + [0., 2., 0., 0., 0.], + [0., 0., 2., 0., 0.], + [0., 0., 0., 2., 0.], + [0., 0., 0., 0., 2.]]) + + +Notice that the return type is a standard ``numpy.ndarray``. + +>>> type(np.multiply(arr, 2)) +numpy.ndarray + +How can we pass our custom array type through this function? Numpy allows a +class to indicate that it would like to handle computations in a custom-defined +way through the interfaces ``__array_ufunc__`` and ``__array_function__``. Let's +take one at a time, starting with ``_array_ufunc__``. This method covers +:ref:`ufuncs`, a class of functions that includes, for example, +:func:`numpy.multiply` and :func:`numpy.sin`. + +The ``__array_ufunc__`` receives: + +- ``ufunc``, a function like ``numpy.multiply`` +- ``method``, a string, differentiating between ``numpy.multiply(...)`` and + variants like ``numpy.multiply.outer``, ``numpy.multiply.accumulate``, and so + on. For the common case, ``numpy.multiply(...)``, ``method == '__call__'``. +- ``inputs``, which could be a mixture of different types +- ``kwargs``, keyword arguments passed to the function + +For this example we will only handle the method ``__call__`` + +>>> from numbers import Number +>>> class DiagonalArray: +... def __init__(self, N, value): +... self._N = N +... self._i = value +... def __repr__(self): +... return f"{self.__class__.__name__}(N={self._N}, value={self._i})" +... def __array__(self, dtype=None): +... return self._i * np.eye(self._N, dtype=dtype) +... def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): +... if method == '__call__': +... N = None +... scalars = [] +... for input in inputs: +... if isinstance(input, Number): +... scalars.append(input) +... elif isinstance(input, self.__class__): +... scalars.append(input._i) +... if N is not None: +... if N != self._N: +... raise TypeError("inconsistent sizes") +... else: +... N = self._N +... else: +... return NotImplemented +... return self.__class__(N, ufunc(*scalars, **kwargs)) +... else: +... return NotImplemented + +Now our custom array type passes through numpy functions. + +>>> arr = DiagonalArray(5, 1) +>>> np.multiply(arr, 3) +DiagonalArray(N=5, value=3) +>>> np.add(arr, 3) +DiagonalArray(N=5, value=4) +>>> np.sin(arr) +DiagonalArray(N=5, value=0.8414709848078965) + +At this point ``arr + 3`` does not work. + +>>> arr + 3 +TypeError: unsupported operand type(s) for *: 'DiagonalArray' and 'int' + +To support it, we need to define the Python interfaces ``__add__``, ``__lt__``, +and so on to dispatch to the corresponding ufunc. We can achieve this +conveniently by inheriting from the mixin +:class:`~numpy.lib.mixins.NDArrayOperatorsMixin`. + +>>> import numpy.lib.mixins +>>> class DiagonalArray(numpy.lib.mixins.NDArrayOperatorsMixin): +... def __init__(self, N, value): +... self._N = N +... self._i = value +... def __repr__(self): +... return f"{self.__class__.__name__}(N={self._N}, value={self._i})" +... def __array__(self, dtype=None): +... return self._i * np.eye(self._N, dtype=dtype) +... def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): +... if method == '__call__': +... N = None +... scalars = [] +... for input in inputs: +... if isinstance(input, Number): +... scalars.append(input) +... elif isinstance(input, self.__class__): +... scalars.append(input._i) +... if N is not None: +... if N != self._N: +... raise TypeError("inconsistent sizes") +... else: +... N = self._N +... else: +... return NotImplemented +... return self.__class__(N, ufunc(*scalars, **kwargs)) +... else: +... return NotImplemented + +>>> arr = DiagonalArray(5, 1) +>>> arr + 3 +DiagonalArray(N=5, value=4) +>>> arr > 0 +DiagonalArray(N=5, value=True) + +Now let's tackle ``__array_function__``. We'll create dict that maps numpy +functions to our custom variants. + +>>> HANDLED_FUNCTIONS = {} +>>> class DiagonalArray(numpy.lib.mixins.NDArrayOperatorsMixin): +... def __init__(self, N, value): +... self._N = N +... self._i = value +... def __repr__(self): +... return f"{self.__class__.__name__}(N={self._N}, value={self._i})" +... def __array__(self, dtype=None): +... return self._i * np.eye(self._N, dtype=dtype) +... def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): +... if method == '__call__': +... N = None +... scalars = [] +... for input in inputs: +... # In this case we accept only scalar numbers or DiagonalArrays. +... if isinstance(input, Number): +... scalars.append(input) +... elif isinstance(input, self.__class__): +... scalars.append(input._i) +... if N is not None: +... if N != self._N: +... raise TypeError("inconsistent sizes") +... else: +... N = self._N +... else: +... return NotImplemented +... return self.__class__(N, ufunc(*scalars, **kwargs)) +... else: +... return NotImplemented +... def __array_function__(self, func, types, args, kwargs): +... if func not in HANDLED_FUNCTIONS: +... return NotImplemented +... # Note: this allows subclasses that don't override +... # __array_function__ to handle DiagonalArray objects. +... if not all(issubclass(t, self.__class__) for t in types): +... return NotImplemented +... return HANDLED_FUNCTIONS[func](*args, **kwargs) +... + +A convenient pattern is to define a decorator ``implements`` that can be used +to add functions to ``HANDLED_FUNCTIONS``. + +>>> def implements(np_function): +... "Register an __array_function__ implementation for DiagonalArray objects." +... def decorator(func): +... HANDLED_FUNCTIONS[np_function] = func +... return func +... return decorator +... + +Now we write implementations of numpy functions for ``DiagonalArray``. +For completeness, to support the usage ``arr.sum()`` add a method ``sum`` that +calls ``numpy.sum(self)``, and the same for ``mean``. + +>>> @implements(np.sum) +... def sum(arr): +... "Implementation of np.sum for DiagonalArray objects" +... return arr._i * arr._N +... +>>> @implements(np.mean) +... def mean(arr): +... "Implementation of np.mean for DiagonalArray objects" +... return arr._i / arr._N +... +>>> arr = DiagonalArray(5, 1) +>>> np.sum(arr) +5 +>>> np.mean(arr) +0.2 + +If the user tries to use any numpy functions not included in +``HANDLED_FUNCTIONS``, a ``TypeError`` will be raised by numpy, indicating that +this operation is not supported. For example, concatenating two +``DiagonalArrays`` does not produce another diagonal array, so it is not +supported. + +>>> np.concatenate([arr, arr]) +TypeError: no implementation found for 'numpy.concatenate' on types that implement __array_function__: [<class '__main__.DiagonalArray'>] + +Additionally, our implementations of ``sum`` and ``mean`` do not accept the +optional arguments that numpy's implementation does. + +>>> np.sum(arr, axis=0) +TypeError: sum() got an unexpected keyword argument 'axis' + +The user always has the option of converting to a normal ``numpy.ndarray`` with +:func:`numpy.asarray` and using standard numpy from there. + +>>> np.concatenate([np.asarray(arr), np.asarray(arr)]) +array([[1., 0., 0., 0., 0.], + [0., 1., 0., 0., 0.], + [0., 0., 1., 0., 0.], + [0., 0., 0., 1., 0.], + [0., 0., 0., 0., 1.], + [1., 0., 0., 0., 0.], + [0., 1., 0., 0., 0.], + [0., 0., 1., 0., 0.], + [0., 0., 0., 1., 0.], + [0., 0., 0., 0., 1.]]) + +Refer to the `dask source code <https://github.com/dask/dask>`_ and +`cupy source code <https://github.com/cupy/cupy>`_ for more fully-worked +examples of custom array containers. + +See also :doc:`NEP 18<neps:nep-0018-array-function-protocol>`. + diff --git a/doc/source/user/basics.indexing.rst b/doc/source/user/basics.indexing.rst index 8844adcae622..264c3d721f4f 100644 --- a/doc/source/user/basics.indexing.rst +++ b/doc/source/user/basics.indexing.rst @@ -1,9 +1,903 @@ .. _basics.indexing: -******** -Indexing -******** +**************************************** +Indexing on :class:`ndarrays <.ndarray>` +**************************************** -.. seealso:: :ref:`Indexing routines <routines.indexing>` +.. seealso:: -.. automodule:: numpy.doc.indexing + :ref:`Indexing routines <routines.indexing>` + +.. sectionauthor:: adapted from "Guide to NumPy" by Travis E. Oliphant + +.. currentmodule:: numpy + +.. index:: indexing, slicing + +:class:`ndarrays <ndarray>` can be indexed using the standard Python +``x[obj]`` syntax, where *x* is the array and *obj* the selection. +There are different kinds of indexing available depending on *obj*: +basic indexing, advanced indexing and field access. + +Most of the following examples show the use of indexing when +referencing data in an array. The examples work just as well +when assigning to an array. See :ref:`assigning-values-to-indexed-arrays` for +specific examples and explanations on how assignments work. + +Note that in Python, ``x[(exp1, exp2, ..., expN)]`` is equivalent to +``x[exp1, exp2, ..., expN]``; the latter is just syntactic sugar +for the former. + + +Basic indexing +-------------- + +.. _single-element-indexing: + +Single element indexing +^^^^^^^^^^^^^^^^^^^^^^^ + +Single element indexing works +exactly like that for other standard Python sequences. It is 0-based, +and accepts negative indices for indexing from the end of the array. :: + + >>> x = np.arange(10) + >>> x[2] + 2 + >>> x[-2] + 8 + +It is not necessary to +separate each dimension's index into its own set of square brackets. :: + + >>> x.shape = (2, 5) # now x is 2-dimensional + >>> x[1, 3] + 8 + >>> x[1, -1] + 9 + +Note that if one indexes a multidimensional array with fewer indices +than dimensions, one gets a subdimensional array. For example: :: + + >>> x[0] + array([0, 1, 2, 3, 4]) + +That is, each index specified selects the array corresponding to the +rest of the dimensions selected. In the above example, choosing 0 +means that the remaining dimension of length 5 is being left unspecified, +and that what is returned is an array of that dimensionality and size. +It must be noted that the returned array is a :term:`view`, i.e., it is not a +copy of the original, but points to the same values in memory as does the +original array. +In this case, the 1-D array at the first position (0) is returned. +So using a single index on the returned array, results in a single +element being returned. That is: :: + + >>> x[0][2] + 2 + +So note that ``x[0, 2] == x[0][2]`` though the second case is more +inefficient as a new temporary array is created after the first index +that is subsequently indexed by 2. + +.. note:: + + NumPy uses C-order indexing. That means that the last + index usually represents the most rapidly changing memory location, + unlike Fortran or IDL, where the first index represents the most + rapidly changing location in memory. This difference represents a + great potential for confusion. + + +Slicing and striding +^^^^^^^^^^^^^^^^^^^^ + +Basic slicing extends Python's basic concept of slicing to N +dimensions. Basic slicing occurs when *obj* is a :class:`slice` object +(constructed by ``start:stop:step`` notation inside of brackets), an +integer, or a tuple of slice objects and integers. :py:data:`Ellipsis` +and :const:`newaxis` objects can be interspersed with these as +well. + +.. deprecated:: 1.15.0 + + In order to remain backward compatible with a common usage in + Numeric, basic slicing is also initiated if the selection object is + any non-ndarray and non-tuple sequence (such as a :class:`list`) containing + :class:`slice` objects, the :py:data:`Ellipsis` object, or the :const:`newaxis` + object, but not for integer arrays or other embedded sequences. + +.. index:: + triple: ndarray; special methods; getitem + triple: ndarray; special methods; setitem + single: ellipsis + single: newaxis + +The simplest case of indexing with *N* integers returns an :ref:`array +scalar <arrays.scalars>` representing the corresponding item. As in +Python, all indices are zero-based: for the *i*-th index :math:`n_i`, +the valid range is :math:`0 \le n_i < d_i` where :math:`d_i` is the +*i*-th element of the shape of the array. Negative indices are +interpreted as counting from the end of the array (*i.e.*, if +:math:`n_i < 0`, it means :math:`n_i + d_i`). + + +All arrays generated by basic slicing are always :term:`views <view>` +of the original array. + +.. note:: + + NumPy slicing creates a :term:`view` instead of a copy as in the case of + built-in Python sequences such as string, tuple and list. + Care must be taken when extracting + a small portion from a large array which becomes useless after the + extraction, because the small portion extracted contains a reference + to the large original array whose memory will not be released until + all arrays derived from it are garbage-collected. In such cases an + explicit ``copy()`` is recommended. + +The standard rules of sequence slicing apply to basic slicing on a +per-dimension basis (including using a step index). Some useful +concepts to remember include: + +- The basic slice syntax is ``i:j:k`` where *i* is the starting index, + *j* is the stopping index, and *k* is the step (:math:`k\neq0`). + This selects the *m* elements (in the corresponding dimension) with + index values *i*, *i + k*, ..., *i + (m - 1) k* where + :math:`m = q + (r\neq0)` and *q* and *r* are the quotient and remainder + obtained by dividing *j - i* by *k*: *j - i = q k + r*, so that + *i + (m - 1) k < j*. + For example:: + + >>> x = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) + >>> x[1:7:2] + array([1, 3, 5]) + +- Negative *i* and *j* are interpreted as *n + i* and *n + j* where + *n* is the number of elements in the corresponding dimension. + Negative *k* makes stepping go towards smaller indices. + From the above example:: + + >>> x[-2:10] + array([8, 9]) + >>> x[-3:3:-1] + array([7, 6, 5, 4]) + +- Assume *n* is the number of elements in the dimension being + sliced. Then, if *i* is not given it defaults to 0 for *k > 0* and + *n - 1* for *k < 0* . If *j* is not given it defaults to *n* for *k > 0* + and *-n-1* for *k < 0* . If *k* is not given it defaults to 1. Note that + ``::`` is the same as ``:`` and means select all indices along this + axis. + From the above example:: + + >>> x[5:] + array([5, 6, 7, 8, 9]) + +- If the number of objects in the selection tuple is less than + *N*, then ``:`` is assumed for any subsequent dimensions. + For example:: + + >>> x = np.array([[[1],[2],[3]], [[4],[5],[6]]]) + >>> x.shape + (2, 3, 1) + >>> x[1:2] + array([[[4], + [5], + [6]]]) + +- An integer, *i*, returns the same values as ``i:i+1`` + **except** the dimensionality of the returned object is reduced by + 1. In particular, a selection tuple with the *p*-th + element an integer (and all other entries ``:``) returns the + corresponding sub-array with dimension *N - 1*. If *N = 1* + then the returned object is an array scalar. These objects are + explained in :ref:`arrays.scalars`. + +- If the selection tuple has all entries ``:`` except the + *p*-th entry which is a slice object ``i:j:k``, + then the returned array has dimension *N* formed by + concatenating the sub-arrays returned by integer indexing of + elements *i*, *i+k*, ..., *i + (m - 1) k < j*, + +- Basic slicing with more than one non-``:`` entry in the slicing + tuple, acts like repeated application of slicing using a single + non-``:`` entry, where the non-``:`` entries are successively taken + (with all other non-``:`` entries replaced by ``:``). Thus, + ``x[ind1, ..., ind2,:]`` acts like ``x[ind1][..., ind2, :]`` under basic + slicing. + + .. warning:: The above is **not** true for advanced indexing. + +- You may use slicing to set values in the array, but (unlike lists) you + can never grow the array. The size of the value to be set in + ``x[obj] = value`` must be (broadcastable) to the same shape as + ``x[obj]``. + +- A slicing tuple can always be constructed as *obj* + and used in the ``x[obj]`` notation. Slice objects can be used in + the construction in place of the ``[start:stop:step]`` + notation. For example, ``x[1:10:5, ::-1]`` can also be implemented + as ``obj = (slice(1, 10, 5), slice(None, None, -1)); x[obj]`` . This + can be useful for constructing generic code that works on arrays + of arbitrary dimensions. See :ref:`dealing-with-variable-indices` + for more information. + +.. index:: + pair: ndarray; view + + +Dimensional indexing tools +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +There are some tools to facilitate the easy matching of array shapes with +expressions and in assignments. + +:py:data:`Ellipsis` expands to the number of ``:`` objects needed for the +selection tuple to index all dimensions. In most cases, this means that the +length of the expanded selection tuple is ``x.ndim``. There may only be a +single ellipsis present. +From the above example:: + + >>> x[..., 0] + array([[1, 2, 3], + [4, 5, 6]]) + +This is equivalent to:: + + >>> x[:, :, 0] + array([[1, 2, 3], + [4, 5, 6]]) + +Each :const:`newaxis` object in the selection tuple serves to expand +the dimensions of the resulting selection by one unit-length +dimension. The added dimension is the position of the :const:`newaxis` +object in the selection tuple. :const:`newaxis` is an alias for +``None``, and ``None`` can be used in place of this with the same result. +From the above example:: + + >>> x[:, np.newaxis, :, :].shape + (2, 1, 3, 1) + >>> x[:, None, :, :].shape + (2, 1, 3, 1) + +This can be handy to combine two +arrays in a way that otherwise would require explicit reshaping +operations. For example:: + + >>> x = np.arange(5) + >>> x[:, np.newaxis] + x[np.newaxis, :] + array([[0, 1, 2, 3, 4], + [1, 2, 3, 4, 5], + [2, 3, 4, 5, 6], + [3, 4, 5, 6, 7], + [4, 5, 6, 7, 8]]) + + +.. _advanced-indexing: + +Advanced indexing +----------------- + +Advanced indexing is triggered when the selection object, *obj*, is a +non-tuple sequence object, an :class:`ndarray` (of data type integer or bool), +or a tuple with at least one sequence object or ndarray (of data type +integer or bool). There are two types of advanced indexing: integer +and Boolean. + +Advanced indexing always returns a *copy* of the data (contrast with +basic slicing that returns a :term:`view`). + +.. warning:: + + The definition of advanced indexing means that ``x[(1, 2, 3),]`` is + fundamentally different than ``x[(1, 2, 3)]``. The latter is + equivalent to ``x[1, 2, 3]`` which will trigger basic selection while + the former will trigger advanced indexing. Be sure to understand + why this occurs. + + Also recognize that ``x[[1, 2, 3]]`` will trigger advanced indexing, + whereas due to the deprecated Numeric compatibility mentioned above, + ``x[[1, 2, slice(None)]]`` will trigger basic slicing. + +Integer array indexing +^^^^^^^^^^^^^^^^^^^^^^ + +Integer array indexing allows selection of arbitrary items in the array +based on their *N*-dimensional index. Each integer array represents a number +of indices into that dimension. + +Negative values are permitted in the index arrays and work as they do with +single indices or slices:: + + >>> x = np.arange(10, 1, -1) + >>> x + array([10, 9, 8, 7, 6, 5, 4, 3, 2]) + >>> x[np.array([3, 3, 1, 8])] + array([7, 7, 9, 2]) + >>> x[np.array([3, 3, -3, 8])] + array([7, 7, 4, 2]) + +If the index values are out of bounds then an ``IndexError`` is thrown:: + + >>> x = np.array([[1, 2], [3, 4], [5, 6]]) + >>> x[np.array([1, -1])] + array([[3, 4], + [5, 6]]) + >>> x[np.array([3, 4])] + IndexError: index 3 is out of bounds for axis 0 with size 3 + +When the index consists of as many integer arrays as dimensions of the array +being indexed, the indexing is straightforward, but different from slicing. + +Advanced indices always are :ref:`broadcast<basics.broadcasting>` and +iterated as *one*:: + + result[i_1, ..., i_M] == x[ind_1[i_1, ..., i_M], ind_2[i_1, ..., i_M], + ..., ind_N[i_1, ..., i_M]] + +Note that the resulting shape is identical to the (broadcast) indexing array +shapes ``ind_1, ..., ind_N``. If the indices cannot be broadcast to the +same shape, an exception ``IndexError: shape mismatch: indexing arrays could +not be broadcast together with shapes...`` is raised. + +Indexing with multidimensional index arrays tend +to be more unusual uses, but they are permitted, and they are useful for some +problems. We’ll start with the simplest multidimensional case:: + + >>> y = np.arange(35).reshape(5, 7) + >>> y + array([[ 0, 1, 2, 3, 4, 5, 6], + [ 7, 8, 9, 10, 11, 12, 13], + [14, 15, 16, 17, 18, 19, 20], + [21, 22, 23, 24, 25, 26, 27], + [28, 29, 30, 31, 32, 33, 34]]) + >>> y[np.array([0, 2, 4]), np.array([0, 1, 2])] + array([ 0, 15, 30]) + +In this case, if the index arrays have a matching shape, and there is an +index array for each dimension of the array being indexed, the resultant +array has the same shape as the index arrays, and the values correspond +to the index set for each position in the index arrays. In this example, +the first index value is 0 for both index arrays, and thus the first value +of the resultant array is ``y[0, 0]``. The next value is ``y[2, 1]``, and +the last is ``y[4, 2]``. + +If the index arrays do not have the same shape, there is an attempt to +broadcast them to the same shape. If they cannot be broadcast to the same +shape, an exception is raised:: + + >>> y[np.array([0, 2, 4]), np.array([0, 1])] + IndexError: shape mismatch: indexing arrays could not be broadcast + together with shapes (3,) (2,) + +The broadcasting mechanism permits index arrays to be combined with +scalars for other indices. The effect is that the scalar value is used +for all the corresponding values of the index arrays:: + + >>> y[np.array([0, 2, 4]), 1] + array([ 1, 15, 29]) + +Jumping to the next level of complexity, it is possible to only partially +index an array with index arrays. It takes a bit of thought to understand +what happens in such cases. For example if we just use one index array +with y:: + + >>> y[np.array([0, 2, 4])] + array([[ 0, 1, 2, 3, 4, 5, 6], + [14, 15, 16, 17, 18, 19, 20], + [28, 29, 30, 31, 32, 33, 34]]) + +It results in the construction of a new array where each value of the +index array selects one row from the array being indexed and the resultant +array has the resulting shape (number of index elements, size of row). + +In general, the shape of the resultant array will be the concatenation of +the shape of the index array (or the shape that all the index arrays were +broadcast to) with the shape of any unused dimensions (those not indexed) +in the array being indexed. + +.. rubric:: Example + +From each row, a specific element should be selected. The row index is just +``[0, 1, 2]`` and the column index specifies the element to choose for the +corresponding row, here ``[0, 1, 0]``. Using both together the task +can be solved using advanced indexing:: + + >>> x = np.array([[1, 2], [3, 4], [5, 6]]) + >>> x[[0, 1, 2], [0, 1, 0]] + array([1, 4, 5]) + +To achieve a behaviour similar to the basic slicing above, broadcasting can be +used. The function :func:`ix_` can help with this broadcasting. This is best +understood with an example. + +.. rubric:: Example + +From a 4x3 array the corner elements should be selected using advanced +indexing. Thus all elements for which the column is one of ``[0, 2]`` and +the row is one of ``[0, 3]`` need to be selected. To use advanced indexing +one needs to select all elements *explicitly*. Using the method explained +previously one could write:: + + >>> x = np.array([[ 0, 1, 2], + ... [ 3, 4, 5], + ... [ 6, 7, 8], + ... [ 9, 10, 11]]) + >>> rows = np.array([[0, 0], + ... [3, 3]], dtype=np.intp) + >>> columns = np.array([[0, 2], + ... [0, 2]], dtype=np.intp) + >>> x[rows, columns] + array([[ 0, 2], + [ 9, 11]]) + +However, since the indexing arrays above just repeat themselves, +broadcasting can be used (compare operations such as +``rows[:, np.newaxis] + columns``) to simplify this:: + + >>> rows = np.array([0, 3], dtype=np.intp) + >>> columns = np.array([0, 2], dtype=np.intp) + >>> rows[:, np.newaxis] + array([[0], + [3]]) + >>> x[rows[:, np.newaxis], columns] + array([[ 0, 2], + [ 9, 11]]) + +This broadcasting can also be achieved using the function :func:`ix_`: + + >>> x[np.ix_(rows, columns)] + array([[ 0, 2], + [ 9, 11]]) + +Note that without the ``np.ix_`` call, only the diagonal elements would +be selected:: + + >>> x[rows, columns] + array([ 0, 11]) + +This difference is the most important thing to remember about +indexing with multiple advanced indices. + +.. rubric:: Example + +A real-life example of where advanced indexing may be useful is for a color +lookup table where we want to map the values of an image into RGB triples for +display. The lookup table could have a shape (nlookup, 3). Indexing +such an array with an image with shape (ny, nx) with dtype=np.uint8 +(or any integer type so long as values are with the bounds of the +lookup table) will result in an array of shape (ny, nx, 3) where a +triple of RGB values is associated with each pixel location. + + +Boolean array indexing +^^^^^^^^^^^^^^^^^^^^^^ + +This advanced indexing occurs when *obj* is an array object of Boolean +type, such as may be returned from comparison operators. A single +boolean index array is practically identical to ``x[obj.nonzero()]`` where, +as described above, :meth:`obj.nonzero() <ndarray.nonzero>` returns a +tuple (of length :attr:`obj.ndim <ndarray.ndim>`) of integer index +arrays showing the :py:data:`True` elements of *obj*. However, it is +faster when ``obj.shape == x.shape``. + +If ``obj.ndim == x.ndim``, ``x[obj]`` returns a 1-dimensional array +filled with the elements of *x* corresponding to the :py:data:`True` +values of *obj*. The search order will be :term:`row-major`, +C-style. If *obj* has :py:data:`True` values at entries that are outside +of the bounds of *x*, then an index error will be raised. If *obj* is +smaller than *x* it is identical to filling it with :py:data:`False`. + +A common use case for this is filtering for desired element values. +For example, one may wish to select all entries from an array which +are not :const:`NaN`:: + + >>> x = np.array([[1., 2.], [np.nan, 3.], [np.nan, np.nan]]) + >>> x[~np.isnan(x)] + array([1., 2., 3.]) + +Or wish to add a constant to all negative elements:: + + >>> x = np.array([1., -1., -2., 3]) + >>> x[x < 0] += 20 + >>> x + array([1., 19., 18., 3.]) + +In general if an index includes a Boolean array, the result will be +identical to inserting ``obj.nonzero()`` into the same position +and using the integer array indexing mechanism described above. +``x[ind_1, boolean_array, ind_2]`` is equivalent to +``x[(ind_1,) + boolean_array.nonzero() + (ind_2,)]``. + +If there is only one Boolean array and no integer indexing array present, +this is straightforward. Care must only be taken to make sure that the +boolean index has *exactly* as many dimensions as it is supposed to work +with. + +In general, when the boolean array has fewer dimensions than the array being +indexed, this is equivalent to ``x[b, ...]``, which means x is indexed by b +followed by as many ``:`` as are needed to fill out the rank of x. Thus the +shape of the result is one dimension containing the number of True elements of +the boolean array, followed by the remaining dimensions of the array being +indexed:: + + >>> x = np.arange(35).reshape(5, 7) + >>> b = x > 20 + >>> b[:, 5] + array([False, False, False, True, True]) + >>> x[b[:, 5]] + array([[21, 22, 23, 24, 25, 26, 27], + [28, 29, 30, 31, 32, 33, 34]]) + +Here the 4th and 5th rows are selected from the indexed array and +combined to make a 2-D array. + +.. rubric:: Example + +From an array, select all rows which sum up to less or equal two:: + + >>> x = np.array([[0, 1], [1, 1], [2, 2]]) + >>> rowsum = x.sum(-1) + >>> x[rowsum <= 2, :] + array([[0, 1], + [1, 1]]) + + +Combining multiple Boolean indexing arrays or a Boolean with an integer +indexing array can best be understood with the +:meth:`obj.nonzero() <ndarray.nonzero>` analogy. The function :func:`ix_` +also supports boolean arrays and will work without any surprises. + +.. rubric:: Example + +Use boolean indexing to select all rows adding up to an even +number. At the same time columns 0 and 2 should be selected with an +advanced integer index. Using the :func:`ix_` function this can be done +with:: + + >>> x = np.array([[ 0, 1, 2], + ... [ 3, 4, 5], + ... [ 6, 7, 8], + ... [ 9, 10, 11]]) + >>> rows = (x.sum(-1) % 2) == 0 + >>> rows + array([False, True, False, True]) + >>> columns = [0, 2] + >>> x[np.ix_(rows, columns)] + array([[ 3, 5], + [ 9, 11]]) + +Without the ``np.ix_`` call, only the diagonal elements would be +selected. + +Or without ``np.ix_`` (compare the integer array examples):: + + >>> rows = rows.nonzero()[0] + >>> x[rows[:, np.newaxis], columns] + array([[ 3, 5], + [ 9, 11]]) + +.. rubric:: Example + +Use a 2-D boolean array of shape (2, 3) +with four True elements to select rows from a 3-D array of shape +(2, 3, 5) results in a 2-D result of shape (4, 5):: + + >>> x = np.arange(30).reshape(2, 3, 5) + >>> x + array([[[ 0, 1, 2, 3, 4], + [ 5, 6, 7, 8, 9], + [10, 11, 12, 13, 14]], + [[15, 16, 17, 18, 19], + [20, 21, 22, 23, 24], + [25, 26, 27, 28, 29]]]) + >>> b = np.array([[True, True, False], [False, True, True]]) + >>> x[b] + array([[ 0, 1, 2, 3, 4], + [ 5, 6, 7, 8, 9], + [20, 21, 22, 23, 24], + [25, 26, 27, 28, 29]]) + + +.. _combining-advanced-and-basic-indexing: + +Combining advanced and basic indexing +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When there is at least one slice (``:``), ellipsis (``...``) or :const:`newaxis` +in the index (or the array has more dimensions than there are advanced indices), +then the behaviour can be more complicated. It is like concatenating the +indexing result for each advanced index element. + +In the simplest case, there is only a *single* advanced index combined with +a slice. For example:: + + >>> y = np.arange(35).reshape(5,7) + >>> y[np.array([0, 2, 4]), 1:3] + array([[ 1, 2], + [15, 16], + [29, 30]]) + +In effect, the slice and index array operation are independent. The slice +operation extracts columns with index 1 and 2, (i.e. the 2nd and 3rd columns), +followed by the index array operation which extracts rows with index 0, 2 and 4 +(i.e the first, third and fifth rows). This is equivalent to:: + + >>> y[:, 1:3][np.array([0, 2, 4]), :] + array([[ 1, 2], + [15, 16], + [29, 30]]) + +A single advanced index can, for example, replace a slice and the result array +will be the same. However, it is a copy and may have a different memory layout. +A slice is preferable when it is possible. +For example:: + + >>> x = np.array([[ 0, 1, 2], + ... [ 3, 4, 5], + ... [ 6, 7, 8], + ... [ 9, 10, 11]]) + >>> x[1:2, 1:3] + array([[4, 5]]) + >>> x[1:2, [1, 2]] + array([[4, 5]]) + +The easiest way to understand a combination of *multiple* advanced indices may +be to think in terms of the resulting shape. There are two parts to the indexing +operation, the subspace defined by the basic indexing (excluding integers) and +the subspace from the advanced indexing part. Two cases of index combination +need to be distinguished: + +* The advanced indices are separated by a slice, :py:data:`Ellipsis` or + :const:`newaxis`. For example ``x[arr1, :, arr2]``. +* The advanced indices are all next to each other. + For example ``x[..., arr1, arr2, :]`` but *not* ``x[arr1, :, 1]`` + since ``1`` is an advanced index in this regard. + +In the first case, the dimensions resulting from the advanced indexing +operation come first in the result array, and the subspace dimensions after +that. +In the second case, the dimensions from the advanced indexing operations +are inserted into the result array at the same spot as they were in the +initial array (the latter logic is what makes simple advanced indexing +behave just like slicing). + +.. rubric:: Example + +Suppose ``x.shape`` is (10, 20, 30) and ``ind`` is a (2, 3, 4)-shaped +indexing :class:`intp` array, then ``result = x[..., ind, :]`` has +shape (10, 2, 3, 4, 30) because the (20,)-shaped subspace has been +replaced with a (2, 3, 4)-shaped broadcasted indexing subspace. If +we let *i, j, k* loop over the (2, 3, 4)-shaped subspace then +``result[..., i, j, k, :] = x[..., ind[i, j, k], :]``. This example +produces the same result as :meth:`x.take(ind, axis=-2) <ndarray.take>`. + +.. rubric:: Example + +Let ``x.shape`` be (10, 20, 30, 40, 50) and suppose ``ind_1`` +and ``ind_2`` can be broadcast to the shape (2, 3, 4). Then +``x[:, ind_1, ind_2]`` has shape (10, 2, 3, 4, 40, 50) because the +(20, 30)-shaped subspace from X has been replaced with the +(2, 3, 4) subspace from the indices. However, +``x[:, ind_1, :, ind_2]`` has shape (2, 3, 4, 10, 30, 50) because there +is no unambiguous place to drop in the indexing subspace, thus +it is tacked-on to the beginning. It is always possible to use +:meth:`.transpose() <ndarray.transpose>` to move the subspace +anywhere desired. Note that this example cannot be replicated +using :func:`take`. + +.. rubric:: Example + +Slicing can be combined with broadcasted boolean indices:: + + >>> x = np.arange(35).reshape(5, 7) + >>> b = x > 20 + >>> b + array([[False, False, False, False, False, False, False], + [False, False, False, False, False, False, False], + [False, False, False, False, False, False, False], + [ True, True, True, True, True, True, True], + [ True, True, True, True, True, True, True]]) + >>> x[b[:, 5], 1:3] + array([[22, 23], + [29, 30]]) + + +.. _arrays.indexing.fields: + +Field access +------------- + +.. seealso:: :ref:`structured_arrays` + +If the :class:`ndarray` object is a structured array the :term:`fields <field>` +of the array can be accessed by indexing the array with strings, +dictionary-like. + +Indexing ``x['field-name']`` returns a new :term:`view` to the array, +which is of the same shape as *x* (except when the field is a +sub-array) but of data type ``x.dtype['field-name']`` and contains +only the part of the data in the specified field. Also, +:ref:`record array <arrays.classes.rec>` scalars can be "indexed" this way. + +Indexing into a structured array can also be done with a list of field names, +e.g. ``x[['field-name1', 'field-name2']]``. As of NumPy 1.16, this returns a +view containing only those fields. In older versions of NumPy, it returned a +copy. See the user guide section on :ref:`structured_arrays` for more +information on multifield indexing. + +If the accessed field is a sub-array, the dimensions of the sub-array +are appended to the shape of the result. +For example:: + + >>> x = np.zeros((2, 2), dtype=[('a', np.int32), ('b', np.float64, (3, 3))]) + >>> x['a'].shape + (2, 2) + >>> x['a'].dtype + dtype('int32') + >>> x['b'].shape + (2, 2, 3, 3) + >>> x['b'].dtype + dtype('float64') + +.. _flat-iterator-indexing: + +Flat Iterator indexing +---------------------- + +:attr:`x.flat <ndarray.flat>` returns an iterator that will iterate +over the entire array (in C-contiguous style with the last index +varying the fastest). This iterator object can also be indexed using +basic slicing or advanced indexing as long as the selection object is +not a tuple. This should be clear from the fact that :attr:`x.flat +<ndarray.flat>` is a 1-dimensional view. It can be used for integer +indexing with 1-dimensional C-style-flat indices. The shape of any +returned array is therefore the shape of the integer indexing object. + +.. index:: + single: indexing + single: ndarray + + +.. _assigning-values-to-indexed-arrays: + +Assigning values to indexed arrays +---------------------------------- + +As mentioned, one can select a subset of an array to assign to using +a single index, slices, and index and mask arrays. The value being +assigned to the indexed array must be shape consistent (the same shape +or broadcastable to the shape the index produces). For example, it is +permitted to assign a constant to a slice: :: + + >>> x = np.arange(10) + >>> x[2:7] = 1 + +or an array of the right size: :: + + >>> x[2:7] = np.arange(5) + +Note that assignments may result in changes if assigning +higher types to lower types (like floats to ints) or even +exceptions (assigning complex to floats or ints): :: + + >>> x[1] = 1.2 + >>> x[1] + 1 + >>> x[1] = 1.2j + TypeError: can't convert complex to int + + +Unlike some of the references (such as array and mask indices) +assignments are always made to the original data in the array +(indeed, nothing else would make sense!). Note though, that some +actions may not work as one may naively expect. This particular +example is often surprising to people: :: + + >>> x = np.arange(0, 50, 10) + >>> x + array([ 0, 10, 20, 30, 40]) + >>> x[np.array([1, 1, 3, 1])] += 1 + >>> x + array([ 0, 11, 20, 31, 40]) + +Where people expect that the 1st location will be incremented by 3. +In fact, it will only be incremented by 1. The reason is that +a new array is extracted from the original (as a temporary) containing +the values at 1, 1, 3, 1, then the value 1 is added to the temporary, +and then the temporary is assigned back to the original array. Thus +the value of the array at ``x[1] + 1`` is assigned to ``x[1]`` three times, +rather than being incremented 3 times. + +.. _dealing-with-variable-indices: + +Dealing with variable numbers of indices within programs +-------------------------------------------------------- + +The indexing syntax is very powerful but limiting when dealing with +a variable number of indices. For example, if you want to write +a function that can handle arguments with various numbers of +dimensions without having to write special case code for each +number of possible dimensions, how can that be done? If one +supplies to the index a tuple, the tuple will be interpreted +as a list of indices. For example:: + + >>> z = np.arange(81).reshape(3, 3, 3, 3) + >>> indices = (1, 1, 1, 1) + >>> z[indices] + 40 + +So one can use code to construct tuples of any number of indices +and then use these within an index. + +Slices can be specified within programs by using the slice() function +in Python. For example: :: + + >>> indices = (1, 1, 1, slice(0, 2)) # same as [1, 1, 1, 0:2] + >>> z[indices] + array([39, 40]) + +Likewise, ellipsis can be specified by code by using the Ellipsis +object: :: + + >>> indices = (1, Ellipsis, 1) # same as [1, ..., 1] + >>> z[indices] + array([[28, 31, 34], + [37, 40, 43], + [46, 49, 52]]) + +For this reason, it is possible to use the output from the +:meth:`np.nonzero() <ndarray.nonzero>` function directly as an index since +it always returns a tuple of index arrays. + +Because the special treatment of tuples, they are not automatically +converted to an array as a list would be. As an example: :: + + >>> z[[1, 1, 1, 1]] # produces a large array + array([[[[27, 28, 29], + [30, 31, 32], ... + >>> z[(1, 1, 1, 1)] # returns a single value + 40 + + +Detailed notes +-------------- + +These are some detailed notes, which are not of importance for day to day +indexing (in no particular order): + +* The native NumPy indexing type is ``intp`` and may differ from the + default integer array type. ``intp`` is the smallest data type + sufficient to safely index any array; for advanced indexing it may be + faster than other types. +* For advanced assignments, there is in general no guarantee for the + iteration order. This means that if an element is set more than once, + it is not possible to predict the final result. +* An empty (tuple) index is a full scalar index into a zero-dimensional array. + ``x[()]`` returns a *scalar* if ``x`` is zero-dimensional and a view + otherwise. On the other hand, ``x[...]`` always returns a view. +* If a zero-dimensional array is present in the index *and* it is a full + integer index the result will be a *scalar* and not a zero-dimensional array. + (Advanced indexing is not triggered.) +* When an ellipsis (``...``) is present but has no size (i.e. replaces zero + ``:``) the result will still always be an array. A view if no advanced index + is present, otherwise a copy. +* The ``nonzero`` equivalence for Boolean arrays does not hold for zero + dimensional boolean arrays. +* When the result of an advanced indexing operation has no elements but an + individual index is out of bounds, whether or not an ``IndexError`` is + raised is undefined (e.g. ``x[[], [123]]`` with ``123`` being out of bounds). +* When a *casting* error occurs during assignment (for example updating a + numerical array using a sequence of strings), the array being assigned + to may end up in an unpredictable partially updated state. + However, if any other error (such as an out of bounds index) occurs, the + array will remain unchanged. +* The memory layout of an advanced indexing result is optimized for each + indexing operation and no particular memory order can be assumed. +* When using a subclass (especially one which manipulates its shape), the + default ``ndarray.__setitem__`` behaviour will call ``__getitem__`` for + *basic* indexing but not for *advanced* indexing. For such a subclass it may + be preferable to call ``ndarray.__setitem__`` with a *base class* ndarray + view on the data. This *must* be done if the subclasses ``__getitem__`` does + not return views. diff --git a/doc/source/user/basics.io.genfromtxt.rst b/doc/source/user/basics.io.genfromtxt.rst index 21832e5aa2bf..8fe7565aa730 100644 --- a/doc/source/user/basics.io.genfromtxt.rst +++ b/doc/source/user/basics.io.genfromtxt.rst @@ -27,19 +27,19 @@ Defining the input ================== The only mandatory argument of :func:`~numpy.genfromtxt` is the source of -the data. It can be a string, a list of strings, or a generator. If a -single string is provided, it is assumed to be the name of a local or -remote file, or an open file-like object with a :meth:`read` method, for -example, a file or :class:`io.StringIO` object. If a list of strings -or a generator returning strings is provided, each string is treated as one -line in a file. When the URL of a remote file is passed, the file is -automatically downloaded to the current directory and opened. +the data. It can be a string, a list of strings, a generator or an open +file-like object with a ``read`` method, for example, a file or +:class:`io.StringIO` object. If a single string is provided, it is assumed +to be the name of a local or remote file. If a list of strings or a generator +returning strings is provided, each string is treated as one line in a file. +When the URL of a remote file is passed, the file is automatically downloaded +to the current directory and opened. Recognized file types are text files and archives. Currently, the function -recognizes :class:`gzip` and :class:`bz2` (`bzip2`) archives. The type of +recognizes ``gzip`` and ``bz2`` (``bzip2``) archives. The type of the archive is determined from the extension of the file: if the filename -ends with ``'.gz'``, a :class:`gzip` archive is expected; if it ends with -``'bz2'``, a :class:`bzip2` archive is assumed. +ends with ``'.gz'``, a ``gzip`` archive is expected; if it ends with +``'bz2'``, a ``bzip2`` archive is assumed. @@ -98,13 +98,11 @@ This behavior can be overwritten by setting the optional argument >>> # Without autostrip >>> np.genfromtxt(StringIO(data), delimiter=",", dtype="|U5") array([['1', ' abc ', ' 2'], - ['3', ' xxx', ' 4']], - dtype='|U5') + ['3', ' xxx', ' 4']], dtype='<U5') >>> # With autostrip >>> np.genfromtxt(StringIO(data), delimiter=",", dtype="|U5", autostrip=True) array([['1', 'abc', '2'], - ['3', 'xxx', '4']], - dtype='|U5') + ['3', 'xxx', '4']], dtype='<U5') The ``comments`` argument @@ -127,11 +125,11 @@ marker(s) is simply ignored:: ... 9, 0 ... """ >>> np.genfromtxt(StringIO(data), comments="#", delimiter=",") - [[ 1. 2.] - [ 3. 4.] - [ 5. 6.] - [ 7. 8.] - [ 9. 0.]] + array([[1., 2.], + [3., 4.], + [5., 6.], + [7., 8.], + [9., 0.]]) .. versionadded:: 1.7.0 @@ -362,9 +360,9 @@ The ``converters`` argument Usually, defining a dtype is sufficient to define how the sequence of strings must be converted. However, some additional control may sometimes be required. For example, we may want to make sure that a date in a format -``YYYY/MM/DD`` is converted to a :class:`datetime` object, or that a string -like ``xx%`` is properly converted to a float between 0 and 1. In such -cases, we should define conversion functions with the ``converters`` +``YYYY/MM/DD`` is converted to a :class:`~datetime.datetime` object, or that +a string like ``xx%`` is properly converted to a float between 0 and 1. In +such cases, we should define conversion functions with the ``converters`` arguments. The value of this argument is typically a dictionary with column indices or @@ -376,12 +374,12 @@ single element of the wanted type. In the following example, the second column is converted from as string representing a percentage to a float between 0 and 1:: - >>> convertfunc = lambda x: float(x.strip("%"))/100. + >>> convertfunc = lambda x: float(x.strip(b"%"))/100. >>> data = u"1, 2.3%, 45.\n6, 78.9%, 0" >>> names = ("i", "p", "n") >>> # General case ..... >>> np.genfromtxt(StringIO(data), delimiter=",", names=names) - array([(1.0, nan, 45.0), (6.0, nan, 0.0)], + array([(1., nan, 45.), (6., nan, 0.)], dtype=[('i', '<f8'), ('p', '<f8'), ('n', '<f8')]) We need to keep in mind that by default, ``dtype=float``. A float is @@ -429,7 +427,7 @@ previous example, we used a converter to transform an empty string into a float. However, user-defined converters may rapidly become cumbersome to manage. -The :func:`~nummpy.genfromtxt` function provides two other complementary +The :func:`~numpy.genfromtxt` function provides two other complementary mechanisms: the ``missing_values`` argument is used to recognize missing data and a second argument, ``filling_values``, is used to process these missing data. @@ -439,7 +437,7 @@ process these missing data. By default, any empty string is marked as missing. We can also consider more complex strings, such as ``"N/A"`` or ``"???"`` to represent missing -or invalid data. The ``missing_values`` argument accepts three kind +or invalid data. The ``missing_values`` argument accepts three kinds of values: a string or a comma-separated string @@ -516,21 +514,15 @@ output array will then be a :class:`~numpy.ma.MaskedArray`. Shortcut functions ================== -In addition to :func:`~numpy.genfromtxt`, the :mod:`numpy.lib.io` module +In addition to :func:`~numpy.genfromtxt`, the :mod:`numpy.lib.npyio` module provides several convenience functions derived from :func:`~numpy.genfromtxt`. These functions work the same way as the original, but they have different default values. -:func:`~numpy.ndfromtxt` - Always set ``usemask=False``. - The output is always a standard :class:`numpy.ndarray`. -:func:`~numpy.mafromtxt` - Always set ``usemask=True``. - The output is always a :class:`~numpy.ma.MaskedArray` -:func:`~numpy.recfromtxt` +:func:`~numpy.npyio.recfromtxt` Returns a standard :class:`numpy.recarray` (if ``usemask=False``) or a - :class:`~numpy.ma.MaskedRecords` array (if ``usemaske=True``). The + :class:`~numpy.ma.mrecords.MaskedRecords` array (if ``usemaske=True``). The default dtype is ``dtype=None``, meaning that the types of each column will be automatically determined. -:func:`~numpy.recfromcsv` - Like :func:`~numpy.recfromtxt`, but with a default ``delimiter=","``. +:func:`~numpy.npyio.recfromcsv` + Like :func:`~numpy.npyio.recfromtxt`, but with a default ``delimiter=","``. diff --git a/doc/source/user/basics.rec.rst b/doc/source/user/basics.rec.rst index b885c9e77dee..1e6f30506c7a 100644 --- a/doc/source/user/basics.rec.rst +++ b/doc/source/user/basics.rec.rst @@ -4,10 +4,652 @@ Structured arrays ***************** -.. automodule:: numpy.doc.structured_arrays +Introduction +============ + +Structured arrays are ndarrays whose datatype is a composition of simpler +datatypes organized as a sequence of named :term:`fields <field>`. For example, +:: + + >>> x = np.array([('Rex', 9, 81.0), ('Fido', 3, 27.0)], + ... dtype=[('name', 'U10'), ('age', 'i4'), ('weight', 'f4')]) + >>> x + array([('Rex', 9, 81.), ('Fido', 3, 27.)], + dtype=[('name', 'U10'), ('age', '<i4'), ('weight', '<f4')]) + +Here ``x`` is a one-dimensional array of length two whose datatype is a +structure with three fields: 1. A string of length 10 or less named 'name', 2. +a 32-bit integer named 'age', and 3. a 32-bit float named 'weight'. + +If you index ``x`` at position 1 you get a structure:: + + >>> x[1] + ('Fido', 3, 27.0) + +You can access and modify individual fields of a structured array by indexing +with the field name:: + + >>> x['age'] + array([9, 3], dtype=int32) + >>> x['age'] = 5 + >>> x + array([('Rex', 5, 81.), ('Fido', 5, 27.)], + dtype=[('name', 'U10'), ('age', '<i4'), ('weight', '<f4')]) + +Structured datatypes are designed to be able to mimic 'structs' in the C +language, and share a similar memory layout. They are meant for interfacing with +C code and for low-level manipulation of structured buffers, for example for +interpreting binary blobs. For these purposes they support specialized features +such as subarrays, nested datatypes, and unions, and allow control over the +memory layout of the structure. + +Users looking to manipulate tabular data, such as stored in csv files, may find +other pydata projects more suitable, such as xarray, pandas, or DataArray. +These provide a high-level interface for tabular data analysis and are better +optimized for that use. For instance, the C-struct-like memory layout of +structured arrays in numpy can lead to poor cache behavior in comparison. + +.. _defining-structured-types: + +Structured Datatypes +==================== + +A structured datatype can be thought of as a sequence of bytes of a certain +length (the structure's :term:`itemsize`) which is interpreted as a collection +of fields. Each field has a name, a datatype, and a byte offset within the +structure. The datatype of a field may be any numpy datatype including other +structured datatypes, and it may also be a :term:`subarray data type` which +behaves like an ndarray of a specified shape. The offsets of the fields are +arbitrary, and fields may even overlap. These offsets are usually determined +automatically by numpy, but can also be specified. + +Structured Datatype Creation +---------------------------- + +Structured datatypes may be created using the function :func:`numpy.dtype`. +There are 4 alternative forms of specification which vary in flexibility and +conciseness. These are further documented in the +:ref:`Data Type Objects <arrays.dtypes.constructing>` reference page, and in +summary they are: + +1. A list of tuples, one tuple per field + + Each tuple has the form ``(fieldname, datatype, shape)`` where shape is + optional. ``fieldname`` is a string (or tuple if titles are used, see + :ref:`Field Titles <titles>` below), ``datatype`` may be any object + convertible to a datatype, and ``shape`` is a tuple of integers specifying + subarray shape. + + >>> np.dtype([('x', 'f4'), ('y', np.float32), ('z', 'f4', (2, 2))]) + dtype([('x', '<f4'), ('y', '<f4'), ('z', '<f4', (2, 2))]) + + If ``fieldname`` is the empty string ``''``, the field will be given a + default name of the form ``f#``, where ``#`` is the integer index of the + field, counting from 0 from the left:: + + >>> np.dtype([('x', 'f4'), ('', 'i4'), ('z', 'i8')]) + dtype([('x', '<f4'), ('f1', '<i4'), ('z', '<i8')]) + + The byte offsets of the fields within the structure and the total + structure itemsize are determined automatically. + +2. A string of comma-separated dtype specifications + + In this shorthand notation any of the :ref:`string dtype specifications + <arrays.dtypes.constructing>` may be used in a string and separated by + commas. The itemsize and byte offsets of the fields are determined + automatically, and the field names are given the default names ``f0``, + ``f1``, etc. :: + + >>> np.dtype('i8, f4, S3') + dtype([('f0', '<i8'), ('f1', '<f4'), ('f2', 'S3')]) + >>> np.dtype('3int8, float32, (2, 3)float64') + dtype([('f0', 'i1', (3,)), ('f1', '<f4'), ('f2', '<f8', (2, 3))]) + +3. A dictionary of field parameter arrays + + This is the most flexible form of specification since it allows control + over the byte-offsets of the fields and the itemsize of the structure. + + The dictionary has two required keys, 'names' and 'formats', and four + optional keys, 'offsets', 'itemsize', 'aligned' and 'titles'. The values + for 'names' and 'formats' should respectively be a list of field names and + a list of dtype specifications, of the same length. The optional 'offsets' + value should be a list of integer byte-offsets, one for each field within + the structure. If 'offsets' is not given the offsets are determined + automatically. The optional 'itemsize' value should be an integer + describing the total size in bytes of the dtype, which must be large + enough to contain all the fields. + :: + + >>> np.dtype({'names': ['col1', 'col2'], 'formats': ['i4', 'f4']}) + dtype([('col1', '<i4'), ('col2', '<f4')]) + >>> np.dtype({'names': ['col1', 'col2'], + ... 'formats': ['i4', 'f4'], + ... 'offsets': [0, 4], + ... 'itemsize': 12}) + dtype({'names': ['col1', 'col2'], 'formats': ['<i4', '<f4'], 'offsets': [0, 4], 'itemsize': 12}) + + Offsets may be chosen such that the fields overlap, though this will mean + that assigning to one field may clobber any overlapping field's data. As + an exception, fields of :class:`numpy.object_` type cannot overlap with + other fields, because of the risk of clobbering the internal object + pointer and then dereferencing it. + + The optional 'aligned' value can be set to ``True`` to make the automatic + offset computation use aligned offsets (see :ref:`offsets-and-alignment`), + as if the 'align' keyword argument of :func:`numpy.dtype` had been set to + True. + + The optional 'titles' value should be a list of titles of the same length + as 'names', see :ref:`Field Titles <titles>` below. + +4. A dictionary of field names + + The use of this form of specification is discouraged, but documented here + because older numpy code may use it. The keys of the dictionary are the + field names and the values are tuples specifying type and offset:: + + >>> np.dtype({'col1': ('i1', 0), 'col2': ('f4', 1)}) + dtype([('col1', 'i1'), ('col2', '<f4')]) + + This form is discouraged because Python dictionaries do not preserve order + in Python versions before Python 3.6, and the order of the fields in a + structured dtype has meaning. :ref:`Field Titles <titles>` may be + specified by using a 3-tuple, see below. + +Manipulating and Displaying Structured Datatypes +------------------------------------------------ + +The list of field names of a structured datatype can be found in the ``names`` +attribute of the dtype object:: + + >>> d = np.dtype([('x', 'i8'), ('y', 'f4')]) + >>> d.names + ('x', 'y') + +The field names may be modified by assigning to the ``names`` attribute using a +sequence of strings of the same length. + +The dtype object also has a dictionary-like attribute, ``fields``, whose keys +are the field names (and :ref:`Field Titles <titles>`, see below) and whose +values are tuples containing the dtype and byte offset of each field. :: + + >>> d.fields + mappingproxy({'x': (dtype('int64'), 0), 'y': (dtype('float32'), 8)}) + +Both the ``names`` and ``fields`` attributes will equal ``None`` for +unstructured arrays. The recommended way to test if a dtype is structured is +with `if dt.names is not None` rather than `if dt.names`, to account for dtypes +with 0 fields. + +The string representation of a structured datatype is shown in the "list of +tuples" form if possible, otherwise numpy falls back to using the more general +dictionary form. + +.. _offsets-and-alignment: + +Automatic Byte Offsets and Alignment +------------------------------------ + +Numpy uses one of two methods to automatically determine the field byte offsets +and the overall itemsize of a structured datatype, depending on whether +``align=True`` was specified as a keyword argument to :func:`numpy.dtype`. + +By default (``align=False``), numpy will pack the fields together such that +each field starts at the byte offset the previous field ended, and the fields +are contiguous in memory. :: + + >>> def print_offsets(d): + ... print("offsets:", [d.fields[name][1] for name in d.names]) + ... print("itemsize:", d.itemsize) + >>> print_offsets(np.dtype('u1, u1, i4, u1, i8, u2')) + offsets: [0, 1, 2, 6, 7, 15] + itemsize: 17 + +If ``align=True`` is set, numpy will pad the structure in the same way many C +compilers would pad a C-struct. Aligned structures can give a performance +improvement in some cases, at the cost of increased datatype size. Padding +bytes are inserted between fields such that each field's byte offset will be a +multiple of that field's alignment, which is usually equal to the field's size +in bytes for simple datatypes, see :c:member:`PyArray_Descr.alignment`. The +structure will also have trailing padding added so that its itemsize is a +multiple of the largest field's alignment. :: + + >>> print_offsets(np.dtype('u1, u1, i4, u1, i8, u2', align=True)) + offsets: [0, 1, 4, 8, 16, 24] + itemsize: 32 + +Note that although almost all modern C compilers pad in this way by default, +padding in C structs is C-implementation-dependent so this memory layout is not +guaranteed to exactly match that of a corresponding struct in a C program. Some +work may be needed, either on the numpy side or the C side, to obtain exact +correspondence. + +If offsets were specified using the optional ``offsets`` key in the +dictionary-based dtype specification, setting ``align=True`` will check that +each field's offset is a multiple of its size and that the itemsize is a +multiple of the largest field size, and raise an exception if not. + +If the offsets of the fields and itemsize of a structured array satisfy the +alignment conditions, the array will have the ``ALIGNED`` :attr:`flag +<numpy.ndarray.flags>` set. + +A convenience function :func:`numpy.lib.recfunctions.repack_fields` converts an +aligned dtype or array to a packed one and vice versa. It takes either a dtype +or structured ndarray as an argument, and returns a copy with fields re-packed, +with or without padding bytes. + +.. _titles: + +Field Titles +------------ + +In addition to field names, fields may also have an associated :term:`title`, +an alternate name, which is sometimes used as an additional description or +alias for the field. The title may be used to index an array, just like a +field name. + +To add titles when using the list-of-tuples form of dtype specification, the +field name may be specified as a tuple of two strings instead of a single +string, which will be the field's title and field name respectively. For +example:: + + >>> np.dtype([(('my title', 'name'), 'f4')]) + dtype([(('my title', 'name'), '<f4')]) + +When using the first form of dictionary-based specification, the titles may be +supplied as an extra ``'titles'`` key as described above. When using the second +(discouraged) dictionary-based specification, the title can be supplied by +providing a 3-element tuple ``(datatype, offset, title)`` instead of the usual +2-element tuple:: + + >>> np.dtype({'name': ('i4', 0, 'my title')}) + dtype([(('my title', 'name'), '<i4')]) + +The ``dtype.fields`` dictionary will contain titles as keys, if any +titles are used. This means effectively that a field with a title will be +represented twice in the fields dictionary. The tuple values for these fields +will also have a third element, the field title. Because of this, and because +the ``names`` attribute preserves the field order while the ``fields`` +attribute may not, it is recommended to iterate through the fields of a dtype +using the ``names`` attribute of the dtype, which will not list titles, as +in:: + + >>> for name in d.names: + ... print(d.fields[name][:2]) + (dtype('int64'), 0) + (dtype('float32'), 8) + +Union types +----------- + +Structured datatypes are implemented in numpy to have base type +:class:`numpy.void` by default, but it is possible to interpret other numpy +types as structured types using the ``(base_dtype, dtype)`` form of dtype +specification described in +:ref:`Data Type Objects <arrays.dtypes.constructing>`. Here, ``base_dtype`` is +the desired underlying dtype, and fields and flags will be copied from +``dtype``. This dtype is similar to a 'union' in C. + +Indexing and Assignment to Structured arrays +============================================ + +Assigning data to a Structured Array +------------------------------------ + +There are a number of ways to assign values to a structured array: Using python +tuples, using scalar values, or using other structured arrays. + +Assignment from Python Native Types (Tuples) +```````````````````````````````````````````` + +The simplest way to assign values to a structured array is using python tuples. +Each assigned value should be a tuple of length equal to the number of fields +in the array, and not a list or array as these will trigger numpy's +broadcasting rules. The tuple's elements are assigned to the successive fields +of the array, from left to right:: + + >>> x = np.array([(1, 2, 3), (4, 5, 6)], dtype='i8, f4, f8') + >>> x[1] = (7, 8, 9) + >>> x + array([(1, 2., 3.), (7, 8., 9.)], + dtype=[('f0', '<i8'), ('f1', '<f4'), ('f2', '<f8')]) + +Assignment from Scalars +``````````````````````` + +A scalar assigned to a structured element will be assigned to all fields. This +happens when a scalar is assigned to a structured array, or when an +unstructured array is assigned to a structured array:: + + >>> x = np.zeros(2, dtype='i8, f4, ?, S1') + >>> x[:] = 3 + >>> x + array([(3, 3., True, b'3'), (3, 3., True, b'3')], + dtype=[('f0', '<i8'), ('f1', '<f4'), ('f2', '?'), ('f3', 'S1')]) + >>> x[:] = np.arange(2) + >>> x + array([(0, 0., False, b'0'), (1, 1., True, b'1')], + dtype=[('f0', '<i8'), ('f1', '<f4'), ('f2', '?'), ('f3', 'S1')]) + +Structured arrays can also be assigned to unstructured arrays, but only if the +structured datatype has just a single field:: + + >>> twofield = np.zeros(2, dtype=[('A', 'i4'), ('B', 'i4')]) + >>> onefield = np.zeros(2, dtype=[('A', 'i4')]) + >>> nostruct = np.zeros(2, dtype='i4') + >>> nostruct[:] = twofield + Traceback (most recent call last): + ... + TypeError: Cannot cast array data from dtype([('A', '<i4'), ('B', '<i4')]) to dtype('int32') according to the rule 'unsafe' + +Assignment from other Structured Arrays +``````````````````````````````````````` + +Assignment between two structured arrays occurs as if the source elements had +been converted to tuples and then assigned to the destination elements. That +is, the first field of the source array is assigned to the first field of the +destination array, and the second field likewise, and so on, regardless of +field names. Structured arrays with a different number of fields cannot be +assigned to each other. Bytes of the destination structure which are not +included in any of the fields are unaffected. :: + + >>> a = np.zeros(3, dtype=[('a', 'i8'), ('b', 'f4'), ('c', 'S3')]) + >>> b = np.ones(3, dtype=[('x', 'f4'), ('y', 'S3'), ('z', 'O')]) + >>> b[:] = a + >>> b + array([(0., b'0.0', b''), (0., b'0.0', b''), (0., b'0.0', b'')], + dtype=[('x', '<f4'), ('y', 'S3'), ('z', 'O')]) + + +Assignment involving subarrays +`````````````````````````````` + +When assigning to fields which are subarrays, the assigned value will first be +broadcast to the shape of the subarray. + +Indexing Structured Arrays +-------------------------- + +Accessing Individual Fields +``````````````````````````` + +Individual fields of a structured array may be accessed and modified by indexing +the array with the field name. :: + + >>> x = np.array([(1, 2), (3, 4)], dtype=[('foo', 'i8'), ('bar', 'f4')]) + >>> x['foo'] + array([1, 3]) + >>> x['foo'] = 10 + >>> x + array([(10, 2.), (10, 4.)], + dtype=[('foo', '<i8'), ('bar', '<f4')]) + +The resulting array is a view into the original array. It shares the same +memory locations and writing to the view will modify the original array. :: + + >>> y = x['bar'] + >>> y[:] = 11 + >>> x + array([(10, 11.), (10, 11.)], + dtype=[('foo', '<i8'), ('bar', '<f4')]) + +This view has the same dtype and itemsize as the indexed field, so it is +typically a non-structured array, except in the case of nested structures. + + >>> y.dtype, y.shape, y.strides + (dtype('float32'), (2,), (12,)) + +If the accessed field is a subarray, the dimensions of the subarray +are appended to the shape of the result:: + + >>> x = np.zeros((2, 2), dtype=[('a', np.int32), ('b', np.float64, (3, 3))]) + >>> x['a'].shape + (2, 2) + >>> x['b'].shape + (2, 2, 3, 3) + +Accessing Multiple Fields +``````````````````````````` + +One can index and assign to a structured array with a multi-field index, where +the index is a list of field names. + +.. warning:: + The behavior of multi-field indexes changed from Numpy 1.15 to Numpy 1.16. + +The result of indexing with a multi-field index is a view into the original +array, as follows:: + + >>> a = np.zeros(3, dtype=[('a', 'i4'), ('b', 'i4'), ('c', 'f4')]) + >>> a[['a', 'c']] + array([(0, 0.), (0, 0.), (0, 0.)], + dtype={'names':['a','c'], 'formats':['<i4','<f4'], 'offsets':[0,8], 'itemsize':12}) + +Assignment to the view modifies the original array. The view's fields will be +in the order they were indexed. Note that unlike for single-field indexing, the +dtype of the view has the same itemsize as the original array, and has fields +at the same offsets as in the original array, and unindexed fields are merely +missing. + +.. warning:: + In Numpy 1.15, indexing an array with a multi-field index returned a copy of + the result above, but with fields packed together in memory as if + passed through :func:`numpy.lib.recfunctions.repack_fields`. + + The new behavior as of Numpy 1.16 leads to extra "padding" bytes at the + location of unindexed fields compared to 1.15. You will need to update any + code which depends on the data having a "packed" layout. For instance code + such as:: + + >>> a[['a', 'c']].view('i8') # Fails in Numpy 1.16 + Traceback (most recent call last): + File "<stdin>", line 1, in <module> + ValueError: When changing to a smaller dtype, its size must be a divisor of the size of original dtype + + will need to be changed. This code has raised a ``FutureWarning`` since + Numpy 1.12, and similar code has raised ``FutureWarning`` since 1.7. + + In 1.16 a number of functions have been introduced in the + :mod:`numpy.lib.recfunctions` module to help users account for this + change. These are + :func:`numpy.lib.recfunctions.repack_fields`. + :func:`numpy.lib.recfunctions.structured_to_unstructured`, + :func:`numpy.lib.recfunctions.unstructured_to_structured`, + :func:`numpy.lib.recfunctions.apply_along_fields`, + :func:`numpy.lib.recfunctions.assign_fields_by_name`, and + :func:`numpy.lib.recfunctions.require_fields`. + + The function :func:`numpy.lib.recfunctions.repack_fields` can always be + used to reproduce the old behavior, as it will return a packed copy of the + structured array. The code above, for example, can be replaced with: + + >>> from numpy.lib.recfunctions import repack_fields + >>> repack_fields(a[['a', 'c']]).view('i8') # supported in 1.16 + array([0, 0, 0]) + + Furthermore, numpy now provides a new function + :func:`numpy.lib.recfunctions.structured_to_unstructured` which is a safer + and more efficient alternative for users who wish to convert structured + arrays to unstructured arrays, as the view above is often indeded to do. + This function allows safe conversion to an unstructured type taking into + account padding, often avoids a copy, and also casts the datatypes + as needed, unlike the view. Code such as: + + >>> b = np.zeros(3, dtype=[('x', 'f4'), ('y', 'f4'), ('z', 'f4')]) + >>> b[['x', 'z']].view('f4') + array([0., 0., 0., 0., 0., 0., 0., 0., 0.], dtype=float32) + + can be made safer by replacing with: + + >>> from numpy.lib.recfunctions import structured_to_unstructured + >>> structured_to_unstructured(b[['x', 'z']]) + array([0, 0, 0]) + + +Assignment to an array with a multi-field index modifies the original array:: + + >>> a[['a', 'c']] = (2, 3) + >>> a + array([(2, 0, 3.), (2, 0, 3.), (2, 0, 3.)], + dtype=[('a', '<i4'), ('b', '<i4'), ('c', '<f4')]) + +This obeys the structured array assignment rules described above. For example, +this means that one can swap the values of two fields using appropriate +multi-field indexes:: + + >>> a[['a', 'c']] = a[['c', 'a']] + +Indexing with an Integer to get a Structured Scalar +``````````````````````````````````````````````````` + +Indexing a single element of a structured array (with an integer index) returns +a structured scalar:: + + >>> x = np.array([(1, 2., 3.)], dtype='i, f, f') + >>> scalar = x[0] + >>> scalar + (1, 2., 3.) + >>> type(scalar) + <class 'numpy.void'> + +Unlike other numpy scalars, structured scalars are mutable and act like views +into the original array, such that modifying the scalar will modify the +original array. Structured scalars also support access and assignment by field +name:: + + >>> x = np.array([(1, 2), (3, 4)], dtype=[('foo', 'i8'), ('bar', 'f4')]) + >>> s = x[0] + >>> s['bar'] = 100 + >>> x + array([(1, 100.), (3, 4.)], + dtype=[('foo', '<i8'), ('bar', '<f4')]) + +Similarly to tuples, structured scalars can also be indexed with an integer:: + + >>> scalar = np.array([(1, 2., 3.)], dtype='i, f, f')[0] + >>> scalar[0] + 1 + >>> scalar[1] = 4 + +Thus, tuples might be thought of as the native Python equivalent to numpy's +structured types, much like native python integers are the equivalent to +numpy's integer types. Structured scalars may be converted to a tuple by +calling `numpy.ndarray.item`:: + + >>> scalar.item(), type(scalar.item()) + ((1, 4.0, 3.0), <class 'tuple'>) + +Viewing Structured Arrays Containing Objects +-------------------------------------------- + +In order to prevent clobbering object pointers in fields of +:class:`object` type, numpy currently does not allow views of structured +arrays containing objects. + +Structure Comparison +-------------------- + +If the dtypes of two void structured arrays are equal, testing the equality of +the arrays will result in a boolean array with the dimensions of the original +arrays, with elements set to ``True`` where all fields of the corresponding +structures are equal. Structured dtypes are equal if the field names, +dtypes and titles are the same, ignoring endianness, and the fields are in +the same order:: + + >>> a = np.zeros(2, dtype=[('a', 'i4'), ('b', 'i4')]) + >>> b = np.ones(2, dtype=[('a', 'i4'), ('b', 'i4')]) + >>> a == b + array([False, False]) + +Currently, if the dtypes of two void structured arrays are not equivalent the +comparison fails, returning the scalar value ``False``. This behavior is +deprecated as of numpy 1.10 and will raise an error or perform elementwise +comparison in the future. + +The ``<`` and ``>`` operators always return ``False`` when comparing void +structured arrays, and arithmetic and bitwise operations are not supported. + +Record Arrays +============= + +As an optional convenience numpy provides an ndarray subclass, +:class:`numpy.recarray` that allows access to fields of structured arrays by +attribute instead of only by index. +Record arrays use a special datatype, :class:`numpy.record`, that allows +field access by attribute on the structured scalars obtained from the array. +The :mod:`numpy.rec` module provides functions for creating recarrays from +various objects. +Additional helper functions for creating and manipulating structured arrays +can be found in :mod:`numpy.lib.recfunctions`. + +The simplest way to create a record array is with ``numpy.rec.array``:: + + >>> recordarr = np.rec.array([(1, 2., 'Hello'), (2, 3., "World")], + ... dtype=[('foo', 'i4'),('bar', 'f4'), ('baz', 'S10')]) + >>> recordarr.bar + array([ 2., 3.], dtype=float32) + >>> recordarr[1:2] + rec.array([(2, 3., b'World')], + dtype=[('foo', '<i4'), ('bar', '<f4'), ('baz', 'S10')]) + >>> recordarr[1:2].foo + array([2], dtype=int32) + >>> recordarr.foo[1:2] + array([2], dtype=int32) + >>> recordarr[1].baz + b'World' + +:func:`numpy.rec.array` can convert a wide variety of arguments into record +arrays, including structured arrays:: + + >>> arr = np.array([(1, 2., 'Hello'), (2, 3., "World")], + ... dtype=[('foo', 'i4'), ('bar', 'f4'), ('baz', 'S10')]) + >>> recordarr = np.rec.array(arr) + +The :mod:`numpy.rec` module provides a number of other convenience functions for +creating record arrays, see :ref:`record array creation routines +<routines.array-creation.rec>`. + +A record array representation of a structured array can be obtained using the +appropriate `view <numpy-ndarray-view>`_:: + + >>> arr = np.array([(1, 2., 'Hello'), (2, 3., "World")], + ... dtype=[('foo', 'i4'),('bar', 'f4'), ('baz', 'a10')]) + >>> recordarr = arr.view(dtype=np.dtype((np.record, arr.dtype)), + ... type=np.recarray) + +For convenience, viewing an ndarray as type :class:`numpy.recarray` will +automatically convert to :class:`numpy.record` datatype, so the dtype can be left +out of the view:: + + >>> recordarr = arr.view(np.recarray) + >>> recordarr.dtype + dtype((numpy.record, [('foo', '<i4'), ('bar', '<f4'), ('baz', 'S10')])) + +To get back to a plain ndarray both the dtype and type must be reset. The +following view does so, taking into account the unusual case that the +recordarr was not a structured type:: + + >>> arr2 = recordarr.view(recordarr.dtype.fields or recordarr.dtype, np.ndarray) + +Record array fields accessed by index or by attribute are returned as a record +array if the field has a structured type but as a plain ndarray otherwise. :: + + >>> recordarr = np.rec.array([('Hello', (1, 2)), ("World", (3, 4))], + ... dtype=[('foo', 'S6'),('bar', [('A', int), ('B', int)])]) + >>> type(recordarr.foo) + <class 'numpy.ndarray'> + >>> type(recordarr.bar) + <class 'numpy.recarray'> + +Note that if a field has the same name as an ndarray attribute, the ndarray +attribute takes precedence. Such fields will be inaccessible by attribute but +will still be accessible by index. + Recarray Helper Functions -************************* +------------------------- .. automodule:: numpy.lib.recfunctions :members: diff --git a/doc/source/user/basics.rst b/doc/source/user/basics.rst index 7875aff6e680..affb85db2f0c 100644 --- a/doc/source/user/basics.rst +++ b/doc/source/user/basics.rst @@ -1,15 +1,22 @@ -************ -NumPy basics -************ +****************** +NumPy fundamentals +****************** + +These documents clarify concepts, design decisions, and technical +constraints in NumPy. This is a great place to understand the +fundamental NumPy ideas and philosophy. .. toctree:: :maxdepth: 1 - basics.types basics.creation - basics.io basics.indexing + basics.io + basics.types basics.broadcasting basics.byteswapping basics.rec + basics.dispatch basics.subclassing + basics.ufuncs + basics.copies diff --git a/doc/source/user/basics.subclassing.rst b/doc/source/user/basics.subclassing.rst index 43315521cfdf..1b78809865aa 100644 --- a/doc/source/user/basics.subclassing.rst +++ b/doc/source/user/basics.subclassing.rst @@ -4,4 +4,749 @@ Subclassing ndarray ******************* -.. automodule:: numpy.doc.subclassing +Introduction +------------ + +Subclassing ndarray is relatively simple, but it has some complications +compared to other Python objects. On this page we explain the machinery +that allows you to subclass ndarray, and the implications for +implementing a subclass. + +ndarrays and object creation +============================ + +Subclassing ndarray is complicated by the fact that new instances of +ndarray classes can come about in three different ways. These are: + +#. Explicit constructor call - as in ``MySubClass(params)``. This is + the usual route to Python instance creation. +#. View casting - casting an existing ndarray as a given subclass +#. New from template - creating a new instance from a template + instance. Examples include returning slices from a subclassed array, + creating return types from ufuncs, and copying arrays. See + :ref:`new-from-template` for more details + +The last two are characteristics of ndarrays - in order to support +things like array slicing. The complications of subclassing ndarray are +due to the mechanisms numpy has to support these latter two routes of +instance creation. + +.. _view-casting: + +View casting +------------ + +*View casting* is the standard ndarray mechanism by which you take an +ndarray of any subclass, and return a view of the array as another +(specified) subclass: + +>>> import numpy as np +>>> # create a completely useless ndarray subclass +>>> class C(np.ndarray): pass +>>> # create a standard ndarray +>>> arr = np.zeros((3,)) +>>> # take a view of it, as our useless subclass +>>> c_arr = arr.view(C) +>>> type(c_arr) +<class 'C'> + +.. _new-from-template: + +Creating new from template +-------------------------- + +New instances of an ndarray subclass can also come about by a very +similar mechanism to :ref:`view-casting`, when numpy finds it needs to +create a new instance from a template instance. The most obvious place +this has to happen is when you are taking slices of subclassed arrays. +For example: + +>>> v = c_arr[1:] +>>> type(v) # the view is of type 'C' +<class 'C'> +>>> v is c_arr # but it's a new instance +False + +The slice is a *view* onto the original ``c_arr`` data. So, when we +take a view from the ndarray, we return a new ndarray, of the same +class, that points to the data in the original. + +There are other points in the use of ndarrays where we need such views, +such as copying arrays (``c_arr.copy()``), creating ufunc output arrays +(see also :ref:`array-wrap`), and reducing methods (like +``c_arr.mean()``). + +Relationship of view casting and new-from-template +-------------------------------------------------- + +These paths both use the same machinery. We make the distinction here, +because they result in different input to your methods. Specifically, +:ref:`view-casting` means you have created a new instance of your array +type from any potential subclass of ndarray. :ref:`new-from-template` +means you have created a new instance of your class from a pre-existing +instance, allowing you - for example - to copy across attributes that +are particular to your subclass. + +Implications for subclassing +---------------------------- + +If we subclass ndarray, we need to deal not only with explicit +construction of our array type, but also :ref:`view-casting` or +:ref:`new-from-template`. NumPy has the machinery to do this, and it is +this machinery that makes subclassing slightly non-standard. + +There are two aspects to the machinery that ndarray uses to support +views and new-from-template in subclasses. + +The first is the use of the ``ndarray.__new__`` method for the main work +of object initialization, rather then the more usual ``__init__`` +method. The second is the use of the ``__array_finalize__`` method to +allow subclasses to clean up after the creation of views and new +instances from templates. + +A brief Python primer on ``__new__`` and ``__init__`` +===================================================== + +``__new__`` is a standard Python method, and, if present, is called +before ``__init__`` when we create a class instance. See the `python +__new__ documentation +<https://docs.python.org/reference/datamodel.html#object.__new__>`_ for more detail. + +For example, consider the following Python code: + +.. testcode:: + + class C: + def __new__(cls, *args): + print('Cls in __new__:', cls) + print('Args in __new__:', args) + # The `object` type __new__ method takes a single argument. + return object.__new__(cls) + + def __init__(self, *args): + print('type(self) in __init__:', type(self)) + print('Args in __init__:', args) + +meaning that we get: + +>>> c = C('hello') +Cls in __new__: <class 'C'> +Args in __new__: ('hello',) +type(self) in __init__: <class 'C'> +Args in __init__: ('hello',) + +When we call ``C('hello')``, the ``__new__`` method gets its own class +as first argument, and the passed argument, which is the string +``'hello'``. After python calls ``__new__``, it usually (see below) +calls our ``__init__`` method, with the output of ``__new__`` as the +first argument (now a class instance), and the passed arguments +following. + +As you can see, the object can be initialized in the ``__new__`` +method or the ``__init__`` method, or both, and in fact ndarray does +not have an ``__init__`` method, because all the initialization is +done in the ``__new__`` method. + +Why use ``__new__`` rather than just the usual ``__init__``? Because +in some cases, as for ndarray, we want to be able to return an object +of some other class. Consider the following: + +.. testcode:: + + class D(C): + def __new__(cls, *args): + print('D cls is:', cls) + print('D args in __new__:', args) + return C.__new__(C, *args) + + def __init__(self, *args): + # we never get here + print('In D __init__') + +meaning that: + +>>> obj = D('hello') +D cls is: <class 'D'> +D args in __new__: ('hello',) +Cls in __new__: <class 'C'> +Args in __new__: ('hello',) +>>> type(obj) +<class 'C'> + +The definition of ``C`` is the same as before, but for ``D``, the +``__new__`` method returns an instance of class ``C`` rather than +``D``. Note that the ``__init__`` method of ``D`` does not get +called. In general, when the ``__new__`` method returns an object of +class other than the class in which it is defined, the ``__init__`` +method of that class is not called. + +This is how subclasses of the ndarray class are able to return views +that preserve the class type. When taking a view, the standard +ndarray machinery creates the new ndarray object with something +like:: + + obj = ndarray.__new__(subtype, shape, ... + +where ``subdtype`` is the subclass. Thus the returned view is of the +same class as the subclass, rather than being of class ``ndarray``. + +That solves the problem of returning views of the same type, but now +we have a new problem. The machinery of ndarray can set the class +this way, in its standard methods for taking views, but the ndarray +``__new__`` method knows nothing of what we have done in our own +``__new__`` method in order to set attributes, and so on. (Aside - +why not call ``obj = subdtype.__new__(...`` then? Because we may not +have a ``__new__`` method with the same call signature). + +The role of ``__array_finalize__`` +================================== + +``__array_finalize__`` is the mechanism that numpy provides to allow +subclasses to handle the various ways that new instances get created. + +Remember that subclass instances can come about in these three ways: + +#. explicit constructor call (``obj = MySubClass(params)``). This will + call the usual sequence of ``MySubClass.__new__`` then (if it exists) + ``MySubClass.__init__``. +#. :ref:`view-casting` +#. :ref:`new-from-template` + +Our ``MySubClass.__new__`` method only gets called in the case of the +explicit constructor call, so we can't rely on ``MySubClass.__new__`` or +``MySubClass.__init__`` to deal with the view casting and +new-from-template. It turns out that ``MySubClass.__array_finalize__`` +*does* get called for all three methods of object creation, so this is +where our object creation housekeeping usually goes. + +* For the explicit constructor call, our subclass will need to create a + new ndarray instance of its own class. In practice this means that + we, the authors of the code, will need to make a call to + ``ndarray.__new__(MySubClass,...)``, a class-hierarchy prepared call to + ``super().__new__(cls, ...)``, or do view casting of an existing array + (see below) +* For view casting and new-from-template, the equivalent of + ``ndarray.__new__(MySubClass,...`` is called, at the C level. + +The arguments that ``__array_finalize__`` receives differ for the three +methods of instance creation above. + +The following code allows us to look at the call sequences and arguments: + +.. testcode:: + + import numpy as np + + class C(np.ndarray): + def __new__(cls, *args, **kwargs): + print('In __new__ with class %s' % cls) + return super().__new__(cls, *args, **kwargs) + + def __init__(self, *args, **kwargs): + # in practice you probably will not need or want an __init__ + # method for your subclass + print('In __init__ with class %s' % self.__class__) + + def __array_finalize__(self, obj): + print('In array_finalize:') + print(' self type is %s' % type(self)) + print(' obj type is %s' % type(obj)) + + +Now: + +>>> # Explicit constructor +>>> c = C((10,)) +In __new__ with class <class 'C'> +In array_finalize: + self type is <class 'C'> + obj type is <type 'NoneType'> +In __init__ with class <class 'C'> +>>> # View casting +>>> a = np.arange(10) +>>> cast_a = a.view(C) +In array_finalize: + self type is <class 'C'> + obj type is <type 'numpy.ndarray'> +>>> # Slicing (example of new-from-template) +>>> cv = c[:1] +In array_finalize: + self type is <class 'C'> + obj type is <class 'C'> + +The signature of ``__array_finalize__`` is:: + + def __array_finalize__(self, obj): + +One sees that the ``super`` call, which goes to +``ndarray.__new__``, passes ``__array_finalize__`` the new object, of our +own class (``self``) as well as the object from which the view has been +taken (``obj``). As you can see from the output above, the ``self`` is +always a newly created instance of our subclass, and the type of ``obj`` +differs for the three instance creation methods: + +* When called from the explicit constructor, ``obj`` is ``None`` +* When called from view casting, ``obj`` can be an instance of any + subclass of ndarray, including our own. +* When called in new-from-template, ``obj`` is another instance of our + own subclass, that we might use to update the new ``self`` instance. + +Because ``__array_finalize__`` is the only method that always sees new +instances being created, it is the sensible place to fill in instance +defaults for new object attributes, among other tasks. + +This may be clearer with an example. + +Simple example - adding an extra attribute to ndarray +----------------------------------------------------- + +.. testcode:: + + import numpy as np + + class InfoArray(np.ndarray): + + def __new__(subtype, shape, dtype=float, buffer=None, offset=0, + strides=None, order=None, info=None): + # Create the ndarray instance of our type, given the usual + # ndarray input arguments. This will call the standard + # ndarray constructor, but return an object of our type. + # It also triggers a call to InfoArray.__array_finalize__ + obj = super().__new__(subtype, shape, dtype, + buffer, offset, strides, order) + # set the new 'info' attribute to the value passed + obj.info = info + # Finally, we must return the newly created object: + return obj + + def __array_finalize__(self, obj): + # ``self`` is a new object resulting from + # ndarray.__new__(InfoArray, ...), therefore it only has + # attributes that the ndarray.__new__ constructor gave it - + # i.e. those of a standard ndarray. + # + # We could have got to the ndarray.__new__ call in 3 ways: + # From an explicit constructor - e.g. InfoArray(): + # obj is None + # (we're in the middle of the InfoArray.__new__ + # constructor, and self.info will be set when we return to + # InfoArray.__new__) + if obj is None: return + # From view casting - e.g arr.view(InfoArray): + # obj is arr + # (type(obj) can be InfoArray) + # From new-from-template - e.g infoarr[:3] + # type(obj) is InfoArray + # + # Note that it is here, rather than in the __new__ method, + # that we set the default value for 'info', because this + # method sees all creation of default objects - with the + # InfoArray.__new__ constructor, but also with + # arr.view(InfoArray). + self.info = getattr(obj, 'info', None) + # We do not need to return anything + + +Using the object looks like this: + + >>> obj = InfoArray(shape=(3,)) # explicit constructor + >>> type(obj) + <class 'InfoArray'> + >>> obj.info is None + True + >>> obj = InfoArray(shape=(3,), info='information') + >>> obj.info + 'information' + >>> v = obj[1:] # new-from-template - here - slicing + >>> type(v) + <class 'InfoArray'> + >>> v.info + 'information' + >>> arr = np.arange(10) + >>> cast_arr = arr.view(InfoArray) # view casting + >>> type(cast_arr) + <class 'InfoArray'> + >>> cast_arr.info is None + True + +This class isn't very useful, because it has the same constructor as the +bare ndarray object, including passing in buffers and shapes and so on. +We would probably prefer the constructor to be able to take an already +formed ndarray from the usual numpy calls to ``np.array`` and return an +object. + +Slightly more realistic example - attribute added to existing array +------------------------------------------------------------------- + +Here is a class that takes a standard ndarray that already exists, casts +as our type, and adds an extra attribute. + +.. testcode:: + + import numpy as np + + class RealisticInfoArray(np.ndarray): + + def __new__(cls, input_array, info=None): + # Input array is an already formed ndarray instance + # We first cast to be our class type + obj = np.asarray(input_array).view(cls) + # add the new attribute to the created instance + obj.info = info + # Finally, we must return the newly created object: + return obj + + def __array_finalize__(self, obj): + # see InfoArray.__array_finalize__ for comments + if obj is None: return + self.info = getattr(obj, 'info', None) + + +So: + + >>> arr = np.arange(5) + >>> obj = RealisticInfoArray(arr, info='information') + >>> type(obj) + <class 'RealisticInfoArray'> + >>> obj.info + 'information' + >>> v = obj[1:] + >>> type(v) + <class 'RealisticInfoArray'> + >>> v.info + 'information' + +.. _array-ufunc: + +``__array_ufunc__`` for ufuncs +------------------------------ + + .. versionadded:: 1.13 + +A subclass can override what happens when executing numpy ufuncs on it by +overriding the default ``ndarray.__array_ufunc__`` method. This method is +executed *instead* of the ufunc and should return either the result of the +operation, or :obj:`NotImplemented` if the operation requested is not +implemented. + +The signature of ``__array_ufunc__`` is:: + + def __array_ufunc__(ufunc, method, *inputs, **kwargs): + + - *ufunc* is the ufunc object that was called. + - *method* is a string indicating how the Ufunc was called, either + ``"__call__"`` to indicate it was called directly, or one of its + :ref:`methods<ufuncs.methods>`: ``"reduce"``, ``"accumulate"``, + ``"reduceat"``, ``"outer"``, or ``"at"``. + - *inputs* is a tuple of the input arguments to the ``ufunc`` + - *kwargs* contains any optional or keyword arguments passed to the + function. This includes any ``out`` arguments, which are always + contained in a tuple. + +A typical implementation would convert any inputs or outputs that are +instances of one's own class, pass everything on to a superclass using +``super()``, and finally return the results after possible +back-conversion. An example, taken from the test case +``test_ufunc_override_with_super`` in ``core/tests/test_umath.py``, is the +following. + +.. testcode:: + + input numpy as np + + class A(np.ndarray): + def __array_ufunc__(self, ufunc, method, *inputs, out=None, **kwargs): + args = [] + in_no = [] + for i, input_ in enumerate(inputs): + if isinstance(input_, A): + in_no.append(i) + args.append(input_.view(np.ndarray)) + else: + args.append(input_) + + outputs = out + out_no = [] + if outputs: + out_args = [] + for j, output in enumerate(outputs): + if isinstance(output, A): + out_no.append(j) + out_args.append(output.view(np.ndarray)) + else: + out_args.append(output) + kwargs['out'] = tuple(out_args) + else: + outputs = (None,) * ufunc.nout + + info = {} + if in_no: + info['inputs'] = in_no + if out_no: + info['outputs'] = out_no + + results = super().__array_ufunc__(ufunc, method, *args, **kwargs) + if results is NotImplemented: + return NotImplemented + + if method == 'at': + if isinstance(inputs[0], A): + inputs[0].info = info + return + + if ufunc.nout == 1: + results = (results,) + + results = tuple((np.asarray(result).view(A) + if output is None else output) + for result, output in zip(results, outputs)) + if results and isinstance(results[0], A): + results[0].info = info + + return results[0] if len(results) == 1 else results + +So, this class does not actually do anything interesting: it just +converts any instances of its own to regular ndarray (otherwise, we'd +get infinite recursion!), and adds an ``info`` dictionary that tells +which inputs and outputs it converted. Hence, e.g., + +>>> a = np.arange(5.).view(A) +>>> b = np.sin(a) +>>> b.info +{'inputs': [0]} +>>> b = np.sin(np.arange(5.), out=(a,)) +>>> b.info +{'outputs': [0]} +>>> a = np.arange(5.).view(A) +>>> b = np.ones(1).view(A) +>>> c = a + b +>>> c.info +{'inputs': [0, 1]} +>>> a += b +>>> a.info +{'inputs': [0, 1], 'outputs': [0]} + +Note that another approach would be to to use ``getattr(ufunc, +methods)(*inputs, **kwargs)`` instead of the ``super`` call. For this example, +the result would be identical, but there is a difference if another operand +also defines ``__array_ufunc__``. E.g., lets assume that we evalulate +``np.add(a, b)``, where ``b`` is an instance of another class ``B`` that has +an override. If you use ``super`` as in the example, +``ndarray.__array_ufunc__`` will notice that ``b`` has an override, which +means it cannot evaluate the result itself. Thus, it will return +`NotImplemented` and so will our class ``A``. Then, control will be passed +over to ``b``, which either knows how to deal with us and produces a result, +or does not and returns `NotImplemented`, raising a ``TypeError``. + +If instead, we replace our ``super`` call with ``getattr(ufunc, method)``, we +effectively do ``np.add(a.view(np.ndarray), b)``. Again, ``B.__array_ufunc__`` +will be called, but now it sees an ``ndarray`` as the other argument. Likely, +it will know how to handle this, and return a new instance of the ``B`` class +to us. Our example class is not set up to handle this, but it might well be +the best approach if, e.g., one were to re-implement ``MaskedArray`` using +``__array_ufunc__``. + +As a final note: if the ``super`` route is suited to a given class, an +advantage of using it is that it helps in constructing class hierarchies. +E.g., suppose that our other class ``B`` also used the ``super`` in its +``__array_ufunc__`` implementation, and we created a class ``C`` that depended +on both, i.e., ``class C(A, B)`` (with, for simplicity, not another +``__array_ufunc__`` override). Then any ufunc on an instance of ``C`` would +pass on to ``A.__array_ufunc__``, the ``super`` call in ``A`` would go to +``B.__array_ufunc__``, and the ``super`` call in ``B`` would go to +``ndarray.__array_ufunc__``, thus allowing ``A`` and ``B`` to collaborate. + +.. _array-wrap: + +``__array_wrap__`` for ufuncs and other functions +------------------------------------------------- + +Prior to numpy 1.13, the behaviour of ufuncs could only be tuned using +``__array_wrap__`` and ``__array_prepare__``. These two allowed one to +change the output type of a ufunc, but, in contrast to +``__array_ufunc__``, did not allow one to make any changes to the inputs. +It is hoped to eventually deprecate these, but ``__array_wrap__`` is also +used by other numpy functions and methods, such as ``squeeze``, so at the +present time is still needed for full functionality. + +Conceptually, ``__array_wrap__`` "wraps up the action" in the sense of +allowing a subclass to set the type of the return value and update +attributes and metadata. Let's show how this works with an example. First +we return to the simpler example subclass, but with a different name and +some print statements: + +.. testcode:: + + import numpy as np + + class MySubClass(np.ndarray): + + def __new__(cls, input_array, info=None): + obj = np.asarray(input_array).view(cls) + obj.info = info + return obj + + def __array_finalize__(self, obj): + print('In __array_finalize__:') + print(' self is %s' % repr(self)) + print(' obj is %s' % repr(obj)) + if obj is None: return + self.info = getattr(obj, 'info', None) + + def __array_wrap__(self, out_arr, context=None): + print('In __array_wrap__:') + print(' self is %s' % repr(self)) + print(' arr is %s' % repr(out_arr)) + # then just call the parent + return super().__array_wrap__(self, out_arr, context) + +We run a ufunc on an instance of our new array: + +>>> obj = MySubClass(np.arange(5), info='spam') +In __array_finalize__: + self is MySubClass([0, 1, 2, 3, 4]) + obj is array([0, 1, 2, 3, 4]) +>>> arr2 = np.arange(5)+1 +>>> ret = np.add(arr2, obj) +In __array_wrap__: + self is MySubClass([0, 1, 2, 3, 4]) + arr is array([1, 3, 5, 7, 9]) +In __array_finalize__: + self is MySubClass([1, 3, 5, 7, 9]) + obj is MySubClass([0, 1, 2, 3, 4]) +>>> ret +MySubClass([1, 3, 5, 7, 9]) +>>> ret.info +'spam' + +Note that the ufunc (``np.add``) has called the ``__array_wrap__`` method +with arguments ``self`` as ``obj``, and ``out_arr`` as the (ndarray) result +of the addition. In turn, the default ``__array_wrap__`` +(``ndarray.__array_wrap__``) has cast the result to class ``MySubClass``, +and called ``__array_finalize__`` - hence the copying of the ``info`` +attribute. This has all happened at the C level. + +But, we could do anything we wanted: + +.. testcode:: + + class SillySubClass(np.ndarray): + + def __array_wrap__(self, arr, context=None): + return 'I lost your data' + +>>> arr1 = np.arange(5) +>>> obj = arr1.view(SillySubClass) +>>> arr2 = np.arange(5) +>>> ret = np.multiply(obj, arr2) +>>> ret +'I lost your data' + +So, by defining a specific ``__array_wrap__`` method for our subclass, +we can tweak the output from ufuncs. The ``__array_wrap__`` method +requires ``self``, then an argument - which is the result of the ufunc - +and an optional parameter *context*. This parameter is returned by +ufuncs as a 3-element tuple: (name of the ufunc, arguments of the ufunc, +domain of the ufunc), but is not set by other numpy functions. Though, +as seen above, it is possible to do otherwise, ``__array_wrap__`` should +return an instance of its containing class. See the masked array +subclass for an implementation. + +In addition to ``__array_wrap__``, which is called on the way out of the +ufunc, there is also an ``__array_prepare__`` method which is called on +the way into the ufunc, after the output arrays are created but before any +computation has been performed. The default implementation does nothing +but pass through the array. ``__array_prepare__`` should not attempt to +access the array data or resize the array, it is intended for setting the +output array type, updating attributes and metadata, and performing any +checks based on the input that may be desired before computation begins. +Like ``__array_wrap__``, ``__array_prepare__`` must return an ndarray or +subclass thereof or raise an error. + +Extra gotchas - custom ``__del__`` methods and ndarray.base +----------------------------------------------------------- + +One of the problems that ndarray solves is keeping track of memory +ownership of ndarrays and their views. Consider the case where we have +created an ndarray, ``arr`` and have taken a slice with ``v = arr[1:]``. +The two objects are looking at the same memory. NumPy keeps track of +where the data came from for a particular array or view, with the +``base`` attribute: + +>>> # A normal ndarray, that owns its own data +>>> arr = np.zeros((4,)) +>>> # In this case, base is None +>>> arr.base is None +True +>>> # We take a view +>>> v1 = arr[1:] +>>> # base now points to the array that it derived from +>>> v1.base is arr +True +>>> # Take a view of a view +>>> v2 = v1[1:] +>>> # base points to the original array that it was derived from +>>> v2.base is arr +True + +In general, if the array owns its own memory, as for ``arr`` in this +case, then ``arr.base`` will be None - there are some exceptions to this +- see the numpy book for more details. + +The ``base`` attribute is useful in being able to tell whether we have +a view or the original array. This in turn can be useful if we need +to know whether or not to do some specific cleanup when the subclassed +array is deleted. For example, we may only want to do the cleanup if +the original array is deleted, but not the views. For an example of +how this can work, have a look at the ``memmap`` class in +``numpy.core``. + +Subclassing and Downstream Compatibility +---------------------------------------- + +When sub-classing ``ndarray`` or creating duck-types that mimic the ``ndarray`` +interface, it is your responsibility to decide how aligned your APIs will be +with those of numpy. For convenience, many numpy functions that have a corresponding +``ndarray`` method (e.g., ``sum``, ``mean``, ``take``, ``reshape``) work by checking +if the first argument to a function has a method of the same name. If it exists, the +method is called instead of coercing the arguments to a numpy array. + +For example, if you want your sub-class or duck-type to be compatible with +numpy's ``sum`` function, the method signature for this object's ``sum`` method +should be the following: + +.. testcode:: + + def sum(self, axis=None, dtype=None, out=None, keepdims=False): + ... + +This is the exact same method signature for ``np.sum``, so now if a user calls +``np.sum`` on this object, numpy will call the object's own ``sum`` method and +pass in these arguments enumerated above in the signature, and no errors will +be raised because the signatures are completely compatible with each other. + +If, however, you decide to deviate from this signature and do something like this: + +.. testcode:: + + def sum(self, axis=None, dtype=None): + ... + +This object is no longer compatible with ``np.sum`` because if you call ``np.sum``, +it will pass in unexpected arguments ``out`` and ``keepdims``, causing a TypeError +to be raised. + +If you wish to maintain compatibility with numpy and its subsequent versions (which +might add new keyword arguments) but do not want to surface all of numpy's arguments, +your function's signature should accept ``**kwargs``. For example: + +.. testcode:: + + def sum(self, axis=None, dtype=None, **unused_kwargs): + ... + +This object is now compatible with ``np.sum`` again because any extraneous arguments +(i.e. keywords that are not ``axis`` or ``dtype``) will be hidden away in the +``**unused_kwargs`` parameter. + + diff --git a/doc/source/user/basics.types.rst b/doc/source/user/basics.types.rst index 5ce5af15a6ad..354f003fbb28 100644 --- a/doc/source/user/basics.types.rst +++ b/doc/source/user/basics.types.rst @@ -1,7 +1,284 @@ +.. _basics.types: + ********** Data types ********** .. seealso:: :ref:`Data type objects <arrays.dtypes>` -.. automodule:: numpy.doc.basics +Array types and conversions between types +========================================= + +NumPy supports a much greater variety of numerical types than Python does. +This section shows which are available, and how to modify an array's data-type. + +The primitive types supported are tied closely to those in C: + +.. list-table:: + :header-rows: 1 + + * - Numpy type + - C type + - Description + + * - `numpy.bool_` + - ``bool`` + - Boolean (True or False) stored as a byte + + * - `numpy.byte` + - ``signed char`` + - Platform-defined + + * - `numpy.ubyte` + - ``unsigned char`` + - Platform-defined + + * - `numpy.short` + - ``short`` + - Platform-defined + + * - `numpy.ushort` + - ``unsigned short`` + - Platform-defined + + * - `numpy.intc` + - ``int`` + - Platform-defined + + * - `numpy.uintc` + - ``unsigned int`` + - Platform-defined + + * - `numpy.int_` + - ``long`` + - Platform-defined + + * - `numpy.uint` + - ``unsigned long`` + - Platform-defined + + * - `numpy.longlong` + - ``long long`` + - Platform-defined + + * - `numpy.ulonglong` + - ``unsigned long long`` + - Platform-defined + + * - `numpy.half` / `numpy.float16` + - + - Half precision float: + sign bit, 5 bits exponent, 10 bits mantissa + + * - `numpy.single` + - ``float`` + - Platform-defined single precision float: + typically sign bit, 8 bits exponent, 23 bits mantissa + + * - `numpy.double` + - ``double`` + - Platform-defined double precision float: + typically sign bit, 11 bits exponent, 52 bits mantissa. + + * - `numpy.longdouble` + - ``long double`` + - Platform-defined extended-precision float + + * - `numpy.csingle` + - ``float complex`` + - Complex number, represented by two single-precision floats (real and imaginary components) + + * - `numpy.cdouble` + - ``double complex`` + - Complex number, represented by two double-precision floats (real and imaginary components). + + * - `numpy.clongdouble` + - ``long double complex`` + - Complex number, represented by two extended-precision floats (real and imaginary components). + + +Since many of these have platform-dependent definitions, a set of fixed-size +aliases are provided (See :ref:`sized-aliases`). + + + +NumPy numerical types are instances of ``dtype`` (data-type) objects, each +having unique characteristics. Once you have imported NumPy using + + :: + + >>> import numpy as np + +the dtypes are available as ``np.bool_``, ``np.float32``, etc. + +Advanced types, not listed above, are explored in +section :ref:`structured_arrays`. + +There are 5 basic numerical types representing booleans (bool), integers (int), +unsigned integers (uint) floating point (float) and complex. Those with numbers +in their name indicate the bitsize of the type (i.e. how many bits are needed +to represent a single value in memory). Some types, such as ``int`` and +``intp``, have differing bitsizes, dependent on the platforms (e.g. 32-bit +vs. 64-bit machines). This should be taken into account when interfacing +with low-level code (such as C or Fortran) where the raw memory is addressed. + +Data-types can be used as functions to convert python numbers to array scalars +(see the array scalar section for an explanation), python sequences of numbers +to arrays of that type, or as arguments to the dtype keyword that many numpy +functions or methods accept. Some examples:: + + >>> import numpy as np + >>> x = np.float32(1.0) + >>> x + 1.0 + >>> y = np.int_([1,2,4]) + >>> y + array([1, 2, 4]) + >>> z = np.arange(3, dtype=np.uint8) + >>> z + array([0, 1, 2], dtype=uint8) + +Array types can also be referred to by character codes, mostly to retain +backward compatibility with older packages such as Numeric. Some +documentation may still refer to these, for example:: + + >>> np.array([1, 2, 3], dtype='f') + array([ 1., 2., 3.], dtype=float32) + +We recommend using dtype objects instead. + +To convert the type of an array, use the .astype() method (preferred) or +the type itself as a function. For example: :: + + >>> z.astype(float) #doctest: +NORMALIZE_WHITESPACE + array([ 0., 1., 2.]) + >>> np.int8(z) + array([0, 1, 2], dtype=int8) + +Note that, above, we use the *Python* float object as a dtype. NumPy knows +that ``int`` refers to ``np.int_``, ``bool`` means ``np.bool_``, +that ``float`` is ``np.float_`` and ``complex`` is ``np.complex_``. +The other data-types do not have Python equivalents. + +To determine the type of an array, look at the dtype attribute:: + + >>> z.dtype + dtype('uint8') + +dtype objects also contain information about the type, such as its bit-width +and its byte-order. The data type can also be used indirectly to query +properties of the type, such as whether it is an integer:: + + >>> d = np.dtype(int) + >>> d + dtype('int32') + + >>> np.issubdtype(d, np.integer) + True + + >>> np.issubdtype(d, np.floating) + False + + +Array Scalars +============= + +NumPy generally returns elements of arrays as array scalars (a scalar +with an associated dtype). Array scalars differ from Python scalars, but +for the most part they can be used interchangeably (the primary +exception is for versions of Python older than v2.x, where integer array +scalars cannot act as indices for lists and tuples). There are some +exceptions, such as when code requires very specific attributes of a scalar +or when it checks specifically whether a value is a Python scalar. Generally, +problems are easily fixed by explicitly converting array scalars +to Python scalars, using the corresponding Python type function +(e.g., ``int``, ``float``, ``complex``, ``str``, ``unicode``). + +The primary advantage of using array scalars is that +they preserve the array type (Python may not have a matching scalar type +available, e.g. ``int16``). Therefore, the use of array scalars ensures +identical behaviour between arrays and scalars, irrespective of whether the +value is inside an array or not. NumPy scalars also have many of the same +methods arrays do. + +.. _overflow-errors: + +Overflow Errors +=============== + +The fixed size of NumPy numeric types may cause overflow errors when a value +requires more memory than available in the data type. For example, +`numpy.power` evaluates ``100 ** 8`` correctly for 64-bit integers, +but gives 1874919424 (incorrect) for a 32-bit integer. + + >>> np.power(100, 8, dtype=np.int64) + 10000000000000000 + >>> np.power(100, 8, dtype=np.int32) + 1874919424 + +The behaviour of NumPy and Python integer types differs significantly for +integer overflows and may confuse users expecting NumPy integers to behave +similar to Python's ``int``. Unlike NumPy, the size of Python's ``int`` is +flexible. This means Python integers may expand to accommodate any integer and +will not overflow. + +NumPy provides `numpy.iinfo` and `numpy.finfo` to verify the +minimum or maximum values of NumPy integer and floating point values +respectively :: + + >>> np.iinfo(int) # Bounds of the default integer on this system. + iinfo(min=-9223372036854775808, max=9223372036854775807, dtype=int64) + >>> np.iinfo(np.int32) # Bounds of a 32-bit integer + iinfo(min=-2147483648, max=2147483647, dtype=int32) + >>> np.iinfo(np.int64) # Bounds of a 64-bit integer + iinfo(min=-9223372036854775808, max=9223372036854775807, dtype=int64) + +If 64-bit integers are still too small the result may be cast to a +floating point number. Floating point numbers offer a larger, but inexact, +range of possible values. + + >>> np.power(100, 100, dtype=np.int64) # Incorrect even with 64-bit int + 0 + >>> np.power(100, 100, dtype=np.float64) + 1e+200 + +Extended Precision +================== + +Python's floating-point numbers are usually 64-bit floating-point numbers, +nearly equivalent to ``np.float64``. In some unusual situations it may be +useful to use floating-point numbers with more precision. Whether this +is possible in numpy depends on the hardware and on the development +environment: specifically, x86 machines provide hardware floating-point +with 80-bit precision, and while most C compilers provide this as their +``long double`` type, MSVC (standard for Windows builds) makes +``long double`` identical to ``double`` (64 bits). NumPy makes the +compiler's ``long double`` available as ``np.longdouble`` (and +``np.clongdouble`` for the complex numbers). You can find out what your +numpy provides with ``np.finfo(np.longdouble)``. + +NumPy does not provide a dtype with more precision than C's +``long double``\\; in particular, the 128-bit IEEE quad precision +data type (FORTRAN's ``REAL*16``\\) is not available. + +For efficient memory alignment, ``np.longdouble`` is usually stored +padded with zero bits, either to 96 or 128 bits. Which is more efficient +depends on hardware and development environment; typically on 32-bit +systems they are padded to 96 bits, while on 64-bit systems they are +typically padded to 128 bits. ``np.longdouble`` is padded to the system +default; ``np.float96`` and ``np.float128`` are provided for users who +want specific padding. In spite of the names, ``np.float96`` and +``np.float128`` provide only as much precision as ``np.longdouble``, +that is, 80 bits on most x86 machines and 64 bits in standard +Windows builds. + +Be warned that even if ``np.longdouble`` offers more precision than +python ``float``, it is easy to lose that extra precision, since +python often forces values to pass through ``float``. For example, +the ``%`` formatting operator requires its arguments to be converted +to standard python types, and it is therefore impossible to preserve +extended precision even if many decimal places are requested. It can +be useful to test your code with the value +``1 + np.finfo(np.longdouble).eps``. + + diff --git a/doc/source/user/basics.ufuncs.rst b/doc/source/user/basics.ufuncs.rst new file mode 100644 index 000000000000..083e31f702b5 --- /dev/null +++ b/doc/source/user/basics.ufuncs.rst @@ -0,0 +1,313 @@ +.. sectionauthor:: adapted from "Guide to NumPy" by Travis E. Oliphant + +.. _ufuncs-basics: + +******************************************** +Universal functions (:class:`.ufunc`) basics +******************************************** + +.. seealso:: :ref:`ufuncs` + +.. index: ufunc, universal function, arithmetic, operation + +A universal function (or :term:`ufunc` for short) is a function that +operates on :class:`ndarrays <numpy.ndarray>` in an element-by-element fashion, +supporting :ref:`array broadcasting <ufuncs.broadcasting>`, :ref:`type +casting <ufuncs.casting>`, and several other standard features. That +is, a ufunc is a ":term:`vectorized <vectorization>`" wrapper for a function +that takes a fixed number of specific inputs and produces a fixed number of +specific outputs. + +In NumPy, universal functions are instances of the +:class:`numpy.ufunc` class. Many of the built-in functions are +implemented in compiled C code. The basic ufuncs operate on scalars, but +there is also a generalized kind for which the basic elements are sub-arrays +(vectors, matrices, etc.), and broadcasting is done over other dimensions. +The simplest example is the addition operator:: + + >>> np.array([0,2,3,4]) + np.array([1,1,-1,2]) + array([1, 3, 2, 6]) + +One can also produce custom :class:`numpy.ufunc` instances using the +:func:`numpy.frompyfunc` factory function. + + +Ufunc methods +============= + +All ufuncs have four methods. They can be found at +:ref:`ufuncs.methods`. However, these methods only make sense on scalar +ufuncs that take two input arguments and return one output argument. +Attempting to call these methods on other ufuncs will cause a +:exc:`ValueError`. + +The reduce-like methods all take an *axis* keyword, a *dtype* +keyword, and an *out* keyword, and the arrays must all have dimension >= 1. +The *axis* keyword specifies the axis of the array over which the reduction +will take place (with negative values counting backwards). Generally, it is an +integer, though for :meth:`numpy.ufunc.reduce`, it can also be a tuple of +``int`` to reduce over several axes at once, or ``None``, to reduce over all +axes. For example:: + + >>> x = np.arange(9).reshape(3,3) + >>> x + array([[0, 1, 2], + [3, 4, 5], + [6, 7, 8]]) + >>> np.add.reduce(x, 1) + array([ 3, 12, 21]) + >>> np.add.reduce(x, (0, 1)) + 36 + +The *dtype* keyword allows you to manage a very common problem that arises +when naively using :meth:`.ufunc.reduce`. Sometimes you may +have an array of a certain data type and wish to add up all of its +elements, but the result does not fit into the data type of the +array. This commonly happens if you have an array of single-byte +integers. The *dtype* keyword allows you to alter the data type over which +the reduction takes place (and therefore the type of the output). Thus, +you can ensure that the output is a data type with precision large enough +to handle your output. The responsibility of altering the reduce type is +mostly up to you. There is one exception: if no *dtype* is given for a +reduction on the "add" or "multiply" operations, then if the input type is +an integer (or Boolean) data-type and smaller than the size of the +:class:`numpy.int_` data type, it will be internally upcast to the :class:`.int_` +(or :class:`numpy.uint`) data-type. In the previous example:: + + >>> x.dtype + dtype('int64') + >>> np.multiply.reduce(x, dtype=float) + array([ 0., 28., 80.]) + +Finally, the *out* keyword allows you to +provide an output array (for single-output ufuncs, which are currently the only +ones supported; for future extension, however, a tuple with a single argument +can be passed in). If *out* is given, the *dtype* argument is ignored. +Considering ``x`` from the previous example:: + + >>> y = np.zeros(3, dtype=int) + >>> y + array([0, 0, 0]) + >>> np.multiply.reduce(x, dtype=float, out=y) + array([ 0, 28, 80]) # dtype argument is ignored + +Ufuncs also have a fifth method, :func:`numpy.ufunc.at`, that allows in place +operations to be performed using advanced indexing. No +:ref:`buffering <use-of-internal-buffers>` is used on the dimensions where +advanced indexing is used, so the advanced index can +list an item more than once and the operation will be performed on the result +of the previous operation for that item. + + +.. _ufuncs-output-type: + +Output type determination +========================= + +The output of the ufunc (and its methods) is not necessarily an +:class:`ndarray <numpy.ndarray>`, if all input arguments are not +:class:`ndarrays <numpy.ndarray>`. Indeed, if any input defines an +:obj:`~.class.__array_ufunc__` method, +control will be passed completely to that function, i.e., the ufunc is +:ref:`overridden <ufuncs.overrides>`. + +If none of the inputs overrides the ufunc, then +all output arrays will be passed to the +:obj:`~.class.__array_prepare__` and +:obj:`~.class.__array_wrap__` methods of the input (besides +:class:`ndarrays <.ndarray>`, and scalars) that defines it **and** has +the highest :obj:`~.class.__array_priority__` +of any other input to the universal function. The default +:obj:`~.class.__array_priority__` of the +ndarray is 0.0, and the default :obj:`~.class.__array_priority__` of a subtype +is 0.0. Matrices have :obj:`~.class.__array_priority__` equal to 10.0. + +All ufuncs can also take output arguments. If necessary, output will +be cast to the data-type(s) of the provided output array(s). If a class +with an :obj:`~.class.__array__` method is used for the output, +results will be written to the object returned by :obj:`~.class.__array__`. +Then, if the class also has an :obj:`~.class.__array_prepare__` method, it is +called so metadata may be determined based on the context of the ufunc (the +context consisting of the ufunc itself, the arguments passed to the ufunc, and +the ufunc domain.) The array object returned by +:obj:`~.class.__array_prepare__` is passed to the ufunc for computation. +Finally, if the class also has an :obj:`~.class.__array_wrap__` method, the +returned :class:`.ndarray` result will be passed to that method just before +passing control back to the caller. + +.. _ufuncs.broadcasting: + +Broadcasting +============ + +.. seealso:: :doc:`Broadcasting basics <basics.broadcasting>` + +.. index:: broadcasting + +Each universal function takes array inputs and produces array outputs +by performing the core function element-wise on the inputs (where an +element is generally a scalar, but can be a vector or higher-order +sub-array for generalized ufuncs). Standard +:ref:`broadcasting rules <general-broadcasting-rules>` are applied +so that inputs not sharing exactly the +same shapes can still be usefully operated on. + +By these rules, if an input has a dimension size of 1 in its shape, the +first data entry in that dimension will be used for all calculations along +that dimension. In other words, the stepping machinery of the +:term:`ufunc` will simply not step along that dimension (the +:ref:`stride <memory-layout>` will be 0 for that dimension). + + +.. _ufuncs.casting: + +Type casting rules +================== + +.. index:: + pair: ufunc; casting rules + +.. note:: + + In NumPy 1.6.0, a type promotion API was created to encapsulate the + mechanism for determining output types. See the functions + :func:`numpy.result_type`, :func:`numpy.promote_types`, and + :func:`numpy.min_scalar_type` for more details. + +At the core of every ufunc is a one-dimensional strided loop that +implements the actual function for a specific type combination. When a +ufunc is created, it is given a static list of inner loops and a +corresponding list of type signatures over which the ufunc operates. +The ufunc machinery uses this list to determine which inner loop to +use for a particular case. You can inspect the :attr:`.types +<.ufunc.types>` attribute for a particular ufunc to see which type +combinations have a defined inner loop and which output type they +produce (:ref:`character codes <arrays.scalars.character-codes>` are used +in said output for brevity). + +Casting must be done on one or more of the inputs whenever the ufunc +does not have a core loop implementation for the input types provided. +If an implementation for the input types cannot be found, then the +algorithm searches for an implementation with a type signature to +which all of the inputs can be cast "safely." The first one it finds +in its internal list of loops is selected and performed, after all +necessary type casting. Recall that internal copies during ufuncs (even +for casting) are limited to the size of an internal buffer (which is user +settable). + +.. note:: + + Universal functions in NumPy are flexible enough to have mixed type + signatures. Thus, for example, a universal function could be defined + that works with floating-point and integer values. See + :func:`numpy.ldexp` for an example. + +By the above description, the casting rules are essentially +implemented by the question of when a data type can be cast "safely" +to another data type. The answer to this question can be determined in +Python with a function call: :func:`can_cast(fromtype, totype) +<numpy.can_cast>`. The example below shows the results of this call for +the 24 internally supported types on the author's 64-bit system. You +can generate this table for your system with the code given in the example. + +.. rubric:: Example + +Code segment showing the "can cast safely" table for a 64-bit system. +Generally the output depends on the system; your system might result in +a different table. + +>>> mark = {False: ' -', True: ' Y'} +>>> def print_table(ntypes): +... print('X ' + ' '.join(ntypes)) +... for row in ntypes: +... print(row, end='') +... for col in ntypes: +... print(mark[np.can_cast(row, col)], end='') +... print() +... +>>> print_table(np.typecodes['All']) +X ? b h i l q p B H I L Q P e f d g F D G S U V O M m +? Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y - Y +b - Y Y Y Y Y Y - - - - - - Y Y Y Y Y Y Y Y Y Y Y - Y +h - - Y Y Y Y Y - - - - - - - Y Y Y Y Y Y Y Y Y Y - Y +i - - - Y Y Y Y - - - - - - - - Y Y - Y Y Y Y Y Y - Y +l - - - - Y Y Y - - - - - - - - Y Y - Y Y Y Y Y Y - Y +q - - - - Y Y Y - - - - - - - - Y Y - Y Y Y Y Y Y - Y +p - - - - Y Y Y - - - - - - - - Y Y - Y Y Y Y Y Y - Y +B - - Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y - Y +H - - - Y Y Y Y - Y Y Y Y Y - Y Y Y Y Y Y Y Y Y Y - Y +I - - - - Y Y Y - - Y Y Y Y - - Y Y - Y Y Y Y Y Y - Y +L - - - - - - - - - - Y Y Y - - Y Y - Y Y Y Y Y Y - - +Q - - - - - - - - - - Y Y Y - - Y Y - Y Y Y Y Y Y - - +P - - - - - - - - - - Y Y Y - - Y Y - Y Y Y Y Y Y - - +e - - - - - - - - - - - - - Y Y Y Y Y Y Y Y Y Y Y - - +f - - - - - - - - - - - - - - Y Y Y Y Y Y Y Y Y Y - - +d - - - - - - - - - - - - - - - Y Y - Y Y Y Y Y Y - - +g - - - - - - - - - - - - - - - - Y - - Y Y Y Y Y - - +F - - - - - - - - - - - - - - - - - Y Y Y Y Y Y Y - - +D - - - - - - - - - - - - - - - - - - Y Y Y Y Y Y - - +G - - - - - - - - - - - - - - - - - - - Y Y Y Y Y - - +S - - - - - - - - - - - - - - - - - - - - Y Y Y Y - - +U - - - - - - - - - - - - - - - - - - - - - Y Y Y - - +V - - - - - - - - - - - - - - - - - - - - - - Y Y - - +O - - - - - - - - - - - - - - - - - - - - - - - Y - - +M - - - - - - - - - - - - - - - - - - - - - - Y Y Y - +m - - - - - - - - - - - - - - - - - - - - - - Y Y - Y + +You should note that, while included in the table for completeness, +the 'S', 'U', and 'V' types cannot be operated on by ufuncs. Also, +note that on a 32-bit system the integer types may have different +sizes, resulting in a slightly altered table. + +Mixed scalar-array operations use a different set of casting rules +that ensure that a scalar cannot "upcast" an array unless the scalar is +of a fundamentally different kind of data (i.e., under a different +hierarchy in the data-type hierarchy) than the array. This rule +enables you to use scalar constants in your code (which, as Python +types, are interpreted accordingly in ufuncs) without worrying about +whether the precision of the scalar constant will cause upcasting on +your large (small precision) array. + +.. _use-of-internal-buffers: + +Use of internal buffers +======================= + +.. index:: buffers + +Internally, buffers are used for misaligned data, swapped data, and +data that has to be converted from one data type to another. The size +of internal buffers is settable on a per-thread basis. There can +be up to :math:`2 (n_{\mathrm{inputs}} + n_{\mathrm{outputs}})` +buffers of the specified size created to handle the data from all the +inputs and outputs of a ufunc. The default size of a buffer is +10,000 elements. Whenever buffer-based calculation would be needed, +but all input arrays are smaller than the buffer size, those +misbehaved or incorrectly-typed arrays will be copied before the +calculation proceeds. Adjusting the size of the buffer may therefore +alter the speed at which ufunc calculations of various sorts are +completed. A simple interface for setting this variable is accessible +using the function :func:`numpy.setbufsize`. + + +Error handling +============== + +.. index:: error handling + +Universal functions can trip special floating-point status registers +in your hardware (such as divide-by-zero). If available on your +platform, these registers will be regularly checked during +calculation. Error handling is controlled on a per-thread basis, +and can be configured using the functions :func:`numpy.seterr` and +:func:`numpy.seterrcall`. + + +.. _ufuncs.overrides: + +Overriding ufunc behavior +========================= + +Classes (including ndarray subclasses) can override how ufuncs act on +them by defining certain special methods. For details, see +:ref:`arrays.classes`. diff --git a/doc/source/user/broadcasting_1.svg b/doc/source/user/broadcasting_1.svg new file mode 100644 index 000000000000..4031c3af4ff5 --- /dev/null +++ b/doc/source/user/broadcasting_1.svg @@ -0,0 +1,669 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + width="137.50784mm" + height="50.70256mm" + viewBox="0 0 137.50784 50.70256" + version="1.1" + id="svg5" + sodipodi:docname="theory.broadcasting_1.svg" + inkscape:version="1.1 (c68e22c387, 2021-05-23)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <sodipodi:namedview + id="namedview7" + pagecolor="#505050" + bordercolor="#eeeeee" + borderopacity="1" + inkscape:pageshadow="0" + inkscape:pageopacity="0" + inkscape:pagecheckerboard="0" + inkscape:document-units="mm" + showgrid="false" + showguides="true" + inkscape:zoom="1.3289991" + inkscape:cx="-66.591468" + inkscape:cy="32.731399" + inkscape:window-width="1920" + inkscape:window-height="1001" + inkscape:window-x="-9" + inkscape:window-y="-9" + inkscape:window-maximized="1" + inkscape:current-layer="layer1" + fit-margin-top="5" + fit-margin-left="5" + fit-margin-right="5" + fit-margin-bottom="5"> + <inkscape:grid + type="xygrid" + id="grid47998" + originx="-26.434583" + originy="-13.532259" /> + </sodipodi:namedview> + <defs + id="defs2"> + <marker + style="overflow:visible" + id="Arrow1Lend" + refX="0" + refY="0" + orient="auto" + inkscape:stockid="Arrow1Lend" + inkscape:isstock="true"> + <path + transform="matrix(-0.8,0,0,-0.8,-10,0)" + style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt" + d="M 0,0 5,-5 -12.5,0 5,5 Z" + id="path96534" /> + </marker> + </defs> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(-26.434582,-13.53226)"> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" + d="m 31.508555,33.859917 4.704047,-5.931184" + id="path5205" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" + d="m 58.319717,33.859917 4.704047,-5.931184" + id="path6922" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" + d="m 49.382665,33.859917 4.704048,-5.931184" + id="path6924" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" + d="m 40.445607,33.859917 4.704048,-5.931184" + id="path6926" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" + d="m 58.319717,47.091968 4.704047,-5.931184" + id="path6961" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" + d="m 36.212602,27.928733 h 8.937053 8.937058 8.937051" + id="path6996" + sodipodi:nodetypes="cccc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" + d="M 63.023764,27.928733 V 41.160784" + id="path7224" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" + d="m 31.508555,33.859917 4.704047,-5.931184" + id="path9982" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" + d="m 58.319717,33.859917 4.704047,-5.931184" + id="path9988" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" + d="m 49.382665,33.859917 4.704048,-5.931184" + id="path9990" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" + d="m 40.445607,33.859917 4.704048,-5.931184" + id="path9992" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" + d="m 58.319717,47.091968 4.704047,-5.931184" + id="path9994" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" + d="m 36.212602,27.928733 h 8.937053 8.937058 8.937051" + id="path9996" + sodipodi:nodetypes="cccc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" + d="M 63.023764,27.928733 V 41.160784" + id="path9998" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.147946px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 79.344654,34.069936 4.704047,-5.931184" + id="path48002" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.147946px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 106.15582,34.069936 4.70405,-5.931184" + id="path48008" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.147946px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 97.21876,34.069936 4.70405,-5.931184" + id="path48010" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.147946px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 88.281704,34.069936 4.704049,-5.931184" + id="path48012" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.147946px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 106.15582,47.301981 4.70405,-5.931184" + id="path48014" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.147946px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 110.85987,28.138752 V 41.370797" + id="path48018" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.147946px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 79.344654,34.069936 4.704047,-5.931184" + id="path48030" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.147946px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 106.15582,34.069936 4.70405,-5.931184" + id="path48036" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.147946px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 97.21876,34.069936 4.70405,-5.931184" + id="path48038" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.147946px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 88.281704,34.069936 4.704049,-5.931184" + id="path48040" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.147946px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 106.15582,47.301981 4.70405,-5.931184" + id="path48042" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.147946px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 84.048701,28.138752 h 8.937052 8.937057 8.93706" + id="path48044" + sodipodi:nodetypes="cccc" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.147946px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 110.85987,28.138752 V 41.370797" + id="path48046" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.147946px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 79.344654,34.069936 h 8.93705 8.937056 8.93706 v 13.232045" + id="path52709" + sodipodi:nodetypes="ccccc" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.147946px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 106.15582,47.301981 H 97.218767 88.28171 79.344654 V 34.069936" + id="path53128" + sodipodi:nodetypes="ccccc" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.147946px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 88.281704,34.069936 V 47.301981" + id="path53650" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.147946px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 97.21876,34.069936 V 47.301981" + id="path53935" + sodipodi:nodetypes="cc" /> + <path + style="fill:#fcffff;fill-opacity:1;stroke:#000000;stroke-width:0.248;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 79.597689,33.925906 c 0.0217,-0.0352 1.048179,-1.335017 2.281023,-2.888597 l 2.241541,-2.824686 h 4.341296 4.341292 l -2.286401,2.886538 -2.286409,2.886544 -4.335912,0.0019 c -3.456122,0.0019 -4.327906,-0.01087 -4.29643,-0.06183 z" + id="path53974" + sodipodi:nodetypes="cscccccccc" /> + <path + style="fill:#fcffff;fill-opacity:1;stroke:#c2c0c0;stroke-width:0.128684;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 88.519647,33.947332 c 0.0095,-0.02368 1.029425,-1.322348 2.26667,-2.885914 l 2.249527,-2.842843 4.332898,-0.0026 c 2.383092,-0.0019 4.332888,0.0078 4.332888,0.02098 0,0.01319 -1.01863,1.307278 -2.263631,2.875812 l -2.263628,2.851889 -4.335929,0.01306 c -2.384764,0.0072 -4.328222,-0.0064 -4.318795,-0.02998 z" + id="path54013" + sodipodi:nodetypes="csccsscccc" /> + <path + style="fill:#fcffff;fill-opacity:1;stroke:#c2c0c0;stroke-width:0.128684;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 97.845189,33.440213 c 0.240884,-0.30228 0.711251,-0.894698 1.045271,-1.316486 0.793854,-1.002451 1.595,-2.012218 2.36656,-2.982813 0.34296,-0.431421 0.6492,-0.816914 0.68053,-0.856639 0.0485,-0.06138 0.70602,-0.07027 4.38287,-0.05913 l 4.32589,0.01306 -2.28034,2.875812 -2.28033,2.875818 h -4.33921 -4.339205 z" + id="path54052" + sodipodi:nodetypes="ssscccccccs" /> + <path + style="fill:#fcffff;fill-opacity:1;stroke:#c2c0c0;stroke-width:0.128684;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 97.304831,40.661714 v -6.518518 h 4.371249 4.37124 v 6.518518 6.518518 h -4.37124 -4.371249 z" + id="path54091" + sodipodi:nodetypes="ccccccccc" /> + <path + style="fill:#fcffff;fill-opacity:1;stroke:#c2c0c0;stroke-width:0.128684;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 88.357846,40.661714 v -6.518518 h 4.37124 4.37124 v 6.518518 6.518518 h -4.37124 -4.37124 z" + id="path54130" + sodipodi:nodetypes="ccccccccc" /> + <path + style="fill:#fcffff;fill-opacity:1;stroke:#000000;stroke-width:0.248;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 79.461984,40.661714 v -6.518518 h 4.371248 4.37124 v 6.518518 6.518518 h -4.37124 -4.371248 z" + id="path54169" + sodipodi:nodetypes="ccccccccc" /> + <path + style="fill:#fcffff;fill-opacity:1;stroke:#c2c0c0;stroke-width:0.128684;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 106.25221,40.556901 4.4e-4,-6.439275 2.22941,-2.811907 c 1.22617,-1.546548 2.23829,-2.821501 2.24913,-2.833211 0.0109,-0.01171 0.0198,2.878057 0.0198,6.421722 v 6.443019 l -2.20348,2.780901 c -1.21193,1.529484 -2.22422,2.802751 -2.24953,2.82946 -0.0273,0.02882 -0.0459,-2.570193 -0.0456,-6.390709 z" + id="path54208" + sodipodi:nodetypes="ccscsccscc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" + d="m 31.508555,33.859917 h 8.937052 8.937058 8.937052" + id="path55000" + sodipodi:nodetypes="cccc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" + d="M 58.319717,47.091968 V 33.859917" + id="path55035" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" + d="M 31.508555,47.091968 V 33.859917" + id="path55070" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" + d="M 40.445607,47.091968 V 33.859917" + id="path55072" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" + d="M 49.382665,47.091968 V 33.859917" + id="path55074" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" + d="m 31.508555,47.091968 h 8.937052 8.937058 8.937052" + id="path55189" + sodipodi:nodetypes="cccc" /> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.91785px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.147946" + x="81.888199" + y="42.102463" + id="text62163"><tspan + sodipodi:role="line" + id="tspan62161" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.91785px;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.147946" + x="81.888199" + y="42.102463">2</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.91785px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#c0c0c0;fill-opacity:1;stroke:none;stroke-width:0.147946" + x="90.934624" + y="42.102463" + id="text66863"><tspan + sodipodi:role="line" + id="tspan66861" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.91785px;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#c0c0c0;fill-opacity:1;stroke-width:0.147946" + x="90.934624" + y="42.102463">2</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.91785px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#c0c0c0;fill-opacity:1;stroke:none;stroke-width:0.147946" + x="99.941132" + y="42.102463" + id="text77828"><tspan + sodipodi:role="line" + id="tspan77826" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.91785px;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#c0c0c0;fill-opacity:1;stroke-width:0.147946" + x="99.941132" + y="42.102463">2</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.91785px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.147946" + x="69.630562" + y="41.596489" + id="text85828"><tspan + sodipodi:role="line" + id="tspan85826" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.91785px;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.147946" + x="69.630562" + y="41.596489">*</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.91785px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.147946" + x="117.43818" + y="40.412045" + id="text87726"><tspan + sodipodi:role="line" + id="tspan87724" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.91785px;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.147946" + x="117.43818" + y="40.412045">=</tspan></text> + <text + xml:space="preserve" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.91785px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.147946" + x="44.636856" + y="23.010897" + id="text89497"><tspan + sodipodi:role="line" + id="tspan89495" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.91785px;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.147946" + x="44.636856" + y="23.010897"><tspan + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.91785px;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal" + id="tspan5460">a</tspan> (3)</tspan></text> + <text + xml:space="preserve" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.91785px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.147946" + x="93.357834" + y="23.034334" + id="text90809"><tspan + sodipodi:role="line" + id="tspan90807" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.91785px;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.147946" + x="93.357834" + y="23.034334"><tspan + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.91785px;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal" + id="tspan2930">b</tspan> (1)</tspan></text> + <text + xml:space="preserve" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.91785px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.147946" + x="136.34079" + y="22.840616" + id="text91329"><tspan + sodipodi:role="line" + id="tspan91327" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.91785px;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.147946" + x="136.34079" + y="22.840616"><tspan + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.91785px;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal" + id="tspan3730">result</tspan> (3)</tspan></text> + <text + xml:space="preserve" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.91785px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.147946" + x="85.080841" + y="59.16547" + id="text93193"><tspan + sodipodi:role="line" + id="tspan93191" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.91785px;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.147946" + x="85.080841" + y="59.16547">stretch</tspan></text> + <path + style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.165526;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow1Lend)" + d="m 81.41288,52.445886 h 23.74344" + id="path96373" + sodipodi:nodetypes="cc" /> + <path + style="fill:#ffffff;stroke:#000000;stroke-width:0.433145;stroke-miterlimit:5.30435;stroke-dasharray:none" + d="m 21.652887,74.977138 c 0.50651,-0.698939 4.230051,-5.429777 8.274534,-10.512973 l 7.353605,-9.242174 16.031996,-0.121532 16.031995,-0.121531 -5.423208,6.822108 c -2.982765,3.752159 -6.772069,8.537686 -8.420677,10.634504 l -2.997469,3.812397 H 36.61781 20.731958 Z" + id="path12997" + transform="matrix(0.26458333,0,0,0.26458333,26.434582,13.53226)" /> + <path + style="fill:#ffffff;stroke:#000000;stroke-width:0.433145;stroke-miterlimit:5.30435;stroke-dasharray:none" + d="m 55.08026,75.409135 c 0.332112,-0.461342 4.101427,-5.244167 8.376257,-10.6285 l 7.772417,-9.789699 h 15.949964 15.949962 l -1.93952,2.426071 c -1.06673,1.334339 -4.858939,6.117164 -8.427127,10.6285 l -6.487614,8.20243 H 70.37551 c -15.273126,0 -15.875315,-0.03302 -15.29525,-0.838802 z" + id="path13036" + transform="matrix(0.26458333,0,0,0.26458333,26.434582,13.53226)" /> + <path + style="fill:#ffffff;stroke:#000000;stroke-width:0.433145;stroke-miterlimit:5.30435;stroke-dasharray:none" + d="m 89.730475,74.283975 c 0.868474,-1.080179 4.664812,-5.863004 8.436305,-10.6285 l 6.85726,-8.664539 h 15.93574 15.93573 l -1.57904,1.963962 c -0.86848,1.08018 -4.66482,5.863005 -8.43631,10.628501 l -6.85726,8.664538 H 104.08717 88.15143 Z" + id="path13075" + transform="matrix(0.26458333,0,0,0.26458333,26.434582,13.53226)" /> + <path + style="fill:#ffffff;stroke:#000000;stroke-width:0.433145;stroke-miterlimit:5.30435;stroke-dasharray:none" + d="M 87.338546,101.89497 V 77.634263 h 16.173804 16.17381 v 24.260707 24.26071 H 103.51235 87.338546 Z" + id="path13114" + transform="matrix(0.26458333,0,0,0.26458333,26.434582,13.53226)" /> + <path + style="fill:#ffffff;stroke:#000000;stroke-width:0.433145;stroke-miterlimit:5.30435;stroke-dasharray:none" + d="M 53.60461,101.89497 V 77.634263 H 69.778415 85.95222 v 24.260707 24.26071 H 69.778415 53.60461 Z" + id="path13153" + transform="matrix(0.26458333,0,0,0.26458333,26.434582,13.53226)" /> + <path + style="fill:#ffffff;stroke:#000000;stroke-width:0.433145;stroke-miterlimit:5.30435;stroke-dasharray:none" + d="M 19.870674,101.89497 V 77.634263 h 16.173805 16.173805 v 24.260707 24.26071 H 36.044479 19.870674 Z" + id="path13192" + transform="matrix(0.26458333,0,0,0.26458333,26.434582,13.53226)" /> + <path + style="fill:#ffffff;stroke:#000000;stroke-width:0.433145;stroke-miterlimit:5.30435;stroke-dasharray:none" + d="M 121.07248,101.12036 V 77.327368 l 8.20243,-10.336784 8.20243,-10.336785 0.11972,23.659407 0.11973,23.659404 -8.32216,10.47037 -8.32215,10.47037 z" + id="path13231" + transform="matrix(0.26458333,0,0,0.26458333,26.434582,13.53226)" /> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.91785px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.147946" + x="34.390987" + y="41.801289" + id="text11724"><tspan + sodipodi:role="line" + id="tspan11722" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.91785px;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.147946" + x="34.390987" + y="41.801289">1</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.91785px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.147946" + x="43.594547" + y="41.801289" + id="text11728"><tspan + sodipodi:role="line" + id="tspan11726" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.91785px;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.147946" + x="43.594547" + y="41.801289">2</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.91785px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.147946" + x="52.389057" + y="41.801289" + id="text11732"><tspan + sodipodi:role="line" + id="tspan11730" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.91785px;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.147946" + x="52.389057" + y="41.801289">3</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.91785px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.147946" + x="34.390987" + y="41.801289" + id="text11736"><tspan + sodipodi:role="line" + id="tspan11734" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.91785px;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.147946" + x="34.390987" + y="41.801289">1</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.91785px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.147946" + x="43.594547" + y="41.801289" + id="text11740"><tspan + sodipodi:role="line" + id="tspan11738" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.91785px;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.147946" + x="43.594547" + y="41.801289">2</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.91785px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.147946" + x="52.389057" + y="41.801289" + id="text11744"><tspan + sodipodi:role="line" + id="tspan11742" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.91785px;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.147946" + x="52.389057" + y="41.801289">3</tspan></text> + <g + id="g14529" + transform="translate(-29.470708,43.570713)"> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 156.53372,-9.7396236 4.70405,-5.9311844" + id="path14293" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 183.34488,-9.7396236 4.70405,-5.9311844" + id="path14295" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 174.40783,-9.7396236 4.70405,-5.9311844" + id="path14297" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 165.47077,-9.7396236 4.70405,-5.9311844" + id="path14299" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 183.34488,3.4924274 4.70405,-5.931184" + id="path14301" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 161.23777,-15.670808 h 8.93705 8.93706 8.93705" + id="path14303" + sodipodi:nodetypes="cccc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="M 188.04893,-15.670808 V -2.4387566" + id="path14305" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 156.53372,-9.7396236 4.70405,-5.9311844" + id="path14307" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 183.34488,-9.7396236 4.70405,-5.9311844" + id="path14309" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 174.40783,-9.7396236 4.70405,-5.9311844" + id="path14311" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 165.47077,-9.7396236 4.70405,-5.9311844" + id="path14313" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 183.34488,3.4924274 4.70405,-5.931184" + id="path14315" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 161.23777,-15.670808 h 8.93705 8.93706 8.93705" + id="path14317" + sodipodi:nodetypes="cccc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="M 188.04893,-15.670808 V -2.4387566" + id="path14319" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 156.53372,-9.7396236 h 8.93705 8.93706 8.93705" + id="path14321" + sodipodi:nodetypes="cccc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="M 183.34488,3.4924274 V -9.7396236" + id="path14323" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="M 156.53372,3.4924274 V -9.7396236" + id="path14325" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="M 165.47077,3.4924274 V -9.7396236" + id="path14327" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="M 174.40783,3.4924274 V -9.7396236" + id="path14329" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.248;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 156.53372,3.4924274 h 8.93705 8.93706 8.93705" + id="path14331" + sodipodi:nodetypes="cccc" /> + <path + style="fill:#ffffff;stroke:#000000;stroke-width:0.114603;stroke-miterlimit:5.30435;stroke-dasharray:none" + d="m 157.18874,-10.22958 c 0.13402,-0.184927 1.1192,-1.436628 2.18931,-2.781557 l 1.94564,-2.445325 4.2418,-0.03216 4.24179,-0.03215 -1.43489,1.805016 c -0.78919,0.992759 -1.79177,2.25893 -2.22797,2.813713 l -0.79308,1.0086964 h -4.20313 -4.20313 z" + id="path14333" /> + <path + style="fill:#ffffff;stroke:#000000;stroke-width:0.114603;stroke-miterlimit:5.30435;stroke-dasharray:none" + d="m 166.03307,-10.115281 c 0.0879,-0.122063 1.08517,-1.387519 2.21622,-2.812124 l 2.05645,-2.590191 h 4.22009 4.2201 l -0.51317,0.641898 c -0.28224,0.353044 -1.28559,1.6185 -2.22968,2.812124 l -1.71651,2.1702264 h -4.20663 c -4.04102,0 -4.20035,-0.00874 -4.04687,-0.2219334 z" + id="path14335" /> + <path + style="fill:#ffffff;stroke:#000000;stroke-width:0.114603;stroke-miterlimit:5.30435;stroke-dasharray:none" + d="m 175.20094,-10.412979 c 0.22978,-0.285798 1.23423,-1.551253 2.2321,-2.812124 l 1.81432,-2.292493 h 4.21633 4.21633 l -0.41779,0.519632 c -0.22978,0.285798 -1.23423,1.551253 -2.23211,2.812124 l -1.81431,2.2924924 h -4.21633 -4.21633 z" + id="path14337" /> + <path + style="fill:#ffffff;stroke:#000000;stroke-width:0.114603;stroke-miterlimit:5.30435;stroke-dasharray:none" + d="m 174.56807,-3.1075702 v -6.4189786 h 4.27932 4.27932 v 6.4189786 6.4189795 h -4.27932 -4.27932 z" + id="path14339" /> + <path + style="fill:#ffffff;stroke:#000000;stroke-width:0.114603;stroke-miterlimit:5.30435;stroke-dasharray:none" + d="m 165.64264,-3.1075702 v -6.4189786 h 4.27931 4.27932 v 6.4189786 6.4189795 h -4.27932 -4.27931 z" + id="path14341" /> + <path + style="fill:#ffffff;stroke:#000000;stroke-width:0.114603;stroke-miterlimit:5.30435;stroke-dasharray:none" + d="m 156.7172,-3.1075702 v -6.4189786 h 4.27932 4.27932 v 6.4189786 6.4189795 h -4.27932 -4.27932 z" + id="path14343" /> + <path + style="fill:#ffffff;stroke:#000000;stroke-width:0.114603;stroke-miterlimit:5.30435;stroke-dasharray:none" + d="m 183.49351,-3.3125191 v -6.295229 l 2.17022,-2.7349409 2.17023,-2.734941 0.0317,6.2598848 0.0317,6.2598839 -2.20191,2.7702854 -2.2019,2.7702854 z" + id="path14345" /> + <g + id="g14377" + transform="translate(29.265446,-43.59954)"> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.91785px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.147946" + x="130.58751" + y="42.458767" + id="text77832"><tspan + sodipodi:role="line" + id="tspan77830" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.91785px;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:0.147946" + x="130.58751" + y="42.458767">2</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.91785px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.147946" + x="139.3645" + y="42.501537" + id="text79640"><tspan + sodipodi:role="line" + id="tspan79638" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.91785px;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:0.147946" + x="139.3645" + y="42.501537">4</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.91785px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.147946" + x="148.36816" + y="42.501537" + id="text81564"><tspan + sodipodi:role="line" + id="tspan81562" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.91785px;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:0.147946" + x="148.36816" + y="42.501537">6</tspan></text> + </g> + </g> + </g> +</svg> diff --git a/doc/source/user/broadcasting_2.svg b/doc/source/user/broadcasting_2.svg new file mode 100644 index 000000000000..6985dad898f4 --- /dev/null +++ b/doc/source/user/broadcasting_2.svg @@ -0,0 +1,1308 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + width="136.12085mm" + height="55.258423mm" + viewBox="0 0 136.12085 55.258424" + version="1.1" + id="svg2361" + inkscape:version="1.1 (c68e22c387, 2021-05-23)" + sodipodi:docname="theory.broadcasting_2.svg" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <sodipodi:namedview + id="namedview2363" + pagecolor="#004100" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageshadow="2" + inkscape:pageopacity="0" + inkscape:pagecheckerboard="0" + inkscape:document-units="mm" + showgrid="true" + inkscape:zoom="1.7469907" + inkscape:cx="30.051677" + inkscape:cy="75.844708" + inkscape:window-width="1920" + inkscape:window-height="1001" + inkscape:window-x="-9" + inkscape:window-y="-9" + inkscape:window-maximized="1" + inkscape:current-layer="layer1" + lock-margins="false" + fit-margin-top="5" + fit-margin-left="5" + fit-margin-right="5" + fit-margin-bottom="5" + showguides="true" + inkscape:guide-bbox="true"> + <inkscape:grid + type="xygrid" + id="grid44453" + originx="-1.3010658" + originy="-2.8566359" /> + </sodipodi:namedview> + <defs + id="defs2358"> + <marker + style="overflow:visible" + id="Arrow1Lend" + refX="0" + refY="0" + orient="auto" + inkscape:stockid="Arrow1Lend" + inkscape:isstock="true"> + <path + transform="matrix(-0.8,0,0,-0.8,-10,0)" + style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt" + d="M 0,0 5,-5 -12.5,0 5,5 Z" + id="path96534" /> + </marker> + </defs> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(142.99324,-67.354454)"> + <text + xml:space="preserve" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:4.57899px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.114475" + x="90.957649" + y="63.798756" + id="text93193" + transform="rotate(90)"><tspan + sodipodi:role="line" + id="tspan93191" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:4.57899px;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.114475" + x="90.957649" + y="63.798756">stretch</tspan></text> + <path + style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.236131;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow1Lend)" + d="M -64.919305,88.215985 V 106.5877" + id="path96373" /> + <g + id="g46002" + transform="matrix(1.0057054,0,0,1.0057054,-82.208516,-72.64114)"> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -47.512773,165.23404 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path21691" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -47.512773,157.41086 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path21910" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -55.335963,165.23404 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path21912" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -55.335963,157.41086 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path21914" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -47.512773,173.05722 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path21916" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -39.689583,173.05722 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path21918" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -47.512773,180.8804 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path21920" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -55.335963,180.8804 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path21922" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -39.689583,165.23404 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path21924" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -55.335963,173.05722 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path21926" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -39.689583,157.41086 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path21928" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -39.689583,180.8804 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path21930" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -55.335963,157.41086 4.34622,-6.08471" + id="path22112" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M -31.866393,157.58471 -27.520173,151.5" + id="path22147" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M -39.689583,157.58471 -35.343363,151.5" + id="path22149" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M -47.512773,157.58471 -43.166553,151.5" + id="path22151" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -27.520173,151.5 v 31.29272" + id="path22266" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -50.989743,151.32615 7.82319,0.17385 h 7.82319 7.82319" + id="path22381" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -31.866393,188.87743 c 4.34622,-6.08471 4.34622,-6.08471 4.34622,-6.08471" + id="path22496" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -31.866393,165.40789 c 4.34622,-6.08471 4.34622,-6.08471 4.34622,-6.08471" + id="path22644" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -31.866393,173.23107 c 4.34622,-6.08471 4.34622,-6.08471 4.34622,-6.08471" + id="path22646" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -31.866393,181.05425 c 4.34622,-6.08471 4.34622,-6.08471 4.34622,-6.08471" + id="path22648" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -47.512773,165.23404 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22650" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -47.512773,157.41086 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22652" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -55.335963,165.23404 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22654" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -55.335963,157.41086 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22656" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -47.512773,173.05722 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22658" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -39.689583,173.05722 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22660" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -47.512773,180.8804 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22662" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -55.335963,180.8804 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22664" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -39.689583,165.23404 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22666" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -55.335963,173.05722 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22668" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -39.689583,157.41086 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22670" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -39.689583,180.8804 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22672" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -55.335963,157.41086 4.34622,-6.08471" + id="path22674" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M -31.866393,157.58471 -27.520173,151.5" + id="path22676" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M -39.689583,157.58471 -35.343363,151.5" + id="path22678" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M -47.512773,157.58471 -43.166553,151.5" + id="path22680" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -27.520173,151.5 v 31.29272" + id="path22682" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -50.989743,151.32615 7.82319,0.17385 h 7.82319 7.82319" + id="path22684" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -31.866393,188.87743 c 4.34622,-6.08471 4.34622,-6.08471 4.34622,-6.08471" + id="path22686" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -31.866393,165.40789 c 4.34622,-6.08471 4.34622,-6.08471 4.34622,-6.08471" + id="path22688" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -31.866393,173.23107 c 4.34622,-6.08471 4.34622,-6.08471 4.34622,-6.08471" + id="path22690" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -31.866393,181.05425 c 4.34622,-6.08471 4.34622,-6.08471 4.34622,-6.08471" + id="path22692" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -55.142483,161.45304 v -3.71055 h 3.71055 3.71054 v 3.71055 3.71055 h -3.71054 -3.71055 z" + id="path13541" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -47.267033,161.45304 v -3.71055 h 3.71054 3.71055 v 3.71055 3.71055 h -3.71055 -3.71054 z" + id="path13580" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -39.543043,161.45304 v -3.71055 h 3.71055 3.71055 v 3.71055 3.71055 h -3.71055 -3.71055 z" + id="path13619" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -39.543043,169.32849 v -3.71055 h 3.71055 3.71055 v 3.71055 3.71054 h -3.71055 -3.71055 z" + id="path13658" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -47.267033,169.32849 v -3.71055 h 3.71054 3.71055 v 3.71055 3.71054 h -3.71055 -3.71054 z" + id="path13697" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -55.142483,169.32849 v -3.71055 h 3.71055 3.71054 v 3.71055 3.71054 h -3.71054 -3.71055 z" + id="path13736" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -55.142483,177.20393 v -3.71054 h 3.71055 3.71054 v 3.71054 3.71055 h -3.71054 -3.71055 z" + id="path13775" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -47.267033,177.20393 v -3.71054 h 3.71054 3.71055 v 3.71054 3.71055 h -3.71055 -3.71054 z" + id="path13814" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -39.543043,177.20393 v -3.71054 h 3.71055 3.71055 v 3.71054 3.71055 h -3.71055 -3.71055 z" + id="path13853" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -39.543043,184.92793 v -3.71055 h 3.71055 3.71055 v 3.71055 3.71055 h -3.71055 -3.71055 z" + id="path13892" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -47.267033,184.92793 v -3.71055 h 3.71054 3.71055 v 3.71055 3.71055 h -3.71055 -3.71054 z" + id="path13931" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -55.142483,184.92793 v -3.71055 h 3.71055 3.71054 v 3.71055 3.71055 h -3.71054 -3.71055 z" + id="path13970" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -54.899273,157.25299 c 0.0622,-0.10561 0.9991,-1.43582 2.08211,-2.95601 l 1.96911,-2.76398 1.44974,0.019 c 0.79736,0.0105 2.42287,0.0616 3.61225,0.11359 l 2.1625,0.0945 -2.01181,2.80185 -2.0118,2.80184 -3.68255,0.0406 c -2.94083,0.0324 -3.65979,0.002 -3.56955,-0.15145 z" + id="path14009" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -46.783393,156.94737 c 0.20247,-0.27071 1.13436,-1.56562 2.07086,-2.87756 l 1.70272,-2.38535 h 3.62228 3.62228 l -2.05577,2.87756 -2.05578,2.87757 h -3.63737 -3.63736 z" + id="path14048" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -39.134073,157.23722 c 0.0666,-0.11131 0.97302,-1.40328 2.01425,-2.87104 l 1.89313,-2.66867 3.60958,-0.007 c 1.98527,-0.004 3.58686,0.0493 3.5591,0.11756 -0.0278,0.0683 -0.93452,1.36316 -2.015,2.87757 l -1.96451,2.75347 h -3.60883 c -2.97073,0 -3.58742,-0.0358 -3.48772,-0.20237 z" + id="path14087" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -31.667593,161.15934 v -3.45903 l 1.83964,-2.59144 c 1.01181,-1.42529 1.89779,-2.65618 1.96886,-2.73531 0.0718,-0.0799 0.12922,1.40769 0.12922,3.34472 v 3.4886 l -1.82587,2.56186 c -1.00422,1.40903 -1.89021,2.62661 -1.96886,2.70574 -0.0897,0.0903 -0.14299,-1.14491 -0.14299,-3.31514 z" + id="path14126" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -31.667593,169.01521 v -3.47859 l 1.83457,-2.57187 c 1.00901,-1.41453 1.895,-2.63661 1.96886,-2.71575 0.0784,-0.084 0.13429,1.30477 0.13429,3.33472 v 3.4786 l -1.83457,2.57187 c -1.00901,1.41452 -1.895,2.63661 -1.96886,2.71574 -0.0784,0.084 -0.13429,-1.30476 -0.13429,-3.33472 z" + id="path14165" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -31.667593,176.85105 v -3.49847 l 1.96886,-2.75406 1.96886,-2.75406 v 3.51758 3.51757 l -1.69335,2.37377 c -0.93135,1.30557 -1.81734,2.5363 -1.96886,2.73496 -0.26885,0.35246 -0.27551,0.27663 -0.27551,-3.13729 z" + id="path14204" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -31.667593,184.63423 v -3.45903 l 1.83964,-2.59143 c 1.01181,-1.4253 1.89779,-2.65619 1.96886,-2.73532 0.0718,-0.0799 0.12922,1.40768 0.12922,3.34472 v 3.4886 l -1.82587,2.56186 c -1.00422,1.40903 -1.89021,2.62661 -1.96886,2.70575 -0.0897,0.0903 -0.14299,-1.14492 -0.14299,-3.31515 z" + id="path14243" /> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-36.608471" + y="162.74039" + id="text17645"><tspan + sodipodi:role="line" + id="tspan17643" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-36.608471" + y="162.74039">0</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-37.600399" + y="170.6588" + id="text21689"><tspan + sodipodi:role="line" + id="tspan21687" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-37.600399" + y="170.6588">10</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-37.477116" + y="178.57722" + id="text22617"><tspan + sodipodi:role="line" + id="tspan22615" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-37.477116" + y="178.57722">20</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-37.494289" + y="186.49469" + id="text23721"><tspan + sodipodi:role="line" + id="tspan23719" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-37.494289" + y="186.49469">30</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-44.447037" + y="162.74039" + id="text25685"><tspan + sodipodi:role="line" + id="tspan25683" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-44.447037" + y="162.74039">0</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-45.438965" + y="170.6588" + id="text25689"><tspan + sodipodi:role="line" + id="tspan25687" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-45.438965" + y="170.6588">10</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-45.315681" + y="178.57722" + id="text25693"><tspan + sodipodi:role="line" + id="tspan25691" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-45.315681" + y="178.57722">20</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-45.332859" + y="186.49469" + id="text25697"><tspan + sodipodi:role="line" + id="tspan25695" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-45.332859" + y="186.49469">30</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-52.285603" + y="162.74039" + id="text25701"><tspan + sodipodi:role="line" + id="tspan25699" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-52.285603" + y="162.74039">0</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-53.277534" + y="170.6588" + id="text25705"><tspan + sodipodi:role="line" + id="tspan25703" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-53.277534" + y="170.6588">10</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-53.154247" + y="178.57722" + id="text25709"><tspan + sodipodi:role="line" + id="tspan25707" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-53.154247" + y="178.57722">20</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-53.171425" + y="186.49469" + id="text25713"><tspan + sodipodi:role="line" + id="tspan25711" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-53.171425" + y="186.49469">30</tspan></text> + </g> + <path + style="fill:none;stroke:#000000;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -88.477569,93.701364 v 8.042716 h 7.867883 v -7.867871 h -7.867883 z" + id="path22942" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -88.477569,85.833492 v 8.042717 h 7.867883 v -7.867873 h -7.867883 z" + id="path22944" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -96.345452,93.701364 v 8.042716 h 7.867883 v -7.867871 h -7.867883 z" + id="path22946" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -96.345452,85.833492 v 8.042717 h 7.867883 v -7.867873 h -7.867883 z" + id="path22948" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -88.477569,101.56924 v 8.04271 h 7.867883 v -7.86787 h -7.867883 z" + id="path22950" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -80.609686,101.56924 v 8.04271 h 7.867885 v -7.86787 h -7.867885 z" + id="path22952" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -88.477569,109.43711 v 8.04272 h 7.867883 v -7.86788 h -7.867883 z" + id="path22954" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -96.345452,109.43711 v 8.04272 h 7.867883 v -7.86788 h -7.867883 z" + id="path22956" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -80.609686,93.701364 v 8.042716 h 7.867885 v -7.867871 h -7.867885 z" + id="path22958" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -96.345452,101.56924 v 8.04271 h 7.867883 v -7.86787 h -7.867883 z" + id="path22960" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -80.609686,85.833492 v 8.042717 h 7.867885 v -7.867873 h -7.867885 z" + id="path22962" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -80.609686,109.43711 v 8.04272 h 7.867885 v -7.86788 h -7.867885 z" + id="path22964" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -96.345452,85.833492 4.37105,-6.119472" + id="path22966" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -72.741801,86.008336 4.371049,-6.119471" + id="path22968" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -80.609686,86.008336 4.371051,-6.119471" + id="path22970" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -88.477569,86.008336 4.37105,-6.119471" + id="path22972" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -91.974402,79.71402 7.867883,0.174845 h 7.867884 7.867883" + id="path22976" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -72.741801,117.47983 c 4.371049,-6.11948 4.371049,-6.11948 4.371049,-6.11948" + id="path22978" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -72.741801,93.876209 c 4.371049,-6.119471 4.371049,-6.119471 4.371049,-6.119471" + id="path22980" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -72.741801,101.74408 c 4.371049,-6.119469 4.371049,-6.119469 4.371049,-6.119469" + id="path22982" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -72.741801,109.61195 c 4.371049,-6.11947 4.371049,-6.11947 4.371049,-6.11947" + id="path22984" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -88.477569,93.701364 v 8.042716 h 7.867883 v -7.867871 h -7.867883 z" + id="path22986" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -88.477569,85.833492 v 8.042717 h 7.867883 v -7.867873 h -7.867883 z" + id="path22988" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -96.345452,93.701364 v 8.042716 h 7.867883 v -7.867871 h -7.867883 z" + id="path22990" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -96.345452,85.833492 v 8.042717 h 7.867883 v -7.867873 h -7.867883 z" + id="path22992" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -88.477569,101.56924 v 8.04271 h 7.867883 v -7.86787 h -7.867883 z" + id="path22994" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -80.609686,101.56924 v 8.04271 h 7.867885 v -7.86787 h -7.867885 z" + id="path22996" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -88.477569,109.43711 v 8.04272 h 7.867883 v -7.86788 h -7.867883 z" + id="path22998" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -96.345452,109.43711 v 8.04272 h 7.867883 v -7.86788 h -7.867883 z" + id="path23000" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -80.609686,93.701364 v 8.042716 h 7.867885 v -7.867871 h -7.867885 z" + id="path23002" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -96.345452,101.56924 v 8.04271 h 7.867883 v -7.86787 h -7.867883 z" + id="path23004" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -80.609686,85.833492 v 8.042717 h 7.867885 v -7.867873 h -7.867885 z" + id="path23006" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -80.609686,109.43711 v 8.04272 h 7.867885 v -7.86788 h -7.867885 z" + id="path23008" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -96.345452,85.833492 4.37105,-6.119472" + id="path23010" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -72.741801,86.008336 4.371049,-6.119471" + id="path23012" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -80.609686,86.008336 4.371051,-6.119471" + id="path23014" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -88.477569,86.008336 4.37105,-6.119471" + id="path23016" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -91.974402,79.71402 7.867883,0.174845 h 7.867884 7.867883" + id="path23020" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -72.741801,117.47983 c 4.371049,-6.11948 4.371049,-6.11948 4.371049,-6.11948" + id="path23022" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -72.741801,93.876209 c 4.371049,-6.119471 4.371049,-6.119471 4.371049,-6.119471" + id="path23024" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -72.741801,101.74408 c 4.371049,-6.119469 4.371049,-6.119469 4.371049,-6.119469" + id="path23026" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -72.741801,109.61195 c 4.371049,-6.11947 4.371049,-6.11947 4.371049,-6.11947" + id="path23028" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -68.370751,79.888865 v 7.867873" + id="path25266" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.266095px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -68.370751,111.36035 v -7.86787 -7.867869 -7.867873" + id="path25301" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.115137" + d="m -95.976457,85.797112 c 0,-0.04012 0.932308,-1.377881 2.071787,-2.972874 l 2.071791,-2.899975 1.469565,0.01909 c 0.808261,0.01077 2.441725,0.062 3.629919,0.114237 l 2.160351,0.0951 -2.014757,2.817848 -2.014754,2.817847 -3.686948,0.04076 c -2.027823,0.02243 -3.686954,0.0075 -3.686954,-0.03214 z" + id="path14282" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.115137" + d="m -87.460034,84.98877 c 0.338488,-0.481696 1.254857,-1.766862 2.036376,-2.855931 l 1.420944,-1.980112 3.609047,-0.04087 c 1.984978,-0.02243 3.60905,-0.001 3.60905,0.04733 0,0.04852 -0.903225,1.352058 -2.007167,2.896751 l -2.007163,2.808543 h -3.638261 -3.638259 z" + id="path14321" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.115137" + d="m -80.028905,85.650787 c 0.06699,-0.117601 0.978585,-1.418412 2.02576,-2.890703 l 1.903952,-2.676902 3.625075,-0.0033 c 3.068717,-0.0033 3.606384,0.02943 3.503272,0.210491 -0.06699,0.117602 -0.978583,1.418422 -2.025756,2.890714 l -1.903953,2.676891 -3.625077,0.0033 c -3.068716,0.0033 -3.606384,-0.02943 -3.503273,-0.210502 z" + id="path14360" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.115137" + d="m -80.440204,89.900966 v -3.731748 h 3.731745 3.731744 v 3.731748 3.731748 h -3.731744 -3.731745 z" + id="path14399" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.115137" + d="m -88.208328,89.900966 v -3.731748 h 3.731747 3.731746 v 3.731748 3.731748 h -3.731746 -3.731747 z" + id="path14438" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.115137" + d="m -96.128772,89.900966 v -3.731748 h 3.731751 3.731746 v 3.731748 3.731748 h -3.731746 -3.731751 z" + id="path14477" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.115137" + d="m -96.128772,97.821402 v -3.731737 h 3.731751 3.731746 v 3.731737 3.731748 h -3.731746 -3.731751 z" + id="path14516" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.115137" + d="m -88.208328,97.821402 v -3.731737 h 3.731747 3.731746 v 3.731737 3.731748 h -3.731746 -3.731747 z" + id="path14555" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.115137" + d="m -80.440204,97.821402 v -3.731737 h 3.731745 3.731744 v 3.731737 3.731748 h -3.731744 -3.731745 z" + id="path14594" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.115137" + d="m -80.440204,105.74185 v -3.73175 h 3.731745 3.731744 v 3.73175 3.73173 h -3.731744 -3.731745 z" + id="path14633" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.115137" + d="m -88.208328,105.74185 v -3.73175 h 3.731747 3.731746 v 3.73175 3.73173 h -3.731746 -3.731747 z" + id="path14672" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.115137" + d="m -96.128772,105.74185 v -3.73175 h 3.731751 3.731746 v 3.73175 3.73173 h -3.731746 -3.731751 z" + id="path14711" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.115137" + d="m -96.128772,113.50997 v -3.73176 h 3.731751 3.731746 v 3.73176 3.73174 h -3.731746 -3.731751 z" + id="path14750" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.115137" + d="m -88.208328,113.50997 v -3.73176 h 3.731747 3.731746 v 3.73176 3.73174 h -3.731746 -3.731747 z" + id="path14789" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.115137" + d="m -80.440204,113.50997 v -3.73176 h 3.731745 3.731744 v 3.73176 3.73174 h -3.731744 -3.731745 z" + id="path14828" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.115137" + d="m -72.519765,113.19491 v -3.49848 l 1.845051,-2.58657 c 1.014778,-1.42259 1.905828,-2.65167 1.980111,-2.73125 0.07881,-0.0845 0.135058,1.31643 0.135058,3.36384 v 3.50852 l -1.836301,2.57651 c -1.009966,1.41707 -1.901013,2.6416 -1.980109,2.72119 -0.09024,0.0908 -0.14381,-1.15878 -0.14381,-3.35376 z" + id="path14867" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.115137" + d="m -72.519765,105.36597 v -3.54385 l 1.94203,-2.730248 1.942033,-2.730242 0.04087,3.500405 0.04087,3.500415 -1.982893,2.77368 -1.982894,2.77369 z" + id="path14906" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.115137" + d="M -72.519765,97.476426 V 93.94804 l 1.980111,-2.769791 1.980109,-2.76978 v 3.547233 3.547221 l -1.850154,2.606246 c -1.017584,1.433432 -1.908633,2.671361 -1.980111,2.750941 -0.07215,0.0803 -0.129955,-1.42462 -0.129955,-3.383684 z" + id="path14945" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.115137" + d="m -72.519765,89.585909 v -3.498475 l 1.845051,-2.586568 c 1.014778,-1.422596 1.905828,-2.651672 1.980111,-2.731245 0.07882,-0.08453 0.135058,1.312207 0.135058,3.353767 v 3.498463 l -1.84505,2.586558 c -1.014779,1.422618 -1.905827,2.651673 -1.980111,2.731267 -0.07882,0.08453 -0.135059,-1.312218 -0.135059,-3.353767 z" + id="path14984" /> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.90273px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.266095" + x="-93.463249" + y="114.9286" + id="text28985"><tspan + sodipodi:role="line" + id="tspan28983" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.90273px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.266095" + x="-93.463249" + y="114.9286">1</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.90273px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.266095" + x="-85.398956" + y="114.95239" + id="text29583"><tspan + sodipodi:role="line" + id="tspan29581" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.90273px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.266095" + x="-85.398956" + y="114.95239">2</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.90273px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.266095" + x="-77.531754" + y="114.95239" + id="text30291"><tspan + sodipodi:role="line" + id="tspan30289" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.90273px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.266095" + x="-77.531754" + y="114.95239">3</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.90273px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.266095" + x="-93.463249" + y="99.005096" + id="text33579"><tspan + sodipodi:role="line" + id="tspan33577" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.90273px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.266095" + x="-93.463249" + y="99.005096">1</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.90273px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.266095" + x="-85.398956" + y="106.99065" + id="text33583"><tspan + sodipodi:role="line" + id="tspan33581" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.90273px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.266095" + x="-85.398956" + y="106.99065">2</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.90273px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.266095" + x="-77.531754" + y="106.99065" + id="text33587"><tspan + sodipodi:role="line" + id="tspan33585" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.90273px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.266095" + x="-77.531754" + y="106.99065">3</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.90273px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.266095" + x="-93.463249" + y="106.96684" + id="text33591"><tspan + sodipodi:role="line" + id="tspan33589" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.90273px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.266095" + x="-93.463249" + y="106.96684">1</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.90273px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.266095" + x="-85.398956" + y="99.028893" + id="text33595"><tspan + sodipodi:role="line" + id="tspan33593" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.90273px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.266095" + x="-85.398956" + y="99.028893">2</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.90273px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.266095" + x="-77.531754" + y="99.028893" + id="text33599"><tspan + sodipodi:role="line" + id="tspan33597" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.90273px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.266095" + x="-77.531754" + y="99.028893">3</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.90273px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.266095" + x="-93.463249" + y="91.04332" + id="text33603"><tspan + sodipodi:role="line" + id="tspan33601" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.90273px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.266095" + x="-93.463249" + y="91.04332">1</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.90273px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.266095" + x="-85.398956" + y="91.067154" + id="text33607"><tspan + sodipodi:role="line" + id="tspan33605" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.90273px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.266095" + x="-85.398956" + y="91.067154">2</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.90273px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.266095" + x="-77.531754" + y="91.067154" + id="text33611"><tspan + sodipodi:role="line" + id="tspan33609" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.90273px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.266095" + x="-77.531754" + y="91.067154">3</tspan></text> + <g + id="g45824" + transform="matrix(1.005713,0,0,1.005713,99.471735,-72.501863)"> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -130.83652,165.09427 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22694" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -130.83652,157.27109 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22696" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -138.65971,165.09427 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22698" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -138.65971,157.27109 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22700" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -130.83652,172.91745 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22702" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -123.01333,172.91745 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22704" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -130.83652,180.74063 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22706" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -138.65971,180.74063 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22708" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -123.01333,165.09427 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22710" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -138.65971,172.91745 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22712" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -123.01333,157.27109 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22714" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -123.01333,180.74063 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22716" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -138.65971,157.27109 4.34622,-6.08471" + id="path22718" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -115.19014,157.44494 4.34622,-6.08471" + id="path22720" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -123.01333,157.44494 4.34622,-6.08471" + id="path22722" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -130.83652,157.44494 4.34622,-6.08471" + id="path22724" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -110.84392,151.36023 v 31.29272" + id="path22726" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -134.31349,151.18638 7.82319,0.17385 h 7.82319 7.82319" + id="path22728" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -115.19014,188.73766 c 4.34622,-6.08471 4.34622,-6.08471 4.34622,-6.08471" + id="path22730" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -115.19014,165.26812 c 4.34622,-6.08471 4.34622,-6.08471 4.34622,-6.08471" + id="path22732" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -115.19014,173.0913 c 4.34622,-6.08471 4.34622,-6.08471 4.34622,-6.08471" + id="path22734" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -115.19014,180.91448 c 4.34622,-6.08471 4.34622,-6.08471 4.34622,-6.08471" + id="path22736" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -130.83652,165.09427 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22898" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -130.83652,157.27109 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22900" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -138.65971,165.09427 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22902" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -138.65971,157.27109 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22904" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -130.83652,172.91745 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22906" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -123.01333,172.91745 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22908" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -130.83652,180.74063 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22910" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -138.65971,180.74063 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22912" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -123.01333,165.09427 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22914" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -138.65971,172.91745 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22916" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -123.01333,157.27109 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22918" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -123.01333,180.74063 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22920" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -138.65971,157.27109 4.34622,-6.08471" + id="path22922" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -115.19014,157.44494 4.34622,-6.08471" + id="path22924" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -123.01333,157.44494 4.34622,-6.08471" + id="path22926" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -130.83652,157.44494 4.34622,-6.08471" + id="path22928" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -110.84392,151.36023 v 31.29272" + id="path22930" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -134.31349,151.18638 7.82319,0.17385 h 7.82319 7.82319" + id="path22932" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -115.19014,188.73766 c 4.34622,-6.08471 4.34622,-6.08471 4.34622,-6.08471" + id="path22934" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -115.19014,165.26812 c 4.34622,-6.08471 4.34622,-6.08471 4.34622,-6.08471" + id="path22936" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -115.19014,173.0913 c 4.34622,-6.08471 4.34622,-6.08471 4.34622,-6.08471" + id="path22938" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -115.19014,180.91448 c 4.34622,-6.08471 4.34622,-6.08471 4.34622,-6.08471" + id="path22940" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -137.98301,156.72127 c 0.20248,-0.27072 1.13436,-1.56562 2.07086,-2.87757 l 1.70273,-2.38535 3.05858,0.009 c 1.68221,0.005 3.30585,0.0559 3.60807,0.11359 l 0.54949,0.10486 -1.97753,2.76398 -1.97753,2.76398 h -3.70141 -3.7014 z" + id="path15023" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -130.26306,157.08883 c 0.0278,-0.0686 0.90239,-1.32645 1.94362,-2.79531 l 1.89313,-2.67066 3.60958,-0.007 c 1.98527,-0.004 3.58687,0.0496 3.5591,0.11813 -0.0278,0.0686 -0.90239,1.32645 -1.94362,2.79532 l -1.89313,2.67066 -3.60959,0.007 c -1.98526,0.004 -3.58686,-0.0496 -3.55909,-0.11813 z" + id="path15062" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -120.55489,154.41164 1.99954,-2.80184 h 3.60814 c 2.84723,0 3.58375,0.0399 3.49249,0.18932 -0.0636,0.10412 -0.95734,1.36495 -1.98606,2.80184 l -1.87041,2.61253 h -3.62162 -3.62162 z" + id="path15101" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -122.86721,161.37839 v -3.71055 h 3.71055 3.71055 v 3.71055 3.71054 h -3.71055 -3.71055 z" + id="path15140" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -130.59121,161.37839 v -3.71055 h 3.71055 3.71055 v 3.71055 3.71054 h -3.71055 -3.71055 z" + id="path15179" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -138.46665,161.37839 v -3.71055 h 3.71055 3.71054 v 3.71055 3.71054 h -3.71054 -3.71055 z" + id="path15218" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -138.46665,169.17811 v -3.63482 h 3.71055 3.71054 v 3.63482 3.63482 h -3.71054 -3.71055 z" + id="path15257" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -130.59121,169.17811 v -3.63482 h 3.71055 3.71055 v 3.63482 3.63482 h -3.71055 -3.71055 z" + id="path15296" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -122.86721,169.17811 v -3.63482 h 3.71055 3.71055 v 3.63482 3.63482 h -3.71055 -3.71055 z" + id="path15335" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -122.86721,176.97783 v -3.71055 h 3.71055 3.71055 v 3.71055 3.71055 h -3.71055 -3.71055 z" + id="path15374" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -130.59121,176.97783 v -3.71055 h 3.71055 3.71055 v 3.71055 3.71055 h -3.71055 -3.71055 z" + id="path15413" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -138.46665,176.97783 v -3.71055 h 3.71055 3.71054 v 3.71055 3.71055 h -3.71054 -3.71055 z" + id="path15452" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -138.46665,184.85328 v -3.71055 h 3.71055 3.71054 v 3.71055 3.71055 h -3.71054 -3.71055 z" + id="path15491" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -130.59121,184.85328 v -3.71055 h 3.71055 3.71055 v 3.71055 3.71055 h -3.71055 -3.71055 z" + id="path15530" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -122.86721,184.85328 v -3.71055 h 3.71055 3.71055 v 3.71055 3.71055 h -3.71055 -3.71055 z" + id="path15569" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -114.99176,184.53043 v -3.48817 l 1.931,-2.71 1.93099,-2.70999 0.0408,3.4588 0.0408,3.45881 -1.84253,2.59548 c -1.01339,1.42752 -1.90067,2.66023 -1.97174,2.73936 -0.0717,0.0799 -0.12922,-1.40749 -0.12922,-3.34429 z" + id="path15645" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -114.99176,176.68413 v -3.45903 l 1.83964,-2.59144 c 1.01181,-1.42528 1.89779,-2.65618 1.96886,-2.73531 0.0717,-0.0799 0.12922,1.40769 0.12922,3.34472 v 3.4886 l -1.82587,2.56186 c -1.00423,1.40903 -1.89021,2.62661 -1.96886,2.70575 -0.0897,0.0903 -0.14299,-1.14492 -0.14299,-3.31515 z" + id="path15684" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -114.99176,168.88852 v -3.5153 l 1.931,-2.7021 1.93099,-2.70211 0.0406,3.45947 0.0406,3.45948 -1.97163,2.75793 -1.97163,2.75793 z" + id="path15723" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -114.99176,161.03537 v -3.50834 l 1.96886,-2.75406 1.96886,-2.75405 v 3.54665 3.54665 l -1.83457,2.57187 c -1.00901,1.41452 -1.895,2.63661 -1.96886,2.71574 -0.0784,0.084 -0.13429,-1.31721 -0.13429,-3.36446 z" + id="path15918" /> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-135.50368" + y="162.51427" + id="text42017"><tspan + sodipodi:role="line" + id="tspan42015" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-135.50368" + y="162.51427">1</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-136.49561" + y="170.43268" + id="text42021"><tspan + sodipodi:role="line" + id="tspan42019" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-136.49561" + y="170.43268">11</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-136.37231" + y="178.3511" + id="text42025"><tspan + sodipodi:role="line" + id="tspan42023" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-136.37231" + y="178.3511">21</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-136.3895" + y="186.26857" + id="text42029"><tspan + id="tspan42027" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-136.3895" + y="186.26857" + sodipodi:role="line">31</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-129.09538" + y="170.45638" + id="text35953"><tspan + sodipodi:role="line" + id="tspan35951" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-129.09538" + y="170.45638">12 </tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-128.79399" + y="178.37479" + id="text36807"><tspan + sodipodi:role="line" + id="tspan36805" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-128.79399" + y="178.37479">22</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-128.81117" + y="186.26857" + id="text37913"><tspan + sodipodi:role="line" + id="tspan37911" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-128.81117" + y="186.26857">32</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-128.03241" + y="162.53795" + id="text42033"><tspan + sodipodi:role="line" + id="tspan42031" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-128.03241" + y="162.53795">2</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-121.76529" + y="170.45638" + id="text36473"><tspan + sodipodi:role="line" + id="tspan36471" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-121.76529" + y="170.45638">13</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-121.642" + y="178.37479" + id="text37579"><tspan + sodipodi:role="line" + id="tspan37577" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-121.642" + y="178.37479">23</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-121.65918" + y="186.26857" + id="text38469"><tspan + sodipodi:role="line" + id="tspan38467" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-121.65918" + y="186.26857">33</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-120.75713" + y="162.53795" + id="text42037"><tspan + sodipodi:role="line" + id="tspan42035" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-120.75713" + y="162.53795">3</tspan></text> + </g> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;font-size:6.08596px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Bold Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.285279" + x="-105.52939" + y="99.007248" + id="text42921"><tspan + sodipodi:role="line" + id="tspan42919" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;font-size:6.08596px;font-family:Arial;-inkscape-font-specification:'Arial, Bold Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.285279" + x="-105.52939" + y="99.007248">+</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;font-size:6.08596px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Bold Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.285279" + x="-51.477856" + y="98.843964" + id="text43309"><tspan + sodipodi:role="line" + id="tspan43307" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;font-size:6.08596px;font-family:Arial;-inkscape-font-specification:'Arial, Bold Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.285279" + x="-51.477856" + y="98.843964">=</tspan></text> + <text + xml:space="preserve" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:4.56447px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.206978" + x="-129.02751" + y="75.677513" + id="text47037"><tspan + sodipodi:role="line" + id="tspan47035" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:4.56447px;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.206978" + x="-129.02751" + y="75.677513"><tspan + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.56447px;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.206978" + id="tspan52971">a </tspan>(4 x 3)</tspan></text> + <text + xml:space="preserve" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:4.56447px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.285279" + x="-84.158951" + y="75.75811" + id="text48337"><tspan + sodipodi:role="line" + id="tspan48335" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:4.56447px;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.285279" + x="-84.158951" + y="75.75811"><tspan + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.56447px;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.285279" + id="tspan57051">b</tspan> (3)</tspan></text> + <text + xml:space="preserve" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:4.56447px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.285279" + x="-35.944736" + y="76.127975" + id="text49033"><tspan + sodipodi:role="line" + id="tspan49031" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:4.56447px;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.285279" + x="-35.944736" + y="76.127975"><tspan + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.56447px;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.285279" + id="tspan58435">result</tspan> (4 x 3)</tspan></text> + </g> +</svg> diff --git a/doc/source/user/broadcasting_3.svg b/doc/source/user/broadcasting_3.svg new file mode 100644 index 000000000000..a12030d9c1f2 --- /dev/null +++ b/doc/source/user/broadcasting_3.svg @@ -0,0 +1,708 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + width="124.43386mm" + height="91.324844mm" + viewBox="0 0 124.43386 91.324844" + version="1.1" + id="svg60162" + inkscape:version="1.1 (c68e22c387, 2021-05-23)" + sodipodi:docname="theory.broadcasting_3.svg" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <sodipodi:namedview + id="namedview60164" + pagecolor="#005d00" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageshadow="2" + inkscape:pageopacity="0" + inkscape:pagecheckerboard="0" + inkscape:document-units="mm" + showgrid="false" + showguides="true" + inkscape:guide-bbox="true" + fit-margin-top="5" + fit-margin-left="5" + fit-margin-bottom="5" + fit-margin-right="5" + inkscape:zoom="0.99674932" + inkscape:cx="-75.746227" + inkscape:cy="366.692" + inkscape:window-width="1920" + inkscape:window-height="1001" + inkscape:window-x="-9" + inkscape:window-y="-9" + inkscape:window-maximized="1" + inkscape:current-layer="g69631" /> + <defs + id="defs60159"> + <marker + style="overflow:visible" + id="Arrow2Lstart" + refX="0" + refY="0" + orient="auto" + inkscape:stockid="Arrow2Lstart" + inkscape:isstock="true"> + <path + transform="matrix(1.1,0,0,1.1,1.1,0)" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + style="fill:context-stroke;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + id="path78024" /> + </marker> + <marker + style="overflow:visible" + id="Arrow2Lend" + refX="0" + refY="0" + orient="auto" + inkscape:stockid="Arrow2Lend" + inkscape:isstock="true"> + <path + transform="matrix(-1.1,0,0,-1.1,-1.1,0)" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + style="fill:context-stroke;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + id="path78027" /> + </marker> + <marker + style="overflow:visible" + id="Arrow1Lend" + refX="0" + refY="0" + orient="auto" + inkscape:stockid="Arrow1Lend" + inkscape:isstock="true"> + <path + transform="matrix(-0.8,0,0,-0.8,-10,0)" + style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt" + d="M 0,0 5,-5 -12.5,0 5,5 Z" + id="path96534" /> + </marker> + </defs> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(-32.1299,-4.5183083)"> + <g + id="g46002" + transform="matrix(1.2203364,0,0,1.2203364,104.81983,-147.93224)"> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -47.512773,165.23404 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path21691" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -47.512773,157.41086 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path21910" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -55.335963,165.23404 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path21912" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -55.335963,157.41086 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path21914" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -47.512773,173.05722 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path21916" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -39.689583,173.05722 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path21918" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -47.512773,180.8804 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path21920" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -55.335963,180.8804 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path21922" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -39.689583,165.23404 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path21924" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -55.335963,173.05722 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path21926" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -39.689583,157.41086 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path21928" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -39.689583,180.8804 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path21930" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -55.335963,157.41086 4.34622,-6.08471" + id="path22112" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M -31.866393,157.58471 -27.520173,151.5" + id="path22147" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M -39.689583,157.58471 -35.343363,151.5" + id="path22149" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M -47.512773,157.58471 -43.166553,151.5" + id="path22151" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -27.520173,151.5 v 31.29272" + id="path22266" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -50.989743,151.32615 7.82319,0.17385 h 7.82319 7.82319" + id="path22381" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -31.866393,188.87743 c 4.34622,-6.08471 4.34622,-6.08471 4.34622,-6.08471" + id="path22496" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -31.866393,165.40789 c 4.34622,-6.08471 4.34622,-6.08471 4.34622,-6.08471" + id="path22644" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -31.866393,173.23107 c 4.34622,-6.08471 4.34622,-6.08471 4.34622,-6.08471" + id="path22646" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -31.866393,181.05425 c 4.34622,-6.08471 4.34622,-6.08471 4.34622,-6.08471" + id="path22648" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -47.512773,165.23404 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22650" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -47.512773,157.41086 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22652" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -55.335963,165.23404 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22654" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -55.335963,157.41086 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22656" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -47.512773,173.05722 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22658" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -39.689583,173.05722 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22660" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -47.512773,180.8804 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22662" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -55.335963,180.8804 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22664" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -39.689583,165.23404 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22666" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -55.335963,173.05722 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22668" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -39.689583,157.41086 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22670" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -39.689583,180.8804 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22672" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -55.335963,157.41086 4.34622,-6.08471" + id="path22674" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M -31.866393,157.58471 -27.520173,151.5" + id="path22676" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M -39.689583,157.58471 -35.343363,151.5" + id="path22678" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M -47.512773,157.58471 -43.166553,151.5" + id="path22680" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -27.520173,151.5 v 31.29272" + id="path22682" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -50.989743,151.32615 7.82319,0.17385 h 7.82319 7.82319" + id="path22684" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -31.866393,188.87743 c 4.34622,-6.08471 4.34622,-6.08471 4.34622,-6.08471" + id="path22686" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -31.866393,165.40789 c 4.34622,-6.08471 4.34622,-6.08471 4.34622,-6.08471" + id="path22688" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -31.866393,173.23107 c 4.34622,-6.08471 4.34622,-6.08471 4.34622,-6.08471" + id="path22690" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -31.866393,181.05425 c 4.34622,-6.08471 4.34622,-6.08471 4.34622,-6.08471" + id="path22692" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -55.142483,161.45304 v -3.71055 h 3.71055 3.71054 v 3.71055 3.71055 h -3.71054 -3.71055 z" + id="path13541" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -47.267033,161.45304 v -3.71055 h 3.71054 3.71055 v 3.71055 3.71055 h -3.71055 -3.71054 z" + id="path13580" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -39.543043,161.45304 v -3.71055 h 3.71055 3.71055 v 3.71055 3.71055 h -3.71055 -3.71055 z" + id="path13619" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -39.543043,169.32849 v -3.71055 h 3.71055 3.71055 v 3.71055 3.71054 h -3.71055 -3.71055 z" + id="path13658" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -47.267033,169.32849 v -3.71055 h 3.71054 3.71055 v 3.71055 3.71054 h -3.71055 -3.71054 z" + id="path13697" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -55.142483,169.32849 v -3.71055 h 3.71055 3.71054 v 3.71055 3.71054 h -3.71054 -3.71055 z" + id="path13736" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -55.142483,177.20393 v -3.71054 h 3.71055 3.71054 v 3.71054 3.71055 h -3.71054 -3.71055 z" + id="path13775" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -47.267033,177.20393 v -3.71054 h 3.71054 3.71055 v 3.71054 3.71055 h -3.71055 -3.71054 z" + id="path13814" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -39.543043,177.20393 v -3.71054 h 3.71055 3.71055 v 3.71054 3.71055 h -3.71055 -3.71055 z" + id="path13853" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -39.543043,184.92793 v -3.71055 h 3.71055 3.71055 v 3.71055 3.71055 h -3.71055 -3.71055 z" + id="path13892" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -47.267033,184.92793 v -3.71055 h 3.71054 3.71055 v 3.71055 3.71055 h -3.71055 -3.71054 z" + id="path13931" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -55.142483,184.92793 v -3.71055 h 3.71055 3.71054 v 3.71055 3.71055 h -3.71054 -3.71055 z" + id="path13970" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -54.899273,157.25299 c 0.0622,-0.10561 0.9991,-1.43582 2.08211,-2.95601 l 1.96911,-2.76398 1.44974,0.019 c 0.79736,0.0105 2.42287,0.0616 3.61225,0.11359 l 2.1625,0.0945 -2.01181,2.80185 -2.0118,2.80184 -3.68255,0.0406 c -2.94083,0.0324 -3.65979,0.002 -3.56955,-0.15145 z" + id="path14009" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -46.783393,156.94737 c 0.20247,-0.27071 1.13436,-1.56562 2.07086,-2.87756 l 1.70272,-2.38535 h 3.62228 3.62228 l -2.05577,2.87756 -2.05578,2.87757 h -3.63737 -3.63736 z" + id="path14048" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -39.134073,157.23722 c 0.0666,-0.11131 0.97302,-1.40328 2.01425,-2.87104 l 1.89313,-2.66867 3.60958,-0.007 c 1.98527,-0.004 3.58686,0.0493 3.5591,0.11756 -0.0278,0.0683 -0.93452,1.36316 -2.015,2.87757 l -1.96451,2.75347 h -3.60883 c -2.97073,0 -3.58742,-0.0358 -3.48772,-0.20237 z" + id="path14087" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -31.667593,161.15934 v -3.45903 l 1.83964,-2.59144 c 1.01181,-1.42529 1.89779,-2.65618 1.96886,-2.73531 0.0718,-0.0799 0.12922,1.40769 0.12922,3.34472 v 3.4886 l -1.82587,2.56186 c -1.00422,1.40903 -1.89021,2.62661 -1.96886,2.70574 -0.0897,0.0903 -0.14299,-1.14491 -0.14299,-3.31514 z" + id="path14126" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -31.667593,169.01521 v -3.47859 l 1.83457,-2.57187 c 1.00901,-1.41453 1.895,-2.63661 1.96886,-2.71575 0.0784,-0.084 0.13429,1.30477 0.13429,3.33472 v 3.4786 l -1.83457,2.57187 c -1.00901,1.41452 -1.895,2.63661 -1.96886,2.71574 -0.0784,0.084 -0.13429,-1.30476 -0.13429,-3.33472 z" + id="path14165" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -31.667593,176.85105 v -3.49847 l 1.96886,-2.75406 1.96886,-2.75406 v 3.51758 3.51757 l -1.69335,2.37377 c -0.93135,1.30557 -1.81734,2.5363 -1.96886,2.73496 -0.26885,0.35246 -0.27551,0.27663 -0.27551,-3.13729 z" + id="path14204" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -31.667593,184.63423 v -3.45903 l 1.83964,-2.59143 c 1.01181,-1.4253 1.89779,-2.65619 1.96886,-2.73532 0.0718,-0.0799 0.12922,1.40768 0.12922,3.34472 v 3.4886 l -1.82587,2.56186 c -1.00422,1.40903 -1.89021,2.62661 -1.96886,2.70575 -0.0897,0.0903 -0.14299,-1.14492 -0.14299,-3.31515 z" + id="path14243" /> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-36.608471" + y="162.74039" + id="text17645"><tspan + sodipodi:role="line" + id="tspan17643" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-36.608471" + y="162.74039">0</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-37.600399" + y="170.6588" + id="text21689"><tspan + sodipodi:role="line" + id="tspan21687" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-37.600399" + y="170.6588">10</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-37.477116" + y="178.57722" + id="text22617"><tspan + sodipodi:role="line" + id="tspan22615" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-37.477116" + y="178.57722">20</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-37.494289" + y="186.49469" + id="text23721"><tspan + sodipodi:role="line" + id="tspan23719" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-37.494289" + y="186.49469">30</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-44.447037" + y="162.74039" + id="text25685"><tspan + sodipodi:role="line" + id="tspan25683" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-44.447037" + y="162.74039">0</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-45.438965" + y="170.6588" + id="text25689"><tspan + sodipodi:role="line" + id="tspan25687" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-45.438965" + y="170.6588">10</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-45.315681" + y="178.57722" + id="text25693"><tspan + sodipodi:role="line" + id="tspan25691" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-45.315681" + y="178.57722">20</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-45.332859" + y="186.49469" + id="text25697"><tspan + sodipodi:role="line" + id="tspan25695" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-45.332859" + y="186.49469">30</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-52.285603" + y="162.74039" + id="text25701"><tspan + sodipodi:role="line" + id="tspan25699" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-52.285603" + y="162.74039">0</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-53.277534" + y="170.6588" + id="text25705"><tspan + sodipodi:role="line" + id="tspan25703" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-53.277534" + y="170.6588">10</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-53.154247" + y="178.57722" + id="text25709"><tspan + sodipodi:role="line" + id="tspan25707" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-53.154247" + y="178.57722">20</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-53.171425" + y="186.49469" + id="text25713"><tspan + sodipodi:role="line" + id="tspan25711" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-53.171425" + y="186.49469">30</tspan></text> + </g> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;font-size:7.38479px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Bold Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.346162" + x="84.700111" + y="61.040264" + id="text42921"><tspan + sodipodi:role="line" + id="tspan42919" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;font-size:7.38479px;font-family:Arial;-inkscape-font-specification:'Arial, Bold Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.346162" + x="84.700111" + y="61.040264">+</tspan></text> + <text + xml:space="preserve" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.53859px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.251149" + x="46.541679" + y="32.299259" + id="text47037"><tspan + sodipodi:role="line" + id="tspan47035" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.53859px;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.251149" + x="46.541679" + y="32.299259"><tspan + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.53859px;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.251149" + id="tspan52971">a </tspan>(4 x 3)</tspan></text> + <g + id="g69631" + transform="matrix(0.79500238,0,0,0.79500238,35.940237,-68.109727)"> + <path + style="fill:none;stroke:#000000;stroke-width:0.406141px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 96.995629,141.48877 v 12.27561 H 109.0044 V 141.75563 H 96.995629 Z" + id="path22696" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.406141px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 84.986869,141.48877 v 12.27561 h 12.00876 v -12.00875 h -12.00876 z" + id="path22700" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.406141px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 109.0044,141.48877 v 12.27561 h 12.00876 V 141.75563 H 109.0044 Z" + id="path22714" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.406141px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 84.986869,141.48877 6.67154,-9.34016" + id="path22718" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.406141px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 121.01316,141.75563 6.67154,-9.34016" + id="path22720" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.406141px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 109.0044,141.75563 6.67154,-9.34016" + id="path22722" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.406141px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 96.995629,141.75563 6.671541,-9.34016" + id="path22724" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.406141px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 91.658409,132.14861 12.008761,0.26686 h 12.00877 12.00876" + id="path22728" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.406141px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 96.995629,141.48877 v 12.27561 H 109.0044 V 141.75563 H 96.995629 Z" + id="path22900" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.406141px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 84.986869,141.48877 v 12.27561 h 12.00876 v -12.00875 h -12.00876 z" + id="path22904" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.406141px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 109.0044,141.48877 v 12.27561 h 12.00876 V 141.75563 H 109.0044 Z" + id="path22918" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.406141px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 84.986869,141.48877 6.67154,-9.34016" + id="path22922" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.406141px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 121.01316,141.75563 6.67154,-9.34016" + id="path22924" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.406141px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 109.0044,141.75563 6.67154,-9.34016" + id="path22926" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.406141px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 96.995629,141.75563 6.671541,-9.34016" + id="path22928" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.175734" + d="m 86.025619,140.64478 c 0.31081,-0.41556 1.74126,-2.40326 3.17881,-4.41713 l 2.61373,-3.66156 4.69498,0.0138 c 2.58223,0.008 5.074551,0.0858 5.538471,0.17437 l 0.84348,0.16096 -3.035551,4.24277 -3.03555,4.24277 h -5.68175 -5.68173 z" + id="path15023" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.175734" + d="m 97.875909,141.209 c 0.0427,-0.10531 1.38518,-2.03613 2.983491,-4.29086 l 2.906,-4.09953 5.54078,-0.0107 c 3.04743,-0.006 5.50592,0.0761 5.4633,0.18133 -0.0427,0.1053 -1.38519,2.03613 -2.9835,4.29088 l -2.906,4.09952 -5.5408,0.0107 c -3.04741,0.006 -5.505901,-0.0761 -5.463271,-0.18133 z" + id="path15062" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.175734" + d="m 109.22869,147.79356 v -5.69577 h 5.69578 5.69577 v 5.69577 5.69576 h -5.69577 -5.69578 z" + id="path15140" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.175734" + d="m 97.372189,147.79356 v -5.69577 h 5.695771 5.69578 v 5.69577 5.69576 h -5.69578 -5.695771 z" + id="path15179" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.175734" + d="m 85.283219,147.79356 v -5.69577 h 5.69577 5.69576 v 5.69577 5.69576 h -5.69576 -5.69577 z" + id="path15218" /> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.95674px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.406141" + x="89.831451" + y="149.49193" + id="text42017"><tspan + sodipodi:role="line" + id="tspan42015" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.95674px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.406141" + x="89.831451" + y="149.49193">1</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.95674px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.406141" + x="102.09368" + y="149.52827" + id="text42033"><tspan + sodipodi:role="line" + id="tspan42031" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.95674px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.406141" + x="102.09368" + y="149.52827">2</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.95674px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.406141" + x="114.05506" + y="149.52827" + id="text42037"><tspan + sodipodi:role="line" + id="tspan42035" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.95674px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.406141" + x="114.05506" + y="149.52827">3</tspan></text> + <path + style="fill:none;stroke:#000000;stroke-width:0.406141px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 121.01316,141.48877 v 12.27561 h 12.00876 v -12.00875 h -12.00876 z" + id="path63486" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.406141px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 133.02192,141.75563 6.67154,-9.34016" + id="path63490" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.406;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 127.6847,132.41547 h 12.00876" + id="path64980" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.406;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 133.02192,153.76438 6.67154,-9.34016" + id="path65130" /> + <path + style="fill:#ffffff;stroke:#000000;stroke-width:0.406;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 139.69346,132.41547 v 12.00875" + id="path65358" /> + <path + style="fill:#ffffff;stroke:none;stroke-width:0.662837;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 414.24937,534.52703 c 0,-0.1315 5.26846,-7.6152 11.70769,-16.63044 l 11.70768,-16.39136 h 21.37323 c 11.75528,0 21.30844,0.16273 21.22924,0.36162 -0.0792,0.19889 -5.37721,7.68259 -11.77337,16.63045 l -11.62938,16.26883 h -21.30755 c -11.71915,0 -21.30754,-0.10759 -21.30754,-0.2391 z" + id="path65514" + transform="scale(0.26458333)" /> + <path + style="fill:#ffffff;stroke:none;stroke-width:0.662837;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 459.67713,534.40451 c 0.0792,-0.19889 5.37721,-7.68259 11.77338,-16.63045 l 11.62938,-16.26883 h 21.30754 c 11.71915,0 21.30755,0.1076 21.30755,0.2391 0,0.1315 -5.26846,7.6152 -11.70769,16.63045 l -11.70769,16.39135 h -21.37322 c -11.75528,0 -21.30844,-0.16273 -21.22925,-0.36162 z" + id="path65553" + transform="scale(0.26458333)" /> + <path + style="fill:#ffffff;stroke:none;stroke-width:0.662837;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 458.30926,558.52392 v -21.59799 h 21.81397 21.81396 v 21.59799 21.59798 h -21.81396 -21.81397 z" + id="path65592" + transform="scale(0.26458333)" /> + <path + style="fill:#ffffff;stroke:none;stroke-width:0.662837;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 503.66503,557.16612 v -20.96876 l 11.66291,-16.33384 11.66292,-16.33384 v 21.04447 21.04447 l -11.29511,15.84777 c -6.21231,8.71627 -11.46062,16.03244 -11.66291,16.25814 -0.20302,0.2265 -0.36781,-8.98423 -0.36781,-20.55841 z" + id="path65631" + transform="scale(0.26458333)" /> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.95672px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="125.8052" + y="149.49046" + id="text66434"><tspan + sodipodi:role="line" + id="tspan66432" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.95672px;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="125.8052" + y="149.49046">4</tspan></text> + </g> + <text + xml:space="preserve" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.53859px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.210344" + x="122.50475" + y="32.375103" + id="text72815"><tspan + sodipodi:role="line" + id="tspan72813" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.53859px;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.210344" + x="122.50475" + y="32.375103"><tspan + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.53859px;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.210344" + id="tspan76229">b</tspan> (4)</tspan></text> + <text + xml:space="preserve" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.53859px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.210344" + x="86.699333" + y="15.341067" + id="text74733"><tspan + sodipodi:role="line" + id="tspan74731" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.53859px;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.210344" + x="86.699333" + y="15.341067">mismatch</tspan></text> + <path + style="fill:none;stroke:#000000;stroke-width:0.322771;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="M 37.765217,27.055961 151.48494,90.702321" + id="path82219" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.322771;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="M 37.581798,90.702321 150.93468,27.055961" + id="path82340" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 61.594429,19.914188 h 68.219681 l 0,5.308925" + id="path1246" /> + </g> +</svg> diff --git a/doc/source/user/broadcasting_4.svg b/doc/source/user/broadcasting_4.svg new file mode 100644 index 000000000000..40f946613612 --- /dev/null +++ b/doc/source/user/broadcasting_4.svg @@ -0,0 +1,1330 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + width="150.64636mm" + height="69.054741mm" + viewBox="0 0 150.64636 69.054741" + version="1.1" + id="svg83310" + inkscape:version="1.1 (c68e22c387, 2021-05-23)" + sodipodi:docname="theory.broadcasting_4.svg" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <sodipodi:namedview + id="namedview83312" + pagecolor="#005200" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageshadow="2" + inkscape:pageopacity="0" + inkscape:pagecheckerboard="0" + inkscape:document-units="mm" + showgrid="false" + showguides="true" + inkscape:guide-bbox="true" + fit-margin-top="5" + lock-margins="true" + fit-margin-left="5" + fit-margin-right="5" + fit-margin-bottom="5" + inkscape:zoom="1.2432572" + inkscape:cx="197.46517" + inkscape:cy="157.24823" + inkscape:window-width="1920" + inkscape:window-height="1001" + inkscape:window-x="-9" + inkscape:window-y="-9" + inkscape:window-maximized="1" + inkscape:current-layer="layer1" /> + <defs + id="defs83307"> + <marker + style="overflow:visible" + id="Arrow1Lend" + refX="0" + refY="0" + orient="auto" + inkscape:stockid="Arrow1Lend" + inkscape:isstock="true"> + <path + transform="matrix(-0.8,0,0,-0.8,-10,0)" + style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt" + d="M 0,0 5,-5 -12.5,0 5,5 Z" + id="path96534" /> + </marker> + </defs> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(-35.165196,-11.993335)"> + <g + id="g91916" + transform="matrix(0.77839728,0,0,0.77839728,27.454002,-34.500382)"> + <text + xml:space="preserve" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:6.56009px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.164003" + x="92.805412" + y="-122.62473" + id="text93193" + transform="rotate(90)"><tspan + sodipodi:role="line" + id="tspan93191" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:6.56009px;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.164003" + x="92.805412" + y="-122.62473">stretch</tspan></text> + <path + style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.338293;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow1Lend)" + d="M 121.01938,88.877548 V 115.1978" + id="path96373" /> + </g> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 49.087539,40.613975 v 8.968943 h 8.773973 v -8.773964 h -8.773973 z" + id="path21691" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 49.087539,31.840013 v 8.968941 h 8.773973 v -8.773962 h -8.773973 z" + id="path21910" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 40.313566,40.613975 v 8.968943 h 8.773973 v -8.773964 h -8.773973 z" + id="path21912" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 40.313566,31.840013 v 8.968941 h 8.773973 v -8.773962 h -8.773973 z" + id="path21914" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 49.087539,49.387937 v 8.968942 h 8.773973 v -8.773961 h -8.773973 z" + id="path21916" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 57.861512,49.387937 v 8.968942 h 8.773973 v -8.773961 h -8.773973 z" + id="path21918" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 49.087539,58.161899 v 8.96894 h 8.773973 v -8.77396 h -8.773973 z" + id="path21920" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 40.313566,58.161899 v 8.96894 h 8.773973 v -8.77396 h -8.773973 z" + id="path21922" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 57.861512,40.613975 v 8.968943 h 8.773973 v -8.773964 h -8.773973 z" + id="path21924" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 40.313566,49.387937 v 8.968942 h 8.773973 v -8.773961 h -8.773973 z" + id="path21926" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 57.861512,31.840013 v 8.968941 h 8.773973 v -8.773962 h -8.773973 z" + id="path21928" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 57.861512,58.161899 v 8.96894 h 8.773973 v -8.77396 h -8.773973 z" + id="path21930" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 40.313566,31.840013 4.874433,-6.824209" + id="path22112" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 66.635485,32.034992 4.874434,-6.824209" + id="path22147" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 57.861512,32.034992 4.874433,-6.824209" + id="path22149" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 49.087539,31.840013 4.874434,-6.824209" + id="path22151" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 71.509919,25.210783 V 60.306629" + id="path22266" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 66.635485,67.130839 c 4.874434,-6.82421 4.874434,-6.82421 4.874434,-6.82421" + id="path22496" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 66.635485,40.808954 c 4.874434,-6.824209 4.874434,-6.824209 4.874434,-6.824209" + id="path22644" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 66.635485,49.582918 c 4.874434,-6.824212 4.874434,-6.824212 4.874434,-6.824212" + id="path22646" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 66.635485,58.356879 c 4.874434,-6.82421 4.874434,-6.82421 4.874434,-6.82421" + id="path22648" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 49.087539,40.613975 v 8.968943 h 8.773973 v -8.773964 h -8.773973 z" + id="path22650" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 49.087539,31.840013 v 8.968941 h 8.773973 v -8.773962 h -8.773973 z" + id="path22652" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 40.313566,40.613975 v 8.968943 h 8.773973 v -8.773964 h -8.773973 z" + id="path22654" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 40.313566,31.840013 v 8.968941 h 8.773973 v -8.773962 h -8.773973 z" + id="path22656" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 49.087539,49.387937 v 8.968942 h 8.773973 v -8.773961 h -8.773973 z" + id="path22658" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 57.861512,49.387937 v 8.968942 h 8.773973 v -8.773961 h -8.773973 z" + id="path22660" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 49.087539,58.161899 v 8.96894 h 8.773973 v -8.77396 h -8.773973 z" + id="path22662" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 40.313566,58.161899 v 8.96894 h 8.773973 v -8.77396 h -8.773973 z" + id="path22664" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 57.861512,40.613975 v 8.968943 h 8.773973 v -8.773964 h -8.773973 z" + id="path22666" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 40.313566,49.387937 v 8.968942 h 8.773973 v -8.773961 h -8.773973 z" + id="path22668" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 57.861512,31.840013 v 8.968941 h 8.773973 v -8.773962 h -8.773973 z" + id="path22670" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 57.861512,58.161899 v 8.96894 h 8.773973 v -8.77396 h -8.773973 z" + id="path22672" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 40.313566,31.840013 4.874433,-6.824209" + id="path22674" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 66.635485,32.034992 4.874434,-6.824209" + id="path22676" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 57.861512,32.034992 4.874433,-6.824209" + id="path22678" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 71.509919,25.210783 V 60.306629" + id="path22682" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 66.635485,67.130839 c 4.874434,-6.82421 4.874434,-6.82421 4.874434,-6.82421" + id="path22686" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 66.635485,40.808954 c 4.874434,-6.824209 4.874434,-6.824209 4.874434,-6.824209" + id="path22688" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 66.635485,49.582918 c 4.874434,-6.824212 4.874434,-6.824212 4.874434,-6.824212" + id="path22690" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296739px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 66.635485,58.356879 c 4.874434,-6.82421 4.874434,-6.82421 4.874434,-6.82421" + id="path22692" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.128397" + d="m 40.53056,36.373455 v -4.161508 h 4.161508 4.161496 v 4.161508 4.161507 H 44.692068 40.53056 Z" + id="path13541" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.128397" + d="m 40.53056,45.206036 v -4.161504 h 4.161508 4.161496 v 4.161504 4.161499 H 44.692068 40.53056 Z" + id="path13736" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.128397" + d="m 40.53056,54.038609 v -4.161496 h 4.161508 4.161496 v 4.161496 4.16151 H 44.692068 40.53056 Z" + id="path13775" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.128397;stroke-opacity:1" + d="m 58.025862,54.038609 v -4.161496 h 4.161508 4.161507 v 4.161496 4.16151 H 62.18737 58.025862 Z" + id="path13853" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.128397;stroke-opacity:1" + d="m 58.025862,62.701339 v -4.16151 h 4.161508 4.161507 v 4.16151 4.16151 H 62.18737 58.025862 Z" + id="path13892" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.128397" + d="m 40.53056,62.701339 v -4.16151 h 4.161508 4.161496 v 4.16151 4.16151 H 44.692068 40.53056 Z" + id="path13970" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 95.383653,40.798804 v 8.969014 h 8.774037 v -8.774041 h -8.774037 z" + id="path22942" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 95.383653,32.024772 v 8.969005 h 8.774037 v -8.774024 h -8.774037 z" + id="path22944" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 86.60962,40.798804 v 8.969014 h 8.774033 V 40.993777 H 86.60962 Z" + id="path22946" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 86.60962,32.024772 v 8.969005 h 8.774033 V 32.219753 H 86.60962 Z" + id="path22948" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 95.383653,49.57283 v 8.968999 h 8.774037 v -8.774011 h -8.774037 z" + id="path22950" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 104.15769,49.57283 v 8.968999 h 8.77405 v -8.774011 h -8.77405 z" + id="path22952" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 95.383653,58.346849 v 8.96902 h 8.774037 v -8.77404 h -8.774037 z" + id="path22954" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 86.60962,58.346849 v 8.96902 h 8.774033 v -8.77404 H 86.60962 Z" + id="path22956" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 104.15769,40.798804 v 8.969014 h 8.77405 v -8.774041 h -8.77405 z" + id="path22958" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 86.60962,49.57283 v 8.968999 h 8.774033 V 49.767818 H 86.60962 Z" + id="path22960" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 104.15769,32.024772 v 8.969005 h 8.77405 v -8.774024 h -8.77405 z" + id="path22962" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 104.15769,58.346849 v 8.96902 h 8.77405 v -8.77404 h -8.77405 z" + id="path22964" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 86.60962,32.024772 4.874473,-6.824256" + id="path22966" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 112.93174,32.219753 4.87446,-6.824256" + id="path22968" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 104.15769,32.219753 4.87447,-6.824256" + id="path22970" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 95.383653,32.219753 4.874477,-6.824256" + id="path22972" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 91.484093,25.200516 8.774037,0.194981 h 8.77403 8.77404" + id="path22976" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 112.93174,67.315869 c 4.87446,-6.82427 4.87446,-6.82427 4.87446,-6.82427" + id="path22978" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 112.93174,40.993777 c 4.87446,-6.824256 4.87446,-6.824256 4.87446,-6.824256" + id="path22980" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 112.93174,49.767818 c 4.87446,-6.824273 4.87446,-6.824273 4.87446,-6.824273" + id="path22982" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 112.93174,58.541829 c 4.87446,-6.82426 4.87446,-6.82426 4.87446,-6.82426" + id="path22984" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 95.383653,40.798804 v 8.969014 h 8.774037 v -8.774041 h -8.774037 z" + id="path22986" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 95.383653,32.024772 v 8.969005 h 8.774037 v -8.774024 h -8.774037 z" + id="path22988" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 86.60962,40.798804 v 8.969014 h 8.774033 V 40.993777 H 86.60962 Z" + id="path22990" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 86.60962,32.024772 v 8.969005 h 8.774033 V 32.219753 H 86.60962 Z" + id="path22992" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 95.383653,49.57283 v 8.968999 h 8.774037 v -8.774011 h -8.774037 z" + id="path22994" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 104.15769,49.57283 v 8.968999 h 8.77405 v -8.774011 h -8.77405 z" + id="path22996" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 95.383653,58.346849 v 8.96902 h 8.774037 v -8.77404 h -8.774037 z" + id="path22998" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 86.60962,58.346849 v 8.96902 h 8.774033 v -8.77404 H 86.60962 Z" + id="path23000" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 104.15769,40.798804 v 8.969014 h 8.77405 v -8.774041 h -8.77405 z" + id="path23002" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 86.60962,49.57283 v 8.968999 h 8.774033 V 49.767818 H 86.60962 Z" + id="path23004" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 104.15769,32.024772 v 8.969005 h 8.77405 v -8.774024 h -8.77405 z" + id="path23006" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 104.15769,58.346849 v 8.96902 h 8.77405 v -8.77404 h -8.77405 z" + id="path23008" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 86.60962,32.024772 4.874473,-6.824256" + id="path23010" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 112.93174,32.219753 4.87446,-6.824256" + id="path23012" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 104.15769,32.219753 4.87447,-6.824256" + id="path23014" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 95.383653,32.219753 4.874477,-6.824256" + id="path23016" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 91.484093,25.200516 8.774037,0.194981 h 8.77403 8.77404" + id="path23020" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 112.93174,67.315869 c 4.87446,-6.82427 4.87446,-6.82427 4.87446,-6.82427" + id="path23022" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 112.93174,40.993777 c 4.87446,-6.824256 4.87446,-6.824256 4.87446,-6.824256" + id="path23024" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 112.93174,49.767818 c 4.87446,-6.824273 4.87446,-6.824273 4.87446,-6.824273" + id="path23026" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 112.93174,58.541829 c 4.87446,-6.82426 4.87446,-6.82426 4.87446,-6.82426" + id="path23028" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 117.8062,25.395497 v 8.774024" + id="path25266" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.296741px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 117.8062,60.491599 v -8.77403 -8.774024 -8.774024" + id="path25301" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.128398" + d="m 87.021112,31.984202 c 0,-0.04476 1.039683,-1.536572 2.310393,-3.315264 l 2.310408,-3.233968 1.638806,0.02125 c 0.901361,0.01253 2.722951,0.0692 4.047985,0.1274 l 2.409163,0.106111 -2.246797,3.14239 -2.246789,3.142374 -4.11158,0.04546 c -2.261377,0.02506 -4.111589,0.0088 -4.111589,-0.03581 z" + id="path14282" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.128398" + d="m 96.518385,31.082756 c 0.377468,-0.537164 1.399379,-1.97035 2.270904,-3.184844 l 1.584601,-2.208157 4.0247,-0.04554 c 2.21359,-0.02506 4.02471,-7.78e-4 4.02471,0.05278 0,0.05402 -1.00725,1.507771 -2.23833,3.230372 l -2.23833,3.131998 h -4.057281 -4.057287 z" + id="path14321" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.128398" + d="m 104.80537,31.821027 c 0.0747,-0.131137 1.09129,-1.581774 2.25907,-3.223624 l 2.12323,-2.985208 4.04258,-0.0039 c 3.42215,-0.0039 4.02173,0.03277 3.90674,0.234726 -0.0747,0.131152 -1.09128,1.581789 -2.25906,3.223647 l -2.12323,2.9852 -4.04258,0.0039 c -3.42215,0.0039 -4.02174,-0.03277 -3.90675,-0.234757 z" + id="path14360" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.128398" + d="m 104.3467,36.560703 v -4.16153 h 4.16152 4.16155 v 4.16153 4.161546 h -4.16155 -4.16152 z" + id="path14399" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.128398" + d="m 95.683904,36.560703 v -4.16153 h 4.161546 4.16153 v 4.16153 4.161546 h -4.16153 -4.161546 z" + id="path14438" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.128398" + d="m 86.851254,36.560703 v -4.16153 h 4.161543 4.161537 v 4.16153 4.161546 h -4.161537 -4.161543 z" + id="path14477" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.128398" + d="m 86.851254,45.393358 v -4.161539 h 4.161543 4.161537 v 4.161539 4.16153 h -4.161537 -4.161543 z" + id="path14516" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.128398" + d="m 95.683904,45.393358 v -4.161539 h 4.161546 4.16153 v 4.161539 4.16153 h -4.16153 -4.161546 z" + id="path14555" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.128398" + d="m 104.3467,45.393358 v -4.161539 h 4.16152 4.16155 v 4.161539 4.16153 h -4.16155 -4.16152 z" + id="path14594" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.128398" + d="m 104.3467,54.225999 v -4.161526 h 4.16152 4.16155 v 4.161526 4.16153 h -4.16155 -4.16152 z" + id="path14633" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.128398" + d="m 95.683904,54.225999 v -4.161526 h 4.161546 4.16153 v 4.161526 4.16153 h -4.16153 -4.161546 z" + id="path14672" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.128398" + d="m 86.851254,54.225999 v -4.161526 h 4.161543 4.161537 v 4.161526 4.16153 h -4.161537 -4.161543 z" + id="path14711" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.128398" + d="m 86.851254,62.888789 v -4.16155 h 4.161543 4.161537 v 4.16155 4.16153 h -4.161537 -4.161543 z" + id="path14750" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.128398" + d="m 95.683904,62.888789 v -4.16155 h 4.161546 4.16153 v 4.16155 4.16153 h -4.16153 -4.161546 z" + id="path14789" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.128398" + d="m 104.3467,62.888789 v -4.16155 h 4.16152 4.16155 v 4.16155 4.16153 h -4.16155 -4.16152 z" + id="path14828" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.128398" + d="m 113.17935,62.537449 v -3.90141 l 2.05754,-2.88447 c 1.13165,-1.58643 2.12532,-2.95706 2.20816,-3.04581 0.0878,-0.0942 0.15061,1.46804 0.15061,3.75126 v 3.9126 l -2.04778,2.87324 c -1.12629,1.58029 -2.11996,2.94585 -2.20816,3.0346 -0.10062,0.10124 -0.16037,-1.29223 -0.16037,-3.74001 z" + id="path14867" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.128398" + d="m 113.17935,53.806829 v -3.951978 l 2.16569,-3.044709 2.16569,-3.044685 0.0455,3.903553 0.0455,3.903559 -2.21128,3.09312 -2.21126,3.09315 z" + id="path14906" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.128398" + d="M 113.17935,45.008643 V 41.07389 l 2.20815,-3.08879 2.20816,-3.088781 v 3.955776 3.95576 l -2.06323,2.906405 c -1.13478,1.598524 -2.12846,2.979027 -2.20817,3.067772 -0.0805,0.08959 -0.14491,-1.588693 -0.14491,-3.773389 z" + id="path14945" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.128398" + d="m 113.17935,36.209366 v -3.901405 l 2.05754,-2.88446 c 1.13165,-1.586444 2.12532,-2.957077 2.20816,-3.045807 0.0878,-0.09422 0.15061,1.463333 0.15061,3.740013 v 3.901397 l -2.05754,2.88446 c -1.13166,1.586451 -2.12532,2.957061 -2.20816,3.04583 -0.0879,0.09422 -0.15061,-1.463356 -0.15061,-3.740028 z" + id="path14984" /> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:4.35221px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.296741" + x="89.823761" + y="64.470802" + id="text28985"><tspan + sodipodi:role="line" + id="tspan28983" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:4.35221px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.296741" + x="89.823761" + y="64.470802">1</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:4.35221px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.296741" + x="98.816841" + y="64.49736" + id="text29583"><tspan + sodipodi:role="line" + id="tspan29581" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:4.35221px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.296741" + x="98.816841" + y="64.49736">2</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:4.35221px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.296741" + x="107.59012" + y="64.49736" + id="text30291"><tspan + sodipodi:role="line" + id="tspan30289" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:4.35221px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.296741" + x="107.59012" + y="64.49736">3</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:4.35221px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.296741" + x="89.823761" + y="46.71336" + id="text33579"><tspan + sodipodi:role="line" + id="tspan33577" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:4.35221px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.296741" + x="89.823761" + y="46.71336">1</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:4.35221px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.296741" + x="98.816841" + y="55.61861" + id="text33583"><tspan + sodipodi:role="line" + id="tspan33581" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:4.35221px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.296741" + x="98.816841" + y="55.61861">2</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:4.35221px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.296741" + x="107.59012" + y="55.61861" + id="text33587"><tspan + sodipodi:role="line" + id="tspan33585" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:4.35221px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.296741" + x="107.59012" + y="55.61861">3</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:4.35221px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.296741" + x="89.823761" + y="55.592068" + id="text33591"><tspan + sodipodi:role="line" + id="tspan33589" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:4.35221px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.296741" + x="89.823761" + y="55.592068">1</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:4.35221px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.296741" + x="98.816841" + y="46.739918" + id="text33595"><tspan + sodipodi:role="line" + id="tspan33593" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:4.35221px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.296741" + x="98.816841" + y="46.739918">2</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:4.35221px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.296741" + x="107.59012" + y="46.739918" + id="text33599"><tspan + sodipodi:role="line" + id="tspan33597" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:4.35221px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.296741" + x="107.59012" + y="46.739918">3</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:4.35221px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.296741" + x="89.823761" + y="37.834614" + id="text33603"><tspan + sodipodi:role="line" + id="tspan33601" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:4.35221px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.296741" + x="89.823761" + y="37.834614">1</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:4.35221px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.296741" + x="98.816841" + y="37.861195" + id="text33607"><tspan + sodipodi:role="line" + id="tspan33605" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:4.35221px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.296741" + x="98.816841" + y="37.861195">2</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:4.35221px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.296741" + x="107.59012" + y="37.861195" + id="text33611"><tspan + sodipodi:role="line" + id="tspan33609" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:4.35221px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.296741" + x="107.59012" + y="37.861195">3</tspan></text> + <g + id="g45824" + transform="matrix(1.1215424,0,0,1.1215424,304.97935,-144.54629)"> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -130.83652,165.09427 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22694" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -130.83652,157.27109 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22696" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -138.65971,165.09427 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22698" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -138.65971,157.27109 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22700" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -130.83652,172.91745 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22702" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -123.01333,172.91745 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22704" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -130.83652,180.74063 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22706" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -138.65971,180.74063 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22708" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -123.01333,165.09427 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22710" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -138.65971,172.91745 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22712" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -123.01333,157.27109 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22714" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -123.01333,180.74063 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22716" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -138.65971,157.27109 4.34622,-6.08471" + id="path22718" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -115.19014,157.44494 4.34622,-6.08471" + id="path22720" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -123.01333,157.44494 4.34622,-6.08471" + id="path22722" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -130.83652,157.44494 4.34622,-6.08471" + id="path22724" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -110.84392,151.36023 v 31.29272" + id="path22726" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -134.31349,151.18638 7.82319,0.17385 h 7.82319 7.82319" + id="path22728" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -115.19014,188.73766 c 4.34622,-6.08471 4.34622,-6.08471 4.34622,-6.08471" + id="path22730" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -115.19014,165.26812 c 4.34622,-6.08471 4.34622,-6.08471 4.34622,-6.08471" + id="path22732" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -115.19014,173.0913 c 4.34622,-6.08471 4.34622,-6.08471 4.34622,-6.08471" + id="path22734" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -115.19014,180.91448 c 4.34622,-6.08471 4.34622,-6.08471 4.34622,-6.08471" + id="path22736" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -130.83652,165.09427 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22898" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -130.83652,157.27109 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22900" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -138.65971,165.09427 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22902" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -138.65971,157.27109 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22904" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -130.83652,172.91745 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22906" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -123.01333,172.91745 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22908" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -130.83652,180.74063 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22910" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -138.65971,180.74063 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22912" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -123.01333,165.09427 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22914" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -138.65971,172.91745 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22916" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -123.01333,157.27109 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22918" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -123.01333,180.74063 v 7.99703 h 7.82319 v -7.82318 h -7.82319 z" + id="path22920" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -138.65971,157.27109 4.34622,-6.08471" + id="path22922" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -115.19014,157.44494 4.34622,-6.08471" + id="path22924" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -123.01333,157.44494 4.34622,-6.08471" + id="path22926" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -130.83652,157.44494 4.34622,-6.08471" + id="path22928" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -110.84392,151.36023 v 31.29272" + id="path22930" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -134.31349,151.18638 7.82319,0.17385 h 7.82319 7.82319" + id="path22932" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -115.19014,188.73766 c 4.34622,-6.08471 4.34622,-6.08471 4.34622,-6.08471" + id="path22934" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -115.19014,165.26812 c 4.34622,-6.08471 4.34622,-6.08471 4.34622,-6.08471" + id="path22936" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -115.19014,173.0913 c 4.34622,-6.08471 4.34622,-6.08471 4.34622,-6.08471" + id="path22938" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -115.19014,180.91448 c 4.34622,-6.08471 4.34622,-6.08471 4.34622,-6.08471" + id="path22940" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -137.98301,156.72127 c 0.20248,-0.27072 1.13436,-1.56562 2.07086,-2.87757 l 1.70273,-2.38535 3.05858,0.009 c 1.68221,0.005 3.30585,0.0559 3.60807,0.11359 l 0.54949,0.10486 -1.97753,2.76398 -1.97753,2.76398 h -3.70141 -3.7014 z" + id="path15023" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -130.26306,157.08883 c 0.0278,-0.0686 0.90239,-1.32645 1.94362,-2.79531 l 1.89313,-2.67066 3.60958,-0.007 c 1.98527,-0.004 3.58687,0.0496 3.5591,0.11813 -0.0278,0.0686 -0.90239,1.32645 -1.94362,2.79532 l -1.89313,2.67066 -3.60959,0.007 c -1.98526,0.004 -3.58686,-0.0496 -3.55909,-0.11813 z" + id="path15062" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -120.55489,154.41164 1.99954,-2.80184 h 3.60814 c 2.84723,0 3.58375,0.0399 3.49249,0.18932 -0.0636,0.10412 -0.95734,1.36495 -1.98606,2.80184 l -1.87041,2.61253 h -3.62162 -3.62162 z" + id="path15101" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -122.86721,161.37839 v -3.71055 h 3.71055 3.71055 v 3.71055 3.71054 h -3.71055 -3.71055 z" + id="path15140" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -130.59121,161.37839 v -3.71055 h 3.71055 3.71055 v 3.71055 3.71054 h -3.71055 -3.71055 z" + id="path15179" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -138.46665,161.37839 v -3.71055 h 3.71055 3.71054 v 3.71055 3.71054 h -3.71054 -3.71055 z" + id="path15218" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -138.46665,169.17811 v -3.63482 h 3.71055 3.71054 v 3.63482 3.63482 h -3.71054 -3.71055 z" + id="path15257" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -130.59121,169.17811 v -3.63482 h 3.71055 3.71055 v 3.63482 3.63482 h -3.71055 -3.71055 z" + id="path15296" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -122.86721,169.17811 v -3.63482 h 3.71055 3.71055 v 3.63482 3.63482 h -3.71055 -3.71055 z" + id="path15335" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -122.86721,176.97783 v -3.71055 h 3.71055 3.71055 v 3.71055 3.71055 h -3.71055 -3.71055 z" + id="path15374" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -130.59121,176.97783 v -3.71055 h 3.71055 3.71055 v 3.71055 3.71055 h -3.71055 -3.71055 z" + id="path15413" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -138.46665,176.97783 v -3.71055 h 3.71055 3.71054 v 3.71055 3.71055 h -3.71054 -3.71055 z" + id="path15452" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -138.46665,184.85328 v -3.71055 h 3.71055 3.71054 v 3.71055 3.71055 h -3.71054 -3.71055 z" + id="path15491" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -130.59121,184.85328 v -3.71055 h 3.71055 3.71055 v 3.71055 3.71055 h -3.71055 -3.71055 z" + id="path15530" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -122.86721,184.85328 v -3.71055 h 3.71055 3.71055 v 3.71055 3.71055 h -3.71055 -3.71055 z" + id="path15569" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -114.99176,184.53043 v -3.48817 l 1.931,-2.71 1.93099,-2.70999 0.0408,3.4588 0.0408,3.45881 -1.84253,2.59548 c -1.01339,1.42752 -1.90067,2.66023 -1.97174,2.73936 -0.0717,0.0799 -0.12922,-1.40749 -0.12922,-3.34429 z" + id="path15645" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -114.99176,176.68413 v -3.45903 l 1.83964,-2.59144 c 1.01181,-1.42528 1.89779,-2.65618 1.96886,-2.73531 0.0717,-0.0799 0.12922,1.40769 0.12922,3.34472 v 3.4886 l -1.82587,2.56186 c -1.00423,1.40903 -1.89021,2.62661 -1.96886,2.70575 -0.0897,0.0903 -0.14299,-1.14492 -0.14299,-3.31515 z" + id="path15684" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -114.99176,168.88852 v -3.5153 l 1.931,-2.7021 1.93099,-2.70211 0.0406,3.45947 0.0406,3.45948 -1.97163,2.75793 -1.97163,2.75793 z" + id="path15723" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.114483" + d="m -114.99176,161.03537 v -3.50834 l 1.96886,-2.75406 1.96886,-2.75405 v 3.54665 3.54665 l -1.83457,2.57187 c -1.00901,1.41452 -1.895,2.63661 -1.96886,2.71574 -0.0784,0.084 -0.13429,-1.31721 -0.13429,-3.36446 z" + id="path15918" /> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-135.50368" + y="162.51427" + id="text42017"><tspan + sodipodi:role="line" + id="tspan42015" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-135.50368" + y="162.51427">1</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-136.49561" + y="170.43268" + id="text42021"><tspan + sodipodi:role="line" + id="tspan42019" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-136.49561" + y="170.43268">11</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-136.37231" + y="178.3511" + id="text42025"><tspan + sodipodi:role="line" + id="tspan42023" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-136.37231" + y="178.3511">21</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-136.3895" + y="186.26857" + id="text42029"><tspan + id="tspan42027" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-136.3895" + y="186.26857" + sodipodi:role="line">31</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-129.09538" + y="170.45638" + id="text35953"><tspan + sodipodi:role="line" + id="tspan35951" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-129.09538" + y="170.45638">12 </tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-128.79399" + y="178.37479" + id="text36807"><tspan + sodipodi:role="line" + id="tspan36805" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-128.79399" + y="178.37479">22</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-128.81117" + y="186.26857" + id="text37913"><tspan + sodipodi:role="line" + id="tspan37911" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-128.81117" + y="186.26857">32</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-128.03241" + y="162.53795" + id="text42033"><tspan + sodipodi:role="line" + id="tspan42031" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-128.03241" + y="162.53795">2</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-121.76529" + y="170.45638" + id="text36473"><tspan + sodipodi:role="line" + id="tspan36471" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-121.76529" + y="170.45638">13</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-121.642" + y="178.37479" + id="text37579"><tspan + sodipodi:role="line" + id="tspan37577" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-121.642" + y="178.37479">23</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-121.65918" + y="186.26857" + id="text38469"><tspan + sodipodi:role="line" + id="tspan38467" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-121.65918" + y="186.26857">33</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-120.75713" + y="162.53795" + id="text42037"><tspan + sodipodi:role="line" + id="tspan42035" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:3.88056px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583" + x="-120.75713" + y="162.53795">3</tspan></text> + </g> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;font-size:6.78689px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Bold Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.318136" + x="76.367958" + y="46.715771" + id="text42921"><tspan + sodipodi:role="line" + id="tspan42919" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;font-size:6.78689px;font-family:Arial;-inkscape-font-specification:'Arial, Bold Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.318136" + x="76.367958" + y="46.715771">+</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;font-size:6.78689px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Bold Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.318136" + x="136.64468" + y="46.533672" + id="text43309"><tspan + sodipodi:role="line" + id="tspan43307" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;font-size:6.78689px;font-family:Arial;-inkscape-font-specification:'Arial, Bold Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.318136" + x="136.64468" + y="46.533672">=</tspan></text> + <text + xml:space="preserve" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.09017px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.230815" + x="50.163528" + y="20.699118" + id="text47037"><tspan + sodipodi:role="line" + id="tspan47035" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.09017px;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.230815" + x="50.163528" + y="20.699118"><tspan + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.09017px;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.230815" + id="tspan52971">a </tspan>(4 x 1)</tspan></text> + <text + xml:space="preserve" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.09017px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.318136" + x="100.19966" + y="20.788992" + id="text48337"><tspan + sodipodi:role="line" + id="tspan48335" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.09017px;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.318136" + x="100.19966" + y="20.788992"><tspan + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.09017px;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.318136" + id="tspan57051">b</tspan> (3)</tspan></text> + <text + xml:space="preserve" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.09017px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.318136" + x="153.96677" + y="21.201468" + id="text49033"><tspan + sodipodi:role="line" + id="tspan49031" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.09017px;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.318136" + x="153.96677" + y="21.201468"><tspan + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.09017px;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.318136" + id="tspan58435">result</tspan> (4 x 3)</tspan></text> + <path + style="fill:none;stroke:#000000;stroke-width:0.205951px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 45.187999,25.015804 c 8.773974,-2e-6 8.773974,-2e-6 8.773974,-2e-6" + id="path89538" /> + <path + style="fill:none;stroke:#c2c0c0;stroke-width:0.205951px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 71.509918,25.210782 h -8.773973 l -8.773972,-0.19498" + id="path89540" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.254195;stroke-opacity:1" + d="m 41.237019,30.974494 c 0.308962,-0.432773 1.372792,-1.923662 2.364068,-3.313087 l 1.802319,-2.52623 h 4.107724 4.107722 l -2.356307,3.308862 -2.356308,3.308859 -4.115483,0.0042 -4.115484,0.0042 z" + id="path90170" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.254195;stroke-opacity:1" + d="m 49.486028,31.719487 c 0,-0.02302 1.035081,-1.491126 2.300178,-3.262446 l 2.300178,-3.220583 4.036101,0.04651 c 2.219854,0.02558 4.062159,0.07234 4.09401,0.103902 0.03185,0.03157 -0.959707,1.47874 -2.203461,3.21594 l -2.261368,3.158544 h -4.13282 c -2.27305,0 -4.132818,-0.01884 -4.132818,-0.04186 z" + id="path90209" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.254195;stroke-opacity:1" + d="m 58.547372,31.540001 c 0.07286,-0.121744 1.064278,-1.534877 2.203152,-3.140293 l 2.070678,-2.918941 4.095305,-0.0071 4.095306,-0.0071 -2.24857,3.147433 -2.248571,3.147431 h -4.049887 c -3.334947,0 -4.026499,-0.03907 -3.917413,-0.22135 z" + id="path90248" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.254195;stroke-opacity:1" + d="m 49.320374,36.399673 v -4.141359 h 4.141358 4.141359 v 4.141359 4.141358 h -4.141359 -4.141358 z" + id="path90287" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.254195;stroke-opacity:1" + d="m 58.100054,36.399673 v -4.141359 h 4.141359 4.141356 v 4.141359 4.141358 h -4.141356 -4.141359 z" + id="path90326" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.254195;stroke-opacity:1" + d="m 49.320374,45.179353 v -4.141358 h 4.141358 4.141359 v 4.141358 4.141359 h -4.141359 -4.141358 z" + id="path90365" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.254195;stroke-opacity:1" + d="m 58.100054,45.179353 v -4.141358 h 4.141359 4.141356 v 4.141358 4.141359 h -4.141356 -4.141359 z" + id="path90404" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.254195;stroke-opacity:1" + d="m 66.879733,36.045756 v -3.898917 l 2.19492,-3.077617 2.19492,-3.077618 0.04443,3.887202 0.04443,3.887203 -2.09267,2.931961 c -1.150967,1.612579 -2.158675,3.002779 -2.23935,3.089334 -0.08544,0.09167 -0.146684,-1.470387 -0.146684,-3.741548 z" + id="path90443" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.254195;stroke-opacity:1" + d="m 66.879733,44.805915 v -3.918442 l 2.19492,-3.075862 2.19492,-3.075865 0.04443,3.904972 0.04443,3.904972 -2.09267,2.93196 c -1.150967,1.61258 -2.158675,3.00278 -2.23935,3.089333 -0.08544,0.09167 -0.146684,-1.478578 -0.146684,-3.761068 z" + id="path90482" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.254195;stroke-opacity:1" + d="m 66.879733,53.585599 v -3.918445 l 2.19492,-3.075865 2.19492,-3.075863 0.04443,3.904972 0.04443,3.904971 -2.09267,2.93196 c -1.150967,1.61258 -2.158675,3.00278 -2.23935,3.08933 -0.08544,0.0917 -0.146684,-1.47857 -0.146684,-3.76106 z" + id="path90521" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.254195;stroke-opacity:1" + d="m 66.879733,62.365269 v -3.91843 l 2.19492,-3.07587 2.19492,-3.07586 0.04443,3.90497 0.04443,3.90497 -2.09267,2.93196 c -1.150967,1.61258 -2.158675,3.00278 -2.23935,3.08933 -0.08544,0.0917 -0.146684,-1.47857 -0.146684,-3.76107 z" + id="path90560" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.254195;stroke-opacity:1" + d="m 49.320374,62.738709 v -4.14136 h 4.141358 4.141359 v 4.14136 4.14136 h -4.141359 -4.141358 z" + id="path90599" /> + <path + style="fill:#ffffff;stroke:#c2c0c0;stroke-width:0.254195;stroke-opacity:1" + d="m 49.320374,53.959029 v -4.141356 h 4.141358 4.141359 v 4.141356 4.14136 h -4.141359 -4.141358 z" + id="path90638" /> + <g + id="g86032" + transform="matrix(0.7308395,0,0,0.7308395,29.446816,-29.293766)"> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.59121px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.381218" + x="43.503597" + y="92.905823" + id="text17645"><tspan + sodipodi:role="line" + id="tspan17643" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.59121px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.381218" + x="43.503597" + y="92.905823">0</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.59121px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.381218" + x="42.074402" + y="104.31487" + id="text21689"><tspan + sodipodi:role="line" + id="tspan21687" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.59121px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.381218" + x="42.074402" + y="104.31487">10</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.59121px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.381218" + x="42.252033" + y="115.72393" + id="text22617"><tspan + sodipodi:role="line" + id="tspan22615" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.59121px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.381218" + x="42.252033" + y="115.72393">20</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.59121px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.381218" + x="42.227287" + y="127.13161" + id="text23721"><tspan + sodipodi:role="line" + id="tspan23719" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.59121px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.381218" + x="42.227287" + y="127.13161">30</tspan></text> + </g> + <g + id="g86042" + transform="matrix(0.7308395,0,0,0.7308395,29.12217,-29.444835)"> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.59121px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.381218" + x="32.209599" + y="92.905823" + id="text25685"><tspan + sodipodi:role="line" + id="tspan25683" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.59121px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.381218" + x="32.209599" + y="92.905823">0</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.59121px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.381218" + x="30.780405" + y="104.31487" + id="text25689"><tspan + sodipodi:role="line" + id="tspan25687" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.59121px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.381218" + x="30.780405" + y="104.31487">10</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.59121px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.381218" + x="30.958035" + y="115.72393" + id="text25693"><tspan + sodipodi:role="line" + id="tspan25691" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.59121px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.381218" + x="30.958035" + y="115.72393">20</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.59121px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.381218" + x="30.933285" + y="127.13161" + id="text25697"><tspan + sodipodi:role="line" + id="tspan25695" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.59121px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#999999;stroke-width:0.381218" + x="30.933285" + y="127.13161">30</tspan></text> + </g> + <g + id="g90765" + transform="matrix(0.7308395,0,0,0.7308395,28.395655,-29.444835)"> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.59121px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.381218" + x="20.9156" + y="92.905823" + id="text25701"><tspan + sodipodi:role="line" + id="tspan25699" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.59121px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.381218" + x="20.9156" + y="92.905823">0</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.59121px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.381218" + x="19.486401" + y="104.31487" + id="text25705"><tspan + sodipodi:role="line" + id="tspan25703" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.59121px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.381218" + x="19.486401" + y="104.31487">10</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.59121px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.381218" + x="19.664036" + y="115.72393" + id="text25709"><tspan + sodipodi:role="line" + id="tspan25707" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.59121px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.381218" + x="19.664036" + y="115.72393">20</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.59121px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.381218" + x="19.639286" + y="127.13161" + id="text25713"><tspan + sodipodi:role="line" + id="tspan25711" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:5.59121px;font-family:Arial;-inkscape-font-specification:'Arial, Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.381218" + x="19.639286" + y="127.13161">30</tspan></text> + </g> + <g + id="g91924" + transform="matrix(0,-0.77839728,0.77839728,0,-25.887022,168.912)"> + <text + xml:space="preserve" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:6.56009px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.164003" + x="92.805412" + y="-122.62473" + id="text91920" + transform="rotate(90)"><tspan + sodipodi:role="line" + id="tspan91918" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:6.56009px;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.164003" + x="92.805412" + y="-122.62473">stretch</tspan></text> + <path + style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.338293;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow1Lend)" + d="M 121.01938,88.877548 V 115.1978" + id="path91922" /> + </g> + </g> +</svg> diff --git a/doc/source/user/broadcasting_5.svg b/doc/source/user/broadcasting_5.svg new file mode 100644 index 000000000000..7d372e909842 --- /dev/null +++ b/doc/source/user/broadcasting_5.svg @@ -0,0 +1,344 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + width="215.56395mm" + height="140.59219mm" + viewBox="0 0 215.56395 140.59219" + version="1.1" + id="svg92220" + inkscape:version="1.1 (c68e22c387, 2021-05-23)" + sodipodi:docname="broadcasting-figure-5.svg" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <sodipodi:namedview + id="namedview92222" + pagecolor="#005400" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageshadow="2" + inkscape:pageopacity="0" + inkscape:pagecheckerboard="0" + inkscape:document-units="mm" + showgrid="false" + showguides="true" + inkscape:guide-bbox="true" + inkscape:zoom="1.1209083" + inkscape:cx="592.8228" + inkscape:cy="219.91094" + inkscape:window-width="1920" + inkscape:window-height="1001" + inkscape:window-x="-9" + inkscape:window-y="-9" + inkscape:window-maximized="1" + inkscape:current-layer="g112380" + fit-margin-top="5" + lock-margins="true" + fit-margin-left="5" + fit-margin-right="5" + fit-margin-bottom="5" /> + <defs + id="defs92217"> + <rect + x="-230.06293" + y="897.24542" + width="189.16286" + height="182.77222" + id="rect97145" /> + </defs> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(1.916979,2.2534185)"> + <text + xml:space="preserve" + transform="scale(0.26458333)" + id="text97143" + style="font-style:italic;font-stretch:condensed;font-size:39.9999px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';white-space:pre;shape-inside:url(#rect97145)" /> + <g + style="fill:none;stroke:none;stroke-linecap:square;stroke-miterlimit:10" + id="g112380" + transform="matrix(0.34260659,0,0,0.35200053,2.6364747,2.279811)"> + <path + fill="#ffffff" + d="M 1.3033791,1.3260505 H 601.30337 V 372.32605 H 1.3033791 Z" + fill-rule="nonzero" + id="path112290" /> + <path + stroke="#cccccc" + stroke-width="1" + stroke-linecap="butt" + d="m 77.5,61.5 v 239" + fill-rule="nonzero" + id="path112292" /> + <path + stroke="#cccccc" + stroke-width="1" + stroke-linecap="butt" + d="m 203.5,61.5 v 239" + fill-rule="nonzero" + id="path112294" /> + <path + stroke="#cccccc" + stroke-width="1" + stroke-linecap="butt" + d="m 329.5,61.5 v 239" + fill-rule="nonzero" + id="path112296" /> + <path + stroke="#cccccc" + stroke-width="1" + stroke-linecap="butt" + d="m 455.5,61.5 v 239" + fill-rule="nonzero" + id="path112298" /> + <path + stroke="#cccccc" + stroke-width="1" + stroke-linecap="butt" + d="m 581.5,61.5 v 239" + fill-rule="nonzero" + id="path112300" /> + <path + stroke="#cccccc" + stroke-width="1" + stroke-linecap="butt" + d="m 77.5,300.5 h 504" + fill-rule="nonzero" + id="path112302" /> + <path + stroke="#cccccc" + stroke-width="1" + stroke-linecap="butt" + d="m 77.5,220.5 h 504" + fill-rule="nonzero" + id="path112304" /> + <path + stroke="#cccccc" + stroke-width="1" + stroke-linecap="butt" + d="m 77.5,141.5 h 504" + fill-rule="nonzero" + id="path112306" /> + <path + stroke="#cccccc" + stroke-width="1" + stroke-linecap="butt" + d="m 77.5,61.5 h 504" + fill-rule="nonzero" + id="path112308" /> + <path + fill="#4285f4" + clip-path="url(#id_0)" + d="m 394.468,100.082 c 0,2.48528 -2.01472,4.5 -4.5,4.5 -2.48527,0 -4.5,-2.01472 -4.5,-4.5 0,-2.48528 2.01473,-4.5 4.5,-4.5 2.48528,0 4.5,2.01472 4.5,4.5 z" + fill-rule="nonzero" + id="path112313" + style="fill:#000000" /> + <path + fill="#4285f4" + clip-path="url(#id_0)" + d="m 545.638,131.88644 c 0,2.48528 -2.0147,4.5 -4.5,4.5 -2.4853,0 -4.5,-2.01472 -4.5,-4.5 0,-2.48527 2.0147,-4.5 4.5,-4.5 2.4853,0 4.5,2.01473 4.5,4.5 z" + fill-rule="nonzero" + id="path112315" + style="fill:#000000" /> + <path + fill="#4285f4" + clip-path="url(#id_0)" + d="m 107.245,252.74333 c 0,2.48527 -2.01472,4.50002 -4.5,4.50002 -2.48528,0 -4.5,-2.01475 -4.5,-4.50002 0,-2.48527 2.01472,-4.5 4.5,-4.5 2.48528,0 4.5,2.01473 4.5,4.5 z" + fill-rule="nonzero" + id="path112317" + style="fill:#000000" /> + <path + fill="#4285f4" + clip-path="url(#id_0)" + d="m 167.713,195.49533 c 0,2.48529 -2.01471,4.5 -4.5,4.5 -2.48528,0 -4.5,-2.01471 -4.5,-4.5 0,-2.48527 2.01472,-4.5 4.5,-4.5 2.48529,0 4.5,2.01473 4.5,4.5 z" + fill-rule="nonzero" + id="path112319" + style="fill:#000000" /> + <path + fill="#000000" + d="m 312.92188,349.45 -2.28126,-8.59374 h 1.17188 l 1.3125,5.64064 q 0.20312,0.875 0.35938,1.75 0.32812,-1.375 0.39062,-1.59376 l 1.625,-5.79688 h 1.375 l 1.23438,4.34374 q 0.45312,1.625 0.67187,3.0469 0.15625,-0.8125 0.42187,-1.875 l 1.34376,-5.51564 h 1.14062 l -2.34375,8.59374 h -1.10937 l -1.79688,-6.54686 q -0.23438,-0.8125 -0.28125,-1 -0.125,0.57812 -0.25,1 l -1.8125,6.54686 z m 13.625,-2 1.09374,0.125 q -0.25,0.95314 -0.95312,1.4844 -0.70312,0.53124 -1.78125,0.53124 -1.35937,0 -2.17187,-0.84374 -0.79688,-0.84376 -0.79688,-2.3594 0,-1.5625 0.8125,-2.42186 0.8125,-0.875 2.09375,-0.875 1.25,0 2.03125,0.84376 0.79688,0.84374 0.79688,2.3906 0,0.0938 0,0.28126 h -4.64063 q 0.0625,1.03124 0.57813,1.57814 0.51562,0.53124 1.29687,0.53124 0.57813,0 0.98437,-0.29688 0.42188,-0.3125 0.65626,-0.96876 z m -3.45313,-1.7031 h 3.46875 q -0.0625,-0.7969 -0.39062,-1.1875 -0.51563,-0.6094 -1.3125,-0.6094 -0.73438,0 -1.23438,0.4844 -0.48438,0.48436 -0.53125,1.3125 z m 6.20313,-3.6719 v -1.21874 h 1.0625 v 1.21874 z m 0,7.375 v -6.21874 h 1.0625 V 349.45 Z m 2.79687,0.51564 1.03125,0.15626 q 0.0625,0.46874 0.35938,0.6875 0.39062,0.29686 1.0625,0.29686 0.73437,0 1.125,-0.29686 0.40624,-0.2969 0.54687,-0.8125 0.0937,-0.32814 0.0781,-1.3594 -0.6875,0.8125 -1.71876,0.8125 -1.28124,0 -1.98437,-0.92186 -0.70313,-0.9375 -0.70313,-2.21874 0,-0.89064 0.3125,-1.64064 0.32813,-0.76562 0.9375,-1.17186 0.60938,-0.40626 1.4375,-0.40626 1.10938,0 1.82813,0.89062 v -0.75 h 0.96875 v 5.375 q 0,1.45314 -0.29688,2.0625 -0.29687,0.60938 -0.9375,0.95314 -0.64062,0.35936 -1.57812,0.35936 -1.10938,0 -1.79688,-0.5 -0.6875,-0.5 -0.67187,-1.51562 z m 0.875,-3.73438 q 0,1.21874 0.48437,1.78124 0.48438,0.5625 1.21876,0.5625 0.73437,0 1.21874,-0.5625 0.5,-0.5625 0.5,-1.75 0,-1.1406 -0.51562,-1.71874 -0.5,-0.57812 -1.21875,-0.57812 -0.70313,0 -1.20313,0.57812 -0.48437,0.5625 -0.48437,1.6875 z m 6.32813,3.21874 v -8.59374 h 1.04687 v 3.07814 q 0.73437,-0.84376 1.85937,-0.84376 0.70313,0 1.20313,0.28126 0.51563,0.2656 0.73437,0.75 0.21876,0.46874 0.21876,1.3906 v 3.9375 h -1.04688 v -3.9375 q 0,-0.79686 -0.34375,-1.15624 Q 342.625,343.9969 342,343.9969 q -0.46875,0 -0.89062,0.25 -0.40626,0.23436 -0.59376,0.65624 -0.17187,0.40626 -0.17187,1.14062 V 349.45 Z m 9.29687,-0.9375 0.15625,0.9219 q -0.45312,0.0937 -0.79688,0.0937 -0.57812,0 -0.89062,-0.17188 -0.3125,-0.1875 -0.45312,-0.48436 -0.125,-0.2969 -0.125,-1.25 v -3.57814 h -0.76563 v -0.8125 h 0.76563 v -1.54686 l 1.04687,-0.625 v 2.17186 h 1.0625 v 0.8125 h -1.0625 v 3.64064 q 0,0.4531 0.0469,0.5781 0.0625,0.125 0.1875,0.20314 0.125,0.0781 0.35938,0.0781 0.1875,0 0.46875,-0.0313 z" + fill-rule="nonzero" + id="path112323" /> + <path + fill="#000000" + d="m 29.550013,204.71458 h -8.59375 v -1.125 h 3.53125 v -4.46875 h -3.53125 v -1.14062 h 8.59375 v 1.14062 h -4.046875 v 4.46875 h 4.046875 z m -2,-13.07812 0.125,-1.09375 q 0.953125,0.25 1.484375,0.95312 0.53125,0.70313 0.53125,1.78125 0,1.35938 -0.84375,2.17188 -0.84375,0.79687 -2.359375,0.79687 -1.5625,0 -2.421875,-0.8125 -0.875,-0.8125 -0.875,-2.09375 0,-1.25 0.84375,-2.03125 0.84375,-0.79687 2.390625,-0.79687 0.09375,0 0.28125,0 v 4.64062 q 1.03125,-0.0625 1.578125,-0.57812 0.53125,-0.51563 0.53125,-1.29688 0,-0.57812 -0.296875,-0.98437 -0.3125,-0.42188 -0.96875,-0.65625 z m -1.703125,3.45312 v -3.46875 q -0.796875,0.0625 -1.1875,0.39063 -0.609375,0.51562 -0.609375,1.3125 0,0.73437 0.484375,1.23437 0.484375,0.48438 1.3125,0.53125 z m -3.671875,-6.20312 h -1.21875 v -1.0625 h 1.21875 z m 7.375,0 h -6.21875 v -1.0625 h 6.21875 z m 0.515625,-2.79688 0.15625,-1.03125 q 0.46875,-0.0625 0.6875,-0.35937 0.296875,-0.39063 0.296875,-1.0625 0,-0.73438 -0.296875,-1.125 -0.296875,-0.40625 -0.8125,-0.54688 -0.328125,-0.0937 -1.359375,-0.0781 0.8125,0.6875 0.8125,1.71875 0,1.28125 -0.921875,1.98437 -0.9375,0.70313 -2.21875,0.70313 -0.890625,0 -1.640625,-0.3125 -0.765625,-0.32813 -1.171875,-0.9375 -0.40625,-0.60938 -0.40625,-1.4375 0,-1.10938 0.890625,-1.82813 h -0.75 v -0.96875 h 5.375 q 1.453125,0 2.0625,0.29688 0.609375,0.29687 0.953125,0.9375 0.359372,0.64062 0.359372,1.57812 0,1.10938 -0.499997,1.79688 -0.5,0.6875 -1.515625,0.67187 z m -3.734375,-0.875 q 1.21875,0 1.78125,-0.48437 0.5625,-0.48438 0.5625,-1.21875 0,-0.73438 -0.5625,-1.21875 -0.5625,-0.5 -1.75,-0.5 -1.140625,0 -1.71875,0.51562 -0.578125,0.5 -0.578125,1.21875 0,0.70313 0.578125,1.20313 0.5625,0.48437 1.6875,0.48437 z m 3.21875,-6.32812 h -8.59375 v -1.04688 h 3.078125 q -0.84375,-0.73437 -0.84375,-1.85937 0,-0.70313 0.28125,-1.20313 0.265625,-0.51562 0.75,-0.73437 0.46875,-0.21875 1.390625,-0.21875 h 3.9375 v 1.04687 h -3.9375 q -0.796875,0 -1.15625,0.34375 -0.359375,0.34375 -0.359375,0.96875 0,0.46875 0.25,0.89063 0.234375,0.40625 0.65625,0.59375 0.40625,0.17187 1.140625,0.17187 h 3.40625 z m -0.9375,-9.29688 0.921875,-0.15625 q 0.09375,0.45313 0.09375,0.79688 0,0.57812 -0.171875,0.89062 -0.1875,0.3125 -0.484375,0.45313 -0.296875,0.125 -1.25,0.125 h -3.578125 v 0.76562 h -0.8125 v -0.76562 h -1.546875 l -0.625,-1.04688 h 2.171875 v -1.0625 h 0.8125 v 1.0625 h 3.640625 q 0.453125,0 0.578125,-0.0469 0.125,-0.0625 0.203125,-0.1875 0.07813,-0.125 0.07813,-0.35938 0,-0.1875 -0.03125,-0.46875 z" + fill-rule="nonzero" + id="path112325" /> + <path + fill="#000000" + d="m 55.01875,304.45 h -1.046876 v -6.71874 q -0.390624,0.35938 -1.015624,0.73438 -0.609376,0.35936 -1.09375,0.53126 v -1.01564 q 0.875,-0.42186 1.53125,-1 0.671874,-0.59376 0.953124,-1.15626 h 0.671876 z m 6.40625,0 v -2.0625 h -3.71875 v -0.96874 l 3.921874,-5.5625 H 62.4875 v 5.5625 h 1.15625 v 0.96874 H 62.4875 v 2.0625 z m 0,-3.03124 v -3.85936 l -2.6875,3.85936 z m 3.625,-1.20312 q 0,-1.53124 0.3125,-2.45314 0.3125,-0.9375 0.92188,-1.4375 0.625,-0.5 1.5625,-0.5 0.6875,0 1.20312,0.28126 0.53125,0.28124 0.875,0.8125 0.34375,0.51564 0.53125,1.26564 0.1875,0.75 0.1875,2.03124 0,1.51562 -0.3125,2.4375 -0.29687,0.92186 -0.92187,1.4375 -0.60938,0.5 -1.5625,0.5 -1.23438,0 -1.95313,-0.89064 Q 65.05,302.6375 65.05,300.21564 Z m 1.07813,0 q 0,2.10936 0.5,2.8125 0.5,0.70312 1.21875,0.70312 0.73437,0 1.21875,-0.70312 0.5,-0.70314 0.5,-2.8125 0,-2.125 -0.5,-2.8125 Q 68.58125,296.7 67.83125,296.7 q -0.71875,0 -1.15625,0.6094 -0.54687,0.78124 -0.54687,2.90624 z" + fill-rule="nonzero" + id="path112327" /> + <path + fill="#000000" + d="m 55.01875,224.93889 h -1.046876 v -6.71875 q -0.390624,0.35937 -1.015624,0.73437 -0.609376,0.35938 -1.09375,0.53125 v -1.01562 q 0.875,-0.42188 1.53125,-1 0.671874,-0.59375 0.953124,-1.15625 h 0.671876 z m 8.5,-6.48438 -1.046876,0.0781 q -0.140624,-0.625 -0.390624,-0.90625 -0.4375,-0.45313 -1.0625,-0.45313 -0.5,0 -0.890626,0.28125 -0.484374,0.375 -0.78125,1.07813 -0.28125,0.6875 -0.296874,1.96875 0.375,-0.57813 0.921874,-0.85938 0.5625,-0.28125 1.171876,-0.28125 1.046874,0 1.78125,0.78125 0.75,0.78125 0.75,2 0,0.8125 -0.359376,1.51563 -0.34375,0.6875 -0.953124,1.0625 -0.609376,0.35937 -1.375,0.35937 -1.328126,0 -2.15625,-0.96875 -0.828126,-0.96875 -0.828126,-3.1875 0,-2.5 0.921876,-3.625 0.796874,-0.98437 2.15625,-0.98437 1.015624,0 1.65625,0.57812 0.65625,0.5625 0.78125,1.5625 z m -4.296876,3.70313 q 0,0.54687 0.21875,1.04687 0.234376,0.48438 0.640626,0.75 0.421874,0.26563 0.890624,0.26563 0.65625,0 1.140626,-0.53125 0.484374,-0.54688 0.484374,-1.46875 0,-0.89063 -0.484374,-1.40625 -0.46875,-0.51563 -1.1875,-0.51563 -0.71875,0 -1.21875,0.51563 -0.484376,0.51562 -0.484376,1.34375 z m 5.828126,0.53125 1.10938,-0.0937 q 0.125,0.8125 0.5625,1.21875 0.45312,0.40625 1.09375,0.40625 0.75,0 1.28125,-0.57813 0.53125,-0.57812 0.53125,-1.51562 0,-0.90625 -0.51563,-1.42188 -0.5,-0.53125 -1.32812,-0.53125 -0.5,0 -0.92188,0.23438 -0.40625,0.23437 -0.64062,0.59375 l -0.98438,-0.125 0.82813,-4.40625 h 4.28125 v 1 h -3.4375 l -0.45313,2.3125 q 0.76563,-0.54688 1.60938,-0.54688 1.125,0 1.89062,0.78125 0.78125,0.78125 0.78125,2.01563 0,1.15625 -0.67187,2.01562 -0.82813,1.03125 -2.25,1.03125 -1.17188,0 -1.92188,-0.65625 -0.73437,-0.65625 -0.84375,-1.73437 z" + fill-rule="nonzero" + id="path112329" /> + <path + fill="#000000" + d="m 55.01875,145.42778 h -1.046876 v -6.71875 q -0.390624,0.35937 -1.015624,0.73437 -0.609376,0.35938 -1.09375,0.53126 v -1.01563 q 0.875,-0.42187 1.53125,-1 0.671874,-0.59375 0.953124,-1.15625 h 0.671876 z m 3.1875,-1.98438 1.015624,-0.0937 q 0.125,0.71874 0.484376,1.04687 0.375,0.3125 0.9375,0.3125 0.484374,0 0.84375,-0.21875 0.375,-0.23438 0.609374,-0.59375 0.234376,-0.375 0.390626,-1 0.15625,-0.64063 0.15625,-1.29687 0,-0.0781 0,-0.21876 -0.3125,0.5 -0.859376,0.82813 -0.546874,0.3125 -1.1875,0.3125 -1.078124,0 -1.8125,-0.76563 Q 58.05,140.97466 58.05,139.70903 q 0,-1.3125 0.765624,-2.10937 0.78125,-0.79688 1.9375,-0.79688 0.828126,0 1.515626,0.45312 0.703124,0.45313 1.0625,1.29688 0.359374,0.82812 0.359374,2.40625 0,1.64063 -0.359374,2.625 -0.34375,0.96875 -1.0625,1.48437 -0.703126,0.5 -1.640626,0.5 -1.015624,0 -1.65625,-0.54687 -0.640624,-0.5625 -0.765624,-1.57813 z m 4.3125,-3.79687 q 0,-0.90625 -0.484376,-1.4375 -0.46875,-0.53125 -1.15625,-0.53125 -0.703124,0 -1.234374,0.57812 -0.515626,0.5625 -0.515626,1.48438 0,0.8125 0.5,1.32812 0.5,0.51563 1.21875,0.51563 0.734376,0 1.203126,-0.51563 0.46875,-0.51562 0.46875,-1.42187 z M 65.05,141.1934 q 0,-1.53124 0.3125,-2.45312 0.3125,-0.9375 0.92188,-1.4375 0.625,-0.5 1.5625,-0.5 0.6875,0 1.20312,0.28125 0.53125,0.28125 0.875,0.8125 0.34375,0.51563 0.53125,1.26563 0.1875,0.75 0.1875,2.03124 0,1.51563 -0.3125,2.4375 -0.29687,0.92188 -0.92187,1.4375 -0.60938,0.5 -1.5625,0.5 -1.23438,0 -1.95313,-0.89062 Q 65.05,143.61528 65.05,141.1934 Z m 1.07813,0 q 0,2.10938 0.5,2.8125 0.5,0.70313 1.21875,0.70313 0.73437,0 1.21875,-0.70313 0.5,-0.70312 0.5,-2.8125 0,-2.125 -0.5,-2.8125 -0.48438,-0.70312 -1.23438,-0.70312 -0.71875,0 -1.15625,0.60938 -0.54687,0.78124 -0.54687,2.90624 z" + fill-rule="nonzero" + id="path112331" /> + <path + fill="#000000" + d="m 56.596874,64.90104 v 1.015624 h -5.6875 q 0,-0.375 0.125,-0.734374 0.21875,-0.578126 0.6875,-1.140626 0.484376,-0.5625 1.390626,-1.296874 1.40625,-1.15625 1.890624,-1.828126 0.5,-0.671874 0.5,-1.265624 0,-0.625 -0.453124,-1.046876 -0.453126,-0.4375 -1.171876,-0.4375 -0.765624,0 -1.21875,0.453126 -0.453124,0.453124 -0.46875,1.265624 L 51.1125,59.77604 q 0.109374,-1.21875 0.828124,-1.84375 0.734376,-0.640626 1.96875,-0.640626 1.234376,0 1.953126,0.6875 0.71875,0.6875 0.71875,1.703126 0,0.515624 -0.21875,1.015624 -0.203126,0.484376 -0.703126,1.046876 -0.484374,0.546874 -1.609374,1.5 -0.953126,0.796874 -1.234376,1.09375 -0.265624,0.28125 -0.4375,0.5625 z m 5.421876,1.015624 h -1.046876 v -6.71875 Q 60.58125,59.55729 59.95625,59.93229 59.346874,60.291664 58.8625,60.46354 v -1.015626 q 0.875,-0.421874 1.53125,-1 0.671874,-0.59375 0.953124,-1.15625 h 0.671876 z m 3.03125,-2.25 1.10938,-0.09375 q 0.125,0.8125 0.5625,1.21875 0.45312,0.40625 1.09375,0.40625 0.75,0 1.28125,-0.578124 0.53125,-0.578126 0.53125,-1.515626 0,-0.90625 -0.51563,-1.421874 -0.5,-0.53125 -1.32812,-0.53125 -0.5,0 -0.92188,0.234374 -0.40625,0.234376 -0.64062,0.59375 l -0.98438,-0.125 0.82813,-4.40625 h 4.28125 v 1 h -3.4375 l -0.45313,2.3125 q 0.76563,-0.546874 1.60938,-0.546874 1.125,0 1.89062,0.78125 0.78125,0.78125 0.78125,2.015624 0,1.15625 -0.67187,2.015626 -0.82813,1.03125 -2.25,1.03125 -1.17188,0 -1.92188,-0.65625 Q 65.15938,64.74479 65.05,63.666664 Z" + fill-rule="nonzero" + id="path112333" /> + <path + fill="#000000" + d="m 74.425,317.45 v -2.0625 h -3.71875 v -0.96874 l 3.92188,-5.5625 h 0.85937 v 5.5625 h 1.15625 v 0.96874 H 75.4875 v 2.0625 z m 0,-3.03124 v -3.85936 l -2.6875,3.85936 z m 3.625,-1.20312 q 0,-1.53124 0.3125,-2.45314 0.3125,-0.9375 0.92188,-1.4375 0.625,-0.5 1.5625,-0.5 0.6875,0 1.20312,0.28126 0.53125,0.28124 0.875,0.8125 0.34375,0.51564 0.53125,1.26564 0.1875,0.75 0.1875,2.03124 0,1.51562 -0.3125,2.4375 -0.29687,0.92186 -0.92187,1.4375 -0.60938,0.5 -1.5625,0.5 -1.23438,0 -1.95313,-0.89064 Q 78.05,315.6375 78.05,313.21564 Z m 1.07813,0 q 0,2.10936 0.5,2.8125 0.5,0.70312 1.21875,0.70312 0.73437,0 1.21875,-0.70312 0.5,-0.70314 0.5,-2.8125 0,-2.125 -0.5,-2.8125 Q 81.58125,309.7 80.83125,309.7 q -0.71875,0 -1.15625,0.6094 -0.54687,0.78124 -0.54687,2.90624 z" + fill-rule="nonzero" + id="path112335" /> + <path + fill="#000000" + d="m 202.49374,310.96564 -1.04687,0.0781 q -0.14063,-0.625 -0.39063,-0.90626 -0.4375,-0.4531 -1.0625,-0.4531 -0.5,0 -0.89062,0.28124 -0.48438,0.375 -0.78125,1.07812 -0.28125,0.6875 -0.29687,1.96874 0.375,-0.5781 0.92187,-0.85936 0.5625,-0.28124 1.17187,-0.28124 1.04688,0 1.78126,0.78124 0.75,0.78126 0.75,2 0,0.8125 -0.35938,1.51562 -0.34375,0.6875 -0.95312,1.0625 -0.60938,0.35938 -1.375,0.35938 -1.32813,0 -2.15626,-0.96874 -0.82812,-0.96876 -0.82812,-3.1875 0,-2.5 0.92188,-3.625 0.79687,-0.9844 2.15624,-0.9844 1.01563,0 1.65626,0.57814 0.65624,0.5625 0.78124,1.5625 z m -4.29687,3.70312 q 0,0.54688 0.21875,1.04688 0.23438,0.48436 0.64062,0.75 0.42188,0.26562 0.89063,0.26562 0.65625,0 1.14063,-0.53126 0.48437,-0.54686 0.48437,-1.46874 0,-0.89062 -0.48437,-1.40626 -0.46876,-0.5156 -1.1875,-0.5156 -0.71876,0 -1.21876,0.5156 -0.48437,0.51564 -0.48437,1.34376 z m 5.82813,0.53124 1.10937,-0.0937 q 0.125,0.8125 0.5625,1.21874 0.45313,0.40626 1.09375,0.40626 0.75,0 1.28125,-0.57812 0.53125,-0.57814 0.53125,-1.51564 0,-0.90624 -0.51562,-1.42186 -0.5,-0.53124 -1.32813,-0.53124 -0.5,0 -0.92187,0.23436 -0.40626,0.23438 -0.64063,0.59374 l -0.98437,-0.125 0.82812,-4.40624 h 4.28125 v 1 h -3.4375 l -0.45313,2.3125 q 0.76563,-0.54686 1.60938,-0.54686 1.125,0 1.89062,0.78124 0.78126,0.78126 0.78126,2.01562 0,1.15624 -0.67188,2.01564 -0.82812,1.03124 -2.25,1.03124 -1.17188,0 -1.92188,-0.65624 -0.73437,-0.65626 -0.84374,-1.7344 z" + fill-rule="nonzero" + id="path112337" /> + <path + fill="#000000" + d="m 323.15625,315.46564 1.01563,-0.0937 q 0.125,0.71874 0.48437,1.04686 0.375,0.3125 0.9375,0.3125 0.48437,0 0.84375,-0.21876 0.375,-0.23436 0.60938,-0.59374 0.23437,-0.375 0.39062,-1 0.15625,-0.64062 0.15625,-1.29686 0,-0.0781 0,-0.21876 -0.3125,0.5 -0.85937,0.82812 -0.54688,0.3125 -1.1875,0.3125 -1.07813,0 -1.8125,-0.76562 Q 323,312.9969 323,311.73126 q 0,-1.3125 0.76562,-2.10936 0.78126,-0.7969 1.9375,-0.7969 0.82813,0 1.51563,0.45314 0.70313,0.45312 1.0625,1.29686 0.35937,0.82814 0.35937,2.40626 0,1.64064 -0.35937,2.625 -0.34375,0.96874 -1.0625,1.48438 -0.70313,0.5 -1.64063,0.5 -1.01562,0 -1.65624,-0.54688 -0.64063,-0.5625 -0.76563,-1.57812 z m 4.3125,-3.79688 q 0,-0.90626 -0.48437,-1.4375 -0.46876,-0.53126 -1.15626,-0.53126 -0.70312,0 -1.23437,0.57814 -0.51563,0.5625 -0.51563,1.48436 0,0.8125 0.5,1.32814 0.5,0.51562 1.21876,0.51562 0.73437,0 1.20312,-0.51562 0.46875,-0.51564 0.46875,-1.42188 z M 330,313.21564 q 0,-1.53124 0.3125,-2.45314 0.3125,-0.9375 0.92188,-1.4375 0.625,-0.5 1.5625,-0.5 0.6875,0 1.20312,0.28126 0.53125,0.28124 0.875,0.8125 0.34375,0.51564 0.53125,1.26564 0.1875,0.75 0.1875,2.03124 0,1.51562 -0.3125,2.4375 -0.29687,0.92186 -0.92187,1.4375 -0.60938,0.5 -1.5625,0.5 -1.23438,0 -1.95313,-0.89064 Q 330,315.6375 330,313.21564 Z m 1.07812,0 q 0,2.10936 0.5,2.8125 0.5,0.70312 1.21876,0.70312 0.73437,0 1.21874,-0.70312 0.5,-0.70314 0.5,-2.8125 0,-2.125 -0.5,-2.8125 -0.48437,-0.70314 -1.23437,-0.70314 -0.71875,0 -1.15625,0.6094 -0.54688,0.78124 -0.54688,2.90624 z" + fill-rule="nonzero" + id="path112339" /> + <path + fill="#000000" + d="m 449.44376,317.45 h -1.04688 v -6.71874 q -0.39062,0.35938 -1.01562,0.73438 -0.60938,0.35936 -1.09376,0.53126 v -1.01564 q 0.875,-0.42186 1.53126,-1 0.67187,-0.59376 0.95312,-1.15626 h 0.67188 z m 7,0 h -1.04688 v -6.71874 q -0.39062,0.35938 -1.01562,0.73438 -0.60938,0.35936 -1.09376,0.53126 v -1.01564 q 0.875,-0.42186 1.53126,-1 0.67187,-0.59376 0.95312,-1.15626 h 0.67188 z m 3.03124,-2.25 1.10938,-0.0937 q 0.125,0.8125 0.5625,1.21874 0.45312,0.40626 1.09375,0.40626 0.75,0 1.28125,-0.57812 0.53125,-0.57814 0.53125,-1.51564 0,-0.90624 -0.51563,-1.42186 -0.5,-0.53124 -1.32812,-0.53124 -0.5,0 -0.92188,0.23436 -0.40624,0.23438 -0.64062,0.59374 l -0.98438,-0.125 0.82813,-4.40624 h 4.28125 v 1 h -3.4375 l -0.45312,2.3125 q 0.76562,-0.54686 1.60937,-0.54686 1.125,0 1.89063,0.78124 0.78124,0.78126 0.78124,2.01562 0,1.15624 -0.67187,2.01564 -0.82813,1.03124 -2.25,1.03124 -1.17187,0 -1.92187,-0.65624 -0.73438,-0.65626 -0.84376,-1.7344 z" + fill-rule="nonzero" + id="path112341" /> + <path + fill="#000000" + d="m 575.41876,317.45 h -1.04686 v -6.71874 q -0.39064,0.35938 -1.01564,0.73438 -0.60936,0.35936 -1.09376,0.53126 v -1.01564 q 0.875,-0.42186 1.53126,-1 0.67188,-0.59376 0.95314,-1.15626 h 0.67186 z m 6.40624,0 v -2.0625 h -3.71874 v -0.96874 l 3.92188,-5.5625 h 0.85936 v 5.5625 h 1.15626 v 0.96874 h -1.15626 v 2.0625 z m 0,-3.03124 v -3.85936 l -2.6875,3.85936 z m 3.625,-1.20312 q 0,-1.53124 0.3125,-2.45314 0.3125,-0.9375 0.9219,-1.4375 0.625,-0.5 1.5625,-0.5 0.6875,0 1.2031,0.28126 0.53126,0.28124 0.875,0.8125 0.34376,0.51564 0.53126,1.26564 0.1875,0.75 0.1875,2.03124 0,1.51562 -0.3125,2.4375 -0.29686,0.92186 -0.92186,1.4375 -0.6094,0.5 -1.5625,0.5 -1.2344,0 -1.95314,-0.89064 -0.84376,-1.0625 -0.84376,-3.48436 z m 1.07814,0 q 0,2.10936 0.5,2.8125 0.5,0.70312 1.21876,0.70312 0.73436,0 1.21874,-0.70312 0.5,-0.70314 0.5,-2.8125 0,-2.125 -0.5,-2.8125 -0.48438,-0.70314 -1.23438,-0.70314 -0.71876,0 -1.15626,0.6094 -0.54686,0.78124 -0.54686,2.90624 z" + fill-rule="nonzero" + id="path112343" /> + <rect + style="fill:#000000;stroke:#000000;stroke-width:1.15183;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect112598" + width="11.020167" + height="9.0759048" + x="428.62012" + y="143.09004" + ry="0" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.761891px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 434.5503,146.90838 162.4192,195.66656" + id="path113258" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.761891px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 434.5503,146.90838 539.95869,131.9254" + id="path113262" /> + <text + xml:space="preserve" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:14.8083px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:0.761891" + x="328.28354" + y="78.15921" + id="text114292" + transform="scale(1.0136168,0.98656613)"><tspan + sodipodi:role="line" + id="tspan114290" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:14.8083px;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:0.761891" + x="328.28354" + y="78.15921">basketball</tspan><tspan + sodipodi:role="line" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:14.8083px;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:0.761891" + x="328.28354" + y="96.669586" + id="tspan114602">player</tspan></text> + <text + xml:space="preserve" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:14.8083px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:0.761891" + x="415.56625" + y="109.67815" + id="text118086" + transform="scale(1.0136168,0.98656613)"><tspan + sodipodi:role="line" + id="tspan118084" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:14.8083px;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:0.761891" + x="415.56625" + y="109.67815">shortest</tspan><tspan + sodipodi:role="line" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:14.8083px;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:0.761891" + x="415.56625" + y="128.18852" + id="tspan118638">distance</tspan></text> + <text + xml:space="preserve" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:14.8083px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:0.446669" + x="504.48294" + y="164.05591" + id="text119852" + transform="scale(1.0136168,0.98656613)"><tspan + sodipodi:role="line" + id="tspan119850" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:14.8083px;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:0.446669" + x="504.48294" + y="164.05591">football</tspan><tspan + sodipodi:role="line" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:14.8083px;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:0.446669" + x="504.48294" + y="182.56631" + id="tspan119876">lineman</tspan></text> + <text + xml:space="preserve" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:14.8083px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:0.391862" + x="112.2299" + y="168.39136" + id="text121552" + transform="scale(1.0136168,0.98656613)"><tspan + sodipodi:role="line" + id="tspan121550" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:14.8083px;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:0.391862" + x="112.2299" + y="168.39136">marathon</tspan><tspan + sodipodi:role="line" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:14.8083px;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:0.391862" + x="112.2299" + y="186.90173" + id="tspan121554">runner</tspan></text> + <text + xml:space="preserve" + style="font-style:italic;font-stretch:condensed;font-size:14.8083px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';fill:#000000;stroke-width:0.370208" + x="119.85936" + y="271.38208" + id="text124154" + transform="scale(1.0136168,0.98656613)"><tspan + sodipodi:role="line" + id="tspan124152" + style="fill:#000000;stroke-width:0.370208" + x="119.85936" + y="271.38208">female</tspan><tspan + sodipodi:role="line" + style="fill:#000000;stroke-width:0.370208" + x="119.85936" + y="289.89246" + id="tspan124156">gymnast</tspan></text> + <text + xml:space="preserve" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:14.8083px;line-height:1.25;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:0.761891" + x="383.78757" + y="182.4346" + id="text128880" + transform="scale(1.0136168,0.98656613)"><tspan + sodipodi:role="line" + id="tspan128878" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:14.8083px;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:0.761891" + x="383.78757" + y="182.4346">athlete to be</tspan><tspan + sodipodi:role="line" + style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:14.8083px;font-family:Arial;-inkscape-font-specification:'Arial, Italic Condensed';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:0.761891" + x="383.78757" + y="200.94498" + id="tspan128882">classified</tspan></text> + <path + style="fill:none;stroke:#000000;stroke-width:1.33901;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1" + d="M 434.5503,146.90838 389.37758,99.902324" + id="path132071" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.761891px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 434.5503,146.90838 101.22492,252.57671" + id="path132073" /> + </g> + </g> +</svg> diff --git a/doc/source/user/building.rst b/doc/source/user/building.rst index 76eb48487a91..22efca4a6f83 100644 --- a/doc/source/user/building.rst +++ b/doc/source/user/building.rst @@ -3,78 +3,109 @@ Building from source ==================== -A general overview of building NumPy from source is given here, with detailed -instructions for specific platforms given separately. +There are two options for building NumPy- building with Gitpod or locally from +source. Your choice depends on your operating system and familiarity with the +command line. -Prerequisites -------------- +Gitpod +------------ -Building NumPy requires the following software installed: +Gitpod is an open-source platform that automatically creates +the correct development environment right in your browser, reducing the need to +install local development environments and deal with incompatible dependencies. -1) Python 2.7.x, 3.4.x or newer +If you are a Windows user, unfamiliar with using the command line or building +NumPy for the first time, it is often faster to build with Gitpod. Here are the +in-depth instructions for building NumPy with `building NumPy with Gitpod`_. - On Debian and derivatives (Ubuntu): python, python-dev (or python3-dev) +.. _building NumPy with Gitpod: https://numpy.org/devdocs/dev/development_gitpod.html + +Building locally +------------------ - On Windows: the official python installer at - `www.python.org <http://www.python.org>`_ is enough +Building locally on your machine gives you +more granular control. If you are a MacOS or Linux user familiar with using the +command line, you can continue with building NumPy locally by following the +instructions below. - Make sure that the Python package distutils is installed before - continuing. For example, in Debian GNU/Linux, installing python-dev - also installs distutils. +.. + This page is referenced from numpy/numpy/__init__.py. Please keep its + location in sync with the link there. - Python must also be compiled with the zlib module enabled. This is - practically always the case with pre-packaged Pythons. +Prerequisites +------------- + +Building NumPy requires the following software installed: + +1) Python 3.6.x or newer + + Please note that the Python development headers also need to be installed, + e.g., on Debian/Ubuntu one needs to install both `python3` and + `python3-dev`. On Windows and macOS this is normally not an issue. 2) Compilers - To build any extension modules for Python, you'll need a C compiler. - Various NumPy modules use FORTRAN 77 libraries, so you'll also need a - FORTRAN 77 compiler installed. + Much of NumPy is written in C. You will need a C compiler that complies + with the C99 standard. - Note that NumPy is developed mainly using GNU compilers. Compilers from - other vendors such as Intel, Absoft, Sun, NAG, Compaq, Vast, Portland, - Lahey, HP, IBM, Microsoft are only supported in the form of community - feedback, and may not work out of the box. GCC 4.x (and later) compilers - are recommended. + While a FORTRAN 77 compiler is not necessary for building NumPy, it is + needed to run the ``numpy.f2py`` tests. These tests are skipped if the + compiler is not auto-detected. + + Note that NumPy is developed mainly using GNU compilers and tested on + MSVC and Clang compilers. Compilers from other vendors such as Intel, + Absoft, Sun, NAG, Compaq, Vast, Portland, Lahey, HP, IBM are only + supported in the form of community feedback, and may not work out of the + box. GCC 4.x (and later) compilers are recommended. On ARM64 (aarch64) + GCC 8.x (and later) are recommended. 3) Linear Algebra libraries NumPy does not require any external linear algebra libraries to be installed. However, if these are available, NumPy's setup script can detect them and use them for building. A number of different LAPACK library setups - can be used, including optimized LAPACK libraries such as ATLAS, MKL or the - Accelerate/vecLib framework on OS X. + can be used, including optimized LAPACK libraries such as OpenBLAS or MKL. + The choice and location of these libraries as well as include paths and + other such build options can be specified in a ``site.cfg`` file located in + the NumPy root repository or a ``.numpy-site.cfg`` file in your home + directory. See the ``site.cfg.example`` example file included in the NumPy + repository or sdist for documentation, and below for specifying search + priority from environmental variables. 4) Cython - To build development versions of NumPy, you'll need a recent version of - Cython. Released NumPy sources on PyPi include the C files generated from - Cython code, so for released versions having Cython installed isn't needed. + For building NumPy, you'll need a recent version of Cython. Basic Installation ------------------ -To install NumPy run:: +To install NumPy, run:: - python setup.py install + pip install . To perform an in-place build that can be run from the source folder run:: python setup.py build_ext --inplace -The NumPy build system uses ``setuptools`` (from numpy 1.11.0, before that it -was plain ``distutils``) and ``numpy.distutils``. -Using ``virtualenv`` should work as expected. - *Note: for build instructions to do development work on NumPy itself, see* :ref:`development-environment`. +Testing +------- + +Make sure to test your builds. To ensure everything stays in shape, see if +all tests pass:: + + $ python runtests.py -v -m full + +For detailed info on testing, see :ref:`testing-builds`. + .. _parallel-builds: Parallel builds ~~~~~~~~~~~~~~~ -From NumPy 1.10.0 on it's also possible to do a parallel build with:: +It's possible to do a parallel build with:: python setup.py build -j 4 install --prefix $HOME/.local @@ -86,22 +117,11 @@ to perform a parallel in-place build, run:: The number of build jobs can also be specified via the environment variable ``NPY_NUM_BUILD_JOBS``. - -FORTRAN ABI mismatch --------------------- - -The two most popular open source fortran compilers are g77 and gfortran. -Unfortunately, they are not ABI compatible, which means that concretely you -should avoid mixing libraries built with one with another. In particular, if -your blas/lapack/atlas is built with g77, you *must* use g77 when building -numpy and scipy; on the contrary, if your atlas is built with gfortran, you -*must* build numpy/scipy with gfortran. This applies for most other cases -where different FORTRAN compilers might have been used. - Choosing the fortran compiler ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -To build with gfortran:: +Compilers are auto-detected; building with a particular compiler can be done +with ``--fcompiler``. E.g. to select gfortran:: python setup.py build --fcompiler=gnu95 @@ -109,39 +129,162 @@ For more information see:: python setup.py build --help-fcompiler -How to check the ABI of blas/lapack/atlas -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +How to check the ABI of BLAS/LAPACK libraries +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +One relatively simple and reliable way to check for the compiler used to +build a library is to use ldd on the library. If libg2c.so is a dependency, +this means that g77 has been used (note: g77 is no longer supported for +building NumPy). If libgfortran.so is a dependency, gfortran has been used. +If both are dependencies, this means both have been used, which is almost +always a very bad idea. + +.. _accelerated-blas-lapack-libraries: + +Accelerated BLAS/LAPACK libraries +--------------------------------- + +NumPy searches for optimized linear algebra libraries such as BLAS and LAPACK. +There are specific orders for searching these libraries, as described below and +in the ``site.cfg.example`` file. + +BLAS +~~~~ + +Note that both BLAS and CBLAS interfaces are needed for a properly +optimized build of NumPy. + +The default order for the libraries are: + +1. MKL +2. BLIS +3. OpenBLAS +4. ATLAS +5. BLAS (NetLIB) + +The detection of BLAS libraries may be bypassed by defining the environment +variable ``NPY_BLAS_LIBS`` , which should contain the exact linker flags you +want to use (interface is assumed to be Fortran 77). Also define +``NPY_CBLAS_LIBS`` (even empty if CBLAS is contained in your BLAS library) to +trigger use of CBLAS and avoid slow fallback code for matrix calculations. + +If you wish to build against OpenBLAS but you also have BLIS available one +may predefine the order of searching via the environment variable +``NPY_BLAS_ORDER`` which is a comma-separated list of the above names which +is used to determine what to search for, for instance:: + + NPY_BLAS_ORDER=ATLAS,blis,openblas,MKL python setup.py build + +will prefer to use ATLAS, then BLIS, then OpenBLAS and as a last resort MKL. +If neither of these exists the build will fail (names are compared +lower case). + +Alternatively one may use ``!`` or ``^`` to negate all items:: + + NPY_BLAS_ORDER='^blas,atlas' python setup.py build + +will allow using anything **but** NetLIB BLAS and ATLAS libraries, the order +of the above list is retained. + +One cannot mix negation and positives, nor have multiple negations, such +cases will raise an error. + +LAPACK +~~~~~~ + +The default order for the libraries are: + +1. MKL +2. OpenBLAS +3. libFLAME +4. ATLAS +5. LAPACK (NetLIB) + +The detection of LAPACK libraries may be bypassed by defining the environment +variable ``NPY_LAPACK_LIBS``, which should contain the exact linker flags you +want to use (language is assumed to be Fortran 77). + +If you wish to build against OpenBLAS but you also have MKL available one +may predefine the order of searching via the environment variable +``NPY_LAPACK_ORDER`` which is a comma-separated list of the above names, +for instance:: + + NPY_LAPACK_ORDER=ATLAS,openblas,MKL python setup.py build + +will prefer to use ATLAS, then OpenBLAS and as a last resort MKL. +If neither of these exists the build will fail (names are compared +lower case). + +Alternatively one may use ``!`` or ``^`` to negate all items:: + + NPY_LAPACK_ORDER='^lapack' python setup.py build + +will allow using anything **but** the NetLIB LAPACK library, the order of +the above list is retained. + +One cannot mix negation and positives, nor have multiple negations, such +cases will raise an error. + +.. deprecated:: 1.20 + The native libraries on macOS, provided by Accelerate, are not fit for use + in NumPy since they have bugs that cause wrong output under easily + reproducible conditions. If the vendor fixes those bugs, the library could + be reinstated, but until then users compiling for themselves should use + another linear algebra library or use the built-in (but slower) default, + see the next section. -One relatively simple and reliable way to check for the compiler used to build -a library is to use ldd on the library. If libg2c.so is a dependency, this -means that g77 has been used. If libgfortran.so is a dependency, gfortran -has been used. If both are dependencies, this means both have been used, which -is almost always a very bad idea. Disabling ATLAS and other accelerated libraries ------------------------------------------------ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Usage of ATLAS and other accelerated libraries in NumPy can be disabled via:: + NPY_BLAS_ORDER= NPY_LAPACK_ORDER= python setup.py build + +or:: + BLAS=None LAPACK=None ATLAS=None python setup.py build -Supplying additional compiler flags ------------------------------------ +64-bit BLAS and LAPACK +~~~~~~~~~~~~~~~~~~~~~~ -Additional compiler flags can be supplied by setting the ``OPT``, -``FOPT`` (for Fortran), and ``CC`` environment variables. -When providing options that should improve the performance of the code ensure -that you also set ``-DNDEBUG`` so that debugging code is not executed. +You can tell Numpy to use 64-bit BLAS/LAPACK libraries by setting the +environment variable:: + NPY_USE_BLAS_ILP64=1 -Building with ATLAS support ---------------------------- +when building Numpy. The following 64-bit BLAS/LAPACK libraries are +supported: -Ubuntu -~~~~~~ +1. OpenBLAS ILP64 with ``64_`` symbol suffix (``openblas64_``) +2. OpenBLAS ILP64 without symbol suffix (``openblas_ilp64``) + +The order in which they are preferred is determined by +``NPY_BLAS_ILP64_ORDER`` and ``NPY_LAPACK_ILP64_ORDER`` environment +variables. The default value is ``openblas64_,openblas_ilp64``. + +.. note:: + + Using non-symbol-suffixed 64-bit BLAS/LAPACK in a program that also + uses 32-bit BLAS/LAPACK can cause crashes under certain conditions + (e.g. with embedded Python interpreters on Linux). -You can install the necessary package for optimized ATLAS with this command:: + The 64-bit OpenBLAS with ``64_`` symbol suffix is obtained by + compiling OpenBLAS with settings:: - sudo apt-get install libatlas-base-dev + make INTERFACE64=1 SYMBOLSUFFIX=64_ + + The symbol suffix avoids the symbol name clashes between 32-bit and + 64-bit BLAS/LAPACK libraries. + + +Supplying additional compiler flags +----------------------------------- + +Additional compiler flags can be supplied by setting the ``OPT``, +``FOPT`` (for Fortran), and ``CC`` environment variables. +When providing options that should improve the performance of the code +ensure that you also set ``-DNDEBUG`` so that debugging code is not +executed. diff --git a/doc/source/user/c-info.beyond-basics.rst b/doc/source/user/c-info.beyond-basics.rst index 5c321088d2f5..7dd22afbf629 100644 --- a/doc/source/user/c-info.beyond-basics.rst +++ b/doc/source/user/c-info.beyond-basics.rst @@ -110,12 +110,12 @@ to a small(er) fraction of the total time. Even if the interior of the loop is performed without a function call it can be advantageous to perform the inner loop over the dimension with the highest number of elements to take advantage of speed enhancements available on micro- -processors that use pipelining to enhance fundmental operations. +processors that use pipelining to enhance fundamental operations. The :c:func:`PyArray_IterAllButAxis` ( ``array``, ``&dim`` ) constructs an iterator object that is modified so that it will not iterate over the dimension indicated by dim. The only restriction on this iterator -object, is that the :c:func:`PyArray_Iter_GOTO1D` ( ``it``, ``ind`` ) macro +object, is that the :c:func:`PyArray_ITER_GOTO1D` ( ``it``, ``ind`` ) macro cannot be used (thus flat indexing won't work either if you pass this object back to Python --- so you shouldn't do this). Note that the returned object from this routine is still usually cast to @@ -172,16 +172,15 @@ iterators so that all that needs to be done to advance to the next element in each array is for PyArray_ITER_NEXT to be called for each of the inputs. This incrementing is automatically performed by :c:func:`PyArray_MultiIter_NEXT` ( ``obj`` ) macro (which can handle a -multiterator ``obj`` as either a :c:type:`PyArrayMultiObject *` or a -:c:type:`PyObject *<PyObject>`). The data from input number ``i`` is available using -:c:func:`PyArray_MultiIter_DATA` ( ``obj``, ``i`` ) and the total (broadcasted) -size as :c:func:`PyArray_MultiIter_SIZE` ( ``obj``). An example of using this +multiterator ``obj`` as either a :c:expr:`PyArrayMultiIterObject *` or a +:c:expr:`PyObject *`). The data from input number ``i`` is available using +:c:func:`PyArray_MultiIter_DATA` ( ``obj``, ``i`` ). An example of using this feature follows. .. code-block:: c mobj = PyArray_MultiIterNew(2, obj1, obj2); - size = PyArray_MultiIter_SIZE(obj); + size = mobj->size; while(size--) { ptr1 = PyArray_MultiIter_DATA(mobj, 0); ptr2 = PyArray_MultiIter_DATA(mobj, 1); @@ -217,14 +216,13 @@ type will behave much like a regular data-type except ufuncs must have 1-d loops registered to handle it separately. Also checking for whether or not other data-types can be cast "safely" to and from this new type or not will always return "can cast" unless you also register -which types your new data-type can be cast to and from. Adding -data-types is one of the less well-tested areas for NumPy 1.0, so -there may be bugs remaining in the approach. Only add a new data-type -if you can't do what you want to do using the OBJECT or VOID -data-types that are already available. As an example of what I -consider a useful application of the ability to add data-types is the -possibility of adding a data-type of arbitrary precision floats to -NumPy. +which types your new data-type can be cast to and from. + +The NumPy source code includes an example of a custom data-type as part +of its test suite. The file ``_rational_tests.c.src`` in the source code +directory ``numpy/numpy/core/src/umath/`` contains an implementation of +a data-type that represents a rational number as the ratio of two 32 bit +integers. .. index:: pair: dtype; adding new @@ -300,9 +298,10 @@ An example castfunc is: static void double_to_float(double *from, float* to, npy_intp n, - void* ig1, void* ig2); - while (n--) { - (*to++) = (double) *(from++); + void* ignore1, void* ignore2) { + while (n--) { + (*to++) = (double) *(from++); + } } This could then be registered to convert doubles to floats using the @@ -330,7 +329,7 @@ function :c:func:`PyArray_RegisterCanCast` (from_descr, totype_number, scalarkind) should be used to specify that the data-type object from_descr can be cast to the data-type with type number totype_number. If you are not trying to alter scalar coercion rules, -then use :c:data:`NPY_NOSCALAR` for the scalarkind argument. +then use :c:enumerator:`NPY_NOSCALAR` for the scalarkind argument. If you want to allow your new data-type to also be able to share in the scalar coercion rules, then you need to specify the scalarkind @@ -340,7 +339,7 @@ available to that function). Then, you can register data-types that can be cast to separately for each scalar kind that may be returned from your user-defined data-type. If you don't register scalar coercion handling, then all of your user-defined data-types will be -seen as :c:data:`NPY_NOSCALAR`. +seen as :c:enumerator:`NPY_NOSCALAR`. Registering a ufunc loop @@ -358,38 +357,6 @@ previously created. Then you call :c:func:`PyUFunc_RegisterLoopForType` this function is ``0`` if the process was successful and ``-1`` with an error condition set if it was not successful. -.. c:function:: int PyUFunc_RegisterLoopForType( \ - PyUFuncObject* ufunc, int usertype, PyUFuncGenericFunction function, \ - int* arg_types, void* data) - - *ufunc* - - The ufunc to attach this loop to. - - *usertype* - - The user-defined type this loop should be indexed under. This number - must be a user-defined type or an error occurs. - - *function* - - The ufunc inner 1-d loop. This function must have the signature as - explained in Section `3 <#sec-creating-a-new>`__ . - - *arg_types* - - (optional) If given, this should contain an array of integers of at - least size ufunc.nargs containing the data-types expected by the loop - function. The data will be copied into a NumPy-managed structure so - the memory for this argument should be deleted after calling this - function. If this is NULL, then it will be assumed that all data-types - are of type usertype. - - *data* - - (optional) Specify any optional data needed by the function which will - be passed when the function is called. - .. index:: pair: dtype; adding new @@ -432,7 +399,7 @@ describe the desired behavior of the type. Typically, a new C-structure is also created to contain the instance-specific information needed for each object of the type as well. For example, :c:data:`&PyArray_Type<PyArray_Type>` is a pointer to the type-object table for the ndarray -while a :c:type:`PyArrayObject *` variable is a pointer to a particular instance +while a :c:expr:`PyArrayObject *` variable is a pointer to a particular instance of an ndarray (one of the members of the ndarray structure is, in turn, a pointer to the type- object table :c:data:`&PyArray_Type<PyArray_Type>`). Finally :c:func:`PyType_Ready` (<pointer_to_type_object>) must be called for @@ -481,7 +448,7 @@ type(s). In particular, to create a sub-type in C follow these steps: module dictionary so it can be accessed from Python. More information on creating sub-types in C can be learned by reading -PEP 253 (available at http://www.python.org/dev/peps/pep-0253). +PEP 253 (available at https://www.python.org/dev/peps/pep-0253). Specific features of ndarray sub-typing @@ -505,7 +472,7 @@ The __array_finalize\__ method attribute is looked-up in the object dictionary. If it is present and not None, then it can be either a CObject containing a pointer to a :c:func:`PyArray_FinalizeFunc` or it can be a method taking a - single argument (which could be None). + single argument (which could be None) If the :obj:`~numpy.class.__array_finalize__` attribute is a CObject, then the pointer must be a pointer to a function with the signature: diff --git a/doc/source/user/c-info.how-to-extend.rst b/doc/source/user/c-info.how-to-extend.rst index 22c3b6e90041..96727a177136 100644 --- a/doc/source/user/c-info.how-to-extend.rst +++ b/doc/source/user/c-info.how-to-extend.rst @@ -36,16 +36,16 @@ into Python as if it were a standard python file. It will contain objects and methods that have been defined and compiled in C code. The basic steps for doing this in Python are well-documented and you can find more information in the documentation for Python itself available -online at `www.python.org <http://www.python.org>`_ . +online at `www.python.org <https://www.python.org>`_ . -In addition to the Python C-API, there is a full and rich C-API for -NumPy allowing sophisticated manipulations on a C-level. However, for -most applications, only a few API calls will typically be used. If all -you need to do is extract a pointer to memory along with some shape -information to pass to another calculation routine, then you will use -very different calls, then if you are trying to create a new array- -like type or add a new data type for ndarrays. This chapter documents -the API calls and macros that are most commonly used. +In addition to the Python C-API, there is a full and rich C-API for NumPy +allowing sophisticated manipulations on a C-level. However, for most +applications, only a few API calls will typically be used. For example, if you +need to just extract a pointer to memory along with some shape information to +pass to another calculation routine, then you will use very different calls +than if you are trying to create a new array-like type or add a new data type +for ndarrays. This chapter documents the API calls and macros that are most +commonly used. Required subroutine @@ -163,7 +163,7 @@ ignored. The *args* argument contains all of the arguments passed in to the function as a tuple. You can do anything you want at this point, but usually the easiest way to manage the input arguments is to call :c:func:`PyArg_ParseTuple` (args, format_string, -addresses_to_C_variables...) or :c:func:`PyArg_UnpackTuple` (tuple, "name" , +addresses_to_C_variables...) or :c:func:`PyArg_UnpackTuple` (tuple, "name", min, max, ...). A good description of how to use the first function is contained in the Python C-API reference manual under section 5.5 (Parsing arguments and building values). You should pay particular @@ -174,7 +174,7 @@ rule. There are several converter functions defined in the NumPy C-API that may be of use. In particular, the :c:func:`PyArray_DescrConverter` function is very useful to support arbitrary data-type specification. This function transforms any valid data-type Python object into a -:c:type:`PyArray_Descr *` object. Remember to pass in the address of the +:c:expr:`PyArray_Descr *` object. Remember to pass in the address of the C-variables that should be filled in. There are lots of examples of how to use :c:func:`PyArg_ParseTuple` @@ -192,7 +192,7 @@ It is important to keep in mind that you get a *borrowed* reference to the object when using the "O" format string. However, the converter functions usually require some form of memory handling. In this example, if the conversion is successful, *dtype* will hold a new -reference to a :c:type:`PyArray_Descr *` object, while *input* will hold a +reference to a :c:expr:`PyArray_Descr *` object, while *input* will hold a borrowed reference. Therefore, if this conversion were mixed with another conversion (say to an integer) and the data-type conversion was successful but the integer conversion failed, then you would need @@ -213,9 +213,9 @@ The :c:func:`Py_BuildValue` (format_string, c_variables...) function makes it easy to build tuples of Python objects from C variables. Pay special attention to the difference between 'N' and 'O' in the format string or you can easily create memory leaks. The 'O' format string -increments the reference count of the :c:type:`PyObject *<PyObject>` C-variable it +increments the reference count of the :c:expr:`PyObject *` C-variable it corresponds to, while the 'N' format string steals a reference to the -corresponding :c:type:`PyObject *<PyObject>` C-variable. You should use 'N' if you have +corresponding :c:expr:`PyObject *` C-variable. You should use 'N' if you have already created a reference for the object and just want to give that reference to the tuple. You should use 'O' if you only have a borrowed reference to an object and need to create one to provide for the @@ -342,7 +342,7 @@ The method is to 4. If you are writing the algorithm, then I recommend that you use the stride information contained in the array to access the elements of - the array (the :c:func:`PyArray_GETPTR` macros make this painless). Then, + the array (the :c:func:`PyArray_GetPtr` macros make this painless). Then, you can relax your requirements so as not to force a single-segment array and the data-copying that might result. @@ -362,9 +362,7 @@ specific builtin data-type ( *e.g.* float), while specifying a particular set of requirements ( *e.g.* contiguous, aligned, and writeable). The syntax is -.. c:function:: PyObject *PyArray_FROM_OTF( \ - PyObject* obj, int typenum, int requirements) - +:c:func:`PyArray_FROM_OTF` Return an ndarray from any Python object, *obj*, that can be converted to an array. The number of dimensions in the returned array is determined by the object. The desired data-type of the @@ -376,7 +374,6 @@ writeable). The syntax is exception is set. *obj* - The object can be any Python object convertible to an ndarray. If the object is already (a subclass of) the ndarray that satisfies the requirements then a new reference is returned. @@ -395,7 +392,6 @@ writeable). The syntax is to the requirements flag. *typenum* - One of the enumerated types or :c:data:`NPY_NOTYPE` if the data-type should be determined from the object itself. The C-based names can be used: @@ -423,7 +419,6 @@ writeable). The syntax is requirements flag to override this behavior. *requirements* - The memory model for an ndarray admits arbitrary strides in each dimension to advance to the next element of the array. Often, however, you need to interface with code that expects a @@ -438,7 +433,7 @@ writeable). The syntax is The requirements flag allows specification of what kind of array is acceptable. If the object passed in does not satisfy - this requirements then a copy is made so that thre returned + this requirements then a copy is made so that the returned object will satisfy the requirements. these ndarray can use a very generic pointer to memory. This flag allows specification of the desired properties of the returned array object. All @@ -446,33 +441,22 @@ writeable). The syntax is flags most commonly needed are :c:data:`NPY_ARRAY_IN_ARRAY`, :c:data:`NPY_OUT_ARRAY`, and :c:data:`NPY_ARRAY_INOUT_ARRAY`: - .. c:var:: NPY_ARRAY_IN_ARRAY - - Equivalent to :c:data:`NPY_ARRAY_C_CONTIGUOUS` \| - :c:data:`NPY_ARRAY_ALIGNED`. This combination of flags is useful - for arrays that must be in C-contiguous order and aligned. - These kinds of arrays are usually input arrays for some - algorithm. + :c:data:`NPY_ARRAY_IN_ARRAY` + This flag is useful for arrays that must be in C-contiguous + order and aligned. These kinds of arrays are usually input + arrays for some algorithm. - .. c:var:: NPY_ARRAY_OUT_ARRAY - - Equivalent to :c:data:`NPY_ARRAY_C_CONTIGUOUS` \| - :c:data:`NPY_ARRAY_ALIGNED` \| :c:data:`NPY_ARRAY_WRITEABLE`. This - combination of flags is useful to specify an array that is + :c:data:`NPY_ARRAY_OUT_ARRAY` + This flag is useful to specify an array that is in C-contiguous order, is aligned, and can be written to as well. Such an array is usually returned as output (although normally such output arrays are created from scratch). - .. c:var:: NPY_ARRAY_INOUT_ARRAY - - Equivalent to :c:data:`NPY_ARRAY_C_CONTIGUOUS` \| - :c:data:`NPY_ARRAY_ALIGNED` \| :c:data:`NPY_ARRAY_WRITEABLE` \| - :c:data:`NPY_ARRAY_WRITEBACKIFCOPY` \| - :c:data:`NPY_ARRAY_UPDATEIFCOPY`. This combination of flags is - useful to specify an array that will be used for both + :c:data:`NPY_ARRAY_INOUT_ARRAY` + This flag is useful to specify an array that will be used for both input and output. :c:func:`PyArray_ResolveWritebackIfCopy` - must be called before :func:`Py_DECREF` at + must be called before :c:func:`Py_DECREF` at the end of the interface routine to write back the temporary data into the original array passed in. Use of the :c:data:`NPY_ARRAY_WRITEBACKIFCOPY` or @@ -487,17 +471,14 @@ writeable). The syntax is Other useful flags that can be OR'd as additional requirements are: - .. c:var:: NPY_ARRAY_FORCECAST - + :c:data:`NPY_ARRAY_FORCECAST` Cast to the desired type, even if it can't be done without losing information. - .. c:var:: NPY_ARRAY_ENSURECOPY - + :c:data:`NPY_ARRAY_ENSURECOPY` Make sure the resulting array is a copy of the original. - .. c:var:: NPY_ARRAY_ENSUREARRAY - + :c:data:`NPY_ARRAY_ENSUREARRAY` Make sure the resulting object is an actual ndarray and not a sub- class. @@ -513,7 +494,7 @@ writeable). The syntax is Creating a brand-new ndarray ---------------------------- -Quite often new arrays must be created from within extension-module +Quite often, new arrays must be created from within extension-module code. Perhaps an output array is needed and you don't want the caller to have to supply it. Perhaps only a temporary array is needed to hold an intermediate calculation. Whatever the need there are simple ways @@ -521,49 +502,15 @@ to get an ndarray object of whatever data-type is needed. The most general function for doing this is :c:func:`PyArray_NewFromDescr`. All array creation functions go through this heavily re-used code. Because of its flexibility, it can be somewhat confusing to use. As a result, -simpler forms exist that are easier to use. - -.. c:function:: PyObject *PyArray_SimpleNew(int nd, npy_intp* dims, int typenum) - - This function allocates new memory and places it in an ndarray - with *nd* dimensions whose shape is determined by the array of - at least *nd* items pointed to by *dims*. The memory for the - array is uninitialized (unless typenum is :c:data:`NPY_OBJECT` in - which case each element in the array is set to NULL). The - *typenum* argument allows specification of any of the builtin - data-types such as :c:data:`NPY_FLOAT` or :c:data:`NPY_LONG`. The - memory for the array can be set to zero if desired using - :c:func:`PyArray_FILLWBYTE` (return_object, 0). - -.. c:function:: PyObject *PyArray_SimpleNewFromData( \ - int nd, npy_intp* dims, int typenum, void* data) - - Sometimes, you want to wrap memory allocated elsewhere into an - ndarray object for downstream use. This routine makes it - straightforward to do that. The first three arguments are the same - as in :c:func:`PyArray_SimpleNew`, the final argument is a pointer to a - block of contiguous memory that the ndarray should use as it's - data-buffer which will be interpreted in C-style contiguous - fashion. A new reference to an ndarray is returned, but the - ndarray will not own its data. When this ndarray is deallocated, - the pointer will not be freed. - - You should ensure that the provided memory is not freed while the - returned array is in existence. The easiest way to handle this is - if data comes from another reference-counted Python object. The - reference count on this object should be increased after the - pointer is passed in, and the base member of the returned ndarray - should point to the Python object that owns the data. Then, when - the ndarray is deallocated, the base-member will be DECREF'd - appropriately. If you want the memory to be freed as soon as the - ndarray is deallocated then simply set the OWNDATA flag on the - returned ndarray. +simpler forms exist that are easier to use. These forms are part of the +:c:func:`PyArray_SimpleNew` family of functions, which simplify the interface +by providing default values for common use cases. Getting at ndarray memory and accessing elements of the ndarray --------------------------------------------------------------- -If obj is an ndarray (:c:type:`PyArrayObject *`), then the data-area of the +If obj is an ndarray (:c:expr:`PyArrayObject *`), then the data-area of the ndarray is pointed to by the void* pointer :c:func:`PyArray_DATA` (obj) or the char* pointer :c:func:`PyArray_BYTES` (obj). Remember that (in general) this data-area may not be aligned according to the data-type, it may @@ -573,7 +520,7 @@ specific element of the array is determined only by the array of npy_intp variables, :c:func:`PyArray_STRIDES` (obj). In particular, this c-array of integers shows how many **bytes** must be added to the current element pointer to get to the next element in each dimension. -For arrays less than 4-dimensions there are :c:func:`PyArray_GETPTR{k}` +For arrays less than 4-dimensions there are ``PyArray_GETPTR{k}`` (obj, ...) macros where {k} is the integer 1, 2, 3, or 4 that make using the array strides easier. The arguments .... represent {k} non- negative integer indices into the array. For example, suppose ``E`` is @@ -586,7 +533,7 @@ contiguous arrays have particular striding patterns. Two array flags whether or not the striding pattern of a particular array matches the C-style contiguous or Fortran-style contiguous or neither. Whether or not the striding pattern matches a standard C or Fortran one can be -tested Using :c:func:`PyArray_ISCONTIGUOUS` (obj) and +tested Using :c:func:`PyArray_IS_C_CONTIGUOUS` (obj) and :c:func:`PyArray_ISFORTRAN` (obj) respectively. Most third-party libraries expect contiguous arrays. But, often it is not difficult to support general-purpose striding. I encourage you to use the striding diff --git a/doc/source/user/c-info.python-as-glue.rst b/doc/source/user/c-info.python-as-glue.rst index 0152ac549fc6..6d514f146c4f 100644 --- a/doc/source/user/c-info.python-as-glue.rst +++ b/doc/source/user/c-info.python-as-glue.rst @@ -1,6 +1,6 @@ -******************** +==================== Using Python as glue -******************** +==================== | There is no conversation more boring than the one where everybody | agrees. @@ -124,9 +124,9 @@ Creating source for a basic extension module Probably the easiest way to introduce f2py is to offer a simple example. Here is one of the subroutines contained in a file named -:file:`add.f`: +:file:`add.f` -.. code-block:: none +.. code-block:: fortran C SUBROUTINE ZADD(A,B,C,N) @@ -149,41 +149,43 @@ routine can be automatically generated by f2py:: You should be able to run this command assuming your search-path is set-up properly. This command will produce an extension module named -addmodule.c in the current directory. This extension module can now be +:file:`addmodule.c` in the current directory. This extension module can now be compiled and used from Python just like any other extension module. Creating a compiled extension module ------------------------------------ -You can also get f2py to compile add.f and also compile its produced +You can also get f2py to both compile :file:`add.f` along with the produced extension module leaving only a shared-library extension file that can be imported from Python:: f2py -c -m add add.f This command leaves a file named add.{ext} in the current directory -(where {ext} is the appropriate extension for a python extension +(where {ext} is the appropriate extension for a Python extension module on your platform --- so, pyd, *etc.* ). This module may then be imported from Python. It will contain a method for each subroutine in add (zadd, cadd, dadd, sadd). The docstring of each method contains information about how the module method may be called:: >>> import add - >>> print add.zadd.__doc__ - zadd - Function signature: - zadd(a,b,c,n) - Required arguments: - a : input rank-1 array('D') with bounds (*) - b : input rank-1 array('D') with bounds (*) - c : input rank-1 array('D') with bounds (*) - n : input int + >>> print(add.zadd.__doc__) + zadd(a,b,c,n) + Wrapper for ``zadd``. + + Parameters + ---------- + a : input rank-1 array('D') with bounds (*) + b : input rank-1 array('D') with bounds (*) + c : input rank-1 array('D') with bounds (*) + n : input int Improving the basic interface ----------------------------- -The default interface is a very literal translation of the fortran +The default interface is a very literal translation of the Fortran code into Python. The Fortran array arguments must now be NumPy arrays and the integer argument should be an integer. The interface will attempt to convert all arguments to their required types (and shapes) @@ -192,7 +194,7 @@ about the semantics of the arguments (such that C is an output and n should really match the array sizes), it is possible to abuse this function in ways that can cause Python to crash. For example:: - >>> add.zadd([1,2,3], [1,2], [3,4], 1000) + >>> add.zadd([1, 2, 3], [1, 2], [3, 4], 1000) will cause a program crash on most systems. Under the covers, the lists are being converted to proper arrays but then the underlying add @@ -209,7 +211,7 @@ interface file use the -h option:: This command leaves the file add.pyf in the current directory. The section of this file corresponding to zadd is: -.. code-block:: none +.. code-block:: fortran subroutine zadd(a,b,c,n) ! in :add:add.f double complex dimension(*) :: a @@ -222,7 +224,7 @@ By placing intent directives and checking code, the interface can be cleaned up quite a bit until the Python module method is both easier to use and more robust. -.. code-block:: none +.. code-block:: fortran subroutine zadd(a,b,c,n) ! in :add:add.f double complex dimension(n) :: a @@ -240,27 +242,32 @@ necessary to tell f2py that the value of n depends on the input a (so that it won't try to create the variable n until the variable a is created). -After modifying ``add.pyf``, the new python module file can be generated -by compiling both ``add.f95`` and ``add.pyf``:: +After modifying ``add.pyf``, the new Python module file can be generated +by compiling both ``add.f`` and ``add.pyf``:: - f2py -c add.pyf add.f95 + f2py -c add.pyf add.f The new interface has docstring:: >>> import add - >>> print add.zadd.__doc__ - zadd - Function signature: - c = zadd(a,b) - Required arguments: - a : input rank-1 array('D') with bounds (n) - b : input rank-1 array('D') with bounds (n) - Return objects: - c : rank-1 array('D') with bounds (n) + >>> print(add.zadd.__doc__) + c = zadd(a,b) + + Wrapper for ``zadd``. + + Parameters + ---------- + a : input rank-1 array('D') with bounds (n) + b : input rank-1 array('D') with bounds (n) + + Returns + ------- + c : rank-1 array('D') with bounds (n) Now, the function can be called in a much more robust way:: - >>> add.zadd([1,2,3],[4,5,6]) - array([ 5.+0.j, 7.+0.j, 9.+0.j]) + >>> add.zadd([1, 2, 3], [4, 5, 6]) + array([5.+0.j, 7.+0.j, 9.+0.j]) Notice the automatic conversion to the correct format that occurred. @@ -269,10 +276,10 @@ Inserting directives in Fortran source -------------------------------------- The nice interface can also be generated automatically by placing the -variable directives as special comments in the original fortran code. -Thus, if I modify the source code to contain: +variable directives as special comments in the original Fortran code. +Thus, if the source code is modified to contain: -.. code-block:: none +.. code-block:: fortran C SUBROUTINE ZADD(A,B,C,N) @@ -291,14 +298,14 @@ Thus, if I modify the source code to contain: 20 CONTINUE END -Then, I can compile the extension module using:: +Then, one can compile the extension module using:: f2py -c -m add add.f The resulting signature for the function add.zadd is exactly the same one that was created previously. If the original source code had contained ``A(N)`` instead of ``A(*)`` and so forth with ``B`` and ``C``, -then I could obtain (nearly) the same interface simply by placing the +then nearly the same interface can be obtained by placing the ``INTENT(OUT) :: C`` comment line in the source code. The only difference is that ``N`` would be an optional input that would default to the length of ``A``. @@ -313,7 +320,7 @@ precision floating-point numbers using a fixed averaging filter. The advantage of using Fortran to index into multi-dimensional arrays should be clear from this example. -.. code-block:: none +.. code-block:: SUBROUTINE DFILTER2D(A,B,M,N) C @@ -387,7 +394,7 @@ distribution of the ``add.f`` module (as part of the package Installation of the new package is easy using:: - python setup.py install + pip install . assuming you have the proper permissions to write to the main site- packages directory for the version of Python you are using. For the @@ -400,13 +407,12 @@ conversion of the .pyf file to a .c file is handled by `numpy.disutils`. Conclusion ---------- -The interface definition file (.pyf) is how you can fine-tune the -interface between Python and Fortran. There is decent documentation -for f2py found in the numpy/f2py/docs directory where-ever NumPy is -installed on your system (usually under site-packages). There is also -more information on using f2py (including how to use it to wrap C -codes) at http://www.scipy.org/Cookbook under the "Using NumPy with -Other Languages" heading. +The interface definition file (.pyf) is how you can fine-tune the interface +between Python and Fortran. There is decent documentation for f2py at +:ref:`f2py`. There is also more information on using f2py (including how to use +it to wrap C codes) at the `"Interfacing With Other Languages" heading of the +SciPy Cookbook. +<https://scipy-cookbook.readthedocs.io/items/idx_interfacing_with_other_languages.html>`_ The f2py method of linking compiled code is currently the most sophisticated and integrated approach. It allows clean separation of @@ -415,7 +421,7 @@ distribution of the extension module. The only draw-back is that it requires the existence of a Fortran compiler in order for a user to install the code. However, with the existence of the free-compilers g77, gfortran, and g95, as well as high-quality commercial compilers, -this restriction is not particularly onerous. In my opinion, Fortran +this restriction is not particularly onerous. In our opinion, Fortran is still the easiest way to write fast and clear code for scientific computing. It handles complex numbers, and multi-dimensional indexing in the most straightforward way. Be aware, however, that some Fortran @@ -486,7 +492,7 @@ Complex addition in Cython Here is part of a Cython module named ``add.pyx`` which implements the complex addition functions we previously implemented using f2py: -.. code-block:: none +.. code-block:: cython cimport cython cimport numpy as np @@ -539,7 +545,7 @@ Image filter in Cython The two-dimensional example we created using Fortran is just as easy to write in Cython: -.. code-block:: none +.. code-block:: cython cimport numpy as np import numpy as np @@ -655,7 +661,7 @@ To use ctypes you must 2. Load the shared library. -3. Convert the python objects to ctypes-understood arguments. +3. Convert the Python objects to ctypes-understood arguments. 4. Call the function from the library with the ctypes arguments. @@ -671,7 +677,7 @@ simply have a shared library available to you). Items to remember are: - A shared library must be compiled in a special way ( *e.g.* using the ``-shared`` flag with gcc). -- On some platforms (*e.g.* Windows) , a shared library requires a +- On some platforms (*e.g.* Windows), a shared library requires a .def file that specifies the functions to be exported. For example a mylib.def file might contain:: @@ -744,14 +750,14 @@ around this restriction that allow ctypes to integrate with other objects. 1. Don't set the argtypes attribute of the function object and define an - :obj:`_as_parameter_` method for the object you want to pass in. The - :obj:`_as_parameter_` method must return a Python int which will be passed + ``_as_parameter_`` method for the object you want to pass in. The + ``_as_parameter_`` method must return a Python int which will be passed directly to the function. 2. Set the argtypes attribute to a list whose entries contain objects with a classmethod named from_param that knows how to convert your object to an object that ctypes can understand (an int/long, string, - unicode, or object with the :obj:`_as_parameter_` attribute). + unicode, or object with the ``_as_parameter_`` attribute). NumPy uses both methods with a preference for the second method because it can be safer. The ctypes attribute of the ndarray returns @@ -764,7 +770,7 @@ correct type, shape, and has the correct flags set or risk nasty crashes if the data-pointer to inappropriate arrays are passed in. To implement the second method, NumPy provides the class-factory -function :func:`ndpointer` in the :mod:`ctypeslib` module. This +function :func:`ndpointer` in the :mod:`numpy.ctypeslib` module. This class-factory function produces an appropriate class that can be placed in an argtypes attribute entry of a ctypes function. The class will contain a from_param method which ctypes will use to convert any @@ -802,7 +808,7 @@ Calling the function The function is accessed as an attribute of or an item from the loaded shared-library. Thus, if ``./mylib.so`` has a function named -``cool_function1`` , I could access this function either as: +``cool_function1``, it may be accessed either as: .. code-block:: python @@ -852,7 +858,7 @@ kind of array from a given input. Complete example ---------------- -In this example, I will show how the addition function and the filter +In this example, we will demonstrate how the addition function and the filter function implemented previously using the other approaches can be implemented using ctypes. First, the C code which implements the algorithms contains the functions ``zadd``, ``dadd``, ``sadd``, ``cadd``, @@ -944,7 +950,7 @@ Linux system this is accomplished using:: Which creates a shared_library named code.so in the current directory. On Windows don't forget to either add ``__declspec(dllexport)`` in front of void on the line preceding each function definition, or write a -code.def file that lists the names of the functions to be exported. +``code.def`` file that lists the names of the functions to be exported. A suitable Python interface to this shared library should be constructed. To do this create a file named interface.py with the @@ -954,25 +960,25 @@ following lines at the top: __all__ = ['add', 'filter2d'] - import numpy as N + import numpy as np import os _path = os.path.dirname('__file__') - lib = N.ctypeslib.load_library('code', _path) - _typedict = {'zadd' : complex, 'sadd' : N.single, - 'cadd' : N.csingle, 'dadd' : float} + lib = np.ctypeslib.load_library('code', _path) + _typedict = {'zadd' : complex, 'sadd' : np.single, + 'cadd' : np.csingle, 'dadd' : float} for name in _typedict.keys(): val = getattr(lib, name) val.restype = None _type = _typedict[name] - val.argtypes = [N.ctypeslib.ndpointer(_type, + val.argtypes = [np.ctypeslib.ndpointer(_type, flags='aligned, contiguous'), - N.ctypeslib.ndpointer(_type, + np.ctypeslib.ndpointer(_type, flags='aligned, contiguous'), - N.ctypeslib.ndpointer(_type, + np.ctypeslib.ndpointer(_type, flags='aligned, contiguous,'\ 'writeable'), - N.ctypeslib.c_intp] + np.ctypeslib.c_intp] This code loads the shared library named ``code.{ext}`` located in the same path as this file. It then adds a return type of void to the @@ -989,13 +995,13 @@ strides and shape of an ndarray) as the last two arguments.: .. code-block:: python lib.dfilter2d.restype=None - lib.dfilter2d.argtypes = [N.ctypeslib.ndpointer(float, ndim=2, + lib.dfilter2d.argtypes = [np.ctypeslib.ndpointer(float, ndim=2, flags='aligned'), - N.ctypeslib.ndpointer(float, ndim=2, + np.ctypeslib.ndpointer(float, ndim=2, flags='aligned, contiguous,'\ 'writeable'), - ctypes.POINTER(N.ctypeslib.c_intp), - ctypes.POINTER(N.ctypeslib.c_intp)] + ctypes.POINTER(np.ctypeslib.c_intp), + ctypes.POINTER(np.ctypeslib.c_intp)] Next, define a simple selection function that chooses which addition function to call in the shared library based on the data-type: @@ -1020,11 +1026,11 @@ written simply as: def add(a, b): requires = ['CONTIGUOUS', 'ALIGNED'] - a = N.asanyarray(a) + a = np.asanyarray(a) func, dtype = select(a.dtype) - a = N.require(a, dtype, requires) - b = N.require(b, dtype, requires) - c = N.empty_like(a) + a = np.require(a, dtype, requires) + b = np.require(b, dtype, requires) + c = np.empty_like(a) func(a,b,c,a.size) return c @@ -1033,8 +1039,8 @@ and: .. code-block:: python def filter2d(a): - a = N.require(a, float, ['ALIGNED']) - b = N.zeros_like(a) + a = np.require(a, float, ['ALIGNED']) + b = np.zeros_like(a) lib.dfilter2d(a, b, a.ctypes.strides, a.ctypes.shape) return b @@ -1066,7 +1072,7 @@ Its disadvantages include - It is difficult to distribute an extension module made using ctypes because of a lack of support for building shared libraries in - distutils (but I suspect this will change in time). + distutils. - You must have shared-libraries of your code (no static libraries). @@ -1088,15 +1094,14 @@ Additional tools you may find useful These tools have been found useful by others using Python and so are included here. They are discussed separately because they are either older ways to do things now handled by f2py, Cython, or ctypes -(SWIG, PyFort) or because I don't know much about them (SIP, Boost). -I have not added links to these -methods because my experience is that you can find the most relevant -link faster using Google or some other search engine, and any links -provided here would be quickly dated. Do not assume that just because -it is included in this list, I don't think the package deserves your -attention. I'm including information about these packages because many -people have found them useful and I'd like to give you as many options -as possible for tackling the problem of easily integrating your code. +(SWIG, PyFort) or because of a lack of reasonable documentation (SIP, Boost). +Links to these methods are not included since the most relevant +can be found using Google or some other search engine, and any links provided +here would be quickly dated. Do not assume that inclusion in this list means +that the package deserves attention. Information about these packages are +collected here because many people have found them useful and we'd like to give +you as many options as possible for tackling the problem of easily integrating +your code. SWIG @@ -1108,7 +1113,7 @@ SWIG Simplified Wrapper and Interface Generator (SWIG) is an old and fairly stable method for wrapping C/C++-libraries to a large variety of other languages. It does not specifically understand NumPy arrays but can be -made useable with NumPy through the use of typemaps. There are some +made usable with NumPy through the use of typemaps. There are some sample typemaps in the numpy/tools/swig directory under numpy.i together with an example module that makes use of them. SWIG excels at wrapping large C/C++ libraries because it can (almost) parse their headers and @@ -1125,12 +1130,12 @@ to the Python-specific typemaps, SWIG can be used to interface a library with other languages such as Perl, Tcl, and Ruby. My experience with SWIG has been generally positive in that it is -relatively easy to use and quite powerful. I used to use it quite +relatively easy to use and quite powerful. It has been used often before becoming more proficient at writing C-extensions. -However, I struggled writing custom interfaces with SWIG because it +However, writing custom interfaces with SWIG is often troublesome because it must be done using the concept of typemaps which are not Python -specific and are written in a C-like syntax. Therefore, I tend to -prefer other gluing strategies and would only attempt to use SWIG to +specific and are written in a C-like syntax. Therefore, other gluing strategies +are preferred and SWIG would be probably considered only to wrap a very-large C/C++ library. Nonetheless, there are others who use SWIG quite happily. @@ -1163,12 +1168,11 @@ those libraries which provides a concise interface for binding C++ classes and functions to Python. The amazing part of the Boost.Python approach is that it works entirely in pure C++ without introducing a new syntax. Many users of C++ report that Boost.Python makes it -possible to combine the best of both worlds in a seamless fashion. I -have not used Boost.Python because I am not a big user of C++ and -using Boost to wrap simple C-subroutines is usually over-kill. It's -primary purpose is to make C++ classes available in Python. So, if you -have a set of C++ classes that need to be integrated cleanly into -Python, consider learning about and using Boost.Python. +possible to combine the best of both worlds in a seamless fashion. Using Boost +to wrap simple C-subroutines is usually over-kill. Its primary purpose is to +make C++ classes available in Python. So, if you have a set of C++ classes that +need to be integrated cleanly into Python, consider learning about and using +Boost.Python. PyFort diff --git a/doc/source/user/c-info.ufunc-tutorial.rst b/doc/source/user/c-info.ufunc-tutorial.rst index 5818ff182272..9bd01b9639e7 100644 --- a/doc/source/user/c-info.ufunc-tutorial.rst +++ b/doc/source/user/c-info.ufunc-tutorial.rst @@ -17,7 +17,7 @@ Creating a new universal function Before reading this, it may help to familiarize yourself with the basics of C extensions for Python by reading/skimming the tutorials in Section 1 of `Extending and Embedding the Python Interpreter -<http://docs.python.org/extending/index.html>`_ and in :doc:`How to extend +<https://docs.python.org/extending/index.html>`_ and in :doc:`How to extend NumPy <c-info.how-to-extend>` The umath module is a computer-generated C-module that creates many @@ -80,6 +80,7 @@ the module. .. code-block:: c + #define PY_SSIZE_T_CLEAN #include <Python.h> #include <math.h> @@ -137,7 +138,6 @@ the module. /* This initiates the module using the above definitions. */ - #if PY_VERSION_HEX >= 0x03000000 static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "spam", @@ -159,17 +159,6 @@ the module. } return m; } - #else - PyMODINIT_FUNC initspam(void) - { - PyObject *m; - - m = Py_InitModule("spam", SpamMethods); - if (m == NULL) { - return; - } - } - #endif To use the setup.py file, place setup.py and spammodule.c in the same folder. Then python setup.py build will build the module to import, @@ -264,11 +253,12 @@ the primary thing that must be changed to create your own ufunc. .. code-block:: c - #include "Python.h" - #include "math.h" + #define PY_SSIZE_T_CLEAN + #include <Python.h> #include "numpy/ndarraytypes.h" #include "numpy/ufuncobject.h" #include "numpy/npy_3kcompat.h" + #include <math.h> /* * single_type_logit.c @@ -322,7 +312,6 @@ the primary thing that must be changed to create your own ufunc. static void *data[1] = {NULL}; - #if PY_VERSION_HEX >= 0x03000000 static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "npufunc", @@ -357,30 +346,6 @@ the primary thing that must be changed to create your own ufunc. return m; } - #else - PyMODINIT_FUNC initnpufunc(void) - { - PyObject *m, *logit, *d; - - - m = Py_InitModule("npufunc", LogitMethods); - if (m == NULL) { - return; - } - - import_array(); - import_umath(); - - logit = PyUFunc_FromFuncAndData(funcs, data, types, 1, 1, 1, - PyUFunc_None, "logit", - "logit_docstring", 0); - - d = PyModule_GetDict(m); - - PyDict_SetItemString(d, "logit", logit); - Py_DECREF(logit); - } - #endif This is a setup.py file for the above code. As before, the module can be build via calling python setup.py build at the command prompt, @@ -464,11 +429,12 @@ the primary thing that must be changed to create your own ufunc. .. code-block:: c - #include "Python.h" - #include "math.h" + #define PY_SSIZE_T_CLEAN + #include <Python.h> #include "numpy/ndarraytypes.h" #include "numpy/ufuncobject.h" #include "numpy/halffloat.h" + #include <math.h> /* * multi_type_logit.c @@ -601,7 +567,6 @@ the primary thing that must be changed to create your own ufunc. NPY_LONGDOUBLE, NPY_LONGDOUBLE}; static void *data[4] = {NULL, NULL, NULL, NULL}; - #if PY_VERSION_HEX >= 0x03000000 static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "npufunc", @@ -636,30 +601,6 @@ the primary thing that must be changed to create your own ufunc. return m; } - #else - PyMODINIT_FUNC initnpufunc(void) - { - PyObject *m, *logit, *d; - - - m = Py_InitModule("npufunc", LogitMethods); - if (m == NULL) { - return; - } - - import_array(); - import_umath(); - - logit = PyUFunc_FromFuncAndData(funcs, data, types, 4, 1, 1, - PyUFunc_None, "logit", - "logit_docstring", 0); - - d = PyModule_GetDict(m); - - PyDict_SetItemString(d, "logit", logit); - Py_DECREF(logit); - } - #endif This is a setup.py file for the above code. As before, the module can be build via calling python setup.py build at the command prompt, @@ -758,11 +699,12 @@ as well as all other properties of a ufunc. .. code-block:: c - #include "Python.h" - #include "math.h" + #define PY_SSIZE_T_CLEAN + #include <Python.h> #include "numpy/ndarraytypes.h" #include "numpy/ufuncobject.h" #include "numpy/halffloat.h" + #include <math.h> /* * multi_arg_logit.c @@ -824,7 +766,6 @@ as well as all other properties of a ufunc. static void *data[1] = {NULL}; - #if PY_VERSION_HEX >= 0x03000000 static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "npufunc", @@ -859,30 +800,6 @@ as well as all other properties of a ufunc. return m; } - #else - PyMODINIT_FUNC initnpufunc(void) - { - PyObject *m, *logit, *d; - - - m = Py_InitModule("npufunc", LogitMethods); - if (m == NULL) { - return; - } - - import_array(); - import_umath(); - - logit = PyUFunc_FromFuncAndData(funcs, data, types, 1, 2, 2, - PyUFunc_None, "logit", - "logit_docstring", 0); - - d = PyModule_GetDict(m); - - PyDict_SetItemString(d, "logit", logit); - Py_DECREF(logit); - } - #endif .. _`sec:NumPy-struct-dtype`: @@ -893,9 +810,9 @@ Example NumPy ufunc with structured array dtype arguments This example shows how to create a ufunc for a structured array dtype. For the example we show a trivial ufunc for adding two arrays with dtype 'u8,u8,u8'. The process is a bit different from the other examples since -a call to PyUFunc_FromFuncAndData doesn't fully register ufuncs for +a call to :c:func:`PyUFunc_FromFuncAndData` doesn't fully register ufuncs for custom dtypes and structured array dtypes. We need to also call -PyUFunc_RegisterLoopForDescr to finish setting up the ufunc. +:c:func:`PyUFunc_RegisterLoopForDescr` to finish setting up the ufunc. We only give the C code as the setup.py file is exactly the same as the setup.py file in `Example NumPy ufunc for one dtype`_, except that @@ -915,11 +832,12 @@ The C file is given below. .. code-block:: c - #include "Python.h" - #include "math.h" + #define PY_SSIZE_T_CLEAN + #include <Python.h> #include "numpy/ndarraytypes.h" #include "numpy/ufuncobject.h" #include "numpy/npy_3kcompat.h" + #include <math.h> /* @@ -976,7 +894,6 @@ The C file is given below. static void *data[1] = {NULL}; - #if defined(NPY_PY3K) static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "struct_ufunc_test", @@ -988,31 +905,18 @@ The C file is given below. NULL, NULL }; - #endif - #if defined(NPY_PY3K) PyMODINIT_FUNC PyInit_struct_ufunc_test(void) - #else - PyMODINIT_FUNC initstruct_ufunc_test(void) - #endif { PyObject *m, *add_triplet, *d; PyObject *dtype_dict; PyArray_Descr *dtype; PyArray_Descr *dtypes[3]; - #if defined(NPY_PY3K) m = PyModule_Create(&moduledef); - #else - m = Py_InitModule("struct_ufunc_test", StructUfuncTestMethods); - #endif if (m == NULL) { - #if defined(NPY_PY3K) return NULL; - #else - return; - #endif } import_array(); @@ -1043,138 +947,9 @@ The C file is given below. PyDict_SetItemString(d, "add_triplet", add_triplet); Py_DECREF(add_triplet); - #if defined(NPY_PY3K) return m; - #endif } - -.. _`sec:PyUFunc-spec`: - -PyUFunc_FromFuncAndData Specification -===================================== - -What follows is the full specification of PyUFunc_FromFuncAndData, which -automatically generates a ufunc from a C function with the correct signature. - -.. seealso:: :c:func:`PyUFunc_FromFuncAndDataAndSignature` - -.. c:function:: PyObject *PyUFunc_FromFuncAndData( \ - PyUFuncGenericFunction* func, void** data, char* types, int ntypes, \ - int nin, int nout, int identity, char* name, char* doc, int unused) - - *func* - - A pointer to an array of 1-d functions to use. This array must be at - least ntypes long. Each entry in the array must be a - ``PyUFuncGenericFunction`` function. This function has the following - signature. An example of a valid 1d loop function is also given. - - .. c:function:: void loop1d( \ - char** args, npy_intp* dimensions, npy_intp* steps, void* data) - - *args* - - An array of pointers to the actual data for the input and output - arrays. The input arguments are given first followed by the output - arguments. - - *dimensions* - - A pointer to the size of the dimension over which this function is - looping. - - *steps* - - A pointer to the number of bytes to jump to get to the - next element in this dimension for each of the input and - output arguments. - - *data* - - Arbitrary data (extra arguments, function names, *etc.* ) - that can be stored with the ufunc and will be passed in - when it is called. - - .. code-block:: c - - static void - double_add(char **args, npy_intp *dimensions, npy_intp *steps, - void *extra) - { - npy_intp i; - npy_intp is1 = steps[0], is2 = steps[1]; - npy_intp os = steps[2], n = dimensions[0]; - char *i1 = args[0], *i2 = args[1], *op = args[2]; - for (i = 0; i < n; i++) { - *((double *)op) = *((double *)i1) + - *((double *)i2); - i1 += is1; - i2 += is2; - op += os; - } - } - - *data* - - An array of data. There should be ntypes entries (or NULL) --- one for - every loop function defined for this ufunc. This data will be passed - in to the 1-d loop. One common use of this data variable is to pass in - an actual function to call to compute the result when a generic 1-d - loop (e.g. :c:func:`PyUFunc_d_d`) is being used. - - *types* - - An array of type-number signatures (type ``char`` ). This - array should be of size (nin+nout)*ntypes and contain the - data-types for the corresponding 1-d loop. The inputs should - be first followed by the outputs. For example, suppose I have - a ufunc that supports 1 integer and 1 double 1-d loop - (length-2 func and data arrays) that takes 2 inputs and - returns 1 output that is always a complex double, then the - types array would be - - .. code-block:: c - - static char types[3] = {NPY_INT, NPY_DOUBLE, NPY_CDOUBLE} - - The bit-width names can also be used (e.g. :c:data:`NPY_INT32`, - :c:data:`NPY_COMPLEX128` ) if desired. - - *ntypes* - - The number of data-types supported. This is equal to the number of 1-d - loops provided. - - *nin* - - The number of input arguments. - - *nout* - - The number of output arguments. - - *identity* - - Either :c:data:`PyUFunc_One`, :c:data:`PyUFunc_Zero`, - :c:data:`PyUFunc_None`. This specifies what should be returned when - an empty array is passed to the reduce method of the ufunc. - - *name* - - A ``NULL`` -terminated string providing the name of this ufunc - (should be the Python name it will be called). - - *doc* - - A documentation string for this ufunc (will be used in generating the - response to ``{ufunc_name}.__doc__``). Do not include the function - signature or the name as this is generated automatically. - - *unused* - - Unused; kept for compatibility. Just set it to zero. - .. index:: pair: ufunc; adding new diff --git a/doc/source/user/depending_on_numpy.rst b/doc/source/user/depending_on_numpy.rst new file mode 100644 index 000000000000..d8e97ef1f967 --- /dev/null +++ b/doc/source/user/depending_on_numpy.rst @@ -0,0 +1,147 @@ +.. _for-downstream-package-authors: + +For downstream package authors +============================== + +This document aims to explain some best practices for authoring a package that +depends on NumPy. + + +Understanding NumPy's versioning and API/ABI stability +------------------------------------------------------ + +NumPy uses a standard, :pep:`440` compliant, versioning scheme: +``major.minor.bugfix``. A *major* release is highly unusual (NumPy is still at +version ``1.xx``) and if it happens it will likely indicate an ABI break. +*Minor* versions are released regularly, typically every 6 months. Minor +versions contain new features, deprecations, and removals of previously +deprecated code. *Bugfix* releases are made even more frequently; they do not +contain any new features or deprecations. + +It is important to know that NumPy, like Python itself and most other +well known scientific Python projects, does **not** use semantic versioning. +Instead, backwards incompatible API changes require deprecation warnings for at +least two releases. For more details, see :ref:`NEP23`. + +NumPy has both a Python API and a C API. The C API can be used directly or via +Cython, f2py, or other such tools. If your package uses the C API, then ABI +(application binary interface) stability of NumPy is important. NumPy's ABI is +forward but not backward compatible. This means: binaries compiled against a +given version of NumPy will still run correctly with newer NumPy versions, but +not with older versions. + + +Testing against the NumPy main branch or pre-releases +----------------------------------------------------- + +For large, actively maintained packages that depend on NumPy, we recommend +testing against the development version of NumPy in CI. To make this easy, +nightly builds are provided as wheels at +https://anaconda.org/scipy-wheels-nightly/. +This helps detect regressions in NumPy that need fixing before the next NumPy +release. Furthermore, we recommend to raise errors on warnings in CI for this +job, either all warnings or otherwise at least ``DeprecationWarning`` and +``FutureWarning``. This gives you an early warning about changes in NumPy to +adapt your code. + + +Adding a dependency on NumPy +---------------------------- + +Build-time dependency +````````````````````` + +If a package either uses the NumPy C API directly or it uses some other tool +that depends on it like Cython or Pythran, NumPy is a *build-time* dependency +of the package. Because the NumPy ABI is only forward compatible, you must +build your own binaries (wheels or other package formats) against the lowest +NumPy version that you support (or an even older version). + +Picking the correct NumPy version to build against for each Python version and +platform can get complicated. There are a couple of ways to do this. +Build-time dependencies are specified in ``pyproject.toml`` (see PEP 517), +which is the file used to build wheels by PEP 517 compliant tools (e.g., +when using ``pip wheel``). + +You can specify everything manually in ``pyproject.toml``, or you can instead +rely on the `oldest-supported-numpy <https://github.com/scipy/oldest-supported-numpy/>`__ +metapackage. ``oldest-supported-numpy`` will specify the correct NumPy version +at build time for wheels, taking into account Python version, Python +implementation (CPython or PyPy), operating system and hardware platform. It +will specify the oldest NumPy version that supports that combination of +characteristics. Note: for platforms for which NumPy provides wheels on PyPI, +it will be the first version with wheels (even if some older NumPy version +happens to build). + +For conda-forge it's a little less complicated: there's dedicated handling for +NumPy in build-time and runtime dependencies, so typically this is enough +(see `here <https://conda-forge.org/docs/maintainer/knowledge_base.html#building-against-numpy>`__ for docs):: + + host: + - numpy + run: + - {{ pin_compatible('numpy') }} + +.. note:: + + ``pip`` has ``--no-use-pep517`` and ``--no-build-isolation`` flags that may + ignore ``pyproject.toml`` or treat it differently - if users use those + flags, they are responsible for installing the correct build dependencies + themselves. + + ``conda`` will always use ``-no-build-isolation``; dependencies for conda + builds are given in the conda recipe (``meta.yaml``), the ones in + ``pyproject.toml`` have no effect. + + Please do not use ``setup_requires`` (it is deprecated and may invoke + ``easy_install``). + +Because for NumPy you have to care about ABI compatibility, you +specify the version with ``==`` to the lowest supported version. For your other +build dependencies you can probably be looser, however it's still important to +set lower and upper bounds for each dependency. It's fine to specify either a +range or a specific version for a dependency like ``wheel`` or ``setuptools``. +It's recommended to set the upper bound of the range to the latest already +released version of ``wheel`` and ``setuptools`` - this prevents future +releases from breaking your packages on PyPI. + + +Runtime dependency & version ranges +``````````````````````````````````` + +NumPy itself and many core scientific Python packages have agreed on a schedule +for dropping support for old Python and NumPy versions: :ref:`NEP29`. We +recommend all packages depending on NumPy to follow the recommendations in NEP +29. + +For *run-time dependencies*, you specify the range of versions in +``install_requires`` in ``setup.py`` (assuming you use ``numpy.distutils`` or +``setuptools`` to build). Getting the upper bound right for NumPy is slightly +tricky. If we don't set any bound, a too-new version will be pulled in a few +years down the line, and NumPy may have deprecated and removed some API that +your package depended on by then. On the other hand if you set the upper bound +to the newest already-released version, then as soon as a new NumPy version is +released there will be no matching version of your package that works with it. + +What to do here depends on your release frequency. Given that NumPy releases +come in a 6-monthly cadence and that features that get deprecated in NumPy +should stay around for another two releases, a good upper bound is +``<1.(xx+3).0`` - where ``xx`` is the minor version of the latest +already-released NumPy. This is safe to do if you release at least once a year. +If your own releases are much less frequent, you may set the upper bound a +little further into the future - this is a trade-off between a future NumPy +version _maybe_ removing something you rely on, and the upper bound being +exceeded which _may_ lead to your package being hard to install in combination +with other packages relying on the latest NumPy. + + +.. note:: + + + SciPy has more documentation on how it builds wheels and deals with its + build-time and runtime dependencies + `here <https://scipy.github.io/devdocs/dev/core-dev/index.html#distributing>`__. + + NumPy and SciPy wheel build CI may also be useful as a reference, it can be + found `here for NumPy <https://github.com/MacPython/numpy-wheels>`__ and + `here for SciPy <https://github.com/MacPython/scipy-wheels>`__. diff --git a/doc/source/user/how-to-how-to.rst b/doc/source/user/how-to-how-to.rst new file mode 100644 index 000000000000..cdf1ad5c3adf --- /dev/null +++ b/doc/source/user/how-to-how-to.rst @@ -0,0 +1,115 @@ +.. _how-to-how-to: + +############################################################################## +How to write a NumPy how-to +############################################################################## + +How-tos get straight to the point -- they + + - answer a focused question, or + - narrow a broad question into focused questions that the user can + choose among. + +****************************************************************************** +A stranger has asked for directions... +****************************************************************************** + +**"I need to refuel my car."** + +****************************************************************************** +Give a brief but explicit answer +****************************************************************************** + + - `"Three kilometers/miles, take a right at Hayseed Road, it's on your left."` + +Add helpful details for newcomers ("Hayseed Road", even though it's the only +turnoff at three km/mi). But not irrelevant ones: + + - Don't also give directions from Route 7. + - Don't explain why the town has only one filling station. + +If there's related background (tutorial, explanation, reference, alternative +approach), bring it to the user's attention with a link ("Directions from Route 7," +"Why so few filling stations?"). + + +****************************************************************************** +Delegate +****************************************************************************** + + - `"Three km/mi, take a right at Hayseed Road, follow the signs."` + +If the information is already documented and succinct enough for a how-to, +just link to it, possibly after an introduction ("Three km/mi, take a right"). + +****************************************************************************** +If the question is broad, narrow and redirect it +****************************************************************************** + + **"I want to see the sights."** + +The `See the sights` how-to should link to a set of narrower how-tos: + +- Find historic buildings +- Find scenic lookouts +- Find the town center + +and these might in turn link to still narrower how-tos -- so the town center +page might link to + + - Find the court house + - Find city hall + +By organizing how-tos this way, you not only display the options for people +who need to narrow their question, you also have provided answers for users +who start with narrower questions ("I want to see historic buildings," "Which +way to city hall?"). + +****************************************************************************** +If there are many steps, break them up +****************************************************************************** + +If a how-to has many steps: + + - Consider breaking a step out into an individual how-to and linking to it. + - Include subheadings. They help readers grasp what's coming and return + where they left off. + +****************************************************************************** +Why write how-tos when there's Stack Overflow, Reddit, Gitter...? +****************************************************************************** + + - We have authoritative answers. + - How-tos make the site less forbidding to non-experts. + - How-tos bring people into the site and help them discover other information + that's here . + - Creating how-tos helps us see NumPy usability through new eyes. + +****************************************************************************** +Aren't how-tos and tutorials the same thing? +****************************************************************************** + +People use the terms "how-to" and "tutorial" interchangeably, but we draw a +distinction, following Daniele Procida's `taxonomy of documentation`_. + + .. _`taxonomy of documentation`: https://documentation.divio.com/ + +Documentation needs to meet users where they are. `How-tos` offer get-it-done +information; the user wants steps to copy and doesn't necessarily want to +understand NumPy. `Tutorials` are warm-fuzzy information; the user wants a +feel for some aspect of NumPy (and again, may or may not care about deeper +knowledge). + +We distinguish both tutorials and how-tos from `Explanations`, which are +deep dives intended to give understanding rather than immediate assistance, +and `References`, which give complete, authoritative data on some concrete +part of NumPy (like its API) but aren't obligated to paint a broader picture. + +For more on tutorials, see :doc:`content/tutorial-style-guide` + +****************************************************************************** +Is this page an example of a how-to? +****************************************************************************** + +Yes -- until the sections with question-mark headings; they explain rather +than giving directions. In a how-to, those would be links. diff --git a/doc/source/user/how-to-io.rst b/doc/source/user/how-to-io.rst new file mode 100644 index 000000000000..d238ccbb605e --- /dev/null +++ b/doc/source/user/how-to-io.rst @@ -0,0 +1,328 @@ +.. _how-to-io: + +############################################################################## +Reading and writing files +############################################################################## + +This page tackles common applications; for the full collection of I/O +routines, see :ref:`routines.io`. + + +****************************************************************************** +Reading text and CSV_ files +****************************************************************************** + +.. _CSV: https://en.wikipedia.org/wiki/Comma-separated_values + +With no missing values +============================================================================== + +Use :func:`numpy.loadtxt`. + +With missing values +============================================================================== + +Use :func:`numpy.genfromtxt`. + +:func:`numpy.genfromtxt` will either + + - return a :ref:`masked array<maskedarray.generic>` + **masking out missing values** (if ``usemask=True``), or + + - **fill in the missing value** with the value specified in + ``filling_values`` (default is ``np.nan`` for float, -1 for int). + +With non-whitespace delimiters +------------------------------------------------------------------------------ +:: + + >>> print(open("csv.txt").read()) # doctest: +SKIP + 1, 2, 3 + 4,, 6 + 7, 8, 9 + + +Masked-array output +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +:: + + >>> np.genfromtxt("csv.txt", delimiter=",", usemask=True) # doctest: +SKIP + masked_array( + data=[[1.0, 2.0, 3.0], + [4.0, --, 6.0], + [7.0, 8.0, 9.0]], + mask=[[False, False, False], + [False, True, False], + [False, False, False]], + fill_value=1e+20) + +Array output +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +:: + + >>> np.genfromtxt("csv.txt", delimiter=",") # doctest: +SKIP + array([[ 1., 2., 3.], + [ 4., nan, 6.], + [ 7., 8., 9.]]) + +Array output, specified fill-in value +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +:: + + >>> np.genfromtxt("csv.txt", delimiter=",", dtype=np.int8, filling_values=99) # doctest: +SKIP + array([[ 1, 2, 3], + [ 4, 99, 6], + [ 7, 8, 9]], dtype=int8) + +Whitespace-delimited +------------------------------------------------------------------------------- + +:func:`numpy.genfromtxt` can also parse whitespace-delimited data files +that have missing values if + +* **Each field has a fixed width**: Use the width as the `delimiter` argument. + :: + + # File with width=4. The data does not have to be justified (for example, + # the 2 in row 1), the last column can be less than width (for example, the 6 + # in row 2), and no delimiting character is required (for instance 8888 and 9 + # in row 3) + + >>> f = open("fixedwidth.txt").read() # doctest: +SKIP + >>> print(f) # doctest: +SKIP + 1 2 3 + 44 6 + 7 88889 + + # Showing spaces as ^ + >>> print(f.replace(" ","^")) # doctest: +SKIP + 1^^^2^^^^^^3 + 44^^^^^^6 + 7^^^88889 + + >>> np.genfromtxt("fixedwidth.txt", delimiter=4) # doctest: +SKIP + array([[1.000e+00, 2.000e+00, 3.000e+00], + [4.400e+01, nan, 6.000e+00], + [7.000e+00, 8.888e+03, 9.000e+00]]) + +* **A special value (e.g. "x") indicates a missing field**: Use it as the + `missing_values` argument. + :: + + >>> print(open("nan.txt").read()) # doctest: +SKIP + 1 2 3 + 44 x 6 + 7 8888 9 + + >>> np.genfromtxt("nan.txt", missing_values="x") # doctest: +SKIP + array([[1.000e+00, 2.000e+00, 3.000e+00], + [4.400e+01, nan, 6.000e+00], + [7.000e+00, 8.888e+03, 9.000e+00]]) + +* **You want to skip the rows with missing values**: Set + `invalid_raise=False`. + :: + + >>> print(open("skip.txt").read()) # doctest: +SKIP + 1 2 3 + 44 6 + 7 888 9 + + >>> np.genfromtxt("skip.txt", invalid_raise=False) # doctest: +SKIP + __main__:1: ConversionWarning: Some errors were detected ! + Line #2 (got 2 columns instead of 3) + array([[ 1., 2., 3.], + [ 7., 888., 9.]]) + + +* **The delimiter whitespace character is different from the whitespace that + indicates missing data**. For instance, if columns are delimited by ``\t``, + then missing data will be recognized if it consists of one + or more spaces. + :: + + >>> f = open("tabs.txt").read() # doctest: +SKIP + >>> print(f) # doctest: +SKIP + 1 2 3 + 44 6 + 7 888 9 + + # Tabs vs. spaces + >>> print(f.replace("\t","^")) # doctest: +SKIP + 1^2^3 + 44^ ^6 + 7^888^9 + + >>> np.genfromtxt("tabs.txt", delimiter="\t", missing_values=" +") # doctest: +SKIP + array([[ 1., 2., 3.], + [ 44., nan, 6.], + [ 7., 888., 9.]]) + +****************************************************************************** +Read a file in .npy or .npz format +****************************************************************************** + +Choices: + + - Use :func:`numpy.load`. It can read files generated by any of + :func:`numpy.save`, :func:`numpy.savez`, or :func:`numpy.savez_compressed`. + + - Use memory mapping. See `numpy.lib.format.open_memmap`. + +****************************************************************************** +Write to a file to be read back by NumPy +****************************************************************************** + +Binary +=============================================================================== + +Use +:func:`numpy.save`, or to store multiple arrays :func:`numpy.savez` +or :func:`numpy.savez_compressed`. + +For :ref:`security and portability <how-to-io-pickle-file>`, set +``allow_pickle=False`` unless the dtype contains Python objects, which +requires pickling. + +Masked arrays :any:`can't currently be saved <MaskedArray.tofile>`, +nor can other arbitrary array subclasses. + +Human-readable +============================================================================== + +:func:`numpy.save` and :func:`numpy.savez` create binary files. To **write a +human-readable file**, use :func:`numpy.savetxt`. The array can only be 1- or +2-dimensional, and there's no ` savetxtz` for multiple files. + +Large arrays +============================================================================== + +See :ref:`how-to-io-large-arrays`. + +****************************************************************************** +Read an arbitrarily formatted binary file ("binary blob") +****************************************************************************** + +Use a :doc:`structured array <basics.rec>`. + +**Example:** + +The ``.wav`` file header is a 44-byte block preceding ``data_size`` bytes of the +actual sound data:: + + chunk_id "RIFF" + chunk_size 4-byte unsigned little-endian integer + format "WAVE" + fmt_id "fmt " + fmt_size 4-byte unsigned little-endian integer + audio_fmt 2-byte unsigned little-endian integer + num_channels 2-byte unsigned little-endian integer + sample_rate 4-byte unsigned little-endian integer + byte_rate 4-byte unsigned little-endian integer + block_align 2-byte unsigned little-endian integer + bits_per_sample 2-byte unsigned little-endian integer + data_id "data" + data_size 4-byte unsigned little-endian integer + +The ``.wav`` file header as a NumPy structured dtype:: + + wav_header_dtype = np.dtype([ + ("chunk_id", (bytes, 4)), # flexible-sized scalar type, item size 4 + ("chunk_size", "<u4"), # little-endian unsigned 32-bit integer + ("format", "S4"), # 4-byte string, alternate spelling of (bytes, 4) + ("fmt_id", "S4"), + ("fmt_size", "<u4"), + ("audio_fmt", "<u2"), # + ("num_channels", "<u2"), # .. more of the same ... + ("sample_rate", "<u4"), # + ("byte_rate", "<u4"), + ("block_align", "<u2"), + ("bits_per_sample", "<u2"), + ("data_id", "S4"), + ("data_size", "<u4"), + # + # the sound data itself cannot be represented here: + # it does not have a fixed size + ]) + + header = np.fromfile(f, dtype=wave_header_dtype, count=1)[0] + +This ``.wav`` example is for illustration; to read a ``.wav`` file in real +life, use Python's built-in module :mod:`wave`. + +(Adapted from Pauli Virtanen, :ref:`advanced_numpy`, licensed +under `CC BY 4.0 <https://creativecommons.org/licenses/by/4.0/>`_.) + +.. _how-to-io-large-arrays: + +****************************************************************************** +Write or read large arrays +****************************************************************************** + +**Arrays too large to fit in memory** can be treated like ordinary in-memory +arrays using memory mapping. + +- Raw array data written with :func:`numpy.ndarray.tofile` or + :func:`numpy.ndarray.tobytes` can be read with :func:`numpy.memmap`:: + + array = numpy.memmap("mydata/myarray.arr", mode="r", dtype=np.int16, shape=(1024, 1024)) + +- Files output by :func:`numpy.save` (that is, using the numpy format) can be read + using :func:`numpy.load` with the ``mmap_mode`` keyword argument:: + + large_array[some_slice] = np.load("path/to/small_array", mmap_mode="r") + +Memory mapping lacks features like data chunking and compression; more +full-featured formats and libraries usable with NumPy include: + +* **HDF5**: `h5py <https://www.h5py.org/>`_ or `PyTables <https://www.pytables.org/>`_. +* **Zarr**: `here <https://zarr.readthedocs.io/en/stable/tutorial.html#reading-and-writing-data>`_. +* **NetCDF**: :class:`scipy.io.netcdf_file`. + +For tradeoffs among memmap, Zarr, and HDF5, see +`pythonspeed.com <https://pythonspeed.com/articles/mmap-vs-zarr-hdf5/>`_. + +****************************************************************************** +Write files for reading by other (non-NumPy) tools +****************************************************************************** + +Formats for **exchanging data** with other tools include HDF5, Zarr, and +NetCDF (see :ref:`how-to-io-large-arrays`). + +****************************************************************************** +Write or read a JSON file +****************************************************************************** + +NumPy arrays are **not** directly +`JSON serializable <https://github.com/numpy/numpy/issues/12481>`_. + + +.. _how-to-io-pickle-file: + +****************************************************************************** +Save/restore using a pickle file +****************************************************************************** + +Avoid when possible; :doc:`pickles <python:library/pickle>` are not secure +against erroneous or maliciously constructed data. + +Use :func:`numpy.save` and :func:`numpy.load`. Set ``allow_pickle=False``, +unless the array dtype includes Python objects, in which case pickling is +required. + +****************************************************************************** +Convert from a pandas DataFrame to a NumPy array +****************************************************************************** + +See :meth:`pandas.DataFrame.to_numpy`. + +****************************************************************************** + Save/restore using `~numpy.ndarray.tofile` and `~numpy.fromfile` +****************************************************************************** + +In general, prefer :func:`numpy.save` and :func:`numpy.load`. + +:func:`numpy.ndarray.tofile` and :func:`numpy.fromfile` lose information on +endianness and precision and so are unsuitable for anything but scratch +storage. + diff --git a/doc/source/user/howtos_index.rst b/doc/source/user/howtos_index.rst new file mode 100644 index 000000000000..89a6f54e791c --- /dev/null +++ b/doc/source/user/howtos_index.rst @@ -0,0 +1,15 @@ +.. _howtos: + +################ +NumPy How Tos +################ + +These documents are intended as recipes to common tasks using NumPy. For +detailed reference documentation of the functions and classes contained in +the package, see the :ref:`API reference <reference>`. + +.. toctree:: + :maxdepth: 1 + + how-to-how-to + how-to-io diff --git a/doc/source/user/images/np_MSE_explanation.png b/doc/source/user/images/np_MSE_explanation.png new file mode 100644 index 000000000000..6e20116f580f Binary files /dev/null and b/doc/source/user/images/np_MSE_explanation.png differ diff --git a/doc/source/user/images/np_MSE_explanation2.png b/doc/source/user/images/np_MSE_explanation2.png new file mode 100644 index 000000000000..578e5022b2f0 Binary files /dev/null and b/doc/source/user/images/np_MSE_explanation2.png differ diff --git a/doc/source/user/images/np_MSE_formula.png b/doc/source/user/images/np_MSE_formula.png new file mode 100644 index 000000000000..7e6982995a82 Binary files /dev/null and b/doc/source/user/images/np_MSE_formula.png differ diff --git a/doc/source/user/images/np_MSE_implementation.png b/doc/source/user/images/np_MSE_implementation.png new file mode 100644 index 000000000000..004e82a1fe85 Binary files /dev/null and b/doc/source/user/images/np_MSE_implementation.png differ diff --git a/doc/source/user/images/np_aggregation.png b/doc/source/user/images/np_aggregation.png new file mode 100644 index 000000000000..4356193eb184 Binary files /dev/null and b/doc/source/user/images/np_aggregation.png differ diff --git a/doc/source/user/images/np_array.png b/doc/source/user/images/np_array.png new file mode 100644 index 000000000000..24ba41294406 Binary files /dev/null and b/doc/source/user/images/np_array.png differ diff --git a/doc/source/user/images/np_array_data_ones.png b/doc/source/user/images/np_array_data_ones.png new file mode 100644 index 000000000000..9b49b6e298ac Binary files /dev/null and b/doc/source/user/images/np_array_data_ones.png differ diff --git a/doc/source/user/images/np_array_dataones.png b/doc/source/user/images/np_array_dataones.png new file mode 100644 index 000000000000..d9b13238772a Binary files /dev/null and b/doc/source/user/images/np_array_dataones.png differ diff --git a/doc/source/user/images/np_create_array.png b/doc/source/user/images/np_create_array.png new file mode 100644 index 000000000000..878bad95cf48 Binary files /dev/null and b/doc/source/user/images/np_create_array.png differ diff --git a/doc/source/user/images/np_create_matrix.png b/doc/source/user/images/np_create_matrix.png new file mode 100644 index 000000000000..65e4535e5708 Binary files /dev/null and b/doc/source/user/images/np_create_matrix.png differ diff --git a/doc/source/user/images/np_data_plus_ones.png b/doc/source/user/images/np_data_plus_ones.png new file mode 100644 index 000000000000..b80c2648c2c9 Binary files /dev/null and b/doc/source/user/images/np_data_plus_ones.png differ diff --git a/doc/source/user/images/np_indexing.png b/doc/source/user/images/np_indexing.png new file mode 100644 index 000000000000..863b2d46f66a Binary files /dev/null and b/doc/source/user/images/np_indexing.png differ diff --git a/doc/source/user/images/np_matrix_aggregation.png b/doc/source/user/images/np_matrix_aggregation.png new file mode 100644 index 000000000000..9c2fc511033a Binary files /dev/null and b/doc/source/user/images/np_matrix_aggregation.png differ diff --git a/doc/source/user/images/np_matrix_aggregation_row.png b/doc/source/user/images/np_matrix_aggregation_row.png new file mode 100644 index 000000000000..d474c271f2c1 Binary files /dev/null and b/doc/source/user/images/np_matrix_aggregation_row.png differ diff --git a/doc/source/user/images/np_matrix_arithmetic.png b/doc/source/user/images/np_matrix_arithmetic.png new file mode 100644 index 000000000000..79470254167c Binary files /dev/null and b/doc/source/user/images/np_matrix_arithmetic.png differ diff --git a/doc/source/user/images/np_matrix_broadcasting.png b/doc/source/user/images/np_matrix_broadcasting.png new file mode 100644 index 000000000000..e8102a7d84a0 Binary files /dev/null and b/doc/source/user/images/np_matrix_broadcasting.png differ diff --git a/doc/source/user/images/np_matrix_indexing.png b/doc/source/user/images/np_matrix_indexing.png new file mode 100644 index 000000000000..97f90f11e0c8 Binary files /dev/null and b/doc/source/user/images/np_matrix_indexing.png differ diff --git a/doc/source/user/images/np_mse_viz1.png b/doc/source/user/images/np_mse_viz1.png new file mode 100644 index 000000000000..987a48c795c8 Binary files /dev/null and b/doc/source/user/images/np_mse_viz1.png differ diff --git a/doc/source/user/images/np_mse_viz2.png b/doc/source/user/images/np_mse_viz2.png new file mode 100644 index 000000000000..5594b03e8276 Binary files /dev/null and b/doc/source/user/images/np_mse_viz2.png differ diff --git a/doc/source/user/images/np_multiply_broadcasting.png b/doc/source/user/images/np_multiply_broadcasting.png new file mode 100644 index 000000000000..02337d903a50 Binary files /dev/null and b/doc/source/user/images/np_multiply_broadcasting.png differ diff --git a/doc/source/user/images/np_ones_zeros_matrix.png b/doc/source/user/images/np_ones_zeros_matrix.png new file mode 100644 index 000000000000..9cb54644f7ba Binary files /dev/null and b/doc/source/user/images/np_ones_zeros_matrix.png differ diff --git a/doc/source/user/images/np_ones_zeros_random.png b/doc/source/user/images/np_ones_zeros_random.png new file mode 100644 index 000000000000..17730713fcf9 Binary files /dev/null and b/doc/source/user/images/np_ones_zeros_random.png differ diff --git a/doc/source/user/images/np_pandas.png b/doc/source/user/images/np_pandas.png new file mode 100644 index 000000000000..cc0cd069f61f Binary files /dev/null and b/doc/source/user/images/np_pandas.png differ diff --git a/doc/source/user/images/np_readcsv.png b/doc/source/user/images/np_readcsv.png new file mode 100644 index 000000000000..9d2b9e0a00c6 Binary files /dev/null and b/doc/source/user/images/np_readcsv.png differ diff --git a/doc/source/user/images/np_reshape.png b/doc/source/user/images/np_reshape.png new file mode 100644 index 000000000000..7ebb8d69df0b Binary files /dev/null and b/doc/source/user/images/np_reshape.png differ diff --git a/doc/source/user/images/np_sub_mult_divide.png b/doc/source/user/images/np_sub_mult_divide.png new file mode 100644 index 000000000000..a5df2a687441 Binary files /dev/null and b/doc/source/user/images/np_sub_mult_divide.png differ diff --git a/doc/source/user/images/np_transposing_reshaping.png b/doc/source/user/images/np_transposing_reshaping.png new file mode 100644 index 000000000000..5399043c2771 Binary files /dev/null and b/doc/source/user/images/np_transposing_reshaping.png differ diff --git a/doc/source/user/index.rst b/doc/source/user/index.rst index a45fec9ecb6d..e5c51351e073 100644 --- a/doc/source/user/index.rst +++ b/doc/source/user/index.rst @@ -1,21 +1,43 @@ +:orphan: + .. _user: ################ -NumPy User Guide +NumPy user guide ################ -This guide is intended as an introductory overview of NumPy and -explains how to install and make use of the most important features of -NumPy. For detailed reference documentation of the functions and -classes contained in the package, see the :ref:`reference`. +This guide is an overview and explains the important features; +details are found in :ref:`reference`. .. toctree:: :maxdepth: 1 - setting-up + whatisnumpy + Installation <https://numpy.org/install/> quickstart + absolute_beginners basics misc numpy-for-matlab-users building c-info + NumPy Tutorials <https://numpy.org/numpy-tutorials/features.html> + howtos_index + depending_on_numpy + + +.. Links to these files are placed directly in the top-level html + (doc/source/_templates/indexcontent.html, which appears for the URLs + numpy.org/devdocs and numpy.org/doc/XX) and are not in any toctree, so + we include them here to avoid a "WARNING: document isn't included in any + toctree" message + +.. toctree:: + :hidden: + + ../f2py/index + ../glossary + ../dev/underthehood + ../bugs + ../release + ../license diff --git a/doc/source/user/install.rst b/doc/source/user/install.rst index dd75436458cd..b9425701f352 100644 --- a/doc/source/user/install.rst +++ b/doc/source/user/install.rst @@ -1,10 +1,7 @@ +:orphan: + **************** Installing NumPy **************** -In most use cases the best way to install NumPy on your system is by using a -pre-built package for your operating system. Please see -http://scipy.org/install.html for links to available options. - -For instructions on building for source package, see -:doc:`building`. This information is useful mainly for advanced users. +See `Installing NumPy <https://numpy.org/install/>`_. \ No newline at end of file diff --git a/doc/source/user/misc.rst b/doc/source/user/misc.rst index c10aea48668e..31647315146d 100644 --- a/doc/source/user/misc.rst +++ b/doc/source/user/misc.rst @@ -2,4 +2,225 @@ Miscellaneous ************* -.. automodule:: numpy.doc.misc +IEEE 754 Floating Point Special Values +-------------------------------------- + +Special values defined in numpy: nan, inf, + +NaNs can be used as a poor-man's mask (if you don't care what the +original value was) + +Note: cannot use equality to test NaNs. E.g.: :: + + >>> myarr = np.array([1., 0., np.nan, 3.]) + >>> np.nonzero(myarr == np.nan) + (array([], dtype=int64),) + >>> np.nan == np.nan # is always False! Use special numpy functions instead. + False + >>> myarr[myarr == np.nan] = 0. # doesn't work + >>> myarr + array([ 1., 0., NaN, 3.]) + >>> myarr[np.isnan(myarr)] = 0. # use this instead find + >>> myarr + array([ 1., 0., 0., 3.]) + +Other related special value functions: :: + + isinf(): True if value is inf + isfinite(): True if not nan or inf + nan_to_num(): Map nan to 0, inf to max float, -inf to min float + +The following corresponds to the usual functions except that nans are excluded +from the results: :: + + nansum() + nanmax() + nanmin() + nanargmax() + nanargmin() + + >>> x = np.arange(10.) + >>> x[3] = np.nan + >>> x.sum() + nan + >>> np.nansum(x) + 42.0 + +How numpy handles numerical exceptions +-------------------------------------- + +The default is to ``'warn'`` for ``invalid``, ``divide``, and ``overflow`` +and ``'ignore'`` for ``underflow``. But this can be changed, and it can be +set individually for different kinds of exceptions. The different behaviors +are: + + - 'ignore' : Take no action when the exception occurs. + - 'warn' : Print a `RuntimeWarning` (via the Python `warnings` module). + - 'raise' : Raise a `FloatingPointError`. + - 'call' : Call a function specified using the `seterrcall` function. + - 'print' : Print a warning directly to ``stdout``. + - 'log' : Record error in a Log object specified by `seterrcall`. + +These behaviors can be set for all kinds of errors or specific ones: + + - all : apply to all numeric exceptions + - invalid : when NaNs are generated + - divide : divide by zero (for integers as well!) + - overflow : floating point overflows + - underflow : floating point underflows + +Note that integer divide-by-zero is handled by the same machinery. +These behaviors are set on a per-thread basis. + +Examples +-------- + +:: + + >>> oldsettings = np.seterr(all='warn') + >>> np.zeros(5,dtype=np.float32)/0. + invalid value encountered in divide + >>> j = np.seterr(under='ignore') + >>> np.array([1.e-100])**10 + >>> j = np.seterr(invalid='raise') + >>> np.sqrt(np.array([-1.])) + FloatingPointError: invalid value encountered in sqrt + >>> def errorhandler(errstr, errflag): + ... print("saw stupid error!") + >>> np.seterrcall(errorhandler) + <function err_handler at 0x...> + >>> j = np.seterr(all='call') + >>> np.zeros(5, dtype=np.int32)/0 + FloatingPointError: invalid value encountered in divide + saw stupid error! + >>> j = np.seterr(**oldsettings) # restore previous + ... # error-handling settings + +Interfacing to C +---------------- +Only a survey of the choices. Little detail on how each works. + +1) Bare metal, wrap your own C-code manually. + + - Plusses: + + - Efficient + - No dependencies on other tools + + - Minuses: + + - Lots of learning overhead: + + - need to learn basics of Python C API + - need to learn basics of numpy C API + - need to learn how to handle reference counting and love it. + + - Reference counting often difficult to get right. + + - getting it wrong leads to memory leaks, and worse, segfaults + + - API will change for Python 3.0! + +2) Cython + + - Plusses: + + - avoid learning C API's + - no dealing with reference counting + - can code in pseudo python and generate C code + - can also interface to existing C code + - should shield you from changes to Python C api + - has become the de-facto standard within the scientific Python community + - fast indexing support for arrays + + - Minuses: + + - Can write code in non-standard form which may become obsolete + - Not as flexible as manual wrapping + +3) ctypes + + - Plusses: + + - part of Python standard library + - good for interfacing to existing shareable libraries, particularly + Windows DLLs + - avoids API/reference counting issues + - good numpy support: arrays have all these in their ctypes + attribute: :: + + a.ctypes.data + a.ctypes.data_as + a.ctypes.shape + a.ctypes.shape_as + a.ctypes.strides + a.ctypes.strides_as + + - Minuses: + + - can't use for writing code to be turned into C extensions, only a wrapper + tool. + +4) SWIG (automatic wrapper generator) + + - Plusses: + + - around a long time + - multiple scripting language support + - C++ support + - Good for wrapping large (many functions) existing C libraries + + - Minuses: + + - generates lots of code between Python and the C code + - can cause performance problems that are nearly impossible to optimize + out + - interface files can be hard to write + - doesn't necessarily avoid reference counting issues or needing to know + API's + +5) scipy.weave + + - Plusses: + + - can turn many numpy expressions into C code + - dynamic compiling and loading of generated C code + - can embed pure C code in Python module and have weave extract, generate + interfaces and compile, etc. + + - Minuses: + + - Future very uncertain: it's the only part of Scipy not ported to Python 3 + and is effectively deprecated in favor of Cython. + +6) Psyco + + - Plusses: + + - Turns pure python into efficient machine code through jit-like + optimizations + - very fast when it optimizes well + + - Minuses: + + - Only on intel (windows?) + - Doesn't do much for numpy? + +Interfacing to Fortran: +----------------------- +The clear choice to wrap Fortran code is +`f2py <https://docs.scipy.org/doc/numpy/f2py/>`_. + +Pyfort is an older alternative, but not supported any longer. +Fwrap is a newer project that looked promising but isn't being developed any +longer. + +Interfacing to C++: +------------------- + 1) Cython + 2) CXX + 3) Boost.python + 4) SWIG + 5) SIP (used mainly in PyQT) + + diff --git a/doc/source/user/numpy-for-matlab-users.rst b/doc/source/user/numpy-for-matlab-users.rst index 475c68c04aab..21e23482adeb 100644 --- a/doc/source/user/numpy-for-matlab-users.rst +++ b/doc/source/user/numpy-for-matlab-users.rst @@ -1,18 +1,15 @@ .. _numpy-for-matlab-users: ====================== -NumPy for Matlab users +NumPy for MATLAB users ====================== Introduction ============ -MATLAB® and NumPy/SciPy have a lot in common. But there are many -differences. NumPy and SciPy were created to do numerical and scientific -computing in the most natural way with Python, not to be MATLAB® clones. -This page is intended to be a place to collect wisdom about the -differences, mostly for the purpose of helping proficient MATLAB® users -become proficient NumPy and SciPy users. +MATLAB® and NumPy have a lot in common, but NumPy was created to work with +Python, not to be a MATLAB clone. This guide will help MATLAB users get started +with NumPy. .. raw:: html @@ -20,234 +17,184 @@ become proficient NumPy and SciPy users. table.docutils td { border: solid 1px #ccc; } </style> -Some Key Differences +Some key differences ==================== .. list-table:: - - * - In MATLAB®, the basic data type is a multidimensional array of - double precision floating point numbers. Most expressions take such - arrays and return such arrays. Operations on the 2-D instances of - these arrays are designed to act more or less like matrix operations - in linear algebra. - - In NumPy the basic type is a multidimensional ``array``. Operations - on these arrays in all dimensionalities including 2D are element-wise - operations. One needs to use specific functions for linear algebra - (though for matrix multiplication, one can use the ``@`` operator - in python 3.5 and above). - - * - MATLAB® uses 1 (one) based indexing. The initial element of a - sequence is found using a(1). + :class: docutils + + * - In MATLAB, the basic type, even for scalars, is a + multidimensional array. Array assignments in MATLAB are stored as + 2D arrays of double precision floating point numbers, unless you + specify the number of dimensions and type. Operations on the 2D + instances of these arrays are modeled on matrix operations in + linear algebra. + + - In NumPy, the basic type is a multidimensional ``array``. Array + assignments in NumPy are usually stored as :ref:`n-dimensional arrays<arrays>` with the + minimum type required to hold the objects in sequence, unless you + specify the number of dimensions and type. NumPy performs + operations element-by-element, so multiplying 2D arrays with + ``*`` is not a matrix multiplication -- it's an + element-by-element multiplication. (The ``@`` operator, available + since Python 3.5, can be used for conventional matrix + multiplication.) + + * - MATLAB numbers indices from 1; ``a(1)`` is the first element. :ref:`See note INDEXING <numpy-for-matlab-users.notes>` - - Python uses 0 (zero) based indexing. The initial element of a - sequence is found using a[0]. - - * - MATLAB®'s scripting language was created for doing linear algebra. - The syntax for basic matrix operations is nice and clean, but the API - for adding GUIs and making full-fledged applications is more or less - an afterthought. - - NumPy is based on Python, which was designed from the outset to be - an excellent general-purpose programming language. While Matlab's - syntax for some array manipulations is more compact than - NumPy's, NumPy (by virtue of being an add-on to Python) can do many - things that Matlab just cannot, for instance dealing properly with - stacks of matrices. - - * - In MATLAB®, arrays have pass-by-value semantics, with a lazy - copy-on-write scheme to prevent actually creating copies until they - are actually needed. Slice operations copy parts of the array. - - In NumPy arrays have pass-by-reference semantics. Slice operations - are views into an array. - - -'array' or 'matrix'? Which should I use? -======================================== - -Historically, NumPy has provided a special matrix type, `np.matrix`, which -is a subclass of ndarray which makes binary operations linear algebra -operations. You may see it used in some existing code instead of `np.array`. -So, which one to use? - -Short answer ------------- - -**Use arrays**. - -- They are the standard vector/matrix/tensor type of numpy. Many numpy - functions return arrays, not matrices. -- There is a clear distinction between element-wise operations and - linear algebra operations. -- You can have standard vectors or row/column vectors if you like. - -Until Python 3.5 the only disadvantage of using the array type was that you -had to use ``dot`` instead of ``*`` to multiply (reduce) two tensors -(scalar product, matrix vector multiplication etc.). Since Python 3.5 you -can use the matrix multiplication ``@`` operator. - -Given the above, we intend to deprecate ``matrix`` eventually. - -Long answer ------------ - -NumPy contains both an ``array`` class and a ``matrix`` class. The -``array`` class is intended to be a general-purpose n-dimensional array -for many kinds of numerical computing, while ``matrix`` is intended to -facilitate linear algebra computations specifically. In practice there -are only a handful of key differences between the two. - -- Operators ``*`` and ``@``, functions ``dot()``, and ``multiply()``: - - - For ``array``, **``*`` means element-wise multiplication**, while - **``@`` means matrix multiplication**; they have associated functions - ``multiply()`` and ``dot()``. (Before python 3.5, ``@`` did not exist - and one had to use ``dot()`` for matrix multiplication). - - For ``matrix``, **``*`` means matrix multiplication**, and for - element-wise multiplication one has to use the ``multiply()`` function. + - NumPy, like Python, numbers indices from 0; ``a[0]`` is the first + element. -- Handling of vectors (one-dimensional arrays) - - - For ``array``, the **vector shapes 1xN, Nx1, and N are all different - things**. Operations like ``A[:,1]`` return a one-dimensional array of - shape N, not a two-dimensional array of shape Nx1. Transpose on a - one-dimensional ``array`` does nothing. - - For ``matrix``, **one-dimensional arrays are always upconverted to 1xN - or Nx1 matrices** (row or column vectors). ``A[:,1]`` returns a - two-dimensional matrix of shape Nx1. - -- Handling of higher-dimensional arrays (ndim > 2) - - - ``array`` objects **can have number of dimensions > 2**; - - ``matrix`` objects **always have exactly two dimensions**. - -- Convenience attributes - - - ``array`` **has a .T attribute**, which returns the transpose of - the data. - - ``matrix`` **also has .H, .I, and .A attributes**, which return - the conjugate transpose, inverse, and ``asarray()`` of the matrix, - respectively. - -- Convenience constructor - - - The ``array`` constructor **takes (nested) Python sequences as - initializers**. As in, ``array([[1,2,3],[4,5,6]])``. - - The ``matrix`` constructor additionally **takes a convenient - string initializer**. As in ``matrix("[1 2 3; 4 5 6]")``. - -There are pros and cons to using both: - -- ``array`` - - - ``:)`` Element-wise multiplication is easy: ``A*B``. - - ``:(`` You have to remember that matrix multiplication has its own - operator, ``@``. - - ``:)`` You can treat one-dimensional arrays as *either* row or column - vectors. ``A @ v`` treats ``v`` as a column vector, while - ``v @ A`` treats ``v`` as a row vector. This can save you having to - type a lot of transposes. - - ``:)`` ``array`` is the "default" NumPy type, so it gets the most - testing, and is the type most likely to be returned by 3rd party - code that uses NumPy. - - ``:)`` Is quite at home handling data of any number of dimensions. - - ``:)`` Closer in semantics to tensor algebra, if you are familiar - with that. - - ``:)`` *All* operations (``*``, ``/``, ``+``, ``-`` etc.) are - element-wise. - - ``:(`` Sparse matrices from ``scipy.sparse`` do not interact as well - with arrays. - -- ``matrix`` - - - ``:\\`` Behavior is more like that of MATLAB® matrices. - - ``<:(`` Maximum of two-dimensional. To hold three-dimensional data you - need ``array`` or perhaps a Python list of ``matrix``. - - ``<:(`` Minimum of two-dimensional. You cannot have vectors. They must be - cast as single-column or single-row matrices. - - ``<:(`` Since ``array`` is the default in NumPy, some functions may - return an ``array`` even if you give them a ``matrix`` as an - argument. This shouldn't happen with NumPy functions (if it does - it's a bug), but 3rd party code based on NumPy may not honor type - preservation like NumPy does. - - ``:)`` ``A*B`` is matrix multiplication, so it looks just like you write - it in linear algebra (For Python >= 3.5 plain arrays have the same - convenience with the ``@`` operator). - - ``<:(`` Element-wise multiplication requires calling a function, - ``multiply(A,B)``. - - ``<:(`` The use of operator overloading is a bit illogical: ``*`` - does not work element-wise but ``/`` does. - - Interaction with ``scipy.sparse`` is a bit cleaner. - -The ``array`` is thus much more advisable to use. Indeed, we intend to -deprecate ``matrix`` eventually. - -Table of Rough MATLAB-NumPy Equivalents + * - MATLAB's scripting language was created for linear algebra so the + syntax for some array manipulations is more compact than + NumPy's. On the other hand, the API for adding GUIs and creating + full-fledged applications is more or less an afterthought. + - NumPy is based on Python, a + general-purpose language. The advantage to NumPy + is access to Python libraries including: `SciPy + <https://www.scipy.org/>`_, `Matplotlib <https://matplotlib.org/>`_, + `Pandas <https://pandas.pydata.org/>`_, `OpenCV <https://opencv.org/>`_, + and more. In addition, Python is often `embedded as a scripting language + <https://en.wikipedia.org/wiki/List_of_Python_software#Embedded_as_a_scripting_language>`_ + in other software, allowing NumPy to be used there too. + + * - MATLAB array slicing uses pass-by-value semantics, with a lazy + copy-on-write scheme to prevent creating copies until they are + needed. Slicing operations copy parts of the array. + - NumPy array slicing uses pass-by-reference, that does not copy + the arguments. Slicing operations are views into an array. + + +Rough equivalents ======================================= -The table below gives rough equivalents for some common MATLAB® -expressions. **These are not exact equivalents**, but rather should be -taken as hints to get you going in the right direction. For more detail -read the built-in documentation on the NumPy functions. +The table below gives rough equivalents for some common MATLAB +expressions. These are similar expressions, not equivalents. For +details, see the :ref:`documentation<reference>`. In the table below, it is assumed that you have executed the following commands in Python: :: - from numpy import * - import scipy.linalg + import numpy as np + from scipy import io, integrate, linalg, signal + from scipy.sparse.linalg import eigs Also assume below that if the Notes talk about "matrix" that the arguments are two-dimensional entities. -General Purpose Equivalents +General purpose equivalents --------------------------- .. list-table:: :header-rows: 1 - * - **MATLAB** - - **numpy** - - **Notes** + * - MATLAB + - NumPy + - Notes * - ``help func`` - - ``info(func)`` or ``help(func)`` or ``func?`` (in Ipython) + - ``info(func)`` or ``help(func)`` or ``func?`` (in IPython) - get help on the function *func* * - ``which func`` - - `see note HELP <numpy-for-matlab-users.notes>`__ + - :ref:`see note HELP <numpy-for-matlab-users.notes>` - find out where *func* is defined * - ``type func`` - - ``source(func)`` or ``func??`` (in Ipython) + - ``np.source(func)`` or ``func??`` (in IPython) - print source for *func* (if not a native function) + * - ``% comment`` + - ``# comment`` + - comment a line of code with the text ``comment`` + + * - :: + + for i=1:3 + fprintf('%i\n',i) + end + + - :: + + for i in range(1, 4): + print(i) + + - use a for-loop to print the numbers 1, 2, and 3 using :py:class:`range <range>` + * - ``a && b`` - ``a and b`` - - short-circuiting logical AND operator (Python native operator); + - short-circuiting logical AND operator (:ref:`Python native operator <python:boolean>`); scalar arguments only * - ``a || b`` - ``a or b`` - - short-circuiting logical OR operator (Python native operator); + - short-circuiting logical OR operator (:ref:`Python native operator <python:boolean>`); scalar arguments only + * - .. code:: matlab + + >> 4 == 4 + ans = 1 + >> 4 == 5 + ans = 0 + + - :: + + >>> 4 == 4 + True + >>> 4 == 5 + False + + - The :ref:`boolean objects <python:bltin-boolean-values>` + in Python are ``True`` and ``False``, as opposed to MATLAB + logical types of ``1`` and ``0``. + + * - .. code:: matlab + + a=4 + if a==4 + fprintf('a = 4\n') + elseif a==5 + fprintf('a = 5\n') + end + + - :: + + a = 4 + if a == 4: + print('a = 4') + elif a == 5: + print('a = 5') + + - create an if-else statement to check if ``a`` is 4 or 5 and print result + * - ``1*i``, ``1*j``, ``1i``, ``1j`` - ``1j`` - complex numbers * - ``eps`` - - ``np.spacing(1)`` - - Distance between 1 and the nearest floating point number. + - ``np.finfo(float).eps`` or ``np.spacing(1)`` + - Upper bound to relative error due to rounding in 64-bit floating point + arithmetic. + + * - ``load data.mat`` + - ``io.loadmat('data.mat')`` + - Load MATLAB variables saved to the file ``data.mat``. (Note: When saving arrays to + ``data.mat`` in MATLAB/Octave, use a recent binary format. :func:`scipy.io.loadmat` + will create a dictionary with the saved arrays and further information.) * - ``ode45`` - - ``scipy.integrate.solve_ivp(f)`` + - ``integrate.solve_ivp(f)`` - integrate an ODE with Runge-Kutta 4,5 * - ``ode15s`` - - ``scipy.integrate.solve_ivp(f, method='BDF')`` + - ``integrate.solve_ivp(f, method='BDF')`` - integrate an ODE with BDF method -Linear Algebra Equivalents + +Linear algebra equivalents -------------------------- .. list-table:: @@ -258,63 +205,63 @@ Linear Algebra Equivalents - Notes * - ``ndims(a)`` - - ``ndim(a)`` or ``a.ndim`` - - get the number of dimensions of an array + - ``np.ndim(a)`` or ``a.ndim`` + - number of dimensions of array ``a`` * - ``numel(a)`` - - ``size(a)`` or ``a.size`` - - get the number of elements of an array + - ``np.size(a)`` or ``a.size`` + - number of elements of array ``a`` * - ``size(a)`` - - ``shape(a)`` or ``a.shape`` - - get the "size" of the matrix + - ``np.shape(a)`` or ``a.shape`` + - "size" of array ``a`` * - ``size(a,n)`` - ``a.shape[n-1]`` - get the number of elements of the n-th dimension of array ``a``. (Note - that MATLAB® uses 1 based indexing while Python uses 0 based indexing, + that MATLAB uses 1 based indexing while Python uses 0 based indexing, See note :ref:`INDEXING <numpy-for-matlab-users.notes>`) * - ``[ 1 2 3; 4 5 6 ]`` - - ``array([[1.,2.,3.], [4.,5.,6.]])`` - - 2x3 matrix literal + - ``np.array([[1. ,2. ,3.], [4. ,5. ,6.]])`` + - define a 2x3 2D array * - ``[ a b; c d ]`` - - ``block([[a,b], [c,d]])`` + - ``np.block([[a, b], [c, d]])`` - construct a matrix from blocks ``a``, ``b``, ``c``, and ``d`` * - ``a(end)`` - ``a[-1]`` - - access last element in the 1xn matrix ``a`` + - access last element in MATLAB vector (1xn or nx1) or 1D NumPy array + ``a`` (length n) * - ``a(2,5)`` - - ``a[1,4]`` - - access element in second row, fifth column + - ``a[1, 4]`` + - access element in second row, fifth column in 2D array ``a`` * - ``a(2,:)`` - - ``a[1]`` or ``a[1,:]`` - - entire second row of ``a`` + - ``a[1]`` or ``a[1, :]`` + - entire second row of 2D array ``a`` * - ``a(1:5,:)`` - - ``a[0:5]`` or ``a[:5]`` or ``a[0:5,:]`` - - the first five rows of ``a`` + - ``a[0:5]`` or ``a[:5]`` or ``a[0:5, :]`` + - first 5 rows of 2D array ``a`` * - ``a(end-4:end,:)`` - ``a[-5:]`` - - the last five rows of ``a`` + - last 5 rows of 2D array ``a`` * - ``a(1:3,5:9)`` - - ``a[0:3][:,4:9]`` - - rows one to three and columns five to nine of ``a``. This gives - read-only access. + - ``a[0:3, 4:9]`` + - The first through third rows and fifth through ninth columns of a 2D array, ``a``. * - ``a([2,4,5],[1,3])`` - - ``a[ix_([1,3,4],[0,2])]`` + - ``a[np.ix_([1, 3, 4], [0, 2])]`` - rows 2,4 and 5 and columns 1 and 3. This allows the matrix to be modified, and doesn't require a regular slice. * - ``a(3:2:21,:)`` - - ``a[ 2:21:2,:]`` + - ``a[2:21:2,:]`` - every other row of ``a``, starting with the third and going to the twenty-first @@ -323,11 +270,11 @@ Linear Algebra Equivalents - every other row of ``a``, starting with the first * - ``a(end:-1:1,:)`` or ``flipud(a)`` - - ``a[ ::-1,:]`` + - ``a[::-1,:]`` - ``a`` with rows in reverse order * - ``a([1:end 1],:)`` - - ``a[r_[:len(a),0]]`` + - ``a[np.r_[:len(a),0]]`` - ``a`` with copy of the first row appended to the end * - ``a.'`` @@ -354,30 +301,30 @@ Linear Algebra Equivalents - ``a**3`` - element-wise exponentiation - * - ``(a>0.5)`` - - ``(a>0.5)`` - - matrix whose i,jth element is (a_ij > 0.5). The Matlab result is an - array of 0s and 1s. The NumPy result is an array of the boolean + * - ``(a > 0.5)`` + - ``(a > 0.5)`` + - matrix whose i,jth element is (a_ij > 0.5). The MATLAB result is an + array of logical values 0 and 1. The NumPy result is an array of the boolean values ``False`` and ``True``. - * - ``find(a>0.5)`` - - ``nonzero(a>0.5)`` + * - ``find(a > 0.5)`` + - ``np.nonzero(a > 0.5)`` - find the indices where (``a`` > 0.5) - * - ``a(:,find(v>0.5))`` - - ``a[:,nonzero(v>0.5)[0]]`` - - extract the columms of ``a`` where vector v > 0.5 + * - ``a(:,find(v > 0.5))`` + - ``a[:,np.nonzero(v > 0.5)[0]]`` + - extract the columns of ``a`` where vector v > 0.5 * - ``a(:,find(v>0.5))`` - - ``a[:,v.T>0.5]`` - - extract the columms of ``a`` where column vector v > 0.5 + - ``a[:, v.T > 0.5]`` + - extract the columns of ``a`` where column vector v > 0.5 * - ``a(a<0.5)=0`` - - ``a[a<0.5]=0`` + - ``a[a < 0.5]=0`` - ``a`` with elements less than 0.5 zeroed out * - ``a .* (a>0.5)`` - - ``a * (a>0.5)`` + - ``a * (a > 0.5)`` - ``a`` with elements less than 0.5 zeroed out * - ``a(:) = 3`` @@ -386,73 +333,86 @@ Linear Algebra Equivalents * - ``y=x`` - ``y = x.copy()`` - - numpy assigns by reference + - NumPy assigns by reference * - ``y=x(2,:)`` - - ``y = x[1,:].copy()`` - - numpy slices are by reference + - ``y = x[1, :].copy()`` + - NumPy slices are by reference * - ``y=x(:)`` - ``y = x.flatten()`` - - turn array into vector (note that this forces a copy) + - turn array into vector (note that this forces a copy). To obtain the + same data ordering as in MATLAB, use ``x.flatten('F')``. * - ``1:10`` - - ``arange(1.,11.)`` or ``r_[1.:11.]`` or ``r_[1:10:10j]`` + - ``np.arange(1., 11.)`` or ``np.r_[1.:11.]`` or ``np.r_[1:10:10j]`` - create an increasing vector (see note :ref:`RANGES <numpy-for-matlab-users.notes>`) * - ``0:9`` - - ``arange(10.)`` or ``r_[:10.]`` or ``r_[:9:10j]`` + - ``np.arange(10.)`` or ``np.r_[:10.]`` or ``np.r_[:9:10j]`` - create an increasing vector (see note :ref:`RANGES <numpy-for-matlab-users.notes>`) * - ``[1:10]'`` - - ``arange(1.,11.)[:, newaxis]`` + - ``np.arange(1.,11.)[:, np.newaxis]`` - create a column vector * - ``zeros(3,4)`` - - ``zeros((3,4))`` + - ``np.zeros((3, 4))`` - 3x4 two-dimensional array full of 64-bit floating point zeros * - ``zeros(3,4,5)`` - - ``zeros((3,4,5))`` + - ``np.zeros((3, 4, 5))`` - 3x4x5 three-dimensional array full of 64-bit floating point zeros * - ``ones(3,4)`` - - ``ones((3,4))`` + - ``np.ones((3, 4))`` - 3x4 two-dimensional array full of 64-bit floating point ones * - ``eye(3)`` - - ``eye(3)`` + - ``np.eye(3)`` - 3x3 identity matrix * - ``diag(a)`` - - ``diag(a)`` - - vector of diagonal elements of ``a`` + - ``np.diag(a)`` + - returns a vector of the diagonal elements of 2D array, ``a`` + + * - ``diag(v,0)`` + - ``np.diag(v, 0)`` + - returns a square diagonal matrix whose nonzero values are the elements of + vector, ``v`` - * - ``diag(a,0)`` - - ``diag(a,0)`` - - square diagonal matrix whose nonzero values are the elements of - ``a`` + * - .. code:: matlab + + rng(42,'twister') + rand(3,4) - * - ``rand(3,4)`` - - ``random.rand(3,4)`` - - random 3x4 matrix + - :: + + from numpy.random import default_rng + rng = default_rng(42) + rng.random(3, 4) + + or older version: ``random.rand((3, 4))`` + + - generate a random 3x4 array with default random number generator and + seed = 42 * - ``linspace(1,3,4)`` - - ``linspace(1,3,4)`` + - ``np.linspace(1,3,4)`` - 4 equally spaced samples between 1 and 3, inclusive * - ``[x,y]=meshgrid(0:8,0:5)`` - - ``mgrid[0:9.,0:6.]`` or ``meshgrid(r_[0:9.],r_[0:6.]`` + - ``np.mgrid[0:9.,0:6.]`` or ``np.meshgrid(r_[0:9.],r_[0:6.]`` - two 2D arrays: one of x values, the other of y values * - - - ``ogrid[0:9.,0:6.]`` or ``ix_(r_[0:9.],r_[0:6.]`` + - ``ogrid[0:9.,0:6.]`` or ``np.ix_(np.r_[0:9.],np.r_[0:6.]`` - the best way to eval functions on a grid * - ``[x,y]=meshgrid([1,2,4],[2,4,5])`` - - ``meshgrid([1,2,4],[2,4,5])`` + - ``np.meshgrid([1,2,4],[2,4,5])`` - * - @@ -460,37 +420,38 @@ Linear Algebra Equivalents - the best way to eval functions on a grid * - ``repmat(a, m, n)`` - - ``tile(a, (m, n))`` + - ``np.tile(a, (m, n))`` - create m by n copies of ``a`` * - ``[a b]`` - - ``concatenate((a,b),1)`` or ``hstack((a,b))`` or - ``column_stack((a,b))`` or ``c_[a,b]`` + - ``np.concatenate((a,b),1)`` or ``np.hstack((a,b))`` or + ``np.column_stack((a,b))`` or ``np.c_[a,b]`` - concatenate columns of ``a`` and ``b`` * - ``[a; b]`` - - ``concatenate((a,b))`` or ``vstack((a,b))`` or ``r_[a,b]`` + - ``np.concatenate((a,b))`` or ``np.vstack((a,b))`` or ``np.r_[a,b]`` - concatenate rows of ``a`` and ``b`` * - ``max(max(a))`` - - ``a.max()`` - - maximum element of ``a`` (with ndims(a)<=2 for matlab) + - ``a.max()`` or ``np.nanmax(a)`` + - maximum element of ``a`` (with ndims(a)<=2 for MATLAB, if there are + NaN's, ``nanmax`` will ignore these and return largest value) * - ``max(a)`` - ``a.max(0)`` - - maximum element of each column of matrix ``a`` + - maximum element of each column of array ``a`` * - ``max(a,[],2)`` - ``a.max(1)`` - - maximum element of each row of matrix ``a`` + - maximum element of each row of array ``a`` * - ``max(a,b)`` - - ``maximum(a, b)`` + - ``np.maximum(a, b)`` - compares ``a`` and ``b`` element-wise, and returns the maximum value from each pair * - ``norm(v)`` - - ``sqrt(v @ v)`` or ``np.linalg.norm(v)`` + - ``np.sqrt(v @ v)`` or ``np.linalg.norm(v)`` - L2 norm of vector ``v`` * - ``a & b`` @@ -499,7 +460,7 @@ Linear Algebra Equivalents LOGICOPS <numpy-for-matlab-users.notes>` * - ``a | b`` - - ``logical_or(a,b)`` + - ``np.logical_or(a,b)`` - element-by-element OR operator (NumPy ufunc) :ref:`See note LOGICOPS <numpy-for-matlab-users.notes>` @@ -513,90 +474,99 @@ Linear Algebra Equivalents * - ``inv(a)`` - ``linalg.inv(a)`` - - inverse of square matrix ``a`` + - inverse of square 2D array ``a`` * - ``pinv(a)`` - ``linalg.pinv(a)`` - - pseudo-inverse of matrix ``a`` + - pseudo-inverse of 2D array ``a`` * - ``rank(a)`` - ``linalg.matrix_rank(a)`` - - matrix rank of a 2D array / matrix ``a`` + - matrix rank of a 2D array ``a`` * - ``a\b`` - - ``linalg.solve(a,b)`` if ``a`` is square; ``linalg.lstsq(a,b)`` + - ``linalg.solve(a, b)`` if ``a`` is square; ``linalg.lstsq(a, b)`` otherwise - solution of a x = b for x * - ``b/a`` - - Solve a.T x.T = b.T instead + - Solve ``a.T x.T = b.T`` instead - solution of x a = b for x * - ``[U,S,V]=svd(a)`` - ``U, S, Vh = linalg.svd(a), V = Vh.T`` - singular value decomposition of ``a`` - * - ``chol(a)`` - - ``linalg.cholesky(a).T`` - - cholesky factorization of a matrix (``chol(a)`` in matlab returns an - upper triangular matrix, but ``linalg.cholesky(a)`` returns a lower - triangular matrix) + * - ``c=chol(a)`` where ``a==c'*c`` + - ``c = linalg.cholesky(a)`` where ``a == c@c.T`` + - Cholesky factorization of a 2D array (``chol(a)`` in MATLAB returns an + upper triangular 2D array, but :func:`~scipy.linalg.cholesky` returns a lower + triangular 2D array) * - ``[V,D]=eig(a)`` - ``D,V = linalg.eig(a)`` - - eigenvalues and eigenvectors of ``a`` + - eigenvalues :math:`\lambda` and eigenvectors :math:`\bar{v}` of ``a``, + where :math:`\lambda\bar{v}=\mathbf{a}\bar{v}` * - ``[V,D]=eig(a,b)`` - - ``V,D = np.linalg.eig(a,b)`` - - eigenvalues and eigenvectors of ``a``, ``b`` + - ``D,V = linalg.eig(a, b)`` + - eigenvalues :math:`\lambda` and eigenvectors :math:`\bar{v}` of + ``a``, ``b`` + where :math:`\lambda\mathbf{b}\bar{v}=\mathbf{a}\bar{v}` - * - ``[V,D]=eigs(a,k)`` - - - - find the ``k`` largest eigenvalues and eigenvectors of ``a`` + * - ``[V,D]=eigs(a,3)`` + - ``D,V = eigs(a, k = 3)`` + - find the ``k=3`` largest eigenvalues and eigenvectors of 2D array, ``a`` * - ``[Q,R,P]=qr(a,0)`` - - ``Q,R = scipy.linalg.qr(a)`` + - ``Q,R = linalg.qr(a)`` - QR decomposition - * - ``[L,U,P]=lu(a)`` - - ``L,U = scipy.linalg.lu(a)`` or ``LU,P=scipy.linalg.lu_factor(a)`` - - LU decomposition (note: P(Matlab) == transpose(P(numpy)) ) + * - ``[L,U,P]=lu(a)`` where ``a==P'*L*U`` + - ``P,L,U = linalg.lu(a)`` where ``a == P@L@U`` + - LU decomposition (note: P(MATLAB) == transpose(P(NumPy))) * - ``conjgrad`` - - ``scipy.sparse.linalg.cg`` + - ``cg`` - Conjugate gradients solver * - ``fft(a)`` - - ``fft(a)`` + - ``np.fft(a)`` - Fourier transform of ``a`` * - ``ifft(a)`` - - ``ifft(a)`` + - ``np.ifft(a)`` - inverse Fourier transform of ``a`` * - ``sort(a)`` - - ``sort(a)`` or ``a.sort()`` - - sort the matrix + - ``np.sort(a)`` or ``a.sort(axis=0)`` + - sort each column of a 2D array, ``a`` - * - ``[b,I] = sortrows(a,i)`` - - ``I = argsort(a[:,i]), b=a[I,:]`` - - sort the rows of the matrix + * - ``sort(a, 2)`` + - ``np.sort(a, axis = 1)`` or ``a.sort(axis = 1)`` + - sort the each row of 2D array, ``a`` - * - ``regress(y,X)`` - - ``linalg.lstsq(X,y)`` - - multilinear regression + * - ``[b,I]=sortrows(a,1)`` + - ``I = np.argsort(a[:, 0]); b = a[I,:]`` + - save the array ``a`` as array ``b`` with rows sorted by the first column + + * - ``x = Z\y`` + - ``x = linalg.lstsq(Z, y)`` + - perform a linear regression of the form :math:`\mathbf{Zx}=\mathbf{y}` * - ``decimate(x, q)`` - - ``scipy.signal.resample(x, len(x)/q)`` + - ``signal.resample(x, np.ceil(len(x)/q))`` - downsample with low-pass filtering * - ``unique(a)`` - - ``unique(a)`` - - + - ``np.unique(a)`` + - a vector of unique values in array ``a`` * - ``squeeze(a)`` - ``a.squeeze()`` - - + - remove singleton dimensions of array ``a``. Note that MATLAB will always + return arrays of 2D or higher while NumPy will return arrays of 0D or + higher .. _numpy-for-matlab-users.notes: @@ -604,73 +574,194 @@ Notes ===== \ **Submatrix**: Assignment to a submatrix can be done with lists of -indexes using the ``ix_`` command. E.g., for 2d array ``a``, one might -do: ``ind=[1,3]; a[np.ix_(ind,ind)]+=100``. +indices using the ``ix_`` command. E.g., for 2D array ``a``, one might +do: ``ind=[1, 3]; a[np.ix_(ind, ind)] += 100``. \ **HELP**: There is no direct equivalent of MATLAB's ``which`` command, -but the commands ``help`` and ``source`` will usually list the filename +but the commands :func:`help` and :func:`numpy.source` will usually list the filename where the function is located. Python also has an ``inspect`` module (do ``import inspect``) which provides a ``getfile`` that often works. -\ **INDEXING**: MATLAB® uses one based indexing, so the initial element +\ **INDEXING**: MATLAB uses one based indexing, so the initial element of a sequence has index 1. Python uses zero based indexing, so the initial element of a sequence has index 0. Confusion and flamewars arise because each has advantages and disadvantages. One based indexing is consistent with common human language usage, where the "first" element of a sequence has index 1. Zero based indexing `simplifies -indexing <http://groups.google.com/group/comp.lang.python/msg/1bf4d925dfbf368?q=g:thl3498076713d&hl=en>`__. +indexing <https://groups.google.com/group/comp.lang.python/msg/1bf4d925dfbf368?q=g:thl3498076713d&hl=en>`__. See also `a text by prof.dr. Edsger W. -Dijkstra <http://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html>`__. +Dijkstra <https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html>`__. -\ **RANGES**: In MATLAB®, ``0:5`` can be used as both a range literal +\ **RANGES**: In MATLAB, ``0:5`` can be used as both a range literal and a 'slice' index (inside parentheses); however, in Python, constructs like ``0:5`` can *only* be used as a slice index (inside square brackets). Thus the somewhat quirky ``r_`` object was created to allow -numpy to have a similarly terse range construction mechanism. Note that +NumPy to have a similarly terse range construction mechanism. Note that ``r_`` is not called like a function or a constructor, but rather *indexed* using square brackets, which allows the use of Python's slice syntax in the arguments. -\ **LOGICOPS**: & or \| in NumPy is bitwise AND/OR, while in Matlab & -and \| are logical AND/OR. The difference should be clear to anyone with -significant programming experience. The two can appear to work the same, -but there are important differences. If you would have used Matlab's & -or \| operators, you should use the NumPy ufuncs -logical\_and/logical\_or. The notable differences between Matlab's and -NumPy's & and \| operators are: +\ **LOGICOPS**: ``&`` or ``|`` in NumPy is bitwise AND/OR, while in MATLAB & +and ``|`` are logical AND/OR. The two can appear to work the same, +but there are important differences. If you would have used MATLAB's ``&`` +or ``|`` operators, you should use the NumPy ufuncs +``logical_and``/``logical_or``. The notable differences between MATLAB's and +NumPy's ``&`` and ``|`` operators are: - Non-logical {0,1} inputs: NumPy's output is the bitwise AND of the - inputs. Matlab treats any non-zero value as 1 and returns the logical - AND. For example (3 & 4) in NumPy is 0, while in Matlab both 3 and 4 - are considered logical true and (3 & 4) returns 1. + inputs. MATLAB treats any non-zero value as 1 and returns the logical + AND. For example ``(3 & 4)`` in NumPy is ``0``, while in MATLAB both ``3`` + and ``4`` + are considered logical true and ``(3 & 4)`` returns ``1``. - Precedence: NumPy's & operator is higher precedence than logical - operators like < and >; Matlab's is the reverse. + operators like ``<`` and ``>``; MATLAB's is the reverse. If you know you have boolean arguments, you can get away with using -NumPy's bitwise operators, but be careful with parentheses, like this: z -= (x > 1) & (x < 2). The absence of NumPy operator forms of logical\_and -and logical\_or is an unfortunate consequence of Python's design. +NumPy's bitwise operators, but be careful with parentheses, like this: ``z += (x > 1) & (x < 2)``. The absence of NumPy operator forms of ``logical_and`` +and ``logical_or`` is an unfortunate consequence of Python's design. -**RESHAPE and LINEAR INDEXING**: Matlab always allows multi-dimensional +**RESHAPE and LINEAR INDEXING**: MATLAB always allows multi-dimensional arrays to be accessed using scalar or linear indices, NumPy does not. -Linear indices are common in Matlab programs, e.g. find() on a matrix +Linear indices are common in MATLAB programs, e.g. ``find()`` on a matrix returns them, whereas NumPy's find behaves differently. When converting -Matlab code it might be necessary to first reshape a matrix to a linear +MATLAB code it might be necessary to first reshape a matrix to a linear sequence, perform some indexing operations and then reshape back. As reshape (usually) produces views onto the same storage, it should be possible to do this fairly efficiently. Note that the scan order used by -reshape in NumPy defaults to the 'C' order, whereas Matlab uses the +reshape in NumPy defaults to the 'C' order, whereas MATLAB uses the Fortran order. If you are simply converting to a linear sequence and -back this doesn't matter. But if you are converting reshapes from Matlab -code which relies on the scan order, then this Matlab code: z = -reshape(x,3,4); should become z = x.reshape(3,4,order='F').copy() in +back this doesn't matter. But if you are converting reshapes from MATLAB +code which relies on the scan order, then this MATLAB code: ``z = +reshape(x,3,4);`` should become ``z = x.reshape(3,4,order='F').copy()`` in NumPy. -Customizing Your Environment +'array' or 'matrix'? Which should I use? +======================================== + +Historically, NumPy has provided a special matrix type, `np.matrix`, which +is a subclass of ndarray which makes binary operations linear algebra +operations. You may see it used in some existing code instead of `np.array`. +So, which one to use? + +Short answer +------------ + +**Use arrays**. + +- They support multidimensional array algebra that is supported in MATLAB +- They are the standard vector/matrix/tensor type of NumPy. Many NumPy + functions return arrays, not matrices. +- There is a clear distinction between element-wise operations and + linear algebra operations. +- You can have standard vectors or row/column vectors if you like. + +Until Python 3.5 the only disadvantage of using the array type was that you +had to use ``dot`` instead of ``*`` to multiply (reduce) two tensors +(scalar product, matrix vector multiplication etc.). Since Python 3.5 you +can use the matrix multiplication ``@`` operator. + +Given the above, we intend to deprecate ``matrix`` eventually. + +Long answer +----------- + +NumPy contains both an ``array`` class and a ``matrix`` class. The +``array`` class is intended to be a general-purpose n-dimensional array +for many kinds of numerical computing, while ``matrix`` is intended to +facilitate linear algebra computations specifically. In practice there +are only a handful of key differences between the two. + +- Operators ``*`` and ``@``, functions ``dot()``, and ``multiply()``: + + - For ``array``, **``*`` means element-wise multiplication**, while + **``@`` means matrix multiplication**; they have associated functions + ``multiply()`` and ``dot()``. (Before Python 3.5, ``@`` did not exist + and one had to use ``dot()`` for matrix multiplication). + - For ``matrix``, **``*`` means matrix multiplication**, and for + element-wise multiplication one has to use the ``multiply()`` function. + +- Handling of vectors (one-dimensional arrays) + + - For ``array``, the **vector shapes 1xN, Nx1, and N are all different + things**. Operations like ``A[:,1]`` return a one-dimensional array of + shape N, not a two-dimensional array of shape Nx1. Transpose on a + one-dimensional ``array`` does nothing. + - For ``matrix``, **one-dimensional arrays are always upconverted to 1xN + or Nx1 matrices** (row or column vectors). ``A[:,1]`` returns a + two-dimensional matrix of shape Nx1. + +- Handling of higher-dimensional arrays (ndim > 2) + + - ``array`` objects **can have number of dimensions > 2**; + - ``matrix`` objects **always have exactly two dimensions**. + +- Convenience attributes + + - ``array`` **has a .T attribute**, which returns the transpose of + the data. + - ``matrix`` **also has .H, .I, and .A attributes**, which return + the conjugate transpose, inverse, and ``asarray()`` of the matrix, + respectively. + +- Convenience constructor + + - The ``array`` constructor **takes (nested) Python sequences as + initializers**. As in, ``array([[1,2,3],[4,5,6]])``. + - The ``matrix`` constructor additionally **takes a convenient + string initializer**. As in ``matrix("[1 2 3; 4 5 6]")``. + +There are pros and cons to using both: + +- ``array`` + + - ``:)`` Element-wise multiplication is easy: ``A*B``. + - ``:(`` You have to remember that matrix multiplication has its own + operator, ``@``. + - ``:)`` You can treat one-dimensional arrays as *either* row or column + vectors. ``A @ v`` treats ``v`` as a column vector, while + ``v @ A`` treats ``v`` as a row vector. This can save you having to + type a lot of transposes. + - ``:)`` ``array`` is the "default" NumPy type, so it gets the most + testing, and is the type most likely to be returned by 3rd party + code that uses NumPy. + - ``:)`` Is quite at home handling data of any number of dimensions. + - ``:)`` Closer in semantics to tensor algebra, if you are familiar + with that. + - ``:)`` *All* operations (``*``, ``/``, ``+``, ``-`` etc.) are + element-wise. + - ``:(`` Sparse matrices from ``scipy.sparse`` do not interact as well + with arrays. + +- ``matrix`` + + - ``:\\`` Behavior is more like that of MATLAB matrices. + - ``<:(`` Maximum of two-dimensional. To hold three-dimensional data you + need ``array`` or perhaps a Python list of ``matrix``. + - ``<:(`` Minimum of two-dimensional. You cannot have vectors. They must be + cast as single-column or single-row matrices. + - ``<:(`` Since ``array`` is the default in NumPy, some functions may + return an ``array`` even if you give them a ``matrix`` as an + argument. This shouldn't happen with NumPy functions (if it does + it's a bug), but 3rd party code based on NumPy may not honor type + preservation like NumPy does. + - ``:)`` ``A*B`` is matrix multiplication, so it looks just like you write + it in linear algebra (For Python >= 3.5 plain arrays have the same + convenience with the ``@`` operator). + - ``<:(`` Element-wise multiplication requires calling a function, + ``multiply(A,B)``. + - ``<:(`` The use of operator overloading is a bit illogical: ``*`` + does not work element-wise but ``/`` does. + - Interaction with ``scipy.sparse`` is a bit cleaner. + +The ``array`` is thus much more advisable to use. Indeed, we intend to +deprecate ``matrix`` eventually. + +Customizing your environment ============================ -In MATLAB® the main tool available to you for customizing the +In MATLAB the main tool available to you for customizing the environment is to modify the search path with the locations of your favorite functions. You can put such customizations into a startup script that MATLAB will run on startup. @@ -684,7 +775,7 @@ NumPy, or rather Python, has similar facilities. interpreter is started, define the ``PYTHONSTARTUP`` environment variable to contain the name of your startup script. -Unlike MATLAB®, where anything on your path can be called immediately, +Unlike MATLAB, where anything on your path can be called immediately, with Python you need to first do an 'import' statement to make functions in a particular file accessible. @@ -693,28 +784,41 @@ this is just an example, not a statement of "best practices"): :: - # Make all numpy available via shorter 'num' prefix - import numpy as num - # Make all matlib functions accessible at the top level via M.func() - import numpy.matlib as M - # Make some matlib functions accessible directly at the top level via, e.g. rand(3,3) - from numpy.matlib import rand,zeros,ones,empty,eye + # Make all numpy available via shorter 'np' prefix + import numpy as np + # + # Make the SciPy linear algebra functions available as linalg.func() + # e.g. linalg.lu, linalg.eig (for general l*B@u==A@u solution) + from scipy import linalg + # # Define a Hermitian function def hermitian(A, **kwargs): - return num.transpose(A,**kwargs).conj() - # Make some shortcuts for transpose,hermitian: - # num.transpose(A) --> T(A) + return np.conj(A,**kwargs).T + # Make a shortcut for hermitian: # hermitian(A) --> H(A) - T = num.transpose H = hermitian +To use the deprecated `matrix` and other `matlib` functions: + +:: + + # Make all matlib functions accessible at the top level via M.func() + import numpy.matlib as M + # Make some matlib functions accessible directly at the top level via, e.g. rand(3,3) + from numpy.matlib import matrix,rand,zeros,ones,empty,eye + Links ===== -See http://mathesaurus.sf.net/ for another MATLAB®/NumPy -cross-reference. +Another somewhat outdated MATLAB/NumPy cross-reference can be found at +http://mathesaurus.sf.net/ + +An extensive list of tools for scientific work with Python can be +found in the `topical software page <https://scipy.org/topical-software.html>`__. -An extensive list of tools for scientific work with python can be -found in the `topical software page <http://scipy.org/topical-software.html>`__. +See +`List of Python software: scripting +<https://en.wikipedia.org/wiki/List_of_Python_software#Embedded_as_a_scripting_language>`_ +for a list of software that use Python as a scripting language -MATLAB® and SimuLink® are registered trademarks of The MathWorks. +MATLAB® and SimuLink® are registered trademarks of The MathWorks, Inc. diff --git a/doc/source/user/plot_approx.py b/doc/source/user/plot_approx.py new file mode 100644 index 000000000000..a2d6981d9b85 --- /dev/null +++ b/doc/source/user/plot_approx.py @@ -0,0 +1,19 @@ +from scipy import misc +import matplotlib.pyplot as plt +import numpy as np +from numpy import linalg + +img = misc.face() +img_array = img / 255 +img_gray = img_array @ [0.2126, 0.7152, 0.0722] + +U, s, Vt = linalg.svd(img_gray) + +Sigma = np.zeros((768, 1024)) +for i in range(768): + Sigma[i, i] = s[i] + +k = 10 + +approx = U @ Sigma[:, :k] @ Vt[:k, :] +plt.imshow(approx, cmap="gray") diff --git a/doc/source/user/plot_face.py b/doc/source/user/plot_face.py new file mode 100644 index 000000000000..c0891e770fef --- /dev/null +++ b/doc/source/user/plot_face.py @@ -0,0 +1,5 @@ +from scipy import misc +import matplotlib.pyplot as plt + +img = misc.face() +plt.imshow(img) diff --git a/doc/source/user/plot_final.py b/doc/source/user/plot_final.py new file mode 100644 index 000000000000..10cb097dd97e --- /dev/null +++ b/doc/source/user/plot_final.py @@ -0,0 +1,19 @@ +from scipy import misc +import matplotlib.pyplot as plt +import numpy as np +from numpy import linalg + +img = misc.face() +img_array = img / 255 +img_array_transposed = np.transpose(img_array, (2, 0, 1)) + +U, s, Vt = linalg.svd(img_array_transposed) + +Sigma = np.zeros((3, 768, 1024)) +for j in range(3): + np.fill_diagonal(Sigma[j, :, :], s[j, :]) + +k = 10 + +approx_img = U @ Sigma[..., :k] @ Vt[..., :k, :] +plt.imshow(np.transpose(approx_img, (1, 2, 0))) diff --git a/doc/source/user/plot_gray.py b/doc/source/user/plot_gray.py new file mode 100644 index 000000000000..6cb46bbe461d --- /dev/null +++ b/doc/source/user/plot_gray.py @@ -0,0 +1,8 @@ +from scipy import misc +import matplotlib.pyplot as plt +import numpy as np + +img = misc.face() +img_array = img / 255 +img_gray = img_array @ [0.2126, 0.7152, 0.0722] +plt.imshow(img_gray, cmap="gray") diff --git a/doc/source/user/plot_gray_svd.py b/doc/source/user/plot_gray_svd.py new file mode 100644 index 000000000000..95439939daf8 --- /dev/null +++ b/doc/source/user/plot_gray_svd.py @@ -0,0 +1,16 @@ +from scipy import misc +import matplotlib.pyplot as plt +import numpy as np +from numpy import linalg + +img = misc.face() +img_array = img / 255 +img_gray = img_array @ [0.2126, 0.7152, 0.0722] + +U, s, Vt = linalg.svd(img_gray) + +Sigma = np.zeros((768, 1024)) +for i in range(768): + Sigma[i, i] = s[i] + +plt.plot(s) diff --git a/doc/source/user/plot_reconstructed.py b/doc/source/user/plot_reconstructed.py new file mode 100644 index 000000000000..37cf3c626cd7 --- /dev/null +++ b/doc/source/user/plot_reconstructed.py @@ -0,0 +1,17 @@ +from scipy import misc +import matplotlib.pyplot as plt +import numpy as np +from numpy import linalg + +img = misc.face() +img_array = img / 255 +img_array_transposed = np.transpose(img_array, (2, 0, 1)) + +U, s, Vt = linalg.svd(img_array_transposed) + +Sigma = np.zeros((3, 768, 1024)) +for j in range(3): + np.fill_diagonal(Sigma[j, :, :], s[j, :]) + +reconstructed = U @ Sigma @ Vt +plt.imshow(np.transpose(reconstructed, (1, 2, 0))) diff --git a/doc/source/user/plots/matplotlib1.py b/doc/source/user/plots/matplotlib1.py new file mode 100644 index 000000000000..2cbf87ffa2fa --- /dev/null +++ b/doc/source/user/plots/matplotlib1.py @@ -0,0 +1,7 @@ +import matplotlib.pyplot as plt +import numpy as np + +a = np.array([2, 1, 5, 7, 4, 6, 8, 14, 10, 9, 18, 20, 22]) + +plt.plot(a) +plt.show() \ No newline at end of file diff --git a/doc/source/user/plots/matplotlib2.py b/doc/source/user/plots/matplotlib2.py new file mode 100644 index 000000000000..e15986c2512d --- /dev/null +++ b/doc/source/user/plots/matplotlib2.py @@ -0,0 +1,8 @@ +import matplotlib.pyplot as plt +import numpy as np + +x = np.linspace(0, 5, 20) +y = np.linspace(0, 10, 20) +plt.plot(x, y, 'purple') # line +plt.plot(x, y, 'o') # dots +plt.show() \ No newline at end of file diff --git a/doc/source/user/plots/matplotlib3.py b/doc/source/user/plots/matplotlib3.py new file mode 100644 index 000000000000..7b56067ef463 --- /dev/null +++ b/doc/source/user/plots/matplotlib3.py @@ -0,0 +1,14 @@ +import numpy as np +import matplotlib.pyplot as plt + +fig = plt.figure() +ax = fig.add_subplot(projection='3d') +X = np.arange(-5, 5, 0.15) +Y = np.arange(-5, 5, 0.15) +X, Y = np.meshgrid(X, Y) +R = np.sqrt(X**2 + Y**2) +Z = np.sin(R) + +ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap='viridis') + +plt.show() \ No newline at end of file diff --git a/doc/source/user/quickstart.rst b/doc/source/user/quickstart.rst index 57a7004cc547..a9cfeca31553 100644 --- a/doc/source/user/quickstart.rst +++ b/doc/source/user/quickstart.rst @@ -1,41 +1,60 @@ =================== -Quickstart tutorial +NumPy quickstart =================== .. currentmodule:: numpy .. testsetup:: - import numpy as np - np.random.seed(1) + >>> import numpy as np + >>> import sys Prerequisites ============= -Before reading this tutorial you should know a bit of Python. If you -would like to refresh your memory, take a look at the `Python -tutorial <http://docs.python.org/tut/>`__. +You'll need to know a bit of Python. For a refresher, see the `Python +tutorial <https://docs.python.org/tutorial/>`__. -If you wish to work the examples in this tutorial, you must also have -some software installed on your computer. Please see -http://scipy.org/install.html for instructions. +To work the examples, you'll need ``matplotlib`` installed +in addition to NumPy. + +**Learner profile** + +This is a quick overview of arrays in NumPy. It demonstrates how n-dimensional +(:math:`n>=2`) arrays are represented and can be manipulated. In particular, if +you don't know how to apply common functions to n-dimensional arrays (without +using for-loops), or if you want to understand axis and shape properties for +n-dimensional arrays, this article might be of help. + +**Learning Objectives** + +After reading, you should be able to: + +- Understand the difference between one-, two- and n-dimensional arrays in + NumPy; +- Understand how to apply some linear algebra operations to n-dimensional + arrays without using for-loops; +- Understand axis and shape properties for n-dimensional arrays. + +.. _quickstart.the-basics: The Basics ========== NumPy's main object is the homogeneous multidimensional array. It is a table of elements (usually numbers), all of the same type, indexed by a -tuple of positive integers. In NumPy dimensions are called *axes*. +tuple of non-negative integers. In NumPy dimensions are called *axes*. -For example, the coordinates of a point in 3D space ``[1, 2, 1]`` has -one axis. That axis has 3 elements in it, so we say it has a length -of 3. In the example pictured below, the array has 2 axes. The first -axis has a length of 2, the second axis has a length of 3. +For example, the array for the coordinates of a point in 3D space, +``[1, 2, 1]``, has one axis. That axis has 3 elements in it, so we say +it has a length of 3. In the example pictured below, the array has 2 +axes. The first axis has a length of 2, the second axis has a length of +3. :: - [[ 1., 0., 0.], - [ 0., 1., 2.]] + [[1., 0., 0.], + [0., 1., 2.]] NumPy's array class is called ``ndarray``. It is also known by the alias ``array``. Note that ``numpy.array`` is not the same as the Standard @@ -88,13 +107,14 @@ An example >>> a.size 15 >>> type(a) - <type 'numpy.ndarray'> + <class 'numpy.ndarray'> >>> b = np.array([6, 7, 8]) >>> b array([6, 7, 8]) >>> type(b) - <type 'numpy.ndarray'> + <class 'numpy.ndarray'> +.. _quickstart.array-creation: Array Creation -------------- @@ -108,7 +128,7 @@ from the type of the elements in the sequences. :: >>> import numpy as np - >>> a = np.array([2,3,4]) + >>> a = np.array([2, 3, 4]) >>> a array([2, 3, 4]) >>> a.dtype @@ -117,14 +137,16 @@ from the type of the elements in the sequences. >>> b.dtype dtype('float64') -A frequent error consists in calling ``array`` with multiple numeric -arguments, rather than providing a single list of numbers as an -argument. +A frequent error consists in calling ``array`` with multiple arguments, +rather than providing a single sequence as an argument. :: - >>> a = np.array(1,2,3,4) # WRONG - >>> a = np.array([1,2,3,4]) # RIGHT + >>> a = np.array(1, 2, 3, 4) # WRONG + Traceback (most recent call last): + ... + TypeError: array() takes from 1 to 2 positional arguments but 4 were given + >>> a = np.array([1, 2, 3, 4]) # RIGHT ``array`` transforms sequences of sequences into two-dimensional arrays, sequences of sequences of sequences into three-dimensional arrays, and @@ -132,19 +154,19 @@ so on. :: - >>> b = np.array([(1.5,2,3), (4,5,6)]) + >>> b = np.array([(1.5, 2, 3), (4, 5, 6)]) >>> b - array([[ 1.5, 2. , 3. ], - [ 4. , 5. , 6. ]]) + array([[1.5, 2. , 3. ], + [4. , 5. , 6. ]]) The type of the array can also be explicitly specified at creation time: :: - >>> c = np.array( [ [1,2], [3,4] ], dtype=complex ) + >>> c = np.array([[1, 2], [3, 4]], dtype=complex) >>> c - array([[ 1.+0.j, 2.+0.j], - [ 3.+0.j, 4.+0.j]]) + array([[1.+0.j, 2.+0.j], + [3.+0.j, 4.+0.j]]) Often, the elements of an array are originally unknown, but its size is known. Hence, NumPy offers several functions to create @@ -155,34 +177,36 @@ The function ``zeros`` creates an array full of zeros, the function ``ones`` creates an array full of ones, and the function ``empty`` creates an array whose initial content is random and depends on the state of the memory. By default, the dtype of the created array is -``float64``. +``float64``, but it can be specified via the key word argument ``dtype``. :: - >>> np.zeros( (3,4) ) - array([[ 0., 0., 0., 0.], - [ 0., 0., 0., 0.], - [ 0., 0., 0., 0.]]) - >>> np.ones( (2,3,4), dtype=np.int16 ) # dtype can also be specified - array([[[ 1, 1, 1, 1], - [ 1, 1, 1, 1], - [ 1, 1, 1, 1]], - [[ 1, 1, 1, 1], - [ 1, 1, 1, 1], - [ 1, 1, 1, 1]]], dtype=int16) - >>> np.empty( (2,3) ) # uninitialized, output may vary - array([[ 3.73603959e-262, 6.02658058e-154, 6.55490914e-260], - [ 5.30498948e-313, 3.14673309e-307, 1.00000000e+000]]) - -To create sequences of numbers, NumPy provides a function analogous to -``range`` that returns arrays instead of lists. + >>> np.zeros((3, 4)) + array([[0., 0., 0., 0.], + [0., 0., 0., 0.], + [0., 0., 0., 0.]]) + >>> np.ones((2, 3, 4), dtype=np.int16) + array([[[1, 1, 1, 1], + [1, 1, 1, 1], + [1, 1, 1, 1]], + <BLANKLINE> + [[1, 1, 1, 1], + [1, 1, 1, 1], + [1, 1, 1, 1]]], dtype=int16) + >>> np.empty((2, 3)) + array([[3.73603959e-262, 6.02658058e-154, 6.55490914e-260], # may vary + [5.30498948e-313, 3.14673309e-307, 1.00000000e+000]]) + +To create sequences of numbers, NumPy provides the ``arange`` function +which is analogous to the Python built-in ``range``, but returns an +array. :: - >>> np.arange( 10, 30, 5 ) + >>> np.arange(10, 30, 5) array([10, 15, 20, 25]) - >>> np.arange( 0, 2, 0.3 ) # it accepts float arguments - array([ 0. , 0.3, 0.6, 0.9, 1.2, 1.5, 1.8]) + >>> np.arange(0, 2, 0.3) # it accepts float arguments + array([0. , 0.3, 0.6, 0.9, 1.2, 1.5, 1.8]) When ``arange`` is used with floating point arguments, it is generally not possible to predict the number of elements obtained, due to the @@ -191,9 +215,9 @@ to use the function ``linspace`` that receives as an argument the number of elements that we want, instead of the step:: >>> from numpy import pi - >>> np.linspace( 0, 2, 9 ) # 9 numbers from 0 to 2 - array([ 0. , 0.25, 0.5 , 0.75, 1. , 1.25, 1.5 , 1.75, 2. ]) - >>> x = np.linspace( 0, 2*pi, 100 ) # useful to evaluate function at lots of points + >>> np.linspace(0, 2, 9) # 9 numbers from 0 to 2 + array([0. , 0.25, 0.5 , 0.75, 1. , 1.25, 1.5 , 1.75, 2. ]) + >>> x = np.linspace(0, 2 * pi, 100) # useful to evaluate function at lots of points >>> f = np.sin(x) .. seealso:: @@ -206,8 +230,8 @@ of elements that we want, instead of the step:: `empty_like`, `arange`, `linspace`, - `numpy.random.rand`, - `numpy.random.randn`, + `numpy.random.Generator.rand`, + `numpy.random.Generator.randn`, `fromfunction`, `fromfile` @@ -227,22 +251,23 @@ matrices and tridimensionals as lists of matrices. :: - >>> a = np.arange(6) # 1d array + >>> a = np.arange(6) # 1d array >>> print(a) [0 1 2 3 4 5] - >>> - >>> b = np.arange(12).reshape(4,3) # 2d array + >>> + >>> b = np.arange(12).reshape(4, 3) # 2d array >>> print(b) [[ 0 1 2] [ 3 4 5] [ 6 7 8] [ 9 10 11]] - >>> - >>> c = np.arange(24).reshape(2,3,4) # 3d array + >>> + >>> c = np.arange(24).reshape(2, 3, 4) # 3d array >>> print(c) [[[ 0 1 2 3] [ 4 5 6 7] [ 8 9 10 11]] + <BLANKLINE> [[12 13 14 15] [16 17 18 19] [20 21 22 23]]] @@ -254,25 +279,27 @@ If an array is too large to be printed, NumPy automatically skips the central part of the array and only prints the corners:: >>> print(np.arange(10000)) - [ 0 1 2 ..., 9997 9998 9999] - >>> - >>> print(np.arange(10000).reshape(100,100)) - [[ 0 1 2 ..., 97 98 99] - [ 100 101 102 ..., 197 198 199] - [ 200 201 202 ..., 297 298 299] - ..., - [9700 9701 9702 ..., 9797 9798 9799] - [9800 9801 9802 ..., 9897 9898 9899] - [9900 9901 9902 ..., 9997 9998 9999]] + [ 0 1 2 ... 9997 9998 9999] + >>> + >>> print(np.arange(10000).reshape(100, 100)) + [[ 0 1 2 ... 97 98 99] + [ 100 101 102 ... 197 198 199] + [ 200 201 202 ... 297 298 299] + ... + [9700 9701 9702 ... 9797 9798 9799] + [9800 9801 9802 ... 9897 9898 9899] + [9900 9901 9902 ... 9997 9998 9999]] To disable this behaviour and force NumPy to print the entire array, you can change the printing options using ``set_printoptions``. :: - >>> np.set_printoptions(threshold=np.nan) + >>> np.set_printoptions(threshold=sys.maxsize) # sys module should be imported +.. _quickstart.basic-operations: + Basic Operations ---------------- @@ -281,35 +308,35 @@ created and filled with the result. :: - >>> a = np.array( [20,30,40,50] ) - >>> b = np.arange( 4 ) + >>> a = np.array([20, 30, 40, 50]) + >>> b = np.arange(4) >>> b array([0, 1, 2, 3]) - >>> c = a-b + >>> c = a - b >>> c array([20, 29, 38, 47]) >>> b**2 array([0, 1, 4, 9]) - >>> 10*np.sin(a) + >>> 10 * np.sin(a) array([ 9.12945251, -9.88031624, 7.4511316 , -2.62374854]) - >>> a<35 - array([ True, True, False, False]) + >>> a < 35 + array([ True, True, False, False]) Unlike in many matrix languages, the product operator ``*`` operates elementwise in NumPy arrays. The matrix product can be performed using the ``@`` operator (in python >=3.5) or the ``dot`` function or method:: - >>> A = np.array( [[1,1], - ... [0,1]] ) - >>> B = np.array( [[2,0], - ... [3,4]] ) - >>> A * B # elementwise product + >>> A = np.array([[1, 1], + ... [0, 1]]) + >>> B = np.array([[2, 0], + ... [3, 4]]) + >>> A * B # elementwise product array([[2, 0], [0, 4]]) - >>> A @ B # matrix product + >>> A @ B # matrix product array([[5, 4], [3, 4]]) - >>> A.dot(B) # another matrix product + >>> A.dot(B) # another matrix product array([[5, 4], [3, 4]]) @@ -318,20 +345,21 @@ existing array rather than create a new one. :: - >>> a = np.ones((2,3), dtype=int) - >>> b = np.random.random((2,3)) + >>> rg = np.random.default_rng(1) # create instance of default random number generator + >>> a = np.ones((2, 3), dtype=int) + >>> b = rg.random((2, 3)) >>> a *= 3 >>> a array([[3, 3, 3], [3, 3, 3]]) >>> b += a >>> b - array([[ 3.417022 , 3.72032449, 3.00011437], - [ 3.30233257, 3.14675589, 3.09233859]]) - >>> a += b # b is not automatically converted to integer type + array([[3.51182162, 3.9504637 , 3.14415961], + [3.94864945, 3.31183145, 3.42332645]]) + >>> a += b # b is not automatically converted to integer type Traceback (most recent call last): - ... - TypeError: Cannot cast ufunc add output from dtype('float64') to dtype('int64') with casting rule 'same_kind' + ... + numpy.core._exceptions._UFuncOutputCastingError: Cannot cast ufunc 'add' output from dtype('float64') to dtype('int64') with casting rule 'same_kind' When operating with arrays of different types, the type of the resulting array corresponds to the more general or precise one (a behavior known @@ -340,15 +368,15 @@ as upcasting). :: >>> a = np.ones(3, dtype=np.int32) - >>> b = np.linspace(0,pi,3) + >>> b = np.linspace(0, pi, 3) >>> b.dtype.name 'float64' - >>> c = a+b + >>> c = a + b >>> c - array([ 1. , 2.57079633, 4.14159265]) + array([1. , 2.57079633, 4.14159265]) >>> c.dtype.name 'float64' - >>> d = np.exp(c*1j) + >>> d = np.exp(c * 1j) >>> d array([ 0.54030231+0.84147098j, -0.84147098+0.54030231j, -0.54030231-0.84147098j]) @@ -360,35 +388,35 @@ the array, are implemented as methods of the ``ndarray`` class. :: - >>> a = np.random.random((2,3)) + >>> a = rg.random((2, 3)) >>> a - array([[ 0.18626021, 0.34556073, 0.39676747], - [ 0.53881673, 0.41919451, 0.6852195 ]]) + array([[0.82770259, 0.40919914, 0.54959369], + [0.02755911, 0.75351311, 0.53814331]]) >>> a.sum() - 2.5718191614547998 + 3.1057109529998157 >>> a.min() - 0.1862602113776709 + 0.027559113243068367 >>> a.max() - 0.6852195003967595 + 0.8277025938204418 By default, these operations apply to the array as though it were a list of numbers, regardless of its shape. However, by specifying the ``axis`` parameter you can apply an operation along the specified axis of an array:: - >>> b = np.arange(12).reshape(3,4) + >>> b = np.arange(12).reshape(3, 4) >>> b array([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11]]) >>> - >>> b.sum(axis=0) # sum of each column + >>> b.sum(axis=0) # sum of each column array([12, 15, 18, 21]) >>> - >>> b.min(axis=1) # min of each row + >>> b.min(axis=1) # min of each row array([0, 4, 8]) >>> - >>> b.cumsum(axis=1) # cumulative sum along each row + >>> b.cumsum(axis=1) # cumulative sum along each row array([[ 0, 1, 3, 6], [ 4, 9, 15, 22], [ 8, 17, 27, 38]]) @@ -399,7 +427,7 @@ Universal Functions NumPy provides familiar mathematical functions such as sin, cos, and exp. In NumPy, these are called "universal -functions"(\ ``ufunc``). Within NumPy, these functions +functions" (\ ``ufunc``). Within NumPy, these functions operate elementwise on an array, producing an array as output. :: @@ -408,12 +436,12 @@ operate elementwise on an array, producing an array as output. >>> B array([0, 1, 2]) >>> np.exp(B) - array([ 1. , 2.71828183, 7.3890561 ]) + array([1. , 2.71828183, 7.3890561 ]) >>> np.sqrt(B) - array([ 0. , 1. , 1.41421356]) + array([0. , 1. , 1.41421356]) >>> C = np.array([2., -1., 4.]) >>> np.add(B, C) - array([ 2., 0., 6.]) + array([2., 0., 6.]) .. seealso:: @@ -437,7 +465,7 @@ operate elementwise on an array, producing an array as output. `dot`, `floor`, `inner`, - `inv`, + `invert`, `lexsort`, `max`, `maximum`, @@ -460,6 +488,8 @@ operate elementwise on an array, producing an array as output. `vectorize`, `where` +.. _quickstart.indexing-slicing-and-iterating: + Indexing, Slicing and Iterating ------------------------------- @@ -477,45 +507,48 @@ and other Python sequences. 8 >>> a[2:5] array([ 8, 27, 64]) - >>> a[:6:2] = -1000 # equivalent to a[0:6:2] = -1000; from start to position 6, exclusive, set every 2nd element to -1000 + >>> # equivalent to a[0:6:2] = 1000; + >>> # from start to position 6, exclusive, set every 2nd element to 1000 + >>> a[:6:2] = 1000 >>> a - array([-1000, 1, -1000, 27, -1000, 125, 216, 343, 512, 729]) - >>> a[ : :-1] # reversed a - array([ 729, 512, 343, 216, 125, -1000, 27, -1000, 1, -1000]) + array([1000, 1, 1000, 27, 1000, 125, 216, 343, 512, 729]) + >>> a[::-1] # reversed a + array([ 729, 512, 343, 216, 125, 1000, 27, 1000, 1, 1000]) >>> for i in a: - ... print(i**(1/3.)) + ... print(i**(1 / 3.)) ... - nan + 9.999999999999998 1.0 - nan + 9.999999999999998 3.0 - nan - 5.0 - 6.0 - 7.0 - 8.0 - 9.0 + 9.999999999999998 + 4.999999999999999 + 5.999999999999999 + 6.999999999999999 + 7.999999999999999 + 8.999999999999998 + **Multidimensional** arrays can have one index per axis. These indices are given in a tuple separated by commas:: - >>> def f(x,y): - ... return 10*x+y + >>> def f(x, y): + ... return 10 * x + y ... - >>> b = np.fromfunction(f,(5,4),dtype=int) + >>> b = np.fromfunction(f, (5, 4), dtype=int) >>> b array([[ 0, 1, 2, 3], [10, 11, 12, 13], [20, 21, 22, 23], [30, 31, 32, 33], [40, 41, 42, 43]]) - >>> b[2,3] + >>> b[2, 3] 23 - >>> b[0:5, 1] # each row in the second column of b + >>> b[0:5, 1] # each row in the second column of b array([ 1, 11, 21, 31, 41]) - >>> b[ : ,1] # equivalent to the previous example + >>> b[:, 1] # equivalent to the previous example array([ 1, 11, 21, 31, 41]) - >>> b[1:3, : ] # each column in the second and third row of b + >>> b[1:3, :] # each column in the second and third row of b array([[10, 11, 12, 13], [20, 21, 22, 23]]) @@ -524,34 +557,34 @@ indices are considered complete slices\ ``:`` :: - >>> b[-1] # the last row. Equivalent to b[-1,:] + >>> b[-1] # the last row. Equivalent to b[-1, :] array([40, 41, 42, 43]) The expression within brackets in ``b[i]`` is treated as an ``i`` followed by as many instances of ``:`` as needed to represent the remaining axes. NumPy also allows you to write this using dots as -``b[i,...]``. +``b[i, ...]``. The **dots** (``...``) represent as many colons as needed to produce a complete indexing tuple. For example, if ``x`` is an array with 5 axes, then -- ``x[1,2,...]`` is equivalent to ``x[1,2,:,:,:]``, -- ``x[...,3]`` to ``x[:,:,:,:,3]`` and -- ``x[4,...,5,:]`` to ``x[4,:,:,5,:]``. +- ``x[1, 2, ...]`` is equivalent to ``x[1, 2, :, :, :]``, +- ``x[..., 3]`` to ``x[:, :, :, :, 3]`` and +- ``x[4, ..., 5, :]`` to ``x[4, :, :, 5, :]``. :: - >>> c = np.array( [[[ 0, 1, 2], # a 3D array (two stacked 2D arrays) - ... [ 10, 12, 13]], - ... [[100,101,102], - ... [110,112,113]]]) + >>> c = np.array([[[ 0, 1, 2], # a 3D array (two stacked 2D arrays) + ... [ 10, 12, 13]], + ... [[100, 101, 102], + ... [110, 112, 113]]]) >>> c.shape (2, 2, 3) - >>> c[1,...] # same as c[1,:,:] or c[1] + >>> c[1, ...] # same as c[1, :, :] or c[1] array([[100, 101, 102], [110, 112, 113]]) - >>> c[...,2] # same as c[:,:,2] + >>> c[..., 2] # same as c[:, :, 2] array([[ 2, 13], [102, 113]]) @@ -569,7 +602,7 @@ first axis:: However, if one wants to perform an operation on each element in the array, one can use the ``flat`` attribute which is an -`iterator <https://docs.python.org/2/tutorial/classes.html#iterators>`__ +`iterator <https://docs.python.org/tutorial/classes.html#iterators>`__ over all the elements of the array:: >>> for element in b.flat: @@ -614,11 +647,11 @@ Changing the shape of an array An array has a shape given by the number of elements along each axis:: - >>> a = np.floor(10*np.random.random((3,4))) + >>> a = np.floor(10 * rg.random((3, 4))) >>> a - array([[ 2., 8., 0., 6.], - [ 4., 5., 1., 1.], - [ 8., 9., 3., 6.]]) + array([[3., 7., 3., 4.], + [1., 4., 2., 2.], + [7., 2., 4., 9.]]) >>> a.shape (3, 4) @@ -627,32 +660,32 @@ following three commands all return a modified array, but do not change the original array:: >>> a.ravel() # returns the array, flattened - array([ 2., 8., 0., 6., 4., 5., 1., 1., 8., 9., 3., 6.]) - >>> a.reshape(6,2) # returns the array with a modified shape - array([[ 2., 8.], - [ 0., 6.], - [ 4., 5.], - [ 1., 1.], - [ 8., 9.], - [ 3., 6.]]) + array([3., 7., 3., 4., 1., 4., 2., 2., 7., 2., 4., 9.]) + >>> a.reshape(6, 2) # returns the array with a modified shape + array([[3., 7.], + [3., 4.], + [1., 4.], + [2., 2.], + [7., 2.], + [4., 9.]]) >>> a.T # returns the array, transposed - array([[ 2., 4., 8.], - [ 8., 5., 9.], - [ 0., 1., 3.], - [ 6., 1., 6.]]) + array([[3., 1., 7.], + [7., 4., 2.], + [3., 2., 4.], + [4., 2., 9.]]) >>> a.T.shape (4, 3) >>> a.shape (3, 4) -The order of the elements in the array resulting from ravel() is +The order of the elements in the array resulting from ``ravel`` is normally "C-style", that is, the rightmost index "changes the fastest", -so the element after a[0,0] is a[0,1]. If the array is reshaped to some +so the element after ``a[0, 0]`` is ``a[0, 1]``. If the array is reshaped to some other shape, again the array is treated as "C-style". NumPy normally -creates arrays stored in this order, so ravel() will usually not need to +creates arrays stored in this order, so ``ravel`` will usually not need to copy its argument, but if the array was made by taking slices of another array or created with unusual options, it may need to be copied. The -functions ravel() and reshape() can also be instructed, using an +functions ``ravel`` and ``reshape`` can also be instructed, using an optional argument, to use FORTRAN-style arrays, in which the leftmost index changes the fastest. @@ -662,21 +695,21 @@ argument with a modified shape, whereas the itself:: >>> a - array([[ 2., 8., 0., 6.], - [ 4., 5., 1., 1.], - [ 8., 9., 3., 6.]]) - >>> a.resize((2,6)) + array([[3., 7., 3., 4.], + [1., 4., 2., 2.], + [7., 2., 4., 9.]]) + >>> a.resize((2, 6)) >>> a - array([[ 2., 8., 0., 6., 4., 5.], - [ 1., 1., 8., 9., 3., 6.]]) + array([[3., 7., 3., 4., 1., 4.], + [2., 2., 7., 2., 4., 9.]]) -If a dimension is given as -1 in a reshaping operation, the other +If a dimension is given as ``-1`` in a reshaping operation, the other dimensions are automatically calculated:: - >>> a.reshape(3,-1) - array([[ 2., 8., 0., 6.], - [ 4., 5., 1., 1.], - [ 8., 9., 3., 6.]]) + >>> a.reshape(3, -1) + array([[3., 7., 3., 4.], + [1., 4., 2., 2.], + [7., 2., 4., 9.]]) .. seealso:: @@ -685,56 +718,64 @@ dimensions are automatically calculated:: `resize`, `ravel` + +.. _quickstart.stacking-arrays: + Stacking together different arrays ---------------------------------- Several arrays can be stacked together along different axes:: - >>> a = np.floor(10*np.random.random((2,2))) + >>> a = np.floor(10 * rg.random((2, 2))) >>> a - array([[ 8., 8.], - [ 0., 0.]]) - >>> b = np.floor(10*np.random.random((2,2))) + array([[9., 7.], + [5., 2.]]) + >>> b = np.floor(10 * rg.random((2, 2))) >>> b - array([[ 1., 8.], - [ 0., 4.]]) - >>> np.vstack((a,b)) - array([[ 8., 8.], - [ 0., 0.], - [ 1., 8.], - [ 0., 4.]]) - >>> np.hstack((a,b)) - array([[ 8., 8., 1., 8.], - [ 0., 0., 0., 4.]]) - -The function `column_stack` -stacks 1D arrays as columns into a 2D array. It is equivalent to -`hstack` only for 2D arrays:: + array([[1., 9.], + [5., 1.]]) + >>> np.vstack((a, b)) + array([[9., 7.], + [5., 2.], + [1., 9.], + [5., 1.]]) + >>> np.hstack((a, b)) + array([[9., 7., 1., 9.], + [5., 2., 5., 1.]]) + +The function `column_stack` stacks 1D arrays as columns into a 2D array. +It is equivalent to `hstack` only for 2D arrays:: >>> from numpy import newaxis - >>> np.column_stack((a,b)) # with 2D arrays - array([[ 8., 8., 1., 8.], - [ 0., 0., 0., 4.]]) - >>> a = np.array([4.,2.]) - >>> b = np.array([3.,8.]) - >>> np.column_stack((a,b)) # returns a 2D array - array([[ 4., 3.], - [ 2., 8.]]) - >>> np.hstack((a,b)) # the result is different - array([ 4., 2., 3., 8.]) - >>> a[:,newaxis] # this allows to have a 2D columns vector - array([[ 4.], - [ 2.]]) - >>> np.column_stack((a[:,newaxis],b[:,newaxis])) - array([[ 4., 3.], - [ 2., 8.]]) - >>> np.hstack((a[:,newaxis],b[:,newaxis])) # the result is the same - array([[ 4., 3.], - [ 2., 8.]]) + >>> np.column_stack((a, b)) # with 2D arrays + array([[9., 7., 1., 9.], + [5., 2., 5., 1.]]) + >>> a = np.array([4., 2.]) + >>> b = np.array([3., 8.]) + >>> np.column_stack((a, b)) # returns a 2D array + array([[4., 3.], + [2., 8.]]) + >>> np.hstack((a, b)) # the result is different + array([4., 2., 3., 8.]) + >>> a[:, newaxis] # view `a` as a 2D column vector + array([[4.], + [2.]]) + >>> np.column_stack((a[:, newaxis], b[:, newaxis])) + array([[4., 3.], + [2., 8.]]) + >>> np.hstack((a[:, newaxis], b[:, newaxis])) # the result is the same + array([[4., 3.], + [2., 8.]]) On the other hand, the function `row_stack` is equivalent to `vstack` -for any input arrays. -In general, for arrays of with more than two dimensions, +for any input arrays. In fact, `row_stack` is an alias for `vstack`:: + + >>> np.column_stack is np.hstack + False + >>> np.row_stack is np.vstack + True + +In general, for arrays with more than two dimensions, `hstack` stacks along their second axes, `vstack` stacks along their first axes, and `concatenate` @@ -743,12 +784,10 @@ which the concatenation should happen. **Note** -In complex cases, `r_` and -`c_` are useful for creating arrays -by stacking numbers along one axis. They allow the use of range literals -(":") :: +In complex cases, `r_` and `c_` are useful for creating arrays by stacking +numbers along one axis. They allow the use of range literals ``:``. :: - >>> np.r_[1:4,0,4] + >>> np.r_[1:4, 0, 4] array([1, 2, 3, 0, 4]) When used with arrays as arguments, @@ -776,25 +815,30 @@ array along its horizontal axis, either by specifying the number of equally shaped arrays to return, or by specifying the columns after which the division should occur:: - >>> a = np.floor(10*np.random.random((2,12))) + >>> a = np.floor(10 * rg.random((2, 12))) >>> a - array([[ 9., 5., 6., 3., 6., 8., 0., 7., 9., 7., 2., 7.], - [ 1., 4., 9., 2., 2., 1., 0., 6., 2., 2., 4., 0.]]) - >>> np.hsplit(a,3) # Split a into 3 - [array([[ 9., 5., 6., 3.], - [ 1., 4., 9., 2.]]), array([[ 6., 8., 0., 7.], - [ 2., 1., 0., 6.]]), array([[ 9., 7., 2., 7.], - [ 2., 2., 4., 0.]])] - >>> np.hsplit(a,(3,4)) # Split a after the third and the fourth column - [array([[ 9., 5., 6.], - [ 1., 4., 9.]]), array([[ 3.], - [ 2.]]), array([[ 6., 8., 0., 7., 9., 7., 2., 7.], - [ 2., 1., 0., 6., 2., 2., 4., 0.]])] + array([[6., 7., 6., 9., 0., 5., 4., 0., 6., 8., 5., 2.], + [8., 5., 5., 7., 1., 8., 6., 7., 1., 8., 1., 0.]]) + >>> # Split `a` into 3 + >>> np.hsplit(a, 3) + [array([[6., 7., 6., 9.], + [8., 5., 5., 7.]]), array([[0., 5., 4., 0.], + [1., 8., 6., 7.]]), array([[6., 8., 5., 2.], + [1., 8., 1., 0.]])] + >>> # Split `a` after the third and the fourth column + >>> np.hsplit(a, (3, 4)) + [array([[6., 7., 6.], + [8., 5., 5.]]), array([[9.], + [7.]]), array([[0., 5., 4., 0., 6., 8., 5., 2.], + [1., 8., 6., 7., 1., 8., 1., 0.]])] `vsplit` splits along the vertical axis, and `array_split` allows one to specify along which axis to split. + +.. _quickstart.copies-and-views: + Copies and Views ================ @@ -805,17 +849,16 @@ for beginners. There are three cases: No Copy at All -------------- -Simple assignments make no copy of array objects or of their data. +Simple assignments make no copy of objects or their data. :: - >>> a = np.arange(12) + >>> a = np.array([[ 0, 1, 2, 3], + ... [ 4, 5, 6, 7], + ... [ 8, 9, 10, 11]]) >>> b = a # no new object is created >>> b is a # a and b are two names for the same ndarray object True - >>> b.shape = 3,4 # changes the shape of a - >>> a.shape - (3, 4) Python passes mutable objects as references, so function calls make no copy. @@ -825,10 +868,10 @@ copy. >>> def f(x): ... print(id(x)) ... - >>> id(a) # id is a unique identifier of an object - 148293216 + >>> id(a) # id is a unique identifier of an object + 148293216 # may vary >>> f(a) - 148293216 + 148293216 # may vary View or Shallow Copy -------------------- @@ -841,15 +884,15 @@ creates a new array object that looks at the same data. >>> c = a.view() >>> c is a False - >>> c.base is a # c is a view of the data owned by a + >>> c.base is a # c is a view of the data owned by a True >>> c.flags.owndata False >>> - >>> c.shape = 2,6 # a's shape doesn't change + >>> c = c.reshape((2, 6)) # a's shape doesn't change >>> a.shape (3, 4) - >>> c[0,4] = 1234 # a's data changes + >>> c[0, 4] = 1234 # a's data changes >>> a array([[ 0, 1, 2, 3], [1234, 5, 6, 7], @@ -857,14 +900,13 @@ creates a new array object that looks at the same data. Slicing an array returns a view of it:: - >>> s = a[ : , 1:3] # spaces added for clarity; could also be written "s = a[:,1:3]" - >>> s[:] = 10 # s[:] is a view of s. Note the difference between s=10 and s[:]=10 + >>> s = a[:, 1:3] + >>> s[:] = 10 # s[:] is a view of s. Note the difference between s = 10 and s[:] = 10 >>> a array([[ 0, 10, 10, 3], [1234, 10, 10, 7], [ 8, 10, 10, 11]]) - Deep Copy --------- @@ -872,18 +914,29 @@ The ``copy`` method makes a complete copy of the array and its data. :: - >>> d = a.copy() # a new array object with new data is created + >>> d = a.copy() # a new array object with new data is created >>> d is a False - >>> d.base is a # d doesn't share anything with a + >>> d.base is a # d doesn't share anything with a False - >>> d[0,0] = 9999 + >>> d[0, 0] = 9999 >>> a array([[ 0, 10, 10, 3], [1234, 10, 10, 7], [ 8, 10, 10, 11]]) +Sometimes ``copy`` should be called after slicing if the original array is not required anymore. +For example, suppose ``a`` is a huge intermediate result and the final result ``b`` only contains +a small fraction of ``a``, a deep copy should be made when constructing ``b`` with slicing:: + + >>> a = np.arange(int(1e8)) + >>> b = a[:100].copy() + >>> del a # the memory of ``a`` can be released. + +If ``b = a[:100]`` is used instead, ``a`` is referenced by ``b`` and will persist in memory +even if ``del a`` is executed. + Functions and Methods Overview ------------------------------ @@ -906,7 +959,7 @@ Array Creation `ogrid`, `ones`, `ones_like`, - `r`, + `r_`, `zeros`, `zeros_like` Conversions @@ -978,6 +1031,8 @@ Basic Linear Algebra Less Basic ========== +.. _broadcasting-rules: + Broadcasting rules ------------------ @@ -996,10 +1051,10 @@ element is assumed to be the same along that dimension for the "broadcast" array. After application of the broadcasting rules, the sizes of all arrays -must match. More details can be found in :doc:`basics.broadcasting`. +must match. More details can be found in :ref:`basics.broadcasting`. -Fancy indexing and index tricks -=============================== +Advanced indexing and index tricks +================================== NumPy offers more indexing facilities than regular Python sequences. In addition to indexing by integers and slices, as we saw before, arrays @@ -1010,13 +1065,13 @@ Indexing with Arrays of Indices :: - >>> a = np.arange(12)**2 # the first 12 square numbers - >>> i = np.array( [ 1,1,3,8,5 ] ) # an array of indices - >>> a[i] # the elements of a at the positions i + >>> a = np.arange(12)**2 # the first 12 square numbers + >>> i = np.array([1, 1, 3, 8, 5]) # an array of indices + >>> a[i] # the elements of `a` at the positions `i` array([ 1, 1, 9, 64, 25]) - >>> - >>> j = np.array( [ [ 3, 4], [ 9, 7 ] ] ) # a bidimensional array of indices - >>> a[j] # the same shape as j + >>> + >>> j = np.array([[3, 4], [9, 7]]) # a bidimensional array of indices + >>> a[j] # the same shape as `j` array([[ 9, 16], [81, 49]]) @@ -1027,18 +1082,19 @@ using a palette. :: - >>> palette = np.array( [ [0,0,0], # black - ... [255,0,0], # red - ... [0,255,0], # green - ... [0,0,255], # blue - ... [255,255,255] ] ) # white - >>> image = np.array( [ [ 0, 1, 2, 0 ], # each value corresponds to a color in the palette - ... [ 0, 3, 4, 0 ] ] ) - >>> palette[image] # the (2,4,3) color image + >>> palette = np.array([[0, 0, 0], # black + ... [255, 0, 0], # red + ... [0, 255, 0], # green + ... [0, 0, 255], # blue + ... [255, 255, 255]]) # white + >>> image = np.array([[0, 1, 2, 0], # each value corresponds to a color in the palette + ... [0, 3, 4, 0]]) + >>> palette[image] # the (2, 4, 3) color image array([[[ 0, 0, 0], [255, 0, 0], [ 0, 255, 0], [ 0, 0, 0]], + <BLANKLINE> [[ 0, 0, 0], [ 0, 0, 255], [255, 255, 255], @@ -1049,85 +1105,87 @@ indices for each dimension must have the same shape. :: - >>> a = np.arange(12).reshape(3,4) + >>> a = np.arange(12).reshape(3, 4) >>> a array([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11]]) - >>> i = np.array( [ [0,1], # indices for the first dim of a - ... [1,2] ] ) - >>> j = np.array( [ [2,1], # indices for the second dim - ... [3,3] ] ) - >>> - >>> a[i,j] # i and j must have equal shape + >>> i = np.array([[0, 1], # indices for the first dim of `a` + ... [1, 2]]) + >>> j = np.array([[2, 1], # indices for the second dim + ... [3, 3]]) + >>> + >>> a[i, j] # i and j must have equal shape array([[ 2, 5], [ 7, 11]]) - >>> - >>> a[i,2] + >>> + >>> a[i, 2] array([[ 2, 6], [ 6, 10]]) - >>> - >>> a[:,j] # i.e., a[ : , j] + >>> + >>> a[:, j] array([[[ 2, 1], [ 3, 3]], + <BLANKLINE> [[ 6, 5], [ 7, 7]], + <BLANKLINE> [[10, 9], [11, 11]]]) -Naturally, we can put ``i`` and ``j`` in a sequence (say a list) and -then do the indexing with the list. +In Python, ``arr[i, j]`` is exactly the same as ``arr[(i, j)]``---so we can +put ``i`` and ``j`` in a ``tuple`` and then do the indexing with that. :: - >>> l = [i,j] - >>> a[l] # equivalent to a[i,j] + >>> l = (i, j) + >>> # equivalent to a[i, j] + >>> a[l] array([[ 2, 5], [ 7, 11]]) However, we can not do this by putting ``i`` and ``j`` into an array, because this array will be interpreted as indexing the first dimension -of a. +of ``a``. :: - >>> s = np.array( [i,j] ) - >>> a[s] # not what we want + >>> s = np.array([i, j]) + >>> # not what we want + >>> a[s] Traceback (most recent call last): - File "<stdin>", line 1, in ? - IndexError: index (3) out of range (0<=index<=2) in dimension 0 - >>> - >>> a[tuple(s)] # same as a[i,j] + File "<stdin>", line 1, in <module> + IndexError: index 3 is out of bounds for axis 0 with size 3 + >>> # same as `a[i, j]` + >>> a[tuple(s)] array([[ 2, 5], [ 7, 11]]) Another common use of indexing with arrays is the search of the maximum value of time-dependent series:: - >>> time = np.linspace(20, 145, 5) # time scale - >>> data = np.sin(np.arange(20)).reshape(5,4) # 4 time-dependent series + >>> time = np.linspace(20, 145, 5) # time scale + >>> data = np.sin(np.arange(20)).reshape(5, 4) # 4 time-dependent series >>> time - array([ 20. , 51.25, 82.5 , 113.75, 145. ]) + array([ 20. , 51.25, 82.5 , 113.75, 145. ]) >>> data array([[ 0. , 0.84147098, 0.90929743, 0.14112001], [-0.7568025 , -0.95892427, -0.2794155 , 0.6569866 ], [ 0.98935825, 0.41211849, -0.54402111, -0.99999021], [-0.53657292, 0.42016704, 0.99060736, 0.65028784], [-0.28790332, -0.96139749, -0.75098725, 0.14987721]]) - >>> - >>> ind = data.argmax(axis=0) # index of the maxima for each series + >>> # index of the maxima for each series + >>> ind = data.argmax(axis=0) >>> ind array([2, 0, 3, 1]) - >>> - >>> time_max = time[ind] # times corresponding to the maxima - >>> - >>> data_max = data[ind, range(data.shape[1])] # => data[ind[0],0], data[ind[1],1]... - >>> + >>> # times corresponding to the maxima + >>> time_max = time[ind] + >>> + >>> data_max = data[ind, range(data.shape[1])] # => data[ind[0], 0], data[ind[1], 1]... >>> time_max - array([ 82.5 , 20. , 113.75, 51.25]) + array([ 82.5 , 20. , 113.75, 51.25]) >>> data_max - array([ 0.98935825, 0.84147098, 0.99060736, 0.6569866 ]) - >>> + array([0.98935825, 0.84147098, 0.99060736, 0.6569866 ]) >>> np.all(data_max == data.max(axis=0)) True @@ -1136,7 +1194,7 @@ You can also use indexing with arrays as a target to assign to:: >>> a = np.arange(5) >>> a array([0, 1, 2, 3, 4]) - >>> a[[1,3,4]] = 0 + >>> a[[1, 3, 4]] = 0 >>> a array([0, 0, 2, 0, 0]) @@ -1144,7 +1202,7 @@ However, when the list of indices contains repetitions, the assignment is done several times, leaving behind the last value:: >>> a = np.arange(5) - >>> a[[0,0,2]]=[1,2,3] + >>> a[[0, 0, 2]] = [1, 2, 3] >>> a array([2, 1, 3, 3, 4]) @@ -1152,13 +1210,13 @@ This is reasonable enough, but watch out if you want to use Python's ``+=`` construct, as it may not do what you expect:: >>> a = np.arange(5) - >>> a[[0,0,2]]+=1 + >>> a[[0, 0, 2]] += 1 >>> a array([1, 1, 3, 3, 4]) Even though 0 occurs twice in the list of indices, the 0th element is -only incremented once. This is because Python requires "a+=1" to be -equivalent to "a = a + 1". +only incremented once. This is because Python requires ``a += 1`` to be +equivalent to ``a = a + 1``. Indexing with Boolean Arrays ---------------------------- @@ -1171,18 +1229,18 @@ which ones we don't. The most natural way one can think of for boolean indexing is to use boolean arrays that have *the same shape* as the original array:: - >>> a = np.arange(12).reshape(3,4) + >>> a = np.arange(12).reshape(3, 4) >>> b = a > 4 - >>> b # b is a boolean with a's shape + >>> b # `b` is a boolean with `a`'s shape array([[False, False, False, False], [False, True, True, True], [ True, True, True, True]]) - >>> a[b] # 1d array with the selected elements + >>> a[b] # 1d array with the selected elements array([ 5, 6, 7, 8, 9, 10, 11]) This property can be very useful in assignments:: - >>> a[b] = 0 # All elements of 'a' higher than 4 become 0 + >>> a[b] = 0 # All elements of `a` higher than 4 become 0 >>> a array([[0, 1, 2, 3], [4, 0, 0, 0], @@ -1191,52 +1249,53 @@ This property can be very useful in assignments:: You can look at the following example to see how to use boolean indexing to generate an image of the `Mandelbrot -set <http://en.wikipedia.org/wiki/Mandelbrot_set>`__: +set <https://en.wikipedia.org/wiki/Mandelbrot_set>`__: .. plot:: >>> import numpy as np >>> import matplotlib.pyplot as plt - >>> def mandelbrot( h,w, maxit=20 ): + >>> def mandelbrot(h, w, maxit=20, r=2): ... """Returns an image of the Mandelbrot fractal of size (h,w).""" - ... y,x = np.ogrid[ -1.4:1.4:h*1j, -2:0.8:w*1j ] - ... c = x+y*1j - ... z = c + ... x = np.linspace(-2.5, 1.5, 4*h+1) + ... y = np.linspace(-1.5, 1.5, 3*w+1) + ... A, B = np.meshgrid(x, y) + ... C = A + B*1j + ... z = np.zeros_like(C) ... divtime = maxit + np.zeros(z.shape, dtype=int) ... ... for i in range(maxit): - ... z = z**2 + c - ... diverge = z*np.conj(z) > 2**2 # who is diverging - ... div_now = diverge & (divtime==maxit) # who is diverging now - ... divtime[div_now] = i # note when - ... z[diverge] = 2 # avoid diverging too much + ... z = z**2 + C + ... diverge = abs(z) > r # who is diverging + ... div_now = diverge & (divtime == maxit) # who is diverging now + ... divtime[div_now] = i # note when + ... z[diverge] = r # avoid diverging too much ... ... return divtime - >>> plt.imshow(mandelbrot(400,400)) - >>> plt.show() + >>> plt.imshow(mandelbrot(400, 400)) The second way of indexing with booleans is more similar to integer indexing; for each dimension of the array we give a 1D boolean array selecting the slices we want:: - >>> a = np.arange(12).reshape(3,4) - >>> b1 = np.array([False,True,True]) # first dim selection - >>> b2 = np.array([True,False,True,False]) # second dim selection - >>> - >>> a[b1,:] # selecting rows + >>> a = np.arange(12).reshape(3, 4) + >>> b1 = np.array([False, True, True]) # first dim selection + >>> b2 = np.array([True, False, True, False]) # second dim selection + >>> + >>> a[b1, :] # selecting rows array([[ 4, 5, 6, 7], [ 8, 9, 10, 11]]) - >>> - >>> a[b1] # same thing + >>> + >>> a[b1] # same thing array([[ 4, 5, 6, 7], [ 8, 9, 10, 11]]) - >>> - >>> a[:,b2] # selecting columns + >>> + >>> a[:, b2] # selecting columns array([[ 0, 2], [ 4, 6], [ 8, 10]]) - >>> - >>> a[b1,b2] # a weird thing to do + >>> + >>> a[b1, b2] # a weird thing to do array([ 4, 10]) Note that the length of the 1D boolean array must coincide with the @@ -1253,14 +1312,17 @@ obtain the result for each n-uplet. For example, if you want to compute all the a+b\*c for all the triplets taken from each of the vectors a, b and c:: - >>> a = np.array([2,3,4,5]) - >>> b = np.array([8,5,4]) - >>> c = np.array([5,4,6,8,3]) - >>> ax,bx,cx = np.ix_(a,b,c) + >>> a = np.array([2, 3, 4, 5]) + >>> b = np.array([8, 5, 4]) + >>> c = np.array([5, 4, 6, 8, 3]) + >>> ax, bx, cx = np.ix_(a, b, c) >>> ax array([[[2]], + <BLANKLINE> [[3]], + <BLANKLINE> [[4]], + <BLANKLINE> [[5]]]) >>> bx array([[[8], @@ -1270,23 +1332,26 @@ and c:: array([[[5, 4, 6, 8, 3]]]) >>> ax.shape, bx.shape, cx.shape ((4, 1, 1), (1, 3, 1), (1, 1, 5)) - >>> result = ax+bx*cx + >>> result = ax + bx * cx >>> result array([[[42, 34, 50, 66, 26], [27, 22, 32, 42, 17], [22, 18, 26, 34, 14]], + <BLANKLINE> [[43, 35, 51, 67, 27], [28, 23, 33, 43, 18], [23, 19, 27, 35, 15]], + <BLANKLINE> [[44, 36, 52, 68, 28], [29, 24, 34, 44, 19], [24, 20, 28, 36, 16]], + <BLANKLINE> [[45, 37, 53, 69, 29], [30, 25, 35, 45, 20], [25, 21, 29, 37, 17]]]) - >>> result[3,2,4] + >>> result[3, 2, 4] 17 - >>> a[3]+b[2]*c[4] + >>> a[3] + b[2] * c[4] 17 You could also implement the reduce as follows:: @@ -1295,28 +1360,31 @@ You could also implement the reduce as follows:: ... vs = np.ix_(*vectors) ... r = ufct.identity ... for v in vs: - ... r = ufct(r,v) + ... r = ufct(r, v) ... return r and then use it as:: - >>> ufunc_reduce(np.add,a,b,c) + >>> ufunc_reduce(np.add, a, b, c) array([[[15, 14, 16, 18, 13], [12, 11, 13, 15, 10], [11, 10, 12, 14, 9]], + <BLANKLINE> [[16, 15, 17, 19, 14], [13, 12, 14, 16, 11], [12, 11, 13, 15, 10]], + <BLANKLINE> [[17, 16, 18, 20, 15], [14, 13, 15, 17, 12], [13, 12, 14, 16, 11]], + <BLANKLINE> [[18, 17, 19, 21, 16], [15, 14, 16, 18, 13], [14, 13, 15, 17, 12]]]) The advantage of this version of reduce compared to the normal -ufunc.reduce is that it makes use of the `Broadcasting -Rules <Tentative_NumPy_Tutorial.html#head-c43f3f81719d84f09ae2b33a22eaf50b26333db8>`__ +ufunc.reduce is that it makes use of the +:ref:`broadcasting rules <broadcasting-rules>` in order to avoid creating an argument array the size of the output times the number of vectors. @@ -1325,64 +1393,6 @@ Indexing with strings See :ref:`structured_arrays`. -Linear Algebra -============== - -Work in progress. Basic linear algebra to be included here. - -Simple Array Operations ------------------------ - -See linalg.py in numpy folder for more. - -:: - - >>> import numpy as np - >>> a = np.array([[1.0, 2.0], [3.0, 4.0]]) - >>> print(a) - [[ 1. 2.] - [ 3. 4.]] - - >>> a.transpose() - array([[ 1., 3.], - [ 2., 4.]]) - - >>> np.linalg.inv(a) - array([[-2. , 1. ], - [ 1.5, -0.5]]) - - >>> u = np.eye(2) # unit 2x2 matrix; "eye" represents "I" - >>> u - array([[ 1., 0.], - [ 0., 1.]]) - >>> j = np.array([[0.0, -1.0], [1.0, 0.0]]) - - >>> j @ j # matrix product - array([[-1., 0.], - [ 0., -1.]]) - - >>> np.trace(u) # trace - 2.0 - - >>> y = np.array([[5.], [7.]]) - >>> np.linalg.solve(a, y) - array([[-3.], - [ 4.]]) - - >>> np.linalg.eig(j) - (array([ 0.+1.j, 0.-1.j]), array([[ 0.70710678+0.j , 0.70710678-0.j ], - [ 0.00000000-0.70710678j, 0.00000000+0.70710678j]])) - -:: - - Parameters: - square matrix - Returns - The eigenvalues, each repeated according to its multiplicity. - The normalized (unit "length") eigenvectors, such that the - column ``v[:,i]`` is the eigenvector corresponding to the - eigenvalue ``w[i]`` . - Tricks and Tips =============== @@ -1395,15 +1405,16 @@ To change the dimensions of an array, you can omit one of the sizes which will then be deduced automatically:: >>> a = np.arange(30) - >>> a.shape = 2,-1,3 # -1 means "whatever is needed" - >>> a.shape + >>> b = a.reshape((2, -1, 3)) # -1 means "whatever is needed" + >>> b.shape (2, 5, 3) - >>> a + >>> b array([[[ 0, 1, 2], [ 3, 4, 5], [ 6, 7, 8], [ 9, 10, 11], [12, 13, 14]], + <BLANKLINE> [[15, 16, 17], [18, 19, 20], [21, 22, 23], @@ -1420,11 +1431,15 @@ functions ``column_stack``, ``dstack``, ``hstack`` and ``vstack``, depending on the dimension in which the stacking is to be done. For example:: - x = np.arange(0,10,2) # x=([0,2,4,6,8]) - y = np.arange(5) # y=([0,1,2,3,4]) - m = np.vstack([x,y]) # m=([[0,2,4,6,8], - # [0,1,2,3,4]]) - xy = np.hstack([x,y]) # xy =([0,2,4,6,8,0,1,2,3,4]) + >>> x = np.arange(0, 10, 2) + >>> y = np.arange(5) + >>> m = np.vstack([x, y]) + >>> m + array([[0, 2, 4, 6, 8], + [0, 1, 2, 3, 4]]) + >>> xy = np.hstack([x, y]) + >>> xy + array([0, 2, 4, 6, 8, 0, 1, 2, 3, 4]) The logic behind those functions in more than two dimensions can be strange. @@ -1437,7 +1452,7 @@ Histograms ---------- The NumPy ``histogram`` function applied to an array returns a pair of -vectors: the histogram of the array and the vector of bins. Beware: +vectors: the histogram of the array and a vector of the bin edges. Beware: ``matplotlib`` also has a function to build histograms (called ``hist``, as in Matlab) that differs from the one in NumPy. The main difference is that ``pylab.hist`` plots the histogram automatically, while @@ -1446,24 +1461,26 @@ that ``pylab.hist`` plots the histogram automatically, while .. plot:: >>> import numpy as np + >>> rg = np.random.default_rng(1) >>> import matplotlib.pyplot as plt >>> # Build a vector of 10000 normal deviates with variance 0.5^2 and mean 2 >>> mu, sigma = 2, 0.5 - >>> v = np.random.normal(mu,sigma,10000) + >>> v = rg.normal(mu, sigma, 10000) >>> # Plot a normalized histogram with 50 bins - >>> plt.hist(v, bins=50, density=1) # matplotlib version (plot) - >>> plt.show() + >>> plt.hist(v, bins=50, density=True) # matplotlib version (plot) >>> # Compute the histogram with numpy and then plot it >>> (n, bins) = np.histogram(v, bins=50, density=True) # NumPy version (no plot) - >>> plt.plot(.5*(bins[1:]+bins[:-1]), n) - >>> plt.show() + >>> plt.plot(.5 * (bins[1:] + bins[:-1]), n) + +With Matplotlib >=3.4 you can also use ``plt.stairs(n, bins)``. Further reading =============== -- The `Python tutorial <http://docs.python.org/tutorial/>`__ +- The `Python tutorial <https://docs.python.org/tutorial/>`__ - :ref:`reference` - `SciPy Tutorial <https://docs.scipy.org/doc/scipy/reference/tutorial/index.html>`__ -- `SciPy Lecture Notes <http://www.scipy-lectures.org>`__ +- `SciPy Lecture Notes <https://scipy-lectures.org>`__ - A `matlab, R, IDL, NumPy/SciPy dictionary <http://mathesaurus.sf.net/>`__ +- :doc:`tutorial-svd <content/tutorial-svd>` diff --git a/doc/source/user/setting-up.rst b/doc/source/user/setting-up.rst deleted file mode 100644 index f70dacf82d62..000000000000 --- a/doc/source/user/setting-up.rst +++ /dev/null @@ -1,9 +0,0 @@ -********** -Setting up -********** - -.. toctree:: - :maxdepth: 1 - - whatisnumpy - install diff --git a/doc/source/user/theory.broadcasting.rst b/doc/source/user/theory.broadcasting.rst new file mode 100644 index 000000000000..a4973e4e67f4 --- /dev/null +++ b/doc/source/user/theory.broadcasting.rst @@ -0,0 +1,15 @@ +:orphan: + +=========================== +Array Broadcasting in Numpy +=========================== + +.. + Originally part of the scipy.org wiki, available `here + <https://scipy.github.io/old-wiki/pages/EricsBroadcastingDoc>`_ or from the + `github repo + <https://github.com/scipy/old-wiki/blob/gh-pages/pages/EricsBroadcastingDoc.html>`_ + +.. note:: + Please refer to the updated :doc:`basics.broadcasting` document. + diff --git a/doc/source/user/troubleshooting-importerror.rst b/doc/source/user/troubleshooting-importerror.rst new file mode 100644 index 000000000000..1f99491a1e95 --- /dev/null +++ b/doc/source/user/troubleshooting-importerror.rst @@ -0,0 +1,148 @@ +:orphan: + +.. Reason for orphan: This page is referenced by the installation + instructions, which have moved from Sphinx to https://numpy.org/install. + All install links in Sphinx now point there, leaving no Sphinx references + to this page. + + +*************************** +Troubleshooting ImportError +*************************** + +.. note:: + + Since this information may be updated regularly, please ensure you are + viewing the most `up-to-date version <https://numpy.org/devdocs/user/troubleshooting-importerror.html>`_. + + +ImportError +=========== + +In certain cases a failed installation or setup issue can cause you to +see the following error message:: + + IMPORTANT: PLEASE READ THIS FOR ADVICE ON HOW TO SOLVE THIS ISSUE! + + Importing the numpy c-extensions failed. This error can happen for + different reasons, often due to issues with your setup. + +The error also has additional information to help you troubleshoot: + +* Your Python version +* Your NumPy version + +Please check both of these carefully to see if they are what you expect. +You may need to check your ``PATH`` or ``PYTHONPATH`` environment variables +(see `Check Environment Variables`_ below). + +The following sections list commonly reported issues depending on your setup. +If you have an issue/solution that you think should appear please open a +NumPy issue so that it will be added. + +There are a few commonly reported issues depending on your system/setup. +If none of the following tips help you, please be sure to note the following: + +* how you installed Python +* how you installed NumPy +* your operating system +* whether or not you have multiple versions of Python installed +* if you built from source, your compiler versions and ideally a build log + +when investigating further and asking for support. + + +Using Python from ``conda`` (Anaconda) +-------------------------------------- + +Please make sure that you have activated your conda environment. +See also the `conda user-guide <https://docs.conda.io/projects/conda/en/latest/user-guide/tasks/manage-environments.html#activating-an-environment>`_. +If you use an external editor/development environment it will have to be set +up correctly. See below for solutions for some common setups. + +Using PyCharm with Anaconda/conda Python +---------------------------------------- + +There are fairly common issues when using PyCharm together with Anaconda, +please see the `PyCharm support <https://www.jetbrains.com/help/pycharm/conda-support-creating-conda-virtual-environment.html>`_ + +Using VSCode with Anaconda/conda Python (or environments) +--------------------------------------------------------- + +A commonly reported issue is related to the environment activation within +VSCode. Please see the `VSCode support <https://code.visualstudio.com/docs/python/environments>`_ +for information on how to correctly set up VSCode with virtual environments +or conda. + +Using Eclipse/PyDev with Anaconda/conda Python (or environments) +---------------------------------------------------------------- + +Please see the +`Anaconda Documentation <https://docs.anaconda.com/anaconda/user-guide/tasks/integration/eclipse-pydev/>`_ +on how to properly configure Eclipse/PyDev to use Anaconda Python with specific +conda environments. + + +Raspberry Pi +------------ + +There are sometimes issues reported on Raspberry Pi setups when installing +using ``pip3 install`` (or ``pip`` install). These will typically mention:: + + libf77blas.so.3: cannot open shared object file: No such file or directory + + +The solution will be to either:: + + sudo apt-get install libatlas-base-dev + +to install the missing libraries expected by the self-compiled NumPy +(ATLAS is a possible provider of linear algebra). + +*Alternatively* use the NumPy provided by Raspbian. In which case run:: + + pip3 uninstall numpy # remove previously installed version + apt install python3-numpy + + +Debug build on Windows +---------------------- + +Rather than building your project in ``DEBUG`` mode on windows, try +building in ``RELEASE`` mode with debug symbols and no optimization. +Full ``DEBUG`` mode on windows changes the names of the DLLs python +expects to find, so if you wish to truly work in ``DEBUG`` mode you will +need to recompile the entire stack of python modules you work with +including NumPy + + +All Setups +---------- + +Occasionally there may be simple issues with old or bad installations +of NumPy. In this case you may just try to uninstall and reinstall NumPy. +Make sure that NumPy is not found after uninstalling. + + +Development Setup +----------------- + +If you are using a development setup, make sure to run ``git clean -xdf`` +to delete all files not under version control (be careful not to lose +any modifications you made, e.g. ``site.cfg``). +In many cases files from old builds may lead to incorrect builds. + + +Check Environment Variables +--------------------------- + +In general how to set and check your environment variables depends on +your system. If you can open a correct python shell, you can also run the +following in python:: + + import os + print("PYTHONPATH:", os.environ.get('PYTHONPATH')) + print("PATH:", os.environ.get('PATH')) + +This may mainly help you if you are not running the python and/or NumPy +version you are expecting to run. diff --git a/doc/source/user/whatisnumpy.rst b/doc/source/user/whatisnumpy.rst index cd74a8de3942..e152a4ae2ee5 100644 --- a/doc/source/user/whatisnumpy.rst +++ b/doc/source/user/whatisnumpy.rst @@ -1,3 +1,5 @@ +.. _whatisnumpy: + ************** What is NumPy? ************** @@ -91,6 +93,11 @@ idiom is even simpler! This last example illustrates two of NumPy's features which are the basis of much of its power: vectorization and broadcasting. +.. _whatis-vectorization: + +Why is NumPy Fast? +------------------ + Vectorization describes the absence of any explicit looping, indexing, etc., in the code - these things are taking place, of course, just "behind the scenes" in optimized, pre-compiled C code. Vectorized @@ -118,11 +125,15 @@ same shape, or a scalar and an array, or even two arrays of with different shapes, provided that the smaller array is "expandable" to the shape of the larger in such a way that the resulting broadcast is unambiguous. For detailed "rules" of broadcasting see -`numpy.doc.broadcasting`. +:ref:`Broadcasting <basics.broadcasting>`. + +Who Else Uses NumPy? +-------------------- NumPy fully supports an object-oriented approach, starting, once again, with `ndarray`. For example, `ndarray` is a class, possessing -numerous methods and attributes. Many of its methods mirror -functions in the outer-most NumPy namespace, giving the programmer -complete freedom to code in whichever paradigm she prefers and/or -which seems most appropriate to the task at hand. +numerous methods and attributes. Many of its methods are mirrored by +functions in the outer-most NumPy namespace, allowing the programmer +to code in whichever paradigm they prefer. This flexibility has allowed the +NumPy array dialect and NumPy `ndarray` class to become the *de-facto* language +of multi-dimensional data interchange used in Python. diff --git a/doc/sphinxext b/doc/sphinxext deleted file mode 160000 index de21addd6560..000000000000 --- a/doc/sphinxext +++ /dev/null @@ -1 +0,0 @@ -Subproject commit de21addd6560576151938a4bc59543d55ce6f085 diff --git a/doc/summarize.py b/doc/summarize.py deleted file mode 100755 index cfce2713edf6..000000000000 --- a/doc/summarize.py +++ /dev/null @@ -1,177 +0,0 @@ -#!/usr/bin/env python -""" -summarize.py - -Show a summary about which NumPy functions are documented and which are not. - -""" -from __future__ import division, absolute_import, print_function - -import os, glob, re, sys, inspect, optparse -try: - # Accessing collections abstract classes from collections - # has been deprecated since Python 3.3 - import collections.abc as collections_abc -except ImportError: - import collections as collections_abc -sys.path.append(os.path.join(os.path.dirname(__file__), 'sphinxext')) -from sphinxext.phantom_import import import_phantom_module - -from sphinxext.autosummary_generate import get_documented - -CUR_DIR = os.path.dirname(__file__) -SOURCE_DIR = os.path.join(CUR_DIR, 'source', 'reference') - -SKIP_LIST = """ -# --- aliases: -alltrue sometrue bitwise_not cumproduct -row_stack column_stack product rank - -# -- skipped: -core lib f2py dual doc emath ma rec char distutils oldnumeric numarray -testing version matlib - -add_docstring add_newdoc add_newdocs fastCopyAndTranspose pkgload -conjugate disp - -int0 object0 unicode0 uint0 string_ string0 void0 - -flagsobj - -setup PackageLoader - -lib.scimath.arccos lib.scimath.arcsin lib.scimath.arccosh lib.scimath.arcsinh -lib.scimath.arctanh lib.scimath.log lib.scimath.log2 lib.scimath.log10 -lib.scimath.logn lib.scimath.power lib.scimath.sqrt - -# --- numpy.random: -random random.info random.mtrand random.ranf random.sample random.random - -# --- numpy.fft: -fft fft.Tester fft.bench fft.fftpack fft.fftpack_lite fft.helper -fft.info fft.test - -# --- numpy.linalg: -linalg linalg.Tester -linalg.bench linalg.info linalg.lapack_lite linalg.linalg linalg.test - -# --- numpy.ctypeslib: -ctypeslib ctypeslib.test - -""".split() - -def main(): - p = optparse.OptionParser(__doc__) - p.add_option("-c", "--columns", action="store", type="int", dest="cols", - default=3, help="Maximum number of columns") - options, args = p.parse_args() - - if len(args) != 0: - p.error('Wrong number of arguments') - - # prepare - fn = os.path.join(CUR_DIR, 'dump.xml') - if os.path.isfile(fn): - import_phantom_module(fn) - - # check - documented, undocumented = check_numpy() - - # report - in_sections = {} - for name, locations in documented.items(): - for (filename, section, keyword, toctree) in locations: - in_sections.setdefault((filename, section, keyword), []).append(name) - - print("Documented") - print("==========\n") - - last_filename = None - for (filename, section, keyword), names in sorted(in_sections.items()): - if filename != last_filename: - print("--- %s\n" % filename) - last_filename = filename - print(" ** ", section) - print(format_in_columns(sorted(names), options.cols)) - print("\n") - - print("") - print("Undocumented") - print("============\n") - print(format_in_columns(sorted(undocumented.keys()), options.cols)) - -def check_numpy(): - documented = get_documented(glob.glob(SOURCE_DIR + '/*.rst')) - undocumented = {} - - import numpy, numpy.fft, numpy.linalg, numpy.random - for mod in [numpy, numpy.fft, numpy.linalg, numpy.random, - numpy.ctypeslib, numpy.emath, numpy.ma]: - undocumented.update(get_undocumented(documented, mod, skip=SKIP_LIST)) - - for d in (documented, undocumented): - for k in d.keys(): - if k.startswith('numpy.'): - d[k[6:]] = d[k] - del d[k] - - return documented, undocumented - -def get_undocumented(documented, module, module_name=None, skip=[]): - """ - Find out which items in NumPy are not documented. - - Returns - ------- - undocumented : dict of bool - Dictionary containing True for each documented item name - and False for each undocumented one. - - """ - undocumented = {} - - if module_name is None: - module_name = module.__name__ - - for name in dir(module): - obj = getattr(module, name) - if name.startswith('_'): continue - - full_name = '.'.join([module_name, name]) - - if full_name in skip: continue - if full_name.startswith('numpy.') and full_name[6:] in skip: continue - if not (inspect.ismodule(obj) or isinstance(obj, collections_abc.Callable) or inspect.isclass(obj)): - continue - - if full_name not in documented: - undocumented[full_name] = True - - return undocumented - -def format_in_columns(lst, max_columns): - """ - Format a list containing strings to a string containing the items - in columns. - """ - lst = [str(_m) for _m in lst] - col_len = max([len(_m) for _m in lst]) + 2 - ncols = 80//col_len - if ncols > max_columns: - ncols = max_columns - if ncols <= 0: - ncols = 1 - - if len(lst) % ncols == 0: - nrows = len(lst)//ncols - else: - nrows = 1 + len(lst)//ncols - - fmt = ' %%-%ds ' % (col_len-2) - - lines = [] - for n in range(nrows): - lines.append("".join([fmt % x for x in lst[n::nrows]])) - return "\n".join(lines) - -if __name__ == "__main__": main() diff --git a/doc/ufuncs.rst.txt b/doc/ufuncs.rst.txt index d628b3f95e5d..9257d3cb0f4a 100644 --- a/doc/ufuncs.rst.txt +++ b/doc/ufuncs.rst.txt @@ -18,7 +18,7 @@ Some benchmarks show that this results in a significant slow-down The approach is therefore, to loop over the largest-dimension (just like the NO_BUFFER) portion of the code. All arrays will either have N or -1 in this last dimension (or their would be a mis-match error). The +1 in this last dimension (or their would be a mismatch error). The buffer size is B. If N <= B (and only if needed), we copy the entire last-dimension into diff --git a/doc_requirements.txt b/doc_requirements.txt new file mode 100644 index 000000000000..c849efb2d749 --- /dev/null +++ b/doc_requirements.txt @@ -0,0 +1,9 @@ +sphinx==4.2.0 +numpydoc==1.1.0 +ipython +scipy +matplotlib +pandas +pydata-sphinx-theme +breathe +sphinx-panels diff --git a/environment.yml b/environment.yml new file mode 100644 index 000000000000..58305e68171c --- /dev/null +++ b/environment.yml @@ -0,0 +1,37 @@ +# To use: +# +# $ conda env create -f environment.yml # `mamba` works too for this command +# $ conda activate numpy-dev +# +name: numpy-dev +channels: + - conda-forge +dependencies: + - python + - cython + - compilers + - openblas + - nomkl + - setuptools=59.2.0 + # For testing + - pytest + - pytest-cov + - pytest-xdist + - hypothesis + # For type annotations + - mypy=0.930 + # For building docs + - sphinx=4.1.1 + - numpydoc=1.1.0 + - ipython + - scipy + - pandas + - matplotlib + - pydata-sphinx-theme + - breathe + # For linting + - pycodestyle=2.7.0 + - gitpython + # Used in some tests + - cffi + - pytz diff --git a/linter_requirements.txt b/linter_requirements.txt new file mode 100644 index 000000000000..6ed26c5c024b --- /dev/null +++ b/linter_requirements.txt @@ -0,0 +1,2 @@ +pycodestyle==2.8.0 +GitPython==3.1.13 \ No newline at end of file diff --git a/numpy/__init__.cython-30.pxd b/numpy/__init__.cython-30.pxd new file mode 100644 index 000000000000..42a46d0b832b --- /dev/null +++ b/numpy/__init__.cython-30.pxd @@ -0,0 +1,1053 @@ +# NumPy static imports for Cython >= 3.0 +# +# If any of the PyArray_* functions are called, import_array must be +# called first. This is done automatically by Cython 3.0+ if a call +# is not detected inside of the module. +# +# Author: Dag Sverre Seljebotn +# + +from cpython.ref cimport Py_INCREF +from cpython.object cimport PyObject, PyTypeObject, PyObject_TypeCheck +cimport libc.stdio as stdio + + +cdef extern from *: + # Leave a marker that the NumPy declarations came from NumPy itself and not from Cython. + # See https://github.com/cython/cython/issues/3573 + """ + /* Using NumPy API declarations from "numpy/__init__.cython-30.pxd" */ + """ + + +cdef extern from "Python.h": + ctypedef Py_ssize_t Py_intptr_t + +cdef extern from "numpy/arrayobject.h": + ctypedef Py_intptr_t npy_intp + ctypedef size_t npy_uintp + + cdef enum NPY_TYPES: + NPY_BOOL + NPY_BYTE + NPY_UBYTE + NPY_SHORT + NPY_USHORT + NPY_INT + NPY_UINT + NPY_LONG + NPY_ULONG + NPY_LONGLONG + NPY_ULONGLONG + NPY_FLOAT + NPY_DOUBLE + NPY_LONGDOUBLE + NPY_CFLOAT + NPY_CDOUBLE + NPY_CLONGDOUBLE + NPY_OBJECT + NPY_STRING + NPY_UNICODE + NPY_VOID + NPY_DATETIME + NPY_TIMEDELTA + NPY_NTYPES + NPY_NOTYPE + + NPY_INT8 + NPY_INT16 + NPY_INT32 + NPY_INT64 + NPY_INT128 + NPY_INT256 + NPY_UINT8 + NPY_UINT16 + NPY_UINT32 + NPY_UINT64 + NPY_UINT128 + NPY_UINT256 + NPY_FLOAT16 + NPY_FLOAT32 + NPY_FLOAT64 + NPY_FLOAT80 + NPY_FLOAT96 + NPY_FLOAT128 + NPY_FLOAT256 + NPY_COMPLEX32 + NPY_COMPLEX64 + NPY_COMPLEX128 + NPY_COMPLEX160 + NPY_COMPLEX192 + NPY_COMPLEX256 + NPY_COMPLEX512 + + NPY_INTP + + ctypedef enum NPY_ORDER: + NPY_ANYORDER + NPY_CORDER + NPY_FORTRANORDER + NPY_KEEPORDER + + ctypedef enum NPY_CASTING: + NPY_NO_CASTING + NPY_EQUIV_CASTING + NPY_SAFE_CASTING + NPY_SAME_KIND_CASTING + NPY_UNSAFE_CASTING + + ctypedef enum NPY_CLIPMODE: + NPY_CLIP + NPY_WRAP + NPY_RAISE + + ctypedef enum NPY_SCALARKIND: + NPY_NOSCALAR, + NPY_BOOL_SCALAR, + NPY_INTPOS_SCALAR, + NPY_INTNEG_SCALAR, + NPY_FLOAT_SCALAR, + NPY_COMPLEX_SCALAR, + NPY_OBJECT_SCALAR + + ctypedef enum NPY_SORTKIND: + NPY_QUICKSORT + NPY_HEAPSORT + NPY_MERGESORT + + ctypedef enum NPY_SEARCHSIDE: + NPY_SEARCHLEFT + NPY_SEARCHRIGHT + + enum: + # DEPRECATED since NumPy 1.7 ! Do not use in new code! + NPY_C_CONTIGUOUS + NPY_F_CONTIGUOUS + NPY_CONTIGUOUS + NPY_FORTRAN + NPY_OWNDATA + NPY_FORCECAST + NPY_ENSURECOPY + NPY_ENSUREARRAY + NPY_ELEMENTSTRIDES + NPY_ALIGNED + NPY_NOTSWAPPED + NPY_WRITEABLE + NPY_UPDATEIFCOPY + NPY_ARR_HAS_DESCR + + NPY_BEHAVED + NPY_BEHAVED_NS + NPY_CARRAY + NPY_CARRAY_RO + NPY_FARRAY + NPY_FARRAY_RO + NPY_DEFAULT + + NPY_IN_ARRAY + NPY_OUT_ARRAY + NPY_INOUT_ARRAY + NPY_IN_FARRAY + NPY_OUT_FARRAY + NPY_INOUT_FARRAY + + NPY_UPDATE_ALL + + enum: + # Added in NumPy 1.7 to replace the deprecated enums above. + NPY_ARRAY_C_CONTIGUOUS + NPY_ARRAY_F_CONTIGUOUS + NPY_ARRAY_OWNDATA + NPY_ARRAY_FORCECAST + NPY_ARRAY_ENSURECOPY + NPY_ARRAY_ENSUREARRAY + NPY_ARRAY_ELEMENTSTRIDES + NPY_ARRAY_ALIGNED + NPY_ARRAY_NOTSWAPPED + NPY_ARRAY_WRITEABLE + NPY_ARRAY_UPDATEIFCOPY + + NPY_ARRAY_BEHAVED + NPY_ARRAY_BEHAVED_NS + NPY_ARRAY_CARRAY + NPY_ARRAY_CARRAY_RO + NPY_ARRAY_FARRAY + NPY_ARRAY_FARRAY_RO + NPY_ARRAY_DEFAULT + + NPY_ARRAY_IN_ARRAY + NPY_ARRAY_OUT_ARRAY + NPY_ARRAY_INOUT_ARRAY + NPY_ARRAY_IN_FARRAY + NPY_ARRAY_OUT_FARRAY + NPY_ARRAY_INOUT_FARRAY + + NPY_ARRAY_UPDATE_ALL + + cdef enum: + NPY_MAXDIMS + + npy_intp NPY_MAX_ELSIZE + + ctypedef void (*PyArray_VectorUnaryFunc)(void *, void *, npy_intp, void *, void *) + + ctypedef struct PyArray_ArrayDescr: + # shape is a tuple, but Cython doesn't support "tuple shape" + # inside a non-PyObject declaration, so we have to declare it + # as just a PyObject*. + PyObject* shape + + ctypedef struct PyArray_Descr: + pass + + ctypedef class numpy.dtype [object PyArray_Descr, check_size ignore]: + # Use PyDataType_* macros when possible, however there are no macros + # for accessing some of the fields, so some are defined. + cdef PyTypeObject* typeobj + cdef char kind + cdef char type + # Numpy sometimes mutates this without warning (e.g. it'll + # sometimes change "|" to "<" in shared dtype objects on + # little-endian machines). If this matters to you, use + # PyArray_IsNativeByteOrder(dtype.byteorder) instead of + # directly accessing this field. + cdef char byteorder + cdef char flags + cdef int type_num + cdef int itemsize "elsize" + cdef int alignment + cdef object fields + cdef tuple names + # Use PyDataType_HASSUBARRAY to test whether this field is + # valid (the pointer can be NULL). Most users should access + # this field via the inline helper method PyDataType_SHAPE. + cdef PyArray_ArrayDescr* subarray + + ctypedef class numpy.flatiter [object PyArrayIterObject, check_size ignore]: + # Use through macros + pass + + ctypedef class numpy.broadcast [object PyArrayMultiIterObject, check_size ignore]: + # Use through macros + pass + + ctypedef struct PyArrayObject: + # For use in situations where ndarray can't replace PyArrayObject*, + # like PyArrayObject**. + pass + + ctypedef class numpy.ndarray [object PyArrayObject, check_size ignore]: + cdef __cythonbufferdefaults__ = {"mode": "strided"} + + # NOTE: no field declarations since direct access is deprecated since NumPy 1.7 + # Instead, we use properties that map to the corresponding C-API functions. + + @property + cdef inline PyObject* base(self) nogil: + """Returns a borrowed reference to the object owning the data/memory. + """ + return PyArray_BASE(self) + + @property + cdef inline dtype descr(self): + """Returns an owned reference to the dtype of the array. + """ + return <dtype>PyArray_DESCR(self) + + @property + cdef inline int ndim(self) nogil: + """Returns the number of dimensions in the array. + """ + return PyArray_NDIM(self) + + @property + cdef inline npy_intp *shape(self) nogil: + """Returns a pointer to the dimensions/shape of the array. + The number of elements matches the number of dimensions of the array (ndim). + Can return NULL for 0-dimensional arrays. + """ + return PyArray_DIMS(self) + + @property + cdef inline npy_intp *strides(self) nogil: + """Returns a pointer to the strides of the array. + The number of elements matches the number of dimensions of the array (ndim). + """ + return PyArray_STRIDES(self) + + @property + cdef inline npy_intp size(self) nogil: + """Returns the total size (in number of elements) of the array. + """ + return PyArray_SIZE(self) + + @property + cdef inline char* data(self) nogil: + """The pointer to the data buffer as a char*. + This is provided for legacy reasons to avoid direct struct field access. + For new code that needs this access, you probably want to cast the result + of `PyArray_DATA()` instead, which returns a 'void*'. + """ + return PyArray_BYTES(self) + + ctypedef unsigned char npy_bool + + ctypedef signed char npy_byte + ctypedef signed short npy_short + ctypedef signed int npy_int + ctypedef signed long npy_long + ctypedef signed long long npy_longlong + + ctypedef unsigned char npy_ubyte + ctypedef unsigned short npy_ushort + ctypedef unsigned int npy_uint + ctypedef unsigned long npy_ulong + ctypedef unsigned long long npy_ulonglong + + ctypedef float npy_float + ctypedef double npy_double + ctypedef long double npy_longdouble + + ctypedef signed char npy_int8 + ctypedef signed short npy_int16 + ctypedef signed int npy_int32 + ctypedef signed long long npy_int64 + ctypedef signed long long npy_int96 + ctypedef signed long long npy_int128 + + ctypedef unsigned char npy_uint8 + ctypedef unsigned short npy_uint16 + ctypedef unsigned int npy_uint32 + ctypedef unsigned long long npy_uint64 + ctypedef unsigned long long npy_uint96 + ctypedef unsigned long long npy_uint128 + + ctypedef float npy_float32 + ctypedef double npy_float64 + ctypedef long double npy_float80 + ctypedef long double npy_float96 + ctypedef long double npy_float128 + + ctypedef struct npy_cfloat: + float real + float imag + + ctypedef struct npy_cdouble: + double real + double imag + + ctypedef struct npy_clongdouble: + long double real + long double imag + + ctypedef struct npy_complex64: + float real + float imag + + ctypedef struct npy_complex128: + double real + double imag + + ctypedef struct npy_complex160: + long double real + long double imag + + ctypedef struct npy_complex192: + long double real + long double imag + + ctypedef struct npy_complex256: + long double real + long double imag + + ctypedef struct PyArray_Dims: + npy_intp *ptr + int len + + int _import_array() except -1 + # A second definition so _import_array isn't marked as used when we use it here. + # Do not use - subject to change any time. + int __pyx_import_array "_import_array"() except -1 + + # + # Macros from ndarrayobject.h + # + bint PyArray_CHKFLAGS(ndarray m, int flags) nogil + bint PyArray_IS_C_CONTIGUOUS(ndarray arr) nogil + bint PyArray_IS_F_CONTIGUOUS(ndarray arr) nogil + bint PyArray_ISCONTIGUOUS(ndarray m) nogil + bint PyArray_ISWRITEABLE(ndarray m) nogil + bint PyArray_ISALIGNED(ndarray m) nogil + + int PyArray_NDIM(ndarray) nogil + bint PyArray_ISONESEGMENT(ndarray) nogil + bint PyArray_ISFORTRAN(ndarray) nogil + int PyArray_FORTRANIF(ndarray) nogil + + void* PyArray_DATA(ndarray) nogil + char* PyArray_BYTES(ndarray) nogil + + npy_intp* PyArray_DIMS(ndarray) nogil + npy_intp* PyArray_STRIDES(ndarray) nogil + npy_intp PyArray_DIM(ndarray, size_t) nogil + npy_intp PyArray_STRIDE(ndarray, size_t) nogil + + PyObject *PyArray_BASE(ndarray) nogil # returns borrowed reference! + PyArray_Descr *PyArray_DESCR(ndarray) nogil # returns borrowed reference to dtype! + PyArray_Descr *PyArray_DTYPE(ndarray) nogil # returns borrowed reference to dtype! NP 1.7+ alias for descr. + int PyArray_FLAGS(ndarray) nogil + void PyArray_CLEARFLAGS(ndarray, int flags) nogil # Added in NumPy 1.7 + void PyArray_ENABLEFLAGS(ndarray, int flags) nogil # Added in NumPy 1.7 + npy_intp PyArray_ITEMSIZE(ndarray) nogil + int PyArray_TYPE(ndarray arr) nogil + + object PyArray_GETITEM(ndarray arr, void *itemptr) + int PyArray_SETITEM(ndarray arr, void *itemptr, object obj) + + bint PyTypeNum_ISBOOL(int) nogil + bint PyTypeNum_ISUNSIGNED(int) nogil + bint PyTypeNum_ISSIGNED(int) nogil + bint PyTypeNum_ISINTEGER(int) nogil + bint PyTypeNum_ISFLOAT(int) nogil + bint PyTypeNum_ISNUMBER(int) nogil + bint PyTypeNum_ISSTRING(int) nogil + bint PyTypeNum_ISCOMPLEX(int) nogil + bint PyTypeNum_ISPYTHON(int) nogil + bint PyTypeNum_ISFLEXIBLE(int) nogil + bint PyTypeNum_ISUSERDEF(int) nogil + bint PyTypeNum_ISEXTENDED(int) nogil + bint PyTypeNum_ISOBJECT(int) nogil + + bint PyDataType_ISBOOL(dtype) nogil + bint PyDataType_ISUNSIGNED(dtype) nogil + bint PyDataType_ISSIGNED(dtype) nogil + bint PyDataType_ISINTEGER(dtype) nogil + bint PyDataType_ISFLOAT(dtype) nogil + bint PyDataType_ISNUMBER(dtype) nogil + bint PyDataType_ISSTRING(dtype) nogil + bint PyDataType_ISCOMPLEX(dtype) nogil + bint PyDataType_ISPYTHON(dtype) nogil + bint PyDataType_ISFLEXIBLE(dtype) nogil + bint PyDataType_ISUSERDEF(dtype) nogil + bint PyDataType_ISEXTENDED(dtype) nogil + bint PyDataType_ISOBJECT(dtype) nogil + bint PyDataType_HASFIELDS(dtype) nogil + bint PyDataType_HASSUBARRAY(dtype) nogil + + bint PyArray_ISBOOL(ndarray) nogil + bint PyArray_ISUNSIGNED(ndarray) nogil + bint PyArray_ISSIGNED(ndarray) nogil + bint PyArray_ISINTEGER(ndarray) nogil + bint PyArray_ISFLOAT(ndarray) nogil + bint PyArray_ISNUMBER(ndarray) nogil + bint PyArray_ISSTRING(ndarray) nogil + bint PyArray_ISCOMPLEX(ndarray) nogil + bint PyArray_ISPYTHON(ndarray) nogil + bint PyArray_ISFLEXIBLE(ndarray) nogil + bint PyArray_ISUSERDEF(ndarray) nogil + bint PyArray_ISEXTENDED(ndarray) nogil + bint PyArray_ISOBJECT(ndarray) nogil + bint PyArray_HASFIELDS(ndarray) nogil + + bint PyArray_ISVARIABLE(ndarray) nogil + + bint PyArray_SAFEALIGNEDCOPY(ndarray) nogil + bint PyArray_ISNBO(char) nogil # works on ndarray.byteorder + bint PyArray_IsNativeByteOrder(char) nogil # works on ndarray.byteorder + bint PyArray_ISNOTSWAPPED(ndarray) nogil + bint PyArray_ISBYTESWAPPED(ndarray) nogil + + bint PyArray_FLAGSWAP(ndarray, int) nogil + + bint PyArray_ISCARRAY(ndarray) nogil + bint PyArray_ISCARRAY_RO(ndarray) nogil + bint PyArray_ISFARRAY(ndarray) nogil + bint PyArray_ISFARRAY_RO(ndarray) nogil + bint PyArray_ISBEHAVED(ndarray) nogil + bint PyArray_ISBEHAVED_RO(ndarray) nogil + + + bint PyDataType_ISNOTSWAPPED(dtype) nogil + bint PyDataType_ISBYTESWAPPED(dtype) nogil + + bint PyArray_DescrCheck(object) + + bint PyArray_Check(object) + bint PyArray_CheckExact(object) + + # Cannot be supported due to out arg: + # bint PyArray_HasArrayInterfaceType(object, dtype, object, object&) + # bint PyArray_HasArrayInterface(op, out) + + + bint PyArray_IsZeroDim(object) + # Cannot be supported due to ## ## in macro: + # bint PyArray_IsScalar(object, verbatim work) + bint PyArray_CheckScalar(object) + bint PyArray_IsPythonNumber(object) + bint PyArray_IsPythonScalar(object) + bint PyArray_IsAnyScalar(object) + bint PyArray_CheckAnyScalar(object) + + ndarray PyArray_GETCONTIGUOUS(ndarray) + bint PyArray_SAMESHAPE(ndarray, ndarray) nogil + npy_intp PyArray_SIZE(ndarray) nogil + npy_intp PyArray_NBYTES(ndarray) nogil + + object PyArray_FROM_O(object) + object PyArray_FROM_OF(object m, int flags) + object PyArray_FROM_OT(object m, int type) + object PyArray_FROM_OTF(object m, int type, int flags) + object PyArray_FROMANY(object m, int type, int min, int max, int flags) + object PyArray_ZEROS(int nd, npy_intp* dims, int type, int fortran) + object PyArray_EMPTY(int nd, npy_intp* dims, int type, int fortran) + void PyArray_FILLWBYTE(object, int val) + npy_intp PyArray_REFCOUNT(object) + object PyArray_ContiguousFromAny(op, int, int min_depth, int max_depth) + unsigned char PyArray_EquivArrTypes(ndarray a1, ndarray a2) + bint PyArray_EquivByteorders(int b1, int b2) nogil + object PyArray_SimpleNew(int nd, npy_intp* dims, int typenum) + object PyArray_SimpleNewFromData(int nd, npy_intp* dims, int typenum, void* data) + #object PyArray_SimpleNewFromDescr(int nd, npy_intp* dims, dtype descr) + object PyArray_ToScalar(void* data, ndarray arr) + + void* PyArray_GETPTR1(ndarray m, npy_intp i) nogil + void* PyArray_GETPTR2(ndarray m, npy_intp i, npy_intp j) nogil + void* PyArray_GETPTR3(ndarray m, npy_intp i, npy_intp j, npy_intp k) nogil + void* PyArray_GETPTR4(ndarray m, npy_intp i, npy_intp j, npy_intp k, npy_intp l) nogil + + void PyArray_XDECREF_ERR(ndarray) + # Cannot be supported due to out arg + # void PyArray_DESCR_REPLACE(descr) + + + object PyArray_Copy(ndarray) + object PyArray_FromObject(object op, int type, int min_depth, int max_depth) + object PyArray_ContiguousFromObject(object op, int type, int min_depth, int max_depth) + object PyArray_CopyFromObject(object op, int type, int min_depth, int max_depth) + + object PyArray_Cast(ndarray mp, int type_num) + object PyArray_Take(ndarray ap, object items, int axis) + object PyArray_Put(ndarray ap, object items, object values) + + void PyArray_ITER_RESET(flatiter it) nogil + void PyArray_ITER_NEXT(flatiter it) nogil + void PyArray_ITER_GOTO(flatiter it, npy_intp* destination) nogil + void PyArray_ITER_GOTO1D(flatiter it, npy_intp ind) nogil + void* PyArray_ITER_DATA(flatiter it) nogil + bint PyArray_ITER_NOTDONE(flatiter it) nogil + + void PyArray_MultiIter_RESET(broadcast multi) nogil + void PyArray_MultiIter_NEXT(broadcast multi) nogil + void PyArray_MultiIter_GOTO(broadcast multi, npy_intp dest) nogil + void PyArray_MultiIter_GOTO1D(broadcast multi, npy_intp ind) nogil + void* PyArray_MultiIter_DATA(broadcast multi, npy_intp i) nogil + void PyArray_MultiIter_NEXTi(broadcast multi, npy_intp i) nogil + bint PyArray_MultiIter_NOTDONE(broadcast multi) nogil + + # Functions from __multiarray_api.h + + # Functions taking dtype and returning object/ndarray are disabled + # for now as they steal dtype references. I'm conservative and disable + # more than is probably needed until it can be checked further. + int PyArray_SetNumericOps (object) + object PyArray_GetNumericOps () + int PyArray_INCREF (ndarray) + int PyArray_XDECREF (ndarray) + void PyArray_SetStringFunction (object, int) + dtype PyArray_DescrFromType (int) + object PyArray_TypeObjectFromType (int) + char * PyArray_Zero (ndarray) + char * PyArray_One (ndarray) + #object PyArray_CastToType (ndarray, dtype, int) + int PyArray_CastTo (ndarray, ndarray) + int PyArray_CastAnyTo (ndarray, ndarray) + int PyArray_CanCastSafely (int, int) + npy_bool PyArray_CanCastTo (dtype, dtype) + int PyArray_ObjectType (object, int) + dtype PyArray_DescrFromObject (object, dtype) + #ndarray* PyArray_ConvertToCommonType (object, int *) + dtype PyArray_DescrFromScalar (object) + dtype PyArray_DescrFromTypeObject (object) + npy_intp PyArray_Size (object) + #object PyArray_Scalar (void *, dtype, object) + #object PyArray_FromScalar (object, dtype) + void PyArray_ScalarAsCtype (object, void *) + #int PyArray_CastScalarToCtype (object, void *, dtype) + #int PyArray_CastScalarDirect (object, dtype, void *, int) + object PyArray_ScalarFromObject (object) + #PyArray_VectorUnaryFunc * PyArray_GetCastFunc (dtype, int) + object PyArray_FromDims (int, int *, int) + #object PyArray_FromDimsAndDataAndDescr (int, int *, dtype, char *) + #object PyArray_FromAny (object, dtype, int, int, int, object) + object PyArray_EnsureArray (object) + object PyArray_EnsureAnyArray (object) + #object PyArray_FromFile (stdio.FILE *, dtype, npy_intp, char *) + #object PyArray_FromString (char *, npy_intp, dtype, npy_intp, char *) + #object PyArray_FromBuffer (object, dtype, npy_intp, npy_intp) + #object PyArray_FromIter (object, dtype, npy_intp) + object PyArray_Return (ndarray) + #object PyArray_GetField (ndarray, dtype, int) + #int PyArray_SetField (ndarray, dtype, int, object) + object PyArray_Byteswap (ndarray, npy_bool) + object PyArray_Resize (ndarray, PyArray_Dims *, int, NPY_ORDER) + int PyArray_MoveInto (ndarray, ndarray) + int PyArray_CopyInto (ndarray, ndarray) + int PyArray_CopyAnyInto (ndarray, ndarray) + int PyArray_CopyObject (ndarray, object) + object PyArray_NewCopy (ndarray, NPY_ORDER) + object PyArray_ToList (ndarray) + object PyArray_ToString (ndarray, NPY_ORDER) + int PyArray_ToFile (ndarray, stdio.FILE *, char *, char *) + int PyArray_Dump (object, object, int) + object PyArray_Dumps (object, int) + int PyArray_ValidType (int) + void PyArray_UpdateFlags (ndarray, int) + object PyArray_New (type, int, npy_intp *, int, npy_intp *, void *, int, int, object) + #object PyArray_NewFromDescr (type, dtype, int, npy_intp *, npy_intp *, void *, int, object) + #dtype PyArray_DescrNew (dtype) + dtype PyArray_DescrNewFromType (int) + double PyArray_GetPriority (object, double) + object PyArray_IterNew (object) + object PyArray_MultiIterNew (int, ...) + + int PyArray_PyIntAsInt (object) + npy_intp PyArray_PyIntAsIntp (object) + int PyArray_Broadcast (broadcast) + void PyArray_FillObjectArray (ndarray, object) + int PyArray_FillWithScalar (ndarray, object) + npy_bool PyArray_CheckStrides (int, int, npy_intp, npy_intp, npy_intp *, npy_intp *) + dtype PyArray_DescrNewByteorder (dtype, char) + object PyArray_IterAllButAxis (object, int *) + #object PyArray_CheckFromAny (object, dtype, int, int, int, object) + #object PyArray_FromArray (ndarray, dtype, int) + object PyArray_FromInterface (object) + object PyArray_FromStructInterface (object) + #object PyArray_FromArrayAttr (object, dtype, object) + #NPY_SCALARKIND PyArray_ScalarKind (int, ndarray*) + int PyArray_CanCoerceScalar (int, int, NPY_SCALARKIND) + object PyArray_NewFlagsObject (object) + npy_bool PyArray_CanCastScalar (type, type) + #int PyArray_CompareUCS4 (npy_ucs4 *, npy_ucs4 *, register size_t) + int PyArray_RemoveSmallest (broadcast) + int PyArray_ElementStrides (object) + void PyArray_Item_INCREF (char *, dtype) + void PyArray_Item_XDECREF (char *, dtype) + object PyArray_FieldNames (object) + object PyArray_Transpose (ndarray, PyArray_Dims *) + object PyArray_TakeFrom (ndarray, object, int, ndarray, NPY_CLIPMODE) + object PyArray_PutTo (ndarray, object, object, NPY_CLIPMODE) + object PyArray_PutMask (ndarray, object, object) + object PyArray_Repeat (ndarray, object, int) + object PyArray_Choose (ndarray, object, ndarray, NPY_CLIPMODE) + int PyArray_Sort (ndarray, int, NPY_SORTKIND) + object PyArray_ArgSort (ndarray, int, NPY_SORTKIND) + object PyArray_SearchSorted (ndarray, object, NPY_SEARCHSIDE, PyObject *) + object PyArray_ArgMax (ndarray, int, ndarray) + object PyArray_ArgMin (ndarray, int, ndarray) + object PyArray_Reshape (ndarray, object) + object PyArray_Newshape (ndarray, PyArray_Dims *, NPY_ORDER) + object PyArray_Squeeze (ndarray) + #object PyArray_View (ndarray, dtype, type) + object PyArray_SwapAxes (ndarray, int, int) + object PyArray_Max (ndarray, int, ndarray) + object PyArray_Min (ndarray, int, ndarray) + object PyArray_Ptp (ndarray, int, ndarray) + object PyArray_Mean (ndarray, int, int, ndarray) + object PyArray_Trace (ndarray, int, int, int, int, ndarray) + object PyArray_Diagonal (ndarray, int, int, int) + object PyArray_Clip (ndarray, object, object, ndarray) + object PyArray_Conjugate (ndarray, ndarray) + object PyArray_Nonzero (ndarray) + object PyArray_Std (ndarray, int, int, ndarray, int) + object PyArray_Sum (ndarray, int, int, ndarray) + object PyArray_CumSum (ndarray, int, int, ndarray) + object PyArray_Prod (ndarray, int, int, ndarray) + object PyArray_CumProd (ndarray, int, int, ndarray) + object PyArray_All (ndarray, int, ndarray) + object PyArray_Any (ndarray, int, ndarray) + object PyArray_Compress (ndarray, object, int, ndarray) + object PyArray_Flatten (ndarray, NPY_ORDER) + object PyArray_Ravel (ndarray, NPY_ORDER) + npy_intp PyArray_MultiplyList (npy_intp *, int) + int PyArray_MultiplyIntList (int *, int) + void * PyArray_GetPtr (ndarray, npy_intp*) + int PyArray_CompareLists (npy_intp *, npy_intp *, int) + #int PyArray_AsCArray (object*, void *, npy_intp *, int, dtype) + #int PyArray_As1D (object*, char **, int *, int) + #int PyArray_As2D (object*, char ***, int *, int *, int) + int PyArray_Free (object, void *) + #int PyArray_Converter (object, object*) + int PyArray_IntpFromSequence (object, npy_intp *, int) + object PyArray_Concatenate (object, int) + object PyArray_InnerProduct (object, object) + object PyArray_MatrixProduct (object, object) + object PyArray_CopyAndTranspose (object) + object PyArray_Correlate (object, object, int) + int PyArray_TypestrConvert (int, int) + #int PyArray_DescrConverter (object, dtype*) + #int PyArray_DescrConverter2 (object, dtype*) + int PyArray_IntpConverter (object, PyArray_Dims *) + #int PyArray_BufferConverter (object, chunk) + int PyArray_AxisConverter (object, int *) + int PyArray_BoolConverter (object, npy_bool *) + int PyArray_ByteorderConverter (object, char *) + int PyArray_OrderConverter (object, NPY_ORDER *) + unsigned char PyArray_EquivTypes (dtype, dtype) + #object PyArray_Zeros (int, npy_intp *, dtype, int) + #object PyArray_Empty (int, npy_intp *, dtype, int) + object PyArray_Where (object, object, object) + object PyArray_Arange (double, double, double, int) + #object PyArray_ArangeObj (object, object, object, dtype) + int PyArray_SortkindConverter (object, NPY_SORTKIND *) + object PyArray_LexSort (object, int) + object PyArray_Round (ndarray, int, ndarray) + unsigned char PyArray_EquivTypenums (int, int) + int PyArray_RegisterDataType (dtype) + int PyArray_RegisterCastFunc (dtype, int, PyArray_VectorUnaryFunc *) + int PyArray_RegisterCanCast (dtype, int, NPY_SCALARKIND) + #void PyArray_InitArrFuncs (PyArray_ArrFuncs *) + object PyArray_IntTupleFromIntp (int, npy_intp *) + int PyArray_TypeNumFromName (char *) + int PyArray_ClipmodeConverter (object, NPY_CLIPMODE *) + #int PyArray_OutputConverter (object, ndarray*) + object PyArray_BroadcastToShape (object, npy_intp *, int) + void _PyArray_SigintHandler (int) + void* _PyArray_GetSigintBuf () + #int PyArray_DescrAlignConverter (object, dtype*) + #int PyArray_DescrAlignConverter2 (object, dtype*) + int PyArray_SearchsideConverter (object, void *) + object PyArray_CheckAxis (ndarray, int *, int) + npy_intp PyArray_OverflowMultiplyList (npy_intp *, int) + int PyArray_CompareString (char *, char *, size_t) + int PyArray_SetBaseObject(ndarray, base) # NOTE: steals a reference to base! Use "set_array_base()" instead. + + +# Typedefs that matches the runtime dtype objects in +# the numpy module. + +# The ones that are commented out needs an IFDEF function +# in Cython to enable them only on the right systems. + +ctypedef npy_int8 int8_t +ctypedef npy_int16 int16_t +ctypedef npy_int32 int32_t +ctypedef npy_int64 int64_t +#ctypedef npy_int96 int96_t +#ctypedef npy_int128 int128_t + +ctypedef npy_uint8 uint8_t +ctypedef npy_uint16 uint16_t +ctypedef npy_uint32 uint32_t +ctypedef npy_uint64 uint64_t +#ctypedef npy_uint96 uint96_t +#ctypedef npy_uint128 uint128_t + +ctypedef npy_float32 float32_t +ctypedef npy_float64 float64_t +#ctypedef npy_float80 float80_t +#ctypedef npy_float128 float128_t + +ctypedef float complex complex64_t +ctypedef double complex complex128_t + +# The int types are mapped a bit surprising -- +# numpy.int corresponds to 'l' and numpy.long to 'q' +ctypedef npy_long int_t +ctypedef npy_longlong long_t +ctypedef npy_longlong longlong_t + +ctypedef npy_ulong uint_t +ctypedef npy_ulonglong ulong_t +ctypedef npy_ulonglong ulonglong_t + +ctypedef npy_intp intp_t +ctypedef npy_uintp uintp_t + +ctypedef npy_double float_t +ctypedef npy_double double_t +ctypedef npy_longdouble longdouble_t + +ctypedef npy_cfloat cfloat_t +ctypedef npy_cdouble cdouble_t +ctypedef npy_clongdouble clongdouble_t + +ctypedef npy_cdouble complex_t + +cdef inline object PyArray_MultiIterNew1(a): + return PyArray_MultiIterNew(1, <void*>a) + +cdef inline object PyArray_MultiIterNew2(a, b): + return PyArray_MultiIterNew(2, <void*>a, <void*>b) + +cdef inline object PyArray_MultiIterNew3(a, b, c): + return PyArray_MultiIterNew(3, <void*>a, <void*>b, <void*> c) + +cdef inline object PyArray_MultiIterNew4(a, b, c, d): + return PyArray_MultiIterNew(4, <void*>a, <void*>b, <void*>c, <void*> d) + +cdef inline object PyArray_MultiIterNew5(a, b, c, d, e): + return PyArray_MultiIterNew(5, <void*>a, <void*>b, <void*>c, <void*> d, <void*> e) + +cdef inline tuple PyDataType_SHAPE(dtype d): + if PyDataType_HASSUBARRAY(d): + return <tuple>d.subarray.shape + else: + return () + + +cdef extern from "numpy/ndarrayobject.h": + PyTypeObject PyTimedeltaArrType_Type + PyTypeObject PyDatetimeArrType_Type + ctypedef int64_t npy_timedelta + ctypedef int64_t npy_datetime + +cdef extern from "numpy/ndarraytypes.h": + ctypedef struct PyArray_DatetimeMetaData: + NPY_DATETIMEUNIT base + int64_t num + +cdef extern from "numpy/arrayscalars.h": + + # abstract types + ctypedef class numpy.generic [object PyObject]: + pass + ctypedef class numpy.number [object PyObject]: + pass + ctypedef class numpy.integer [object PyObject]: + pass + ctypedef class numpy.signedinteger [object PyObject]: + pass + ctypedef class numpy.unsignedinteger [object PyObject]: + pass + ctypedef class numpy.inexact [object PyObject]: + pass + ctypedef class numpy.floating [object PyObject]: + pass + ctypedef class numpy.complexfloating [object PyObject]: + pass + ctypedef class numpy.flexible [object PyObject]: + pass + ctypedef class numpy.character [object PyObject]: + pass + + ctypedef struct PyDatetimeScalarObject: + # PyObject_HEAD + npy_datetime obval + PyArray_DatetimeMetaData obmeta + + ctypedef struct PyTimedeltaScalarObject: + # PyObject_HEAD + npy_timedelta obval + PyArray_DatetimeMetaData obmeta + + ctypedef enum NPY_DATETIMEUNIT: + NPY_FR_Y + NPY_FR_M + NPY_FR_W + NPY_FR_D + NPY_FR_B + NPY_FR_h + NPY_FR_m + NPY_FR_s + NPY_FR_ms + NPY_FR_us + NPY_FR_ns + NPY_FR_ps + NPY_FR_fs + NPY_FR_as + + +# +# ufunc API +# + +cdef extern from "numpy/ufuncobject.h": + + ctypedef void (*PyUFuncGenericFunction) (char **, npy_intp *, npy_intp *, void *) + + ctypedef class numpy.ufunc [object PyUFuncObject, check_size ignore]: + cdef: + int nin, nout, nargs + int identity + PyUFuncGenericFunction *functions + void **data + int ntypes + int check_return + char *name + char *types + char *doc + void *ptr + PyObject *obj + PyObject *userloops + + cdef enum: + PyUFunc_Zero + PyUFunc_One + PyUFunc_None + UFUNC_ERR_IGNORE + UFUNC_ERR_WARN + UFUNC_ERR_RAISE + UFUNC_ERR_CALL + UFUNC_ERR_PRINT + UFUNC_ERR_LOG + UFUNC_MASK_DIVIDEBYZERO + UFUNC_MASK_OVERFLOW + UFUNC_MASK_UNDERFLOW + UFUNC_MASK_INVALID + UFUNC_SHIFT_DIVIDEBYZERO + UFUNC_SHIFT_OVERFLOW + UFUNC_SHIFT_UNDERFLOW + UFUNC_SHIFT_INVALID + UFUNC_FPE_DIVIDEBYZERO + UFUNC_FPE_OVERFLOW + UFUNC_FPE_UNDERFLOW + UFUNC_FPE_INVALID + UFUNC_ERR_DEFAULT + UFUNC_ERR_DEFAULT2 + + object PyUFunc_FromFuncAndData(PyUFuncGenericFunction *, + void **, char *, int, int, int, int, char *, char *, int) + int PyUFunc_RegisterLoopForType(ufunc, int, + PyUFuncGenericFunction, int *, void *) + void PyUFunc_f_f_As_d_d \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_d_d \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_f_f \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_g_g \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_F_F_As_D_D \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_F_F \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_D_D \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_G_G \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_O_O \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_ff_f_As_dd_d \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_ff_f \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_dd_d \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_gg_g \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_FF_F_As_DD_D \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_DD_D \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_FF_F \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_GG_G \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_OO_O \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_O_O_method \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_OO_O_method \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_On_Om \ + (char **, npy_intp *, npy_intp *, void *) + int PyUFunc_GetPyValues \ + (char *, int *, int *, PyObject **) + int PyUFunc_checkfperr \ + (int, PyObject *, int *) + void PyUFunc_clearfperr() + int PyUFunc_getfperr() + int PyUFunc_handlefperr \ + (int, PyObject *, int, int *) + int PyUFunc_ReplaceLoopBySignature \ + (ufunc, PyUFuncGenericFunction, int *, PyUFuncGenericFunction *) + object PyUFunc_FromFuncAndDataAndSignature \ + (PyUFuncGenericFunction *, void **, char *, int, int, int, + int, char *, char *, int, char *) + + int _import_umath() except -1 + +cdef inline void set_array_base(ndarray arr, object base): + Py_INCREF(base) # important to do this before stealing the reference below! + PyArray_SetBaseObject(arr, base) + +cdef inline object get_array_base(ndarray arr): + base = PyArray_BASE(arr) + if base is NULL: + return None + return <object>base + +# Versions of the import_* functions which are more suitable for +# Cython code. +cdef inline int import_array() except -1: + try: + __pyx_import_array() + except Exception: + raise ImportError("numpy.core.multiarray failed to import") + +cdef inline int import_umath() except -1: + try: + _import_umath() + except Exception: + raise ImportError("numpy.core.umath failed to import") + +cdef inline int import_ufunc() except -1: + try: + _import_umath() + except Exception: + raise ImportError("numpy.core.umath failed to import") + + +cdef inline bint is_timedelta64_object(object obj): + """ + Cython equivalent of `isinstance(obj, np.timedelta64)` + + Parameters + ---------- + obj : object + + Returns + ------- + bool + """ + return PyObject_TypeCheck(obj, &PyTimedeltaArrType_Type) + + +cdef inline bint is_datetime64_object(object obj): + """ + Cython equivalent of `isinstance(obj, np.datetime64)` + + Parameters + ---------- + obj : object + + Returns + ------- + bool + """ + return PyObject_TypeCheck(obj, &PyDatetimeArrType_Type) + + +cdef inline npy_datetime get_datetime64_value(object obj) nogil: + """ + returns the int64 value underlying scalar numpy datetime64 object + + Note that to interpret this as a datetime, the corresponding unit is + also needed. That can be found using `get_datetime64_unit`. + """ + return (<PyDatetimeScalarObject*>obj).obval + + +cdef inline npy_timedelta get_timedelta64_value(object obj) nogil: + """ + returns the int64 value underlying scalar numpy timedelta64 object + """ + return (<PyTimedeltaScalarObject*>obj).obval + + +cdef inline NPY_DATETIMEUNIT get_datetime64_unit(object obj) nogil: + """ + returns the unit part of the dtype for a numpy datetime64 object. + """ + return <NPY_DATETIMEUNIT>(<PyDatetimeScalarObject*>obj).obmeta.base diff --git a/numpy/__init__.pxd b/numpy/__init__.pxd new file mode 100644 index 000000000000..97f3da2e5673 --- /dev/null +++ b/numpy/__init__.pxd @@ -0,0 +1,1018 @@ +# NumPy static imports for Cython < 3.0 +# +# If any of the PyArray_* functions are called, import_array must be +# called first. +# +# Author: Dag Sverre Seljebotn +# + +DEF _buffer_format_string_len = 255 + +cimport cpython.buffer as pybuf +from cpython.ref cimport Py_INCREF +from cpython.mem cimport PyObject_Malloc, PyObject_Free +from cpython.object cimport PyObject, PyTypeObject +from cpython.buffer cimport PyObject_GetBuffer +from cpython.type cimport type +cimport libc.stdio as stdio + +cdef extern from "Python.h": + ctypedef int Py_intptr_t + bint PyObject_TypeCheck(object obj, PyTypeObject* type) + +cdef extern from "numpy/arrayobject.h": + ctypedef Py_intptr_t npy_intp + ctypedef size_t npy_uintp + + cdef enum NPY_TYPES: + NPY_BOOL + NPY_BYTE + NPY_UBYTE + NPY_SHORT + NPY_USHORT + NPY_INT + NPY_UINT + NPY_LONG + NPY_ULONG + NPY_LONGLONG + NPY_ULONGLONG + NPY_FLOAT + NPY_DOUBLE + NPY_LONGDOUBLE + NPY_CFLOAT + NPY_CDOUBLE + NPY_CLONGDOUBLE + NPY_OBJECT + NPY_STRING + NPY_UNICODE + NPY_VOID + NPY_DATETIME + NPY_TIMEDELTA + NPY_NTYPES + NPY_NOTYPE + + NPY_INT8 + NPY_INT16 + NPY_INT32 + NPY_INT64 + NPY_INT128 + NPY_INT256 + NPY_UINT8 + NPY_UINT16 + NPY_UINT32 + NPY_UINT64 + NPY_UINT128 + NPY_UINT256 + NPY_FLOAT16 + NPY_FLOAT32 + NPY_FLOAT64 + NPY_FLOAT80 + NPY_FLOAT96 + NPY_FLOAT128 + NPY_FLOAT256 + NPY_COMPLEX32 + NPY_COMPLEX64 + NPY_COMPLEX128 + NPY_COMPLEX160 + NPY_COMPLEX192 + NPY_COMPLEX256 + NPY_COMPLEX512 + + NPY_INTP + + ctypedef enum NPY_ORDER: + NPY_ANYORDER + NPY_CORDER + NPY_FORTRANORDER + NPY_KEEPORDER + + ctypedef enum NPY_CASTING: + NPY_NO_CASTING + NPY_EQUIV_CASTING + NPY_SAFE_CASTING + NPY_SAME_KIND_CASTING + NPY_UNSAFE_CASTING + + ctypedef enum NPY_CLIPMODE: + NPY_CLIP + NPY_WRAP + NPY_RAISE + + ctypedef enum NPY_SCALARKIND: + NPY_NOSCALAR, + NPY_BOOL_SCALAR, + NPY_INTPOS_SCALAR, + NPY_INTNEG_SCALAR, + NPY_FLOAT_SCALAR, + NPY_COMPLEX_SCALAR, + NPY_OBJECT_SCALAR + + ctypedef enum NPY_SORTKIND: + NPY_QUICKSORT + NPY_HEAPSORT + NPY_MERGESORT + + ctypedef enum NPY_SEARCHSIDE: + NPY_SEARCHLEFT + NPY_SEARCHRIGHT + + enum: + # DEPRECATED since NumPy 1.7 ! Do not use in new code! + NPY_C_CONTIGUOUS + NPY_F_CONTIGUOUS + NPY_CONTIGUOUS + NPY_FORTRAN + NPY_OWNDATA + NPY_FORCECAST + NPY_ENSURECOPY + NPY_ENSUREARRAY + NPY_ELEMENTSTRIDES + NPY_ALIGNED + NPY_NOTSWAPPED + NPY_WRITEABLE + NPY_UPDATEIFCOPY + NPY_ARR_HAS_DESCR + + NPY_BEHAVED + NPY_BEHAVED_NS + NPY_CARRAY + NPY_CARRAY_RO + NPY_FARRAY + NPY_FARRAY_RO + NPY_DEFAULT + + NPY_IN_ARRAY + NPY_OUT_ARRAY + NPY_INOUT_ARRAY + NPY_IN_FARRAY + NPY_OUT_FARRAY + NPY_INOUT_FARRAY + + NPY_UPDATE_ALL + + enum: + # Added in NumPy 1.7 to replace the deprecated enums above. + NPY_ARRAY_C_CONTIGUOUS + NPY_ARRAY_F_CONTIGUOUS + NPY_ARRAY_OWNDATA + NPY_ARRAY_FORCECAST + NPY_ARRAY_ENSURECOPY + NPY_ARRAY_ENSUREARRAY + NPY_ARRAY_ELEMENTSTRIDES + NPY_ARRAY_ALIGNED + NPY_ARRAY_NOTSWAPPED + NPY_ARRAY_WRITEABLE + NPY_ARRAY_UPDATEIFCOPY + + NPY_ARRAY_BEHAVED + NPY_ARRAY_BEHAVED_NS + NPY_ARRAY_CARRAY + NPY_ARRAY_CARRAY_RO + NPY_ARRAY_FARRAY + NPY_ARRAY_FARRAY_RO + NPY_ARRAY_DEFAULT + + NPY_ARRAY_IN_ARRAY + NPY_ARRAY_OUT_ARRAY + NPY_ARRAY_INOUT_ARRAY + NPY_ARRAY_IN_FARRAY + NPY_ARRAY_OUT_FARRAY + NPY_ARRAY_INOUT_FARRAY + + NPY_ARRAY_UPDATE_ALL + + cdef enum: + NPY_MAXDIMS + + npy_intp NPY_MAX_ELSIZE + + ctypedef void (*PyArray_VectorUnaryFunc)(void *, void *, npy_intp, void *, void *) + + ctypedef struct PyArray_ArrayDescr: + # shape is a tuple, but Cython doesn't support "tuple shape" + # inside a non-PyObject declaration, so we have to declare it + # as just a PyObject*. + PyObject* shape + + ctypedef struct PyArray_Descr: + pass + + ctypedef class numpy.dtype [object PyArray_Descr, check_size ignore]: + # Use PyDataType_* macros when possible, however there are no macros + # for accessing some of the fields, so some are defined. + cdef PyTypeObject* typeobj + cdef char kind + cdef char type + # Numpy sometimes mutates this without warning (e.g. it'll + # sometimes change "|" to "<" in shared dtype objects on + # little-endian machines). If this matters to you, use + # PyArray_IsNativeByteOrder(dtype.byteorder) instead of + # directly accessing this field. + cdef char byteorder + cdef char flags + cdef int type_num + cdef int itemsize "elsize" + cdef int alignment + cdef object fields + cdef tuple names + # Use PyDataType_HASSUBARRAY to test whether this field is + # valid (the pointer can be NULL). Most users should access + # this field via the inline helper method PyDataType_SHAPE. + cdef PyArray_ArrayDescr* subarray + + ctypedef class numpy.flatiter [object PyArrayIterObject, check_size ignore]: + # Use through macros + pass + + ctypedef class numpy.broadcast [object PyArrayMultiIterObject, check_size ignore]: + cdef int numiter + cdef npy_intp size, index + cdef int nd + cdef npy_intp *dimensions + cdef void **iters + + ctypedef struct PyArrayObject: + # For use in situations where ndarray can't replace PyArrayObject*, + # like PyArrayObject**. + pass + + ctypedef class numpy.ndarray [object PyArrayObject, check_size ignore]: + cdef __cythonbufferdefaults__ = {"mode": "strided"} + + cdef: + # Only taking a few of the most commonly used and stable fields. + # One should use PyArray_* macros instead to access the C fields. + char *data + int ndim "nd" + npy_intp *shape "dimensions" + npy_intp *strides + dtype descr # deprecated since NumPy 1.7 ! + PyObject* base # NOT PUBLIC, DO NOT USE ! + + + + ctypedef unsigned char npy_bool + + ctypedef signed char npy_byte + ctypedef signed short npy_short + ctypedef signed int npy_int + ctypedef signed long npy_long + ctypedef signed long long npy_longlong + + ctypedef unsigned char npy_ubyte + ctypedef unsigned short npy_ushort + ctypedef unsigned int npy_uint + ctypedef unsigned long npy_ulong + ctypedef unsigned long long npy_ulonglong + + ctypedef float npy_float + ctypedef double npy_double + ctypedef long double npy_longdouble + + ctypedef signed char npy_int8 + ctypedef signed short npy_int16 + ctypedef signed int npy_int32 + ctypedef signed long long npy_int64 + ctypedef signed long long npy_int96 + ctypedef signed long long npy_int128 + + ctypedef unsigned char npy_uint8 + ctypedef unsigned short npy_uint16 + ctypedef unsigned int npy_uint32 + ctypedef unsigned long long npy_uint64 + ctypedef unsigned long long npy_uint96 + ctypedef unsigned long long npy_uint128 + + ctypedef float npy_float32 + ctypedef double npy_float64 + ctypedef long double npy_float80 + ctypedef long double npy_float96 + ctypedef long double npy_float128 + + ctypedef struct npy_cfloat: + float real + float imag + + ctypedef struct npy_cdouble: + double real + double imag + + ctypedef struct npy_clongdouble: + long double real + long double imag + + ctypedef struct npy_complex64: + float real + float imag + + ctypedef struct npy_complex128: + double real + double imag + + ctypedef struct npy_complex160: + long double real + long double imag + + ctypedef struct npy_complex192: + long double real + long double imag + + ctypedef struct npy_complex256: + long double real + long double imag + + ctypedef struct PyArray_Dims: + npy_intp *ptr + int len + + int _import_array() except -1 + # A second definition so _import_array isn't marked as used when we use it here. + # Do not use - subject to change any time. + int __pyx_import_array "_import_array"() except -1 + + # + # Macros from ndarrayobject.h + # + bint PyArray_CHKFLAGS(ndarray m, int flags) nogil + bint PyArray_IS_C_CONTIGUOUS(ndarray arr) nogil + bint PyArray_IS_F_CONTIGUOUS(ndarray arr) nogil + bint PyArray_ISCONTIGUOUS(ndarray m) nogil + bint PyArray_ISWRITEABLE(ndarray m) nogil + bint PyArray_ISALIGNED(ndarray m) nogil + + int PyArray_NDIM(ndarray) nogil + bint PyArray_ISONESEGMENT(ndarray) nogil + bint PyArray_ISFORTRAN(ndarray) nogil + int PyArray_FORTRANIF(ndarray) nogil + + void* PyArray_DATA(ndarray) nogil + char* PyArray_BYTES(ndarray) nogil + + npy_intp* PyArray_DIMS(ndarray) nogil + npy_intp* PyArray_STRIDES(ndarray) nogil + npy_intp PyArray_DIM(ndarray, size_t) nogil + npy_intp PyArray_STRIDE(ndarray, size_t) nogil + + PyObject *PyArray_BASE(ndarray) nogil # returns borrowed reference! + PyArray_Descr *PyArray_DESCR(ndarray) nogil # returns borrowed reference to dtype! + int PyArray_FLAGS(ndarray) nogil + npy_intp PyArray_ITEMSIZE(ndarray) nogil + int PyArray_TYPE(ndarray arr) nogil + + object PyArray_GETITEM(ndarray arr, void *itemptr) + int PyArray_SETITEM(ndarray arr, void *itemptr, object obj) + + bint PyTypeNum_ISBOOL(int) nogil + bint PyTypeNum_ISUNSIGNED(int) nogil + bint PyTypeNum_ISSIGNED(int) nogil + bint PyTypeNum_ISINTEGER(int) nogil + bint PyTypeNum_ISFLOAT(int) nogil + bint PyTypeNum_ISNUMBER(int) nogil + bint PyTypeNum_ISSTRING(int) nogil + bint PyTypeNum_ISCOMPLEX(int) nogil + bint PyTypeNum_ISPYTHON(int) nogil + bint PyTypeNum_ISFLEXIBLE(int) nogil + bint PyTypeNum_ISUSERDEF(int) nogil + bint PyTypeNum_ISEXTENDED(int) nogil + bint PyTypeNum_ISOBJECT(int) nogil + + bint PyDataType_ISBOOL(dtype) nogil + bint PyDataType_ISUNSIGNED(dtype) nogil + bint PyDataType_ISSIGNED(dtype) nogil + bint PyDataType_ISINTEGER(dtype) nogil + bint PyDataType_ISFLOAT(dtype) nogil + bint PyDataType_ISNUMBER(dtype) nogil + bint PyDataType_ISSTRING(dtype) nogil + bint PyDataType_ISCOMPLEX(dtype) nogil + bint PyDataType_ISPYTHON(dtype) nogil + bint PyDataType_ISFLEXIBLE(dtype) nogil + bint PyDataType_ISUSERDEF(dtype) nogil + bint PyDataType_ISEXTENDED(dtype) nogil + bint PyDataType_ISOBJECT(dtype) nogil + bint PyDataType_HASFIELDS(dtype) nogil + bint PyDataType_HASSUBARRAY(dtype) nogil + + bint PyArray_ISBOOL(ndarray) nogil + bint PyArray_ISUNSIGNED(ndarray) nogil + bint PyArray_ISSIGNED(ndarray) nogil + bint PyArray_ISINTEGER(ndarray) nogil + bint PyArray_ISFLOAT(ndarray) nogil + bint PyArray_ISNUMBER(ndarray) nogil + bint PyArray_ISSTRING(ndarray) nogil + bint PyArray_ISCOMPLEX(ndarray) nogil + bint PyArray_ISPYTHON(ndarray) nogil + bint PyArray_ISFLEXIBLE(ndarray) nogil + bint PyArray_ISUSERDEF(ndarray) nogil + bint PyArray_ISEXTENDED(ndarray) nogil + bint PyArray_ISOBJECT(ndarray) nogil + bint PyArray_HASFIELDS(ndarray) nogil + + bint PyArray_ISVARIABLE(ndarray) nogil + + bint PyArray_SAFEALIGNEDCOPY(ndarray) nogil + bint PyArray_ISNBO(char) nogil # works on ndarray.byteorder + bint PyArray_IsNativeByteOrder(char) nogil # works on ndarray.byteorder + bint PyArray_ISNOTSWAPPED(ndarray) nogil + bint PyArray_ISBYTESWAPPED(ndarray) nogil + + bint PyArray_FLAGSWAP(ndarray, int) nogil + + bint PyArray_ISCARRAY(ndarray) nogil + bint PyArray_ISCARRAY_RO(ndarray) nogil + bint PyArray_ISFARRAY(ndarray) nogil + bint PyArray_ISFARRAY_RO(ndarray) nogil + bint PyArray_ISBEHAVED(ndarray) nogil + bint PyArray_ISBEHAVED_RO(ndarray) nogil + + + bint PyDataType_ISNOTSWAPPED(dtype) nogil + bint PyDataType_ISBYTESWAPPED(dtype) nogil + + bint PyArray_DescrCheck(object) + + bint PyArray_Check(object) + bint PyArray_CheckExact(object) + + # Cannot be supported due to out arg: + # bint PyArray_HasArrayInterfaceType(object, dtype, object, object&) + # bint PyArray_HasArrayInterface(op, out) + + + bint PyArray_IsZeroDim(object) + # Cannot be supported due to ## ## in macro: + # bint PyArray_IsScalar(object, verbatim work) + bint PyArray_CheckScalar(object) + bint PyArray_IsPythonNumber(object) + bint PyArray_IsPythonScalar(object) + bint PyArray_IsAnyScalar(object) + bint PyArray_CheckAnyScalar(object) + + ndarray PyArray_GETCONTIGUOUS(ndarray) + bint PyArray_SAMESHAPE(ndarray, ndarray) nogil + npy_intp PyArray_SIZE(ndarray) nogil + npy_intp PyArray_NBYTES(ndarray) nogil + + object PyArray_FROM_O(object) + object PyArray_FROM_OF(object m, int flags) + object PyArray_FROM_OT(object m, int type) + object PyArray_FROM_OTF(object m, int type, int flags) + object PyArray_FROMANY(object m, int type, int min, int max, int flags) + object PyArray_ZEROS(int nd, npy_intp* dims, int type, int fortran) + object PyArray_EMPTY(int nd, npy_intp* dims, int type, int fortran) + void PyArray_FILLWBYTE(object, int val) + npy_intp PyArray_REFCOUNT(object) + object PyArray_ContiguousFromAny(op, int, int min_depth, int max_depth) + unsigned char PyArray_EquivArrTypes(ndarray a1, ndarray a2) + bint PyArray_EquivByteorders(int b1, int b2) nogil + object PyArray_SimpleNew(int nd, npy_intp* dims, int typenum) + object PyArray_SimpleNewFromData(int nd, npy_intp* dims, int typenum, void* data) + #object PyArray_SimpleNewFromDescr(int nd, npy_intp* dims, dtype descr) + object PyArray_ToScalar(void* data, ndarray arr) + + void* PyArray_GETPTR1(ndarray m, npy_intp i) nogil + void* PyArray_GETPTR2(ndarray m, npy_intp i, npy_intp j) nogil + void* PyArray_GETPTR3(ndarray m, npy_intp i, npy_intp j, npy_intp k) nogil + void* PyArray_GETPTR4(ndarray m, npy_intp i, npy_intp j, npy_intp k, npy_intp l) nogil + + void PyArray_XDECREF_ERR(ndarray) + # Cannot be supported due to out arg + # void PyArray_DESCR_REPLACE(descr) + + + object PyArray_Copy(ndarray) + object PyArray_FromObject(object op, int type, int min_depth, int max_depth) + object PyArray_ContiguousFromObject(object op, int type, int min_depth, int max_depth) + object PyArray_CopyFromObject(object op, int type, int min_depth, int max_depth) + + object PyArray_Cast(ndarray mp, int type_num) + object PyArray_Take(ndarray ap, object items, int axis) + object PyArray_Put(ndarray ap, object items, object values) + + void PyArray_ITER_RESET(flatiter it) nogil + void PyArray_ITER_NEXT(flatiter it) nogil + void PyArray_ITER_GOTO(flatiter it, npy_intp* destination) nogil + void PyArray_ITER_GOTO1D(flatiter it, npy_intp ind) nogil + void* PyArray_ITER_DATA(flatiter it) nogil + bint PyArray_ITER_NOTDONE(flatiter it) nogil + + void PyArray_MultiIter_RESET(broadcast multi) nogil + void PyArray_MultiIter_NEXT(broadcast multi) nogil + void PyArray_MultiIter_GOTO(broadcast multi, npy_intp dest) nogil + void PyArray_MultiIter_GOTO1D(broadcast multi, npy_intp ind) nogil + void* PyArray_MultiIter_DATA(broadcast multi, npy_intp i) nogil + void PyArray_MultiIter_NEXTi(broadcast multi, npy_intp i) nogil + bint PyArray_MultiIter_NOTDONE(broadcast multi) nogil + + # Functions from __multiarray_api.h + + # Functions taking dtype and returning object/ndarray are disabled + # for now as they steal dtype references. I'm conservative and disable + # more than is probably needed until it can be checked further. + int PyArray_SetNumericOps (object) + object PyArray_GetNumericOps () + int PyArray_INCREF (ndarray) + int PyArray_XDECREF (ndarray) + void PyArray_SetStringFunction (object, int) + dtype PyArray_DescrFromType (int) + object PyArray_TypeObjectFromType (int) + char * PyArray_Zero (ndarray) + char * PyArray_One (ndarray) + #object PyArray_CastToType (ndarray, dtype, int) + int PyArray_CastTo (ndarray, ndarray) + int PyArray_CastAnyTo (ndarray, ndarray) + int PyArray_CanCastSafely (int, int) + npy_bool PyArray_CanCastTo (dtype, dtype) + int PyArray_ObjectType (object, int) + dtype PyArray_DescrFromObject (object, dtype) + #ndarray* PyArray_ConvertToCommonType (object, int *) + dtype PyArray_DescrFromScalar (object) + dtype PyArray_DescrFromTypeObject (object) + npy_intp PyArray_Size (object) + #object PyArray_Scalar (void *, dtype, object) + #object PyArray_FromScalar (object, dtype) + void PyArray_ScalarAsCtype (object, void *) + #int PyArray_CastScalarToCtype (object, void *, dtype) + #int PyArray_CastScalarDirect (object, dtype, void *, int) + object PyArray_ScalarFromObject (object) + #PyArray_VectorUnaryFunc * PyArray_GetCastFunc (dtype, int) + object PyArray_FromDims (int, int *, int) + #object PyArray_FromDimsAndDataAndDescr (int, int *, dtype, char *) + #object PyArray_FromAny (object, dtype, int, int, int, object) + object PyArray_EnsureArray (object) + object PyArray_EnsureAnyArray (object) + #object PyArray_FromFile (stdio.FILE *, dtype, npy_intp, char *) + #object PyArray_FromString (char *, npy_intp, dtype, npy_intp, char *) + #object PyArray_FromBuffer (object, dtype, npy_intp, npy_intp) + #object PyArray_FromIter (object, dtype, npy_intp) + object PyArray_Return (ndarray) + #object PyArray_GetField (ndarray, dtype, int) + #int PyArray_SetField (ndarray, dtype, int, object) + object PyArray_Byteswap (ndarray, npy_bool) + object PyArray_Resize (ndarray, PyArray_Dims *, int, NPY_ORDER) + int PyArray_MoveInto (ndarray, ndarray) + int PyArray_CopyInto (ndarray, ndarray) + int PyArray_CopyAnyInto (ndarray, ndarray) + int PyArray_CopyObject (ndarray, object) + object PyArray_NewCopy (ndarray, NPY_ORDER) + object PyArray_ToList (ndarray) + object PyArray_ToString (ndarray, NPY_ORDER) + int PyArray_ToFile (ndarray, stdio.FILE *, char *, char *) + int PyArray_Dump (object, object, int) + object PyArray_Dumps (object, int) + int PyArray_ValidType (int) + void PyArray_UpdateFlags (ndarray, int) + object PyArray_New (type, int, npy_intp *, int, npy_intp *, void *, int, int, object) + #object PyArray_NewFromDescr (type, dtype, int, npy_intp *, npy_intp *, void *, int, object) + #dtype PyArray_DescrNew (dtype) + dtype PyArray_DescrNewFromType (int) + double PyArray_GetPriority (object, double) + object PyArray_IterNew (object) + object PyArray_MultiIterNew (int, ...) + + int PyArray_PyIntAsInt (object) + npy_intp PyArray_PyIntAsIntp (object) + int PyArray_Broadcast (broadcast) + void PyArray_FillObjectArray (ndarray, object) + int PyArray_FillWithScalar (ndarray, object) + npy_bool PyArray_CheckStrides (int, int, npy_intp, npy_intp, npy_intp *, npy_intp *) + dtype PyArray_DescrNewByteorder (dtype, char) + object PyArray_IterAllButAxis (object, int *) + #object PyArray_CheckFromAny (object, dtype, int, int, int, object) + #object PyArray_FromArray (ndarray, dtype, int) + object PyArray_FromInterface (object) + object PyArray_FromStructInterface (object) + #object PyArray_FromArrayAttr (object, dtype, object) + #NPY_SCALARKIND PyArray_ScalarKind (int, ndarray*) + int PyArray_CanCoerceScalar (int, int, NPY_SCALARKIND) + object PyArray_NewFlagsObject (object) + npy_bool PyArray_CanCastScalar (type, type) + #int PyArray_CompareUCS4 (npy_ucs4 *, npy_ucs4 *, register size_t) + int PyArray_RemoveSmallest (broadcast) + int PyArray_ElementStrides (object) + void PyArray_Item_INCREF (char *, dtype) + void PyArray_Item_XDECREF (char *, dtype) + object PyArray_FieldNames (object) + object PyArray_Transpose (ndarray, PyArray_Dims *) + object PyArray_TakeFrom (ndarray, object, int, ndarray, NPY_CLIPMODE) + object PyArray_PutTo (ndarray, object, object, NPY_CLIPMODE) + object PyArray_PutMask (ndarray, object, object) + object PyArray_Repeat (ndarray, object, int) + object PyArray_Choose (ndarray, object, ndarray, NPY_CLIPMODE) + int PyArray_Sort (ndarray, int, NPY_SORTKIND) + object PyArray_ArgSort (ndarray, int, NPY_SORTKIND) + object PyArray_SearchSorted (ndarray, object, NPY_SEARCHSIDE, PyObject *) + object PyArray_ArgMax (ndarray, int, ndarray) + object PyArray_ArgMin (ndarray, int, ndarray) + object PyArray_Reshape (ndarray, object) + object PyArray_Newshape (ndarray, PyArray_Dims *, NPY_ORDER) + object PyArray_Squeeze (ndarray) + #object PyArray_View (ndarray, dtype, type) + object PyArray_SwapAxes (ndarray, int, int) + object PyArray_Max (ndarray, int, ndarray) + object PyArray_Min (ndarray, int, ndarray) + object PyArray_Ptp (ndarray, int, ndarray) + object PyArray_Mean (ndarray, int, int, ndarray) + object PyArray_Trace (ndarray, int, int, int, int, ndarray) + object PyArray_Diagonal (ndarray, int, int, int) + object PyArray_Clip (ndarray, object, object, ndarray) + object PyArray_Conjugate (ndarray, ndarray) + object PyArray_Nonzero (ndarray) + object PyArray_Std (ndarray, int, int, ndarray, int) + object PyArray_Sum (ndarray, int, int, ndarray) + object PyArray_CumSum (ndarray, int, int, ndarray) + object PyArray_Prod (ndarray, int, int, ndarray) + object PyArray_CumProd (ndarray, int, int, ndarray) + object PyArray_All (ndarray, int, ndarray) + object PyArray_Any (ndarray, int, ndarray) + object PyArray_Compress (ndarray, object, int, ndarray) + object PyArray_Flatten (ndarray, NPY_ORDER) + object PyArray_Ravel (ndarray, NPY_ORDER) + npy_intp PyArray_MultiplyList (npy_intp *, int) + int PyArray_MultiplyIntList (int *, int) + void * PyArray_GetPtr (ndarray, npy_intp*) + int PyArray_CompareLists (npy_intp *, npy_intp *, int) + #int PyArray_AsCArray (object*, void *, npy_intp *, int, dtype) + #int PyArray_As1D (object*, char **, int *, int) + #int PyArray_As2D (object*, char ***, int *, int *, int) + int PyArray_Free (object, void *) + #int PyArray_Converter (object, object*) + int PyArray_IntpFromSequence (object, npy_intp *, int) + object PyArray_Concatenate (object, int) + object PyArray_InnerProduct (object, object) + object PyArray_MatrixProduct (object, object) + object PyArray_CopyAndTranspose (object) + object PyArray_Correlate (object, object, int) + int PyArray_TypestrConvert (int, int) + #int PyArray_DescrConverter (object, dtype*) + #int PyArray_DescrConverter2 (object, dtype*) + int PyArray_IntpConverter (object, PyArray_Dims *) + #int PyArray_BufferConverter (object, chunk) + int PyArray_AxisConverter (object, int *) + int PyArray_BoolConverter (object, npy_bool *) + int PyArray_ByteorderConverter (object, char *) + int PyArray_OrderConverter (object, NPY_ORDER *) + unsigned char PyArray_EquivTypes (dtype, dtype) + #object PyArray_Zeros (int, npy_intp *, dtype, int) + #object PyArray_Empty (int, npy_intp *, dtype, int) + object PyArray_Where (object, object, object) + object PyArray_Arange (double, double, double, int) + #object PyArray_ArangeObj (object, object, object, dtype) + int PyArray_SortkindConverter (object, NPY_SORTKIND *) + object PyArray_LexSort (object, int) + object PyArray_Round (ndarray, int, ndarray) + unsigned char PyArray_EquivTypenums (int, int) + int PyArray_RegisterDataType (dtype) + int PyArray_RegisterCastFunc (dtype, int, PyArray_VectorUnaryFunc *) + int PyArray_RegisterCanCast (dtype, int, NPY_SCALARKIND) + #void PyArray_InitArrFuncs (PyArray_ArrFuncs *) + object PyArray_IntTupleFromIntp (int, npy_intp *) + int PyArray_TypeNumFromName (char *) + int PyArray_ClipmodeConverter (object, NPY_CLIPMODE *) + #int PyArray_OutputConverter (object, ndarray*) + object PyArray_BroadcastToShape (object, npy_intp *, int) + void _PyArray_SigintHandler (int) + void* _PyArray_GetSigintBuf () + #int PyArray_DescrAlignConverter (object, dtype*) + #int PyArray_DescrAlignConverter2 (object, dtype*) + int PyArray_SearchsideConverter (object, void *) + object PyArray_CheckAxis (ndarray, int *, int) + npy_intp PyArray_OverflowMultiplyList (npy_intp *, int) + int PyArray_CompareString (char *, char *, size_t) + int PyArray_SetBaseObject(ndarray, base) # NOTE: steals a reference to base! Use "set_array_base()" instead. + + +# Typedefs that matches the runtime dtype objects in +# the numpy module. + +# The ones that are commented out needs an IFDEF function +# in Cython to enable them only on the right systems. + +ctypedef npy_int8 int8_t +ctypedef npy_int16 int16_t +ctypedef npy_int32 int32_t +ctypedef npy_int64 int64_t +#ctypedef npy_int96 int96_t +#ctypedef npy_int128 int128_t + +ctypedef npy_uint8 uint8_t +ctypedef npy_uint16 uint16_t +ctypedef npy_uint32 uint32_t +ctypedef npy_uint64 uint64_t +#ctypedef npy_uint96 uint96_t +#ctypedef npy_uint128 uint128_t + +ctypedef npy_float32 float32_t +ctypedef npy_float64 float64_t +#ctypedef npy_float80 float80_t +#ctypedef npy_float128 float128_t + +ctypedef float complex complex64_t +ctypedef double complex complex128_t + +# The int types are mapped a bit surprising -- +# numpy.int corresponds to 'l' and numpy.long to 'q' +ctypedef npy_long int_t +ctypedef npy_longlong long_t +ctypedef npy_longlong longlong_t + +ctypedef npy_ulong uint_t +ctypedef npy_ulonglong ulong_t +ctypedef npy_ulonglong ulonglong_t + +ctypedef npy_intp intp_t +ctypedef npy_uintp uintp_t + +ctypedef npy_double float_t +ctypedef npy_double double_t +ctypedef npy_longdouble longdouble_t + +ctypedef npy_cfloat cfloat_t +ctypedef npy_cdouble cdouble_t +ctypedef npy_clongdouble clongdouble_t + +ctypedef npy_cdouble complex_t + +cdef inline object PyArray_MultiIterNew1(a): + return PyArray_MultiIterNew(1, <void*>a) + +cdef inline object PyArray_MultiIterNew2(a, b): + return PyArray_MultiIterNew(2, <void*>a, <void*>b) + +cdef inline object PyArray_MultiIterNew3(a, b, c): + return PyArray_MultiIterNew(3, <void*>a, <void*>b, <void*> c) + +cdef inline object PyArray_MultiIterNew4(a, b, c, d): + return PyArray_MultiIterNew(4, <void*>a, <void*>b, <void*>c, <void*> d) + +cdef inline object PyArray_MultiIterNew5(a, b, c, d, e): + return PyArray_MultiIterNew(5, <void*>a, <void*>b, <void*>c, <void*> d, <void*> e) + +cdef inline tuple PyDataType_SHAPE(dtype d): + if PyDataType_HASSUBARRAY(d): + return <tuple>d.subarray.shape + else: + return () + + +cdef extern from "numpy/ndarrayobject.h": + PyTypeObject PyTimedeltaArrType_Type + PyTypeObject PyDatetimeArrType_Type + ctypedef int64_t npy_timedelta + ctypedef int64_t npy_datetime + +cdef extern from "numpy/ndarraytypes.h": + ctypedef struct PyArray_DatetimeMetaData: + NPY_DATETIMEUNIT base + int64_t num + +cdef extern from "numpy/arrayscalars.h": + + # abstract types + ctypedef class numpy.generic [object PyObject]: + pass + ctypedef class numpy.number [object PyObject]: + pass + ctypedef class numpy.integer [object PyObject]: + pass + ctypedef class numpy.signedinteger [object PyObject]: + pass + ctypedef class numpy.unsignedinteger [object PyObject]: + pass + ctypedef class numpy.inexact [object PyObject]: + pass + ctypedef class numpy.floating [object PyObject]: + pass + ctypedef class numpy.complexfloating [object PyObject]: + pass + ctypedef class numpy.flexible [object PyObject]: + pass + ctypedef class numpy.character [object PyObject]: + pass + + ctypedef struct PyDatetimeScalarObject: + # PyObject_HEAD + npy_datetime obval + PyArray_DatetimeMetaData obmeta + + ctypedef struct PyTimedeltaScalarObject: + # PyObject_HEAD + npy_timedelta obval + PyArray_DatetimeMetaData obmeta + + ctypedef enum NPY_DATETIMEUNIT: + NPY_FR_Y + NPY_FR_M + NPY_FR_W + NPY_FR_D + NPY_FR_B + NPY_FR_h + NPY_FR_m + NPY_FR_s + NPY_FR_ms + NPY_FR_us + NPY_FR_ns + NPY_FR_ps + NPY_FR_fs + NPY_FR_as + + +# +# ufunc API +# + +cdef extern from "numpy/ufuncobject.h": + + ctypedef void (*PyUFuncGenericFunction) (char **, npy_intp *, npy_intp *, void *) + + ctypedef class numpy.ufunc [object PyUFuncObject, check_size ignore]: + cdef: + int nin, nout, nargs + int identity + PyUFuncGenericFunction *functions + void **data + int ntypes + int check_return + char *name + char *types + char *doc + void *ptr + PyObject *obj + PyObject *userloops + + cdef enum: + PyUFunc_Zero + PyUFunc_One + PyUFunc_None + UFUNC_ERR_IGNORE + UFUNC_ERR_WARN + UFUNC_ERR_RAISE + UFUNC_ERR_CALL + UFUNC_ERR_PRINT + UFUNC_ERR_LOG + UFUNC_MASK_DIVIDEBYZERO + UFUNC_MASK_OVERFLOW + UFUNC_MASK_UNDERFLOW + UFUNC_MASK_INVALID + UFUNC_SHIFT_DIVIDEBYZERO + UFUNC_SHIFT_OVERFLOW + UFUNC_SHIFT_UNDERFLOW + UFUNC_SHIFT_INVALID + UFUNC_FPE_DIVIDEBYZERO + UFUNC_FPE_OVERFLOW + UFUNC_FPE_UNDERFLOW + UFUNC_FPE_INVALID + UFUNC_ERR_DEFAULT + UFUNC_ERR_DEFAULT2 + + object PyUFunc_FromFuncAndData(PyUFuncGenericFunction *, + void **, char *, int, int, int, int, char *, char *, int) + int PyUFunc_RegisterLoopForType(ufunc, int, + PyUFuncGenericFunction, int *, void *) + void PyUFunc_f_f_As_d_d \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_d_d \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_f_f \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_g_g \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_F_F_As_D_D \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_F_F \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_D_D \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_G_G \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_O_O \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_ff_f_As_dd_d \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_ff_f \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_dd_d \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_gg_g \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_FF_F_As_DD_D \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_DD_D \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_FF_F \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_GG_G \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_OO_O \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_O_O_method \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_OO_O_method \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_On_Om \ + (char **, npy_intp *, npy_intp *, void *) + int PyUFunc_GetPyValues \ + (char *, int *, int *, PyObject **) + int PyUFunc_checkfperr \ + (int, PyObject *, int *) + void PyUFunc_clearfperr() + int PyUFunc_getfperr() + int PyUFunc_handlefperr \ + (int, PyObject *, int, int *) + int PyUFunc_ReplaceLoopBySignature \ + (ufunc, PyUFuncGenericFunction, int *, PyUFuncGenericFunction *) + object PyUFunc_FromFuncAndDataAndSignature \ + (PyUFuncGenericFunction *, void **, char *, int, int, int, + int, char *, char *, int, char *) + + int _import_umath() except -1 + +cdef inline void set_array_base(ndarray arr, object base): + Py_INCREF(base) # important to do this before stealing the reference below! + PyArray_SetBaseObject(arr, base) + +cdef inline object get_array_base(ndarray arr): + base = PyArray_BASE(arr) + if base is NULL: + return None + return <object>base + +# Versions of the import_* functions which are more suitable for +# Cython code. +cdef inline int import_array() except -1: + try: + __pyx_import_array() + except Exception: + raise ImportError("numpy.core.multiarray failed to import") + +cdef inline int import_umath() except -1: + try: + _import_umath() + except Exception: + raise ImportError("numpy.core.umath failed to import") + +cdef inline int import_ufunc() except -1: + try: + _import_umath() + except Exception: + raise ImportError("numpy.core.umath failed to import") + +cdef extern from *: + # Leave a marker that the NumPy declarations came from this file + # See https://github.com/cython/cython/issues/3573 + """ + /* NumPy API declarations from "numpy/__init__.pxd" */ + """ + + +cdef inline bint is_timedelta64_object(object obj): + """ + Cython equivalent of `isinstance(obj, np.timedelta64)` + + Parameters + ---------- + obj : object + + Returns + ------- + bool + """ + return PyObject_TypeCheck(obj, &PyTimedeltaArrType_Type) + + +cdef inline bint is_datetime64_object(object obj): + """ + Cython equivalent of `isinstance(obj, np.datetime64)` + + Parameters + ---------- + obj : object + + Returns + ------- + bool + """ + return PyObject_TypeCheck(obj, &PyDatetimeArrType_Type) + + +cdef inline npy_datetime get_datetime64_value(object obj) nogil: + """ + returns the int64 value underlying scalar numpy datetime64 object + + Note that to interpret this as a datetime, the corresponding unit is + also needed. That can be found using `get_datetime64_unit`. + """ + return (<PyDatetimeScalarObject*>obj).obval + + +cdef inline npy_timedelta get_timedelta64_value(object obj) nogil: + """ + returns the int64 value underlying scalar numpy timedelta64 object + """ + return (<PyTimedeltaScalarObject*>obj).obval + + +cdef inline NPY_DATETIMEUNIT get_datetime64_unit(object obj) nogil: + """ + returns the unit part of the dtype for a numpy datetime64 object. + """ + return <NPY_DATETIMEUNIT>(<PyDatetimeScalarObject*>obj).obmeta.base diff --git a/numpy/__init__.py b/numpy/__init__.py index d250ed5ac53f..e8d1820a1406 100644 --- a/numpy/__init__.py +++ b/numpy/__init__.py @@ -11,10 +11,10 @@ ---------------------------- Documentation is available in two forms: docstrings provided with the code, and a loose standing reference guide, available from -`the NumPy homepage <http://www.scipy.org>`_. +`the NumPy homepage <https://www.scipy.org>`_. We recommend exploring the docstrings using -`IPython <http://ipython.scipy.org>`_, an advanced Python shell with +`IPython <https://ipython.org>`_, an advanced Python shell with TAB-completion and introspection capabilities. See below for further instructions. @@ -79,7 +79,9 @@ show_config Show numpy build configuration dual - Overwrite certain functions with high-performance Scipy tools + Overwrite certain functions with high-performance SciPy tools. + Note: `numpy.dual` is deprecated. Use the functions from NumPy or Scipy + directly instead of importing them from `numpy.dual`. matlib Make everything matrices. __version__ @@ -104,13 +106,13 @@ Exceptions to this rule are documented. """ -from __future__ import division, absolute_import, print_function - import sys import warnings -from ._globals import ModuleDeprecationWarning, VisibleDeprecationWarning -from ._globals import _NoValue +from ._globals import ( + ModuleDeprecationWarning, VisibleDeprecationWarning, + _NoValue, _CopyMode +) # We first need to detect if we're being called as part of the numpy setup # procedure itself in a reliable manner. @@ -124,27 +126,17 @@ else: try: from numpy.__config__ import show as show_config - except ImportError: + except ImportError as e: msg = """Error importing numpy: you should not try to import numpy from its source directory; please exit the numpy source tree, and relaunch your python interpreter from there.""" - raise ImportError(msg) - - from .version import git_revision as __git_revision__ - from .version import version as __version__ - - from ._import_tools import PackageLoader + raise ImportError(msg) from e - def pkgload(*packages, **options): - loader = PackageLoader(infunc=True) - return loader(*packages, **options) - - from . import add_newdocs - __all__ = ['add_newdocs', - 'ModuleDeprecationWarning', + __all__ = ['ModuleDeprecationWarning', 'VisibleDeprecationWarning'] - pkgload.__doc__ = PackageLoader.__call__.__doc__ + # mapping of {name: (value, deprecation_msg)} + __deprecated_attrs__ = {} # Allow distributors to run custom init code from . import _distributor_init @@ -153,7 +145,10 @@ def pkgload(*packages, **options): from .core import * from . import compat from . import lib + # NOTE: to be revisited following future namespace cleanup. + # See gh-14454 and gh-15672 for discussion. from .lib import * + from . import linalg from . import fft from . import polynomial @@ -162,40 +157,172 @@ def pkgload(*packages, **options): from . import ma from . import matrixlib as _mat from .matrixlib import * - from .compat import long - # Make these accessible from numpy name-space - # but not imported in from numpy import * - if sys.version_info[0] >= 3: - from builtins import bool, int, float, complex, object, str - unicode = str - else: - from __builtin__ import bool, int, float, complex, object, unicode, str + # Deprecations introduced in NumPy 1.20.0, 2020-06-06 + import builtins as _builtins + + _msg = ( + "`np.{n}` is a deprecated alias for the builtin `{n}`. " + "To silence this warning, use `{n}` by itself. Doing this will not " + "modify any behavior and is safe. {extended_msg}\n" + "Deprecated in NumPy 1.20; for more details and guidance: " + "https://numpy.org/devdocs/release/1.20.0-notes.html#deprecations") + + _specific_msg = ( + "If you specifically wanted the numpy scalar type, use `np.{}` here.") + + _int_extended_msg = ( + "When replacing `np.{}`, you may wish to use e.g. `np.int64` " + "or `np.int32` to specify the precision. If you wish to review " + "your current use, check the release note link for " + "additional information.") + + _type_info = [ + ("object", ""), # The NumPy scalar only exists by name. + ("bool", _specific_msg.format("bool_")), + ("float", _specific_msg.format("float64")), + ("complex", _specific_msg.format("complex128")), + ("str", _specific_msg.format("str_")), + ("int", _int_extended_msg.format("int"))] + + __deprecated_attrs__.update({ + n: (getattr(_builtins, n), _msg.format(n=n, extended_msg=extended_msg)) + for n, extended_msg in _type_info + }) + + # Numpy 1.20.0, 2020-10-19 + __deprecated_attrs__["typeDict"] = ( + core.numerictypes.typeDict, + "`np.typeDict` is a deprecated alias for `np.sctypeDict`." + ) + + # NumPy 1.22, 2021-10-20 + __deprecated_attrs__["MachAr"] = ( + core._machar.MachAr, + "`np.MachAr` is deprecated (NumPy 1.22)." + ) + + _msg = ( + "`np.{n}` is a deprecated alias for `np.compat.{n}`. " + "To silence this warning, use `np.compat.{n}` by itself. " + "In the likely event your code does not need to work on Python 2 " + "you can use the builtin `{n2}` for which `np.compat.{n}` is itself " + "an alias. Doing this will not modify any behaviour and is safe. " + "{extended_msg}\n" + "Deprecated in NumPy 1.20; for more details and guidance: " + "https://numpy.org/devdocs/release/1.20.0-notes.html#deprecations") + + __deprecated_attrs__["long"] = ( + getattr(compat, "long"), + _msg.format(n="long", n2="int", + extended_msg=_int_extended_msg.format("long"))) + + __deprecated_attrs__["unicode"] = ( + getattr(compat, "unicode"), + _msg.format(n="unicode", n2="str", + extended_msg=_specific_msg.format("str_"))) + + del _msg, _specific_msg, _int_extended_msg, _type_info, _builtins from .core import round, abs, max, min + # now that numpy modules are imported, can initialize limits + core.getlimits._register_known_types() - __all__.extend(['__version__', 'pkgload', 'PackageLoader', - 'show_config']) + __all__.extend(['__version__', 'show_config']) __all__.extend(core.__all__) __all__.extend(_mat.__all__) __all__.extend(lib.__all__) __all__.extend(['linalg', 'fft', 'random', 'ctypeslib', 'ma']) + # Remove one of the two occurrences of `issubdtype`, which is exposed as + # both `numpy.core.issubdtype` and `numpy.lib.issubdtype`. + __all__.remove('issubdtype') + + # These are exported by np.core, but are replaced by the builtins below + # remove them to ensure that we don't end up with `np.long == np.int_`, + # which would be a breaking change. + del long, unicode + __all__.remove('long') + __all__.remove('unicode') + + # Remove things that are in the numpy.lib but not in the numpy namespace + # Note that there is a test (numpy/tests/test_public_api.py:test_numpy_namespace) + # that prevents adding more things to the main namespace by accident. + # The list below will grow until the `from .lib import *` fixme above is + # taken care of + __all__.remove('Arrayterator') + del Arrayterator + + # These names were removed in NumPy 1.20. For at least one release, + # attempts to access these names in the numpy namespace will trigger + # a warning, and calling the function will raise an exception. + _financial_names = ['fv', 'ipmt', 'irr', 'mirr', 'nper', 'npv', 'pmt', + 'ppmt', 'pv', 'rate'] + __expired_functions__ = { + name: (f'In accordance with NEP 32, the function {name} was removed ' + 'from NumPy version 1.20. A replacement for this function ' + 'is available in the numpy_financial library: ' + 'https://pypi.org/project/numpy-financial') + for name in _financial_names} + + # Filter out Cython harmless warnings + warnings.filterwarnings("ignore", message="numpy.dtype size changed") + warnings.filterwarnings("ignore", message="numpy.ufunc size changed") + warnings.filterwarnings("ignore", message="numpy.ndarray size changed") + # oldnumeric and numarray were removed in 1.9. In case some packages import # but do not use them, we define them here for backward compatibility. oldnumeric = 'removed' numarray = 'removed' - # We don't actually use this ourselves anymore, but I'm not 100% sure that - # no-one else in the world is using it (though I hope not) - from .testing import Tester + def __getattr__(attr): + # Warn for expired attributes, and return a dummy function + # that always raises an exception. + try: + msg = __expired_functions__[attr] + except KeyError: + pass + else: + warnings.warn(msg, DeprecationWarning, stacklevel=2) + + def _expired(*args, **kwds): + raise RuntimeError(msg) + + return _expired + + # Emit warnings for deprecated attributes + try: + val, msg = __deprecated_attrs__[attr] + except KeyError: + pass + else: + warnings.warn(msg, DeprecationWarning, stacklevel=2) + return val + + # Importing Tester requires importing all of UnitTest which is not a + # cheap import Since it is mainly used in test suits, we lazy import it + # here to save on the order of 10 ms of import time for most users + # + # The previous way Tester was imported also had a side effect of adding + # the full `numpy.testing` namespace + if attr == 'testing': + import numpy.testing as testing + return testing + elif attr == 'Tester': + from .testing import Tester + return Tester + + raise AttributeError("module {!r} has no attribute " + "{!r}".format(__name__, attr)) + + def __dir__(): + return list(globals().keys() | {'Tester', 'testing'}) # Pytest testing - from numpy.testing._private.pytesttester import PytestTester + from numpy._pytesttester import PytestTester test = PytestTester(__name__) del PytestTester - def _sanity_check(): """ Quick sanity checks for common bugs caused by environment. @@ -214,8 +341,78 @@ def _sanity_check(): except AssertionError: msg = ("The current Numpy installation ({!r}) fails to " "pass simple sanity checks. This can be caused for example " - "by incorrect BLAS library being linked in.") - raise RuntimeError(msg.format(__file__)) + "by incorrect BLAS library being linked in, or by mixing " + "package managers (pip, conda, apt, ...). Search closed " + "numpy issues for similar problems.") + raise RuntimeError(msg.format(__file__)) from None _sanity_check() del _sanity_check + + def _mac_os_check(): + """ + Quick Sanity check for Mac OS look for accelerate build bugs. + Testing numpy polyfit calls init_dgelsd(LAPACK) + """ + try: + c = array([3., 2., 1.]) + x = linspace(0, 2, 5) + y = polyval(c, x) + _ = polyfit(x, y, 2, cov=True) + except ValueError: + pass + + import sys + if sys.platform == "darwin": + with warnings.catch_warnings(record=True) as w: + _mac_os_check() + # Throw runtime error, if the test failed Check for warning and error_message + error_message = "" + if len(w) > 0: + error_message = "{}: {}".format(w[-1].category.__name__, str(w[-1].message)) + msg = ( + "Polyfit sanity test emitted a warning, most likely due " + "to using a buggy Accelerate backend." + "\nIf you compiled yourself, more information is available at:" + "\nhttps://numpy.org/doc/stable/user/building.html#accelerated-blas-lapack-libraries" + "\nOtherwise report this to the vendor " + "that provided NumPy.\n{}\n".format(error_message)) + raise RuntimeError(msg) + del _mac_os_check + + # We usually use madvise hugepages support, but on some old kernels it + # is slow and thus better avoided. + # Specifically kernel version 4.6 had a bug fix which probably fixed this: + # https://github.com/torvalds/linux/commit/7cf91a98e607c2f935dbcc177d70011e95b8faff + import os + use_hugepage = os.environ.get("NUMPY_MADVISE_HUGEPAGE", None) + if sys.platform == "linux" and use_hugepage is None: + # If there is an issue with parsing the kernel version, + # set use_hugepages to 0. Usage of LooseVersion will handle + # the kernel version parsing better, but avoided since it + # will increase the import time. See: #16679 for related discussion. + try: + use_hugepage = 1 + kernel_version = os.uname().release.split(".")[:2] + kernel_version = tuple(int(v) for v in kernel_version) + if kernel_version < (4, 6): + use_hugepage = 0 + except ValueError: + use_hugepages = 0 + elif use_hugepage is None: + # This is not Linux, so it should not matter, just enable anyway + use_hugepage = 1 + else: + use_hugepage = int(use_hugepage) + + # Note that this will currently only make a difference on Linux + core.multiarray._set_madvise_hugepage(use_hugepage) + + # Give a warning if NumPy is reloaded or imported on a sub-interpreter + # We do this from python, since the C-module may not be reloaded and + # it is tidier organized. + core.multiarray._multiarray_umath._reload_guard() + + +# get the version using versioneer +from .version import __version__, git_revision as __git_version__ diff --git a/numpy/__init__.pyi b/numpy/__init__.pyi new file mode 100644 index 000000000000..eb1e81c6ac66 --- /dev/null +++ b/numpy/__init__.pyi @@ -0,0 +1,4341 @@ +import builtins +import os +import sys +import mmap +import ctypes as ct +import array as _array +import datetime as dt +import enum +from abc import abstractmethod +from types import TracebackType, MappingProxyType +from contextlib import ContextDecorator + +if sys.version_info >= (3, 9): + from types import GenericAlias + +from numpy._pytesttester import PytestTester +from numpy.core._internal import _ctypes + +from numpy.typing import ( + # Arrays + ArrayLike, + NDArray, + _SupportsArray, + _NestedSequence, + _FiniteNestedSequence, + _SupportsArray, + _ArrayLikeBool_co, + _ArrayLikeUInt_co, + _ArrayLikeInt_co, + _ArrayLikeFloat_co, + _ArrayLikeComplex_co, + _ArrayLikeNumber_co, + _ArrayLikeTD64_co, + _ArrayLikeDT64_co, + _ArrayLikeObject_co, + _ArrayLikeStr_co, + _ArrayLikeBytes_co, + + # DTypes + DTypeLike, + _SupportsDType, + _VoidDTypeLike, + + # Shapes + _Shape, + _ShapeLike, + + # Scalars + _CharLike_co, + _BoolLike_co, + _IntLike_co, + _FloatLike_co, + _ComplexLike_co, + _TD64Like_co, + _NumberLike_co, + _ScalarLike_co, + + # `number` precision + NBitBase, + _256Bit, + _128Bit, + _96Bit, + _80Bit, + _64Bit, + _32Bit, + _16Bit, + _8Bit, + _NBitByte, + _NBitShort, + _NBitIntC, + _NBitIntP, + _NBitInt, + _NBitLongLong, + _NBitHalf, + _NBitSingle, + _NBitDouble, + _NBitLongDouble, + + # Character codes + _BoolCodes, + _UInt8Codes, + _UInt16Codes, + _UInt32Codes, + _UInt64Codes, + _Int8Codes, + _Int16Codes, + _Int32Codes, + _Int64Codes, + _Float16Codes, + _Float32Codes, + _Float64Codes, + _Complex64Codes, + _Complex128Codes, + _ByteCodes, + _ShortCodes, + _IntCCodes, + _IntPCodes, + _IntCodes, + _LongLongCodes, + _UByteCodes, + _UShortCodes, + _UIntCCodes, + _UIntPCodes, + _UIntCodes, + _ULongLongCodes, + _HalfCodes, + _SingleCodes, + _DoubleCodes, + _LongDoubleCodes, + _CSingleCodes, + _CDoubleCodes, + _CLongDoubleCodes, + _DT64Codes, + _TD64Codes, + _StrCodes, + _BytesCodes, + _VoidCodes, + _ObjectCodes, + + # Ufuncs + _UFunc_Nin1_Nout1, + _UFunc_Nin2_Nout1, + _UFunc_Nin1_Nout2, + _UFunc_Nin2_Nout2, + _GUFunc_Nin2_Nout1, +) + +from numpy.typing._callable import ( + _BoolOp, + _BoolBitOp, + _BoolSub, + _BoolTrueDiv, + _BoolMod, + _BoolDivMod, + _TD64Div, + _IntTrueDiv, + _UnsignedIntOp, + _UnsignedIntBitOp, + _UnsignedIntMod, + _UnsignedIntDivMod, + _SignedIntOp, + _SignedIntBitOp, + _SignedIntMod, + _SignedIntDivMod, + _FloatOp, + _FloatMod, + _FloatDivMod, + _ComplexOp, + _NumberOp, + _ComparisonOp, +) + +# NOTE: Numpy's mypy plugin is used for removing the types unavailable +# to the specific platform +from numpy.typing._extended_precision import ( + uint128 as uint128, + uint256 as uint256, + int128 as int128, + int256 as int256, + float80 as float80, + float96 as float96, + float128 as float128, + float256 as float256, + complex160 as complex160, + complex192 as complex192, + complex256 as complex256, + complex512 as complex512, +) + +from typing import ( + Literal as L, + Any, + ByteString, + Callable, + Container, + Callable, + Dict, + Generic, + IO, + Iterable, + Iterator, + List, + Mapping, + NoReturn, + Optional, + overload, + Sequence, + Sized, + SupportsComplex, + SupportsFloat, + SupportsInt, + Text, + Tuple, + Type, + TypeVar, + Union, + Protocol, + SupportsIndex, + Final, + final, + ClassVar, + Set, +) + +# Ensures that the stubs are picked up +from numpy import ( + ctypeslib as ctypeslib, + fft as fft, + lib as lib, + linalg as linalg, + ma as ma, + matrixlib as matrixlib, + polynomial as polynomial, + random as random, + testing as testing, + version as version, +) + +from numpy.core import defchararray, records +char = defchararray +rec = records + +from numpy.core.function_base import ( + linspace as linspace, + logspace as logspace, + geomspace as geomspace, +) + +from numpy.core.fromnumeric import ( + take as take, + reshape as reshape, + choose as choose, + repeat as repeat, + put as put, + swapaxes as swapaxes, + transpose as transpose, + partition as partition, + argpartition as argpartition, + sort as sort, + argsort as argsort, + argmax as argmax, + argmin as argmin, + searchsorted as searchsorted, + resize as resize, + squeeze as squeeze, + diagonal as diagonal, + trace as trace, + ravel as ravel, + nonzero as nonzero, + shape as shape, + compress as compress, + clip as clip, + sum as sum, + all as all, + any as any, + cumsum as cumsum, + ptp as ptp, + amax as amax, + amin as amin, + prod as prod, + cumprod as cumprod, + ndim as ndim, + size as size, + around as around, + mean as mean, + std as std, + var as var, +) + +from numpy.core._asarray import ( + require as require, +) + +from numpy.core._type_aliases import ( + sctypes as sctypes, + sctypeDict as sctypeDict, +) + +from numpy.core._ufunc_config import ( + seterr as seterr, + geterr as geterr, + setbufsize as setbufsize, + getbufsize as getbufsize, + seterrcall as seterrcall, + geterrcall as geterrcall, + _ErrKind, + _ErrFunc, + _ErrDictOptional, +) + +from numpy.core.arrayprint import ( + set_printoptions as set_printoptions, + get_printoptions as get_printoptions, + array2string as array2string, + format_float_scientific as format_float_scientific, + format_float_positional as format_float_positional, + array_repr as array_repr, + array_str as array_str, + set_string_function as set_string_function, + printoptions as printoptions, +) + +from numpy.core.einsumfunc import ( + einsum as einsum, + einsum_path as einsum_path, +) + +from numpy.core.multiarray import ( + ALLOW_THREADS as ALLOW_THREADS, + BUFSIZE as BUFSIZE, + CLIP as CLIP, + MAXDIMS as MAXDIMS, + MAY_SHARE_BOUNDS as MAY_SHARE_BOUNDS, + MAY_SHARE_EXACT as MAY_SHARE_EXACT, + RAISE as RAISE, + WRAP as WRAP, + tracemalloc_domain as tracemalloc_domain, + array as array, + empty_like as empty_like, + empty as empty, + zeros as zeros, + concatenate as concatenate, + inner as inner, + where as where, + lexsort as lexsort, + can_cast as can_cast, + min_scalar_type as min_scalar_type, + result_type as result_type, + dot as dot, + vdot as vdot, + bincount as bincount, + copyto as copyto, + putmask as putmask, + packbits as packbits, + unpackbits as unpackbits, + shares_memory as shares_memory, + may_share_memory as may_share_memory, + asarray as asarray, + asanyarray as asanyarray, + ascontiguousarray as ascontiguousarray, + asfortranarray as asfortranarray, + arange as arange, + busday_count as busday_count, + busday_offset as busday_offset, + compare_chararrays as compare_chararrays, + datetime_as_string as datetime_as_string, + datetime_data as datetime_data, + frombuffer as frombuffer, + fromfile as fromfile, + fromiter as fromiter, + is_busday as is_busday, + promote_types as promote_types, + seterrobj as seterrobj, + geterrobj as geterrobj, + fromstring as fromstring, + frompyfunc as frompyfunc, + nested_iters as nested_iters, + flagsobj, +) + +from numpy.core.numeric import ( + zeros_like as zeros_like, + ones as ones, + ones_like as ones_like, + full as full, + full_like as full_like, + count_nonzero as count_nonzero, + isfortran as isfortran, + argwhere as argwhere, + flatnonzero as flatnonzero, + correlate as correlate, + convolve as convolve, + outer as outer, + tensordot as tensordot, + roll as roll, + rollaxis as rollaxis, + moveaxis as moveaxis, + cross as cross, + indices as indices, + fromfunction as fromfunction, + isscalar as isscalar, + binary_repr as binary_repr, + base_repr as base_repr, + identity as identity, + allclose as allclose, + isclose as isclose, + array_equal as array_equal, + array_equiv as array_equiv, +) + +from numpy.core.numerictypes import ( + maximum_sctype as maximum_sctype, + issctype as issctype, + obj2sctype as obj2sctype, + issubclass_ as issubclass_, + issubsctype as issubsctype, + issubdtype as issubdtype, + sctype2char as sctype2char, + find_common_type as find_common_type, + nbytes as nbytes, + cast as cast, + ScalarType as ScalarType, + typecodes as typecodes, +) + +from numpy.core.shape_base import ( + atleast_1d as atleast_1d, + atleast_2d as atleast_2d, + atleast_3d as atleast_3d, + block as block, + hstack as hstack, + stack as stack, + vstack as vstack, +) + +from numpy.lib import ( + emath as emath, +) + +from numpy.lib.arraypad import ( + pad as pad, +) + +from numpy.lib.arraysetops import ( + ediff1d as ediff1d, + intersect1d as intersect1d, + setxor1d as setxor1d, + union1d as union1d, + setdiff1d as setdiff1d, + unique as unique, + in1d as in1d, + isin as isin, +) + +from numpy.lib.arrayterator import ( + Arrayterator as Arrayterator, +) + +from numpy.lib.function_base import ( + select as select, + piecewise as piecewise, + trim_zeros as trim_zeros, + copy as copy, + iterable as iterable, + percentile as percentile, + diff as diff, + gradient as gradient, + angle as angle, + unwrap as unwrap, + sort_complex as sort_complex, + disp as disp, + flip as flip, + rot90 as rot90, + extract as extract, + place as place, + asarray_chkfinite as asarray_chkfinite, + average as average, + bincount as bincount, + digitize as digitize, + cov as cov, + corrcoef as corrcoef, + msort as msort, + median as median, + sinc as sinc, + hamming as hamming, + hanning as hanning, + bartlett as bartlett, + blackman as blackman, + kaiser as kaiser, + trapz as trapz, + i0 as i0, + add_newdoc as add_newdoc, + add_docstring as add_docstring, + meshgrid as meshgrid, + delete as delete, + insert as insert, + append as append, + interp as interp, + add_newdoc_ufunc as add_newdoc_ufunc, + quantile as quantile, +) + +from numpy.lib.histograms import ( + histogram_bin_edges as histogram_bin_edges, + histogram as histogram, + histogramdd as histogramdd, +) + +from numpy.lib.index_tricks import ( + ravel_multi_index as ravel_multi_index, + unravel_index as unravel_index, + mgrid as mgrid, + ogrid as ogrid, + r_ as r_, + c_ as c_, + s_ as s_, + index_exp as index_exp, + ix_ as ix_, + fill_diagonal as fill_diagonal, + diag_indices as diag_indices, + diag_indices_from as diag_indices_from, +) + +from numpy.lib.nanfunctions import ( + nansum as nansum, + nanmax as nanmax, + nanmin as nanmin, + nanargmax as nanargmax, + nanargmin as nanargmin, + nanmean as nanmean, + nanmedian as nanmedian, + nanpercentile as nanpercentile, + nanvar as nanvar, + nanstd as nanstd, + nanprod as nanprod, + nancumsum as nancumsum, + nancumprod as nancumprod, + nanquantile as nanquantile, +) + +from numpy.lib.npyio import ( + savetxt as savetxt, + loadtxt as loadtxt, + genfromtxt as genfromtxt, + recfromtxt as recfromtxt, + recfromcsv as recfromcsv, + load as load, + save as save, + savez as savez, + savez_compressed as savez_compressed, + packbits as packbits, + unpackbits as unpackbits, + fromregex as fromregex, +) + +from numpy.lib.polynomial import ( + poly as poly, + roots as roots, + polyint as polyint, + polyder as polyder, + polyadd as polyadd, + polysub as polysub, + polymul as polymul, + polydiv as polydiv, + polyval as polyval, + polyfit as polyfit, +) + +from numpy.lib.shape_base import ( + column_stack as column_stack, + row_stack as row_stack, + dstack as dstack, + array_split as array_split, + split as split, + hsplit as hsplit, + vsplit as vsplit, + dsplit as dsplit, + apply_over_axes as apply_over_axes, + expand_dims as expand_dims, + apply_along_axis as apply_along_axis, + kron as kron, + tile as tile, + get_array_wrap as get_array_wrap, + take_along_axis as take_along_axis, + put_along_axis as put_along_axis, +) + +from numpy.lib.stride_tricks import ( + broadcast_to as broadcast_to, + broadcast_arrays as broadcast_arrays, + broadcast_shapes as broadcast_shapes, +) + +from numpy.lib.twodim_base import ( + diag as diag, + diagflat as diagflat, + eye as eye, + fliplr as fliplr, + flipud as flipud, + tri as tri, + triu as triu, + tril as tril, + vander as vander, + histogram2d as histogram2d, + mask_indices as mask_indices, + tril_indices as tril_indices, + tril_indices_from as tril_indices_from, + triu_indices as triu_indices, + triu_indices_from as triu_indices_from, +) + +from numpy.lib.type_check import ( + mintypecode as mintypecode, + asfarray as asfarray, + real as real, + imag as imag, + iscomplex as iscomplex, + isreal as isreal, + iscomplexobj as iscomplexobj, + isrealobj as isrealobj, + nan_to_num as nan_to_num, + real_if_close as real_if_close, + typename as typename, + common_type as common_type, +) + +from numpy.lib.ufunclike import ( + fix as fix, + isposinf as isposinf, + isneginf as isneginf, +) + +from numpy.lib.utils import ( + issubclass_ as issubclass_, + issubsctype as issubsctype, + issubdtype as issubdtype, + deprecate as deprecate, + deprecate_with_doc as deprecate_with_doc, + get_include as get_include, + info as info, + source as source, + who as who, + lookfor as lookfor, + byte_bounds as byte_bounds, + safe_eval as safe_eval, +) + +from numpy.matrixlib import ( + asmatrix as asmatrix, + mat as mat, + bmat as bmat, +) + +_AnyStr_contra = TypeVar("_AnyStr_contra", str, bytes, contravariant=True) + +# Protocol for representing file-like-objects accepted +# by `ndarray.tofile` and `fromfile` +class _IOProtocol(Protocol): + def flush(self) -> object: ... + def fileno(self) -> int: ... + def tell(self) -> SupportsIndex: ... + def seek(self, offset: int, whence: int, /) -> object: ... + +# NOTE: `seek`, `write` and `flush` are technically only required +# for `readwrite`/`write` modes +class _MemMapIOProtocol(Protocol): + def flush(self) -> object: ... + def fileno(self) -> SupportsIndex: ... + def tell(self) -> int: ... + def seek(self, offset: int, whence: int, /) -> object: ... + def write(self, s: bytes, /) -> object: ... + @property + def read(self) -> object: ... + +class _SupportsWrite(Protocol[_AnyStr_contra]): + def write(self, s: _AnyStr_contra, /) -> object: ... + +__all__: List[str] +__path__: List[str] +__version__: str +__git_version__: str +test: PytestTester + +# TODO: Move placeholders to their respective module once +# their annotations are properly implemented +# +# Placeholders for classes + +# Some of these are aliases; others are wrappers with an identical signature +round = around +round_ = around +max = amax +min = amin +product = prod +cumproduct = cumprod +sometrue = any +alltrue = all + +def show_config() -> None: ... + +_NdArraySubClass = TypeVar("_NdArraySubClass", bound=ndarray) +_DTypeScalar_co = TypeVar("_DTypeScalar_co", covariant=True, bound=generic) +_ByteOrder = L["S", "<", ">", "=", "|", "L", "B", "N", "I"] + +class dtype(Generic[_DTypeScalar_co]): + names: None | Tuple[builtins.str, ...] + # Overload for subclass of generic + @overload + def __new__( + cls, + dtype: Type[_DTypeScalar_co], + align: bool = ..., + copy: bool = ..., + ) -> dtype[_DTypeScalar_co]: ... + # Overloads for string aliases, Python types, and some assorted + # other special cases. Order is sometimes important because of the + # subtype relationships + # + # bool < int < float < complex < object + # + # so we have to make sure the overloads for the narrowest type is + # first. + # Builtin types + @overload + def __new__(cls, dtype: Type[bool], align: bool = ..., copy: bool = ...) -> dtype[bool_]: ... + @overload + def __new__(cls, dtype: Type[int], align: bool = ..., copy: bool = ...) -> dtype[int_]: ... + @overload + def __new__(cls, dtype: None | Type[float], align: bool = ..., copy: bool = ...) -> dtype[float_]: ... + @overload + def __new__(cls, dtype: Type[complex], align: bool = ..., copy: bool = ...) -> dtype[complex_]: ... + @overload + def __new__(cls, dtype: Type[builtins.str], align: bool = ..., copy: bool = ...) -> dtype[str_]: ... + @overload + def __new__(cls, dtype: Type[bytes], align: bool = ..., copy: bool = ...) -> dtype[bytes_]: ... + + # `unsignedinteger` string-based representations and ctypes + @overload + def __new__(cls, dtype: _UInt8Codes | Type[ct.c_uint8], align: bool = ..., copy: bool = ...) -> dtype[uint8]: ... + @overload + def __new__(cls, dtype: _UInt16Codes | Type[ct.c_uint16], align: bool = ..., copy: bool = ...) -> dtype[uint16]: ... + @overload + def __new__(cls, dtype: _UInt32Codes | Type[ct.c_uint32], align: bool = ..., copy: bool = ...) -> dtype[uint32]: ... + @overload + def __new__(cls, dtype: _UInt64Codes | Type[ct.c_uint64], align: bool = ..., copy: bool = ...) -> dtype[uint64]: ... + @overload + def __new__(cls, dtype: _UByteCodes | Type[ct.c_ubyte], align: bool = ..., copy: bool = ...) -> dtype[ubyte]: ... + @overload + def __new__(cls, dtype: _UShortCodes | Type[ct.c_ushort], align: bool = ..., copy: bool = ...) -> dtype[ushort]: ... + @overload + def __new__(cls, dtype: _UIntCCodes | Type[ct.c_uint], align: bool = ..., copy: bool = ...) -> dtype[uintc]: ... + + # NOTE: We're assuming here that `uint_ptr_t == size_t`, + # an assumption that does not hold in rare cases (same for `ssize_t`) + @overload + def __new__(cls, dtype: _UIntPCodes | Type[ct.c_void_p] | Type[ct.c_size_t], align: bool = ..., copy: bool = ...) -> dtype[uintp]: ... + @overload + def __new__(cls, dtype: _UIntCodes | Type[ct.c_ulong], align: bool = ..., copy: bool = ...) -> dtype[uint]: ... + @overload + def __new__(cls, dtype: _ULongLongCodes | Type[ct.c_ulonglong], align: bool = ..., copy: bool = ...) -> dtype[ulonglong]: ... + + # `signedinteger` string-based representations and ctypes + @overload + def __new__(cls, dtype: _Int8Codes | Type[ct.c_int8], align: bool = ..., copy: bool = ...) -> dtype[int8]: ... + @overload + def __new__(cls, dtype: _Int16Codes | Type[ct.c_int16], align: bool = ..., copy: bool = ...) -> dtype[int16]: ... + @overload + def __new__(cls, dtype: _Int32Codes | Type[ct.c_int32], align: bool = ..., copy: bool = ...) -> dtype[int32]: ... + @overload + def __new__(cls, dtype: _Int64Codes | Type[ct.c_int64], align: bool = ..., copy: bool = ...) -> dtype[int64]: ... + @overload + def __new__(cls, dtype: _ByteCodes | Type[ct.c_byte], align: bool = ..., copy: bool = ...) -> dtype[byte]: ... + @overload + def __new__(cls, dtype: _ShortCodes | Type[ct.c_short], align: bool = ..., copy: bool = ...) -> dtype[short]: ... + @overload + def __new__(cls, dtype: _IntCCodes | Type[ct.c_int], align: bool = ..., copy: bool = ...) -> dtype[intc]: ... + @overload + def __new__(cls, dtype: _IntPCodes | Type[ct.c_ssize_t], align: bool = ..., copy: bool = ...) -> dtype[intp]: ... + @overload + def __new__(cls, dtype: _IntCodes | Type[ct.c_long], align: bool = ..., copy: bool = ...) -> dtype[int_]: ... + @overload + def __new__(cls, dtype: _LongLongCodes | Type[ct.c_longlong], align: bool = ..., copy: bool = ...) -> dtype[longlong]: ... + + # `floating` string-based representations and ctypes + @overload + def __new__(cls, dtype: _Float16Codes, align: bool = ..., copy: bool = ...) -> dtype[float16]: ... + @overload + def __new__(cls, dtype: _Float32Codes, align: bool = ..., copy: bool = ...) -> dtype[float32]: ... + @overload + def __new__(cls, dtype: _Float64Codes, align: bool = ..., copy: bool = ...) -> dtype[float64]: ... + @overload + def __new__(cls, dtype: _HalfCodes, align: bool = ..., copy: bool = ...) -> dtype[half]: ... + @overload + def __new__(cls, dtype: _SingleCodes | Type[ct.c_float], align: bool = ..., copy: bool = ...) -> dtype[single]: ... + @overload + def __new__(cls, dtype: _DoubleCodes | Type[ct.c_double], align: bool = ..., copy: bool = ...) -> dtype[double]: ... + @overload + def __new__(cls, dtype: _LongDoubleCodes | Type[ct.c_longdouble], align: bool = ..., copy: bool = ...) -> dtype[longdouble]: ... + + # `complexfloating` string-based representations + @overload + def __new__(cls, dtype: _Complex64Codes, align: bool = ..., copy: bool = ...) -> dtype[complex64]: ... + @overload + def __new__(cls, dtype: _Complex128Codes, align: bool = ..., copy: bool = ...) -> dtype[complex128]: ... + @overload + def __new__(cls, dtype: _CSingleCodes, align: bool = ..., copy: bool = ...) -> dtype[csingle]: ... + @overload + def __new__(cls, dtype: _CDoubleCodes, align: bool = ..., copy: bool = ...) -> dtype[cdouble]: ... + @overload + def __new__(cls, dtype: _CLongDoubleCodes, align: bool = ..., copy: bool = ...) -> dtype[clongdouble]: ... + + # Miscellaneous string-based representations and ctypes + @overload + def __new__(cls, dtype: _BoolCodes | Type[ct.c_bool], align: bool = ..., copy: bool = ...) -> dtype[bool_]: ... + @overload + def __new__(cls, dtype: _TD64Codes, align: bool = ..., copy: bool = ...) -> dtype[timedelta64]: ... + @overload + def __new__(cls, dtype: _DT64Codes, align: bool = ..., copy: bool = ...) -> dtype[datetime64]: ... + @overload + def __new__(cls, dtype: _StrCodes, align: bool = ..., copy: bool = ...) -> dtype[str_]: ... + @overload + def __new__(cls, dtype: _BytesCodes | Type[ct.c_char], align: bool = ..., copy: bool = ...) -> dtype[bytes_]: ... + @overload + def __new__(cls, dtype: _VoidCodes, align: bool = ..., copy: bool = ...) -> dtype[void]: ... + @overload + def __new__(cls, dtype: _ObjectCodes | Type[ct.py_object], align: bool = ..., copy: bool = ...) -> dtype[object_]: ... + + # dtype of a dtype is the same dtype + @overload + def __new__( + cls, + dtype: dtype[_DTypeScalar_co], + align: bool = ..., + copy: bool = ..., + ) -> dtype[_DTypeScalar_co]: ... + @overload + def __new__( + cls, + dtype: _SupportsDType[dtype[_DTypeScalar_co]], + align: bool = ..., + copy: bool = ..., + ) -> dtype[_DTypeScalar_co]: ... + # Handle strings that can't be expressed as literals; i.e. s1, s2, ... + @overload + def __new__( + cls, + dtype: builtins.str, + align: bool = ..., + copy: bool = ..., + ) -> dtype[Any]: ... + # Catchall overload for void-likes + @overload + def __new__( + cls, + dtype: _VoidDTypeLike, + align: bool = ..., + copy: bool = ..., + ) -> dtype[void]: ... + # Catchall overload for object-likes + @overload + def __new__( + cls, + dtype: Type[object], + align: bool = ..., + copy: bool = ..., + ) -> dtype[object_]: ... + + if sys.version_info >= (3, 9): + def __class_getitem__(self, item: Any) -> GenericAlias: ... + + @overload + def __getitem__(self: dtype[void], key: List[builtins.str]) -> dtype[void]: ... + @overload + def __getitem__(self: dtype[void], key: builtins.str | SupportsIndex) -> dtype[Any]: ... + + # NOTE: In the future 1-based multiplications will also yield `flexible` dtypes + @overload + def __mul__(self: _DType, value: L[1]) -> _DType: ... + @overload + def __mul__(self: _FlexDType, value: SupportsIndex) -> _FlexDType: ... + @overload + def __mul__(self, value: SupportsIndex) -> dtype[void]: ... + + # NOTE: `__rmul__` seems to be broken when used in combination with + # literals as of mypy 0.902. Set the return-type to `dtype[Any]` for + # now for non-flexible dtypes. + @overload + def __rmul__(self: _FlexDType, value: SupportsIndex) -> _FlexDType: ... + @overload + def __rmul__(self, value: SupportsIndex) -> dtype[Any]: ... + + def __gt__(self, other: DTypeLike) -> bool: ... + def __ge__(self, other: DTypeLike) -> bool: ... + def __lt__(self, other: DTypeLike) -> bool: ... + def __le__(self, other: DTypeLike) -> bool: ... + + # Explicitly defined `__eq__` and `__ne__` to get around mypy's + # `strict_equality` option; even though their signatures are + # identical to their `object`-based counterpart + def __eq__(self, other: Any) -> bool: ... + def __ne__(self, other: Any) -> bool: ... + + @property + def alignment(self) -> int: ... + @property + def base(self) -> dtype[Any]: ... + @property + def byteorder(self) -> builtins.str: ... + @property + def char(self) -> builtins.str: ... + @property + def descr(self) -> List[Tuple[builtins.str, builtins.str] | Tuple[builtins.str, builtins.str, _Shape]]: ... + @property + def fields( + self, + ) -> None | MappingProxyType[builtins.str, Tuple[dtype[Any], int] | Tuple[dtype[Any], int, Any]]: ... + @property + def flags(self) -> int: ... + @property + def hasobject(self) -> bool: ... + @property + def isbuiltin(self) -> int: ... + @property + def isnative(self) -> bool: ... + @property + def isalignedstruct(self) -> bool: ... + @property + def itemsize(self) -> int: ... + @property + def kind(self) -> builtins.str: ... + @property + def metadata(self) -> None | MappingProxyType[builtins.str, Any]: ... + @property + def name(self) -> builtins.str: ... + @property + def num(self) -> int: ... + @property + def shape(self) -> _Shape: ... + @property + def ndim(self) -> int: ... + @property + def subdtype(self) -> None | Tuple[dtype[Any], _Shape]: ... + def newbyteorder(self: _DType, __new_order: _ByteOrder = ...) -> _DType: ... + @property + def str(self) -> builtins.str: ... + @property + def type(self) -> Type[_DTypeScalar_co]: ... + +_ArrayLikeInt = Union[ + int, + integer, + Sequence[Union[int, integer]], + Sequence[Sequence[Any]], # TODO: wait for support for recursive types + ndarray +] + +_FlatIterSelf = TypeVar("_FlatIterSelf", bound=flatiter) + +class flatiter(Generic[_NdArraySubClass]): + @property + def base(self) -> _NdArraySubClass: ... + @property + def coords(self) -> _Shape: ... + @property + def index(self) -> int: ... + def copy(self) -> _NdArraySubClass: ... + def __iter__(self: _FlatIterSelf) -> _FlatIterSelf: ... + def __next__(self: flatiter[ndarray[Any, dtype[_ScalarType]]]) -> _ScalarType: ... + def __len__(self) -> int: ... + @overload + def __getitem__( + self: flatiter[ndarray[Any, dtype[_ScalarType]]], + key: Union[int, integer], + ) -> _ScalarType: ... + @overload + def __getitem__( + self, key: Union[_ArrayLikeInt, slice, ellipsis], + ) -> _NdArraySubClass: ... + @overload + def __array__(self: flatiter[ndarray[Any, _DType]], dtype: None = ..., /) -> ndarray[Any, _DType]: ... + @overload + def __array__(self, dtype: _DType, /) -> ndarray[Any, _DType]: ... + +_OrderKACF = Optional[L["K", "A", "C", "F"]] +_OrderACF = Optional[L["A", "C", "F"]] +_OrderCF = Optional[L["C", "F"]] + +_ModeKind = L["raise", "wrap", "clip"] +_PartitionKind = L["introselect"] +_SortKind = L["quicksort", "mergesort", "heapsort", "stable"] +_SortSide = L["left", "right"] + +_ArraySelf = TypeVar("_ArraySelf", bound=_ArrayOrScalarCommon) + +class _ArrayOrScalarCommon: + @property + def T(self: _ArraySelf) -> _ArraySelf: ... + @property + def data(self) -> memoryview: ... + @property + def flags(self) -> flagsobj: ... + @property + def itemsize(self) -> int: ... + @property + def nbytes(self) -> int: ... + def __bool__(self) -> bool: ... + def __bytes__(self) -> bytes: ... + def __str__(self) -> str: ... + def __repr__(self) -> str: ... + def __copy__(self: _ArraySelf) -> _ArraySelf: ... + def __deepcopy__(self: _ArraySelf, memo: None | Dict[int, Any], /) -> _ArraySelf: ... + + # TODO: How to deal with the non-commutative nature of `==` and `!=`? + # xref numpy/numpy#17368 + def __eq__(self, other: Any) -> Any: ... + def __ne__(self, other: Any) -> Any: ... + def copy(self: _ArraySelf, order: _OrderKACF = ...) -> _ArraySelf: ... + def dump(self, file: str | bytes | os.PathLike[str] | os.PathLike[bytes] | _SupportsWrite[bytes]) -> None: ... + def dumps(self) -> bytes: ... + def tobytes(self, order: _OrderKACF = ...) -> bytes: ... + # NOTE: `tostring()` is deprecated and therefore excluded + # def tostring(self, order=...): ... + def tofile( + self, + fid: str | bytes | os.PathLike[str] | os.PathLike[bytes] | _IOProtocol, + sep: str = ..., + format: str = ..., + ) -> None: ... + # generics and 0d arrays return builtin scalars + def tolist(self) -> Any: ... + + @property + def __array_interface__(self) -> Dict[str, Any]: ... + @property + def __array_priority__(self) -> float: ... + @property + def __array_struct__(self) -> Any: ... # builtins.PyCapsule + def __setstate__(self, state: Tuple[ + SupportsIndex, # version + _ShapeLike, # Shape + _DType_co, # DType + bool, # F-continuous + bytes | List[Any], # Data + ], /) -> None: ... + # a `bool_` is returned when `keepdims=True` and `self` is a 0d array + + @overload + def all( + self, + axis: None = ..., + out: None = ..., + keepdims: L[False] = ..., + ) -> bool_: ... + @overload + def all( + self, + axis: Optional[_ShapeLike] = ..., + out: None = ..., + keepdims: bool = ..., + ) -> Any: ... + @overload + def all( + self, + axis: Optional[_ShapeLike] = ..., + out: _NdArraySubClass = ..., + keepdims: bool = ..., + ) -> _NdArraySubClass: ... + + @overload + def any( + self, + axis: None = ..., + out: None = ..., + keepdims: L[False] = ..., + ) -> bool_: ... + @overload + def any( + self, + axis: Optional[_ShapeLike] = ..., + out: None = ..., + keepdims: bool = ..., + ) -> Any: ... + @overload + def any( + self, + axis: Optional[_ShapeLike] = ..., + out: _NdArraySubClass = ..., + keepdims: bool = ..., + ) -> _NdArraySubClass: ... + + @overload + def argmax( + self, + axis: None = ..., + out: None = ..., + *, + keepdims: L[False] = ..., + ) -> intp: ... + @overload + def argmax( + self, + axis: _ShapeLike = ..., + out: None = ..., + *, + keepdims: bool = ..., + ) -> Any: ... + @overload + def argmax( + self, + axis: Optional[_ShapeLike] = ..., + out: _NdArraySubClass = ..., + *, + keepdims: bool = ..., + ) -> _NdArraySubClass: ... + + @overload + def argmin( + self, + axis: None = ..., + out: None = ..., + *, + keepdims: L[False] = ..., + ) -> intp: ... + @overload + def argmin( + self, + axis: _ShapeLike = ..., + out: None = ..., + *, + keepdims: bool = ..., + ) -> Any: ... + @overload + def argmin( + self, + axis: Optional[_ShapeLike] = ..., + out: _NdArraySubClass = ..., + *, + keepdims: bool = ..., + ) -> _NdArraySubClass: ... + + def argsort( + self, + axis: Optional[SupportsIndex] = ..., + kind: Optional[_SortKind] = ..., + order: Union[None, str, Sequence[str]] = ..., + ) -> ndarray: ... + + @overload + def choose( + self, + choices: ArrayLike, + out: None = ..., + mode: _ModeKind = ..., + ) -> ndarray: ... + @overload + def choose( + self, + choices: ArrayLike, + out: _NdArraySubClass = ..., + mode: _ModeKind = ..., + ) -> _NdArraySubClass: ... + + @overload + def clip( + self, + min: ArrayLike = ..., + max: Optional[ArrayLike] = ..., + out: None = ..., + **kwargs: Any, + ) -> ndarray: ... + @overload + def clip( + self, + min: None = ..., + max: ArrayLike = ..., + out: None = ..., + **kwargs: Any, + ) -> ndarray: ... + @overload + def clip( + self, + min: ArrayLike = ..., + max: Optional[ArrayLike] = ..., + out: _NdArraySubClass = ..., + **kwargs: Any, + ) -> _NdArraySubClass: ... + @overload + def clip( + self, + min: None = ..., + max: ArrayLike = ..., + out: _NdArraySubClass = ..., + **kwargs: Any, + ) -> _NdArraySubClass: ... + + @overload + def compress( + self, + a: ArrayLike, + axis: Optional[SupportsIndex] = ..., + out: None = ..., + ) -> ndarray: ... + @overload + def compress( + self, + a: ArrayLike, + axis: Optional[SupportsIndex] = ..., + out: _NdArraySubClass = ..., + ) -> _NdArraySubClass: ... + + def conj(self: _ArraySelf) -> _ArraySelf: ... + + def conjugate(self: _ArraySelf) -> _ArraySelf: ... + + @overload + def cumprod( + self, + axis: Optional[SupportsIndex] = ..., + dtype: DTypeLike = ..., + out: None = ..., + ) -> ndarray: ... + @overload + def cumprod( + self, + axis: Optional[SupportsIndex] = ..., + dtype: DTypeLike = ..., + out: _NdArraySubClass = ..., + ) -> _NdArraySubClass: ... + + @overload + def cumsum( + self, + axis: Optional[SupportsIndex] = ..., + dtype: DTypeLike = ..., + out: None = ..., + ) -> ndarray: ... + @overload + def cumsum( + self, + axis: Optional[SupportsIndex] = ..., + dtype: DTypeLike = ..., + out: _NdArraySubClass = ..., + ) -> _NdArraySubClass: ... + + @overload + def max( + self, + axis: Optional[_ShapeLike] = ..., + out: None = ..., + keepdims: bool = ..., + initial: _NumberLike_co = ..., + where: _ArrayLikeBool_co = ..., + ) -> Any: ... + @overload + def max( + self, + axis: Optional[_ShapeLike] = ..., + out: _NdArraySubClass = ..., + keepdims: bool = ..., + initial: _NumberLike_co = ..., + where: _ArrayLikeBool_co = ..., + ) -> _NdArraySubClass: ... + + @overload + def mean( + self, + axis: Optional[_ShapeLike] = ..., + dtype: DTypeLike = ..., + out: None = ..., + keepdims: bool = ..., + ) -> Any: ... + @overload + def mean( + self, + axis: Optional[_ShapeLike] = ..., + dtype: DTypeLike = ..., + out: _NdArraySubClass = ..., + keepdims: bool = ..., + ) -> _NdArraySubClass: ... + + @overload + def min( + self, + axis: Optional[_ShapeLike] = ..., + out: None = ..., + keepdims: bool = ..., + initial: _NumberLike_co = ..., + where: _ArrayLikeBool_co = ..., + ) -> Any: ... + @overload + def min( + self, + axis: Optional[_ShapeLike] = ..., + out: _NdArraySubClass = ..., + keepdims: bool = ..., + initial: _NumberLike_co = ..., + where: _ArrayLikeBool_co = ..., + ) -> _NdArraySubClass: ... + + def newbyteorder( + self: _ArraySelf, + __new_order: _ByteOrder = ..., + ) -> _ArraySelf: ... + + @overload + def prod( + self, + axis: Optional[_ShapeLike] = ..., + dtype: DTypeLike = ..., + out: None = ..., + keepdims: bool = ..., + initial: _NumberLike_co = ..., + where: _ArrayLikeBool_co = ..., + ) -> Any: ... + @overload + def prod( + self, + axis: Optional[_ShapeLike] = ..., + dtype: DTypeLike = ..., + out: _NdArraySubClass = ..., + keepdims: bool = ..., + initial: _NumberLike_co = ..., + where: _ArrayLikeBool_co = ..., + ) -> _NdArraySubClass: ... + + @overload + def ptp( + self, + axis: Optional[_ShapeLike] = ..., + out: None = ..., + keepdims: bool = ..., + ) -> Any: ... + @overload + def ptp( + self, + axis: Optional[_ShapeLike] = ..., + out: _NdArraySubClass = ..., + keepdims: bool = ..., + ) -> _NdArraySubClass: ... + + @overload + def round( + self: _ArraySelf, + decimals: SupportsIndex = ..., + out: None = ..., + ) -> _ArraySelf: ... + @overload + def round( + self, + decimals: SupportsIndex = ..., + out: _NdArraySubClass = ..., + ) -> _NdArraySubClass: ... + + @overload + def std( + self, + axis: Optional[_ShapeLike] = ..., + dtype: DTypeLike = ..., + out: None = ..., + ddof: int = ..., + keepdims: bool = ..., + ) -> Any: ... + @overload + def std( + self, + axis: Optional[_ShapeLike] = ..., + dtype: DTypeLike = ..., + out: _NdArraySubClass = ..., + ddof: int = ..., + keepdims: bool = ..., + ) -> _NdArraySubClass: ... + + @overload + def sum( + self, + axis: Optional[_ShapeLike] = ..., + dtype: DTypeLike = ..., + out: None = ..., + keepdims: bool = ..., + initial: _NumberLike_co = ..., + where: _ArrayLikeBool_co = ..., + ) -> Any: ... + @overload + def sum( + self, + axis: Optional[_ShapeLike] = ..., + dtype: DTypeLike = ..., + out: _NdArraySubClass = ..., + keepdims: bool = ..., + initial: _NumberLike_co = ..., + where: _ArrayLikeBool_co = ..., + ) -> _NdArraySubClass: ... + + @overload + def var( + self, + axis: Optional[_ShapeLike] = ..., + dtype: DTypeLike = ..., + out: None = ..., + ddof: int = ..., + keepdims: bool = ..., + ) -> Any: ... + @overload + def var( + self, + axis: Optional[_ShapeLike] = ..., + dtype: DTypeLike = ..., + out: _NdArraySubClass = ..., + ddof: int = ..., + keepdims: bool = ..., + ) -> _NdArraySubClass: ... + +_DType = TypeVar("_DType", bound=dtype[Any]) +_DType_co = TypeVar("_DType_co", covariant=True, bound=dtype[Any]) +_FlexDType = TypeVar("_FlexDType", bound=dtype[flexible]) + +# TODO: Set the `bound` to something more suitable once we +# have proper shape support +_ShapeType = TypeVar("_ShapeType", bound=Any) +_ShapeType2 = TypeVar("_ShapeType2", bound=Any) +_NumberType = TypeVar("_NumberType", bound=number[Any]) + +# There is currently no exhaustive way to type the buffer protocol, +# as it is implemented exclusivelly in the C API (python/typing#593) +_SupportsBuffer = Union[ + bytes, + bytearray, + memoryview, + _array.array[Any], + mmap.mmap, + NDArray[Any], + generic, +] + +_T = TypeVar("_T") +_T_co = TypeVar("_T_co", covariant=True) +_T_contra = TypeVar("_T_contra", contravariant=True) +_2Tuple = Tuple[_T, _T] +_CastingKind = L["no", "equiv", "safe", "same_kind", "unsafe"] + +_DTypeLike = Union[ + dtype[_ScalarType], + Type[_ScalarType], + _SupportsDType[dtype[_ScalarType]], +] + +_ArrayUInt_co = NDArray[Union[bool_, unsignedinteger[Any]]] +_ArrayInt_co = NDArray[Union[bool_, integer[Any]]] +_ArrayFloat_co = NDArray[Union[bool_, integer[Any], floating[Any]]] +_ArrayComplex_co = NDArray[Union[bool_, integer[Any], floating[Any], complexfloating[Any, Any]]] +_ArrayNumber_co = NDArray[Union[bool_, number[Any]]] +_ArrayTD64_co = NDArray[Union[bool_, integer[Any], timedelta64]] + +# Introduce an alias for `dtype` to avoid naming conflicts. +_dtype = dtype + +# `builtins.PyCapsule` unfortunately lacks annotations as of the moment; +# use `Any` as a stopgap measure +_PyCapsule = Any + +class _SupportsItem(Protocol[_T_co]): + def item(self, args: Any, /) -> _T_co: ... + +class _SupportsReal(Protocol[_T_co]): + @property + def real(self) -> _T_co: ... + +class _SupportsImag(Protocol[_T_co]): + @property + def imag(self) -> _T_co: ... + +class ndarray(_ArrayOrScalarCommon, Generic[_ShapeType, _DType_co]): + @property + def base(self) -> Optional[ndarray]: ... + @property + def ndim(self) -> int: ... + @property + def size(self) -> int: ... + @property + def real( + self: NDArray[_SupportsReal[_ScalarType]], # type: ignore[type-var] + ) -> ndarray[_ShapeType, _dtype[_ScalarType]]: ... + @real.setter + def real(self, value: ArrayLike) -> None: ... + @property + def imag( + self: NDArray[_SupportsImag[_ScalarType]], # type: ignore[type-var] + ) -> ndarray[_ShapeType, _dtype[_ScalarType]]: ... + @imag.setter + def imag(self, value: ArrayLike) -> None: ... + def __new__( + cls: Type[_ArraySelf], + shape: _ShapeLike, + dtype: DTypeLike = ..., + buffer: None | _SupportsBuffer = ..., + offset: SupportsIndex = ..., + strides: None | _ShapeLike = ..., + order: _OrderKACF = ..., + ) -> _ArraySelf: ... + + if sys.version_info >= (3, 9): + def __class_getitem__(self, item: Any) -> GenericAlias: ... + + @overload + def __array__(self, dtype: None = ..., /) -> ndarray[Any, _DType_co]: ... + @overload + def __array__(self, dtype: _DType, /) -> ndarray[Any, _DType]: ... + + def __array_ufunc__( + self, + ufunc: ufunc, + method: L["__call__", "reduce", "reduceat", "accumulate", "outer", "inner"], + *inputs: Any, + **kwargs: Any, + ) -> Any: ... + + def __array_function__( + self, + func: Callable[..., Any], + types: Iterable[type], + args: Iterable[Any], + kwargs: Mapping[str, Any], + ) -> Any: ... + + @property + def __array_finalize__(self) -> None: ... + + def __array_wrap__( + self, + array: ndarray[_ShapeType2, _DType], + context: None | Tuple[ufunc, Tuple[Any, ...], int] = ..., + /, + ) -> ndarray[_ShapeType2, _DType]: ... + + def __array_prepare__( + self, + array: ndarray[_ShapeType2, _DType], + context: None | Tuple[ufunc, Tuple[Any, ...], int] = ..., + /, + ) -> ndarray[_ShapeType2, _DType]: ... + + @overload + def __getitem__(self, key: Union[ + SupportsIndex, + _ArrayLikeInt_co, + Tuple[SupportsIndex | _ArrayLikeInt_co, ...], + ]) -> Any: ... + @overload + def __getitem__(self, key: Union[ + None, + slice, + ellipsis, + SupportsIndex, + _ArrayLikeInt_co, + Tuple[None | slice | ellipsis | _ArrayLikeInt_co | SupportsIndex, ...], + ]) -> ndarray[Any, _DType_co]: ... + @overload + def __getitem__(self: NDArray[void], key: str) -> NDArray[Any]: ... + @overload + def __getitem__(self: NDArray[void], key: list[str]) -> ndarray[_ShapeType, _dtype[void]]: ... + + @property + def ctypes(self) -> _ctypes[int]: ... + @property + def shape(self) -> _Shape: ... + @shape.setter + def shape(self, value: _ShapeLike) -> None: ... + @property + def strides(self) -> _Shape: ... + @strides.setter + def strides(self, value: _ShapeLike) -> None: ... + def byteswap(self: _ArraySelf, inplace: bool = ...) -> _ArraySelf: ... + def fill(self, value: Any) -> None: ... + @property + def flat(self: _NdArraySubClass) -> flatiter[_NdArraySubClass]: ... + + # Use the same output type as that of the underlying `generic` + @overload + def item( + self: ndarray[Any, _dtype[_SupportsItem[_T]]], # type: ignore[type-var] + *args: SupportsIndex, + ) -> _T: ... + @overload + def item( + self: ndarray[Any, _dtype[_SupportsItem[_T]]], # type: ignore[type-var] + args: Tuple[SupportsIndex, ...], + /, + ) -> _T: ... + + @overload + def itemset(self, value: Any, /) -> None: ... + @overload + def itemset(self, item: _ShapeLike, value: Any, /) -> None: ... + + @overload + def resize(self, new_shape: _ShapeLike, /, *, refcheck: bool = ...) -> None: ... + @overload + def resize(self, *new_shape: SupportsIndex, refcheck: bool = ...) -> None: ... + + def setflags( + self, write: bool = ..., align: bool = ..., uic: bool = ... + ) -> None: ... + + def squeeze( + self, + axis: Union[SupportsIndex, Tuple[SupportsIndex, ...]] = ..., + ) -> ndarray[Any, _DType_co]: ... + + def swapaxes( + self, + axis1: SupportsIndex, + axis2: SupportsIndex, + ) -> ndarray[Any, _DType_co]: ... + + @overload + def transpose(self: _ArraySelf, axes: _ShapeLike, /) -> _ArraySelf: ... + @overload + def transpose(self: _ArraySelf, *axes: SupportsIndex) -> _ArraySelf: ... + + def argpartition( + self, + kth: _ArrayLikeInt_co, + axis: Optional[SupportsIndex] = ..., + kind: _PartitionKind = ..., + order: Union[None, str, Sequence[str]] = ..., + ) -> ndarray[Any, _dtype[intp]]: ... + + def diagonal( + self, + offset: SupportsIndex = ..., + axis1: SupportsIndex = ..., + axis2: SupportsIndex = ..., + ) -> ndarray[Any, _DType_co]: ... + + # 1D + 1D returns a scalar; + # all other with at least 1 non-0D array return an ndarray. + @overload + def dot(self, b: _ScalarLike_co, out: None = ...) -> ndarray: ... + @overload + def dot(self, b: ArrayLike, out: None = ...) -> Any: ... # type: ignore[misc] + @overload + def dot(self, b: ArrayLike, out: _NdArraySubClass) -> _NdArraySubClass: ... + + # `nonzero()` is deprecated for 0d arrays/generics + def nonzero(self) -> Tuple[ndarray[Any, _dtype[intp]], ...]: ... + + def partition( + self, + kth: _ArrayLikeInt_co, + axis: SupportsIndex = ..., + kind: _PartitionKind = ..., + order: Union[None, str, Sequence[str]] = ..., + ) -> None: ... + + # `put` is technically available to `generic`, + # but is pointless as `generic`s are immutable + def put( + self, + ind: _ArrayLikeInt_co, + v: ArrayLike, + mode: _ModeKind = ..., + ) -> None: ... + + @overload + def searchsorted( # type: ignore[misc] + self, # >= 1D array + v: _ScalarLike_co, # 0D array-like + side: _SortSide = ..., + sorter: Optional[_ArrayLikeInt_co] = ..., + ) -> intp: ... + @overload + def searchsorted( + self, # >= 1D array + v: ArrayLike, + side: _SortSide = ..., + sorter: Optional[_ArrayLikeInt_co] = ..., + ) -> ndarray[Any, _dtype[intp]]: ... + + def setfield( + self, + val: ArrayLike, + dtype: DTypeLike, + offset: SupportsIndex = ..., + ) -> None: ... + + def sort( + self, + axis: SupportsIndex = ..., + kind: Optional[_SortKind] = ..., + order: Union[None, str, Sequence[str]] = ..., + ) -> None: ... + + @overload + def trace( + self, # >= 2D array + offset: SupportsIndex = ..., + axis1: SupportsIndex = ..., + axis2: SupportsIndex = ..., + dtype: DTypeLike = ..., + out: None = ..., + ) -> Any: ... + @overload + def trace( + self, # >= 2D array + offset: SupportsIndex = ..., + axis1: SupportsIndex = ..., + axis2: SupportsIndex = ..., + dtype: DTypeLike = ..., + out: _NdArraySubClass = ..., + ) -> _NdArraySubClass: ... + + @overload + def take( # type: ignore[misc] + self: ndarray[Any, _dtype[_ScalarType]], + indices: _IntLike_co, + axis: Optional[SupportsIndex] = ..., + out: None = ..., + mode: _ModeKind = ..., + ) -> _ScalarType: ... + @overload + def take( # type: ignore[misc] + self, + indices: _ArrayLikeInt_co, + axis: Optional[SupportsIndex] = ..., + out: None = ..., + mode: _ModeKind = ..., + ) -> ndarray[Any, _DType_co]: ... + @overload + def take( + self, + indices: _ArrayLikeInt_co, + axis: Optional[SupportsIndex] = ..., + out: _NdArraySubClass = ..., + mode: _ModeKind = ..., + ) -> _NdArraySubClass: ... + + def repeat( + self, + repeats: _ArrayLikeInt_co, + axis: Optional[SupportsIndex] = ..., + ) -> ndarray[Any, _DType_co]: ... + + def flatten( + self, + order: _OrderKACF = ..., + ) -> ndarray[Any, _DType_co]: ... + + def ravel( + self, + order: _OrderKACF = ..., + ) -> ndarray[Any, _DType_co]: ... + + @overload + def reshape( + self, shape: _ShapeLike, /, *, order: _OrderACF = ... + ) -> ndarray[Any, _DType_co]: ... + @overload + def reshape( + self, *shape: SupportsIndex, order: _OrderACF = ... + ) -> ndarray[Any, _DType_co]: ... + + @overload + def astype( + self, + dtype: _DTypeLike[_ScalarType], + order: _OrderKACF = ..., + casting: _CastingKind = ..., + subok: bool = ..., + copy: bool | _CopyMode = ..., + ) -> NDArray[_ScalarType]: ... + @overload + def astype( + self, + dtype: DTypeLike, + order: _OrderKACF = ..., + casting: _CastingKind = ..., + subok: bool = ..., + copy: bool | _CopyMode = ..., + ) -> NDArray[Any]: ... + + @overload + def view(self: _ArraySelf) -> _ArraySelf: ... + @overload + def view(self, type: Type[_NdArraySubClass]) -> _NdArraySubClass: ... + @overload + def view(self, dtype: _DTypeLike[_ScalarType]) -> NDArray[_ScalarType]: ... + @overload + def view(self, dtype: DTypeLike) -> NDArray[Any]: ... + @overload + def view( + self, + dtype: DTypeLike, + type: Type[_NdArraySubClass], + ) -> _NdArraySubClass: ... + + @overload + def getfield( + self, + dtype: _DTypeLike[_ScalarType], + offset: SupportsIndex = ... + ) -> NDArray[_ScalarType]: ... + @overload + def getfield( + self, + dtype: DTypeLike, + offset: SupportsIndex = ... + ) -> NDArray[Any]: ... + + # Dispatch to the underlying `generic` via protocols + def __int__( + self: ndarray[Any, _dtype[SupportsInt]], # type: ignore[type-var] + ) -> int: ... + + def __float__( + self: ndarray[Any, _dtype[SupportsFloat]], # type: ignore[type-var] + ) -> float: ... + + def __complex__( + self: ndarray[Any, _dtype[SupportsComplex]], # type: ignore[type-var] + ) -> complex: ... + + def __index__( + self: ndarray[Any, _dtype[SupportsIndex]], # type: ignore[type-var] + ) -> int: ... + + def __len__(self) -> int: ... + def __setitem__(self, key, value): ... + def __iter__(self) -> Any: ... + def __contains__(self, key) -> bool: ... + + # The last overload is for catching recursive objects whose + # nesting is too deep. + # The first overload is for catching `bytes` (as they are a subtype of + # `Sequence[int]`) and `str`. As `str` is a recursive sequence of + # strings, it will pass through the final overload otherwise + + @overload + def __lt__(self: _ArrayNumber_co, other: _ArrayLikeNumber_co) -> NDArray[bool_]: ... + @overload + def __lt__(self: _ArrayTD64_co, other: _ArrayLikeTD64_co) -> NDArray[bool_]: ... + @overload + def __lt__(self: NDArray[datetime64], other: _ArrayLikeDT64_co) -> NDArray[bool_]: ... + @overload + def __lt__(self: NDArray[object_], other: Any) -> NDArray[bool_]: ... + @overload + def __lt__(self: NDArray[Any], other: _ArrayLikeObject_co) -> NDArray[bool_]: ... + + @overload + def __le__(self: _ArrayNumber_co, other: _ArrayLikeNumber_co) -> NDArray[bool_]: ... + @overload + def __le__(self: _ArrayTD64_co, other: _ArrayLikeTD64_co) -> NDArray[bool_]: ... + @overload + def __le__(self: NDArray[datetime64], other: _ArrayLikeDT64_co) -> NDArray[bool_]: ... + @overload + def __le__(self: NDArray[object_], other: Any) -> NDArray[bool_]: ... + @overload + def __le__(self: NDArray[Any], other: _ArrayLikeObject_co) -> NDArray[bool_]: ... + + @overload + def __gt__(self: _ArrayNumber_co, other: _ArrayLikeNumber_co) -> NDArray[bool_]: ... + @overload + def __gt__(self: _ArrayTD64_co, other: _ArrayLikeTD64_co) -> NDArray[bool_]: ... + @overload + def __gt__(self: NDArray[datetime64], other: _ArrayLikeDT64_co) -> NDArray[bool_]: ... + @overload + def __gt__(self: NDArray[object_], other: Any) -> NDArray[bool_]: ... + @overload + def __gt__(self: NDArray[Any], other: _ArrayLikeObject_co) -> NDArray[bool_]: ... + + @overload + def __ge__(self: _ArrayNumber_co, other: _ArrayLikeNumber_co) -> NDArray[bool_]: ... + @overload + def __ge__(self: _ArrayTD64_co, other: _ArrayLikeTD64_co) -> NDArray[bool_]: ... + @overload + def __ge__(self: NDArray[datetime64], other: _ArrayLikeDT64_co) -> NDArray[bool_]: ... + @overload + def __ge__(self: NDArray[object_], other: Any) -> NDArray[bool_]: ... + @overload + def __ge__(self: NDArray[Any], other: _ArrayLikeObject_co) -> NDArray[bool_]: ... + + # Unary ops + @overload + def __abs__(self: NDArray[bool_]) -> NDArray[bool_]: ... + @overload + def __abs__(self: NDArray[complexfloating[_NBit1, _NBit1]]) -> NDArray[floating[_NBit1]]: ... + @overload + def __abs__(self: NDArray[_NumberType]) -> NDArray[_NumberType]: ... + @overload + def __abs__(self: NDArray[timedelta64]) -> NDArray[timedelta64]: ... + @overload + def __abs__(self: NDArray[object_]) -> Any: ... + + @overload + def __invert__(self: NDArray[bool_]) -> NDArray[bool_]: ... + @overload + def __invert__(self: NDArray[_IntType]) -> NDArray[_IntType]: ... + @overload + def __invert__(self: NDArray[object_]) -> Any: ... + + @overload + def __pos__(self: NDArray[_NumberType]) -> NDArray[_NumberType]: ... + @overload + def __pos__(self: NDArray[timedelta64]) -> NDArray[timedelta64]: ... + @overload + def __pos__(self: NDArray[object_]) -> Any: ... + + @overload + def __neg__(self: NDArray[_NumberType]) -> NDArray[_NumberType]: ... + @overload + def __neg__(self: NDArray[timedelta64]) -> NDArray[timedelta64]: ... + @overload + def __neg__(self: NDArray[object_]) -> Any: ... + + # Binary ops + # NOTE: `ndarray` does not implement `__imatmul__` + @overload + def __matmul__(self: NDArray[bool_], other: _ArrayLikeBool_co) -> NDArray[bool_]: ... # type: ignore[misc] + @overload + def __matmul__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co) -> NDArray[unsignedinteger[Any]]: ... # type: ignore[misc] + @overload + def __matmul__(self: _ArrayInt_co, other: _ArrayLikeInt_co) -> NDArray[signedinteger[Any]]: ... # type: ignore[misc] + @overload + def __matmul__(self: _ArrayFloat_co, other: _ArrayLikeFloat_co) -> NDArray[floating[Any]]: ... # type: ignore[misc] + @overload + def __matmul__(self: _ArrayComplex_co, other: _ArrayLikeComplex_co) -> NDArray[complexfloating[Any, Any]]: ... + @overload + def __matmul__(self: NDArray[object_], other: Any) -> Any: ... + @overload + def __matmul__(self: NDArray[Any], other: _ArrayLikeObject_co) -> Any: ... + + @overload + def __rmatmul__(self: NDArray[bool_], other: _ArrayLikeBool_co) -> NDArray[bool_]: ... # type: ignore[misc] + @overload + def __rmatmul__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co) -> NDArray[unsignedinteger[Any]]: ... # type: ignore[misc] + @overload + def __rmatmul__(self: _ArrayInt_co, other: _ArrayLikeInt_co) -> NDArray[signedinteger[Any]]: ... # type: ignore[misc] + @overload + def __rmatmul__(self: _ArrayFloat_co, other: _ArrayLikeFloat_co) -> NDArray[floating[Any]]: ... # type: ignore[misc] + @overload + def __rmatmul__(self: _ArrayComplex_co, other: _ArrayLikeComplex_co) -> NDArray[complexfloating[Any, Any]]: ... + @overload + def __rmatmul__(self: NDArray[object_], other: Any) -> Any: ... + @overload + def __rmatmul__(self: NDArray[Any], other: _ArrayLikeObject_co) -> Any: ... + + @overload + def __mod__(self: NDArray[bool_], other: _ArrayLikeBool_co) -> NDArray[int8]: ... # type: ignore[misc] + @overload + def __mod__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co) -> NDArray[unsignedinteger[Any]]: ... # type: ignore[misc] + @overload + def __mod__(self: _ArrayInt_co, other: _ArrayLikeInt_co) -> NDArray[signedinteger[Any]]: ... # type: ignore[misc] + @overload + def __mod__(self: _ArrayFloat_co, other: _ArrayLikeFloat_co) -> NDArray[floating[Any]]: ... # type: ignore[misc] + @overload + def __mod__(self: _ArrayTD64_co, other: _SupportsArray[_dtype[timedelta64]] | _NestedSequence[_SupportsArray[_dtype[timedelta64]]]) -> NDArray[timedelta64]: ... + @overload + def __mod__(self: NDArray[object_], other: Any) -> Any: ... + @overload + def __mod__(self: NDArray[Any], other: _ArrayLikeObject_co) -> Any: ... + + @overload + def __rmod__(self: NDArray[bool_], other: _ArrayLikeBool_co) -> NDArray[int8]: ... # type: ignore[misc] + @overload + def __rmod__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co) -> NDArray[unsignedinteger[Any]]: ... # type: ignore[misc] + @overload + def __rmod__(self: _ArrayInt_co, other: _ArrayLikeInt_co) -> NDArray[signedinteger[Any]]: ... # type: ignore[misc] + @overload + def __rmod__(self: _ArrayFloat_co, other: _ArrayLikeFloat_co) -> NDArray[floating[Any]]: ... # type: ignore[misc] + @overload + def __rmod__(self: _ArrayTD64_co, other: _SupportsArray[_dtype[timedelta64]] | _NestedSequence[_SupportsArray[_dtype[timedelta64]]]) -> NDArray[timedelta64]: ... + @overload + def __rmod__(self: NDArray[object_], other: Any) -> Any: ... + @overload + def __rmod__(self: NDArray[Any], other: _ArrayLikeObject_co) -> Any: ... + + @overload + def __divmod__(self: NDArray[bool_], other: _ArrayLikeBool_co) -> _2Tuple[NDArray[int8]]: ... # type: ignore[misc] + @overload + def __divmod__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co) -> _2Tuple[NDArray[unsignedinteger[Any]]]: ... # type: ignore[misc] + @overload + def __divmod__(self: _ArrayInt_co, other: _ArrayLikeInt_co) -> _2Tuple[NDArray[signedinteger[Any]]]: ... # type: ignore[misc] + @overload + def __divmod__(self: _ArrayFloat_co, other: _ArrayLikeFloat_co) -> _2Tuple[NDArray[floating[Any]]]: ... # type: ignore[misc] + @overload + def __divmod__(self: _ArrayTD64_co, other: _SupportsArray[_dtype[timedelta64]] | _NestedSequence[_SupportsArray[_dtype[timedelta64]]]) -> Tuple[NDArray[int64], NDArray[timedelta64]]: ... + + @overload + def __rdivmod__(self: NDArray[bool_], other: _ArrayLikeBool_co) -> _2Tuple[NDArray[int8]]: ... # type: ignore[misc] + @overload + def __rdivmod__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co) -> _2Tuple[NDArray[unsignedinteger[Any]]]: ... # type: ignore[misc] + @overload + def __rdivmod__(self: _ArrayInt_co, other: _ArrayLikeInt_co) -> _2Tuple[NDArray[signedinteger[Any]]]: ... # type: ignore[misc] + @overload + def __rdivmod__(self: _ArrayFloat_co, other: _ArrayLikeFloat_co) -> _2Tuple[NDArray[floating[Any]]]: ... # type: ignore[misc] + @overload + def __rdivmod__(self: _ArrayTD64_co, other: _SupportsArray[_dtype[timedelta64]] | _NestedSequence[_SupportsArray[_dtype[timedelta64]]]) -> Tuple[NDArray[int64], NDArray[timedelta64]]: ... + + @overload + def __add__(self: NDArray[bool_], other: _ArrayLikeBool_co) -> NDArray[bool_]: ... # type: ignore[misc] + @overload + def __add__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co) -> NDArray[unsignedinteger[Any]]: ... # type: ignore[misc] + @overload + def __add__(self: _ArrayInt_co, other: _ArrayLikeInt_co) -> NDArray[signedinteger[Any]]: ... # type: ignore[misc] + @overload + def __add__(self: _ArrayFloat_co, other: _ArrayLikeFloat_co) -> NDArray[floating[Any]]: ... # type: ignore[misc] + @overload + def __add__(self: _ArrayComplex_co, other: _ArrayLikeComplex_co) -> NDArray[complexfloating[Any, Any]]: ... # type: ignore[misc] + @overload + def __add__(self: _ArrayTD64_co, other: _ArrayLikeTD64_co) -> NDArray[timedelta64]: ... # type: ignore[misc] + @overload + def __add__(self: _ArrayTD64_co, other: _ArrayLikeDT64_co) -> NDArray[datetime64]: ... + @overload + def __add__(self: NDArray[datetime64], other: _ArrayLikeTD64_co) -> NDArray[datetime64]: ... + @overload + def __add__(self: NDArray[object_], other: Any) -> Any: ... + @overload + def __add__(self: NDArray[Any], other: _ArrayLikeObject_co) -> Any: ... + + @overload + def __radd__(self: NDArray[bool_], other: _ArrayLikeBool_co) -> NDArray[bool_]: ... # type: ignore[misc] + @overload + def __radd__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co) -> NDArray[unsignedinteger[Any]]: ... # type: ignore[misc] + @overload + def __radd__(self: _ArrayInt_co, other: _ArrayLikeInt_co) -> NDArray[signedinteger[Any]]: ... # type: ignore[misc] + @overload + def __radd__(self: _ArrayFloat_co, other: _ArrayLikeFloat_co) -> NDArray[floating[Any]]: ... # type: ignore[misc] + @overload + def __radd__(self: _ArrayComplex_co, other: _ArrayLikeComplex_co) -> NDArray[complexfloating[Any, Any]]: ... # type: ignore[misc] + @overload + def __radd__(self: _ArrayTD64_co, other: _ArrayLikeTD64_co) -> NDArray[timedelta64]: ... # type: ignore[misc] + @overload + def __radd__(self: _ArrayTD64_co, other: _ArrayLikeDT64_co) -> NDArray[datetime64]: ... + @overload + def __radd__(self: NDArray[datetime64], other: _ArrayLikeTD64_co) -> NDArray[datetime64]: ... + @overload + def __radd__(self: NDArray[object_], other: Any) -> Any: ... + @overload + def __radd__(self: NDArray[Any], other: _ArrayLikeObject_co) -> Any: ... + + @overload + def __sub__(self: NDArray[bool_], other: _ArrayLikeBool_co) -> NoReturn: ... + @overload + def __sub__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co) -> NDArray[unsignedinteger[Any]]: ... # type: ignore[misc] + @overload + def __sub__(self: _ArrayInt_co, other: _ArrayLikeInt_co) -> NDArray[signedinteger[Any]]: ... # type: ignore[misc] + @overload + def __sub__(self: _ArrayFloat_co, other: _ArrayLikeFloat_co) -> NDArray[floating[Any]]: ... # type: ignore[misc] + @overload + def __sub__(self: _ArrayComplex_co, other: _ArrayLikeComplex_co) -> NDArray[complexfloating[Any, Any]]: ... # type: ignore[misc] + @overload + def __sub__(self: _ArrayTD64_co, other: _ArrayLikeTD64_co) -> NDArray[timedelta64]: ... # type: ignore[misc] + @overload + def __sub__(self: NDArray[datetime64], other: _ArrayLikeTD64_co) -> NDArray[datetime64]: ... + @overload + def __sub__(self: NDArray[datetime64], other: _ArrayLikeDT64_co) -> NDArray[timedelta64]: ... + @overload + def __sub__(self: NDArray[object_], other: Any) -> Any: ... + @overload + def __sub__(self: NDArray[Any], other: _ArrayLikeObject_co) -> Any: ... + + @overload + def __rsub__(self: NDArray[bool_], other: _ArrayLikeBool_co) -> NoReturn: ... + @overload + def __rsub__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co) -> NDArray[unsignedinteger[Any]]: ... # type: ignore[misc] + @overload + def __rsub__(self: _ArrayInt_co, other: _ArrayLikeInt_co) -> NDArray[signedinteger[Any]]: ... # type: ignore[misc] + @overload + def __rsub__(self: _ArrayFloat_co, other: _ArrayLikeFloat_co) -> NDArray[floating[Any]]: ... # type: ignore[misc] + @overload + def __rsub__(self: _ArrayComplex_co, other: _ArrayLikeComplex_co) -> NDArray[complexfloating[Any, Any]]: ... # type: ignore[misc] + @overload + def __rsub__(self: _ArrayTD64_co, other: _ArrayLikeTD64_co) -> NDArray[timedelta64]: ... # type: ignore[misc] + @overload + def __rsub__(self: _ArrayTD64_co, other: _ArrayLikeDT64_co) -> NDArray[datetime64]: ... # type: ignore[misc] + @overload + def __rsub__(self: NDArray[datetime64], other: _ArrayLikeDT64_co) -> NDArray[timedelta64]: ... + @overload + def __rsub__(self: NDArray[object_], other: Any) -> Any: ... + @overload + def __rsub__(self: NDArray[Any], other: _ArrayLikeObject_co) -> Any: ... + + @overload + def __mul__(self: NDArray[bool_], other: _ArrayLikeBool_co) -> NDArray[bool_]: ... # type: ignore[misc] + @overload + def __mul__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co) -> NDArray[unsignedinteger[Any]]: ... # type: ignore[misc] + @overload + def __mul__(self: _ArrayInt_co, other: _ArrayLikeInt_co) -> NDArray[signedinteger[Any]]: ... # type: ignore[misc] + @overload + def __mul__(self: _ArrayFloat_co, other: _ArrayLikeFloat_co) -> NDArray[floating[Any]]: ... # type: ignore[misc] + @overload + def __mul__(self: _ArrayComplex_co, other: _ArrayLikeComplex_co) -> NDArray[complexfloating[Any, Any]]: ... # type: ignore[misc] + @overload + def __mul__(self: _ArrayTD64_co, other: _ArrayLikeFloat_co) -> NDArray[timedelta64]: ... + @overload + def __mul__(self: _ArrayFloat_co, other: _ArrayLikeTD64_co) -> NDArray[timedelta64]: ... + @overload + def __mul__(self: NDArray[object_], other: Any) -> Any: ... + @overload + def __mul__(self: NDArray[Any], other: _ArrayLikeObject_co) -> Any: ... + + @overload + def __rmul__(self: NDArray[bool_], other: _ArrayLikeBool_co) -> NDArray[bool_]: ... # type: ignore[misc] + @overload + def __rmul__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co) -> NDArray[unsignedinteger[Any]]: ... # type: ignore[misc] + @overload + def __rmul__(self: _ArrayInt_co, other: _ArrayLikeInt_co) -> NDArray[signedinteger[Any]]: ... # type: ignore[misc] + @overload + def __rmul__(self: _ArrayFloat_co, other: _ArrayLikeFloat_co) -> NDArray[floating[Any]]: ... # type: ignore[misc] + @overload + def __rmul__(self: _ArrayComplex_co, other: _ArrayLikeComplex_co) -> NDArray[complexfloating[Any, Any]]: ... # type: ignore[misc] + @overload + def __rmul__(self: _ArrayTD64_co, other: _ArrayLikeFloat_co) -> NDArray[timedelta64]: ... + @overload + def __rmul__(self: _ArrayFloat_co, other: _ArrayLikeTD64_co) -> NDArray[timedelta64]: ... + @overload + def __rmul__(self: NDArray[object_], other: Any) -> Any: ... + @overload + def __rmul__(self: NDArray[Any], other: _ArrayLikeObject_co) -> Any: ... + + @overload + def __floordiv__(self: NDArray[bool_], other: _ArrayLikeBool_co) -> NDArray[int8]: ... # type: ignore[misc] + @overload + def __floordiv__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co) -> NDArray[unsignedinteger[Any]]: ... # type: ignore[misc] + @overload + def __floordiv__(self: _ArrayInt_co, other: _ArrayLikeInt_co) -> NDArray[signedinteger[Any]]: ... # type: ignore[misc] + @overload + def __floordiv__(self: _ArrayFloat_co, other: _ArrayLikeFloat_co) -> NDArray[floating[Any]]: ... # type: ignore[misc] + @overload + def __floordiv__(self: NDArray[timedelta64], other: _SupportsArray[_dtype[timedelta64]] | _NestedSequence[_SupportsArray[_dtype[timedelta64]]]) -> NDArray[int64]: ... + @overload + def __floordiv__(self: NDArray[timedelta64], other: _ArrayLikeBool_co) -> NoReturn: ... + @overload + def __floordiv__(self: NDArray[timedelta64], other: _ArrayLikeFloat_co) -> NDArray[timedelta64]: ... + @overload + def __floordiv__(self: NDArray[object_], other: Any) -> Any: ... + @overload + def __floordiv__(self: NDArray[Any], other: _ArrayLikeObject_co) -> Any: ... + + @overload + def __rfloordiv__(self: NDArray[bool_], other: _ArrayLikeBool_co) -> NDArray[int8]: ... # type: ignore[misc] + @overload + def __rfloordiv__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co) -> NDArray[unsignedinteger[Any]]: ... # type: ignore[misc] + @overload + def __rfloordiv__(self: _ArrayInt_co, other: _ArrayLikeInt_co) -> NDArray[signedinteger[Any]]: ... # type: ignore[misc] + @overload + def __rfloordiv__(self: _ArrayFloat_co, other: _ArrayLikeFloat_co) -> NDArray[floating[Any]]: ... # type: ignore[misc] + @overload + def __rfloordiv__(self: NDArray[timedelta64], other: _SupportsArray[_dtype[timedelta64]] | _NestedSequence[_SupportsArray[_dtype[timedelta64]]]) -> NDArray[int64]: ... + @overload + def __rfloordiv__(self: NDArray[bool_], other: _ArrayLikeTD64_co) -> NoReturn: ... + @overload + def __rfloordiv__(self: _ArrayFloat_co, other: _ArrayLikeTD64_co) -> NDArray[timedelta64]: ... + @overload + def __rfloordiv__(self: NDArray[object_], other: Any) -> Any: ... + @overload + def __rfloordiv__(self: NDArray[Any], other: _ArrayLikeObject_co) -> Any: ... + + @overload + def __pow__(self: NDArray[bool_], other: _ArrayLikeBool_co) -> NDArray[int8]: ... # type: ignore[misc] + @overload + def __pow__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co) -> NDArray[unsignedinteger[Any]]: ... # type: ignore[misc] + @overload + def __pow__(self: _ArrayInt_co, other: _ArrayLikeInt_co) -> NDArray[signedinteger[Any]]: ... # type: ignore[misc] + @overload + def __pow__(self: _ArrayFloat_co, other: _ArrayLikeFloat_co) -> NDArray[floating[Any]]: ... # type: ignore[misc] + @overload + def __pow__(self: _ArrayComplex_co, other: _ArrayLikeComplex_co) -> NDArray[complexfloating[Any, Any]]: ... + @overload + def __pow__(self: NDArray[object_], other: Any) -> Any: ... + @overload + def __pow__(self: NDArray[Any], other: _ArrayLikeObject_co) -> Any: ... + + @overload + def __rpow__(self: NDArray[bool_], other: _ArrayLikeBool_co) -> NDArray[int8]: ... # type: ignore[misc] + @overload + def __rpow__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co) -> NDArray[unsignedinteger[Any]]: ... # type: ignore[misc] + @overload + def __rpow__(self: _ArrayInt_co, other: _ArrayLikeInt_co) -> NDArray[signedinteger[Any]]: ... # type: ignore[misc] + @overload + def __rpow__(self: _ArrayFloat_co, other: _ArrayLikeFloat_co) -> NDArray[floating[Any]]: ... # type: ignore[misc] + @overload + def __rpow__(self: _ArrayComplex_co, other: _ArrayLikeComplex_co) -> NDArray[complexfloating[Any, Any]]: ... + @overload + def __rpow__(self: NDArray[object_], other: Any) -> Any: ... + @overload + def __rpow__(self: NDArray[Any], other: _ArrayLikeObject_co) -> Any: ... + + @overload + def __truediv__(self: _ArrayInt_co, other: _ArrayInt_co) -> NDArray[float64]: ... # type: ignore[misc] + @overload + def __truediv__(self: _ArrayFloat_co, other: _ArrayLikeFloat_co) -> NDArray[floating[Any]]: ... # type: ignore[misc] + @overload + def __truediv__(self: _ArrayComplex_co, other: _ArrayLikeComplex_co) -> NDArray[complexfloating[Any, Any]]: ... # type: ignore[misc] + @overload + def __truediv__(self: NDArray[timedelta64], other: _SupportsArray[_dtype[timedelta64]] | _NestedSequence[_SupportsArray[_dtype[timedelta64]]]) -> NDArray[float64]: ... + @overload + def __truediv__(self: NDArray[timedelta64], other: _ArrayLikeBool_co) -> NoReturn: ... + @overload + def __truediv__(self: NDArray[timedelta64], other: _ArrayLikeFloat_co) -> NDArray[timedelta64]: ... + @overload + def __truediv__(self: NDArray[object_], other: Any) -> Any: ... + @overload + def __truediv__(self: NDArray[Any], other: _ArrayLikeObject_co) -> Any: ... + + @overload + def __rtruediv__(self: _ArrayInt_co, other: _ArrayInt_co) -> NDArray[float64]: ... # type: ignore[misc] + @overload + def __rtruediv__(self: _ArrayFloat_co, other: _ArrayLikeFloat_co) -> NDArray[floating[Any]]: ... # type: ignore[misc] + @overload + def __rtruediv__(self: _ArrayComplex_co, other: _ArrayLikeComplex_co) -> NDArray[complexfloating[Any, Any]]: ... # type: ignore[misc] + @overload + def __rtruediv__(self: NDArray[timedelta64], other: _SupportsArray[_dtype[timedelta64]] | _NestedSequence[_SupportsArray[_dtype[timedelta64]]]) -> NDArray[float64]: ... + @overload + def __rtruediv__(self: NDArray[bool_], other: _ArrayLikeTD64_co) -> NoReturn: ... + @overload + def __rtruediv__(self: _ArrayFloat_co, other: _ArrayLikeTD64_co) -> NDArray[timedelta64]: ... + @overload + def __rtruediv__(self: NDArray[object_], other: Any) -> Any: ... + @overload + def __rtruediv__(self: NDArray[Any], other: _ArrayLikeObject_co) -> Any: ... + + @overload + def __lshift__(self: NDArray[bool_], other: _ArrayLikeBool_co) -> NDArray[int8]: ... # type: ignore[misc] + @overload + def __lshift__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co) -> NDArray[unsignedinteger[Any]]: ... # type: ignore[misc] + @overload + def __lshift__(self: _ArrayInt_co, other: _ArrayLikeInt_co) -> NDArray[signedinteger[Any]]: ... + @overload + def __lshift__(self: NDArray[object_], other: Any) -> Any: ... + @overload + def __lshift__(self: NDArray[Any], other: _ArrayLikeObject_co) -> Any: ... + + @overload + def __rlshift__(self: NDArray[bool_], other: _ArrayLikeBool_co) -> NDArray[int8]: ... # type: ignore[misc] + @overload + def __rlshift__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co) -> NDArray[unsignedinteger[Any]]: ... # type: ignore[misc] + @overload + def __rlshift__(self: _ArrayInt_co, other: _ArrayLikeInt_co) -> NDArray[signedinteger[Any]]: ... + @overload + def __rlshift__(self: NDArray[object_], other: Any) -> Any: ... + @overload + def __rlshift__(self: NDArray[Any], other: _ArrayLikeObject_co) -> Any: ... + + @overload + def __rshift__(self: NDArray[bool_], other: _ArrayLikeBool_co) -> NDArray[int8]: ... # type: ignore[misc] + @overload + def __rshift__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co) -> NDArray[unsignedinteger[Any]]: ... # type: ignore[misc] + @overload + def __rshift__(self: _ArrayInt_co, other: _ArrayLikeInt_co) -> NDArray[signedinteger[Any]]: ... + @overload + def __rshift__(self: NDArray[object_], other: Any) -> Any: ... + @overload + def __rshift__(self: NDArray[Any], other: _ArrayLikeObject_co) -> Any: ... + + @overload + def __rrshift__(self: NDArray[bool_], other: _ArrayLikeBool_co) -> NDArray[int8]: ... # type: ignore[misc] + @overload + def __rrshift__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co) -> NDArray[unsignedinteger[Any]]: ... # type: ignore[misc] + @overload + def __rrshift__(self: _ArrayInt_co, other: _ArrayLikeInt_co) -> NDArray[signedinteger[Any]]: ... + @overload + def __rrshift__(self: NDArray[object_], other: Any) -> Any: ... + @overload + def __rrshift__(self: NDArray[Any], other: _ArrayLikeObject_co) -> Any: ... + + @overload + def __and__(self: NDArray[bool_], other: _ArrayLikeBool_co) -> NDArray[bool_]: ... # type: ignore[misc] + @overload + def __and__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co) -> NDArray[unsignedinteger[Any]]: ... # type: ignore[misc] + @overload + def __and__(self: _ArrayInt_co, other: _ArrayLikeInt_co) -> NDArray[signedinteger[Any]]: ... + @overload + def __and__(self: NDArray[object_], other: Any) -> Any: ... + @overload + def __and__(self: NDArray[Any], other: _ArrayLikeObject_co) -> Any: ... + + @overload + def __rand__(self: NDArray[bool_], other: _ArrayLikeBool_co) -> NDArray[bool_]: ... # type: ignore[misc] + @overload + def __rand__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co) -> NDArray[unsignedinteger[Any]]: ... # type: ignore[misc] + @overload + def __rand__(self: _ArrayInt_co, other: _ArrayLikeInt_co) -> NDArray[signedinteger[Any]]: ... + @overload + def __rand__(self: NDArray[object_], other: Any) -> Any: ... + @overload + def __rand__(self: NDArray[Any], other: _ArrayLikeObject_co) -> Any: ... + + @overload + def __xor__(self: NDArray[bool_], other: _ArrayLikeBool_co) -> NDArray[bool_]: ... # type: ignore[misc] + @overload + def __xor__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co) -> NDArray[unsignedinteger[Any]]: ... # type: ignore[misc] + @overload + def __xor__(self: _ArrayInt_co, other: _ArrayLikeInt_co) -> NDArray[signedinteger[Any]]: ... + @overload + def __xor__(self: NDArray[object_], other: Any) -> Any: ... + @overload + def __xor__(self: NDArray[Any], other: _ArrayLikeObject_co) -> Any: ... + + @overload + def __rxor__(self: NDArray[bool_], other: _ArrayLikeBool_co) -> NDArray[bool_]: ... # type: ignore[misc] + @overload + def __rxor__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co) -> NDArray[unsignedinteger[Any]]: ... # type: ignore[misc] + @overload + def __rxor__(self: _ArrayInt_co, other: _ArrayLikeInt_co) -> NDArray[signedinteger[Any]]: ... + @overload + def __rxor__(self: NDArray[object_], other: Any) -> Any: ... + @overload + def __rxor__(self: NDArray[Any], other: _ArrayLikeObject_co) -> Any: ... + + @overload + def __or__(self: NDArray[bool_], other: _ArrayLikeBool_co) -> NDArray[bool_]: ... # type: ignore[misc] + @overload + def __or__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co) -> NDArray[unsignedinteger[Any]]: ... # type: ignore[misc] + @overload + def __or__(self: _ArrayInt_co, other: _ArrayLikeInt_co) -> NDArray[signedinteger[Any]]: ... + @overload + def __or__(self: NDArray[object_], other: Any) -> Any: ... + @overload + def __or__(self: NDArray[Any], other: _ArrayLikeObject_co) -> Any: ... + + @overload + def __ror__(self: NDArray[bool_], other: _ArrayLikeBool_co) -> NDArray[bool_]: ... # type: ignore[misc] + @overload + def __ror__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co) -> NDArray[unsignedinteger[Any]]: ... # type: ignore[misc] + @overload + def __ror__(self: _ArrayInt_co, other: _ArrayLikeInt_co) -> NDArray[signedinteger[Any]]: ... + @overload + def __ror__(self: NDArray[object_], other: Any) -> Any: ... + @overload + def __ror__(self: NDArray[Any], other: _ArrayLikeObject_co) -> Any: ... + + # `np.generic` does not support inplace operations + @overload + def __iadd__(self: NDArray[bool_], other: _ArrayLikeBool_co) -> NDArray[bool_]: ... + @overload + def __iadd__(self: NDArray[unsignedinteger[_NBit1]], other: _ArrayLikeUInt_co) -> NDArray[unsignedinteger[_NBit1]]: ... + @overload + def __iadd__(self: NDArray[signedinteger[_NBit1]], other: _ArrayLikeInt_co) -> NDArray[signedinteger[_NBit1]]: ... + @overload + def __iadd__(self: NDArray[floating[_NBit1]], other: _ArrayLikeFloat_co) -> NDArray[floating[_NBit1]]: ... + @overload + def __iadd__(self: NDArray[complexfloating[_NBit1, _NBit1]], other: _ArrayLikeComplex_co) -> NDArray[complexfloating[_NBit1, _NBit1]]: ... + @overload + def __iadd__(self: NDArray[timedelta64], other: _ArrayLikeTD64_co) -> NDArray[timedelta64]: ... + @overload + def __iadd__(self: NDArray[datetime64], other: _ArrayLikeTD64_co) -> NDArray[datetime64]: ... + @overload + def __iadd__(self: NDArray[object_], other: Any) -> NDArray[object_]: ... + + @overload + def __isub__(self: NDArray[unsignedinteger[_NBit1]], other: _ArrayLikeUInt_co) -> NDArray[unsignedinteger[_NBit1]]: ... + @overload + def __isub__(self: NDArray[signedinteger[_NBit1]], other: _ArrayLikeInt_co) -> NDArray[signedinteger[_NBit1]]: ... + @overload + def __isub__(self: NDArray[floating[_NBit1]], other: _ArrayLikeFloat_co) -> NDArray[floating[_NBit1]]: ... + @overload + def __isub__(self: NDArray[complexfloating[_NBit1, _NBit1]], other: _ArrayLikeComplex_co) -> NDArray[complexfloating[_NBit1, _NBit1]]: ... + @overload + def __isub__(self: NDArray[timedelta64], other: _ArrayLikeTD64_co) -> NDArray[timedelta64]: ... + @overload + def __isub__(self: NDArray[datetime64], other: _ArrayLikeTD64_co) -> NDArray[datetime64]: ... + @overload + def __isub__(self: NDArray[object_], other: Any) -> NDArray[object_]: ... + + @overload + def __imul__(self: NDArray[bool_], other: _ArrayLikeBool_co) -> NDArray[bool_]: ... + @overload + def __imul__(self: NDArray[unsignedinteger[_NBit1]], other: _ArrayLikeUInt_co) -> NDArray[unsignedinteger[_NBit1]]: ... + @overload + def __imul__(self: NDArray[signedinteger[_NBit1]], other: _ArrayLikeInt_co) -> NDArray[signedinteger[_NBit1]]: ... + @overload + def __imul__(self: NDArray[floating[_NBit1]], other: _ArrayLikeFloat_co) -> NDArray[floating[_NBit1]]: ... + @overload + def __imul__(self: NDArray[complexfloating[_NBit1, _NBit1]], other: _ArrayLikeComplex_co) -> NDArray[complexfloating[_NBit1, _NBit1]]: ... + @overload + def __imul__(self: NDArray[timedelta64], other: _ArrayLikeFloat_co) -> NDArray[timedelta64]: ... + @overload + def __imul__(self: NDArray[object_], other: Any) -> NDArray[object_]: ... + + @overload + def __itruediv__(self: NDArray[floating[_NBit1]], other: _ArrayLikeFloat_co) -> NDArray[floating[_NBit1]]: ... + @overload + def __itruediv__(self: NDArray[complexfloating[_NBit1, _NBit1]], other: _ArrayLikeComplex_co) -> NDArray[complexfloating[_NBit1, _NBit1]]: ... + @overload + def __itruediv__(self: NDArray[timedelta64], other: _ArrayLikeBool_co) -> NoReturn: ... + @overload + def __itruediv__(self: NDArray[timedelta64], other: _ArrayLikeInt_co) -> NDArray[timedelta64]: ... + @overload + def __itruediv__(self: NDArray[object_], other: Any) -> NDArray[object_]: ... + + @overload + def __ifloordiv__(self: NDArray[unsignedinteger[_NBit1]], other: _ArrayLikeUInt_co) -> NDArray[unsignedinteger[_NBit1]]: ... + @overload + def __ifloordiv__(self: NDArray[signedinteger[_NBit1]], other: _ArrayLikeInt_co) -> NDArray[signedinteger[_NBit1]]: ... + @overload + def __ifloordiv__(self: NDArray[floating[_NBit1]], other: _ArrayLikeFloat_co) -> NDArray[floating[_NBit1]]: ... + @overload + def __ifloordiv__(self: NDArray[complexfloating[_NBit1, _NBit1]], other: _ArrayLikeComplex_co) -> NDArray[complexfloating[_NBit1, _NBit1]]: ... + @overload + def __ifloordiv__(self: NDArray[timedelta64], other: _ArrayLikeBool_co) -> NoReturn: ... + @overload + def __ifloordiv__(self: NDArray[timedelta64], other: _ArrayLikeInt_co) -> NDArray[timedelta64]: ... + @overload + def __ifloordiv__(self: NDArray[object_], other: Any) -> NDArray[object_]: ... + + @overload + def __ipow__(self: NDArray[unsignedinteger[_NBit1]], other: _ArrayLikeUInt_co) -> NDArray[unsignedinteger[_NBit1]]: ... + @overload + def __ipow__(self: NDArray[signedinteger[_NBit1]], other: _ArrayLikeInt_co) -> NDArray[signedinteger[_NBit1]]: ... + @overload + def __ipow__(self: NDArray[floating[_NBit1]], other: _ArrayLikeFloat_co) -> NDArray[floating[_NBit1]]: ... + @overload + def __ipow__(self: NDArray[complexfloating[_NBit1, _NBit1]], other: _ArrayLikeComplex_co) -> NDArray[complexfloating[_NBit1, _NBit1]]: ... + @overload + def __ipow__(self: NDArray[object_], other: Any) -> NDArray[object_]: ... + + @overload + def __imod__(self: NDArray[unsignedinteger[_NBit1]], other: _ArrayLikeUInt_co) -> NDArray[unsignedinteger[_NBit1]]: ... + @overload + def __imod__(self: NDArray[signedinteger[_NBit1]], other: _ArrayLikeInt_co) -> NDArray[signedinteger[_NBit1]]: ... + @overload + def __imod__(self: NDArray[floating[_NBit1]], other: _ArrayLikeFloat_co) -> NDArray[floating[_NBit1]]: ... + @overload + def __imod__(self: NDArray[timedelta64], other: _SupportsArray[_dtype[timedelta64]] | _NestedSequence[_SupportsArray[_dtype[timedelta64]]]) -> NDArray[timedelta64]: ... + @overload + def __imod__(self: NDArray[object_], other: Any) -> NDArray[object_]: ... + + @overload + def __ilshift__(self: NDArray[unsignedinteger[_NBit1]], other: _ArrayLikeUInt_co) -> NDArray[unsignedinteger[_NBit1]]: ... + @overload + def __ilshift__(self: NDArray[signedinteger[_NBit1]], other: _ArrayLikeInt_co) -> NDArray[signedinteger[_NBit1]]: ... + @overload + def __ilshift__(self: NDArray[object_], other: Any) -> NDArray[object_]: ... + + @overload + def __irshift__(self: NDArray[unsignedinteger[_NBit1]], other: _ArrayLikeUInt_co) -> NDArray[unsignedinteger[_NBit1]]: ... + @overload + def __irshift__(self: NDArray[signedinteger[_NBit1]], other: _ArrayLikeInt_co) -> NDArray[signedinteger[_NBit1]]: ... + @overload + def __irshift__(self: NDArray[object_], other: Any) -> NDArray[object_]: ... + + @overload + def __iand__(self: NDArray[bool_], other: _ArrayLikeBool_co) -> NDArray[bool_]: ... + @overload + def __iand__(self: NDArray[unsignedinteger[_NBit1]], other: _ArrayLikeUInt_co) -> NDArray[unsignedinteger[_NBit1]]: ... + @overload + def __iand__(self: NDArray[signedinteger[_NBit1]], other: _ArrayLikeInt_co) -> NDArray[signedinteger[_NBit1]]: ... + @overload + def __iand__(self: NDArray[object_], other: Any) -> NDArray[object_]: ... + + @overload + def __ixor__(self: NDArray[bool_], other: _ArrayLikeBool_co) -> NDArray[bool_]: ... + @overload + def __ixor__(self: NDArray[unsignedinteger[_NBit1]], other: _ArrayLikeUInt_co) -> NDArray[unsignedinteger[_NBit1]]: ... + @overload + def __ixor__(self: NDArray[signedinteger[_NBit1]], other: _ArrayLikeInt_co) -> NDArray[signedinteger[_NBit1]]: ... + @overload + def __ixor__(self: NDArray[object_], other: Any) -> NDArray[object_]: ... + + @overload + def __ior__(self: NDArray[bool_], other: _ArrayLikeBool_co) -> NDArray[bool_]: ... + @overload + def __ior__(self: NDArray[unsignedinteger[_NBit1]], other: _ArrayLikeUInt_co) -> NDArray[unsignedinteger[_NBit1]]: ... + @overload + def __ior__(self: NDArray[signedinteger[_NBit1]], other: _ArrayLikeInt_co) -> NDArray[signedinteger[_NBit1]]: ... + @overload + def __ior__(self: NDArray[object_], other: Any) -> NDArray[object_]: ... + + def __dlpack__(self: NDArray[number[Any]], *, stream: None = ...) -> _PyCapsule: ... + def __dlpack_device__(self) -> Tuple[int, L[0]]: ... + + # Keep `dtype` at the bottom to avoid name conflicts with `np.dtype` + @property + def dtype(self) -> _DType_co: ... + +# NOTE: while `np.generic` is not technically an instance of `ABCMeta`, +# the `@abstractmethod` decorator is herein used to (forcefully) deny +# the creation of `np.generic` instances. +# The `# type: ignore` comments are necessary to silence mypy errors regarding +# the missing `ABCMeta` metaclass. + +# See https://github.com/numpy/numpy-stubs/pull/80 for more details. + +_ScalarType = TypeVar("_ScalarType", bound=generic) +_NBit1 = TypeVar("_NBit1", bound=NBitBase) +_NBit2 = TypeVar("_NBit2", bound=NBitBase) + +class generic(_ArrayOrScalarCommon): + @abstractmethod + def __init__(self, *args: Any, **kwargs: Any) -> None: ... + @overload + def __array__(self: _ScalarType, dtype: None = ..., /) -> ndarray[Any, _dtype[_ScalarType]]: ... + @overload + def __array__(self, dtype: _DType, /) -> ndarray[Any, _DType]: ... + @property + def base(self) -> None: ... + @property + def ndim(self) -> L[0]: ... + @property + def size(self) -> L[1]: ... + @property + def shape(self) -> Tuple[()]: ... + @property + def strides(self) -> Tuple[()]: ... + def byteswap(self: _ScalarType, inplace: L[False] = ...) -> _ScalarType: ... + @property + def flat(self: _ScalarType) -> flatiter[ndarray[Any, _dtype[_ScalarType]]]: ... + + @overload + def astype( + self, + dtype: _DTypeLike[_ScalarType], + order: _OrderKACF = ..., + casting: _CastingKind = ..., + subok: bool = ..., + copy: bool | _CopyMode = ..., + ) -> _ScalarType: ... + @overload + def astype( + self, + dtype: DTypeLike, + order: _OrderKACF = ..., + casting: _CastingKind = ..., + subok: bool = ..., + copy: bool | _CopyMode = ..., + ) -> Any: ... + + # NOTE: `view` will perform a 0D->scalar cast, + # thus the array `type` is irrelevant to the output type + @overload + def view( + self: _ScalarType, + type: Type[ndarray[Any, Any]] = ..., + ) -> _ScalarType: ... + @overload + def view( + self, + dtype: _DTypeLike[_ScalarType], + type: Type[ndarray[Any, Any]] = ..., + ) -> _ScalarType: ... + @overload + def view( + self, + dtype: DTypeLike, + type: Type[ndarray[Any, Any]] = ..., + ) -> Any: ... + + @overload + def getfield( + self, + dtype: _DTypeLike[_ScalarType], + offset: SupportsIndex = ... + ) -> _ScalarType: ... + @overload + def getfield( + self, + dtype: DTypeLike, + offset: SupportsIndex = ... + ) -> Any: ... + + def item( + self, args: L[0] | Tuple[()] | Tuple[L[0]] = ..., /, + ) -> Any: ... + + @overload + def take( # type: ignore[misc] + self: _ScalarType, + indices: _IntLike_co, + axis: Optional[SupportsIndex] = ..., + out: None = ..., + mode: _ModeKind = ..., + ) -> _ScalarType: ... + @overload + def take( # type: ignore[misc] + self: _ScalarType, + indices: _ArrayLikeInt_co, + axis: Optional[SupportsIndex] = ..., + out: None = ..., + mode: _ModeKind = ..., + ) -> ndarray[Any, _dtype[_ScalarType]]: ... + @overload + def take( + self, + indices: _ArrayLikeInt_co, + axis: Optional[SupportsIndex] = ..., + out: _NdArraySubClass = ..., + mode: _ModeKind = ..., + ) -> _NdArraySubClass: ... + + def repeat( + self: _ScalarType, + repeats: _ArrayLikeInt_co, + axis: Optional[SupportsIndex] = ..., + ) -> ndarray[Any, _dtype[_ScalarType]]: ... + + def flatten( + self: _ScalarType, + order: _OrderKACF = ..., + ) -> ndarray[Any, _dtype[_ScalarType]]: ... + + def ravel( + self: _ScalarType, + order: _OrderKACF = ..., + ) -> ndarray[Any, _dtype[_ScalarType]]: ... + + @overload + def reshape( + self: _ScalarType, shape: _ShapeLike, /, *, order: _OrderACF = ... + ) -> ndarray[Any, _dtype[_ScalarType]]: ... + @overload + def reshape( + self: _ScalarType, *shape: SupportsIndex, order: _OrderACF = ... + ) -> ndarray[Any, _dtype[_ScalarType]]: ... + + def squeeze( + self: _ScalarType, axis: Union[L[0], Tuple[()]] = ... + ) -> _ScalarType: ... + def transpose(self: _ScalarType, axes: Tuple[()] = ..., /) -> _ScalarType: ... + # Keep `dtype` at the bottom to avoid name conflicts with `np.dtype` + @property + def dtype(self: _ScalarType) -> _dtype[_ScalarType]: ... + +class number(generic, Generic[_NBit1]): # type: ignore + @property + def real(self: _ArraySelf) -> _ArraySelf: ... + @property + def imag(self: _ArraySelf) -> _ArraySelf: ... + if sys.version_info >= (3, 9): + def __class_getitem__(self, item: Any) -> GenericAlias: ... + def __int__(self) -> int: ... + def __float__(self) -> float: ... + def __complex__(self) -> complex: ... + def __neg__(self: _ArraySelf) -> _ArraySelf: ... + def __pos__(self: _ArraySelf) -> _ArraySelf: ... + def __abs__(self: _ArraySelf) -> _ArraySelf: ... + # Ensure that objects annotated as `number` support arithmetic operations + __add__: _NumberOp + __radd__: _NumberOp + __sub__: _NumberOp + __rsub__: _NumberOp + __mul__: _NumberOp + __rmul__: _NumberOp + __floordiv__: _NumberOp + __rfloordiv__: _NumberOp + __pow__: _NumberOp + __rpow__: _NumberOp + __truediv__: _NumberOp + __rtruediv__: _NumberOp + __lt__: _ComparisonOp[_NumberLike_co, _ArrayLikeNumber_co] + __le__: _ComparisonOp[_NumberLike_co, _ArrayLikeNumber_co] + __gt__: _ComparisonOp[_NumberLike_co, _ArrayLikeNumber_co] + __ge__: _ComparisonOp[_NumberLike_co, _ArrayLikeNumber_co] + +class bool_(generic): + def __init__(self, value: object = ..., /) -> None: ... + def item( + self, args: L[0] | Tuple[()] | Tuple[L[0]] = ..., /, + ) -> bool: ... + def tolist(self) -> bool: ... + @property + def real(self: _ArraySelf) -> _ArraySelf: ... + @property + def imag(self: _ArraySelf) -> _ArraySelf: ... + def __int__(self) -> int: ... + def __float__(self) -> float: ... + def __complex__(self) -> complex: ... + def __abs__(self: _ArraySelf) -> _ArraySelf: ... + __add__: _BoolOp[bool_] + __radd__: _BoolOp[bool_] + __sub__: _BoolSub + __rsub__: _BoolSub + __mul__: _BoolOp[bool_] + __rmul__: _BoolOp[bool_] + __floordiv__: _BoolOp[int8] + __rfloordiv__: _BoolOp[int8] + __pow__: _BoolOp[int8] + __rpow__: _BoolOp[int8] + __truediv__: _BoolTrueDiv + __rtruediv__: _BoolTrueDiv + def __invert__(self) -> bool_: ... + __lshift__: _BoolBitOp[int8] + __rlshift__: _BoolBitOp[int8] + __rshift__: _BoolBitOp[int8] + __rrshift__: _BoolBitOp[int8] + __and__: _BoolBitOp[bool_] + __rand__: _BoolBitOp[bool_] + __xor__: _BoolBitOp[bool_] + __rxor__: _BoolBitOp[bool_] + __or__: _BoolBitOp[bool_] + __ror__: _BoolBitOp[bool_] + __mod__: _BoolMod + __rmod__: _BoolMod + __divmod__: _BoolDivMod + __rdivmod__: _BoolDivMod + __lt__: _ComparisonOp[_NumberLike_co, _ArrayLikeNumber_co] + __le__: _ComparisonOp[_NumberLike_co, _ArrayLikeNumber_co] + __gt__: _ComparisonOp[_NumberLike_co, _ArrayLikeNumber_co] + __ge__: _ComparisonOp[_NumberLike_co, _ArrayLikeNumber_co] + +bool8 = bool_ + +class object_(generic): + def __init__(self, value: object = ..., /) -> None: ... + @property + def real(self: _ArraySelf) -> _ArraySelf: ... + @property + def imag(self: _ArraySelf) -> _ArraySelf: ... + # The 3 protocols below may or may not raise, + # depending on the underlying object + def __int__(self) -> int: ... + def __float__(self) -> float: ... + def __complex__(self) -> complex: ... + +object0 = object_ + +# The `datetime64` constructors requires an object with the three attributes below, +# and thus supports datetime duck typing +class _DatetimeScalar(Protocol): + @property + def day(self) -> int: ... + @property + def month(self) -> int: ... + @property + def year(self) -> int: ... + +# TODO: `item`/`tolist` returns either `dt.date`, `dt.datetime` or `int` +# depending on the unit +class datetime64(generic): + @overload + def __init__( + self, + value: None | datetime64 | _CharLike_co | _DatetimeScalar = ..., + format: _CharLike_co | Tuple[_CharLike_co, _IntLike_co] = ..., + /, + ) -> None: ... + @overload + def __init__( + self, + value: int, + format: _CharLike_co | Tuple[_CharLike_co, _IntLike_co], + /, + ) -> None: ... + def __add__(self, other: _TD64Like_co) -> datetime64: ... + def __radd__(self, other: _TD64Like_co) -> datetime64: ... + @overload + def __sub__(self, other: datetime64) -> timedelta64: ... + @overload + def __sub__(self, other: _TD64Like_co) -> datetime64: ... + def __rsub__(self, other: datetime64) -> timedelta64: ... + __lt__: _ComparisonOp[datetime64, _ArrayLikeDT64_co] + __le__: _ComparisonOp[datetime64, _ArrayLikeDT64_co] + __gt__: _ComparisonOp[datetime64, _ArrayLikeDT64_co] + __ge__: _ComparisonOp[datetime64, _ArrayLikeDT64_co] + +_IntValue = Union[SupportsInt, _CharLike_co, SupportsIndex] +_FloatValue = Union[None, _CharLike_co, SupportsFloat, SupportsIndex] +_ComplexValue = Union[ + None, + _CharLike_co, + SupportsFloat, + SupportsComplex, + SupportsIndex, + complex, # `complex` is not a subtype of `SupportsComplex` +] + +class integer(number[_NBit1]): # type: ignore + @property + def numerator(self: _ScalarType) -> _ScalarType: ... + @property + def denominator(self) -> L[1]: ... + @overload + def __round__(self, ndigits: None = ...) -> int: ... + @overload + def __round__(self: _ScalarType, ndigits: SupportsIndex) -> _ScalarType: ... + + # NOTE: `__index__` is technically defined in the bottom-most + # sub-classes (`int64`, `uint32`, etc) + def item( + self, args: L[0] | Tuple[()] | Tuple[L[0]] = ..., /, + ) -> int: ... + def tolist(self) -> int: ... + def is_integer(self) -> L[True]: ... + def bit_count(self: _ScalarType) -> int: ... + def __index__(self) -> int: ... + __truediv__: _IntTrueDiv[_NBit1] + __rtruediv__: _IntTrueDiv[_NBit1] + def __mod__(self, value: _IntLike_co) -> integer: ... + def __rmod__(self, value: _IntLike_co) -> integer: ... + def __invert__(self: _IntType) -> _IntType: ... + # Ensure that objects annotated as `integer` support bit-wise operations + def __lshift__(self, other: _IntLike_co) -> integer: ... + def __rlshift__(self, other: _IntLike_co) -> integer: ... + def __rshift__(self, other: _IntLike_co) -> integer: ... + def __rrshift__(self, other: _IntLike_co) -> integer: ... + def __and__(self, other: _IntLike_co) -> integer: ... + def __rand__(self, other: _IntLike_co) -> integer: ... + def __or__(self, other: _IntLike_co) -> integer: ... + def __ror__(self, other: _IntLike_co) -> integer: ... + def __xor__(self, other: _IntLike_co) -> integer: ... + def __rxor__(self, other: _IntLike_co) -> integer: ... + +class signedinteger(integer[_NBit1]): + def __init__(self, value: _IntValue = ..., /) -> None: ... + __add__: _SignedIntOp[_NBit1] + __radd__: _SignedIntOp[_NBit1] + __sub__: _SignedIntOp[_NBit1] + __rsub__: _SignedIntOp[_NBit1] + __mul__: _SignedIntOp[_NBit1] + __rmul__: _SignedIntOp[_NBit1] + __floordiv__: _SignedIntOp[_NBit1] + __rfloordiv__: _SignedIntOp[_NBit1] + __pow__: _SignedIntOp[_NBit1] + __rpow__: _SignedIntOp[_NBit1] + __lshift__: _SignedIntBitOp[_NBit1] + __rlshift__: _SignedIntBitOp[_NBit1] + __rshift__: _SignedIntBitOp[_NBit1] + __rrshift__: _SignedIntBitOp[_NBit1] + __and__: _SignedIntBitOp[_NBit1] + __rand__: _SignedIntBitOp[_NBit1] + __xor__: _SignedIntBitOp[_NBit1] + __rxor__: _SignedIntBitOp[_NBit1] + __or__: _SignedIntBitOp[_NBit1] + __ror__: _SignedIntBitOp[_NBit1] + __mod__: _SignedIntMod[_NBit1] + __rmod__: _SignedIntMod[_NBit1] + __divmod__: _SignedIntDivMod[_NBit1] + __rdivmod__: _SignedIntDivMod[_NBit1] + +int8 = signedinteger[_8Bit] +int16 = signedinteger[_16Bit] +int32 = signedinteger[_32Bit] +int64 = signedinteger[_64Bit] + +byte = signedinteger[_NBitByte] +short = signedinteger[_NBitShort] +intc = signedinteger[_NBitIntC] +intp = signedinteger[_NBitIntP] +int0 = signedinteger[_NBitIntP] +int_ = signedinteger[_NBitInt] +longlong = signedinteger[_NBitLongLong] + +# TODO: `item`/`tolist` returns either `dt.timedelta` or `int` +# depending on the unit +class timedelta64(generic): + def __init__( + self, + value: None | int | _CharLike_co | dt.timedelta | timedelta64 = ..., + format: _CharLike_co | Tuple[_CharLike_co, _IntLike_co] = ..., + /, + ) -> None: ... + @property + def numerator(self: _ScalarType) -> _ScalarType: ... + @property + def denominator(self) -> L[1]: ... + + # NOTE: Only a limited number of units support conversion + # to builtin scalar types: `Y`, `M`, `ns`, `ps`, `fs`, `as` + def __int__(self) -> int: ... + def __float__(self) -> float: ... + def __complex__(self) -> complex: ... + def __neg__(self: _ArraySelf) -> _ArraySelf: ... + def __pos__(self: _ArraySelf) -> _ArraySelf: ... + def __abs__(self: _ArraySelf) -> _ArraySelf: ... + def __add__(self, other: _TD64Like_co) -> timedelta64: ... + def __radd__(self, other: _TD64Like_co) -> timedelta64: ... + def __sub__(self, other: _TD64Like_co) -> timedelta64: ... + def __rsub__(self, other: _TD64Like_co) -> timedelta64: ... + def __mul__(self, other: _FloatLike_co) -> timedelta64: ... + def __rmul__(self, other: _FloatLike_co) -> timedelta64: ... + __truediv__: _TD64Div[float64] + __floordiv__: _TD64Div[int64] + def __rtruediv__(self, other: timedelta64) -> float64: ... + def __rfloordiv__(self, other: timedelta64) -> int64: ... + def __mod__(self, other: timedelta64) -> timedelta64: ... + def __rmod__(self, other: timedelta64) -> timedelta64: ... + def __divmod__(self, other: timedelta64) -> Tuple[int64, timedelta64]: ... + def __rdivmod__(self, other: timedelta64) -> Tuple[int64, timedelta64]: ... + __lt__: _ComparisonOp[_TD64Like_co, _ArrayLikeTD64_co] + __le__: _ComparisonOp[_TD64Like_co, _ArrayLikeTD64_co] + __gt__: _ComparisonOp[_TD64Like_co, _ArrayLikeTD64_co] + __ge__: _ComparisonOp[_TD64Like_co, _ArrayLikeTD64_co] + +class unsignedinteger(integer[_NBit1]): + # NOTE: `uint64 + signedinteger -> float64` + def __init__(self, value: _IntValue = ..., /) -> None: ... + __add__: _UnsignedIntOp[_NBit1] + __radd__: _UnsignedIntOp[_NBit1] + __sub__: _UnsignedIntOp[_NBit1] + __rsub__: _UnsignedIntOp[_NBit1] + __mul__: _UnsignedIntOp[_NBit1] + __rmul__: _UnsignedIntOp[_NBit1] + __floordiv__: _UnsignedIntOp[_NBit1] + __rfloordiv__: _UnsignedIntOp[_NBit1] + __pow__: _UnsignedIntOp[_NBit1] + __rpow__: _UnsignedIntOp[_NBit1] + __lshift__: _UnsignedIntBitOp[_NBit1] + __rlshift__: _UnsignedIntBitOp[_NBit1] + __rshift__: _UnsignedIntBitOp[_NBit1] + __rrshift__: _UnsignedIntBitOp[_NBit1] + __and__: _UnsignedIntBitOp[_NBit1] + __rand__: _UnsignedIntBitOp[_NBit1] + __xor__: _UnsignedIntBitOp[_NBit1] + __rxor__: _UnsignedIntBitOp[_NBit1] + __or__: _UnsignedIntBitOp[_NBit1] + __ror__: _UnsignedIntBitOp[_NBit1] + __mod__: _UnsignedIntMod[_NBit1] + __rmod__: _UnsignedIntMod[_NBit1] + __divmod__: _UnsignedIntDivMod[_NBit1] + __rdivmod__: _UnsignedIntDivMod[_NBit1] + +uint8 = unsignedinteger[_8Bit] +uint16 = unsignedinteger[_16Bit] +uint32 = unsignedinteger[_32Bit] +uint64 = unsignedinteger[_64Bit] + +ubyte = unsignedinteger[_NBitByte] +ushort = unsignedinteger[_NBitShort] +uintc = unsignedinteger[_NBitIntC] +uintp = unsignedinteger[_NBitIntP] +uint0 = unsignedinteger[_NBitIntP] +uint = unsignedinteger[_NBitInt] +ulonglong = unsignedinteger[_NBitLongLong] + +class inexact(number[_NBit1]): # type: ignore + def __getnewargs__(self: inexact[_64Bit]) -> Tuple[float, ...]: ... + +_IntType = TypeVar("_IntType", bound=integer) +_FloatType = TypeVar('_FloatType', bound=floating) + +class floating(inexact[_NBit1]): + def __init__(self, value: _FloatValue = ..., /) -> None: ... + def item( + self, args: L[0] | Tuple[()] | Tuple[L[0]] = ..., + /, + ) -> float: ... + def tolist(self) -> float: ... + def is_integer(self) -> bool: ... + def hex(self: float64) -> str: ... + @classmethod + def fromhex(cls: Type[float64], string: str, /) -> float64: ... + def as_integer_ratio(self) -> Tuple[int, int]: ... + if sys.version_info >= (3, 9): + def __ceil__(self: float64) -> int: ... + def __floor__(self: float64) -> int: ... + def __trunc__(self: float64) -> int: ... + def __getnewargs__(self: float64) -> Tuple[float]: ... + def __getformat__(self: float64, typestr: L["double", "float"], /) -> str: ... + @overload + def __round__(self, ndigits: None = ...) -> int: ... + @overload + def __round__(self: _ScalarType, ndigits: SupportsIndex) -> _ScalarType: ... + __add__: _FloatOp[_NBit1] + __radd__: _FloatOp[_NBit1] + __sub__: _FloatOp[_NBit1] + __rsub__: _FloatOp[_NBit1] + __mul__: _FloatOp[_NBit1] + __rmul__: _FloatOp[_NBit1] + __truediv__: _FloatOp[_NBit1] + __rtruediv__: _FloatOp[_NBit1] + __floordiv__: _FloatOp[_NBit1] + __rfloordiv__: _FloatOp[_NBit1] + __pow__: _FloatOp[_NBit1] + __rpow__: _FloatOp[_NBit1] + __mod__: _FloatMod[_NBit1] + __rmod__: _FloatMod[_NBit1] + __divmod__: _FloatDivMod[_NBit1] + __rdivmod__: _FloatDivMod[_NBit1] + +float16 = floating[_16Bit] +float32 = floating[_32Bit] +float64 = floating[_64Bit] + +half = floating[_NBitHalf] +single = floating[_NBitSingle] +double = floating[_NBitDouble] +float_ = floating[_NBitDouble] +longdouble = floating[_NBitLongDouble] +longfloat = floating[_NBitLongDouble] + +# The main reason for `complexfloating` having two typevars is cosmetic. +# It is used to clarify why `complex128`s precision is `_64Bit`, the latter +# describing the two 64 bit floats representing its real and imaginary component + +class complexfloating(inexact[_NBit1], Generic[_NBit1, _NBit2]): + def __init__(self, value: _ComplexValue = ..., /) -> None: ... + def item( + self, args: L[0] | Tuple[()] | Tuple[L[0]] = ..., /, + ) -> complex: ... + def tolist(self) -> complex: ... + @property + def real(self) -> floating[_NBit1]: ... # type: ignore[override] + @property + def imag(self) -> floating[_NBit2]: ... # type: ignore[override] + def __abs__(self) -> floating[_NBit1]: ... # type: ignore[override] + def __getnewargs__(self: complex128) -> Tuple[float, float]: ... + # NOTE: Deprecated + # def __round__(self, ndigits=...): ... + __add__: _ComplexOp[_NBit1] + __radd__: _ComplexOp[_NBit1] + __sub__: _ComplexOp[_NBit1] + __rsub__: _ComplexOp[_NBit1] + __mul__: _ComplexOp[_NBit1] + __rmul__: _ComplexOp[_NBit1] + __truediv__: _ComplexOp[_NBit1] + __rtruediv__: _ComplexOp[_NBit1] + __pow__: _ComplexOp[_NBit1] + __rpow__: _ComplexOp[_NBit1] + +complex64 = complexfloating[_32Bit, _32Bit] +complex128 = complexfloating[_64Bit, _64Bit] + +csingle = complexfloating[_NBitSingle, _NBitSingle] +singlecomplex = complexfloating[_NBitSingle, _NBitSingle] +cdouble = complexfloating[_NBitDouble, _NBitDouble] +complex_ = complexfloating[_NBitDouble, _NBitDouble] +cfloat = complexfloating[_NBitDouble, _NBitDouble] +clongdouble = complexfloating[_NBitLongDouble, _NBitLongDouble] +clongfloat = complexfloating[_NBitLongDouble, _NBitLongDouble] +longcomplex = complexfloating[_NBitLongDouble, _NBitLongDouble] + +class flexible(generic): ... # type: ignore + +# TODO: `item`/`tolist` returns either `bytes` or `tuple` +# depending on whether or not it's used as an opaque bytes sequence +# or a structure +class void(flexible): + def __init__(self, value: _IntLike_co | bytes, /) -> None: ... + @property + def real(self: _ArraySelf) -> _ArraySelf: ... + @property + def imag(self: _ArraySelf) -> _ArraySelf: ... + def setfield( + self, val: ArrayLike, dtype: DTypeLike, offset: int = ... + ) -> None: ... + @overload + def __getitem__(self, key: str | SupportsIndex) -> Any: ... + @overload + def __getitem__(self, key: list[str]) -> void: ... + def __setitem__( + self, + key: str | List[str] | SupportsIndex, + value: ArrayLike, + ) -> None: ... + +void0 = void + +class character(flexible): # type: ignore + def __int__(self) -> int: ... + def __float__(self) -> float: ... + +# NOTE: Most `np.bytes_` / `np.str_` methods return their +# builtin `bytes` / `str` counterpart + +class bytes_(character, bytes): + @overload + def __init__(self, value: object = ..., /) -> None: ... + @overload + def __init__( + self, value: str, /, encoding: str = ..., errors: str = ... + ) -> None: ... + def item( + self, args: L[0] | Tuple[()] | Tuple[L[0]] = ..., /, + ) -> bytes: ... + def tolist(self) -> bytes: ... + +string_ = bytes_ +bytes0 = bytes_ + +class str_(character, str): + @overload + def __init__(self, value: object = ..., /) -> None: ... + @overload + def __init__( + self, value: bytes, /, encoding: str = ..., errors: str = ... + ) -> None: ... + def item( + self, args: L[0] | Tuple[()] | Tuple[L[0]] = ..., /, + ) -> str: ... + def tolist(self) -> str: ... + +unicode_ = str_ +str0 = str_ + +# +# Constants +# + +Inf: Final[float] +Infinity: Final[float] +NAN: Final[float] +NINF: Final[float] +NZERO: Final[float] +NaN: Final[float] +PINF: Final[float] +PZERO: Final[float] +e: Final[float] +euler_gamma: Final[float] +inf: Final[float] +infty: Final[float] +nan: Final[float] +pi: Final[float] + +CLIP: L[0] +WRAP: L[1] +RAISE: L[2] + +ERR_IGNORE: L[0] +ERR_WARN: L[1] +ERR_RAISE: L[2] +ERR_CALL: L[3] +ERR_PRINT: L[4] +ERR_LOG: L[5] +ERR_DEFAULT: L[521] + +SHIFT_DIVIDEBYZERO: L[0] +SHIFT_OVERFLOW: L[3] +SHIFT_UNDERFLOW: L[6] +SHIFT_INVALID: L[9] + +FPE_DIVIDEBYZERO: L[1] +FPE_OVERFLOW: L[2] +FPE_UNDERFLOW: L[4] +FPE_INVALID: L[8] + +FLOATING_POINT_SUPPORT: L[1] +UFUNC_BUFSIZE_DEFAULT = BUFSIZE + +little_endian: Final[bool] +True_: Final[bool_] +False_: Final[bool_] + +UFUNC_PYVALS_NAME: L["UFUNC_PYVALS"] + +newaxis: None + +# See `npt._ufunc` for more concrete nin-/nout-specific stubs +class ufunc: + @property + def __name__(self) -> str: ... + @property + def __doc__(self) -> str: ... + __call__: Callable[..., Any] + @property + def nin(self) -> int: ... + @property + def nout(self) -> int: ... + @property + def nargs(self) -> int: ... + @property + def ntypes(self) -> int: ... + @property + def types(self) -> List[str]: ... + # Broad return type because it has to encompass things like + # + # >>> np.logical_and.identity is True + # True + # >>> np.add.identity is 0 + # True + # >>> np.sin.identity is None + # True + # + # and any user-defined ufuncs. + @property + def identity(self) -> Any: ... + # This is None for ufuncs and a string for gufuncs. + @property + def signature(self) -> Optional[str]: ... + # The next four methods will always exist, but they will just + # raise a ValueError ufuncs with that don't accept two input + # arguments and return one output argument. Because of that we + # can't type them very precisely. + reduce: Any + accumulate: Any + reduce: Any + outer: Any + # Similarly at won't be defined for ufuncs that return multiple + # outputs, so we can't type it very precisely. + at: Any + +# Parameters: `__name__`, `ntypes` and `identity` +absolute: _UFunc_Nin1_Nout1[L['absolute'], L[20], None] +add: _UFunc_Nin2_Nout1[L['add'], L[22], L[0]] +arccos: _UFunc_Nin1_Nout1[L['arccos'], L[8], None] +arccosh: _UFunc_Nin1_Nout1[L['arccosh'], L[8], None] +arcsin: _UFunc_Nin1_Nout1[L['arcsin'], L[8], None] +arcsinh: _UFunc_Nin1_Nout1[L['arcsinh'], L[8], None] +arctan2: _UFunc_Nin2_Nout1[L['arctan2'], L[5], None] +arctan: _UFunc_Nin1_Nout1[L['arctan'], L[8], None] +arctanh: _UFunc_Nin1_Nout1[L['arctanh'], L[8], None] +bitwise_and: _UFunc_Nin2_Nout1[L['bitwise_and'], L[12], L[-1]] +bitwise_not: _UFunc_Nin1_Nout1[L['invert'], L[12], None] +bitwise_or: _UFunc_Nin2_Nout1[L['bitwise_or'], L[12], L[0]] +bitwise_xor: _UFunc_Nin2_Nout1[L['bitwise_xor'], L[12], L[0]] +cbrt: _UFunc_Nin1_Nout1[L['cbrt'], L[5], None] +ceil: _UFunc_Nin1_Nout1[L['ceil'], L[7], None] +conj: _UFunc_Nin1_Nout1[L['conjugate'], L[18], None] +conjugate: _UFunc_Nin1_Nout1[L['conjugate'], L[18], None] +copysign: _UFunc_Nin2_Nout1[L['copysign'], L[4], None] +cos: _UFunc_Nin1_Nout1[L['cos'], L[9], None] +cosh: _UFunc_Nin1_Nout1[L['cosh'], L[8], None] +deg2rad: _UFunc_Nin1_Nout1[L['deg2rad'], L[5], None] +degrees: _UFunc_Nin1_Nout1[L['degrees'], L[5], None] +divide: _UFunc_Nin2_Nout1[L['true_divide'], L[11], None] +divmod: _UFunc_Nin2_Nout2[L['divmod'], L[15], None] +equal: _UFunc_Nin2_Nout1[L['equal'], L[23], None] +exp2: _UFunc_Nin1_Nout1[L['exp2'], L[8], None] +exp: _UFunc_Nin1_Nout1[L['exp'], L[10], None] +expm1: _UFunc_Nin1_Nout1[L['expm1'], L[8], None] +fabs: _UFunc_Nin1_Nout1[L['fabs'], L[5], None] +float_power: _UFunc_Nin2_Nout1[L['float_power'], L[4], None] +floor: _UFunc_Nin1_Nout1[L['floor'], L[7], None] +floor_divide: _UFunc_Nin2_Nout1[L['floor_divide'], L[21], None] +fmax: _UFunc_Nin2_Nout1[L['fmax'], L[21], None] +fmin: _UFunc_Nin2_Nout1[L['fmin'], L[21], None] +fmod: _UFunc_Nin2_Nout1[L['fmod'], L[15], None] +frexp: _UFunc_Nin1_Nout2[L['frexp'], L[4], None] +gcd: _UFunc_Nin2_Nout1[L['gcd'], L[11], L[0]] +greater: _UFunc_Nin2_Nout1[L['greater'], L[23], None] +greater_equal: _UFunc_Nin2_Nout1[L['greater_equal'], L[23], None] +heaviside: _UFunc_Nin2_Nout1[L['heaviside'], L[4], None] +hypot: _UFunc_Nin2_Nout1[L['hypot'], L[5], L[0]] +invert: _UFunc_Nin1_Nout1[L['invert'], L[12], None] +isfinite: _UFunc_Nin1_Nout1[L['isfinite'], L[20], None] +isinf: _UFunc_Nin1_Nout1[L['isinf'], L[20], None] +isnan: _UFunc_Nin1_Nout1[L['isnan'], L[20], None] +isnat: _UFunc_Nin1_Nout1[L['isnat'], L[2], None] +lcm: _UFunc_Nin2_Nout1[L['lcm'], L[11], None] +ldexp: _UFunc_Nin2_Nout1[L['ldexp'], L[8], None] +left_shift: _UFunc_Nin2_Nout1[L['left_shift'], L[11], None] +less: _UFunc_Nin2_Nout1[L['less'], L[23], None] +less_equal: _UFunc_Nin2_Nout1[L['less_equal'], L[23], None] +log10: _UFunc_Nin1_Nout1[L['log10'], L[8], None] +log1p: _UFunc_Nin1_Nout1[L['log1p'], L[8], None] +log2: _UFunc_Nin1_Nout1[L['log2'], L[8], None] +log: _UFunc_Nin1_Nout1[L['log'], L[10], None] +logaddexp2: _UFunc_Nin2_Nout1[L['logaddexp2'], L[4], float] +logaddexp: _UFunc_Nin2_Nout1[L['logaddexp'], L[4], float] +logical_and: _UFunc_Nin2_Nout1[L['logical_and'], L[20], L[True]] +logical_not: _UFunc_Nin1_Nout1[L['logical_not'], L[20], None] +logical_or: _UFunc_Nin2_Nout1[L['logical_or'], L[20], L[False]] +logical_xor: _UFunc_Nin2_Nout1[L['logical_xor'], L[19], L[False]] +matmul: _GUFunc_Nin2_Nout1[L['matmul'], L[19], None] +maximum: _UFunc_Nin2_Nout1[L['maximum'], L[21], None] +minimum: _UFunc_Nin2_Nout1[L['minimum'], L[21], None] +mod: _UFunc_Nin2_Nout1[L['remainder'], L[16], None] +modf: _UFunc_Nin1_Nout2[L['modf'], L[4], None] +multiply: _UFunc_Nin2_Nout1[L['multiply'], L[23], L[1]] +negative: _UFunc_Nin1_Nout1[L['negative'], L[19], None] +nextafter: _UFunc_Nin2_Nout1[L['nextafter'], L[4], None] +not_equal: _UFunc_Nin2_Nout1[L['not_equal'], L[23], None] +positive: _UFunc_Nin1_Nout1[L['positive'], L[19], None] +power: _UFunc_Nin2_Nout1[L['power'], L[18], None] +rad2deg: _UFunc_Nin1_Nout1[L['rad2deg'], L[5], None] +radians: _UFunc_Nin1_Nout1[L['radians'], L[5], None] +reciprocal: _UFunc_Nin1_Nout1[L['reciprocal'], L[18], None] +remainder: _UFunc_Nin2_Nout1[L['remainder'], L[16], None] +right_shift: _UFunc_Nin2_Nout1[L['right_shift'], L[11], None] +rint: _UFunc_Nin1_Nout1[L['rint'], L[10], None] +sign: _UFunc_Nin1_Nout1[L['sign'], L[19], None] +signbit: _UFunc_Nin1_Nout1[L['signbit'], L[4], None] +sin: _UFunc_Nin1_Nout1[L['sin'], L[9], None] +sinh: _UFunc_Nin1_Nout1[L['sinh'], L[8], None] +spacing: _UFunc_Nin1_Nout1[L['spacing'], L[4], None] +sqrt: _UFunc_Nin1_Nout1[L['sqrt'], L[10], None] +square: _UFunc_Nin1_Nout1[L['square'], L[18], None] +subtract: _UFunc_Nin2_Nout1[L['subtract'], L[21], None] +tan: _UFunc_Nin1_Nout1[L['tan'], L[8], None] +tanh: _UFunc_Nin1_Nout1[L['tanh'], L[8], None] +true_divide: _UFunc_Nin2_Nout1[L['true_divide'], L[11], None] +trunc: _UFunc_Nin1_Nout1[L['trunc'], L[7], None] + +abs = absolute + +class _CopyMode(enum.Enum): + ALWAYS: L[True] + IF_NEEDED: L[False] + NEVER: L[2] + +# Warnings +class ModuleDeprecationWarning(DeprecationWarning): ... +class VisibleDeprecationWarning(UserWarning): ... +class ComplexWarning(RuntimeWarning): ... +class RankWarning(UserWarning): ... + +# Errors +class TooHardError(RuntimeError): ... + +class AxisError(ValueError, IndexError): + axis: None | int + ndim: None | int + @overload + def __init__(self, axis: str, ndim: None = ..., msg_prefix: None = ...) -> None: ... + @overload + def __init__(self, axis: int, ndim: int, msg_prefix: None | str = ...) -> None: ... + +_CallType = TypeVar("_CallType", bound=Union[_ErrFunc, _SupportsWrite[str]]) + +class errstate(Generic[_CallType], ContextDecorator): + call: _CallType + kwargs: _ErrDictOptional + + # Expand `**kwargs` into explicit keyword-only arguments + def __init__( + self, + *, + call: _CallType = ..., + all: Optional[_ErrKind] = ..., + divide: Optional[_ErrKind] = ..., + over: Optional[_ErrKind] = ..., + under: Optional[_ErrKind] = ..., + invalid: Optional[_ErrKind] = ..., + ) -> None: ... + def __enter__(self) -> None: ... + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_value: Optional[BaseException], + traceback: Optional[TracebackType], + /, + ) -> None: ... + +class ndenumerate(Generic[_ScalarType]): + iter: flatiter[NDArray[_ScalarType]] + @overload + def __new__( + cls, arr: _FiniteNestedSequence[_SupportsArray[dtype[_ScalarType]]], + ) -> ndenumerate[_ScalarType]: ... + @overload + def __new__(cls, arr: str | _NestedSequence[str]) -> ndenumerate[str_]: ... + @overload + def __new__(cls, arr: bytes | _NestedSequence[bytes]) -> ndenumerate[bytes_]: ... + @overload + def __new__(cls, arr: bool | _NestedSequence[bool]) -> ndenumerate[bool_]: ... + @overload + def __new__(cls, arr: int | _NestedSequence[int]) -> ndenumerate[int_]: ... + @overload + def __new__(cls, arr: float | _NestedSequence[float]) -> ndenumerate[float_]: ... + @overload + def __new__(cls, arr: complex | _NestedSequence[complex]) -> ndenumerate[complex_]: ... + def __next__(self: ndenumerate[_ScalarType]) -> Tuple[_Shape, _ScalarType]: ... + def __iter__(self: _T) -> _T: ... + +class ndindex: + def __init__(self, *shape: SupportsIndex) -> None: ... + def __iter__(self: _T) -> _T: ... + def __next__(self) -> _Shape: ... + +class DataSource: + def __init__( + self, + destpath: Union[None, str, os.PathLike[str]] = ..., + ) -> None: ... + def __del__(self) -> None: ... + def abspath(self, path: str) -> str: ... + def exists(self, path: str) -> bool: ... + + # Whether the file-object is opened in string or bytes mode (by default) + # depends on the file-extension of `path` + def open( + self, + path: str, + mode: str = ..., + encoding: Optional[str] = ..., + newline: Optional[str] = ..., + ) -> IO[Any]: ... + +# TODO: The type of each `__next__` and `iters` return-type depends +# on the length and dtype of `args`; we can't describe this behavior yet +# as we lack variadics (PEP 646). +class broadcast: + def __new__(cls, *args: ArrayLike) -> broadcast: ... + @property + def index(self) -> int: ... + @property + def iters(self) -> Tuple[flatiter[Any], ...]: ... + @property + def nd(self) -> int: ... + @property + def ndim(self) -> int: ... + @property + def numiter(self) -> int: ... + @property + def shape(self) -> _Shape: ... + @property + def size(self) -> int: ... + def __next__(self) -> Tuple[Any, ...]: ... + def __iter__(self: _T) -> _T: ... + def reset(self) -> None: ... + +class busdaycalendar: + def __new__( + cls, + weekmask: ArrayLike = ..., + holidays: ArrayLike = ..., + ) -> busdaycalendar: ... + @property + def weekmask(self) -> NDArray[bool_]: ... + @property + def holidays(self) -> NDArray[datetime64]: ... + +class finfo(Generic[_FloatType]): + dtype: dtype[_FloatType] + bits: int + eps: _FloatType + epsneg: _FloatType + iexp: int + machep: int + max: _FloatType + maxexp: int + min: _FloatType + minexp: int + negep: int + nexp: int + nmant: int + precision: int + resolution: _FloatType + smallest_subnormal: _FloatType + @property + def smallest_normal(self) -> _FloatType: ... + @property + def tiny(self) -> _FloatType: ... + @overload + def __new__( + cls, dtype: inexact[_NBit1] | _DTypeLike[inexact[_NBit1]] + ) -> finfo[floating[_NBit1]]: ... + @overload + def __new__( + cls, dtype: complex | float | Type[complex] | Type[float] + ) -> finfo[float_]: ... + @overload + def __new__( + cls, dtype: str + ) -> finfo[floating[Any]]: ... + +class iinfo(Generic[_IntType]): + dtype: dtype[_IntType] + kind: str + bits: int + key: str + @property + def min(self) -> int: ... + @property + def max(self) -> int: ... + + @overload + def __new__(cls, dtype: _IntType | _DTypeLike[_IntType]) -> iinfo[_IntType]: ... + @overload + def __new__(cls, dtype: int | Type[int]) -> iinfo[int_]: ... + @overload + def __new__(cls, dtype: str) -> iinfo[Any]: ... + +class format_parser: + dtype: dtype[void] + def __init__( + self, + formats: DTypeLike, + names: None | str | Sequence[str], + titles: None | str | Sequence[str], + aligned: bool = ..., + byteorder: None | _ByteOrder = ..., + ) -> None: ... + +class recarray(ndarray[_ShapeType, _DType_co]): + # NOTE: While not strictly mandatory, we're demanding here that arguments + # for the `format_parser`- and `dtype`-based dtype constructors are + # mutually exclusive + @overload + def __new__( + subtype, + shape: _ShapeLike, + dtype: None = ..., + buf: None | _SupportsBuffer = ..., + offset: SupportsIndex = ..., + strides: None | _ShapeLike = ..., + *, + formats: DTypeLike, + names: None | str | Sequence[str] = ..., + titles: None | str | Sequence[str] = ..., + byteorder: None | _ByteOrder = ..., + aligned: bool = ..., + order: _OrderKACF = ..., + ) -> recarray[Any, dtype[record]]: ... + @overload + def __new__( + subtype, + shape: _ShapeLike, + dtype: DTypeLike, + buf: None | _SupportsBuffer = ..., + offset: SupportsIndex = ..., + strides: None | _ShapeLike = ..., + formats: None = ..., + names: None = ..., + titles: None = ..., + byteorder: None = ..., + aligned: L[False] = ..., + order: _OrderKACF = ..., + ) -> recarray[Any, dtype[Any]]: ... + def __array_finalize__(self, obj: object) -> None: ... + def __getattribute__(self, attr: str) -> Any: ... + def __setattr__(self, attr: str, val: ArrayLike) -> None: ... + @overload + def __getitem__(self, indx: Union[ + SupportsIndex, + _ArrayLikeInt_co, + Tuple[SupportsIndex | _ArrayLikeInt_co, ...], + ]) -> Any: ... + @overload + def __getitem__(self: recarray[Any, dtype[void]], indx: Union[ + None, + slice, + ellipsis, + SupportsIndex, + _ArrayLikeInt_co, + Tuple[None | slice | ellipsis | _ArrayLikeInt_co | SupportsIndex, ...], + ]) -> recarray[Any, _DType_co]: ... + @overload + def __getitem__(self, indx: Union[ + None, + slice, + ellipsis, + SupportsIndex, + _ArrayLikeInt_co, + Tuple[None | slice | ellipsis | _ArrayLikeInt_co | SupportsIndex, ...], + ]) -> ndarray[Any, _DType_co]: ... + @overload + def __getitem__(self, indx: str) -> NDArray[Any]: ... + @overload + def __getitem__(self, indx: list[str]) -> recarray[_ShapeType, dtype[record]]: ... + @overload + def field(self, attr: int | str, val: None = ...) -> Any: ... + @overload + def field(self, attr: int | str, val: ArrayLike) -> None: ... + +class record(void): + def __getattribute__(self, attr: str) -> Any: ... + def __setattr__(self, attr: str, val: ArrayLike) -> None: ... + def pprint(self) -> str: ... + @overload + def __getitem__(self, key: str | SupportsIndex) -> Any: ... + @overload + def __getitem__(self, key: list[str]) -> record: ... + +_NDIterFlagsKind = L[ + "buffered", + "c_index", + "copy_if_overlap", + "common_dtype", + "delay_bufalloc", + "external_loop", + "f_index", + "grow_inner", "growinner", + "multi_index", + "ranged", + "refs_ok", + "reduce_ok", + "zerosize_ok", +] + +_NDIterOpFlagsKind = L[ + "aligned", + "allocate", + "arraymask", + "copy", + "config", + "nbo", + "no_subtype", + "no_broadcast", + "overlap_assume_elementwise", + "readonly", + "readwrite", + "updateifcopy", + "virtual", + "writeonly", + "writemasked" +] + +@final +class nditer: + def __new__( + cls, + op: ArrayLike | Sequence[ArrayLike], + flags: None | Sequence[_NDIterFlagsKind] = ..., + op_flags: None | Sequence[Sequence[_NDIterOpFlagsKind]] = ..., + op_dtypes: DTypeLike | Sequence[DTypeLike] = ..., + order: _OrderKACF = ..., + casting: _CastingKind = ..., + op_axes: None | Sequence[Sequence[SupportsIndex]] = ..., + itershape: None | _ShapeLike = ..., + buffersize: SupportsIndex = ..., + ) -> nditer: ... + def __enter__(self) -> nditer: ... + def __exit__( + self, + exc_type: None | Type[BaseException], + exc_value: None | BaseException, + traceback: None | TracebackType, + ) -> None: ... + def __iter__(self) -> nditer: ... + def __next__(self) -> Tuple[NDArray[Any], ...]: ... + def __len__(self) -> int: ... + def __copy__(self) -> nditer: ... + @overload + def __getitem__(self, index: SupportsIndex) -> NDArray[Any]: ... + @overload + def __getitem__(self, index: slice) -> Tuple[NDArray[Any], ...]: ... + def __setitem__(self, index: slice | SupportsIndex, value: ArrayLike) -> None: ... + def close(self) -> None: ... + def copy(self) -> nditer: ... + def debug_print(self) -> None: ... + def enable_external_loop(self) -> None: ... + def iternext(self) -> bool: ... + def remove_axis(self, i: SupportsIndex, /) -> None: ... + def remove_multi_index(self) -> None: ... + def reset(self) -> None: ... + @property + def dtypes(self) -> Tuple[dtype[Any], ...]: ... + @property + def finished(self) -> bool: ... + @property + def has_delayed_bufalloc(self) -> bool: ... + @property + def has_index(self) -> bool: ... + @property + def has_multi_index(self) -> bool: ... + @property + def index(self) -> int: ... + @property + def iterationneedsapi(self) -> bool: ... + @property + def iterindex(self) -> int: ... + @property + def iterrange(self) -> Tuple[int, ...]: ... + @property + def itersize(self) -> int: ... + @property + def itviews(self) -> Tuple[NDArray[Any], ...]: ... + @property + def multi_index(self) -> Tuple[int, ...]: ... + @property + def ndim(self) -> int: ... + @property + def nop(self) -> int: ... + @property + def operands(self) -> Tuple[NDArray[Any], ...]: ... + @property + def shape(self) -> Tuple[int, ...]: ... + @property + def value(self) -> Tuple[NDArray[Any], ...]: ... + +_MemMapModeKind = L[ + "readonly", "r", + "copyonwrite", "c", + "readwrite", "r+", + "write", "w+", +] + +class memmap(ndarray[_ShapeType, _DType_co]): + __array_priority__: ClassVar[float] + filename: str | None + offset: int + mode: str + @overload + def __new__( + subtype, + filename: str | bytes | os.PathLike[str] | os.PathLike[bytes] | _MemMapIOProtocol, + dtype: Type[uint8] = ..., + mode: _MemMapModeKind = ..., + offset: int = ..., + shape: None | int | Tuple[int, ...] = ..., + order: _OrderKACF = ..., + ) -> memmap[Any, dtype[uint8]]: ... + @overload + def __new__( + subtype, + filename: str | bytes | os.PathLike[str] | os.PathLike[bytes] | _MemMapIOProtocol, + dtype: _DTypeLike[_ScalarType], + mode: _MemMapModeKind = ..., + offset: int = ..., + shape: None | int | Tuple[int, ...] = ..., + order: _OrderKACF = ..., + ) -> memmap[Any, dtype[_ScalarType]]: ... + @overload + def __new__( + subtype, + filename: str | bytes | os.PathLike[str] | os.PathLike[bytes] | _MemMapIOProtocol, + dtype: DTypeLike, + mode: _MemMapModeKind = ..., + offset: int = ..., + shape: None | int | Tuple[int, ...] = ..., + order: _OrderKACF = ..., + ) -> memmap[Any, dtype[Any]]: ... + def __array_finalize__(self, obj: memmap[Any, Any]) -> None: ... + def __array_wrap__( + self, + array: memmap[_ShapeType, _DType_co], + context: None | Tuple[ufunc, Tuple[Any, ...], int] = ..., + ) -> Any: ... + def flush(self) -> None: ... + +class vectorize: + pyfunc: Callable[..., Any] + cache: bool + signature: None | str + otypes: None | str + excluded: Set[int | str] + __doc__: None | str + def __init__( + self, + pyfunc: Callable[..., Any], + otypes: None | str | Iterable[DTypeLike] = ..., + doc: None | str = ..., + excluded: None | Iterable[int | str] = ..., + cache: bool = ..., + signature: None | str = ..., + ) -> None: ... + def __call__(self, *args: Any, **kwargs: Any) -> NDArray[Any]: ... + +class poly1d: + @property + def variable(self) -> str: ... + @property + def order(self) -> int: ... + @property + def o(self) -> int: ... + @property + def roots(self) -> NDArray[Any]: ... + @property + def r(self) -> NDArray[Any]: ... + + @property + def coeffs(self) -> NDArray[Any]: ... + @coeffs.setter + def coeffs(self, value: NDArray[Any]) -> None: ... + + @property + def c(self) -> NDArray[Any]: ... + @c.setter + def c(self, value: NDArray[Any]) -> None: ... + + @property + def coef(self) -> NDArray[Any]: ... + @coef.setter + def coef(self, value: NDArray[Any]) -> None: ... + + @property + def coefficients(self) -> NDArray[Any]: ... + @coefficients.setter + def coefficients(self, value: NDArray[Any]) -> None: ... + + __hash__: None # type: ignore + + @overload + def __array__(self, t: None = ...) -> NDArray[Any]: ... + @overload + def __array__(self, t: _DType) -> ndarray[Any, _DType]: ... + + @overload + def __call__(self, val: _ScalarLike_co) -> Any: ... + @overload + def __call__(self, val: poly1d) -> poly1d: ... + @overload + def __call__(self, val: ArrayLike) -> NDArray[Any]: ... + + def __init__( + self, + c_or_r: ArrayLike, + r: bool = ..., + variable: None | str = ..., + ) -> None: ... + def __len__(self) -> int: ... + def __neg__(self) -> poly1d: ... + def __pos__(self) -> poly1d: ... + def __mul__(self, other: ArrayLike) -> poly1d: ... + def __rmul__(self, other: ArrayLike) -> poly1d: ... + def __add__(self, other: ArrayLike) -> poly1d: ... + def __radd__(self, other: ArrayLike) -> poly1d: ... + def __pow__(self, val: _FloatLike_co) -> poly1d: ... # Integral floats are accepted + def __sub__(self, other: ArrayLike) -> poly1d: ... + def __rsub__(self, other: ArrayLike) -> poly1d: ... + def __div__(self, other: ArrayLike) -> poly1d: ... + def __truediv__(self, other: ArrayLike) -> poly1d: ... + def __rdiv__(self, other: ArrayLike) -> poly1d: ... + def __rtruediv__(self, other: ArrayLike) -> poly1d: ... + def __getitem__(self, val: int) -> Any: ... + def __setitem__(self, key: int, val: Any) -> None: ... + def __iter__(self) -> Iterator[Any]: ... + def deriv(self, m: SupportsInt | SupportsIndex = ...) -> poly1d: ... + def integ( + self, + m: SupportsInt | SupportsIndex = ..., + k: None | _ArrayLikeComplex_co | _ArrayLikeObject_co = ..., + ) -> poly1d: ... + +class matrix(ndarray[_ShapeType, _DType_co]): + __array_priority__: ClassVar[float] + def __new__( + subtype, + data: ArrayLike, + dtype: DTypeLike = ..., + copy: bool = ..., + ) -> matrix[Any, Any]: ... + def __array_finalize__(self, obj: NDArray[Any]) -> None: ... + + @overload + def __getitem__(self, key: Union[ + SupportsIndex, + _ArrayLikeInt_co, + Tuple[SupportsIndex | _ArrayLikeInt_co, ...], + ]) -> Any: ... + @overload + def __getitem__(self, key: Union[ + None, + slice, + ellipsis, + SupportsIndex, + _ArrayLikeInt_co, + Tuple[None | slice | ellipsis | _ArrayLikeInt_co | SupportsIndex, ...], + ]) -> matrix[Any, _DType_co]: ... + @overload + def __getitem__(self: NDArray[void], key: str) -> matrix[Any, dtype[Any]]: ... + @overload + def __getitem__(self: NDArray[void], key: list[str]) -> matrix[_ShapeType, dtype[void]]: ... + + def __mul__(self, other: ArrayLike) -> matrix[Any, Any]: ... + def __rmul__(self, other: ArrayLike) -> matrix[Any, Any]: ... + def __imul__(self, other: ArrayLike) -> matrix[_ShapeType, _DType_co]: ... + def __pow__(self, other: ArrayLike) -> matrix[Any, Any]: ... + def __ipow__(self, other: ArrayLike) -> matrix[_ShapeType, _DType_co]: ... + + @overload + def sum(self, axis: None = ..., dtype: DTypeLike = ..., out: None = ...) -> Any: ... + @overload + def sum(self, axis: _ShapeLike, dtype: DTypeLike = ..., out: None = ...) -> matrix[Any, Any]: ... + @overload + def sum(self, axis: None | _ShapeLike = ..., dtype: DTypeLike = ..., out: _NdArraySubClass = ...) -> _NdArraySubClass: ... + + @overload + def mean(self, axis: None = ..., dtype: DTypeLike = ..., out: None = ...) -> Any: ... + @overload + def mean(self, axis: _ShapeLike, dtype: DTypeLike = ..., out: None = ...) -> matrix[Any, Any]: ... + @overload + def mean(self, axis: None | _ShapeLike = ..., dtype: DTypeLike = ..., out: _NdArraySubClass = ...) -> _NdArraySubClass: ... + + @overload + def std(self, axis: None = ..., dtype: DTypeLike = ..., out: None = ..., ddof: float = ...) -> Any: ... + @overload + def std(self, axis: _ShapeLike, dtype: DTypeLike = ..., out: None = ..., ddof: float = ...) -> matrix[Any, Any]: ... + @overload + def std(self, axis: None | _ShapeLike = ..., dtype: DTypeLike = ..., out: _NdArraySubClass = ..., ddof: float = ...) -> _NdArraySubClass: ... + + @overload + def var(self, axis: None = ..., dtype: DTypeLike = ..., out: None = ..., ddof: float = ...) -> Any: ... + @overload + def var(self, axis: _ShapeLike, dtype: DTypeLike = ..., out: None = ..., ddof: float = ...) -> matrix[Any, Any]: ... + @overload + def var(self, axis: None | _ShapeLike = ..., dtype: DTypeLike = ..., out: _NdArraySubClass = ..., ddof: float = ...) -> _NdArraySubClass: ... + + @overload + def prod(self, axis: None = ..., dtype: DTypeLike = ..., out: None = ...) -> Any: ... + @overload + def prod(self, axis: _ShapeLike, dtype: DTypeLike = ..., out: None = ...) -> matrix[Any, Any]: ... + @overload + def prod(self, axis: None | _ShapeLike = ..., dtype: DTypeLike = ..., out: _NdArraySubClass = ...) -> _NdArraySubClass: ... + + @overload + def any(self, axis: None = ..., out: None = ...) -> bool_: ... + @overload + def any(self, axis: _ShapeLike, out: None = ...) -> matrix[Any, dtype[bool_]]: ... + @overload + def any(self, axis: None | _ShapeLike = ..., out: _NdArraySubClass = ...) -> _NdArraySubClass: ... + + @overload + def all(self, axis: None = ..., out: None = ...) -> bool_: ... + @overload + def all(self, axis: _ShapeLike, out: None = ...) -> matrix[Any, dtype[bool_]]: ... + @overload + def all(self, axis: None | _ShapeLike = ..., out: _NdArraySubClass = ...) -> _NdArraySubClass: ... + + @overload + def max(self: NDArray[_ScalarType], axis: None = ..., out: None = ...) -> _ScalarType: ... + @overload + def max(self, axis: _ShapeLike, out: None = ...) -> matrix[Any, _DType_co]: ... + @overload + def max(self, axis: None | _ShapeLike = ..., out: _NdArraySubClass = ...) -> _NdArraySubClass: ... + + @overload + def min(self: NDArray[_ScalarType], axis: None = ..., out: None = ...) -> _ScalarType: ... + @overload + def min(self, axis: _ShapeLike, out: None = ...) -> matrix[Any, _DType_co]: ... + @overload + def min(self, axis: None | _ShapeLike = ..., out: _NdArraySubClass = ...) -> _NdArraySubClass: ... + + @overload + def argmax(self: NDArray[_ScalarType], axis: None = ..., out: None = ...) -> intp: ... + @overload + def argmax(self, axis: _ShapeLike, out: None = ...) -> matrix[Any, dtype[intp]]: ... + @overload + def argmax(self, axis: None | _ShapeLike = ..., out: _NdArraySubClass = ...) -> _NdArraySubClass: ... + + @overload + def argmin(self: NDArray[_ScalarType], axis: None = ..., out: None = ...) -> intp: ... + @overload + def argmin(self, axis: _ShapeLike, out: None = ...) -> matrix[Any, dtype[intp]]: ... + @overload + def argmin(self, axis: None | _ShapeLike = ..., out: _NdArraySubClass = ...) -> _NdArraySubClass: ... + + @overload + def ptp(self: NDArray[_ScalarType], axis: None = ..., out: None = ...) -> _ScalarType: ... + @overload + def ptp(self, axis: _ShapeLike, out: None = ...) -> matrix[Any, _DType_co]: ... + @overload + def ptp(self, axis: None | _ShapeLike = ..., out: _NdArraySubClass = ...) -> _NdArraySubClass: ... + + def squeeze(self, axis: None | _ShapeLike = ...) -> matrix[Any, _DType_co]: ... + def tolist(self: matrix[Any, dtype[_SupportsItem[_T]]]) -> List[List[_T]]: ... # type: ignore[typevar] + def ravel(self, order: _OrderKACF = ...) -> matrix[Any, _DType_co]: ... + def flatten(self, order: _OrderKACF = ...) -> matrix[Any, _DType_co]: ... + + @property + def T(self) -> matrix[Any, _DType_co]: ... + @property + def I(self) -> matrix[Any, Any]: ... + @property + def A(self) -> ndarray[_ShapeType, _DType_co]: ... + @property + def A1(self) -> ndarray[Any, _DType_co]: ... + @property + def H(self) -> matrix[Any, _DType_co]: ... + def getT(self) -> matrix[Any, _DType_co]: ... + def getI(self) -> matrix[Any, Any]: ... + def getA(self) -> ndarray[_ShapeType, _DType_co]: ... + def getA1(self) -> ndarray[Any, _DType_co]: ... + def getH(self) -> matrix[Any, _DType_co]: ... + +_CharType = TypeVar("_CharType", str_, bytes_) +_CharDType = TypeVar("_CharDType", dtype[str_], dtype[bytes_]) +_CharArray = chararray[Any, dtype[_CharType]] + +class chararray(ndarray[_ShapeType, _CharDType]): + @overload + def __new__( + subtype, + shape: _ShapeLike, + itemsize: SupportsIndex | SupportsInt = ..., + unicode: L[False] = ..., + buffer: _SupportsBuffer = ..., + offset: SupportsIndex = ..., + strides: _ShapeLike = ..., + order: _OrderKACF = ..., + ) -> chararray[Any, dtype[bytes_]]: ... + @overload + def __new__( + subtype, + shape: _ShapeLike, + itemsize: SupportsIndex | SupportsInt = ..., + unicode: L[True] = ..., + buffer: _SupportsBuffer = ..., + offset: SupportsIndex = ..., + strides: _ShapeLike = ..., + order: _OrderKACF = ..., + ) -> chararray[Any, dtype[str_]]: ... + + def __array_finalize__(self, obj: NDArray[str_ | bytes_]) -> None: ... + def __mul__(self, other: _ArrayLikeInt_co) -> chararray[Any, _CharDType]: ... + def __rmul__(self, other: _ArrayLikeInt_co) -> chararray[Any, _CharDType]: ... + def __mod__(self, i: Any) -> chararray[Any, _CharDType]: ... + + @overload + def __eq__( + self: _CharArray[str_], + other: _ArrayLikeStr_co, + ) -> NDArray[bool_]: ... + @overload + def __eq__( + self: _CharArray[bytes_], + other: _ArrayLikeBytes_co, + ) -> NDArray[bool_]: ... + + @overload + def __ne__( + self: _CharArray[str_], + other: _ArrayLikeStr_co, + ) -> NDArray[bool_]: ... + @overload + def __ne__( + self: _CharArray[bytes_], + other: _ArrayLikeBytes_co, + ) -> NDArray[bool_]: ... + + @overload + def __ge__( + self: _CharArray[str_], + other: _ArrayLikeStr_co, + ) -> NDArray[bool_]: ... + @overload + def __ge__( + self: _CharArray[bytes_], + other: _ArrayLikeBytes_co, + ) -> NDArray[bool_]: ... + + @overload + def __le__( + self: _CharArray[str_], + other: _ArrayLikeStr_co, + ) -> NDArray[bool_]: ... + @overload + def __le__( + self: _CharArray[bytes_], + other: _ArrayLikeBytes_co, + ) -> NDArray[bool_]: ... + + @overload + def __gt__( + self: _CharArray[str_], + other: _ArrayLikeStr_co, + ) -> NDArray[bool_]: ... + @overload + def __gt__( + self: _CharArray[bytes_], + other: _ArrayLikeBytes_co, + ) -> NDArray[bool_]: ... + + @overload + def __lt__( + self: _CharArray[str_], + other: _ArrayLikeStr_co, + ) -> NDArray[bool_]: ... + @overload + def __lt__( + self: _CharArray[bytes_], + other: _ArrayLikeBytes_co, + ) -> NDArray[bool_]: ... + + @overload + def __add__( + self: _CharArray[str_], + other: _ArrayLikeStr_co, + ) -> _CharArray[str_]: ... + @overload + def __add__( + self: _CharArray[bytes_], + other: _ArrayLikeBytes_co, + ) -> _CharArray[bytes_]: ... + + @overload + def __radd__( + self: _CharArray[str_], + other: _ArrayLikeStr_co, + ) -> _CharArray[str_]: ... + @overload + def __radd__( + self: _CharArray[bytes_], + other: _ArrayLikeBytes_co, + ) -> _CharArray[bytes_]: ... + + @overload + def center( + self: _CharArray[str_], + width: _ArrayLikeInt_co, + fillchar: _ArrayLikeStr_co = ..., + ) -> _CharArray[str_]: ... + @overload + def center( + self: _CharArray[bytes_], + width: _ArrayLikeInt_co, + fillchar: _ArrayLikeBytes_co = ..., + ) -> _CharArray[bytes_]: ... + + @overload + def count( + self: _CharArray[str_], + sub: _ArrayLikeStr_co, + start: _ArrayLikeInt_co = ..., + end: None | _ArrayLikeInt_co = ..., + ) -> NDArray[int_]: ... + @overload + def count( + self: _CharArray[bytes_], + sub: _ArrayLikeBytes_co, + start: _ArrayLikeInt_co = ..., + end: None | _ArrayLikeInt_co = ..., + ) -> NDArray[int_]: ... + + def decode( + self: _CharArray[bytes_], + encoding: None | str = ..., + errors: None | str = ..., + ) -> _CharArray[str_]: ... + + def encode( + self: _CharArray[str_], + encoding: None | str = ..., + errors: None | str = ..., + ) -> _CharArray[bytes_]: ... + + @overload + def endswith( + self: _CharArray[str_], + suffix: _ArrayLikeStr_co, + start: _ArrayLikeInt_co = ..., + end: None | _ArrayLikeInt_co = ..., + ) -> NDArray[bool_]: ... + @overload + def endswith( + self: _CharArray[bytes_], + suffix: _ArrayLikeBytes_co, + start: _ArrayLikeInt_co = ..., + end: None | _ArrayLikeInt_co = ..., + ) -> NDArray[bool_]: ... + + def expandtabs( + self, + tabsize: _ArrayLikeInt_co = ..., + ) -> chararray[Any, _CharDType]: ... + + @overload + def find( + self: _CharArray[str_], + sub: _ArrayLikeStr_co, + start: _ArrayLikeInt_co = ..., + end: None | _ArrayLikeInt_co = ..., + ) -> NDArray[int_]: ... + @overload + def find( + self: _CharArray[bytes_], + sub: _ArrayLikeBytes_co, + start: _ArrayLikeInt_co = ..., + end: None | _ArrayLikeInt_co = ..., + ) -> NDArray[int_]: ... + + @overload + def index( + self: _CharArray[str_], + sub: _ArrayLikeStr_co, + start: _ArrayLikeInt_co = ..., + end: None | _ArrayLikeInt_co = ..., + ) -> NDArray[int_]: ... + @overload + def index( + self: _CharArray[bytes_], + sub: _ArrayLikeBytes_co, + start: _ArrayLikeInt_co = ..., + end: None | _ArrayLikeInt_co = ..., + ) -> NDArray[int_]: ... + + @overload + def join( + self: _CharArray[str_], + seq: _ArrayLikeStr_co, + ) -> _CharArray[str_]: ... + @overload + def join( + self: _CharArray[bytes_], + seq: _ArrayLikeBytes_co, + ) -> _CharArray[bytes_]: ... + + @overload + def ljust( + self: _CharArray[str_], + width: _ArrayLikeInt_co, + fillchar: _ArrayLikeStr_co = ..., + ) -> _CharArray[str_]: ... + @overload + def ljust( + self: _CharArray[bytes_], + width: _ArrayLikeInt_co, + fillchar: _ArrayLikeBytes_co = ..., + ) -> _CharArray[bytes_]: ... + + @overload + def lstrip( + self: _CharArray[str_], + chars: None | _ArrayLikeStr_co = ..., + ) -> _CharArray[str_]: ... + @overload + def lstrip( + self: _CharArray[bytes_], + chars: None | _ArrayLikeBytes_co = ..., + ) -> _CharArray[bytes_]: ... + + @overload + def partition( + self: _CharArray[str_], + sep: _ArrayLikeStr_co, + ) -> _CharArray[str_]: ... + @overload + def partition( + self: _CharArray[bytes_], + sep: _ArrayLikeBytes_co, + ) -> _CharArray[bytes_]: ... + + @overload + def replace( + self: _CharArray[str_], + old: _ArrayLikeStr_co, + new: _ArrayLikeStr_co, + count: None | _ArrayLikeInt_co = ..., + ) -> _CharArray[str_]: ... + @overload + def replace( + self: _CharArray[bytes_], + old: _ArrayLikeBytes_co, + new: _ArrayLikeBytes_co, + count: None | _ArrayLikeInt_co = ..., + ) -> _CharArray[bytes_]: ... + + @overload + def rfind( + self: _CharArray[str_], + sub: _ArrayLikeStr_co, + start: _ArrayLikeInt_co = ..., + end: None | _ArrayLikeInt_co = ..., + ) -> NDArray[int_]: ... + @overload + def rfind( + self: _CharArray[bytes_], + sub: _ArrayLikeBytes_co, + start: _ArrayLikeInt_co = ..., + end: None | _ArrayLikeInt_co = ..., + ) -> NDArray[int_]: ... + + @overload + def rindex( + self: _CharArray[str_], + sub: _ArrayLikeStr_co, + start: _ArrayLikeInt_co = ..., + end: None | _ArrayLikeInt_co = ..., + ) -> NDArray[int_]: ... + @overload + def rindex( + self: _CharArray[bytes_], + sub: _ArrayLikeBytes_co, + start: _ArrayLikeInt_co = ..., + end: None | _ArrayLikeInt_co = ..., + ) -> NDArray[int_]: ... + + @overload + def rjust( + self: _CharArray[str_], + width: _ArrayLikeInt_co, + fillchar: _ArrayLikeStr_co = ..., + ) -> _CharArray[str_]: ... + @overload + def rjust( + self: _CharArray[bytes_], + width: _ArrayLikeInt_co, + fillchar: _ArrayLikeBytes_co = ..., + ) -> _CharArray[bytes_]: ... + + @overload + def rpartition( + self: _CharArray[str_], + sep: _ArrayLikeStr_co, + ) -> _CharArray[str_]: ... + @overload + def rpartition( + self: _CharArray[bytes_], + sep: _ArrayLikeBytes_co, + ) -> _CharArray[bytes_]: ... + + @overload + def rsplit( + self: _CharArray[str_], + sep: None | _ArrayLikeStr_co = ..., + maxsplit: None | _ArrayLikeInt_co = ..., + ) -> NDArray[object_]: ... + @overload + def rsplit( + self: _CharArray[bytes_], + sep: None | _ArrayLikeBytes_co = ..., + maxsplit: None | _ArrayLikeInt_co = ..., + ) -> NDArray[object_]: ... + + @overload + def rstrip( + self: _CharArray[str_], + chars: None | _ArrayLikeStr_co = ..., + ) -> _CharArray[str_]: ... + @overload + def rstrip( + self: _CharArray[bytes_], + chars: None | _ArrayLikeBytes_co = ..., + ) -> _CharArray[bytes_]: ... + + @overload + def split( + self: _CharArray[str_], + sep: None | _ArrayLikeStr_co = ..., + maxsplit: None | _ArrayLikeInt_co = ..., + ) -> NDArray[object_]: ... + @overload + def split( + self: _CharArray[bytes_], + sep: None | _ArrayLikeBytes_co = ..., + maxsplit: None | _ArrayLikeInt_co = ..., + ) -> NDArray[object_]: ... + + def splitlines(self, keepends: None | _ArrayLikeBool_co = ...) -> NDArray[object_]: ... + + @overload + def startswith( + self: _CharArray[str_], + prefix: _ArrayLikeStr_co, + start: _ArrayLikeInt_co = ..., + end: None | _ArrayLikeInt_co = ..., + ) -> NDArray[bool_]: ... + @overload + def startswith( + self: _CharArray[bytes_], + prefix: _ArrayLikeBytes_co, + start: _ArrayLikeInt_co = ..., + end: None | _ArrayLikeInt_co = ..., + ) -> NDArray[bool_]: ... + + @overload + def strip( + self: _CharArray[str_], + chars: None | _ArrayLikeStr_co = ..., + ) -> _CharArray[str_]: ... + @overload + def strip( + self: _CharArray[bytes_], + chars: None | _ArrayLikeBytes_co = ..., + ) -> _CharArray[bytes_]: ... + + @overload + def translate( + self: _CharArray[str_], + table: _ArrayLikeStr_co, + deletechars: None | _ArrayLikeStr_co = ..., + ) -> _CharArray[str_]: ... + @overload + def translate( + self: _CharArray[bytes_], + table: _ArrayLikeBytes_co, + deletechars: None | _ArrayLikeBytes_co = ..., + ) -> _CharArray[bytes_]: ... + + def zfill(self, width: _ArrayLikeInt_co) -> chararray[Any, _CharDType]: ... + def capitalize(self) -> chararray[_ShapeType, _CharDType]: ... + def title(self) -> chararray[_ShapeType, _CharDType]: ... + def swapcase(self) -> chararray[_ShapeType, _CharDType]: ... + def lower(self) -> chararray[_ShapeType, _CharDType]: ... + def upper(self) -> chararray[_ShapeType, _CharDType]: ... + def isalnum(self) -> ndarray[_ShapeType, dtype[bool_]]: ... + def isalpha(self) -> ndarray[_ShapeType, dtype[bool_]]: ... + def isdigit(self) -> ndarray[_ShapeType, dtype[bool_]]: ... + def islower(self) -> ndarray[_ShapeType, dtype[bool_]]: ... + def isspace(self) -> ndarray[_ShapeType, dtype[bool_]]: ... + def istitle(self) -> ndarray[_ShapeType, dtype[bool_]]: ... + def isupper(self) -> ndarray[_ShapeType, dtype[bool_]]: ... + def isnumeric(self) -> ndarray[_ShapeType, dtype[bool_]]: ... + def isdecimal(self) -> ndarray[_ShapeType, dtype[bool_]]: ... + +# NOTE: Deprecated +# class MachAr: ... + +class _SupportsDLPack(Protocol[_T_contra]): + def __dlpack__(self, *, stream: None | _T_contra = ...) -> _PyCapsule: ... + +def _from_dlpack(__obj: _SupportsDLPack[None]) -> NDArray[Any]: ... diff --git a/numpy/_build_utils/README b/numpy/_build_utils/README deleted file mode 100644 index 6976e0233996..000000000000 --- a/numpy/_build_utils/README +++ /dev/null @@ -1,8 +0,0 @@ -======= -WARNING -======= - -This directory (numpy/_build_utils) is *not* part of the public numpy API, - - it is internal build support for numpy. - - it is only present in source distributions or during an in place build - - it is *not* installed with the rest of numpy diff --git a/numpy/_build_utils/__init__.py b/numpy/_build_utils/__init__.py deleted file mode 100644 index 1d0f69b67d8f..000000000000 --- a/numpy/_build_utils/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import division, absolute_import, print_function diff --git a/numpy/_build_utils/apple_accelerate.py b/numpy/_build_utils/apple_accelerate.py deleted file mode 100644 index 36dd7584a6e4..000000000000 --- a/numpy/_build_utils/apple_accelerate.py +++ /dev/null @@ -1,28 +0,0 @@ -from __future__ import division, absolute_import, print_function - -import os -import sys -import re - -__all__ = ['uses_accelerate_framework', 'get_sgemv_fix'] - -def uses_accelerate_framework(info): - """ Returns True if Accelerate framework is used for BLAS/LAPACK """ - # If we're not building on Darwin (macOS), don't use Accelerate - if sys.platform != "darwin": - return False - # If we're building on macOS, but targeting a different platform, - # don't use Accelerate. - if os.getenv('_PYTHON_HOST_PLATFORM', None): - return False - r_accelerate = re.compile("Accelerate") - extra_link_args = info.get('extra_link_args', '') - for arg in extra_link_args: - if r_accelerate.search(arg): - return True - return False - -def get_sgemv_fix(): - """ Returns source file needed to correct SGEMV """ - path = os.path.abspath(os.path.dirname(__file__)) - return [os.path.join(path, 'src', 'apple_sgemv_fix.c')] diff --git a/numpy/_build_utils/src/apple_sgemv_fix.c b/numpy/_build_utils/src/apple_sgemv_fix.c deleted file mode 100644 index 4c9c82ece6d7..000000000000 --- a/numpy/_build_utils/src/apple_sgemv_fix.c +++ /dev/null @@ -1,227 +0,0 @@ -/* This is a collection of ugly hacks to circumvent a bug in - * Apple Accelerate framework's SGEMV subroutine. - * - * See: https://github.com/numpy/numpy/issues/4007 - * - * SGEMV in Accelerate framework will segfault on MacOS X version 10.9 - * (aka Mavericks) if arrays are not aligned to 32 byte boundaries - * and the CPU supports AVX instructions. This can produce segfaults - * in np.dot. - * - * This patch overshadows the symbols cblas_sgemv, sgemv_ and sgemv - * exported by Accelerate to produce the correct behavior. The MacOS X - * version and CPU specs are checked on module import. If Mavericks and - * AVX are detected the call to SGEMV is emulated with a call to SGEMM - * if the arrays are not 32 byte aligned. If the exported symbols cannot - * be overshadowed on module import, a fatal error is produced and the - * process aborts. All the fixes are in a self-contained C file - * and do not alter the multiarray C code. The patch is not applied - * unless NumPy is configured to link with Apple's Accelerate - * framework. - * - */ - -#define NPY_NO_DEPRECATED_API NPY_API_VERSION -#include "Python.h" -#include "numpy/arrayobject.h" - -#include <string.h> -#include <dlfcn.h> -#include <stdlib.h> -#include <stdio.h> - -/* ----------------------------------------------------------------- */ -/* Original cblas_sgemv */ - -#define VECLIB_FILE "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/vecLib" - -enum CBLAS_ORDER {CblasRowMajor=101, CblasColMajor=102}; -enum CBLAS_TRANSPOSE {CblasNoTrans=111, CblasTrans=112, CblasConjTrans=113}; -extern void cblas_xerbla(int info, const char *rout, const char *form, ...); - -typedef void cblas_sgemv_t(const enum CBLAS_ORDER order, - const enum CBLAS_TRANSPOSE TransA, const int M, const int N, - const float alpha, const float *A, const int lda, - const float *X, const int incX, - const float beta, float *Y, const int incY); - -typedef void cblas_sgemm_t(const enum CBLAS_ORDER order, - const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_TRANSPOSE TransB, - const int M, const int N, const int K, - const float alpha, const float *A, const int lda, - const float *B, const int ldb, - const float beta, float *C, const int incC); - -typedef void fortran_sgemv_t( const char* trans, const int* m, const int* n, - const float* alpha, const float* A, const int* ldA, - const float* X, const int* incX, - const float* beta, float* Y, const int* incY ); - -static void *veclib = NULL; -static cblas_sgemv_t *accelerate_cblas_sgemv = NULL; -static cblas_sgemm_t *accelerate_cblas_sgemm = NULL; -static fortran_sgemv_t *accelerate_sgemv = NULL; -static int AVX_and_10_9 = 0; - -/* Dynamic check for AVX support - * __builtin_cpu_supports("avx") is available in gcc 4.8, - * but clang and icc do not currently support it. */ -#define cpu_supports_avx()\ -(system("sysctl -n machdep.cpu.features | grep -q AVX") == 0) - -/* Check if we are using MacOS X version 10.9 */ -#define using_mavericks()\ -(system("sw_vers -productVersion | grep -q 10\\.9\\.") == 0) - -__attribute__((destructor)) -static void unloadlib(void) -{ - if (veclib) dlclose(veclib); -} - -__attribute__((constructor)) -static void loadlib() -/* automatically executed on module import */ -{ - char errormsg[1024]; - int AVX, MAVERICKS; - memset((void*)errormsg, 0, sizeof(errormsg)); - /* check if the CPU supports AVX */ - AVX = cpu_supports_avx(); - /* check if the OS is MacOS X Mavericks */ - MAVERICKS = using_mavericks(); - /* we need the workaround when the CPU supports - * AVX and the OS version is Mavericks */ - AVX_and_10_9 = AVX && MAVERICKS; - /* load vecLib */ - veclib = dlopen(VECLIB_FILE, RTLD_LOCAL | RTLD_FIRST); - if (!veclib) { - veclib = NULL; - snprintf(errormsg, sizeof(errormsg), - "Failed to open vecLib from location '%s'.", VECLIB_FILE); - Py_FatalError(errormsg); /* calls abort() and dumps core */ - } - /* resolve Fortran SGEMV from Accelerate */ - accelerate_sgemv = (fortran_sgemv_t*) dlsym(veclib, "sgemv_"); - if (!accelerate_sgemv) { - unloadlib(); - Py_FatalError("Failed to resolve symbol 'sgemv_'."); - } - /* resolve cblas_sgemv from Accelerate */ - accelerate_cblas_sgemv = (cblas_sgemv_t*) dlsym(veclib, "cblas_sgemv"); - if (!accelerate_cblas_sgemv) { - unloadlib(); - Py_FatalError("Failed to resolve symbol 'cblas_sgemv'."); - } - /* resolve cblas_sgemm from Accelerate */ - accelerate_cblas_sgemm = (cblas_sgemm_t*) dlsym(veclib, "cblas_sgemm"); - if (!accelerate_cblas_sgemm) { - unloadlib(); - Py_FatalError("Failed to resolve symbol 'cblas_sgemm'."); - } -} - -/* ----------------------------------------------------------------- */ -/* Fortran SGEMV override */ - -void sgemv_( const char* trans, const int* m, const int* n, - const float* alpha, const float* A, const int* ldA, - const float* X, const int* incX, - const float* beta, float* Y, const int* incY ) -{ - /* It is safe to use the original SGEMV if we are not using AVX on Mavericks - * or the input arrays A, X and Y are all aligned on 32 byte boundaries. */ - #define BADARRAY(x) (((npy_intp)(void*)x) % 32) - const int use_sgemm = AVX_and_10_9 && (BADARRAY(A) || BADARRAY(X) || BADARRAY(Y)); - if (!use_sgemm) { - accelerate_sgemv(trans,m,n,alpha,A,ldA,X,incX,beta,Y,incY); - return; - } - - /* Arrays are misaligned, the CPU supports AVX, and we are running - * Mavericks. - * - * Emulation of SGEMV with SGEMM: - * - * SGEMV allows vectors to be strided. SGEMM requires all arrays to be - * contiguous along the leading dimension. To emulate striding in SGEMV - * with the leading dimension arguments in SGEMM we compute - * - * Y = alpha * op(A) @ X + beta * Y - * - * as - * - * Y.T = alpha * X.T @ op(A).T + beta * Y.T - * - * Because Fortran uses column major order and X.T and Y.T are row vectors, - * the leading dimensions of X.T and Y.T in SGEMM become equal to the - * strides of the column vectors X and Y in SGEMV. */ - - switch (*trans) { - case 'T': - case 't': - case 'C': - case 'c': - accelerate_cblas_sgemm( CblasColMajor, CblasNoTrans, CblasNoTrans, - 1, *n, *m, *alpha, X, *incX, A, *ldA, *beta, Y, *incY ); - break; - case 'N': - case 'n': - accelerate_cblas_sgemm( CblasColMajor, CblasNoTrans, CblasTrans, - 1, *m, *n, *alpha, X, *incX, A, *ldA, *beta, Y, *incY ); - break; - default: - cblas_xerbla(1, "SGEMV", "Illegal transpose setting: %c\n", *trans); - } -} - -/* ----------------------------------------------------------------- */ -/* Override for an alias symbol for sgemv_ in Accelerate */ - -void sgemv (char *trans, - const int *m, const int *n, - const float *alpha, - const float *A, const int *lda, - const float *B, const int *incB, - const float *beta, - float *C, const int *incC) -{ - sgemv_(trans,m,n,alpha,A,lda,B,incB,beta,C,incC); -} - -/* ----------------------------------------------------------------- */ -/* cblas_sgemv override, based on Netlib CBLAS code */ - -void cblas_sgemv(const enum CBLAS_ORDER order, - const enum CBLAS_TRANSPOSE TransA, const int M, const int N, - const float alpha, const float *A, const int lda, - const float *X, const int incX, const float beta, - float *Y, const int incY) -{ - char TA; - if (order == CblasColMajor) - { - if (TransA == CblasNoTrans) TA = 'N'; - else if (TransA == CblasTrans) TA = 'T'; - else if (TransA == CblasConjTrans) TA = 'C'; - else - { - cblas_xerbla(2, "cblas_sgemv","Illegal TransA setting, %d\n", TransA); - } - sgemv_(&TA, &M, &N, &alpha, A, &lda, X, &incX, &beta, Y, &incY); - } - else if (order == CblasRowMajor) - { - if (TransA == CblasNoTrans) TA = 'T'; - else if (TransA == CblasTrans) TA = 'N'; - else if (TransA == CblasConjTrans) TA = 'N'; - else - { - cblas_xerbla(2, "cblas_sgemv", "Illegal TransA setting, %d\n", TransA); - return; - } - sgemv_(&TA, &N, &M, &alpha, A, &lda, X, &incX, &beta, Y, &incY); - } - else - cblas_xerbla(1, "cblas_sgemv", "Illegal Order setting, %d\n", order); -} diff --git a/numpy/_globals.py b/numpy/_globals.py index 9a7b458f1239..c888747258c7 100644 --- a/numpy/_globals.py +++ b/numpy/_globals.py @@ -15,11 +15,11 @@ def foo(arg=np._NoValue): motivated this module. """ -from __future__ import division, absolute_import, print_function - +import enum __ALL__ = [ - 'ModuleDeprecationWarning', 'VisibleDeprecationWarning', '_NoValue' + 'ModuleDeprecationWarning', 'VisibleDeprecationWarning', + '_NoValue', '_CopyMode' ] @@ -39,7 +39,9 @@ class ModuleDeprecationWarning(DeprecationWarning): nose tester will let pass without making tests fail. """ - pass + + +ModuleDeprecationWarning.__module__ = 'numpy' class VisibleDeprecationWarning(UserWarning): @@ -50,20 +52,35 @@ class VisibleDeprecationWarning(UserWarning): the usage is most likely a user bug. """ - pass -class _NoValueType(object): + +VisibleDeprecationWarning.__module__ = 'numpy' + + +class _NoValueType: """Special keyword value. The instance of this class may be used as the default value assigned to a - deprecated keyword in order to check if it has been given a user defined - value. + keyword if no other obvious default (e.g., `None`) is suitable, + + Common reasons for using this keyword are: + + - A new keyword is added to a function, and that function forwards its + inputs to another function or method which can be defined outside of + NumPy. For example, ``np.std(x)`` calls ``x.std``, so when a ``keepdims`` + keyword was added that could only be forwarded if the user explicitly + specified ``keepdims``; downstream array libraries may not have added + the same keyword, so adding ``x.std(..., keepdims=keepdims)`` + unconditionally could have broken previously working code. + - A keyword is being deprecated, and a deprecation warning must only be + emitted when the keyword is used. + """ __instance = None def __new__(cls): # ensure that only one instance exists if not cls.__instance: - cls.__instance = super(_NoValueType, cls).__new__(cls) + cls.__instance = super().__new__(cls) return cls.__instance # needed for python 2 to preserve identity through a pickle @@ -73,4 +90,40 @@ def __reduce__(self): def __repr__(self): return "<no value>" + _NoValue = _NoValueType() + + +class _CopyMode(enum.Enum): + """ + An enumeration for the copy modes supported + by numpy.copy() and numpy.array(). The following three modes are supported, + + - ALWAYS: This means that a deep copy of the input + array will always be taken. + - IF_NEEDED: This means that a deep copy of the input + array will be taken only if necessary. + - NEVER: This means that the deep copy will never be taken. + If a copy cannot be avoided then a `ValueError` will be + raised. + + Note that the buffer-protocol could in theory do copies. NumPy currently + assumes an object exporting the buffer protocol will never do this. + """ + + ALWAYS = True + IF_NEEDED = False + NEVER = 2 + + def __bool__(self): + # For backwards compatiblity + if self == _CopyMode.ALWAYS: + return True + + if self == _CopyMode.IF_NEEDED: + return False + + raise ValueError(f"{self} is neither True nor False.") + + +_CopyMode.__module__ = 'numpy' diff --git a/numpy/_import_tools.py b/numpy/_import_tools.py deleted file mode 100644 index cb8bc477c175..000000000000 --- a/numpy/_import_tools.py +++ /dev/null @@ -1,351 +0,0 @@ -from __future__ import division, absolute_import, print_function - -import os -import sys -import warnings - -__all__ = ['PackageLoader'] - -class PackageLoader(object): - def __init__(self, verbose=False, infunc=False): - """ Manages loading packages. - """ - - if infunc: - _level = 2 - else: - _level = 1 - self.parent_frame = frame = sys._getframe(_level) - self.parent_name = eval('__name__', frame.f_globals, frame.f_locals) - parent_path = eval('__path__', frame.f_globals, frame.f_locals) - if isinstance(parent_path, str): - parent_path = [parent_path] - self.parent_path = parent_path - if '__all__' not in frame.f_locals: - exec('__all__ = []', frame.f_globals, frame.f_locals) - self.parent_export_names = eval('__all__', frame.f_globals, frame.f_locals) - - self.info_modules = {} - self.imported_packages = [] - self.verbose = None - - def _get_info_files(self, package_dir, parent_path, parent_package=None): - """ Return list of (package name,info.py file) from parent_path subdirectories. - """ - from glob import glob - files = glob(os.path.join(parent_path, package_dir, 'info.py')) - for info_file in glob(os.path.join(parent_path, package_dir, 'info.pyc')): - if info_file[:-1] not in files: - files.append(info_file) - info_files = [] - for info_file in files: - package_name = os.path.dirname(info_file[len(parent_path)+1:])\ - .replace(os.sep, '.') - if parent_package: - package_name = parent_package + '.' + package_name - info_files.append((package_name, info_file)) - info_files.extend(self._get_info_files('*', - os.path.dirname(info_file), - package_name)) - return info_files - - def _init_info_modules(self, packages=None): - """Initialize info_modules = {<package_name>: <package info.py module>}. - """ - from numpy.compat import npy_load_module - info_files = [] - info_modules = self.info_modules - - if packages is None: - for path in self.parent_path: - info_files.extend(self._get_info_files('*', path)) - else: - for package_name in packages: - package_dir = os.path.join(*package_name.split('.')) - for path in self.parent_path: - names_files = self._get_info_files(package_dir, path) - if names_files: - info_files.extend(names_files) - break - else: - try: - exec('import %s.info as info' % (package_name)) - info_modules[package_name] = info - except ImportError as msg: - self.warn('No scipy-style subpackage %r found in %s. '\ - 'Ignoring: %s'\ - % (package_name, ':'.join(self.parent_path), msg)) - - for package_name, info_file in info_files: - if package_name in info_modules: - continue - fullname = self.parent_name +'.'+ package_name - if info_file[-1]=='c': - filedescriptor = ('.pyc', 'rb', 2) - else: - filedescriptor = ('.py', 'U', 1) - - try: - info_module = npy_load_module(fullname + '.info', - info_file, - filedescriptor) - except Exception as msg: - self.error(msg) - info_module = None - - if info_module is None or getattr(info_module, 'ignore', False): - info_modules.pop(package_name, None) - else: - self._init_info_modules(getattr(info_module, 'depends', [])) - info_modules[package_name] = info_module - - return - - def _get_sorted_names(self): - """ Return package names sorted in the order as they should be - imported due to dependence relations between packages. - """ - - depend_dict = {} - for name, info_module in self.info_modules.items(): - depend_dict[name] = getattr(info_module, 'depends', []) - package_names = [] - - for name in list(depend_dict.keys()): - if not depend_dict[name]: - package_names.append(name) - del depend_dict[name] - - while depend_dict: - for name, lst in list(depend_dict.items()): - new_lst = [n for n in lst if n in depend_dict] - if not new_lst: - package_names.append(name) - del depend_dict[name] - else: - depend_dict[name] = new_lst - - return package_names - - def __call__(self,*packages, **options): - """Load one or more packages into parent package top-level namespace. - - This function is intended to shorten the need to import many - subpackages, say of scipy, constantly with statements such as - - import scipy.linalg, scipy.fftpack, scipy.etc... - - Instead, you can say: - - import scipy - scipy.pkgload('linalg','fftpack',...) - - or - - scipy.pkgload() - - to load all of them in one call. - - If a name which doesn't exist in scipy's namespace is - given, a warning is shown. - - Parameters - ---------- - *packages : arg-tuple - the names (one or more strings) of all the modules one - wishes to load into the top-level namespace. - verbose= : integer - verbosity level [default: -1]. - verbose=-1 will suspend also warnings. - force= : bool - when True, force reloading loaded packages [default: False]. - postpone= : bool - when True, don't load packages [default: False] - - """ - # 2014-10-29, 1.10 - warnings.warn('pkgload and PackageLoader are obsolete ' - 'and will be removed in a future version of numpy', - DeprecationWarning, stacklevel=2) - frame = self.parent_frame - self.info_modules = {} - if options.get('force', False): - self.imported_packages = [] - self.verbose = verbose = options.get('verbose', -1) - postpone = options.get('postpone', None) - self._init_info_modules(packages or None) - - self.log('Imports to %r namespace\n----------------------------'\ - % self.parent_name) - - for package_name in self._get_sorted_names(): - if package_name in self.imported_packages: - continue - info_module = self.info_modules[package_name] - global_symbols = getattr(info_module, 'global_symbols', []) - postpone_import = getattr(info_module, 'postpone_import', False) - if (postpone and not global_symbols) \ - or (postpone_import and postpone is not None): - continue - - old_object = frame.f_locals.get(package_name, None) - - cmdstr = 'import '+package_name - if self._execcmd(cmdstr): - continue - self.imported_packages.append(package_name) - - if verbose!=-1: - new_object = frame.f_locals.get(package_name) - if old_object is not None and old_object is not new_object: - self.warn('Overwriting %s=%s (was %s)' \ - % (package_name, self._obj2repr(new_object), - self._obj2repr(old_object))) - - if '.' not in package_name: - self.parent_export_names.append(package_name) - - for symbol in global_symbols: - if symbol=='*': - symbols = eval('getattr(%s,"__all__",None)'\ - % (package_name), - frame.f_globals, frame.f_locals) - if symbols is None: - symbols = eval('dir(%s)' % (package_name), - frame.f_globals, frame.f_locals) - symbols = [s for s in symbols if not s.startswith('_')] - else: - symbols = [symbol] - - if verbose!=-1: - old_objects = {} - for s in symbols: - if s in frame.f_locals: - old_objects[s] = frame.f_locals[s] - - cmdstr = 'from '+package_name+' import '+symbol - if self._execcmd(cmdstr): - continue - - if verbose!=-1: - for s, old_object in old_objects.items(): - new_object = frame.f_locals[s] - if new_object is not old_object: - self.warn('Overwriting %s=%s (was %s)' \ - % (s, self._obj2repr(new_object), - self._obj2repr(old_object))) - - if symbol=='*': - self.parent_export_names.extend(symbols) - else: - self.parent_export_names.append(symbol) - - return - - def _execcmd(self, cmdstr): - """ Execute command in parent_frame.""" - frame = self.parent_frame - try: - exec (cmdstr, frame.f_globals, frame.f_locals) - except Exception as msg: - self.error('%s -> failed: %s' % (cmdstr, msg)) - return True - else: - self.log('%s -> success' % (cmdstr)) - return - - def _obj2repr(self, obj): - """ Return repr(obj) with""" - module = getattr(obj, '__module__', None) - file = getattr(obj, '__file__', None) - if module is not None: - return repr(obj) + ' from ' + module - if file is not None: - return repr(obj) + ' from ' + file - return repr(obj) - - def log(self, mess): - if self.verbose>1: - print(str(mess), file=sys.stderr) - def warn(self, mess): - if self.verbose>=0: - print(str(mess), file=sys.stderr) - def error(self, mess): - if self.verbose!=-1: - print(str(mess), file=sys.stderr) - - def _get_doc_title(self, info_module): - """ Get the title from a package info.py file. - """ - title = getattr(info_module, '__doc_title__', None) - if title is not None: - return title - title = getattr(info_module, '__doc__', None) - if title is not None: - title = title.lstrip().split('\n', 1)[0] - return title - return '* Not Available *' - - def _format_titles(self,titles,colsep='---'): - display_window_width = 70 # How to determine the correct value in runtime?? - lengths = [len(name)-name.find('.')-1 for (name, title) in titles]+[0] - max_length = max(lengths) - lines = [] - for (name, title) in titles: - name = name[name.find('.')+1:] - w = max_length - len(name) - words = title.split() - line = '%s%s %s' % (name, w*' ', colsep) - tab = len(line) * ' ' - while words: - word = words.pop(0) - if len(line)+len(word)>display_window_width: - lines.append(line) - line = tab - line += ' ' + word - lines.append(line) - return '\n'.join(lines) - - def get_pkgdocs(self): - """ Return documentation summary of subpackages. - """ - import sys - self.info_modules = {} - self._init_info_modules(None) - - titles = [] - symbols = [] - for package_name, info_module in self.info_modules.items(): - global_symbols = getattr(info_module, 'global_symbols', []) - fullname = self.parent_name +'.'+ package_name - note = '' - if fullname not in sys.modules: - note = ' [*]' - titles.append((fullname, self._get_doc_title(info_module) + note)) - if global_symbols: - symbols.append((package_name, ', '.join(global_symbols))) - - retstr = self._format_titles(titles) +\ - '\n [*] - using a package requires explicit import (see pkgload)' - - - if symbols: - retstr += """\n\nGlobal symbols from subpackages"""\ - """\n-------------------------------\n""" +\ - self._format_titles(symbols, '-->') - - return retstr - -class PackageLoaderDebug(PackageLoader): - def _execcmd(self, cmdstr): - """ Execute command in parent_frame.""" - frame = self.parent_frame - print('Executing', repr(cmdstr), '...', end=' ') - sys.stdout.flush() - exec (cmdstr, frame.f_globals, frame.f_locals) - print('ok') - sys.stdout.flush() - return - -if int(os.environ.get('NUMPY_IMPORT_DEBUG', '0')): - PackageLoader = PackageLoaderDebug diff --git a/numpy/testing/_private/pytesttester.py b/numpy/_pytesttester.py similarity index 69% rename from numpy/testing/_private/pytesttester.py rename to numpy/_pytesttester.py index 8c73fafa432b..8decb9dd79a1 100644 --- a/numpy/testing/_private/pytesttester.py +++ b/numpy/_pytesttester.py @@ -5,8 +5,8 @@ boiler plate for doing that is to put the following in the module ``__init__.py`` file:: - from numpy.testing import PytestTester - test = PytestTester(__name__).test + from numpy._pytesttester import PytestTester + test = PytestTester(__name__) del PytestTester @@ -15,7 +15,7 @@ whether or not that file is found as follows: * ``pytest.ini`` is present (develop mode) - All warnings except those explicily filtered out are raised as error. + All warnings except those explicitly filtered out are raised as error. * ``pytest.ini`` is absent (release mode) DeprecationWarnings and PendingDeprecationWarnings are ignored, other warnings are passed through. @@ -23,9 +23,10 @@ In practice, tests run from the numpy repo are run in develop mode. That includes the standard ``python runtests.py`` invocation. +This module is imported by every numpy subpackage, so lies at the top level to +simplify circular import issues. For the same reason, it contains no numpy +imports at module scope, instead importing numpy within function calls. """ -from __future__ import division, absolute_import, print_function - import sys import os @@ -39,16 +40,18 @@ def _show_numpy_info(): print("NumPy version %s" % np.__version__) relaxed_strides = np.ones((10, 1), order="C").flags.f_contiguous print("NumPy relaxed strides checking option:", relaxed_strides) + info = np.lib.utils._opt_info() + print("NumPy CPU features: ", (info if info else 'nothing enabled')) + -class PytestTester(object): +class PytestTester: """ Pytest test runner. - This class is made available in ``numpy.testing``, and a test function - is typically added to a package's __init__.py like so:: + A test function is typically added to a package's __init__.py like so:: - from numpy.testing import PytestTester + from numpy._pytesttester import PytestTester test = PytestTester(__name__).test del PytestTester @@ -65,6 +68,12 @@ class PytestTester(object): module_name : module name The name of the module to test. + Notes + ----- + Unlike the previous ``nose``-based implementation, this class is not + publicly exposed as it performs some ``numpy``-specific warning + suppression. + """ def __init__(self, module_name): self.module_name = module_name @@ -102,36 +111,23 @@ def __call__(self, label='fast', verbose=1, extra_argv=None, Notes ----- - Each NumPy module exposes `test` in its namespace to run all tests for it. - For example, to run all tests for numpy.lib: + Each NumPy module exposes `test` in its namespace to run all tests for + it. For example, to run all tests for numpy.lib: >>> np.lib.test() #doctest: +SKIP Examples -------- >>> result = np.lib.test() #doctest: +SKIP - Running unit tests for numpy.lib ... - Ran 976 tests in 3.933s - - OK - - >>> result.errors #doctest: +SKIP - [] - >>> result.knownfail #doctest: +SKIP - [] + 1023 passed, 2 skipped, 6 deselected, 1 xfailed in 10.39 seconds + >>> result + True """ import pytest import warnings - #FIXME This is no longer needed? Assume it was for use in tests. - # cap verbosity at 3, which is equivalent to the pytest '-vv' option - #from . import utils - #verbose = min(int(verbose), 3) - #utils.verbose = verbose - # - module = sys.modules[self.module_name] module_path = os.path.abspath(module.__path__[0]) @@ -141,13 +137,20 @@ def __call__(self, label='fast', verbose=1, extra_argv=None, # offset verbosity. The "-q" cancels a "-v". pytest_args += ["-q"] - # Filter out distutils cpu warnings (could be localized to - # distutils tests). ASV has problems with top level import, - # so fetch module for suppression here. with warnings.catch_warnings(): warnings.simplefilter("always") + # Filter out distutils cpu warnings (could be localized to + # distutils tests). ASV has problems with top level import, + # so fetch module for suppression here. from numpy.distutils import cpuinfo + with warnings.catch_warnings(record=True): + # Ignore the warning from importing the array_api submodule. This + # warning is done on import, so it would break pytest collection, + # but importing it early here prevents the warning from being + # issued when it imported again. + import numpy.array_api + # Filter out annoying import messages. Want these in both develop and # release mode. pytest_args += [ @@ -157,6 +160,12 @@ def __call__(self, label='fast', verbose=1, extra_argv=None, "-W ignore::UserWarning:cpuinfo", ] + # When testing matrices, ignore their PendingDeprecationWarnings + pytest_args += [ + "-W ignore:the matrix subclass is not", + "-W ignore:Importing from numpy.matlib is", + ] + if doctests: raise ValueError("Doctests not supported") @@ -170,7 +179,13 @@ def __call__(self, label='fast', verbose=1, extra_argv=None, pytest_args += ["--cov=" + module_path] if label == "fast": - pytest_args += ["-m", "not slow"] + # not importing at the top level to avoid circular import of module + from numpy.testing import IS_PYPY + if IS_PYPY: + pytest_args += ["-m", "not slow and not slow_pypy"] + else: + pytest_args += ["-m", "not slow"] + elif label != "full": pytest_args += ["-m", label] @@ -182,7 +197,6 @@ def __call__(self, label='fast', verbose=1, extra_argv=None, pytest_args += ["--pyargs"] + list(tests) - # run tests. _show_numpy_info() diff --git a/numpy/_pytesttester.pyi b/numpy/_pytesttester.pyi new file mode 100644 index 000000000000..0be64b3f7488 --- /dev/null +++ b/numpy/_pytesttester.pyi @@ -0,0 +1,17 @@ +from typing import List, Iterable, Literal as L + +__all__: List[str] + +class PytestTester: + module_name: str + def __init__(self, module_name: str) -> None: ... + def __call__( + self, + label: L["fast", "full"] = ..., + verbose: int = ..., + extra_argv: None | Iterable[str] = ..., + doctests: L[False] = ..., + coverage: bool = ..., + durations: int = ..., + tests: None | Iterable[str] = ..., + ) -> bool: ... diff --git a/numpy/_version.py b/numpy/_version.py new file mode 100644 index 000000000000..6d2c55669798 --- /dev/null +++ b/numpy/_version.py @@ -0,0 +1,523 @@ +# This file helps to compute a version number in source trees obtained from +# git-archive tarball (such as those provided by githubs download-from-tag +# feature). Distribution tarballs (built by setup.py sdist) and build +# directories (produced by setup.py build) will contain a much shorter file +# that just contains the computed version number. + +# This file is released into the public domain. Generated by +# versioneer-0.19 (https://github.com/python-versioneer/python-versioneer) + +"""Git implementation of _version.py.""" + +import errno +import os +import re +import subprocess +import sys + + +def get_keywords(): + """Get the keywords needed to look up the version information.""" + # these strings will be replaced by git during git-archive. + # setup.py/versioneer.py will grep for the variable names, so they must + # each be defined on a line of their own. _version.py will just call + # get_keywords(). + git_refnames = "$Format:%d$" + git_full = "$Format:%H$" + git_date = "$Format:%ci$" + keywords = {"refnames": git_refnames, "full": git_full, "date": git_date} + return keywords + + +class VersioneerConfig: + """Container for Versioneer configuration parameters.""" + + +def get_config(): + """Create, populate and return the VersioneerConfig() object.""" + # these strings are filled in when 'setup.py versioneer' creates + # _version.py + cfg = VersioneerConfig() + cfg.VCS = "git" + cfg.style = "pep440" + cfg.tag_prefix = "v" + cfg.parentdir_prefix = "numpy-" + cfg.versionfile_source = "numpy/_version.py" + cfg.verbose = False + return cfg + + +class NotThisMethod(Exception): + """Exception raised if a method is not valid for the current scenario.""" + + +LONG_VERSION_PY = {} +HANDLERS = {} + + +def register_vcs_handler(vcs, method): # decorator + """Create decorator to mark a method as the handler of a VCS.""" + def decorate(f): + """Store f in HANDLERS[vcs][method].""" + if vcs not in HANDLERS: + HANDLERS[vcs] = {} + HANDLERS[vcs][method] = f + return f + return decorate + + +def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, + env=None): + """Call the given command(s).""" + assert isinstance(commands, list) + p = None + for c in commands: + try: + dispcmd = str([c] + args) + # remember shell=False, so use git.cmd on windows, not just git + p = subprocess.Popen([c] + args, cwd=cwd, env=env, + stdout=subprocess.PIPE, + stderr=(subprocess.PIPE if hide_stderr + else None)) + break + except EnvironmentError: + e = sys.exc_info()[1] + if e.errno == errno.ENOENT: + continue + if verbose: + print("unable to run %s" % dispcmd) + print(e) + return None, None + else: + if verbose: + print("unable to find command, tried %s" % (commands,)) + return None, None + stdout = p.communicate()[0].strip().decode() + if p.returncode != 0: + if verbose: + print("unable to run %s (error)" % dispcmd) + print("stdout was %s" % stdout) + return None, p.returncode + return stdout, p.returncode + + +def versions_from_parentdir(parentdir_prefix, root, verbose): + """Try to determine the version from the parent directory name. + + Source tarballs conventionally unpack into a directory that includes both + the project name and a version string. We will also support searching up + two directory levels for an appropriately named parent directory + """ + rootdirs = [] + + for i in range(3): + dirname = os.path.basename(root) + if dirname.startswith(parentdir_prefix): + return {"version": dirname[len(parentdir_prefix):], + "full-revisionid": None, + "dirty": False, "error": None, "date": None} + else: + rootdirs.append(root) + root = os.path.dirname(root) # up a level + + if verbose: + print("Tried directories %s but none started with prefix %s" % + (str(rootdirs), parentdir_prefix)) + raise NotThisMethod("rootdir doesn't start with parentdir_prefix") + + +@register_vcs_handler("git", "get_keywords") +def git_get_keywords(versionfile_abs): + """Extract version information from the given file.""" + # the code embedded in _version.py can just fetch the value of these + # keywords. When used from setup.py, we don't want to import _version.py, + # so we do it with a regexp instead. This function is not used from + # _version.py. + keywords = {} + try: + with open(versionfile_abs, "r") as f: + for line in f.readlines(): + if line.strip().startswith("git_refnames ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["refnames"] = mo.group(1) + if line.strip().startswith("git_full ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["full"] = mo.group(1) + if line.strip().startswith("git_date ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["date"] = mo.group(1) + except EnvironmentError: + pass + return keywords + + +@register_vcs_handler("git", "keywords") +def git_versions_from_keywords(keywords, tag_prefix, verbose): + """Get version information from git keywords.""" + if not keywords: + raise NotThisMethod("no keywords at all, weird") + date = keywords.get("date") + if date is not None: + # Use only the last line. Previous lines may contain GPG signature + # information. + date = date.splitlines()[-1] + + # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant + # datestamp. However we prefer "%ci" (which expands to an "ISO-8601 + # -like" string, which we must then edit to make compliant), because + # it's been around since git-1.5.3, and it's too difficult to + # discover which version we're using, or to work around using an + # older one. + date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) + refnames = keywords["refnames"].strip() + if refnames.startswith("$Format"): + if verbose: + print("keywords are unexpanded, not using") + raise NotThisMethod("unexpanded keywords, not a git-archive tarball") + refs = set([r.strip() for r in refnames.strip("()").split(",")]) + # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of + # just "foo-1.0". If we see a "tag: " prefix, prefer those. + TAG = "tag: " + tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)]) + if not tags: + # Either we're using git < 1.8.3, or there really are no tags. We use + # a heuristic: assume all version tags have a digit. The old git %d + # expansion behaves like git log --decorate=short and strips out the + # refs/heads/ and refs/tags/ prefixes that would let us distinguish + # between branches and tags. By ignoring refnames without digits, we + # filter out many common branch names like "release" and + # "stabilization", as well as "HEAD" and "main". + tags = set([r for r in refs if re.search(r'\d', r)]) + if verbose: + print("discarding '%s', no digits" % ",".join(refs - tags)) + if verbose: + print("likely tags: %s" % ",".join(sorted(tags))) + for ref in sorted(tags): + # sorting will prefer e.g. "2.0" over "2.0rc1" + if ref.startswith(tag_prefix): + r = ref[len(tag_prefix):] + if verbose: + print("picking %s" % r) + return {"version": r, + "full-revisionid": keywords["full"].strip(), + "dirty": False, "error": None, + "date": date} + # no suitable tags, so version is "0+unknown", but full hex is still there + if verbose: + print("no suitable tags, using unknown + full revision id") + return {"version": "0+unknown", + "full-revisionid": keywords["full"].strip(), + "dirty": False, "error": "no suitable tags", "date": None} + + +@register_vcs_handler("git", "pieces_from_vcs") +def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): + """Get version from 'git describe' in the root of the source tree. + + This only gets called if the git-archive 'subst' keywords were *not* + expanded, and _version.py hasn't already been rewritten with a short + version string, meaning we're inside a checked out source tree. + """ + GITS = ["git"] + if sys.platform == "win32": + GITS = ["git.cmd", "git.exe"] + + out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root, + hide_stderr=True) + if rc != 0: + if verbose: + print("Directory %s not under git control" % root) + raise NotThisMethod("'git rev-parse --git-dir' returned error") + + # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] + # if there isn't one, this yields HEX[-dirty] (no NUM) + describe_out, rc = run_command(GITS, ["describe", "--tags", "--dirty=", + "--always", "--long", + "--match", "%s*" % tag_prefix], + cwd=root) + # --long was added in git-1.5.5 + if describe_out is None: + raise NotThisMethod("'git describe' failed") + describe_out = describe_out.strip() + full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) + if full_out is None: + raise NotThisMethod("'git rev-parse' failed") + full_out = full_out.strip() + + pieces = {} + pieces["long"] = full_out + pieces["short"] = full_out[:7] # maybe improved later + pieces["error"] = None + + # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] + # TAG might have hyphens. + git_describe = describe_out + + # look for -dirty suffix + dirty = git_describe.endswith("-dirty") + pieces["dirty"] = dirty + if dirty: + git_describe = git_describe[:git_describe.rindex("-dirty")] + + # now we have TAG-NUM-gHEX or HEX + + if "-" in git_describe: + # TAG-NUM-gHEX + mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe) + if not mo: + # unparseable. Maybe git-describe is misbehaving? + pieces["error"] = ("unable to parse git-describe output: '%s'" + % describe_out) + return pieces + + # tag + full_tag = mo.group(1) + if not full_tag.startswith(tag_prefix): + if verbose: + fmt = "tag '%s' doesn't start with prefix '%s'" + print(fmt % (full_tag, tag_prefix)) + pieces["error"] = ("tag '%s' doesn't start with prefix '%s'" + % (full_tag, tag_prefix)) + return pieces + pieces["closest-tag"] = full_tag[len(tag_prefix):] + + # distance: number of commits since tag + pieces["distance"] = int(mo.group(2)) + + # commit: short hex revision ID + pieces["short"] = mo.group(3) + + else: + # HEX: no tags + pieces["closest-tag"] = None + count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"], + cwd=root) + pieces["distance"] = int(count_out) # total number of commits + + # commit date: see ISO-8601 comment in git_versions_from_keywords() + date = run_command(GITS, ["show", "-s", "--format=%ci", "HEAD"], + cwd=root)[0].strip() + # Use only the last line. Previous lines may contain GPG signature + # information. + date = date.splitlines()[-1] + pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) + + return pieces + + +def plus_or_dot(pieces): + """Return a + if we don't already have one, else return a .""" + if "+" in pieces.get("closest-tag", ""): + return "." + return "+" + + +def render_pep440(pieces): + """Build up version string, with post-release "local version identifier". + + Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you + get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty + + Exceptions: + 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + rendered += plus_or_dot(pieces) + rendered += "%d.g%s" % (pieces["distance"], pieces["short"]) + if pieces["dirty"]: + rendered += ".dirty" + else: + # exception #1 + rendered = "0+untagged.%d.g%s" % (pieces["distance"], + pieces["short"]) + if pieces["dirty"]: + rendered += ".dirty" + return rendered + + +def render_pep440_pre(pieces): + """TAG[.post0.devDISTANCE] -- No -dirty. + + Exceptions: + 1: no tags. 0.post0.devDISTANCE + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"]: + rendered += ".post0.dev%d" % pieces["distance"] + else: + # exception #1 + rendered = "0.post0.dev%d" % pieces["distance"] + return rendered + + +def render_pep440_post(pieces): + """TAG[.postDISTANCE[.dev0]+gHEX] . + + The ".dev0" means dirty. Note that .dev0 sorts backwards + (a dirty tree will appear "older" than the corresponding clean one), + but you shouldn't be releasing software with -dirty anyways. + + Exceptions: + 1: no tags. 0.postDISTANCE[.dev0] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + rendered += ".post%d" % pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + rendered += plus_or_dot(pieces) + rendered += "g%s" % pieces["short"] + else: + # exception #1 + rendered = "0.post%d" % pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + rendered += "+g%s" % pieces["short"] + return rendered + + +def render_pep440_old(pieces): + """TAG[.postDISTANCE[.dev0]] . + + The ".dev0" means dirty. + + Exceptions: + 1: no tags. 0.postDISTANCE[.dev0] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + rendered += ".post%d" % pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + else: + # exception #1 + rendered = "0.post%d" % pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + return rendered + + +def render_git_describe(pieces): + """TAG[-DISTANCE-gHEX][-dirty]. + + Like 'git describe --tags --dirty --always'. + + Exceptions: + 1: no tags. HEX[-dirty] (note: no 'g' prefix) + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"]: + rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) + else: + # exception #1 + rendered = pieces["short"] + if pieces["dirty"]: + rendered += "-dirty" + return rendered + + +def render_git_describe_long(pieces): + """TAG-DISTANCE-gHEX[-dirty]. + + Like 'git describe --tags --dirty --always -long'. + The distance/hash is unconditional. + + Exceptions: + 1: no tags. HEX[-dirty] (note: no 'g' prefix) + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) + else: + # exception #1 + rendered = pieces["short"] + if pieces["dirty"]: + rendered += "-dirty" + return rendered + + +def render(pieces, style): + """Render the given version pieces into the requested style.""" + if pieces["error"]: + return {"version": "unknown", + "full-revisionid": pieces.get("long"), + "dirty": None, + "error": pieces["error"], + "date": None} + + if not style or style == "default": + style = "pep440" # the default + + if style == "pep440": + rendered = render_pep440(pieces) + elif style == "pep440-pre": + rendered = render_pep440_pre(pieces) + elif style == "pep440-post": + rendered = render_pep440_post(pieces) + elif style == "pep440-old": + rendered = render_pep440_old(pieces) + elif style == "git-describe": + rendered = render_git_describe(pieces) + elif style == "git-describe-long": + rendered = render_git_describe_long(pieces) + else: + raise ValueError("unknown style '%s'" % style) + + return {"version": rendered, "full-revisionid": pieces["long"], + "dirty": pieces["dirty"], "error": None, + "date": pieces.get("date")} + + +def get_versions(): + """Get version information or return default if unable to do so.""" + # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have + # __file__, we can work backwards from there to the root. Some + # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which + # case we can only use expanded keywords. + + cfg = get_config() + verbose = cfg.verbose + + try: + return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, + verbose) + except NotThisMethod: + pass + + try: + root = os.path.realpath(__file__) + # versionfile_source is the relative path from the top of the source + # tree (where the .git directory might live) to this file. Invert + # this to find the root from __file__. + for i in cfg.versionfile_source.split('/'): + root = os.path.dirname(root) + except NameError: + return {"version": "0+unknown", "full-revisionid": None, + "dirty": None, + "error": "unable to find root of source tree", + "date": None} + + try: + pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose) + return render(pieces, cfg.style) + except NotThisMethod: + pass + + try: + if cfg.parentdir_prefix: + return versions_from_parentdir(cfg.parentdir_prefix, root, verbose) + except NotThisMethod: + pass + + return {"version": "0+unknown", "full-revisionid": None, + "dirty": None, + "error": "unable to compute version", "date": None} diff --git a/numpy/array_api/__init__.py b/numpy/array_api/__init__.py new file mode 100644 index 000000000000..bbe2fdce26e3 --- /dev/null +++ b/numpy/array_api/__init__.py @@ -0,0 +1,375 @@ +""" +A NumPy sub-namespace that conforms to the Python array API standard. + +This submodule accompanies NEP 47, which proposes its inclusion in NumPy. It +is still considered experimental, and will issue a warning when imported. + +This is a proof-of-concept namespace that wraps the corresponding NumPy +functions to give a conforming implementation of the Python array API standard +(https://data-apis.github.io/array-api/latest/). The standard is currently in +an RFC phase and comments on it are both welcome and encouraged. Comments +should be made either at https://github.com/data-apis/array-api or at +https://github.com/data-apis/consortium-feedback/discussions. + +NumPy already follows the proposed spec for the most part, so this module +serves mostly as a thin wrapper around it. However, NumPy also implements a +lot of behavior that is not included in the spec, so this serves as a +restricted subset of the API. Only those functions that are part of the spec +are included in this namespace, and all functions are given with the exact +signature given in the spec, including the use of position-only arguments, and +omitting any extra keyword arguments implemented by NumPy but not part of the +spec. The behavior of some functions is also modified from the NumPy behavior +to conform to the standard. Note that the underlying array object itself is +wrapped in a wrapper Array() class, but is otherwise unchanged. This submodule +is implemented in pure Python with no C extensions. + +The array API spec is designed as a "minimal API subset" and explicitly allows +libraries to include behaviors not specified by it. But users of this module +that intend to write portable code should be aware that only those behaviors +that are listed in the spec are guaranteed to be implemented across libraries. +Consequently, the NumPy implementation was chosen to be both conforming and +minimal, so that users can use this implementation of the array API namespace +and be sure that behaviors that it defines will be available in conforming +namespaces from other libraries. + +A few notes about the current state of this submodule: + +- There is a test suite that tests modules against the array API standard at + https://github.com/data-apis/array-api-tests. The test suite is still a work + in progress, but the existing tests pass on this module, with a few + exceptions: + + - DLPack support (see https://github.com/data-apis/array-api/pull/106) is + not included here, as it requires a full implementation in NumPy proper + first. + + The test suite is not yet complete, and even the tests that exist are not + guaranteed to give a comprehensive coverage of the spec. Therefore, when + reviewing and using this submodule, you should refer to the standard + documents themselves. There are some tests in numpy.array_api.tests, but + they primarily focus on things that are not tested by the official array API + test suite. + +- There is a custom array object, numpy.array_api.Array, which is returned by + all functions in this module. All functions in the array API namespace + implicitly assume that they will only receive this object as input. The only + way to create instances of this object is to use one of the array creation + functions. It does not have a public constructor on the object itself. The + object is a small wrapper class around numpy.ndarray. The main purpose of it + is to restrict the namespace of the array object to only those dtypes and + only those methods that are required by the spec, as well as to limit/change + certain behavior that differs in the spec. In particular: + + - The array API namespace does not have scalar objects, only 0-D arrays. + Operations on Array that would create a scalar in NumPy create a 0-D + array. + + - Indexing: Only a subset of indices supported by NumPy are required by the + spec. The Array object restricts indexing to only allow those types of + indices that are required by the spec. See the docstring of the + numpy.array_api.Array._validate_indices helper function for more + information. + + - Type promotion: Some type promotion rules are different in the spec. In + particular, the spec does not have any value-based casting. The spec also + does not require cross-kind casting, like integer -> floating-point. Only + those promotions that are explicitly required by the array API + specification are allowed in this module. See NEP 47 for more info. + + - Functions do not automatically call asarray() on their input, and will not + work if the input type is not Array. The exception is array creation + functions, and Python operators on the Array object, which accept Python + scalars of the same type as the array dtype. + +- All functions include type annotations, corresponding to those given in the + spec (see _typing.py for definitions of some custom types). These do not + currently fully pass mypy due to some limitations in mypy. + +- Dtype objects are just the NumPy dtype objects, e.g., float64 = + np.dtype('float64'). The spec does not require any behavior on these dtype + objects other than that they be accessible by name and be comparable by + equality, but it was considered too much extra complexity to create custom + objects to represent dtypes. + +- All places where the implementations in this submodule are known to deviate + from their corresponding functions in NumPy are marked with "# Note:" + comments. + +Still TODO in this module are: + +- DLPack support for numpy.ndarray is still in progress. See + https://github.com/numpy/numpy/pull/19083. + +- The copy=False keyword argument to asarray() is not yet implemented. This + requires support in numpy.asarray() first. + +- Some functions are not yet fully tested in the array API test suite, and may + require updates that are not yet known until the tests are written. + +- The spec is still in an RFC phase and may still have minor updates, which + will need to be reflected here. + +- Complex number support in array API spec is planned but not yet finalized, + as are the fft extension and certain linear algebra functions such as eig + that require complex dtypes. + +""" + +import warnings + +warnings.warn( + "The numpy.array_api submodule is still experimental. See NEP 47.", stacklevel=2 +) + +__all__ = [] + +from ._constants import e, inf, nan, pi + +__all__ += ["e", "inf", "nan", "pi"] + +from ._creation_functions import ( + asarray, + arange, + empty, + empty_like, + eye, + from_dlpack, + full, + full_like, + linspace, + meshgrid, + ones, + ones_like, + tril, + triu, + zeros, + zeros_like, +) + +__all__ += [ + "asarray", + "arange", + "empty", + "empty_like", + "eye", + "from_dlpack", + "full", + "full_like", + "linspace", + "meshgrid", + "ones", + "ones_like", + "tril", + "triu", + "zeros", + "zeros_like", +] + +from ._data_type_functions import ( + astype, + broadcast_arrays, + broadcast_to, + can_cast, + finfo, + iinfo, + result_type, +) + +__all__ += [ + "astype", + "broadcast_arrays", + "broadcast_to", + "can_cast", + "finfo", + "iinfo", + "result_type", +] + +from ._dtypes import ( + int8, + int16, + int32, + int64, + uint8, + uint16, + uint32, + uint64, + float32, + float64, + bool, +) + +__all__ += [ + "int8", + "int16", + "int32", + "int64", + "uint8", + "uint16", + "uint32", + "uint64", + "float32", + "float64", + "bool", +] + +from ._elementwise_functions import ( + abs, + acos, + acosh, + add, + asin, + asinh, + atan, + atan2, + atanh, + bitwise_and, + bitwise_left_shift, + bitwise_invert, + bitwise_or, + bitwise_right_shift, + bitwise_xor, + ceil, + cos, + cosh, + divide, + equal, + exp, + expm1, + floor, + floor_divide, + greater, + greater_equal, + isfinite, + isinf, + isnan, + less, + less_equal, + log, + log1p, + log2, + log10, + logaddexp, + logical_and, + logical_not, + logical_or, + logical_xor, + multiply, + negative, + not_equal, + positive, + pow, + remainder, + round, + sign, + sin, + sinh, + square, + sqrt, + subtract, + tan, + tanh, + trunc, +) + +__all__ += [ + "abs", + "acos", + "acosh", + "add", + "asin", + "asinh", + "atan", + "atan2", + "atanh", + "bitwise_and", + "bitwise_left_shift", + "bitwise_invert", + "bitwise_or", + "bitwise_right_shift", + "bitwise_xor", + "ceil", + "cos", + "cosh", + "divide", + "equal", + "exp", + "expm1", + "floor", + "floor_divide", + "greater", + "greater_equal", + "isfinite", + "isinf", + "isnan", + "less", + "less_equal", + "log", + "log1p", + "log2", + "log10", + "logaddexp", + "logical_and", + "logical_not", + "logical_or", + "logical_xor", + "multiply", + "negative", + "not_equal", + "positive", + "pow", + "remainder", + "round", + "sign", + "sin", + "sinh", + "square", + "sqrt", + "subtract", + "tan", + "tanh", + "trunc", +] + +# linalg is an extension in the array API spec, which is a sub-namespace. Only +# a subset of functions in it are imported into the top-level namespace. +from . import linalg + +__all__ += ["linalg"] + +from .linalg import matmul, tensordot, matrix_transpose, vecdot + +__all__ += ["matmul", "tensordot", "matrix_transpose", "vecdot"] + +from ._manipulation_functions import ( + concat, + expand_dims, + flip, + permute_dims, + reshape, + roll, + squeeze, + stack, +) + +__all__ += ["concat", "expand_dims", "flip", "permute_dims", "reshape", "roll", "squeeze", "stack"] + +from ._searching_functions import argmax, argmin, nonzero, where + +__all__ += ["argmax", "argmin", "nonzero", "where"] + +from ._set_functions import unique_all, unique_counts, unique_inverse, unique_values + +__all__ += ["unique_all", "unique_counts", "unique_inverse", "unique_values"] + +from ._sorting_functions import argsort, sort + +__all__ += ["argsort", "sort"] + +from ._statistical_functions import max, mean, min, prod, std, sum, var + +__all__ += ["max", "mean", "min", "prod", "std", "sum", "var"] + +from ._utility_functions import all, any + +__all__ += ["all", "any"] diff --git a/numpy/array_api/_array_object.py b/numpy/array_api/_array_object.py new file mode 100644 index 000000000000..75baf34b07da --- /dev/null +++ b/numpy/array_api/_array_object.py @@ -0,0 +1,1087 @@ +""" +Wrapper class around the ndarray object for the array API standard. + +The array API standard defines some behaviors differently than ndarray, in +particular, type promotion rules are different (the standard has no +value-based casting). The standard also specifies a more limited subset of +array methods and functionalities than are implemented on ndarray. Since the +goal of the array_api namespace is to be a minimal implementation of the array +API standard, we need to define a separate wrapper class for the array_api +namespace. + +The standard compliant class is only a wrapper class. It is *not* a subclass +of ndarray. +""" + +from __future__ import annotations + +import operator +from enum import IntEnum +from ._creation_functions import asarray +from ._dtypes import ( + _all_dtypes, + _boolean_dtypes, + _integer_dtypes, + _integer_or_boolean_dtypes, + _floating_dtypes, + _numeric_dtypes, + _result_type, + _dtype_categories, +) + +from typing import TYPE_CHECKING, Optional, Tuple, Union, Any + +if TYPE_CHECKING: + from ._typing import Any, PyCapsule, Device, Dtype + import numpy.typing as npt + +import numpy as np + +from numpy import array_api + + +class Array: + """ + n-d array object for the array API namespace. + + See the docstring of :py:obj:`np.ndarray <numpy.ndarray>` for more + information. + + This is a wrapper around numpy.ndarray that restricts the usage to only + those things that are required by the array API namespace. Note, + attributes on this object that start with a single underscore are not part + of the API specification and should only be used internally. This object + should not be constructed directly. Rather, use one of the creation + functions, such as asarray(). + + """ + + # Use a custom constructor instead of __init__, as manually initializing + # this class is not supported API. + @classmethod + def _new(cls, x, /): + """ + This is a private method for initializing the array API Array + object. + + Functions outside of the array_api submodule should not use this + method. Use one of the creation functions instead, such as + ``asarray``. + + """ + obj = super().__new__(cls) + # Note: The spec does not have array scalars, only 0-D arrays. + if isinstance(x, np.generic): + # Convert the array scalar to a 0-D array + x = np.asarray(x) + if x.dtype not in _all_dtypes: + raise TypeError( + f"The array_api namespace does not support the dtype '{x.dtype}'" + ) + obj._array = x + return obj + + # Prevent Array() from working + def __new__(cls, *args, **kwargs): + raise TypeError( + "The array_api Array object should not be instantiated directly. Use an array creation function, such as asarray(), instead." + ) + + # These functions are not required by the spec, but are implemented for + # the sake of usability. + + def __str__(self: Array, /) -> str: + """ + Performs the operation __str__. + """ + return self._array.__str__().replace("array", "Array") + + def __repr__(self: Array, /) -> str: + """ + Performs the operation __repr__. + """ + suffix = f", dtype={self.dtype.name})" + if 0 in self.shape: + prefix = "empty(" + mid = str(self.shape) + else: + prefix = "Array(" + mid = np.array2string(self._array, separator=', ', prefix=prefix, suffix=suffix) + return prefix + mid + suffix + + # This function is not required by the spec, but we implement it here for + # convenience so that np.asarray(np.array_api.Array) will work. + def __array__(self, dtype: None | np.dtype[Any] = None) -> npt.NDArray[Any]: + """ + Warning: this method is NOT part of the array API spec. Implementers + of other libraries need not include it, and users should not assume it + will be present in other implementations. + + """ + return np.asarray(self._array, dtype=dtype) + + # These are various helper functions to make the array behavior match the + # spec in places where it either deviates from or is more strict than + # NumPy behavior + + def _check_allowed_dtypes(self, other, dtype_category, op): + """ + Helper function for operators to only allow specific input dtypes + + Use like + + other = self._check_allowed_dtypes(other, 'numeric', '__add__') + if other is NotImplemented: + return other + """ + + if self.dtype not in _dtype_categories[dtype_category]: + raise TypeError(f"Only {dtype_category} dtypes are allowed in {op}") + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) + elif isinstance(other, Array): + if other.dtype not in _dtype_categories[dtype_category]: + raise TypeError(f"Only {dtype_category} dtypes are allowed in {op}") + else: + return NotImplemented + + # This will raise TypeError for type combinations that are not allowed + # to promote in the spec (even if the NumPy array operator would + # promote them). + res_dtype = _result_type(self.dtype, other.dtype) + if op.startswith("__i"): + # Note: NumPy will allow in-place operators in some cases where + # the type promoted operator does not match the left-hand side + # operand. For example, + + # >>> a = np.array(1, dtype=np.int8) + # >>> a += np.array(1, dtype=np.int16) + + # The spec explicitly disallows this. + if res_dtype != self.dtype: + raise TypeError( + f"Cannot perform {op} with dtypes {self.dtype} and {other.dtype}" + ) + + return other + + # Helper function to match the type promotion rules in the spec + def _promote_scalar(self, scalar): + """ + Returns a promoted version of a Python scalar appropriate for use with + operations on self. + + This may raise an OverflowError in cases where the scalar is an + integer that is too large to fit in a NumPy integer dtype, or + TypeError when the scalar type is incompatible with the dtype of self. + """ + if isinstance(scalar, bool): + if self.dtype not in _boolean_dtypes: + raise TypeError( + "Python bool scalars can only be promoted with bool arrays" + ) + elif isinstance(scalar, int): + if self.dtype in _boolean_dtypes: + raise TypeError( + "Python int scalars cannot be promoted with bool arrays" + ) + elif isinstance(scalar, float): + if self.dtype not in _floating_dtypes: + raise TypeError( + "Python float scalars can only be promoted with floating-point arrays." + ) + else: + raise TypeError("'scalar' must be a Python scalar") + + # Note: the spec only specifies integer-dtype/int promotion + # behavior for integers within the bounds of the integer dtype. + # Outside of those bounds we use the default NumPy behavior (either + # cast or raise OverflowError). + return Array._new(np.array(scalar, self.dtype)) + + @staticmethod + def _normalize_two_args(x1, x2): + """ + Normalize inputs to two arg functions to fix type promotion rules + + NumPy deviates from the spec type promotion rules in cases where one + argument is 0-dimensional and the other is not. For example: + + >>> import numpy as np + >>> a = np.array([1.0], dtype=np.float32) + >>> b = np.array(1.0, dtype=np.float64) + >>> np.add(a, b) # The spec says this should be float64 + array([2.], dtype=float32) + + To fix this, we add a dimension to the 0-dimension array before passing it + through. This works because a dimension would be added anyway from + broadcasting, so the resulting shape is the same, but this prevents NumPy + from not promoting the dtype. + """ + # Another option would be to use signature=(x1.dtype, x2.dtype, None), + # but that only works for ufuncs, so we would have to call the ufuncs + # directly in the operator methods. One should also note that this + # sort of trick wouldn't work for functions like searchsorted, which + # don't do normal broadcasting, but there aren't any functions like + # that in the array API namespace. + if x1.ndim == 0 and x2.ndim != 0: + # The _array[None] workaround was chosen because it is relatively + # performant. broadcast_to(x1._array, x2.shape) is much slower. We + # could also manually type promote x2, but that is more complicated + # and about the same performance as this. + x1 = Array._new(x1._array[None]) + elif x2.ndim == 0 and x1.ndim != 0: + x2 = Array._new(x2._array[None]) + return (x1, x2) + + # Note: A large fraction of allowed indices are disallowed here (see the + # docstring below) + @staticmethod + def _validate_index(key, shape): + """ + Validate an index according to the array API. + + The array API specification only requires a subset of indices that are + supported by NumPy. This function will reject any index that is + allowed by NumPy but not required by the array API specification. We + always raise ``IndexError`` on such indices (the spec does not require + any specific behavior on them, but this makes the NumPy array API + namespace a minimal implementation of the spec). See + https://data-apis.org/array-api/latest/API_specification/indexing.html + for the full list of required indexing behavior + + This function either raises IndexError if the index ``key`` is + invalid, or a new key to be used in place of ``key`` in indexing. It + only raises ``IndexError`` on indices that are not already rejected by + NumPy, as NumPy will already raise the appropriate error on such + indices. ``shape`` may be None, in which case, only cases that are + independent of the array shape are checked. + + The following cases are allowed by NumPy, but not specified by the array + API specification: + + - Indices to not include an implicit ellipsis at the end. That is, + every axis of an array must be explicitly indexed or an ellipsis + included. + + - The start and stop of a slice may not be out of bounds. In + particular, for a slice ``i:j:k`` on an axis of size ``n``, only the + following are allowed: + + - ``i`` or ``j`` omitted (``None``). + - ``-n <= i <= max(0, n - 1)``. + - For ``k > 0`` or ``k`` omitted (``None``), ``-n <= j <= n``. + - For ``k < 0``, ``-n - 1 <= j <= max(0, n - 1)``. + + - Boolean array indices are not allowed as part of a larger tuple + index. + + - Integer array indices are not allowed (with the exception of 0-D + arrays, which are treated the same as scalars). + + Additionally, it should be noted that indices that would return a + scalar in NumPy will return a 0-D array. Array scalars are not allowed + in the specification, only 0-D arrays. This is done in the + ``Array._new`` constructor, not this function. + + """ + if isinstance(key, slice): + if shape is None: + return key + if shape == (): + return key + if len(shape) > 1: + raise IndexError( + "Multidimensional arrays must include an index for every axis or use an ellipsis" + ) + size = shape[0] + # Ensure invalid slice entries are passed through. + if key.start is not None: + try: + operator.index(key.start) + except TypeError: + return key + if not (-size <= key.start <= size): + raise IndexError( + "Slices with out-of-bounds start are not allowed in the array API namespace" + ) + if key.stop is not None: + try: + operator.index(key.stop) + except TypeError: + return key + step = 1 if key.step is None else key.step + if (step > 0 and not (-size <= key.stop <= size) + or step < 0 and not (-size - 1 <= key.stop <= max(0, size - 1))): + raise IndexError("Slices with out-of-bounds stop are not allowed in the array API namespace") + return key + + elif isinstance(key, tuple): + key = tuple(Array._validate_index(idx, None) for idx in key) + + for idx in key: + if ( + isinstance(idx, np.ndarray) + and idx.dtype in _boolean_dtypes + or isinstance(idx, (bool, np.bool_)) + ): + if len(key) == 1: + return key + raise IndexError( + "Boolean array indices combined with other indices are not allowed in the array API namespace" + ) + if isinstance(idx, tuple): + raise IndexError( + "Nested tuple indices are not allowed in the array API namespace" + ) + + if shape is None: + return key + n_ellipsis = key.count(...) + if n_ellipsis > 1: + return key + ellipsis_i = key.index(...) if n_ellipsis else len(key) + + for idx, size in list(zip(key[:ellipsis_i], shape)) + list( + zip(key[:ellipsis_i:-1], shape[:ellipsis_i:-1]) + ): + Array._validate_index(idx, (size,)) + if n_ellipsis == 0 and len(key) < len(shape): + raise IndexError( + "Multidimensional arrays must include an index for every axis or use an ellipsis" + ) + return key + elif isinstance(key, bool): + return key + elif isinstance(key, Array): + if key.dtype in _integer_dtypes: + if key.ndim != 0: + raise IndexError( + "Non-zero dimensional integer array indices are not allowed in the array API namespace" + ) + return key._array + elif key is Ellipsis: + return key + elif key is None: + raise IndexError( + "newaxis indices are not allowed in the array API namespace" + ) + try: + key = operator.index(key) + if shape is not None and len(shape) > 1: + raise IndexError( + "Multidimensional arrays must include an index for every axis or use an ellipsis" + ) + return key + except TypeError: + # Note: This also omits boolean arrays that are not already in + # Array() form, like a list of booleans. + raise IndexError( + "Only integers, slices (`:`), ellipsis (`...`), and boolean arrays are valid indices in the array API namespace" + ) + + # Everything below this line is required by the spec. + + def __abs__(self: Array, /) -> Array: + """ + Performs the operation __abs__. + """ + if self.dtype not in _numeric_dtypes: + raise TypeError("Only numeric dtypes are allowed in __abs__") + res = self._array.__abs__() + return self.__class__._new(res) + + def __add__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __add__. + """ + other = self._check_allowed_dtypes(other, "numeric", "__add__") + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__add__(other._array) + return self.__class__._new(res) + + def __and__(self: Array, other: Union[int, bool, Array], /) -> Array: + """ + Performs the operation __and__. + """ + other = self._check_allowed_dtypes(other, "integer or boolean", "__and__") + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__and__(other._array) + return self.__class__._new(res) + + def __array_namespace__( + self: Array, /, *, api_version: Optional[str] = None + ) -> Any: + if api_version is not None and not api_version.startswith("2021."): + raise ValueError(f"Unrecognized array API version: {api_version!r}") + return array_api + + def __bool__(self: Array, /) -> bool: + """ + Performs the operation __bool__. + """ + # Note: This is an error here. + if self._array.ndim != 0: + raise TypeError("bool is only allowed on arrays with 0 dimensions") + if self.dtype not in _boolean_dtypes: + raise ValueError("bool is only allowed on boolean arrays") + res = self._array.__bool__() + return res + + def __dlpack__(self: Array, /, *, stream: None = None) -> PyCapsule: + """ + Performs the operation __dlpack__. + """ + return self._array.__dlpack__(stream=stream) + + def __dlpack_device__(self: Array, /) -> Tuple[IntEnum, int]: + """ + Performs the operation __dlpack_device__. + """ + # Note: device support is required for this + return self._array.__dlpack_device__() + + def __eq__(self: Array, other: Union[int, float, bool, Array], /) -> Array: + """ + Performs the operation __eq__. + """ + # Even though "all" dtypes are allowed, we still require them to be + # promotable with each other. + other = self._check_allowed_dtypes(other, "all", "__eq__") + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__eq__(other._array) + return self.__class__._new(res) + + def __float__(self: Array, /) -> float: + """ + Performs the operation __float__. + """ + # Note: This is an error here. + if self._array.ndim != 0: + raise TypeError("float is only allowed on arrays with 0 dimensions") + if self.dtype not in _floating_dtypes: + raise ValueError("float is only allowed on floating-point arrays") + res = self._array.__float__() + return res + + def __floordiv__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __floordiv__. + """ + other = self._check_allowed_dtypes(other, "numeric", "__floordiv__") + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__floordiv__(other._array) + return self.__class__._new(res) + + def __ge__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __ge__. + """ + other = self._check_allowed_dtypes(other, "numeric", "__ge__") + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__ge__(other._array) + return self.__class__._new(res) + + def __getitem__( + self: Array, + key: Union[ + int, slice, ellipsis, Tuple[Union[int, slice, ellipsis], ...], Array + ], + /, + ) -> Array: + """ + Performs the operation __getitem__. + """ + # Note: Only indices required by the spec are allowed. See the + # docstring of _validate_index + key = self._validate_index(key, self.shape) + res = self._array.__getitem__(key) + return self._new(res) + + def __gt__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __gt__. + """ + other = self._check_allowed_dtypes(other, "numeric", "__gt__") + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__gt__(other._array) + return self.__class__._new(res) + + def __int__(self: Array, /) -> int: + """ + Performs the operation __int__. + """ + # Note: This is an error here. + if self._array.ndim != 0: + raise TypeError("int is only allowed on arrays with 0 dimensions") + if self.dtype not in _integer_dtypes: + raise ValueError("int is only allowed on integer arrays") + res = self._array.__int__() + return res + + def __index__(self: Array, /) -> int: + """ + Performs the operation __index__. + """ + res = self._array.__index__() + return res + + def __invert__(self: Array, /) -> Array: + """ + Performs the operation __invert__. + """ + if self.dtype not in _integer_or_boolean_dtypes: + raise TypeError("Only integer or boolean dtypes are allowed in __invert__") + res = self._array.__invert__() + return self.__class__._new(res) + + def __le__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __le__. + """ + other = self._check_allowed_dtypes(other, "numeric", "__le__") + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__le__(other._array) + return self.__class__._new(res) + + def __lshift__(self: Array, other: Union[int, Array], /) -> Array: + """ + Performs the operation __lshift__. + """ + other = self._check_allowed_dtypes(other, "integer", "__lshift__") + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__lshift__(other._array) + return self.__class__._new(res) + + def __lt__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __lt__. + """ + other = self._check_allowed_dtypes(other, "numeric", "__lt__") + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__lt__(other._array) + return self.__class__._new(res) + + def __matmul__(self: Array, other: Array, /) -> Array: + """ + Performs the operation __matmul__. + """ + # matmul is not defined for scalars, but without this, we may get + # the wrong error message from asarray. + other = self._check_allowed_dtypes(other, "numeric", "__matmul__") + if other is NotImplemented: + return other + res = self._array.__matmul__(other._array) + return self.__class__._new(res) + + def __mod__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __mod__. + """ + other = self._check_allowed_dtypes(other, "numeric", "__mod__") + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__mod__(other._array) + return self.__class__._new(res) + + def __mul__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __mul__. + """ + other = self._check_allowed_dtypes(other, "numeric", "__mul__") + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__mul__(other._array) + return self.__class__._new(res) + + def __ne__(self: Array, other: Union[int, float, bool, Array], /) -> Array: + """ + Performs the operation __ne__. + """ + other = self._check_allowed_dtypes(other, "all", "__ne__") + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__ne__(other._array) + return self.__class__._new(res) + + def __neg__(self: Array, /) -> Array: + """ + Performs the operation __neg__. + """ + if self.dtype not in _numeric_dtypes: + raise TypeError("Only numeric dtypes are allowed in __neg__") + res = self._array.__neg__() + return self.__class__._new(res) + + def __or__(self: Array, other: Union[int, bool, Array], /) -> Array: + """ + Performs the operation __or__. + """ + other = self._check_allowed_dtypes(other, "integer or boolean", "__or__") + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__or__(other._array) + return self.__class__._new(res) + + def __pos__(self: Array, /) -> Array: + """ + Performs the operation __pos__. + """ + if self.dtype not in _numeric_dtypes: + raise TypeError("Only numeric dtypes are allowed in __pos__") + res = self._array.__pos__() + return self.__class__._new(res) + + # PEP 484 requires int to be a subtype of float, but __pow__ should not + # accept int. + def __pow__(self: Array, other: Union[float, Array], /) -> Array: + """ + Performs the operation __pow__. + """ + from ._elementwise_functions import pow + + other = self._check_allowed_dtypes(other, "floating-point", "__pow__") + if other is NotImplemented: + return other + # Note: NumPy's __pow__ does not follow type promotion rules for 0-d + # arrays, so we use pow() here instead. + return pow(self, other) + + def __rshift__(self: Array, other: Union[int, Array], /) -> Array: + """ + Performs the operation __rshift__. + """ + other = self._check_allowed_dtypes(other, "integer", "__rshift__") + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__rshift__(other._array) + return self.__class__._new(res) + + def __setitem__( + self, + key: Union[ + int, slice, ellipsis, Tuple[Union[int, slice, ellipsis], ...], Array + ], + value: Union[int, float, bool, Array], + /, + ) -> None: + """ + Performs the operation __setitem__. + """ + # Note: Only indices required by the spec are allowed. See the + # docstring of _validate_index + key = self._validate_index(key, self.shape) + self._array.__setitem__(key, asarray(value)._array) + + def __sub__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __sub__. + """ + other = self._check_allowed_dtypes(other, "numeric", "__sub__") + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__sub__(other._array) + return self.__class__._new(res) + + # PEP 484 requires int to be a subtype of float, but __truediv__ should + # not accept int. + def __truediv__(self: Array, other: Union[float, Array], /) -> Array: + """ + Performs the operation __truediv__. + """ + other = self._check_allowed_dtypes(other, "floating-point", "__truediv__") + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__truediv__(other._array) + return self.__class__._new(res) + + def __xor__(self: Array, other: Union[int, bool, Array], /) -> Array: + """ + Performs the operation __xor__. + """ + other = self._check_allowed_dtypes(other, "integer or boolean", "__xor__") + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__xor__(other._array) + return self.__class__._new(res) + + def __iadd__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __iadd__. + """ + other = self._check_allowed_dtypes(other, "numeric", "__iadd__") + if other is NotImplemented: + return other + self._array.__iadd__(other._array) + return self + + def __radd__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __radd__. + """ + other = self._check_allowed_dtypes(other, "numeric", "__radd__") + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__radd__(other._array) + return self.__class__._new(res) + + def __iand__(self: Array, other: Union[int, bool, Array], /) -> Array: + """ + Performs the operation __iand__. + """ + other = self._check_allowed_dtypes(other, "integer or boolean", "__iand__") + if other is NotImplemented: + return other + self._array.__iand__(other._array) + return self + + def __rand__(self: Array, other: Union[int, bool, Array], /) -> Array: + """ + Performs the operation __rand__. + """ + other = self._check_allowed_dtypes(other, "integer or boolean", "__rand__") + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__rand__(other._array) + return self.__class__._new(res) + + def __ifloordiv__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __ifloordiv__. + """ + other = self._check_allowed_dtypes(other, "numeric", "__ifloordiv__") + if other is NotImplemented: + return other + self._array.__ifloordiv__(other._array) + return self + + def __rfloordiv__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __rfloordiv__. + """ + other = self._check_allowed_dtypes(other, "numeric", "__rfloordiv__") + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__rfloordiv__(other._array) + return self.__class__._new(res) + + def __ilshift__(self: Array, other: Union[int, Array], /) -> Array: + """ + Performs the operation __ilshift__. + """ + other = self._check_allowed_dtypes(other, "integer", "__ilshift__") + if other is NotImplemented: + return other + self._array.__ilshift__(other._array) + return self + + def __rlshift__(self: Array, other: Union[int, Array], /) -> Array: + """ + Performs the operation __rlshift__. + """ + other = self._check_allowed_dtypes(other, "integer", "__rlshift__") + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__rlshift__(other._array) + return self.__class__._new(res) + + def __imatmul__(self: Array, other: Array, /) -> Array: + """ + Performs the operation __imatmul__. + """ + # Note: NumPy does not implement __imatmul__. + + # matmul is not defined for scalars, but without this, we may get + # the wrong error message from asarray. + other = self._check_allowed_dtypes(other, "numeric", "__imatmul__") + if other is NotImplemented: + return other + + # __imatmul__ can only be allowed when it would not change the shape + # of self. + other_shape = other.shape + if self.shape == () or other_shape == (): + raise ValueError("@= requires at least one dimension") + if len(other_shape) == 1 or other_shape[-1] != other_shape[-2]: + raise ValueError("@= cannot change the shape of the input array") + self._array[:] = self._array.__matmul__(other._array) + return self + + def __rmatmul__(self: Array, other: Array, /) -> Array: + """ + Performs the operation __rmatmul__. + """ + # matmul is not defined for scalars, but without this, we may get + # the wrong error message from asarray. + other = self._check_allowed_dtypes(other, "numeric", "__rmatmul__") + if other is NotImplemented: + return other + res = self._array.__rmatmul__(other._array) + return self.__class__._new(res) + + def __imod__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __imod__. + """ + other = self._check_allowed_dtypes(other, "numeric", "__imod__") + if other is NotImplemented: + return other + self._array.__imod__(other._array) + return self + + def __rmod__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __rmod__. + """ + other = self._check_allowed_dtypes(other, "numeric", "__rmod__") + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__rmod__(other._array) + return self.__class__._new(res) + + def __imul__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __imul__. + """ + other = self._check_allowed_dtypes(other, "numeric", "__imul__") + if other is NotImplemented: + return other + self._array.__imul__(other._array) + return self + + def __rmul__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __rmul__. + """ + other = self._check_allowed_dtypes(other, "numeric", "__rmul__") + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__rmul__(other._array) + return self.__class__._new(res) + + def __ior__(self: Array, other: Union[int, bool, Array], /) -> Array: + """ + Performs the operation __ior__. + """ + other = self._check_allowed_dtypes(other, "integer or boolean", "__ior__") + if other is NotImplemented: + return other + self._array.__ior__(other._array) + return self + + def __ror__(self: Array, other: Union[int, bool, Array], /) -> Array: + """ + Performs the operation __ror__. + """ + other = self._check_allowed_dtypes(other, "integer or boolean", "__ror__") + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__ror__(other._array) + return self.__class__._new(res) + + def __ipow__(self: Array, other: Union[float, Array], /) -> Array: + """ + Performs the operation __ipow__. + """ + other = self._check_allowed_dtypes(other, "floating-point", "__ipow__") + if other is NotImplemented: + return other + self._array.__ipow__(other._array) + return self + + def __rpow__(self: Array, other: Union[float, Array], /) -> Array: + """ + Performs the operation __rpow__. + """ + from ._elementwise_functions import pow + + other = self._check_allowed_dtypes(other, "floating-point", "__rpow__") + if other is NotImplemented: + return other + # Note: NumPy's __pow__ does not follow the spec type promotion rules + # for 0-d arrays, so we use pow() here instead. + return pow(other, self) + + def __irshift__(self: Array, other: Union[int, Array], /) -> Array: + """ + Performs the operation __irshift__. + """ + other = self._check_allowed_dtypes(other, "integer", "__irshift__") + if other is NotImplemented: + return other + self._array.__irshift__(other._array) + return self + + def __rrshift__(self: Array, other: Union[int, Array], /) -> Array: + """ + Performs the operation __rrshift__. + """ + other = self._check_allowed_dtypes(other, "integer", "__rrshift__") + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__rrshift__(other._array) + return self.__class__._new(res) + + def __isub__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __isub__. + """ + other = self._check_allowed_dtypes(other, "numeric", "__isub__") + if other is NotImplemented: + return other + self._array.__isub__(other._array) + return self + + def __rsub__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __rsub__. + """ + other = self._check_allowed_dtypes(other, "numeric", "__rsub__") + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__rsub__(other._array) + return self.__class__._new(res) + + def __itruediv__(self: Array, other: Union[float, Array], /) -> Array: + """ + Performs the operation __itruediv__. + """ + other = self._check_allowed_dtypes(other, "floating-point", "__itruediv__") + if other is NotImplemented: + return other + self._array.__itruediv__(other._array) + return self + + def __rtruediv__(self: Array, other: Union[float, Array], /) -> Array: + """ + Performs the operation __rtruediv__. + """ + other = self._check_allowed_dtypes(other, "floating-point", "__rtruediv__") + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__rtruediv__(other._array) + return self.__class__._new(res) + + def __ixor__(self: Array, other: Union[int, bool, Array], /) -> Array: + """ + Performs the operation __ixor__. + """ + other = self._check_allowed_dtypes(other, "integer or boolean", "__ixor__") + if other is NotImplemented: + return other + self._array.__ixor__(other._array) + return self + + def __rxor__(self: Array, other: Union[int, bool, Array], /) -> Array: + """ + Performs the operation __rxor__. + """ + other = self._check_allowed_dtypes(other, "integer or boolean", "__rxor__") + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__rxor__(other._array) + return self.__class__._new(res) + + def to_device(self: Array, device: Device, /, stream: None = None) -> Array: + if stream is not None: + raise ValueError("The stream argument to to_device() is not supported") + if device == 'cpu': + return self + raise ValueError(f"Unsupported device {device!r}") + + @property + def dtype(self) -> Dtype: + """ + Array API compatible wrapper for :py:meth:`np.ndarray.dtype <numpy.ndarray.dtype>`. + + See its docstring for more information. + """ + return self._array.dtype + + @property + def device(self) -> Device: + return "cpu" + + # Note: mT is new in array API spec (see matrix_transpose) + @property + def mT(self) -> Array: + from .linalg import matrix_transpose + return matrix_transpose(self) + + @property + def ndim(self) -> int: + """ + Array API compatible wrapper for :py:meth:`np.ndarray.ndim <numpy.ndarray.ndim>`. + + See its docstring for more information. + """ + return self._array.ndim + + @property + def shape(self) -> Tuple[int, ...]: + """ + Array API compatible wrapper for :py:meth:`np.ndarray.shape <numpy.ndarray.shape>`. + + See its docstring for more information. + """ + return self._array.shape + + @property + def size(self) -> int: + """ + Array API compatible wrapper for :py:meth:`np.ndarray.size <numpy.ndarray.size>`. + + See its docstring for more information. + """ + return self._array.size + + @property + def T(self) -> Array: + """ + Array API compatible wrapper for :py:meth:`np.ndarray.T <numpy.ndarray.T>`. + + See its docstring for more information. + """ + # Note: T only works on 2-dimensional arrays. See the corresponding + # note in the specification: + # https://data-apis.org/array-api/latest/API_specification/array_object.html#t + if self.ndim != 2: + raise ValueError("x.T requires x to have 2 dimensions. Use x.mT to transpose stacks of matrices and permute_dims() to permute dimensions.") + return self.__class__._new(self._array.T) diff --git a/numpy/array_api/_constants.py b/numpy/array_api/_constants.py new file mode 100644 index 000000000000..9541941e7c6f --- /dev/null +++ b/numpy/array_api/_constants.py @@ -0,0 +1,6 @@ +import numpy as np + +e = np.e +inf = np.inf +nan = np.nan +pi = np.pi diff --git a/numpy/array_api/_creation_functions.py b/numpy/array_api/_creation_functions.py new file mode 100644 index 000000000000..741498ff610f --- /dev/null +++ b/numpy/array_api/_creation_functions.py @@ -0,0 +1,351 @@ +from __future__ import annotations + + +from typing import TYPE_CHECKING, List, Optional, Tuple, Union + +if TYPE_CHECKING: + from ._typing import ( + Array, + Device, + Dtype, + NestedSequence, + SupportsBufferProtocol, + ) + from collections.abc import Sequence +from ._dtypes import _all_dtypes + +import numpy as np + + +def _check_valid_dtype(dtype): + # Note: Only spelling dtypes as the dtype objects is supported. + + # We use this instead of "dtype in _all_dtypes" because the dtype objects + # define equality with the sorts of things we want to disallow. + for d in (None,) + _all_dtypes: + if dtype is d: + return + raise ValueError("dtype must be one of the supported dtypes") + + +def asarray( + obj: Union[ + Array, + bool, + int, + float, + NestedSequence[bool | int | float], + SupportsBufferProtocol, + ], + /, + *, + dtype: Optional[Dtype] = None, + device: Optional[Device] = None, + copy: Optional[Union[bool, np._CopyMode]] = None, +) -> Array: + """ + Array API compatible wrapper for :py:func:`np.asarray <numpy.asarray>`. + + See its docstring for more information. + """ + # _array_object imports in this file are inside the functions to avoid + # circular imports + from ._array_object import Array + + _check_valid_dtype(dtype) + if device not in ["cpu", None]: + raise ValueError(f"Unsupported device {device!r}") + if copy in (False, np._CopyMode.IF_NEEDED): + # Note: copy=False is not yet implemented in np.asarray + raise NotImplementedError("copy=False is not yet implemented") + if isinstance(obj, Array): + if dtype is not None and obj.dtype != dtype: + copy = True + if copy in (True, np._CopyMode.ALWAYS): + return Array._new(np.array(obj._array, copy=True, dtype=dtype)) + return obj + if dtype is None and isinstance(obj, int) and (obj > 2 ** 64 or obj < -(2 ** 63)): + # Give a better error message in this case. NumPy would convert this + # to an object array. TODO: This won't handle large integers in lists. + raise OverflowError("Integer out of bounds for array dtypes") + res = np.asarray(obj, dtype=dtype) + return Array._new(res) + + +def arange( + start: Union[int, float], + /, + stop: Optional[Union[int, float]] = None, + step: Union[int, float] = 1, + *, + dtype: Optional[Dtype] = None, + device: Optional[Device] = None, +) -> Array: + """ + Array API compatible wrapper for :py:func:`np.arange <numpy.arange>`. + + See its docstring for more information. + """ + from ._array_object import Array + + _check_valid_dtype(dtype) + if device not in ["cpu", None]: + raise ValueError(f"Unsupported device {device!r}") + return Array._new(np.arange(start, stop=stop, step=step, dtype=dtype)) + + +def empty( + shape: Union[int, Tuple[int, ...]], + *, + dtype: Optional[Dtype] = None, + device: Optional[Device] = None, +) -> Array: + """ + Array API compatible wrapper for :py:func:`np.empty <numpy.empty>`. + + See its docstring for more information. + """ + from ._array_object import Array + + _check_valid_dtype(dtype) + if device not in ["cpu", None]: + raise ValueError(f"Unsupported device {device!r}") + return Array._new(np.empty(shape, dtype=dtype)) + + +def empty_like( + x: Array, /, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None +) -> Array: + """ + Array API compatible wrapper for :py:func:`np.empty_like <numpy.empty_like>`. + + See its docstring for more information. + """ + from ._array_object import Array + + _check_valid_dtype(dtype) + if device not in ["cpu", None]: + raise ValueError(f"Unsupported device {device!r}") + return Array._new(np.empty_like(x._array, dtype=dtype)) + + +def eye( + n_rows: int, + n_cols: Optional[int] = None, + /, + *, + k: int = 0, + dtype: Optional[Dtype] = None, + device: Optional[Device] = None, +) -> Array: + """ + Array API compatible wrapper for :py:func:`np.eye <numpy.eye>`. + + See its docstring for more information. + """ + from ._array_object import Array + + _check_valid_dtype(dtype) + if device not in ["cpu", None]: + raise ValueError(f"Unsupported device {device!r}") + return Array._new(np.eye(n_rows, M=n_cols, k=k, dtype=dtype)) + + +def from_dlpack(x: object, /) -> Array: + from ._array_object import Array + + return Array._new(np._from_dlpack(x)) + + +def full( + shape: Union[int, Tuple[int, ...]], + fill_value: Union[int, float], + *, + dtype: Optional[Dtype] = None, + device: Optional[Device] = None, +) -> Array: + """ + Array API compatible wrapper for :py:func:`np.full <numpy.full>`. + + See its docstring for more information. + """ + from ._array_object import Array + + _check_valid_dtype(dtype) + if device not in ["cpu", None]: + raise ValueError(f"Unsupported device {device!r}") + if isinstance(fill_value, Array) and fill_value.ndim == 0: + fill_value = fill_value._array + res = np.full(shape, fill_value, dtype=dtype) + if res.dtype not in _all_dtypes: + # This will happen if the fill value is not something that NumPy + # coerces to one of the acceptable dtypes. + raise TypeError("Invalid input to full") + return Array._new(res) + + +def full_like( + x: Array, + /, + fill_value: Union[int, float], + *, + dtype: Optional[Dtype] = None, + device: Optional[Device] = None, +) -> Array: + """ + Array API compatible wrapper for :py:func:`np.full_like <numpy.full_like>`. + + See its docstring for more information. + """ + from ._array_object import Array + + _check_valid_dtype(dtype) + if device not in ["cpu", None]: + raise ValueError(f"Unsupported device {device!r}") + res = np.full_like(x._array, fill_value, dtype=dtype) + if res.dtype not in _all_dtypes: + # This will happen if the fill value is not something that NumPy + # coerces to one of the acceptable dtypes. + raise TypeError("Invalid input to full_like") + return Array._new(res) + + +def linspace( + start: Union[int, float], + stop: Union[int, float], + /, + num: int, + *, + dtype: Optional[Dtype] = None, + device: Optional[Device] = None, + endpoint: bool = True, +) -> Array: + """ + Array API compatible wrapper for :py:func:`np.linspace <numpy.linspace>`. + + See its docstring for more information. + """ + from ._array_object import Array + + _check_valid_dtype(dtype) + if device not in ["cpu", None]: + raise ValueError(f"Unsupported device {device!r}") + return Array._new(np.linspace(start, stop, num, dtype=dtype, endpoint=endpoint)) + + +def meshgrid(*arrays: Array, indexing: str = "xy") -> List[Array]: + """ + Array API compatible wrapper for :py:func:`np.meshgrid <numpy.meshgrid>`. + + See its docstring for more information. + """ + from ._array_object import Array + + # Note: unlike np.meshgrid, only inputs with all the same dtype are + # allowed + + if len({a.dtype for a in arrays}) > 1: + raise ValueError("meshgrid inputs must all have the same dtype") + + return [ + Array._new(array) + for array in np.meshgrid(*[a._array for a in arrays], indexing=indexing) + ] + + +def ones( + shape: Union[int, Tuple[int, ...]], + *, + dtype: Optional[Dtype] = None, + device: Optional[Device] = None, +) -> Array: + """ + Array API compatible wrapper for :py:func:`np.ones <numpy.ones>`. + + See its docstring for more information. + """ + from ._array_object import Array + + _check_valid_dtype(dtype) + if device not in ["cpu", None]: + raise ValueError(f"Unsupported device {device!r}") + return Array._new(np.ones(shape, dtype=dtype)) + + +def ones_like( + x: Array, /, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None +) -> Array: + """ + Array API compatible wrapper for :py:func:`np.ones_like <numpy.ones_like>`. + + See its docstring for more information. + """ + from ._array_object import Array + + _check_valid_dtype(dtype) + if device not in ["cpu", None]: + raise ValueError(f"Unsupported device {device!r}") + return Array._new(np.ones_like(x._array, dtype=dtype)) + + +def tril(x: Array, /, *, k: int = 0) -> Array: + """ + Array API compatible wrapper for :py:func:`np.tril <numpy.tril>`. + + See its docstring for more information. + """ + from ._array_object import Array + + if x.ndim < 2: + # Note: Unlike np.tril, x must be at least 2-D + raise ValueError("x must be at least 2-dimensional for tril") + return Array._new(np.tril(x._array, k=k)) + + +def triu(x: Array, /, *, k: int = 0) -> Array: + """ + Array API compatible wrapper for :py:func:`np.triu <numpy.triu>`. + + See its docstring for more information. + """ + from ._array_object import Array + + if x.ndim < 2: + # Note: Unlike np.triu, x must be at least 2-D + raise ValueError("x must be at least 2-dimensional for triu") + return Array._new(np.triu(x._array, k=k)) + + +def zeros( + shape: Union[int, Tuple[int, ...]], + *, + dtype: Optional[Dtype] = None, + device: Optional[Device] = None, +) -> Array: + """ + Array API compatible wrapper for :py:func:`np.zeros <numpy.zeros>`. + + See its docstring for more information. + """ + from ._array_object import Array + + _check_valid_dtype(dtype) + if device not in ["cpu", None]: + raise ValueError(f"Unsupported device {device!r}") + return Array._new(np.zeros(shape, dtype=dtype)) + + +def zeros_like( + x: Array, /, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None +) -> Array: + """ + Array API compatible wrapper for :py:func:`np.zeros_like <numpy.zeros_like>`. + + See its docstring for more information. + """ + from ._array_object import Array + + _check_valid_dtype(dtype) + if device not in ["cpu", None]: + raise ValueError(f"Unsupported device {device!r}") + return Array._new(np.zeros_like(x._array, dtype=dtype)) diff --git a/numpy/array_api/_data_type_functions.py b/numpy/array_api/_data_type_functions.py new file mode 100644 index 000000000000..e4d6db61bb84 --- /dev/null +++ b/numpy/array_api/_data_type_functions.py @@ -0,0 +1,134 @@ +from __future__ import annotations + +from ._array_object import Array +from ._dtypes import _all_dtypes, _result_type + +from dataclasses import dataclass +from typing import TYPE_CHECKING, List, Tuple, Union + +if TYPE_CHECKING: + from ._typing import Dtype + from collections.abc import Sequence + +import numpy as np + + +# Note: astype is a function, not an array method as in NumPy. +def astype(x: Array, dtype: Dtype, /, *, copy: bool = True) -> Array: + if not copy and dtype == x.dtype: + return x + return Array._new(x._array.astype(dtype=dtype, copy=copy)) + + +def broadcast_arrays(*arrays: Array) -> List[Array]: + """ + Array API compatible wrapper for :py:func:`np.broadcast_arrays <numpy.broadcast_arrays>`. + + See its docstring for more information. + """ + from ._array_object import Array + + return [ + Array._new(array) for array in np.broadcast_arrays(*[a._array for a in arrays]) + ] + + +def broadcast_to(x: Array, /, shape: Tuple[int, ...]) -> Array: + """ + Array API compatible wrapper for :py:func:`np.broadcast_to <numpy.broadcast_to>`. + + See its docstring for more information. + """ + from ._array_object import Array + + return Array._new(np.broadcast_to(x._array, shape)) + + +def can_cast(from_: Union[Dtype, Array], to: Dtype, /) -> bool: + """ + Array API compatible wrapper for :py:func:`np.can_cast <numpy.can_cast>`. + + See its docstring for more information. + """ + from ._array_object import Array + + if isinstance(from_, Array): + from_ = from_._array + return np.can_cast(from_, to) + + +# These are internal objects for the return types of finfo and iinfo, since +# the NumPy versions contain extra data that isn't part of the spec. +@dataclass +class finfo_object: + bits: int + # Note: The types of the float data here are float, whereas in NumPy they + # are scalars of the corresponding float dtype. + eps: float + max: float + min: float + smallest_normal: float + + +@dataclass +class iinfo_object: + bits: int + max: int + min: int + + +def finfo(type: Union[Dtype, Array], /) -> finfo_object: + """ + Array API compatible wrapper for :py:func:`np.finfo <numpy.finfo>`. + + See its docstring for more information. + """ + fi = np.finfo(type) + # Note: The types of the float data here are float, whereas in NumPy they + # are scalars of the corresponding float dtype. + return finfo_object( + fi.bits, + float(fi.eps), + float(fi.max), + float(fi.min), + float(fi.smallest_normal), + ) + + +def iinfo(type: Union[Dtype, Array], /) -> iinfo_object: + """ + Array API compatible wrapper for :py:func:`np.iinfo <numpy.iinfo>`. + + See its docstring for more information. + """ + ii = np.iinfo(type) + return iinfo_object(ii.bits, ii.max, ii.min) + + +def result_type(*arrays_and_dtypes: Union[Array, Dtype]) -> Dtype: + """ + Array API compatible wrapper for :py:func:`np.result_type <numpy.result_type>`. + + See its docstring for more information. + """ + # Note: we use a custom implementation that gives only the type promotions + # required by the spec rather than using np.result_type. NumPy implements + # too many extra type promotions like int64 + uint64 -> float64, and does + # value-based casting on scalar arrays. + A = [] + for a in arrays_and_dtypes: + if isinstance(a, Array): + a = a.dtype + elif isinstance(a, np.ndarray) or a not in _all_dtypes: + raise TypeError("result_type() inputs must be array_api arrays or dtypes") + A.append(a) + + if len(A) == 0: + raise ValueError("at least one array or dtype is required") + elif len(A) == 1: + return A[0] + else: + t = A[0] + for t2 in A[1:]: + t = _result_type(t, t2) + return t diff --git a/numpy/array_api/_dtypes.py b/numpy/array_api/_dtypes.py new file mode 100644 index 000000000000..476d619fee63 --- /dev/null +++ b/numpy/array_api/_dtypes.py @@ -0,0 +1,143 @@ +import numpy as np + +# Note: we use dtype objects instead of dtype classes. The spec does not +# require any behavior on dtypes other than equality. +int8 = np.dtype("int8") +int16 = np.dtype("int16") +int32 = np.dtype("int32") +int64 = np.dtype("int64") +uint8 = np.dtype("uint8") +uint16 = np.dtype("uint16") +uint32 = np.dtype("uint32") +uint64 = np.dtype("uint64") +float32 = np.dtype("float32") +float64 = np.dtype("float64") +# Note: This name is changed +bool = np.dtype("bool") + +_all_dtypes = ( + int8, + int16, + int32, + int64, + uint8, + uint16, + uint32, + uint64, + float32, + float64, + bool, +) +_boolean_dtypes = (bool,) +_floating_dtypes = (float32, float64) +_integer_dtypes = (int8, int16, int32, int64, uint8, uint16, uint32, uint64) +_integer_or_boolean_dtypes = ( + bool, + int8, + int16, + int32, + int64, + uint8, + uint16, + uint32, + uint64, +) +_numeric_dtypes = ( + float32, + float64, + int8, + int16, + int32, + int64, + uint8, + uint16, + uint32, + uint64, +) + +_dtype_categories = { + "all": _all_dtypes, + "numeric": _numeric_dtypes, + "integer": _integer_dtypes, + "integer or boolean": _integer_or_boolean_dtypes, + "boolean": _boolean_dtypes, + "floating-point": _floating_dtypes, +} + + +# Note: the spec defines a restricted type promotion table compared to NumPy. +# In particular, cross-kind promotions like integer + float or boolean + +# integer are not allowed, even for functions that accept both kinds. +# Additionally, NumPy promotes signed integer + uint64 to float64, but this +# promotion is not allowed here. To be clear, Python scalar int objects are +# allowed to promote to floating-point dtypes, but only in array operators +# (see Array._promote_scalar) method in _array_object.py. +_promotion_table = { + (int8, int8): int8, + (int8, int16): int16, + (int8, int32): int32, + (int8, int64): int64, + (int16, int8): int16, + (int16, int16): int16, + (int16, int32): int32, + (int16, int64): int64, + (int32, int8): int32, + (int32, int16): int32, + (int32, int32): int32, + (int32, int64): int64, + (int64, int8): int64, + (int64, int16): int64, + (int64, int32): int64, + (int64, int64): int64, + (uint8, uint8): uint8, + (uint8, uint16): uint16, + (uint8, uint32): uint32, + (uint8, uint64): uint64, + (uint16, uint8): uint16, + (uint16, uint16): uint16, + (uint16, uint32): uint32, + (uint16, uint64): uint64, + (uint32, uint8): uint32, + (uint32, uint16): uint32, + (uint32, uint32): uint32, + (uint32, uint64): uint64, + (uint64, uint8): uint64, + (uint64, uint16): uint64, + (uint64, uint32): uint64, + (uint64, uint64): uint64, + (int8, uint8): int16, + (int8, uint16): int32, + (int8, uint32): int64, + (int16, uint8): int16, + (int16, uint16): int32, + (int16, uint32): int64, + (int32, uint8): int32, + (int32, uint16): int32, + (int32, uint32): int64, + (int64, uint8): int64, + (int64, uint16): int64, + (int64, uint32): int64, + (uint8, int8): int16, + (uint16, int8): int32, + (uint32, int8): int64, + (uint8, int16): int16, + (uint16, int16): int32, + (uint32, int16): int64, + (uint8, int32): int32, + (uint16, int32): int32, + (uint32, int32): int64, + (uint8, int64): int64, + (uint16, int64): int64, + (uint32, int64): int64, + (float32, float32): float32, + (float32, float64): float64, + (float64, float32): float64, + (float64, float64): float64, + (bool, bool): bool, +} + + +def _result_type(type1, type2): + if (type1, type2) in _promotion_table: + return _promotion_table[type1, type2] + raise TypeError(f"{type1} and {type2} cannot be type promoted together") diff --git a/numpy/array_api/_elementwise_functions.py b/numpy/array_api/_elementwise_functions.py new file mode 100644 index 000000000000..4408fe833b4c --- /dev/null +++ b/numpy/array_api/_elementwise_functions.py @@ -0,0 +1,729 @@ +from __future__ import annotations + +from ._dtypes import ( + _boolean_dtypes, + _floating_dtypes, + _integer_dtypes, + _integer_or_boolean_dtypes, + _numeric_dtypes, + _result_type, +) +from ._array_object import Array + +import numpy as np + + +def abs(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.abs <numpy.abs>`. + + See its docstring for more information. + """ + if x.dtype not in _numeric_dtypes: + raise TypeError("Only numeric dtypes are allowed in abs") + return Array._new(np.abs(x._array)) + + +# Note: the function name is different here +def acos(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.arccos <numpy.arccos>`. + + See its docstring for more information. + """ + if x.dtype not in _floating_dtypes: + raise TypeError("Only floating-point dtypes are allowed in acos") + return Array._new(np.arccos(x._array)) + + +# Note: the function name is different here +def acosh(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.arccosh <numpy.arccosh>`. + + See its docstring for more information. + """ + if x.dtype not in _floating_dtypes: + raise TypeError("Only floating-point dtypes are allowed in acosh") + return Array._new(np.arccosh(x._array)) + + +def add(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.add <numpy.add>`. + + See its docstring for more information. + """ + if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: + raise TypeError("Only numeric dtypes are allowed in add") + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.add(x1._array, x2._array)) + + +# Note: the function name is different here +def asin(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.arcsin <numpy.arcsin>`. + + See its docstring for more information. + """ + if x.dtype not in _floating_dtypes: + raise TypeError("Only floating-point dtypes are allowed in asin") + return Array._new(np.arcsin(x._array)) + + +# Note: the function name is different here +def asinh(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.arcsinh <numpy.arcsinh>`. + + See its docstring for more information. + """ + if x.dtype not in _floating_dtypes: + raise TypeError("Only floating-point dtypes are allowed in asinh") + return Array._new(np.arcsinh(x._array)) + + +# Note: the function name is different here +def atan(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.arctan <numpy.arctan>`. + + See its docstring for more information. + """ + if x.dtype not in _floating_dtypes: + raise TypeError("Only floating-point dtypes are allowed in atan") + return Array._new(np.arctan(x._array)) + + +# Note: the function name is different here +def atan2(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.arctan2 <numpy.arctan2>`. + + See its docstring for more information. + """ + if x1.dtype not in _floating_dtypes or x2.dtype not in _floating_dtypes: + raise TypeError("Only floating-point dtypes are allowed in atan2") + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.arctan2(x1._array, x2._array)) + + +# Note: the function name is different here +def atanh(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.arctanh <numpy.arctanh>`. + + See its docstring for more information. + """ + if x.dtype not in _floating_dtypes: + raise TypeError("Only floating-point dtypes are allowed in atanh") + return Array._new(np.arctanh(x._array)) + + +def bitwise_and(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.bitwise_and <numpy.bitwise_and>`. + + See its docstring for more information. + """ + if ( + x1.dtype not in _integer_or_boolean_dtypes + or x2.dtype not in _integer_or_boolean_dtypes + ): + raise TypeError("Only integer or boolean dtypes are allowed in bitwise_and") + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.bitwise_and(x1._array, x2._array)) + + +# Note: the function name is different here +def bitwise_left_shift(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.left_shift <numpy.left_shift>`. + + See its docstring for more information. + """ + if x1.dtype not in _integer_dtypes or x2.dtype not in _integer_dtypes: + raise TypeError("Only integer dtypes are allowed in bitwise_left_shift") + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + # Note: bitwise_left_shift is only defined for x2 nonnegative. + if np.any(x2._array < 0): + raise ValueError("bitwise_left_shift(x1, x2) is only defined for x2 >= 0") + return Array._new(np.left_shift(x1._array, x2._array)) + + +# Note: the function name is different here +def bitwise_invert(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.invert <numpy.invert>`. + + See its docstring for more information. + """ + if x.dtype not in _integer_or_boolean_dtypes: + raise TypeError("Only integer or boolean dtypes are allowed in bitwise_invert") + return Array._new(np.invert(x._array)) + + +def bitwise_or(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.bitwise_or <numpy.bitwise_or>`. + + See its docstring for more information. + """ + if ( + x1.dtype not in _integer_or_boolean_dtypes + or x2.dtype not in _integer_or_boolean_dtypes + ): + raise TypeError("Only integer or boolean dtypes are allowed in bitwise_or") + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.bitwise_or(x1._array, x2._array)) + + +# Note: the function name is different here +def bitwise_right_shift(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.right_shift <numpy.right_shift>`. + + See its docstring for more information. + """ + if x1.dtype not in _integer_dtypes or x2.dtype not in _integer_dtypes: + raise TypeError("Only integer dtypes are allowed in bitwise_right_shift") + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + # Note: bitwise_right_shift is only defined for x2 nonnegative. + if np.any(x2._array < 0): + raise ValueError("bitwise_right_shift(x1, x2) is only defined for x2 >= 0") + return Array._new(np.right_shift(x1._array, x2._array)) + + +def bitwise_xor(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.bitwise_xor <numpy.bitwise_xor>`. + + See its docstring for more information. + """ + if ( + x1.dtype not in _integer_or_boolean_dtypes + or x2.dtype not in _integer_or_boolean_dtypes + ): + raise TypeError("Only integer or boolean dtypes are allowed in bitwise_xor") + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.bitwise_xor(x1._array, x2._array)) + + +def ceil(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.ceil <numpy.ceil>`. + + See its docstring for more information. + """ + if x.dtype not in _numeric_dtypes: + raise TypeError("Only numeric dtypes are allowed in ceil") + if x.dtype in _integer_dtypes: + # Note: The return dtype of ceil is the same as the input + return x + return Array._new(np.ceil(x._array)) + + +def cos(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.cos <numpy.cos>`. + + See its docstring for more information. + """ + if x.dtype not in _floating_dtypes: + raise TypeError("Only floating-point dtypes are allowed in cos") + return Array._new(np.cos(x._array)) + + +def cosh(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.cosh <numpy.cosh>`. + + See its docstring for more information. + """ + if x.dtype not in _floating_dtypes: + raise TypeError("Only floating-point dtypes are allowed in cosh") + return Array._new(np.cosh(x._array)) + + +def divide(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.divide <numpy.divide>`. + + See its docstring for more information. + """ + if x1.dtype not in _floating_dtypes or x2.dtype not in _floating_dtypes: + raise TypeError("Only floating-point dtypes are allowed in divide") + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.divide(x1._array, x2._array)) + + +def equal(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.equal <numpy.equal>`. + + See its docstring for more information. + """ + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.equal(x1._array, x2._array)) + + +def exp(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.exp <numpy.exp>`. + + See its docstring for more information. + """ + if x.dtype not in _floating_dtypes: + raise TypeError("Only floating-point dtypes are allowed in exp") + return Array._new(np.exp(x._array)) + + +def expm1(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.expm1 <numpy.expm1>`. + + See its docstring for more information. + """ + if x.dtype not in _floating_dtypes: + raise TypeError("Only floating-point dtypes are allowed in expm1") + return Array._new(np.expm1(x._array)) + + +def floor(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.floor <numpy.floor>`. + + See its docstring for more information. + """ + if x.dtype not in _numeric_dtypes: + raise TypeError("Only numeric dtypes are allowed in floor") + if x.dtype in _integer_dtypes: + # Note: The return dtype of floor is the same as the input + return x + return Array._new(np.floor(x._array)) + + +def floor_divide(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.floor_divide <numpy.floor_divide>`. + + See its docstring for more information. + """ + if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: + raise TypeError("Only numeric dtypes are allowed in floor_divide") + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.floor_divide(x1._array, x2._array)) + + +def greater(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.greater <numpy.greater>`. + + See its docstring for more information. + """ + if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: + raise TypeError("Only numeric dtypes are allowed in greater") + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.greater(x1._array, x2._array)) + + +def greater_equal(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.greater_equal <numpy.greater_equal>`. + + See its docstring for more information. + """ + if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: + raise TypeError("Only numeric dtypes are allowed in greater_equal") + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.greater_equal(x1._array, x2._array)) + + +def isfinite(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.isfinite <numpy.isfinite>`. + + See its docstring for more information. + """ + if x.dtype not in _numeric_dtypes: + raise TypeError("Only numeric dtypes are allowed in isfinite") + return Array._new(np.isfinite(x._array)) + + +def isinf(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.isinf <numpy.isinf>`. + + See its docstring for more information. + """ + if x.dtype not in _numeric_dtypes: + raise TypeError("Only numeric dtypes are allowed in isinf") + return Array._new(np.isinf(x._array)) + + +def isnan(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.isnan <numpy.isnan>`. + + See its docstring for more information. + """ + if x.dtype not in _numeric_dtypes: + raise TypeError("Only numeric dtypes are allowed in isnan") + return Array._new(np.isnan(x._array)) + + +def less(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.less <numpy.less>`. + + See its docstring for more information. + """ + if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: + raise TypeError("Only numeric dtypes are allowed in less") + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.less(x1._array, x2._array)) + + +def less_equal(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.less_equal <numpy.less_equal>`. + + See its docstring for more information. + """ + if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: + raise TypeError("Only numeric dtypes are allowed in less_equal") + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.less_equal(x1._array, x2._array)) + + +def log(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.log <numpy.log>`. + + See its docstring for more information. + """ + if x.dtype not in _floating_dtypes: + raise TypeError("Only floating-point dtypes are allowed in log") + return Array._new(np.log(x._array)) + + +def log1p(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.log1p <numpy.log1p>`. + + See its docstring for more information. + """ + if x.dtype not in _floating_dtypes: + raise TypeError("Only floating-point dtypes are allowed in log1p") + return Array._new(np.log1p(x._array)) + + +def log2(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.log2 <numpy.log2>`. + + See its docstring for more information. + """ + if x.dtype not in _floating_dtypes: + raise TypeError("Only floating-point dtypes are allowed in log2") + return Array._new(np.log2(x._array)) + + +def log10(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.log10 <numpy.log10>`. + + See its docstring for more information. + """ + if x.dtype not in _floating_dtypes: + raise TypeError("Only floating-point dtypes are allowed in log10") + return Array._new(np.log10(x._array)) + + +def logaddexp(x1: Array, x2: Array) -> Array: + """ + Array API compatible wrapper for :py:func:`np.logaddexp <numpy.logaddexp>`. + + See its docstring for more information. + """ + if x1.dtype not in _floating_dtypes or x2.dtype not in _floating_dtypes: + raise TypeError("Only floating-point dtypes are allowed in logaddexp") + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.logaddexp(x1._array, x2._array)) + + +def logical_and(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.logical_and <numpy.logical_and>`. + + See its docstring for more information. + """ + if x1.dtype not in _boolean_dtypes or x2.dtype not in _boolean_dtypes: + raise TypeError("Only boolean dtypes are allowed in logical_and") + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.logical_and(x1._array, x2._array)) + + +def logical_not(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.logical_not <numpy.logical_not>`. + + See its docstring for more information. + """ + if x.dtype not in _boolean_dtypes: + raise TypeError("Only boolean dtypes are allowed in logical_not") + return Array._new(np.logical_not(x._array)) + + +def logical_or(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.logical_or <numpy.logical_or>`. + + See its docstring for more information. + """ + if x1.dtype not in _boolean_dtypes or x2.dtype not in _boolean_dtypes: + raise TypeError("Only boolean dtypes are allowed in logical_or") + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.logical_or(x1._array, x2._array)) + + +def logical_xor(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.logical_xor <numpy.logical_xor>`. + + See its docstring for more information. + """ + if x1.dtype not in _boolean_dtypes or x2.dtype not in _boolean_dtypes: + raise TypeError("Only boolean dtypes are allowed in logical_xor") + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.logical_xor(x1._array, x2._array)) + + +def multiply(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.multiply <numpy.multiply>`. + + See its docstring for more information. + """ + if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: + raise TypeError("Only numeric dtypes are allowed in multiply") + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.multiply(x1._array, x2._array)) + + +def negative(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.negative <numpy.negative>`. + + See its docstring for more information. + """ + if x.dtype not in _numeric_dtypes: + raise TypeError("Only numeric dtypes are allowed in negative") + return Array._new(np.negative(x._array)) + + +def not_equal(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.not_equal <numpy.not_equal>`. + + See its docstring for more information. + """ + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.not_equal(x1._array, x2._array)) + + +def positive(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.positive <numpy.positive>`. + + See its docstring for more information. + """ + if x.dtype not in _numeric_dtypes: + raise TypeError("Only numeric dtypes are allowed in positive") + return Array._new(np.positive(x._array)) + + +# Note: the function name is different here +def pow(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.power <numpy.power>`. + + See its docstring for more information. + """ + if x1.dtype not in _floating_dtypes or x2.dtype not in _floating_dtypes: + raise TypeError("Only floating-point dtypes are allowed in pow") + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.power(x1._array, x2._array)) + + +def remainder(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.remainder <numpy.remainder>`. + + See its docstring for more information. + """ + if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: + raise TypeError("Only numeric dtypes are allowed in remainder") + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.remainder(x1._array, x2._array)) + + +def round(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.round <numpy.round>`. + + See its docstring for more information. + """ + if x.dtype not in _numeric_dtypes: + raise TypeError("Only numeric dtypes are allowed in round") + return Array._new(np.round(x._array)) + + +def sign(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.sign <numpy.sign>`. + + See its docstring for more information. + """ + if x.dtype not in _numeric_dtypes: + raise TypeError("Only numeric dtypes are allowed in sign") + return Array._new(np.sign(x._array)) + + +def sin(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.sin <numpy.sin>`. + + See its docstring for more information. + """ + if x.dtype not in _floating_dtypes: + raise TypeError("Only floating-point dtypes are allowed in sin") + return Array._new(np.sin(x._array)) + + +def sinh(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.sinh <numpy.sinh>`. + + See its docstring for more information. + """ + if x.dtype not in _floating_dtypes: + raise TypeError("Only floating-point dtypes are allowed in sinh") + return Array._new(np.sinh(x._array)) + + +def square(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.square <numpy.square>`. + + See its docstring for more information. + """ + if x.dtype not in _numeric_dtypes: + raise TypeError("Only numeric dtypes are allowed in square") + return Array._new(np.square(x._array)) + + +def sqrt(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.sqrt <numpy.sqrt>`. + + See its docstring for more information. + """ + if x.dtype not in _floating_dtypes: + raise TypeError("Only floating-point dtypes are allowed in sqrt") + return Array._new(np.sqrt(x._array)) + + +def subtract(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.subtract <numpy.subtract>`. + + See its docstring for more information. + """ + if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: + raise TypeError("Only numeric dtypes are allowed in subtract") + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.subtract(x1._array, x2._array)) + + +def tan(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.tan <numpy.tan>`. + + See its docstring for more information. + """ + if x.dtype not in _floating_dtypes: + raise TypeError("Only floating-point dtypes are allowed in tan") + return Array._new(np.tan(x._array)) + + +def tanh(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.tanh <numpy.tanh>`. + + See its docstring for more information. + """ + if x.dtype not in _floating_dtypes: + raise TypeError("Only floating-point dtypes are allowed in tanh") + return Array._new(np.tanh(x._array)) + + +def trunc(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.trunc <numpy.trunc>`. + + See its docstring for more information. + """ + if x.dtype not in _numeric_dtypes: + raise TypeError("Only numeric dtypes are allowed in trunc") + if x.dtype in _integer_dtypes: + # Note: The return dtype of trunc is the same as the input + return x + return Array._new(np.trunc(x._array)) diff --git a/numpy/array_api/_manipulation_functions.py b/numpy/array_api/_manipulation_functions.py new file mode 100644 index 000000000000..4f2114ff5cb6 --- /dev/null +++ b/numpy/array_api/_manipulation_functions.py @@ -0,0 +1,97 @@ +from __future__ import annotations + +from ._array_object import Array +from ._data_type_functions import result_type + +from typing import List, Optional, Tuple, Union + +import numpy as np + +# Note: the function name is different here +def concat( + arrays: Union[Tuple[Array, ...], List[Array]], /, *, axis: Optional[int] = 0 +) -> Array: + """ + Array API compatible wrapper for :py:func:`np.concatenate <numpy.concatenate>`. + + See its docstring for more information. + """ + # Note: Casting rules here are different from the np.concatenate default + # (no for scalars with axis=None, no cross-kind casting) + dtype = result_type(*arrays) + arrays = tuple(a._array for a in arrays) + return Array._new(np.concatenate(arrays, axis=axis, dtype=dtype)) + + +def expand_dims(x: Array, /, *, axis: int) -> Array: + """ + Array API compatible wrapper for :py:func:`np.expand_dims <numpy.expand_dims>`. + + See its docstring for more information. + """ + return Array._new(np.expand_dims(x._array, axis)) + + +def flip(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> Array: + """ + Array API compatible wrapper for :py:func:`np.flip <numpy.flip>`. + + See its docstring for more information. + """ + return Array._new(np.flip(x._array, axis=axis)) + + +# Note: The function name is different here (see also matrix_transpose). +# Unlike transpose(), the axes argument is required. +def permute_dims(x: Array, /, axes: Tuple[int, ...]) -> Array: + """ + Array API compatible wrapper for :py:func:`np.transpose <numpy.transpose>`. + + See its docstring for more information. + """ + return Array._new(np.transpose(x._array, axes)) + + +def reshape(x: Array, /, shape: Tuple[int, ...]) -> Array: + """ + Array API compatible wrapper for :py:func:`np.reshape <numpy.reshape>`. + + See its docstring for more information. + """ + return Array._new(np.reshape(x._array, shape)) + + +def roll( + x: Array, + /, + shift: Union[int, Tuple[int, ...]], + *, + axis: Optional[Union[int, Tuple[int, ...]]] = None, +) -> Array: + """ + Array API compatible wrapper for :py:func:`np.roll <numpy.roll>`. + + See its docstring for more information. + """ + return Array._new(np.roll(x._array, shift, axis=axis)) + + +def squeeze(x: Array, /, axis: Union[int, Tuple[int, ...]]) -> Array: + """ + Array API compatible wrapper for :py:func:`np.squeeze <numpy.squeeze>`. + + See its docstring for more information. + """ + return Array._new(np.squeeze(x._array, axis=axis)) + + +def stack(arrays: Union[Tuple[Array, ...], List[Array]], /, *, axis: int = 0) -> Array: + """ + Array API compatible wrapper for :py:func:`np.stack <numpy.stack>`. + + See its docstring for more information. + """ + # Call result type here just to raise on disallowed type combinations + result_type(*arrays) + arrays = tuple(a._array for a in arrays) + return Array._new(np.stack(arrays, axis=axis)) diff --git a/numpy/array_api/_searching_functions.py b/numpy/array_api/_searching_functions.py new file mode 100644 index 000000000000..40f5a4d2e8fc --- /dev/null +++ b/numpy/array_api/_searching_functions.py @@ -0,0 +1,47 @@ +from __future__ import annotations + +from ._array_object import Array +from ._dtypes import _result_type + +from typing import Optional, Tuple + +import numpy as np + + +def argmax(x: Array, /, *, axis: Optional[int] = None, keepdims: bool = False) -> Array: + """ + Array API compatible wrapper for :py:func:`np.argmax <numpy.argmax>`. + + See its docstring for more information. + """ + return Array._new(np.asarray(np.argmax(x._array, axis=axis, keepdims=keepdims))) + + +def argmin(x: Array, /, *, axis: Optional[int] = None, keepdims: bool = False) -> Array: + """ + Array API compatible wrapper for :py:func:`np.argmin <numpy.argmin>`. + + See its docstring for more information. + """ + return Array._new(np.asarray(np.argmin(x._array, axis=axis, keepdims=keepdims))) + + +def nonzero(x: Array, /) -> Tuple[Array, ...]: + """ + Array API compatible wrapper for :py:func:`np.nonzero <numpy.nonzero>`. + + See its docstring for more information. + """ + return tuple(Array._new(i) for i in np.nonzero(x._array)) + + +def where(condition: Array, x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.where <numpy.where>`. + + See its docstring for more information. + """ + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.where(condition._array, x1._array, x2._array)) diff --git a/numpy/array_api/_set_functions.py b/numpy/array_api/_set_functions.py new file mode 100644 index 000000000000..05ee7e555838 --- /dev/null +++ b/numpy/array_api/_set_functions.py @@ -0,0 +1,92 @@ +from __future__ import annotations + +from ._array_object import Array + +from typing import NamedTuple + +import numpy as np + +# Note: np.unique() is split into four functions in the array API: +# unique_all, unique_counts, unique_inverse, and unique_values (this is done +# to remove polymorphic return types). + +# Note: The various unique() functions are supposed to return multiple NaNs. +# This does not match the NumPy behavior, however, this is currently left as a +# TODO in this implementation as this behavior may be reverted in np.unique(). +# See https://github.com/numpy/numpy/issues/20326. + +# Note: The functions here return a namedtuple (np.unique() returns a normal +# tuple). + +class UniqueAllResult(NamedTuple): + values: Array + indices: Array + inverse_indices: Array + counts: Array + + +class UniqueCountsResult(NamedTuple): + values: Array + counts: Array + + +class UniqueInverseResult(NamedTuple): + values: Array + inverse_indices: Array + + +def unique_all(x: Array, /) -> UniqueAllResult: + """ + Array API compatible wrapper for :py:func:`np.unique <numpy.unique>`. + + See its docstring for more information. + """ + res = np.unique( + x._array, + return_counts=True, + return_index=True, + return_inverse=True, + ) + + return UniqueAllResult(*[Array._new(i) for i in res]) + + +def unique_counts(x: Array, /) -> UniqueCountsResult: + res = np.unique( + x._array, + return_counts=True, + return_index=False, + return_inverse=False, + ) + + return UniqueCountsResult(*[Array._new(i) for i in res]) + + +def unique_inverse(x: Array, /) -> UniqueInverseResult: + """ + Array API compatible wrapper for :py:func:`np.unique <numpy.unique>`. + + See its docstring for more information. + """ + res = np.unique( + x._array, + return_counts=False, + return_index=False, + return_inverse=True, + ) + return UniqueInverseResult(*[Array._new(i) for i in res]) + + +def unique_values(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.unique <numpy.unique>`. + + See its docstring for more information. + """ + res = np.unique( + x._array, + return_counts=False, + return_index=False, + return_inverse=False, + ) + return Array._new(res) diff --git a/numpy/array_api/_sorting_functions.py b/numpy/array_api/_sorting_functions.py new file mode 100644 index 000000000000..9cd49786cb8d --- /dev/null +++ b/numpy/array_api/_sorting_functions.py @@ -0,0 +1,37 @@ +from __future__ import annotations + +from ._array_object import Array + +import numpy as np + + +def argsort( + x: Array, /, *, axis: int = -1, descending: bool = False, stable: bool = True +) -> Array: + """ + Array API compatible wrapper for :py:func:`np.argsort <numpy.argsort>`. + + See its docstring for more information. + """ + # Note: this keyword argument is different, and the default is different. + kind = "stable" if stable else "quicksort" + res = np.argsort(x._array, axis=axis, kind=kind) + if descending: + res = np.flip(res, axis=axis) + return Array._new(res) + + +def sort( + x: Array, /, *, axis: int = -1, descending: bool = False, stable: bool = True +) -> Array: + """ + Array API compatible wrapper for :py:func:`np.sort <numpy.sort>`. + + See its docstring for more information. + """ + # Note: this keyword argument is different, and the default is different. + kind = "stable" if stable else "quicksort" + res = np.sort(x._array, axis=axis, kind=kind) + if descending: + res = np.flip(res, axis=axis) + return Array._new(res) diff --git a/numpy/array_api/_statistical_functions.py b/numpy/array_api/_statistical_functions.py new file mode 100644 index 000000000000..5bc831ac2965 --- /dev/null +++ b/numpy/array_api/_statistical_functions.py @@ -0,0 +1,115 @@ +from __future__ import annotations + +from ._dtypes import ( + _floating_dtypes, + _numeric_dtypes, +) +from ._array_object import Array +from ._creation_functions import asarray +from ._dtypes import float32, float64 + +from typing import TYPE_CHECKING, Optional, Tuple, Union + +if TYPE_CHECKING: + from ._typing import Dtype + +import numpy as np + + +def max( + x: Array, + /, + *, + axis: Optional[Union[int, Tuple[int, ...]]] = None, + keepdims: bool = False, +) -> Array: + if x.dtype not in _numeric_dtypes: + raise TypeError("Only numeric dtypes are allowed in max") + return Array._new(np.max(x._array, axis=axis, keepdims=keepdims)) + + +def mean( + x: Array, + /, + *, + axis: Optional[Union[int, Tuple[int, ...]]] = None, + keepdims: bool = False, +) -> Array: + if x.dtype not in _floating_dtypes: + raise TypeError("Only floating-point dtypes are allowed in mean") + return Array._new(np.mean(x._array, axis=axis, keepdims=keepdims)) + + +def min( + x: Array, + /, + *, + axis: Optional[Union[int, Tuple[int, ...]]] = None, + keepdims: bool = False, +) -> Array: + if x.dtype not in _numeric_dtypes: + raise TypeError("Only numeric dtypes are allowed in min") + return Array._new(np.min(x._array, axis=axis, keepdims=keepdims)) + + +def prod( + x: Array, + /, + *, + axis: Optional[Union[int, Tuple[int, ...]]] = None, + dtype: Optional[Dtype] = None, + keepdims: bool = False, +) -> Array: + if x.dtype not in _numeric_dtypes: + raise TypeError("Only numeric dtypes are allowed in prod") + # Note: sum() and prod() always upcast float32 to float64 for dtype=None + # We need to do so here before computing the product to avoid overflow + if dtype is None and x.dtype == float32: + dtype = float64 + return Array._new(np.prod(x._array, dtype=dtype, axis=axis, keepdims=keepdims)) + + +def std( + x: Array, + /, + *, + axis: Optional[Union[int, Tuple[int, ...]]] = None, + correction: Union[int, float] = 0.0, + keepdims: bool = False, +) -> Array: + # Note: the keyword argument correction is different here + if x.dtype not in _floating_dtypes: + raise TypeError("Only floating-point dtypes are allowed in std") + return Array._new(np.std(x._array, axis=axis, ddof=correction, keepdims=keepdims)) + + +def sum( + x: Array, + /, + *, + axis: Optional[Union[int, Tuple[int, ...]]] = None, + dtype: Optional[Dtype] = None, + keepdims: bool = False, +) -> Array: + if x.dtype not in _numeric_dtypes: + raise TypeError("Only numeric dtypes are allowed in sum") + # Note: sum() and prod() always upcast integers to (u)int64 and float32 to + # float64 for dtype=None. `np.sum` does that too for integers, but not for + # float32, so we need to special-case it here + if dtype is None and x.dtype == float32: + dtype = float64 + return Array._new(np.sum(x._array, axis=axis, dtype=dtype, keepdims=keepdims)) + + +def var( + x: Array, + /, + *, + axis: Optional[Union[int, Tuple[int, ...]]] = None, + correction: Union[int, float] = 0.0, + keepdims: bool = False, +) -> Array: + # Note: the keyword argument correction is different here + if x.dtype not in _floating_dtypes: + raise TypeError("Only floating-point dtypes are allowed in var") + return Array._new(np.var(x._array, axis=axis, ddof=correction, keepdims=keepdims)) diff --git a/numpy/array_api/_typing.py b/numpy/array_api/_typing.py new file mode 100644 index 000000000000..dfa87b3584bf --- /dev/null +++ b/numpy/array_api/_typing.py @@ -0,0 +1,74 @@ +""" +This file defines the types for type annotations. + +These names aren't part of the module namespace, but they are used in the +annotations in the function signatures. The functions in the module are only +valid for inputs that match the given type annotations. +""" + +from __future__ import annotations + +__all__ = [ + "Array", + "Device", + "Dtype", + "SupportsDLPack", + "SupportsBufferProtocol", + "PyCapsule", +] + +import sys +from typing import ( + Any, + Literal, + Sequence, + Type, + Union, + TYPE_CHECKING, + TypeVar, + Protocol, +) + +from ._array_object import Array +from numpy import ( + dtype, + int8, + int16, + int32, + int64, + uint8, + uint16, + uint32, + uint64, + float32, + float64, +) + +_T_co = TypeVar("_T_co", covariant=True) + +class NestedSequence(Protocol[_T_co]): + def __getitem__(self, key: int, /) -> _T_co | NestedSequence[_T_co]: ... + def __len__(self, /) -> int: ... + +Device = Literal["cpu"] +if TYPE_CHECKING or sys.version_info >= (3, 9): + Dtype = dtype[Union[ + int8, + int16, + int32, + int64, + uint8, + uint16, + uint32, + uint64, + float32, + float64, + ]] +else: + Dtype = dtype + +SupportsBufferProtocol = Any +PyCapsule = Any + +class SupportsDLPack(Protocol): + def __dlpack__(self, /, *, stream: None = ...) -> PyCapsule: ... diff --git a/numpy/array_api/_utility_functions.py b/numpy/array_api/_utility_functions.py new file mode 100644 index 000000000000..5ecb4bd9fef7 --- /dev/null +++ b/numpy/array_api/_utility_functions.py @@ -0,0 +1,37 @@ +from __future__ import annotations + +from ._array_object import Array + +from typing import Optional, Tuple, Union + +import numpy as np + + +def all( + x: Array, + /, + *, + axis: Optional[Union[int, Tuple[int, ...]]] = None, + keepdims: bool = False, +) -> Array: + """ + Array API compatible wrapper for :py:func:`np.all <numpy.all>`. + + See its docstring for more information. + """ + return Array._new(np.asarray(np.all(x._array, axis=axis, keepdims=keepdims))) + + +def any( + x: Array, + /, + *, + axis: Optional[Union[int, Tuple[int, ...]]] = None, + keepdims: bool = False, +) -> Array: + """ + Array API compatible wrapper for :py:func:`np.any <numpy.any>`. + + See its docstring for more information. + """ + return Array._new(np.asarray(np.any(x._array, axis=axis, keepdims=keepdims))) diff --git a/numpy/array_api/linalg.py b/numpy/array_api/linalg.py new file mode 100644 index 000000000000..8d7ba659ea9d --- /dev/null +++ b/numpy/array_api/linalg.py @@ -0,0 +1,408 @@ +from __future__ import annotations + +from ._dtypes import _floating_dtypes, _numeric_dtypes +from ._array_object import Array + +from typing import TYPE_CHECKING +if TYPE_CHECKING: + from ._typing import Literal, Optional, Sequence, Tuple, Union + +from typing import NamedTuple + +import numpy.linalg +import numpy as np + +class EighResult(NamedTuple): + eigenvalues: Array + eigenvectors: Array + +class QRResult(NamedTuple): + Q: Array + R: Array + +class SlogdetResult(NamedTuple): + sign: Array + logabsdet: Array + +class SVDResult(NamedTuple): + U: Array + S: Array + Vh: Array + +# Note: the inclusion of the upper keyword is different from +# np.linalg.cholesky, which does not have it. +def cholesky(x: Array, /, *, upper: bool = False) -> Array: + """ + Array API compatible wrapper for :py:func:`np.linalg.cholesky <numpy.linalg.cholesky>`. + + See its docstring for more information. + """ + # Note: the restriction to floating-point dtypes only is different from + # np.linalg.cholesky. + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in cholesky') + L = np.linalg.cholesky(x._array) + if upper: + return Array._new(L).mT + return Array._new(L) + +# Note: cross is the numpy top-level namespace, not np.linalg +def cross(x1: Array, x2: Array, /, *, axis: int = -1) -> Array: + """ + Array API compatible wrapper for :py:func:`np.cross <numpy.cross>`. + + See its docstring for more information. + """ + if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in cross') + # Note: this is different from np.cross(), which broadcasts + if x1.shape != x2.shape: + raise ValueError('x1 and x2 must have the same shape') + if x1.ndim == 0: + raise ValueError('cross() requires arrays of dimension at least 1') + # Note: this is different from np.cross(), which allows dimension 2 + if x1.shape[axis] != 3: + raise ValueError('cross() dimension must equal 3') + return Array._new(np.cross(x1._array, x2._array, axis=axis)) + +def det(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.linalg.det <numpy.linalg.det>`. + + See its docstring for more information. + """ + # Note: the restriction to floating-point dtypes only is different from + # np.linalg.det. + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in det') + return Array._new(np.linalg.det(x._array)) + +# Note: diagonal is the numpy top-level namespace, not np.linalg +def diagonal(x: Array, /, *, offset: int = 0) -> Array: + """ + Array API compatible wrapper for :py:func:`np.diagonal <numpy.diagonal>`. + + See its docstring for more information. + """ + # Note: diagonal always operates on the last two axes, whereas np.diagonal + # operates on the first two axes by default + return Array._new(np.diagonal(x._array, offset=offset, axis1=-2, axis2=-1)) + + +# Note: the keyword argument name upper is different from np.linalg.eigh +def eigh(x: Array, /) -> EighResult: + """ + Array API compatible wrapper for :py:func:`np.linalg.eigh <numpy.linalg.eigh>`. + + See its docstring for more information. + """ + # Note: the restriction to floating-point dtypes only is different from + # np.linalg.eigh. + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in eigh') + + # Note: the return type here is a namedtuple, which is different from + # np.eigh, which only returns a tuple. + return EighResult(*map(Array._new, np.linalg.eigh(x._array))) + + +# Note: the keyword argument name upper is different from np.linalg.eigvalsh +def eigvalsh(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.linalg.eigvalsh <numpy.linalg.eigvalsh>`. + + See its docstring for more information. + """ + # Note: the restriction to floating-point dtypes only is different from + # np.linalg.eigvalsh. + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in eigvalsh') + + return Array._new(np.linalg.eigvalsh(x._array)) + +def inv(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.linalg.inv <numpy.linalg.inv>`. + + See its docstring for more information. + """ + # Note: the restriction to floating-point dtypes only is different from + # np.linalg.inv. + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in inv') + + return Array._new(np.linalg.inv(x._array)) + + +# Note: matmul is the numpy top-level namespace but not in np.linalg +def matmul(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.matmul <numpy.matmul>`. + + See its docstring for more information. + """ + # Note: the restriction to numeric dtypes only is different from + # np.matmul. + if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in matmul') + + return Array._new(np.matmul(x1._array, x2._array)) + + +# Note: the name here is different from norm(). The array API norm is split +# into matrix_norm and vector_norm(). + +# The type for ord should be Optional[Union[int, float, Literal[np.inf, +# -np.inf, 'fro', 'nuc']]], but Literal does not support floating-point +# literals. +def matrix_norm(x: Array, /, *, keepdims: bool = False, ord: Optional[Union[int, float, Literal['fro', 'nuc']]] = 'fro') -> Array: + """ + Array API compatible wrapper for :py:func:`np.linalg.norm <numpy.linalg.norm>`. + + See its docstring for more information. + """ + # Note: the restriction to floating-point dtypes only is different from + # np.linalg.norm. + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in matrix_norm') + + return Array._new(np.linalg.norm(x._array, axis=(-2, -1), keepdims=keepdims, ord=ord)) + + +def matrix_power(x: Array, n: int, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.matrix_power <numpy.matrix_power>`. + + See its docstring for more information. + """ + # Note: the restriction to floating-point dtypes only is different from + # np.linalg.matrix_power. + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed for the first argument of matrix_power') + + # np.matrix_power already checks if n is an integer + return Array._new(np.linalg.matrix_power(x._array, n)) + +# Note: the keyword argument name rtol is different from np.linalg.matrix_rank +def matrix_rank(x: Array, /, *, rtol: Optional[Union[float, Array]] = None) -> Array: + """ + Array API compatible wrapper for :py:func:`np.matrix_rank <numpy.matrix_rank>`. + + See its docstring for more information. + """ + # Note: this is different from np.linalg.matrix_rank, which supports 1 + # dimensional arrays. + if x.ndim < 2: + raise np.linalg.LinAlgError("1-dimensional array given. Array must be at least two-dimensional") + S = np.linalg.svd(x._array, compute_uv=False) + if rtol is None: + tol = S.max(axis=-1, keepdims=True) * max(x.shape[-2:]) * np.finfo(S.dtype).eps + else: + if isinstance(rtol, Array): + rtol = rtol._array + # Note: this is different from np.linalg.matrix_rank, which does not multiply + # the tolerance by the largest singular value. + tol = S.max(axis=-1, keepdims=True)*np.asarray(rtol)[..., np.newaxis] + return Array._new(np.count_nonzero(S > tol, axis=-1)) + + +# Note: this function is new in the array API spec. Unlike transpose, it only +# transposes the last two axes. +def matrix_transpose(x: Array, /) -> Array: + if x.ndim < 2: + raise ValueError("x must be at least 2-dimensional for matrix_transpose") + return Array._new(np.swapaxes(x._array, -1, -2)) + +# Note: outer is the numpy top-level namespace, not np.linalg +def outer(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.outer <numpy.outer>`. + + See its docstring for more information. + """ + # Note: the restriction to numeric dtypes only is different from + # np.outer. + if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in outer') + + # Note: the restriction to only 1-dim arrays is different from np.outer + if x1.ndim != 1 or x2.ndim != 1: + raise ValueError('The input arrays to outer must be 1-dimensional') + + return Array._new(np.outer(x1._array, x2._array)) + +# Note: the keyword argument name rtol is different from np.linalg.pinv +def pinv(x: Array, /, *, rtol: Optional[Union[float, Array]] = None) -> Array: + """ + Array API compatible wrapper for :py:func:`np.linalg.pinv <numpy.linalg.pinv>`. + + See its docstring for more information. + """ + # Note: the restriction to floating-point dtypes only is different from + # np.linalg.pinv. + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in pinv') + + # Note: this is different from np.linalg.pinv, which does not multiply the + # default tolerance by max(M, N). + if rtol is None: + rtol = max(x.shape[-2:]) * np.finfo(x.dtype).eps + return Array._new(np.linalg.pinv(x._array, rcond=rtol)) + +def qr(x: Array, /, *, mode: Literal['reduced', 'complete'] = 'reduced') -> QRResult: + """ + Array API compatible wrapper for :py:func:`np.linalg.qr <numpy.linalg.qr>`. + + See its docstring for more information. + """ + # Note: the restriction to floating-point dtypes only is different from + # np.linalg.qr. + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in qr') + + # Note: the return type here is a namedtuple, which is different from + # np.linalg.qr, which only returns a tuple. + return QRResult(*map(Array._new, np.linalg.qr(x._array, mode=mode))) + +def slogdet(x: Array, /) -> SlogdetResult: + """ + Array API compatible wrapper for :py:func:`np.linalg.slogdet <numpy.linalg.slogdet>`. + + See its docstring for more information. + """ + # Note: the restriction to floating-point dtypes only is different from + # np.linalg.slogdet. + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in slogdet') + + # Note: the return type here is a namedtuple, which is different from + # np.linalg.slogdet, which only returns a tuple. + return SlogdetResult(*map(Array._new, np.linalg.slogdet(x._array))) + +# Note: unlike np.linalg.solve, the array API solve() only accepts x2 as a +# vector when it is exactly 1-dimensional. All other cases treat x2 as a stack +# of matrices. The np.linalg.solve behavior of allowing stacks of both +# matrices and vectors is ambiguous c.f. +# https://github.com/numpy/numpy/issues/15349 and +# https://github.com/data-apis/array-api/issues/285. + +# To workaround this, the below is the code from np.linalg.solve except +# only calling solve1 in the exactly 1D case. +def _solve(a, b): + from ..linalg.linalg import (_makearray, _assert_stacked_2d, + _assert_stacked_square, _commonType, + isComplexType, get_linalg_error_extobj, + _raise_linalgerror_singular) + from ..linalg import _umath_linalg + + a, _ = _makearray(a) + _assert_stacked_2d(a) + _assert_stacked_square(a) + b, wrap = _makearray(b) + t, result_t = _commonType(a, b) + + # This part is different from np.linalg.solve + if b.ndim == 1: + gufunc = _umath_linalg.solve1 + else: + gufunc = _umath_linalg.solve + + # This does nothing currently but is left in because it will be relevant + # when complex dtype support is added to the spec in 2022. + signature = 'DD->D' if isComplexType(t) else 'dd->d' + extobj = get_linalg_error_extobj(_raise_linalgerror_singular) + r = gufunc(a, b, signature=signature, extobj=extobj) + + return wrap(r.astype(result_t, copy=False)) + +def solve(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.linalg.solve <numpy.linalg.solve>`. + + See its docstring for more information. + """ + # Note: the restriction to floating-point dtypes only is different from + # np.linalg.solve. + if x1.dtype not in _floating_dtypes or x2.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in solve') + + return Array._new(_solve(x1._array, x2._array)) + +def svd(x: Array, /, *, full_matrices: bool = True) -> SVDResult: + """ + Array API compatible wrapper for :py:func:`np.linalg.svd <numpy.linalg.svd>`. + + See its docstring for more information. + """ + # Note: the restriction to floating-point dtypes only is different from + # np.linalg.svd. + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in svd') + + # Note: the return type here is a namedtuple, which is different from + # np.svd, which only returns a tuple. + return SVDResult(*map(Array._new, np.linalg.svd(x._array, full_matrices=full_matrices))) + +# Note: svdvals is not in NumPy (but it is in SciPy). It is equivalent to +# np.linalg.svd(compute_uv=False). +def svdvals(x: Array, /) -> Union[Array, Tuple[Array, ...]]: + return Array._new(np.linalg.svd(x._array, compute_uv=False)) + +# Note: tensordot is the numpy top-level namespace but not in np.linalg + +# Note: axes must be a tuple, unlike np.tensordot where it can be an array or array-like. +def tensordot(x1: Array, x2: Array, /, *, axes: Union[int, Tuple[Sequence[int], Sequence[int]]] = 2) -> Array: + # Note: the restriction to numeric dtypes only is different from + # np.tensordot. + if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in tensordot') + + return Array._new(np.tensordot(x1._array, x2._array, axes=axes)) + +# Note: trace is the numpy top-level namespace, not np.linalg +def trace(x: Array, /, *, offset: int = 0) -> Array: + """ + Array API compatible wrapper for :py:func:`np.trace <numpy.trace>`. + + See its docstring for more information. + """ + # Note: trace always operates on the last two axes, whereas np.trace + # operates on the first two axes by default + return Array._new(np.asarray(np.trace(x._array, offset=offset, axis1=-2, axis2=-1))) + +# Note: vecdot is not in NumPy +def vecdot(x1: Array, x2: Array, /, *, axis: int = -1) -> Array: + return tensordot(x1, x2, axes=((axis,), (axis,))) + + +# Note: the name here is different from norm(). The array API norm is split +# into matrix_norm and vector_norm(). + +# The type for ord should be Optional[Union[int, float, Literal[np.inf, +# -np.inf]]] but Literal does not support floating-point literals. +def vector_norm(x: Array, /, *, axis: Optional[Union[int, Tuple[int, int]]] = None, keepdims: bool = False, ord: Optional[Union[int, float]] = 2) -> Array: + """ + Array API compatible wrapper for :py:func:`np.linalg.norm <numpy.linalg.norm>`. + + See its docstring for more information. + """ + # Note: the restriction to floating-point dtypes only is different from + # np.linalg.norm. + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in norm') + + a = x._array + if axis is None: + a = a.flatten() + axis = 0 + elif isinstance(axis, tuple): + # Note: The axis argument supports any number of axes, whereas norm() + # only supports a single axis for vector norm. + rest = tuple(i for i in range(a.ndim) if i not in axis) + newshape = axis + rest + a = np.transpose(a, newshape).reshape((np.prod([a.shape[i] for i in axis]), *[a.shape[i] for i in rest])) + axis = 0 + return Array._new(np.linalg.norm(a, axis=axis, keepdims=keepdims, ord=ord)) + + +__all__ = ['cholesky', 'cross', 'det', 'diagonal', 'eigh', 'eigvalsh', 'inv', 'matmul', 'matrix_norm', 'matrix_power', 'matrix_rank', 'matrix_transpose', 'outer', 'pinv', 'qr', 'slogdet', 'solve', 'svd', 'svdvals', 'tensordot', 'trace', 'vecdot', 'vector_norm'] diff --git a/numpy/array_api/setup.py b/numpy/array_api/setup.py new file mode 100644 index 000000000000..c8bc2910268e --- /dev/null +++ b/numpy/array_api/setup.py @@ -0,0 +1,12 @@ +def configuration(parent_package="", top_path=None): + from numpy.distutils.misc_util import Configuration + + config = Configuration("array_api", parent_package, top_path) + config.add_subpackage("tests") + return config + + +if __name__ == "__main__": + from numpy.distutils.core import setup + + setup(configuration=configuration) diff --git a/numpy/array_api/tests/__init__.py b/numpy/array_api/tests/__init__.py new file mode 100644 index 000000000000..536062e38279 --- /dev/null +++ b/numpy/array_api/tests/__init__.py @@ -0,0 +1,7 @@ +""" +Tests for the array API namespace. + +Note, full compliance with the array API can be tested with the official array API test +suite https://github.com/data-apis/array-api-tests. This test suite primarily +focuses on those things that are not tested by the official test suite. +""" diff --git a/numpy/array_api/tests/test_array_object.py b/numpy/array_api/tests/test_array_object.py new file mode 100644 index 000000000000..b980bacca493 --- /dev/null +++ b/numpy/array_api/tests/test_array_object.py @@ -0,0 +1,324 @@ +import operator + +from numpy.testing import assert_raises +import numpy as np + +from .. import ones, asarray, result_type, all, equal +from .._array_object import Array +from .._dtypes import ( + _all_dtypes, + _boolean_dtypes, + _floating_dtypes, + _integer_dtypes, + _integer_or_boolean_dtypes, + _numeric_dtypes, + int8, + int16, + int32, + int64, + uint64, +) + + +def test_validate_index(): + # The indexing tests in the official array API test suite test that the + # array object correctly handles the subset of indices that are required + # by the spec. But the NumPy array API implementation specifically + # disallows any index not required by the spec, via Array._validate_index. + # This test focuses on testing that non-valid indices are correctly + # rejected. See + # https://data-apis.org/array-api/latest/API_specification/indexing.html + # and the docstring of Array._validate_index for the exact indexing + # behavior that should be allowed. This does not test indices that are + # already invalid in NumPy itself because Array will generally just pass + # such indices directly to the underlying np.ndarray. + + a = ones((3, 4)) + + # Out of bounds slices are not allowed + assert_raises(IndexError, lambda: a[:4]) + assert_raises(IndexError, lambda: a[:-4]) + assert_raises(IndexError, lambda: a[:3:-1]) + assert_raises(IndexError, lambda: a[:-5:-1]) + assert_raises(IndexError, lambda: a[4:]) + assert_raises(IndexError, lambda: a[-4:]) + assert_raises(IndexError, lambda: a[4::-1]) + assert_raises(IndexError, lambda: a[-4::-1]) + + assert_raises(IndexError, lambda: a[...,:5]) + assert_raises(IndexError, lambda: a[...,:-5]) + assert_raises(IndexError, lambda: a[...,:5:-1]) + assert_raises(IndexError, lambda: a[...,:-6:-1]) + assert_raises(IndexError, lambda: a[...,5:]) + assert_raises(IndexError, lambda: a[...,-5:]) + assert_raises(IndexError, lambda: a[...,5::-1]) + assert_raises(IndexError, lambda: a[...,-5::-1]) + + # Boolean indices cannot be part of a larger tuple index + assert_raises(IndexError, lambda: a[a[:,0]==1,0]) + assert_raises(IndexError, lambda: a[a[:,0]==1,...]) + assert_raises(IndexError, lambda: a[..., a[0]==1]) + assert_raises(IndexError, lambda: a[[True, True, True]]) + assert_raises(IndexError, lambda: a[(True, True, True),]) + + # Integer array indices are not allowed (except for 0-D) + idx = asarray([[0, 1]]) + assert_raises(IndexError, lambda: a[idx]) + assert_raises(IndexError, lambda: a[idx,]) + assert_raises(IndexError, lambda: a[[0, 1]]) + assert_raises(IndexError, lambda: a[(0, 1), (0, 1)]) + assert_raises(IndexError, lambda: a[[0, 1]]) + assert_raises(IndexError, lambda: a[np.array([[0, 1]])]) + + # np.newaxis is not allowed + assert_raises(IndexError, lambda: a[None]) + assert_raises(IndexError, lambda: a[None, ...]) + assert_raises(IndexError, lambda: a[..., None]) + + # Multiaxis indices must contain exactly as many indices as dimensions + assert_raises(IndexError, lambda: a[()]) + assert_raises(IndexError, lambda: a[0,]) + assert_raises(IndexError, lambda: a[0]) + assert_raises(IndexError, lambda: a[:]) + +def test_operators(): + # For every operator, we test that it works for the required type + # combinations and raises TypeError otherwise + binary_op_dtypes = { + "__add__": "numeric", + "__and__": "integer_or_boolean", + "__eq__": "all", + "__floordiv__": "numeric", + "__ge__": "numeric", + "__gt__": "numeric", + "__le__": "numeric", + "__lshift__": "integer", + "__lt__": "numeric", + "__mod__": "numeric", + "__mul__": "numeric", + "__ne__": "all", + "__or__": "integer_or_boolean", + "__pow__": "floating", + "__rshift__": "integer", + "__sub__": "numeric", + "__truediv__": "floating", + "__xor__": "integer_or_boolean", + } + + # Recompute each time because of in-place ops + def _array_vals(): + for d in _integer_dtypes: + yield asarray(1, dtype=d) + for d in _boolean_dtypes: + yield asarray(False, dtype=d) + for d in _floating_dtypes: + yield asarray(1.0, dtype=d) + + for op, dtypes in binary_op_dtypes.items(): + ops = [op] + if op not in ["__eq__", "__ne__", "__le__", "__ge__", "__lt__", "__gt__"]: + rop = "__r" + op[2:] + iop = "__i" + op[2:] + ops += [rop, iop] + for s in [1, 1.0, False]: + for _op in ops: + for a in _array_vals(): + # Test array op scalar. From the spec, the following combinations + # are supported: + + # - Python bool for a bool array dtype, + # - a Python int within the bounds of the given dtype for integer array dtypes, + # - a Python int or float for floating-point array dtypes + + # We do not do bounds checking for int scalars, but rather use the default + # NumPy behavior for casting in that case. + + if ((dtypes == "all" + or dtypes == "numeric" and a.dtype in _numeric_dtypes + or dtypes == "integer" and a.dtype in _integer_dtypes + or dtypes == "integer_or_boolean" and a.dtype in _integer_or_boolean_dtypes + or dtypes == "boolean" and a.dtype in _boolean_dtypes + or dtypes == "floating" and a.dtype in _floating_dtypes + ) + # bool is a subtype of int, which is why we avoid + # isinstance here. + and (a.dtype in _boolean_dtypes and type(s) == bool + or a.dtype in _integer_dtypes and type(s) == int + or a.dtype in _floating_dtypes and type(s) in [float, int] + )): + # Only test for no error + getattr(a, _op)(s) + else: + assert_raises(TypeError, lambda: getattr(a, _op)(s)) + + # Test array op array. + for _op in ops: + for x in _array_vals(): + for y in _array_vals(): + # See the promotion table in NEP 47 or the array + # API spec page on type promotion. Mixed kind + # promotion is not defined. + if (x.dtype == uint64 and y.dtype in [int8, int16, int32, int64] + or y.dtype == uint64 and x.dtype in [int8, int16, int32, int64] + or x.dtype in _integer_dtypes and y.dtype not in _integer_dtypes + or y.dtype in _integer_dtypes and x.dtype not in _integer_dtypes + or x.dtype in _boolean_dtypes and y.dtype not in _boolean_dtypes + or y.dtype in _boolean_dtypes and x.dtype not in _boolean_dtypes + or x.dtype in _floating_dtypes and y.dtype not in _floating_dtypes + or y.dtype in _floating_dtypes and x.dtype not in _floating_dtypes + ): + assert_raises(TypeError, lambda: getattr(x, _op)(y)) + # Ensure in-place operators only promote to the same dtype as the left operand. + elif ( + _op.startswith("__i") + and result_type(x.dtype, y.dtype) != x.dtype + ): + assert_raises(TypeError, lambda: getattr(x, _op)(y)) + # Ensure only those dtypes that are required for every operator are allowed. + elif (dtypes == "all" and (x.dtype in _boolean_dtypes and y.dtype in _boolean_dtypes + or x.dtype in _numeric_dtypes and y.dtype in _numeric_dtypes) + or (dtypes == "numeric" and x.dtype in _numeric_dtypes and y.dtype in _numeric_dtypes) + or dtypes == "integer" and x.dtype in _integer_dtypes and y.dtype in _numeric_dtypes + or dtypes == "integer_or_boolean" and (x.dtype in _integer_dtypes and y.dtype in _integer_dtypes + or x.dtype in _boolean_dtypes and y.dtype in _boolean_dtypes) + or dtypes == "boolean" and x.dtype in _boolean_dtypes and y.dtype in _boolean_dtypes + or dtypes == "floating" and x.dtype in _floating_dtypes and y.dtype in _floating_dtypes + ): + getattr(x, _op)(y) + else: + assert_raises(TypeError, lambda: getattr(x, _op)(y)) + + unary_op_dtypes = { + "__abs__": "numeric", + "__invert__": "integer_or_boolean", + "__neg__": "numeric", + "__pos__": "numeric", + } + for op, dtypes in unary_op_dtypes.items(): + for a in _array_vals(): + if ( + dtypes == "numeric" + and a.dtype in _numeric_dtypes + or dtypes == "integer_or_boolean" + and a.dtype in _integer_or_boolean_dtypes + ): + # Only test for no error + getattr(a, op)() + else: + assert_raises(TypeError, lambda: getattr(a, op)()) + + # Finally, matmul() must be tested separately, because it works a bit + # different from the other operations. + def _matmul_array_vals(): + for a in _array_vals(): + yield a + for d in _all_dtypes: + yield ones((3, 4), dtype=d) + yield ones((4, 2), dtype=d) + yield ones((4, 4), dtype=d) + + # Scalars always error + for _op in ["__matmul__", "__rmatmul__", "__imatmul__"]: + for s in [1, 1.0, False]: + for a in _matmul_array_vals(): + if (type(s) in [float, int] and a.dtype in _floating_dtypes + or type(s) == int and a.dtype in _integer_dtypes): + # Type promotion is valid, but @ is not allowed on 0-D + # inputs, so the error is a ValueError + assert_raises(ValueError, lambda: getattr(a, _op)(s)) + else: + assert_raises(TypeError, lambda: getattr(a, _op)(s)) + + for x in _matmul_array_vals(): + for y in _matmul_array_vals(): + if (x.dtype == uint64 and y.dtype in [int8, int16, int32, int64] + or y.dtype == uint64 and x.dtype in [int8, int16, int32, int64] + or x.dtype in _integer_dtypes and y.dtype not in _integer_dtypes + or y.dtype in _integer_dtypes and x.dtype not in _integer_dtypes + or x.dtype in _floating_dtypes and y.dtype not in _floating_dtypes + or y.dtype in _floating_dtypes and x.dtype not in _floating_dtypes + or x.dtype in _boolean_dtypes + or y.dtype in _boolean_dtypes + ): + assert_raises(TypeError, lambda: x.__matmul__(y)) + assert_raises(TypeError, lambda: y.__rmatmul__(x)) + assert_raises(TypeError, lambda: x.__imatmul__(y)) + elif x.shape == () or y.shape == () or x.shape[1] != y.shape[0]: + assert_raises(ValueError, lambda: x.__matmul__(y)) + assert_raises(ValueError, lambda: y.__rmatmul__(x)) + if result_type(x.dtype, y.dtype) != x.dtype: + assert_raises(TypeError, lambda: x.__imatmul__(y)) + else: + assert_raises(ValueError, lambda: x.__imatmul__(y)) + else: + x.__matmul__(y) + y.__rmatmul__(x) + if result_type(x.dtype, y.dtype) != x.dtype: + assert_raises(TypeError, lambda: x.__imatmul__(y)) + elif y.shape[0] != y.shape[1]: + # This one fails because x @ y has a different shape from x + assert_raises(ValueError, lambda: x.__imatmul__(y)) + else: + x.__imatmul__(y) + + +def test_python_scalar_construtors(): + b = asarray(False) + i = asarray(0) + f = asarray(0.0) + + assert bool(b) == False + assert int(i) == 0 + assert float(f) == 0.0 + assert operator.index(i) == 0 + + # bool/int/float should only be allowed on 0-D arrays. + assert_raises(TypeError, lambda: bool(asarray([False]))) + assert_raises(TypeError, lambda: int(asarray([0]))) + assert_raises(TypeError, lambda: float(asarray([0.0]))) + assert_raises(TypeError, lambda: operator.index(asarray([0]))) + + # bool/int/float should only be allowed on arrays of the corresponding + # dtype + assert_raises(ValueError, lambda: bool(i)) + assert_raises(ValueError, lambda: bool(f)) + + assert_raises(ValueError, lambda: int(b)) + assert_raises(ValueError, lambda: int(f)) + + assert_raises(ValueError, lambda: float(b)) + assert_raises(ValueError, lambda: float(i)) + + assert_raises(TypeError, lambda: operator.index(b)) + assert_raises(TypeError, lambda: operator.index(f)) + + +def test_device_property(): + a = ones((3, 4)) + assert a.device == 'cpu' + + assert all(equal(a.to_device('cpu'), a)) + assert_raises(ValueError, lambda: a.to_device('gpu')) + + assert all(equal(asarray(a, device='cpu'), a)) + assert_raises(ValueError, lambda: asarray(a, device='gpu')) + +def test_array_properties(): + a = ones((1, 2, 3)) + b = ones((2, 3)) + assert_raises(ValueError, lambda: a.T) + + assert isinstance(b.T, Array) + assert b.T.shape == (3, 2) + + assert isinstance(a.mT, Array) + assert a.mT.shape == (1, 3, 2) + assert isinstance(b.mT, Array) + assert b.mT.shape == (3, 2) + +def test___array__(): + a = ones((2, 3), dtype=int16) + assert np.asarray(a) is a._array + b = np.asarray(a, dtype=np.float64) + assert np.all(np.equal(b, np.ones((2, 3), dtype=np.float64))) + assert b.dtype == np.float64 diff --git a/numpy/array_api/tests/test_creation_functions.py b/numpy/array_api/tests/test_creation_functions.py new file mode 100644 index 000000000000..be9eaa38378c --- /dev/null +++ b/numpy/array_api/tests/test_creation_functions.py @@ -0,0 +1,142 @@ +from numpy.testing import assert_raises +import numpy as np + +from .. import all +from .._creation_functions import ( + asarray, + arange, + empty, + empty_like, + eye, + full, + full_like, + linspace, + meshgrid, + ones, + ones_like, + zeros, + zeros_like, +) +from .._dtypes import float32, float64 +from .._array_object import Array + + +def test_asarray_errors(): + # Test various protections against incorrect usage + assert_raises(TypeError, lambda: Array([1])) + assert_raises(TypeError, lambda: asarray(["a"])) + assert_raises(ValueError, lambda: asarray([1.0], dtype=np.float16)) + assert_raises(OverflowError, lambda: asarray(2**100)) + # Preferably this would be OverflowError + # assert_raises(OverflowError, lambda: asarray([2**100])) + assert_raises(TypeError, lambda: asarray([2**100])) + asarray([1], device="cpu") # Doesn't error + assert_raises(ValueError, lambda: asarray([1], device="gpu")) + + assert_raises(ValueError, lambda: asarray([1], dtype=int)) + assert_raises(ValueError, lambda: asarray([1], dtype="i")) + + +def test_asarray_copy(): + a = asarray([1]) + b = asarray(a, copy=True) + a[0] = 0 + assert all(b[0] == 1) + assert all(a[0] == 0) + a = asarray([1]) + b = asarray(a, copy=np._CopyMode.ALWAYS) + a[0] = 0 + assert all(b[0] == 1) + assert all(a[0] == 0) + a = asarray([1]) + b = asarray(a, copy=np._CopyMode.NEVER) + a[0] = 0 + assert all(b[0] == 0) + assert_raises(NotImplementedError, lambda: asarray(a, copy=False)) + assert_raises(NotImplementedError, + lambda: asarray(a, copy=np._CopyMode.IF_NEEDED)) + + +def test_arange_errors(): + arange(1, device="cpu") # Doesn't error + assert_raises(ValueError, lambda: arange(1, device="gpu")) + assert_raises(ValueError, lambda: arange(1, dtype=int)) + assert_raises(ValueError, lambda: arange(1, dtype="i")) + + +def test_empty_errors(): + empty((1,), device="cpu") # Doesn't error + assert_raises(ValueError, lambda: empty((1,), device="gpu")) + assert_raises(ValueError, lambda: empty((1,), dtype=int)) + assert_raises(ValueError, lambda: empty((1,), dtype="i")) + + +def test_empty_like_errors(): + empty_like(asarray(1), device="cpu") # Doesn't error + assert_raises(ValueError, lambda: empty_like(asarray(1), device="gpu")) + assert_raises(ValueError, lambda: empty_like(asarray(1), dtype=int)) + assert_raises(ValueError, lambda: empty_like(asarray(1), dtype="i")) + + +def test_eye_errors(): + eye(1, device="cpu") # Doesn't error + assert_raises(ValueError, lambda: eye(1, device="gpu")) + assert_raises(ValueError, lambda: eye(1, dtype=int)) + assert_raises(ValueError, lambda: eye(1, dtype="i")) + + +def test_full_errors(): + full((1,), 0, device="cpu") # Doesn't error + assert_raises(ValueError, lambda: full((1,), 0, device="gpu")) + assert_raises(ValueError, lambda: full((1,), 0, dtype=int)) + assert_raises(ValueError, lambda: full((1,), 0, dtype="i")) + + +def test_full_like_errors(): + full_like(asarray(1), 0, device="cpu") # Doesn't error + assert_raises(ValueError, lambda: full_like(asarray(1), 0, device="gpu")) + assert_raises(ValueError, lambda: full_like(asarray(1), 0, dtype=int)) + assert_raises(ValueError, lambda: full_like(asarray(1), 0, dtype="i")) + + +def test_linspace_errors(): + linspace(0, 1, 10, device="cpu") # Doesn't error + assert_raises(ValueError, lambda: linspace(0, 1, 10, device="gpu")) + assert_raises(ValueError, lambda: linspace(0, 1, 10, dtype=float)) + assert_raises(ValueError, lambda: linspace(0, 1, 10, dtype="f")) + + +def test_ones_errors(): + ones((1,), device="cpu") # Doesn't error + assert_raises(ValueError, lambda: ones((1,), device="gpu")) + assert_raises(ValueError, lambda: ones((1,), dtype=int)) + assert_raises(ValueError, lambda: ones((1,), dtype="i")) + + +def test_ones_like_errors(): + ones_like(asarray(1), device="cpu") # Doesn't error + assert_raises(ValueError, lambda: ones_like(asarray(1), device="gpu")) + assert_raises(ValueError, lambda: ones_like(asarray(1), dtype=int)) + assert_raises(ValueError, lambda: ones_like(asarray(1), dtype="i")) + + +def test_zeros_errors(): + zeros((1,), device="cpu") # Doesn't error + assert_raises(ValueError, lambda: zeros((1,), device="gpu")) + assert_raises(ValueError, lambda: zeros((1,), dtype=int)) + assert_raises(ValueError, lambda: zeros((1,), dtype="i")) + + +def test_zeros_like_errors(): + zeros_like(asarray(1), device="cpu") # Doesn't error + assert_raises(ValueError, lambda: zeros_like(asarray(1), device="gpu")) + assert_raises(ValueError, lambda: zeros_like(asarray(1), dtype=int)) + assert_raises(ValueError, lambda: zeros_like(asarray(1), dtype="i")) + +def test_meshgrid_dtype_errors(): + # Doesn't raise + meshgrid() + meshgrid(asarray([1.], dtype=float32)) + meshgrid(asarray([1.], dtype=float32), asarray([1.], dtype=float32)) + + assert_raises(ValueError, lambda: meshgrid(asarray([1.], dtype=float32), asarray([1.], dtype=float64))) diff --git a/numpy/array_api/tests/test_elementwise_functions.py b/numpy/array_api/tests/test_elementwise_functions.py new file mode 100644 index 000000000000..a9274aec9278 --- /dev/null +++ b/numpy/array_api/tests/test_elementwise_functions.py @@ -0,0 +1,111 @@ +from inspect import getfullargspec + +from numpy.testing import assert_raises + +from .. import asarray, _elementwise_functions +from .._elementwise_functions import bitwise_left_shift, bitwise_right_shift +from .._dtypes import ( + _dtype_categories, + _boolean_dtypes, + _floating_dtypes, + _integer_dtypes, +) + + +def nargs(func): + return len(getfullargspec(func).args) + + +def test_function_types(): + # Test that every function accepts only the required input types. We only + # test the negative cases here (error). The positive cases are tested in + # the array API test suite. + + elementwise_function_input_types = { + "abs": "numeric", + "acos": "floating-point", + "acosh": "floating-point", + "add": "numeric", + "asin": "floating-point", + "asinh": "floating-point", + "atan": "floating-point", + "atan2": "floating-point", + "atanh": "floating-point", + "bitwise_and": "integer or boolean", + "bitwise_invert": "integer or boolean", + "bitwise_left_shift": "integer", + "bitwise_or": "integer or boolean", + "bitwise_right_shift": "integer", + "bitwise_xor": "integer or boolean", + "ceil": "numeric", + "cos": "floating-point", + "cosh": "floating-point", + "divide": "floating-point", + "equal": "all", + "exp": "floating-point", + "expm1": "floating-point", + "floor": "numeric", + "floor_divide": "numeric", + "greater": "numeric", + "greater_equal": "numeric", + "isfinite": "numeric", + "isinf": "numeric", + "isnan": "numeric", + "less": "numeric", + "less_equal": "numeric", + "log": "floating-point", + "logaddexp": "floating-point", + "log10": "floating-point", + "log1p": "floating-point", + "log2": "floating-point", + "logical_and": "boolean", + "logical_not": "boolean", + "logical_or": "boolean", + "logical_xor": "boolean", + "multiply": "numeric", + "negative": "numeric", + "not_equal": "all", + "positive": "numeric", + "pow": "floating-point", + "remainder": "numeric", + "round": "numeric", + "sign": "numeric", + "sin": "floating-point", + "sinh": "floating-point", + "sqrt": "floating-point", + "square": "numeric", + "subtract": "numeric", + "tan": "floating-point", + "tanh": "floating-point", + "trunc": "numeric", + } + + def _array_vals(): + for d in _integer_dtypes: + yield asarray(1, dtype=d) + for d in _boolean_dtypes: + yield asarray(False, dtype=d) + for d in _floating_dtypes: + yield asarray(1.0, dtype=d) + + for x in _array_vals(): + for func_name, types in elementwise_function_input_types.items(): + dtypes = _dtype_categories[types] + func = getattr(_elementwise_functions, func_name) + if nargs(func) == 2: + for y in _array_vals(): + if x.dtype not in dtypes or y.dtype not in dtypes: + assert_raises(TypeError, lambda: func(x, y)) + else: + if x.dtype not in dtypes: + assert_raises(TypeError, lambda: func(x)) + + +def test_bitwise_shift_error(): + # bitwise shift functions should raise when the second argument is negative + assert_raises( + ValueError, lambda: bitwise_left_shift(asarray([1, 1]), asarray([1, -1])) + ) + assert_raises( + ValueError, lambda: bitwise_right_shift(asarray([1, 1]), asarray([1, -1])) + ) diff --git a/numpy/compat/__init__.py b/numpy/compat/__init__.py index 5b371f5c064b..afee621b8726 100644 --- a/numpy/compat/__init__.py +++ b/numpy/compat/__init__.py @@ -8,8 +8,6 @@ * we may only need a small subset of the copied library/module """ -from __future__ import division, absolute_import, print_function - from . import _inspect from . import py3k from ._inspect import getargspec, formatargspec diff --git a/numpy/compat/_inspect.py b/numpy/compat/_inspect.py index 76bf544a5df0..9a874a71dd0a 100644 --- a/numpy/compat/_inspect.py +++ b/numpy/compat/_inspect.py @@ -5,8 +5,6 @@ no overhead. """ -from __future__ import division, absolute_import, print_function - import types __all__ = ['getargspec', 'formatargspec'] @@ -184,9 +182,8 @@ def formatargvalues(args, varargs, varkw, locals, def convert(name, locals=locals, formatarg=formatarg, formatvalue=formatvalue): return formatarg(name) + formatvalue(locals[name]) - specs = [] - for i in range(len(args)): - specs.append(strseq(args[i], convert, join)) + specs = [strseq(arg, convert, join) for arg in args] + if varargs: specs.append(formatvarargs(varargs) + formatvalue(locals[varargs])) if varkw: diff --git a/numpy/compat/py3k.py b/numpy/compat/py3k.py index d5bb2e4c7db4..3d10bb988c2f 100644 --- a/numpy/compat/py3k.py +++ b/numpy/compat/py3k.py @@ -1,80 +1,61 @@ """ -Python 3 compatibility tools. +Python 3.X compatibility tools. -""" -from __future__ import division, absolute_import, print_function +While this file was originally intended for Python 2 -> 3 transition, +it is now used to create a compatibility layer between different +minor versions of Python 3. +While the active version of numpy may not support a given version of python, we +allow downstream libraries to continue to use these shims for forward +compatibility with numpy while they transition their code to newer versions of +Python. +""" __all__ = ['bytes', 'asbytes', 'isfileobj', 'getexception', 'strchar', 'unicode', 'asunicode', 'asbytes_nested', 'asunicode_nested', 'asstr', 'open_latin1', 'long', 'basestring', 'sixu', - 'integer_types', 'is_pathlib_path', 'npy_load_module', 'Path'] + 'integer_types', 'is_pathlib_path', 'npy_load_module', 'Path', + 'pickle', 'contextlib_nullcontext', 'os_fspath', 'os_PathLike'] import sys +import os +from pathlib import Path +import io try: - from pathlib import Path + import pickle5 as pickle except ImportError: - Path = None - -if sys.version_info[0] >= 3: - import io - - long = int - integer_types = (int,) - basestring = str - unicode = str - bytes = bytes + import pickle - def asunicode(s): - if isinstance(s, bytes): - return s.decode('latin1') - return str(s) +long = int +integer_types = (int,) +basestring = str +unicode = str +bytes = bytes - def asbytes(s): - if isinstance(s, bytes): - return s - return str(s).encode('latin1') +def asunicode(s): + if isinstance(s, bytes): + return s.decode('latin1') + return str(s) - def asstr(s): - if isinstance(s, bytes): - return s.decode('latin1') - return str(s) - - def isfileobj(f): - return isinstance(f, (io.FileIO, io.BufferedReader, io.BufferedWriter)) - - def open_latin1(filename, mode='r'): - return open(filename, mode=mode, encoding='iso-8859-1') - - def sixu(s): +def asbytes(s): + if isinstance(s, bytes): return s + return str(s).encode('latin1') - strchar = 'U' - +def asstr(s): + if isinstance(s, bytes): + return s.decode('latin1') + return str(s) -else: - bytes = str - long = long - basestring = basestring - unicode = unicode - integer_types = (int, long) - asbytes = str - asstr = str - strchar = 'S' +def isfileobj(f): + return isinstance(f, (io.FileIO, io.BufferedReader, io.BufferedWriter)) - def isfileobj(f): - return isinstance(f, file) +def open_latin1(filename, mode='r'): + return open(filename, mode=mode, encoding='iso-8859-1') - def asunicode(s): - if isinstance(s, unicode): - return s - return str(s).decode('ascii') - - def open_latin1(filename, mode='r'): - return open(filename, mode=mode) - - def sixu(s): - return unicode(s, 'unicode_escape') +def sixu(s): + return s +strchar = 'U' def getexception(): return sys.exc_info()[1] @@ -93,64 +74,64 @@ def asunicode_nested(x): def is_pathlib_path(obj): """ - Check whether obj is a pathlib.Path object. + Check whether obj is a `pathlib.Path` object. + + Prefer using ``isinstance(obj, os.PathLike)`` instead of this function. """ - return Path is not None and isinstance(obj, Path) - -if sys.version_info[0] >= 3 and sys.version_info[1] >= 4: - def npy_load_module(name, fn, info=None): - """ - Load a module. - - .. versionadded:: 1.11.2 - - Parameters - ---------- - name : str - Full module name. - fn : str - Path to module file. - info : tuple, optional - Only here for backward compatibility with Python 2.*. - - Returns - ------- - mod : module - - """ - import importlib.machinery - return importlib.machinery.SourceFileLoader(name, fn).load_module() -else: - def npy_load_module(name, fn, info=None): - """ - Load a module. - - .. versionadded:: 1.11.2 - - Parameters - ---------- - name : str - Full module name. - fn : str - Path to module file. - info : tuple, optional - Information as returned by `imp.find_module` - (suffix, mode, type). - - Returns - ------- - mod : module - - """ - import imp - import os - if info is None: - path = os.path.dirname(fn) - fo, fn, info = imp.find_module(name, [path]) - else: - fo = open(fn, info[1]) - try: - mod = imp.load_module(name, fo, fn, info) - finally: - fo.close() - return mod + return isinstance(obj, Path) + +# from Python 3.7 +class contextlib_nullcontext: + """Context manager that does no additional processing. + + Used as a stand-in for a normal context manager, when a particular + block of code is only sometimes used with a normal context manager: + + cm = optional_cm if condition else nullcontext() + with cm: + # Perform operation, using optional_cm if condition is True + + .. note:: + Prefer using `contextlib.nullcontext` instead of this context manager. + """ + + def __init__(self, enter_result=None): + self.enter_result = enter_result + + def __enter__(self): + return self.enter_result + + def __exit__(self, *excinfo): + pass + + +def npy_load_module(name, fn, info=None): + """ + Load a module. Uses ``load_module`` which will be deprecated in python + 3.12. An alternative that uses ``exec_module`` is in + numpy.distutils.misc_util.exec_mod_from_location + + .. versionadded:: 1.11.2 + + Parameters + ---------- + name : str + Full module name. + fn : str + Path to module file. + info : tuple, optional + Only here for backward compatibility with Python 2.*. + + Returns + ------- + mod : module + + """ + # Explicitly lazy import this to avoid paying the cost + # of importing importlib at startup + from importlib.machinery import SourceFileLoader + return SourceFileLoader(name, fn).load_module() + + +os_fspath = os.fspath +os_PathLike = os.PathLike diff --git a/numpy/compat/setup.py b/numpy/compat/setup.py index 882857428cdf..c1b34a2cc952 100644 --- a/numpy/compat/setup.py +++ b/numpy/compat/setup.py @@ -1,10 +1,8 @@ -from __future__ import division, print_function - def configuration(parent_package='',top_path=None): from numpy.distutils.misc_util import Configuration config = Configuration('compat', parent_package, top_path) - config.add_data_dir('tests') + config.add_subpackage('tests') return config if __name__ == '__main__': diff --git a/numpy/compat/tests/test_compat.py b/numpy/compat/tests/test_compat.py index 1543aafaf540..2b8acbaa0662 100644 --- a/numpy/compat/tests/test_compat.py +++ b/numpy/compat/tests/test_compat.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - from os.path import join from numpy.compat import isfileobj diff --git a/numpy/conftest.py b/numpy/conftest.py index 7b177174886a..fd5fdd77d224 100644 --- a/numpy/conftest.py +++ b/numpy/conftest.py @@ -1,12 +1,12 @@ """ Pytest configuration and fixtures for the Numpy test suite. """ -from __future__ import division, absolute_import, print_function +import os +import tempfile -import warnings +import hypothesis import pytest import numpy -import importlib from numpy.core._multiarray_tests import get_fpu_mode @@ -14,6 +14,59 @@ _old_fpu_mode = None _collect_results = {} +# Use a known and persistent tmpdir for hypothesis' caches, which +# can be automatically cleared by the OS or user. +hypothesis.configuration.set_hypothesis_home_dir( + os.path.join(tempfile.gettempdir(), ".hypothesis") +) + +# We register two custom profiles for Numpy - for details see +# https://hypothesis.readthedocs.io/en/latest/settings.html +# The first is designed for our own CI runs; the latter also +# forces determinism and is designed for use via np.test() +hypothesis.settings.register_profile( + name="numpy-profile", deadline=None, print_blob=True, +) +hypothesis.settings.register_profile( + name="np.test() profile", + deadline=None, print_blob=True, database=None, derandomize=True, + suppress_health_check=hypothesis.HealthCheck.all(), +) +# Note that the default profile is chosen based on the presence +# of pytest.ini, but can be overridden by passing the +# --hypothesis-profile=NAME argument to pytest. +_pytest_ini = os.path.join(os.path.dirname(__file__), "..", "pytest.ini") +hypothesis.settings.load_profile( + "numpy-profile" if os.path.isfile(_pytest_ini) else "np.test() profile" +) + + +def pytest_configure(config): + config.addinivalue_line("markers", + "valgrind_error: Tests that are known to error under valgrind.") + config.addinivalue_line("markers", + "leaks_references: Tests that are known to leak references.") + config.addinivalue_line("markers", + "slow: Tests that are very slow.") + config.addinivalue_line("markers", + "slow_pypy: Tests that are very slow on pypy.") + + +def pytest_addoption(parser): + parser.addoption("--available-memory", action="store", default=None, + help=("Set amount of memory available for running the " + "test suite. This can result to tests requiring " + "especially large amounts of memory to be skipped. " + "Equivalent to setting environment variable " + "NPY_AVAILABLE_MEM. Default: determined" + "automatically.")) + + +def pytest_sessionstart(session): + available_mem = session.config.getoption('available_memory') + if available_mem is not None: + os.environ['NPY_AVAILABLE_MEM'] = available_mem + #FIXME when yield tests are gone. @pytest.hookimpl() @@ -60,3 +113,7 @@ def check_fpu_mode(request): @pytest.fixture(autouse=True) def add_np(doctest_namespace): doctest_namespace['np'] = numpy + +@pytest.fixture(autouse=True) +def env_setup(monkeypatch): + monkeypatch.setenv('PYTHONHASHSEED', '0') diff --git a/numpy/core/__init__.py b/numpy/core/__init__.py index 4d9cbf5dac01..b89e27f0f765 100644 --- a/numpy/core/__init__.py +++ b/numpy/core/__init__.py @@ -1,11 +1,18 @@ -from __future__ import division, absolute_import, print_function +""" +Contains the core of NumPy: ndarray, ufuncs, dtypes, etc. + +Please note that this module is private. All functions and objects +are available in the main ``numpy`` namespace - use that instead. + +""" -from .info import __doc__ from numpy.version import version as __version__ +import os +import warnings + # disables OpenBLAS affinity setting of the main thread that limits # python threads or processes to one core -import os env_added = [] for envkey in ['OPENBLAS_MAIN_FREE', 'GOTOBLAS_MAIN_FREE']: if envkey not in os.environ: @@ -15,14 +22,30 @@ try: from . import multiarray except ImportError as exc: + import sys msg = """ -Importing the multiarray numpy extension module failed. Most -likely you are trying to import a failed build of numpy. -If you're working with a numpy git repo, try `git clean -xdf` (removes all -files not under version control). Otherwise reinstall numpy. + +IMPORTANT: PLEASE READ THIS FOR ADVICE ON HOW TO SOLVE THIS ISSUE! + +Importing the numpy C-extensions failed. This error can happen for +many reasons, often due to issues with your setup or how NumPy was +installed. + +We have compiled some common reasons and troubleshooting tips at: + + https://numpy.org/devdocs/user/troubleshooting-importerror.html + +Please note and check the following: + + * The Python version is: Python%d.%d from "%s" + * The NumPy version is: "%s" + +and make sure that they are the versions you expect. +Please carefully study the documentation linked above for further help. Original error was: %s -""" % (exc,) +""" % (sys.version_info[0], sys.version_info[1], sys.executable, + __version__, exc) raise ImportError(msg) finally: for envkey in env_added: @@ -32,7 +55,19 @@ del os from . import umath -from . import _internal # for freeze programs + +# Check that multiarray,umath are pure python modules wrapping +# _multiarray_umath and not either of the old c-extension modules +if not (hasattr(multiarray, '_multiarray_umath') and + hasattr(umath, '_multiarray_umath')): + import sys + path = sys.modules['numpy'].__path__ + msg = ("Something is wrong with the numpy installation. " + "While importing we detected an older version of " + "numpy in {}. One method of fixing this is to repeatedly uninstall " + "numpy until none is found, then reinstall this version.") + raise ImportError(msg.format(path)) + from . import numerictypes as nt multiarray.set_typeDict(nt.sctypeDict) from . import numeric @@ -41,13 +76,13 @@ from .fromnumeric import * from . import defchararray as char from . import records as rec -from .records import * +from .records import record, recarray, format_parser from .memmap import * from .defchararray import chararray from . import function_base from .function_base import * -from . import machar -from .machar import * +from . import _machar +from ._machar import * from . import getlimits from .getlimits import * from . import shape_base @@ -59,21 +94,28 @@ from .fromnumeric import amax as max, amin as min, round_ as round from .numeric import absolute as abs +# do this after everything else, to minimize the chance of this misleadingly +# appearing in an import-time traceback +from . import _add_newdocs +from . import _add_newdocs_scalars +# add these for module-freeze analysis (like PyInstaller) +from . import _dtype_ctypes +from . import _internal +from . import _dtype +from . import _methods + __all__ = ['char', 'rec', 'memmap'] __all__ += numeric.__all__ -__all__ += fromnumeric.__all__ -__all__ += rec.__all__ +__all__ += ['record', 'recarray', 'format_parser'] __all__ += ['chararray'] __all__ += function_base.__all__ -__all__ += machar.__all__ __all__ += getlimits.__all__ __all__ += shape_base.__all__ __all__ += einsumfunc.__all__ -# Make it possible so that ufuncs can be pickled -# Here are the loading and unloading functions -# The name numpy.core._ufunc_reconstruct must be -# available for unpickling to work. +# We used to use `np.core._ufunc_reconstruct` to unpickle. This is unnecessary, +# but old pickles saved before 1.20 will be using it, and there is no reason +# to break loading them. def _ufunc_reconstruct(module, name): # The `fromlist` kwarg is required to ensure that `mod` points to the # inner-most module rather than the parent package when module name is @@ -82,24 +124,53 @@ def _ufunc_reconstruct(module, name): mod = __import__(module, fromlist=[name]) return getattr(mod, name) + def _ufunc_reduce(func): - from pickle import whichmodule - name = func.__name__ - return _ufunc_reconstruct, (whichmodule(func, name), name) + # Report the `__name__`. pickle will try to find the module. Note that + # pickle supports for this `__name__` to be a `__qualname__`. It may + # make sense to add a `__qualname__` to ufuncs, to allow this more + # explicitly (Numba has ufuncs as attributes). + # See also: https://github.com/dask/distributed/issues/3450 + return func.__name__ + + +def _DType_reconstruct(scalar_type): + # This is a work-around to pickle type(np.dtype(np.float64)), etc. + # and it should eventually be replaced with a better solution, e.g. when + # DTypes become HeapTypes. + return type(dtype(scalar_type)) + + +def _DType_reduce(DType): + # To pickle a DType without having to add top-level names, pickle the + # scalar type for now (and assume that reconstruction will be possible). + if DType is dtype: + return "dtype" # must pickle `np.dtype` as a singleton. + scalar_type = DType.type # pickle the scalar type for reconstruction + return _DType_reconstruct, (scalar_type,) + + +def __getattr__(name): + # Deprecated 2021-10-20, NumPy 1.22 + if name == "machar": + warnings.warn( + "The `np.core.machar` module is deprecated (NumPy 1.22)", + DeprecationWarning, stacklevel=2, + ) + return _machar + raise AttributeError(f"Module {__name__!r} has no attribute {name!r}") + +import copyreg -import sys -if sys.version_info[0] >= 3: - import copyreg -else: - import copy_reg as copyreg +copyreg.pickle(ufunc, _ufunc_reduce) +copyreg.pickle(type(dtype), _DType_reduce, _DType_reconstruct) -copyreg.pickle(ufunc, _ufunc_reduce, _ufunc_reconstruct) -# Unclutter namespace (must keep _ufunc_reconstruct for unpickling) +# Unclutter namespace (must keep _*_reconstruct for unpickling) del copyreg -del sys del _ufunc_reduce +del _DType_reduce -from numpy.testing._private.pytesttester import PytestTester +from numpy._pytesttester import PytestTester test = PytestTester(__name__) del PytestTester diff --git a/numpy/core/__init__.pyi b/numpy/core/__init__.pyi new file mode 100644 index 000000000000..4c7a42bf3db4 --- /dev/null +++ b/numpy/core/__init__.pyi @@ -0,0 +1,2 @@ +# NOTE: The `np.core` namespace is deliberately kept empty due to it +# being private (despite the lack of leading underscore) diff --git a/numpy/add_newdocs.py b/numpy/core/_add_newdocs.py similarity index 58% rename from numpy/add_newdocs.py rename to numpy/core/_add_newdocs.py index 9372b343137d..078c5897678c 100644 --- a/numpy/add_newdocs.py +++ b/numpy/core/_add_newdocs.py @@ -8,9 +8,9 @@ core/fromnumeric.py, core/defmatrix.py up-to-date. """ -from __future__ import division, absolute_import, print_function -from numpy.lib import add_newdoc +from numpy.core.function_base import add_newdoc +from numpy.core.overrides import array_function_like_doc ############################################################################### # @@ -47,7 +47,7 @@ >>> x = np.arange(6).reshape(2, 3) >>> fl = x.flat >>> type(fl) - <type 'numpy.flatiter'> + <class 'numpy.flatiter'> >>> for item in fl: ... print(item) ... @@ -90,7 +90,7 @@ >>> fl = x.flat >>> fl.coords (0, 0) - >>> fl.next() + >>> next(fl) 0 >>> fl.coords (0, 1) @@ -109,7 +109,7 @@ >>> fl = x.flat >>> fl.index 0 - >>> fl.next() + >>> next(fl) 0 >>> fl.index 1 @@ -151,6 +151,8 @@ add_newdoc('numpy.core', 'nditer', """ + nditer(op, flags=None, op_flags=None, op_dtypes=None, order='K', casting='safe', op_axes=None, itershape=None, buffersize=0) + Efficient multi-dimensional iterator object to iterate over arrays. To get started using this object, see the :ref:`introductory guide to array iteration <arrays.nditer>`. @@ -159,62 +161,63 @@ ---------- op : ndarray or sequence of array_like The array(s) to iterate over. + flags : sequence of str, optional - Flags to control the behavior of the iterator. + Flags to control the behavior of the iterator. - * "buffered" enables buffering when required. - * "c_index" causes a C-order index to be tracked. - * "f_index" causes a Fortran-order index to be tracked. - * "multi_index" causes a multi-index, or a tuple of indices + * ``buffered`` enables buffering when required. + * ``c_index`` causes a C-order index to be tracked. + * ``f_index`` causes a Fortran-order index to be tracked. + * ``multi_index`` causes a multi-index, or a tuple of indices with one per iteration dimension, to be tracked. - * "common_dtype" causes all the operands to be converted to + * ``common_dtype`` causes all the operands to be converted to a common data type, with copying or buffering as necessary. - * "copy_if_overlap" causes the iterator to determine if read + * ``copy_if_overlap`` causes the iterator to determine if read operands have overlap with write operands, and make temporary copies as necessary to avoid overlap. False positives (needless copying) are possible in some cases. - * "delay_bufalloc" delays allocation of the buffers until - a reset() call is made. Allows "allocate" operands to + * ``delay_bufalloc`` delays allocation of the buffers until + a reset() call is made. Allows ``allocate`` operands to be initialized before their values are copied into the buffers. - * "external_loop" causes the `values` given to be + * ``external_loop`` causes the ``values`` given to be one-dimensional arrays with multiple values instead of zero-dimensional arrays. - * "grow_inner" allows the `value` array sizes to be made - larger than the buffer size when both "buffered" and - "external_loop" is used. - * "ranged" allows the iterator to be restricted to a sub-range + * ``grow_inner`` allows the ``value`` array sizes to be made + larger than the buffer size when both ``buffered`` and + ``external_loop`` is used. + * ``ranged`` allows the iterator to be restricted to a sub-range of the iterindex values. - * "refs_ok" enables iteration of reference types, such as + * ``refs_ok`` enables iteration of reference types, such as object arrays. - * "reduce_ok" enables iteration of "readwrite" operands + * ``reduce_ok`` enables iteration of ``readwrite`` operands which are broadcasted, also known as reduction operands. - * "zerosize_ok" allows `itersize` to be zero. + * ``zerosize_ok`` allows `itersize` to be zero. op_flags : list of list of str, optional - This is a list of flags for each operand. At minimum, one of - "readonly", "readwrite", or "writeonly" must be specified. - - * "readonly" indicates the operand will only be read from. - * "readwrite" indicates the operand will be read from and written to. - * "writeonly" indicates the operand will only be written to. - * "no_broadcast" prevents the operand from being broadcasted. - * "contig" forces the operand data to be contiguous. - * "aligned" forces the operand data to be aligned. - * "nbo" forces the operand data to be in native byte order. - * "copy" allows a temporary read-only copy if required. - * "updateifcopy" allows a temporary read-write copy if required. - * "allocate" causes the array to be allocated if it is None - in the `op` parameter. - * "no_subtype" prevents an "allocate" operand from using a subtype. - * "arraymask" indicates that this operand is the mask to use + This is a list of flags for each operand. At minimum, one of + ``readonly``, ``readwrite``, or ``writeonly`` must be specified. + + * ``readonly`` indicates the operand will only be read from. + * ``readwrite`` indicates the operand will be read from and written to. + * ``writeonly`` indicates the operand will only be written to. + * ``no_broadcast`` prevents the operand from being broadcasted. + * ``contig`` forces the operand data to be contiguous. + * ``aligned`` forces the operand data to be aligned. + * ``nbo`` forces the operand data to be in native byte order. + * ``copy`` allows a temporary read-only copy if required. + * ``updateifcopy`` allows a temporary read-write copy if required. + * ``allocate`` causes the array to be allocated if it is None + in the ``op`` parameter. + * ``no_subtype`` prevents an ``allocate`` operand from using a subtype. + * ``arraymask`` indicates that this operand is the mask to use for selecting elements when writing to operands with the 'writemasked' flag set. The iterator does not enforce this, but when writing from a buffer back to the array, it only copies those elements indicated by this mask. - * 'writemasked' indicates that only elements where the chosen - 'arraymask' operand is True will be written to. - * "overlap_assume_elementwise" can be used to mark operands that are + * ``writemasked`` indicates that only elements where the chosen + ``arraymask`` operand is True will be written to. + * ``overlap_assume_elementwise`` can be used to mark operands that are accessed only in the iterator order, to allow less conservative - copying when "copy_if_overlap" is present. + copying when ``copy_if_overlap`` is present. op_dtypes : dtype or tuple of dtype(s), optional The required data type(s) of the operands. If copying or buffering is enabled, the data will be converted to/from their original types. @@ -223,7 +226,7 @@ Fortran order, 'A' means 'F' order if all the arrays are Fortran contiguous, 'C' order otherwise, and 'K' means as close to the order the array elements appear in memory as possible. This also - affects the element memory order of "allocate" operands, as they + affects the element memory order of ``allocate`` operands, as they are allocated to be compatible with iteration order. Default is 'K'. casting : {'no', 'equiv', 'safe', 'same_kind', 'unsafe'}, optional @@ -231,20 +234,20 @@ or buffering. Setting this to 'unsafe' is not recommended, as it can adversely affect accumulations. - * 'no' means the data types should not be cast at all. - * 'equiv' means only byte-order changes are allowed. - * 'safe' means only casts which can preserve values are allowed. - * 'same_kind' means only safe casts or casts within a kind, - like float64 to float32, are allowed. - * 'unsafe' means any data conversions may be done. + * 'no' means the data types should not be cast at all. + * 'equiv' means only byte-order changes are allowed. + * 'safe' means only casts which can preserve values are allowed. + * 'same_kind' means only safe casts or casts within a kind, + like float64 to float32, are allowed. + * 'unsafe' means any data conversions may be done. op_axes : list of list of ints, optional If provided, is a list of ints or None for each operands. The list of axes for an operand is a mapping from the dimensions of the iterator to the dimensions of the operand. A value of -1 can be placed for entries, causing that dimension to be - treated as "newaxis". + treated as `newaxis`. itershape : tuple of ints, optional - The desired shape of the iterator. This allows "allocate" operands + The desired shape of the iterator. This allows ``allocate`` operands with a dimension mapped by op_axes not corresponding to a dimension of a different operand to get a value not equal to 1 for that dimension. @@ -261,19 +264,19 @@ finished : bool Whether the iteration over the operands is finished or not. has_delayed_bufalloc : bool - If True, the iterator was created with the "delay_bufalloc" flag, + If True, the iterator was created with the ``delay_bufalloc`` flag, and no reset() function was called on it yet. has_index : bool - If True, the iterator was created with either the "c_index" or - the "f_index" flag, and the property `index` can be used to + If True, the iterator was created with either the ``c_index`` or + the ``f_index`` flag, and the property `index` can be used to retrieve it. has_multi_index : bool - If True, the iterator was created with the "multi_index" flag, + If True, the iterator was created with the ``multi_index`` flag, and the property `multi_index` can be used to retrieve it. index - When the "c_index" or "f_index" flag was used, this property + When the ``c_index`` or ``f_index`` flag was used, this property provides access to the index. Raises a ValueError if accessed - and `has_index` is False. + and ``has_index`` is False. iterationneedsapi : bool Whether iteration requires access to the Python API, for example if one of the operands is an object array. @@ -286,11 +289,11 @@ and optimized iterator access pattern. Valid only before the iterator is closed. multi_index - When the "multi_index" flag was used, this property + When the ``multi_index`` flag was used, this property provides access to the index. Raises a ValueError if accessed - accessed and `has_multi_index` is False. + accessed and ``has_multi_index`` is False. ndim : int - The iterator's dimension. + The dimensions of the iterator. nop : int The number of iterator operands. operands : tuple of operand(s) @@ -299,8 +302,8 @@ shape : tuple of ints Shape tuple, the shape of the iterator. value - Value of `operands` at current iteration. Normally, this is a - tuple of array scalars, but if the flag "external_loop" is used, + Value of ``operands`` at current iteration. Normally, this is a + tuple of array scalars, but if the flag ``external_loop`` is used, it is a tuple of one dimensional arrays. Notes @@ -311,96 +314,93 @@ The Python exposure supplies two iteration interfaces, one which follows the Python iterator protocol, and another which mirrors the C-style do-while pattern. The native Python approach is better in most cases, but - if you need the iterator's coordinates or index, use the C-style pattern. + if you need the coordinates or index of an iterator, use the C-style pattern. Examples -------- Here is how we might write an ``iter_add`` function, using the - Python iterator protocol:: - - def iter_add_py(x, y, out=None): - addop = np.add - it = np.nditer([x, y, out], [], - [['readonly'], ['readonly'], ['writeonly','allocate']]) - with it: - for (a, b, c) in it: - addop(a, b, out=c) - return it.operands[2] - - Here is the same function, but following the C-style pattern:: - - def iter_add(x, y, out=None): - addop = np.add - - it = np.nditer([x, y, out], [], - [['readonly'], ['readonly'], ['writeonly','allocate']]) - with it: - while not it.finished: - addop(it[0], it[1], out=it[2]) - it.iternext() - - return it.operands[2] - - Here is an example outer product function:: - - def outer_it(x, y, out=None): - mulop = np.multiply - - it = np.nditer([x, y, out], ['external_loop'], - [['readonly'], ['readonly'], ['writeonly', 'allocate']], - op_axes=[list(range(x.ndim)) + [-1] * y.ndim, - [-1] * x.ndim + list(range(y.ndim)), - None]) - with it: - for (a, b, c) in it: - mulop(a, b, out=c) - return it.operands[2] - - >>> a = np.arange(2)+1 - >>> b = np.arange(3)+1 - >>> outer_it(a,b) - array([[1, 2, 3], - [2, 4, 6]]) - - Here is an example function which operates like a "lambda" ufunc:: - - def luf(lamdaexpr, *args, **kwargs): - "luf(lambdaexpr, op1, ..., opn, out=None, order='K', casting='safe', buffersize=0)" - nargs = len(args) - op = (kwargs.get('out',None),) + args - it = np.nditer(op, ['buffered','external_loop'], - [['writeonly','allocate','no_broadcast']] + - [['readonly','nbo','aligned']]*nargs, - order=kwargs.get('order','K'), - casting=kwargs.get('casting','safe'), - buffersize=kwargs.get('buffersize',0)) - while not it.finished: - it[0] = lamdaexpr(*it[1:]) - it.iternext() - return it.operands[0] - - >>> a = np.arange(5) - >>> b = np.ones(5) - >>> luf(lambda i,j:i*i + j/2, a, b) - array([ 0.5, 1.5, 4.5, 9.5, 16.5]) - - If operand flags `"writeonly"` or `"readwrite"` are used the operands may - be views into the original data with the `WRITEBACKIFCOPY` flag. In this case - nditer must be used as a context manager or the nditer.close - method must be called before using the result. The temporary - data will be written back to the original data when the `__exit__` - function is called but not before: - - >>> a = np.arange(6, dtype='i4')[::-2] - >>> with nditer(a, [], - ... [['writeonly', 'updateifcopy']], - ... casting='unsafe', - ... op_dtypes=[np.dtype('f4')]) as i: - ... x = i.operands[0] - ... x[:] = [-1, -2, -3] - ... # a still unchanged here - >>> a, x - array([-1, -2, -3]), array([-1, -2, -3]) + Python iterator protocol: + + >>> def iter_add_py(x, y, out=None): + ... addop = np.add + ... it = np.nditer([x, y, out], [], + ... [['readonly'], ['readonly'], ['writeonly','allocate']]) + ... with it: + ... for (a, b, c) in it: + ... addop(a, b, out=c) + ... return it.operands[2] + + Here is the same function, but following the C-style pattern: + + >>> def iter_add(x, y, out=None): + ... addop = np.add + ... it = np.nditer([x, y, out], [], + ... [['readonly'], ['readonly'], ['writeonly','allocate']]) + ... with it: + ... while not it.finished: + ... addop(it[0], it[1], out=it[2]) + ... it.iternext() + ... return it.operands[2] + + Here is an example outer product function: + + >>> def outer_it(x, y, out=None): + ... mulop = np.multiply + ... it = np.nditer([x, y, out], ['external_loop'], + ... [['readonly'], ['readonly'], ['writeonly', 'allocate']], + ... op_axes=[list(range(x.ndim)) + [-1] * y.ndim, + ... [-1] * x.ndim + list(range(y.ndim)), + ... None]) + ... with it: + ... for (a, b, c) in it: + ... mulop(a, b, out=c) + ... return it.operands[2] + + >>> a = np.arange(2)+1 + >>> b = np.arange(3)+1 + >>> outer_it(a,b) + array([[1, 2, 3], + [2, 4, 6]]) + + Here is an example function which operates like a "lambda" ufunc: + + >>> def luf(lamdaexpr, *args, **kwargs): + ... '''luf(lambdaexpr, op1, ..., opn, out=None, order='K', casting='safe', buffersize=0)''' + ... nargs = len(args) + ... op = (kwargs.get('out',None),) + args + ... it = np.nditer(op, ['buffered','external_loop'], + ... [['writeonly','allocate','no_broadcast']] + + ... [['readonly','nbo','aligned']]*nargs, + ... order=kwargs.get('order','K'), + ... casting=kwargs.get('casting','safe'), + ... buffersize=kwargs.get('buffersize',0)) + ... while not it.finished: + ... it[0] = lamdaexpr(*it[1:]) + ... it.iternext() + ... return it.operands[0] + + >>> a = np.arange(5) + >>> b = np.ones(5) + >>> luf(lambda i,j:i*i + j/2, a, b) + array([ 0.5, 1.5, 4.5, 9.5, 16.5]) + + If operand flags `"writeonly"` or `"readwrite"` are used the + operands may be views into the original data with the + `WRITEBACKIFCOPY` flag. In this case `nditer` must be used as a + context manager or the `nditer.close` method must be called before + using the result. The temporary data will be written back to the + original data when the `__exit__` function is called but not before: + + >>> a = np.arange(6, dtype='i4')[::-2] + >>> with np.nditer(a, [], + ... [['writeonly', 'updateifcopy']], + ... casting='unsafe', + ... op_dtypes=[np.dtype('f4')]) as i: + ... x = i.operands[0] + ... x[:] = [-1, -2, -3] + ... # a still unchanged here + >>> a, x + (array([-1, -2, -3], dtype=int32), array([-1., -2., -3.], dtype=float32)) It is important to note that once the iterator is exited, dangling references (like `x` in the example) may or may not share data with @@ -411,6 +411,8 @@ def luf(lamdaexpr, *args, **kwargs): `x.data` will still point at some part of `a.data`, and writing to one will affect the other. + Context management and the `close` method appeared in version 1.15.0. + """) # nditer methods @@ -426,10 +428,10 @@ def luf(lamdaexpr, *args, **kwargs): >>> x = np.arange(10) >>> y = x + 1 >>> it = np.nditer([x, y]) - >>> it.next() + >>> next(it) (array(0), array(1)) >>> it2 = it.copy() - >>> it2.next() + >>> next(it2) (array(1), array(2)) """)) @@ -476,7 +478,7 @@ def luf(lamdaexpr, *args, **kwargs): add_newdoc('numpy.core', 'nditer', ('remove_axis', """ - remove_axis(i) + remove_axis(i, /) Removes axis `i` from the iterator. Requires that the flag "multi_index" be enabled. @@ -502,6 +504,9 @@ def luf(lamdaexpr, *args, **kwargs): add_newdoc('numpy.core', 'nested_iters', """ + nested_iters(op, axes, flags=None, op_flags=None, op_dtypes=None, \ + order="K", casting="safe", buffersize=0) + Create nditers for use in nested loops Create a tuple of `nditer` objects which iterate in nested loops over @@ -542,7 +547,6 @@ def luf(lamdaexpr, *args, **kwargs): ... print(i.multi_index) ... for y in j: ... print('', j.multi_index, y) - (0,) (0, 0) 0 (0, 1) 1 @@ -567,6 +571,8 @@ def luf(lamdaexpr, *args, **kwargs): Resolve all writeback semantics in writeable operands. + .. versionadded:: 1.15.0 + See Also -------- @@ -602,9 +608,11 @@ def luf(lamdaexpr, *args, **kwargs): -------- broadcast_arrays broadcast_to + broadcast_shapes Examples -------- + Manually adding two vectors, using broadcasting: >>> x = np.array([[1], [2], [3]]) @@ -614,9 +622,9 @@ def luf(lamdaexpr, *args, **kwargs): >>> out = np.empty(b.shape) >>> out.flat = [u+v for (u,v) in b] >>> out - array([[ 5., 6., 7.], - [ 6., 7., 8.], - [ 7., 8., 9.]]) + array([[5., 6., 7.], + [6., 7., 8.], + [7., 8., 9.]]) Compare against built-in broadcasting: @@ -640,7 +648,7 @@ def luf(lamdaexpr, *args, **kwargs): >>> b = np.broadcast(x, y) >>> b.index 0 - >>> b.next(), b.next(), b.next() + >>> next(b), next(b), next(b) ((1, 4), (1, 5), (1, 6)) >>> b.index 3 @@ -664,7 +672,7 @@ def luf(lamdaexpr, *args, **kwargs): >>> y = np.array([[4], [5], [6]]) >>> b = np.broadcast(x, y) >>> row, col = b.iters - >>> row.next(), col.next() + >>> next(row), next(col) (1, 4) """)) @@ -759,11 +767,11 @@ def luf(lamdaexpr, *args, **kwargs): Examples -------- >>> x = np.array([1, 2, 3]) - >>> y = np.array([[4], [5], [6]] + >>> y = np.array([[4], [5], [6]]) >>> b = np.broadcast(x, y) >>> b.index 0 - >>> b.next(), b.next(), b.next() + >>> next(b), next(b), next(b) ((1, 4), (2, 4), (3, 4)) >>> b.index 3 @@ -781,7 +789,8 @@ def luf(lamdaexpr, *args, **kwargs): add_newdoc('numpy.core.multiarray', 'array', """ - array(object, dtype=None, copy=True, order='K', subok=False, ndmin=0) + array(object, dtype=None, *, copy=True, order='K', subok=False, ndmin=0, + like=None) Create an array. @@ -790,11 +799,12 @@ def luf(lamdaexpr, *args, **kwargs): object : array_like An array, any object exposing the array interface, an object whose __array__ method returns an array, or any (nested) sequence. + If object is a scalar, a 0-dimensional array containing object is + returned. dtype : data-type, optional The desired data-type for the array. If not given, then the type will be determined as the minimum type required to hold the objects in the - sequence. This argument can only be used to 'upcast' the array. For - downcasting, use the .astype(t) method. + sequence. copy : bool, optional If true (default), then the object is copied. Otherwise, a copy will only be made if __array__ returns a copy, if obj is a nested sequence, @@ -816,7 +826,7 @@ def luf(lamdaexpr, *args, **kwargs): ===== ========= =================================================== When ``copy=False`` and a copy is made for other reasons, the result is - the same as if ``copy=True``, with some exceptions for `A`, see the + the same as if ``copy=True``, with some exceptions for 'A', see the Notes section. The default order is 'K'. subok : bool, optional If True, then sub-classes will be passed-through, otherwise @@ -825,6 +835,9 @@ def luf(lamdaexpr, *args, **kwargs): Specifies the minimum number of dimensions that the resulting array should have. Ones will be pre-pended to the shape as needed to meet this requirement. + ${ARRAY_FUNCTION_LIKE} + + .. versionadded:: 1.20.0 Returns ------- @@ -891,11 +904,250 @@ def luf(lamdaexpr, *args, **kwargs): matrix([[1, 2], [3, 4]]) - """) + """.replace( + "${ARRAY_FUNCTION_LIKE}", + array_function_like_doc, + )) + +add_newdoc('numpy.core.multiarray', 'asarray', + """ + asarray(a, dtype=None, order=None, *, like=None) + + Convert the input to an array. + + Parameters + ---------- + a : array_like + Input data, in any form that can be converted to an array. This + includes lists, lists of tuples, tuples, tuples of tuples, tuples + of lists and ndarrays. + dtype : data-type, optional + By default, the data-type is inferred from the input data. + order : {'C', 'F', 'A', 'K'}, optional + Memory layout. 'A' and 'K' depend on the order of input array a. + 'C' row-major (C-style), + 'F' column-major (Fortran-style) memory representation. + 'A' (any) means 'F' if `a` is Fortran contiguous, 'C' otherwise + 'K' (keep) preserve input order + Defaults to 'K'. + ${ARRAY_FUNCTION_LIKE} + + .. versionadded:: 1.20.0 + + Returns + ------- + out : ndarray + Array interpretation of `a`. No copy is performed if the input + is already an ndarray with matching dtype and order. If `a` is a + subclass of ndarray, a base class ndarray is returned. + + See Also + -------- + asanyarray : Similar function which passes through subclasses. + ascontiguousarray : Convert input to a contiguous array. + asfarray : Convert input to a floating point ndarray. + asfortranarray : Convert input to an ndarray with column-major + memory order. + asarray_chkfinite : Similar function which checks input for NaNs and Infs. + fromiter : Create an array from an iterator. + fromfunction : Construct an array by executing a function on grid + positions. + + Examples + -------- + Convert a list into an array: + + >>> a = [1, 2] + >>> np.asarray(a) + array([1, 2]) + + Existing arrays are not copied: + + >>> a = np.array([1, 2]) + >>> np.asarray(a) is a + True + + If `dtype` is set, array is copied only if dtype does not match: + + >>> a = np.array([1, 2], dtype=np.float32) + >>> np.asarray(a, dtype=np.float32) is a + True + >>> np.asarray(a, dtype=np.float64) is a + False + + Contrary to `asanyarray`, ndarray subclasses are not passed through: + + >>> issubclass(np.recarray, np.ndarray) + True + >>> a = np.array([(1.0, 2), (3.0, 4)], dtype='f4,i4').view(np.recarray) + >>> np.asarray(a) is a + False + >>> np.asanyarray(a) is a + True + + """.replace( + "${ARRAY_FUNCTION_LIKE}", + array_function_like_doc, + )) + +add_newdoc('numpy.core.multiarray', 'asanyarray', + """ + asanyarray(a, dtype=None, order=None, *, like=None) + + Convert the input to an ndarray, but pass ndarray subclasses through. + + Parameters + ---------- + a : array_like + Input data, in any form that can be converted to an array. This + includes scalars, lists, lists of tuples, tuples, tuples of tuples, + tuples of lists, and ndarrays. + dtype : data-type, optional + By default, the data-type is inferred from the input data. + order : {'C', 'F', 'A', 'K'}, optional + Memory layout. 'A' and 'K' depend on the order of input array a. + 'C' row-major (C-style), + 'F' column-major (Fortran-style) memory representation. + 'A' (any) means 'F' if `a` is Fortran contiguous, 'C' otherwise + 'K' (keep) preserve input order + Defaults to 'C'. + ${ARRAY_FUNCTION_LIKE} + + .. versionadded:: 1.20.0 + + Returns + ------- + out : ndarray or an ndarray subclass + Array interpretation of `a`. If `a` is an ndarray or a subclass + of ndarray, it is returned as-is and no copy is performed. + + See Also + -------- + asarray : Similar function which always returns ndarrays. + ascontiguousarray : Convert input to a contiguous array. + asfarray : Convert input to a floating point ndarray. + asfortranarray : Convert input to an ndarray with column-major + memory order. + asarray_chkfinite : Similar function which checks input for NaNs and + Infs. + fromiter : Create an array from an iterator. + fromfunction : Construct an array by executing a function on grid + positions. + + Examples + -------- + Convert a list into an array: + + >>> a = [1, 2] + >>> np.asanyarray(a) + array([1, 2]) + + Instances of `ndarray` subclasses are passed through as-is: + + >>> a = np.array([(1.0, 2), (3.0, 4)], dtype='f4,i4').view(np.recarray) + >>> np.asanyarray(a) is a + True + + """.replace( + "${ARRAY_FUNCTION_LIKE}", + array_function_like_doc, + )) + +add_newdoc('numpy.core.multiarray', 'ascontiguousarray', + """ + ascontiguousarray(a, dtype=None, *, like=None) + + Return a contiguous array (ndim >= 1) in memory (C order). + + Parameters + ---------- + a : array_like + Input array. + dtype : str or dtype object, optional + Data-type of returned array. + ${ARRAY_FUNCTION_LIKE} + + .. versionadded:: 1.20.0 + + Returns + ------- + out : ndarray + Contiguous array of same shape and content as `a`, with type `dtype` + if specified. + + See Also + -------- + asfortranarray : Convert input to an ndarray with column-major + memory order. + require : Return an ndarray that satisfies requirements. + ndarray.flags : Information about the memory layout of the array. + + Examples + -------- + >>> x = np.arange(6).reshape(2,3) + >>> np.ascontiguousarray(x, dtype=np.float32) + array([[0., 1., 2.], + [3., 4., 5.]], dtype=float32) + >>> x.flags['C_CONTIGUOUS'] + True + + Note: This function returns an array with at least one-dimension (1-d) + so it will not preserve 0-d arrays. + + """.replace( + "${ARRAY_FUNCTION_LIKE}", + array_function_like_doc, + )) + +add_newdoc('numpy.core.multiarray', 'asfortranarray', + """ + asfortranarray(a, dtype=None, *, like=None) + + Return an array (ndim >= 1) laid out in Fortran order in memory. + + Parameters + ---------- + a : array_like + Input array. + dtype : str or dtype object, optional + By default, the data-type is inferred from the input data. + ${ARRAY_FUNCTION_LIKE} + + .. versionadded:: 1.20.0 + + Returns + ------- + out : ndarray + The input `a` in Fortran, or column-major, order. + + See Also + -------- + ascontiguousarray : Convert input to a contiguous (C order) array. + asanyarray : Convert input to an ndarray with either row or + column-major memory order. + require : Return an ndarray that satisfies requirements. + ndarray.flags : Information about the memory layout of the array. + + Examples + -------- + >>> x = np.arange(6).reshape(2,3) + >>> y = np.asfortranarray(x) + >>> x.flags['F_CONTIGUOUS'] + False + >>> y.flags['F_CONTIGUOUS'] + True + + Note: This function returns an array with at least one-dimension (1-d) + so it will not preserve 0-d arrays. + + """.replace( + "${ARRAY_FUNCTION_LIKE}", + array_function_like_doc, + )) add_newdoc('numpy.core.multiarray', 'empty', """ - empty(shape, dtype=float, order='C') + empty(shape, dtype=float, order='C', *, like=None) Return a new array of given shape and type, without initializing entries. @@ -910,6 +1162,9 @@ def luf(lamdaexpr, *args, **kwargs): Whether to store multi-dimensional data in row-major (C-style) or column-major (Fortran-style) order in memory. + ${ARRAY_FUNCTION_LIKE} + + .. versionadded:: 1.20.0 Returns ------- @@ -936,73 +1191,16 @@ def luf(lamdaexpr, *args, **kwargs): -------- >>> np.empty([2, 2]) array([[ -9.74499359e+001, 6.69583040e-309], - [ 2.13182611e-314, 3.06959433e-309]]) #random + [ 2.13182611e-314, 3.06959433e-309]]) #uninitialized >>> np.empty([2, 2], dtype=int) array([[-1073741821, -1067949133], - [ 496041986, 19249760]]) #random - - """) - -add_newdoc('numpy.core.multiarray', 'empty_like', - """ - empty_like(prototype, dtype=None, order='K', subok=True) - - Return a new array with the same shape and type as a given array. - - Parameters - ---------- - prototype : array_like - The shape and data-type of `prototype` define these same attributes - of the returned array. - dtype : data-type, optional - Overrides the data type of the result. - - .. versionadded:: 1.6.0 - order : {'C', 'F', 'A', or 'K'}, optional - Overrides the memory layout of the result. 'C' means C-order, - 'F' means F-order, 'A' means 'F' if ``prototype`` is Fortran - contiguous, 'C' otherwise. 'K' means match the layout of ``prototype`` - as closely as possible. - - .. versionadded:: 1.6.0 - subok : bool, optional. - If True, then the newly created array will use the sub-class - type of 'a', otherwise it will be a base-class array. Defaults - to True. - - Returns - ------- - out : ndarray - Array of uninitialized (arbitrary) data with the same - shape and type as `prototype`. - - See Also - -------- - ones_like : Return an array of ones with shape and type of input. - zeros_like : Return an array of zeros with shape and type of input. - full_like : Return a new array with shape of input filled with value. - empty : Return a new uninitialized array. - - Notes - ----- - This function does *not* initialize the returned array; to do that use - `zeros_like` or `ones_like` instead. It may be marginally faster than - the functions that do set the array values. - - Examples - -------- - >>> a = ([1,2,3], [4,5,6]) # a is array-like - >>> np.empty_like(a) - array([[-1073741821, -1073741821, 3], #random - [ 0, 0, -1073741821]]) - >>> a = np.array([[1., 2., 3.],[4.,5.,6.]]) - >>> np.empty_like(a) - array([[ -2.00000715e+000, 1.48219694e-323, -2.00000572e+000],#random - [ 4.38791518e-305, -2.00000715e+000, 4.17269252e-309]]) - - """) + [ 496041986, 19249760]]) #uninitialized + """.replace( + "${ARRAY_FUNCTION_LIKE}", + array_function_like_doc, + )) add_newdoc('numpy.core.multiarray', 'scalar', """ @@ -1020,7 +1218,7 @@ def luf(lamdaexpr, *args, **kwargs): add_newdoc('numpy.core.multiarray', 'zeros', """ - zeros(shape, dtype=float, order='C') + zeros(shape, dtype=float, order='C', *, like=None) Return a new array of given shape and type, filled with zeros. @@ -1035,6 +1233,9 @@ def luf(lamdaexpr, *args, **kwargs): Whether to store multi-dimensional data in row-major (C-style) or column-major (Fortran-style) order in memory. + ${ARRAY_FUNCTION_LIKE} + + .. versionadded:: 1.20.0 Returns ------- @@ -1069,7 +1270,10 @@ def luf(lamdaexpr, *args, **kwargs): array([(0, 0), (0, 0)], dtype=[('x', '<i4'), ('y', '<i4')]) - """) + """.replace( + "${ARRAY_FUNCTION_LIKE}", + array_function_like_doc, + )) add_newdoc('numpy.core.multiarray', 'set_typeDict', """set_typeDict(dict) @@ -1081,7 +1285,7 @@ def luf(lamdaexpr, *args, **kwargs): add_newdoc('numpy.core.multiarray', 'fromstring', """ - fromstring(string, dtype=float, count=-1, sep='') + fromstring(string, dtype=float, count=-1, *, sep, like=None) A new 1-D array initialized from text data in a string. @@ -1091,7 +1295,12 @@ def luf(lamdaexpr, *args, **kwargs): A string containing the data. dtype : data-type, optional The data type of the array; default: float. For binary input data, - the data must be in exactly this format. + the data must be in exactly this format. Most builtin numeric types are + supported and extension types may be supported. + + .. versionadded:: 1.18.0 + Complex dtypes. + count : int, optional Read this number of `dtype` elements from the data. If this is negative (the default), the count will be determined from the @@ -1101,9 +1310,17 @@ def luf(lamdaexpr, *args, **kwargs): elements is also ignored. .. deprecated:: 1.14 - If this argument is not provided, `fromstring` falls back on the - behaviour of `frombuffer` after encoding unicode string inputs as - either utf-8 (python 3), or the default encoding (python 2). + Passing ``sep=''``, the default, is deprecated since it will + trigger the deprecated binary mode of this function. This mode + interprets `string` as binary bytes, rather than ASCII text with + decimal numbers, an operation which is better spelt + ``frombuffer(string, dtype, count)``. If `string` contains unicode + text, the binary mode of `fromstring` will first encode it into + bytes using either utf-8 (python 3) or the default encoding + (python 2), neither of which produce sane results. + ${ARRAY_FUNCTION_LIKE} + + .. versionadded:: 1.20.0 Returns ------- @@ -1127,23 +1344,66 @@ def luf(lamdaexpr, *args, **kwargs): >>> np.fromstring('1, 2', dtype=int, sep=',') array([1, 2]) + """.replace( + "${ARRAY_FUNCTION_LIKE}", + array_function_like_doc, + )) + +add_newdoc('numpy.core.multiarray', 'compare_chararrays', + """ + compare_chararrays(a1, a2, cmp, rstrip) + + Performs element-wise comparison of two string arrays using the + comparison operator specified by `cmp_op`. + + Parameters + ---------- + a1, a2 : array_like + Arrays to be compared. + cmp : {"<", "<=", "==", ">=", ">", "!="} + Type of comparison. + rstrip : Boolean + If True, the spaces at the end of Strings are removed before the comparison. + + Returns + ------- + out : ndarray + The output array of type Boolean with the same shape as a and b. + + Raises + ------ + ValueError + If `cmp_op` is not valid. + TypeError + If at least one of `a` or `b` is a non-string array + + Examples + -------- + >>> a = np.array(["a", "b", "cde"]) + >>> b = np.array(["a", "a", "dec"]) + >>> np.compare_chararrays(a, b, ">", True) + array([False, True, False]) + """) add_newdoc('numpy.core.multiarray', 'fromiter', """ - fromiter(iterable, dtype, count=-1) + fromiter(iter, dtype, count=-1, *, like=None) Create a new 1-dimensional array from an iterable object. Parameters ---------- - iterable : iterable object + iter : iterable object An iterable object providing data for the array. dtype : data-type The data-type of the returned array. count : int, optional The number of items to read from *iterable*. The default is -1, which means all data is read. + ${ARRAY_FUNCTION_LIKE} + + .. versionadded:: 1.20.0 Returns ------- @@ -1161,11 +1421,14 @@ def luf(lamdaexpr, *args, **kwargs): >>> np.fromiter(iterable, float) array([ 0., 1., 4., 9., 16.]) - """) + """.replace( + "${ARRAY_FUNCTION_LIKE}", + array_function_like_doc, + )) add_newdoc('numpy.core.multiarray', 'fromfile', """ - fromfile(file, dtype=float, count=-1, sep='') + fromfile(file, dtype=float, count=-1, sep='', offset=0, *, like=None) Construct an array from data in a text or binary file. @@ -1175,12 +1438,21 @@ def luf(lamdaexpr, *args, **kwargs): Parameters ---------- - file : file or str + file : file or str or Path Open file object or filename. + + .. versionchanged:: 1.17.0 + `pathlib.Path` objects are now accepted. + dtype : data-type Data type of the returned array. For binary files, it is used to determine the size and byte-order of the items in the file. + Most builtin numeric types are supported and extension types may be supported. + + .. versionadded:: 1.18.0 + Complex dtypes. + count : int Number of items to read. ``-1`` means all items (i.e., the complete file). @@ -1190,6 +1462,14 @@ def luf(lamdaexpr, *args, **kwargs): Spaces (" ") in the separator match zero or more whitespace characters. A separator consisting only of spaces must match at least one whitespace. + offset : int + The offset (in bytes) from the file's current position. Defaults to 0. + Only permitted for binary files. + + .. versionadded:: 1.17.0 + ${ARRAY_FUNCTION_LIKE} + + .. versionadded:: 1.20.0 See also -------- @@ -1200,7 +1480,7 @@ def luf(lamdaexpr, *args, **kwargs): Notes ----- Do not rely on the combination of `tofile` and `fromfile` for - data storage, as the binary files generated are are not platform + data storage, as the binary files generated are not platform independent. In particular, no byte-order or data-type information is saved. Data can be stored in the platform independent ``.npy`` format using `save` and `load` instead. @@ -1209,38 +1489,41 @@ def luf(lamdaexpr, *args, **kwargs): -------- Construct an ndarray: - >>> dt = np.dtype([('time', [('min', int), ('sec', int)]), + >>> dt = np.dtype([('time', [('min', np.int64), ('sec', np.int64)]), ... ('temp', float)]) >>> x = np.zeros((1,), dtype=dt) >>> x['time']['min'] = 10; x['temp'] = 98.25 >>> x array([((10, 0), 98.25)], - dtype=[('time', [('min', '<i4'), ('sec', '<i4')]), ('temp', '<f8')]) + dtype=[('time', [('min', '<i8'), ('sec', '<i8')]), ('temp', '<f8')]) Save the raw data to disk: - >>> import os - >>> fname = os.tmpnam() + >>> import tempfile + >>> fname = tempfile.mkstemp()[1] >>> x.tofile(fname) Read the raw data from disk: >>> np.fromfile(fname, dtype=dt) array([((10, 0), 98.25)], - dtype=[('time', [('min', '<i4'), ('sec', '<i4')]), ('temp', '<f8')]) + dtype=[('time', [('min', '<i8'), ('sec', '<i8')]), ('temp', '<f8')]) The recommended way to store and load data: >>> np.save(fname, x) >>> np.load(fname + '.npy') array([((10, 0), 98.25)], - dtype=[('time', [('min', '<i4'), ('sec', '<i4')]), ('temp', '<f8')]) + dtype=[('time', [('min', '<i8'), ('sec', '<i8')]), ('temp', '<f8')]) - """) + """.replace( + "${ARRAY_FUNCTION_LIKE}", + array_function_like_doc, + )) add_newdoc('numpy.core.multiarray', 'frombuffer', """ - frombuffer(buffer, dtype=float, count=-1, offset=0) + frombuffer(buffer, dtype=float, count=-1, offset=0, *, like=None) Interpret a buffer as a 1-dimensional array. @@ -1254,6 +1537,13 @@ def luf(lamdaexpr, *args, **kwargs): Number of items to read. ``-1`` means all data in the buffer. offset : int, optional Start reading the buffer from this offset (in bytes); default: 0. + ${ARRAY_FUNCTION_LIKE} + + .. versionadded:: 1.20.0 + + Returns + ------- + out : ndarray Notes ----- @@ -1262,179 +1552,38 @@ def luf(lamdaexpr, *args, **kwargs): >>> dt = np.dtype(int) >>> dt = dt.newbyteorder('>') - >>> np.frombuffer(buf, dtype=dt) + >>> np.frombuffer(buf, dtype=dt) # doctest: +SKIP The data of the resulting array will not be byteswapped, but will be interpreted correctly. Examples -------- - >>> s = 'hello world' + >>> s = b'hello world' >>> np.frombuffer(s, dtype='S1', count=5, offset=6) - array(['w', 'o', 'r', 'l', 'd'], - dtype='|S1') + array([b'w', b'o', b'r', b'l', b'd'], dtype='|S1') >>> np.frombuffer(b'\\x01\\x02', dtype=np.uint8) array([1, 2], dtype=uint8) >>> np.frombuffer(b'\\x01\\x02\\x03\\x04\\x05', dtype=np.uint8, count=3) array([1, 2, 3], dtype=uint8) - """) - -add_newdoc('numpy.core.multiarray', 'concatenate', - """ - concatenate((a1, a2, ...), axis=0, out=None) - - Join a sequence of arrays along an existing axis. - - Parameters - ---------- - a1, a2, ... : sequence of array_like - The arrays must have the same shape, except in the dimension - corresponding to `axis` (the first, by default). - axis : int, optional - The axis along which the arrays will be joined. If axis is None, - arrays are flattened before use. Default is 0. - out : ndarray, optional - If provided, the destination to place the result. The shape must be - correct, matching that of what concatenate would have returned if no - out argument were specified. - - Returns - ------- - res : ndarray - The concatenated array. - - See Also - -------- - ma.concatenate : Concatenate function that preserves input masks. - array_split : Split an array into multiple sub-arrays of equal or - near-equal size. - split : Split array into a list of multiple sub-arrays of equal size. - hsplit : Split array into multiple sub-arrays horizontally (column wise) - vsplit : Split array into multiple sub-arrays vertically (row wise) - dsplit : Split array into multiple sub-arrays along the 3rd axis (depth). - stack : Stack a sequence of arrays along a new axis. - hstack : Stack arrays in sequence horizontally (column wise) - vstack : Stack arrays in sequence vertically (row wise) - dstack : Stack arrays in sequence depth wise (along third dimension) - - Notes - ----- - When one or more of the arrays to be concatenated is a MaskedArray, - this function will return a MaskedArray object instead of an ndarray, - but the input masks are *not* preserved. In cases where a MaskedArray - is expected as input, use the ma.concatenate function from the masked - array module instead. - - Examples - -------- - >>> a = np.array([[1, 2], [3, 4]]) - >>> b = np.array([[5, 6]]) - >>> np.concatenate((a, b), axis=0) - array([[1, 2], - [3, 4], - [5, 6]]) - >>> np.concatenate((a, b.T), axis=1) - array([[1, 2, 5], - [3, 4, 6]]) - >>> np.concatenate((a, b), axis=None) - array([1, 2, 3, 4, 5, 6]) - - This function will not preserve masking of MaskedArray inputs. - - >>> a = np.ma.arange(3) - >>> a[1] = np.ma.masked - >>> b = np.arange(2, 5) - >>> a - masked_array(data = [0 -- 2], - mask = [False True False], - fill_value = 999999) - >>> b - array([2, 3, 4]) - >>> np.concatenate([a, b]) - masked_array(data = [0 1 2 2 3 4], - mask = False, - fill_value = 999999) - >>> np.ma.concatenate([a, b]) - masked_array(data = [0 -- 2 2 3 4], - mask = [False True False False False False], - fill_value = 999999) - - """) + """.replace( + "${ARRAY_FUNCTION_LIKE}", + array_function_like_doc, + )) -add_newdoc('numpy.core', 'inner', +add_newdoc('numpy.core.multiarray', '_from_dlpack', """ - inner(a, b) - - Inner product of two arrays. - - Ordinary inner product of vectors for 1-D arrays (without complex - conjugation), in higher dimensions a sum product over the last axes. - - Parameters - ---------- - a, b : array_like - If `a` and `b` are nonscalar, their last dimensions must match. + _from_dlpack(x, /) - Returns - ------- - out : ndarray - `out.shape = a.shape[:-1] + b.shape[:-1]` - - Raises - ------ - ValueError - If the last dimension of `a` and `b` has different size. + Create a NumPy array from an object implementing the ``__dlpack__`` + protocol. See Also -------- - tensordot : Sum products over arbitrary axes. - dot : Generalised matrix product, using second last dimension of `b`. - einsum : Einstein summation convention. - - Notes - ----- - For vectors (1-D arrays) it computes the ordinary inner-product:: - - np.inner(a, b) = sum(a[:]*b[:]) - - More generally, if `ndim(a) = r > 0` and `ndim(b) = s > 0`:: - - np.inner(a, b) = np.tensordot(a, b, axes=(-1,-1)) - - or explicitly:: - - np.inner(a, b)[i0,...,ir-1,j0,...,js-1] - = sum(a[i0,...,ir-1,:]*b[j0,...,js-1,:]) - - In addition `a` or `b` may be scalars, in which case:: - - np.inner(a,b) = a*b - - Examples - -------- - Ordinary inner product for vectors: - - >>> a = np.array([1,2,3]) - >>> b = np.array([0,1,0]) - >>> np.inner(a, b) - 2 - - A multidimensional example: - - >>> a = np.arange(24).reshape((2,3,4)) - >>> b = np.arange(4) - >>> np.inner(a, b) - array([[ 14, 38, 62], - [ 86, 110, 134]]) - - An example where `b` is a scalar: - - >>> np.inner(np.eye(2), 7) - array([[ 7., 0.], - [ 0., 7.]]) - + `Array API documentation + <https://data-apis.org/array-api/latest/design_topics/data_interchange.html#syntax-for-data-interchange-with-dlpack>`_ """) add_newdoc('numpy.core', 'fastCopyAndTranspose', @@ -1445,29 +1594,28 @@ def luf(lamdaexpr, *args, **kwargs): add_newdoc('numpy.core.multiarray', 'arange', """ - arange([start,] stop[, step,], dtype=None) + arange([start,] stop[, step,], dtype=None, *, like=None) Return evenly spaced values within a given interval. Values are generated within the half-open interval ``[start, stop)`` (in other words, the interval including `start` but excluding `stop`). For integer arguments the function is equivalent to the Python built-in - `range <http://docs.python.org/lib/built-in-funcs.html>`_ function, - but returns an ndarray rather than a list. + `range` function, but returns an ndarray rather than a list. - When using a non-integer step, such as 0.1, the results will often not - be consistent. It is better to use ``linspace`` for these cases. + When using a non-integer step, such as 0.1, it is often better to use + `numpy.linspace`. See the warnings section below for more information. Parameters ---------- - start : number, optional + start : integer or real, optional Start of interval. The interval includes this value. The default start value is 0. - stop : number + stop : integer or real End of interval. The interval does not include this value, except in some cases where `step` is not an integer and floating point round-off affects the length of `out`. - step : number, optional + step : integer or real, optional Spacing between values. For any output `out`, this is the distance between two adjacent values, ``out[i+1] - out[i]``. The default step size is 1. If `step` is specified as a position argument, @@ -1475,6 +1623,9 @@ def luf(lamdaexpr, *args, **kwargs): dtype : dtype The type of the output array. If `dtype` is not given, infer the data type from the other input arguments. + ${ARRAY_FUNCTION_LIKE} + + .. versionadded:: 1.20.0 Returns ------- @@ -1486,15 +1637,34 @@ def luf(lamdaexpr, *args, **kwargs): this rule may result in the last element of `out` being greater than `stop`. - See Also + Warnings -------- - linspace : Evenly spaced numbers with careful handling of endpoints. - ogrid: Arrays of evenly spaced numbers in N-dimensions. - mgrid: Grid-shaped arrays of evenly spaced numbers in N-dimensions. + The length of the output might not be numerically stable. - Examples - -------- - >>> np.arange(3) + Another stability issue is due to the internal implementation of + `numpy.arange`. + The actual step value used to populate the array is + ``dtype(start + step) - dtype(start)`` and not `step`. Precision loss + can occur here, due to casting or due to using floating points when + `start` is much larger than `step`. This can lead to unexpected + behaviour. For example:: + + >>> np.arange(0, 5, 0.5, dtype=int) + array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) + >>> np.arange(-3, 3, 0.5, dtype=int) + array([-3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8]) + + In such cases, the use of `numpy.linspace` should be preferred. + + See Also + -------- + numpy.linspace : Evenly spaced numbers with careful handling of endpoints. + numpy.ogrid: Arrays of evenly spaced numbers in N-dimensions. + numpy.mgrid: Grid-shaped arrays of evenly spaced numbers in N-dimensions. + + Examples + -------- + >>> np.arange(3) array([0, 1, 2]) >>> np.arange(3.0) array([ 0., 1., 2.]) @@ -1503,12 +1673,15 @@ def luf(lamdaexpr, *args, **kwargs): >>> np.arange(3,7,2) array([3, 5]) - """) + """.replace( + "${ARRAY_FUNCTION_LIKE}", + array_function_like_doc, + )) add_newdoc('numpy.core.multiarray', '_get_ndarray_c_version', """_get_ndarray_c_version() - Return the compile time NDARRAY_VERSION number. + Return the compile time NPY_VERSION (formerly called NDARRAY_VERSION) number. """) @@ -1534,6 +1707,12 @@ def luf(lamdaexpr, *args, **kwargs): Set numerical operators for array objects. + .. deprecated:: 1.16 + + For the general case, use :c:func:`PyUFunc_ReplaceLoopBySignature`. + For ndarray subclasses, define the ``__array_ufunc__`` method and + override the relevant ufunc. + Parameters ---------- op1, op2, ... : callable @@ -1572,262 +1751,6 @@ def luf(lamdaexpr, *args, **kwargs): """) -add_newdoc('numpy.core.multiarray', 'where', - """ - where(condition, [x, y]) - - Return elements, either from `x` or `y`, depending on `condition`. - - If only `condition` is given, return ``condition.nonzero()``. - - Parameters - ---------- - condition : array_like, bool - When True, yield `x`, otherwise yield `y`. - x, y : array_like, optional - Values from which to choose. `x`, `y` and `condition` need to be - broadcastable to some shape. - - Returns - ------- - out : ndarray or tuple of ndarrays - If both `x` and `y` are specified, the output array contains - elements of `x` where `condition` is True, and elements from - `y` elsewhere. - - If only `condition` is given, return the tuple - ``condition.nonzero()``, the indices where `condition` is True. - - See Also - -------- - nonzero, choose - - Notes - ----- - If `x` and `y` are given and input arrays are 1-D, `where` is - equivalent to:: - - [xv if c else yv for (c,xv,yv) in zip(condition,x,y)] - - Examples - -------- - >>> np.where([[True, False], [True, True]], - ... [[1, 2], [3, 4]], - ... [[9, 8], [7, 6]]) - array([[1, 8], - [3, 4]]) - - >>> np.where([[0, 1], [1, 0]]) - (array([0, 1]), array([1, 0])) - - >>> x = np.arange(9.).reshape(3, 3) - >>> np.where( x > 5 ) - (array([2, 2, 2]), array([0, 1, 2])) - >>> x[np.where( x > 3.0 )] # Note: result is 1D. - array([ 4., 5., 6., 7., 8.]) - >>> np.where(x < 5, x, -1) # Note: broadcasting. - array([[ 0., 1., 2.], - [ 3., 4., -1.], - [-1., -1., -1.]]) - - Find the indices of elements of `x` that are in `goodvalues`. - - >>> goodvalues = [3, 4, 7] - >>> ix = np.isin(x, goodvalues) - >>> ix - array([[False, False, False], - [ True, True, False], - [False, True, False]]) - >>> np.where(ix) - (array([1, 1, 2]), array([0, 1, 1])) - - """) - - -add_newdoc('numpy.core.multiarray', 'lexsort', - """ - lexsort(keys, axis=-1) - - Perform an indirect stable sort using a sequence of keys. - - Given multiple sorting keys, which can be interpreted as columns in a - spreadsheet, lexsort returns an array of integer indices that describes - the sort order by multiple columns. The last key in the sequence is used - for the primary sort order, the second-to-last key for the secondary sort - order, and so on. The keys argument must be a sequence of objects that - can be converted to arrays of the same shape. If a 2D array is provided - for the keys argument, it's rows are interpreted as the sorting keys and - sorting is according to the last row, second last row etc. - - Parameters - ---------- - keys : (k, N) array or tuple containing k (N,)-shaped sequences - The `k` different "columns" to be sorted. The last column (or row if - `keys` is a 2D array) is the primary sort key. - axis : int, optional - Axis to be indirectly sorted. By default, sort over the last axis. - - Returns - ------- - indices : (N,) ndarray of ints - Array of indices that sort the keys along the specified axis. - - See Also - -------- - argsort : Indirect sort. - ndarray.sort : In-place sort. - sort : Return a sorted copy of an array. - - Examples - -------- - Sort names: first by surname, then by name. - - >>> surnames = ('Hertz', 'Galilei', 'Hertz') - >>> first_names = ('Heinrich', 'Galileo', 'Gustav') - >>> ind = np.lexsort((first_names, surnames)) - >>> ind - array([1, 2, 0]) - - >>> [surnames[i] + ", " + first_names[i] for i in ind] - ['Galilei, Galileo', 'Hertz, Gustav', 'Hertz, Heinrich'] - - Sort two columns of numbers: - - >>> a = [1,5,1,4,3,4,4] # First column - >>> b = [9,4,0,4,0,2,1] # Second column - >>> ind = np.lexsort((b,a)) # Sort by a, then by b - >>> print(ind) - [2 0 4 6 5 3 1] - - >>> [(a[i],b[i]) for i in ind] - [(1, 0), (1, 9), (3, 0), (4, 1), (4, 2), (4, 4), (5, 4)] - - Note that sorting is first according to the elements of ``a``. - Secondary sorting is according to the elements of ``b``. - - A normal ``argsort`` would have yielded: - - >>> [(a[i],b[i]) for i in np.argsort(a)] - [(1, 9), (1, 0), (3, 0), (4, 4), (4, 2), (4, 1), (5, 4)] - - Structured arrays are sorted lexically by ``argsort``: - - >>> x = np.array([(1,9), (5,4), (1,0), (4,4), (3,0), (4,2), (4,1)], - ... dtype=np.dtype([('x', int), ('y', int)])) - - >>> np.argsort(x) # or np.argsort(x, order=('x', 'y')) - array([2, 0, 4, 6, 5, 3, 1]) - - """) - -add_newdoc('numpy.core.multiarray', 'can_cast', - """ - can_cast(from_, to, casting='safe') - - Returns True if cast between data types can occur according to the - casting rule. If from is a scalar or array scalar, also returns - True if the scalar value can be cast without overflow or truncation - to an integer. - - Parameters - ---------- - from_ : dtype, dtype specifier, scalar, or array - Data type, scalar, or array to cast from. - to : dtype or dtype specifier - Data type to cast to. - casting : {'no', 'equiv', 'safe', 'same_kind', 'unsafe'}, optional - Controls what kind of data casting may occur. - - * 'no' means the data types should not be cast at all. - * 'equiv' means only byte-order changes are allowed. - * 'safe' means only casts which can preserve values are allowed. - * 'same_kind' means only safe casts or casts within a kind, - like float64 to float32, are allowed. - * 'unsafe' means any data conversions may be done. - - Returns - ------- - out : bool - True if cast can occur according to the casting rule. - - Notes - ----- - Starting in NumPy 1.9, can_cast function now returns False in 'safe' - casting mode for integer/float dtype and string dtype if the string dtype - length is not long enough to store the max integer/float value converted - to a string. Previously can_cast in 'safe' mode returned True for - integer/float dtype and a string dtype of any length. - - See also - -------- - dtype, result_type - - Examples - -------- - Basic examples - - >>> np.can_cast(np.int32, np.int64) - True - >>> np.can_cast(np.float64, complex) - True - >>> np.can_cast(complex, float) - False - - >>> np.can_cast('i8', 'f8') - True - >>> np.can_cast('i8', 'f4') - False - >>> np.can_cast('i4', 'S4') - False - - Casting scalars - - >>> np.can_cast(100, 'i1') - True - >>> np.can_cast(150, 'i1') - False - >>> np.can_cast(150, 'u1') - True - - >>> np.can_cast(3.5e100, np.float32) - False - >>> np.can_cast(1000.0, np.float32) - True - - Array scalar checks the value, array does not - - >>> np.can_cast(np.array(1000.0), np.float32) - True - >>> np.can_cast(np.array([1000.0]), np.float32) - False - - Using the casting rules - - >>> np.can_cast('i8', 'i8', 'no') - True - >>> np.can_cast('<i8', '>i8', 'no') - False - - >>> np.can_cast('<i8', '>i8', 'equiv') - True - >>> np.can_cast('<i4', '>i8', 'equiv') - False - - >>> np.can_cast('<i4', '>i8', 'safe') - True - >>> np.can_cast('<i8', '>i4', 'safe') - False - - >>> np.can_cast('<i8', '>i4', 'same_kind') - True - >>> np.can_cast('<i8', '>u4', 'same_kind') - False - - >>> np.can_cast('<i8', '>u4', 'unsafe') - True - - """) - add_newdoc('numpy.core.multiarray', 'promote_types', """ promote_types(type1, type2) @@ -1888,508 +1811,225 @@ def luf(lamdaexpr, *args, **kwargs): """) -add_newdoc('numpy.core.multiarray', 'min_scalar_type', +add_newdoc('numpy.core.multiarray', 'c_einsum', """ - min_scalar_type(a) - - For scalar ``a``, returns the data type with the smallest size - and smallest scalar kind which can hold its value. For non-scalar - array ``a``, returns the vector's dtype unmodified. - - Floating point values are not demoted to integers, - and complex values are not demoted to floats. - - Parameters - ---------- - a : scalar or array_like - The value whose minimal data type is to be found. - - Returns - ------- - out : dtype - The minimal data type. - - Notes - ----- - .. versionadded:: 1.6.0 + c_einsum(subscripts, *operands, out=None, dtype=None, order='K', + casting='safe') - See Also - -------- - result_type, promote_types, dtype, can_cast - - Examples - -------- - >>> np.min_scalar_type(10) - dtype('uint8') - - >>> np.min_scalar_type(-260) - dtype('int16') - - >>> np.min_scalar_type(3.1) - dtype('float16') + *This documentation shadows that of the native python implementation of the `einsum` function, + except all references and examples related to the `optimize` argument (v 0.12.0) have been removed.* - >>> np.min_scalar_type(1e50) - dtype('float64') - - >>> np.min_scalar_type(np.arange(4,dtype='f8')) - dtype('float64') - - """) - -add_newdoc('numpy.core.multiarray', 'result_type', - """ - result_type(*arrays_and_dtypes) + Evaluates the Einstein summation convention on the operands. - Returns the type that results from applying the NumPy - type promotion rules to the arguments. + Using the Einstein summation convention, many common multi-dimensional, + linear algebraic array operations can be represented in a simple fashion. + In *implicit* mode `einsum` computes these values. - Type promotion in NumPy works similarly to the rules in languages - like C++, with some slight differences. When both scalars and - arrays are used, the array's type takes precedence and the actual value - of the scalar is taken into account. + In *explicit* mode, `einsum` provides further flexibility to compute + other array operations that might not be considered classical Einstein + summation operations, by disabling, or forcing summation over specified + subscript labels. - For example, calculating 3*a, where a is an array of 32-bit floats, - intuitively should result in a 32-bit float output. If the 3 is a - 32-bit integer, the NumPy rules indicate it can't convert losslessly - into a 32-bit float, so a 64-bit float should be the result type. - By examining the value of the constant, '3', we see that it fits in - an 8-bit integer, which can be cast losslessly into the 32-bit float. + See the notes and examples for clarification. Parameters ---------- - arrays_and_dtypes : list of arrays and dtypes - The operands of some operation whose result type is needed. + subscripts : str + Specifies the subscripts for summation as comma separated list of + subscript labels. An implicit (classical Einstein summation) + calculation is performed unless the explicit indicator '->' is + included as well as subscript labels of the precise output form. + operands : list of array_like + These are the arrays for the operation. + out : ndarray, optional + If provided, the calculation is done into this array. + dtype : {data-type, None}, optional + If provided, forces the calculation to use the data type specified. + Note that you may have to also give a more liberal `casting` + parameter to allow the conversions. Default is None. + order : {'C', 'F', 'A', 'K'}, optional + Controls the memory layout of the output. 'C' means it should + be C contiguous. 'F' means it should be Fortran contiguous, + 'A' means it should be 'F' if the inputs are all 'F', 'C' otherwise. + 'K' means it should be as close to the layout of the inputs as + is possible, including arbitrarily permuted axes. + Default is 'K'. + casting : {'no', 'equiv', 'safe', 'same_kind', 'unsafe'}, optional + Controls what kind of data casting may occur. Setting this to + 'unsafe' is not recommended, as it can adversely affect accumulations. + + * 'no' means the data types should not be cast at all. + * 'equiv' means only byte-order changes are allowed. + * 'safe' means only casts which can preserve values are allowed. + * 'same_kind' means only safe casts or casts within a kind, + like float64 to float32, are allowed. + * 'unsafe' means any data conversions may be done. + + Default is 'safe'. + optimize : {False, True, 'greedy', 'optimal'}, optional + Controls if intermediate optimization should occur. No optimization + will occur if False and True will default to the 'greedy' algorithm. + Also accepts an explicit contraction list from the ``np.einsum_path`` + function. See ``np.einsum_path`` for more details. Defaults to False. Returns ------- - out : dtype - The result type. + output : ndarray + The calculation based on the Einstein summation convention. - See also + See Also -------- - dtype, promote_types, min_scalar_type, can_cast + einsum_path, dot, inner, outer, tensordot, linalg.multi_dot Notes ----- .. versionadded:: 1.6.0 - The specific algorithm used is as follows. + The Einstein summation convention can be used to compute + many multi-dimensional, linear algebraic array operations. `einsum` + provides a succinct way of representing these. - Categories are determined by first checking which of boolean, - integer (int/uint), or floating point (float/complex) the maximum - kind of all the arrays and the scalars are. + A non-exhaustive list of these operations, + which can be computed by `einsum`, is shown below along with examples: - If there are only scalars or the maximum category of the scalars - is higher than the maximum category of the arrays, - the data types are combined with :func:`promote_types` - to produce the return value. + * Trace of an array, :py:func:`numpy.trace`. + * Return a diagonal, :py:func:`numpy.diag`. + * Array axis summations, :py:func:`numpy.sum`. + * Transpositions and permutations, :py:func:`numpy.transpose`. + * Matrix multiplication and dot product, :py:func:`numpy.matmul` :py:func:`numpy.dot`. + * Vector inner and outer products, :py:func:`numpy.inner` :py:func:`numpy.outer`. + * Broadcasting, element-wise and scalar multiplication, :py:func:`numpy.multiply`. + * Tensor contractions, :py:func:`numpy.tensordot`. + * Chained array operations, in efficient calculation order, :py:func:`numpy.einsum_path`. - Otherwise, `min_scalar_type` is called on each array, and - the resulting data types are all combined with :func:`promote_types` - to produce the return value. - - The set of int values is not a subset of the uint values for types - with the same number of bits, something not reflected in - :func:`min_scalar_type`, but handled as a special case in `result_type`. - - Examples - -------- - >>> np.result_type(3, np.arange(7, dtype='i1')) - dtype('int8') - - >>> np.result_type('i4', 'c8') - dtype('complex128') - - >>> np.result_type(3.0, -2) - dtype('float64') - - """) - -add_newdoc('numpy.core.multiarray', 'newbuffer', - """ - newbuffer(size) - - Return a new uninitialized buffer object. - - Parameters - ---------- - size : int - Size in bytes of returned buffer object. - - Returns - ------- - newbuffer : buffer object - Returned, uninitialized buffer object of `size` bytes. - - """) - -add_newdoc('numpy.core.multiarray', 'getbuffer', - """ - getbuffer(obj [,offset[, size]]) - - Create a buffer object from the given object referencing a slice of - length size starting at offset. + The subscripts string is a comma-separated list of subscript labels, + where each label refers to a dimension of the corresponding operand. + Whenever a label is repeated it is summed, so ``np.einsum('i,i', a, b)`` + is equivalent to :py:func:`np.inner(a,b) <numpy.inner>`. If a label + appears only once, it is not summed, so ``np.einsum('i', a)`` produces a + view of ``a`` with no changes. A further example ``np.einsum('ij,jk', a, b)`` + describes traditional matrix multiplication and is equivalent to + :py:func:`np.matmul(a,b) <numpy.matmul>`. Repeated subscript labels in one + operand take the diagonal. For example, ``np.einsum('ii', a)`` is equivalent + to :py:func:`np.trace(a) <numpy.trace>`. + + In *implicit mode*, the chosen subscripts are important + since the axes of the output are reordered alphabetically. This + means that ``np.einsum('ij', a)`` doesn't affect a 2D array, while + ``np.einsum('ji', a)`` takes its transpose. Additionally, + ``np.einsum('ij,jk', a, b)`` returns a matrix multiplication, while, + ``np.einsum('ij,jh', a, b)`` returns the transpose of the + multiplication since subscript 'h' precedes subscript 'i'. + + In *explicit mode* the output can be directly controlled by + specifying output subscript labels. This requires the + identifier '->' as well as the list of output subscript labels. + This feature increases the flexibility of the function since + summing can be disabled or forced when required. The call + ``np.einsum('i->', a)`` is like :py:func:`np.sum(a, axis=-1) <numpy.sum>`, + and ``np.einsum('ii->i', a)`` is like :py:func:`np.diag(a) <numpy.diag>`. + The difference is that `einsum` does not allow broadcasting by default. + Additionally ``np.einsum('ij,jh->ih', a, b)`` directly specifies the + order of the output subscript labels and therefore returns matrix + multiplication, unlike the example above in implicit mode. - Default is the entire buffer. A read-write buffer is attempted followed - by a read-only buffer. + To enable and control broadcasting, use an ellipsis. Default + NumPy-style broadcasting is done by adding an ellipsis + to the left of each term, like ``np.einsum('...ii->...i', a)``. + To take the trace along the first and last axes, + you can do ``np.einsum('i...i', a)``, or to do a matrix-matrix + product with the left-most indices instead of rightmost, one can do + ``np.einsum('ij...,jk...->ik...', a, b)``. - Parameters - ---------- - obj : object + When there is only one operand, no axes are summed, and no output + parameter is provided, a view into the operand is returned instead + of a new array. Thus, taking the diagonal as ``np.einsum('ii->i', a)`` + produces a view (changed in version 1.10.0). - offset : int, optional + `einsum` also provides an alternative way to provide the subscripts + and operands as ``einsum(op0, sublist0, op1, sublist1, ..., [sublistout])``. + If the output shape is not provided in this format `einsum` will be + calculated in implicit mode, otherwise it will be performed explicitly. + The examples below have corresponding `einsum` calls with the two + parameter methods. - size : int, optional + .. versionadded:: 1.10.0 - Returns - ------- - buffer_obj : buffer + Views returned from einsum are now writeable whenever the input array + is writeable. For example, ``np.einsum('ijk...->kji...', a)`` will now + have the same effect as :py:func:`np.swapaxes(a, 0, 2) <numpy.swapaxes>` + and ``np.einsum('ii->i', a)`` will return a writeable view of the diagonal + of a 2D array. Examples -------- - >>> buf = np.getbuffer(np.ones(5), 1, 3) - >>> len(buf) - 3 - >>> buf[0] - '\\x00' - >>> buf - <read-write buffer for 0x8af1e70, size 3, offset 1 at 0x8ba4ec0> + >>> a = np.arange(25).reshape(5,5) + >>> b = np.arange(5) + >>> c = np.arange(6).reshape(2,3) - """) + Trace of a matrix: -add_newdoc('numpy.core', 'dot', - """ - dot(a, b, out=None) + >>> np.einsum('ii', a) + 60 + >>> np.einsum(a, [0,0]) + 60 + >>> np.trace(a) + 60 - Dot product of two arrays. Specifically, + Extract the diagonal (requires explicit form): - - If both `a` and `b` are 1-D arrays, it is inner product of vectors - (without complex conjugation). + >>> np.einsum('ii->i', a) + array([ 0, 6, 12, 18, 24]) + >>> np.einsum(a, [0,0], [0]) + array([ 0, 6, 12, 18, 24]) + >>> np.diag(a) + array([ 0, 6, 12, 18, 24]) - - If both `a` and `b` are 2-D arrays, it is matrix multiplication, - but using :func:`matmul` or ``a @ b`` is preferred. + Sum over an axis (requires explicit form): - - If either `a` or `b` is 0-D (scalar), it is equivalent to :func:`multiply` - and using ``numpy.multiply(a, b)`` or ``a * b`` is preferred. + >>> np.einsum('ij->i', a) + array([ 10, 35, 60, 85, 110]) + >>> np.einsum(a, [0,1], [0]) + array([ 10, 35, 60, 85, 110]) + >>> np.sum(a, axis=1) + array([ 10, 35, 60, 85, 110]) - - If `a` is an N-D array and `b` is a 1-D array, it is a sum product over - the last axis of `a` and `b`. + For higher dimensional arrays summing a single axis can be done with ellipsis: - - If `a` is an N-D array and `b` is an M-D array (where ``M>=2``), it is a - sum product over the last axis of `a` and the second-to-last axis of `b`:: + >>> np.einsum('...j->...', a) + array([ 10, 35, 60, 85, 110]) + >>> np.einsum(a, [Ellipsis,1], [Ellipsis]) + array([ 10, 35, 60, 85, 110]) - dot(a, b)[i,j,k,m] = sum(a[i,j,:] * b[k,:,m]) + Compute a matrix transpose, or reorder any number of axes: - Parameters - ---------- - a : array_like - First argument. - b : array_like - Second argument. - out : ndarray, optional - Output argument. This must have the exact kind that would be returned - if it was not used. In particular, it must have the right type, must be - C-contiguous, and its dtype must be the dtype that would be returned - for `dot(a,b)`. This is a performance feature. Therefore, if these - conditions are not met, an exception is raised, instead of attempting - to be flexible. - - Returns - ------- - output : ndarray - Returns the dot product of `a` and `b`. If `a` and `b` are both - scalars or both 1-D arrays then a scalar is returned; otherwise - an array is returned. - If `out` is given, then it is returned. - - Raises - ------ - ValueError - If the last dimension of `a` is not the same size as - the second-to-last dimension of `b`. - - See Also - -------- - vdot : Complex-conjugating dot product. - tensordot : Sum products over arbitrary axes. - einsum : Einstein summation convention. - matmul : '@' operator as method with out parameter. - - Examples - -------- - >>> np.dot(3, 4) - 12 - - Neither argument is complex-conjugated: - - >>> np.dot([2j, 3j], [2j, 3j]) - (-13+0j) - - For 2-D arrays it is the matrix product: - - >>> a = [[1, 0], [0, 1]] - >>> b = [[4, 1], [2, 2]] - >>> np.dot(a, b) - array([[4, 1], - [2, 2]]) - - >>> a = np.arange(3*4*5*6).reshape((3,4,5,6)) - >>> b = np.arange(3*4*5*6)[::-1].reshape((5,4,6,3)) - >>> np.dot(a, b)[2,3,2,1,2,2] - 499128 - >>> sum(a[2,3,2,:] * b[1,2,:,2]) - 499128 - - """) - -add_newdoc('numpy.core', 'matmul', - """ - matmul(a, b, out=None) - - Matrix product of two arrays. - - The behavior depends on the arguments in the following way. - - - If both arguments are 2-D they are multiplied like conventional - matrices. - - If either argument is N-D, N > 2, it is treated as a stack of - matrices residing in the last two indexes and broadcast accordingly. - - If the first argument is 1-D, it is promoted to a matrix by - prepending a 1 to its dimensions. After matrix multiplication - the prepended 1 is removed. - - If the second argument is 1-D, it is promoted to a matrix by - appending a 1 to its dimensions. After matrix multiplication - the appended 1 is removed. - - Multiplication by a scalar is not allowed, use ``*`` instead. Note that - multiplying a stack of matrices with a vector will result in a stack of - vectors, but matmul will not recognize it as such. - - ``matmul`` differs from ``dot`` in two important ways. - - - Multiplication by scalars is not allowed. - - Stacks of matrices are broadcast together as if the matrices - were elements. - - .. warning:: - This function is preliminary and included in NumPy 1.10.0 for testing - and documentation. Its semantics will not change, but the number and - order of the optional arguments will. - - .. versionadded:: 1.10.0 - - Parameters - ---------- - a : array_like - First argument. - b : array_like - Second argument. - out : ndarray, optional - Output argument. This must have the exact kind that would be returned - if it was not used. In particular, it must have the right type, must be - C-contiguous, and its dtype must be the dtype that would be returned - for `dot(a,b)`. This is a performance feature. Therefore, if these - conditions are not met, an exception is raised, instead of attempting - to be flexible. - - Returns - ------- - output : ndarray - Returns the dot product of `a` and `b`. If `a` and `b` are both - 1-D arrays then a scalar is returned; otherwise an array is - returned. If `out` is given, then it is returned. - - Raises - ------ - ValueError - If the last dimension of `a` is not the same size as - the second-to-last dimension of `b`. - - If scalar value is passed. - - See Also - -------- - vdot : Complex-conjugating dot product. - tensordot : Sum products over arbitrary axes. - einsum : Einstein summation convention. - dot : alternative matrix product with different broadcasting rules. - - Notes - ----- - The matmul function implements the semantics of the `@` operator introduced - in Python 3.5 following PEP465. - - Examples - -------- - For 2-D arrays it is the matrix product: - - >>> a = [[1, 0], [0, 1]] - >>> b = [[4, 1], [2, 2]] - >>> np.matmul(a, b) - array([[4, 1], - [2, 2]]) - - For 2-D mixed with 1-D, the result is the usual. - - >>> a = [[1, 0], [0, 1]] - >>> b = [1, 2] - >>> np.matmul(a, b) - array([1, 2]) - >>> np.matmul(b, a) - array([1, 2]) - - - Broadcasting is conventional for stacks of arrays - - >>> a = np.arange(2*2*4).reshape((2,2,4)) - >>> b = np.arange(2*2*4).reshape((2,4,2)) - >>> np.matmul(a,b).shape - (2, 2, 2) - >>> np.matmul(a,b)[0,1,1] - 98 - >>> sum(a[0,1,:] * b[0,:,1]) - 98 - - Vector, vector returns the scalar inner product, but neither argument - is complex-conjugated: - - >>> np.matmul([2j, 3j], [2j, 3j]) - (-13+0j) - - Scalar multiplication raises an error. - - >>> np.matmul([1,2], 3) - Traceback (most recent call last): - ... - ValueError: Scalar operands are not allowed, use '*' instead - - """) - - -add_newdoc('numpy.core', 'c_einsum', - """ - c_einsum(subscripts, *operands, out=None, dtype=None, order='K', casting='safe') - - Evaluates the Einstein summation convention on the operands. - - Using the Einstein summation convention, many common multi-dimensional - array operations can be represented in a simple fashion. This function - provides a way to compute such summations. The best way to understand this - function is to try the examples below, which show how many common NumPy - functions can be implemented as calls to `einsum`. - - This is the core C function. - - Parameters - ---------- - subscripts : str - Specifies the subscripts for summation. - operands : list of array_like - These are the arrays for the operation. - out : ndarray, optional - If provided, the calculation is done into this array. - dtype : {data-type, None}, optional - If provided, forces the calculation to use the data type specified. - Note that you may have to also give a more liberal `casting` - parameter to allow the conversions. Default is None. - order : {'C', 'F', 'A', 'K'}, optional - Controls the memory layout of the output. 'C' means it should - be C contiguous. 'F' means it should be Fortran contiguous, - 'A' means it should be 'F' if the inputs are all 'F', 'C' otherwise. - 'K' means it should be as close to the layout as the inputs as - is possible, including arbitrarily permuted axes. - Default is 'K'. - casting : {'no', 'equiv', 'safe', 'same_kind', 'unsafe'}, optional - Controls what kind of data casting may occur. Setting this to - 'unsafe' is not recommended, as it can adversely affect accumulations. - - * 'no' means the data types should not be cast at all. - * 'equiv' means only byte-order changes are allowed. - * 'safe' means only casts which can preserve values are allowed. - * 'same_kind' means only safe casts or casts within a kind, - like float64 to float32, are allowed. - * 'unsafe' means any data conversions may be done. - - Default is 'safe'. - - Returns - ------- - output : ndarray - The calculation based on the Einstein summation convention. - - See Also - -------- - einsum, dot, inner, outer, tensordot - - Notes - ----- - .. versionadded:: 1.6.0 - - The subscripts string is a comma-separated list of subscript labels, - where each label refers to a dimension of the corresponding operand. - Repeated subscripts labels in one operand take the diagonal. For example, - ``np.einsum('ii', a)`` is equivalent to ``np.trace(a)``. - - Whenever a label is repeated, it is summed, so ``np.einsum('i,i', a, b)`` - is equivalent to ``np.inner(a,b)``. If a label appears only once, - it is not summed, so ``np.einsum('i', a)`` produces a view of ``a`` - with no changes. - - The order of labels in the output is by default alphabetical. This - means that ``np.einsum('ij', a)`` doesn't affect a 2D array, while - ``np.einsum('ji', a)`` takes its transpose. - - The output can be controlled by specifying output subscript labels - as well. This specifies the label order, and allows summing to - be disallowed or forced when desired. The call ``np.einsum('i->', a)`` - is like ``np.sum(a, axis=-1)``, and ``np.einsum('ii->i', a)`` - is like ``np.diag(a)``. The difference is that `einsum` does not - allow broadcasting by default. - - To enable and control broadcasting, use an ellipsis. Default - NumPy-style broadcasting is done by adding an ellipsis - to the left of each term, like ``np.einsum('...ii->...i', a)``. - To take the trace along the first and last axes, - you can do ``np.einsum('i...i', a)``, or to do a matrix-matrix - product with the left-most indices instead of rightmost, you can do - ``np.einsum('ij...,jk...->ik...', a, b)``. - - When there is only one operand, no axes are summed, and no output - parameter is provided, a view into the operand is returned instead - of a new array. Thus, taking the diagonal as ``np.einsum('ii->i', a)`` - produces a view. - - An alternative way to provide the subscripts and operands is as - ``einsum(op0, sublist0, op1, sublist1, ..., [sublistout])``. The examples - below have corresponding `einsum` calls with the two parameter methods. - - .. versionadded:: 1.10.0 - - Views returned from einsum are now writeable whenever the input array - is writeable. For example, ``np.einsum('ijk...->kji...', a)`` will now - have the same effect as ``np.swapaxes(a, 0, 2)`` and - ``np.einsum('ii->i', a)`` will return a writeable view of the diagonal - of a 2D array. + >>> np.einsum('ji', c) + array([[0, 3], + [1, 4], + [2, 5]]) + >>> np.einsum('ij->ji', c) + array([[0, 3], + [1, 4], + [2, 5]]) + >>> np.einsum(c, [1,0]) + array([[0, 3], + [1, 4], + [2, 5]]) + >>> np.transpose(c) + array([[0, 3], + [1, 4], + [2, 5]]) - Examples - -------- - >>> a = np.arange(25).reshape(5,5) - >>> b = np.arange(5) - >>> c = np.arange(6).reshape(2,3) + Vector inner products: - >>> np.einsum('ii', a) - 60 - >>> np.einsum(a, [0,0]) - 60 - >>> np.trace(a) - 60 + >>> np.einsum('i,i', b, b) + 30 + >>> np.einsum(b, [0], b, [0]) + 30 + >>> np.inner(b,b) + 30 - >>> np.einsum('ii->i', a) - array([ 0, 6, 12, 18, 24]) - >>> np.einsum(a, [0,0], [0]) - array([ 0, 6, 12, 18, 24]) - >>> np.diag(a) - array([ 0, 6, 12, 18, 24]) + Matrix vector multiplication: >>> np.einsum('ij,j', a, b) array([ 30, 80, 130, 180, 230]) @@ -2400,20 +2040,12 @@ def luf(lamdaexpr, *args, **kwargs): >>> np.einsum('...j,j', a, b) array([ 30, 80, 130, 180, 230]) - >>> np.einsum('ji', c) - array([[0, 3], - [1, 4], - [2, 5]]) - >>> np.einsum(c, [1,0]) - array([[0, 3], - [1, 4], - [2, 5]]) - >>> c.T - array([[0, 3], - [1, 4], - [2, 5]]) + Broadcasting and scalar multiplication: >>> np.einsum('..., ...', 3, c) + array([[ 0, 3, 6], + [ 9, 12, 15]]) + >>> np.einsum(',ij', 3, c) array([[ 0, 3, 6], [ 9, 12, 15]]) >>> np.einsum(3, [Ellipsis], c, [Ellipsis]) @@ -2423,12 +2055,7 @@ def luf(lamdaexpr, *args, **kwargs): array([[ 0, 3, 6], [ 9, 12, 15]]) - >>> np.einsum('i,i', b, b) - 30 - >>> np.einsum(b, [0], b, [0]) - 30 - >>> np.inner(b,b) - 30 + Vector outer product: >>> np.einsum('i,j', np.arange(2)+1, b) array([[0, 1, 2, 3, 4], @@ -2440,12 +2067,7 @@ def luf(lamdaexpr, *args, **kwargs): array([[0, 1, 2, 3, 4], [0, 2, 4, 6, 8]]) - >>> np.einsum('i...->...', a) - array([50, 55, 60, 65, 70]) - >>> np.einsum(a, [0,Ellipsis], [Ellipsis]) - array([50, 55, 60, 65, 70]) - >>> np.sum(a, axis=0) - array([50, 55, 60, 65, 70]) + Tensor contraction: >>> a = np.arange(60.).reshape(3,4,5) >>> b = np.arange(24.).reshape(4,3,2) @@ -2468,6 +2090,17 @@ def luf(lamdaexpr, *args, **kwargs): [ 4796., 5162.], [ 4928., 5306.]]) + Writeable returned arrays (since version 1.10.0): + + >>> a = np.zeros((3, 3)) + >>> np.einsum('ii->i', a)[:] = 1 + >>> a + array([[ 1., 0., 0.], + [ 0., 1., 0.], + [ 0., 0., 1.]]) + + Example of ellipsis use: + >>> a = np.arange(6).reshape((3,2)) >>> b = np.arange(12).reshape((4,3)) >>> np.einsum('ki,jk->ij', a, b) @@ -2480,69 +2113,6 @@ def luf(lamdaexpr, *args, **kwargs): array([[10, 28, 46, 64], [13, 40, 67, 94]]) - >>> # since version 1.10.0 - >>> a = np.zeros((3, 3)) - >>> np.einsum('ii->i', a)[:] = 1 - >>> a - array([[ 1., 0., 0.], - [ 0., 1., 0.], - [ 0., 0., 1.]]) - - """) - -add_newdoc('numpy.core', 'vdot', - """ - vdot(a, b) - - Return the dot product of two vectors. - - The vdot(`a`, `b`) function handles complex numbers differently than - dot(`a`, `b`). If the first argument is complex the complex conjugate - of the first argument is used for the calculation of the dot product. - - Note that `vdot` handles multidimensional arrays differently than `dot`: - it does *not* perform a matrix product, but flattens input arguments - to 1-D vectors first. Consequently, it should only be used for vectors. - - Parameters - ---------- - a : array_like - If `a` is complex the complex conjugate is taken before calculation - of the dot product. - b : array_like - Second argument to the dot product. - - Returns - ------- - output : ndarray - Dot product of `a` and `b`. Can be an int, float, or - complex depending on the types of `a` and `b`. - - See Also - -------- - dot : Return the dot product without using the complex conjugate of the - first argument. - - Examples - -------- - >>> a = np.array([1+2j,3+4j]) - >>> b = np.array([5+6j,7+8j]) - >>> np.vdot(a, b) - (70-8j) - >>> np.vdot(b, a) - (70+8j) - - Note that higher-dimensional arrays are flattened! - - >>> a = np.array([[1, 4], [5, 6]]) - >>> b = np.array([[4, 1], [2, 2]]) - >>> np.vdot(a, b) - 30 - >>> np.vdot(b, a) - 30 - >>> 1*4 + 4*1 + 5*2 + 6*2 - 30 - """) @@ -2647,6 +2217,8 @@ def luf(lamdaexpr, *args, **kwargs): empty : Create an array, but leave its allocated memory unchanged (i.e., it contains "garbage"). dtype : Create a data-type. + numpy.typing.NDArray : An ndarray alias :term:`generic <generic type>` + w.r.t. its `dtype.type <numpy.dtype.type>`. Notes ----- @@ -2669,8 +2241,8 @@ def luf(lamdaexpr, *args, **kwargs): First mode, `buffer` is None: >>> np.ndarray(shape=(2,2), dtype=float, order='F') - array([[ -1.13698227e+002, 4.25087011e-303], - [ 2.88528414e-306, 3.27025015e-309]]) #random + array([[0.0e+000, 0.0e+000], # random + [ nan, 2.5e-323]]) Second mode: @@ -2704,13 +2276,15 @@ def luf(lamdaexpr, *args, **kwargs): add_newdoc('numpy.core.multiarray', 'ndarray', ('__array_struct__', """Array protocol: C-struct side.""")) +add_newdoc('numpy.core.multiarray', 'ndarray', ('__dlpack__', + """a.__dlpack__(*, stream=None) + + DLPack Protocol: Part of the Array API.""")) -add_newdoc('numpy.core.multiarray', 'ndarray', ('_as_parameter_', - """Allow the array to be interpreted as a ctypes object by returning the - data-memory location as an integer - - """)) - +add_newdoc('numpy.core.multiarray', 'ndarray', ('__dlpack_device__', + """a.__dlpack_device__() + + DLPack Protocol: Part of the Array API.""")) add_newdoc('numpy.core.multiarray', 'ndarray', ('base', """ @@ -2763,75 +2337,49 @@ def luf(lamdaexpr, *args, **kwargs): in "Guide to NumPy" (we have omitted undocumented public attributes, as well as documented private attributes): - * data: A pointer to the memory area of the array as a Python integer. - This memory area may contain data that is not aligned, or not in correct - byte-order. The memory area may not even be writeable. The array - flags and data-type of this array should be respected when passing this - attribute to arbitrary C-code to avoid trouble that can include Python - crashing. User Beware! The value of this attribute is exactly the same - as self._array_interface_['data'][0]. - - * shape (c_intp*self.ndim): A ctypes array of length self.ndim where - the basetype is the C-integer corresponding to dtype('p') on this - platform. This base-type could be c_int, c_long, or c_longlong - depending on the platform. The c_intp type is defined accordingly in - numpy.ctypeslib. The ctypes array contains the shape of the underlying - array. - - * strides (c_intp*self.ndim): A ctypes array of length self.ndim where - the basetype is the same as for the shape attribute. This ctypes array - contains the strides information from the underlying array. This strides - information is important for showing how many bytes must be jumped to - get to the next element in the array. - - * data_as(obj): Return the data pointer cast to a particular c-types object. - For example, calling self._as_parameter_ is equivalent to - self.data_as(ctypes.c_void_p). Perhaps you want to use the data as a - pointer to a ctypes array of floating-point data: - self.data_as(ctypes.POINTER(ctypes.c_double)). - - * shape_as(obj): Return the shape tuple as an array of some other c-types - type. For example: self.shape_as(ctypes.c_short). - - * strides_as(obj): Return the strides tuple as an array of some other - c-types type. For example: self.strides_as(ctypes.c_longlong). - - Be careful using the ctypes attribute - especially on temporary - arrays or arrays constructed on the fly. For example, calling - ``(a+b).ctypes.data_as(ctypes.c_void_p)`` returns a pointer to memory - that is invalid because the array created as (a+b) is deallocated - before the next Python statement. You can avoid this problem using - either ``c=a+b`` or ``ct=(a+b).ctypes``. In the latter case, ct will - hold a reference to the array until ct is deleted or re-assigned. + .. autoattribute:: numpy.core._internal._ctypes.data + :noindex: + + .. autoattribute:: numpy.core._internal._ctypes.shape + :noindex: + + .. autoattribute:: numpy.core._internal._ctypes.strides + :noindex: + + .. automethod:: numpy.core._internal._ctypes.data_as + :noindex: + + .. automethod:: numpy.core._internal._ctypes.shape_as + :noindex: + + .. automethod:: numpy.core._internal._ctypes.strides_as + :noindex: If the ctypes module is not available, then the ctypes attribute of array objects still returns something useful, but ctypes objects are not returned and errors may be raised instead. In particular, - the object will still have the as parameter attribute which will + the object will still have the ``as_parameter`` attribute which will return an integer equal to the data attribute. Examples -------- >>> import ctypes + >>> x = np.array([[0, 1], [2, 3]], dtype=np.int32) >>> x array([[0, 1], - [2, 3]]) + [2, 3]], dtype=int32) >>> x.ctypes.data - 30439712 - >>> x.ctypes.data_as(ctypes.POINTER(ctypes.c_long)) - <ctypes.LP_c_long object at 0x01F01300> - >>> x.ctypes.data_as(ctypes.POINTER(ctypes.c_long)).contents - c_long(0) - >>> x.ctypes.data_as(ctypes.POINTER(ctypes.c_longlong)).contents - c_longlong(4294967296L) + 31962608 # may vary + >>> x.ctypes.data_as(ctypes.POINTER(ctypes.c_uint32)) + <__main__.LP_c_uint object at 0x7ff2fc1fc200> # may vary + >>> x.ctypes.data_as(ctypes.POINTER(ctypes.c_uint32)).contents + c_uint(0) + >>> x.ctypes.data_as(ctypes.POINTER(ctypes.c_uint64)).contents + c_ulong(4294967296) >>> x.ctypes.shape - <numpy.core._internal.c_long_Array_2 object at 0x01FFD580> - >>> x.ctypes.shape_as(ctypes.c_long) - <numpy.core._internal.c_long_Array_2 object at 0x01FCE620> + <numpy.core._internal.c_long_Array_2 object at 0x7ff2fc1fce60> # may vary >>> x.ctypes.strides - <numpy.core._internal.c_long_Array_2 object at 0x01FCE620> - >>> x.ctypes.strides_as(ctypes.c_longlong) - <numpy.core._internal.c_longlong_Array_2 object at 0x01F01300> + <numpy.core._internal.c_long_Array_2 object at 0x7ff2fc1ff320> # may vary """)) @@ -3005,7 +2553,7 @@ def luf(lamdaexpr, *args, **kwargs): >>> x.T.flat[3] 5 >>> type(x.flat) - <type 'numpy.flatiter'> + <class 'numpy.flatiter'> An assignment example: @@ -3105,7 +2653,8 @@ def luf(lamdaexpr, *args, **kwargs): >>> np.zeros((4,2))[::2].shape = (-1,) Traceback (most recent call last): File "<stdin>", line 1, in <module> - AttributeError: incompatible shape for a non-contiguous array + AttributeError: Incompatible shape for in-place modification. Use + `.reshape()` to make a copy with the desired shape. See Also -------- @@ -3124,7 +2673,7 @@ def luf(lamdaexpr, *args, **kwargs): Notes ----- - `a.size` returns a standard arbitrary precision Python integer. This + `a.size` returns a standard arbitrary precision Python integer. This may not be the case with other methods of obtaining the same value (like the suggested ``np.prod(a.shape)``, which returns an instance of ``np.int_``), and may be relevant if the value is used further in @@ -3205,8 +2754,9 @@ def luf(lamdaexpr, *args, **kwargs): add_newdoc('numpy.core.multiarray', 'ndarray', ('T', """ - Same as self.transpose(), except that self is returned if - self.ndim < 2. + The transposed array. + + Same as ``self.transpose()``. Examples -------- @@ -3223,6 +2773,10 @@ def luf(lamdaexpr, *args, **kwargs): >>> x.T array([ 1., 2., 3., 4.]) + See Also + -------- + transpose + """)) @@ -3234,7 +2788,7 @@ def luf(lamdaexpr, *args, **kwargs): add_newdoc('numpy.core.multiarray', 'ndarray', ('__array__', - """ a.__array__(|dtype) -> reference if type unchanged, copy otherwise. + """ a.__array__([dtype], /) -> reference if type unchanged, copy otherwise. Returns either a new reference to self if dtype is not given or a new array of provided data type if dtype is different from the current dtype of the @@ -3244,13 +2798,17 @@ def luf(lamdaexpr, *args, **kwargs): add_newdoc('numpy.core.multiarray', 'ndarray', ('__array_prepare__', - """a.__array_prepare__(obj) -> Object of same type as ndarray object obj. + """a.__array_prepare__(array[, context], /) + + Returns a view of `array` with the same type as self. """)) add_newdoc('numpy.core.multiarray', 'ndarray', ('__array_wrap__', - """a.__array_wrap__(obj) -> Object of same type as ndarray object a. + """a.__array_wrap__(array[, context], /) + + Returns a view of `array` with the same type as self. """)) @@ -3265,6 +2823,39 @@ def luf(lamdaexpr, *args, **kwargs): """)) +add_newdoc('numpy.core.multiarray', 'ndarray', ('__class_getitem__', + """a.__class_getitem__(item, /) + + Return a parametrized wrapper around the `~numpy.ndarray` type. + + .. versionadded:: 1.22 + + Returns + ------- + alias : types.GenericAlias + A parametrized `~numpy.ndarray` type. + + Examples + -------- + >>> from typing import Any + >>> import numpy as np + + >>> np.ndarray[Any, np.dtype[Any]] + numpy.ndarray[typing.Any, numpy.dtype[typing.Any]] + + Notes + ----- + This method is only available for python 3.9 and later. + + See Also + -------- + :pep:`585` : Type hinting generics in standard collections. + numpy.typing.NDArray : An ndarray alias :term:`generic <generic type>` + w.r.t. its `dtype.type <numpy.dtype.type>`. + + """)) + + add_newdoc('numpy.core.multiarray', 'ndarray', ('__deepcopy__', """a.__deepcopy__(memo, /) -> Deep copy of array. @@ -3304,7 +2895,7 @@ def luf(lamdaexpr, *args, **kwargs): add_newdoc('numpy.core.multiarray', 'ndarray', ('all', """ - a.all(axis=None, out=None, keepdims=False) + a.all(axis=None, out=None, keepdims=False, *, where=True) Returns True if all elements evaluate to True. @@ -3319,7 +2910,7 @@ def luf(lamdaexpr, *args, **kwargs): add_newdoc('numpy.core.multiarray', 'ndarray', ('any', """ - a.any(axis=None, out=None, keepdims=False) + a.any(axis=None, out=None, keepdims=False, *, where=True) Returns True if any of the elements of `a` evaluate to True. @@ -3351,7 +2942,7 @@ def luf(lamdaexpr, *args, **kwargs): """ a.argmin(axis=None, out=None) - Return indices of the minimum values along the given axis of `a`. + Return indices of the minimum values along the given axis. Refer to `numpy.argmin` for detailed documentation. @@ -3364,7 +2955,7 @@ def luf(lamdaexpr, *args, **kwargs): add_newdoc('numpy.core.multiarray', 'ndarray', ('argsort', """ - a.argsort(axis=-1, kind='quicksort', order=None) + a.argsort(axis=-1, kind=None, order=None) Returns the indices that would sort this array. @@ -3440,10 +3031,15 @@ def luf(lamdaexpr, *args, **kwargs): Notes ----- - Starting in NumPy 1.9, astype method now returns an error if the string - dtype to cast to is not long enough in 'safe' casting mode to hold the max - value of integer/float array that is being casted. Previously the casting - was allowed even if the result was truncated. + .. versionchanged:: 1.17.0 + Casting between a simple data type and a structured one is possible only + for "unsafe" casting. Casting to multiple fields is allowed, but + casting from multiple fields is not. + + .. versionchanged:: 1.9.0 + Casting from numeric to string types in 'safe' casting mode requires + that the string dtype length is long enough to store the max + integer/float value converted. Raises ------ @@ -3455,7 +3051,7 @@ def luf(lamdaexpr, *args, **kwargs): -------- >>> x = np.array([1, 2, 2.5]) >>> x - array([ 1. , 2. , 2.5]) + array([1. , 2. , 2.5]) >>> x.astype(int) array([1, 2, 2]) @@ -3471,6 +3067,8 @@ def luf(lamdaexpr, *args, **kwargs): Toggle between low-endian and big-endian data representation by returning a byteswapped array, optionally swapped in-place. + Arrays of byte-strings are not swapped. The real and imaginary + parts of a complex number are swapped individually. Parameters ---------- @@ -3486,19 +3084,31 @@ def luf(lamdaexpr, *args, **kwargs): Examples -------- >>> A = np.array([1, 256, 8755], dtype=np.int16) - >>> map(hex, A) + >>> list(map(hex, A)) ['0x1', '0x100', '0x2233'] >>> A.byteswap(inplace=True) array([ 256, 1, 13090], dtype=int16) - >>> map(hex, A) + >>> list(map(hex, A)) ['0x100', '0x1', '0x3322'] - Arrays of strings are not swapped + Arrays of byte-strings are not swapped - >>> A = np.array(['ceg', 'fac']) + >>> A = np.array([b'ceg', b'fac']) >>> A.byteswap() - array(['ceg', 'fac'], - dtype='|S3') + array([b'ceg', b'fac'], dtype='|S3') + + ``A.newbyteorder().byteswap()`` produces an array with the same values + but different representation in memory + + >>> A = np.array([1, 2, 3]) + >>> A.view(np.uint8) + array([1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, + 0, 0], dtype=uint8) + >>> A.newbyteorder().byteswap(inplace=True) + array([1, 2, 3]) + >>> A.view(np.uint8) + array([0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, + 0, 3], dtype=uint8) """)) @@ -3520,7 +3130,7 @@ def luf(lamdaexpr, *args, **kwargs): add_newdoc('numpy.core.multiarray', 'ndarray', ('clip', """ - a.clip(min=None, max=None, out=None) + a.clip(min=None, max=None, out=None, **kwargs) Return an array whose values are limited to ``[min, max]``. One of max or min must be given. @@ -3592,14 +3202,20 @@ def luf(lamdaexpr, *args, **kwargs): 'F' means F-order, 'A' means 'F' if `a` is Fortran contiguous, 'C' otherwise. 'K' means match the layout of `a` as closely as possible. (Note that this function and :func:`numpy.copy` are very - similar, but have different default values for their order= - arguments.) + similar but have different default values for their order= + arguments, and this function always passes sub-classes through.) See also -------- - numpy.copy + numpy.copy : Similar function with different default behavior numpy.copyto + Notes + ----- + This function is the preferred method for creating an array copy. The + function :func:`numpy.copy` is similar, but it defaults to using order 'K', + and will not pass sub-classes through by default. + Examples -------- >>> x = np.array([[1,2,3],[4,5,6]], order='F') @@ -3669,33 +3285,7 @@ def luf(lamdaexpr, *args, **kwargs): """)) -add_newdoc('numpy.core.multiarray', 'ndarray', ('dot', - """ - a.dot(b, out=None) - - Dot product of two arrays. - - Refer to `numpy.dot` for full documentation. - - See Also - -------- - numpy.dot : equivalent function - - Examples - -------- - >>> a = np.eye(2) - >>> b = np.ones((2, 2)) * 2 - >>> a.dot(b) - array([[ 2., 2.], - [ 2., 2.]]) - - This array method can be conveniently chained: - - >>> a.dot(b).dot(b) - array([[ 8., 8.], - [ 8., 8.]]) - - """)) +add_newdoc('numpy.core.multiarray', 'ndarray', ('dot')) add_newdoc('numpy.core.multiarray', 'ndarray', ('dump', @@ -3706,9 +3296,12 @@ def luf(lamdaexpr, *args, **kwargs): Parameters ---------- - file : str + file : str or Path A string naming the dump file. + .. versionchanged:: 1.17.0 + `pathlib.Path` objects are now accepted. + """)) @@ -3717,7 +3310,7 @@ def luf(lamdaexpr, *args, **kwargs): a.dumps() Returns the pickle of the array as a string. - pickle.loads or numpy.loads will convert the string back to an array. + pickle.loads will convert the string back to an array. Parameters ---------- @@ -3746,7 +3339,7 @@ def luf(lamdaexpr, *args, **kwargs): >>> a = np.empty(2) >>> a.fill(1) >>> a - array([ 1., 1.]) + array([1., 1.]) """)) @@ -3815,18 +3408,18 @@ def luf(lamdaexpr, *args, **kwargs): >>> x = np.diag([1.+1.j]*2) >>> x[1, 1] = 2 + 4.j >>> x - array([[ 1.+1.j, 0.+0.j], - [ 0.+0.j, 2.+4.j]]) + array([[1.+1.j, 0.+0.j], + [0.+0.j, 2.+4.j]]) >>> x.getfield(np.float64) - array([[ 1., 0.], - [ 0., 2.]]) + array([[1., 0.], + [0., 2.]]) By choosing an offset of 8 bytes we can select the complex part of the array for our view: >>> x.getfield(np.float64, offset=8) - array([[ 1., 0.], - [ 0., 4.]]) + array([[1., 0.], + [0., 4.]]) """)) @@ -3872,19 +3465,20 @@ def luf(lamdaexpr, *args, **kwargs): Examples -------- + >>> np.random.seed(123) >>> x = np.random.randint(9, size=(3, 3)) >>> x - array([[3, 1, 7], - [2, 8, 3], - [8, 5, 3]]) + array([[2, 2, 6], + [1, 3, 6], + [1, 0, 1]]) >>> x.item(3) - 2 + 1 >>> x.item(7) - 5 + 0 >>> x.item((0, 1)) - 1 + 2 >>> x.item((2, 2)) - 3 + 1 """)) @@ -3920,24 +3514,25 @@ def luf(lamdaexpr, *args, **kwargs): Examples -------- + >>> np.random.seed(123) >>> x = np.random.randint(9, size=(3, 3)) >>> x - array([[3, 1, 7], - [2, 8, 3], - [8, 5, 3]]) + array([[2, 2, 6], + [1, 3, 6], + [1, 0, 1]]) >>> x.itemset(4, 0) >>> x.itemset((2, 2), 9) >>> x - array([[3, 1, 7], - [2, 0, 3], - [8, 5, 9]]) + array([[2, 2, 6], + [1, 0, 6], + [1, 0, 9]]) """)) add_newdoc('numpy.core.multiarray', 'ndarray', ('max', """ - a.max(axis=None, out=None, keepdims=False) + a.max(axis=None, out=None, keepdims=False, initial=<no value>, where=True) Return the maximum along a given axis. @@ -3952,7 +3547,7 @@ def luf(lamdaexpr, *args, **kwargs): add_newdoc('numpy.core.multiarray', 'ndarray', ('mean', """ - a.mean(axis=None, dtype=None, out=None, keepdims=False) + a.mean(axis=None, dtype=None, out=None, keepdims=False, *, where=True) Returns the average of the array elements along given axis. @@ -3967,7 +3562,7 @@ def luf(lamdaexpr, *args, **kwargs): add_newdoc('numpy.core.multiarray', 'ndarray', ('min', """ - a.min(axis=None, out=None, keepdims=False) + a.min(axis=None, out=None, keepdims=False, initial=<no value>, where=True) Return the minimum along a given axis. @@ -3980,90 +3575,9 @@ def luf(lamdaexpr, *args, **kwargs): """)) -add_newdoc('numpy.core.multiarray', 'shares_memory', - """ - shares_memory(a, b, max_work=None) - - Determine if two arrays share memory - - Parameters - ---------- - a, b : ndarray - Input arrays - max_work : int, optional - Effort to spend on solving the overlap problem (maximum number - of candidate solutions to consider). The following special - values are recognized: - - max_work=MAY_SHARE_EXACT (default) - The problem is solved exactly. In this case, the function returns - True only if there is an element shared between the arrays. - max_work=MAY_SHARE_BOUNDS - Only the memory bounds of a and b are checked. - - Raises - ------ - numpy.TooHardError - Exceeded max_work. - - Returns - ------- - out : bool - - See Also - -------- - may_share_memory - - Examples - -------- - >>> np.may_share_memory(np.array([1,2]), np.array([5,8,9])) - False - - """) - - -add_newdoc('numpy.core.multiarray', 'may_share_memory', - """ - may_share_memory(a, b, max_work=None) - - Determine if two arrays might share memory - - A return of True does not necessarily mean that the two arrays - share any element. It just means that they *might*. - - Only the memory bounds of a and b are checked by default. - - Parameters - ---------- - a, b : ndarray - Input arrays - max_work : int, optional - Effort to spend on solving the overlap problem. See - `shares_memory` for details. Default for ``may_share_memory`` - is to do a bounds check. - - Returns - ------- - out : bool - - See Also - -------- - shares_memory - - Examples - -------- - >>> np.may_share_memory(np.array([1,2]), np.array([5,8,9])) - False - >>> x = np.zeros([3, 4]) - >>> np.may_share_memory(x[:,0], x[:,1]) - True - - """) - - add_newdoc('numpy.core.multiarray', 'ndarray', ('newbyteorder', """ - arr.newbyteorder(new_order='S') + arr.newbyteorder(new_order='S', /) Return the array with the same data viewed with a different byte order. @@ -4083,15 +3597,13 @@ def luf(lamdaexpr, *args, **kwargs): below. `new_order` codes can be any of: * 'S' - swap dtype from current to opposite endian - * {'<', 'L'} - little endian - * {'>', 'B'} - big endian - * {'=', 'N'} - native order + * {'<', 'little'} - little endian + * {'>', 'big'} - big endian + * {'=', 'native'} - native order, equivalent to `sys.byteorder` * {'|', 'I'} - ignore (no change to byte order) The default value ('S') results in swapping the current - byte order. The code does a case-insensitive check on the first - letter of `new_order` for the alternatives above. For example, - any of 'B' or 'b' or 'biggish' are valid to specify big-endian. + byte order. Returns @@ -4120,7 +3632,7 @@ def luf(lamdaexpr, *args, **kwargs): add_newdoc('numpy.core.multiarray', 'ndarray', ('prod', """ - a.prod(axis=None, dtype=None, out=None, keepdims=False) + a.prod(axis=None, dtype=None, out=None, keepdims=False, initial=1, where=True) Return the product of the array elements over the given axis @@ -4162,81 +3674,6 @@ def luf(lamdaexpr, *args, **kwargs): """)) -add_newdoc('numpy.core.multiarray', 'copyto', - """ - copyto(dst, src, casting='same_kind', where=True) - - Copies values from one array to another, broadcasting as necessary. - - Raises a TypeError if the `casting` rule is violated, and if - `where` is provided, it selects which elements to copy. - - .. versionadded:: 1.7.0 - - Parameters - ---------- - dst : ndarray - The array into which values are copied. - src : array_like - The array from which values are copied. - casting : {'no', 'equiv', 'safe', 'same_kind', 'unsafe'}, optional - Controls what kind of data casting may occur when copying. - - * 'no' means the data types should not be cast at all. - * 'equiv' means only byte-order changes are allowed. - * 'safe' means only casts which can preserve values are allowed. - * 'same_kind' means only safe casts or casts within a kind, - like float64 to float32, are allowed. - * 'unsafe' means any data conversions may be done. - where : array_like of bool, optional - A boolean array which is broadcasted to match the dimensions - of `dst`, and selects elements to copy from `src` to `dst` - wherever it contains the value True. - - """) - -add_newdoc('numpy.core.multiarray', 'putmask', - """ - putmask(a, mask, values) - - Changes elements of an array based on conditional and input values. - - Sets ``a.flat[n] = values[n]`` for each n where ``mask.flat[n]==True``. - - If `values` is not the same size as `a` and `mask` then it will repeat. - This gives behavior different from ``a[mask] = values``. - - Parameters - ---------- - a : array_like - Target array. - mask : array_like - Boolean mask array. It has to be the same shape as `a`. - values : array_like - Values to put into `a` where `mask` is True. If `values` is smaller - than `a` it will be repeated. - - See Also - -------- - place, put, take, copyto - - Examples - -------- - >>> x = np.arange(6).reshape(2, 3) - >>> np.putmask(x, x>2, x**2) - >>> x - array([[ 0, 1, 2], - [ 9, 16, 25]]) - - If `values` is smaller than `a` it is repeated: - - >>> x = np.arange(5) - >>> np.putmask(x, x>1, [-33, -44]) - >>> x - array([ 0, 1, -33, -44, -33]) - - """) - add_newdoc('numpy.core.multiarray', 'ndarray', ('ravel', """ @@ -4371,7 +3808,7 @@ def luf(lamdaexpr, *args, **kwargs): >>> a.resize((1, 1)) Traceback (most recent call last): ... - ValueError: cannot resize an array that has been referenced ... + ValueError: cannot resize an array that references or is referenced ... Unless `refcheck` is False: @@ -4444,23 +3881,23 @@ def luf(lamdaexpr, *args, **kwargs): -------- >>> x = np.eye(3) >>> x.getfield(np.float64) - array([[ 1., 0., 0.], - [ 0., 1., 0.], - [ 0., 0., 1.]]) + array([[1., 0., 0.], + [0., 1., 0.], + [0., 0., 1.]]) >>> x.setfield(3, np.int32) >>> x.getfield(np.int32) array([[3, 3, 3], [3, 3, 3], - [3, 3, 3]]) + [3, 3, 3]], dtype=int32) >>> x - array([[ 1.00000000e+000, 1.48219694e-323, 1.48219694e-323], - [ 1.48219694e-323, 1.00000000e+000, 1.48219694e-323], - [ 1.48219694e-323, 1.48219694e-323, 1.00000000e+000]]) + array([[1.0e+000, 1.5e-323, 1.5e-323], + [1.5e-323, 1.0e+000, 1.5e-323], + [1.5e-323, 1.5e-323, 1.0e+000]]) >>> x.setfield(np.eye(3), np.int32) >>> x - array([[ 1., 0., 0.], - [ 0., 1., 0.], - [ 0., 0., 1.]]) + array([[1., 0., 0.], + [0., 1., 0.], + [0., 0., 1.]]) """)) @@ -4513,6 +3950,9 @@ def luf(lamdaexpr, *args, **kwargs): Examples -------- + >>> y = np.array([[3, 1, 7], + ... [2, 0, 0], + ... [8, 5, 9]]) >>> y array([[3, 1, 7], [2, 0, 0], @@ -4544,9 +3984,9 @@ def luf(lamdaexpr, *args, **kwargs): add_newdoc('numpy.core.multiarray', 'ndarray', ('sort', """ - a.sort(axis=-1, kind='quicksort', order=None) + a.sort(axis=-1, kind=None, order=None) - Sort an array, in-place. + Sort an array in-place. Refer to `numpy.sort` for full documentation. Parameters ---------- @@ -4554,7 +3994,14 @@ def luf(lamdaexpr, *args, **kwargs): Axis along which to sort. Default is -1, which means sort along the last axis. kind : {'quicksort', 'mergesort', 'heapsort', 'stable'}, optional - Sorting algorithm. Default is 'quicksort'. + Sorting algorithm. The default is 'quicksort'. Note that both 'stable' + and 'mergesort' use timsort under the covers and, in general, the + actual implementation will vary with datatype. The 'mergesort' option + is retained for backwards compatibility. + + .. versionchanged:: 1.15.0 + The 'stable' option was added. + order : str or list of str, optional When `a` is an array with fields defined, this argument specifies which fields to compare first, second, etc. A single field can @@ -4565,14 +4012,14 @@ def luf(lamdaexpr, *args, **kwargs): See Also -------- numpy.sort : Return a sorted copy of an array. - argsort : Indirect sort. - lexsort : Indirect stable sort on multiple keys. - searchsorted : Find elements in sorted array. - partition: Partial sort. + numpy.argsort : Indirect sort. + numpy.lexsort : Indirect stable sort on multiple keys. + numpy.searchsorted : Find elements in sorted array. + numpy.partition: Partial sort. Notes ----- - See ``sort`` for notes on the different sorting algorithms. + See `numpy.sort` for notes on the different sorting algorithms. Examples -------- @@ -4592,8 +4039,8 @@ def luf(lamdaexpr, *args, **kwargs): >>> a = np.array([('a', 2), ('c', 1)], dtype=[('x', 'S1'), ('y', int)]) >>> a.sort(order='y') >>> a - array([('c', 1), ('a', 2)], - dtype=[('x', '|S1'), ('y', '<i4')]) + array([(b'c', 1), (b'a', 2)], + dtype=[('x', 'S1'), ('y', '<i8')]) """)) @@ -4619,6 +4066,9 @@ def luf(lamdaexpr, *args, **kwargs): The order of all elements in the partitions is undefined. If provided with a sequence of kth it will partition all elements indexed by kth of them into their sorted position at once. + + .. deprecated:: 1.22.0 + Passing booleans as index is deprecated. axis : int, optional Axis along which to sort. Default is -1, which means sort along the last axis. @@ -4649,6 +4099,7 @@ def luf(lamdaexpr, *args, **kwargs): array([2, 1, 3, 4]) >>> a.partition((1, 3)) + >>> a array([1, 2, 3, 4]) """)) @@ -4657,7 +4108,7 @@ def luf(lamdaexpr, *args, **kwargs): """ a.squeeze(axis=None) - Remove single-dimensional entries from the shape of `a`. + Remove axes of length one from `a`. Refer to `numpy.squeeze` for full documentation. @@ -4670,7 +4121,7 @@ def luf(lamdaexpr, *args, **kwargs): add_newdoc('numpy.core.multiarray', 'ndarray', ('std', """ - a.std(axis=None, dtype=None, out=None, ddof=0, keepdims=False) + a.std(axis=None, dtype=None, out=None, ddof=0, keepdims=False, *, where=True) Returns the standard deviation of the array elements along given axis. @@ -4685,7 +4136,7 @@ def luf(lamdaexpr, *args, **kwargs): add_newdoc('numpy.core.multiarray', 'ndarray', ('sum', """ - a.sum(axis=None, dtype=None, out=None, keepdims=False) + a.sum(axis=None, dtype=None, out=None, keepdims=False, initial=0, where=True) Return the sum of the array elements over the given axis. @@ -4740,8 +4191,12 @@ def luf(lamdaexpr, *args, **kwargs): Parameters ---------- - fid : file or str + fid : file or str or Path An open file object, or a string containing a filename. + + .. versionchanged:: 1.17.0 + `pathlib.Path` objects are now accepted. + sep : str Separator between array items for text output. If "" (empty), a binary file is written, equivalent to @@ -4759,7 +4214,7 @@ def luf(lamdaexpr, *args, **kwargs): machines with different endianness. Some of these problems can be overcome by outputting the data as text files, at the expense of speed and file size. - + When fid is a file object, array contents are directly written to the file, bypassing the file object's ``write`` method. As a result, tofile cannot be used with files objects supporting compression (e.g., GzipFile) @@ -4772,10 +4227,14 @@ def luf(lamdaexpr, *args, **kwargs): """ a.tolist() - Return the array as a (possibly nested) list. + Return the array as an ``a.ndim``-levels deep nested list of Python scalars. Return a copy of the array data as a (nested) Python list. - Data items are converted to the nearest compatible Python type. + Data items are converted to the nearest compatible builtin Python type, via + the `~numpy.ndarray.item` function. + + If ``a.ndim`` is 0, then since the depth of the nested list is 0, it will + not be a list at all, but a simple Python scalar. Parameters ---------- @@ -4783,45 +4242,68 @@ def luf(lamdaexpr, *args, **kwargs): Returns ------- - y : list + y : object, or list of object, or list of list of object, or ... The possibly nested list of array elements. Notes ----- - The array may be recreated, ``a = np.array(a.tolist())``. + The array may be recreated via ``a = np.array(a.tolist())``, although this + may sometimes lose precision. Examples -------- - >>> a = np.array([1, 2]) - >>> a.tolist() + For a 1D array, ``a.tolist()`` is almost the same as ``list(a)``, + except that ``tolist`` changes numpy scalars to Python scalars: + + >>> a = np.uint32([1, 2]) + >>> a_list = list(a) + >>> a_list [1, 2] + >>> type(a_list[0]) + <class 'numpy.uint32'> + >>> a_tolist = a.tolist() + >>> a_tolist + [1, 2] + >>> type(a_tolist[0]) + <class 'int'> + + Additionally, for a 2D array, ``tolist`` applies recursively: + >>> a = np.array([[1, 2], [3, 4]]) >>> list(a) [array([1, 2]), array([3, 4])] >>> a.tolist() [[1, 2], [3, 4]] + The base case for this recursion is a 0D array: + + >>> a = np.array(1) + >>> list(a) + Traceback (most recent call last): + ... + TypeError: iteration over a 0-d array + >>> a.tolist() + 1 """)) -tobytesdoc = """ - a.{name}(order='C') +add_newdoc('numpy.core.multiarray', 'ndarray', ('tobytes', """ + a.tobytes(order='C') Construct Python bytes containing the raw data bytes in the array. Constructs Python bytes showing a copy of the raw contents of - data memory. The bytes object can be produced in either 'C' or 'Fortran', - or 'Any' order (the default is 'C'-order). 'Any' order means C-order - unless the F_CONTIGUOUS flag in the array is set, in which case it - means 'Fortran' order. + data memory. The bytes object is produced in C-order by default. + This behavior is controlled by the ``order`` parameter. - {deprecated} + .. versionadded:: 1.9.0 Parameters ---------- - order : {{'C', 'F', None}}, optional - Order of the data for multidimensional arrays: - C, Fortran, or the same as for the original array. + order : {'C', 'F', 'A'}, optional + Controls the memory layout of the bytes object. 'C' means C-order, + 'F' means F-order, 'A' (short for *Any*) means 'F' if `a` is + Fortran contiguous, 'C' otherwise. Default is 'C'. Returns ------- @@ -4830,26 +4312,27 @@ def luf(lamdaexpr, *args, **kwargs): Examples -------- - >>> x = np.array([[0, 1], [2, 3]]) + >>> x = np.array([[0, 1], [2, 3]], dtype='<u2') >>> x.tobytes() - b'\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x03\\x00\\x00\\x00' + b'\\x00\\x00\\x01\\x00\\x02\\x00\\x03\\x00' >>> x.tobytes('C') == x.tobytes() True >>> x.tobytes('F') - b'\\x00\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x03\\x00\\x00\\x00' + b'\\x00\\x00\\x02\\x00\\x01\\x00\\x03\\x00' - """ + """)) + + +add_newdoc('numpy.core.multiarray', 'ndarray', ('tostring', r""" + a.tostring(order='C') + + A compatibility alias for `tobytes`, with exactly the same behavior. + + Despite its name, it returns `bytes` not `str`\ s. + + .. deprecated:: 1.19.0 + """)) -add_newdoc('numpy.core.multiarray', 'ndarray', - ('tostring', tobytesdoc.format(name='tostring', - deprecated= - 'This function is a compatibility ' - 'alias for tobytes. Despite its ' - 'name it returns bytes not ' - 'strings.'))) -add_newdoc('numpy.core.multiarray', 'ndarray', - ('tobytes', tobytesdoc.format(name='tobytes', - deprecated='.. versionadded:: 1.9.0'))) add_newdoc('numpy.core.multiarray', 'ndarray', ('trace', """ @@ -4872,9 +4355,11 @@ def luf(lamdaexpr, *args, **kwargs): Returns a view of the array with axes transposed. - For a 1-D array, this has no effect. (To change between column and - row vectors, first cast the 1-D array into a matrix object.) - For a 2-D array, this is the usual matrix transpose. + For a 1-D array this has no effect, as a transposed vector is simply the + same vector. To convert a 1-D array into a 2D column vector, an additional + dimension must be added. `np.atleast2d(a).T` achieves this, as does + `a[:, np.newaxis]`. + For a 2-D array, this is a standard matrix transpose. For an n-D array, if axes are given, their order indicates how the axes are permuted (see Examples). If axes are not provided and ``a.shape = (i[0], i[1], ... i[n-2], i[n-1])``, then @@ -4899,7 +4384,9 @@ def luf(lamdaexpr, *args, **kwargs): See Also -------- + transpose : Equivalent function ndarray.T : Array property returning the array transposed. + ndarray.reshape : Give a new shape to an array without changing its data. Examples -------- @@ -4922,7 +4409,7 @@ def luf(lamdaexpr, *args, **kwargs): add_newdoc('numpy.core.multiarray', 'ndarray', ('var', """ - a.var(axis=None, dtype=None, out=None, ddof=0, keepdims=False) + a.var(axis=None, dtype=None, out=None, ddof=0, keepdims=False, *, where=True) Returns the variance of the array elements, along given axis. @@ -4937,21 +4424,26 @@ def luf(lamdaexpr, *args, **kwargs): add_newdoc('numpy.core.multiarray', 'ndarray', ('view', """ - a.view(dtype=None, type=None) + a.view([dtype][, type]) New view of array with the same data. + .. note:: + Passing None for ``dtype`` is different from omitting the parameter, + since the former invokes ``dtype(None)`` which is an alias for + ``dtype('float_')``. + Parameters ---------- dtype : data-type or ndarray sub-class, optional - Data-type descriptor of the returned view, e.g., float32 or int16. The - default, None, results in the view having the same data-type as `a`. + Data-type descriptor of the returned view, e.g., float32 or int16. + Omitting it results in the view having the same data-type as `a`. This argument can also be specified as an ndarray sub-class, which then specifies the type of the returned object (this is equivalent to setting the ``type`` parameter). type : Python type, optional - Type of the returned view, e.g., ndarray or matrix. Again, the - default None results in type preservation. + Type of the returned view, e.g., ndarray or matrix. Again, omission + of the parameter results in type preservation. Notes ----- @@ -4986,7 +4478,7 @@ def luf(lamdaexpr, *args, **kwargs): >>> y matrix([[513]], dtype=int16) >>> print(type(y)) - <class 'numpy.matrixlib.defmatrix.matrix'> + <class 'numpy.matrix'> Creating a view on a structured array so it can be used in calculations @@ -4996,19 +4488,19 @@ def luf(lamdaexpr, *args, **kwargs): array([[1, 2], [3, 4]], dtype=int8) >>> xv.mean(0) - array([ 2., 3.]) + array([2., 3.]) Making changes to the view changes the underlying array >>> xv[0,1] = 20 - >>> print(x) - [(1, 20) (3, 4)] + >>> x + array([(1, 20), (3, 4)], dtype=[('a', 'i1'), ('b', 'i1')]) Using a view to convert an array to a recarray: >>> z = x.view(np.recarray) >>> z.a - array([1], dtype=int8) + array([1, 3], dtype=int8) Views share data: @@ -5026,8 +4518,8 @@ def luf(lamdaexpr, *args, **kwargs): [4, 5]], dtype=int16) >>> y.view(dtype=[('width', np.int16), ('length', np.int16)]) Traceback (most recent call last): - File "<stdin>", line 1, in <module> - ValueError: new type not compatible with array. + ... + ValueError: To change to a dtype of a different size, the array must be C-contiguous >>> z = y.copy() >>> z.view(dtype=[('width', np.int16), ('length', np.int16)]) array([[(1, 2)], @@ -5043,7 +4535,7 @@ def luf(lamdaexpr, *args, **kwargs): add_newdoc('numpy.core.umath', 'frompyfunc', """ - frompyfunc(func, nin, nout) + frompyfunc(func, /, nin, nout, *[, identity]) Takes an arbitrary Python function and returns a NumPy ufunc. @@ -5058,6 +4550,13 @@ def luf(lamdaexpr, *args, **kwargs): The number of input arguments. nout : int The number of objects returned by `func`. + identity : object, optional + The value to use for the `~numpy.ufunc.identity` attribute of the resulting + object. If specified, this is equivalent to setting the underlying + C ``identity`` field to ``PyUFunc_IdentityValue``. + If omitted, the identity is set to ``PyUFunc_None``. Note that this is + _not_ equivalent to setting the identity to ``None``, which implies the + operation is reorderable. Returns ------- @@ -5066,7 +4565,7 @@ def luf(lamdaexpr, *args, **kwargs): See Also -------- - vectorize : evaluates pyfunc over input arrays using broadcasting rules of numpy + vectorize : Evaluates pyfunc over input arrays using broadcasting rules of numpy. Notes ----- @@ -5078,10 +4577,9 @@ def luf(lamdaexpr, *args, **kwargs): >>> oct_array = np.frompyfunc(oct, 1, 1) >>> oct_array(np.array((10, 30, 100))) - array([012, 036, 0144], dtype=object) + array(['0o12', '0o36', '0o144'], dtype=object) >>> np.array((oct(10), oct(30), oct(100))) # for comparison - array(['012', '036', '0144'], - dtype='|S4') + array(['0o12', '0o36', '0o144'], dtype='<U5') """) @@ -5128,7 +4626,7 @@ def luf(lamdaexpr, *args, **kwargs): Examples -------- >>> np.geterrobj() # first get the defaults - [10000, 0, None] + [8192, 521, None] >>> def err_handler(type, flag): ... print("Floating point error (%s), with flag %s" % (type, flag)) @@ -5137,13 +4635,13 @@ def luf(lamdaexpr, *args, **kwargs): >>> old_err = np.seterr(divide='raise') >>> old_handler = np.seterrcall(err_handler) >>> np.geterrobj() - [20000, 2, <function err_handler at 0x91dcaac>] + [8192, 521, <function err_handler at 0x91dcaac>] >>> old_err = np.seterr(all='ignore') >>> np.base_repr(np.geterrobj()[1], 8) '0' >>> old_err = np.seterr(divide='warn', over='log', under='call', - invalid='print') + ... invalid='print') >>> np.base_repr(np.geterrobj()[1], 8) '4351' @@ -5151,7 +4649,7 @@ def luf(lamdaexpr, *args, **kwargs): add_newdoc('numpy.core.umath', 'seterrobj', """ - seterrobj(errobj) + seterrobj(errobj, /) Set the object that defines floating-point error handling. @@ -5192,7 +4690,7 @@ def luf(lamdaexpr, *args, **kwargs): -------- >>> old_errobj = np.geterrobj() # first get the defaults >>> old_errobj - [10000, 0, None] + [8192, 521, None] >>> def err_handler(type, flag): ... print("Floating point error (%s), with flag %s" % (type, flag)) @@ -5215,422 +4713,95 @@ def luf(lamdaexpr, *args, **kwargs): # ############################################################################## -add_newdoc('numpy.core.multiarray', 'digitize', +add_newdoc('numpy.core.multiarray', 'add_docstring', """ - digitize(x, bins, right=False) + add_docstring(obj, docstring) - Return the indices of the bins to which each value in input array belongs. + Add a docstring to a built-in obj if possible. + If the obj already has a docstring raise a RuntimeError + If this routine does not know how to add a docstring to the object + raise a TypeError + """) - ========= ============= ============================ - `right` order of bins returned index `i` satisfies - ========= ============= ============================ - ``False`` increasing ``bins[i-1] <= x < bins[i]`` - ``True`` increasing ``bins[i-1] < x <= bins[i]`` - ``False`` decreasing ``bins[i-1] > x >= bins[i]`` - ``True`` decreasing ``bins[i-1] >= x > bins[i]`` - ========= ============= ============================ +add_newdoc('numpy.core.umath', '_add_newdoc_ufunc', + """ + add_ufunc_docstring(ufunc, new_docstring) - If values in `x` are beyond the bounds of `bins`, 0 or ``len(bins)`` is - returned as appropriate. + Replace the docstring for a ufunc with new_docstring. + This method will only work if the current docstring for + the ufunc is NULL. (At the C level, i.e. when ufunc->doc is NULL.) Parameters ---------- - x : array_like - Input array to be binned. Prior to NumPy 1.10.0, this array had to - be 1-dimensional, but can now have any shape. - bins : array_like - Array of bins. It has to be 1-dimensional and monotonic. - right : bool, optional - Indicating whether the intervals include the right or the left bin - edge. Default behavior is (right==False) indicating that the interval - does not include the right edge. The left bin end is open in this - case, i.e., bins[i-1] <= x < bins[i] is the default behavior for - monotonically increasing bins. - - Returns - ------- - indices : ndarray of ints - Output array of indices, of same shape as `x`. - - Raises - ------ - ValueError - If `bins` is not monotonic. - TypeError - If the type of the input is complex. - - See Also - -------- - bincount, histogram, unique, searchsorted + ufunc : numpy.ufunc + A ufunc whose current doc is NULL. + new_docstring : string + The new docstring for the ufunc. Notes ----- - If values in `x` are such that they fall outside the bin range, - attempting to index `bins` with the indices that `digitize` returns - will result in an IndexError. + This method allocates memory for new_docstring on + the heap. Technically this creates a mempory leak, since this + memory will not be reclaimed until the end of the program + even if the ufunc itself is removed. However this will only + be a problem if the user is repeatedly creating ufuncs with + no documentation, adding documentation via add_newdoc_ufunc, + and then throwing away the ufunc. + """) - .. versionadded:: 1.10.0 +add_newdoc('numpy.core.multiarray', 'get_handler_name', + """ + get_handler_name(a: ndarray) -> str,None - `np.digitize` is implemented in terms of `np.searchsorted`. This means - that a binary search is used to bin the values, which scales much better - for larger number of bins than the previous linear search. It also removes - the requirement for the input array to be 1-dimensional. + Return the name of the memory handler used by `a`. If not provided, return + the name of the memory handler that will be used to allocate data for the + next `ndarray` in this context. May return None if `a` does not own its + memory, in which case you can traverse ``a.base`` for a memory handler. + """) - For monotonically _increasing_ `bins`, the following are equivalent:: +add_newdoc('numpy.core.multiarray', 'get_handler_version', + """ + get_handler_version(a: ndarray) -> int,None - np.digitize(x, bins, right=True) - np.searchsorted(bins, x, side='left') + Return the version of the memory handler used by `a`. If not provided, + return the version of the memory handler that will be used to allocate data + for the next `ndarray` in this context. May return None if `a` does not own + its memory, in which case you can traverse ``a.base`` for a memory handler. + """) - Note that as the order of the arguments are reversed, the side must be too. - The `searchsorted` call is marginally faster, as it does not do any - monotonicity checks. Perhaps more importantly, it supports all dtypes. +add_newdoc('numpy.core.multiarray', '_set_madvise_hugepage', + """ + _set_madvise_hugepage(enabled: bool) -> bool - Examples - -------- - >>> x = np.array([0.2, 6.4, 3.0, 1.6]) - >>> bins = np.array([0.0, 1.0, 2.5, 4.0, 10.0]) - >>> inds = np.digitize(x, bins) - >>> inds - array([1, 4, 3, 2]) - >>> for n in range(x.size): - ... print(bins[inds[n]-1], "<=", x[n], "<", bins[inds[n]]) - ... - 0.0 <= 0.2 < 1.0 - 4.0 <= 6.4 < 10.0 - 2.5 <= 3.0 < 4.0 - 1.0 <= 1.6 < 2.5 - - >>> x = np.array([1.2, 10.0, 12.4, 15.5, 20.]) - >>> bins = np.array([0, 5, 10, 15, 20]) - >>> np.digitize(x,bins,right=True) - array([1, 2, 3, 4, 4]) - >>> np.digitize(x,bins,right=False) - array([1, 3, 3, 4, 5]) + Set or unset use of ``madvise (2)`` MADV_HUGEPAGE support when + allocating the array data. Returns the previously set value. + See `global_state` for more information. """) -add_newdoc('numpy.core.multiarray', 'bincount', +add_newdoc('numpy.core._multiarray_tests', 'format_float_OSprintf_g', """ - bincount(x, weights=None, minlength=0) + format_float_OSprintf_g(val, precision) + + Print a floating point scalar using the system's printf function, + equivalent to: - Count number of occurrences of each value in array of non-negative ints. + printf("%.*g", precision, val); - The number of bins (of size 1) is one larger than the largest value in - `x`. If `minlength` is specified, there will be at least this number - of bins in the output array (though it will be longer if necessary, - depending on the contents of `x`). - Each bin gives the number of occurrences of its index value in `x`. - If `weights` is specified the input array is weighted by it, i.e. if a - value ``n`` is found at position ``i``, ``out[n] += weight[i]`` instead - of ``out[n] += 1``. + for half/float/double, or replacing 'g' by 'Lg' for longdouble. This + method is designed to help cross-validate the format_float_* methods. Parameters ---------- - x : array_like, 1 dimension, nonnegative ints - Input array. - weights : array_like, optional - Weights, array of the same shape as `x`. - minlength : int, optional - A minimum number of bins for the output array. + val : python float or numpy floating scalar + Value to format. - .. versionadded:: 1.6.0 + precision : non-negative integer, optional + Precision given to printf. Returns ------- - out : ndarray of ints - The result of binning the input array. - The length of `out` is equal to ``np.amax(x)+1``. - - Raises - ------ - ValueError - If the input is not 1-dimensional, or contains elements with negative - values, or if `minlength` is negative. - TypeError - If the type of the input is float or complex. - - See Also - -------- - histogram, digitize, unique - - Examples - -------- - >>> np.bincount(np.arange(5)) - array([1, 1, 1, 1, 1]) - >>> np.bincount(np.array([0, 1, 1, 3, 2, 1, 7])) - array([1, 3, 1, 1, 0, 0, 0, 1]) - - >>> x = np.array([0, 1, 1, 3, 2, 1, 7, 23]) - >>> np.bincount(x).size == np.amax(x)+1 - True - - The input array needs to be of integer dtype, otherwise a - TypeError is raised: - - >>> np.bincount(np.arange(5, dtype=float)) - Traceback (most recent call last): - File "<stdin>", line 1, in <module> - TypeError: array cannot be safely cast to required type - - A possible use of ``bincount`` is to perform sums over - variable-size chunks of an array, using the ``weights`` keyword. - - >>> w = np.array([0.3, 0.5, 0.2, 0.7, 1., -0.6]) # weights - >>> x = np.array([0, 1, 1, 2, 2, 2]) - >>> np.bincount(x, weights=w) - array([ 0.3, 0.7, 1.1]) - - """) - -add_newdoc('numpy.core.multiarray', 'ravel_multi_index', - """ - ravel_multi_index(multi_index, dims, mode='raise', order='C') - - Converts a tuple of index arrays into an array of flat - indices, applying boundary modes to the multi-index. - - Parameters - ---------- - multi_index : tuple of array_like - A tuple of integer arrays, one array for each dimension. - dims : tuple of ints - The shape of array into which the indices from ``multi_index`` apply. - mode : {'raise', 'wrap', 'clip'}, optional - Specifies how out-of-bounds indices are handled. Can specify - either one mode or a tuple of modes, one mode per index. - - * 'raise' -- raise an error (default) - * 'wrap' -- wrap around - * 'clip' -- clip to the range - - In 'clip' mode, a negative index which would normally - wrap will clip to 0 instead. - order : {'C', 'F'}, optional - Determines whether the multi-index should be viewed as - indexing in row-major (C-style) or column-major - (Fortran-style) order. - - Returns - ------- - raveled_indices : ndarray - An array of indices into the flattened version of an array - of dimensions ``dims``. - - See Also - -------- - unravel_index - - Notes - ----- - .. versionadded:: 1.6.0 - - Examples - -------- - >>> arr = np.array([[3,6,6],[4,5,1]]) - >>> np.ravel_multi_index(arr, (7,6)) - array([22, 41, 37]) - >>> np.ravel_multi_index(arr, (7,6), order='F') - array([31, 41, 13]) - >>> np.ravel_multi_index(arr, (4,6), mode='clip') - array([22, 23, 19]) - >>> np.ravel_multi_index(arr, (4,4), mode=('clip','wrap')) - array([12, 13, 13]) - - >>> np.ravel_multi_index((3,1,4,1), (6,7,8,9)) - 1621 - """) - -add_newdoc('numpy.core.multiarray', 'unravel_index', - """ - unravel_index(indices, dims, order='C') - - Converts a flat index or array of flat indices into a tuple - of coordinate arrays. - - Parameters - ---------- - indices : array_like - An integer array whose elements are indices into the flattened - version of an array of dimensions ``dims``. Before version 1.6.0, - this function accepted just one index value. - dims : tuple of ints - The shape of the array to use for unraveling ``indices``. - order : {'C', 'F'}, optional - Determines whether the indices should be viewed as indexing in - row-major (C-style) or column-major (Fortran-style) order. - - .. versionadded:: 1.6.0 - - Returns - ------- - unraveled_coords : tuple of ndarray - Each array in the tuple has the same shape as the ``indices`` - array. - - See Also - -------- - ravel_multi_index - - Examples - -------- - >>> np.unravel_index([22, 41, 37], (7,6)) - (array([3, 6, 6]), array([4, 5, 1])) - >>> np.unravel_index([31, 41, 13], (7,6), order='F') - (array([3, 6, 6]), array([4, 5, 1])) - - >>> np.unravel_index(1621, (6,7,8,9)) - (3, 1, 4, 1) - - """) - -add_newdoc('numpy.core.multiarray', 'add_docstring', - """ - add_docstring(obj, docstring) - - Add a docstring to a built-in obj if possible. - If the obj already has a docstring raise a RuntimeError - If this routine does not know how to add a docstring to the object - raise a TypeError - """) - -add_newdoc('numpy.core.umath', '_add_newdoc_ufunc', - """ - add_ufunc_docstring(ufunc, new_docstring) - - Replace the docstring for a ufunc with new_docstring. - This method will only work if the current docstring for - the ufunc is NULL. (At the C level, i.e. when ufunc->doc is NULL.) - - Parameters - ---------- - ufunc : numpy.ufunc - A ufunc whose current doc is NULL. - new_docstring : string - The new docstring for the ufunc. - - Notes - ----- - This method allocates memory for new_docstring on - the heap. Technically this creates a mempory leak, since this - memory will not be reclaimed until the end of the program - even if the ufunc itself is removed. However this will only - be a problem if the user is repeatedly creating ufuncs with - no documentation, adding documentation via add_newdoc_ufunc, - and then throwing away the ufunc. - """) - -add_newdoc('numpy.core.multiarray', 'packbits', - """ - packbits(myarray, axis=None) - - Packs the elements of a binary-valued array into bits in a uint8 array. - - The result is padded to full bytes by inserting zero bits at the end. - - Parameters - ---------- - myarray : array_like - An array of integers or booleans whose elements should be packed to - bits. - axis : int, optional - The dimension over which bit-packing is done. - ``None`` implies packing the flattened array. - - Returns - ------- - packed : ndarray - Array of type uint8 whose elements represent bits corresponding to the - logical (0 or nonzero) value of the input elements. The shape of - `packed` has the same number of dimensions as the input (unless `axis` - is None, in which case the output is 1-D). - - See Also - -------- - unpackbits: Unpacks elements of a uint8 array into a binary-valued output - array. - - Examples - -------- - >>> a = np.array([[[1,0,1], - ... [0,1,0]], - ... [[1,1,0], - ... [0,0,1]]]) - >>> b = np.packbits(a, axis=-1) - >>> b - array([[[160],[64]],[[192],[32]]], dtype=uint8) - - Note that in binary 160 = 1010 0000, 64 = 0100 0000, 192 = 1100 0000, - and 32 = 0010 0000. - - """) - -add_newdoc('numpy.core.multiarray', 'unpackbits', - """ - unpackbits(myarray, axis=None) - - Unpacks elements of a uint8 array into a binary-valued output array. - - Each element of `myarray` represents a bit-field that should be unpacked - into a binary-valued output array. The shape of the output array is either - 1-D (if `axis` is None) or the same shape as the input array with unpacking - done along the axis specified. - - Parameters - ---------- - myarray : ndarray, uint8 type - Input array. - axis : int, optional - The dimension over which bit-unpacking is done. - ``None`` implies unpacking the flattened array. - - Returns - ------- - unpacked : ndarray, uint8 type - The elements are binary-valued (0 or 1). - - See Also - -------- - packbits : Packs the elements of a binary-valued array into bits in a uint8 - array. - - Examples - -------- - >>> a = np.array([[2], [7], [23]], dtype=np.uint8) - >>> a - array([[ 2], - [ 7], - [23]], dtype=uint8) - >>> b = np.unpackbits(a, axis=1) - >>> b - array([[0, 0, 0, 0, 0, 0, 1, 0], - [0, 0, 0, 0, 0, 1, 1, 1], - [0, 0, 0, 1, 0, 1, 1, 1]], dtype=uint8) - - """) - -add_newdoc('numpy.core._multiarray_tests', 'format_float_OSprintf_g', - """ - format_float_OSprintf_g(val, precision) - - Print a floating point scalar using the system's printf function, - equivalent to: - - printf("%.*g", precision, val); - - for half/float/double, or replacing 'g' by 'Lg' for longdouble. This - method is designed to help cross-validate the format_float_* methods. - - Parameters - ---------- - val : python float or numpy floating scalar - Value to format. - - precision : non-negative integer, optional - Precision given to printf. - - Returns - ------- - rep : string - The string representation of the floating point value + rep : string + The string representation of the floating point value See Also -------- @@ -5664,10 +4835,8 @@ def luf(lamdaexpr, *args, **kwargs): A detailed explanation of ufuncs can be found in the docs for :ref:`ufuncs`. - Calling ufuncs: - =============== + **Calling ufuncs:** ``op(*x[, out], where=True, **kwargs)`` - op(*x[, out], where=True, **kwargs) Apply `op` to the arguments `*x` elementwise, broadcasting the arguments. The broadcasting rules are: @@ -5683,13 +4852,15 @@ def luf(lamdaexpr, *args, **kwargs): Alternate array object(s) in which to put the result; if provided, it must have a shape that the inputs broadcast to. A tuple of arrays (possible only as a keyword argument) must have length equal to the - number of outputs; use `None` for uninitialized outputs to be + number of outputs; use None for uninitialized outputs to be allocated by the ufunc. where : array_like, optional - Values of True indicate to calculate the ufunc at that position, values - of False indicate to leave the value in the output alone. Note that if - an uninitialized return array is created via the default ``out=None``, - then the elements where the values are False will remain uninitialized. + This condition is broadcast over the input. At locations where the + condition is True, the `out` array will be set to the ufunc result. + Elsewhere, the `out` array will retain its original value. + Note that if an uninitialized `out` array is created via the default + ``out=None``, locations within it where the condition is False will + remain uninitialized. **kwargs For other keyword-only arguments, see the :ref:`ufunc docs <ufuncs.kwargs>`. @@ -5875,7 +5046,7 @@ def luf(lamdaexpr, *args, **kwargs): ----- Generalized ufuncs are used internally in many linalg functions, and in the testing suite; the examples below are taken from these. - For ufuncs that operate on scalars, the signature is `None`, which is + For ufuncs that operate on scalars, the signature is None, which is equivalent to '()' for every argument. Examples @@ -5896,14 +5067,14 @@ def luf(lamdaexpr, *args, **kwargs): add_newdoc('numpy.core', 'ufunc', ('reduce', """ - reduce(a, axis=0, dtype=None, out=None, keepdims=False, initial) + reduce(array, axis=0, dtype=None, out=None, keepdims=False, initial=<no value>, where=True) - Reduces `a`'s dimension by one, by applying ufunc along one axis. + Reduces `array`'s dimension by one, by applying ufunc along one axis. - Let :math:`a.shape = (N_0, ..., N_i, ..., N_{M-1})`. Then - :math:`ufunc.reduce(a, axis=i)[k_0, ..,k_{i-1}, k_{i+1}, .., k_{M-1}]` = + Let :math:`array.shape = (N_0, ..., N_i, ..., N_{M-1})`. Then + :math:`ufunc.reduce(array, axis=i)[k_0, ..,k_{i-1}, k_{i+1}, .., k_{M-1}]` = the result of iterating `j` over :math:`range(N_i)`, cumulatively applying - ufunc to each :math:`a[k_0, ..,k_{i-1}, j, k_{i+1}, .., k_{M-1}]`. + ufunc to each :math:`array[k_0, ..,k_{i-1}, j, k_{i+1}, .., k_{M-1}]`. For a one-dimensional array, reduce produces results equivalent to: :: @@ -5916,7 +5087,7 @@ def luf(lamdaexpr, *args, **kwargs): Parameters ---------- - a : array_like + array : array_like The array to act on. axis : None or int or tuple of ints, optional Axis or axes along which a reduction is performed. @@ -5926,7 +5097,7 @@ def luf(lamdaexpr, *args, **kwargs): .. versionadded:: 1.7.0 - If this is `None`, a reduction is performed over all the axes. + If this is None, a reduction is performed over all the axes. If this is a tuple of ints, a reduction is performed on multiple axes, instead of a single axis or all the axes as before. @@ -5939,9 +5110,9 @@ def luf(lamdaexpr, *args, **kwargs): to the data-type of the output array if this is provided, or the data-type of the input array if no output array is provided. out : ndarray, None, or tuple of ndarray and None, optional - A location into which the result is stored. If not provided or `None`, + A location into which the result is stored. If not provided or None, a freshly-allocated array is returned. For consistency with - :ref:`ufunc.__call__`, if given as a keyword, this may be wrapped in a + ``ufunc.__call__``, if given as a keyword, this may be wrapped in a 1-element tuple. .. versionchanged:: 1.13.0 @@ -5949,7 +5120,7 @@ def luf(lamdaexpr, *args, **kwargs): keepdims : bool, optional If this is set to True, the axes which are reduced are left in the result as dimensions with size one. With this option, - the result will broadcast correctly against the original `arr`. + the result will broadcast correctly against the original `array`. .. versionadded:: 1.7.0 initial : scalar, optional @@ -5958,9 +5129,17 @@ def luf(lamdaexpr, *args, **kwargs): to None - otherwise it defaults to ufunc.identity. If ``None`` is given, the first element of the reduction is used, and an error is thrown if the reduction is empty. - + .. versionadded:: 1.15.0 + where : array_like of bool, optional + A boolean array which is broadcasted to match the dimensions + of `array`, and selects elements to include in the reduction. Note + that for ufuncs like ``minimum`` that do not have an identity + defined, one has to pass in also ``initial``. + + .. versionadded:: 1.17.0 + Returns ------- r : ndarray @@ -5991,20 +5170,25 @@ def luf(lamdaexpr, *args, **kwargs): >>> np.add.reduce(X, 2) array([[ 1, 5], [ 9, 13]]) - - You can use the ``initial`` keyword argument to initialize the reduction with a - different value. - + + You can use the ``initial`` keyword argument to initialize the reduction + with a different value, and ``where`` to select specific elements to include: + >>> np.add.reduce([10], initial=5) 15 - >>> np.add.reduce(np.ones((2, 2, 2)), axis=(0, 2), initializer=10) + >>> np.add.reduce(np.ones((2, 2, 2)), axis=(0, 2), initial=10) array([14., 14.]) - + >>> a = np.array([10., np.nan, 10]) + >>> np.add.reduce(a, where=~np.isnan(a)) + 20.0 + Allows reductions of empty arrays where they would normally fail, i.e. for ufuncs without an identity. - + >>> np.minimum.reduce([], initial=np.inf) inf + >>> np.minimum.reduce([[1., 2.], [3., 4.]], initial=10., where=[True, False]) + array([ 1., 10.]) >>> np.minimum.reduce([]) Traceback (most recent call last): ... @@ -6043,9 +5227,9 @@ def luf(lamdaexpr, *args, **kwargs): to the data-type of the output array if such is provided, or the the data-type of the input array if no output array is provided. out : ndarray, None, or tuple of ndarray and None, optional - A location into which the result is stored. If not provided or `None`, + A location into which the result is stored. If not provided or None, a freshly-allocated array is returned. For consistency with - :ref:`ufunc.__call__`, if given as a keyword, this may be wrapped in a + ``ufunc.__call__``, if given as a keyword, this may be wrapped in a 1-element tuple. .. versionchanged:: 1.13.0 @@ -6070,50 +5254,50 @@ def luf(lamdaexpr, *args, **kwargs): >>> I = np.eye(2) >>> I - array([[ 1., 0.], - [ 0., 1.]]) + array([[1., 0.], + [0., 1.]]) Accumulate along axis 0 (rows), down columns: >>> np.add.accumulate(I, 0) - array([[ 1., 0.], - [ 1., 1.]]) + array([[1., 0.], + [1., 1.]]) >>> np.add.accumulate(I) # no axis specified = axis zero - array([[ 1., 0.], - [ 1., 1.]]) + array([[1., 0.], + [1., 1.]]) Accumulate along axis 1 (columns), through rows: >>> np.add.accumulate(I, 1) - array([[ 1., 1.], - [ 0., 1.]]) + array([[1., 1.], + [0., 1.]]) """)) add_newdoc('numpy.core', 'ufunc', ('reduceat', """ - reduceat(a, indices, axis=0, dtype=None, out=None) + reduceat(array, indices, axis=0, dtype=None, out=None) Performs a (local) reduce with specified slices over a single axis. For i in ``range(len(indices))``, `reduceat` computes - ``ufunc.reduce(a[indices[i]:indices[i+1]])``, which becomes the i-th + ``ufunc.reduce(array[indices[i]:indices[i+1]])``, which becomes the i-th generalized "row" parallel to `axis` in the final result (i.e., in a 2-D array, for example, if `axis = 0`, it becomes the i-th row, but if `axis = 1`, it becomes the i-th column). There are three exceptions to this: * when ``i = len(indices) - 1`` (so for the last index), - ``indices[i+1] = a.shape[axis]``. + ``indices[i+1] = array.shape[axis]``. * if ``indices[i] >= indices[i + 1]``, the i-th generalized "row" is - simply ``a[indices[i]]``. - * if ``indices[i] >= len(a)`` or ``indices[i] < 0``, an error is raised. + simply ``array[indices[i]]``. + * if ``indices[i] >= len(array)`` or ``indices[i] < 0``, an error is raised. The shape of the output depends on the size of `indices`, and may be - larger than `a` (this happens if ``len(indices) > a.shape[axis]``). + larger than `array` (this happens if ``len(indices) > array.shape[axis]``). Parameters ---------- - a : array_like + array : array_like The array to act on. indices : array_like Paired indices, comma separated (not colon), specifying slices to @@ -6125,9 +5309,9 @@ def luf(lamdaexpr, *args, **kwargs): to the data type of the output array if this is provided, or the data type of the input array if no output array is provided. out : ndarray, None, or tuple of ndarray and None, optional - A location into which the result is stored. If not provided or `None`, + A location into which the result is stored. If not provided or None, a freshly-allocated array is returned. For consistency with - :ref:`ufunc.__call__`, if given as a keyword, this may be wrapped in a + ``ufunc.__call__``, if given as a keyword, this may be wrapped in a 1-element tuple. .. versionchanged:: 1.13.0 @@ -6143,14 +5327,15 @@ def luf(lamdaexpr, *args, **kwargs): ----- A descriptive example: - If `a` is 1-D, the function `ufunc.accumulate(a)` is the same as - ``ufunc.reduceat(a, indices)[::2]`` where `indices` is + If `array` is 1-D, the function `ufunc.accumulate(array)` is the same as + ``ufunc.reduceat(array, indices)[::2]`` where `indices` is ``range(len(array) - 1)`` with a zero placed in every other element: - ``indices = zeros(2 * len(a) - 1)``, ``indices[1::2] = range(1, len(a))``. + ``indices = zeros(2 * len(array) - 1)``, + ``indices[1::2] = range(1, len(array))``. - Don't be fooled by this attribute's name: `reduceat(a)` is not - necessarily smaller than `a`. + Don't be fooled by this attribute's name: `reduceat(array)` is not + necessarily smaller than `array`. Examples -------- @@ -6163,10 +5348,10 @@ def luf(lamdaexpr, *args, **kwargs): >>> x = np.linspace(0, 15, 16).reshape(4,4) >>> x - array([[ 0., 1., 2., 3.], - [ 4., 5., 6., 7.], - [ 8., 9., 10., 11.], - [ 12., 13., 14., 15.]]) + array([[ 0., 1., 2., 3.], + [ 4., 5., 6., 7.], + [ 8., 9., 10., 11.], + [12., 13., 14., 15.]]) :: @@ -6178,11 +5363,11 @@ def luf(lamdaexpr, *args, **kwargs): # [row1 + row2 + row3 + row4] >>> np.add.reduceat(x, [0, 3, 1, 2, 0]) - array([[ 12., 15., 18., 21.], - [ 12., 13., 14., 15.], - [ 4., 5., 6., 7.], - [ 8., 9., 10., 11.], - [ 24., 28., 32., 36.]]) + array([[12., 15., 18., 21.], + [12., 13., 14., 15.], + [ 4., 5., 6., 7.], + [ 8., 9., 10., 11.], + [24., 28., 32., 36.]]) :: @@ -6190,16 +5375,16 @@ def luf(lamdaexpr, *args, **kwargs): # [col1 * col2 * col3, col4] >>> np.multiply.reduceat(x, [0, 3], 1) - array([[ 0., 3.], - [ 120., 7.], - [ 720., 11.], - [ 2184., 15.]]) + array([[ 0., 3.], + [ 120., 7.], + [ 720., 11.], + [2184., 15.]]) """)) add_newdoc('numpy.core', 'ufunc', ('outer', - """ - outer(A, B, **kwargs) + r""" + outer(A, B, /, **kwargs) Apply the ufunc `op` to all pairs (a, b) with a in `A` and b in `B`. @@ -6214,7 +5399,7 @@ def luf(lamdaexpr, *args, **kwargs): r = empty(len(A),len(B)) for i in range(len(A)): for j in range(len(B)): - r[i,j] = op(A[i], B[j]) # op = ufunc in question + r[i,j] = op(A[i], B[j]) # op = ufunc in question Parameters ---------- @@ -6224,6 +5409,7 @@ def luf(lamdaexpr, *args, **kwargs): Second array kwargs : any Arguments to pass on to the ufunc. Typically `dtype` or `out`. + See `ufunc` for a comprehensive overview of all available arguments. Returns ------- @@ -6232,7 +5418,13 @@ def luf(lamdaexpr, *args, **kwargs): See Also -------- - numpy.outer + numpy.outer : A less powerful version of ``np.multiply.outer`` + that `ravel`\ s all inputs to 1D. This exists + primarily for compatibility with old code. + + tensordot : ``np.tensordot(a, b, axes=((), ()))`` and + ``np.multiply.outer(a, b)`` behave same for all + dimensions of a and b. Examples -------- @@ -6263,7 +5455,7 @@ def luf(lamdaexpr, *args, **kwargs): add_newdoc('numpy.core', 'ufunc', ('at', """ - at(a, indices, b=None) + at(a, indices, b=None, /) Performs unbuffered in place operation on operand 'a' for elements specified by 'indices'. For addition ufunc, this method is equivalent to @@ -6292,14 +5484,14 @@ def luf(lamdaexpr, *args, **kwargs): >>> a = np.array([1, 2, 3, 4]) >>> np.negative.at(a, [0, 1]) - >>> print(a) - array([-1, -2, 3, 4]) + >>> a + array([-1, -2, 3, 4]) Increment items 0 and 1, and increment item 2 twice: >>> a = np.array([1, 2, 3, 4]) >>> np.add.at(a, [0, 1, 2, 2], 1) - >>> print(a) + >>> a array([2, 3, 5, 4]) Add items 0 and 1 in first array to second array, @@ -6308,7 +5500,7 @@ def luf(lamdaexpr, *args, **kwargs): >>> a = np.array([1, 2, 3, 4]) >>> b = np.array([1, 2]) >>> np.add.at(a, [0, 1], b) - >>> print(a) + >>> a array([2, 4, 3, 4]) """)) @@ -6327,7 +5519,7 @@ def luf(lamdaexpr, *args, **kwargs): add_newdoc('numpy.core.multiarray', 'dtype', """ - dtype(obj, align=False, copy=False) + dtype(dtype, align=False, copy=False) Create a data type object. @@ -6337,7 +5529,7 @@ def luf(lamdaexpr, *args, **kwargs): Parameters ---------- - obj + dtype Object to be converted to a data type object. align : bool, optional Add padding to the fields to match what a C compiler would output @@ -6373,13 +5565,13 @@ def luf(lamdaexpr, *args, **kwargs): Structured type, two fields: the first field contains an unsigned int, the second an int32: - >>> np.dtype([('f1', np.uint), ('f2', np.int32)]) - dtype([('f1', '<u4'), ('f2', '<i4')]) + >>> np.dtype([('f1', np.uint64), ('f2', np.int32)]) + dtype([('f1', '<u8'), ('f2', '<i4')]) Using array-protocol type strings: >>> np.dtype([('a','f8'),('b','S10')]) - dtype([('a', '<f8'), ('b', '|S10')]) + dtype([('a', '<f8'), ('b', 'S10')]) Using comma-separated field formats. The shape is (2,3): @@ -6389,24 +5581,24 @@ def luf(lamdaexpr, *args, **kwargs): Using tuples. ``int`` is a fixed type, 3 the field's shape. ``void`` is a flexible type, here of size 10: - >>> np.dtype([('hello',(int,3)),('world',np.void,10)]) - dtype([('hello', '<i4', 3), ('world', '|V10')]) + >>> np.dtype([('hello',(np.int64,3)),('world',np.void,10)]) + dtype([('hello', '<i8', (3,)), ('world', 'V10')]) Subdivide ``int16`` into 2 ``int8``'s, called x and y. 0 and 1 are the offsets in bytes: >>> np.dtype((np.int16, {'x':(np.int8,0), 'y':(np.int8,1)})) - dtype(('<i2', [('x', '|i1'), ('y', '|i1')])) + dtype((numpy.int16, [('x', 'i1'), ('y', 'i1')])) Using dictionaries. Two fields named 'gender' and 'age': >>> np.dtype({'names':['gender','age'], 'formats':['S1',np.uint8]}) - dtype([('gender', '|S1'), ('age', '|u1')]) + dtype([('gender', 'S1'), ('age', 'u1')]) Offsets in bytes, here 0 and 25: >>> np.dtype({'surname':('S25',0),'age':(np.uint8,25)}) - dtype([('surname', '|S25'), ('age', '|u1')]) + dtype([('surname', 'S25'), ('age', 'u1')]) """) @@ -6422,6 +5614,17 @@ def luf(lamdaexpr, *args, **kwargs): More information is available in the C-API section of the manual. + Examples + -------- + + >>> x = np.dtype('i4') + >>> x.alignment + 4 + + >>> x = np.dtype(float) + >>> x.alignment + 8 + """)) add_newdoc('numpy.core.multiarray', 'dtype', ('byteorder', @@ -6468,17 +5671,39 @@ def luf(lamdaexpr, *args, **kwargs): """)) add_newdoc('numpy.core.multiarray', 'dtype', ('char', - """A unique character code for each of the 21 different built-in types.""")) + """A unique character code for each of the 21 different built-in types. + + Examples + -------- + + >>> x = np.dtype(float) + >>> x.char + 'd' + + """)) add_newdoc('numpy.core.multiarray', 'dtype', ('descr', """ - PEP3118 interface description of the data-type. + `__array_interface__` description of the data-type. The format is that required by the 'descr' key in the - PEP3118 `__array_interface__` attribute. + `__array_interface__` attribute. + + Warning: This attribute exists specifically for `__array_interface__`, + and passing it directly to `np.dtype` will not accurately reconstruct + some dtypes (e.g., scalar and subarray dtypes). + + Examples + -------- + + >>> x = np.dtype(float) + >>> x.descr + [('', '<f8')] + + >>> dt = np.dtype([('name', np.str_, 16), ('grades', np.float64, (2,))]) + >>> dt.descr + [('name', '<U16'), ('grades', '<f8', (2,))] - Warning: This attribute exists specifically for PEP3118 compliance, and - is not a datatype description compatible with `np.dtype`. """)) add_newdoc('numpy.core.multiarray', 'dtype', ('fields', @@ -6490,6 +5715,7 @@ def luf(lamdaexpr, *args, **kwargs): (dtype, offset[, title]) + Offset is limited to C int, which is signed and usually 32 bits. If present, the optional title can be any object (if it is a string or unicode then it will also be a key in the fields dictionary, otherwise it's meta-data). Notice also that the first two elements @@ -6518,6 +5744,18 @@ def luf(lamdaexpr, *args, **kwargs): of these flags is in C-API documentation; they are largely useful for user-defined data-types. + The following example demonstrates that operations on this particular + dtype requires Python C-API. + + Examples + -------- + + >>> x = np.dtype([('a', np.int32, 8), ('b', np.float64, 6)]) + >>> x.flags + 16 + >>> np.core.multiarray.NEEDS_PYAPI + 16 + """)) add_newdoc('numpy.core.multiarray', 'dtype', ('hasobject', @@ -6575,6 +5813,7 @@ def luf(lamdaexpr, *args, **kwargs): field alignment. This flag is sticky, so when combining multiple structs together, it is preserved and produces new dtypes which are also aligned. + """)) add_newdoc('numpy.core.multiarray', 'dtype', ('itemsize', @@ -6584,6 +5823,19 @@ def luf(lamdaexpr, *args, **kwargs): For 18 of the 21 types this number is fixed by the data-type. For the flexible data-types, this number can be anything. + Examples + -------- + + >>> arr = np.array([[1, 2], [3, 4]]) + >>> arr.dtype + dtype('int64') + >>> arr.itemsize + 8 + + >>> dt = np.dtype([('name', np.str_, 16), ('grades', np.float64, (2,))]) + >>> dt.itemsize + 80 + """)) add_newdoc('numpy.core.multiarray', 'dtype', ('kind', @@ -6604,6 +5856,58 @@ def luf(lamdaexpr, *args, **kwargs): V void = ====================== + Examples + -------- + + >>> dt = np.dtype('i4') + >>> dt.kind + 'i' + >>> dt = np.dtype('f8') + >>> dt.kind + 'f' + >>> dt = np.dtype([('field1', 'f8')]) + >>> dt.kind + 'V' + + """)) + +add_newdoc('numpy.core.multiarray', 'dtype', ('metadata', + """ + Either ``None`` or a readonly dictionary of metadata (mappingproxy). + + The metadata field can be set using any dictionary at data-type + creation. NumPy currently has no uniform approach to propagating + metadata; although some array operations preserve it, there is no + guarantee that others will. + + .. warning:: + + Although used in certain projects, this feature was long undocumented + and is not well supported. Some aspects of metadata propagation + are expected to change in the future. + + Examples + -------- + + >>> dt = np.dtype(float, metadata={"key": "value"}) + >>> dt.metadata["key"] + 'value' + >>> arr = np.array([1, 2, 3], dtype=dt) + >>> arr.dtype.metadata + mappingproxy({'key': 'value'}) + + Adding arrays with identical datatypes currently preserves the metadata: + + >>> (arr + arr).dtype.metadata + mappingproxy({'key': 'value'}) + + But if the arrays have different dtype metadata, the metadata may be + dropped: + + >>> dt2 = np.dtype(float, metadata={"key2": "value2"}) + >>> arr2 = np.array([3, 2, 1], dtype=dt2) + >>> (arr + arr2).dtype.metadata is None + True # The metadata field is cleared so None is returned """)) add_newdoc('numpy.core.multiarray', 'dtype', ('name', @@ -6612,6 +5916,16 @@ def luf(lamdaexpr, *args, **kwargs): Un-sized flexible data-type objects do not have this attribute. + Examples + -------- + + >>> x = np.dtype(float) + >>> x.name + 'float64' + >>> x = np.dtype([('a', np.int32, 8), ('b', np.float64, 6)]) + >>> x.name + 'void640' + """)) add_newdoc('numpy.core.multiarray', 'dtype', ('names', @@ -6635,6 +5949,17 @@ def luf(lamdaexpr, *args, **kwargs): These are roughly ordered from least-to-most precision. + Examples + -------- + + >>> dt = np.dtype(str) + >>> dt.num + 19 + + >>> dt = np.dtype(float) + >>> dt.num + 12 + """)) add_newdoc('numpy.core.multiarray', 'dtype', ('shape', @@ -6642,7 +5967,18 @@ def luf(lamdaexpr, *args, **kwargs): Shape tuple of the sub-array if this data type describes a sub-array, and ``()`` otherwise. - """)) + Examples + -------- + + >>> dt = np.dtype(('i4', 4)) + >>> dt.shape + (4,) + + >>> dt = np.dtype(('i4', (2, 3))) + >>> dt.shape + (2, 3) + + """)) add_newdoc('numpy.core.multiarray', 'dtype', ('ndim', """ @@ -6651,6 +5987,20 @@ def luf(lamdaexpr, *args, **kwargs): .. versionadded:: 1.13.0 + Examples + -------- + >>> x = np.dtype(float) + >>> x.ndim + 0 + + >>> x = np.dtype((float, 8)) + >>> x.ndim + 1 + + >>> x = np.dtype(('i4', (3, 4))) + >>> x.ndim + 2 + """)) add_newdoc('numpy.core.multiarray', 'dtype', ('str', @@ -6668,6 +6018,41 @@ def luf(lamdaexpr, *args, **kwargs): then the extra dimensions implied by *shape* are tacked on to the end of the retrieved array. + See Also + -------- + dtype.base + + Examples + -------- + >>> x = numpy.dtype('8f') + >>> x.subdtype + (dtype('float32'), (8,)) + + >>> x = numpy.dtype('i2') + >>> x.subdtype + >>> + + """)) + +add_newdoc('numpy.core.multiarray', 'dtype', ('base', + """ + Returns dtype for the base element of the subarrays, + regardless of their dimension or shape. + + See Also + -------- + dtype.subdtype + + Examples + -------- + >>> x = numpy.dtype('8f') + >>> x.base + dtype('float32') + + >>> x = numpy.dtype('i2') + >>> x.base + dtype('int16') + """)) add_newdoc('numpy.core.multiarray', 'dtype', ('type', @@ -6681,7 +6066,7 @@ def luf(lamdaexpr, *args, **kwargs): add_newdoc('numpy.core.multiarray', 'dtype', ('newbyteorder', """ - newbyteorder(new_order='S') + newbyteorder(new_order='S', /) Return a new dtype with a different byte order. @@ -6695,15 +6080,11 @@ def luf(lamdaexpr, *args, **kwargs): byte order. `new_order` codes can be any of: * 'S' - swap dtype from current to opposite endian - * {'<', 'L'} - little endian - * {'>', 'B'} - big endian - * {'=', 'N'} - native order + * {'<', 'little'} - little endian + * {'>', 'big'} - big endian + * {'=', 'native'} - native order * {'|', 'I'} - ignore (no change to byte order) - The code does a case-insensitive check on the first letter of - `new_order` for these alternatives. For example, any of '>' - or 'B' or 'b' or 'brian' are valid to specify big-endian. - Returns ------- new_dtype : dtype @@ -6744,6 +6125,97 @@ def luf(lamdaexpr, *args, **kwargs): """)) +add_newdoc('numpy.core.multiarray', 'dtype', ('__class_getitem__', + """ + __class_getitem__(item, /) + + Return a parametrized wrapper around the `~numpy.dtype` type. + + .. versionadded:: 1.22 + + Returns + ------- + alias : types.GenericAlias + A parametrized `~numpy.dtype` type. + + Examples + -------- + >>> import numpy as np + + >>> np.dtype[np.int64] + numpy.dtype[numpy.int64] + + Notes + ----- + This method is only available for python 3.9 and later. + + See Also + -------- + :pep:`585` : Type hinting generics in standard collections. + + """)) + +add_newdoc('numpy.core.multiarray', 'dtype', ('__ge__', + """ + __ge__(value, /) + + Return ``self >= value``. + + Equivalent to ``np.can_cast(value, self, casting="safe")``. + + See Also + -------- + can_cast : Returns True if cast between data types can occur according to + the casting rule. + + """)) + +add_newdoc('numpy.core.multiarray', 'dtype', ('__le__', + """ + __le__(value, /) + + Return ``self <= value``. + + Equivalent to ``np.can_cast(self, value, casting="safe")``. + + See Also + -------- + can_cast : Returns True if cast between data types can occur according to + the casting rule. + + """)) + +add_newdoc('numpy.core.multiarray', 'dtype', ('__gt__', + """ + __ge__(value, /) + + Return ``self > value``. + + Equivalent to + ``self != value and np.can_cast(value, self, casting="safe")``. + + See Also + -------- + can_cast : Returns True if cast between data types can occur according to + the casting rule. + + """)) + +add_newdoc('numpy.core.multiarray', 'dtype', ('__lt__', + """ + __lt__(value, /) + + Return ``self < value``. + + Equivalent to + ``self != value and np.can_cast(self, value, casting="safe")``. + + See Also + -------- + can_cast : Returns True if cast between data types can occur according to + the casting rule. + + """)) ############################################################################## # @@ -6809,7 +6281,7 @@ def luf(lamdaexpr, *args, **kwargs): ... holidays=['2011-07-01', '2011-07-04', '2011-07-17']) >>> # Default is Monday to Friday weekdays ... bdd.weekmask - array([ True, True, True, True, True, False, False], dtype='bool') + array([ True, True, True, True, True, False, False]) >>> # Any holidays already on the weekend are removed ... bdd.holidays array(['2011-07-01', '2011-07-04'], dtype='datetime64[D]') @@ -6821,211 +6293,6 @@ def luf(lamdaexpr, *args, **kwargs): add_newdoc('numpy.core.multiarray', 'busdaycalendar', ('holidays', """A copy of the holiday array indicating additional invalid days.""")) -add_newdoc('numpy.core.multiarray', 'is_busday', - """ - is_busday(dates, weekmask='1111100', holidays=None, busdaycal=None, out=None) - - Calculates which of the given dates are valid days, and which are not. - - .. versionadded:: 1.7.0 - - Parameters - ---------- - dates : array_like of datetime64[D] - The array of dates to process. - weekmask : str or array_like of bool, optional - A seven-element array indicating which of Monday through Sunday are - valid days. May be specified as a length-seven list or array, like - [1,1,1,1,1,0,0]; a length-seven string, like '1111100'; or a string - like "Mon Tue Wed Thu Fri", made up of 3-character abbreviations for - weekdays, optionally separated by white space. Valid abbreviations - are: Mon Tue Wed Thu Fri Sat Sun - holidays : array_like of datetime64[D], optional - An array of dates to consider as invalid dates. They may be - specified in any order, and NaT (not-a-time) dates are ignored. - This list is saved in a normalized form that is suited for - fast calculations of valid days. - busdaycal : busdaycalendar, optional - A `busdaycalendar` object which specifies the valid days. If this - parameter is provided, neither weekmask nor holidays may be - provided. - out : array of bool, optional - If provided, this array is filled with the result. - - Returns - ------- - out : array of bool - An array with the same shape as ``dates``, containing True for - each valid day, and False for each invalid day. - - See Also - -------- - busdaycalendar: An object that specifies a custom set of valid days. - busday_offset : Applies an offset counted in valid days. - busday_count : Counts how many valid days are in a half-open date range. - - Examples - -------- - >>> # The weekdays are Friday, Saturday, and Monday - ... np.is_busday(['2011-07-01', '2011-07-02', '2011-07-18'], - ... holidays=['2011-07-01', '2011-07-04', '2011-07-17']) - array([False, False, True], dtype='bool') - """) - -add_newdoc('numpy.core.multiarray', 'busday_offset', - """ - busday_offset(dates, offsets, roll='raise', weekmask='1111100', holidays=None, busdaycal=None, out=None) - - First adjusts the date to fall on a valid day according to - the ``roll`` rule, then applies offsets to the given dates - counted in valid days. - - .. versionadded:: 1.7.0 - - Parameters - ---------- - dates : array_like of datetime64[D] - The array of dates to process. - offsets : array_like of int - The array of offsets, which is broadcast with ``dates``. - roll : {'raise', 'nat', 'forward', 'following', 'backward', 'preceding', 'modifiedfollowing', 'modifiedpreceding'}, optional - How to treat dates that do not fall on a valid day. The default - is 'raise'. - - * 'raise' means to raise an exception for an invalid day. - * 'nat' means to return a NaT (not-a-time) for an invalid day. - * 'forward' and 'following' mean to take the first valid day - later in time. - * 'backward' and 'preceding' mean to take the first valid day - earlier in time. - * 'modifiedfollowing' means to take the first valid day - later in time unless it is across a Month boundary, in which - case to take the first valid day earlier in time. - * 'modifiedpreceding' means to take the first valid day - earlier in time unless it is across a Month boundary, in which - case to take the first valid day later in time. - weekmask : str or array_like of bool, optional - A seven-element array indicating which of Monday through Sunday are - valid days. May be specified as a length-seven list or array, like - [1,1,1,1,1,0,0]; a length-seven string, like '1111100'; or a string - like "Mon Tue Wed Thu Fri", made up of 3-character abbreviations for - weekdays, optionally separated by white space. Valid abbreviations - are: Mon Tue Wed Thu Fri Sat Sun - holidays : array_like of datetime64[D], optional - An array of dates to consider as invalid dates. They may be - specified in any order, and NaT (not-a-time) dates are ignored. - This list is saved in a normalized form that is suited for - fast calculations of valid days. - busdaycal : busdaycalendar, optional - A `busdaycalendar` object which specifies the valid days. If this - parameter is provided, neither weekmask nor holidays may be - provided. - out : array of datetime64[D], optional - If provided, this array is filled with the result. - - Returns - ------- - out : array of datetime64[D] - An array with a shape from broadcasting ``dates`` and ``offsets`` - together, containing the dates with offsets applied. - - See Also - -------- - busdaycalendar: An object that specifies a custom set of valid days. - is_busday : Returns a boolean array indicating valid days. - busday_count : Counts how many valid days are in a half-open date range. - - Examples - -------- - >>> # First business day in October 2011 (not accounting for holidays) - ... np.busday_offset('2011-10', 0, roll='forward') - numpy.datetime64('2011-10-03','D') - >>> # Last business day in February 2012 (not accounting for holidays) - ... np.busday_offset('2012-03', -1, roll='forward') - numpy.datetime64('2012-02-29','D') - >>> # Third Wednesday in January 2011 - ... np.busday_offset('2011-01', 2, roll='forward', weekmask='Wed') - numpy.datetime64('2011-01-19','D') - >>> # 2012 Mother's Day in Canada and the U.S. - ... np.busday_offset('2012-05', 1, roll='forward', weekmask='Sun') - numpy.datetime64('2012-05-13','D') - - >>> # First business day on or after a date - ... np.busday_offset('2011-03-20', 0, roll='forward') - numpy.datetime64('2011-03-21','D') - >>> np.busday_offset('2011-03-22', 0, roll='forward') - numpy.datetime64('2011-03-22','D') - >>> # First business day after a date - ... np.busday_offset('2011-03-20', 1, roll='backward') - numpy.datetime64('2011-03-21','D') - >>> np.busday_offset('2011-03-22', 1, roll='backward') - numpy.datetime64('2011-03-23','D') - """) - -add_newdoc('numpy.core.multiarray', 'busday_count', - """ - busday_count(begindates, enddates, weekmask='1111100', holidays=[], busdaycal=None, out=None) - - Counts the number of valid days between `begindates` and - `enddates`, not including the day of `enddates`. - - If ``enddates`` specifies a date value that is earlier than the - corresponding ``begindates`` date value, the count will be negative. - - .. versionadded:: 1.7.0 - - Parameters - ---------- - begindates : array_like of datetime64[D] - The array of the first dates for counting. - enddates : array_like of datetime64[D] - The array of the end dates for counting, which are excluded - from the count themselves. - weekmask : str or array_like of bool, optional - A seven-element array indicating which of Monday through Sunday are - valid days. May be specified as a length-seven list or array, like - [1,1,1,1,1,0,0]; a length-seven string, like '1111100'; or a string - like "Mon Tue Wed Thu Fri", made up of 3-character abbreviations for - weekdays, optionally separated by white space. Valid abbreviations - are: Mon Tue Wed Thu Fri Sat Sun - holidays : array_like of datetime64[D], optional - An array of dates to consider as invalid dates. They may be - specified in any order, and NaT (not-a-time) dates are ignored. - This list is saved in a normalized form that is suited for - fast calculations of valid days. - busdaycal : busdaycalendar, optional - A `busdaycalendar` object which specifies the valid days. If this - parameter is provided, neither weekmask nor holidays may be - provided. - out : array of int, optional - If provided, this array is filled with the result. - - Returns - ------- - out : array of int - An array with a shape from broadcasting ``begindates`` and ``enddates`` - together, containing the number of valid days between - the begin and end dates. - - See Also - -------- - busdaycalendar: An object that specifies a custom set of valid days. - is_busday : Returns a boolean array indicating valid days. - busday_offset : Applies an offset counted in valid days. - - Examples - -------- - >>> # Number of weekdays in January 2011 - ... np.busday_count('2011-01', '2011-02') - 21 - >>> # Number of weekdays in 2011 - ... np.busday_count('2011', '2012') - 260 - >>> # Number of Saturdays in 2011 - ... np.busday_count('2011', '2012', weekmask='Sat') - 53 - """) - add_newdoc('numpy.core.multiarray', 'normalize_axis_index', """ normalize_axis_index(axis, ndim, msg_prefix=None) @@ -7077,75 +6344,14 @@ def luf(lamdaexpr, *args, **kwargs): AxisError: axes_arg: axis -4 is out of bounds for array of dimension 3 """) -add_newdoc('numpy.core.multiarray', 'datetime_as_string', - """ - datetime_as_string(arr, unit=None, timezone='naive', casting='same_kind') - - Convert an array of datetimes into an array of strings. - - Parameters - ---------- - arr : array_like of datetime64 - The array of UTC timestamps to format. - unit : str - One of None, 'auto', or a :ref:`datetime unit <arrays.dtypes.dateunits>`. - timezone : {'naive', 'UTC', 'local'} or tzinfo - Timezone information to use when displaying the datetime. If 'UTC', end - with a Z to indicate UTC time. If 'local', convert to the local timezone - first, and suffix with a +-#### timezone offset. If a tzinfo object, - then do as with 'local', but use the specified timezone. - casting : {'no', 'equiv', 'safe', 'same_kind', 'unsafe'} - Casting to allow when changing between datetime units. - - Returns - ------- - str_arr : ndarray - An array of strings the same shape as `arr`. - - Examples - -------- - >>> d = np.arange('2002-10-27T04:30', 4*60, 60, dtype='M8[m]') - >>> d - array(['2002-10-27T04:30', '2002-10-27T05:30', '2002-10-27T06:30', - '2002-10-27T07:30'], dtype='datetime64[m]') - - Setting the timezone to UTC shows the same information, but with a Z suffix - - >>> np.datetime_as_string(d, timezone='UTC') - array(['2002-10-27T04:30Z', '2002-10-27T05:30Z', '2002-10-27T06:30Z', - '2002-10-27T07:30Z'], dtype='<U35') - - Note that we picked datetimes that cross a DST boundary. Passing in a - ``pytz`` timezone object will print the appropriate offset - - >>> np.datetime_as_string(d, timezone=pytz.timezone('US/Eastern')) - array(['2002-10-27T00:30-0400', '2002-10-27T01:30-0400', - '2002-10-27T01:30-0500', '2002-10-27T02:30-0500'], dtype='<U39') - - Passing in a unit will change the precision - - >>> np.datetime_as_string(d, unit='h') - array(['2002-10-27T04', '2002-10-27T05', '2002-10-27T06', '2002-10-27T07'], - dtype='<U32') - >>> np.datetime_as_string(d, unit='s') - array(['2002-10-27T04:30:00', '2002-10-27T05:30:00', '2002-10-27T06:30:00', - '2002-10-27T07:30:00'], dtype='<U38') - - 'casting' can be used to specify whether precision can be changed - - >>> np.datetime_as_string(d, unit='h', casting='safe') - TypeError: Cannot create a datetime string as units 'h' from a NumPy - datetime with units 'm' according to the rule 'safe' - """) - add_newdoc('numpy.core.multiarray', 'datetime_data', """ datetime_data(dtype, /) Get information about the step size of a date or time type. - The returned tuple can be passed as the second argument of `datetime64` and - `timedelta64`. + The returned tuple can be passed as the second argument of `numpy.datetime64` and + `numpy.timedelta64`. Parameters ---------- @@ -7169,100 +6375,12 @@ def luf(lamdaexpr, *args, **kwargs): array(250, dtype='timedelta64[s]') The result can be used to construct a datetime that uses the same units - as a timedelta:: + as a timedelta >>> np.datetime64('2010', np.datetime_data(dt_25s)) numpy.datetime64('2010-01-01T00:00:00','25s') """) -############################################################################## -# -# nd_grid instances -# -############################################################################## - -add_newdoc('numpy.lib.index_tricks', 'mgrid', - """ - `nd_grid` instance which returns a dense multi-dimensional "meshgrid". - - An instance of `numpy.lib.index_tricks.nd_grid` which returns an dense - (or fleshed out) mesh-grid when indexed, so that each returned argument - has the same shape. The dimensions and number of the output arrays are - equal to the number of indexing dimensions. If the step length is not a - complex number, then the stop is not inclusive. - - However, if the step length is a **complex number** (e.g. 5j), then - the integer part of its magnitude is interpreted as specifying the - number of points to create between the start and stop values, where - the stop value **is inclusive**. - - Returns - ---------- - mesh-grid `ndarrays` all of the same dimensions - - See Also - -------- - numpy.lib.index_tricks.nd_grid : class of `ogrid` and `mgrid` objects - ogrid : like mgrid but returns open (not fleshed out) mesh grids - r_ : array concatenator - - Examples - -------- - >>> np.mgrid[0:5,0:5] - array([[[0, 0, 0, 0, 0], - [1, 1, 1, 1, 1], - [2, 2, 2, 2, 2], - [3, 3, 3, 3, 3], - [4, 4, 4, 4, 4]], - [[0, 1, 2, 3, 4], - [0, 1, 2, 3, 4], - [0, 1, 2, 3, 4], - [0, 1, 2, 3, 4], - [0, 1, 2, 3, 4]]]) - >>> np.mgrid[-1:1:5j] - array([-1. , -0.5, 0. , 0.5, 1. ]) - - """) - -add_newdoc('numpy.lib.index_tricks', 'ogrid', - """ - `nd_grid` instance which returns an open multi-dimensional "meshgrid". - - An instance of `numpy.lib.index_tricks.nd_grid` which returns an open - (i.e. not fleshed out) mesh-grid when indexed, so that only one dimension - of each returned array is greater than 1. The dimension and number of the - output arrays are equal to the number of indexing dimensions. If the step - length is not a complex number, then the stop is not inclusive. - - However, if the step length is a **complex number** (e.g. 5j), then - the integer part of its magnitude is interpreted as specifying the - number of points to create between the start and stop values, where - the stop value **is inclusive**. - - Returns - ---------- - mesh-grid `ndarrays` with only one dimension :math:`\\neq 1` - - See Also - -------- - np.lib.index_tricks.nd_grid : class of `ogrid` and `mgrid` objects - mgrid : like `ogrid` but returns dense (or fleshed out) mesh grids - r_ : array concatenator - - Examples - -------- - >>> from numpy import ogrid - >>> ogrid[-1:1:5j] - array([-1. , -0.5, 0. , 0.5, 1. ]) - >>> ogrid[0:5,0:5] - [array([[0], - [1], - [2], - [3], - [4]]), array([[0, 1, 2, 3, 4]])] - - """) - ############################################################################## # @@ -7284,33 +6402,21 @@ def luf(lamdaexpr, *args, **kwargs): # Attributes -add_newdoc('numpy.core.numerictypes', 'generic', ('T', - """ - Not implemented (virtual attribute) - - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class so as to - provide a uniform API. - - See Also - -------- - The corresponding attribute of the derived class of interest. - - """)) +def refer_to_array_attribute(attr, method=True): + docstring = """ + Scalar {} identical to the corresponding array attribute. -add_newdoc('numpy.core.numerictypes', 'generic', ('base', + Please see `ndarray.{}`. """ - Not implemented (virtual attribute) - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class so as to - a uniform API. + return attr, docstring.format("method" if method else "attribute", attr) - See Also - -------- - The corresponding attribute of the derived class of interest. - """)) +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('T', method=False)) + +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('base', method=False)) add_newdoc('numpy.core.numerictypes', 'generic', ('data', """Pointer to start of data.""")) @@ -7350,359 +6456,84 @@ def luf(lamdaexpr, *args, **kwargs): # Methods -add_newdoc('numpy.core.numerictypes', 'generic', ('all', - """ - Not implemented (virtual attribute) +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('all')) - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('any')) - See Also - -------- - The corresponding attribute of the derived class of interest. +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('argmax')) - """)) +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('argmin')) -add_newdoc('numpy.core.numerictypes', 'generic', ('any', - """ - Not implemented (virtual attribute) +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('argsort')) - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. - - See Also - -------- - The corresponding attribute of the derived class of interest. - - """)) - -add_newdoc('numpy.core.numerictypes', 'generic', ('argmax', - """ - Not implemented (virtual attribute) - - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. - - See Also - -------- - The corresponding attribute of the derived class of interest. - - """)) - -add_newdoc('numpy.core.numerictypes', 'generic', ('argmin', - """ - Not implemented (virtual attribute) - - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. - - See Also - -------- - The corresponding attribute of the derived class of interest. - - """)) - -add_newdoc('numpy.core.numerictypes', 'generic', ('argsort', - """ - Not implemented (virtual attribute) - - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. - - See Also - -------- - The corresponding attribute of the derived class of interest. - - """)) - -add_newdoc('numpy.core.numerictypes', 'generic', ('astype', - """ - Not implemented (virtual attribute) - - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. - - See Also - -------- - The corresponding attribute of the derived class of interest. - - """)) - -add_newdoc('numpy.core.numerictypes', 'generic', ('byteswap', - """ - Not implemented (virtual attribute) - - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class so as to - provide a uniform API. - - See Also - -------- - The corresponding attribute of the derived class of interest. - - """)) - -add_newdoc('numpy.core.numerictypes', 'generic', ('choose', - """ - Not implemented (virtual attribute) - - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. - - See Also - -------- - The corresponding attribute of the derived class of interest. - - """)) - -add_newdoc('numpy.core.numerictypes', 'generic', ('clip', - """ - Not implemented (virtual attribute) - - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. - - See Also - -------- - The corresponding attribute of the derived class of interest. - - """)) - -add_newdoc('numpy.core.numerictypes', 'generic', ('compress', - """ - Not implemented (virtual attribute) - - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. - - See Also - -------- - The corresponding attribute of the derived class of interest. - - """)) - -add_newdoc('numpy.core.numerictypes', 'generic', ('conjugate', - """ - Not implemented (virtual attribute) - - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. - - See Also - -------- - The corresponding attribute of the derived class of interest. - - """)) - -add_newdoc('numpy.core.numerictypes', 'generic', ('copy', - """ - Not implemented (virtual attribute) - - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. - - See Also - -------- - The corresponding attribute of the derived class of interest. - - """)) - -add_newdoc('numpy.core.numerictypes', 'generic', ('cumprod', - """ - Not implemented (virtual attribute) - - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. - - See Also - -------- - The corresponding attribute of the derived class of interest. - - """)) - -add_newdoc('numpy.core.numerictypes', 'generic', ('cumsum', - """ - Not implemented (virtual attribute) - - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. - - See Also - -------- - The corresponding attribute of the derived class of interest. - - """)) - -add_newdoc('numpy.core.numerictypes', 'generic', ('diagonal', - """ - Not implemented (virtual attribute) - - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. - - See Also - -------- - The corresponding attribute of the derived class of interest. - - """)) - -add_newdoc('numpy.core.numerictypes', 'generic', ('dump', - """ - Not implemented (virtual attribute) - - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. - - See Also - -------- - The corresponding attribute of the derived class of interest. - - """)) - -add_newdoc('numpy.core.numerictypes', 'generic', ('dumps', - """ - Not implemented (virtual attribute) - - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. - - See Also - -------- - The corresponding attribute of the derived class of interest. - - """)) - -add_newdoc('numpy.core.numerictypes', 'generic', ('fill', - """ - Not implemented (virtual attribute) - - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. - - See Also - -------- - The corresponding attribute of the derived class of interest. - - """)) - -add_newdoc('numpy.core.numerictypes', 'generic', ('flatten', - """ - Not implemented (virtual attribute) - - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. - - See Also - -------- - The corresponding attribute of the derived class of interest. - - """)) - -add_newdoc('numpy.core.numerictypes', 'generic', ('getfield', - """ - Not implemented (virtual attribute) - - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. - - See Also - -------- - The corresponding attribute of the derived class of interest. - - """)) - -add_newdoc('numpy.core.numerictypes', 'generic', ('item', - """ - Not implemented (virtual attribute) +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('astype')) - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('byteswap')) - See Also - -------- - The corresponding attribute of the derived class of interest. +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('choose')) - """)) +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('clip')) -add_newdoc('numpy.core.numerictypes', 'generic', ('itemset', - """ - Not implemented (virtual attribute) +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('compress')) - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('conjugate')) - See Also - -------- - The corresponding attribute of the derived class of interest. +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('copy')) - """)) +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('cumprod')) -add_newdoc('numpy.core.numerictypes', 'generic', ('max', - """ - Not implemented (virtual attribute) +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('cumsum')) - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('diagonal')) - See Also - -------- - The corresponding attribute of the derived class of interest. +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('dump')) - """)) +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('dumps')) -add_newdoc('numpy.core.numerictypes', 'generic', ('mean', - """ - Not implemented (virtual attribute) +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('fill')) - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('flatten')) - See Also - -------- - The corresponding attribute of the derived class of interest. +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('getfield')) - """)) +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('item')) -add_newdoc('numpy.core.numerictypes', 'generic', ('min', - """ - Not implemented (virtual attribute) +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('itemset')) - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('max')) - See Also - -------- - The corresponding attribute of the derived class of interest. +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('mean')) - """)) +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('min')) add_newdoc('numpy.core.numerictypes', 'generic', ('newbyteorder', """ - newbyteorder(new_order='S') + newbyteorder(new_order='S', /) Return a new `dtype` with a different byte order. @@ -7711,9 +6542,9 @@ def luf(lamdaexpr, *args, **kwargs): The `new_order` code can be any from the following: * 'S' - swap dtype from current to opposite endian - * {'<', 'L'} - little endian - * {'>', 'B'} - big endian - * {'=', 'N'} - native order + * {'<', 'little'} - little endian + * {'>', 'big'} - big endian + * {'=', 'native'} - native order * {'|', 'I'} - ignore (no change to byte order) Parameters @@ -7721,9 +6552,7 @@ def luf(lamdaexpr, *args, **kwargs): new_order : str, optional Byte order to force; a value from the byte order specifications above. The default value ('S') results in swapping the current - byte order. The code does a case-insensitive check on the first - letter of `new_order` for the alternatives above. For example, - any of 'B' or 'b' or 'biggish' are valid to specify big-endian. + byte order. Returns @@ -7733,419 +6562,174 @@ def luf(lamdaexpr, *args, **kwargs): """)) -add_newdoc('numpy.core.numerictypes', 'generic', ('nonzero', - """ - Not implemented (virtual attribute) - - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. - - See Also - -------- - The corresponding attribute of the derived class of interest. - - """)) - -add_newdoc('numpy.core.numerictypes', 'generic', ('prod', - """ - Not implemented (virtual attribute) - - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. - - See Also - -------- - The corresponding attribute of the derived class of interest. - - """)) - -add_newdoc('numpy.core.numerictypes', 'generic', ('ptp', - """ - Not implemented (virtual attribute) - - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. - - See Also - -------- - The corresponding attribute of the derived class of interest. - - """)) - -add_newdoc('numpy.core.numerictypes', 'generic', ('put', - """ - Not implemented (virtual attribute) - - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. - - See Also - -------- - The corresponding attribute of the derived class of interest. - - """)) - -add_newdoc('numpy.core.numerictypes', 'generic', ('ravel', - """ - Not implemented (virtual attribute) - - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. - - See Also - -------- - The corresponding attribute of the derived class of interest. - - """)) - -add_newdoc('numpy.core.numerictypes', 'generic', ('repeat', - """ - Not implemented (virtual attribute) - - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. - - See Also - -------- - The corresponding attribute of the derived class of interest. - - """)) - -add_newdoc('numpy.core.numerictypes', 'generic', ('reshape', - """ - Not implemented (virtual attribute) - - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. - - See Also - -------- - The corresponding attribute of the derived class of interest. - - """)) - -add_newdoc('numpy.core.numerictypes', 'generic', ('resize', - """ - Not implemented (virtual attribute) - - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. - - See Also - -------- - The corresponding attribute of the derived class of interest. - - """)) - -add_newdoc('numpy.core.numerictypes', 'generic', ('round', - """ - Not implemented (virtual attribute) - - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. - - See Also - -------- - The corresponding attribute of the derived class of interest. - - """)) - -add_newdoc('numpy.core.numerictypes', 'generic', ('searchsorted', - """ - Not implemented (virtual attribute) - - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. - - See Also - -------- - The corresponding attribute of the derived class of interest. - - """)) - -add_newdoc('numpy.core.numerictypes', 'generic', ('setfield', - """ - Not implemented (virtual attribute) - - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. - - See Also - -------- - The corresponding attribute of the derived class of interest. - - """)) - -add_newdoc('numpy.core.numerictypes', 'generic', ('setflags', - """ - Not implemented (virtual attribute) - - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class so as to - provide a uniform API. - - See Also - -------- - The corresponding attribute of the derived class of interest. - - """)) - -add_newdoc('numpy.core.numerictypes', 'generic', ('sort', - """ - Not implemented (virtual attribute) - - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. - - See Also - -------- - The corresponding attribute of the derived class of interest. - - """)) - -add_newdoc('numpy.core.numerictypes', 'generic', ('squeeze', - """ - Not implemented (virtual attribute) - - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. - - See Also - -------- - The corresponding attribute of the derived class of interest. - - """)) - -add_newdoc('numpy.core.numerictypes', 'generic', ('std', - """ - Not implemented (virtual attribute) - - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. - - See Also - -------- - The corresponding attribute of the derived class of interest. - - """)) - -add_newdoc('numpy.core.numerictypes', 'generic', ('sum', - """ - Not implemented (virtual attribute) - - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. - - See Also - -------- - The corresponding attribute of the derived class of interest. - - """)) +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('nonzero')) -add_newdoc('numpy.core.numerictypes', 'generic', ('swapaxes', - """ - Not implemented (virtual attribute) +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('prod')) - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('ptp')) - See Also - -------- - The corresponding attribute of the derived class of interest. +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('put')) - """)) +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('ravel')) -add_newdoc('numpy.core.numerictypes', 'generic', ('take', - """ - Not implemented (virtual attribute) +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('repeat')) - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('reshape')) - See Also - -------- - The corresponding attribute of the derived class of interest. +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('resize')) - """)) +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('round')) -add_newdoc('numpy.core.numerictypes', 'generic', ('tofile', - """ - Not implemented (virtual attribute) +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('searchsorted')) - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('setfield')) - See Also - -------- - The corresponding attribute of the derived class of interest. +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('setflags')) - """)) +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('sort')) -add_newdoc('numpy.core.numerictypes', 'generic', ('tolist', - """ - Not implemented (virtual attribute) +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('squeeze')) - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('std')) - See Also - -------- - The corresponding attribute of the derived class of interest. +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('sum')) - """)) +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('swapaxes')) -add_newdoc('numpy.core.numerictypes', 'generic', ('tostring', - """ - Not implemented (virtual attribute) +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('take')) - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('tofile')) - See Also - -------- - The corresponding attribute of the derived class of interest. +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('tolist')) - """)) +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('tostring')) -add_newdoc('numpy.core.numerictypes', 'generic', ('trace', - """ - Not implemented (virtual attribute) +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('trace')) - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('transpose')) - See Also - -------- - The corresponding attribute of the derived class of interest. +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('var')) - """)) +add_newdoc('numpy.core.numerictypes', 'generic', + refer_to_array_attribute('view')) -add_newdoc('numpy.core.numerictypes', 'generic', ('transpose', +add_newdoc('numpy.core.numerictypes', 'number', ('__class_getitem__', """ - Not implemented (virtual attribute) - - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. - - See Also - -------- - The corresponding attribute of the derived class of interest. + __class_getitem__(item, /) - """)) + Return a parametrized wrapper around the `~numpy.number` type. -add_newdoc('numpy.core.numerictypes', 'generic', ('var', - """ - Not implemented (virtual attribute) + .. versionadded:: 1.22 - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. + Returns + ------- + alias : types.GenericAlias + A parametrized `~numpy.number` type. - See Also + Examples -------- - The corresponding attribute of the derived class of interest. + >>> from typing import Any + >>> import numpy as np - """)) - -add_newdoc('numpy.core.numerictypes', 'generic', ('view', - """ - Not implemented (virtual attribute) + >>> np.signedinteger[Any] + numpy.signedinteger[typing.Any] - Class generic exists solely to derive numpy scalars from, and possesses, - albeit unimplemented, all the attributes of the ndarray class - so as to provide a uniform API. + Notes + ----- + This method is only available for python 3.9 and later. See Also -------- - The corresponding attribute of the derived class of interest. + :pep:`585` : Type hinting generics in standard collections. """)) - ############################################################################## # -# Documentation for other scalar classes +# Documentation for scalar type abstract base classes in type hierarchy # ############################################################################## -add_newdoc('numpy.core.numerictypes', 'bool_', - """NumPy's Boolean type. Character code: ``?``. Alias: bool8""") -add_newdoc('numpy.core.numerictypes', 'complex64', +add_newdoc('numpy.core.numerictypes', 'number', """ - Complex number type composed of two 32 bit floats. Character code: 'F'. + Abstract base class of all numeric scalar types. """) -add_newdoc('numpy.core.numerictypes', 'complex128', +add_newdoc('numpy.core.numerictypes', 'integer', """ - Complex number type composed of two 64 bit floats. Character code: 'D'. - Python complex compatible. + Abstract base class of all integer scalar types. """) -add_newdoc('numpy.core.numerictypes', 'complex256', +add_newdoc('numpy.core.numerictypes', 'signedinteger', """ - Complex number type composed of two 128-bit floats. Character code: 'G'. + Abstract base class of all signed integer scalar types. """) -add_newdoc('numpy.core.numerictypes', 'float32', +add_newdoc('numpy.core.numerictypes', 'unsignedinteger', """ - 32-bit floating-point number. Character code 'f'. C float compatible. + Abstract base class of all unsigned integer scalar types. """) -add_newdoc('numpy.core.numerictypes', 'float64', +add_newdoc('numpy.core.numerictypes', 'inexact', """ - 64-bit floating-point number. Character code 'd'. Python float compatible. + Abstract base class of all numeric scalar types with a (potentially) + inexact representation of the values in its range, such as + floating-point numbers. """) -add_newdoc('numpy.core.numerictypes', 'float96', +add_newdoc('numpy.core.numerictypes', 'floating', """ + Abstract base class of all floating-point scalar types. + """) -add_newdoc('numpy.core.numerictypes', 'float128', +add_newdoc('numpy.core.numerictypes', 'complexfloating', """ - 128-bit floating-point number. Character code: 'g'. C long float - compatible. + Abstract base class of all complex number scalar types that are made up of + floating-point numbers. """) -add_newdoc('numpy.core.numerictypes', 'int8', - """8-bit integer. Character code ``b``. C char compatible.""") - -add_newdoc('numpy.core.numerictypes', 'int16', - """16-bit integer. Character code ``h``. C short compatible.""") +add_newdoc('numpy.core.numerictypes', 'flexible', + """ + Abstract base class of all scalar types without predefined length. + The actual size of these types depends on the specific `np.dtype` + instantiation. -add_newdoc('numpy.core.numerictypes', 'int32', - """32-bit integer. Character code 'i'. C int compatible.""") + """) -add_newdoc('numpy.core.numerictypes', 'int64', - """64-bit integer. Character code 'l'. Python int compatible.""") +add_newdoc('numpy.core.numerictypes', 'character', + """ + Abstract base class of all character string scalar types. -add_newdoc('numpy.core.numerictypes', 'object_', - """Any Python object. Character code: 'O'.""") + """) diff --git a/numpy/core/_add_newdocs_scalars.py b/numpy/core/_add_newdocs_scalars.py new file mode 100644 index 000000000000..94859a9d556d --- /dev/null +++ b/numpy/core/_add_newdocs_scalars.py @@ -0,0 +1,311 @@ +""" +This file is separate from ``_add_newdocs.py`` so that it can be mocked out by +our sphinx ``conf.py`` during doc builds, where we want to avoid showing +platform-dependent information. +""" +from numpy.core import dtype +from numpy.core import numerictypes as _numerictypes +from numpy.core.function_base import add_newdoc +import platform + +############################################################################## +# +# Documentation for concrete scalar classes +# +############################################################################## + +def numeric_type_aliases(aliases): + def type_aliases_gen(): + for alias, doc in aliases: + try: + alias_type = getattr(_numerictypes, alias) + except AttributeError: + # The set of aliases that actually exist varies between platforms + pass + else: + yield (alias_type, alias, doc) + return list(type_aliases_gen()) + + +possible_aliases = numeric_type_aliases([ + ('int8', '8-bit signed integer (``-128`` to ``127``)'), + ('int16', '16-bit signed integer (``-32_768`` to ``32_767``)'), + ('int32', '32-bit signed integer (``-2_147_483_648`` to ``2_147_483_647``)'), + ('int64', '64-bit signed integer (``-9_223_372_036_854_775_808`` to ``9_223_372_036_854_775_807``)'), + ('intp', 'Signed integer large enough to fit pointer, compatible with C ``intptr_t``'), + ('uint8', '8-bit unsigned integer (``0`` to ``255``)'), + ('uint16', '16-bit unsigned integer (``0`` to ``65_535``)'), + ('uint32', '32-bit unsigned integer (``0`` to ``4_294_967_295``)'), + ('uint64', '64-bit unsigned integer (``0`` to ``18_446_744_073_709_551_615``)'), + ('uintp', 'Unsigned integer large enough to fit pointer, compatible with C ``uintptr_t``'), + ('float16', '16-bit-precision floating-point number type: sign bit, 5 bits exponent, 10 bits mantissa'), + ('float32', '32-bit-precision floating-point number type: sign bit, 8 bits exponent, 23 bits mantissa'), + ('float64', '64-bit precision floating-point number type: sign bit, 11 bits exponent, 52 bits mantissa'), + ('float96', '96-bit extended-precision floating-point number type'), + ('float128', '128-bit extended-precision floating-point number type'), + ('complex64', 'Complex number type composed of 2 32-bit-precision floating-point numbers'), + ('complex128', 'Complex number type composed of 2 64-bit-precision floating-point numbers'), + ('complex192', 'Complex number type composed of 2 96-bit extended-precision floating-point numbers'), + ('complex256', 'Complex number type composed of 2 128-bit extended-precision floating-point numbers'), + ]) + + + + +def add_newdoc_for_scalar_type(obj, fixed_aliases, doc): + # note: `:field: value` is rST syntax which renders as field lists. + o = getattr(_numerictypes, obj) + + character_code = dtype(o).char + canonical_name_doc = "" if obj == o.__name__ else ":Canonical name: `numpy.{}`\n ".format(obj) + alias_doc = ''.join(":Alias: `numpy.{}`\n ".format(alias) for alias in fixed_aliases) + alias_doc += ''.join(":Alias on this platform ({} {}): `numpy.{}`: {}.\n ".format(platform.system(), platform.machine(), alias, doc) + for (alias_type, alias, doc) in possible_aliases if alias_type is o) + docstring = """ + {doc} + + :Character code: ``'{character_code}'`` + {canonical_name_doc}{alias_doc} + """.format(doc=doc.strip(), character_code=character_code, + canonical_name_doc=canonical_name_doc, alias_doc=alias_doc) + + add_newdoc('numpy.core.numerictypes', obj, docstring) + + +add_newdoc_for_scalar_type('bool_', ['bool8'], + """ + Boolean type (True or False), stored as a byte. + + .. warning:: + + The :class:`bool_` type is not a subclass of the :class:`int_` type + (the :class:`bool_` is not even a number type). This is different + than Python's default implementation of :class:`bool` as a + sub-class of :class:`int`. + """) + +add_newdoc_for_scalar_type('byte', [], + """ + Signed integer type, compatible with C ``char``. + """) + +add_newdoc_for_scalar_type('short', [], + """ + Signed integer type, compatible with C ``short``. + """) + +add_newdoc_for_scalar_type('intc', [], + """ + Signed integer type, compatible with C ``int``. + """) + +add_newdoc_for_scalar_type('int_', [], + """ + Signed integer type, compatible with Python `int` and C ``long``. + """) + +add_newdoc_for_scalar_type('longlong', [], + """ + Signed integer type, compatible with C ``long long``. + """) + +add_newdoc_for_scalar_type('ubyte', [], + """ + Unsigned integer type, compatible with C ``unsigned char``. + """) + +add_newdoc_for_scalar_type('ushort', [], + """ + Unsigned integer type, compatible with C ``unsigned short``. + """) + +add_newdoc_for_scalar_type('uintc', [], + """ + Unsigned integer type, compatible with C ``unsigned int``. + """) + +add_newdoc_for_scalar_type('uint', [], + """ + Unsigned integer type, compatible with C ``unsigned long``. + """) + +add_newdoc_for_scalar_type('ulonglong', [], + """ + Signed integer type, compatible with C ``unsigned long long``. + """) + +add_newdoc_for_scalar_type('half', [], + """ + Half-precision floating-point number type. + """) + +add_newdoc_for_scalar_type('single', [], + """ + Single-precision floating-point number type, compatible with C ``float``. + """) + +add_newdoc_for_scalar_type('double', ['float_'], + """ + Double-precision floating-point number type, compatible with Python `float` + and C ``double``. + """) + +add_newdoc_for_scalar_type('longdouble', ['longfloat'], + """ + Extended-precision floating-point number type, compatible with C + ``long double`` but not necessarily with IEEE 754 quadruple-precision. + """) + +add_newdoc_for_scalar_type('csingle', ['singlecomplex'], + """ + Complex number type composed of two single-precision floating-point + numbers. + """) + +add_newdoc_for_scalar_type('cdouble', ['cfloat', 'complex_'], + """ + Complex number type composed of two double-precision floating-point + numbers, compatible with Python `complex`. + """) + +add_newdoc_for_scalar_type('clongdouble', ['clongfloat', 'longcomplex'], + """ + Complex number type composed of two extended-precision floating-point + numbers. + """) + +add_newdoc_for_scalar_type('object_', [], + """ + Any Python object. + """) + +add_newdoc_for_scalar_type('str_', ['unicode_'], + r""" + A unicode string. + + When used in arrays, this type strips trailing null codepoints. + + Unlike the builtin `str`, this supports the :ref:`python:bufferobjects`, exposing its + contents as UCS4: + + >>> m = memoryview(np.str_("abc")) + >>> m.format + '3w' + >>> m.tobytes() + b'a\x00\x00\x00b\x00\x00\x00c\x00\x00\x00' + """) + +add_newdoc_for_scalar_type('bytes_', ['string_'], + r""" + A byte string. + + When used in arrays, this type strips trailing null bytes. + """) + +add_newdoc_for_scalar_type('void', [], + r""" + Either an opaque sequence of bytes, or a structure. + + >>> np.void(b'abcd') + void(b'\x61\x62\x63\x64') + + Structured `void` scalars can only be constructed via extraction from :ref:`structured_arrays`: + + >>> arr = np.array((1, 2), dtype=[('x', np.int8), ('y', np.int8)]) + >>> arr[()] + (1, 2) # looks like a tuple, but is `np.void` + """) + +add_newdoc_for_scalar_type('datetime64', [], + """ + If created from a 64-bit integer, it represents an offset from + ``1970-01-01T00:00:00``. + If created from string, the string can be in ISO 8601 date + or datetime format. + + >>> np.datetime64(10, 'Y') + numpy.datetime64('1980') + >>> np.datetime64('1980', 'Y') + numpy.datetime64('1980') + >>> np.datetime64(10, 'D') + numpy.datetime64('1970-01-11') + + See :ref:`arrays.datetime` for more information. + """) + +add_newdoc_for_scalar_type('timedelta64', [], + """ + A timedelta stored as a 64-bit integer. + + See :ref:`arrays.datetime` for more information. + """) + +add_newdoc('numpy.core.numerictypes', "integer", ('is_integer', + """ + integer.is_integer() -> bool + + Return ``True`` if the number is finite with integral value. + + .. versionadded:: 1.22 + + Examples + -------- + >>> np.int64(-2).is_integer() + True + >>> np.uint32(5).is_integer() + True + """)) + +# TODO: work out how to put this on the base class, np.floating +for float_name in ('half', 'single', 'double', 'longdouble'): + add_newdoc('numpy.core.numerictypes', float_name, ('as_integer_ratio', + """ + {ftype}.as_integer_ratio() -> (int, int) + + Return a pair of integers, whose ratio is exactly equal to the original + floating point number, and with a positive denominator. + Raise `OverflowError` on infinities and a `ValueError` on NaNs. + + >>> np.{ftype}(10.0).as_integer_ratio() + (10, 1) + >>> np.{ftype}(0.0).as_integer_ratio() + (0, 1) + >>> np.{ftype}(-.25).as_integer_ratio() + (-1, 4) + """.format(ftype=float_name))) + + add_newdoc('numpy.core.numerictypes', float_name, ('is_integer', + f""" + {float_name}.is_integer() -> bool + + Return ``True`` if the floating point number is finite with integral + value, and ``False`` otherwise. + + .. versionadded:: 1.22 + + Examples + -------- + >>> np.{float_name}(-2.0).is_integer() + True + >>> np.{float_name}(3.2).is_integer() + False + """)) + +for int_name in ('int8', 'uint8', 'int16', 'uint16', 'int32', 'uint32', + 'int64', 'uint64', 'int64', 'uint64', 'int64', 'uint64'): + # Add negative examples for signed cases by checking typecode + add_newdoc('numpy.core.numerictypes', int_name, ('bit_count', + f""" + {int_name}.bit_count() -> int + + Computes the number of 1-bits in the absolute value of the input. + Analogous to the builtin `int.bit_count` or ``popcount`` in C++. + + Examples + -------- + >>> np.{int_name}(127).bit_count() + 7""" + + (f""" + >>> np.{int_name}(-127).bit_count() + 7 + """ if dtype(int_name).char.islower() else ""))) diff --git a/numpy/core/_asarray.py b/numpy/core/_asarray.py new file mode 100644 index 000000000000..ecb4e7c39d0c --- /dev/null +++ b/numpy/core/_asarray.py @@ -0,0 +1,140 @@ +""" +Functions in the ``as*array`` family that promote array-likes into arrays. + +`require` fits this category despite its name not matching this pattern. +""" +from .overrides import ( + array_function_dispatch, + set_array_function_like_doc, + set_module, +) +from .multiarray import array, asanyarray + + +__all__ = ["require"] + + + +def _require_dispatcher(a, dtype=None, requirements=None, *, like=None): + return (like,) + + +@set_array_function_like_doc +@set_module('numpy') +def require(a, dtype=None, requirements=None, *, like=None): + """ + Return an ndarray of the provided type that satisfies requirements. + + This function is useful to be sure that an array with the correct flags + is returned for passing to compiled code (perhaps through ctypes). + + Parameters + ---------- + a : array_like + The object to be converted to a type-and-requirement-satisfying array. + dtype : data-type + The required data-type. If None preserve the current dtype. If your + application requires the data to be in native byteorder, include + a byteorder specification as a part of the dtype specification. + requirements : str or list of str + The requirements list can be any of the following + + * 'F_CONTIGUOUS' ('F') - ensure a Fortran-contiguous array + * 'C_CONTIGUOUS' ('C') - ensure a C-contiguous array + * 'ALIGNED' ('A') - ensure a data-type aligned array + * 'WRITEABLE' ('W') - ensure a writable array + * 'OWNDATA' ('O') - ensure an array that owns its own data + * 'ENSUREARRAY', ('E') - ensure a base array, instead of a subclass + ${ARRAY_FUNCTION_LIKE} + + .. versionadded:: 1.20.0 + + Returns + ------- + out : ndarray + Array with specified requirements and type if given. + + See Also + -------- + asarray : Convert input to an ndarray. + asanyarray : Convert to an ndarray, but pass through ndarray subclasses. + ascontiguousarray : Convert input to a contiguous array. + asfortranarray : Convert input to an ndarray with column-major + memory order. + ndarray.flags : Information about the memory layout of the array. + + Notes + ----- + The returned array will be guaranteed to have the listed requirements + by making a copy if needed. + + Examples + -------- + >>> x = np.arange(6).reshape(2,3) + >>> x.flags + C_CONTIGUOUS : True + F_CONTIGUOUS : False + OWNDATA : False + WRITEABLE : True + ALIGNED : True + WRITEBACKIFCOPY : False + UPDATEIFCOPY : False + + >>> y = np.require(x, dtype=np.float32, requirements=['A', 'O', 'W', 'F']) + >>> y.flags + C_CONTIGUOUS : False + F_CONTIGUOUS : True + OWNDATA : True + WRITEABLE : True + ALIGNED : True + WRITEBACKIFCOPY : False + UPDATEIFCOPY : False + + """ + if like is not None: + return _require_with_like( + a, + dtype=dtype, + requirements=requirements, + like=like, + ) + + possible_flags = {'C': 'C', 'C_CONTIGUOUS': 'C', 'CONTIGUOUS': 'C', + 'F': 'F', 'F_CONTIGUOUS': 'F', 'FORTRAN': 'F', + 'A': 'A', 'ALIGNED': 'A', + 'W': 'W', 'WRITEABLE': 'W', + 'O': 'O', 'OWNDATA': 'O', + 'E': 'E', 'ENSUREARRAY': 'E'} + if not requirements: + return asanyarray(a, dtype=dtype) + else: + requirements = {possible_flags[x.upper()] for x in requirements} + + if 'E' in requirements: + requirements.remove('E') + subok = False + else: + subok = True + + order = 'A' + if requirements >= {'C', 'F'}: + raise ValueError('Cannot specify both "C" and "F" order') + elif 'F' in requirements: + order = 'F' + requirements.remove('F') + elif 'C' in requirements: + order = 'C' + requirements.remove('C') + + arr = array(a, dtype=dtype, order=order, copy=False, subok=subok) + + for prop in requirements: + if not arr.flags[prop]: + arr = arr.copy(order) + break + return arr + + +_require_with_like = array_function_dispatch( + _require_dispatcher +)(require) diff --git a/numpy/core/_asarray.pyi b/numpy/core/_asarray.pyi new file mode 100644 index 000000000000..fee9b7b6e0e0 --- /dev/null +++ b/numpy/core/_asarray.pyi @@ -0,0 +1,41 @@ +from typing import TypeVar, Union, Iterable, overload, Literal + +from numpy import ndarray +from numpy.typing import ArrayLike, DTypeLike + +_ArrayType = TypeVar("_ArrayType", bound=ndarray) + +_Requirements = Literal[ + "C", "C_CONTIGUOUS", "CONTIGUOUS", + "F", "F_CONTIGUOUS", "FORTRAN", + "A", "ALIGNED", + "W", "WRITEABLE", + "O", "OWNDATA" +] +_E = Literal["E", "ENSUREARRAY"] +_RequirementsWithE = Union[_Requirements, _E] + +@overload +def require( + a: _ArrayType, + dtype: None = ..., + requirements: Union[None, _Requirements, Iterable[_Requirements]] = ..., + *, + like: ArrayLike = ... +) -> _ArrayType: ... +@overload +def require( + a: object, + dtype: DTypeLike = ..., + requirements: Union[_E, Iterable[_RequirementsWithE]] = ..., + *, + like: ArrayLike = ... +) -> ndarray: ... +@overload +def require( + a: object, + dtype: DTypeLike = ..., + requirements: Union[None, _Requirements, Iterable[_Requirements]] = ..., + *, + like: ArrayLike = ... +) -> ndarray: ... diff --git a/numpy/core/_dtype.py b/numpy/core/_dtype.py new file mode 100644 index 000000000000..c3a22b1c6bb0 --- /dev/null +++ b/numpy/core/_dtype.py @@ -0,0 +1,349 @@ +""" +A place for code to be called from the implementation of np.dtype + +String handling is much easier to do correctly in python. +""" +import numpy as np + + +_kind_to_stem = { + 'u': 'uint', + 'i': 'int', + 'c': 'complex', + 'f': 'float', + 'b': 'bool', + 'V': 'void', + 'O': 'object', + 'M': 'datetime', + 'm': 'timedelta', + 'S': 'bytes', + 'U': 'str', +} + + +def _kind_name(dtype): + try: + return _kind_to_stem[dtype.kind] + except KeyError as e: + raise RuntimeError( + "internal dtype error, unknown kind {!r}" + .format(dtype.kind) + ) from None + + +def __str__(dtype): + if dtype.fields is not None: + return _struct_str(dtype, include_align=True) + elif dtype.subdtype: + return _subarray_str(dtype) + elif issubclass(dtype.type, np.flexible) or not dtype.isnative: + return dtype.str + else: + return dtype.name + + +def __repr__(dtype): + arg_str = _construction_repr(dtype, include_align=False) + if dtype.isalignedstruct: + arg_str = arg_str + ", align=True" + return "dtype({})".format(arg_str) + + +def _unpack_field(dtype, offset, title=None): + """ + Helper function to normalize the items in dtype.fields. + + Call as: + + dtype, offset, title = _unpack_field(*dtype.fields[name]) + """ + return dtype, offset, title + + +def _isunsized(dtype): + # PyDataType_ISUNSIZED + return dtype.itemsize == 0 + + +def _construction_repr(dtype, include_align=False, short=False): + """ + Creates a string repr of the dtype, excluding the 'dtype()' part + surrounding the object. This object may be a string, a list, or + a dict depending on the nature of the dtype. This + is the object passed as the first parameter to the dtype + constructor, and if no additional constructor parameters are + given, will reproduce the exact memory layout. + + Parameters + ---------- + short : bool + If true, this creates a shorter repr using 'kind' and 'itemsize', instead + of the longer type name. + + include_align : bool + If true, this includes the 'align=True' parameter + inside the struct dtype construction dict when needed. Use this flag + if you want a proper repr string without the 'dtype()' part around it. + + If false, this does not preserve the + 'align=True' parameter or sticky NPY_ALIGNED_STRUCT flag for + struct arrays like the regular repr does, because the 'align' + flag is not part of first dtype constructor parameter. This + mode is intended for a full 'repr', where the 'align=True' is + provided as the second parameter. + """ + if dtype.fields is not None: + return _struct_str(dtype, include_align=include_align) + elif dtype.subdtype: + return _subarray_str(dtype) + else: + return _scalar_str(dtype, short=short) + + +def _scalar_str(dtype, short): + byteorder = _byte_order_str(dtype) + + if dtype.type == np.bool_: + if short: + return "'?'" + else: + return "'bool'" + + elif dtype.type == np.object_: + # The object reference may be different sizes on different + # platforms, so it should never include the itemsize here. + return "'O'" + + elif dtype.type == np.string_: + if _isunsized(dtype): + return "'S'" + else: + return "'S%d'" % dtype.itemsize + + elif dtype.type == np.unicode_: + if _isunsized(dtype): + return "'%sU'" % byteorder + else: + return "'%sU%d'" % (byteorder, dtype.itemsize / 4) + + # unlike the other types, subclasses of void are preserved - but + # historically the repr does not actually reveal the subclass + elif issubclass(dtype.type, np.void): + if _isunsized(dtype): + return "'V'" + else: + return "'V%d'" % dtype.itemsize + + elif dtype.type == np.datetime64: + return "'%sM8%s'" % (byteorder, _datetime_metadata_str(dtype)) + + elif dtype.type == np.timedelta64: + return "'%sm8%s'" % (byteorder, _datetime_metadata_str(dtype)) + + elif np.issubdtype(dtype, np.number): + # Short repr with endianness, like '<f8' + if short or dtype.byteorder not in ('=', '|'): + return "'%s%c%d'" % (byteorder, dtype.kind, dtype.itemsize) + + # Longer repr, like 'float64' + else: + return "'%s%d'" % (_kind_name(dtype), 8*dtype.itemsize) + + elif dtype.isbuiltin == 2: + return dtype.type.__name__ + + else: + raise RuntimeError( + "Internal error: NumPy dtype unrecognized type number") + + +def _byte_order_str(dtype): + """ Normalize byteorder to '<' or '>' """ + # hack to obtain the native and swapped byte order characters + swapped = np.dtype(int).newbyteorder('S') + native = swapped.newbyteorder('S') + + byteorder = dtype.byteorder + if byteorder == '=': + return native.byteorder + if byteorder == 'S': + # TODO: this path can never be reached + return swapped.byteorder + elif byteorder == '|': + return '' + else: + return byteorder + + +def _datetime_metadata_str(dtype): + # TODO: this duplicates the C metastr_to_unicode functionality + unit, count = np.datetime_data(dtype) + if unit == 'generic': + return '' + elif count == 1: + return '[{}]'.format(unit) + else: + return '[{}{}]'.format(count, unit) + + +def _struct_dict_str(dtype, includealignedflag): + # unpack the fields dictionary into ls + names = dtype.names + fld_dtypes = [] + offsets = [] + titles = [] + for name in names: + fld_dtype, offset, title = _unpack_field(*dtype.fields[name]) + fld_dtypes.append(fld_dtype) + offsets.append(offset) + titles.append(title) + + # Build up a string to make the dictionary + + if np.core.arrayprint._get_legacy_print_mode() <= 121: + colon = ":" + fieldsep = "," + else: + colon = ": " + fieldsep = ", " + + # First, the names + ret = "{'names'%s[" % colon + ret += fieldsep.join(repr(name) for name in names) + + # Second, the formats + ret += "], 'formats'%s[" % colon + ret += fieldsep.join( + _construction_repr(fld_dtype, short=True) for fld_dtype in fld_dtypes) + + # Third, the offsets + ret += "], 'offsets'%s[" % colon + ret += fieldsep.join("%d" % offset for offset in offsets) + + # Fourth, the titles + if any(title is not None for title in titles): + ret += "], 'titles'%s[" % colon + ret += fieldsep.join(repr(title) for title in titles) + + # Fifth, the itemsize + ret += "], 'itemsize'%s%d" % (colon, dtype.itemsize) + + if (includealignedflag and dtype.isalignedstruct): + # Finally, the aligned flag + ret += ", 'aligned'%sTrue}" % colon + else: + ret += "}" + + return ret + + +def _is_packed(dtype): + """ + Checks whether the structured data type in 'dtype' + has a simple layout, where all the fields are in order, + and follow each other with no alignment padding. + + When this returns true, the dtype can be reconstructed + from a list of the field names and dtypes with no additional + dtype parameters. + + Duplicates the C `is_dtype_struct_simple_unaligned_layout` function. + """ + total_offset = 0 + for name in dtype.names: + fld_dtype, fld_offset, title = _unpack_field(*dtype.fields[name]) + if fld_offset != total_offset: + return False + total_offset += fld_dtype.itemsize + if total_offset != dtype.itemsize: + return False + return True + + +def _struct_list_str(dtype): + items = [] + for name in dtype.names: + fld_dtype, fld_offset, title = _unpack_field(*dtype.fields[name]) + + item = "(" + if title is not None: + item += "({!r}, {!r}), ".format(title, name) + else: + item += "{!r}, ".format(name) + # Special case subarray handling here + if fld_dtype.subdtype is not None: + base, shape = fld_dtype.subdtype + item += "{}, {}".format( + _construction_repr(base, short=True), + shape + ) + else: + item += _construction_repr(fld_dtype, short=True) + + item += ")" + items.append(item) + + return "[" + ", ".join(items) + "]" + + +def _struct_str(dtype, include_align): + # The list str representation can't include the 'align=' flag, + # so if it is requested and the struct has the aligned flag set, + # we must use the dict str instead. + if not (include_align and dtype.isalignedstruct) and _is_packed(dtype): + sub = _struct_list_str(dtype) + + else: + sub = _struct_dict_str(dtype, include_align) + + # If the data type isn't the default, void, show it + if dtype.type != np.void: + return "({t.__module__}.{t.__name__}, {f})".format(t=dtype.type, f=sub) + else: + return sub + + +def _subarray_str(dtype): + base, shape = dtype.subdtype + return "({}, {})".format( + _construction_repr(base, short=True), + shape + ) + + +def _name_includes_bit_suffix(dtype): + if dtype.type == np.object_: + # pointer size varies by system, best to omit it + return False + elif dtype.type == np.bool_: + # implied + return False + elif np.issubdtype(dtype, np.flexible) and _isunsized(dtype): + # unspecified + return False + else: + return True + + +def _name_get(dtype): + # provides dtype.name.__get__, documented as returning a "bit name" + + if dtype.isbuiltin == 2: + # user dtypes don't promise to do anything special + return dtype.type.__name__ + + if issubclass(dtype.type, np.void): + # historically, void subclasses preserve their name, eg `record64` + name = dtype.type.__name__ + else: + name = _kind_name(dtype) + + # append bit counts + if _name_includes_bit_suffix(dtype): + name += "{}".format(dtype.itemsize * 8) + + # append metadata to datetimes + if dtype.type in (np.datetime64, np.timedelta64): + name += _datetime_metadata_str(dtype) + + return name diff --git a/numpy/core/_dtype_ctypes.py b/numpy/core/_dtype_ctypes.py new file mode 100644 index 000000000000..6d7cbb244215 --- /dev/null +++ b/numpy/core/_dtype_ctypes.py @@ -0,0 +1,117 @@ +""" +Conversion from ctypes to dtype. + +In an ideal world, we could achieve this through the PEP3118 buffer protocol, +something like:: + + def dtype_from_ctypes_type(t): + # needed to ensure that the shape of `t` is within memoryview.format + class DummyStruct(ctypes.Structure): + _fields_ = [('a', t)] + + # empty to avoid memory allocation + ctype_0 = (DummyStruct * 0)() + mv = memoryview(ctype_0) + + # convert the struct, and slice back out the field + return _dtype_from_pep3118(mv.format)['a'] + +Unfortunately, this fails because: + +* ctypes cannot handle length-0 arrays with PEP3118 (bpo-32782) +* PEP3118 cannot represent unions, but both numpy and ctypes can +* ctypes cannot handle big-endian structs with PEP3118 (bpo-32780) +""" + +# We delay-import ctypes for distributions that do not include it. +# While this module is not used unless the user passes in ctypes +# members, it is eagerly imported from numpy/core/__init__.py. +import numpy as np + + +def _from_ctypes_array(t): + return np.dtype((dtype_from_ctypes_type(t._type_), (t._length_,))) + + +def _from_ctypes_structure(t): + for item in t._fields_: + if len(item) > 2: + raise TypeError( + "ctypes bitfields have no dtype equivalent") + + if hasattr(t, "_pack_"): + import ctypes + formats = [] + offsets = [] + names = [] + current_offset = 0 + for fname, ftyp in t._fields_: + names.append(fname) + formats.append(dtype_from_ctypes_type(ftyp)) + # Each type has a default offset, this is platform dependent for some types. + effective_pack = min(t._pack_, ctypes.alignment(ftyp)) + current_offset = ((current_offset + effective_pack - 1) // effective_pack) * effective_pack + offsets.append(current_offset) + current_offset += ctypes.sizeof(ftyp) + + return np.dtype(dict( + formats=formats, + offsets=offsets, + names=names, + itemsize=ctypes.sizeof(t))) + else: + fields = [] + for fname, ftyp in t._fields_: + fields.append((fname, dtype_from_ctypes_type(ftyp))) + + # by default, ctypes structs are aligned + return np.dtype(fields, align=True) + + +def _from_ctypes_scalar(t): + """ + Return the dtype type with endianness included if it's the case + """ + if getattr(t, '__ctype_be__', None) is t: + return np.dtype('>' + t._type_) + elif getattr(t, '__ctype_le__', None) is t: + return np.dtype('<' + t._type_) + else: + return np.dtype(t._type_) + + +def _from_ctypes_union(t): + import ctypes + formats = [] + offsets = [] + names = [] + for fname, ftyp in t._fields_: + names.append(fname) + formats.append(dtype_from_ctypes_type(ftyp)) + offsets.append(0) # Union fields are offset to 0 + + return np.dtype(dict( + formats=formats, + offsets=offsets, + names=names, + itemsize=ctypes.sizeof(t))) + + +def dtype_from_ctypes_type(t): + """ + Construct a dtype object from a ctypes type + """ + import _ctypes + if issubclass(t, _ctypes.Array): + return _from_ctypes_array(t) + elif issubclass(t, _ctypes._Pointer): + raise TypeError("ctypes pointers have no dtype equivalent") + elif issubclass(t, _ctypes.Structure): + return _from_ctypes_structure(t) + elif issubclass(t, _ctypes.Union): + return _from_ctypes_union(t) + elif isinstance(getattr(t, '_type_', None), str): + return _from_ctypes_scalar(t) + else: + raise NotImplementedError( + "Unknown ctypes type {}".format(t.__name__)) diff --git a/numpy/core/_exceptions.py b/numpy/core/_exceptions.py new file mode 100644 index 000000000000..3cd8042ce180 --- /dev/null +++ b/numpy/core/_exceptions.py @@ -0,0 +1,271 @@ +""" +Various richly-typed exceptions, that also help us deal with string formatting +in python where it's easier. + +By putting the formatting in `__str__`, we also avoid paying the cost for +users who silence the exceptions. +""" +from numpy.core.overrides import set_module + +def _unpack_tuple(tup): + if len(tup) == 1: + return tup[0] + else: + return tup + + +def _display_as_base(cls): + """ + A decorator that makes an exception class look like its base. + + We use this to hide subclasses that are implementation details - the user + should catch the base type, which is what the traceback will show them. + + Classes decorated with this decorator are subject to removal without a + deprecation warning. + """ + assert issubclass(cls, Exception) + cls.__name__ = cls.__base__.__name__ + return cls + + +class UFuncTypeError(TypeError): + """ Base class for all ufunc exceptions """ + def __init__(self, ufunc): + self.ufunc = ufunc + + +@_display_as_base +class _UFuncBinaryResolutionError(UFuncTypeError): + """ Thrown when a binary resolution fails """ + def __init__(self, ufunc, dtypes): + super().__init__(ufunc) + self.dtypes = tuple(dtypes) + assert len(self.dtypes) == 2 + + def __str__(self): + return ( + "ufunc {!r} cannot use operands with types {!r} and {!r}" + ).format( + self.ufunc.__name__, *self.dtypes + ) + + +@_display_as_base +class _UFuncNoLoopError(UFuncTypeError): + """ Thrown when a ufunc loop cannot be found """ + def __init__(self, ufunc, dtypes): + super().__init__(ufunc) + self.dtypes = tuple(dtypes) + + def __str__(self): + return ( + "ufunc {!r} did not contain a loop with signature matching types " + "{!r} -> {!r}" + ).format( + self.ufunc.__name__, + _unpack_tuple(self.dtypes[:self.ufunc.nin]), + _unpack_tuple(self.dtypes[self.ufunc.nin:]) + ) + + +@_display_as_base +class _UFuncCastingError(UFuncTypeError): + def __init__(self, ufunc, casting, from_, to): + super().__init__(ufunc) + self.casting = casting + self.from_ = from_ + self.to = to + + +@_display_as_base +class _UFuncInputCastingError(_UFuncCastingError): + """ Thrown when a ufunc input cannot be casted """ + def __init__(self, ufunc, casting, from_, to, i): + super().__init__(ufunc, casting, from_, to) + self.in_i = i + + def __str__(self): + # only show the number if more than one input exists + i_str = "{} ".format(self.in_i) if self.ufunc.nin != 1 else "" + return ( + "Cannot cast ufunc {!r} input {}from {!r} to {!r} with casting " + "rule {!r}" + ).format( + self.ufunc.__name__, i_str, self.from_, self.to, self.casting + ) + + +@_display_as_base +class _UFuncOutputCastingError(_UFuncCastingError): + """ Thrown when a ufunc output cannot be casted """ + def __init__(self, ufunc, casting, from_, to, i): + super().__init__(ufunc, casting, from_, to) + self.out_i = i + + def __str__(self): + # only show the number if more than one output exists + i_str = "{} ".format(self.out_i) if self.ufunc.nout != 1 else "" + return ( + "Cannot cast ufunc {!r} output {}from {!r} to {!r} with casting " + "rule {!r}" + ).format( + self.ufunc.__name__, i_str, self.from_, self.to, self.casting + ) + + +# Exception used in shares_memory() +@set_module('numpy') +class TooHardError(RuntimeError): + pass + + +@set_module('numpy') +class AxisError(ValueError, IndexError): + """Axis supplied was invalid. + + This is raised whenever an ``axis`` parameter is specified that is larger + than the number of array dimensions. + For compatibility with code written against older numpy versions, which + raised a mixture of `ValueError` and `IndexError` for this situation, this + exception subclasses both to ensure that ``except ValueError`` and + ``except IndexError`` statements continue to catch `AxisError`. + + .. versionadded:: 1.13 + + Parameters + ---------- + axis : int or str + The out of bounds axis or a custom exception message. + If an axis is provided, then `ndim` should be specified as well. + ndim : int, optional + The number of array dimensions. + msg_prefix : str, optional + A prefix for the exception message. + + Attributes + ---------- + axis : int, optional + The out of bounds axis or ``None`` if a custom exception + message was provided. This should be the axis as passed by + the user, before any normalization to resolve negative indices. + + .. versionadded:: 1.22 + ndim : int, optional + The number of array dimensions or ``None`` if a custom exception + message was provided. + + .. versionadded:: 1.22 + + + Examples + -------- + >>> array_1d = np.arange(10) + >>> np.cumsum(array_1d, axis=1) + Traceback (most recent call last): + ... + numpy.AxisError: axis 1 is out of bounds for array of dimension 1 + + Negative axes are preserved: + + >>> np.cumsum(array_1d, axis=-2) + Traceback (most recent call last): + ... + numpy.AxisError: axis -2 is out of bounds for array of dimension 1 + + The class constructor generally takes the axis and arrays' + dimensionality as arguments: + + >>> print(np.AxisError(2, 1, msg_prefix='error')) + error: axis 2 is out of bounds for array of dimension 1 + + Alternatively, a custom exception message can be passed: + + >>> print(np.AxisError('Custom error message')) + Custom error message + + """ + + __slots__ = ("axis", "ndim", "_msg") + + def __init__(self, axis, ndim=None, msg_prefix=None): + if ndim is msg_prefix is None: + # single-argument form: directly set the error message + self._msg = axis + self.axis = None + self.ndim = None + else: + self._msg = msg_prefix + self.axis = axis + self.ndim = ndim + + def __str__(self): + axis = self.axis + ndim = self.ndim + + if axis is ndim is None: + return self._msg + else: + msg = f"axis {axis} is out of bounds for array of dimension {ndim}" + if self._msg is not None: + msg = f"{self._msg}: {msg}" + return msg + + +@_display_as_base +class _ArrayMemoryError(MemoryError): + """ Thrown when an array cannot be allocated""" + def __init__(self, shape, dtype): + self.shape = shape + self.dtype = dtype + + @property + def _total_size(self): + num_bytes = self.dtype.itemsize + for dim in self.shape: + num_bytes *= dim + return num_bytes + + @staticmethod + def _size_to_string(num_bytes): + """ Convert a number of bytes into a binary size string """ + + # https://en.wikipedia.org/wiki/Binary_prefix + LOG2_STEP = 10 + STEP = 1024 + units = ['bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB'] + + unit_i = max(num_bytes.bit_length() - 1, 1) // LOG2_STEP + unit_val = 1 << (unit_i * LOG2_STEP) + n_units = num_bytes / unit_val + del unit_val + + # ensure we pick a unit that is correct after rounding + if round(n_units) == STEP: + unit_i += 1 + n_units /= STEP + + # deal with sizes so large that we don't have units for them + if unit_i >= len(units): + new_unit_i = len(units) - 1 + n_units *= 1 << ((unit_i - new_unit_i) * LOG2_STEP) + unit_i = new_unit_i + + unit_name = units[unit_i] + # format with a sensible number of digits + if unit_i == 0: + # no decimal point on bytes + return '{:.0f} {}'.format(n_units, unit_name) + elif round(n_units) < 1000: + # 3 significant figures, if none are dropped to the left of the . + return '{:#.3g} {}'.format(n_units, unit_name) + else: + # just give all the digits otherwise + return '{:#.0f} {}'.format(n_units, unit_name) + + def __str__(self): + size_str = self._size_to_string(self._total_size) + return ( + "Unable to allocate {} for an array with shape {} and data type {}" + .format(size_str, self.shape, self.dtype) + ) diff --git a/numpy/core/_internal.py b/numpy/core/_internal.py index 9990bacf0723..8942955f60c6 100644 --- a/numpy/core/_internal.py +++ b/numpy/core/_internal.py @@ -1,42 +1,42 @@ """ -A place for code to be called from core C-code. +A place for internal code Some things are more easily handled Python. """ -from __future__ import division, absolute_import, print_function - +import ast import re import sys +import platform +import warnings -from numpy.compat import basestring from .multiarray import dtype, array, ndarray try: import ctypes except ImportError: ctypes = None -from .numerictypes import object_ -if (sys.byteorder == 'little'): - _nbo = b'<' +IS_PYPY = platform.python_implementation() == 'PyPy' + +if sys.byteorder == 'little': + _nbo = '<' else: - _nbo = b'>' + _nbo = '>' def _makenames_list(adict, align): allfields = [] - fnames = list(adict.keys()) - for fname in fnames: - obj = adict[fname] + + for fname, obj in adict.items(): n = len(obj) - if not isinstance(obj, tuple) or n not in [2, 3]: + if not isinstance(obj, tuple) or n not in (2, 3): raise ValueError("entry not a 2- or 3- tuple") - if (n > 2) and (obj[2] == fname): + if n > 2 and obj[2] == fname: continue num = int(obj[1]) - if (num < 0): + if num < 0: raise ValueError("invalid offset.") format = dtype(obj[0], align=align) - if (n > 2): + if n > 2: title = obj[2] else: title = None @@ -68,7 +68,7 @@ def _usefields(adict, align): res = adict[name] formats.append(res[0]) offsets.append(res[1]) - if (len(res) > 2): + if len(res) > 2: titles.append(res[2]) else: titles.append(None) @@ -108,7 +108,7 @@ def _array_descr(descriptor): for field in ordered_fields: if field[1] > offset: num = field[1] - offset - result.append(('', '|V%d' % num)) + result.append(('', f'|V{num}')) offset += num elif field[1] < offset: raise ValueError( @@ -128,7 +128,7 @@ def _array_descr(descriptor): if descriptor.itemsize > offset: num = descriptor.itemsize - offset - result.append(('', '|V%d' % num)) + result.append(('', f'|V{num}')) return result @@ -143,16 +143,16 @@ def _reconstruct(subtype, shape, dtype): # format_re was originally from numarray by J. Todd Miller -format_re = re.compile(br'(?P<order1>[<>|=]?)' - br'(?P<repeats> *[(]?[ ,0-9L]*[)]? *)' - br'(?P<order2>[<>|=]?)' - br'(?P<dtype>[A-Za-z0-9.?]*(?:\[[a-zA-Z0-9,.]+\])?)') -sep_re = re.compile(br'\s*,\s*') -space_re = re.compile(br'\s+$') +format_re = re.compile(r'(?P<order1>[<>|=]?)' + r'(?P<repeats> *[(]?[ ,0-9]*[)]? *)' + r'(?P<order2>[<>|=]?)' + r'(?P<dtype>[A-Za-z0-9.?]*(?:\[[a-zA-Z0-9,.]+\])?)') +sep_re = re.compile(r'\s*,\s*') +space_re = re.compile(r'\s+$') # astr is a string (perhaps comma separated) -_convorder = {b'=': _nbo} +_convorder = {'=': _nbo} def _commastring(astr): startindex = 0 @@ -162,8 +162,9 @@ def _commastring(astr): try: (order1, repeats, order2, dtype) = mo.groups() except (TypeError, AttributeError): - raise ValueError('format number %d of "%s" is not recognized' % - (len(result)+1, astr)) + raise ValueError( + f'format number {len(result)+1} of "{astr}" is not recognized' + ) from None startindex = mo.end() # Separator or ending padding if startindex < len(astr): @@ -177,9 +178,9 @@ def _commastring(astr): (len(result)+1, astr)) startindex = mo.end() - if order2 == b'': + if order2 == '': order = order1 - elif order1 == b'': + elif order1 == '': order = order2 else: order1 = _convorder.get(order1, order1) @@ -190,18 +191,18 @@ def _commastring(astr): (order1, order2)) order = order1 - if order in [b'|', b'=', _nbo]: - order = b'' + if order in ('|', '=', _nbo): + order = '' dtype = order + dtype - if (repeats == b''): + if (repeats == ''): newitem = dtype else: - newitem = (dtype, eval(repeats)) + newitem = (dtype, ast.literal_eval(repeats)) result.append(newitem) return result -class dummy_ctype(object): +class dummy_ctype: def __init__(self, cls): self._cls = cls def __mul__(self, other): @@ -222,7 +223,7 @@ def _getintp_ctype(): val = dummy_ctype(np.intp) else: char = dtype('p').char - if (char == 'i'): + if char == 'i': val = ctypes.c_int elif char == 'l': val = ctypes.c_long @@ -236,55 +237,159 @@ def _getintp_ctype(): # Used for .ctypes attribute of ndarray -class _missing_ctypes(object): +class _missing_ctypes: def cast(self, num, obj): - return num + return num.value + + class c_void_p: + def __init__(self, ptr): + self.value = ptr - def c_void_p(self, num): - return num -class _ctypes(object): +class _ctypes: def __init__(self, array, ptr=None): + self._arr = array + if ctypes: self._ctypes = ctypes + self._data = self._ctypes.c_void_p(ptr) else: + # fake a pointer-like object that holds onto the reference self._ctypes = _missing_ctypes() - self._arr = array - self._data = ptr + self._data = self._ctypes.c_void_p(ptr) + self._data._objects = array + if self._arr.ndim == 0: self._zerod = True else: self._zerod = False def data_as(self, obj): - return self._ctypes.cast(self._data, obj) + """ + Return the data pointer cast to a particular c-types object. + For example, calling ``self._as_parameter_`` is equivalent to + ``self.data_as(ctypes.c_void_p)``. Perhaps you want to use the data as a + pointer to a ctypes array of floating-point data: + ``self.data_as(ctypes.POINTER(ctypes.c_double))``. + + The returned pointer will keep a reference to the array. + """ + # _ctypes.cast function causes a circular reference of self._data in + # self._data._objects. Attributes of self._data cannot be released + # until gc.collect is called. Make a copy of the pointer first then let + # it hold the array reference. This is a workaround to circumvent the + # CPython bug https://bugs.python.org/issue12836 + ptr = self._ctypes.cast(self._data, obj) + ptr._arr = self._arr + return ptr def shape_as(self, obj): + """ + Return the shape tuple as an array of some other c-types + type. For example: ``self.shape_as(ctypes.c_short)``. + """ if self._zerod: return None return (obj*self._arr.ndim)(*self._arr.shape) def strides_as(self, obj): + """ + Return the strides tuple as an array of some other + c-types type. For example: ``self.strides_as(ctypes.c_longlong)``. + """ if self._zerod: return None return (obj*self._arr.ndim)(*self._arr.strides) + @property + def data(self): + """ + A pointer to the memory area of the array as a Python integer. + This memory area may contain data that is not aligned, or not in correct + byte-order. The memory area may not even be writeable. The array + flags and data-type of this array should be respected when passing this + attribute to arbitrary C-code to avoid trouble that can include Python + crashing. User Beware! The value of this attribute is exactly the same + as ``self._array_interface_['data'][0]``. + + Note that unlike ``data_as``, a reference will not be kept to the array: + code like ``ctypes.c_void_p((a + b).ctypes.data)`` will result in a + pointer to a deallocated array, and should be spelt + ``(a + b).ctypes.data_as(ctypes.c_void_p)`` + """ + return self._data.value + + @property + def shape(self): + """ + (c_intp*self.ndim): A ctypes array of length self.ndim where + the basetype is the C-integer corresponding to ``dtype('p')`` on this + platform (see `~numpy.ctypeslib.c_intp`). This base-type could be + `ctypes.c_int`, `ctypes.c_long`, or `ctypes.c_longlong` depending on + the platform. The ctypes array contains the shape of + the underlying array. + """ + return self.shape_as(_getintp_ctype()) + + @property + def strides(self): + """ + (c_intp*self.ndim): A ctypes array of length self.ndim where + the basetype is the same as for the shape attribute. This ctypes array + contains the strides information from the underlying array. This strides + information is important for showing how many bytes must be jumped to + get to the next element in the array. + """ + return self.strides_as(_getintp_ctype()) + + @property + def _as_parameter_(self): + """ + Overrides the ctypes semi-magic method + + Enables `c_func(some_array.ctypes)` + """ + return self.data_as(ctypes.c_void_p) + + # Numpy 1.21.0, 2021-05-18 + def get_data(self): - return self._data + """Deprecated getter for the `_ctypes.data` property. + + .. deprecated:: 1.21 + """ + warnings.warn('"get_data" is deprecated. Use "data" instead', + DeprecationWarning, stacklevel=2) + return self.data def get_shape(self): - return self.shape_as(_getintp_ctype()) + """Deprecated getter for the `_ctypes.shape` property. + + .. deprecated:: 1.21 + """ + warnings.warn('"get_shape" is deprecated. Use "shape" instead', + DeprecationWarning, stacklevel=2) + return self.shape def get_strides(self): - return self.strides_as(_getintp_ctype()) + """Deprecated getter for the `_ctypes.strides` property. + + .. deprecated:: 1.21 + """ + warnings.warn('"get_strides" is deprecated. Use "strides" instead', + DeprecationWarning, stacklevel=2) + return self.strides def get_as_parameter(self): - return self._ctypes.c_void_p(self._data) + """Deprecated getter for the `_ctypes._as_parameter_` property. - data = property(get_data, None, doc="c-types data") - shape = property(get_shape, None, doc="c-types shape") - strides = property(get_strides, None, doc="c-types strides") - _as_parameter_ = property(get_as_parameter, None, doc="_as parameter_") + .. deprecated:: 1.21 + """ + warnings.warn( + '"get_as_parameter" is deprecated. Use "_as_parameter_" instead', + DeprecationWarning, stacklevel=2, + ) + return self._as_parameter_ def _newnames(datatype, order): @@ -303,12 +408,12 @@ def _newnames(datatype, order): nameslist.remove(name) except ValueError: if name in seen: - raise ValueError("duplicate field name: %s" % (name,)) + raise ValueError(f"duplicate field name: {name}") from None else: - raise ValueError("unknown field name: %s" % (name,)) + raise ValueError(f"unknown field name: {name}") from None seen.add(name) return tuple(list(order) + nameslist) - raise ValueError("unsupported order value: %s" % (order,)) + raise ValueError(f"unsupported order value: {order}") def _copy_fields(ary): """Return copy of structured array with padding between fields removed. @@ -352,7 +457,7 @@ def _getfield_is_safe(oldtype, newtype, offset): if newtype.hasobject or oldtype.hasobject: if offset == 0 and newtype == oldtype: return - if oldtype.names: + if oldtype.names is not None: for name in oldtype.names: if (oldtype.fields[name][1] == offset and oldtype.fields[name][0] == newtype): @@ -444,46 +549,51 @@ def _view_is_safe(oldtype, newtype): } _pep3118_standard_typechars = ''.join(_pep3118_standard_map.keys()) -def _dtype_from_pep3118(spec): +_pep3118_unsupported_map = { + 'u': 'UCS-2 strings', + '&': 'pointers', + 't': 'bitfields', + 'X': 'function pointers', +} - class Stream(object): - def __init__(self, s): - self.s = s - self.byteorder = '@' +class _Stream: + def __init__(self, s): + self.s = s + self.byteorder = '@' - def advance(self, n): - res = self.s[:n] - self.s = self.s[n:] - return res + def advance(self, n): + res = self.s[:n] + self.s = self.s[n:] + return res - def consume(self, c): - if self.s[:len(c)] == c: - self.advance(len(c)) - return True - return False - - def consume_until(self, c): - if callable(c): - i = 0 - while i < len(self.s) and not c(self.s[i]): - i = i + 1 - return self.advance(i) - else: - i = self.s.index(c) - res = self.advance(i) - self.advance(len(c)) - return res + def consume(self, c): + if self.s[:len(c)] == c: + self.advance(len(c)) + return True + return False - @property - def next(self): - return self.s[0] + def consume_until(self, c): + if callable(c): + i = 0 + while i < len(self.s) and not c(self.s[i]): + i = i + 1 + return self.advance(i) + else: + i = self.s.index(c) + res = self.advance(i) + self.advance(len(c)) + return res + + @property + def next(self): + return self.s[0] - def __bool__(self): - return bool(self.s) - __nonzero__ = __bool__ + def __bool__(self): + return bool(self.s) - stream = Stream(spec) +def _dtype_from_pep3118(spec): + stream = _Stream(spec) dtype, align = __dtype_from_pep3118(stream, is_subdtype=False) return dtype @@ -555,6 +665,11 @@ def __dtype_from_pep3118(stream, is_subdtype): stream.byteorder, stream.byteorder) value = dtype(numpy_byteorder + dtypechar) align = value.alignment + elif stream.next in _pep3118_unsupported_map: + desc = _pep3118_unsupported_map[stream.next] + raise NotImplementedError( + "Unrepresentable PEP 3118 data type {!r} ({})" + .format(stream.next, desc)) else: raise ValueError("Unknown PEP 3118 data type specifier %r" % stream.s) @@ -599,8 +714,7 @@ def __dtype_from_pep3118(stream, is_subdtype): if not (is_padding and name is None): if name is not None and name in field_spec['names']: - raise RuntimeError("Duplicate field name '%s' in PEP3118 format" - % name) + raise RuntimeError(f"Duplicate field name '{name}' in PEP3118 format") field_spec['names'].append(name) field_spec['formats'].append(value) field_spec['offsets'].append(offset) @@ -636,7 +750,7 @@ def _fix_names(field_spec): j = 0 while True: - name = 'f{}'.format(j) + name = f'f{j}' if name not in names: break j = j + 1 @@ -679,27 +793,6 @@ def _gcd(a, b): def _lcm(a, b): return a // _gcd(a, b) * b -# Exception used in shares_memory() -class TooHardError(RuntimeError): - pass - -class AxisError(ValueError, IndexError): - """ Axis supplied was invalid. """ - def __init__(self, axis, ndim=None, msg_prefix=None): - # single-argument form just delegates to base class - if ndim is None and msg_prefix is None: - msg = axis - - # do the string formatting here, to save work in the C code - else: - msg = ("axis {} is out of bounds for array of dimension {}" - .format(axis, ndim)) - if msg_prefix is not None: - msg = "{}: {}".format(msg_prefix, msg) - - super(AxisError, self).__init__(msg) - - def array_ufunc_errmsg_formatter(dummy, ufunc, method, *inputs, **kwargs): """ Format the error message for when __array_ufunc__ gives up. """ args_string = ', '.join(['{!r}'.format(arg) for arg in inputs] + @@ -712,6 +805,13 @@ def array_ufunc_errmsg_formatter(dummy, ufunc, method, *inputs, **kwargs): .format(ufunc, method, args_string, types_string)) +def array_function_errmsg_formatter(public_api, types): + """ Format the error message for when __array_ufunc__ gives up. """ + func_name = '{}.{}'.format(public_api.__module__, public_api.__name__) + return ("no implementation found for '{}' on types that implement " + '__array_function__: {}'.format(func_name, list(types))) + + def _ufunc_doc_signature_formatter(ufunc): """ Builds a signature string which resembles PEP 457 @@ -723,7 +823,7 @@ def _ufunc_doc_signature_formatter(ufunc): if ufunc.nin == 1: in_args = 'x' else: - in_args = ', '.join('x{}'.format(i+1) for i in range(ufunc.nin)) + in_args = ', '.join(f'x{i+1}' for i in range(ufunc.nin)) # output arguments are both keyword or positional if ufunc.nout == 0: @@ -743,11 +843,13 @@ def _ufunc_doc_signature_formatter(ufunc): ", order='K'" ", dtype=None" ", subok=True" - "[, signature" - ", extobj]" ) + + # NOTE: gufuncs may or may not support the `axis` parameter if ufunc.signature is None: - kwargs = ", where=True" + kwargs + kwargs = f", where=True{kwargs}[, signature, extobj]" + else: + kwargs += "[, signature, extobj, axes, axis]" # join all the parts together return '{name}({in_args}{out_args}, *{kwargs})'.format( @@ -758,14 +860,19 @@ def _ufunc_doc_signature_formatter(ufunc): ) -def _is_from_ctypes(obj): - # determine if an object comes from ctypes, in order to work around +def npy_ctypes_check(cls): + # determine if a class comes from ctypes, in order to work around # a bug in the buffer protocol for those objects, bpo-10746 try: # ctypes class are new-style, so have an __mro__. This probably fails # for ctypes classes with multiple inheritance. - ctype_base = type(obj).__mro__[-2] + if IS_PYPY: + # (..., _ctypes.basics._CData, Bufferable, object) + ctype_base = cls.__mro__[-3] + else: + # # (..., _ctypes._CData, object) + ctype_base = cls.__mro__[-2] # right now, they're part of the _ctypes module - return 'ctypes' in ctype_base.__module__ + return '_ctypes' in ctype_base.__module__ except Exception: return False diff --git a/numpy/core/_internal.pyi b/numpy/core/_internal.pyi new file mode 100644 index 000000000000..f4bfd770f0f2 --- /dev/null +++ b/numpy/core/_internal.pyi @@ -0,0 +1,30 @@ +from typing import Any, TypeVar, Type, overload, Optional, Generic +import ctypes as ct + +from numpy import ndarray +from numpy.ctypeslib import c_intp + +_CastT = TypeVar("_CastT", bound=ct._CanCastTo) # Copied from `ctypes.cast` +_CT = TypeVar("_CT", bound=ct._CData) +_PT = TypeVar("_PT", bound=Optional[int]) + +# TODO: Let the likes of `shape_as` and `strides_as` return `None` +# for 0D arrays once we've got shape-support + +class _ctypes(Generic[_PT]): + @overload + def __new__(cls, array: ndarray[Any, Any], ptr: None = ...) -> _ctypes[None]: ... + @overload + def __new__(cls, array: ndarray[Any, Any], ptr: _PT) -> _ctypes[_PT]: ... + @property + def data(self) -> _PT: ... + @property + def shape(self) -> ct.Array[c_intp]: ... + @property + def strides(self) -> ct.Array[c_intp]: ... + @property + def _as_parameter_(self) -> ct.c_void_p: ... + + def data_as(self, obj: Type[_CastT]) -> _CastT: ... + def shape_as(self, obj: Type[_CT]) -> ct.Array[_CT]: ... + def strides_as(self, obj: Type[_CT]) -> ct.Array[_CT]: ... diff --git a/numpy/core/machar.py b/numpy/core/_machar.py similarity index 91% rename from numpy/core/machar.py rename to numpy/core/_machar.py index 7578544fe729..ace19a429f79 100644 --- a/numpy/core/machar.py +++ b/numpy/core/_machar.py @@ -1,20 +1,21 @@ """ -Machine arithmetics - determine the parameters of the +Machine arithmetic - determine the parameters of the floating-point arithmetic system Author: Pearu Peterson, September 2003 """ -from __future__ import division, absolute_import, print_function - __all__ = ['MachAr'] from numpy.core.fromnumeric import any -from numpy.core.numeric import errstate +from numpy.core._ufunc_config import errstate +from numpy.core.overrides import set_module # Need to speed this up...especially for longfloat -class MachAr(object): +# Deprecated 2021-10-20, NumPy 1.22 +@set_module('numpy') +class MachAr: """ Diagnosing machine parameters. @@ -40,8 +41,8 @@ class MachAr(object): Smallest (most negative) power of `ibeta` consistent with there being no leading zeros in the mantissa. xmin : float - Floating point number ``beta**minexp`` (the smallest [in - magnitude] usable floating value). + Floating-point number ``beta**minexp`` (the smallest [in + magnitude] positive floating point number with full precision). maxexp : int Smallest (positive) power of `ibeta` that causes overflow. xmax : float @@ -56,13 +57,19 @@ class MachAr(object): epsilon : float Same as `eps`. tiny : float - Same as `xmin`. + An alias for `smallest_normal`, kept for backwards compatibility. huge : float Same as `xmax`. precision : float ``- int(-log10(eps))`` resolution : float ``- 10**(-precision)`` + smallest_normal : float + The smallest positive floating point number with 1 as leading bit in + the mantissa following IEEE-754. Same as `xmin`. + smallest_subnormal : float + The smallest positive floating point number with 0 as leading bit in + the mantissa following IEEE-754. Parameters ---------- @@ -293,6 +300,8 @@ def _do_init(self, float_conv, int_conv, float_to_float, float_to_str, title): else: xmax = xmax * beta + smallest_subnormal = abs(xmin / beta ** (it)) + self.ibeta = ibeta self.it = it self.negep = negep @@ -316,6 +325,8 @@ def _do_init(self, float_conv, int_conv, float_to_float, float_to_str, title): self.epsilon = self.eps self.tiny = self.xmin self.huge = self.xmax + self.smallest_normal = self.xmin + self.smallest_subnormal = float_to_float(smallest_subnormal) import math self.precision = int(-math.log10(float_to_float(self.eps))) @@ -333,6 +344,8 @@ def __str__(self): 'negep =%(negep)s epsneg=%(_str_epsneg)s (beta**epsneg)\n' 'minexp=%(minexp)s xmin=%(_str_xmin)s (beta**minexp == tiny)\n' 'maxexp=%(maxexp)s xmax=%(_str_xmax)s ((1-epsneg)*beta**maxexp == huge)\n' + 'smallest_normal=%(smallest_normal)s ' + 'smallest_subnormal=%(smallest_subnormal)s\n' '---------------------------------------------------------------------\n' ) return fmt % self.__dict__ diff --git a/numpy/core/_methods.py b/numpy/core/_methods.py index 33f6d01a89c3..a239e2c87eb7 100644 --- a/numpy/core/_methods.py +++ b/numpy/core/_methods.py @@ -3,15 +3,16 @@ and the Python code for the NumPy-namespace function """ -from __future__ import division, absolute_import, print_function - import warnings +from contextlib import nullcontext from numpy.core import multiarray as mu from numpy.core import umath as um -from numpy.core.numeric import asanyarray +from numpy.core.multiarray import asanyarray from numpy.core import numerictypes as nt +from numpy.core import _exceptions from numpy._globals import _NoValue +from numpy.compat import pickle, os_fspath # save those O(100) nanoseconds! umr_maximum = um.maximum.reduce @@ -21,47 +22,150 @@ umr_any = um.logical_or.reduce umr_all = um.logical_and.reduce +# Complex types to -> (2,)float view for fast-path computation in _var() +_complex_to_float = { + nt.dtype(nt.csingle) : nt.dtype(nt.single), + nt.dtype(nt.cdouble) : nt.dtype(nt.double), +} +# Special case for windows: ensure double takes precedence +if nt.dtype(nt.longdouble) != nt.dtype(nt.double): + _complex_to_float.update({ + nt.dtype(nt.clongdouble) : nt.dtype(nt.longdouble), + }) + # avoid keyword arguments to speed up parsing, saves about 15%-20% for very # small reductions def _amax(a, axis=None, out=None, keepdims=False, - initial=_NoValue): - return umr_maximum(a, axis, None, out, keepdims, initial) + initial=_NoValue, where=True): + return umr_maximum(a, axis, None, out, keepdims, initial, where) def _amin(a, axis=None, out=None, keepdims=False, - initial=_NoValue): - return umr_minimum(a, axis, None, out, keepdims, initial) + initial=_NoValue, where=True): + return umr_minimum(a, axis, None, out, keepdims, initial, where) def _sum(a, axis=None, dtype=None, out=None, keepdims=False, - initial=_NoValue): - return umr_sum(a, axis, dtype, out, keepdims, initial) + initial=_NoValue, where=True): + return umr_sum(a, axis, dtype, out, keepdims, initial, where) def _prod(a, axis=None, dtype=None, out=None, keepdims=False, - initial=_NoValue): - return umr_prod(a, axis, dtype, out, keepdims, initial) - -def _any(a, axis=None, dtype=None, out=None, keepdims=False): - return umr_any(a, axis, dtype, out, keepdims) - -def _all(a, axis=None, dtype=None, out=None, keepdims=False): - return umr_all(a, axis, dtype, out, keepdims) - -def _count_reduce_items(arr, axis): - if axis is None: - axis = tuple(range(arr.ndim)) - if not isinstance(axis, tuple): - axis = (axis,) - items = 1 - for ax in axis: - items *= arr.shape[ax] + initial=_NoValue, where=True): + return umr_prod(a, axis, dtype, out, keepdims, initial, where) + +def _any(a, axis=None, dtype=None, out=None, keepdims=False, *, where=True): + # Parsing keyword arguments is currently fairly slow, so avoid it for now + if where is True: + return umr_any(a, axis, dtype, out, keepdims) + return umr_any(a, axis, dtype, out, keepdims, where=where) + +def _all(a, axis=None, dtype=None, out=None, keepdims=False, *, where=True): + # Parsing keyword arguments is currently fairly slow, so avoid it for now + if where is True: + return umr_all(a, axis, dtype, out, keepdims) + return umr_all(a, axis, dtype, out, keepdims, where=where) + +def _count_reduce_items(arr, axis, keepdims=False, where=True): + # fast-path for the default case + if where is True: + # no boolean mask given, calculate items according to axis + if axis is None: + axis = tuple(range(arr.ndim)) + elif not isinstance(axis, tuple): + axis = (axis,) + items = nt.intp(1) + for ax in axis: + items *= arr.shape[mu.normalize_axis_index(ax, arr.ndim)] + else: + # TODO: Optimize case when `where` is broadcast along a non-reduction + # axis and full sum is more excessive than needed. + + # guarded to protect circular imports + from numpy.lib.stride_tricks import broadcast_to + # count True values in (potentially broadcasted) boolean mask + items = umr_sum(broadcast_to(where, arr.shape), axis, nt.intp, None, + keepdims) return items -def _mean(a, axis=None, dtype=None, out=None, keepdims=False): +# Numpy 1.17.0, 2019-02-24 +# Various clip behavior deprecations, marked with _clip_dep as a prefix. + +def _clip_dep_is_scalar_nan(a): + # guarded to protect circular imports + from numpy.core.fromnumeric import ndim + if ndim(a) != 0: + return False + try: + return um.isnan(a) + except TypeError: + return False + +def _clip_dep_is_byte_swapped(a): + if isinstance(a, mu.ndarray): + return not a.dtype.isnative + return False + +def _clip_dep_invoke_with_casting(ufunc, *args, out=None, casting=None, **kwargs): + # normal path + if casting is not None: + return ufunc(*args, out=out, casting=casting, **kwargs) + + # try to deal with broken casting rules + try: + return ufunc(*args, out=out, **kwargs) + except _exceptions._UFuncOutputCastingError as e: + # Numpy 1.17.0, 2019-02-24 + warnings.warn( + "Converting the output of clip from {!r} to {!r} is deprecated. " + "Pass `casting=\"unsafe\"` explicitly to silence this warning, or " + "correct the type of the variables.".format(e.from_, e.to), + DeprecationWarning, + stacklevel=2 + ) + return ufunc(*args, out=out, casting="unsafe", **kwargs) + +def _clip(a, min=None, max=None, out=None, *, casting=None, **kwargs): + if min is None and max is None: + raise ValueError("One of max or min must be given") + + # Numpy 1.17.0, 2019-02-24 + # This deprecation probably incurs a substantial slowdown for small arrays, + # it will be good to get rid of it. + if not _clip_dep_is_byte_swapped(a) and not _clip_dep_is_byte_swapped(out): + using_deprecated_nan = False + if _clip_dep_is_scalar_nan(min): + min = -float('inf') + using_deprecated_nan = True + if _clip_dep_is_scalar_nan(max): + max = float('inf') + using_deprecated_nan = True + if using_deprecated_nan: + warnings.warn( + "Passing `np.nan` to mean no clipping in np.clip has always " + "been unreliable, and is now deprecated. " + "In future, this will always return nan, like it already does " + "when min or max are arrays that contain nan. " + "To skip a bound, pass either None or an np.inf of an " + "appropriate sign.", + DeprecationWarning, + stacklevel=2 + ) + + if min is None: + return _clip_dep_invoke_with_casting( + um.minimum, a, max, out=out, casting=casting, **kwargs) + elif max is None: + return _clip_dep_invoke_with_casting( + um.maximum, a, min, out=out, casting=casting, **kwargs) + else: + return _clip_dep_invoke_with_casting( + um.clip, a, min, max, out=out, casting=casting, **kwargs) + +def _mean(a, axis=None, dtype=None, out=None, keepdims=False, *, where=True): arr = asanyarray(a) is_float16_result = False - rcount = _count_reduce_items(arr, axis) - # Make this warning show up first - if rcount == 0: + + rcount = _count_reduce_items(arr, axis, keepdims=keepdims, where=where) + if rcount == 0 if where is True else umr_any(rcount == 0, axis=None): warnings.warn("Mean of empty slice.", RuntimeWarning, stacklevel=2) # Cast bool, unsigned int, and int to float64 by default @@ -72,7 +176,7 @@ def _mean(a, axis=None, dtype=None, out=None, keepdims=False): dtype = mu.dtype('f4') is_float16_result = True - ret = umr_sum(arr, axis, dtype, out, keepdims) + ret = umr_sum(arr, axis, dtype, out, keepdims, where=where) if isinstance(ret, mu.ndarray): ret = um.true_divide( ret, rcount, out=ret, casting='unsafe', subok=False) @@ -88,12 +192,13 @@ def _mean(a, axis=None, dtype=None, out=None, keepdims=False): return ret -def _var(a, axis=None, dtype=None, out=None, ddof=0, keepdims=False): +def _var(a, axis=None, dtype=None, out=None, ddof=0, keepdims=False, *, + where=True): arr = asanyarray(a) - rcount = _count_reduce_items(arr, axis) + rcount = _count_reduce_items(arr, axis, keepdims=keepdims, where=where) # Make this warning show up on top. - if ddof >= rcount: + if ddof >= rcount if where is True else umr_any(ddof >= rcount, axis=None): warnings.warn("Degrees of freedom <= 0 for slice", RuntimeWarning, stacklevel=2) @@ -104,25 +209,44 @@ def _var(a, axis=None, dtype=None, out=None, ddof=0, keepdims=False): # Compute the mean. # Note that if dtype is not of inexact type then arraymean will # not be either. - arrmean = umr_sum(arr, axis, dtype, keepdims=True) - if isinstance(arrmean, mu.ndarray): - arrmean = um.true_divide( - arrmean, rcount, out=arrmean, casting='unsafe', subok=False) + arrmean = umr_sum(arr, axis, dtype, keepdims=True, where=where) + # The shape of rcount has to match arrmean to not change the shape of out + # in broadcasting. Otherwise, it cannot be stored back to arrmean. + if rcount.ndim == 0: + # fast-path for default case when where is True + div = rcount else: + # matching rcount to arrmean when where is specified as array + div = rcount.reshape(arrmean.shape) + if isinstance(arrmean, mu.ndarray): + arrmean = um.true_divide(arrmean, div, out=arrmean, casting='unsafe', + subok=False) + elif hasattr(arrmean, "dtype"): arrmean = arrmean.dtype.type(arrmean / rcount) + else: + arrmean = arrmean / rcount # Compute sum of squared deviations from mean # Note that x may not be inexact and that we need it to be an array, # not a scalar. x = asanyarray(arr - arrmean) - if issubclass(arr.dtype.type, nt.complexfloating): - x = um.multiply(x, um.conjugate(x), out=x).real - else: + + if issubclass(arr.dtype.type, (nt.floating, nt.integer)): x = um.multiply(x, x, out=x) - ret = umr_sum(x, axis, dtype, out, keepdims) + # Fast-paths for built-in complex types + elif x.dtype in _complex_to_float: + xv = x.view(dtype=(_complex_to_float[x.dtype], (2,))) + um.multiply(xv, xv, out=xv) + x = um.add(xv[..., 0], xv[..., 1], out=x.real).real + # Most general case; includes handling object arrays containing imaginary + # numbers and complex types with non-native byteorder + else: + x = um.multiply(x, um.conjugate(x), out=x).real + + ret = umr_sum(x, axis, dtype, out, keepdims=keepdims, where=where) # Compute degrees of freedom and make sure it is not negative. - rcount = max([rcount - ddof, 0]) + rcount = um.maximum(rcount - ddof, 0) # divide by degrees of freedom if isinstance(ret, mu.ndarray): @@ -135,9 +259,10 @@ def _var(a, axis=None, dtype=None, out=None, ddof=0, keepdims=False): return ret -def _std(a, axis=None, dtype=None, out=None, ddof=0, keepdims=False): +def _std(a, axis=None, dtype=None, out=None, ddof=0, keepdims=False, *, + where=True): ret = _var(a, axis=axis, dtype=dtype, out=out, ddof=ddof, - keepdims=keepdims) + keepdims=keepdims, where=where) if isinstance(ret, mu.ndarray): ret = um.sqrt(ret, out=ret) @@ -154,3 +279,14 @@ def _ptp(a, axis=None, out=None, keepdims=False): umr_minimum(a, axis, None, None, keepdims), out ) + +def _dump(self, file, protocol=2): + if hasattr(file, 'write'): + ctx = nullcontext(file) + else: + ctx = open(os_fspath(file), "wb") + with ctx as f: + pickle.dump(self, f, protocol=protocol) + +def _dumps(self, protocol=2): + return pickle.dumps(self, protocol=protocol) diff --git a/numpy/core/_string_helpers.py b/numpy/core/_string_helpers.py new file mode 100644 index 000000000000..45e6a739ee50 --- /dev/null +++ b/numpy/core/_string_helpers.py @@ -0,0 +1,100 @@ +""" +String-handling utilities to avoid locale-dependence. + +Used primarily to generate type name aliases. +""" +# "import string" is costly to import! +# Construct the translation tables directly +# "A" = chr(65), "a" = chr(97) +_all_chars = [chr(_m) for _m in range(256)] +_ascii_upper = _all_chars[65:65+26] +_ascii_lower = _all_chars[97:97+26] +LOWER_TABLE = "".join(_all_chars[:65] + _ascii_lower + _all_chars[65+26:]) +UPPER_TABLE = "".join(_all_chars[:97] + _ascii_upper + _all_chars[97+26:]) + + +def english_lower(s): + """ Apply English case rules to convert ASCII strings to all lower case. + + This is an internal utility function to replace calls to str.lower() such + that we can avoid changing behavior with changing locales. In particular, + Turkish has distinct dotted and dotless variants of the Latin letter "I" in + both lowercase and uppercase. Thus, "I".lower() != "i" in a "tr" locale. + + Parameters + ---------- + s : str + + Returns + ------- + lowered : str + + Examples + -------- + >>> from numpy.core.numerictypes import english_lower + >>> english_lower('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_') + 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz0123456789_' + >>> english_lower('') + '' + """ + lowered = s.translate(LOWER_TABLE) + return lowered + + +def english_upper(s): + """ Apply English case rules to convert ASCII strings to all upper case. + + This is an internal utility function to replace calls to str.upper() such + that we can avoid changing behavior with changing locales. In particular, + Turkish has distinct dotted and dotless variants of the Latin letter "I" in + both lowercase and uppercase. Thus, "i".upper() != "I" in a "tr" locale. + + Parameters + ---------- + s : str + + Returns + ------- + uppered : str + + Examples + -------- + >>> from numpy.core.numerictypes import english_upper + >>> english_upper('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_') + 'ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_' + >>> english_upper('') + '' + """ + uppered = s.translate(UPPER_TABLE) + return uppered + + +def english_capitalize(s): + """ Apply English case rules to convert the first character of an ASCII + string to upper case. + + This is an internal utility function to replace calls to str.capitalize() + such that we can avoid changing behavior with changing locales. + + Parameters + ---------- + s : str + + Returns + ------- + capitalized : str + + Examples + -------- + >>> from numpy.core.numerictypes import english_capitalize + >>> english_capitalize('int8') + 'Int8' + >>> english_capitalize('Int8') + 'Int8' + >>> english_capitalize('') + '' + """ + if s: + return english_upper(s[0]) + s[1:] + else: + return s diff --git a/numpy/core/_type_aliases.py b/numpy/core/_type_aliases.py new file mode 100644 index 000000000000..3765a0d34e18 --- /dev/null +++ b/numpy/core/_type_aliases.py @@ -0,0 +1,235 @@ +""" +Due to compatibility, numpy has a very large number of different naming +conventions for the scalar types (those subclassing from `numpy.generic`). +This file produces a convoluted set of dictionaries mapping names to types, +and sometimes other mappings too. + +.. data:: allTypes + A dictionary of names to types that will be exposed as attributes through + ``np.core.numerictypes.*`` + +.. data:: sctypeDict + Similar to `allTypes`, but maps a broader set of aliases to their types. + +.. data:: sctypes + A dictionary keyed by a "type group" string, providing a list of types + under that group. + +""" + +from numpy.compat import unicode +from numpy.core._string_helpers import english_lower +from numpy.core.multiarray import typeinfo, dtype +from numpy.core._dtype import _kind_name + + +sctypeDict = {} # Contains all leaf-node scalar types with aliases +allTypes = {} # Collect the types we will add to the module + + +# separate the actual type info from the abstract base classes +_abstract_types = {} +_concrete_typeinfo = {} +for k, v in typeinfo.items(): + # make all the keys lowercase too + k = english_lower(k) + if isinstance(v, type): + _abstract_types[k] = v + else: + _concrete_typeinfo[k] = v + +_concrete_types = {v.type for k, v in _concrete_typeinfo.items()} + + +def _bits_of(obj): + try: + info = next(v for v in _concrete_typeinfo.values() if v.type is obj) + except StopIteration: + if obj in _abstract_types.values(): + msg = "Cannot count the bits of an abstract type" + raise ValueError(msg) from None + + # some third-party type - make a best-guess + return dtype(obj).itemsize * 8 + else: + return info.bits + + +def bitname(obj): + """Return a bit-width name for a given type object""" + bits = _bits_of(obj) + dt = dtype(obj) + char = dt.kind + base = _kind_name(dt) + + if base == 'object': + bits = 0 + + if bits != 0: + char = "%s%d" % (char, bits // 8) + + return base, bits, char + + +def _add_types(): + for name, info in _concrete_typeinfo.items(): + # define C-name and insert typenum and typechar references also + allTypes[name] = info.type + sctypeDict[name] = info.type + sctypeDict[info.char] = info.type + sctypeDict[info.num] = info.type + + for name, cls in _abstract_types.items(): + allTypes[name] = cls +_add_types() + +# This is the priority order used to assign the bit-sized NPY_INTxx names, which +# must match the order in npy_common.h in order for NPY_INTxx and np.intxx to be +# consistent. +# If two C types have the same size, then the earliest one in this list is used +# as the sized name. +_int_ctypes = ['long', 'longlong', 'int', 'short', 'byte'] +_uint_ctypes = list('u' + t for t in _int_ctypes) + +def _add_aliases(): + for name, info in _concrete_typeinfo.items(): + # these are handled by _add_integer_aliases + if name in _int_ctypes or name in _uint_ctypes: + continue + + # insert bit-width version for this class (if relevant) + base, bit, char = bitname(info.type) + + myname = "%s%d" % (base, bit) + + # ensure that (c)longdouble does not overwrite the aliases assigned to + # (c)double + if name in ('longdouble', 'clongdouble') and myname in allTypes: + continue + + allTypes[myname] = info.type + + # add mapping for both the bit name and the numarray name + sctypeDict[myname] = info.type + + # add forward, reverse, and string mapping to numarray + sctypeDict[char] = info.type + + +_add_aliases() + +def _add_integer_aliases(): + seen_bits = set() + for i_ctype, u_ctype in zip(_int_ctypes, _uint_ctypes): + i_info = _concrete_typeinfo[i_ctype] + u_info = _concrete_typeinfo[u_ctype] + bits = i_info.bits # same for both + + for info, charname, intname in [ + (i_info,'i%d' % (bits//8,), 'int%d' % bits), + (u_info,'u%d' % (bits//8,), 'uint%d' % bits)]: + if bits not in seen_bits: + # sometimes two different types have the same number of bits + # if so, the one iterated over first takes precedence + allTypes[intname] = info.type + sctypeDict[intname] = info.type + sctypeDict[charname] = info.type + + seen_bits.add(bits) + +_add_integer_aliases() + +# We use these later +void = allTypes['void'] + +# +# Rework the Python names (so that float and complex and int are consistent +# with Python usage) +# +def _set_up_aliases(): + type_pairs = [('complex_', 'cdouble'), + ('int0', 'intp'), + ('uint0', 'uintp'), + ('single', 'float'), + ('csingle', 'cfloat'), + ('singlecomplex', 'cfloat'), + ('float_', 'double'), + ('intc', 'int'), + ('uintc', 'uint'), + ('int_', 'long'), + ('uint', 'ulong'), + ('cfloat', 'cdouble'), + ('longfloat', 'longdouble'), + ('clongfloat', 'clongdouble'), + ('longcomplex', 'clongdouble'), + ('bool_', 'bool'), + ('bytes_', 'string'), + ('string_', 'string'), + ('str_', 'unicode'), + ('unicode_', 'unicode'), + ('object_', 'object')] + for alias, t in type_pairs: + allTypes[alias] = allTypes[t] + sctypeDict[alias] = sctypeDict[t] + # Remove aliases overriding python types and modules + to_remove = ['ulong', 'object', 'int', 'float', + 'complex', 'bool', 'string', 'datetime', 'timedelta', + 'bytes', 'str'] + + for t in to_remove: + try: + del allTypes[t] + del sctypeDict[t] + except KeyError: + pass +_set_up_aliases() + + +sctypes = {'int': [], + 'uint':[], + 'float':[], + 'complex':[], + 'others':[bool, object, bytes, unicode, void]} + +def _add_array_type(typename, bits): + try: + t = allTypes['%s%d' % (typename, bits)] + except KeyError: + pass + else: + sctypes[typename].append(t) + +def _set_array_types(): + ibytes = [1, 2, 4, 8, 16, 32, 64] + fbytes = [2, 4, 8, 10, 12, 16, 32, 64] + for bytes in ibytes: + bits = 8*bytes + _add_array_type('int', bits) + _add_array_type('uint', bits) + for bytes in fbytes: + bits = 8*bytes + _add_array_type('float', bits) + _add_array_type('complex', 2*bits) + _gi = dtype('p') + if _gi.type not in sctypes['int']: + indx = 0 + sz = _gi.itemsize + _lst = sctypes['int'] + while (indx < len(_lst) and sz >= _lst[indx](0).itemsize): + indx += 1 + sctypes['int'].insert(indx, _gi.type) + sctypes['uint'].insert(indx, dtype('P').type) +_set_array_types() + + +# Add additional strings to the sctypeDict +_toadd = ['int', 'float', 'complex', 'bool', 'object', + 'str', 'bytes', ('a', 'bytes_')] + +for name in _toadd: + if isinstance(name, tuple): + sctypeDict[name[0]] = allTypes[name[1]] + else: + sctypeDict[name] = allTypes['%s_' % name] + +del _toadd, name diff --git a/numpy/core/_type_aliases.pyi b/numpy/core/_type_aliases.pyi new file mode 100644 index 000000000000..c10d072f9f0b --- /dev/null +++ b/numpy/core/_type_aliases.pyi @@ -0,0 +1,13 @@ +from typing import Dict, Union, Type, List, TypedDict + +from numpy import generic, signedinteger, unsignedinteger, floating, complexfloating + +class _SCTypes(TypedDict): + int: List[Type[signedinteger]] + uint: List[Type[unsignedinteger]] + float: List[Type[floating]] + complex: List[Type[complexfloating]] + others: List[type] + +sctypeDict: Dict[Union[int, str], Type[generic]] +sctypes: _SCTypes diff --git a/numpy/core/_ufunc_config.py b/numpy/core/_ufunc_config.py new file mode 100644 index 000000000000..b40e7445ec5b --- /dev/null +++ b/numpy/core/_ufunc_config.py @@ -0,0 +1,446 @@ +""" +Functions for changing global ufunc configuration + +This provides helpers which wrap `umath.geterrobj` and `umath.seterrobj` +""" +import collections.abc +import contextlib + +from .overrides import set_module +from .umath import ( + UFUNC_BUFSIZE_DEFAULT, + ERR_IGNORE, ERR_WARN, ERR_RAISE, ERR_CALL, ERR_PRINT, ERR_LOG, ERR_DEFAULT, + SHIFT_DIVIDEBYZERO, SHIFT_OVERFLOW, SHIFT_UNDERFLOW, SHIFT_INVALID, +) +from . import umath + +__all__ = [ + "seterr", "geterr", "setbufsize", "getbufsize", "seterrcall", "geterrcall", + "errstate", +] + +_errdict = {"ignore": ERR_IGNORE, + "warn": ERR_WARN, + "raise": ERR_RAISE, + "call": ERR_CALL, + "print": ERR_PRINT, + "log": ERR_LOG} + +_errdict_rev = {value: key for key, value in _errdict.items()} + + +@set_module('numpy') +def seterr(all=None, divide=None, over=None, under=None, invalid=None): + """ + Set how floating-point errors are handled. + + Note that operations on integer scalar types (such as `int16`) are + handled like floating point, and are affected by these settings. + + Parameters + ---------- + all : {'ignore', 'warn', 'raise', 'call', 'print', 'log'}, optional + Set treatment for all types of floating-point errors at once: + + - ignore: Take no action when the exception occurs. + - warn: Print a `RuntimeWarning` (via the Python `warnings` module). + - raise: Raise a `FloatingPointError`. + - call: Call a function specified using the `seterrcall` function. + - print: Print a warning directly to ``stdout``. + - log: Record error in a Log object specified by `seterrcall`. + + The default is not to change the current behavior. + divide : {'ignore', 'warn', 'raise', 'call', 'print', 'log'}, optional + Treatment for division by zero. + over : {'ignore', 'warn', 'raise', 'call', 'print', 'log'}, optional + Treatment for floating-point overflow. + under : {'ignore', 'warn', 'raise', 'call', 'print', 'log'}, optional + Treatment for floating-point underflow. + invalid : {'ignore', 'warn', 'raise', 'call', 'print', 'log'}, optional + Treatment for invalid floating-point operation. + + Returns + ------- + old_settings : dict + Dictionary containing the old settings. + + See also + -------- + seterrcall : Set a callback function for the 'call' mode. + geterr, geterrcall, errstate + + Notes + ----- + The floating-point exceptions are defined in the IEEE 754 standard [1]_: + + - Division by zero: infinite result obtained from finite numbers. + - Overflow: result too large to be expressed. + - Underflow: result so close to zero that some precision + was lost. + - Invalid operation: result is not an expressible number, typically + indicates that a NaN was produced. + + .. [1] https://en.wikipedia.org/wiki/IEEE_754 + + Examples + -------- + >>> old_settings = np.seterr(all='ignore') #seterr to known value + >>> np.seterr(over='raise') + {'divide': 'ignore', 'over': 'ignore', 'under': 'ignore', 'invalid': 'ignore'} + >>> np.seterr(**old_settings) # reset to default + {'divide': 'ignore', 'over': 'raise', 'under': 'ignore', 'invalid': 'ignore'} + + >>> np.int16(32000) * np.int16(3) + 30464 + >>> old_settings = np.seterr(all='warn', over='raise') + >>> np.int16(32000) * np.int16(3) + Traceback (most recent call last): + File "<stdin>", line 1, in <module> + FloatingPointError: overflow encountered in short_scalars + + >>> old_settings = np.seterr(all='print') + >>> np.geterr() + {'divide': 'print', 'over': 'print', 'under': 'print', 'invalid': 'print'} + >>> np.int16(32000) * np.int16(3) + 30464 + + """ + + pyvals = umath.geterrobj() + old = geterr() + + if divide is None: + divide = all or old['divide'] + if over is None: + over = all or old['over'] + if under is None: + under = all or old['under'] + if invalid is None: + invalid = all or old['invalid'] + + maskvalue = ((_errdict[divide] << SHIFT_DIVIDEBYZERO) + + (_errdict[over] << SHIFT_OVERFLOW) + + (_errdict[under] << SHIFT_UNDERFLOW) + + (_errdict[invalid] << SHIFT_INVALID)) + + pyvals[1] = maskvalue + umath.seterrobj(pyvals) + return old + + +@set_module('numpy') +def geterr(): + """ + Get the current way of handling floating-point errors. + + Returns + ------- + res : dict + A dictionary with keys "divide", "over", "under", and "invalid", + whose values are from the strings "ignore", "print", "log", "warn", + "raise", and "call". The keys represent possible floating-point + exceptions, and the values define how these exceptions are handled. + + See Also + -------- + geterrcall, seterr, seterrcall + + Notes + ----- + For complete documentation of the types of floating-point exceptions and + treatment options, see `seterr`. + + Examples + -------- + >>> np.geterr() + {'divide': 'warn', 'over': 'warn', 'under': 'ignore', 'invalid': 'warn'} + >>> np.arange(3.) / np.arange(3.) + array([nan, 1., 1.]) + + >>> oldsettings = np.seterr(all='warn', over='raise') + >>> np.geterr() + {'divide': 'warn', 'over': 'raise', 'under': 'warn', 'invalid': 'warn'} + >>> np.arange(3.) / np.arange(3.) + array([nan, 1., 1.]) + + """ + maskvalue = umath.geterrobj()[1] + mask = 7 + res = {} + val = (maskvalue >> SHIFT_DIVIDEBYZERO) & mask + res['divide'] = _errdict_rev[val] + val = (maskvalue >> SHIFT_OVERFLOW) & mask + res['over'] = _errdict_rev[val] + val = (maskvalue >> SHIFT_UNDERFLOW) & mask + res['under'] = _errdict_rev[val] + val = (maskvalue >> SHIFT_INVALID) & mask + res['invalid'] = _errdict_rev[val] + return res + + +@set_module('numpy') +def setbufsize(size): + """ + Set the size of the buffer used in ufuncs. + + Parameters + ---------- + size : int + Size of buffer. + + """ + if size > 10e6: + raise ValueError("Buffer size, %s, is too big." % size) + if size < 5: + raise ValueError("Buffer size, %s, is too small." % size) + if size % 16 != 0: + raise ValueError("Buffer size, %s, is not a multiple of 16." % size) + + pyvals = umath.geterrobj() + old = getbufsize() + pyvals[0] = size + umath.seterrobj(pyvals) + return old + + +@set_module('numpy') +def getbufsize(): + """ + Return the size of the buffer used in ufuncs. + + Returns + ------- + getbufsize : int + Size of ufunc buffer in bytes. + + """ + return umath.geterrobj()[0] + + +@set_module('numpy') +def seterrcall(func): + """ + Set the floating-point error callback function or log object. + + There are two ways to capture floating-point error messages. The first + is to set the error-handler to 'call', using `seterr`. Then, set + the function to call using this function. + + The second is to set the error-handler to 'log', using `seterr`. + Floating-point errors then trigger a call to the 'write' method of + the provided object. + + Parameters + ---------- + func : callable f(err, flag) or object with write method + Function to call upon floating-point errors ('call'-mode) or + object whose 'write' method is used to log such message ('log'-mode). + + The call function takes two arguments. The first is a string describing + the type of error (such as "divide by zero", "overflow", "underflow", + or "invalid value"), and the second is the status flag. The flag is a + byte, whose four least-significant bits indicate the type of error, one + of "divide", "over", "under", "invalid":: + + [0 0 0 0 divide over under invalid] + + In other words, ``flags = divide + 2*over + 4*under + 8*invalid``. + + If an object is provided, its write method should take one argument, + a string. + + Returns + ------- + h : callable, log instance or None + The old error handler. + + See Also + -------- + seterr, geterr, geterrcall + + Examples + -------- + Callback upon error: + + >>> def err_handler(type, flag): + ... print("Floating point error (%s), with flag %s" % (type, flag)) + ... + + >>> saved_handler = np.seterrcall(err_handler) + >>> save_err = np.seterr(all='call') + + >>> np.array([1, 2, 3]) / 0.0 + Floating point error (divide by zero), with flag 1 + array([inf, inf, inf]) + + >>> np.seterrcall(saved_handler) + <function err_handler at 0x...> + >>> np.seterr(**save_err) + {'divide': 'call', 'over': 'call', 'under': 'call', 'invalid': 'call'} + + Log error message: + + >>> class Log: + ... def write(self, msg): + ... print("LOG: %s" % msg) + ... + + >>> log = Log() + >>> saved_handler = np.seterrcall(log) + >>> save_err = np.seterr(all='log') + + >>> np.array([1, 2, 3]) / 0.0 + LOG: Warning: divide by zero encountered in true_divide + array([inf, inf, inf]) + + >>> np.seterrcall(saved_handler) + <numpy.core.numeric.Log object at 0x...> + >>> np.seterr(**save_err) + {'divide': 'log', 'over': 'log', 'under': 'log', 'invalid': 'log'} + + """ + if func is not None and not isinstance(func, collections.abc.Callable): + if (not hasattr(func, 'write') or + not isinstance(func.write, collections.abc.Callable)): + raise ValueError("Only callable can be used as callback") + pyvals = umath.geterrobj() + old = geterrcall() + pyvals[2] = func + umath.seterrobj(pyvals) + return old + + +@set_module('numpy') +def geterrcall(): + """ + Return the current callback function used on floating-point errors. + + When the error handling for a floating-point error (one of "divide", + "over", "under", or "invalid") is set to 'call' or 'log', the function + that is called or the log instance that is written to is returned by + `geterrcall`. This function or log instance has been set with + `seterrcall`. + + Returns + ------- + errobj : callable, log instance or None + The current error handler. If no handler was set through `seterrcall`, + ``None`` is returned. + + See Also + -------- + seterrcall, seterr, geterr + + Notes + ----- + For complete documentation of the types of floating-point exceptions and + treatment options, see `seterr`. + + Examples + -------- + >>> np.geterrcall() # we did not yet set a handler, returns None + + >>> oldsettings = np.seterr(all='call') + >>> def err_handler(type, flag): + ... print("Floating point error (%s), with flag %s" % (type, flag)) + >>> oldhandler = np.seterrcall(err_handler) + >>> np.array([1, 2, 3]) / 0.0 + Floating point error (divide by zero), with flag 1 + array([inf, inf, inf]) + + >>> cur_handler = np.geterrcall() + >>> cur_handler is err_handler + True + + """ + return umath.geterrobj()[2] + + +class _unspecified: + pass + + +_Unspecified = _unspecified() + + +@set_module('numpy') +class errstate(contextlib.ContextDecorator): + """ + errstate(**kwargs) + + Context manager for floating-point error handling. + + Using an instance of `errstate` as a context manager allows statements in + that context to execute with a known error handling behavior. Upon entering + the context the error handling is set with `seterr` and `seterrcall`, and + upon exiting it is reset to what it was before. + + .. versionchanged:: 1.17.0 + `errstate` is also usable as a function decorator, saving + a level of indentation if an entire function is wrapped. + See :py:class:`contextlib.ContextDecorator` for more information. + + Parameters + ---------- + kwargs : {divide, over, under, invalid} + Keyword arguments. The valid keywords are the possible floating-point + exceptions. Each keyword should have a string value that defines the + treatment for the particular error. Possible values are + {'ignore', 'warn', 'raise', 'call', 'print', 'log'}. + + See Also + -------- + seterr, geterr, seterrcall, geterrcall + + Notes + ----- + For complete documentation of the types of floating-point exceptions and + treatment options, see `seterr`. + + Examples + -------- + >>> olderr = np.seterr(all='ignore') # Set error handling to known state. + + >>> np.arange(3) / 0. + array([nan, inf, inf]) + >>> with np.errstate(divide='warn'): + ... np.arange(3) / 0. + array([nan, inf, inf]) + + >>> np.sqrt(-1) + nan + >>> with np.errstate(invalid='raise'): + ... np.sqrt(-1) + Traceback (most recent call last): + File "<stdin>", line 2, in <module> + FloatingPointError: invalid value encountered in sqrt + + Outside the context the error handling behavior has not changed: + + >>> np.geterr() + {'divide': 'ignore', 'over': 'ignore', 'under': 'ignore', 'invalid': 'ignore'} + + """ + + def __init__(self, *, call=_Unspecified, **kwargs): + self.call = call + self.kwargs = kwargs + + def __enter__(self): + self.oldstate = seterr(**self.kwargs) + if self.call is not _Unspecified: + self.oldcall = seterrcall(self.call) + + def __exit__(self, *exc_info): + seterr(**self.oldstate) + if self.call is not _Unspecified: + seterrcall(self.oldcall) + + +def _setdef(): + defval = [UFUNC_BUFSIZE_DEFAULT, ERR_DEFAULT, None] + umath.seterrobj(defval) + + +# set the default values +_setdef() diff --git a/numpy/core/_ufunc_config.pyi b/numpy/core/_ufunc_config.pyi new file mode 100644 index 000000000000..cd7129bcb140 --- /dev/null +++ b/numpy/core/_ufunc_config.pyi @@ -0,0 +1,36 @@ +from typing import Optional, Union, Callable, Any, Literal, TypedDict + +from numpy import _SupportsWrite + +_ErrKind = Literal["ignore", "warn", "raise", "call", "print", "log"] +_ErrFunc = Callable[[str, int], Any] + +class _ErrDict(TypedDict): + divide: _ErrKind + over: _ErrKind + under: _ErrKind + invalid: _ErrKind + +class _ErrDictOptional(TypedDict, total=False): + all: Optional[_ErrKind] + divide: Optional[_ErrKind] + over: Optional[_ErrKind] + under: Optional[_ErrKind] + invalid: Optional[_ErrKind] + +def seterr( + all: Optional[_ErrKind] = ..., + divide: Optional[_ErrKind] = ..., + over: Optional[_ErrKind] = ..., + under: Optional[_ErrKind] = ..., + invalid: Optional[_ErrKind] = ..., +) -> _ErrDict: ... +def geterr() -> _ErrDict: ... +def setbufsize(size: int) -> int: ... +def getbufsize() -> int: ... +def seterrcall( + func: Union[None, _ErrFunc, _SupportsWrite[str]] +) -> Union[None, _ErrFunc, _SupportsWrite[str]]: ... +def geterrcall() -> Union[None, _ErrFunc, _SupportsWrite[str]]: ... + +# See `numpy/__init__.pyi` for the `errstate` class diff --git a/numpy/core/arrayprint.py b/numpy/core/arrayprint.py index 6d15cb23f28f..d7e9bf79534c 100644 --- a/numpy/core/arrayprint.py +++ b/numpy/core/arrayprint.py @@ -3,8 +3,6 @@ $Id: arrayprint.py,v 1.9 2005/09/13 13:58:44 teoliphant Exp $ """ -from __future__ import division, absolute_import, print_function - __all__ = ["array2string", "array_str", "array_repr", "set_string_function", "set_printoptions", "get_printoptions", "printoptions", "format_float_positional", "format_float_scientific"] @@ -24,30 +22,27 @@ # scalars are printed inside an ndarray. Only the latter strs are currently # user-customizable. -import sys import functools -if sys.version_info[0] >= 3: - try: - from _thread import get_ident - except ImportError: - from _dummy_thread import get_ident -else: - try: - from thread import get_ident - except ImportError: - from dummy_thread import get_ident +import numbers +import sys +try: + from _thread import get_ident +except ImportError: + from _dummy_thread import get_ident import numpy as np from . import numerictypes as _nt -from .umath import absolute, not_equal, isnan, isinf, isfinite, isnat +from .umath import absolute, isinf, isfinite, isnat from . import multiarray from .multiarray import (array, dragon4_positional, dragon4_scientific, - datetime_as_string, datetime_data, dtype, ndarray, + datetime_as_string, datetime_data, ndarray, set_legacy_print_mode) -from .fromnumeric import ravel, any +from .fromnumeric import any from .numeric import concatenate, asarray, errstate from .numerictypes import (longlong, intc, int_, float_, complex_, bool_, flexible) +from .overrides import array_function_dispatch, set_module +import operator import warnings import contextlib @@ -62,12 +57,17 @@ 'infstr': 'inf', 'sign': '-', 'formatter': None, - 'legacy': False} + # Internally stored as an int to simplify comparisons; converted from/to + # str/False on the way in/out. + 'legacy': sys.maxsize} def _make_options_dict(precision=None, threshold=None, edgeitems=None, linewidth=None, suppress=None, nanstr=None, infstr=None, sign=None, formatter=None, floatmode=None, legacy=None): - """ make a dictionary out of the non-None arguments, plus sanity checks """ + """ + Make a dictionary out of the non-None arguments, plus conversion of + *legacy* and sanity checks. + """ options = {k: v for k, v in locals().items() if v is not None} @@ -82,15 +82,41 @@ def _make_options_dict(precision=None, threshold=None, edgeitems=None, if sign not in [None, '-', '+', ' ']: raise ValueError("sign option must be one of ' ', '+', or '-'") - if legacy not in [None, False, '1.13']: - warnings.warn("legacy printing option can currently only be '1.13' or " - "`False`", stacklevel=3) + if legacy == False: + options['legacy'] = sys.maxsize + elif legacy == '1.13': + options['legacy'] = 113 + elif legacy == '1.21': + options['legacy'] = 121 + elif legacy is None: + pass # OK, do nothing. + else: + warnings.warn( + "legacy printing option can currently only be '1.13', '1.21', or " + "`False`", stacklevel=3) + + if threshold is not None: + # forbid the bad threshold arg suggested by stack overflow, gh-12351 + if not isinstance(threshold, numbers.Number): + raise TypeError("threshold must be numeric") + if np.isnan(threshold): + raise ValueError("threshold must be non-NAN, try " + "sys.maxsize for untruncated representation") + + if precision is not None: + # forbid the bad precision arg as suggested by issue #18254 + try: + options['precision'] = operator.index(precision) + except TypeError as e: + raise TypeError('precision must be an integer') from e return options + +@set_module('numpy') def set_printoptions(precision=None, threshold=None, edgeitems=None, linewidth=None, suppress=None, nanstr=None, infstr=None, - formatter=None, sign=None, floatmode=None, **kwarg): + formatter=None, sign=None, floatmode=None, *, legacy=None): """ Set printing options. @@ -101,11 +127,12 @@ def set_printoptions(precision=None, threshold=None, edgeitems=None, ---------- precision : int or None, optional Number of digits of precision for floating point output (default 8). - May be `None` if `floatmode` is not `fixed`, to print as many digits as + May be None if `floatmode` is not `fixed`, to print as many digits as necessary to uniquely specify the value. threshold : int, optional Total number of array elements which trigger summarization rather than full repr (default 1000). + To always use the full repr without summarization, pass `sys.maxsize`. edgeitems : int, optional Number of array items in summary at beginning and end of each dimension (default 3). @@ -144,7 +171,6 @@ def set_printoptions(precision=None, threshold=None, edgeitems=None, - 'longcomplexfloat' : composed of two 128-bit floats - 'numpystr' : types `numpy.string_` and `numpy.unicode_` - 'object' : `np.object_` arrays - - 'str' : all other strings Other keys that can be used to set a group of types at once are: @@ -152,10 +178,11 @@ def set_printoptions(precision=None, threshold=None, edgeitems=None, - 'int_kind' : sets 'int' - 'float_kind' : sets 'float' and 'longfloat' - 'complex_kind' : sets 'complexfloat' and 'longcomplexfloat' - - 'str_kind' : sets 'str' and 'numpystr' + - 'str_kind' : sets 'numpystr' floatmode : str, optional Controls the interpretation of the `precision` option for - floating-point types. Can take the following values: + floating-point types. Can take the following values + (default maxprec_equal): * 'fixed': Always print exactly `precision` fractional digits, even if this would print more or fewer digits than @@ -174,40 +201,52 @@ def set_printoptions(precision=None, threshold=None, edgeitems=None, legacy : string or `False`, optional If set to the string `'1.13'` enables 1.13 legacy printing mode. This approximates numpy 1.13 print output by including a space in the sign - position of floats and different behavior for 0d arrays. If set to - `False`, disables legacy mode. Unrecognized strings will be ignored - with a warning for forward compatibility. + position of floats and different behavior for 0d arrays. This also + enables 1.21 legacy printing mode (described below). + + If set to the string `'1.21'` enables 1.21 legacy printing mode. This + approximates numpy 1.21 print output of complex structured dtypes + by not inserting spaces after commas that separate fields and after + colons. + + If set to `False`, disables legacy mode. + + Unrecognized strings will be ignored with a warning for forward + compatibility. .. versionadded:: 1.14.0 + .. versionchanged:: 1.22.0 See Also -------- - get_printoptions, set_string_function, array2string + get_printoptions, printoptions, set_string_function, array2string Notes ----- `formatter` is always reset with a call to `set_printoptions`. + Use `printoptions` as a context manager to set the values temporarily. + Examples -------- Floating point precision can be set: >>> np.set_printoptions(precision=4) - >>> print(np.array([1.123456789])) - [ 1.1235] + >>> np.array([1.123456789]) + [1.1235] Long arrays can be summarised: >>> np.set_printoptions(threshold=5) - >>> print(np.arange(10)) - [0 1 2 ..., 7 8 9] + >>> np.arange(10) + array([0, 1, 2, ..., 7, 8, 9]) Small results can be suppressed: >>> eps = np.finfo(float).eps >>> x = np.arange(4.) >>> x**2 - (x + eps)**2 - array([ -4.9304e-32, -4.4409e-16, 0.0000e+00, 0.0000e+00]) + array([-4.9304e-32, -4.4409e-16, 0.0000e+00, 0.0000e+00]) >>> np.set_printoptions(suppress=True) >>> x**2 - (x + eps)**2 array([-0., -0., 0., 0.]) @@ -224,15 +263,17 @@ def set_printoptions(precision=None, threshold=None, edgeitems=None, To put back the default options, you can use: - >>> np.set_printoptions(edgeitems=3,infstr='inf', + >>> np.set_printoptions(edgeitems=3, infstr='inf', ... linewidth=75, nanstr='nan', precision=8, ... suppress=False, threshold=1000, formatter=None) - """ - legacy = kwarg.pop('legacy', None) - if kwarg: - msg = "set_printoptions() got unexpected keyword argument '{}'" - raise TypeError(msg.format(kwarg.popitem()[0])) + Also to temporarily override options, use `printoptions` as a context manager: + + >>> with np.printoptions(precision=2, suppress=True, threshold=5): + ... np.linspace(0, 10, 10) + array([ 0. , 1.11, 2.22, ..., 7.78, 8.89, 10. ]) + + """ opt = _make_options_dict(precision, threshold, edgeitems, linewidth, suppress, nanstr, infstr, sign, formatter, floatmode, legacy) @@ -241,14 +282,17 @@ def set_printoptions(precision=None, threshold=None, edgeitems=None, _format_options.update(opt) # set the C variable for legacy mode - if _format_options['legacy'] == '1.13': + if _format_options['legacy'] == 113: set_legacy_print_mode(113) # reset the sign option in legacy mode to avoid confusion _format_options['sign'] = '-' - elif _format_options['legacy'] is False: + elif _format_options['legacy'] == 121: + set_legacy_print_mode(121) + elif _format_options['legacy'] == sys.maxsize: set_legacy_print_mode(0) +@set_module('numpy') def get_printoptions(): """ Return the current print options. @@ -272,12 +316,22 @@ def get_printoptions(): See Also -------- - set_printoptions, set_string_function + set_printoptions, printoptions, set_string_function """ - return _format_options.copy() + opts = _format_options.copy() + opts['legacy'] = { + 113: '1.13', 121: '1.21', sys.maxsize: False, + }[opts['legacy']] + return opts + +def _get_legacy_print_mode(): + """Return the legacy print mode as an int.""" + return _format_options['legacy'] + +@set_module('numpy') @contextlib.contextmanager def printoptions(*args, **kwargs): """Context manager for setting print options. @@ -289,9 +343,10 @@ def printoptions(*args, **kwargs): Examples -------- + >>> from numpy.testing import assert_equal >>> with np.printoptions(precision=2): - ... print(np.array([2.0])) / 3 - [0.67] + ... np.array([2.0]) / 3 + array([0.67]) The `as`-clause of the `with`-statement gives the current print options: @@ -345,36 +400,33 @@ def repr_format(x): def str_format(x): return str(x) -def _get_formatdict(data, **opt): - prec, fmode = opt['precision'], opt['floatmode'] - supp, sign = opt['suppress'], opt['sign'] - legacy = opt['legacy'] +def _get_formatdict(data, *, precision, floatmode, suppress, sign, legacy, + formatter, **kwargs): + # note: extra arguments in kwargs are ignored # wrapped in lambdas to avoid taking a code path with the wrong type of data formatdict = { 'bool': lambda: BoolFormat(data), 'int': lambda: IntegerFormat(data), - 'float': lambda: - FloatingFormat(data, prec, fmode, supp, sign, legacy=legacy), - 'longfloat': lambda: - FloatingFormat(data, prec, fmode, supp, sign, legacy=legacy), - 'complexfloat': lambda: - ComplexFloatingFormat(data, prec, fmode, supp, sign, legacy=legacy), - 'longcomplexfloat': lambda: - ComplexFloatingFormat(data, prec, fmode, supp, sign, legacy=legacy), + 'float': lambda: FloatingFormat( + data, precision, floatmode, suppress, sign, legacy=legacy), + 'longfloat': lambda: FloatingFormat( + data, precision, floatmode, suppress, sign, legacy=legacy), + 'complexfloat': lambda: ComplexFloatingFormat( + data, precision, floatmode, suppress, sign, legacy=legacy), + 'longcomplexfloat': lambda: ComplexFloatingFormat( + data, precision, floatmode, suppress, sign, legacy=legacy), 'datetime': lambda: DatetimeFormat(data, legacy=legacy), 'timedelta': lambda: TimedeltaFormat(data), 'object': lambda: _object_format, 'void': lambda: str_format, - 'numpystr': lambda: repr_format, - 'str': lambda: str} + 'numpystr': lambda: repr_format} # we need to wrap values in `formatter` in a lambda, so that the interface # is the same as the above values. def indirect(x): return lambda: x - formatter = opt['formatter'] if formatter is not None: fkeys = [k for k in formatter.keys() if formatter[k] is not None] if 'all' in fkeys: @@ -390,8 +442,7 @@ def indirect(x): for key in ['complexfloat', 'longcomplexfloat']: formatdict[key] = indirect(formatter['complex_kind']) if 'str_kind' in fkeys: - for key in ['numpystr', 'str']: - formatdict[key] = indirect(formatter['str_kind']) + formatdict['numpystr'] = indirect(formatter['str_kind']) for key in formatdict.keys(): if key in fkeys: formatdict[key] = indirect(formatter[key]) @@ -405,7 +456,9 @@ def _get_format_function(data, **options): dtype_ = data.dtype dtypeobj = dtype_.type formatdict = _get_formatdict(data, **options) - if issubclass(dtypeobj, _nt.bool_): + if dtypeobj is None: + return formatdict["numpystr"]() + elif issubclass(dtypeobj, _nt.bool_): return formatdict['bool']() elif issubclass(dtypeobj, _nt.integer): if issubclass(dtypeobj, _nt.timedelta64): @@ -496,31 +549,44 @@ def _array2string(a, options, separator=' ', prefix=""): return lst +def _array2string_dispatcher( + a, max_line_width=None, precision=None, + suppress_small=None, separator=None, prefix=None, + style=None, formatter=None, threshold=None, + edgeitems=None, sign=None, floatmode=None, suffix=None, + *, legacy=None): + return (a,) + + +@array_function_dispatch(_array2string_dispatcher, module='numpy') def array2string(a, max_line_width=None, precision=None, suppress_small=None, separator=' ', prefix="", style=np._NoValue, formatter=None, threshold=None, edgeitems=None, sign=None, floatmode=None, suffix="", - **kwarg): + *, legacy=None): """ Return a string representation of an array. Parameters ---------- - a : array_like + a : ndarray Input array. max_line_width : int, optional - The maximum number of columns the string should span. Newline - characters splits the string appropriately after array elements. + Inserts newlines if text is longer than `max_line_width`. + Defaults to ``numpy.get_printoptions()['linewidth']``. precision : int or None, optional - Floating point precision. Default is the current printing - precision (usually 8), which can be altered using `set_printoptions`. + Floating point precision. + Defaults to ``numpy.get_printoptions()['precision']``. suppress_small : bool, optional - Represent very small numbers as zero. A number is "very small" if it - is smaller than the current printing precision. + Represent numbers "very close" to zero as zero; default is False. + Very close is defined by precision: if the precision is 8, e.g., + numbers smaller (in absolute value) than 5e-9 are represented as + zero. + Defaults to ``numpy.get_printoptions()['suppress']``. separator : str, optional Inserted between elements. prefix : str, optional - suffix: str, optional + suffix : str, optional The length of the prefix and suffix strings are used to respectively align and wrap the output. An array is typically printed as:: @@ -528,6 +594,8 @@ def array2string(a, max_line_width=None, precision=None, The output is left-padded by the length of the prefix string, and wrapping is forced at the column ``max_line_width - len(suffix)``. + It should be noted that the content of prefix and suffix strings are + not included in the output. style : _NoValue, optional Has no effect, do not use. @@ -549,7 +617,6 @@ def array2string(a, max_line_width=None, precision=None, - 'longcomplexfloat' : composed of two 128-bit floats - 'void' : type `numpy.void` - 'numpystr' : types `numpy.string_` and `numpy.unicode_` - - 'str' : all other strings Other keys that can be used to set a group of types at once are: @@ -557,21 +624,26 @@ def array2string(a, max_line_width=None, precision=None, - 'int_kind' : sets 'int' - 'float_kind' : sets 'float' and 'longfloat' - 'complex_kind' : sets 'complexfloat' and 'longcomplexfloat' - - 'str_kind' : sets 'str' and 'numpystr' + - 'str_kind' : sets 'numpystr' threshold : int, optional Total number of array elements which trigger summarization rather than full repr. + Defaults to ``numpy.get_printoptions()['threshold']``. edgeitems : int, optional Number of array items in summary at beginning and end of each dimension. + Defaults to ``numpy.get_printoptions()['edgeitems']``. sign : string, either '-', '+', or ' ', optional Controls printing of the sign of floating-point types. If '+', always print the sign of positive values. If ' ', always prints a space (whitespace character) in the sign position of positive values. If '-', omit the sign character of positive values. + Defaults to ``numpy.get_printoptions()['sign']``. floatmode : str, optional Controls the interpretation of the `precision` option for - floating-point types. Can take the following values: + floating-point types. + Defaults to ``numpy.get_printoptions()['floatmode']``. + Can take the following values: - 'fixed': Always print exactly `precision` fractional digits, even if this would print more or fewer digits than @@ -622,9 +694,9 @@ def array2string(a, max_line_width=None, precision=None, Examples -------- >>> x = np.array([1e-16,1,2,3]) - >>> print(np.array2string(x, precision=2, separator=',', - ... suppress_small=True)) - [ 0., 1., 2., 3.] + >>> np.array2string(x, precision=2, separator=',', + ... suppress_small=True) + '[0.,1.,2.,3.]' >>> x = np.arange(3.) >>> np.array2string(x, formatter={'float_kind':lambda x: "%.2f" % x}) @@ -632,13 +704,9 @@ def array2string(a, max_line_width=None, precision=None, >>> x = np.arange(3) >>> np.array2string(x, formatter={'int':lambda x: hex(x)}) - '[0x0L 0x1L 0x2L]' + '[0x0 0x1 0x2]' """ - legacy = kwarg.pop('legacy', None) - if kwarg: - msg = "array2string() got unexpected keyword argument '{}'" - raise TypeError(msg.format(kwarg.popitem()[0])) overrides = _make_options_dict(precision, threshold, edgeitems, max_line_width, suppress_small, None, None, @@ -646,11 +714,11 @@ def array2string(a, max_line_width=None, precision=None, options = _format_options.copy() options.update(overrides) - if options['legacy'] == '1.13': + if options['legacy'] <= 113: if style is np._NoValue: style = repr - if a.shape == () and not a.dtype.names: + if a.shape == () and a.dtype.names is None: return style(a.item()) elif style is not np._NoValue: # Deprecation 11-9-2017 v1.14 @@ -658,7 +726,7 @@ def array2string(a, max_line_width=None, precision=None, " except in 1.13 'legacy' mode", DeprecationWarning, stacklevel=3) - if options['legacy'] != '1.13': + if options['legacy'] > 113: options['linewidth'] -= len(suffix) # treat as a null array if any of shape elements == 0 @@ -670,8 +738,8 @@ def array2string(a, max_line_width=None, precision=None, def _extendLine(s, line, word, line_width, next_line_prefix, legacy): needs_wrap = len(line) + len(word) > line_width - if legacy != '1.13': - s# don't wrap lines if it won't help + if legacy > 113: + # don't wrap lines if it won't help if len(line) <= len(next_line_prefix): needs_wrap = False @@ -682,6 +750,33 @@ def _extendLine(s, line, word, line_width, next_line_prefix, legacy): return s, line +def _extendLine_pretty(s, line, word, line_width, next_line_prefix, legacy): + """ + Extends line with nicely formatted (possibly multi-line) string ``word``. + """ + words = word.splitlines() + if len(words) == 1 or legacy <= 113: + return _extendLine(s, line, word, line_width, next_line_prefix, legacy) + + max_word_length = max(len(word) for word in words) + if (len(line) + max_word_length > line_width and + len(line) > len(next_line_prefix)): + s += line.rstrip() + '\n' + line = next_line_prefix + words[0] + indent = next_line_prefix + else: + indent = len(line)*' ' + line += words[0] + + for word in words[1::]: + s += line.rstrip() + '\n' + line = indent + word + + suffix_length = max_word_length - len(words[-1]) + line += suffix_length*' ' + + return s, line + def _formatArray(a, format_function, line_width, next_line_prefix, separator, edge_items, summary_insert, legacy): """formatArray is designed for two modes of operation: @@ -706,7 +801,7 @@ def recurser(index, hanging_indent, curr_width): # when recursing, add a space to align with the [ added, and reduce the # length of the line by 1 next_hanging_indent = hanging_indent + ' ' - if legacy == '1.13': + if legacy <= 113: next_width = curr_width else: next_width = curr_width - len(']') @@ -726,7 +821,7 @@ def recurser(index, hanging_indent, curr_width): # last axis (rows) - wrap elements if they would not fit on one line if axes_left == 1: # the length up until the beginning of the separator / bracket - if legacy == '1.13': + if legacy <= 113: elem_width = curr_width - len(separator.rstrip()) else: elem_width = curr_width - max(len(separator.rstrip()), len(']')) @@ -734,29 +829,29 @@ def recurser(index, hanging_indent, curr_width): line = hanging_indent for i in range(leading_items): word = recurser(index + (i,), next_hanging_indent, next_width) - s, line = _extendLine( + s, line = _extendLine_pretty( s, line, word, elem_width, hanging_indent, legacy) line += separator if show_summary: s, line = _extendLine( s, line, summary_insert, elem_width, hanging_indent, legacy) - if legacy == '1.13': + if legacy <= 113: line += ", " else: line += separator for i in range(trailing_items, 1, -1): word = recurser(index + (-i,), next_hanging_indent, next_width) - s, line = _extendLine( + s, line = _extendLine_pretty( s, line, word, elem_width, hanging_indent, legacy) line += separator - if legacy == '1.13': + if legacy <= 113: # width of the separator is not considered on 1.13 elem_width = curr_width word = recurser(index + (-1,), next_hanging_indent, next_width) - s, line = _extendLine( + s, line = _extendLine_pretty( s, line, word, elem_width, hanging_indent, legacy) s += line @@ -771,7 +866,7 @@ def recurser(index, hanging_indent, curr_width): s += hanging_indent + nested + line_sep if show_summary: - if legacy == '1.13': + if legacy <= 113: # trailing space, fixed nbr of newlines, and fixed separator s += hanging_indent + summary_insert + ", \n" else: @@ -807,16 +902,16 @@ def _none_or_positive_arg(x, name): raise ValueError("{} must be >= 0".format(name)) return x -class FloatingFormat(object): +class FloatingFormat: """ Formatter for subtypes of np.floating """ def __init__(self, data, precision, floatmode, suppress_small, sign=False, - **kwarg): + *, legacy=None): # for backcompatibility, accept bools if isinstance(sign, bool): sign = '+' if sign else '-' - self._legacy = kwarg.get('legacy', False) - if self._legacy == '1.13': + self._legacy = legacy + if self._legacy <= 113: # when not 0d, legacy does not support '-' if data.shape != () and sign == '-': sign = ' ' @@ -857,9 +952,10 @@ def fillFormat(self, data): self.trim = '.' self.exp_size = -1 self.unique = True + self.min_digits = None elif self.exp_format: trim, unique = '.', True - if self.floatmode == 'fixed' or self._legacy == '1.13': + if self.floatmode == 'fixed' or self._legacy <= 113: trim, unique = 'k', False strs = (dragon4_scientific(x, precision=self.precision, unique=unique, trim=trim, sign=self.sign == '+') @@ -870,19 +966,18 @@ def fillFormat(self, data): self.trim = 'k' self.precision = max(len(s) for s in frac_part) + self.min_digits = self.precision + self.unique = unique # for back-compat with np 1.13, use 2 spaces & sign and full prec - if self._legacy == '1.13': + if self._legacy <= 113: self.pad_left = 3 else: # this should be only 1 or 2. Can be calculated from sign. self.pad_left = max(len(s) for s in int_part) # pad_right is only needed for nan length calculation self.pad_right = self.exp_size + 2 + self.precision - - self.unique = False else: - # first pass printing to determine sizes trim, unique = '.', True if self.floatmode == 'fixed': trim, unique = 'k', False @@ -892,22 +987,22 @@ def fillFormat(self, data): sign=self.sign == '+') for x in finite_vals) int_part, frac_part = zip(*(s.split('.') for s in strs)) - if self._legacy == '1.13': + if self._legacy <= 113: self.pad_left = 1 + max(len(s.lstrip('-+')) for s in int_part) else: self.pad_left = max(len(s) for s in int_part) self.pad_right = max(len(s) for s in frac_part) self.exp_size = -1 + self.unique = unique if self.floatmode in ['fixed', 'maxprec_equal']: - self.precision = self.pad_right - self.unique = False + self.precision = self.min_digits = self.pad_right self.trim = 'k' else: - self.unique = True self.trim = '.' + self.min_digits = 0 - if self._legacy != '1.13': + if self._legacy > 113: # account for sign = ' ' by adding one to pad_left if self.sign == ' ' and not any(np.signbit(finite_vals)): self.pad_left += 1 @@ -934,6 +1029,7 @@ def __call__(self, x): if self.exp_format: return dragon4_scientific(x, precision=self.precision, + min_digits=self.min_digits, unique=self.unique, trim=self.trim, sign=self.sign == '+', @@ -942,6 +1038,7 @@ def __call__(self, x): else: return dragon4_positional(x, precision=self.precision, + min_digits=self.min_digits, unique=self.unique, fractional=True, trim=self.trim, @@ -949,22 +1046,11 @@ def __call__(self, x): pad_left=self.pad_left, pad_right=self.pad_right) -# for back-compatibility, we keep the classes for each float type too -class FloatFormat(FloatingFormat): - def __init__(self, *args, **kwargs): - warnings.warn("FloatFormat has been replaced by FloatingFormat", - DeprecationWarning, stacklevel=2) - super(FloatFormat, self).__init__(*args, **kwargs) - - -class LongFloatFormat(FloatingFormat): - def __init__(self, *args, **kwargs): - warnings.warn("LongFloatFormat has been replaced by FloatingFormat", - DeprecationWarning, stacklevel=2) - super(LongFloatFormat, self).__init__(*args, **kwargs) +@set_module('numpy') def format_float_scientific(x, precision=None, unique=True, trim='k', - sign=False, pad_left=None, exp_digits=None): + sign=False, pad_left=None, exp_digits=None, + min_digits=None): """ Format a floating-point scalar as a decimal string in scientific notation. @@ -982,11 +1068,12 @@ def format_float_scientific(x, precision=None, unique=True, trim='k', If `True`, use a digit-generation strategy which gives the shortest representation which uniquely identifies the floating-point number from other values of the same type, by judicious rounding. If `precision` - was omitted, print all necessary digits, otherwise digit generation is - cut off after `precision` digits and the remaining value is rounded. + is given fewer digits than necessary can be printed. If `min_digits` + is given more can be printed, in which cases the last digit is rounded + with unbiased rounding. If `False`, digits are generated as if printing an infinite-precision value and stopping after `precision` digits, rounding the remaining - value. + value with unbiased rounding trim : one of 'k', '.', '0', '-', optional Controls post-processing trimming of trailing digits, as follows: @@ -1003,7 +1090,13 @@ def format_float_scientific(x, precision=None, unique=True, trim='k', exp_digits : non-negative integer, optional Pad the exponent with zeros until it contains at least this many digits. If omitted, the exponent will be at least 2 digits. + min_digits : non-negative integer or None, optional + Minimum number of digits to print. This only has an effect for + `unique=True`. In that case more digits than necessary to uniquely + identify the value may be printed and rounded unbiased. + -- versionadded:: 1.21.0 + Returns ------- rep : string @@ -1026,13 +1119,18 @@ def format_float_scientific(x, precision=None, unique=True, trim='k', precision = _none_or_positive_arg(precision, 'precision') pad_left = _none_or_positive_arg(pad_left, 'pad_left') exp_digits = _none_or_positive_arg(exp_digits, 'exp_digits') + min_digits = _none_or_positive_arg(min_digits, 'min_digits') + if min_digits > 0 and precision > 0 and min_digits > precision: + raise ValueError("min_digits must be less than or equal to precision") return dragon4_scientific(x, precision=precision, unique=unique, trim=trim, sign=sign, pad_left=pad_left, - exp_digits=exp_digits) + exp_digits=exp_digits, min_digits=min_digits) + +@set_module('numpy') def format_float_positional(x, precision=None, unique=True, fractional=True, trim='k', sign=False, - pad_left=None, pad_right=None): + pad_left=None, pad_right=None, min_digits=None): """ Format a floating-point scalar as a decimal string in positional notation. @@ -1050,16 +1148,19 @@ def format_float_positional(x, precision=None, unique=True, If `True`, use a digit-generation strategy which gives the shortest representation which uniquely identifies the floating-point number from other values of the same type, by judicious rounding. If `precision` - was omitted, print out all necessary digits, otherwise digit generation - is cut off after `precision` digits and the remaining value is rounded. + is given fewer digits than necessary can be printed, or if `min_digits` + is given more can be printed, in which cases the last digit is rounded + with unbiased rounding. If `False`, digits are generated as if printing an infinite-precision value and stopping after `precision` digits, rounding the remaining - value. + value with unbiased rounding fractional : boolean, optional - If `True`, the cutoff of `precision` digits refers to the total number - of digits after the decimal point, including leading zeros. - If `False`, `precision` refers to the total number of significant - digits, before or after the decimal point, ignoring leading zeros. + If `True`, the cutoffs of `precision` and `min_digits` refer to the + total number of digits after the decimal point, including leading + zeros. + If `False`, `precision` and `min_digits` refer to the total number of + significant digits, before or after the decimal point, ignoring leading + zeros. trim : one of 'k', '.', '0', '-', optional Controls post-processing trimming of trailing digits, as follows: @@ -1076,6 +1177,12 @@ def format_float_positional(x, precision=None, unique=True, pad_right : non-negative integer, optional Pad the right side of the string with whitespace until at least that many characters are to the right of the decimal point. + min_digits : non-negative integer or None, optional + Minimum number of digits to print. Only has an effect if `unique=True` + in which case additional digits past those necessary to uniquely + identify the value may be printed, rounding the last additional digit. + + -- versionadded:: 1.21.0 Returns ------- @@ -1100,13 +1207,19 @@ def format_float_positional(x, precision=None, unique=True, precision = _none_or_positive_arg(precision, 'precision') pad_left = _none_or_positive_arg(pad_left, 'pad_left') pad_right = _none_or_positive_arg(pad_right, 'pad_right') + min_digits = _none_or_positive_arg(min_digits, 'min_digits') + if not fractional and precision == 0: + raise ValueError("precision must be greater than 0 if " + "fractional=False") + if min_digits > 0 and precision > 0 and min_digits > precision: + raise ValueError("min_digits must be less than or equal to precision") return dragon4_positional(x, precision=precision, unique=unique, fractional=fractional, trim=trim, sign=sign, pad_left=pad_left, - pad_right=pad_right) + pad_right=pad_right, min_digits=min_digits) -class IntegerFormat(object): +class IntegerFormat: def __init__(self, data): if data.size > 0: max_str_len = max(len(str(np.max(data))), @@ -1119,7 +1232,7 @@ def __call__(self, x): return self.format % x -class BoolFormat(object): +class BoolFormat: def __init__(self, data, **kwargs): # add an extra space so " True" and "False" have the same length and # array elements align nicely when printed, except in 0d arrays @@ -1129,23 +1242,27 @@ def __call__(self, x): return self.truestr if x else "False" -class ComplexFloatingFormat(object): +class ComplexFloatingFormat: """ Formatter for subtypes of np.complexfloating """ def __init__(self, x, precision, floatmode, suppress_small, - sign=False, **kwarg): + sign=False, *, legacy=None): # for backcompatibility, accept bools if isinstance(sign, bool): sign = '+' if sign else '-' floatmode_real = floatmode_imag = floatmode - if kwarg.get('legacy', False) == '1.13': + if legacy <= 113: floatmode_real = 'maxprec_equal' floatmode_imag = 'maxprec' - self.real_format = FloatingFormat(x.real, precision, floatmode_real, - suppress_small, sign=sign, **kwarg) - self.imag_format = FloatingFormat(x.imag, precision, floatmode_imag, - suppress_small, sign='+', **kwarg) + self.real_format = FloatingFormat( + x.real, precision, floatmode_real, suppress_small, + sign=sign, legacy=legacy + ) + self.imag_format = FloatingFormat( + x.imag, precision, floatmode_imag, suppress_small, + sign='+', legacy=legacy + ) def __call__(self, x): r = self.real_format(x.real) @@ -1157,23 +1274,8 @@ def __call__(self, x): return r + i -# for back-compatibility, we keep the classes for each complex type too -class ComplexFormat(ComplexFloatingFormat): - def __init__(self, *args, **kwargs): - warnings.warn( - "ComplexFormat has been replaced by ComplexFloatingFormat", - DeprecationWarning, stacklevel=2) - super(ComplexFormat, self).__init__(*args, **kwargs) - -class LongComplexFormat(ComplexFloatingFormat): - def __init__(self, *args, **kwargs): - warnings.warn( - "LongComplexFormat has been replaced by ComplexFloatingFormat", - DeprecationWarning, stacklevel=2) - super(LongComplexFormat, self).__init__(*args, **kwargs) - -class _TimelikeFormat(object): +class _TimelikeFormat: def __init__(self, data): non_nat = data[~isnat(data)] if len(non_nat) > 0: @@ -1217,12 +1319,12 @@ def __init__(self, x, unit=None, timezone=None, casting='same_kind', self.legacy = legacy # must be called after the above are configured - super(DatetimeFormat, self).__init__(x) + super().__init__(x) def __call__(self, x): - if self.legacy == '1.13': + if self.legacy <= 113: return self._format_non_nat(x) - return super(DatetimeFormat, self).__call__(x) + return super().__call__(x) def _format_non_nat(self, x): return "'%s'" % datetime_as_string(x, @@ -1236,7 +1338,7 @@ def _format_non_nat(self, x): return str(x.astype('i8')) -class SubArrayFormat(object): +class SubArrayFormat: def __init__(self, format_function): self.format_function = format_function @@ -1246,7 +1348,7 @@ def __call__(self, arr): return "[" + ", ".join(self.__call__(a) for a in arr) + "]" -class StructuredVoidFormat(object): +class StructuredVoidFormat: """ Formatter for structured np.void objects. @@ -1282,16 +1384,6 @@ def __call__(self, x): return "({})".format(", ".join(str_fields)) -# for backwards compatibility -class StructureFormat(StructuredVoidFormat): - def __init__(self, *args, **kwargs): - # NumPy 1.14, 2018-02-14 - warnings.warn( - "StructureFormat has been replaced by StructuredVoidFormat", - DeprecationWarning, stacklevel=2) - super(StructureFormat, self).__init__(*args, **kwargs) - - def _void_scalar_repr(x): """ Implements the repr for structured-void scalars. It is called from the @@ -1331,10 +1423,10 @@ def dtype_is_implied(dtype): >>> np.core.arrayprint.dtype_is_implied(np.int8) False >>> np.array([1, 2, 3], np.int8) - array([1, 2, 3], dtype=np.int8) + array([1, 2, 3], dtype=int8) """ dtype = np.dtype(dtype) - if _format_options['legacy'] == '1.13' and dtype.type == bool_: + if _format_options['legacy'] <= 113 and dtype.type == bool_: return False # not just void types can be structured, and names are not part of the repr @@ -1351,8 +1443,12 @@ def dtype_short_repr(dtype): The intent is roughly that the following holds >>> from numpy import * + >>> dt = np.int64([1, 2]).dtype >>> assert eval(dtype_short_repr(dt)) == dt """ + if type(dtype).__repr__ != np.dtype.__repr__: + # TODO: Custom repr for user DTypes, logic should likely move. + return repr(dtype) if dtype.names is not None: # structured dtypes give a list or tuple repr return str(dtype) @@ -1368,6 +1464,59 @@ def dtype_short_repr(dtype): return typename +def _array_repr_implementation( + arr, max_line_width=None, precision=None, suppress_small=None, + array2string=array2string): + """Internal version of array_repr() that allows overriding array2string.""" + if max_line_width is None: + max_line_width = _format_options['linewidth'] + + if type(arr) is not ndarray: + class_name = type(arr).__name__ + else: + class_name = "array" + + skipdtype = dtype_is_implied(arr.dtype) and arr.size > 0 + + prefix = class_name + "(" + suffix = ")" if skipdtype else "," + + if (_format_options['legacy'] <= 113 and + arr.shape == () and not arr.dtype.names): + lst = repr(arr.item()) + elif arr.size > 0 or arr.shape == (0,): + lst = array2string(arr, max_line_width, precision, suppress_small, + ', ', prefix, suffix=suffix) + else: # show zero-length shape unless it is (0,) + lst = "[], shape=%s" % (repr(arr.shape),) + + arr_str = prefix + lst + suffix + + if skipdtype: + return arr_str + + dtype_str = "dtype={})".format(dtype_short_repr(arr.dtype)) + + # compute whether we should put dtype on a new line: Do so if adding the + # dtype would extend the last line past max_line_width. + # Note: This line gives the correct result even when rfind returns -1. + last_line_len = len(arr_str) - (arr_str.rfind('\n') + 1) + spacer = " " + if _format_options['legacy'] <= 113: + if issubclass(arr.dtype.type, flexible): + spacer = '\n' + ' '*len(class_name + "(") + elif last_line_len + len(dtype_str) + 1 > max_line_width: + spacer = '\n' + ' '*len(class_name + "(") + + return arr_str + spacer + dtype_str + + +def _array_repr_dispatcher( + arr, max_line_width=None, precision=None, suppress_small=None): + return (arr,) + + +@array_function_dispatch(_array_repr_dispatcher, module='numpy') def array_repr(arr, max_line_width=None, precision=None, suppress_small=None): """ Return the string representation of an array. @@ -1377,15 +1526,17 @@ def array_repr(arr, max_line_width=None, precision=None, suppress_small=None): arr : ndarray Input array. max_line_width : int, optional - The maximum number of columns the string should span. Newline - characters split the string appropriately after array elements. + Inserts newlines if text is longer than `max_line_width`. + Defaults to ``numpy.get_printoptions()['linewidth']``. precision : int, optional - Floating point precision. Default is the current printing precision - (usually 8), which can be altered using `set_printoptions`. + Floating point precision. + Defaults to ``numpy.get_printoptions()['precision']``. suppress_small : bool, optional - Represent very small numbers as zero, default is False. Very small - is defined by `precision`, if the precision is 8 then - numbers smaller than 5e-9 are represented as zero. + Represent numbers "very close" to zero as zero; default is False. + Very close is defined by precision: if the precision is 8, e.g., + numbers smaller (in absolute value) than 5e-9 are represented as + zero. + Defaults to ``numpy.get_printoptions()['suppress']``. Returns ------- @@ -1401,59 +1552,52 @@ def array_repr(arr, max_line_width=None, precision=None, suppress_small=None): >>> np.array_repr(np.array([1,2])) 'array([1, 2])' >>> np.array_repr(np.ma.array([0.])) - 'MaskedArray([ 0.])' + 'MaskedArray([0.])' >>> np.array_repr(np.array([], np.int32)) 'array([], dtype=int32)' >>> x = np.array([1e-6, 4e-7, 2, 3]) >>> np.array_repr(x, precision=6, suppress_small=True) - 'array([ 0.000001, 0. , 2. , 3. ])' + 'array([0.000001, 0. , 2. , 3. ])' """ - if max_line_width is None: - max_line_width = _format_options['linewidth'] + return _array_repr_implementation( + arr, max_line_width, precision, suppress_small) - if type(arr) is not ndarray: - class_name = type(arr).__name__ - else: - class_name = "array" - skipdtype = dtype_is_implied(arr.dtype) and arr.size > 0 - - prefix = class_name + "(" - suffix = ")" if skipdtype else "," +@_recursive_guard() +def _guarded_repr_or_str(v): + if isinstance(v, bytes): + return repr(v) + return str(v) - if (_format_options['legacy'] == '1.13' and - arr.shape == () and not arr.dtype.names): - lst = repr(arr.item()) - elif arr.size > 0 or arr.shape == (0,): - lst = array2string(arr, max_line_width, precision, suppress_small, - ', ', prefix, suffix=suffix) - else: # show zero-length shape unless it is (0,) - lst = "[], shape=%s" % (repr(arr.shape),) - arr_str = prefix + lst + suffix +def _array_str_implementation( + a, max_line_width=None, precision=None, suppress_small=None, + array2string=array2string): + """Internal version of array_str() that allows overriding array2string.""" + if (_format_options['legacy'] <= 113 and + a.shape == () and not a.dtype.names): + return str(a.item()) - if skipdtype: - return arr_str + # the str of 0d arrays is a special case: It should appear like a scalar, + # so floats are not truncated by `precision`, and strings are not wrapped + # in quotes. So we return the str of the scalar value. + if a.shape == (): + # obtain a scalar and call str on it, avoiding problems for subclasses + # for which indexing with () returns a 0d instead of a scalar by using + # ndarray's getindex. Also guard against recursive 0d object arrays. + return _guarded_repr_or_str(np.ndarray.__getitem__(a, ())) - dtype_str = "dtype={})".format(dtype_short_repr(arr.dtype)) + return array2string(a, max_line_width, precision, suppress_small, ' ', "") - # compute whether we should put dtype on a new line: Do so if adding the - # dtype would extend the last line past max_line_width. - # Note: This line gives the correct result even when rfind returns -1. - last_line_len = len(arr_str) - (arr_str.rfind('\n') + 1) - spacer = " " - if _format_options['legacy'] == '1.13': - if issubclass(arr.dtype.type, flexible): - spacer = '\n' + ' '*len(class_name + "(") - elif last_line_len + len(dtype_str) + 1 > max_line_width: - spacer = '\n' + ' '*len(class_name + "(") - return arr_str + spacer + dtype_str +def _array_str_dispatcher( + a, max_line_width=None, precision=None, suppress_small=None): + return (a,) -_guarded_str = _recursive_guard()(str) +@array_function_dispatch(_array_str_dispatcher, module='numpy') def array_str(a, max_line_width=None, precision=None, suppress_small=None): """ Return a string representation of the data in an array. @@ -1467,16 +1611,17 @@ def array_str(a, max_line_width=None, precision=None, suppress_small=None): a : ndarray Input array. max_line_width : int, optional - Inserts newlines if text is longer than `max_line_width`. The - default is, indirectly, 75. + Inserts newlines if text is longer than `max_line_width`. + Defaults to ``numpy.get_printoptions()['linewidth']``. precision : int, optional - Floating point precision. Default is the current printing precision - (usually 8), which can be altered using `set_printoptions`. + Floating point precision. + Defaults to ``numpy.get_printoptions()['precision']``. suppress_small : bool, optional Represent numbers "very close" to zero as zero; default is False. Very close is defined by precision: if the precision is 8, e.g., numbers smaller (in absolute value) than 5e-9 are represented as zero. + Defaults to ``numpy.get_printoptions()['suppress']``. See Also -------- @@ -1488,20 +1633,17 @@ def array_str(a, max_line_width=None, precision=None, suppress_small=None): '[0 1 2]' """ - if (_format_options['legacy'] == '1.13' and - a.shape == () and not a.dtype.names): - return str(a.item()) + return _array_str_implementation( + a, max_line_width, precision, suppress_small) - # the str of 0d arrays is a special case: It should appear like a scalar, - # so floats are not truncated by `precision`, and strings are not wrapped - # in quotes. So we return the str of the scalar value. - if a.shape == (): - # obtain a scalar and call str on it, avoiding problems for subclasses - # for which indexing with () returns a 0d instead of a scalar by using - # ndarray's getindex. Also guard against recursive 0d object arrays. - return _guarded_str(np.ndarray.__getitem__(a, ())) - return array2string(a, max_line_width, precision, suppress_small, ' ', "") +# needed if __array_function__ is disabled +_array2string_impl = getattr(array2string, '__wrapped__', array2string) +_default_array_str = functools.partial(_array_str_implementation, + array2string=_array2string_impl) +_default_array_repr = functools.partial(_array_repr_implementation, + array2string=_array2string_impl) + def set_string_function(f, repr=True): """ @@ -1532,8 +1674,8 @@ def set_string_function(f, repr=True): >>> a = np.arange(10) >>> a HA! - What are you going to do now? - >>> print(a) - [0 1 2 3 4 5 6 7 8 9] + >>> _ = a + >>> # [0 1 2 3 4 5 6 7 8 9] We can reset the function to the default: @@ -1551,16 +1693,13 @@ def set_string_function(f, repr=True): >>> x.__str__() 'random' >>> x.__repr__() - 'array([ 0, 1, 2, 3])' + 'array([0, 1, 2, 3])' """ if f is None: if repr: - return multiarray.set_string_function(array_repr, 1) + return multiarray.set_string_function(_default_array_repr, 1) else: - return multiarray.set_string_function(array_str, 0) + return multiarray.set_string_function(_default_array_str, 0) else: return multiarray.set_string_function(f, repr) - -set_string_function(array_str, 0) -set_string_function(array_repr, 1) diff --git a/numpy/core/arrayprint.pyi b/numpy/core/arrayprint.pyi new file mode 100644 index 000000000000..0d338206f604 --- /dev/null +++ b/numpy/core/arrayprint.pyi @@ -0,0 +1,141 @@ +from types import TracebackType +from typing import Any, Optional, Callable, Union, Type, Literal, TypedDict, SupportsIndex + +# Using a private class is by no means ideal, but it is simply a consequence +# of a `contextlib.context` returning an instance of aforementioned class +from contextlib import _GeneratorContextManager + +from numpy import ( + ndarray, + generic, + bool_, + integer, + timedelta64, + datetime64, + floating, + complexfloating, + void, + str_, + bytes_, + longdouble, + clongdouble, +) +from numpy.typing import ArrayLike, _CharLike_co, _FloatLike_co + +_FloatMode = Literal["fixed", "unique", "maxprec", "maxprec_equal"] + +class _FormatDict(TypedDict, total=False): + bool: Callable[[bool_], str] + int: Callable[[integer[Any]], str] + timedelta: Callable[[timedelta64], str] + datetime: Callable[[datetime64], str] + float: Callable[[floating[Any]], str] + longfloat: Callable[[longdouble], str] + complexfloat: Callable[[complexfloating[Any, Any]], str] + longcomplexfloat: Callable[[clongdouble], str] + void: Callable[[void], str] + numpystr: Callable[[_CharLike_co], str] + object: Callable[[object], str] + all: Callable[[object], str] + int_kind: Callable[[integer[Any]], str] + float_kind: Callable[[floating[Any]], str] + complex_kind: Callable[[complexfloating[Any, Any]], str] + str_kind: Callable[[_CharLike_co], str] + +class _FormatOptions(TypedDict): + precision: int + threshold: int + edgeitems: int + linewidth: int + suppress: bool + nanstr: str + infstr: str + formatter: Optional[_FormatDict] + sign: Literal["-", "+", " "] + floatmode: _FloatMode + legacy: Literal[False, "1.13", "1.21"] + +def set_printoptions( + precision: Optional[SupportsIndex] = ..., + threshold: Optional[int] = ..., + edgeitems: Optional[int] = ..., + linewidth: Optional[int] = ..., + suppress: Optional[bool] = ..., + nanstr: Optional[str] = ..., + infstr: Optional[str] = ..., + formatter: Optional[_FormatDict] = ..., + sign: Optional[Literal["-", "+", " "]] = ..., + floatmode: Optional[_FloatMode] = ..., + *, + legacy: Optional[Literal[False, "1.13", "1.21"]] = ... +) -> None: ... +def get_printoptions() -> _FormatOptions: ... +def array2string( + a: ndarray[Any, Any], + max_line_width: Optional[int] = ..., + precision: Optional[SupportsIndex] = ..., + suppress_small: Optional[bool] = ..., + separator: str = ..., + prefix: str = ..., + # NOTE: With the `style` argument being deprecated, + # all arguments between `formatter` and `suffix` are de facto + # keyworld-only arguments + *, + formatter: Optional[_FormatDict] = ..., + threshold: Optional[int] = ..., + edgeitems: Optional[int] = ..., + sign: Optional[Literal["-", "+", " "]] = ..., + floatmode: Optional[_FloatMode] = ..., + suffix: str = ..., + legacy: Optional[Literal[False, "1.13", "1.21"]] = ..., +) -> str: ... +def format_float_scientific( + x: _FloatLike_co, + precision: Optional[int] = ..., + unique: bool = ..., + trim: Literal["k", ".", "0", "-"] = ..., + sign: bool = ..., + pad_left: Optional[int] = ..., + exp_digits: Optional[int] = ..., + min_digits: Optional[int] = ..., +) -> str: ... +def format_float_positional( + x: _FloatLike_co, + precision: Optional[int] = ..., + unique: bool = ..., + fractional: bool = ..., + trim: Literal["k", ".", "0", "-"] = ..., + sign: bool = ..., + pad_left: Optional[int] = ..., + pad_right: Optional[int] = ..., + min_digits: Optional[int] = ..., +) -> str: ... +def array_repr( + arr: ndarray[Any, Any], + max_line_width: Optional[int] = ..., + precision: Optional[SupportsIndex] = ..., + suppress_small: Optional[bool] = ..., +) -> str: ... +def array_str( + a: ndarray[Any, Any], + max_line_width: Optional[int] = ..., + precision: Optional[SupportsIndex] = ..., + suppress_small: Optional[bool] = ..., +) -> str: ... +def set_string_function( + f: Optional[Callable[[ndarray[Any, Any]], str]], repr: bool = ... +) -> None: ... +def printoptions( + precision: Optional[SupportsIndex] = ..., + threshold: Optional[int] = ..., + edgeitems: Optional[int] = ..., + linewidth: Optional[int] = ..., + suppress: Optional[bool] = ..., + nanstr: Optional[str] = ..., + infstr: Optional[str] = ..., + formatter: Optional[_FormatDict] = ..., + sign: Optional[Literal["-", "+", " "]] = ..., + floatmode: Optional[_FloatMode] = ..., + *, + legacy: Optional[Literal[False, "1.13", "1.21"]] = ... +) -> _GeneratorContextManager[_FormatOptions]: ... diff --git a/numpy/core/code_generators/__init__.py b/numpy/core/code_generators/__init__.py index 1d0f69b67d8f..e69de29bb2d1 100644 --- a/numpy/core/code_generators/__init__.py +++ b/numpy/core/code_generators/__init__.py @@ -1 +0,0 @@ -from __future__ import division, absolute_import, print_function diff --git a/numpy/core/code_generators/cversions.txt b/numpy/core/code_generators/cversions.txt index cc6c3a5fbf6d..e7b3ef697edc 100644 --- a/numpy/core/code_generators/cversions.txt +++ b/numpy/core/code_generators/cversions.txt @@ -1,6 +1,8 @@ # Hash below were defined from numpy_api_order.txt and ufunc_api_order.txt # When adding a new version here for a new minor release, also add the same -# version as NPY_x_y_API_VERSION in numpyconfig.h +# version as NPY_x_y_API_VERSION in numpyconfig.h and C_API_VERSION in +# setup_common.py. + 0x00000001 = 603580d224763e58c5e7147f804dc0f5 0x00000002 = 8ecb29306758515ae69749c803a75da1 0x00000003 = bf22c0d05b31625d2a7015988d61ce5a @@ -40,7 +42,23 @@ # Version 12 (NumPy 1.14) Added PyArray_ResolveWritebackIfCopy, # PyArray_SetWritebackIfCopyBase and deprecated PyArray_SetUpdateIfCopyBase. +# Version 12 (NumPy 1.15) No change. 0x0000000c = a1bc756c5782853ec2e3616cf66869d8 -# Version 13 (NumPy 1.15) Added NpyIter_Close -0x0000000d = 4386e829d65aafce6bd09a85b142d585 +# Version 13 (NumPy 1.16) +# Deprecate PyArray_SetNumericOps and PyArray_GetNumericOps, +# Add fields core_dim_flags and core_dim_sizes to PyUFuncObject. +# Add PyUFunc_FromFuncAndDataAndSignatureAndIdentity to ufunc_funcs_api. +# Version 13 (NumPy 1.17) No change. +# Version 13 (NumPy 1.18) No change. +# Version 13 (NumPy 1.19) No change. +0x0000000d = 5b0e8bbded00b166125974fc71e80a33 + +# Version 14 (NumPy 1.20) +# DType related API additions. +# A new field was added to the end of PyArrayObject_fields. +# Version 14 (NumPy 1.21) No change. +0x0000000e = 17a0f366e55ec05e5c5c149123478452 + +# Version 15 (NumPy 1.22) Configurable memory allocations +0x0000000f = b8783365b873681cd204be50cdfb448d diff --git a/numpy/core/code_generators/genapi.py b/numpy/core/code_generators/genapi.py index 42c564a978ca..b401ee6a581e 100644 --- a/numpy/core/code_generators/genapi.py +++ b/numpy/core/code_generators/genapi.py @@ -6,11 +6,13 @@ specified. """ -from __future__ import division, absolute_import, print_function +from numpy.distutils.conv_template import process_file as process_c_file -import sys, os, re import hashlib - +import io +import os +import re +import sys import textwrap from os.path import join @@ -19,12 +21,17 @@ # The files under src/ that are scanned for API functions API_FILES = [join('multiarray', 'alloc.c'), + join('multiarray', 'abstractdtypes.c'), + join('multiarray', 'arrayfunction_override.c'), join('multiarray', 'array_assign_array.c'), join('multiarray', 'array_assign_scalar.c'), + join('multiarray', 'array_coercion.c'), + join('multiarray', 'array_method.c'), join('multiarray', 'arrayobject.c'), join('multiarray', 'arraytypes.c.src'), join('multiarray', 'buffer.c'), join('multiarray', 'calculation.c'), + join('multiarray', 'common_dtype.c'), join('multiarray', 'conversion_utils.c'), join('multiarray', 'convert.c'), join('multiarray', 'convert_datatype.c'), @@ -34,6 +41,8 @@ join('multiarray', 'datetime_busdaycal.c'), join('multiarray', 'datetime_strings.c'), join('multiarray', 'descriptor.c'), + join('multiarray', 'dlpack.c'), + join('multiarray', 'dtypemeta.c'), join('multiarray', 'einsum.c.src'), join('multiarray', 'flagsobject.c'), join('multiarray', 'getset.c'), @@ -72,7 +81,7 @@ def _repl(str): return str.replace('Bool', 'npy_bool') -class StealRef(object): +class StealRef: def __init__(self, arg): self.arg = arg # counting from 1 @@ -83,7 +92,7 @@ def __str__(self): return 'NPY_STEALS_REF_TO_ARG(%d)' % self.arg -class NonNull(object): +class NonNull: def __init__(self, arg): self.arg = arg # counting from 1 @@ -94,7 +103,7 @@ def __str__(self): return 'NPY_GCC_NONNULL(%d)' % self.arg -class Function(object): +class Function: def __init__(self, name, return_type, args, doc=''): self.name = name self.return_type = _repl(return_type) @@ -163,14 +172,12 @@ def skip_brackets(s, lbrac, rbrac): def split_arguments(argstr): arguments = [] - bracket_counts = {'(': 0, '[': 0} current_argument = [] - state = 0 i = 0 def finish_arg(): if current_argument: argstr = ''.join(current_argument).strip() - m = re.match(r'(.*(\s+|[*]))(\w+)$', argstr) + m = re.match(r'(.*(\s+|\*))(\w+)$', argstr) if m: typename = m.group(1).strip() name = m.group(3) @@ -216,7 +223,10 @@ def find_functions(filename, tag='API'): This function does foo... */ """ - fo = open(filename, 'r') + if filename.endswith(('.c.src', '.h.src')): + fo = io.StringIO(process_c_file(filename)) + else: + fo = open(filename, 'r') functions = [] return_type = None function_name = None @@ -260,7 +270,8 @@ def find_functions(filename, tag='API'): elif state == STATE_ARGS: if line.startswith('{'): # finished - fargs_str = ' '.join(function_args).rstrip(' )') + # remove any white space and the closing bracket: + fargs_str = ' '.join(function_args).rstrip()[:-1].rstrip() fargs = split_arguments(fargs_str) f = Function(function_name, return_type, fargs, '\n'.join(doclist)) @@ -272,9 +283,11 @@ def find_functions(filename, tag='API'): state = SCANNING else: function_args.append(line) - except Exception: - print(filename, lineno + 1) + except ParseError: raise + except Exception as e: + msg = "see chained exception for details" + raise ParseError(filename, lineno + 1, msg) from e fo.close() return functions @@ -303,12 +316,14 @@ def write_file(filename, data): # Those *Api classes instances know how to output strings for the generated code -class TypeApi(object): - def __init__(self, name, index, ptr_cast, api_name): +class TypeApi: + def __init__(self, name, index, ptr_cast, api_name, internal_type=None): self.index = index self.name = name self.ptr_cast = ptr_cast self.api_name = api_name + # The type used internally, if None, same as exported (ptr_cast) + self.internal_type = internal_type def define_from_array_api_string(self): return "#define %s (*(%s *)%s[%d])" % (self.name, @@ -320,12 +335,22 @@ def array_api_define(self): return " (void *) &%s" % self.name def internal_define(self): - astr = """\ -extern NPY_NO_EXPORT PyTypeObject %(type)s; -""" % {'type': self.name} + if self.internal_type is None: + return f"extern NPY_NO_EXPORT {self.ptr_cast} {self.name};\n" + + # If we are here, we need to define a larger struct internally, which + # the type can be cast safely. But we want to normally use the original + # type, so name mangle: + mangled_name = f"{self.name}Full" + astr = ( + # Create the mangled name: + f"extern NPY_NO_EXPORT {self.internal_type} {mangled_name};\n" + # And define the name as: (*(type *)(&mangled_name)) + f"#define {self.name} (*({self.ptr_cast} *)(&{mangled_name}))\n" + ) return astr -class GlobalVarApi(object): +class GlobalVarApi: def __init__(self, name, index, type, api_name): self.name = name self.index = index @@ -349,7 +374,7 @@ def internal_define(self): # Dummy to be able to consistently use *Api instances for all items in the # array api -class BoolValuesApi(object): +class BoolValuesApi: def __init__(self, name, index, api_name): self.name = name self.index = index @@ -371,7 +396,7 @@ def internal_define(self): """ return astr -class FunctionApi(object): +class FunctionApi: def __init__(self, name, index, annotations, return_type, args, api_name): self.name = name self.index = index @@ -400,9 +425,7 @@ def array_api_define(self): return " (void *) %s" % self.name def internal_define(self): - annstr = [] - for a in self.annotations: - annstr.append(str(a)) + annstr = [str(a) for a in self.annotations] annstr = ' '.join(annstr) astr = """\ NPY_NO_EXPORT %s %s %s \\\n (%s);""" % (annstr, self.return_type, @@ -463,10 +486,7 @@ def get_api_functions(tagname, api_dict): functions = [] for f in API_FILES: functions.extend(find_functions(f, tagname)) - dfunctions = [] - for func in functions: - o = api_dict[func.name][0] - dfunctions.append( (o, func) ) + dfunctions = [(api_dict[func.name][0], func) for func in functions] dfunctions.sort() return [a[1] for a in dfunctions] @@ -489,14 +509,11 @@ def get_versions_hash(): d = [] file = os.path.join(os.path.dirname(__file__), 'cversions.txt') - fid = open(file, 'r') - try: + with open(file, 'r') as fid: for line in fid: m = VERRE.match(line) if m: d.append((int(m.group(1), 16), m.group(2))) - finally: - fid.close() return dict(d) diff --git a/numpy/core/code_generators/generate_numpy_api.py b/numpy/core/code_generators/generate_numpy_api.py index b4aeaa277fd8..37975966f625 100644 --- a/numpy/core/code_generators/generate_numpy_api.py +++ b/numpy/core/code_generators/generate_numpy_api.py @@ -1,5 +1,3 @@ -from __future__ import division, print_function - import os import genapi @@ -46,11 +44,10 @@ _import_array(void) { int st; - PyObject *numpy = PyImport_ImportModule("numpy.core.multiarray"); + PyObject *numpy = PyImport_ImportModule("numpy.core._multiarray_umath"); PyObject *c_api = NULL; if (numpy == NULL) { - PyErr_SetString(PyExc_ImportError, "numpy.core.multiarray failed to import"); return -1; } c_api = PyObject_GetAttrString(numpy, "_ARRAY_API"); @@ -60,21 +57,12 @@ return -1; } -#if PY_VERSION_HEX >= 0x03000000 if (!PyCapsule_CheckExact(c_api)) { PyErr_SetString(PyExc_RuntimeError, "_ARRAY_API is not PyCapsule object"); Py_DECREF(c_api); return -1; } PyArray_API = (void **)PyCapsule_GetPointer(c_api, NULL); -#else - if (!PyCObject_Check(c_api)) { - PyErr_SetString(PyExc_RuntimeError, "_ARRAY_API is not PyCObject object"); - Py_DECREF(c_api); - return -1; - } - PyArray_API = (void **)PyCObject_AsVoidPtr(c_api); -#endif Py_DECREF(c_api); if (PyArray_API == NULL) { PyErr_SetString(PyExc_RuntimeError, "_ARRAY_API is NULL pointer"); @@ -121,13 +109,7 @@ return 0; } -#if PY_VERSION_HEX >= 0x03000000 -#define NUMPY_IMPORT_ARRAY_RETVAL NULL -#else -#define NUMPY_IMPORT_ARRAY_RETVAL -#endif - -#define import_array() {if (_import_array() < 0) {PyErr_Print(); PyErr_SetString(PyExc_ImportError, "numpy.core.multiarray failed to import"); return NUMPY_IMPORT_ARRAY_RETVAL; } } +#define import_array() {if (_import_array() < 0) {PyErr_Print(); PyErr_SetString(PyExc_ImportError, "numpy.core.multiarray failed to import"); return NULL; } } #define import_array1(ret) {if (_import_array() < 0) {PyErr_Print(); PyErr_SetString(PyExc_ImportError, "numpy.core.multiarray failed to import"); return ret; } } @@ -193,8 +175,7 @@ def do_generate_api(targets, sources): genapi.check_api_dict(multiarray_api_index) numpyapi_list = genapi.get_api_functions('NUMPY_API', - multiarray_funcs) - ordered_funcs_api = genapi.order_dict(multiarray_funcs) + multiarray_funcs) # Create dict name -> *Api instance api_name = 'PyArray_API' @@ -217,7 +198,9 @@ def do_generate_api(targets, sources): for name, val in types_api.items(): index = val[0] - multiarray_api_dict[name] = TypeApi(name, index, 'PyTypeObject', api_name) + internal_type = None if len(val) == 1 else val[1] + multiarray_api_dict[name] = TypeApi( + name, index, 'PyTypeObject', api_name, internal_type) if len(multiarray_api_dict) != len(multiarray_api_index): keys_dict = set(multiarray_api_dict.keys()) diff --git a/numpy/core/code_generators/generate_ufunc_api.py b/numpy/core/code_generators/generate_ufunc_api.py index 3bcf137f7c38..04c023675fae 100644 --- a/numpy/core/code_generators/generate_ufunc_api.py +++ b/numpy/core/code_generators/generate_ufunc_api.py @@ -1,12 +1,9 @@ -from __future__ import division, print_function - import os import genapi import numpy_api -from genapi import \ - TypeApi, GlobalVarApi, FunctionApi, BoolValuesApi +from genapi import TypeApi, FunctionApi h_template = r""" #ifdef _UMATHMODULE @@ -36,11 +33,12 @@ static NPY_INLINE int _import_umath(void) { - PyObject *numpy = PyImport_ImportModule("numpy.core.umath"); + PyObject *numpy = PyImport_ImportModule("numpy.core._multiarray_umath"); PyObject *c_api = NULL; if (numpy == NULL) { - PyErr_SetString(PyExc_ImportError, "numpy.core.umath failed to import"); + PyErr_SetString(PyExc_ImportError, + "numpy.core._multiarray_umath failed to import"); return -1; } c_api = PyObject_GetAttrString(numpy, "_UFUNC_API"); @@ -50,21 +48,12 @@ return -1; } -#if PY_VERSION_HEX >= 0x03000000 if (!PyCapsule_CheckExact(c_api)) { PyErr_SetString(PyExc_RuntimeError, "_UFUNC_API is not PyCapsule object"); Py_DECREF(c_api); return -1; } PyUFunc_API = (void **)PyCapsule_GetPointer(c_api, NULL); -#else - if (!PyCObject_Check(c_api)) { - PyErr_SetString(PyExc_RuntimeError, "_UFUNC_API is not PyCObject object"); - Py_DECREF(c_api); - return -1; - } - PyUFunc_API = (void **)PyCObject_AsVoidPtr(c_api); -#endif Py_DECREF(c_api); if (PyUFunc_API == NULL) { PyErr_SetString(PyExc_RuntimeError, "_UFUNC_API is NULL pointer"); @@ -73,12 +62,6 @@ return 0; } -#if PY_VERSION_HEX >= 0x03000000 -#define NUMPY_IMPORT_UMATH_RETVAL NULL -#else -#define NUMPY_IMPORT_UMATH_RETVAL -#endif - #define import_umath() \ do {\ UFUNC_NOFPE\ @@ -86,7 +69,7 @@ PyErr_Print();\ PyErr_SetString(PyExc_ImportError,\ "numpy.core.umath failed to import");\ - return NUMPY_IMPORT_UMATH_RETVAL;\ + return NULL;\ }\ } while(0) diff --git a/numpy/core/code_generators/generate_umath.py b/numpy/core/code_generators/generate_umath.py index 632bcb41f162..292d9e0d37e2 100644 --- a/numpy/core/code_generators/generate_umath.py +++ b/numpy/core/code_generators/generate_umath.py @@ -1,5 +1,3 @@ -from __future__ import division, print_function - import os import re import struct @@ -10,24 +8,27 @@ import ufunc_docstrings as docstrings sys.path.pop(0) -Zero = "PyUFunc_Zero" -One = "PyUFunc_One" -None_ = "PyUFunc_None" -AllOnes = "PyUFunc_MinusOne" -ReorderableNone = "PyUFunc_ReorderableNone" +Zero = "PyLong_FromLong(0)" +One = "PyLong_FromLong(1)" +True_ = "(Py_INCREF(Py_True), Py_True)" +False_ = "(Py_INCREF(Py_False), Py_False)" +None_ = object() +AllOnes = "PyLong_FromLong(-1)" +MinusInfinity = 'PyFloat_FromDouble(-NPY_INFINITY)' +ReorderableNone = "(Py_INCREF(Py_None), Py_None)" # Sentinel value to specify using the full type description in the # function name -class FullTypeDescr(object): +class FullTypeDescr: pass -class FuncNameSuffix(object): +class FuncNameSuffix: """Stores the suffix to append when generating functions names. """ def __init__(self, suffix): self.suffix = suffix -class TypeDescription(object): +class TypeDescription: """Type signature for a ufunc. Attributes @@ -44,11 +45,20 @@ class TypeDescription(object): astype : dict or None, optional If astype['x'] is 'y', uses PyUFunc_x_x_As_y_y/PyUFunc_xx_x_As_yy_y instead of PyUFunc_x_x/PyUFunc_xx_x. + cfunc_alias : str or none, optional + Appended to inner loop C function name, e.g., FLOAT_{cfunc_alias}. See make_arrays. + NOTE: it doesn't support 'astype' simd: list Available SIMD ufunc loops, dispatched at runtime in specified order Currently only supported for simples types (see make_arrays) + dispatch: str or None, optional + Dispatch-able source name without its extension '.dispatch.c' that + contains the definition of ufunc, dispatched at runtime depending on the + specified targets of the dispatch-able source. + NOTE: it doesn't support 'astype' """ - def __init__(self, type, f=None, in_=None, out=None, astype=None, simd=None): + def __init__(self, type, f=None, in_=None, out=None, astype=None, cfunc_alias=None, + simd=None, dispatch=None): self.type = type self.func_data = f if astype is None: @@ -60,7 +70,9 @@ def __init__(self, type, f=None, in_=None, out=None, astype=None, simd=None): if out is not None: out = out.replace('P', type) self.out = out + self.cfunc_alias = cfunc_alias self.simd = simd + self.dispatch = dispatch def finish_signature(self, nin, nout): if self.in_ is None: @@ -71,16 +83,22 @@ def finish_signature(self, nin, nout): assert len(self.out) == nout self.astype = self.astype_dict.get(self.type, None) -_fdata_map = dict(e='npy_%sf', f='npy_%sf', d='npy_%s', g='npy_%sl', - F='nc_%sf', D='nc_%s', G='nc_%sl') +_fdata_map = dict( + e='npy_%sf', + f='npy_%sf', + d='npy_%s', + g='npy_%sl', + F='nc_%sf', + D='nc_%s', + G='nc_%sl' +) + def build_func_data(types, f): - func_data = [] - for t in types: - d = _fdata_map.get(t, '%s') % (f,) - func_data.append(d) + func_data = [_fdata_map.get(t, '%s') % (f,) for t in types] return func_data -def TD(types, f=None, astype=None, in_=None, out=None, simd=None): +def TD(types, f=None, astype=None, in_=None, out=None, cfunc_alias=None, + simd=None, dispatch=None): if f is not None: if isinstance(f, str): func_data = build_func_data(types, f) @@ -109,10 +127,19 @@ def TD(types, f=None, astype=None, in_=None, out=None, simd=None): simdt = [k for k, v in simd if t in v] else: simdt = [] - tds.append(TypeDescription(t, f=fd, in_=i, out=o, astype=astype, simd=simdt)) + + # [(dispatch file name without extension '.dispatch.c*', list of types)] + if dispatch: + dispt = ([k for k, v in dispatch if t in v]+[None])[0] + else: + dispt = None + tds.append(TypeDescription( + t, f=fd, in_=i, out=o, astype=astype, cfunc_alias=cfunc_alias, + simd=simdt, dispatch=dispt + )) return tds -class Ufunc(object): +class Ufunc: """Description of a ufunc. Attributes @@ -124,7 +151,7 @@ class Ufunc(object): type_descriptions : list of TypeDescription objects """ def __init__(self, nin, nout, identity, docstring, typereso, - *type_descriptions): + *type_descriptions, signature=None): self.nin = nin self.nout = nout if identity is None: @@ -133,6 +160,7 @@ def __init__(self, nin, nout, identity, docstring, typereso, self.docstring = docstring self.typereso = typereso self.type_descriptions = [] + self.signature = signature for td in type_descriptions: self.type_descriptions.extend(td) for td in self.type_descriptions: @@ -141,12 +169,8 @@ def __init__(self, nin, nout, identity, docstring, typereso, # String-handling utilities to avoid locale-dependence. import string -if sys.version_info[0] < 3: - UPPER_TABLE = string.maketrans(string.ascii_lowercase, - string.ascii_uppercase) -else: - UPPER_TABLE = bytes.maketrans(bytes(string.ascii_lowercase, "ascii"), - bytes(string.ascii_uppercase, "ascii")) +UPPER_TABLE = bytes.maketrans(bytes(string.ascii_lowercase, "ascii"), + bytes(string.ascii_uppercase, "ascii")) def english_upper(s): """ Apply English case rules to convert ASCII strings to all upper case. @@ -188,36 +212,41 @@ def english_upper(s): # output specification (optional) # ] -chartoname = {'?': 'bool', - 'b': 'byte', - 'B': 'ubyte', - 'h': 'short', - 'H': 'ushort', - 'i': 'int', - 'I': 'uint', - 'l': 'long', - 'L': 'ulong', - 'q': 'longlong', - 'Q': 'ulonglong', - 'e': 'half', - 'f': 'float', - 'd': 'double', - 'g': 'longdouble', - 'F': 'cfloat', - 'D': 'cdouble', - 'G': 'clongdouble', - 'M': 'datetime', - 'm': 'timedelta', - 'O': 'OBJECT', - # '.' is like 'O', but calls a method of the object instead - # of a function - 'P': 'OBJECT', - } +chartoname = { + '?': 'bool', + 'b': 'byte', + 'B': 'ubyte', + 'h': 'short', + 'H': 'ushort', + 'i': 'int', + 'I': 'uint', + 'l': 'long', + 'L': 'ulong', + 'q': 'longlong', + 'Q': 'ulonglong', + 'e': 'half', + 'f': 'float', + 'd': 'double', + 'g': 'longdouble', + 'F': 'cfloat', + 'D': 'cdouble', + 'G': 'clongdouble', + 'M': 'datetime', + 'm': 'timedelta', + 'O': 'OBJECT', + # '.' is like 'O', but calls a method of the object instead + # of a function + 'P': 'OBJECT', +} + +noobj = '?bBhHiIlLqQefdgFDGmM' +all = '?bBhHiIlLqQefdgFDGOmM' -all = '?bBhHiIlLqQefdgFDGOMm' O = 'O' P = 'P' ints = 'bBhHiIlLqQ' +sints = 'bhilq' +uints = 'BHILQ' times = 'Mm' timedeltaonly = 'm' intsO = ints + O @@ -227,6 +256,7 @@ def english_upper(s): fltsO = flts + O fltsP = flts + P cmplx = 'FDG' +cmplxvec = 'FD' cmplxO = cmplx + O cmplxP = cmplx + P inexact = flts + cmplx @@ -234,10 +264,8 @@ def english_upper(s): noint = inexact+O nointP = inexact+P allP = bints+times+flts+cmplxP -nobool = all[1:] -noobj = all[:-3]+all[-2:] -nobool_or_obj = all[1:-3]+all[-2:] -nobool_or_datetime = all[1:-2]+all[-1:] +nobool_or_obj = noobj[1:] +nobool_or_datetime = noobj[1:-1] + O # includes m - timedelta64 intflt = ints+flts intfltcmplx = ints+flts+cmplx nocmplx = bints+times+flts @@ -264,7 +292,7 @@ def english_upper(s): Ufunc(2, 1, Zero, docstrings.get('numpy.core.umath.add'), 'PyUFunc_AdditionTypeResolver', - TD(notimes_or_obj, simd=[('avx2', ints)]), + TD(notimes_or_obj, simd=[('avx2', ints)], dispatch=[('loops_arithm_fp', 'fdFD')]), [TypeDescription('M', FullTypeDescr, 'Mm', 'M'), TypeDescription('m', FullTypeDescr, 'mm', 'm'), TypeDescription('M', FullTypeDescr, 'mM', 'M'), @@ -275,7 +303,7 @@ def english_upper(s): Ufunc(2, 1, None, # Zero is only a unit to the right, not the left docstrings.get('numpy.core.umath.subtract'), 'PyUFunc_SubtractionTypeResolver', - TD(notimes_or_obj, simd=[('avx2', ints)]), + TD(ints + inexact, simd=[('avx2', ints)], dispatch=[('loops_arithm_fp', 'fdFD')]), [TypeDescription('M', FullTypeDescr, 'Mm', 'M'), TypeDescription('m', FullTypeDescr, 'mm', 'm'), TypeDescription('M', FullTypeDescr, 'MM', 'm'), @@ -286,7 +314,7 @@ def english_upper(s): Ufunc(2, 1, One, docstrings.get('numpy.core.umath.multiply'), 'PyUFunc_MultiplicationTypeResolver', - TD(notimes_or_obj, simd=[('avx2', ints)]), + TD(notimes_or_obj, simd=[('avx2', ints)], dispatch=[('loops_arithm_fp', 'fdFD')]), [TypeDescription('m', FullTypeDescr, 'mq', 'm'), TypeDescription('m', FullTypeDescr, 'qm', 'm'), TypeDescription('m', FullTypeDescr, 'md', 'm'), @@ -294,25 +322,17 @@ def english_upper(s): ], TD(O, f='PyNumber_Multiply'), ), -'divide': - Ufunc(2, 1, None, # One is only a unit to the right, not the left - docstrings.get('numpy.core.umath.divide'), - 'PyUFunc_MixedDivisionTypeResolver', - TD(intfltcmplx), - [TypeDescription('m', FullTypeDescr, 'mq', 'm'), - TypeDescription('m', FullTypeDescr, 'md', 'm'), - TypeDescription('m', FullTypeDescr, 'mm', 'd'), - ], - TD(O, f='PyNumber_Divide'), - ), +#'divide' : aliased to true_divide in umathmodule.c:initumath 'floor_divide': Ufunc(2, 1, None, # One is only a unit to the right, not the left docstrings.get('numpy.core.umath.floor_divide'), 'PyUFunc_DivisionTypeResolver', - TD(intfltcmplx), + TD(ints, cfunc_alias='divide', + dispatch=[('loops_arithmetic', 'bBhHiIlLqQ')]), + TD(flts), [TypeDescription('m', FullTypeDescr, 'mq', 'm'), TypeDescription('m', FullTypeDescr, 'md', 'm'), - #TypeDescription('m', FullTypeDescr, 'mm', 'd'), + TypeDescription('m', FullTypeDescr, 'mm', 'q'), ], TD(O, f='PyNumber_FloorDivide'), ), @@ -320,10 +340,10 @@ def english_upper(s): Ufunc(2, 1, None, # One is only a unit to the right, not the left docstrings.get('numpy.core.umath.true_divide'), 'PyUFunc_TrueDivisionTypeResolver', - TD(flts+cmplx), - [TypeDescription('m', FullTypeDescr, 'mq', 'm'), - TypeDescription('m', FullTypeDescr, 'md', 'm'), - TypeDescription('m', FullTypeDescr, 'mm', 'd'), + TD(flts+cmplx, cfunc_alias='divide', dispatch=[('loops_arithm_fp', 'fd')]), + [TypeDescription('m', FullTypeDescr, 'mq', 'm', cfunc_alias='divide'), + TypeDescription('m', FullTypeDescr, 'md', 'm', cfunc_alias='divide'), + TypeDescription('m', FullTypeDescr, 'mm', 'd', cfunc_alias='divide'), ], TD(O, f='PyNumber_TrueDivide'), ), @@ -331,7 +351,7 @@ def english_upper(s): Ufunc(1, 1, None, docstrings.get('numpy.core.umath.conjugate'), None, - TD(ints+flts+cmplx, simd=[('avx2', ints)]), + TD(ints+flts+cmplx, simd=[('avx2', ints), ('avx512f', cmplxvec)]), TD(P, f='conjugate'), ), 'fmod': @@ -339,21 +359,21 @@ def english_upper(s): docstrings.get('numpy.core.umath.fmod'), None, TD(ints), - TD(flts, f='fmod', astype={'e':'f'}), + TD(flts, f='fmod', astype={'e': 'f'}), TD(P, f='fmod'), ), 'square': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.square'), None, - TD(ints+inexact, simd=[('avx2', ints)]), + TD(ints+inexact, simd=[('avx2', ints), ('avx512f', 'FD')], dispatch=[('loops_unary_fp', 'fd')]), TD(O, f='Py_square'), ), 'reciprocal': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.reciprocal'), None, - TD(ints+inexact, simd=[('avx2', ints)]), + TD(ints+inexact, simd=[('avx2', ints)], dispatch=[('loops_unary_fp', 'fd')]), TD(O, f='Py_reciprocal'), ), # This is no longer used as numpy.ones_like, however it is @@ -370,7 +390,7 @@ def english_upper(s): docstrings.get('numpy.core.umath.power'), None, TD(ints), - TD(inexact, f='pow', astype={'e':'f'}), + TD(inexact, f='pow', astype={'e': 'f'}), TD(O, f='npy_ObjectPower'), ), 'float_power': @@ -383,8 +403,8 @@ def english_upper(s): Ufunc(1, 1, None, docstrings.get('numpy.core.umath.absolute'), 'PyUFunc_AbsoluteTypeResolver', - TD(bints+flts+timedeltaonly), - TD(cmplx, out=('f', 'd', 'g')), + TD(bints+flts+timedeltaonly, dispatch=[('loops_unary_fp', 'fd')]), + TD(cmplx, simd=[('avx512f', cmplxvec)], out=('f', 'd', 'g')), TD(O, f='PyNumber_Absolute'), ), '_arg': @@ -397,14 +417,14 @@ def english_upper(s): Ufunc(1, 1, None, docstrings.get('numpy.core.umath.negative'), 'PyUFunc_NegativeTypeResolver', - TD(bints+flts+timedeltaonly, simd=[('avx2', ints)]), + TD(ints+flts+timedeltaonly, simd=[('avx2', ints)]), TD(cmplx, f='neg'), TD(O, f='PyNumber_Negative'), ), 'positive': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.positive'), - 'PyUFunc_SimpleUnaryOperationTypeResolver', + 'PyUFunc_SimpleUniformOperationTypeResolver', TD(ints+flts+timedeltaonly), TD(cmplx, f='pos'), TD(O, f='PyNumber_Positive'), @@ -412,7 +432,7 @@ def english_upper(s): 'sign': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.sign'), - 'PyUFunc_SimpleUnaryOperationTypeResolver', + 'PyUFunc_SimpleUniformOperationTypeResolver', TD(nobool_or_datetime), ), 'greater': @@ -421,6 +441,7 @@ def english_upper(s): 'PyUFunc_SimpleBinaryComparisonTypeResolver', TD(all, out='?', simd=[('avx2', ints)]), [TypeDescription('O', FullTypeDescr, 'OO', 'O')], + TD('O', out='?'), ), 'greater_equal': Ufunc(2, 1, None, @@ -428,6 +449,7 @@ def english_upper(s): 'PyUFunc_SimpleBinaryComparisonTypeResolver', TD(all, out='?', simd=[('avx2', ints)]), [TypeDescription('O', FullTypeDescr, 'OO', 'O')], + TD('O', out='?'), ), 'less': Ufunc(2, 1, None, @@ -435,6 +457,7 @@ def english_upper(s): 'PyUFunc_SimpleBinaryComparisonTypeResolver', TD(all, out='?', simd=[('avx2', ints)]), [TypeDescription('O', FullTypeDescr, 'OO', 'O')], + TD('O', out='?'), ), 'less_equal': Ufunc(2, 1, None, @@ -442,6 +465,7 @@ def english_upper(s): 'PyUFunc_SimpleBinaryComparisonTypeResolver', TD(all, out='?', simd=[('avx2', ints)]), [TypeDescription('O', FullTypeDescr, 'OO', 'O')], + TD('O', out='?'), ), 'equal': Ufunc(2, 1, None, @@ -449,6 +473,7 @@ def english_upper(s): 'PyUFunc_SimpleBinaryComparisonTypeResolver', TD(all, out='?', simd=[('avx2', ints)]), [TypeDescription('O', FullTypeDescr, 'OO', 'O')], + TD('O', out='?'), ), 'not_equal': Ufunc(2, 1, None, @@ -456,9 +481,10 @@ def english_upper(s): 'PyUFunc_SimpleBinaryComparisonTypeResolver', TD(all, out='?', simd=[('avx2', ints)]), [TypeDescription('O', FullTypeDescr, 'OO', 'O')], + TD('O', out='?'), ), 'logical_and': - Ufunc(2, 1, One, + Ufunc(2, 1, True_, docstrings.get('numpy.core.umath.logical_and'), 'PyUFunc_SimpleBinaryComparisonTypeResolver', TD(nodatetime_or_obj, out='?', simd=[('avx2', ints)]), @@ -472,58 +498,66 @@ def english_upper(s): TD(O, f='npy_ObjectLogicalNot'), ), 'logical_or': - Ufunc(2, 1, Zero, + Ufunc(2, 1, False_, docstrings.get('numpy.core.umath.logical_or'), 'PyUFunc_SimpleBinaryComparisonTypeResolver', TD(nodatetime_or_obj, out='?', simd=[('avx2', ints)]), TD(O, f='npy_ObjectLogicalOr'), ), 'logical_xor': - Ufunc(2, 1, Zero, + Ufunc(2, 1, False_, docstrings.get('numpy.core.umath.logical_xor'), 'PyUFunc_SimpleBinaryComparisonTypeResolver', TD(nodatetime_or_obj, out='?'), + # TODO: using obj.logical_xor() seems pretty much useless: TD(P, f='logical_xor'), ), 'maximum': Ufunc(2, 1, ReorderableNone, docstrings.get('numpy.core.umath.maximum'), - 'PyUFunc_SimpleBinaryOperationTypeResolver', - TD(noobj), + 'PyUFunc_SimpleUniformOperationTypeResolver', + TD(noobj, simd=[('avx512f', 'fd')]), TD(O, f='npy_ObjectMax') ), 'minimum': Ufunc(2, 1, ReorderableNone, docstrings.get('numpy.core.umath.minimum'), - 'PyUFunc_SimpleBinaryOperationTypeResolver', - TD(noobj), + 'PyUFunc_SimpleUniformOperationTypeResolver', + TD(noobj, simd=[('avx512f', 'fd')]), TD(O, f='npy_ObjectMin') ), +'clip': + Ufunc(3, 1, ReorderableNone, + docstrings.get('numpy.core.umath.clip'), + 'PyUFunc_SimpleUniformOperationTypeResolver', + TD(noobj), + [TypeDescription('O', 'npy_ObjectClip', 'OOO', 'O')] + ), 'fmax': Ufunc(2, 1, ReorderableNone, docstrings.get('numpy.core.umath.fmax'), - 'PyUFunc_SimpleBinaryOperationTypeResolver', + 'PyUFunc_SimpleUniformOperationTypeResolver', TD(noobj), TD(O, f='npy_ObjectMax') ), 'fmin': Ufunc(2, 1, ReorderableNone, docstrings.get('numpy.core.umath.fmin'), - 'PyUFunc_SimpleBinaryOperationTypeResolver', + 'PyUFunc_SimpleUniformOperationTypeResolver', TD(noobj), TD(O, f='npy_ObjectMin') ), 'logaddexp': - Ufunc(2, 1, None, + Ufunc(2, 1, MinusInfinity, docstrings.get('numpy.core.umath.logaddexp'), None, - TD(flts, f="logaddexp", astype={'e':'f'}) + TD(flts, f="logaddexp", astype={'e': 'f'}) ), 'logaddexp2': - Ufunc(2, 1, None, + Ufunc(2, 1, MinusInfinity, docstrings.get('numpy.core.umath.logaddexp2'), None, - TD(flts, f="logaddexp2", astype={'e':'f'}) + TD(flts, f="logaddexp2", astype={'e': 'f'}) ), 'bitwise_and': Ufunc(2, 1, AllOnes, @@ -571,249 +605,301 @@ def english_upper(s): Ufunc(2, 1, None, docstrings.get('numpy.core.umath.heaviside'), None, - TD(flts, f='heaviside', astype={'e':'f'}), + TD(flts, f='heaviside', astype={'e': 'f'}), ), 'degrees': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.degrees'), None, - TD(fltsP, f='degrees', astype={'e':'f'}), + TD(fltsP, f='degrees', astype={'e': 'f'}), ), 'rad2deg': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.rad2deg'), None, - TD(fltsP, f='rad2deg', astype={'e':'f'}), + TD(fltsP, f='rad2deg', astype={'e': 'f'}), ), 'radians': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.radians'), None, - TD(fltsP, f='radians', astype={'e':'f'}), + TD(fltsP, f='radians', astype={'e': 'f'}), ), 'deg2rad': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.deg2rad'), None, - TD(fltsP, f='deg2rad', astype={'e':'f'}), + TD(fltsP, f='deg2rad', astype={'e': 'f'}), ), 'arccos': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.arccos'), None, - TD(inexact, f='acos', astype={'e':'f'}), + TD('e', f='acos', astype={'e': 'f'}), + TD('fd', dispatch=[('loops_umath_fp', 'fd')]), + TD(inexact, f='acos', astype={'e': 'f'}), TD(P, f='arccos'), ), 'arccosh': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.arccosh'), None, - TD(inexact, f='acosh', astype={'e':'f'}), + TD('e', f='acosh', astype={'e': 'f'}), + TD('fd', dispatch=[('loops_umath_fp', 'fd')]), + TD(inexact, f='acosh', astype={'e': 'f'}), TD(P, f='arccosh'), ), 'arcsin': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.arcsin'), None, - TD(inexact, f='asin', astype={'e':'f'}), + TD('e', f='asin', astype={'e': 'f'}), + TD('fd', dispatch=[('loops_umath_fp', 'fd')]), + TD(inexact, f='asin', astype={'e': 'f'}), TD(P, f='arcsin'), ), 'arcsinh': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.arcsinh'), None, - TD(inexact, f='asinh', astype={'e':'f'}), + TD('e', f='asinh', astype={'e': 'f'}), + TD('fd', dispatch=[('loops_umath_fp', 'fd')]), + TD(inexact, f='asinh', astype={'e': 'f'}), TD(P, f='arcsinh'), ), 'arctan': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.arctan'), None, - TD(inexact, f='atan', astype={'e':'f'}), + TD('e', f='atan', astype={'e': 'f'}), + TD('fd', dispatch=[('loops_umath_fp', 'fd')]), + TD(inexact, f='atan', astype={'e': 'f'}), TD(P, f='arctan'), ), 'arctanh': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.arctanh'), None, - TD(inexact, f='atanh', astype={'e':'f'}), + TD('e', f='atanh', astype={'e': 'f'}), + TD('fd', dispatch=[('loops_umath_fp', 'fd')]), + TD(inexact, f='atanh', astype={'e': 'f'}), TD(P, f='arctanh'), ), 'cos': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.cos'), None, - TD(inexact, f='cos', astype={'e':'f'}), + TD('e', f='cos', astype={'e': 'f'}), + TD('f', dispatch=[('loops_trigonometric', 'f')]), + TD('d', dispatch=[('loops_umath_fp', 'd')]), + TD('fdg' + cmplx, f='cos'), TD(P, f='cos'), ), 'sin': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.sin'), None, - TD(inexact, f='sin', astype={'e':'f'}), + TD('e', f='sin', astype={'e': 'f'}), + TD('f', dispatch=[('loops_trigonometric', 'f')]), + TD('d', dispatch=[('loops_umath_fp', 'd')]), + TD('fdg' + cmplx, f='sin'), TD(P, f='sin'), ), 'tan': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.tan'), None, - TD(inexact, f='tan', astype={'e':'f'}), + TD('e', f='tan', astype={'e': 'f'}), + TD('fd', dispatch=[('loops_umath_fp', 'fd')]), + TD(inexact, f='tan', astype={'e': 'f'}), TD(P, f='tan'), ), 'cosh': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.cosh'), None, - TD(inexact, f='cosh', astype={'e':'f'}), + TD('e', f='cosh', astype={'e': 'f'}), + TD('fd', dispatch=[('loops_umath_fp', 'fd')]), + TD(inexact, f='cosh', astype={'e': 'f'}), TD(P, f='cosh'), ), 'sinh': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.sinh'), None, - TD(inexact, f='sinh', astype={'e':'f'}), + TD('e', f='sinh', astype={'e': 'f'}), + TD('fd', dispatch=[('loops_umath_fp', 'fd')]), + TD(inexact, f='sinh', astype={'e': 'f'}), TD(P, f='sinh'), ), 'tanh': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.tanh'), None, - TD(inexact, f='tanh', astype={'e':'f'}), + TD('e', f='tanh', astype={'e': 'f'}), + TD('fd', dispatch=[('loops_umath_fp', 'fd')]), + TD(inexact, f='tanh', astype={'e': 'f'}), TD(P, f='tanh'), ), 'exp': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.exp'), None, - TD(inexact, f='exp', astype={'e':'f'}), + TD('e', f='exp', astype={'e': 'f'}), + TD('fd', dispatch=[('loops_exponent_log', 'fd')]), + TD('fdg' + cmplx, f='exp'), TD(P, f='exp'), ), 'exp2': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.exp2'), None, - TD(inexact, f='exp2', astype={'e':'f'}), + TD('e', f='exp2', astype={'e': 'f'}), + TD('fd', dispatch=[('loops_umath_fp', 'fd')]), + TD(inexact, f='exp2', astype={'e': 'f'}), TD(P, f='exp2'), ), 'expm1': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.expm1'), None, - TD(inexact, f='expm1', astype={'e':'f'}), + TD('e', f='expm1', astype={'e': 'f'}), + TD('fd', dispatch=[('loops_umath_fp', 'fd')]), + TD(inexact, f='expm1', astype={'e': 'f'}), TD(P, f='expm1'), ), 'log': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.log'), None, - TD(inexact, f='log', astype={'e':'f'}), + TD('e', f='log', astype={'e': 'f'}), + TD('fd', dispatch=[('loops_exponent_log', 'fd')]), + TD('fdg' + cmplx, f='log'), TD(P, f='log'), ), 'log2': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.log2'), None, - TD(inexact, f='log2', astype={'e':'f'}), + TD('e', f='log2', astype={'e': 'f'}), + TD('fd', dispatch=[('loops_umath_fp', 'fd')]), + TD(inexact, f='log2', astype={'e': 'f'}), TD(P, f='log2'), ), 'log10': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.log10'), None, - TD(inexact, f='log10', astype={'e':'f'}), + TD('e', f='log10', astype={'e': 'f'}), + TD('fd', dispatch=[('loops_umath_fp', 'fd')]), + TD(inexact, f='log10', astype={'e': 'f'}), TD(P, f='log10'), ), 'log1p': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.log1p'), None, - TD(inexact, f='log1p', astype={'e':'f'}), + TD('e', f='log1p', astype={'e': 'f'}), + TD('fd', dispatch=[('loops_umath_fp', 'fd')]), + TD(inexact, f='log1p', astype={'e': 'f'}), TD(P, f='log1p'), ), 'sqrt': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.sqrt'), None, - TD('e', f='sqrt', astype={'e':'f'}), - TD(inexactvec), - TD(inexact, f='sqrt', astype={'e':'f'}), + TD('e', f='sqrt', astype={'e': 'f'}), + TD(inexactvec, dispatch=[('loops_unary_fp', 'fd')]), + TD('fdg' + cmplx, f='sqrt'), TD(P, f='sqrt'), ), 'cbrt': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.cbrt'), None, - TD(flts, f='cbrt', astype={'e':'f'}), + TD('e', f='cbrt', astype={'e': 'f'}), + TD('fd', dispatch=[('loops_umath_fp', 'fd')]), + TD(flts, f='cbrt', astype={'e': 'f'}), TD(P, f='cbrt'), ), 'ceil': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.ceil'), None, - TD(flts, f='ceil', astype={'e':'f'}), - TD(P, f='ceil'), + TD('e', f='ceil', astype={'e': 'f'}), + TD(inexactvec, dispatch=[('loops_unary_fp', 'fd')]), + TD('fdg', f='ceil'), + TD(O, f='npy_ObjectCeil'), ), 'trunc': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.trunc'), None, - TD(flts, f='trunc', astype={'e':'f'}), - TD(P, f='trunc'), + TD('e', f='trunc', astype={'e': 'f'}), + TD(inexactvec, simd=[('fma', 'fd'), ('avx512f', 'fd')]), + TD('fdg', f='trunc'), + TD(O, f='npy_ObjectTrunc'), ), 'fabs': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.fabs'), None, - TD(flts, f='fabs', astype={'e':'f'}), + TD(flts, f='fabs', astype={'e': 'f'}), TD(P, f='fabs'), ), 'floor': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.floor'), None, - TD(flts, f='floor', astype={'e':'f'}), - TD(P, f='floor'), + TD('e', f='floor', astype={'e': 'f'}), + TD(inexactvec, simd=[('fma', 'fd'), ('avx512f', 'fd')]), + TD('fdg', f='floor'), + TD(O, f='npy_ObjectFloor'), ), 'rint': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.rint'), None, - TD(inexact, f='rint', astype={'e':'f'}), + TD('e', f='rint', astype={'e': 'f'}), + TD(inexactvec, simd=[('fma', 'fd'), ('avx512f', 'fd')]), + TD('fdg' + cmplx, f='rint'), TD(P, f='rint'), ), 'arctan2': Ufunc(2, 1, None, docstrings.get('numpy.core.umath.arctan2'), None, - TD(flts, f='atan2', astype={'e':'f'}), + TD(flts, f='atan2', astype={'e': 'f'}), TD(P, f='arctan2'), ), 'remainder': Ufunc(2, 1, None, docstrings.get('numpy.core.umath.remainder'), - None, + 'PyUFunc_RemainderTypeResolver', TD(intflt), + [TypeDescription('m', FullTypeDescr, 'mm', 'm')], TD(O, f='PyNumber_Remainder'), ), 'divmod': Ufunc(2, 2, None, docstrings.get('numpy.core.umath.divmod'), - None, + 'PyUFunc_DivmodTypeResolver', TD(intflt), + [TypeDescription('m', FullTypeDescr, 'mm', 'qm')], # TD(O, f='PyNumber_Divmod'), # gh-9730 ), 'hypot': Ufunc(2, 1, Zero, docstrings.get('numpy.core.umath.hypot'), None, - TD(flts, f='hypot', astype={'e':'f'}), + TD(flts, f='hypot', astype={'e': 'f'}), TD(P, f='hypot'), ), 'isnan': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.isnan'), - None, - TD(inexact, out='?'), + 'PyUFunc_IsFiniteTypeResolver', + TD(noobj, simd=[('avx512_skx', 'fd')], out='?'), ), 'isnat': Ufunc(1, 1, None, @@ -824,20 +910,20 @@ def english_upper(s): 'isinf': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.isinf'), - None, - TD(inexact, out='?'), + 'PyUFunc_IsFiniteTypeResolver', + TD(noobj, simd=[('avx512_skx', 'fd')], out='?'), ), 'isfinite': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.isfinite'), - None, - TD(inexact, out='?'), + 'PyUFunc_IsFiniteTypeResolver', + TD(noobj, simd=[('avx512_skx', 'fd')], out='?'), ), 'signbit': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.signbit'), None, - TD(flts, out='?'), + TD(flts, simd=[('avx512_skx', 'fd')], out='?'), ), 'copysign': Ufunc(2, 1, None, @@ -868,10 +954,10 @@ def english_upper(s): docstrings.get('numpy.core.umath.ldexp'), None, [TypeDescription('e', None, 'ei', 'e'), - TypeDescription('f', None, 'fi', 'f'), + TypeDescription('f', None, 'fi', 'f', dispatch='loops_exponent_log'), TypeDescription('e', FuncNameSuffix('long'), 'el', 'e'), TypeDescription('f', FuncNameSuffix('long'), 'fl', 'f'), - TypeDescription('d', None, 'di', 'd'), + TypeDescription('d', None, 'di', 'd', dispatch='loops_exponent_log'), TypeDescription('d', FuncNameSuffix('long'), 'dl', 'd'), TypeDescription('g', None, 'gi', 'g'), TypeDescription('g', FuncNameSuffix('long'), 'gl', 'g'), @@ -882,31 +968,35 @@ def english_upper(s): docstrings.get('numpy.core.umath.frexp'), None, [TypeDescription('e', None, 'e', 'ei'), - TypeDescription('f', None, 'f', 'fi'), - TypeDescription('d', None, 'd', 'di'), + TypeDescription('f', None, 'f', 'fi', dispatch='loops_exponent_log'), + TypeDescription('d', None, 'd', 'di', dispatch='loops_exponent_log'), TypeDescription('g', None, 'g', 'gi'), ], ), 'gcd' : Ufunc(2, 1, Zero, docstrings.get('numpy.core.umath.gcd'), - "PyUFunc_SimpleBinaryOperationTypeResolver", + "PyUFunc_SimpleUniformOperationTypeResolver", TD(ints), TD('O', f='npy_ObjectGCD'), ), 'lcm' : Ufunc(2, 1, None, docstrings.get('numpy.core.umath.lcm'), - "PyUFunc_SimpleBinaryOperationTypeResolver", + "PyUFunc_SimpleUniformOperationTypeResolver", TD(ints), TD('O', f='npy_ObjectLCM'), - ) + ), +'matmul' : + Ufunc(2, 1, None, + docstrings.get('numpy.core.umath.matmul'), + "PyUFunc_SimpleUniformOperationTypeResolver", + TD(notimes_or_obj), + TD(O), + signature='(n?,k),(k,m?)->(n?,m?)', + ), } -if sys.version_info[0] >= 3: - # Will be aliased to true_divide in umathmodule.c.src:InitOtherOperators - del defdict['divide'] - def indent(st, spaces): indentation = ' '*spaces indented = indentation + st.replace('\n', '\n'+indentation) @@ -914,25 +1004,35 @@ def indent(st, spaces): indented = re.sub(r' +$', r'', indented) return indented -chartotype1 = {'e': 'e_e', - 'f': 'f_f', - 'd': 'd_d', - 'g': 'g_g', - 'F': 'F_F', - 'D': 'D_D', - 'G': 'G_G', - 'O': 'O_O', - 'P': 'O_O_method'} +# maps [nin, nout][type] to a suffix +arity_lookup = { + (1, 1): { + 'e': 'e_e', + 'f': 'f_f', + 'd': 'd_d', + 'g': 'g_g', + 'F': 'F_F', + 'D': 'D_D', + 'G': 'G_G', + 'O': 'O_O', + 'P': 'O_O_method', + }, + (2, 1): { + 'e': 'ee_e', + 'f': 'ff_f', + 'd': 'dd_d', + 'g': 'gg_g', + 'F': 'FF_F', + 'D': 'DD_D', + 'G': 'GG_G', + 'O': 'OO_O', + 'P': 'OO_O_method', + }, + (3, 1): { + 'O': 'OOO_O', + } +} -chartotype2 = {'e': 'ee_e', - 'f': 'ff_f', - 'd': 'dd_d', - 'g': 'gg_g', - 'F': 'FF_F', - 'D': 'DD_D', - 'G': 'GG_G', - 'O': 'OO_O', - 'P': 'OO_O_method'} #for each name # 1) create functions, data, and signature # 2) fill in functions and data in InitOperators @@ -944,6 +1044,7 @@ def make_arrays(funcdict): # later code1list = [] code2list = [] + dispdict = {} names = sorted(funcdict.keys()) for name in names: uf = funcdict[name] @@ -954,40 +1055,40 @@ def make_arrays(funcdict): sub = 0 for t in uf.type_descriptions: + cfunc_alias = t.cfunc_alias if t.cfunc_alias else name + cfunc_fname = None if t.func_data is FullTypeDescr: tname = english_upper(chartoname[t.type]) datalist.append('(void *)NULL') - funclist.append( - '%s_%s_%s_%s' % (tname, t.in_, t.out, name)) + cfunc_fname = f"{tname}_{t.in_}_{t.out}_{cfunc_alias}" elif isinstance(t.func_data, FuncNameSuffix): datalist.append('(void *)NULL') tname = english_upper(chartoname[t.type]) - funclist.append( - '%s_%s_%s' % (tname, name, t.func_data.suffix)) + cfunc_fname = f"{tname}_{cfunc_alias}_{t.func_data.suffix}" elif t.func_data is None: datalist.append('(void *)NULL') tname = english_upper(chartoname[t.type]) - funclist.append('%s_%s' % (tname, name)) + cfunc_fname = f"{tname}_{cfunc_alias}" if t.simd is not None: for vt in t.simd: code2list.append(textwrap.dedent("""\ #ifdef HAVE_ATTRIBUTE_TARGET_{ISA} - if (npy_cpu_supports("{isa}")) {{ - {fname}_functions[{idx}] = {type}_{fname}_{isa}; + if (NPY_CPU_HAVE({ISA})) {{ + {fname}_functions[{idx}] = {cname}_{isa}; }} #endif """).format( ISA=vt.upper(), isa=vt, - fname=name, type=tname, idx=k + fname=name, cname=cfunc_fname, idx=k )) else: - funclist.append('NULL') - if (uf.nin, uf.nout) == (2, 1): - thedict = chartotype2 - elif (uf.nin, uf.nout) == (1, 1): - thedict = chartotype1 - else: - raise ValueError("Could not handle {}[{}]".format(name, t.type)) + try: + thedict = arity_lookup[uf.nin, uf.nout] + except KeyError as e: + raise ValueError( + f"Could not handle {name}[{t.type}] " + f"with nin={uf.nin}, nout={uf.nout}" + ) from None astype = '' if not t.astype is None: @@ -1010,6 +1111,13 @@ def make_arrays(funcdict): #datalist.append('(void *)%s' % t.func_data) sub += 1 + if cfunc_fname: + funclist.append(cfunc_fname) + if t.dispatch: + dispdict.setdefault(t.dispatch, []).append((name, k, cfunc_fname)) + else: + funclist.append('NULL') + for x in t.in_ + t.out: siglist.append('NPY_%s' % (english_upper(chartoname[x]),)) @@ -1024,6 +1132,17 @@ def make_arrays(funcdict): % (name, datanames)) code1list.append("static char %s_signatures[] = {%s};" % (name, signames)) + + for dname, funcs in dispdict.items(): + code2list.append(textwrap.dedent(f""" + #ifndef NPY_DISABLE_OPTIMIZATION + #include "{dname}.dispatch.h" + #endif + """)) + for (ufunc_name, func_idx, cfunc_name) in funcs: + code2list.append(textwrap.dedent(f"""\ + NPY_CPU_DISPATCH_CALL_XB({ufunc_name}_functions[{func_idx}] = {cfunc_name}); + """)) return "\n".join(code1list), "\n".join(code2list) def make_ufuncs(funcdict): @@ -1033,32 +1152,51 @@ def make_ufuncs(funcdict): uf = funcdict[name] mlist = [] docstring = textwrap.dedent(uf.docstring).strip() - if sys.version_info[0] < 3: - docstring = docstring.encode('string-escape') - docstring = docstring.replace(r'"', r'\"') - else: - docstring = docstring.encode('unicode-escape').decode('ascii') - docstring = docstring.replace(r'"', r'\"') - # XXX: I don't understand why the following replace is not - # necessary in the python 2 case. - docstring = docstring.replace(r"'", r"\'") + docstring = docstring.encode('unicode-escape').decode('ascii') + docstring = docstring.replace(r'"', r'\"') + docstring = docstring.replace(r"'", r"\'") # Split the docstring because some compilers (like MS) do not like big # string literal in C code. We split at endlines because textwrap.wrap # do not play well with \n docstring = '\\n\"\"'.join(docstring.split(r"\n")) + if uf.signature is None: + sig = "NULL" + else: + sig = '"{}"'.format(uf.signature) fmt = textwrap.dedent("""\ - f = PyUFunc_FromFuncAndData( + identity = {identity_expr}; + if ({has_identity} && identity == NULL) {{ + return -1; + }} + f = PyUFunc_FromFuncAndDataAndSignatureAndIdentity( {name}_functions, {name}_data, {name}_signatures, {nloops}, {nin}, {nout}, {identity}, "{name}", - "{doc}", 0 + "{doc}", 0, {sig}, identity ); + if ({has_identity}) {{ + Py_DECREF(identity); + }} if (f == NULL) {{ return -1; - }}""") - mlist.append(fmt.format( + }} + """) + args = dict( name=name, nloops=len(uf.type_descriptions), - nin=uf.nin, nout=uf.nout, identity=uf.identity, doc=docstring - )) + nin=uf.nin, nout=uf.nout, + has_identity='0' if uf.identity is None_ else '1', + identity='PyUFunc_IdentityValue', + identity_expr=uf.identity, + doc=docstring, + sig=sig, + ) + + # Only PyUFunc_None means don't reorder - we pass this using the old + # argument + if uf.identity is None_: + args['identity'] = 'PyUFunc_None' + args['identity_expr'] = 'NULL' + + mlist.append(fmt.format(**args)) if uf.typereso is not None: mlist.append( r"((PyUFuncObject *)f)->type_resolver = &%s;" % uf.typereso) @@ -1079,12 +1217,16 @@ def make_code(funcdict, filename): Please make changes to the code generator program (%s) **/ - #include "cpuid.h" + #include "ufunc_object.h" + #include "ufunc_type_resolution.h" + #include "loops.h" + #include "matmul.h" + #include "clip.h" %s static int InitOperators(PyObject *dictionary) { - PyObject *f; + PyObject *f, *identity; %s %s @@ -1097,7 +1239,6 @@ def make_code(funcdict, filename): if __name__ == "__main__": filename = __file__ - fid = open('__umath_generated.c', 'w') code = make_code(defdict, filename) - fid.write(code) - fid.close() + with open('__umath_generated.c', 'w') as fid: + fid.write(code) diff --git a/numpy/core/code_generators/numpy_api.py b/numpy/core/code_generators/numpy_api.py index 6cfbbbcc7b25..d12d62d8fe9a 100644 --- a/numpy/core/code_generators/numpy_api.py +++ b/numpy/core/code_generators/numpy_api.py @@ -13,14 +13,13 @@ exception, so it should hopefully not get unnoticed). """ -from __future__ import division, absolute_import, print_function - from code_generators.genapi import StealRef, NonNull # index, type multiarray_global_vars = { 'NPY_NUMUSERTYPES': (7, 'int'), 'NPY_DEFAULT_ASSIGN_CASTING': (292, 'NPY_CASTING'), + 'PyDataMem_DefaultHandler': (306, 'PyObject*'), } multiarray_scalar_bool_values = { @@ -32,7 +31,9 @@ multiarray_types_api = { 'PyBigArray_Type': (1,), 'PyArray_Type': (2,), - 'PyArrayDescr_Type': (3,), + # Internally, PyArrayDescr_Type is a PyArray_DTypeMeta, + # the following also defines PyArrayDescr_TypeFull (Full appended) + 'PyArrayDescr_Type': (3, "PyArray_DTypeMeta"), 'PyArrayFlags_Type': (4,), 'PyArrayIter_Type': (5,), 'PyArrayMultiIter_Type': (6,), @@ -76,9 +77,9 @@ # End 1.6 API } -#define NPY_NUMUSERTYPES (*(int *)PyArray_API[6]) -#define PyBoolArrType_Type (*(PyTypeObject *)PyArray_API[7]) -#define _PyArrayScalar_BoolValues ((PyBoolScalarObject *)PyArray_API[8]) +# define NPY_NUMUSERTYPES (*(int *)PyArray_API[6]) +# define PyBoolArrType_Type (*(PyTypeObject *)PyArray_API[7]) +# define _PyArrayScalar_BoolValues ((PyBoolScalarObject *)PyArray_API[8]) multiarray_funcs_api = { 'PyArray_GetNDArrayCVersion': (0,), @@ -350,8 +351,9 @@ 'PyArray_ResolveWritebackIfCopy': (302,), 'PyArray_SetWritebackIfCopyBase': (303,), # End 1.14 API - 'NpyIter_Close': (304,), - # End 1.15 API + 'PyDataMem_SetHandler': (304,), + 'PyDataMem_GetHandler': (305,), + # End 1.21 API } ufunc_types_api = { @@ -404,6 +406,8 @@ # End 1.7 API 'PyUFunc_RegisterLoopForDescr': (41,), # End 1.8 API + 'PyUFunc_FromFuncAndDataAndSignatureAndIdentity': (42,), + # End 1.16 API } # List of all the dicts which define the C API diff --git a/numpy/core/code_generators/ufunc_docstrings.py b/numpy/core/code_generators/ufunc_docstrings.py index f7d58a26f452..c9be945693dc 100644 --- a/numpy/core/code_generators/ufunc_docstrings.py +++ b/numpy/core/code_generators/ufunc_docstrings.py @@ -9,7 +9,6 @@ at compile time. """ -from __future__ import division, absolute_import, print_function import textwrap docdict = {} @@ -22,16 +21,23 @@ def get(name): 'PARAMS': textwrap.dedent(""" out : ndarray, None, or tuple of ndarray and None, optional A location into which the result is stored. If provided, it must have - a shape that the inputs broadcast to. If not provided or `None`, + a shape that the inputs broadcast to. If not provided or None, a freshly-allocated array is returned. A tuple (possible only as a keyword argument) must have length equal to the number of outputs. where : array_like, optional - Values of True indicate to calculate the ufunc at that position, values - of False indicate to leave the value in the output alone. + This condition is broadcast over the input. At locations where the + condition is True, the `out` array will be set to the ufunc result. + Elsewhere, the `out` array will retain its original value. + Note that if an uninitialized `out` array is created via the default + ``out=None``, locations within it where the condition is False will + remain uninitialized. **kwargs For other keyword-only arguments, see the :ref:`ufunc docs <ufuncs.kwargs>`. """).strip(), + 'BROADCASTABLE_2': ("If ``x1.shape != x2.shape``, they must be " + "broadcastable to a common\n shape (which becomes " + "the shape of the output)."), 'OUT_SCALAR_1': "This is a scalar if `x` is a scalar.", 'OUT_SCALAR_2': "This is a scalar if both `x1` and `x2` are scalars.", } @@ -39,13 +45,20 @@ def get(name): def add_newdoc(place, name, doc): doc = textwrap.dedent(doc).strip() - if name[0] != '_': + skip = ( + # gufuncs do not use the OUT_SCALAR replacement strings + 'matmul', + # clip has 3 inputs, which is not handled by this + 'clip', + ) + if name[0] != '_' and name not in skip: if '\nx :' in doc: assert '$OUT_SCALAR_1' in doc, "in {}".format(name) elif '\nx2 :' in doc or '\nx1, x2 :' in doc: assert '$OUT_SCALAR_2' in doc, "in {}".format(name) else: assert False, "Could not detect number of inputs in {}".format(name) + for k, v in subst.items(): doc = doc.replace('$' + k, v) @@ -94,6 +107,13 @@ def add_newdoc(place, name, doc): >>> plt.imshow(np.abs(xx), extent=[-10, 10, -10, 10], cmap='gray') >>> plt.show() + The `abs` function can be used as a shorthand for ``np.absolute`` on + ndarrays. + + >>> x = np.array([-1.2, 1.2]) + >>> abs(x) + array([1.2, 1.2]) + """) add_newdoc('numpy.core.umath', 'add', @@ -103,9 +123,8 @@ def add_newdoc(place, name, doc): Parameters ---------- x1, x2 : array_like - The arrays to be added. If ``x1.shape != x2.shape``, they must be - broadcastable to a common shape (which may be the shape of one or - the other). + The arrays to be added. + $BROADCASTABLE_2 $PARAMS Returns @@ -129,6 +148,14 @@ def add_newdoc(place, name, doc): [ 3., 5., 7.], [ 6., 8., 10.]]) + The ``+`` operator can be used as a shorthand for ``np.add`` on ndarrays. + + >>> x1 = np.arange(9.0).reshape((3, 3)) + >>> x2 = np.arange(3.0) + >>> x1 + x2 + array([[ 0., 2., 4.], + [ 3., 5., 7.], + [ 6., 8., 10.]]) """) add_newdoc('numpy.core.umath', 'arccos', @@ -158,7 +185,7 @@ def add_newdoc(place, name, doc): Notes ----- `arccos` is a multivalued function: for each `x` there are infinitely - many numbers `z` such that `cos(z) = x`. The convention is to return + many numbers `z` such that ``cos(z) = x``. The convention is to return the angle `z` whose real part lies in `[0, pi]`. For real-valued input data types, `arccos` always returns real output. @@ -166,7 +193,7 @@ def add_newdoc(place, name, doc): it yields ``nan`` and sets the `invalid` floating point error flag. For complex-valued input, `arccos` is a complex analytic function that - has branch cuts `[-inf, -1]` and `[1, inf]` and is continuous from + has branch cuts ``[-inf, -1]`` and `[1, inf]` and is continuous from above on the former and from below on the latter. The inverse `cos` is also known as `acos` or cos^-1. @@ -174,7 +201,8 @@ def add_newdoc(place, name, doc): References ---------- M. Abramowitz and I.A. Stegun, "Handbook of Mathematical Functions", - 10th printing, 1964, pp. 79. http://www.math.sfu.ca/~cbm/aands/ + 10th printing, 1964, pp. 79. + https://personal.math.ubc.ca/~cbm/aands/page_79.htm Examples -------- @@ -218,7 +246,7 @@ def add_newdoc(place, name, doc): ----- `arccosh` is a multivalued function: for each `x` there are infinitely many numbers `z` such that `cosh(z) = x`. The convention is to return the - `z` whose imaginary part lies in `[-pi, pi]` and the real part in + `z` whose imaginary part lies in ``[-pi, pi]`` and the real part in ``[0, inf]``. For real-valued input data types, `arccosh` always returns real output. @@ -231,9 +259,10 @@ def add_newdoc(place, name, doc): References ---------- .. [1] M. Abramowitz and I.A. Stegun, "Handbook of Mathematical Functions", - 10th printing, 1964, pp. 86. http://www.math.sfu.ca/~cbm/aands/ + 10th printing, 1964, pp. 86. + https://personal.math.ubc.ca/~cbm/aands/page_86.htm .. [2] Wikipedia, "Inverse hyperbolic function", - http://en.wikipedia.org/wiki/Arccosh + https://en.wikipedia.org/wiki/Arccosh Examples -------- @@ -285,7 +314,7 @@ def add_newdoc(place, name, doc): ---------- Abramowitz, M. and Stegun, I. A., *Handbook of Mathematical Functions*, 10th printing, New York: Dover, 1964, pp. 79ff. - http://www.math.sfu.ca/~cbm/aands/ + https://personal.math.ubc.ca/~cbm/aands/page_79.htm Examples -------- @@ -333,9 +362,10 @@ def add_newdoc(place, name, doc): References ---------- .. [1] M. Abramowitz and I.A. Stegun, "Handbook of Mathematical Functions", - 10th printing, 1964, pp. 86. http://www.math.sfu.ca/~cbm/aands/ + 10th printing, 1964, pp. 86. + https://personal.math.ubc.ca/~cbm/aands/page_86.htm .. [2] Wikipedia, "Inverse hyperbolic function", - http://en.wikipedia.org/wiki/Arcsinh + https://en.wikipedia.org/wiki/Arcsinh Examples -------- @@ -379,7 +409,7 @@ def add_newdoc(place, name, doc): it yields ``nan`` and sets the `invalid` floating point error flag. For complex-valued input, `arctan` is a complex analytic function that - has [`1j, infj`] and [`-1j, -infj`] as branch cuts, and is continuous + has [``1j, infj``] and [``-1j, -infj``] as branch cuts, and is continuous from the left on the former and from the right on the latter. The inverse tangent is also known as `atan` or tan^{-1}. @@ -388,7 +418,7 @@ def add_newdoc(place, name, doc): ---------- Abramowitz, M. and Stegun, I. A., *Handbook of Mathematical Functions*, 10th printing, New York: Dover, 1964, pp. 79. - http://www.math.sfu.ca/~cbm/aands/ + https://personal.math.ubc.ca/~cbm/aands/page_79.htm Examples -------- @@ -431,8 +461,8 @@ def add_newdoc(place, name, doc): x1 : array_like, real-valued `y`-coordinates. x2 : array_like, real-valued - `x`-coordinates. `x2` must be broadcastable to match the shape of - `x1` or vice versa. + `x`-coordinates. + $BROADCASTABLE_2 $PARAMS Returns @@ -517,7 +547,7 @@ def add_newdoc(place, name, doc): Notes ----- `arctanh` is a multivalued function: for each `x` there are infinitely - many numbers `z` such that `tanh(z) = x`. The convention is to return + many numbers `z` such that ``tanh(z) = x``. The convention is to return the `z` whose imaginary part lies in `[-pi/2, pi/2]`. For real-valued input data types, `arctanh` always returns real output. @@ -533,9 +563,10 @@ def add_newdoc(place, name, doc): References ---------- .. [1] M. Abramowitz and I.A. Stegun, "Handbook of Mathematical Functions", - 10th printing, 1964, pp. 86. http://www.math.sfu.ca/~cbm/aands/ + 10th printing, 1964, pp. 86. + https://personal.math.ubc.ca/~cbm/aands/page_86.htm .. [2] Wikipedia, "Inverse hyperbolic function", - http://en.wikipedia.org/wiki/Arctanh + https://en.wikipedia.org/wiki/Arctanh Examples -------- @@ -556,6 +587,7 @@ def add_newdoc(place, name, doc): ---------- x1, x2 : array_like Only integer and boolean types are handled. + $BROADCASTABLE_2 $PARAMS Returns @@ -595,6 +627,14 @@ def add_newdoc(place, name, doc): >>> np.bitwise_and([True, True], [False, True]) array([False, True]) + The ``&`` operator can be used as a shorthand for ``np.bitwise_and`` on + ndarrays. + + >>> x1 = np.array([2, 5, 255]) + >>> x2 = np.array([3, 14, 16]) + >>> x1 & x2 + array([ 2, 4, 16]) + """) add_newdoc('numpy.core.umath', 'bitwise_or', @@ -609,6 +649,7 @@ def add_newdoc(place, name, doc): ---------- x1, x2 : array_like Only integer and boolean types are handled. + $BROADCASTABLE_2 $PARAMS Returns @@ -627,7 +668,7 @@ def add_newdoc(place, name, doc): Examples -------- - The number 13 has the binaray representation ``00001101``. Likewise, + The number 13 has the binary representation ``00001101``. Likewise, 16 is represented by ``00010000``. The bit-wise OR of 13 and 16 is then ``000111011``, or 29: @@ -647,12 +688,20 @@ def add_newdoc(place, name, doc): array([ 6, 5, 255]) >>> np.array([2, 5, 255]) | np.array([4, 4, 4]) array([ 6, 5, 255]) - >>> np.bitwise_or(np.array([2, 5, 255, 2147483647L], dtype=np.int32), - ... np.array([4, 4, 4, 2147483647L], dtype=np.int32)) + >>> np.bitwise_or(np.array([2, 5, 255, 2147483647], dtype=np.int32), + ... np.array([4, 4, 4, 2147483647], dtype=np.int32)) array([ 6, 5, 255, 2147483647]) >>> np.bitwise_or([True, True], [False, True]) array([ True, True]) + The ``|`` operator can be used as a shorthand for ``np.bitwise_or`` on + ndarrays. + + >>> x1 = np.array([2, 5, 255]) + >>> x2 = np.array([4, 4, 4]) + >>> x1 | x2 + array([ 6, 5, 255]) + """) add_newdoc('numpy.core.umath', 'bitwise_xor', @@ -667,6 +716,7 @@ def add_newdoc(place, name, doc): ---------- x1, x2 : array_like Only integer and boolean types are handled. + $BROADCASTABLE_2 $PARAMS Returns @@ -704,6 +754,14 @@ def add_newdoc(place, name, doc): >>> np.bitwise_xor([True, True], [False, True]) array([ True, False]) + The ``^`` operator can be used as a shorthand for ``np.bitwise_xor`` on + ndarrays. + + >>> x1 = np.array([True, True]) + >>> x2 = np.array([False, True]) + >>> x1 ^ x2 + array([ True, False]) + """) add_newdoc('numpy.core.umath', 'ceil', @@ -711,7 +769,7 @@ def add_newdoc(place, name, doc): Return the ceiling of the input, element-wise. The ceil of the scalar `x` is the smallest integer `i`, such that - `i >= x`. It is often denoted as :math:`\\lceil x \\rceil`. + ``i >= x``. It is often denoted as :math:`\\lceil x \\rceil`. Parameters ---------- @@ -727,7 +785,7 @@ def add_newdoc(place, name, doc): See Also -------- - floor, trunc, rint + floor, trunc, rint, fix Examples -------- @@ -759,7 +817,7 @@ def add_newdoc(place, name, doc): See Also -------- - ceil, floor, rint + ceil, floor, rint, fix Notes ----- @@ -792,6 +850,13 @@ def add_newdoc(place, name, doc): The complex conjugate of `x`, with same dtype as `y`. $OUT_SCALAR_1 + Notes + ----- + `conj` is an alias for `conjugate`: + + >>> np.conj is np.conjugate + True + Examples -------- >>> np.conjugate(1+2j) @@ -836,6 +901,7 @@ def add_newdoc(place, name, doc): array([ 1.00000000e+00, 6.12303177e-17, -1.00000000e+00]) >>> >>> # Example of providing the optional output parameter + >>> out1 = np.array([0], dtype='d') >>> out2 = np.cos([0.1], out1) >>> out2 is out1 True @@ -844,7 +910,7 @@ def add_newdoc(place, name, doc): >>> np.cos(np.zeros((3,3)),np.zeros((2,2))) Traceback (most recent call last): File "<stdin>", line 1, in <module> - ValueError: invalid return array shape + ValueError: operands could not be broadcast together with shapes (3,3) (2,2) """) @@ -911,7 +977,7 @@ def add_newdoc(place, name, doc): 270., 300., 330.]) >>> out = np.zeros((rad.shape)) - >>> r = degrees(rad, out) + >>> r = np.degrees(rad, out) >>> np.all(r == out) True @@ -969,6 +1035,7 @@ def add_newdoc(place, name, doc): Input values. x2 : array_like The value of the function when x1 is 0. + $BROADCASTABLE_2 $PARAMS Returns @@ -1004,6 +1071,7 @@ def add_newdoc(place, name, doc): Dividend array. x2 : array_like Divisor array. + $BROADCASTABLE_2 $PARAMS Returns @@ -1023,9 +1091,7 @@ def add_newdoc(place, name, doc): Behavior on division by zero can be changed using ``seterr``. - In Python 2, when both ``x1`` and ``x2`` are of an integer type, - ``divide`` will behave like ``floor_divide``. In Python 3, it behaves - like ``true_divide``. + Behaves like ``true_divide``. Examples -------- @@ -1038,31 +1104,20 @@ def add_newdoc(place, name, doc): [ Inf, 4. , 2.5], [ Inf, 7. , 4. ]]) - Note the behavior with integer types (Python 2 only): - - >>> np.divide(2, 4) - 0 - >>> np.divide(2, 4.) - 0.5 - - Division by zero always yields zero in integer arithmetic (again, - Python 2 only), and does not raise an exception or a warning: - - >>> np.divide(np.array([0, 1], dtype=int), np.array([0, 0], dtype=int)) - array([0, 0]) - - Division by zero can, however, be caught using ``seterr``: - - >>> old_err_state = np.seterr(divide='raise') - >>> np.divide(1, 0) - Traceback (most recent call last): - File "<stdin>", line 1, in <module> - FloatingPointError: divide by zero encountered in divide - >>> ignored_states = np.seterr(**old_err_state) >>> np.divide(1, 0) 0 + The ``/`` operator can be used as a shorthand for ``np.divide`` on + ndarrays. + + >>> x1 = np.arange(9.0).reshape((3, 3)) + >>> x2 = 2 * np.ones(3) + >>> x1 / x2 + array([[0. , 0.5, 1. ], + [1.5, 2. , 2.5], + [3. , 3.5, 4. ]]) + """) add_newdoc('numpy.core.umath', 'equal', @@ -1072,7 +1127,8 @@ def add_newdoc(place, name, doc): Parameters ---------- x1, x2 : array_like - Input arrays of the same shape. + Input arrays. + $BROADCASTABLE_2 $PARAMS Returns @@ -1097,6 +1153,14 @@ def add_newdoc(place, name, doc): >>> np.equal(1, np.ones(1)) array([ True]) + The ``==`` operator can be used as a shorthand for ``np.equal`` on + ndarrays. + + >>> a = np.array([2, 4, 6]) + >>> b = np.array([2, 4, 2]) + >>> a == b + array([ True, True, False]) + """) add_newdoc('numpy.core.umath', 'exp', @@ -1136,10 +1200,10 @@ def add_newdoc(place, name, doc): References ---------- .. [1] Wikipedia, "Exponential function", - http://en.wikipedia.org/wiki/Exponential_function + https://en.wikipedia.org/wiki/Exponential_function .. [2] M. Abramovitz and I. A. Stegun, "Handbook of Mathematical Functions with Formulas, Graphs, and Mathematical Tables," Dover, 1964, p. 69, - http://www.math.sfu.ca/~cbm/aands/page_69.htm + https://personal.math.ubc.ca/~cbm/aands/page_69.htm Examples -------- @@ -1204,7 +1268,7 @@ def add_newdoc(place, name, doc): Parameters ---------- x : array_like - Input values. + Input values. $PARAMS Returns @@ -1291,13 +1355,14 @@ def add_newdoc(place, name, doc): See Also -------- - ceil, trunc, rint + ceil, trunc, rint, fix Notes ----- - Some spreadsheet programs calculate the "floor-towards-zero", in other - words ``floor(-2.5) == -2``. NumPy instead uses the definition of - `floor` where `floor(-2.5) == -3`. + Some spreadsheet programs calculate the "floor-towards-zero", where + ``floor(-2.5) == -2``. NumPy instead uses the definition of + `floor` where `floor(-2.5) == -3`. The "floor-towards-zero" + function is called ``fix`` in NumPy. Examples -------- @@ -1311,7 +1376,7 @@ def add_newdoc(place, name, doc): """ Return the largest integer smaller or equal to the division of the inputs. It is equivalent to the Python ``//`` operator and pairs with the - Python ``%`` (`remainder`), function so that ``b = a % b + b * (a // b)`` + Python ``%`` (`remainder`), function so that ``a = a % b + b * (a // b)`` up to roundoff. Parameters @@ -1320,6 +1385,7 @@ def add_newdoc(place, name, doc): Numerator. x2 : array_like Denominator. + $BROADCASTABLE_2 $PARAMS Returns @@ -1343,11 +1409,18 @@ def add_newdoc(place, name, doc): >>> np.floor_divide([1., 2., 3., 4.], 2.5) array([ 0., 0., 1., 1.]) + The ``//`` operator can be used as a shorthand for ``np.floor_divide`` + on ndarrays. + + >>> x1 = np.array([1., 2., 3., 4.]) + >>> x1 // 2.5 + array([0., 0., 1., 1.]) + """) add_newdoc('numpy.core.umath', 'fmod', """ - Return the element-wise remainder of division. + Returns the element-wise remainder of division. This is the NumPy implementation of the C library function fmod, the remainder has the same sign as the dividend `x1`. It is equivalent to @@ -1360,6 +1433,7 @@ def add_newdoc(place, name, doc): Dividend. x2 : array_like Divisor. + $BROADCASTABLE_2 $PARAMS Returns @@ -1409,9 +1483,8 @@ def add_newdoc(place, name, doc): Parameters ---------- x1, x2 : array_like - Input arrays. If ``x1.shape != x2.shape``, they must be - broadcastable to a common shape (which may be the shape of one or - the other). + Input arrays. + $BROADCASTABLE_2 $PARAMS Returns @@ -1431,10 +1504,11 @@ def add_newdoc(place, name, doc): >>> np.greater([4,2],[2,2]) array([ True, False]) - If the inputs are ndarrays, then np.greater is equivalent to '>'. + The ``>`` operator can be used as a shorthand for ``np.greater`` on + ndarrays. - >>> a = np.array([4,2]) - >>> b = np.array([2,2]) + >>> a = np.array([4, 2]) + >>> b = np.array([2, 2]) >>> a > b array([ True, False]) @@ -1447,9 +1521,8 @@ def add_newdoc(place, name, doc): Parameters ---------- x1, x2 : array_like - Input arrays. If ``x1.shape != x2.shape``, they must be - broadcastable to a common shape (which may be the shape of one or - the other). + Input arrays. + $BROADCASTABLE_2 $PARAMS Returns @@ -1468,6 +1541,14 @@ def add_newdoc(place, name, doc): >>> np.greater_equal([4, 2, 1], [2, 2, 2]) array([ True, True, False]) + The ``>=`` operator can be used as a shorthand for ``np.greater_equal`` + on ndarrays. + + >>> a = np.array([4, 2, 1]) + >>> b = np.array([2, 2, 2]) + >>> a >= b + array([ True, True, False]) + """) add_newdoc('numpy.core.umath', 'hypot', @@ -1483,6 +1564,7 @@ def add_newdoc(place, name, doc): ---------- x1, x2 : array_like Leg of the triangle(s). + $BROADCASTABLE_2 $PARAMS Returns @@ -1551,47 +1633,52 @@ def add_newdoc(place, name, doc): References ---------- .. [1] Wikipedia, "Two's complement", - http://en.wikipedia.org/wiki/Two's_complement + https://en.wikipedia.org/wiki/Two's_complement Examples -------- We've seen that 13 is represented by ``00001101``. The invert or bit-wise NOT of 13 is then: - >>> np.invert(np.array([13], dtype=uint8)) - array([242], dtype=uint8) + >>> x = np.invert(np.array(13, dtype=np.uint8)) + >>> x + 242 >>> np.binary_repr(x, width=8) - '00001101' - >>> np.binary_repr(242, width=8) '11110010' The result depends on the bit-width: - >>> np.invert(np.array([13], dtype=uint16)) - array([65522], dtype=uint16) + >>> x = np.invert(np.array(13, dtype=np.uint16)) + >>> x + 65522 >>> np.binary_repr(x, width=16) - '0000000000001101' - >>> np.binary_repr(65522, width=16) '1111111111110010' When using signed integer types the result is the two's complement of the result for the unsigned type: - >>> np.invert(np.array([13], dtype=int8)) + >>> np.invert(np.array([13], dtype=np.int8)) array([-14], dtype=int8) >>> np.binary_repr(-14, width=8) '11110010' Booleans are accepted as well: - >>> np.invert(array([True, False])) + >>> np.invert(np.array([True, False])) + array([False, True]) + + The ``~`` operator can be used as a shorthand for ``np.invert`` on + ndarrays. + + >>> x1 = np.array([True, False]) + >>> ~x1 array([False, True]) """) add_newdoc('numpy.core.umath', 'isfinite', """ - Test element-wise for finiteness (not infinity or not Not a Number). + Test element-wise for finiteness (not infinity and not Not a Number). The result is returned as a boolean array. @@ -1740,6 +1827,8 @@ def add_newdoc(place, name, doc): """ Test element-wise for NaT (not a time) and return result as a boolean array. + .. versionadded:: 1.13.0 + Parameters ---------- x : array_like @@ -1781,6 +1870,7 @@ def add_newdoc(place, name, doc): Input values. x2 : array_like of integer type Number of zeros to append to `x1`. Has to be non-negative. + $BROADCASTABLE_2 $PARAMS Returns @@ -1807,6 +1897,25 @@ def add_newdoc(place, name, doc): >>> np.left_shift(5, [1,2,3]) array([10, 20, 40]) + Note that the dtype of the second argument may change the dtype of the + result and can lead to unexpected results in some cases (see + :ref:`Casting Rules <ufuncs.casting>`): + + >>> a = np.left_shift(np.uint8(255), 1) # Expect 254 + >>> print(a, type(a)) # Unexpected result due to upcasting + 510 <class 'numpy.int64'> + >>> b = np.left_shift(np.uint8(255), np.uint8(1)) + >>> print(b, type(b)) + 254 <class 'numpy.uint8'> + + The ``<<`` operator can be used as a shorthand for ``np.left_shift`` on + ndarrays. + + >>> x1 = 5 + >>> x2 = np.array([1, 2, 3]) + >>> x1 << x2 + array([10, 20, 40]) + """) add_newdoc('numpy.core.umath', 'less', @@ -1816,9 +1925,8 @@ def add_newdoc(place, name, doc): Parameters ---------- x1, x2 : array_like - Input arrays. If ``x1.shape != x2.shape``, they must be - broadcastable to a common shape (which may be the shape of one or - the other). + Input arrays. + $BROADCASTABLE_2 $PARAMS Returns @@ -1837,18 +1945,24 @@ def add_newdoc(place, name, doc): >>> np.less([1, 2], [2, 2]) array([ True, False]) + The ``<`` operator can be used as a shorthand for ``np.less`` on ndarrays. + + >>> a = np.array([1, 2]) + >>> b = np.array([2, 2]) + >>> a < b + array([ True, False]) + """) add_newdoc('numpy.core.umath', 'less_equal', """ - Return the truth value of (x1 =< x2) element-wise. + Return the truth value of (x1 <= x2) element-wise. Parameters ---------- x1, x2 : array_like - Input arrays. If ``x1.shape != x2.shape``, they must be - broadcastable to a common shape (which may be the shape of one or - the other). + Input arrays. + $BROADCASTABLE_2 $PARAMS Returns @@ -1867,6 +1981,14 @@ def add_newdoc(place, name, doc): >>> np.less_equal([4, 2, 1], [2, 2, 2]) array([False, True, True]) + The ``<=`` operator can be used as a shorthand for ``np.less_equal`` on + ndarrays. + + >>> a = np.array([4, 2, 1]) + >>> b = np.array([2, 2, 2]) + >>> a <= b + array([False, True, True]) + """) add_newdoc('numpy.core.umath', 'log', @@ -1911,8 +2033,9 @@ def add_newdoc(place, name, doc): References ---------- .. [1] M. Abramowitz and I.A. Stegun, "Handbook of Mathematical Functions", - 10th printing, 1964, pp. 67. http://www.math.sfu.ca/~cbm/aands/ - .. [2] Wikipedia, "Logarithm". http://en.wikipedia.org/wiki/Logarithm + 10th printing, 1964, pp. 67. + https://personal.math.ubc.ca/~cbm/aands/page_67.htm + .. [2] Wikipedia, "Logarithm". https://en.wikipedia.org/wiki/Logarithm Examples -------- @@ -1960,13 +2083,14 @@ def add_newdoc(place, name, doc): References ---------- .. [1] M. Abramowitz and I.A. Stegun, "Handbook of Mathematical Functions", - 10th printing, 1964, pp. 67. http://www.math.sfu.ca/~cbm/aands/ - .. [2] Wikipedia, "Logarithm". http://en.wikipedia.org/wiki/Logarithm + 10th printing, 1964, pp. 67. + https://personal.math.ubc.ca/~cbm/aands/page_67.htm + .. [2] Wikipedia, "Logarithm". https://en.wikipedia.org/wiki/Logarithm Examples -------- >>> np.log10([1e-15, -3.]) - array([-15., NaN]) + array([-15., nan]) """) @@ -2033,6 +2157,7 @@ def add_newdoc(place, name, doc): ---------- x1, x2 : array_like Input values. + $BROADCASTABLE_2 $PARAMS Returns @@ -2075,6 +2200,7 @@ def add_newdoc(place, name, doc): ---------- x1, x2 : array_like Input values. + $BROADCASTABLE_2 $PARAMS Returns @@ -2146,8 +2272,9 @@ def add_newdoc(place, name, doc): References ---------- .. [1] M. Abramowitz and I.A. Stegun, "Handbook of Mathematical Functions", - 10th printing, 1964, pp. 67. http://www.math.sfu.ca/~cbm/aands/ - .. [2] Wikipedia, "Logarithm". http://en.wikipedia.org/wiki/Logarithm + 10th printing, 1964, pp. 67. + https://personal.math.ubc.ca/~cbm/aands/page_67.htm + .. [2] Wikipedia, "Logarithm". https://en.wikipedia.org/wiki/Logarithm Examples -------- @@ -2165,14 +2292,15 @@ def add_newdoc(place, name, doc): Parameters ---------- x1, x2 : array_like - Input arrays. `x1` and `x2` must be of the same shape. + Input arrays. + $BROADCASTABLE_2 $PARAMS Returns ------- y : ndarray or bool - Boolean result with the same shape as `x1` and `x2` of the logical - AND operation on corresponding elements of `x1` and `x2`. + Boolean result of the logical AND operation applied to the elements + of `x1` and `x2`; the shape is determined by broadcasting. $OUT_SCALAR_2 See Also @@ -2191,6 +2319,15 @@ def add_newdoc(place, name, doc): >>> np.logical_and(x>1, x<4) array([False, False, True, True, False]) + + The ``&`` operator can be used as a shorthand for ``np.logical_and`` on + boolean ndarrays. + + >>> a = np.array([True, False]) + >>> b = np.array([False, False]) + >>> a & b + array([False, False]) + """) add_newdoc('numpy.core.umath', 'logical_not', @@ -2235,14 +2372,14 @@ def add_newdoc(place, name, doc): ---------- x1, x2 : array_like Logical OR is applied to the elements of `x1` and `x2`. - They have to be of the same shape. + $BROADCASTABLE_2 $PARAMS Returns ------- y : ndarray or bool - Boolean result with the same shape as `x1` and `x2` of the logical - OR operation on elements of `x1` and `x2`. + Boolean result of the logical OR operation applied to the elements + of `x1` and `x2`; the shape is determined by broadcasting. $OUT_SCALAR_2 See Also @@ -2261,6 +2398,14 @@ def add_newdoc(place, name, doc): >>> np.logical_or(x < 1, x > 3) array([ True, False, False, False, True]) + The ``|`` operator can be used as a shorthand for ``np.logical_or`` on + boolean ndarrays. + + >>> a = np.array([True, False]) + >>> b = np.array([False, False]) + >>> a | b + array([ True, False]) + """) add_newdoc('numpy.core.umath', 'logical_xor', @@ -2270,16 +2415,15 @@ def add_newdoc(place, name, doc): Parameters ---------- x1, x2 : array_like - Logical XOR is applied to the elements of `x1` and `x2`. They must - be broadcastable to the same shape. + Logical XOR is applied to the elements of `x1` and `x2`. + $BROADCASTABLE_2 $PARAMS Returns ------- y : bool or ndarray of bool Boolean result of the logical XOR operation applied to the elements - of `x1` and `x2`; the shape is determined by whether or not - broadcasting of one or both arrays was required. + of `x1` and `x2`; the shape is determined by broadcasting. $OUT_SCALAR_2 See Also @@ -2319,8 +2463,8 @@ def add_newdoc(place, name, doc): Parameters ---------- x1, x2 : array_like - The arrays holding the elements to be compared. They must have - the same shape, or shapes that can be broadcast to a single shape. + The arrays holding the elements to be compared. + $BROADCASTABLE_2 $PARAMS Returns @@ -2358,7 +2502,7 @@ def add_newdoc(place, name, doc): [ 0.5, 2. ]]) >>> np.maximum([np.nan, 0, np.nan], [0, np.nan, np.nan]) - array([ NaN, NaN, NaN]) + array([nan, nan, nan]) >>> np.maximum(np.Inf, 1) inf @@ -2378,8 +2522,8 @@ def add_newdoc(place, name, doc): Parameters ---------- x1, x2 : array_like - The arrays holding the elements to be compared. They must have - the same shape, or shapes that can be broadcast to a single shape. + The arrays holding the elements to be compared. + $BROADCASTABLE_2 $PARAMS Returns @@ -2417,7 +2561,7 @@ def add_newdoc(place, name, doc): [ 0. , 1. ]]) >>> np.minimum([np.nan, 0, np.nan],[0, np.nan, np.nan]) - array([ NaN, NaN, NaN]) + array([nan, nan, nan]) >>> np.minimum(-np.Inf, 1) -inf @@ -2437,8 +2581,8 @@ def add_newdoc(place, name, doc): Parameters ---------- x1, x2 : array_like - The arrays holding the elements to be compared. They must have - the same shape. + The arrays holding the elements to be compared. + $BROADCASTABLE_2 $PARAMS Returns @@ -2477,7 +2621,7 @@ def add_newdoc(place, name, doc): [ 0.5, 2. ]]) >>> np.fmax([np.nan, 0, np.nan],[0, np.nan, np.nan]) - array([ 0., 0., NaN]) + array([ 0., 0., nan]) """) @@ -2495,8 +2639,8 @@ def add_newdoc(place, name, doc): Parameters ---------- x1, x2 : array_like - The arrays holding the elements to be compared. They must have - the same shape. + The arrays holding the elements to be compared. + $BROADCASTABLE_2 $PARAMS Returns @@ -2535,10 +2679,181 @@ def add_newdoc(place, name, doc): [ 0. , 1. ]]) >>> np.fmin([np.nan, 0, np.nan],[0, np.nan, np.nan]) - array([ 0., 0., NaN]) + array([ 0., 0., nan]) """) +add_newdoc('numpy.core.umath', 'clip', + """ + Clip (limit) the values in an array. + + Given an interval, values outside the interval are clipped to + the interval edges. For example, if an interval of ``[0, 1]`` + is specified, values smaller than 0 become 0, and values larger + than 1 become 1. + + Equivalent to but faster than ``np.minimum(np.maximum(a, a_min), a_max)``. + + Parameters + ---------- + a : array_like + Array containing elements to clip. + a_min : array_like + Minimum value. + a_max : array_like + Maximum value. + out : ndarray, optional + The results will be placed in this array. It may be the input + array for in-place clipping. `out` must be of the right shape + to hold the output. Its type is preserved. + $PARAMS + + See Also + -------- + numpy.clip : + Wrapper that makes the ``a_min`` and ``a_max`` arguments optional, + dispatching to one of `~numpy.core.umath.clip`, + `~numpy.core.umath.minimum`, and `~numpy.core.umath.maximum`. + + Returns + ------- + clipped_array : ndarray + An array with the elements of `a`, but where values + < `a_min` are replaced with `a_min`, and those > `a_max` + with `a_max`. + """) + +add_newdoc('numpy.core.umath', 'matmul', + """ + Matrix product of two arrays. + + Parameters + ---------- + x1, x2 : array_like + Input arrays, scalars not allowed. + out : ndarray, optional + A location into which the result is stored. If provided, it must have + a shape that matches the signature `(n,k),(k,m)->(n,m)`. If not + provided or None, a freshly-allocated array is returned. + **kwargs + For other keyword-only arguments, see the + :ref:`ufunc docs <ufuncs.kwargs>`. + + .. versionadded:: 1.16 + Now handles ufunc kwargs + + Returns + ------- + y : ndarray + The matrix product of the inputs. + This is a scalar only when both x1, x2 are 1-d vectors. + + Raises + ------ + ValueError + If the last dimension of `x1` is not the same size as + the second-to-last dimension of `x2`. + + If a scalar value is passed in. + + See Also + -------- + vdot : Complex-conjugating dot product. + tensordot : Sum products over arbitrary axes. + einsum : Einstein summation convention. + dot : alternative matrix product with different broadcasting rules. + + Notes + ----- + + The behavior depends on the arguments in the following way. + + - If both arguments are 2-D they are multiplied like conventional + matrices. + - If either argument is N-D, N > 2, it is treated as a stack of + matrices residing in the last two indexes and broadcast accordingly. + - If the first argument is 1-D, it is promoted to a matrix by + prepending a 1 to its dimensions. After matrix multiplication + the prepended 1 is removed. + - If the second argument is 1-D, it is promoted to a matrix by + appending a 1 to its dimensions. After matrix multiplication + the appended 1 is removed. + + ``matmul`` differs from ``dot`` in two important ways: + + - Multiplication by scalars is not allowed, use ``*`` instead. + - Stacks of matrices are broadcast together as if the matrices + were elements, respecting the signature ``(n,k),(k,m)->(n,m)``: + + >>> a = np.ones([9, 5, 7, 4]) + >>> c = np.ones([9, 5, 4, 3]) + >>> np.dot(a, c).shape + (9, 5, 7, 9, 5, 3) + >>> np.matmul(a, c).shape + (9, 5, 7, 3) + >>> # n is 7, k is 4, m is 3 + + The matmul function implements the semantics of the ``@`` operator introduced + in Python 3.5 following :pep:`465`. + + Examples + -------- + For 2-D arrays it is the matrix product: + + >>> a = np.array([[1, 0], + ... [0, 1]]) + >>> b = np.array([[4, 1], + ... [2, 2]]) + >>> np.matmul(a, b) + array([[4, 1], + [2, 2]]) + + For 2-D mixed with 1-D, the result is the usual. + + >>> a = np.array([[1, 0], + ... [0, 1]]) + >>> b = np.array([1, 2]) + >>> np.matmul(a, b) + array([1, 2]) + >>> np.matmul(b, a) + array([1, 2]) + + + Broadcasting is conventional for stacks of arrays + + >>> a = np.arange(2 * 2 * 4).reshape((2, 2, 4)) + >>> b = np.arange(2 * 2 * 4).reshape((2, 4, 2)) + >>> np.matmul(a,b).shape + (2, 2, 2) + >>> np.matmul(a, b)[0, 1, 1] + 98 + >>> sum(a[0, 1, :] * b[0 , :, 1]) + 98 + + Vector, vector returns the scalar inner product, but neither argument + is complex-conjugated: + + >>> np.matmul([2j, 3j], [2j, 3j]) + (-13+0j) + + Scalar multiplication raises an error. + + >>> np.matmul([1,2], 3) + Traceback (most recent call last): + ... + ValueError: matmul: Input operand 1 does not have enough dimensions ... + + The ``@`` operator can be used as a shorthand for ``np.matmul`` on + ndarrays. + + >>> x1 = np.array([2j, 3j]) + >>> x2 = np.array([2j, 3j]) + >>> x1 @ x2 + (-13+0j) + + .. versionadded:: 1.10.0 + """) + add_newdoc('numpy.core.umath', 'modf', """ Return the fractional and integral parts of an array, element-wise. @@ -2587,13 +2902,13 @@ def add_newdoc(place, name, doc): ---------- x1, x2 : array_like Input arrays to be multiplied. + $BROADCASTABLE_2 $PARAMS Returns ------- y : ndarray - The product of `x1` and `x2`, element-wise. Returns a scalar if - both `x1` and `x2` are scalars. + The product of `x1` and `x2`, element-wise. $OUT_SCALAR_2 Notes @@ -2612,6 +2927,16 @@ def add_newdoc(place, name, doc): [ 0., 4., 10.], [ 0., 7., 16.]]) + The ``*`` operator can be used as a shorthand for ``np.multiply`` on + ndarrays. + + >>> x1 = np.arange(9.0).reshape((3, 3)) + >>> x2 = np.arange(3.0) + >>> x1 * x2 + array([[ 0., 1., 4.], + [ 0., 4., 10.], + [ 0., 7., 16.]]) + """) add_newdoc('numpy.core.umath', 'negative', @@ -2635,6 +2960,13 @@ def add_newdoc(place, name, doc): >>> np.negative([1.,-1.]) array([-1., 1.]) + The unary ``-`` operator can be used as a shorthand for ``np.negative`` on + ndarrays. + + >>> x1 = np.array(([1., -1.])) + >>> -x1 + array([-1., 1.]) + """) add_newdoc('numpy.core.umath', 'positive', @@ -2659,6 +2991,20 @@ def add_newdoc(place, name, doc): Equivalent to `x.copy()`, but only defined for types that support arithmetic. + Examples + -------- + + >>> x1 = np.array(([1., -1.])) + >>> np.positive(x1) + array([ 1., -1.]) + + The unary ``+`` operator can be used as a shorthand for ``np.positive`` on + ndarrays. + + >>> x1 = np.array(([1., -1.])) + >>> +x1 + array([ 1., -1.]) + """) add_newdoc('numpy.core.umath', 'not_equal', @@ -2669,6 +3015,7 @@ def add_newdoc(place, name, doc): ---------- x1, x2 : array_like Input arrays. + $BROADCASTABLE_2 $PARAMS Returns @@ -2690,6 +3037,15 @@ def add_newdoc(place, name, doc): array([[False, True], [False, True]]) + The ``!=`` operator can be used as a shorthand for ``np.not_equal`` on + ndarrays. + + >>> a = np.array([1., 2.]) + >>> b = np.array([1., 3.]) + >>> a != b + array([False, True]) + + """) add_newdoc('numpy.core.umath', '_ones_like', @@ -2709,8 +3065,14 @@ def add_newdoc(place, name, doc): First array elements raised to powers from second array, element-wise. Raise each base in `x1` to the positionally-corresponding power in - `x2`. `x1` and `x2` must be broadcastable to the same shape. Note that an - integer type raised to a negative integer power will raise a ValueError. + `x2`. `x1` and `x2` must be broadcastable to the same shape. + + An integer type raised to a negative integer power will raise a + ``ValueError``. + + Negative values raised to a non-integral value will return ``nan``. + To get complex results, cast the input to complex, or specify the + ``dtype`` to be ``complex`` (see the example below). Parameters ---------- @@ -2718,6 +3080,7 @@ def add_newdoc(place, name, doc): The bases. x2 : array_like The exponents. + $BROADCASTABLE_2 $PARAMS Returns @@ -2732,9 +3095,9 @@ def add_newdoc(place, name, doc): Examples -------- - Cube each element in a list. + Cube each element in an array. - >>> x1 = range(6) + >>> x1 = np.arange(6) >>> x1 [0, 1, 2, 3, 4, 5] >>> np.power(x1, 3) @@ -2756,6 +3119,29 @@ def add_newdoc(place, name, doc): array([[ 0, 1, 8, 27, 16, 5], [ 0, 1, 8, 27, 16, 5]]) + The ``**`` operator can be used as a shorthand for ``np.power`` on + ndarrays. + + >>> x2 = np.array([1, 2, 3, 3, 2, 1]) + >>> x1 = np.arange(6) + >>> x1 ** x2 + array([ 0, 1, 8, 27, 16, 5]) + + Negative values raised to a non-integral value will result in ``nan`` + (and a warning will be generated). + + >>> x3 = np.array([-1.0, -4.0]) + >>> with np.errstate(invalid='ignore'): + ... p = np.power(x3, 1.5) + ... + >>> p + array([nan, nan]) + + To get complex results, give the argument ``dtype=complex``. + + >>> np.power(x3, 1.5, dtype=complex) + array([-1.83697020e-16-1.j, -1.46957616e-15-8.j]) + """) add_newdoc('numpy.core.umath', 'float_power', @@ -2769,6 +3155,10 @@ def add_newdoc(place, name, doc): inexact. The intent is that the function will return a usable result for negative powers and seldom overflow for positive powers. + Negative values raised to a non-integral value will return ``nan``. + To get complex results, cast the input to complex, or specify the + ``dtype`` to be ``complex`` (see the example below). + .. versionadded:: 1.12.0 Parameters @@ -2777,6 +3167,7 @@ def add_newdoc(place, name, doc): The bases. x2 : array_like The exponents. + $BROADCASTABLE_2 $PARAMS Returns @@ -2815,6 +3206,21 @@ def add_newdoc(place, name, doc): array([[ 0., 1., 8., 27., 16., 5.], [ 0., 1., 8., 27., 16., 5.]]) + Negative values raised to a non-integral value will result in ``nan`` + (and a warning will be generated). + + >>> x3 = np.array([-1, -4]) + >>> with np.errstate(invalid='ignore'): + ... p = np.float_power(x3, 1.5) + ... + >>> p + array([nan, nan]) + + To get complex results, give the argument ``dtype=complex``. + + >>> np.float_power(x3, 1.5, dtype=complex) + array([-1.83697020e-16-1.j, -1.46957616e-15-8.j]) + """) add_newdoc('numpy.core.umath', 'radians', @@ -2926,7 +3332,7 @@ def add_newdoc(place, name, doc): add_newdoc('numpy.core.umath', 'remainder', """ - Return element-wise remainder of division. + Returns the element-wise remainder of division. Computes the remainder complementary to the `floor_divide` function. It is equivalent to the Python modulus operator``x1 % x2`` and has the same sign @@ -2949,6 +3355,7 @@ def add_newdoc(place, name, doc): Dividend array. x2 : array_like Divisor array. + $BROADCASTABLE_2 $PARAMS Returns @@ -2968,6 +3375,7 @@ def add_newdoc(place, name, doc): ----- Returns 0 when `x2` is 0 and both `x1` and `x2` are (arrays of) integers. + ``mod`` is an alias of ``remainder``. Examples -------- @@ -2976,6 +3384,13 @@ def add_newdoc(place, name, doc): >>> np.remainder(np.arange(7), 5) array([0, 1, 2, 3, 4, 0, 1]) + The ``%`` operator can be used as a shorthand for ``np.remainder`` on + ndarrays. + + >>> x1 = np.arange(7) + >>> x1 % 5 + array([0, 1, 2, 3, 4, 0, 1]) + """) add_newdoc('numpy.core.umath', 'divmod', @@ -2994,6 +3409,7 @@ def add_newdoc(place, name, doc): Dividend array. x2 : array_like Divisor array. + $BROADCASTABLE_2 $PARAMS Returns @@ -3017,6 +3433,13 @@ def add_newdoc(place, name, doc): >>> np.divmod(np.arange(5), 3) (array([0, 0, 0, 1, 1]), array([0, 1, 2, 0, 1])) + The `divmod` function can be used as a shorthand for ``np.divmod`` on + ndarrays. + + >>> x = np.arange(5) + >>> divmod(x, 3) + (array([0, 0, 0, 1, 1]), array([0, 1, 2, 0, 1])) + """) add_newdoc('numpy.core.umath', 'right_shift', @@ -3033,6 +3456,7 @@ def add_newdoc(place, name, doc): Input values. x2 : array_like, int Number of bits to remove at the right of `x1`. + $BROADCASTABLE_2 $PARAMS Returns @@ -3059,6 +3483,14 @@ def add_newdoc(place, name, doc): >>> np.right_shift(10, [1,2,3]) array([5, 2, 1]) + The ``>>`` operator can be used as a shorthand for ``np.right_shift`` on + ndarrays. + + >>> x1 = 10 + >>> x2 = np.array([1,2,3]) + >>> x1 >> x2 + array([5, 2, 1]) + """) add_newdoc('numpy.core.umath', 'rint', @@ -3079,7 +3511,13 @@ def add_newdoc(place, name, doc): See Also -------- - ceil, floor, trunc + fix, ceil, floor, trunc + + Notes + ----- + For values exactly halfway between rounded decimal values, NumPy + rounds to the nearest even value. Thus 1.5 and 2.5 round to 2.0, + -0.5 and 0.5 round to 0.0, etc. Examples -------- @@ -3159,9 +3597,7 @@ def add_newdoc(place, name, doc): """ Change the sign of x1 to that of x2, element-wise. - If both arguments are arrays or sequences, they have to be of the same - length. If `x2` is a scalar, its sign will be copied to all elements of - `x1`. + If `x2` is a scalar, its sign will be copied to all elements of `x1`. Parameters ---------- @@ -3169,6 +3605,7 @@ def add_newdoc(place, name, doc): Values to change the sign of. x2 : array_like The sign of `x2` is copied to `x1`. + $BROADCASTABLE_2 $PARAMS Returns @@ -3203,6 +3640,7 @@ def add_newdoc(place, name, doc): Values to find the next representable value of. x2 : array_like The direction where to look for the next representable value of `x1`. + $BROADCASTABLE_2 $PARAMS Returns @@ -3351,6 +3789,7 @@ def add_newdoc(place, name, doc): >>> # Discrepancy due to vagaries of floating point arithmetic. >>> # Example of providing the optional output parameter + >>> out1 = np.array([0], dtype='d') >>> out2 = np.sinh([0.1], out1) >>> out2 is out1 True @@ -3359,7 +3798,7 @@ def add_newdoc(place, name, doc): >>> np.sinh(np.zeros((3,3)),np.zeros((2,2))) Traceback (most recent call last): File "<stdin>", line 1, in <module> - ValueError: invalid return array shape + ValueError: operands could not be broadcast together with shapes (3,3) (2,2) """) @@ -3404,8 +3843,8 @@ def add_newdoc(place, name, doc): >>> np.sqrt([4, -1, -3+4J]) array([ 2.+0.j, 0.+1.j, 1.+2.j]) - >>> np.sqrt([4, -1, numpy.inf]) - array([ 2., NaN, Inf]) + >>> np.sqrt([4, -1, np.inf]) + array([ 2., nan, inf]) """) @@ -3474,6 +3913,7 @@ def add_newdoc(place, name, doc): ---------- x1, x2 : array_like The arrays to be subtracted from each other. + $BROADCASTABLE_2 $PARAMS Returns @@ -3498,6 +3938,16 @@ def add_newdoc(place, name, doc): [ 3., 3., 3.], [ 6., 6., 6.]]) + The ``-`` operator can be used as a shorthand for ``np.subtract`` on + ndarrays. + + >>> x1 = np.arange(9.0).reshape((3, 3)) + >>> x2 = np.arange(3.0) + >>> x1 - x2 + array([[0., 0., 0.], + [3., 3., 3.], + [6., 6., 6.]]) + """) add_newdoc('numpy.core.umath', 'tan', @@ -3536,6 +3986,7 @@ def add_newdoc(place, name, doc): >>> >>> # Example of providing the optional output parameter illustrating >>> # that what is returned is a reference to said parameter + >>> out1 = np.array([0], dtype='d') >>> out2 = np.cos([0.1], out1) >>> out2 is out1 True @@ -3544,7 +3995,7 @@ def add_newdoc(place, name, doc): >>> np.cos(np.zeros((3,3)),np.zeros((2,2))) Traceback (most recent call last): File "<stdin>", line 1, in <module> - ValueError: invalid return array shape + ValueError: operands could not be broadcast together with shapes (3,3) (2,2) """) @@ -3575,10 +4026,10 @@ def add_newdoc(place, name, doc): ---------- .. [1] M. Abramowitz and I. A. Stegun, Handbook of Mathematical Functions. New York, NY: Dover, 1972, pg. 83. - http://www.math.sfu.ca/~cbm/aands/ + https://personal.math.ubc.ca/~cbm/aands/page_83.htm .. [2] Wikipedia, "Hyperbolic function", - http://en.wikipedia.org/wiki/Hyperbolic_function + https://en.wikipedia.org/wiki/Hyperbolic_function Examples -------- @@ -3587,6 +4038,7 @@ def add_newdoc(place, name, doc): >>> # Example of providing the optional output parameter illustrating >>> # that what is returned is a reference to said parameter + >>> out1 = np.array([0], dtype='d') >>> out2 = np.tanh([0.1], out1) >>> out2 is out1 True @@ -3595,7 +4047,7 @@ def add_newdoc(place, name, doc): >>> np.tanh(np.zeros((3,3)),np.zeros((2,2))) Traceback (most recent call last): File "<stdin>", line 1, in <module> - ValueError: invalid return array shape + ValueError: operands could not be broadcast together with shapes (3,3) (2,2) """) @@ -3603,9 +4055,8 @@ def add_newdoc(place, name, doc): """ Returns a true division of the inputs, element-wise. - Instead of the Python traditional 'floor division', this returns a true - division. True division adjusts the output type to present the best - answer, regardless of input types. + Unlike 'floor division', true division adjusts the output type + to present the best answer, regardless of input types. Parameters ---------- @@ -3613,6 +4064,7 @@ def add_newdoc(place, name, doc): Dividend array. x2 : array_like Divisor array. + $BROADCASTABLE_2 $PARAMS Returns @@ -3622,12 +4074,7 @@ def add_newdoc(place, name, doc): Notes ----- - The floor division operator ``//`` was added in Python 2.2 making - ``//`` and ``/`` equivalent operators. The default floor division - operation of ``/`` can be replaced by true division with ``from - __future__ import division``. - - In Python 3.0, ``//`` is the floor division operator and ``/`` the + In Python, ``//`` is the floor division operator and ``/`` the true division operator. The ``true_divide(x1, x2)`` function is equivalent to true division in Python. @@ -3637,17 +4084,19 @@ def add_newdoc(place, name, doc): >>> np.true_divide(x, 4) array([ 0. , 0.25, 0.5 , 0.75, 1. ]) - >>> x/4 - array([0, 0, 0, 0, 1]) - >>> x//4 - array([0, 0, 0, 0, 1]) - - >>> from __future__ import division >>> x/4 array([ 0. , 0.25, 0.5 , 0.75, 1. ]) + >>> x//4 array([0, 0, 0, 0, 1]) + The ``/`` operator can be used as a shorthand for ``np.true_divide`` on + ndarrays. + + >>> x = np.arange(5) + >>> x / 4 + array([0. , 0.25, 0.5 , 0.75, 1. ]) + """) add_newdoc('numpy.core.umath', 'frexp', @@ -3655,7 +4104,7 @@ def add_newdoc(place, name, doc): Decompose the elements of x into mantissa and twos exponent. Returns (`mantissa`, `exponent`), where `x = mantissa * 2**exponent``. - The mantissa is lies in the open interval(-1, 1), while the twos + The mantissa lies in the open interval(-1, 1), while the twos exponent is a signed integer. Parameters @@ -3712,6 +4161,7 @@ def add_newdoc(place, name, doc): Array of multipliers. x2 : array_like, int Array of twos exponents. + $BROADCASTABLE_2 $PARAMS Returns @@ -3734,7 +4184,7 @@ def add_newdoc(place, name, doc): Examples -------- >>> np.ldexp(5, np.arange(4)) - array([ 5., 10., 20., 40.], dtype=float32) + array([ 5., 10., 20., 40.], dtype=float16) >>> x = np.arange(6) >>> np.ldexp(*np.frexp(x)) @@ -3749,7 +4199,8 @@ def add_newdoc(place, name, doc): Parameters ---------- x1, x2 : array_like, int - Arrays of values + Arrays of values. + $BROADCASTABLE_2 Returns ------- @@ -3779,7 +4230,8 @@ def add_newdoc(place, name, doc): Parameters ---------- x1, x2 : array_like, int - Arrays of values + Arrays of values. + $BROADCASTABLE_2 Returns ------- diff --git a/numpy/core/cversions.py b/numpy/core/cversions.py index 7995dd9931e7..00159c3a8031 100644 --- a/numpy/core/cversions.py +++ b/numpy/core/cversions.py @@ -3,8 +3,6 @@ The API has is defined by numpy_api_order and ufunc_api_order. """ -from __future__ import division, absolute_import, print_function - from os.path import dirname from code_generators.genapi import fullapi_hash diff --git a/numpy/core/defchararray.py b/numpy/core/defchararray.py index 6d0a0add58da..3521e778e1bd 100644 --- a/numpy/core/defchararray.py +++ b/numpy/core/defchararray.py @@ -15,18 +15,19 @@ The preferred alias for `defchararray` is `numpy.char`. """ -from __future__ import division, absolute_import, print_function - -import sys -from .numerictypes import string_, unicode_, integer, object_, bool_, character +import functools +from .numerictypes import ( + string_, unicode_, integer, int_, object_, bool_, character) from .numeric import ndarray, compare_chararrays from .numeric import array as narray from numpy.core.multiarray import _vec_string -from numpy.compat import asbytes, long +from numpy.core.overrides import set_module +from numpy.core import overrides +from numpy.compat import asbytes import numpy __all__ = [ - 'chararray', 'equal', 'not_equal', 'greater_equal', 'less_equal', + 'equal', 'not_equal', 'greater_equal', 'less_equal', 'greater', 'less', 'str_len', 'add', 'multiply', 'mod', 'capitalize', 'center', 'count', 'decode', 'encode', 'endswith', 'expandtabs', 'find', 'index', 'isalnum', 'isalpha', 'isdigit', 'islower', 'isspace', @@ -39,13 +40,10 @@ _globalvar = 0 -if sys.version_info[0] >= 3: - _unicode = str - _bytes = bytes -else: - _unicode = unicode - _bytes = str -_len = len + +array_function_dispatch = functools.partial( + overrides.array_function_dispatch, module='numpy.char') + def _use_unicode(*args): """ @@ -56,7 +54,7 @@ def _use_unicode(*args): result should be unicode. """ for x in args: - if (isinstance(x, _unicode) or + if (isinstance(x, str) or issubclass(numpy.asarray(x).dtype.type, unicode_)): return unicode_ return string_ @@ -75,7 +73,7 @@ def _clean_args(*args): Many of the Python string operations that have optional arguments do not use 'None' to indicate a default value. In these cases, - we need to remove all `None` arguments, and those following them. + we need to remove all None arguments, and those following them. """ newargs = [] for chk in args: @@ -95,6 +93,11 @@ def _get_num_chars(a): return a.itemsize +def _binary_op_dispatcher(x1, x2): + return (x1, x2) + + +@array_function_dispatch(_binary_op_dispatcher) def equal(x1, x2): """ Return (x1 == x2) element-wise. @@ -110,8 +113,8 @@ def equal(x1, x2): Returns ------- - out : ndarray or bool - Output array of bools, or a single bool if x1 and x2 are scalars. + out : ndarray + Output array of bools. See Also -------- @@ -119,6 +122,8 @@ def equal(x1, x2): """ return compare_chararrays(x1, x2, '==', True) + +@array_function_dispatch(_binary_op_dispatcher) def not_equal(x1, x2): """ Return (x1 != x2) element-wise. @@ -134,8 +139,8 @@ def not_equal(x1, x2): Returns ------- - out : ndarray or bool - Output array of bools, or a single bool if x1 and x2 are scalars. + out : ndarray + Output array of bools. See Also -------- @@ -143,6 +148,8 @@ def not_equal(x1, x2): """ return compare_chararrays(x1, x2, '!=', True) + +@array_function_dispatch(_binary_op_dispatcher) def greater_equal(x1, x2): """ Return (x1 >= x2) element-wise. @@ -159,8 +166,8 @@ def greater_equal(x1, x2): Returns ------- - out : ndarray or bool - Output array of bools, or a single bool if x1 and x2 are scalars. + out : ndarray + Output array of bools. See Also -------- @@ -168,6 +175,8 @@ def greater_equal(x1, x2): """ return compare_chararrays(x1, x2, '>=', True) + +@array_function_dispatch(_binary_op_dispatcher) def less_equal(x1, x2): """ Return (x1 <= x2) element-wise. @@ -183,8 +192,8 @@ def less_equal(x1, x2): Returns ------- - out : ndarray or bool - Output array of bools, or a single bool if x1 and x2 are scalars. + out : ndarray + Output array of bools. See Also -------- @@ -192,6 +201,8 @@ def less_equal(x1, x2): """ return compare_chararrays(x1, x2, '<=', True) + +@array_function_dispatch(_binary_op_dispatcher) def greater(x1, x2): """ Return (x1 > x2) element-wise. @@ -207,8 +218,8 @@ def greater(x1, x2): Returns ------- - out : ndarray or bool - Output array of bools, or a single bool if x1 and x2 are scalars. + out : ndarray + Output array of bools. See Also -------- @@ -216,6 +227,8 @@ def greater(x1, x2): """ return compare_chararrays(x1, x2, '>', True) + +@array_function_dispatch(_binary_op_dispatcher) def less(x1, x2): """ Return (x1 < x2) element-wise. @@ -231,8 +244,8 @@ def less(x1, x2): Returns ------- - out : ndarray or bool - Output array of bools, or a single bool if x1 and x2 are scalars. + out : ndarray + Output array of bools. See Also -------- @@ -240,6 +253,12 @@ def less(x1, x2): """ return compare_chararrays(x1, x2, '<', True) + +def _unary_op_dispatcher(a): + return (a,) + + +@array_function_dispatch(_unary_op_dispatcher) def str_len(a): """ Return len(a) element-wise. @@ -253,12 +272,17 @@ def str_len(a): out : ndarray Output array of integers - See also + See Also -------- - __builtin__.len + builtins.len """ - return _vec_string(a, integer, '__len__') + # Note: __len__, etc. currently return ints, which are not C-integers. + # Generally intp would be expected for lengths, although int is sufficient + # due to the dtype itemsize limitation. + return _vec_string(a, int_, '__len__') + +@array_function_dispatch(_binary_op_dispatcher) def add(x1, x2): """ Return element-wise string concatenation for two arrays of str or unicode. @@ -285,6 +309,12 @@ def add(x1, x2): dtype = _use_unicode(arr1, arr2) return _vec_string(arr1, (dtype, out_size), '__add__', (arr2,)) + +def _multiply_dispatcher(a, i): + return (a,) + + +@array_function_dispatch(_multiply_dispatcher) def multiply(a, i): """ Return (a * i), that is string multiple concatenation, @@ -309,14 +339,20 @@ def multiply(a, i): i_arr = numpy.asarray(i) if not issubclass(i_arr.dtype.type, integer): raise ValueError("Can only multiply by integers") - out_size = _get_num_chars(a_arr) * max(long(i_arr.max()), 0) + out_size = _get_num_chars(a_arr) * max(int(i_arr.max()), 0) return _vec_string( a_arr, (a_arr.dtype.type, out_size), '__mul__', (i_arr,)) + +def _mod_dispatcher(a, values): + return (a, values) + + +@array_function_dispatch(_mod_dispatcher) def mod(a, values): """ Return (a % i), that is pre-Python 2.6 string formatting - (iterpolation), element-wise for a pair of array_likes of str + (interpolation), element-wise for a pair of array_likes of str or unicode. Parameters @@ -331,7 +367,7 @@ def mod(a, values): out : ndarray Output array of str or unicode, depending on input types - See also + See Also -------- str.__mod__ @@ -339,6 +375,8 @@ def mod(a, values): return _to_string_or_unicode_array( _vec_string(a, object_, '__mod__', (values,))) + +@array_function_dispatch(_unary_op_dispatcher) def capitalize(a): """ Return a copy of `a` with only the first character of each element @@ -359,7 +397,7 @@ def capitalize(a): Output array of str or unicode, depending on input types - See also + See Also -------- str.capitalize @@ -377,6 +415,11 @@ def capitalize(a): return _vec_string(a_arr, a_arr.dtype, 'capitalize') +def _center_dispatcher(a, width, fillchar=None): + return (a,) + + +@array_function_dispatch(_center_dispatcher) def center(a, width, fillchar=' '): """ Return a copy of `a` with its elements centered in a string of @@ -399,20 +442,25 @@ def center(a, width, fillchar=' '): Output array of str or unicode, depending on input types - See also + See Also -------- str.center """ a_arr = numpy.asarray(a) width_arr = numpy.asarray(width) - size = long(numpy.max(width_arr.flat)) + size = int(numpy.max(width_arr.flat)) if numpy.issubdtype(a_arr.dtype, numpy.string_): fillchar = asbytes(fillchar) return _vec_string( a_arr, (a_arr.dtype.type, size), 'center', (width_arr, fillchar)) +def _count_dispatcher(a, sub, start=None, end=None): + return (a,) + + +@array_function_dispatch(_count_dispatcher) def count(a, sub, start=0, end=None): """ Returns an array with the number of non-overlapping occurrences of @@ -436,7 +484,7 @@ def count(a, sub, start=0, end=None): out : ndarray Output array of ints. - See also + See Also -------- str.count @@ -444,8 +492,7 @@ def count(a, sub, start=0, end=None): -------- >>> c = np.array(['aAaAaA', ' aA ', 'abBABba']) >>> c - array(['aAaAaA', ' aA ', 'abBABba'], - dtype='|S7') + array(['aAaAaA', ' aA ', 'abBABba'], dtype='<U7') >>> np.char.count(c, 'A') array([3, 1, 1]) >>> np.char.count(c, 'aA') @@ -456,9 +503,14 @@ def count(a, sub, start=0, end=None): array([1, 0, 0]) """ - return _vec_string(a, integer, 'count', [sub, start] + _clean_args(end)) + return _vec_string(a, int_, 'count', [sub, start] + _clean_args(end)) + +def _code_dispatcher(a, encoding=None, errors=None): + return (a,) + +@array_function_dispatch(_code_dispatcher) def decode(a, encoding=None, errors=None): """ Calls `str.decode` element-wise. @@ -481,7 +533,7 @@ def decode(a, encoding=None, errors=None): ------- out : ndarray - See also + See Also -------- str.decode @@ -493,8 +545,7 @@ def decode(a, encoding=None, errors=None): -------- >>> c = np.array(['aAaAaA', ' aA ', 'abBABba']) >>> c - array(['aAaAaA', ' aA ', 'abBABba'], - dtype='|S7') + array(['aAaAaA', ' aA ', 'abBABba'], dtype='<U7') >>> np.char.encode(c, encoding='cp037') array(['\\x81\\xc1\\x81\\xc1\\x81\\xc1', '@@\\x81\\xc1@@', '\\x81\\x82\\xc2\\xc1\\xc2\\x82\\x81'], @@ -505,6 +556,7 @@ def decode(a, encoding=None, errors=None): _vec_string(a, object_, 'decode', _clean_args(encoding, errors))) +@array_function_dispatch(_code_dispatcher) def encode(a, encoding=None, errors=None): """ Calls `str.encode` element-wise. @@ -527,7 +579,7 @@ def encode(a, encoding=None, errors=None): ------- out : ndarray - See also + See Also -------- str.encode @@ -540,6 +592,11 @@ def encode(a, encoding=None, errors=None): _vec_string(a, object_, 'encode', _clean_args(encoding, errors))) +def _endswith_dispatcher(a, suffix, start=None, end=None): + return (a,) + + +@array_function_dispatch(_endswith_dispatcher) def endswith(a, suffix, start=0, end=None): """ Returns a boolean array which is `True` where the string element @@ -562,7 +619,7 @@ def endswith(a, suffix, start=0, end=None): out : ndarray Outputs an array of bools. - See also + See Also -------- str.endswith @@ -572,8 +629,7 @@ def endswith(a, suffix, start=0, end=None): >>> s[0] = 'foo' >>> s[1] = 'bar' >>> s - array(['foo', 'bar'], - dtype='|S3') + array(['foo', 'bar'], dtype='<U3') >>> np.char.endswith(s, 'ar') array([False, True]) >>> np.char.endswith(s, 'a', start=1, end=2) @@ -584,6 +640,11 @@ def endswith(a, suffix, start=0, end=None): a, bool_, 'endswith', [suffix, start] + _clean_args(end)) +def _expandtabs_dispatcher(a, tabsize=None): + return (a,) + + +@array_function_dispatch(_expandtabs_dispatcher) def expandtabs(a, tabsize=8): """ Return a copy of each string element where all tab characters are @@ -610,7 +671,7 @@ def expandtabs(a, tabsize=8): out : ndarray Output array of str or unicode, depending on input type - See also + See Also -------- str.expandtabs @@ -619,6 +680,7 @@ def expandtabs(a, tabsize=8): _vec_string(a, object_, 'expandtabs', (tabsize,))) +@array_function_dispatch(_count_dispatcher) def find(a, sub, start=0, end=None): """ For each element, return the lowest index in the string where @@ -645,15 +707,16 @@ def find(a, sub, start=0, end=None): out : ndarray or int Output array of ints. Returns -1 if `sub` is not found. - See also + See Also -------- str.find """ return _vec_string( - a, integer, 'find', [sub, start] + _clean_args(end)) + a, int_, 'find', [sub, start] + _clean_args(end)) +@array_function_dispatch(_count_dispatcher) def index(a, sub, start=0, end=None): """ Like `find`, but raises `ValueError` when the substring is not found. @@ -673,14 +736,16 @@ def index(a, sub, start=0, end=None): out : ndarray Output array of ints. Returns -1 if `sub` is not found. - See also + See Also -------- find, str.find """ return _vec_string( - a, integer, 'index', [sub, start] + _clean_args(end)) + a, int_, 'index', [sub, start] + _clean_args(end)) + +@array_function_dispatch(_unary_op_dispatcher) def isalnum(a): """ Returns true for each element if all characters in the string are @@ -699,12 +764,14 @@ def isalnum(a): out : ndarray Output array of str or unicode, depending on input type - See also + See Also -------- str.isalnum """ return _vec_string(a, bool_, 'isalnum') + +@array_function_dispatch(_unary_op_dispatcher) def isalpha(a): """ Returns true for each element if all characters in the string are @@ -723,12 +790,14 @@ def isalpha(a): out : ndarray Output array of bools - See also + See Also -------- str.isalpha """ return _vec_string(a, bool_, 'isalpha') + +@array_function_dispatch(_unary_op_dispatcher) def isdigit(a): """ Returns true for each element if all characters in the string are @@ -747,12 +816,14 @@ def isdigit(a): out : ndarray Output array of bools - See also + See Also -------- str.isdigit """ return _vec_string(a, bool_, 'isdigit') + +@array_function_dispatch(_unary_op_dispatcher) def islower(a): """ Returns true for each element if all cased characters in the @@ -772,12 +843,14 @@ def islower(a): out : ndarray Output array of bools - See also + See Also -------- str.islower """ return _vec_string(a, bool_, 'islower') + +@array_function_dispatch(_unary_op_dispatcher) def isspace(a): """ Returns true for each element if there are only whitespace @@ -797,12 +870,14 @@ def isspace(a): out : ndarray Output array of bools - See also + See Also -------- str.isspace """ return _vec_string(a, bool_, 'isspace') + +@array_function_dispatch(_unary_op_dispatcher) def istitle(a): """ Returns true for each element if the element is a titlecased @@ -821,12 +896,14 @@ def istitle(a): out : ndarray Output array of bools - See also + See Also -------- str.istitle """ return _vec_string(a, bool_, 'istitle') + +@array_function_dispatch(_unary_op_dispatcher) def isupper(a): """ Returns true for each element if all cased characters in the @@ -846,12 +923,18 @@ def isupper(a): out : ndarray Output array of bools - See also + See Also -------- str.isupper """ return _vec_string(a, bool_, 'isupper') + +def _join_dispatcher(sep, seq): + return (sep, seq) + + +@array_function_dispatch(_join_dispatcher) def join(sep, seq): """ Return a string which is the concatenation of the strings in the @@ -869,7 +952,7 @@ def join(sep, seq): out : ndarray Output array of str or unicode, depending on input types - See also + See Also -------- str.join """ @@ -877,6 +960,12 @@ def join(sep, seq): _vec_string(sep, object_, 'join', (seq,))) + +def _just_dispatcher(a, width, fillchar=None): + return (a,) + + +@array_function_dispatch(_just_dispatcher) def ljust(a, width, fillchar=' '): """ Return an array with the elements of `a` left-justified in a @@ -898,20 +987,21 @@ def ljust(a, width, fillchar=' '): out : ndarray Output array of str or unicode, depending on input type - See also + See Also -------- str.ljust """ a_arr = numpy.asarray(a) width_arr = numpy.asarray(width) - size = long(numpy.max(width_arr.flat)) + size = int(numpy.max(width_arr.flat)) if numpy.issubdtype(a_arr.dtype, numpy.string_): fillchar = asbytes(fillchar) return _vec_string( a_arr, (a_arr.dtype.type, size), 'ljust', (width_arr, fillchar)) +@array_function_dispatch(_unary_op_dispatcher) def lower(a): """ Return an array with the elements converted to lowercase. @@ -930,24 +1020,27 @@ def lower(a): out : ndarray, {str, unicode} Output array of str or unicode, depending on input type - See also + See Also -------- str.lower Examples -------- >>> c = np.array(['A1B C', '1BCA', 'BCA1']); c - array(['A1B C', '1BCA', 'BCA1'], - dtype='|S5') + array(['A1B C', '1BCA', 'BCA1'], dtype='<U5') >>> np.char.lower(c) - array(['a1b c', '1bca', 'bca1'], - dtype='|S5') + array(['a1b c', '1bca', 'bca1'], dtype='<U5') """ a_arr = numpy.asarray(a) return _vec_string(a_arr, a_arr.dtype, 'lower') +def _strip_dispatcher(a, chars=None): + return (a,) + + +@array_function_dispatch(_strip_dispatcher) def lstrip(a, chars=None): """ For each element in `a`, return a copy with the leading characters @@ -972,7 +1065,7 @@ def lstrip(a, chars=None): out : ndarray, {str, unicode} Output array of str or unicode, depending on input type - See also + See Also -------- str.lstrip @@ -980,23 +1073,20 @@ def lstrip(a, chars=None): -------- >>> c = np.array(['aAaAaA', ' aA ', 'abBABba']) >>> c - array(['aAaAaA', ' aA ', 'abBABba'], - dtype='|S7') + array(['aAaAaA', ' aA ', 'abBABba'], dtype='<U7') The 'a' variable is unstripped from c[1] because whitespace leading. >>> np.char.lstrip(c, 'a') - array(['AaAaA', ' aA ', 'bBABba'], - dtype='|S7') + array(['AaAaA', ' aA ', 'bBABba'], dtype='<U7') >>> np.char.lstrip(c, 'A') # leaves c unchanged - array(['aAaAaA', ' aA ', 'abBABba'], - dtype='|S7') + array(['aAaAaA', ' aA ', 'abBABba'], dtype='<U7') >>> (np.char.lstrip(c, ' ') == np.char.lstrip(c, '')).all() - ... # XXX: is this a regression? this line now returns False + ... # XXX: is this a regression? This used to return True ... # np.char.lstrip(c,'') does not modify c at all. - True + False >>> (np.char.lstrip(c, ' ') == np.char.lstrip(c, None)).all() True @@ -1005,6 +1095,11 @@ def lstrip(a, chars=None): return _vec_string(a_arr, a_arr.dtype, 'lstrip', (chars,)) +def _partition_dispatcher(a, sep): + return (a,) + + +@array_function_dispatch(_partition_dispatcher) def partition(a, sep): """ Partition each element in `a` around `sep`. @@ -1031,7 +1126,7 @@ def partition(a, sep): The output array will have an extra dimension with 3 elements per input element. - See also + See Also -------- str.partition @@ -1040,6 +1135,11 @@ def partition(a, sep): _vec_string(a, object_, 'partition', (sep,))) +def _replace_dispatcher(a, old, new, count=None): + return (a,) + + +@array_function_dispatch(_replace_dispatcher) def replace(a, old, new, count=None): """ For each element in `a`, return a copy of the string with all @@ -1062,7 +1162,7 @@ def replace(a, old, new, count=None): out : ndarray Output array of str or unicode, depending on input type - See also + See Also -------- str.replace @@ -1072,6 +1172,7 @@ def replace(a, old, new, count=None): a, object_, 'replace', [old, new] + _clean_args(count))) +@array_function_dispatch(_count_dispatcher) def rfind(a, sub, start=0, end=None): """ For each element in `a`, return the highest index in the string @@ -1095,15 +1196,16 @@ def rfind(a, sub, start=0, end=None): out : ndarray Output array of ints. Return -1 on failure. - See also + See Also -------- str.rfind """ return _vec_string( - a, integer, 'rfind', [sub, start] + _clean_args(end)) + a, int_, 'rfind', [sub, start] + _clean_args(end)) +@array_function_dispatch(_count_dispatcher) def rindex(a, sub, start=0, end=None): """ Like `rfind`, but raises `ValueError` when the substring `sub` is @@ -1124,15 +1226,16 @@ def rindex(a, sub, start=0, end=None): out : ndarray Output array of ints. - See also + See Also -------- rfind, str.rindex """ return _vec_string( - a, integer, 'rindex', [sub, start] + _clean_args(end)) + a, int_, 'rindex', [sub, start] + _clean_args(end)) +@array_function_dispatch(_just_dispatcher) def rjust(a, width, fillchar=' '): """ Return an array with the elements of `a` right-justified in a @@ -1154,20 +1257,21 @@ def rjust(a, width, fillchar=' '): out : ndarray Output array of str or unicode, depending on input type - See also + See Also -------- str.rjust """ a_arr = numpy.asarray(a) width_arr = numpy.asarray(width) - size = long(numpy.max(width_arr.flat)) + size = int(numpy.max(width_arr.flat)) if numpy.issubdtype(a_arr.dtype, numpy.string_): fillchar = asbytes(fillchar) return _vec_string( a_arr, (a_arr.dtype.type, size), 'rjust', (width_arr, fillchar)) +@array_function_dispatch(_partition_dispatcher) def rpartition(a, sep): """ Partition (split) each element around the right-most separator. @@ -1194,7 +1298,7 @@ def rpartition(a, sep): type. The output array will have an extra dimension with 3 elements per input element. - See also + See Also -------- str.rpartition @@ -1203,6 +1307,11 @@ def rpartition(a, sep): _vec_string(a, object_, 'rpartition', (sep,))) +def _split_dispatcher(a, sep=None, maxsplit=None): + return (a,) + + +@array_function_dispatch(_split_dispatcher) def rsplit(a, sep=None, maxsplit=None): """ For each element in `a`, return a list of the words in the @@ -1218,7 +1327,7 @@ def rsplit(a, sep=None, maxsplit=None): a : array_like of str or unicode sep : str or unicode, optional - If `sep` is not specified or `None`, any whitespace string + If `sep` is not specified or None, any whitespace string is a separator. maxsplit : int, optional If `maxsplit` is given, at most `maxsplit` splits are done, @@ -1229,7 +1338,7 @@ def rsplit(a, sep=None, maxsplit=None): out : ndarray Array of list objects - See also + See Also -------- str.rsplit, split @@ -1240,6 +1349,11 @@ def rsplit(a, sep=None, maxsplit=None): a, object_, 'rsplit', [sep] + _clean_args(maxsplit)) +def _strip_dispatcher(a, chars=None): + return (a,) + + +@array_function_dispatch(_strip_dispatcher) def rstrip(a, chars=None): """ For each element in `a`, return a copy with the trailing @@ -1263,7 +1377,7 @@ def rstrip(a, chars=None): out : ndarray Output array of str or unicode, depending on input type - See also + See Also -------- str.rstrip @@ -1272,10 +1386,10 @@ def rstrip(a, chars=None): >>> c = np.array(['aAaAaA', 'abBABba'], dtype='S7'); c array(['aAaAaA', 'abBABba'], dtype='|S7') - >>> np.char.rstrip(c, 'a') + >>> np.char.rstrip(c, b'a') array(['aAaAaA', 'abBABb'], dtype='|S7') - >>> np.char.rstrip(c, 'A') + >>> np.char.rstrip(c, b'A') array(['aAaAa', 'abBABba'], dtype='|S7') @@ -1284,6 +1398,7 @@ def rstrip(a, chars=None): return _vec_string(a_arr, a_arr.dtype, 'rstrip', (chars,)) +@array_function_dispatch(_split_dispatcher) def split(a, sep=None, maxsplit=None): """ For each element in `a`, return a list of the words in the @@ -1296,7 +1411,7 @@ def split(a, sep=None, maxsplit=None): a : array_like of str or unicode sep : str or unicode, optional - If `sep` is not specified or `None`, any whitespace string is a + If `sep` is not specified or None, any whitespace string is a separator. maxsplit : int, optional @@ -1307,7 +1422,7 @@ def split(a, sep=None, maxsplit=None): out : ndarray Array of list objects - See also + See Also -------- str.split, rsplit @@ -1318,6 +1433,11 @@ def split(a, sep=None, maxsplit=None): a, object_, 'split', [sep] + _clean_args(maxsplit)) +def _splitlines_dispatcher(a, keepends=None): + return (a,) + + +@array_function_dispatch(_splitlines_dispatcher) def splitlines(a, keepends=None): """ For each element in `a`, return a list of the lines in the @@ -1338,7 +1458,7 @@ def splitlines(a, keepends=None): out : ndarray Array of list objects - See also + See Also -------- str.splitlines @@ -1347,6 +1467,11 @@ def splitlines(a, keepends=None): a, object_, 'splitlines', _clean_args(keepends)) +def _startswith_dispatcher(a, prefix, start=None, end=None): + return (a,) + + +@array_function_dispatch(_startswith_dispatcher) def startswith(a, prefix, start=0, end=None): """ Returns a boolean array which is `True` where the string element @@ -1369,7 +1494,7 @@ def startswith(a, prefix, start=0, end=None): out : ndarray Array of booleans - See also + See Also -------- str.startswith @@ -1378,6 +1503,7 @@ def startswith(a, prefix, start=0, end=None): a, bool_, 'startswith', [prefix, start] + _clean_args(end)) +@array_function_dispatch(_strip_dispatcher) def strip(a, chars=None): """ For each element in `a`, return a copy with the leading and @@ -1401,7 +1527,7 @@ def strip(a, chars=None): out : ndarray Output array of str or unicode, depending on input type - See also + See Also -------- str.strip @@ -1409,23 +1535,20 @@ def strip(a, chars=None): -------- >>> c = np.array(['aAaAaA', ' aA ', 'abBABba']) >>> c - array(['aAaAaA', ' aA ', 'abBABba'], - dtype='|S7') + array(['aAaAaA', ' aA ', 'abBABba'], dtype='<U7') >>> np.char.strip(c) - array(['aAaAaA', 'aA', 'abBABba'], - dtype='|S7') + array(['aAaAaA', 'aA', 'abBABba'], dtype='<U7') >>> np.char.strip(c, 'a') # 'a' unstripped from c[1] because whitespace leads - array(['AaAaA', ' aA ', 'bBABb'], - dtype='|S7') + array(['AaAaA', ' aA ', 'bBABb'], dtype='<U7') >>> np.char.strip(c, 'A') # 'A' unstripped from c[1] because (unprinted) ws trails - array(['aAaAa', ' aA ', 'abBABba'], - dtype='|S7') + array(['aAaAa', ' aA ', 'abBABba'], dtype='<U7') """ a_arr = numpy.asarray(a) return _vec_string(a_arr, a_arr.dtype, 'strip', _clean_args(chars)) +@array_function_dispatch(_unary_op_dispatcher) def swapcase(a): """ Return element-wise a copy of the string with @@ -1445,7 +1568,7 @@ def swapcase(a): out : ndarray, {str, unicode} Output array of str or unicode, depending on input type - See also + See Also -------- str.swapcase @@ -1463,6 +1586,7 @@ def swapcase(a): return _vec_string(a_arr, a_arr.dtype, 'swapcase') +@array_function_dispatch(_unary_op_dispatcher) def title(a): """ Return element-wise title cased version of string or unicode. @@ -1484,7 +1608,7 @@ def title(a): out : ndarray Output array of str or unicode, depending on input type - See also + See Also -------- str.title @@ -1502,6 +1626,11 @@ def title(a): return _vec_string(a_arr, a_arr.dtype, 'title') +def _translate_dispatcher(a, table, deletechars=None): + return (a,) + + +@array_function_dispatch(_translate_dispatcher) def translate(a, table, deletechars=None): """ For each element in `a`, return a copy of the string where all @@ -1524,7 +1653,7 @@ def translate(a, table, deletechars=None): out : ndarray Output array of str or unicode, depending on input type - See also + See Also -------- str.translate @@ -1538,6 +1667,7 @@ def translate(a, table, deletechars=None): a_arr, a_arr.dtype, 'translate', [table] + _clean_args(deletechars)) +@array_function_dispatch(_unary_op_dispatcher) def upper(a): """ Return an array with the elements converted to uppercase. @@ -1556,24 +1686,27 @@ def upper(a): out : ndarray, {str, unicode} Output array of str or unicode, depending on input type - See also + See Also -------- str.upper Examples -------- >>> c = np.array(['a1b c', '1bca', 'bca1']); c - array(['a1b c', '1bca', 'bca1'], - dtype='|S5') + array(['a1b c', '1bca', 'bca1'], dtype='<U5') >>> np.char.upper(c) - array(['A1B C', '1BCA', 'BCA1'], - dtype='|S5') + array(['A1B C', '1BCA', 'BCA1'], dtype='<U5') """ a_arr = numpy.asarray(a) return _vec_string(a_arr, a_arr.dtype, 'upper') +def _zfill_dispatcher(a, width): + return (a,) + + +@array_function_dispatch(_zfill_dispatcher) def zfill(a, width): """ Return the numeric string left-filled with zeros @@ -1592,18 +1725,19 @@ def zfill(a, width): out : ndarray, {str, unicode} Output array of str or unicode, depending on input type - See also + See Also -------- str.zfill """ a_arr = numpy.asarray(a) width_arr = numpy.asarray(width) - size = long(numpy.max(width_arr.flat)) + size = int(numpy.max(width_arr.flat)) return _vec_string( a_arr, (a_arr.dtype.type, size), 'zfill', (width_arr,)) +@array_function_dispatch(_unary_op_dispatcher) def isnumeric(a): """ For each element, return True if there are only numeric @@ -1625,7 +1759,7 @@ def isnumeric(a): out : ndarray, bool Array of booleans of same shape as `a`. - See also + See Also -------- unicode.isnumeric @@ -1635,6 +1769,7 @@ def isnumeric(a): return _vec_string(a, bool_, 'isnumeric') +@array_function_dispatch(_unary_op_dispatcher) def isdecimal(a): """ For each element, return True if there are only decimal @@ -1643,7 +1778,7 @@ def isdecimal(a): Calls `unicode.isdecimal` element-wise. Decimal characters include digit characters, and all characters - that that can be used to form decimal-radix numbers, + that can be used to form decimal-radix numbers, e.g. ``U+0660, ARABIC-INDIC DIGIT ZERO``. Parameters @@ -1656,7 +1791,7 @@ def isdecimal(a): out : ndarray, bool Array of booleans identical in shape to `a`. - See also + See Also -------- unicode.isdecimal @@ -1666,6 +1801,7 @@ def isdecimal(a): return _vec_string(a, bool_, 'isdecimal') +@set_module('numpy') class chararray(ndarray): """ chararray(shape, itemsize=1, unicode=False, buffer=None, offset=0, @@ -1698,7 +1834,7 @@ class adds the following functionality: This constructor creates the array, using `buffer` (with `offset` and `strides`) if it is not ``None``. If `buffer` is ``None``, then constructs a new array with `strides` in "C order", unless both - ``len(shape) >= 2`` and ``order='Fortran'``, in which case `strides` + ``len(shape) >= 2`` and ``order='F'``, in which case `strides` is in "Fortran order". Methods @@ -1775,7 +1911,7 @@ class adds the following functionality: unicode : bool, optional Are the array elements of type unicode (True) or string (False). Default is False. - buffer : int, optional + buffer : object exposing the buffer interface or str, optional Memory address of the start of the array data. Default is None, in which case a new array is created. offset : int, optional @@ -1794,18 +1930,16 @@ class adds the following functionality: >>> charar = np.chararray((3, 3)) >>> charar[:] = 'a' >>> charar - chararray([['a', 'a', 'a'], - ['a', 'a', 'a'], - ['a', 'a', 'a']], - dtype='|S1') + chararray([[b'a', b'a', b'a'], + [b'a', b'a', b'a'], + [b'a', b'a', b'a']], dtype='|S1') >>> charar = np.chararray(charar.shape, itemsize=5) >>> charar[:] = 'abc' >>> charar - chararray([['abc', 'abc', 'abc'], - ['abc', 'abc', 'abc'], - ['abc', 'abc', 'abc']], - dtype='|S5') + chararray([[b'abc', b'abc', b'abc'], + [b'abc', b'abc', b'abc'], + [b'abc', b'abc', b'abc']], dtype='|S5') """ def __new__(subtype, shape, itemsize=1, unicode=False, buffer=None, @@ -1817,13 +1951,13 @@ def __new__(subtype, shape, itemsize=1, unicode=False, buffer=None, else: dtype = string_ - # force itemsize to be a Python long, since using NumPy integer + # force itemsize to be a Python int, since using NumPy integer # types results in itemsize.itemsize being used as the size of # strings in the new array. - itemsize = long(itemsize) + itemsize = int(itemsize) - if sys.version_info[0] >= 3 and isinstance(buffer, _unicode): - # On Py3, unicode objects do not have the buffer interface + if isinstance(buffer, str): + # unicode objects do not have the buffer interface filler = buffer buffer = None else: @@ -1853,7 +1987,7 @@ def __getitem__(self, obj): if isinstance(val, character): temp = val.rstrip() - if _len(temp) == 0: + if len(temp) == 0: val = '' else: val = temp @@ -1869,7 +2003,7 @@ def __eq__(self, other): """ Return (self == other) element-wise. - See also + See Also -------- equal """ @@ -1879,7 +2013,7 @@ def __ne__(self, other): """ Return (self != other) element-wise. - See also + See Also -------- not_equal """ @@ -1889,7 +2023,7 @@ def __ge__(self, other): """ Return (self >= other) element-wise. - See also + See Also -------- greater_equal """ @@ -1899,7 +2033,7 @@ def __le__(self, other): """ Return (self <= other) element-wise. - See also + See Also -------- less_equal """ @@ -1909,7 +2043,7 @@ def __gt__(self, other): """ Return (self > other) element-wise. - See also + See Also -------- greater """ @@ -1919,7 +2053,7 @@ def __lt__(self, other): """ Return (self < other) element-wise. - See also + See Also -------- less """ @@ -1930,7 +2064,7 @@ def __add__(self, other): Return (self + other), that is string concatenation, element-wise for a pair of array_likes of str or unicode. - See also + See Also -------- add """ @@ -1941,7 +2075,7 @@ def __radd__(self, other): Return (other + self), that is string concatenation, element-wise for a pair of array_likes of `string_` or `unicode_`. - See also + See Also -------- add """ @@ -1952,7 +2086,7 @@ def __mul__(self, i): Return (self * i), that is string multiple concatenation, element-wise. - See also + See Also -------- multiply """ @@ -1963,7 +2097,7 @@ def __rmul__(self, i): Return (self * i), that is string multiple concatenation, element-wise. - See also + See Also -------- multiply """ @@ -1972,10 +2106,10 @@ def __rmul__(self, i): def __mod__(self, i): """ Return (self % i), that is pre-Python 2.6 string formatting - (iterpolation), element-wise for a pair of array_likes of `string_` + (interpolation), element-wise for a pair of array_likes of `string_` or `unicode_`. - See also + See Also -------- mod """ @@ -1984,7 +2118,7 @@ def __mod__(self, i): def __rmod__(self, other): return NotImplemented - def argsort(self, axis=-1, kind='quicksort', order=None): + def argsort(self, axis=-1, kind=None, order=None): """ Return the indices that sort the array lexicographically. @@ -2010,7 +2144,7 @@ def capitalize(self): Return a copy of `self` with only the first character of each element capitalized. - See also + See Also -------- char.capitalize @@ -2022,7 +2156,7 @@ def center(self, width, fillchar=' '): Return a copy of `self` with its elements centered in a string of length `width`. - See also + See Also -------- center """ @@ -2033,7 +2167,7 @@ def count(self, sub, start=0, end=None): Returns an array with the number of non-overlapping occurrences of substring `sub` in the range [`start`, `end`]. - See also + See Also -------- char.count @@ -2044,7 +2178,7 @@ def decode(self, encoding=None, errors=None): """ Calls `str.decode` element-wise. - See also + See Also -------- char.decode @@ -2055,7 +2189,7 @@ def encode(self, encoding=None, errors=None): """ Calls `str.encode` element-wise. - See also + See Also -------- char.encode @@ -2067,7 +2201,7 @@ def endswith(self, suffix, start=0, end=None): Returns a boolean array which is `True` where the string element in `self` ends with `suffix`, otherwise `False`. - See also + See Also -------- char.endswith @@ -2079,7 +2213,7 @@ def expandtabs(self, tabsize=8): Return a copy of each string element where all tab characters are replaced by one or more spaces. - See also + See Also -------- char.expandtabs @@ -2091,7 +2225,7 @@ def find(self, sub, start=0, end=None): For each element, return the lowest index in the string where substring `sub` is found. - See also + See Also -------- char.find @@ -2102,7 +2236,7 @@ def index(self, sub, start=0, end=None): """ Like `find`, but raises `ValueError` when the substring is not found. - See also + See Also -------- char.index @@ -2115,7 +2249,7 @@ def isalnum(self): are alphanumeric and there is at least one character, false otherwise. - See also + See Also -------- char.isalnum @@ -2128,7 +2262,7 @@ def isalpha(self): are alphabetic and there is at least one character, false otherwise. - See also + See Also -------- char.isalpha @@ -2140,7 +2274,7 @@ def isdigit(self): Returns true for each element if all characters in the string are digits and there is at least one character, false otherwise. - See also + See Also -------- char.isdigit @@ -2153,7 +2287,7 @@ def islower(self): string are lowercase and there is at least one cased character, false otherwise. - See also + See Also -------- char.islower @@ -2166,7 +2300,7 @@ def isspace(self): characters in the string and there is at least one character, false otherwise. - See also + See Also -------- char.isspace @@ -2178,7 +2312,7 @@ def istitle(self): Returns true for each element if the element is a titlecased string and there is at least one character, false otherwise. - See also + See Also -------- char.istitle @@ -2191,7 +2325,7 @@ def isupper(self): string are uppercase and there is at least one character, false otherwise. - See also + See Also -------- char.isupper @@ -2203,7 +2337,7 @@ def join(self, seq): Return a string which is the concatenation of the strings in the sequence `seq`. - See also + See Also -------- char.join @@ -2215,7 +2349,7 @@ def ljust(self, width, fillchar=' '): Return an array with the elements of `self` left-justified in a string of length `width`. - See also + See Also -------- char.ljust @@ -2227,7 +2361,7 @@ def lower(self): Return an array with the elements of `self` converted to lowercase. - See also + See Also -------- char.lower @@ -2239,7 +2373,7 @@ def lstrip(self, chars=None): For each element in `self`, return a copy with the leading characters removed. - See also + See Also -------- char.lstrip @@ -2250,7 +2384,7 @@ def partition(self, sep): """ Partition each element in `self` around `sep`. - See also + See Also -------- partition """ @@ -2261,7 +2395,7 @@ def replace(self, old, new, count=None): For each element in `self`, return a copy of the string with all occurrences of substring `old` replaced by `new`. - See also + See Also -------- char.replace @@ -2274,7 +2408,7 @@ def rfind(self, sub, start=0, end=None): where substring `sub` is found, such that `sub` is contained within [`start`, `end`]. - See also + See Also -------- char.rfind @@ -2286,7 +2420,7 @@ def rindex(self, sub, start=0, end=None): Like `rfind`, but raises `ValueError` when the substring `sub` is not found. - See also + See Also -------- char.rindex @@ -2298,7 +2432,7 @@ def rjust(self, width, fillchar=' '): Return an array with the elements of `self` right-justified in a string of length `width`. - See also + See Also -------- char.rjust @@ -2309,7 +2443,7 @@ def rpartition(self, sep): """ Partition each element in `self` around `sep`. - See also + See Also -------- rpartition """ @@ -2320,7 +2454,7 @@ def rsplit(self, sep=None, maxsplit=None): For each element in `self`, return a list of the words in the string, using `sep` as the delimiter string. - See also + See Also -------- char.rsplit @@ -2332,7 +2466,7 @@ def rstrip(self, chars=None): For each element in `self`, return a copy with the trailing characters removed. - See also + See Also -------- char.rstrip @@ -2344,7 +2478,7 @@ def split(self, sep=None, maxsplit=None): For each element in `self`, return a list of the words in the string, using `sep` as the delimiter string. - See also + See Also -------- char.split @@ -2356,7 +2490,7 @@ def splitlines(self, keepends=None): For each element in `self`, return a list of the lines in the element, breaking at line boundaries. - See also + See Also -------- char.splitlines @@ -2368,7 +2502,7 @@ def startswith(self, prefix, start=0, end=None): Returns a boolean array which is `True` where the string element in `self` starts with `prefix`, otherwise `False`. - See also + See Also -------- char.startswith @@ -2380,7 +2514,7 @@ def strip(self, chars=None): For each element in `self`, return a copy with the leading and trailing characters removed. - See also + See Also -------- char.strip @@ -2392,7 +2526,7 @@ def swapcase(self): For each element in `self`, return a copy of the string with uppercase characters converted to lowercase and vice versa. - See also + See Also -------- char.swapcase @@ -2405,7 +2539,7 @@ def title(self): string: words start with uppercase characters, all remaining cased characters are lowercase. - See also + See Also -------- char.title @@ -2419,7 +2553,7 @@ def translate(self, table, deletechars=None): `deletechars` are removed, and the remaining characters have been mapped through the given translation table. - See also + See Also -------- char.translate @@ -2431,7 +2565,7 @@ def upper(self): Return an array with the elements of `self` converted to uppercase. - See also + See Also -------- char.upper @@ -2443,7 +2577,7 @@ def zfill(self, width): Return the numeric string left-filled with zeros in a string of length `width`. - See also + See Also -------- char.zfill @@ -2455,7 +2589,7 @@ def isnumeric(self): For each element in `self`, return True if there are only numeric characters in the element. - See also + See Also -------- char.isnumeric @@ -2467,7 +2601,7 @@ def isdecimal(self): For each element in `self`, return True if there are only decimal characters in the element. - See also + See Also -------- char.isdecimal @@ -2475,6 +2609,7 @@ def isdecimal(self): return isdecimal(self) +@set_module("numpy.char") def array(obj, itemsize=None, copy=True, unicode=None, order=None): """ Create a `chararray`. @@ -2519,7 +2654,7 @@ class adds the following functionality: unicode : bool, optional When true, the resulting `chararray` can contain Unicode characters, when false only 8-bit characters. If unicode is - `None` and `obj` is one of the following: + None and `obj` is one of the following: - a `chararray`, - an ndarray of type `str` or `unicode` @@ -2537,35 +2672,16 @@ class adds the following functionality: be in any order (either C-, Fortran-contiguous, or even discontiguous). """ - if isinstance(obj, (_bytes, _unicode)): + if isinstance(obj, (bytes, str)): if unicode is None: - if isinstance(obj, _unicode): + if isinstance(obj, str): unicode = True else: unicode = False if itemsize is None: - itemsize = _len(obj) - shape = _len(obj) // itemsize - - if unicode: - if sys.maxunicode == 0xffff: - # On a narrow Python build, the buffer for Unicode - # strings is UCS2, which doesn't match the buffer for - # NumPy Unicode types, which is ALWAYS UCS4. - # Therefore, we need to convert the buffer. On Python - # 2.6 and later, we can use the utf_32 codec. Earlier - # versions don't have that codec, so we convert to a - # numerical array that matches the input buffer, and - # then use NumPy to convert it to UCS4. All of this - # should happen in native endianness. - obj = obj.encode('utf_32') - else: - obj = _unicode(obj) - else: - # Let the default Unicode -> string encoding (if any) take - # precedence. - obj = _bytes(obj) + itemsize = len(obj) + shape = len(obj) // itemsize return chararray(shape, itemsize=itemsize, unicode=unicode, buffer=obj, order=order) @@ -2604,7 +2720,7 @@ class adds the following functionality: (itemsize != obj.itemsize) or (not unicode and isinstance(obj, unicode_)) or (unicode and isinstance(obj, string_))): - obj = obj.astype((dtype, long(itemsize))) + obj = obj.astype((dtype, int(itemsize))) return obj if isinstance(obj, ndarray) and issubclass(obj.dtype.type, object): @@ -2627,6 +2743,7 @@ class adds the following functionality: return val.view(chararray) +@set_module("numpy.char") def asarray(obj, itemsize=None, unicode=None, order=None): """ Convert the input to a `chararray`, copying the data only if @@ -2659,7 +2776,7 @@ class adds the following functionality: unicode : bool, optional When true, the resulting `chararray` can contain Unicode characters, when false only 8-bit characters. If unicode is - `None` and `obj` is one of the following: + None and `obj` is one of the following: - a `chararray`, - an ndarray of type `str` or 'unicode` diff --git a/numpy/core/defchararray.pyi b/numpy/core/defchararray.pyi new file mode 100644 index 000000000000..28d247b056e4 --- /dev/null +++ b/numpy/core/defchararray.pyi @@ -0,0 +1,422 @@ +from typing import ( + Literal as L, + overload, + TypeVar, + Any, + List, +) + +from numpy import ( + chararray as chararray, + dtype, + str_, + bytes_, + int_, + bool_, + object_, + _OrderKACF, +) + +from numpy.typing import ( + NDArray, + _ArrayLikeStr_co as U_co, + _ArrayLikeBytes_co as S_co, + _ArrayLikeInt_co as i_co, + _ArrayLikeBool_co as b_co, +) + +from numpy.core.multiarray import compare_chararrays as compare_chararrays + +_SCT = TypeVar("_SCT", str_, bytes_) +_CharArray = chararray[Any, dtype[_SCT]] + +__all__: List[str] + +# Comparison +@overload +def equal(x1: U_co, x2: U_co) -> NDArray[bool_]: ... +@overload +def equal(x1: S_co, x2: S_co) -> NDArray[bool_]: ... + +@overload +def not_equal(x1: U_co, x2: U_co) -> NDArray[bool_]: ... +@overload +def not_equal(x1: S_co, x2: S_co) -> NDArray[bool_]: ... + +@overload +def greater_equal(x1: U_co, x2: U_co) -> NDArray[bool_]: ... +@overload +def greater_equal(x1: S_co, x2: S_co) -> NDArray[bool_]: ... + +@overload +def less_equal(x1: U_co, x2: U_co) -> NDArray[bool_]: ... +@overload +def less_equal(x1: S_co, x2: S_co) -> NDArray[bool_]: ... + +@overload +def greater(x1: U_co, x2: U_co) -> NDArray[bool_]: ... +@overload +def greater(x1: S_co, x2: S_co) -> NDArray[bool_]: ... + +@overload +def less(x1: U_co, x2: U_co) -> NDArray[bool_]: ... +@overload +def less(x1: S_co, x2: S_co) -> NDArray[bool_]: ... + +# String operations +@overload +def add(x1: U_co, x2: U_co) -> NDArray[str_]: ... +@overload +def add(x1: S_co, x2: S_co) -> NDArray[bytes_]: ... + +@overload +def multiply(a: U_co, i: i_co) -> NDArray[str_]: ... +@overload +def multiply(a: S_co, i: i_co) -> NDArray[bytes_]: ... + +@overload +def mod(a: U_co, value: Any) -> NDArray[str_]: ... +@overload +def mod(a: S_co, value: Any) -> NDArray[bytes_]: ... + +@overload +def capitalize(a: U_co) -> NDArray[str_]: ... +@overload +def capitalize(a: S_co) -> NDArray[bytes_]: ... + +@overload +def center(a: U_co, width: i_co, fillchar: U_co = ...) -> NDArray[str_]: ... +@overload +def center(a: S_co, width: i_co, fillchar: S_co = ...) -> NDArray[bytes_]: ... + +def decode( + a: S_co, + encoding: None | str = ..., + errors: None | str = ..., +) -> NDArray[str_]: ... + +def encode( + a: U_co, + encoding: None | str = ..., + errors: None | str = ..., +) -> NDArray[bytes_]: ... + +@overload +def expandtabs(a: U_co, tabsize: i_co = ...) -> NDArray[str_]: ... +@overload +def expandtabs(a: S_co, tabsize: i_co = ...) -> NDArray[bytes_]: ... + +@overload +def join(sep: U_co, seq: U_co) -> NDArray[str_]: ... +@overload +def join(sep: S_co, seq: S_co) -> NDArray[bytes_]: ... + +@overload +def ljust(a: U_co, width: i_co, fillchar: U_co = ...) -> NDArray[str_]: ... +@overload +def ljust(a: S_co, width: i_co, fillchar: S_co = ...) -> NDArray[bytes_]: ... + +@overload +def lower(a: U_co) -> NDArray[str_]: ... +@overload +def lower(a: S_co) -> NDArray[bytes_]: ... + +@overload +def lstrip(a: U_co, chars: None | U_co = ...) -> NDArray[str_]: ... +@overload +def lstrip(a: S_co, chars: None | S_co = ...) -> NDArray[bytes_]: ... + +@overload +def partition(a: U_co, sep: U_co) -> NDArray[str_]: ... +@overload +def partition(a: S_co, sep: S_co) -> NDArray[bytes_]: ... + +@overload +def replace( + a: U_co, + old: U_co, + new: U_co, + count: None | i_co = ..., +) -> NDArray[str_]: ... +@overload +def replace( + a: S_co, + old: S_co, + new: S_co, + count: None | i_co = ..., +) -> NDArray[bytes_]: ... + +@overload +def rjust( + a: U_co, + width: i_co, + fillchar: U_co = ..., +) -> NDArray[str_]: ... +@overload +def rjust( + a: S_co, + width: i_co, + fillchar: S_co = ..., +) -> NDArray[bytes_]: ... + +@overload +def rpartition(a: U_co, sep: U_co) -> NDArray[str_]: ... +@overload +def rpartition(a: S_co, sep: S_co) -> NDArray[bytes_]: ... + +@overload +def rsplit( + a: U_co, + sep: None | U_co = ..., + maxsplit: None | i_co = ..., +) -> NDArray[object_]: ... +@overload +def rsplit( + a: S_co, + sep: None | S_co = ..., + maxsplit: None | i_co = ..., +) -> NDArray[object_]: ... + +@overload +def rstrip(a: U_co, chars: None | U_co = ...) -> NDArray[str_]: ... +@overload +def rstrip(a: S_co, chars: None | S_co = ...) -> NDArray[bytes_]: ... + +@overload +def split( + a: U_co, + sep: None | U_co = ..., + maxsplit: None | i_co = ..., +) -> NDArray[object_]: ... +@overload +def split( + a: S_co, + sep: None | S_co = ..., + maxsplit: None | i_co = ..., +) -> NDArray[object_]: ... + +@overload +def splitlines(a: U_co, keepends: None | b_co = ...) -> NDArray[object_]: ... +@overload +def splitlines(a: S_co, keepends: None | b_co = ...) -> NDArray[object_]: ... + +@overload +def strip(a: U_co, chars: None | U_co = ...) -> NDArray[str_]: ... +@overload +def strip(a: S_co, chars: None | S_co = ...) -> NDArray[bytes_]: ... + +@overload +def swapcase(a: U_co) -> NDArray[str_]: ... +@overload +def swapcase(a: S_co) -> NDArray[bytes_]: ... + +@overload +def title(a: U_co) -> NDArray[str_]: ... +@overload +def title(a: S_co) -> NDArray[bytes_]: ... + +@overload +def translate( + a: U_co, + table: U_co, + deletechars: None | U_co = ..., +) -> NDArray[str_]: ... +@overload +def translate( + a: S_co, + table: S_co, + deletechars: None | S_co = ..., +) -> NDArray[bytes_]: ... + +@overload +def upper(a: U_co) -> NDArray[str_]: ... +@overload +def upper(a: S_co) -> NDArray[bytes_]: ... + +@overload +def zfill(a: U_co, width: i_co) -> NDArray[str_]: ... +@overload +def zfill(a: S_co, width: i_co) -> NDArray[bytes_]: ... + +# String information +@overload +def count( + a: U_co, + sub: U_co, + start: i_co = ..., + end: None | i_co = ..., +) -> NDArray[int_]: ... +@overload +def count( + a: S_co, + sub: S_co, + start: i_co = ..., + end: None | i_co = ..., +) -> NDArray[int_]: ... + +@overload +def endswith( + a: U_co, + suffix: U_co, + start: i_co = ..., + end: None | i_co = ..., +) -> NDArray[bool_]: ... +@overload +def endswith( + a: S_co, + suffix: S_co, + start: i_co = ..., + end: None | i_co = ..., +) -> NDArray[bool_]: ... + +@overload +def find( + a: U_co, + sub: U_co, + start: i_co = ..., + end: None | i_co = ..., +) -> NDArray[int_]: ... +@overload +def find( + a: S_co, + sub: S_co, + start: i_co = ..., + end: None | i_co = ..., +) -> NDArray[int_]: ... + +@overload +def index( + a: U_co, + sub: U_co, + start: i_co = ..., + end: None | i_co = ..., +) -> NDArray[int_]: ... +@overload +def index( + a: S_co, + sub: S_co, + start: i_co = ..., + end: None | i_co = ..., +) -> NDArray[int_]: ... + +def isalpha(a: U_co | S_co) -> NDArray[bool_]: ... +def isalnum(a: U_co | S_co) -> NDArray[bool_]: ... +def isdecimal(a: U_co | S_co) -> NDArray[bool_]: ... +def isdigit(a: U_co | S_co) -> NDArray[bool_]: ... +def islower(a: U_co | S_co) -> NDArray[bool_]: ... +def isnumeric(a: U_co | S_co) -> NDArray[bool_]: ... +def isspace(a: U_co | S_co) -> NDArray[bool_]: ... +def istitle(a: U_co | S_co) -> NDArray[bool_]: ... +def isupper(a: U_co | S_co) -> NDArray[bool_]: ... + +@overload +def rfind( + a: U_co, + sub: U_co, + start: i_co = ..., + end: None | i_co = ..., +) -> NDArray[int_]: ... +@overload +def rfind( + a: S_co, + sub: S_co, + start: i_co = ..., + end: None | i_co = ..., +) -> NDArray[int_]: ... + +@overload +def rindex( + a: U_co, + sub: U_co, + start: i_co = ..., + end: None | i_co = ..., +) -> NDArray[int_]: ... +@overload +def rindex( + a: S_co, + sub: S_co, + start: i_co = ..., + end: None | i_co = ..., +) -> NDArray[int_]: ... + +@overload +def startswith( + a: U_co, + prefix: U_co, + start: i_co = ..., + end: None | i_co = ..., +) -> NDArray[bool_]: ... +@overload +def startswith( + a: S_co, + prefix: S_co, + start: i_co = ..., + end: None | i_co = ..., +) -> NDArray[bool_]: ... + +def str_len(A: U_co | S_co) -> NDArray[int_]: ... + +# Overload 1 and 2: str- or bytes-based array-likes +# overload 3: arbitrary object with unicode=False (-> bytes_) +# overload 4: arbitrary object with unicode=True (-> str_) +@overload +def array( + obj: U_co, + itemsize: None | int = ..., + copy: bool = ..., + unicode: L[False] = ..., + order: _OrderKACF = ..., +) -> _CharArray[str_]: ... +@overload +def array( + obj: S_co, + itemsize: None | int = ..., + copy: bool = ..., + unicode: L[False] = ..., + order: _OrderKACF = ..., +) -> _CharArray[bytes_]: ... +@overload +def array( + obj: object, + itemsize: None | int = ..., + copy: bool = ..., + unicode: L[False] = ..., + order: _OrderKACF = ..., +) -> _CharArray[bytes_]: ... +@overload +def array( + obj: object, + itemsize: None | int = ..., + copy: bool = ..., + unicode: L[True] = ..., + order: _OrderKACF = ..., +) -> _CharArray[str_]: ... + +@overload +def asarray( + obj: U_co, + itemsize: None | int = ..., + unicode: L[False] = ..., + order: _OrderKACF = ..., +) -> _CharArray[str_]: ... +@overload +def asarray( + obj: S_co, + itemsize: None | int = ..., + unicode: L[False] = ..., + order: _OrderKACF = ..., +) -> _CharArray[bytes_]: ... +@overload +def asarray( + obj: object, + itemsize: None | int = ..., + unicode: L[False] = ..., + order: _OrderKACF = ..., +) -> _CharArray[bytes_]: ... +@overload +def asarray( + obj: object, + itemsize: None | int = ..., + unicode: L[True] = ..., + order: _OrderKACF = ..., +) -> _CharArray[str_]: ... diff --git a/numpy/core/einsumfunc.py b/numpy/core/einsumfunc.py index a4c18d482e59..c78d3db23abc 100644 --- a/numpy/core/einsumfunc.py +++ b/numpy/core/einsumfunc.py @@ -2,11 +2,12 @@ Implementation of optimized einsum. """ -from __future__ import division, absolute_import, print_function +import itertools +import operator -from numpy.compat import basestring from numpy.core.multiarray import c_einsum -from numpy.core.numeric import asarray, asanyarray, result_type, tensordot, dot +from numpy.core.numeric import asanyarray, tensordot +from numpy.core.overrides import array_function_dispatch __all__ = ['einsum', 'einsum_path'] @@ -14,6 +15,44 @@ einsum_symbols_set = set(einsum_symbols) +def _flop_count(idx_contraction, inner, num_terms, size_dictionary): + """ + Computes the number of FLOPS in the contraction. + + Parameters + ---------- + idx_contraction : iterable + The indices involved in the contraction + inner : bool + Does this contraction require an inner product? + num_terms : int + The number of terms in a contraction + size_dictionary : dict + The size of each of the indices in idx_contraction + + Returns + ------- + flop_count : int + The total number of FLOPS required for the contraction. + + Examples + -------- + + >>> _flop_count('abc', False, 1, {'a': 2, 'b':3, 'c':5}) + 30 + + >>> _flop_count('abc', True, 2, {'a': 2, 'b':3, 'c':5}) + 60 + + """ + + overall_size = _compute_size_by_dict(idx_contraction, size_dictionary) + op_factor = max(1, num_terms - 1) + if inner: + op_factor += 1 + + return overall_size * op_factor + def _compute_size_by_dict(indices, idx_dict): """ Computes the product of the elements in indices based on the dictionary @@ -128,9 +167,9 @@ def _optimal_path(input_sets, output_set, idx_dict, memory_limit): Examples -------- >>> isets = [set('abd'), set('ac'), set('bdc')] - >>> oset = set('') + >>> oset = set() >>> idx_sizes = {'a': 1, 'b':2, 'c':3, 'd':4} - >>> _path__optimal_path(isets, oset, idx_sizes, 5000) + >>> _optimal_path(isets, oset, idx_sizes, 5000) [(0, 2), (0, 1)] """ @@ -139,14 +178,9 @@ def _optimal_path(input_sets, output_set, idx_dict, memory_limit): iter_results = [] # Compute all unique pairs - comb_iter = [] - for x in range(len(input_sets) - iteration): - for y in range(x + 1, len(input_sets) - iteration): - comb_iter.append((x, y)) - for curr in full_results: cost, positions, remaining = curr - for con in comb_iter: + for con in itertools.combinations(range(len(input_sets) - iteration), 2): # Find the contraction cont = _find_contraction(con, remaining, output_set) @@ -157,15 +191,10 @@ def _optimal_path(input_sets, output_set, idx_dict, memory_limit): if new_size > memory_limit: continue - # Find cost - new_cost = _compute_size_by_dict(idx_contract, idx_dict) - if idx_removed: - new_cost *= 2 - # Build (total_cost, positions, indices_remaining) - new_cost += cost + total_cost = cost + _flop_count(idx_contract, idx_removed, len(con), idx_dict) new_pos = positions + [con] - iter_results.append((new_cost, new_pos, new_input_sets)) + iter_results.append((total_cost, new_pos, new_input_sets)) # Update combinatorial list, if we did not find anything return best # path + remaining contractions @@ -183,6 +212,102 @@ def _optimal_path(input_sets, output_set, idx_dict, memory_limit): path = min(full_results, key=lambda x: x[0])[1] return path +def _parse_possible_contraction(positions, input_sets, output_set, idx_dict, memory_limit, path_cost, naive_cost): + """Compute the cost (removed size + flops) and resultant indices for + performing the contraction specified by ``positions``. + + Parameters + ---------- + positions : tuple of int + The locations of the proposed tensors to contract. + input_sets : list of sets + The indices found on each tensors. + output_set : set + The output indices of the expression. + idx_dict : dict + Mapping of each index to its size. + memory_limit : int + The total allowed size for an intermediary tensor. + path_cost : int + The contraction cost so far. + naive_cost : int + The cost of the unoptimized expression. + + Returns + ------- + cost : (int, int) + A tuple containing the size of any indices removed, and the flop cost. + positions : tuple of int + The locations of the proposed tensors to contract. + new_input_sets : list of sets + The resulting new list of indices if this proposed contraction is performed. + + """ + + # Find the contraction + contract = _find_contraction(positions, input_sets, output_set) + idx_result, new_input_sets, idx_removed, idx_contract = contract + + # Sieve the results based on memory_limit + new_size = _compute_size_by_dict(idx_result, idx_dict) + if new_size > memory_limit: + return None + + # Build sort tuple + old_sizes = (_compute_size_by_dict(input_sets[p], idx_dict) for p in positions) + removed_size = sum(old_sizes) - new_size + + # NB: removed_size used to be just the size of any removed indices i.e.: + # helpers.compute_size_by_dict(idx_removed, idx_dict) + cost = _flop_count(idx_contract, idx_removed, len(positions), idx_dict) + sort = (-removed_size, cost) + + # Sieve based on total cost as well + if (path_cost + cost) > naive_cost: + return None + + # Add contraction to possible choices + return [sort, positions, new_input_sets] + + +def _update_other_results(results, best): + """Update the positions and provisional input_sets of ``results`` based on + performing the contraction result ``best``. Remove any involving the tensors + contracted. + + Parameters + ---------- + results : list + List of contraction results produced by ``_parse_possible_contraction``. + best : list + The best contraction of ``results`` i.e. the one that will be performed. + + Returns + ------- + mod_results : list + The list of modified results, updated with outcome of ``best`` contraction. + """ + + best_con = best[1] + bx, by = best_con + mod_results = [] + + for cost, (x, y), con_sets in results: + + # Ignore results involving tensors just contracted + if x in best_con or y in best_con: + continue + + # Update the input_sets + del con_sets[by - int(by > x) - int(by > y)] + del con_sets[bx - int(bx > x) - int(bx > y)] + con_sets.insert(-1, best[2][-1]) + + # Update the position indices + mod_con = x - int(x > bx) - int(x > by), y - int(y > bx) - int(y > by) + mod_results.append((cost, mod_con, con_sets)) + + return mod_results def _greedy_path(input_sets, output_set, idx_dict, memory_limit): """ @@ -202,7 +327,7 @@ def _greedy_path(input_sets, output_set, idx_dict, memory_limit): Set that represents the rhs side of the overall einsum subscript idx_dict : dictionary Dictionary of index sizes - memory_limit_limit : int + memory_limit : int The maximum number of elements in a temporary array Returns @@ -213,52 +338,74 @@ def _greedy_path(input_sets, output_set, idx_dict, memory_limit): Examples -------- >>> isets = [set('abd'), set('ac'), set('bdc')] - >>> oset = set('') + >>> oset = set() >>> idx_sizes = {'a': 1, 'b':2, 'c':3, 'd':4} - >>> _path__greedy_path(isets, oset, idx_sizes, 5000) + >>> _greedy_path(isets, oset, idx_sizes, 5000) [(0, 2), (0, 1)] """ + # Handle trivial cases that leaked through if len(input_sets) == 1: return [(0,)] + elif len(input_sets) == 2: + return [(0, 1)] + + # Build up a naive cost + contract = _find_contraction(range(len(input_sets)), input_sets, output_set) + idx_result, new_input_sets, idx_removed, idx_contract = contract + naive_cost = _flop_count(idx_contract, idx_removed, len(input_sets), idx_dict) + # Initially iterate over all pairs + comb_iter = itertools.combinations(range(len(input_sets)), 2) + known_contractions = [] + + path_cost = 0 path = [] - for iteration in range(len(input_sets) - 1): - iteration_results = [] - comb_iter = [] - # Compute all unique pairs - for x in range(len(input_sets)): - for y in range(x + 1, len(input_sets)): - comb_iter.append((x, y)) + for iteration in range(len(input_sets) - 1): + # Iterate over all pairs on first step, only previously found pairs on subsequent steps for positions in comb_iter: - # Find the contraction - contract = _find_contraction(positions, input_sets, output_set) - idx_result, new_input_sets, idx_removed, idx_contract = contract - - # Sieve the results based on memory_limit - if _compute_size_by_dict(idx_result, idx_dict) > memory_limit: + # Always initially ignore outer products + if input_sets[positions[0]].isdisjoint(input_sets[positions[1]]): continue - # Build sort tuple - removed_size = _compute_size_by_dict(idx_removed, idx_dict) - cost = _compute_size_by_dict(idx_contract, idx_dict) - sort = (-removed_size, cost) + result = _parse_possible_contraction(positions, input_sets, output_set, idx_dict, memory_limit, path_cost, + naive_cost) + if result is not None: + known_contractions.append(result) + + # If we do not have a inner contraction, rescan pairs including outer products + if len(known_contractions) == 0: - # Add contraction to possible choices - iteration_results.append([sort, positions, new_input_sets]) + # Then check the outer products + for positions in itertools.combinations(range(len(input_sets)), 2): + result = _parse_possible_contraction(positions, input_sets, output_set, idx_dict, memory_limit, + path_cost, naive_cost) + if result is not None: + known_contractions.append(result) - # If we did not find a new contraction contract remaining - if len(iteration_results) == 0: - path.append(tuple(range(len(input_sets)))) - break + # If we still did not find any remaining contractions, default back to einsum like behavior + if len(known_contractions) == 0: + path.append(tuple(range(len(input_sets)))) + break # Sort based on first index - best = min(iteration_results, key=lambda x: x[0]) - path.append(best[1]) + best = min(known_contractions, key=lambda x: x[0]) + + # Now propagate as many unused contractions as possible to next iteration + known_contractions = _update_other_results(known_contractions, best) + + # Next iteration only compute contractions with the new tensor + # All other contractions have been accounted for input_sets = best[2] + new_tensor_pos = len(input_sets) - 1 + comb_iter = ((i, new_tensor_pos) for i in range(new_tensor_pos)) + + # Update path and total cost + path.append(best[1]) + path_cost += best[0][1] return path @@ -314,26 +461,27 @@ def _can_dot(inputs, result, idx_removed): if len(inputs) != 2: return False - # Build a few temporaries input_left, input_right = inputs + + for c in set(input_left + input_right): + # can't deal with repeated indices on same input or more than 2 total + nl, nr = input_left.count(c), input_right.count(c) + if (nl > 1) or (nr > 1) or (nl + nr > 2): + return False + + # can't do implicit summation or dimension collapse e.g. + # "ab,bc->c" (implicitly sum over 'a') + # "ab,ca->ca" (take diagonal of 'a') + if nl + nr - 1 == int(c in result): + return False + + # Build a few temporaries set_left = set(input_left) set_right = set(input_right) keep_left = set_left - idx_removed keep_right = set_right - idx_removed rs = len(idx_removed) - # Indices must overlap between the two operands - if not len(set_left & set_right): - return False - - # We cannot have duplicate indices ("ijj, jk -> ik") - if (len(set_left) != len(input_left)) or (len(set_right) != len(input_right)): - return False - - # Cannot handle partial inner ("ij, ji -> i") - if len(keep_left & keep_right): - return False - # At this point we are a DOT, GEMV, or GEMM operation # Handle inner products @@ -371,6 +519,7 @@ def _can_dot(inputs, result, idx_removed): # We are a matrix-matrix product, but we need to copy data return True + def _parse_einsum_input(operands): """ A reproduction of einsum c side einsum parsing in python. @@ -388,19 +537,20 @@ def _parse_einsum_input(operands): -------- The operand list is simplified to reduce printing: + >>> np.random.seed(123) >>> a = np.random.rand(4, 4) >>> b = np.random.rand(4, 4, 4) - >>> __parse_einsum_input(('...a,...a->...', a, b)) - ('za,xza', 'xz', [a, b]) + >>> _parse_einsum_input(('...a,...a->...', a, b)) + ('za,xza', 'xz', [a, b]) # may vary - >>> __parse_einsum_input((a, [Ellipsis, 0], b, [Ellipsis, 0])) - ('za,xza', 'xz', [a, b]) + >>> _parse_einsum_input((a, [Ellipsis, 0], b, [Ellipsis, 0])) + ('za,xza', 'xz', [a, b]) # may vary """ if len(operands) == 0: raise ValueError("No input operands") - if isinstance(operands[0], basestring): + if isinstance(operands[0], str): subscripts = operands[0].replace(" ", "") operands = [asanyarray(v) for v in operands[1:]] @@ -427,11 +577,13 @@ def _parse_einsum_input(operands): for s in sub: if s is Ellipsis: subscripts += "..." - elif isinstance(s, int): - subscripts += einsum_symbols[s] else: - raise TypeError("For this input type lists must contain " - "either int or Ellipsis") + try: + s = operator.index(s) + except TypeError as e: + raise TypeError("For this input type lists must contain " + "either int or Ellipsis") from e + subscripts += einsum_symbols[s] if num != last: subscripts += "," @@ -440,11 +592,13 @@ def _parse_einsum_input(operands): for s in output_list: if s is Ellipsis: subscripts += "..." - elif isinstance(s, int): - subscripts += einsum_symbols[s] else: - raise TypeError("For this input type lists must contain " - "either int or Ellipsis") + try: + s = operator.index(s) + except TypeError as e: + raise TypeError("For this input type lists must contain " + "either int or Ellipsis") from e + subscripts += einsum_symbols[s] # Check for proper "->" if ("-" in subscripts) or (">" in subscripts): invalid = (subscripts.count("-") > 1) or (subscripts.count(">") > 1) @@ -539,7 +693,18 @@ def _parse_einsum_input(operands): return (input_subscripts, output_subscript, operands) -def einsum_path(*operands, **kwargs): +def _einsum_path_dispatcher(*operands, optimize=None, einsum_call=None): + # NOTE: technically, we should only dispatch on array-like arguments, not + # subscripts (given as strings). But separating operands into + # arrays/subscripts is a little tricky/slow (given einsum's two supported + # signatures), so as a practical shortcut we dispatch on everything. + # Strings will be ignored for dispatching since they don't define + # __array_function__. + return operands + + +@array_function_dispatch(_einsum_path_dispatcher, module='numpy') +def einsum_path(*operands, optimize='greedy', einsum_call=False): """ einsum_path(subscripts, *operands, optimize='greedy') @@ -601,6 +766,7 @@ def einsum_path(*operands, **kwargs): of the contraction and the remaining contraction ``(0, 1)`` is then completed. + >>> np.random.seed(123) >>> a = np.random.rand(2, 2) >>> b = np.random.rand(2, 5) >>> c = np.random.rand(5, 2) @@ -608,7 +774,7 @@ def einsum_path(*operands, **kwargs): >>> print(path_info[0]) ['einsum_path', (1, 2), (0, 1)] >>> print(path_info[1]) - Complete contraction: ij,jk,kl->il + Complete contraction: ij,jk,kl->il # may vary Naive scaling: 4 Optimized scaling: 3 Naive FLOP count: 1.600e+02 @@ -627,12 +793,12 @@ def einsum_path(*operands, **kwargs): >>> I = np.random.rand(10, 10, 10, 10) >>> C = np.random.rand(10, 10) >>> path_info = np.einsum_path('ea,fb,abcd,gc,hd->efgh', C, C, I, C, C, - optimize='greedy') + ... optimize='greedy') >>> print(path_info[0]) ['einsum_path', (0, 2), (0, 3), (0, 2), (0, 1)] - >>> print(path_info[1]) - Complete contraction: ea,fb,abcd,gc,hd->efgh + >>> print(path_info[1]) + Complete contraction: ea,fb,abcd,gc,hd->efgh # may vary Naive scaling: 8 Optimized scaling: 5 Naive FLOP count: 8.000e+08 @@ -648,16 +814,8 @@ def einsum_path(*operands, **kwargs): 5 defg,hd->efgh efgh->efgh """ - # Make sure all keywords are valid - valid_contract_kwargs = ['optimize', 'einsum_call'] - unknown_kwargs = [k for (k, v) in kwargs.items() if k - not in valid_contract_kwargs] - if len(unknown_kwargs): - raise TypeError("Did not understand the following kwargs:" - " %s" % unknown_kwargs) - # Figure out what the path really is - path_type = kwargs.pop('optimize', True) + path_type = optimize if path_type is True: path_type = 'greedy' if path_type is None: @@ -666,7 +824,7 @@ def einsum_path(*operands, **kwargs): memory_limit = None # No optimization or a named path algorithm - if (path_type is False) or isinstance(path_type, basestring): + if (path_type is False) or isinstance(path_type, str): pass # Given an explicit path @@ -674,7 +832,7 @@ def einsum_path(*operands, **kwargs): pass # Path tuple with memory limit - elif ((len(path_type) == 2) and isinstance(path_type[0], basestring) and + elif ((len(path_type) == 2) and isinstance(path_type[0], str) and isinstance(path_type[1], (int, float))): memory_limit = int(path_type[1]) path_type = path_type[0] @@ -683,11 +841,10 @@ def einsum_path(*operands, **kwargs): raise TypeError("Did not understand the path: %s" % str(path_type)) # Hidden option, only einsum should call this - einsum_call_arg = kwargs.pop("einsum_call", False) + einsum_call_arg = einsum_call # Python side parsing input_subscripts, output_subscript, operands = _parse_einsum_input(operands) - subscripts = input_subscripts + '->' + output_subscript # Build a few useful list and sets input_list = input_subscripts.split(',') @@ -697,6 +854,7 @@ def einsum_path(*operands, **kwargs): # Get length of each unique dimension and ensure all dimensions are correct dimension_dict = {} + broadcast_indices = [[] for x in range(len(input_list))] for tnum, term in enumerate(input_list): sh = operands[tnum].shape if len(sh) != len(term): @@ -705,6 +863,11 @@ def einsum_path(*operands, **kwargs): % (input_subscripts[tnum], tnum)) for cnum, char in enumerate(term): dim = sh[cnum] + + # Build out broadcast indices + if dim == 1: + broadcast_indices[tnum].append(char) + if char in dimension_dict.keys(): # For broadcasting cases we always want the largest dim size if dimension_dict[char] == 1: @@ -716,10 +879,12 @@ def einsum_path(*operands, **kwargs): else: dimension_dict[char] = dim + # Convert broadcast inds to sets + broadcast_indices = [set(x) for x in broadcast_indices] + # Compute size of each input array plus the output array - size_list = [] - for term in input_list + [output_subscript]: - size_list.append(_compute_size_by_dict(term, dimension_dict)) + size_list = [_compute_size_by_dict(term, dimension_dict) + for term in input_list + [output_subscript]] max_size = max(size_list) if memory_limit is None: @@ -729,20 +894,14 @@ def einsum_path(*operands, **kwargs): # Compute naive cost # This isn't quite right, need to look into exactly how einsum does this - naive_cost = _compute_size_by_dict(indices, dimension_dict) - indices_in_input = input_subscripts.replace(',', '') - mult = max(len(input_list) - 1, 1) - if (len(indices_in_input) - len(set(indices_in_input))): - mult *= 2 - naive_cost *= mult + inner_product = (sum(len(x) for x in input_sets) - len(indices)) > 0 + naive_cost = _flop_count(indices, inner_product, len(input_list), dimension_dict) # Compute the path if (path_type is False) or (len(input_list) in [1, 2]) or (indices == output_set): # Nothing to be optimized, leave it to einsum path = [tuple(range(len(input_list)))] elif path_type == "greedy": - # Maximum memory should be at most out_size for this algorithm - memory_arg = min(memory_arg, max_size) path = _greedy_path(input_sets, output_set, dimension_dict, memory_arg) elif path_type == "optimal": path = _optimal_path(input_sets, output_set, dimension_dict, memory_arg) @@ -761,18 +920,24 @@ def einsum_path(*operands, **kwargs): contract = _find_contraction(contract_inds, input_sets, output_set) out_inds, input_sets, idx_removed, idx_contract = contract - cost = _compute_size_by_dict(idx_contract, dimension_dict) - if idx_removed: - cost *= 2 + cost = _flop_count(idx_contract, idx_removed, len(contract_inds), dimension_dict) cost_list.append(cost) scale_list.append(len(idx_contract)) size_list.append(_compute_size_by_dict(out_inds, dimension_dict)) + bcast = set() tmp_inputs = [] for x in contract_inds: tmp_inputs.append(input_list.pop(x)) + bcast |= broadcast_indices.pop(x) - do_blas = _can_dot(tmp_inputs, out_inds, idx_removed) + new_bcast_inds = bcast - idx_removed + + # If we're broadcasting, nix blas + if not len(idx_removed & bcast): + do_blas = _can_dot(tmp_inputs, out_inds, idx_removed) + else: + do_blas = False # Last contraction if (cnum - len(path)) == -1: @@ -782,6 +947,7 @@ def einsum_path(*operands, **kwargs): idx_result = "".join([x[1] for x in sorted(sort_result)]) input_list.append(idx_result) + broadcast_indices.append(new_bcast_inds) einsum_str = ",".join(tmp_inputs) + "->" + idx_result contraction = (contract_inds, idx_removed, einsum_str, input_list[:], do_blas) @@ -820,27 +986,43 @@ def einsum_path(*operands, **kwargs): return (path, path_print) +def _einsum_dispatcher(*operands, out=None, optimize=None, **kwargs): + # Arguably we dispatch on more arguments than we really should; see note in + # _einsum_path_dispatcher for why. + yield from operands + yield out + + # Rewrite einsum to handle different cases -def einsum(*operands, **kwargs): +@array_function_dispatch(_einsum_dispatcher, module='numpy') +def einsum(*operands, out=None, optimize=False, **kwargs): """ einsum(subscripts, *operands, out=None, dtype=None, order='K', casting='safe', optimize=False) Evaluates the Einstein summation convention on the operands. - Using the Einstein summation convention, many common multi-dimensional - array operations can be represented in a simple fashion. This function - provides a way to compute such summations. The best way to understand this - function is to try the examples below, which show how many common NumPy - functions can be implemented as calls to `einsum`. + Using the Einstein summation convention, many common multi-dimensional, + linear algebraic array operations can be represented in a simple fashion. + In *implicit* mode `einsum` computes these values. + + In *explicit* mode, `einsum` provides further flexibility to compute + other array operations that might not be considered classical Einstein + summation operations, by disabling, or forcing summation over specified + subscript labels. + + See the notes and examples for clarification. Parameters ---------- subscripts : str - Specifies the subscripts for summation. + Specifies the subscripts for summation as comma separated list of + subscript labels. An implicit (classical Einstein summation) + calculation is performed unless the explicit indicator '->' is + included as well as subscript labels of the precise output form. operands : list of array_like These are the arrays for the operation. - out : {ndarray, None}, optional + out : ndarray, optional If provided, the calculation is done into this array. dtype : {data-type, None}, optional If provided, forces the calculation to use the data type specified. @@ -869,7 +1051,7 @@ def einsum(*operands, **kwargs): Controls if intermediate optimization should occur. No optimization will occur if False and True will default to the 'greedy' algorithm. Also accepts an explicit contraction list from the ``np.einsum_path`` - function. See ``np.einsum_path`` for more details. Default is True. + function. See ``np.einsum_path`` for more details. Defaults to False. Returns ------- @@ -879,55 +1061,94 @@ def einsum(*operands, **kwargs): See Also -------- einsum_path, dot, inner, outer, tensordot, linalg.multi_dot + einops : + similar verbose interface is provided by + `einops <https://github.com/arogozhnikov/einops>`_ package to cover + additional operations: transpose, reshape/flatten, repeat/tile, + squeeze/unsqueeze and reductions. + opt_einsum : + `opt_einsum <https://optimized-einsum.readthedocs.io/en/stable/>`_ + optimizes contraction order for einsum-like expressions + in backend-agnostic manner. Notes ----- .. versionadded:: 1.6.0 - The subscripts string is a comma-separated list of subscript labels, - where each label refers to a dimension of the corresponding operand. - Repeated subscripts labels in one operand take the diagonal. For example, - ``np.einsum('ii', a)`` is equivalent to ``np.trace(a)``. + The Einstein summation convention can be used to compute + many multi-dimensional, linear algebraic array operations. `einsum` + provides a succinct way of representing these. - Whenever a label is repeated, it is summed, so ``np.einsum('i,i', a, b)`` - is equivalent to ``np.inner(a,b)``. If a label appears only once, - it is not summed, so ``np.einsum('i', a)`` produces a view of ``a`` - with no changes. + A non-exhaustive list of these operations, + which can be computed by `einsum`, is shown below along with examples: - The order of labels in the output is by default alphabetical. This - means that ``np.einsum('ij', a)`` doesn't affect a 2D array, while - ``np.einsum('ji', a)`` takes its transpose. + * Trace of an array, :py:func:`numpy.trace`. + * Return a diagonal, :py:func:`numpy.diag`. + * Array axis summations, :py:func:`numpy.sum`. + * Transpositions and permutations, :py:func:`numpy.transpose`. + * Matrix multiplication and dot product, :py:func:`numpy.matmul` :py:func:`numpy.dot`. + * Vector inner and outer products, :py:func:`numpy.inner` :py:func:`numpy.outer`. + * Broadcasting, element-wise and scalar multiplication, :py:func:`numpy.multiply`. + * Tensor contractions, :py:func:`numpy.tensordot`. + * Chained array operations, in efficient calculation order, :py:func:`numpy.einsum_path`. - The output can be controlled by specifying output subscript labels - as well. This specifies the label order, and allows summing to - be disallowed or forced when desired. The call ``np.einsum('i->', a)`` - is like ``np.sum(a, axis=-1)``, and ``np.einsum('ii->i', a)`` - is like ``np.diag(a)``. The difference is that `einsum` does not - allow broadcasting by default. + The subscripts string is a comma-separated list of subscript labels, + where each label refers to a dimension of the corresponding operand. + Whenever a label is repeated it is summed, so ``np.einsum('i,i', a, b)`` + is equivalent to :py:func:`np.inner(a,b) <numpy.inner>`. If a label + appears only once, it is not summed, so ``np.einsum('i', a)`` produces a + view of ``a`` with no changes. A further example ``np.einsum('ij,jk', a, b)`` + describes traditional matrix multiplication and is equivalent to + :py:func:`np.matmul(a,b) <numpy.matmul>`. Repeated subscript labels in one + operand take the diagonal. For example, ``np.einsum('ii', a)`` is equivalent + to :py:func:`np.trace(a) <numpy.trace>`. + + In *implicit mode*, the chosen subscripts are important + since the axes of the output are reordered alphabetically. This + means that ``np.einsum('ij', a)`` doesn't affect a 2D array, while + ``np.einsum('ji', a)`` takes its transpose. Additionally, + ``np.einsum('ij,jk', a, b)`` returns a matrix multiplication, while, + ``np.einsum('ij,jh', a, b)`` returns the transpose of the + multiplication since subscript 'h' precedes subscript 'i'. + + In *explicit mode* the output can be directly controlled by + specifying output subscript labels. This requires the + identifier '->' as well as the list of output subscript labels. + This feature increases the flexibility of the function since + summing can be disabled or forced when required. The call + ``np.einsum('i->', a)`` is like :py:func:`np.sum(a, axis=-1) <numpy.sum>`, + and ``np.einsum('ii->i', a)`` is like :py:func:`np.diag(a) <numpy.diag>`. + The difference is that `einsum` does not allow broadcasting by default. + Additionally ``np.einsum('ij,jh->ih', a, b)`` directly specifies the + order of the output subscript labels and therefore returns matrix + multiplication, unlike the example above in implicit mode. To enable and control broadcasting, use an ellipsis. Default NumPy-style broadcasting is done by adding an ellipsis to the left of each term, like ``np.einsum('...ii->...i', a)``. To take the trace along the first and last axes, you can do ``np.einsum('i...i', a)``, or to do a matrix-matrix - product with the left-most indices instead of rightmost, you can do + product with the left-most indices instead of rightmost, one can do ``np.einsum('ij...,jk...->ik...', a, b)``. When there is only one operand, no axes are summed, and no output parameter is provided, a view into the operand is returned instead of a new array. Thus, taking the diagonal as ``np.einsum('ii->i', a)`` - produces a view. + produces a view (changed in version 1.10.0). - An alternative way to provide the subscripts and operands is as - ``einsum(op0, sublist0, op1, sublist1, ..., [sublistout])``. The examples - below have corresponding `einsum` calls with the two parameter methods. + `einsum` also provides an alternative way to provide the subscripts + and operands as ``einsum(op0, sublist0, op1, sublist1, ..., [sublistout])``. + If the output shape is not provided in this format `einsum` will be + calculated in implicit mode, otherwise it will be performed explicitly. + The examples below have corresponding `einsum` calls with the two + parameter methods. .. versionadded:: 1.10.0 Views returned from einsum are now writeable whenever the input array is writeable. For example, ``np.einsum('ijk...->kji...', a)`` will now - have the same effect as ``np.swapaxes(a, 0, 2)`` and - ``np.einsum('ii->i', a)`` will return a writeable view of the diagonal + have the same effect as :py:func:`np.swapaxes(a, 0, 2) <numpy.swapaxes>` + and ``np.einsum('ii->i', a)`` will return a writeable view of the diagonal of a 2D array. .. versionadded:: 1.12.0 @@ -937,7 +1158,14 @@ def einsum(*operands, **kwargs): can greatly increase the computational efficiency at the cost of a larger memory footprint during computation. - See ``np.einsum_path`` for more details. + Typically a 'greedy' algorithm is applied which empirical tests have shown + returns the optimal path in the majority of cases. In some cases 'optimal' + will return the superlative path through a more expensive, exhaustive search. + For iterative calculations it may be advisable to calculate the optimal path + once and reuse that path by supplying it as an argument. An example is given + below. + + See :py:func:`numpy.einsum_path` for more details. Examples -------- @@ -945,6 +1173,8 @@ def einsum(*operands, **kwargs): >>> b = np.arange(5) >>> c = np.arange(6).reshape(2,3) + Trace of a matrix: + >>> np.einsum('ii', a) 60 >>> np.einsum(a, [0,0]) @@ -952,6 +1182,8 @@ def einsum(*operands, **kwargs): >>> np.trace(a) 60 + Extract the diagonal (requires explicit form): + >>> np.einsum('ii->i', a) array([ 0, 6, 12, 18, 24]) >>> np.einsum(a, [0,0], [0]) @@ -959,16 +1191,29 @@ def einsum(*operands, **kwargs): >>> np.diag(a) array([ 0, 6, 12, 18, 24]) - >>> np.einsum('ij,j', a, b) - array([ 30, 80, 130, 180, 230]) - >>> np.einsum(a, [0,1], b, [1]) - array([ 30, 80, 130, 180, 230]) - >>> np.dot(a, b) - array([ 30, 80, 130, 180, 230]) - >>> np.einsum('...j,j', a, b) - array([ 30, 80, 130, 180, 230]) + Sum over an axis (requires explicit form): + + >>> np.einsum('ij->i', a) + array([ 10, 35, 60, 85, 110]) + >>> np.einsum(a, [0,1], [0]) + array([ 10, 35, 60, 85, 110]) + >>> np.sum(a, axis=1) + array([ 10, 35, 60, 85, 110]) + + For higher dimensional arrays summing a single axis can be done with ellipsis: + + >>> np.einsum('...j->...', a) + array([ 10, 35, 60, 85, 110]) + >>> np.einsum(a, [Ellipsis,1], [Ellipsis]) + array([ 10, 35, 60, 85, 110]) + + Compute a matrix transpose, or reorder any number of axes: >>> np.einsum('ji', c) + array([[0, 3], + [1, 4], + [2, 5]]) + >>> np.einsum('ij->ji', c) array([[0, 3], [1, 4], [2, 5]]) @@ -976,15 +1221,37 @@ def einsum(*operands, **kwargs): array([[0, 3], [1, 4], [2, 5]]) - >>> c.T + >>> np.transpose(c) array([[0, 3], [1, 4], [2, 5]]) + Vector inner products: + + >>> np.einsum('i,i', b, b) + 30 + >>> np.einsum(b, [0], b, [0]) + 30 + >>> np.inner(b,b) + 30 + + Matrix vector multiplication: + + >>> np.einsum('ij,j', a, b) + array([ 30, 80, 130, 180, 230]) + >>> np.einsum(a, [0,1], b, [1]) + array([ 30, 80, 130, 180, 230]) + >>> np.dot(a, b) + array([ 30, 80, 130, 180, 230]) + >>> np.einsum('...j,j', a, b) + array([ 30, 80, 130, 180, 230]) + + Broadcasting and scalar multiplication: + >>> np.einsum('..., ...', 3, c) array([[ 0, 3, 6], [ 9, 12, 15]]) - >>> np.einsum(',ij', 3, C) + >>> np.einsum(',ij', 3, c) array([[ 0, 3, 6], [ 9, 12, 15]]) >>> np.einsum(3, [Ellipsis], c, [Ellipsis]) @@ -994,12 +1261,7 @@ def einsum(*operands, **kwargs): array([[ 0, 3, 6], [ 9, 12, 15]]) - >>> np.einsum('i,i', b, b) - 30 - >>> np.einsum(b, [0], b, [0]) - 30 - >>> np.inner(b,b) - 30 + Vector outer product: >>> np.einsum('i,j', np.arange(2)+1, b) array([[0, 1, 2, 3, 4], @@ -1011,33 +1273,39 @@ def einsum(*operands, **kwargs): array([[0, 1, 2, 3, 4], [0, 2, 4, 6, 8]]) - >>> np.einsum('i...->...', a) - array([50, 55, 60, 65, 70]) - >>> np.einsum(a, [0,Ellipsis], [Ellipsis]) - array([50, 55, 60, 65, 70]) - >>> np.sum(a, axis=0) - array([50, 55, 60, 65, 70]) + Tensor contraction: >>> a = np.arange(60.).reshape(3,4,5) >>> b = np.arange(24.).reshape(4,3,2) >>> np.einsum('ijk,jil->kl', a, b) - array([[ 4400., 4730.], - [ 4532., 4874.], - [ 4664., 5018.], - [ 4796., 5162.], - [ 4928., 5306.]]) + array([[4400., 4730.], + [4532., 4874.], + [4664., 5018.], + [4796., 5162.], + [4928., 5306.]]) >>> np.einsum(a, [0,1,2], b, [1,0,3], [2,3]) - array([[ 4400., 4730.], - [ 4532., 4874.], - [ 4664., 5018.], - [ 4796., 5162.], - [ 4928., 5306.]]) + array([[4400., 4730.], + [4532., 4874.], + [4664., 5018.], + [4796., 5162.], + [4928., 5306.]]) >>> np.tensordot(a,b, axes=([1,0],[0,1])) - array([[ 4400., 4730.], - [ 4532., 4874.], - [ 4664., 5018.], - [ 4796., 5162.], - [ 4928., 5306.]]) + array([[4400., 4730.], + [4532., 4874.], + [4664., 5018.], + [4796., 5162.], + [4928., 5306.]]) + + Writeable returned arrays (since version 1.10.0): + + >>> a = np.zeros((3, 3)) + >>> np.einsum('ii->i', a)[:] = 1 + >>> a + array([[1., 0., 0.], + [0., 1., 0.], + [0., 0., 1.]]) + + Example of ellipsis use: >>> a = np.arange(6).reshape((3,2)) >>> b = np.arange(12).reshape((4,3)) @@ -1051,82 +1319,87 @@ def einsum(*operands, **kwargs): array([[10, 28, 46, 64], [13, 40, 67, 94]]) - >>> # since version 1.10.0 - >>> a = np.zeros((3, 3)) - >>> np.einsum('ii->i', a)[:] = 1 - >>> a - array([[ 1., 0., 0.], - [ 0., 1., 0.], - [ 0., 0., 1.]]) + Chained array operations. For more complicated contractions, speed ups + might be achieved by repeatedly computing a 'greedy' path or pre-computing the + 'optimal' path and repeatedly applying it, using an + `einsum_path` insertion (since version 1.12.0). Performance improvements can be + particularly significant with larger arrays: - """ + >>> a = np.ones(64).reshape(2,4,8) + + Basic `einsum`: ~1520ms (benchmarked on 3.1GHz Intel i5.) + + >>> for iteration in range(500): + ... _ = np.einsum('ijk,ilm,njm,nlk,abc->',a,a,a,a,a) + + Sub-optimal `einsum` (due to repeated path calculation time): ~330ms + + >>> for iteration in range(500): + ... _ = np.einsum('ijk,ilm,njm,nlk,abc->',a,a,a,a,a, optimize='optimal') + + Greedy `einsum` (faster optimal path approximation): ~160ms + + >>> for iteration in range(500): + ... _ = np.einsum('ijk,ilm,njm,nlk,abc->',a,a,a,a,a, optimize='greedy') + + Optimal `einsum` (best usage pattern in some use cases): ~110ms - # Grab non-einsum kwargs; never optimize 2-argument case. - optimize_arg = kwargs.pop('optimize', len(operands) > 3) + >>> path = np.einsum_path('ijk,ilm,njm,nlk,abc->',a,a,a,a,a, optimize='optimal')[0] + >>> for iteration in range(500): + ... _ = np.einsum('ijk,ilm,njm,nlk,abc->',a,a,a,a,a, optimize=path) + + """ + # Special handling if out is specified + specified_out = out is not None # If no optimization, run pure einsum - if optimize_arg is False: + if optimize is False: + if specified_out: + kwargs['out'] = out return c_einsum(*operands, **kwargs) - valid_einsum_kwargs = ['out', 'dtype', 'order', 'casting'] - einsum_kwargs = {k: v for (k, v) in kwargs.items() if - k in valid_einsum_kwargs} - - # Make sure all keywords are valid - valid_contract_kwargs = ['optimize'] + valid_einsum_kwargs + # Check the kwargs to avoid a more cryptic error later, without having to + # repeat default values here + valid_einsum_kwargs = ['dtype', 'order', 'casting'] unknown_kwargs = [k for (k, v) in kwargs.items() if - k not in valid_contract_kwargs] - + k not in valid_einsum_kwargs] if len(unknown_kwargs): raise TypeError("Did not understand the following kwargs: %s" % unknown_kwargs) - # Special handeling if out is specified - specified_out = False - out_array = einsum_kwargs.pop('out', None) - if out_array is not None: - specified_out = True - # Build the contraction list and operand - operands, contraction_list = einsum_path(*operands, optimize=optimize_arg, + operands, contraction_list = einsum_path(*operands, optimize=optimize, einsum_call=True) - handle_out = False + # Handle order kwarg for output array, c_einsum allows mixed case + output_order = kwargs.pop('order', 'K') + if output_order.upper() == 'A': + if all(arr.flags.f_contiguous for arr in operands): + output_order = 'F' + else: + output_order = 'C' # Start contraction loop for num, contraction in enumerate(contraction_list): inds, idx_rm, einsum_str, remaining, blas = contraction - tmp_operands = [] - for x in inds: - tmp_operands.append(operands.pop(x)) + tmp_operands = [operands.pop(x) for x in inds] # Do we need to deal with the output? - if specified_out and ((num + 1) == len(contraction_list)): - handle_out = True + handle_out = specified_out and ((num + 1) == len(contraction_list)) - # Handle broadcasting vs BLAS cases + # Call tensordot if still possible if blas: # Checks have already been handled input_str, results_index = einsum_str.split('->') input_left, input_right = input_str.split(',') - if 1 in tmp_operands[0].shape or 1 in tmp_operands[1].shape: - left_dims = {dim: size for dim, size in - zip(input_left, tmp_operands[0].shape)} - right_dims = {dim: size for dim, size in - zip(input_right, tmp_operands[1].shape)} - # If dims do not match we are broadcasting, BLAS off - if any(left_dims[ind] != right_dims[ind] for ind in idx_rm): - blas = False - # Call tensordot if still possible - if blas: tensor_result = input_left + input_right for s in idx_rm: tensor_result = tensor_result.replace(s, "") # Find indices to contract over left_pos, right_pos = [], [] - for s in idx_rm: + for s in sorted(idx_rm): left_pos.append(input_left.find(s)) right_pos.append(input_right.find(s)) @@ -1136,23 +1409,23 @@ def einsum(*operands, **kwargs): # Build a new view if needed if (tensor_result != results_index) or handle_out: if handle_out: - einsum_kwargs["out"] = out_array - new_view = c_einsum(tensor_result + '->' + results_index, new_view, **einsum_kwargs) + kwargs["out"] = out + new_view = c_einsum(tensor_result + '->' + results_index, new_view, **kwargs) # Call einsum else: # If out was specified if handle_out: - einsum_kwargs["out"] = out_array + kwargs["out"] = out # Do the contraction - new_view = c_einsum(einsum_str, *tmp_operands, **einsum_kwargs) + new_view = c_einsum(einsum_str, *tmp_operands, **kwargs) # Append new items and dereference what we can operands.append(new_view) del tmp_operands, new_view if specified_out: - return out_array + return out else: - return operands[0] + return asanyarray(operands[0], order=output_order) diff --git a/numpy/core/einsumfunc.pyi b/numpy/core/einsumfunc.pyi new file mode 100644 index 000000000000..aabb04c478b9 --- /dev/null +++ b/numpy/core/einsumfunc.pyi @@ -0,0 +1,145 @@ +from typing import List, TypeVar, Optional, Any, overload, Union, Tuple, Sequence, Literal + +from numpy import ( + ndarray, + dtype, + bool_, + unsignedinteger, + signedinteger, + floating, + complexfloating, + number, + _OrderKACF, +) +from numpy.typing import ( + _ArrayLikeBool_co, + _ArrayLikeUInt_co, + _ArrayLikeInt_co, + _ArrayLikeFloat_co, + _ArrayLikeComplex_co, + _DTypeLikeBool, + _DTypeLikeUInt, + _DTypeLikeInt, + _DTypeLikeFloat, + _DTypeLikeComplex, + _DTypeLikeComplex_co, +) + +_ArrayType = TypeVar( + "_ArrayType", + bound=ndarray[Any, dtype[Union[bool_, number[Any]]]], +) + +_OptimizeKind = Union[ + None, bool, Literal["greedy", "optimal"], Sequence[Any] +] +_CastingSafe = Literal["no", "equiv", "safe", "same_kind"] +_CastingUnsafe = Literal["unsafe"] + +__all__: List[str] + +# TODO: Properly handle the `casting`-based combinatorics +# TODO: We need to evaluate the content `__subscripts` in order +# to identify whether or an array or scalar is returned. At a cursory +# glance this seems like something that can quite easily be done with +# a mypy plugin. +# Something like `is_scalar = bool(__subscripts.partition("->")[-1])` +@overload +def einsum( + subscripts: str, + /, + *operands: _ArrayLikeBool_co, + out: None = ..., + dtype: Optional[_DTypeLikeBool] = ..., + order: _OrderKACF = ..., + casting: _CastingSafe = ..., + optimize: _OptimizeKind = ..., +) -> Any: ... +@overload +def einsum( + subscripts: str, + /, + *operands: _ArrayLikeUInt_co, + out: None = ..., + dtype: Optional[_DTypeLikeUInt] = ..., + order: _OrderKACF = ..., + casting: _CastingSafe = ..., + optimize: _OptimizeKind = ..., +) -> Any: ... +@overload +def einsum( + subscripts: str, + /, + *operands: _ArrayLikeInt_co, + out: None = ..., + dtype: Optional[_DTypeLikeInt] = ..., + order: _OrderKACF = ..., + casting: _CastingSafe = ..., + optimize: _OptimizeKind = ..., +) -> Any: ... +@overload +def einsum( + subscripts: str, + /, + *operands: _ArrayLikeFloat_co, + out: None = ..., + dtype: Optional[_DTypeLikeFloat] = ..., + order: _OrderKACF = ..., + casting: _CastingSafe = ..., + optimize: _OptimizeKind = ..., +) -> Any: ... +@overload +def einsum( + subscripts: str, + /, + *operands: _ArrayLikeComplex_co, + out: None = ..., + dtype: Optional[_DTypeLikeComplex] = ..., + order: _OrderKACF = ..., + casting: _CastingSafe = ..., + optimize: _OptimizeKind = ..., +) -> Any: ... +@overload +def einsum( + subscripts: str, + /, + *operands: Any, + casting: _CastingUnsafe, + dtype: Optional[_DTypeLikeComplex_co] = ..., + out: None = ..., + order: _OrderKACF = ..., + optimize: _OptimizeKind = ..., +) -> Any: ... +@overload +def einsum( + subscripts: str, + /, + *operands: _ArrayLikeComplex_co, + out: _ArrayType, + dtype: Optional[_DTypeLikeComplex_co] = ..., + order: _OrderKACF = ..., + casting: _CastingSafe = ..., + optimize: _OptimizeKind = ..., +) -> _ArrayType: ... +@overload +def einsum( + subscripts: str, + /, + *operands: Any, + out: _ArrayType, + casting: _CastingUnsafe, + dtype: Optional[_DTypeLikeComplex_co] = ..., + order: _OrderKACF = ..., + optimize: _OptimizeKind = ..., +) -> _ArrayType: ... + +# NOTE: `einsum_call` is a hidden kwarg unavailable for public use. +# It is therefore excluded from the signatures below. +# NOTE: In practice the list consists of a `str` (first element) +# and a variable number of integer tuples. +def einsum_path( + subscripts: str, + /, + *operands: _ArrayLikeComplex_co, + optimize: _OptimizeKind = ..., +) -> Tuple[List[Any], str]: ... diff --git a/numpy/core/fromnumeric.py b/numpy/core/fromnumeric.py index d1aae0aa0147..3242124acf50 100644 --- a/numpy/core/fromnumeric.py +++ b/numpy/core/fromnumeric.py @@ -1,17 +1,16 @@ """Module containing non-deprecated functions borrowed from Numeric. """ -from __future__ import division, absolute_import, print_function - +import functools import types import warnings import numpy as np -from .. import VisibleDeprecationWarning from . import multiarray as mu +from . import overrides from . import umath as um from . import numerictypes as nt -from .numeric import asarray, array, asanyarray, concatenate +from .multiarray import asarray, array, asanyarray, concatenate from . import _methods _dt_ = nt.sctype2char @@ -22,7 +21,7 @@ 'argmin', 'argpartition', 'argsort', 'around', 'choose', 'clip', 'compress', 'cumprod', 'cumproduct', 'cumsum', 'diagonal', 'mean', 'ndim', 'nonzero', 'partition', 'prod', 'product', 'ptp', 'put', - 'rank', 'ravel', 'repeat', 'reshape', 'resize', 'round_', + 'ravel', 'repeat', 'reshape', 'resize', 'round_', 'searchsorted', 'shape', 'size', 'sometrue', 'sort', 'squeeze', 'std', 'sum', 'swapaxes', 'take', 'trace', 'transpose', 'var', ] @@ -31,6 +30,9 @@ # save away Python sum _sum_ = sum +array_function_dispatch = functools.partial( + overrides.array_function_dispatch, module='numpy') + # functions that are now methods def _wrapit(obj, method, *args, **kwds): @@ -47,25 +49,26 @@ def _wrapit(obj, method, *args, **kwds): def _wrapfunc(obj, method, *args, **kwds): - try: - return getattr(obj, method)(*args, **kwds) - - # An AttributeError occurs if the object does not have - # such a method in its class. + bound = getattr(obj, method, None) + if bound is None: + return _wrapit(obj, method, *args, **kwds) - # A TypeError occurs if the object does have such a method - # in its class, but its signature is not identical to that - # of NumPy's. This situation has occurred in the case of - # a downstream library like 'pandas'. - except (AttributeError, TypeError): + try: + return bound(*args, **kwds) + except TypeError: + # A TypeError occurs if the object does have such a method in its + # class, but its signature is not identical to that of NumPy's. This + # situation has occurred in the case of a downstream library like + # 'pandas'. + # + # Call _wrapit from within the except clause to ensure a potential + # exception has a traceback chain. return _wrapit(obj, method, *args, **kwds) def _wrapreduction(obj, ufunc, method, axis, dtype, out, **kwargs): - passkwargs = {} - for k, v in kwargs.items(): - if v is not np._NoValue: - passkwargs[k] = v + passkwargs = {k: v for k, v in kwargs.items() + if v is not np._NoValue} if type(obj) is not mu.ndarray: try: @@ -83,6 +86,11 @@ def _wrapreduction(obj, ufunc, method, axis, dtype, out, **kwargs): return ufunc.reduce(obj, axis, dtype, out, **passkwargs) +def _take_dispatcher(a, indices, axis=None, out=None, mode=None): + return (a, out) + + +@array_function_dispatch(_take_dispatcher) def take(a, indices, axis=None, out=None, mode='raise'): """ Take elements from an array along an axis. @@ -119,7 +127,8 @@ def take(a, indices, axis=None, out=None, mode='raise'): input array is used. out : ndarray, optional (Ni..., Nj..., Nk...) If provided, the result will be placed in this array. It should - be of the appropriate shape and dtype. + be of the appropriate shape and dtype. Note that `out` is always + buffered if `mode='raise'`; use other modes for better performance. mode : {'raise', 'wrap', 'clip'}, optional Specifies how out-of-bounds indices will behave. @@ -181,7 +190,12 @@ def take(a, indices, axis=None, out=None, mode='raise'): return _wrapfunc(a, 'take', indices, axis=axis, out=out, mode=mode) +def _reshape_dispatcher(a, newshape, order=None): + return (a,) + + # not deprecated --- copy if necessary, view otherwise +@array_function_dispatch(_reshape_dispatcher) def reshape(a, newshape, order='C'): """ Gives a new shape to an array without changing its data. @@ -227,13 +241,18 @@ def reshape(a, newshape, order='C'): you should assign the new shape to the shape attribute of the array:: >>> a = np.zeros((10, 2)) + # A transpose makes the array non-contiguous >>> b = a.T + # Taking a view makes it possible to modify the shape without modifying # the initial object. >>> c = b.view() >>> c.shape = (20) - AttributeError: incompatible shape for a non-contiguous array + Traceback (most recent call last): + ... + AttributeError: Incompatible shape for in-place modification. Use + `.reshape()` to make a copy with the desired shape. The `order` keyword gives the index ordering both for *fetching* the values from `a`, and then *placing* the values into the output array. @@ -279,9 +298,16 @@ def reshape(a, newshape, order='C'): return _wrapfunc(a, 'reshape', newshape, order=order) +def _choose_dispatcher(a, choices, out=None, mode=None): + yield a + yield from choices + yield out + + +@array_function_dispatch(_choose_dispatcher) def choose(a, choices, out=None, mode='raise'): """ - Construct an array from an index array and a set of arrays to choose from. + Construct an array from an index array and a list of arrays to choose from. First of all, if confused or uncertain, definitely look at the Examples - in its full generality, this function is less simple than it might @@ -292,34 +318,34 @@ def choose(a, choices, out=None, mode='raise'): But this omits some subtleties. Here is a fully general summary: - Given an "index" array (`a`) of integers and a sequence of `n` arrays + Given an "index" array (`a`) of integers and a sequence of ``n`` arrays (`choices`), `a` and each choice array are first broadcast, as necessary, to arrays of a common shape; calling these *Ba* and *Bchoices[i], i = 0,...,n-1* we have that, necessarily, ``Ba.shape == Bchoices[i].shape`` - for each `i`. Then, a new array with shape ``Ba.shape`` is created as + for each ``i``. Then, a new array with shape ``Ba.shape`` is created as follows: - * if ``mode=raise`` (the default), then, first of all, each element of - `a` (and thus `Ba`) must be in the range `[0, n-1]`; now, suppose that - `i` (in that range) is the value at the `(j0, j1, ..., jm)` position - in `Ba` - then the value at the same position in the new array is the - value in `Bchoices[i]` at that same position; + * if ``mode='raise'`` (the default), then, first of all, each element of + ``a`` (and thus ``Ba``) must be in the range ``[0, n-1]``; now, suppose + that ``i`` (in that range) is the value at the ``(j0, j1, ..., jm)`` + position in ``Ba`` - then the value at the same position in the new array + is the value in ``Bchoices[i]`` at that same position; - * if ``mode=wrap``, values in `a` (and thus `Ba`) may be any (signed) + * if ``mode='wrap'``, values in `a` (and thus `Ba`) may be any (signed) integer; modular arithmetic is used to map integers outside the range `[0, n-1]` back into that range; and then the new array is constructed as above; - * if ``mode=clip``, values in `a` (and thus `Ba`) may be any (signed) - integer; negative integers are mapped to 0; values greater than `n-1` - are mapped to `n-1`; and then the new array is constructed as above. + * if ``mode='clip'``, values in `a` (and thus ``Ba``) may be any (signed) + integer; negative integers are mapped to 0; values greater than ``n-1`` + are mapped to ``n-1``; and then the new array is constructed as above. Parameters ---------- a : int array - This array must contain integers in `[0, n-1]`, where `n` is the number - of choices, unless ``mode=wrap`` or ``mode=clip``, in which cases any - integers are permissible. + This array must contain integers in ``[0, n-1]``, where ``n`` is the + number of choices, unless ``mode=wrap`` or ``mode=clip``, in which + cases any integers are permissible. choices : sequence of arrays Choice arrays. `a` and all of the choices must be broadcastable to the same shape. If `choices` is itself an array (not recommended), then @@ -327,12 +353,13 @@ def choose(a, choices, out=None, mode='raise'): ``choices.shape[0]``) is taken as defining the "sequence". out : array, optional If provided, the result will be inserted into this array. It should - be of the appropriate shape and dtype. + be of the appropriate shape and dtype. Note that `out` is always + buffered if ``mode='raise'``; use other modes for better performance. mode : {'raise' (default), 'wrap', 'clip'}, optional - Specifies how indices outside `[0, n-1]` will be treated: + Specifies how indices outside ``[0, n-1]`` will be treated: * 'raise' : an exception is raised - * 'wrap' : value becomes value mod `n` + * 'wrap' : value becomes value mod ``n`` * 'clip' : values < 0 are mapped to 0, values > n-1 are mapped to n-1 Returns @@ -349,6 +376,7 @@ def choose(a, choices, out=None, mode='raise'): See Also -------- ndarray.choose : equivalent method + numpy.take_along_axis : Preferable if `choices` is an array Notes ----- @@ -401,6 +429,11 @@ def choose(a, choices, out=None, mode='raise'): return _wrapfunc(a, 'choose', choices, out=out, mode=mode) +def _repeat_dispatcher(a, repeats, axis=None): + return (a,) + + +@array_function_dispatch(_repeat_dispatcher) def repeat(a, repeats, axis=None): """ Repeat elements of an array. @@ -425,6 +458,7 @@ def repeat(a, repeats, axis=None): See Also -------- tile : Tile an array. + unique : Find the unique elements of an array. Examples -------- @@ -445,6 +479,11 @@ def repeat(a, repeats, axis=None): return _wrapfunc(a, 'repeat', repeats, axis=axis) +def _put_dispatcher(a, ind, v, mode=None): + return (a, ind, v) + + +@array_function_dispatch(_put_dispatcher) def put(a, ind, v, mode='raise'): """ Replaces specified elements of an array with given values. @@ -474,7 +513,8 @@ def put(a, ind, v, mode='raise'): 'clip' mode means that all indices that are too large are replaced by the index that addresses the last element along that axis. Note - that this disables indexing with negative numbers. + that this disables indexing with negative numbers. In 'raise' mode, + if an exception occurs the target array may still be modified. See Also -------- @@ -496,13 +536,18 @@ def put(a, ind, v, mode='raise'): """ try: put = a.put - except AttributeError: + except AttributeError as e: raise TypeError("argument 1 must be numpy.ndarray, " - "not {name}".format(name=type(a).__name__)) + "not {name}".format(name=type(a).__name__)) from e return put(ind, v, mode=mode) +def _swapaxes_dispatcher(a, axis1, axis2): + return (a,) + + +@array_function_dispatch(_swapaxes_dispatcher) def swapaxes(a, axis1, axis2): """ Interchange two axes of an array. @@ -549,17 +594,29 @@ def swapaxes(a, axis1, axis2): return _wrapfunc(a, 'swapaxes', axis1, axis2) +def _transpose_dispatcher(a, axes=None): + return (a,) + + +@array_function_dispatch(_transpose_dispatcher) def transpose(a, axes=None): """ - Permute the dimensions of an array. + Reverse or permute the axes of an array; returns the modified array. + + For an array a with two axes, transpose(a) gives the matrix transpose. + + Refer to `numpy.ndarray.transpose` for full documentation. Parameters ---------- a : array_like Input array. - axes : list of ints, optional - By default, reverse the dimensions, otherwise permute the axes - according to the values given. + axes : tuple or list of ints, optional + If specified, it must be a tuple or list which contains a permutation of + [0,1,..,N-1] where N is the number of axes of a. The i'th axis of the + returned array will correspond to the axis numbered ``axes[i]`` of the + input. If not specified, defaults to ``range(a.ndim)[::-1]``, which + reverses the order of the axes. Returns ------- @@ -569,6 +626,7 @@ def transpose(a, axes=None): See Also -------- + ndarray.transpose : Equivalent method moveaxis argsort @@ -594,10 +652,19 @@ def transpose(a, axes=None): >>> np.transpose(x, (1, 0, 2)).shape (2, 1, 3) + >>> x = np.ones((2, 3, 4, 5)) + >>> np.transpose(x).shape + (5, 4, 3, 2) + """ return _wrapfunc(a, 'transpose', axes) +def _partition_dispatcher(a, kth, axis=None, kind=None, order=None): + return (a,) + + +@array_function_dispatch(_partition_dispatcher) def partition(a, kth, axis=-1, kind='introselect', order=None): """ Return a partitioned copy of an array. @@ -622,6 +689,9 @@ def partition(a, kth, axis=-1, kind='introselect', order=None): it. The order of all elements in the partitions is undefined. If provided with a sequence of k-th it will partition all elements indexed by k-th of them into their sorted position at once. + + .. deprecated:: 1.22.0 + Passing booleans as index is deprecated. axis : int or None, optional Axis along which to sort. If None, the array is flattened before sorting. The default is -1, which sorts along the last axis. @@ -689,6 +759,11 @@ def partition(a, kth, axis=-1, kind='introselect', order=None): return a +def _argpartition_dispatcher(a, kth, axis=None, kind=None, order=None): + return (a,) + + +@array_function_dispatch(_argpartition_dispatcher) def argpartition(a, kth, axis=-1, kind='introselect', order=None): """ Perform an indirect partition along the given axis using the @@ -709,6 +784,9 @@ def argpartition(a, kth, axis=-1, kind='introselect', order=None): elements in the partitions is undefined. If provided with a sequence of k-th it will partition all of them into their sorted position at once. + + .. deprecated:: 1.22.0 + Passing booleans as index is deprecated. axis : int or None, optional Axis along which to sort. The default is -1 (the last axis). If None, the flattened array is used. @@ -733,7 +811,9 @@ def argpartition(a, kth, axis=-1, kind='introselect', order=None): -------- partition : Describes partition algorithms used. ndarray.partition : Inplace partition. - argsort : Full indirect sort + argsort : Full indirect sort. + take_along_axis : Apply ``index_array`` from argpartition + to an array as if by calling partition. Notes ----- @@ -753,11 +833,24 @@ def argpartition(a, kth, axis=-1, kind='introselect', order=None): >>> np.array(x)[np.argpartition(x, 3)] array([2, 1, 3, 4]) + Multi-dimensional array: + + >>> x = np.array([[3, 4, 2], [1, 3, 1]]) + >>> index_array = np.argpartition(x, kth=1, axis=-1) + >>> np.take_along_axis(x, index_array, axis=-1) # same as np.partition(x, kth=1) + array([[2, 3, 4], + [1, 1, 3]]) + """ return _wrapfunc(a, 'argpartition', kth, axis=axis, kind=kind, order=order) -def sort(a, axis=-1, kind='quicksort', order=None): +def _sort_dispatcher(a, axis=None, kind=None, order=None): + return (a,) + + +@array_function_dispatch(_sort_dispatcher) +def sort(a, axis=-1, kind=None, order=None): """ Return a sorted copy of an array. @@ -769,7 +862,14 @@ def sort(a, axis=-1, kind='quicksort', order=None): Axis along which to sort. If None, the array is flattened before sorting. The default is -1, which sorts along the last axis. kind : {'quicksort', 'mergesort', 'heapsort', 'stable'}, optional - Sorting algorithm. Default is 'quicksort'. + Sorting algorithm. The default is 'quicksort'. Note that both 'stable' + and 'mergesort' use timsort or radix sort under the covers and, in general, + the actual implementation will vary with data type. The 'mergesort' option + is retained for backwards compatibility. + + .. versionchanged:: 1.15.0. + The 'stable' option was added. + order : str or list of str, optional When `a` is an array with fields defined, this argument specifies which fields to compare first, second, etc. A single field can @@ -795,17 +895,22 @@ def sort(a, axis=-1, kind='quicksort', order=None): The various sorting algorithms are characterized by their average speed, worst case performance, work space size, and whether they are stable. A stable sort keeps items with the same key in the same relative - order. The three available algorithms have the following + order. The four algorithms implemented in NumPy have the following properties: =========== ======= ============= ============ ======== kind speed worst case work space stable =========== ======= ============= ============ ======== 'quicksort' 1 O(n^2) 0 no - 'mergesort' 2 O(n*log(n)) ~n/2 yes 'heapsort' 3 O(n*log(n)) 0 no + 'mergesort' 2 O(n*log(n)) ~n/2 yes + 'timsort' 2 O(n*log(n)) ~n/2 yes =========== ======= ============= ============ ======== + .. note:: The datatype determines which of 'mergesort' or 'timsort' + is actually used, even if 'mergesort' is specified. User selection + at a finer scale is not currently available. + All the sort algorithms make temporary copies of the data when sorting along any but the last axis. Consequently, sorting along the last axis is faster and uses less space than sorting along @@ -829,13 +934,34 @@ def sort(a, axis=-1, kind='quicksort', order=None): .. versionadded:: 1.12.0 - quicksort has been changed to an introsort which will switch - heapsort when it does not make enough progress. This makes its - worst case O(n*log(n)). + quicksort has been changed to `introsort <https://en.wikipedia.org/wiki/Introsort>`_. + When sorting does not make enough progress it switches to + `heapsort <https://en.wikipedia.org/wiki/Heapsort>`_. + This implementation makes quicksort O(n*log(n)) in the worst case. + + 'stable' automatically chooses the best stable sorting algorithm + for the data type being sorted. + It, along with 'mergesort' is currently mapped to + `timsort <https://en.wikipedia.org/wiki/Timsort>`_ + or `radix sort <https://en.wikipedia.org/wiki/Radix_sort>`_ + depending on the data type. + API forward compatibility currently limits the + ability to select the implementation and it is hardwired for the different + data types. + + .. versionadded:: 1.17.0 + + Timsort is added for better performance on already or nearly + sorted data. On random data timsort is almost identical to + mergesort. It is now used for stable sort while quicksort is still the + default sort if none is chosen. For timsort details, refer to + `CPython listsort.txt <https://github.com/python/cpython/blob/3.7/Objects/listsort.txt>`_. + 'mergesort' and 'stable' are mapped to radix sort for integer data types. Radix sort is an + O(n) sort instead of O(n log n). + + .. versionchanged:: 1.18.0 - 'stable' automatically choses the best stable sorting algorithm - for the data type being sorted. It is currently mapped to - merge sort. + NaT now sorts to the end of arrays for consistency with NaN. Examples -------- @@ -879,7 +1005,12 @@ def sort(a, axis=-1, kind='quicksort', order=None): return a -def argsort(a, axis=-1, kind='quicksort', order=None): +def _argsort_dispatcher(a, axis=None, kind=None, order=None): + return (a,) + + +@array_function_dispatch(_argsort_dispatcher) +def argsort(a, axis=-1, kind=None, order=None): """ Returns the indices that would sort an array. @@ -895,7 +1026,13 @@ def argsort(a, axis=-1, kind='quicksort', order=None): Axis along which to sort. The default is -1 (the last axis). If None, the flattened array is used. kind : {'quicksort', 'mergesort', 'heapsort', 'stable'}, optional - Sorting algorithm. + Sorting algorithm. The default is 'quicksort'. Note that both 'stable' + and 'mergesort' use timsort under the covers and, in general, the + actual implementation will vary with data type. The 'mergesort' option + is retained for backwards compatibility. + + .. versionchanged:: 1.15.0. + The 'stable' option was added. order : str or list of str, optional When `a` is an array with fields defined, this argument specifies which fields to compare first, second, etc. A single field can @@ -906,10 +1043,10 @@ def argsort(a, axis=-1, kind='quicksort', order=None): Returns ------- index_array : ndarray, int - Array of indices that sort `a` along the specified axis. + Array of indices that sort `a` along the specified `axis`. If `a` is one-dimensional, ``a[index_array]`` yields a sorted `a`. - More generally, ``np.take_along_axis(a, index_array, axis=a)`` always - yields the sorted `a`, irrespective of dimensionality. + More generally, ``np.take_along_axis(a, index_array, axis=axis)`` + always yields the sorted `a`, irrespective of dimensionality. See Also -------- @@ -917,6 +1054,8 @@ def argsort(a, axis=-1, kind='quicksort', order=None): lexsort : Indirect stable sort with multiple keys. ndarray.sort : Inplace sort. argpartition : Indirect partial sort. + take_along_axis : Apply ``index_array`` from argsort + to an array as if by calling sort. Notes ----- @@ -940,13 +1079,21 @@ def argsort(a, axis=-1, kind='quicksort', order=None): array([[0, 3], [2, 2]]) - >>> np.argsort(x, axis=0) # sorts along first axis (down) + >>> ind = np.argsort(x, axis=0) # sorts along first axis (down) + >>> ind array([[0, 1], [1, 0]]) + >>> np.take_along_axis(x, ind, axis=0) # same as np.sort(x, axis=0) + array([[0, 2], + [2, 3]]) - >>> np.argsort(x, axis=1) # sorts along last axis (across) + >>> ind = np.argsort(x, axis=1) # sorts along last axis (across) + >>> ind array([[0, 1], [0, 1]]) + >>> np.take_along_axis(x, ind, axis=1) # same as np.sort(x, axis=1) + array([[0, 3], + [2, 2]]) Indices of the sorted elements of a N-dimensional array: @@ -973,7 +1120,12 @@ def argsort(a, axis=-1, kind='quicksort', order=None): return _wrapfunc(a, 'argsort', axis=axis, kind=kind, order=order) -def argmax(a, axis=None, out=None): +def _argmax_dispatcher(a, axis=None, out=None, *, keepdims=np._NoValue): + return (a, out) + + +@array_function_dispatch(_argmax_dispatcher) +def argmax(a, axis=None, out=None, *, keepdims=np._NoValue): """ Returns the indices of the maximum values along an axis. @@ -987,18 +1139,28 @@ def argmax(a, axis=None, out=None): out : array, optional If provided, the result will be inserted into this array. It should be of the appropriate shape and dtype. + keepdims : bool, optional + If this is set to True, the axes which are reduced are left + in the result as dimensions with size one. With this option, + the result will broadcast correctly against the array. + + .. versionadded:: 1.22.0 Returns ------- index_array : ndarray of ints Array of indices into the array. It has the same shape as `a.shape` - with the dimension along `axis` removed. + with the dimension along `axis` removed. If `keepdims` is set to True, + then the size of `axis` will be 1 with the resulting array having same + shape as `a.shape`. See Also -------- ndarray.argmax, argmin amax : The maximum value along a given axis. unravel_index : Convert a flat index into an index tuple. + take_along_axis : Apply ``np.expand_dims(index_array, axis)`` + from argmax to an array as if by calling max. Notes ----- @@ -1007,10 +1169,10 @@ def argmax(a, axis=None, out=None): Examples -------- - >>> a = np.arange(6).reshape(2,3) + >>> a = np.arange(6).reshape(2,3) + 10 >>> a - array([[0, 1, 2], - [3, 4, 5]]) + array([[10, 11, 12], + [13, 14, 15]]) >>> np.argmax(a) 5 >>> np.argmax(a, axis=0) @@ -1024,7 +1186,7 @@ def argmax(a, axis=None, out=None): >>> ind (1, 2) >>> a[ind] - 5 + 15 >>> b = np.arange(6) >>> b[1] = 5 @@ -1033,11 +1195,33 @@ def argmax(a, axis=None, out=None): >>> np.argmax(b) # Only the first occurrence is returned. 1 + >>> x = np.array([[4,2,3], [1,0,3]]) + >>> index_array = np.argmax(x, axis=-1) + >>> # Same as np.amax(x, axis=-1, keepdims=True) + >>> np.take_along_axis(x, np.expand_dims(index_array, axis=-1), axis=-1) + array([[4], + [3]]) + >>> # Same as np.amax(x, axis=-1) + >>> np.take_along_axis(x, np.expand_dims(index_array, axis=-1), axis=-1).squeeze(axis=-1) + array([4, 3]) + + Setting `keepdims` to `True`, + + >>> x = np.arange(24).reshape((2, 3, 4)) + >>> res = np.argmax(x, axis=1, keepdims=True) + >>> res.shape + (2, 1, 4) """ - return _wrapfunc(a, 'argmax', axis=axis, out=out) + kwds = {'keepdims': keepdims} if keepdims is not np._NoValue else {} + return _wrapfunc(a, 'argmax', axis=axis, out=out, **kwds) + +def _argmin_dispatcher(a, axis=None, out=None, *, keepdims=np._NoValue): + return (a, out) -def argmin(a, axis=None, out=None): + +@array_function_dispatch(_argmin_dispatcher) +def argmin(a, axis=None, out=None, *, keepdims=np._NoValue): """ Returns the indices of the minimum values along an axis. @@ -1051,18 +1235,28 @@ def argmin(a, axis=None, out=None): out : array, optional If provided, the result will be inserted into this array. It should be of the appropriate shape and dtype. + keepdims : bool, optional + If this is set to True, the axes which are reduced are left + in the result as dimensions with size one. With this option, + the result will broadcast correctly against the array. + + .. versionadded:: 1.22.0 Returns ------- index_array : ndarray of ints Array of indices into the array. It has the same shape as `a.shape` - with the dimension along `axis` removed. + with the dimension along `axis` removed. If `keepdims` is set to True, + then the size of `axis` will be 1 with the resulting array having same + shape as `a.shape`. See Also -------- ndarray.argmin, argmax amin : The minimum value along a given axis. unravel_index : Convert a flat index into an index tuple. + take_along_axis : Apply ``np.expand_dims(index_array, axis)`` + from argmin to an array as if by calling min. Notes ----- @@ -1071,10 +1265,10 @@ def argmin(a, axis=None, out=None): Examples -------- - >>> a = np.arange(6).reshape(2,3) + >>> a = np.arange(6).reshape(2,3) + 10 >>> a - array([[0, 1, 2], - [3, 4, 5]]) + array([[10, 11, 12], + [13, 14, 15]]) >>> np.argmin(a) 0 >>> np.argmin(a, axis=0) @@ -1088,19 +1282,41 @@ def argmin(a, axis=None, out=None): >>> ind (0, 0) >>> a[ind] - 0 + 10 - >>> b = np.arange(6) - >>> b[4] = 0 + >>> b = np.arange(6) + 10 + >>> b[4] = 10 >>> b - array([0, 1, 2, 3, 0, 5]) + array([10, 11, 12, 13, 10, 15]) >>> np.argmin(b) # Only the first occurrence is returned. 0 + >>> x = np.array([[4,2,3], [1,0,3]]) + >>> index_array = np.argmin(x, axis=-1) + >>> # Same as np.amin(x, axis=-1, keepdims=True) + >>> np.take_along_axis(x, np.expand_dims(index_array, axis=-1), axis=-1) + array([[2], + [0]]) + >>> # Same as np.amax(x, axis=-1) + >>> np.take_along_axis(x, np.expand_dims(index_array, axis=-1), axis=-1).squeeze(axis=-1) + array([2, 0]) + + Setting `keepdims` to `True`, + + >>> x = np.arange(24).reshape((2, 3, 4)) + >>> res = np.argmin(x, axis=1, keepdims=True) + >>> res.shape + (2, 1, 4) """ - return _wrapfunc(a, 'argmin', axis=axis, out=out) + kwds = {'keepdims': keepdims} if keepdims is not np._NoValue else {} + return _wrapfunc(a, 'argmin', axis=axis, out=out, **kwds) + + +def _searchsorted_dispatcher(a, v, side=None, sorter=None): + return (a, v, sorter) +@array_function_dispatch(_searchsorted_dispatcher) def searchsorted(a, v, side='left', sorter=None): """ Find indices where elements should be inserted to maintain order. @@ -1138,8 +1354,9 @@ def searchsorted(a, v, side='left', sorter=None): Returns ------- - indices : array of ints - Array of insertion points with the same shape as `v`. + indices : int or array of ints + Array of insertion points with the same shape as `v`, + or an integer if `v` is a scalar. See Also -------- @@ -1153,7 +1370,7 @@ def searchsorted(a, v, side='left', sorter=None): As of NumPy 1.4.0 `searchsorted` works with real/complex arrays containing `nan` values. The enhanced sort order is documented in `sort`. - This function is a faster version of the builtin python `bisect.bisect_left` + This function uses the same algorithm as the builtin python `bisect.bisect_left` (``side='left'``) and `bisect.bisect_right` (``side='right'``) functions, which is also vectorized in the `v` argument. @@ -1170,6 +1387,11 @@ def searchsorted(a, v, side='left', sorter=None): return _wrapfunc(a, 'searchsorted', v, side=side, sorter=sorter) +def _resize_dispatcher(a, new_shape): + return (a,) + + +@array_function_dispatch(_resize_dispatcher) def resize(a, new_shape): """ Return a new array with the specified shape. @@ -1192,12 +1414,29 @@ def resize(a, new_shape): reshaped_array : ndarray The new array is formed from the data in the old array, repeated if necessary to fill out the required number of elements. The - data are repeated in the order that they are stored in memory. + data are repeated iterating over the array in C-order. See Also -------- + numpy.reshape : Reshape an array without changing the total size. + numpy.pad : Enlarge and pad an array. + numpy.repeat : Repeat elements of an array. ndarray.resize : resize an array in-place. + Notes + ----- + When the total size of the array does not change `~numpy.reshape` should + be used. In most other cases either indexing (to reduce the size) + or padding (to increase the size) may be a more appropriate solution. + + Warning: This functionality does **not** consider axes separately, + i.e. it does not apply interpolation/extrapolation. + It fills the return array with the required number of elements, iterating + over `a` in C-order, disregarding axes (and cycling back from the start if + the new shape is larger). This functionality is therefore not suitable to + resize images, or data where each axis represents a separate and distinct + entity. + Examples -------- >>> a=np.array([[0,1],[2,3]]) @@ -1213,29 +1452,33 @@ def resize(a, new_shape): """ if isinstance(new_shape, (int, nt.integer)): new_shape = (new_shape,) + a = ravel(a) - Na = len(a) - total_size = um.multiply.reduce(new_shape) - if Na == 0 or total_size == 0: - return mu.zeros(new_shape, a.dtype) - n_copies = int(total_size / Na) - extra = total_size % Na + new_size = 1 + for dim_length in new_shape: + new_size *= dim_length + if dim_length < 0: + raise ValueError('all elements of `new_shape` must be non-negative') - if extra != 0: - n_copies = n_copies + 1 - extra = Na - extra + if a.size == 0 or new_size == 0: + # First case must zero fill. The second would have repeats == 0. + return np.zeros_like(a, shape=new_shape) - a = concatenate((a,) * n_copies) - if extra > 0: - a = a[:-extra] + repeats = -(-new_size // a.size) # ceil division + a = concatenate((a,) * repeats)[:new_size] return reshape(a, new_shape) +def _squeeze_dispatcher(a, axis=None): + return (a,) + + +@array_function_dispatch(_squeeze_dispatcher) def squeeze(a, axis=None): """ - Remove single-dimensional entries from the shape of an array. + Remove axes of length one from `a`. Parameters ---------- @@ -1244,7 +1487,7 @@ def squeeze(a, axis=None): axis : None or int or tuple of ints, optional .. versionadded:: 1.7.0 - Selects a subset of the single-dimensional entries in the + Selects a subset of the entries of length one in the shape. If an axis is selected with shape entry greater than one, an error is raised. @@ -1253,16 +1496,17 @@ def squeeze(a, axis=None): squeezed : ndarray The input array, but with all or a subset of the dimensions of length 1 removed. This is always `a` itself - or a view into `a`. + or a view into `a`. Note that if all axes are squeezed, + the result is a 0d array and not a scalar. Raises ------ ValueError - If `axis` is not `None`, and an axis being squeezed is not of length 1 + If `axis` is not None, and an axis being squeezed is not of length 1 See Also -------- - expand_dims : The inverse operation, adding singleton dimensions + expand_dims : The inverse operation, adding entries of length one reshape : Insert, remove, and combine dimensions, and resize existing ones Examples @@ -1280,17 +1524,32 @@ def squeeze(a, axis=None): ValueError: cannot select an axis to squeeze out which has size not equal to one >>> np.squeeze(x, axis=2).shape (1, 3) + >>> x = np.array([[1234]]) + >>> x.shape + (1, 1) + >>> np.squeeze(x) + array(1234) # 0d array + >>> np.squeeze(x).shape + () + >>> np.squeeze(x)[()] + 1234 """ try: squeeze = a.squeeze except AttributeError: - return _wrapit(a, 'squeeze') + return _wrapit(a, 'squeeze', axis=axis) if axis is None: return squeeze() else: return squeeze(axis=axis) + +def _diagonal_dispatcher(a, offset=None, axis1=None, axis2=None): + return (a,) + + +@array_function_dispatch(_diagonal_dispatcher) def diagonal(a, offset=0, axis1=0, axis2=1): """ Return specified diagonals. @@ -1346,7 +1605,7 @@ def diagonal(a, offset=0, axis1=0, axis2=1): same type as `a` is returned unless `a` is a `matrix`, in which case a 1-D array rather than a (2-D) `matrix` is returned in order to maintain backward compatibility. - + If ``a.ndim > 2``, then the dimensions specified by `axis1` and `axis2` are removed, and a new axis inserted at the end corresponding to the diagonal. @@ -1380,9 +1639,9 @@ def diagonal(a, offset=0, axis1=0, axis2=1): [2, 3]], [[4, 5], [6, 7]]]) - >>> a.diagonal(0, # Main diagonals of two arrays created by skipping - ... 0, # across the outer(left)-most axis last and - ... 1) # the "middle" (row) axis first. + >>> a.diagonal(0, # Main diagonals of two arrays created by skipping + ... 0, # across the outer(left)-most axis last and + ... 1) # the "middle" (row) axis first. array([[0, 6], [1, 7]]) @@ -1390,13 +1649,28 @@ def diagonal(a, offset=0, axis1=0, axis2=1): corresponds to fixing the right-most (column) axis, and that the diagonals are "packed" in rows. - >>> a[:,:,0] # main diagonal is [0 6] + >>> a[:,:,0] # main diagonal is [0 6] array([[0, 2], [4, 6]]) - >>> a[:,:,1] # main diagonal is [1 7] + >>> a[:,:,1] # main diagonal is [1 7] array([[1, 3], [5, 7]]) + The anti-diagonal can be obtained by reversing the order of elements + using either `numpy.flipud` or `numpy.fliplr`. + + >>> a = np.arange(9).reshape(3, 3) + >>> a + array([[0, 1, 2], + [3, 4, 5], + [6, 7, 8]]) + >>> np.fliplr(a).diagonal() # Horizontal flip + array([2, 4, 6]) + >>> np.flipud(a).diagonal() # Vertical flip + array([6, 4, 2]) + + Note that the order in which the diagonal is retrieved varies depending + on the flip function. """ if isinstance(a, np.matrix): # Make diagonal of matrix 1-D to preserve backward compatibility. @@ -1405,6 +1679,12 @@ def diagonal(a, offset=0, axis1=0, axis2=1): return asanyarray(a).diagonal(offset=offset, axis1=axis1, axis2=axis2) +def _trace_dispatcher( + a, offset=None, axis1=None, axis2=None, dtype=None, out=None): + return (a, out) + + +@array_function_dispatch(_trace_dispatcher) def trace(a, offset=0, axis1=0, axis2=1, dtype=None, out=None): """ Return the sum along diagonals of the array. @@ -1468,6 +1748,11 @@ def trace(a, offset=0, axis1=0, axis2=1, dtype=None, out=None): return asanyarray(a).trace(offset=offset, axis1=axis1, axis2=axis2, dtype=dtype, out=out) +def _ravel_dispatcher(a, order=None): + return (a,) + + +@array_function_dispatch(_ravel_dispatcher) def ravel(a, order='C'): """Return a contiguous flattened array. @@ -1531,21 +1816,21 @@ def ravel(a, order='C'): It is equivalent to ``reshape(-1, order=order)``. >>> x = np.array([[1, 2, 3], [4, 5, 6]]) - >>> print(np.ravel(x)) - [1 2 3 4 5 6] + >>> np.ravel(x) + array([1, 2, 3, 4, 5, 6]) - >>> print(x.reshape(-1)) - [1 2 3 4 5 6] + >>> x.reshape(-1) + array([1, 2, 3, 4, 5, 6]) - >>> print(np.ravel(x, order='F')) - [1 4 2 5 3 6] + >>> np.ravel(x, order='F') + array([1, 4, 2, 5, 3, 6]) When ``order`` is 'A', it will preserve the array's 'C' or 'F' ordering: - >>> print(np.ravel(x.T)) - [1 4 2 5 3 6] - >>> print(np.ravel(x.T, order='A')) - [1 2 3 4 5 6] + >>> np.ravel(x.T) + array([1, 4, 2, 5, 3, 6]) + >>> np.ravel(x.T, order='A') + array([1, 2, 3, 4, 5, 6]) When ``order`` is 'K', it will preserve orderings that are neither 'C' nor 'F', but won't reverse axes: @@ -1574,6 +1859,11 @@ def ravel(a, order='C'): return asanyarray(a).ravel(order=order) +def _nonzero_dispatcher(a): + return (a,) + + +@array_function_dispatch(_nonzero_dispatcher) def nonzero(a): """ Return the indices of the elements that are non-zero. @@ -1581,17 +1871,19 @@ def nonzero(a): Returns a tuple of arrays, one for each dimension of `a`, containing the indices of the non-zero elements in that dimension. The values in `a` are always tested and returned in - row-major, C-style order. The corresponding non-zero - values can be obtained with:: + row-major, C-style order. - a[nonzero(a)] + To group the indices by element, rather than dimension, use `argwhere`, + which returns a row for each non-zero element. - To group the indices by element, rather than dimension, use:: + .. note:: - transpose(nonzero(a)) + When called on a zero-d array or scalar, ``nonzero(a)`` is treated + as ``nonzero(atleast_1d(a))``. - The result of this is always a 2-D array, with a row for - each non-zero element. + .. deprecated:: 1.17.0 + + Use `atleast_1d` explicitly if this behavior is deliberate. Parameters ---------- @@ -1613,30 +1905,36 @@ def nonzero(a): count_nonzero : Counts the number of non-zero elements in the input array. + Notes + ----- + While the nonzero values can be obtained with ``a[nonzero(a)]``, it is + recommended to use ``x[x.astype(bool)]`` or ``x[x != 0]`` instead, which + will correctly handle 0-d arrays. + Examples -------- - >>> x = np.array([[1,0,0], [0,2,0], [1,1,0]]) + >>> x = np.array([[3, 0, 0], [0, 4, 0], [5, 6, 0]]) >>> x - array([[1, 0, 0], - [0, 2, 0], - [1, 1, 0]]) + array([[3, 0, 0], + [0, 4, 0], + [5, 6, 0]]) >>> np.nonzero(x) (array([0, 1, 2, 2]), array([0, 1, 0, 1])) >>> x[np.nonzero(x)] - array([1, 2, 1, 1]) + array([3, 4, 5, 6]) >>> np.transpose(np.nonzero(x)) array([[0, 0], [1, 1], [2, 0], - [2, 1]) + [2, 1]]) A common use for ``nonzero`` is to find the indices of an array, where a condition is True. Given an array `a`, the condition `a` > 3 is a boolean array and since False is interpreted as 0, np.nonzero(a > 3) yields the indices of the `a` where the condition is true. - >>> a = np.array([[1,2,3],[4,5,6],[7,8,9]]) + >>> a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) >>> a > 3 array([[False, False, False], [ True, True, True], @@ -1644,7 +1942,14 @@ def nonzero(a): >>> np.nonzero(a > 3) (array([1, 1, 1, 2, 2, 2]), array([0, 1, 2, 0, 1, 2])) - The ``nonzero`` method of the boolean array can also be called. + Using this result to index `a` is equivalent to using the mask directly: + + >>> a[np.nonzero(a > 3)] + array([4, 5, 6, 7, 8, 9]) + >>> a[a > 3] # prefer this spelling + array([4, 5, 6, 7, 8, 9]) + + ``nonzero`` can also be called as a method of the array. >>> (a > 3).nonzero() (array([1, 1, 1, 2, 2, 2]), array([0, 1, 2, 0, 1, 2])) @@ -1653,6 +1958,11 @@ def nonzero(a): return _wrapfunc(a, 'nonzero') +def _shape_dispatcher(a): + return (a,) + + +@array_function_dispatch(_shape_dispatcher) def shape(a): """ Return the shape of an array. @@ -1670,7 +1980,7 @@ def shape(a): See Also -------- - alen + len ndarray.shape : Equivalent array method. Examples @@ -1698,6 +2008,11 @@ def shape(a): return result +def _compress_dispatcher(condition, a, axis=None, out=None): + return (condition, a, out) + + +@array_function_dispatch(_compress_dispatcher) def compress(condition, a, axis=None, out=None): """ Return selected slices of an array along given axis. @@ -1731,8 +2046,8 @@ def compress(condition, a, axis=None, out=None): -------- take, choose, diag, diagonal, select ndarray.compress : Equivalent method in ndarray - np.extract: Equivalent method when working on 1-D arrays - numpy.doc.ufuncs : Section "Output arguments" + extract : Equivalent method when working on 1-D arrays + :ref:`ufuncs-output-type` Examples -------- @@ -1761,7 +2076,12 @@ def compress(condition, a, axis=None, out=None): return _wrapfunc(a, 'compress', condition, axis=axis, out=out) -def clip(a, a_min, a_max, out=None): +def _clip_dispatcher(a, a_min, a_max, out=None, **kwargs): + return (a, a_min, a_max) + + +@array_function_dispatch(_clip_dispatcher) +def clip(a, a_min, a_max, out=None, **kwargs): """ Clip (limit) the values in an array. @@ -1770,23 +2090,27 @@ def clip(a, a_min, a_max, out=None): is specified, values smaller than 0 become 0, and values larger than 1 become 1. + Equivalent to but faster than ``np.minimum(a_max, np.maximum(a, a_min))``. + + No check is performed to ensure ``a_min < a_max``. + Parameters ---------- a : array_like Array containing elements to clip. - a_min : scalar or array_like or `None` - Minimum value. If `None`, clipping is not performed on lower - interval edge. Not more than one of `a_min` and `a_max` may be - `None`. - a_max : scalar or array_like or `None` - Maximum value. If `None`, clipping is not performed on upper - interval edge. Not more than one of `a_min` and `a_max` may be - `None`. If `a_min` or `a_max` are array_like, then the three - arrays will be broadcasted to match their shapes. + a_min, a_max : array_like or None + Minimum and maximum value. If ``None``, clipping is not performed on + the corresponding edge. Only one of `a_min` and `a_max` may be + ``None``. Both are broadcast against `a`. out : ndarray, optional The results will be placed in this array. It may be the input array for in-place clipping. `out` must be of the right shape to hold the output. Its type is preserved. + **kwargs + For other keyword-only arguments, see the + :ref:`ufunc docs <ufuncs.kwargs>`. + + .. versionadded:: 1.17.0 Returns ------- @@ -1797,17 +2121,27 @@ def clip(a, a_min, a_max, out=None): See Also -------- - numpy.doc.ufuncs : Section "Output arguments" + :ref:`ufuncs-output-type` + + Notes + ----- + When `a_min` is greater than `a_max`, `clip` returns an + array in which all values are equal to `a_max`, + as shown in the second example. Examples -------- >>> a = np.arange(10) - >>> np.clip(a, 1, 8) - array([1, 1, 2, 3, 4, 5, 6, 7, 8, 8]) >>> a array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) + >>> np.clip(a, 1, 8) + array([1, 1, 2, 3, 4, 5, 6, 7, 8, 8]) + >>> np.clip(a, 8, 1) + array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1]) >>> np.clip(a, 3, 6, out=a) array([3, 3, 3, 3, 4, 5, 6, 6, 6, 6]) + >>> a + array([3, 3, 3, 3, 4, 5, 6, 6, 6, 6]) >>> a = np.arange(10) >>> a array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) @@ -1815,10 +2149,17 @@ def clip(a, a_min, a_max, out=None): array([3, 4, 2, 3, 4, 5, 6, 7, 8, 8]) """ - return _wrapfunc(a, 'clip', a_min, a_max, out=out) + return _wrapfunc(a, 'clip', a_min, a_max, out=out, **kwargs) + + +def _sum_dispatcher(a, axis=None, dtype=None, out=None, keepdims=None, + initial=None, where=None): + return (a, out) -def sum(a, axis=None, dtype=None, out=None, keepdims=np._NoValue, initial=np._NoValue): +@array_function_dispatch(_sum_dispatcher) +def sum(a, axis=None, dtype=None, out=None, keepdims=np._NoValue, + initial=np._NoValue, where=np._NoValue): """ Sum of array elements over a given axis. @@ -1862,6 +2203,11 @@ def sum(a, axis=None, dtype=None, out=None, keepdims=np._NoValue, initial=np._No .. versionadded:: 1.15.0 + where : array_like of bool, optional + Elements to include in the sum. See `~numpy.ufunc.reduce` for details. + + .. versionadded:: 1.17.0 + Returns ------- sum_along_axis : ndarray @@ -1874,6 +2220,8 @@ def sum(a, axis=None, dtype=None, out=None, keepdims=np._NoValue, initial=np._No -------- ndarray.sum : Equivalent method. + add.reduce : Equivalent functionality of `add`. + cumsum : Cumulative sum of array elements. trapz : Integration of array values using the composite trapezoidal rule. @@ -1890,6 +2238,23 @@ def sum(a, axis=None, dtype=None, out=None, keepdims=np._NoValue, initial=np._No >>> np.sum([]) 0.0 + For floating point numbers the numerical precision of sum (and + ``np.add.reduce``) is in general limited by directly adding each number + individually to the result causing rounding errors in every step. + However, often numpy will use a numerically better approach (partial + pairwise summation) leading to improved precision in many use-cases. + This improved precision is always provided when no ``axis`` is given. + When ``axis`` is given, it will depend on which axis is summed. + Technically, to provide the best speed possible, the improved precision + is only used when the summation is along the fast axis in memory. + Note that the exact precision may vary depending on other parameters. + In contrast to NumPy, Python's ``math.fsum`` function uses a slower but + more precise approach to summation. + Especially when summing a large number of lower precision floating point + numbers, such as ``float32``, numerical errors can become significant. + In such cases it can be advisable to use `dtype="float64"` to use a higher + precision for the output. + Examples -------- >>> np.sum([0.5, 1.5]) @@ -1902,6 +2267,8 @@ def sum(a, axis=None, dtype=None, out=None, keepdims=np._NoValue, initial=np._No array([0, 6]) >>> np.sum([[0, 1], [0, 5]], axis=1) array([1, 5]) + >>> np.sum([[0, 1], [np.nan, 5]], where=[False, True], axis=1) + array([1., 5.]) If the accumulator is too small, overflow occurs: @@ -1917,8 +2284,8 @@ def sum(a, axis=None, dtype=None, out=None, keepdims=np._NoValue, initial=np._No # 2018-02-25, 1.15.0 warnings.warn( "Calling np.sum(generator) is deprecated, and in the future will give a different result. " - "Use np.sum(np.from_iter(generator)) or the python sum builtin instead.", - DeprecationWarning, stacklevel=2) + "Use np.sum(np.fromiter(generator)) or the python sum builtin instead.", + DeprecationWarning, stacklevel=3) res = _sum_(a) if out is not None: @@ -1927,10 +2294,16 @@ def sum(a, axis=None, dtype=None, out=None, keepdims=np._NoValue, initial=np._No return res return _wrapreduction(a, np.add, 'sum', axis, dtype, out, keepdims=keepdims, - initial=initial) + initial=initial, where=where) -def any(a, axis=None, out=None, keepdims=np._NoValue): +def _any_dispatcher(a, axis=None, out=None, keepdims=None, *, + where=np._NoValue): + return (a, where, out) + + +@array_function_dispatch(_any_dispatcher) +def any(a, axis=None, out=None, keepdims=np._NoValue, *, where=np._NoValue): """ Test whether any array element along a given axis evaluates to True. @@ -1942,7 +2315,7 @@ def any(a, axis=None, out=None, keepdims=np._NoValue): Input array or object that can be converted to an array. axis : None or int or tuple of ints, optional Axis or axes along which a logical OR reduction is performed. - The default (`axis` = `None`) is to perform a logical OR over all + The default (``axis=None``) is to perform a logical OR over all the dimensions of the input array. `axis` may be negative, in which case it counts from the last to the first axis. @@ -1955,7 +2328,7 @@ def any(a, axis=None, out=None, keepdims=np._NoValue): the same shape as the expected output and its type is preserved (e.g., if it is of type float, then it will remain so, returning 1.0 for True and 0.0 for False, regardless of the type of `a`). - See `doc.ufuncs` (Section "Output arguments") for details. + See :ref:`ufuncs-output-type` for more details. keepdims : bool, optional If this is set to True, the axes which are reduced are left @@ -1968,6 +2341,12 @@ def any(a, axis=None, out=None, keepdims=np._NoValue): sub-class' method does not implement `keepdims` any exceptions will be raised. + where : array_like of bool, optional + Elements to include in checking for any `True` values. + See `~numpy.ufunc.reduce` for details. + + .. versionadded:: 1.20.0 + Returns ------- any : bool or ndarray @@ -1999,10 +2378,13 @@ def any(a, axis=None, out=None, keepdims=np._NoValue): >>> np.any(np.nan) True - >>> o=np.array([False]) + >>> np.any([[True, False], [False, False]], where=[[False], [True]]) + False + + >>> o=np.array(False) >>> z=np.any([-1, 4, 5], out=o) >>> z, o - (array([ True]), array([ True])) + (array(True), array(True)) >>> # Check now that z is a reference to o >>> z is o True @@ -2010,10 +2392,17 @@ def any(a, axis=None, out=None, keepdims=np._NoValue): (191614240, 191614240) """ - return _wrapreduction(a, np.logical_or, 'any', axis, None, out, keepdims=keepdims) + return _wrapreduction(a, np.logical_or, 'any', axis, None, out, + keepdims=keepdims, where=where) + +def _all_dispatcher(a, axis=None, out=None, keepdims=None, *, + where=None): + return (a, where, out) -def all(a, axis=None, out=None, keepdims=np._NoValue): + +@array_function_dispatch(_all_dispatcher) +def all(a, axis=None, out=None, keepdims=np._NoValue, *, where=np._NoValue): """ Test whether all array elements along a given axis evaluate to True. @@ -2023,7 +2412,7 @@ def all(a, axis=None, out=None, keepdims=np._NoValue): Input array or object that can be converted to an array. axis : None or int or tuple of ints, optional Axis or axes along which a logical AND reduction is performed. - The default (`axis` = `None`) is to perform a logical AND over all + The default (``axis=None``) is to perform a logical AND over all the dimensions of the input array. `axis` may be negative, in which case it counts from the last to the first axis. @@ -2035,8 +2424,8 @@ def all(a, axis=None, out=None, keepdims=np._NoValue): Alternate output array in which to place the result. It must have the same shape as the expected output and its type is preserved (e.g., if ``dtype(out)`` is float, the result - will consist of 0.0's and 1.0's). See `doc.ufuncs` (Section - "Output arguments") for more details. + will consist of 0.0's and 1.0's). See :ref:`ufuncs-output-type` for more + details. keepdims : bool, optional If this is set to True, the axes which are reduced are left @@ -2049,6 +2438,12 @@ def all(a, axis=None, out=None, keepdims=np._NoValue): sub-class' method does not implement `keepdims` any exceptions will be raised. + where : array_like of bool, optional + Elements to include in checking for all `True` values. + See `~numpy.ufunc.reduce` for details. + + .. versionadded:: 1.20.0 + Returns ------- all : ndarray, bool @@ -2080,15 +2475,24 @@ def all(a, axis=None, out=None, keepdims=np._NoValue): >>> np.all([1.0, np.nan]) True - >>> o=np.array([False]) + >>> np.all([[True, True], [False, True]], where=[[True], [False]]) + True + + >>> o=np.array(False) >>> z=np.all([-1, 4, 5], out=o) - >>> id(z), id(o), z # doctest: +SKIP - (28293632, 28293632, array([ True])) + >>> id(z), id(o), z + (28293632, 28293632, array(True)) # may vary """ - return _wrapreduction(a, np.logical_and, 'all', axis, None, out, keepdims=keepdims) + return _wrapreduction(a, np.logical_and, 'all', axis, None, out, + keepdims=keepdims, where=where) + + +def _cumsum_dispatcher(a, axis=None, dtype=None, out=None): + return (a, out) +@array_function_dispatch(_cumsum_dispatcher) def cumsum(a, axis=None, dtype=None, out=None): """ Return the cumulative sum of the elements along a given axis. @@ -2109,8 +2513,8 @@ def cumsum(a, axis=None, dtype=None, out=None): out : ndarray, optional Alternative output array in which to place the result. It must have the same shape and buffer length as the expected output - but the type will be cast if necessary. See `doc.ufuncs` - (Section "Output arguments") for more details. + but the type will be cast if necessary. See :ref:`ufuncs-output-type` for + more details. Returns ------- @@ -2120,20 +2524,21 @@ def cumsum(a, axis=None, dtype=None, out=None): result has the same size as `a`, and the same shape as `a` if `axis` is not None or `a` is a 1-d array. - See Also -------- sum : Sum array elements. - trapz : Integration of array values using the composite trapezoidal rule. - - diff : Calculate the n-th discrete difference along given axis. + diff : Calculate the n-th discrete difference along given axis. Notes ----- Arithmetic is modular when using integer types, and no error is raised on overflow. + ``cumsum(a)[-1]`` may not be equal to ``sum(a)`` for floating-point + values since ``sum`` may use a pairwise summation routine, reducing + the roundoff-error. See `sum` for more information. + Examples -------- >>> a = np.array([[1,2,3], [4,5,6]]) @@ -2152,16 +2557,37 @@ def cumsum(a, axis=None, dtype=None, out=None): array([[ 1, 3, 6], [ 4, 9, 15]]) + ``cumsum(b)[-1]`` may not be equal to ``sum(b)`` + + >>> b = np.array([1, 2e-9, 3e-9] * 1000000) + >>> b.cumsum()[-1] + 1000000.0050045159 + >>> b.sum() + 1000000.0050000029 + """ return _wrapfunc(a, 'cumsum', axis=axis, dtype=dtype, out=out) +def _ptp_dispatcher(a, axis=None, out=None, keepdims=None): + return (a, out) + + +@array_function_dispatch(_ptp_dispatcher) def ptp(a, axis=None, out=None, keepdims=np._NoValue): """ Range of values (maximum - minimum) along an axis. The name of the function comes from the acronym for 'peak to peak'. + .. warning:: + `ptp` preserves the data type of the array. This means the + return value for an input of signed integers with n bits + (e.g. `np.int8`, `np.int16`, etc) is also a signed integer + with n bits. In that case, peak-to-peak values greater than + ``2**(n-1)-1`` will be returned as negative values. An example + with a work-around is shown below. + Parameters ---------- a : array_like @@ -2199,16 +2625,33 @@ def ptp(a, axis=None, out=None, keepdims=np._NoValue): Examples -------- - >>> x = np.arange(4).reshape((2,2)) - >>> x - array([[0, 1], - [2, 3]]) + >>> x = np.array([[4, 9, 2, 10], + ... [6, 9, 7, 12]]) + + >>> np.ptp(x, axis=1) + array([8, 6]) >>> np.ptp(x, axis=0) - array([2, 2]) + array([2, 0, 5, 2]) - >>> np.ptp(x, axis=1) - array([1, 1]) + >>> np.ptp(x) + 10 + + This example shows that a negative value can be returned when + the input is an array of signed integers. + + >>> y = np.array([[1, 127], + ... [0, 127], + ... [-1, 127], + ... [-2, 127]], dtype=np.int8) + >>> np.ptp(y, axis=1) + array([ 126, 127, -128, -127], dtype=int8) + + A work-around is to use the `view()` method to view the result as + unsigned integers with the same bit width: + + >>> np.ptp(y, axis=1).view(np.uint8) + array([126, 127, 128, 129], dtype=uint8) """ kwargs = {} @@ -2224,7 +2667,14 @@ def ptp(a, axis=None, out=None, keepdims=np._NoValue): return _methods._ptp(a, axis=axis, out=out, **kwargs) -def amax(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue): +def _amax_dispatcher(a, axis=None, out=None, keepdims=None, initial=None, + where=None): + return (a, out) + + +@array_function_dispatch(_amax_dispatcher) +def amax(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue, + where=np._NoValue): """ Return the maximum of an array or maximum along an axis. @@ -2243,7 +2693,7 @@ def amax(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue): out : ndarray, optional Alternative output array in which to place the result. Must be of the same shape and buffer length as the expected output. - See `doc.ufuncs` (Section "Output arguments") for more details. + See :ref:`ufuncs-output-type` for more details. keepdims : bool, optional If this is set to True, the axes which are reduced are left @@ -2262,6 +2712,11 @@ def amax(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue): .. versionadded:: 1.15.0 + where : array_like of bool, optional + Elements to compare for the maximum. See `~numpy.ufunc.reduce` + for details. + + .. versionadded:: 1.17.0 Returns ------- @@ -2307,34 +2762,44 @@ def amax(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue): array([2, 3]) >>> np.amax(a, axis=1) # Maxima along the second axis array([1, 3]) - + >>> np.amax(a, where=[False, True], initial=-1, axis=0) + array([-1, 3]) >>> b = np.arange(5, dtype=float) >>> b[2] = np.NaN >>> np.amax(b) nan + >>> np.amax(b, where=~np.isnan(b), initial=-1) + 4.0 >>> np.nanmax(b) 4.0 You can use an initial value to compute the maximum of an empty slice, or to initialize it to a different value: - >>> np.max([[-50], [10]], axis=-1, initial=0) + >>> np.amax([[-50], [10]], axis=-1, initial=0) array([ 0, 10]) Notice that the initial value is used as one of the elements for which the maximum is determined, unlike for the default argument Python's max function, which is only used for empty iterables. - >>> np.max([5], initial=6) + >>> np.amax([5], initial=6) 6 >>> max([5], default=6) 5 """ - return _wrapreduction(a, np.maximum, 'max', axis, None, out, keepdims=keepdims, - initial=initial) + return _wrapreduction(a, np.maximum, 'max', axis, None, out, + keepdims=keepdims, initial=initial, where=where) + +def _amin_dispatcher(a, axis=None, out=None, keepdims=None, initial=None, + where=None): + return (a, out) -def amin(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue): + +@array_function_dispatch(_amin_dispatcher) +def amin(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue, + where=np._NoValue): """ Return the minimum of an array or minimum along an axis. @@ -2353,7 +2818,7 @@ def amin(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue): out : ndarray, optional Alternative output array in which to place the result. Must be of the same shape and buffer length as the expected output. - See `doc.ufuncs` (Section "Output arguments") for more details. + See :ref:`ufuncs-output-type` for more details. keepdims : bool, optional If this is set to True, the axes which are reduced are left @@ -2372,6 +2837,12 @@ def amin(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue): .. versionadded:: 1.15.0 + where : array_like of bool, optional + Elements to compare for the minimum. See `~numpy.ufunc.reduce` + for details. + + .. versionadded:: 1.17.0 + Returns ------- amin : ndarray or scalar @@ -2416,15 +2887,19 @@ def amin(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue): array([0, 1]) >>> np.amin(a, axis=1) # Minima along the second axis array([0, 2]) + >>> np.amin(a, where=[False, True], initial=10, axis=0) + array([10, 1]) >>> b = np.arange(5, dtype=float) >>> b[2] = np.NaN >>> np.amin(b) nan + >>> np.amin(b, where=~np.isnan(b), initial=10) + 0.0 >>> np.nanmin(b) 0.0 - >>> np.min([[-50], [10]], axis=-1, initial=0) + >>> np.amin([[-50], [10]], axis=-1, initial=0) array([-50, 0]) Notice that the initial value is used as one of the elements for which the @@ -2433,19 +2908,27 @@ def amin(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue): Notice that this isn't the same as Python's ``default`` argument. - >>> np.min([6], initial=5) + >>> np.amin([6], initial=5) 5 >>> min([6], default=5) 6 """ - return _wrapreduction(a, np.minimum, 'min', axis, None, out, keepdims=keepdims, - initial=initial) + return _wrapreduction(a, np.minimum, 'min', axis, None, out, + keepdims=keepdims, initial=initial, where=where) + +def _alen_dispathcer(a): + return (a,) + +@array_function_dispatch(_alen_dispathcer) def alen(a): """ Return the length of the first dimension of the input array. + .. deprecated:: 1.18 + `numpy.alen` is deprecated, use `len` instead. + Parameters ---------- a : array_like @@ -2469,13 +2952,24 @@ def alen(a): 7 """ + # NumPy 1.18.0, 2019-08-02 + warnings.warn( + "`np.alen` is deprecated, use `len` instead", + DeprecationWarning, stacklevel=2) try: return len(a) except TypeError: return len(array(a, ndmin=1)) -def prod(a, axis=None, dtype=None, out=None, keepdims=np._NoValue, initial=np._NoValue): +def _prod_dispatcher(a, axis=None, dtype=None, out=None, keepdims=None, + initial=None, where=None): + return (a, out) + + +@array_function_dispatch(_prod_dispatcher) +def prod(a, axis=None, dtype=None, out=None, keepdims=np._NoValue, + initial=np._NoValue, where=np._NoValue): """ Return the product of array elements over a given axis. @@ -2520,6 +3014,11 @@ def prod(a, axis=None, dtype=None, out=None, keepdims=np._NoValue, initial=np._N .. versionadded:: 1.15.0 + where : array_like of bool, optional + Elements to include in the product. See `~numpy.ufunc.reduce` for details. + + .. versionadded:: 1.17.0 + Returns ------- product_along_axis : ndarray, see `dtype` parameter above. @@ -2529,7 +3028,7 @@ def prod(a, axis=None, dtype=None, out=None, keepdims=np._NoValue, initial=np._N See Also -------- ndarray.prod : equivalent method - numpy.doc.ufuncs : Section "Output arguments" + :ref:`ufuncs-output-type` Notes ----- @@ -2537,8 +3036,8 @@ def prod(a, axis=None, dtype=None, out=None, keepdims=np._NoValue, initial=np._N raised on overflow. That means that, on a 32-bit platform: >>> x = np.array([536870910, 536870910, 536870910, 536870910]) - >>> np.prod(x) # random - 16 + >>> np.prod(x) + 16 # may vary The product of an empty array is the neutral element 1: @@ -2562,6 +3061,11 @@ def prod(a, axis=None, dtype=None, out=None, keepdims=np._NoValue, initial=np._N >>> np.prod([[1.,2.],[3.,4.]], axis=1) array([ 2., 12.]) + Or select specific elements to include: + + >>> np.prod([1., np.nan, 3.], where=[True, False, True]) + 3.0 + If the type of `x` is unsigned, then the output type is the unsigned platform integer: @@ -2581,10 +3085,15 @@ def prod(a, axis=None, dtype=None, out=None, keepdims=np._NoValue, initial=np._N >>> np.prod([1, 2], initial=5) 10 """ - return _wrapreduction(a, np.multiply, 'prod', axis, dtype, out, keepdims=keepdims, - initial=initial) + return _wrapreduction(a, np.multiply, 'prod', axis, dtype, out, + keepdims=keepdims, initial=initial, where=where) +def _cumprod_dispatcher(a, axis=None, dtype=None, out=None): + return (a, out) + + +@array_function_dispatch(_cumprod_dispatcher) def cumprod(a, axis=None, dtype=None, out=None): """ Return the cumulative product of elements along a given axis. @@ -2615,7 +3124,7 @@ def cumprod(a, axis=None, dtype=None, out=None): See Also -------- - numpy.doc.ufuncs : Section "Output arguments" + :ref:`ufuncs-output-type` Notes ----- @@ -2648,6 +3157,11 @@ def cumprod(a, axis=None, dtype=None, out=None): return _wrapfunc(a, 'cumprod', axis=axis, dtype=dtype, out=out) +def _ndim_dispatcher(a): + return (a,) + + +@array_function_dispatch(_ndim_dispatcher) def ndim(a): """ Return the number of dimensions of an array. @@ -2685,6 +3199,11 @@ def ndim(a): return asarray(a).ndim +def _size_dispatcher(a, axis=None): + return (a,) + + +@array_function_dispatch(_size_dispatcher) def size(a, axis=None): """ Return the number of elements along a given axis. @@ -2731,6 +3250,11 @@ def size(a, axis=None): return asarray(a).shape[axis] +def _around_dispatcher(a, decimals=None, out=None): + return (a, out) + + +@array_function_dispatch(_around_dispatcher) def around(a, decimals=0, out=None): """ Evenly round to the given number of decimals. @@ -2746,8 +3270,8 @@ def around(a, decimals=0, out=None): out : ndarray, optional Alternative output array in which to place the result. It must have the same shape as the expected output, but the type of the output - values will be cast if necessary. See `doc.ufuncs` (Section - "Output arguments") for details. + values will be cast if necessary. See :ref:`ufuncs-output-type` for more + details. Returns ------- @@ -2770,27 +3294,51 @@ def around(a, decimals=0, out=None): ----- For values exactly halfway between rounded decimal values, NumPy rounds to the nearest even value. Thus 1.5 and 2.5 round to 2.0, - -0.5 and 0.5 round to 0.0, etc. Results may also be surprising due - to the inexact representation of decimal fractions in the IEEE - floating point standard [1]_ and errors introduced when scaling - by powers of ten. + -0.5 and 0.5 round to 0.0, etc. + + ``np.around`` uses a fast but sometimes inexact algorithm to round + floating-point datatypes. For positive `decimals` it is equivalent to + ``np.true_divide(np.rint(a * 10**decimals), 10**decimals)``, which has + error due to the inexact representation of decimal fractions in the IEEE + floating point standard [1]_ and errors introduced when scaling by powers + of ten. For instance, note the extra "1" in the following: + + >>> np.round(56294995342131.5, 3) + 56294995342131.51 + + If your goal is to print such values with a fixed number of decimals, it is + preferable to use numpy's float printing routines to limit the number of + printed decimals: + + >>> np.format_float_positional(56294995342131.5, precision=3) + '56294995342131.5' + + The float printing routines use an accurate but much more computationally + demanding algorithm to compute the number of digits after the decimal + point. + + Alternatively, Python's builtin `round` function uses a more accurate + but slower algorithm for 64-bit floating point values: + + >>> round(56294995342131.5, 3) + 56294995342131.5 + >>> np.round(16.055, 2), round(16.055, 2) # equals 16.0549999999999997 + (16.06, 16.05) + References ---------- - .. [1] "Lecture Notes on the Status of IEEE 754", William Kahan, - http://www.cs.berkeley.edu/~wkahan/ieee754status/IEEE754.PDF - .. [2] "How Futile are Mindless Assessments of - Roundoff in Floating-Point Computation?", William Kahan, - http://www.cs.berkeley.edu/~wkahan/Mindless.pdf + .. [1] "Lecture Notes on the Status of IEEE 754", William Kahan, + https://people.eecs.berkeley.edu/~wkahan/ieee754status/IEEE754.PDF Examples -------- >>> np.around([0.37, 1.64]) - array([ 0., 2.]) + array([0., 2.]) >>> np.around([0.37, 1.64], decimals=1) - array([ 0.4, 1.6]) + array([0.4, 1.6]) >>> np.around([.5, 1.5, 2.5, 3.5, 4.5]) # rounds to nearest even value - array([ 0., 2., 2., 4., 4.]) + array([0., 2., 2., 4., 4.]) >>> np.around([1,2,3,11], decimals=1) # ndarray of ints is returned array([ 1, 2, 3, 11]) >>> np.around([1,2,3,11], decimals=-1) @@ -2800,21 +3348,14 @@ def around(a, decimals=0, out=None): return _wrapfunc(a, 'round', decimals=decimals, out=out) -def round_(a, decimals=0, out=None): - """ - Round an array to the given number of decimals. - - Refer to `around` for full documentation. +def _mean_dispatcher(a, axis=None, dtype=None, out=None, keepdims=None, *, + where=None): + return (a, where, out) - See Also - -------- - around : equivalent function - """ - return around(a, decimals=decimals, out=out) - - -def mean(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): +@array_function_dispatch(_mean_dispatcher) +def mean(a, axis=None, dtype=None, out=None, keepdims=np._NoValue, *, + where=np._NoValue): """ Compute the arithmetic mean along the specified axis. @@ -2843,7 +3384,7 @@ def mean(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): Alternate output array in which to place the result. The default is ``None``; if provided, it must have the same shape as the expected output, but the type will be cast if necessary. - See `doc.ufuncs` for details. + See :ref:`ufuncs-output-type` for more details. keepdims : bool, optional If this is set to True, the axes which are reduced are left @@ -2856,6 +3397,11 @@ def mean(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): sub-class' method does not implement `keepdims` any exceptions will be raised. + where : array_like of bool, optional + Elements to include in the mean. See `~numpy.ufunc.reduce` for details. + + .. versionadded:: 1.20.0 + Returns ------- m : ndarray, see dtype parameter above @@ -2887,9 +3433,9 @@ def mean(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): >>> np.mean(a) 2.5 >>> np.mean(a, axis=0) - array([ 2., 3.]) + array([2., 3.]) >>> np.mean(a, axis=1) - array([ 1.5, 3.5]) + array([1.5, 3.5]) In single precision, `mean` can be inaccurate: @@ -2902,12 +3448,21 @@ def mean(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): Computing the mean in float64 is more accurate: >>> np.mean(a, dtype=np.float64) - 0.55000000074505806 + 0.55000000074505806 # may vary + + Specifying a where argument: + >>> a = np.array([[5, 9, 13], [14, 10, 12], [11, 15, 19]]) + >>> np.mean(a) + 12.0 + >>> np.mean(a, where=[[True], [False], [False]]) + 9.0 """ kwargs = {} if keepdims is not np._NoValue: kwargs['keepdims'] = keepdims + if where is not np._NoValue: + kwargs['where'] = where if type(a) is not mu.ndarray: try: mean = a.mean @@ -2920,7 +3475,14 @@ def mean(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): out=out, **kwargs) -def std(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): +def _std_dispatcher(a, axis=None, dtype=None, out=None, ddof=None, + keepdims=None, *, where=None): + return (a, where, out) + + +@array_function_dispatch(_std_dispatcher) +def std(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue, *, + where=np._NoValue): """ Compute the standard deviation along the specified axis. @@ -2963,6 +3525,12 @@ def std(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): sub-class' method does not implement `keepdims` any exceptions will be raised. + where : array_like of bool, optional + Elements to include in the standard deviation. + See `~numpy.ufunc.reduce` for details. + + .. versionadded:: 1.20.0 + Returns ------- standard_deviation : ndarray, see dtype parameter above. @@ -2972,22 +3540,23 @@ def std(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): See Also -------- var, mean, nanmean, nanstd, nanvar - numpy.doc.ufuncs : Section "Output arguments" + :ref:`ufuncs-output-type` Notes ----- The standard deviation is the square root of the average of the squared - deviations from the mean, i.e., ``std = sqrt(mean(abs(x - x.mean())**2))``. - - The average squared deviation is normally calculated as - ``x.sum() / N``, where ``N = len(x)``. If, however, `ddof` is specified, - the divisor ``N - ddof`` is used instead. In standard statistical - practice, ``ddof=1`` provides an unbiased estimator of the variance - of the infinite population. ``ddof=0`` provides a maximum likelihood - estimate of the variance for normally distributed variables. The - standard deviation computed in this function is the square root of - the estimated variance, so even with ``ddof=1``, it will not be an - unbiased estimate of the standard deviation per se. + deviations from the mean, i.e., ``std = sqrt(mean(x))``, where + ``x = abs(a - a.mean())**2``. + + The average squared deviation is typically calculated as ``x.sum() / N``, + where ``N = len(x)``. If, however, `ddof` is specified, the divisor + ``N - ddof`` is used instead. In standard statistical practice, ``ddof=1`` + provides an unbiased estimator of the variance of the infinite population. + ``ddof=0`` provides a maximum likelihood estimate of the variance for + normally distributed variables. The standard deviation computed in this + function is the square root of the estimated variance, so even with + ``ddof=1``, it will not be an unbiased estimate of the standard deviation + per se. Note that, for complex numbers, `std` takes the absolute value before squaring, so that the result is always real and nonnegative. @@ -3002,11 +3571,11 @@ def std(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): -------- >>> a = np.array([[1, 2], [3, 4]]) >>> np.std(a) - 1.1180339887498949 + 1.1180339887498949 # may vary >>> np.std(a, axis=0) - array([ 1., 1.]) + array([1., 1.]) >>> np.std(a, axis=1) - array([ 0.5, 0.5]) + array([0.5, 0.5]) In single precision, std() can be inaccurate: @@ -3019,13 +3588,22 @@ def std(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): Computing the standard deviation in float64 is more accurate: >>> np.std(a, dtype=np.float64) - 0.44999999925494177 + 0.44999999925494177 # may vary + + Specifying a where argument: + + >>> a = np.array([[14, 8, 11, 10], [7, 9, 10, 11], [10, 15, 5, 10]]) + >>> np.std(a) + 2.614064523559687 # may vary + >>> np.std(a, where=[[True], [True], [False]]) + 2.0 """ kwargs = {} if keepdims is not np._NoValue: kwargs['keepdims'] = keepdims - + if where is not np._NoValue: + kwargs['where'] = where if type(a) is not mu.ndarray: try: std = a.std @@ -3038,7 +3616,14 @@ def std(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): **kwargs) -def var(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): +def _var_dispatcher(a, axis=None, dtype=None, out=None, ddof=None, + keepdims=None, *, where=None): + return (a, where, out) + + +@array_function_dispatch(_var_dispatcher) +def var(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue, *, + where=np._NoValue): """ Compute the variance along the specified axis. @@ -3061,7 +3646,7 @@ def var(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): instead of a single axis or all the axes as before. dtype : data-type, optional Type to use in computing the variance. For arrays of integer type - the default is `float32`; for arrays of float types it is the same as + the default is `float64`; for arrays of float types it is the same as the array type. out : ndarray, optional Alternate output array in which to place the result. It must have @@ -3082,6 +3667,12 @@ def var(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): sub-class' method does not implement `keepdims` any exceptions will be raised. + where : array_like of bool, optional + Elements to include in the variance. See `~numpy.ufunc.reduce` for + details. + + .. versionadded:: 1.20.0 + Returns ------- variance : ndarray, see dtype parameter above @@ -3090,15 +3681,15 @@ def var(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): See Also -------- - std , mean, nanmean, nanstd, nanvar - numpy.doc.ufuncs : Section "Output arguments" + std, mean, nanmean, nanstd, nanvar + :ref:`ufuncs-output-type` Notes ----- The variance is the average of the squared deviations from the mean, - i.e., ``var = mean(abs(x - x.mean())**2)``. + i.e., ``var = mean(x)``, where ``x = abs(a - a.mean())**2``. - The mean is normally calculated as ``x.sum() / N``, where ``N = len(x)``. + The mean is typically calculated as ``x.sum() / N``, where ``N = len(x)``. If, however, `ddof` is specified, the divisor ``N - ddof`` is used instead. In standard statistical practice, ``ddof=1`` provides an unbiased estimator of the variance of a hypothetical infinite population. @@ -3120,9 +3711,9 @@ def var(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): >>> np.var(a) 1.25 >>> np.var(a, axis=0) - array([ 1., 1.]) + array([1., 1.]) >>> np.var(a, axis=1) - array([ 0.25, 0.25]) + array([0.25, 0.25]) In single precision, var() can be inaccurate: @@ -3135,14 +3726,24 @@ def var(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): Computing the variance in float64 is more accurate: >>> np.var(a, dtype=np.float64) - 0.20249999932944759 + 0.20249999932944759 # may vary >>> ((1-0.55)**2 + (0.1-0.55)**2)/2 0.2025 + Specifying a where argument: + + >>> a = np.array([[14, 8, 11, 10], [7, 9, 10, 11], [10, 15, 5, 10]]) + >>> np.var(a) + 6.833333333333333 # may vary + >>> np.var(a, where=[[True], [True], [False]]) + 4.0 + """ kwargs = {} if keepdims is not np._NoValue: kwargs['keepdims'] = keepdims + if where is not np._NoValue: + kwargs['where'] = where if type(a) is not mu.ndarray: try: @@ -3160,6 +3761,19 @@ def var(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): # Aliases of other functions. These have their own definitions only so that # they can have unique docstrings. +@array_function_dispatch(_around_dispatcher) +def round_(a, decimals=0, out=None): + """ + Round an array to the given number of decimals. + + See Also + -------- + around : equivalent function; see for details. + """ + return around(a, decimals=decimals, out=out) + + +@array_function_dispatch(_prod_dispatcher, verify=False) def product(*args, **kwargs): """ Return the product of array elements over a given axis. @@ -3171,6 +3785,7 @@ def product(*args, **kwargs): return prod(*args, **kwargs) +@array_function_dispatch(_cumprod_dispatcher, verify=False) def cumproduct(*args, **kwargs): """ Return the cumulative product over the given axis. @@ -3182,6 +3797,7 @@ def cumproduct(*args, **kwargs): return cumprod(*args, **kwargs) +@array_function_dispatch(_any_dispatcher, verify=False) def sometrue(*args, **kwargs): """ Check whether some values are true. @@ -3195,6 +3811,7 @@ def sometrue(*args, **kwargs): return any(*args, **kwargs) +@array_function_dispatch(_all_dispatcher, verify=False) def alltrue(*args, **kwargs): """ Check if all elements of input array are true. @@ -3204,29 +3821,3 @@ def alltrue(*args, **kwargs): numpy.all : Equivalent function; see for details. """ return all(*args, **kwargs) - - -def rank(a): - """ - Return the number of dimensions of an array. - - .. note:: - This function is deprecated in NumPy 1.9 to avoid confusion with - `numpy.linalg.matrix_rank`. The ``ndim`` attribute or function - should be used instead. - - See Also - -------- - ndim : equivalent non-deprecated function - - Notes - ----- - In the old Numeric package, `rank` was the term used for the number of - dimensions, but in NumPy `ndim` is used instead. - """ - # 2014-04-12, 1.9 - warnings.warn( - "`rank` is deprecated; use the `ndim` attribute or function instead. " - "To find the rank of a matrix see `numpy.linalg.matrix_rank`.", - VisibleDeprecationWarning, stacklevel=2) - return ndim(a) diff --git a/numpy/core/fromnumeric.pyi b/numpy/core/fromnumeric.pyi new file mode 100644 index 000000000000..3cbe1d5c5ce2 --- /dev/null +++ b/numpy/core/fromnumeric.pyi @@ -0,0 +1,363 @@ +import datetime as dt +from typing import Optional, Union, Sequence, Tuple, Any, overload, TypeVar, Literal + +from numpy import ( + ndarray, + number, + integer, + intp, + bool_, + generic, + _OrderKACF, + _OrderACF, + _ModeKind, + _PartitionKind, + _SortKind, + _SortSide, +) +from numpy.typing import ( + DTypeLike, + ArrayLike, + _ShapeLike, + _Shape, + _ArrayLikeBool_co, + _ArrayLikeInt_co, + _NumberLike_co, +) + +# Various annotations for scalars + +# While dt.datetime and dt.timedelta are not technically part of NumPy, +# they are one of the rare few builtin scalars which serve as valid return types. +# See https://github.com/numpy/numpy-stubs/pull/67#discussion_r412604113. +_ScalarNumpy = Union[generic, dt.datetime, dt.timedelta] +_ScalarBuiltin = Union[str, bytes, dt.date, dt.timedelta, bool, int, float, complex] +_Scalar = Union[_ScalarBuiltin, _ScalarNumpy] + +# Integers and booleans can generally be used interchangeably +_ScalarGeneric = TypeVar("_ScalarGeneric", bound=generic) + +_Number = TypeVar("_Number", bound=number) + +# The signature of take() follows a common theme with its overloads: +# 1. A generic comes in; the same generic comes out +# 2. A scalar comes in; a generic comes out +# 3. An array-like object comes in; some keyword ensures that a generic comes out +# 4. An array-like object comes in; an ndarray or generic comes out +def take( + a: ArrayLike, + indices: _ArrayLikeInt_co, + axis: Optional[int] = ..., + out: Optional[ndarray] = ..., + mode: _ModeKind = ..., +) -> Any: ... + +def reshape( + a: ArrayLike, + newshape: _ShapeLike, + order: _OrderACF = ..., +) -> ndarray: ... + +def choose( + a: _ArrayLikeInt_co, + choices: ArrayLike, + out: Optional[ndarray] = ..., + mode: _ModeKind = ..., +) -> Any: ... + +def repeat( + a: ArrayLike, + repeats: _ArrayLikeInt_co, + axis: Optional[int] = ..., +) -> ndarray: ... + +def put( + a: ndarray, + ind: _ArrayLikeInt_co, + v: ArrayLike, + mode: _ModeKind = ..., +) -> None: ... + +def swapaxes( + a: ArrayLike, + axis1: int, + axis2: int, +) -> ndarray: ... + +def transpose( + a: ArrayLike, + axes: Union[None, Sequence[int], ndarray] = ... +) -> ndarray: ... + +def partition( + a: ArrayLike, + kth: _ArrayLikeInt_co, + axis: Optional[int] = ..., + kind: _PartitionKind = ..., + order: Union[None, str, Sequence[str]] = ..., +) -> ndarray: ... + +def argpartition( + a: ArrayLike, + kth: _ArrayLikeInt_co, + axis: Optional[int] = ..., + kind: _PartitionKind = ..., + order: Union[None, str, Sequence[str]] = ..., +) -> Any: ... + +def sort( + a: ArrayLike, + axis: Optional[int] = ..., + kind: Optional[_SortKind] = ..., + order: Union[None, str, Sequence[str]] = ..., +) -> ndarray: ... + +def argsort( + a: ArrayLike, + axis: Optional[int] = ..., + kind: Optional[_SortKind] = ..., + order: Union[None, str, Sequence[str]] = ..., +) -> ndarray: ... + +@overload +def argmax( + a: ArrayLike, + axis: None = ..., + out: Optional[ndarray] = ..., + *, + keepdims: Literal[False] = ..., +) -> intp: ... +@overload +def argmax( + a: ArrayLike, + axis: Optional[int] = ..., + out: Optional[ndarray] = ..., + *, + keepdims: bool = ..., +) -> Any: ... + +@overload +def argmin( + a: ArrayLike, + axis: None = ..., + out: Optional[ndarray] = ..., + *, + keepdims: Literal[False] = ..., +) -> intp: ... +@overload +def argmin( + a: ArrayLike, + axis: Optional[int] = ..., + out: Optional[ndarray] = ..., + *, + keepdims: bool = ..., +) -> Any: ... + +@overload +def searchsorted( + a: ArrayLike, + v: _Scalar, + side: _SortSide = ..., + sorter: Optional[_ArrayLikeInt_co] = ..., # 1D int array +) -> intp: ... +@overload +def searchsorted( + a: ArrayLike, + v: ArrayLike, + side: _SortSide = ..., + sorter: Optional[_ArrayLikeInt_co] = ..., # 1D int array +) -> ndarray: ... + +def resize( + a: ArrayLike, + new_shape: _ShapeLike, +) -> ndarray: ... + +@overload +def squeeze( + a: _ScalarGeneric, + axis: Optional[_ShapeLike] = ..., +) -> _ScalarGeneric: ... +@overload +def squeeze( + a: ArrayLike, + axis: Optional[_ShapeLike] = ..., +) -> ndarray: ... + +def diagonal( + a: ArrayLike, + offset: int = ..., + axis1: int = ..., + axis2: int = ..., # >= 2D array +) -> ndarray: ... + +def trace( + a: ArrayLike, # >= 2D array + offset: int = ..., + axis1: int = ..., + axis2: int = ..., + dtype: DTypeLike = ..., + out: Optional[ndarray] = ..., +) -> Any: ... + +def ravel(a: ArrayLike, order: _OrderKACF = ...) -> ndarray: ... + +def nonzero(a: ArrayLike) -> Tuple[ndarray, ...]: ... + +def shape(a: ArrayLike) -> _Shape: ... + +def compress( + condition: ArrayLike, # 1D bool array + a: ArrayLike, + axis: Optional[int] = ..., + out: Optional[ndarray] = ..., +) -> ndarray: ... + +@overload +def clip( + a: ArrayLike, + a_min: ArrayLike, + a_max: Optional[ArrayLike], + out: Optional[ndarray] = ..., + **kwargs: Any, +) -> Any: ... +@overload +def clip( + a: ArrayLike, + a_min: None, + a_max: ArrayLike, + out: Optional[ndarray] = ..., + **kwargs: Any, +) -> Any: ... + +def sum( + a: ArrayLike, + axis: _ShapeLike = ..., + dtype: DTypeLike = ..., + out: Optional[ndarray] = ..., + keepdims: bool = ..., + initial: _NumberLike_co = ..., + where: _ArrayLikeBool_co = ..., +) -> Any: ... + +@overload +def all( + a: ArrayLike, + axis: None = ..., + out: None = ..., + keepdims: Literal[False] = ..., +) -> bool_: ... +@overload +def all( + a: ArrayLike, + axis: Optional[_ShapeLike] = ..., + out: Optional[ndarray] = ..., + keepdims: bool = ..., +) -> Any: ... + +@overload +def any( + a: ArrayLike, + axis: None = ..., + out: None = ..., + keepdims: Literal[False] = ..., +) -> bool_: ... +@overload +def any( + a: ArrayLike, + axis: Optional[_ShapeLike] = ..., + out: Optional[ndarray] = ..., + keepdims: bool = ..., +) -> Any: ... + +def cumsum( + a: ArrayLike, + axis: Optional[int] = ..., + dtype: DTypeLike = ..., + out: Optional[ndarray] = ..., +) -> ndarray: ... + +def ptp( + a: ArrayLike, + axis: Optional[_ShapeLike] = ..., + out: Optional[ndarray] = ..., + keepdims: bool = ..., +) -> Any: ... + +def amax( + a: ArrayLike, + axis: Optional[_ShapeLike] = ..., + out: Optional[ndarray] = ..., + keepdims: bool = ..., + initial: _NumberLike_co = ..., + where: _ArrayLikeBool_co = ..., +) -> Any: ... + +def amin( + a: ArrayLike, + axis: Optional[_ShapeLike] = ..., + out: Optional[ndarray] = ..., + keepdims: bool = ..., + initial: _NumberLike_co = ..., + where: _ArrayLikeBool_co = ..., +) -> Any: ... + +# TODO: `np.prod()``: For object arrays `initial` does not necessarily +# have to be a numerical scalar. +# The only requirement is that it is compatible +# with the `.__mul__()` method(s) of the passed array's elements. + +# Note that the same situation holds for all wrappers around +# `np.ufunc.reduce`, e.g. `np.sum()` (`.__add__()`). +def prod( + a: ArrayLike, + axis: Optional[_ShapeLike] = ..., + dtype: DTypeLike = ..., + out: Optional[ndarray] = ..., + keepdims: bool = ..., + initial: _NumberLike_co = ..., + where: _ArrayLikeBool_co = ..., +) -> Any: ... + +def cumprod( + a: ArrayLike, + axis: Optional[int] = ..., + dtype: DTypeLike = ..., + out: Optional[ndarray] = ..., +) -> ndarray: ... + +def ndim(a: ArrayLike) -> int: ... + +def size(a: ArrayLike, axis: Optional[int] = ...) -> int: ... + +def around( + a: ArrayLike, + decimals: int = ..., + out: Optional[ndarray] = ..., +) -> Any: ... + +def mean( + a: ArrayLike, + axis: Optional[_ShapeLike] = ..., + dtype: DTypeLike = ..., + out: Optional[ndarray] = ..., + keepdims: bool = ..., +) -> Any: ... + +def std( + a: ArrayLike, + axis: Optional[_ShapeLike] = ..., + dtype: DTypeLike = ..., + out: Optional[ndarray] = ..., + ddof: int = ..., + keepdims: bool = ..., +) -> Any: ... + +def var( + a: ArrayLike, + axis: Optional[_ShapeLike] = ..., + dtype: DTypeLike = ..., + out: Optional[ndarray] = ..., + ddof: int = ..., + keepdims: bool = ..., +) -> Any: ... diff --git a/numpy/core/function_base.py b/numpy/core/function_base.py index 82de1a36ef6f..e940ac230537 100644 --- a/numpy/core/function_base.py +++ b/numpy/core/function_base.py @@ -1,28 +1,28 @@ -from __future__ import division, absolute_import, print_function - +import functools import warnings import operator +import types from . import numeric as _nx -from .numeric import (result_type, NaN, shares_memory, MAY_SHARE_BOUNDS, - TooHardError,asanyarray) +from .numeric import result_type, NaN, asanyarray, ndim +from numpy.core.multiarray import add_docstring +from numpy.core import overrides __all__ = ['logspace', 'linspace', 'geomspace'] -def _index_deprecate(i, stacklevel=2): - try: - i = operator.index(i) - except TypeError: - msg = ("object of type {} cannot be safely interpreted as " - "an integer.".format(type(i))) - i = int(i) - stacklevel += 1 - warnings.warn(msg, DeprecationWarning, stacklevel=stacklevel) - return i +array_function_dispatch = functools.partial( + overrides.array_function_dispatch, module='numpy') + + +def _linspace_dispatcher(start, stop, num=None, endpoint=None, retstep=None, + dtype=None, axis=None): + return (start, stop) -def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None): +@array_function_dispatch(_linspace_dispatcher) +def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None, + axis=0): """ Return evenly spaced numbers over a specified interval. @@ -31,11 +31,19 @@ def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None): The endpoint of the interval can optionally be excluded. + .. versionchanged:: 1.16.0 + Non-scalar `start` and `stop` are now supported. + + .. versionchanged:: 1.20.0 + Values are rounded towards ``-inf`` instead of ``0`` when an + integer ``dtype`` is specified. The old behavior can + still be obtained with ``np.linspace(start, stop, num).astype(int)`` + Parameters ---------- - start : scalar + start : array_like The starting value of the sequence. - stop : scalar + stop : array_like The end value of the sequence, unless `endpoint` is set to False. In that case, the sequence consists of all but the last of ``num + 1`` evenly spaced samples, so that `stop` is excluded. Note that the step @@ -49,11 +57,20 @@ def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None): If True, return (`samples`, `step`), where `step` is the spacing between samples. dtype : dtype, optional - The type of the output array. If `dtype` is not given, infer the data - type from the other input arguments. + The type of the output array. If `dtype` is not given, the data type + is inferred from `start` and `stop`. The inferred dtype will never be + an integer; `float` is chosen even if the arguments would produce an + array of integers. .. versionadded:: 1.9.0 + axis : int, optional + The axis in the result to store the samples. Relevant only if start + or stop are array-like. By default (0), the samples will be along a + new axis inserted at the beginning. Use -1 to get an axis at the end. + + .. versionadded:: 1.16.0 + Returns ------- samples : ndarray @@ -70,16 +87,19 @@ def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None): -------- arange : Similar to `linspace`, but uses a step size (instead of the number of samples). - logspace : Samples uniformly distributed in log space. + geomspace : Similar to `linspace`, but with numbers spaced evenly on a log + scale (a geometric progression). + logspace : Similar to `geomspace`, but with the end points specified as + logarithms. Examples -------- >>> np.linspace(2.0, 3.0, num=5) - array([ 2. , 2.25, 2.5 , 2.75, 3. ]) + array([2. , 2.25, 2.5 , 2.75, 3. ]) >>> np.linspace(2.0, 3.0, num=5, endpoint=False) - array([ 2. , 2.2, 2.4, 2.6, 2.8]) + array([2. , 2.2, 2.4, 2.6, 2.8]) >>> np.linspace(2.0, 3.0, num=5, retstep=True) - (array([ 2. , 2.25, 2.5 , 2.75, 3. ]), 0.25) + (array([2. , 2.25, 2.5 , 2.75, 3. ]), 0.25) Graphical illustration: @@ -97,8 +117,7 @@ def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None): >>> plt.show() """ - # 2016-02-25, 1.12 - num = _index_deprecate(num) + num = operator.index(num) if num < 0: raise ValueError("Number of samples, %s, must be non-negative." % num) div = (num - 1) if endpoint else num @@ -112,16 +131,15 @@ def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None): if dtype is None: dtype = dt - y = _nx.arange(0, num, dtype=dt) - delta = stop - start + y = _nx.arange(0, num, dtype=dt).reshape((-1,) + (1,) * ndim(delta)) # In-place multiplication y *= delta/div is faster, but prevents the multiplicant # from overriding what class is produced, and thus prevents, e.g. use of Quantities, # see gh-7142. Hence, we multiply in place only for standard scalar types. - _mult_inplace = _nx.isscalar(delta) - if num > 1: + _mult_inplace = _nx.isscalar(delta) + if div > 0: step = delta / div - if step == 0: + if _nx.any(step == 0): # Special handling for denormal numbers, gh-5437 y /= div if _mult_inplace: @@ -134,7 +152,8 @@ def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None): else: y = y * step else: - # 0 and 1 item long sequences have an undefined step + # sequences with 0 items or 1 item with endpoint=True (i.e. div <= 0) + # have an undefined step step = NaN # Multiply with delta to allow possible override of output class. y = y * delta @@ -144,13 +163,26 @@ def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None): if endpoint and num > 1: y[-1] = stop + if axis != 0: + y = _nx.moveaxis(y, 0, axis) + + if _nx.issubdtype(dtype, _nx.integer): + _nx.floor(y, out=y) + if retstep: return y.astype(dtype, copy=False), step else: return y.astype(dtype, copy=False) -def logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None): +def _logspace_dispatcher(start, stop, num=None, endpoint=None, base=None, + dtype=None, axis=None): + return (start, stop) + + +@array_function_dispatch(_logspace_dispatcher) +def logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None, + axis=0): """ Return numbers spaced evenly on a log scale. @@ -158,11 +190,14 @@ def logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None): (`base` to the power of `start`) and ends with ``base ** stop`` (see `endpoint` below). + .. versionchanged:: 1.16.0 + Non-scalar `start` and `stop` are now supported. + Parameters ---------- - start : float + start : array_like ``base ** start`` is the starting value of the sequence. - stop : float + stop : array_like ``base ** stop`` is the final value of the sequence, unless `endpoint` is False. In that case, ``num + 1`` values are spaced over the interval in log-space, of which all but the last (a sequence of @@ -172,13 +207,22 @@ def logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None): endpoint : boolean, optional If true, `stop` is the last sample. Otherwise, it is not included. Default is True. - base : float, optional + base : array_like, optional The base of the log space. The step size between the elements in ``ln(samples) / ln(base)`` (or ``log_base(samples)``) is uniform. Default is 10.0. dtype : dtype - The type of the output array. If `dtype` is not given, infer the data - type from the other input arguments. + The type of the output array. If `dtype` is not given, the data type + is inferred from `start` and `stop`. The inferred type will never be + an integer; `float` is chosen even if the arguments would produce an + array of integers. + axis : int, optional + The axis in the result to store the samples. Relevant only if start + or stop are array-like. By default (0), the samples will be along a + new axis inserted at the beginning. Use -1 to get an axis at the end. + + .. versionadded:: 1.16.0 + Returns ------- @@ -206,11 +250,11 @@ def logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None): Examples -------- >>> np.logspace(2.0, 3.0, num=4) - array([ 100. , 215.443469 , 464.15888336, 1000. ]) + array([ 100. , 215.443469 , 464.15888336, 1000. ]) >>> np.logspace(2.0, 3.0, num=4, endpoint=False) - array([ 100. , 177.827941 , 316.22776602, 562.34132519]) + array([100. , 177.827941 , 316.22776602, 562.34132519]) >>> np.logspace(2.0, 3.0, num=4, base=2.0) - array([ 4. , 5.0396842 , 6.34960421, 8. ]) + array([4. , 5.0396842 , 6.34960421, 8. ]) Graphical illustration: @@ -228,24 +272,33 @@ def logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None): >>> plt.show() """ - y = linspace(start, stop, num=num, endpoint=endpoint) + y = linspace(start, stop, num=num, endpoint=endpoint, axis=axis) if dtype is None: return _nx.power(base, y) - return _nx.power(base, y).astype(dtype) + return _nx.power(base, y).astype(dtype, copy=False) + + +def _geomspace_dispatcher(start, stop, num=None, endpoint=None, dtype=None, + axis=None): + return (start, stop) -def geomspace(start, stop, num=50, endpoint=True, dtype=None): +@array_function_dispatch(_geomspace_dispatcher) +def geomspace(start, stop, num=50, endpoint=True, dtype=None, axis=0): """ Return numbers spaced evenly on a log scale (a geometric progression). This is similar to `logspace`, but with endpoints specified directly. Each output sample is a constant multiple of the previous. + .. versionchanged:: 1.16.0 + Non-scalar `start` and `stop` are now supported. + Parameters ---------- - start : scalar + start : array_like The starting value of the sequence. - stop : scalar + stop : array_like The final value of the sequence, unless `endpoint` is False. In that case, ``num + 1`` values are spaced over the interval in log-space, of which all but the last (a sequence of @@ -256,8 +309,16 @@ def geomspace(start, stop, num=50, endpoint=True, dtype=None): If true, `stop` is the last sample. Otherwise, it is not included. Default is True. dtype : dtype - The type of the output array. If `dtype` is not given, infer the data - type from the other input arguments. + The type of the output array. If `dtype` is not given, the data type + is inferred from `start` and `stop`. The inferred dtype will never be + an integer; `float` is chosen even if the arguments would produce an + array of integers. + axis : int, optional + The axis in the result to store the samples. Relevant only if start + or stop are array-like. By default (0), the samples will be along a + new axis inserted at the beginning. Use -1 to get an axis at the end. + + .. versionadded:: 1.16.0 Returns ------- @@ -300,59 +361,169 @@ def geomspace(start, stop, num=50, endpoint=True, dtype=None): Negative, decreasing, and complex inputs are allowed: >>> np.geomspace(1000, 1, num=4) - array([ 1000., 100., 10., 1.]) + array([1000., 100., 10., 1.]) >>> np.geomspace(-1000, -1, num=4) array([-1000., -100., -10., -1.]) >>> np.geomspace(1j, 1000j, num=4) # Straight line - array([ 0. +1.j, 0. +10.j, 0. +100.j, 0.+1000.j]) + array([0. +1.j, 0. +10.j, 0. +100.j, 0.+1000.j]) >>> np.geomspace(-1+0j, 1+0j, num=5) # Circle - array([-1.00000000+0.j , -0.70710678+0.70710678j, - 0.00000000+1.j , 0.70710678+0.70710678j, - 1.00000000+0.j ]) + array([-1.00000000e+00+1.22464680e-16j, -7.07106781e-01+7.07106781e-01j, + 6.12323400e-17+1.00000000e+00j, 7.07106781e-01+7.07106781e-01j, + 1.00000000e+00+0.00000000e+00j]) - Graphical illustration of ``endpoint`` parameter: + Graphical illustration of `endpoint` parameter: >>> import matplotlib.pyplot as plt >>> N = 10 >>> y = np.zeros(N) >>> plt.semilogx(np.geomspace(1, 1000, N, endpoint=True), y + 1, 'o') + [<matplotlib.lines.Line2D object at 0x...>] >>> plt.semilogx(np.geomspace(1, 1000, N, endpoint=False), y + 2, 'o') + [<matplotlib.lines.Line2D object at 0x...>] >>> plt.axis([0.5, 2000, 0, 3]) + [0.5, 2000, 0, 3] >>> plt.grid(True, color='0.7', linestyle='-', which='both', axis='both') >>> plt.show() """ - if start == 0 or stop == 0: + start = asanyarray(start) + stop = asanyarray(stop) + if _nx.any(start == 0) or _nx.any(stop == 0): raise ValueError('Geometric sequence cannot include zero') - dt = result_type(start, stop, float(num)) + dt = result_type(start, stop, float(num), _nx.zeros((), dtype)) if dtype is None: dtype = dt else: # complex to dtype('complex128'), for instance dtype = _nx.dtype(dtype) + # Promote both arguments to the same dtype in case, for instance, one is + # complex and another is negative and log would produce NaN otherwise. + # Copy since we may change things in-place further down. + start = start.astype(dt, copy=True) + stop = stop.astype(dt, copy=True) + + out_sign = _nx.ones(_nx.broadcast(start, stop).shape, dt) # Avoid negligible real or imaginary parts in output by rotating to # positive real, calculating, then undoing rotation - out_sign = 1 - if start.real == stop.real == 0: - start, stop = start.imag, stop.imag - out_sign = 1j * out_sign - if _nx.sign(start) == _nx.sign(stop) == -1: - start, stop = -start, -stop - out_sign = -out_sign - - # Promote both arguments to the same dtype in case, for instance, one is - # complex and another is negative and log would produce NaN otherwise - start = start + (stop - stop) - stop = stop + (start - start) - if _nx.issubdtype(dtype, _nx.complexfloating): - start = start + 0j - stop = stop + 0j + if _nx.issubdtype(dt, _nx.complexfloating): + all_imag = (start.real == 0.) & (stop.real == 0.) + if _nx.any(all_imag): + start[all_imag] = start[all_imag].imag + stop[all_imag] = stop[all_imag].imag + out_sign[all_imag] = 1j + + both_negative = (_nx.sign(start) == -1) & (_nx.sign(stop) == -1) + if _nx.any(both_negative): + _nx.negative(start, out=start, where=both_negative) + _nx.negative(stop, out=stop, where=both_negative) + _nx.negative(out_sign, out=out_sign, where=both_negative) log_start = _nx.log10(start) log_stop = _nx.log10(stop) - result = out_sign * logspace(log_start, log_stop, num=num, - endpoint=endpoint, base=10.0, dtype=dtype) + result = logspace(log_start, log_stop, num=num, + endpoint=endpoint, base=10.0, dtype=dtype) + + # Make sure the endpoints match the start and stop arguments. This is + # necessary because np.exp(np.log(x)) is not necessarily equal to x. + if num > 0: + result[0] = start + if num > 1 and endpoint: + result[-1] = stop - return result.astype(dtype) + result = out_sign * result + + if axis != 0: + result = _nx.moveaxis(result, 0, axis) + + return result.astype(dtype, copy=False) + + +def _needs_add_docstring(obj): + """ + Returns true if the only way to set the docstring of `obj` from python is + via add_docstring. + + This function errs on the side of being overly conservative. + """ + Py_TPFLAGS_HEAPTYPE = 1 << 9 + + if isinstance(obj, (types.FunctionType, types.MethodType, property)): + return False + + if isinstance(obj, type) and obj.__flags__ & Py_TPFLAGS_HEAPTYPE: + return False + + return True + + +def _add_docstring(obj, doc, warn_on_python): + if warn_on_python and not _needs_add_docstring(obj): + warnings.warn( + "add_newdoc was used on a pure-python object {}. " + "Prefer to attach it directly to the source." + .format(obj), + UserWarning, + stacklevel=3) + try: + add_docstring(obj, doc) + except Exception: + pass + + +def add_newdoc(place, obj, doc, warn_on_python=True): + """ + Add documentation to an existing object, typically one defined in C + + The purpose is to allow easier editing of the docstrings without requiring + a re-compile. This exists primarily for internal use within numpy itself. + + Parameters + ---------- + place : str + The absolute name of the module to import from + obj : str + The name of the object to add documentation to, typically a class or + function name + doc : {str, Tuple[str, str], List[Tuple[str, str]]} + If a string, the documentation to apply to `obj` + + If a tuple, then the first element is interpreted as an attribute of + `obj` and the second as the docstring to apply - ``(method, docstring)`` + + If a list, then each element of the list should be a tuple of length + two - ``[(method1, docstring1), (method2, docstring2), ...]`` + warn_on_python : bool + If True, the default, emit `UserWarning` if this is used to attach + documentation to a pure-python object. + + Notes + ----- + This routine never raises an error if the docstring can't be written, but + will raise an error if the object being documented does not exist. + + This routine cannot modify read-only docstrings, as appear + in new-style classes or built-in functions. Because this + routine never raises an error the caller must check manually + that the docstrings were changed. + + Since this function grabs the ``char *`` from a c-level str object and puts + it into the ``tp_doc`` slot of the type of `obj`, it violates a number of + C-API best-practices, by: + + - modifying a `PyTypeObject` after calling `PyType_Ready` + - calling `Py_INCREF` on the str and losing the reference, so the str + will never be released + + If possible it should be avoided. + """ + new = getattr(__import__(place, globals(), {}, [obj]), obj) + if isinstance(doc, str): + _add_docstring(new, doc.strip(), warn_on_python) + elif isinstance(doc, tuple): + attr, docstring = doc + _add_docstring(getattr(new, attr), docstring.strip(), warn_on_python) + elif isinstance(doc, list): + for attr, docstring in doc: + _add_docstring(getattr(new, attr), docstring.strip(), warn_on_python) diff --git a/numpy/core/function_base.pyi b/numpy/core/function_base.pyi new file mode 100644 index 000000000000..68d3b3a98f57 --- /dev/null +++ b/numpy/core/function_base.pyi @@ -0,0 +1,60 @@ +from typing import overload, Tuple, Union, Sequence, Any, SupportsIndex, Literal, List + +from numpy import ndarray +from numpy.typing import ArrayLike, DTypeLike, _SupportsArray, _NumberLike_co + +# TODO: wait for support for recursive types +_ArrayLikeNested = Sequence[Sequence[Any]] +_ArrayLikeNumber = Union[ + _NumberLike_co, Sequence[_NumberLike_co], ndarray, _SupportsArray, _ArrayLikeNested +] + +__all__: List[str] + +@overload +def linspace( + start: _ArrayLikeNumber, + stop: _ArrayLikeNumber, + num: SupportsIndex = ..., + endpoint: bool = ..., + retstep: Literal[False] = ..., + dtype: DTypeLike = ..., + axis: SupportsIndex = ..., +) -> ndarray: ... +@overload +def linspace( + start: _ArrayLikeNumber, + stop: _ArrayLikeNumber, + num: SupportsIndex = ..., + endpoint: bool = ..., + retstep: Literal[True] = ..., + dtype: DTypeLike = ..., + axis: SupportsIndex = ..., +) -> Tuple[ndarray, Any]: ... + +def logspace( + start: _ArrayLikeNumber, + stop: _ArrayLikeNumber, + num: SupportsIndex = ..., + endpoint: bool = ..., + base: _ArrayLikeNumber = ..., + dtype: DTypeLike = ..., + axis: SupportsIndex = ..., +) -> ndarray: ... + +def geomspace( + start: _ArrayLikeNumber, + stop: _ArrayLikeNumber, + num: SupportsIndex = ..., + endpoint: bool = ..., + dtype: DTypeLike = ..., + axis: SupportsIndex = ..., +) -> ndarray: ... + +# Re-exported to `np.lib.function_base` +def add_newdoc( + place: str, + obj: str, + doc: str | Tuple[str, str] | List[Tuple[str, str]], + warn_on_python: bool = ..., +) -> None: ... diff --git a/numpy/core/getlimits.py b/numpy/core/getlimits.py index e450a660da59..ab4a4d2be696 100644 --- a/numpy/core/getlimits.py +++ b/numpy/core/getlimits.py @@ -1,18 +1,16 @@ """Machine limits for Float32 and Float64 and (long double) if available... """ -from __future__ import division, absolute_import, print_function - __all__ = ['finfo', 'iinfo'] import warnings -from .machar import MachAr +from ._machar import MachAr +from .overrides import set_module from . import numeric from . import numerictypes as ntypes -from .numeric import array, inf -from .umath import log10, exp2 -from . import umath +from .numeric import array, inf, NaN +from .umath import log10, exp2, nextafter, isnan def _fr0(a): @@ -31,13 +29,102 @@ def _fr1(a): return a +class MachArLike: + """ Object to simulate MachAr instance """ + def __init__(self, ftype, *, eps, epsneg, huge, tiny, + ibeta, smallest_subnormal=None, **kwargs): + self.params = _MACHAR_PARAMS[ftype] + self.ftype = ftype + self.title = self.params['title'] + # Parameter types same as for discovered MachAr object. + if not smallest_subnormal: + self._smallest_subnormal = nextafter( + self.ftype(0), self.ftype(1), dtype=self.ftype) + else: + self._smallest_subnormal = smallest_subnormal + self.epsilon = self.eps = self._float_to_float(eps) + self.epsneg = self._float_to_float(epsneg) + self.xmax = self.huge = self._float_to_float(huge) + self.xmin = self._float_to_float(tiny) + self.smallest_normal = self.tiny = self._float_to_float(tiny) + self.ibeta = self.params['itype'](ibeta) + self.__dict__.update(kwargs) + self.precision = int(-log10(self.eps)) + self.resolution = self._float_to_float( + self._float_conv(10) ** (-self.precision)) + self._str_eps = self._float_to_str(self.eps) + self._str_epsneg = self._float_to_str(self.epsneg) + self._str_xmin = self._float_to_str(self.xmin) + self._str_xmax = self._float_to_str(self.xmax) + self._str_resolution = self._float_to_str(self.resolution) + self._str_smallest_normal = self._float_to_str(self.xmin) + + @property + def smallest_subnormal(self): + """Return the value for the smallest subnormal. + + Returns + ------- + smallest_subnormal : float + value for the smallest subnormal. + + Warns + ----- + UserWarning + If the calculated value for the smallest subnormal is zero. + """ + # Check that the calculated value is not zero, in case it raises a + # warning. + value = self._smallest_subnormal + if self.ftype(0) == value: + warnings.warn( + 'The value of the smallest subnormal for {} type ' + 'is zero.'.format(self.ftype), UserWarning, stacklevel=2) + + return self._float_to_float(value) + + @property + def _str_smallest_subnormal(self): + """Return the string representation of the smallest subnormal.""" + return self._float_to_str(self.smallest_subnormal) + + def _float_to_float(self, value): + """Converts float to float. + + Parameters + ---------- + value : float + value to be converted. + """ + return _fr1(self._float_conv(value)) + + def _float_conv(self, value): + """Converts float to conv. + + Parameters + ---------- + value : float + value to be converted. + """ + return array([value], self.ftype) + + def _float_to_str(self, value): + """Converts float to str. + + Parameters + ---------- + value : float + value to be converted. + """ + return self.params['fmt'] % array(_fr0(value)[0], self.ftype) + + _convert_to_float = { ntypes.csingle: ntypes.single, ntypes.complex_: ntypes.float_, ntypes.clongfloat: ntypes.longfloat } - # Parameters for creating MachAr / MachAr-like objects _title_fmt = 'numpy {} precision floating point number' _MACHAR_PARAMS = { @@ -58,194 +145,161 @@ def _fr1(a): fmt = '%12.5e', title = _title_fmt.format('half'))} - -class MachArLike(object): - """ Object to simulate MachAr instance """ - - def __init__(self, - ftype, - **kwargs): - params = _MACHAR_PARAMS[ftype] - float_conv = lambda v: array([v], ftype) - float_to_float = lambda v : _fr1(float_conv(v)) - self._float_to_str = lambda v: (params['fmt'] % - array(_fr0(v)[0], ftype)) - self.title = params['title'] - # Parameter types same as for discovered MachAr object. - self.epsilon = self.eps = float_to_float(kwargs.pop('eps')) - self.epsneg = float_to_float(kwargs.pop('epsneg')) - self.xmax = self.huge = float_to_float(kwargs.pop('huge')) - self.xmin = self.tiny = float_to_float(kwargs.pop('tiny')) - self.ibeta = params['itype'](kwargs.pop('ibeta')) - self.__dict__.update(kwargs) - self.precision = int(-log10(self.eps)) - self.resolution = float_to_float(float_conv(10) ** (-self.precision)) - - # Properties below to delay need for float_to_str, and thus avoid circular - # imports during early numpy module loading. - # See: https://github.com/numpy/numpy/pull/8983#discussion_r115838683 - - @property - def _str_eps(self): - return self._float_to_str(self.eps) - - @property - def _str_epsneg(self): - return self._float_to_str(self.epsneg) - - @property - def _str_xmin(self): - return self._float_to_str(self.xmin) - - @property - def _str_xmax(self): - return self._float_to_str(self.xmax) - - @property - def _str_resolution(self): - return self._float_to_str(self.resolution) - - -# Known parameters for float16 -# See docstring of MachAr class for description of parameters. -_f16 = ntypes.float16 -_float16_ma = MachArLike(_f16, - machep=-10, - negep=-11, - minexp=-14, - maxexp=16, - it=10, - iexp=5, - ibeta=2, - irnd=5, - ngrd=0, - eps=exp2(_f16(-10)), - epsneg=exp2(_f16(-11)), - huge=_f16(65504), - tiny=_f16(2 ** -14)) - -# Known parameters for float32 -_f32 = ntypes.float32 -_float32_ma = MachArLike(_f32, - machep=-23, - negep=-24, - minexp=-126, - maxexp=128, - it=23, - iexp=8, - ibeta=2, - irnd=5, - ngrd=0, - eps=exp2(_f32(-23)), - epsneg=exp2(_f32(-24)), - huge=_f32((1 - 2 ** -24) * 2**128), - tiny=exp2(_f32(-126))) - -# Known parameters for float64 -_f64 = ntypes.float64 -_epsneg_f64 = 2.0 ** -53.0 -_tiny_f64 = 2.0 ** -1022.0 -_float64_ma = MachArLike(_f64, - machep=-52, - negep=-53, - minexp=-1022, - maxexp=1024, - it=52, - iexp=11, - ibeta=2, - irnd=5, - ngrd=0, - eps=2.0 ** -52.0, - epsneg=_epsneg_f64, - huge=(1.0 - _epsneg_f64) / _tiny_f64 * _f64(4), - tiny=_tiny_f64) - -# Known parameters for IEEE 754 128-bit binary float -_ld = ntypes.longdouble -_epsneg_f128 = exp2(_ld(-113)) -_tiny_f128 = exp2(_ld(-16382)) -# Ignore runtime error when this is not f128 -with numeric.errstate(all='ignore'): - _huge_f128 = (_ld(1) - _epsneg_f128) / _tiny_f128 * _ld(4) -_float128_ma = MachArLike(_ld, - machep=-112, - negep=-113, - minexp=-16382, - maxexp=16384, - it=112, - iexp=15, - ibeta=2, - irnd=5, - ngrd=0, - eps=exp2(_ld(-112)), - epsneg=_epsneg_f128, - huge=_huge_f128, - tiny=_tiny_f128) - -# Known parameters for float80 (Intel 80-bit extended precision) -_epsneg_f80 = exp2(_ld(-64)) -_tiny_f80 = exp2(_ld(-16382)) -# Ignore runtime error when this is not f80 -with numeric.errstate(all='ignore'): - _huge_f80 = (_ld(1) - _epsneg_f80) / _tiny_f80 * _ld(4) -_float80_ma = MachArLike(_ld, - machep=-63, - negep=-64, - minexp=-16382, - maxexp=16384, - it=63, - iexp=15, - ibeta=2, - irnd=5, - ngrd=0, - eps=exp2(_ld(-63)), - epsneg=_epsneg_f80, - huge=_huge_f80, - tiny=_tiny_f80) - -# Guessed / known parameters for double double; see: -# https://en.wikipedia.org/wiki/Quadruple-precision_floating-point_format#Double-double_arithmetic -# These numbers have the same exponent range as float64, but extended number of -# digits in the significand. -_huge_dd = (umath.nextafter(_ld(inf), _ld(0)) - if hasattr(umath, 'nextafter') # Missing on some platforms? - else _float64_ma.huge) -_float_dd_ma = MachArLike(_ld, - machep=-105, - negep=-106, - minexp=-1022, - maxexp=1024, - it=105, - iexp=11, - ibeta=2, - irnd=5, - ngrd=0, - eps=exp2(_ld(-105)), - epsneg= exp2(_ld(-106)), - huge=_huge_dd, - tiny=exp2(_ld(-1022))) - - # Key to identify the floating point type. Key is result of # ftype('-0.1').newbyteorder('<').tobytes() # See: # https://perl5.git.perl.org/perl.git/blob/3118d7d684b56cbeb702af874f4326683c45f045:/Configure -_KNOWN_TYPES = { - b'\x9a\x99\x99\x99\x99\x99\xb9\xbf' : _float64_ma, - b'\xcd\xcc\xcc\xbd' : _float32_ma, - b'f\xae' : _float16_ma, +_KNOWN_TYPES = {} +def _register_type(machar, bytepat): + _KNOWN_TYPES[bytepat] = machar +_float_ma = {} + + +def _register_known_types(): + # Known parameters for float16 + # See docstring of MachAr class for description of parameters. + f16 = ntypes.float16 + float16_ma = MachArLike(f16, + machep=-10, + negep=-11, + minexp=-14, + maxexp=16, + it=10, + iexp=5, + ibeta=2, + irnd=5, + ngrd=0, + eps=exp2(f16(-10)), + epsneg=exp2(f16(-11)), + huge=f16(65504), + tiny=f16(2 ** -14)) + _register_type(float16_ma, b'f\xae') + _float_ma[16] = float16_ma + + # Known parameters for float32 + f32 = ntypes.float32 + float32_ma = MachArLike(f32, + machep=-23, + negep=-24, + minexp=-126, + maxexp=128, + it=23, + iexp=8, + ibeta=2, + irnd=5, + ngrd=0, + eps=exp2(f32(-23)), + epsneg=exp2(f32(-24)), + huge=f32((1 - 2 ** -24) * 2**128), + tiny=exp2(f32(-126))) + _register_type(float32_ma, b'\xcd\xcc\xcc\xbd') + _float_ma[32] = float32_ma + + # Known parameters for float64 + f64 = ntypes.float64 + epsneg_f64 = 2.0 ** -53.0 + tiny_f64 = 2.0 ** -1022.0 + float64_ma = MachArLike(f64, + machep=-52, + negep=-53, + minexp=-1022, + maxexp=1024, + it=52, + iexp=11, + ibeta=2, + irnd=5, + ngrd=0, + eps=2.0 ** -52.0, + epsneg=epsneg_f64, + huge=(1.0 - epsneg_f64) / tiny_f64 * f64(4), + tiny=tiny_f64) + _register_type(float64_ma, b'\x9a\x99\x99\x99\x99\x99\xb9\xbf') + _float_ma[64] = float64_ma + + # Known parameters for IEEE 754 128-bit binary float + ld = ntypes.longdouble + epsneg_f128 = exp2(ld(-113)) + tiny_f128 = exp2(ld(-16382)) + # Ignore runtime error when this is not f128 + with numeric.errstate(all='ignore'): + huge_f128 = (ld(1) - epsneg_f128) / tiny_f128 * ld(4) + float128_ma = MachArLike(ld, + machep=-112, + negep=-113, + minexp=-16382, + maxexp=16384, + it=112, + iexp=15, + ibeta=2, + irnd=5, + ngrd=0, + eps=exp2(ld(-112)), + epsneg=epsneg_f128, + huge=huge_f128, + tiny=tiny_f128) + # IEEE 754 128-bit binary float + _register_type(float128_ma, + b'\x9a\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\xfb\xbf') + _register_type(float128_ma, + b'\x9a\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\xfb\xbf') + _float_ma[128] = float128_ma + + # Known parameters for float80 (Intel 80-bit extended precision) + epsneg_f80 = exp2(ld(-64)) + tiny_f80 = exp2(ld(-16382)) + # Ignore runtime error when this is not f80 + with numeric.errstate(all='ignore'): + huge_f80 = (ld(1) - epsneg_f80) / tiny_f80 * ld(4) + float80_ma = MachArLike(ld, + machep=-63, + negep=-64, + minexp=-16382, + maxexp=16384, + it=63, + iexp=15, + ibeta=2, + irnd=5, + ngrd=0, + eps=exp2(ld(-63)), + epsneg=epsneg_f80, + huge=huge_f80, + tiny=tiny_f80) # float80, first 10 bytes containing actual storage - b'\xcd\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xfb\xbf' : _float80_ma, + _register_type(float80_ma, b'\xcd\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xfb\xbf') + _float_ma[80] = float80_ma + + # Guessed / known parameters for double double; see: + # https://en.wikipedia.org/wiki/Quadruple-precision_floating-point_format#Double-double_arithmetic + # These numbers have the same exponent range as float64, but extended number of + # digits in the significand. + huge_dd = nextafter(ld(inf), ld(0), dtype=ld) + # As the smallest_normal in double double is so hard to calculate we set + # it to NaN. + smallest_normal_dd = NaN + # Leave the same value for the smallest subnormal as double + smallest_subnormal_dd = ld(nextafter(0., 1.)) + float_dd_ma = MachArLike(ld, + machep=-105, + negep=-106, + minexp=-1022, + maxexp=1024, + it=105, + iexp=11, + ibeta=2, + irnd=5, + ngrd=0, + eps=exp2(ld(-105)), + epsneg=exp2(ld(-106)), + huge=huge_dd, + tiny=smallest_normal_dd, + smallest_subnormal=smallest_subnormal_dd) # double double; low, high order (e.g. PPC 64) - b'\x9a\x99\x99\x99\x99\x99Y<\x9a\x99\x99\x99\x99\x99\xb9\xbf' : - _float_dd_ma, + _register_type(float_dd_ma, + b'\x9a\x99\x99\x99\x99\x99Y<\x9a\x99\x99\x99\x99\x99\xb9\xbf') # double double; high, low order (e.g. PPC 64 le) - b'\x9a\x99\x99\x99\x99\x99\xb9\xbf\x9a\x99\x99\x99\x99\x99Y<' : - _float_dd_ma, - # IEEE 754 128-bit binary float - b'\x9a\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\xfb\xbf' : - _float128_ma, -} + _register_type(float_dd_ma, + b'\x9a\x99\x99\x99\x99\x99\xb9\xbf\x9a\x99\x99\x99\x99\x99Y<') + _float_ma['dd'] = float_dd_ma def _get_machar(ftype): @@ -276,11 +330,15 @@ def _get_machar(ftype): raise ValueError(repr(ftype)) # Detect known / suspected types key = ftype('-0.1').newbyteorder('<').tobytes() - ma_like = _KNOWN_TYPES.get(key) - # Could be 80 bit == 10 byte extended precision, where last bytes can be - # random garbage. Try comparing first 10 bytes to pattern. - if ma_like is None and ftype == ntypes.longdouble: + ma_like = None + if ftype == ntypes.longdouble: + # Could be 80 bit == 10 byte extended precision, where last bytes can + # be random garbage. + # Comparing first 10 bytes to pattern first to avoid branching on the + # random garbage. ma_like = _KNOWN_TYPES.get(key[:10]) + if ma_like is None: + ma_like = _KNOWN_TYPES.get(key) if ma_like is not None: return ma_like # Fall back to parameter discovery @@ -302,7 +360,8 @@ def _discovered_machar(ftype): params['title']) -class finfo(object): +@set_module('numpy') +class finfo: """ finfo(dtype) @@ -313,18 +372,21 @@ class finfo(object): bits : int The number of bits occupied by the type. eps : float - The smallest representable positive number such that - ``1.0 + eps != 1.0``. Type of `eps` is an appropriate floating - point type. - epsneg : floating point number of the appropriate type - The smallest representable positive number such that - ``1.0 - epsneg != 1.0``. + The difference between 1.0 and the next smallest representable float + larger than 1.0. For example, for 64-bit binary floats in the IEEE-754 + standard, ``eps = 2**-52``, approximately 2.22e-16. + epsneg : float + The difference between 1.0 and the next smallest representable float + less than 1.0. For example, for 64-bit binary floats in the IEEE-754 + standard, ``epsneg = 2**-53``, approximately 1.11e-16. iexp : int The number of bits in the exponent portion of the floating point representation. machar : MachAr The object which calculated these parameters and holds more detailed information. + + .. deprecated:: 1.22 machep : int The exponent that yields `eps`. max : floating point number of the appropriate type @@ -349,8 +411,13 @@ class finfo(object): The approximate decimal resolution of this type, i.e., ``10**-precision``. tiny : float - The smallest positive usable number. Type of `tiny` is an - appropriate floating point type. + An alias for `smallest_normal`, kept for backwards compatibility. + smallest_normal : float + The smallest positive floating point number with 1 as leading bit in + the mantissa following IEEE-754 (see Notes). + smallest_subnormal : float + The smallest positive floating point number with 0 as leading bit in + the mantissa following IEEE-754. Parameters ---------- @@ -361,6 +428,8 @@ class finfo(object): -------- MachAr : The implementation of the tests that produce this information. iinfo : The equivalent for integer data types. + spacing : The distance between a value and the nearest adjacent number + nextafter : The next floating point value after x1 towards x2 Notes ----- @@ -369,6 +438,18 @@ class finfo(object): impacts import times. These objects are cached, so calling ``finfo()`` repeatedly inside your functions is not a problem. + Note that ``smallest_normal`` is not actually the smallest positive + representable value in a NumPy floating point type. As in the IEEE-754 + standard [1]_, NumPy floating point types make use of subnormal numbers to + fill the gap between 0 and ``smallest_normal``. However, subnormal numbers + may have significantly reduced precision [2]_. + + References + ---------- + .. [1] IEEE Standard for Floating-Point Arithmetic, IEEE Std 754-2008, + pp.1-70, 2008, http://www.doi.org/10.1109/IEEESTD.2008.4610935 + .. [2] Wikipedia, "Denormal Numbers", + https://en.wikipedia.org/wiki/Denormal_number """ _finfo_cache = {} @@ -414,7 +495,7 @@ def _init(self, dtype): 'maxexp', 'minexp', 'negep', 'machep']: setattr(self, word, getattr(machar, word)) - for word in ['tiny', 'resolution', 'epsneg']: + for word in ['resolution', 'epsneg', 'smallest_subnormal']: setattr(self, word, getattr(machar, word).flat[0]) self.bits = self.dtype.itemsize * 8 self.max = machar.huge.flat[0] @@ -422,12 +503,14 @@ def _init(self, dtype): self.eps = machar.eps.flat[0] self.nexp = machar.iexp self.nmant = machar.it - self.machar = machar + self._machar = machar self._str_tiny = machar._str_xmin.strip() self._str_max = machar._str_xmax.strip() self._str_epsneg = machar._str_epsneg.strip() self._str_eps = machar._str_eps.strip() self._str_resolution = machar._str_resolution.strip() + self._str_smallest_normal = machar._str_smallest_normal.strip() + self._str_smallest_subnormal = machar._str_smallest_subnormal.strip() return self def __str__(self): @@ -440,6 +523,8 @@ def __str__(self): 'minexp = %(minexp)6s tiny = %(_str_tiny)s\n' 'maxexp = %(maxexp)6s max = %(_str_max)s\n' 'nexp = %(nexp)6s min = -max\n' + 'smallest_normal = %(_str_smallest_normal)s ' + 'smallest_subnormal = %(_str_smallest_subnormal)s\n' '---------------------------------------------------------------\n' ) return fmt % self.__dict__ @@ -451,8 +536,63 @@ def __repr__(self): return (("%(klass)s(resolution=%(resolution)s, min=-%(_str_max)s," " max=%(_str_max)s, dtype=%(dtype)s)") % d) + @property + def smallest_normal(self): + """Return the value for the smallest normal. + + Returns + ------- + smallest_normal : float + Value for the smallest normal. + + Warns + ----- + UserWarning + If the calculated value for the smallest normal is requested for + double-double. + """ + # This check is necessary because the value for smallest_normal is + # platform dependent for longdouble types. + if isnan(self._machar.smallest_normal.flat[0]): + warnings.warn( + 'The value of smallest normal is undefined for double double', + UserWarning, stacklevel=2) + return self._machar.smallest_normal.flat[0] + + @property + def tiny(self): + """Return the value for tiny, alias of smallest_normal. + + Returns + ------- + tiny : float + Value for the smallest normal, alias of smallest_normal. + + Warns + ----- + UserWarning + If the calculated value for the smallest normal is requested for + double-double. + """ + return self.smallest_normal + + @property + def machar(self): + """The object which calculated these parameters and holds more + detailed information. + + .. deprecated:: 1.22 + """ + # Deprecated 2021-10-27, NumPy 1.22 + warnings.warn( + "`finfo.machar` is deprecated (NumPy 1.22)", + DeprecationWarning, stacklevel=2, + ) + return self._machar + -class iinfo(object): +@set_module('numpy') +class iinfo: """ iinfo(type) @@ -513,8 +653,9 @@ def __init__(self, int_type): self.bits = self.dtype.itemsize * 8 self.key = "%s%d" % (self.kind, self.bits) if self.kind not in 'iu': - raise ValueError("Invalid integer data type.") + raise ValueError("Invalid integer data type %r." % (self.kind,)) + @property def min(self): """Minimum value of given dtype.""" if self.kind == 'u': @@ -527,8 +668,7 @@ def min(self): iinfo._min_vals[self.key] = val return val - min = property(min) - + @property def max(self): """Maximum value of given dtype.""" try: @@ -541,8 +681,6 @@ def max(self): iinfo._max_vals[self.key] = val return val - max = property(max) - def __str__(self): """String representation.""" fmt = ( @@ -557,4 +695,3 @@ def __str__(self): def __repr__(self): return "%s(min=%s, max=%s, dtype=%s)" % (self.__class__.__name__, self.min, self.max, self.dtype) - diff --git a/numpy/core/getlimits.pyi b/numpy/core/getlimits.pyi new file mode 100644 index 000000000000..66d0629954d2 --- /dev/null +++ b/numpy/core/getlimits.pyi @@ -0,0 +1,8 @@ +from typing import List + +from numpy import ( + finfo as finfo, + iinfo as iinfo, +) + +__all__: List[str] diff --git a/numpy/core/include/numpy/.doxyfile b/numpy/core/include/numpy/.doxyfile new file mode 100644 index 000000000000..ed2aefff78c7 --- /dev/null +++ b/numpy/core/include/numpy/.doxyfile @@ -0,0 +1,2 @@ +INCLUDE_PATH += @CUR_DIR +PREDEFINED += NPY_INTERNAL_BUILD diff --git a/numpy/core/include/numpy/_neighborhood_iterator_imp.h b/numpy/core/include/numpy/_neighborhood_iterator_imp.h index e8860cbc73bb..07e2363d00c3 100644 --- a/numpy/core/include/numpy/_neighborhood_iterator_imp.h +++ b/numpy/core/include/numpy/_neighborhood_iterator_imp.h @@ -1,4 +1,4 @@ -#ifndef _NPY_INCLUDE_NEIGHBORHOOD_IMP +#ifndef NUMPY_CORE_INCLUDE_NUMPY__NEIGHBORHOOD_IMP_H_ #error You should not include this header directly #endif /* diff --git a/numpy/core/include/numpy/arrayobject.h b/numpy/core/include/numpy/arrayobject.h index 4f46d6b1ac91..da47bb09627a 100644 --- a/numpy/core/include/numpy/arrayobject.h +++ b/numpy/core/include/numpy/arrayobject.h @@ -1,4 +1,5 @@ -#ifndef Py_ARRAYOBJECT_H +#ifndef NUMPY_CORE_INCLUDE_NUMPY_ARRAYOBJECT_H_ +#define NUMPY_CORE_INCLUDE_NUMPY_ARRAYOBJECT_H_ #define Py_ARRAYOBJECT_H #include "ndarrayobject.h" @@ -8,4 +9,4 @@ #include "noprefix.h" #endif -#endif +#endif /* NUMPY_CORE_INCLUDE_NUMPY_ARRAYOBJECT_H_ */ diff --git a/numpy/core/include/numpy/arrayscalars.h b/numpy/core/include/numpy/arrayscalars.h index 64450e713213..a20a6801686d 100644 --- a/numpy/core/include/numpy/arrayscalars.h +++ b/numpy/core/include/numpy/arrayscalars.h @@ -1,5 +1,5 @@ -#ifndef _NPY_ARRAYSCALARS_H_ -#define _NPY_ARRAYSCALARS_H_ +#ifndef NUMPY_CORE_INCLUDE_NUMPY_ARRAYSCALARS_H_ +#define NUMPY_CORE_INCLUDE_NUMPY_ARRAYSCALARS_H_ #ifndef _MULTIARRAYMODULE typedef struct { @@ -134,8 +134,14 @@ typedef struct { char obval; } PyScalarObject; -#define PyStringScalarObject PyStringObject -#define PyUnicodeScalarObject PyUnicodeObject +#define PyStringScalarObject PyBytesObject +typedef struct { + /* note that the PyObject_HEAD macro lives right here */ + PyUnicodeObject base; + Py_UCS4 *obval; + char *buffer_fmt; +} PyUnicodeScalarObject; + typedef struct { PyObject_VAR_HEAD @@ -143,6 +149,7 @@ typedef struct { PyArray_Descr *descr; int flags; PyObject *base; + void *_buffer_info; /* private buffer info, tagged to allow warning */ } PyVoidScalarObject; /* Macros @@ -172,4 +179,4 @@ typedef struct { #define PyArrayScalar_ASSIGN(obj, cls, val) \ PyArrayScalar_VAL(obj, cls) = val -#endif +#endif /* NUMPY_CORE_INCLUDE_NUMPY_ARRAYSCALARS_H_ */ diff --git a/numpy/core/include/numpy/experimental_dtype_api.h b/numpy/core/include/numpy/experimental_dtype_api.h new file mode 100644 index 000000000000..effa66baf4c2 --- /dev/null +++ b/numpy/core/include/numpy/experimental_dtype_api.h @@ -0,0 +1,386 @@ +/* + * This header exports the new experimental DType API as proposed in + * NEPs 41 to 43. For background, please check these NEPs. Otherwise, + * this header also serves as documentation for the time being. + * + * Please do not hesitate to contact @seberg with questions. This is + * developed together with https://github.com/seberg/experimental_user_dtypes + * and those interested in experimenting are encouraged to contribute there. + * + * To use the functions defined in the header, call:: + * + * if (import_experimental_dtype_api(version) < 0) { + * return NULL; + * } + * + * in your module init. (A version mismatch will be reported, just update + * to the correct one, this will alert you of possible changes.) + * + * The following lists the main symbols currently exported. Please do not + * hesitate to ask for help or clarification: + * + * - PyUFunc_AddLoopFromSpec: + * + * Register a new loop for a ufunc. This uses the `PyArrayMethod_Spec` + * which must be filled in (see in-line comments). + * + * - PyUFunc_AddPromoter: + * + * Register a new promoter for a ufunc. A promoter is a function stored + * in a PyCapsule (see in-line comments). It is passed the operation and + * requested DType signatures and can mutate it to attempt a new search + * for a matching loop/promoter. + * I.e. for Numba a promoter could even add the desired loop. + * + * - PyArrayInitDTypeMeta_FromSpec: + * + * Initialize a new DType. It must currently be a static Python C type + * that is declared as `PyArray_DTypeMeta` and not `PyTypeObject`. + * Further, it must subclass `np.dtype` and set its type to + * `PyArrayDTypeMeta_Type` (before calling `PyType_Read()`). + * + * - PyArray_CommonDType: + * + * Find the common-dtype ("promotion") for two DType classes. Similar + * to `np.result_type`, but works on the classes and not instances. + * + * - PyArray_PromoteDTypeSequence: + * + * Same as CommonDType, but works with an arbitrary number of DTypes. + * This function is smarter and can often return successful and unambiguous + * results when `common_dtype(common_dtype(dt1, dt2), dt3)` would + * depend on the operation order or fail. Nevertheless, DTypes should + * aim to ensure that their common-dtype implementation is associative + * and commutative! (Mainly, unsigned and signed integers are not.) + * + * For guaranteed consistent results DTypes must implement common-Dtype + * "transitively". If A promotes B and B promotes C, than A must generally + * also promote C; where "promotes" means implements the promotion. + * (There are some exceptions for abstract DTypes) + * + * WARNING + * ======= + * + * By using this header, you understand that this is a fully experimental + * exposure. Details are expected to change, and some options may have no + * effect. (Please contact @seberg if you have questions!) + * If the exposure stops working, please file a bug report with NumPy. + * Further, a DType created using this API/header should still be expected + * to be incompatible with some functionality inside and outside of NumPy. + * In this case crashes must be expected. Please report any such problems + * so that they can be fixed before final exposure. + * Furthermore, expect missing checks for programming errors which the final + * API is expected to have. + * + * Symbols with a leading underscore are likely to not be included in the + * first public version, if these are central to your use-case, please let + * us know, so that we can reconsider. + * + * "Array-like" consumer API not yet under considerations + * ====================================================== + * + * The new DType API is designed in a way to make it potentially useful for + * alternative "array-like" implementations. This will require careful + * exposure of details and functions and is not part of this experimental API. + */ + +#ifndef NUMPY_CORE_INCLUDE_NUMPY_EXPERIMENTAL_DTYPE_API_H_ +#define NUMPY_CORE_INCLUDE_NUMPY_EXPERIMENTAL_DTYPE_API_H_ + +#include <Python.h> +#include "ndarraytypes.h" + + +/* + * Just a hack so I don't forget importing as much myself, I spend way too + * much time noticing it the first time around :). + */ +static void +__not_imported(void) +{ + printf("*****\nCritical error, dtype API not imported\n*****\n"); +} +static void *__uninitialized_table[] = { + &__not_imported, &__not_imported, &__not_imported, &__not_imported, + &__not_imported, &__not_imported, &__not_imported, &__not_imported}; + + +static void **__experimental_dtype_api_table = __uninitialized_table; + + +/* + * DTypeMeta struct, the content may be made fully opaque (except the size). + * We may also move everything into a single `void *dt_slots`. + */ +typedef struct { + PyHeapTypeObject super; + PyArray_Descr *singleton; + int type_num; + PyTypeObject *scalar_type; + npy_uint64 flags; + void *dt_slots; + void *reserved[3]; +} PyArray_DTypeMeta; + + +/* + * ****************************************************** + * ArrayMethod API (Casting and UFuncs) + * ****************************************************** + */ +/* + * NOTE: Expected changes: + * * invert logic of floating point error flag + * * probably split runtime and general flags into two + * * should possibly not use an enum for typdef for more stable ABI? + */ +typedef enum { + /* Flag for whether the GIL is required */ + NPY_METH_REQUIRES_PYAPI = 1 << 1, + /* + * Some functions cannot set floating point error flags, this flag + * gives us the option (not requirement) to skip floating point error + * setup/check. No function should set error flags and ignore them + * since it would interfere with chaining operations (e.g. casting). + */ + NPY_METH_NO_FLOATINGPOINT_ERRORS = 1 << 2, + /* Whether the method supports unaligned access (not runtime) */ + NPY_METH_SUPPORTS_UNALIGNED = 1 << 3, + + /* All flags which can change at runtime */ + NPY_METH_RUNTIME_FLAGS = ( + NPY_METH_REQUIRES_PYAPI | + NPY_METH_NO_FLOATINGPOINT_ERRORS), +} NPY_ARRAYMETHOD_FLAGS; + + +/* + * The main object for creating a new ArrayMethod. We use the typical `slots` + * mechanism used by the Python limited API (see below for the slot defs). + */ +typedef struct { + const char *name; + int nin, nout; + NPY_CASTING casting; + NPY_ARRAYMETHOD_FLAGS flags; + PyObject **dtypes; /* array of DType class objects */ + PyType_Slot *slots; +} PyArrayMethod_Spec; + + +typedef PyObject *_ufunc_addloop_fromspec_func( + PyObject *ufunc, PyArrayMethod_Spec *spec); +/* + * The main ufunc registration function. This adds a new implementation/loop + * to a ufunc. It replaces `PyUFunc_RegisterLoopForType`. + */ +#define PyUFunc_AddLoopFromSpec \ + (*(_ufunc_addloop_fromspec_func *)(__experimental_dtype_api_table[0])) + + +/* + * Type of the C promoter function, which must be wrapped into a + * PyCapsule with name "numpy._ufunc_promoter". + * + * Note that currently the output dtypes are always NULL unless they are + * also part of the signature. This is an implementation detail and could + * change in the future. However, in general promoters should not have a + * need for output dtypes. + * (There are potential use-cases, these are currently unsupported.) + */ +typedef int promoter_function(PyObject *ufunc, + PyArray_DTypeMeta *op_dtypes[], PyArray_DTypeMeta *signature[], + PyArray_DTypeMeta *new_op_dtypes[]); + +/* + * Function to register a promoter. + * + * @param ufunc The ufunc object to register the promoter with. + * @param DType_tuple A Python tuple containing DTypes or None matching the + * number of inputs and outputs of the ufunc. + * @param promoter A PyCapsule with name "numpy._ufunc_promoter" containing + * a pointer to a `promoter_function`. + */ +typedef int _ufunc_addpromoter_func( + PyObject *ufunc, PyObject *DType_tuple, PyObject *promoter); +#define PyUFunc_AddPromoter \ + (*(_ufunc_addpromoter_func *)(__experimental_dtype_api_table[1])) + +/* + * In addition to the normal casting levels, NPY_CAST_IS_VIEW indicates + * that no cast operation is necessary at all (although a copy usually will be) + * + * NOTE: The most likely modification here is to add an additional + * `view_offset` output to resolve_descriptors. If set, it would + * indicate both that it is a view and what offset to use. This means that + * e.g. `arr.imag` could be implemented by an ArrayMethod. + */ +#define NPY_CAST_IS_VIEW _NPY_CAST_IS_VIEW + +/* + * The resolve descriptors function, must be able to handle NULL values for + * all output (but not input) `given_descrs` and fill `loop_descrs`. + * Return -1 on error or 0 if the operation is not possible without an error + * set. (This may still be in flux.) + * Otherwise must return the "casting safety", for normal functions, this is + * almost always "safe" (or even "equivalent"?). + * + * `resolve_descriptors` is optional if all output DTypes are non-parametric. + */ +#define NPY_METH_resolve_descriptors 1 +typedef NPY_CASTING (resolve_descriptors_function)( + /* "method" is currently opaque (necessary e.g. to wrap Python) */ + PyObject *method, + /* DTypes the method was created for */ + PyObject **dtypes, + /* Input descriptors (instances). Outputs may be NULL. */ + PyArray_Descr **given_descrs, + /* Exact loop descriptors to use, must not hold references on error */ + PyArray_Descr **loop_descrs); + +/* NOT public yet: Signature needs adapting as external API. */ +#define _NPY_METH_get_loop 2 + +/* + * Current public API to define fast inner-loops. You must provide a + * strided loop. If this is a cast between two "versions" of the same dtype + * you must also provide an unaligned strided loop. + * Other loops are useful to optimize the very common contiguous case. + * + * NOTE: As of now, NumPy will NOT use unaligned loops in ufuncs! + */ +#define NPY_METH_strided_loop 3 +#define NPY_METH_contiguous_loop 4 +#define NPY_METH_unaligned_strided_loop 5 +#define NPY_METH_unaligned_contiguous_loop 6 + + +typedef struct { + PyObject *caller; /* E.g. the original ufunc, may be NULL */ + PyObject *method; /* The method "self". Currently an opaque object */ + + /* Operand descriptors, filled in by resolve_descriptors */ + PyArray_Descr **descriptors; + /* Structure may grow (this is harmless for DType authors) */ +} PyArrayMethod_Context; + +typedef int (PyArrayMethod_StridedLoop)(PyArrayMethod_Context *context, + char *const *data, const npy_intp *dimensions, const npy_intp *strides, + NpyAuxData *transferdata); + + + +/* + * **************************** + * DTYPE API + * **************************** + */ + +#define NPY_DT_ABSTRACT 1 << 1 +#define NPY_DT_PARAMETRIC 1 << 2 + +#define NPY_DT_discover_descr_from_pyobject 1 +#define _NPY_DT_is_known_scalar_type 2 +#define NPY_DT_default_descr 3 +#define NPY_DT_common_dtype 4 +#define NPY_DT_common_instance 5 +#define NPY_DT_setitem 6 +#define NPY_DT_getitem 7 + + +// TODO: These slots probably still need some thought, and/or a way to "grow"? +typedef struct{ + PyTypeObject *typeobj; /* type of python scalar or NULL */ + int flags; /* flags, including parametric and abstract */ + /* NULL terminated cast definitions. Use NULL for the newly created DType */ + PyArrayMethod_Spec **casts; + PyType_Slot *slots; + /* Baseclass or NULL (will always subclass `np.dtype`) */ + PyTypeObject *baseclass; +} PyArrayDTypeMeta_Spec; + + +#define PyArrayDTypeMeta_Type \ + (*(PyTypeObject *)__experimental_dtype_api_table[2]) +typedef int __dtypemeta_fromspec( + PyArray_DTypeMeta *DType, PyArrayDTypeMeta_Spec *dtype_spec); +/* + * Finalize creation of a DTypeMeta. You must ensure that the DTypeMeta is + * a proper subclass. The DTypeMeta object has additional fields compared to + * a normal PyTypeObject! + * The only (easy) creation of a new DType is to create a static Type which + * inherits `PyArray_DescrType`, sets its type to `PyArrayDTypeMeta_Type` and + * uses `PyArray_DTypeMeta` defined above as the C-structure. + */ +#define PyArrayInitDTypeMeta_FromSpec \ + ((__dtypemeta_fromspec *)(__experimental_dtype_api_table[3])) + + +/* + * ************************************* + * WORKING WITH DTYPES + * ************************************* + */ + +typedef PyArray_DTypeMeta *__common_dtype( + PyArray_DTypeMeta *DType1, PyArray_DTypeMeta *DType2); +#define PyArray_CommonDType \ + ((__common_dtype *)(__experimental_dtype_api_table[4])) + + +typedef PyArray_DTypeMeta *__promote_dtype_sequence( + npy_intp num, PyArray_DTypeMeta *DTypes[]); +#define PyArray_PromoteDTypeSequence \ + ((__promote_dtype_sequence *)(__experimental_dtype_api_table[5])) + + +/* + * ******************************** + * Initialization + * ******************************** + * + * Import the experimental API, the version must match the one defined in + * the header to ensure changes are taken into account. NumPy will further + * runtime-check this. + * You must call this function to use the symbols defined in this file. + */ +#define __EXPERIMENTAL_DTYPE_VERSION 2 + +static int +import_experimental_dtype_api(int version) +{ + if (version != __EXPERIMENTAL_DTYPE_VERSION) { + PyErr_Format(PyExc_RuntimeError, + "DType API version %d did not match header version %d. Please " + "update the import statement and check for API changes.", + version, __EXPERIMENTAL_DTYPE_VERSION); + return -1; + } + if (__experimental_dtype_api_table != __uninitialized_table) { + /* already imported. */ + return 0; + } + + PyObject *multiarray = PyImport_ImportModule("numpy.core._multiarray_umath"); + if (multiarray == NULL) { + return -1; + } + + PyObject *api = PyObject_CallMethod(multiarray, + "_get_experimental_dtype_api", "i", version); + Py_DECREF(multiarray); + if (api == NULL) { + return -1; + } + __experimental_dtype_api_table = PyCapsule_GetPointer(api, + "experimental_dtype_api_table"); + Py_DECREF(api); + + if (__experimental_dtype_api_table == NULL) { + __experimental_dtype_api_table = __uninitialized_table; + return -1; + } + return 0; +} + +#endif /* NUMPY_CORE_INCLUDE_NUMPY_EXPERIMENTAL_DTYPE_API_H_ */ diff --git a/numpy/core/include/numpy/halffloat.h b/numpy/core/include/numpy/halffloat.h index ab0d221fb431..950401664e10 100644 --- a/numpy/core/include/numpy/halffloat.h +++ b/numpy/core/include/numpy/halffloat.h @@ -1,5 +1,5 @@ -#ifndef __NPY_HALFFLOAT_H__ -#define __NPY_HALFFLOAT_H__ +#ifndef NUMPY_CORE_INCLUDE_NUMPY_HALFFLOAT_H_ +#define NUMPY_CORE_INCLUDE_NUMPY_HALFFLOAT_H_ #include <Python.h> #include <numpy/npy_math.h> @@ -67,4 +67,4 @@ npy_uint64 npy_halfbits_to_doublebits(npy_uint16 h); } #endif -#endif +#endif /* NUMPY_CORE_INCLUDE_NUMPY_HALFFLOAT_H_ */ diff --git a/numpy/core/include/numpy/libdivide/LICENSE.txt b/numpy/core/include/numpy/libdivide/LICENSE.txt new file mode 100644 index 000000000000..d72a7c388d40 --- /dev/null +++ b/numpy/core/include/numpy/libdivide/LICENSE.txt @@ -0,0 +1,21 @@ + zlib License + ------------ + + Copyright (C) 2010 - 2019 ridiculous_fish, <libdivide@ridiculousfish.com> + Copyright (C) 2016 - 2019 Kim Walisch, <kim.walisch@gmail.com> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. diff --git a/numpy/core/include/numpy/libdivide/libdivide.h b/numpy/core/include/numpy/libdivide/libdivide.h new file mode 100644 index 000000000000..f4eb8039b50c --- /dev/null +++ b/numpy/core/include/numpy/libdivide/libdivide.h @@ -0,0 +1,2079 @@ +// libdivide.h - Optimized integer division +// https://libdivide.com +// +// Copyright (C) 2010 - 2019 ridiculous_fish, <libdivide@ridiculousfish.com> +// Copyright (C) 2016 - 2019 Kim Walisch, <kim.walisch@gmail.com> +// +// libdivide is dual-licensed under the Boost or zlib licenses. +// You may use libdivide under the terms of either of these. +// See LICENSE.txt for more details. + +#ifndef NUMPY_CORE_INCLUDE_NUMPY_LIBDIVIDE_LIBDIVIDE_H_ +#define NUMPY_CORE_INCLUDE_NUMPY_LIBDIVIDE_LIBDIVIDE_H_ + +#define LIBDIVIDE_VERSION "3.0" +#define LIBDIVIDE_VERSION_MAJOR 3 +#define LIBDIVIDE_VERSION_MINOR 0 + +#include <stdint.h> + +#if defined(__cplusplus) + #include <cstdlib> + #include <cstdio> + #include <type_traits> +#else + #include <stdlib.h> + #include <stdio.h> +#endif + +#if defined(LIBDIVIDE_AVX512) + #include <immintrin.h> +#elif defined(LIBDIVIDE_AVX2) + #include <immintrin.h> +#elif defined(LIBDIVIDE_SSE2) + #include <emmintrin.h> +#endif + +#if defined(_MSC_VER) + #include <intrin.h> + // disable warning C4146: unary minus operator applied + // to unsigned type, result still unsigned + #pragma warning(disable: 4146) + #define LIBDIVIDE_VC +#endif + +#if !defined(__has_builtin) + #define __has_builtin(x) 0 +#endif + +#if defined(__SIZEOF_INT128__) + #define HAS_INT128_T + // clang-cl on Windows does not yet support 128-bit division + #if !(defined(__clang__) && defined(LIBDIVIDE_VC)) + #define HAS_INT128_DIV + #endif +#endif + +#if defined(__x86_64__) || defined(_M_X64) + #define LIBDIVIDE_X86_64 +#endif + +#if defined(__i386__) + #define LIBDIVIDE_i386 +#endif + +#if defined(__GNUC__) || defined(__clang__) + #define LIBDIVIDE_GCC_STYLE_ASM +#endif + +#if defined(__cplusplus) || defined(LIBDIVIDE_VC) + #define LIBDIVIDE_FUNCTION __FUNCTION__ +#else + #define LIBDIVIDE_FUNCTION __func__ +#endif + +#define LIBDIVIDE_ERROR(msg) \ + do { \ + fprintf(stderr, "libdivide.h:%d: %s(): Error: %s\n", \ + __LINE__, LIBDIVIDE_FUNCTION, msg); \ + abort(); \ + } while (0) + +#if defined(LIBDIVIDE_ASSERTIONS_ON) + #define LIBDIVIDE_ASSERT(x) \ + do { \ + if (!(x)) { \ + fprintf(stderr, "libdivide.h:%d: %s(): Assertion failed: %s\n", \ + __LINE__, LIBDIVIDE_FUNCTION, #x); \ + abort(); \ + } \ + } while (0) +#else + #define LIBDIVIDE_ASSERT(x) +#endif + +#ifdef __cplusplus +namespace libdivide { +#endif + +// pack divider structs to prevent compilers from padding. +// This reduces memory usage by up to 43% when using a large +// array of libdivide dividers and improves performance +// by up to 10% because of reduced memory bandwidth. +#pragma pack(push, 1) + +struct libdivide_u32_t { + uint32_t magic; + uint8_t more; +}; + +struct libdivide_s32_t { + int32_t magic; + uint8_t more; +}; + +struct libdivide_u64_t { + uint64_t magic; + uint8_t more; +}; + +struct libdivide_s64_t { + int64_t magic; + uint8_t more; +}; + +struct libdivide_u32_branchfree_t { + uint32_t magic; + uint8_t more; +}; + +struct libdivide_s32_branchfree_t { + int32_t magic; + uint8_t more; +}; + +struct libdivide_u64_branchfree_t { + uint64_t magic; + uint8_t more; +}; + +struct libdivide_s64_branchfree_t { + int64_t magic; + uint8_t more; +}; + +#pragma pack(pop) + +// Explanation of the "more" field: +// +// * Bits 0-5 is the shift value (for shift path or mult path). +// * Bit 6 is the add indicator for mult path. +// * Bit 7 is set if the divisor is negative. We use bit 7 as the negative +// divisor indicator so that we can efficiently use sign extension to +// create a bitmask with all bits set to 1 (if the divisor is negative) +// or 0 (if the divisor is positive). +// +// u32: [0-4] shift value +// [5] ignored +// [6] add indicator +// magic number of 0 indicates shift path +// +// s32: [0-4] shift value +// [5] ignored +// [6] add indicator +// [7] indicates negative divisor +// magic number of 0 indicates shift path +// +// u64: [0-5] shift value +// [6] add indicator +// magic number of 0 indicates shift path +// +// s64: [0-5] shift value +// [6] add indicator +// [7] indicates negative divisor +// magic number of 0 indicates shift path +// +// In s32 and s64 branchfree modes, the magic number is negated according to +// whether the divisor is negated. In branchfree strategy, it is not negated. + +enum { + LIBDIVIDE_32_SHIFT_MASK = 0x1F, + LIBDIVIDE_64_SHIFT_MASK = 0x3F, + LIBDIVIDE_ADD_MARKER = 0x40, + LIBDIVIDE_NEGATIVE_DIVISOR = 0x80 +}; + +static inline struct libdivide_s32_t libdivide_s32_gen(int32_t d); +static inline struct libdivide_u32_t libdivide_u32_gen(uint32_t d); +static inline struct libdivide_s64_t libdivide_s64_gen(int64_t d); +static inline struct libdivide_u64_t libdivide_u64_gen(uint64_t d); + +static inline struct libdivide_s32_branchfree_t libdivide_s32_branchfree_gen(int32_t d); +static inline struct libdivide_u32_branchfree_t libdivide_u32_branchfree_gen(uint32_t d); +static inline struct libdivide_s64_branchfree_t libdivide_s64_branchfree_gen(int64_t d); +static inline struct libdivide_u64_branchfree_t libdivide_u64_branchfree_gen(uint64_t d); + +static inline int32_t libdivide_s32_do(int32_t numer, const struct libdivide_s32_t *denom); +static inline uint32_t libdivide_u32_do(uint32_t numer, const struct libdivide_u32_t *denom); +static inline int64_t libdivide_s64_do(int64_t numer, const struct libdivide_s64_t *denom); +static inline uint64_t libdivide_u64_do(uint64_t numer, const struct libdivide_u64_t *denom); + +static inline int32_t libdivide_s32_branchfree_do(int32_t numer, const struct libdivide_s32_branchfree_t *denom); +static inline uint32_t libdivide_u32_branchfree_do(uint32_t numer, const struct libdivide_u32_branchfree_t *denom); +static inline int64_t libdivide_s64_branchfree_do(int64_t numer, const struct libdivide_s64_branchfree_t *denom); +static inline uint64_t libdivide_u64_branchfree_do(uint64_t numer, const struct libdivide_u64_branchfree_t *denom); + +static inline int32_t libdivide_s32_recover(const struct libdivide_s32_t *denom); +static inline uint32_t libdivide_u32_recover(const struct libdivide_u32_t *denom); +static inline int64_t libdivide_s64_recover(const struct libdivide_s64_t *denom); +static inline uint64_t libdivide_u64_recover(const struct libdivide_u64_t *denom); + +static inline int32_t libdivide_s32_branchfree_recover(const struct libdivide_s32_branchfree_t *denom); +static inline uint32_t libdivide_u32_branchfree_recover(const struct libdivide_u32_branchfree_t *denom); +static inline int64_t libdivide_s64_branchfree_recover(const struct libdivide_s64_branchfree_t *denom); +static inline uint64_t libdivide_u64_branchfree_recover(const struct libdivide_u64_branchfree_t *denom); + +//////// Internal Utility Functions + +static inline uint32_t libdivide_mullhi_u32(uint32_t x, uint32_t y) { + uint64_t xl = x, yl = y; + uint64_t rl = xl * yl; + return (uint32_t)(rl >> 32); +} + +static inline int32_t libdivide_mullhi_s32(int32_t x, int32_t y) { + int64_t xl = x, yl = y; + int64_t rl = xl * yl; + // needs to be arithmetic shift + return (int32_t)(rl >> 32); +} + +static inline uint64_t libdivide_mullhi_u64(uint64_t x, uint64_t y) { +#if defined(LIBDIVIDE_VC) && \ + defined(LIBDIVIDE_X86_64) + return __umulh(x, y); +#elif defined(HAS_INT128_T) + __uint128_t xl = x, yl = y; + __uint128_t rl = xl * yl; + return (uint64_t)(rl >> 64); +#else + // full 128 bits are x0 * y0 + (x0 * y1 << 32) + (x1 * y0 << 32) + (x1 * y1 << 64) + uint32_t mask = 0xFFFFFFFF; + uint32_t x0 = (uint32_t)(x & mask); + uint32_t x1 = (uint32_t)(x >> 32); + uint32_t y0 = (uint32_t)(y & mask); + uint32_t y1 = (uint32_t)(y >> 32); + uint32_t x0y0_hi = libdivide_mullhi_u32(x0, y0); + uint64_t x0y1 = x0 * (uint64_t)y1; + uint64_t x1y0 = x1 * (uint64_t)y0; + uint64_t x1y1 = x1 * (uint64_t)y1; + uint64_t temp = x1y0 + x0y0_hi; + uint64_t temp_lo = temp & mask; + uint64_t temp_hi = temp >> 32; + + return x1y1 + temp_hi + ((temp_lo + x0y1) >> 32); +#endif +} + +static inline int64_t libdivide_mullhi_s64(int64_t x, int64_t y) { +#if defined(LIBDIVIDE_VC) && \ + defined(LIBDIVIDE_X86_64) + return __mulh(x, y); +#elif defined(HAS_INT128_T) + __int128_t xl = x, yl = y; + __int128_t rl = xl * yl; + return (int64_t)(rl >> 64); +#else + // full 128 bits are x0 * y0 + (x0 * y1 << 32) + (x1 * y0 << 32) + (x1 * y1 << 64) + uint32_t mask = 0xFFFFFFFF; + uint32_t x0 = (uint32_t)(x & mask); + uint32_t y0 = (uint32_t)(y & mask); + int32_t x1 = (int32_t)(x >> 32); + int32_t y1 = (int32_t)(y >> 32); + uint32_t x0y0_hi = libdivide_mullhi_u32(x0, y0); + int64_t t = x1 * (int64_t)y0 + x0y0_hi; + int64_t w1 = x0 * (int64_t)y1 + (t & mask); + + return x1 * (int64_t)y1 + (t >> 32) + (w1 >> 32); +#endif +} + +static inline int32_t libdivide_count_leading_zeros32(uint32_t val) { +#if defined(__GNUC__) || \ + __has_builtin(__builtin_clz) + // Fast way to count leading zeros + return __builtin_clz(val); +#elif defined(LIBDIVIDE_VC) + unsigned long result; + if (_BitScanReverse(&result, val)) { + return 31 - result; + } + return 0; +#else + if (val == 0) + return 32; + int32_t result = 8; + uint32_t hi = 0xFFU << 24; + while ((val & hi) == 0) { + hi >>= 8; + result += 8; + } + while (val & hi) { + result -= 1; + hi <<= 1; + } + return result; +#endif +} + +static inline int32_t libdivide_count_leading_zeros64(uint64_t val) { +#if defined(__GNUC__) || \ + __has_builtin(__builtin_clzll) + // Fast way to count leading zeros + return __builtin_clzll(val); +#elif defined(LIBDIVIDE_VC) && defined(_WIN64) + unsigned long result; + if (_BitScanReverse64(&result, val)) { + return 63 - result; + } + return 0; +#else + uint32_t hi = val >> 32; + uint32_t lo = val & 0xFFFFFFFF; + if (hi != 0) return libdivide_count_leading_zeros32(hi); + return 32 + libdivide_count_leading_zeros32(lo); +#endif +} + +// libdivide_64_div_32_to_32: divides a 64-bit uint {u1, u0} by a 32-bit +// uint {v}. The result must fit in 32 bits. +// Returns the quotient directly and the remainder in *r +static inline uint32_t libdivide_64_div_32_to_32(uint32_t u1, uint32_t u0, uint32_t v, uint32_t *r) { +#if (defined(LIBDIVIDE_i386) || defined(LIBDIVIDE_X86_64)) && \ + defined(LIBDIVIDE_GCC_STYLE_ASM) + uint32_t result; + __asm__("divl %[v]" + : "=a"(result), "=d"(*r) + : [v] "r"(v), "a"(u0), "d"(u1) + ); + return result; +#else + uint64_t n = ((uint64_t)u1 << 32) | u0; + uint32_t result = (uint32_t)(n / v); + *r = (uint32_t)(n - result * (uint64_t)v); + return result; +#endif +} + +// libdivide_128_div_64_to_64: divides a 128-bit uint {u1, u0} by a 64-bit +// uint {v}. The result must fit in 64 bits. +// Returns the quotient directly and the remainder in *r +static uint64_t libdivide_128_div_64_to_64(uint64_t u1, uint64_t u0, uint64_t v, uint64_t *r) { +#if defined(LIBDIVIDE_X86_64) && \ + defined(LIBDIVIDE_GCC_STYLE_ASM) + uint64_t result; + __asm__("divq %[v]" + : "=a"(result), "=d"(*r) + : [v] "r"(v), "a"(u0), "d"(u1) + ); + return result; +#elif defined(HAS_INT128_T) && \ + defined(HAS_INT128_DIV) + __uint128_t n = ((__uint128_t)u1 << 64) | u0; + uint64_t result = (uint64_t)(n / v); + *r = (uint64_t)(n - result * (__uint128_t)v); + return result; +#else + // Code taken from Hacker's Delight: + // http://www.hackersdelight.org/HDcode/divlu.c. + // License permits inclusion here per: + // http://www.hackersdelight.org/permissions.htm + + const uint64_t b = (1ULL << 32); // Number base (32 bits) + uint64_t un1, un0; // Norm. dividend LSD's + uint64_t vn1, vn0; // Norm. divisor digits + uint64_t q1, q0; // Quotient digits + uint64_t un64, un21, un10; // Dividend digit pairs + uint64_t rhat; // A remainder + int32_t s; // Shift amount for norm + + // If overflow, set rem. to an impossible value, + // and return the largest possible quotient + if (u1 >= v) { + *r = (uint64_t) -1; + return (uint64_t) -1; + } + + // count leading zeros + s = libdivide_count_leading_zeros64(v); + if (s > 0) { + // Normalize divisor + v = v << s; + un64 = (u1 << s) | (u0 >> (64 - s)); + un10 = u0 << s; // Shift dividend left + } else { + // Avoid undefined behavior of (u0 >> 64). + // The behavior is undefined if the right operand is + // negative, or greater than or equal to the length + // in bits of the promoted left operand. + un64 = u1; + un10 = u0; + } + + // Break divisor up into two 32-bit digits + vn1 = v >> 32; + vn0 = v & 0xFFFFFFFF; + + // Break right half of dividend into two digits + un1 = un10 >> 32; + un0 = un10 & 0xFFFFFFFF; + + // Compute the first quotient digit, q1 + q1 = un64 / vn1; + rhat = un64 - q1 * vn1; + + while (q1 >= b || q1 * vn0 > b * rhat + un1) { + q1 = q1 - 1; + rhat = rhat + vn1; + if (rhat >= b) + break; + } + + // Multiply and subtract + un21 = un64 * b + un1 - q1 * v; + + // Compute the second quotient digit + q0 = un21 / vn1; + rhat = un21 - q0 * vn1; + + while (q0 >= b || q0 * vn0 > b * rhat + un0) { + q0 = q0 - 1; + rhat = rhat + vn1; + if (rhat >= b) + break; + } + + *r = (un21 * b + un0 - q0 * v) >> s; + return q1 * b + q0; +#endif +} + +// Bitshift a u128 in place, left (signed_shift > 0) or right (signed_shift < 0) +static inline void libdivide_u128_shift(uint64_t *u1, uint64_t *u0, int32_t signed_shift) { + if (signed_shift > 0) { + uint32_t shift = signed_shift; + *u1 <<= shift; + *u1 |= *u0 >> (64 - shift); + *u0 <<= shift; + } + else if (signed_shift < 0) { + uint32_t shift = -signed_shift; + *u0 >>= shift; + *u0 |= *u1 << (64 - shift); + *u1 >>= shift; + } +} + +// Computes a 128 / 128 -> 64 bit division, with a 128 bit remainder. +static uint64_t libdivide_128_div_128_to_64(uint64_t u_hi, uint64_t u_lo, uint64_t v_hi, uint64_t v_lo, uint64_t *r_hi, uint64_t *r_lo) { +#if defined(HAS_INT128_T) && \ + defined(HAS_INT128_DIV) + __uint128_t ufull = u_hi; + __uint128_t vfull = v_hi; + ufull = (ufull << 64) | u_lo; + vfull = (vfull << 64) | v_lo; + uint64_t res = (uint64_t)(ufull / vfull); + __uint128_t remainder = ufull - (vfull * res); + *r_lo = (uint64_t)remainder; + *r_hi = (uint64_t)(remainder >> 64); + return res; +#else + // Adapted from "Unsigned Doubleword Division" in Hacker's Delight + // We want to compute u / v + typedef struct { uint64_t hi; uint64_t lo; } u128_t; + u128_t u = {u_hi, u_lo}; + u128_t v = {v_hi, v_lo}; + + if (v.hi == 0) { + // divisor v is a 64 bit value, so we just need one 128/64 division + // Note that we are simpler than Hacker's Delight here, because we know + // the quotient fits in 64 bits whereas Hacker's Delight demands a full + // 128 bit quotient + *r_hi = 0; + return libdivide_128_div_64_to_64(u.hi, u.lo, v.lo, r_lo); + } + // Here v >= 2**64 + // We know that v.hi != 0, so count leading zeros is OK + // We have 0 <= n <= 63 + uint32_t n = libdivide_count_leading_zeros64(v.hi); + + // Normalize the divisor so its MSB is 1 + u128_t v1t = v; + libdivide_u128_shift(&v1t.hi, &v1t.lo, n); + uint64_t v1 = v1t.hi; // i.e. v1 = v1t >> 64 + + // To ensure no overflow + u128_t u1 = u; + libdivide_u128_shift(&u1.hi, &u1.lo, -1); + + // Get quotient from divide unsigned insn. + uint64_t rem_ignored; + uint64_t q1 = libdivide_128_div_64_to_64(u1.hi, u1.lo, v1, &rem_ignored); + + // Undo normalization and division of u by 2. + u128_t q0 = {0, q1}; + libdivide_u128_shift(&q0.hi, &q0.lo, n); + libdivide_u128_shift(&q0.hi, &q0.lo, -63); + + // Make q0 correct or too small by 1 + // Equivalent to `if (q0 != 0) q0 = q0 - 1;` + if (q0.hi != 0 || q0.lo != 0) { + q0.hi -= (q0.lo == 0); // borrow + q0.lo -= 1; + } + + // Now q0 is correct. + // Compute q0 * v as q0v + // = (q0.hi << 64 + q0.lo) * (v.hi << 64 + v.lo) + // = (q0.hi * v.hi << 128) + (q0.hi * v.lo << 64) + + // (q0.lo * v.hi << 64) + q0.lo * v.lo) + // Each term is 128 bit + // High half of full product (upper 128 bits!) are dropped + u128_t q0v = {0, 0}; + q0v.hi = q0.hi*v.lo + q0.lo*v.hi + libdivide_mullhi_u64(q0.lo, v.lo); + q0v.lo = q0.lo*v.lo; + + // Compute u - q0v as u_q0v + // This is the remainder + u128_t u_q0v = u; + u_q0v.hi -= q0v.hi + (u.lo < q0v.lo); // second term is borrow + u_q0v.lo -= q0v.lo; + + // Check if u_q0v >= v + // This checks if our remainder is larger than the divisor + if ((u_q0v.hi > v.hi) || + (u_q0v.hi == v.hi && u_q0v.lo >= v.lo)) { + // Increment q0 + q0.lo += 1; + q0.hi += (q0.lo == 0); // carry + + // Subtract v from remainder + u_q0v.hi -= v.hi + (u_q0v.lo < v.lo); + u_q0v.lo -= v.lo; + } + + *r_hi = u_q0v.hi; + *r_lo = u_q0v.lo; + + LIBDIVIDE_ASSERT(q0.hi == 0); + return q0.lo; +#endif +} + +////////// UINT32 + +static inline struct libdivide_u32_t libdivide_internal_u32_gen(uint32_t d, int branchfree) { + if (d == 0) { + LIBDIVIDE_ERROR("divider must be != 0"); + } + + struct libdivide_u32_t result; + uint32_t floor_log_2_d = 31 - libdivide_count_leading_zeros32(d); + + // Power of 2 + if ((d & (d - 1)) == 0) { + // We need to subtract 1 from the shift value in case of an unsigned + // branchfree divider because there is a hardcoded right shift by 1 + // in its division algorithm. Because of this we also need to add back + // 1 in its recovery algorithm. + result.magic = 0; + result.more = (uint8_t)(floor_log_2_d - (branchfree != 0)); + } else { + uint8_t more; + uint32_t rem, proposed_m; + proposed_m = libdivide_64_div_32_to_32(1U << floor_log_2_d, 0, d, &rem); + + LIBDIVIDE_ASSERT(rem > 0 && rem < d); + const uint32_t e = d - rem; + + // This power works if e < 2**floor_log_2_d. + if (!branchfree && (e < (1U << floor_log_2_d))) { + // This power works + more = floor_log_2_d; + } else { + // We have to use the general 33-bit algorithm. We need to compute + // (2**power) / d. However, we already have (2**(power-1))/d and + // its remainder. By doubling both, and then correcting the + // remainder, we can compute the larger division. + // don't care about overflow here - in fact, we expect it + proposed_m += proposed_m; + const uint32_t twice_rem = rem + rem; + if (twice_rem >= d || twice_rem < rem) proposed_m += 1; + more = floor_log_2_d | LIBDIVIDE_ADD_MARKER; + } + result.magic = 1 + proposed_m; + result.more = more; + // result.more's shift should in general be ceil_log_2_d. But if we + // used the smaller power, we subtract one from the shift because we're + // using the smaller power. If we're using the larger power, we + // subtract one from the shift because it's taken care of by the add + // indicator. So floor_log_2_d happens to be correct in both cases. + } + return result; +} + +struct libdivide_u32_t libdivide_u32_gen(uint32_t d) { + return libdivide_internal_u32_gen(d, 0); +} + +struct libdivide_u32_branchfree_t libdivide_u32_branchfree_gen(uint32_t d) { + if (d == 1) { + LIBDIVIDE_ERROR("branchfree divider must be != 1"); + } + struct libdivide_u32_t tmp = libdivide_internal_u32_gen(d, 1); + struct libdivide_u32_branchfree_t ret = {tmp.magic, (uint8_t)(tmp.more & LIBDIVIDE_32_SHIFT_MASK)}; + return ret; +} + +uint32_t libdivide_u32_do(uint32_t numer, const struct libdivide_u32_t *denom) { + uint8_t more = denom->more; + if (!denom->magic) { + return numer >> more; + } + else { + uint32_t q = libdivide_mullhi_u32(denom->magic, numer); + if (more & LIBDIVIDE_ADD_MARKER) { + uint32_t t = ((numer - q) >> 1) + q; + return t >> (more & LIBDIVIDE_32_SHIFT_MASK); + } + else { + // All upper bits are 0, + // don't need to mask them off. + return q >> more; + } + } +} + +uint32_t libdivide_u32_branchfree_do(uint32_t numer, const struct libdivide_u32_branchfree_t *denom) { + uint32_t q = libdivide_mullhi_u32(denom->magic, numer); + uint32_t t = ((numer - q) >> 1) + q; + return t >> denom->more; +} + +uint32_t libdivide_u32_recover(const struct libdivide_u32_t *denom) { + uint8_t more = denom->more; + uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK; + + if (!denom->magic) { + return 1U << shift; + } else if (!(more & LIBDIVIDE_ADD_MARKER)) { + // We compute q = n/d = n*m / 2^(32 + shift) + // Therefore we have d = 2^(32 + shift) / m + // We need to ceil it. + // We know d is not a power of 2, so m is not a power of 2, + // so we can just add 1 to the floor + uint32_t hi_dividend = 1U << shift; + uint32_t rem_ignored; + return 1 + libdivide_64_div_32_to_32(hi_dividend, 0, denom->magic, &rem_ignored); + } else { + // Here we wish to compute d = 2^(32+shift+1)/(m+2^32). + // Notice (m + 2^32) is a 33 bit number. Use 64 bit division for now + // Also note that shift may be as high as 31, so shift + 1 will + // overflow. So we have to compute it as 2^(32+shift)/(m+2^32), and + // then double the quotient and remainder. + uint64_t half_n = 1ULL << (32 + shift); + uint64_t d = (1ULL << 32) | denom->magic; + // Note that the quotient is guaranteed <= 32 bits, but the remainder + // may need 33! + uint32_t half_q = (uint32_t)(half_n / d); + uint64_t rem = half_n % d; + // We computed 2^(32+shift)/(m+2^32) + // Need to double it, and then add 1 to the quotient if doubling th + // remainder would increase the quotient. + // Note that rem<<1 cannot overflow, since rem < d and d is 33 bits + uint32_t full_q = half_q + half_q + ((rem<<1) >= d); + + // We rounded down in gen (hence +1) + return full_q + 1; + } +} + +uint32_t libdivide_u32_branchfree_recover(const struct libdivide_u32_branchfree_t *denom) { + uint8_t more = denom->more; + uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK; + + if (!denom->magic) { + return 1U << (shift + 1); + } else { + // Here we wish to compute d = 2^(32+shift+1)/(m+2^32). + // Notice (m + 2^32) is a 33 bit number. Use 64 bit division for now + // Also note that shift may be as high as 31, so shift + 1 will + // overflow. So we have to compute it as 2^(32+shift)/(m+2^32), and + // then double the quotient and remainder. + uint64_t half_n = 1ULL << (32 + shift); + uint64_t d = (1ULL << 32) | denom->magic; + // Note that the quotient is guaranteed <= 32 bits, but the remainder + // may need 33! + uint32_t half_q = (uint32_t)(half_n / d); + uint64_t rem = half_n % d; + // We computed 2^(32+shift)/(m+2^32) + // Need to double it, and then add 1 to the quotient if doubling th + // remainder would increase the quotient. + // Note that rem<<1 cannot overflow, since rem < d and d is 33 bits + uint32_t full_q = half_q + half_q + ((rem<<1) >= d); + + // We rounded down in gen (hence +1) + return full_q + 1; + } +} + +/////////// UINT64 + +static inline struct libdivide_u64_t libdivide_internal_u64_gen(uint64_t d, int branchfree) { + if (d == 0) { + LIBDIVIDE_ERROR("divider must be != 0"); + } + + struct libdivide_u64_t result; + uint32_t floor_log_2_d = 63 - libdivide_count_leading_zeros64(d); + + // Power of 2 + if ((d & (d - 1)) == 0) { + // We need to subtract 1 from the shift value in case of an unsigned + // branchfree divider because there is a hardcoded right shift by 1 + // in its division algorithm. Because of this we also need to add back + // 1 in its recovery algorithm. + result.magic = 0; + result.more = (uint8_t)(floor_log_2_d - (branchfree != 0)); + } else { + uint64_t proposed_m, rem; + uint8_t more; + // (1 << (64 + floor_log_2_d)) / d + proposed_m = libdivide_128_div_64_to_64(1ULL << floor_log_2_d, 0, d, &rem); + + LIBDIVIDE_ASSERT(rem > 0 && rem < d); + const uint64_t e = d - rem; + + // This power works if e < 2**floor_log_2_d. + if (!branchfree && e < (1ULL << floor_log_2_d)) { + // This power works + more = floor_log_2_d; + } else { + // We have to use the general 65-bit algorithm. We need to compute + // (2**power) / d. However, we already have (2**(power-1))/d and + // its remainder. By doubling both, and then correcting the + // remainder, we can compute the larger division. + // don't care about overflow here - in fact, we expect it + proposed_m += proposed_m; + const uint64_t twice_rem = rem + rem; + if (twice_rem >= d || twice_rem < rem) proposed_m += 1; + more = floor_log_2_d | LIBDIVIDE_ADD_MARKER; + } + result.magic = 1 + proposed_m; + result.more = more; + // result.more's shift should in general be ceil_log_2_d. But if we + // used the smaller power, we subtract one from the shift because we're + // using the smaller power. If we're using the larger power, we + // subtract one from the shift because it's taken care of by the add + // indicator. So floor_log_2_d happens to be correct in both cases, + // which is why we do it outside of the if statement. + } + return result; +} + +struct libdivide_u64_t libdivide_u64_gen(uint64_t d) { + return libdivide_internal_u64_gen(d, 0); +} + +struct libdivide_u64_branchfree_t libdivide_u64_branchfree_gen(uint64_t d) { + if (d == 1) { + LIBDIVIDE_ERROR("branchfree divider must be != 1"); + } + struct libdivide_u64_t tmp = libdivide_internal_u64_gen(d, 1); + struct libdivide_u64_branchfree_t ret = {tmp.magic, (uint8_t)(tmp.more & LIBDIVIDE_64_SHIFT_MASK)}; + return ret; +} + +uint64_t libdivide_u64_do(uint64_t numer, const struct libdivide_u64_t *denom) { + uint8_t more = denom->more; + if (!denom->magic) { + return numer >> more; + } + else { + uint64_t q = libdivide_mullhi_u64(denom->magic, numer); + if (more & LIBDIVIDE_ADD_MARKER) { + uint64_t t = ((numer - q) >> 1) + q; + return t >> (more & LIBDIVIDE_64_SHIFT_MASK); + } + else { + // All upper bits are 0, + // don't need to mask them off. + return q >> more; + } + } +} + +uint64_t libdivide_u64_branchfree_do(uint64_t numer, const struct libdivide_u64_branchfree_t *denom) { + uint64_t q = libdivide_mullhi_u64(denom->magic, numer); + uint64_t t = ((numer - q) >> 1) + q; + return t >> denom->more; +} + +uint64_t libdivide_u64_recover(const struct libdivide_u64_t *denom) { + uint8_t more = denom->more; + uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK; + + if (!denom->magic) { + return 1ULL << shift; + } else if (!(more & LIBDIVIDE_ADD_MARKER)) { + // We compute q = n/d = n*m / 2^(64 + shift) + // Therefore we have d = 2^(64 + shift) / m + // We need to ceil it. + // We know d is not a power of 2, so m is not a power of 2, + // so we can just add 1 to the floor + uint64_t hi_dividend = 1ULL << shift; + uint64_t rem_ignored; + return 1 + libdivide_128_div_64_to_64(hi_dividend, 0, denom->magic, &rem_ignored); + } else { + // Here we wish to compute d = 2^(64+shift+1)/(m+2^64). + // Notice (m + 2^64) is a 65 bit number. This gets hairy. See + // libdivide_u32_recover for more on what we do here. + // TODO: do something better than 128 bit math + + // Full n is a (potentially) 129 bit value + // half_n is a 128 bit value + // Compute the hi half of half_n. Low half is 0. + uint64_t half_n_hi = 1ULL << shift, half_n_lo = 0; + // d is a 65 bit value. The high bit is always set to 1. + const uint64_t d_hi = 1, d_lo = denom->magic; + // Note that the quotient is guaranteed <= 64 bits, + // but the remainder may need 65! + uint64_t r_hi, r_lo; + uint64_t half_q = libdivide_128_div_128_to_64(half_n_hi, half_n_lo, d_hi, d_lo, &r_hi, &r_lo); + // We computed 2^(64+shift)/(m+2^64) + // Double the remainder ('dr') and check if that is larger than d + // Note that d is a 65 bit value, so r1 is small and so r1 + r1 + // cannot overflow + uint64_t dr_lo = r_lo + r_lo; + uint64_t dr_hi = r_hi + r_hi + (dr_lo < r_lo); // last term is carry + int dr_exceeds_d = (dr_hi > d_hi) || (dr_hi == d_hi && dr_lo >= d_lo); + uint64_t full_q = half_q + half_q + (dr_exceeds_d ? 1 : 0); + return full_q + 1; + } +} + +uint64_t libdivide_u64_branchfree_recover(const struct libdivide_u64_branchfree_t *denom) { + uint8_t more = denom->more; + uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK; + + if (!denom->magic) { + return 1ULL << (shift + 1); + } else { + // Here we wish to compute d = 2^(64+shift+1)/(m+2^64). + // Notice (m + 2^64) is a 65 bit number. This gets hairy. See + // libdivide_u32_recover for more on what we do here. + // TODO: do something better than 128 bit math + + // Full n is a (potentially) 129 bit value + // half_n is a 128 bit value + // Compute the hi half of half_n. Low half is 0. + uint64_t half_n_hi = 1ULL << shift, half_n_lo = 0; + // d is a 65 bit value. The high bit is always set to 1. + const uint64_t d_hi = 1, d_lo = denom->magic; + // Note that the quotient is guaranteed <= 64 bits, + // but the remainder may need 65! + uint64_t r_hi, r_lo; + uint64_t half_q = libdivide_128_div_128_to_64(half_n_hi, half_n_lo, d_hi, d_lo, &r_hi, &r_lo); + // We computed 2^(64+shift)/(m+2^64) + // Double the remainder ('dr') and check if that is larger than d + // Note that d is a 65 bit value, so r1 is small and so r1 + r1 + // cannot overflow + uint64_t dr_lo = r_lo + r_lo; + uint64_t dr_hi = r_hi + r_hi + (dr_lo < r_lo); // last term is carry + int dr_exceeds_d = (dr_hi > d_hi) || (dr_hi == d_hi && dr_lo >= d_lo); + uint64_t full_q = half_q + half_q + (dr_exceeds_d ? 1 : 0); + return full_q + 1; + } +} + +/////////// SINT32 + +static inline struct libdivide_s32_t libdivide_internal_s32_gen(int32_t d, int branchfree) { + if (d == 0) { + LIBDIVIDE_ERROR("divider must be != 0"); + } + + struct libdivide_s32_t result; + + // If d is a power of 2, or negative a power of 2, we have to use a shift. + // This is especially important because the magic algorithm fails for -1. + // To check if d is a power of 2 or its inverse, it suffices to check + // whether its absolute value has exactly one bit set. This works even for + // INT_MIN, because abs(INT_MIN) == INT_MIN, and INT_MIN has one bit set + // and is a power of 2. + uint32_t ud = (uint32_t)d; + uint32_t absD = (d < 0) ? -ud : ud; + uint32_t floor_log_2_d = 31 - libdivide_count_leading_zeros32(absD); + // check if exactly one bit is set, + // don't care if absD is 0 since that's divide by zero + if ((absD & (absD - 1)) == 0) { + // Branchfree and normal paths are exactly the same + result.magic = 0; + result.more = floor_log_2_d | (d < 0 ? LIBDIVIDE_NEGATIVE_DIVISOR : 0); + } else { + LIBDIVIDE_ASSERT(floor_log_2_d >= 1); + + uint8_t more; + // the dividend here is 2**(floor_log_2_d + 31), so the low 32 bit word + // is 0 and the high word is floor_log_2_d - 1 + uint32_t rem, proposed_m; + proposed_m = libdivide_64_div_32_to_32(1U << (floor_log_2_d - 1), 0, absD, &rem); + const uint32_t e = absD - rem; + + // We are going to start with a power of floor_log_2_d - 1. + // This works if works if e < 2**floor_log_2_d. + if (!branchfree && e < (1U << floor_log_2_d)) { + // This power works + more = floor_log_2_d - 1; + } else { + // We need to go one higher. This should not make proposed_m + // overflow, but it will make it negative when interpreted as an + // int32_t. + proposed_m += proposed_m; + const uint32_t twice_rem = rem + rem; + if (twice_rem >= absD || twice_rem < rem) proposed_m += 1; + more = floor_log_2_d | LIBDIVIDE_ADD_MARKER; + } + + proposed_m += 1; + int32_t magic = (int32_t)proposed_m; + + // Mark if we are negative. Note we only negate the magic number in the + // branchfull case. + if (d < 0) { + more |= LIBDIVIDE_NEGATIVE_DIVISOR; + if (!branchfree) { + magic = -magic; + } + } + + result.more = more; + result.magic = magic; + } + return result; +} + +struct libdivide_s32_t libdivide_s32_gen(int32_t d) { + return libdivide_internal_s32_gen(d, 0); +} + +struct libdivide_s32_branchfree_t libdivide_s32_branchfree_gen(int32_t d) { + struct libdivide_s32_t tmp = libdivide_internal_s32_gen(d, 1); + struct libdivide_s32_branchfree_t result = {tmp.magic, tmp.more}; + return result; +} + +int32_t libdivide_s32_do(int32_t numer, const struct libdivide_s32_t *denom) { + uint8_t more = denom->more; + uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK; + + if (!denom->magic) { + uint32_t sign = (int8_t)more >> 7; + uint32_t mask = (1U << shift) - 1; + uint32_t uq = numer + ((numer >> 31) & mask); + int32_t q = (int32_t)uq; + q >>= shift; + q = (q ^ sign) - sign; + return q; + } else { + uint32_t uq = (uint32_t)libdivide_mullhi_s32(denom->magic, numer); + if (more & LIBDIVIDE_ADD_MARKER) { + // must be arithmetic shift and then sign extend + int32_t sign = (int8_t)more >> 7; + // q += (more < 0 ? -numer : numer) + // cast required to avoid UB + uq += ((uint32_t)numer ^ sign) - sign; + } + int32_t q = (int32_t)uq; + q >>= shift; + q += (q < 0); + return q; + } +} + +int32_t libdivide_s32_branchfree_do(int32_t numer, const struct libdivide_s32_branchfree_t *denom) { + uint8_t more = denom->more; + uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK; + // must be arithmetic shift and then sign extend + int32_t sign = (int8_t)more >> 7; + int32_t magic = denom->magic; + int32_t q = libdivide_mullhi_s32(magic, numer); + q += numer; + + // If q is non-negative, we have nothing to do + // If q is negative, we want to add either (2**shift)-1 if d is a power of + // 2, or (2**shift) if it is not a power of 2 + uint32_t is_power_of_2 = (magic == 0); + uint32_t q_sign = (uint32_t)(q >> 31); + q += q_sign & ((1U << shift) - is_power_of_2); + + // Now arithmetic right shift + q >>= shift; + // Negate if needed + q = (q ^ sign) - sign; + + return q; +} + +int32_t libdivide_s32_recover(const struct libdivide_s32_t *denom) { + uint8_t more = denom->more; + uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK; + if (!denom->magic) { + uint32_t absD = 1U << shift; + if (more & LIBDIVIDE_NEGATIVE_DIVISOR) { + absD = -absD; + } + return (int32_t)absD; + } else { + // Unsigned math is much easier + // We negate the magic number only in the branchfull case, and we don't + // know which case we're in. However we have enough information to + // determine the correct sign of the magic number. The divisor was + // negative if LIBDIVIDE_NEGATIVE_DIVISOR is set. If ADD_MARKER is set, + // the magic number's sign is opposite that of the divisor. + // We want to compute the positive magic number. + int negative_divisor = (more & LIBDIVIDE_NEGATIVE_DIVISOR); + int magic_was_negated = (more & LIBDIVIDE_ADD_MARKER) + ? denom->magic > 0 : denom->magic < 0; + + // Handle the power of 2 case (including branchfree) + if (denom->magic == 0) { + int32_t result = 1U << shift; + return negative_divisor ? -result : result; + } + + uint32_t d = (uint32_t)(magic_was_negated ? -denom->magic : denom->magic); + uint64_t n = 1ULL << (32 + shift); // this shift cannot exceed 30 + uint32_t q = (uint32_t)(n / d); + int32_t result = (int32_t)q; + result += 1; + return negative_divisor ? -result : result; + } +} + +int32_t libdivide_s32_branchfree_recover(const struct libdivide_s32_branchfree_t *denom) { + return libdivide_s32_recover((const struct libdivide_s32_t *)denom); +} + +///////////// SINT64 + +static inline struct libdivide_s64_t libdivide_internal_s64_gen(int64_t d, int branchfree) { + if (d == 0) { + LIBDIVIDE_ERROR("divider must be != 0"); + } + + struct libdivide_s64_t result; + + // If d is a power of 2, or negative a power of 2, we have to use a shift. + // This is especially important because the magic algorithm fails for -1. + // To check if d is a power of 2 or its inverse, it suffices to check + // whether its absolute value has exactly one bit set. This works even for + // INT_MIN, because abs(INT_MIN) == INT_MIN, and INT_MIN has one bit set + // and is a power of 2. + uint64_t ud = (uint64_t)d; + uint64_t absD = (d < 0) ? -ud : ud; + uint32_t floor_log_2_d = 63 - libdivide_count_leading_zeros64(absD); + // check if exactly one bit is set, + // don't care if absD is 0 since that's divide by zero + if ((absD & (absD - 1)) == 0) { + // Branchfree and non-branchfree cases are the same + result.magic = 0; + result.more = floor_log_2_d | (d < 0 ? LIBDIVIDE_NEGATIVE_DIVISOR : 0); + } else { + // the dividend here is 2**(floor_log_2_d + 63), so the low 64 bit word + // is 0 and the high word is floor_log_2_d - 1 + uint8_t more; + uint64_t rem, proposed_m; + proposed_m = libdivide_128_div_64_to_64(1ULL << (floor_log_2_d - 1), 0, absD, &rem); + const uint64_t e = absD - rem; + + // We are going to start with a power of floor_log_2_d - 1. + // This works if works if e < 2**floor_log_2_d. + if (!branchfree && e < (1ULL << floor_log_2_d)) { + // This power works + more = floor_log_2_d - 1; + } else { + // We need to go one higher. This should not make proposed_m + // overflow, but it will make it negative when interpreted as an + // int32_t. + proposed_m += proposed_m; + const uint64_t twice_rem = rem + rem; + if (twice_rem >= absD || twice_rem < rem) proposed_m += 1; + // note that we only set the LIBDIVIDE_NEGATIVE_DIVISOR bit if we + // also set ADD_MARKER this is an annoying optimization that + // enables algorithm #4 to avoid the mask. However we always set it + // in the branchfree case + more = floor_log_2_d | LIBDIVIDE_ADD_MARKER; + } + proposed_m += 1; + int64_t magic = (int64_t)proposed_m; + + // Mark if we are negative + if (d < 0) { + more |= LIBDIVIDE_NEGATIVE_DIVISOR; + if (!branchfree) { + magic = -magic; + } + } + + result.more = more; + result.magic = magic; + } + return result; +} + +struct libdivide_s64_t libdivide_s64_gen(int64_t d) { + return libdivide_internal_s64_gen(d, 0); +} + +struct libdivide_s64_branchfree_t libdivide_s64_branchfree_gen(int64_t d) { + struct libdivide_s64_t tmp = libdivide_internal_s64_gen(d, 1); + struct libdivide_s64_branchfree_t ret = {tmp.magic, tmp.more}; + return ret; +} + +int64_t libdivide_s64_do(int64_t numer, const struct libdivide_s64_t *denom) { + uint8_t more = denom->more; + uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK; + + if (!denom->magic) { // shift path + uint64_t mask = (1ULL << shift) - 1; + uint64_t uq = numer + ((numer >> 63) & mask); + int64_t q = (int64_t)uq; + q >>= shift; + // must be arithmetic shift and then sign-extend + int64_t sign = (int8_t)more >> 7; + q = (q ^ sign) - sign; + return q; + } else { + uint64_t uq = (uint64_t)libdivide_mullhi_s64(denom->magic, numer); + if (more & LIBDIVIDE_ADD_MARKER) { + // must be arithmetic shift and then sign extend + int64_t sign = (int8_t)more >> 7; + // q += (more < 0 ? -numer : numer) + // cast required to avoid UB + uq += ((uint64_t)numer ^ sign) - sign; + } + int64_t q = (int64_t)uq; + q >>= shift; + q += (q < 0); + return q; + } +} + +int64_t libdivide_s64_branchfree_do(int64_t numer, const struct libdivide_s64_branchfree_t *denom) { + uint8_t more = denom->more; + uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK; + // must be arithmetic shift and then sign extend + int64_t sign = (int8_t)more >> 7; + int64_t magic = denom->magic; + int64_t q = libdivide_mullhi_s64(magic, numer); + q += numer; + + // If q is non-negative, we have nothing to do. + // If q is negative, we want to add either (2**shift)-1 if d is a power of + // 2, or (2**shift) if it is not a power of 2. + uint64_t is_power_of_2 = (magic == 0); + uint64_t q_sign = (uint64_t)(q >> 63); + q += q_sign & ((1ULL << shift) - is_power_of_2); + + // Arithmetic right shift + q >>= shift; + // Negate if needed + q = (q ^ sign) - sign; + + return q; +} + +int64_t libdivide_s64_recover(const struct libdivide_s64_t *denom) { + uint8_t more = denom->more; + uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK; + if (denom->magic == 0) { // shift path + uint64_t absD = 1ULL << shift; + if (more & LIBDIVIDE_NEGATIVE_DIVISOR) { + absD = -absD; + } + return (int64_t)absD; + } else { + // Unsigned math is much easier + int negative_divisor = (more & LIBDIVIDE_NEGATIVE_DIVISOR); + int magic_was_negated = (more & LIBDIVIDE_ADD_MARKER) + ? denom->magic > 0 : denom->magic < 0; + + uint64_t d = (uint64_t)(magic_was_negated ? -denom->magic : denom->magic); + uint64_t n_hi = 1ULL << shift, n_lo = 0; + uint64_t rem_ignored; + uint64_t q = libdivide_128_div_64_to_64(n_hi, n_lo, d, &rem_ignored); + int64_t result = (int64_t)(q + 1); + if (negative_divisor) { + result = -result; + } + return result; + } +} + +int64_t libdivide_s64_branchfree_recover(const struct libdivide_s64_branchfree_t *denom) { + return libdivide_s64_recover((const struct libdivide_s64_t *)denom); +} + +#if defined(LIBDIVIDE_AVX512) + +static inline __m512i libdivide_u32_do_vector(__m512i numers, const struct libdivide_u32_t *denom); +static inline __m512i libdivide_s32_do_vector(__m512i numers, const struct libdivide_s32_t *denom); +static inline __m512i libdivide_u64_do_vector(__m512i numers, const struct libdivide_u64_t *denom); +static inline __m512i libdivide_s64_do_vector(__m512i numers, const struct libdivide_s64_t *denom); + +static inline __m512i libdivide_u32_branchfree_do_vector(__m512i numers, const struct libdivide_u32_branchfree_t *denom); +static inline __m512i libdivide_s32_branchfree_do_vector(__m512i numers, const struct libdivide_s32_branchfree_t *denom); +static inline __m512i libdivide_u64_branchfree_do_vector(__m512i numers, const struct libdivide_u64_branchfree_t *denom); +static inline __m512i libdivide_s64_branchfree_do_vector(__m512i numers, const struct libdivide_s64_branchfree_t *denom); + +//////// Internal Utility Functions + +static inline __m512i libdivide_s64_signbits(__m512i v) {; + return _mm512_srai_epi64(v, 63); +} + +static inline __m512i libdivide_s64_shift_right_vector(__m512i v, int amt) { + return _mm512_srai_epi64(v, amt); +} + +// Here, b is assumed to contain one 32-bit value repeated. +static inline __m512i libdivide_mullhi_u32_vector(__m512i a, __m512i b) { + __m512i hi_product_0Z2Z = _mm512_srli_epi64(_mm512_mul_epu32(a, b), 32); + __m512i a1X3X = _mm512_srli_epi64(a, 32); + __m512i mask = _mm512_set_epi32(-1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0); + __m512i hi_product_Z1Z3 = _mm512_and_si512(_mm512_mul_epu32(a1X3X, b), mask); + return _mm512_or_si512(hi_product_0Z2Z, hi_product_Z1Z3); +} + +// b is one 32-bit value repeated. +static inline __m512i libdivide_mullhi_s32_vector(__m512i a, __m512i b) { + __m512i hi_product_0Z2Z = _mm512_srli_epi64(_mm512_mul_epi32(a, b), 32); + __m512i a1X3X = _mm512_srli_epi64(a, 32); + __m512i mask = _mm512_set_epi32(-1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0); + __m512i hi_product_Z1Z3 = _mm512_and_si512(_mm512_mul_epi32(a1X3X, b), mask); + return _mm512_or_si512(hi_product_0Z2Z, hi_product_Z1Z3); +} + +// Here, y is assumed to contain one 64-bit value repeated. +// https://stackoverflow.com/a/28827013 +static inline __m512i libdivide_mullhi_u64_vector(__m512i x, __m512i y) { + __m512i lomask = _mm512_set1_epi64(0xffffffff); + __m512i xh = _mm512_shuffle_epi32(x, (_MM_PERM_ENUM) 0xB1); + __m512i yh = _mm512_shuffle_epi32(y, (_MM_PERM_ENUM) 0xB1); + __m512i w0 = _mm512_mul_epu32(x, y); + __m512i w1 = _mm512_mul_epu32(x, yh); + __m512i w2 = _mm512_mul_epu32(xh, y); + __m512i w3 = _mm512_mul_epu32(xh, yh); + __m512i w0h = _mm512_srli_epi64(w0, 32); + __m512i s1 = _mm512_add_epi64(w1, w0h); + __m512i s1l = _mm512_and_si512(s1, lomask); + __m512i s1h = _mm512_srli_epi64(s1, 32); + __m512i s2 = _mm512_add_epi64(w2, s1l); + __m512i s2h = _mm512_srli_epi64(s2, 32); + __m512i hi = _mm512_add_epi64(w3, s1h); + hi = _mm512_add_epi64(hi, s2h); + + return hi; +} + +// y is one 64-bit value repeated. +static inline __m512i libdivide_mullhi_s64_vector(__m512i x, __m512i y) { + __m512i p = libdivide_mullhi_u64_vector(x, y); + __m512i t1 = _mm512_and_si512(libdivide_s64_signbits(x), y); + __m512i t2 = _mm512_and_si512(libdivide_s64_signbits(y), x); + p = _mm512_sub_epi64(p, t1); + p = _mm512_sub_epi64(p, t2); + return p; +} + +////////// UINT32 + +__m512i libdivide_u32_do_vector(__m512i numers, const struct libdivide_u32_t *denom) { + uint8_t more = denom->more; + if (!denom->magic) { + return _mm512_srli_epi32(numers, more); + } + else { + __m512i q = libdivide_mullhi_u32_vector(numers, _mm512_set1_epi32(denom->magic)); + if (more & LIBDIVIDE_ADD_MARKER) { + // uint32_t t = ((numer - q) >> 1) + q; + // return t >> denom->shift; + uint32_t shift = more & LIBDIVIDE_32_SHIFT_MASK; + __m512i t = _mm512_add_epi32(_mm512_srli_epi32(_mm512_sub_epi32(numers, q), 1), q); + return _mm512_srli_epi32(t, shift); + } + else { + return _mm512_srli_epi32(q, more); + } + } +} + +__m512i libdivide_u32_branchfree_do_vector(__m512i numers, const struct libdivide_u32_branchfree_t *denom) { + __m512i q = libdivide_mullhi_u32_vector(numers, _mm512_set1_epi32(denom->magic)); + __m512i t = _mm512_add_epi32(_mm512_srli_epi32(_mm512_sub_epi32(numers, q), 1), q); + return _mm512_srli_epi32(t, denom->more); +} + +////////// UINT64 + +__m512i libdivide_u64_do_vector(__m512i numers, const struct libdivide_u64_t *denom) { + uint8_t more = denom->more; + if (!denom->magic) { + return _mm512_srli_epi64(numers, more); + } + else { + __m512i q = libdivide_mullhi_u64_vector(numers, _mm512_set1_epi64(denom->magic)); + if (more & LIBDIVIDE_ADD_MARKER) { + // uint32_t t = ((numer - q) >> 1) + q; + // return t >> denom->shift; + uint32_t shift = more & LIBDIVIDE_64_SHIFT_MASK; + __m512i t = _mm512_add_epi64(_mm512_srli_epi64(_mm512_sub_epi64(numers, q), 1), q); + return _mm512_srli_epi64(t, shift); + } + else { + return _mm512_srli_epi64(q, more); + } + } +} + +__m512i libdivide_u64_branchfree_do_vector(__m512i numers, const struct libdivide_u64_branchfree_t *denom) { + __m512i q = libdivide_mullhi_u64_vector(numers, _mm512_set1_epi64(denom->magic)); + __m512i t = _mm512_add_epi64(_mm512_srli_epi64(_mm512_sub_epi64(numers, q), 1), q); + return _mm512_srli_epi64(t, denom->more); +} + +////////// SINT32 + +__m512i libdivide_s32_do_vector(__m512i numers, const struct libdivide_s32_t *denom) { + uint8_t more = denom->more; + if (!denom->magic) { + uint32_t shift = more & LIBDIVIDE_32_SHIFT_MASK; + uint32_t mask = (1U << shift) - 1; + __m512i roundToZeroTweak = _mm512_set1_epi32(mask); + // q = numer + ((numer >> 31) & roundToZeroTweak); + __m512i q = _mm512_add_epi32(numers, _mm512_and_si512(_mm512_srai_epi32(numers, 31), roundToZeroTweak)); + q = _mm512_srai_epi32(q, shift); + __m512i sign = _mm512_set1_epi32((int8_t)more >> 7); + // q = (q ^ sign) - sign; + q = _mm512_sub_epi32(_mm512_xor_si512(q, sign), sign); + return q; + } + else { + __m512i q = libdivide_mullhi_s32_vector(numers, _mm512_set1_epi32(denom->magic)); + if (more & LIBDIVIDE_ADD_MARKER) { + // must be arithmetic shift + __m512i sign = _mm512_set1_epi32((int8_t)more >> 7); + // q += ((numer ^ sign) - sign); + q = _mm512_add_epi32(q, _mm512_sub_epi32(_mm512_xor_si512(numers, sign), sign)); + } + // q >>= shift + q = _mm512_srai_epi32(q, more & LIBDIVIDE_32_SHIFT_MASK); + q = _mm512_add_epi32(q, _mm512_srli_epi32(q, 31)); // q += (q < 0) + return q; + } +} + +__m512i libdivide_s32_branchfree_do_vector(__m512i numers, const struct libdivide_s32_branchfree_t *denom) { + int32_t magic = denom->magic; + uint8_t more = denom->more; + uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK; + // must be arithmetic shift + __m512i sign = _mm512_set1_epi32((int8_t)more >> 7); + __m512i q = libdivide_mullhi_s32_vector(numers, _mm512_set1_epi32(magic)); + q = _mm512_add_epi32(q, numers); // q += numers + + // If q is non-negative, we have nothing to do + // If q is negative, we want to add either (2**shift)-1 if d is + // a power of 2, or (2**shift) if it is not a power of 2 + uint32_t is_power_of_2 = (magic == 0); + __m512i q_sign = _mm512_srai_epi32(q, 31); // q_sign = q >> 31 + __m512i mask = _mm512_set1_epi32((1U << shift) - is_power_of_2); + q = _mm512_add_epi32(q, _mm512_and_si512(q_sign, mask)); // q = q + (q_sign & mask) + q = _mm512_srai_epi32(q, shift); // q >>= shift + q = _mm512_sub_epi32(_mm512_xor_si512(q, sign), sign); // q = (q ^ sign) - sign + return q; +} + +////////// SINT64 + +__m512i libdivide_s64_do_vector(__m512i numers, const struct libdivide_s64_t *denom) { + uint8_t more = denom->more; + int64_t magic = denom->magic; + if (magic == 0) { // shift path + uint32_t shift = more & LIBDIVIDE_64_SHIFT_MASK; + uint64_t mask = (1ULL << shift) - 1; + __m512i roundToZeroTweak = _mm512_set1_epi64(mask); + // q = numer + ((numer >> 63) & roundToZeroTweak); + __m512i q = _mm512_add_epi64(numers, _mm512_and_si512(libdivide_s64_signbits(numers), roundToZeroTweak)); + q = libdivide_s64_shift_right_vector(q, shift); + __m512i sign = _mm512_set1_epi32((int8_t)more >> 7); + // q = (q ^ sign) - sign; + q = _mm512_sub_epi64(_mm512_xor_si512(q, sign), sign); + return q; + } + else { + __m512i q = libdivide_mullhi_s64_vector(numers, _mm512_set1_epi64(magic)); + if (more & LIBDIVIDE_ADD_MARKER) { + // must be arithmetic shift + __m512i sign = _mm512_set1_epi32((int8_t)more >> 7); + // q += ((numer ^ sign) - sign); + q = _mm512_add_epi64(q, _mm512_sub_epi64(_mm512_xor_si512(numers, sign), sign)); + } + // q >>= denom->mult_path.shift + q = libdivide_s64_shift_right_vector(q, more & LIBDIVIDE_64_SHIFT_MASK); + q = _mm512_add_epi64(q, _mm512_srli_epi64(q, 63)); // q += (q < 0) + return q; + } +} + +__m512i libdivide_s64_branchfree_do_vector(__m512i numers, const struct libdivide_s64_branchfree_t *denom) { + int64_t magic = denom->magic; + uint8_t more = denom->more; + uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK; + // must be arithmetic shift + __m512i sign = _mm512_set1_epi32((int8_t)more >> 7); + + // libdivide_mullhi_s64(numers, magic); + __m512i q = libdivide_mullhi_s64_vector(numers, _mm512_set1_epi64(magic)); + q = _mm512_add_epi64(q, numers); // q += numers + + // If q is non-negative, we have nothing to do. + // If q is negative, we want to add either (2**shift)-1 if d is + // a power of 2, or (2**shift) if it is not a power of 2. + uint32_t is_power_of_2 = (magic == 0); + __m512i q_sign = libdivide_s64_signbits(q); // q_sign = q >> 63 + __m512i mask = _mm512_set1_epi64((1ULL << shift) - is_power_of_2); + q = _mm512_add_epi64(q, _mm512_and_si512(q_sign, mask)); // q = q + (q_sign & mask) + q = libdivide_s64_shift_right_vector(q, shift); // q >>= shift + q = _mm512_sub_epi64(_mm512_xor_si512(q, sign), sign); // q = (q ^ sign) - sign + return q; +} + +#elif defined(LIBDIVIDE_AVX2) + +static inline __m256i libdivide_u32_do_vector(__m256i numers, const struct libdivide_u32_t *denom); +static inline __m256i libdivide_s32_do_vector(__m256i numers, const struct libdivide_s32_t *denom); +static inline __m256i libdivide_u64_do_vector(__m256i numers, const struct libdivide_u64_t *denom); +static inline __m256i libdivide_s64_do_vector(__m256i numers, const struct libdivide_s64_t *denom); + +static inline __m256i libdivide_u32_branchfree_do_vector(__m256i numers, const struct libdivide_u32_branchfree_t *denom); +static inline __m256i libdivide_s32_branchfree_do_vector(__m256i numers, const struct libdivide_s32_branchfree_t *denom); +static inline __m256i libdivide_u64_branchfree_do_vector(__m256i numers, const struct libdivide_u64_branchfree_t *denom); +static inline __m256i libdivide_s64_branchfree_do_vector(__m256i numers, const struct libdivide_s64_branchfree_t *denom); + +//////// Internal Utility Functions + +// Implementation of _mm256_srai_epi64(v, 63) (from AVX512). +static inline __m256i libdivide_s64_signbits(__m256i v) { + __m256i hiBitsDuped = _mm256_shuffle_epi32(v, _MM_SHUFFLE(3, 3, 1, 1)); + __m256i signBits = _mm256_srai_epi32(hiBitsDuped, 31); + return signBits; +} + +// Implementation of _mm256_srai_epi64 (from AVX512). +static inline __m256i libdivide_s64_shift_right_vector(__m256i v, int amt) { + const int b = 64 - amt; + __m256i m = _mm256_set1_epi64x(1ULL << (b - 1)); + __m256i x = _mm256_srli_epi64(v, amt); + __m256i result = _mm256_sub_epi64(_mm256_xor_si256(x, m), m); + return result; +} + +// Here, b is assumed to contain one 32-bit value repeated. +static inline __m256i libdivide_mullhi_u32_vector(__m256i a, __m256i b) { + __m256i hi_product_0Z2Z = _mm256_srli_epi64(_mm256_mul_epu32(a, b), 32); + __m256i a1X3X = _mm256_srli_epi64(a, 32); + __m256i mask = _mm256_set_epi32(-1, 0, -1, 0, -1, 0, -1, 0); + __m256i hi_product_Z1Z3 = _mm256_and_si256(_mm256_mul_epu32(a1X3X, b), mask); + return _mm256_or_si256(hi_product_0Z2Z, hi_product_Z1Z3); +} + +// b is one 32-bit value repeated. +static inline __m256i libdivide_mullhi_s32_vector(__m256i a, __m256i b) { + __m256i hi_product_0Z2Z = _mm256_srli_epi64(_mm256_mul_epi32(a, b), 32); + __m256i a1X3X = _mm256_srli_epi64(a, 32); + __m256i mask = _mm256_set_epi32(-1, 0, -1, 0, -1, 0, -1, 0); + __m256i hi_product_Z1Z3 = _mm256_and_si256(_mm256_mul_epi32(a1X3X, b), mask); + return _mm256_or_si256(hi_product_0Z2Z, hi_product_Z1Z3); +} + +// Here, y is assumed to contain one 64-bit value repeated. +// https://stackoverflow.com/a/28827013 +static inline __m256i libdivide_mullhi_u64_vector(__m256i x, __m256i y) { + __m256i lomask = _mm256_set1_epi64x(0xffffffff); + __m256i xh = _mm256_shuffle_epi32(x, 0xB1); // x0l, x0h, x1l, x1h + __m256i yh = _mm256_shuffle_epi32(y, 0xB1); // y0l, y0h, y1l, y1h + __m256i w0 = _mm256_mul_epu32(x, y); // x0l*y0l, x1l*y1l + __m256i w1 = _mm256_mul_epu32(x, yh); // x0l*y0h, x1l*y1h + __m256i w2 = _mm256_mul_epu32(xh, y); // x0h*y0l, x1h*y0l + __m256i w3 = _mm256_mul_epu32(xh, yh); // x0h*y0h, x1h*y1h + __m256i w0h = _mm256_srli_epi64(w0, 32); + __m256i s1 = _mm256_add_epi64(w1, w0h); + __m256i s1l = _mm256_and_si256(s1, lomask); + __m256i s1h = _mm256_srli_epi64(s1, 32); + __m256i s2 = _mm256_add_epi64(w2, s1l); + __m256i s2h = _mm256_srli_epi64(s2, 32); + __m256i hi = _mm256_add_epi64(w3, s1h); + hi = _mm256_add_epi64(hi, s2h); + + return hi; +} + +// y is one 64-bit value repeated. +static inline __m256i libdivide_mullhi_s64_vector(__m256i x, __m256i y) { + __m256i p = libdivide_mullhi_u64_vector(x, y); + __m256i t1 = _mm256_and_si256(libdivide_s64_signbits(x), y); + __m256i t2 = _mm256_and_si256(libdivide_s64_signbits(y), x); + p = _mm256_sub_epi64(p, t1); + p = _mm256_sub_epi64(p, t2); + return p; +} + +////////// UINT32 + +__m256i libdivide_u32_do_vector(__m256i numers, const struct libdivide_u32_t *denom) { + uint8_t more = denom->more; + if (!denom->magic) { + return _mm256_srli_epi32(numers, more); + } + else { + __m256i q = libdivide_mullhi_u32_vector(numers, _mm256_set1_epi32(denom->magic)); + if (more & LIBDIVIDE_ADD_MARKER) { + // uint32_t t = ((numer - q) >> 1) + q; + // return t >> denom->shift; + uint32_t shift = more & LIBDIVIDE_32_SHIFT_MASK; + __m256i t = _mm256_add_epi32(_mm256_srli_epi32(_mm256_sub_epi32(numers, q), 1), q); + return _mm256_srli_epi32(t, shift); + } + else { + return _mm256_srli_epi32(q, more); + } + } +} + +__m256i libdivide_u32_branchfree_do_vector(__m256i numers, const struct libdivide_u32_branchfree_t *denom) { + __m256i q = libdivide_mullhi_u32_vector(numers, _mm256_set1_epi32(denom->magic)); + __m256i t = _mm256_add_epi32(_mm256_srli_epi32(_mm256_sub_epi32(numers, q), 1), q); + return _mm256_srli_epi32(t, denom->more); +} + +////////// UINT64 + +__m256i libdivide_u64_do_vector(__m256i numers, const struct libdivide_u64_t *denom) { + uint8_t more = denom->more; + if (!denom->magic) { + return _mm256_srli_epi64(numers, more); + } + else { + __m256i q = libdivide_mullhi_u64_vector(numers, _mm256_set1_epi64x(denom->magic)); + if (more & LIBDIVIDE_ADD_MARKER) { + // uint32_t t = ((numer - q) >> 1) + q; + // return t >> denom->shift; + uint32_t shift = more & LIBDIVIDE_64_SHIFT_MASK; + __m256i t = _mm256_add_epi64(_mm256_srli_epi64(_mm256_sub_epi64(numers, q), 1), q); + return _mm256_srli_epi64(t, shift); + } + else { + return _mm256_srli_epi64(q, more); + } + } +} + +__m256i libdivide_u64_branchfree_do_vector(__m256i numers, const struct libdivide_u64_branchfree_t *denom) { + __m256i q = libdivide_mullhi_u64_vector(numers, _mm256_set1_epi64x(denom->magic)); + __m256i t = _mm256_add_epi64(_mm256_srli_epi64(_mm256_sub_epi64(numers, q), 1), q); + return _mm256_srli_epi64(t, denom->more); +} + +////////// SINT32 + +__m256i libdivide_s32_do_vector(__m256i numers, const struct libdivide_s32_t *denom) { + uint8_t more = denom->more; + if (!denom->magic) { + uint32_t shift = more & LIBDIVIDE_32_SHIFT_MASK; + uint32_t mask = (1U << shift) - 1; + __m256i roundToZeroTweak = _mm256_set1_epi32(mask); + // q = numer + ((numer >> 31) & roundToZeroTweak); + __m256i q = _mm256_add_epi32(numers, _mm256_and_si256(_mm256_srai_epi32(numers, 31), roundToZeroTweak)); + q = _mm256_srai_epi32(q, shift); + __m256i sign = _mm256_set1_epi32((int8_t)more >> 7); + // q = (q ^ sign) - sign; + q = _mm256_sub_epi32(_mm256_xor_si256(q, sign), sign); + return q; + } + else { + __m256i q = libdivide_mullhi_s32_vector(numers, _mm256_set1_epi32(denom->magic)); + if (more & LIBDIVIDE_ADD_MARKER) { + // must be arithmetic shift + __m256i sign = _mm256_set1_epi32((int8_t)more >> 7); + // q += ((numer ^ sign) - sign); + q = _mm256_add_epi32(q, _mm256_sub_epi32(_mm256_xor_si256(numers, sign), sign)); + } + // q >>= shift + q = _mm256_srai_epi32(q, more & LIBDIVIDE_32_SHIFT_MASK); + q = _mm256_add_epi32(q, _mm256_srli_epi32(q, 31)); // q += (q < 0) + return q; + } +} + +__m256i libdivide_s32_branchfree_do_vector(__m256i numers, const struct libdivide_s32_branchfree_t *denom) { + int32_t magic = denom->magic; + uint8_t more = denom->more; + uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK; + // must be arithmetic shift + __m256i sign = _mm256_set1_epi32((int8_t)more >> 7); + __m256i q = libdivide_mullhi_s32_vector(numers, _mm256_set1_epi32(magic)); + q = _mm256_add_epi32(q, numers); // q += numers + + // If q is non-negative, we have nothing to do + // If q is negative, we want to add either (2**shift)-1 if d is + // a power of 2, or (2**shift) if it is not a power of 2 + uint32_t is_power_of_2 = (magic == 0); + __m256i q_sign = _mm256_srai_epi32(q, 31); // q_sign = q >> 31 + __m256i mask = _mm256_set1_epi32((1U << shift) - is_power_of_2); + q = _mm256_add_epi32(q, _mm256_and_si256(q_sign, mask)); // q = q + (q_sign & mask) + q = _mm256_srai_epi32(q, shift); // q >>= shift + q = _mm256_sub_epi32(_mm256_xor_si256(q, sign), sign); // q = (q ^ sign) - sign + return q; +} + +////////// SINT64 + +__m256i libdivide_s64_do_vector(__m256i numers, const struct libdivide_s64_t *denom) { + uint8_t more = denom->more; + int64_t magic = denom->magic; + if (magic == 0) { // shift path + uint32_t shift = more & LIBDIVIDE_64_SHIFT_MASK; + uint64_t mask = (1ULL << shift) - 1; + __m256i roundToZeroTweak = _mm256_set1_epi64x(mask); + // q = numer + ((numer >> 63) & roundToZeroTweak); + __m256i q = _mm256_add_epi64(numers, _mm256_and_si256(libdivide_s64_signbits(numers), roundToZeroTweak)); + q = libdivide_s64_shift_right_vector(q, shift); + __m256i sign = _mm256_set1_epi32((int8_t)more >> 7); + // q = (q ^ sign) - sign; + q = _mm256_sub_epi64(_mm256_xor_si256(q, sign), sign); + return q; + } + else { + __m256i q = libdivide_mullhi_s64_vector(numers, _mm256_set1_epi64x(magic)); + if (more & LIBDIVIDE_ADD_MARKER) { + // must be arithmetic shift + __m256i sign = _mm256_set1_epi32((int8_t)more >> 7); + // q += ((numer ^ sign) - sign); + q = _mm256_add_epi64(q, _mm256_sub_epi64(_mm256_xor_si256(numers, sign), sign)); + } + // q >>= denom->mult_path.shift + q = libdivide_s64_shift_right_vector(q, more & LIBDIVIDE_64_SHIFT_MASK); + q = _mm256_add_epi64(q, _mm256_srli_epi64(q, 63)); // q += (q < 0) + return q; + } +} + +__m256i libdivide_s64_branchfree_do_vector(__m256i numers, const struct libdivide_s64_branchfree_t *denom) { + int64_t magic = denom->magic; + uint8_t more = denom->more; + uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK; + // must be arithmetic shift + __m256i sign = _mm256_set1_epi32((int8_t)more >> 7); + + // libdivide_mullhi_s64(numers, magic); + __m256i q = libdivide_mullhi_s64_vector(numers, _mm256_set1_epi64x(magic)); + q = _mm256_add_epi64(q, numers); // q += numers + + // If q is non-negative, we have nothing to do. + // If q is negative, we want to add either (2**shift)-1 if d is + // a power of 2, or (2**shift) if it is not a power of 2. + uint32_t is_power_of_2 = (magic == 0); + __m256i q_sign = libdivide_s64_signbits(q); // q_sign = q >> 63 + __m256i mask = _mm256_set1_epi64x((1ULL << shift) - is_power_of_2); + q = _mm256_add_epi64(q, _mm256_and_si256(q_sign, mask)); // q = q + (q_sign & mask) + q = libdivide_s64_shift_right_vector(q, shift); // q >>= shift + q = _mm256_sub_epi64(_mm256_xor_si256(q, sign), sign); // q = (q ^ sign) - sign + return q; +} + +#elif defined(LIBDIVIDE_SSE2) + +static inline __m128i libdivide_u32_do_vector(__m128i numers, const struct libdivide_u32_t *denom); +static inline __m128i libdivide_s32_do_vector(__m128i numers, const struct libdivide_s32_t *denom); +static inline __m128i libdivide_u64_do_vector(__m128i numers, const struct libdivide_u64_t *denom); +static inline __m128i libdivide_s64_do_vector(__m128i numers, const struct libdivide_s64_t *denom); + +static inline __m128i libdivide_u32_branchfree_do_vector(__m128i numers, const struct libdivide_u32_branchfree_t *denom); +static inline __m128i libdivide_s32_branchfree_do_vector(__m128i numers, const struct libdivide_s32_branchfree_t *denom); +static inline __m128i libdivide_u64_branchfree_do_vector(__m128i numers, const struct libdivide_u64_branchfree_t *denom); +static inline __m128i libdivide_s64_branchfree_do_vector(__m128i numers, const struct libdivide_s64_branchfree_t *denom); + +//////// Internal Utility Functions + +// Implementation of _mm_srai_epi64(v, 63) (from AVX512). +static inline __m128i libdivide_s64_signbits(__m128i v) { + __m128i hiBitsDuped = _mm_shuffle_epi32(v, _MM_SHUFFLE(3, 3, 1, 1)); + __m128i signBits = _mm_srai_epi32(hiBitsDuped, 31); + return signBits; +} + +// Implementation of _mm_srai_epi64 (from AVX512). +static inline __m128i libdivide_s64_shift_right_vector(__m128i v, int amt) { + const int b = 64 - amt; + __m128i m = _mm_set1_epi64x(1ULL << (b - 1)); + __m128i x = _mm_srli_epi64(v, amt); + __m128i result = _mm_sub_epi64(_mm_xor_si128(x, m), m); + return result; +} + +// Here, b is assumed to contain one 32-bit value repeated. +static inline __m128i libdivide_mullhi_u32_vector(__m128i a, __m128i b) { + __m128i hi_product_0Z2Z = _mm_srli_epi64(_mm_mul_epu32(a, b), 32); + __m128i a1X3X = _mm_srli_epi64(a, 32); + __m128i mask = _mm_set_epi32(-1, 0, -1, 0); + __m128i hi_product_Z1Z3 = _mm_and_si128(_mm_mul_epu32(a1X3X, b), mask); + return _mm_or_si128(hi_product_0Z2Z, hi_product_Z1Z3); +} + +// SSE2 does not have a signed multiplication instruction, but we can convert +// unsigned to signed pretty efficiently. Again, b is just a 32 bit value +// repeated four times. +static inline __m128i libdivide_mullhi_s32_vector(__m128i a, __m128i b) { + __m128i p = libdivide_mullhi_u32_vector(a, b); + // t1 = (a >> 31) & y, arithmetic shift + __m128i t1 = _mm_and_si128(_mm_srai_epi32(a, 31), b); + __m128i t2 = _mm_and_si128(_mm_srai_epi32(b, 31), a); + p = _mm_sub_epi32(p, t1); + p = _mm_sub_epi32(p, t2); + return p; +} + +// Here, y is assumed to contain one 64-bit value repeated. +// https://stackoverflow.com/a/28827013 +static inline __m128i libdivide_mullhi_u64_vector(__m128i x, __m128i y) { + __m128i lomask = _mm_set1_epi64x(0xffffffff); + __m128i xh = _mm_shuffle_epi32(x, 0xB1); // x0l, x0h, x1l, x1h + __m128i yh = _mm_shuffle_epi32(y, 0xB1); // y0l, y0h, y1l, y1h + __m128i w0 = _mm_mul_epu32(x, y); // x0l*y0l, x1l*y1l + __m128i w1 = _mm_mul_epu32(x, yh); // x0l*y0h, x1l*y1h + __m128i w2 = _mm_mul_epu32(xh, y); // x0h*y0l, x1h*y0l + __m128i w3 = _mm_mul_epu32(xh, yh); // x0h*y0h, x1h*y1h + __m128i w0h = _mm_srli_epi64(w0, 32); + __m128i s1 = _mm_add_epi64(w1, w0h); + __m128i s1l = _mm_and_si128(s1, lomask); + __m128i s1h = _mm_srli_epi64(s1, 32); + __m128i s2 = _mm_add_epi64(w2, s1l); + __m128i s2h = _mm_srli_epi64(s2, 32); + __m128i hi = _mm_add_epi64(w3, s1h); + hi = _mm_add_epi64(hi, s2h); + + return hi; +} + +// y is one 64-bit value repeated. +static inline __m128i libdivide_mullhi_s64_vector(__m128i x, __m128i y) { + __m128i p = libdivide_mullhi_u64_vector(x, y); + __m128i t1 = _mm_and_si128(libdivide_s64_signbits(x), y); + __m128i t2 = _mm_and_si128(libdivide_s64_signbits(y), x); + p = _mm_sub_epi64(p, t1); + p = _mm_sub_epi64(p, t2); + return p; +} + +////////// UINT32 + +__m128i libdivide_u32_do_vector(__m128i numers, const struct libdivide_u32_t *denom) { + uint8_t more = denom->more; + if (!denom->magic) { + return _mm_srli_epi32(numers, more); + } + else { + __m128i q = libdivide_mullhi_u32_vector(numers, _mm_set1_epi32(denom->magic)); + if (more & LIBDIVIDE_ADD_MARKER) { + // uint32_t t = ((numer - q) >> 1) + q; + // return t >> denom->shift; + uint32_t shift = more & LIBDIVIDE_32_SHIFT_MASK; + __m128i t = _mm_add_epi32(_mm_srli_epi32(_mm_sub_epi32(numers, q), 1), q); + return _mm_srli_epi32(t, shift); + } + else { + return _mm_srli_epi32(q, more); + } + } +} + +__m128i libdivide_u32_branchfree_do_vector(__m128i numers, const struct libdivide_u32_branchfree_t *denom) { + __m128i q = libdivide_mullhi_u32_vector(numers, _mm_set1_epi32(denom->magic)); + __m128i t = _mm_add_epi32(_mm_srli_epi32(_mm_sub_epi32(numers, q), 1), q); + return _mm_srli_epi32(t, denom->more); +} + +////////// UINT64 + +__m128i libdivide_u64_do_vector(__m128i numers, const struct libdivide_u64_t *denom) { + uint8_t more = denom->more; + if (!denom->magic) { + return _mm_srli_epi64(numers, more); + } + else { + __m128i q = libdivide_mullhi_u64_vector(numers, _mm_set1_epi64x(denom->magic)); + if (more & LIBDIVIDE_ADD_MARKER) { + // uint32_t t = ((numer - q) >> 1) + q; + // return t >> denom->shift; + uint32_t shift = more & LIBDIVIDE_64_SHIFT_MASK; + __m128i t = _mm_add_epi64(_mm_srli_epi64(_mm_sub_epi64(numers, q), 1), q); + return _mm_srli_epi64(t, shift); + } + else { + return _mm_srli_epi64(q, more); + } + } +} + +__m128i libdivide_u64_branchfree_do_vector(__m128i numers, const struct libdivide_u64_branchfree_t *denom) { + __m128i q = libdivide_mullhi_u64_vector(numers, _mm_set1_epi64x(denom->magic)); + __m128i t = _mm_add_epi64(_mm_srli_epi64(_mm_sub_epi64(numers, q), 1), q); + return _mm_srli_epi64(t, denom->more); +} + +////////// SINT32 + +__m128i libdivide_s32_do_vector(__m128i numers, const struct libdivide_s32_t *denom) { + uint8_t more = denom->more; + if (!denom->magic) { + uint32_t shift = more & LIBDIVIDE_32_SHIFT_MASK; + uint32_t mask = (1U << shift) - 1; + __m128i roundToZeroTweak = _mm_set1_epi32(mask); + // q = numer + ((numer >> 31) & roundToZeroTweak); + __m128i q = _mm_add_epi32(numers, _mm_and_si128(_mm_srai_epi32(numers, 31), roundToZeroTweak)); + q = _mm_srai_epi32(q, shift); + __m128i sign = _mm_set1_epi32((int8_t)more >> 7); + // q = (q ^ sign) - sign; + q = _mm_sub_epi32(_mm_xor_si128(q, sign), sign); + return q; + } + else { + __m128i q = libdivide_mullhi_s32_vector(numers, _mm_set1_epi32(denom->magic)); + if (more & LIBDIVIDE_ADD_MARKER) { + // must be arithmetic shift + __m128i sign = _mm_set1_epi32((int8_t)more >> 7); + // q += ((numer ^ sign) - sign); + q = _mm_add_epi32(q, _mm_sub_epi32(_mm_xor_si128(numers, sign), sign)); + } + // q >>= shift + q = _mm_srai_epi32(q, more & LIBDIVIDE_32_SHIFT_MASK); + q = _mm_add_epi32(q, _mm_srli_epi32(q, 31)); // q += (q < 0) + return q; + } +} + +__m128i libdivide_s32_branchfree_do_vector(__m128i numers, const struct libdivide_s32_branchfree_t *denom) { + int32_t magic = denom->magic; + uint8_t more = denom->more; + uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK; + // must be arithmetic shift + __m128i sign = _mm_set1_epi32((int8_t)more >> 7); + __m128i q = libdivide_mullhi_s32_vector(numers, _mm_set1_epi32(magic)); + q = _mm_add_epi32(q, numers); // q += numers + + // If q is non-negative, we have nothing to do + // If q is negative, we want to add either (2**shift)-1 if d is + // a power of 2, or (2**shift) if it is not a power of 2 + uint32_t is_power_of_2 = (magic == 0); + __m128i q_sign = _mm_srai_epi32(q, 31); // q_sign = q >> 31 + __m128i mask = _mm_set1_epi32((1U << shift) - is_power_of_2); + q = _mm_add_epi32(q, _mm_and_si128(q_sign, mask)); // q = q + (q_sign & mask) + q = _mm_srai_epi32(q, shift); // q >>= shift + q = _mm_sub_epi32(_mm_xor_si128(q, sign), sign); // q = (q ^ sign) - sign + return q; +} + +////////// SINT64 + +__m128i libdivide_s64_do_vector(__m128i numers, const struct libdivide_s64_t *denom) { + uint8_t more = denom->more; + int64_t magic = denom->magic; + if (magic == 0) { // shift path + uint32_t shift = more & LIBDIVIDE_64_SHIFT_MASK; + uint64_t mask = (1ULL << shift) - 1; + __m128i roundToZeroTweak = _mm_set1_epi64x(mask); + // q = numer + ((numer >> 63) & roundToZeroTweak); + __m128i q = _mm_add_epi64(numers, _mm_and_si128(libdivide_s64_signbits(numers), roundToZeroTweak)); + q = libdivide_s64_shift_right_vector(q, shift); + __m128i sign = _mm_set1_epi32((int8_t)more >> 7); + // q = (q ^ sign) - sign; + q = _mm_sub_epi64(_mm_xor_si128(q, sign), sign); + return q; + } + else { + __m128i q = libdivide_mullhi_s64_vector(numers, _mm_set1_epi64x(magic)); + if (more & LIBDIVIDE_ADD_MARKER) { + // must be arithmetic shift + __m128i sign = _mm_set1_epi32((int8_t)more >> 7); + // q += ((numer ^ sign) - sign); + q = _mm_add_epi64(q, _mm_sub_epi64(_mm_xor_si128(numers, sign), sign)); + } + // q >>= denom->mult_path.shift + q = libdivide_s64_shift_right_vector(q, more & LIBDIVIDE_64_SHIFT_MASK); + q = _mm_add_epi64(q, _mm_srli_epi64(q, 63)); // q += (q < 0) + return q; + } +} + +__m128i libdivide_s64_branchfree_do_vector(__m128i numers, const struct libdivide_s64_branchfree_t *denom) { + int64_t magic = denom->magic; + uint8_t more = denom->more; + uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK; + // must be arithmetic shift + __m128i sign = _mm_set1_epi32((int8_t)more >> 7); + + // libdivide_mullhi_s64(numers, magic); + __m128i q = libdivide_mullhi_s64_vector(numers, _mm_set1_epi64x(magic)); + q = _mm_add_epi64(q, numers); // q += numers + + // If q is non-negative, we have nothing to do. + // If q is negative, we want to add either (2**shift)-1 if d is + // a power of 2, or (2**shift) if it is not a power of 2. + uint32_t is_power_of_2 = (magic == 0); + __m128i q_sign = libdivide_s64_signbits(q); // q_sign = q >> 63 + __m128i mask = _mm_set1_epi64x((1ULL << shift) - is_power_of_2); + q = _mm_add_epi64(q, _mm_and_si128(q_sign, mask)); // q = q + (q_sign & mask) + q = libdivide_s64_shift_right_vector(q, shift); // q >>= shift + q = _mm_sub_epi64(_mm_xor_si128(q, sign), sign); // q = (q ^ sign) - sign + return q; +} + +#endif + +/////////// C++ stuff + +#ifdef __cplusplus + +// The C++ divider class is templated on both an integer type +// (like uint64_t) and an algorithm type. +// * BRANCHFULL is the default algorithm type. +// * BRANCHFREE is the branchfree algorithm type. +enum { + BRANCHFULL, + BRANCHFREE +}; + +#if defined(LIBDIVIDE_AVX512) + #define LIBDIVIDE_VECTOR_TYPE __m512i +#elif defined(LIBDIVIDE_AVX2) + #define LIBDIVIDE_VECTOR_TYPE __m256i +#elif defined(LIBDIVIDE_SSE2) + #define LIBDIVIDE_VECTOR_TYPE __m128i +#endif + +#if !defined(LIBDIVIDE_VECTOR_TYPE) + #define LIBDIVIDE_DIVIDE_VECTOR(ALGO) +#else + #define LIBDIVIDE_DIVIDE_VECTOR(ALGO) \ + LIBDIVIDE_VECTOR_TYPE divide(LIBDIVIDE_VECTOR_TYPE n) const { \ + return libdivide_##ALGO##_do_vector(n, &denom); \ + } +#endif + +// The DISPATCHER_GEN() macro generates C++ methods (for the given integer +// and algorithm types) that redirect to libdivide's C API. +#define DISPATCHER_GEN(T, ALGO) \ + libdivide_##ALGO##_t denom; \ + dispatcher() { } \ + dispatcher(T d) \ + : denom(libdivide_##ALGO##_gen(d)) \ + { } \ + T divide(T n) const { \ + return libdivide_##ALGO##_do(n, &denom); \ + } \ + LIBDIVIDE_DIVIDE_VECTOR(ALGO) \ + T recover() const { \ + return libdivide_##ALGO##_recover(&denom); \ + } + +// The dispatcher selects a specific division algorithm for a given +// type and ALGO using partial template specialization. +template<bool IS_INTEGRAL, bool IS_SIGNED, int SIZEOF, int ALGO> struct dispatcher { }; + +template<> struct dispatcher<true, true, sizeof(int32_t), BRANCHFULL> { DISPATCHER_GEN(int32_t, s32) }; +template<> struct dispatcher<true, true, sizeof(int32_t), BRANCHFREE> { DISPATCHER_GEN(int32_t, s32_branchfree) }; +template<> struct dispatcher<true, false, sizeof(uint32_t), BRANCHFULL> { DISPATCHER_GEN(uint32_t, u32) }; +template<> struct dispatcher<true, false, sizeof(uint32_t), BRANCHFREE> { DISPATCHER_GEN(uint32_t, u32_branchfree) }; +template<> struct dispatcher<true, true, sizeof(int64_t), BRANCHFULL> { DISPATCHER_GEN(int64_t, s64) }; +template<> struct dispatcher<true, true, sizeof(int64_t), BRANCHFREE> { DISPATCHER_GEN(int64_t, s64_branchfree) }; +template<> struct dispatcher<true, false, sizeof(uint64_t), BRANCHFULL> { DISPATCHER_GEN(uint64_t, u64) }; +template<> struct dispatcher<true, false, sizeof(uint64_t), BRANCHFREE> { DISPATCHER_GEN(uint64_t, u64_branchfree) }; + +// This is the main divider class for use by the user (C++ API). +// The actual division algorithm is selected using the dispatcher struct +// based on the integer and algorithm template parameters. +template<typename T, int ALGO = BRANCHFULL> +class divider { +public: + // We leave the default constructor empty so that creating + // an array of dividers and then initializing them + // later doesn't slow us down. + divider() { } + + // Constructor that takes the divisor as a parameter + divider(T d) : div(d) { } + + // Divides n by the divisor + T divide(T n) const { + return div.divide(n); + } + + // Recovers the divisor, returns the value that was + // used to initialize this divider object. + T recover() const { + return div.recover(); + } + + bool operator==(const divider<T, ALGO>& other) const { + return div.denom.magic == other.denom.magic && + div.denom.more == other.denom.more; + } + + bool operator!=(const divider<T, ALGO>& other) const { + return !(*this == other); + } + +#if defined(LIBDIVIDE_VECTOR_TYPE) + // Treats the vector as packed integer values with the same type as + // the divider (e.g. s32, u32, s64, u64) and divides each of + // them by the divider, returning the packed quotients. + LIBDIVIDE_VECTOR_TYPE divide(LIBDIVIDE_VECTOR_TYPE n) const { + return div.divide(n); + } +#endif + +private: + // Storage for the actual divisor + dispatcher<std::is_integral<T>::value, + std::is_signed<T>::value, sizeof(T), ALGO> div; +}; + +// Overload of operator / for scalar division +template<typename T, int ALGO> +T operator/(T n, const divider<T, ALGO>& div) { + return div.divide(n); +} + +// Overload of operator /= for scalar division +template<typename T, int ALGO> +T& operator/=(T& n, const divider<T, ALGO>& div) { + n = div.divide(n); + return n; +} + +#if defined(LIBDIVIDE_VECTOR_TYPE) + // Overload of operator / for vector division + template<typename T, int ALGO> + LIBDIVIDE_VECTOR_TYPE operator/(LIBDIVIDE_VECTOR_TYPE n, const divider<T, ALGO>& div) { + return div.divide(n); + } + // Overload of operator /= for vector division + template<typename T, int ALGO> + LIBDIVIDE_VECTOR_TYPE& operator/=(LIBDIVIDE_VECTOR_TYPE& n, const divider<T, ALGO>& div) { + n = div.divide(n); + return n; + } +#endif + +// libdivdie::branchfree_divider<T> +template <typename T> +using branchfree_divider = divider<T, BRANCHFREE>; + +} // namespace libdivide + +#endif // __cplusplus + +#endif // NUMPY_CORE_INCLUDE_NUMPY_LIBDIVIDE_LIBDIVIDE_H_ diff --git a/numpy/core/include/numpy/ndarrayobject.h b/numpy/core/include/numpy/ndarrayobject.h index 12fc7098cc21..2eb951486e82 100644 --- a/numpy/core/include/numpy/ndarrayobject.h +++ b/numpy/core/include/numpy/ndarrayobject.h @@ -1,17 +1,11 @@ /* * DON'T INCLUDE THIS DIRECTLY. */ +#ifndef NUMPY_CORE_INCLUDE_NUMPY_NDARRAYOBJECT_H_ +#define NUMPY_CORE_INCLUDE_NUMPY_NDARRAYOBJECT_H_ -#ifndef NPY_NDARRAYOBJECT_H -#define NPY_NDARRAYOBJECT_H #ifdef __cplusplus -#define CONFUSE_EMACS { -#define CONFUSE_EMACS2 } -extern "C" CONFUSE_EMACS -#undef CONFUSE_EMACS -#undef CONFUSE_EMACS2 -/* ... otherwise a semi-smart identer (like emacs) tries to indent - everything when you're typing */ +extern "C" { #endif #include <Python.h> @@ -29,7 +23,7 @@ extern "C" CONFUSE_EMACS /* C-API that requires previous API to be defined */ -#define PyArray_DescrCheck(op) (((PyObject*)(op))->ob_type==&PyArrayDescr_Type) +#define PyArray_DescrCheck(op) PyObject_TypeCheck(op, &PyArrayDescr_Type) #define PyArray_Check(op) PyObject_TypeCheck(op, &PyArray_Type) #define PyArray_CheckExact(op) (((PyObject*)(op))->ob_type == &PyArray_Type) @@ -51,7 +45,6 @@ extern "C" CONFUSE_EMACS #define PyArray_CheckScalar(m) (PyArray_IsScalar(m, Generic) || \ PyArray_IsZeroDim(m)) -#if PY_MAJOR_VERSION >= 3 #define PyArray_IsPythonNumber(obj) \ (PyFloat_Check(obj) || PyComplex_Check(obj) || \ PyLong_Check(obj) || PyBool_Check(obj)) @@ -60,17 +53,6 @@ extern "C" CONFUSE_EMACS #define PyArray_IsPythonScalar(obj) \ (PyArray_IsPythonNumber(obj) || PyBytes_Check(obj) || \ PyUnicode_Check(obj)) -#else -#define PyArray_IsPythonNumber(obj) \ - (PyInt_Check(obj) || PyFloat_Check(obj) || PyComplex_Check(obj) || \ - PyLong_Check(obj) || PyBool_Check(obj)) -#define PyArray_IsIntegerScalar(obj) (PyInt_Check(obj) \ - || PyLong_Check(obj) \ - || PyArray_IsScalar((obj), Integer)) -#define PyArray_IsPythonScalar(obj) \ - (PyArray_IsPythonNumber(obj) || PyString_Check(obj) || \ - PyUnicode_Check(obj)) -#endif #define PyArray_IsAnyScalar(obj) \ (PyArray_IsScalar(obj, Generic) || PyArray_IsPythonScalar(obj)) @@ -232,17 +214,17 @@ PyArray_DiscardWritebackIfCopy(PyArrayObject *arr) /* Check to see if this key in the dictionary is the "title" entry of the tuple (i.e. a duplicate dictionary entry in the fields - dict. + dict). */ static NPY_INLINE int NPY_TITLE_KEY_check(PyObject *key, PyObject *value) { PyObject *title; - if (PyTuple_GET_SIZE(value) != 3) { + if (PyTuple_Size(value) != 3) { return 0; } - title = PyTuple_GET_ITEM(value, 2); + title = PyTuple_GetItem(value, 2); if (key == title) { return 1; } @@ -254,11 +236,6 @@ NPY_TITLE_KEY_check(PyObject *key, PyObject *value) if (PyUnicode_Check(title) && PyUnicode_Check(key)) { return PyUnicode_Compare(title, key) == 0 ? 1 : 0; } -#if PY_VERSION_HEX < 0x03000000 - if (PyString_Check(title) && PyString_Check(key)) { - return PyObject_Compare(title, key) == 0 ? 1 : 0; - } -#endif #endif return 0; } @@ -288,4 +265,4 @@ PyArray_XDECREF_ERR(PyArrayObject *arr) #endif -#endif /* NPY_NDARRAYOBJECT_H */ +#endif /* NUMPY_CORE_INCLUDE_NUMPY_NDARRAYOBJECT_H_ */ diff --git a/numpy/core/include/numpy/ndarraytypes.h b/numpy/core/include/numpy/ndarraytypes.h index cf73cecea6d3..6240adc0c7f1 100644 --- a/numpy/core/include/numpy/ndarraytypes.h +++ b/numpy/core/include/numpy/ndarraytypes.h @@ -1,5 +1,5 @@ -#ifndef NDARRAYTYPES_H -#define NDARRAYTYPES_H +#ifndef NUMPY_CORE_INCLUDE_NUMPY_NDARRAYTYPES_H_ +#define NUMPY_CORE_INCLUDE_NUMPY_NDARRAYTYPES_H_ #include "npy_common.h" #include "npy_endian.h" @@ -156,12 +156,20 @@ enum NPY_TYPECHAR { NPY_COMPLEXLTR = 'c' }; +/* + * Changing this may break Numpy API compatibility + * due to changing offsets in PyArray_ArrFuncs, so be + * careful. Here we have reused the mergesort slot for + * any kind of stable sort, the actual implementation will + * depend on the data type. + */ typedef enum { NPY_QUICKSORT=0, NPY_HEAPSORT=1, - NPY_MERGESORT=2 + NPY_MERGESORT=2, + NPY_STABLESORT=2, } NPY_SORTKIND; -#define NPY_NSORTS (NPY_MERGESORT + 1) +#define NPY_NSORTS (NPY_STABLESORT + 1) typedef enum { @@ -202,6 +210,7 @@ typedef enum { /* For specifying allowed casting in operations which support it */ typedef enum { + _NPY_ERROR_OCCURRED_IN_CAST = -1, /* Only allow identical types */ NPY_NO_CASTING=0, /* Allow identical and byte swapped types */ @@ -211,7 +220,14 @@ typedef enum { /* Allow safe casts or casts within the same kind */ NPY_SAME_KIND_CASTING=3, /* Allow any casts */ - NPY_UNSAFE_CASTING=4 + NPY_UNSAFE_CASTING=4, + /* + * Flag to allow signalling that a cast is a view, this flag is not + * valid when requesting a cast of specific safety. + * _NPY_CAST_IS_VIEW|NPY_EQUIV_CASTING means the same as NPY_NO_CASTING. + */ + // TODO-DTYPES: Needs to be documented. + _NPY_CAST_IS_VIEW = 1 << 16, } NPY_CASTING; typedef enum { @@ -220,6 +236,12 @@ typedef enum { NPY_RAISE=2 } NPY_CLIPMODE; +typedef enum { + NPY_VALID=0, + NPY_SAME=1, + NPY_FULL=2 +} NPY_CORRELATEMODE; + /* The special not-a-time (NaT) value */ #define NPY_DATETIME_NAT NPY_MIN_INT64 @@ -333,33 +355,19 @@ struct NpyAuxData_tag { #define NPY_ERR(str) fprintf(stderr, #str); fflush(stderr); #define NPY_ERR2(str) fprintf(stderr, str); fflush(stderr); -#define NPY_STRINGIFY(x) #x -#define NPY_TOSTRING(x) NPY_STRINGIFY(x) - - /* - * Macros to define how array, and dimension/strides data is - * allocated. - */ - - /* Data buffer - PyDataMem_NEW/FREE/RENEW are in multiarraymodule.c */ +/* +* Macros to define how array, and dimension/strides data is +* allocated. These should be made private +*/ #define NPY_USE_PYMEM 1 + #if NPY_USE_PYMEM == 1 - /* numpy sometimes calls PyArray_malloc() with the GIL released. On Python - 3.3 and older, it was safe to call PyMem_Malloc() with the GIL released. - On Python 3.4 and newer, it's better to use PyMem_RawMalloc() to be able - to use tracemalloc. On Python 3.6, calling PyMem_Malloc() with the GIL - released is now a fatal error in debug mode. */ -# if PY_VERSION_HEX >= 0x03040000 -# define PyArray_malloc PyMem_RawMalloc -# define PyArray_free PyMem_RawFree -# define PyArray_realloc PyMem_RawRealloc -# else -# define PyArray_malloc PyMem_Malloc -# define PyArray_free PyMem_Free -# define PyArray_realloc PyMem_Realloc -# endif +/* use the Raw versions which are safe to call with the GIL released */ +#define PyArray_malloc PyMem_RawMalloc +#define PyArray_free PyMem_RawFree +#define PyArray_realloc PyMem_RawRealloc #else #define PyArray_malloc malloc #define PyArray_free free @@ -505,7 +513,8 @@ typedef struct { PyArray_NonzeroFunc *nonzero; /* - * Used for arange. + * Used for arange. Should return 0 on success + * and -1 on failure. * Can be NULL. */ PyArray_FillFunc *fill; @@ -655,6 +664,29 @@ typedef struct _arr_descr { PyObject *shape; /* a tuple */ } PyArray_ArrayDescr; +/* + * Memory handler structure for array data. + */ +/* The declaration of free differs from PyMemAllocatorEx */ +typedef struct { + void *ctx; + void* (*malloc) (void *ctx, size_t size); + void* (*calloc) (void *ctx, size_t nelem, size_t elsize); + void* (*realloc) (void *ctx, void *ptr, size_t new_size); + void (*free) (void *ctx, void *ptr, size_t size); + /* + * This is the end of the version=1 struct. Only add new fields after + * this line + */ +} PyDataMemAllocator; + +typedef struct { + char name[127]; /* multiple of 64 to keep the struct aligned */ + uint8_t version; /* currently 1 */ + PyDataMemAllocator allocator; +} PyDataMem_Handler; + + /* * The main array object structure. * @@ -704,6 +736,11 @@ typedef struct tagPyArrayObject_fields { int flags; /* For weak references */ PyObject *weakreflist; + void *_buffer_info; /* private buffer info, tagged to allow warning */ + /* + * For malloc/calloc/realloc/free per object + */ + PyObject *mem_handler; } PyArrayObject_fields; /* @@ -723,7 +760,18 @@ typedef struct tagPyArrayObject { } PyArrayObject; #endif -#define NPY_SIZEOF_PYARRAYOBJECT (sizeof(PyArrayObject_fields)) +/* + * Removed 2020-Nov-25, NumPy 1.20 + * #define NPY_SIZEOF_PYARRAYOBJECT (sizeof(PyArrayObject_fields)) + * + * The above macro was removed as it gave a false sense of a stable ABI + * with respect to the structures size. If you require a runtime constant, + * you can use `PyArray_Type.tp_basicsize` instead. Otherwise, please + * see the PyArrayObject documentation or ask the NumPy developers for + * information on how to correctly replace the macro in a way that is + * compatible with multiple NumPy versions. + */ + /* Array Flags Object */ typedef struct PyArrayFlagsObject { @@ -820,7 +868,7 @@ typedef int (PyArray_FinalizeFunc)(PyArrayObject *, PyObject *); /* * Always copy the array. Returned arrays are always CONTIGUOUS, - * ALIGNED, and WRITEABLE. + * ALIGNED, and WRITEABLE. See also: NPY_ARRAY_ENSURENOCOPY = 0x4000. * * This flag may be requested in constructor functions. */ @@ -833,6 +881,17 @@ typedef int (PyArray_FinalizeFunc)(PyArrayObject *, PyObject *); */ #define NPY_ARRAY_ENSUREARRAY 0x0040 +#if defined(NPY_INTERNAL_BUILD) && NPY_INTERNAL_BUILD + /* + * Dual use of the ENSUREARRAY flag, to indicate that this was converted + * from a python float, int, or complex. + * An array using this flag must be a temporary array that can never + * leave the C internals of NumPy. Even if it does, ENSUREARRAY is + * absolutely safe to abuse, since it already is a base class array :). + */ + #define _NPY_ARRAY_WAS_PYSCALAR 0x0040 +#endif /* NPY_INTERNAL_BUILD */ + /* * Make sure that the strides are in units of the element size Needed * for some operations with record-arrays. @@ -878,6 +937,13 @@ typedef int (PyArray_FinalizeFunc)(PyArrayObject *, PyObject *); #define NPY_ARRAY_UPDATEIFCOPY 0x1000 /* Deprecated in 1.14 */ #define NPY_ARRAY_WRITEBACKIFCOPY 0x2000 +/* + * No copy may be made while converting from an object/array (result is a view) + * + * This flag may be requested in constructor functions. + */ +#define NPY_ARRAY_ENSURENOCOPY 0x4000 + /* * NOTE: there are also internal flags defined in multiarray/arrayobject.h, * which start at bit 31 and work down. @@ -949,12 +1015,12 @@ typedef int (PyArray_FinalizeFunc)(PyArrayObject *, PyObject *); */ -#define PyArray_ISCONTIGUOUS(m) PyArray_CHKFLAGS(m, NPY_ARRAY_C_CONTIGUOUS) -#define PyArray_ISWRITEABLE(m) PyArray_CHKFLAGS(m, NPY_ARRAY_WRITEABLE) -#define PyArray_ISALIGNED(m) PyArray_CHKFLAGS(m, NPY_ARRAY_ALIGNED) +#define PyArray_ISCONTIGUOUS(m) PyArray_CHKFLAGS((m), NPY_ARRAY_C_CONTIGUOUS) +#define PyArray_ISWRITEABLE(m) PyArray_CHKFLAGS((m), NPY_ARRAY_WRITEABLE) +#define PyArray_ISALIGNED(m) PyArray_CHKFLAGS((m), NPY_ARRAY_ALIGNED) -#define PyArray_IS_C_CONTIGUOUS(m) PyArray_CHKFLAGS(m, NPY_ARRAY_C_CONTIGUOUS) -#define PyArray_IS_F_CONTIGUOUS(m) PyArray_CHKFLAGS(m, NPY_ARRAY_F_CONTIGUOUS) +#define PyArray_IS_C_CONTIGUOUS(m) PyArray_CHKFLAGS((m), NPY_ARRAY_C_CONTIGUOUS) +#define PyArray_IS_F_CONTIGUOUS(m) PyArray_CHKFLAGS((m), NPY_ARRAY_F_CONTIGUOUS) /* the variable is used in some places, so always define it */ #define NPY_BEGIN_THREADS_DEF PyThreadState *_save=NULL; @@ -964,15 +1030,15 @@ typedef int (PyArray_FinalizeFunc)(PyArrayObject *, PyObject *); #define NPY_BEGIN_THREADS do {_save = PyEval_SaveThread();} while (0); #define NPY_END_THREADS do { if (_save) \ { PyEval_RestoreThread(_save); _save = NULL;} } while (0); -#define NPY_BEGIN_THREADS_THRESHOLDED(loop_size) do { if (loop_size > 500) \ +#define NPY_BEGIN_THREADS_THRESHOLDED(loop_size) do { if ((loop_size) > 500) \ { _save = PyEval_SaveThread();} } while (0); #define NPY_BEGIN_THREADS_DESCR(dtype) \ - do {if (!(PyDataType_FLAGCHK(dtype, NPY_NEEDS_PYAPI))) \ + do {if (!(PyDataType_FLAGCHK((dtype), NPY_NEEDS_PYAPI))) \ NPY_BEGIN_THREADS;} while (0); #define NPY_END_THREADS_DESCR(dtype) \ - do {if (!(PyDataType_FLAGCHK(dtype, NPY_NEEDS_PYAPI))) \ + do {if (!(PyDataType_FLAGCHK((dtype), NPY_NEEDS_PYAPI))) \ NPY_END_THREADS; } while (0); #define NPY_ALLOW_C_API_DEF PyGILState_STATE __save__; @@ -1086,7 +1152,8 @@ typedef struct PyArrayIterObject_tag PyArrayIterObject; * type of the function which translates a set of coordinates to a * pointer to the data */ -typedef char* (*npy_iter_get_dataptr_t)(PyArrayIterObject* iter, npy_intp*); +typedef char* (*npy_iter_get_dataptr_t)( + PyArrayIterObject* iter, const npy_intp*); struct PyArrayIterObject_tag { PyObject_HEAD @@ -1109,7 +1176,7 @@ struct PyArrayIterObject_tag { /* Iterator API */ -#define PyArrayIter_Check(op) PyObject_TypeCheck(op, &PyArrayIter_Type) +#define PyArrayIter_Check(op) PyObject_TypeCheck((op), &PyArrayIter_Type) #define _PyAIT(it) ((PyArrayIterObject *)(it)) #define PyArray_ITER_RESET(it) do { \ @@ -1187,7 +1254,7 @@ struct PyArrayIterObject_tag { #define PyArray_ITER_GOTO1D(it, ind) do { \ int __npy_i; \ - npy_intp __npy_ind = (npy_intp) (ind); \ + npy_intp __npy_ind = (npy_intp)(ind); \ if (__npy_ind < 0) __npy_ind += _PyAIT(it)->size; \ _PyAIT(it)->index = __npy_ind; \ if (_PyAIT(it)->nd_m1 == 0) { \ @@ -1201,6 +1268,8 @@ struct PyArrayIterObject_tag { _PyAIT(it)->dataptr = PyArray_BYTES(_PyAIT(it)->ao); \ for (__npy_i = 0; __npy_i<=_PyAIT(it)->nd_m1; \ __npy_i++) { \ + _PyAIT(it)->coordinates[__npy_i] = \ + (__npy_ind / _PyAIT(it)->factors[__npy_i]); \ _PyAIT(it)->dataptr += \ (__npy_ind / _PyAIT(it)->factors[__npy_i]) \ * _PyAIT(it)->strides[__npy_i]; \ @@ -1271,7 +1340,6 @@ typedef struct { #define PyArray_MultiIter_NOTDONE(multi) \ (_PyMIT(multi)->index < _PyMIT(multi)->size) - /* * Store the information needed for fancy-indexing over an array. The * fields are slightly unordered to keep consec, dataptr and subspace @@ -1428,9 +1496,11 @@ PyArrayNeighborhoodIter_Next2D(PyArrayNeighborhoodIterObject* iter); * Include inline implementations - functions defined there are not * considered public API */ -#define _NPY_INCLUDE_NEIGHBORHOOD_IMP +#define NUMPY_CORE_INCLUDE_NUMPY__NEIGHBORHOOD_IMP_H_ #include "_neighborhood_iterator_imp.h" -#undef _NPY_INCLUDE_NEIGHBORHOOD_IMP +#undef NUMPY_CORE_INCLUDE_NUMPY__NEIGHBORHOOD_IMP_H_ + + /* The default array type */ #define NPY_DEFAULT_TYPE NPY_DOUBLE @@ -1447,9 +1517,8 @@ PyArrayNeighborhoodIter_Next2D(PyArrayNeighborhoodIterObject* iter); * checking of correctness when working with these objects in C. */ -#define PyArray_ISONESEGMENT(m) (PyArray_NDIM(m) == 0 || \ - PyArray_CHKFLAGS(m, NPY_ARRAY_C_CONTIGUOUS) || \ - PyArray_CHKFLAGS(m, NPY_ARRAY_F_CONTIGUOUS)) +#define PyArray_ISONESEGMENT(m) (PyArray_CHKFLAGS(m, NPY_ARRAY_C_CONTIGUOUS) || \ + PyArray_CHKFLAGS(m, NPY_ARRAY_F_CONTIGUOUS)) #define PyArray_ISFORTRAN(m) (PyArray_CHKFLAGS(m, NPY_ARRAY_F_CONTIGUOUS) && \ (!PyArray_CHKFLAGS(m, NPY_ARRAY_C_CONTIGUOUS))) @@ -1550,11 +1619,15 @@ PyArray_GETITEM(const PyArrayObject *arr, const char *itemptr) (void *)itemptr, (PyArrayObject *)arr); } +/* + * SETITEM should only be used if it is known that the value is a scalar + * and of a type understood by the arrays dtype. + * Use `PyArray_Pack` if the value may be of a different dtype. + */ static NPY_INLINE int PyArray_SETITEM(PyArrayObject *arr, char *itemptr, PyObject *v) { - return ((PyArrayObject_fields *)arr)->descr->f->setitem( - v, itemptr, arr); + return ((PyArrayObject_fields *)arr)->descr->f->setitem(v, itemptr, arr); } #else @@ -1618,6 +1691,12 @@ PyArray_CLEARFLAGS(PyArrayObject *arr, int flags) ((PyArrayObject_fields *)arr)->flags &= ~flags; } +static NPY_INLINE NPY_RETURNS_BORROWED_REF PyObject * +PyArray_HANDLER(PyArrayObject *arr) +{ + return ((PyArrayObject_fields *)arr)->mem_handler; +} + #define PyTypeNum_ISBOOL(type) ((type) == NPY_BOOL) #define PyTypeNum_ISUNSIGNED(type) (((type) == NPY_UBYTE) || \ @@ -1670,7 +1749,7 @@ PyArray_CLEARFLAGS(PyArrayObject *arr, int flags) #define PyTypeNum_ISOBJECT(type) ((type) == NPY_OBJECT) -#define PyDataType_ISBOOL(obj) PyTypeNum_ISBOOL(_PyADt(obj)) +#define PyDataType_ISBOOL(obj) PyTypeNum_ISBOOL(((PyArray_Descr*)(obj))->type_num) #define PyDataType_ISUNSIGNED(obj) PyTypeNum_ISUNSIGNED(((PyArray_Descr*)(obj))->type_num) #define PyDataType_ISSIGNED(obj) PyTypeNum_ISSIGNED(((PyArray_Descr*)(obj))->type_num) #define PyDataType_ISINTEGER(obj) PyTypeNum_ISINTEGER(((PyArray_Descr*)(obj))->type_num ) @@ -1686,7 +1765,8 @@ PyArray_CLEARFLAGS(PyArrayObject *arr, int flags) #define PyDataType_ISOBJECT(obj) PyTypeNum_ISOBJECT(((PyArray_Descr*)(obj))->type_num) #define PyDataType_HASFIELDS(obj) (((PyArray_Descr *)(obj))->names != NULL) #define PyDataType_HASSUBARRAY(dtype) ((dtype)->subarray != NULL) -#define PyDataType_ISUNSIZED(dtype) ((dtype)->elsize == 0) +#define PyDataType_ISUNSIZED(dtype) ((dtype)->elsize == 0 && \ + !PyDataType_HASFIELDS(dtype)) #define PyDataType_MAKEUNSIZED(dtype) ((dtype)->elsize = 0) #define PyArray_ISBOOL(obj) PyTypeNum_ISBOOL(PyArray_TYPE(obj)) @@ -1757,9 +1837,9 @@ typedef struct { } npy_stride_sort_item; /************************************************************ - * This is the form of the struct that's returned pointed by the - * PyCObject attribute of an array __array_struct__. See - * http://docs.scipy.org/doc/numpy/reference/arrays.interface.html for the full + * This is the form of the struct that's stored in the + * PyCapsule returned by an array's __array_struct__ attribute. See + * https://docs.scipy.org/doc/numpy/reference/arrays.interface.html for the full * documentation. ************************************************************/ typedef struct { @@ -1808,6 +1888,65 @@ typedef struct { typedef void (PyDataMem_EventHookFunc)(void *inp, void *outp, size_t size, void *user_data); + +/* + * PyArray_DTypeMeta related definitions. + * + * As of now, this API is preliminary and will be extended as necessary. + */ +#if defined(NPY_INTERNAL_BUILD) && NPY_INTERNAL_BUILD + /* + * The Structures defined in this block are currently considered + * private API and may change without warning! + * Part of this (at least the size) is exepcted to be public API without + * further modifications. + */ + /* TODO: Make this definition public in the API, as soon as its settled */ + NPY_NO_EXPORT extern PyTypeObject PyArrayDTypeMeta_Type; + + /* + * While NumPy DTypes would not need to be heap types the plan is to + * make DTypes available in Python at which point they will be heap types. + * Since we also wish to add fields to the DType class, this looks like + * a typical instance definition, but with PyHeapTypeObject instead of + * only the PyObject_HEAD. + * This must only be exposed very extremely careful consideration, since + * it is a fairly complex construct which may be better to allow + * refactoring of. + */ + typedef struct { + PyHeapTypeObject super; + + /* + * Most DTypes will have a singleton default instance, for the + * parametric legacy DTypes (bytes, string, void, datetime) this + * may be a pointer to the *prototype* instance? + */ + PyArray_Descr *singleton; + /* Copy of the legacy DTypes type number, usually invalid. */ + int type_num; + + /* The type object of the scalar instances (may be NULL?) */ + PyTypeObject *scalar_type; + /* + * DType flags to signal legacy, parametric, or + * abstract. But plenty of space for additional information/flags. + */ + npy_uint64 flags; + + /* + * Use indirection in order to allow a fixed size for this struct. + * A stable ABI size makes creating a static DType less painful + * while also ensuring flexibility for all opaque API (with one + * indirection due the pointer lookup). + */ + void *dt_slots; + void *reserved[3]; + } PyArray_DTypeMeta; + +#endif /* NPY_INTERNAL_BUILD */ + + /* * Use the keyword NPY_DEPRECATED_INCLUDES to ensure that the header files * npy_*_*_deprecated_api.h are only included from here and nowhere else. @@ -1834,4 +1973,4 @@ typedef void (PyDataMem_EventHookFunc)(void *inp, void *outp, size_t size, */ #undef NPY_DEPRECATED_INCLUDES -#endif /* NPY_ARRAYTYPES_H */ +#endif /* NUMPY_CORE_INCLUDE_NUMPY_NDARRAYTYPES_H_ */ diff --git a/numpy/core/include/numpy/noprefix.h b/numpy/core/include/numpy/noprefix.h index 041f301928ec..2c0ce1420e2c 100644 --- a/numpy/core/include/numpy/noprefix.h +++ b/numpy/core/include/numpy/noprefix.h @@ -1,5 +1,5 @@ -#ifndef NPY_NOPREFIX_H -#define NPY_NOPREFIX_H +#ifndef NUMPY_CORE_INCLUDE_NUMPY_NOPREFIX_H_ +#define NUMPY_CORE_INCLUDE_NUMPY_NOPREFIX_H_ /* * You can directly include noprefix.h as a backward @@ -209,4 +209,4 @@ #define MAX_ELSIZE NPY_MAX_ELSIZE #endif -#endif +#endif /* NUMPY_CORE_INCLUDE_NUMPY_NOPREFIX_H_ */ diff --git a/numpy/core/include/numpy/npy_1_7_deprecated_api.h b/numpy/core/include/numpy/npy_1_7_deprecated_api.h index 4c318bc4784c..4fd4015a991a 100644 --- a/numpy/core/include/numpy/npy_1_7_deprecated_api.h +++ b/numpy/core/include/numpy/npy_1_7_deprecated_api.h @@ -1,21 +1,23 @@ -#ifndef _NPY_1_7_DEPRECATED_API_H -#define _NPY_1_7_DEPRECATED_API_H - #ifndef NPY_DEPRECATED_INCLUDES #error "Should never include npy_*_*_deprecated_api directly." #endif +#ifndef NUMPY_CORE_INCLUDE_NUMPY_NPY_1_7_DEPRECATED_API_H_ +#define NUMPY_CORE_INCLUDE_NUMPY_NPY_1_7_DEPRECATED_API_H_ + +/* Emit a warning if the user did not specifically request the old API */ +#ifndef NPY_NO_DEPRECATED_API #if defined(_WIN32) #define _WARN___STR2__(x) #x #define _WARN___STR1__(x) _WARN___STR2__(x) #define _WARN___LOC__ __FILE__ "(" _WARN___STR1__(__LINE__) ") : Warning Msg: " -#pragma message(_WARN___LOC__"Using deprecated NumPy API, disable it by " \ - "#defining NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION") -#elif defined(__GNUC__) -#warning "Using deprecated NumPy API, disable it by " \ - "#defining NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION" +#pragma message(_WARN___LOC__"Using deprecated NumPy API, disable it with " \ + "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION") +#else +#warning "Using deprecated NumPy API, disable it with " \ + "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION" +#endif #endif -/* TODO: How to do this warning message for other compilers? */ /* * This header exists to collect all dangerous/deprecated NumPy API @@ -66,18 +68,11 @@ #define PyArray_DEFAULT NPY_DEFAULT_TYPE /* These DATETIME bits aren't used internally */ -#if PY_VERSION_HEX >= 0x03000000 #define PyDataType_GetDatetimeMetaData(descr) \ ((descr->metadata == NULL) ? NULL : \ ((PyArray_DatetimeMetaData *)(PyCapsule_GetPointer( \ PyDict_GetItemString( \ descr->metadata, NPY_METADATA_DTSTR), NULL)))) -#else -#define PyDataType_GetDatetimeMetaData(descr) \ - ((descr->metadata == NULL) ? NULL : \ - ((PyArray_DatetimeMetaData *)(PyCObject_AsVoidPtr( \ - PyDict_GetItemString(descr->metadata, NPY_METADATA_DTSTR))))) -#endif /* * Deprecated as of NumPy 1.7, this kind of shortcut doesn't @@ -127,4 +122,4 @@ */ #include "old_defines.h" -#endif +#endif /* NUMPY_CORE_INCLUDE_NUMPY_NPY_1_7_DEPRECATED_API_H_ */ diff --git a/numpy/core/include/numpy/npy_3kcompat.h b/numpy/core/include/numpy/npy_3kcompat.h index 2d0ccd3b9238..22c103e93da9 100644 --- a/numpy/core/include/numpy/npy_3kcompat.h +++ b/numpy/core/include/numpy/npy_3kcompat.h @@ -7,17 +7,15 @@ * strong backwards compatibility guarantees at the moment. */ -#ifndef _NPY_3KCOMPAT_H_ -#define _NPY_3KCOMPAT_H_ +#ifndef NUMPY_CORE_INCLUDE_NUMPY_NPY_3KCOMPAT_H_ +#define NUMPY_CORE_INCLUDE_NUMPY_NPY_3KCOMPAT_H_ #include <Python.h> #include <stdio.h> -#if PY_VERSION_HEX >= 0x03000000 #ifndef NPY_PY3K #define NPY_PY3K 1 #endif -#endif #include "numpy/npy_common.h" #include "numpy/ndarrayobject.h" @@ -30,6 +28,30 @@ extern "C" { * PyInt -> PyLong */ + +/* + * This is a renamed copy of the Python non-limited API function _PyLong_AsInt. It is + * included here because it is missing from the PyPy API. It completes the PyLong_As* + * group of functions and can be useful in replacing PyInt_Check. + */ +static NPY_INLINE int +Npy__PyLong_AsInt(PyObject *obj) +{ + int overflow; + long result = PyLong_AsLongAndOverflow(obj, &overflow); + + /* INT_MAX and INT_MIN are defined in Python.h */ + if (overflow || result > INT_MAX || result < INT_MIN) { + /* XXX: could be cute and give a different + message for overflow == -1 */ + PyErr_SetString(PyExc_OverflowError, + "Python int too large to convert to C int"); + return -1; + } + return (int)result; +} + + #if defined(NPY_PY3K) /* Return True only if the long fits in a C long */ static NPY_INLINE int PyInt_Check(PyObject *op) { @@ -41,10 +63,12 @@ static NPY_INLINE int PyInt_Check(PyObject *op) { return (overflow == 0); } + #define PyInt_FromLong PyLong_FromLong #define PyInt_AsLong PyLong_AsLong #define PyInt_AS_LONG PyLong_AsLong #define PyInt_AsSsize_t PyLong_AsSsize_t +#define PyNumber_Int PyNumber_Long /* NOTE: * @@ -61,12 +85,42 @@ static NPY_INLINE int PyInt_Check(PyObject *op) { PySlice_GetIndicesEx((PySliceObject *)op, nop, start, end, step, slicelength) #endif -/* <2.7.11 and <3.4.4 have the wrong argument type for Py_EnterRecursiveCall */ -#if (PY_VERSION_HEX < 0x02070B00) || \ - ((0x03000000 <= PY_VERSION_HEX) && (PY_VERSION_HEX < 0x03040400)) - #define Npy_EnterRecursiveCall(x) Py_EnterRecursiveCall((char *)(x)) -#else - #define Npy_EnterRecursiveCall(x) Py_EnterRecursiveCall(x) +#if PY_VERSION_HEX < 0x030900a4 + /* Introduced in https://github.com/python/cpython/commit/d2ec81a8c99796b51fb8c49b77a7fe369863226f */ + #define Py_SET_TYPE(obj, type) ((Py_TYPE(obj) = (type)), (void)0) + /* Introduced in https://github.com/python/cpython/commit/b10dc3e7a11fcdb97e285882eba6da92594f90f9 */ + #define Py_SET_SIZE(obj, size) ((Py_SIZE(obj) = (size)), (void)0) + /* Introduced in https://github.com/python/cpython/commit/c86a11221df7e37da389f9c6ce6e47ea22dc44ff */ + #define Py_SET_REFCNT(obj, refcnt) ((Py_REFCNT(obj) = (refcnt)), (void)0) +#endif + + +#define Npy_EnterRecursiveCall(x) Py_EnterRecursiveCall(x) + +/* Py_SETREF was added in 3.5.2, and only if Py_LIMITED_API is absent */ +#if PY_VERSION_HEX < 0x03050200 + #define Py_SETREF(op, op2) \ + do { \ + PyObject *_py_tmp = (PyObject *)(op); \ + (op) = (op2); \ + Py_DECREF(_py_tmp); \ + } while (0) +#endif + +/* introduced in https://github.com/python/cpython/commit/a24107b04c1277e3c1105f98aff5bfa3a98b33a0 */ +#if PY_VERSION_HEX < 0x030800A3 + static NPY_INLINE PyObject * + _PyDict_GetItemStringWithError(PyObject *v, const char *key) + { + PyObject *kv, *rv; + kv = PyUnicode_FromString(key); + if (kv == NULL) { + return NULL; + } + rv = PyDict_GetItemWithError(v, kv); + Py_DECREF(kv); + return rv; + } #endif /* @@ -141,20 +195,14 @@ static NPY_INLINE int PyInt_Check(PyObject *op) { static NPY_INLINE void PyUnicode_ConcatAndDel(PyObject **left, PyObject *right) { - PyObject *newobj; - newobj = PyUnicode_Concat(*left, right); - Py_DECREF(*left); + Py_SETREF(*left, PyUnicode_Concat(*left, right)); Py_DECREF(right); - *left = newobj; } static NPY_INLINE void PyUnicode_Concat2(PyObject **left, PyObject *right) { - PyObject *newobj; - newobj = PyUnicode_Concat(*left, right); - Py_DECREF(*left); - *left = newobj; + Py_SETREF(*left, PyUnicode_Concat(*left, right)); } /* @@ -168,6 +216,7 @@ static NPY_INLINE FILE* npy_PyFile_Dup2(PyObject *file, char *mode, npy_off_t *orig_pos) { int fd, fd2, unbuf; + Py_ssize_t fd2_tmp; PyObject *ret, *os, *io, *io_raw; npy_off_t pos; FILE *handle; @@ -203,8 +252,17 @@ npy_PyFile_Dup2(PyObject *file, char *mode, npy_off_t *orig_pos) if (ret == NULL) { return NULL; } - fd2 = PyNumber_AsSsize_t(ret, NULL); + fd2_tmp = PyNumber_AsSsize_t(ret, PyExc_IOError); Py_DECREF(ret); + if (fd2_tmp == -1 && PyErr_Occurred()) { + return NULL; + } + if (fd2_tmp < INT_MIN || fd2_tmp > INT_MAX) { + PyErr_SetString(PyExc_IOError, + "Getting an 'int' from os.dup() failed"); + return NULL; + } + fd2 = (int)fd2_tmp; /* Convert to FILE* handle */ #ifdef _WIN32 @@ -215,6 +273,7 @@ npy_PyFile_Dup2(PyObject *file, char *mode, npy_off_t *orig_pos) if (handle == NULL) { PyErr_SetString(PyExc_IOError, "Getting a FILE* from a Python file object failed"); + return NULL; } /* Record the original raw file handle position */ @@ -378,6 +437,68 @@ npy_PyFile_CloseFile(PyObject *file) return 0; } + +/* This is a copy of _PyErr_ChainExceptions + */ +static NPY_INLINE void +npy_PyErr_ChainExceptions(PyObject *exc, PyObject *val, PyObject *tb) +{ + if (exc == NULL) + return; + + if (PyErr_Occurred()) { + /* only py3 supports this anyway */ + #ifdef NPY_PY3K + PyObject *exc2, *val2, *tb2; + PyErr_Fetch(&exc2, &val2, &tb2); + PyErr_NormalizeException(&exc, &val, &tb); + if (tb != NULL) { + PyException_SetTraceback(val, tb); + Py_DECREF(tb); + } + Py_DECREF(exc); + PyErr_NormalizeException(&exc2, &val2, &tb2); + PyException_SetContext(val2, val); + PyErr_Restore(exc2, val2, tb2); + #endif + } + else { + PyErr_Restore(exc, val, tb); + } +} + + +/* This is a copy of _PyErr_ChainExceptions, with: + * - a minimal implementation for python 2 + * - __cause__ used instead of __context__ + */ +static NPY_INLINE void +npy_PyErr_ChainExceptionsCause(PyObject *exc, PyObject *val, PyObject *tb) +{ + if (exc == NULL) + return; + + if (PyErr_Occurred()) { + /* only py3 supports this anyway */ + #ifdef NPY_PY3K + PyObject *exc2, *val2, *tb2; + PyErr_Fetch(&exc2, &val2, &tb2); + PyErr_NormalizeException(&exc, &val, &tb); + if (tb != NULL) { + PyException_SetTraceback(val, tb); + Py_DECREF(tb); + } + Py_DECREF(exc); + PyErr_NormalizeException(&exc2, &val2, &tb2); + PyException_SetCause(val2, val); + PyErr_Restore(exc2, val2, tb2); + #endif + } + else { + PyErr_Restore(exc, val, tb); + } +} + /* * PyObject_Cmp */ @@ -422,8 +543,6 @@ PyObject_Cmp(PyObject *i1, PyObject *i2, int *cmp) * The main job here is to get rid of the improved error handling * of PyCapsules. It's a shame... */ -#if PY_VERSION_HEX >= 0x03000000 - static NPY_INLINE PyObject * NpyCapsule_FromVoidPtr(void *ptr, void (*dtor)(PyObject *)) { @@ -468,43 +587,9 @@ NpyCapsule_Check(PyObject *ptr) return PyCapsule_CheckExact(ptr); } -#else - -static NPY_INLINE PyObject * -NpyCapsule_FromVoidPtr(void *ptr, void (*dtor)(void *)) -{ - return PyCObject_FromVoidPtr(ptr, dtor); -} - -static NPY_INLINE PyObject * -NpyCapsule_FromVoidPtrAndDesc(void *ptr, void* context, - void (*dtor)(void *, void *)) -{ - return PyCObject_FromVoidPtrAndDesc(ptr, context, dtor); -} - -static NPY_INLINE void * -NpyCapsule_AsVoidPtr(PyObject *ptr) -{ - return PyCObject_AsVoidPtr(ptr); -} - -static NPY_INLINE void * -NpyCapsule_GetDesc(PyObject *obj) -{ - return PyCObject_GetDesc(obj); -} - -static NPY_INLINE int -NpyCapsule_Check(PyObject *ptr) -{ - return PyCObject_Check(ptr); -} - -#endif - #ifdef __cplusplus } #endif -#endif /* _NPY_3KCOMPAT_H_ */ + +#endif /* NUMPY_CORE_INCLUDE_NUMPY_NPY_3KCOMPAT_H_ */ diff --git a/numpy/core/include/numpy/npy_common.h b/numpy/core/include/numpy/npy_common.h index 5faff43851fe..88794ca07e36 100644 --- a/numpy/core/include/numpy/npy_common.h +++ b/numpy/core/include/numpy/npy_common.h @@ -1,5 +1,8 @@ -#ifndef _NPY_COMMON_H_ -#define _NPY_COMMON_H_ +#ifndef NUMPY_CORE_INCLUDE_NUMPY_NPY_COMMON_H_ +#define NUMPY_CORE_INCLUDE_NUMPY_NPY_COMMON_H_ + +/* need Python.h for npy_intp, npy_uintp */ +#include <Python.h> /* numpconfig.h is auto-generated */ #include "numpyconfig.h" @@ -7,16 +10,15 @@ #include <npy_config.h> #endif -/* need Python.h for npy_intp, npy_uintp */ -#include <Python.h> - /* * using static inline modifiers when defining npy_math functions * allows the compiler to make optimizations when possible */ -#if NPY_INTERNAL_BUILD #ifndef NPY_INLINE_MATH -#define NPY_INLINE_MATH 1 +#if defined(NPY_INTERNAL_BUILD) && NPY_INTERNAL_BUILD + #define NPY_INLINE_MATH 1 +#else + #define NPY_INLINE_MATH 0 #endif #endif @@ -44,12 +46,33 @@ #else #define NPY_GCC_TARGET_AVX #endif + +#if defined HAVE_ATTRIBUTE_TARGET_AVX2_WITH_INTRINSICS +#define HAVE_ATTRIBUTE_TARGET_FMA +#define NPY_GCC_TARGET_FMA __attribute__((target("avx2,fma"))) +#endif + #if defined HAVE_ATTRIBUTE_TARGET_AVX2 && defined HAVE_LINK_AVX2 #define NPY_GCC_TARGET_AVX2 __attribute__((target("avx2"))) #else #define NPY_GCC_TARGET_AVX2 #endif +#if defined HAVE_ATTRIBUTE_TARGET_AVX512F && defined HAVE_LINK_AVX512F +#define NPY_GCC_TARGET_AVX512F __attribute__((target("avx512f"))) +#elif defined HAVE_ATTRIBUTE_TARGET_AVX512F_WITH_INTRINSICS +#define NPY_GCC_TARGET_AVX512F __attribute__((target("avx512f"))) +#else +#define NPY_GCC_TARGET_AVX512F +#endif + +#if defined HAVE_ATTRIBUTE_TARGET_AVX512_SKX && defined HAVE_LINK_AVX512_SKX +#define NPY_GCC_TARGET_AVX512_SKX __attribute__((target("avx512f,avx512dq,avx512vl,avx512bw,avx512cd"))) +#elif defined HAVE_ATTRIBUTE_TARGET_AVX512_SKX_WITH_INTRINSICS +#define NPY_GCC_TARGET_AVX512_SKX __attribute__((target("avx512f,avx512dq,avx512vl,avx512bw,avx512cd"))) +#else +#define NPY_GCC_TARGET_AVX512_SKX +#endif /* * mark an argument (starting from 1) that must not be NULL and is not checked * DO NOT USE IF FUNCTION CHECKS FOR NULL!! the compiler will remove the check @@ -68,6 +91,13 @@ #define NPY_HAVE_SSE2_INTRINSICS #endif +#if defined HAVE_IMMINTRIN_H && defined HAVE_LINK_AVX2 +#define NPY_HAVE_AVX2_INTRINSICS +#endif + +#if defined HAVE_IMMINTRIN_H && defined HAVE_LINK_AVX512F +#define NPY_HAVE_AVX512F_INTRINSICS +#endif /* * give a hint to the compiler which branch is more likely or unlikely * to occur, e.g. rare error cases: @@ -113,6 +143,14 @@ #define NPY_INLINE #endif +#ifdef _MSC_VER + #define NPY_FINLINE static __forceinline +#elif defined(__GNUC__) + #define NPY_FINLINE static NPY_INLINE __attribute__((always_inline)) +#else + #define NPY_FINLINE static +#endif + #ifdef HAVE___THREAD #define NPY_TLS __thread #else @@ -226,11 +264,10 @@ typedef Py_uintptr_t npy_uintp; #define constchar char /* NPY_INTP_FMT Note: - * Unlike the other NPY_*_FMT macros which are used with - * PyOS_snprintf, NPY_INTP_FMT is used with PyErr_Format and - * PyString_Format. These functions use different formatting - * codes which are portably specified according to the Python - * documentation. See ticket #1795. + * Unlike the other NPY_*_FMT macros, which are used with PyOS_snprintf, + * NPY_INTP_FMT is used with PyErr_Format and PyUnicode_FromFormat. Those + * functions use different formatting codes that are portably specified + * according to the Python documentation. See issue gh-2388. */ #if NPY_SIZEOF_PY_INTPTR_T == NPY_SIZEOF_INT #define NPY_INTP NPY_INT @@ -319,14 +356,31 @@ typedef unsigned long npy_ulonglong; typedef unsigned char npy_bool; #define NPY_FALSE 0 #define NPY_TRUE 1 - - +/* + * `NPY_SIZEOF_LONGDOUBLE` isn't usually equal to sizeof(long double). + * In some certain cases, it may forced to be equal to sizeof(double) + * even against the compiler implementation and the same goes for + * `complex long double`. + * + * Therefore, avoid `long double`, use `npy_longdouble` instead, + * and when it comes to standard math functions make sure of using + * the double version when `NPY_SIZEOF_LONGDOUBLE` == `NPY_SIZEOF_DOUBLE`. + * For example: + * npy_longdouble *ptr, x; + * #if NPY_SIZEOF_LONGDOUBLE == NPY_SIZEOF_DOUBLE + * npy_longdouble r = modf(x, ptr); + * #else + * npy_longdouble r = modfl(x, ptr); + * #endif + * + * See https://github.com/numpy/numpy/issues/20348 + */ #if NPY_SIZEOF_LONGDOUBLE == NPY_SIZEOF_DOUBLE - typedef double npy_longdouble; - #define NPY_LONGDOUBLE_FMT "g" + #define NPY_LONGDOUBLE_FMT "g" + typedef double npy_longdouble; #else - typedef long double npy_longdouble; - #define NPY_LONGDOUBLE_FMT "Lg" + #define NPY_LONGDOUBLE_FMT "Lg" + typedef long double npy_longdouble; #endif #ifndef Py_USING_UNICODE @@ -348,18 +402,8 @@ typedef long npy_long; typedef float npy_float; typedef double npy_double; -/* - * Hash value compatibility. - * As of Python 3.2 hash values are of type Py_hash_t. - * Previous versions use C long. - */ -#if PY_VERSION_HEX < 0x03020000 -typedef long npy_hash_t; -#define NPY_SIZEOF_HASH_T NPY_SIZEOF_LONG -#else typedef Py_hash_t npy_hash_t; #define NPY_SIZEOF_HASH_T NPY_SIZEOF_INTP -#endif /* * Disabling C99 complex usage: a lot of C code in numpy/scipy rely on being @@ -1080,4 +1124,4 @@ typedef npy_int64 npy_datetime; /* End of typedefs for numarray style bit-width names */ -#endif +#endif /* NUMPY_CORE_INCLUDE_NUMPY_NPY_COMMON_H_ */ diff --git a/numpy/core/include/numpy/npy_cpu.h b/numpy/core/include/numpy/npy_cpu.h index 106ffa450026..78d229e7dfc5 100644 --- a/numpy/core/include/numpy/npy_cpu.h +++ b/numpy/core/include/numpy/npy_cpu.h @@ -18,12 +18,13 @@ * NPY_CPU_ARCEL * NPY_CPU_ARCEB * NPY_CPU_RISCV64 + * NPY_CPU_LOONGARCH + * NPY_CPU_WASM */ -#ifndef _NPY_CPUARCH_H_ -#define _NPY_CPUARCH_H_ +#ifndef NUMPY_CORE_INCLUDE_NUMPY_NPY_CPU_H_ +#define NUMPY_CORE_INCLUDE_NUMPY_NPY_CPU_H_ #include "numpyconfig.h" -#include <string.h> /* for memcpy */ #if defined( __i386__ ) || defined(i386) || defined(_M_IX86) /* @@ -39,17 +40,19 @@ * _M_AMD64 defined by MS compiler */ #define NPY_CPU_AMD64 +#elif defined(__powerpc64__) && defined(__LITTLE_ENDIAN__) + #define NPY_CPU_PPC64LE +#elif defined(__powerpc64__) && defined(__BIG_ENDIAN__) + #define NPY_CPU_PPC64 #elif defined(__ppc__) || defined(__powerpc__) || defined(_ARCH_PPC) /* * __ppc__ is defined by gcc, I remember having seen __powerpc__ once, * but can't find it ATM * _ARCH_PPC is used by at least gcc on AIX + * As __powerpc__ and _ARCH_PPC are also defined by PPC64 check + * for those specifically first before defaulting to ppc */ #define NPY_CPU_PPC -#elif defined(__ppc64le__) - #define NPY_CPU_PPC64LE -#elif defined(__ppc64__) - #define NPY_CPU_PPC64 #elif defined(__sparc__) || defined(__sparc) /* __sparc__ is defined by gcc and Forte (e.g. Sun) compilers */ #define NPY_CPU_SPARC @@ -61,10 +64,28 @@ #define NPY_CPU_HPPA #elif defined(__alpha__) #define NPY_CPU_ALPHA -#elif defined(__arm__) && defined(__ARMEL__) - #define NPY_CPU_ARMEL -#elif defined(__arm__) && defined(__ARMEB__) - #define NPY_CPU_ARMEB +#elif defined(__arm__) || defined(__aarch64__) || defined(_M_ARM64) + /* _M_ARM64 is defined in MSVC for ARM64 compilation on Windows */ + #if defined(__ARMEB__) || defined(__AARCH64EB__) + #if defined(__ARM_32BIT_STATE) + #define NPY_CPU_ARMEB_AARCH32 + #elif defined(__ARM_64BIT_STATE) + #define NPY_CPU_ARMEB_AARCH64 + #else + #define NPY_CPU_ARMEB + #endif + #elif defined(__ARMEL__) || defined(__AARCH64EL__) || defined(_M_ARM64) + #if defined(__ARM_32BIT_STATE) + #define NPY_CPU_ARMEL_AARCH32 + #elif defined(__ARM_64BIT_STATE) || defined(_M_ARM64) + #define NPY_CPU_ARMEL_AARCH64 + #else + #define NPY_CPU_ARMEL + #endif + #else + # error Unknown ARM CPU, please report this to numpy maintainers with \ + information about your platform (OS, CPU and compiler) + #endif #elif defined(__sh__) && defined(__LITTLE_ENDIAN__) #define NPY_CPU_SH_LE #elif defined(__sh__) && defined(__BIG_ENDIAN__) @@ -75,8 +96,6 @@ #define NPY_CPU_MIPSEB #elif defined(__or1k__) #define NPY_CPU_OR1K -#elif defined(__aarch64__) - #define NPY_CPU_AARCH64 #elif defined(__mc68000__) #define NPY_CPU_M68K #elif defined(__arc__) && defined(__LITTLE_ENDIAN__) @@ -85,17 +104,26 @@ #define NPY_CPU_ARCEB #elif defined(__riscv) && defined(__riscv_xlen) && __riscv_xlen == 64 #define NPY_CPU_RISCV64 +#elif defined(__loongarch__) + #define NPY_CPU_LOONGARCH +#elif defined(__EMSCRIPTEN__) + /* __EMSCRIPTEN__ is defined by emscripten: an LLVM-to-Web compiler */ + #define NPY_CPU_WASM #else #error Unknown CPU, please report this to numpy maintainers with \ information about your platform (OS, CPU and compiler) #endif -#define NPY_COPY_PYOBJECT_PTR(dst, src) memcpy(dst, src, sizeof(PyObject *)) - -#if (defined(NPY_CPU_X86) || defined(NPY_CPU_AMD64)) -#define NPY_CPU_HAVE_UNALIGNED_ACCESS 1 -#else -#define NPY_CPU_HAVE_UNALIGNED_ACCESS 0 +/* + * Except for the following architectures, memory access is limited to the natural + * alignment of data types otherwise it may lead to bus error or performance regression. + * For more details about unaligned access, see https://www.kernel.org/doc/Documentation/unaligned-memory-access.txt. +*/ +#if defined(NPY_CPU_X86) || defined(NPY_CPU_AMD64) || defined(__aarch64__) || defined(__powerpc64__) + #define NPY_ALIGNMENT_REQUIRED 0 #endif - +#ifndef NPY_ALIGNMENT_REQUIRED + #define NPY_ALIGNMENT_REQUIRED 1 #endif + +#endif /* NUMPY_CORE_INCLUDE_NUMPY_NPY_CPU_H_ */ diff --git a/numpy/core/include/numpy/npy_endian.h b/numpy/core/include/numpy/npy_endian.h index 649bdb0a6b36..5e58a7f52cee 100644 --- a/numpy/core/include/numpy/npy_endian.h +++ b/numpy/core/include/numpy/npy_endian.h @@ -1,5 +1,5 @@ -#ifndef _NPY_ENDIAN_H_ -#define _NPY_ENDIAN_H_ +#ifndef NUMPY_CORE_INCLUDE_NUMPY_NPY_ENDIAN_H_ +#define NUMPY_CORE_INCLUDE_NUMPY_NPY_ENDIAN_H_ /* * NPY_BYTE_ORDER is set to the same value as BYTE_ORDER set by glibc in @@ -37,33 +37,41 @@ #define NPY_LITTLE_ENDIAN 1234 #define NPY_BIG_ENDIAN 4321 - #if defined(NPY_CPU_X86) \ - || defined(NPY_CPU_AMD64) \ - || defined(NPY_CPU_IA64) \ - || defined(NPY_CPU_ALPHA) \ - || defined(NPY_CPU_ARMEL) \ - || defined(NPY_CPU_AARCH64) \ - || defined(NPY_CPU_SH_LE) \ - || defined(NPY_CPU_MIPSEL) \ - || defined(NPY_CPU_PPC64LE) \ - || defined(NPY_CPU_ARCEL) \ - || defined(NPY_CPU_RISCV64) + #if defined(NPY_CPU_X86) \ + || defined(NPY_CPU_AMD64) \ + || defined(NPY_CPU_IA64) \ + || defined(NPY_CPU_ALPHA) \ + || defined(NPY_CPU_ARMEL) \ + || defined(NPY_CPU_ARMEL_AARCH32) \ + || defined(NPY_CPU_ARMEL_AARCH64) \ + || defined(NPY_CPU_SH_LE) \ + || defined(NPY_CPU_MIPSEL) \ + || defined(NPY_CPU_PPC64LE) \ + || defined(NPY_CPU_ARCEL) \ + || defined(NPY_CPU_RISCV64) \ + || defined(NPY_CPU_LOONGARCH) \ + || defined(NPY_CPU_WASM) #define NPY_BYTE_ORDER NPY_LITTLE_ENDIAN - #elif defined(NPY_CPU_PPC) \ - || defined(NPY_CPU_SPARC) \ - || defined(NPY_CPU_S390) \ - || defined(NPY_CPU_HPPA) \ - || defined(NPY_CPU_PPC64) \ - || defined(NPY_CPU_ARMEB) \ - || defined(NPY_CPU_SH_BE) \ - || defined(NPY_CPU_MIPSEB) \ - || defined(NPY_CPU_OR1K) \ - || defined(NPY_CPU_M68K) \ + + #elif defined(NPY_CPU_PPC) \ + || defined(NPY_CPU_SPARC) \ + || defined(NPY_CPU_S390) \ + || defined(NPY_CPU_HPPA) \ + || defined(NPY_CPU_PPC64) \ + || defined(NPY_CPU_ARMEB) \ + || defined(NPY_CPU_ARMEB_AARCH32) \ + || defined(NPY_CPU_ARMEB_AARCH64) \ + || defined(NPY_CPU_SH_BE) \ + || defined(NPY_CPU_MIPSEB) \ + || defined(NPY_CPU_OR1K) \ + || defined(NPY_CPU_M68K) \ || defined(NPY_CPU_ARCEB) #define NPY_BYTE_ORDER NPY_BIG_ENDIAN + #else #error Unknown CPU: can not set endianness #endif -#endif #endif + +#endif /* NUMPY_CORE_INCLUDE_NUMPY_NPY_ENDIAN_H_ */ diff --git a/numpy/core/include/numpy/npy_interrupt.h b/numpy/core/include/numpy/npy_interrupt.h index 40cb7ac5efdb..69a0374dd8e9 100644 --- a/numpy/core/include/numpy/npy_interrupt.h +++ b/numpy/core/include/numpy/npy_interrupt.h @@ -1,82 +1,21 @@ - -/* Signal handling: - -This header file defines macros that allow your code to handle -interrupts received during processing. Interrupts that -could reasonably be handled: - -SIGINT, SIGABRT, SIGALRM, SIGSEGV - -****Warning*************** - -Do not allow code that creates temporary memory or increases reference -counts of Python objects to be interrupted unless you handle it -differently. - -************************** - -The mechanism for handling interrupts is conceptually simple: - - - replace the signal handler with our own home-grown version - and store the old one. - - run the code to be interrupted -- if an interrupt occurs - the handler should basically just cause a return to the - calling function for finish work. - - restore the old signal handler - -Of course, every code that allows interrupts must account for -returning via the interrupt and handle clean-up correctly. But, -even still, the simple paradigm is complicated by at least three -factors. - - 1) platform portability (i.e. Microsoft says not to use longjmp - to return from signal handling. They have a __try and __except - extension to C instead but what about mingw?). - - 2) how to handle threads: apparently whether signals are delivered to - every thread of the process or the "invoking" thread is platform - dependent. --- we don't handle threads for now. - - 3) do we need to worry about re-entrance. For now, assume the - code will not call-back into itself. - -Ideas: - - 1) Start by implementing an approach that works on platforms that - can use setjmp and longjmp functionality and does nothing - on other platforms. - - 2) Ignore threads --- i.e. do not mix interrupt handling and threads - - 3) Add a default signal_handler function to the C-API but have the rest - use macros. - - -Simple Interface: - - -In your C-extension: around a block of code you want to be interruptible -with a SIGINT - -NPY_SIGINT_ON -[code] -NPY_SIGINT_OFF - -In order for this to work correctly, the -[code] block must not allocate any memory or alter the reference count of any -Python objects. In other words [code] must be interruptible so that continuation -after NPY_SIGINT_OFF will only be "missing some computations" - -Interrupt handling does not work well with threads. - -*/ - -/* Add signal handling macros - Make the global variable and signal handler part of the C-API -*/ - -#ifndef NPY_INTERRUPT_H -#define NPY_INTERRUPT_H +/* + * This API is only provided because it is part of publicly exported + * headers. Its use is considered DEPRECATED, and it will be removed + * eventually. + * (This includes the _PyArray_SigintHandler and _PyArray_GetSigintBuf + * functions which are however, public API, and not headers.) + * + * Instead of using these non-threadsafe macros consider periodically + * querying `PyErr_CheckSignals()` or `PyOS_InterruptOccurred()` will work. + * Both of these require holding the GIL, although cpython could add a + * version of `PyOS_InterruptOccurred()` which does not. Such a version + * actually exists as private API in Python 3.10, and backported to 3.9 and 3.8, + * see also https://bugs.python.org/issue41037 and + * https://github.com/python/cpython/pull/20599). + */ + +#ifndef NUMPY_CORE_INCLUDE_NUMPY_NPY_INTERRUPT_H_ +#define NUMPY_CORE_INCLUDE_NUMPY_NPY_INTERRUPT_H_ #ifndef NPY_NO_SIGNAL @@ -107,11 +46,11 @@ Interrupt handling does not work well with threads. PyOS_setsig(SIGINT, _npy_sig_save); \ } -#else /* NPY_NO_SIGNAL */ +#else /* NPY_NO_SIGNAL */ #define NPY_SIGINT_ON #define NPY_SIGINT_OFF -#endif /* HAVE_SIGSETJMP */ +#endif /* HAVE_SIGSETJMP */ -#endif /* NPY_INTERRUPT_H */ +#endif /* NUMPY_CORE_INCLUDE_NUMPY_NPY_INTERRUPT_H_ */ diff --git a/numpy/core/include/numpy/npy_math.h b/numpy/core/include/numpy/npy_math.h index 582390cdcb87..bead0dc14064 100644 --- a/numpy/core/include/numpy/npy_math.h +++ b/numpy/core/include/numpy/npy_math.h @@ -1,18 +1,16 @@ -#ifndef __NPY_MATH_C99_H_ -#define __NPY_MATH_C99_H_ +#ifndef NUMPY_CORE_INCLUDE_NUMPY_NPY_MATH_H_ +#define NUMPY_CORE_INCLUDE_NUMPY_NPY_MATH_H_ #ifdef __cplusplus extern "C" { #endif +#include <numpy/npy_common.h> + #include <math.h> #ifdef __SUNPRO_CC #include <sunmath.h> #endif -#ifdef HAVE_NPY_CONFIG_H -#include <npy_config.h> -#endif -#include <numpy/npy_common.h> /* By adding static inline specifiers to npy_math function definitions when appropriate, compiler is given the opportunity to optimize */ @@ -113,6 +111,56 @@ NPY_INLINE static float __npy_nzerof(void) #define NPY_SQRT2l 1.414213562373095048801688724209698079L /* sqrt(2) */ #define NPY_SQRT1_2l 0.707106781186547524400844362104849039L /* 1/sqrt(2) */ +/* + * Integer functions. + */ +NPY_INPLACE npy_uint npy_gcdu(npy_uint a, npy_uint b); +NPY_INPLACE npy_uint npy_lcmu(npy_uint a, npy_uint b); +NPY_INPLACE npy_ulong npy_gcdul(npy_ulong a, npy_ulong b); +NPY_INPLACE npy_ulong npy_lcmul(npy_ulong a, npy_ulong b); +NPY_INPLACE npy_ulonglong npy_gcdull(npy_ulonglong a, npy_ulonglong b); +NPY_INPLACE npy_ulonglong npy_lcmull(npy_ulonglong a, npy_ulonglong b); + +NPY_INPLACE npy_int npy_gcd(npy_int a, npy_int b); +NPY_INPLACE npy_int npy_lcm(npy_int a, npy_int b); +NPY_INPLACE npy_long npy_gcdl(npy_long a, npy_long b); +NPY_INPLACE npy_long npy_lcml(npy_long a, npy_long b); +NPY_INPLACE npy_longlong npy_gcdll(npy_longlong a, npy_longlong b); +NPY_INPLACE npy_longlong npy_lcmll(npy_longlong a, npy_longlong b); + +NPY_INPLACE npy_ubyte npy_rshiftuhh(npy_ubyte a, npy_ubyte b); +NPY_INPLACE npy_ubyte npy_lshiftuhh(npy_ubyte a, npy_ubyte b); +NPY_INPLACE npy_ushort npy_rshiftuh(npy_ushort a, npy_ushort b); +NPY_INPLACE npy_ushort npy_lshiftuh(npy_ushort a, npy_ushort b); +NPY_INPLACE npy_uint npy_rshiftu(npy_uint a, npy_uint b); +NPY_INPLACE npy_uint npy_lshiftu(npy_uint a, npy_uint b); +NPY_INPLACE npy_ulong npy_rshiftul(npy_ulong a, npy_ulong b); +NPY_INPLACE npy_ulong npy_lshiftul(npy_ulong a, npy_ulong b); +NPY_INPLACE npy_ulonglong npy_rshiftull(npy_ulonglong a, npy_ulonglong b); +NPY_INPLACE npy_ulonglong npy_lshiftull(npy_ulonglong a, npy_ulonglong b); + +NPY_INPLACE npy_byte npy_rshifthh(npy_byte a, npy_byte b); +NPY_INPLACE npy_byte npy_lshifthh(npy_byte a, npy_byte b); +NPY_INPLACE npy_short npy_rshifth(npy_short a, npy_short b); +NPY_INPLACE npy_short npy_lshifth(npy_short a, npy_short b); +NPY_INPLACE npy_int npy_rshift(npy_int a, npy_int b); +NPY_INPLACE npy_int npy_lshift(npy_int a, npy_int b); +NPY_INPLACE npy_long npy_rshiftl(npy_long a, npy_long b); +NPY_INPLACE npy_long npy_lshiftl(npy_long a, npy_long b); +NPY_INPLACE npy_longlong npy_rshiftll(npy_longlong a, npy_longlong b); +NPY_INPLACE npy_longlong npy_lshiftll(npy_longlong a, npy_longlong b); + +NPY_INPLACE uint8_t npy_popcountuhh(npy_ubyte a); +NPY_INPLACE uint8_t npy_popcountuh(npy_ushort a); +NPY_INPLACE uint8_t npy_popcountu(npy_uint a); +NPY_INPLACE uint8_t npy_popcountul(npy_ulong a); +NPY_INPLACE uint8_t npy_popcountull(npy_ulonglong a); +NPY_INPLACE uint8_t npy_popcounthh(npy_byte a); +NPY_INPLACE uint8_t npy_popcounth(npy_short a); +NPY_INPLACE uint8_t npy_popcount(npy_int a); +NPY_INPLACE uint8_t npy_popcountl(npy_long a); +NPY_INPLACE uint8_t npy_popcountll(npy_longlong a); + /* * C99 double math funcs */ @@ -165,7 +213,7 @@ double npy_spacing(double x); /* use builtins to avoid function calls in tight loops * only available if npy_config.h is available (= numpys own build) */ -#if HAVE___BUILTIN_ISNAN +#ifdef HAVE___BUILTIN_ISNAN #define npy_isnan(x) __builtin_isnan(x) #else #ifndef NPY_HAVE_DECL_ISNAN @@ -181,7 +229,7 @@ double npy_spacing(double x); /* only available if npy_config.h is available (= numpys own build) */ -#if HAVE___BUILTIN_ISFINITE +#ifdef HAVE___BUILTIN_ISFINITE #define npy_isfinite(x) __builtin_isfinite(x) #else #ifndef NPY_HAVE_DECL_ISFINITE @@ -196,7 +244,7 @@ double npy_spacing(double x); #endif /* only available if npy_config.h is available (= numpys own build) */ -#if HAVE___BUILTIN_ISINF +#ifdef HAVE___BUILTIN_ISINF #define npy_isinf(x) __builtin_isinf(x) #else #ifndef NPY_HAVE_DECL_ISINF @@ -354,7 +402,7 @@ NPY_INPLACE npy_longdouble npy_heavisidel(npy_longdouble x, npy_longdouble h0); union { \ ctype z; \ type a[2]; \ - } z1;; \ + } z1; \ \ z1.a[0] = (x); \ z1.a[1] = (y); \ @@ -548,4 +596,4 @@ void npy_set_floatstatus_invalid(void); #include "npy_math_internal.h" #endif -#endif +#endif /* NUMPY_CORE_INCLUDE_NUMPY_NPY_MATH_H_ */ diff --git a/numpy/core/include/numpy/npy_no_deprecated_api.h b/numpy/core/include/numpy/npy_no_deprecated_api.h index 6183dc2784a7..39658c0bd2d6 100644 --- a/numpy/core/include/numpy/npy_no_deprecated_api.h +++ b/numpy/core/include/numpy/npy_no_deprecated_api.h @@ -9,11 +9,12 @@ #ifndef NPY_NO_DEPRECATED_API /* put this check here since there may be multiple includes in C extensions. */ -#if defined(NDARRAYTYPES_H) || defined(_NPY_DEPRECATED_API_H) || \ - defined(OLD_DEFINES_H) +#if defined(NUMPY_CORE_INCLUDE_NUMPY_NDARRAYTYPES_H_) || \ + defined(NUMPY_CORE_INCLUDE_NUMPY_NPY_DEPRECATED_API_H) || \ + defined(NUMPY_CORE_INCLUDE_NUMPY_OLD_DEFINES_H_) #error "npy_no_deprecated_api.h" must be first among numpy includes. #else #define NPY_NO_DEPRECATED_API NPY_API_VERSION #endif -#endif +#endif /* NPY_NO_DEPRECATED_API */ diff --git a/numpy/core/include/numpy/npy_os.h b/numpy/core/include/numpy/npy_os.h index 9228c3916eab..efa0e4012f91 100644 --- a/numpy/core/include/numpy/npy_os.h +++ b/numpy/core/include/numpy/npy_os.h @@ -1,5 +1,5 @@ -#ifndef _NPY_OS_H_ -#define _NPY_OS_H_ +#ifndef NUMPY_CORE_INCLUDE_NUMPY_NPY_OS_H_ +#define NUMPY_CORE_INCLUDE_NUMPY_NPY_OS_H_ #if defined(linux) || defined(__linux) || defined(__linux__) #define NPY_OS_LINUX @@ -27,4 +27,4 @@ #define NPY_OS_UNKNOWN #endif -#endif +#endif /* NUMPY_CORE_INCLUDE_NUMPY_NPY_OS_H_ */ diff --git a/numpy/core/include/numpy/numpyconfig.h b/numpy/core/include/numpy/numpyconfig.h index 04a3738b92f8..e4c17f7e19a5 100644 --- a/numpy/core/include/numpy/numpyconfig.h +++ b/numpy/core/include/numpy/numpyconfig.h @@ -1,5 +1,5 @@ -#ifndef _NPY_NUMPYCONFIG_H_ -#define _NPY_NUMPYCONFIG_H_ +#ifndef NUMPY_CORE_INCLUDE_NUMPY_NPY_NUMPYCONFIG_H_ +#define NUMPY_CORE_INCLUDE_NUMPY_NPY_NUMPYCONFIG_H_ #include "_numpyconfig.h" @@ -19,6 +19,25 @@ #define NPY_SIZEOF_LONG 4 #define NPY_SIZEOF_PY_INTPTR_T 4 #endif + + #undef NPY_SIZEOF_LONGDOUBLE + #undef NPY_SIZEOF_COMPLEX_LONGDOUBLE + + #if defined(__arm64__) + #define NPY_SIZEOF_LONGDOUBLE 8 + #define NPY_SIZEOF_COMPLEX_LONGDOUBLE 16 + #elif defined(__x86_64) + #define NPY_SIZEOF_LONGDOUBLE 16 + #define NPY_SIZEOF_COMPLEX_LONGDOUBLE 32 + #elif defined (__i386) + #define NPY_SIZEOF_LONGDOUBLE 12 + #define NPY_SIZEOF_COMPLEX_LONGDOUBLE 24 + #elif defined(__ppc__) || defined (__ppc64__) + #define NPY_SIZEOF_LONGDOUBLE 16 + #define NPY_SIZEOF_COMPLEX_LONGDOUBLE 32 + #else + #error "unknown architecture" + #endif #endif /** @@ -36,5 +55,13 @@ #define NPY_1_12_API_VERSION 0x00000008 #define NPY_1_13_API_VERSION 0x00000008 #define NPY_1_14_API_VERSION 0x00000008 +#define NPY_1_15_API_VERSION 0x00000008 +#define NPY_1_16_API_VERSION 0x00000008 +#define NPY_1_17_API_VERSION 0x00000008 +#define NPY_1_18_API_VERSION 0x00000008 +#define NPY_1_19_API_VERSION 0x00000008 +#define NPY_1_20_API_VERSION 0x0000000e +#define NPY_1_21_API_VERSION 0x0000000e +#define NPY_1_22_API_VERSION 0x0000000f -#endif +#endif /* NUMPY_CORE_INCLUDE_NUMPY_NPY_NUMPYCONFIG_H_ */ diff --git a/numpy/core/include/numpy/old_defines.h b/numpy/core/include/numpy/old_defines.h index abf81595ae16..b3fa677512c4 100644 --- a/numpy/core/include/numpy/old_defines.h +++ b/numpy/core/include/numpy/old_defines.h @@ -1,6 +1,6 @@ /* This header is deprecated as of NumPy 1.7 */ -#ifndef OLD_DEFINES_H -#define OLD_DEFINES_H +#ifndef NUMPY_CORE_INCLUDE_NUMPY_OLD_DEFINES_H_ +#define NUMPY_CORE_INCLUDE_NUMPY_OLD_DEFINES_H_ #if defined(NPY_NO_DEPRECATED_API) && NPY_NO_DEPRECATED_API >= NPY_1_7_API_VERSION #error The header "old_defines.h" is deprecated as of NumPy 1.7. @@ -184,4 +184,4 @@ #define PyArray_UCS4 npy_ucs4 -#endif +#endif /* NUMPY_CORE_INCLUDE_NUMPY_OLD_DEFINES_H_ */ diff --git a/numpy/core/include/numpy/oldnumeric.h b/numpy/core/include/numpy/oldnumeric.h index 38530faf045a..6604e8d17847 100644 --- a/numpy/core/include/numpy/oldnumeric.h +++ b/numpy/core/include/numpy/oldnumeric.h @@ -1,3 +1,8 @@ +#ifndef NUMPY_CORE_INCLUDE_NUMPY_OLDNUMERIC_H_ +#define NUMPY_CORE_INCLUDE_NUMPY_OLDNUMERIC_H_ + +/* FIXME -- this file can be deleted? */ + #include "arrayobject.h" #ifndef PYPY_VERSION @@ -23,3 +28,5 @@ #undef import_array #define import_array() { if (_import_array() < 0) {PyErr_Print(); PyErr_SetString(PyExc_ImportError, "numpy.core.multiarray failed to import"); } } + +#endif /* NUMPY_CORE_INCLUDE_NUMPY_OLDNUMERIC_H_ */ diff --git a/numpy/core/include/numpy/random/bitgen.h b/numpy/core/include/numpy/random/bitgen.h new file mode 100644 index 000000000000..162dd5c57530 --- /dev/null +++ b/numpy/core/include/numpy/random/bitgen.h @@ -0,0 +1,20 @@ +#ifndef NUMPY_CORE_INCLUDE_NUMPY_RANDOM_BITGEN_H_ +#define NUMPY_CORE_INCLUDE_NUMPY_RANDOM_BITGEN_H_ + +#pragma once +#include <stddef.h> +#include <stdbool.h> +#include <stdint.h> + +/* Must match the declaration in numpy/random/<any>.pxd */ + +typedef struct bitgen { + void *state; + uint64_t (*next_uint64)(void *st); + uint32_t (*next_uint32)(void *st); + double (*next_double)(void *st); + uint64_t (*next_raw)(void *st); +} bitgen_t; + + +#endif /* NUMPY_CORE_INCLUDE_NUMPY_RANDOM_BITGEN_H_ */ diff --git a/numpy/core/include/numpy/random/distributions.h b/numpy/core/include/numpy/random/distributions.h new file mode 100644 index 000000000000..dacf7782909f --- /dev/null +++ b/numpy/core/include/numpy/random/distributions.h @@ -0,0 +1,209 @@ +#ifndef NUMPY_CORE_INCLUDE_NUMPY_RANDOM_DISTRIBUTIONS_H_ +#define NUMPY_CORE_INCLUDE_NUMPY_RANDOM_DISTRIBUTIONS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <Python.h> +#include "numpy/npy_common.h" +#include <stddef.h> +#include <stdbool.h> +#include <stdint.h> + +#include "numpy/npy_math.h" +#include "numpy/random/bitgen.h" + +/* + * RAND_INT_TYPE is used to share integer generators with RandomState which + * used long in place of int64_t. If changing a distribution that uses + * RAND_INT_TYPE, then the original unmodified copy must be retained for + * use in RandomState by copying to the legacy distributions source file. + */ +#ifdef NP_RANDOM_LEGACY +#define RAND_INT_TYPE long +#define RAND_INT_MAX LONG_MAX +#else +#define RAND_INT_TYPE int64_t +#define RAND_INT_MAX INT64_MAX +#endif + +#if defined(_MSC_VER) || defined(__CYGWIN__) +#define DECLDIR __declspec(dllexport) +#else +#define DECLDIR extern +#endif + +#ifndef MIN +#define MIN(x, y) (((x) < (y)) ? x : y) +#define MAX(x, y) (((x) > (y)) ? x : y) +#endif + +#ifndef M_PI +#define M_PI 3.14159265358979323846264338328 +#endif + +typedef struct s_binomial_t { + int has_binomial; /* !=0: following parameters initialized for binomial */ + double psave; + RAND_INT_TYPE nsave; + double r; + double q; + double fm; + RAND_INT_TYPE m; + double p1; + double xm; + double xl; + double xr; + double c; + double laml; + double lamr; + double p2; + double p3; + double p4; +} binomial_t; + +DECLDIR float random_standard_uniform_f(bitgen_t *bitgen_state); +DECLDIR double random_standard_uniform(bitgen_t *bitgen_state); +DECLDIR void random_standard_uniform_fill(bitgen_t *, npy_intp, double *); +DECLDIR void random_standard_uniform_fill_f(bitgen_t *, npy_intp, float *); + +DECLDIR int64_t random_positive_int64(bitgen_t *bitgen_state); +DECLDIR int32_t random_positive_int32(bitgen_t *bitgen_state); +DECLDIR int64_t random_positive_int(bitgen_t *bitgen_state); +DECLDIR uint64_t random_uint(bitgen_t *bitgen_state); + +DECLDIR double random_standard_exponential(bitgen_t *bitgen_state); +DECLDIR float random_standard_exponential_f(bitgen_t *bitgen_state); +DECLDIR void random_standard_exponential_fill(bitgen_t *, npy_intp, double *); +DECLDIR void random_standard_exponential_fill_f(bitgen_t *, npy_intp, float *); +DECLDIR void random_standard_exponential_inv_fill(bitgen_t *, npy_intp, double *); +DECLDIR void random_standard_exponential_inv_fill_f(bitgen_t *, npy_intp, float *); + +DECLDIR double random_standard_normal(bitgen_t *bitgen_state); +DECLDIR float random_standard_normal_f(bitgen_t *bitgen_state); +DECLDIR void random_standard_normal_fill(bitgen_t *, npy_intp, double *); +DECLDIR void random_standard_normal_fill_f(bitgen_t *, npy_intp, float *); +DECLDIR double random_standard_gamma(bitgen_t *bitgen_state, double shape); +DECLDIR float random_standard_gamma_f(bitgen_t *bitgen_state, float shape); + +DECLDIR double random_normal(bitgen_t *bitgen_state, double loc, double scale); + +DECLDIR double random_gamma(bitgen_t *bitgen_state, double shape, double scale); +DECLDIR float random_gamma_f(bitgen_t *bitgen_state, float shape, float scale); + +DECLDIR double random_exponential(bitgen_t *bitgen_state, double scale); +DECLDIR double random_uniform(bitgen_t *bitgen_state, double lower, double range); +DECLDIR double random_beta(bitgen_t *bitgen_state, double a, double b); +DECLDIR double random_chisquare(bitgen_t *bitgen_state, double df); +DECLDIR double random_f(bitgen_t *bitgen_state, double dfnum, double dfden); +DECLDIR double random_standard_cauchy(bitgen_t *bitgen_state); +DECLDIR double random_pareto(bitgen_t *bitgen_state, double a); +DECLDIR double random_weibull(bitgen_t *bitgen_state, double a); +DECLDIR double random_power(bitgen_t *bitgen_state, double a); +DECLDIR double random_laplace(bitgen_t *bitgen_state, double loc, double scale); +DECLDIR double random_gumbel(bitgen_t *bitgen_state, double loc, double scale); +DECLDIR double random_logistic(bitgen_t *bitgen_state, double loc, double scale); +DECLDIR double random_lognormal(bitgen_t *bitgen_state, double mean, double sigma); +DECLDIR double random_rayleigh(bitgen_t *bitgen_state, double mode); +DECLDIR double random_standard_t(bitgen_t *bitgen_state, double df); +DECLDIR double random_noncentral_chisquare(bitgen_t *bitgen_state, double df, + double nonc); +DECLDIR double random_noncentral_f(bitgen_t *bitgen_state, double dfnum, + double dfden, double nonc); +DECLDIR double random_wald(bitgen_t *bitgen_state, double mean, double scale); +DECLDIR double random_vonmises(bitgen_t *bitgen_state, double mu, double kappa); +DECLDIR double random_triangular(bitgen_t *bitgen_state, double left, double mode, + double right); + +DECLDIR RAND_INT_TYPE random_poisson(bitgen_t *bitgen_state, double lam); +DECLDIR RAND_INT_TYPE random_negative_binomial(bitgen_t *bitgen_state, double n, + double p); + +DECLDIR int64_t random_binomial(bitgen_t *bitgen_state, double p, + int64_t n, binomial_t *binomial); + +DECLDIR int64_t random_logseries(bitgen_t *bitgen_state, double p); +DECLDIR int64_t random_geometric(bitgen_t *bitgen_state, double p); +DECLDIR RAND_INT_TYPE random_geometric_search(bitgen_t *bitgen_state, double p); +DECLDIR RAND_INT_TYPE random_zipf(bitgen_t *bitgen_state, double a); +DECLDIR int64_t random_hypergeometric(bitgen_t *bitgen_state, + int64_t good, int64_t bad, int64_t sample); +DECLDIR uint64_t random_interval(bitgen_t *bitgen_state, uint64_t max); + +/* Generate random uint64 numbers in closed interval [off, off + rng]. */ +DECLDIR uint64_t random_bounded_uint64(bitgen_t *bitgen_state, uint64_t off, + uint64_t rng, uint64_t mask, + bool use_masked); + +/* Generate random uint32 numbers in closed interval [off, off + rng]. */ +DECLDIR uint32_t random_buffered_bounded_uint32(bitgen_t *bitgen_state, + uint32_t off, uint32_t rng, + uint32_t mask, bool use_masked, + int *bcnt, uint32_t *buf); +DECLDIR uint16_t random_buffered_bounded_uint16(bitgen_t *bitgen_state, + uint16_t off, uint16_t rng, + uint16_t mask, bool use_masked, + int *bcnt, uint32_t *buf); +DECLDIR uint8_t random_buffered_bounded_uint8(bitgen_t *bitgen_state, uint8_t off, + uint8_t rng, uint8_t mask, + bool use_masked, int *bcnt, + uint32_t *buf); +DECLDIR npy_bool random_buffered_bounded_bool(bitgen_t *bitgen_state, npy_bool off, + npy_bool rng, npy_bool mask, + bool use_masked, int *bcnt, + uint32_t *buf); + +DECLDIR void random_bounded_uint64_fill(bitgen_t *bitgen_state, uint64_t off, + uint64_t rng, npy_intp cnt, + bool use_masked, uint64_t *out); +DECLDIR void random_bounded_uint32_fill(bitgen_t *bitgen_state, uint32_t off, + uint32_t rng, npy_intp cnt, + bool use_masked, uint32_t *out); +DECLDIR void random_bounded_uint16_fill(bitgen_t *bitgen_state, uint16_t off, + uint16_t rng, npy_intp cnt, + bool use_masked, uint16_t *out); +DECLDIR void random_bounded_uint8_fill(bitgen_t *bitgen_state, uint8_t off, + uint8_t rng, npy_intp cnt, + bool use_masked, uint8_t *out); +DECLDIR void random_bounded_bool_fill(bitgen_t *bitgen_state, npy_bool off, + npy_bool rng, npy_intp cnt, + bool use_masked, npy_bool *out); + +DECLDIR void random_multinomial(bitgen_t *bitgen_state, RAND_INT_TYPE n, RAND_INT_TYPE *mnix, + double *pix, npy_intp d, binomial_t *binomial); + +/* multivariate hypergeometric, "count" method */ +DECLDIR int random_multivariate_hypergeometric_count(bitgen_t *bitgen_state, + int64_t total, + size_t num_colors, int64_t *colors, + int64_t nsample, + size_t num_variates, int64_t *variates); + +/* multivariate hypergeometric, "marginals" method */ +DECLDIR void random_multivariate_hypergeometric_marginals(bitgen_t *bitgen_state, + int64_t total, + size_t num_colors, int64_t *colors, + int64_t nsample, + size_t num_variates, int64_t *variates); + +/* Common to legacy-distributions.c and distributions.c but not exported */ + +RAND_INT_TYPE random_binomial_btpe(bitgen_t *bitgen_state, + RAND_INT_TYPE n, + double p, + binomial_t *binomial); +RAND_INT_TYPE random_binomial_inversion(bitgen_t *bitgen_state, + RAND_INT_TYPE n, + double p, + binomial_t *binomial); +double random_loggam(double x); +static NPY_INLINE double next_double(bitgen_t *bitgen_state) { + return bitgen_state->next_double(bitgen_state->state); +} + +#ifdef __cplusplus +} +#endif + +#endif /* NUMPY_CORE_INCLUDE_NUMPY_RANDOM_DISTRIBUTIONS_H_ */ diff --git a/numpy/core/include/numpy/ufuncobject.h b/numpy/core/include/numpy/ufuncobject.h index 4b1b3d325ba0..1d7050bbe5a3 100644 --- a/numpy/core/include/numpy/ufuncobject.h +++ b/numpy/core/include/numpy/ufuncobject.h @@ -1,5 +1,5 @@ -#ifndef Py_UFUNCOBJECT_H -#define Py_UFUNCOBJECT_H +#ifndef NUMPY_CORE_INCLUDE_NUMPY_UFUNCOBJECT_H_ +#define NUMPY_CORE_INCLUDE_NUMPY_UFUNCOBJECT_H_ #include <numpy/npy_math.h> #include <numpy/npy_common.h> @@ -14,8 +14,8 @@ extern "C" { */ typedef void (*PyUFuncGenericFunction) (char **args, - npy_intp *dimensions, - npy_intp *strides, + npy_intp const *dimensions, + npy_intp const *strides, void *innerloopdata); /* @@ -66,27 +66,14 @@ typedef int (PyUFunc_TypeResolutionFunc)( PyArray_Descr **out_dtypes); /* - * Given an array of DTypes as returned by the PyUFunc_TypeResolutionFunc, - * and an array of fixed strides (the array will contain NPY_MAX_INTP for - * strides which are not necessarily fixed), returns an inner loop - * with associated auxiliary data. - * - * For backwards compatibility, there is a variant of the inner loop - * selection which returns an inner loop irrespective of the strides, - * and with a void* static auxiliary data instead of an NpyAuxData * - * dynamically allocatable auxiliary data. + * Legacy loop selector. (This should NOT normally be used and we can expect + * that only the `PyUFunc_DefaultLegacyInnerLoopSelector` is ever set). + * However, unlike the masked version, it probably still works. * * ufunc: The ufunc object. * dtypes: An array which has been populated with dtypes, * in most cases by the type resolution function * for the same ufunc. - * fixed_strides: For each input/output, either the stride that - * will be used every time the function is called - * or NPY_MAX_INTP if the stride might change or - * is not known ahead of time. The loop selection - * function may use this stride to pick inner loops - * which are optimized for contiguous or 0-stride - * cases. * out_innerloop: Should be populated with the correct ufunc inner * loop for the given type. * out_innerloopdata: Should be populated with the void* data to @@ -101,15 +88,7 @@ typedef int (PyUFunc_LegacyInnerLoopSelectionFunc)( PyUFuncGenericFunction *out_innerloop, void **out_innerloopdata, int *out_needs_api); -typedef int (PyUFunc_MaskedInnerLoopSelectionFunc)( - struct _tagPyUFuncObject *ufunc, - PyArray_Descr **dtypes, - PyArray_Descr *mask_dtype, - npy_intp *fixed_strides, - npy_intp fixed_mask_stride, - PyUFunc_MaskedStridedInnerLoopFunc **out_innerloop, - NpyAuxData **out_innerloopdata, - int *out_needs_api); + typedef struct _tagPyUFuncObject { PyObject_HEAD @@ -120,7 +99,11 @@ typedef struct _tagPyUFuncObject { */ int nin, nout, nargs; - /* Identity for reduction, either PyUFunc_One or PyUFunc_Zero */ + /* + * Identity for reduction, any of PyUFunc_One, PyUFunc_Zero + * PyUFunc_MinusOne, PyUFunc_None, PyUFunc_ReorderableNone, + * PyUFunc_IdentityValue. + */ int identity; /* Array of one-dimensional core loops */ @@ -190,11 +173,10 @@ typedef struct _tagPyUFuncObject { * but this was never implemented. (This is also why the above * selector is called the "legacy" selector.) */ - void *reserved2; - /* - * A function which returns a masked inner loop for the ufunc. - */ - PyUFunc_MaskedInnerLoopSelectionFunc *masked_inner_loop_selector; + vectorcallfunc vectorcall; + + /* Was previously the `PyUFunc_MaskedInnerLoopSelectionFunc` */ + void *_always_null_previously_masked_innerloop_selector; /* * List of flags for each operand when ufunc is called by nditer object. @@ -209,9 +191,39 @@ typedef struct _tagPyUFuncObject { * set by nditer object. */ npy_uint32 iter_flags; + + /* New in NPY_API_VERSION 0x0000000D and above */ + + /* + * for each core_num_dim_ix distinct dimension names, + * the possible "frozen" size (-1 if not frozen). + */ + npy_intp *core_dim_sizes; + + /* + * for each distinct core dimension, a set of UFUNC_CORE_DIM* flags + */ + npy_uint32 *core_dim_flags; + + /* Identity for reduction, when identity == PyUFunc_IdentityValue */ + PyObject *identity_value; + + /* New in NPY_API_VERSION 0x0000000F and above */ + + /* New private fields related to dispatching */ + void *_dispatch_cache; + /* A PyListObject of `(tuple of DTypes, ArrayMethod/Promoter)` */ + PyObject *_loops; } PyUFuncObject; #include "arrayobject.h" +/* Generalized ufunc; 0x0001 reserved for possible use as CORE_ENABLED */ +/* the core dimension's size will be determined by the operands. */ +#define UFUNC_CORE_DIM_SIZE_INFERRED 0x0002 +/* the core dimension may be absent */ +#define UFUNC_CORE_DIM_CAN_IGNORE 0x0004 +/* flags inferred during execution */ +#define UFUNC_CORE_DIM_MISSING 0x00040000 #define UFUNC_ERR_IGNORE 0 #define UFUNC_ERR_WARN 1 @@ -276,6 +288,12 @@ typedef struct _tagPyUFuncObject { * This case allows reduction with multiple axes at once. */ #define PyUFunc_ReorderableNone -2 +/* + * UFunc unit is an identity_value, and the order of operations can be reordered + * This case allows reduction with multiple axes at once. + */ +#define PyUFunc_IdentityValue -3 + #define UFUNC_REDUCE 0 #define UFUNC_ACCUMULATE 1 @@ -306,30 +324,6 @@ typedef struct _loop1d_info { #define UFUNC_PYVALS_NAME "UFUNC_PYVALS" -#define UFUNC_CHECK_ERROR(arg) \ - do {if ((((arg)->obj & UFUNC_OBJ_NEEDS_API) && PyErr_Occurred()) || \ - ((arg)->errormask && \ - PyUFunc_checkfperr((arg)->errormask, \ - (arg)->errobj, \ - &(arg)->first))) \ - goto fail;} while (0) - - -/* keep in sync with ieee754.c.src */ -#if defined(sun) || defined(__BSD__) || defined(__OpenBSD__) || \ - (defined(__FreeBSD__) && (__FreeBSD_version < 502114)) || \ - defined(__NetBSD__) || \ - defined(__GLIBC__) || defined(__APPLE__) || \ - defined(__CYGWIN__) || defined(__MINGW32__) || \ - (defined(__FreeBSD__) && (__FreeBSD_version >= 502114)) || \ - defined(_AIX) || \ - defined(_MSC_VER) || \ - defined(__osf__) && defined(__alpha) -#else -#define NO_FLOATING_POINT_SUPPORT -#endif - - /* * THESE MACROS ARE DEPRECATED. * Use npy_set_floatstatus_* in the npymath library. @@ -339,10 +333,6 @@ typedef struct _loop1d_info { #define UFUNC_FPE_UNDERFLOW NPY_FPE_UNDERFLOW #define UFUNC_FPE_INVALID NPY_FPE_INVALID -#define UFUNC_CHECK_STATUS(ret) \ - { \ - ret = npy_clear_floatstatus(); \ - } #define generate_divbyzero_error() npy_set_floatstatus_divbyzero() #define generate_overflow_error() npy_set_floatstatus_overflow() @@ -356,8 +346,8 @@ typedef struct _loop1d_info { #endif #endif - #ifdef __cplusplus } #endif -#endif /* !Py_UFUNCOBJECT_H */ + +#endif /* NUMPY_CORE_INCLUDE_NUMPY_UFUNCOBJECT_H_ */ diff --git a/numpy/core/include/numpy/utils.h b/numpy/core/include/numpy/utils.h index 32218b8c7ca3..e2b57f9e508d 100644 --- a/numpy/core/include/numpy/utils.h +++ b/numpy/core/include/numpy/utils.h @@ -1,21 +1,37 @@ -#ifndef __NUMPY_UTILS_HEADER__ -#define __NUMPY_UTILS_HEADER__ +#ifndef NUMPY_CORE_INCLUDE_NUMPY_UTILS_H_ +#define NUMPY_CORE_INCLUDE_NUMPY_UTILS_H_ #ifndef __COMP_NPY_UNUSED - #if defined(__GNUC__) - #define __COMP_NPY_UNUSED __attribute__ ((__unused__)) - # elif defined(__ICC) - #define __COMP_NPY_UNUSED __attribute__ ((__unused__)) - # elif defined(__clang__) - #define __COMP_NPY_UNUSED __attribute__ ((unused)) - #else - #define __COMP_NPY_UNUSED - #endif + #if defined(__GNUC__) + #define __COMP_NPY_UNUSED __attribute__ ((__unused__)) + #elif defined(__ICC) + #define __COMP_NPY_UNUSED __attribute__ ((__unused__)) + #elif defined(__clang__) + #define __COMP_NPY_UNUSED __attribute__ ((unused)) + #else + #define __COMP_NPY_UNUSED + #endif +#endif + +#if defined(__GNUC__) || defined(__ICC) || defined(__clang__) + #define NPY_DECL_ALIGNED(x) __attribute__ ((aligned (x))) +#elif defined(_MSC_VER) + #define NPY_DECL_ALIGNED(x) __declspec(align(x)) +#else + #define NPY_DECL_ALIGNED(x) #endif /* Use this to tag a variable as not used. It will remove unused variable * warning on support platforms (see __COM_NPY_UNUSED) and mangle the variable * to avoid accidental use */ #define NPY_UNUSED(x) (__NPY_UNUSED_TAGGED ## x) __COMP_NPY_UNUSED +#define NPY_EXPAND(x) x -#endif +#define NPY_STRINGIFY(x) #x +#define NPY_TOSTRING(x) NPY_STRINGIFY(x) + +#define NPY_CAT__(a, b) a ## b +#define NPY_CAT_(a, b) NPY_CAT__(a, b) +#define NPY_CAT(a, b) NPY_CAT_(a, b) + +#endif /* NUMPY_CORE_INCLUDE_NUMPY_UTILS_H_ */ diff --git a/numpy/core/info.py b/numpy/core/info.py deleted file mode 100644 index c6f7bbcf2ac0..000000000000 --- a/numpy/core/info.py +++ /dev/null @@ -1,87 +0,0 @@ -"""Defines a multi-dimensional array and useful procedures for Numerical computation. - -Functions - -- array - NumPy Array construction -- zeros - Return an array of all zeros -- empty - Return an uninitialized array -- shape - Return shape of sequence or array -- rank - Return number of dimensions -- size - Return number of elements in entire array or a - certain dimension -- fromstring - Construct array from (byte) string -- take - Select sub-arrays using sequence of indices -- put - Set sub-arrays using sequence of 1-D indices -- putmask - Set portion of arrays using a mask -- reshape - Return array with new shape -- repeat - Repeat elements of array -- choose - Construct new array from indexed array tuple -- correlate - Correlate two 1-d arrays -- searchsorted - Search for element in 1-d array -- sum - Total sum over a specified dimension -- average - Average, possibly weighted, over axis or array. -- cumsum - Cumulative sum over a specified dimension -- product - Total product over a specified dimension -- cumproduct - Cumulative product over a specified dimension -- alltrue - Logical and over an entire axis -- sometrue - Logical or over an entire axis -- allclose - Tests if sequences are essentially equal - -More Functions: - -- arange - Return regularly spaced array -- asarray - Guarantee NumPy array -- convolve - Convolve two 1-d arrays -- swapaxes - Exchange axes -- concatenate - Join arrays together -- transpose - Permute axes -- sort - Sort elements of array -- argsort - Indices of sorted array -- argmax - Index of largest value -- argmin - Index of smallest value -- inner - Innerproduct of two arrays -- dot - Dot product (matrix multiplication) -- outer - Outerproduct of two arrays -- resize - Return array with arbitrary new shape -- indices - Tuple of indices -- fromfunction - Construct array from universal function -- diagonal - Return diagonal array -- trace - Trace of array -- dump - Dump array to file object (pickle) -- dumps - Return pickled string representing data -- load - Return array stored in file object -- loads - Return array from pickled string -- ravel - Return array as 1-D -- nonzero - Indices of nonzero elements for 1-D array -- shape - Shape of array -- where - Construct array from binary result -- compress - Elements of array where condition is true -- clip - Clip array between two values -- ones - Array of all ones -- identity - 2-D identity array (matrix) - -(Universal) Math Functions - - add logical_or exp - subtract logical_xor log - multiply logical_not log10 - divide maximum sin - divide_safe minimum sinh - conjugate bitwise_and sqrt - power bitwise_or tan - absolute bitwise_xor tanh - negative invert ceil - greater left_shift fabs - greater_equal right_shift floor - less arccos arctan2 - less_equal arcsin fmod - equal arctan hypot - not_equal cos around - logical_and cosh sign - arccosh arcsinh arctanh - -""" -from __future__ import division, absolute_import, print_function - -depends = ['testing'] -global_symbols = ['*'] diff --git a/numpy/core/memmap.py b/numpy/core/memmap.py index 536fa60943e1..b0d9cb3af7bf 100644 --- a/numpy/core/memmap.py +++ b/numpy/core/memmap.py @@ -1,8 +1,9 @@ -from __future__ import division, absolute_import, print_function +from contextlib import nullcontext import numpy as np from .numeric import uint8, ndarray, dtype -from numpy.compat import long, basestring, is_pathlib_path +from numpy.compat import os_fspath, is_pathlib_path +from numpy.core.overrides import set_module __all__ = ['memmap'] @@ -17,6 +18,8 @@ "write":"w+" } + +@set_module('numpy') class memmap(ndarray): """Create a memory-map to an array stored in a *binary* file on disk. @@ -34,7 +37,10 @@ class memmap(ndarray): This class may at some point be turned into a factory function which returns a view into an mmap buffer. - Delete the memmap instance to close the memmap file. + Flush the memmap instance to write the changes to the file. Currently there + is no API to close the underlying ``mmap``. It is tricky to ensure the + resource is actually closed, since it may be shared between different + memmap instances. Parameters @@ -94,7 +100,7 @@ class memmap(ndarray): flush Flush any changes in memory to file on disk. When you delete a memmap object, flush is called first to write - changes to disk before removing the object. + changes to disk. See also @@ -106,7 +112,7 @@ class memmap(ndarray): The memmap object can be used anywhere an ndarray is accepted. Given a memmap ``fp``, ``isinstance(fp, numpy.ndarray)`` returns ``True``. - + Memory-mapped files cannot be larger than 2GB on 32-bit systems. When a memmap causes a file to be created or extended beyond its @@ -130,9 +136,9 @@ class memmap(ndarray): >>> fp = np.memmap(filename, dtype='float32', mode='w+', shape=(3,4)) >>> fp - memmap([[ 0., 0., 0., 0.], - [ 0., 0., 0., 0.], - [ 0., 0., 0., 0.]], dtype=float32) + memmap([[0., 0., 0., 0.], + [0., 0., 0., 0.], + [0., 0., 0., 0.]], dtype=float32) Write data to memmap array: @@ -145,9 +151,9 @@ class memmap(ndarray): >>> fp.filename == path.abspath(filename) True - Deletion flushes memory changes to disk before removing the object: + Flushes memory changes to disk in order to read them back - >>> del fp + >>> fp.flush() Load the memmap and verify data was stored: @@ -206,83 +212,76 @@ def __new__(subtype, filename, dtype=uint8, mode='r+', offset=0, import os.path try: mode = mode_equivalents[mode] - except KeyError: + except KeyError as e: if mode not in valid_filemodes: - raise ValueError("mode must be one of %s" % - (valid_filemodes + list(mode_equivalents.keys()))) - - if hasattr(filename, 'read'): - fid = filename - own_file = False - elif is_pathlib_path(filename): - fid = filename.open((mode == 'c' and 'r' or mode)+'b') - own_file = True - else: - fid = open(filename, (mode == 'c' and 'r' or mode)+'b') - own_file = True + raise ValueError( + "mode must be one of {!r} (got {!r})" + .format(valid_filemodes + list(mode_equivalents.keys()), mode) + ) from None - if (mode == 'w+') and shape is None: + if mode == 'w+' and shape is None: raise ValueError("shape must be given") - fid.seek(0, 2) - flen = fid.tell() - descr = dtypedescr(dtype) - _dbytes = descr.itemsize - - if shape is None: - bytes = flen - offset - if (bytes % _dbytes): - fid.close() - raise ValueError("Size of available data is not a " - "multiple of the data-type size.") - size = bytes // _dbytes - shape = (size,) - else: - if not isinstance(shape, tuple): - shape = (shape,) - size = np.intp(1) # avoid default choice of np.int_, which might overflow - for k in shape: - size *= k - - bytes = long(offset + size*_dbytes) - - if mode == 'w+' or (mode == 'r+' and flen < bytes): - fid.seek(bytes - 1, 0) - fid.write(b'\0') - fid.flush() - - if mode == 'c': - acc = mmap.ACCESS_COPY - elif mode == 'r': - acc = mmap.ACCESS_READ - else: - acc = mmap.ACCESS_WRITE - - start = offset - offset % mmap.ALLOCATIONGRANULARITY - bytes -= start - array_offset = offset - start - mm = mmap.mmap(fid.fileno(), bytes, access=acc, offset=start) - - self = ndarray.__new__(subtype, shape, dtype=descr, buffer=mm, - offset=array_offset, order=order) - self._mmap = mm - self.offset = offset - self.mode = mode - - if isinstance(filename, basestring): - self.filename = os.path.abspath(filename) - elif is_pathlib_path(filename): - self.filename = filename.resolve() - # py3 returns int for TemporaryFile().name - elif (hasattr(filename, "name") and - isinstance(filename.name, basestring)): - self.filename = os.path.abspath(filename.name) - # same as memmap copies (e.g. memmap + 1) + if hasattr(filename, 'read'): + f_ctx = nullcontext(filename) else: - self.filename = None - - if own_file: - fid.close() + f_ctx = open(os_fspath(filename), ('r' if mode == 'c' else mode)+'b') + + with f_ctx as fid: + fid.seek(0, 2) + flen = fid.tell() + descr = dtypedescr(dtype) + _dbytes = descr.itemsize + + if shape is None: + bytes = flen - offset + if bytes % _dbytes: + raise ValueError("Size of available data is not a " + "multiple of the data-type size.") + size = bytes // _dbytes + shape = (size,) + else: + if not isinstance(shape, tuple): + shape = (shape,) + size = np.intp(1) # avoid default choice of np.int_, which might overflow + for k in shape: + size *= k + + bytes = int(offset + size*_dbytes) + + if mode in ('w+', 'r+') and flen < bytes: + fid.seek(bytes - 1, 0) + fid.write(b'\0') + fid.flush() + + if mode == 'c': + acc = mmap.ACCESS_COPY + elif mode == 'r': + acc = mmap.ACCESS_READ + else: + acc = mmap.ACCESS_WRITE + + start = offset - offset % mmap.ALLOCATIONGRANULARITY + bytes -= start + array_offset = offset - start + mm = mmap.mmap(fid.fileno(), bytes, access=acc, offset=start) + + self = ndarray.__new__(subtype, shape, dtype=descr, buffer=mm, + offset=array_offset, order=order) + self._mmap = mm + self.offset = offset + self.mode = mode + + if is_pathlib_path(filename): + # special case - if we were constructed with a pathlib.path, + # then filename is a path object, not a string + self.filename = filename.resolve() + elif hasattr(fid, "name") and isinstance(fid.name, str): + # py3 returns int for TemporaryFile().name + self.filename = os.path.abspath(fid.name) + # same as memmap copies (e.g. memmap + 1) + else: + self.filename = None return self @@ -317,7 +316,7 @@ def flush(self): self.base.flush() def __array_wrap__(self, arr, context=None): - arr = super(memmap, self).__array_wrap__(arr, context) + arr = super().__array_wrap__(arr, context) # Return a memmap if a memmap was given as the output of the # ufunc. Leave the arr class unchanged if self is not a memmap @@ -332,7 +331,7 @@ def __array_wrap__(self, arr, context=None): return arr.view(np.ndarray) def __getitem__(self, index): - res = super(memmap, self).__getitem__(index) + res = super().__getitem__(index) if type(res) is memmap and res._mmap is None: return res.view(type=ndarray) return res diff --git a/numpy/core/memmap.pyi b/numpy/core/memmap.pyi new file mode 100644 index 000000000000..ba595bf1ef64 --- /dev/null +++ b/numpy/core/memmap.pyi @@ -0,0 +1,5 @@ +from typing import List + +from numpy import memmap as memmap + +__all__: List[str] diff --git a/numpy/core/multiarray.py b/numpy/core/multiarray.py new file mode 100644 index 000000000000..f88d75978697 --- /dev/null +++ b/numpy/core/multiarray.py @@ -0,0 +1,1690 @@ +""" +Create the numpy.core.multiarray namespace for backward compatibility. In v1.16 +the multiarray and umath c-extension modules were merged into a single +_multiarray_umath extension module. So we replicate the old namespace +by importing from the extension module. + +""" + +import functools +from . import overrides +from . import _multiarray_umath +from ._multiarray_umath import * # noqa: F403 +# These imports are needed for backward compatibility, +# do not change them. issue gh-15518 +# _get_ndarray_c_version is semi-public, on purpose not added to __all__ +from ._multiarray_umath import ( + _fastCopyAndTranspose, _flagdict, _from_dlpack, _insert, _reconstruct, + _vec_string, _ARRAY_API, _monotonicity, _get_ndarray_c_version, + _set_madvise_hugepage, + ) + +__all__ = [ + '_ARRAY_API', 'ALLOW_THREADS', 'BUFSIZE', 'CLIP', 'DATETIMEUNITS', + 'ITEM_HASOBJECT', 'ITEM_IS_POINTER', 'LIST_PICKLE', 'MAXDIMS', + 'MAY_SHARE_BOUNDS', 'MAY_SHARE_EXACT', 'NEEDS_INIT', 'NEEDS_PYAPI', + 'RAISE', 'USE_GETITEM', 'USE_SETITEM', 'WRAP', '_fastCopyAndTranspose', + '_flagdict', '_from_dlpack', '_insert', '_reconstruct', '_vec_string', + '_monotonicity', 'add_docstring', 'arange', 'array', 'asarray', + 'asanyarray', 'ascontiguousarray', 'asfortranarray', 'bincount', + 'broadcast', 'busday_count', 'busday_offset', 'busdaycalendar', 'can_cast', + 'compare_chararrays', 'concatenate', 'copyto', 'correlate', 'correlate2', + 'count_nonzero', 'c_einsum', 'datetime_as_string', 'datetime_data', + 'dot', 'dragon4_positional', 'dragon4_scientific', 'dtype', + 'empty', 'empty_like', 'error', 'flagsobj', 'flatiter', 'format_longfloat', + 'frombuffer', 'fromfile', 'fromiter', 'fromstring', + 'get_handler_name', 'get_handler_version', 'inner', 'interp', + 'interp_complex', 'is_busday', 'lexsort', 'matmul', 'may_share_memory', + 'min_scalar_type', 'ndarray', 'nditer', 'nested_iters', + 'normalize_axis_index', 'packbits', 'promote_types', 'putmask', + 'ravel_multi_index', 'result_type', 'scalar', 'set_datetimeparse_function', + 'set_legacy_print_mode', 'set_numeric_ops', 'set_string_function', + 'set_typeDict', 'shares_memory', 'tracemalloc_domain', 'typeinfo', + 'unpackbits', 'unravel_index', 'vdot', 'where', 'zeros'] + +# For backward compatibility, make sure pickle imports these functions from here +_reconstruct.__module__ = 'numpy.core.multiarray' +scalar.__module__ = 'numpy.core.multiarray' + + +_from_dlpack.__module__ = 'numpy' +arange.__module__ = 'numpy' +array.__module__ = 'numpy' +asarray.__module__ = 'numpy' +asanyarray.__module__ = 'numpy' +ascontiguousarray.__module__ = 'numpy' +asfortranarray.__module__ = 'numpy' +datetime_data.__module__ = 'numpy' +empty.__module__ = 'numpy' +frombuffer.__module__ = 'numpy' +fromfile.__module__ = 'numpy' +fromiter.__module__ = 'numpy' +frompyfunc.__module__ = 'numpy' +fromstring.__module__ = 'numpy' +geterrobj.__module__ = 'numpy' +may_share_memory.__module__ = 'numpy' +nested_iters.__module__ = 'numpy' +promote_types.__module__ = 'numpy' +set_numeric_ops.__module__ = 'numpy' +seterrobj.__module__ = 'numpy' +zeros.__module__ = 'numpy' + + +# We can't verify dispatcher signatures because NumPy's C functions don't +# support introspection. +array_function_from_c_func_and_dispatcher = functools.partial( + overrides.array_function_from_dispatcher, + module='numpy', docs_from_dispatcher=True, verify=False) + + +@array_function_from_c_func_and_dispatcher(_multiarray_umath.empty_like) +def empty_like(prototype, dtype=None, order=None, subok=None, shape=None): + """ + empty_like(prototype, dtype=None, order='K', subok=True, shape=None) + + Return a new array with the same shape and type as a given array. + + Parameters + ---------- + prototype : array_like + The shape and data-type of `prototype` define these same attributes + of the returned array. + dtype : data-type, optional + Overrides the data type of the result. + + .. versionadded:: 1.6.0 + order : {'C', 'F', 'A', or 'K'}, optional + Overrides the memory layout of the result. 'C' means C-order, + 'F' means F-order, 'A' means 'F' if `prototype` is Fortran + contiguous, 'C' otherwise. 'K' means match the layout of `prototype` + as closely as possible. + + .. versionadded:: 1.6.0 + subok : bool, optional. + If True, then the newly created array will use the sub-class + type of `prototype`, otherwise it will be a base-class array. Defaults + to True. + shape : int or sequence of ints, optional. + Overrides the shape of the result. If order='K' and the number of + dimensions is unchanged, will try to keep order, otherwise, + order='C' is implied. + + .. versionadded:: 1.17.0 + + Returns + ------- + out : ndarray + Array of uninitialized (arbitrary) data with the same + shape and type as `prototype`. + + See Also + -------- + ones_like : Return an array of ones with shape and type of input. + zeros_like : Return an array of zeros with shape and type of input. + full_like : Return a new array with shape of input filled with value. + empty : Return a new uninitialized array. + + Notes + ----- + This function does *not* initialize the returned array; to do that use + `zeros_like` or `ones_like` instead. It may be marginally faster than + the functions that do set the array values. + + Examples + -------- + >>> a = ([1,2,3], [4,5,6]) # a is array-like + >>> np.empty_like(a) + array([[-1073741821, -1073741821, 3], # uninitialized + [ 0, 0, -1073741821]]) + >>> a = np.array([[1., 2., 3.],[4.,5.,6.]]) + >>> np.empty_like(a) + array([[ -2.00000715e+000, 1.48219694e-323, -2.00000572e+000], # uninitialized + [ 4.38791518e-305, -2.00000715e+000, 4.17269252e-309]]) + + """ + return (prototype,) + + +@array_function_from_c_func_and_dispatcher(_multiarray_umath.concatenate) +def concatenate(arrays, axis=None, out=None, *, dtype=None, casting=None): + """ + concatenate((a1, a2, ...), axis=0, out=None, dtype=None, casting="same_kind") + + Join a sequence of arrays along an existing axis. + + Parameters + ---------- + a1, a2, ... : sequence of array_like + The arrays must have the same shape, except in the dimension + corresponding to `axis` (the first, by default). + axis : int, optional + The axis along which the arrays will be joined. If axis is None, + arrays are flattened before use. Default is 0. + out : ndarray, optional + If provided, the destination to place the result. The shape must be + correct, matching that of what concatenate would have returned if no + out argument were specified. + dtype : str or dtype + If provided, the destination array will have this dtype. Cannot be + provided together with `out`. + + .. versionadded:: 1.20.0 + + casting : {'no', 'equiv', 'safe', 'same_kind', 'unsafe'}, optional + Controls what kind of data casting may occur. Defaults to 'same_kind'. + + .. versionadded:: 1.20.0 + + Returns + ------- + res : ndarray + The concatenated array. + + See Also + -------- + ma.concatenate : Concatenate function that preserves input masks. + array_split : Split an array into multiple sub-arrays of equal or + near-equal size. + split : Split array into a list of multiple sub-arrays of equal size. + hsplit : Split array into multiple sub-arrays horizontally (column wise). + vsplit : Split array into multiple sub-arrays vertically (row wise). + dsplit : Split array into multiple sub-arrays along the 3rd axis (depth). + stack : Stack a sequence of arrays along a new axis. + block : Assemble arrays from blocks. + hstack : Stack arrays in sequence horizontally (column wise). + vstack : Stack arrays in sequence vertically (row wise). + dstack : Stack arrays in sequence depth wise (along third dimension). + column_stack : Stack 1-D arrays as columns into a 2-D array. + + Notes + ----- + When one or more of the arrays to be concatenated is a MaskedArray, + this function will return a MaskedArray object instead of an ndarray, + but the input masks are *not* preserved. In cases where a MaskedArray + is expected as input, use the ma.concatenate function from the masked + array module instead. + + Examples + -------- + >>> a = np.array([[1, 2], [3, 4]]) + >>> b = np.array([[5, 6]]) + >>> np.concatenate((a, b), axis=0) + array([[1, 2], + [3, 4], + [5, 6]]) + >>> np.concatenate((a, b.T), axis=1) + array([[1, 2, 5], + [3, 4, 6]]) + >>> np.concatenate((a, b), axis=None) + array([1, 2, 3, 4, 5, 6]) + + This function will not preserve masking of MaskedArray inputs. + + >>> a = np.ma.arange(3) + >>> a[1] = np.ma.masked + >>> b = np.arange(2, 5) + >>> a + masked_array(data=[0, --, 2], + mask=[False, True, False], + fill_value=999999) + >>> b + array([2, 3, 4]) + >>> np.concatenate([a, b]) + masked_array(data=[0, 1, 2, 2, 3, 4], + mask=False, + fill_value=999999) + >>> np.ma.concatenate([a, b]) + masked_array(data=[0, --, 2, 2, 3, 4], + mask=[False, True, False, False, False, False], + fill_value=999999) + + """ + if out is not None: + # optimize for the typical case where only arrays is provided + arrays = list(arrays) + arrays.append(out) + return arrays + + +@array_function_from_c_func_and_dispatcher(_multiarray_umath.inner) +def inner(a, b): + """ + inner(a, b, /) + + Inner product of two arrays. + + Ordinary inner product of vectors for 1-D arrays (without complex + conjugation), in higher dimensions a sum product over the last axes. + + Parameters + ---------- + a, b : array_like + If `a` and `b` are nonscalar, their last dimensions must match. + + Returns + ------- + out : ndarray + If `a` and `b` are both + scalars or both 1-D arrays then a scalar is returned; otherwise + an array is returned. + ``out.shape = (*a.shape[:-1], *b.shape[:-1])`` + + Raises + ------ + ValueError + If both `a` and `b` are nonscalar and their last dimensions have + different sizes. + + See Also + -------- + tensordot : Sum products over arbitrary axes. + dot : Generalised matrix product, using second last dimension of `b`. + einsum : Einstein summation convention. + + Notes + ----- + For vectors (1-D arrays) it computes the ordinary inner-product:: + + np.inner(a, b) = sum(a[:]*b[:]) + + More generally, if `ndim(a) = r > 0` and `ndim(b) = s > 0`:: + + np.inner(a, b) = np.tensordot(a, b, axes=(-1,-1)) + + or explicitly:: + + np.inner(a, b)[i0,...,ir-2,j0,...,js-2] + = sum(a[i0,...,ir-2,:]*b[j0,...,js-2,:]) + + In addition `a` or `b` may be scalars, in which case:: + + np.inner(a,b) = a*b + + Examples + -------- + Ordinary inner product for vectors: + + >>> a = np.array([1,2,3]) + >>> b = np.array([0,1,0]) + >>> np.inner(a, b) + 2 + + Some multidimensional examples: + + >>> a = np.arange(24).reshape((2,3,4)) + >>> b = np.arange(4) + >>> c = np.inner(a, b) + >>> c.shape + (2, 3) + >>> c + array([[ 14, 38, 62], + [ 86, 110, 134]]) + + >>> a = np.arange(2).reshape((1,1,2)) + >>> b = np.arange(6).reshape((3,2)) + >>> c = np.inner(a, b) + >>> c.shape + (1, 1, 3) + >>> c + array([[[1, 3, 5]]]) + + An example where `b` is a scalar: + + >>> np.inner(np.eye(2), 7) + array([[7., 0.], + [0., 7.]]) + + """ + return (a, b) + + +@array_function_from_c_func_and_dispatcher(_multiarray_umath.where) +def where(condition, x=None, y=None): + """ + where(condition, [x, y], /) + + Return elements chosen from `x` or `y` depending on `condition`. + + .. note:: + When only `condition` is provided, this function is a shorthand for + ``np.asarray(condition).nonzero()``. Using `nonzero` directly should be + preferred, as it behaves correctly for subclasses. The rest of this + documentation covers only the case where all three arguments are + provided. + + Parameters + ---------- + condition : array_like, bool + Where True, yield `x`, otherwise yield `y`. + x, y : array_like + Values from which to choose. `x`, `y` and `condition` need to be + broadcastable to some shape. + + Returns + ------- + out : ndarray + An array with elements from `x` where `condition` is True, and elements + from `y` elsewhere. + + See Also + -------- + choose + nonzero : The function that is called when x and y are omitted + + Notes + ----- + If all the arrays are 1-D, `where` is equivalent to:: + + [xv if c else yv + for c, xv, yv in zip(condition, x, y)] + + Examples + -------- + >>> a = np.arange(10) + >>> a + array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) + >>> np.where(a < 5, a, 10*a) + array([ 0, 1, 2, 3, 4, 50, 60, 70, 80, 90]) + + This can be used on multidimensional arrays too: + + >>> np.where([[True, False], [True, True]], + ... [[1, 2], [3, 4]], + ... [[9, 8], [7, 6]]) + array([[1, 8], + [3, 4]]) + + The shapes of x, y, and the condition are broadcast together: + + >>> x, y = np.ogrid[:3, :4] + >>> np.where(x < y, x, 10 + y) # both x and 10+y are broadcast + array([[10, 0, 0, 0], + [10, 11, 1, 1], + [10, 11, 12, 2]]) + + >>> a = np.array([[0, 1, 2], + ... [0, 2, 4], + ... [0, 3, 6]]) + >>> np.where(a < 4, a, -1) # -1 is broadcast + array([[ 0, 1, 2], + [ 0, 2, -1], + [ 0, 3, -1]]) + """ + return (condition, x, y) + + +@array_function_from_c_func_and_dispatcher(_multiarray_umath.lexsort) +def lexsort(keys, axis=None): + """ + lexsort(keys, axis=-1) + + Perform an indirect stable sort using a sequence of keys. + + Given multiple sorting keys, which can be interpreted as columns in a + spreadsheet, lexsort returns an array of integer indices that describes + the sort order by multiple columns. The last key in the sequence is used + for the primary sort order, the second-to-last key for the secondary sort + order, and so on. The keys argument must be a sequence of objects that + can be converted to arrays of the same shape. If a 2D array is provided + for the keys argument, its rows are interpreted as the sorting keys and + sorting is according to the last row, second last row etc. + + Parameters + ---------- + keys : (k, N) array or tuple containing k (N,)-shaped sequences + The `k` different "columns" to be sorted. The last column (or row if + `keys` is a 2D array) is the primary sort key. + axis : int, optional + Axis to be indirectly sorted. By default, sort over the last axis. + + Returns + ------- + indices : (N,) ndarray of ints + Array of indices that sort the keys along the specified axis. + + See Also + -------- + argsort : Indirect sort. + ndarray.sort : In-place sort. + sort : Return a sorted copy of an array. + + Examples + -------- + Sort names: first by surname, then by name. + + >>> surnames = ('Hertz', 'Galilei', 'Hertz') + >>> first_names = ('Heinrich', 'Galileo', 'Gustav') + >>> ind = np.lexsort((first_names, surnames)) + >>> ind + array([1, 2, 0]) + + >>> [surnames[i] + ", " + first_names[i] for i in ind] + ['Galilei, Galileo', 'Hertz, Gustav', 'Hertz, Heinrich'] + + Sort two columns of numbers: + + >>> a = [1,5,1,4,3,4,4] # First column + >>> b = [9,4,0,4,0,2,1] # Second column + >>> ind = np.lexsort((b,a)) # Sort by a, then by b + >>> ind + array([2, 0, 4, 6, 5, 3, 1]) + + >>> [(a[i],b[i]) for i in ind] + [(1, 0), (1, 9), (3, 0), (4, 1), (4, 2), (4, 4), (5, 4)] + + Note that sorting is first according to the elements of ``a``. + Secondary sorting is according to the elements of ``b``. + + A normal ``argsort`` would have yielded: + + >>> [(a[i],b[i]) for i in np.argsort(a)] + [(1, 9), (1, 0), (3, 0), (4, 4), (4, 2), (4, 1), (5, 4)] + + Structured arrays are sorted lexically by ``argsort``: + + >>> x = np.array([(1,9), (5,4), (1,0), (4,4), (3,0), (4,2), (4,1)], + ... dtype=np.dtype([('x', int), ('y', int)])) + + >>> np.argsort(x) # or np.argsort(x, order=('x', 'y')) + array([2, 0, 4, 6, 5, 3, 1]) + + """ + if isinstance(keys, tuple): + return keys + else: + return (keys,) + + +@array_function_from_c_func_and_dispatcher(_multiarray_umath.can_cast) +def can_cast(from_, to, casting=None): + """ + can_cast(from_, to, casting='safe') + + Returns True if cast between data types can occur according to the + casting rule. If from is a scalar or array scalar, also returns + True if the scalar value can be cast without overflow or truncation + to an integer. + + Parameters + ---------- + from_ : dtype, dtype specifier, scalar, or array + Data type, scalar, or array to cast from. + to : dtype or dtype specifier + Data type to cast to. + casting : {'no', 'equiv', 'safe', 'same_kind', 'unsafe'}, optional + Controls what kind of data casting may occur. + + * 'no' means the data types should not be cast at all. + * 'equiv' means only byte-order changes are allowed. + * 'safe' means only casts which can preserve values are allowed. + * 'same_kind' means only safe casts or casts within a kind, + like float64 to float32, are allowed. + * 'unsafe' means any data conversions may be done. + + Returns + ------- + out : bool + True if cast can occur according to the casting rule. + + Notes + ----- + .. versionchanged:: 1.17.0 + Casting between a simple data type and a structured one is possible only + for "unsafe" casting. Casting to multiple fields is allowed, but + casting from multiple fields is not. + + .. versionchanged:: 1.9.0 + Casting from numeric to string types in 'safe' casting mode requires + that the string dtype length is long enough to store the maximum + integer/float value converted. + + See also + -------- + dtype, result_type + + Examples + -------- + Basic examples + + >>> np.can_cast(np.int32, np.int64) + True + >>> np.can_cast(np.float64, complex) + True + >>> np.can_cast(complex, float) + False + + >>> np.can_cast('i8', 'f8') + True + >>> np.can_cast('i8', 'f4') + False + >>> np.can_cast('i4', 'S4') + False + + Casting scalars + + >>> np.can_cast(100, 'i1') + True + >>> np.can_cast(150, 'i1') + False + >>> np.can_cast(150, 'u1') + True + + >>> np.can_cast(3.5e100, np.float32) + False + >>> np.can_cast(1000.0, np.float32) + True + + Array scalar checks the value, array does not + + >>> np.can_cast(np.array(1000.0), np.float32) + True + >>> np.can_cast(np.array([1000.0]), np.float32) + False + + Using the casting rules + + >>> np.can_cast('i8', 'i8', 'no') + True + >>> np.can_cast('<i8', '>i8', 'no') + False + + >>> np.can_cast('<i8', '>i8', 'equiv') + True + >>> np.can_cast('<i4', '>i8', 'equiv') + False + + >>> np.can_cast('<i4', '>i8', 'safe') + True + >>> np.can_cast('<i8', '>i4', 'safe') + False + + >>> np.can_cast('<i8', '>i4', 'same_kind') + True + >>> np.can_cast('<i8', '>u4', 'same_kind') + False + + >>> np.can_cast('<i8', '>u4', 'unsafe') + True + + """ + return (from_,) + + +@array_function_from_c_func_and_dispatcher(_multiarray_umath.min_scalar_type) +def min_scalar_type(a): + """ + min_scalar_type(a, /) + + For scalar ``a``, returns the data type with the smallest size + and smallest scalar kind which can hold its value. For non-scalar + array ``a``, returns the vector's dtype unmodified. + + Floating point values are not demoted to integers, + and complex values are not demoted to floats. + + Parameters + ---------- + a : scalar or array_like + The value whose minimal data type is to be found. + + Returns + ------- + out : dtype + The minimal data type. + + Notes + ----- + .. versionadded:: 1.6.0 + + See Also + -------- + result_type, promote_types, dtype, can_cast + + Examples + -------- + >>> np.min_scalar_type(10) + dtype('uint8') + + >>> np.min_scalar_type(-260) + dtype('int16') + + >>> np.min_scalar_type(3.1) + dtype('float16') + + >>> np.min_scalar_type(1e50) + dtype('float64') + + >>> np.min_scalar_type(np.arange(4,dtype='f8')) + dtype('float64') + + """ + return (a,) + + +@array_function_from_c_func_and_dispatcher(_multiarray_umath.result_type) +def result_type(*arrays_and_dtypes): + """ + result_type(*arrays_and_dtypes) + + Returns the type that results from applying the NumPy + type promotion rules to the arguments. + + Type promotion in NumPy works similarly to the rules in languages + like C++, with some slight differences. When both scalars and + arrays are used, the array's type takes precedence and the actual value + of the scalar is taken into account. + + For example, calculating 3*a, where a is an array of 32-bit floats, + intuitively should result in a 32-bit float output. If the 3 is a + 32-bit integer, the NumPy rules indicate it can't convert losslessly + into a 32-bit float, so a 64-bit float should be the result type. + By examining the value of the constant, '3', we see that it fits in + an 8-bit integer, which can be cast losslessly into the 32-bit float. + + Parameters + ---------- + arrays_and_dtypes : list of arrays and dtypes + The operands of some operation whose result type is needed. + + Returns + ------- + out : dtype + The result type. + + See also + -------- + dtype, promote_types, min_scalar_type, can_cast + + Notes + ----- + .. versionadded:: 1.6.0 + + The specific algorithm used is as follows. + + Categories are determined by first checking which of boolean, + integer (int/uint), or floating point (float/complex) the maximum + kind of all the arrays and the scalars are. + + If there are only scalars or the maximum category of the scalars + is higher than the maximum category of the arrays, + the data types are combined with :func:`promote_types` + to produce the return value. + + Otherwise, `min_scalar_type` is called on each array, and + the resulting data types are all combined with :func:`promote_types` + to produce the return value. + + The set of int values is not a subset of the uint values for types + with the same number of bits, something not reflected in + :func:`min_scalar_type`, but handled as a special case in `result_type`. + + Examples + -------- + >>> np.result_type(3, np.arange(7, dtype='i1')) + dtype('int8') + + >>> np.result_type('i4', 'c8') + dtype('complex128') + + >>> np.result_type(3.0, -2) + dtype('float64') + + """ + return arrays_and_dtypes + + +@array_function_from_c_func_and_dispatcher(_multiarray_umath.dot) +def dot(a, b, out=None): + """ + dot(a, b, out=None) + + Dot product of two arrays. Specifically, + + - If both `a` and `b` are 1-D arrays, it is inner product of vectors + (without complex conjugation). + + - If both `a` and `b` are 2-D arrays, it is matrix multiplication, + but using :func:`matmul` or ``a @ b`` is preferred. + + - If either `a` or `b` is 0-D (scalar), it is equivalent to :func:`multiply` + and using ``numpy.multiply(a, b)`` or ``a * b`` is preferred. + + - If `a` is an N-D array and `b` is a 1-D array, it is a sum product over + the last axis of `a` and `b`. + + - If `a` is an N-D array and `b` is an M-D array (where ``M>=2``), it is a + sum product over the last axis of `a` and the second-to-last axis of `b`:: + + dot(a, b)[i,j,k,m] = sum(a[i,j,:] * b[k,:,m]) + + Parameters + ---------- + a : array_like + First argument. + b : array_like + Second argument. + out : ndarray, optional + Output argument. This must have the exact kind that would be returned + if it was not used. In particular, it must have the right type, must be + C-contiguous, and its dtype must be the dtype that would be returned + for `dot(a,b)`. This is a performance feature. Therefore, if these + conditions are not met, an exception is raised, instead of attempting + to be flexible. + + Returns + ------- + output : ndarray + Returns the dot product of `a` and `b`. If `a` and `b` are both + scalars or both 1-D arrays then a scalar is returned; otherwise + an array is returned. + If `out` is given, then it is returned. + + Raises + ------ + ValueError + If the last dimension of `a` is not the same size as + the second-to-last dimension of `b`. + + See Also + -------- + vdot : Complex-conjugating dot product. + tensordot : Sum products over arbitrary axes. + einsum : Einstein summation convention. + matmul : '@' operator as method with out parameter. + linalg.multi_dot : Chained dot product. + + Examples + -------- + >>> np.dot(3, 4) + 12 + + Neither argument is complex-conjugated: + + >>> np.dot([2j, 3j], [2j, 3j]) + (-13+0j) + + For 2-D arrays it is the matrix product: + + >>> a = [[1, 0], [0, 1]] + >>> b = [[4, 1], [2, 2]] + >>> np.dot(a, b) + array([[4, 1], + [2, 2]]) + + >>> a = np.arange(3*4*5*6).reshape((3,4,5,6)) + >>> b = np.arange(3*4*5*6)[::-1].reshape((5,4,6,3)) + >>> np.dot(a, b)[2,3,2,1,2,2] + 499128 + >>> sum(a[2,3,2,:] * b[1,2,:,2]) + 499128 + + """ + return (a, b, out) + + +@array_function_from_c_func_and_dispatcher(_multiarray_umath.vdot) +def vdot(a, b): + """ + vdot(a, b, /) + + Return the dot product of two vectors. + + The vdot(`a`, `b`) function handles complex numbers differently than + dot(`a`, `b`). If the first argument is complex the complex conjugate + of the first argument is used for the calculation of the dot product. + + Note that `vdot` handles multidimensional arrays differently than `dot`: + it does *not* perform a matrix product, but flattens input arguments + to 1-D vectors first. Consequently, it should only be used for vectors. + + Parameters + ---------- + a : array_like + If `a` is complex the complex conjugate is taken before calculation + of the dot product. + b : array_like + Second argument to the dot product. + + Returns + ------- + output : ndarray + Dot product of `a` and `b`. Can be an int, float, or + complex depending on the types of `a` and `b`. + + See Also + -------- + dot : Return the dot product without using the complex conjugate of the + first argument. + + Examples + -------- + >>> a = np.array([1+2j,3+4j]) + >>> b = np.array([5+6j,7+8j]) + >>> np.vdot(a, b) + (70-8j) + >>> np.vdot(b, a) + (70+8j) + + Note that higher-dimensional arrays are flattened! + + >>> a = np.array([[1, 4], [5, 6]]) + >>> b = np.array([[4, 1], [2, 2]]) + >>> np.vdot(a, b) + 30 + >>> np.vdot(b, a) + 30 + >>> 1*4 + 4*1 + 5*2 + 6*2 + 30 + + """ + return (a, b) + + +@array_function_from_c_func_and_dispatcher(_multiarray_umath.bincount) +def bincount(x, weights=None, minlength=None): + """ + bincount(x, /, weights=None, minlength=0) + + Count number of occurrences of each value in array of non-negative ints. + + The number of bins (of size 1) is one larger than the largest value in + `x`. If `minlength` is specified, there will be at least this number + of bins in the output array (though it will be longer if necessary, + depending on the contents of `x`). + Each bin gives the number of occurrences of its index value in `x`. + If `weights` is specified the input array is weighted by it, i.e. if a + value ``n`` is found at position ``i``, ``out[n] += weight[i]`` instead + of ``out[n] += 1``. + + Parameters + ---------- + x : array_like, 1 dimension, nonnegative ints + Input array. + weights : array_like, optional + Weights, array of the same shape as `x`. + minlength : int, optional + A minimum number of bins for the output array. + + .. versionadded:: 1.6.0 + + Returns + ------- + out : ndarray of ints + The result of binning the input array. + The length of `out` is equal to ``np.amax(x)+1``. + + Raises + ------ + ValueError + If the input is not 1-dimensional, or contains elements with negative + values, or if `minlength` is negative. + TypeError + If the type of the input is float or complex. + + See Also + -------- + histogram, digitize, unique + + Examples + -------- + >>> np.bincount(np.arange(5)) + array([1, 1, 1, 1, 1]) + >>> np.bincount(np.array([0, 1, 1, 3, 2, 1, 7])) + array([1, 3, 1, 1, 0, 0, 0, 1]) + + >>> x = np.array([0, 1, 1, 3, 2, 1, 7, 23]) + >>> np.bincount(x).size == np.amax(x)+1 + True + + The input array needs to be of integer dtype, otherwise a + TypeError is raised: + + >>> np.bincount(np.arange(5, dtype=float)) + Traceback (most recent call last): + ... + TypeError: Cannot cast array data from dtype('float64') to dtype('int64') + according to the rule 'safe' + + A possible use of ``bincount`` is to perform sums over + variable-size chunks of an array, using the ``weights`` keyword. + + >>> w = np.array([0.3, 0.5, 0.2, 0.7, 1., -0.6]) # weights + >>> x = np.array([0, 1, 1, 2, 2, 2]) + >>> np.bincount(x, weights=w) + array([ 0.3, 0.7, 1.1]) + + """ + return (x, weights) + + +@array_function_from_c_func_and_dispatcher(_multiarray_umath.ravel_multi_index) +def ravel_multi_index(multi_index, dims, mode=None, order=None): + """ + ravel_multi_index(multi_index, dims, mode='raise', order='C') + + Converts a tuple of index arrays into an array of flat + indices, applying boundary modes to the multi-index. + + Parameters + ---------- + multi_index : tuple of array_like + A tuple of integer arrays, one array for each dimension. + dims : tuple of ints + The shape of array into which the indices from ``multi_index`` apply. + mode : {'raise', 'wrap', 'clip'}, optional + Specifies how out-of-bounds indices are handled. Can specify + either one mode or a tuple of modes, one mode per index. + + * 'raise' -- raise an error (default) + * 'wrap' -- wrap around + * 'clip' -- clip to the range + + In 'clip' mode, a negative index which would normally + wrap will clip to 0 instead. + order : {'C', 'F'}, optional + Determines whether the multi-index should be viewed as + indexing in row-major (C-style) or column-major + (Fortran-style) order. + + Returns + ------- + raveled_indices : ndarray + An array of indices into the flattened version of an array + of dimensions ``dims``. + + See Also + -------- + unravel_index + + Notes + ----- + .. versionadded:: 1.6.0 + + Examples + -------- + >>> arr = np.array([[3,6,6],[4,5,1]]) + >>> np.ravel_multi_index(arr, (7,6)) + array([22, 41, 37]) + >>> np.ravel_multi_index(arr, (7,6), order='F') + array([31, 41, 13]) + >>> np.ravel_multi_index(arr, (4,6), mode='clip') + array([22, 23, 19]) + >>> np.ravel_multi_index(arr, (4,4), mode=('clip','wrap')) + array([12, 13, 13]) + + >>> np.ravel_multi_index((3,1,4,1), (6,7,8,9)) + 1621 + """ + return multi_index + + +@array_function_from_c_func_and_dispatcher(_multiarray_umath.unravel_index) +def unravel_index(indices, shape=None, order=None): + """ + unravel_index(indices, shape, order='C') + + Converts a flat index or array of flat indices into a tuple + of coordinate arrays. + + Parameters + ---------- + indices : array_like + An integer array whose elements are indices into the flattened + version of an array of dimensions ``shape``. Before version 1.6.0, + this function accepted just one index value. + shape : tuple of ints + The shape of the array to use for unraveling ``indices``. + + .. versionchanged:: 1.16.0 + Renamed from ``dims`` to ``shape``. + + order : {'C', 'F'}, optional + Determines whether the indices should be viewed as indexing in + row-major (C-style) or column-major (Fortran-style) order. + + .. versionadded:: 1.6.0 + + Returns + ------- + unraveled_coords : tuple of ndarray + Each array in the tuple has the same shape as the ``indices`` + array. + + See Also + -------- + ravel_multi_index + + Examples + -------- + >>> np.unravel_index([22, 41, 37], (7,6)) + (array([3, 6, 6]), array([4, 5, 1])) + >>> np.unravel_index([31, 41, 13], (7,6), order='F') + (array([3, 6, 6]), array([4, 5, 1])) + + >>> np.unravel_index(1621, (6,7,8,9)) + (3, 1, 4, 1) + + """ + return (indices,) + + +@array_function_from_c_func_and_dispatcher(_multiarray_umath.copyto) +def copyto(dst, src, casting=None, where=None): + """ + copyto(dst, src, casting='same_kind', where=True) + + Copies values from one array to another, broadcasting as necessary. + + Raises a TypeError if the `casting` rule is violated, and if + `where` is provided, it selects which elements to copy. + + .. versionadded:: 1.7.0 + + Parameters + ---------- + dst : ndarray + The array into which values are copied. + src : array_like + The array from which values are copied. + casting : {'no', 'equiv', 'safe', 'same_kind', 'unsafe'}, optional + Controls what kind of data casting may occur when copying. + + * 'no' means the data types should not be cast at all. + * 'equiv' means only byte-order changes are allowed. + * 'safe' means only casts which can preserve values are allowed. + * 'same_kind' means only safe casts or casts within a kind, + like float64 to float32, are allowed. + * 'unsafe' means any data conversions may be done. + where : array_like of bool, optional + A boolean array which is broadcasted to match the dimensions + of `dst`, and selects elements to copy from `src` to `dst` + wherever it contains the value True. + """ + return (dst, src, where) + + +@array_function_from_c_func_and_dispatcher(_multiarray_umath.putmask) +def putmask(a, mask, values): + """ + putmask(a, mask, values) + + Changes elements of an array based on conditional and input values. + + Sets ``a.flat[n] = values[n]`` for each n where ``mask.flat[n]==True``. + + If `values` is not the same size as `a` and `mask` then it will repeat. + This gives behavior different from ``a[mask] = values``. + + Parameters + ---------- + a : ndarray + Target array. + mask : array_like + Boolean mask array. It has to be the same shape as `a`. + values : array_like + Values to put into `a` where `mask` is True. If `values` is smaller + than `a` it will be repeated. + + See Also + -------- + place, put, take, copyto + + Examples + -------- + >>> x = np.arange(6).reshape(2, 3) + >>> np.putmask(x, x>2, x**2) + >>> x + array([[ 0, 1, 2], + [ 9, 16, 25]]) + + If `values` is smaller than `a` it is repeated: + + >>> x = np.arange(5) + >>> np.putmask(x, x>1, [-33, -44]) + >>> x + array([ 0, 1, -33, -44, -33]) + + """ + return (a, mask, values) + + +@array_function_from_c_func_and_dispatcher(_multiarray_umath.packbits) +def packbits(a, axis=None, bitorder='big'): + """ + packbits(a, /, axis=None, bitorder='big') + + Packs the elements of a binary-valued array into bits in a uint8 array. + + The result is padded to full bytes by inserting zero bits at the end. + + Parameters + ---------- + a : array_like + An array of integers or booleans whose elements should be packed to + bits. + axis : int, optional + The dimension over which bit-packing is done. + ``None`` implies packing the flattened array. + bitorder : {'big', 'little'}, optional + The order of the input bits. 'big' will mimic bin(val), + ``[0, 0, 0, 0, 0, 0, 1, 1] => 3 = 0b00000011``, 'little' will + reverse the order so ``[1, 1, 0, 0, 0, 0, 0, 0] => 3``. + Defaults to 'big'. + + .. versionadded:: 1.17.0 + + Returns + ------- + packed : ndarray + Array of type uint8 whose elements represent bits corresponding to the + logical (0 or nonzero) value of the input elements. The shape of + `packed` has the same number of dimensions as the input (unless `axis` + is None, in which case the output is 1-D). + + See Also + -------- + unpackbits: Unpacks elements of a uint8 array into a binary-valued output + array. + + Examples + -------- + >>> a = np.array([[[1,0,1], + ... [0,1,0]], + ... [[1,1,0], + ... [0,0,1]]]) + >>> b = np.packbits(a, axis=-1) + >>> b + array([[[160], + [ 64]], + [[192], + [ 32]]], dtype=uint8) + + Note that in binary 160 = 1010 0000, 64 = 0100 0000, 192 = 1100 0000, + and 32 = 0010 0000. + + """ + return (a,) + + +@array_function_from_c_func_and_dispatcher(_multiarray_umath.unpackbits) +def unpackbits(a, axis=None, count=None, bitorder='big'): + """ + unpackbits(a, /, axis=None, count=None, bitorder='big') + + Unpacks elements of a uint8 array into a binary-valued output array. + + Each element of `a` represents a bit-field that should be unpacked + into a binary-valued output array. The shape of the output array is + either 1-D (if `axis` is ``None``) or the same shape as the input + array with unpacking done along the axis specified. + + Parameters + ---------- + a : ndarray, uint8 type + Input array. + axis : int, optional + The dimension over which bit-unpacking is done. + ``None`` implies unpacking the flattened array. + count : int or None, optional + The number of elements to unpack along `axis`, provided as a way + of undoing the effect of packing a size that is not a multiple + of eight. A non-negative number means to only unpack `count` + bits. A negative number means to trim off that many bits from + the end. ``None`` means to unpack the entire array (the + default). Counts larger than the available number of bits will + add zero padding to the output. Negative counts must not + exceed the available number of bits. + + .. versionadded:: 1.17.0 + + bitorder : {'big', 'little'}, optional + The order of the returned bits. 'big' will mimic bin(val), + ``3 = 0b00000011 => [0, 0, 0, 0, 0, 0, 1, 1]``, 'little' will reverse + the order to ``[1, 1, 0, 0, 0, 0, 0, 0]``. + Defaults to 'big'. + + .. versionadded:: 1.17.0 + + Returns + ------- + unpacked : ndarray, uint8 type + The elements are binary-valued (0 or 1). + + See Also + -------- + packbits : Packs the elements of a binary-valued array into bits in + a uint8 array. + + Examples + -------- + >>> a = np.array([[2], [7], [23]], dtype=np.uint8) + >>> a + array([[ 2], + [ 7], + [23]], dtype=uint8) + >>> b = np.unpackbits(a, axis=1) + >>> b + array([[0, 0, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 0, 0, 1, 1, 1], + [0, 0, 0, 1, 0, 1, 1, 1]], dtype=uint8) + >>> c = np.unpackbits(a, axis=1, count=-3) + >>> c + array([[0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 1, 0]], dtype=uint8) + + >>> p = np.packbits(b, axis=0) + >>> np.unpackbits(p, axis=0) + array([[0, 0, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 0, 0, 1, 1, 1], + [0, 0, 0, 1, 0, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0]], dtype=uint8) + >>> np.array_equal(b, np.unpackbits(p, axis=0, count=b.shape[0])) + True + + """ + return (a,) + + +@array_function_from_c_func_and_dispatcher(_multiarray_umath.shares_memory) +def shares_memory(a, b, max_work=None): + """ + shares_memory(a, b, /, max_work=None) + + Determine if two arrays share memory. + + .. warning:: + + This function can be exponentially slow for some inputs, unless + `max_work` is set to a finite number or ``MAY_SHARE_BOUNDS``. + If in doubt, use `numpy.may_share_memory` instead. + + Parameters + ---------- + a, b : ndarray + Input arrays + max_work : int, optional + Effort to spend on solving the overlap problem (maximum number + of candidate solutions to consider). The following special + values are recognized: + + max_work=MAY_SHARE_EXACT (default) + The problem is solved exactly. In this case, the function returns + True only if there is an element shared between the arrays. Finding + the exact solution may take extremely long in some cases. + max_work=MAY_SHARE_BOUNDS + Only the memory bounds of a and b are checked. + + Raises + ------ + numpy.TooHardError + Exceeded max_work. + + Returns + ------- + out : bool + + See Also + -------- + may_share_memory + + Examples + -------- + >>> x = np.array([1, 2, 3, 4]) + >>> np.shares_memory(x, np.array([5, 6, 7])) + False + >>> np.shares_memory(x[::2], x) + True + >>> np.shares_memory(x[::2], x[1::2]) + False + + Checking whether two arrays share memory is NP-complete, and + runtime may increase exponentially in the number of + dimensions. Hence, `max_work` should generally be set to a finite + number, as it is possible to construct examples that take + extremely long to run: + + >>> from numpy.lib.stride_tricks import as_strided + >>> x = np.zeros([192163377], dtype=np.int8) + >>> x1 = as_strided(x, strides=(36674, 61119, 85569), shape=(1049, 1049, 1049)) + >>> x2 = as_strided(x[64023025:], strides=(12223, 12224, 1), shape=(1049, 1049, 1)) + >>> np.shares_memory(x1, x2, max_work=1000) + Traceback (most recent call last): + ... + numpy.TooHardError: Exceeded max_work + + Running ``np.shares_memory(x1, x2)`` without `max_work` set takes + around 1 minute for this case. It is possible to find problems + that take still significantly longer. + + """ + return (a, b) + + +@array_function_from_c_func_and_dispatcher(_multiarray_umath.may_share_memory) +def may_share_memory(a, b, max_work=None): + """ + may_share_memory(a, b, /, max_work=None) + + Determine if two arrays might share memory + + A return of True does not necessarily mean that the two arrays + share any element. It just means that they *might*. + + Only the memory bounds of a and b are checked by default. + + Parameters + ---------- + a, b : ndarray + Input arrays + max_work : int, optional + Effort to spend on solving the overlap problem. See + `shares_memory` for details. Default for ``may_share_memory`` + is to do a bounds check. + + Returns + ------- + out : bool + + See Also + -------- + shares_memory + + Examples + -------- + >>> np.may_share_memory(np.array([1,2]), np.array([5,8,9])) + False + >>> x = np.zeros([3, 4]) + >>> np.may_share_memory(x[:,0], x[:,1]) + True + + """ + return (a, b) + + +@array_function_from_c_func_and_dispatcher(_multiarray_umath.is_busday) +def is_busday(dates, weekmask=None, holidays=None, busdaycal=None, out=None): + """ + is_busday(dates, weekmask='1111100', holidays=None, busdaycal=None, out=None) + + Calculates which of the given dates are valid days, and which are not. + + .. versionadded:: 1.7.0 + + Parameters + ---------- + dates : array_like of datetime64[D] + The array of dates to process. + weekmask : str or array_like of bool, optional + A seven-element array indicating which of Monday through Sunday are + valid days. May be specified as a length-seven list or array, like + [1,1,1,1,1,0,0]; a length-seven string, like '1111100'; or a string + like "Mon Tue Wed Thu Fri", made up of 3-character abbreviations for + weekdays, optionally separated by white space. Valid abbreviations + are: Mon Tue Wed Thu Fri Sat Sun + holidays : array_like of datetime64[D], optional + An array of dates to consider as invalid dates. They may be + specified in any order, and NaT (not-a-time) dates are ignored. + This list is saved in a normalized form that is suited for + fast calculations of valid days. + busdaycal : busdaycalendar, optional + A `busdaycalendar` object which specifies the valid days. If this + parameter is provided, neither weekmask nor holidays may be + provided. + out : array of bool, optional + If provided, this array is filled with the result. + + Returns + ------- + out : array of bool + An array with the same shape as ``dates``, containing True for + each valid day, and False for each invalid day. + + See Also + -------- + busdaycalendar : An object that specifies a custom set of valid days. + busday_offset : Applies an offset counted in valid days. + busday_count : Counts how many valid days are in a half-open date range. + + Examples + -------- + >>> # The weekdays are Friday, Saturday, and Monday + ... np.is_busday(['2011-07-01', '2011-07-02', '2011-07-18'], + ... holidays=['2011-07-01', '2011-07-04', '2011-07-17']) + array([False, False, True]) + """ + return (dates, weekmask, holidays, out) + + +@array_function_from_c_func_and_dispatcher(_multiarray_umath.busday_offset) +def busday_offset(dates, offsets, roll=None, weekmask=None, holidays=None, + busdaycal=None, out=None): + """ + busday_offset(dates, offsets, roll='raise', weekmask='1111100', holidays=None, busdaycal=None, out=None) + + First adjusts the date to fall on a valid day according to + the ``roll`` rule, then applies offsets to the given dates + counted in valid days. + + .. versionadded:: 1.7.0 + + Parameters + ---------- + dates : array_like of datetime64[D] + The array of dates to process. + offsets : array_like of int + The array of offsets, which is broadcast with ``dates``. + roll : {'raise', 'nat', 'forward', 'following', 'backward', 'preceding', 'modifiedfollowing', 'modifiedpreceding'}, optional + How to treat dates that do not fall on a valid day. The default + is 'raise'. + + * 'raise' means to raise an exception for an invalid day. + * 'nat' means to return a NaT (not-a-time) for an invalid day. + * 'forward' and 'following' mean to take the first valid day + later in time. + * 'backward' and 'preceding' mean to take the first valid day + earlier in time. + * 'modifiedfollowing' means to take the first valid day + later in time unless it is across a Month boundary, in which + case to take the first valid day earlier in time. + * 'modifiedpreceding' means to take the first valid day + earlier in time unless it is across a Month boundary, in which + case to take the first valid day later in time. + weekmask : str or array_like of bool, optional + A seven-element array indicating which of Monday through Sunday are + valid days. May be specified as a length-seven list or array, like + [1,1,1,1,1,0,0]; a length-seven string, like '1111100'; or a string + like "Mon Tue Wed Thu Fri", made up of 3-character abbreviations for + weekdays, optionally separated by white space. Valid abbreviations + are: Mon Tue Wed Thu Fri Sat Sun + holidays : array_like of datetime64[D], optional + An array of dates to consider as invalid dates. They may be + specified in any order, and NaT (not-a-time) dates are ignored. + This list is saved in a normalized form that is suited for + fast calculations of valid days. + busdaycal : busdaycalendar, optional + A `busdaycalendar` object which specifies the valid days. If this + parameter is provided, neither weekmask nor holidays may be + provided. + out : array of datetime64[D], optional + If provided, this array is filled with the result. + + Returns + ------- + out : array of datetime64[D] + An array with a shape from broadcasting ``dates`` and ``offsets`` + together, containing the dates with offsets applied. + + See Also + -------- + busdaycalendar : An object that specifies a custom set of valid days. + is_busday : Returns a boolean array indicating valid days. + busday_count : Counts how many valid days are in a half-open date range. + + Examples + -------- + >>> # First business day in October 2011 (not accounting for holidays) + ... np.busday_offset('2011-10', 0, roll='forward') + numpy.datetime64('2011-10-03') + >>> # Last business day in February 2012 (not accounting for holidays) + ... np.busday_offset('2012-03', -1, roll='forward') + numpy.datetime64('2012-02-29') + >>> # Third Wednesday in January 2011 + ... np.busday_offset('2011-01', 2, roll='forward', weekmask='Wed') + numpy.datetime64('2011-01-19') + >>> # 2012 Mother's Day in Canada and the U.S. + ... np.busday_offset('2012-05', 1, roll='forward', weekmask='Sun') + numpy.datetime64('2012-05-13') + + >>> # First business day on or after a date + ... np.busday_offset('2011-03-20', 0, roll='forward') + numpy.datetime64('2011-03-21') + >>> np.busday_offset('2011-03-22', 0, roll='forward') + numpy.datetime64('2011-03-22') + >>> # First business day after a date + ... np.busday_offset('2011-03-20', 1, roll='backward') + numpy.datetime64('2011-03-21') + >>> np.busday_offset('2011-03-22', 1, roll='backward') + numpy.datetime64('2011-03-23') + """ + return (dates, offsets, weekmask, holidays, out) + + +@array_function_from_c_func_and_dispatcher(_multiarray_umath.busday_count) +def busday_count(begindates, enddates, weekmask=None, holidays=None, + busdaycal=None, out=None): + """ + busday_count(begindates, enddates, weekmask='1111100', holidays=[], busdaycal=None, out=None) + + Counts the number of valid days between `begindates` and + `enddates`, not including the day of `enddates`. + + If ``enddates`` specifies a date value that is earlier than the + corresponding ``begindates`` date value, the count will be negative. + + .. versionadded:: 1.7.0 + + Parameters + ---------- + begindates : array_like of datetime64[D] + The array of the first dates for counting. + enddates : array_like of datetime64[D] + The array of the end dates for counting, which are excluded + from the count themselves. + weekmask : str or array_like of bool, optional + A seven-element array indicating which of Monday through Sunday are + valid days. May be specified as a length-seven list or array, like + [1,1,1,1,1,0,0]; a length-seven string, like '1111100'; or a string + like "Mon Tue Wed Thu Fri", made up of 3-character abbreviations for + weekdays, optionally separated by white space. Valid abbreviations + are: Mon Tue Wed Thu Fri Sat Sun + holidays : array_like of datetime64[D], optional + An array of dates to consider as invalid dates. They may be + specified in any order, and NaT (not-a-time) dates are ignored. + This list is saved in a normalized form that is suited for + fast calculations of valid days. + busdaycal : busdaycalendar, optional + A `busdaycalendar` object which specifies the valid days. If this + parameter is provided, neither weekmask nor holidays may be + provided. + out : array of int, optional + If provided, this array is filled with the result. + + Returns + ------- + out : array of int + An array with a shape from broadcasting ``begindates`` and ``enddates`` + together, containing the number of valid days between + the begin and end dates. + + See Also + -------- + busdaycalendar : An object that specifies a custom set of valid days. + is_busday : Returns a boolean array indicating valid days. + busday_offset : Applies an offset counted in valid days. + + Examples + -------- + >>> # Number of weekdays in January 2011 + ... np.busday_count('2011-01', '2011-02') + 21 + >>> # Number of weekdays in 2011 + >>> np.busday_count('2011', '2012') + 260 + >>> # Number of Saturdays in 2011 + ... np.busday_count('2011', '2012', weekmask='Sat') + 53 + """ + return (begindates, enddates, weekmask, holidays, out) + + +@array_function_from_c_func_and_dispatcher( + _multiarray_umath.datetime_as_string) +def datetime_as_string(arr, unit=None, timezone=None, casting=None): + """ + datetime_as_string(arr, unit=None, timezone='naive', casting='same_kind') + + Convert an array of datetimes into an array of strings. + + Parameters + ---------- + arr : array_like of datetime64 + The array of UTC timestamps to format. + unit : str + One of None, 'auto', or a :ref:`datetime unit <arrays.dtypes.dateunits>`. + timezone : {'naive', 'UTC', 'local'} or tzinfo + Timezone information to use when displaying the datetime. If 'UTC', end + with a Z to indicate UTC time. If 'local', convert to the local timezone + first, and suffix with a +-#### timezone offset. If a tzinfo object, + then do as with 'local', but use the specified timezone. + casting : {'no', 'equiv', 'safe', 'same_kind', 'unsafe'} + Casting to allow when changing between datetime units. + + Returns + ------- + str_arr : ndarray + An array of strings the same shape as `arr`. + + Examples + -------- + >>> import pytz + >>> d = np.arange('2002-10-27T04:30', 4*60, 60, dtype='M8[m]') + >>> d + array(['2002-10-27T04:30', '2002-10-27T05:30', '2002-10-27T06:30', + '2002-10-27T07:30'], dtype='datetime64[m]') + + Setting the timezone to UTC shows the same information, but with a Z suffix + + >>> np.datetime_as_string(d, timezone='UTC') + array(['2002-10-27T04:30Z', '2002-10-27T05:30Z', '2002-10-27T06:30Z', + '2002-10-27T07:30Z'], dtype='<U35') + + Note that we picked datetimes that cross a DST boundary. Passing in a + ``pytz`` timezone object will print the appropriate offset + + >>> np.datetime_as_string(d, timezone=pytz.timezone('US/Eastern')) + array(['2002-10-27T00:30-0400', '2002-10-27T01:30-0400', + '2002-10-27T01:30-0500', '2002-10-27T02:30-0500'], dtype='<U39') + + Passing in a unit will change the precision + + >>> np.datetime_as_string(d, unit='h') + array(['2002-10-27T04', '2002-10-27T05', '2002-10-27T06', '2002-10-27T07'], + dtype='<U32') + >>> np.datetime_as_string(d, unit='s') + array(['2002-10-27T04:30:00', '2002-10-27T05:30:00', '2002-10-27T06:30:00', + '2002-10-27T07:30:00'], dtype='<U38') + + 'casting' can be used to specify whether precision can be changed + + >>> np.datetime_as_string(d, unit='h', casting='safe') + Traceback (most recent call last): + ... + TypeError: Cannot create a datetime string as units 'h' from a NumPy + datetime with units 'm' according to the rule 'safe' + """ + return (arr,) diff --git a/numpy/core/multiarray.pyi b/numpy/core/multiarray.pyi new file mode 100644 index 000000000000..a9f68e1815cf --- /dev/null +++ b/numpy/core/multiarray.pyi @@ -0,0 +1,1027 @@ +# TODO: Sort out any and all missing functions in this namespace + +import os +import datetime as dt +from typing import ( + Literal as L, + Any, + Callable, + Iterable, + Optional, + overload, + TypeVar, + List, + Type, + Union, + Sequence, + Tuple, + SupportsIndex, + final, + Final, + Protocol, +) + +from numpy import ( + # Re-exports + busdaycalendar as busdaycalendar, + broadcast as broadcast, + dtype as dtype, + ndarray as ndarray, + nditer as nditer, + + # The rest + ufunc, + str_, + bool_, + uint8, + intp, + int_, + float64, + timedelta64, + datetime64, + generic, + unsignedinteger, + signedinteger, + floating, + complexfloating, + _OrderKACF, + _OrderCF, + _CastingKind, + _ModeKind, + _SupportsBuffer, + _IOProtocol, + _CopyMode, + _NDIterFlagsKind, + _NDIterOpFlagsKind, +) + +from numpy.typing import ( + # Shapes + _ShapeLike, + + # DTypes + DTypeLike, + _SupportsDType, + + # Arrays + NDArray, + ArrayLike, + _SupportsArray, + _FiniteNestedSequence, + _ArrayLikeBool_co, + _ArrayLikeUInt_co, + _ArrayLikeInt_co, + _ArrayLikeFloat_co, + _ArrayLikeComplex_co, + _ArrayLikeTD64_co, + _ArrayLikeDT64_co, + _ArrayLikeObject_co, + _ArrayLikeStr_co, + _ArrayLikeBytes_co, + _ScalarLike_co, + _IntLike_co, + _FloatLike_co, + _TD64Like_co, +) + +_SCT = TypeVar("_SCT", bound=generic) +_ArrayType = TypeVar("_ArrayType", bound=NDArray[Any]) + +# Subscriptable subsets of `npt.DTypeLike` and `npt.ArrayLike` +_DTypeLike = Union[ + dtype[_SCT], + Type[_SCT], + _SupportsDType[dtype[_SCT]], +] +_ArrayLike = _FiniteNestedSequence[_SupportsArray[dtype[_SCT]]] + +# Valid time units +_UnitKind = L[ + "Y", + "M", + "D", + "h", + "m", + "s", + "ms", + "us", "μs", + "ns", + "ps", + "fs", + "as", +] +_RollKind = L[ # `raise` is deliberately excluded + "nat", + "forward", + "following", + "backward", + "preceding", + "modifiedfollowing", + "modifiedpreceding", +] + +__all__: List[str] + +ALLOW_THREADS: Final[int] # 0 or 1 (system-specific) +BUFSIZE: L[8192] +CLIP: L[0] +WRAP: L[1] +RAISE: L[2] +MAXDIMS: L[32] +MAY_SHARE_BOUNDS: L[0] +MAY_SHARE_EXACT: L[-1] +tracemalloc_domain: L[389047] + +@overload +def empty_like( + prototype: _ArrayType, + dtype: None = ..., + order: _OrderKACF = ..., + subok: bool = ..., + shape: Optional[_ShapeLike] = ..., +) -> _ArrayType: ... +@overload +def empty_like( + prototype: _ArrayLike[_SCT], + dtype: None = ..., + order: _OrderKACF = ..., + subok: bool = ..., + shape: Optional[_ShapeLike] = ..., +) -> NDArray[_SCT]: ... +@overload +def empty_like( + prototype: object, + dtype: None = ..., + order: _OrderKACF = ..., + subok: bool = ..., + shape: Optional[_ShapeLike] = ..., +) -> NDArray[Any]: ... +@overload +def empty_like( + prototype: Any, + dtype: _DTypeLike[_SCT], + order: _OrderKACF = ..., + subok: bool = ..., + shape: Optional[_ShapeLike] = ..., +) -> NDArray[_SCT]: ... +@overload +def empty_like( + prototype: Any, + dtype: DTypeLike, + order: _OrderKACF = ..., + subok: bool = ..., + shape: Optional[_ShapeLike] = ..., +) -> NDArray[Any]: ... + +@overload +def array( + object: _ArrayType, + dtype: None = ..., + *, + copy: bool | _CopyMode = ..., + order: _OrderKACF = ..., + subok: L[True], + ndmin: int = ..., + like: ArrayLike = ..., +) -> _ArrayType: ... +@overload +def array( + object: _ArrayLike[_SCT], + dtype: None = ..., + *, + copy: bool | _CopyMode = ..., + order: _OrderKACF = ..., + subok: bool = ..., + ndmin: int = ..., + like: ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def array( + object: object, + dtype: None = ..., + *, + copy: bool | _CopyMode = ..., + order: _OrderKACF = ..., + subok: bool = ..., + ndmin: int = ..., + like: ArrayLike = ..., +) -> NDArray[Any]: ... +@overload +def array( + object: Any, + dtype: _DTypeLike[_SCT], + *, + copy: bool | _CopyMode = ..., + order: _OrderKACF = ..., + subok: bool = ..., + ndmin: int = ..., + like: ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def array( + object: Any, + dtype: DTypeLike, + *, + copy: bool | _CopyMode = ..., + order: _OrderKACF = ..., + subok: bool = ..., + ndmin: int = ..., + like: ArrayLike = ..., +) -> NDArray[Any]: ... + +@overload +def zeros( + shape: _ShapeLike, + dtype: None = ..., + order: _OrderCF = ..., + *, + like: ArrayLike = ..., +) -> NDArray[float64]: ... +@overload +def zeros( + shape: _ShapeLike, + dtype: _DTypeLike[_SCT], + order: _OrderCF = ..., + *, + like: ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def zeros( + shape: _ShapeLike, + dtype: DTypeLike, + order: _OrderCF = ..., + *, + like: ArrayLike = ..., +) -> NDArray[Any]: ... + +@overload +def empty( + shape: _ShapeLike, + dtype: None = ..., + order: _OrderCF = ..., + *, + like: ArrayLike = ..., +) -> NDArray[float64]: ... +@overload +def empty( + shape: _ShapeLike, + dtype: _DTypeLike[_SCT], + order: _OrderCF = ..., + *, + like: ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def empty( + shape: _ShapeLike, + dtype: DTypeLike, + order: _OrderCF = ..., + *, + like: ArrayLike = ..., +) -> NDArray[Any]: ... + +@overload +def unravel_index( # type: ignore[misc] + indices: _IntLike_co, + shape: _ShapeLike, + order: _OrderCF = ..., +) -> Tuple[intp, ...]: ... +@overload +def unravel_index( + indices: _ArrayLikeInt_co, + shape: _ShapeLike, + order: _OrderCF = ..., +) -> Tuple[NDArray[intp], ...]: ... + +@overload +def ravel_multi_index( # type: ignore[misc] + multi_index: Sequence[_IntLike_co], + dims: Sequence[SupportsIndex], + mode: Union[_ModeKind, Tuple[_ModeKind, ...]] = ..., + order: _OrderCF = ..., +) -> intp: ... +@overload +def ravel_multi_index( + multi_index: Sequence[_ArrayLikeInt_co], + dims: Sequence[SupportsIndex], + mode: Union[_ModeKind, Tuple[_ModeKind, ...]] = ..., + order: _OrderCF = ..., +) -> NDArray[intp]: ... + +@overload +def concatenate( # type: ignore[misc] + arrays: _ArrayLike[_SCT], + /, + axis: Optional[SupportsIndex] = ..., + out: None = ..., + *, + dtype: None = ..., + casting: Optional[_CastingKind] = ... +) -> NDArray[_SCT]: ... +@overload +def concatenate( # type: ignore[misc] + arrays: ArrayLike, + /, + axis: Optional[SupportsIndex] = ..., + out: None = ..., + *, + dtype: None = ..., + casting: Optional[_CastingKind] = ... +) -> NDArray[Any]: ... +@overload +def concatenate( # type: ignore[misc] + arrays: ArrayLike, + /, + axis: Optional[SupportsIndex] = ..., + out: None = ..., + *, + dtype: _DTypeLike[_SCT], + casting: Optional[_CastingKind] = ... +) -> NDArray[_SCT]: ... +@overload +def concatenate( # type: ignore[misc] + arrays: ArrayLike, + /, + axis: Optional[SupportsIndex] = ..., + out: None = ..., + *, + dtype: DTypeLike, + casting: Optional[_CastingKind] = ... +) -> NDArray[Any]: ... +@overload +def concatenate( + arrays: ArrayLike, + /, + axis: Optional[SupportsIndex] = ..., + out: _ArrayType = ..., + *, + dtype: DTypeLike = ..., + casting: Optional[_CastingKind] = ... +) -> _ArrayType: ... + +def inner( + a: ArrayLike, + b: ArrayLike, + /, +) -> Any: ... + +@overload +def where( + condition: ArrayLike, + /, +) -> Tuple[NDArray[intp], ...]: ... +@overload +def where( + condition: ArrayLike, + x: ArrayLike, + y: ArrayLike, + /, +) -> NDArray[Any]: ... + +def lexsort( + keys: ArrayLike, + axis: Optional[SupportsIndex] = ..., +) -> Any: ... + +def can_cast( + from_: Union[ArrayLike, DTypeLike], + to: DTypeLike, + casting: Optional[_CastingKind] = ..., +) -> bool: ... + +def min_scalar_type( + a: ArrayLike, /, +) -> dtype[Any]: ... + +def result_type( + *arrays_and_dtypes: Union[ArrayLike, DTypeLike], +) -> dtype[Any]: ... + +@overload +def dot(a: ArrayLike, b: ArrayLike, out: None = ...) -> Any: ... +@overload +def dot(a: ArrayLike, b: ArrayLike, out: _ArrayType) -> _ArrayType: ... + +@overload +def vdot(a: _ArrayLikeBool_co, b: _ArrayLikeBool_co, /) -> bool_: ... # type: ignore[misc] +@overload +def vdot(a: _ArrayLikeUInt_co, b: _ArrayLikeUInt_co, /) -> unsignedinteger[Any]: ... # type: ignore[misc] +@overload +def vdot(a: _ArrayLikeInt_co, b: _ArrayLikeInt_co, /) -> signedinteger[Any]: ... # type: ignore[misc] +@overload +def vdot(a: _ArrayLikeFloat_co, b: _ArrayLikeFloat_co, /) -> floating[Any]: ... # type: ignore[misc] +@overload +def vdot(a: _ArrayLikeComplex_co, b: _ArrayLikeComplex_co, /) -> complexfloating[Any, Any]: ... # type: ignore[misc] +@overload +def vdot(a: _ArrayLikeTD64_co, b: _ArrayLikeTD64_co, /) -> timedelta64: ... +@overload +def vdot(a: _ArrayLikeObject_co, b: Any, /) -> Any: ... +@overload +def vdot(a: Any, b: _ArrayLikeObject_co, /) -> Any: ... + +def bincount( + x: ArrayLike, + /, + weights: Optional[ArrayLike] = ..., + minlength: SupportsIndex = ..., +) -> NDArray[intp]: ... + +def copyto( + dst: NDArray[Any], + src: ArrayLike, + casting: Optional[_CastingKind] = ..., + where: Optional[_ArrayLikeBool_co] = ..., +) -> None: ... + +def putmask( + a: NDArray[Any], + mask: _ArrayLikeBool_co, + values: ArrayLike, +) -> None: ... + +def packbits( + a: _ArrayLikeInt_co, + /, + axis: Optional[SupportsIndex] = ..., + bitorder: L["big", "little"] = ..., +) -> NDArray[uint8]: ... + +def unpackbits( + a: _ArrayLike[uint8], + /, + axis: Optional[SupportsIndex] = ..., + count: Optional[SupportsIndex] = ..., + bitorder: L["big", "little"] = ..., +) -> NDArray[uint8]: ... + +def shares_memory( + a: object, + b: object, + /, + max_work: Optional[int] = ..., +) -> bool: ... + +def may_share_memory( + a: object, + b: object, + /, + max_work: Optional[int] = ..., +) -> bool: ... + +@overload +def asarray( + a: _ArrayLike[_SCT], + dtype: None = ..., + order: _OrderKACF = ..., + *, + like: ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def asarray( + a: object, + dtype: None = ..., + order: _OrderKACF = ..., + *, + like: ArrayLike = ..., +) -> NDArray[Any]: ... +@overload +def asarray( + a: Any, + dtype: _DTypeLike[_SCT], + order: _OrderKACF = ..., + *, + like: ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def asarray( + a: Any, + dtype: DTypeLike, + order: _OrderKACF = ..., + *, + like: ArrayLike = ..., +) -> NDArray[Any]: ... + +@overload +def asanyarray( + a: _ArrayType, # Preserve subclass-information + dtype: None = ..., + order: _OrderKACF = ..., + *, + like: ArrayLike = ..., +) -> _ArrayType: ... +@overload +def asanyarray( + a: _ArrayLike[_SCT], + dtype: None = ..., + order: _OrderKACF = ..., + *, + like: ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def asanyarray( + a: object, + dtype: None = ..., + order: _OrderKACF = ..., + *, + like: ArrayLike = ..., +) -> NDArray[Any]: ... +@overload +def asanyarray( + a: Any, + dtype: _DTypeLike[_SCT], + order: _OrderKACF = ..., + *, + like: ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def asanyarray( + a: Any, + dtype: DTypeLike, + order: _OrderKACF = ..., + *, + like: ArrayLike = ..., +) -> NDArray[Any]: ... + +@overload +def ascontiguousarray( + a: _ArrayLike[_SCT], + dtype: None = ..., + *, + like: ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def ascontiguousarray( + a: object, + dtype: None = ..., + *, + like: ArrayLike = ..., +) -> NDArray[Any]: ... +@overload +def ascontiguousarray( + a: Any, + dtype: _DTypeLike[_SCT], + *, + like: ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def ascontiguousarray( + a: Any, + dtype: DTypeLike, + *, + like: ArrayLike = ..., +) -> NDArray[Any]: ... + +@overload +def asfortranarray( + a: _ArrayLike[_SCT], + dtype: None = ..., + *, + like: ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def asfortranarray( + a: object, + dtype: None = ..., + *, + like: ArrayLike = ..., +) -> NDArray[Any]: ... +@overload +def asfortranarray( + a: Any, + dtype: _DTypeLike[_SCT], + *, + like: ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def asfortranarray( + a: Any, + dtype: DTypeLike, + *, + like: ArrayLike = ..., +) -> NDArray[Any]: ... + +# In practice `List[Any]` is list with an int, int and a valid +# `np.seterrcall()` object +def geterrobj() -> List[Any]: ... +def seterrobj(errobj: List[Any], /) -> None: ... + +def promote_types(__type1: DTypeLike, __type2: DTypeLike) -> dtype[Any]: ... + +# `sep` is a de facto mandatory argument, as its default value is deprecated +@overload +def fromstring( + string: str | bytes, + dtype: None = ..., + count: SupportsIndex = ..., + *, + sep: str, + like: ArrayLike = ..., +) -> NDArray[float64]: ... +@overload +def fromstring( + string: str | bytes, + dtype: _DTypeLike[_SCT], + count: SupportsIndex = ..., + *, + sep: str, + like: ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def fromstring( + string: str | bytes, + dtype: DTypeLike, + count: SupportsIndex = ..., + *, + sep: str, + like: ArrayLike = ..., +) -> NDArray[Any]: ... + +def frompyfunc( + func: Callable[..., Any], /, + nin: SupportsIndex, + nout: SupportsIndex, + *, + identity: Any = ..., +) -> ufunc: ... + +@overload +def fromfile( + file: str | bytes | os.PathLike[Any] | _IOProtocol, + dtype: None = ..., + count: SupportsIndex = ..., + sep: str = ..., + offset: SupportsIndex = ..., + *, + like: ArrayLike = ..., +) -> NDArray[float64]: ... +@overload +def fromfile( + file: str | bytes | os.PathLike[Any] | _IOProtocol, + dtype: _DTypeLike[_SCT], + count: SupportsIndex = ..., + sep: str = ..., + offset: SupportsIndex = ..., + *, + like: ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def fromfile( + file: str | bytes | os.PathLike[Any] | _IOProtocol, + dtype: DTypeLike, + count: SupportsIndex = ..., + sep: str = ..., + offset: SupportsIndex = ..., + *, + like: ArrayLike = ..., +) -> NDArray[Any]: ... + +@overload +def fromiter( + iter: Iterable[Any], + dtype: _DTypeLike[_SCT], + count: SupportsIndex = ..., + *, + like: ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def fromiter( + iter: Iterable[Any], + dtype: DTypeLike, + count: SupportsIndex = ..., + *, + like: ArrayLike = ..., +) -> NDArray[Any]: ... + +@overload +def frombuffer( + buffer: _SupportsBuffer, + dtype: None = ..., + count: SupportsIndex = ..., + offset: SupportsIndex = ..., + *, + like: ArrayLike = ..., +) -> NDArray[float64]: ... +@overload +def frombuffer( + buffer: _SupportsBuffer, + dtype: _DTypeLike[_SCT], + count: SupportsIndex = ..., + offset: SupportsIndex = ..., + *, + like: ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def frombuffer( + buffer: _SupportsBuffer, + dtype: DTypeLike, + count: SupportsIndex = ..., + offset: SupportsIndex = ..., + *, + like: ArrayLike = ..., +) -> NDArray[Any]: ... + +@overload +def arange( # type: ignore[misc] + stop: _IntLike_co, + /, *, + dtype: None = ..., + like: ArrayLike = ..., +) -> NDArray[signedinteger[Any]]: ... +@overload +def arange( # type: ignore[misc] + start: _IntLike_co, + stop: _IntLike_co, + step: _IntLike_co = ..., + dtype: None = ..., + *, + like: ArrayLike = ..., +) -> NDArray[signedinteger[Any]]: ... +@overload +def arange( # type: ignore[misc] + stop: _FloatLike_co, + /, *, + dtype: None = ..., + like: ArrayLike = ..., +) -> NDArray[floating[Any]]: ... +@overload +def arange( # type: ignore[misc] + start: _FloatLike_co, + stop: _FloatLike_co, + step: _FloatLike_co = ..., + dtype: None = ..., + *, + like: ArrayLike = ..., +) -> NDArray[floating[Any]]: ... +@overload +def arange( + stop: _TD64Like_co, + /, *, + dtype: None = ..., + like: ArrayLike = ..., +) -> NDArray[timedelta64]: ... +@overload +def arange( + start: _TD64Like_co, + stop: _TD64Like_co, + step: _TD64Like_co = ..., + dtype: None = ..., + *, + like: ArrayLike = ..., +) -> NDArray[timedelta64]: ... +@overload +def arange( # both start and stop must always be specified for datetime64 + start: datetime64, + stop: datetime64, + step: datetime64 = ..., + dtype: None = ..., + *, + like: ArrayLike = ..., +) -> NDArray[datetime64]: ... +@overload +def arange( + stop: Any, + /, *, + dtype: _DTypeLike[_SCT], + like: ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def arange( + start: Any, + stop: Any, + step: Any = ..., + dtype: _DTypeLike[_SCT] = ..., + *, + like: ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def arange( + stop: Any, /, + *, + dtype: DTypeLike, + like: ArrayLike = ..., +) -> NDArray[Any]: ... +@overload +def arange( + start: Any, + stop: Any, + step: Any = ..., + dtype: DTypeLike = ..., + *, + like: ArrayLike = ..., +) -> NDArray[Any]: ... + +def datetime_data( + dtype: str | _DTypeLike[datetime64] | _DTypeLike[timedelta64], /, +) -> Tuple[str, int]: ... + +# The datetime functions perform unsafe casts to `datetime64[D]`, +# so a lot of different argument types are allowed here + +@overload +def busday_count( # type: ignore[misc] + begindates: _ScalarLike_co, + enddates: _ScalarLike_co, + weekmask: ArrayLike = ..., + holidays: None | ArrayLike = ..., + busdaycal: None | busdaycalendar = ..., + out: None = ..., +) -> int_: ... +@overload +def busday_count( # type: ignore[misc] + begindates: ArrayLike, + enddates: ArrayLike, + weekmask: ArrayLike = ..., + holidays: None | ArrayLike = ..., + busdaycal: None | busdaycalendar = ..., + out: None = ..., +) -> NDArray[int_]: ... +@overload +def busday_count( + begindates: ArrayLike, + enddates: ArrayLike, + weekmask: ArrayLike = ..., + holidays: None | ArrayLike = ..., + busdaycal: None | busdaycalendar = ..., + out: _ArrayType = ..., +) -> _ArrayType: ... + +# `roll="raise"` is (more or less?) equivalent to `casting="safe"` +@overload +def busday_offset( # type: ignore[misc] + dates: datetime64, + offsets: _TD64Like_co, + roll: L["raise"] = ..., + weekmask: ArrayLike = ..., + holidays: None | ArrayLike = ..., + busdaycal: None | busdaycalendar = ..., + out: None = ..., +) -> datetime64: ... +@overload +def busday_offset( # type: ignore[misc] + dates: _ArrayLike[datetime64], + offsets: _ArrayLikeTD64_co, + roll: L["raise"] = ..., + weekmask: ArrayLike = ..., + holidays: None | ArrayLike = ..., + busdaycal: None | busdaycalendar = ..., + out: None = ..., +) -> NDArray[datetime64]: ... +@overload +def busday_offset( # type: ignore[misc] + dates: _ArrayLike[datetime64], + offsets: _ArrayLike[timedelta64], + roll: L["raise"] = ..., + weekmask: ArrayLike = ..., + holidays: None | ArrayLike = ..., + busdaycal: None | busdaycalendar = ..., + out: _ArrayType = ..., +) -> _ArrayType: ... +@overload +def busday_offset( # type: ignore[misc] + dates: _ScalarLike_co, + offsets: _ScalarLike_co, + roll: _RollKind, + weekmask: ArrayLike = ..., + holidays: None | ArrayLike = ..., + busdaycal: None | busdaycalendar = ..., + out: None = ..., +) -> datetime64: ... +@overload +def busday_offset( # type: ignore[misc] + dates: ArrayLike, + offsets: ArrayLike, + roll: _RollKind, + weekmask: ArrayLike = ..., + holidays: None | ArrayLike = ..., + busdaycal: None | busdaycalendar = ..., + out: None = ..., +) -> NDArray[datetime64]: ... +@overload +def busday_offset( + dates: ArrayLike, + offsets: ArrayLike, + roll: _RollKind, + weekmask: ArrayLike = ..., + holidays: None | ArrayLike = ..., + busdaycal: None | busdaycalendar = ..., + out: _ArrayType = ..., +) -> _ArrayType: ... + +@overload +def is_busday( # type: ignore[misc] + dates: _ScalarLike_co, + weekmask: ArrayLike = ..., + holidays: None | ArrayLike = ..., + busdaycal: None | busdaycalendar = ..., + out: None = ..., +) -> bool_: ... +@overload +def is_busday( # type: ignore[misc] + dates: ArrayLike, + weekmask: ArrayLike = ..., + holidays: None | ArrayLike = ..., + busdaycal: None | busdaycalendar = ..., + out: None = ..., +) -> NDArray[bool_]: ... +@overload +def is_busday( + dates: ArrayLike, + weekmask: ArrayLike = ..., + holidays: None | ArrayLike = ..., + busdaycal: None | busdaycalendar = ..., + out: _ArrayType = ..., +) -> _ArrayType: ... + +@overload +def datetime_as_string( # type: ignore[misc] + arr: datetime64, + unit: None | L["auto"] | _UnitKind = ..., + timezone: L["naive", "UTC", "local"] | dt.tzinfo = ..., + casting: _CastingKind = ..., +) -> str_: ... +@overload +def datetime_as_string( + arr: _ArrayLikeDT64_co, + unit: None | L["auto"] | _UnitKind = ..., + timezone: L["naive", "UTC", "local"] | dt.tzinfo = ..., + casting: _CastingKind = ..., +) -> NDArray[str_]: ... + +@overload +def compare_chararrays( + a1: _ArrayLikeStr_co, + a2: _ArrayLikeStr_co, + cmp: L["<", "<=", "==", ">=", ">", "!="], + rstrip: bool, +) -> NDArray[bool_]: ... +@overload +def compare_chararrays( + a1: _ArrayLikeBytes_co, + a2: _ArrayLikeBytes_co, + cmp: L["<", "<=", "==", ">=", ">", "!="], + rstrip: bool, +) -> NDArray[bool_]: ... + +def add_docstring(obj: Callable[..., Any], docstring: str, /) -> None: ... + +_GetItemKeys = L[ + "C", "CONTIGUOUS", "C_CONTIGUOUS", + "F", "FORTRAN", "F_CONTIGUOUS", + "W", "WRITEABLE", + "B", "BEHAVED", + "O", "OWNDATA", + "A", "ALIGNED", + "X", "WRITEBACKIFCOPY", + "CA", "CARRAY", + "FA", "FARRAY", + "FNC", + "FORC", +] +_SetItemKeys = L[ + "A", "ALIGNED", + "W", "WRITABLE", + "X", "WRITEBACKIFCOPY", +] + +@final +class flagsobj: + __hash__: None # type: ignore[assignment] + aligned: bool + # NOTE: deprecated + # updateifcopy: bool + writeable: bool + writebackifcopy: bool + @property + def behaved(self) -> bool: ... + @property + def c_contiguous(self) -> bool: ... + @property + def carray(self) -> bool: ... + @property + def contiguous(self) -> bool: ... + @property + def f_contiguous(self) -> bool: ... + @property + def farray(self) -> bool: ... + @property + def fnc(self) -> bool: ... + @property + def forc(self) -> bool: ... + @property + def fortran(self) -> bool: ... + @property + def num(self) -> int: ... + @property + def owndata(self) -> bool: ... + def __getitem__(self, key: _GetItemKeys) -> bool: ... + def __setitem__(self, key: _SetItemKeys, value: bool) -> None: ... + +def nested_iters( + op: ArrayLike | Sequence[ArrayLike], + axes: Sequence[Sequence[SupportsIndex]], + flags: None | Sequence[_NDIterFlagsKind] = ..., + op_flags: None | Sequence[Sequence[_NDIterOpFlagsKind]] = ..., + op_dtypes: DTypeLike | Sequence[DTypeLike] = ..., + order: _OrderKACF = ..., + casting: _CastingKind = ..., + buffersize: SupportsIndex = ..., +) -> Tuple[nditer, ...]: ... diff --git a/numpy/core/numeric.py b/numpy/core/numeric.py index 7ade3d22451a..344d40d934cf 100644 --- a/numpy/core/numeric.py +++ b/numpy/core/numeric.py @@ -1,11 +1,4 @@ -from __future__ import division, absolute_import, print_function - -try: - # Accessing collections abstract classes from collections - # has been deprecated since Python 3.3 - import collections.abc as collections_abc -except ImportError: - import collections as collections_abc +import functools import itertools import operator import sys @@ -17,69 +10,54 @@ from .multiarray import ( _fastCopyAndTranspose as fastCopyAndTranspose, ALLOW_THREADS, BUFSIZE, CLIP, MAXDIMS, MAY_SHARE_BOUNDS, MAY_SHARE_EXACT, RAISE, - WRAP, arange, array, broadcast, can_cast, compare_chararrays, - concatenate, copyto, count_nonzero, dot, dtype, empty, - empty_like, flatiter, frombuffer, fromfile, fromiter, fromstring, - inner, int_asbuffer, lexsort, matmul, may_share_memory, + WRAP, arange, array, asarray, asanyarray, ascontiguousarray, + asfortranarray, broadcast, can_cast, compare_chararrays, + concatenate, copyto, dot, dtype, empty, + empty_like, flatiter, frombuffer, _from_dlpack, fromfile, fromiter, + fromstring, inner, lexsort, matmul, may_share_memory, min_scalar_type, ndarray, nditer, nested_iters, promote_types, putmask, result_type, set_numeric_ops, shares_memory, vdot, where, zeros, normalize_axis_index) -if sys.version_info[0] < 3: - from .multiarray import newbuffer, getbuffer +from . import overrides from . import umath -from .umath import (multiply, invert, sin, UFUNC_BUFSIZE_DEFAULT, - ERR_IGNORE, ERR_WARN, ERR_RAISE, ERR_CALL, ERR_PRINT, - ERR_LOG, ERR_DEFAULT, PINF, NAN) +from . import shape_base +from .overrides import set_array_function_like_doc, set_module +from .umath import (multiply, invert, sin, PINF, NAN) from . import numerictypes from .numerictypes import longlong, intc, int_, float_, complex_, bool_ -from ._internal import TooHardError, AxisError +from ._exceptions import TooHardError, AxisError +from ._ufunc_config import errstate bitwise_not = invert ufunc = type(sin) newaxis = None -if sys.version_info[0] >= 3: - import pickle - basestring = str - import builtins -else: - import cPickle as pickle - import __builtin__ as builtins - - -def loads(*args, **kwargs): - # NumPy 1.15.0, 2017-12-10 - warnings.warn( - "np.core.numeric.loads is deprecated, use pickle.loads instead", - DeprecationWarning, stacklevel=2) - return pickle.loads(*args, **kwargs) +array_function_dispatch = functools.partial( + overrides.array_function_dispatch, module='numpy') __all__ = [ 'newaxis', 'ndarray', 'flatiter', 'nditer', 'nested_iters', 'ufunc', - 'arange', 'array', 'zeros', 'count_nonzero', 'empty', 'broadcast', 'dtype', - 'fromstring', 'fromfile', 'frombuffer', 'int_asbuffer', 'where', + 'arange', 'array', 'asarray', 'asanyarray', 'ascontiguousarray', + 'asfortranarray', 'zeros', 'count_nonzero', 'empty', 'broadcast', 'dtype', + 'fromstring', 'fromfile', 'frombuffer', '_from_dlpack', 'where', 'argwhere', 'copyto', 'concatenate', 'fastCopyAndTranspose', 'lexsort', 'set_numeric_ops', 'can_cast', 'promote_types', 'min_scalar_type', - 'result_type', 'asarray', 'asanyarray', 'ascontiguousarray', - 'asfortranarray', 'isfortran', 'empty_like', 'zeros_like', 'ones_like', + 'result_type', 'isfortran', 'empty_like', 'zeros_like', 'ones_like', 'correlate', 'convolve', 'inner', 'dot', 'outer', 'vdot', 'roll', - 'rollaxis', 'moveaxis', 'cross', 'tensordot', 'little_endian', 'require', + 'rollaxis', 'moveaxis', 'cross', 'tensordot', 'little_endian', 'fromiter', 'array_equal', 'array_equiv', 'indices', 'fromfunction', - 'isclose', 'load', 'loads', 'isscalar', 'binary_repr', 'base_repr', 'ones', - 'identity', 'allclose', 'compare_chararrays', 'putmask', 'seterr', - 'geterr', 'setbufsize', 'getbufsize', 'seterrcall', 'geterrcall', - 'errstate', 'flatnonzero', 'Inf', 'inf', 'infty', 'Infinity', 'nan', 'NaN', + 'isclose', 'isscalar', 'binary_repr', 'base_repr', 'ones', + 'identity', 'allclose', 'compare_chararrays', 'putmask', + 'flatnonzero', 'Inf', 'inf', 'infty', 'Infinity', 'nan', 'NaN', 'False_', 'True_', 'bitwise_not', 'CLIP', 'RAISE', 'WRAP', 'MAXDIMS', 'BUFSIZE', 'ALLOW_THREADS', 'ComplexWarning', 'full', 'full_like', 'matmul', 'shares_memory', 'may_share_memory', 'MAY_SHARE_BOUNDS', 'MAY_SHARE_EXACT', 'TooHardError', 'AxisError'] -if sys.version_info[0] < 3: - __all__.extend(['getbuffer', 'newbuffer']) - +@set_module('numpy') class ComplexWarning(RuntimeWarning): """ The warning raised when casting a complex dtype to a real dtype. @@ -91,7 +69,12 @@ class ComplexWarning(RuntimeWarning): pass -def zeros_like(a, dtype=None, order='K', subok=True): +def _zeros_like_dispatcher(a, dtype=None, order=None, subok=None, shape=None): + return (a,) + + +@array_function_dispatch(_zeros_like_dispatcher) +def zeros_like(a, dtype=None, order='K', subok=True, shape=None): """ Return an array of zeros with the same shape and type as a given array. @@ -113,8 +96,14 @@ def zeros_like(a, dtype=None, order='K', subok=True): .. versionadded:: 1.6.0 subok : bool, optional. If True, then the newly created array will use the sub-class - type of 'a', otherwise it will be a base-class array. Defaults + type of `a`, otherwise it will be a base-class array. Defaults to True. + shape : int or sequence of ints, optional. + Overrides the shape of the result. If order='K' and the number of + dimensions is unchanged, will try to keep order, otherwise, + order='C' is implied. + + .. versionadded:: 1.17.0 Returns ------- @@ -141,19 +130,25 @@ def zeros_like(a, dtype=None, order='K', subok=True): >>> y = np.arange(3, dtype=float) >>> y - array([ 0., 1., 2.]) + array([0., 1., 2.]) >>> np.zeros_like(y) - array([ 0., 0., 0.]) + array([0., 0., 0.]) """ - res = empty_like(a, dtype=dtype, order=order, subok=subok) + res = empty_like(a, dtype=dtype, order=order, subok=subok, shape=shape) # needed instead of a 0 to get same result as zeros for for string dtypes z = zeros(1, dtype=res.dtype) multiarray.copyto(res, z, casting='unsafe') return res -def ones(shape, dtype=None, order='C'): +def _ones_dispatcher(shape, dtype=None, order=None, *, like=None): + return(like,) + + +@set_array_function_like_doc +@set_module('numpy') +def ones(shape, dtype=None, order='C', *, like=None): """ Return a new array of given shape and type, filled with ones. @@ -168,6 +163,9 @@ def ones(shape, dtype=None, order='C'): Whether to store multi-dimensional data in row-major (C-style) or column-major (Fortran-style) order in memory. + ${ARRAY_FUNCTION_LIKE} + + .. versionadded:: 1.20.0 Returns ------- @@ -185,27 +183,40 @@ def ones(shape, dtype=None, order='C'): Examples -------- >>> np.ones(5) - array([ 1., 1., 1., 1., 1.]) + array([1., 1., 1., 1., 1.]) >>> np.ones((5,), dtype=int) array([1, 1, 1, 1, 1]) >>> np.ones((2, 1)) - array([[ 1.], - [ 1.]]) + array([[1.], + [1.]]) >>> s = (2,2) >>> np.ones(s) - array([[ 1., 1.], - [ 1., 1.]]) + array([[1., 1.], + [1., 1.]]) """ + if like is not None: + return _ones_with_like(shape, dtype=dtype, order=order, like=like) + a = empty(shape, dtype, order) multiarray.copyto(a, 1, casting='unsafe') return a -def ones_like(a, dtype=None, order='K', subok=True): +_ones_with_like = array_function_dispatch( + _ones_dispatcher +)(ones) + + +def _ones_like_dispatcher(a, dtype=None, order=None, subok=None, shape=None): + return (a,) + + +@array_function_dispatch(_ones_like_dispatcher) +def ones_like(a, dtype=None, order='K', subok=True, shape=None): """ Return an array of ones with the same shape and type as a given array. @@ -227,8 +238,14 @@ def ones_like(a, dtype=None, order='K', subok=True): .. versionadded:: 1.6.0 subok : bool, optional. If True, then the newly created array will use the sub-class - type of 'a', otherwise it will be a base-class array. Defaults + type of `a`, otherwise it will be a base-class array. Defaults to True. + shape : int or sequence of ints, optional. + Overrides the shape of the result. If order='K' and the number of + dimensions is unchanged, will try to keep order, otherwise, + order='C' is implied. + + .. versionadded:: 1.17.0 Returns ------- @@ -255,17 +272,23 @@ def ones_like(a, dtype=None, order='K', subok=True): >>> y = np.arange(3, dtype=float) >>> y - array([ 0., 1., 2.]) + array([0., 1., 2.]) >>> np.ones_like(y) - array([ 1., 1., 1.]) + array([1., 1., 1.]) """ - res = empty_like(a, dtype=dtype, order=order, subok=subok) + res = empty_like(a, dtype=dtype, order=order, subok=subok, shape=shape) multiarray.copyto(res, 1, casting='unsafe') return res -def full(shape, fill_value, dtype=None, order='C'): +def _full_dispatcher(shape, fill_value, dtype=None, order=None, *, like=None): + return(like,) + + +@set_array_function_like_doc +@set_module('numpy') +def full(shape, fill_value, dtype=None, order='C', *, like=None): """ Return a new array of given shape and type, filled with `fill_value`. @@ -273,14 +296,17 @@ def full(shape, fill_value, dtype=None, order='C'): ---------- shape : int or sequence of ints Shape of the new array, e.g., ``(2, 3)`` or ``2``. - fill_value : scalar + fill_value : scalar or array_like Fill value. dtype : data-type, optional - The desired data-type for the array The default, `None`, means - `np.array(fill_value).dtype`. + The desired data-type for the array The default, None, means + ``np.array(fill_value).dtype``. order : {'C', 'F'}, optional Whether to store multidimensional data in C- or Fortran-contiguous (row- or column-wise) order in memory. + ${ARRAY_FUNCTION_LIKE} + + .. versionadded:: 1.20.0 Returns ------- @@ -297,21 +323,39 @@ def full(shape, fill_value, dtype=None, order='C'): Examples -------- >>> np.full((2, 2), np.inf) - array([[ inf, inf], - [ inf, inf]]) + array([[inf, inf], + [inf, inf]]) >>> np.full((2, 2), 10) array([[10, 10], [10, 10]]) + >>> np.full((2, 2), [1, 2]) + array([[1, 2], + [1, 2]]) + """ + if like is not None: + return _full_with_like(shape, fill_value, dtype=dtype, order=order, like=like) + if dtype is None: - dtype = array(fill_value).dtype + fill_value = asarray(fill_value) + dtype = fill_value.dtype a = empty(shape, dtype, order) multiarray.copyto(a, fill_value, casting='unsafe') return a -def full_like(a, fill_value, dtype=None, order='K', subok=True): +_full_with_like = array_function_dispatch( + _full_dispatcher +)(full) + + +def _full_like_dispatcher(a, fill_value, dtype=None, order=None, subok=None, shape=None): + return (a,) + + +@array_function_dispatch(_full_like_dispatcher) +def full_like(a, fill_value, dtype=None, order='K', subok=True, shape=None): """ Return a full array with the same shape and type as a given array. @@ -331,8 +375,14 @@ def full_like(a, fill_value, dtype=None, order='K', subok=True): as possible. subok : bool, optional. If True, then the newly created array will use the sub-class - type of 'a', otherwise it will be a base-class array. Defaults + type of `a`, otherwise it will be a base-class array. Defaults to True. + shape : int or sequence of ints, optional. + Overrides the shape of the result. If order='K' and the number of + dimensions is unchanged, will try to keep order, otherwise, + order='C' is implied. + + .. versionadded:: 1.17.0 Returns ------- @@ -354,21 +404,26 @@ def full_like(a, fill_value, dtype=None, order='K', subok=True): >>> np.full_like(x, 0.1) array([0, 0, 0, 0, 0, 0]) >>> np.full_like(x, 0.1, dtype=np.double) - array([ 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]) + array([0.1, 0.1, 0.1, 0.1, 0.1, 0.1]) >>> np.full_like(x, np.nan, dtype=np.double) - array([ nan, nan, nan, nan, nan, nan]) + array([nan, nan, nan, nan, nan, nan]) >>> y = np.arange(6, dtype=np.double) >>> np.full_like(y, 0.1) - array([ 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]) + array([0.1, 0.1, 0.1, 0.1, 0.1, 0.1]) """ - res = empty_like(a, dtype=dtype, order=order, subok=subok) + res = empty_like(a, dtype=dtype, order=order, subok=subok, shape=shape) multiarray.copyto(res, fill_value, casting='unsafe') return res -def count_nonzero(a, axis=None): +def _count_nonzero_dispatcher(a, axis=None, *, keepdims=None): + return (a,) + + +@array_function_dispatch(_count_nonzero_dispatcher) +def count_nonzero(a, axis=None, *, keepdims=False): """ Counts the number of non-zero values in the array ``a``. @@ -393,6 +448,13 @@ def count_nonzero(a, axis=None): .. versionadded:: 1.12.0 + keepdims : bool, optional + If this is set to True, the axes that are counted are left + in the result as dimensions with size one. With this option, + the result will broadcast correctly against the input array. + + .. versionadded:: 1.19.0 + Returns ------- count : int or array of int @@ -408,15 +470,19 @@ def count_nonzero(a, axis=None): -------- >>> np.count_nonzero(np.eye(4)) 4 - >>> np.count_nonzero([[0,1,7,0,0],[3,0,0,2,19]]) + >>> a = np.array([[0, 1, 7, 0], + ... [3, 0, 2, 19]]) + >>> np.count_nonzero(a) 5 - >>> np.count_nonzero([[0,1,7,0,0],[3,0,0,2,19]], axis=0) - array([1, 1, 1, 1, 1]) - >>> np.count_nonzero([[0,1,7,0,0],[3,0,0,2,19]], axis=1) + >>> np.count_nonzero(a, axis=0) + array([1, 1, 2, 1]) + >>> np.count_nonzero(a, axis=1) array([2, 3]) - + >>> np.count_nonzero(a, axis=1, keepdims=True) + array([[2], + [3]]) """ - if axis is None: + if axis is None and not keepdims: return multiarray.count_nonzero(a) a = asanyarray(a) @@ -427,307 +493,13 @@ def count_nonzero(a, axis=None): else: a_bool = a.astype(np.bool_, copy=False) - return a_bool.sum(axis=axis, dtype=np.intp) - - -def asarray(a, dtype=None, order=None): - """Convert the input to an array. - - Parameters - ---------- - a : array_like - Input data, in any form that can be converted to an array. This - includes lists, lists of tuples, tuples, tuples of tuples, tuples - of lists and ndarrays. - dtype : data-type, optional - By default, the data-type is inferred from the input data. - order : {'C', 'F'}, optional - Whether to use row-major (C-style) or - column-major (Fortran-style) memory representation. - Defaults to 'C'. - - Returns - ------- - out : ndarray - Array interpretation of `a`. No copy is performed if the input - is already an ndarray with matching dtype and order. If `a` is a - subclass of ndarray, a base class ndarray is returned. - - See Also - -------- - asanyarray : Similar function which passes through subclasses. - ascontiguousarray : Convert input to a contiguous array. - asfarray : Convert input to a floating point ndarray. - asfortranarray : Convert input to an ndarray with column-major - memory order. - asarray_chkfinite : Similar function which checks input for NaNs and Infs. - fromiter : Create an array from an iterator. - fromfunction : Construct an array by executing a function on grid - positions. - - Examples - -------- - Convert a list into an array: - - >>> a = [1, 2] - >>> np.asarray(a) - array([1, 2]) - - Existing arrays are not copied: - - >>> a = np.array([1, 2]) - >>> np.asarray(a) is a - True - - If `dtype` is set, array is copied only if dtype does not match: - - >>> a = np.array([1, 2], dtype=np.float32) - >>> np.asarray(a, dtype=np.float32) is a - True - >>> np.asarray(a, dtype=np.float64) is a - False - - Contrary to `asanyarray`, ndarray subclasses are not passed through: - - >>> issubclass(np.recarray, np.ndarray) - True - >>> a = np.array([(1.0, 2), (3.0, 4)], dtype='f4,i4').view(np.recarray) - >>> np.asarray(a) is a - False - >>> np.asanyarray(a) is a - True - - """ - return array(a, dtype, copy=False, order=order) - - -def asanyarray(a, dtype=None, order=None): - """Convert the input to an ndarray, but pass ndarray subclasses through. - - Parameters - ---------- - a : array_like - Input data, in any form that can be converted to an array. This - includes scalars, lists, lists of tuples, tuples, tuples of tuples, - tuples of lists, and ndarrays. - dtype : data-type, optional - By default, the data-type is inferred from the input data. - order : {'C', 'F'}, optional - Whether to use row-major (C-style) or column-major - (Fortran-style) memory representation. Defaults to 'C'. - - Returns - ------- - out : ndarray or an ndarray subclass - Array interpretation of `a`. If `a` is an ndarray or a subclass - of ndarray, it is returned as-is and no copy is performed. - - See Also - -------- - asarray : Similar function which always returns ndarrays. - ascontiguousarray : Convert input to a contiguous array. - asfarray : Convert input to a floating point ndarray. - asfortranarray : Convert input to an ndarray with column-major - memory order. - asarray_chkfinite : Similar function which checks input for NaNs and - Infs. - fromiter : Create an array from an iterator. - fromfunction : Construct an array by executing a function on grid - positions. - - Examples - -------- - Convert a list into an array: - - >>> a = [1, 2] - >>> np.asanyarray(a) - array([1, 2]) - - Instances of `ndarray` subclasses are passed through as-is: - - >>> a = np.array([(1.0, 2), (3.0, 4)], dtype='f4,i4').view(np.recarray) - >>> np.asanyarray(a) is a - True - - """ - return array(a, dtype, copy=False, order=order, subok=True) - - -def ascontiguousarray(a, dtype=None): - """ - Return a contiguous array in memory (C order). - - Parameters - ---------- - a : array_like - Input array. - dtype : str or dtype object, optional - Data-type of returned array. - - Returns - ------- - out : ndarray - Contiguous array of same shape and content as `a`, with type `dtype` - if specified. - - See Also - -------- - asfortranarray : Convert input to an ndarray with column-major - memory order. - require : Return an ndarray that satisfies requirements. - ndarray.flags : Information about the memory layout of the array. - - Examples - -------- - >>> x = np.arange(6).reshape(2,3) - >>> np.ascontiguousarray(x, dtype=np.float32) - array([[ 0., 1., 2.], - [ 3., 4., 5.]], dtype=float32) - >>> x.flags['C_CONTIGUOUS'] - True - - """ - return array(a, dtype, copy=False, order='C', ndmin=1) - - -def asfortranarray(a, dtype=None): - """ - Return an array laid out in Fortran order in memory. - - Parameters - ---------- - a : array_like - Input array. - dtype : str or dtype object, optional - By default, the data-type is inferred from the input data. - - Returns - ------- - out : ndarray - The input `a` in Fortran, or column-major, order. - - See Also - -------- - ascontiguousarray : Convert input to a contiguous (C order) array. - asanyarray : Convert input to an ndarray with either row or - column-major memory order. - require : Return an ndarray that satisfies requirements. - ndarray.flags : Information about the memory layout of the array. - - Examples - -------- - >>> x = np.arange(6).reshape(2,3) - >>> y = np.asfortranarray(x) - >>> x.flags['F_CONTIGUOUS'] - False - >>> y.flags['F_CONTIGUOUS'] - True - - """ - return array(a, dtype, copy=False, order='F', ndmin=1) - - -def require(a, dtype=None, requirements=None): - """ - Return an ndarray of the provided type that satisfies requirements. - - This function is useful to be sure that an array with the correct flags - is returned for passing to compiled code (perhaps through ctypes). - - Parameters - ---------- - a : array_like - The object to be converted to a type-and-requirement-satisfying array. - dtype : data-type - The required data-type. If None preserve the current dtype. If your - application requires the data to be in native byteorder, include - a byteorder specification as a part of the dtype specification. - requirements : str or list of str - The requirements list can be any of the following - - * 'F_CONTIGUOUS' ('F') - ensure a Fortran-contiguous array - * 'C_CONTIGUOUS' ('C') - ensure a C-contiguous array - * 'ALIGNED' ('A') - ensure a data-type aligned array - * 'WRITEABLE' ('W') - ensure a writable array - * 'OWNDATA' ('O') - ensure an array that owns its own data - * 'ENSUREARRAY', ('E') - ensure a base array, instead of a subclass - - See Also - -------- - asarray : Convert input to an ndarray. - asanyarray : Convert to an ndarray, but pass through ndarray subclasses. - ascontiguousarray : Convert input to a contiguous array. - asfortranarray : Convert input to an ndarray with column-major - memory order. - ndarray.flags : Information about the memory layout of the array. - - Notes - ----- - The returned array will be guaranteed to have the listed requirements - by making a copy if needed. - - Examples - -------- - >>> x = np.arange(6).reshape(2,3) - >>> x.flags - C_CONTIGUOUS : True - F_CONTIGUOUS : False - OWNDATA : False - WRITEABLE : True - ALIGNED : True - WRITEBACKIFCOPY : False - UPDATEIFCOPY : False - - >>> y = np.require(x, dtype=np.float32, requirements=['A', 'O', 'W', 'F']) - >>> y.flags - C_CONTIGUOUS : False - F_CONTIGUOUS : True - OWNDATA : True - WRITEABLE : True - ALIGNED : True - WRITEBACKIFCOPY : False - UPDATEIFCOPY : False - - """ - possible_flags = {'C': 'C', 'C_CONTIGUOUS': 'C', 'CONTIGUOUS': 'C', - 'F': 'F', 'F_CONTIGUOUS': 'F', 'FORTRAN': 'F', - 'A': 'A', 'ALIGNED': 'A', - 'W': 'W', 'WRITEABLE': 'W', - 'O': 'O', 'OWNDATA': 'O', - 'E': 'E', 'ENSUREARRAY': 'E'} - if not requirements: - return asanyarray(a, dtype=dtype) - else: - requirements = set(possible_flags[x.upper()] for x in requirements) - - if 'E' in requirements: - requirements.remove('E') - subok = False - else: - subok = True - - order = 'A' - if requirements >= set(['C', 'F']): - raise ValueError('Cannot specify both "C" and "F" order') - elif 'F' in requirements: - order = 'F' - requirements.remove('F') - elif 'C' in requirements: - order = 'C' - requirements.remove('C') - - arr = array(a, dtype=dtype, order=order, copy=False, subok=subok) - - for prop in requirements: - if not arr.flags[prop]: - arr = arr.copy(order) - break - return arr + return a_bool.sum(axis=axis, dtype=np.intp, keepdims=keepdims) +@set_module('numpy') def isfortran(a): """ - Returns True if the array is Fortran contiguous but *not* C contiguous. + Check if the array is Fortran contiguous but *not* C contiguous. This function is obsolete and, because of changes due to relaxed stride checking, its return value for the same array may differ for versions @@ -739,6 +511,11 @@ def isfortran(a): a : ndarray Input array. + Returns + ------- + isfortran : bool + Returns True if the array is Fortran contiguous but *not* C contiguous. + Examples -------- @@ -754,7 +531,7 @@ def isfortran(a): >>> np.isfortran(a) False - >>> b = np.array([[1, 2, 3], [4, 5, 6]], order='FORTRAN') + >>> b = np.array([[1, 2, 3], [4, 5, 6]], order='F') >>> b array([[1, 2, 3], [4, 5, 6]]) @@ -780,13 +557,18 @@ def isfortran(a): C-ordered arrays evaluate as False even if they are also FORTRAN-ordered. - >>> np.isfortran(np.array([1, 2], order='FORTRAN')) + >>> np.isfortran(np.array([1, 2], order='F')) False """ return a.flags.fnc +def _argwhere_dispatcher(a): + return (a,) + + +@array_function_dispatch(_argwhere_dispatcher) def argwhere(a): """ Find the indices of array elements that are non-zero, grouped by element. @@ -798,8 +580,10 @@ def argwhere(a): Returns ------- - index_array : ndarray + index_array : (N, a.ndim) ndarray Indices of elements that are non-zero. Indices are grouped by element. + This array will have shape ``(N, a.ndim)`` where ``N`` is the number of + non-zero items. See Also -------- @@ -807,7 +591,8 @@ def argwhere(a): Notes ----- - ``np.argwhere(a)`` is the same as ``np.transpose(np.nonzero(a))``. + ``np.argwhere(a)`` is almost the same as ``np.transpose(np.nonzero(a))``, + but produces a result of the correct shape for a 0D array. The output of ``argwhere`` is not suitable for indexing arrays. For this purpose use ``nonzero(a)`` instead. @@ -825,9 +610,19 @@ def argwhere(a): [1, 2]]) """ + # nonzero does not behave well on 0d, so promote to 1d + if np.ndim(a) == 0: + a = shape_base.atleast_1d(a) + # then remove the added dimension + return argwhere(a)[:,:0] return transpose(nonzero(a)) +def _flatnonzero_dispatcher(a): + return (a,) + + +@array_function_dispatch(_flatnonzero_dispatcher) def flatnonzero(a): """ Return indices that are non-zero in the flattened version of a. @@ -868,17 +663,11 @@ def flatnonzero(a): return np.nonzero(np.ravel(a))[0] -_mode_from_name_dict = {'v': 0, - 's': 1, - 'f': 2} - - -def _mode_from_name(mode): - if isinstance(mode, basestring): - return _mode_from_name_dict[mode.lower()[0]] - return mode +def _correlate_dispatcher(a, v, mode=None): + return (a, v) +@array_function_dispatch(_correlate_dispatcher) def correlate(a, v, mode='valid'): """ Cross-correlation of two 1-dimensional sequences. @@ -911,6 +700,7 @@ def correlate(a, v, mode='valid'): -------- convolve : Discrete, linear convolution of two one-dimensional sequences. multiarray.correlate : Old, no conjugate, version of correlate. + scipy.signal.correlate : uses FFT which has superior performance on large arrays. Notes ----- @@ -921,14 +711,19 @@ def correlate(a, v, mode='valid'): which is related to ``c_{av}[k]`` by ``c'_{av}[k] = c_{av}[-k]``. + `numpy.correlate` may perform slowly in large arrays (i.e. n = 1e5) because it does + not use the FFT to compute the convolution; in that case, `scipy.signal.correlate` might + be preferable. + + Examples -------- >>> np.correlate([1, 2, 3], [0, 1, 0.5]) - array([ 3.5]) + array([3.5]) >>> np.correlate([1, 2, 3], [0, 1, 0.5], "same") - array([ 2. , 3.5, 3. ]) + array([2. , 3.5, 3. ]) >>> np.correlate([1, 2, 3], [0, 1, 0.5], "full") - array([ 0.5, 2. , 3.5, 3. , 0. ]) + array([0.5, 2. , 3.5, 3. , 0. ]) Using complex sequences: @@ -943,10 +738,14 @@ def correlate(a, v, mode='valid'): array([ 0.0+0.j , 3.0+1.j , 1.5+1.5j, 1.0+0.j , 0.5+0.5j]) """ - mode = _mode_from_name(mode) return multiarray.correlate2(a, v, mode) +def _convolve_dispatcher(a, v, mode=None): + return (a, v) + + +@array_function_dispatch(_convolve_dispatcher) def convolve(a, v, mode='full'): """ Returns the discrete, linear convolution of two one-dimensional sequences. @@ -1010,7 +809,8 @@ def convolve(a, v, mode='full'): References ---------- - .. [1] Wikipedia, "Convolution", http://en.wikipedia.org/wiki/Convolution. + .. [1] Wikipedia, "Convolution", + https://en.wikipedia.org/wiki/Convolution Examples -------- @@ -1018,20 +818,20 @@ def convolve(a, v, mode='full'): before "sliding" the two across one another: >>> np.convolve([1, 2, 3], [0, 1, 0.5]) - array([ 0. , 1. , 2.5, 4. , 1.5]) + array([0. , 1. , 2.5, 4. , 1.5]) Only return the middle values of the convolution. Contains boundary effects, where zeros are taken into account: >>> np.convolve([1,2,3],[0,1,0.5], 'same') - array([ 1. , 2.5, 4. ]) + array([1. , 2.5, 4. ]) The two arrays are of the same length, so there is only one position where they completely overlap: >>> np.convolve([1,2,3],[0,1,0.5], 'valid') - array([ 2.5]) + array([2.5]) """ a, v = array(a, copy=False, ndmin=1), array(v, copy=False, ndmin=1) @@ -1041,10 +841,14 @@ def convolve(a, v, mode='full'): raise ValueError('a cannot be empty') if len(v) == 0: raise ValueError('v cannot be empty') - mode = _mode_from_name(mode) return multiarray.correlate(a, v[::-1], mode) +def _outer_dispatcher(a, b, out=None): + return (a, b, out) + + +@array_function_dispatch(_outer_dispatcher) def outer(a, b, out=None): """ Compute the outer product of two vectors. @@ -1080,8 +884,11 @@ def outer(a, b, out=None): -------- inner einsum : ``einsum('i,j->ij', a.ravel(), b.ravel())`` is the equivalent. - ufunc.outer : A generalization to N dimensions and other operations. - ``np.multiply.outer(a.ravel(), b.ravel())`` is the equivalent. + ufunc.outer : A generalization to dimensions other than 1D and other + operations. ``np.multiply.outer(a.ravel(), b.ravel())`` + is the equivalent. + tensordot : ``np.tensordot(a.ravel(), b.ravel(), axes=((), ()))`` + is the equivalent. References ---------- @@ -1102,11 +909,11 @@ def outer(a, b, out=None): [-2., -1., 0., 1., 2.]]) >>> im = np.outer(1j*np.linspace(2, -2, 5), np.ones((5,))) >>> im - array([[ 0.+2.j, 0.+2.j, 0.+2.j, 0.+2.j, 0.+2.j], - [ 0.+1.j, 0.+1.j, 0.+1.j, 0.+1.j, 0.+1.j], - [ 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j], - [ 0.-1.j, 0.-1.j, 0.-1.j, 0.-1.j, 0.-1.j], - [ 0.-2.j, 0.-2.j, 0.-2.j, 0.-2.j, 0.-2.j]]) + array([[0.+2.j, 0.+2.j, 0.+2.j, 0.+2.j, 0.+2.j], + [0.+1.j, 0.+1.j, 0.+1.j, 0.+1.j, 0.+1.j], + [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j], + [0.-1.j, 0.-1.j, 0.-1.j, 0.-1.j, 0.-1.j], + [0.-2.j, 0.-2.j, 0.-2.j, 0.-2.j, 0.-2.j]]) >>> grid = rl + im >>> grid array([[-2.+2.j, -1.+2.j, 0.+2.j, 1.+2.j, 2.+2.j], @@ -1119,9 +926,9 @@ def outer(a, b, out=None): >>> x = np.array(['a', 'b', 'c'], dtype=object) >>> np.outer(x, [1, 2, 3]) - array([[a, aa, aaa], - [b, bb, bbb], - [c, cc, ccc]], dtype=object) + array([['a', 'aa', 'aaa'], + ['b', 'bb', 'bbb'], + ['c', 'cc', 'ccc']], dtype=object) """ a = asarray(a) @@ -1129,22 +936,25 @@ def outer(a, b, out=None): return multiply(a.ravel()[:, newaxis], b.ravel()[newaxis, :], out) +def _tensordot_dispatcher(a, b, axes=None): + return (a, b) + + +@array_function_dispatch(_tensordot_dispatcher) def tensordot(a, b, axes=2): """ - Compute tensor dot product along specified axes for arrays >= 1-D. + Compute tensor dot product along specified axes. - Given two tensors (arrays of dimension greater than or equal to one), - `a` and `b`, and an array_like object containing two array_like - objects, ``(a_axes, b_axes)``, sum the products of `a`'s and `b`'s - elements (components) over the axes specified by ``a_axes`` and - ``b_axes``. The third argument can be a single non-negative - integer_like scalar, ``N``; if it is such, then the last ``N`` - dimensions of `a` and the first ``N`` dimensions of `b` are summed - over. + Given two tensors, `a` and `b`, and an array_like object containing + two array_like objects, ``(a_axes, b_axes)``, sum the products of + `a`'s and `b`'s elements (components) over the axes specified by + ``a_axes`` and ``b_axes``. The third argument can be a single non-negative + integer_like scalar, ``N``; if it is such, then the last ``N`` dimensions + of `a` and the first ``N`` dimensions of `b` are summed over. Parameters ---------- - a, b : array_like, len(shape) >= 1 + a, b : array_like Tensors to "dot". axes : int or (2,) array_like @@ -1155,6 +965,11 @@ def tensordot(a, b, axes=2): Or, a list of axes to be summed over, first sequence applying to `a`, second to `b`. Both elements array_like must be of the same length. + Returns + ------- + output : ndarray + The tensor dot product of the input. + See Also -------- dot, einsum @@ -1175,6 +990,9 @@ def tensordot(a, b, axes=2): two sequences of the same length, with the first axis to sum over given first in both sequences, the second axis second, and so forth. + The shape of the result consists of the non-contracted axes of the + first tensor, followed by the non-contracted axes of the second. + Examples -------- A "traditional" example: @@ -1185,11 +1003,11 @@ def tensordot(a, b, axes=2): >>> c.shape (5, 2) >>> c - array([[ 4400., 4730.], - [ 4532., 4874.], - [ 4664., 5018.], - [ 4796., 5162.], - [ 4928., 5306.]]) + array([[4400., 4730.], + [4532., 4874.], + [4664., 5018.], + [4796., 5162.], + [4928., 5306.]]) >>> # A slower but equivalent way of computing the same... >>> d = np.zeros((5,2)) >>> for i in range(5): @@ -1215,40 +1033,40 @@ def tensordot(a, b, axes=2): [3, 4]], [[5, 6], [7, 8]]]) - array([[a, b], - [c, d]], dtype=object) + array([['a', 'b'], + ['c', 'd']], dtype=object) >>> np.tensordot(a, A) # third argument default is 2 for double-contraction - array([abbcccdddd, aaaaabbbbbbcccccccdddddddd], dtype=object) + array(['abbcccdddd', 'aaaaabbbbbbcccccccdddddddd'], dtype=object) >>> np.tensordot(a, A, 1) - array([[[acc, bdd], - [aaacccc, bbbdddd]], - [[aaaaacccccc, bbbbbdddddd], - [aaaaaaacccccccc, bbbbbbbdddddddd]]], dtype=object) + array([[['acc', 'bdd'], + ['aaacccc', 'bbbdddd']], + [['aaaaacccccc', 'bbbbbdddddd'], + ['aaaaaaacccccccc', 'bbbbbbbdddddddd']]], dtype=object) >>> np.tensordot(a, A, 0) # tensor product (result too long to incl.) - array([[[[[a, b], - [c, d]], + array([[[[['a', 'b'], + ['c', 'd']], ... >>> np.tensordot(a, A, (0, 1)) - array([[[abbbbb, cddddd], - [aabbbbbb, ccdddddd]], - [[aaabbbbbbb, cccddddddd], - [aaaabbbbbbbb, ccccdddddddd]]], dtype=object) + array([[['abbbbb', 'cddddd'], + ['aabbbbbb', 'ccdddddd']], + [['aaabbbbbbb', 'cccddddddd'], + ['aaaabbbbbbbb', 'ccccdddddddd']]], dtype=object) >>> np.tensordot(a, A, (2, 1)) - array([[[abb, cdd], - [aaabbbb, cccdddd]], - [[aaaaabbbbbb, cccccdddddd], - [aaaaaaabbbbbbbb, cccccccdddddddd]]], dtype=object) + array([[['abb', 'cdd'], + ['aaabbbb', 'cccdddd']], + [['aaaaabbbbbb', 'cccccdddddd'], + ['aaaaaaabbbbbbbb', 'cccccccdddddddd']]], dtype=object) >>> np.tensordot(a, A, ((0, 1), (0, 1))) - array([abbbcccccddddddd, aabbbbccccccdddddddd], dtype=object) + array(['abbbcccccddddddd', 'aabbbbccccccdddddddd'], dtype=object) >>> np.tensordot(a, A, ((2, 1), (1, 0))) - array([acccbbdddd, aaaaacccccccbbbbbbdddddddd], dtype=object) + array(['acccbbdddd', 'aaaaacccccccbbbbbbdddddddd'], dtype=object) """ try: @@ -1315,6 +1133,11 @@ def tensordot(a, b, axes=2): return res.reshape(olda + oldb) +def _roll_dispatcher(a, shift, axis=None): + return (a,) + + +@array_function_dispatch(_roll_dispatcher) def roll(a, shift, axis=None): """ Roll array elements along a given axis. @@ -1358,20 +1181,37 @@ def roll(a, shift, axis=None): >>> x = np.arange(10) >>> np.roll(x, 2) array([8, 9, 0, 1, 2, 3, 4, 5, 6, 7]) + >>> np.roll(x, -2) + array([2, 3, 4, 5, 6, 7, 8, 9, 0, 1]) - >>> x2 = np.reshape(x, (2,5)) + >>> x2 = np.reshape(x, (2, 5)) >>> x2 array([[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]]) >>> np.roll(x2, 1) array([[9, 0, 1, 2, 3], [4, 5, 6, 7, 8]]) + >>> np.roll(x2, -1) + array([[1, 2, 3, 4, 5], + [6, 7, 8, 9, 0]]) >>> np.roll(x2, 1, axis=0) + array([[5, 6, 7, 8, 9], + [0, 1, 2, 3, 4]]) + >>> np.roll(x2, -1, axis=0) array([[5, 6, 7, 8, 9], [0, 1, 2, 3, 4]]) >>> np.roll(x2, 1, axis=1) array([[4, 0, 1, 2, 3], [9, 5, 6, 7, 8]]) + >>> np.roll(x2, -1, axis=1) + array([[1, 2, 3, 4, 0], + [6, 7, 8, 9, 5]]) + >>> np.roll(x2, (1, 1), axis=(1, 0)) + array([[9, 5, 6, 7, 8], + [4, 0, 1, 2, 3]]) + >>> np.roll(x2, (2, 1), axis=(1, 0)) + array([[8, 9, 5, 6, 7], + [3, 4, 0, 1, 2]]) """ a = asanyarray(a) @@ -1404,6 +1244,11 @@ def roll(a, shift, axis=None): return result +def _rollaxis_dispatcher(a, axis, start=None): + return (a,) + + +@array_function_dispatch(_rollaxis_dispatcher) def rollaxis(a, axis, start=0): """ Roll the specified axis backwards, until it lies in a given position. @@ -1417,11 +1262,39 @@ def rollaxis(a, axis, start=0): a : ndarray Input array. axis : int - The axis to roll backwards. The positions of the other axes do not + The axis to be rolled. The positions of the other axes do not change relative to one another. start : int, optional - The axis is rolled until it lies before this position. The default, - 0, results in a "complete" roll. + When ``start <= axis``, the axis is rolled back until it lies in + this position. When ``start > axis``, the axis is rolled until it + lies before this position. The default, 0, results in a "complete" + roll. The following table describes how negative values of ``start`` + are interpreted: + + .. table:: + :align: left + + +-------------------+----------------------+ + | ``start`` | Normalized ``start`` | + +===================+======================+ + | ``-(arr.ndim+1)`` | raise ``AxisError`` | + +-------------------+----------------------+ + | ``-arr.ndim`` | 0 | + +-------------------+----------------------+ + | |vdots| | |vdots| | + +-------------------+----------------------+ + | ``-1`` | ``arr.ndim-1`` | + +-------------------+----------------------+ + | ``0`` | ``0`` | + +-------------------+----------------------+ + | |vdots| | |vdots| | + +-------------------+----------------------+ + | ``arr.ndim`` | ``arr.ndim`` | + +-------------------+----------------------+ + | ``arr.ndim + 1`` | raise ``AxisError`` | + +-------------------+----------------------+ + + .. |vdots| unicode:: U+22EE .. Vertical Ellipsis Returns ------- @@ -1508,11 +1381,14 @@ def normalize_axis_tuple(axis, ndim, argname=None, allow_duplicate=False): -------- normalize_axis_index : normalizing a single scalar axis """ - try: - axis = [operator.index(axis)] - except TypeError: - axis = tuple(axis) - axis = tuple(normalize_axis_index(ax, ndim, argname) for ax in axis) + # Optimization to speed-up the most common cases. + if type(axis) not in (tuple, list): + try: + axis = [operator.index(axis)] + except TypeError: + pass + # Going via an iterator directly is slower than via list comprehension. + axis = tuple([normalize_axis_index(ax, ndim, argname) for ax in axis]) if not allow_duplicate and len(set(axis)) != len(axis): if argname: raise ValueError('repeated axis in `{}` argument'.format(argname)) @@ -1521,6 +1397,11 @@ def normalize_axis_tuple(axis, ndim, argname=None, allow_duplicate=False): return axis +def _moveaxis_dispatcher(a, source, destination): + return (a,) + + +@array_function_dispatch(_moveaxis_dispatcher) def moveaxis(a, source, destination): """ Move axes of an array to new positions. @@ -1546,12 +1427,11 @@ def moveaxis(a, source, destination): See Also -------- - transpose: Permute the dimensions of an array. - swapaxes: Interchange two axes of an array. + transpose : Permute the dimensions of an array. + swapaxes : Interchange two axes of an array. Examples -------- - >>> x = np.zeros((3, 4, 5)) >>> np.moveaxis(x, 0, -1).shape (4, 5, 3) @@ -1592,11 +1472,11 @@ def moveaxis(a, source, destination): return result -# fix hack in scipy which imports this function -def _move_axis_to_0(a, axis): - return moveaxis(a, axis, 0) +def _cross_dispatcher(a, b, axisa=None, axisb=None, axisc=None, axis=None): + return (a, b) +@array_function_dispatch(_cross_dispatcher) def cross(a, b, axisa=-1, axisb=-1, axisc=-1, axis=None): """ Return the cross product of two (arrays of) vectors. @@ -1678,7 +1558,7 @@ def cross(a, b, axisa=-1, axisb=-1, axisc=-1, axis=None): >>> x = [1,2] >>> y = [4,5] >>> np.cross(x, y) - -3 + array(-3) Multiple vector cross-products. Note that the direction of the cross product vector is defined by the `right-hand rule`. @@ -1797,11 +1677,12 @@ def cross(a, b, axisa=-1, axisb=-1, axisc=-1, axis=None): little_endian = (sys.byteorder == 'little') -def indices(dimensions, dtype=int): +@set_module('numpy') +def indices(dimensions, dtype=int, sparse=False): """ Return an array representing the indices of a grid. - Compute an array where the subarrays contain index values 0,1,... + Compute an array where the subarrays contain index values 0, 1, ... varying only along the corresponding axis. Parameters @@ -1810,28 +1691,38 @@ def indices(dimensions, dtype=int): The shape of the grid. dtype : dtype, optional Data type of the result. + sparse : boolean, optional + Return a sparse representation of the grid instead of a dense + representation. Default is False. + + .. versionadded:: 1.17 Returns ------- - grid : ndarray - The array of grid indices, - ``grid.shape = (len(dimensions),) + tuple(dimensions)``. + grid : one ndarray or tuple of ndarrays + If sparse is False: + Returns one array of grid indices, + ``grid.shape = (len(dimensions),) + tuple(dimensions)``. + If sparse is True: + Returns a tuple of arrays, with + ``grid[i].shape = (1, ..., 1, dimensions[i], 1, ..., 1)`` with + dimensions[i] in the ith place See Also -------- - mgrid, meshgrid + mgrid, ogrid, meshgrid Notes ----- - The output shape is obtained by prepending the number of dimensions - in front of the tuple of dimensions, i.e. if `dimensions` is a tuple - ``(r0, ..., rN-1)`` of length ``N``, the output shape is - ``(N,r0,...,rN-1)``. + The output shape in the dense case is obtained by prepending the number + of dimensions in front of the tuple of dimensions, i.e. if `dimensions` + is a tuple ``(r0, ..., rN-1)`` of length ``N``, the output shape is + ``(N, r0, ..., rN-1)``. The subarrays ``grid[k]`` contains the N-D array of indices along the ``k-th`` axis. Explicitly:: - grid[k,i0,i1,...,iN-1] = ik + grid[k, i0, i1, ..., iN-1] = ik Examples -------- @@ -1856,19 +1747,46 @@ def indices(dimensions, dtype=int): Note that it would be more straightforward in the above example to extract the required elements directly with ``x[:2, :3]``. + If sparse is set to true, the grid will be returned in a sparse + representation. + + >>> i, j = np.indices((2, 3), sparse=True) + >>> i.shape + (2, 1) + >>> j.shape + (1, 3) + >>> i # row indices + array([[0], + [1]]) + >>> j # column indices + array([[0, 1, 2]]) + """ dimensions = tuple(dimensions) N = len(dimensions) shape = (1,)*N - res = empty((N,)+dimensions, dtype=dtype) + if sparse: + res = tuple() + else: + res = empty((N,)+dimensions, dtype=dtype) for i, dim in enumerate(dimensions): - res[i] = arange(dim, dtype=dtype).reshape( + idx = arange(dim, dtype=dtype).reshape( shape[:i] + (dim,) + shape[i+1:] ) + if sparse: + res = res + (idx,) + else: + res[i] = idx return res -def fromfunction(function, shape, **kwargs): +def _fromfunction_dispatcher(function, shape, *, dtype=None, like=None, **kwargs): + return (like,) + + +@set_array_function_like_doc +@set_module('numpy') +def fromfunction(function, shape, *, dtype=float, like=None, **kwargs): """ Construct an array by executing a function over each coordinate. @@ -1889,6 +1807,9 @@ def fromfunction(function, shape, **kwargs): dtype : data-type, optional Data-type of the coordinate arrays passed to `function`. By default, `dtype` is float. + ${ARRAY_FUNCTION_LIKE} + + .. versionadded:: 1.20.0 Returns ------- @@ -1896,7 +1817,7 @@ def fromfunction(function, shape, **kwargs): The result of the call to `function` is passed back directly. Therefore the shape of `fromfunction` is completely determined by `function`. If `function` returns a scalar value, the shape of - `fromfunction` would match the `shape` parameter. + `fromfunction` would not match the `shape` parameter. See Also -------- @@ -1919,29 +1840,81 @@ def fromfunction(function, shape, **kwargs): [2, 3, 4]]) """ - dtype = kwargs.pop('dtype', float) + if like is not None: + return _fromfunction_with_like(function, shape, dtype=dtype, like=like, **kwargs) + args = indices(shape, dtype=dtype) return function(*args, **kwargs) -def isscalar(num): +_fromfunction_with_like = array_function_dispatch( + _fromfunction_dispatcher +)(fromfunction) + + +def _frombuffer(buf, dtype, shape, order): + return frombuffer(buf, dtype=dtype).reshape(shape, order=order) + + +@set_module('numpy') +def isscalar(element): """ - Returns True if the type of `num` is a scalar type. + Returns True if the type of `element` is a scalar type. Parameters ---------- - num : any + element : any Input argument, can be of any type and shape. Returns ------- val : bool - True if `num` is a scalar type, False if it is not. + True if `element` is a scalar type, False if it is not. + + See Also + -------- + ndim : Get the number of dimensions of an array + + Notes + ----- + If you need a stricter way to identify a *numerical* scalar, use + ``isinstance(x, numbers.Number)``, as that returns ``False`` for most + non-numerical elements such as strings. + + In most cases ``np.ndim(x) == 0`` should be used instead of this function, + as that will also return true for 0d arrays. This is how numpy overloads + functions in the style of the ``dx`` arguments to `gradient` and the ``bins`` + argument to `histogram`. Some key differences: + + +--------------------------------------+---------------+-------------------+ + | x |``isscalar(x)``|``np.ndim(x) == 0``| + +======================================+===============+===================+ + | PEP 3141 numeric objects (including | ``True`` | ``True`` | + | builtins) | | | + +--------------------------------------+---------------+-------------------+ + | builtin string and buffer objects | ``True`` | ``True`` | + +--------------------------------------+---------------+-------------------+ + | other builtin objects, like | ``False`` | ``True`` | + | `pathlib.Path`, `Exception`, | | | + | the result of `re.compile` | | | + +--------------------------------------+---------------+-------------------+ + | third-party objects like | ``False`` | ``True`` | + | `matplotlib.figure.Figure` | | | + +--------------------------------------+---------------+-------------------+ + | zero-dimensional numpy arrays | ``False`` | ``True`` | + +--------------------------------------+---------------+-------------------+ + | other numpy arrays | ``False`` | ``False`` | + +--------------------------------------+---------------+-------------------+ + | `list`, `tuple`, and other sequence | ``False`` | ``False`` | + | objects | | | + +--------------------------------------+---------------+-------------------+ Examples -------- >>> np.isscalar(3.1) True + >>> np.isscalar(np.array(3.1)) + False >>> np.isscalar([3.1]) False >>> np.isscalar(False) @@ -1952,18 +1925,19 @@ def isscalar(num): NumPy supports PEP 3141 numbers: >>> from fractions import Fraction - >>> isscalar(Fraction(5, 17)) + >>> np.isscalar(Fraction(5, 17)) True >>> from numbers import Number - >>> isscalar(Number()) + >>> np.isscalar(Number()) True """ - return (isinstance(num, generic) - or type(num) in ScalarType - or isinstance(num, numbers.Number)) + return (isinstance(element, generic) + or type(element) in ScalarType + or isinstance(element, numbers.Number)) +@set_module('numpy') def binary_repr(num, width=None): """ Return the binary representation of the input number as a string. @@ -2015,7 +1989,7 @@ def binary_repr(num, width=None): References ---------- .. [1] Wikipedia, "Two's complement", - http://en.wikipedia.org/wiki/Two's_complement + https://en.wikipedia.org/wiki/Two's_complement Examples -------- @@ -2042,6 +2016,10 @@ def warn_if_insufficient(width, binwidth): "will raise an error in the future.", DeprecationWarning, stacklevel=3) + # Ensure that num is a Python integer to avoid overflow or unwanted + # casts to floating point. + num = operator.index(num) + if num == 0: return '0' * (width or 1) @@ -2074,6 +2052,7 @@ def warn_if_insufficient(width, binwidth): return '1' * (outwidth - binwidth) + binary +@set_module('numpy') def base_repr(number, base=2, padding=0): """ Return a string representation of a number in the given base system. @@ -2130,29 +2109,6 @@ def base_repr(number, base=2, padding=0): return ''.join(reversed(res or '0')) -def load(file): - """ - Wrapper around cPickle.load which accepts either a file-like object or - a filename. - - Note that the NumPy binary format is not based on pickle/cPickle anymore. - For details on the preferred way of loading and saving files, see `load` - and `save`. - - See Also - -------- - load, save - - """ - # NumPy 1.15.0, 2017-12-10 - warnings.warn( - "np.core.numeric.load is deprecated, use pickle.load instead", - DeprecationWarning, stacklevel=2) - if isinstance(file, type("")): - file = open(file, "rb") - return pickle.load(file) - - # These are all essentially abbreviations # These might wind up in a special abbreviations module @@ -2168,7 +2124,13 @@ def _maketup(descr, val): return tuple(res) -def identity(n, dtype=None): +def _identity_dispatcher(n, dtype=None, *, like=None): + return (like,) + + +@set_array_function_like_doc +@set_module('numpy') +def identity(n, dtype=None, *, like=None): """ Return the identity array. @@ -2181,6 +2143,9 @@ def identity(n, dtype=None): Number of rows (and columns) in `n` x `n` output. dtype : data-type, optional Data-type of the output. Defaults to ``float``. + ${ARRAY_FUNCTION_LIKE} + + .. versionadded:: 1.20.0 Returns ------- @@ -2191,15 +2156,28 @@ def identity(n, dtype=None): Examples -------- >>> np.identity(3) - array([[ 1., 0., 0.], - [ 0., 1., 0.], - [ 0., 0., 1.]]) + array([[1., 0., 0.], + [0., 1., 0.], + [0., 0., 1.]]) """ + if like is not None: + return _identity_with_like(n, dtype=dtype, like=like) + from numpy import eye - return eye(n, dtype=dtype) + return eye(n, dtype=dtype, like=like) + + +_identity_with_like = array_function_dispatch( + _identity_dispatcher +)(identity) + +def _allclose_dispatcher(a, b, rtol=None, atol=None, equal_nan=None): + return (a, b) + +@array_function_dispatch(_allclose_dispatcher) def allclose(a, b, rtol=1.e-5, atol=1.e-8, equal_nan=False): """ Returns True if two arrays are element-wise equal within a tolerance. @@ -2209,9 +2187,9 @@ def allclose(a, b, rtol=1.e-5, atol=1.e-8, equal_nan=False): `atol` are added together to compare against the absolute difference between `a` and `b`. - If either array contains one or more NaNs, False is returned. - Infs are treated as equal if they are in the same place and of the same - sign in both arrays. + NaNs are treated as equal if they are in the same place and if + ``equal_nan=True``. Infs are treated as equal if they are in the same + place and of the same sign in both arrays. Parameters ---------- @@ -2253,6 +2231,9 @@ def allclose(a, b, rtol=1.e-5, atol=1.e-8, equal_nan=False): ``allclose(a, b)`` to evaluate to True. The same is true for `equal` but not `array_equal`. + `allclose` is not defined for non-numeric data types. + `bool` is considered a numeric data-type for this purpose. + Examples -------- >>> np.allclose([1e10,1e-7], [1.00001e10,1e-8]) @@ -2271,6 +2252,11 @@ def allclose(a, b, rtol=1.e-5, atol=1.e-8, equal_nan=False): return bool(res) +def _isclose_dispatcher(a, b, rtol=None, atol=None, equal_nan=None): + return (a, b) + + +@array_function_dispatch(_isclose_dispatcher) def isclose(a, b, rtol=1.e-5, atol=1.e-8, equal_nan=False): """ Returns a boolean array where two arrays are element-wise equal within a @@ -2306,6 +2292,7 @@ def isclose(a, b, rtol=1.e-5, atol=1.e-8, equal_nan=False): See Also -------- allclose + math.isclose Notes ----- @@ -2326,26 +2313,29 @@ def isclose(a, b, rtol=1.e-5, atol=1.e-8, equal_nan=False): `atol` should be carefully selected for the use case at hand. A zero value for `atol` will result in `False` if either `a` or `b` is zero. + `isclose` is not defined for non-numeric data types. + `bool` is considered a numeric data-type for this purpose. + Examples -------- >>> np.isclose([1e10,1e-7], [1.00001e10,1e-8]) - array([True, False]) + array([ True, False]) >>> np.isclose([1e10,1e-8], [1.00001e10,1e-9]) - array([True, True]) + array([ True, True]) >>> np.isclose([1e10,1e-8], [1.0001e10,1e-9]) - array([False, True]) + array([False, True]) >>> np.isclose([1.0, np.nan], [1.0, np.nan]) - array([True, False]) + array([ True, False]) >>> np.isclose([1.0, np.nan], [1.0, np.nan], equal_nan=True) - array([True, True]) + array([ True, True]) >>> np.isclose([1e-8, 1e-7], [0.0, 0.0]) - array([ True, False], dtype=bool) + array([ True, False]) >>> np.isclose([1e-100, 1e-7], [0.0, 0.0], atol=0.0) - array([False, False], dtype=bool) + array([False, False]) >>> np.isclose([1e-10, 1e-10], [1e-20, 0.0]) - array([ True, True], dtype=bool) + array([ True, True]) >>> np.isclose([1e-10, 1e-10], [1e-20, 0.999999e-10], atol=0.0) - array([False, True], dtype=bool) + array([False, True]) """ def within_tol(x, y, atol, rtol): with errstate(invalid='ignore'): @@ -2357,8 +2347,13 @@ def within_tol(x, y, atol, rtol): # Make sure y is an inexact type to avoid bad behavior on abs(MIN_INT). # This will cause casting of x later. Also, make sure to allow subclasses # (e.g., for numpy.ma). - dt = multiarray.result_type(y, 1.) - y = array(y, dtype=dt, copy=False, subok=True) + # NOTE: We explicitly allow timedelta, which used to work. This could + # possibly be deprecated. See also gh-18286. + # timedelta works if `atol` is an integer or also a timedelta. + # Although, the default tolerances are unlikely to be useful + if y.dtype.kind != "m": + dt = multiarray.result_type(y, 1.) + y = asanyarray(y, dtype=dt) xfin = isfinite(x) yfin = isfinite(y) @@ -2386,7 +2381,12 @@ def within_tol(x, y, atol, rtol): return cond[()] # Flatten 0d arrays to scalars -def array_equal(a1, a2): +def _array_equal_dispatcher(a1, a2, equal_nan=None): + return (a1, a2) + + +@array_function_dispatch(_array_equal_dispatcher) +def array_equal(a1, a2, equal_nan=False): """ True if two arrays have the same shape and elements, False otherwise. @@ -2394,6 +2394,12 @@ def array_equal(a1, a2): ---------- a1, a2 : array_like Input arrays. + equal_nan : bool + Whether to compare NaN's as equal. If the dtype of a1 and a2 is + complex, values will be considered equal if either the real or the + imaginary component of a given value is ``nan``. + + .. versionadded:: 1.19.0 Returns ------- @@ -2417,7 +2423,21 @@ def array_equal(a1, a2): False >>> np.array_equal([1, 2], [1, 4]) False + >>> a = np.array([1, np.nan]) + >>> np.array_equal(a, a) + False + >>> np.array_equal(a, a, equal_nan=True) + True + + When ``equal_nan`` is True, complex values with nan components are + considered equal if either the real *or* the imaginary components are nan. + >>> a = np.array([1 + 1j]) + >>> b = a.copy() + >>> a.real = np.nan + >>> b.imag = np.nan + >>> np.array_equal(a, b, equal_nan=True) + True """ try: a1, a2 = asarray(a1), asarray(a2) @@ -2425,9 +2445,22 @@ def array_equal(a1, a2): return False if a1.shape != a2.shape: return False - return bool(asarray(a1 == a2).all()) + if not equal_nan: + return bool(asarray(a1 == a2).all()) + # Handling NaN values if equal_nan is True + a1nan, a2nan = isnan(a1), isnan(a2) + # NaN's occur at different locations + if not (a1nan == a2nan).all(): + return False + # Shapes of a1, a2 and masks are guaranteed to be consistent by this point + return bool(asarray(a1[~a1nan] == a2[~a1nan]).all()) +def _array_equiv_dispatcher(a1, a2): + return (a1, a2) + + +@array_function_dispatch(_array_equiv_dispatcher) def array_equiv(a1, a2): """ Returns True if input arrays are shape consistent and all elements equal. @@ -2475,438 +2508,6 @@ def array_equiv(a1, a2): return bool(asarray(a1 == a2).all()) -_errdict = {"ignore": ERR_IGNORE, - "warn": ERR_WARN, - "raise": ERR_RAISE, - "call": ERR_CALL, - "print": ERR_PRINT, - "log": ERR_LOG} - -_errdict_rev = {} -for key in _errdict.keys(): - _errdict_rev[_errdict[key]] = key -del key - - -def seterr(all=None, divide=None, over=None, under=None, invalid=None): - """ - Set how floating-point errors are handled. - - Note that operations on integer scalar types (such as `int16`) are - handled like floating point, and are affected by these settings. - - Parameters - ---------- - all : {'ignore', 'warn', 'raise', 'call', 'print', 'log'}, optional - Set treatment for all types of floating-point errors at once: - - - ignore: Take no action when the exception occurs. - - warn: Print a `RuntimeWarning` (via the Python `warnings` module). - - raise: Raise a `FloatingPointError`. - - call: Call a function specified using the `seterrcall` function. - - print: Print a warning directly to ``stdout``. - - log: Record error in a Log object specified by `seterrcall`. - - The default is not to change the current behavior. - divide : {'ignore', 'warn', 'raise', 'call', 'print', 'log'}, optional - Treatment for division by zero. - over : {'ignore', 'warn', 'raise', 'call', 'print', 'log'}, optional - Treatment for floating-point overflow. - under : {'ignore', 'warn', 'raise', 'call', 'print', 'log'}, optional - Treatment for floating-point underflow. - invalid : {'ignore', 'warn', 'raise', 'call', 'print', 'log'}, optional - Treatment for invalid floating-point operation. - - Returns - ------- - old_settings : dict - Dictionary containing the old settings. - - See also - -------- - seterrcall : Set a callback function for the 'call' mode. - geterr, geterrcall, errstate - - Notes - ----- - The floating-point exceptions are defined in the IEEE 754 standard [1]_: - - - Division by zero: infinite result obtained from finite numbers. - - Overflow: result too large to be expressed. - - Underflow: result so close to zero that some precision - was lost. - - Invalid operation: result is not an expressible number, typically - indicates that a NaN was produced. - - .. [1] http://en.wikipedia.org/wiki/IEEE_754 - - Examples - -------- - >>> old_settings = np.seterr(all='ignore') #seterr to known value - >>> np.seterr(over='raise') - {'over': 'ignore', 'divide': 'ignore', 'invalid': 'ignore', - 'under': 'ignore'} - >>> np.seterr(**old_settings) # reset to default - {'over': 'raise', 'divide': 'ignore', 'invalid': 'ignore', - 'under': 'ignore'} - - >>> np.int16(32000) * np.int16(3) - 30464 - >>> old_settings = np.seterr(all='warn', over='raise') - >>> np.int16(32000) * np.int16(3) - Traceback (most recent call last): - File "<stdin>", line 1, in <module> - FloatingPointError: overflow encountered in short_scalars - - >>> old_settings = np.seterr(all='print') - >>> np.geterr() - {'over': 'print', 'divide': 'print', 'invalid': 'print', 'under': 'print'} - >>> np.int16(32000) * np.int16(3) - Warning: overflow encountered in short_scalars - 30464 - - """ - - pyvals = umath.geterrobj() - old = geterr() - - if divide is None: - divide = all or old['divide'] - if over is None: - over = all or old['over'] - if under is None: - under = all or old['under'] - if invalid is None: - invalid = all or old['invalid'] - - maskvalue = ((_errdict[divide] << SHIFT_DIVIDEBYZERO) + - (_errdict[over] << SHIFT_OVERFLOW) + - (_errdict[under] << SHIFT_UNDERFLOW) + - (_errdict[invalid] << SHIFT_INVALID)) - - pyvals[1] = maskvalue - umath.seterrobj(pyvals) - return old - - -def geterr(): - """ - Get the current way of handling floating-point errors. - - Returns - ------- - res : dict - A dictionary with keys "divide", "over", "under", and "invalid", - whose values are from the strings "ignore", "print", "log", "warn", - "raise", and "call". The keys represent possible floating-point - exceptions, and the values define how these exceptions are handled. - - See Also - -------- - geterrcall, seterr, seterrcall - - Notes - ----- - For complete documentation of the types of floating-point exceptions and - treatment options, see `seterr`. - - Examples - -------- - >>> np.geterr() - {'over': 'warn', 'divide': 'warn', 'invalid': 'warn', - 'under': 'ignore'} - >>> np.arange(3.) / np.arange(3.) - array([ NaN, 1., 1.]) - - >>> oldsettings = np.seterr(all='warn', over='raise') - >>> np.geterr() - {'over': 'raise', 'divide': 'warn', 'invalid': 'warn', 'under': 'warn'} - >>> np.arange(3.) / np.arange(3.) - __main__:1: RuntimeWarning: invalid value encountered in divide - array([ NaN, 1., 1.]) - - """ - maskvalue = umath.geterrobj()[1] - mask = 7 - res = {} - val = (maskvalue >> SHIFT_DIVIDEBYZERO) & mask - res['divide'] = _errdict_rev[val] - val = (maskvalue >> SHIFT_OVERFLOW) & mask - res['over'] = _errdict_rev[val] - val = (maskvalue >> SHIFT_UNDERFLOW) & mask - res['under'] = _errdict_rev[val] - val = (maskvalue >> SHIFT_INVALID) & mask - res['invalid'] = _errdict_rev[val] - return res - - -def setbufsize(size): - """ - Set the size of the buffer used in ufuncs. - - Parameters - ---------- - size : int - Size of buffer. - - """ - if size > 10e6: - raise ValueError("Buffer size, %s, is too big." % size) - if size < 5: - raise ValueError("Buffer size, %s, is too small." % size) - if size % 16 != 0: - raise ValueError("Buffer size, %s, is not a multiple of 16." % size) - - pyvals = umath.geterrobj() - old = getbufsize() - pyvals[0] = size - umath.seterrobj(pyvals) - return old - - -def getbufsize(): - """ - Return the size of the buffer used in ufuncs. - - Returns - ------- - getbufsize : int - Size of ufunc buffer in bytes. - - """ - return umath.geterrobj()[0] - - -def seterrcall(func): - """ - Set the floating-point error callback function or log object. - - There are two ways to capture floating-point error messages. The first - is to set the error-handler to 'call', using `seterr`. Then, set - the function to call using this function. - - The second is to set the error-handler to 'log', using `seterr`. - Floating-point errors then trigger a call to the 'write' method of - the provided object. - - Parameters - ---------- - func : callable f(err, flag) or object with write method - Function to call upon floating-point errors ('call'-mode) or - object whose 'write' method is used to log such message ('log'-mode). - - The call function takes two arguments. The first is a string describing - the type of error (such as "divide by zero", "overflow", "underflow", - or "invalid value"), and the second is the status flag. The flag is a - byte, whose four least-significant bits indicate the type of error, one - of "divide", "over", "under", "invalid":: - - [0 0 0 0 divide over under invalid] - - In other words, ``flags = divide + 2*over + 4*under + 8*invalid``. - - If an object is provided, its write method should take one argument, - a string. - - Returns - ------- - h : callable, log instance or None - The old error handler. - - See Also - -------- - seterr, geterr, geterrcall - - Examples - -------- - Callback upon error: - - >>> def err_handler(type, flag): - ... print("Floating point error (%s), with flag %s" % (type, flag)) - ... - - >>> saved_handler = np.seterrcall(err_handler) - >>> save_err = np.seterr(all='call') - - >>> np.array([1, 2, 3]) / 0.0 - Floating point error (divide by zero), with flag 1 - array([ Inf, Inf, Inf]) - - >>> np.seterrcall(saved_handler) - <function err_handler at 0x...> - >>> np.seterr(**save_err) - {'over': 'call', 'divide': 'call', 'invalid': 'call', 'under': 'call'} - - Log error message: - - >>> class Log(object): - ... def write(self, msg): - ... print("LOG: %s" % msg) - ... - - >>> log = Log() - >>> saved_handler = np.seterrcall(log) - >>> save_err = np.seterr(all='log') - - >>> np.array([1, 2, 3]) / 0.0 - LOG: Warning: divide by zero encountered in divide - <BLANKLINE> - array([ Inf, Inf, Inf]) - - >>> np.seterrcall(saved_handler) - <__main__.Log object at 0x...> - >>> np.seterr(**save_err) - {'over': 'log', 'divide': 'log', 'invalid': 'log', 'under': 'log'} - - """ - if func is not None and not isinstance(func, collections_abc.Callable): - if not hasattr(func, 'write') or not isinstance(func.write, collections_abc.Callable): - raise ValueError("Only callable can be used as callback") - pyvals = umath.geterrobj() - old = geterrcall() - pyvals[2] = func - umath.seterrobj(pyvals) - return old - - -def geterrcall(): - """ - Return the current callback function used on floating-point errors. - - When the error handling for a floating-point error (one of "divide", - "over", "under", or "invalid") is set to 'call' or 'log', the function - that is called or the log instance that is written to is returned by - `geterrcall`. This function or log instance has been set with - `seterrcall`. - - Returns - ------- - errobj : callable, log instance or None - The current error handler. If no handler was set through `seterrcall`, - ``None`` is returned. - - See Also - -------- - seterrcall, seterr, geterr - - Notes - ----- - For complete documentation of the types of floating-point exceptions and - treatment options, see `seterr`. - - Examples - -------- - >>> np.geterrcall() # we did not yet set a handler, returns None - - >>> oldsettings = np.seterr(all='call') - >>> def err_handler(type, flag): - ... print("Floating point error (%s), with flag %s" % (type, flag)) - >>> oldhandler = np.seterrcall(err_handler) - >>> np.array([1, 2, 3]) / 0.0 - Floating point error (divide by zero), with flag 1 - array([ Inf, Inf, Inf]) - - >>> cur_handler = np.geterrcall() - >>> cur_handler is err_handler - True - - """ - return umath.geterrobj()[2] - - -class _unspecified(object): - pass - - -_Unspecified = _unspecified() - - -class errstate(object): - """ - errstate(**kwargs) - - Context manager for floating-point error handling. - - Using an instance of `errstate` as a context manager allows statements in - that context to execute with a known error handling behavior. Upon entering - the context the error handling is set with `seterr` and `seterrcall`, and - upon exiting it is reset to what it was before. - - Parameters - ---------- - kwargs : {divide, over, under, invalid} - Keyword arguments. The valid keywords are the possible floating-point - exceptions. Each keyword should have a string value that defines the - treatment for the particular error. Possible values are - {'ignore', 'warn', 'raise', 'call', 'print', 'log'}. - - See Also - -------- - seterr, geterr, seterrcall, geterrcall - - Notes - ----- - The ``with`` statement was introduced in Python 2.5, and can only be used - there by importing it: ``from __future__ import with_statement``. In - earlier Python versions the ``with`` statement is not available. - - For complete documentation of the types of floating-point exceptions and - treatment options, see `seterr`. - - Examples - -------- - >>> from __future__ import with_statement # use 'with' in Python 2.5 - >>> olderr = np.seterr(all='ignore') # Set error handling to known state. - - >>> np.arange(3) / 0. - array([ NaN, Inf, Inf]) - >>> with np.errstate(divide='warn'): - ... np.arange(3) / 0. - ... - __main__:2: RuntimeWarning: divide by zero encountered in divide - array([ NaN, Inf, Inf]) - - >>> np.sqrt(-1) - nan - >>> with np.errstate(invalid='raise'): - ... np.sqrt(-1) - Traceback (most recent call last): - File "<stdin>", line 2, in <module> - FloatingPointError: invalid value encountered in sqrt - - Outside the context the error handling behavior has not changed: - - >>> np.geterr() - {'over': 'warn', 'divide': 'warn', 'invalid': 'warn', - 'under': 'ignore'} - - """ - # Note that we don't want to run the above doctests because they will fail - # without a from __future__ import with_statement - - def __init__(self, **kwargs): - self.call = kwargs.pop('call', _Unspecified) - self.kwargs = kwargs - - def __enter__(self): - self.oldstate = seterr(**self.kwargs) - if self.call is not _Unspecified: - self.oldcall = seterrcall(self.call) - - def __exit__(self, *exc_info): - seterr(**self.oldstate) - if self.call is not _Unspecified: - seterrcall(self.oldcall) - - -def _setdef(): - defval = [UFUNC_BUFSIZE_DEFAULT, ERR_DEFAULT, None] - umath.seterrobj(defval) - - -# set the default values -_setdef() - Inf = inf = infty = Infinity = PINF nan = NaN = NAN False_ = bool_(False) @@ -2914,15 +2515,10 @@ def _setdef(): def extend_all(module): - adict = {} - for a in __all__: - adict[a] = 1 - try: - mall = getattr(module, '__all__') - except AttributeError: - mall = [k for k in module.__dict__.keys() if not k.startswith('_')] + existing = set(__all__) + mall = getattr(module, '__all__') for a in mall: - if a not in adict: + if a not in existing: __all__.append(a) @@ -2932,7 +2528,13 @@ def extend_all(module): from .fromnumeric import * from . import arrayprint from .arrayprint import * +from . import _asarray +from ._asarray import * +from . import _ufunc_config +from ._ufunc_config import * extend_all(fromnumeric) extend_all(umath) extend_all(numerictypes) extend_all(arrayprint) +extend_all(_asarray) +extend_all(_ufunc_config) diff --git a/numpy/core/numeric.pyi b/numpy/core/numeric.pyi new file mode 100644 index 000000000000..d7ec303518a2 --- /dev/null +++ b/numpy/core/numeric.pyi @@ -0,0 +1,635 @@ +from typing import ( + Any, + Union, + Sequence, + Tuple, + Callable, + List, + overload, + TypeVar, + Literal, + Type, + SupportsAbs, + SupportsIndex, + NoReturn, +) +from typing_extensions import TypeGuard + +from numpy import ( + ComplexWarning as ComplexWarning, + dtype, + generic, + unsignedinteger, + signedinteger, + floating, + complexfloating, + bool_, + int_, + intp, + float64, + timedelta64, + object_, + _OrderKACF, + _OrderCF, +) + +from numpy.typing import ( + ArrayLike, + NDArray, + DTypeLike, + _ShapeLike, + _SupportsDType, + _FiniteNestedSequence, + _SupportsArray, + _ScalarLike_co, + _ArrayLikeBool_co, + _ArrayLikeUInt_co, + _ArrayLikeInt_co, + _ArrayLikeFloat_co, + _ArrayLikeComplex_co, + _ArrayLikeTD64_co, + _ArrayLikeObject_co, +) + +_T = TypeVar("_T") +_SCT = TypeVar("_SCT", bound=generic) +_ArrayType = TypeVar("_ArrayType", bound=NDArray[Any]) + +_DTypeLike = Union[ + dtype[_SCT], + Type[_SCT], + _SupportsDType[dtype[_SCT]], +] +_ArrayLike = _FiniteNestedSequence[_SupportsArray[dtype[_SCT]]] +_CorrelateMode = Literal["valid", "same", "full"] + +__all__: List[str] + +@overload +def zeros_like( + a: _ArrayType, + dtype: None = ..., + order: _OrderKACF = ..., + subok: Literal[True] = ..., + shape: None = ..., +) -> _ArrayType: ... +@overload +def zeros_like( + a: _ArrayLike[_SCT], + dtype: None = ..., + order: _OrderKACF = ..., + subok: bool = ..., + shape: None | _ShapeLike = ..., +) -> NDArray[_SCT]: ... +@overload +def zeros_like( + a: object, + dtype: None = ..., + order: _OrderKACF = ..., + subok: bool = ..., + shape: None | _ShapeLike= ..., +) -> NDArray[Any]: ... +@overload +def zeros_like( + a: Any, + dtype: _DTypeLike[_SCT], + order: _OrderKACF = ..., + subok: bool = ..., + shape: None | _ShapeLike= ..., +) -> NDArray[_SCT]: ... +@overload +def zeros_like( + a: Any, + dtype: DTypeLike, + order: _OrderKACF = ..., + subok: bool = ..., + shape: None | _ShapeLike= ..., +) -> NDArray[Any]: ... + +@overload +def ones( + shape: _ShapeLike, + dtype: None = ..., + order: _OrderCF = ..., + *, + like: ArrayLike = ..., +) -> NDArray[float64]: ... +@overload +def ones( + shape: _ShapeLike, + dtype: _DTypeLike[_SCT], + order: _OrderCF = ..., + *, + like: ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def ones( + shape: _ShapeLike, + dtype: DTypeLike, + order: _OrderCF = ..., + *, + like: ArrayLike = ..., +) -> NDArray[Any]: ... + +@overload +def ones_like( + a: _ArrayType, + dtype: None = ..., + order: _OrderKACF = ..., + subok: Literal[True] = ..., + shape: None = ..., +) -> _ArrayType: ... +@overload +def ones_like( + a: _ArrayLike[_SCT], + dtype: None = ..., + order: _OrderKACF = ..., + subok: bool = ..., + shape: None | _ShapeLike = ..., +) -> NDArray[_SCT]: ... +@overload +def ones_like( + a: object, + dtype: None = ..., + order: _OrderKACF = ..., + subok: bool = ..., + shape: None | _ShapeLike= ..., +) -> NDArray[Any]: ... +@overload +def ones_like( + a: Any, + dtype: _DTypeLike[_SCT], + order: _OrderKACF = ..., + subok: bool = ..., + shape: None | _ShapeLike= ..., +) -> NDArray[_SCT]: ... +@overload +def ones_like( + a: Any, + dtype: DTypeLike, + order: _OrderKACF = ..., + subok: bool = ..., + shape: None | _ShapeLike= ..., +) -> NDArray[Any]: ... + +@overload +def full( + shape: _ShapeLike, + fill_value: Any, + dtype: None = ..., + order: _OrderCF = ..., + *, + like: ArrayLike = ..., +) -> NDArray[Any]: ... +@overload +def full( + shape: _ShapeLike, + fill_value: Any, + dtype: _DTypeLike[_SCT], + order: _OrderCF = ..., + *, + like: ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def full( + shape: _ShapeLike, + fill_value: Any, + dtype: DTypeLike, + order: _OrderCF = ..., + *, + like: ArrayLike = ..., +) -> NDArray[Any]: ... + +@overload +def full_like( + a: _ArrayType, + fill_value: Any, + dtype: None = ..., + order: _OrderKACF = ..., + subok: Literal[True] = ..., + shape: None = ..., +) -> _ArrayType: ... +@overload +def full_like( + a: _ArrayLike[_SCT], + fill_value: Any, + dtype: None = ..., + order: _OrderKACF = ..., + subok: bool = ..., + shape: None | _ShapeLike = ..., +) -> NDArray[_SCT]: ... +@overload +def full_like( + a: object, + fill_value: Any, + dtype: None = ..., + order: _OrderKACF = ..., + subok: bool = ..., + shape: None | _ShapeLike= ..., +) -> NDArray[Any]: ... +@overload +def full_like( + a: Any, + fill_value: Any, + dtype: _DTypeLike[_SCT], + order: _OrderKACF = ..., + subok: bool = ..., + shape: None | _ShapeLike= ..., +) -> NDArray[_SCT]: ... +@overload +def full_like( + a: Any, + fill_value: Any, + dtype: DTypeLike, + order: _OrderKACF = ..., + subok: bool = ..., + shape: None | _ShapeLike= ..., +) -> NDArray[Any]: ... + +@overload +def count_nonzero( + a: ArrayLike, + axis: None = ..., + *, + keepdims: Literal[False] = ..., +) -> int: ... +@overload +def count_nonzero( + a: ArrayLike, + axis: _ShapeLike = ..., + *, + keepdims: bool = ..., +) -> Any: ... # TODO: np.intp or ndarray[np.intp] + +def isfortran(a: NDArray[Any] | generic) -> bool: ... + +def argwhere(a: ArrayLike) -> NDArray[intp]: ... + +def flatnonzero(a: ArrayLike) -> NDArray[intp]: ... + +@overload +def correlate( + a: _ArrayLikeBool_co, + v: _ArrayLikeBool_co, + mode: _CorrelateMode = ..., +) -> NDArray[bool_]: ... +@overload +def correlate( + a: _ArrayLikeUInt_co, + v: _ArrayLikeUInt_co, + mode: _CorrelateMode = ..., +) -> NDArray[unsignedinteger[Any]]: ... +@overload +def correlate( + a: _ArrayLikeInt_co, + v: _ArrayLikeInt_co, + mode: _CorrelateMode = ..., +) -> NDArray[signedinteger[Any]]: ... +@overload +def correlate( + a: _ArrayLikeFloat_co, + v: _ArrayLikeFloat_co, + mode: _CorrelateMode = ..., +) -> NDArray[floating[Any]]: ... +@overload +def correlate( + a: _ArrayLikeComplex_co, + v: _ArrayLikeComplex_co, + mode: _CorrelateMode = ..., +) -> NDArray[complexfloating[Any, Any]]: ... +@overload +def correlate( + a: _ArrayLikeTD64_co, + v: _ArrayLikeTD64_co, + mode: _CorrelateMode = ..., +) -> NDArray[timedelta64]: ... +@overload +def correlate( + a: _ArrayLikeObject_co, + v: _ArrayLikeObject_co, + mode: _CorrelateMode = ..., +) -> NDArray[object_]: ... + +@overload +def convolve( + a: _ArrayLikeBool_co, + v: _ArrayLikeBool_co, + mode: _CorrelateMode = ..., +) -> NDArray[bool_]: ... +@overload +def convolve( + a: _ArrayLikeUInt_co, + v: _ArrayLikeUInt_co, + mode: _CorrelateMode = ..., +) -> NDArray[unsignedinteger[Any]]: ... +@overload +def convolve( + a: _ArrayLikeInt_co, + v: _ArrayLikeInt_co, + mode: _CorrelateMode = ..., +) -> NDArray[signedinteger[Any]]: ... +@overload +def convolve( + a: _ArrayLikeFloat_co, + v: _ArrayLikeFloat_co, + mode: _CorrelateMode = ..., +) -> NDArray[floating[Any]]: ... +@overload +def convolve( + a: _ArrayLikeComplex_co, + v: _ArrayLikeComplex_co, + mode: _CorrelateMode = ..., +) -> NDArray[complexfloating[Any, Any]]: ... +@overload +def convolve( + a: _ArrayLikeTD64_co, + v: _ArrayLikeTD64_co, + mode: _CorrelateMode = ..., +) -> NDArray[timedelta64]: ... +@overload +def convolve( + a: _ArrayLikeObject_co, + v: _ArrayLikeObject_co, + mode: _CorrelateMode = ..., +) -> NDArray[object_]: ... + +@overload +def outer( + a: _ArrayLikeBool_co, + b: _ArrayLikeBool_co, + out: None = ..., +) -> NDArray[bool_]: ... +@overload +def outer( + a: _ArrayLikeUInt_co, + b: _ArrayLikeUInt_co, + out: None = ..., +) -> NDArray[unsignedinteger[Any]]: ... +@overload +def outer( + a: _ArrayLikeInt_co, + b: _ArrayLikeInt_co, + out: None = ..., +) -> NDArray[signedinteger[Any]]: ... +@overload +def outer( + a: _ArrayLikeFloat_co, + b: _ArrayLikeFloat_co, + out: None = ..., +) -> NDArray[floating[Any]]: ... +@overload +def outer( + a: _ArrayLikeComplex_co, + b: _ArrayLikeComplex_co, + out: None = ..., +) -> NDArray[complexfloating[Any, Any]]: ... +@overload +def outer( + a: _ArrayLikeTD64_co, + b: _ArrayLikeTD64_co, + out: None = ..., +) -> NDArray[timedelta64]: ... +@overload +def outer( + a: _ArrayLikeObject_co, + b: _ArrayLikeObject_co, + out: None = ..., +) -> NDArray[object_]: ... +@overload +def outer( + a: _ArrayLikeComplex_co | _ArrayLikeTD64_co | _ArrayLikeObject_co, + b: _ArrayLikeComplex_co | _ArrayLikeTD64_co | _ArrayLikeObject_co, + out: _ArrayType, +) -> _ArrayType: ... + +@overload +def tensordot( + a: _ArrayLikeBool_co, + b: _ArrayLikeBool_co, + axes: int | Tuple[_ShapeLike, _ShapeLike] = ..., +) -> NDArray[bool_]: ... +@overload +def tensordot( + a: _ArrayLikeUInt_co, + b: _ArrayLikeUInt_co, + axes: int | Tuple[_ShapeLike, _ShapeLike] = ..., +) -> NDArray[unsignedinteger[Any]]: ... +@overload +def tensordot( + a: _ArrayLikeInt_co, + b: _ArrayLikeInt_co, + axes: int | Tuple[_ShapeLike, _ShapeLike] = ..., +) -> NDArray[signedinteger[Any]]: ... +@overload +def tensordot( + a: _ArrayLikeFloat_co, + b: _ArrayLikeFloat_co, + axes: int | Tuple[_ShapeLike, _ShapeLike] = ..., +) -> NDArray[floating[Any]]: ... +@overload +def tensordot( + a: _ArrayLikeComplex_co, + b: _ArrayLikeComplex_co, + axes: int | Tuple[_ShapeLike, _ShapeLike] = ..., +) -> NDArray[complexfloating[Any, Any]]: ... +@overload +def tensordot( + a: _ArrayLikeTD64_co, + b: _ArrayLikeTD64_co, + axes: int | Tuple[_ShapeLike, _ShapeLike] = ..., +) -> NDArray[timedelta64]: ... +@overload +def tensordot( + a: _ArrayLikeObject_co, + b: _ArrayLikeObject_co, + axes: int | Tuple[_ShapeLike, _ShapeLike] = ..., +) -> NDArray[object_]: ... + +@overload +def roll( + a: _ArrayLike[_SCT], + shift: _ShapeLike, + axis: None | _ShapeLike = ..., +) -> NDArray[_SCT]: ... +@overload +def roll( + a: ArrayLike, + shift: _ShapeLike, + axis: None | _ShapeLike = ..., +) -> NDArray[Any]: ... + +def rollaxis( + a: NDArray[_SCT], + axis: int, + start: int = ..., +) -> NDArray[_SCT]: ... + +def moveaxis( + a: NDArray[_SCT], + source: _ShapeLike, + destination: _ShapeLike, +) -> NDArray[_SCT]: ... + +@overload +def cross( + a: _ArrayLikeBool_co, + b: _ArrayLikeBool_co, + axisa: int = ..., + axisb: int = ..., + axisc: int = ..., + axis: None | int = ..., +) -> NoReturn: ... +@overload +def cross( + a: _ArrayLikeUInt_co, + b: _ArrayLikeUInt_co, + axisa: int = ..., + axisb: int = ..., + axisc: int = ..., + axis: None | int = ..., +) -> NDArray[unsignedinteger[Any]]: ... +@overload +def cross( + a: _ArrayLikeInt_co, + b: _ArrayLikeInt_co, + axisa: int = ..., + axisb: int = ..., + axisc: int = ..., + axis: None | int = ..., +) -> NDArray[signedinteger[Any]]: ... +@overload +def cross( + a: _ArrayLikeFloat_co, + b: _ArrayLikeFloat_co, + axisa: int = ..., + axisb: int = ..., + axisc: int = ..., + axis: None | int = ..., +) -> NDArray[floating[Any]]: ... +@overload +def cross( + a: _ArrayLikeComplex_co, + b: _ArrayLikeComplex_co, + axisa: int = ..., + axisb: int = ..., + axisc: int = ..., + axis: None | int = ..., +) -> NDArray[complexfloating[Any, Any]]: ... +@overload +def cross( + a: _ArrayLikeObject_co, + b: _ArrayLikeObject_co, + axisa: int = ..., + axisb: int = ..., + axisc: int = ..., + axis: None | int = ..., +) -> NDArray[object_]: ... + +@overload +def indices( + dimensions: Sequence[int], + dtype: Type[int] = ..., + sparse: Literal[False] = ..., +) -> NDArray[int_]: ... +@overload +def indices( + dimensions: Sequence[int], + dtype: Type[int] = ..., + sparse: Literal[True] = ..., +) -> Tuple[NDArray[int_], ...]: ... +@overload +def indices( + dimensions: Sequence[int], + dtype: _DTypeLike[_SCT], + sparse: Literal[False] = ..., +) -> NDArray[_SCT]: ... +@overload +def indices( + dimensions: Sequence[int], + dtype: _DTypeLike[_SCT], + sparse: Literal[True], +) -> Tuple[NDArray[_SCT], ...]: ... +@overload +def indices( + dimensions: Sequence[int], + dtype: DTypeLike, + sparse: Literal[False] = ..., +) -> NDArray[Any]: ... +@overload +def indices( + dimensions: Sequence[int], + dtype: DTypeLike, + sparse: Literal[True], +) -> Tuple[NDArray[Any], ...]: ... + +def fromfunction( + function: Callable[..., _T], + shape: Sequence[int], + *, + dtype: DTypeLike = ..., + like: ArrayLike = ..., + **kwargs: Any, +) -> _T: ... + +def isscalar(element: object) -> TypeGuard[ + generic | bool | int | float | complex | str | bytes | memoryview +]: ... + +def binary_repr(num: int, width: None | int = ...) -> str: ... + +def base_repr( + number: SupportsAbs[float], + base: float = ..., + padding: SupportsIndex = ..., +) -> str: ... + +@overload +def identity( + n: int, + dtype: None = ..., + *, + like: ArrayLike = ..., +) -> NDArray[float64]: ... +@overload +def identity( + n: int, + dtype: _DTypeLike[_SCT], + *, + like: ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def identity( + n: int, + dtype: DTypeLike, + *, + like: ArrayLike = ..., +) -> NDArray[Any]: ... + +def allclose( + a: ArrayLike, + b: ArrayLike, + rtol: float = ..., + atol: float = ..., + equal_nan: bool = ..., +) -> bool: ... + +@overload +def isclose( + a: _ScalarLike_co, + b: _ScalarLike_co, + rtol: float = ..., + atol: float = ..., + equal_nan: bool = ..., +) -> bool_: ... +@overload +def isclose( + a: ArrayLike, + b: ArrayLike, + rtol: float = ..., + atol: float = ..., + equal_nan: bool = ..., +) -> NDArray[bool_]: ... + +def array_equal(a1: ArrayLike, a2: ArrayLike, equal_nan: bool = ...) -> bool: ... + +def array_equiv(a1: ArrayLike, a2: ArrayLike) -> bool: ... diff --git a/numpy/core/numerictypes.py b/numpy/core/numerictypes.py index f7f25dd95af9..8e5de852bcff 100644 --- a/numpy/core/numerictypes.py +++ b/numpy/core/numerictypes.py @@ -5,7 +5,7 @@ Exported symbols include: Dictionary with all registered number types (including aliases): - typeDict + sctypeDict Type objects (not all will be available, depends on platform): see variable sctypes for which ones you have @@ -79,387 +79,48 @@ \\-> object_ (not used much) (kind=O) """ -from __future__ import division, absolute_import, print_function - -import types as _types -import sys import numbers -import warnings -from numpy.compat import bytes, long from numpy.core.multiarray import ( - typeinfo, ndarray, array, empty, dtype, datetime_data, - datetime_as_string, busday_offset, busday_count, is_busday, - busdaycalendar + ndarray, array, dtype, datetime_data, datetime_as_string, + busday_offset, busday_count, is_busday, busdaycalendar ) - +from numpy.core.overrides import set_module # we add more at the bottom -__all__ = ['sctypeDict', 'sctypeNA', 'typeDict', 'typeNA', 'sctypes', +__all__ = ['sctypeDict', 'sctypes', 'ScalarType', 'obj2sctype', 'cast', 'nbytes', 'sctype2char', 'maximum_sctype', 'issctype', 'typecodes', 'find_common_type', 'issubdtype', 'datetime_data', 'datetime_as_string', 'busday_offset', 'busday_count', 'is_busday', 'busdaycalendar', ] +# we don't need all these imports, but we need to keep them for compatibility +# for users using np.core.numerictypes.UPPER_TABLE +from ._string_helpers import ( + english_lower, english_upper, english_capitalize, LOWER_TABLE, UPPER_TABLE +) + +from ._type_aliases import ( + sctypeDict, + allTypes, + bitname, + sctypes, + _concrete_types, + _concrete_typeinfo, + _bits_of, +) +from ._dtype import _kind_name # we don't export these for import *, but we do want them accessible # as numerictypes.bool, etc. -if sys.version_info[0] >= 3: - from builtins import bool, int, float, complex, object, str - unicode = str -else: - from __builtin__ import bool, int, float, complex, object, unicode, str - - -# String-handling utilities to avoid locale-dependence. - -# "import string" is costly to import! -# Construct the translation tables directly -# "A" = chr(65), "a" = chr(97) -_all_chars = [chr(_m) for _m in range(256)] -_ascii_upper = _all_chars[65:65+26] -_ascii_lower = _all_chars[97:97+26] -LOWER_TABLE = "".join(_all_chars[:65] + _ascii_lower + _all_chars[65+26:]) -UPPER_TABLE = "".join(_all_chars[:97] + _ascii_upper + _all_chars[97+26:]) - - -def english_lower(s): - """ Apply English case rules to convert ASCII strings to all lower case. +from builtins import bool, int, float, complex, object, str, bytes +from numpy.compat import long, unicode - This is an internal utility function to replace calls to str.lower() such - that we can avoid changing behavior with changing locales. In particular, - Turkish has distinct dotted and dotless variants of the Latin letter "I" in - both lowercase and uppercase. Thus, "I".lower() != "i" in a "tr" locale. - - Parameters - ---------- - s : str - - Returns - ------- - lowered : str - - Examples - -------- - >>> from numpy.core.numerictypes import english_lower - >>> english_lower('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_') - 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz0123456789_' - >>> english_lower('') - '' - """ - lowered = s.translate(LOWER_TABLE) - return lowered - -def english_upper(s): - """ Apply English case rules to convert ASCII strings to all upper case. - - This is an internal utility function to replace calls to str.upper() such - that we can avoid changing behavior with changing locales. In particular, - Turkish has distinct dotted and dotless variants of the Latin letter "I" in - both lowercase and uppercase. Thus, "i".upper() != "I" in a "tr" locale. - - Parameters - ---------- - s : str - - Returns - ------- - uppered : str - Examples - -------- - >>> from numpy.core.numerictypes import english_upper - >>> english_upper('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_') - 'ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_' - >>> english_upper('') - '' - """ - uppered = s.translate(UPPER_TABLE) - return uppered - -def english_capitalize(s): - """ Apply English case rules to convert the first character of an ASCII - string to upper case. - - This is an internal utility function to replace calls to str.capitalize() - such that we can avoid changing behavior with changing locales. - - Parameters - ---------- - s : str - - Returns - ------- - capitalized : str - - Examples - -------- - >>> from numpy.core.numerictypes import english_capitalize - >>> english_capitalize('int8') - 'Int8' - >>> english_capitalize('Int8') - 'Int8' - >>> english_capitalize('') - '' - """ - if s: - return english_upper(s[0]) + s[1:] - else: - return s - - -sctypeDict = {} # Contains all leaf-node scalar types with aliases -sctypeNA = {} # Contails all leaf-node types -> numarray type equivalences -allTypes = {} # Collect the types we will add to the module here - -def _evalname(name): - k = 0 - for ch in name: - if ch in '0123456789': - break - k += 1 - try: - bits = int(name[k:]) - except ValueError: - bits = 0 - base = name[:k] - return base, bits - -def bitname(obj): - """Return a bit-width name for a given type object""" - name = obj.__name__ - base = '' - char = '' - try: - if name[-1] == '_': - newname = name[:-1] - else: - newname = name - info = typeinfo[english_upper(newname)] - assert(info.type == obj) # sanity check - bits = info.bits - - except KeyError: # bit-width name - base, bits = _evalname(name) - char = base[0] - - if name == 'bool_': - char = 'b' - base = 'bool' - elif name == 'void': - char = 'V' - base = 'void' - elif name == 'object_': - char = 'O' - base = 'object' - bits = 0 - elif name == 'datetime64': - char = 'M' - elif name == 'timedelta64': - char = 'm' - - if sys.version_info[0] >= 3: - if name == 'bytes_': - char = 'S' - base = 'bytes' - elif name == 'str_': - char = 'U' - base = 'str' - else: - if name == 'string_': - char = 'S' - base = 'string' - elif name == 'unicode_': - char = 'U' - base = 'unicode' - - bytes = bits // 8 - - if char != '' and bytes != 0: - char = "%s%d" % (char, bytes) - - return base, bits, char - - -def _add_types(): - for type_name, info in typeinfo.items(): - name = english_lower(type_name) - if not isinstance(info, type): - # define C-name and insert typenum and typechar references also - allTypes[name] = info.type - sctypeDict[name] = info.type - sctypeDict[info.char] = info.type - sctypeDict[info.num] = info.type - - else: # generic class - allTypes[name] = info -_add_types() - -def _add_aliases(): - for type_name, info in typeinfo.items(): - if isinstance(info, type): - continue - name = english_lower(type_name) - - # insert bit-width version for this class (if relevant) - base, bit, char = bitname(info.type) - if base[-3:] == 'int' or char[0] in 'ui': - continue - if base != '': - myname = "%s%d" % (base, bit) - if (name not in ('longdouble', 'clongdouble') or - myname not in allTypes): - base_capitalize = english_capitalize(base) - if base == 'complex': - na_name = '%s%d' % (base_capitalize, bit//2) - elif base == 'bool': - na_name = base_capitalize - else: - na_name = "%s%d" % (base_capitalize, bit) - - allTypes[myname] = info.type - - # add mapping for both the bit name and the numarray name - sctypeDict[myname] = info.type - sctypeDict[na_name] = info.type - - # add forward, reverse, and string mapping to numarray - sctypeNA[na_name] = info.type - sctypeNA[info.type] = na_name - sctypeNA[info.char] = na_name - if char != '': - sctypeDict[char] = info.type - sctypeNA[char] = na_name -_add_aliases() - -# Integers are handled so that the int32 and int64 types should agree -# exactly with NPY_INT32, NPY_INT64. We need to enforce the same checking -# as is done in arrayobject.h where the order of getting a bit-width match -# is long, longlong, int, short, char. -def _add_integer_aliases(): - _ctypes = ['LONG', 'LONGLONG', 'INT', 'SHORT', 'BYTE'] - for ctype in _ctypes: - i_info = typeinfo[ctype] - u_info = typeinfo['U'+ctype] - bits = i_info.bits # same for both - - for info, charname, intname, Intname in [ - (i_info,'i%d' % (bits//8,), 'int%d' % bits, 'Int%d' % bits), - (u_info,'u%d' % (bits//8,), 'uint%d' % bits, 'UInt%d' % bits)]: - if intname not in allTypes.keys(): - allTypes[intname] = info.type - sctypeDict[intname] = info.type - sctypeDict[Intname] = info.type - sctypeDict[charname] = info.type - sctypeNA[Intname] = info.type - sctypeNA[charname] = info.type - sctypeNA[info.type] = Intname - sctypeNA[info.char] = Intname -_add_integer_aliases() - -# We use these later -void = allTypes['void'] +# We use this later generic = allTypes['generic'] -# -# Rework the Python names (so that float and complex and int are consistent -# with Python usage) -# -def _set_up_aliases(): - type_pairs = [('complex_', 'cdouble'), - ('int0', 'intp'), - ('uint0', 'uintp'), - ('single', 'float'), - ('csingle', 'cfloat'), - ('singlecomplex', 'cfloat'), - ('float_', 'double'), - ('intc', 'int'), - ('uintc', 'uint'), - ('int_', 'long'), - ('uint', 'ulong'), - ('cfloat', 'cdouble'), - ('longfloat', 'longdouble'), - ('clongfloat', 'clongdouble'), - ('longcomplex', 'clongdouble'), - ('bool_', 'bool'), - ('unicode_', 'unicode'), - ('object_', 'object')] - if sys.version_info[0] >= 3: - type_pairs.extend([('bytes_', 'string'), - ('str_', 'unicode'), - ('string_', 'string')]) - else: - type_pairs.extend([('str_', 'string'), - ('string_', 'string'), - ('bytes_', 'string')]) - for alias, t in type_pairs: - allTypes[alias] = allTypes[t] - sctypeDict[alias] = sctypeDict[t] - # Remove aliases overriding python types and modules - to_remove = ['ulong', 'object', 'unicode', 'int', 'long', 'float', - 'complex', 'bool', 'string', 'datetime', 'timedelta'] - if sys.version_info[0] >= 3: - # Py3K - to_remove.append('bytes') - to_remove.append('str') - to_remove.remove('unicode') - to_remove.remove('long') - for t in to_remove: - try: - del allTypes[t] - del sctypeDict[t] - except KeyError: - pass -_set_up_aliases() - -# Now, construct dictionary to lookup character codes from types -_sctype2char_dict = {} -def _construct_char_code_lookup(): - for name, info in typeinfo.items(): - if not isinstance(info, type): - if info.char not in ['p', 'P']: - _sctype2char_dict[info.type] = info.char -_construct_char_code_lookup() - - -sctypes = {'int': [], - 'uint':[], - 'float':[], - 'complex':[], - 'others':[bool, object, bytes, unicode, void]} - -def _add_array_type(typename, bits): - try: - t = allTypes['%s%d' % (typename, bits)] - except KeyError: - pass - else: - sctypes[typename].append(t) - -def _set_array_types(): - ibytes = [1, 2, 4, 8, 16, 32, 64] - fbytes = [2, 4, 8, 10, 12, 16, 32, 64] - for bytes in ibytes: - bits = 8*bytes - _add_array_type('int', bits) - _add_array_type('uint', bits) - for bytes in fbytes: - bits = 8*bytes - _add_array_type('float', bits) - _add_array_type('complex', 2*bits) - _gi = dtype('p') - if _gi.type not in sctypes['int']: - indx = 0 - sz = _gi.itemsize - _lst = sctypes['int'] - while (indx < len(_lst) and sz >= _lst[indx](0).itemsize): - indx += 1 - sctypes['int'].insert(indx, _gi.type) - sctypes['uint'].insert(indx, dtype('P').type) -_set_array_types() - - genericTypeRank = ['bool', 'int8', 'uint8', 'int16', 'uint16', 'int32', 'uint32', 'int64', 'uint64', 'int128', 'uint128', 'float16', @@ -468,6 +129,7 @@ def _set_array_types(): 'complex32', 'complex64', 'complex128', 'complex160', 'complex192', 'complex256', 'complex512', 'object'] +@set_module('numpy') def maximum_sctype(t): """ Return the scalar type of highest precision of the same kind as the input. @@ -491,33 +153,33 @@ def maximum_sctype(t): Examples -------- >>> np.maximum_sctype(int) - <type 'numpy.int64'> + <class 'numpy.int64'> >>> np.maximum_sctype(np.uint8) - <type 'numpy.uint64'> + <class 'numpy.uint64'> >>> np.maximum_sctype(complex) - <type 'numpy.complex192'> + <class 'numpy.complex256'> # may vary >>> np.maximum_sctype(str) - <type 'numpy.string_'> + <class 'numpy.str_'> >>> np.maximum_sctype('i2') - <type 'numpy.int64'> + <class 'numpy.int64'> >>> np.maximum_sctype('f4') - <type 'numpy.float96'> + <class 'numpy.float128'> # may vary """ g = obj2sctype(t) if g is None: return t t = g - name = t.__name__ - base, bits = _evalname(name) - if bits == 0: - return t - else: + base = _kind_name(dtype(t)) + if base in sctypes: return sctypes[base][-1] + else: + return t +@set_module('numpy') def issctype(rep): """ Determines whether the given object represents a scalar data-type. @@ -562,6 +224,8 @@ def issctype(rep): except Exception: return False + +@set_module('numpy') def obj2sctype(rep, default=None): """ Return the scalar dtype or NumPy equivalent of Python type of an object. @@ -586,22 +250,21 @@ def obj2sctype(rep, default=None): Examples -------- >>> np.obj2sctype(np.int32) - <type 'numpy.int32'> + <class 'numpy.int32'> >>> np.obj2sctype(np.array([1., 2.])) - <type 'numpy.float64'> + <class 'numpy.float64'> >>> np.obj2sctype(np.array([1.j])) - <type 'numpy.complex128'> + <class 'numpy.complex128'> >>> np.obj2sctype(dict) - <type 'numpy.object_'> + <class 'numpy.object_'> >>> np.obj2sctype('string') - <type 'numpy.string_'> >>> np.obj2sctype(1, default=list) - <type 'list'> + <class 'list'> """ - # prevent abtract classes being upcast + # prevent abstract classes being upcast if isinstance(rep, type) and issubclass(rep, generic): return rep # extract dtype from arrays @@ -616,6 +279,7 @@ def obj2sctype(rep, default=None): return res.type +@set_module('numpy') def issubclass_(arg1, arg2): """ Determine if a class is a subclass of a second class. @@ -644,9 +308,11 @@ def issubclass_(arg1, arg2): Examples -------- >>> np.issubclass_(np.int32, int) - True + False >>> np.issubclass_(np.int32, float) False + >>> np.issubclass_(np.float64, float) + True """ try: @@ -654,6 +320,8 @@ def issubclass_(arg1, arg2): except TypeError: return False + +@set_module('numpy') def issubsctype(arg1, arg2): """ Determine if the first argument is a subclass of the second argument. @@ -670,12 +338,12 @@ def issubsctype(arg1, arg2): See Also -------- - issctype, issubdtype,obj2sctype + issctype, issubdtype, obj2sctype Examples -------- >>> np.issubsctype('S8', str) - True + False >>> np.issubsctype(np.array([1]), int) True >>> np.issubsctype(np.array([1]), float) @@ -684,14 +352,18 @@ def issubsctype(arg1, arg2): """ return issubclass(obj2sctype(arg1), obj2sctype(arg2)) + +@set_module('numpy') def issubdtype(arg1, arg2): - """ + r""" Returns True if first argument is a typecode lower/equal in type hierarchy. + This is like the builtin :func:`issubclass`, but for `dtype`\ s. + Parameters ---------- arg1, arg2 : dtype_like - dtype or string representing a typecode. + `dtype` or object coercible to one Returns ------- @@ -699,49 +371,51 @@ def issubdtype(arg1, arg2): See Also -------- + :ref:`arrays.scalars` : Overview of the numpy type hierarchy. issubsctype, issubclass_ - numpy.core.numerictypes : Overview of numpy type hierarchy. Examples -------- - >>> np.issubdtype('S1', np.string_) + `issubdtype` can be used to check the type of arrays: + + >>> ints = np.array([1, 2, 3], dtype=np.int32) + >>> np.issubdtype(ints.dtype, np.integer) + True + >>> np.issubdtype(ints.dtype, np.floating) + False + + >>> floats = np.array([1, 2, 3], dtype=np.float32) + >>> np.issubdtype(floats.dtype, np.integer) + False + >>> np.issubdtype(floats.dtype, np.floating) True + + Similar types of different sizes are not subdtypes of each other: + >>> np.issubdtype(np.float64, np.float32) False + >>> np.issubdtype(np.float32, np.float64) + False + + but both are subtypes of `floating`: + + >>> np.issubdtype(np.float64, np.floating) + True + >>> np.issubdtype(np.float32, np.floating) + True + + For convenience, dtype-like objects are allowed too: + + >>> np.issubdtype('S1', np.string_) + True + >>> np.issubdtype('i4', np.signedinteger) + True """ if not issubclass_(arg1, generic): arg1 = dtype(arg1).type if not issubclass_(arg2, generic): - arg2_orig = arg2 arg2 = dtype(arg2).type - if not isinstance(arg2_orig, dtype): - # weird deprecated behaviour, that tried to infer np.floating from - # float, and similar less obvious things, such as np.generic from - # basestring - mro = arg2.mro() - arg2 = mro[1] if len(mro) > 1 else mro[0] - - def type_repr(x): - """ Helper to produce clear error messages """ - if not isinstance(x, type): - return repr(x) - elif issubclass(x, generic): - return "np.{}".format(x.__name__) - else: - return x.__name__ - - # 1.14, 2017-08-01 - warnings.warn( - "Conversion of the second argument of issubdtype from `{raw}` " - "to `{abstract}` is deprecated. In future, it will be treated " - "as `{concrete} == np.dtype({raw}).type`.".format( - raw=type_repr(arg2_orig), - abstract=type_repr(arg2), - concrete=type_repr(dtype(arg2_orig).type) - ), - FutureWarning, stacklevel=2 - ) return issubclass(arg1, arg2) @@ -764,9 +438,7 @@ def __getitem__(self, obj): _maxvals = _typedict() _minvals = _typedict() def _construct_lookups(): - for name, info in typeinfo.items(): - if isinstance(info, type): - continue + for name, info in _concrete_typeinfo.items(): obj = info.type nbytes[obj] = info.bits // 8 _alignment[obj] = info.alignment @@ -779,6 +451,8 @@ def _construct_lookups(): _construct_lookups() + +@set_module('numpy') def sctype2char(sctype): """ Return the string representation of a scalar dtype. @@ -806,9 +480,9 @@ def sctype2char(sctype): Examples -------- - >>> for sctype in [np.int32, float, complex, np.string_, np.ndarray]: + >>> for sctype in [np.int32, np.double, np.complex_, np.string_, np.ndarray]: ... print(np.sctype2char(sctype)) - l + l # may vary d D S @@ -824,56 +498,28 @@ def sctype2char(sctype): sctype = obj2sctype(sctype) if sctype is None: raise ValueError("unrecognized type") - return _sctype2char_dict[sctype] + if sctype not in _concrete_types: + # for compatibility + raise KeyError(sctype) + return dtype(sctype).char # Create dictionary of casting functions that wrap sequences # indexed by type or type character - - cast = _typedict() -try: - ScalarType = [_types.IntType, _types.FloatType, _types.ComplexType, - _types.LongType, _types.BooleanType, - _types.StringType, _types.UnicodeType, _types.BufferType] -except AttributeError: - # Py3K - ScalarType = [int, float, complex, int, bool, bytes, str, memoryview] - -ScalarType.extend(_sctype2char_dict.keys()) -ScalarType = tuple(ScalarType) -for key in _sctype2char_dict.keys(): +for key in _concrete_types: cast[key] = lambda x, k=key: array(x, copy=False).astype(k) -# Create the typestring lookup dictionary -_typestr = _typedict() -for key in _sctype2char_dict.keys(): - if issubclass(key, allTypes['flexible']): - _typestr[key] = _sctype2char_dict[key] - else: - _typestr[key] = empty((1,), key).dtype.str[1:] - -# Make sure all typestrings are in sctypeDict -for key, val in _typestr.items(): - if val not in sctypeDict: - sctypeDict[val] = key - -# Add additional strings to the sctypeDict - -if sys.version_info[0] >= 3: - _toadd = ['int', 'float', 'complex', 'bool', 'object', - 'str', 'bytes', 'object', ('a', allTypes['bytes_'])] -else: - _toadd = ['int', 'float', 'complex', 'bool', 'object', 'string', - ('str', allTypes['string_']), - 'unicode', 'object', ('a', allTypes['string_'])] - -for name in _toadd: - if isinstance(name, tuple): - sctypeDict[name[0]] = name[1] - else: - sctypeDict[name] = allTypes['%s_' % name] -del _toadd, name +def _scalar_type_key(typ): + """A ``key`` function for `sorted`.""" + dt = dtype(typ) + return (dt.kind.lower(), dt.itemsize) + + +ScalarType = [int, float, complex, int, bool, bytes, str, memoryview] +ScalarType += sorted(_concrete_types, key=_scalar_type_key) +ScalarType = tuple(ScalarType) + # Now add the types we've determined to this module for key in allTypes: @@ -893,8 +539,8 @@ def sctype2char(sctype): 'All':'?bhilqpBHILQPefdgFDGSUVOMm'} # backwards compatibility --- deprecated name +# Formal deprecation: Numpy 1.20.0, 2020-10-19 (see numpy/__init__.py) typeDict = sctypeDict -typeNA = sctypeNA # b -> boolean # u -> unsigned integer @@ -947,6 +593,8 @@ def _register_types(): _register_types() + +@set_module('numpy') def find_common_type(array_types, scalar_types): """ Determine common type following standard coercion rules. diff --git a/numpy/core/numerictypes.pyi b/numpy/core/numerictypes.pyi new file mode 100644 index 000000000000..1d3ff773bbd7 --- /dev/null +++ b/numpy/core/numerictypes.pyi @@ -0,0 +1,173 @@ +import sys +import types +from typing import ( + Literal as L, + Type, + Union, + Tuple, + overload, + Any, + TypeVar, + Dict, + List, + Iterable, + Protocol, + TypedDict, +) + +from numpy import ( + ndarray, + dtype, + generic, + bool_, + ubyte, + ushort, + uintc, + uint, + ulonglong, + byte, + short, + intc, + int_, + longlong, + half, + single, + double, + longdouble, + csingle, + cdouble, + clongdouble, + datetime64, + timedelta64, + object_, + str_, + bytes_, + void, +) + +from numpy.core._type_aliases import ( + sctypeDict as sctypeDict, + sctypes as sctypes, +) + +from numpy.typing import DTypeLike, ArrayLike, _SupportsDType + +_T = TypeVar("_T") +_SCT = TypeVar("_SCT", bound=generic) + +# A paramtrizable subset of `npt.DTypeLike` +_DTypeLike = Union[ + Type[_SCT], + dtype[_SCT], + _SupportsDType[dtype[_SCT]], +] + +class _CastFunc(Protocol): + def __call__( + self, x: ArrayLike, k: DTypeLike = ... + ) -> ndarray[Any, dtype[Any]]: ... + +class _TypeCodes(TypedDict): + Character: L['c'] + Integer: L['bhilqp'] + UnsignedInteger: L['BHILQP'] + Float: L['efdg'] + Complex: L['FDG'] + AllInteger: L['bBhHiIlLqQpP'] + AllFloat: L['efdgFDG'] + Datetime: L['Mm'] + All: L['?bhilqpBHILQPefdgFDGSUVOMm'] + +class _typedict(Dict[Type[generic], _T]): + def __getitem__(self, key: DTypeLike) -> _T: ... + +if sys.version_info >= (3, 10): + _TypeTuple = Union[ + Type[Any], + types.UnionType, + Tuple[Union[Type[Any], types.UnionType, Tuple[Any, ...]], ...], + ] +else: + _TypeTuple = Union[ + Type[Any], + Tuple[Union[Type[Any], Tuple[Any, ...]], ...], + ] + +__all__: List[str] + +@overload +def maximum_sctype(t: _DTypeLike[_SCT]) -> Type[_SCT]: ... +@overload +def maximum_sctype(t: DTypeLike) -> Type[Any]: ... + +@overload +def issctype(rep: dtype[Any] | Type[Any]) -> bool: ... +@overload +def issctype(rep: object) -> L[False]: ... + +@overload +def obj2sctype(rep: _DTypeLike[_SCT], default: None = ...) -> None | Type[_SCT]: ... +@overload +def obj2sctype(rep: _DTypeLike[_SCT], default: _T) -> _T | Type[_SCT]: ... +@overload +def obj2sctype(rep: DTypeLike, default: None = ...) -> None | Type[Any]: ... +@overload +def obj2sctype(rep: DTypeLike, default: _T) -> _T | Type[Any]: ... +@overload +def obj2sctype(rep: object, default: None = ...) -> None: ... +@overload +def obj2sctype(rep: object, default: _T) -> _T: ... + +@overload +def issubclass_(arg1: Type[Any], arg2: _TypeTuple) -> bool: ... +@overload +def issubclass_(arg1: object, arg2: object) -> L[False]: ... + +def issubsctype(arg1: DTypeLike, arg2: DTypeLike) -> bool: ... + +def issubdtype(arg1: DTypeLike, arg2: DTypeLike) -> bool: ... + +def sctype2char(sctype: DTypeLike) -> str: ... + +def find_common_type( + array_types: Iterable[DTypeLike], + scalar_types: Iterable[DTypeLike], +) -> dtype[Any]: ... + +cast: _typedict[_CastFunc] +nbytes: _typedict[int] +typecodes: _TypeCodes +ScalarType: Tuple[ + Type[int], + Type[float], + Type[complex], + Type[int], + Type[bool], + Type[bytes], + Type[str], + Type[memoryview], + Type[bool_], + Type[csingle], + Type[cdouble], + Type[clongdouble], + Type[half], + Type[single], + Type[double], + Type[longdouble], + Type[byte], + Type[short], + Type[intc], + Type[int_], + Type[longlong], + Type[timedelta64], + Type[datetime64], + Type[object_], + Type[bytes_], + Type[str_], + Type[ubyte], + Type[ushort], + Type[uintc], + Type[uint], + Type[ulonglong], + Type[void], +] diff --git a/numpy/core/overrides.py b/numpy/core/overrides.py new file mode 100644 index 000000000000..840cf38c9ccb --- /dev/null +++ b/numpy/core/overrides.py @@ -0,0 +1,204 @@ +"""Implementation of __array_function__ overrides from NEP-18.""" +import collections +import functools +import os + +from numpy.core._multiarray_umath import ( + add_docstring, implement_array_function, _get_implementing_args) +from numpy.compat._inspect import getargspec + + +ARRAY_FUNCTION_ENABLED = bool( + int(os.environ.get('NUMPY_EXPERIMENTAL_ARRAY_FUNCTION', 1))) + +array_function_like_doc = ( + """like : array_like + Reference object to allow the creation of arrays which are not + NumPy arrays. If an array-like passed in as ``like`` supports + the ``__array_function__`` protocol, the result will be defined + by it. In this case, it ensures the creation of an array object + compatible with that passed in via this argument.""" +) + +def set_array_function_like_doc(public_api): + if public_api.__doc__ is not None: + public_api.__doc__ = public_api.__doc__.replace( + "${ARRAY_FUNCTION_LIKE}", + array_function_like_doc, + ) + return public_api + + +add_docstring( + implement_array_function, + """ + Implement a function with checks for __array_function__ overrides. + + All arguments are required, and can only be passed by position. + + Parameters + ---------- + implementation : function + Function that implements the operation on NumPy array without + overrides when called like ``implementation(*args, **kwargs)``. + public_api : function + Function exposed by NumPy's public API originally called like + ``public_api(*args, **kwargs)`` on which arguments are now being + checked. + relevant_args : iterable + Iterable of arguments to check for __array_function__ methods. + args : tuple + Arbitrary positional arguments originally passed into ``public_api``. + kwargs : dict + Arbitrary keyword arguments originally passed into ``public_api``. + + Returns + ------- + Result from calling ``implementation()`` or an ``__array_function__`` + method, as appropriate. + + Raises + ------ + TypeError : if no implementation is found. + """) + + +# exposed for testing purposes; used internally by implement_array_function +add_docstring( + _get_implementing_args, + """ + Collect arguments on which to call __array_function__. + + Parameters + ---------- + relevant_args : iterable of array-like + Iterable of possibly array-like arguments to check for + __array_function__ methods. + + Returns + ------- + Sequence of arguments with __array_function__ methods, in the order in + which they should be called. + """) + + +ArgSpec = collections.namedtuple('ArgSpec', 'args varargs keywords defaults') + + +def verify_matching_signatures(implementation, dispatcher): + """Verify that a dispatcher function has the right signature.""" + implementation_spec = ArgSpec(*getargspec(implementation)) + dispatcher_spec = ArgSpec(*getargspec(dispatcher)) + + if (implementation_spec.args != dispatcher_spec.args or + implementation_spec.varargs != dispatcher_spec.varargs or + implementation_spec.keywords != dispatcher_spec.keywords or + (bool(implementation_spec.defaults) != + bool(dispatcher_spec.defaults)) or + (implementation_spec.defaults is not None and + len(implementation_spec.defaults) != + len(dispatcher_spec.defaults))): + raise RuntimeError('implementation and dispatcher for %s have ' + 'different function signatures' % implementation) + + if implementation_spec.defaults is not None: + if dispatcher_spec.defaults != (None,) * len(dispatcher_spec.defaults): + raise RuntimeError('dispatcher functions can only use None for ' + 'default argument values') + + +def set_module(module): + """Decorator for overriding __module__ on a function or class. + + Example usage:: + + @set_module('numpy') + def example(): + pass + + assert example.__module__ == 'numpy' + """ + def decorator(func): + if module is not None: + func.__module__ = module + return func + return decorator + + +def array_function_dispatch(dispatcher, module=None, verify=True, + docs_from_dispatcher=False): + """Decorator for adding dispatch with the __array_function__ protocol. + + See NEP-18 for example usage. + + Parameters + ---------- + dispatcher : callable + Function that when called like ``dispatcher(*args, **kwargs)`` with + arguments from the NumPy function call returns an iterable of + array-like arguments to check for ``__array_function__``. + module : str, optional + __module__ attribute to set on new function, e.g., ``module='numpy'``. + By default, module is copied from the decorated function. + verify : bool, optional + If True, verify the that the signature of the dispatcher and decorated + function signatures match exactly: all required and optional arguments + should appear in order with the same names, but the default values for + all optional arguments should be ``None``. Only disable verification + if the dispatcher's signature needs to deviate for some particular + reason, e.g., because the function has a signature like + ``func(*args, **kwargs)``. + docs_from_dispatcher : bool, optional + If True, copy docs from the dispatcher function onto the dispatched + function, rather than from the implementation. This is useful for + functions defined in C, which otherwise don't have docstrings. + + Returns + ------- + Function suitable for decorating the implementation of a NumPy function. + """ + + if not ARRAY_FUNCTION_ENABLED: + def decorator(implementation): + if docs_from_dispatcher: + add_docstring(implementation, dispatcher.__doc__) + if module is not None: + implementation.__module__ = module + return implementation + return decorator + + def decorator(implementation): + if verify: + verify_matching_signatures(implementation, dispatcher) + + if docs_from_dispatcher: + add_docstring(implementation, dispatcher.__doc__) + + @functools.wraps(implementation) + def public_api(*args, **kwargs): + relevant_args = dispatcher(*args, **kwargs) + return implement_array_function( + implementation, public_api, relevant_args, args, kwargs) + + public_api.__code__ = public_api.__code__.replace( + co_name=implementation.__name__, + co_filename='<__array_function__ internals>') + if module is not None: + public_api.__module__ = module + + public_api._implementation = implementation + + return public_api + + return decorator + + +def array_function_from_dispatcher( + implementation, module=None, verify=True, docs_from_dispatcher=True): + """Like array_function_dispatcher, but with function arguments flipped.""" + + def decorator(dispatcher): + return array_function_dispatch( + dispatcher, module, verify=verify, + docs_from_dispatcher=docs_from_dispatcher)(implementation) + return decorator diff --git a/numpy/core/records.py b/numpy/core/records.py index 612d39322c3e..c014bc97cda8 100644 --- a/numpy/core/records.py +++ b/numpy/core/records.py @@ -7,10 +7,9 @@ integers, bools etc. However, it is possible for elements to be combinations of these using structured types, such as:: - >>> a = np.array([(1, 2.0), (1, 2.0)], dtype=[('x', int), ('y', float)]) + >>> a = np.array([(1, 2.0), (1, 2.0)], dtype=[('x', np.int64), ('y', np.float64)]) >>> a - array([(1, 2.0), (1, 2.0)], - dtype=[('x', '<i4'), ('y', '<f8')]) + array([(1, 2.), (1, 2.)], dtype=[('x', '<i8'), ('y', '<f8')]) Here, each element consists of two fields: x (and int), and y (a float). This is known as a structured array. The different fields are analogous @@ -21,7 +20,7 @@ array([1, 1]) >>> a['y'] - array([ 2., 2.]) + array([2., 2.]) Record arrays allow us to access fields as properties:: @@ -31,22 +30,24 @@ array([1, 1]) >>> ar.y - array([ 2., 2.]) + array([2., 2.]) """ -from __future__ import division, absolute_import, print_function - -import sys -import os import warnings +from collections import Counter +from contextlib import nullcontext from . import numeric as sb from . import numerictypes as nt -from numpy.compat import isfileobj, bytes, long -from .arrayprint import get_printoptions +from numpy.compat import os_fspath +from numpy.core.overrides import set_module +from .arrayprint import _get_legacy_print_mode # All of the functions allow formats to be a dtype -__all__ = ['record', 'recarray', 'format_parser'] +__all__ = [ + 'record', 'recarray', 'format_parser', + 'fromarrays', 'fromrecords', 'fromstring', 'fromfile', 'array', +] ndarray = sb.ndarray @@ -67,22 +68,24 @@ 'i':'|'} # formats regular expression -# allows multidimension spec with a tuple syntax in front +# allows multidimensional spec with a tuple syntax in front # of the letter code '(2,3)f4' and ' ( 2 , 3 ) f4 ' # are equally allowed -numfmt = nt.typeDict +numfmt = nt.sctypeDict + def find_duplicate(list): """Find duplication in a list, return a list of duplicated elements""" - dup = [] - for i in range(len(list)): - if (list[i] in list[i + 1:]): - if (list[i] not in dup): - dup.append(list[i]) - return dup - -class format_parser(object): + return [ + item + for item, counts in Counter(list).items() + if counts > 1 + ] + + +@set_module('numpy') +class format_parser: """ Class to convert formats, names, titles description to a dtype. @@ -125,10 +128,9 @@ class format_parser(object): Examples -------- - >>> np.format_parser(['f8', 'i4', 'a5'], ['col1', 'col2', 'col3'], + >>> np.format_parser(['<f8', '<i4', '<a5'], ['col1', 'col2', 'col3'], ... ['T1', 'T2', 'T3']).dtype - dtype([(('T1', 'col1'), '<f8'), (('T2', 'col2'), '<i4'), - (('T3', 'col3'), '|S5')]) + dtype([(('T1', 'col1'), '<f8'), (('T2', 'col2'), '<i4'), (('T3', 'col3'), 'S5')]) `names` and/or `titles` can be empty lists. If `titles` is an empty list, titles will simply not appear. If `names` is empty, default field names @@ -136,28 +138,29 @@ class format_parser(object): >>> np.format_parser(['f8', 'i4', 'a5'], ['col1', 'col2', 'col3'], ... []).dtype - dtype([('col1', '<f8'), ('col2', '<i4'), ('col3', '|S5')]) - >>> np.format_parser(['f8', 'i4', 'a5'], [], []).dtype - dtype([('f0', '<f8'), ('f1', '<i4'), ('f2', '|S5')]) + dtype([('col1', '<f8'), ('col2', '<i4'), ('col3', '<S5')]) + >>> np.format_parser(['<f8', '<i4', '<a5'], [], []).dtype + dtype([('f0', '<f8'), ('f1', '<i4'), ('f2', 'S5')]) """ def __init__(self, formats, names, titles, aligned=False, byteorder=None): self._parseFormats(formats, aligned) self._setfieldnames(names, titles) - self._createdescr(byteorder) - self.dtype = self._descr + self._createdtype(byteorder) - def _parseFormats(self, formats, aligned=0): + def _parseFormats(self, formats, aligned=False): """ Parse the field formats """ if formats is None: raise ValueError("Need formats argument") if isinstance(formats, list): - if len(formats) < 2: - formats.append('') - formats = ','.join(formats) - dtype = sb.dtype(formats, aligned) + dtype = sb.dtype( + [('f{}'.format(i), format_) for i, format_ in enumerate(formats)], + aligned, + ) + else: + dtype = sb.dtype(formats, aligned) fields = dtype.fields if fields is None: dtype = sb.dtype([('f1', dtype)], aligned) @@ -171,8 +174,8 @@ def _setfieldnames(self, names, titles): """convert input field names into a list and assign to the _names attribute """ - if (names): - if (type(names) in [list, tuple]): + if names: + if type(names) in [list, tuple]: pass elif isinstance(names, str): names = names.split(',') @@ -194,25 +197,28 @@ def _setfieldnames(self, names, titles): if _dup: raise ValueError("Duplicate field names: %s" % _dup) - if (titles): + if titles: self._titles = [n.strip() for n in titles[:self._nfields]] else: self._titles = [] titles = [] - if (self._nfields > len(titles)): + if self._nfields > len(titles): self._titles += [None] * (self._nfields - len(titles)) - def _createdescr(self, byteorder): - descr = sb.dtype({'names':self._names, - 'formats':self._f_formats, - 'offsets':self._offsets, - 'titles':self._titles}) - if (byteorder is not None): + def _createdtype(self, byteorder): + dtype = sb.dtype({ + 'names': self._names, + 'formats': self._f_formats, + 'offsets': self._offsets, + 'titles': self._titles, + }) + if byteorder is not None: byteorder = _byteorderconv[byteorder[0]] - descr = descr.newbyteorder(byteorder) + dtype = dtype.newbyteorder(byteorder) + + self.dtype = dtype - self._descr = descr class record(nt.void): """A data-type scalar that allows field access as attribute lookup. @@ -224,17 +230,17 @@ class record(nt.void): __module__ = 'numpy' def __repr__(self): - if get_printoptions()['legacy'] == '1.13': + if _get_legacy_print_mode() <= 113: return self.__str__() - return super(record, self).__repr__() + return super().__repr__() def __str__(self): - if get_printoptions()['legacy'] == '1.13': + if _get_legacy_print_mode() <= 113: return str(self.item()) - return super(record, self).__str__() + return super().__str__() def __getattribute__(self, attr): - if attr in ['setfield', 'getfield', 'dtype']: + if attr in ('setfield', 'getfield', 'dtype'): return nt.void.__getattribute__(self, attr) try: return nt.void.__getattribute__(self, attr) @@ -251,15 +257,15 @@ def __getattribute__(self, attr): except AttributeError: #happens if field is Object type return obj - if dt.fields: - return obj.view((self.__class__, obj.dtype.fields)) + if dt.names is not None: + return obj.view((self.__class__, obj.dtype)) return obj else: raise AttributeError("'record' object has no " "attribute '%s'" % attr) def __setattr__(self, attr, val): - if attr in ['setfield', 'getfield', 'dtype']: + if attr in ('setfield', 'getfield', 'dtype'): raise AttributeError("Cannot set '%s' attribute" % attr) fielddict = nt.void.__getattribute__(self, 'dtype').fields res = fielddict.get(attr, None) @@ -276,8 +282,8 @@ def __getitem__(self, indx): obj = nt.void.__getitem__(self, indx) # copy behavior of record.__getattribute__, - if isinstance(obj, nt.void) and obj.dtype.fields: - return obj.view((self.__class__, obj.dtype.fields)) + if isinstance(obj, nt.void) and obj.dtype.names is not None: + return obj.view((self.__class__, obj.dtype)) else: # return a single element return obj @@ -287,10 +293,8 @@ def pprint(self): # pretty-print all fields names = self.dtype.names maxlen = max(len(name) for name in names) - rows = [] fmt = '%% %ds: %%s' % maxlen - for name in names: - rows.append(fmt % (name, getattr(self, name))) + rows = [fmt % (name, getattr(self, name)) for name in names] return "\n".join(rows) # The recarray is almost identical to a standard array (which supports @@ -360,7 +364,7 @@ class recarray(ndarray): See Also -------- - rec.fromrecords : Construct a record array from data. + core.records.fromrecords : Construct a record array from data. record : fundamental data-type for `recarray`. format_parser : determine a data-type from formats, names, titles. @@ -379,20 +383,19 @@ class recarray(ndarray): -------- Create an array with two fields, ``x`` and ``y``: - >>> x = np.array([(1.0, 2), (3.0, 4)], dtype=[('x', float), ('y', int)]) + >>> x = np.array([(1.0, 2), (3.0, 4)], dtype=[('x', '<f8'), ('y', '<i8')]) >>> x - array([(1.0, 2), (3.0, 4)], - dtype=[('x', '<f8'), ('y', '<i4')]) + array([(1., 2), (3., 4)], dtype=[('x', '<f8'), ('y', '<i8')]) >>> x['x'] - array([ 1., 3.]) + array([1., 3.]) View the array as a record array: >>> x = x.view(np.recarray) >>> x.x - array([ 1., 3.]) + array([1., 3.]) >>> x.y array([2, 4]) @@ -419,7 +422,7 @@ def __new__(subtype, shape, dtype=None, buf=None, offset=0, strides=None, if dtype is not None: descr = sb.dtype(dtype) else: - descr = format_parser(formats, names, titles, aligned, byteorder)._descr + descr = format_parser(formats, names, titles, aligned, byteorder).dtype if buf is None: self = ndarray.__new__(subtype, shape, (record, descr), order=order) @@ -430,7 +433,7 @@ def __new__(subtype, shape, dtype=None, buf=None, offset=0, strides=None, return self def __array_finalize__(self, obj): - if self.dtype.type is not record and self.dtype.fields: + if self.dtype.type is not record and self.dtype.names is not None: # if self.dtype is not np.record, invoke __setattr__ which will # convert it to a record if it is a void dtype. self.dtype = self.dtype @@ -448,8 +451,8 @@ def __getattribute__(self, attr): fielddict = ndarray.__getattribute__(self, 'dtype').fields try: res = fielddict[attr][:2] - except (TypeError, KeyError): - raise AttributeError("recarray has no attribute %s" % attr) + except (TypeError, KeyError) as e: + raise AttributeError("recarray has no attribute %s" % attr) from e obj = self.getfield(*res) # At this point obj will always be a recarray, since (see @@ -458,7 +461,7 @@ def __getattribute__(self, attr): # with void type convert it to the same dtype.type (eg to preserve # numpy.record type if present), since nested structured fields do not # inherit type. Don't do this for non-void structures though. - if obj.dtype.fields: + if obj.dtype.names is not None: if issubclass(obj.dtype.type, nt.void): return obj.view(dtype=(self.dtype.type, obj.dtype)) return obj @@ -473,7 +476,7 @@ def __setattr__(self, attr, val): # Automatically convert (void) structured types to records # (but not non-void structures, subarrays, or non-structured voids) - if attr == 'dtype' and issubclass(val.type, nt.void) and val.fields: + if attr == 'dtype' and issubclass(val.type, nt.void) and val.names is not None: val = sb.dtype((record, val)) newattr = attr not in self.__dict__ @@ -482,8 +485,7 @@ def __setattr__(self, attr, val): except Exception: fielddict = ndarray.__getattribute__(self, 'dtype').fields or {} if attr not in fielddict: - exctype, value = sys.exc_info()[:2] - raise exctype(value) + raise else: fielddict = ndarray.__getattribute__(self, 'dtype').fields or {} if attr not in fielddict: @@ -497,17 +499,19 @@ def __setattr__(self, attr, val): return ret try: res = fielddict[attr][:2] - except (TypeError, KeyError): - raise AttributeError("record array has no attribute %s" % attr) + except (TypeError, KeyError) as e: + raise AttributeError( + "record array has no attribute %s" % attr + ) from e return self.setfield(val, *res) def __getitem__(self, indx): - obj = super(recarray, self).__getitem__(indx) + obj = super().__getitem__(indx) # copy behavior of getattr, except that here # we might also be returning a single element if isinstance(obj, ndarray): - if obj.dtype.fields: + if obj.dtype.names is not None: obj = obj.view(type(self)) if issubclass(obj.dtype.type, nt.void): return obj.view(dtype=(self.dtype.type, obj.dtype)) @@ -521,8 +525,7 @@ def __getitem__(self, indx): def __repr__(self): repr_dtype = self.dtype - if (self.dtype.type is record - or (not issubclass(self.dtype.type, nt.void))): + if self.dtype.type is record or not issubclass(self.dtype.type, nt.void): # If this is a full record array (has numpy.record dtype), # or if it has a scalar (non-void) dtype with no records, # represent it using the rec.array function. Since rec.array @@ -548,7 +551,7 @@ def __repr__(self): lst = "[], shape=%s" % (repr(self.shape),) lf = '\n'+' '*len(prefix) - if get_printoptions()['legacy'] == '1.13': + if _get_legacy_print_mode() <= 113: lf = ' ' + lf # trailing space return fmt % (lst, lf, repr_dtype) @@ -563,53 +566,94 @@ def field(self, attr, val=None): if val is None: obj = self.getfield(*res) - if obj.dtype.fields: + if obj.dtype.names is not None: return obj return obj.view(ndarray) else: return self.setfield(val, *res) +def _deprecate_shape_0_as_None(shape): + if shape == 0: + warnings.warn( + "Passing `shape=0` to have the shape be inferred is deprecated, " + "and in future will be equivalent to `shape=(0,)`. To infer " + "the shape and suppress this warning, pass `shape=None` instead.", + FutureWarning, stacklevel=3) + return None + else: + return shape + + +@set_module("numpy.rec") def fromarrays(arrayList, dtype=None, shape=None, formats=None, names=None, titles=None, aligned=False, byteorder=None): - """ create a record array from a (flat) list of arrays + """Create a record array from a (flat) list of arrays + + Parameters + ---------- + arrayList : list or tuple + List of array-like objects (such as lists, tuples, + and ndarrays). + dtype : data-type, optional + valid dtype for all arrays + shape : int or tuple of ints, optional + Shape of the resulting array. If not provided, inferred from + ``arrayList[0]``. + formats, names, titles, aligned, byteorder : + If `dtype` is ``None``, these arguments are passed to + `numpy.format_parser` to construct a dtype. See that function for + detailed documentation. + Returns + ------- + np.recarray + Record array consisting of given arrayList columns. + + Examples + -------- >>> x1=np.array([1,2,3,4]) >>> x2=np.array(['a','dd','xyz','12']) >>> x3=np.array([1.1,2,3,4]) >>> r = np.core.records.fromarrays([x1,x2,x3],names='a,b,c') >>> print(r[1]) - (2, 'dd', 2.0) + (2, 'dd', 2.0) # may vary >>> x1[1]=34 >>> r.a array([1, 2, 3, 4]) + + >>> x1 = np.array([1, 2, 3, 4]) + >>> x2 = np.array(['a', 'dd', 'xyz', '12']) + >>> x3 = np.array([1.1, 2, 3,4]) + >>> r = np.core.records.fromarrays( + ... [x1, x2, x3], + ... dtype=np.dtype([('a', np.int32), ('b', 'S3'), ('c', np.float32)])) + >>> r + rec.array([(1, b'a', 1.1), (2, b'dd', 2. ), (3, b'xyz', 3. ), + (4, b'12', 4. )], + dtype=[('a', '<i4'), ('b', 'S3'), ('c', '<f4')]) """ arrayList = [sb.asarray(x) for x in arrayList] - if shape is None or shape == 0: - shape = arrayList[0].shape + # NumPy 1.19.0, 2020-01-01 + shape = _deprecate_shape_0_as_None(shape) - if isinstance(shape, int): + if shape is None: + shape = arrayList[0].shape + elif isinstance(shape, int): shape = (shape,) if formats is None and dtype is None: # go through each object in the list to see if it is an ndarray # and determine the formats. - formats = [] - for obj in arrayList: - if not isinstance(obj, ndarray): - raise ValueError("item in the array list must be an ndarray.") - formats.append(obj.dtype.str) - formats = ','.join(formats) + formats = [obj.dtype for obj in arrayList] if dtype is not None: descr = sb.dtype(dtype) - _names = descr.names else: - parsed = format_parser(formats, names, titles, aligned, byteorder) - _names = parsed._names - descr = parsed._descr + descr = format_parser(formats, names, titles, aligned, byteorder).dtype + _names = descr.names # Determine shape from data-type. if len(descr) != len(arrayList): @@ -621,36 +665,51 @@ def fromarrays(arrayList, dtype=None, shape=None, formats=None, if nn > 0: shape = shape[:-nn] + _array = recarray(shape, descr) + + # populate the record array (makes a copy) for k, obj in enumerate(arrayList): nn = descr[k].ndim testshape = obj.shape[:obj.ndim - nn] + name = _names[k] if testshape != shape: - raise ValueError("array-shape mismatch in array %d" % k) + raise ValueError(f'array-shape mismatch in array {k} ("{name}")') - _array = recarray(shape, descr) - - # populate the record array (makes a copy) - for i in range(len(arrayList)): - _array[_names[i]] = arrayList[i] + _array[name] = obj return _array + +@set_module("numpy.rec") def fromrecords(recList, dtype=None, shape=None, formats=None, names=None, titles=None, aligned=False, byteorder=None): - """ create a recarray from a list of records in text form - - The data in the same field can be heterogeneous, they will be promoted - to the highest data type. This method is intended for creating - smaller record arrays. If used to create large array without formats - defined + """Create a recarray from a list of records in text form. - r=fromrecords([(2,3.,'abc')]*100000) - - it can be slow. + Parameters + ---------- + recList : sequence + data in the same field may be heterogeneous - they will be promoted + to the highest data type. + dtype : data-type, optional + valid dtype for all arrays + shape : int or tuple of ints, optional + shape of each array. + formats, names, titles, aligned, byteorder : + If `dtype` is ``None``, these arguments are passed to + `numpy.format_parser` to construct a dtype. See that function for + detailed documentation. + + If both `formats` and `dtype` are None, then this will auto-detect + formats. Use list of tuples rather than list of lists for faster + processing. - If formats is None, then this will auto-detect formats. Use list of - tuples rather than list of lists for faster processing. + Returns + ------- + np.recarray + record array consisting of given recList rows. + Examples + -------- >>> r=np.core.records.fromrecords([(456,'dbe',1.2),(2,'de',1.3)], ... names='col1,col2,col3') >>> print(r[0]) @@ -658,11 +717,11 @@ def fromrecords(recList, dtype=None, shape=None, formats=None, names=None, >>> r.col1 array([456, 2]) >>> r.col2 - array(['dbe', 'de'], - dtype='|S3') + array(['dbe', 'de'], dtype='<U3') >>> import pickle - >>> print(pickle.loads(pickle.dumps(r))) - [(456, 'dbe', 1.2) (2, 'de', 1.3)] + >>> pickle.loads(pickle.dumps(r)) + rec.array([(456, 'dbe', 1.2), ( 2, 'de', 1.3)], + dtype=[('col1', '<i8'), ('col2', '<U3'), ('col3', '<f8')]) """ if formats is None and dtype is None: # slower @@ -674,14 +733,16 @@ def fromrecords(recList, dtype=None, shape=None, formats=None, names=None, if dtype is not None: descr = sb.dtype((record, dtype)) else: - descr = format_parser(formats, names, titles, aligned, byteorder)._descr + descr = format_parser(formats, names, titles, aligned, byteorder).dtype try: retval = sb.array(recList, dtype=descr) except (TypeError, ValueError): - if (shape is None or shape == 0): + # NumPy 1.19.0, 2020-01-01 + shape = _deprecate_shape_0_as_None(shape) + if shape is None: shape = len(recList) - if isinstance(shape, (int, long)): + if isinstance(shape, int): shape = (shape,) if len(shape) > 1: raise ValueError("Can only deal with 1-d array.") @@ -704,43 +765,119 @@ def fromrecords(recList, dtype=None, shape=None, formats=None, names=None, return res +@set_module("numpy.rec") def fromstring(datastring, dtype=None, shape=None, offset=0, formats=None, names=None, titles=None, aligned=False, byteorder=None): - """ create a (read-only) record array from binary data contained in - a string""" + r"""Create a record array from binary data + + Note that despite the name of this function it does not accept `str` + instances. + + Parameters + ---------- + datastring : bytes-like + Buffer of binary data + dtype : data-type, optional + Valid dtype for all arrays + shape : int or tuple of ints, optional + Shape of each array. + offset : int, optional + Position in the buffer to start reading from. + formats, names, titles, aligned, byteorder : + If `dtype` is ``None``, these arguments are passed to + `numpy.format_parser` to construct a dtype. See that function for + detailed documentation. + + + Returns + ------- + np.recarray + Record array view into the data in datastring. This will be readonly + if `datastring` is readonly. + + See Also + -------- + numpy.frombuffer + + Examples + -------- + >>> a = b'\x01\x02\x03abc' + >>> np.core.records.fromstring(a, dtype='u1,u1,u1,S3') + rec.array([(1, 2, 3, b'abc')], + dtype=[('f0', 'u1'), ('f1', 'u1'), ('f2', 'u1'), ('f3', 'S3')]) + + >>> grades_dtype = [('Name', (np.str_, 10)), ('Marks', np.float64), + ... ('GradeLevel', np.int32)] + >>> grades_array = np.array([('Sam', 33.3, 3), ('Mike', 44.4, 5), + ... ('Aadi', 66.6, 6)], dtype=grades_dtype) + >>> np.core.records.fromstring(grades_array.tobytes(), dtype=grades_dtype) + rec.array([('Sam', 33.3, 3), ('Mike', 44.4, 5), ('Aadi', 66.6, 6)], + dtype=[('Name', '<U10'), ('Marks', '<f8'), ('GradeLevel', '<i4')]) + + >>> s = '\x01\x02\x03abc' + >>> np.core.records.fromstring(s, dtype='u1,u1,u1,S3') + Traceback (most recent call last) + ... + TypeError: a bytes-like object is required, not 'str' + """ if dtype is None and formats is None: - raise ValueError("Must have dtype= or formats=") + raise TypeError("fromstring() needs a 'dtype' or 'formats' argument") if dtype is not None: descr = sb.dtype(dtype) else: - descr = format_parser(formats, names, titles, aligned, byteorder)._descr + descr = format_parser(formats, names, titles, aligned, byteorder).dtype itemsize = descr.itemsize - if (shape is None or shape == 0 or shape == -1): + + # NumPy 1.19.0, 2020-01-01 + shape = _deprecate_shape_0_as_None(shape) + + if shape in (None, -1): shape = (len(datastring) - offset) // itemsize _array = recarray(shape, descr, buf=datastring, offset=offset) return _array def get_remaining_size(fd): + pos = fd.tell() try: - fn = fd.fileno() - except AttributeError: - return os.path.getsize(fd.name) - fd.tell() - st = os.fstat(fn) - size = st.st_size - fd.tell() - return size + fd.seek(0, 2) + return fd.tell() - pos + finally: + fd.seek(pos, 0) + +@set_module("numpy.rec") def fromfile(fd, dtype=None, shape=None, offset=0, formats=None, names=None, titles=None, aligned=False, byteorder=None): """Create an array from binary file data - If file is a string then that file is opened, else it is assumed - to be a file object. The file object must support random access - (i.e. it must have tell and seek methods). + Parameters + ---------- + fd : str or file type + If file is a string or a path-like object then that file is opened, + else it is assumed to be a file object. The file object must + support random access (i.e. it must have tell and seek methods). + dtype : data-type, optional + valid dtype for all arrays + shape : int or tuple of ints, optional + shape of each array. + offset : int, optional + Position in the file to start reading from. + formats, names, titles, aligned, byteorder : + If `dtype` is ``None``, these arguments are passed to + `numpy.format_parser` to construct a dtype. See that function for + detailed documentation + + Returns + ------- + np.recarray + record array consisting of data enclosed in file. + Examples + -------- >>> from tempfile import TemporaryFile >>> a = np.empty(10,dtype='f8,i4,a5') >>> a[5] = (0.5,10,'abcde') @@ -749,7 +886,7 @@ def fromfile(fd, dtype=None, shape=None, offset=0, formats=None, >>> a = a.newbyteorder('<') >>> a.tofile(fd) >>> - >>> fd.seek(0) + >>> _ = fd.seek(0) >>> r=np.core.records.fromfile(fd, formats='f8,i4,a5', shape=10, ... byteorder='<') >>> print(r[5]) @@ -758,57 +895,148 @@ def fromfile(fd, dtype=None, shape=None, offset=0, formats=None, (10,) """ - if (shape is None or shape == 0): + if dtype is None and formats is None: + raise TypeError("fromfile() needs a 'dtype' or 'formats' argument") + + # NumPy 1.19.0, 2020-01-01 + shape = _deprecate_shape_0_as_None(shape) + + if shape is None: shape = (-1,) - elif isinstance(shape, (int, long)): + elif isinstance(shape, int): shape = (shape,) - name = 0 - if isinstance(fd, str): - name = 1 - fd = open(fd, 'rb') - if (offset > 0): - fd.seek(offset, 1) - size = get_remaining_size(fd) - - if dtype is not None: - descr = sb.dtype(dtype) + if hasattr(fd, 'readinto'): + # GH issue 2504. fd supports io.RawIOBase or io.BufferedIOBase interface. + # Example of fd: gzip, BytesIO, BufferedReader + # file already opened + ctx = nullcontext(fd) else: - descr = format_parser(formats, names, titles, aligned, byteorder)._descr + # open file + ctx = open(os_fspath(fd), 'rb') - itemsize = descr.itemsize + with ctx as fd: + if offset > 0: + fd.seek(offset, 1) + size = get_remaining_size(fd) + + if dtype is not None: + descr = sb.dtype(dtype) + else: + descr = format_parser(formats, names, titles, aligned, byteorder).dtype - shapeprod = sb.array(shape).prod() - shapesize = shapeprod * itemsize - if shapesize < 0: - shape = list(shape) - shape[shape.index(-1)] = size / -shapesize - shape = tuple(shape) - shapeprod = sb.array(shape).prod() + itemsize = descr.itemsize - nbytes = shapeprod * itemsize + shapeprod = sb.array(shape).prod(dtype=nt.intp) + shapesize = shapeprod * itemsize + if shapesize < 0: + shape = list(shape) + shape[shape.index(-1)] = size // -shapesize + shape = tuple(shape) + shapeprod = sb.array(shape).prod(dtype=nt.intp) - if nbytes > size: - raise ValueError( - "Not enough bytes left in file for specified shape and type") + nbytes = shapeprod * itemsize - # create the array - _array = recarray(shape, descr) - nbytesread = fd.readinto(_array.data) - if nbytesread != nbytes: - raise IOError("Didn't read as many bytes as expected") - if name: - fd.close() + if nbytes > size: + raise ValueError( + "Not enough bytes left in file for specified shape and type") + + # create the array + _array = recarray(shape, descr) + nbytesread = fd.readinto(_array.data) + if nbytesread != nbytes: + raise OSError("Didn't read as many bytes as expected") return _array + +@set_module("numpy.rec") def array(obj, dtype=None, shape=None, offset=0, strides=None, formats=None, names=None, titles=None, aligned=False, byteorder=None, copy=True): - """Construct a record array from a wide-variety of objects. + """ + Construct a record array from a wide-variety of objects. + + A general-purpose record array constructor that dispatches to the + appropriate `recarray` creation function based on the inputs (see Notes). + + Parameters + ---------- + obj : any + Input object. See Notes for details on how various input types are + treated. + dtype : data-type, optional + Valid dtype for array. + shape : int or tuple of ints, optional + Shape of each array. + offset : int, optional + Position in the file or buffer to start reading from. + strides : tuple of ints, optional + Buffer (`buf`) is interpreted according to these strides (strides + define how many bytes each array element, row, column, etc. + occupy in memory). + formats, names, titles, aligned, byteorder : + If `dtype` is ``None``, these arguments are passed to + `numpy.format_parser` to construct a dtype. See that function for + detailed documentation. + copy : bool, optional + Whether to copy the input object (True), or to use a reference instead. + This option only applies when the input is an ndarray or recarray. + Defaults to True. + + Returns + ------- + np.recarray + Record array created from the specified object. + + Notes + ----- + If `obj` is ``None``, then call the `~numpy.recarray` constructor. If + `obj` is a string, then call the `fromstring` constructor. If `obj` is a + list or a tuple, then if the first object is an `~numpy.ndarray`, call + `fromarrays`, otherwise call `fromrecords`. If `obj` is a + `~numpy.recarray`, then make a copy of the data in the recarray + (if ``copy=True``) and use the new formats, names, and titles. If `obj` + is a file, then call `fromfile`. Finally, if obj is an `ndarray`, then + return ``obj.view(recarray)``, making a copy of the data if ``copy=True``. + + Examples + -------- + >>> a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) + array([[1, 2, 3], + [4, 5, 6], + [7, 8, 9]]) + + >>> np.core.records.array(a) + rec.array([[1, 2, 3], + [4, 5, 6], + [7, 8, 9]], + dtype=int32) + + >>> b = [(1, 1), (2, 4), (3, 9)] + >>> c = np.core.records.array(b, formats = ['i2', 'f2'], names = ('x', 'y')) + >>> c + rec.array([(1, 1.0), (2, 4.0), (3, 9.0)], + dtype=[('x', '<i2'), ('y', '<f2')]) + + >>> c.x + rec.array([1, 2, 3], dtype=int16) + + >>> c.y + rec.array([ 1.0, 4.0, 9.0], dtype=float16) + + >>> r = np.rec.array(['abc','def'], names=['col1','col2']) + >>> print(r.col1) + abc + + >>> r.col1 + array('abc', dtype='<U3') + + >>> r.col2 + array('def', dtype='<U3') """ - if ((isinstance(obj, (type(None), str)) or isfileobj(obj)) and - (formats is None) and (dtype is None)): + if ((isinstance(obj, (type(None), str)) or hasattr(obj, 'readinto')) and + formats is None and dtype is None): raise ValueError("Must define formats (or dtype) if object is " "None, string, or an open file") @@ -817,7 +1045,7 @@ def array(obj, dtype=None, shape=None, offset=0, strides=None, formats=None, dtype = sb.dtype(dtype) elif formats is not None: dtype = format_parser(formats, names, titles, - aligned, byteorder)._descr + aligned, byteorder).dtype else: kwds = {'formats': formats, 'names': names, @@ -849,7 +1077,7 @@ def array(obj, dtype=None, shape=None, offset=0, strides=None, formats=None, new = new.copy() return new - elif isfileobj(obj): + elif hasattr(obj, 'readinto'): return fromfile(obj, dtype=dtype, shape=shape, offset=offset) elif isinstance(obj, ndarray): diff --git a/numpy/core/records.pyi b/numpy/core/records.pyi new file mode 100644 index 000000000000..172bab3eeea7 --- /dev/null +++ b/numpy/core/records.pyi @@ -0,0 +1,237 @@ +import os +from typing import ( + List, + Sequence, + Any, + TypeVar, + Iterable, + overload, + Tuple, + Protocol, +) + +from numpy import ( + format_parser as format_parser, + record as record, + recarray as recarray, + dtype, + generic, + void, + _ByteOrder, + _SupportsBuffer, +) + +from numpy.typing import ( + ArrayLike, + DTypeLike, + NDArray, + _ShapeLike, + _ArrayLikeVoid_co, + _NestedSequence, +) + +_SCT = TypeVar("_SCT", bound=generic) + +_RecArray = recarray[Any, dtype[_SCT]] + +class _SupportsReadInto(Protocol): + def seek(self, offset: int, whence: int, /) -> object: ... + def tell(self, /) -> int: ... + def readinto(self, buffer: memoryview, /) -> int: ... + +__all__: List[str] + +@overload +def fromarrays( + arrayList: Iterable[ArrayLike], + dtype: DTypeLike = ..., + shape: None | _ShapeLike = ..., + formats: None = ..., + names: None = ..., + titles: None = ..., + aligned: bool = ..., + byteorder: None = ..., +) -> _RecArray[Any]: ... +@overload +def fromarrays( + arrayList: Iterable[ArrayLike], + dtype: None = ..., + shape: None | _ShapeLike = ..., + *, + formats: DTypeLike, + names: None | str | Sequence[str] = ..., + titles: None | str | Sequence[str] = ..., + aligned: bool = ..., + byteorder: None | _ByteOrder = ..., +) -> _RecArray[record]: ... + +@overload +def fromrecords( + recList: _ArrayLikeVoid_co | Tuple[Any, ...] | _NestedSequence[Tuple[Any, ...]], + dtype: DTypeLike = ..., + shape: None | _ShapeLike = ..., + formats: None = ..., + names: None = ..., + titles: None = ..., + aligned: bool = ..., + byteorder: None = ..., +) -> _RecArray[record]: ... +@overload +def fromrecords( + recList: _ArrayLikeVoid_co | Tuple[Any, ...] | _NestedSequence[Tuple[Any, ...]], + dtype: None = ..., + shape: None | _ShapeLike = ..., + *, + formats: DTypeLike, + names: None | str | Sequence[str] = ..., + titles: None | str | Sequence[str] = ..., + aligned: bool = ..., + byteorder: None | _ByteOrder = ..., +) -> _RecArray[record]: ... + +@overload +def fromstring( + datastring: _SupportsBuffer, + dtype: DTypeLike, + shape: None | _ShapeLike = ..., + offset: int = ..., + formats: None = ..., + names: None = ..., + titles: None = ..., + aligned: bool = ..., + byteorder: None = ..., +) -> _RecArray[record]: ... +@overload +def fromstring( + datastring: _SupportsBuffer, + dtype: None = ..., + shape: None | _ShapeLike = ..., + offset: int = ..., + *, + formats: DTypeLike, + names: None | str | Sequence[str] = ..., + titles: None | str | Sequence[str] = ..., + aligned: bool = ..., + byteorder: None | _ByteOrder = ..., +) -> _RecArray[record]: ... + +@overload +def fromfile( + fd: str | bytes | os.PathLike[str] | os.PathLike[bytes] | _SupportsReadInto, + dtype: DTypeLike, + shape: None | _ShapeLike = ..., + offset: int = ..., + formats: None = ..., + names: None = ..., + titles: None = ..., + aligned: bool = ..., + byteorder: None = ..., +) -> _RecArray[Any]: ... +@overload +def fromfile( + fd: str | bytes | os.PathLike[str] | os.PathLike[bytes] | _SupportsReadInto, + dtype: None = ..., + shape: None | _ShapeLike = ..., + offset: int = ..., + *, + formats: DTypeLike, + names: None | str | Sequence[str] = ..., + titles: None | str | Sequence[str] = ..., + aligned: bool = ..., + byteorder: None | _ByteOrder = ..., +) -> _RecArray[record]: ... + +@overload +def array( + obj: _SCT | NDArray[_SCT], + dtype: None = ..., + shape: None | _ShapeLike = ..., + offset: int = ..., + formats: None = ..., + names: None = ..., + titles: None = ..., + aligned: bool = ..., + byteorder: None = ..., + copy: bool = ..., +) -> _RecArray[_SCT]: ... +@overload +def array( + obj: ArrayLike, + dtype: DTypeLike, + shape: None | _ShapeLike = ..., + offset: int = ..., + formats: None = ..., + names: None = ..., + titles: None = ..., + aligned: bool = ..., + byteorder: None = ..., + copy: bool = ..., +) -> _RecArray[Any]: ... +@overload +def array( + obj: ArrayLike, + dtype: None = ..., + shape: None | _ShapeLike = ..., + offset: int = ..., + *, + formats: DTypeLike, + names: None | str | Sequence[str] = ..., + titles: None | str | Sequence[str] = ..., + aligned: bool = ..., + byteorder: None | _ByteOrder = ..., + copy: bool = ..., +) -> _RecArray[record]: ... +@overload +def array( + obj: None, + dtype: DTypeLike, + shape: _ShapeLike, + offset: int = ..., + formats: None = ..., + names: None = ..., + titles: None = ..., + aligned: bool = ..., + byteorder: None = ..., + copy: bool = ..., +) -> _RecArray[Any]: ... +@overload +def array( + obj: None, + dtype: None = ..., + *, + shape: _ShapeLike, + offset: int = ..., + formats: DTypeLike, + names: None | str | Sequence[str] = ..., + titles: None | str | Sequence[str] = ..., + aligned: bool = ..., + byteorder: None | _ByteOrder = ..., + copy: bool = ..., +) -> _RecArray[record]: ... +@overload +def array( + obj: _SupportsReadInto, + dtype: DTypeLike, + shape: None | _ShapeLike = ..., + offset: int = ..., + formats: None = ..., + names: None = ..., + titles: None = ..., + aligned: bool = ..., + byteorder: None = ..., + copy: bool = ..., +) -> _RecArray[Any]: ... +@overload +def array( + obj: _SupportsReadInto, + dtype: None = ..., + shape: None | _ShapeLike = ..., + offset: int = ..., + *, + formats: DTypeLike, + names: None | str | Sequence[str] = ..., + titles: None | str | Sequence[str] = ..., + aligned: bool = ..., + byteorder: None | _ByteOrder = ..., + copy: bool = ..., +) -> _RecArray[record]: ... diff --git a/numpy/core/setup.py b/numpy/core/setup.py index f826b278f3b0..a5f423d8fe4f 100644 --- a/numpy/core/setup.py +++ b/numpy/core/setup.py @@ -1,21 +1,18 @@ -from __future__ import division, print_function - import os import sys import pickle import copy -import sysconfig import warnings import platform +import textwrap +import glob from os.path import join + from numpy.distutils import log from distutils.dep_util import newer -from distutils.sysconfig import get_config_var -from numpy._build_utils.apple_accelerate import ( - uses_accelerate_framework, get_sgemv_fix - ) +from sysconfig import get_config_var from numpy.compat import npy_load_module -from setup_common import * +from setup_common import * # noqa: F403 # Set to True to enable relaxed strides checking. This (mostly) means # that `strides[dim]` is ignored if `shape[dim] == 1` when setting flags. @@ -37,7 +34,7 @@ # Use pickle in all cases, as cPickle is gone in python3 and the difference # in time is only in build. -- Charles Harris, 2013-03-30 -class CallOnceOnly(object): +class CallOnceOnly: def __init__(self): self._check_types = None self._check_ieee_macros = None @@ -67,6 +64,20 @@ def check_complex(self, *a, **kw): out = copy.deepcopy(pickle.loads(self._check_complex)) return out +def can_link_svml(): + """SVML library is supported only on x86_64 architecture and currently + only on linux + """ + machine = platform.machine() + system = platform.system() + return "x86_64" in machine and system == "Linux" + +def check_svml_submodule(svmlpath): + if not os.path.exists(svmlpath + "/README.md"): + raise RuntimeError("Missing `SVML` submodule! Run `git submodule " + "update --init` to fix this.") + return True + def pythonlib_dir(): """return path where libpython* is.""" if sys.platform == 'win32': @@ -106,7 +117,7 @@ def win32_checks(deflist): if a == "Intel" or a == "AMD64": deflist.append('FORCE_NO_LONG_DOUBLE_FORMATTING') -def check_math_capabilities(config, moredefs, mathlibs): +def check_math_capabilities(config, ext, moredefs, mathlibs): def check_func(func_name): return config.check_func(func_name, libraries=mathlibs, decl=True, call=True) @@ -153,7 +164,8 @@ def check_funcs(funcs_name): for h in OPTIONAL_HEADERS: if config.check_func("", decl=False, call=False, headers=[h]): - moredefs.append((fname2def(h).replace(".", "_"), 1)) + h = h.replace(".", "_").replace(os.path.sep, "_") + moredefs.append((fname2def(h), 1)) for tup in OPTIONAL_INTRINSICS: headers = None @@ -170,6 +182,19 @@ def check_funcs(funcs_name): for dec, fn in OPTIONAL_FUNCTION_ATTRIBUTES: if config.check_gcc_function_attribute(dec, fn): moredefs.append((fname2def(fn), 1)) + if fn == 'attribute_target_avx512f': + # GH-14787: Work around GCC<8.4 bug when compiling with AVX512 + # support on Windows-based platforms + if (sys.platform in ('win32', 'cygwin') and + config.check_compiler_gcc() and + not config.check_gcc_version_at_least(8, 4)): + ext.extra_compile_args.extend( + ['-ffixed-xmm%s' % n for n in range(16, 32)]) + + for dec, fn, code, header in OPTIONAL_FUNCTION_ATTRIBUTES_WITH_INTRINSICS: + if config.check_gcc_function_attribute_with_intrinsics(dec, fn, code, + header): + moredefs.append((fname2def(fn), 1)) for fn in OPTIONAL_VARIABLE_ATTRIBUTES: if config.check_gcc_variable_attribute(fn): @@ -371,22 +396,24 @@ def check_mathlib(config_cmd): mathlibs = libs break else: - raise EnvironmentError("math library missing; rerun " - "setup.py after setting the " - "MATHLIB env variable") + raise RuntimeError( + "math library missing; rerun setup.py after setting the " + "MATHLIB env variable") return mathlibs def visibility_define(config): """Return the define value to use for NPY_VISIBILITY_HIDDEN (may be empty string).""" - if config.check_compiler_gcc4(): - return '__attribute__((visibility("hidden")))' + hide = '__attribute__((visibility("hidden")))' + if config.check_gcc_function_attribute(hide, 'hideme'): + return hide else: return '' def configuration(parent_package='',top_path=None): from numpy.distutils.misc_util import Configuration, dot_join - from numpy.distutils.system_info import get_info + from numpy.distutils.system_info import (get_info, blas_opt_info, + lapack_opt_info) config = Configuration('core', parent_package, top_path) local_dir = config.local_path @@ -425,7 +452,7 @@ def generate_config_h(ext, build_dir): mathlibs = check_mathlib(config_cmd) moredefs.append(('MATHLIB', ','.join(mathlibs))) - check_math_capabilities(config_cmd, moredefs, mathlibs) + check_math_capabilities(config_cmd, ext, moredefs, mathlibs) moredefs.extend(cocache.check_ieee_macros(config_cmd)[0]) moredefs.extend(cocache.check_complex(config_cmd, mathlibs)[0]) @@ -443,62 +470,68 @@ def generate_config_h(ext, build_dir): # Inline check inline = config_cmd.check_inline() + if can_link_svml(): + moredefs.append(('NPY_CAN_LINK_SVML', 1)) + # Use relaxed stride checking if NPY_RELAXED_STRIDES_CHECKING: moredefs.append(('NPY_RELAXED_STRIDES_CHECKING', 1)) + else: + moredefs.append(('NPY_RELAXED_STRIDES_CHECKING', 0)) # Use bogus stride debug aid when relaxed strides are enabled if NPY_RELAXED_STRIDES_DEBUG: moredefs.append(('NPY_RELAXED_STRIDES_DEBUG', 1)) + else: + moredefs.append(('NPY_RELAXED_STRIDES_DEBUG', 0)) # Get long double representation rep = check_long_double_representation(config_cmd) moredefs.append(('HAVE_LDOUBLE_%s' % rep, 1)) - # Py3K check - if sys.version_info[0] == 3: - moredefs.append(('NPY_PY3K', 1)) + if check_for_right_shift_internal_compiler_error(config_cmd): + moredefs.append('NPY_DO_NOT_OPTIMIZE_LONG_right_shift') + moredefs.append('NPY_DO_NOT_OPTIMIZE_ULONG_right_shift') + moredefs.append('NPY_DO_NOT_OPTIMIZE_LONGLONG_right_shift') + moredefs.append('NPY_DO_NOT_OPTIMIZE_ULONGLONG_right_shift') # Generate the config.h file from moredefs - target_f = open(target, 'w') - for d in moredefs: - if isinstance(d, str): - target_f.write('#define %s\n' % (d)) + with open(target, 'w') as target_f: + for d in moredefs: + if isinstance(d, str): + target_f.write('#define %s\n' % (d)) + else: + target_f.write('#define %s %s\n' % (d[0], d[1])) + + # define inline to our keyword, or nothing + target_f.write('#ifndef __cplusplus\n') + if inline == 'inline': + target_f.write('/* #undef inline */\n') else: - target_f.write('#define %s %s\n' % (d[0], d[1])) - - # define inline to our keyword, or nothing - target_f.write('#ifndef __cplusplus\n') - if inline == 'inline': - target_f.write('/* #undef inline */\n') - else: - target_f.write('#define inline %s\n' % inline) - target_f.write('#endif\n') - - # add the guard to make sure config.h is never included directly, - # but always through npy_config.h - target_f.write(""" -#ifndef _NPY_NPY_CONFIG_H_ -#error config.h should never be included directly, include npy_config.h instead -#endif -""") - - target_f.close() - print('File:', target) - target_f = open(target) - print(target_f.read()) - target_f.close() - print('EOF') + target_f.write('#define inline %s\n' % inline) + target_f.write('#endif\n') + + # add the guard to make sure config.h is never included directly, + # but always through npy_config.h + target_f.write(textwrap.dedent(""" + #ifndef NUMPY_CORE_SRC_COMMON_NPY_CONFIG_H_ + #error config.h should never be included directly, include npy_config.h instead + #endif + """)) + + log.info('File: %s' % target) + with open(target) as target_f: + log.info(target_f.read()) + log.info('EOF') else: mathlibs = [] - target_f = open(target) - for line in target_f: - s = '#define MATHLIB' - if line.startswith(s): - value = line[len(s):].strip() - if value: - mathlibs.extend(value.split(',')) - target_f.close() + with open(target) as target_f: + for line in target_f: + s = '#define MATHLIB' + if line.startswith(s): + value = line[len(s):].strip() + if value: + mathlibs.extend(value.split(',')) # Ugly: this can be called within a library and not an extension, # in which case there is no libraries attributes (and none is @@ -514,9 +547,9 @@ def generate_config_h(ext, build_dir): def generate_numpyconfig_h(ext, build_dir): """Depends on config.h: generate_config_h has to be called before !""" - # put private include directory in build_dir on search path - # allows using code generation in headers headers - config.add_include_dirs(join(build_dir, "src", "private")) + # put common include directory in build_dir on search path + # allows using code generation in headers + config.add_include_dirs(join(build_dir, "src", "common")) config.add_include_dirs(join(build_dir, "src", "npymath")) target = join(build_dir, header_dir, '_numpyconfig.h') @@ -561,27 +594,25 @@ def generate_numpyconfig_h(ext, build_dir): moredefs.append(('NPY_API_VERSION', '0x%.8X' % C_API_VERSION)) # Add moredefs to header - target_f = open(target, 'w') - for d in moredefs: - if isinstance(d, str): - target_f.write('#define %s\n' % (d)) - else: - target_f.write('#define %s %s\n' % (d[0], d[1])) - - # Define __STDC_FORMAT_MACROS - target_f.write(""" -#ifndef __STDC_FORMAT_MACROS -#define __STDC_FORMAT_MACROS 1 -#endif -""") - target_f.close() + with open(target, 'w') as target_f: + for d in moredefs: + if isinstance(d, str): + target_f.write('#define %s\n' % (d)) + else: + target_f.write('#define %s %s\n' % (d[0], d[1])) + + # Define __STDC_FORMAT_MACROS + target_f.write(textwrap.dedent(""" + #ifndef __STDC_FORMAT_MACROS + #define __STDC_FORMAT_MACROS 1 + #endif + """)) # Dump the numpyconfig.h header to stdout - print('File: %s' % target) - target_f = open(target) - print(target_f.read()) - target_f.close() - print('EOF') + log.info('File: %s' % target) + with open(target) as target_f: + log.info(target_f.read()) + log.info('EOF') config.add_data_files((header_dir, target)) return target @@ -603,15 +634,16 @@ def generate_api(ext, build_dir): generate_numpy_api = generate_api_func('generate_numpy_api') generate_ufunc_api = generate_api_func('generate_ufunc_api') - config.add_include_dirs(join(local_dir, "src", "private")) + config.add_include_dirs(join(local_dir, "src", "common")) config.add_include_dirs(join(local_dir, "src")) config.add_include_dirs(join(local_dir)) - config.add_data_files('include/numpy/*.h') + config.add_data_dir('include/numpy') config.add_include_dirs(join('src', 'npymath')) config.add_include_dirs(join('src', 'multiarray')) config.add_include_dirs(join('src', 'umath')) config.add_include_dirs(join('src', 'npysort')) + config.add_include_dirs(join('src', '_simd')) config.add_define_macros([("NPY_INTERNAL_BUILD", "1")]) # this macro indicates that Numpy build is in process config.add_define_macros([("HAVE_NPY_CONFIG_H", "1")]) @@ -629,23 +661,6 @@ def generate_api(ext, build_dir): join(codegen_dir, 'genapi.py'), ] - ####################################################################### - # dummy module # - ####################################################################### - - # npymath needs the config.h and numpyconfig.h files to be generated, but - # build_clib cannot handle generate_config_h and generate_numpyconfig_h - # (don't ask). Because clib are generated before extensions, we have to - # explicitly add an extension which has generate_config_h and - # generate_numpyconfig_h as sources *before* adding npymath. - - config.add_extension('_dummy', - sources=[join('src', 'dummymodule.c'), - generate_config_h, - generate_numpyconfig_h, - generate_numpy_api] - ) - ####################################################################### # npymath library # ####################################################################### @@ -657,13 +672,38 @@ def get_mathlib_info(*args): # but we cannot use add_installed_pkg_config here either, so we only # update the substitution dictionary during npymath build config_cmd = config.get_config_cmd() - # Check that the toolchain works, to fail early if it doesn't # (avoid late errors with MATHLIB which are confusing if the # compiler does not work). - st = config_cmd.try_link('int main(void) { return 0;}') - if not st: - raise RuntimeError("Broken toolchain: cannot link a simple C program") + for lang, test_code, note in ( + ('c', 'int main(void) { return 0;}', ''), + ('c++', ( + 'int main(void)' + '{ auto x = 0.0; return static_cast<int>(x); }' + ), ( + 'note: A compiler with support for C++11 language ' + 'features is required.' + ) + ), + ): + is_cpp = lang == 'c++' + if is_cpp: + # this a workround to get rid of invalid c++ flags + # without doing big changes to config. + # c tested first, compiler should be here + bk_c = config_cmd.compiler + config_cmd.compiler = bk_c.cxx_compiler() + st = config_cmd.try_link(test_code, lang=lang) + if not st: + # rerun the failing command in verbose mode + config_cmd.compiler.verbose = True + config_cmd.try_link(test_code, lang=lang) + raise RuntimeError( + f"Broken toolchain: cannot link a simple {lang.upper()} " + f"program. {note}" + ) + if is_cpp: + config_cmd.compiler = bk_c mlibs = check_mathlib(config_cmd) posix_mlib = ' '.join(['-l%s' % l for l in mlibs]) @@ -677,15 +717,25 @@ def get_mathlib_info(*args): join('src', 'npymath', 'npy_math_complex.c.src'), join('src', 'npymath', 'halffloat.c') ] - - # Must be true for CRT compilers but not MinGW/cygwin. See gh-9977. - is_msvc = platform.system() == 'Windows' + + def gl_if_msvc(build_cmd): + """ Add flag if we are using MSVC compiler + + We can't see this in our scope, because we have not initialized the + distutils build command, so use this deferred calculation to run when + we are building the library. + """ + if build_cmd.compiler.compiler_type == 'msvc': + # explicitly disable whole-program optimization + return ['/GL-'] + return [] + config.add_installed_library('npymath', sources=npymath_sources + [get_mathlib_info], install_dir='lib', build_info={ 'include_dirs' : [], # empty list required for creating npy_math_internal.h - 'extra_compiler_args' : (['/GL-'] if is_msvc else []), + 'extra_compiler_args': [gl_if_msvc], }) config.add_npy_pkg_config("npymath.ini.in", "lib/npy-pkg-config", subst_dict) @@ -693,66 +743,123 @@ def get_mathlib_info(*args): subst_dict) ####################################################################### - # npysort library # + # multiarray_tests module # ####################################################################### - # This library is created for the build but it is not installed - npysort_sources = [join('src', 'npysort', 'quicksort.c.src'), - join('src', 'npysort', 'mergesort.c.src'), - join('src', 'npysort', 'heapsort.c.src'), - join('src', 'private', 'npy_partition.h.src'), - join('src', 'npysort', 'selection.c.src'), - join('src', 'private', 'npy_binsearch.h.src'), - join('src', 'npysort', 'binsearch.c.src'), - ] - config.add_library('npysort', - sources=npysort_sources, - include_dirs=[]) + config.add_extension('_multiarray_tests', + sources=[join('src', 'multiarray', '_multiarray_tests.c.src'), + join('src', 'common', 'mem_overlap.c'), + join('src', 'common', 'npy_argparse.c'), + join('src', 'common', 'npy_hashtable.c')], + depends=[join('src', 'common', 'mem_overlap.h'), + join('src', 'common', 'npy_argparse.h'), + join('src', 'common', 'npy_hashtable.h'), + join('src', 'common', 'npy_extint128.h')], + libraries=['npymath']) ####################################################################### - # multiarray module # + # _multiarray_umath module - common part # + ####################################################################### + + common_deps = [ + join('src', 'common', 'dlpack', 'dlpack.h'), + join('src', 'common', 'array_assign.h'), + join('src', 'common', 'binop_override.h'), + join('src', 'common', 'cblasfuncs.h'), + join('src', 'common', 'lowlevel_strided_loops.h'), + join('src', 'common', 'mem_overlap.h'), + join('src', 'common', 'npy_argparse.h'), + join('src', 'common', 'npy_cblas.h'), + join('src', 'common', 'npy_config.h'), + join('src', 'common', 'npy_ctypes.h'), + join('src', 'common', 'npy_dlpack.h'), + join('src', 'common', 'npy_extint128.h'), + join('src', 'common', 'npy_import.h'), + join('src', 'common', 'npy_hashtable.h'), + join('src', 'common', 'npy_longdouble.h'), + join('src', 'common', 'npy_svml.h'), + join('src', 'common', 'templ_common.h.src'), + join('src', 'common', 'ucsnarrow.h'), + join('src', 'common', 'ufunc_override.h'), + join('src', 'common', 'umathmodule.h'), + join('src', 'common', 'numpyos.h'), + join('src', 'common', 'npy_cpu_dispatch.h'), + join('src', 'common', 'simd', 'simd.h'), + ] + + common_src = [ + join('src', 'common', 'array_assign.c'), + join('src', 'common', 'mem_overlap.c'), + join('src', 'common', 'npy_argparse.c'), + join('src', 'common', 'npy_hashtable.c'), + join('src', 'common', 'npy_longdouble.c'), + join('src', 'common', 'templ_common.h.src'), + join('src', 'common', 'ucsnarrow.c'), + join('src', 'common', 'ufunc_override.c'), + join('src', 'common', 'numpyos.c'), + join('src', 'common', 'npy_cpu_features.c.src'), + ] + + if os.environ.get('NPY_USE_BLAS_ILP64', "0") != "0": + blas_info = get_info('blas_ilp64_opt', 2) + else: + blas_info = get_info('blas_opt', 0) + + have_blas = blas_info and ('HAVE_CBLAS', None) in blas_info.get('define_macros', []) + + if have_blas: + extra_info = blas_info + # These files are also in MANIFEST.in so that they are always in + # the source distribution independently of HAVE_CBLAS. + common_src.extend([join('src', 'common', 'cblasfuncs.c'), + join('src', 'common', 'python_xerbla.c'), + ]) + else: + extra_info = {} + + ####################################################################### + # _multiarray_umath module - multiarray part # ####################################################################### multiarray_deps = [ + join('src', 'multiarray', 'abstractdtypes.h'), join('src', 'multiarray', 'arrayobject.h'), join('src', 'multiarray', 'arraytypes.h'), - join('src', 'multiarray', 'array_assign.h'), - join('src', 'multiarray', 'buffer.h'), + join('src', 'multiarray', 'arrayfunction_override.h'), + join('src', 'multiarray', 'array_coercion.h'), + join('src', 'multiarray', 'array_method.h'), + join('src', 'multiarray', 'npy_buffer.h'), join('src', 'multiarray', 'calculation.h'), - join('src', 'multiarray', 'cblasfuncs.h'), join('src', 'multiarray', 'common.h'), + join('src', 'multiarray', 'common_dtype.h'), join('src', 'multiarray', 'convert_datatype.h'), join('src', 'multiarray', 'convert.h'), join('src', 'multiarray', 'conversion_utils.h'), join('src', 'multiarray', 'ctors.h'), join('src', 'multiarray', 'descriptor.h'), + join('src', 'multiarray', 'dtypemeta.h'), + join('src', 'multiarray', 'dtype_transfer.h'), join('src', 'multiarray', 'dragon4.h'), + join('src', 'multiarray', 'einsum_debug.h'), + join('src', 'multiarray', 'einsum_sumprod.h'), + join('src', 'multiarray', 'experimental_public_dtype_api.h'), join('src', 'multiarray', 'getset.h'), join('src', 'multiarray', 'hashdescr.h'), join('src', 'multiarray', 'iterators.h'), + join('src', 'multiarray', 'legacy_dtype_implementation.h'), join('src', 'multiarray', 'mapping.h'), join('src', 'multiarray', 'methods.h'), join('src', 'multiarray', 'multiarraymodule.h'), join('src', 'multiarray', 'nditer_impl.h'), join('src', 'multiarray', 'number.h'), - join('src', 'multiarray', 'numpyos.h'), join('src', 'multiarray', 'refcount.h'), join('src', 'multiarray', 'scalartypes.h'), join('src', 'multiarray', 'sequence.h'), join('src', 'multiarray', 'shape.h'), join('src', 'multiarray', 'strfuncs.h'), join('src', 'multiarray', 'typeinfo.h'), - join('src', 'multiarray', 'ucsnarrow.h'), join('src', 'multiarray', 'usertypes.h'), join('src', 'multiarray', 'vdot.h'), - join('src', 'private', 'npy_config.h'), - join('src', 'private', 'templ_common.h.src'), - join('src', 'private', 'lowlevel_strided_loops.h'), - join('src', 'private', 'mem_overlap.h'), - join('src', 'private', 'npy_longdouble.h'), - join('src', 'private', 'ufunc_override.h'), - join('src', 'private', 'binop_override.h'), - join('src', 'private', 'npy_extint128.h'), join('include', 'numpy', 'arrayobject.h'), join('include', 'numpy', '_neighborhood_iterator_imp.h'), join('include', 'numpy', 'npy_endian.h'), @@ -772,19 +879,23 @@ def get_mathlib_info(*args): join('include', 'numpy', 'npy_1_7_deprecated_api.h'), # add library sources as distuils does not consider libraries # dependencies - ] + npysort_sources + npymath_sources + ] + npymath_sources multiarray_src = [ + join('src', 'multiarray', 'abstractdtypes.c'), join('src', 'multiarray', 'alloc.c'), join('src', 'multiarray', 'arrayobject.c'), join('src', 'multiarray', 'arraytypes.c.src'), - join('src', 'multiarray', 'array_assign.c'), + join('src', 'multiarray', 'array_coercion.c'), + join('src', 'multiarray', 'array_method.c'), join('src', 'multiarray', 'array_assign_scalar.c'), join('src', 'multiarray', 'array_assign_array.c'), + join('src', 'multiarray', 'arrayfunction_override.c'), join('src', 'multiarray', 'buffer.c'), join('src', 'multiarray', 'calculation.c'), join('src', 'multiarray', 'compiled_base.c'), join('src', 'multiarray', 'common.c'), + join('src', 'multiarray', 'common_dtype.c'), join('src', 'multiarray', 'convert.c'), join('src', 'multiarray', 'convert_datatype.c'), join('src', 'multiarray', 'conversion_utils.c'), @@ -794,14 +905,19 @@ def get_mathlib_info(*args): join('src', 'multiarray', 'datetime_busday.c'), join('src', 'multiarray', 'datetime_busdaycal.c'), join('src', 'multiarray', 'descriptor.c'), + join('src', 'multiarray', 'dlpack.c'), + join('src', 'multiarray', 'dtypemeta.c'), join('src', 'multiarray', 'dragon4.c'), join('src', 'multiarray', 'dtype_transfer.c'), join('src', 'multiarray', 'einsum.c.src'), + join('src', 'multiarray', 'einsum_sumprod.c.src'), + join('src', 'multiarray', 'experimental_public_dtype_api.c'), join('src', 'multiarray', 'flagsobject.c'), join('src', 'multiarray', 'getset.c'), join('src', 'multiarray', 'hashdescr.c'), join('src', 'multiarray', 'item_selection.c'), join('src', 'multiarray', 'iterators.c'), + join('src', 'multiarray', 'legacy_dtype_implementation.c'), join('src', 'multiarray', 'lowlevel_strided_loops.c.src'), join('src', 'multiarray', 'mapping.c'), join('src', 'multiarray', 'methods.c'), @@ -811,7 +927,6 @@ def get_mathlib_info(*args): join('src', 'multiarray', 'nditer_constr.c'), join('src', 'multiarray', 'nditer_pywrap.c'), join('src', 'multiarray', 'number.c'), - join('src', 'multiarray', 'numpyos.c'), join('src', 'multiarray', 'refcount.c'), join('src', 'multiarray', 'sequence.c'), join('src', 'multiarray', 'shape.c'), @@ -821,40 +936,21 @@ def get_mathlib_info(*args): join('src', 'multiarray', 'temp_elide.c'), join('src', 'multiarray', 'typeinfo.c'), join('src', 'multiarray', 'usertypes.c'), - join('src', 'multiarray', 'ucsnarrow.c'), join('src', 'multiarray', 'vdot.c'), - join('src', 'private', 'templ_common.h.src'), - join('src', 'private', 'mem_overlap.c'), - join('src', 'private', 'npy_longdouble.c'), - join('src', 'private', 'ufunc_override.c'), + join('src', 'common', 'npy_sort.h.src'), + join('src', 'npysort', 'quicksort.c.src'), + join('src', 'npysort', 'mergesort.c.src'), + join('src', 'npysort', 'timsort.c.src'), + join('src', 'npysort', 'heapsort.c.src'), + join('src', 'npysort', 'radixsort.cpp'), + join('src', 'common', 'npy_partition.h.src'), + join('src', 'npysort', 'selection.c.src'), + join('src', 'common', 'npy_binsearch.h.src'), + join('src', 'npysort', 'binsearch.c.src'), ] - blas_info = get_info('blas_opt', 0) - if blas_info and ('HAVE_CBLAS', None) in blas_info.get('define_macros', []): - extra_info = blas_info - # These files are also in MANIFEST.in so that they are always in - # the source distribution independently of HAVE_CBLAS. - multiarray_src.extend([join('src', 'multiarray', 'cblasfuncs.c'), - join('src', 'multiarray', 'python_xerbla.c'), - ]) - if uses_accelerate_framework(blas_info): - multiarray_src.extend(get_sgemv_fix()) - else: - extra_info = {} - - config.add_extension('multiarray', - sources=multiarray_src + - [generate_config_h, - generate_numpyconfig_h, - generate_numpy_api, - join(codegen_dir, 'generate_numpy_api.py'), - join('*.py')], - depends=deps + multiarray_deps, - libraries=['npymath', 'npysort'], - extra_info=extra_info) - ####################################################################### - # umath module # + # _multiarray_umath module - umath part # ####################################################################### def generate_umath_c(ext, build_dir): @@ -864,10 +960,9 @@ def generate_umath_c(ext, build_dir): os.makedirs(dir) script = generate_umath_py if newer(script, target): - f = open(target, 'w') - f.write(generate_umath.make_code(generate_umath.defdict, - generate_umath.__file__)) - f.close() + with open(target, 'w') as f: + f.write(generate_umath.make_code(generate_umath.defdict, + generate_umath.__file__)) return [] umath_src = [ @@ -876,48 +971,79 @@ def generate_umath_c(ext, build_dir): join('src', 'umath', 'funcs.inc.src'), join('src', 'umath', 'simd.inc.src'), join('src', 'umath', 'loops.h.src'), + join('src', 'umath', 'loops_utils.h.src'), join('src', 'umath', 'loops.c.src'), + join('src', 'umath', 'loops_unary_fp.dispatch.c.src'), + join('src', 'umath', 'loops_arithm_fp.dispatch.c.src'), + join('src', 'umath', 'loops_arithmetic.dispatch.c.src'), + join('src', 'umath', 'loops_trigonometric.dispatch.c.src'), + join('src', 'umath', 'loops_umath_fp.dispatch.c.src'), + join('src', 'umath', 'loops_exponent_log.dispatch.c.src'), + join('src', 'umath', 'matmul.h.src'), + join('src', 'umath', 'matmul.c.src'), + join('src', 'umath', 'clip.h'), + join('src', 'umath', 'clip.cpp'), + join('src', 'umath', 'dispatching.c'), + join('src', 'umath', 'legacy_array_method.c'), join('src', 'umath', 'ufunc_object.c'), join('src', 'umath', 'extobj.c'), - join('src', 'umath', 'cpuid.c'), join('src', 'umath', 'scalarmath.c.src'), join('src', 'umath', 'ufunc_type_resolution.c'), join('src', 'umath', 'override.c'), - join('src', 'private', 'mem_overlap.c'), - join('src', 'private', 'npy_longdouble.c'), - join('src', 'private', 'ufunc_override.c')] + # For testing. Eventually, should use public API and be separate: + join('src', 'umath', '_scaled_float_dtype.c'), + ] umath_deps = [ generate_umath_py, join('include', 'numpy', 'npy_math.h'), join('include', 'numpy', 'halffloat.h'), join('src', 'multiarray', 'common.h'), - join('src', 'private', 'templ_common.h.src'), + join('src', 'multiarray', 'number.h'), + join('src', 'common', 'templ_common.h.src'), join('src', 'umath', 'simd.inc.src'), join('src', 'umath', 'override.h'), join(codegen_dir, 'generate_ufunc_api.py'), - join('src', 'private', 'lowlevel_strided_loops.h'), - join('src', 'private', 'mem_overlap.h'), - join('src', 'private', 'npy_longdouble.h'), - join('src', 'private', 'ufunc_override.h'), - join('src', 'private', 'binop_override.h')] + npymath_sources - - config.add_extension('umath', - sources=umath_src + + ] + + svml_path = join('numpy', 'core', 'src', 'umath', 'svml') + svml_objs = [] + if can_link_svml() and check_svml_submodule(svml_path): + svml_objs = glob.glob(svml_path + '/**/*.s', recursive=True) + + config.add_extension('_multiarray_umath', + # Forcing C language even though we have C++ sources. + # It forces the C linker and don't link C++ runtime. + language = 'c', + sources=multiarray_src + umath_src + + common_src + [generate_config_h, - generate_numpyconfig_h, - generate_umath_c, - generate_ufunc_api], - depends=deps + umath_deps, + generate_numpyconfig_h, + generate_numpy_api, + join(codegen_dir, 'generate_numpy_api.py'), + join('*.py'), + generate_umath_c, + generate_ufunc_api, + ], + depends=deps + multiarray_deps + umath_deps + + common_deps, libraries=['npymath'], - ) + extra_objects=svml_objs, + extra_info=extra_info, + extra_cxx_compile_args=['-std=c++11', + '-D__STDC_VERSION__=0', + '-fno-exceptions', + '-fno-rtti']) ####################################################################### # umath_tests module # ####################################################################### - config.add_extension('_umath_tests', - sources=[join('src', 'umath', '_umath_tests.c.src')]) + config.add_extension('_umath_tests', sources=[ + join('src', 'umath', '_umath_tests.c.src'), + join('src', 'umath', '_umath_tests.dispatch.c'), + join('src', 'common', 'npy_cpu_features.c.src'), + ]) ####################################################################### # custom rational dtype module # @@ -933,16 +1059,6 @@ def generate_umath_c(ext, build_dir): config.add_extension('_struct_ufunc_tests', sources=[join('src', 'umath', '_struct_ufunc_tests.c.src')]) - ####################################################################### - # multiarray_tests module # - ####################################################################### - - config.add_extension('_multiarray_tests', - sources=[join('src', 'multiarray', '_multiarray_tests.c.src'), - join('src', 'private', 'mem_overlap.c')], - depends=[join('src', 'private', 'mem_overlap.h'), - join('src', 'private', 'npy_extint128.h')], - libraries=['npymath']) ####################################################################### # operand_flag_tests module # @@ -951,8 +1067,32 @@ def generate_umath_c(ext, build_dir): config.add_extension('_operand_flag_tests', sources=[join('src', 'umath', '_operand_flag_tests.c.src')]) - config.add_data_dir('tests') + ####################################################################### + # SIMD module # + ####################################################################### + + config.add_extension('_simd', sources=[ + join('src', 'common', 'npy_cpu_features.c.src'), + join('src', '_simd', '_simd.c'), + join('src', '_simd', '_simd_inc.h.src'), + join('src', '_simd', '_simd_data.inc.src'), + join('src', '_simd', '_simd.dispatch.c.src'), + ], depends=[ + join('src', 'common', 'npy_cpu_dispatch.h'), + join('src', 'common', 'simd', 'simd.h'), + join('src', '_simd', '_simd.h'), + join('src', '_simd', '_simd_inc.h.src'), + join('src', '_simd', '_simd_data.inc.src'), + join('src', '_simd', '_simd_arg.inc'), + join('src', '_simd', '_simd_convert.inc'), + join('src', '_simd', '_simd_easyintrin.inc'), + join('src', '_simd', '_simd_vector.inc'), + ]) + + config.add_subpackage('tests') config.add_data_dir('tests/data') + config.add_data_dir('tests/examples') + config.add_data_files('*.pyi') config.make_svn_version_py() diff --git a/numpy/core/setup_common.py b/numpy/core/setup_common.py index 70a43046c3e4..70e8fc89705a 100644 --- a/numpy/core/setup_common.py +++ b/numpy/core/setup_common.py @@ -1,10 +1,8 @@ -from __future__ import division, absolute_import, print_function - # Code common to build tools import sys import warnings import copy -import binascii +import textwrap from numpy.distutils.misc_util import mingw32 @@ -14,7 +12,7 @@ #------------------- # How to change C_API_VERSION ? # - increase C_API_VERSION value -# - record the hash for the new C API with the script cversions.py +# - record the hash for the new C API with the cversions.py script # and add the hash to cversions.txt # The hash values are used to remind developers when the C API number was not # updated - generates a MismatchCAPIWarning warning which is turned into an @@ -40,8 +38,13 @@ # 0x0000000a - 1.12.x # 0x0000000b - 1.13.x # 0x0000000c - 1.14.x -# 0x0000000d - 1.15.x -C_API_VERSION = 0x0000000d +# 0x0000000c - 1.15.x +# 0x0000000d - 1.16.x +# 0x0000000d - 1.19.x +# 0x0000000e - 1.20.x +# 0x0000000e - 1.21.x +# 0x0000000f - 1.22.x +C_API_VERSION = 0x0000000f class MismatchCAPIWarning(Warning): pass @@ -50,7 +53,7 @@ def is_released(config): """Return True if a released version of numpy is detected.""" from distutils.version import LooseVersion - v = config.get_version('../version.py') + v = config.get_version('../_version.py') if v is None: raise ValueError("Could not get version") pv = LooseVersion(vstring=v).version @@ -80,21 +83,20 @@ def get_api_versions(apiversion, codegen_dir): return curapi_hash, apis_hash[apiversion] def check_api_version(apiversion, codegen_dir): - """Emits a MismacthCAPIWarning if the C API version needs updating.""" + """Emits a MismatchCAPIWarning if the C API version needs updating.""" curapi_hash, api_hash = get_api_versions(apiversion, codegen_dir) # If different hash, it means that the api .txt files in # codegen_dir have been updated without the API version being # updated. Any modification in those .txt files should be reflected # in the api and eventually abi versions. - # To compute the checksum of the current API, use - # code_generators/cversions.py script + # To compute the checksum of the current API, use numpy/core/cversions.py if not curapi_hash == api_hash: msg = ("API mismatch detected, the C API version " "numbers have to be updated. Current C api version is %d, " - "with checksum %s, but recorded checksum for C API version %d in " - "codegen_dir/cversions.txt is %s. If functions were added in the " - "C API, you have to update C_API_VERSION in %s." + "with checksum %s, but recorded checksum for C API version %d " + "in core/codegen_dir/cversions.txt is %s. If functions were " + "added in the C API, you have to update C_API_VERSION in %s." ) warnings.warn(msg % (apiversion, curapi_hash, apiversion, api_hash, __file__), @@ -110,16 +112,18 @@ def check_api_version(apiversion, codegen_dir): "rint", "trunc", "exp2", "log2", "hypot", "atan2", "pow", "copysign", "nextafter", "ftello", "fseeko", "strtoll", "strtoull", "cbrt", "strtold_l", "fallocate", - "backtrace"] + "backtrace", "madvise"] OPTIONAL_HEADERS = [ # sse headers only enabled automatically on amd64/x32 builds "xmmintrin.h", # SSE "emmintrin.h", # SSE2 + "immintrin.h", # AVX "features.h", # for glibc version linux "xlocale.h", # see GH#8367 "dlfcn.h", # dladdr + "sys/mman.h", #madvise ] # optional gcc compiler builtins and their call arguments and optional a @@ -132,9 +136,6 @@ def check_api_version(apiversion, codegen_dir): ("__builtin_bswap64", '5u'), ("__builtin_expect", '5, 0'), ("__builtin_mul_overflow", '5, 5, (int*)5'), - # broken on OSX 10.11, make sure its not optimized away - ("volatile int r = __builtin_cpu_supports", '"sse"', - "stdio.h", "__BUILTIN_CPU_SUPPORTS"), # MMX only needed for icc, but some clangs don't have it ("_m_from_int64", '0', "emmintrin.h"), ("_mm_load_ps", '(float*)0', "xmmintrin.h"), # SSE @@ -147,6 +148,12 @@ def check_api_version(apiversion, codegen_dir): "stdio.h", "LINK_AVX"), ("__asm__ volatile", '"vpand %ymm1, %ymm2, %ymm3"', "stdio.h", "LINK_AVX2"), + ("__asm__ volatile", '"vpaddd %zmm1, %zmm2, %zmm3"', + "stdio.h", "LINK_AVX512F"), + ("__asm__ volatile", '"vfpclasspd $0x40, %zmm15, %k6\\n"\ + "vmovdqu8 %xmm0, %xmm1\\n"\ + "vpbroadcastmb2q %k0, %xmm0\\n"', + "stdio.h", "LINK_AVX512_SKX"), ("__asm__ volatile", '"xgetbv"', "stdio.h", "XGETBV"), ] @@ -163,6 +170,36 @@ def check_api_version(apiversion, codegen_dir): 'attribute_target_avx'), ('__attribute__((target ("avx2")))', 'attribute_target_avx2'), + ('__attribute__((target ("avx512f")))', + 'attribute_target_avx512f'), + ('__attribute__((target ("avx512f,avx512dq,avx512bw,avx512vl,avx512cd")))', + 'attribute_target_avx512_skx'), + ] + +# function attributes with intrinsics +# To ensure your compiler can compile avx intrinsics with just the attributes +# gcc 4.8.4 support attributes but not with intrisics +# tested via "#include<%s> int %s %s(void *){code; return 0;};" % (header, attribute, name, code) +# function name will be converted to HAVE_<upper-case-name> preprocessor macro +# The _mm512_castps_si512 instruction is specific check for AVX-512F support +# in gcc-4.9 which is missing a subset of intrinsics. See +# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61878 +OPTIONAL_FUNCTION_ATTRIBUTES_WITH_INTRINSICS = [('__attribute__((target("avx2,fma")))', + 'attribute_target_avx2_with_intrinsics', + '__m256 temp = _mm256_set1_ps(1.0); temp = \ + _mm256_fmadd_ps(temp, temp, temp)', + 'immintrin.h'), + ('__attribute__((target("avx512f")))', + 'attribute_target_avx512f_with_intrinsics', + '__m512i temp = _mm512_castps_si512(_mm512_set1_ps(1.0))', + 'immintrin.h'), + ('__attribute__((target ("avx512f,avx512dq,avx512bw,avx512vl,avx512cd")))', + 'attribute_target_avx512_skx_with_intrinsics', + '__mmask8 temp = _mm512_fpclass_pd_mask(_mm512_set1_pd(1.0), 0x01);\ + __m512i unused_temp = \ + _mm512_castps_si512(_mm512_set1_ps(1.0));\ + _mm_mask_storeu_epi8(NULL, 0xFF, _mm_broadcastmb_epi64(temp))', + 'immintrin.h'), ] # variable attributes tested via "int %s a" % attribute @@ -221,9 +258,9 @@ def check_long_double_representation(cmd): # Disable multi-file interprocedural optimization in the Intel compiler on Linux # which generates intermediary object files and prevents checking the # float representation. - elif (sys.platform != "win32" - and cmd.compiler.compiler_type.startswith('intel') - and '-ipo' in cmd.compiler.cc_exe): + elif (sys.platform != "win32" + and cmd.compiler.compiler_type.startswith('intel') + and '-ipo' in cmd.compiler.cc_exe): newcompiler = cmd.compiler.cc_exe.replace(' -ipo', '') cmd.compiler.set_executables( compiler=newcompiler, @@ -241,8 +278,9 @@ def check_long_double_representation(cmd): except ValueError: # try linking to support CC="gcc -flto" or icc -ipo # struct needs to be volatile so it isn't optimized away + # additionally "clang -flto" requires the foo struct to be used body = body.replace('struct', 'volatile struct') - body += "int main(void) { return 0; }\n" + body += "int main(void) { return foo.before[0]; }\n" src, obj = cmd._compile(body, None, None, 'c') cmd.temp_files.append("_configtest") cmd.compiler.link_executable([obj], "_configtest") @@ -282,43 +320,20 @@ def pyod(filename): out : seq list of lines of od output - Note - ---- + Notes + ----- We only implement enough to get the necessary information for long double representation, this is not intended as a compatible replacement for od. """ - def _pyod2(): - out = [] + out = [] + with open(filename, 'rb') as fid: + yo2 = [oct(o)[2:] for o in fid.read()] + for i in range(0, len(yo2), 16): + line = ['%07d' % int(oct(i)[2:])] + line.extend(['%03d' % int(c) for c in yo2[i:i+16]]) + out.append(" ".join(line)) + return out - fid = open(filename, 'rb') - try: - yo = [int(oct(int(binascii.b2a_hex(o), 16))) for o in fid.read()] - for i in range(0, len(yo), 16): - line = ['%07d' % int(oct(i))] - line.extend(['%03d' % c for c in yo[i:i+16]]) - out.append(" ".join(line)) - return out - finally: - fid.close() - - def _pyod3(): - out = [] - - fid = open(filename, 'rb') - try: - yo2 = [oct(o)[2:] for o in fid.read()] - for i in range(0, len(yo2), 16): - line = ['%07d' % int(oct(i)[2:])] - line.extend(['%03d' % int(c) for c in yo2[i:i+16]]) - out.append(" ".join(line)) - return out - finally: - fid.close() - - if sys.version_info[0] < 3: - return _pyod2() - else: - return _pyod3() _BEFORE_SEQ = ['000', '000', '000', '000', '000', '000', '000', '000', '001', '043', '105', '147', '211', '253', '315', '357'] @@ -397,3 +412,41 @@ def long_double_representation(lines): else: # We never detected the after_sequence raise ValueError("Could not lock sequences (%s)" % saw) + + +def check_for_right_shift_internal_compiler_error(cmd): + """ + On our arm CI, this fails with an internal compilation error + + The failure looks like the following, and can be reproduced on ARM64 GCC 5.4: + + <source>: In function 'right_shift': + <source>:4:20: internal compiler error: in expand_shift_1, at expmed.c:2349 + ip1[i] = ip1[i] >> in2; + ^ + Please submit a full bug report, + with preprocessed source if appropriate. + See <http://gcc.gnu.org/bugs.html> for instructions. + Compiler returned: 1 + + This function returns True if this compiler bug is present, and we need to + turn off optimization for the function + """ + cmd._check_compiler() + has_optimize = cmd.try_compile(textwrap.dedent("""\ + __attribute__((optimize("O3"))) void right_shift() {} + """), None, None) + if not has_optimize: + return False + + no_err = cmd.try_compile(textwrap.dedent("""\ + typedef long the_type; /* fails also for unsigned and long long */ + __attribute__((optimize("O3"))) void right_shift(the_type in2, the_type *ip1, int n) { + for (int i = 0; i < n; i++) { + if (in2 < (the_type)sizeof(the_type) * 8) { + ip1[i] = ip1[i] >> in2; + } + } + } + """), None, None) + return not no_err diff --git a/numpy/core/shape_base.py b/numpy/core/shape_base.py index 319c25088102..a81a04f7ff0e 100644 --- a/numpy/core/shape_base.py +++ b/numpy/core/shape_base.py @@ -1,13 +1,26 @@ -from __future__ import division, absolute_import, print_function - __all__ = ['atleast_1d', 'atleast_2d', 'atleast_3d', 'block', 'hstack', 'stack', 'vstack'] +import functools +import itertools +import operator +import warnings from . import numeric as _nx -from .numeric import array, asanyarray, newaxis -from .multiarray import normalize_axis_index +from . import overrides +from .multiarray import array, asanyarray, normalize_axis_index +from . import fromnumeric as _from_nx + + +array_function_dispatch = functools.partial( + overrides.array_function_dispatch, module='numpy') + + +def _atleast_1d_dispatcher(*arys): + return arys + +@array_function_dispatch(_atleast_1d_dispatcher) def atleast_1d(*arys): """ Convert inputs to arrays with at least one dimension. @@ -33,13 +46,13 @@ def atleast_1d(*arys): Examples -------- >>> np.atleast_1d(1.0) - array([ 1.]) + array([1.]) >>> x = np.arange(9.0).reshape(3,3) >>> np.atleast_1d(x) - array([[ 0., 1., 2.], - [ 3., 4., 5.], - [ 6., 7., 8.]]) + array([[0., 1., 2.], + [3., 4., 5.], + [6., 7., 8.]]) >>> np.atleast_1d(x) is x True @@ -60,6 +73,12 @@ def atleast_1d(*arys): else: return res + +def _atleast_2d_dispatcher(*arys): + return arys + + +@array_function_dispatch(_atleast_2d_dispatcher) def atleast_2d(*arys): """ View inputs as arrays with at least two dimensions. @@ -85,11 +104,11 @@ def atleast_2d(*arys): Examples -------- >>> np.atleast_2d(3.0) - array([[ 3.]]) + array([[3.]]) >>> x = np.arange(3.0) >>> np.atleast_2d(x) - array([[ 0., 1., 2.]]) + array([[0., 1., 2.]]) >>> np.atleast_2d(x).base is x True @@ -103,7 +122,7 @@ def atleast_2d(*arys): if ary.ndim == 0: result = ary.reshape(1, 1) elif ary.ndim == 1: - result = ary[newaxis,:] + result = ary[_nx.newaxis, :] else: result = ary res.append(result) @@ -112,6 +131,12 @@ def atleast_2d(*arys): else: return res + +def _atleast_3d_dispatcher(*arys): + return arys + + +@array_function_dispatch(_atleast_3d_dispatcher) def atleast_3d(*arys): """ View inputs as arrays with at least three dimensions. @@ -139,7 +164,7 @@ def atleast_3d(*arys): Examples -------- >>> np.atleast_3d(3.0) - array([[[ 3.]]]) + array([[[3.]]]) >>> x = np.arange(3.0) >>> np.atleast_3d(x).shape @@ -152,7 +177,7 @@ def atleast_3d(*arys): True >>> for arr in np.atleast_3d([1, 2], [[1, 2]], [[[1, 2]]]): - ... print(arr, arr.shape) + ... print(arr, arr.shape) # doctest: +SKIP ... [[[1] [2]]] (1, 2, 1) @@ -167,9 +192,9 @@ def atleast_3d(*arys): if ary.ndim == 0: result = ary.reshape(1, 1, 1) elif ary.ndim == 1: - result = ary[newaxis,:, newaxis] + result = ary[_nx.newaxis, :, _nx.newaxis] elif ary.ndim == 2: - result = ary[:,:, newaxis] + result = ary[:, :, _nx.newaxis] else: result = ary res.append(result) @@ -179,6 +204,22 @@ def atleast_3d(*arys): return res +def _arrays_for_stack_dispatcher(arrays, stacklevel=4): + if not hasattr(arrays, '__getitem__') and hasattr(arrays, '__iter__'): + warnings.warn('arrays to stack must be passed as a "sequence" type ' + 'such as list or tuple. Support for non-sequence ' + 'iterables such as generators is deprecated as of ' + 'NumPy 1.16 and will raise an error in the future.', + FutureWarning, stacklevel=stacklevel) + return () + return arrays + + +def _vhstack_dispatcher(tup): + return _arrays_for_stack_dispatcher(tup) + + +@array_function_dispatch(_vhstack_dispatcher) def vstack(tup): """ Stack arrays in sequence vertically (row wise). @@ -205,34 +246,43 @@ def vstack(tup): See Also -------- + concatenate : Join a sequence of arrays along an existing axis. stack : Join a sequence of arrays along a new axis. + block : Assemble an nd-array from nested lists of blocks. hstack : Stack arrays in sequence horizontally (column wise). - dstack : Stack arrays in sequence depth wise (along third dimension). - concatenate : Join a sequence of arrays along an existing axis. - vsplit : Split array into a list of multiple sub-arrays vertically. - block : Assemble arrays from blocks. + dstack : Stack arrays in sequence depth wise (along third axis). + column_stack : Stack 1-D arrays as columns into a 2-D array. + vsplit : Split an array into multiple sub-arrays vertically (row-wise). Examples -------- >>> a = np.array([1, 2, 3]) - >>> b = np.array([2, 3, 4]) + >>> b = np.array([4, 5, 6]) >>> np.vstack((a,b)) array([[1, 2, 3], - [2, 3, 4]]) + [4, 5, 6]]) >>> a = np.array([[1], [2], [3]]) - >>> b = np.array([[2], [3], [4]]) + >>> b = np.array([[4], [5], [6]]) >>> np.vstack((a,b)) array([[1], [2], [3], - [2], - [3], - [4]]) + [4], + [5], + [6]]) """ - return _nx.concatenate([atleast_2d(_m) for _m in tup], 0) + if not overrides.ARRAY_FUNCTION_ENABLED: + # raise warning if necessary + _arrays_for_stack_dispatcher(tup, stacklevel=2) + arrs = atleast_2d(*tup) + if not isinstance(arrs, list): + arrs = [arrs] + return _nx.concatenate(arrs, 0) + +@array_function_dispatch(_vhstack_dispatcher) def hstack(tup): """ Stack arrays in sequence horizontally (column wise). @@ -259,28 +309,35 @@ def hstack(tup): See Also -------- + concatenate : Join a sequence of arrays along an existing axis. stack : Join a sequence of arrays along a new axis. + block : Assemble an nd-array from nested lists of blocks. vstack : Stack arrays in sequence vertically (row wise). dstack : Stack arrays in sequence depth wise (along third axis). - concatenate : Join a sequence of arrays along an existing axis. - hsplit : Split array along second axis. - block : Assemble arrays from blocks. + column_stack : Stack 1-D arrays as columns into a 2-D array. + hsplit : Split an array into multiple sub-arrays horizontally (column-wise). Examples -------- >>> a = np.array((1,2,3)) - >>> b = np.array((2,3,4)) + >>> b = np.array((4,5,6)) >>> np.hstack((a,b)) - array([1, 2, 3, 2, 3, 4]) + array([1, 2, 3, 4, 5, 6]) >>> a = np.array([[1],[2],[3]]) - >>> b = np.array([[2],[3],[4]]) + >>> b = np.array([[4],[5],[6]]) >>> np.hstack((a,b)) - array([[1, 2], - [2, 3], - [3, 4]]) + array([[1, 4], + [2, 5], + [3, 6]]) """ - arrs = [atleast_1d(_m) for _m in tup] + if not overrides.ARRAY_FUNCTION_ENABLED: + # raise warning if necessary + _arrays_for_stack_dispatcher(tup, stacklevel=2) + + arrs = atleast_1d(*tup) + if not isinstance(arrs, list): + arrs = [arrs] # As a special case, dimension 0 of 1-dimensional arrays is "horizontal" if arrs and arrs[0].ndim == 1: return _nx.concatenate(arrs, 0) @@ -288,13 +345,23 @@ def hstack(tup): return _nx.concatenate(arrs, 1) +def _stack_dispatcher(arrays, axis=None, out=None): + arrays = _arrays_for_stack_dispatcher(arrays, stacklevel=6) + if out is not None: + # optimize for the typical case where only arrays is provided + arrays = list(arrays) + arrays.append(out) + return arrays + + +@array_function_dispatch(_stack_dispatcher) def stack(arrays, axis=0, out=None): """ Join a sequence of arrays along a new axis. - The `axis` parameter specifies the index of the new axis in the dimensions - of the result. For example, if ``axis=0`` it will be the first dimension - and if ``axis=-1`` it will be the last dimension. + The ``axis`` parameter specifies the index of the new axis in the + dimensions of the result. For example, if ``axis=0`` it will be the first + dimension and if ``axis=-1`` it will be the last dimension. .. versionadded:: 1.10.0 @@ -302,8 +369,10 @@ def stack(arrays, axis=0, out=None): ---------- arrays : sequence of array_like Each array must have the same shape. + axis : int, optional The axis in the result array along which the input arrays are stacked. + out : ndarray, optional If provided, the destination to place the result. The shape must be correct, matching that of what stack would have returned if no @@ -317,8 +386,8 @@ def stack(arrays, axis=0, out=None): See Also -------- concatenate : Join a sequence of arrays along an existing axis. + block : Assemble an nd-array from nested lists of blocks. split : Split array into a list of multiple sub-arrays of equal size. - block : Assemble arrays from blocks. Examples -------- @@ -333,22 +402,26 @@ def stack(arrays, axis=0, out=None): (3, 4, 10) >>> a = np.array([1, 2, 3]) - >>> b = np.array([2, 3, 4]) + >>> b = np.array([4, 5, 6]) >>> np.stack((a, b)) array([[1, 2, 3], - [2, 3, 4]]) + [4, 5, 6]]) >>> np.stack((a, b), axis=-1) - array([[1, 2], - [2, 3], - [3, 4]]) + array([[1, 4], + [2, 5], + [3, 6]]) """ + if not overrides.ARRAY_FUNCTION_ENABLED: + # raise warning if necessary + _arrays_for_stack_dispatcher(arrays, stacklevel=2) + arrays = [asanyarray(arr) for arr in arrays] if not arrays: raise ValueError('need at least one array to stack') - shapes = set(arr.shape for arr in arrays) + shapes = {arr.shape for arr in arrays} if len(shapes) != 1: raise ValueError('all input arrays must have the same shape') @@ -360,6 +433,22 @@ def stack(arrays, axis=0, out=None): return _nx.concatenate(expanded_arrays, axis=axis, out=out) +# Internal functions to eliminate the overhead of repeated dispatch in one of +# the two possible paths inside np.block. +# Use getattr to protect against __array_function__ being disabled. +_size = getattr(_from_nx.size, '__wrapped__', _from_nx.size) +_ndim = getattr(_from_nx.ndim, '__wrapped__', _from_nx.ndim) +_concatenate = getattr(_from_nx.concatenate, '__wrapped__', _from_nx.concatenate) + + +def _block_format_index(index): + """ + Convert a list of indices ``[0, 1, 2]`` into ``"arrays[0][1][2]"``. + """ + idx_str = ''.join('[{}]'.format(i) for i in index if i is not None) + return 'arrays' + idx_str + + def _block_check_depths_match(arrays, parent_index=[]): """ Recursive function checking that the depths of nested lists in `arrays` @@ -370,19 +459,27 @@ def _block_check_depths_match(arrays, parent_index=[]): for each innermost list, in case an error needs to be raised, so that the index of the offending list can be printed as part of the error. - The parameter `parent_index` is the full index of `arrays` within the - nested lists passed to _block_check_depths_match at the top of the - recursion. - The return value is a pair. The first item returned is the full index - of an element (specifically the first element) from the bottom of the - nesting in `arrays`. An empty list at the bottom of the nesting is - represented by a `None` index. - The second item is the maximum of the ndims of the arrays nested in - `arrays`. + Parameters + ---------- + arrays : nested list of arrays + The arrays to check + parent_index : list of int + The full index of `arrays` within the nested lists passed to + `_block_check_depths_match` at the top of the recursion. + + Returns + ------- + first_index : list of int + The full index of an element from the bottom of the nesting in + `arrays`. If any element at the bottom is an empty list, this will + refer to it, and the last index along the empty axis will be None. + max_arr_ndim : int + The maximum of the ndims of the arrays nested in `arrays`. + final_size: int + The number of elements in the final array. This is used the motivate + the choice of algorithm used using benchmarking wisdom. + """ - def format_index(index): - idx_str = ''.join('[{}]'.format(i) for i in index if i is not None) - return 'arrays' + idx_str if type(arrays) is tuple: # not strictly necessary, but saves us from: # - more than one way to do things - no point treating tuples like @@ -393,15 +490,16 @@ def format_index(index): '{} is a tuple. ' 'Only lists can be used to arrange blocks, and np.block does ' 'not allow implicit conversion from tuple to ndarray.'.format( - format_index(parent_index) + _block_format_index(parent_index) ) ) elif type(arrays) is list and len(arrays) > 0: idxs_ndims = (_block_check_depths_match(arr, parent_index + [i]) for i, arr in enumerate(arrays)) - first_index, max_arr_ndim = next(idxs_ndims) - for index, ndim in idxs_ndims: + first_index, max_arr_ndim, final_size = next(idxs_ndims) + for index, ndim, size in idxs_ndims: + final_size += size if ndim > max_arr_ndim: max_arr_ndim = ndim if len(index) != len(first_index): @@ -410,51 +508,176 @@ def format_index(index): "{}, but there is an element at depth {} ({})".format( len(first_index), len(index), - format_index(index) + _block_format_index(index) ) ) - return first_index, max_arr_ndim + # propagate our flag that indicates an empty list at the bottom + if index[-1] is None: + first_index = index + + return first_index, max_arr_ndim, final_size elif type(arrays) is list and len(arrays) == 0: # We've 'bottomed out' on an empty list - return parent_index + [None], 0 + return parent_index + [None], 0, 0 else: # We've 'bottomed out' - arrays is either a scalar or an array - return parent_index, _nx.ndim(arrays) + size = _size(arrays) + return parent_index, _ndim(arrays), size + + +def _atleast_nd(a, ndim): + # Ensures `a` has at least `ndim` dimensions by prepending + # ones to `a.shape` as necessary + return array(a, ndmin=ndim, copy=False, subok=True) + + +def _accumulate(values): + return list(itertools.accumulate(values)) -def _block(arrays, max_depth, result_ndim): +def _concatenate_shapes(shapes, axis): + """Given array shapes, return the resulting shape and slices prefixes. + + These help in nested concatenation. + + Returns + ------- + shape: tuple of int + This tuple satisfies: + ``` + shape, _ = _concatenate_shapes([arr.shape for shape in arrs], axis) + shape == concatenate(arrs, axis).shape + ``` + + slice_prefixes: tuple of (slice(start, end), ) + For a list of arrays being concatenated, this returns the slice + in the larger array at axis that needs to be sliced into. + + For example, the following holds: + ``` + ret = concatenate([a, b, c], axis) + _, (sl_a, sl_b, sl_c) = concatenate_slices([a, b, c], axis) + + ret[(slice(None),) * axis + sl_a] == a + ret[(slice(None),) * axis + sl_b] == b + ret[(slice(None),) * axis + sl_c] == c + ``` + + These are called slice prefixes since they are used in the recursive + blocking algorithm to compute the left-most slices during the + recursion. Therefore, they must be prepended to rest of the slice + that was computed deeper in the recursion. + + These are returned as tuples to ensure that they can quickly be added + to existing slice tuple without creating a new tuple every time. + + """ + # Cache a result that will be reused. + shape_at_axis = [shape[axis] for shape in shapes] + + # Take a shape, any shape + first_shape = shapes[0] + first_shape_pre = first_shape[:axis] + first_shape_post = first_shape[axis+1:] + + if any(shape[:axis] != first_shape_pre or + shape[axis+1:] != first_shape_post for shape in shapes): + raise ValueError( + 'Mismatched array shapes in block along axis {}.'.format(axis)) + + shape = (first_shape_pre + (sum(shape_at_axis),) + first_shape[axis+1:]) + + offsets_at_axis = _accumulate(shape_at_axis) + slice_prefixes = [(slice(start, end),) + for start, end in zip([0] + offsets_at_axis, + offsets_at_axis)] + return shape, slice_prefixes + + +def _block_info_recursion(arrays, max_depth, result_ndim, depth=0): + """ + Returns the shape of the final array, along with a list + of slices and a list of arrays that can be used for assignment inside the + new array + + Parameters + ---------- + arrays : nested list of arrays + The arrays to check + max_depth : list of int + The number of nested lists + result_ndim : int + The number of dimensions in thefinal array. + + Returns + ------- + shape : tuple of int + The shape that the final array will take on. + slices: list of tuple of slices + The slices into the full array required for assignment. These are + required to be prepended with ``(Ellipsis, )`` to obtain to correct + final index. + arrays: list of ndarray + The data to assign to each slice of the full array + + """ + if depth < max_depth: + shapes, slices, arrays = zip( + *[_block_info_recursion(arr, max_depth, result_ndim, depth+1) + for arr in arrays]) + + axis = result_ndim - max_depth + depth + shape, slice_prefixes = _concatenate_shapes(shapes, axis) + + # Prepend the slice prefix and flatten the slices + slices = [slice_prefix + the_slice + for slice_prefix, inner_slices in zip(slice_prefixes, slices) + for the_slice in inner_slices] + + # Flatten the array list + arrays = functools.reduce(operator.add, arrays) + + return shape, slices, arrays + else: + # We've 'bottomed out' - arrays is either a scalar or an array + # type(arrays) is not list + # Return the slice and the array inside a list to be consistent with + # the recursive case. + arr = _atleast_nd(arrays, result_ndim) + return arr.shape, [()], [arr] + + +def _block(arrays, max_depth, result_ndim, depth=0): """ - Internal implementation of block. `arrays` is the argument passed to + Internal implementation of block based on repeated concatenation. + `arrays` is the argument passed to block. `max_depth` is the depth of nested lists within `arrays` and `result_ndim` is the greatest of the dimensions of the arrays in `arrays` and the depth of the lists in `arrays` (see block docstring for details). """ - def atleast_nd(a, ndim): - # Ensures `a` has at least `ndim` dimensions by prepending - # ones to `a.shape` as necessary - return array(a, ndmin=ndim, copy=False, subok=True) - - def block_recursion(arrays, depth=0): - if depth < max_depth: - if len(arrays) == 0: - raise ValueError('Lists cannot be empty') - arrs = [block_recursion(arr, depth+1) for arr in arrays] - return _nx.concatenate(arrs, axis=-(max_depth-depth)) - else: - # We've 'bottomed out' - arrays is either a scalar or an array - # type(arrays) is not list - return atleast_nd(arrays, result_ndim) + if depth < max_depth: + arrs = [_block(arr, max_depth, result_ndim, depth+1) + for arr in arrays] + return _concatenate(arrs, axis=-(max_depth-depth)) + else: + # We've 'bottomed out' - arrays is either a scalar or an array + # type(arrays) is not list + return _atleast_nd(arrays, result_ndim) + - try: - return block_recursion(arrays) - finally: - # recursive closures have a cyclic reference to themselves, which - # requires gc to collect (gh-10620). To avoid this problem, for - # performance and PyPy friendliness, we break the cycle: - block_recursion = None +def _block_dispatcher(arrays): + # Use type(...) is list to match the behavior of np.block(), which special + # cases list specifically rather than allowing for generic iterables or + # tuple. Also, we know that list.__array_function__ will never exist. + if type(arrays) is list: + for subarrays in arrays: + yield from _block_dispatcher(subarrays) + else: + yield arrays +@array_function_dispatch(_block_dispatcher) def block(arrays): """ Assemble an nd-array from nested lists of blocks. @@ -502,12 +725,13 @@ def block(arrays): See Also -------- - concatenate : Join a sequence of arrays together. - stack : Stack arrays in sequence along a new dimension. - hstack : Stack arrays in sequence horizontally (column wise). + concatenate : Join a sequence of arrays along an existing axis. + stack : Join a sequence of arrays along a new axis. vstack : Stack arrays in sequence vertically (row wise). - dstack : Stack arrays in sequence depth wise (along third dimension). - vsplit : Split array into a list of multiple sub-arrays vertically. + hstack : Stack arrays in sequence horizontally (column wise). + dstack : Stack arrays in sequence depth wise (along third axis). + column_stack : Stack 1-D arrays as columns into a 2-D array. + vsplit : Split an array into multiple sub-arrays vertically (row-wise). Notes ----- @@ -549,11 +773,11 @@ def block(arrays): ... [A, np.zeros((2, 3))], ... [np.ones((3, 2)), B ] ... ]) - array([[ 2., 0., 0., 0., 0.], - [ 0., 2., 0., 0., 0.], - [ 1., 1., 3., 0., 0.], - [ 1., 1., 0., 3., 0.], - [ 1., 1., 0., 0., 3.]]) + array([[2., 0., 0., 0., 0.], + [0., 2., 0., 0., 0.], + [1., 1., 3., 0., 0.], + [1., 1., 0., 3., 0.], + [1., 1., 0., 0., 3.]]) With a list of depth 1, `block` can be used as `hstack` @@ -561,9 +785,9 @@ def block(arrays): array([1, 2, 3]) >>> a = np.array([1, 2, 3]) - >>> b = np.array([2, 3, 4]) + >>> b = np.array([4, 5, 6]) >>> np.block([a, b, 10]) # hstack([a, b, 10]) - array([1, 2, 3, 2, 3, 4, 10]) + array([ 1, 2, 3, 4, 5, 6, 10]) >>> A = np.ones((2, 2), int) >>> B = 2 * A @@ -574,10 +798,10 @@ def block(arrays): With a list of depth 2, `block` can be used in place of `vstack`: >>> a = np.array([1, 2, 3]) - >>> b = np.array([2, 3, 4]) + >>> b = np.array([4, 5, 6]) >>> np.block([[a], [b]]) # vstack([a, b]) array([[1, 2, 3], - [2, 3, 4]]) + [4, 5, 6]]) >>> A = np.ones((2, 2), int) >>> B = 2 * A @@ -603,6 +827,74 @@ def block(arrays): """ - bottom_index, arr_ndim = _block_check_depths_match(arrays) + arrays, list_ndim, result_ndim, final_size = _block_setup(arrays) + + # It was found through benchmarking that making an array of final size + # around 256x256 was faster by straight concatenation on a + # i7-7700HQ processor and dual channel ram 2400MHz. + # It didn't seem to matter heavily on the dtype used. + # + # A 2D array using repeated concatenation requires 2 copies of the array. + # + # The fastest algorithm will depend on the ratio of CPU power to memory + # speed. + # One can monitor the results of the benchmark + # https://pv.github.io/numpy-bench/#bench_shape_base.Block2D.time_block2d + # to tune this parameter until a C version of the `_block_info_recursion` + # algorithm is implemented which would likely be faster than the python + # version. + if list_ndim * final_size > (2 * 512 * 512): + return _block_slicing(arrays, list_ndim, result_ndim) + else: + return _block_concatenate(arrays, list_ndim, result_ndim) + + +# These helper functions are mostly used for testing. +# They allow us to write tests that directly call `_block_slicing` +# or `_block_concatenate` without blocking large arrays to force the wisdom +# to trigger the desired path. +def _block_setup(arrays): + """ + Returns + (`arrays`, list_ndim, result_ndim, final_size) + """ + bottom_index, arr_ndim, final_size = _block_check_depths_match(arrays) list_ndim = len(bottom_index) - return _block(arrays, list_ndim, max(arr_ndim, list_ndim)) + if bottom_index and bottom_index[-1] is None: + raise ValueError( + 'List at {} cannot be empty'.format( + _block_format_index(bottom_index) + ) + ) + result_ndim = max(arr_ndim, list_ndim) + return arrays, list_ndim, result_ndim, final_size + + +def _block_slicing(arrays, list_ndim, result_ndim): + shape, slices, arrays = _block_info_recursion( + arrays, list_ndim, result_ndim) + dtype = _nx.result_type(*[arr.dtype for arr in arrays]) + + # Test preferring F only in the case that all input arrays are F + F_order = all(arr.flags['F_CONTIGUOUS'] for arr in arrays) + C_order = all(arr.flags['C_CONTIGUOUS'] for arr in arrays) + order = 'F' if F_order and not C_order else 'C' + result = _nx.empty(shape=shape, dtype=dtype, order=order) + # Note: In a c implementation, the function + # PyArray_CreateMultiSortedStridePerm could be used for more advanced + # guessing of the desired order. + + for the_slice, arr in zip(slices, arrays): + result[(Ellipsis,) + the_slice] = arr + return result + + +def _block_concatenate(arrays, list_ndim, result_ndim): + result = _block(arrays, list_ndim, result_ndim) + if list_ndim == 0: + # Catch an edge case where _block returns a view because + # `arrays` is a single numpy array and not a list of numpy arrays. + # This might copy scalars or lists twice, but this isn't a likely + # usecase for those interested in performance + result = result.copy() + return result diff --git a/numpy/core/shape_base.pyi b/numpy/core/shape_base.pyi new file mode 100644 index 000000000000..159ad2781c05 --- /dev/null +++ b/numpy/core/shape_base.pyi @@ -0,0 +1,66 @@ +from typing import TypeVar, overload, List, Sequence, Any, SupportsIndex + +from numpy import generic, dtype +from numpy.typing import ArrayLike, NDArray, _FiniteNestedSequence, _SupportsArray + +_SCT = TypeVar("_SCT", bound=generic) +_ArrayType = TypeVar("_ArrayType", bound=NDArray[Any]) + +_ArrayLike = _FiniteNestedSequence[_SupportsArray[dtype[_SCT]]] + +__all__: List[str] + +@overload +def atleast_1d(arys: _ArrayLike[_SCT], /) -> NDArray[_SCT]: ... +@overload +def atleast_1d(arys: ArrayLike, /) -> NDArray[Any]: ... +@overload +def atleast_1d(*arys: ArrayLike) -> List[NDArray[Any]]: ... + +@overload +def atleast_2d(arys: _ArrayLike[_SCT], /) -> NDArray[_SCT]: ... +@overload +def atleast_2d(arys: ArrayLike, /) -> NDArray[Any]: ... +@overload +def atleast_2d(*arys: ArrayLike) -> List[NDArray[Any]]: ... + +@overload +def atleast_3d(arys: _ArrayLike[_SCT], /) -> NDArray[_SCT]: ... +@overload +def atleast_3d(arys: ArrayLike, /) -> NDArray[Any]: ... +@overload +def atleast_3d(*arys: ArrayLike) -> List[NDArray[Any]]: ... + +@overload +def vstack(tup: Sequence[_ArrayLike[_SCT]]) -> NDArray[_SCT]: ... +@overload +def vstack(tup: Sequence[ArrayLike]) -> NDArray[Any]: ... + +@overload +def hstack(tup: Sequence[_ArrayLike[_SCT]]) -> NDArray[_SCT]: ... +@overload +def hstack(tup: Sequence[ArrayLike]) -> NDArray[Any]: ... + +@overload +def stack( + arrays: Sequence[_ArrayLike[_SCT]], + axis: SupportsIndex = ..., + out: None = ..., +) -> NDArray[_SCT]: ... +@overload +def stack( + arrays: Sequence[ArrayLike], + axis: SupportsIndex = ..., + out: None = ..., +) -> NDArray[Any]: ... +@overload +def stack( + arrays: Sequence[ArrayLike], + axis: SupportsIndex = ..., + out: _ArrayType = ..., +) -> _ArrayType: ... + +@overload +def block(arrays: _ArrayLike[_SCT]) -> NDArray[_SCT]: ... +@overload +def block(arrays: ArrayLike) -> NDArray[Any]: ... diff --git a/numpy/core/src/_simd/_simd.c b/numpy/core/src/_simd/_simd.c new file mode 100644 index 000000000000..b1fdd4478d9d --- /dev/null +++ b/numpy/core/src/_simd/_simd.c @@ -0,0 +1,73 @@ +#include "_simd.h" + +PyMODINIT_FUNC PyInit__simd(void) +{ + static struct PyModuleDef defs = { + .m_base = PyModuleDef_HEAD_INIT, + .m_name = "numpy.core._simd", + .m_size = -1 + }; + if (npy_cpu_init() < 0) { + return NULL; + } + PyObject *m = PyModule_Create(&defs); + if (m == NULL) { + return NULL; + } + PyObject *targets = PyDict_New(); + if (targets == NULL) { + goto err; + } + if (PyModule_AddObject(m, "targets", targets) < 0) { + Py_DECREF(targets); + goto err; + } + // add keys for non-supported optimizations with None value + #define ATTACH_MODULE(TESTED_FEATURES, TARGET_NAME, MAKE_MSVC_HAPPY) \ + { \ + PyObject *simd_mod; \ + if (!TESTED_FEATURES) { \ + Py_INCREF(Py_None); \ + simd_mod = Py_None; \ + } else { \ + simd_mod = NPY_CAT(simd_create_module_, TARGET_NAME)(); \ + if (simd_mod == NULL) { \ + goto err; \ + } \ + } \ + const char *target_name = NPY_TOSTRING(TARGET_NAME); \ + if (PyDict_SetItemString(targets, target_name, simd_mod) < 0) { \ + Py_DECREF(simd_mod); \ + goto err; \ + } \ + Py_INCREF(simd_mod); \ + if (PyModule_AddObject(m, target_name, simd_mod) < 0) { \ + Py_DECREF(simd_mod); \ + goto err; \ + } \ + } + + #define ATTACH_BASELINE_MODULE(MAKE_MSVC_HAPPY) \ + { \ + PyObject *simd_mod = simd_create_module(); \ + if (simd_mod == NULL) { \ + goto err; \ + } \ + if (PyDict_SetItemString(targets, "baseline", simd_mod) < 0) { \ + Py_DECREF(simd_mod); \ + goto err; \ + } \ + Py_INCREF(simd_mod); \ + if (PyModule_AddObject(m, "baseline", simd_mod) < 0) { \ + Py_DECREF(simd_mod); \ + goto err; \ + } \ + } + + NPY__CPU_DISPATCH_CALL(NPY_CPU_HAVE, ATTACH_MODULE, MAKE_MSVC_HAPPY) + NPY__CPU_DISPATCH_BASELINE_CALL(ATTACH_BASELINE_MODULE, MAKE_MSVC_HAPPY) + return m; +err: + Py_DECREF(m); + return NULL; +} diff --git a/numpy/core/src/_simd/_simd.dispatch.c.src b/numpy/core/src/_simd/_simd.dispatch.c.src new file mode 100644 index 000000000000..84de9a059fc8 --- /dev/null +++ b/numpy/core/src/_simd/_simd.dispatch.c.src @@ -0,0 +1,757 @@ +/*@targets #simd_test*/ +#include "_simd.h" +#include "_simd_inc.h" + +#if NPY_SIMD +#include "_simd_data.inc" +#include "_simd_convert.inc" +#include "_simd_vector.inc" +#include "_simd_arg.inc" +#include "_simd_easyintrin.inc" + +//######################################################################### +//## Defining NPYV intrinsics as module functions +//######################################################################### +/**begin repeat + * #sfx = u8, s8, u16, s16, u32, s32, u64, s64, f32, f64# + * #bsfx = b8, b8, b16, b16, b32, b32, b64, b64, b32, b64# + * #esfx = u16, s8, u32,s16, u32, s32, u64, s64, f32, f64# + * #expand_sup= 1, 0, 1, 0, 0, 0, 0, 0, 0, 0# + * #simd_sup = 1, 1, 1, 1, 1, 1, 1, 1, 1, NPY_SIMD_F64# + * #fp_only = 0, 0, 0, 0, 0, 0, 0, 0, 1, 1# + * #sat_sup = 1, 1, 1, 1, 0, 0, 0, 0, 0, 0# + * #mul_sup = 1, 1, 1, 1, 1, 1, 0, 0, 1, 1# + * #div_sup = 0, 0, 0, 0, 0, 0, 0, 0, 1, 1# + * #fused_sup = 0, 0, 0, 0, 0, 0, 0, 0, 1, 1# + * #sumup_sup = 1, 0, 1, 0, 0, 0, 0, 0, 0, 0# + * #sum_sup = 0, 0, 0, 0, 1, 0, 1, 0, 1, 1# + * #rev64_sup = 1, 1, 1, 1, 1, 1, 0, 0, 1, 0# + * #ncont_sup = 0, 0, 0, 0, 1, 1, 1, 1, 1, 1# + * #intdiv_sup= 1, 1, 1, 1, 1, 1, 1, 1, 0, 0# + * #shl_imm = 0, 0, 15, 15, 31, 31, 63, 63, 0, 0# + * #shr_imm = 0, 0, 16, 16, 32, 32, 64, 64, 0, 0# + */ +#if @simd_sup@ +/*************************** + * Memory + ***************************/ +/**begin repeat1 + * # intrin = load, loada, loads, loadl# + */ +SIMD_IMPL_INTRIN_1(@intrin@_@sfx@, v@sfx@, q@sfx@) +/**end repeat1**/ +/**begin repeat1 + * # intrin = store, storea, stores, storel, storeh# + */ +// special definition due to the nature of @intrin@ +static PyObject * +simd__intrin_@intrin@_@sfx@(PyObject* NPY_UNUSED(self), PyObject *args) +{ + simd_arg seq_arg = {.dtype = simd_data_q@sfx@}; + simd_arg vec_arg = {.dtype = simd_data_v@sfx@}; + if (!PyArg_ParseTuple( + args, "O&O&:@intrin@_@sfx@", + simd_arg_converter, &seq_arg, + simd_arg_converter, &vec_arg + )) { + return NULL; + } + npyv_@intrin@_@sfx@(seq_arg.data.q@sfx@, vec_arg.data.v@sfx@); + // write-back + if (simd_sequence_fill_iterable(seq_arg.obj, seq_arg.data.q@sfx@, simd_data_q@sfx@)) { + simd_arg_free(&seq_arg); + return NULL; + } + simd_arg_free(&seq_arg); + Py_RETURN_NONE; +} +/**end repeat1**/ + +/**************************************** + * Non-contiguous/Partial Memory access + ****************************************/ +#if @ncont_sup@ +// Partial Load +SIMD_IMPL_INTRIN_3(load_till_@sfx@, v@sfx@, q@sfx@, u32, @sfx@) +SIMD_IMPL_INTRIN_2(load_tillz_@sfx@, v@sfx@, q@sfx@, u32) + +// Partial Store +static PyObject * +simd__intrin_store_till_@sfx@(PyObject* NPY_UNUSED(self), PyObject *args) +{ + simd_arg seq_arg = {.dtype = simd_data_q@sfx@}; + simd_arg nlane_arg = {.dtype = simd_data_u32}; + simd_arg vec_arg = {.dtype = simd_data_v@sfx@}; + if (!PyArg_ParseTuple( + args, "O&O&O&:store_till_@sfx@", + simd_arg_converter, &seq_arg, + simd_arg_converter, &nlane_arg, + simd_arg_converter, &vec_arg + )) { + return NULL; + } + npyv_store_till_@sfx@( + seq_arg.data.q@sfx@, nlane_arg.data.u32, vec_arg.data.v@sfx@ + ); + // write-back + if (simd_sequence_fill_iterable(seq_arg.obj, seq_arg.data.q@sfx@, simd_data_q@sfx@)) { + simd_arg_free(&seq_arg); + return NULL; + } + simd_arg_free(&seq_arg); + Py_RETURN_NONE; +} + +// Non-contiguous Load +/**begin repeat1 + * #intrin = loadn, loadn_till, loadn_tillz# + * #till = 0, 1, 1# + * #fill = 0, 1, 0# + * #format = , O&O&, O&# + */ +static PyObject * +simd__intrin_@intrin@_@sfx@(PyObject* NPY_UNUSED(self), PyObject *args) +{ + simd_arg seq_arg = {.dtype = simd_data_q@sfx@}; + simd_arg stride_arg = {.dtype = simd_data_s64}; +#if @till@ + simd_arg nlane_arg = {.dtype = simd_data_u32}; +#endif // till +#if @fill@ + simd_arg fill_arg = {.dtype = simd_data_@sfx@}; +#endif + if (!PyArg_ParseTuple( + args, "@format@O&O&:@intrin@_@sfx@", + simd_arg_converter, &seq_arg, + simd_arg_converter, &stride_arg +#if @till@ + ,simd_arg_converter, &nlane_arg +#endif +#if @fill@ + ,simd_arg_converter, &fill_arg +#endif + )) { + return NULL; + } + npyv_lanetype_@sfx@ *seq_ptr = seq_arg.data.q@sfx@; + npy_intp stride = (npy_intp)stride_arg.data.s64; + Py_ssize_t cur_seq_len = simd_sequence_len(seq_ptr); + Py_ssize_t min_seq_len = stride * npyv_nlanes_@sfx@; + if (stride < 0) { + seq_ptr += cur_seq_len -1; + min_seq_len = -min_seq_len; + } + if (cur_seq_len < min_seq_len) { + PyErr_Format(PyExc_ValueError, + "@intrin@_@sfx@(), according to provided stride %d, the " + "minimum acceptable size of the required sequence is %d, given(%d)", + stride, min_seq_len, cur_seq_len + ); + goto err; + } + npyv_@sfx@ rvec = npyv_@intrin@_@sfx@( + seq_ptr, stride + #if @till@ + , nlane_arg.data.u32 + #endif + #if @fill@ + , fill_arg.data.@sfx@ + #endif + ); + simd_arg ret = { + .dtype = simd_data_v@sfx@, .data = {.v@sfx@=rvec} + }; + simd_arg_free(&seq_arg); + return simd_arg_to_obj(&ret); +err: + simd_arg_free(&seq_arg); + return NULL; +} +/**end repeat1**/ + +// Non-contiguous Store +/**begin repeat1 + * #intrin = storen, storen_till# + * #till = 0, 1# + * #format = , O&# + */ +static PyObject * +simd__intrin_@intrin@_@sfx@(PyObject* NPY_UNUSED(self), PyObject *args) +{ + simd_arg seq_arg = {.dtype = simd_data_q@sfx@}; + simd_arg stride_arg = {.dtype = simd_data_s64}; + simd_arg vec_arg = {.dtype = simd_data_v@sfx@}; +#if @till@ + simd_arg nlane_arg = {.dtype = simd_data_u32}; +#endif + if (!PyArg_ParseTuple( + args, "@format@O&O&O&:storen_@sfx@", + simd_arg_converter, &seq_arg, + simd_arg_converter, &stride_arg +#if @till@ + ,simd_arg_converter, &nlane_arg +#endif + ,simd_arg_converter, &vec_arg + )) { + return NULL; + } + npyv_lanetype_@sfx@ *seq_ptr = seq_arg.data.q@sfx@; + npy_intp stride = (npy_intp)stride_arg.data.s64; + Py_ssize_t cur_seq_len = simd_sequence_len(seq_ptr); + Py_ssize_t min_seq_len = stride * npyv_nlanes_@sfx@; + if (stride < 0) { + seq_ptr += cur_seq_len -1; + min_seq_len = -min_seq_len; + } + // overflow guard + if (cur_seq_len < min_seq_len) { + PyErr_Format(PyExc_ValueError, + "@intrin@_@sfx@(), according to provided stride %d, the" + "minimum acceptable size of the required sequence is %d, given(%d)", + stride, min_seq_len, cur_seq_len + ); + goto err; + } + npyv_@intrin@_@sfx@( + seq_ptr, stride + #if @till@ + ,nlane_arg.data.u32 + #endif + ,vec_arg.data.v@sfx@ + ); + // write-back + if (simd_sequence_fill_iterable(seq_arg.obj, seq_arg.data.q@sfx@, simd_data_q@sfx@)) { + goto err; + } + simd_arg_free(&seq_arg); + Py_RETURN_NONE; +err: + simd_arg_free(&seq_arg); + return NULL; +} +/**end repeat1**/ +#endif // @ncont_sup@ + +/*************************** + * Misc + ***************************/ +SIMD_IMPL_INTRIN_0(zero_@sfx@, v@sfx@) +SIMD_IMPL_INTRIN_1(setall_@sfx@, v@sfx@, @sfx@) +SIMD_IMPL_INTRIN_3(select_@sfx@, v@sfx@, v@bsfx@, v@sfx@, v@sfx@) + +/**begin repeat1 + * #sfx_to = u8, s8, u16, s16, u32, s32, u64, s64, f32, f64# + * #simd_sup2 = 1, 1, 1, 1, 1, 1, 1, 1, 1, NPY_SIMD_F64# + */ +#if @simd_sup2@ +SIMD_IMPL_INTRIN_1(reinterpret_@sfx_to@_@sfx@, v@sfx_to@, v@sfx@) +#endif // simd_sup2 +/**end repeat1**/ + +/** + * special definition due to the nature of intrinsics + * npyv_setf_@sfx@ and npy_set_@sfx@. +*/ +/**begin repeat1 + * #intrin = setf, set# + */ +static PyObject * +simd__intrin_@intrin@_@sfx@(PyObject* NPY_UNUSED(self), PyObject *args) +{ + npyv_lanetype_@sfx@ *data = simd_sequence_from_iterable(args, simd_data_q@sfx@, npyv_nlanes_@sfx@); + if (data == NULL) { + return NULL; + } + simd_data r = {.v@sfx@ = npyv_@intrin@_@sfx@( + data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], + data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15], + data[16], data[17], data[18], data[19], data[20], data[21], data[22], data[23], + data[24], data[25], data[26], data[27], data[28], data[29], data[30], data[31], + data[32], data[33], data[34], data[35], data[36], data[37], data[38], data[39], + data[40], data[41], data[42], data[43], data[44], data[45], data[46], data[47], + data[48], data[49], data[50], data[51], data[52], data[53], data[54], data[55], + data[56], data[57], data[58], data[59], data[60], data[61], data[62], data[63], + data[64] // for setf + )}; + simd_sequence_free(data); + return (PyObject*)PySIMDVector_FromData(r, simd_data_v@sfx@); +} +/**end repeat1**/ + +/*************************** + * Reorder + ***************************/ +/**begin repeat1 + * # intrin = combinel, combineh# + */ +SIMD_IMPL_INTRIN_2(@intrin@_@sfx@, v@sfx@, v@sfx@, v@sfx@) +/**end repeat1**/ + +/**begin repeat1 + * # intrin = combine, zip# + */ +SIMD_IMPL_INTRIN_2(@intrin@_@sfx@, v@sfx@x2, v@sfx@, v@sfx@) +/**end repeat1**/ + +#if @rev64_sup@ +SIMD_IMPL_INTRIN_1(rev64_@sfx@, v@sfx@, v@sfx@) +#endif + +/*************************** + * Operators + ***************************/ +#if @shl_imm@ > 0 +SIMD_IMPL_INTRIN_2(shl_@sfx@, v@sfx@, v@sfx@, u8) +SIMD_IMPL_INTRIN_2(shr_@sfx@, v@sfx@, v@sfx@, u8) +// immediate constant +SIMD_IMPL_INTRIN_2IMM(shli_@sfx@, v@sfx@, v@sfx@, @shl_imm@) +SIMD_IMPL_INTRIN_2IMM(shri_@sfx@, v@sfx@, v@sfx@, @shr_imm@) +#endif // shl_imm + +/**begin repeat1 + * #intrin = and, or, xor# + */ +SIMD_IMPL_INTRIN_2(@intrin@_@sfx@, v@sfx@, v@sfx@, v@sfx@) +/**end repeat1**/ + +SIMD_IMPL_INTRIN_1(not_@sfx@, v@sfx@, v@sfx@) + +/**begin repeat1 + * #intrin = cmpeq, cmpneq, cmpgt, cmpge, cmplt, cmple# + */ +SIMD_IMPL_INTRIN_2(@intrin@_@sfx@, v@bsfx@, v@sfx@, v@sfx@) +/**end repeat1**/ + +/*************************** + * Conversion + ***************************/ +SIMD_IMPL_INTRIN_1(cvt_@sfx@_@bsfx@, v@sfx@, v@bsfx@) +SIMD_IMPL_INTRIN_1(cvt_@bsfx@_@sfx@, v@bsfx@, v@sfx@) +#if @expand_sup@ +SIMD_IMPL_INTRIN_1(expand_@esfx@_@sfx@, v@esfx@x2, v@sfx@) +#endif // expand_sup +/*************************** + * Arithmetic + ***************************/ +/**begin repeat1 + * #intrin = add, sub# + */ +SIMD_IMPL_INTRIN_2(@intrin@_@sfx@, v@sfx@, v@sfx@, v@sfx@) +/**end repeat1**/ + +#if @sat_sup@ +/**begin repeat1 + * #intrin = adds, subs# + */ +SIMD_IMPL_INTRIN_2(@intrin@_@sfx@, v@sfx@, v@sfx@, v@sfx@) +/**end repeat1**/ +#endif // sat_sup + +#if @mul_sup@ +SIMD_IMPL_INTRIN_2(mul_@sfx@, v@sfx@, v@sfx@, v@sfx@) +#endif // mul_sup + +#if @div_sup@ +SIMD_IMPL_INTRIN_2(div_@sfx@, v@sfx@, v@sfx@, v@sfx@) +#endif // div_sup + +#if @intdiv_sup@ +SIMD_IMPL_INTRIN_1(divisor_@sfx@, v@sfx@x3, @sfx@) +SIMD_IMPL_INTRIN_2(divc_@sfx@, v@sfx@, v@sfx@, v@sfx@x3) +#endif // intdiv_sup + +#if @fused_sup@ +/**begin repeat1 + * #intrin = muladd, mulsub, nmuladd, nmulsub# + */ +SIMD_IMPL_INTRIN_3(@intrin@_@sfx@, v@sfx@, v@sfx@, v@sfx@, v@sfx@) +/**end repeat1**/ +#endif // fused_sup + +#if @sum_sup@ +SIMD_IMPL_INTRIN_1(sum_@sfx@, @sfx@, v@sfx@) +#endif // sum_sup + +#if @sumup_sup@ +SIMD_IMPL_INTRIN_1(sumup_@sfx@, @esfx@, v@sfx@) +#endif // sumup_sup + +/*************************** + * Math + ***************************/ +#if @fp_only@ +/**begin repeat1 + * #intrin = sqrt, recip, abs, square, ceil, trunc# + */ +SIMD_IMPL_INTRIN_1(@intrin@_@sfx@, v@sfx@, v@sfx@) +/**end repeat1**/ +#endif + +/**begin repeat1 + * #intrin = max, min# + */ +SIMD_IMPL_INTRIN_2(@intrin@_@sfx@, v@sfx@, v@sfx@, v@sfx@) +/**end repeat1**/ + +#if @fp_only@ +/**begin repeat1 + * #intrin = maxp, minp# + */ +SIMD_IMPL_INTRIN_2(@intrin@_@sfx@, v@sfx@, v@sfx@, v@sfx@) +/**end repeat1**/ +#endif + +/*************************** + * Mask operations + ***************************/ +/**begin repeat1 + * #intrin = ifadd, ifsub# + */ + SIMD_IMPL_INTRIN_4(@intrin@_@sfx@, v@sfx@, v@bsfx@, v@sfx@, v@sfx@, v@sfx@) +/**end repeat1**/ + +#endif // simd_sup +/**end repeat**/ +/************************************************************************* + * Variant + ************************************************************************/ +SIMD_IMPL_INTRIN_0N(cleanup) + +/************************************************************************* + * A special section for f32/f64 intrinsics outside the main repeater + ************************************************************************/ +/*************************** + * Operators + ***************************/ +// check special cases +SIMD_IMPL_INTRIN_1(notnan_f32, vb32, vf32) +#if NPY_SIMD_F64 + SIMD_IMPL_INTRIN_1(notnan_f64, vb64, vf64) +#endif +/*************************** + * Conversions + ***************************/ +// round to nearest integer (assume even) +SIMD_IMPL_INTRIN_1(round_s32_f32, vs32, vf32) +#if NPY_SIMD_F64 + SIMD_IMPL_INTRIN_2(round_s32_f64, vs32, vf64, vf64) +#endif + +/************************************************************************* + * A special section for boolean intrinsics outside the main repeater + ************************************************************************/ +/*************************** + * Operators + ***************************/ +// Logical +/**begin repeat + * #bsfx = b8, b16, b32, b64# + */ +SIMD_IMPL_INTRIN_2(and_@bsfx@, v@bsfx@, v@bsfx@, v@bsfx@) +SIMD_IMPL_INTRIN_2(or_@bsfx@, v@bsfx@, v@bsfx@, v@bsfx@) +SIMD_IMPL_INTRIN_2(xor_@bsfx@, v@bsfx@, v@bsfx@, v@bsfx@) +SIMD_IMPL_INTRIN_1(not_@bsfx@, v@bsfx@, v@bsfx@) +/**end repeat**/ +/*************************** + * Conversions + ***************************/ +// Convert mask vector to integer bitfield +/**begin repeat + * #bsfx = b8, b16, b32, b64# + */ +SIMD_IMPL_INTRIN_1(tobits_@bsfx@, u64, v@bsfx@) +/**end repeat**/ + + +//######################################################################### +//## Attach module functions +//######################################################################### +static PyMethodDef simd__intrinsics_methods[] = { +/**begin repeat + * #sfx = u8, s8, u16, s16, u32, s32, u64, s64, f32, f64# + * #bsfx = b8, b8, b16, b16, b32, b32, b64, b64, b32, b64# + * #esfx = u16, s8, u32,s16, u32, s32, u64, s64, f32, f64# + * #expand_sup =1, 0, 1, 0, 0, 0, 0, 0, 0, 0# + * #simd_sup = 1, 1, 1, 1, 1, 1, 1, 1, 1, NPY_SIMD_F64# + * #fp_only = 0, 0, 0, 0, 0, 0, 0, 0, 1, 1# + * #sat_sup = 1, 1, 1, 1, 0, 0, 0, 0, 0, 0# + * #mul_sup = 1, 1, 1, 1, 1, 1, 0, 0, 1, 1# + * #div_sup = 0, 0, 0, 0, 0, 0, 0, 0, 1, 1# + * #fused_sup = 0, 0, 0, 0, 0, 0, 0, 0, 1, 1# + * #sumup_sup = 1, 0, 1, 0, 0, 0, 0, 0, 0, 0# + * #sum_sup = 0, 0, 0, 0, 1, 0, 1, 0, 1, 1# + * #rev64_sup = 1, 1, 1, 1, 1, 1, 0, 0, 1, 0# + * #ncont_sup = 0, 0, 0, 0, 1, 1, 1, 1, 1, 1# + * #intdiv_sup= 1, 1, 1, 1, 1, 1, 1, 1, 0, 0# + * #shl_imm = 0, 0, 15, 15, 31, 31, 63, 63, 0, 0# + * #shr_imm = 0, 0, 16, 16, 32, 32, 64, 64, 0, 0# + */ +#if @simd_sup@ + +/*************************** + * Memory + ***************************/ +/**begin repeat1 + * # intrin = load, loada, loads, loadl, store, storea, stores, storel, storeh# + */ +SIMD_INTRIN_DEF(@intrin@_@sfx@) +/**end repeat1**/ + +/**************************************** + * Non-contiguous/Partial Memory access + ****************************************/ +#if @ncont_sup@ +/**begin repeat1 + * #intrin = load_till, load_tillz, loadn, loadn_till, loadn_tillz, + * store_till, storen, storen_till# + */ +SIMD_INTRIN_DEF(@intrin@_@sfx@) +/**end repeat1**/ +#endif // ncont_sup + +/*************************** + * Misc + ***************************/ +/**begin repeat1 + * #sfx_to = u8, s8, u16, s16, u32, s32, u64, s64, f32, f64# + * #simd_sup2 = 1, 1, 1, 1, 1, 1, 1, 1, 1, NPY_SIMD_F64# + */ +#if @simd_sup2@ +SIMD_INTRIN_DEF(reinterpret_@sfx_to@_@sfx@) +#endif // simd_sup2 +/**end repeat1**/ + +/**begin repeat1 + * # intrin = set, setf, setall, zero, select# + */ +SIMD_INTRIN_DEF(@intrin@_@sfx@) +/**end repeat1**/ + +/*************************** + * Reorder + ***************************/ +/**begin repeat1 + * # intrin = combinel, combineh, combine, zip# + */ +SIMD_INTRIN_DEF(@intrin@_@sfx@) +/**end repeat1**/ + +#if @rev64_sup@ +SIMD_INTRIN_DEF(rev64_@sfx@) +#endif + +/*************************** + * Operators + ***************************/ +#if @shl_imm@ > 0 +/**begin repeat1 + * # intrin = shl, shr, shli, shri# + */ +SIMD_INTRIN_DEF(@intrin@_@sfx@) +/**end repeat1**/ +#endif // shl_imm + +/**begin repeat1 + * #intrin = and, or, xor, not, cmpeq, cmpneq, cmpgt, cmpge, cmplt, cmple# + */ +SIMD_INTRIN_DEF(@intrin@_@sfx@) +/**end repeat1**/ + +/*************************** + * Conversion + ***************************/ +SIMD_INTRIN_DEF(cvt_@sfx@_@bsfx@) +SIMD_INTRIN_DEF(cvt_@bsfx@_@sfx@) +#if @expand_sup@ +SIMD_INTRIN_DEF(expand_@esfx@_@sfx@) +#endif // expand_sup +/*************************** + * Arithmetic + ***************************/ +/**begin repeat1 + * #intrin = add, sub# + */ +SIMD_INTRIN_DEF(@intrin@_@sfx@) +/**end repeat1**/ + +#if @sat_sup@ +/**begin repeat1 + * #intrin = adds, subs# + */ +SIMD_INTRIN_DEF(@intrin@_@sfx@) +/**end repeat1**/ +#endif // sat_sup + +#if @mul_sup@ +SIMD_INTRIN_DEF(mul_@sfx@) +#endif // mul_sup + +#if @div_sup@ +SIMD_INTRIN_DEF(div_@sfx@) +#endif // div_sup + +#if @intdiv_sup@ +SIMD_INTRIN_DEF(divisor_@sfx@) +SIMD_INTRIN_DEF(divc_@sfx@) +#endif // intdiv_sup + +#if @fused_sup@ +/**begin repeat1 + * #intrin = muladd, mulsub, nmuladd, nmulsub# + */ +SIMD_INTRIN_DEF(@intrin@_@sfx@) +/**end repeat1**/ +#endif // fused_sup + +#if @sum_sup@ +SIMD_INTRIN_DEF(sum_@sfx@) +#endif // sum_sup + +#if @sumup_sup@ +SIMD_INTRIN_DEF(sumup_@sfx@) +#endif // sumup_sup +/*************************** + * Math + ***************************/ +#if @fp_only@ +/**begin repeat1 + * #intrin = sqrt, recip, abs, square, ceil, trunc# + */ +SIMD_INTRIN_DEF(@intrin@_@sfx@) +/**end repeat1**/ +#endif + +/**begin repeat1 + * #intrin = max, min# + */ +SIMD_INTRIN_DEF(@intrin@_@sfx@) +/**end repeat1**/ + +#if @fp_only@ +/**begin repeat1 + * #intrin = maxp, minp# + */ +SIMD_INTRIN_DEF(@intrin@_@sfx@) +/**end repeat1**/ +#endif + +/*************************** + * Mask operations + ***************************/ +/**begin repeat1 + * #intrin = ifadd, ifsub# + */ + SIMD_INTRIN_DEF(@intrin@_@sfx@) +/**end repeat1**/ + +#endif // simd_sup +/**end repeat**/ +/************************************************************************* + * Variant + ************************************************************************/ +SIMD_INTRIN_DEF(cleanup) + +/************************************************************************* + * A special section for f32/f64 intrinsics outside the main repeater + ************************************************************************/ +/*************************** + * Operators + ***************************/ +// check special cases +SIMD_INTRIN_DEF(notnan_f32) +#if NPY_SIMD_F64 + SIMD_INTRIN_DEF(notnan_f64) +#endif +/*************************** + * Conversions + ***************************/ +// round to nearest integer (assume even) +SIMD_INTRIN_DEF(round_s32_f32) +#if NPY_SIMD_F64 + SIMD_INTRIN_DEF(round_s32_f64) +#endif + +/************************************************************************* + * A special section for boolean intrinsics outside the main repeater + ************************************************************************/ +/*************************** + * Operators + ***************************/ +// Logical +/**begin repeat + * #bsfx = b8, b16, b32, b64# + */ +SIMD_INTRIN_DEF(and_@bsfx@) +SIMD_INTRIN_DEF(or_@bsfx@) +SIMD_INTRIN_DEF(xor_@bsfx@) +SIMD_INTRIN_DEF(not_@bsfx@) +/**end repeat**/ +/*************************** + * Conversions + ***************************/ +// Convert mask vector to integer bitfield +/**begin repeat + * #bsfx = b8, b16, b32, b64# + */ +SIMD_INTRIN_DEF(tobits_@bsfx@) +/**end repeat**/ + +/************************************************************************/ +{NULL, NULL, 0, NULL} +}; // PyMethodDef + +#endif // NPY_SIMD + +//######################################################################### +//## Defining a separate module for each target +//######################################################################### +NPY_VISIBILITY_HIDDEN PyObject * +NPY_CPU_DISPATCH_CURFX(simd_create_module)(void) +{ + static struct PyModuleDef defs = { + .m_base = PyModuleDef_HEAD_INIT, + .m_size = -1, + #ifdef NPY__CPU_TARGET_CURRENT + .m_name = "numpy.core._simd." NPY_TOSTRING(NPY__CPU_TARGET_CURRENT), + #else + .m_name = "numpy.core._simd.baseline", + #endif + #if NPY_SIMD + .m_methods = simd__intrinsics_methods + #else + .m_methods = NULL + #endif + }; + PyObject *m = PyModule_Create(&defs); + if (m == NULL) { + return NULL; + } + if (PyModule_AddIntConstant(m, "simd", NPY_SIMD)) { + goto err; + } + if (PyModule_AddIntConstant(m, "simd_f64", NPY_SIMD_F64)) { + goto err; + } + if (PyModule_AddIntConstant(m, "simd_fma3", NPY_SIMD_FMA3)) { + goto err; + } + if (PyModule_AddIntConstant(m, "simd_width", NPY_SIMD_WIDTH)) { + goto err; + } +#if NPY_SIMD + if (PySIMDVectorType_Init(m)) { + goto err; + } + /**begin repeat + * #sfx = u8, s8, u16, s16, u32, s32, u64, s64, f32, f64# + */ + if (PyModule_AddIntConstant(m, "nlanes_@sfx@", npyv_nlanes_@sfx@)) { + goto err; + } + /**end repeat**/ +#endif // NPY_SIMD + return m; +err: + Py_DECREF(m); + return NULL; +} diff --git a/numpy/core/src/_simd/_simd.h b/numpy/core/src/_simd/_simd.h new file mode 100644 index 000000000000..d9905c80127c --- /dev/null +++ b/numpy/core/src/_simd/_simd.h @@ -0,0 +1,30 @@ +/** + * A module to expose the NumPy C SIMD vectorization interface "NPYV" for testing purposes. + * + * Please keep this module independent from other c-extension modules, + * since NPYV intrinsics may be involved in their functionality, + * which increases the degree of complexity in tracking and detecting errors. + * + * TODO: Add an independent sphinx doc. + * + * Please add any new NPYV intrinsics in '_simd.dispatch.c.src'. + */ +#ifndef _SIMD_SIMD_H_ +#define _SIMD_SIMD_H_ + +#include <Python.h> +#include "numpy/npy_common.h" + +#ifndef NPY_DISABLE_OPTIMIZATION +// autogenerated, required for CPU dispatch macros +#include "_simd.dispatch.h" +#endif +/** + * Create a new module for each required optimization which contains all NPYV intrinsics, + * + * If required optimization is not supported by NPYV, the module will still provides + * access to NPYV constants NPY_SIMD, NPY_SIMD_F64, and NPY_SIMD_WIDTH but without + * any intrinsics. + */ +NPY_CPU_DISPATCH_DECLARE(NPY_VISIBILITY_HIDDEN PyObject *simd_create_module, (void)) +#endif // _SIMD_SIMD_H_ diff --git a/numpy/core/src/_simd/_simd_arg.inc b/numpy/core/src/_simd/_simd_arg.inc new file mode 100644 index 000000000000..f5bcf5487c65 --- /dev/null +++ b/numpy/core/src/_simd/_simd_arg.inc @@ -0,0 +1,85 @@ +/** + * This file is included by `_simd.dispatch.c.src`. Its contents are affected by the simd configuration, and + * therefore must be built multiple times. Making it a standalone `.c` file with `NPY_VISIBILITY_HIDDEN` + * symbols would require judicious use of `NPY_CPU_DISPATCH_DECLARE` and `NPY_CPU_DISPATCH_CURFX`, which was + * deemed too harmful to readability. + */ +/************************************ + ** Protected Definitions + ************************************/ +static int +simd_arg_from_obj(PyObject *obj, simd_arg *arg) +{ + assert(arg->dtype != 0); + const simd_data_info *info = simd_data_getinfo(arg->dtype); + if (info->is_scalar) { + arg->data = simd_scalar_from_number(obj, arg->dtype); + } + else if (info->is_sequence) { + unsigned min_seq_size = simd_data_getinfo(info->to_vector)->nlanes; + arg->data.qu8 = simd_sequence_from_iterable(obj, arg->dtype, min_seq_size); + } + else if (info->is_vectorx) { + arg->data = simd_vectorx_from_tuple(obj, arg->dtype); + } + else if (info->is_vector) { + arg->data = PySIMDVector_AsData((PySIMDVectorObject*)obj, arg->dtype); + } else { + arg->data.u64 = 0; + PyErr_Format(PyExc_RuntimeError, + "unhandled arg from obj type id:%d, name:%s", arg->dtype, info->pyname + ); + return -1; + } + if (PyErr_Occurred()) { + return -1; + } + return 0; +} + +static PyObject * +simd_arg_to_obj(const simd_arg *arg) +{ + assert(arg->dtype != 0); + const simd_data_info *info = simd_data_getinfo(arg->dtype); + if (info->is_scalar) { + return simd_scalar_to_number(arg->data, arg->dtype); + } + if (info->is_sequence) { + return simd_sequence_to_list(arg->data.qu8, arg->dtype); + } + if (info->is_vectorx) { + return simd_vectorx_to_tuple(arg->data, arg->dtype); + } + if (info->is_vector) { + return (PyObject*)PySIMDVector_FromData(arg->data, arg->dtype); + } + PyErr_Format(PyExc_RuntimeError, + "unhandled arg to object type id:%d, name:%s", arg->dtype, info->pyname + ); + return NULL; +} + +static void +simd_arg_free(simd_arg *arg) +{ + const simd_data_info *info = simd_data_getinfo(arg->dtype); + if (info->is_sequence) { + simd_sequence_free(arg->data.qu8); + } +} + +static int +simd_arg_converter(PyObject *obj, simd_arg *arg) +{ + if (obj != NULL) { + if (simd_arg_from_obj(obj, arg) < 0) { + return 0; + } + arg->obj = obj; + return Py_CLEANUP_SUPPORTED; + } else { + simd_arg_free(arg); + } + return 1; +} diff --git a/numpy/core/src/_simd/_simd_convert.inc b/numpy/core/src/_simd/_simd_convert.inc new file mode 100644 index 000000000000..46e044479a56 --- /dev/null +++ b/numpy/core/src/_simd/_simd_convert.inc @@ -0,0 +1,211 @@ +/** + * This file is included by `_simd.dispatch.c.src`. Its contents are affected by the simd configuration, and + * therefore must be built multiple times. Making it a standalone `.c` file with `NPY_VISIBILITY_HIDDEN` + * symbols would require judicious use of `NPY_CPU_DISPATCH_DECLARE` and `NPY_CPU_DISPATCH_CURFX`, which was + * deemed too harmful to readability. + */ +/************************************ + ** Protected Definitions + ************************************/ +static simd_data +simd_scalar_from_number(PyObject *obj, simd_data_type dtype) +{ + const simd_data_info *info = simd_data_getinfo(dtype); + assert(info->is_scalar && info->lane_size > 0); + simd_data data; + if (info->is_float) { + data.f64 = PyFloat_AsDouble(obj); + if (dtype == simd_data_f32){ + data.f32 = (float)data.f64; + } + } else { + data.u64 = PyLong_AsUnsignedLongLongMask(obj); + } + return data; +} + +static PyObject * +simd_scalar_to_number(simd_data data, simd_data_type dtype) +{ + const simd_data_info *info = simd_data_getinfo(dtype); + assert(info->is_scalar && info->lane_size > 0); + if (info->is_float) { + if (dtype == simd_data_f32) { + return PyFloat_FromDouble(data.f32); + } + return PyFloat_FromDouble(data.f64); + } + int leftb = (sizeof(npyv_lanetype_u64) - info->lane_size) * 8; + data.u64 <<= leftb; + if (info->is_signed) { + return PyLong_FromLongLong(data.s64 >> leftb); + } + return PyLong_FromUnsignedLongLong(data.u64 >> leftb); +} + +typedef struct { + Py_ssize_t len; + void *ptr; +} simd__alloc_data; + +static void * +simd_sequence_new(Py_ssize_t len, simd_data_type dtype) +{ + const simd_data_info *info = simd_data_getinfo(dtype); + assert(len > 0 && info->is_sequence && info->lane_size > 0); + size_t size = sizeof(simd__alloc_data) + len * info->lane_size + NPY_SIMD_WIDTH; + void *ptr = malloc(size); + if (ptr == NULL) { + return PyErr_NoMemory(); + } + // align the pointer + simd__alloc_data *a_ptr = (simd__alloc_data *)( + ((uintptr_t)ptr + sizeof(simd__alloc_data) + NPY_SIMD_WIDTH) & ~(uintptr_t)(NPY_SIMD_WIDTH-1) + ); + a_ptr[-1].len = len; + a_ptr[-1].ptr = ptr; + return a_ptr; +} + +static Py_ssize_t +simd_sequence_len(void const *ptr) +{ + return ((simd__alloc_data const*)ptr)[-1].len; +} + +static void +simd_sequence_free(void *ptr) +{ + free(((simd__alloc_data *)ptr)[-1].ptr); +} + +static void * +simd_sequence_from_iterable(PyObject *obj, simd_data_type dtype, Py_ssize_t min_size) +{ + const simd_data_info *info = simd_data_getinfo(dtype); + assert(info->is_sequence && info->lane_size > 0); + PyObject *seq_obj = PySequence_Fast(obj, "expected a sequence"); + if (seq_obj == NULL) { + return NULL; + } + Py_ssize_t seq_size = PySequence_Fast_GET_SIZE(seq_obj); + if (seq_size < min_size) { + PyErr_Format(PyExc_ValueError, + "minimum acceptable size of the required sequence is %d, given(%d)", + min_size, seq_size + ); + Py_DECREF(seq_obj); + return NULL; + } + npyv_lanetype_u8 *dst = simd_sequence_new(seq_size, dtype); + if (dst == NULL) { + return NULL; + } + PyObject **seq_items = PySequence_Fast_ITEMS(seq_obj); + for (Py_ssize_t i = 0; i < seq_size; ++i) { + simd_data data = simd_scalar_from_number(seq_items[i], info->to_scalar); + npyv_lanetype_u8 *sdst = dst + i * info->lane_size; + memcpy(sdst, &data.u64, info->lane_size); + } + Py_DECREF(seq_obj); + + if (PyErr_Occurred()) { + simd_sequence_free(dst); + return NULL; + } + return dst; +} + +static int +simd_sequence_fill_iterable(PyObject *obj, const void *ptr, simd_data_type dtype) +{ + const simd_data_info *info = simd_data_getinfo(dtype); + if (!PySequence_Check(obj)) { + PyErr_Format(PyExc_TypeError, + "a sequence object is required to fill %s", info->pyname + ); + return -1; + } + const npyv_lanetype_u8 *src = ptr; + Py_ssize_t seq_len = simd_sequence_len(ptr); + for (Py_ssize_t i = 0; i < seq_len; ++i) { + const npyv_lanetype_u8 *ssrc = src + i * info->lane_size; + simd_data data; + memcpy(&data.u64, ssrc, info->lane_size); + PyObject *item = simd_scalar_to_number(data, info->to_scalar); + if (item == NULL) { + return -1; + } + int res = PySequence_SetItem(obj, i, item); + Py_DECREF(item); + if (res < 0) { + return -1; + } + } + return 0; +} + +static PyObject * +simd_sequence_to_list(const void *ptr, simd_data_type dtype) +{ + PyObject *list = PyList_New(simd_sequence_len(ptr)); + if (list == NULL) { + return NULL; + } + if (simd_sequence_fill_iterable(list, ptr, dtype) < 0) { + Py_DECREF(list); + return NULL; + } + return list; +} + +static simd_data +simd_vectorx_from_tuple(PyObject *obj, simd_data_type dtype) +{ + const simd_data_info *info = simd_data_getinfo(dtype); + // NPYV currently only supports x2 and x3 + assert(info->is_vectorx > 1 && info->is_vectorx < 4); + + simd_data data = {.u64 = 0}; + if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != info->is_vectorx) { + PyErr_Format(PyExc_TypeError, + "a tuple of %d vector type %s is required", + info->is_vectorx, simd_data_getinfo(info->to_vector)->pyname + ); + return data; + } + for (int i = 0; i < info->is_vectorx; ++i) { + PyObject *item = PyTuple_GET_ITEM(obj, i); + // get the max multi-vec and let the compiler do the rest + data.vu64x3.val[i] = PySIMDVector_AsData((PySIMDVectorObject*)item, info->to_vector).vu64; + if (PyErr_Occurred()) { + return data; + } + } + return data; +} + +static PyObject * +simd_vectorx_to_tuple(simd_data data, simd_data_type dtype) +{ + const simd_data_info *info = simd_data_getinfo(dtype); + // NPYV currently only supports x2 and x3 + assert(info->is_vectorx > 1 && info->is_vectorx < 4); + + PyObject *tuple = PyTuple_New(info->is_vectorx); + if (tuple == NULL) { + return NULL; + } + for (int i = 0; i < info->is_vectorx; ++i) { + // get the max multi-vector and let the compiler handle the rest + simd_data vdata = {.vu64 = data.vu64x3.val[i]}; + PyObject *item = (PyObject*)PySIMDVector_FromData(vdata, info->to_vector); + if (item == NULL) { + // TODO: improve log add item number + Py_DECREF(tuple); + return NULL; + } + PyTuple_SET_ITEM(tuple, i, item); + } + return tuple; +} diff --git a/numpy/core/src/_simd/_simd_data.inc.src b/numpy/core/src/_simd/_simd_data.inc.src new file mode 100644 index 000000000000..5c796487c923 --- /dev/null +++ b/numpy/core/src/_simd/_simd_data.inc.src @@ -0,0 +1,93 @@ +/** + * This file is included by `_simd.dispatch.c.src`. Its contents are affected by the simd configuration, and + * therefore must be built multiple times. Making it a standalone `.c` file with `NPY_VISIBILITY_HIDDEN` + * symbols would require judicious use of `NPY_CPU_DISPATCH_DECLARE` and `NPY_CPU_DISPATCH_CURFX`, which was + * deemed too harmful to readability. + */ +/************************************ + ** Private Definitions + ************************************/ +static simd_data_info simd__data_registry[simd_data_end] = +{ + [simd_data_none] = {.pyname="none"}, + /**begin repeat + * #sfx = u8, u16, u32, u64, s8, s16, s32, s64, f32, f64# + * #sig = 0*4, 1*4, 0*2# + * #fp = 0*4, 0*4, 1*2# + * #name = int*8, float, float# + */ + [simd_data_@sfx@] = { + .pyname="@name@", .is_unsigned=!@sig@&&!@fp@, .is_signed=@sig@, .is_float=@fp@, + .is_scalar=1, .to_scalar = simd_data_@sfx@, .to_vector = simd_data_v@sfx@, + .lane_size = sizeof(npyv_lanetype_@sfx@) + }, + /**end repeat**/ + // sequences + /**begin repeat + * #sfx = u8, u16, u32, u64, s8, s16, s32, s64, f32, f64# + * #sig = 0*4, 1*4, 0*2# + * #fp = 0*4, 0*4, 1*2# + * #name = int*8, float, float# + */ + [simd_data_q@sfx@] = { + .pyname="[@name@]", .is_unsigned=!@sig@&&!@fp@, .is_signed=@sig@, .is_float=@fp@, + .is_sequence=1, .to_scalar = simd_data_@sfx@, .to_vector = simd_data_v@sfx@, + .nlanes=npyv_nlanes_@sfx@, .lane_size = sizeof(npyv_lanetype_@sfx@) + }, + /**end repeat**/ + // vectors + /**begin repeat + * #sfx = u8, u16, u32, u64, s8, s16, s32, s64, f32, f64# + * #sig = 0*4, 1*4, 0*2# + * #fp = 0*4, 0*4, 1*2# + */ + [simd_data_v@sfx@] = { + .pyname="npyv_@sfx@", .is_unsigned=!@sig@&&!@fp@, .is_signed=@sig@, .is_float=@fp@, + .is_vector=1, .to_scalar = simd_data_@sfx@, .to_vector = simd_data_v@sfx@, + .nlanes=npyv_nlanes_@sfx@, .lane_size = sizeof(npyv_lanetype_@sfx@) + }, + /**end repeat**/ + // boolean vectors, treated as unsigned and converted internally + // to add compatibility among all SIMD extensions + /**begin repeat + * #sfx = u8, u16, u32, u64# + * #bsfx = b8, b16, b32, b64# + */ + [simd_data_v@bsfx@] = { + .pyname="npyv_@bsfx@", .is_bool=1, .is_vector=1, + .to_scalar = simd_data_@sfx@, .to_vector = simd_data_v@sfx@, + .nlanes=npyv_nlanes_@sfx@, .lane_size = sizeof(npyv_lanetype_@sfx@) + }, + /**end repeat**/ + // multi-vectors x2 + /**begin repeat + * #sfx = u8, u16, u32, u64, s8, s16, s32, s64, f32, f64# + * #sig = 0*4, 1*4, 0*2# + * #fp = 0*4, 0*4, 1*2# + */ + [simd_data_v@sfx@x2] = { + .pyname="npyv_@sfx@x2", .is_unsigned=!@sig@&&!@fp@, .is_signed=@sig@, .is_float=@fp@, + .is_vectorx=2, .to_scalar = simd_data_@sfx@, .to_vector = simd_data_v@sfx@, + .nlanes=2, .lane_size = sizeof(npyv_lanetype_@sfx@) + }, + /**end repeat**/ + // multi-vectors x3 + /**begin repeat + * #sfx = u8, u16, u32, u64, s8, s16, s32, s64, f32, f64# + * #sig = 0*4, 1*4, 0*2# + * #fp = 0*4, 0*4, 1*2# + */ + [simd_data_v@sfx@x3] = { + .pyname="npyv_@sfx@x3", .is_unsigned=!@sig@&&!@fp@, .is_signed=@sig@, .is_float=@fp@, + .is_vectorx=3, .to_scalar = simd_data_@sfx@, .to_vector = simd_data_v@sfx@, + .nlanes=3, .lane_size = sizeof(npyv_lanetype_@sfx@) + }, + /**end repeat**/ +}; + +/************************************ + ** Protected Definitions + ************************************/ +static const simd_data_info * +simd_data_getinfo(simd_data_type dtype) +{ return &simd__data_registry[dtype]; } diff --git a/numpy/core/src/_simd/_simd_easyintrin.inc b/numpy/core/src/_simd/_simd_easyintrin.inc new file mode 100644 index 000000000000..4521b2d87f07 --- /dev/null +++ b/numpy/core/src/_simd/_simd_easyintrin.inc @@ -0,0 +1,244 @@ +/** + * This file is included by `_simd.dispatch.c.src`. Its contents are affected by the simd configuration, and + * therefore must be built multiple times. Making it a standalone `.c` file with `NPY_VISIBILITY_HIDDEN` + * symbols would require judicious use of `NPY_CPU_DISPATCH_DECLARE` and `NPY_CPU_DISPATCH_CURFX`, which was + * deemed too harmful to readability. + */ +#define SIMD_INTRIN_DEF(NAME) \ + { NPY_TOSTRING(NAME), simd__intrin_##NAME, METH_VARARGS, NULL } , // comma + +#define SIMD_IMPL_INTRIN_0(NAME, RET) \ + static PyObject *simd__intrin_##NAME \ + (PyObject* NPY_UNUSED(self), PyObject *args) \ + { \ + if (!PyArg_ParseTuple( \ + args, ":" NPY_TOSTRING(NAME)) \ + ) return NULL; \ + simd_arg a = { \ + .dtype = simd_data_##RET, \ + .data = {.RET = npyv_##NAME()}, \ + }; \ + return simd_arg_to_obj(&a); \ + } + +#define SIMD_IMPL_INTRIN_0N(NAME) \ + static PyObject *simd__intrin_##NAME \ + (PyObject* NPY_UNUSED(self), PyObject *args) \ + { \ + if (!PyArg_ParseTuple( \ + args, ":" NPY_TOSTRING(NAME)) \ + ) return NULL; \ + npyv_##NAME(); \ + Py_RETURN_NONE; \ + } + +#define SIMD_IMPL_INTRIN_1(NAME, RET, IN0) \ + static PyObject *simd__intrin_##NAME \ + (PyObject* NPY_UNUSED(self), PyObject *args) \ + { \ + simd_arg arg = {.dtype = simd_data_##IN0}; \ + if (!PyArg_ParseTuple( \ + args, "O&:"NPY_TOSTRING(NAME), \ + simd_arg_converter, &arg \ + )) return NULL; \ + simd_data data = {.RET = npyv_##NAME( \ + arg.data.IN0 \ + )}; \ + simd_arg_free(&arg); \ + simd_arg ret = { \ + .data = data, .dtype = simd_data_##RET \ + }; \ + return simd_arg_to_obj(&ret); \ + } + +#define SIMD_IMPL_INTRIN_2(NAME, RET, IN0, IN1) \ + static PyObject *simd__intrin_##NAME \ + (PyObject* NPY_UNUSED(self), PyObject *args) \ + { \ + simd_arg arg1 = {.dtype = simd_data_##IN0}; \ + simd_arg arg2 = {.dtype = simd_data_##IN1}; \ + if (!PyArg_ParseTuple( \ + args, "O&O&:"NPY_TOSTRING(NAME), \ + simd_arg_converter, &arg1, \ + simd_arg_converter, &arg2 \ + )) return NULL; \ + simd_data data = {.RET = npyv_##NAME( \ + arg1.data.IN0, arg2.data.IN1 \ + )}; \ + simd_arg_free(&arg1); \ + simd_arg_free(&arg2); \ + simd_arg ret = { \ + .data = data, .dtype = simd_data_##RET \ + }; \ + return simd_arg_to_obj(&ret); \ + } + +#define SIMD__REPEAT_2IMM(C, NAME, IN0) \ + C == arg2.data.u8 ? NPY_CAT(npyv_, NAME)(arg1.data.IN0, C) : + +#define SIMD_IMPL_INTRIN_2IMM(NAME, RET, IN0, CONST_RNG) \ + static PyObject *simd__intrin_##NAME \ + (PyObject* NPY_UNUSED(self), PyObject *args) \ + { \ + simd_arg arg1 = {.dtype = simd_data_##IN0}; \ + simd_arg arg2 = {.dtype = simd_data_u8}; \ + if (!PyArg_ParseTuple( \ + args, "O&O&:"NPY_TOSTRING(NAME), \ + simd_arg_converter, &arg1, \ + simd_arg_converter, &arg2 \ + )) return NULL; \ + simd_data data = {.u64 = 0}; \ + data.RET = NPY_CAT(SIMD__IMPL_COUNT_, CONST_RNG)( \ + SIMD__REPEAT_2IMM, NAME, IN0 \ + ) data.RET; \ + simd_arg_free(&arg1); \ + simd_arg ret = { \ + .data = data, .dtype = simd_data_##RET \ + }; \ + return simd_arg_to_obj(&ret); \ + } + +#define SIMD_IMPL_INTRIN_3(NAME, RET, IN0, IN1, IN2) \ + static PyObject *simd__intrin_##NAME \ + (PyObject* NPY_UNUSED(self), PyObject *args) \ + { \ + simd_arg arg1 = {.dtype = simd_data_##IN0}; \ + simd_arg arg2 = {.dtype = simd_data_##IN1}; \ + simd_arg arg3 = {.dtype = simd_data_##IN2}; \ + if (!PyArg_ParseTuple( \ + args, "O&O&O&:"NPY_TOSTRING(NAME), \ + simd_arg_converter, &arg1, \ + simd_arg_converter, &arg2, \ + simd_arg_converter, &arg3 \ + )) return NULL; \ + simd_data data = {.RET = npyv_##NAME( \ + arg1.data.IN0, arg2.data.IN1, \ + arg3.data.IN2 \ + )}; \ + simd_arg_free(&arg1); \ + simd_arg_free(&arg2); \ + simd_arg_free(&arg3); \ + simd_arg ret = { \ + .data = data, .dtype = simd_data_##RET \ + }; \ + return simd_arg_to_obj(&ret); \ + } + +#define SIMD_IMPL_INTRIN_4(NAME, RET, IN0, IN1, IN2, IN3) \ + static PyObject *simd__intrin_##NAME \ + (PyObject* NPY_UNUSED(self), PyObject *args) \ + { \ + simd_arg arg1 = {.dtype = simd_data_##IN0}; \ + simd_arg arg2 = {.dtype = simd_data_##IN1}; \ + simd_arg arg3 = {.dtype = simd_data_##IN2}; \ + simd_arg arg4 = {.dtype = simd_data_##IN3}; \ + if (!PyArg_ParseTuple( \ + args, "O&O&O&O&:"NPY_TOSTRING(NAME), \ + simd_arg_converter, &arg1, \ + simd_arg_converter, &arg2, \ + simd_arg_converter, &arg3, \ + simd_arg_converter, &arg4 \ + )) return NULL; \ + simd_data data = {.RET = npyv_##NAME( \ + arg1.data.IN0, arg2.data.IN1, \ + arg3.data.IN2, arg4.data.IN3 \ + )}; \ + simd_arg_free(&arg1); \ + simd_arg_free(&arg2); \ + simd_arg_free(&arg3); \ + simd_arg_free(&arg4); \ + simd_arg ret = { \ + .data = data, .dtype = simd_data_##RET \ + }; \ + return simd_arg_to_obj(&ret); \ + } + +/** + * Helper macros for repeating and expand a certain macro. + * Mainly used for converting a scalar to an immediate constant. + */ +#define SIMD__IMPL_COUNT_7(FN, ...) \ + NPY_EXPAND(FN(0, __VA_ARGS__)) \ + SIMD__IMPL_COUNT_7_(FN, __VA_ARGS__) + +#define SIMD__IMPL_COUNT_8(FN, ...) \ + SIMD__IMPL_COUNT_7_(FN, __VA_ARGS__) \ + NPY_EXPAND(FN(8, __VA_ARGS__)) + +#define SIMD__IMPL_COUNT_15(FN, ...) \ + NPY_EXPAND(FN(0, __VA_ARGS__)) \ + SIMD__IMPL_COUNT_15_(FN, __VA_ARGS__) + +#define SIMD__IMPL_COUNT_16(FN, ...) \ + SIMD__IMPL_COUNT_15_(FN, __VA_ARGS__) \ + NPY_EXPAND(FN(16, __VA_ARGS__)) + +#define SIMD__IMPL_COUNT_31(FN, ...) \ + NPY_EXPAND(FN(0, __VA_ARGS__)) \ + SIMD__IMPL_COUNT_31_(FN, __VA_ARGS__) + +#define SIMD__IMPL_COUNT_32(FN, ...) \ + SIMD__IMPL_COUNT_31_(FN, __VA_ARGS__) \ + NPY_EXPAND(FN(32, __VA_ARGS__)) + +#define SIMD__IMPL_COUNT_47(FN, ...) \ + NPY_EXPAND(FN(0, __VA_ARGS__)) \ + SIMD__IMPL_COUNT_47_(FN, __VA_ARGS__) + +#define SIMD__IMPL_COUNT_48(FN, ...) \ + SIMD__IMPL_COUNT_47_(FN, __VA_ARGS__) \ + NPY_EXPAND(FN(48, __VA_ARGS__)) + +#define SIMD__IMPL_COUNT_63(FN, ...) \ + NPY_EXPAND(FN(0, __VA_ARGS__)) \ + SIMD__IMPL_COUNT_63_(FN, __VA_ARGS__) + +#define SIMD__IMPL_COUNT_64(FN, ...) \ + SIMD__IMPL_COUNT_63_(FN, __VA_ARGS__) \ + NPY_EXPAND(FN(64, __VA_ARGS__)) + +#define SIMD__IMPL_COUNT_7_(FN, ...) \ + NPY_EXPAND(FN(1, __VA_ARGS__)) \ + NPY_EXPAND(FN(2, __VA_ARGS__)) NPY_EXPAND(FN(3, __VA_ARGS__)) \ + NPY_EXPAND(FN(4, __VA_ARGS__)) NPY_EXPAND(FN(5, __VA_ARGS__)) \ + NPY_EXPAND(FN(6, __VA_ARGS__)) NPY_EXPAND(FN(7, __VA_ARGS__)) + +#define SIMD__IMPL_COUNT_15_(FN, ...) \ + SIMD__IMPL_COUNT_7_(FN, __VA_ARGS__) \ + NPY_EXPAND(FN(8, __VA_ARGS__)) NPY_EXPAND(FN(9, __VA_ARGS__)) \ + NPY_EXPAND(FN(10, __VA_ARGS__)) NPY_EXPAND(FN(11, __VA_ARGS__)) \ + NPY_EXPAND(FN(12, __VA_ARGS__)) NPY_EXPAND(FN(13, __VA_ARGS__)) \ + NPY_EXPAND(FN(14, __VA_ARGS__)) NPY_EXPAND(FN(15, __VA_ARGS__)) + +#define SIMD__IMPL_COUNT_31_(FN, ...) \ + SIMD__IMPL_COUNT_15_(FN, __VA_ARGS__) \ + NPY_EXPAND(FN(16, __VA_ARGS__)) NPY_EXPAND(FN(17, __VA_ARGS__)) \ + NPY_EXPAND(FN(18, __VA_ARGS__)) NPY_EXPAND(FN(19, __VA_ARGS__)) \ + NPY_EXPAND(FN(20, __VA_ARGS__)) NPY_EXPAND(FN(21, __VA_ARGS__)) \ + NPY_EXPAND(FN(22, __VA_ARGS__)) NPY_EXPAND(FN(23, __VA_ARGS__)) \ + NPY_EXPAND(FN(24, __VA_ARGS__)) NPY_EXPAND(FN(25, __VA_ARGS__)) \ + NPY_EXPAND(FN(26, __VA_ARGS__)) NPY_EXPAND(FN(27, __VA_ARGS__)) \ + NPY_EXPAND(FN(28, __VA_ARGS__)) NPY_EXPAND(FN(29, __VA_ARGS__)) \ + NPY_EXPAND(FN(30, __VA_ARGS__)) NPY_EXPAND(FN(31, __VA_ARGS__)) + +#define SIMD__IMPL_COUNT_47_(FN, ...) \ + SIMD__IMPL_COUNT_31_(FN, __VA_ARGS__) \ + NPY_EXPAND(FN(32, __VA_ARGS__)) NPY_EXPAND(FN(33, __VA_ARGS__)) \ + NPY_EXPAND(FN(34, __VA_ARGS__)) NPY_EXPAND(FN(35, __VA_ARGS__)) \ + NPY_EXPAND(FN(36, __VA_ARGS__)) NPY_EXPAND(FN(37, __VA_ARGS__)) \ + NPY_EXPAND(FN(38, __VA_ARGS__)) NPY_EXPAND(FN(39, __VA_ARGS__)) \ + NPY_EXPAND(FN(40, __VA_ARGS__)) NPY_EXPAND(FN(41, __VA_ARGS__)) \ + NPY_EXPAND(FN(42, __VA_ARGS__)) NPY_EXPAND(FN(43, __VA_ARGS__)) \ + NPY_EXPAND(FN(44, __VA_ARGS__)) NPY_EXPAND(FN(45, __VA_ARGS__)) \ + NPY_EXPAND(FN(46, __VA_ARGS__)) NPY_EXPAND(FN(47, __VA_ARGS__)) + +#define SIMD__IMPL_COUNT_63_(FN, ...) \ + SIMD__IMPL_COUNT_47_(FN, __VA_ARGS__) \ + NPY_EXPAND(FN(48, __VA_ARGS__)) NPY_EXPAND(FN(49, __VA_ARGS__)) \ + NPY_EXPAND(FN(50, __VA_ARGS__)) NPY_EXPAND(FN(51, __VA_ARGS__)) \ + NPY_EXPAND(FN(52, __VA_ARGS__)) NPY_EXPAND(FN(53, __VA_ARGS__)) \ + NPY_EXPAND(FN(54, __VA_ARGS__)) NPY_EXPAND(FN(55, __VA_ARGS__)) \ + NPY_EXPAND(FN(56, __VA_ARGS__)) NPY_EXPAND(FN(57, __VA_ARGS__)) \ + NPY_EXPAND(FN(58, __VA_ARGS__)) NPY_EXPAND(FN(59, __VA_ARGS__)) \ + NPY_EXPAND(FN(60, __VA_ARGS__)) NPY_EXPAND(FN(61, __VA_ARGS__)) \ + NPY_EXPAND(FN(62, __VA_ARGS__)) NPY_EXPAND(FN(63, __VA_ARGS__)) diff --git a/numpy/core/src/_simd/_simd_inc.h.src b/numpy/core/src/_simd/_simd_inc.h.src new file mode 100644 index 000000000000..fbdf982c2984 --- /dev/null +++ b/numpy/core/src/_simd/_simd_inc.h.src @@ -0,0 +1,421 @@ +#ifndef _SIMD_SIMD_INC_H_ +#define _SIMD_SIMD_INC_H_ + +#include <Python.h> +#include "simd/simd.h" + +#if NPY_SIMD +/************************************ + ** Types + ************************************/ +/** + * Gather all data types supported by the module. +*/ +typedef union +{ + // scalars + /**begin repeat + * #sfx = u8, u16, u32, u64, s8, s16, s32, s64, f32, f64# + */ + npyv_lanetype_@sfx@ @sfx@; + /**end repeat**/ + // sequence + /**begin repeat + * #sfx = u8, u16, u32, u64, s8, s16, s32, s64, f32, f64# + */ + npyv_lanetype_@sfx@ *q@sfx@; + /**end repeat**/ + // vectors + /**begin repeat + * #sfx = u8, u16, u32, u64, s8, s16, s32, s64, f32, b8, b16, b32, b64# + */ + npyv_@sfx@ v@sfx@; + /**end repeat**/ + // multi-vectors x2 + /**begin repeat + * #sfx = u8, u16, u32, u64, s8, s16, s32, s64, f32# + */ + npyv_@sfx@x2 v@sfx@x2; + /**end repeat**/ + // multi-vectors x3 + /**begin repeat + * #sfx = u8, u16, u32, u64, s8, s16, s32, s64, f32# + */ + npyv_@sfx@x3 v@sfx@x3; + /**end repeat**/ +#if NPY_SIMD_F64 + npyv_f64 vf64; + npyv_f64x2 vf64x2; + npyv_f64x3 vf64x3; +#endif +} simd_data; + +/** + * Data types IDs and suffixes. Must be same data types as the ones + * in union 'simd_data' to fit the macros in '_simd_inc_easyintrin.h'. +*/ +typedef enum +{ + simd_data_none = 0, + // scalars + /**begin repeat + * #sfx = u8, u16, u32, u64, s8, s16, s32, s64, f32, f64# + */ + simd_data_@sfx@, + /**end repeat**/ + // sequences + /**begin repeat + * #sfx = u8, u16, u32, u64, s8, s16, s32, s64, f32, f64# + */ + simd_data_q@sfx@, + /**end repeat**/ + // vectors + /**begin repeat + * #sfx = u8, u16, u32, u64, s8, s16, s32, s64, f32, f64, b8, b16, b32, b64# + */ + simd_data_v@sfx@, + /**end repeat**/ + // multi-vectors x2 + /**begin repeat + * #sfx = u8, u16, u32, u64, s8, s16, s32, s64, f32, f64# + */ + simd_data_v@sfx@x2, + /**end repeat**/ + // multi-vectors x3 + /**begin repeat + * #sfx = u8, u16, u32, u64, s8, s16, s32, s64, f32, f64# + */ + simd_data_v@sfx@x3, + /**end repeat**/ + simd_data_end, +} simd_data_type; +/************************************ + ** Declarations (inc_data) + ************************************/ +/** + * simd_data_type information + */ +typedef struct +{ + // type name compatible with python style + const char *pyname; + // returns '1' if the type represent a unsigned integer + int is_unsigned:1; + // returns '1' if the type represent a signed integer + int is_signed:1; + // returns '1' if the type represent a single or double precision + int is_float:1; + // returns '1' if the type represent a boolean + int is_bool:1; + // returns '1' if the type represent a sequence + int is_sequence:1; + // returns '1' if the type represent a scalar + int is_scalar:1; + // returns '1' if the type represent a vector + int is_vector:1; + // returns the len of multi-vector if the type represent x2 or x3 vector + // otherwise returns 0, e.g. returns 2 if data type is simd_data_vu8x2 + int is_vectorx; + // returns the equivalent scalar data type e.g. simd_data_vu8 -> simd_data_u8 + simd_data_type to_scalar; + // returns the equivalent scalar data type e.g. simd_data_s8 -> simd_data_vs8 + // NOTE: returns the will equivalent "unsigned" vector type in case of "boolean" vector + // e.g. simd_data_vb8 -> simd_data_vu8 + simd_data_type to_vector; + // number of vector lanes + int nlanes; + // sizeof lane type + int lane_size; +} simd_data_info; + +/** + * Returns data info of certain dtype. + * + * Example: + ** const simd_data_info *info = simd_data_getinfo(simd_data_vu8); + ** if (info->is_vector && info->is_unsigned) { + ** ... + ** } + */ +static const simd_data_info * +simd_data_getinfo(simd_data_type dtype); + +/************************************ + ** Declarations (inc_vector) + ************************************/ +typedef struct +{ + PyObject_HEAD + // vector type id + simd_data_type dtype; + // vector data, aligned for safe casting + npyv_lanetype_u8 NPY_DECL_ALIGNED(NPY_SIMD_WIDTH) data[NPY_SIMD_WIDTH]; +} PySIMDVectorObject; +/** + * Create a Python obj(PySIMDVectorObject) from a NPYV vector based on the contents + * of `data`(simd_data) and according to the vector data type `dtype` + * on range(simd_data_[vu8:vf64]). + * Return NULL and a Python exception on failure, otherwise new reference. + * + * Example: + ** simd_data data = {.vu8 = npyv_setall_u8(0xff)}; + ** PySIMDVectorObject *obj = PySIMDVector_FromData(data, simd_data_vu8); + ** if (obj != NULL) { + ** printf("I have a valid vector obj and first element is \n", obj->data[0]); + ** Py_DECREF(obj); + ** } + */ +static PySIMDVectorObject * +PySIMDVector_FromData(simd_data data, simd_data_type dtype); +/** + * Return a NPYV vector(simd_data) representation of `obj`(PySIMDVectorObject) and + * according to the vector data type `dtype` on range (simd_data_[vu8:vf64]). + * Raise a Python exception on failure. + * + * Example: + ** simd_data data = PySIMDVector_AsData(vec_obj, simd_data_vf32); + ** if (!PyErr_Occurred()) { + ** npyv_f32 add_1 = npyv_add_f32(data.vf32, npyv_setall_f32(1)); + ** ... + ** } + */ +static simd_data +PySIMDVector_AsData(PySIMDVectorObject *obj, simd_data_type dtype); +/** + * initialize and register PySIMDVectorType to certain PyModule, + * PySIMDVectorType can be reached through attribute 'vector_type'. + * return -1 on error, 0 on success. + */ +static int +PySIMDVectorType_Init(PyObject *module); + +/************************************ + ** Declarations (inc_convert) + ************************************/ +/** + * Return a C scalar(simd_data) representation of `obj` and + * according to the scalar data type `dtype` on range (simd_data_[u8:f64]). + * Raise a Python exception on failure. + * + * Example: + ** simd_data data = simd_scalar_from_number(obj, simd_data_f32); + ** if (!PyErr_Occurred()) { + ** printf("I have a valid float %d\n", data.f32); + ** } + */ +static simd_data +simd_scalar_from_number(PyObject *obj, simd_data_type dtype); +/** + * Create a Python scalar from a C scalar based on the contents + * of `data`(simd_data) and according to the scalar data type `dtype` + * on range(simd_data_[u8:f64]). + * Return NULL and a Python exception on failure, otherwise new reference. + * + * Example: + ** simd_data data = {.u32 = 0x7fffffff}; + ** PyObject *obj = simd_scalar_to_number(data, simd_data_s32); + ** if (obj != NULL) { + ** printf("I have a valid Python integer %d\n", PyLong_AsLong(obj)); + ** Py_DECREF(obj); + ** } + */ +static PyObject * +simd_scalar_to_number(simd_data data, simd_data_type dtype); +/** + * Allocate a C array in memory according to number of elements `len` + * and sequence data type `dtype` on range(simd_data_[qu8:qf64]). + * + * Return aligned pointer based on `NPY_SIMD_WIDTH` or NULL + * with a Python exception on failure. + * + * Example: + ** npyv_lanetype_f64 *aligned_ptr = simd_sequence_new(npyv_nlanes_f64, simd_data_f64); + ** if (aligned_ptr != NULL) { + ** // aligned store + ** npyv_storea_f64(aligned_ptr, npyv_setall_f64(1.0)); + ** printf("The first element of my array %f\n", aligned_ptr[0]); + ** simd_sequence_free(aligned_ptr); + ** } + */ +static void * +simd_sequence_new(Py_ssize_t len, simd_data_type dtype); +/** + * Return the number of elements of the allocated C array `ptr` + * by `simd_sequence_new()` or `simd_sequence_from_iterable()`. + */ +static Py_ssize_t +simd_sequence_len(const void *ptr); +/** + * Free the allocated C array by `simd_sequence_new()` or + * `simd_sequence_from_iterable()`. + */ +static void +simd_sequence_free(void *ptr); +/** + * Return a C array representation of a PyObject sequence `obj` and + * according to the sequence data type `dtype` on range (simd_data_[qu8:qf64]). + * + * Note: parameter `min_size` takes the number of minimum acceptable elements. + * + * Return aligned pointer based on `NPY_SIMD_WIDTH` or NULL + * with a Python exception on failure. + * + * Example: + ** npyv_lanetype_u32 *ptr = simd_sequence_from_iterable(seq_obj, simd_data_qu32, npyv_nlanes_u32); + ** if (ptr != NULL) { + ** npyv_u32 a = npyv_load_u32(ptr); + ** ... + ** simd_sequence_free(ptr); + ** } + ** + */ +static void * +simd_sequence_from_iterable(PyObject *obj, simd_data_type dtype, Py_ssize_t min_size); +/** + * Fill a Python sequence object `obj` with a C array `ptr` allocated by + * `simd_sequence_new()` or `simd_sequence_from_iterable()` according to + * to the sequence data type `dtype` on range (simd_data_[qu8:qf64]). + * + * Return 0 on success or -1 with a Python exception on failure. + */ +static int +simd_sequence_fill_iterable(PyObject *obj, const void *ptr, simd_data_type dtype); +/** + * Create a Python list from a C array `ptr` allocated by + * `simd_sequence_new()` or `simd_sequence_from_iterable()` according to + * to the sequence data type `dtype` on range (simd_data_[qu8:qf64]). + * + * Return NULL and a Python exception on failure, otherwise new reference. + */ +static PyObject * +simd_sequence_to_list(const void *ptr, simd_data_type dtype); +/** + * Return a SIMD multi-vector(simd_data) representation of Python tuple of + * (simd_vector*,) `obj` according to the scalar data type `dtype` + * on range (simd_data_[vu8x2:vf64x2])-(simd_data_[vu8x3:vf64x3]). + * + * Raise a Python exception on failure. + * + * Example: + ** simd_data data = simd_vectorx_from_tuple(tuple_obj, simd_data_vf32x2); + ** if (!PyErr_Occurred()) { + ** npyv_f32 sum = npyv_add_f32(data.vf32x2.val[0], data.vf32x2.val[1]); + ** ... + ** } + ** + */ +static simd_data +simd_vectorx_from_tuple(PyObject *obj, simd_data_type dtype); +/** + * Create a Python tuple of 'simd_vector' from a SIMD multi-vector + * based on the contents of `data`(simd_data) and according to + * the multi-vector data type `dtype` on range + * (simd_data_[vu8x2:vf64x2])-(simd_data_[vu8x3:vf64x3]). + * + * Return NULL and a Python exception on failure, otherwise new reference. + */ +static PyObject * +simd_vectorx_to_tuple(simd_data data, simd_data_type dtype); + +/************************************ + ** Declarations (inc_arg) + ************************************/ +typedef struct +{ + simd_data_type dtype; + simd_data data; + // set by simd_arg_converter() + PyObject *obj; +} simd_arg; +/** + * The following functions gather all conversions between all data types + * and they can used instead of all above functions. + */ +/** + * Convert a Python object `obj` into simd_data `arg->data` according to the + * required data type `arg->dtype`. + * + * Return -1 and raise Python exception on failure, otherwise return 0. + * + * Notes: + * - requires `simd_arg_free()` or `simd_sequence_free()` + * to free allocated C array, in case of sequence data types. + * - the number of minimum acceptable elements for sequence data + * types is the number of lanes of the equivalent vector data type. + * + * Example #1: + ** simd_arg arg = {.dtype = simd_data_qu8}; + ** if (simd_arg_from_obj(seq_obj, &arg) < 0) { + ** // fails to convert a python sequence object to C array of uint8 + ** return; + ** } + ** npyv_u8 v_u8 = npyv_load_u8(arg->data.qu8); + ** ... + ** simd_arg_free(&arg); + * + * Example #2: + ** simd_arg arg = {.dtype = simd_data_vf32}; + ** if (simd_arg_from_obj(vector_obj, &arg) < 0) { + ** // fails to convert a python simd_vector to NPYV vector + ** return; + ** } + ** npyv_f32 add_one = npyv_add_f32(arg->data.vu8, npyv_setall_f32(1)); + ** ... + */ +static int +simd_arg_from_obj(PyObject *obj, simd_arg *arg); +/** + * Convert a simd_data `arg->data` to into a Python object according to the + * required data type `arg->dtype`. + * + * Return NULL and raise Python exception on failure, otherwise return + * new reference. + * + * Example: + ** simd_arg arg = {.dtype = simd_data_u32, .data = {.u32 = 0xffffffff}}; + ** PyObject *obj = simd_arg_to_obj(&arg); + ** if (obj == NULL) { + ** // fails convert C uint32 to Python integer. + ** return; + ** } + ** + */ +static PyObject * +simd_arg_to_obj(const simd_arg *arg); +/** + * Converter function used similar to simd_arg_from_obj() but + * used with PyArg_Parse*(). + * + * Notes: + * - requires `simd_arg_free()` or `simd_sequence_free()` + * to free allocated C array, in case of sequence data types. + * - the number of minimum acceptable elements for sequence data + * types is the number of lanes of the equivalent vector data type. + * - use 'arg->obj' to retrieve the parameter obj. + * + * Example: + ** simd_arg seq_f32 = {.dtype = simd_data_qf32}; + ** simd_arg vec_f32 = {.dtype = simd_data_vf32}; + ** if (!PyArg_ParseTuple( + ** args, "O&O&:add_sum_f32", + ** simd_arg_converter, &seq_f32, + ** simd_arg_converter, &vec_f32 + ** )) { + ** // fail + ** return; + ** } + ** npyv_f32 load_a = npyv_load_f32(seq_f32.data.qf32); + ** npyv_f32 sum = npyv_add_f32(load_a, vec_f32.data.vf32); + ** ... + ** simd_arg_free(&seq_f32); + */ +static int +simd_arg_converter(PyObject *obj, simd_arg *arg); +/** + * Free the allocated C array, if the arg hold sequence data type. + */ +static void +simd_arg_free(simd_arg *arg); + +#endif // NPY_SIMD +#endif // _SIMD_SIMD_INC_H_ diff --git a/numpy/core/src/_simd/_simd_vector.inc b/numpy/core/src/_simd/_simd_vector.inc new file mode 100644 index 000000000000..3d0c15375074 --- /dev/null +++ b/numpy/core/src/_simd/_simd_vector.inc @@ -0,0 +1,193 @@ +/** + * This file is included by `_simd.dispatch.c.src`. Its contents are affected by the simd configuration, and + * therefore must be built multiple times. Making it a standalone `.c` file with `NPY_VISIBILITY_HIDDEN` + * symbols would require judicious use of `NPY_CPU_DISPATCH_DECLARE` and `NPY_CPU_DISPATCH_CURFX`, which was + * deemed too harmful to readability. + */ +/************************************ + ** Private Definitions + ************************************/ +static Py_ssize_t +simd__vector_length(PySIMDVectorObject *self) +{ + return simd_data_getinfo(self->dtype)->nlanes; +} +static PyObject * +simd__vector_item(PySIMDVectorObject *self, Py_ssize_t i) +{ + const simd_data_info *info = simd_data_getinfo(self->dtype); + int nlanes = info->nlanes; + if (i >= nlanes) { + PyErr_SetString(PyExc_IndexError, "vector index out of range"); + return NULL; + } + npyv_lanetype_u8 *src = self->data + i * info->lane_size; + simd_data data; + memcpy(&data.u64, src, info->lane_size); + return simd_scalar_to_number(data, info->to_scalar); +} + +static PySequenceMethods simd__vector_as_sequence = { + .sq_length = (lenfunc) simd__vector_length, + .sq_item = (ssizeargfunc) simd__vector_item +}; + +static PyObject * +simd__vector_name(PySIMDVectorObject *self, void *NPY_UNUSED(ignored)) +{ + return PyUnicode_FromString(simd_data_getinfo(self->dtype)->pyname); +} +static PyGetSetDef simd__vector_getset[] = { + { "__name__", (getter)simd__vector_name, NULL, NULL, NULL }, + { NULL, NULL, NULL, NULL, NULL } +}; + +static PyObject * +simd__vector_repr(PySIMDVectorObject *self) +{ + PyObject *obj = PySequence_List((PyObject*)self); + if (obj != NULL) { + const char *type_name = simd_data_getinfo(self->dtype)->pyname; + PyObject *repr = PyUnicode_FromFormat("<%s of %R>", type_name, obj); + Py_DECREF(obj); + return repr; + } + return obj; +} +static PyObject * +simd__vector_compare(PyObject *self, PyObject *other, int cmp_op) +{ + PyObject *obj; + if (PyTuple_Check(other)) { + obj = PySequence_Tuple(self); + } else if (PyList_Check(other)) { + obj = PySequence_List(self); + } else { + obj = PySequence_Fast(self, "invalid argument, expected a vector"); + } + if (obj != NULL) { + PyObject *rich = PyObject_RichCompare(obj, other, cmp_op); + Py_DECREF(obj); + return rich; + } + return obj; +} +static PyTypeObject PySIMDVectorType = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = NPY_TOSTRING(NPY_CPU_DISPATCH_CURFX(VECTOR)), + .tp_basicsize = sizeof(PySIMDVectorObject), + .tp_repr = (reprfunc)simd__vector_repr, + .tp_as_sequence = &simd__vector_as_sequence, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_richcompare = simd__vector_compare, + .tp_getset = simd__vector_getset +}; + +/************************************ + ** Protected Definitions + ************************************/ +/* + * Force inlining the following functions on CYGWIN to avoid spilling vector + * registers into the stack to workaround GCC/WIN64 bug that performs + * miss-align load variable of 256/512-bit vector from non-aligned + * 256/512-bit stack pointer. + * + * check the following links for more clearification: + * https://github.com/numpy/numpy/pull/18330#issuecomment-821539919 + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=49001 + */ +#if defined(__CYGWIN__) || (defined(__GNUC__) && defined(_WIN64)) + #define CYG_FINLINE NPY_FINLINE +#else + #define CYG_FINLINE static +#endif +CYG_FINLINE PySIMDVectorObject * +PySIMDVector_FromData(simd_data data, simd_data_type dtype) +{ + const simd_data_info *info = simd_data_getinfo(dtype); + assert(info->is_vector && info->nlanes > 0); + + PySIMDVectorObject *vec = PyObject_New(PySIMDVectorObject, &PySIMDVectorType); + if (vec == NULL) { + return (PySIMDVectorObject*)PyErr_NoMemory(); + } + vec->dtype = dtype; + if (info->is_bool) { + // boolean vectors are internally treated as unsigned + // vectors to add compatibility among all SIMD extensions + switch(dtype) { + case simd_data_vb8: + data.vu8 = npyv_cvt_u8_b8(data.vb8); + break; + case simd_data_vb16: + data.vu16 = npyv_cvt_u16_b16(data.vb16); + break; + case simd_data_vb32: + data.vu32 = npyv_cvt_u32_b32(data.vb32); + break; + default: + data.vu64 = npyv_cvt_u64_b64(data.vb64); + } + } + npyv_store_u8(vec->data, data.vu8); + return vec; +} + +CYG_FINLINE simd_data +PySIMDVector_AsData(PySIMDVectorObject *vec, simd_data_type dtype) +{ + const simd_data_info *info = simd_data_getinfo(dtype); + assert(info->is_vector && info->nlanes > 0); + + simd_data data = {.u64 = 0}; + if (!PyObject_IsInstance( + (PyObject *)vec, (PyObject *)&PySIMDVectorType + )) { + PyErr_Format(PyExc_TypeError, + "a vector type %s is required", info->pyname + ); + return data; + } + if (vec->dtype != dtype) { + PyErr_Format(PyExc_TypeError, + "a vector type %s is required, got(%s)", + info->pyname, simd_data_getinfo(vec->dtype)->pyname + ); + return data; + } + + data.vu8 = npyv_load_u8(vec->data); + if (info->is_bool) { + // boolean vectors are internally treated as unsigned + // vectors to add compatibility among all SIMD extensions + switch(dtype) { + case simd_data_vb8: + data.vb8 = npyv_cvt_b8_u8(data.vu8); + break; + case simd_data_vb16: + data.vb16 = npyv_cvt_b16_u16(data.vu16); + break; + case simd_data_vb32: + data.vb32 = npyv_cvt_b32_u32(data.vu32); + break; + default: + data.vb64 = npyv_cvt_b64_u64(data.vu64); + } + } + return data; +} + +static int +PySIMDVectorType_Init(PyObject *module) +{ + Py_INCREF(&PySIMDVectorType); + if (PyType_Ready(&PySIMDVectorType)) { + return -1; + } + if (PyModule_AddObject( + module, "vector_type",(PyObject *)&PySIMDVectorType + )) { + return -1; + } + return 0; +} diff --git a/numpy/core/src/common/.doxyfile b/numpy/core/src/common/.doxyfile new file mode 100644 index 000000000000..462cbbcfa182 --- /dev/null +++ b/numpy/core/src/common/.doxyfile @@ -0,0 +1 @@ +INCLUDE_PATH += @CUR_DIR diff --git a/numpy/core/src/common/array_assign.c b/numpy/core/src/common/array_assign.c new file mode 100644 index 000000000000..b7495fc09930 --- /dev/null +++ b/numpy/core/src/common/array_assign.c @@ -0,0 +1,170 @@ +/* + * This file implements some helper functions for the array assignment + * routines. The actual assignment routines are in array_assign_*.c + * + * Written by Mark Wiebe (mwwiebe@gmail.com) + * Copyright (c) 2011 by Enthought, Inc. + * + * See LICENSE.txt for the license. + */ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE + +#define PY_SSIZE_T_CLEAN +#include <Python.h> + +#include <numpy/ndarraytypes.h> +#include "npy_config.h" +#include "npy_pycompat.h" + +#include "shape.h" + +#include "array_assign.h" +#include "common.h" +#include "lowlevel_strided_loops.h" +#include "mem_overlap.h" + +/* See array_assign.h for parameter documentation */ +NPY_NO_EXPORT int +broadcast_strides(int ndim, npy_intp const *shape, + int strides_ndim, npy_intp const *strides_shape, npy_intp const *strides, + char const *strides_name, + npy_intp *out_strides) +{ + int idim, idim_start = ndim - strides_ndim; + + /* Can't broadcast to fewer dimensions */ + if (idim_start < 0) { + goto broadcast_error; + } + + /* + * Process from the end to the start, so that 'strides' and 'out_strides' + * can point to the same memory. + */ + for (idim = ndim - 1; idim >= idim_start; --idim) { + npy_intp strides_shape_value = strides_shape[idim - idim_start]; + /* If it doesn't have dimension one, it must match */ + if (strides_shape_value == 1) { + out_strides[idim] = 0; + } + else if (strides_shape_value != shape[idim]) { + goto broadcast_error; + } + else { + out_strides[idim] = strides[idim - idim_start]; + } + } + + /* New dimensions get a zero stride */ + for (idim = 0; idim < idim_start; ++idim) { + out_strides[idim] = 0; + } + + return 0; + +broadcast_error: { + PyObject *shape1 = convert_shape_to_string(strides_ndim, + strides_shape, ""); + if (shape1 == NULL) { + return -1; + } + + PyObject *shape2 = convert_shape_to_string(ndim, shape, ""); + if (shape2 == NULL) { + Py_DECREF(shape1); + return -1; + } + PyErr_Format(PyExc_ValueError, + "could not broadcast %s from shape %S into shape %S", + strides_name, shape1, shape2); + Py_DECREF(shape1); + Py_DECREF(shape2); + return -1; + } +} + +/* See array_assign.h for parameter documentation */ +NPY_NO_EXPORT int +raw_array_is_aligned(int ndim, npy_intp const *shape, + char *data, npy_intp const *strides, int alignment) +{ + + /* + * The code below expects the following: + * * that alignment is a power of two, as required by the C standard. + * * that casting from pointer to uintp gives a sensible representation + * we can use bitwise operations on (perhaps *not* req. by C std, + * but assumed by glibc so it should be fine) + * * that casting stride from intp to uintp (to avoid dependence on the + * signed int representation) preserves remainder wrt alignment, so + * stride%a is the same as ((unsigned intp)stride)%a. Req. by C std. + * + * The code checks whether the lowest log2(alignment) bits of `data` + * and all `strides` are 0, as this implies that + * (data + n*stride)%alignment == 0 for all integers n. + */ + + if (alignment > 1) { + npy_uintp align_check = (npy_uintp)data; + int i; + + for (i = 0; i < ndim; i++) { +#if NPY_RELAXED_STRIDES_CHECKING + /* skip dim == 1 as it is not required to have stride 0 */ + if (shape[i] > 1) { + /* if shape[i] == 1, the stride is never used */ + align_check |= (npy_uintp)strides[i]; + } + else if (shape[i] == 0) { + /* an array with zero elements is always aligned */ + return 1; + } +#else /* not NPY_RELAXED_STRIDES_CHECKING */ + align_check |= (npy_uintp)strides[i]; +#endif /* not NPY_RELAXED_STRIDES_CHECKING */ + } + + return npy_is_aligned((void *)align_check, alignment); + } + else if (alignment == 1) { + return 1; + } + else { + /* always return false for alignment == 0, which means cannot-be-aligned */ + return 0; + } +} + +NPY_NO_EXPORT int +IsAligned(PyArrayObject *ap) +{ + return raw_array_is_aligned(PyArray_NDIM(ap), PyArray_DIMS(ap), + PyArray_DATA(ap), PyArray_STRIDES(ap), + PyArray_DESCR(ap)->alignment); +} + +NPY_NO_EXPORT int +IsUintAligned(PyArrayObject *ap) +{ + return raw_array_is_aligned(PyArray_NDIM(ap), PyArray_DIMS(ap), + PyArray_DATA(ap), PyArray_STRIDES(ap), + npy_uint_alignment(PyArray_DESCR(ap)->elsize)); +} + + + +/* Returns 1 if the arrays have overlapping data, 0 otherwise */ +NPY_NO_EXPORT int +arrays_overlap(PyArrayObject *arr1, PyArrayObject *arr2) +{ + mem_overlap_t result; + + result = solve_may_share_memory(arr1, arr2, NPY_MAY_SHARE_BOUNDS); + if (result == MEM_OVERLAP_NO) { + return 0; + } + else { + return 1; + } +} diff --git a/numpy/core/src/multiarray/array_assign.h b/numpy/core/src/common/array_assign.h similarity index 66% rename from numpy/core/src/multiarray/array_assign.h rename to numpy/core/src/common/array_assign.h index 3fecff0074e1..8a28ed1d3a01 100644 --- a/numpy/core/src/multiarray/array_assign.h +++ b/numpy/core/src/common/array_assign.h @@ -1,5 +1,5 @@ -#ifndef _NPY_PRIVATE__ARRAY_ASSIGN_H_ -#define _NPY_PRIVATE__ARRAY_ASSIGN_H_ +#ifndef NUMPY_CORE_SRC_COMMON_ARRAY_ASSIGN_H_ +#define NUMPY_CORE_SRC_COMMON_ARRAY_ASSIGN_H_ /* * An array assignment function for copying arrays, treating the @@ -44,8 +44,8 @@ PyArray_AssignRawScalar(PyArrayObject *dst, * Returns 0 on success, -1 on failure. */ NPY_NO_EXPORT int -raw_array_assign_scalar(int ndim, npy_intp *shape, - PyArray_Descr *dst_dtype, char *dst_data, npy_intp *dst_strides, +raw_array_assign_scalar(int ndim, npy_intp const *shape, + PyArray_Descr *dst_dtype, char *dst_data, npy_intp const *dst_strides, PyArray_Descr *src_dtype, char *src_data); /* @@ -55,11 +55,11 @@ raw_array_assign_scalar(int ndim, npy_intp *shape, * Returns 0 on success, -1 on failure. */ NPY_NO_EXPORT int -raw_array_wheremasked_assign_scalar(int ndim, npy_intp *shape, - PyArray_Descr *dst_dtype, char *dst_data, npy_intp *dst_strides, +raw_array_wheremasked_assign_scalar(int ndim, npy_intp const *shape, + PyArray_Descr *dst_dtype, char *dst_data, npy_intp const *dst_strides, PyArray_Descr *src_dtype, char *src_data, PyArray_Descr *wheremask_dtype, char *wheremask_data, - npy_intp *wheremask_strides); + npy_intp const *wheremask_strides); /******** LOW-LEVEL ARRAY MANIPULATION HELPERS ********/ @@ -80,21 +80,39 @@ raw_array_wheremasked_assign_scalar(int ndim, npy_intp *shape, * Returns 0 on success, -1 on failure. */ NPY_NO_EXPORT int -broadcast_strides(int ndim, npy_intp *shape, - int strides_ndim, npy_intp *strides_shape, npy_intp *strides, - char *strides_name, +broadcast_strides(int ndim, npy_intp const *shape, + int strides_ndim, npy_intp const *strides_shape, npy_intp const *strides, + char const *strides_name, npy_intp *out_strides); /* * Checks whether a data pointer + set of strides refers to a raw - * array which is fully aligned data. + * array whose elements are all aligned to a given alignment. Returns + * 1 if data is aligned to alignment or 0 if not. + * alignment should be a power of two, or may be the sentinel value 0 to mean + * cannot-be-aligned, in which case 0 (false) is always returned. */ NPY_NO_EXPORT int -raw_array_is_aligned(int ndim, char *data, npy_intp *strides, int alignment); +raw_array_is_aligned(int ndim, npy_intp const *shape, + char *data, npy_intp const *strides, int alignment); + +/* + * Checks if an array is aligned to its "true alignment" + * given by dtype->alignment. + */ +NPY_NO_EXPORT int +IsAligned(PyArrayObject *ap); + +/* + * Checks if an array is aligned to its "uint alignment" + * given by npy_uint_alignment(dtype->elsize). + */ +NPY_NO_EXPORT int +IsUintAligned(PyArrayObject *ap); /* Returns 1 if the arrays have overlapping data, 0 otherwise */ NPY_NO_EXPORT int arrays_overlap(PyArrayObject *arr1, PyArrayObject *arr2); -#endif +#endif /* NUMPY_CORE_SRC_COMMON_ARRAY_ASSIGN_H_ */ diff --git a/numpy/core/src/private/binop_override.h b/numpy/core/src/common/binop_override.h similarity index 96% rename from numpy/core/src/private/binop_override.h rename to numpy/core/src/common/binop_override.h index 47df63e389ee..61bc05ef3719 100644 --- a/numpy/core/src/private/binop_override.h +++ b/numpy/core/src/common/binop_override.h @@ -1,5 +1,5 @@ -#ifndef __BINOP_OVERRIDE_H -#define __BINOP_OVERRIDE_H +#ifndef NUMPY_CORE_SRC_COMMON_BINOP_OVERRIDE_H_ +#define NUMPY_CORE_SRC_COMMON_BINOP_OVERRIDE_H_ #include <string.h> #include <Python.h> @@ -129,11 +129,14 @@ binop_should_defer(PyObject *self, PyObject *other, int inplace) * check whether __array_ufunc__ equals None. */ attr = PyArray_LookupSpecial(other, "__array_ufunc__"); - if (attr) { + if (attr != NULL) { defer = !inplace && (attr == Py_None); Py_DECREF(attr); return defer; } + else if (PyErr_Occurred()) { + PyErr_Clear(); /* TODO[gh-14801]: propagate crashes during attribute access? */ + } /* * Otherwise, we need to check for the legacy __array_priority__. But if * other.__class__ is a subtype of self.__class__, then it's already had @@ -209,4 +212,4 @@ binop_should_defer(PyObject *self, PyObject *other, int inplace) } \ } while (0) -#endif +#endif /* NUMPY_CORE_SRC_COMMON_BINOP_OVERRIDE_H_ */ diff --git a/numpy/core/src/multiarray/cblasfuncs.c b/numpy/core/src/common/cblasfuncs.c similarity index 81% rename from numpy/core/src/multiarray/cblasfuncs.c rename to numpy/core/src/common/cblasfuncs.c index c941bb29be26..714636782663 100644 --- a/numpy/core/src/multiarray/cblasfuncs.c +++ b/numpy/core/src/common/cblasfuncs.c @@ -2,42 +2,18 @@ * This module provides a BLAS optimized matrix multiply, * inner product and dot for numpy arrays */ - #define NPY_NO_DEPRECATED_API NPY_API_VERSION #define _MULTIARRAYMODULE +#define PY_SSIZE_T_CLEAN #include <Python.h> -#include <assert.h> -#include <numpy/arrayobject.h> + +#include "numpy/arrayobject.h" #include "npy_cblas.h" #include "arraytypes.h" #include "common.h" -#include "mem_overlap.h" - -/* - * Helper: call appropriate BLAS dot function for typenum. - * Strides are NumPy strides. - */ -static void -blas_dot(int typenum, npy_intp n, - void *a, npy_intp stridea, void *b, npy_intp strideb, void *res) -{ - switch (typenum) { - case NPY_DOUBLE: - DOUBLE_dot(a, stridea, b, strideb, res, n, NULL); - break; - case NPY_FLOAT: - FLOAT_dot(a, stridea, b, strideb, res, n, NULL); - break; - case NPY_CDOUBLE: - CDOUBLE_dot(a, stridea, b, strideb, res, n, NULL); - break; - case NPY_CFLOAT: - CFLOAT_dot(a, stridea, b, strideb, res, n, NULL); - break; - } -} +#include <assert.h> static const double oneD[2] = {1.0, 0.0}, zeroD[2] = {0.0, 0.0}; @@ -50,28 +26,28 @@ static const float oneF[2] = {1.0, 0.0}, zeroF[2] = {0.0, 0.0}; static void gemm(int typenum, enum CBLAS_ORDER order, enum CBLAS_TRANSPOSE transA, enum CBLAS_TRANSPOSE transB, - int m, int n, int k, - PyArrayObject *A, int lda, PyArrayObject *B, int ldb, PyArrayObject *R) + npy_intp m, npy_intp n, npy_intp k, + PyArrayObject *A, npy_intp lda, PyArrayObject *B, npy_intp ldb, PyArrayObject *R) { const void *Adata = PyArray_DATA(A), *Bdata = PyArray_DATA(B); void *Rdata = PyArray_DATA(R); - int ldc = PyArray_DIM(R, 1) > 1 ? PyArray_DIM(R, 1) : 1; + npy_intp ldc = PyArray_DIM(R, 1) > 1 ? PyArray_DIM(R, 1) : 1; switch (typenum) { case NPY_DOUBLE: - cblas_dgemm(order, transA, transB, m, n, k, 1., + CBLAS_FUNC(cblas_dgemm)(order, transA, transB, m, n, k, 1., Adata, lda, Bdata, ldb, 0., Rdata, ldc); break; case NPY_FLOAT: - cblas_sgemm(order, transA, transB, m, n, k, 1.f, + CBLAS_FUNC(cblas_sgemm)(order, transA, transB, m, n, k, 1.f, Adata, lda, Bdata, ldb, 0.f, Rdata, ldc); break; case NPY_CDOUBLE: - cblas_zgemm(order, transA, transB, m, n, k, oneD, + CBLAS_FUNC(cblas_zgemm)(order, transA, transB, m, n, k, oneD, Adata, lda, Bdata, ldb, zeroD, Rdata, ldc); break; case NPY_CFLOAT: - cblas_cgemm(order, transA, transB, m, n, k, oneF, + CBLAS_FUNC(cblas_cgemm)(order, transA, transB, m, n, k, oneF, Adata, lda, Bdata, ldb, zeroF, Rdata, ldc); break; } @@ -83,29 +59,29 @@ gemm(int typenum, enum CBLAS_ORDER order, */ static void gemv(int typenum, enum CBLAS_ORDER order, enum CBLAS_TRANSPOSE trans, - PyArrayObject *A, int lda, PyArrayObject *X, int incX, + PyArrayObject *A, npy_intp lda, PyArrayObject *X, npy_intp incX, PyArrayObject *R) { const void *Adata = PyArray_DATA(A), *Xdata = PyArray_DATA(X); void *Rdata = PyArray_DATA(R); - int m = PyArray_DIM(A, 0), n = PyArray_DIM(A, 1); + npy_intp m = PyArray_DIM(A, 0), n = PyArray_DIM(A, 1); switch (typenum) { case NPY_DOUBLE: - cblas_dgemv(order, trans, m, n, 1., Adata, lda, Xdata, incX, + CBLAS_FUNC(cblas_dgemv)(order, trans, m, n, 1., Adata, lda, Xdata, incX, 0., Rdata, 1); break; case NPY_FLOAT: - cblas_sgemv(order, trans, m, n, 1.f, Adata, lda, Xdata, incX, + CBLAS_FUNC(cblas_sgemv)(order, trans, m, n, 1.f, Adata, lda, Xdata, incX, 0.f, Rdata, 1); break; case NPY_CDOUBLE: - cblas_zgemv(order, trans, m, n, oneD, Adata, lda, Xdata, incX, + CBLAS_FUNC(cblas_zgemv)(order, trans, m, n, oneD, Adata, lda, Xdata, incX, zeroD, Rdata, 1); break; case NPY_CFLOAT: - cblas_cgemv(order, trans, m, n, oneF, Adata, lda, Xdata, incX, + CBLAS_FUNC(cblas_cgemv)(order, trans, m, n, oneF, Adata, lda, Xdata, incX, zeroF, Rdata, 1); break; } @@ -117,19 +93,19 @@ gemv(int typenum, enum CBLAS_ORDER order, enum CBLAS_TRANSPOSE trans, */ static void syrk(int typenum, enum CBLAS_ORDER order, enum CBLAS_TRANSPOSE trans, - int n, int k, - PyArrayObject *A, int lda, PyArrayObject *R) + npy_intp n, npy_intp k, + PyArrayObject *A, npy_intp lda, PyArrayObject *R) { const void *Adata = PyArray_DATA(A); void *Rdata = PyArray_DATA(R); - int ldc = PyArray_DIM(R, 1) > 1 ? PyArray_DIM(R, 1) : 1; + npy_intp ldc = PyArray_DIM(R, 1) > 1 ? PyArray_DIM(R, 1) : 1; npy_intp i; npy_intp j; switch (typenum) { case NPY_DOUBLE: - cblas_dsyrk(order, CblasUpper, trans, n, k, 1., + CBLAS_FUNC(cblas_dsyrk)(order, CblasUpper, trans, n, k, 1., Adata, lda, 0., Rdata, ldc); for (i = 0; i < n; i++) { @@ -140,7 +116,7 @@ syrk(int typenum, enum CBLAS_ORDER order, enum CBLAS_TRANSPOSE trans, } break; case NPY_FLOAT: - cblas_ssyrk(order, CblasUpper, trans, n, k, 1.f, + CBLAS_FUNC(cblas_ssyrk)(order, CblasUpper, trans, n, k, 1.f, Adata, lda, 0.f, Rdata, ldc); for (i = 0; i < n; i++) { @@ -151,7 +127,7 @@ syrk(int typenum, enum CBLAS_ORDER order, enum CBLAS_TRANSPOSE trans, } break; case NPY_CDOUBLE: - cblas_zsyrk(order, CblasUpper, trans, n, k, oneD, + CBLAS_FUNC(cblas_zsyrk)(order, CblasUpper, trans, n, k, oneD, Adata, lda, zeroD, Rdata, ldc); for (i = 0; i < n; i++) { @@ -162,7 +138,7 @@ syrk(int typenum, enum CBLAS_ORDER order, enum CBLAS_TRANSPOSE trans, } break; case NPY_CFLOAT: - cblas_csyrk(order, CblasUpper, trans, n, k, oneF, + CBLAS_FUNC(cblas_csyrk)(order, CblasUpper, trans, n, k, oneF, Adata, lda, zeroF, Rdata, ldc); for (i = 0; i < n; i++) { @@ -208,12 +184,13 @@ _select_matrix_shape(PyArrayObject *array) * This also makes sure that the data segment is aligned with * an itemsize address as well by returning one if not true. */ -static int +NPY_NO_EXPORT int _bad_strides(PyArrayObject *ap) { int itemsize = PyArray_ITEMSIZE(ap); int i, N=PyArray_NDIM(ap); npy_intp *strides = PyArray_STRIDES(ap); + npy_intp *dims = PyArray_DIMS(ap); if (((npy_intp)(PyArray_DATA(ap)) % itemsize) != 0) { return 1; @@ -222,6 +199,9 @@ _bad_strides(PyArrayObject *ap) if ((strides[i] < 0) || (strides[i] % itemsize) != 0) { return 1; } + if ((strides[i] == 0 && dims[i] > 1)) { + return 1; + } } return 0; @@ -244,7 +224,7 @@ cblas_matrixproduct(int typenum, PyArrayObject *ap1, PyArrayObject *ap2, PyArrayObject *out) { PyArrayObject *result = NULL, *out_buf = NULL; - int j, lda, ldb; + npy_intp j, lda, ldb; npy_intp l; int nd; npy_intp ap1stride = 0; @@ -379,77 +359,9 @@ cblas_matrixproduct(int typenum, PyArrayObject *ap1, PyArrayObject *ap2, } } - if (out != NULL) { - int d; - - /* verify that out is usable */ - if (PyArray_NDIM(out) != nd || - PyArray_TYPE(out) != typenum || - !PyArray_ISCARRAY(out)) { - - PyErr_SetString(PyExc_ValueError, - "output array is not acceptable (must have the right datatype, " - "number of dimensions, and be a C-Array)"); - goto fail; - } - for (d = 0; d < nd; ++d) { - if (dimensions[d] != PyArray_DIM(out, d)) { - PyErr_SetString(PyExc_ValueError, - "output array has wrong dimensions"); - goto fail; - } - } - - /* check for memory overlap */ - if (!(solve_may_share_memory(out, ap1, 1) == 0 && - solve_may_share_memory(out, ap2, 1) == 0)) { - /* allocate temporary output array */ - out_buf = (PyArrayObject *)PyArray_NewLikeArray(out, NPY_CORDER, - NULL, 0); - if (out_buf == NULL) { - goto fail; - } - - /* set copy-back */ - Py_INCREF(out); - if (PyArray_SetWritebackIfCopyBase(out_buf, out) < 0) { - Py_DECREF(out); - goto fail; - } - } - else { - Py_INCREF(out); - out_buf = out; - } - Py_INCREF(out); - result = out; - } - else { - double prior1, prior2; - PyTypeObject *subtype; - PyObject *tmp; - - /* Choose which subtype to return */ - if (Py_TYPE(ap1) != Py_TYPE(ap2)) { - prior2 = PyArray_GetPriority((PyObject *)ap2, 0.0); - prior1 = PyArray_GetPriority((PyObject *)ap1, 0.0); - subtype = (prior2 > prior1 ? Py_TYPE(ap2) : Py_TYPE(ap1)); - } - else { - prior1 = prior2 = 0.0; - subtype = Py_TYPE(ap1); - } - - tmp = (PyObject *)(prior2 > prior1 ? ap2 : ap1); - - out_buf = (PyArrayObject *)PyArray_New(subtype, nd, dimensions, - typenum, NULL, NULL, 0, 0, tmp); - if (out_buf == NULL) { - goto fail; - } - - Py_INCREF(out_buf); - result = out_buf; + out_buf = new_array_for_sum(ap1, ap2, out, nd, dimensions, typenum, &result); + if (out_buf == NULL) { + goto fail; } numbytes = PyArray_NBYTES(out_buf); @@ -475,14 +387,15 @@ cblas_matrixproduct(int typenum, PyArrayObject *ap1, PyArrayObject *ap2, *((double *)PyArray_DATA(ap1)); } else if (ap1shape != _matrix) { - cblas_daxpy(l, + CBLAS_FUNC(cblas_daxpy)(l, *((double *)PyArray_DATA(ap2)), (double *)PyArray_DATA(ap1), ap1stride/sizeof(double), (double *)PyArray_DATA(out_buf), 1); } else { - int maxind, oind, i, a1s, outs; + int maxind, oind; + npy_intp i, a1s, outs; char *ptr, *optr; double val; @@ -495,7 +408,7 @@ cblas_matrixproduct(int typenum, PyArrayObject *ap1, PyArrayObject *ap2, a1s = PyArray_STRIDE(ap1, maxind) / sizeof(double); outs = PyArray_STRIDE(out_buf, maxind) / sizeof(double); for (i = 0; i < PyArray_DIM(ap1, oind); i++) { - cblas_daxpy(l, val, (double *)ptr, a1s, + CBLAS_FUNC(cblas_daxpy)(l, val, (double *)ptr, a1s, (double *)optr, outs); ptr += PyArray_STRIDE(ap1, oind); optr += PyArray_STRIDE(out_buf, oind); @@ -513,14 +426,15 @@ cblas_matrixproduct(int typenum, PyArrayObject *ap1, PyArrayObject *ap2, res->imag = ptr1->real * ptr2->imag + ptr1->imag * ptr2->real; } else if (ap1shape != _matrix) { - cblas_zaxpy(l, + CBLAS_FUNC(cblas_zaxpy)(l, (double *)PyArray_DATA(ap2), (double *)PyArray_DATA(ap1), ap1stride/sizeof(npy_cdouble), (double *)PyArray_DATA(out_buf), 1); } else { - int maxind, oind, i, a1s, outs; + int maxind, oind; + npy_intp i, a1s, outs; char *ptr, *optr; double *pval; @@ -533,7 +447,7 @@ cblas_matrixproduct(int typenum, PyArrayObject *ap1, PyArrayObject *ap2, a1s = PyArray_STRIDE(ap1, maxind) / sizeof(npy_cdouble); outs = PyArray_STRIDE(out_buf, maxind) / sizeof(npy_cdouble); for (i = 0; i < PyArray_DIM(ap1, oind); i++) { - cblas_zaxpy(l, pval, (double *)ptr, a1s, + CBLAS_FUNC(cblas_zaxpy)(l, pval, (double *)ptr, a1s, (double *)optr, outs); ptr += PyArray_STRIDE(ap1, oind); optr += PyArray_STRIDE(out_buf, oind); @@ -546,14 +460,15 @@ cblas_matrixproduct(int typenum, PyArrayObject *ap1, PyArrayObject *ap2, *((float *)PyArray_DATA(ap1)); } else if (ap1shape != _matrix) { - cblas_saxpy(l, + CBLAS_FUNC(cblas_saxpy)(l, *((float *)PyArray_DATA(ap2)), (float *)PyArray_DATA(ap1), ap1stride/sizeof(float), (float *)PyArray_DATA(out_buf), 1); } else { - int maxind, oind, i, a1s, outs; + int maxind, oind; + npy_intp i, a1s, outs; char *ptr, *optr; float val; @@ -566,7 +481,7 @@ cblas_matrixproduct(int typenum, PyArrayObject *ap1, PyArrayObject *ap2, a1s = PyArray_STRIDE(ap1, maxind) / sizeof(float); outs = PyArray_STRIDE(out_buf, maxind) / sizeof(float); for (i = 0; i < PyArray_DIM(ap1, oind); i++) { - cblas_saxpy(l, val, (float *)ptr, a1s, + CBLAS_FUNC(cblas_saxpy)(l, val, (float *)ptr, a1s, (float *)optr, outs); ptr += PyArray_STRIDE(ap1, oind); optr += PyArray_STRIDE(out_buf, oind); @@ -584,14 +499,15 @@ cblas_matrixproduct(int typenum, PyArrayObject *ap1, PyArrayObject *ap2, res->imag = ptr1->real * ptr2->imag + ptr1->imag * ptr2->real; } else if (ap1shape != _matrix) { - cblas_caxpy(l, + CBLAS_FUNC(cblas_caxpy)(l, (float *)PyArray_DATA(ap2), (float *)PyArray_DATA(ap1), ap1stride/sizeof(npy_cfloat), (float *)PyArray_DATA(out_buf), 1); } else { - int maxind, oind, i, a1s, outs; + int maxind, oind; + npy_intp i, a1s, outs; char *ptr, *optr; float *pval; @@ -604,7 +520,7 @@ cblas_matrixproduct(int typenum, PyArrayObject *ap1, PyArrayObject *ap2, a1s = PyArray_STRIDE(ap1, maxind) / sizeof(npy_cfloat); outs = PyArray_STRIDE(out_buf, maxind) / sizeof(npy_cfloat); for (i = 0; i < PyArray_DIM(ap1, oind); i++) { - cblas_caxpy(l, pval, (float *)ptr, a1s, + CBLAS_FUNC(cblas_caxpy)(l, pval, (float *)ptr, a1s, (float *)optr, outs); ptr += PyArray_STRIDE(ap1, oind); optr += PyArray_STRIDE(out_buf, oind); @@ -617,17 +533,17 @@ cblas_matrixproduct(int typenum, PyArrayObject *ap1, PyArrayObject *ap2, NPY_BEGIN_ALLOW_THREADS; /* Dot product between two vectors -- Level 1 BLAS */ - blas_dot(typenum, l, + PyArray_DESCR(out_buf)->f->dotfunc( PyArray_DATA(ap1), PyArray_STRIDE(ap1, (ap1shape == _row)), PyArray_DATA(ap2), PyArray_STRIDE(ap2, 0), - PyArray_DATA(out_buf)); + PyArray_DATA(out_buf), l, NULL); NPY_END_ALLOW_THREADS; } else if (ap1shape == _matrix && ap2shape != _matrix) { /* Matrix vector multiplication -- Level 2 BLAS */ /* lda must be MAX(M,1) */ enum CBLAS_ORDER Order; - int ap2s; + npy_intp ap2s; if (!PyArray_ISONESEGMENT(ap1)) { PyObject *new; @@ -654,7 +570,7 @@ cblas_matrixproduct(int typenum, PyArrayObject *ap1, PyArrayObject *ap2, else if (ap1shape != _matrix && ap2shape == _matrix) { /* Vector matrix multiplication -- Level 2 BLAS */ enum CBLAS_ORDER Order; - int ap1s; + npy_intp ap1s; if (!PyArray_ISONESEGMENT(ap2)) { PyObject *new; @@ -691,7 +607,7 @@ cblas_matrixproduct(int typenum, PyArrayObject *ap1, PyArrayObject *ap2, */ enum CBLAS_ORDER Order; enum CBLAS_TRANSPOSE Trans1, Trans2; - int M, N, L; + npy_intp M, N, L; /* Optimization possible: */ /* diff --git a/numpy/core/src/common/cblasfuncs.h b/numpy/core/src/common/cblasfuncs.h new file mode 100644 index 000000000000..71c533f369a4 --- /dev/null +++ b/numpy/core/src/common/cblasfuncs.h @@ -0,0 +1,7 @@ +#ifndef NUMPY_CORE_SRC_COMMON_CBLASFUNCS_H_ +#define NUMPY_CORE_SRC_COMMON_CBLASFUNCS_H_ + +NPY_NO_EXPORT PyObject * +cblas_matrixproduct(int, PyArrayObject *, PyArrayObject *, PyArrayObject *); + +#endif /* NUMPY_CORE_SRC_COMMON_CBLASFUNCS_H_ */ diff --git a/numpy/core/src/common/dlpack/dlpack.h b/numpy/core/src/common/dlpack/dlpack.h new file mode 100644 index 000000000000..29209aee12ab --- /dev/null +++ b/numpy/core/src/common/dlpack/dlpack.h @@ -0,0 +1,201 @@ +// Taken from: +// https://github.com/dmlc/dlpack/blob/9b6176fdecb55e9bf39b16f08b96913ed3f275b4/include/dlpack/dlpack.h +/*! + * Copyright (c) 2017 by Contributors + * \file dlpack.h + * \brief The common header of DLPack. + */ +#ifndef DLPACK_DLPACK_H_ +#define DLPACK_DLPACK_H_ + +#ifdef __cplusplus +#define DLPACK_EXTERN_C extern "C" +#else +#define DLPACK_EXTERN_C +#endif + +/*! \brief The current version of dlpack */ +#define DLPACK_VERSION 050 + +/*! \brief DLPACK_DLL prefix for windows */ +#ifdef _WIN32 +#ifdef DLPACK_EXPORTS +#define DLPACK_DLL __declspec(dllexport) +#else +#define DLPACK_DLL __declspec(dllimport) +#endif +#else +#define DLPACK_DLL +#endif + +#include <stdint.h> +#include <stddef.h> + +#ifdef __cplusplus +extern "C" { +#endif +/*! + * \brief The device type in DLDevice. + */ +typedef enum { + /*! \brief CPU device */ + kDLCPU = 1, + /*! \brief CUDA GPU device */ + kDLCUDA = 2, + /*! + * \brief Pinned CUDA CPU memory by cudaMallocHost + */ + kDLCUDAHost = 3, + /*! \brief OpenCL devices. */ + kDLOpenCL = 4, + /*! \brief Vulkan buffer for next generation graphics. */ + kDLVulkan = 7, + /*! \brief Metal for Apple GPU. */ + kDLMetal = 8, + /*! \brief Verilog simulator buffer */ + kDLVPI = 9, + /*! \brief ROCm GPUs for AMD GPUs */ + kDLROCM = 10, + /*! + * \brief Pinned ROCm CPU memory allocated by hipMallocHost + */ + kDLROCMHost = 11, + /*! + * \brief Reserved extension device type, + * used for quickly test extension device + * The semantics can differ depending on the implementation. + */ + kDLExtDev = 12, + /*! + * \brief CUDA managed/unified memory allocated by cudaMallocManaged + */ + kDLCUDAManaged = 13, +} DLDeviceType; + +/*! + * \brief A Device for Tensor and operator. + */ +typedef struct { + /*! \brief The device type used in the device. */ + DLDeviceType device_type; + /*! + * \brief The device index. + * For vanilla CPU memory, pinned memory, or managed memory, this is set to 0. + */ + int device_id; +} DLDevice; + +/*! + * \brief The type code options DLDataType. + */ +typedef enum { + /*! \brief signed integer */ + kDLInt = 0U, + /*! \brief unsigned integer */ + kDLUInt = 1U, + /*! \brief IEEE floating point */ + kDLFloat = 2U, + /*! + * \brief Opaque handle type, reserved for testing purposes. + * Frameworks need to agree on the handle data type for the exchange to be well-defined. + */ + kDLOpaqueHandle = 3U, + /*! \brief bfloat16 */ + kDLBfloat = 4U, + /*! + * \brief complex number + * (C/C++/Python layout: compact struct per complex number) + */ + kDLComplex = 5U, +} DLDataTypeCode; + +/*! + * \brief The data type the tensor can hold. + * + * Examples + * - float: type_code = 2, bits = 32, lanes=1 + * - float4(vectorized 4 float): type_code = 2, bits = 32, lanes=4 + * - int8: type_code = 0, bits = 8, lanes=1 + * - std::complex<float>: type_code = 5, bits = 64, lanes = 1 + */ +typedef struct { + /*! + * \brief Type code of base types. + * We keep it uint8_t instead of DLDataTypeCode for minimal memory + * footprint, but the value should be one of DLDataTypeCode enum values. + * */ + uint8_t code; + /*! + * \brief Number of bits, common choices are 8, 16, 32. + */ + uint8_t bits; + /*! \brief Number of lanes in the type, used for vector types. */ + uint16_t lanes; +} DLDataType; + +/*! + * \brief Plain C Tensor object, does not manage memory. + */ +typedef struct { + /*! + * \brief The opaque data pointer points to the allocated data. This will be + * CUDA device pointer or cl_mem handle in OpenCL. This pointer is always + * aligned to 256 bytes as in CUDA. + * + * For given DLTensor, the size of memory required to store the contents of + * data is calculated as follows: + * + * \code{.c} + * static inline size_t GetDataSize(const DLTensor* t) { + * size_t size = 1; + * for (tvm_index_t i = 0; i < t->ndim; ++i) { + * size *= t->shape[i]; + * } + * size *= (t->dtype.bits * t->dtype.lanes + 7) / 8; + * return size; + * } + * \endcode + */ + void* data; + /*! \brief The device of the tensor */ + DLDevice device; + /*! \brief Number of dimensions */ + int ndim; + /*! \brief The data type of the pointer*/ + DLDataType dtype; + /*! \brief The shape of the tensor */ + int64_t* shape; + /*! + * \brief strides of the tensor (in number of elements, not bytes) + * can be NULL, indicating tensor is compact and row-majored. + */ + int64_t* strides; + /*! \brief The offset in bytes to the beginning pointer to data */ + uint64_t byte_offset; +} DLTensor; + +/*! + * \brief C Tensor object, manage memory of DLTensor. This data structure is + * intended to facilitate the borrowing of DLTensor by another framework. It is + * not meant to transfer the tensor. When the borrowing framework doesn't need + * the tensor, it should call the deleter to notify the host that the resource + * is no longer needed. + */ +typedef struct DLManagedTensor { + /*! \brief DLTensor which is being memory managed */ + DLTensor dl_tensor; + /*! \brief the context of the original host framework of DLManagedTensor in + * which DLManagedTensor is used in the framework. It can also be NULL. + */ + void * manager_ctx; + /*! \brief Destructor signature void (*)(void*) - this should be called + * to destruct manager_ctx which holds the DLManagedTensor. It can be NULL + * if there is no way for the caller to provide a reasonable destructor. + * The destructors deletes the argument self as well. + */ + void (*deleter)(struct DLManagedTensor * self); +} DLManagedTensor; +#ifdef __cplusplus +} // DLPACK_EXTERN_C +#endif +#endif // DLPACK_DLPACK_H_ diff --git a/numpy/core/src/private/get_attr_string.h b/numpy/core/src/common/get_attr_string.h similarity index 72% rename from numpy/core/src/private/get_attr_string.h rename to numpy/core/src/common/get_attr_string.h index bec87c5ed1c9..3b23b2e6619b 100644 --- a/numpy/core/src/private/get_attr_string.h +++ b/numpy/core/src/common/get_attr_string.h @@ -1,5 +1,5 @@ -#ifndef __GET_ATTR_STRING_H -#define __GET_ATTR_STRING_H +#ifndef NUMPY_CORE_SRC_COMMON_GET_ATTR_STRING_H_ +#define NUMPY_CORE_SRC_COMMON_GET_ATTR_STRING_H_ static NPY_INLINE npy_bool _is_basic_python_type(PyTypeObject *tp) @@ -7,9 +7,6 @@ _is_basic_python_type(PyTypeObject *tp) return ( /* Basic number types */ tp == &PyBool_Type || -#if !defined(NPY_PY3K) - tp == &PyInt_Type || -#endif tp == &PyLong_Type || tp == &PyFloat_Type || tp == &PyComplex_Type || @@ -22,9 +19,6 @@ _is_basic_python_type(PyTypeObject *tp) tp == &PyFrozenSet_Type || tp == &PyUnicode_Type || tp == &PyBytes_Type || -#if !defined(NPY_PY3K) - tp == &PyString_Type || -#endif /* other builtins */ tp == &PySlice_Type || @@ -40,45 +34,37 @@ _is_basic_python_type(PyTypeObject *tp) } /* - * Stripped down version of PyObject_GetAttrString, - * avoids lookups for None, tuple, and List objects, - * and doesn't create a PyErr since this code ignores it. + * Stripped down version of PyObject_GetAttrString(obj, name) that does not + * raise PyExc_AttributeError. * - * This can be much faster then PyObject_GetAttrString where - * exceptions are not used by caller. + * This allows it to avoid creating then discarding exception objects when + * performing lookups on objects without any attributes. * - * 'obj' is the object to search for attribute. - * - * 'name' is the attribute to search for. - * - * Returns attribute value on success, NULL on failure. + * Returns attribute value on success, NULL without an exception set if + * there is no such attribute, and NULL with an exception on failure. */ static NPY_INLINE PyObject * -maybe_get_attr(PyObject *obj, char *name) +maybe_get_attr(PyObject *obj, char const *name) { PyTypeObject *tp = Py_TYPE(obj); PyObject *res = (PyObject *)NULL; /* Attribute referenced by (char *)name */ if (tp->tp_getattr != NULL) { - res = (*tp->tp_getattr)(obj, name); - if (res == NULL) { + res = (*tp->tp_getattr)(obj, (char *)name); + if (res == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) { PyErr_Clear(); } } /* Attribute referenced by (PyObject *)name */ else if (tp->tp_getattro != NULL) { -#if defined(NPY_PY3K) PyObject *w = PyUnicode_InternFromString(name); -#else - PyObject *w = PyString_InternFromString(name); -#endif if (w == NULL) { return (PyObject *)NULL; } res = (*tp->tp_getattro)(obj, w); Py_DECREF(w); - if (res == NULL) { + if (res == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) { PyErr_Clear(); } } @@ -95,7 +81,7 @@ maybe_get_attr(PyObject *obj, char *name) * In future, could be made more like _Py_LookupSpecial */ static NPY_INLINE PyObject * -PyArray_LookupSpecial(PyObject *obj, char *name) +PyArray_LookupSpecial(PyObject *obj, char const *name) { PyTypeObject *tp = Py_TYPE(obj); @@ -103,7 +89,6 @@ PyArray_LookupSpecial(PyObject *obj, char *name) if (_is_basic_python_type(tp)) { return NULL; } - return maybe_get_attr((PyObject *)tp, name); } @@ -116,7 +101,7 @@ PyArray_LookupSpecial(PyObject *obj, char *name) * Kept for backwards compatibility. In future, we should deprecate this. */ static NPY_INLINE PyObject * -PyArray_LookupSpecial_OnInstance(PyObject *obj, char *name) +PyArray_LookupSpecial_OnInstance(PyObject *obj, char const *name) { PyTypeObject *tp = Py_TYPE(obj); @@ -128,4 +113,4 @@ PyArray_LookupSpecial_OnInstance(PyObject *obj, char *name) return maybe_get_attr(obj, name); } -#endif +#endif /* NUMPY_CORE_SRC_COMMON_GET_ATTR_STRING_H_ */ diff --git a/numpy/core/src/private/lowlevel_strided_loops.h b/numpy/core/src/common/lowlevel_strided_loops.h similarity index 75% rename from numpy/core/src/private/lowlevel_strided_loops.h rename to numpy/core/src/common/lowlevel_strided_loops.h index 094612b7d41e..ad86c04895a9 100644 --- a/numpy/core/src/private/lowlevel_strided_loops.h +++ b/numpy/core/src/common/lowlevel_strided_loops.h @@ -1,13 +1,20 @@ -#ifndef __LOWLEVEL_STRIDED_LOOPS_H -#define __LOWLEVEL_STRIDED_LOOPS_H +#ifndef NUMPY_CORE_SRC_COMMON_LOWLEVEL_STRIDED_LOOPS_H_ +#define NUMPY_CORE_SRC_COMMON_LOWLEVEL_STRIDED_LOOPS_H_ #include "common.h" -#include <npy_config.h> +#include "npy_config.h" +#include "array_method.h" +#include "dtype_transfer.h" #include "mem_overlap.h" +/* For PyArray_ macros used below */ +#include "numpy/ndarrayobject.h" + /* * NOTE: This API should remain private for the time being, to allow * for further refinement. I think the 'aligned' mechanism - * needs changing, for example. + * needs changing, for example. + * + * Note: Updated in 2018 to distinguish "true" from "uint" alignment. */ /* @@ -25,52 +32,35 @@ * Use NPY_AUXDATA_CLONE and NPY_AUXDATA_FREE to deal with this data. * */ -typedef void (PyArray_StridedUnaryOp)(char *dst, npy_intp dst_stride, - char *src, npy_intp src_stride, - npy_intp N, npy_intp src_itemsize, - NpyAuxData *transferdata); +// TODO: FIX! That comment belongs to something now in array-method /* * This is for pointers to functions which behave exactly as - * for PyArray_StridedUnaryOp, but with an additional mask controlling + * for PyArrayMethod_StridedLoop, but with an additional mask controlling * which values are transformed. * + * TODO: We should move this mask "capability" to the ArrayMethod itself + * probably. Although for NumPy internal things this works decently, + * and exposing it there should be well thought out to be useful beyond + * NumPy if possible. + * * In particular, the 'i'-th element is operated on if and only if * mask[i*mask_stride] is true. */ -typedef void (PyArray_MaskedStridedUnaryOp)(char *dst, npy_intp dst_stride, - char *src, npy_intp src_stride, - npy_bool *mask, npy_intp mask_stride, - npy_intp N, npy_intp src_itemsize, - NpyAuxData *transferdata); - -/* - * This function pointer is for binary operations that input two - * arbitrarily strided one-dimensional array segments and output - * an arbitrarily strided array segment of the same size. - * It may be a fully general function, or a specialized function - * when the strides or item size have particular known values. - * - * Examples of binary operations are the basic arithmetic operations, - * logical operators AND, OR, and many others. - * - * The 'transferdata' parameter is slightly special, following a - * generic auxiliary data pattern defined in ndarraytypes.h - * Use NPY_AUXDATA_CLONE and NPY_AUXDATA_FREE to deal with this data. - * - */ -typedef void (PyArray_StridedBinaryOp)(char *dst, npy_intp dst_stride, - char *src0, npy_intp src0_stride, - char *src1, npy_intp src1_stride, - npy_intp N, NpyAuxData *transferdata); +typedef int (PyArray_MaskedStridedUnaryOp)( + PyArrayMethod_Context *context, char *const *args, + const npy_intp *dimensions, const npy_intp *strides, + npy_bool *mask, npy_intp mask_stride, + NpyAuxData *auxdata); /* * Gives back a function pointer to a specialized function for copying * strided memory. Returns NULL if there is a problem with the inputs. * * aligned: - * Should be 1 if the src and dst pointers are always aligned, - * 0 otherwise. + * Should be 1 if the src and dst pointers always point to + * locations at which a uint of equal size to dtype->elsize + * would be aligned, 0 otherwise. * src_stride: * Should be the src stride if it will always be the same, * NPY_MAX_INTP otherwise. @@ -81,7 +71,7 @@ typedef void (PyArray_StridedBinaryOp)(char *dst, npy_intp dst_stride, * Should be the item size if it will always be the same, 0 otherwise. * */ -NPY_NO_EXPORT PyArray_StridedUnaryOp * +NPY_NO_EXPORT PyArrayMethod_StridedLoop * PyArray_GetStridedCopyFn(int aligned, npy_intp src_stride, npy_intp dst_stride, npy_intp itemsize); @@ -96,7 +86,7 @@ PyArray_GetStridedCopyFn(int aligned, * * Parameters are as for PyArray_GetStridedCopyFn. */ -NPY_NO_EXPORT PyArray_StridedUnaryOp * +NPY_NO_EXPORT PyArrayMethod_StridedLoop * PyArray_GetStridedCopySwapFn(int aligned, npy_intp src_stride, npy_intp dst_stride, npy_intp itemsize); @@ -111,7 +101,7 @@ PyArray_GetStridedCopySwapFn(int aligned, * * Parameters are as for PyArray_GetStridedCopyFn. */ -NPY_NO_EXPORT PyArray_StridedUnaryOp * +NPY_NO_EXPORT PyArrayMethod_StridedLoop * PyArray_GetStridedCopySwapPairFn(int aligned, npy_intp src_stride, npy_intp dst_stride, npy_intp itemsize); @@ -130,7 +120,7 @@ NPY_NO_EXPORT int PyArray_GetStridedZeroPadCopyFn(int aligned, int unicode_swap, npy_intp src_stride, npy_intp dst_stride, npy_intp src_itemsize, npy_intp dst_itemsize, - PyArray_StridedUnaryOp **outstransfer, + PyArrayMethod_StridedLoop **outstransfer, NpyAuxData **outtransferdata); /* @@ -139,7 +129,7 @@ PyArray_GetStridedZeroPadCopyFn(int aligned, int unicode_swap, * to dst_type_num. If a conversion is unsupported, returns NULL * without setting a Python exception. */ -NPY_NO_EXPORT PyArray_StridedUnaryOp * +NPY_NO_EXPORT PyArrayMethod_StridedLoop * PyArray_GetStridedNumericCastFn(int aligned, npy_intp src_stride, npy_intp dst_stride, int src_type_num, int dst_type_num); @@ -154,7 +144,7 @@ NPY_NO_EXPORT int PyArray_GetDTypeCopySwapFn(int aligned, npy_intp src_stride, npy_intp dst_stride, PyArray_Descr *dtype, - PyArray_StridedUnaryOp **outstransfer, + PyArrayMethod_StridedLoop **outstransfer, NpyAuxData **outtransferdata); /* @@ -165,8 +155,9 @@ PyArray_GetDTypeCopySwapFn(int aligned, * function when the transfer function is no longer required. * * aligned: - * Should be 1 if the src and dst pointers are always aligned, - * 0 otherwise. + * Should be 1 if the src and dst pointers always point to + * locations at which a uint of equal size to dtype->elsize + * would be aligned, 0 otherwise. * src_stride: * Should be the src stride if it will always be the same, * NPY_MAX_INTP otherwise. @@ -174,8 +165,7 @@ PyArray_GetDTypeCopySwapFn(int aligned, * Should be the dst stride if it will always be the same, * NPY_MAX_INTP otherwise. * src_dtype: - * The data type of source data. If this is NULL, a transfer - * function which sets the destination to zeros is produced. + * The data type of source data. Must not be NULL. * dst_dtype: * The data type of destination data. If this is NULL and * move_references is 1, a transfer function which decrements @@ -184,12 +174,10 @@ PyArray_GetDTypeCopySwapFn(int aligned, * If 0, the destination data gets new reference ownership. * If 1, the references from the source data are moved to * the destination data. - * out_stransfer: - * The resulting transfer function is placed here. - * out_transferdata: - * The auxiliary data for the transfer function is placed here. - * When finished with the transfer function, the caller must call - * NPY_AUXDATA_FREE on this data. + * cast_info: + * A pointer to an (uninitialized) `NPY_cast_info` struct, the caller + * must call `NPY_cast_info_xfree` on it (except on error) and handle + * its memory livespan. * out_needs_api: * If this is non-NULL, and the transfer function produced needs * to call into the (Python) API, this gets set to 1. This @@ -207,10 +195,27 @@ PyArray_GetDTypeTransferFunction(int aligned, npy_intp src_stride, npy_intp dst_stride, PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype, int move_references, - PyArray_StridedUnaryOp **out_stransfer, - NpyAuxData **out_transferdata, + NPY_cast_info *cast_info, int *out_needs_api); +NPY_NO_EXPORT int +get_fields_transfer_function(int aligned, + npy_intp src_stride, npy_intp dst_stride, + PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype, + int move_references, + PyArrayMethod_StridedLoop **out_stransfer, + NpyAuxData **out_transferdata, + int *out_needs_api); + +NPY_NO_EXPORT int +get_subarray_transfer_function(int aligned, + npy_intp src_stride, npy_intp dst_stride, + PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype, + int move_references, + PyArrayMethod_StridedLoop **out_stransfer, + NpyAuxData **out_transferdata, + int *out_needs_api); + /* * This is identical to PyArray_GetDTypeTransferFunction, but returns a * transfer function which also takes a mask as a parameter. The mask is used @@ -235,8 +240,7 @@ PyArray_GetMaskedDTypeTransferFunction(int aligned, PyArray_Descr *dst_dtype, PyArray_Descr *mask_dtype, int move_references, - PyArray_MaskedStridedUnaryOp **out_stransfer, - NpyAuxData **out_transferdata, + NPY_cast_info *cast_info, int *out_needs_api); /* @@ -264,6 +268,7 @@ PyArray_CastRawArrays(npy_intp count, * The return value is the number of elements it couldn't copy. A return value * of 0 means all elements were copied, a larger value means the end of * the n-dimensional array was reached before 'count' elements were copied. + * A negative return value indicates an error occurred. * * ndim: * The number of dimensions of the n-dimensional array. @@ -290,42 +295,37 @@ PyArray_CastRawArrays(npy_intp count, * sizes, for example a casting operation, the 'stransfer' function * should be specialized for that, in which case 'stransfer' will use * this parameter as the source item size. - * stransfer: - * The strided transfer function. - * transferdata: - * An auxiliary data pointer passed to the strided transfer function. - * This follows the conventions of NpyAuxData objects. + * cast_info: + * Pointer to the NPY_cast_info struct which summarizes all information + * necessary to perform a cast. */ NPY_NO_EXPORT npy_intp PyArray_TransferNDimToStrided(npy_intp ndim, char *dst, npy_intp dst_stride, - char *src, npy_intp *src_strides, npy_intp src_strides_inc, - npy_intp *coords, npy_intp coords_inc, - npy_intp *shape, npy_intp shape_inc, + char *src, npy_intp const *src_strides, npy_intp src_strides_inc, + npy_intp const *coords, npy_intp coords_inc, + npy_intp const *shape, npy_intp shape_inc, npy_intp count, npy_intp src_itemsize, - PyArray_StridedUnaryOp *stransfer, - NpyAuxData *transferdata); + NPY_cast_info *cast_info); NPY_NO_EXPORT npy_intp PyArray_TransferStridedToNDim(npy_intp ndim, - char *dst, npy_intp *dst_strides, npy_intp dst_strides_inc, + char *dst, npy_intp const *dst_strides, npy_intp dst_strides_inc, char *src, npy_intp src_stride, - npy_intp *coords, npy_intp coords_inc, - npy_intp *shape, npy_intp shape_inc, + npy_intp const *coords, npy_intp coords_inc, + npy_intp const *shape, npy_intp shape_inc, npy_intp count, npy_intp src_itemsize, - PyArray_StridedUnaryOp *stransfer, - NpyAuxData *transferdata); + NPY_cast_info *cast_info); NPY_NO_EXPORT npy_intp PyArray_TransferMaskedStridedToNDim(npy_intp ndim, - char *dst, npy_intp *dst_strides, npy_intp dst_strides_inc, + char *dst, npy_intp const *dst_strides, npy_intp dst_strides_inc, char *src, npy_intp src_stride, npy_bool *mask, npy_intp mask_stride, - npy_intp *coords, npy_intp coords_inc, - npy_intp *shape, npy_intp shape_inc, + npy_intp const *coords, npy_intp coords_inc, + npy_intp const *shape, npy_intp shape_inc, npy_intp count, npy_intp src_itemsize, - PyArray_MaskedStridedUnaryOp *stransfer, - NpyAuxData *data); + NPY_cast_info *cast_info); NPY_NO_EXPORT int mapiter_trivial_get(PyArrayObject *self, PyArrayObject *ind, @@ -358,8 +358,8 @@ mapiter_set(PyArrayMapIterObject *mit); * Returns 0 on success, -1 on failure. */ NPY_NO_EXPORT int -PyArray_PrepareOneRawArrayIter(int ndim, npy_intp *shape, - char *data, npy_intp *strides, +PyArray_PrepareOneRawArrayIter(int ndim, npy_intp const *shape, + char *data, npy_intp const *strides, int *out_ndim, npy_intp *out_shape, char **out_data, npy_intp *out_strides); @@ -380,9 +380,9 @@ PyArray_PrepareOneRawArrayIter(int ndim, npy_intp *shape, * Returns 0 on success, -1 on failure. */ NPY_NO_EXPORT int -PyArray_PrepareTwoRawArrayIter(int ndim, npy_intp *shape, - char *dataA, npy_intp *stridesA, - char *dataB, npy_intp *stridesB, +PyArray_PrepareTwoRawArrayIter(int ndim, npy_intp const *shape, + char *dataA, npy_intp const *stridesA, + char *dataB, npy_intp const *stridesB, int *out_ndim, npy_intp *out_shape, char **out_dataA, npy_intp *out_stridesA, char **out_dataB, npy_intp *out_stridesB); @@ -404,10 +404,10 @@ PyArray_PrepareTwoRawArrayIter(int ndim, npy_intp *shape, * Returns 0 on success, -1 on failure. */ NPY_NO_EXPORT int -PyArray_PrepareThreeRawArrayIter(int ndim, npy_intp *shape, - char *dataA, npy_intp *stridesA, - char *dataB, npy_intp *stridesB, - char *dataC, npy_intp *stridesC, +PyArray_PrepareThreeRawArrayIter(int ndim, npy_intp const *shape, + char *dataA, npy_intp const *stridesA, + char *dataB, npy_intp const *stridesB, + char *dataC, npy_intp const *stridesC, int *out_ndim, npy_intp *out_shape, char **out_dataA, npy_intp *out_stridesA, char **out_dataB, npy_intp *out_stridesB, @@ -631,7 +631,7 @@ npy_bswap8_unaligned(char * x) * * Here is example code for a single array: * - * if (PyArray_TRIVIALLY_ITERABLE(self) { + * if (PyArray_TRIVIALLY_ITERABLE(self)) { * char *data; * npy_intp count, stride; * @@ -647,25 +647,6 @@ npy_bswap8_unaligned(char * x) * // Create iterator, etc... * } * - * Here is example code for a pair of arrays: - * - * if (PyArray_TRIVIALLY_ITERABLE_PAIR(a1, a2) { - * char *data1, *data2; - * npy_intp count, stride1, stride2; - * - * PyArray_PREPARE_TRIVIAL_PAIR_ITERATION(a1, a2, count, - * data1, data2, stride1, stride2); - * - * while (count--) { - * // Use the data1 and data2 pointers - * - * data1 += stride1; - * data2 += stride2; - * } - * } - * else { - * // Create iterator, etc... - * } */ /* @@ -689,21 +670,16 @@ npy_bswap8_unaligned(char * x) #define PyArray_TRIVIALLY_ITERABLE_OP_NOREAD 0 #define PyArray_TRIVIALLY_ITERABLE_OP_READ 1 -#define PyArray_EQUIVALENTLY_ITERABLE_BASE(arr1, arr2) ( \ - PyArray_NDIM(arr1) == PyArray_NDIM(arr2) && \ - PyArray_CompareLists(PyArray_DIMS(arr1), \ - PyArray_DIMS(arr2), \ - PyArray_NDIM(arr1)) && \ - (PyArray_FLAGS(arr1)&(NPY_ARRAY_C_CONTIGUOUS| \ - NPY_ARRAY_F_CONTIGUOUS)) & \ - (PyArray_FLAGS(arr2)&(NPY_ARRAY_C_CONTIGUOUS| \ - NPY_ARRAY_F_CONTIGUOUS)) \ - ) +#define PyArray_TRIVIALLY_ITERABLE(arr) ( \ + PyArray_NDIM(arr) <= 1 || \ + PyArray_CHKFLAGS(arr, NPY_ARRAY_C_CONTIGUOUS) || \ + PyArray_CHKFLAGS(arr, NPY_ARRAY_F_CONTIGUOUS) \ + ) #define PyArray_TRIVIAL_PAIR_ITERATION_STRIDE(size, arr) ( \ - size == 1 ? 0 : ((PyArray_NDIM(arr) == 1) ? \ - PyArray_STRIDE(arr, 0) : \ - PyArray_ITEMSIZE(arr))) + assert(PyArray_TRIVIALLY_ITERABLE(arr)), \ + size == 1 ? 0 : ((PyArray_NDIM(arr) == 1) ? \ + PyArray_STRIDE(arr, 0) : PyArray_ITEMSIZE(arr))) static NPY_INLINE int PyArray_EQUIVALENTLY_ITERABLE_OVERLAP_OK(PyArrayObject *arr1, PyArrayObject *arr2, @@ -757,15 +733,22 @@ PyArray_EQUIVALENTLY_ITERABLE_OVERLAP_OK(PyArrayObject *arr1, PyArrayObject *arr return (!arr1_read || arr1_ahead) && (!arr2_read || arr2_ahead); } +#define PyArray_EQUIVALENTLY_ITERABLE_BASE(arr1, arr2) ( \ + PyArray_NDIM(arr1) == PyArray_NDIM(arr2) && \ + PyArray_CompareLists(PyArray_DIMS(arr1), \ + PyArray_DIMS(arr2), \ + PyArray_NDIM(arr1)) && \ + (PyArray_FLAGS(arr1)&(NPY_ARRAY_C_CONTIGUOUS| \ + NPY_ARRAY_F_CONTIGUOUS)) & \ + (PyArray_FLAGS(arr2)&(NPY_ARRAY_C_CONTIGUOUS| \ + NPY_ARRAY_F_CONTIGUOUS)) \ + ) + #define PyArray_EQUIVALENTLY_ITERABLE(arr1, arr2, arr1_read, arr2_read) ( \ PyArray_EQUIVALENTLY_ITERABLE_BASE(arr1, arr2) && \ PyArray_EQUIVALENTLY_ITERABLE_OVERLAP_OK( \ arr1, arr2, arr1_read, arr2_read)) -#define PyArray_TRIVIALLY_ITERABLE(arr) ( \ - PyArray_NDIM(arr) <= 1 || \ - PyArray_CHKFLAGS(arr, NPY_ARRAY_C_CONTIGUOUS) || \ - PyArray_CHKFLAGS(arr, NPY_ARRAY_F_CONTIGUOUS) \ - ) + #define PyArray_PREPARE_TRIVIAL_ITERATION(arr, count, data, stride) \ count = PyArray_SIZE(arr); \ data = PyArray_BYTES(arr); \ @@ -774,17 +757,6 @@ PyArray_EQUIVALENTLY_ITERABLE_OVERLAP_OK(PyArrayObject *arr1, PyArrayObject *arr PyArray_STRIDE(arr, 0) : \ PyArray_ITEMSIZE(arr))); - -#define PyArray_TRIVIALLY_ITERABLE_PAIR(arr1, arr2, arr1_read, arr2_read) ( \ - PyArray_TRIVIALLY_ITERABLE(arr1) && \ - (PyArray_NDIM(arr2) == 0 || \ - PyArray_EQUIVALENTLY_ITERABLE_BASE(arr1, arr2) || \ - (PyArray_NDIM(arr1) == 0 && \ - PyArray_TRIVIALLY_ITERABLE(arr2) \ - ) \ - ) && \ - PyArray_EQUIVALENTLY_ITERABLE_OVERLAP_OK(arr1, arr2, arr1_read, arr2_read) \ - ) #define PyArray_PREPARE_TRIVIAL_PAIR_ITERATION(arr1, arr2, \ count, \ data1, data2, \ @@ -798,45 +770,4 @@ PyArray_EQUIVALENTLY_ITERABLE_OVERLAP_OK(PyArrayObject *arr1, PyArrayObject *arr stride2 = PyArray_TRIVIAL_PAIR_ITERATION_STRIDE(size2, arr2); \ } -#define PyArray_TRIVIALLY_ITERABLE_TRIPLE(arr1, arr2, arr3, arr1_read, arr2_read, arr3_read) ( \ - PyArray_TRIVIALLY_ITERABLE(arr1) && \ - ((PyArray_NDIM(arr2) == 0 && \ - (PyArray_NDIM(arr3) == 0 || \ - PyArray_EQUIVALENTLY_ITERABLE_BASE(arr1, arr3) \ - ) \ - ) || \ - (PyArray_EQUIVALENTLY_ITERABLE_BASE(arr1, arr2) && \ - (PyArray_NDIM(arr3) == 0 || \ - PyArray_EQUIVALENTLY_ITERABLE_BASE(arr1, arr3) \ - ) \ - ) || \ - (PyArray_NDIM(arr1) == 0 && \ - PyArray_TRIVIALLY_ITERABLE(arr2) && \ - (PyArray_NDIM(arr3) == 0 || \ - PyArray_EQUIVALENTLY_ITERABLE_BASE(arr2, arr3) \ - ) \ - ) \ - ) && \ - PyArray_EQUIVALENTLY_ITERABLE_OVERLAP_OK(arr1, arr2, arr1_read, arr2_read) && \ - PyArray_EQUIVALENTLY_ITERABLE_OVERLAP_OK(arr1, arr3, arr1_read, arr3_read) && \ - PyArray_EQUIVALENTLY_ITERABLE_OVERLAP_OK(arr2, arr3, arr2_read, arr3_read) \ - ) - -#define PyArray_PREPARE_TRIVIAL_TRIPLE_ITERATION(arr1, arr2, arr3, \ - count, \ - data1, data2, data3, \ - stride1, stride2, stride3) { \ - npy_intp size1 = PyArray_SIZE(arr1); \ - npy_intp size2 = PyArray_SIZE(arr2); \ - npy_intp size3 = PyArray_SIZE(arr3); \ - count = ((size1 > size2) || size1 == 0) ? size1 : size2; \ - count = ((size3 > count) || size3 == 0) ? size3 : count; \ - data1 = PyArray_BYTES(arr1); \ - data2 = PyArray_BYTES(arr2); \ - data3 = PyArray_BYTES(arr3); \ - stride1 = PyArray_TRIVIAL_PAIR_ITERATION_STRIDE(size1, arr1); \ - stride2 = PyArray_TRIVIAL_PAIR_ITERATION_STRIDE(size2, arr2); \ - stride3 = PyArray_TRIVIAL_PAIR_ITERATION_STRIDE(size3, arr3); \ - } - -#endif +#endif /* NUMPY_CORE_SRC_COMMON_LOWLEVEL_STRIDED_LOOPS_H_ */ diff --git a/numpy/core/src/private/mem_overlap.c b/numpy/core/src/common/mem_overlap.c similarity index 99% rename from numpy/core/src/private/mem_overlap.c rename to numpy/core/src/common/mem_overlap.c index 21db1893b3d1..2632e1413f48 100644 --- a/numpy/core/src/private/mem_overlap.c +++ b/numpy/core/src/common/mem_overlap.c @@ -127,7 +127,7 @@ ends up considering all values x3=0...5 separately. The upper bound for work done is prod(shape_a)*prod(shape_b), which scales - faster than than work done by binary ufuncs, after broadcasting, + faster than work done by binary ufuncs, after broadcasting, prod(shape_a). The bound may be loose, but it is possible to construct hard instances where ufunc is faster (adapted from [2,3]):: @@ -181,9 +181,11 @@ All rights reserved. Licensed under 3-clause BSD license, see LICENSE.txt. */ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION + +#define PY_SSIZE_T_CLEAN #include <Python.h> -#define NPY_NO_DEPRECATED_API NPY_API_VERSION #include "numpy/ndarraytypes.h" #include "mem_overlap.h" #include "npy_extint128.h" diff --git a/numpy/core/src/private/mem_overlap.h b/numpy/core/src/common/mem_overlap.h similarity index 90% rename from numpy/core/src/private/mem_overlap.h rename to numpy/core/src/common/mem_overlap.h index 8044f1663198..3aa4f798b370 100644 --- a/numpy/core/src/private/mem_overlap.h +++ b/numpy/core/src/common/mem_overlap.h @@ -1,5 +1,5 @@ -#ifndef MEM_OVERLAP_H_ -#define MEM_OVERLAP_H_ +#ifndef NUMPY_CORE_SRC_COMMON_MEM_OVERLAP_H_ +#define NUMPY_CORE_SRC_COMMON_MEM_OVERLAP_H_ #include "npy_config.h" #include "numpy/ndarraytypes.h" @@ -46,5 +46,4 @@ offset_bounds_from_strides(const int itemsize, const int nd, const npy_intp *dims, const npy_intp *strides, npy_intp *lower_offset, npy_intp *upper_offset); -#endif - +#endif /* NUMPY_CORE_SRC_COMMON_MEM_OVERLAP_H_ */ diff --git a/numpy/core/src/common/npy_argparse.c b/numpy/core/src/common/npy_argparse.c new file mode 100644 index 000000000000..76123c1ed864 --- /dev/null +++ b/numpy/core/src/common/npy_argparse.c @@ -0,0 +1,422 @@ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE + +#define PY_SSIZE_T_CLEAN +#include <Python.h> + +#include "numpy/ndarraytypes.h" +#include "npy_argparse.h" +#include "npy_pycompat.h" +#include "npy_import.h" + +#include "arrayfunction_override.h" + + +/** + * Small wrapper converting to array just like CPython does. + * + * We could use our own PyArray_PyIntAsInt function, but it handles floats + * differently. + * A disadvantage of this function compared to ``PyArg_*("i")`` code is that + * it will not say which parameter is wrong. + * + * @param obj The python object to convert + * @param value The output value + * + * @returns 0 on failure and 1 on success (`NPY_FAIL`, `NPY_SUCCEED`) + */ +NPY_NO_EXPORT int +PyArray_PythonPyIntFromInt(PyObject *obj, int *value) +{ + /* Pythons behaviour is to check only for float explicitly... */ + if (NPY_UNLIKELY(PyFloat_Check(obj))) { + PyErr_SetString(PyExc_TypeError, + "integer argument expected, got float"); + return NPY_FAIL; + } + + long result = PyLong_AsLong(obj); + if (NPY_UNLIKELY((result == -1) && PyErr_Occurred())) { + return NPY_FAIL; + } + if (NPY_UNLIKELY((result > INT_MAX) || (result < INT_MIN))) { + PyErr_SetString(PyExc_OverflowError, + "Python int too large to convert to C int"); + return NPY_FAIL; + } + else { + *value = (int)result; + return NPY_SUCCEED; + } +} + + +typedef int convert(PyObject *, void *); + +/** + * Internal function to initialize keyword argument parsing. + * + * This does a few simple jobs: + * + * * Check the input for consistency to find coding errors, for example + * a parameter not marked with | after one marked with | (optional). + * 2. Find the number of positional-only arguments, the number of + * total, required, and keyword arguments. + * 3. Intern all keyword arguments strings to allow fast, identity based + * parsing and avoid string creation overhead on each call. + * + * @param funcname Name of the function, mainly used for errors. + * @param cache A cache object stored statically in the parsing function + * @param va_orig Argument list to npy_parse_arguments + * @return 0 on success, -1 on failure + */ +static int +initialize_keywords(const char *funcname, + _NpyArgParserCache *cache, va_list va_orig) { + va_list va; + int nargs = 0; + int nkwargs = 0; + int npositional_only = 0; + int nrequired = 0; + int npositional = 0; + char state = '\0'; + + va_copy(va, va_orig); + while (1) { + /* Count length first: */ + char *name = va_arg(va, char *); + convert *converter = va_arg(va, convert *); + void *data = va_arg(va, void *); + + /* Check if this is the sentinel, only converter may be NULL */ + if ((name == NULL) && (converter == NULL) && (data == NULL)) { + break; + } + + if (name == NULL) { + PyErr_Format(PyExc_SystemError, + "NumPy internal error: name is NULL in %s() at " + "argument %d.", funcname, nargs); + va_end(va); + return -1; + } + if (data == NULL) { + PyErr_Format(PyExc_SystemError, + "NumPy internal error: data is NULL in %s() at " + "argument %d.", funcname, nargs); + va_end(va); + return -1; + } + + nargs += 1; + if (*name == '|') { + if (state == '$') { + PyErr_Format(PyExc_SystemError, + "NumPy internal error: positional argument `|` " + "after keyword only `$` one to %s() at argument %d.", + funcname, nargs); + va_end(va); + return -1; + } + state = '|'; + name++; /* advance to actual name. */ + npositional += 1; + } + else if (*name == '$') { + state = '$'; + name++; /* advance to actual name. */ + } + else { + if (state != '\0') { + PyErr_Format(PyExc_SystemError, + "NumPy internal error: non-required argument after " + "required | or $ one to %s() at argument %d.", + funcname, nargs); + va_end(va); + return -1; + } + + nrequired += 1; + npositional += 1; + } + + if (*name == '\0') { + /* Empty string signals positional only */ + if (state != '\0') { + PyErr_Format(PyExc_SystemError, + "NumPy internal error: non-kwarg marked with | or $ " + "to %s() at argument %d.", funcname, nargs); + va_end(va); + return -1; + } + npositional_only += 1; + } + else { + nkwargs += 1; + } + } + va_end(va); + + if (npositional == -1) { + npositional = nargs; + } + + if (nargs > _NPY_MAX_KWARGS) { + PyErr_Format(PyExc_SystemError, + "NumPy internal error: function %s() has %d arguments, but " + "the maximum is currently limited to %d for easier parsing; " + "it can be increased by modifying `_NPY_MAX_KWARGS`.", + funcname, nargs, _NPY_MAX_KWARGS); + return -1; + } + + /* + * Do any necessary string allocation and interning, + * creating a caching object. + */ + cache->nargs = nargs; + cache->npositional_only = npositional_only; + cache->npositional = npositional; + cache->nrequired = nrequired; + + /* NULL kw_strings for easier cleanup (and NULL termination) */ + memset(cache->kw_strings, 0, sizeof(PyObject *) * (nkwargs + 1)); + + va_copy(va, va_orig); + for (int i = 0; i < nargs; i++) { + /* Advance through non-kwargs, which do not require setup. */ + char *name = va_arg(va, char *); + va_arg(va, convert *); + va_arg(va, void *); + + if (*name == '|' || *name == '$') { + name++; /* ignore | and $ */ + } + if (i >= npositional_only) { + int i_kwarg = i - npositional_only; + cache->kw_strings[i_kwarg] = PyUString_InternFromString(name); + if (cache->kw_strings[i_kwarg] == NULL) { + va_end(va); + goto error; + } + } + } + + va_end(va); + return 0; + +error: + for (int i = 0; i < nkwargs; i++) { + Py_XDECREF(cache->kw_strings[i]); + } + cache->npositional = -1; /* not initialized */ + return -1; +} + + +static int +raise_incorrect_number_of_positional_args(const char *funcname, + const _NpyArgParserCache *cache, Py_ssize_t len_args) +{ + if (cache->npositional == cache->nrequired) { + PyErr_Format(PyExc_TypeError, + "%s() takes %d positional arguments but %zd were given", + funcname, cache->npositional, len_args); + } + else { + PyErr_Format(PyExc_TypeError, + "%s() takes from %d to %d positional arguments but " + "%zd were given", + funcname, cache->nrequired, cache->npositional, len_args); + } + return -1; +} + +static void +raise_missing_argument(const char *funcname, + const _NpyArgParserCache *cache, int i) +{ + if (i < cache->npositional_only) { + PyErr_Format(PyExc_TypeError, + "%s() missing required positional argument %d", + funcname, i); + } + else { + PyObject *kw = cache->kw_strings[i - cache->npositional_only]; + PyErr_Format(PyExc_TypeError, + "%s() missing required argument '%S' (pos %d)", + funcname, kw, i); + } +} + + +/** + * Generic helper for argument parsing + * + * See macro version for an example pattern of how to use this function. + * + * @param funcname + * @param cache + * @param args Python passed args (METH_FASTCALL) + * @param len_args + * @param kwnames + * @param ... List of arguments (see macro version). + * + * @return Returns 0 on success and -1 on failure. + */ +NPY_NO_EXPORT int +_npy_parse_arguments(const char *funcname, + /* cache_ptr is a NULL initialized persistent storage for data */ + _NpyArgParserCache *cache, + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames, + /* ... is NULL, NULL, NULL terminated: name, converter, value */ + ...) +{ + if (NPY_UNLIKELY(cache->npositional == -1)) { + va_list va; + va_start(va, kwnames); + + int res = initialize_keywords(funcname, cache, va); + va_end(va); + if (res < 0) { + return -1; + } + } + + if (NPY_UNLIKELY(len_args > cache->npositional)) { + return raise_incorrect_number_of_positional_args( + funcname, cache, len_args); + } + + /* NOTE: Could remove the limit but too many kwargs are slow anyway. */ + PyObject *all_arguments[NPY_MAXARGS]; + + for (Py_ssize_t i = 0; i < len_args; i++) { + all_arguments[i] = args[i]; + } + + /* Without kwargs, do not iterate all converters. */ + int max_nargs = (int)len_args; + Py_ssize_t len_kwargs = 0; + + /* If there are any kwargs, first handle them */ + if (NPY_LIKELY(kwnames != NULL)) { + len_kwargs = PyTuple_GET_SIZE(kwnames); + max_nargs = cache->nargs; + + for (int i = len_args; i < cache->nargs; i++) { + all_arguments[i] = NULL; + } + + for (Py_ssize_t i = 0; i < len_kwargs; i++) { + PyObject *key = PyTuple_GET_ITEM(kwnames, i); + PyObject *value = args[i + len_args]; + PyObject *const *name; + + /* Super-fast path, check identity: */ + for (name = cache->kw_strings; *name != NULL; name++) { + if (*name == key) { + break; + } + } + if (NPY_UNLIKELY(*name == NULL)) { + /* Slow fallback, if identity checks failed for some reason */ + for (name = cache->kw_strings; *name != NULL; name++) { + int eq = PyObject_RichCompareBool(*name, key, Py_EQ); + if (eq == -1) { + return -1; + } + else if (eq) { + break; + } + } + if (NPY_UNLIKELY(*name == NULL)) { + /* Invalid keyword argument. */ + PyErr_Format(PyExc_TypeError, + "%s() got an unexpected keyword argument '%S'", + funcname, key); + return -1; + } + } + + Py_ssize_t param_pos = ( + (name - cache->kw_strings) + cache->npositional_only); + + /* There could be an identical positional argument */ + if (NPY_UNLIKELY(all_arguments[param_pos] != NULL)) { + PyErr_Format(PyExc_TypeError, + "argument for %s() given by name ('%S') and position " + "(position %zd)", funcname, key, param_pos); + return -1; + } + + all_arguments[param_pos] = value; + } + } + + /* + * There cannot be too many args, too many kwargs would find an + * incorrect one above. + */ + assert(len_args + len_kwargs <= cache->nargs); + + /* At this time `all_arguments` holds either NULLs or the objects */ + va_list va; + va_start(va, kwnames); + + for (int i = 0; i < max_nargs; i++) { + va_arg(va, char *); + convert *converter = va_arg(va, convert *); + void *data = va_arg(va, void *); + + if (all_arguments[i] == NULL) { + continue; + } + + int res; + if (converter == NULL) { + *((PyObject **) data) = all_arguments[i]; + continue; + } + res = converter(all_arguments[i], data); + + if (NPY_UNLIKELY(res == NPY_SUCCEED)) { + continue; + } + else if (NPY_UNLIKELY(res == NPY_FAIL)) { + /* It is usually the users responsibility to clean up. */ + goto converting_failed; + } + else if (NPY_UNLIKELY(res == Py_CLEANUP_SUPPORTED)) { + /* TODO: Implementing cleanup if/when needed should not be hard */ + PyErr_Format(PyExc_SystemError, + "converter cleanup of parameter %d to %s() not supported.", + i, funcname); + goto converting_failed; + } + assert(0); + } + + /* Required arguments are typically not passed as keyword arguments */ + if (NPY_UNLIKELY(len_args < cache->nrequired)) { + /* (PyArg_* also does this after the actual parsing is finished) */ + if (NPY_UNLIKELY(max_nargs < cache->nrequired)) { + raise_missing_argument(funcname, cache, max_nargs); + goto converting_failed; + } + for (int i = 0; i < cache->nrequired; i++) { + if (NPY_UNLIKELY(all_arguments[i] == NULL)) { + raise_missing_argument(funcname, cache, i); + goto converting_failed; + } + } + } + + va_end(va); + return 0; + +converting_failed: + va_end(va); + return -1; + +} diff --git a/numpy/core/src/common/npy_argparse.h b/numpy/core/src/common/npy_argparse.h new file mode 100644 index 000000000000..f4122103d22b --- /dev/null +++ b/numpy/core/src/common/npy_argparse.h @@ -0,0 +1,96 @@ +#ifndef NUMPY_CORE_SRC_COMMON_NPY_ARGPARSE_H +#define NUMPY_CORE_SRC_COMMON_NPY_ARGPARSE_H + +#include <Python.h> +#include "numpy/ndarraytypes.h" + +/* + * This file defines macros to help with keyword argument parsing. + * This solves two issues as of now: + * 1. Pythons C-API PyArg_* keyword argument parsers are slow, due to + * not caching the strings they use. + * 2. It allows the use of METH_ARGPARSE (and `tp_vectorcall`) + * when available in Python, which removes a large chunk of overhead. + * + * Internally CPython achieves similar things by using a code generator + * argument clinic. NumPy may well decide to use argument clinic or a different + * solution in the future. + */ + +NPY_NO_EXPORT int +PyArray_PythonPyIntFromInt(PyObject *obj, int *value); + + +#define _NPY_MAX_KWARGS 15 + +typedef struct { + int npositional; + int nargs; + int npositional_only; + int nrequired; + /* Null terminated list of keyword argument name strings */ + PyObject *kw_strings[_NPY_MAX_KWARGS+1]; +} _NpyArgParserCache; + + +/* + * The sole purpose of this macro is to hide the argument parsing cache. + * Since this cache must be static, this also removes a source of error. + */ +#define NPY_PREPARE_ARGPARSER static _NpyArgParserCache __argparse_cache = {-1} + +/** + * Macro to help with argument parsing. + * + * The pattern for using this macro is by defining the method as: + * + * @code + * static PyObject * + * my_method(PyObject *self, + * PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) + * { + * NPY_PREPARE_ARGPARSER; + * + * PyObject *argument1, *argument3; + * int argument2 = -1; + * if (npy_parse_arguments("method", args, len_args, kwnames), + * "argument1", NULL, &argument1, + * "|argument2", &PyArray_PythonPyIntFromInt, &argument2, + * "$argument3", NULL, &argument3, + * NULL, NULL, NULL) < 0) { + * return NULL; + * } + * } + * @endcode + * + * The `NPY_PREPARE_ARGPARSER` macro sets up a static cache variable necessary + * to hold data for speeding up the parsing. `npy_parse_arguments` must be + * used in cunjunction with the macro defined in the same scope. + * (No two `npy_parse_arguments` may share a single `NPY_PREPARE_ARGPARSER`.) + * + * @param funcname + * @param args Python passed args (METH_FASTCALL) + * @param len_args Number of arguments (not flagged) + * @param kwnames Tuple as passed by METH_FASTCALL or NULL. + * @param ... List of arguments must be param1_name, param1_converter, + * *param1_outvalue, param2_name, ..., NULL, NULL, NULL. + * Where name is ``char *``, ``converter`` a python converter + * function or NULL and ``outvalue`` is the ``void *`` passed to + * the converter (holding the converted data or a borrowed + * reference if converter is NULL). + * + * @return Returns 0 on success and -1 on failure. + */ +NPY_NO_EXPORT int +_npy_parse_arguments(const char *funcname, + /* cache_ptr is a NULL initialized persistent storage for data */ + _NpyArgParserCache *cache_ptr, + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames, + /* va_list is NULL, NULL, NULL terminated: name, converter, value */ + ...) NPY_GCC_NONNULL(1); + +#define npy_parse_arguments(funcname, args, len_args, kwnames, ...) \ + _npy_parse_arguments(funcname, &__argparse_cache, \ + args, len_args, kwnames, __VA_ARGS__) + +#endif /* NUMPY_CORE_SRC_COMMON_NPY_ARGPARSE_H */ diff --git a/numpy/core/src/private/npy_binsearch.h.src b/numpy/core/src/common/npy_binsearch.h.src similarity index 97% rename from numpy/core/src/private/npy_binsearch.h.src rename to numpy/core/src/common/npy_binsearch.h.src index ce3b34b0ef4c..052c444828c3 100644 --- a/numpy/core/src/private/npy_binsearch.h.src +++ b/numpy/core/src/common/npy_binsearch.h.src @@ -40,12 +40,12 @@ typedef struct { * cfloat, cdouble, clongdouble, datetime, timedelta# */ -NPY_VISIBILITY_HIDDEN void +NPY_NO_EXPORT void binsearch_@side@_@suff@(const char *arr, const char *key, char *ret, npy_intp arr_len, npy_intp key_len, npy_intp arr_str, npy_intp key_str, npy_intp ret_str, PyArrayObject *unused); -NPY_VISIBILITY_HIDDEN int +NPY_NO_EXPORT int argbinsearch_@side@_@suff@(const char *arr, const char *key, const char *sort, char *ret, npy_intp arr_len, npy_intp key_len, @@ -54,12 +54,12 @@ argbinsearch_@side@_@suff@(const char *arr, const char *key, PyArrayObject *unused); /**end repeat1**/ -NPY_VISIBILITY_HIDDEN void +NPY_NO_EXPORT void npy_binsearch_@side@(const char *arr, const char *key, char *ret, npy_intp arr_len, npy_intp key_len, npy_intp arr_str, npy_intp key_str, npy_intp ret_str, PyArrayObject *cmp); -NPY_VISIBILITY_HIDDEN int +NPY_NO_EXPORT int npy_argbinsearch_@side@(const char *arr, const char *key, const char *sort, char *ret, npy_intp arr_len, npy_intp key_len, diff --git a/numpy/core/src/common/npy_cblas.h b/numpy/core/src/common/npy_cblas.h new file mode 100644 index 000000000000..30fec1a653d8 --- /dev/null +++ b/numpy/core/src/common/npy_cblas.h @@ -0,0 +1,101 @@ +/* + * This header provides numpy a consistent interface to CBLAS code. It is needed + * because not all providers of cblas provide cblas.h. For instance, MKL provides + * mkl_cblas.h and also typedefs the CBLAS_XXX enums. + */ +#ifndef NUMPY_CORE_SRC_COMMON_NPY_CBLAS_H_ +#define NUMPY_CORE_SRC_COMMON_NPY_CBLAS_H_ + +#include <stddef.h> + +/* Allow the use in C++ code. */ +#ifdef __cplusplus +extern "C" +{ +#endif + +/* + * Enumerated and derived types + */ +enum CBLAS_ORDER {CblasRowMajor=101, CblasColMajor=102}; +enum CBLAS_TRANSPOSE {CblasNoTrans=111, CblasTrans=112, CblasConjTrans=113}; +enum CBLAS_UPLO {CblasUpper=121, CblasLower=122}; +enum CBLAS_DIAG {CblasNonUnit=131, CblasUnit=132}; +enum CBLAS_SIDE {CblasLeft=141, CblasRight=142}; + +#define CBLAS_INDEX size_t /* this may vary between platforms */ + +#ifdef NO_APPEND_FORTRAN +#define BLAS_FORTRAN_SUFFIX +#else +#define BLAS_FORTRAN_SUFFIX _ +#endif + +#ifndef BLAS_SYMBOL_PREFIX +#define BLAS_SYMBOL_PREFIX +#endif + +#ifndef BLAS_SYMBOL_SUFFIX +#define BLAS_SYMBOL_SUFFIX +#endif + +#define BLAS_FUNC_CONCAT(name,prefix,suffix,suffix2) prefix ## name ## suffix ## suffix2 +#define BLAS_FUNC_EXPAND(name,prefix,suffix,suffix2) BLAS_FUNC_CONCAT(name,prefix,suffix,suffix2) + +#define CBLAS_FUNC(name) BLAS_FUNC_EXPAND(name,BLAS_SYMBOL_PREFIX,,BLAS_SYMBOL_SUFFIX) +#define BLAS_FUNC(name) BLAS_FUNC_EXPAND(name,BLAS_SYMBOL_PREFIX,BLAS_FORTRAN_SUFFIX,BLAS_SYMBOL_SUFFIX) + +#ifdef HAVE_BLAS_ILP64 +#define CBLAS_INT npy_int64 +#define CBLAS_INT_MAX NPY_MAX_INT64 +#else +#define CBLAS_INT int +#define CBLAS_INT_MAX INT_MAX +#endif + +#define BLASNAME(name) CBLAS_FUNC(name) +#define BLASINT CBLAS_INT + +#include "npy_cblas_base.h" + +#undef BLASINT +#undef BLASNAME + + +/* + * Convert NumPy stride to BLAS stride. Returns 0 if conversion cannot be done + * (BLAS won't handle negative or zero strides the way we want). + */ +static NPY_INLINE CBLAS_INT +blas_stride(npy_intp stride, unsigned itemsize) +{ + /* + * Should probably check pointer alignment also, but this may cause + * problems if we require complex to be 16 byte aligned. + */ + if (stride > 0 && (stride % itemsize) == 0) { + stride /= itemsize; + if (stride <= CBLAS_INT_MAX) { + return stride; + } + } + return 0; +} + +/* + * Define a chunksize for CBLAS. + * + * The chunksize is the greatest power of two less than CBLAS_INT_MAX. + */ +#if NPY_MAX_INTP > CBLAS_INT_MAX +# define NPY_CBLAS_CHUNK (CBLAS_INT_MAX / 2 + 1) +#else +# define NPY_CBLAS_CHUNK NPY_MAX_INTP +#endif + + +#ifdef __cplusplus +} +#endif + +#endif /* NUMPY_CORE_SRC_COMMON_NPY_CBLAS_H_ */ diff --git a/numpy/core/src/common/npy_cblas_base.h b/numpy/core/src/common/npy_cblas_base.h new file mode 100644 index 000000000000..12dfb2e784f9 --- /dev/null +++ b/numpy/core/src/common/npy_cblas_base.h @@ -0,0 +1,562 @@ +/* + * This header provides numpy a consistent interface to CBLAS code. It is needed + * because not all providers of cblas provide cblas.h. For instance, MKL provides + * mkl_cblas.h and also typedefs the CBLAS_XXX enums. + */ + +/* + * =========================================================================== + * Prototypes for level 1 BLAS functions (complex are recast as routines) + * =========================================================================== + */ +#ifndef NUMPY_CORE_SRC_COMMON_NPY_CBLAS_BASE_H_ +#define NUMPY_CORE_SRC_COMMON_NPY_CBLAS_BASE_H_ + +float BLASNAME(cblas_sdsdot)(const BLASINT N, const float alpha, const float *X, + const BLASINT incX, const float *Y, const BLASINT incY); +double BLASNAME(cblas_dsdot)(const BLASINT N, const float *X, const BLASINT incX, const float *Y, + const BLASINT incY); +float BLASNAME(cblas_sdot)(const BLASINT N, const float *X, const BLASINT incX, + const float *Y, const BLASINT incY); +double BLASNAME(cblas_ddot)(const BLASINT N, const double *X, const BLASINT incX, + const double *Y, const BLASINT incY); + +/* + * Functions having prefixes Z and C only + */ +void BLASNAME(cblas_cdotu_sub)(const BLASINT N, const void *X, const BLASINT incX, + const void *Y, const BLASINT incY, void *dotu); +void BLASNAME(cblas_cdotc_sub)(const BLASINT N, const void *X, const BLASINT incX, + const void *Y, const BLASINT incY, void *dotc); + +void BLASNAME(cblas_zdotu_sub)(const BLASINT N, const void *X, const BLASINT incX, + const void *Y, const BLASINT incY, void *dotu); +void BLASNAME(cblas_zdotc_sub)(const BLASINT N, const void *X, const BLASINT incX, + const void *Y, const BLASINT incY, void *dotc); + + +/* + * Functions having prefixes S D SC DZ + */ +float BLASNAME(cblas_snrm2)(const BLASINT N, const float *X, const BLASINT incX); +float BLASNAME(cblas_sasum)(const BLASINT N, const float *X, const BLASINT incX); + +double BLASNAME(cblas_dnrm2)(const BLASINT N, const double *X, const BLASINT incX); +double BLASNAME(cblas_dasum)(const BLASINT N, const double *X, const BLASINT incX); + +float BLASNAME(cblas_scnrm2)(const BLASINT N, const void *X, const BLASINT incX); +float BLASNAME(cblas_scasum)(const BLASINT N, const void *X, const BLASINT incX); + +double BLASNAME(cblas_dznrm2)(const BLASINT N, const void *X, const BLASINT incX); +double BLASNAME(cblas_dzasum)(const BLASINT N, const void *X, const BLASINT incX); + + +/* + * Functions having standard 4 prefixes (S D C Z) + */ +CBLAS_INDEX BLASNAME(cblas_isamax)(const BLASINT N, const float *X, const BLASINT incX); +CBLAS_INDEX BLASNAME(cblas_idamax)(const BLASINT N, const double *X, const BLASINT incX); +CBLAS_INDEX BLASNAME(cblas_icamax)(const BLASINT N, const void *X, const BLASINT incX); +CBLAS_INDEX BLASNAME(cblas_izamax)(const BLASINT N, const void *X, const BLASINT incX); + +/* + * =========================================================================== + * Prototypes for level 1 BLAS routines + * =========================================================================== + */ + +/* + * Routines with standard 4 prefixes (s, d, c, z) + */ +void BLASNAME(cblas_sswap)(const BLASINT N, float *X, const BLASINT incX, + float *Y, const BLASINT incY); +void BLASNAME(cblas_scopy)(const BLASINT N, const float *X, const BLASINT incX, + float *Y, const BLASINT incY); +void BLASNAME(cblas_saxpy)(const BLASINT N, const float alpha, const float *X, + const BLASINT incX, float *Y, const BLASINT incY); + +void BLASNAME(cblas_dswap)(const BLASINT N, double *X, const BLASINT incX, + double *Y, const BLASINT incY); +void BLASNAME(cblas_dcopy)(const BLASINT N, const double *X, const BLASINT incX, + double *Y, const BLASINT incY); +void BLASNAME(cblas_daxpy)(const BLASINT N, const double alpha, const double *X, + const BLASINT incX, double *Y, const BLASINT incY); + +void BLASNAME(cblas_cswap)(const BLASINT N, void *X, const BLASINT incX, + void *Y, const BLASINT incY); +void BLASNAME(cblas_ccopy)(const BLASINT N, const void *X, const BLASINT incX, + void *Y, const BLASINT incY); +void BLASNAME(cblas_caxpy)(const BLASINT N, const void *alpha, const void *X, + const BLASINT incX, void *Y, const BLASINT incY); + +void BLASNAME(cblas_zswap)(const BLASINT N, void *X, const BLASINT incX, + void *Y, const BLASINT incY); +void BLASNAME(cblas_zcopy)(const BLASINT N, const void *X, const BLASINT incX, + void *Y, const BLASINT incY); +void BLASNAME(cblas_zaxpy)(const BLASINT N, const void *alpha, const void *X, + const BLASINT incX, void *Y, const BLASINT incY); + + +/* + * Routines with S and D prefix only + */ +void BLASNAME(cblas_srotg)(float *a, float *b, float *c, float *s); +void BLASNAME(cblas_srotmg)(float *d1, float *d2, float *b1, const float b2, float *P); +void BLASNAME(cblas_srot)(const BLASINT N, float *X, const BLASINT incX, + float *Y, const BLASINT incY, const float c, const float s); +void BLASNAME(cblas_srotm)(const BLASINT N, float *X, const BLASINT incX, + float *Y, const BLASINT incY, const float *P); + +void BLASNAME(cblas_drotg)(double *a, double *b, double *c, double *s); +void BLASNAME(cblas_drotmg)(double *d1, double *d2, double *b1, const double b2, double *P); +void BLASNAME(cblas_drot)(const BLASINT N, double *X, const BLASINT incX, + double *Y, const BLASINT incY, const double c, const double s); +void BLASNAME(cblas_drotm)(const BLASINT N, double *X, const BLASINT incX, + double *Y, const BLASINT incY, const double *P); + + +/* + * Routines with S D C Z CS and ZD prefixes + */ +void BLASNAME(cblas_sscal)(const BLASINT N, const float alpha, float *X, const BLASINT incX); +void BLASNAME(cblas_dscal)(const BLASINT N, const double alpha, double *X, const BLASINT incX); +void BLASNAME(cblas_cscal)(const BLASINT N, const void *alpha, void *X, const BLASINT incX); +void BLASNAME(cblas_zscal)(const BLASINT N, const void *alpha, void *X, const BLASINT incX); +void BLASNAME(cblas_csscal)(const BLASINT N, const float alpha, void *X, const BLASINT incX); +void BLASNAME(cblas_zdscal)(const BLASINT N, const double alpha, void *X, const BLASINT incX); + +/* + * =========================================================================== + * Prototypes for level 2 BLAS + * =========================================================================== + */ + +/* + * Routines with standard 4 prefixes (S, D, C, Z) + */ +void BLASNAME(cblas_sgemv)(const enum CBLAS_ORDER order, + const enum CBLAS_TRANSPOSE TransA, const BLASINT M, const BLASINT N, + const float alpha, const float *A, const BLASINT lda, + const float *X, const BLASINT incX, const float beta, + float *Y, const BLASINT incY); +void BLASNAME(cblas_sgbmv)(const enum CBLAS_ORDER order, + const enum CBLAS_TRANSPOSE TransA, const BLASINT M, const BLASINT N, + const BLASINT KL, const BLASINT KU, const float alpha, + const float *A, const BLASINT lda, const float *X, + const BLASINT incX, const float beta, float *Y, const BLASINT incY); +void BLASNAME(cblas_strmv)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const BLASINT N, const float *A, const BLASINT lda, + float *X, const BLASINT incX); +void BLASNAME(cblas_stbmv)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const BLASINT N, const BLASINT K, const float *A, const BLASINT lda, + float *X, const BLASINT incX); +void BLASNAME(cblas_stpmv)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const BLASINT N, const float *Ap, float *X, const BLASINT incX); +void BLASNAME(cblas_strsv)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const BLASINT N, const float *A, const BLASINT lda, float *X, + const BLASINT incX); +void BLASNAME(cblas_stbsv)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const BLASINT N, const BLASINT K, const float *A, const BLASINT lda, + float *X, const BLASINT incX); +void BLASNAME(cblas_stpsv)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const BLASINT N, const float *Ap, float *X, const BLASINT incX); + +void BLASNAME(cblas_dgemv)(const enum CBLAS_ORDER order, + const enum CBLAS_TRANSPOSE TransA, const BLASINT M, const BLASINT N, + const double alpha, const double *A, const BLASINT lda, + const double *X, const BLASINT incX, const double beta, + double *Y, const BLASINT incY); +void BLASNAME(cblas_dgbmv)(const enum CBLAS_ORDER order, + const enum CBLAS_TRANSPOSE TransA, const BLASINT M, const BLASINT N, + const BLASINT KL, const BLASINT KU, const double alpha, + const double *A, const BLASINT lda, const double *X, + const BLASINT incX, const double beta, double *Y, const BLASINT incY); +void BLASNAME(cblas_dtrmv)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const BLASINT N, const double *A, const BLASINT lda, + double *X, const BLASINT incX); +void BLASNAME(cblas_dtbmv)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const BLASINT N, const BLASINT K, const double *A, const BLASINT lda, + double *X, const BLASINT incX); +void BLASNAME(cblas_dtpmv)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const BLASINT N, const double *Ap, double *X, const BLASINT incX); +void BLASNAME(cblas_dtrsv)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const BLASINT N, const double *A, const BLASINT lda, double *X, + const BLASINT incX); +void BLASNAME(cblas_dtbsv)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const BLASINT N, const BLASINT K, const double *A, const BLASINT lda, + double *X, const BLASINT incX); +void BLASNAME(cblas_dtpsv)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const BLASINT N, const double *Ap, double *X, const BLASINT incX); + +void BLASNAME(cblas_cgemv)(const enum CBLAS_ORDER order, + const enum CBLAS_TRANSPOSE TransA, const BLASINT M, const BLASINT N, + const void *alpha, const void *A, const BLASINT lda, + const void *X, const BLASINT incX, const void *beta, + void *Y, const BLASINT incY); +void BLASNAME(cblas_cgbmv)(const enum CBLAS_ORDER order, + const enum CBLAS_TRANSPOSE TransA, const BLASINT M, const BLASINT N, + const BLASINT KL, const BLASINT KU, const void *alpha, + const void *A, const BLASINT lda, const void *X, + const BLASINT incX, const void *beta, void *Y, const BLASINT incY); +void BLASNAME(cblas_ctrmv)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const BLASINT N, const void *A, const BLASINT lda, + void *X, const BLASINT incX); +void BLASNAME(cblas_ctbmv)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const BLASINT N, const BLASINT K, const void *A, const BLASINT lda, + void *X, const BLASINT incX); +void BLASNAME(cblas_ctpmv)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const BLASINT N, const void *Ap, void *X, const BLASINT incX); +void BLASNAME(cblas_ctrsv)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const BLASINT N, const void *A, const BLASINT lda, void *X, + const BLASINT incX); +void BLASNAME(cblas_ctbsv)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const BLASINT N, const BLASINT K, const void *A, const BLASINT lda, + void *X, const BLASINT incX); +void BLASNAME(cblas_ctpsv)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const BLASINT N, const void *Ap, void *X, const BLASINT incX); + +void BLASNAME(cblas_zgemv)(const enum CBLAS_ORDER order, + const enum CBLAS_TRANSPOSE TransA, const BLASINT M, const BLASINT N, + const void *alpha, const void *A, const BLASINT lda, + const void *X, const BLASINT incX, const void *beta, + void *Y, const BLASINT incY); +void BLASNAME(cblas_zgbmv)(const enum CBLAS_ORDER order, + const enum CBLAS_TRANSPOSE TransA, const BLASINT M, const BLASINT N, + const BLASINT KL, const BLASINT KU, const void *alpha, + const void *A, const BLASINT lda, const void *X, + const BLASINT incX, const void *beta, void *Y, const BLASINT incY); +void BLASNAME(cblas_ztrmv)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const BLASINT N, const void *A, const BLASINT lda, + void *X, const BLASINT incX); +void BLASNAME(cblas_ztbmv)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const BLASINT N, const BLASINT K, const void *A, const BLASINT lda, + void *X, const BLASINT incX); +void BLASNAME(cblas_ztpmv)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const BLASINT N, const void *Ap, void *X, const BLASINT incX); +void BLASNAME(cblas_ztrsv)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const BLASINT N, const void *A, const BLASINT lda, void *X, + const BLASINT incX); +void BLASNAME(cblas_ztbsv)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const BLASINT N, const BLASINT K, const void *A, const BLASINT lda, + void *X, const BLASINT incX); +void BLASNAME(cblas_ztpsv)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, + const BLASINT N, const void *Ap, void *X, const BLASINT incX); + + +/* + * Routines with S and D prefixes only + */ +void BLASNAME(cblas_ssymv)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const BLASINT N, const float alpha, const float *A, + const BLASINT lda, const float *X, const BLASINT incX, + const float beta, float *Y, const BLASINT incY); +void BLASNAME(cblas_ssbmv)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const BLASINT N, const BLASINT K, const float alpha, const float *A, + const BLASINT lda, const float *X, const BLASINT incX, + const float beta, float *Y, const BLASINT incY); +void BLASNAME(cblas_sspmv)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const BLASINT N, const float alpha, const float *Ap, + const float *X, const BLASINT incX, + const float beta, float *Y, const BLASINT incY); +void BLASNAME(cblas_sger)(const enum CBLAS_ORDER order, const BLASINT M, const BLASINT N, + const float alpha, const float *X, const BLASINT incX, + const float *Y, const BLASINT incY, float *A, const BLASINT lda); +void BLASNAME(cblas_ssyr)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const BLASINT N, const float alpha, const float *X, + const BLASINT incX, float *A, const BLASINT lda); +void BLASNAME(cblas_sspr)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const BLASINT N, const float alpha, const float *X, + const BLASINT incX, float *Ap); +void BLASNAME(cblas_ssyr2)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const BLASINT N, const float alpha, const float *X, + const BLASINT incX, const float *Y, const BLASINT incY, float *A, + const BLASINT lda); +void BLASNAME(cblas_sspr2)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const BLASINT N, const float alpha, const float *X, + const BLASINT incX, const float *Y, const BLASINT incY, float *A); + +void BLASNAME(cblas_dsymv)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const BLASINT N, const double alpha, const double *A, + const BLASINT lda, const double *X, const BLASINT incX, + const double beta, double *Y, const BLASINT incY); +void BLASNAME(cblas_dsbmv)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const BLASINT N, const BLASINT K, const double alpha, const double *A, + const BLASINT lda, const double *X, const BLASINT incX, + const double beta, double *Y, const BLASINT incY); +void BLASNAME(cblas_dspmv)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const BLASINT N, const double alpha, const double *Ap, + const double *X, const BLASINT incX, + const double beta, double *Y, const BLASINT incY); +void BLASNAME(cblas_dger)(const enum CBLAS_ORDER order, const BLASINT M, const BLASINT N, + const double alpha, const double *X, const BLASINT incX, + const double *Y, const BLASINT incY, double *A, const BLASINT lda); +void BLASNAME(cblas_dsyr)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const BLASINT N, const double alpha, const double *X, + const BLASINT incX, double *A, const BLASINT lda); +void BLASNAME(cblas_dspr)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const BLASINT N, const double alpha, const double *X, + const BLASINT incX, double *Ap); +void BLASNAME(cblas_dsyr2)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const BLASINT N, const double alpha, const double *X, + const BLASINT incX, const double *Y, const BLASINT incY, double *A, + const BLASINT lda); +void BLASNAME(cblas_dspr2)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const BLASINT N, const double alpha, const double *X, + const BLASINT incX, const double *Y, const BLASINT incY, double *A); + + +/* + * Routines with C and Z prefixes only + */ +void BLASNAME(cblas_chemv)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const BLASINT N, const void *alpha, const void *A, + const BLASINT lda, const void *X, const BLASINT incX, + const void *beta, void *Y, const BLASINT incY); +void BLASNAME(cblas_chbmv)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const BLASINT N, const BLASINT K, const void *alpha, const void *A, + const BLASINT lda, const void *X, const BLASINT incX, + const void *beta, void *Y, const BLASINT incY); +void BLASNAME(cblas_chpmv)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const BLASINT N, const void *alpha, const void *Ap, + const void *X, const BLASINT incX, + const void *beta, void *Y, const BLASINT incY); +void BLASNAME(cblas_cgeru)(const enum CBLAS_ORDER order, const BLASINT M, const BLASINT N, + const void *alpha, const void *X, const BLASINT incX, + const void *Y, const BLASINT incY, void *A, const BLASINT lda); +void BLASNAME(cblas_cgerc)(const enum CBLAS_ORDER order, const BLASINT M, const BLASINT N, + const void *alpha, const void *X, const BLASINT incX, + const void *Y, const BLASINT incY, void *A, const BLASINT lda); +void BLASNAME(cblas_cher)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const BLASINT N, const float alpha, const void *X, const BLASINT incX, + void *A, const BLASINT lda); +void BLASNAME(cblas_chpr)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const BLASINT N, const float alpha, const void *X, + const BLASINT incX, void *A); +void BLASNAME(cblas_cher2)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, const BLASINT N, + const void *alpha, const void *X, const BLASINT incX, + const void *Y, const BLASINT incY, void *A, const BLASINT lda); +void BLASNAME(cblas_chpr2)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, const BLASINT N, + const void *alpha, const void *X, const BLASINT incX, + const void *Y, const BLASINT incY, void *Ap); + +void BLASNAME(cblas_zhemv)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const BLASINT N, const void *alpha, const void *A, + const BLASINT lda, const void *X, const BLASINT incX, + const void *beta, void *Y, const BLASINT incY); +void BLASNAME(cblas_zhbmv)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const BLASINT N, const BLASINT K, const void *alpha, const void *A, + const BLASINT lda, const void *X, const BLASINT incX, + const void *beta, void *Y, const BLASINT incY); +void BLASNAME(cblas_zhpmv)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const BLASINT N, const void *alpha, const void *Ap, + const void *X, const BLASINT incX, + const void *beta, void *Y, const BLASINT incY); +void BLASNAME(cblas_zgeru)(const enum CBLAS_ORDER order, const BLASINT M, const BLASINT N, + const void *alpha, const void *X, const BLASINT incX, + const void *Y, const BLASINT incY, void *A, const BLASINT lda); +void BLASNAME(cblas_zgerc)(const enum CBLAS_ORDER order, const BLASINT M, const BLASINT N, + const void *alpha, const void *X, const BLASINT incX, + const void *Y, const BLASINT incY, void *A, const BLASINT lda); +void BLASNAME(cblas_zher)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const BLASINT N, const double alpha, const void *X, const BLASINT incX, + void *A, const BLASINT lda); +void BLASNAME(cblas_zhpr)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, + const BLASINT N, const double alpha, const void *X, + const BLASINT incX, void *A); +void BLASNAME(cblas_zher2)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, const BLASINT N, + const void *alpha, const void *X, const BLASINT incX, + const void *Y, const BLASINT incY, void *A, const BLASINT lda); +void BLASNAME(cblas_zhpr2)(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, const BLASINT N, + const void *alpha, const void *X, const BLASINT incX, + const void *Y, const BLASINT incY, void *Ap); + +/* + * =========================================================================== + * Prototypes for level 3 BLAS + * =========================================================================== + */ + +/* + * Routines with standard 4 prefixes (S, D, C, Z) + */ +void BLASNAME(cblas_sgemm)(const enum CBLAS_ORDER Order, const enum CBLAS_TRANSPOSE TransA, + const enum CBLAS_TRANSPOSE TransB, const BLASINT M, const BLASINT N, + const BLASINT K, const float alpha, const float *A, + const BLASINT lda, const float *B, const BLASINT ldb, + const float beta, float *C, const BLASINT ldc); +void BLASNAME(cblas_ssymm)(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, + const enum CBLAS_UPLO Uplo, const BLASINT M, const BLASINT N, + const float alpha, const float *A, const BLASINT lda, + const float *B, const BLASINT ldb, const float beta, + float *C, const BLASINT ldc); +void BLASNAME(cblas_ssyrk)(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE Trans, const BLASINT N, const BLASINT K, + const float alpha, const float *A, const BLASINT lda, + const float beta, float *C, const BLASINT ldc); +void BLASNAME(cblas_ssyr2k)(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE Trans, const BLASINT N, const BLASINT K, + const float alpha, const float *A, const BLASINT lda, + const float *B, const BLASINT ldb, const float beta, + float *C, const BLASINT ldc); +void BLASNAME(cblas_strmm)(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, + const enum CBLAS_UPLO Uplo, const enum CBLAS_TRANSPOSE TransA, + const enum CBLAS_DIAG Diag, const BLASINT M, const BLASINT N, + const float alpha, const float *A, const BLASINT lda, + float *B, const BLASINT ldb); +void BLASNAME(cblas_strsm)(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, + const enum CBLAS_UPLO Uplo, const enum CBLAS_TRANSPOSE TransA, + const enum CBLAS_DIAG Diag, const BLASINT M, const BLASINT N, + const float alpha, const float *A, const BLASINT lda, + float *B, const BLASINT ldb); + +void BLASNAME(cblas_dgemm)(const enum CBLAS_ORDER Order, const enum CBLAS_TRANSPOSE TransA, + const enum CBLAS_TRANSPOSE TransB, const BLASINT M, const BLASINT N, + const BLASINT K, const double alpha, const double *A, + const BLASINT lda, const double *B, const BLASINT ldb, + const double beta, double *C, const BLASINT ldc); +void BLASNAME(cblas_dsymm)(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, + const enum CBLAS_UPLO Uplo, const BLASINT M, const BLASINT N, + const double alpha, const double *A, const BLASINT lda, + const double *B, const BLASINT ldb, const double beta, + double *C, const BLASINT ldc); +void BLASNAME(cblas_dsyrk)(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE Trans, const BLASINT N, const BLASINT K, + const double alpha, const double *A, const BLASINT lda, + const double beta, double *C, const BLASINT ldc); +void BLASNAME(cblas_dsyr2k)(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE Trans, const BLASINT N, const BLASINT K, + const double alpha, const double *A, const BLASINT lda, + const double *B, const BLASINT ldb, const double beta, + double *C, const BLASINT ldc); +void BLASNAME(cblas_dtrmm)(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, + const enum CBLAS_UPLO Uplo, const enum CBLAS_TRANSPOSE TransA, + const enum CBLAS_DIAG Diag, const BLASINT M, const BLASINT N, + const double alpha, const double *A, const BLASINT lda, + double *B, const BLASINT ldb); +void BLASNAME(cblas_dtrsm)(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, + const enum CBLAS_UPLO Uplo, const enum CBLAS_TRANSPOSE TransA, + const enum CBLAS_DIAG Diag, const BLASINT M, const BLASINT N, + const double alpha, const double *A, const BLASINT lda, + double *B, const BLASINT ldb); + +void BLASNAME(cblas_cgemm)(const enum CBLAS_ORDER Order, const enum CBLAS_TRANSPOSE TransA, + const enum CBLAS_TRANSPOSE TransB, const BLASINT M, const BLASINT N, + const BLASINT K, const void *alpha, const void *A, + const BLASINT lda, const void *B, const BLASINT ldb, + const void *beta, void *C, const BLASINT ldc); +void BLASNAME(cblas_csymm)(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, + const enum CBLAS_UPLO Uplo, const BLASINT M, const BLASINT N, + const void *alpha, const void *A, const BLASINT lda, + const void *B, const BLASINT ldb, const void *beta, + void *C, const BLASINT ldc); +void BLASNAME(cblas_csyrk)(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE Trans, const BLASINT N, const BLASINT K, + const void *alpha, const void *A, const BLASINT lda, + const void *beta, void *C, const BLASINT ldc); +void BLASNAME(cblas_csyr2k)(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE Trans, const BLASINT N, const BLASINT K, + const void *alpha, const void *A, const BLASINT lda, + const void *B, const BLASINT ldb, const void *beta, + void *C, const BLASINT ldc); +void BLASNAME(cblas_ctrmm)(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, + const enum CBLAS_UPLO Uplo, const enum CBLAS_TRANSPOSE TransA, + const enum CBLAS_DIAG Diag, const BLASINT M, const BLASINT N, + const void *alpha, const void *A, const BLASINT lda, + void *B, const BLASINT ldb); +void BLASNAME(cblas_ctrsm)(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, + const enum CBLAS_UPLO Uplo, const enum CBLAS_TRANSPOSE TransA, + const enum CBLAS_DIAG Diag, const BLASINT M, const BLASINT N, + const void *alpha, const void *A, const BLASINT lda, + void *B, const BLASINT ldb); + +void BLASNAME(cblas_zgemm)(const enum CBLAS_ORDER Order, const enum CBLAS_TRANSPOSE TransA, + const enum CBLAS_TRANSPOSE TransB, const BLASINT M, const BLASINT N, + const BLASINT K, const void *alpha, const void *A, + const BLASINT lda, const void *B, const BLASINT ldb, + const void *beta, void *C, const BLASINT ldc); +void BLASNAME(cblas_zsymm)(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, + const enum CBLAS_UPLO Uplo, const BLASINT M, const BLASINT N, + const void *alpha, const void *A, const BLASINT lda, + const void *B, const BLASINT ldb, const void *beta, + void *C, const BLASINT ldc); +void BLASNAME(cblas_zsyrk)(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE Trans, const BLASINT N, const BLASINT K, + const void *alpha, const void *A, const BLASINT lda, + const void *beta, void *C, const BLASINT ldc); +void BLASNAME(cblas_zsyr2k)(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE Trans, const BLASINT N, const BLASINT K, + const void *alpha, const void *A, const BLASINT lda, + const void *B, const BLASINT ldb, const void *beta, + void *C, const BLASINT ldc); +void BLASNAME(cblas_ztrmm)(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, + const enum CBLAS_UPLO Uplo, const enum CBLAS_TRANSPOSE TransA, + const enum CBLAS_DIAG Diag, const BLASINT M, const BLASINT N, + const void *alpha, const void *A, const BLASINT lda, + void *B, const BLASINT ldb); +void BLASNAME(cblas_ztrsm)(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, + const enum CBLAS_UPLO Uplo, const enum CBLAS_TRANSPOSE TransA, + const enum CBLAS_DIAG Diag, const BLASINT M, const BLASINT N, + const void *alpha, const void *A, const BLASINT lda, + void *B, const BLASINT ldb); + + +/* + * Routines with prefixes C and Z only + */ +void BLASNAME(cblas_chemm)(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, + const enum CBLAS_UPLO Uplo, const BLASINT M, const BLASINT N, + const void *alpha, const void *A, const BLASINT lda, + const void *B, const BLASINT ldb, const void *beta, + void *C, const BLASINT ldc); +void BLASNAME(cblas_cherk)(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE Trans, const BLASINT N, const BLASINT K, + const float alpha, const void *A, const BLASINT lda, + const float beta, void *C, const BLASINT ldc); +void BLASNAME(cblas_cher2k)(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE Trans, const BLASINT N, const BLASINT K, + const void *alpha, const void *A, const BLASINT lda, + const void *B, const BLASINT ldb, const float beta, + void *C, const BLASINT ldc); + +void BLASNAME(cblas_zhemm)(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, + const enum CBLAS_UPLO Uplo, const BLASINT M, const BLASINT N, + const void *alpha, const void *A, const BLASINT lda, + const void *B, const BLASINT ldb, const void *beta, + void *C, const BLASINT ldc); +void BLASNAME(cblas_zherk)(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE Trans, const BLASINT N, const BLASINT K, + const double alpha, const void *A, const BLASINT lda, + const double beta, void *C, const BLASINT ldc); +void BLASNAME(cblas_zher2k)(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo, + const enum CBLAS_TRANSPOSE Trans, const BLASINT N, const BLASINT K, + const void *alpha, const void *A, const BLASINT lda, + const void *B, const BLASINT ldb, const double beta, + void *C, const BLASINT ldc); + +void BLASNAME(cblas_xerbla)(BLASINT p, const char *rout, const char *form, ...); + +#endif /* NUMPY_CORE_SRC_COMMON_NPY_CBLAS_BASE_H_ */ diff --git a/numpy/core/src/private/npy_config.h b/numpy/core/src/common/npy_config.h similarity index 55% rename from numpy/core/src/private/npy_config.h rename to numpy/core/src/common/npy_config.h index 107b3cb5bf21..fd0f1855c8d3 100644 --- a/numpy/core/src/private/npy_config.h +++ b/numpy/core/src/common/npy_config.h @@ -1,27 +1,14 @@ -#ifndef _NPY_NPY_CONFIG_H_ -#define _NPY_NPY_CONFIG_H_ +#ifndef NUMPY_CORE_SRC_COMMON_NPY_CONFIG_H_ +#define NUMPY_CORE_SRC_COMMON_NPY_CONFIG_H_ #include "config.h" +#include "npy_cpu_features.h" +#include "npy_cpu_dispatch.h" #include "numpy/numpyconfig.h" #include "numpy/npy_cpu.h" #include "numpy/npy_os.h" -/* - * largest alignment the copy loops might require - * required as string, void and complex types might get copied using larger - * instructions than required to operate on them. E.g. complex float is copied - * in 8 byte moves but arithmetic on them only loads in 4 byte moves. - * the sparc platform may need that alignment for long doubles. - * amd64 is not harmed much by the bloat as the system provides 16 byte - * alignment by default. - */ -#if (defined NPY_CPU_X86 || defined _WIN32) -#define NPY_MAX_COPY_ALIGNMENT 8 -#else -#define NPY_MAX_COPY_ALIGNMENT 16 -#endif - -/* blacklist */ +/* blocklist */ /* Disable broken Sun Workshop Pro math functions */ #ifdef __SUNPRO_C @@ -32,6 +19,15 @@ #endif +/* Disable broken functions on z/OS */ +#if defined (__MVS__) + +#undef HAVE_POWF +#undef HAVE_EXPF +#undef HAVE___THREAD + +#endif + /* Disable broken MS math functions */ #if (defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__MINGW32_VERSION) @@ -45,7 +41,7 @@ #endif -#if defined(_MSC_VER) && (_MSC_VER == 1900) +#if defined(_MSC_VER) && (_MSC_VER >= 1900) #undef HAVE_CASIN #undef HAVE_CASINF @@ -59,6 +55,18 @@ #undef HAVE_CATANH #undef HAVE_CATANHF #undef HAVE_CATANHL +#undef HAVE_CSQRT +#undef HAVE_CSQRTF +#undef HAVE_CSQRTL +#undef HAVE_CLOG +#undef HAVE_CLOGF +#undef HAVE_CLOGL +#undef HAVE_CACOS +#undef HAVE_CACOSF +#undef HAVE_CACOSL +#undef HAVE_CACOSH +#undef HAVE_CACOSHF +#undef HAVE_CACOSHL #endif @@ -88,6 +96,51 @@ #undef HAVE_POWL #endif +#ifdef __CYGWIN__ +/* Loss of precision */ +#undef HAVE_CASINHL +#undef HAVE_CASINH +#undef HAVE_CASINHF + +/* Loss of precision */ +#undef HAVE_CATANHL +#undef HAVE_CATANH +#undef HAVE_CATANHF + +/* Loss of precision and branch cuts */ +#undef HAVE_CATANL +#undef HAVE_CATAN +#undef HAVE_CATANF + +/* Branch cuts */ +#undef HAVE_CACOSHF +#undef HAVE_CACOSH + +/* Branch cuts */ +#undef HAVE_CSQRTF +#undef HAVE_CSQRT + +/* Branch cuts and loss of precision */ +#undef HAVE_CASINF +#undef HAVE_CASIN +#undef HAVE_CASINL + +/* Branch cuts */ +#undef HAVE_CACOSF +#undef HAVE_CACOS + +/* log2(exp2(i)) off by a few eps */ +#undef HAVE_LOG2 + +/* np.power(..., dtype=np.complex256) doesn't report overflow */ +#undef HAVE_CPOWL +#undef HAVE_CEXPL + +/* Builtin abs reports overflow */ +#undef HAVE_CABSL +#undef HAVE_HYPOTL +#endif + /* Disable broken gnu trig functions */ #if defined(HAVE_FEATURES_H) #include <features.h> @@ -114,9 +167,9 @@ #undef HAVE_CACOSHF #undef HAVE_CACOSHL -#endif /* __GLIBC_PREREQ(2, 18) */ -#endif /* defined(__GLIBC_PREREQ) */ +#endif /* __GLIBC_PREREQ(2, 18) */ +#endif /* defined(__GLIBC_PREREQ) */ -#endif /* defined(HAVE_FEATURES_H) */ +#endif /* defined(HAVE_FEATURES_H) */ -#endif +#endif /* NUMPY_CORE_SRC_COMMON_NPY_CONFIG_H_ */ diff --git a/numpy/core/src/common/npy_cpu_dispatch.h b/numpy/core/src/common/npy_cpu_dispatch.h new file mode 100644 index 000000000000..e814cd425e83 --- /dev/null +++ b/numpy/core/src/common/npy_cpu_dispatch.h @@ -0,0 +1,265 @@ +#ifndef NUMPY_CORE_SRC_COMMON_NPY_CPU_DISPATCH_H_ +#define NUMPY_CORE_SRC_COMMON_NPY_CPU_DISPATCH_H_ +/** + * This file is part of the NumPy CPU dispatcher. Please have a look at doc/reference/simd-optimizations.html + * To get a better understanding of the mechanism behind it. + */ +#include "npy_cpu_features.h" // NPY_CPU_HAVE +#include "numpy/utils.h" // NPY_EXPAND, NPY_CAT +/** + * Including the main configuration header 'npy_cpu_dispatch_config.h'. + * + * This header is generated by the distutils module 'ccompiler_opt', + * and contains all the #definitions and headers for platform-specific instruction-sets + * that had been configured through command arguments '--cpu-baseline' and '--cpu-dispatch'. + * + * It also contains extra C #definitions and macros that are used for implementing + * NumPy module's attributes `__cpu_baseline__` and `__cpu_dispaٍtch__`. + */ +/** + * Note: Always guard the generated headers within 'NPY_DISABLE_OPTIMIZATION', + * due the nature of command argument '--disable-optimization', + * which is explicitly disabling the module ccompiler_opt. + */ +#ifndef NPY_DISABLE_OPTIMIZATION + #if defined(__powerpc64__) && !defined(__cplusplus) && defined(bool) + /** + * "altivec.h" header contains the definitions(bool, vector, pixel), + * usually in c++ we undefine them after including the header. + * It's better anyway to take them off and use built-in types(__vector, __pixel, __bool) instead, + * since c99 supports bool variables which may lead to ambiguous errors. + */ + // backup 'bool' before including '_cpu_dispatch.h', since it may not defined as a compiler token. + #define NPY__DISPATCH_DEFBOOL + typedef bool npy__dispatch_bkbool; + #endif + #include "npy_cpu_dispatch_config.h" + #ifdef NPY_HAVE_VSX + #undef bool + #undef vector + #undef pixel + #ifdef NPY__DISPATCH_DEFBOOL + #define bool npy__dispatch_bkbool + #endif + #endif +#endif // !NPY_DISABLE_OPTIMIZATION +/** + * Macro NPY_CPU_DISPATCH_CURFX(NAME) + * + * Returns @NAME suffixed with "_" + "the current target" during compiling + * the wrapped sources that generated from the dispatch-able sources according + * to the provided configuration statements. + * + * It also returns @NAME as-is without any suffix when it comes to the baseline or + * in case if the optimization is disabled. + * + * The idea behind this Macro is to allow exporting certain symbols and to + * avoid linking duplications due to the nature of the dispatch-able sources. + * + * Example: + * @targets baseline avx avx512_skx vsx3 asimdhp // configuration statements + * + * void NPY_CPU_DISPATCH_CURFX(dispatch_me)(const int *src, int *dst) + * { + * // the kernel + * } + * + * By assuming the required optimizations are enabled via '--cpu-dspatch' and + * the compiler supported them too, then the generated symbols will be named as follows: + * + * - x86: + * dispatch_me(const int*, int*) // baseline + * dispatch_me_AVX(const int*, int*) + * dispatch_me_AVX512_SKX(const int*, int*) + * + * - ppc64: + * dispatch_me(const int*, int*) + * dispatch_me_VSX3(const int*, int*) + * + * - ARM: + * dispatch_me(const int*, int*) + * dispatch_me_ASIMHP(const int*, int*) + * + * - unsupported arch or when optimization is disabled: + * dispatch_me(const int*, int*) + * + * For forward declarations, see 'NPY_CPU_DISPATCH_DECLARE'. + */ +#ifdef NPY__CPU_TARGET_CURRENT + // 'NPY__CPU_TARGET_CURRENT': only defined by the dispatch-able sources + #define NPY_CPU_DISPATCH_CURFX(NAME) NPY_CAT(NPY_CAT(NAME, _), NPY__CPU_TARGET_CURRENT) +#else + #define NPY_CPU_DISPATCH_CURFX(NAME) NPY_EXPAND(NAME) +#endif +/** + * Defining the default behavior for the configurable macros of dispatch-able sources, + * 'NPY__CPU_DISPATCH_CALL(...)' and 'NPY__CPU_DISPATCH_BASELINE_CALL(...)' + * + * These macros are defined inside the generated config files that been derived from + * the configuration statements of the dispatch-able sources. + * + * The generated config file takes the same name of the dispatch-able source with replacing + * the extension to '.h' instead of '.c', and it should be treated as a header template. + * + * For more clarification, please have a look at doc/reference/simd-optimizations.html. + */ +#ifndef NPY_DISABLE_OPTIMIZATION + #define NPY__CPU_DISPATCH_BASELINE_CALL(CB, ...) \ + &&"Expected config header of the dispatch-able source"; + #define NPY__CPU_DISPATCH_CALL(CHK, CB, ...) \ + &&"Expected config header of the dispatch-able source"; +#else + /** + * We assume by default that all configuration statements contains 'baseline' option however, + * if the dispatch-able source doesn't require it, then the dispatch-able source and following macros + * need to be guard it with '#ifndef NPY_DISABLE_OPTIMIZATION' + */ + #define NPY__CPU_DISPATCH_BASELINE_CALL(CB, ...) \ + NPY_EXPAND(CB(__VA_ARGS__)) + #define NPY__CPU_DISPATCH_CALL(CHK, CB, ...) +#endif // !NPY_DISABLE_OPTIMIZATION +/** + * Macro NPY_CPU_DISPATCH_DECLARE(LEFT, ...) is used to provide forward + * declarations for the exported variables and functions that defined inside + * the dispatch-able sources. + * + * The first argument should ends with the exported function or variable name, + * while the Macro pasting the extra arguments. + * + * Examples: + * #ifndef NPY_DISABLE_OPTIMIZATION + * #include "dispatchable_source_name.dispatch.h" + * #endif + * + * NPY_CPU_DISPATCH_DECLARE(void dispatch_me, (const int*, int*)) + * NPY_CPU_DISPATCH_DECLARE(extern cb_type callback_tab, [TAB_SIZE]) + * + * By assuming the provided config header derived from a dispatch-able source, + * that configured with "@targets baseline sse41 vsx3 asimdhp", + * they supported by the compiler and enabled via '--cpu-dspatch', + * then the prototype declrations at the above example will equivalent to the follows: + * + * - x86: + * void dispatch_me(const int*, int*); // baseline + * void dispatch_me_SSE41(const int*, int*); + * + * extern cb_type callback_tab[TAB_SIZE]; + * extern cb_type callback_tab_SSE41[TAB_SIZE]; + * + * - ppc64: + * void dispatch_me(const int*, int*); + * void dispatch_me_VSX3(const int*, int*); + * + * extern cb_type callback_tab[TAB_SIZE]; + * extern cb_type callback_tab_VSX3[TAB_SIZE]; + * + * - ARM: + * void dispatch_me(const int*, int*); + * void dispatch_me_ASIMDHP(const int*, int*); + * + * extern cb_type callback_tab[TAB_SIZE]; + * extern cb_type callback_tab_ASIMDHP[TAB_SIZE]; + * + * - unsupported arch or when optimization is disabled: + * void dispatch_me(const int*, int*); + * extern cb_type callback_tab[TAB_SIZE]; + * + * For runtime dispatching, see 'NPY_CPU_DISPATCH_CALL' + */ +#define NPY_CPU_DISPATCH_DECLARE(...) \ + NPY__CPU_DISPATCH_CALL(NPY_CPU_DISPATCH_DECLARE_CHK_, NPY_CPU_DISPATCH_DECLARE_CB_, __VA_ARGS__) \ + NPY__CPU_DISPATCH_BASELINE_CALL(NPY_CPU_DISPATCH_DECLARE_BASE_CB_, __VA_ARGS__) +// Preprocessor callbacks +#define NPY_CPU_DISPATCH_DECLARE_CB_(DUMMY, TARGET_NAME, LEFT, ...) \ + NPY_CAT(NPY_CAT(LEFT, _), TARGET_NAME) __VA_ARGS__; +#define NPY_CPU_DISPATCH_DECLARE_BASE_CB_(LEFT, ...) \ + LEFT __VA_ARGS__; +// Dummy CPU runtime checking +#define NPY_CPU_DISPATCH_DECLARE_CHK_(FEATURE) +/** + * Macro NPY_CPU_DISPATCH_DECLARE_XB(LEFT, ...) + * + * Same as `NPY_CPU_DISPATCH_DECLARE` but exclude the baseline declaration even + * if it was provided within the configuration statements. + */ +#define NPY_CPU_DISPATCH_DECLARE_XB(...) \ + NPY__CPU_DISPATCH_CALL(NPY_CPU_DISPATCH_DECLARE_CHK_, NPY_CPU_DISPATCH_DECLARE_CB_, __VA_ARGS__) +/** + * Macro NPY_CPU_DISPATCH_CALL(LEFT, ...) is used for runtime dispatching + * of the exported functions and variables within the dispatch-able sources + * according to the highested interesed CPU features that supported by the + * running machine depending on the required optimizations. + * + * The first argument should ends with the exported function or variable name, + * while the Macro pasting the extra arguments. + * + * Example: + * Assume we have a dispatch-able source exporting the following function: + * + * @targets baseline avx2 avx512_skx // configuration statements + * + * void NPY_CPU_DISPATCH_CURFX(dispatch_me)(const int *src, int *dst) + * { + * // the kernel + * } + * + * In order to call or to assign the pointer of it from outside the dispatch-able source, + * you have to use this Macro as follows: + * + * // bring the generated config header of the dispatch-able source + * #ifndef NPY_DISABLE_OPTIMIZATION + * #include "dispatchable_source_name.dispatch.h" + * #endif + * // forward declaration + * NPY_CPU_DISPATCH_DECLARE(dispatch_me, (const int *src, int *dst)) + * + * typedef void(*func_type)(const int*, int*); + * func_type the_callee(const int *src, int *dst, func_type *cb) + * { + * // direct call + * NPY_CPU_DISPATCH_CALL(dispatch_me, (src, dst)); + * // assign the pointer + * *cb = NPY_CPU_DISPATCH_CALL(dispatch_me); + * // or + * NPY_CPU_DISPATCH_CALL(*cb = dispatch_me); + * // return the pointer + * return NPY_CPU_DISPATCH_CALL(dispatch_me); + * } + */ +#define NPY_CPU_DISPATCH_CALL(...) \ + NPY__CPU_DISPATCH_CALL(NPY_CPU_HAVE, NPY_CPU_DISPATCH_CALL_CB_, __VA_ARGS__) \ + NPY__CPU_DISPATCH_BASELINE_CALL(NPY_CPU_DISPATCH_CALL_BASE_CB_, __VA_ARGS__) +// Preprocessor callbacks +#define NPY_CPU_DISPATCH_CALL_CB_(TESTED_FEATURES, TARGET_NAME, LEFT, ...) \ + (TESTED_FEATURES) ? (NPY_CAT(NPY_CAT(LEFT, _), TARGET_NAME) __VA_ARGS__) : +#define NPY_CPU_DISPATCH_CALL_BASE_CB_(LEFT, ...) \ + (LEFT __VA_ARGS__) +/** + * Macro NPY_CPU_DISPATCH_CALL_XB(LEFT, ...) + * + * Same as `NPY_CPU_DISPATCH_DECLARE` but exclude the baseline declaration even + * if it was provided within the configuration statements. + * Returns void. + */ +#define NPY_CPU_DISPATCH_CALL_XB_CB_(TESTED_FEATURES, TARGET_NAME, LEFT, ...) \ + (TESTED_FEATURES) ? (void) (NPY_CAT(NPY_CAT(LEFT, _), TARGET_NAME) __VA_ARGS__) : +#define NPY_CPU_DISPATCH_CALL_XB(...) \ + NPY__CPU_DISPATCH_CALL(NPY_CPU_HAVE, NPY_CPU_DISPATCH_CALL_XB_CB_, __VA_ARGS__) \ + ((void) 0 /* discarded expression value */) +/** + * Macro NPY_CPU_DISPATCH_CALL_ALL(LEFT, ...) + * + * Same as `NPY_CPU_DISPATCH_CALL` but dispatching all the required optimizations for + * the exported functions and variables instead of highest interested one. + * Returns void. + */ +#define NPY_CPU_DISPATCH_CALL_ALL(...) \ + (NPY__CPU_DISPATCH_CALL(NPY_CPU_HAVE, NPY_CPU_DISPATCH_CALL_ALL_CB_, __VA_ARGS__) \ + NPY__CPU_DISPATCH_BASELINE_CALL(NPY_CPU_DISPATCH_CALL_ALL_BASE_CB_, __VA_ARGS__)) +// Preprocessor callbacks +#define NPY_CPU_DISPATCH_CALL_ALL_CB_(TESTED_FEATURES, TARGET_NAME, LEFT, ...) \ + ((TESTED_FEATURES) ? (NPY_CAT(NPY_CAT(LEFT, _), TARGET_NAME) __VA_ARGS__) : (void) 0), +#define NPY_CPU_DISPATCH_CALL_ALL_BASE_CB_(LEFT, ...) \ + ( LEFT __VA_ARGS__ ) + +#endif // NUMPY_CORE_SRC_COMMON_NPY_CPU_DISPATCH_H_ diff --git a/numpy/core/src/common/npy_cpu_features.c.src b/numpy/core/src/common/npy_cpu_features.c.src new file mode 100644 index 000000000000..a2383c45f61d --- /dev/null +++ b/numpy/core/src/common/npy_cpu_features.c.src @@ -0,0 +1,678 @@ +#include "npy_cpu_features.h" +#include "npy_cpu_dispatch.h" // To guarantee the CPU baseline definitions are in scope. +#include "numpy/npy_common.h" // for NPY_INLINE +#include "numpy/npy_cpu.h" // To guarantee the CPU definitions are in scope. + +/******************** Private Definitions *********************/ + +// Hold all CPU features boolean values +static unsigned char npy__cpu_have[NPY_CPU_FEATURE_MAX]; + +/******************** Private Declarations *********************/ + +// Almost detect all CPU features in runtime +static void +npy__cpu_init_features(void); +/* + * Disable CPU dispatched features at runtime if environment variable + * 'NPY_DISABLE_CPU_FEATURES' is defined. + * Multiple features can be present, and separated by space, comma, or tab. + * Raises an error if parsing fails or if the feature was not enabled +*/ +static int +npy__cpu_try_disable_env(void); + +/* Ensure the build's CPU baseline features are supported at runtime */ +static int +npy__cpu_validate_baseline(void); + +/******************** Public Definitions *********************/ + +NPY_VISIBILITY_HIDDEN int +npy_cpu_have(int feature_id) +{ + if (feature_id <= NPY_CPU_FEATURE_NONE || feature_id >= NPY_CPU_FEATURE_MAX) + return 0; + return npy__cpu_have[feature_id]; +} + +NPY_VISIBILITY_HIDDEN int +npy_cpu_init(void) +{ + npy__cpu_init_features(); + if (npy__cpu_validate_baseline() < 0) { + return -1; + } + if (npy__cpu_try_disable_env() < 0) { + return -1; + } + return 0; +} + +NPY_VISIBILITY_HIDDEN PyObject * +npy_cpu_features_dict(void) +{ + PyObject *dict = PyDict_New(); + if (dict) { + /**begin repeat + * #feature = MMX, SSE, SSE2, SSE3, SSSE3, SSE41, POPCNT, SSE42, + * AVX, F16C, XOP, FMA4, FMA3, AVX2, AVX512F, + * AVX512CD, AVX512ER, AVX512PF, AVX5124FMAPS, AVX5124VNNIW, + * AVX512VPOPCNTDQ, AVX512VL, AVX512BW, AVX512DQ, AVX512VNNI, + * AVX512IFMA, AVX512VBMI, AVX512VBMI2, AVX512BITALG, + * AVX512_KNL, AVX512_KNM, AVX512_SKX, AVX512_CLX, AVX512_CNL, AVX512_ICL, + * VSX, VSX2, VSX3, + * NEON, NEON_FP16, NEON_VFPV4, ASIMD, FPHP, ASIMDHP, ASIMDDP, ASIMDFHM# + */ + if (PyDict_SetItemString(dict, "@feature@", + npy__cpu_have[NPY_CPU_FEATURE_@feature@] ? Py_True : Py_False) < 0) { + Py_DECREF(dict); + return NULL; + } + /**end repeat**/ + } + return dict; +} + +#define NPY__CPU_PYLIST_APPEND_CB(FEATURE, LIST) \ + item = PyUnicode_FromString(NPY_TOSTRING(FEATURE)); \ + if (item == NULL) { \ + Py_DECREF(LIST); \ + return NULL; \ + } \ + PyList_SET_ITEM(LIST, index++, item); + +NPY_VISIBILITY_HIDDEN PyObject * +npy_cpu_baseline_list(void) +{ +#if !defined(NPY_DISABLE_OPTIMIZATION) && NPY_WITH_CPU_BASELINE_N > 0 + PyObject *list = PyList_New(NPY_WITH_CPU_BASELINE_N), *item; + int index = 0; + if (list != NULL) { + NPY_WITH_CPU_BASELINE_CALL(NPY__CPU_PYLIST_APPEND_CB, list) + } + return list; +#else + return PyList_New(0); +#endif +} + +NPY_VISIBILITY_HIDDEN PyObject * +npy_cpu_dispatch_list(void) +{ +#if !defined(NPY_DISABLE_OPTIMIZATION) && NPY_WITH_CPU_DISPATCH_N > 0 + PyObject *list = PyList_New(NPY_WITH_CPU_DISPATCH_N), *item; + int index = 0; + if (list != NULL) { + NPY_WITH_CPU_DISPATCH_CALL(NPY__CPU_PYLIST_APPEND_CB, list) + } + return list; +#else + return PyList_New(0); +#endif +} + +/******************** Private Definitions *********************/ +#define NPY__CPU_FEATURE_ID_CB(FEATURE, WITH_FEATURE) \ + if (strcmp(NPY_TOSTRING(FEATURE), WITH_FEATURE) == 0) \ + return NPY_CAT(NPY_CPU_FEATURE_, FEATURE); +/** + * Returns CPU feature's ID, if the 'feature' was part of baseline + * features that had been configured via --cpu-baseline + * otherwise it returns 0 +*/ +static NPY_INLINE int +npy__cpu_baseline_fid(const char *feature) +{ +#if !defined(NPY_DISABLE_OPTIMIZATION) && NPY_WITH_CPU_BASELINE_N > 0 + NPY_WITH_CPU_BASELINE_CALL(NPY__CPU_FEATURE_ID_CB, feature) +#endif + return 0; +} +/** + * Returns CPU feature's ID, if the 'feature' was part of dispatched + * features that had been configured via --cpu-dispatch + * otherwise it returns 0 +*/ +static NPY_INLINE int +npy__cpu_dispatch_fid(const char *feature) +{ +#if !defined(NPY_DISABLE_OPTIMIZATION) && NPY_WITH_CPU_DISPATCH_N > 0 + NPY_WITH_CPU_DISPATCH_CALL(NPY__CPU_FEATURE_ID_CB, feature) +#endif + return 0; +} + +static int +npy__cpu_validate_baseline(void) +{ +#if !defined(NPY_DISABLE_OPTIMIZATION) && NPY_WITH_CPU_BASELINE_N > 0 + char baseline_failure[sizeof(NPY_WITH_CPU_BASELINE) + 1]; + char *fptr = &baseline_failure[0]; + + #define NPY__CPU_VALIDATE_CB(FEATURE, DUMMY) \ + if (!npy__cpu_have[NPY_CAT(NPY_CPU_FEATURE_, FEATURE)]) { \ + const int size = sizeof(NPY_TOSTRING(FEATURE)); \ + memcpy(fptr, NPY_TOSTRING(FEATURE), size); \ + fptr[size] = ' '; fptr += size + 1; \ + } + NPY_WITH_CPU_BASELINE_CALL(NPY__CPU_VALIDATE_CB, DUMMY) // extra arg for msvc + *fptr = '\0'; + + if (baseline_failure[0] != '\0') { + *(fptr-1) = '\0'; // trim the last space + PyErr_Format(PyExc_RuntimeError, + "NumPy was built with baseline optimizations: \n" + "(" NPY_WITH_CPU_BASELINE ") but your machine doesn't support:\n(%s).", + baseline_failure + ); + return -1; + } +#endif + return 0; +} + +static int +npy__cpu_try_disable_env(void) +{ + char *disenv = getenv("NPY_DISABLE_CPU_FEATURES"); + if (disenv == NULL || disenv[0] == 0) { + return 0; + } + #define NPY__CPU_ENV_ERR_HEAD \ + "During parsing environment variable 'NPY_DISABLE_CPU_FEATURES':\n" + +#if !defined(NPY_DISABLE_OPTIMIZATION) && NPY_WITH_CPU_DISPATCH_N > 0 + #define NPY__MAX_VAR_LEN 1024 // More than enough for this era + size_t var_len = strlen(disenv) + 1; + if (var_len > NPY__MAX_VAR_LEN) { + PyErr_Format(PyExc_RuntimeError, + "Length of environment variable 'NPY_DISABLE_CPU_FEATURES' is %d, only %d accepted", + var_len, NPY__MAX_VAR_LEN - 1 + ); + return -1; + } + char disable_features[NPY__MAX_VAR_LEN]; + memcpy(disable_features, disenv, var_len); + + char nexist[NPY__MAX_VAR_LEN]; + char *nexist_cur = &nexist[0]; + + char notsupp[sizeof(NPY_WITH_CPU_DISPATCH) + 1]; + char *notsupp_cur = ¬supp[0]; + + //comma and space including (htab, vtab, CR, LF, FF) + const char *delim = ", \t\v\r\n\f"; + char *feature = strtok(disable_features, delim); + while (feature) { + if (npy__cpu_baseline_fid(feature) > 0) { + PyErr_Format(PyExc_RuntimeError, + NPY__CPU_ENV_ERR_HEAD + "You cannot disable CPU feature '%s', since it is part of " + "the baseline optimizations:\n" + "(" NPY_WITH_CPU_BASELINE ").", + feature + ); + return -1; + } + // check if the feature is part of dispatched features + int feature_id = npy__cpu_dispatch_fid(feature); + if (feature_id == 0) { + int flen = strlen(feature); + memcpy(nexist_cur, feature, flen); + nexist_cur[flen] = ' '; nexist_cur += flen + 1; + goto next; + } + // check if the feature supported by the running machine + if (!npy__cpu_have[feature_id]) { + int flen = strlen(feature); + memcpy(notsupp_cur, feature, flen); + notsupp_cur[flen] = ' '; notsupp_cur += flen + 1; + goto next; + } + // Finally we can disable it + npy__cpu_have[feature_id] = 0; + next: + feature = strtok(NULL, delim); + } + + *nexist_cur = '\0'; + if (nexist[0] != '\0') { + *(nexist_cur-1) = '\0'; // trim the last space + if (PyErr_WarnFormat(PyExc_RuntimeWarning, 1, + NPY__CPU_ENV_ERR_HEAD + "You cannot disable CPU features (%s), since " + "they are not part of the dispatched optimizations\n" + "(" NPY_WITH_CPU_DISPATCH ").", + nexist + ) < 0) { + return -1; + } + } + + *notsupp_cur = '\0'; + if (notsupp[0] != '\0') { + *(notsupp_cur-1) = '\0'; // trim the last space + if (PyErr_WarnFormat(PyExc_RuntimeWarning, 1, + NPY__CPU_ENV_ERR_HEAD + "You cannot disable CPU features (%s), since " + "they are not supported by your machine.", + notsupp + ) < 0) { + return -1; + } + } +#else + if (PyErr_WarnFormat(PyExc_RuntimeWarning, 1, + NPY__CPU_ENV_ERR_HEAD + "You cannot use environment variable 'NPY_DISABLE_CPU_FEATURES', since " + #ifdef NPY_DISABLE_OPTIMIZATION + "the NumPy library was compiled with optimization disabled." + #else + "the NumPy library was compiled without any dispatched optimizations." + #endif + ) < 0) { + return -1; + } +#endif + return 0; +} + +/**************************************************************** + * This section is reserved to defining @npy__cpu_init_features + * for each CPU architecture, please try to keep it clean. Ty + ****************************************************************/ + +/***************** X86 ******************/ + +#if defined(NPY_CPU_AMD64) || defined(NPY_CPU_X86) + +#ifdef _MSC_VER + #include <intrin.h> +#elif defined(__INTEL_COMPILER) + #include <immintrin.h> +#endif + +static int +npy__cpu_getxcr0(void) +{ +#if defined(_MSC_VER) || defined (__INTEL_COMPILER) + return _xgetbv(0); +#elif defined(__GNUC__) || defined(__clang__) + /* named form of xgetbv not supported on OSX, so must use byte form, see: + * https://github.com/asmjit/asmjit/issues/78 + */ + unsigned int eax, edx; + __asm(".byte 0x0F, 0x01, 0xd0" : "=a"(eax), "=d"(edx) : "c"(0)); + return eax; +#else + return 0; +#endif +} + +static void +npy__cpu_cpuid(int reg[4], int func_id) +{ +#if defined(_MSC_VER) + __cpuidex(reg, func_id, 0); +#elif defined(__INTEL_COMPILER) + __cpuid(reg, func_id); +#elif defined(__GNUC__) || defined(__clang__) + #if defined(NPY_CPU_X86) && defined(__PIC__) + // %ebx may be the PIC register + __asm__("xchg{l}\t{%%}ebx, %1\n\t" + "cpuid\n\t" + "xchg{l}\t{%%}ebx, %1\n\t" + : "=a" (reg[0]), "=r" (reg[1]), "=c" (reg[2]), + "=d" (reg[3]) + : "a" (func_id), "c" (0) + ); + #else + __asm__("cpuid\n\t" + : "=a" (reg[0]), "=b" (reg[1]), "=c" (reg[2]), + "=d" (reg[3]) + : "a" (func_id), "c" (0) + ); + #endif +#else + reg[0] = 0; +#endif +} + +static void +npy__cpu_init_features(void) +{ + memset(npy__cpu_have, 0, sizeof(npy__cpu_have[0]) * NPY_CPU_FEATURE_MAX); + + // validate platform support + int reg[] = {0, 0, 0, 0}; + npy__cpu_cpuid(reg, 0); + if (reg[0] == 0) { + npy__cpu_have[NPY_CPU_FEATURE_MMX] = 1; + npy__cpu_have[NPY_CPU_FEATURE_SSE] = 1; + npy__cpu_have[NPY_CPU_FEATURE_SSE2] = 1; + #ifdef NPY_CPU_AMD64 + npy__cpu_have[NPY_CPU_FEATURE_SSE3] = 1; + #endif + return; + } + + npy__cpu_cpuid(reg, 1); + npy__cpu_have[NPY_CPU_FEATURE_MMX] = (reg[3] & (1 << 23)) != 0; + npy__cpu_have[NPY_CPU_FEATURE_SSE] = (reg[3] & (1 << 25)) != 0; + npy__cpu_have[NPY_CPU_FEATURE_SSE2] = (reg[3] & (1 << 26)) != 0; + npy__cpu_have[NPY_CPU_FEATURE_SSE3] = (reg[2] & (1 << 0)) != 0; + npy__cpu_have[NPY_CPU_FEATURE_SSSE3] = (reg[2] & (1 << 9)) != 0; + npy__cpu_have[NPY_CPU_FEATURE_SSE41] = (reg[2] & (1 << 19)) != 0; + npy__cpu_have[NPY_CPU_FEATURE_POPCNT] = (reg[2] & (1 << 23)) != 0; + npy__cpu_have[NPY_CPU_FEATURE_SSE42] = (reg[2] & (1 << 20)) != 0; + npy__cpu_have[NPY_CPU_FEATURE_F16C] = (reg[2] & (1 << 29)) != 0; + + // check OSXSAVE + if ((reg[2] & (1 << 27)) == 0) + return; + // check AVX OS support + int xcr = npy__cpu_getxcr0(); + if ((xcr & 6) != 6) + return; + npy__cpu_have[NPY_CPU_FEATURE_AVX] = (reg[2] & (1 << 28)) != 0; + if (!npy__cpu_have[NPY_CPU_FEATURE_AVX]) + return; + npy__cpu_have[NPY_CPU_FEATURE_FMA3] = (reg[2] & (1 << 12)) != 0; + + // second call to the cpuid to get extended AMD feature bits + npy__cpu_cpuid(reg, 0x80000001); + npy__cpu_have[NPY_CPU_FEATURE_XOP] = (reg[2] & (1 << 11)) != 0; + npy__cpu_have[NPY_CPU_FEATURE_FMA4] = (reg[2] & (1 << 16)) != 0; + + // third call to the cpuid to get extended AVX2 & AVX512 feature bits + npy__cpu_cpuid(reg, 7); + npy__cpu_have[NPY_CPU_FEATURE_AVX2] = (reg[1] & (1 << 5)) != 0; + if (!npy__cpu_have[NPY_CPU_FEATURE_AVX2]) + return; + // detect AVX2 & FMA3 + npy__cpu_have[NPY_CPU_FEATURE_FMA] = npy__cpu_have[NPY_CPU_FEATURE_FMA3]; + + // check AVX512 OS support + int avx512_os = (xcr & 0xe6) == 0xe6; +#if defined(__APPLE__) && defined(__x86_64__) + /** + * On darwin, machines with AVX512 support, by default, threads are created with + * AVX512 masked off in XCR0 and an AVX-sized savearea is used. + * However, AVX512 capabilities are advertised in the commpage and via sysctl. + * for more information, check: + * - https://github.com/apple/darwin-xnu/blob/0a798f6738bc1db01281fc08ae024145e84df927/osfmk/i386/fpu.c#L175-L201 + * - https://github.com/golang/go/issues/43089 + * - https://github.com/numpy/numpy/issues/19319 + */ + if (!avx512_os) { + npy_uintp commpage64_addr = 0x00007fffffe00000ULL; + npy_uint16 commpage64_ver = *((npy_uint16*)(commpage64_addr + 0x01E)); + // cpu_capabilities64 undefined in versions < 13 + if (commpage64_ver > 12) { + npy_uint64 commpage64_cap = *((npy_uint64*)(commpage64_addr + 0x010)); + avx512_os = (commpage64_cap & 0x0000004000000000ULL) != 0; + } + } +#endif + if (!avx512_os) { + return; + } + npy__cpu_have[NPY_CPU_FEATURE_AVX512F] = (reg[1] & (1 << 16)) != 0; + npy__cpu_have[NPY_CPU_FEATURE_AVX512CD] = (reg[1] & (1 << 28)) != 0; + if (npy__cpu_have[NPY_CPU_FEATURE_AVX512F] && npy__cpu_have[NPY_CPU_FEATURE_AVX512CD]) { + // Knights Landing + npy__cpu_have[NPY_CPU_FEATURE_AVX512PF] = (reg[1] & (1 << 26)) != 0; + npy__cpu_have[NPY_CPU_FEATURE_AVX512ER] = (reg[1] & (1 << 27)) != 0; + npy__cpu_have[NPY_CPU_FEATURE_AVX512_KNL] = npy__cpu_have[NPY_CPU_FEATURE_AVX512ER] && + npy__cpu_have[NPY_CPU_FEATURE_AVX512PF]; + // Knights Mill + npy__cpu_have[NPY_CPU_FEATURE_AVX512VPOPCNTDQ] = (reg[2] & (1 << 14)) != 0; + npy__cpu_have[NPY_CPU_FEATURE_AVX5124VNNIW] = (reg[3] & (1 << 2)) != 0; + npy__cpu_have[NPY_CPU_FEATURE_AVX5124FMAPS] = (reg[3] & (1 << 3)) != 0; + npy__cpu_have[NPY_CPU_FEATURE_AVX512_KNM] = npy__cpu_have[NPY_CPU_FEATURE_AVX512_KNL] && + npy__cpu_have[NPY_CPU_FEATURE_AVX5124FMAPS] && + npy__cpu_have[NPY_CPU_FEATURE_AVX5124VNNIW] && + npy__cpu_have[NPY_CPU_FEATURE_AVX512VPOPCNTDQ]; + + // Skylake-X + npy__cpu_have[NPY_CPU_FEATURE_AVX512DQ] = (reg[1] & (1 << 17)) != 0; + npy__cpu_have[NPY_CPU_FEATURE_AVX512BW] = (reg[1] & (1 << 30)) != 0; + npy__cpu_have[NPY_CPU_FEATURE_AVX512VL] = (reg[1] & (1 << 31)) != 0; + npy__cpu_have[NPY_CPU_FEATURE_AVX512_SKX] = npy__cpu_have[NPY_CPU_FEATURE_AVX512BW] && + npy__cpu_have[NPY_CPU_FEATURE_AVX512DQ] && + npy__cpu_have[NPY_CPU_FEATURE_AVX512VL]; + // Cascade Lake + npy__cpu_have[NPY_CPU_FEATURE_AVX512VNNI] = (reg[2] & (1 << 11)) != 0; + npy__cpu_have[NPY_CPU_FEATURE_AVX512_CLX] = npy__cpu_have[NPY_CPU_FEATURE_AVX512_SKX] && + npy__cpu_have[NPY_CPU_FEATURE_AVX512VNNI]; + + // Cannon Lake + npy__cpu_have[NPY_CPU_FEATURE_AVX512IFMA] = (reg[1] & (1 << 21)) != 0; + npy__cpu_have[NPY_CPU_FEATURE_AVX512VBMI] = (reg[2] & (1 << 1)) != 0; + npy__cpu_have[NPY_CPU_FEATURE_AVX512_CNL] = npy__cpu_have[NPY_CPU_FEATURE_AVX512_SKX] && + npy__cpu_have[NPY_CPU_FEATURE_AVX512IFMA] && + npy__cpu_have[NPY_CPU_FEATURE_AVX512VBMI]; + // Ice Lake + npy__cpu_have[NPY_CPU_FEATURE_AVX512VBMI2] = (reg[2] & (1 << 6)) != 0; + npy__cpu_have[NPY_CPU_FEATURE_AVX512BITALG] = (reg[2] & (1 << 12)) != 0; + npy__cpu_have[NPY_CPU_FEATURE_AVX512_ICL] = npy__cpu_have[NPY_CPU_FEATURE_AVX512_CLX] && + npy__cpu_have[NPY_CPU_FEATURE_AVX512_CNL] && + npy__cpu_have[NPY_CPU_FEATURE_AVX512VBMI2] && + npy__cpu_have[NPY_CPU_FEATURE_AVX512BITALG] && + npy__cpu_have[NPY_CPU_FEATURE_AVX512VPOPCNTDQ]; + } +} + +/***************** POWER ******************/ + +#elif defined(NPY_CPU_PPC64) || defined(NPY_CPU_PPC64LE) + +#ifdef __linux__ + #include <sys/auxv.h> + #ifndef AT_HWCAP2 + #define AT_HWCAP2 26 + #endif + #ifndef PPC_FEATURE2_ARCH_3_00 + #define PPC_FEATURE2_ARCH_3_00 0x00800000 + #endif +#endif + +static void +npy__cpu_init_features(void) +{ + memset(npy__cpu_have, 0, sizeof(npy__cpu_have[0]) * NPY_CPU_FEATURE_MAX); +#ifdef __linux__ + unsigned int hwcap = getauxval(AT_HWCAP); + if ((hwcap & PPC_FEATURE_HAS_VSX) == 0) + return; + + hwcap = getauxval(AT_HWCAP2); + if (hwcap & PPC_FEATURE2_ARCH_3_00) + { + npy__cpu_have[NPY_CPU_FEATURE_VSX] = + npy__cpu_have[NPY_CPU_FEATURE_VSX2] = + npy__cpu_have[NPY_CPU_FEATURE_VSX3] = 1; + return; + } + npy__cpu_have[NPY_CPU_FEATURE_VSX2] = (hwcap & PPC_FEATURE2_ARCH_2_07) != 0; + npy__cpu_have[NPY_CPU_FEATURE_VSX] = 1; +// TODO: AIX, FreeBSD +#else + npy__cpu_have[NPY_CPU_FEATURE_VSX] = 1; + #if defined(NPY_CPU_PPC64LE) || defined(NPY_HAVE_VSX2) + npy__cpu_have[NPY_CPU_FEATURE_VSX2] = 1; + #endif + #ifdef NPY_HAVE_VSX3 + npy__cpu_have[NPY_CPU_FEATURE_VSX3] = 1; + #endif +#endif +} + +/***************** ARM ******************/ + +#elif defined(__arm__) || defined(__aarch64__) + +static NPY_INLINE void +npy__cpu_init_features_arm8(void) +{ + npy__cpu_have[NPY_CPU_FEATURE_NEON] = + npy__cpu_have[NPY_CPU_FEATURE_NEON_FP16] = + npy__cpu_have[NPY_CPU_FEATURE_NEON_VFPV4] = + npy__cpu_have[NPY_CPU_FEATURE_ASIMD] = 1; +} + +#if defined(__linux__) || defined(__FreeBSD__) +/* + * we aren't sure of what kind kernel or clib we deal with + * so we play it safe +*/ +#include <stdio.h> +#include "npy_cpuinfo_parser.h" + +__attribute__((weak)) unsigned long getauxval(unsigned long); // linker should handle it +#ifdef __FreeBSD__ +__attribute__((weak)) int elf_aux_info(int, void *, int); // linker should handle it + +static unsigned long getauxval(unsigned long k) +{ + unsigned long val = 0ul; + if (elf_aux_info == 0 || elf_aux_info((int)k, (void *)&val, (int)sizeof(val)) != 0) { + return 0ul; + } + return val; +} +#endif +static int +npy__cpu_init_features_linux(void) +{ + unsigned long hwcap = 0, hwcap2 = 0; + #ifdef __linux__ + if (getauxval != 0) { + hwcap = getauxval(NPY__HWCAP); + #ifdef __arm__ + hwcap2 = getauxval(NPY__HWCAP2); + #endif + } else { + unsigned long auxv[2]; + int fd = open("/proc/self/auxv", O_RDONLY); + if (fd >= 0) { + while (read(fd, &auxv, sizeof(auxv)) == sizeof(auxv)) { + if (auxv[0] == NPY__HWCAP) { + hwcap = auxv[1]; + } + #ifdef __arm__ + else if (auxv[0] == NPY__HWCAP2) { + hwcap2 = auxv[1]; + } + #endif + // detect the end + else if (auxv[0] == 0 && auxv[1] == 0) { + break; + } + } + close(fd); + } + } + #else + hwcap = getauxval(NPY__HWCAP); + #ifdef __arm__ + hwcap2 = getauxval(NPY__HWCAP2); + #endif + #endif + if (hwcap == 0 && hwcap2 == 0) { + #ifdef __linux__ + /* + * try parsing with /proc/cpuinfo, if sandboxed + * failback to compiler definitions + */ + if(!get_feature_from_proc_cpuinfo(&hwcap, &hwcap2)) { + return 0; + } + #else + return 0; + #endif + } +#ifdef __arm__ + // Detect Arm8 (aarch32 state) + if ((hwcap2 & NPY__HWCAP2_AES) || (hwcap2 & NPY__HWCAP2_SHA1) || + (hwcap2 & NPY__HWCAP2_SHA2) || (hwcap2 & NPY__HWCAP2_PMULL) || + (hwcap2 & NPY__HWCAP2_CRC32)) + { + hwcap = hwcap2; +#else + if (1) + { + if (!(hwcap & (NPY__HWCAP_FP | NPY__HWCAP_ASIMD))) { + // Is this could happen? maybe disabled by kernel + // BTW this will break the baseline of AARCH64 + return 1; + } +#endif + npy__cpu_have[NPY_CPU_FEATURE_FPHP] = (hwcap & NPY__HWCAP_FPHP) != 0; + npy__cpu_have[NPY_CPU_FEATURE_ASIMDHP] = (hwcap & NPY__HWCAP_ASIMDHP) != 0; + npy__cpu_have[NPY_CPU_FEATURE_ASIMDDP] = (hwcap & NPY__HWCAP_ASIMDDP) != 0; + npy__cpu_have[NPY_CPU_FEATURE_ASIMDFHM] = (hwcap & NPY__HWCAP_ASIMDFHM) != 0; + npy__cpu_init_features_arm8(); + } else { + npy__cpu_have[NPY_CPU_FEATURE_NEON] = (hwcap & NPY__HWCAP_NEON) != 0; + if (npy__cpu_have[NPY_CPU_FEATURE_NEON]) { + npy__cpu_have[NPY_CPU_FEATURE_NEON_FP16] = (hwcap & NPY__HWCAP_HALF) != 0; + npy__cpu_have[NPY_CPU_FEATURE_NEON_VFPV4] = (hwcap & NPY__HWCAP_VFPv4) != 0; + } + } + return 1; +} +#endif + +static void +npy__cpu_init_features(void) +{ + memset(npy__cpu_have, 0, sizeof(npy__cpu_have[0]) * NPY_CPU_FEATURE_MAX); +#ifdef __linux__ + if (npy__cpu_init_features_linux()) + return; +#endif + // We have nothing else todo +#if defined(NPY_HAVE_ASIMD) || defined(__aarch64__) || (defined(__ARM_ARCH) && __ARM_ARCH >= 8) + #if defined(NPY_HAVE_FPHP) || defined(__ARM_FEATURE_FP16_VECTOR_ARITHMETIC) + npy__cpu_have[NPY_CPU_FEATURE_FPHP] = 1; + #endif + #if defined(NPY_HAVE_ASIMDHP) || defined(__ARM_FEATURE_FP16_VECTOR_ARITHMETIC) + npy__cpu_have[NPY_CPU_FEATURE_ASIMDHP] = 1; + #endif + #if defined(NPY_HAVE_ASIMDDP) || defined(__ARM_FEATURE_DOTPROD) + npy__cpu_have[NPY_CPU_FEATURE_ASIMDDP] = 1; + #endif + #if defined(NPY_HAVE_ASIMDFHM) || defined(__ARM_FEATURE_FP16FML) + npy__cpu_have[NPY_CPU_FEATURE_ASIMDFHM] = 1; + #endif + npy__cpu_init_features_arm8(); +#else + #if defined(NPY_HAVE_NEON) || defined(__ARM_NEON__) + npy__cpu_have[NPY_CPU_FEATURE_NEON] = 1; + #endif + #if defined(NPY_HAVE_NEON_FP16) || defined(__ARM_FP16_FORMAT_IEEE) || (defined(__ARM_FP) && (__ARM_FP & 2)) + npy__cpu_have[NPY_CPU_FEATURE_NEON_FP16] = npy__cpu_have[NPY_CPU_FEATURE_NEON]; + #endif + #if defined(NPY_HAVE_NEON_VFPV4) || defined(__ARM_FEATURE_FMA) + npy__cpu_have[NPY_CPU_FEATURE_NEON_VFPV4] = npy__cpu_have[NPY_CPU_FEATURE_NEON]; + #endif +#endif +} + +/*********** Unsupported ARCH ***********/ +#else +static void +npy__cpu_init_features(void) +{ + /* + * just in case if the compiler doesn't respect ANSI + * but for knowing paltforms it still nessecery, because @npy__cpu_init_features + * may called multiple of times and we need to clear the disabled features by + * ENV Var or maybe in the future we can support other methods like + * global variables, go back to @npy__cpu_try_disable_env for more understanding + */ + memset(npy__cpu_have, 0, sizeof(npy__cpu_have[0]) * NPY_CPU_FEATURE_MAX); +} +#endif diff --git a/numpy/core/src/common/npy_cpu_features.h b/numpy/core/src/common/npy_cpu_features.h new file mode 100644 index 000000000000..ce1fc822ac03 --- /dev/null +++ b/numpy/core/src/common/npy_cpu_features.h @@ -0,0 +1,171 @@ +#ifndef NUMPY_CORE_SRC_COMMON_NPY_CPU_FEATURES_H_ +#define NUMPY_CORE_SRC_COMMON_NPY_CPU_FEATURES_H_ + +#include <Python.h> // for PyObject +#include "numpy/numpyconfig.h" // for NPY_VISIBILITY_HIDDEN + +#ifdef __cplusplus +extern "C" { +#endif + +enum npy_cpu_features +{ + NPY_CPU_FEATURE_NONE = 0, + // X86 + NPY_CPU_FEATURE_MMX = 1, + NPY_CPU_FEATURE_SSE = 2, + NPY_CPU_FEATURE_SSE2 = 3, + NPY_CPU_FEATURE_SSE3 = 4, + NPY_CPU_FEATURE_SSSE3 = 5, + NPY_CPU_FEATURE_SSE41 = 6, + NPY_CPU_FEATURE_POPCNT = 7, + NPY_CPU_FEATURE_SSE42 = 8, + NPY_CPU_FEATURE_AVX = 9, + NPY_CPU_FEATURE_F16C = 10, + NPY_CPU_FEATURE_XOP = 11, + NPY_CPU_FEATURE_FMA4 = 12, + NPY_CPU_FEATURE_FMA3 = 13, + NPY_CPU_FEATURE_AVX2 = 14, + NPY_CPU_FEATURE_FMA = 15, // AVX2 & FMA3, provides backward compatibility + + NPY_CPU_FEATURE_AVX512F = 30, + NPY_CPU_FEATURE_AVX512CD = 31, + NPY_CPU_FEATURE_AVX512ER = 32, + NPY_CPU_FEATURE_AVX512PF = 33, + NPY_CPU_FEATURE_AVX5124FMAPS = 34, + NPY_CPU_FEATURE_AVX5124VNNIW = 35, + NPY_CPU_FEATURE_AVX512VPOPCNTDQ = 36, + NPY_CPU_FEATURE_AVX512BW = 37, + NPY_CPU_FEATURE_AVX512DQ = 38, + NPY_CPU_FEATURE_AVX512VL = 39, + NPY_CPU_FEATURE_AVX512IFMA = 40, + NPY_CPU_FEATURE_AVX512VBMI = 41, + NPY_CPU_FEATURE_AVX512VNNI = 42, + NPY_CPU_FEATURE_AVX512VBMI2 = 43, + NPY_CPU_FEATURE_AVX512BITALG = 44, + + // X86 CPU Groups + // Knights Landing (F,CD,ER,PF) + NPY_CPU_FEATURE_AVX512_KNL = 101, + // Knights Mill (F,CD,ER,PF,4FMAPS,4VNNIW,VPOPCNTDQ) + NPY_CPU_FEATURE_AVX512_KNM = 102, + // Skylake-X (F,CD,BW,DQ,VL) + NPY_CPU_FEATURE_AVX512_SKX = 103, + // Cascade Lake (F,CD,BW,DQ,VL,VNNI) + NPY_CPU_FEATURE_AVX512_CLX = 104, + // Cannon Lake (F,CD,BW,DQ,VL,IFMA,VBMI) + NPY_CPU_FEATURE_AVX512_CNL = 105, + // Ice Lake (F,CD,BW,DQ,VL,IFMA,VBMI,VNNI,VBMI2,BITALG,VPOPCNTDQ) + NPY_CPU_FEATURE_AVX512_ICL = 106, + + // IBM/POWER VSX + // POWER7 + NPY_CPU_FEATURE_VSX = 200, + // POWER8 + NPY_CPU_FEATURE_VSX2 = 201, + // POWER9 + NPY_CPU_FEATURE_VSX3 = 202, + + // ARM + NPY_CPU_FEATURE_NEON = 300, + NPY_CPU_FEATURE_NEON_FP16 = 301, + // FMA + NPY_CPU_FEATURE_NEON_VFPV4 = 302, + // Advanced SIMD + NPY_CPU_FEATURE_ASIMD = 303, + // ARMv8.2 half-precision + NPY_CPU_FEATURE_FPHP = 304, + // ARMv8.2 half-precision vector arithm + NPY_CPU_FEATURE_ASIMDHP = 305, + // ARMv8.2 dot product + NPY_CPU_FEATURE_ASIMDDP = 306, + // ARMv8.2 single&half-precision multiply + NPY_CPU_FEATURE_ASIMDFHM = 307, + + NPY_CPU_FEATURE_MAX +}; + +/* + * Initialize CPU features + * + * This function + * - detects runtime CPU features + * - check that baseline CPU features are present + * - uses 'NPY_DISABLE_CPU_FEATURES' to disable dispatchable features + * + * It will set a RuntimeError when + * - CPU baseline features from the build are not supported at runtime + * - 'NPY_DISABLE_CPU_FEATURES' tries to disable a baseline feature + * and will warn if 'NPY_DISABLE_CPU_FEATURES' tries to disable a feature that + * is not disabled (the machine or build does not support it, or the project was + * not built with any feature optimization support) + * return 0 on success otherwise return -1 + */ +NPY_VISIBILITY_HIDDEN int +npy_cpu_init(void); + +/* + * return 0 if CPU feature isn't available + * note: `npy_cpu_init` must be called first otherwise it will always return 0 +*/ +NPY_VISIBILITY_HIDDEN int +npy_cpu_have(int feature_id); + +#define NPY_CPU_HAVE(FEATURE_NAME) \ +npy_cpu_have(NPY_CPU_FEATURE_##FEATURE_NAME) + +/* + * return a new dictionary contains CPU feature names + * with runtime availability. + * same as npy_cpu_have, `npy_cpu_init` must be called first. + */ +NPY_VISIBILITY_HIDDEN PyObject * +npy_cpu_features_dict(void); +/* + * Return a new a Python list contains the minimal set of required optimizations + * that supported by the compiler and platform according to the specified + * values to command argument '--cpu-baseline'. + * + * This function is mainly used to implement umath's attrbute '__cpu_baseline__', + * and the items are sorted from the lowest to highest interest. + * + * For example, according to the default build configuration and by assuming the compiler + * support all the involved optimizations then the returned list should equivalent to: + * + * On x86: ['SSE', 'SSE2'] + * On x64: ['SSE', 'SSE2', 'SSE3'] + * On armhf: [] + * On aarch64: ['NEON', 'NEON_FP16', 'NEON_VPFV4', 'ASIMD'] + * On ppc64: [] + * On ppc64le: ['VSX', 'VSX2'] + * On any other arch or if the optimization is disabled: [] + */ +NPY_VISIBILITY_HIDDEN PyObject * +npy_cpu_baseline_list(void); +/* + * Return a new a Python list contains the dispatched set of additional optimizations + * that supported by the compiler and platform according to the specified + * values to command argument '--cpu-dispatch'. + * + * This function is mainly used to implement umath's attrbute '__cpu_dispatch__', + * and the items are sorted from the lowest to highest interest. + * + * For example, according to the default build configuration and by assuming the compiler + * support all the involved optimizations then the returned list should equivalent to: + * + * On x86: ['SSE3', 'SSSE3', 'SSE41', 'POPCNT', 'SSE42', 'AVX', 'F16C', 'FMA3', 'AVX2', 'AVX512F', ...] + * On x64: ['SSSE3', 'SSE41', 'POPCNT', 'SSE42', 'AVX', 'F16C', 'FMA3', 'AVX2', 'AVX512F', ...] + * On armhf: ['NEON', 'NEON_FP16', 'NEON_VPFV4', 'ASIMD', 'ASIMDHP', 'ASIMDDP', 'ASIMDFHM'] + * On aarch64: ['ASIMDHP', 'ASIMDDP', 'ASIMDFHM'] + * On ppc64: ['VSX', 'VSX2', 'VSX3'] + * On ppc64le: ['VSX3'] + * On any other arch or if the optimization is disabled: [] + */ +NPY_VISIBILITY_HIDDEN PyObject * +npy_cpu_dispatch_list(void); + +#ifdef __cplusplus +} +#endif + +#endif // NUMPY_CORE_SRC_COMMON_NPY_CPU_FEATURES_H_ diff --git a/numpy/core/src/common/npy_cpuinfo_parser.h b/numpy/core/src/common/npy_cpuinfo_parser.h new file mode 100644 index 000000000000..364873a23ed6 --- /dev/null +++ b/numpy/core/src/common/npy_cpuinfo_parser.h @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef NUMPY_CORE_SRC_COMMON_NPY_CPUINFO_PARSER_H_ +#define NUMPY_CORE_SRC_COMMON_NPY_CPUINFO_PARSER_H_ +#include <errno.h> +#include <stdio.h> +#include <fcntl.h> +#include <string.h> +#include <stddef.h> + +#define NPY__HWCAP 16 +#define NPY__HWCAP2 26 + +// arch/arm/include/uapi/asm/hwcap.h +#define NPY__HWCAP_HALF (1 << 1) +#define NPY__HWCAP_NEON (1 << 12) +#define NPY__HWCAP_VFPv3 (1 << 13) +#define NPY__HWCAP_VFPv4 (1 << 16) +#define NPY__HWCAP2_AES (1 << 0) +#define NPY__HWCAP2_PMULL (1 << 1) +#define NPY__HWCAP2_SHA1 (1 << 2) +#define NPY__HWCAP2_SHA2 (1 << 3) +#define NPY__HWCAP2_CRC32 (1 << 4) +// arch/arm64/include/uapi/asm/hwcap.h +#define NPY__HWCAP_FP (1 << 0) +#define NPY__HWCAP_ASIMD (1 << 1) +#define NPY__HWCAP_FPHP (1 << 9) +#define NPY__HWCAP_ASIMDHP (1 << 10) +#define NPY__HWCAP_ASIMDDP (1 << 20) +#define NPY__HWCAP_ASIMDFHM (1 << 23) +/* + * Get the size of a file by reading it until the end. This is needed + * because files under /proc do not always return a valid size when + * using fseek(0, SEEK_END) + ftell(). Nor can they be mmap()-ed. + */ +static int +get_file_size(const char* pathname) +{ + int fd, result = 0; + char buffer[256]; + + fd = open(pathname, O_RDONLY); + if (fd < 0) { + return -1; + } + + for (;;) { + int ret = read(fd, buffer, sizeof buffer); + if (ret < 0) { + if (errno == EINTR) { + continue; + } + break; + } + if (ret == 0) { + break; + } + result += ret; + } + close(fd); + return result; +} + +/* + * Read the content of /proc/cpuinfo into a user-provided buffer. + * Return the length of the data, or -1 on error. Does *not* + * zero-terminate the content. Will not read more + * than 'buffsize' bytes. + */ +static int +read_file(const char* pathname, char* buffer, size_t buffsize) +{ + int fd, count; + + fd = open(pathname, O_RDONLY); + if (fd < 0) { + return -1; + } + count = 0; + while (count < (int)buffsize) { + int ret = read(fd, buffer + count, buffsize - count); + if (ret < 0) { + if (errno == EINTR) { + continue; + } + if (count == 0) { + count = -1; + } + break; + } + if (ret == 0) { + break; + } + count += ret; + } + close(fd); + return count; +} + +/* + * Extract the content of a the first occurrence of a given field in + * the content of /proc/cpuinfo and return it as a heap-allocated + * string that must be freed by the caller. + * + * Return NULL if not found + */ +static char* +extract_cpuinfo_field(const char* buffer, int buflen, const char* field) +{ + int fieldlen = strlen(field); + const char* bufend = buffer + buflen; + char* result = NULL; + int len; + const char *p, *q; + + /* Look for first field occurrence, and ensures it starts the line. */ + p = buffer; + for (;;) { + p = memmem(p, bufend-p, field, fieldlen); + if (p == NULL) { + goto EXIT; + } + + if (p == buffer || p[-1] == '\n') { + break; + } + + p += fieldlen; + } + + /* Skip to the first column followed by a space */ + p += fieldlen; + p = memchr(p, ':', bufend-p); + if (p == NULL || p[1] != ' ') { + goto EXIT; + } + + /* Find the end of the line */ + p += 2; + q = memchr(p, '\n', bufend-p); + if (q == NULL) { + q = bufend; + } + + /* Copy the line into a heap-allocated buffer */ + len = q - p; + result = malloc(len + 1); + if (result == NULL) { + goto EXIT; + } + + memcpy(result, p, len); + result[len] = '\0'; + +EXIT: + return result; +} + +/* + * Checks that a space-separated list of items contains one given 'item'. + * Returns 1 if found, 0 otherwise. + */ +static int +has_list_item(const char* list, const char* item) +{ + const char* p = list; + int itemlen = strlen(item); + + if (list == NULL) { + return 0; + } + + while (*p) { + const char* q; + + /* skip spaces */ + while (*p == ' ' || *p == '\t') { + p++; + } + + /* find end of current list item */ + q = p; + while (*q && *q != ' ' && *q != '\t') { + q++; + } + + if (itemlen == q-p && !memcmp(p, item, itemlen)) { + return 1; + } + + /* skip to next item */ + p = q; + } + return 0; +} + +static void setHwcap(char* cpuFeatures, unsigned long* hwcap) { + *hwcap |= has_list_item(cpuFeatures, "neon") ? NPY__HWCAP_NEON : 0; + *hwcap |= has_list_item(cpuFeatures, "half") ? NPY__HWCAP_HALF : 0; + *hwcap |= has_list_item(cpuFeatures, "vfpv3") ? NPY__HWCAP_VFPv3 : 0; + *hwcap |= has_list_item(cpuFeatures, "vfpv4") ? NPY__HWCAP_VFPv4 : 0; + + *hwcap |= has_list_item(cpuFeatures, "asimd") ? NPY__HWCAP_ASIMD : 0; + *hwcap |= has_list_item(cpuFeatures, "fp") ? NPY__HWCAP_FP : 0; + *hwcap |= has_list_item(cpuFeatures, "fphp") ? NPY__HWCAP_FPHP : 0; + *hwcap |= has_list_item(cpuFeatures, "asimdhp") ? NPY__HWCAP_ASIMDHP : 0; + *hwcap |= has_list_item(cpuFeatures, "asimddp") ? NPY__HWCAP_ASIMDDP : 0; + *hwcap |= has_list_item(cpuFeatures, "asimdfhm") ? NPY__HWCAP_ASIMDFHM : 0; +} + +static int +get_feature_from_proc_cpuinfo(unsigned long *hwcap, unsigned long *hwcap2) { + char* cpuinfo = NULL; + int cpuinfo_len; + cpuinfo_len = get_file_size("/proc/cpuinfo"); + if (cpuinfo_len < 0) { + return 0; + } + cpuinfo = malloc(cpuinfo_len); + if (cpuinfo == NULL) { + return 0; + } + cpuinfo_len = read_file("/proc/cpuinfo", cpuinfo, cpuinfo_len); + char* cpuFeatures = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "Features"); + if(cpuFeatures == NULL) { + return 0; + } + setHwcap(cpuFeatures, hwcap); + *hwcap2 |= *hwcap; + *hwcap2 |= has_list_item(cpuFeatures, "aes") ? NPY__HWCAP2_AES : 0; + *hwcap2 |= has_list_item(cpuFeatures, "pmull") ? NPY__HWCAP2_PMULL : 0; + *hwcap2 |= has_list_item(cpuFeatures, "sha1") ? NPY__HWCAP2_SHA1 : 0; + *hwcap2 |= has_list_item(cpuFeatures, "sha2") ? NPY__HWCAP2_SHA2 : 0; + *hwcap2 |= has_list_item(cpuFeatures, "crc32") ? NPY__HWCAP2_CRC32 : 0; + return 1; +} +#endif /* NUMPY_CORE_SRC_COMMON_NPY_CPUINFO_PARSER_H_ */ diff --git a/numpy/core/src/common/npy_ctypes.h b/numpy/core/src/common/npy_ctypes.h new file mode 100644 index 000000000000..05761cad3c1c --- /dev/null +++ b/numpy/core/src/common/npy_ctypes.h @@ -0,0 +1,50 @@ +#ifndef NUMPY_CORE_SRC_COMMON_NPY_CTYPES_H_ +#define NUMPY_CORE_SRC_COMMON_NPY_CTYPES_H_ + +#include <Python.h> + +#include "npy_import.h" + +/* + * Check if a python type is a ctypes class. + * + * Works like the Py<type>_Check functions, returning true if the argument + * looks like a ctypes object. + * + * This entire function is just a wrapper around the Python function of the + * same name. + */ +NPY_INLINE static int +npy_ctypes_check(PyTypeObject *obj) +{ + static PyObject *py_func = NULL; + PyObject *ret_obj; + int ret; + + npy_cache_import("numpy.core._internal", "npy_ctypes_check", &py_func); + if (py_func == NULL) { + goto fail; + } + + ret_obj = PyObject_CallFunctionObjArgs(py_func, (PyObject *)obj, NULL); + if (ret_obj == NULL) { + goto fail; + } + + ret = PyObject_IsTrue(ret_obj); + Py_DECREF(ret_obj); + if (ret == -1) { + goto fail; + } + + return ret; + +fail: + /* If the above fails, then we should just assume that the type is not from + * ctypes + */ + PyErr_Clear(); + return 0; +} + +#endif /* NUMPY_CORE_SRC_COMMON_NPY_CTYPES_H_ */ diff --git a/numpy/core/src/common/npy_dlpack.h b/numpy/core/src/common/npy_dlpack.h new file mode 100644 index 000000000000..14ca352c01a7 --- /dev/null +++ b/numpy/core/src/common/npy_dlpack.h @@ -0,0 +1,28 @@ +#include "Python.h" +#include "dlpack/dlpack.h" + +#ifndef NPY_DLPACK_H +#define NPY_DLPACK_H + +// Part of the Array API specification. +#define NPY_DLPACK_CAPSULE_NAME "dltensor" +#define NPY_DLPACK_USED_CAPSULE_NAME "used_dltensor" + +// Used internally by NumPy to store a base object +// as it has to release a reference to the original +// capsule. +#define NPY_DLPACK_INTERNAL_CAPSULE_NAME "numpy_dltensor" + +PyObject * +array_dlpack(PyArrayObject *self, PyObject *const *args, Py_ssize_t len_args, + PyObject *kwnames); + + +PyObject * +array_dlpack_device(PyArrayObject *self, PyObject *NPY_UNUSED(args)); + + +NPY_NO_EXPORT PyObject * +_from_dlpack(PyObject *NPY_UNUSED(self), PyObject *obj); + +#endif diff --git a/numpy/core/src/private/npy_extint128.h b/numpy/core/src/common/npy_extint128.h similarity index 97% rename from numpy/core/src/private/npy_extint128.h rename to numpy/core/src/common/npy_extint128.h index a887ff317a6e..d563c2ac8588 100644 --- a/numpy/core/src/private/npy_extint128.h +++ b/numpy/core/src/common/npy_extint128.h @@ -1,5 +1,5 @@ -#ifndef NPY_EXTINT128_H_ -#define NPY_EXTINT128_H_ +#ifndef NUMPY_CORE_SRC_COMMON_NPY_EXTINT128_H_ +#define NUMPY_CORE_SRC_COMMON_NPY_EXTINT128_H_ typedef struct { @@ -314,4 +314,4 @@ ceildiv_128_64(npy_extint128_t a, npy_int64 b) return result; } -#endif +#endif /* NUMPY_CORE_SRC_COMMON_NPY_EXTINT128_H_ */ diff --git a/numpy/core/src/private/npy_fpmath.h b/numpy/core/src/common/npy_fpmath.h similarity index 85% rename from numpy/core/src/private/npy_fpmath.h rename to numpy/core/src/common/npy_fpmath.h index dbb3fb23dde6..27e9ea3f4ece 100644 --- a/numpy/core/src/private/npy_fpmath.h +++ b/numpy/core/src/common/npy_fpmath.h @@ -1,5 +1,5 @@ -#ifndef _NPY_NPY_FPMATH_H_ -#define _NPY_NPY_FPMATH_H_ +#ifndef NUMPY_CORE_SRC_COMMON_NPY_NPY_FPMATH_H_ +#define NUMPY_CORE_SRC_COMMON_NPY_NPY_FPMATH_H_ #include "npy_config.h" @@ -27,4 +27,4 @@ #define HAVE_LDOUBLE_DOUBLE_DOUBLE_BE #endif -#endif +#endif /* NUMPY_CORE_SRC_COMMON_NPY_NPY_FPMATH_H_ */ diff --git a/numpy/core/src/common/npy_hashtable.c b/numpy/core/src/common/npy_hashtable.c new file mode 100644 index 000000000000..af9e2df432a1 --- /dev/null +++ b/numpy/core/src/common/npy_hashtable.c @@ -0,0 +1,220 @@ +/* + * This functionality is designed specifically for the ufunc machinery to + * dispatch based on multiple DTypes. Since this is designed to be used + * as purely a cache, it currently does no reference counting. + * Even though this is a cache, there is currently no maximum size. It may + * make sense to limit the size, or count collisions: If too many collisions + * occur, we could grow the cache, otherwise, just replace an old item that + * was presumably not used for a long time. + * + * If a different part of NumPy requires a custom hashtable, the code should + * be reused with care since specializing it more for the ufunc dispatching + * case is likely desired. + */ + +#include "templ_common.h" +#include "npy_hashtable.h" + + + +#if SIZEOF_PY_UHASH_T > 4 +#define _NpyHASH_XXPRIME_1 ((Py_uhash_t)11400714785074694791ULL) +#define _NpyHASH_XXPRIME_2 ((Py_uhash_t)14029467366897019727ULL) +#define _NpyHASH_XXPRIME_5 ((Py_uhash_t)2870177450012600261ULL) +#define _NpyHASH_XXROTATE(x) ((x << 31) | (x >> 33)) /* Rotate left 31 bits */ +#else +#define _NpyHASH_XXPRIME_1 ((Py_uhash_t)2654435761UL) +#define _NpyHASH_XXPRIME_2 ((Py_uhash_t)2246822519UL) +#define _NpyHASH_XXPRIME_5 ((Py_uhash_t)374761393UL) +#define _NpyHASH_XXROTATE(x) ((x << 13) | (x >> 19)) /* Rotate left 13 bits */ +#endif + +/* + * This hashing function is basically the Python tuple hash with the type + * identity hash inlined. The tuple hash itself is a reduced version of xxHash. + * + * Users cannot control pointers, so we do not have to worry about DoS attacks? + */ +static NPY_INLINE Py_hash_t +identity_list_hash(PyObject *const *v, int len) +{ + Py_uhash_t acc = _NpyHASH_XXPRIME_5; + for (int i = 0; i < len; i++) { + /* + * Lane is the single item hash, which for us is the rotated pointer. + * Identical to the python type hash (pointers end with 0s normally). + */ + size_t y = (size_t)v[i]; + Py_uhash_t lane = (y >> 4) | (y << (8 * SIZEOF_VOID_P - 4)); + acc += lane * _NpyHASH_XXPRIME_2; + acc = _NpyHASH_XXROTATE(acc); + acc *= _NpyHASH_XXPRIME_1; + } + return acc; +} +#undef _NpyHASH_XXPRIME_1 +#undef _NpyHASH_XXPRIME_2 +#undef _NpyHASH_XXPRIME_5 +#undef _NpyHASH_XXROTATE + + +static NPY_INLINE PyObject ** +find_item(PyArrayIdentityHash const *tb, PyObject *const *key) +{ + Py_hash_t hash = identity_list_hash(key, tb->key_len); + npy_uintp perturb = (npy_uintp)hash; + npy_intp bucket; + npy_intp mask = tb->size - 1 ; + PyObject **item; + + bucket = (npy_intp)hash & mask; + while (1) { + item = &(tb->buckets[bucket * (tb->key_len + 1)]); + + if (item[0] == NULL) { + /* The item is not in the cache; return the empty bucket */ + return item; + } + if (memcmp(item+1, key, tb->key_len * sizeof(PyObject *)) == 0) { + /* This is a match, so return the item/bucket */ + return item; + } + /* Hash collision, perturb like Python (must happen rarely!) */ + perturb >>= 5; /* Python uses the macro PERTURB_SHIFT == 5 */ + bucket = mask & (bucket * 5 + perturb + 1); + } +} + + +NPY_NO_EXPORT PyArrayIdentityHash * +PyArrayIdentityHash_New(int key_len) +{ + PyArrayIdentityHash *res = PyMem_Malloc(sizeof(PyArrayIdentityHash)); + if (res == NULL) { + PyErr_NoMemory(); + return NULL; + } + + assert(key_len > 0); + res->key_len = key_len; + res->size = 4; /* Start with a size of 4 */ + res->nelem = 0; + + res->buckets = PyMem_Calloc(4 * (key_len + 1), sizeof(PyObject *)); + if (res->buckets == NULL) { + PyErr_NoMemory(); + PyMem_Free(res); + return NULL; + } + return res; +} + + +NPY_NO_EXPORT void +PyArrayIdentityHash_Dealloc(PyArrayIdentityHash *tb) +{ + PyMem_Free(tb->buckets); + PyMem_Free(tb); +} + + +static int +_resize_if_necessary(PyArrayIdentityHash *tb) +{ + npy_intp new_size, prev_size = tb->size; + PyObject **old_table = tb->buckets; + assert(prev_size > 0); + + if ((tb->nelem + 1) * 2 > prev_size) { + /* Double in size */ + new_size = prev_size * 2; + } + else { + new_size = prev_size; + while ((tb->nelem + 8) * 2 < new_size / 2) { + /* + * Should possibly be improved. However, we assume that we + * almost never shrink. Still if we do, do not shrink as much + * as possible to avoid growing right away. + */ + new_size /= 2; + } + assert(new_size >= 4); + } + if (new_size == prev_size) { + return 0; + } + + npy_intp alloc_size; + if (npy_mul_with_overflow_intp(&alloc_size, new_size, tb->key_len + 1)) { + return -1; + } + tb->buckets = PyMem_Calloc(alloc_size, sizeof(PyObject *)); + if (tb->buckets == NULL) { + tb->buckets = old_table; + PyErr_NoMemory(); + return -1; + } + + tb->size = new_size; + for (npy_intp i = 0; i < prev_size; i++) { + PyObject **item = &old_table[i * (tb->key_len + 1)]; + if (item[0] != NULL) { + tb->nelem -= 1; /* Decrement, setitem will increment again */ + PyArrayIdentityHash_SetItem(tb, item+1, item[0], 1); + } + } + PyMem_Free(old_table); + return 0; +} + + +/** + * Add an item to the identity cache. The storage location must not change + * unless the cache is cleared. + * + * @param tb The mapping. + * @param key The key, must be a C-array of pointers of the length + * corresponding to the mapping. + * @param value Normally a Python object, no reference counting is done. + * use NULL to clear an item. If the item does not exist, no + * action is performed for NULL. + * @param replace If 1, allow replacements. + * @returns 0 on success, -1 with a MemoryError or RuntimeError (if an item + * is added which is already in the cache). The caller should avoid + * the RuntimeError. + */ +NPY_NO_EXPORT int +PyArrayIdentityHash_SetItem(PyArrayIdentityHash *tb, + PyObject *const *key, PyObject *value, int replace) +{ + if (value != NULL && _resize_if_necessary(tb) < 0) { + /* Shrink, only if a new value is added. */ + return -1; + } + + PyObject **tb_item = find_item(tb, key); + if (value != NULL) { + if (tb_item[0] != NULL && !replace) { + PyErr_SetString(PyExc_RuntimeError, + "Identity cache already includes the item."); + return -1; + } + tb_item[0] = value; + memcpy(tb_item+1, key, tb->key_len * sizeof(PyObject *)); + tb->nelem += 1; + } + else { + /* Clear the bucket -- just the value should be enough though. */ + memset(tb_item, 0, (tb->key_len + 1) * sizeof(PyObject *)); + } + + return 0; +} + + +NPY_NO_EXPORT PyObject * +PyArrayIdentityHash_GetItem(PyArrayIdentityHash const *tb, PyObject *const *key) +{ + return find_item(tb, key)[0]; +} diff --git a/numpy/core/src/common/npy_hashtable.h b/numpy/core/src/common/npy_hashtable.h new file mode 100644 index 000000000000..a0bf81967d75 --- /dev/null +++ b/numpy/core/src/common/npy_hashtable.h @@ -0,0 +1,32 @@ +#ifndef NUMPY_CORE_SRC_COMMON_NPY_NPY_HASHTABLE_H_ +#define NUMPY_CORE_SRC_COMMON_NPY_NPY_HASHTABLE_H_ + +#include <Python.h> + +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#include "numpy/ndarraytypes.h" + + +typedef struct { + int key_len; /* number of identities used */ + /* Buckets stores: val1, key1[0], key1[1], ..., val2, key2[0], ... */ + PyObject **buckets; + npy_intp size; /* current size */ + npy_intp nelem; /* number of elements */ +} PyArrayIdentityHash; + + +NPY_NO_EXPORT int +PyArrayIdentityHash_SetItem(PyArrayIdentityHash *tb, + PyObject *const *key, PyObject *value, int replace); + +NPY_NO_EXPORT PyObject * +PyArrayIdentityHash_GetItem(PyArrayIdentityHash const *tb, PyObject *const *key); + +NPY_NO_EXPORT PyArrayIdentityHash * +PyArrayIdentityHash_New(int key_len); + +NPY_NO_EXPORT void +PyArrayIdentityHash_Dealloc(PyArrayIdentityHash *tb); + +#endif /* NUMPY_CORE_SRC_COMMON_NPY_NPY_HASHTABLE_H_ */ diff --git a/numpy/core/src/private/npy_import.h b/numpy/core/src/common/npy_import.h similarity index 83% rename from numpy/core/src/private/npy_import.h rename to numpy/core/src/common/npy_import.h index 221e1e645a47..f36b6924a864 100644 --- a/numpy/core/src/private/npy_import.h +++ b/numpy/core/src/common/npy_import.h @@ -1,5 +1,5 @@ -#ifndef NPY_IMPORT_H -#define NPY_IMPORT_H +#ifndef NUMPY_CORE_SRC_COMMON_NPY_IMPORT_H_ +#define NUMPY_CORE_SRC_COMMON_NPY_IMPORT_H_ #include <Python.h> @@ -19,7 +19,7 @@ NPY_INLINE static void npy_cache_import(const char *module, const char *attr, PyObject **cache) { - if (*cache == NULL) { + if (NPY_UNLIKELY(*cache == NULL)) { PyObject *mod = PyImport_ImportModule(module); if (mod != NULL) { @@ -29,4 +29,4 @@ npy_cache_import(const char *module, const char *attr, PyObject **cache) } } -#endif +#endif /* NUMPY_CORE_SRC_COMMON_NPY_IMPORT_H_ */ diff --git a/numpy/core/src/common/npy_longdouble.c b/numpy/core/src/common/npy_longdouble.c new file mode 100644 index 000000000000..38dfd325c685 --- /dev/null +++ b/numpy/core/src/common/npy_longdouble.c @@ -0,0 +1,175 @@ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE + +#define PY_SSIZE_T_CLEAN +#include <Python.h> + +#include "numpy/ndarraytypes.h" +#include "numpy/npy_math.h" +#include "npy_pycompat.h" +#include "numpyos.h" + +/* + * Heavily derived from PyLong_FromDouble + * Notably, we can't set the digits directly, so have to shift and or instead. + */ +NPY_VISIBILITY_HIDDEN PyObject * +npy_longdouble_to_PyLong(npy_longdouble ldval) +{ + PyObject *v; + PyObject *l_chunk_size; + /* + * number of bits to extract at a time. CPython uses 30, but that's because + * it's tied to the internal long representation + */ + const int chunk_size = NPY_BITSOF_LONGLONG; + npy_longdouble frac; + int i, ndig, expo, neg; + neg = 0; + + if (npy_isinf(ldval)) { + PyErr_SetString(PyExc_OverflowError, + "cannot convert longdouble infinity to integer"); + return NULL; + } + if (npy_isnan(ldval)) { + PyErr_SetString(PyExc_ValueError, + "cannot convert longdouble NaN to integer"); + return NULL; + } + if (ldval < 0.0) { + neg = 1; + ldval = -ldval; + } + frac = npy_frexpl(ldval, &expo); /* ldval = frac*2**expo; 0.0 <= frac < 1.0 */ + v = PyLong_FromLong(0L); + if (v == NULL) + return NULL; + if (expo <= 0) + return v; + + ndig = (expo-1) / chunk_size + 1; + + l_chunk_size = PyLong_FromLong(chunk_size); + if (l_chunk_size == NULL) { + Py_DECREF(v); + return NULL; + } + + /* Get the MSBs of the integral part of the float */ + frac = npy_ldexpl(frac, (expo-1) % chunk_size + 1); + for (i = ndig; --i >= 0; ) { + npy_ulonglong chunk = (npy_ulonglong)frac; + PyObject *l_chunk; + /* v = v << chunk_size */ + Py_SETREF(v, PyNumber_Lshift(v, l_chunk_size)); + if (v == NULL) { + goto done; + } + l_chunk = PyLong_FromUnsignedLongLong(chunk); + if (l_chunk == NULL) { + Py_DECREF(v); + v = NULL; + goto done; + } + /* v = v | chunk */ + Py_SETREF(v, PyNumber_Or(v, l_chunk)); + Py_DECREF(l_chunk); + if (v == NULL) { + goto done; + } + + /* Remove the msbs, and repeat */ + frac = frac - (npy_longdouble) chunk; + frac = npy_ldexpl(frac, chunk_size); + } + + /* v = -v */ + if (neg) { + Py_SETREF(v, PyNumber_Negative(v)); + if (v == NULL) { + goto done; + } + } + +done: + Py_DECREF(l_chunk_size); + return v; +} + +/* Helper function to get unicode(PyLong).encode('utf8') */ +static PyObject * +_PyLong_Bytes(PyObject *long_obj) { + PyObject *bytes; + PyObject *unicode = PyObject_Str(long_obj); + if (unicode == NULL) { + return NULL; + } + bytes = PyUnicode_AsUTF8String(unicode); + Py_DECREF(unicode); + return bytes; +} + + +/** + * TODO: currently a hack that converts the long through a string. This is + * correct, but slow. + * + * Another approach would be to do this numerically, in a similar way to + * PyLong_AsDouble. + * However, in order to respect rounding modes correctly, this needs to know + * the size of the mantissa, which is platform-dependent. + */ +NPY_VISIBILITY_HIDDEN npy_longdouble +npy_longdouble_from_PyLong(PyObject *long_obj) { + npy_longdouble result = 1234; + char *end; + char *cstr; + PyObject *bytes; + + /* convert the long to a string */ + bytes = _PyLong_Bytes(long_obj); + if (bytes == NULL) { + return -1; + } + + cstr = PyBytes_AsString(bytes); + if (cstr == NULL) { + goto fail; + } + end = NULL; + + /* convert the string to a long double and capture errors */ + errno = 0; + result = NumPyOS_ascii_strtold(cstr, &end); + if (errno == ERANGE) { + /* strtold returns INFINITY of the correct sign. */ + if (PyErr_Warn(PyExc_RuntimeWarning, + "overflow encountered in conversion from python long") < 0) { + goto fail; + } + } + else if (errno) { + PyErr_Format(PyExc_RuntimeError, + "Could not parse python long as longdouble: %s (%s)", + cstr, + strerror(errno)); + goto fail; + } + + /* Extra characters at the end of the string, or nothing parsed */ + if (end == cstr || *end != '\0') { + PyErr_Format(PyExc_RuntimeError, + "Could not parse long as longdouble: %s", + cstr); + goto fail; + } + + /* finally safe to decref now that we're done with `end` */ + Py_DECREF(bytes); + return result; + +fail: + Py_DECREF(bytes); + return -1; +} diff --git a/numpy/core/src/common/npy_longdouble.h b/numpy/core/src/common/npy_longdouble.h new file mode 100644 index 000000000000..cf8b37bc9f25 --- /dev/null +++ b/numpy/core/src/common/npy_longdouble.h @@ -0,0 +1,27 @@ +#ifndef NUMPY_CORE_SRC_COMMON_NPY_LONGDOUBLE_H_ +#define NUMPY_CORE_SRC_COMMON_NPY_LONGDOUBLE_H_ + +#include "npy_config.h" +#include "numpy/ndarraytypes.h" + +/* Convert a npy_longdouble to a python `long` integer. + * + * Results are rounded towards zero. + * + * This performs the same task as PyLong_FromDouble, but for long doubles + * which have a greater range. + */ +NPY_VISIBILITY_HIDDEN PyObject * +npy_longdouble_to_PyLong(npy_longdouble ldval); + +/* Convert a python `long` integer to a npy_longdouble + * + * This performs the same task as PyLong_AsDouble, but for long doubles + * which have a greater range. + * + * Returns -1 if an error occurs. + */ +NPY_VISIBILITY_HIDDEN npy_longdouble +npy_longdouble_from_PyLong(PyObject *long_obj); + +#endif /* NUMPY_CORE_SRC_COMMON_NPY_LONGDOUBLE_H_ */ diff --git a/numpy/core/src/private/npy_partition.h.src b/numpy/core/src/common/npy_partition.h.src similarity index 94% rename from numpy/core/src/private/npy_partition.h.src rename to numpy/core/src/common/npy_partition.h.src index a22cf911c789..72c2095f11cc 100644 --- a/numpy/core/src/private/npy_partition.h.src +++ b/numpy/core/src/common/npy_partition.h.src @@ -42,12 +42,12 @@ * npy_cdouble, npy_clongdouble# */ -NPY_VISIBILITY_HIDDEN int introselect_@suff@(@type@ *v, npy_intp num, +NPY_NO_EXPORT int introselect_@suff@(@type@ *v, npy_intp num, npy_intp kth, npy_intp * pivots, npy_intp * npiv, void *NOT_USED); -NPY_VISIBILITY_HIDDEN int aintroselect_@suff@(@type@ *v, npy_intp* tosort, npy_intp num, +NPY_NO_EXPORT int aintroselect_@suff@(@type@ *v, npy_intp* tosort, npy_intp num, npy_intp kth, npy_intp * pivots, npy_intp * npiv, @@ -113,9 +113,6 @@ get_argpartition_func(int type, NPY_SELECTKIND which) npy_intp i; npy_intp ntypes = ARRAY_SIZE(_part_map); - if (which >= NPY_NSELECTS) { - return NULL; - } for (i = 0; i < ntypes; i++) { if (type == _part_map[i].typenum) { return _part_map[i].argpart[which]; diff --git a/numpy/core/src/common/npy_pycompat.h b/numpy/core/src/common/npy_pycompat.h new file mode 100644 index 000000000000..6641cd59109f --- /dev/null +++ b/numpy/core/src/common/npy_pycompat.h @@ -0,0 +1,22 @@ +#ifndef NUMPY_CORE_SRC_COMMON_NPY_PYCOMPAT_H_ +#define NUMPY_CORE_SRC_COMMON_NPY_PYCOMPAT_H_ + +#include "numpy/npy_3kcompat.h" + + +/* + * In Python 3.10a7 (or b1), python started using the identity for the hash + * when a value is NaN. See https://bugs.python.org/issue43475 + */ +#if PY_VERSION_HEX > 0x030a00a6 +#define Npy_HashDouble _Py_HashDouble +#else +static NPY_INLINE Py_hash_t +Npy_HashDouble(PyObject *NPY_UNUSED(identity), double val) +{ + return _Py_HashDouble(val); +} +#endif + + +#endif /* NUMPY_CORE_SRC_COMMON_NPY_PYCOMPAT_H_ */ diff --git a/numpy/core/src/common/npy_sort.h.src b/numpy/core/src/common/npy_sort.h.src new file mode 100644 index 000000000000..b4a1e9b0cad9 --- /dev/null +++ b/numpy/core/src/common/npy_sort.h.src @@ -0,0 +1,105 @@ +#ifndef __NPY_SORT_H__ +#define __NPY_SORT_H__ + +/* Python include is for future object sorts */ +#include <Python.h> +#include <numpy/npy_common.h> +#include <numpy/ndarraytypes.h> + +#define NPY_ENOMEM 1 +#define NPY_ECOMP 2 + +static NPY_INLINE int npy_get_msb(npy_uintp unum) +{ + int depth_limit = 0; + while (unum >>= 1) { + depth_limit++; + } + return depth_limit; +} + + +/* + ***************************************************************************** + ** NUMERIC SORTS ** + ***************************************************************************** + */ + + +/**begin repeat + * + * #suff = bool, byte, ubyte, short, ushort, int, uint, long, ulong, + * longlong, ulonglong, half, float, double, longdouble, + * cfloat, cdouble, clongdouble, datetime, timedelta# + */ + +NPY_NO_EXPORT int quicksort_@suff@(void *vec, npy_intp cnt, void *null); +NPY_NO_EXPORT int heapsort_@suff@(void *vec, npy_intp cnt, void *null); +NPY_NO_EXPORT int mergesort_@suff@(void *vec, npy_intp cnt, void *null); +NPY_NO_EXPORT int timsort_@suff@(void *vec, npy_intp cnt, void *null); +NPY_NO_EXPORT int aquicksort_@suff@(void *vec, npy_intp *ind, npy_intp cnt, void *null); +NPY_NO_EXPORT int aheapsort_@suff@(void *vec, npy_intp *ind, npy_intp cnt, void *null); +NPY_NO_EXPORT int amergesort_@suff@(void *vec, npy_intp *ind, npy_intp cnt, void *null); +NPY_NO_EXPORT int atimsort_@suff@(void *vec, npy_intp *ind, npy_intp cnt, void *null); + +/**end repeat**/ + +/**begin repeat + * + * #suff = bool, byte, ubyte, short, ushort, int, uint, long, ulong, + * longlong, ulonglong# + */ +#ifdef __cplusplus +extern "C" { +#endif +NPY_NO_EXPORT int radixsort_@suff@(void *vec, npy_intp cnt, void *null); +NPY_NO_EXPORT int aradixsort_@suff@(void *vec, npy_intp *ind, npy_intp cnt, void *null); +#ifdef __cplusplus +} +#endif + +/**end repeat**/ + + + +/* + ***************************************************************************** + ** STRING SORTS ** + ***************************************************************************** + */ + + +/**begin repeat + * + * #suff = string, unicode# + */ + +NPY_NO_EXPORT int quicksort_@suff@(void *vec, npy_intp cnt, void *arr); +NPY_NO_EXPORT int heapsort_@suff@(void *vec, npy_intp cnt, void *arr); +NPY_NO_EXPORT int mergesort_@suff@(void *vec, npy_intp cnt, void *arr); +NPY_NO_EXPORT int timsort_@suff@(void *vec, npy_intp cnt, void *arr); +NPY_NO_EXPORT int aquicksort_@suff@(void *vec, npy_intp *ind, npy_intp cnt, void *arr); +NPY_NO_EXPORT int aheapsort_@suff@(void *vec, npy_intp *ind, npy_intp cnt, void *arr); +NPY_NO_EXPORT int amergesort_@suff@(void *vec, npy_intp *ind, npy_intp cnt, void *arr); +NPY_NO_EXPORT int atimsort_@suff@(void *vec, npy_intp *ind, npy_intp cnt, void *arr); + +/**end repeat**/ + + +/* + ***************************************************************************** + ** GENERIC SORT ** + ***************************************************************************** + */ + + +NPY_NO_EXPORT int npy_quicksort(void *vec, npy_intp cnt, void *arr); +NPY_NO_EXPORT int npy_heapsort(void *vec, npy_intp cnt, void *arr); +NPY_NO_EXPORT int npy_mergesort(void *vec, npy_intp cnt, void *arr); +NPY_NO_EXPORT int npy_timsort(void *vec, npy_intp cnt, void *arr); +NPY_NO_EXPORT int npy_aquicksort(void *vec, npy_intp *ind, npy_intp cnt, void *arr); +NPY_NO_EXPORT int npy_aheapsort(void *vec, npy_intp *ind, npy_intp cnt, void *arr); +NPY_NO_EXPORT int npy_amergesort(void *vec, npy_intp *ind, npy_intp cnt, void *arr); +NPY_NO_EXPORT int npy_atimsort(void *vec, npy_intp *ind, npy_intp cnt, void *arr); + +#endif diff --git a/numpy/core/src/common/npy_svml.h b/numpy/core/src/common/npy_svml.h new file mode 100644 index 000000000000..4292f7090333 --- /dev/null +++ b/numpy/core/src/common/npy_svml.h @@ -0,0 +1,41 @@ +#if NPY_SIMD && defined(NPY_HAVE_AVX512_SKX) && defined(NPY_CAN_LINK_SVML) +extern __m512 __svml_exp2f16(__m512 x); +extern __m512 __svml_log2f16(__m512 x); +extern __m512 __svml_log10f16(__m512 x); +extern __m512 __svml_expm1f16(__m512 x); +extern __m512 __svml_log1pf16(__m512 x); +extern __m512 __svml_cbrtf16(__m512 x); +extern __m512 __svml_sinf16(__m512 x); +extern __m512 __svml_cosf16(__m512 x); +extern __m512 __svml_tanf16(__m512 x); +extern __m512 __svml_asinf16(__m512 x); +extern __m512 __svml_acosf16(__m512 x); +extern __m512 __svml_atanf16(__m512 x); +extern __m512 __svml_atan2f16(__m512 x); +extern __m512 __svml_sinhf16(__m512 x); +extern __m512 __svml_coshf16(__m512 x); +extern __m512 __svml_tanhf16(__m512 x); +extern __m512 __svml_asinhf16(__m512 x); +extern __m512 __svml_acoshf16(__m512 x); +extern __m512 __svml_atanhf16(__m512 x); + +extern __m512d __svml_exp28(__m512d x); +extern __m512d __svml_log28(__m512d x); +extern __m512d __svml_log108(__m512d x); +extern __m512d __svml_expm18(__m512d x); +extern __m512d __svml_log1p8(__m512d x); +extern __m512d __svml_cbrt8(__m512d x); +extern __m512d __svml_sin8(__m512d x); +extern __m512d __svml_cos8(__m512d x); +extern __m512d __svml_tan8(__m512d x); +extern __m512d __svml_asin8(__m512d x); +extern __m512d __svml_acos8(__m512d x); +extern __m512d __svml_atan8(__m512d x); +extern __m512d __svml_atan28(__m512d x); +extern __m512d __svml_sinh8(__m512d x); +extern __m512d __svml_cosh8(__m512d x); +extern __m512d __svml_tanh8(__m512d x); +extern __m512d __svml_asinh8(__m512d x); +extern __m512d __svml_acosh8(__m512d x); +extern __m512d __svml_atanh8(__m512d x); +#endif diff --git a/numpy/core/src/common/numpy_tag.h b/numpy/core/src/common/numpy_tag.h new file mode 100644 index 000000000000..dc8d5286b07d --- /dev/null +++ b/numpy/core/src/common/numpy_tag.h @@ -0,0 +1,78 @@ +#ifndef _NPY_COMMON_TAG_H_ +#define _NPY_COMMON_TAG_H_ + +namespace npy { + +struct integral_tag { +}; +struct floating_point_tag { +}; +struct complex_tag { +}; +struct date_tag { +}; + +struct bool_tag : integral_tag { + using type = npy_bool; +}; +struct byte_tag : integral_tag { + using type = npy_byte; +}; +struct ubyte_tag : integral_tag { + using type = npy_ubyte; +}; +struct short_tag : integral_tag { + using type = npy_short; +}; +struct ushort_tag : integral_tag { + using type = npy_ushort; +}; +struct int_tag : integral_tag { + using type = npy_int; +}; +struct uint_tag : integral_tag { + using type = npy_uint; +}; +struct long_tag : integral_tag { + using type = npy_long; +}; +struct ulong_tag : integral_tag { + using type = npy_ulong; +}; +struct longlong_tag : integral_tag { + using type = npy_longlong; +}; +struct ulonglong_tag : integral_tag { + using type = npy_ulonglong; +}; +struct half_tag { + using type = npy_half; +}; +struct float_tag : floating_point_tag { + using type = npy_float; +}; +struct double_tag : floating_point_tag { + using type = npy_double; +}; +struct longdouble_tag : floating_point_tag { + using type = npy_longdouble; +}; +struct cfloat_tag : complex_tag { + using type = npy_cfloat; +}; +struct cdouble_tag : complex_tag { + using type = npy_cdouble; +}; +struct clongdouble_tag : complex_tag { + using type = npy_clongdouble; +}; +struct datetime_tag : date_tag { + using type = npy_datetime; +}; +struct timedelta_tag : date_tag { + using type = npy_timedelta; +}; + +} // namespace npy + +#endif diff --git a/numpy/core/src/multiarray/numpyos.c b/numpy/core/src/common/numpyos.c similarity index 96% rename from numpy/core/src/multiarray/numpyos.c rename to numpy/core/src/common/numpyos.c index 52dcbf3c8c90..4551a06a2ba1 100644 --- a/numpy/core/src/multiarray/numpyos.c +++ b/numpy/core/src/common/numpyos.c @@ -1,11 +1,9 @@ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE + #define PY_SSIZE_T_CLEAN #include <Python.h> -#include <locale.h> -#include <stdio.h> - -#define NPY_NO_DEPRECATED_API NPY_API_VERSION -#define _MULTIARRAYMODULE #include "numpy/arrayobject.h" #include "numpy/npy_math.h" @@ -13,14 +11,13 @@ #include "npy_pycompat.h" +#include <locale.h> +#include <stdio.h> + #ifdef HAVE_STRTOLD_L #include <stdlib.h> #ifdef HAVE_XLOCALE_H - /* - * the defines from xlocale.h are included in locale.h on some systems; - * see gh-8367 - */ - #include <xlocale.h> +#include <xlocale.h> // xlocale was removed in glibc 2.26, see gh-8367 #endif #endif @@ -248,7 +245,7 @@ check_ascii_format(const char *format) * Fix the generated string: make sure the decimal is ., that exponent has a * minimal number of digits, and that it has a decimal + one digit after that * decimal if decimal argument != 0 (Same effect that 'Z' format in - * PyOS_ascii_formatd + * PyOS_ascii_formatd) */ static char* fix_ascii_format(char* buf, size_t buflen, int decimal) @@ -283,7 +280,7 @@ fix_ascii_format(char* buf, size_t buflen, int decimal) * converting. * - value: The value to convert * - decimal: if != 0, always has a decimal, and at leasat one digit after - * the decimal. This has the same effect as passing 'Z' in the origianl + * the decimal. This has the same effect as passing 'Z' in the original * PyOS_ascii_formatd * * This is similar to PyOS_ascii_formatd in python > 2.6, except that it does @@ -769,3 +766,31 @@ NumPyOS_ascii_ftoLf(FILE *fp, long double *value) } return r; } + +NPY_NO_EXPORT npy_longlong +NumPyOS_strtoll(const char *str, char **endptr, int base) +{ +#if defined HAVE_STRTOLL + return strtoll(str, endptr, base); +#elif defined _MSC_VER + return _strtoi64(str, endptr, base); +#else + /* ok on 64 bit posix */ + return PyOS_strtol(str, endptr, base); +#endif +} + +NPY_NO_EXPORT npy_ulonglong +NumPyOS_strtoull(const char *str, char **endptr, int base) +{ +#if defined HAVE_STRTOULL + return strtoull(str, endptr, base); +#elif defined _MSC_VER + return _strtoui64(str, endptr, base); +#else + /* ok on 64 bit posix */ + return PyOS_strtoul(str, endptr, base); +#endif +} + + diff --git a/numpy/core/src/multiarray/numpyos.h b/numpy/core/src/common/numpyos.h similarity index 66% rename from numpy/core/src/multiarray/numpyos.h rename to numpy/core/src/common/numpyos.h index 7ca795a6f564..ce49cbea7f6e 100644 --- a/numpy/core/src/multiarray/numpyos.h +++ b/numpy/core/src/common/numpyos.h @@ -1,5 +1,5 @@ -#ifndef _NPY_NUMPYOS_H_ -#define _NPY_NUMPYOS_H_ +#ifndef NUMPY_CORE_SRC_COMMON_NPY_NUMPYOS_H_ +#define NUMPY_CORE_SRC_COMMON_NPY_NUMPYOS_H_ NPY_NO_EXPORT char* NumPyOS_ascii_formatd(char *buffer, size_t buf_size, @@ -31,4 +31,12 @@ NumPyOS_ascii_ftoLf(FILE *fp, long double *value); NPY_NO_EXPORT int NumPyOS_ascii_isspace(int c); -#endif +/* Convert a string to an int in an arbitrary base */ +NPY_NO_EXPORT npy_longlong +NumPyOS_strtoll(const char *str, char **endptr, int base); + +/* Convert a string to an int in an arbitrary base */ +NPY_NO_EXPORT npy_ulonglong +NumPyOS_strtoull(const char *str, char **endptr, int base); + +#endif /* NUMPY_CORE_SRC_COMMON_NPY_NUMPYOS_H_ */ diff --git a/numpy/core/src/multiarray/python_xerbla.c b/numpy/core/src/common/python_xerbla.c similarity index 79% rename from numpy/core/src/multiarray/python_xerbla.c rename to numpy/core/src/common/python_xerbla.c index bdf0b9058f7e..37a41408be22 100644 --- a/numpy/core/src/multiarray/python_xerbla.c +++ b/numpy/core/src/common/python_xerbla.c @@ -1,10 +1,8 @@ -#include "Python.h" +#define PY_SSIZE_T_CLEAN +#include <Python.h> -/* - * From f2c.h, this should be safe unless fortran is set to use 64 - * bit integers. We don't seem to have any good way to detect that. - */ -typedef int integer; +#include "numpy/npy_common.h" +#include "npy_cblas.h" /* From the original manpage: @@ -23,7 +21,7 @@ typedef int integer; info: Number of the invalid parameter. */ -int xerbla_(char *srname, integer *info) +CBLAS_INT BLAS_FUNC(xerbla)(char *srname, CBLAS_INT *info) { static const char format[] = "On entry to %.*s" \ " parameter number %d had an illegal value"; @@ -41,7 +39,7 @@ int xerbla_(char *srname, integer *info) #ifdef WITH_THREAD save = PyGILState_Ensure(); #endif - PyOS_snprintf(buf, sizeof(buf), format, len, srname, *info); + PyOS_snprintf(buf, sizeof(buf), format, len, srname, (int)*info); PyErr_SetString(PyExc_ValueError, buf); #ifdef WITH_THREAD PyGILState_Release(save); diff --git a/numpy/core/src/common/simd/avx2/arithmetic.h b/numpy/core/src/common/simd/avx2/arithmetic.h new file mode 100644 index 000000000000..ad9688338772 --- /dev/null +++ b/numpy/core/src/common/simd/avx2/arithmetic.h @@ -0,0 +1,336 @@ +#ifndef NPY_SIMD + #error "Not a standalone header" +#endif + +#ifndef _NPY_SIMD_AVX2_ARITHMETIC_H +#define _NPY_SIMD_AVX2_ARITHMETIC_H + +#include "../sse/utils.h" +/*************************** + * Addition + ***************************/ +// non-saturated +#define npyv_add_u8 _mm256_add_epi8 +#define npyv_add_s8 _mm256_add_epi8 +#define npyv_add_u16 _mm256_add_epi16 +#define npyv_add_s16 _mm256_add_epi16 +#define npyv_add_u32 _mm256_add_epi32 +#define npyv_add_s32 _mm256_add_epi32 +#define npyv_add_u64 _mm256_add_epi64 +#define npyv_add_s64 _mm256_add_epi64 +#define npyv_add_f32 _mm256_add_ps +#define npyv_add_f64 _mm256_add_pd + +// saturated +#define npyv_adds_u8 _mm256_adds_epu8 +#define npyv_adds_s8 _mm256_adds_epi8 +#define npyv_adds_u16 _mm256_adds_epu16 +#define npyv_adds_s16 _mm256_adds_epi16 +// TODO: rest, after implement Packs intrins + +/*************************** + * Subtraction + ***************************/ +// non-saturated +#define npyv_sub_u8 _mm256_sub_epi8 +#define npyv_sub_s8 _mm256_sub_epi8 +#define npyv_sub_u16 _mm256_sub_epi16 +#define npyv_sub_s16 _mm256_sub_epi16 +#define npyv_sub_u32 _mm256_sub_epi32 +#define npyv_sub_s32 _mm256_sub_epi32 +#define npyv_sub_u64 _mm256_sub_epi64 +#define npyv_sub_s64 _mm256_sub_epi64 +#define npyv_sub_f32 _mm256_sub_ps +#define npyv_sub_f64 _mm256_sub_pd + +// saturated +#define npyv_subs_u8 _mm256_subs_epu8 +#define npyv_subs_s8 _mm256_subs_epi8 +#define npyv_subs_u16 _mm256_subs_epu16 +#define npyv_subs_s16 _mm256_subs_epi16 +// TODO: rest, after implement Packs intrins + +/*************************** + * Multiplication + ***************************/ +// non-saturated +#define npyv_mul_u8 npyv256_mul_u8 +#define npyv_mul_s8 npyv_mul_u8 +#define npyv_mul_u16 _mm256_mullo_epi16 +#define npyv_mul_s16 _mm256_mullo_epi16 +#define npyv_mul_u32 _mm256_mullo_epi32 +#define npyv_mul_s32 _mm256_mullo_epi32 +#define npyv_mul_f32 _mm256_mul_ps +#define npyv_mul_f64 _mm256_mul_pd + +// saturated +// TODO: after implement Packs intrins + +/*************************** + * Integer Division + ***************************/ +// See simd/intdiv.h for more clarification +// divide each unsigned 8-bit element by a precomputed divisor +NPY_FINLINE npyv_u8 npyv_divc_u8(npyv_u8 a, const npyv_u8x3 divisor) +{ + const __m256i bmask = _mm256_set1_epi32(0x00FF00FF); + const __m128i shf1 = _mm256_castsi256_si128(divisor.val[1]); + const __m128i shf2 = _mm256_castsi256_si128(divisor.val[2]); + const __m256i shf1b = _mm256_set1_epi8(0xFFU >> _mm_cvtsi128_si32(shf1)); + const __m256i shf2b = _mm256_set1_epi8(0xFFU >> _mm_cvtsi128_si32(shf2)); + // high part of unsigned multiplication + __m256i mulhi_even = _mm256_mullo_epi16(_mm256_and_si256(a, bmask), divisor.val[0]); + mulhi_even = _mm256_srli_epi16(mulhi_even, 8); + __m256i mulhi_odd = _mm256_mullo_epi16(_mm256_srli_epi16(a, 8), divisor.val[0]); + __m256i mulhi = _mm256_blendv_epi8(mulhi_odd, mulhi_even, bmask); + // floor(a/d) = (mulhi + ((a-mulhi) >> sh1)) >> sh2 + __m256i q = _mm256_sub_epi8(a, mulhi); + q = _mm256_and_si256(_mm256_srl_epi16(q, shf1), shf1b); + q = _mm256_add_epi8(mulhi, q); + q = _mm256_and_si256(_mm256_srl_epi16(q, shf2), shf2b); + return q; +} +// divide each signed 8-bit element by a precomputed divisor (round towards zero) +NPY_FINLINE npyv_s16 npyv_divc_s16(npyv_s16 a, const npyv_s16x3 divisor); +NPY_FINLINE npyv_s8 npyv_divc_s8(npyv_s8 a, const npyv_s8x3 divisor) +{ + const __m256i bmask = _mm256_set1_epi32(0x00FF00FF); + // instead of _mm256_cvtepi8_epi16/_mm256_packs_epi16 to wrap around overflow + __m256i divc_even = npyv_divc_s16(_mm256_srai_epi16(_mm256_slli_epi16(a, 8), 8), divisor); + __m256i divc_odd = npyv_divc_s16(_mm256_srai_epi16(a, 8), divisor); + divc_odd = _mm256_slli_epi16(divc_odd, 8); + return _mm256_blendv_epi8(divc_odd, divc_even, bmask); +} +// divide each unsigned 16-bit element by a precomputed divisor +NPY_FINLINE npyv_u16 npyv_divc_u16(npyv_u16 a, const npyv_u16x3 divisor) +{ + const __m128i shf1 = _mm256_castsi256_si128(divisor.val[1]); + const __m128i shf2 = _mm256_castsi256_si128(divisor.val[2]); + // high part of unsigned multiplication + __m256i mulhi = _mm256_mulhi_epu16(a, divisor.val[0]); + // floor(a/d) = (mulhi + ((a-mulhi) >> sh1)) >> sh2 + __m256i q = _mm256_sub_epi16(a, mulhi); + q = _mm256_srl_epi16(q, shf1); + q = _mm256_add_epi16(mulhi, q); + q = _mm256_srl_epi16(q, shf2); + return q; +} +// divide each signed 16-bit element by a precomputed divisor (round towards zero) +NPY_FINLINE npyv_s16 npyv_divc_s16(npyv_s16 a, const npyv_s16x3 divisor) +{ + const __m128i shf1 = _mm256_castsi256_si128(divisor.val[1]); + // high part of signed multiplication + __m256i mulhi = _mm256_mulhi_epi16(a, divisor.val[0]); + // q = ((a + mulhi) >> sh1) - XSIGN(a) + // trunc(a/d) = (q ^ dsign) - dsign + __m256i q = _mm256_sra_epi16(_mm256_add_epi16(a, mulhi), shf1); + q = _mm256_sub_epi16(q, _mm256_srai_epi16(a, 15)); + q = _mm256_sub_epi16(_mm256_xor_si256(q, divisor.val[2]), divisor.val[2]); + return q; +} +// divide each unsigned 32-bit element by a precomputed divisor +NPY_FINLINE npyv_u32 npyv_divc_u32(npyv_u32 a, const npyv_u32x3 divisor) +{ + const __m128i shf1 = _mm256_castsi256_si128(divisor.val[1]); + const __m128i shf2 = _mm256_castsi256_si128(divisor.val[2]); + // high part of unsigned multiplication + __m256i mulhi_even = _mm256_srli_epi64(_mm256_mul_epu32(a, divisor.val[0]), 32); + __m256i mulhi_odd = _mm256_mul_epu32(_mm256_srli_epi64(a, 32), divisor.val[0]); + __m256i mulhi = _mm256_blend_epi32(mulhi_even, mulhi_odd, 0xAA); + // floor(a/d) = (mulhi + ((a-mulhi) >> sh1)) >> sh2 + __m256i q = _mm256_sub_epi32(a, mulhi); + q = _mm256_srl_epi32(q, shf1); + q = _mm256_add_epi32(mulhi, q); + q = _mm256_srl_epi32(q, shf2); + return q; +} +// divide each signed 32-bit element by a precomputed divisor (round towards zero) +NPY_FINLINE npyv_s32 npyv_divc_s32(npyv_s32 a, const npyv_s32x3 divisor) +{ + const __m128i shf1 = _mm256_castsi256_si128(divisor.val[1]); + // high part of signed multiplication + __m256i mulhi_even = _mm256_srli_epi64(_mm256_mul_epi32(a, divisor.val[0]), 32); + __m256i mulhi_odd = _mm256_mul_epi32(_mm256_srli_epi64(a, 32), divisor.val[0]); + __m256i mulhi = _mm256_blend_epi32(mulhi_even, mulhi_odd, 0xAA); + // q = ((a + mulhi) >> sh1) - XSIGN(a) + // trunc(a/d) = (q ^ dsign) - dsign + __m256i q = _mm256_sra_epi32(_mm256_add_epi32(a, mulhi), shf1); + q = _mm256_sub_epi32(q, _mm256_srai_epi32(a, 31)); + q = _mm256_sub_epi32(_mm256_xor_si256(q, divisor.val[2]), divisor.val[2]); + return q; +} +// returns the high 64 bits of unsigned 64-bit multiplication +// xref https://stackoverflow.com/a/28827013 +NPY_FINLINE npyv_u64 npyv__mullhi_u64(npyv_u64 a, npyv_u64 b) +{ + __m256i lomask = npyv_setall_s64(0xffffffff); + __m256i a_hi = _mm256_srli_epi64(a, 32); // a0l, a0h, a1l, a1h + __m256i b_hi = _mm256_srli_epi64(b, 32); // b0l, b0h, b1l, b1h + // compute partial products + __m256i w0 = _mm256_mul_epu32(a, b); // a0l*b0l, a1l*b1l + __m256i w1 = _mm256_mul_epu32(a, b_hi); // a0l*b0h, a1l*b1h + __m256i w2 = _mm256_mul_epu32(a_hi, b); // a0h*b0l, a1h*b0l + __m256i w3 = _mm256_mul_epu32(a_hi, b_hi); // a0h*b0h, a1h*b1h + // sum partial products + __m256i w0h = _mm256_srli_epi64(w0, 32); + __m256i s1 = _mm256_add_epi64(w1, w0h); + __m256i s1l = _mm256_and_si256(s1, lomask); + __m256i s1h = _mm256_srli_epi64(s1, 32); + + __m256i s2 = _mm256_add_epi64(w2, s1l); + __m256i s2h = _mm256_srli_epi64(s2, 32); + + __m256i hi = _mm256_add_epi64(w3, s1h); + hi = _mm256_add_epi64(hi, s2h); + return hi; +} +// divide each unsigned 64-bit element by a divisor +NPY_FINLINE npyv_u64 npyv_divc_u64(npyv_u64 a, const npyv_u64x3 divisor) +{ + const __m128i shf1 = _mm256_castsi256_si128(divisor.val[1]); + const __m128i shf2 = _mm256_castsi256_si128(divisor.val[2]); + // high part of unsigned multiplication + __m256i mulhi = npyv__mullhi_u64(a, divisor.val[0]); + // floor(a/d) = (mulhi + ((a-mulhi) >> sh1)) >> sh2 + __m256i q = _mm256_sub_epi64(a, mulhi); + q = _mm256_srl_epi64(q, shf1); + q = _mm256_add_epi64(mulhi, q); + q = _mm256_srl_epi64(q, shf2); + return q; +} +// divide each unsigned 64-bit element by a divisor (round towards zero) +NPY_FINLINE npyv_s64 npyv_divc_s64(npyv_s64 a, const npyv_s64x3 divisor) +{ + const __m128i shf1 = _mm256_castsi256_si128(divisor.val[1]); + // high part of unsigned multiplication + __m256i mulhi = npyv__mullhi_u64(a, divisor.val[0]); + // convert unsigned to signed high multiplication + // mulhi - ((a < 0) ? m : 0) - ((m < 0) ? a : 0); + __m256i asign = _mm256_cmpgt_epi64(_mm256_setzero_si256(), a); + __m256i msign = _mm256_cmpgt_epi64(_mm256_setzero_si256(), divisor.val[0]); + __m256i m_asign = _mm256_and_si256(divisor.val[0], asign); + __m256i a_msign = _mm256_and_si256(a, msign); + mulhi = _mm256_sub_epi64(mulhi, m_asign); + mulhi = _mm256_sub_epi64(mulhi, a_msign); + // q = (a + mulhi) >> sh + __m256i q = _mm256_add_epi64(a, mulhi); + // emulate arithmetic right shift + const __m256i sigb = npyv_setall_s64(1LL << 63); + q = _mm256_srl_epi64(_mm256_add_epi64(q, sigb), shf1); + q = _mm256_sub_epi64(q, _mm256_srl_epi64(sigb, shf1)); + // q = q - XSIGN(a) + // trunc(a/d) = (q ^ dsign) - dsign + q = _mm256_sub_epi64(q, asign); + q = _mm256_sub_epi64(_mm256_xor_si256(q, divisor.val[2]), divisor.val[2]); + return q; +} +/*************************** + * Division + ***************************/ +// TODO: emulate integer division +#define npyv_div_f32 _mm256_div_ps +#define npyv_div_f64 _mm256_div_pd + +/*************************** + * FUSED + ***************************/ +#ifdef NPY_HAVE_FMA3 + // multiply and add, a*b + c + #define npyv_muladd_f32 _mm256_fmadd_ps + #define npyv_muladd_f64 _mm256_fmadd_pd + // multiply and subtract, a*b - c + #define npyv_mulsub_f32 _mm256_fmsub_ps + #define npyv_mulsub_f64 _mm256_fmsub_pd + // negate multiply and add, -(a*b) + c + #define npyv_nmuladd_f32 _mm256_fnmadd_ps + #define npyv_nmuladd_f64 _mm256_fnmadd_pd + // negate multiply and subtract, -(a*b) - c + #define npyv_nmulsub_f32 _mm256_fnmsub_ps + #define npyv_nmulsub_f64 _mm256_fnmsub_pd +#else + // multiply and add, a*b + c + NPY_FINLINE npyv_f32 npyv_muladd_f32(npyv_f32 a, npyv_f32 b, npyv_f32 c) + { return npyv_add_f32(npyv_mul_f32(a, b), c); } + NPY_FINLINE npyv_f64 npyv_muladd_f64(npyv_f64 a, npyv_f64 b, npyv_f64 c) + { return npyv_add_f64(npyv_mul_f64(a, b), c); } + // multiply and subtract, a*b - c + NPY_FINLINE npyv_f32 npyv_mulsub_f32(npyv_f32 a, npyv_f32 b, npyv_f32 c) + { return npyv_sub_f32(npyv_mul_f32(a, b), c); } + NPY_FINLINE npyv_f64 npyv_mulsub_f64(npyv_f64 a, npyv_f64 b, npyv_f64 c) + { return npyv_sub_f64(npyv_mul_f64(a, b), c); } + // negate multiply and add, -(a*b) + c + NPY_FINLINE npyv_f32 npyv_nmuladd_f32(npyv_f32 a, npyv_f32 b, npyv_f32 c) + { return npyv_sub_f32(c, npyv_mul_f32(a, b)); } + NPY_FINLINE npyv_f64 npyv_nmuladd_f64(npyv_f64 a, npyv_f64 b, npyv_f64 c) + { return npyv_sub_f64(c, npyv_mul_f64(a, b)); } + // negate multiply and subtract, -(a*b) - c + NPY_FINLINE npyv_f32 npyv_nmulsub_f32(npyv_f32 a, npyv_f32 b, npyv_f32 c) + { + npyv_f32 neg_a = npyv_xor_f32(a, npyv_setall_f32(-0.0f)); + return npyv_sub_f32(npyv_mul_f32(neg_a, b), c); + } + NPY_FINLINE npyv_f64 npyv_nmulsub_f64(npyv_f64 a, npyv_f64 b, npyv_f64 c) + { + npyv_f64 neg_a = npyv_xor_f64(a, npyv_setall_f64(-0.0)); + return npyv_sub_f64(npyv_mul_f64(neg_a, b), c); + } +#endif // !NPY_HAVE_FMA3 + +/*************************** + * Summation + ***************************/ +// reduce sum across vector +NPY_FINLINE npy_uint32 npyv_sum_u32(npyv_u32 a) +{ + __m256i s0 = _mm256_hadd_epi32(a, a); + s0 = _mm256_hadd_epi32(s0, s0); + __m128i s1 = _mm256_extracti128_si256(s0, 1); + s1 = _mm_add_epi32(_mm256_castsi256_si128(s0), s1); + return _mm_cvtsi128_si32(s1); +} + +NPY_FINLINE npy_uint64 npyv_sum_u64(npyv_u64 a) +{ + __m256i two = _mm256_add_epi64(a, _mm256_shuffle_epi32(a, _MM_SHUFFLE(1, 0, 3, 2))); + __m128i one = _mm_add_epi64(_mm256_castsi256_si128(two), _mm256_extracti128_si256(two, 1)); + return (npy_uint64)npyv128_cvtsi128_si64(one); +} + +NPY_FINLINE float npyv_sum_f32(npyv_f32 a) +{ + __m256 sum_halves = _mm256_hadd_ps(a, a); + sum_halves = _mm256_hadd_ps(sum_halves, sum_halves); + __m128 lo = _mm256_castps256_ps128(sum_halves); + __m128 hi = _mm256_extractf128_ps(sum_halves, 1); + __m128 sum = _mm_add_ps(lo, hi); + return _mm_cvtss_f32(sum); +} + +NPY_FINLINE double npyv_sum_f64(npyv_f64 a) +{ + __m256d sum_halves = _mm256_hadd_pd(a, a); + __m128d lo = _mm256_castpd256_pd128(sum_halves); + __m128d hi = _mm256_extractf128_pd(sum_halves, 1); + __m128d sum = _mm_add_pd(lo, hi); + return _mm_cvtsd_f64(sum); +} + +// expand the source vector and performs sum reduce +NPY_FINLINE npy_uint16 npyv_sumup_u8(npyv_u8 a) +{ + __m256i four = _mm256_sad_epu8(a, _mm256_setzero_si256()); + __m128i two = _mm_add_epi16(_mm256_castsi256_si128(four), _mm256_extracti128_si256(four, 1)); + __m128i one = _mm_add_epi16(two, _mm_unpackhi_epi64(two, two)); + return (npy_uint16)_mm_cvtsi128_si32(one); +} + +NPY_FINLINE npy_uint32 npyv_sumup_u16(npyv_u16 a) +{ + const npyv_u16 even_mask = _mm256_set1_epi32(0x0000FFFF); + __m256i even = _mm256_and_si256(a, even_mask); + __m256i odd = _mm256_srli_epi32(a, 16); + __m256i eight = _mm256_add_epi32(even, odd); + return npyv_sum_u32(eight); +} + +#endif // _NPY_SIMD_AVX2_ARITHMETIC_H diff --git a/numpy/core/src/common/simd/avx2/avx2.h b/numpy/core/src/common/simd/avx2/avx2.h new file mode 100644 index 000000000000..02ff536fb15e --- /dev/null +++ b/numpy/core/src/common/simd/avx2/avx2.h @@ -0,0 +1,74 @@ +#ifndef _NPY_SIMD_H_ + #error "Not a standalone header" +#endif +#define NPY_SIMD 256 +#define NPY_SIMD_WIDTH 32 +#define NPY_SIMD_F64 1 +#ifdef NPY_HAVE_FMA3 + #define NPY_SIMD_FMA3 1 // native support +#else + #define NPY_SIMD_FMA3 0 // fast emulated +#endif +// Enough limit to allow us to use _mm256_i32gather_* +#define NPY_SIMD_MAXLOAD_STRIDE32 (0x7fffffff / 8) + +typedef __m256i npyv_u8; +typedef __m256i npyv_s8; +typedef __m256i npyv_u16; +typedef __m256i npyv_s16; +typedef __m256i npyv_u32; +typedef __m256i npyv_s32; +typedef __m256i npyv_u64; +typedef __m256i npyv_s64; +typedef __m256 npyv_f32; +typedef __m256d npyv_f64; + +typedef __m256i npyv_b8; +typedef __m256i npyv_b16; +typedef __m256i npyv_b32; +typedef __m256i npyv_b64; + +typedef struct { __m256i val[2]; } npyv_m256ix2; +typedef npyv_m256ix2 npyv_u8x2; +typedef npyv_m256ix2 npyv_s8x2; +typedef npyv_m256ix2 npyv_u16x2; +typedef npyv_m256ix2 npyv_s16x2; +typedef npyv_m256ix2 npyv_u32x2; +typedef npyv_m256ix2 npyv_s32x2; +typedef npyv_m256ix2 npyv_u64x2; +typedef npyv_m256ix2 npyv_s64x2; + +typedef struct { __m256i val[3]; } npyv_m256ix3; +typedef npyv_m256ix3 npyv_u8x3; +typedef npyv_m256ix3 npyv_s8x3; +typedef npyv_m256ix3 npyv_u16x3; +typedef npyv_m256ix3 npyv_s16x3; +typedef npyv_m256ix3 npyv_u32x3; +typedef npyv_m256ix3 npyv_s32x3; +typedef npyv_m256ix3 npyv_u64x3; +typedef npyv_m256ix3 npyv_s64x3; + +typedef struct { __m256 val[2]; } npyv_f32x2; +typedef struct { __m256d val[2]; } npyv_f64x2; +typedef struct { __m256 val[3]; } npyv_f32x3; +typedef struct { __m256d val[3]; } npyv_f64x3; + +#define npyv_nlanes_u8 32 +#define npyv_nlanes_s8 32 +#define npyv_nlanes_u16 16 +#define npyv_nlanes_s16 16 +#define npyv_nlanes_u32 8 +#define npyv_nlanes_s32 8 +#define npyv_nlanes_u64 4 +#define npyv_nlanes_s64 4 +#define npyv_nlanes_f32 8 +#define npyv_nlanes_f64 4 + +#include "utils.h" +#include "memory.h" +#include "misc.h" +#include "reorder.h" +#include "operators.h" +#include "conversion.h" +#include "arithmetic.h" +#include "math.h" diff --git a/numpy/core/src/common/simd/avx2/conversion.h b/numpy/core/src/common/simd/avx2/conversion.h new file mode 100644 index 000000000000..64e051686794 --- /dev/null +++ b/numpy/core/src/common/simd/avx2/conversion.h @@ -0,0 +1,69 @@ +#ifndef NPY_SIMD + #error "Not a standalone header" +#endif + +#ifndef _NPY_SIMD_AVX2_CVT_H +#define _NPY_SIMD_AVX2_CVT_H + +// convert mask types to integer types +#define npyv_cvt_u8_b8(A) A +#define npyv_cvt_s8_b8(A) A +#define npyv_cvt_u16_b16(A) A +#define npyv_cvt_s16_b16(A) A +#define npyv_cvt_u32_b32(A) A +#define npyv_cvt_s32_b32(A) A +#define npyv_cvt_u64_b64(A) A +#define npyv_cvt_s64_b64(A) A +#define npyv_cvt_f32_b32 _mm256_castsi256_ps +#define npyv_cvt_f64_b64 _mm256_castsi256_pd + +// convert integer types to mask types +#define npyv_cvt_b8_u8(BL) BL +#define npyv_cvt_b8_s8(BL) BL +#define npyv_cvt_b16_u16(BL) BL +#define npyv_cvt_b16_s16(BL) BL +#define npyv_cvt_b32_u32(BL) BL +#define npyv_cvt_b32_s32(BL) BL +#define npyv_cvt_b64_u64(BL) BL +#define npyv_cvt_b64_s64(BL) BL +#define npyv_cvt_b32_f32 _mm256_castps_si256 +#define npyv_cvt_b64_f64 _mm256_castpd_si256 + +// convert boolean vector to integer bitfield +NPY_FINLINE npy_uint64 npyv_tobits_b8(npyv_b8 a) +{ return (npy_uint32)_mm256_movemask_epi8(a); } + +NPY_FINLINE npy_uint64 npyv_tobits_b16(npyv_b16 a) +{ + __m128i pack = _mm_packs_epi16(_mm256_castsi256_si128(a), _mm256_extracti128_si256(a, 1)); + return (npy_uint16)_mm_movemask_epi8(pack); +} +NPY_FINLINE npy_uint64 npyv_tobits_b32(npyv_b32 a) +{ return (npy_uint8)_mm256_movemask_ps(_mm256_castsi256_ps(a)); } +NPY_FINLINE npy_uint64 npyv_tobits_b64(npyv_b64 a) +{ return (npy_uint8)_mm256_movemask_pd(_mm256_castsi256_pd(a)); } + +// expand +NPY_FINLINE npyv_u16x2 npyv_expand_u16_u8(npyv_u8 data) { + npyv_u16x2 r; + r.val[0] = _mm256_cvtepu8_epi16(_mm256_castsi256_si128(data)); + r.val[1] = _mm256_cvtepu8_epi16(_mm256_extracti128_si256(data, 1)); + return r; +} + +NPY_FINLINE npyv_u32x2 npyv_expand_u32_u16(npyv_u16 data) { + npyv_u32x2 r; + r.val[0] = _mm256_cvtepu16_epi32(_mm256_castsi256_si128(data)); + r.val[1] = _mm256_cvtepu16_epi32(_mm256_extracti128_si256(data, 1)); + return r; +} + +// round to nearest integer (assuming even) +#define npyv_round_s32_f32 _mm256_cvtps_epi32 +NPY_FINLINE npyv_s32 npyv_round_s32_f64(npyv_f64 a, npyv_f64 b) +{ + __m128i lo = _mm256_cvtpd_epi32(a), hi = _mm256_cvtpd_epi32(b); + return _mm256_inserti128_si256(_mm256_castsi128_si256(lo), hi, 1); +} + +#endif // _NPY_SIMD_AVX2_CVT_H diff --git a/numpy/core/src/common/simd/avx2/math.h b/numpy/core/src/common/simd/avx2/math.h new file mode 100644 index 000000000000..ec15e50e1fdb --- /dev/null +++ b/numpy/core/src/common/simd/avx2/math.h @@ -0,0 +1,116 @@ +#ifndef NPY_SIMD + #error "Not a standalone header" +#endif + +#ifndef _NPY_SIMD_AVX2_MATH_H +#define _NPY_SIMD_AVX2_MATH_H +/*************************** + * Elementary + ***************************/ +// Square root +#define npyv_sqrt_f32 _mm256_sqrt_ps +#define npyv_sqrt_f64 _mm256_sqrt_pd + +// Reciprocal +NPY_FINLINE npyv_f32 npyv_recip_f32(npyv_f32 a) +{ return _mm256_div_ps(_mm256_set1_ps(1.0f), a); } +NPY_FINLINE npyv_f64 npyv_recip_f64(npyv_f64 a) +{ return _mm256_div_pd(_mm256_set1_pd(1.0), a); } + +// Absolute +NPY_FINLINE npyv_f32 npyv_abs_f32(npyv_f32 a) +{ + return _mm256_and_ps( + a, _mm256_castsi256_ps(_mm256_set1_epi32(0x7fffffff)) + ); +} +NPY_FINLINE npyv_f64 npyv_abs_f64(npyv_f64 a) +{ + return _mm256_and_pd( + a, _mm256_castsi256_pd(npyv_setall_s64(0x7fffffffffffffffLL)) + ); +} + +// Square +NPY_FINLINE npyv_f32 npyv_square_f32(npyv_f32 a) +{ return _mm256_mul_ps(a, a); } +NPY_FINLINE npyv_f64 npyv_square_f64(npyv_f64 a) +{ return _mm256_mul_pd(a, a); } + +// Maximum, natively mapping with no guarantees to handle NaN. +#define npyv_max_f32 _mm256_max_ps +#define npyv_max_f64 _mm256_max_pd +// Maximum, supports IEEE floating-point arithmetic (IEC 60559), +// - If one of the two vectors contains NaN, the equivalent element of the other vector is set +// - Only if both corresponded elements are NaN, NaN is set. +NPY_FINLINE npyv_f32 npyv_maxp_f32(npyv_f32 a, npyv_f32 b) +{ + __m256 nn = _mm256_cmp_ps(b, b, _CMP_ORD_Q); + __m256 max = _mm256_max_ps(a, b); + return _mm256_blendv_ps(a, max, nn); +} +NPY_FINLINE npyv_f64 npyv_maxp_f64(npyv_f64 a, npyv_f64 b) +{ + __m256d nn = _mm256_cmp_pd(b, b, _CMP_ORD_Q); + __m256d max = _mm256_max_pd(a, b); + return _mm256_blendv_pd(a, max, nn); +} +// Maximum, integer operations +#define npyv_max_u8 _mm256_max_epu8 +#define npyv_max_s8 _mm256_max_epi8 +#define npyv_max_u16 _mm256_max_epu16 +#define npyv_max_s16 _mm256_max_epi16 +#define npyv_max_u32 _mm256_max_epu32 +#define npyv_max_s32 _mm256_max_epi32 +NPY_FINLINE npyv_u64 npyv_max_u64(npyv_u64 a, npyv_u64 b) +{ + return _mm256_blendv_epi8(b, a, npyv_cmpgt_u64(a, b)); +} +NPY_FINLINE npyv_s64 npyv_max_s64(npyv_s64 a, npyv_s64 b) +{ + return _mm256_blendv_epi8(b, a, _mm256_cmpgt_epi64(a, b)); +} + +// Minimum, natively mapping with no guarantees to handle NaN. +#define npyv_min_f32 _mm256_min_ps +#define npyv_min_f64 _mm256_min_pd +// Minimum, supports IEEE floating-point arithmetic (IEC 60559), +// - If one of the two vectors contains NaN, the equivalent element of the other vector is set +// - Only if both corresponded elements are NaN, NaN is set. +NPY_FINLINE npyv_f32 npyv_minp_f32(npyv_f32 a, npyv_f32 b) +{ + __m256 nn = _mm256_cmp_ps(b, b, _CMP_ORD_Q); + __m256 min = _mm256_min_ps(a, b); + return _mm256_blendv_ps(a, min, nn); +} +NPY_FINLINE npyv_f64 npyv_minp_f64(npyv_f64 a, npyv_f64 b) +{ + __m256d nn = _mm256_cmp_pd(b, b, _CMP_ORD_Q); + __m256d min = _mm256_min_pd(a, b); + return _mm256_blendv_pd(a, min, nn); +} +// Minimum, integer operations +#define npyv_min_u8 _mm256_min_epu8 +#define npyv_min_s8 _mm256_min_epi8 +#define npyv_min_u16 _mm256_min_epu16 +#define npyv_min_s16 _mm256_min_epi16 +#define npyv_min_u32 _mm256_min_epu32 +#define npyv_min_s32 _mm256_min_epi32 +NPY_FINLINE npyv_u64 npyv_min_u64(npyv_u64 a, npyv_u64 b) +{ + return _mm256_blendv_epi8(b, a, npyv_cmplt_u64(a, b)); +} +NPY_FINLINE npyv_s64 npyv_min_s64(npyv_s64 a, npyv_s64 b) +{ + return _mm256_blendv_epi8(a, b, _mm256_cmpgt_epi64(a, b)); +} + +// ceil +#define npyv_ceil_f32 _mm256_ceil_ps +#define npyv_ceil_f64 _mm256_ceil_pd + +// trunc +#define npyv_trunc_f32(A) _mm256_round_ps(A, _MM_FROUND_TO_ZERO) +#define npyv_trunc_f64(A) _mm256_round_pd(A, _MM_FROUND_TO_ZERO) + +#endif // _NPY_SIMD_AVX2_MATH_H diff --git a/numpy/core/src/common/simd/avx2/memory.h b/numpy/core/src/common/simd/avx2/memory.h new file mode 100644 index 000000000000..5891a270aa18 --- /dev/null +++ b/numpy/core/src/common/simd/avx2/memory.h @@ -0,0 +1,356 @@ +#ifndef NPY_SIMD + #error "Not a standalone header" +#endif + +#include "misc.h" + +#ifndef _NPY_SIMD_AVX2_MEMORY_H +#define _NPY_SIMD_AVX2_MEMORY_H + +/*************************** + * load/store + ***************************/ +#define NPYV_IMPL_AVX2_MEM_INT(CTYPE, SFX) \ + NPY_FINLINE npyv_##SFX npyv_load_##SFX(const CTYPE *ptr) \ + { return _mm256_loadu_si256((const __m256i*)ptr); } \ + NPY_FINLINE npyv_##SFX npyv_loada_##SFX(const CTYPE *ptr) \ + { return _mm256_load_si256((const __m256i*)ptr); } \ + NPY_FINLINE npyv_##SFX npyv_loads_##SFX(const CTYPE *ptr) \ + { return _mm256_stream_load_si256((const __m256i*)ptr); } \ + NPY_FINLINE npyv_##SFX npyv_loadl_##SFX(const CTYPE *ptr) \ + { return _mm256_castsi128_si256(_mm_loadu_si128((const __m128i*)ptr)); } \ + NPY_FINLINE void npyv_store_##SFX(CTYPE *ptr, npyv_##SFX vec) \ + { _mm256_storeu_si256((__m256i*)ptr, vec); } \ + NPY_FINLINE void npyv_storea_##SFX(CTYPE *ptr, npyv_##SFX vec) \ + { _mm256_store_si256((__m256i*)ptr, vec); } \ + NPY_FINLINE void npyv_stores_##SFX(CTYPE *ptr, npyv_##SFX vec) \ + { _mm256_stream_si256((__m256i*)ptr, vec); } \ + NPY_FINLINE void npyv_storel_##SFX(CTYPE *ptr, npyv_##SFX vec) \ + { _mm_storeu_si128((__m128i*)(ptr), _mm256_castsi256_si128(vec)); } \ + NPY_FINLINE void npyv_storeh_##SFX(CTYPE *ptr, npyv_##SFX vec) \ + { _mm_storeu_si128((__m128i*)(ptr), _mm256_extracti128_si256(vec, 1)); } + +NPYV_IMPL_AVX2_MEM_INT(npy_uint8, u8) +NPYV_IMPL_AVX2_MEM_INT(npy_int8, s8) +NPYV_IMPL_AVX2_MEM_INT(npy_uint16, u16) +NPYV_IMPL_AVX2_MEM_INT(npy_int16, s16) +NPYV_IMPL_AVX2_MEM_INT(npy_uint32, u32) +NPYV_IMPL_AVX2_MEM_INT(npy_int32, s32) +NPYV_IMPL_AVX2_MEM_INT(npy_uint64, u64) +NPYV_IMPL_AVX2_MEM_INT(npy_int64, s64) + +// unaligned load +#define npyv_load_f32 _mm256_loadu_ps +#define npyv_load_f64 _mm256_loadu_pd +// aligned load +#define npyv_loada_f32 _mm256_load_ps +#define npyv_loada_f64 _mm256_load_pd +// stream load +#define npyv_loads_f32(PTR) \ + _mm256_castsi256_ps(_mm256_stream_load_si256((const __m256i*)(PTR))) +#define npyv_loads_f64(PTR) \ + _mm256_castsi256_pd(_mm256_stream_load_si256((const __m256i*)(PTR))) +// load lower part +#define npyv_loadl_f32(PTR) _mm256_castps128_ps256(_mm_loadu_ps(PTR)) +#define npyv_loadl_f64(PTR) _mm256_castpd128_pd256(_mm_loadu_pd(PTR)) +// unaligned store +#define npyv_store_f32 _mm256_storeu_ps +#define npyv_store_f64 _mm256_storeu_pd +// aligned store +#define npyv_storea_f32 _mm256_store_ps +#define npyv_storea_f64 _mm256_store_pd +// stream store +#define npyv_stores_f32 _mm256_stream_ps +#define npyv_stores_f64 _mm256_stream_pd +// store lower part +#define npyv_storel_f32(PTR, VEC) _mm_storeu_ps(PTR, _mm256_castps256_ps128(VEC)) +#define npyv_storel_f64(PTR, VEC) _mm_storeu_pd(PTR, _mm256_castpd256_pd128(VEC)) +// store higher part +#define npyv_storeh_f32(PTR, VEC) _mm_storeu_ps(PTR, _mm256_extractf128_ps(VEC, 1)) +#define npyv_storeh_f64(PTR, VEC) _mm_storeu_pd(PTR, _mm256_extractf128_pd(VEC, 1)) +/*************************** + * Non-contiguous Load + ***************************/ +//// 32 +NPY_FINLINE npyv_u32 npyv_loadn_u32(const npy_uint32 *ptr, npy_intp stride) +{ + assert(llabs(stride) <= NPY_SIMD_MAXLOAD_STRIDE32); + const __m256i steps = _mm256_setr_epi32(0, 1, 2, 3, 4, 5, 6, 7); + const __m256i idx = _mm256_mullo_epi32(_mm256_set1_epi32((int)stride), steps); + return _mm256_i32gather_epi32((const int*)ptr, idx, 4); +} +NPY_FINLINE npyv_s32 npyv_loadn_s32(const npy_int32 *ptr, npy_intp stride) +{ return npyv_loadn_u32((const npy_uint32*)ptr, stride); } +NPY_FINLINE npyv_f32 npyv_loadn_f32(const float *ptr, npy_intp stride) +{ return _mm256_castsi256_ps(npyv_loadn_u32((const npy_uint32*)ptr, stride)); } +//// 64 +#if 0 // slower +NPY_FINLINE npyv_u64 npyv_loadn_u64(const npy_uint64 *ptr, npy_intp stride) +{ + const __m256i idx = npyv_set_s64(0, 1*stride, 2*stride, 3*stride); + return _mm256_i64gather_epi64((const void*)ptr, idx, 8); +} +NPY_FINLINE npyv_s64 npyv_loadn_s64(const npy_int64 *ptr, npy_intp stride) +{ return npyv_loadn_u64((const npy_uint64*)ptr, stride); } +NPY_FINLINE npyv_f64 npyv_loadn_f64(const double *ptr, npy_intp stride) +{ return _mm256_castsi256_pd(npyv_loadn_u64((const npy_uint64*)ptr, stride)); } +#endif +NPY_FINLINE npyv_f64 npyv_loadn_f64(const double *ptr, npy_intp stride) +{ + __m128d a0 = _mm_castsi128_pd(_mm_loadl_epi64((const __m128i*)ptr)); + __m128d a2 = _mm_castsi128_pd(_mm_loadl_epi64((const __m128i*)(ptr + stride*2))); + __m128d a01 = _mm_loadh_pd(a0, ptr + stride); + __m128d a23 = _mm_loadh_pd(a2, ptr + stride*3); + return _mm256_insertf128_pd(_mm256_castpd128_pd256(a01), a23, 1); +} +NPY_FINLINE npyv_u64 npyv_loadn_u64(const npy_uint64 *ptr, npy_intp stride) +{ return _mm256_castpd_si256(npyv_loadn_f64((const double*)ptr, stride)); } +NPY_FINLINE npyv_s64 npyv_loadn_s64(const npy_int64 *ptr, npy_intp stride) +{ return _mm256_castpd_si256(npyv_loadn_f64((const double*)ptr, stride)); } +/*************************** + * Non-contiguous Store + ***************************/ +//// 32 +NPY_FINLINE void npyv_storen_s32(npy_int32 *ptr, npy_intp stride, npyv_s32 a) +{ + __m128i a0 = _mm256_castsi256_si128(a); + __m128i a1 = _mm256_extracti128_si256(a, 1); + ptr[stride * 0] = _mm_cvtsi128_si32(a0); + ptr[stride * 1] = _mm_extract_epi32(a0, 1); + ptr[stride * 2] = _mm_extract_epi32(a0, 2); + ptr[stride * 3] = _mm_extract_epi32(a0, 3); + ptr[stride * 4] = _mm_cvtsi128_si32(a1); + ptr[stride * 5] = _mm_extract_epi32(a1, 1); + ptr[stride * 6] = _mm_extract_epi32(a1, 2); + ptr[stride * 7] = _mm_extract_epi32(a1, 3); +} +NPY_FINLINE void npyv_storen_u32(npy_uint32 *ptr, npy_intp stride, npyv_u32 a) +{ npyv_storen_s32((npy_int32*)ptr, stride, a); } +NPY_FINLINE void npyv_storen_f32(float *ptr, npy_intp stride, npyv_f32 a) +{ npyv_storen_s32((npy_int32*)ptr, stride, _mm256_castps_si256(a)); } +//// 64 +NPY_FINLINE void npyv_storen_f64(double *ptr, npy_intp stride, npyv_f64 a) +{ + __m128d a0 = _mm256_castpd256_pd128(a); + __m128d a1 = _mm256_extractf128_pd(a, 1); + _mm_storel_pd(ptr + stride * 0, a0); + _mm_storeh_pd(ptr + stride * 1, a0); + _mm_storel_pd(ptr + stride * 2, a1); + _mm_storeh_pd(ptr + stride * 3, a1); +} +NPY_FINLINE void npyv_storen_u64(npy_uint64 *ptr, npy_intp stride, npyv_u64 a) +{ npyv_storen_f64((double*)ptr, stride, _mm256_castsi256_pd(a)); } +NPY_FINLINE void npyv_storen_s64(npy_int64 *ptr, npy_intp stride, npyv_s64 a) +{ npyv_storen_f64((double*)ptr, stride, _mm256_castsi256_pd(a)); } + +/********************************* + * Partial Load + *********************************/ +//// 32 +NPY_FINLINE npyv_s32 npyv_load_till_s32(const npy_int32 *ptr, npy_uintp nlane, npy_int32 fill) +{ + assert(nlane > 0); + const __m256i vfill = _mm256_set1_epi32(fill); + const __m256i steps = _mm256_setr_epi32(0, 1, 2, 3, 4, 5, 6, 7); + __m256i vnlane = _mm256_set1_epi32(nlane > 8 ? 8 : (int)nlane); + __m256i mask = _mm256_cmpgt_epi32(vnlane, steps); + __m256i payload = _mm256_maskload_epi32((const int*)ptr, mask); + return _mm256_blendv_epi8(vfill, payload, mask); +} +// fill zero to rest lanes +NPY_FINLINE npyv_s32 npyv_load_tillz_s32(const npy_int32 *ptr, npy_uintp nlane) +{ + assert(nlane > 0); + const __m256i steps = _mm256_setr_epi32(0, 1, 2, 3, 4, 5, 6, 7); + __m256i vnlane = _mm256_set1_epi32(nlane > 8 ? 8 : (int)nlane); + __m256i mask = _mm256_cmpgt_epi32(vnlane, steps); + return _mm256_maskload_epi32((const int*)ptr, mask); +} +//// 64 +NPY_FINLINE npyv_s64 npyv_load_till_s64(const npy_int64 *ptr, npy_uintp nlane, npy_int64 fill) +{ + assert(nlane > 0); + const __m256i vfill = npyv_setall_s64(fill); + const __m256i steps = npyv_set_s64(0, 1, 2, 3); + __m256i vnlane = npyv_setall_s64(nlane > 4 ? 4 : (int)nlane); + __m256i mask = _mm256_cmpgt_epi64(vnlane, steps); + __m256i payload = _mm256_maskload_epi64((const void*)ptr, mask); + return _mm256_blendv_epi8(vfill, payload, mask); +} +// fill zero to rest lanes +NPY_FINLINE npyv_s64 npyv_load_tillz_s64(const npy_int64 *ptr, npy_uintp nlane) +{ + assert(nlane > 0); + const __m256i steps = npyv_set_s64(0, 1, 2, 3); + __m256i vnlane = npyv_setall_s64(nlane > 4 ? 4 : (int)nlane); + __m256i mask = _mm256_cmpgt_epi64(vnlane, steps); + return _mm256_maskload_epi64((const void*)ptr, mask); +} +/********************************* + * Non-contiguous partial load + *********************************/ +//// 32 +NPY_FINLINE npyv_s32 +npyv_loadn_till_s32(const npy_int32 *ptr, npy_intp stride, npy_uintp nlane, npy_int32 fill) +{ + assert(nlane > 0); + assert(llabs(stride) <= NPY_SIMD_MAXLOAD_STRIDE32); + const __m256i vfill = _mm256_set1_epi32(fill); + const __m256i steps = _mm256_setr_epi32(0, 1, 2, 3, 4, 5, 6, 7); + const __m256i idx = _mm256_mullo_epi32(_mm256_set1_epi32((int)stride), steps); + __m256i vnlane = _mm256_set1_epi32(nlane > 8 ? 8 : (int)nlane); + __m256i mask = _mm256_cmpgt_epi32(vnlane, steps); + return _mm256_mask_i32gather_epi32(vfill, (const int*)ptr, idx, mask, 4); +} +// fill zero to rest lanes +NPY_FINLINE npyv_s32 +npyv_loadn_tillz_s32(const npy_int32 *ptr, npy_intp stride, npy_uintp nlane) +{ return npyv_loadn_till_s32(ptr, stride, nlane, 0); } +//// 64 +NPY_FINLINE npyv_s64 +npyv_loadn_till_s64(const npy_int64 *ptr, npy_intp stride, npy_uintp nlane, npy_int64 fill) +{ + assert(nlane > 0); + const __m256i vfill = npyv_setall_s64(fill); + const __m256i idx = npyv_set_s64(0, 1*stride, 2*stride, 3*stride); + const __m256i steps = npyv_set_s64(0, 1, 2, 3); + __m256i vnlane = npyv_setall_s64(nlane > 4 ? 4 : (int)nlane); + __m256i mask = _mm256_cmpgt_epi64(vnlane, steps); + return _mm256_mask_i64gather_epi64(vfill, (const void*)ptr, idx, mask, 8); +} +// fill zero to rest lanes +NPY_FINLINE npyv_s64 +npyv_loadn_tillz_s64(const npy_int64 *ptr, npy_intp stride, npy_uintp nlane) +{ return npyv_loadn_till_s64(ptr, stride, nlane, 0); } +/********************************* + * Partial store + *********************************/ +//// 32 +NPY_FINLINE void npyv_store_till_s32(npy_int32 *ptr, npy_uintp nlane, npyv_s32 a) +{ + assert(nlane > 0); + const __m256i steps = _mm256_setr_epi32(0, 1, 2, 3, 4, 5, 6, 7); + __m256i vnlane = _mm256_set1_epi32(nlane > 8 ? 8 : (int)nlane); + __m256i mask = _mm256_cmpgt_epi32(vnlane, steps); + _mm256_maskstore_epi32((int*)ptr, mask, a); +} +//// 64 +NPY_FINLINE void npyv_store_till_s64(npy_int64 *ptr, npy_uintp nlane, npyv_s64 a) +{ + assert(nlane > 0); + const __m256i steps = npyv_set_s64(0, 1, 2, 3); + __m256i vnlane = npyv_setall_s64(nlane > 8 ? 8 : (int)nlane); + __m256i mask = _mm256_cmpgt_epi64(vnlane, steps); + _mm256_maskstore_epi64((void*)ptr, mask, a); +} +/********************************* + * Non-contiguous partial store + *********************************/ +//// 32 +NPY_FINLINE void npyv_storen_till_s32(npy_int32 *ptr, npy_intp stride, npy_uintp nlane, npyv_s32 a) +{ + assert(nlane > 0); + __m128i a0 = _mm256_castsi256_si128(a); + __m128i a1 = _mm256_extracti128_si256(a, 1); + switch(nlane) { + default: + ptr[stride*7] = _mm_extract_epi32(a1, 3); + case 7: + ptr[stride*6] = _mm_extract_epi32(a1, 2); + case 6: + ptr[stride*5] = _mm_extract_epi32(a1, 1); + case 5: + ptr[stride*4] = _mm_extract_epi32(a1, 0); + case 4: + ptr[stride*3] = _mm_extract_epi32(a0, 3); + case 3: + ptr[stride*2] = _mm_extract_epi32(a0, 2); + case 2: + ptr[stride*1] = _mm_extract_epi32(a0, 1); + case 1: + ptr[stride*0] = _mm_extract_epi32(a0, 0); + } +} +//// 64 +NPY_FINLINE void npyv_storen_till_s64(npy_int64 *ptr, npy_intp stride, npy_uintp nlane, npyv_s64 a) +{ + assert(nlane > 0); + __m128d a0 = _mm256_castpd256_pd128(_mm256_castsi256_pd(a)); + __m128d a1 = _mm256_extractf128_pd(_mm256_castsi256_pd(a), 1); + double *dptr = (double*)ptr; + switch(nlane) { + default: + _mm_storeh_pd(dptr + stride * 3, a1); + case 3: + _mm_storel_pd(dptr + stride * 2, a1); + case 2: + _mm_storeh_pd(dptr + stride * 1, a0); + case 1: + _mm_storel_pd(dptr + stride * 0, a0); + } +} + +/***************************************************************************** + * Implement partial load/store for u32/f32/u64/f64... via reinterpret cast + *****************************************************************************/ +#define NPYV_IMPL_AVX2_REST_PARTIAL_TYPES(F_SFX, T_SFX) \ + NPY_FINLINE npyv_##F_SFX npyv_load_till_##F_SFX \ + (const npyv_lanetype_##F_SFX *ptr, npy_uintp nlane, npyv_lanetype_##F_SFX fill) \ + { \ + union { \ + npyv_lanetype_##F_SFX from_##F_SFX; \ + npyv_lanetype_##T_SFX to_##T_SFX; \ + } pun = {.from_##F_SFX = fill}; \ + return npyv_reinterpret_##F_SFX##_##T_SFX(npyv_load_till_##T_SFX( \ + (const npyv_lanetype_##T_SFX *)ptr, nlane, pun.to_##T_SFX \ + )); \ + } \ + NPY_FINLINE npyv_##F_SFX npyv_loadn_till_##F_SFX \ + (const npyv_lanetype_##F_SFX *ptr, npy_intp stride, npy_uintp nlane, \ + npyv_lanetype_##F_SFX fill) \ + { \ + union { \ + npyv_lanetype_##F_SFX from_##F_SFX; \ + npyv_lanetype_##T_SFX to_##T_SFX; \ + } pun = {.from_##F_SFX = fill}; \ + return npyv_reinterpret_##F_SFX##_##T_SFX(npyv_loadn_till_##T_SFX( \ + (const npyv_lanetype_##T_SFX *)ptr, stride, nlane, pun.to_##T_SFX \ + )); \ + } \ + NPY_FINLINE npyv_##F_SFX npyv_load_tillz_##F_SFX \ + (const npyv_lanetype_##F_SFX *ptr, npy_uintp nlane) \ + { \ + return npyv_reinterpret_##F_SFX##_##T_SFX(npyv_load_tillz_##T_SFX( \ + (const npyv_lanetype_##T_SFX *)ptr, nlane \ + )); \ + } \ + NPY_FINLINE npyv_##F_SFX npyv_loadn_tillz_##F_SFX \ + (const npyv_lanetype_##F_SFX *ptr, npy_intp stride, npy_uintp nlane) \ + { \ + return npyv_reinterpret_##F_SFX##_##T_SFX(npyv_loadn_tillz_##T_SFX( \ + (const npyv_lanetype_##T_SFX *)ptr, stride, nlane \ + )); \ + } \ + NPY_FINLINE void npyv_store_till_##F_SFX \ + (npyv_lanetype_##F_SFX *ptr, npy_uintp nlane, npyv_##F_SFX a) \ + { \ + npyv_store_till_##T_SFX( \ + (npyv_lanetype_##T_SFX *)ptr, nlane, \ + npyv_reinterpret_##T_SFX##_##F_SFX(a) \ + ); \ + } \ + NPY_FINLINE void npyv_storen_till_##F_SFX \ + (npyv_lanetype_##F_SFX *ptr, npy_intp stride, npy_uintp nlane, npyv_##F_SFX a) \ + { \ + npyv_storen_till_##T_SFX( \ + (npyv_lanetype_##T_SFX *)ptr, stride, nlane, \ + npyv_reinterpret_##T_SFX##_##F_SFX(a) \ + ); \ + } + +NPYV_IMPL_AVX2_REST_PARTIAL_TYPES(u32, s32) +NPYV_IMPL_AVX2_REST_PARTIAL_TYPES(f32, s32) +NPYV_IMPL_AVX2_REST_PARTIAL_TYPES(u64, s64) +NPYV_IMPL_AVX2_REST_PARTIAL_TYPES(f64, s64) + +#endif // _NPY_SIMD_AVX2_MEMORY_H diff --git a/numpy/core/src/common/simd/avx2/misc.h b/numpy/core/src/common/simd/avx2/misc.h new file mode 100644 index 000000000000..5e91e91b3d0f --- /dev/null +++ b/numpy/core/src/common/simd/avx2/misc.h @@ -0,0 +1,246 @@ +#ifndef NPY_SIMD + #error "Not a standalone header" +#endif + +#ifndef _NPY_SIMD_AVX2_MISC_H +#define _NPY_SIMD_AVX2_MISC_H + +// vector with zero lanes +#define npyv_zero_u8 _mm256_setzero_si256 +#define npyv_zero_s8 _mm256_setzero_si256 +#define npyv_zero_u16 _mm256_setzero_si256 +#define npyv_zero_s16 _mm256_setzero_si256 +#define npyv_zero_u32 _mm256_setzero_si256 +#define npyv_zero_s32 _mm256_setzero_si256 +#define npyv_zero_u64 _mm256_setzero_si256 +#define npyv_zero_s64 _mm256_setzero_si256 +#define npyv_zero_f32 _mm256_setzero_ps +#define npyv_zero_f64 _mm256_setzero_pd + +// vector with a specific value set to all lanes +#define npyv_setall_u8(VAL) _mm256_set1_epi8((char)VAL) +#define npyv_setall_s8(VAL) _mm256_set1_epi8((char)VAL) +#define npyv_setall_u16(VAL) _mm256_set1_epi16((short)VAL) +#define npyv_setall_s16(VAL) _mm256_set1_epi16((short)VAL) +#define npyv_setall_u32(VAL) _mm256_set1_epi32((int)VAL) +#define npyv_setall_s32(VAL) _mm256_set1_epi32(VAL) +#define npyv_setall_f32(VAL) _mm256_set1_ps(VAL) +#define npyv_setall_f64(VAL) _mm256_set1_pd(VAL) + +NPY_FINLINE __m256i npyv__setr_epi64(npy_int64, npy_int64, npy_int64, npy_int64); +NPY_FINLINE npyv_u64 npyv_setall_u64(npy_uint64 a) +{ + npy_int64 ai = (npy_int64)a; +#if defined(_MSC_VER) && defined(_M_IX86) + return npyv__setr_epi64(ai, ai, ai, ai); +#else + return _mm256_set1_epi64x(ai); +#endif +} +NPY_FINLINE npyv_s64 npyv_setall_s64(npy_int64 a) +{ +#if defined(_MSC_VER) && defined(_M_IX86) + return npyv__setr_epi64(a, a, a, a); +#else + return _mm256_set1_epi64x(a); +#endif +} +/* + * vector with specific values set to each lane and + * set a specific value to all remained lanes + * + * Args that generated by NPYV__SET_FILL_* not going to expand if + * _mm256_setr_* are defined as macros. +*/ +NPY_FINLINE __m256i npyv__setr_epi8( + char i0, char i1, char i2, char i3, char i4, char i5, char i6, char i7, + char i8, char i9, char i10, char i11, char i12, char i13, char i14, char i15, + char i16, char i17, char i18, char i19, char i20, char i21, char i22, char i23, + char i24, char i25, char i26, char i27, char i28, char i29, char i30, char i31) +{ + return _mm256_setr_epi8( + i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15, + i16, i17, i18, i19, i20, i21, i22, i23, i24, i25, i26, i27, i28, i29, i30, i31 + ); +} +NPY_FINLINE __m256i npyv__setr_epi16( + short i0, short i1, short i2, short i3, short i4, short i5, short i6, short i7, + short i8, short i9, short i10, short i11, short i12, short i13, short i14, short i15) +{ + return _mm256_setr_epi16(i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15); +} +NPY_FINLINE __m256i npyv__setr_epi32(int i0, int i1, int i2, int i3, int i4, int i5, int i6, int i7) +{ + return _mm256_setr_epi32(i0, i1, i2, i3, i4, i5, i6, i7); +} +NPY_FINLINE __m256i npyv__setr_epi64(npy_int64 i0, npy_int64 i1, npy_int64 i2, npy_int64 i3) +{ +#if defined(_MSC_VER) && defined(_M_IX86) + return _mm256_setr_epi32( + (int)i0, (int)(i0 >> 32), (int)i1, (int)(i1 >> 32), + (int)i2, (int)(i2 >> 32), (int)i3, (int)(i3 >> 32) + ); +#else + return _mm256_setr_epi64x(i0, i1, i2, i3); +#endif +} + +NPY_FINLINE __m256 npyv__setr_ps(float i0, float i1, float i2, float i3, float i4, float i5, + float i6, float i7) +{ + return _mm256_setr_ps(i0, i1, i2, i3, i4, i5, i6, i7); +} +NPY_FINLINE __m256d npyv__setr_pd(double i0, double i1, double i2, double i3) +{ + return _mm256_setr_pd(i0, i1, i2, i3); +} +#define npyv_setf_u8(FILL, ...) npyv__setr_epi8(NPYV__SET_FILL_32(char, FILL, __VA_ARGS__)) +#define npyv_setf_s8(FILL, ...) npyv__setr_epi8(NPYV__SET_FILL_32(char, FILL, __VA_ARGS__)) +#define npyv_setf_u16(FILL, ...) npyv__setr_epi16(NPYV__SET_FILL_16(short, FILL, __VA_ARGS__)) +#define npyv_setf_s16(FILL, ...) npyv__setr_epi16(NPYV__SET_FILL_16(short, FILL, __VA_ARGS__)) +#define npyv_setf_u32(FILL, ...) npyv__setr_epi32(NPYV__SET_FILL_8(int, FILL, __VA_ARGS__)) +#define npyv_setf_s32(FILL, ...) npyv__setr_epi32(NPYV__SET_FILL_8(int, FILL, __VA_ARGS__)) +#define npyv_setf_u64(FILL, ...) npyv__setr_epi64(NPYV__SET_FILL_4(npy_uint64, FILL, __VA_ARGS__)) +#define npyv_setf_s64(FILL, ...) npyv__setr_epi64(NPYV__SET_FILL_4(npy_int64, FILL, __VA_ARGS__)) +#define npyv_setf_f32(FILL, ...) npyv__setr_ps(NPYV__SET_FILL_8(float, FILL, __VA_ARGS__)) +#define npyv_setf_f64(FILL, ...) npyv__setr_pd(NPYV__SET_FILL_4(double, FILL, __VA_ARGS__)) + +// vector with specific values set to each lane and +// set zero to all remained lanes +#define npyv_set_u8(...) npyv_setf_u8(0, __VA_ARGS__) +#define npyv_set_s8(...) npyv_setf_s8(0, __VA_ARGS__) +#define npyv_set_u16(...) npyv_setf_u16(0, __VA_ARGS__) +#define npyv_set_s16(...) npyv_setf_s16(0, __VA_ARGS__) +#define npyv_set_u32(...) npyv_setf_u32(0, __VA_ARGS__) +#define npyv_set_s32(...) npyv_setf_s32(0, __VA_ARGS__) +#define npyv_set_u64(...) npyv_setf_u64(0, __VA_ARGS__) +#define npyv_set_s64(...) npyv_setf_s64(0, __VA_ARGS__) +#define npyv_set_f32(...) npyv_setf_f32(0, __VA_ARGS__) +#define npyv_set_f64(...) npyv_setf_f64(0, __VA_ARGS__) + +// Per lane select +#define npyv_select_u8(MASK, A, B) _mm256_blendv_epi8(B, A, MASK) +#define npyv_select_s8 npyv_select_u8 +#define npyv_select_u16 npyv_select_u8 +#define npyv_select_s16 npyv_select_u8 +#define npyv_select_u32 npyv_select_u8 +#define npyv_select_s32 npyv_select_u8 +#define npyv_select_u64 npyv_select_u8 +#define npyv_select_s64 npyv_select_u8 +#define npyv_select_f32(MASK, A, B) _mm256_blendv_ps(B, A, _mm256_castsi256_ps(MASK)) +#define npyv_select_f64(MASK, A, B) _mm256_blendv_pd(B, A, _mm256_castsi256_pd(MASK)) + +// Reinterpret +#define npyv_reinterpret_u8_u8(X) X +#define npyv_reinterpret_u8_s8(X) X +#define npyv_reinterpret_u8_u16(X) X +#define npyv_reinterpret_u8_s16(X) X +#define npyv_reinterpret_u8_u32(X) X +#define npyv_reinterpret_u8_s32(X) X +#define npyv_reinterpret_u8_u64(X) X +#define npyv_reinterpret_u8_s64(X) X +#define npyv_reinterpret_u8_f32 _mm256_castps_si256 +#define npyv_reinterpret_u8_f64 _mm256_castpd_si256 + +#define npyv_reinterpret_s8_s8(X) X +#define npyv_reinterpret_s8_u8(X) X +#define npyv_reinterpret_s8_u16(X) X +#define npyv_reinterpret_s8_s16(X) X +#define npyv_reinterpret_s8_u32(X) X +#define npyv_reinterpret_s8_s32(X) X +#define npyv_reinterpret_s8_u64(X) X +#define npyv_reinterpret_s8_s64(X) X +#define npyv_reinterpret_s8_f32 _mm256_castps_si256 +#define npyv_reinterpret_s8_f64 _mm256_castpd_si256 + +#define npyv_reinterpret_u16_u16(X) X +#define npyv_reinterpret_u16_u8(X) X +#define npyv_reinterpret_u16_s8(X) X +#define npyv_reinterpret_u16_s16(X) X +#define npyv_reinterpret_u16_u32(X) X +#define npyv_reinterpret_u16_s32(X) X +#define npyv_reinterpret_u16_u64(X) X +#define npyv_reinterpret_u16_s64(X) X +#define npyv_reinterpret_u16_f32 _mm256_castps_si256 +#define npyv_reinterpret_u16_f64 _mm256_castpd_si256 + +#define npyv_reinterpret_s16_s16(X) X +#define npyv_reinterpret_s16_u8(X) X +#define npyv_reinterpret_s16_s8(X) X +#define npyv_reinterpret_s16_u16(X) X +#define npyv_reinterpret_s16_u32(X) X +#define npyv_reinterpret_s16_s32(X) X +#define npyv_reinterpret_s16_u64(X) X +#define npyv_reinterpret_s16_s64(X) X +#define npyv_reinterpret_s16_f32 _mm256_castps_si256 +#define npyv_reinterpret_s16_f64 _mm256_castpd_si256 + +#define npyv_reinterpret_u32_u32(X) X +#define npyv_reinterpret_u32_u8(X) X +#define npyv_reinterpret_u32_s8(X) X +#define npyv_reinterpret_u32_u16(X) X +#define npyv_reinterpret_u32_s16(X) X +#define npyv_reinterpret_u32_s32(X) X +#define npyv_reinterpret_u32_u64(X) X +#define npyv_reinterpret_u32_s64(X) X +#define npyv_reinterpret_u32_f32 _mm256_castps_si256 +#define npyv_reinterpret_u32_f64 _mm256_castpd_si256 + +#define npyv_reinterpret_s32_s32(X) X +#define npyv_reinterpret_s32_u8(X) X +#define npyv_reinterpret_s32_s8(X) X +#define npyv_reinterpret_s32_u16(X) X +#define npyv_reinterpret_s32_s16(X) X +#define npyv_reinterpret_s32_u32(X) X +#define npyv_reinterpret_s32_u64(X) X +#define npyv_reinterpret_s32_s64(X) X +#define npyv_reinterpret_s32_f32 _mm256_castps_si256 +#define npyv_reinterpret_s32_f64 _mm256_castpd_si256 + +#define npyv_reinterpret_u64_u64(X) X +#define npyv_reinterpret_u64_u8(X) X +#define npyv_reinterpret_u64_s8(X) X +#define npyv_reinterpret_u64_u16(X) X +#define npyv_reinterpret_u64_s16(X) X +#define npyv_reinterpret_u64_u32(X) X +#define npyv_reinterpret_u64_s32(X) X +#define npyv_reinterpret_u64_s64(X) X +#define npyv_reinterpret_u64_f32 _mm256_castps_si256 +#define npyv_reinterpret_u64_f64 _mm256_castpd_si256 + +#define npyv_reinterpret_s64_s64(X) X +#define npyv_reinterpret_s64_u8(X) X +#define npyv_reinterpret_s64_s8(X) X +#define npyv_reinterpret_s64_u16(X) X +#define npyv_reinterpret_s64_s16(X) X +#define npyv_reinterpret_s64_u32(X) X +#define npyv_reinterpret_s64_s32(X) X +#define npyv_reinterpret_s64_u64(X) X +#define npyv_reinterpret_s64_f32 _mm256_castps_si256 +#define npyv_reinterpret_s64_f64 _mm256_castpd_si256 + +#define npyv_reinterpret_f32_f32(X) X +#define npyv_reinterpret_f32_u8 _mm256_castsi256_ps +#define npyv_reinterpret_f32_s8 _mm256_castsi256_ps +#define npyv_reinterpret_f32_u16 _mm256_castsi256_ps +#define npyv_reinterpret_f32_s16 _mm256_castsi256_ps +#define npyv_reinterpret_f32_u32 _mm256_castsi256_ps +#define npyv_reinterpret_f32_s32 _mm256_castsi256_ps +#define npyv_reinterpret_f32_u64 _mm256_castsi256_ps +#define npyv_reinterpret_f32_s64 _mm256_castsi256_ps +#define npyv_reinterpret_f32_f64 _mm256_castpd_ps + +#define npyv_reinterpret_f64_f64(X) X +#define npyv_reinterpret_f64_u8 _mm256_castsi256_pd +#define npyv_reinterpret_f64_s8 _mm256_castsi256_pd +#define npyv_reinterpret_f64_u16 _mm256_castsi256_pd +#define npyv_reinterpret_f64_s16 _mm256_castsi256_pd +#define npyv_reinterpret_f64_u32 _mm256_castsi256_pd +#define npyv_reinterpret_f64_s32 _mm256_castsi256_pd +#define npyv_reinterpret_f64_u64 _mm256_castsi256_pd +#define npyv_reinterpret_f64_s64 _mm256_castsi256_pd +#define npyv_reinterpret_f64_f32 _mm256_castps_pd + +#define npyv_cleanup _mm256_zeroall + +#endif // _NPY_SIMD_SSE_MISC_H diff --git a/numpy/core/src/common/simd/avx2/operators.h b/numpy/core/src/common/simd/avx2/operators.h new file mode 100644 index 000000000000..5fc7719e916d --- /dev/null +++ b/numpy/core/src/common/simd/avx2/operators.h @@ -0,0 +1,222 @@ +#ifndef NPY_SIMD + #error "Not a standalone header" +#endif + +#ifndef _NPY_SIMD_AVX2_OPERATORS_H +#define _NPY_SIMD_AVX2_OPERATORS_H + +/*************************** + * Shifting + ***************************/ + +// left +#define npyv_shl_u16(A, C) _mm256_sll_epi16(A, _mm_cvtsi32_si128(C)) +#define npyv_shl_s16(A, C) _mm256_sll_epi16(A, _mm_cvtsi32_si128(C)) +#define npyv_shl_u32(A, C) _mm256_sll_epi32(A, _mm_cvtsi32_si128(C)) +#define npyv_shl_s32(A, C) _mm256_sll_epi32(A, _mm_cvtsi32_si128(C)) +#define npyv_shl_u64(A, C) _mm256_sll_epi64(A, _mm_cvtsi32_si128(C)) +#define npyv_shl_s64(A, C) _mm256_sll_epi64(A, _mm_cvtsi32_si128(C)) + +// left by an immediate constant +#define npyv_shli_u16 _mm256_slli_epi16 +#define npyv_shli_s16 _mm256_slli_epi16 +#define npyv_shli_u32 _mm256_slli_epi32 +#define npyv_shli_s32 _mm256_slli_epi32 +#define npyv_shli_u64 _mm256_slli_epi64 +#define npyv_shli_s64 _mm256_slli_epi64 + +// right +#define npyv_shr_u16(A, C) _mm256_srl_epi16(A, _mm_cvtsi32_si128(C)) +#define npyv_shr_s16(A, C) _mm256_sra_epi16(A, _mm_cvtsi32_si128(C)) +#define npyv_shr_u32(A, C) _mm256_srl_epi32(A, _mm_cvtsi32_si128(C)) +#define npyv_shr_s32(A, C) _mm256_sra_epi32(A, _mm_cvtsi32_si128(C)) +#define npyv_shr_u64(A, C) _mm256_srl_epi64(A, _mm_cvtsi32_si128(C)) +NPY_FINLINE __m256i npyv_shr_s64(__m256i a, int c) +{ + const __m256i sbit = _mm256_set1_epi64x(0x8000000000000000); + const __m128i c64 = _mm_cvtsi32_si128(c); + __m256i r = _mm256_srl_epi64(_mm256_add_epi64(a, sbit), c64); + return _mm256_sub_epi64(r, _mm256_srl_epi64(sbit, c64)); +} + +// right by an immediate constant +#define npyv_shri_u16 _mm256_srli_epi16 +#define npyv_shri_s16 _mm256_srai_epi16 +#define npyv_shri_u32 _mm256_srli_epi32 +#define npyv_shri_s32 _mm256_srai_epi32 +#define npyv_shri_u64 _mm256_srli_epi64 +#define npyv_shri_s64 npyv_shr_s64 + +/*************************** + * Logical + ***************************/ +// AND +#define npyv_and_u8 _mm256_and_si256 +#define npyv_and_s8 _mm256_and_si256 +#define npyv_and_u16 _mm256_and_si256 +#define npyv_and_s16 _mm256_and_si256 +#define npyv_and_u32 _mm256_and_si256 +#define npyv_and_s32 _mm256_and_si256 +#define npyv_and_u64 _mm256_and_si256 +#define npyv_and_s64 _mm256_and_si256 +#define npyv_and_f32 _mm256_and_ps +#define npyv_and_f64 _mm256_and_pd +#define npyv_and_b8 _mm256_and_si256 +#define npyv_and_b16 _mm256_and_si256 +#define npyv_and_b32 _mm256_and_si256 +#define npyv_and_b64 _mm256_and_si256 + +// OR +#define npyv_or_u8 _mm256_or_si256 +#define npyv_or_s8 _mm256_or_si256 +#define npyv_or_u16 _mm256_or_si256 +#define npyv_or_s16 _mm256_or_si256 +#define npyv_or_u32 _mm256_or_si256 +#define npyv_or_s32 _mm256_or_si256 +#define npyv_or_u64 _mm256_or_si256 +#define npyv_or_s64 _mm256_or_si256 +#define npyv_or_f32 _mm256_or_ps +#define npyv_or_f64 _mm256_or_pd +#define npyv_or_b8 _mm256_or_si256 +#define npyv_or_b16 _mm256_or_si256 +#define npyv_or_b32 _mm256_or_si256 +#define npyv_or_b64 _mm256_or_si256 + +// XOR +#define npyv_xor_u8 _mm256_xor_si256 +#define npyv_xor_s8 _mm256_xor_si256 +#define npyv_xor_u16 _mm256_xor_si256 +#define npyv_xor_s16 _mm256_xor_si256 +#define npyv_xor_u32 _mm256_xor_si256 +#define npyv_xor_s32 _mm256_xor_si256 +#define npyv_xor_u64 _mm256_xor_si256 +#define npyv_xor_s64 _mm256_xor_si256 +#define npyv_xor_f32 _mm256_xor_ps +#define npyv_xor_f64 _mm256_xor_pd +#define npyv_xor_b8 _mm256_xor_si256 +#define npyv_xor_b16 _mm256_xor_si256 +#define npyv_xor_b32 _mm256_xor_si256 +#define npyv_xor_b64 _mm256_xor_si256 + +// NOT +#define npyv_not_u8(A) _mm256_xor_si256(A, _mm256_set1_epi32(-1)) +#define npyv_not_s8 npyv_not_u8 +#define npyv_not_u16 npyv_not_u8 +#define npyv_not_s16 npyv_not_u8 +#define npyv_not_u32 npyv_not_u8 +#define npyv_not_s32 npyv_not_u8 +#define npyv_not_u64 npyv_not_u8 +#define npyv_not_s64 npyv_not_u8 +#define npyv_not_f32(A) _mm256_xor_ps(A, _mm256_castsi256_ps(_mm256_set1_epi32(-1))) +#define npyv_not_f64(A) _mm256_xor_pd(A, _mm256_castsi256_pd(_mm256_set1_epi32(-1))) +#define npyv_not_b8 npyv_not_u8 +#define npyv_not_b16 npyv_not_u8 +#define npyv_not_b32 npyv_not_u8 +#define npyv_not_b64 npyv_not_u8 + +/*************************** + * Comparison + ***************************/ + +// int Equal +#define npyv_cmpeq_u8 _mm256_cmpeq_epi8 +#define npyv_cmpeq_s8 _mm256_cmpeq_epi8 +#define npyv_cmpeq_u16 _mm256_cmpeq_epi16 +#define npyv_cmpeq_s16 _mm256_cmpeq_epi16 +#define npyv_cmpeq_u32 _mm256_cmpeq_epi32 +#define npyv_cmpeq_s32 _mm256_cmpeq_epi32 +#define npyv_cmpeq_u64 _mm256_cmpeq_epi64 +#define npyv_cmpeq_s64 _mm256_cmpeq_epi64 + +// int Not Equal +#define npyv_cmpneq_u8(A, B) npyv_not_u8(_mm256_cmpeq_epi8(A, B)) +#define npyv_cmpneq_s8 npyv_cmpneq_u8 +#define npyv_cmpneq_u16(A, B) npyv_not_u16(_mm256_cmpeq_epi16(A, B)) +#define npyv_cmpneq_s16 npyv_cmpneq_u16 +#define npyv_cmpneq_u32(A, B) npyv_not_u32(_mm256_cmpeq_epi32(A, B)) +#define npyv_cmpneq_s32 npyv_cmpneq_u32 +#define npyv_cmpneq_u64(A, B) npyv_not_u64(_mm256_cmpeq_epi64(A, B)) +#define npyv_cmpneq_s64 npyv_cmpneq_u64 + +// signed greater than +#define npyv_cmpgt_s8 _mm256_cmpgt_epi8 +#define npyv_cmpgt_s16 _mm256_cmpgt_epi16 +#define npyv_cmpgt_s32 _mm256_cmpgt_epi32 +#define npyv_cmpgt_s64 _mm256_cmpgt_epi64 + +// signed greater than or equal +#define npyv_cmpge_s8(A, B) npyv_not_s8(_mm256_cmpgt_epi8(B, A)) +#define npyv_cmpge_s16(A, B) npyv_not_s16(_mm256_cmpgt_epi16(B, A)) +#define npyv_cmpge_s32(A, B) npyv_not_s32(_mm256_cmpgt_epi32(B, A)) +#define npyv_cmpge_s64(A, B) npyv_not_s64(_mm256_cmpgt_epi64(B, A)) + +// unsigned greater than +#define NPYV_IMPL_AVX2_UNSIGNED_GT(LEN, SIGN) \ + NPY_FINLINE __m256i npyv_cmpgt_u##LEN(__m256i a, __m256i b) \ + { \ + const __m256i sbit = _mm256_set1_epi32(SIGN); \ + return _mm256_cmpgt_epi##LEN( \ + _mm256_xor_si256(a, sbit), _mm256_xor_si256(b, sbit) \ + ); \ + } + +NPYV_IMPL_AVX2_UNSIGNED_GT(8, 0x80808080) +NPYV_IMPL_AVX2_UNSIGNED_GT(16, 0x80008000) +NPYV_IMPL_AVX2_UNSIGNED_GT(32, 0x80000000) + +NPY_FINLINE __m256i npyv_cmpgt_u64(__m256i a, __m256i b) +{ + const __m256i sbit = _mm256_set1_epi64x(0x8000000000000000); + return _mm256_cmpgt_epi64(_mm256_xor_si256(a, sbit), _mm256_xor_si256(b, sbit)); +} + +// unsigned greater than or equal +NPY_FINLINE __m256i npyv_cmpge_u8(__m256i a, __m256i b) +{ return _mm256_cmpeq_epi8(a, _mm256_max_epu8(a, b)); } +NPY_FINLINE __m256i npyv_cmpge_u16(__m256i a, __m256i b) +{ return _mm256_cmpeq_epi16(a, _mm256_max_epu16(a, b)); } +NPY_FINLINE __m256i npyv_cmpge_u32(__m256i a, __m256i b) +{ return _mm256_cmpeq_epi32(a, _mm256_max_epu32(a, b)); } +#define npyv_cmpge_u64(A, B) npyv_not_u64(npyv_cmpgt_u64(B, A)) + +// less than +#define npyv_cmplt_u8(A, B) npyv_cmpgt_u8(B, A) +#define npyv_cmplt_s8(A, B) npyv_cmpgt_s8(B, A) +#define npyv_cmplt_u16(A, B) npyv_cmpgt_u16(B, A) +#define npyv_cmplt_s16(A, B) npyv_cmpgt_s16(B, A) +#define npyv_cmplt_u32(A, B) npyv_cmpgt_u32(B, A) +#define npyv_cmplt_s32(A, B) npyv_cmpgt_s32(B, A) +#define npyv_cmplt_u64(A, B) npyv_cmpgt_u64(B, A) +#define npyv_cmplt_s64(A, B) npyv_cmpgt_s64(B, A) + +// less than or equal +#define npyv_cmple_u8(A, B) npyv_cmpge_u8(B, A) +#define npyv_cmple_s8(A, B) npyv_cmpge_s8(B, A) +#define npyv_cmple_u16(A, B) npyv_cmpge_u16(B, A) +#define npyv_cmple_s16(A, B) npyv_cmpge_s16(B, A) +#define npyv_cmple_u32(A, B) npyv_cmpge_u32(B, A) +#define npyv_cmple_s32(A, B) npyv_cmpge_s32(B, A) +#define npyv_cmple_u64(A, B) npyv_cmpge_u64(B, A) +#define npyv_cmple_s64(A, B) npyv_cmpge_s64(B, A) + +// precision comparison +#define npyv_cmpeq_f32(A, B) _mm256_castps_si256(_mm256_cmp_ps(A, B, _CMP_EQ_OQ)) +#define npyv_cmpeq_f64(A, B) _mm256_castpd_si256(_mm256_cmp_pd(A, B, _CMP_EQ_OQ)) +#define npyv_cmpneq_f32(A, B) _mm256_castps_si256(_mm256_cmp_ps(A, B, _CMP_NEQ_OQ)) +#define npyv_cmpneq_f64(A, B) _mm256_castpd_si256(_mm256_cmp_pd(A, B, _CMP_NEQ_OQ)) +#define npyv_cmplt_f32(A, B) _mm256_castps_si256(_mm256_cmp_ps(A, B, _CMP_LT_OQ)) +#define npyv_cmplt_f64(A, B) _mm256_castpd_si256(_mm256_cmp_pd(A, B, _CMP_LT_OQ)) +#define npyv_cmple_f32(A, B) _mm256_castps_si256(_mm256_cmp_ps(A, B, _CMP_LE_OQ)) +#define npyv_cmple_f64(A, B) _mm256_castpd_si256(_mm256_cmp_pd(A, B, _CMP_LE_OQ)) +#define npyv_cmpgt_f32(A, B) _mm256_castps_si256(_mm256_cmp_ps(A, B, _CMP_GT_OQ)) +#define npyv_cmpgt_f64(A, B) _mm256_castpd_si256(_mm256_cmp_pd(A, B, _CMP_GT_OQ)) +#define npyv_cmpge_f32(A, B) _mm256_castps_si256(_mm256_cmp_ps(A, B, _CMP_GE_OQ)) +#define npyv_cmpge_f64(A, B) _mm256_castpd_si256(_mm256_cmp_pd(A, B, _CMP_GE_OQ)) + +// check special cases +NPY_FINLINE npyv_b32 npyv_notnan_f32(npyv_f32 a) +{ return _mm256_castps_si256(_mm256_cmp_ps(a, a, _CMP_ORD_Q)); } +NPY_FINLINE npyv_b64 npyv_notnan_f64(npyv_f64 a) +{ return _mm256_castpd_si256(_mm256_cmp_pd(a, a, _CMP_ORD_Q)); } + +#endif // _NPY_SIMD_AVX2_OPERATORS_H diff --git a/numpy/core/src/common/simd/avx2/reorder.h b/numpy/core/src/common/simd/avx2/reorder.h new file mode 100644 index 000000000000..4d6ec8f759b5 --- /dev/null +++ b/numpy/core/src/common/simd/avx2/reorder.h @@ -0,0 +1,129 @@ +#ifndef NPY_SIMD + #error "Not a standalone header" +#endif + +#ifndef _NPY_SIMD_AVX2_REORDER_H +#define _NPY_SIMD_AVX2_REORDER_H + +// combine lower part of two vectors +#define npyv_combinel_u8(A, B) _mm256_permute2x128_si256(A, B, 0x20) +#define npyv_combinel_s8 npyv_combinel_u8 +#define npyv_combinel_u16 npyv_combinel_u8 +#define npyv_combinel_s16 npyv_combinel_u8 +#define npyv_combinel_u32 npyv_combinel_u8 +#define npyv_combinel_s32 npyv_combinel_u8 +#define npyv_combinel_u64 npyv_combinel_u8 +#define npyv_combinel_s64 npyv_combinel_u8 +#define npyv_combinel_f32(A, B) _mm256_permute2f128_ps(A, B, 0x20) +#define npyv_combinel_f64(A, B) _mm256_permute2f128_pd(A, B, 0x20) + +// combine higher part of two vectors +#define npyv_combineh_u8(A, B) _mm256_permute2x128_si256(A, B, 0x31) +#define npyv_combineh_s8 npyv_combineh_u8 +#define npyv_combineh_u16 npyv_combineh_u8 +#define npyv_combineh_s16 npyv_combineh_u8 +#define npyv_combineh_u32 npyv_combineh_u8 +#define npyv_combineh_s32 npyv_combineh_u8 +#define npyv_combineh_u64 npyv_combineh_u8 +#define npyv_combineh_s64 npyv_combineh_u8 +#define npyv_combineh_f32(A, B) _mm256_permute2f128_ps(A, B, 0x31) +#define npyv_combineh_f64(A, B) _mm256_permute2f128_pd(A, B, 0x31) + +// combine two vectors from lower and higher parts of two other vectors +NPY_FINLINE npyv_m256ix2 npyv__combine(__m256i a, __m256i b) +{ + npyv_m256ix2 r; + __m256i a1b0 = _mm256_permute2x128_si256(a, b, 0x21); + r.val[0] = _mm256_blend_epi32(a, a1b0, 0xF0); + r.val[1] = _mm256_blend_epi32(b, a1b0, 0xF); + return r; +} +NPY_FINLINE npyv_f32x2 npyv_combine_f32(__m256 a, __m256 b) +{ + npyv_f32x2 r; + __m256 a1b0 = _mm256_permute2f128_ps(a, b, 0x21); + r.val[0] = _mm256_blend_ps(a, a1b0, 0xF0); + r.val[1] = _mm256_blend_ps(b, a1b0, 0xF); + return r; +} +NPY_FINLINE npyv_f64x2 npyv_combine_f64(__m256d a, __m256d b) +{ + npyv_f64x2 r; + __m256d a1b0 = _mm256_permute2f128_pd(a, b, 0x21); + r.val[0] = _mm256_blend_pd(a, a1b0, 0xC); + r.val[1] = _mm256_blend_pd(b, a1b0, 0x3); + return r; +} +#define npyv_combine_u8 npyv__combine +#define npyv_combine_s8 npyv__combine +#define npyv_combine_u16 npyv__combine +#define npyv_combine_s16 npyv__combine +#define npyv_combine_u32 npyv__combine +#define npyv_combine_s32 npyv__combine +#define npyv_combine_u64 npyv__combine +#define npyv_combine_s64 npyv__combine + +// interleave two vectors +#define NPYV_IMPL_AVX2_ZIP_U(T_VEC, LEN) \ + NPY_FINLINE T_VEC##x2 npyv_zip_u##LEN(T_VEC a, T_VEC b) \ + { \ + __m256i ab0 = _mm256_unpacklo_epi##LEN(a, b); \ + __m256i ab1 = _mm256_unpackhi_epi##LEN(a, b); \ + return npyv__combine(ab0, ab1); \ + } + +NPYV_IMPL_AVX2_ZIP_U(npyv_u8, 8) +NPYV_IMPL_AVX2_ZIP_U(npyv_u16, 16) +NPYV_IMPL_AVX2_ZIP_U(npyv_u32, 32) +NPYV_IMPL_AVX2_ZIP_U(npyv_u64, 64) +#define npyv_zip_s8 npyv_zip_u8 +#define npyv_zip_s16 npyv_zip_u16 +#define npyv_zip_s32 npyv_zip_u32 +#define npyv_zip_s64 npyv_zip_u64 + +NPY_FINLINE npyv_f32x2 npyv_zip_f32(__m256 a, __m256 b) +{ + __m256 ab0 = _mm256_unpacklo_ps(a, b); + __m256 ab1 = _mm256_unpackhi_ps(a, b); + return npyv_combine_f32(ab0, ab1); +} +NPY_FINLINE npyv_f64x2 npyv_zip_f64(__m256d a, __m256d b) +{ + __m256d ab0 = _mm256_unpacklo_pd(a, b); + __m256d ab1 = _mm256_unpackhi_pd(a, b); + return npyv_combine_f64(ab0, ab1); +} + +// Reverse elements of each 64-bit lane +NPY_FINLINE npyv_u8 npyv_rev64_u8(npyv_u8 a) +{ + const __m256i idx = _mm256_setr_epi8( + 7, 6, 5, 4, 3, 2, 1, 0,/*64*/15, 14, 13, 12, 11, 10, 9, 8, + 7, 6, 5, 4, 3, 2, 1, 0,/*64*/15, 14, 13, 12, 11, 10, 9, 8 + ); + return _mm256_shuffle_epi8(a, idx); +} +#define npyv_rev64_s8 npyv_rev64_u8 + +NPY_FINLINE npyv_u16 npyv_rev64_u16(npyv_u16 a) +{ + const __m256i idx = _mm256_setr_epi8( + 6, 7, 4, 5, 2, 3, 0, 1,/*64*/14, 15, 12, 13, 10, 11, 8, 9, + 6, 7, 4, 5, 2, 3, 0, 1,/*64*/14, 15, 12, 13, 10, 11, 8, 9 + ); + return _mm256_shuffle_epi8(a, idx); +} +#define npyv_rev64_s16 npyv_rev64_u16 + +NPY_FINLINE npyv_u32 npyv_rev64_u32(npyv_u32 a) +{ + return _mm256_shuffle_epi32(a, _MM_SHUFFLE(2, 3, 0, 1)); +} +#define npyv_rev64_s32 npyv_rev64_u32 + +NPY_FINLINE npyv_f32 npyv_rev64_f32(npyv_f32 a) +{ + return _mm256_shuffle_ps(a, a, _MM_SHUFFLE(2, 3, 0, 1)); +} + +#endif // _NPY_SIMD_AVX2_REORDER_H diff --git a/numpy/core/src/common/simd/avx2/utils.h b/numpy/core/src/common/simd/avx2/utils.h new file mode 100644 index 000000000000..24f1af5d1bcf --- /dev/null +++ b/numpy/core/src/common/simd/avx2/utils.h @@ -0,0 +1,21 @@ +#ifndef NPY_SIMD + #error "Not a standalone header" +#endif + +#ifndef _NPY_SIMD_AVX2_UTILS_H +#define _NPY_SIMD_AVX2_UTILS_H + +#define npyv256_shuffle_odd(A) _mm256_permute4x64_epi64(A, _MM_SHUFFLE(3, 1, 2, 0)) +#define npyv256_shuffle_odd_ps(A) _mm256_castsi256_ps(npyv256_shuffle_odd(_mm256_castps_si256(A))) +#define npyv256_shuffle_odd_pd(A) _mm256_permute4x64_pd(A, _MM_SHUFFLE(3, 1, 2, 0)) + +NPY_FINLINE __m256i npyv256_mul_u8(__m256i a, __m256i b) +{ + const __m256i mask = _mm256_set1_epi32(0xFF00FF00); + __m256i even = _mm256_mullo_epi16(a, b); + __m256i odd = _mm256_mullo_epi16(_mm256_srai_epi16(a, 8), _mm256_srai_epi16(b, 8)); + odd = _mm256_slli_epi16(odd, 8); + return _mm256_blendv_epi8(even, odd, mask); +} + +#endif // _NPY_SIMD_AVX2_UTILS_H diff --git a/numpy/core/src/common/simd/avx512/arithmetic.h b/numpy/core/src/common/simd/avx512/arithmetic.h new file mode 100644 index 000000000000..f8632e701790 --- /dev/null +++ b/numpy/core/src/common/simd/avx512/arithmetic.h @@ -0,0 +1,441 @@ +#ifndef NPY_SIMD + #error "Not a standalone header" +#endif + +#ifndef _NPY_SIMD_AVX512_ARITHMETIC_H +#define _NPY_SIMD_AVX512_ARITHMETIC_H + +#include "../avx2/utils.h" +#include "../sse/utils.h" +/*************************** + * Addition + ***************************/ +// non-saturated +#ifdef NPY_HAVE_AVX512BW + #define npyv_add_u8 _mm512_add_epi8 + #define npyv_add_u16 _mm512_add_epi16 +#else + NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_add_u8, _mm256_add_epi8) + NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_add_u16, _mm256_add_epi16) +#endif +#define npyv_add_s8 npyv_add_u8 +#define npyv_add_s16 npyv_add_u16 +#define npyv_add_u32 _mm512_add_epi32 +#define npyv_add_s32 _mm512_add_epi32 +#define npyv_add_u64 _mm512_add_epi64 +#define npyv_add_s64 _mm512_add_epi64 +#define npyv_add_f32 _mm512_add_ps +#define npyv_add_f64 _mm512_add_pd + +// saturated +#ifdef NPY_HAVE_AVX512BW + #define npyv_adds_u8 _mm512_adds_epu8 + #define npyv_adds_s8 _mm512_adds_epi8 + #define npyv_adds_u16 _mm512_adds_epu16 + #define npyv_adds_s16 _mm512_adds_epi16 +#else + NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_adds_u8, _mm256_adds_epu8) + NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_adds_s8, _mm256_adds_epi8) + NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_adds_u16, _mm256_adds_epu16) + NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_adds_s16, _mm256_adds_epi16) +#endif +// TODO: rest, after implement Packs intrins + +/*************************** + * Subtraction + ***************************/ +// non-saturated +#ifdef NPY_HAVE_AVX512BW + #define npyv_sub_u8 _mm512_sub_epi8 + #define npyv_sub_u16 _mm512_sub_epi16 +#else + NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_sub_u8, _mm256_sub_epi8) + NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_sub_u16, _mm256_sub_epi16) +#endif +#define npyv_sub_s8 npyv_sub_u8 +#define npyv_sub_s16 npyv_sub_u16 +#define npyv_sub_u32 _mm512_sub_epi32 +#define npyv_sub_s32 _mm512_sub_epi32 +#define npyv_sub_u64 _mm512_sub_epi64 +#define npyv_sub_s64 _mm512_sub_epi64 +#define npyv_sub_f32 _mm512_sub_ps +#define npyv_sub_f64 _mm512_sub_pd + +// saturated +#ifdef NPY_HAVE_AVX512BW + #define npyv_subs_u8 _mm512_subs_epu8 + #define npyv_subs_s8 _mm512_subs_epi8 + #define npyv_subs_u16 _mm512_subs_epu16 + #define npyv_subs_s16 _mm512_subs_epi16 +#else + NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_subs_u8, _mm256_subs_epu8) + NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_subs_s8, _mm256_subs_epi8) + NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_subs_u16, _mm256_subs_epu16) + NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_subs_s16, _mm256_subs_epi16) +#endif +// TODO: rest, after implement Packs intrins + +/*************************** + * Multiplication + ***************************/ +// non-saturated +#ifdef NPY_HAVE_AVX512BW +NPY_FINLINE __m512i npyv_mul_u8(__m512i a, __m512i b) +{ + __m512i even = _mm512_mullo_epi16(a, b); + __m512i odd = _mm512_mullo_epi16(_mm512_srai_epi16(a, 8), _mm512_srai_epi16(b, 8)); + odd = _mm512_slli_epi16(odd, 8); + return _mm512_mask_blend_epi8(0xAAAAAAAAAAAAAAAA, even, odd); +} +#else + NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_mul_u8, npyv256_mul_u8) +#endif + +#ifdef NPY_HAVE_AVX512BW + #define npyv_mul_u16 _mm512_mullo_epi16 +#else + NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_mul_u16, _mm256_mullo_epi16) +#endif +#define npyv_mul_s8 npyv_mul_u8 +#define npyv_mul_s16 npyv_mul_u16 +#define npyv_mul_u32 _mm512_mullo_epi32 +#define npyv_mul_s32 _mm512_mullo_epi32 +#define npyv_mul_f32 _mm512_mul_ps +#define npyv_mul_f64 _mm512_mul_pd + +// saturated +// TODO: after implement Packs intrins + +/*************************** + * Integer Division + ***************************/ +// See simd/intdiv.h for more clarification +// divide each unsigned 8-bit element by divisor +NPY_FINLINE npyv_u8 npyv_divc_u8(npyv_u8 a, const npyv_u8x3 divisor) +{ + const __m128i shf1 = _mm512_castsi512_si128(divisor.val[1]); + const __m128i shf2 = _mm512_castsi512_si128(divisor.val[2]); +#ifdef NPY_HAVE_AVX512BW + const __m512i bmask = _mm512_set1_epi32(0x00FF00FF); + const __m512i shf1b = _mm512_set1_epi8(0xFFU >> _mm_cvtsi128_si32(shf1)); + const __m512i shf2b = _mm512_set1_epi8(0xFFU >> _mm_cvtsi128_si32(shf2)); + // high part of unsigned multiplication + __m512i mulhi_even = _mm512_mullo_epi16(_mm512_and_si512(a, bmask), divisor.val[0]); + mulhi_even = _mm512_srli_epi16(mulhi_even, 8); + __m512i mulhi_odd = _mm512_mullo_epi16(_mm512_srli_epi16(a, 8), divisor.val[0]); + __m512i mulhi = _mm512_mask_mov_epi8(mulhi_even, 0xAAAAAAAAAAAAAAAA, mulhi_odd); + // floor(a/d) = (mulhi + ((a-mulhi) >> sh1)) >> sh2 + __m512i q = _mm512_sub_epi8(a, mulhi); + q = _mm512_and_si512(_mm512_srl_epi16(q, shf1), shf1b); + q = _mm512_add_epi8(mulhi, q); + q = _mm512_and_si512(_mm512_srl_epi16(q, shf2), shf2b); + return q; +#else + const __m256i bmask = _mm256_set1_epi32(0x00FF00FF); + const __m256i shf1b = _mm256_set1_epi8(0xFFU >> _mm_cvtsi128_si32(shf1)); + const __m256i shf2b = _mm256_set1_epi8(0xFFU >> _mm_cvtsi128_si32(shf2)); + const __m512i shf2bw= npyv512_combine_si256(shf2b, shf2b); + const __m256i mulc = npyv512_lower_si256(divisor.val[0]); + //// lower 256-bit + __m256i lo_a = npyv512_lower_si256(a); + // high part of unsigned multiplication + __m256i mulhi_even = _mm256_mullo_epi16(_mm256_and_si256(lo_a, bmask), mulc); + mulhi_even = _mm256_srli_epi16(mulhi_even, 8); + __m256i mulhi_odd = _mm256_mullo_epi16(_mm256_srli_epi16(lo_a, 8), mulc); + __m256i mulhi = _mm256_blendv_epi8(mulhi_odd, mulhi_even, bmask); + // floor(a/d) = (mulhi + ((a-mulhi) >> sh1)) >> sh2 + __m256i lo_q = _mm256_sub_epi8(lo_a, mulhi); + lo_q = _mm256_and_si256(_mm256_srl_epi16(lo_q, shf1), shf1b); + lo_q = _mm256_add_epi8(mulhi, lo_q); + lo_q = _mm256_srl_epi16(lo_q, shf2); // no sign extend + + //// higher 256-bit + __m256i hi_a = npyv512_higher_si256(a); + // high part of unsigned multiplication + mulhi_even = _mm256_mullo_epi16(_mm256_and_si256(hi_a, bmask), mulc); + mulhi_even = _mm256_srli_epi16(mulhi_even, 8); + mulhi_odd = _mm256_mullo_epi16(_mm256_srli_epi16(hi_a, 8), mulc); + mulhi = _mm256_blendv_epi8(mulhi_odd, mulhi_even, bmask); + // floor(a/d) = (mulhi + ((a-mulhi) >> sh1)) >> sh2 + __m256i hi_q = _mm256_sub_epi8(hi_a, mulhi); + hi_q = _mm256_and_si256(_mm256_srl_epi16(hi_q, shf1), shf1b); + hi_q = _mm256_add_epi8(mulhi, hi_q); + hi_q = _mm256_srl_epi16(hi_q, shf2); // no sign extend + return _mm512_and_si512(npyv512_combine_si256(lo_q, hi_q), shf2bw); // extend sign +#endif +} +// divide each signed 8-bit element by divisor (round towards zero) +NPY_FINLINE npyv_s16 npyv_divc_s16(npyv_s16 a, const npyv_s16x3 divisor); +NPY_FINLINE npyv_s8 npyv_divc_s8(npyv_s8 a, const npyv_s8x3 divisor) +{ + __m512i divc_even = npyv_divc_s16(npyv_shri_s16(npyv_shli_s16(a, 8), 8), divisor); + __m512i divc_odd = npyv_divc_s16(npyv_shri_s16(a, 8), divisor); + divc_odd = npyv_shli_s16(divc_odd, 8); +#ifdef NPY_HAVE_AVX512BW + return _mm512_mask_mov_epi8(divc_even, 0xAAAAAAAAAAAAAAAA, divc_odd); +#else + const __m512i bmask = _mm512_set1_epi32(0x00FF00FF); + return npyv_select_u8(bmask, divc_even, divc_odd); +#endif +} +// divide each unsigned 16-bit element by divisor +NPY_FINLINE npyv_u16 npyv_divc_u16(npyv_u16 a, const npyv_u16x3 divisor) +{ + const __m128i shf1 = _mm512_castsi512_si128(divisor.val[1]); + const __m128i shf2 = _mm512_castsi512_si128(divisor.val[2]); + // floor(a/d) = (mulhi + ((a-mulhi) >> sh1)) >> sh2 + #define NPYV__DIVC_U16(RLEN, A, MULC, R) \ + mulhi = _mm##RLEN##_mulhi_epu16(A, MULC); \ + R = _mm##RLEN##_sub_epi16(A, mulhi); \ + R = _mm##RLEN##_srl_epi16(R, shf1); \ + R = _mm##RLEN##_add_epi16(mulhi, R); \ + R = _mm##RLEN##_srl_epi16(R, shf2); + +#ifdef NPY_HAVE_AVX512BW + __m512i mulhi, q; + NPYV__DIVC_U16(512, a, divisor.val[0], q) + return q; +#else + const __m256i m = npyv512_lower_si256(divisor.val[0]); + __m256i lo_a = npyv512_lower_si256(a); + __m256i hi_a = npyv512_higher_si256(a); + + __m256i mulhi, lo_q, hi_q; + NPYV__DIVC_U16(256, lo_a, m, lo_q) + NPYV__DIVC_U16(256, hi_a, m, hi_q) + return npyv512_combine_si256(lo_q, hi_q); +#endif + #undef NPYV__DIVC_U16 +} +// divide each signed 16-bit element by divisor (round towards zero) +NPY_FINLINE npyv_s16 npyv_divc_s16(npyv_s16 a, const npyv_s16x3 divisor) +{ + const __m128i shf1 = _mm512_castsi512_si128(divisor.val[1]); + // q = ((a + mulhi) >> sh1) - XSIGN(a) + // trunc(a/d) = (q ^ dsign) - dsign + #define NPYV__DIVC_S16(RLEN, A, MULC, DSIGN, R) \ + mulhi = _mm##RLEN##_mulhi_epi16(A, MULC); \ + R = _mm##RLEN##_sra_epi16(_mm##RLEN##_add_epi16(A, mulhi), shf1); \ + R = _mm##RLEN##_sub_epi16(R, _mm##RLEN##_srai_epi16(A, 15)); \ + R = _mm##RLEN##_sub_epi16(_mm##RLEN##_xor_si##RLEN(R, DSIGN), DSIGN); + +#ifdef NPY_HAVE_AVX512BW + __m512i mulhi, q; + NPYV__DIVC_S16(512, a, divisor.val[0], divisor.val[2], q) + return q; +#else + const __m256i m = npyv512_lower_si256(divisor.val[0]); + const __m256i dsign = npyv512_lower_si256(divisor.val[2]); + __m256i lo_a = npyv512_lower_si256(a); + __m256i hi_a = npyv512_higher_si256(a); + + __m256i mulhi, lo_q, hi_q; + NPYV__DIVC_S16(256, lo_a, m, dsign, lo_q) + NPYV__DIVC_S16(256, hi_a, m, dsign, hi_q) + return npyv512_combine_si256(lo_q, hi_q); +#endif + #undef NPYV__DIVC_S16 +} +// divide each unsigned 32-bit element by divisor +NPY_FINLINE npyv_u32 npyv_divc_u32(npyv_u32 a, const npyv_u32x3 divisor) +{ + const __m128i shf1 = _mm512_castsi512_si128(divisor.val[1]); + const __m128i shf2 = _mm512_castsi512_si128(divisor.val[2]); + // high part of unsigned multiplication + __m512i mulhi_even = _mm512_srli_epi64(_mm512_mul_epu32(a, divisor.val[0]), 32); + __m512i mulhi_odd = _mm512_mul_epu32(_mm512_srli_epi64(a, 32), divisor.val[0]); + __m512i mulhi = _mm512_mask_mov_epi32(mulhi_even, 0xAAAA, mulhi_odd); + // floor(a/d) = (mulhi + ((a-mulhi) >> sh1)) >> sh2 + __m512i q = _mm512_sub_epi32(a, mulhi); + q = _mm512_srl_epi32(q, shf1); + q = _mm512_add_epi32(mulhi, q); + q = _mm512_srl_epi32(q, shf2); + return q; +} +// divide each signed 32-bit element by divisor (round towards zero) +NPY_FINLINE npyv_s32 npyv_divc_s32(npyv_s32 a, const npyv_s32x3 divisor) +{ + const __m128i shf1 = _mm512_castsi512_si128(divisor.val[1]); + // high part of signed multiplication + __m512i mulhi_even = _mm512_srli_epi64(_mm512_mul_epi32(a, divisor.val[0]), 32); + __m512i mulhi_odd = _mm512_mul_epi32(_mm512_srli_epi64(a, 32), divisor.val[0]); + __m512i mulhi = _mm512_mask_mov_epi32(mulhi_even, 0xAAAA, mulhi_odd); + // q = ((a + mulhi) >> sh1) - XSIGN(a) + // trunc(a/d) = (q ^ dsign) - dsign + __m512i q = _mm512_sra_epi32(_mm512_add_epi32(a, mulhi), shf1); + q = _mm512_sub_epi32(q, _mm512_srai_epi32(a, 31)); + q = _mm512_sub_epi32(_mm512_xor_si512(q, divisor.val[2]), divisor.val[2]); + return q; +} +// returns the high 64 bits of unsigned 64-bit multiplication +// xref https://stackoverflow.com/a/28827013 +NPY_FINLINE npyv_u64 npyv__mullhi_u64(npyv_u64 a, npyv_u64 b) +{ + __m512i lomask = npyv_setall_s64(0xffffffff); + __m512i a_hi = _mm512_srli_epi64(a, 32); // a0l, a0h, a1l, a1h + __m512i b_hi = _mm512_srli_epi64(b, 32); // b0l, b0h, b1l, b1h + // compute partial products + __m512i w0 = _mm512_mul_epu32(a, b); // a0l*b0l, a1l*b1l + __m512i w1 = _mm512_mul_epu32(a, b_hi); // a0l*b0h, a1l*b1h + __m512i w2 = _mm512_mul_epu32(a_hi, b); // a0h*b0l, a1h*b0l + __m512i w3 = _mm512_mul_epu32(a_hi, b_hi); // a0h*b0h, a1h*b1h + // sum partial products + __m512i w0h = _mm512_srli_epi64(w0, 32); + __m512i s1 = _mm512_add_epi64(w1, w0h); + __m512i s1l = _mm512_and_si512(s1, lomask); + __m512i s1h = _mm512_srli_epi64(s1, 32); + + __m512i s2 = _mm512_add_epi64(w2, s1l); + __m512i s2h = _mm512_srli_epi64(s2, 32); + + __m512i hi = _mm512_add_epi64(w3, s1h); + hi = _mm512_add_epi64(hi, s2h); + return hi; +} +// divide each unsigned 64-bit element by a divisor +NPY_FINLINE npyv_u64 npyv_divc_u64(npyv_u64 a, const npyv_u64x3 divisor) +{ + const __m128i shf1 = _mm512_castsi512_si128(divisor.val[1]); + const __m128i shf2 = _mm512_castsi512_si128(divisor.val[2]); + // high part of unsigned multiplication + __m512i mulhi = npyv__mullhi_u64(a, divisor.val[0]); + // floor(a/d) = (mulhi + ((a-mulhi) >> sh1)) >> sh2 + __m512i q = _mm512_sub_epi64(a, mulhi); + q = _mm512_srl_epi64(q, shf1); + q = _mm512_add_epi64(mulhi, q); + q = _mm512_srl_epi64(q, shf2); + return q; +} +// divide each unsigned 64-bit element by a divisor (round towards zero) +NPY_FINLINE npyv_s64 npyv_divc_s64(npyv_s64 a, const npyv_s64x3 divisor) +{ + const __m128i shf1 = _mm512_castsi512_si128(divisor.val[1]); + // high part of unsigned multiplication + __m512i mulhi = npyv__mullhi_u64(a, divisor.val[0]); + // convert unsigned to signed high multiplication + // mulhi - ((a < 0) ? m : 0) - ((m < 0) ? a : 0); + __m512i asign = _mm512_srai_epi64(a, 63); + __m512i msign = _mm512_srai_epi64(divisor.val[0], 63); + __m512i m_asign = _mm512_and_si512(divisor.val[0], asign); + __m512i a_msign = _mm512_and_si512(a, msign); + mulhi = _mm512_sub_epi64(mulhi, m_asign); + mulhi = _mm512_sub_epi64(mulhi, a_msign); + // q = ((a + mulhi) >> sh1) - XSIGN(a) + // trunc(a/d) = (q ^ dsign) - dsign + __m512i q = _mm512_sra_epi64(_mm512_add_epi64(a, mulhi), shf1); + q = _mm512_sub_epi64(q, asign); + q = _mm512_sub_epi64(_mm512_xor_si512(q, divisor.val[2]), divisor.val[2]); + return q; +} +/*************************** + * Division + ***************************/ +// TODO: emulate integer division +#define npyv_div_f32 _mm512_div_ps +#define npyv_div_f64 _mm512_div_pd + +/*************************** + * FUSED + ***************************/ +// multiply and add, a*b + c +#define npyv_muladd_f32 _mm512_fmadd_ps +#define npyv_muladd_f64 _mm512_fmadd_pd +// multiply and subtract, a*b - c +#define npyv_mulsub_f32 _mm512_fmsub_ps +#define npyv_mulsub_f64 _mm512_fmsub_pd +// negate multiply and add, -(a*b) + c +#define npyv_nmuladd_f32 _mm512_fnmadd_ps +#define npyv_nmuladd_f64 _mm512_fnmadd_pd +// negate multiply and subtract, -(a*b) - c +#define npyv_nmulsub_f32 _mm512_fnmsub_ps +#define npyv_nmulsub_f64 _mm512_fnmsub_pd + +/*************************** + * Summation: Calculates the sum of all vector elements. + * there are three ways to implement reduce sum for AVX512: + * 1- split(256) /add /split(128) /add /hadd /hadd /extract + * 2- shuff(cross) /add /shuff(cross) /add /shuff /add /shuff /add /extract + * 3- _mm512_reduce_add_ps/pd + * The first one is been widely used by many projects + * + * the second one is used by Intel Compiler, maybe because the + * latency of hadd increased by (2-3) starting from Skylake-X which makes two + * extra shuffles(non-cross) cheaper. check https://godbolt.org/z/s3G9Er for more info. + * + * The third one is almost the same as the second one but only works for + * intel compiler/GCC 7.1/Clang 4, we still need to support older GCC. + ***************************/ +// reduce sum across vector +#ifdef NPY_HAVE_AVX512F_REDUCE + #define npyv_sum_u32 _mm512_reduce_add_epi32 + #define npyv_sum_u64 _mm512_reduce_add_epi64 + #define npyv_sum_f32 _mm512_reduce_add_ps + #define npyv_sum_f64 _mm512_reduce_add_pd +#else + NPY_FINLINE npy_uint32 npyv_sum_u32(npyv_u32 a) + { + __m256i half = _mm256_add_epi32(npyv512_lower_si256(a), npyv512_higher_si256(a)); + __m128i quarter = _mm_add_epi32(_mm256_castsi256_si128(half), _mm256_extracti128_si256(half, 1)); + quarter = _mm_hadd_epi32(quarter, quarter); + return _mm_cvtsi128_si32(_mm_hadd_epi32(quarter, quarter)); + } + + NPY_FINLINE npy_uint64 npyv_sum_u64(npyv_u64 a) + { + __m256i four = _mm256_add_epi64(npyv512_lower_si256(a), npyv512_higher_si256(a)); + __m256i two = _mm256_add_epi64(four, _mm256_shuffle_epi32(four, _MM_SHUFFLE(1, 0, 3, 2))); + __m128i one = _mm_add_epi64(_mm256_castsi256_si128(two), _mm256_extracti128_si256(two, 1)); + return (npy_uint64)npyv128_cvtsi128_si64(one); + } + + NPY_FINLINE float npyv_sum_f32(npyv_f32 a) + { + __m512 h64 = _mm512_shuffle_f32x4(a, a, _MM_SHUFFLE(3, 2, 3, 2)); + __m512 sum32 = _mm512_add_ps(a, h64); + __m512 h32 = _mm512_shuffle_f32x4(sum32, sum32, _MM_SHUFFLE(1, 0, 3, 2)); + __m512 sum16 = _mm512_add_ps(sum32, h32); + __m512 h16 = _mm512_permute_ps(sum16, _MM_SHUFFLE(1, 0, 3, 2)); + __m512 sum8 = _mm512_add_ps(sum16, h16); + __m512 h4 = _mm512_permute_ps(sum8, _MM_SHUFFLE(2, 3, 0, 1)); + __m512 sum4 = _mm512_add_ps(sum8, h4); + return _mm_cvtss_f32(_mm512_castps512_ps128(sum4)); + } + + NPY_FINLINE double npyv_sum_f64(npyv_f64 a) + { + __m512d h64 = _mm512_shuffle_f64x2(a, a, _MM_SHUFFLE(3, 2, 3, 2)); + __m512d sum32 = _mm512_add_pd(a, h64); + __m512d h32 = _mm512_permutex_pd(sum32, _MM_SHUFFLE(1, 0, 3, 2)); + __m512d sum16 = _mm512_add_pd(sum32, h32); + __m512d h16 = _mm512_permute_pd(sum16, _MM_SHUFFLE(2, 3, 0, 1)); + __m512d sum8 = _mm512_add_pd(sum16, h16); + return _mm_cvtsd_f64(_mm512_castpd512_pd128(sum8)); + } +#endif + +// expand the source vector and performs sum reduce +NPY_FINLINE npy_uint16 npyv_sumup_u8(npyv_u8 a) +{ +#ifdef NPY_HAVE_AVX512BW + __m512i eight = _mm512_sad_epu8(a, _mm512_setzero_si512()); + __m256i four = _mm256_add_epi16(npyv512_lower_si256(eight), npyv512_higher_si256(eight)); +#else + __m256i lo_four = _mm256_sad_epu8(npyv512_lower_si256(a), _mm256_setzero_si256()); + __m256i hi_four = _mm256_sad_epu8(npyv512_higher_si256(a), _mm256_setzero_si256()); + __m256i four = _mm256_add_epi16(lo_four, hi_four); +#endif + __m128i two = _mm_add_epi16(_mm256_castsi256_si128(four), _mm256_extracti128_si256(four, 1)); + __m128i one = _mm_add_epi16(two, _mm_unpackhi_epi64(two, two)); + return (npy_uint16)_mm_cvtsi128_si32(one); +} + +NPY_FINLINE npy_uint32 npyv_sumup_u16(npyv_u16 a) +{ + const npyv_u16 even_mask = _mm512_set1_epi32(0x0000FFFF); + __m512i even = _mm512_and_si512(a, even_mask); + __m512i odd = _mm512_srli_epi32(a, 16); + __m512i ff = _mm512_add_epi32(even, odd); + return npyv_sum_u32(ff); +} + +#endif // _NPY_SIMD_AVX512_ARITHMETIC_H diff --git a/numpy/core/src/common/simd/avx512/avx512.h b/numpy/core/src/common/simd/avx512/avx512.h new file mode 100644 index 000000000000..f38686834cfb --- /dev/null +++ b/numpy/core/src/common/simd/avx512/avx512.h @@ -0,0 +1,77 @@ +#ifndef _NPY_SIMD_H_ + #error "Not a standalone header" +#endif +#define NPY_SIMD 512 +#define NPY_SIMD_WIDTH 64 +#define NPY_SIMD_F64 1 +#define NPY_SIMD_FMA3 1 // native support +// Enough limit to allow us to use _mm512_i32gather_* and _mm512_i32scatter_* +#define NPY_SIMD_MAXLOAD_STRIDE32 (0x7fffffff / 16) +#define NPY_SIMD_MAXSTORE_STRIDE32 (0x7fffffff / 16) + +typedef __m512i npyv_u8; +typedef __m512i npyv_s8; +typedef __m512i npyv_u16; +typedef __m512i npyv_s16; +typedef __m512i npyv_u32; +typedef __m512i npyv_s32; +typedef __m512i npyv_u64; +typedef __m512i npyv_s64; +typedef __m512 npyv_f32; +typedef __m512d npyv_f64; + +#ifdef NPY_HAVE_AVX512BW +typedef __mmask64 npyv_b8; +typedef __mmask32 npyv_b16; +#else +typedef __m512i npyv_b8; +typedef __m512i npyv_b16; +#endif +typedef __mmask16 npyv_b32; +typedef __mmask8 npyv_b64; + +typedef struct { __m512i val[2]; } npyv_m512ix2; +typedef npyv_m512ix2 npyv_u8x2; +typedef npyv_m512ix2 npyv_s8x2; +typedef npyv_m512ix2 npyv_u16x2; +typedef npyv_m512ix2 npyv_s16x2; +typedef npyv_m512ix2 npyv_u32x2; +typedef npyv_m512ix2 npyv_s32x2; +typedef npyv_m512ix2 npyv_u64x2; +typedef npyv_m512ix2 npyv_s64x2; + +typedef struct { __m512i val[3]; } npyv_m512ix3; +typedef npyv_m512ix3 npyv_u8x3; +typedef npyv_m512ix3 npyv_s8x3; +typedef npyv_m512ix3 npyv_u16x3; +typedef npyv_m512ix3 npyv_s16x3; +typedef npyv_m512ix3 npyv_u32x3; +typedef npyv_m512ix3 npyv_s32x3; +typedef npyv_m512ix3 npyv_u64x3; +typedef npyv_m512ix3 npyv_s64x3; + +typedef struct { __m512 val[2]; } npyv_f32x2; +typedef struct { __m512d val[2]; } npyv_f64x2; +typedef struct { __m512 val[3]; } npyv_f32x3; +typedef struct { __m512d val[3]; } npyv_f64x3; + +#define npyv_nlanes_u8 64 +#define npyv_nlanes_s8 64 +#define npyv_nlanes_u16 32 +#define npyv_nlanes_s16 32 +#define npyv_nlanes_u32 16 +#define npyv_nlanes_s32 16 +#define npyv_nlanes_u64 8 +#define npyv_nlanes_s64 8 +#define npyv_nlanes_f32 16 +#define npyv_nlanes_f64 8 + +#include "utils.h" +#include "memory.h" +#include "misc.h" +#include "reorder.h" +#include "operators.h" +#include "conversion.h" +#include "arithmetic.h" +#include "math.h" +#include "maskop.h" diff --git a/numpy/core/src/common/simd/avx512/conversion.h b/numpy/core/src/common/simd/avx512/conversion.h new file mode 100644 index 000000000000..0bd44179b332 --- /dev/null +++ b/numpy/core/src/common/simd/avx512/conversion.h @@ -0,0 +1,138 @@ +#ifndef NPY_SIMD + #error "Not a standalone header" +#endif + +#ifndef _NPY_SIMD_AVX512_CVT_H +#define _NPY_SIMD_AVX512_CVT_H + +// convert mask to integer vectors +#ifdef NPY_HAVE_AVX512BW + #define npyv_cvt_u8_b8 _mm512_movm_epi8 + #define npyv_cvt_u16_b16 _mm512_movm_epi16 +#else + #define npyv_cvt_u8_b8(BL) BL + #define npyv_cvt_u16_b16(BL) BL +#endif +#define npyv_cvt_s8_b8 npyv_cvt_u8_b8 +#define npyv_cvt_s16_b16 npyv_cvt_u16_b16 + +#ifdef NPY_HAVE_AVX512DQ + #define npyv_cvt_u32_b32 _mm512_movm_epi32 + #define npyv_cvt_u64_b64 _mm512_movm_epi64 +#else + #define npyv_cvt_u32_b32(BL) _mm512_maskz_set1_epi32(BL, (int)-1) + #define npyv_cvt_u64_b64(BL) _mm512_maskz_set1_epi64(BL, (npy_int64)-1) +#endif +#define npyv_cvt_s32_b32 npyv_cvt_u32_b32 +#define npyv_cvt_s64_b64 npyv_cvt_u64_b64 +#define npyv_cvt_f32_b32(BL) _mm512_castsi512_ps(npyv_cvt_u32_b32(BL)) +#define npyv_cvt_f64_b64(BL) _mm512_castsi512_pd(npyv_cvt_u64_b64(BL)) + +// convert integer vectors to mask +#ifdef NPY_HAVE_AVX512BW + #define npyv_cvt_b8_u8 _mm512_movepi8_mask + #define npyv_cvt_b16_u16 _mm512_movepi16_mask +#else + #define npyv_cvt_b8_u8(A) A + #define npyv_cvt_b16_u16(A) A +#endif +#define npyv_cvt_b8_s8 npyv_cvt_b8_u8 +#define npyv_cvt_b16_s16 npyv_cvt_b16_u16 + +#ifdef NPY_HAVE_AVX512DQ + #define npyv_cvt_b32_u32 _mm512_movepi32_mask + #define npyv_cvt_b64_u64 _mm512_movepi64_mask +#else + #define npyv_cvt_b32_u32(A) _mm512_cmpneq_epu32_mask(A, _mm512_setzero_si512()) + #define npyv_cvt_b64_u64(A) _mm512_cmpneq_epu64_mask(A, _mm512_setzero_si512()) +#endif +#define npyv_cvt_b32_s32 npyv_cvt_b32_u32 +#define npyv_cvt_b64_s64 npyv_cvt_b64_u64 +#define npyv_cvt_b32_f32(A) npyv_cvt_b32_u32(_mm512_castps_si512(A)) +#define npyv_cvt_b64_f64(A) npyv_cvt_b64_u64(_mm512_castpd_si512(A)) + +// expand +NPY_FINLINE npyv_u16x2 npyv_expand_u16_u8(npyv_u8 data) +{ + npyv_u16x2 r; + __m256i lo = npyv512_lower_si256(data); + __m256i hi = npyv512_higher_si256(data); +#ifdef NPY_HAVE_AVX512BW + r.val[0] = _mm512_cvtepu8_epi16(lo); + r.val[1] = _mm512_cvtepu8_epi16(hi); +#else + __m256i loelo = _mm256_cvtepu8_epi16(_mm256_castsi256_si128(lo)); + __m256i loehi = _mm256_cvtepu8_epi16(_mm256_extracti128_si256(lo, 1)); + __m256i hielo = _mm256_cvtepu8_epi16(_mm256_castsi256_si128(hi)); + __m256i hiehi = _mm256_cvtepu8_epi16(_mm256_extracti128_si256(hi, 1)); + r.val[0] = npyv512_combine_si256(loelo, loehi); + r.val[1] = npyv512_combine_si256(hielo, hiehi); +#endif + return r; +} + +NPY_FINLINE npyv_u32x2 npyv_expand_u32_u16(npyv_u16 data) +{ + npyv_u32x2 r; + __m256i lo = npyv512_lower_si256(data); + __m256i hi = npyv512_higher_si256(data); +#ifdef NPY_HAVE_AVX512BW + r.val[0] = _mm512_cvtepu16_epi32(lo); + r.val[1] = _mm512_cvtepu16_epi32(hi); +#else + __m256i loelo = _mm256_cvtepu16_epi32(_mm256_castsi256_si128(lo)); + __m256i loehi = _mm256_cvtepu16_epi32(_mm256_extracti128_si256(lo, 1)); + __m256i hielo = _mm256_cvtepu16_epi32(_mm256_castsi256_si128(hi)); + __m256i hiehi = _mm256_cvtepu16_epi32(_mm256_extracti128_si256(hi, 1)); + r.val[0] = npyv512_combine_si256(loelo, loehi); + r.val[1] = npyv512_combine_si256(hielo, hiehi); +#endif + return r; +} + +// convert boolean vectors to integer bitfield +NPY_FINLINE npy_uint64 npyv_tobits_b8(npyv_b8 a) +{ +#ifdef NPY_HAVE_AVX512BW_MASK + return (npy_uint64)_cvtmask64_u64(a); +#elif defined(NPY_HAVE_AVX512BW) + return (npy_uint64)a; +#else + int mask_lo = _mm256_movemask_epi8(npyv512_lower_si256(a)); + int mask_hi = _mm256_movemask_epi8(npyv512_higher_si256(a)); + return (unsigned)mask_lo | ((npy_uint64)(unsigned)mask_hi << 32); +#endif +} +NPY_FINLINE npy_uint64 npyv_tobits_b16(npyv_b16 a) +{ +#ifdef NPY_HAVE_AVX512BW_MASK + return (npy_uint32)_cvtmask32_u32(a); +#elif defined(NPY_HAVE_AVX512BW) + return (npy_uint32)a; +#else + __m256i pack = _mm256_packs_epi16( + npyv512_lower_si256(a), npyv512_higher_si256(a) + ); + return (npy_uint32)_mm256_movemask_epi8(_mm256_permute4x64_epi64(pack, _MM_SHUFFLE(3, 1, 2, 0))); +#endif +} +NPY_FINLINE npy_uint64 npyv_tobits_b32(npyv_b32 a) +{ return (npy_uint16)a; } +NPY_FINLINE npy_uint64 npyv_tobits_b64(npyv_b64 a) +{ +#ifdef NPY_HAVE_AVX512DQ_MASK + return _cvtmask8_u32(a); +#else + return (npy_uint8)a; +#endif +} + +// round to nearest integer (assuming even) +#define npyv_round_s32_f32 _mm512_cvtps_epi32 +NPY_FINLINE npyv_s32 npyv_round_s32_f64(npyv_f64 a, npyv_f64 b) +{ + __m256i lo = _mm512_cvtpd_epi32(a), hi = _mm512_cvtpd_epi32(b); + return npyv512_combine_si256(lo, hi); +} + +#endif // _NPY_SIMD_AVX512_CVT_H diff --git a/numpy/core/src/common/simd/avx512/maskop.h b/numpy/core/src/common/simd/avx512/maskop.h new file mode 100644 index 000000000000..d1c188390a11 --- /dev/null +++ b/numpy/core/src/common/simd/avx512/maskop.h @@ -0,0 +1,54 @@ +#ifndef NPY_SIMD + #error "Not a standalone header, use simd/simd.h instead" +#endif + +#ifndef _NPY_SIMD_AVX512_MASKOP_H +#define _NPY_SIMD_AVX512_MASKOP_H + +/** + * Implements conditional addition and subtraction. + * e.g. npyv_ifadd_f32(m, a, b, c) -> m ? a + b : c + * e.g. npyv_ifsub_f32(m, a, b, c) -> m ? a - b : c + */ +#define NPYV_IMPL_AVX512_EMULATE_MASK_ADDSUB(SFX, BSFX) \ + NPY_FINLINE npyv_##SFX npyv_ifadd_##SFX \ + (npyv_##BSFX m, npyv_##SFX a, npyv_##SFX b, npyv_##SFX c) \ + { \ + npyv_##SFX add = npyv_add_##SFX(a, b); \ + return npyv_select_##SFX(m, add, c); \ + } \ + NPY_FINLINE npyv_##SFX npyv_ifsub_##SFX \ + (npyv_##BSFX m, npyv_##SFX a, npyv_##SFX b, npyv_##SFX c) \ + { \ + npyv_##SFX sub = npyv_sub_##SFX(a, b); \ + return npyv_select_##SFX(m, sub, c); \ + } + +#define NPYV_IMPL_AVX512_MASK_ADDSUB(SFX, BSFX, ZSFX) \ + NPY_FINLINE npyv_##SFX npyv_ifadd_##SFX \ + (npyv_##BSFX m, npyv_##SFX a, npyv_##SFX b, npyv_##SFX c) \ + { return _mm512_mask_add_##ZSFX(c, m, a, b); } \ + NPY_FINLINE npyv_##SFX npyv_ifsub_##SFX \ + (npyv_##BSFX m, npyv_##SFX a, npyv_##SFX b, npyv_##SFX c) \ + { return _mm512_mask_sub_##ZSFX(c, m, a, b); } + +#ifdef NPY_HAVE_AVX512BW + NPYV_IMPL_AVX512_MASK_ADDSUB(u8, b8, epi8) + NPYV_IMPL_AVX512_MASK_ADDSUB(s8, b8, epi8) + NPYV_IMPL_AVX512_MASK_ADDSUB(u16, b16, epi16) + NPYV_IMPL_AVX512_MASK_ADDSUB(s16, b16, epi16) +#else + NPYV_IMPL_AVX512_EMULATE_MASK_ADDSUB(u8, b8) + NPYV_IMPL_AVX512_EMULATE_MASK_ADDSUB(s8, b8) + NPYV_IMPL_AVX512_EMULATE_MASK_ADDSUB(u16, b16) + NPYV_IMPL_AVX512_EMULATE_MASK_ADDSUB(s16, b16) +#endif + +NPYV_IMPL_AVX512_MASK_ADDSUB(u32, b32, epi32) +NPYV_IMPL_AVX512_MASK_ADDSUB(s32, b32, epi32) +NPYV_IMPL_AVX512_MASK_ADDSUB(u64, b64, epi64) +NPYV_IMPL_AVX512_MASK_ADDSUB(s64, b64, epi64) +NPYV_IMPL_AVX512_MASK_ADDSUB(f32, b32, ps) +NPYV_IMPL_AVX512_MASK_ADDSUB(f64, b64, pd) + +#endif // _NPY_SIMD_AVX512_MASKOP_H diff --git a/numpy/core/src/common/simd/avx512/math.h b/numpy/core/src/common/simd/avx512/math.h new file mode 100644 index 000000000000..f30e50ad05df --- /dev/null +++ b/numpy/core/src/common/simd/avx512/math.h @@ -0,0 +1,123 @@ +#ifndef NPY_SIMD + #error "Not a standalone header" +#endif + +#ifndef _NPY_SIMD_AVX512_MATH_H +#define _NPY_SIMD_AVX512_MATH_H + +/*************************** + * Elementary + ***************************/ +// Square root +#define npyv_sqrt_f32 _mm512_sqrt_ps +#define npyv_sqrt_f64 _mm512_sqrt_pd + +// Reciprocal +NPY_FINLINE npyv_f32 npyv_recip_f32(npyv_f32 a) +{ return _mm512_div_ps(_mm512_set1_ps(1.0f), a); } +NPY_FINLINE npyv_f64 npyv_recip_f64(npyv_f64 a) +{ return _mm512_div_pd(_mm512_set1_pd(1.0), a); } + +// Absolute +NPY_FINLINE npyv_f32 npyv_abs_f32(npyv_f32 a) +{ +#if 0 // def NPY_HAVE_AVX512DQ + return _mm512_range_ps(a, a, 8); +#else + return npyv_and_f32( + a, _mm512_castsi512_ps(_mm512_set1_epi32(0x7fffffff)) + ); +#endif +} +NPY_FINLINE npyv_f64 npyv_abs_f64(npyv_f64 a) +{ +#if 0 // def NPY_HAVE_AVX512DQ + return _mm512_range_pd(a, a, 8); +#else + return npyv_and_f64( + a, _mm512_castsi512_pd(npyv_setall_s64(0x7fffffffffffffffLL)) + ); +#endif +} + +// Square +NPY_FINLINE npyv_f32 npyv_square_f32(npyv_f32 a) +{ return _mm512_mul_ps(a, a); } +NPY_FINLINE npyv_f64 npyv_square_f64(npyv_f64 a) +{ return _mm512_mul_pd(a, a); } + +// Maximum, natively mapping with no guarantees to handle NaN. +#define npyv_max_f32 _mm512_max_ps +#define npyv_max_f64 _mm512_max_pd +// Maximum, supports IEEE floating-point arithmetic (IEC 60559), +// - If one of the two vectors contains NaN, the equivalent element of the other vector is set +// - Only if both corresponded elements are NaN, NaN is set. +NPY_FINLINE npyv_f32 npyv_maxp_f32(npyv_f32 a, npyv_f32 b) +{ + __mmask16 nn = _mm512_cmp_ps_mask(b, b, _CMP_ORD_Q); + return _mm512_mask_max_ps(a, nn, a, b); +} +NPY_FINLINE npyv_f64 npyv_maxp_f64(npyv_f64 a, npyv_f64 b) +{ + __mmask8 nn = _mm512_cmp_pd_mask(b, b, _CMP_ORD_Q); + return _mm512_mask_max_pd(a, nn, a, b); +} +// Maximum, integer operations +#ifdef NPY_HAVE_AVX512BW + #define npyv_max_u8 _mm512_max_epu8 + #define npyv_max_s8 _mm512_max_epi8 + #define npyv_max_u16 _mm512_max_epu16 + #define npyv_max_s16 _mm512_max_epi16 +#else + NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_max_u8, _mm256_max_epu8) + NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_max_s8, _mm256_max_epi8) + NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_max_u16, _mm256_max_epu16) + NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_max_s16, _mm256_max_epi16) +#endif +#define npyv_max_u32 _mm512_max_epu32 +#define npyv_max_s32 _mm512_max_epi32 +#define npyv_max_u64 _mm512_max_epu64 +#define npyv_max_s64 _mm512_max_epi64 + +// Minimum, natively mapping with no guarantees to handle NaN. +#define npyv_min_f32 _mm512_min_ps +#define npyv_min_f64 _mm512_min_pd +// Minimum, supports IEEE floating-point arithmetic (IEC 60559), +// - If one of the two vectors contains NaN, the equivalent element of the other vector is set +// - Only if both corresponded elements are NaN, NaN is set. +NPY_FINLINE npyv_f32 npyv_minp_f32(npyv_f32 a, npyv_f32 b) +{ + __mmask16 nn = _mm512_cmp_ps_mask(b, b, _CMP_ORD_Q); + return _mm512_mask_min_ps(a, nn, a, b); +} +NPY_FINLINE npyv_f64 npyv_minp_f64(npyv_f64 a, npyv_f64 b) +{ + __mmask8 nn = _mm512_cmp_pd_mask(b, b, _CMP_ORD_Q); + return _mm512_mask_min_pd(a, nn, a, b); +} +// Minimum, integer operations +#ifdef NPY_HAVE_AVX512BW + #define npyv_min_u8 _mm512_min_epu8 + #define npyv_min_s8 _mm512_min_epi8 + #define npyv_min_u16 _mm512_min_epu16 + #define npyv_min_s16 _mm512_min_epi16 +#else + NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_min_u8, _mm256_min_epu8) + NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_min_s8, _mm256_min_epi8) + NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_min_u16, _mm256_min_epu16) + NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_min_s16, _mm256_min_epi16) +#endif +#define npyv_min_u32 _mm512_min_epu32 +#define npyv_min_s32 _mm512_min_epi32 +#define npyv_min_u64 _mm512_min_epu64 +#define npyv_min_s64 _mm512_min_epi64 + +// ceil +#define npyv_ceil_f32(A) _mm512_roundscale_ps(A, _MM_FROUND_TO_POS_INF) +#define npyv_ceil_f64(A) _mm512_roundscale_pd(A, _MM_FROUND_TO_POS_INF) + +// trunc +#define npyv_trunc_f32(A) _mm512_roundscale_ps(A, _MM_FROUND_TO_ZERO) +#define npyv_trunc_f64(A) _mm512_roundscale_pd(A, _MM_FROUND_TO_ZERO) + +#endif // _NPY_SIMD_AVX512_MATH_H diff --git a/numpy/core/src/common/simd/avx512/memory.h b/numpy/core/src/common/simd/avx512/memory.h new file mode 100644 index 000000000000..47095bf72aa1 --- /dev/null +++ b/numpy/core/src/common/simd/avx512/memory.h @@ -0,0 +1,332 @@ +#ifndef NPY_SIMD + #error "Not a standalone header" +#endif + +#ifndef _NPY_SIMD_AVX512_MEMORY_H +#define _NPY_SIMD_AVX512_MEMORY_H + +#include "misc.h" + +/*************************** + * load/store + ***************************/ +#if defined(__GNUC__) + // GCC expect pointer argument type to be `void*` instead of `const void *`, + // which cause a massive warning. + #define npyv__loads(PTR) _mm512_stream_load_si512((__m512i*)(PTR)) +#else + #define npyv__loads(PTR) _mm512_stream_load_si512((const __m512i*)(PTR)) +#endif +#if defined(_MSC_VER) && defined(_M_IX86) + // workaround msvc(32bit) overflow bug, reported at + // https://developercommunity.visualstudio.com/content/problem/911872/u.html + NPY_FINLINE __m512i npyv__loadl(const __m256i *ptr) + { + __m256i a = _mm256_loadu_si256(ptr); + return _mm512_inserti64x4(_mm512_castsi256_si512(a), a, 0); + } +#else + #define npyv__loadl(PTR) \ + _mm512_castsi256_si512(_mm256_loadu_si256(PTR)) +#endif +#define NPYV_IMPL_AVX512_MEM_INT(CTYPE, SFX) \ + NPY_FINLINE npyv_##SFX npyv_load_##SFX(const CTYPE *ptr) \ + { return _mm512_loadu_si512((const __m512i*)ptr); } \ + NPY_FINLINE npyv_##SFX npyv_loada_##SFX(const CTYPE *ptr) \ + { return _mm512_load_si512((const __m512i*)ptr); } \ + NPY_FINLINE npyv_##SFX npyv_loads_##SFX(const CTYPE *ptr) \ + { return npyv__loads(ptr); } \ + NPY_FINLINE npyv_##SFX npyv_loadl_##SFX(const CTYPE *ptr) \ + { return npyv__loadl((const __m256i *)ptr); } \ + NPY_FINLINE void npyv_store_##SFX(CTYPE *ptr, npyv_##SFX vec) \ + { _mm512_storeu_si512((__m512i*)ptr, vec); } \ + NPY_FINLINE void npyv_storea_##SFX(CTYPE *ptr, npyv_##SFX vec) \ + { _mm512_store_si512((__m512i*)ptr, vec); } \ + NPY_FINLINE void npyv_stores_##SFX(CTYPE *ptr, npyv_##SFX vec) \ + { _mm512_stream_si512((__m512i*)ptr, vec); } \ + NPY_FINLINE void npyv_storel_##SFX(CTYPE *ptr, npyv_##SFX vec) \ + { _mm256_storeu_si256((__m256i*)ptr, npyv512_lower_si256(vec)); } \ + NPY_FINLINE void npyv_storeh_##SFX(CTYPE *ptr, npyv_##SFX vec) \ + { _mm256_storeu_si256((__m256i*)(ptr), npyv512_higher_si256(vec)); } + +NPYV_IMPL_AVX512_MEM_INT(npy_uint8, u8) +NPYV_IMPL_AVX512_MEM_INT(npy_int8, s8) +NPYV_IMPL_AVX512_MEM_INT(npy_uint16, u16) +NPYV_IMPL_AVX512_MEM_INT(npy_int16, s16) +NPYV_IMPL_AVX512_MEM_INT(npy_uint32, u32) +NPYV_IMPL_AVX512_MEM_INT(npy_int32, s32) +NPYV_IMPL_AVX512_MEM_INT(npy_uint64, u64) +NPYV_IMPL_AVX512_MEM_INT(npy_int64, s64) + +// unaligned load +#define npyv_load_f32(PTR) _mm512_loadu_ps((const __m512*)(PTR)) +#define npyv_load_f64(PTR) _mm512_loadu_pd((const __m512d*)(PTR)) +// aligned load +#define npyv_loada_f32(PTR) _mm512_load_ps((const __m512*)(PTR)) +#define npyv_loada_f64(PTR) _mm512_load_pd((const __m512d*)(PTR)) +// load lower part +#if defined(_MSC_VER) && defined(_M_IX86) + #define npyv_loadl_f32(PTR) _mm512_castsi512_ps(npyv__loadl((const __m256i *)(PTR))) + #define npyv_loadl_f64(PTR) _mm512_castsi512_pd(npyv__loadl((const __m256i *)(PTR))) +#else + #define npyv_loadl_f32(PTR) _mm512_castps256_ps512(_mm256_loadu_ps(PTR)) + #define npyv_loadl_f64(PTR) _mm512_castpd256_pd512(_mm256_loadu_pd(PTR)) +#endif +// stream load +#define npyv_loads_f32(PTR) _mm512_castsi512_ps(npyv__loads(PTR)) +#define npyv_loads_f64(PTR) _mm512_castsi512_pd(npyv__loads(PTR)) +// unaligned store +#define npyv_store_f32 _mm512_storeu_ps +#define npyv_store_f64 _mm512_storeu_pd +// aligned store +#define npyv_storea_f32 _mm512_store_ps +#define npyv_storea_f64 _mm512_store_pd +// stream store +#define npyv_stores_f32 _mm512_stream_ps +#define npyv_stores_f64 _mm512_stream_pd +// store lower part +#define npyv_storel_f32(PTR, VEC) _mm256_storeu_ps(PTR, npyv512_lower_ps256(VEC)) +#define npyv_storel_f64(PTR, VEC) _mm256_storeu_pd(PTR, npyv512_lower_pd256(VEC)) +// store higher part +#define npyv_storeh_f32(PTR, VEC) _mm256_storeu_ps(PTR, npyv512_higher_ps256(VEC)) +#define npyv_storeh_f64(PTR, VEC) _mm256_storeu_pd(PTR, npyv512_higher_pd256(VEC)) +/*************************** + * Non-contiguous Load + ***************************/ +//// 32 +NPY_FINLINE npyv_u32 npyv_loadn_u32(const npy_uint32 *ptr, npy_intp stride) +{ + assert(llabs(stride) <= NPY_SIMD_MAXLOAD_STRIDE32); + const __m512i steps = npyv_set_s32( + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 + ); + const __m512i idx = _mm512_mullo_epi32(steps, _mm512_set1_epi32((int)stride)); + return _mm512_i32gather_epi32(idx, (const __m512i*)ptr, 4); +} +NPY_FINLINE npyv_s32 npyv_loadn_s32(const npy_int32 *ptr, npy_intp stride) +{ return npyv_loadn_u32((const npy_uint32*)ptr, stride); } +NPY_FINLINE npyv_f32 npyv_loadn_f32(const float *ptr, npy_intp stride) +{ return _mm512_castsi512_ps(npyv_loadn_u32((const npy_uint32*)ptr, stride)); } +//// 64 +NPY_FINLINE npyv_u64 npyv_loadn_u64(const npy_uint64 *ptr, npy_intp stride) +{ + const __m512i idx = npyv_set_s64( + 0*stride, 1*stride, 2*stride, 3*stride, + 4*stride, 5*stride, 6*stride, 7*stride + ); + return _mm512_i64gather_epi64(idx, (const __m512i*)ptr, 8); +} +NPY_FINLINE npyv_s64 npyv_loadn_s64(const npy_int64 *ptr, npy_intp stride) +{ return npyv_loadn_u64((const npy_uint64*)ptr, stride); } +NPY_FINLINE npyv_f64 npyv_loadn_f64(const double *ptr, npy_intp stride) +{ return _mm512_castsi512_pd(npyv_loadn_u64((const npy_uint64*)ptr, stride)); } +/*************************** + * Non-contiguous Store + ***************************/ +//// 32 +NPY_FINLINE void npyv_storen_u32(npy_uint32 *ptr, npy_intp stride, npyv_u32 a) +{ + assert(llabs(stride) <= NPY_SIMD_MAXSTORE_STRIDE32); + const __m512i steps = _mm512_setr_epi32( + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 + ); + const __m512i idx = _mm512_mullo_epi32(steps, _mm512_set1_epi32((int)stride)); + _mm512_i32scatter_epi32((__m512i*)ptr, idx, a, 4); +} +NPY_FINLINE void npyv_storen_s32(npy_int32 *ptr, npy_intp stride, npyv_s32 a) +{ npyv_storen_u32((npy_uint32*)ptr, stride, a); } +NPY_FINLINE void npyv_storen_f32(float *ptr, npy_intp stride, npyv_f32 a) +{ npyv_storen_u32((npy_uint32*)ptr, stride, _mm512_castps_si512(a)); } +//// 64 +NPY_FINLINE void npyv_storen_u64(npy_uint64 *ptr, npy_intp stride, npyv_u64 a) +{ + const __m512i idx = npyv_set_s64( + 0*stride, 1*stride, 2*stride, 3*stride, + 4*stride, 5*stride, 6*stride, 7*stride + ); + _mm512_i64scatter_epi64((__m512i*)ptr, idx, a, 8); +} +NPY_FINLINE void npyv_storen_s64(npy_int64 *ptr, npy_intp stride, npyv_s64 a) +{ npyv_storen_u64((npy_uint64*)ptr, stride, a); } +NPY_FINLINE void npyv_storen_f64(double *ptr, npy_intp stride, npyv_f64 a) +{ npyv_storen_u64((npy_uint64*)ptr, stride, _mm512_castpd_si512(a)); } + +/********************************* + * Partial Load + *********************************/ +//// 32 +NPY_FINLINE npyv_s32 npyv_load_till_s32(const npy_int32 *ptr, npy_uintp nlane, npy_int32 fill) +{ + assert(nlane > 0); + const __m512i vfill = _mm512_set1_epi32(fill); + const __mmask16 mask = nlane > 31 ? -1 : (1 << nlane) - 1; + return _mm512_mask_loadu_epi32(vfill, mask, (const __m512i*)ptr); +} +// fill zero to rest lanes +NPY_FINLINE npyv_s32 npyv_load_tillz_s32(const npy_int32 *ptr, npy_uintp nlane) +{ + assert(nlane > 0); + const __mmask16 mask = nlane > 31 ? -1 : (1 << nlane) - 1; + return _mm512_maskz_loadu_epi32(mask, (const __m512i*)ptr); +} +//// 64 +NPY_FINLINE npyv_s64 npyv_load_till_s64(const npy_int64 *ptr, npy_uintp nlane, npy_int64 fill) +{ + assert(nlane > 0); + const __m512i vfill = npyv_setall_s64(fill); + const __mmask8 mask = nlane > 31 ? -1 : (1 << nlane) - 1; + return _mm512_mask_loadu_epi64(vfill, mask, (const __m512i*)ptr); +} +// fill zero to rest lanes +NPY_FINLINE npyv_s64 npyv_load_tillz_s64(const npy_int64 *ptr, npy_uintp nlane) +{ + assert(nlane > 0); + const __mmask8 mask = nlane > 15 ? -1 : (1 << nlane) - 1; + return _mm512_maskz_loadu_epi64(mask, (const __m512i*)ptr); +} +/********************************* + * Non-contiguous partial load + *********************************/ +//// 32 +NPY_FINLINE npyv_s32 +npyv_loadn_till_s32(const npy_int32 *ptr, npy_intp stride, npy_uintp nlane, npy_int32 fill) +{ + assert(nlane > 0); + assert(llabs(stride) <= NPY_SIMD_MAXLOAD_STRIDE32); + const __m512i steps = npyv_set_s32( + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 + ); + const __m512i idx = _mm512_mullo_epi32(steps, _mm512_set1_epi32((int)stride)); + const __m512i vfill = _mm512_set1_epi32(fill); + const __mmask16 mask = nlane > 31 ? -1 : (1 << nlane) - 1; + return _mm512_mask_i32gather_epi32(vfill, mask, idx, (const __m512i*)ptr, 4); +} +// fill zero to rest lanes +NPY_FINLINE npyv_s32 +npyv_loadn_tillz_s32(const npy_int32 *ptr, npy_intp stride, npy_uintp nlane) +{ return npyv_loadn_till_s32(ptr, stride, nlane, 0); } +//// 64 +NPY_FINLINE npyv_s64 +npyv_loadn_till_s64(const npy_int64 *ptr, npy_intp stride, npy_uintp nlane, npy_int64 fill) +{ + assert(nlane > 0); + const __m512i idx = npyv_set_s64( + 0*stride, 1*stride, 2*stride, 3*stride, + 4*stride, 5*stride, 6*stride, 7*stride + ); + const __m512i vfill = npyv_setall_s64(fill); + const __mmask8 mask = nlane > 31 ? -1 : (1 << nlane) - 1; + return _mm512_mask_i64gather_epi64(vfill, mask, idx, (const __m512i*)ptr, 8); +} +// fill zero to rest lanes +NPY_FINLINE npyv_s64 +npyv_loadn_tillz_s64(const npy_int64 *ptr, npy_intp stride, npy_uintp nlane) +{ return npyv_loadn_till_s64(ptr, stride, nlane, 0); } +/********************************* + * Partial store + *********************************/ +//// 32 +NPY_FINLINE void npyv_store_till_s32(npy_int32 *ptr, npy_uintp nlane, npyv_s32 a) +{ + assert(nlane > 0); + const __mmask16 mask = nlane > 31 ? -1 : (1 << nlane) - 1; + _mm512_mask_storeu_epi32((__m512i*)ptr, mask, a); +} +//// 64 +NPY_FINLINE void npyv_store_till_s64(npy_int64 *ptr, npy_uintp nlane, npyv_s64 a) +{ + assert(nlane > 0); + const __mmask8 mask = nlane > 15 ? -1 : (1 << nlane) - 1; + _mm512_mask_storeu_epi64((__m512i*)ptr, mask, a); +} +/********************************* + * Non-contiguous partial store + *********************************/ +//// 32 +NPY_FINLINE void npyv_storen_till_s32(npy_int32 *ptr, npy_intp stride, npy_uintp nlane, npyv_s32 a) +{ + assert(nlane > 0); + assert(llabs(stride) <= NPY_SIMD_MAXSTORE_STRIDE32); + const __m512i steps = _mm512_setr_epi32( + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 + ); + const __m512i idx = _mm512_mullo_epi32(steps, _mm512_set1_epi32((int)stride)); + const __mmask16 mask = nlane > 31 ? -1 : (1 << nlane) - 1; + _mm512_mask_i32scatter_epi32((__m512i*)ptr, mask, idx, a, 4); +} +//// 64 +NPY_FINLINE void npyv_storen_till_s64(npy_int64 *ptr, npy_intp stride, npy_uintp nlane, npyv_s64 a) +{ + assert(nlane > 0); + const __m512i idx = npyv_set_s64( + 0*stride, 1*stride, 2*stride, 3*stride, + 4*stride, 5*stride, 6*stride, 7*stride + ); + const __mmask8 mask = nlane > 15 ? -1 : (1 << nlane) - 1; + _mm512_mask_i64scatter_epi64((__m512i*)ptr, mask, idx, a, 8); +} + +/***************************************************************************** + * Implement partial load/store for u32/f32/u64/f64... via reinterpret cast + *****************************************************************************/ +#define NPYV_IMPL_AVX512_REST_PARTIAL_TYPES(F_SFX, T_SFX) \ + NPY_FINLINE npyv_##F_SFX npyv_load_till_##F_SFX \ + (const npyv_lanetype_##F_SFX *ptr, npy_uintp nlane, npyv_lanetype_##F_SFX fill) \ + { \ + union { \ + npyv_lanetype_##F_SFX from_##F_SFX; \ + npyv_lanetype_##T_SFX to_##T_SFX; \ + } pun = {.from_##F_SFX = fill}; \ + return npyv_reinterpret_##F_SFX##_##T_SFX(npyv_load_till_##T_SFX( \ + (const npyv_lanetype_##T_SFX *)ptr, nlane, pun.to_##T_SFX \ + )); \ + } \ + NPY_FINLINE npyv_##F_SFX npyv_loadn_till_##F_SFX \ + (const npyv_lanetype_##F_SFX *ptr, npy_intp stride, npy_uintp nlane, \ + npyv_lanetype_##F_SFX fill) \ + { \ + union { \ + npyv_lanetype_##F_SFX from_##F_SFX; \ + npyv_lanetype_##T_SFX to_##T_SFX; \ + } pun = {.from_##F_SFX = fill}; \ + return npyv_reinterpret_##F_SFX##_##T_SFX(npyv_loadn_till_##T_SFX( \ + (const npyv_lanetype_##T_SFX *)ptr, stride, nlane, pun.to_##T_SFX \ + )); \ + } \ + NPY_FINLINE npyv_##F_SFX npyv_load_tillz_##F_SFX \ + (const npyv_lanetype_##F_SFX *ptr, npy_uintp nlane) \ + { \ + return npyv_reinterpret_##F_SFX##_##T_SFX(npyv_load_tillz_##T_SFX( \ + (const npyv_lanetype_##T_SFX *)ptr, nlane \ + )); \ + } \ + NPY_FINLINE npyv_##F_SFX npyv_loadn_tillz_##F_SFX \ + (const npyv_lanetype_##F_SFX *ptr, npy_intp stride, npy_uintp nlane) \ + { \ + return npyv_reinterpret_##F_SFX##_##T_SFX(npyv_loadn_tillz_##T_SFX( \ + (const npyv_lanetype_##T_SFX *)ptr, stride, nlane \ + )); \ + } \ + NPY_FINLINE void npyv_store_till_##F_SFX \ + (npyv_lanetype_##F_SFX *ptr, npy_uintp nlane, npyv_##F_SFX a) \ + { \ + npyv_store_till_##T_SFX( \ + (npyv_lanetype_##T_SFX *)ptr, nlane, \ + npyv_reinterpret_##T_SFX##_##F_SFX(a) \ + ); \ + } \ + NPY_FINLINE void npyv_storen_till_##F_SFX \ + (npyv_lanetype_##F_SFX *ptr, npy_intp stride, npy_uintp nlane, npyv_##F_SFX a) \ + { \ + npyv_storen_till_##T_SFX( \ + (npyv_lanetype_##T_SFX *)ptr, stride, nlane, \ + npyv_reinterpret_##T_SFX##_##F_SFX(a) \ + ); \ + } + +NPYV_IMPL_AVX512_REST_PARTIAL_TYPES(u32, s32) +NPYV_IMPL_AVX512_REST_PARTIAL_TYPES(f32, s32) +NPYV_IMPL_AVX512_REST_PARTIAL_TYPES(u64, s64) +NPYV_IMPL_AVX512_REST_PARTIAL_TYPES(f64, s64) + +#endif // _NPY_SIMD_AVX512_MEMORY_H diff --git a/numpy/core/src/common/simd/avx512/misc.h b/numpy/core/src/common/simd/avx512/misc.h new file mode 100644 index 000000000000..c3039ecfedcb --- /dev/null +++ b/numpy/core/src/common/simd/avx512/misc.h @@ -0,0 +1,280 @@ +#ifndef NPY_SIMD + #error "Not a standalone header" +#endif + +#ifndef _NPY_SIMD_AVX512_MISC_H +#define _NPY_SIMD_AVX512_MISC_H + +// set all lanes to zero +#define npyv_zero_u8 _mm512_setzero_si512 +#define npyv_zero_s8 _mm512_setzero_si512 +#define npyv_zero_u16 _mm512_setzero_si512 +#define npyv_zero_s16 _mm512_setzero_si512 +#define npyv_zero_u32 _mm512_setzero_si512 +#define npyv_zero_s32 _mm512_setzero_si512 +#define npyv_zero_u64 _mm512_setzero_si512 +#define npyv_zero_s64 _mm512_setzero_si512 +#define npyv_zero_f32 _mm512_setzero_ps +#define npyv_zero_f64 _mm512_setzero_pd + +// set all lanes to same value +#define npyv_setall_u8(VAL) _mm512_set1_epi8((char)VAL) +#define npyv_setall_s8(VAL) _mm512_set1_epi8((char)VAL) +#define npyv_setall_u16(VAL) _mm512_set1_epi16((short)VAL) +#define npyv_setall_s16(VAL) _mm512_set1_epi16((short)VAL) +#define npyv_setall_u32(VAL) _mm512_set1_epi32((int)VAL) +#define npyv_setall_s32(VAL) _mm512_set1_epi32(VAL) +#define npyv_setall_f32(VAL) _mm512_set1_ps(VAL) +#define npyv_setall_f64(VAL) _mm512_set1_pd(VAL) + +NPY_FINLINE __m512i npyv__setr_epi64( + npy_int64, npy_int64, npy_int64, npy_int64, + npy_int64, npy_int64, npy_int64, npy_int64 +); +NPY_FINLINE npyv_u64 npyv_setall_u64(npy_uint64 a) +{ + npy_int64 ai = (npy_int64)a; +#if defined(_MSC_VER) && defined(_M_IX86) + return npyv__setr_epi64(ai, ai, ai, ai, ai, ai, ai, ai); +#else + return _mm512_set1_epi64(ai); +#endif +} +NPY_FINLINE npyv_s64 npyv_setall_s64(npy_int64 a) +{ +#if defined(_MSC_VER) && defined(_M_IX86) + return npyv__setr_epi64(a, a, a, a, a, a, a, a); +#else + return _mm512_set1_epi64(a); +#endif +} +/** + * vector with specific values set to each lane and + * set a specific value to all remained lanes + * + * _mm512_set_epi8 and _mm512_set_epi16 are missing in many compilers + */ +NPY_FINLINE __m512i npyv__setr_epi8( + char i0, char i1, char i2, char i3, char i4, char i5, char i6, char i7, + char i8, char i9, char i10, char i11, char i12, char i13, char i14, char i15, + char i16, char i17, char i18, char i19, char i20, char i21, char i22, char i23, + char i24, char i25, char i26, char i27, char i28, char i29, char i30, char i31, + char i32, char i33, char i34, char i35, char i36, char i37, char i38, char i39, + char i40, char i41, char i42, char i43, char i44, char i45, char i46, char i47, + char i48, char i49, char i50, char i51, char i52, char i53, char i54, char i55, + char i56, char i57, char i58, char i59, char i60, char i61, char i62, char i63) +{ + const char NPY_DECL_ALIGNED(64) data[64] = { + i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15, + i16, i17, i18, i19, i20, i21, i22, i23, i24, i25, i26, i27, i28, i29, i30, i31, + i32, i33, i34, i35, i36, i37, i38, i39, i40, i41, i42, i43, i44, i45, i46, i47, + i48, i49, i50, i51, i52, i53, i54, i55, i56, i57, i58, i59, i60, i61, i62, i63 + }; + return _mm512_load_si512((const void*)data); +} +NPY_FINLINE __m512i npyv__setr_epi16( + short i0, short i1, short i2, short i3, short i4, short i5, short i6, short i7, + short i8, short i9, short i10, short i11, short i12, short i13, short i14, short i15, + short i16, short i17, short i18, short i19, short i20, short i21, short i22, short i23, + short i24, short i25, short i26, short i27, short i28, short i29, short i30, short i31) +{ + const short NPY_DECL_ALIGNED(64) data[32] = { + i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15, + i16, i17, i18, i19, i20, i21, i22, i23, i24, i25, i26, i27, i28, i29, i30, i31 + }; + return _mm512_load_si512((const void*)data); +} +// args that generated by NPYV__SET_FILL_* not going to expand if +// _mm512_setr_* are defined as macros. +NPY_FINLINE __m512i npyv__setr_epi32( + int i0, int i1, int i2, int i3, int i4, int i5, int i6, int i7, + int i8, int i9, int i10, int i11, int i12, int i13, int i14, int i15) +{ + return _mm512_setr_epi32(i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15); +} +NPY_FINLINE __m512i npyv__setr_epi64(npy_int64 i0, npy_int64 i1, npy_int64 i2, npy_int64 i3, + npy_int64 i4, npy_int64 i5, npy_int64 i6, npy_int64 i7) +{ +#if defined(_MSC_VER) && defined(_M_IX86) + return _mm512_setr_epi32( + (int)i0, (int)(i0 >> 32), (int)i1, (int)(i1 >> 32), + (int)i2, (int)(i2 >> 32), (int)i3, (int)(i3 >> 32), + (int)i4, (int)(i4 >> 32), (int)i5, (int)(i5 >> 32), + (int)i6, (int)(i6 >> 32), (int)i7, (int)(i7 >> 32) + ); +#else + return _mm512_setr_epi64(i0, i1, i2, i3, i4, i5, i6, i7); +#endif +} + +NPY_FINLINE __m512 npyv__setr_ps( + float i0, float i1, float i2, float i3, float i4, float i5, float i6, float i7, + float i8, float i9, float i10, float i11, float i12, float i13, float i14, float i15) +{ + return _mm512_setr_ps(i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15); +} +NPY_FINLINE __m512d npyv__setr_pd(double i0, double i1, double i2, double i3, + double i4, double i5, double i6, double i7) +{ + return _mm512_setr_pd(i0, i1, i2, i3, i4, i5, i6, i7); +} +#define npyv_setf_u8(FILL, ...) npyv__setr_epi8(NPYV__SET_FILL_64(char, FILL, __VA_ARGS__)) +#define npyv_setf_s8(FILL, ...) npyv__setr_epi8(NPYV__SET_FILL_64(char, FILL, __VA_ARGS__)) +#define npyv_setf_u16(FILL, ...) npyv__setr_epi16(NPYV__SET_FILL_32(short, FILL, __VA_ARGS__)) +#define npyv_setf_s16(FILL, ...) npyv__setr_epi16(NPYV__SET_FILL_32(short, FILL, __VA_ARGS__)) +#define npyv_setf_u32(FILL, ...) npyv__setr_epi32(NPYV__SET_FILL_16(int, FILL, __VA_ARGS__)) +#define npyv_setf_s32(FILL, ...) npyv__setr_epi32(NPYV__SET_FILL_16(int, FILL, __VA_ARGS__)) +#define npyv_setf_u64(FILL, ...) npyv__setr_epi64(NPYV__SET_FILL_8(npy_int64, FILL, __VA_ARGS__)) +#define npyv_setf_s64(FILL, ...) npyv__setr_epi64(NPYV__SET_FILL_8(npy_int64, FILL, __VA_ARGS__)) +#define npyv_setf_f32(FILL, ...) npyv__setr_ps(NPYV__SET_FILL_16(float, FILL, __VA_ARGS__)) +#define npyv_setf_f64(FILL, ...) npyv__setr_pd(NPYV__SET_FILL_8(double, FILL, __VA_ARGS__)) + +// vector with specific values set to each lane and +// set zero to all remained lanes +#define npyv_set_u8(...) npyv_setf_u8(0, __VA_ARGS__) +#define npyv_set_s8(...) npyv_setf_s8(0, __VA_ARGS__) +#define npyv_set_u16(...) npyv_setf_u16(0, __VA_ARGS__) +#define npyv_set_s16(...) npyv_setf_s16(0, __VA_ARGS__) +#define npyv_set_u32(...) npyv_setf_u32(0, __VA_ARGS__) +#define npyv_set_s32(...) npyv_setf_s32(0, __VA_ARGS__) +#define npyv_set_u64(...) npyv_setf_u64(0, __VA_ARGS__) +#define npyv_set_s64(...) npyv_setf_s64(0, __VA_ARGS__) +#define npyv_set_f32(...) npyv_setf_f32(0, __VA_ARGS__) +#define npyv_set_f64(...) npyv_setf_f64(0, __VA_ARGS__) + +// per lane select +#ifdef NPY_HAVE_AVX512BW + #define npyv_select_u8(MASK, A, B) _mm512_mask_blend_epi8(MASK, B, A) + #define npyv_select_u16(MASK, A, B) _mm512_mask_blend_epi16(MASK, B, A) +#else + NPY_FINLINE __m512i npyv_select_u8(__m512i mask, __m512i a, __m512i b) + { return _mm512_xor_si512(b, _mm512_and_si512(_mm512_xor_si512(b, a), mask)); } + #define npyv_select_u16 npyv_select_u8 +#endif +#define npyv_select_s8 npyv_select_u8 +#define npyv_select_s16 npyv_select_u16 +#define npyv_select_u32(MASK, A, B) _mm512_mask_blend_epi32(MASK, B, A) +#define npyv_select_s32 npyv_select_u32 +#define npyv_select_u64(MASK, A, B) _mm512_mask_blend_epi64(MASK, B, A) +#define npyv_select_s64 npyv_select_u64 +#define npyv_select_f32(MASK, A, B) _mm512_mask_blend_ps(MASK, B, A) +#define npyv_select_f64(MASK, A, B) _mm512_mask_blend_pd(MASK, B, A) + +// reinterpret +#define npyv_reinterpret_u8_u8(X) X +#define npyv_reinterpret_u8_s8(X) X +#define npyv_reinterpret_u8_u16(X) X +#define npyv_reinterpret_u8_s16(X) X +#define npyv_reinterpret_u8_u32(X) X +#define npyv_reinterpret_u8_s32(X) X +#define npyv_reinterpret_u8_u64(X) X +#define npyv_reinterpret_u8_s64(X) X +#define npyv_reinterpret_u8_f32 _mm512_castps_si512 +#define npyv_reinterpret_u8_f64 _mm512_castpd_si512 + +#define npyv_reinterpret_s8_s8(X) X +#define npyv_reinterpret_s8_u8(X) X +#define npyv_reinterpret_s8_u16(X) X +#define npyv_reinterpret_s8_s16(X) X +#define npyv_reinterpret_s8_u32(X) X +#define npyv_reinterpret_s8_s32(X) X +#define npyv_reinterpret_s8_u64(X) X +#define npyv_reinterpret_s8_s64(X) X +#define npyv_reinterpret_s8_f32 _mm512_castps_si512 +#define npyv_reinterpret_s8_f64 _mm512_castpd_si512 + +#define npyv_reinterpret_u16_u16(X) X +#define npyv_reinterpret_u16_u8(X) X +#define npyv_reinterpret_u16_s8(X) X +#define npyv_reinterpret_u16_s16(X) X +#define npyv_reinterpret_u16_u32(X) X +#define npyv_reinterpret_u16_s32(X) X +#define npyv_reinterpret_u16_u64(X) X +#define npyv_reinterpret_u16_s64(X) X +#define npyv_reinterpret_u16_f32 _mm512_castps_si512 +#define npyv_reinterpret_u16_f64 _mm512_castpd_si512 + +#define npyv_reinterpret_s16_s16(X) X +#define npyv_reinterpret_s16_u8(X) X +#define npyv_reinterpret_s16_s8(X) X +#define npyv_reinterpret_s16_u16(X) X +#define npyv_reinterpret_s16_u32(X) X +#define npyv_reinterpret_s16_s32(X) X +#define npyv_reinterpret_s16_u64(X) X +#define npyv_reinterpret_s16_s64(X) X +#define npyv_reinterpret_s16_f32 _mm512_castps_si512 +#define npyv_reinterpret_s16_f64 _mm512_castpd_si512 + +#define npyv_reinterpret_u32_u32(X) X +#define npyv_reinterpret_u32_u8(X) X +#define npyv_reinterpret_u32_s8(X) X +#define npyv_reinterpret_u32_u16(X) X +#define npyv_reinterpret_u32_s16(X) X +#define npyv_reinterpret_u32_s32(X) X +#define npyv_reinterpret_u32_u64(X) X +#define npyv_reinterpret_u32_s64(X) X +#define npyv_reinterpret_u32_f32 _mm512_castps_si512 +#define npyv_reinterpret_u32_f64 _mm512_castpd_si512 + +#define npyv_reinterpret_s32_s32(X) X +#define npyv_reinterpret_s32_u8(X) X +#define npyv_reinterpret_s32_s8(X) X +#define npyv_reinterpret_s32_u16(X) X +#define npyv_reinterpret_s32_s16(X) X +#define npyv_reinterpret_s32_u32(X) X +#define npyv_reinterpret_s32_u64(X) X +#define npyv_reinterpret_s32_s64(X) X +#define npyv_reinterpret_s32_f32 _mm512_castps_si512 +#define npyv_reinterpret_s32_f64 _mm512_castpd_si512 + +#define npyv_reinterpret_u64_u64(X) X +#define npyv_reinterpret_u64_u8(X) X +#define npyv_reinterpret_u64_s8(X) X +#define npyv_reinterpret_u64_u16(X) X +#define npyv_reinterpret_u64_s16(X) X +#define npyv_reinterpret_u64_u32(X) X +#define npyv_reinterpret_u64_s32(X) X +#define npyv_reinterpret_u64_s64(X) X +#define npyv_reinterpret_u64_f32 _mm512_castps_si512 +#define npyv_reinterpret_u64_f64 _mm512_castpd_si512 + +#define npyv_reinterpret_s64_s64(X) X +#define npyv_reinterpret_s64_u8(X) X +#define npyv_reinterpret_s64_s8(X) X +#define npyv_reinterpret_s64_u16(X) X +#define npyv_reinterpret_s64_s16(X) X +#define npyv_reinterpret_s64_u32(X) X +#define npyv_reinterpret_s64_s32(X) X +#define npyv_reinterpret_s64_u64(X) X +#define npyv_reinterpret_s64_f32 _mm512_castps_si512 +#define npyv_reinterpret_s64_f64 _mm512_castpd_si512 + +#define npyv_reinterpret_f32_f32(X) X +#define npyv_reinterpret_f32_u8 _mm512_castsi512_ps +#define npyv_reinterpret_f32_s8 _mm512_castsi512_ps +#define npyv_reinterpret_f32_u16 _mm512_castsi512_ps +#define npyv_reinterpret_f32_s16 _mm512_castsi512_ps +#define npyv_reinterpret_f32_u32 _mm512_castsi512_ps +#define npyv_reinterpret_f32_s32 _mm512_castsi512_ps +#define npyv_reinterpret_f32_u64 _mm512_castsi512_ps +#define npyv_reinterpret_f32_s64 _mm512_castsi512_ps +#define npyv_reinterpret_f32_f64 _mm512_castpd_ps + +#define npyv_reinterpret_f64_f64(X) X +#define npyv_reinterpret_f64_u8 _mm512_castsi512_pd +#define npyv_reinterpret_f64_s8 _mm512_castsi512_pd +#define npyv_reinterpret_f64_u16 _mm512_castsi512_pd +#define npyv_reinterpret_f64_s16 _mm512_castsi512_pd +#define npyv_reinterpret_f64_u32 _mm512_castsi512_pd +#define npyv_reinterpret_f64_s32 _mm512_castsi512_pd +#define npyv_reinterpret_f64_u64 _mm512_castsi512_pd +#define npyv_reinterpret_f64_s64 _mm512_castsi512_pd +#define npyv_reinterpret_f64_f32 _mm512_castps_pd + +#ifdef NPY_HAVE_AVX512_KNL + #define npyv_cleanup() ((void)0) +#else + #define npyv_cleanup _mm256_zeroall +#endif + +#endif // _NPY_SIMD_AVX512_MISC_H diff --git a/numpy/core/src/common/simd/avx512/operators.h b/numpy/core/src/common/simd/avx512/operators.h new file mode 100644 index 000000000000..d53932fa8726 --- /dev/null +++ b/numpy/core/src/common/simd/avx512/operators.h @@ -0,0 +1,324 @@ +#ifndef NPY_SIMD + #error "Not a standalone header" +#endif + +#ifndef _NPY_SIMD_AVX512_OPERATORS_H +#define _NPY_SIMD_AVX512_OPERATORS_H + +/*************************** + * Shifting + ***************************/ + +// left +#ifdef NPY_HAVE_AVX512BW + #define npyv_shl_u16(A, C) _mm512_sll_epi16(A, _mm_cvtsi32_si128(C)) +#else + #define NPYV_IMPL_AVX512_SHIFT(FN, INTRIN) \ + NPY_FINLINE __m512i npyv_##FN(__m512i a, int c) \ + { \ + __m256i l = npyv512_lower_si256(a); \ + __m256i h = npyv512_higher_si256(a); \ + __m128i cv = _mm_cvtsi32_si128(c); \ + l = _mm256_##INTRIN(l, cv); \ + h = _mm256_##INTRIN(h, cv); \ + return npyv512_combine_si256(l, h); \ + } + + NPYV_IMPL_AVX512_SHIFT(shl_u16, sll_epi16) +#endif +#define npyv_shl_s16 npyv_shl_u16 +#define npyv_shl_u32(A, C) _mm512_sll_epi32(A, _mm_cvtsi32_si128(C)) +#define npyv_shl_s32(A, C) _mm512_sll_epi32(A, _mm_cvtsi32_si128(C)) +#define npyv_shl_u64(A, C) _mm512_sll_epi64(A, _mm_cvtsi32_si128(C)) +#define npyv_shl_s64(A, C) _mm512_sll_epi64(A, _mm_cvtsi32_si128(C)) + +// left by an immediate constant +#ifdef NPY_HAVE_AVX512BW + #define npyv_shli_u16 _mm512_slli_epi16 +#else + #define npyv_shli_u16 npyv_shl_u16 +#endif +#define npyv_shli_s16 npyv_shl_u16 +#define npyv_shli_u32 _mm512_slli_epi32 +#define npyv_shli_s32 _mm512_slli_epi32 +#define npyv_shli_u64 _mm512_slli_epi64 +#define npyv_shli_s64 _mm512_slli_epi64 + +// right +#ifdef NPY_HAVE_AVX512BW + #define npyv_shr_u16(A, C) _mm512_srl_epi16(A, _mm_cvtsi32_si128(C)) + #define npyv_shr_s16(A, C) _mm512_sra_epi16(A, _mm_cvtsi32_si128(C)) +#else + NPYV_IMPL_AVX512_SHIFT(shr_u16, srl_epi16) + NPYV_IMPL_AVX512_SHIFT(shr_s16, sra_epi16) +#endif +#define npyv_shr_u32(A, C) _mm512_srl_epi32(A, _mm_cvtsi32_si128(C)) +#define npyv_shr_s32(A, C) _mm512_sra_epi32(A, _mm_cvtsi32_si128(C)) +#define npyv_shr_u64(A, C) _mm512_srl_epi64(A, _mm_cvtsi32_si128(C)) +#define npyv_shr_s64(A, C) _mm512_sra_epi64(A, _mm_cvtsi32_si128(C)) + +// right by an immediate constant +#ifdef NPY_HAVE_AVX512BW + #define npyv_shri_u16 _mm512_srli_epi16 + #define npyv_shri_s16 _mm512_srai_epi16 +#else + #define npyv_shri_u16 npyv_shr_u16 + #define npyv_shri_s16 npyv_shr_s16 +#endif +#define npyv_shri_u32 _mm512_srli_epi32 +#define npyv_shri_s32 _mm512_srai_epi32 +#define npyv_shri_u64 _mm512_srli_epi64 +#define npyv_shri_s64 _mm512_srai_epi64 + +/*************************** + * Logical + ***************************/ + +// AND +#define npyv_and_u8 _mm512_and_si512 +#define npyv_and_s8 _mm512_and_si512 +#define npyv_and_u16 _mm512_and_si512 +#define npyv_and_s16 _mm512_and_si512 +#define npyv_and_u32 _mm512_and_si512 +#define npyv_and_s32 _mm512_and_si512 +#define npyv_and_u64 _mm512_and_si512 +#define npyv_and_s64 _mm512_and_si512 +#ifdef NPY_HAVE_AVX512DQ + #define npyv_and_f32 _mm512_and_ps + #define npyv_and_f64 _mm512_and_pd +#else + NPYV_IMPL_AVX512_FROM_SI512_PS_2ARG(npyv_and_f32, _mm512_and_si512) + NPYV_IMPL_AVX512_FROM_SI512_PD_2ARG(npyv_and_f64, _mm512_and_si512) +#endif +// OR +#define npyv_or_u8 _mm512_or_si512 +#define npyv_or_s8 _mm512_or_si512 +#define npyv_or_u16 _mm512_or_si512 +#define npyv_or_s16 _mm512_or_si512 +#define npyv_or_u32 _mm512_or_si512 +#define npyv_or_s32 _mm512_or_si512 +#define npyv_or_u64 _mm512_or_si512 +#define npyv_or_s64 _mm512_or_si512 +#ifdef NPY_HAVE_AVX512DQ + #define npyv_or_f32 _mm512_or_ps + #define npyv_or_f64 _mm512_or_pd +#else + NPYV_IMPL_AVX512_FROM_SI512_PS_2ARG(npyv_or_f32, _mm512_or_si512) + NPYV_IMPL_AVX512_FROM_SI512_PD_2ARG(npyv_or_f64, _mm512_or_si512) +#endif + +// XOR +#define npyv_xor_u8 _mm512_xor_si512 +#define npyv_xor_s8 _mm512_xor_si512 +#define npyv_xor_u16 _mm512_xor_si512 +#define npyv_xor_s16 _mm512_xor_si512 +#define npyv_xor_u32 _mm512_xor_si512 +#define npyv_xor_s32 _mm512_xor_si512 +#define npyv_xor_u64 _mm512_xor_si512 +#define npyv_xor_s64 _mm512_xor_si512 +#ifdef NPY_HAVE_AVX512DQ + #define npyv_xor_f32 _mm512_xor_ps + #define npyv_xor_f64 _mm512_xor_pd +#else + NPYV_IMPL_AVX512_FROM_SI512_PS_2ARG(npyv_xor_f32, _mm512_xor_si512) + NPYV_IMPL_AVX512_FROM_SI512_PD_2ARG(npyv_xor_f64, _mm512_xor_si512) +#endif +// NOT +#define npyv_not_u8(A) _mm512_xor_si512(A, _mm512_set1_epi32(-1)) +#define npyv_not_s8 npyv_not_u8 +#define npyv_not_u16 npyv_not_u8 +#define npyv_not_s16 npyv_not_u8 +#define npyv_not_u32 npyv_not_u8 +#define npyv_not_s32 npyv_not_u8 +#define npyv_not_u64 npyv_not_u8 +#define npyv_not_s64 npyv_not_u8 +#ifdef NPY_HAVE_AVX512DQ + #define npyv_not_f32(A) _mm512_xor_ps(A, _mm512_castsi512_ps(_mm512_set1_epi32(-1))) + #define npyv_not_f64(A) _mm512_xor_pd(A, _mm512_castsi512_pd(_mm512_set1_epi32(-1))) +#else + #define npyv_not_f32(A) _mm512_castsi512_ps(npyv_not_u32(_mm512_castps_si512(A))) + #define npyv_not_f64(A) _mm512_castsi512_pd(npyv_not_u64(_mm512_castpd_si512(A))) +#endif + +/*************************** + * Logical (boolean) + ***************************/ +#ifdef NPY_HAVE_AVX512BW_MASK + #define npyv_and_b8 _kand_mask64 + #define npyv_and_b16 _kand_mask32 + #define npyv_or_b8 _kor_mask64 + #define npyv_or_b16 _kor_mask32 + #define npyv_xor_b8 _kxor_mask64 + #define npyv_xor_b16 _kxor_mask32 + #define npyv_not_b8 _knot_mask64 + #define npyv_not_b16 _knot_mask32 +#elif defined(NPY_HAVE_AVX512BW) + NPY_FINLINE npyv_b8 npyv_and_b8(npyv_b8 a, npyv_b8 b) + { return a & b; } + NPY_FINLINE npyv_b16 npyv_and_b16(npyv_b16 a, npyv_b16 b) + { return a & b; } + NPY_FINLINE npyv_b8 npyv_or_b8(npyv_b8 a, npyv_b8 b) + { return a | b; } + NPY_FINLINE npyv_b16 npyv_or_b16(npyv_b16 a, npyv_b16 b) + { return a | b; } + NPY_FINLINE npyv_b8 npyv_xor_b8(npyv_b8 a, npyv_b8 b) + { return a ^ b; } + NPY_FINLINE npyv_b16 npyv_xor_b16(npyv_b16 a, npyv_b16 b) + { return a ^ b; } + NPY_FINLINE npyv_b8 npyv_not_b8(npyv_b8 a) + { return ~a; } + NPY_FINLINE npyv_b16 npyv_not_b16(npyv_b16 a) + { return ~a; } +#else + #define npyv_and_b8 _mm512_and_si512 + #define npyv_and_b16 _mm512_and_si512 + #define npyv_or_b8 _mm512_or_si512 + #define npyv_or_b16 _mm512_or_si512 + #define npyv_xor_b8 _mm512_xor_si512 + #define npyv_xor_b16 _mm512_xor_si512 + #define npyv_not_b8 npyv_not_u8 + #define npyv_not_b16 npyv_not_u8 +#endif + +#define npyv_and_b32 _mm512_kand +#define npyv_or_b32 _mm512_kor +#define npyv_xor_b32 _mm512_kxor +#define npyv_not_b32 _mm512_knot + +#ifdef NPY_HAVE_AVX512DQ_MASK + #define npyv_and_b64 _kand_mask8 + #define npyv_or_b64 _kor_mask8 + #define npyv_xor_b64 _kxor_mask8 + #define npyv_not_b64 _knot_mask8 +#else + NPY_FINLINE npyv_b64 npyv_and_b64(npyv_b64 a, npyv_b64 b) + { return (npyv_b64)_mm512_kand((npyv_b32)a, (npyv_b32)b); } + NPY_FINLINE npyv_b64 npyv_or_b64(npyv_b64 a, npyv_b64 b) + { return (npyv_b64)_mm512_kor((npyv_b32)a, (npyv_b32)b); } + NPY_FINLINE npyv_b64 npyv_xor_b64(npyv_b64 a, npyv_b64 b) + { return (npyv_b64)_mm512_kxor((npyv_b32)a, (npyv_b32)b); } + NPY_FINLINE npyv_b64 npyv_not_b64(npyv_b64 a) + { return (npyv_b64)_mm512_knot((npyv_b32)a); } +#endif + +/*************************** + * Comparison + ***************************/ + +// int Equal +#ifdef NPY_HAVE_AVX512BW + #define npyv_cmpeq_u8 _mm512_cmpeq_epu8_mask + #define npyv_cmpeq_s8 _mm512_cmpeq_epi8_mask + #define npyv_cmpeq_u16 _mm512_cmpeq_epu16_mask + #define npyv_cmpeq_s16 _mm512_cmpeq_epi16_mask +#else + NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_cmpeq_u8, _mm256_cmpeq_epi8) + NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_cmpeq_u16, _mm256_cmpeq_epi16) + #define npyv_cmpeq_s8 npyv_cmpeq_u8 + #define npyv_cmpeq_s16 npyv_cmpeq_u16 +#endif +#define npyv_cmpeq_u32 _mm512_cmpeq_epu32_mask +#define npyv_cmpeq_s32 _mm512_cmpeq_epi32_mask +#define npyv_cmpeq_u64 _mm512_cmpeq_epu64_mask +#define npyv_cmpeq_s64 _mm512_cmpeq_epi64_mask + +// int not equal +#ifdef NPY_HAVE_AVX512BW + #define npyv_cmpneq_u8 _mm512_cmpneq_epu8_mask + #define npyv_cmpneq_s8 _mm512_cmpneq_epi8_mask + #define npyv_cmpneq_u16 _mm512_cmpneq_epu16_mask + #define npyv_cmpneq_s16 _mm512_cmpneq_epi16_mask +#else + #define npyv_cmpneq_u8(A, B) npyv_not_u8(npyv_cmpeq_u8(A, B)) + #define npyv_cmpneq_u16(A, B) npyv_not_u16(npyv_cmpeq_u16(A, B)) + #define npyv_cmpneq_s8 npyv_cmpneq_u8 + #define npyv_cmpneq_s16 npyv_cmpneq_u16 +#endif +#define npyv_cmpneq_u32 _mm512_cmpneq_epu32_mask +#define npyv_cmpneq_s32 _mm512_cmpneq_epi32_mask +#define npyv_cmpneq_u64 _mm512_cmpneq_epu64_mask +#define npyv_cmpneq_s64 _mm512_cmpneq_epi64_mask + +// greater than +#ifdef NPY_HAVE_AVX512BW + #define npyv_cmpgt_u8 _mm512_cmpgt_epu8_mask + #define npyv_cmpgt_s8 _mm512_cmpgt_epi8_mask + #define npyv_cmpgt_u16 _mm512_cmpgt_epu16_mask + #define npyv_cmpgt_s16 _mm512_cmpgt_epi16_mask +#else + NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_cmpgt_s8, _mm256_cmpgt_epi8) + NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_cmpgt_s16, _mm256_cmpgt_epi16) + NPY_FINLINE __m512i npyv_cmpgt_u8(__m512i a, __m512i b) + { + const __m512i sbit = _mm512_set1_epi32(0x80808080); + return npyv_cmpgt_s8(_mm512_xor_si512(a, sbit), _mm512_xor_si512(b, sbit)); + } + NPY_FINLINE __m512i npyv_cmpgt_u16(__m512i a, __m512i b) + { + const __m512i sbit = _mm512_set1_epi32(0x80008000); + return npyv_cmpgt_s16(_mm512_xor_si512(a, sbit), _mm512_xor_si512(b, sbit)); + } +#endif +#define npyv_cmpgt_u32 _mm512_cmpgt_epu32_mask +#define npyv_cmpgt_s32 _mm512_cmpgt_epi32_mask +#define npyv_cmpgt_u64 _mm512_cmpgt_epu64_mask +#define npyv_cmpgt_s64 _mm512_cmpgt_epi64_mask + +// greater than or equal +#ifdef NPY_HAVE_AVX512BW + #define npyv_cmpge_u8 _mm512_cmpge_epu8_mask + #define npyv_cmpge_s8 _mm512_cmpge_epi8_mask + #define npyv_cmpge_u16 _mm512_cmpge_epu16_mask + #define npyv_cmpge_s16 _mm512_cmpge_epi16_mask +#else + #define npyv_cmpge_u8(A, B) npyv_not_u8(npyv_cmpgt_u8(B, A)) + #define npyv_cmpge_s8(A, B) npyv_not_s8(npyv_cmpgt_s8(B, A)) + #define npyv_cmpge_u16(A, B) npyv_not_u16(npyv_cmpgt_u16(B, A)) + #define npyv_cmpge_s16(A, B) npyv_not_s16(npyv_cmpgt_s16(B, A)) +#endif +#define npyv_cmpge_u32 _mm512_cmpge_epu32_mask +#define npyv_cmpge_s32 _mm512_cmpge_epi32_mask +#define npyv_cmpge_u64 _mm512_cmpge_epu64_mask +#define npyv_cmpge_s64 _mm512_cmpge_epi64_mask + +// less than +#define npyv_cmplt_u8(A, B) npyv_cmpgt_u8(B, A) +#define npyv_cmplt_s8(A, B) npyv_cmpgt_s8(B, A) +#define npyv_cmplt_u16(A, B) npyv_cmpgt_u16(B, A) +#define npyv_cmplt_s16(A, B) npyv_cmpgt_s16(B, A) +#define npyv_cmplt_u32(A, B) npyv_cmpgt_u32(B, A) +#define npyv_cmplt_s32(A, B) npyv_cmpgt_s32(B, A) +#define npyv_cmplt_u64(A, B) npyv_cmpgt_u64(B, A) +#define npyv_cmplt_s64(A, B) npyv_cmpgt_s64(B, A) + +// less than or equal +#define npyv_cmple_u8(A, B) npyv_cmpge_u8(B, A) +#define npyv_cmple_s8(A, B) npyv_cmpge_s8(B, A) +#define npyv_cmple_u16(A, B) npyv_cmpge_u16(B, A) +#define npyv_cmple_s16(A, B) npyv_cmpge_s16(B, A) +#define npyv_cmple_u32(A, B) npyv_cmpge_u32(B, A) +#define npyv_cmple_s32(A, B) npyv_cmpge_s32(B, A) +#define npyv_cmple_u64(A, B) npyv_cmpge_u64(B, A) +#define npyv_cmple_s64(A, B) npyv_cmpge_s64(B, A) + +// precision comparison +#define npyv_cmpeq_f32(A, B) _mm512_cmp_ps_mask(A, B, _CMP_EQ_OQ) +#define npyv_cmpeq_f64(A, B) _mm512_cmp_pd_mask(A, B, _CMP_EQ_OQ) +#define npyv_cmpneq_f32(A, B) _mm512_cmp_ps_mask(A, B, _CMP_NEQ_OQ) +#define npyv_cmpneq_f64(A, B) _mm512_cmp_pd_mask(A, B, _CMP_NEQ_OQ) +#define npyv_cmplt_f32(A, B) _mm512_cmp_ps_mask(A, B, _CMP_LT_OQ) +#define npyv_cmplt_f64(A, B) _mm512_cmp_pd_mask(A, B, _CMP_LT_OQ) +#define npyv_cmple_f32(A, B) _mm512_cmp_ps_mask(A, B, _CMP_LE_OQ) +#define npyv_cmple_f64(A, B) _mm512_cmp_pd_mask(A, B, _CMP_LE_OQ) +#define npyv_cmpgt_f32(A, B) _mm512_cmp_ps_mask(A, B, _CMP_GT_OQ) +#define npyv_cmpgt_f64(A, B) _mm512_cmp_pd_mask(A, B, _CMP_GT_OQ) +#define npyv_cmpge_f32(A, B) _mm512_cmp_ps_mask(A, B, _CMP_GE_OQ) +#define npyv_cmpge_f64(A, B) _mm512_cmp_pd_mask(A, B, _CMP_GE_OQ) + +// check special cases +NPY_FINLINE npyv_b32 npyv_notnan_f32(npyv_f32 a) +{ return _mm512_cmp_ps_mask(a, a, _CMP_ORD_Q); } +NPY_FINLINE npyv_b64 npyv_notnan_f64(npyv_f64 a) +{ return _mm512_cmp_pd_mask(a, a, _CMP_ORD_Q); } + +#endif // _NPY_SIMD_AVX512_OPERATORS_H diff --git a/numpy/core/src/common/simd/avx512/reorder.h b/numpy/core/src/common/simd/avx512/reorder.h new file mode 100644 index 000000000000..f043004ecc45 --- /dev/null +++ b/numpy/core/src/common/simd/avx512/reorder.h @@ -0,0 +1,226 @@ +#ifndef NPY_SIMD + #error "Not a standalone header" +#endif + +#ifndef _NPY_SIMD_AVX512_REORDER_H +#define _NPY_SIMD_AVX512_REORDER_H + +// combine lower part of two vectors +#define npyv_combinel_u8(A, B) _mm512_inserti64x4(A, _mm512_castsi512_si256(B), 1) +#define npyv_combinel_s8 npyv_combinel_u8 +#define npyv_combinel_u16 npyv_combinel_u8 +#define npyv_combinel_s16 npyv_combinel_u8 +#define npyv_combinel_u32 npyv_combinel_u8 +#define npyv_combinel_s32 npyv_combinel_u8 +#define npyv_combinel_u64 npyv_combinel_u8 +#define npyv_combinel_s64 npyv_combinel_u8 +#define npyv_combinel_f64(A, B) _mm512_insertf64x4(A, _mm512_castpd512_pd256(B), 1) +#ifdef NPY_HAVE_AVX512DQ + #define npyv_combinel_f32(A, B) \ + _mm512_insertf32x8(A, _mm512_castps512_ps256(B), 1) +#else + #define npyv_combinel_f32(A, B) \ + _mm512_castsi512_ps(npyv_combinel_u8(_mm512_castps_si512(A), _mm512_castps_si512(B))) +#endif + +// combine higher part of two vectors +#define npyv_combineh_u8(A, B) _mm512_inserti64x4(B, _mm512_extracti64x4_epi64(A, 1), 0) +#define npyv_combineh_s8 npyv_combineh_u8 +#define npyv_combineh_u16 npyv_combineh_u8 +#define npyv_combineh_s16 npyv_combineh_u8 +#define npyv_combineh_u32 npyv_combineh_u8 +#define npyv_combineh_s32 npyv_combineh_u8 +#define npyv_combineh_u64 npyv_combineh_u8 +#define npyv_combineh_s64 npyv_combineh_u8 +#define npyv_combineh_f64(A, B) _mm512_insertf64x4(B, _mm512_extractf64x4_pd(A, 1), 0) +#ifdef NPY_HAVE_AVX512DQ + #define npyv_combineh_f32(A, B) \ + _mm512_insertf32x8(B, _mm512_extractf32x8_ps(A, 1), 0) +#else + #define npyv_combineh_f32(A, B) \ + _mm512_castsi512_ps(npyv_combineh_u8(_mm512_castps_si512(A), _mm512_castps_si512(B))) +#endif + +// combine two vectors from lower and higher parts of two other vectors +NPY_FINLINE npyv_m512ix2 npyv__combine(__m512i a, __m512i b) +{ + npyv_m512ix2 r; + r.val[0] = npyv_combinel_u8(a, b); + r.val[1] = npyv_combineh_u8(a, b); + return r; +} +NPY_FINLINE npyv_f32x2 npyv_combine_f32(__m512 a, __m512 b) +{ + npyv_f32x2 r; + r.val[0] = npyv_combinel_f32(a, b); + r.val[1] = npyv_combineh_f32(a, b); + return r; +} +NPY_FINLINE npyv_f64x2 npyv_combine_f64(__m512d a, __m512d b) +{ + npyv_f64x2 r; + r.val[0] = npyv_combinel_f64(a, b); + r.val[1] = npyv_combineh_f64(a, b); + return r; +} +#define npyv_combine_u8 npyv__combine +#define npyv_combine_s8 npyv__combine +#define npyv_combine_u16 npyv__combine +#define npyv_combine_s16 npyv__combine +#define npyv_combine_u32 npyv__combine +#define npyv_combine_s32 npyv__combine +#define npyv_combine_u64 npyv__combine +#define npyv_combine_s64 npyv__combine + +// interleave two vectors +#ifndef NPY_HAVE_AVX512BW + NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv__unpacklo_epi8, _mm256_unpacklo_epi8) + NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv__unpackhi_epi8, _mm256_unpackhi_epi8) + NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv__unpacklo_epi16, _mm256_unpacklo_epi16) + NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv__unpackhi_epi16, _mm256_unpackhi_epi16) +#endif + +NPY_FINLINE npyv_u64x2 npyv_zip_u64(__m512i a, __m512i b) +{ + npyv_u64x2 r; + r.val[0] = _mm512_permutex2var_epi64(a, npyv_set_u64(0, 8, 1, 9, 2, 10, 3, 11), b); + r.val[1] = _mm512_permutex2var_epi64(a, npyv_set_u64(4, 12, 5, 13, 6, 14, 7, 15), b); + return r; +} +#define npyv_zip_s64 npyv_zip_u64 + +NPY_FINLINE npyv_u8x2 npyv_zip_u8(__m512i a, __m512i b) +{ + npyv_u8x2 r; +#ifdef NPY_HAVE_AVX512VBMI + r.val[0] = _mm512_permutex2var_epi8(a, + npyv_set_u8(0, 64, 1, 65, 2, 66, 3, 67, 4, 68, 5, 69, 6, 70, 7, 71, + 8, 72, 9, 73, 10, 74, 11, 75, 12, 76, 13, 77, 14, 78, 15, 79, + 16, 80, 17, 81, 18, 82, 19, 83, 20, 84, 21, 85, 22, 86, 23, 87, + 24, 88, 25, 89, 26, 90, 27, 91, 28, 92, 29, 93, 30, 94, 31, 95), b); + r.val[1] = _mm512_permutex2var_epi8(a, + npyv_set_u8(32, 96, 33, 97, 34, 98, 35, 99, 36, 100, 37, 101, 38, 102, 39, 103, + 40, 104, 41, 105, 42, 106, 43, 107, 44, 108, 45, 109, 46, 110, 47, 111, + 48, 112, 49, 113, 50, 114, 51, 115, 52, 116, 53, 117, 54, 118, 55, 119, + 56, 120, 57, 121, 58, 122, 59, 123, 60, 124, 61, 125, 62, 126, 63, 127), b); +#else + #ifdef NPY_HAVE_AVX512BW + __m512i ab0 = _mm512_unpacklo_epi8(a, b); + __m512i ab1 = _mm512_unpackhi_epi8(a, b); + #else + __m512i ab0 = npyv__unpacklo_epi8(a, b); + __m512i ab1 = npyv__unpackhi_epi8(a, b); + #endif + r.val[0] = _mm512_permutex2var_epi64(ab0, npyv_set_u64(0, 1, 8, 9, 2, 3, 10, 11), ab1); + r.val[1] = _mm512_permutex2var_epi64(ab0, npyv_set_u64(4, 5, 12, 13, 6, 7, 14, 15), ab1); +#endif + return r; +} +#define npyv_zip_s8 npyv_zip_u8 + +NPY_FINLINE npyv_u16x2 npyv_zip_u16(__m512i a, __m512i b) +{ + npyv_u16x2 r; +#ifdef NPY_HAVE_AVX512BW + r.val[0] = _mm512_permutex2var_epi16(a, + npyv_set_u16(0, 32, 1, 33, 2, 34, 3, 35, 4, 36, 5, 37, 6, 38, 7, 39, + 8, 40, 9, 41, 10, 42, 11, 43, 12, 44, 13, 45, 14, 46, 15, 47), b); + r.val[1] = _mm512_permutex2var_epi16(a, + npyv_set_u16(16, 48, 17, 49, 18, 50, 19, 51, 20, 52, 21, 53, 22, 54, 23, 55, + 24, 56, 25, 57, 26, 58, 27, 59, 28, 60, 29, 61, 30, 62, 31, 63), b); +#else + __m512i ab0 = npyv__unpacklo_epi16(a, b); + __m512i ab1 = npyv__unpackhi_epi16(a, b); + r.val[0] = _mm512_permutex2var_epi64(ab0, npyv_set_u64(0, 1, 8, 9, 2, 3, 10, 11), ab1); + r.val[1] = _mm512_permutex2var_epi64(ab0, npyv_set_u64(4, 5, 12, 13, 6, 7, 14, 15), ab1); +#endif + return r; +} +#define npyv_zip_s16 npyv_zip_u16 + +NPY_FINLINE npyv_u32x2 npyv_zip_u32(__m512i a, __m512i b) +{ + npyv_u32x2 r; + r.val[0] = _mm512_permutex2var_epi32(a, + npyv_set_u32(0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23), b); + r.val[1] = _mm512_permutex2var_epi32(a, + npyv_set_u32(8, 24, 9, 25, 10, 26, 11, 27, 12, 28, 13, 29, 14, 30, 15, 31), b); + return r; +} +#define npyv_zip_s32 npyv_zip_u32 + +NPY_FINLINE npyv_f32x2 npyv_zip_f32(__m512 a, __m512 b) +{ + npyv_f32x2 r; + r.val[0] = _mm512_permutex2var_ps(a, + npyv_set_u32(0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23), b); + r.val[1] = _mm512_permutex2var_ps(a, + npyv_set_u32(8, 24, 9, 25, 10, 26, 11, 27, 12, 28, 13, 29, 14, 30, 15, 31), b); + return r; +} + +NPY_FINLINE npyv_f64x2 npyv_zip_f64(__m512d a, __m512d b) +{ + npyv_f64x2 r; + r.val[0] = _mm512_permutex2var_pd(a, npyv_set_u64(0, 8, 1, 9, 2, 10, 3, 11), b); + r.val[1] = _mm512_permutex2var_pd(a, npyv_set_u64(4, 12, 5, 13, 6, 14, 7, 15), b); + return r; +} + +// Reverse elements of each 64-bit lane +NPY_FINLINE npyv_u8 npyv_rev64_u8(npyv_u8 a) +{ +#ifdef NPY_HAVE_AVX512BW + const __m512i idx = npyv_set_u8( + 7, 6, 5, 4, 3, 2, 1, 0,/*64*/15, 14, 13, 12, 11, 10, 9, 8, + 7, 6, 5, 4, 3, 2, 1, 0,/*64*/15, 14, 13, 12, 11, 10, 9, 8, + 7, 6, 5, 4, 3, 2, 1, 0,/*64*/15, 14, 13, 12, 11, 10, 9, 8, + 7, 6, 5, 4, 3, 2, 1, 0,/*64*/15, 14, 13, 12, 11, 10, 9, 8 + ); + return _mm512_shuffle_epi8(a, idx); +#else + const __m256i idx = _mm256_setr_epi8( + 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, + 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8 + ); + __m256i lo = _mm256_shuffle_epi8(npyv512_lower_si256(a), idx); + __m256i hi = _mm256_shuffle_epi8(npyv512_higher_si256(a), idx); + return npyv512_combine_si256(lo, hi); +#endif +} +#define npyv_rev64_s8 npyv_rev64_u8 + +NPY_FINLINE npyv_u16 npyv_rev64_u16(npyv_u16 a) +{ +#ifdef NPY_HAVE_AVX512BW + const __m512i idx = npyv_set_u8( + 6, 7, 4, 5, 2, 3, 0, 1,/*64*/14, 15, 12, 13, 10, 11, 8, 9, + 6, 7, 4, 5, 2, 3, 0, 1,/*64*/14, 15, 12, 13, 10, 11, 8, 9, + 6, 7, 4, 5, 2, 3, 0, 1,/*64*/14, 15, 12, 13, 10, 11, 8, 9, + 6, 7, 4, 5, 2, 3, 0, 1,/*64*/14, 15, 12, 13, 10, 11, 8, 9 + ); + return _mm512_shuffle_epi8(a, idx); +#else + const __m256i idx = _mm256_setr_epi8( + 6, 7, 4, 5, 2, 3, 0, 1,/*64*/14, 15, 12, 13, 10, 11, 8, 9, + 6, 7, 4, 5, 2, 3, 0, 1,/*64*/14, 15, 12, 13, 10, 11, 8, 9 + ); + __m256i lo = _mm256_shuffle_epi8(npyv512_lower_si256(a), idx); + __m256i hi = _mm256_shuffle_epi8(npyv512_higher_si256(a), idx); + return npyv512_combine_si256(lo, hi); +#endif +} +#define npyv_rev64_s16 npyv_rev64_u16 + +NPY_FINLINE npyv_u32 npyv_rev64_u32(npyv_u32 a) +{ + return _mm512_shuffle_epi32(a, _MM_SHUFFLE(2, 3, 0, 1)); +} +#define npyv_rev64_s32 npyv_rev64_u32 + +NPY_FINLINE npyv_f32 npyv_rev64_f32(npyv_f32 a) +{ + return _mm512_shuffle_ps(a, a, _MM_SHUFFLE(2, 3, 0, 1)); +} + +#endif // _NPY_SIMD_AVX512_REORDER_H diff --git a/numpy/core/src/common/simd/avx512/utils.h b/numpy/core/src/common/simd/avx512/utils.h new file mode 100644 index 000000000000..c3079283f491 --- /dev/null +++ b/numpy/core/src/common/simd/avx512/utils.h @@ -0,0 +1,90 @@ +#ifndef NPY_SIMD + #error "Not a standalone header" +#endif + +#ifndef _NPY_SIMD_AVX512_UTILS_H +#define _NPY_SIMD_AVX512_UTILS_H + +#define npyv512_lower_si256 _mm512_castsi512_si256 +#define npyv512_lower_ps256 _mm512_castps512_ps256 +#define npyv512_lower_pd256 _mm512_castpd512_pd256 + +#define npyv512_higher_si256(A) _mm512_extracti64x4_epi64(A, 1) +#define npyv512_higher_pd256(A) _mm512_extractf64x4_pd(A, 1) + +#ifdef NPY_HAVE_AVX512DQ + #define npyv512_higher_ps256(A) _mm512_extractf32x8_ps(A, 1) +#else + #define npyv512_higher_ps256(A) \ + _mm256_castsi256_ps(_mm512_extracti64x4_epi64(_mm512_castps_si512(A), 1)) +#endif + +#define npyv512_combine_si256(A, B) _mm512_inserti64x4(_mm512_castsi256_si512(A), B, 1) +#define npyv512_combine_pd256(A, B) _mm512_insertf64x4(_mm512_castpd256_pd512(A), B, 1) + +#ifdef NPY_HAVE_AVX512DQ + #define npyv512_combine_ps256(A, B) _mm512_insertf32x8(_mm512_castps256_ps512(A), B, 1) +#else + #define npyv512_combine_ps256(A, B) \ + _mm512_castsi512_ps(npyv512_combine_si256(_mm256_castps_si256(A), _mm256_castps_si256(B))) +#endif + +#define NPYV_IMPL_AVX512_FROM_AVX2_1ARG(FN_NAME, INTRIN) \ + NPY_FINLINE __m512i FN_NAME(__m512i a) \ + { \ + __m256i l_a = npyv512_lower_si256(a); \ + __m256i h_a = npyv512_higher_si256(a); \ + l_a = INTRIN(l_a); \ + h_a = INTRIN(h_a); \ + return npyv512_combine_si256(l_a, h_a); \ + } + +#define NPYV_IMPL_AVX512_FROM_AVX2_PS_1ARG(FN_NAME, INTRIN) \ + NPY_FINLINE __m512 FN_NAME(__m512 a) \ + { \ + __m256 l_a = npyv512_lower_ps256(a); \ + __m256 h_a = npyv512_higher_ps256(a); \ + l_a = INTRIN(l_a); \ + h_a = INTRIN(h_a); \ + return npyv512_combine_ps256(l_a, h_a); \ + } + +#define NPYV_IMPL_AVX512_FROM_AVX2_PD_1ARG(FN_NAME, INTRIN) \ + NPY_FINLINE __m512d FN_NAME(__m512d a) \ + { \ + __m256d l_a = npyv512_lower_pd256(a); \ + __m256d h_a = npyv512_higher_pd256(a); \ + l_a = INTRIN(l_a); \ + h_a = INTRIN(h_a); \ + return npyv512_combine_pd256(l_a, h_a); \ + } + +#define NPYV_IMPL_AVX512_FROM_AVX2_2ARG(FN_NAME, INTRIN) \ + NPY_FINLINE __m512i FN_NAME(__m512i a, __m512i b) \ + { \ + __m256i l_a = npyv512_lower_si256(a); \ + __m256i h_a = npyv512_higher_si256(a); \ + __m256i l_b = npyv512_lower_si256(b); \ + __m256i h_b = npyv512_higher_si256(b); \ + l_a = INTRIN(l_a, l_b); \ + h_a = INTRIN(h_a, h_b); \ + return npyv512_combine_si256(l_a, h_a); \ + } + +#define NPYV_IMPL_AVX512_FROM_SI512_PS_2ARG(FN_NAME, INTRIN) \ + NPY_FINLINE __m512 FN_NAME(__m512 a, __m512 b) \ + { \ + return _mm512_castsi512_ps(INTRIN( \ + _mm512_castps_si512(a), _mm512_castps_si512(b) \ + )); \ + } + +#define NPYV_IMPL_AVX512_FROM_SI512_PD_2ARG(FN_NAME, INTRIN) \ + NPY_FINLINE __m512d FN_NAME(__m512d a, __m512d b) \ + { \ + return _mm512_castsi512_pd(INTRIN( \ + _mm512_castpd_si512(a), _mm512_castpd_si512(b) \ + )); \ + } + +#endif // _NPY_SIMD_AVX512_UTILS_H diff --git a/numpy/core/src/common/simd/emulate_maskop.h b/numpy/core/src/common/simd/emulate_maskop.h new file mode 100644 index 000000000000..41e397c2d301 --- /dev/null +++ b/numpy/core/src/common/simd/emulate_maskop.h @@ -0,0 +1,44 @@ +/** + * This header is used internally by all current supported SIMD extensions, + * execpt for AVX512. + */ +#ifndef NPY_SIMD + #error "Not a standalone header, use simd/simd.h instead" +#endif + +#ifndef _NPY_SIMD_EMULATE_MASKOP_H +#define _NPY_SIMD_EMULATE_MASKOP_H + +/** + * Implements conditional addition and subtraction. + * e.g. npyv_ifadd_f32(mask, a, b, c) -> mask ? a + b : c + * e.g. npyv_ifsub_f32(mask, a, b, c) -> mask ? a - b : c + */ +#define NPYV_IMPL_EMULATE_MASK_ADDSUB(SFX, BSFX) \ + NPY_FINLINE npyv_##SFX npyv_ifadd_##SFX \ + (npyv_##BSFX m, npyv_##SFX a, npyv_##SFX b, npyv_##SFX c) \ + { \ + npyv_##SFX add = npyv_add_##SFX(a, b); \ + return npyv_select_##SFX(m, add, c); \ + } \ + NPY_FINLINE npyv_##SFX npyv_ifsub_##SFX \ + (npyv_##BSFX m, npyv_##SFX a, npyv_##SFX b, npyv_##SFX c) \ + { \ + npyv_##SFX sub = npyv_sub_##SFX(a, b); \ + return npyv_select_##SFX(m, sub, c); \ + } + +NPYV_IMPL_EMULATE_MASK_ADDSUB(u8, b8) +NPYV_IMPL_EMULATE_MASK_ADDSUB(s8, b8) +NPYV_IMPL_EMULATE_MASK_ADDSUB(u16, b16) +NPYV_IMPL_EMULATE_MASK_ADDSUB(s16, b16) +NPYV_IMPL_EMULATE_MASK_ADDSUB(u32, b32) +NPYV_IMPL_EMULATE_MASK_ADDSUB(s32, b32) +NPYV_IMPL_EMULATE_MASK_ADDSUB(u64, b64) +NPYV_IMPL_EMULATE_MASK_ADDSUB(s64, b64) +NPYV_IMPL_EMULATE_MASK_ADDSUB(f32, b32) +#if NPY_SIMD_F64 + NPYV_IMPL_EMULATE_MASK_ADDSUB(f64, b64) +#endif + +#endif // _NPY_SIMD_EMULATE_MASKOP_H diff --git a/numpy/core/src/common/simd/intdiv.h b/numpy/core/src/common/simd/intdiv.h new file mode 100644 index 000000000000..a7a461721dba --- /dev/null +++ b/numpy/core/src/common/simd/intdiv.h @@ -0,0 +1,476 @@ +/** + * This header implements `npyv_divisor_*` intrinsics used for computing the parameters + * of fast integer division, while division intrinsics `npyv_divc_*` are defined in + * {extension}/arithmetic.h. + */ +#ifndef NPY_SIMD + #error "Not a standalone header, use simd/simd.h instead" +#endif +#ifndef _NPY_SIMD_INTDIV_H +#define _NPY_SIMD_INTDIV_H +/********************************************************************************** + ** Integer division + ********************************************************************************** + * Almost all architecture (except Power10) doesn't support integer vector division, + * also the cost of scalar division in architectures like x86 is too high it can take + * 30 to 40 cycles on modern chips and up to 100 on old ones. + * + * Therefore we are using division by multiplying with precomputed reciprocal technique, + * the method that been used in this implementation is based on T. Granlund and P. L. Montgomery + * “Division by invariant integers using multiplication(see [Figure 4.1, 5.1] + * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.1.2556) + * + * It shows a good impact for all architectures especially on X86, + * however computing divisor parameters is kind of expensive so this implementation + * should only works when divisor is a scalar and used multiple of times. + * + * The division process is separated into two intrinsics for each data type + * + * 1- npyv_{dtype}x3 npyv_divisor_{dtype} ({dtype} divisor); + * For computing the divisor parameters (multiplier + shifters + sign of divisor(signed only)) + * + * 2- npyv_{dtype} npyv_divisor_{dtype} (npyv_{dtype} dividend, npyv_{dtype}x3 divisor_parms); + * For performing the final division. + * + ** For example: + * int vstep = npyv_nlanes_s32; // number of lanes + * int x = 0x6e70; + * npyv_s32x3 divisor = npyv_divisor_s32(x); // init divisor params + * for (; len >= vstep; src += vstep, dst += vstep, len -= vstep) { + * npyv_s32 a = npyv_load_s32(*src); // load s32 vector from memory + * a = npyv_divc_s32(a, divisor); // divide all elements by x + * npyv_store_s32(dst, a); // store s32 vector into memory + * } + * + ** NOTES: + * - For 64-bit division on Aarch64 and IBM/Power, we fall-back to the scalar division + * since emulating multiply-high is expensive and both architectures have very fast dividers. + * + ** TODO: + * - Add support for Power10(VSX4) + * + *************************************************************** + ** Figure 4.1: Unsigned division by run–time invariant divisor + *************************************************************** + * Initialization (given uword d with 1 ≤ d < 2^N): + * int l = ceil(log2(d)); + * uword m = 2^N * (2^l− d) / d + 1; + * int sh1 = min(l, 1); + * int sh2 = max(l − 1, 0); + * + * For q = FLOOR(a/d), all uword: + * uword t1 = MULUH(m, a); + * q = SRL(t1 + SRL(a − t1, sh1), sh2); + * + ************************************************************************************ + ** Figure 5.1: Signed division by run–time invariant divisor, rounded towards zero + ************************************************************************************ + * Initialization (given constant sword d with d !=0): + * int l = max(ceil(log2(abs(d))), 1); + * udword m0 = 1 + (2^(N+l-1)) / abs(d); + * sword m = m0 − 2^N; + * sword dsign = XSIGN(d); + * int sh = l − 1; + * + * For q = TRUNC(a/d), all sword: + * sword q0 = a + MULSH(m, a); + * q0 = SRA(q0, sh) − XSIGN(a); + * q = EOR(q0, dsign) − dsign; + */ +/** + * bit-scan reverse for non-zeros. returns the index of the highest set bit. + * equivalent to floor(log2(a)) + */ +#ifdef _MSC_VER + #include <intrin.h> // _BitScanReverse +#endif +NPY_FINLINE unsigned npyv__bitscan_revnz_u32(npy_uint32 a) +{ + assert(a > 0); // due to use __builtin_clz + unsigned r; +#if defined(NPY_HAVE_SSE2) && defined(_MSC_VER) + unsigned long rl; + (void)_BitScanReverse(&rl, (unsigned long)a); + r = (unsigned)rl; +#elif defined(NPY_HAVE_SSE2) && (defined(__GNUC__) || defined(__clang__) || defined(__INTEL_COMPILER)) + __asm__("bsr %1, %0" : "=r" (r) : "r"(a)); +#elif defined(__GNUC__) || defined(__clang__) + r = 31 - __builtin_clz(a); // performs on arm -> clz, ppc -> cntlzw +#else + r = 0; + while (a >>= 1) { + r++; + } +#endif + return r; +} +NPY_FINLINE unsigned npyv__bitscan_revnz_u64(npy_uint64 a) +{ + assert(a > 0); // due to use __builtin_clzll +#if defined(_M_AMD64) && defined(_MSC_VER) + unsigned long rl; + (void)_BitScanReverse64(&rl, a); + return (unsigned)rl; +#elif defined(__x86_64__) && (defined(__GNUC__) || defined(__clang__) || defined(__INTEL_COMPILER)) + npy_uint64 r; + __asm__("bsrq %1, %0" : "=r"(r) : "r"(a)); + return (unsigned)r; +#elif defined(__GNUC__) || defined(__clang__) + return 63 - __builtin_clzll(a); +#else + npy_uint64 a_hi = a >> 32; + if (a_hi == 0) { + return npyv__bitscan_revnz_u32((npy_uint32)a); + } + return 32 + npyv__bitscan_revnz_u32((npy_uint32)a_hi); +#endif +} +/** + * Divides 128-bit unsigned integer by a 64-bit when the lower + * 64-bit of the dividend is zero. + * + * This function is needed to calculate the multiplier of 64-bit integer division + * see npyv_divisor_u64/npyv_divisor_s64. + */ +NPY_FINLINE npy_uint64 npyv__divh128_u64(npy_uint64 high, npy_uint64 divisor) +{ + assert(divisor > 1); + npy_uint64 quotient; +#if defined(_M_X64) && defined(_MSC_VER) && _MSC_VER >= 1920 + npy_uint64 remainder; + quotient = _udiv128(high, 0, divisor, &remainder); + (void)remainder; +#elif defined(__x86_64__) && (defined(__GNUC__) || defined(__clang__) || defined(__INTEL_COMPILER)) + __asm__("divq %[d]" : "=a"(quotient) : [d] "r"(divisor), "a"(0), "d"(high)); +#elif defined(__SIZEOF_INT128__) + quotient = (npy_uint64)((((__uint128_t)high) << 64) / divisor); +#else + /** + * Minified version based on Donald Knuth’s Algorithm D (Division of nonnegative integers), + * and Generic implementation in Hacker’s Delight. + * + * See https://skanthak.homepage.t-online.de/division.html + * with respect to the license of the Hacker's Delight book + * (https://web.archive.org/web/20190408122508/http://www.hackersdelight.org/permissions.htm) + */ + // shift amount for normalize + unsigned ldz = 63 - npyv__bitscan_revnz_u64(divisor); + // normalize divisor + divisor <<= ldz; + high <<= ldz; + // break divisor up into two 32-bit digits + npy_uint32 divisor_hi = divisor >> 32; + npy_uint32 divisor_lo = divisor & 0xFFFFFFFF; + // compute high quotient digit + npy_uint64 quotient_hi = high / divisor_hi; + npy_uint64 remainder = high - divisor_hi * quotient_hi; + npy_uint64 base32 = 1ULL << 32; + while (quotient_hi >= base32 || quotient_hi*divisor_lo > base32*remainder) { + --quotient_hi; + remainder += divisor_hi; + if (remainder >= base32) { + break; + } + } + // compute dividend digit pairs + npy_uint64 dividend_pairs = base32*high - divisor*quotient_hi; + // compute second quotient digit for lower zeros + npy_uint32 quotient_lo = (npy_uint32)(dividend_pairs / divisor_hi); + quotient = base32*quotient_hi + quotient_lo; +#endif + return quotient; +} +// Initializing divisor parameters for unsigned 8-bit division +NPY_FINLINE npyv_u8x3 npyv_divisor_u8(npy_uint8 d) +{ + unsigned l, l2, sh1, sh2, m; + switch (d) { + case 0: // LCOV_EXCL_LINE + // for potential divide by zero, On x86 GCC inserts `ud2` instruction + // instead of letting the HW/CPU trap it which leads to illegal instruction exception. + // 'volatile' should suppress this behavior and allow us to raise HW/CPU + // arithmetic exception. + m = sh1 = sh2 = 1 / ((npy_uint8 volatile *)&d)[0]; + break; + case 1: + m = 1; sh1 = sh2 = 0; + break; + case 2: + m = 1; sh1 = 1; sh2 = 0; + break; + default: + l = npyv__bitscan_revnz_u32(d - 1) + 1; // ceil(log2(d)) + l2 = (npy_uint8)(1 << l); // 2^l, overflow to 0 if l = 8 + m = ((npy_uint16)((l2 - d) << 8)) / d + 1; // multiplier + sh1 = 1; sh2 = l - 1; // shift counts + } + npyv_u8x3 divisor; +#ifdef NPY_HAVE_SSE2 // SSE/AVX2/AVX512 + divisor.val[0] = npyv_setall_u16(m); + divisor.val[1] = npyv_set_u8(sh1); + divisor.val[2] = npyv_set_u8(sh2); +#elif defined(NPY_HAVE_VSX2) + divisor.val[0] = npyv_setall_u8(m); + divisor.val[1] = npyv_setall_u8(sh1); + divisor.val[2] = npyv_setall_u8(sh2); +#elif defined(NPY_HAVE_NEON) + divisor.val[0] = npyv_setall_u8(m); + divisor.val[1] = npyv_reinterpret_u8_s8(npyv_setall_s8(-sh1)); + divisor.val[2] = npyv_reinterpret_u8_s8(npyv_setall_s8(-sh2)); +#else + #error "please initialize the shifting operand for the new architecture" +#endif + return divisor; +} +// Initializing divisor parameters for signed 8-bit division +NPY_FINLINE npyv_s16x3 npyv_divisor_s16(npy_int16 d); +NPY_FINLINE npyv_s8x3 npyv_divisor_s8(npy_int8 d) +{ +#ifdef NPY_HAVE_SSE2 // SSE/AVX2/AVX512 + npyv_s16x3 p = npyv_divisor_s16(d); + npyv_s8x3 r; + r.val[0] = npyv_reinterpret_s8_s16(p.val[0]); + r.val[1] = npyv_reinterpret_s8_s16(p.val[1]); + r.val[2] = npyv_reinterpret_s8_s16(p.val[2]); + return r; +#else + int d1 = abs(d); + int sh, m; + if (d1 > 1) { + sh = (int)npyv__bitscan_revnz_u32(d1-1); // ceil(log2(abs(d))) - 1 + m = (1 << (8 + sh)) / d1 + 1; // multiplier + } + else if (d1 == 1) { + sh = 0; m = 1; + } + else { + // raise arithmetic exception for d == 0 + sh = m = 1 / ((npy_int8 volatile *)&d)[0]; // LCOV_EXCL_LINE + } + npyv_s8x3 divisor; + divisor.val[0] = npyv_setall_s8(m); + divisor.val[2] = npyv_setall_s8(d < 0 ? -1 : 0); + #ifdef NPY_HAVE_VSX2 + divisor.val[1] = npyv_setall_s8(sh); + #elif defined(NPY_HAVE_NEON) + divisor.val[1] = npyv_setall_s8(-sh); + #else + #error "please initialize the shifting operand for the new architecture" + #endif + return divisor; +#endif +} +// Initializing divisor parameters for unsigned 16-bit division +NPY_FINLINE npyv_u16x3 npyv_divisor_u16(npy_uint16 d) +{ + unsigned l, l2, sh1, sh2, m; + switch (d) { + case 0: // LCOV_EXCL_LINE + // raise arithmetic exception for d == 0 + m = sh1 = sh2 = 1 / ((npy_uint16 volatile *)&d)[0]; + break; + case 1: + m = 1; sh1 = sh2 = 0; + break; + case 2: + m = 1; sh1 = 1; sh2 = 0; + break; + default: + l = npyv__bitscan_revnz_u32(d - 1) + 1; // ceil(log2(d)) + l2 = (npy_uint16)(1 << l); // 2^l, overflow to 0 if l = 16 + m = ((l2 - d) << 16) / d + 1; // multiplier + sh1 = 1; sh2 = l - 1; // shift counts + } + npyv_u16x3 divisor; + divisor.val[0] = npyv_setall_u16(m); +#ifdef NPY_HAVE_SSE2 // SSE/AVX2/AVX512 + divisor.val[1] = npyv_set_u16(sh1); + divisor.val[2] = npyv_set_u16(sh2); +#elif defined(NPY_HAVE_VSX2) + divisor.val[1] = npyv_setall_u16(sh1); + divisor.val[2] = npyv_setall_u16(sh2); +#elif defined(NPY_HAVE_NEON) + divisor.val[1] = npyv_reinterpret_u16_s16(npyv_setall_s16(-sh1)); + divisor.val[2] = npyv_reinterpret_u16_s16(npyv_setall_s16(-sh2)); +#else + #error "please initialize the shifting operand for the new architecture" +#endif + return divisor; +} +// Initializing divisor parameters for signed 16-bit division +NPY_FINLINE npyv_s16x3 npyv_divisor_s16(npy_int16 d) +{ + int d1 = abs(d); + int sh, m; + if (d1 > 1) { + sh = (int)npyv__bitscan_revnz_u32(d1 - 1); // ceil(log2(abs(d))) - 1 + m = (1 << (16 + sh)) / d1 + 1; // multiplier + } + else if (d1 == 1) { + sh = 0; m = 1; + } + else { + // raise arithmetic exception for d == 0 + sh = m = 1 / ((npy_int16 volatile *)&d)[0]; // LCOV_EXCL_LINE + } + npyv_s16x3 divisor; + divisor.val[0] = npyv_setall_s16(m); + divisor.val[2] = npyv_setall_s16(d < 0 ? -1 : 0); // sign of divisor +#ifdef NPY_HAVE_SSE2 // SSE/AVX2/AVX512 + divisor.val[1] = npyv_set_s16(sh); +#elif defined(NPY_HAVE_VSX2) + divisor.val[1] = npyv_setall_s16(sh); +#elif defined(NPY_HAVE_NEON) + divisor.val[1] = npyv_setall_s16(-sh); +#else + #error "please initialize the shifting operand for the new architecture" +#endif + return divisor; +} +// Initializing divisor parameters for unsigned 32-bit division +NPY_FINLINE npyv_u32x3 npyv_divisor_u32(npy_uint32 d) +{ + npy_uint32 l, l2, sh1, sh2, m; + switch (d) { + case 0: // LCOV_EXCL_LINE + // raise arithmetic exception for d == 0 + m = sh1 = sh2 = 1 / ((npy_uint32 volatile *)&d)[0]; // LCOV_EXCL_LINE + break; + case 1: + m = 1; sh1 = sh2 = 0; + break; + case 2: + m = 1; sh1 = 1; sh2 = 0; + break; + default: + l = npyv__bitscan_revnz_u32(d - 1) + 1; // ceil(log2(d)) + l2 = (npy_uint32)(1ULL << l); // 2^l, overflow to 0 if l = 32 + m = ((npy_uint64)(l2 - d) << 32) / d + 1; // multiplier + sh1 = 1; sh2 = l - 1; // shift counts + } + npyv_u32x3 divisor; + divisor.val[0] = npyv_setall_u32(m); +#ifdef NPY_HAVE_SSE2 // SSE/AVX2/AVX512 + divisor.val[1] = npyv_set_u32(sh1); + divisor.val[2] = npyv_set_u32(sh2); +#elif defined(NPY_HAVE_VSX2) + divisor.val[1] = npyv_setall_u32(sh1); + divisor.val[2] = npyv_setall_u32(sh2); +#elif defined(NPY_HAVE_NEON) + divisor.val[1] = npyv_reinterpret_u32_s32(npyv_setall_s32(-sh1)); + divisor.val[2] = npyv_reinterpret_u32_s32(npyv_setall_s32(-sh2)); +#else + #error "please initialize the shifting operand for the new architecture" +#endif + return divisor; +} +// Initializing divisor parameters for signed 32-bit division +NPY_FINLINE npyv_s32x3 npyv_divisor_s32(npy_int32 d) +{ + npy_int32 d1 = abs(d); + npy_int32 sh, m; + // Handel abs overflow + if ((npy_uint32)d == 0x80000000U) { + m = 0x80000001; + sh = 30; + } + else if (d1 > 1) { + sh = npyv__bitscan_revnz_u32(d1 - 1); // ceil(log2(abs(d))) - 1 + m = (1ULL << (32 + sh)) / d1 + 1; // multiplier + } + else if (d1 == 1) { + sh = 0; m = 1; + } + else { + // raise arithmetic exception for d == 0 + sh = m = 1 / ((npy_int32 volatile *)&d)[0]; // LCOV_EXCL_LINE + } + npyv_s32x3 divisor; + divisor.val[0] = npyv_setall_s32(m); + divisor.val[2] = npyv_setall_s32(d < 0 ? -1 : 0); // sign of divisor +#ifdef NPY_HAVE_SSE2 // SSE/AVX2/AVX512 + divisor.val[1] = npyv_set_s32(sh); +#elif defined(NPY_HAVE_VSX2) + divisor.val[1] = npyv_setall_s32(sh); +#elif defined(NPY_HAVE_NEON) + divisor.val[1] = npyv_setall_s32(-sh); +#else + #error "please initialize the shifting operand for the new architecture" +#endif + return divisor; +} +// Initializing divisor parameters for unsigned 64-bit division +NPY_FINLINE npyv_u64x3 npyv_divisor_u64(npy_uint64 d) +{ + npyv_u64x3 divisor; +#if defined(NPY_HAVE_VSX2) || defined(NPY_HAVE_NEON) + divisor.val[0] = npyv_setall_u64(d); +#else + npy_uint64 l, l2, sh1, sh2, m; + switch (d) { + case 0: // LCOV_EXCL_LINE + // raise arithmetic exception for d == 0 + m = sh1 = sh2 = 1 / ((npy_uint64 volatile *)&d)[0]; // LCOV_EXCL_LINE + break; + case 1: + m = 1; sh1 = sh2 = 0; + break; + case 2: + m = 1; sh1 = 1; sh2 = 0; + break; + default: + l = npyv__bitscan_revnz_u64(d - 1) + 1; // ceil(log2(d)) + l2 = l < 64 ? 1ULL << l : 0; // 2^l + m = npyv__divh128_u64(l2 - d, d) + 1; // multiplier + sh1 = 1; sh2 = l - 1; // shift counts + } + divisor.val[0] = npyv_setall_u64(m); + #ifdef NPY_HAVE_SSE2 // SSE/AVX2/AVX512 + divisor.val[1] = npyv_set_u64(sh1); + divisor.val[2] = npyv_set_u64(sh2); + #else + #error "please initialize the shifting operand for the new architecture" + #endif +#endif + return divisor; +} +// Initializing divisor parameters for signed 64-bit division +NPY_FINLINE npyv_s64x3 npyv_divisor_s64(npy_int64 d) +{ + npyv_s64x3 divisor; +#if defined(NPY_HAVE_VSX2) || defined(NPY_HAVE_NEON) + divisor.val[0] = npyv_setall_s64(d); + divisor.val[1] = npyv_cvt_s64_b64( + npyv_cmpeq_s64(npyv_setall_s64(-1), divisor.val[0]) + ); +#else + npy_int64 d1 = llabs(d); + npy_int64 sh, m; + // Handel abs overflow + if ((npy_uint64)d == 0x8000000000000000ULL) { + m = 0x8000000000000001LL; + sh = 62; + } + else if (d1 > 1) { + sh = npyv__bitscan_revnz_u64(d1 - 1); // ceil(log2(abs(d))) - 1 + m = npyv__divh128_u64(1ULL << sh, d1) + 1; // multiplier + } + else if (d1 == 1) { + sh = 0; m = 1; + } + else { + // raise arithmetic exception for d == 0 + sh = m = 1 / ((npy_int64 volatile *)&d)[0]; // LCOV_EXCL_LINE + } + divisor.val[0] = npyv_setall_s64(m); + divisor.val[2] = npyv_setall_s64(d < 0 ? -1 : 0); // sign of divisor + #ifdef NPY_HAVE_SSE2 // SSE/AVX2/AVX512 + divisor.val[1] = npyv_set_s64(sh); + #else + #error "please initialize the shifting operand for the new architecture" + #endif +#endif + return divisor; +} + +#endif // _NPY_SIMD_INTDIV_H diff --git a/numpy/core/src/common/simd/neon/arithmetic.h b/numpy/core/src/common/simd/neon/arithmetic.h new file mode 100644 index 000000000000..00994806df68 --- /dev/null +++ b/numpy/core/src/common/simd/neon/arithmetic.h @@ -0,0 +1,330 @@ +#ifndef NPY_SIMD + #error "Not a standalone header" +#endif + +#ifndef _NPY_SIMD_NEON_ARITHMETIC_H +#define _NPY_SIMD_NEON_ARITHMETIC_H + +/*************************** + * Addition + ***************************/ +// non-saturated +#define npyv_add_u8 vaddq_u8 +#define npyv_add_s8 vaddq_s8 +#define npyv_add_u16 vaddq_u16 +#define npyv_add_s16 vaddq_s16 +#define npyv_add_u32 vaddq_u32 +#define npyv_add_s32 vaddq_s32 +#define npyv_add_u64 vaddq_u64 +#define npyv_add_s64 vaddq_s64 +#define npyv_add_f32 vaddq_f32 +#define npyv_add_f64 vaddq_f64 + +// saturated +#define npyv_adds_u8 vqaddq_u8 +#define npyv_adds_s8 vqaddq_s8 +#define npyv_adds_u16 vqaddq_u16 +#define npyv_adds_s16 vqaddq_s16 + +/*************************** + * Subtraction + ***************************/ +// non-saturated +#define npyv_sub_u8 vsubq_u8 +#define npyv_sub_s8 vsubq_s8 +#define npyv_sub_u16 vsubq_u16 +#define npyv_sub_s16 vsubq_s16 +#define npyv_sub_u32 vsubq_u32 +#define npyv_sub_s32 vsubq_s32 +#define npyv_sub_u64 vsubq_u64 +#define npyv_sub_s64 vsubq_s64 +#define npyv_sub_f32 vsubq_f32 +#define npyv_sub_f64 vsubq_f64 + +// saturated +#define npyv_subs_u8 vqsubq_u8 +#define npyv_subs_s8 vqsubq_s8 +#define npyv_subs_u16 vqsubq_u16 +#define npyv_subs_s16 vqsubq_s16 + +/*************************** + * Multiplication + ***************************/ +// non-saturated +#define npyv_mul_u8 vmulq_u8 +#define npyv_mul_s8 vmulq_s8 +#define npyv_mul_u16 vmulq_u16 +#define npyv_mul_s16 vmulq_s16 +#define npyv_mul_u32 vmulq_u32 +#define npyv_mul_s32 vmulq_s32 +#define npyv_mul_f32 vmulq_f32 +#define npyv_mul_f64 vmulq_f64 + +/*************************** + * Integer Division + ***************************/ +// See simd/intdiv.h for more clarification +// divide each unsigned 8-bit element by a precomputed divisor +NPY_FINLINE npyv_u8 npyv_divc_u8(npyv_u8 a, const npyv_u8x3 divisor) +{ + const uint8x8_t mulc_lo = vget_low_u8(divisor.val[0]); + // high part of unsigned multiplication + uint16x8_t mull_lo = vmull_u8(vget_low_u8(a), mulc_lo); +#if NPY_SIMD_F64 + uint16x8_t mull_hi = vmull_high_u8(a, divisor.val[0]); + // get the high unsigned bytes + uint8x16_t mulhi = vuzp2q_u8(vreinterpretq_u8_u16(mull_lo), vreinterpretq_u8_u16(mull_hi)); +#else + const uint8x8_t mulc_hi = vget_high_u8(divisor.val[0]); + uint16x8_t mull_hi = vmull_u8(vget_high_u8(a), mulc_hi); + uint8x16_t mulhi = vuzpq_u8(vreinterpretq_u8_u16(mull_lo), vreinterpretq_u8_u16(mull_hi)).val[1]; +#endif + // floor(a/d) = (mulhi + ((a-mulhi) >> sh1)) >> sh2 + uint8x16_t q = vsubq_u8(a, mulhi); + q = vshlq_u8(q, vreinterpretq_s8_u8(divisor.val[1])); + q = vaddq_u8(mulhi, q); + q = vshlq_u8(q, vreinterpretq_s8_u8(divisor.val[2])); + return q; +} +// divide each signed 8-bit element by a precomputed divisor (round towards zero) +NPY_FINLINE npyv_s8 npyv_divc_s8(npyv_s8 a, const npyv_s8x3 divisor) +{ + const int8x8_t mulc_lo = vget_low_s8(divisor.val[0]); + // high part of signed multiplication + int16x8_t mull_lo = vmull_s8(vget_low_s8(a), mulc_lo); +#if NPY_SIMD_F64 + int16x8_t mull_hi = vmull_high_s8(a, divisor.val[0]); + // get the high unsigned bytes + int8x16_t mulhi = vuzp2q_s8(vreinterpretq_s8_s16(mull_lo), vreinterpretq_s8_s16(mull_hi)); +#else + const int8x8_t mulc_hi = vget_high_s8(divisor.val[0]); + int16x8_t mull_hi = vmull_s8(vget_high_s8(a), mulc_hi); + int8x16_t mulhi = vuzpq_s8(vreinterpretq_s8_s16(mull_lo), vreinterpretq_s8_s16(mull_hi)).val[1]; +#endif + // q = ((a + mulhi) >> sh1) - XSIGN(a) + // trunc(a/d) = (q ^ dsign) - dsign + int8x16_t q = vshlq_s8(vaddq_s8(a, mulhi), divisor.val[1]); + q = vsubq_s8(q, vshrq_n_s8(a, 7)); + q = vsubq_s8(veorq_s8(q, divisor.val[2]), divisor.val[2]); + return q; +} +// divide each unsigned 16-bit element by a precomputed divisor +NPY_FINLINE npyv_u16 npyv_divc_u16(npyv_u16 a, const npyv_u16x3 divisor) +{ + const uint16x4_t mulc_lo = vget_low_u16(divisor.val[0]); + // high part of unsigned multiplication + uint32x4_t mull_lo = vmull_u16(vget_low_u16(a), mulc_lo); +#if NPY_SIMD_F64 + uint32x4_t mull_hi = vmull_high_u16(a, divisor.val[0]); + // get the high unsigned bytes + uint16x8_t mulhi = vuzp2q_u16(vreinterpretq_u16_u32(mull_lo), vreinterpretq_u16_u32(mull_hi)); +#else + const uint16x4_t mulc_hi = vget_high_u16(divisor.val[0]); + uint32x4_t mull_hi = vmull_u16(vget_high_u16(a), mulc_hi); + uint16x8_t mulhi = vuzpq_u16(vreinterpretq_u16_u32(mull_lo), vreinterpretq_u16_u32(mull_hi)).val[1]; +#endif + // floor(a/d) = (mulhi + ((a-mulhi) >> sh1)) >> sh2 + uint16x8_t q = vsubq_u16(a, mulhi); + q = vshlq_u16(q, vreinterpretq_s16_u16(divisor.val[1])); + q = vaddq_u16(mulhi, q); + q = vshlq_u16(q, vreinterpretq_s16_u16(divisor.val[2])); + return q; +} +// divide each signed 16-bit element by a precomputed divisor (round towards zero) +NPY_FINLINE npyv_s16 npyv_divc_s16(npyv_s16 a, const npyv_s16x3 divisor) +{ + const int16x4_t mulc_lo = vget_low_s16(divisor.val[0]); + // high part of signed multiplication + int32x4_t mull_lo = vmull_s16(vget_low_s16(a), mulc_lo); +#if NPY_SIMD_F64 + int32x4_t mull_hi = vmull_high_s16(a, divisor.val[0]); + // get the high unsigned bytes + int16x8_t mulhi = vuzp2q_s16(vreinterpretq_s16_s32(mull_lo), vreinterpretq_s16_s32(mull_hi)); +#else + const int16x4_t mulc_hi = vget_high_s16(divisor.val[0]); + int32x4_t mull_hi = vmull_s16(vget_high_s16(a), mulc_hi); + int16x8_t mulhi = vuzpq_s16(vreinterpretq_s16_s32(mull_lo), vreinterpretq_s16_s32(mull_hi)).val[1]; +#endif + // q = ((a + mulhi) >> sh1) - XSIGN(a) + // trunc(a/d) = (q ^ dsign) - dsign + int16x8_t q = vshlq_s16(vaddq_s16(a, mulhi), divisor.val[1]); + q = vsubq_s16(q, vshrq_n_s16(a, 15)); + q = vsubq_s16(veorq_s16(q, divisor.val[2]), divisor.val[2]); + return q; +} +// divide each unsigned 32-bit element by a precomputed divisor +NPY_FINLINE npyv_u32 npyv_divc_u32(npyv_u32 a, const npyv_u32x3 divisor) +{ + const uint32x2_t mulc_lo = vget_low_u32(divisor.val[0]); + // high part of unsigned multiplication + uint64x2_t mull_lo = vmull_u32(vget_low_u32(a), mulc_lo); +#if NPY_SIMD_F64 + uint64x2_t mull_hi = vmull_high_u32(a, divisor.val[0]); + // get the high unsigned bytes + uint32x4_t mulhi = vuzp2q_u32(vreinterpretq_u32_u64(mull_lo), vreinterpretq_u32_u64(mull_hi)); +#else + const uint32x2_t mulc_hi = vget_high_u32(divisor.val[0]); + uint64x2_t mull_hi = vmull_u32(vget_high_u32(a), mulc_hi); + uint32x4_t mulhi = vuzpq_u32(vreinterpretq_u32_u64(mull_lo), vreinterpretq_u32_u64(mull_hi)).val[1]; +#endif + // floor(a/d) = (mulhi + ((a-mulhi) >> sh1)) >> sh2 + uint32x4_t q = vsubq_u32(a, mulhi); + q = vshlq_u32(q, vreinterpretq_s32_u32(divisor.val[1])); + q = vaddq_u32(mulhi, q); + q = vshlq_u32(q, vreinterpretq_s32_u32(divisor.val[2])); + return q; +} +// divide each signed 32-bit element by a precomputed divisor (round towards zero) +NPY_FINLINE npyv_s32 npyv_divc_s32(npyv_s32 a, const npyv_s32x3 divisor) +{ + const int32x2_t mulc_lo = vget_low_s32(divisor.val[0]); + // high part of signed multiplication + int64x2_t mull_lo = vmull_s32(vget_low_s32(a), mulc_lo); +#if NPY_SIMD_F64 + int64x2_t mull_hi = vmull_high_s32(a, divisor.val[0]); + // get the high unsigned bytes + int32x4_t mulhi = vuzp2q_s32(vreinterpretq_s32_s64(mull_lo), vreinterpretq_s32_s64(mull_hi)); +#else + const int32x2_t mulc_hi = vget_high_s32(divisor.val[0]); + int64x2_t mull_hi = vmull_s32(vget_high_s32(a), mulc_hi); + int32x4_t mulhi = vuzpq_s32(vreinterpretq_s32_s64(mull_lo), vreinterpretq_s32_s64(mull_hi)).val[1]; +#endif + // q = ((a + mulhi) >> sh1) - XSIGN(a) + // trunc(a/d) = (q ^ dsign) - dsign + int32x4_t q = vshlq_s32(vaddq_s32(a, mulhi), divisor.val[1]); + q = vsubq_s32(q, vshrq_n_s32(a, 31)); + q = vsubq_s32(veorq_s32(q, divisor.val[2]), divisor.val[2]); + return q; +} +// divide each unsigned 64-bit element by a divisor +NPY_FINLINE npyv_u64 npyv_divc_u64(npyv_u64 a, const npyv_u64x3 divisor) +{ + const uint64_t d = vgetq_lane_u64(divisor.val[0], 0); + return npyv_set_u64(vgetq_lane_u64(a, 0) / d, vgetq_lane_u64(a, 1) / d); +} +// returns the high 64 bits of signed 64-bit multiplication +NPY_FINLINE npyv_s64 npyv_divc_s64(npyv_s64 a, const npyv_s64x3 divisor) +{ + const int64_t d = vgetq_lane_s64(divisor.val[0], 0); + return npyv_set_s64(vgetq_lane_s64(a, 0) / d, vgetq_lane_s64(a, 1) / d); +} +/*************************** + * Division + ***************************/ +#if NPY_SIMD_F64 + #define npyv_div_f32 vdivq_f32 +#else + NPY_FINLINE npyv_f32 npyv_div_f32(npyv_f32 a, npyv_f32 b) + { + // Based on ARM doc, see https://developer.arm.com/documentation/dui0204/j/CIHDIACI + // estimate to 1/b + npyv_f32 recipe = vrecpeq_f32(b); + /** + * Newton-Raphson iteration: + * x[n+1] = x[n] * (2-d * x[n]) + * converges to (1/d) if x0 is the result of VRECPE applied to d. + * + * NOTE: at least 3 iterations is needed to improve precision + */ + recipe = vmulq_f32(vrecpsq_f32(b, recipe), recipe); + recipe = vmulq_f32(vrecpsq_f32(b, recipe), recipe); + recipe = vmulq_f32(vrecpsq_f32(b, recipe), recipe); + // a/b = a*recip(b) + return vmulq_f32(a, recipe); + } +#endif +#define npyv_div_f64 vdivq_f64 + +/*************************** + * FUSED F32 + ***************************/ +#ifdef NPY_HAVE_NEON_VFPV4 // FMA + // multiply and add, a*b + c + NPY_FINLINE npyv_f32 npyv_muladd_f32(npyv_f32 a, npyv_f32 b, npyv_f32 c) + { return vfmaq_f32(c, a, b); } + // multiply and subtract, a*b - c + NPY_FINLINE npyv_f32 npyv_mulsub_f32(npyv_f32 a, npyv_f32 b, npyv_f32 c) + { return vfmaq_f32(vnegq_f32(c), a, b); } + // negate multiply and add, -(a*b) + c + NPY_FINLINE npyv_f32 npyv_nmuladd_f32(npyv_f32 a, npyv_f32 b, npyv_f32 c) + { return vfmsq_f32(c, a, b); } + // negate multiply and subtract, -(a*b) - c + NPY_FINLINE npyv_f32 npyv_nmulsub_f32(npyv_f32 a, npyv_f32 b, npyv_f32 c) + { return vfmsq_f32(vnegq_f32(c), a, b); } +#else + // multiply and add, a*b + c + NPY_FINLINE npyv_f32 npyv_muladd_f32(npyv_f32 a, npyv_f32 b, npyv_f32 c) + { return vmlaq_f32(c, a, b); } + // multiply and subtract, a*b - c + NPY_FINLINE npyv_f32 npyv_mulsub_f32(npyv_f32 a, npyv_f32 b, npyv_f32 c) + { return vmlaq_f32(vnegq_f32(c), a, b); } + // negate multiply and add, -(a*b) + c + NPY_FINLINE npyv_f32 npyv_nmuladd_f32(npyv_f32 a, npyv_f32 b, npyv_f32 c) + { return vmlsq_f32(c, a, b); } + // negate multiply and subtract, -(a*b) - c + NPY_FINLINE npyv_f32 npyv_nmulsub_f32(npyv_f32 a, npyv_f32 b, npyv_f32 c) + { return vmlsq_f32(vnegq_f32(c), a, b); } +#endif +/*************************** + * FUSED F64 + ***************************/ +#if NPY_SIMD_F64 + NPY_FINLINE npyv_f64 npyv_muladd_f64(npyv_f64 a, npyv_f64 b, npyv_f64 c) + { return vfmaq_f64(c, a, b); } + NPY_FINLINE npyv_f64 npyv_mulsub_f64(npyv_f64 a, npyv_f64 b, npyv_f64 c) + { return vfmaq_f64(vnegq_f64(c), a, b); } + NPY_FINLINE npyv_f64 npyv_nmuladd_f64(npyv_f64 a, npyv_f64 b, npyv_f64 c) + { return vfmsq_f64(c, a, b); } + NPY_FINLINE npyv_f64 npyv_nmulsub_f64(npyv_f64 a, npyv_f64 b, npyv_f64 c) + { return vfmsq_f64(vnegq_f64(c), a, b); } +#endif // NPY_SIMD_F64 + +/*************************** + * Summation + ***************************/ +// reduce sum across vector +#if NPY_SIMD_F64 + #define npyv_sum_u32 vaddvq_u32 + #define npyv_sum_u64 vaddvq_u64 + #define npyv_sum_f32 vaddvq_f32 + #define npyv_sum_f64 vaddvq_f64 +#else + NPY_FINLINE npy_uint64 npyv_sum_u64(npyv_u64 a) + { + return vget_lane_u64(vadd_u64(vget_low_u64(a), vget_high_u64(a)),0); + } + + NPY_FINLINE npy_uint32 npyv_sum_u32(npyv_u32 a) + { + uint32x2_t a0 = vpadd_u32(vget_low_u32(a), vget_high_u32(a)); + return (unsigned)vget_lane_u32(vpadd_u32(a0, vget_high_u32(a)),0); + } + + NPY_FINLINE float npyv_sum_f32(npyv_f32 a) + { + float32x2_t r = vadd_f32(vget_high_f32(a), vget_low_f32(a)); + return vget_lane_f32(vpadd_f32(r, r), 0); + } +#endif + +// expand the source vector and performs sum reduce +#if NPY_SIMD_F64 + #define npyv_sumup_u8 vaddlvq_u8 + #define npyv_sumup_u16 vaddlvq_u16 +#else + NPY_FINLINE npy_uint16 npyv_sumup_u8(npyv_u8 a) + { + uint32x4_t t0 = vpaddlq_u16(vpaddlq_u8(a)); + uint32x2_t t1 = vpadd_u32(vget_low_u32(t0), vget_high_u32(t0)); + return vget_lane_u32(vpadd_u32(t1, t1), 0); + } + + NPY_FINLINE npy_uint32 npyv_sumup_u16(npyv_u16 a) + { + uint32x4_t t0 = vpaddlq_u16(a); + uint32x2_t t1 = vpadd_u32(vget_low_u32(t0), vget_high_u32(t0)); + return vget_lane_u32(vpadd_u32(t1, t1), 0); + } +#endif + +#endif // _NPY_SIMD_NEON_ARITHMETIC_H diff --git a/numpy/core/src/common/simd/neon/conversion.h b/numpy/core/src/common/simd/neon/conversion.h new file mode 100644 index 000000000000..7487559d1c30 --- /dev/null +++ b/numpy/core/src/common/simd/neon/conversion.h @@ -0,0 +1,109 @@ +#ifndef NPY_SIMD + #error "Not a standalone header" +#endif + +#ifndef _NPY_SIMD_NEON_CVT_H +#define _NPY_SIMD_NEON_CVT_H + +// convert boolean vectors to integer vectors +#define npyv_cvt_u8_b8(A) A +#define npyv_cvt_s8_b8 vreinterpretq_s8_u8 +#define npyv_cvt_u16_b16(A) A +#define npyv_cvt_s16_b16 vreinterpretq_s16_u16 +#define npyv_cvt_u32_b32(A) A +#define npyv_cvt_s32_b32 vreinterpretq_s32_u32 +#define npyv_cvt_u64_b64(A) A +#define npyv_cvt_s64_b64 vreinterpretq_s64_u64 +#define npyv_cvt_f32_b32 vreinterpretq_f32_u32 +#define npyv_cvt_f64_b64 vreinterpretq_f64_u64 + +// convert integer vectors to boolean vectors +#define npyv_cvt_b8_u8(BL) BL +#define npyv_cvt_b8_s8 vreinterpretq_u8_s8 +#define npyv_cvt_b16_u16(BL) BL +#define npyv_cvt_b16_s16 vreinterpretq_u16_s16 +#define npyv_cvt_b32_u32(BL) BL +#define npyv_cvt_b32_s32 vreinterpretq_u32_s32 +#define npyv_cvt_b64_u64(BL) BL +#define npyv_cvt_b64_s64 vreinterpretq_u64_s64 +#define npyv_cvt_b32_f32 vreinterpretq_u32_f32 +#define npyv_cvt_b64_f64 vreinterpretq_u64_f64 + +// convert boolean vector to integer bitfield +NPY_FINLINE npy_uint64 npyv_tobits_b8(npyv_b8 a) +{ + const npyv_u8 scale = npyv_set_u8(1, 2, 4, 8, 16, 32, 64, 128, 1, 2, 4, 8, 16, 32, 64, 128); + npyv_u8 seq_scale = vandq_u8(a, scale); +#if NPY_SIMD_F64 + npy_uint8 sumlo = vaddv_u8(vget_low_u8(seq_scale)); + npy_uint8 sumhi = vaddv_u8(vget_high_u8(seq_scale)); + return sumlo + ((int)sumhi << 8); +#else + npyv_u64 sumh = vpaddlq_u32(vpaddlq_u16(vpaddlq_u8(seq_scale))); + return vgetq_lane_u64(sumh, 0) + ((int)vgetq_lane_u64(sumh, 1) << 8); +#endif +} +NPY_FINLINE npy_uint64 npyv_tobits_b16(npyv_b16 a) +{ + const npyv_u16 scale = npyv_set_u16(1, 2, 4, 8, 16, 32, 64, 128); + npyv_u16 seq_scale = vandq_u16(a, scale); +#if NPY_SIMD_F64 + return vaddvq_u16(seq_scale); +#else + npyv_u64 sumh = vpaddlq_u32(vpaddlq_u16(seq_scale)); + return vgetq_lane_u64(sumh, 0) + vgetq_lane_u64(sumh, 1); +#endif +} +NPY_FINLINE npy_uint64 npyv_tobits_b32(npyv_b32 a) +{ + const npyv_u32 scale = npyv_set_u32(1, 2, 4, 8); + npyv_u32 seq_scale = vandq_u32(a, scale); +#if NPY_SIMD_F64 + return vaddvq_u32(seq_scale); +#else + npyv_u64 sumh = vpaddlq_u32(seq_scale); + return vgetq_lane_u64(sumh, 0) + vgetq_lane_u64(sumh, 1); +#endif +} +NPY_FINLINE npy_uint64 npyv_tobits_b64(npyv_b64 a) +{ + npyv_u64 bit = vshrq_n_u64(a, 63); + return vgetq_lane_u64(bit, 0) | ((int)vgetq_lane_u64(bit, 1) << 1); +} + +//expand +NPY_FINLINE npyv_u16x2 npyv_expand_u16_u8(npyv_u8 data) { + npyv_u16x2 r; + r.val[0] = vmovl_u8(vget_low_u8(data)); + r.val[1] = vmovl_u8(vget_high_u8(data)); + return r; +} + +NPY_FINLINE npyv_u32x2 npyv_expand_u32_u16(npyv_u16 data) { + npyv_u32x2 r; + r.val[0] = vmovl_u16(vget_low_u16(data)); + r.val[1] = vmovl_u16(vget_high_u16(data)); + return r; +} + +// round to nearest integer +#if NPY_SIMD_F64 + #define npyv_round_s32_f32 vcvtnq_s32_f32 + NPY_FINLINE npyv_s32 npyv_round_s32_f64(npyv_f64 a, npyv_f64 b) + { + npyv_s64 lo = vcvtnq_s64_f64(a), hi = vcvtnq_s64_f64(b); + return vcombine_s32(vmovn_s64(lo), vmovn_s64(hi)); + } +#else + NPY_FINLINE npyv_s32 npyv_round_s32_f32(npyv_f32 a) + { + // halves will be rounded up. it's very costly + // to obey IEEE standard on arm7. tests should pass +-1 difference + const npyv_u32 sign = vdupq_n_u32(0x80000000); + const npyv_f32 half = vdupq_n_f32(0.5f); + npyv_f32 sign_half = vbslq_f32(sign, a, half); + return vcvtq_s32_f32(vaddq_f32(a, sign_half)); + } +#endif + +#endif // _NPY_SIMD_NEON_CVT_H diff --git a/numpy/core/src/common/simd/neon/math.h b/numpy/core/src/common/simd/neon/math.h new file mode 100644 index 000000000000..19e5cd846f7d --- /dev/null +++ b/numpy/core/src/common/simd/neon/math.h @@ -0,0 +1,226 @@ +#ifndef NPY_SIMD + #error "Not a standalone header" +#endif + +#ifndef _NPY_SIMD_NEON_MATH_H +#define _NPY_SIMD_NEON_MATH_H + +/*************************** + * Elementary + ***************************/ +// Absolute +#define npyv_abs_f32 vabsq_f32 +#define npyv_abs_f64 vabsq_f64 + +// Square +NPY_FINLINE npyv_f32 npyv_square_f32(npyv_f32 a) +{ return vmulq_f32(a, a); } +#if NPY_SIMD_F64 + NPY_FINLINE npyv_f64 npyv_square_f64(npyv_f64 a) + { return vmulq_f64(a, a); } +#endif + +// Square root +#if NPY_SIMD_F64 + #define npyv_sqrt_f32 vsqrtq_f32 + #define npyv_sqrt_f64 vsqrtq_f64 +#else + // Based on ARM doc, see https://developer.arm.com/documentation/dui0204/j/CIHDIACI + NPY_FINLINE npyv_f32 npyv_sqrt_f32(npyv_f32 a) + { + const npyv_f32 zero = vdupq_n_f32(0.0f); + const npyv_u32 pinf = vdupq_n_u32(0x7f800000); + npyv_u32 is_zero = vceqq_f32(a, zero), is_inf = vceqq_u32(vreinterpretq_u32_f32(a), pinf); + // guard against floating-point division-by-zero error + npyv_f32 guard_byz = vbslq_f32(is_zero, vreinterpretq_f32_u32(pinf), a); + // estimate to (1/√a) + npyv_f32 rsqrte = vrsqrteq_f32(guard_byz); + /** + * Newton-Raphson iteration: + * x[n+1] = x[n] * (3-d * (x[n]*x[n]) )/2) + * converges to (1/√d)if x0 is the result of VRSQRTE applied to d. + * + * NOTE: at least 3 iterations is needed to improve precision + */ + rsqrte = vmulq_f32(vrsqrtsq_f32(vmulq_f32(a, rsqrte), rsqrte), rsqrte); + rsqrte = vmulq_f32(vrsqrtsq_f32(vmulq_f32(a, rsqrte), rsqrte), rsqrte); + rsqrte = vmulq_f32(vrsqrtsq_f32(vmulq_f32(a, rsqrte), rsqrte), rsqrte); + // a * (1/√a) + npyv_f32 sqrt = vmulq_f32(a, rsqrte); + // return zero if the a is zero + // - return zero if a is zero. + // - return positive infinity if a is positive infinity + return vbslq_f32(vorrq_u32(is_zero, is_inf), a, sqrt); + } +#endif // NPY_SIMD_F64 + +// Reciprocal +NPY_FINLINE npyv_f32 npyv_recip_f32(npyv_f32 a) +{ +#if NPY_SIMD_F64 + const npyv_f32 one = vdupq_n_f32(1.0f); + return npyv_div_f32(one, a); +#else + npyv_f32 recipe = vrecpeq_f32(a); + /** + * Newton-Raphson iteration: + * x[n+1] = x[n] * (2-d * x[n]) + * converges to (1/d) if x0 is the result of VRECPE applied to d. + * + * NOTE: at least 3 iterations is needed to improve precision + */ + recipe = vmulq_f32(vrecpsq_f32(a, recipe), recipe); + recipe = vmulq_f32(vrecpsq_f32(a, recipe), recipe); + recipe = vmulq_f32(vrecpsq_f32(a, recipe), recipe); + return recipe; +#endif +} +#if NPY_SIMD_F64 + NPY_FINLINE npyv_f64 npyv_recip_f64(npyv_f64 a) + { + const npyv_f64 one = vdupq_n_f64(1.0); + return npyv_div_f64(one, a); + } +#endif // NPY_SIMD_F64 + +// Maximum, natively mapping with no guarantees to handle NaN. +#define npyv_max_f32 vmaxq_f32 +#define npyv_max_f64 vmaxq_f64 +// Maximum, supports IEEE floating-point arithmetic (IEC 60559), +// - If one of the two vectors contains NaN, the equivalent element of the other vector is set +// - Only if both corresponded elements are NaN, NaN is set. +#ifdef NPY_HAVE_ASIMD + #define npyv_maxp_f32 vmaxnmq_f32 +#else + NPY_FINLINE npyv_f32 npyv_maxp_f32(npyv_f32 a, npyv_f32 b) + { + npyv_u32 nn_a = vceqq_f32(a, a); + npyv_u32 nn_b = vceqq_f32(b, b); + return vmaxq_f32(vbslq_f32(nn_a, a, b), vbslq_f32(nn_b, b, a)); + } +#endif +#if NPY_SIMD_F64 + #define npyv_maxp_f64 vmaxnmq_f64 +#endif // NPY_SIMD_F64 +// Maximum, integer operations +#define npyv_max_u8 vmaxq_u8 +#define npyv_max_s8 vmaxq_s8 +#define npyv_max_u16 vmaxq_u16 +#define npyv_max_s16 vmaxq_s16 +#define npyv_max_u32 vmaxq_u32 +#define npyv_max_s32 vmaxq_s32 +NPY_FINLINE npyv_u64 npyv_max_u64(npyv_u64 a, npyv_u64 b) +{ + return vbslq_u64(npyv_cmpgt_u64(a, b), a, b); +} +NPY_FINLINE npyv_s64 npyv_max_s64(npyv_s64 a, npyv_s64 b) +{ + return vbslq_s64(npyv_cmpgt_s64(a, b), a, b); +} + +// Minimum, natively mapping with no guarantees to handle NaN. +#define npyv_min_f32 vminq_f32 +#define npyv_min_f64 vminq_f64 +// Minimum, supports IEEE floating-point arithmetic (IEC 60559), +// - If one of the two vectors contains NaN, the equivalent element of the other vector is set +// - Only if both corresponded elements are NaN, NaN is set. +#ifdef NPY_HAVE_ASIMD + #define npyv_minp_f32 vminnmq_f32 +#else + NPY_FINLINE npyv_f32 npyv_minp_f32(npyv_f32 a, npyv_f32 b) + { + npyv_u32 nn_a = vceqq_f32(a, a); + npyv_u32 nn_b = vceqq_f32(b, b); + return vminq_f32(vbslq_f32(nn_a, a, b), vbslq_f32(nn_b, b, a)); + } +#endif +#if NPY_SIMD_F64 + #define npyv_minp_f64 vminnmq_f64 +#endif // NPY_SIMD_F64 +// Minimum, integer operations +#define npyv_min_u8 vminq_u8 +#define npyv_min_s8 vminq_s8 +#define npyv_min_u16 vminq_u16 +#define npyv_min_s16 vminq_s16 +#define npyv_min_u32 vminq_u32 +#define npyv_min_s32 vminq_s32 +NPY_FINLINE npyv_u64 npyv_min_u64(npyv_u64 a, npyv_u64 b) +{ + return vbslq_u64(npyv_cmplt_u64(a, b), a, b); +} +NPY_FINLINE npyv_s64 npyv_min_s64(npyv_s64 a, npyv_s64 b) +{ + return vbslq_s64(npyv_cmplt_s64(a, b), a, b); +} + +// ceil +#ifdef NPY_HAVE_ASIMD + #define npyv_ceil_f32 vrndpq_f32 +#else + NPY_FINLINE npyv_f32 npyv_ceil_f32(npyv_f32 a) + { + const npyv_s32 szero = vreinterpretq_s32_f32(vdupq_n_f32(-0.0f)); + const npyv_u32 one = vreinterpretq_u32_f32(vdupq_n_f32(1.0f)); + const npyv_s32 max_int = vdupq_n_s32(0x7fffffff); + /** + * On armv7, vcvtq.f32 handles special cases as follows: + * NaN return 0 + * +inf or +outrange return 0x80000000(-0.0f) + * -inf or -outrange return 0x7fffffff(nan) + */ + npyv_s32 roundi = vcvtq_s32_f32(a); + npyv_f32 round = vcvtq_f32_s32(roundi); + npyv_f32 ceil = vaddq_f32(round, vreinterpretq_f32_u32( + vandq_u32(vcltq_f32(round, a), one)) + ); + // respect signed zero, e.g. -0.5 -> -0.0 + npyv_f32 rzero = vreinterpretq_f32_s32(vorrq_s32( + vreinterpretq_s32_f32(ceil), + vandq_s32(vreinterpretq_s32_f32(a), szero) + )); + // if nan or overflow return a + npyv_u32 nnan = npyv_notnan_f32(a); + npyv_u32 overflow = vorrq_u32( + vceqq_s32(roundi, szero), vceqq_s32(roundi, max_int) + ); + return vbslq_f32(vbicq_u32(nnan, overflow), rzero, a); + } +#endif +#if NPY_SIMD_F64 + #define npyv_ceil_f64 vrndpq_f64 +#endif // NPY_SIMD_F64 + +// trunc +#ifdef NPY_HAVE_ASIMD + #define npyv_trunc_f32 vrndq_f32 +#else + NPY_FINLINE npyv_f32 npyv_trunc_f32(npyv_f32 a) + { + const npyv_s32 szero = vreinterpretq_s32_f32(vdupq_n_f32(-0.0f)); + const npyv_s32 max_int = vdupq_n_s32(0x7fffffff); + /** + * On armv7, vcvtq.f32 handles special cases as follows: + * NaN return 0 + * +inf or +outrange return 0x80000000(-0.0f) + * -inf or -outrange return 0x7fffffff(nan) + */ + npyv_s32 roundi = vcvtq_s32_f32(a); + npyv_f32 round = vcvtq_f32_s32(roundi); + // respect signed zero, e.g. -0.5 -> -0.0 + npyv_f32 rzero = vreinterpretq_f32_s32(vorrq_s32( + vreinterpretq_s32_f32(round), + vandq_s32(vreinterpretq_s32_f32(a), szero) + )); + // if nan or overflow return a + npyv_u32 nnan = npyv_notnan_f32(a); + npyv_u32 overflow = vorrq_u32( + vceqq_s32(roundi, szero), vceqq_s32(roundi, max_int) + ); + return vbslq_f32(vbicq_u32(nnan, overflow), rzero, a); + } +#endif +#if NPY_SIMD_F64 + #define npyv_trunc_f64 vrndq_f64 +#endif // NPY_SIMD_F64 + +#endif // _NPY_SIMD_NEON_MATH_H diff --git a/numpy/core/src/common/simd/neon/memory.h b/numpy/core/src/common/simd/neon/memory.h new file mode 100644 index 000000000000..1e258f1bcbef --- /dev/null +++ b/numpy/core/src/common/simd/neon/memory.h @@ -0,0 +1,336 @@ +#ifndef NPY_SIMD + #error "Not a standalone header" +#endif + +#ifndef _NPY_SIMD_NEON_MEMORY_H +#define _NPY_SIMD_NEON_MEMORY_H + +#include "misc.h" + +/*************************** + * load/store + ***************************/ +// GCC requires literal type definitions for pointers types otherwise it causes ambiguous errors +#define NPYV_IMPL_NEON_MEM(SFX, CTYPE) \ + NPY_FINLINE npyv_##SFX npyv_load_##SFX(const npyv_lanetype_##SFX *ptr) \ + { return vld1q_##SFX((const CTYPE*)ptr); } \ + NPY_FINLINE npyv_##SFX npyv_loada_##SFX(const npyv_lanetype_##SFX *ptr) \ + { return vld1q_##SFX((const CTYPE*)ptr); } \ + NPY_FINLINE npyv_##SFX npyv_loads_##SFX(const npyv_lanetype_##SFX *ptr) \ + { return vld1q_##SFX((const CTYPE*)ptr); } \ + NPY_FINLINE npyv_##SFX npyv_loadl_##SFX(const npyv_lanetype_##SFX *ptr) \ + { \ + return vcombine_##SFX( \ + vld1_##SFX((const CTYPE*)ptr), vdup_n_##SFX(0) \ + ); \ + } \ + NPY_FINLINE void npyv_store_##SFX(npyv_lanetype_##SFX *ptr, npyv_##SFX vec) \ + { vst1q_##SFX((CTYPE*)ptr, vec); } \ + NPY_FINLINE void npyv_storea_##SFX(npyv_lanetype_##SFX *ptr, npyv_##SFX vec) \ + { vst1q_##SFX((CTYPE*)ptr, vec); } \ + NPY_FINLINE void npyv_stores_##SFX(npyv_lanetype_##SFX *ptr, npyv_##SFX vec) \ + { vst1q_##SFX((CTYPE*)ptr, vec); } \ + NPY_FINLINE void npyv_storel_##SFX(npyv_lanetype_##SFX *ptr, npyv_##SFX vec) \ + { vst1_##SFX((CTYPE*)ptr, vget_low_##SFX(vec)); } \ + NPY_FINLINE void npyv_storeh_##SFX(npyv_lanetype_##SFX *ptr, npyv_##SFX vec) \ + { vst1_##SFX((CTYPE*)ptr, vget_high_##SFX(vec)); } + +NPYV_IMPL_NEON_MEM(u8, uint8_t) +NPYV_IMPL_NEON_MEM(s8, int8_t) +NPYV_IMPL_NEON_MEM(u16, uint16_t) +NPYV_IMPL_NEON_MEM(s16, int16_t) +NPYV_IMPL_NEON_MEM(u32, uint32_t) +NPYV_IMPL_NEON_MEM(s32, int32_t) +NPYV_IMPL_NEON_MEM(u64, uint64_t) +NPYV_IMPL_NEON_MEM(s64, int64_t) +NPYV_IMPL_NEON_MEM(f32, float) +#if NPY_SIMD_F64 +NPYV_IMPL_NEON_MEM(f64, double) +#endif +/*************************** + * Non-contiguous Load + ***************************/ +NPY_FINLINE npyv_s32 npyv_loadn_s32(const npy_int32 *ptr, npy_intp stride) +{ + switch (stride) { + case 2: + return vld2q_s32((const int32_t*)ptr).val[0]; + case 3: + return vld3q_s32((const int32_t*)ptr).val[0]; + case 4: + return vld4q_s32((const int32_t*)ptr).val[0]; + default:; + int32x2_t ax = vcreate_s32(*ptr); + int32x4_t a = vcombine_s32(ax, ax); + a = vld1q_lane_s32((const int32_t*)ptr + stride, a, 1); + a = vld1q_lane_s32((const int32_t*)ptr + stride*2, a, 2); + a = vld1q_lane_s32((const int32_t*)ptr + stride*3, a, 3); + return a; + } +} + +NPY_FINLINE npyv_u32 npyv_loadn_u32(const npy_uint32 *ptr, npy_intp stride) +{ + return npyv_reinterpret_u32_s32( + npyv_loadn_s32((const npy_int32*)ptr, stride) + ); +} +NPY_FINLINE npyv_f32 npyv_loadn_f32(const float *ptr, npy_intp stride) +{ + return npyv_reinterpret_f32_s32( + npyv_loadn_s32((const npy_int32*)ptr, stride) + ); +} +//// 64 +NPY_FINLINE npyv_s64 npyv_loadn_s64(const npy_int64 *ptr, npy_intp stride) +{ + return vcombine_s64( + vld1_s64((const int64_t*)ptr), vld1_s64((const int64_t*)ptr + stride) + ); +} +NPY_FINLINE npyv_u64 npyv_loadn_u64(const npy_uint64 *ptr, npy_intp stride) +{ + return npyv_reinterpret_u64_s64( + npyv_loadn_s64((const npy_int64*)ptr, stride) + ); +} +#if NPY_SIMD_F64 +NPY_FINLINE npyv_f64 npyv_loadn_f64(const double *ptr, npy_intp stride) +{ + return npyv_reinterpret_f64_s64( + npyv_loadn_s64((const npy_int64*)ptr, stride) + ); +} +#endif +/*************************** + * Non-contiguous Store + ***************************/ +//// 32 +NPY_FINLINE void npyv_storen_s32(npy_int32 *ptr, npy_intp stride, npyv_s32 a) +{ + vst1q_lane_s32((int32_t*)ptr, a, 0); + vst1q_lane_s32((int32_t*)ptr + stride, a, 1); + vst1q_lane_s32((int32_t*)ptr + stride*2, a, 2); + vst1q_lane_s32((int32_t*)ptr + stride*3, a, 3); +} +NPY_FINLINE void npyv_storen_u32(npy_uint32 *ptr, npy_intp stride, npyv_u32 a) +{ npyv_storen_s32((npy_int32*)ptr, stride, npyv_reinterpret_s32_u32(a)); } +NPY_FINLINE void npyv_storen_f32(float *ptr, npy_intp stride, npyv_f32 a) +{ npyv_storen_s32((npy_int32*)ptr, stride, npyv_reinterpret_s32_f32(a)); } +//// 64 +NPY_FINLINE void npyv_storen_s64(npy_int64 *ptr, npy_intp stride, npyv_s64 a) +{ + vst1q_lane_s64((int64_t*)ptr, a, 0); + vst1q_lane_s64((int64_t*)ptr + stride, a, 1); +} +NPY_FINLINE void npyv_storen_u64(npy_uint64 *ptr, npy_intp stride, npyv_u64 a) +{ npyv_storen_s64((npy_int64*)ptr, stride, npyv_reinterpret_s64_u64(a)); } + +#if NPY_SIMD_F64 +NPY_FINLINE void npyv_storen_f64(double *ptr, npy_intp stride, npyv_f64 a) +{ npyv_storen_s64((npy_int64*)ptr, stride, npyv_reinterpret_s64_f64(a)); } +#endif + +/********************************* + * Partial Load + *********************************/ +//// 32 +NPY_FINLINE npyv_s32 npyv_load_till_s32(const npy_int32 *ptr, npy_uintp nlane, npy_int32 fill) +{ + assert(nlane > 0); + switch(nlane) { + case 1: + return vld1q_lane_s32((const int32_t*)ptr, vdupq_n_s32(fill), 0); + case 2: + return vcombine_s32(vld1_s32((const int32_t*)ptr), vdup_n_s32(fill)); + case 3: + return vcombine_s32( + vld1_s32((const int32_t*)ptr), + vld1_lane_s32((const int32_t*)ptr + 2, vdup_n_s32(fill), 0) + ); + default: + return npyv_load_s32(ptr); + } +} +// fill zero to rest lanes +NPY_FINLINE npyv_s32 npyv_load_tillz_s32(const npy_int32 *ptr, npy_uintp nlane) +{ return npyv_load_till_s32(ptr, nlane, 0); } +//// 64 +NPY_FINLINE npyv_s64 npyv_load_till_s64(const npy_int64 *ptr, npy_uintp nlane, npy_int64 fill) +{ + assert(nlane > 0); + if (nlane == 1) { + return vcombine_s64(vld1_s64((const int64_t*)ptr), vdup_n_s64(fill)); + } + return npyv_load_s64(ptr); +} +// fill zero to rest lanes +NPY_FINLINE npyv_s64 npyv_load_tillz_s64(const npy_int64 *ptr, npy_uintp nlane) +{ return npyv_load_till_s64(ptr, nlane, 0); } + +/********************************* + * Non-contiguous partial load + *********************************/ +//// 32 +NPY_FINLINE npyv_s32 +npyv_loadn_till_s32(const npy_int32 *ptr, npy_intp stride, npy_uintp nlane, npy_int32 fill) +{ + assert(nlane > 0); + int32x4_t vfill = vdupq_n_s32(fill); + switch(nlane) { + case 3: + vfill = vld1q_lane_s32((const int32_t*)ptr + stride*2, vfill, 2); + case 2: + vfill = vld1q_lane_s32((const int32_t*)ptr + stride, vfill, 1); + case 1: + vfill = vld1q_lane_s32((const int32_t*)ptr, vfill, 0); + return vfill; + default: + return npyv_loadn_s32(ptr, stride); + } +} +NPY_FINLINE npyv_s32 +npyv_loadn_tillz_s32(const npy_int32 *ptr, npy_intp stride, npy_uintp nlane) +{ return npyv_loadn_till_s32(ptr, stride, nlane, 0); } + +NPY_FINLINE npyv_s64 +npyv_loadn_till_s64(const npy_int64 *ptr, npy_intp stride, npy_uintp nlane, npy_int64 fill) +{ + assert(nlane > 0); + if (nlane == 1) { + return vcombine_s64(vld1_s64((const int64_t*)ptr), vdup_n_s64(fill)); + } + return npyv_loadn_s64(ptr, stride); +} +// fill zero to rest lanes +NPY_FINLINE npyv_s64 npyv_loadn_tillz_s64(const npy_int64 *ptr, npy_intp stride, npy_uintp nlane) +{ return npyv_loadn_till_s64(ptr, stride, nlane, 0); } + +/********************************* + * Partial store + *********************************/ +//// 32 +NPY_FINLINE void npyv_store_till_s32(npy_int32 *ptr, npy_uintp nlane, npyv_s32 a) +{ + assert(nlane > 0); + switch(nlane) { + case 1: + vst1q_lane_s32((int32_t*)ptr, a, 0); + break; + case 2: + vst1_s32((int32_t*)ptr, vget_low_s32(a)); + break; + case 3: + vst1_s32((int32_t*)ptr, vget_low_s32(a)); + vst1q_lane_s32((int32_t*)ptr + 2, a, 2); + break; + default: + npyv_store_s32(ptr, a); + } +} +//// 64 +NPY_FINLINE void npyv_store_till_s64(npy_int64 *ptr, npy_uintp nlane, npyv_s64 a) +{ + assert(nlane > 0); + if (nlane == 1) { + vst1q_lane_s64((int64_t*)ptr, a, 0); + return; + } + npyv_store_s64(ptr, a); +} +/********************************* + * Non-contiguous partial store + *********************************/ +//// 32 +NPY_FINLINE void npyv_storen_till_s32(npy_int32 *ptr, npy_intp stride, npy_uintp nlane, npyv_s32 a) +{ + assert(nlane > 0); + switch(nlane) { + default: + vst1q_lane_s32((int32_t*)ptr + stride*3, a, 3); + case 3: + vst1q_lane_s32((int32_t*)ptr + stride*2, a, 2); + case 2: + vst1q_lane_s32((int32_t*)ptr + stride, a, 1); + case 1: + vst1q_lane_s32((int32_t*)ptr, a, 0); + break; + } +} +//// 64 +NPY_FINLINE void npyv_storen_till_s64(npy_int64 *ptr, npy_intp stride, npy_uintp nlane, npyv_s64 a) +{ + assert(nlane > 0); + if (nlane == 1) { + vst1q_lane_s64((int64_t*)ptr, a, 0); + return; + } + npyv_storen_s64(ptr, stride, a); +} + +/***************************************************************** + * Implement partial load/store for u32/f32/u64/f64... via casting + *****************************************************************/ +#define NPYV_IMPL_NEON_REST_PARTIAL_TYPES(F_SFX, T_SFX) \ + NPY_FINLINE npyv_##F_SFX npyv_load_till_##F_SFX \ + (const npyv_lanetype_##F_SFX *ptr, npy_uintp nlane, npyv_lanetype_##F_SFX fill) \ + { \ + union { \ + npyv_lanetype_##F_SFX from_##F_SFX; \ + npyv_lanetype_##T_SFX to_##T_SFX; \ + } pun = {.from_##F_SFX = fill}; \ + return npyv_reinterpret_##F_SFX##_##T_SFX(npyv_load_till_##T_SFX( \ + (const npyv_lanetype_##T_SFX *)ptr, nlane, pun.to_##T_SFX \ + )); \ + } \ + NPY_FINLINE npyv_##F_SFX npyv_loadn_till_##F_SFX \ + (const npyv_lanetype_##F_SFX *ptr, npy_intp stride, npy_uintp nlane, \ + npyv_lanetype_##F_SFX fill) \ + { \ + union { \ + npyv_lanetype_##F_SFX from_##F_SFX; \ + npyv_lanetype_##T_SFX to_##T_SFX; \ + } pun = {.from_##F_SFX = fill}; \ + return npyv_reinterpret_##F_SFX##_##T_SFX(npyv_loadn_till_##T_SFX( \ + (const npyv_lanetype_##T_SFX *)ptr, stride, nlane, pun.to_##T_SFX \ + )); \ + } \ + NPY_FINLINE npyv_##F_SFX npyv_load_tillz_##F_SFX \ + (const npyv_lanetype_##F_SFX *ptr, npy_uintp nlane) \ + { \ + return npyv_reinterpret_##F_SFX##_##T_SFX(npyv_load_tillz_##T_SFX( \ + (const npyv_lanetype_##T_SFX *)ptr, nlane \ + )); \ + } \ + NPY_FINLINE npyv_##F_SFX npyv_loadn_tillz_##F_SFX \ + (const npyv_lanetype_##F_SFX *ptr, npy_intp stride, npy_uintp nlane) \ + { \ + return npyv_reinterpret_##F_SFX##_##T_SFX(npyv_loadn_tillz_##T_SFX( \ + (const npyv_lanetype_##T_SFX *)ptr, stride, nlane \ + )); \ + } \ + NPY_FINLINE void npyv_store_till_##F_SFX \ + (npyv_lanetype_##F_SFX *ptr, npy_uintp nlane, npyv_##F_SFX a) \ + { \ + npyv_store_till_##T_SFX( \ + (npyv_lanetype_##T_SFX *)ptr, nlane, \ + npyv_reinterpret_##T_SFX##_##F_SFX(a) \ + ); \ + } \ + NPY_FINLINE void npyv_storen_till_##F_SFX \ + (npyv_lanetype_##F_SFX *ptr, npy_intp stride, npy_uintp nlane, npyv_##F_SFX a) \ + { \ + npyv_storen_till_##T_SFX( \ + (npyv_lanetype_##T_SFX *)ptr, stride, nlane, \ + npyv_reinterpret_##T_SFX##_##F_SFX(a) \ + ); \ + } + +NPYV_IMPL_NEON_REST_PARTIAL_TYPES(u32, s32) +NPYV_IMPL_NEON_REST_PARTIAL_TYPES(f32, s32) +NPYV_IMPL_NEON_REST_PARTIAL_TYPES(u64, s64) +#if NPY_SIMD_F64 +NPYV_IMPL_NEON_REST_PARTIAL_TYPES(f64, s64) +#endif + +#endif // _NPY_SIMD_NEON_MEMORY_H diff --git a/numpy/core/src/common/simd/neon/misc.h b/numpy/core/src/common/simd/neon/misc.h new file mode 100644 index 000000000000..51b0c38584a3 --- /dev/null +++ b/numpy/core/src/common/simd/neon/misc.h @@ -0,0 +1,255 @@ +#ifndef NPY_SIMD + #error "Not a standalone header" +#endif + +#ifndef _NPY_SIMD_NEON_MISC_H +#define _NPY_SIMD_NEON_MISC_H + +// vector with zero lanes +#define npyv_zero_u8() vreinterpretq_u8_s32(npyv_zero_s32()) +#define npyv_zero_s8() vreinterpretq_s8_s32(npyv_zero_s32()) +#define npyv_zero_u16() vreinterpretq_u16_s32(npyv_zero_s32()) +#define npyv_zero_s16() vreinterpretq_s16_s32(npyv_zero_s32()) +#define npyv_zero_u32() vdupq_n_u32((unsigned)0) +#define npyv_zero_s32() vdupq_n_s32((int)0) +#define npyv_zero_u64() vreinterpretq_u64_s32(npyv_zero_s32()) +#define npyv_zero_s64() vreinterpretq_s64_s32(npyv_zero_s32()) +#define npyv_zero_f32() vdupq_n_f32(0.0f) +#define npyv_zero_f64() vdupq_n_f64(0.0) + +// vector with a specific value set to all lanes +#define npyv_setall_u8 vdupq_n_u8 +#define npyv_setall_s8 vdupq_n_s8 +#define npyv_setall_u16 vdupq_n_u16 +#define npyv_setall_s16 vdupq_n_s16 +#define npyv_setall_u32 vdupq_n_u32 +#define npyv_setall_s32 vdupq_n_s32 +#define npyv_setall_u64 vdupq_n_u64 +#define npyv_setall_s64 vdupq_n_s64 +#define npyv_setall_f32 vdupq_n_f32 +#define npyv_setall_f64 vdupq_n_f64 + +// vector with specific values set to each lane and +// set a specific value to all remained lanes +NPY_FINLINE uint8x16_t npyv__set_u8(npy_uint8 i0, npy_uint8 i1, npy_uint8 i2, npy_uint8 i3, + npy_uint8 i4, npy_uint8 i5, npy_uint8 i6, npy_uint8 i7, npy_uint8 i8, npy_uint8 i9, + npy_uint8 i10, npy_uint8 i11, npy_uint8 i12, npy_uint8 i13, npy_uint8 i14, npy_uint8 i15) +{ + const uint8_t NPY_DECL_ALIGNED(16) data[16] = { + i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15 + }; + return vld1q_u8(data); +} +#define npyv_setf_u8(FILL, ...) npyv__set_u8(NPYV__SET_FILL_16(npy_uint8, FILL, __VA_ARGS__)) + +NPY_FINLINE int8x16_t npyv__set_s8(npy_int8 i0, npy_int8 i1, npy_int8 i2, npy_int8 i3, + npy_int8 i4, npy_int8 i5, npy_int8 i6, npy_int8 i7, npy_int8 i8, npy_int8 i9, + npy_int8 i10, npy_int8 i11, npy_int8 i12, npy_int8 i13, npy_int8 i14, npy_int8 i15) +{ + const int8_t NPY_DECL_ALIGNED(16) data[16] = { + i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15 + }; + return vld1q_s8(data); +} +#define npyv_setf_s8(FILL, ...) npyv__set_s8(NPYV__SET_FILL_16(npy_int8, FILL, __VA_ARGS__)) + +NPY_FINLINE uint16x8_t npyv__set_u16(npy_uint16 i0, npy_uint16 i1, npy_uint16 i2, npy_uint16 i3, + npy_uint16 i4, npy_uint16 i5, npy_uint16 i6, npy_uint16 i7) +{ + const uint16_t NPY_DECL_ALIGNED(16) data[8] = {i0, i1, i2, i3, i4, i5, i6, i7}; + return vld1q_u16(data); +} +#define npyv_setf_u16(FILL, ...) npyv__set_u16(NPYV__SET_FILL_8(npy_uint16, FILL, __VA_ARGS__)) + +NPY_FINLINE int16x8_t npyv__set_s16(npy_int16 i0, npy_int16 i1, npy_int16 i2, npy_int16 i3, + npy_int16 i4, npy_int16 i5, npy_int16 i6, npy_int16 i7) +{ + const int16_t NPY_DECL_ALIGNED(16) data[8] = {i0, i1, i2, i3, i4, i5, i6, i7}; + return vld1q_s16(data); +} +#define npyv_setf_s16(FILL, ...) npyv__set_s16(NPYV__SET_FILL_8(npy_int16, FILL, __VA_ARGS__)) + +NPY_FINLINE uint32x4_t npyv__set_u32(npy_uint32 i0, npy_uint32 i1, npy_uint32 i2, npy_uint32 i3) +{ + const uint32_t NPY_DECL_ALIGNED(16) data[4] = {i0, i1, i2, i3}; + return vld1q_u32(data); +} +#define npyv_setf_u32(FILL, ...) npyv__set_u32(NPYV__SET_FILL_4(npy_uint32, FILL, __VA_ARGS__)) + +NPY_FINLINE int32x4_t npyv__set_s32(npy_int32 i0, npy_int32 i1, npy_int32 i2, npy_int32 i3) +{ + const int32_t NPY_DECL_ALIGNED(16) data[4] = {i0, i1, i2, i3}; + return vld1q_s32(data); +} +#define npyv_setf_s32(FILL, ...) npyv__set_s32(NPYV__SET_FILL_4(npy_int32, FILL, __VA_ARGS__)) + +NPY_FINLINE uint64x2_t npyv__set_u64(npy_uint64 i0, npy_uint64 i1) +{ + const uint64_t NPY_DECL_ALIGNED(16) data[2] = {i0, i1}; + return vld1q_u64(data); +} +#define npyv_setf_u64(FILL, ...) npyv__set_u64(NPYV__SET_FILL_2(npy_int64, FILL, __VA_ARGS__)) + +NPY_FINLINE int64x2_t npyv__set_s64(npy_int64 i0, npy_int64 i1) +{ + const int64_t NPY_DECL_ALIGNED(16) data[2] = {i0, i1}; + return vld1q_s64(data); +} +#define npyv_setf_s64(FILL, ...) npyv__set_s64(NPYV__SET_FILL_2(npy_int64, FILL, __VA_ARGS__)) + +NPY_FINLINE float32x4_t npyv__set_f32(float i0, float i1, float i2, float i3) +{ + const float NPY_DECL_ALIGNED(16) data[4] = {i0, i1, i2, i3}; + return vld1q_f32(data); +} +#define npyv_setf_f32(FILL, ...) npyv__set_f32(NPYV__SET_FILL_4(float, FILL, __VA_ARGS__)) + +#ifdef __aarch64__ +NPY_FINLINE float64x2_t npyv__set_f64(double i0, double i1) +{ + const double NPY_DECL_ALIGNED(16) data[2] = {i0, i1}; + return vld1q_f64(data); +} +#define npyv_setf_f64(FILL, ...) npyv__set_f64(NPYV__SET_FILL_2(double, FILL, __VA_ARGS__)) +#endif + +// vector with specific values set to each lane and +// set zero to all remained lanes +#define npyv_set_u8(...) npyv_setf_u8(0, __VA_ARGS__) +#define npyv_set_s8(...) npyv_setf_s8(0, __VA_ARGS__) +#define npyv_set_u16(...) npyv_setf_u16(0, __VA_ARGS__) +#define npyv_set_s16(...) npyv_setf_s16(0, __VA_ARGS__) +#define npyv_set_u32(...) npyv_setf_u32(0, __VA_ARGS__) +#define npyv_set_s32(...) npyv_setf_s32(0, __VA_ARGS__) +#define npyv_set_u64(...) npyv_setf_u64(0, __VA_ARGS__) +#define npyv_set_s64(...) npyv_setf_s64(0, __VA_ARGS__) +#define npyv_set_f32(...) npyv_setf_f32(0, __VA_ARGS__) +#define npyv_set_f64(...) npyv_setf_f64(0, __VA_ARGS__) + +// Per lane select +#define npyv_select_u8 vbslq_u8 +#define npyv_select_s8 vbslq_s8 +#define npyv_select_u16 vbslq_u16 +#define npyv_select_s16 vbslq_s16 +#define npyv_select_u32 vbslq_u32 +#define npyv_select_s32 vbslq_s32 +#define npyv_select_u64 vbslq_u64 +#define npyv_select_s64 vbslq_s64 +#define npyv_select_f32 vbslq_f32 +#define npyv_select_f64 vbslq_f64 + +// Reinterpret +#define npyv_reinterpret_u8_u8(X) X +#define npyv_reinterpret_u8_s8 vreinterpretq_u8_s8 +#define npyv_reinterpret_u8_u16 vreinterpretq_u8_u16 +#define npyv_reinterpret_u8_s16 vreinterpretq_u8_s16 +#define npyv_reinterpret_u8_u32 vreinterpretq_u8_u32 +#define npyv_reinterpret_u8_s32 vreinterpretq_u8_s32 +#define npyv_reinterpret_u8_u64 vreinterpretq_u8_u64 +#define npyv_reinterpret_u8_s64 vreinterpretq_u8_s64 +#define npyv_reinterpret_u8_f32 vreinterpretq_u8_f32 +#define npyv_reinterpret_u8_f64 vreinterpretq_u8_f64 + +#define npyv_reinterpret_s8_s8(X) X +#define npyv_reinterpret_s8_u8 vreinterpretq_s8_u8 +#define npyv_reinterpret_s8_u16 vreinterpretq_s8_u16 +#define npyv_reinterpret_s8_s16 vreinterpretq_s8_s16 +#define npyv_reinterpret_s8_u32 vreinterpretq_s8_u32 +#define npyv_reinterpret_s8_s32 vreinterpretq_s8_s32 +#define npyv_reinterpret_s8_u64 vreinterpretq_s8_u64 +#define npyv_reinterpret_s8_s64 vreinterpretq_s8_s64 +#define npyv_reinterpret_s8_f32 vreinterpretq_s8_f32 +#define npyv_reinterpret_s8_f64 vreinterpretq_s8_f64 + +#define npyv_reinterpret_u16_u16(X) X +#define npyv_reinterpret_u16_u8 vreinterpretq_u16_u8 +#define npyv_reinterpret_u16_s8 vreinterpretq_u16_s8 +#define npyv_reinterpret_u16_s16 vreinterpretq_u16_s16 +#define npyv_reinterpret_u16_u32 vreinterpretq_u16_u32 +#define npyv_reinterpret_u16_s32 vreinterpretq_u16_s32 +#define npyv_reinterpret_u16_u64 vreinterpretq_u16_u64 +#define npyv_reinterpret_u16_s64 vreinterpretq_u16_s64 +#define npyv_reinterpret_u16_f32 vreinterpretq_u16_f32 +#define npyv_reinterpret_u16_f64 vreinterpretq_u16_f64 + +#define npyv_reinterpret_s16_s16(X) X +#define npyv_reinterpret_s16_u8 vreinterpretq_s16_u8 +#define npyv_reinterpret_s16_s8 vreinterpretq_s16_s8 +#define npyv_reinterpret_s16_u16 vreinterpretq_s16_u16 +#define npyv_reinterpret_s16_u32 vreinterpretq_s16_u32 +#define npyv_reinterpret_s16_s32 vreinterpretq_s16_s32 +#define npyv_reinterpret_s16_u64 vreinterpretq_s16_u64 +#define npyv_reinterpret_s16_s64 vreinterpretq_s16_s64 +#define npyv_reinterpret_s16_f32 vreinterpretq_s16_f32 +#define npyv_reinterpret_s16_f64 vreinterpretq_s16_f64 + +#define npyv_reinterpret_u32_u32(X) X +#define npyv_reinterpret_u32_u8 vreinterpretq_u32_u8 +#define npyv_reinterpret_u32_s8 vreinterpretq_u32_s8 +#define npyv_reinterpret_u32_u16 vreinterpretq_u32_u16 +#define npyv_reinterpret_u32_s16 vreinterpretq_u32_s16 +#define npyv_reinterpret_u32_s32 vreinterpretq_u32_s32 +#define npyv_reinterpret_u32_u64 vreinterpretq_u32_u64 +#define npyv_reinterpret_u32_s64 vreinterpretq_u32_s64 +#define npyv_reinterpret_u32_f32 vreinterpretq_u32_f32 +#define npyv_reinterpret_u32_f64 vreinterpretq_u32_f64 + +#define npyv_reinterpret_s32_s32(X) X +#define npyv_reinterpret_s32_u8 vreinterpretq_s32_u8 +#define npyv_reinterpret_s32_s8 vreinterpretq_s32_s8 +#define npyv_reinterpret_s32_u16 vreinterpretq_s32_u16 +#define npyv_reinterpret_s32_s16 vreinterpretq_s32_s16 +#define npyv_reinterpret_s32_u32 vreinterpretq_s32_u32 +#define npyv_reinterpret_s32_u64 vreinterpretq_s32_u64 +#define npyv_reinterpret_s32_s64 vreinterpretq_s32_s64 +#define npyv_reinterpret_s32_f32 vreinterpretq_s32_f32 +#define npyv_reinterpret_s32_f64 vreinterpretq_s32_f64 + +#define npyv_reinterpret_u64_u64(X) X +#define npyv_reinterpret_u64_u8 vreinterpretq_u64_u8 +#define npyv_reinterpret_u64_s8 vreinterpretq_u64_s8 +#define npyv_reinterpret_u64_u16 vreinterpretq_u64_u16 +#define npyv_reinterpret_u64_s16 vreinterpretq_u64_s16 +#define npyv_reinterpret_u64_u32 vreinterpretq_u64_u32 +#define npyv_reinterpret_u64_s32 vreinterpretq_u64_s32 +#define npyv_reinterpret_u64_s64 vreinterpretq_u64_s64 +#define npyv_reinterpret_u64_f32 vreinterpretq_u64_f32 +#define npyv_reinterpret_u64_f64 vreinterpretq_u64_f64 + +#define npyv_reinterpret_s64_s64(X) X +#define npyv_reinterpret_s64_u8 vreinterpretq_s64_u8 +#define npyv_reinterpret_s64_s8 vreinterpretq_s64_s8 +#define npyv_reinterpret_s64_u16 vreinterpretq_s64_u16 +#define npyv_reinterpret_s64_s16 vreinterpretq_s64_s16 +#define npyv_reinterpret_s64_u32 vreinterpretq_s64_u32 +#define npyv_reinterpret_s64_s32 vreinterpretq_s64_s32 +#define npyv_reinterpret_s64_u64 vreinterpretq_s64_u64 +#define npyv_reinterpret_s64_f32 vreinterpretq_s64_f32 +#define npyv_reinterpret_s64_f64 vreinterpretq_s64_f64 + +#define npyv_reinterpret_f32_f32(X) X +#define npyv_reinterpret_f32_u8 vreinterpretq_f32_u8 +#define npyv_reinterpret_f32_s8 vreinterpretq_f32_s8 +#define npyv_reinterpret_f32_u16 vreinterpretq_f32_u16 +#define npyv_reinterpret_f32_s16 vreinterpretq_f32_s16 +#define npyv_reinterpret_f32_u32 vreinterpretq_f32_u32 +#define npyv_reinterpret_f32_s32 vreinterpretq_f32_s32 +#define npyv_reinterpret_f32_u64 vreinterpretq_f32_u64 +#define npyv_reinterpret_f32_s64 vreinterpretq_f32_s64 +#define npyv_reinterpret_f32_f64 vreinterpretq_f32_f64 + +#define npyv_reinterpret_f64_f64(X) X +#define npyv_reinterpret_f64_u8 vreinterpretq_f64_u8 +#define npyv_reinterpret_f64_s8 vreinterpretq_f64_s8 +#define npyv_reinterpret_f64_u16 vreinterpretq_f64_u16 +#define npyv_reinterpret_f64_s16 vreinterpretq_f64_s16 +#define npyv_reinterpret_f64_u32 vreinterpretq_f64_u32 +#define npyv_reinterpret_f64_s32 vreinterpretq_f64_s32 +#define npyv_reinterpret_f64_u64 vreinterpretq_f64_u64 +#define npyv_reinterpret_f64_s64 vreinterpretq_f64_s64 +#define npyv_reinterpret_f64_f32 vreinterpretq_f64_f32 + +// Only required by AVX2/AVX512 +#define npyv_cleanup() ((void)0) + +#endif // _NPY_SIMD_NEON_MISC_H diff --git a/numpy/core/src/common/simd/neon/neon.h b/numpy/core/src/common/simd/neon/neon.h new file mode 100644 index 000000000000..e6f6a732478a --- /dev/null +++ b/numpy/core/src/common/simd/neon/neon.h @@ -0,0 +1,80 @@ +#ifndef _NPY_SIMD_H_ + #error "Not a standalone header" +#endif + +#define NPY_SIMD 128 +#define NPY_SIMD_WIDTH 16 + +#ifdef __aarch64__ + #define NPY_SIMD_F64 1 +#else + #define NPY_SIMD_F64 0 +#endif +#ifdef NPY_HAVE_NEON_VFPV4 + #define NPY_SIMD_FMA3 1 // native support +#else + #define NPY_SIMD_FMA3 0 // HW emulated +#endif + +typedef uint8x16_t npyv_u8; +typedef int8x16_t npyv_s8; +typedef uint16x8_t npyv_u16; +typedef int16x8_t npyv_s16; +typedef uint32x4_t npyv_u32; +typedef int32x4_t npyv_s32; +typedef uint64x2_t npyv_u64; +typedef int64x2_t npyv_s64; +typedef float32x4_t npyv_f32; +#if NPY_SIMD_F64 +typedef float64x2_t npyv_f64; +#endif + +typedef uint8x16_t npyv_b8; +typedef uint16x8_t npyv_b16; +typedef uint32x4_t npyv_b32; +typedef uint64x2_t npyv_b64; + +typedef uint8x16x2_t npyv_u8x2; +typedef int8x16x2_t npyv_s8x2; +typedef uint16x8x2_t npyv_u16x2; +typedef int16x8x2_t npyv_s16x2; +typedef uint32x4x2_t npyv_u32x2; +typedef int32x4x2_t npyv_s32x2; +typedef uint64x2x2_t npyv_u64x2; +typedef int64x2x2_t npyv_s64x2; +typedef float32x4x2_t npyv_f32x2; +#if NPY_SIMD_F64 +typedef float64x2x2_t npyv_f64x2; +#endif + +typedef uint8x16x3_t npyv_u8x3; +typedef int8x16x3_t npyv_s8x3; +typedef uint16x8x3_t npyv_u16x3; +typedef int16x8x3_t npyv_s16x3; +typedef uint32x4x3_t npyv_u32x3; +typedef int32x4x3_t npyv_s32x3; +typedef uint64x2x3_t npyv_u64x3; +typedef int64x2x3_t npyv_s64x3; +typedef float32x4x3_t npyv_f32x3; +#if NPY_SIMD_F64 +typedef float64x2x3_t npyv_f64x3; +#endif + +#define npyv_nlanes_u8 16 +#define npyv_nlanes_s8 16 +#define npyv_nlanes_u16 8 +#define npyv_nlanes_s16 8 +#define npyv_nlanes_u32 4 +#define npyv_nlanes_s32 4 +#define npyv_nlanes_u64 2 +#define npyv_nlanes_s64 2 +#define npyv_nlanes_f32 4 +#define npyv_nlanes_f64 2 + +#include "memory.h" +#include "misc.h" +#include "reorder.h" +#include "operators.h" +#include "conversion.h" +#include "arithmetic.h" +#include "math.h" diff --git a/numpy/core/src/common/simd/neon/operators.h b/numpy/core/src/common/simd/neon/operators.h new file mode 100644 index 000000000000..b43ba36537e9 --- /dev/null +++ b/numpy/core/src/common/simd/neon/operators.h @@ -0,0 +1,243 @@ +#ifndef NPY_SIMD + #error "Not a standalone header" +#endif + +#ifndef _NPY_SIMD_NEON_OPERATORS_H +#define _NPY_SIMD_NEON_OPERATORS_H + +/*************************** + * Shifting + ***************************/ + +// left +#define npyv_shl_u16(A, C) vshlq_u16(A, npyv_setall_s16(C)) +#define npyv_shl_s16(A, C) vshlq_s16(A, npyv_setall_s16(C)) +#define npyv_shl_u32(A, C) vshlq_u32(A, npyv_setall_s32(C)) +#define npyv_shl_s32(A, C) vshlq_s32(A, npyv_setall_s32(C)) +#define npyv_shl_u64(A, C) vshlq_u64(A, npyv_setall_s64(C)) +#define npyv_shl_s64(A, C) vshlq_s64(A, npyv_setall_s64(C)) + +// left by an immediate constant +#define npyv_shli_u16 vshlq_n_u16 +#define npyv_shli_s16 vshlq_n_s16 +#define npyv_shli_u32 vshlq_n_u32 +#define npyv_shli_s32 vshlq_n_s32 +#define npyv_shli_u64 vshlq_n_u64 +#define npyv_shli_s64 vshlq_n_s64 + +// right +#define npyv_shr_u16(A, C) vshlq_u16(A, npyv_setall_s16(-(C))) +#define npyv_shr_s16(A, C) vshlq_s16(A, npyv_setall_s16(-(C))) +#define npyv_shr_u32(A, C) vshlq_u32(A, npyv_setall_s32(-(C))) +#define npyv_shr_s32(A, C) vshlq_s32(A, npyv_setall_s32(-(C))) +#define npyv_shr_u64(A, C) vshlq_u64(A, npyv_setall_s64(-(C))) +#define npyv_shr_s64(A, C) vshlq_s64(A, npyv_setall_s64(-(C))) + +// right by an immediate constant +#define npyv_shri_u16 vshrq_n_u16 +#define npyv_shri_s16 vshrq_n_s16 +#define npyv_shri_u32 vshrq_n_u32 +#define npyv_shri_s32 vshrq_n_s32 +#define npyv_shri_u64 vshrq_n_u64 +#define npyv_shri_s64 vshrq_n_s64 + +/*************************** + * Logical + ***************************/ + +// AND +#define npyv_and_u8 vandq_u8 +#define npyv_and_s8 vandq_s8 +#define npyv_and_u16 vandq_u16 +#define npyv_and_s16 vandq_s16 +#define npyv_and_u32 vandq_u32 +#define npyv_and_s32 vandq_s32 +#define npyv_and_u64 vandq_u64 +#define npyv_and_s64 vandq_s64 +#define npyv_and_f32(A, B) \ + vreinterpretq_f32_u8(vandq_u8(vreinterpretq_u8_f32(A), vreinterpretq_u8_f32(B))) +#define npyv_and_f64(A, B) \ + vreinterpretq_f64_u8(vandq_u8(vreinterpretq_u8_f64(A), vreinterpretq_u8_f64(B))) +#define npyv_and_b8 vandq_u8 +#define npyv_and_b16 vandq_u16 +#define npyv_and_b32 vandq_u32 +#define npyv_and_b64 vandq_u64 + +// OR +#define npyv_or_u8 vorrq_u8 +#define npyv_or_s8 vorrq_s8 +#define npyv_or_u16 vorrq_u16 +#define npyv_or_s16 vorrq_s16 +#define npyv_or_u32 vorrq_u32 +#define npyv_or_s32 vorrq_s32 +#define npyv_or_u64 vorrq_u64 +#define npyv_or_s64 vorrq_s64 +#define npyv_or_f32(A, B) \ + vreinterpretq_f32_u8(vorrq_u8(vreinterpretq_u8_f32(A), vreinterpretq_u8_f32(B))) +#define npyv_or_f64(A, B) \ + vreinterpretq_f64_u8(vorrq_u8(vreinterpretq_u8_f64(A), vreinterpretq_u8_f64(B))) +#define npyv_or_b8 vorrq_u8 +#define npyv_or_b16 vorrq_u16 +#define npyv_or_b32 vorrq_u32 +#define npyv_or_b64 vorrq_u64 + + +// XOR +#define npyv_xor_u8 veorq_u8 +#define npyv_xor_s8 veorq_s8 +#define npyv_xor_u16 veorq_u16 +#define npyv_xor_s16 veorq_s16 +#define npyv_xor_u32 veorq_u32 +#define npyv_xor_s32 veorq_s32 +#define npyv_xor_u64 veorq_u64 +#define npyv_xor_s64 veorq_s64 +#define npyv_xor_f32(A, B) \ + vreinterpretq_f32_u8(veorq_u8(vreinterpretq_u8_f32(A), vreinterpretq_u8_f32(B))) +#define npyv_xor_f64(A, B) \ + vreinterpretq_f64_u8(veorq_u8(vreinterpretq_u8_f64(A), vreinterpretq_u8_f64(B))) +#define npyv_xor_b8 veorq_u8 +#define npyv_xor_b16 veorq_u16 +#define npyv_xor_b32 veorq_u32 +#define npyv_xor_b64 veorq_u64 + +// NOT +#define npyv_not_u8 vmvnq_u8 +#define npyv_not_s8 vmvnq_s8 +#define npyv_not_u16 vmvnq_u16 +#define npyv_not_s16 vmvnq_s16 +#define npyv_not_u32 vmvnq_u32 +#define npyv_not_s32 vmvnq_s32 +#define npyv_not_u64(A) vreinterpretq_u64_u8(vmvnq_u8(vreinterpretq_u8_u64(A))) +#define npyv_not_s64(A) vreinterpretq_s64_u8(vmvnq_u8(vreinterpretq_u8_s64(A))) +#define npyv_not_f32(A) vreinterpretq_f32_u8(vmvnq_u8(vreinterpretq_u8_f32(A))) +#define npyv_not_f64(A) vreinterpretq_f64_u8(vmvnq_u8(vreinterpretq_u8_f64(A))) +#define npyv_not_b8 vmvnq_u8 +#define npyv_not_b16 vmvnq_u16 +#define npyv_not_b32 vmvnq_u32 +#define npyv_not_b64 npyv_not_u64 + +/*************************** + * Comparison + ***************************/ + +// equal +#define npyv_cmpeq_u8 vceqq_u8 +#define npyv_cmpeq_s8 vceqq_s8 +#define npyv_cmpeq_u16 vceqq_u16 +#define npyv_cmpeq_s16 vceqq_s16 +#define npyv_cmpeq_u32 vceqq_u32 +#define npyv_cmpeq_s32 vceqq_s32 +#define npyv_cmpeq_f32 vceqq_f32 +#define npyv_cmpeq_f64 vceqq_f64 + +#ifdef __aarch64__ + #define npyv_cmpeq_u64 vceqq_u64 + #define npyv_cmpeq_s64 vceqq_s64 +#else + NPY_FINLINE uint64x2_t npyv_cmpeq_u64(uint64x2_t a, uint64x2_t b) + { + uint64x2_t cmpeq = vreinterpretq_u64_u32(vceqq_u32( + vreinterpretq_u32_u64(a), vreinterpretq_u32_u64(b) + )); + uint64x2_t cmpeq_h = vshlq_n_u64(cmpeq, 32); + uint64x2_t test = vandq_u64(cmpeq, cmpeq_h); + return vreinterpretq_u64_s64(vshrq_n_s64(vreinterpretq_s64_u64(test), 32)); + } + #define npyv_cmpeq_s64(A, B) \ + npyv_cmpeq_u64(vreinterpretq_u64_s64(A), vreinterpretq_u64_s64(B)) +#endif + +// not Equal +#define npyv_cmpneq_u8(A, B) vmvnq_u8(vceqq_u8(A, B)) +#define npyv_cmpneq_s8(A, B) vmvnq_u8(vceqq_s8(A, B)) +#define npyv_cmpneq_u16(A, B) vmvnq_u16(vceqq_u16(A, B)) +#define npyv_cmpneq_s16(A, B) vmvnq_u16(vceqq_s16(A, B)) +#define npyv_cmpneq_u32(A, B) vmvnq_u32(vceqq_u32(A, B)) +#define npyv_cmpneq_s32(A, B) vmvnq_u32(vceqq_s32(A, B)) +#define npyv_cmpneq_u64(A, B) npyv_not_u64(npyv_cmpeq_u64(A, B)) +#define npyv_cmpneq_s64(A, B) npyv_not_u64(npyv_cmpeq_s64(A, B)) +#define npyv_cmpneq_f32(A, B) vmvnq_u32(vceqq_f32(A, B)) +#define npyv_cmpneq_f64(A, B) npyv_not_u64(vceqq_f64(A, B)) + +// greater than +#define npyv_cmpgt_u8 vcgtq_u8 +#define npyv_cmpgt_s8 vcgtq_s8 +#define npyv_cmpgt_u16 vcgtq_u16 +#define npyv_cmpgt_s16 vcgtq_s16 +#define npyv_cmpgt_u32 vcgtq_u32 +#define npyv_cmpgt_s32 vcgtq_s32 +#define npyv_cmpgt_f32 vcgtq_f32 +#define npyv_cmpgt_f64 vcgtq_f64 + +#ifdef __aarch64__ + #define npyv_cmpgt_u64 vcgtq_u64 + #define npyv_cmpgt_s64 vcgtq_s64 +#else + NPY_FINLINE uint64x2_t npyv_cmpgt_s64(int64x2_t a, int64x2_t b) + { + int64x2_t sub = vsubq_s64(b, a); + uint64x2_t nsame_sbit = vreinterpretq_u64_s64(veorq_s64(a, b)); + int64x2_t test = vbslq_s64(nsame_sbit, b, sub); + int64x2_t extend_sbit = vshrq_n_s64(test, 63); + return vreinterpretq_u64_s64(extend_sbit); + } + NPY_FINLINE uint64x2_t npyv_cmpgt_u64(uint64x2_t a, uint64x2_t b) + { + const uint64x2_t sbit = npyv_setall_u64(0x8000000000000000); + a = npyv_xor_u64(a, sbit); + b = npyv_xor_u64(b, sbit); + return npyv_cmpgt_s64(vreinterpretq_s64_u64(a), vreinterpretq_s64_u64(b)); + } +#endif + +// greater than or equal +#define npyv_cmpge_u8 vcgeq_u8 +#define npyv_cmpge_s8 vcgeq_s8 +#define npyv_cmpge_u16 vcgeq_u16 +#define npyv_cmpge_s16 vcgeq_s16 +#define npyv_cmpge_u32 vcgeq_u32 +#define npyv_cmpge_s32 vcgeq_s32 +#define npyv_cmpge_f32 vcgeq_f32 +#define npyv_cmpge_f64 vcgeq_f64 + +#ifdef __aarch64__ + #define npyv_cmpge_u64 vcgeq_u64 + #define npyv_cmpge_s64 vcgeq_s64 +#else + #define npyv_cmpge_u64(A, B) npyv_not_u64(npyv_cmpgt_u64(B, A)) + #define npyv_cmpge_s64(A, B) npyv_not_u64(npyv_cmpgt_s64(B, A)) +#endif + +// less than +#define npyv_cmplt_u8(A, B) npyv_cmpgt_u8(B, A) +#define npyv_cmplt_s8(A, B) npyv_cmpgt_s8(B, A) +#define npyv_cmplt_u16(A, B) npyv_cmpgt_u16(B, A) +#define npyv_cmplt_s16(A, B) npyv_cmpgt_s16(B, A) +#define npyv_cmplt_u32(A, B) npyv_cmpgt_u32(B, A) +#define npyv_cmplt_s32(A, B) npyv_cmpgt_s32(B, A) +#define npyv_cmplt_u64(A, B) npyv_cmpgt_u64(B, A) +#define npyv_cmplt_s64(A, B) npyv_cmpgt_s64(B, A) +#define npyv_cmplt_f32(A, B) npyv_cmpgt_f32(B, A) +#define npyv_cmplt_f64(A, B) npyv_cmpgt_f64(B, A) + +// less than or equal +#define npyv_cmple_u8(A, B) npyv_cmpge_u8(B, A) +#define npyv_cmple_s8(A, B) npyv_cmpge_s8(B, A) +#define npyv_cmple_u16(A, B) npyv_cmpge_u16(B, A) +#define npyv_cmple_s16(A, B) npyv_cmpge_s16(B, A) +#define npyv_cmple_u32(A, B) npyv_cmpge_u32(B, A) +#define npyv_cmple_s32(A, B) npyv_cmpge_s32(B, A) +#define npyv_cmple_u64(A, B) npyv_cmpge_u64(B, A) +#define npyv_cmple_s64(A, B) npyv_cmpge_s64(B, A) +#define npyv_cmple_f32(A, B) npyv_cmpge_f32(B, A) +#define npyv_cmple_f64(A, B) npyv_cmpge_f64(B, A) + +// check special cases +NPY_FINLINE npyv_b32 npyv_notnan_f32(npyv_f32 a) +{ return vceqq_f32(a, a); } +#if NPY_SIMD_F64 + NPY_FINLINE npyv_b64 npyv_notnan_f64(npyv_f64 a) + { return vceqq_f64(a, a); } +#endif + +#endif // _NPY_SIMD_NEON_OPERATORS_H diff --git a/numpy/core/src/common/simd/neon/reorder.h b/numpy/core/src/common/simd/neon/reorder.h new file mode 100644 index 000000000000..50b06ed11c01 --- /dev/null +++ b/numpy/core/src/common/simd/neon/reorder.h @@ -0,0 +1,119 @@ +#ifndef NPY_SIMD + #error "Not a standalone header" +#endif + +#ifndef _NPY_SIMD_NEON_REORDER_H +#define _NPY_SIMD_NEON_REORDER_H + +// combine lower part of two vectors +#ifdef __aarch64__ + #define npyv_combinel_u8(A, B) vreinterpretq_u8_u64(vzip1q_u64(vreinterpretq_u64_u8(A), vreinterpretq_u64_u8(B))) + #define npyv_combinel_s8(A, B) vreinterpretq_s8_u64(vzip1q_u64(vreinterpretq_u64_s8(A), vreinterpretq_u64_s8(B))) + #define npyv_combinel_u16(A, B) vreinterpretq_u16_u64(vzip1q_u64(vreinterpretq_u64_u16(A), vreinterpretq_u64_u16(B))) + #define npyv_combinel_s16(A, B) vreinterpretq_s16_u64(vzip1q_u64(vreinterpretq_u64_s16(A), vreinterpretq_u64_s16(B))) + #define npyv_combinel_u32(A, B) vreinterpretq_u32_u64(vzip1q_u64(vreinterpretq_u64_u32(A), vreinterpretq_u64_u32(B))) + #define npyv_combinel_s32(A, B) vreinterpretq_s32_u64(vzip1q_u64(vreinterpretq_u64_s32(A), vreinterpretq_u64_s32(B))) + #define npyv_combinel_u64 vzip1q_u64 + #define npyv_combinel_s64 vzip1q_s64 + #define npyv_combinel_f32(A, B) vreinterpretq_f32_u64(vzip1q_u64(vreinterpretq_u64_f32(A), vreinterpretq_u64_f32(B))) + #define npyv_combinel_f64 vzip1q_f64 +#else + #define npyv_combinel_u8(A, B) vcombine_u8(vget_low_u8(A), vget_low_u8(B)) + #define npyv_combinel_s8(A, B) vcombine_s8(vget_low_s8(A), vget_low_s8(B)) + #define npyv_combinel_u16(A, B) vcombine_u16(vget_low_u16(A), vget_low_u16(B)) + #define npyv_combinel_s16(A, B) vcombine_s16(vget_low_s16(A), vget_low_s16(B)) + #define npyv_combinel_u32(A, B) vcombine_u32(vget_low_u32(A), vget_low_u32(B)) + #define npyv_combinel_s32(A, B) vcombine_s32(vget_low_s32(A), vget_low_s32(B)) + #define npyv_combinel_u64(A, B) vcombine_u64(vget_low_u64(A), vget_low_u64(B)) + #define npyv_combinel_s64(A, B) vcombine_s64(vget_low_s64(A), vget_low_s64(B)) + #define npyv_combinel_f32(A, B) vcombine_f32(vget_low_f32(A), vget_low_f32(B)) +#endif + +// combine higher part of two vectors +#ifdef __aarch64__ + #define npyv_combineh_u8(A, B) vreinterpretq_u8_u64(vzip2q_u64(vreinterpretq_u64_u8(A), vreinterpretq_u64_u8(B))) + #define npyv_combineh_s8(A, B) vreinterpretq_s8_u64(vzip2q_u64(vreinterpretq_u64_s8(A), vreinterpretq_u64_s8(B))) + #define npyv_combineh_u16(A, B) vreinterpretq_u16_u64(vzip2q_u64(vreinterpretq_u64_u16(A), vreinterpretq_u64_u16(B))) + #define npyv_combineh_s16(A, B) vreinterpretq_s16_u64(vzip2q_u64(vreinterpretq_u64_s16(A), vreinterpretq_u64_s16(B))) + #define npyv_combineh_u32(A, B) vreinterpretq_u32_u64(vzip2q_u64(vreinterpretq_u64_u32(A), vreinterpretq_u64_u32(B))) + #define npyv_combineh_s32(A, B) vreinterpretq_s32_u64(vzip2q_u64(vreinterpretq_u64_s32(A), vreinterpretq_u64_s32(B))) + #define npyv_combineh_u64 vzip2q_u64 + #define npyv_combineh_s64 vzip2q_s64 + #define npyv_combineh_f32(A, B) vreinterpretq_f32_u64(vzip2q_u64(vreinterpretq_u64_f32(A), vreinterpretq_u64_f32(B))) + #define npyv_combineh_f64 vzip2q_f64 +#else + #define npyv_combineh_u8(A, B) vcombine_u8(vget_high_u8(A), vget_high_u8(B)) + #define npyv_combineh_s8(A, B) vcombine_s8(vget_high_s8(A), vget_high_s8(B)) + #define npyv_combineh_u16(A, B) vcombine_u16(vget_high_u16(A), vget_high_u16(B)) + #define npyv_combineh_s16(A, B) vcombine_s16(vget_high_s16(A), vget_high_s16(B)) + #define npyv_combineh_u32(A, B) vcombine_u32(vget_high_u32(A), vget_high_u32(B)) + #define npyv_combineh_s32(A, B) vcombine_s32(vget_high_s32(A), vget_high_s32(B)) + #define npyv_combineh_u64(A, B) vcombine_u64(vget_high_u64(A), vget_high_u64(B)) + #define npyv_combineh_s64(A, B) vcombine_s64(vget_high_s64(A), vget_high_s64(B)) + #define npyv_combineh_f32(A, B) vcombine_f32(vget_high_f32(A), vget_high_f32(B)) +#endif + +// combine two vectors from lower and higher parts of two other vectors +#define NPYV_IMPL_NEON_COMBINE(T_VEC, SFX) \ + NPY_FINLINE T_VEC##x2 npyv_combine_##SFX(T_VEC a, T_VEC b) \ + { \ + T_VEC##x2 r; \ + r.val[0] = NPY_CAT(npyv_combinel_, SFX)(a, b); \ + r.val[1] = NPY_CAT(npyv_combineh_, SFX)(a, b); \ + return r; \ + } + +NPYV_IMPL_NEON_COMBINE(npyv_u8, u8) +NPYV_IMPL_NEON_COMBINE(npyv_s8, s8) +NPYV_IMPL_NEON_COMBINE(npyv_u16, u16) +NPYV_IMPL_NEON_COMBINE(npyv_s16, s16) +NPYV_IMPL_NEON_COMBINE(npyv_u32, u32) +NPYV_IMPL_NEON_COMBINE(npyv_s32, s32) +NPYV_IMPL_NEON_COMBINE(npyv_u64, u64) +NPYV_IMPL_NEON_COMBINE(npyv_s64, s64) +NPYV_IMPL_NEON_COMBINE(npyv_f32, f32) +#ifdef __aarch64__ +NPYV_IMPL_NEON_COMBINE(npyv_f64, f64) +#endif + +// interleave two vectors +#define NPYV_IMPL_NEON_ZIP(T_VEC, SFX) \ + NPY_FINLINE T_VEC##x2 npyv_zip_##SFX(T_VEC a, T_VEC b) \ + { \ + T_VEC##x2 r; \ + r.val[0] = vzip1q_##SFX(a, b); \ + r.val[1] = vzip2q_##SFX(a, b); \ + return r; \ + } + +#ifdef __aarch64__ + NPYV_IMPL_NEON_ZIP(npyv_u8, u8) + NPYV_IMPL_NEON_ZIP(npyv_s8, s8) + NPYV_IMPL_NEON_ZIP(npyv_u16, u16) + NPYV_IMPL_NEON_ZIP(npyv_s16, s16) + NPYV_IMPL_NEON_ZIP(npyv_u32, u32) + NPYV_IMPL_NEON_ZIP(npyv_s32, s32) + NPYV_IMPL_NEON_ZIP(npyv_f32, f32) + NPYV_IMPL_NEON_ZIP(npyv_f64, f64) +#else + #define npyv_zip_u8 vzipq_u8 + #define npyv_zip_s8 vzipq_s8 + #define npyv_zip_u16 vzipq_u16 + #define npyv_zip_s16 vzipq_s16 + #define npyv_zip_u32 vzipq_u32 + #define npyv_zip_s32 vzipq_s32 + #define npyv_zip_f32 vzipq_f32 +#endif +#define npyv_zip_u64 npyv_combine_u64 +#define npyv_zip_s64 npyv_combine_s64 + +// Reverse elements of each 64-bit lane +#define npyv_rev64_u8 vrev64q_u8 +#define npyv_rev64_s8 vrev64q_s8 +#define npyv_rev64_u16 vrev64q_u16 +#define npyv_rev64_s16 vrev64q_s16 +#define npyv_rev64_u32 vrev64q_u32 +#define npyv_rev64_s32 vrev64q_s32 +#define npyv_rev64_f32 vrev64q_f32 + +#endif // _NPY_SIMD_NEON_REORDER_H diff --git a/numpy/core/src/common/simd/simd.h b/numpy/core/src/common/simd/simd.h new file mode 100644 index 000000000000..08b2a7d000f6 --- /dev/null +++ b/numpy/core/src/common/simd/simd.h @@ -0,0 +1,136 @@ +#ifndef _NPY_SIMD_H_ +#define _NPY_SIMD_H_ +/** + * the NumPy C SIMD vectorization interface "NPYV" are types and functions intended + * to simplify vectorization of code on different platforms, currently supports + * the following SIMD extensions SSE, AVX2, AVX512, VSX and NEON. + * + * TODO: Add an independent sphinx doc. +*/ +#include "numpy/npy_common.h" +#include "npy_cpu_dispatch.h" +#include "simd_utils.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// lane type by intrin suffix +typedef npy_uint8 npyv_lanetype_u8; +typedef npy_int8 npyv_lanetype_s8; +typedef npy_uint16 npyv_lanetype_u16; +typedef npy_int16 npyv_lanetype_s16; +typedef npy_uint32 npyv_lanetype_u32; +typedef npy_int32 npyv_lanetype_s32; +typedef npy_uint64 npyv_lanetype_u64; +typedef npy_int64 npyv_lanetype_s64; +typedef float npyv_lanetype_f32; +typedef double npyv_lanetype_f64; + +#if defined(_MSC_VER) && defined(_M_IX86) +/* + * Avoid using any of the following intrinsics with MSVC 32-bit, + * even if they are apparently work on newer versions. + * They had bad impact on the generated instructions, + * sometimes the compiler deal with them without the respect + * of 32-bit mode which lead to crush due to execute 64-bit + * instructions and other times generate bad emulated instructions. + */ + #undef _mm512_set1_epi64 + #undef _mm256_set1_epi64x + #undef _mm_set1_epi64x + #undef _mm512_setr_epi64x + #undef _mm256_setr_epi64x + #undef _mm_setr_epi64x + #undef _mm512_set_epi64x + #undef _mm256_set_epi64x + #undef _mm_set_epi64x +#endif +#if defined(NPY_HAVE_AVX512F) && !defined(NPY_SIMD_FORCE_256) && !defined(NPY_SIMD_FORCE_128) + #include "avx512/avx512.h" +#elif defined(NPY_HAVE_AVX2) && !defined(NPY_SIMD_FORCE_128) + #include "avx2/avx2.h" +#elif defined(NPY_HAVE_SSE2) + #include "sse/sse.h" +#endif + +// TODO: Add support for VSX(2.06) and BE Mode +#if defined(NPY_HAVE_VSX2) && defined(__LITTLE_ENDIAN__) + #include "vsx/vsx.h" +#endif + +#ifdef NPY_HAVE_NEON + #include "neon/neon.h" +#endif + +#ifndef NPY_SIMD + #define NPY_SIMD 0 + #define NPY_SIMD_WIDTH 0 + #define NPY_SIMD_F64 0 + #define NPY_SIMD_FMA3 0 +#endif + +// enable emulated mask operations for all SIMD extension except for AVX512 +#if !defined(NPY_HAVE_AVX512F) && NPY_SIMD && NPY_SIMD < 512 + #include "emulate_maskop.h" +#endif + +// enable integer divisor generator for all SIMD extensions +#if NPY_SIMD + #include "intdiv.h" +#endif + +/** + * Some SIMD extensions currently(AVX2, AVX512F) require (de facto) + * a maximum number of strides sizes when dealing with non-contiguous memory access. + * + * Therefore the following functions must be used to check the maximum + * acceptable limit of strides before using any of non-contiguous load/store intrinsics. + * + * For instance: + * npy_intp ld_stride = step[0] / sizeof(float); + * npy_intp st_stride = step[1] / sizeof(float); + * + * if (npyv_loadable_stride_f32(ld_stride) && npyv_storable_stride_f32(st_stride)) { + * for (;;) + * npyv_f32 a = npyv_loadn_f32(ld_pointer, ld_stride); + * // ... + * npyv_storen_f32(st_pointer, st_stride, a); + * } + * else { + * for (;;) + * // C scalars + * } + */ +#ifndef NPY_SIMD_MAXLOAD_STRIDE32 + #define NPY_SIMD_MAXLOAD_STRIDE32 0 +#endif +#ifndef NPY_SIMD_MAXSTORE_STRIDE32 + #define NPY_SIMD_MAXSTORE_STRIDE32 0 +#endif +#ifndef NPY_SIMD_MAXLOAD_STRIDE64 + #define NPY_SIMD_MAXLOAD_STRIDE64 0 +#endif +#ifndef NPY_SIMD_MAXSTORE_STRIDE64 + #define NPY_SIMD_MAXSTORE_STRIDE64 0 +#endif +#define NPYV_IMPL_MAXSTRIDE(SFX, MAXLOAD, MAXSTORE) \ + NPY_FINLINE int npyv_loadable_stride_##SFX(npy_intp stride) \ + { return MAXLOAD > 0 ? llabs(stride) <= MAXLOAD : 1; } \ + NPY_FINLINE int npyv_storable_stride_##SFX(npy_intp stride) \ + { return MAXSTORE > 0 ? llabs(stride) <= MAXSTORE : 1; } +#if NPY_SIMD + NPYV_IMPL_MAXSTRIDE(u32, NPY_SIMD_MAXLOAD_STRIDE32, NPY_SIMD_MAXSTORE_STRIDE32) + NPYV_IMPL_MAXSTRIDE(s32, NPY_SIMD_MAXLOAD_STRIDE32, NPY_SIMD_MAXSTORE_STRIDE32) + NPYV_IMPL_MAXSTRIDE(f32, NPY_SIMD_MAXLOAD_STRIDE32, NPY_SIMD_MAXSTORE_STRIDE32) + NPYV_IMPL_MAXSTRIDE(u64, NPY_SIMD_MAXLOAD_STRIDE64, NPY_SIMD_MAXSTORE_STRIDE64) + NPYV_IMPL_MAXSTRIDE(s64, NPY_SIMD_MAXLOAD_STRIDE64, NPY_SIMD_MAXSTORE_STRIDE64) +#endif +#if NPY_SIMD_F64 + NPYV_IMPL_MAXSTRIDE(f64, NPY_SIMD_MAXLOAD_STRIDE64, NPY_SIMD_MAXSTORE_STRIDE64) +#endif + +#ifdef __cplusplus +} +#endif +#endif // _NPY_SIMD_H_ diff --git a/numpy/core/src/common/simd/simd_utils.h b/numpy/core/src/common/simd/simd_utils.h new file mode 100644 index 000000000000..06c2f16f7683 --- /dev/null +++ b/numpy/core/src/common/simd/simd_utils.h @@ -0,0 +1,48 @@ +#ifndef _NPY_SIMD_UTILS_H +#define _NPY_SIMD_UTILS_H + +#define NPYV__SET_2(CAST, I0, I1, ...) (CAST)(I0), (CAST)(I1) + +#define NPYV__SET_4(CAST, I0, I1, I2, I3, ...) \ + (CAST)(I0), (CAST)(I1), (CAST)(I2), (CAST)(I3) + +#define NPYV__SET_8(CAST, I0, I1, I2, I3, I4, I5, I6, I7, ...) \ + (CAST)(I0), (CAST)(I1), (CAST)(I2), (CAST)(I3), (CAST)(I4), (CAST)(I5), (CAST)(I6), (CAST)(I7) + +#define NPYV__SET_16(CAST, I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13, I14, I15, ...) \ + NPYV__SET_8(CAST, I0, I1, I2, I3, I4, I5, I6, I7), \ + NPYV__SET_8(CAST, I8, I9, I10, I11, I12, I13, I14, I15) + +#define NPYV__SET_32(CAST, I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13, I14, I15, \ +I16, I17, I18, I19, I20, I21, I22, I23, I24, I25, I26, I27, I28, I29, I30, I31, ...) \ + \ + NPYV__SET_16(CAST, I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13, I14, I15), \ + NPYV__SET_16(CAST, I16, I17, I18, I19, I20, I21, I22, I23, I24, I25, I26, I27, I28, I29, I30, I31) + +#define NPYV__SET_64(CAST, I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13, I14, I15, \ +I16, I17, I18, I19, I20, I21, I22, I23, I24, I25, I26, I27, I28, I29, I30, I31, \ +I32, I33, I34, I35, I36, I37, I38, I39, I40, I41, I42, I43, I44, I45, I46, I47, \ +I48, I49, I50, I51, I52, I53, I54, I55, I56, I57, I58, I59, I60, I61, I62, I63, ...) \ + \ + NPYV__SET_32(CAST, I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13, I14, I15, \ +I16, I17, I18, I19, I20, I21, I22, I23, I24, I25, I26, I27, I28, I29, I30, I31), \ + NPYV__SET_32(CAST, I32, I33, I34, I35, I36, I37, I38, I39, I40, I41, I42, I43, I44, I45, I46, I47, \ +I48, I49, I50, I51, I52, I53, I54, I55, I56, I57, I58, I59, I60, I61, I62, I63) + +#define NPYV__SET_FILL_2(CAST, F, ...) NPY_EXPAND(NPYV__SET_2(CAST, __VA_ARGS__, F, F)) + +#define NPYV__SET_FILL_4(CAST, F, ...) NPY_EXPAND(NPYV__SET_4(CAST, __VA_ARGS__, F, F, F, F)) + +#define NPYV__SET_FILL_8(CAST, F, ...) NPY_EXPAND(NPYV__SET_8(CAST, __VA_ARGS__, F, F, F, F, F, F, F, F)) + +#define NPYV__SET_FILL_16(CAST, F, ...) NPY_EXPAND(NPYV__SET_16(CAST, __VA_ARGS__, \ + F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F)) + +#define NPYV__SET_FILL_32(CAST, F, ...) NPY_EXPAND(NPYV__SET_32(CAST, __VA_ARGS__, \ + F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F)) + +#define NPYV__SET_FILL_64(CAST, F, ...) NPY_EXPAND(NPYV__SET_64(CAST, __VA_ARGS__, \ + F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, \ + F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F)) + +#endif // _NPY_SIMD_UTILS_H diff --git a/numpy/core/src/common/simd/sse/arithmetic.h b/numpy/core/src/common/simd/sse/arithmetic.h new file mode 100644 index 000000000000..bced35108116 --- /dev/null +++ b/numpy/core/src/common/simd/sse/arithmetic.h @@ -0,0 +1,385 @@ +#ifndef NPY_SIMD + #error "Not a standalone header" +#endif + +#ifndef _NPY_SIMD_SSE_ARITHMETIC_H +#define _NPY_SIMD_SSE_ARITHMETIC_H + +/*************************** + * Addition + ***************************/ +// non-saturated +#define npyv_add_u8 _mm_add_epi8 +#define npyv_add_s8 _mm_add_epi8 +#define npyv_add_u16 _mm_add_epi16 +#define npyv_add_s16 _mm_add_epi16 +#define npyv_add_u32 _mm_add_epi32 +#define npyv_add_s32 _mm_add_epi32 +#define npyv_add_u64 _mm_add_epi64 +#define npyv_add_s64 _mm_add_epi64 +#define npyv_add_f32 _mm_add_ps +#define npyv_add_f64 _mm_add_pd + +// saturated +#define npyv_adds_u8 _mm_adds_epu8 +#define npyv_adds_s8 _mm_adds_epi8 +#define npyv_adds_u16 _mm_adds_epu16 +#define npyv_adds_s16 _mm_adds_epi16 +// TODO: rest, after implement Packs intrins + +/*************************** + * Subtraction + ***************************/ +// non-saturated +#define npyv_sub_u8 _mm_sub_epi8 +#define npyv_sub_s8 _mm_sub_epi8 +#define npyv_sub_u16 _mm_sub_epi16 +#define npyv_sub_s16 _mm_sub_epi16 +#define npyv_sub_u32 _mm_sub_epi32 +#define npyv_sub_s32 _mm_sub_epi32 +#define npyv_sub_u64 _mm_sub_epi64 +#define npyv_sub_s64 _mm_sub_epi64 +#define npyv_sub_f32 _mm_sub_ps +#define npyv_sub_f64 _mm_sub_pd + +// saturated +#define npyv_subs_u8 _mm_subs_epu8 +#define npyv_subs_s8 _mm_subs_epi8 +#define npyv_subs_u16 _mm_subs_epu16 +#define npyv_subs_s16 _mm_subs_epi16 +// TODO: rest, after implement Packs intrins + +/*************************** + * Multiplication + ***************************/ +// non-saturated +NPY_FINLINE __m128i npyv_mul_u8(__m128i a, __m128i b) +{ + const __m128i mask = _mm_set1_epi32(0xFF00FF00); + __m128i even = _mm_mullo_epi16(a, b); + __m128i odd = _mm_mullo_epi16(_mm_srai_epi16(a, 8), _mm_srai_epi16(b, 8)); + odd = _mm_slli_epi16(odd, 8); + return npyv_select_u8(mask, odd, even); +} +#define npyv_mul_s8 npyv_mul_u8 +#define npyv_mul_u16 _mm_mullo_epi16 +#define npyv_mul_s16 _mm_mullo_epi16 + +#ifdef NPY_HAVE_SSE41 + #define npyv_mul_u32 _mm_mullo_epi32 +#else + NPY_FINLINE __m128i npyv_mul_u32(__m128i a, __m128i b) + { + __m128i even = _mm_mul_epu32(a, b); + __m128i odd = _mm_mul_epu32(_mm_srli_epi64(a, 32), _mm_srli_epi64(b, 32)); + __m128i low = _mm_unpacklo_epi32(even, odd); + __m128i high = _mm_unpackhi_epi32(even, odd); + return _mm_unpacklo_epi64(low, high); + } +#endif // NPY_HAVE_SSE41 +#define npyv_mul_s32 npyv_mul_u32 +// TODO: emulate 64-bit*/ +#define npyv_mul_f32 _mm_mul_ps +#define npyv_mul_f64 _mm_mul_pd + +// saturated +// TODO: after implement Packs intrins + +/*************************** + * Integer Division + ***************************/ +// See simd/intdiv.h for more clarification +// divide each unsigned 8-bit element by a precomputed divisor +NPY_FINLINE npyv_u8 npyv_divc_u8(npyv_u8 a, const npyv_u8x3 divisor) +{ + const __m128i bmask = _mm_set1_epi32(0x00FF00FF); + const __m128i shf1b = _mm_set1_epi8(0xFFU >> _mm_cvtsi128_si32(divisor.val[1])); + const __m128i shf2b = _mm_set1_epi8(0xFFU >> _mm_cvtsi128_si32(divisor.val[2])); + // high part of unsigned multiplication + __m128i mulhi_even = _mm_mullo_epi16(_mm_and_si128(a, bmask), divisor.val[0]); + __m128i mulhi_odd = _mm_mullo_epi16(_mm_srli_epi16(a, 8), divisor.val[0]); + mulhi_even = _mm_srli_epi16(mulhi_even, 8); + __m128i mulhi = npyv_select_u8(bmask, mulhi_even, mulhi_odd); + // floor(a/d) = (mulhi + ((a-mulhi) >> sh1)) >> sh2 + __m128i q = _mm_sub_epi8(a, mulhi); + q = _mm_and_si128(_mm_srl_epi16(q, divisor.val[1]), shf1b); + q = _mm_add_epi8(mulhi, q); + q = _mm_and_si128(_mm_srl_epi16(q, divisor.val[2]), shf2b); + return q; +} +// divide each signed 8-bit element by a precomputed divisor (round towards zero) +NPY_FINLINE npyv_s16 npyv_divc_s16(npyv_s16 a, const npyv_s16x3 divisor); +NPY_FINLINE npyv_s8 npyv_divc_s8(npyv_s8 a, const npyv_s8x3 divisor) +{ + const __m128i bmask = _mm_set1_epi32(0x00FF00FF); + // instead of _mm_cvtepi8_epi16/_mm_packs_epi16 to wrap around overflow + __m128i divc_even = npyv_divc_s16(_mm_srai_epi16(_mm_slli_epi16(a, 8), 8), divisor); + __m128i divc_odd = npyv_divc_s16(_mm_srai_epi16(a, 8), divisor); + divc_odd = _mm_slli_epi16(divc_odd, 8); + return npyv_select_u8(bmask, divc_even, divc_odd); +} +// divide each unsigned 16-bit element by a precomputed divisor +NPY_FINLINE npyv_u16 npyv_divc_u16(npyv_u16 a, const npyv_u16x3 divisor) +{ + // high part of unsigned multiplication + __m128i mulhi = _mm_mulhi_epu16(a, divisor.val[0]); + // floor(a/d) = (mulhi + ((a-mulhi) >> sh1)) >> sh2 + __m128i q = _mm_sub_epi16(a, mulhi); + q = _mm_srl_epi16(q, divisor.val[1]); + q = _mm_add_epi16(mulhi, q); + q = _mm_srl_epi16(q, divisor.val[2]); + return q; +} +// divide each signed 16-bit element by a precomputed divisor (round towards zero) +NPY_FINLINE npyv_s16 npyv_divc_s16(npyv_s16 a, const npyv_s16x3 divisor) +{ + // high part of signed multiplication + __m128i mulhi = _mm_mulhi_epi16(a, divisor.val[0]); + // q = ((a + mulhi) >> sh1) - XSIGN(a) + // trunc(a/d) = (q ^ dsign) - dsign + __m128i q = _mm_sra_epi16(_mm_add_epi16(a, mulhi), divisor.val[1]); + q = _mm_sub_epi16(q, _mm_srai_epi16(a, 15)); + q = _mm_sub_epi16(_mm_xor_si128(q, divisor.val[2]), divisor.val[2]); + return q; +} +// divide each unsigned 32-bit element by a precomputed divisor +NPY_FINLINE npyv_u32 npyv_divc_u32(npyv_u32 a, const npyv_u32x3 divisor) +{ + // high part of unsigned multiplication + __m128i mulhi_even = _mm_srli_epi64(_mm_mul_epu32(a, divisor.val[0]), 32); + __m128i mulhi_odd = _mm_mul_epu32(_mm_srli_epi64(a, 32), divisor.val[0]); +#ifdef NPY_HAVE_SSE41 + __m128i mulhi = _mm_blend_epi16(mulhi_even, mulhi_odd, 0xCC); +#else + __m128i mask_13 = _mm_setr_epi32(0, -1, 0, -1); + mulhi_odd = _mm_and_si128(mulhi_odd, mask_13); + __m128i mulhi = _mm_or_si128(mulhi_even, mulhi_odd); +#endif + // floor(a/d) = (mulhi + ((a-mulhi) >> sh1)) >> sh2 + __m128i q = _mm_sub_epi32(a, mulhi); + q = _mm_srl_epi32(q, divisor.val[1]); + q = _mm_add_epi32(mulhi, q); + q = _mm_srl_epi32(q, divisor.val[2]); + return q; +} +// divide each signed 32-bit element by a precomputed divisor (round towards zero) +NPY_FINLINE npyv_s32 npyv_divc_s32(npyv_s32 a, const npyv_s32x3 divisor) +{ + __m128i asign = _mm_srai_epi32(a, 31); +#ifdef NPY_HAVE_SSE41 + // high part of signed multiplication + __m128i mulhi_even = _mm_srli_epi64(_mm_mul_epi32(a, divisor.val[0]), 32); + __m128i mulhi_odd = _mm_mul_epi32(_mm_srli_epi64(a, 32), divisor.val[0]); + __m128i mulhi = _mm_blend_epi16(mulhi_even, mulhi_odd, 0xCC); +#else // not SSE4.1 + // high part of "unsigned" multiplication + __m128i mulhi_even = _mm_srli_epi64(_mm_mul_epu32(a, divisor.val[0]), 32); + __m128i mulhi_odd = _mm_mul_epu32(_mm_srli_epi64(a, 32), divisor.val[0]); + __m128i mask_13 = _mm_setr_epi32(0, -1, 0, -1); + mulhi_odd = _mm_and_si128(mulhi_odd, mask_13); + __m128i mulhi = _mm_or_si128(mulhi_even, mulhi_odd); + // convert unsigned to signed high multiplication + // mulhi - ((a < 0) ? m : 0) - ((m < 0) ? a : 0); + const __m128i msign= _mm_srai_epi32(divisor.val[0], 31); + __m128i m_asign = _mm_and_si128(divisor.val[0], asign); + __m128i a_msign = _mm_and_si128(a, msign); + mulhi = _mm_sub_epi32(mulhi, m_asign); + mulhi = _mm_sub_epi32(mulhi, a_msign); +#endif + // q = ((a + mulhi) >> sh1) - XSIGN(a) + // trunc(a/d) = (q ^ dsign) - dsign + __m128i q = _mm_sra_epi32(_mm_add_epi32(a, mulhi), divisor.val[1]); + q = _mm_sub_epi32(q, asign); + q = _mm_sub_epi32(_mm_xor_si128(q, divisor.val[2]), divisor.val[2]); + return q; +} +// returns the high 64 bits of unsigned 64-bit multiplication +// xref https://stackoverflow.com/a/28827013 +NPY_FINLINE npyv_u64 npyv__mullhi_u64(npyv_u64 a, npyv_u64 b) +{ + __m128i lomask = npyv_setall_s64(0xffffffff); + __m128i a_hi = _mm_srli_epi64(a, 32); // a0l, a0h, a1l, a1h + __m128i b_hi = _mm_srli_epi64(b, 32); // b0l, b0h, b1l, b1h + // compute partial products + __m128i w0 = _mm_mul_epu32(a, b); // a0l*b0l, a1l*b1l + __m128i w1 = _mm_mul_epu32(a, b_hi); // a0l*b0h, a1l*b1h + __m128i w2 = _mm_mul_epu32(a_hi, b); // a0h*b0l, a1h*b0l + __m128i w3 = _mm_mul_epu32(a_hi, b_hi); // a0h*b0h, a1h*b1h + // sum partial products + __m128i w0h = _mm_srli_epi64(w0, 32); + __m128i s1 = _mm_add_epi64(w1, w0h); + __m128i s1l = _mm_and_si128(s1, lomask); + __m128i s1h = _mm_srli_epi64(s1, 32); + + __m128i s2 = _mm_add_epi64(w2, s1l); + __m128i s2h = _mm_srli_epi64(s2, 32); + + __m128i hi = _mm_add_epi64(w3, s1h); + hi = _mm_add_epi64(hi, s2h); + return hi; +} +// divide each unsigned 64-bit element by a precomputed divisor +NPY_FINLINE npyv_u64 npyv_divc_u64(npyv_u64 a, const npyv_u64x3 divisor) +{ + // high part of unsigned multiplication + __m128i mulhi = npyv__mullhi_u64(a, divisor.val[0]); + // floor(a/d) = (mulhi + ((a-mulhi) >> sh1)) >> sh2 + __m128i q = _mm_sub_epi64(a, mulhi); + q = _mm_srl_epi64(q, divisor.val[1]); + q = _mm_add_epi64(mulhi, q); + q = _mm_srl_epi64(q, divisor.val[2]); + return q; +} +// divide each signed 64-bit element by a precomputed divisor (round towards zero) +NPY_FINLINE npyv_s64 npyv_divc_s64(npyv_s64 a, const npyv_s64x3 divisor) +{ + // high part of unsigned multiplication + __m128i mulhi = npyv__mullhi_u64(a, divisor.val[0]); + // convert unsigned to signed high multiplication + // mulhi - ((a < 0) ? m : 0) - ((m < 0) ? a : 0); +#ifdef NPY_HAVE_SSE42 + const __m128i msign= _mm_cmpgt_epi64(_mm_setzero_si128(), divisor.val[0]); + __m128i asign = _mm_cmpgt_epi64(_mm_setzero_si128(), a); +#else + const __m128i msign= _mm_srai_epi32(_mm_shuffle_epi32(divisor.val[0], _MM_SHUFFLE(3, 3, 1, 1)), 31); + __m128i asign = _mm_srai_epi32(_mm_shuffle_epi32(a, _MM_SHUFFLE(3, 3, 1, 1)), 31); +#endif + __m128i m_asign = _mm_and_si128(divisor.val[0], asign); + __m128i a_msign = _mm_and_si128(a, msign); + mulhi = _mm_sub_epi64(mulhi, m_asign); + mulhi = _mm_sub_epi64(mulhi, a_msign); + // q = (a + mulhi) >> sh + __m128i q = _mm_add_epi64(a, mulhi); + // emulate arithmetic right shift + const __m128i sigb = npyv_setall_s64(1LL << 63); + q = _mm_srl_epi64(_mm_add_epi64(q, sigb), divisor.val[1]); + q = _mm_sub_epi64(q, _mm_srl_epi64(sigb, divisor.val[1])); + // q = q - XSIGN(a) + // trunc(a/d) = (q ^ dsign) - dsign + q = _mm_sub_epi64(q, asign); + q = _mm_sub_epi64(_mm_xor_si128(q, divisor.val[2]), divisor.val[2]); + return q; +} +/*************************** + * Division + ***************************/ +// TODO: emulate integer division +#define npyv_div_f32 _mm_div_ps +#define npyv_div_f64 _mm_div_pd +/*************************** + * FUSED + ***************************/ +#ifdef NPY_HAVE_FMA3 + // multiply and add, a*b + c + #define npyv_muladd_f32 _mm_fmadd_ps + #define npyv_muladd_f64 _mm_fmadd_pd + // multiply and subtract, a*b - c + #define npyv_mulsub_f32 _mm_fmsub_ps + #define npyv_mulsub_f64 _mm_fmsub_pd + // negate multiply and add, -(a*b) + c + #define npyv_nmuladd_f32 _mm_fnmadd_ps + #define npyv_nmuladd_f64 _mm_fnmadd_pd + // negate multiply and subtract, -(a*b) - c + #define npyv_nmulsub_f32 _mm_fnmsub_ps + #define npyv_nmulsub_f64 _mm_fnmsub_pd +#elif defined(NPY_HAVE_FMA4) + // multiply and add, a*b + c + #define npyv_muladd_f32 _mm_macc_ps + #define npyv_muladd_f64 _mm_macc_pd + // multiply and subtract, a*b - c + #define npyv_mulsub_f32 _mm_msub_ps + #define npyv_mulsub_f64 _mm_msub_pd + // negate multiply and add, -(a*b) + c + #define npyv_nmuladd_f32 _mm_nmacc_ps + #define npyv_nmuladd_f64 _mm_nmacc_pd +#else + // multiply and add, a*b + c + NPY_FINLINE npyv_f32 npyv_muladd_f32(npyv_f32 a, npyv_f32 b, npyv_f32 c) + { return npyv_add_f32(npyv_mul_f32(a, b), c); } + NPY_FINLINE npyv_f64 npyv_muladd_f64(npyv_f64 a, npyv_f64 b, npyv_f64 c) + { return npyv_add_f64(npyv_mul_f64(a, b), c); } + // multiply and subtract, a*b - c + NPY_FINLINE npyv_f32 npyv_mulsub_f32(npyv_f32 a, npyv_f32 b, npyv_f32 c) + { return npyv_sub_f32(npyv_mul_f32(a, b), c); } + NPY_FINLINE npyv_f64 npyv_mulsub_f64(npyv_f64 a, npyv_f64 b, npyv_f64 c) + { return npyv_sub_f64(npyv_mul_f64(a, b), c); } + // negate multiply and add, -(a*b) + c + NPY_FINLINE npyv_f32 npyv_nmuladd_f32(npyv_f32 a, npyv_f32 b, npyv_f32 c) + { return npyv_sub_f32(c, npyv_mul_f32(a, b)); } + NPY_FINLINE npyv_f64 npyv_nmuladd_f64(npyv_f64 a, npyv_f64 b, npyv_f64 c) + { return npyv_sub_f64(c, npyv_mul_f64(a, b)); } +#endif // NPY_HAVE_FMA3 +#ifndef NPY_HAVE_FMA3 // for FMA4 and NON-FMA3 + // negate multiply and subtract, -(a*b) - c + NPY_FINLINE npyv_f32 npyv_nmulsub_f32(npyv_f32 a, npyv_f32 b, npyv_f32 c) + { + npyv_f32 neg_a = npyv_xor_f32(a, npyv_setall_f32(-0.0f)); + return npyv_sub_f32(npyv_mul_f32(neg_a, b), c); + } + NPY_FINLINE npyv_f64 npyv_nmulsub_f64(npyv_f64 a, npyv_f64 b, npyv_f64 c) + { + npyv_f64 neg_a = npyv_xor_f64(a, npyv_setall_f64(-0.0)); + return npyv_sub_f64(npyv_mul_f64(neg_a, b), c); + } +#endif // !NPY_HAVE_FMA3 + +/*************************** + * Summation + ***************************/ +// reduce sum across vector +NPY_FINLINE npy_uint32 npyv_sum_u32(npyv_u32 a) +{ + __m128i t = _mm_add_epi32(a, _mm_srli_si128(a, 8)); + t = _mm_add_epi32(t, _mm_srli_si128(t, 4)); + return (unsigned)_mm_cvtsi128_si32(t); +} + +NPY_FINLINE npy_uint64 npyv_sum_u64(npyv_u64 a) +{ + __m128i one = _mm_add_epi64(a, _mm_unpackhi_epi64(a, a)); + return (npy_uint64)npyv128_cvtsi128_si64(one); +} + +NPY_FINLINE float npyv_sum_f32(npyv_f32 a) +{ +#ifdef NPY_HAVE_SSE3 + __m128 sum_halves = _mm_hadd_ps(a, a); + return _mm_cvtss_f32(_mm_hadd_ps(sum_halves, sum_halves)); +#else + __m128 t1 = _mm_movehl_ps(a, a); + __m128 t2 = _mm_add_ps(a, t1); + __m128 t3 = _mm_shuffle_ps(t2, t2, 1); + __m128 t4 = _mm_add_ss(t2, t3); + return _mm_cvtss_f32(t4); +#endif +} + +NPY_FINLINE double npyv_sum_f64(npyv_f64 a) +{ +#ifdef NPY_HAVE_SSE3 + return _mm_cvtsd_f64(_mm_hadd_pd(a, a)); +#else + return _mm_cvtsd_f64(_mm_add_pd(a, _mm_unpackhi_pd(a, a))); +#endif +} + +// expand the source vector and performs sum reduce +NPY_FINLINE npy_uint16 npyv_sumup_u8(npyv_u8 a) +{ + __m128i two = _mm_sad_epu8(a, _mm_setzero_si128()); + __m128i one = _mm_add_epi16(two, _mm_unpackhi_epi64(two, two)); + return (npy_uint16)_mm_cvtsi128_si32(one); +} + +NPY_FINLINE npy_uint32 npyv_sumup_u16(npyv_u16 a) +{ + const __m128i even_mask = _mm_set1_epi32(0x0000FFFF); + __m128i even = _mm_and_si128(a, even_mask); + __m128i odd = _mm_srli_epi32(a, 16); + __m128i four = _mm_add_epi32(even, odd); + return npyv_sum_u32(four); +} + +#endif // _NPY_SIMD_SSE_ARITHMETIC_H + + diff --git a/numpy/core/src/common/simd/sse/conversion.h b/numpy/core/src/common/simd/sse/conversion.h new file mode 100644 index 000000000000..ab7eb490727b --- /dev/null +++ b/numpy/core/src/common/simd/sse/conversion.h @@ -0,0 +1,70 @@ +#ifndef NPY_SIMD + #error "Not a standalone header" +#endif + +#ifndef _NPY_SIMD_SSE_CVT_H +#define _NPY_SIMD_SSE_CVT_H + +// convert mask types to integer types +#define npyv_cvt_u8_b8(BL) BL +#define npyv_cvt_s8_b8(BL) BL +#define npyv_cvt_u16_b16(BL) BL +#define npyv_cvt_s16_b16(BL) BL +#define npyv_cvt_u32_b32(BL) BL +#define npyv_cvt_s32_b32(BL) BL +#define npyv_cvt_u64_b64(BL) BL +#define npyv_cvt_s64_b64(BL) BL +#define npyv_cvt_f32_b32 _mm_castsi128_ps +#define npyv_cvt_f64_b64 _mm_castsi128_pd + +// convert integer types to mask types +#define npyv_cvt_b8_u8(A) A +#define npyv_cvt_b8_s8(A) A +#define npyv_cvt_b16_u16(A) A +#define npyv_cvt_b16_s16(A) A +#define npyv_cvt_b32_u32(A) A +#define npyv_cvt_b32_s32(A) A +#define npyv_cvt_b64_u64(A) A +#define npyv_cvt_b64_s64(A) A +#define npyv_cvt_b32_f32 _mm_castps_si128 +#define npyv_cvt_b64_f64 _mm_castpd_si128 + +// convert boolean vector to integer bitfield +NPY_FINLINE npy_uint64 npyv_tobits_b8(npyv_b8 a) +{ return (npy_uint16)_mm_movemask_epi8(a); } +NPY_FINLINE npy_uint64 npyv_tobits_b16(npyv_b16 a) +{ + __m128i pack = _mm_packs_epi16(a, a); + return (npy_uint8)_mm_movemask_epi8(pack); +} +NPY_FINLINE npy_uint64 npyv_tobits_b32(npyv_b32 a) +{ return (npy_uint8)_mm_movemask_ps(_mm_castsi128_ps(a)); } +NPY_FINLINE npy_uint64 npyv_tobits_b64(npyv_b64 a) +{ return (npy_uint8)_mm_movemask_pd(_mm_castsi128_pd(a)); } + +// expand +NPY_FINLINE npyv_u16x2 npyv_expand_u16_u8(npyv_u8 data) { + npyv_u16x2 r; + const __m128i z = _mm_setzero_si128(); + r.val[0] = _mm_unpacklo_epi8(data, z); + r.val[1] = _mm_unpackhi_epi8(data, z); + return r; +} + +NPY_FINLINE npyv_u32x2 npyv_expand_u32_u16(npyv_u16 data) { + npyv_u32x2 r; + const __m128i z = _mm_setzero_si128(); + r.val[0] = _mm_unpacklo_epi16(data, z); + r.val[1] = _mm_unpackhi_epi16(data, z); + return r; +} + +// round to nearest integer (assuming even) +#define npyv_round_s32_f32 _mm_cvtps_epi32 +NPY_FINLINE npyv_s32 npyv_round_s32_f64(npyv_f64 a, npyv_f64 b) +{ + __m128i lo = _mm_cvtpd_epi32(a), hi = _mm_cvtpd_epi32(b); + return _mm_unpacklo_epi64(lo, hi); +} + +#endif // _NPY_SIMD_SSE_CVT_H diff --git a/numpy/core/src/common/simd/sse/math.h b/numpy/core/src/common/simd/sse/math.h new file mode 100644 index 000000000000..5daf7711e416 --- /dev/null +++ b/numpy/core/src/common/simd/sse/math.h @@ -0,0 +1,205 @@ +#ifndef NPY_SIMD + #error "Not a standalone header" +#endif + +#ifndef _NPY_SIMD_SSE_MATH_H +#define _NPY_SIMD_SSE_MATH_H +/*************************** + * Elementary + ***************************/ +// Square root +#define npyv_sqrt_f32 _mm_sqrt_ps +#define npyv_sqrt_f64 _mm_sqrt_pd + +// Reciprocal +NPY_FINLINE npyv_f32 npyv_recip_f32(npyv_f32 a) +{ return _mm_div_ps(_mm_set1_ps(1.0f), a); } +NPY_FINLINE npyv_f64 npyv_recip_f64(npyv_f64 a) +{ return _mm_div_pd(_mm_set1_pd(1.0), a); } + +// Absolute +NPY_FINLINE npyv_f32 npyv_abs_f32(npyv_f32 a) +{ + return _mm_and_ps( + a, _mm_castsi128_ps(_mm_set1_epi32(0x7fffffff)) + ); +} +NPY_FINLINE npyv_f64 npyv_abs_f64(npyv_f64 a) +{ + return _mm_and_pd( + a, _mm_castsi128_pd(npyv_setall_s64(0x7fffffffffffffffLL)) + ); +} + +// Square +NPY_FINLINE npyv_f32 npyv_square_f32(npyv_f32 a) +{ return _mm_mul_ps(a, a); } +NPY_FINLINE npyv_f64 npyv_square_f64(npyv_f64 a) +{ return _mm_mul_pd(a, a); } + +// Maximum, natively mapping with no guarantees to handle NaN. +#define npyv_max_f32 _mm_max_ps +#define npyv_max_f64 _mm_max_pd +// Maximum, supports IEEE floating-point arithmetic (IEC 60559), +// - If one of the two vectors contains NaN, the equivalent element of the other vector is set +// - Only if both corresponded elements are NaN, NaN is set. +NPY_FINLINE npyv_f32 npyv_maxp_f32(npyv_f32 a, npyv_f32 b) +{ + __m128 nn = _mm_cmpord_ps(b, b); + __m128 max = _mm_max_ps(a, b); + return npyv_select_f32(_mm_castps_si128(nn), max, a); +} +NPY_FINLINE npyv_f64 npyv_maxp_f64(npyv_f64 a, npyv_f64 b) +{ + __m128d nn = _mm_cmpord_pd(b, b); + __m128d max = _mm_max_pd(a, b); + return npyv_select_f64(_mm_castpd_si128(nn), max, a); +} +// Maximum, integer operations +#ifdef NPY_HAVE_SSE41 + #define npyv_max_s8 _mm_max_epi8 + #define npyv_max_u16 _mm_max_epu16 + #define npyv_max_u32 _mm_max_epu32 + #define npyv_max_s32 _mm_max_epi32 +#else + NPY_FINLINE npyv_s8 npyv_max_s8(npyv_s8 a, npyv_s8 b) + { + return npyv_select_s8(npyv_cmpgt_s8(a, b), a, b); + } + NPY_FINLINE npyv_u16 npyv_max_u16(npyv_u16 a, npyv_u16 b) + { + return npyv_select_u16(npyv_cmpgt_u16(a, b), a, b); + } + NPY_FINLINE npyv_u32 npyv_max_u32(npyv_u32 a, npyv_u32 b) + { + return npyv_select_u32(npyv_cmpgt_u32(a, b), a, b); + } + NPY_FINLINE npyv_s32 npyv_max_s32(npyv_s32 a, npyv_s32 b) + { + return npyv_select_s32(npyv_cmpgt_s32(a, b), a, b); + } +#endif +#define npyv_max_u8 _mm_max_epu8 +#define npyv_max_s16 _mm_max_epi16 +NPY_FINLINE npyv_u64 npyv_max_u64(npyv_u64 a, npyv_u64 b) +{ + return npyv_select_u64(npyv_cmpgt_u64(a, b), a, b); +} +NPY_FINLINE npyv_s64 npyv_max_s64(npyv_s64 a, npyv_s64 b) +{ + return npyv_select_s64(npyv_cmpgt_s64(a, b), a, b); +} + +// Minimum, natively mapping with no guarantees to handle NaN. +#define npyv_min_f32 _mm_min_ps +#define npyv_min_f64 _mm_min_pd +// Minimum, supports IEEE floating-point arithmetic (IEC 60559), +// - If one of the two vectors contains NaN, the equivalent element of the other vector is set +// - Only if both corresponded elements are NaN, NaN is set. +NPY_FINLINE npyv_f32 npyv_minp_f32(npyv_f32 a, npyv_f32 b) +{ + __m128 nn = _mm_cmpord_ps(b, b); + __m128 min = _mm_min_ps(a, b); + return npyv_select_f32(_mm_castps_si128(nn), min, a); +} +NPY_FINLINE npyv_f64 npyv_minp_f64(npyv_f64 a, npyv_f64 b) +{ + __m128d nn = _mm_cmpord_pd(b, b); + __m128d min = _mm_min_pd(a, b); + return npyv_select_f64(_mm_castpd_si128(nn), min, a); +} +// Minimum, integer operations +#ifdef NPY_HAVE_SSE41 + #define npyv_min_s8 _mm_min_epi8 + #define npyv_min_u16 _mm_min_epu16 + #define npyv_min_u32 _mm_min_epu32 + #define npyv_min_s32 _mm_min_epi32 +#else + NPY_FINLINE npyv_s8 npyv_min_s8(npyv_s8 a, npyv_s8 b) + { + return npyv_select_s8(npyv_cmplt_s8(a, b), a, b); + } + NPY_FINLINE npyv_u16 npyv_min_u16(npyv_u16 a, npyv_u16 b) + { + return npyv_select_u16(npyv_cmplt_u16(a, b), a, b); + } + NPY_FINLINE npyv_u32 npyv_min_u32(npyv_u32 a, npyv_u32 b) + { + return npyv_select_u32(npyv_cmplt_u32(a, b), a, b); + } + NPY_FINLINE npyv_s32 npyv_min_s32(npyv_s32 a, npyv_s32 b) + { + return npyv_select_s32(npyv_cmplt_s32(a, b), a, b); + } +#endif +#define npyv_min_u8 _mm_min_epu8 +#define npyv_min_s16 _mm_min_epi16 +NPY_FINLINE npyv_u64 npyv_min_u64(npyv_u64 a, npyv_u64 b) +{ + return npyv_select_u64(npyv_cmplt_u64(a, b), a, b); +} +NPY_FINLINE npyv_s64 npyv_min_s64(npyv_s64 a, npyv_s64 b) +{ + return npyv_select_s64(npyv_cmplt_s64(a, b), a, b); +} + +// ceil +#ifdef NPY_HAVE_SSE41 + #define npyv_ceil_f32 _mm_ceil_ps + #define npyv_ceil_f64 _mm_ceil_pd +#else + NPY_FINLINE npyv_f32 npyv_ceil_f32(npyv_f32 a) + { + const npyv_f32 szero = _mm_set1_ps(-0.0f); + const npyv_f32 one = _mm_set1_ps(1.0f); + npyv_s32 roundi = _mm_cvttps_epi32(a); + npyv_f32 round = _mm_cvtepi32_ps(roundi); + npyv_f32 ceil = _mm_add_ps(round, _mm_and_ps(_mm_cmplt_ps(round, a), one)); + // respect signed zero, e.g. -0.5 -> -0.0 + npyv_f32 rzero = _mm_or_ps(ceil, _mm_and_ps(a, szero)); + // if overflow return a + return npyv_select_f32(_mm_cmpeq_epi32(roundi, _mm_castps_si128(szero)), a, rzero); + } + NPY_FINLINE npyv_f64 npyv_ceil_f64(npyv_f64 a) + { + const npyv_f64 szero = _mm_set1_pd(-0.0); + const npyv_f64 one = _mm_set1_pd(1.0); + const npyv_f64 two_power_52 = _mm_set1_pd(0x10000000000000); + npyv_f64 sign_two52 = _mm_or_pd(two_power_52, _mm_and_pd(a, szero)); + // round by add magic number 2^52 + npyv_f64 round = _mm_sub_pd(_mm_add_pd(a, sign_two52), sign_two52); + npyv_f64 ceil = _mm_add_pd(round, _mm_and_pd(_mm_cmplt_pd(round, a), one)); + // respect signed zero, e.g. -0.5 -> -0.0 + return _mm_or_pd(ceil, _mm_and_pd(a, szero)); + } +#endif + +// trunc +#ifdef NPY_HAVE_SSE41 + #define npyv_trunc_f32(A) _mm_round_ps(A, _MM_FROUND_TO_ZERO) + #define npyv_trunc_f64(A) _mm_round_pd(A, _MM_FROUND_TO_ZERO) +#else + NPY_FINLINE npyv_f32 npyv_trunc_f32(npyv_f32 a) + { + const npyv_f32 szero = _mm_set1_ps(-0.0f); + npyv_s32 roundi = _mm_cvttps_epi32(a); + npyv_f32 trunc = _mm_cvtepi32_ps(roundi); + // respect signed zero, e.g. -0.5 -> -0.0 + npyv_f32 rzero = _mm_or_ps(trunc, _mm_and_ps(a, szero)); + // if overflow return a + return npyv_select_f32(_mm_cmpeq_epi32(roundi, _mm_castps_si128(szero)), a, rzero); + } + NPY_FINLINE npyv_f64 npyv_trunc_f64(npyv_f64 a) + { + const npyv_f64 szero = _mm_set1_pd(-0.0); + const npyv_f64 one = _mm_set1_pd(1.0); + const npyv_f64 two_power_52 = _mm_set1_pd(0x10000000000000); + npyv_f64 abs_a = npyv_abs_f64(a); + // round by add magic number 2^52 + npyv_f64 abs_round = _mm_sub_pd(_mm_add_pd(abs_a, two_power_52), two_power_52); + npyv_f64 subtrahend = _mm_and_pd(_mm_cmpgt_pd(abs_round, abs_a), one); + return _mm_or_pd(_mm_sub_pd(abs_round, subtrahend), _mm_and_pd(a, szero)); + } +#endif + +#endif // _NPY_SIMD_SSE_MATH_H diff --git a/numpy/core/src/common/simd/sse/memory.h b/numpy/core/src/common/simd/sse/memory.h new file mode 100644 index 000000000000..1074c3b02efe --- /dev/null +++ b/numpy/core/src/common/simd/sse/memory.h @@ -0,0 +1,498 @@ +#ifndef NPY_SIMD + #error "Not a standalone header" +#endif + +#ifndef _NPY_SIMD_SSE_MEMORY_H +#define _NPY_SIMD_SSE_MEMORY_H + +#include "misc.h" + +/*************************** + * load/store + ***************************/ +// stream load +#ifdef NPY_HAVE_SSE41 + #define npyv__loads(PTR) _mm_stream_load_si128((__m128i *)(PTR)) +#else + #define npyv__loads(PTR) _mm_load_si128((const __m128i *)(PTR)) +#endif +#define NPYV_IMPL_SSE_MEM_INT(CTYPE, SFX) \ + NPY_FINLINE npyv_##SFX npyv_load_##SFX(const CTYPE *ptr) \ + { return _mm_loadu_si128((const __m128i*)ptr); } \ + NPY_FINLINE npyv_##SFX npyv_loada_##SFX(const CTYPE *ptr) \ + { return _mm_load_si128((const __m128i*)ptr); } \ + NPY_FINLINE npyv_##SFX npyv_loads_##SFX(const CTYPE *ptr) \ + { return npyv__loads(ptr); } \ + NPY_FINLINE npyv_##SFX npyv_loadl_##SFX(const CTYPE *ptr) \ + { return _mm_loadl_epi64((const __m128i*)ptr); } \ + NPY_FINLINE void npyv_store_##SFX(CTYPE *ptr, npyv_##SFX vec) \ + { _mm_storeu_si128((__m128i*)ptr, vec); } \ + NPY_FINLINE void npyv_storea_##SFX(CTYPE *ptr, npyv_##SFX vec) \ + { _mm_store_si128((__m128i*)ptr, vec); } \ + NPY_FINLINE void npyv_stores_##SFX(CTYPE *ptr, npyv_##SFX vec) \ + { _mm_stream_si128((__m128i*)ptr, vec); } \ + NPY_FINLINE void npyv_storel_##SFX(CTYPE *ptr, npyv_##SFX vec) \ + { _mm_storel_epi64((__m128i *)ptr, vec); } \ + NPY_FINLINE void npyv_storeh_##SFX(CTYPE *ptr, npyv_##SFX vec) \ + { _mm_storel_epi64((__m128i *)ptr, _mm_unpackhi_epi64(vec, vec)); } + +NPYV_IMPL_SSE_MEM_INT(npy_uint8, u8) +NPYV_IMPL_SSE_MEM_INT(npy_int8, s8) +NPYV_IMPL_SSE_MEM_INT(npy_uint16, u16) +NPYV_IMPL_SSE_MEM_INT(npy_int16, s16) +NPYV_IMPL_SSE_MEM_INT(npy_uint32, u32) +NPYV_IMPL_SSE_MEM_INT(npy_int32, s32) +NPYV_IMPL_SSE_MEM_INT(npy_uint64, u64) +NPYV_IMPL_SSE_MEM_INT(npy_int64, s64) + +// unaligned load +#define npyv_load_f32 _mm_loadu_ps +#define npyv_load_f64 _mm_loadu_pd +// aligned load +#define npyv_loada_f32 _mm_load_ps +#define npyv_loada_f64 _mm_load_pd +// load lower part +#define npyv_loadl_f32(PTR) _mm_castsi128_ps(npyv_loadl_u32((const npy_uint32*)(PTR))) +#define npyv_loadl_f64(PTR) _mm_castsi128_pd(npyv_loadl_u32((const npy_uint32*)(PTR))) +// stream load +#define npyv_loads_f32(PTR) _mm_castsi128_ps(npyv__loads(PTR)) +#define npyv_loads_f64(PTR) _mm_castsi128_pd(npyv__loads(PTR)) +// unaligned store +#define npyv_store_f32 _mm_storeu_ps +#define npyv_store_f64 _mm_storeu_pd +// aligned store +#define npyv_storea_f32 _mm_store_ps +#define npyv_storea_f64 _mm_store_pd +// stream store +#define npyv_stores_f32 _mm_stream_ps +#define npyv_stores_f64 _mm_stream_pd +// store lower part +#define npyv_storel_f32(PTR, VEC) _mm_storel_epi64((__m128i*)(PTR), _mm_castps_si128(VEC)); +#define npyv_storel_f64(PTR, VEC) _mm_storel_epi64((__m128i*)(PTR), _mm_castpd_si128(VEC)); +// store higher part +#define npyv_storeh_f32(PTR, VEC) npyv_storeh_u32((npy_uint32*)(PTR), _mm_castps_si128(VEC)) +#define npyv_storeh_f64(PTR, VEC) npyv_storeh_u32((npy_uint32*)(PTR), _mm_castpd_si128(VEC)) +/*************************** + * Non-contiguous Load + ***************************/ +//// 32 +NPY_FINLINE npyv_s32 npyv_loadn_s32(const npy_int32 *ptr, npy_intp stride) +{ + __m128i a = _mm_cvtsi32_si128(*ptr); +#ifdef NPY_HAVE_SSE41 + a = _mm_insert_epi32(a, ptr[stride], 1); + a = _mm_insert_epi32(a, ptr[stride*2], 2); + a = _mm_insert_epi32(a, ptr[stride*3], 3); +#else + __m128i a1 = _mm_cvtsi32_si128(ptr[stride]); + __m128i a2 = _mm_cvtsi32_si128(ptr[stride*2]); + __m128i a3 = _mm_cvtsi32_si128(ptr[stride*3]); + a = _mm_unpacklo_epi32(a, a1); + a = _mm_unpacklo_epi64(a, _mm_unpacklo_epi32(a2, a3)); +#endif + return a; +} +NPY_FINLINE npyv_u32 npyv_loadn_u32(const npy_uint32 *ptr, npy_intp stride) +{ return npyv_loadn_s32((const npy_int32*)ptr, stride); } +NPY_FINLINE npyv_f32 npyv_loadn_f32(const float *ptr, npy_intp stride) +{ return _mm_castsi128_ps(npyv_loadn_s32((const npy_int32*)ptr, stride)); } +//// 64 +NPY_FINLINE npyv_f64 npyv_loadn_f64(const double *ptr, npy_intp stride) +{ return _mm_loadh_pd(npyv_loadl_f64(ptr), ptr + stride); } +NPY_FINLINE npyv_u64 npyv_loadn_u64(const npy_uint64 *ptr, npy_intp stride) +{ return _mm_castpd_si128(npyv_loadn_f64((const double*)ptr, stride)); } +NPY_FINLINE npyv_s64 npyv_loadn_s64(const npy_int64 *ptr, npy_intp stride) +{ return _mm_castpd_si128(npyv_loadn_f64((const double*)ptr, stride)); } +/*************************** + * Non-contiguous Store + ***************************/ +//// 32 +NPY_FINLINE void npyv_storen_s32(npy_int32 *ptr, npy_intp stride, npyv_s32 a) +{ + ptr[stride * 0] = _mm_cvtsi128_si32(a); +#ifdef NPY_HAVE_SSE41 + ptr[stride * 1] = _mm_extract_epi32(a, 1); + ptr[stride * 2] = _mm_extract_epi32(a, 2); + ptr[stride * 3] = _mm_extract_epi32(a, 3); +#else + ptr[stride * 1] = _mm_cvtsi128_si32(_mm_shuffle_epi32(a, _MM_SHUFFLE(0, 0, 0, 1))); + ptr[stride * 2] = _mm_cvtsi128_si32(_mm_shuffle_epi32(a, _MM_SHUFFLE(0, 0, 0, 2))); + ptr[stride * 3] = _mm_cvtsi128_si32(_mm_shuffle_epi32(a, _MM_SHUFFLE(0, 0, 0, 3))); +#endif +} +NPY_FINLINE void npyv_storen_u32(npy_uint32 *ptr, npy_intp stride, npyv_u32 a) +{ npyv_storen_s32((npy_int32*)ptr, stride, a); } +NPY_FINLINE void npyv_storen_f32(float *ptr, npy_intp stride, npyv_f32 a) +{ npyv_storen_s32((npy_int32*)ptr, stride, _mm_castps_si128(a)); } +//// 64 +NPY_FINLINE void npyv_storen_f64(double *ptr, npy_intp stride, npyv_f64 a) +{ + _mm_storel_pd(ptr, a); + _mm_storeh_pd(ptr + stride, a); +} +NPY_FINLINE void npyv_storen_u64(npy_uint64 *ptr, npy_intp stride, npyv_u64 a) +{ npyv_storen_f64((double*)ptr, stride, _mm_castsi128_pd(a)); } +NPY_FINLINE void npyv_storen_s64(npy_int64 *ptr, npy_intp stride, npyv_s64 a) +{ npyv_storen_f64((double*)ptr, stride, _mm_castsi128_pd(a)); } + +/********************************* + * Partial Load + *********************************/ +#if defined(__clang__) && __clang_major__ > 7 + /** + * Clang >=8 perform aggressive optimization that tends to + * zero the bits of upper half part of vectors even + * when we try to fill it up with certain scalars, + * which my lead to zero division errors. + */ + #define NPYV__CLANG_ZEROUPPER +#endif +//// 32 +NPY_FINLINE npyv_s32 npyv_load_till_s32(const npy_int32 *ptr, npy_uintp nlane, npy_int32 fill) +{ + assert(nlane > 0); +#ifdef NPYV__CLANG_ZEROUPPER + if (nlane > 3) { + return npyv_load_s32(ptr); + } + npy_int32 NPY_DECL_ALIGNED(16) data[4] = {fill, fill, fill, fill}; + for (npy_uint64 i = 0; i < nlane; ++i) { + data[i] = ptr[i]; + } + return npyv_loada_s32(data); +#else + #ifndef NPY_HAVE_SSE41 + const short *wptr = (const short*)ptr; + #endif + const __m128i vfill = npyv_setall_s32(fill); + __m128i a; + switch(nlane) { + case 2: + return _mm_castpd_si128( + _mm_loadl_pd(_mm_castsi128_pd(vfill), (double*)ptr) + ); + #ifdef NPY_HAVE_SSE41 + case 1: + return _mm_insert_epi32(vfill, ptr[0], 0); + case 3: + a = _mm_loadl_epi64((const __m128i*)ptr); + a = _mm_insert_epi32(a, ptr[2], 2); + a = _mm_insert_epi32(a, fill, 3); + return a; + #else + case 1: + a = _mm_insert_epi16(vfill, wptr[0], 0); + return _mm_insert_epi16(a, wptr[1], 1); + case 3: + a = _mm_loadl_epi64((const __m128i*)ptr); + a = _mm_unpacklo_epi64(a, vfill); + a = _mm_insert_epi16(a, wptr[4], 4); + a = _mm_insert_epi16(a, wptr[5], 5); + return a; + #endif // NPY_HAVE_SSE41 + default: + return npyv_load_s32(ptr); + } +#endif +} +// fill zero to rest lanes +NPY_FINLINE npyv_s32 npyv_load_tillz_s32(const npy_int32 *ptr, npy_uintp nlane) +{ + assert(nlane > 0); + switch(nlane) { + case 1: + return _mm_cvtsi32_si128(*ptr); + case 2: + return _mm_loadl_epi64((const __m128i*)ptr); + case 3:; + npyv_s32 a = _mm_loadl_epi64((const __m128i*)ptr); + #ifdef NPY_HAVE_SSE41 + return _mm_insert_epi32(a, ptr[2], 2); + #else + return _mm_unpacklo_epi64(a, _mm_cvtsi32_si128(ptr[2])); + #endif + default: + return npyv_load_s32(ptr); + } +} +//// 64 +NPY_FINLINE npyv_s64 npyv_load_till_s64(const npy_int64 *ptr, npy_uintp nlane, npy_int64 fill) +{ + assert(nlane > 0); +#ifdef NPYV__CLANG_ZEROUPPER + if (nlane <= 2) { + npy_int64 NPY_DECL_ALIGNED(16) data[2] = {fill, fill}; + for (npy_uint64 i = 0; i < nlane; ++i) { + data[i] = ptr[i]; + } + return npyv_loada_s64(data); + } +#else + if (nlane == 1) { + const __m128i vfill = npyv_setall_s64(fill); + return _mm_castpd_si128( + _mm_loadl_pd(_mm_castsi128_pd(vfill), (double*)ptr) + ); + } +#endif + return npyv_load_s64(ptr); +} +// fill zero to rest lanes +NPY_FINLINE npyv_s64 npyv_load_tillz_s64(const npy_int64 *ptr, npy_uintp nlane) +{ + assert(nlane > 0); + if (nlane == 1) { + return _mm_loadl_epi64((const __m128i*)ptr); + } + return npyv_load_s64(ptr); +} +/********************************* + * Non-contiguous partial load + *********************************/ +//// 32 +NPY_FINLINE npyv_s32 +npyv_loadn_till_s32(const npy_int32 *ptr, npy_intp stride, npy_uintp nlane, npy_int32 fill) +{ + assert(nlane > 0); +#ifdef NPYV__CLANG_ZEROUPPER + if (nlane > 3) { + return npyv_loadn_s32(ptr, stride); + } + npy_int32 NPY_DECL_ALIGNED(16) data[4] = {fill, fill, fill, fill}; + for (npy_uint64 i = 0; i < nlane; ++i) { + data[i] = ptr[stride*i]; + } + return npyv_loada_s32(data); +#else + __m128i vfill = npyv_setall_s32(fill); + #ifndef NPY_HAVE_SSE41 + const short *wptr = (const short*)ptr; + #endif + switch(nlane) { + #ifdef NPY_HAVE_SSE41 + case 3: + vfill = _mm_insert_epi32(vfill, ptr[stride*2], 2); + case 2: + vfill = _mm_insert_epi32(vfill, ptr[stride], 1); + case 1: + vfill = _mm_insert_epi32(vfill, ptr[0], 0); + break; + #else + case 3: + vfill = _mm_unpacklo_epi32(_mm_cvtsi32_si128(ptr[stride*2]), vfill); + case 2: + vfill = _mm_unpacklo_epi64(_mm_unpacklo_epi32( + _mm_cvtsi32_si128(*ptr), _mm_cvtsi32_si128(ptr[stride]) + ), vfill); + break; + case 1: + vfill = _mm_insert_epi16(vfill, wptr[0], 0); + vfill = _mm_insert_epi16(vfill, wptr[1], 1); + break; + #endif // NPY_HAVE_SSE41 + default: + return npyv_loadn_s32(ptr, stride); + } // switch + return vfill; +#endif +} +// fill zero to rest lanes +NPY_FINLINE npyv_s32 +npyv_loadn_tillz_s32(const npy_int32 *ptr, npy_intp stride, npy_uintp nlane) +{ + assert(nlane > 0); + switch(nlane) { + case 1: + return _mm_cvtsi32_si128(ptr[0]); + case 2:; + npyv_s32 a = _mm_cvtsi32_si128(ptr[0]); +#ifdef NPY_HAVE_SSE41 + return _mm_insert_epi32(a, ptr[stride], 1); +#else + return _mm_unpacklo_epi32(a, _mm_cvtsi32_si128(ptr[stride])); +#endif // NPY_HAVE_SSE41 + case 3:; + a = _mm_cvtsi32_si128(ptr[0]); +#ifdef NPY_HAVE_SSE41 + a = _mm_insert_epi32(a, ptr[stride], 1); + a = _mm_insert_epi32(a, ptr[stride*2], 2); + return a; +#else + a = _mm_unpacklo_epi32(a, _mm_cvtsi32_si128(ptr[stride])); + a = _mm_unpacklo_epi64(a, _mm_cvtsi32_si128(ptr[stride*2])); + return a; +#endif // NPY_HAVE_SSE41 + default: + return npyv_loadn_s32(ptr, stride); + } +} +//// 64 +NPY_FINLINE npyv_s64 +npyv_loadn_till_s64(const npy_int64 *ptr, npy_intp stride, npy_uintp nlane, npy_int64 fill) +{ + assert(nlane > 0); +#ifdef NPYV__CLANG_ZEROUPPER + if (nlane <= 2) { + npy_int64 NPY_DECL_ALIGNED(16) data[2] = {fill, fill}; + for (npy_uint64 i = 0; i < nlane; ++i) { + data[i] = ptr[i*stride]; + } + return npyv_loada_s64(data); + } +#else + if (nlane == 1) { + const __m128i vfill = npyv_setall_s64(fill); + return _mm_castpd_si128( + _mm_loadl_pd(_mm_castsi128_pd(vfill), (double*)ptr) + ); + } +#endif + return npyv_loadn_s64(ptr, stride); +} +// fill zero to rest lanes +NPY_FINLINE npyv_s64 npyv_loadn_tillz_s64(const npy_int64 *ptr, npy_intp stride, npy_uintp nlane) +{ + assert(nlane > 0); + if (nlane == 1) { + return _mm_loadl_epi64((const __m128i*)ptr); + } + return npyv_loadn_s64(ptr, stride); +} +/********************************* + * Partial store + *********************************/ +//// 32 +NPY_FINLINE void npyv_store_till_s32(npy_int32 *ptr, npy_uintp nlane, npyv_s32 a) +{ + assert(nlane > 0); + switch(nlane) { + case 1: + *ptr = _mm_cvtsi128_si32(a); + break; + case 2: + _mm_storel_epi64((__m128i *)ptr, a); + break; + case 3: + _mm_storel_epi64((__m128i *)ptr, a); + #ifdef NPY_HAVE_SSE41 + ptr[2] = _mm_extract_epi32(a, 2); + #else + ptr[2] = _mm_cvtsi128_si32(_mm_shuffle_epi32(a, _MM_SHUFFLE(0, 0, 0, 2))); + #endif + break; + default: + npyv_store_s32(ptr, a); + } +} +//// 64 +NPY_FINLINE void npyv_store_till_s64(npy_int64 *ptr, npy_uintp nlane, npyv_s64 a) +{ + assert(nlane > 0); + if (nlane == 1) { + _mm_storel_epi64((__m128i *)ptr, a); + return; + } + npyv_store_s64(ptr, a); +} +/********************************* + * Non-contiguous partial store + *********************************/ +//// 32 +NPY_FINLINE void npyv_storen_till_s32(npy_int32 *ptr, npy_intp stride, npy_uintp nlane, npyv_s32 a) +{ + assert(nlane > 0); + switch(nlane) { +#ifdef NPY_HAVE_SSE41 + default: + ptr[stride*3] = _mm_extract_epi32(a, 3); + case 3: + ptr[stride*2] = _mm_extract_epi32(a, 2); + case 2: + ptr[stride*1] = _mm_extract_epi32(a, 1); +#else + default: + ptr[stride*3] = _mm_cvtsi128_si32(_mm_shuffle_epi32(a, _MM_SHUFFLE(0, 0, 0, 3))); + case 3: + ptr[stride*2] = _mm_cvtsi128_si32(_mm_shuffle_epi32(a, _MM_SHUFFLE(0, 0, 0, 2))); + case 2: + ptr[stride*1] = _mm_cvtsi128_si32(_mm_shuffle_epi32(a, _MM_SHUFFLE(0, 0, 0, 1))); +#endif + case 1: + ptr[stride*0] = _mm_cvtsi128_si32(a); + break; + } +} +//// 64 +NPY_FINLINE void npyv_storen_till_s64(npy_int64 *ptr, npy_intp stride, npy_uintp nlane, npyv_s64 a) +{ + assert(nlane > 0); + if (nlane == 1) { + _mm_storel_epi64((__m128i *)ptr, a); + return; + } + npyv_storen_s64(ptr, stride, a); +} +/***************************************************************** + * Implement partial load/store for u32/f32/u64/f64... via casting + *****************************************************************/ +#define NPYV_IMPL_SSE_REST_PARTIAL_TYPES(F_SFX, T_SFX) \ + NPY_FINLINE npyv_##F_SFX npyv_load_till_##F_SFX \ + (const npyv_lanetype_##F_SFX *ptr, npy_uintp nlane, npyv_lanetype_##F_SFX fill) \ + { \ + union { \ + npyv_lanetype_##F_SFX from_##F_SFX; \ + npyv_lanetype_##T_SFX to_##T_SFX; \ + } pun = {.from_##F_SFX = fill}; \ + return npyv_reinterpret_##F_SFX##_##T_SFX(npyv_load_till_##T_SFX( \ + (const npyv_lanetype_##T_SFX *)ptr, nlane, pun.to_##T_SFX \ + )); \ + } \ + NPY_FINLINE npyv_##F_SFX npyv_loadn_till_##F_SFX \ + (const npyv_lanetype_##F_SFX *ptr, npy_intp stride, npy_uintp nlane, \ + npyv_lanetype_##F_SFX fill) \ + { \ + union { \ + npyv_lanetype_##F_SFX from_##F_SFX; \ + npyv_lanetype_##T_SFX to_##T_SFX; \ + } pun = {.from_##F_SFX = fill}; \ + return npyv_reinterpret_##F_SFX##_##T_SFX(npyv_loadn_till_##T_SFX( \ + (const npyv_lanetype_##T_SFX *)ptr, stride, nlane, pun.to_##T_SFX \ + )); \ + } \ + NPY_FINLINE npyv_##F_SFX npyv_load_tillz_##F_SFX \ + (const npyv_lanetype_##F_SFX *ptr, npy_uintp nlane) \ + { \ + return npyv_reinterpret_##F_SFX##_##T_SFX(npyv_load_tillz_##T_SFX( \ + (const npyv_lanetype_##T_SFX *)ptr, nlane \ + )); \ + } \ + NPY_FINLINE npyv_##F_SFX npyv_loadn_tillz_##F_SFX \ + (const npyv_lanetype_##F_SFX *ptr, npy_intp stride, npy_uintp nlane) \ + { \ + return npyv_reinterpret_##F_SFX##_##T_SFX(npyv_loadn_tillz_##T_SFX( \ + (const npyv_lanetype_##T_SFX *)ptr, stride, nlane \ + )); \ + } \ + NPY_FINLINE void npyv_store_till_##F_SFX \ + (npyv_lanetype_##F_SFX *ptr, npy_uintp nlane, npyv_##F_SFX a) \ + { \ + npyv_store_till_##T_SFX( \ + (npyv_lanetype_##T_SFX *)ptr, nlane, \ + npyv_reinterpret_##T_SFX##_##F_SFX(a) \ + ); \ + } \ + NPY_FINLINE void npyv_storen_till_##F_SFX \ + (npyv_lanetype_##F_SFX *ptr, npy_intp stride, npy_uintp nlane, npyv_##F_SFX a) \ + { \ + npyv_storen_till_##T_SFX( \ + (npyv_lanetype_##T_SFX *)ptr, stride, nlane, \ + npyv_reinterpret_##T_SFX##_##F_SFX(a) \ + ); \ + } + +NPYV_IMPL_SSE_REST_PARTIAL_TYPES(u32, s32) +NPYV_IMPL_SSE_REST_PARTIAL_TYPES(f32, s32) +NPYV_IMPL_SSE_REST_PARTIAL_TYPES(u64, s64) +NPYV_IMPL_SSE_REST_PARTIAL_TYPES(f64, s64) + +#endif // _NPY_SIMD_SSE_MEMORY_H diff --git a/numpy/core/src/common/simd/sse/misc.h b/numpy/core/src/common/simd/sse/misc.h new file mode 100644 index 000000000000..7d13fbf555c5 --- /dev/null +++ b/numpy/core/src/common/simd/sse/misc.h @@ -0,0 +1,246 @@ +#ifndef NPY_SIMD + #error "Not a standalone header" +#endif + +#ifndef _NPY_SIMD_SSE_MISC_H +#define _NPY_SIMD_SSE_MISC_H + +// vector with zero lanes +#define npyv_zero_u8 _mm_setzero_si128 +#define npyv_zero_s8 _mm_setzero_si128 +#define npyv_zero_u16 _mm_setzero_si128 +#define npyv_zero_s16 _mm_setzero_si128 +#define npyv_zero_u32 _mm_setzero_si128 +#define npyv_zero_s32 _mm_setzero_si128 +#define npyv_zero_u64 _mm_setzero_si128 +#define npyv_zero_s64 _mm_setzero_si128 +#define npyv_zero_f32 _mm_setzero_ps +#define npyv_zero_f64 _mm_setzero_pd + +// vector with a specific value set to all lanes +#define npyv_setall_u8(VAL) _mm_set1_epi8((char)(VAL)) +#define npyv_setall_s8(VAL) _mm_set1_epi8((char)(VAL)) +#define npyv_setall_u16(VAL) _mm_set1_epi16((short)(VAL)) +#define npyv_setall_s16(VAL) _mm_set1_epi16((short)(VAL)) +#define npyv_setall_u32(VAL) _mm_set1_epi32((int)(VAL)) +#define npyv_setall_s32(VAL) _mm_set1_epi32((int)(VAL)) +#define npyv_setall_f32 _mm_set1_ps +#define npyv_setall_f64 _mm_set1_pd + +NPY_FINLINE __m128i npyv__setr_epi64(npy_int64 i0, npy_int64 i1); + +NPY_FINLINE npyv_u64 npyv_setall_u64(npy_uint64 a) +{ +#if defined(_MSC_VER) && defined(_M_IX86) + return npyv__setr_epi64((npy_int64)a, (npy_int64)a); +#else + return _mm_set1_epi64x((npy_int64)a); +#endif +} +NPY_FINLINE npyv_s64 npyv_setall_s64(npy_int64 a) +{ +#if defined(_MSC_VER) && defined(_M_IX86) + return npyv__setr_epi64(a, a); +#else + return _mm_set1_epi64x((npy_int64)a); +#endif +} + +/** + * vector with specific values set to each lane and + * set a specific value to all remained lanes + * + * Args that generated by NPYV__SET_FILL_* not going to expand if + * _mm_setr_* are defined as macros. + */ +NPY_FINLINE __m128i npyv__setr_epi8( + char i0, char i1, char i2, char i3, char i4, char i5, char i6, char i7, + char i8, char i9, char i10, char i11, char i12, char i13, char i14, char i15) +{ + return _mm_setr_epi8(i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15); +} +NPY_FINLINE __m128i npyv__setr_epi16(short i0, short i1, short i2, short i3, short i4, short i5, + short i6, short i7) +{ + return _mm_setr_epi16(i0, i1, i2, i3, i4, i5, i6, i7); +} +NPY_FINLINE __m128i npyv__setr_epi32(int i0, int i1, int i2, int i3) +{ + return _mm_setr_epi32(i0, i1, i2, i3); +} +NPY_FINLINE __m128i npyv__setr_epi64(npy_int64 i0, npy_int64 i1) +{ +#if defined(_MSC_VER) && defined(_M_IX86) + return _mm_setr_epi32((int)i0, (int)(i0 >> 32), (int)i1, (int)(i1 >> 32)); +#else + return _mm_set_epi64x(i1, i0); +#endif +} +NPY_FINLINE __m128 npyv__setr_ps(float i0, float i1, float i2, float i3) +{ + return _mm_setr_ps(i0, i1, i2, i3); +} +NPY_FINLINE __m128d npyv__setr_pd(double i0, double i1) +{ + return _mm_setr_pd(i0, i1); +} +#define npyv_setf_u8(FILL, ...) npyv__setr_epi8(NPYV__SET_FILL_16(char, FILL, __VA_ARGS__)) +#define npyv_setf_s8(FILL, ...) npyv__setr_epi8(NPYV__SET_FILL_16(char, FILL, __VA_ARGS__)) +#define npyv_setf_u16(FILL, ...) npyv__setr_epi16(NPYV__SET_FILL_8(short, FILL, __VA_ARGS__)) +#define npyv_setf_s16(FILL, ...) npyv__setr_epi16(NPYV__SET_FILL_8(short, FILL, __VA_ARGS__)) +#define npyv_setf_u32(FILL, ...) npyv__setr_epi32(NPYV__SET_FILL_4(int, FILL, __VA_ARGS__)) +#define npyv_setf_s32(FILL, ...) npyv__setr_epi32(NPYV__SET_FILL_4(int, FILL, __VA_ARGS__)) +#define npyv_setf_u64(FILL, ...) npyv__setr_epi64(NPYV__SET_FILL_2(npy_int64, FILL, __VA_ARGS__)) +#define npyv_setf_s64(FILL, ...) npyv__setr_epi64(NPYV__SET_FILL_2(npy_int64, FILL, __VA_ARGS__)) +#define npyv_setf_f32(FILL, ...) npyv__setr_ps(NPYV__SET_FILL_4(float, FILL, __VA_ARGS__)) +#define npyv_setf_f64(FILL, ...) npyv__setr_pd(NPYV__SET_FILL_2(double, FILL, __VA_ARGS__)) + +// vector with specific values set to each lane and +// set zero to all remained lanes +#define npyv_set_u8(...) npyv_setf_u8(0, __VA_ARGS__) +#define npyv_set_s8(...) npyv_setf_s8(0, __VA_ARGS__) +#define npyv_set_u16(...) npyv_setf_u16(0, __VA_ARGS__) +#define npyv_set_s16(...) npyv_setf_s16(0, __VA_ARGS__) +#define npyv_set_u32(...) npyv_setf_u32(0, __VA_ARGS__) +#define npyv_set_s32(...) npyv_setf_s32(0, __VA_ARGS__) +#define npyv_set_u64(...) npyv_setf_u64(0, __VA_ARGS__) +#define npyv_set_s64(...) npyv_setf_s64(0, __VA_ARGS__) +#define npyv_set_f32(...) npyv_setf_f32(0, __VA_ARGS__) +#define npyv_set_f64(...) npyv_setf_f64(0, __VA_ARGS__) + +// Per lane select +#ifdef NPY_HAVE_SSE41 + #define npyv_select_u8(MASK, A, B) _mm_blendv_epi8(B, A, MASK) + #define npyv_select_f32(MASK, A, B) _mm_blendv_ps(B, A, _mm_castsi128_ps(MASK)) + #define npyv_select_f64(MASK, A, B) _mm_blendv_pd(B, A, _mm_castsi128_pd(MASK)) +#else + NPY_FINLINE __m128i npyv_select_u8(__m128i mask, __m128i a, __m128i b) + { return _mm_xor_si128(b, _mm_and_si128(_mm_xor_si128(b, a), mask)); } + NPY_FINLINE __m128 npyv_select_f32(__m128i mask, __m128 a, __m128 b) + { return _mm_xor_ps(b, _mm_and_ps(_mm_xor_ps(b, a), _mm_castsi128_ps(mask))); } + NPY_FINLINE __m128d npyv_select_f64(__m128i mask, __m128d a, __m128d b) + { return _mm_xor_pd(b, _mm_and_pd(_mm_xor_pd(b, a), _mm_castsi128_pd(mask))); } +#endif +#define npyv_select_s8 npyv_select_u8 +#define npyv_select_u16 npyv_select_u8 +#define npyv_select_s16 npyv_select_u8 +#define npyv_select_u32 npyv_select_u8 +#define npyv_select_s32 npyv_select_u8 +#define npyv_select_u64 npyv_select_u8 +#define npyv_select_s64 npyv_select_u8 + +// Reinterpret +#define npyv_reinterpret_u8_u8(X) X +#define npyv_reinterpret_u8_s8(X) X +#define npyv_reinterpret_u8_u16(X) X +#define npyv_reinterpret_u8_s16(X) X +#define npyv_reinterpret_u8_u32(X) X +#define npyv_reinterpret_u8_s32(X) X +#define npyv_reinterpret_u8_u64(X) X +#define npyv_reinterpret_u8_s64(X) X +#define npyv_reinterpret_u8_f32 _mm_castps_si128 +#define npyv_reinterpret_u8_f64 _mm_castpd_si128 + +#define npyv_reinterpret_s8_s8(X) X +#define npyv_reinterpret_s8_u8(X) X +#define npyv_reinterpret_s8_u16(X) X +#define npyv_reinterpret_s8_s16(X) X +#define npyv_reinterpret_s8_u32(X) X +#define npyv_reinterpret_s8_s32(X) X +#define npyv_reinterpret_s8_u64(X) X +#define npyv_reinterpret_s8_s64(X) X +#define npyv_reinterpret_s8_f32 _mm_castps_si128 +#define npyv_reinterpret_s8_f64 _mm_castpd_si128 + +#define npyv_reinterpret_u16_u16(X) X +#define npyv_reinterpret_u16_u8(X) X +#define npyv_reinterpret_u16_s8(X) X +#define npyv_reinterpret_u16_s16(X) X +#define npyv_reinterpret_u16_u32(X) X +#define npyv_reinterpret_u16_s32(X) X +#define npyv_reinterpret_u16_u64(X) X +#define npyv_reinterpret_u16_s64(X) X +#define npyv_reinterpret_u16_f32 _mm_castps_si128 +#define npyv_reinterpret_u16_f64 _mm_castpd_si128 + +#define npyv_reinterpret_s16_s16(X) X +#define npyv_reinterpret_s16_u8(X) X +#define npyv_reinterpret_s16_s8(X) X +#define npyv_reinterpret_s16_u16(X) X +#define npyv_reinterpret_s16_u32(X) X +#define npyv_reinterpret_s16_s32(X) X +#define npyv_reinterpret_s16_u64(X) X +#define npyv_reinterpret_s16_s64(X) X +#define npyv_reinterpret_s16_f32 _mm_castps_si128 +#define npyv_reinterpret_s16_f64 _mm_castpd_si128 + +#define npyv_reinterpret_u32_u32(X) X +#define npyv_reinterpret_u32_u8(X) X +#define npyv_reinterpret_u32_s8(X) X +#define npyv_reinterpret_u32_u16(X) X +#define npyv_reinterpret_u32_s16(X) X +#define npyv_reinterpret_u32_s32(X) X +#define npyv_reinterpret_u32_u64(X) X +#define npyv_reinterpret_u32_s64(X) X +#define npyv_reinterpret_u32_f32 _mm_castps_si128 +#define npyv_reinterpret_u32_f64 _mm_castpd_si128 + +#define npyv_reinterpret_s32_s32(X) X +#define npyv_reinterpret_s32_u8(X) X +#define npyv_reinterpret_s32_s8(X) X +#define npyv_reinterpret_s32_u16(X) X +#define npyv_reinterpret_s32_s16(X) X +#define npyv_reinterpret_s32_u32(X) X +#define npyv_reinterpret_s32_u64(X) X +#define npyv_reinterpret_s32_s64(X) X +#define npyv_reinterpret_s32_f32 _mm_castps_si128 +#define npyv_reinterpret_s32_f64 _mm_castpd_si128 + +#define npyv_reinterpret_u64_u64(X) X +#define npyv_reinterpret_u64_u8(X) X +#define npyv_reinterpret_u64_s8(X) X +#define npyv_reinterpret_u64_u16(X) X +#define npyv_reinterpret_u64_s16(X) X +#define npyv_reinterpret_u64_u32(X) X +#define npyv_reinterpret_u64_s32(X) X +#define npyv_reinterpret_u64_s64(X) X +#define npyv_reinterpret_u64_f32 _mm_castps_si128 +#define npyv_reinterpret_u64_f64 _mm_castpd_si128 + +#define npyv_reinterpret_s64_s64(X) X +#define npyv_reinterpret_s64_u8(X) X +#define npyv_reinterpret_s64_s8(X) X +#define npyv_reinterpret_s64_u16(X) X +#define npyv_reinterpret_s64_s16(X) X +#define npyv_reinterpret_s64_u32(X) X +#define npyv_reinterpret_s64_s32(X) X +#define npyv_reinterpret_s64_u64(X) X +#define npyv_reinterpret_s64_f32 _mm_castps_si128 +#define npyv_reinterpret_s64_f64 _mm_castpd_si128 + +#define npyv_reinterpret_f32_f32(X) X +#define npyv_reinterpret_f32_u8 _mm_castsi128_ps +#define npyv_reinterpret_f32_s8 _mm_castsi128_ps +#define npyv_reinterpret_f32_u16 _mm_castsi128_ps +#define npyv_reinterpret_f32_s16 _mm_castsi128_ps +#define npyv_reinterpret_f32_u32 _mm_castsi128_ps +#define npyv_reinterpret_f32_s32 _mm_castsi128_ps +#define npyv_reinterpret_f32_u64 _mm_castsi128_ps +#define npyv_reinterpret_f32_s64 _mm_castsi128_ps +#define npyv_reinterpret_f32_f64 _mm_castpd_ps + +#define npyv_reinterpret_f64_f64(X) X +#define npyv_reinterpret_f64_u8 _mm_castsi128_pd +#define npyv_reinterpret_f64_s8 _mm_castsi128_pd +#define npyv_reinterpret_f64_u16 _mm_castsi128_pd +#define npyv_reinterpret_f64_s16 _mm_castsi128_pd +#define npyv_reinterpret_f64_u32 _mm_castsi128_pd +#define npyv_reinterpret_f64_s32 _mm_castsi128_pd +#define npyv_reinterpret_f64_u64 _mm_castsi128_pd +#define npyv_reinterpret_f64_s64 _mm_castsi128_pd +#define npyv_reinterpret_f64_f32 _mm_castps_pd + +// Only required by AVX2/AVX512 +#define npyv_cleanup() ((void)0) + +#endif // _NPY_SIMD_SSE_MISC_H diff --git a/numpy/core/src/common/simd/sse/operators.h b/numpy/core/src/common/simd/sse/operators.h new file mode 100644 index 000000000000..51c84fb4e9d9 --- /dev/null +++ b/numpy/core/src/common/simd/sse/operators.h @@ -0,0 +1,280 @@ +#ifndef NPY_SIMD + #error "Not a standalone header" +#endif + +#ifndef _NPY_SIMD_SSE_OPERATORS_H +#define _NPY_SIMD_SSE_OPERATORS_H + +/*************************** + * Shifting + ***************************/ + +// left +#define npyv_shl_u16(A, C) _mm_sll_epi16(A, _mm_cvtsi32_si128(C)) +#define npyv_shl_s16(A, C) _mm_sll_epi16(A, _mm_cvtsi32_si128(C)) +#define npyv_shl_u32(A, C) _mm_sll_epi32(A, _mm_cvtsi32_si128(C)) +#define npyv_shl_s32(A, C) _mm_sll_epi32(A, _mm_cvtsi32_si128(C)) +#define npyv_shl_u64(A, C) _mm_sll_epi64(A, _mm_cvtsi32_si128(C)) +#define npyv_shl_s64(A, C) _mm_sll_epi64(A, _mm_cvtsi32_si128(C)) + +// left by an immediate constant +#define npyv_shli_u16 _mm_slli_epi16 +#define npyv_shli_s16 _mm_slli_epi16 +#define npyv_shli_u32 _mm_slli_epi32 +#define npyv_shli_s32 _mm_slli_epi32 +#define npyv_shli_u64 _mm_slli_epi64 +#define npyv_shli_s64 _mm_slli_epi64 + +// right +#define npyv_shr_u16(A, C) _mm_srl_epi16(A, _mm_cvtsi32_si128(C)) +#define npyv_shr_s16(A, C) _mm_sra_epi16(A, _mm_cvtsi32_si128(C)) +#define npyv_shr_u32(A, C) _mm_srl_epi32(A, _mm_cvtsi32_si128(C)) +#define npyv_shr_s32(A, C) _mm_sra_epi32(A, _mm_cvtsi32_si128(C)) +#define npyv_shr_u64(A, C) _mm_srl_epi64(A, _mm_cvtsi32_si128(C)) +NPY_FINLINE __m128i npyv_shr_s64(__m128i a, int c) +{ + const __m128i sbit = npyv_setall_s64(0x8000000000000000); + const __m128i cv = _mm_cvtsi32_si128(c); + __m128i r = _mm_srl_epi64(_mm_add_epi64(a, sbit), cv); + return _mm_sub_epi64(r, _mm_srl_epi64(sbit, cv)); +} + +// Right by an immediate constant +#define npyv_shri_u16 _mm_srli_epi16 +#define npyv_shri_s16 _mm_srai_epi16 +#define npyv_shri_u32 _mm_srli_epi32 +#define npyv_shri_s32 _mm_srai_epi32 +#define npyv_shri_u64 _mm_srli_epi64 +#define npyv_shri_s64 npyv_shr_s64 + +/*************************** + * Logical + ***************************/ + +// AND +#define npyv_and_u8 _mm_and_si128 +#define npyv_and_s8 _mm_and_si128 +#define npyv_and_u16 _mm_and_si128 +#define npyv_and_s16 _mm_and_si128 +#define npyv_and_u32 _mm_and_si128 +#define npyv_and_s32 _mm_and_si128 +#define npyv_and_u64 _mm_and_si128 +#define npyv_and_s64 _mm_and_si128 +#define npyv_and_f32 _mm_and_ps +#define npyv_and_f64 _mm_and_pd +#define npyv_and_b8 _mm_and_si128 +#define npyv_and_b16 _mm_and_si128 +#define npyv_and_b32 _mm_and_si128 +#define npyv_and_b64 _mm_and_si128 + +// OR +#define npyv_or_u8 _mm_or_si128 +#define npyv_or_s8 _mm_or_si128 +#define npyv_or_u16 _mm_or_si128 +#define npyv_or_s16 _mm_or_si128 +#define npyv_or_u32 _mm_or_si128 +#define npyv_or_s32 _mm_or_si128 +#define npyv_or_u64 _mm_or_si128 +#define npyv_or_s64 _mm_or_si128 +#define npyv_or_f32 _mm_or_ps +#define npyv_or_f64 _mm_or_pd +#define npyv_or_b8 _mm_or_si128 +#define npyv_or_b16 _mm_or_si128 +#define npyv_or_b32 _mm_or_si128 +#define npyv_or_b64 _mm_or_si128 + +// XOR +#define npyv_xor_u8 _mm_xor_si128 +#define npyv_xor_s8 _mm_xor_si128 +#define npyv_xor_u16 _mm_xor_si128 +#define npyv_xor_s16 _mm_xor_si128 +#define npyv_xor_u32 _mm_xor_si128 +#define npyv_xor_s32 _mm_xor_si128 +#define npyv_xor_u64 _mm_xor_si128 +#define npyv_xor_s64 _mm_xor_si128 +#define npyv_xor_f32 _mm_xor_ps +#define npyv_xor_f64 _mm_xor_pd +#define npyv_xor_b8 _mm_xor_si128 +#define npyv_xor_b16 _mm_xor_si128 +#define npyv_xor_b32 _mm_xor_si128 +#define npyv_xor_b64 _mm_xor_si128 + +// NOT +#define npyv_not_u8(A) _mm_xor_si128(A, _mm_set1_epi32(-1)) +#define npyv_not_s8 npyv_not_u8 +#define npyv_not_u16 npyv_not_u8 +#define npyv_not_s16 npyv_not_u8 +#define npyv_not_u32 npyv_not_u8 +#define npyv_not_s32 npyv_not_u8 +#define npyv_not_u64 npyv_not_u8 +#define npyv_not_s64 npyv_not_u8 +#define npyv_not_f32(A) _mm_xor_ps(A, _mm_castsi128_ps(_mm_set1_epi32(-1))) +#define npyv_not_f64(A) _mm_xor_pd(A, _mm_castsi128_pd(_mm_set1_epi32(-1))) +#define npyv_not_b8 npyv_not_u8 +#define npyv_not_b16 npyv_not_u8 +#define npyv_not_b32 npyv_not_u8 +#define npyv_not_b64 npyv_not_u8 + +/*************************** + * Comparison + ***************************/ + +// Int Equal +#define npyv_cmpeq_u8 _mm_cmpeq_epi8 +#define npyv_cmpeq_s8 _mm_cmpeq_epi8 +#define npyv_cmpeq_u16 _mm_cmpeq_epi16 +#define npyv_cmpeq_s16 _mm_cmpeq_epi16 +#define npyv_cmpeq_u32 _mm_cmpeq_epi32 +#define npyv_cmpeq_s32 _mm_cmpeq_epi32 +#define npyv_cmpeq_s64 npyv_cmpeq_u64 + +#ifdef NPY_HAVE_SSE41 + #define npyv_cmpeq_u64 _mm_cmpeq_epi64 +#else + NPY_FINLINE __m128i npyv_cmpeq_u64(__m128i a, __m128i b) + { + __m128i cmpeq = _mm_cmpeq_epi32(a, b); + __m128i cmpeq_h = _mm_srli_epi64(cmpeq, 32); + __m128i test = _mm_and_si128(cmpeq, cmpeq_h); + return _mm_shuffle_epi32(test, _MM_SHUFFLE(2, 2, 0, 0)); + } +#endif + +// Int Not Equal +#ifdef NPY_HAVE_XOP + #define npyv_cmpneq_u8 _mm_comneq_epi8 + #define npyv_cmpneq_u16 _mm_comneq_epi16 + #define npyv_cmpneq_u32 _mm_comneq_epi32 + #define npyv_cmpneq_u64 _mm_comneq_epi64 +#else + #define npyv_cmpneq_u8(A, B) npyv_not_u8(npyv_cmpeq_u8(A, B)) + #define npyv_cmpneq_u16(A, B) npyv_not_u16(npyv_cmpeq_u16(A, B)) + #define npyv_cmpneq_u32(A, B) npyv_not_u32(npyv_cmpeq_u32(A, B)) + #define npyv_cmpneq_u64(A, B) npyv_not_u64(npyv_cmpeq_u64(A, B)) +#endif +#define npyv_cmpneq_s8 npyv_cmpneq_u8 +#define npyv_cmpneq_s16 npyv_cmpneq_u16 +#define npyv_cmpneq_s32 npyv_cmpneq_u32 +#define npyv_cmpneq_s64 npyv_cmpneq_u64 + +// signed greater than +#define npyv_cmpgt_s8 _mm_cmpgt_epi8 +#define npyv_cmpgt_s16 _mm_cmpgt_epi16 +#define npyv_cmpgt_s32 _mm_cmpgt_epi32 + +#ifdef NPY_HAVE_SSE42 + #define npyv_cmpgt_s64 _mm_cmpgt_epi64 +#else + NPY_FINLINE __m128i npyv_cmpgt_s64(__m128i a, __m128i b) + { + __m128i sub = _mm_sub_epi64(b, a); + __m128i nsame_sbit = _mm_xor_si128(a, b); + // nsame_sbit ? b : sub + __m128i test = _mm_xor_si128(sub, _mm_and_si128(_mm_xor_si128(sub, b), nsame_sbit)); + __m128i extend_sbit = _mm_shuffle_epi32(_mm_srai_epi32(test, 31), _MM_SHUFFLE(3, 3, 1, 1)); + return extend_sbit; + } +#endif + +// signed greater than or equal +#ifdef NPY_HAVE_XOP + #define npyv_cmpge_s8 _mm_comge_epi8 + #define npyv_cmpge_s16 _mm_comge_epi16 + #define npyv_cmpge_s32 _mm_comge_epi32 + #define npyv_cmpge_s64 _mm_comge_epi64 +#else + #define npyv_cmpge_s8(A, B) npyv_not_s8(_mm_cmpgt_epi8(B, A)) + #define npyv_cmpge_s16(A, B) npyv_not_s16(_mm_cmpgt_epi16(B, A)) + #define npyv_cmpge_s32(A, B) npyv_not_s32(_mm_cmpgt_epi32(B, A)) + #define npyv_cmpge_s64(A, B) npyv_not_s64(npyv_cmpgt_s64(B, A)) +#endif + +// unsigned greater than +#ifdef NPY_HAVE_XOP + #define npyv_cmpgt_u8 _mm_comgt_epu8 + #define npyv_cmpgt_u16 _mm_comgt_epu16 + #define npyv_cmpgt_u32 _mm_comgt_epu32 + #define npyv_cmpgt_u64 _mm_comgt_epu64 +#else + #define NPYV_IMPL_SSE_UNSIGNED_GT(LEN, SIGN) \ + NPY_FINLINE __m128i npyv_cmpgt_u##LEN(__m128i a, __m128i b) \ + { \ + const __m128i sbit = _mm_set1_epi32(SIGN); \ + return _mm_cmpgt_epi##LEN( \ + _mm_xor_si128(a, sbit), _mm_xor_si128(b, sbit) \ + ); \ + } + + NPYV_IMPL_SSE_UNSIGNED_GT(8, 0x80808080) + NPYV_IMPL_SSE_UNSIGNED_GT(16, 0x80008000) + NPYV_IMPL_SSE_UNSIGNED_GT(32, 0x80000000) + + NPY_FINLINE __m128i npyv_cmpgt_u64(__m128i a, __m128i b) + { + const __m128i sbit = npyv_setall_s64(0x8000000000000000); + return npyv_cmpgt_s64(_mm_xor_si128(a, sbit), _mm_xor_si128(b, sbit)); + } +#endif + +// unsigned greater than or equal +#ifdef NPY_HAVE_XOP + #define npyv_cmpge_u8 _mm_comge_epu8 + #define npyv_cmpge_u16 _mm_comge_epu16 + #define npyv_cmpge_u32 _mm_comge_epu32 + #define npyv_cmpge_u64 _mm_comge_epu64 +#else + NPY_FINLINE __m128i npyv_cmpge_u8(__m128i a, __m128i b) + { return _mm_cmpeq_epi8(a, _mm_max_epu8(a, b)); } + #ifdef NPY_HAVE_SSE41 + NPY_FINLINE __m128i npyv_cmpge_u16(__m128i a, __m128i b) + { return _mm_cmpeq_epi16(a, _mm_max_epu16(a, b)); } + NPY_FINLINE __m128i npyv_cmpge_u32(__m128i a, __m128i b) + { return _mm_cmpeq_epi32(a, _mm_max_epu32(a, b)); } + #else + #define npyv_cmpge_u16(A, B) _mm_cmpeq_epi16(_mm_subs_epu16(B, A), _mm_setzero_si128()) + #define npyv_cmpge_u32(A, B) npyv_not_u32(npyv_cmpgt_u32(B, A)) + #endif + #define npyv_cmpge_u64(A, B) npyv_not_u64(npyv_cmpgt_u64(B, A)) +#endif + +// less than +#define npyv_cmplt_u8(A, B) npyv_cmpgt_u8(B, A) +#define npyv_cmplt_s8(A, B) npyv_cmpgt_s8(B, A) +#define npyv_cmplt_u16(A, B) npyv_cmpgt_u16(B, A) +#define npyv_cmplt_s16(A, B) npyv_cmpgt_s16(B, A) +#define npyv_cmplt_u32(A, B) npyv_cmpgt_u32(B, A) +#define npyv_cmplt_s32(A, B) npyv_cmpgt_s32(B, A) +#define npyv_cmplt_u64(A, B) npyv_cmpgt_u64(B, A) +#define npyv_cmplt_s64(A, B) npyv_cmpgt_s64(B, A) + +// less than or equal +#define npyv_cmple_u8(A, B) npyv_cmpge_u8(B, A) +#define npyv_cmple_s8(A, B) npyv_cmpge_s8(B, A) +#define npyv_cmple_u16(A, B) npyv_cmpge_u16(B, A) +#define npyv_cmple_s16(A, B) npyv_cmpge_s16(B, A) +#define npyv_cmple_u32(A, B) npyv_cmpge_u32(B, A) +#define npyv_cmple_s32(A, B) npyv_cmpge_s32(B, A) +#define npyv_cmple_u64(A, B) npyv_cmpge_u64(B, A) +#define npyv_cmple_s64(A, B) npyv_cmpge_s64(B, A) + +// precision comparison +#define npyv_cmpeq_f32(a, b) _mm_castps_si128(_mm_cmpeq_ps(a, b)) +#define npyv_cmpeq_f64(a, b) _mm_castpd_si128(_mm_cmpeq_pd(a, b)) +#define npyv_cmpneq_f32(a, b) _mm_castps_si128(_mm_cmpneq_ps(a, b)) +#define npyv_cmpneq_f64(a, b) _mm_castpd_si128(_mm_cmpneq_pd(a, b)) +#define npyv_cmplt_f32(a, b) _mm_castps_si128(_mm_cmplt_ps(a, b)) +#define npyv_cmplt_f64(a, b) _mm_castpd_si128(_mm_cmplt_pd(a, b)) +#define npyv_cmple_f32(a, b) _mm_castps_si128(_mm_cmple_ps(a, b)) +#define npyv_cmple_f64(a, b) _mm_castpd_si128(_mm_cmple_pd(a, b)) +#define npyv_cmpgt_f32(a, b) _mm_castps_si128(_mm_cmpgt_ps(a, b)) +#define npyv_cmpgt_f64(a, b) _mm_castpd_si128(_mm_cmpgt_pd(a, b)) +#define npyv_cmpge_f32(a, b) _mm_castps_si128(_mm_cmpge_ps(a, b)) +#define npyv_cmpge_f64(a, b) _mm_castpd_si128(_mm_cmpge_pd(a, b)) + +// check special cases +NPY_FINLINE npyv_b32 npyv_notnan_f32(npyv_f32 a) +{ return _mm_castps_si128(_mm_cmpord_ps(a, a)); } +NPY_FINLINE npyv_b64 npyv_notnan_f64(npyv_f64 a) +{ return _mm_castpd_si128(_mm_cmpord_pd(a, a)); } + +#endif // _NPY_SIMD_SSE_OPERATORS_H diff --git a/numpy/core/src/common/simd/sse/reorder.h b/numpy/core/src/common/simd/sse/reorder.h new file mode 100644 index 000000000000..d96ab9c5688b --- /dev/null +++ b/numpy/core/src/common/simd/sse/reorder.h @@ -0,0 +1,125 @@ +#ifndef NPY_SIMD + #error "Not a standalone header" +#endif + +#ifndef _NPY_SIMD_SSE_REORDER_H +#define _NPY_SIMD_SSE_REORDER_H + +// combine lower part of two vectors +#define npyv_combinel_u8 _mm_unpacklo_epi64 +#define npyv_combinel_s8 _mm_unpacklo_epi64 +#define npyv_combinel_u16 _mm_unpacklo_epi64 +#define npyv_combinel_s16 _mm_unpacklo_epi64 +#define npyv_combinel_u32 _mm_unpacklo_epi64 +#define npyv_combinel_s32 _mm_unpacklo_epi64 +#define npyv_combinel_u64 _mm_unpacklo_epi64 +#define npyv_combinel_s64 _mm_unpacklo_epi64 +#define npyv_combinel_f32(A, B) _mm_castsi128_ps(_mm_unpacklo_epi64(_mm_castps_si128(A), _mm_castps_si128(B))) +#define npyv_combinel_f64 _mm_unpacklo_pd + +// combine higher part of two vectors +#define npyv_combineh_u8 _mm_unpackhi_epi64 +#define npyv_combineh_s8 _mm_unpackhi_epi64 +#define npyv_combineh_u16 _mm_unpackhi_epi64 +#define npyv_combineh_s16 _mm_unpackhi_epi64 +#define npyv_combineh_u32 _mm_unpackhi_epi64 +#define npyv_combineh_s32 _mm_unpackhi_epi64 +#define npyv_combineh_u64 _mm_unpackhi_epi64 +#define npyv_combineh_s64 _mm_unpackhi_epi64 +#define npyv_combineh_f32(A, B) _mm_castsi128_ps(_mm_unpackhi_epi64(_mm_castps_si128(A), _mm_castps_si128(B))) +#define npyv_combineh_f64 _mm_unpackhi_pd + +// combine two vectors from lower and higher parts of two other vectors +NPY_FINLINE npyv_m128ix2 npyv__combine(__m128i a, __m128i b) +{ + npyv_m128ix2 r; + r.val[0] = npyv_combinel_u8(a, b); + r.val[1] = npyv_combineh_u8(a, b); + return r; +} +NPY_FINLINE npyv_f32x2 npyv_combine_f32(__m128 a, __m128 b) +{ + npyv_f32x2 r; + r.val[0] = npyv_combinel_f32(a, b); + r.val[1] = npyv_combineh_f32(a, b); + return r; +} +NPY_FINLINE npyv_f64x2 npyv_combine_f64(__m128d a, __m128d b) +{ + npyv_f64x2 r; + r.val[0] = npyv_combinel_f64(a, b); + r.val[1] = npyv_combineh_f64(a, b); + return r; +} +#define npyv_combine_u8 npyv__combine +#define npyv_combine_s8 npyv__combine +#define npyv_combine_u16 npyv__combine +#define npyv_combine_s16 npyv__combine +#define npyv_combine_u32 npyv__combine +#define npyv_combine_s32 npyv__combine +#define npyv_combine_u64 npyv__combine +#define npyv_combine_s64 npyv__combine + +// interleave two vectors +#define NPYV_IMPL_SSE_ZIP(T_VEC, SFX, INTR_SFX) \ + NPY_FINLINE T_VEC##x2 npyv_zip_##SFX(T_VEC a, T_VEC b) \ + { \ + T_VEC##x2 r; \ + r.val[0] = _mm_unpacklo_##INTR_SFX(a, b); \ + r.val[1] = _mm_unpackhi_##INTR_SFX(a, b); \ + return r; \ + } + +NPYV_IMPL_SSE_ZIP(npyv_u8, u8, epi8) +NPYV_IMPL_SSE_ZIP(npyv_s8, s8, epi8) +NPYV_IMPL_SSE_ZIP(npyv_u16, u16, epi16) +NPYV_IMPL_SSE_ZIP(npyv_s16, s16, epi16) +NPYV_IMPL_SSE_ZIP(npyv_u32, u32, epi32) +NPYV_IMPL_SSE_ZIP(npyv_s32, s32, epi32) +NPYV_IMPL_SSE_ZIP(npyv_u64, u64, epi64) +NPYV_IMPL_SSE_ZIP(npyv_s64, s64, epi64) +NPYV_IMPL_SSE_ZIP(npyv_f32, f32, ps) +NPYV_IMPL_SSE_ZIP(npyv_f64, f64, pd) + +// Reverse elements of each 64-bit lane +NPY_FINLINE npyv_u16 npyv_rev64_u16(npyv_u16 a) +{ +#ifdef NPY_HAVE_SSSE3 + const __m128i idx = _mm_setr_epi8( + 6, 7, 4, 5, 2, 3, 0, 1,/*64*/14, 15, 12, 13, 10, 11, 8, 9 + ); + return _mm_shuffle_epi8(a, idx); +#else + __m128i lo = _mm_shufflelo_epi16(a, _MM_SHUFFLE(0, 1, 2, 3)); + return _mm_shufflehi_epi16(lo, _MM_SHUFFLE(0, 1, 2, 3)); +#endif +} +#define npyv_rev64_s16 npyv_rev64_u16 + +NPY_FINLINE npyv_u8 npyv_rev64_u8(npyv_u8 a) +{ +#ifdef NPY_HAVE_SSSE3 + const __m128i idx = _mm_setr_epi8( + 7, 6, 5, 4, 3, 2, 1, 0,/*64*/15, 14, 13, 12, 11, 10, 9, 8 + ); + return _mm_shuffle_epi8(a, idx); +#else + __m128i rev16 = npyv_rev64_u16(a); + // swap 8bit pairs + return _mm_or_si128(_mm_slli_epi16(rev16, 8), _mm_srli_epi16(rev16, 8)); +#endif +} +#define npyv_rev64_s8 npyv_rev64_u8 + +NPY_FINLINE npyv_u32 npyv_rev64_u32(npyv_u32 a) +{ + return _mm_shuffle_epi32(a, _MM_SHUFFLE(2, 3, 0, 1)); +} +#define npyv_rev64_s32 npyv_rev64_u32 + +NPY_FINLINE npyv_f32 npyv_rev64_f32(npyv_f32 a) +{ + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 3, 0, 1)); +} + +#endif // _NPY_SIMD_SSE_REORDER_H diff --git a/numpy/core/src/common/simd/sse/sse.h b/numpy/core/src/common/simd/sse/sse.h new file mode 100644 index 000000000000..0bb404312867 --- /dev/null +++ b/numpy/core/src/common/simd/sse/sse.h @@ -0,0 +1,72 @@ +#ifndef _NPY_SIMD_H_ + #error "Not a standalone header" +#endif + +#define NPY_SIMD 128 +#define NPY_SIMD_WIDTH 16 +#define NPY_SIMD_F64 1 +#if defined(NPY_HAVE_FMA3) || defined(NPY_HAVE_FMA4) + #define NPY_SIMD_FMA3 1 // native support +#else + #define NPY_SIMD_FMA3 0 // fast emulated +#endif +typedef __m128i npyv_u8; +typedef __m128i npyv_s8; +typedef __m128i npyv_u16; +typedef __m128i npyv_s16; +typedef __m128i npyv_u32; +typedef __m128i npyv_s32; +typedef __m128i npyv_u64; +typedef __m128i npyv_s64; +typedef __m128 npyv_f32; +typedef __m128d npyv_f64; + +typedef __m128i npyv_b8; +typedef __m128i npyv_b16; +typedef __m128i npyv_b32; +typedef __m128i npyv_b64; + +typedef struct { __m128i val[2]; } npyv_m128ix2; +typedef npyv_m128ix2 npyv_u8x2; +typedef npyv_m128ix2 npyv_s8x2; +typedef npyv_m128ix2 npyv_u16x2; +typedef npyv_m128ix2 npyv_s16x2; +typedef npyv_m128ix2 npyv_u32x2; +typedef npyv_m128ix2 npyv_s32x2; +typedef npyv_m128ix2 npyv_u64x2; +typedef npyv_m128ix2 npyv_s64x2; + +typedef struct { __m128i val[3]; } npyv_m128ix3; +typedef npyv_m128ix3 npyv_u8x3; +typedef npyv_m128ix3 npyv_s8x3; +typedef npyv_m128ix3 npyv_u16x3; +typedef npyv_m128ix3 npyv_s16x3; +typedef npyv_m128ix3 npyv_u32x3; +typedef npyv_m128ix3 npyv_s32x3; +typedef npyv_m128ix3 npyv_u64x3; +typedef npyv_m128ix3 npyv_s64x3; + +typedef struct { __m128 val[2]; } npyv_f32x2; +typedef struct { __m128d val[2]; } npyv_f64x2; +typedef struct { __m128 val[3]; } npyv_f32x3; +typedef struct { __m128d val[3]; } npyv_f64x3; + +#define npyv_nlanes_u8 16 +#define npyv_nlanes_s8 16 +#define npyv_nlanes_u16 8 +#define npyv_nlanes_s16 8 +#define npyv_nlanes_u32 4 +#define npyv_nlanes_s32 4 +#define npyv_nlanes_u64 2 +#define npyv_nlanes_s64 2 +#define npyv_nlanes_f32 4 +#define npyv_nlanes_f64 2 + +#include "utils.h" +#include "memory.h" +#include "misc.h" +#include "reorder.h" +#include "operators.h" +#include "conversion.h" +#include "arithmetic.h" +#include "math.h" diff --git a/numpy/core/src/common/simd/sse/utils.h b/numpy/core/src/common/simd/sse/utils.h new file mode 100644 index 000000000000..c23def11d44c --- /dev/null +++ b/numpy/core/src/common/simd/sse/utils.h @@ -0,0 +1,19 @@ +#ifndef NPY_SIMD + #error "Not a standalone header" +#endif + +#ifndef _NPY_SIMD_SSE_UTILS_H +#define _NPY_SIMD_SSE_UTILS_H + +#if !defined(__x86_64__) && !defined(_M_X64) +NPY_FINLINE npy_int64 npyv128_cvtsi128_si64(__m128i a) +{ + npy_int64 NPY_DECL_ALIGNED(16) idx[2]; + _mm_store_si128((__m128i *)idx, a); + return idx[0]; +} +#else + #define npyv128_cvtsi128_si64 _mm_cvtsi128_si64 +#endif + +#endif // _NPY_SIMD_SSE_UTILS_H diff --git a/numpy/core/src/common/simd/vsx/arithmetic.h b/numpy/core/src/common/simd/vsx/arithmetic.h new file mode 100644 index 000000000000..eaca536201fb --- /dev/null +++ b/numpy/core/src/common/simd/vsx/arithmetic.h @@ -0,0 +1,295 @@ +#ifndef NPY_SIMD + #error "Not a standalone header" +#endif + +#ifndef _NPY_SIMD_VSX_ARITHMETIC_H +#define _NPY_SIMD_VSX_ARITHMETIC_H + +/*************************** + * Addition + ***************************/ +// non-saturated +#define npyv_add_u8 vec_add +#define npyv_add_s8 vec_add +#define npyv_add_u16 vec_add +#define npyv_add_s16 vec_add +#define npyv_add_u32 vec_add +#define npyv_add_s32 vec_add +#define npyv_add_u64 vec_add +#define npyv_add_s64 vec_add +#define npyv_add_f32 vec_add +#define npyv_add_f64 vec_add + +// saturated +#define npyv_adds_u8 vec_adds +#define npyv_adds_s8 vec_adds +#define npyv_adds_u16 vec_adds +#define npyv_adds_s16 vec_adds + +/*************************** + * Subtraction + ***************************/ +// non-saturated +#define npyv_sub_u8 vec_sub +#define npyv_sub_s8 vec_sub +#define npyv_sub_u16 vec_sub +#define npyv_sub_s16 vec_sub +#define npyv_sub_u32 vec_sub +#define npyv_sub_s32 vec_sub +#define npyv_sub_u64 vec_sub +#define npyv_sub_s64 vec_sub +#define npyv_sub_f32 vec_sub +#define npyv_sub_f64 vec_sub + +// saturated +#define npyv_subs_u8 vec_subs +#define npyv_subs_s8 vec_subs +#define npyv_subs_u16 vec_subs +#define npyv_subs_s16 vec_subs + +/*************************** + * Multiplication + ***************************/ +// non-saturated +// up to GCC 6 vec_mul only supports precisions and llong +#if defined(__GNUC__) && __GNUC__ < 7 + #define NPYV_IMPL_VSX_MUL(T_VEC, SFX, ...) \ + NPY_FINLINE T_VEC npyv_mul_##SFX(T_VEC a, T_VEC b) \ + { \ + const npyv_u8 ev_od = {__VA_ARGS__}; \ + return vec_perm( \ + (T_VEC)vec_mule(a, b), \ + (T_VEC)vec_mulo(a, b), ev_od \ + ); \ + } + + NPYV_IMPL_VSX_MUL(npyv_u8, u8, 0, 16, 2, 18, 4, 20, 6, 22, 8, 24, 10, 26, 12, 28, 14, 30) + NPYV_IMPL_VSX_MUL(npyv_s8, s8, 0, 16, 2, 18, 4, 20, 6, 22, 8, 24, 10, 26, 12, 28, 14, 30) + NPYV_IMPL_VSX_MUL(npyv_u16, u16, 0, 1, 16, 17, 4, 5, 20, 21, 8, 9, 24, 25, 12, 13, 28, 29) + NPYV_IMPL_VSX_MUL(npyv_s16, s16, 0, 1, 16, 17, 4, 5, 20, 21, 8, 9, 24, 25, 12, 13, 28, 29) + + // vmuluwm can be used for unsigned or signed 32-bit integers + #define NPYV_IMPL_VSX_MUL_32(T_VEC, SFX) \ + NPY_FINLINE T_VEC npyv_mul_##SFX(T_VEC a, T_VEC b) \ + { \ + T_VEC ret; \ + __asm__ __volatile__( \ + "vmuluwm %0,%1,%2" : \ + "=v" (ret) : "v" (a), "v" (b) \ + ); \ + return ret; \ + } + + NPYV_IMPL_VSX_MUL_32(npyv_u32, u32) + NPYV_IMPL_VSX_MUL_32(npyv_s32, s32) + +#else + #define npyv_mul_u8 vec_mul + #define npyv_mul_s8 vec_mul + #define npyv_mul_u16 vec_mul + #define npyv_mul_s16 vec_mul + #define npyv_mul_u32 vec_mul + #define npyv_mul_s32 vec_mul +#endif +#define npyv_mul_f32 vec_mul +#define npyv_mul_f64 vec_mul + +/*************************** + * Integer Division + ***************************/ +/*** + * TODO: Add support for VSX4(Power10) + */ +// See simd/intdiv.h for more clarification +// divide each unsigned 8-bit element by a precomputed divisor +NPY_FINLINE npyv_u8 npyv_divc_u8(npyv_u8 a, const npyv_u8x3 divisor) +{ + const npyv_u8 mergeo_perm = { + 1, 17, 3, 19, 5, 21, 7, 23, 9, 25, 11, 27, 13, 29, 15, 31 + }; + // high part of unsigned multiplication + npyv_u16 mul_even = vec_mule(a, divisor.val[0]); + npyv_u16 mul_odd = vec_mulo(a, divisor.val[0]); + npyv_u8 mulhi = (npyv_u8)vec_perm(mul_even, mul_odd, mergeo_perm); + // floor(a/d) = (mulhi + ((a-mulhi) >> sh1)) >> sh2 + npyv_u8 q = vec_sub(a, mulhi); + q = vec_sr(q, divisor.val[1]); + q = vec_add(mulhi, q); + q = vec_sr(q, divisor.val[2]); + return q; +} +// divide each signed 8-bit element by a precomputed divisor +NPY_FINLINE npyv_s8 npyv_divc_s8(npyv_s8 a, const npyv_s8x3 divisor) +{ + const npyv_u8 mergeo_perm = { + 1, 17, 3, 19, 5, 21, 7, 23, 9, 25, 11, 27, 13, 29, 15, 31 + }; + // high part of signed multiplication + npyv_s16 mul_even = vec_mule(a, divisor.val[0]); + npyv_s16 mul_odd = vec_mulo(a, divisor.val[0]); + npyv_s8 mulhi = (npyv_s8)vec_perm(mul_even, mul_odd, mergeo_perm); + // q = ((a + mulhi) >> sh1) - XSIGN(a) + // trunc(a/d) = (q ^ dsign) - dsign + npyv_s8 q = vec_sra(vec_add(a, mulhi), (npyv_u8)divisor.val[1]); + q = vec_sub(q, vec_sra(a, npyv_setall_u8(7))); + q = vec_sub(vec_xor(q, divisor.val[2]), divisor.val[2]); + return q; +} +// divide each unsigned 16-bit element by a precomputed divisor +NPY_FINLINE npyv_u16 npyv_divc_u16(npyv_u16 a, const npyv_u16x3 divisor) +{ + const npyv_u8 mergeo_perm = { + 2, 3, 18, 19, 6, 7, 22, 23, 10, 11, 26, 27, 14, 15, 30, 31 + }; + // high part of unsigned multiplication + npyv_u32 mul_even = vec_mule(a, divisor.val[0]); + npyv_u32 mul_odd = vec_mulo(a, divisor.val[0]); + npyv_u16 mulhi = (npyv_u16)vec_perm(mul_even, mul_odd, mergeo_perm); + // floor(a/d) = (mulhi + ((a-mulhi) >> sh1)) >> sh2 + npyv_u16 q = vec_sub(a, mulhi); + q = vec_sr(q, divisor.val[1]); + q = vec_add(mulhi, q); + q = vec_sr(q, divisor.val[2]); + return q; +} +// divide each signed 16-bit element by a precomputed divisor (round towards zero) +NPY_FINLINE npyv_s16 npyv_divc_s16(npyv_s16 a, const npyv_s16x3 divisor) +{ + const npyv_u8 mergeo_perm = { + 2, 3, 18, 19, 6, 7, 22, 23, 10, 11, 26, 27, 14, 15, 30, 31 + }; + // high part of signed multiplication + npyv_s32 mul_even = vec_mule(a, divisor.val[0]); + npyv_s32 mul_odd = vec_mulo(a, divisor.val[0]); + npyv_s16 mulhi = (npyv_s16)vec_perm(mul_even, mul_odd, mergeo_perm); + // q = ((a + mulhi) >> sh1) - XSIGN(a) + // trunc(a/d) = (q ^ dsign) - dsign + npyv_s16 q = vec_sra(vec_add(a, mulhi), (npyv_u16)divisor.val[1]); + q = vec_sub(q, vec_sra(a, npyv_setall_u16(15))); + q = vec_sub(vec_xor(q, divisor.val[2]), divisor.val[2]); + return q; +} +// divide each unsigned 32-bit element by a precomputed divisor +NPY_FINLINE npyv_u32 npyv_divc_u32(npyv_u32 a, const npyv_u32x3 divisor) +{ +#if defined(__GNUC__) && __GNUC__ < 8 + // Doubleword integer wide multiplication supported by GCC 8+ + npyv_u64 mul_even, mul_odd; + __asm__ ("vmulouw %0,%1,%2" : "=v" (mul_even) : "v" (a), "v" (divisor.val[0])); + __asm__ ("vmuleuw %0,%1,%2" : "=v" (mul_odd) : "v" (a), "v" (divisor.val[0])); +#else + // Doubleword integer wide multiplication supported by GCC 8+ + npyv_u64 mul_even = vec_mule(a, divisor.val[0]); + npyv_u64 mul_odd = vec_mulo(a, divisor.val[0]); +#endif + // high part of unsigned multiplication + npyv_u32 mulhi = vec_mergeo((npyv_u32)mul_even, (npyv_u32)mul_odd); + // floor(x/d) = (((a-mulhi) >> sh1) + mulhi) >> sh2 + npyv_u32 q = vec_sub(a, mulhi); + q = vec_sr(q, divisor.val[1]); + q = vec_add(mulhi, q); + q = vec_sr(q, divisor.val[2]); + return q; +} +// divide each signed 32-bit element by a precomputed divisor (round towards zero) +NPY_FINLINE npyv_s32 npyv_divc_s32(npyv_s32 a, const npyv_s32x3 divisor) +{ +#if defined(__GNUC__) && __GNUC__ < 8 + // Doubleword integer wide multiplication supported by GCC8+ + npyv_s64 mul_even, mul_odd; + __asm__ ("vmulosw %0,%1,%2" : "=v" (mul_even) : "v" (a), "v" (divisor.val[0])); + __asm__ ("vmulesw %0,%1,%2" : "=v" (mul_odd) : "v" (a), "v" (divisor.val[0])); +#else + // Doubleword integer wide multiplication supported by GCC8+ + npyv_s64 mul_even = vec_mule(a, divisor.val[0]); + npyv_s64 mul_odd = vec_mulo(a, divisor.val[0]); +#endif + // high part of signed multiplication + npyv_s32 mulhi = vec_mergeo((npyv_s32)mul_even, (npyv_s32)mul_odd); + // q = ((a + mulhi) >> sh1) - XSIGN(a) + // trunc(a/d) = (q ^ dsign) - dsign + npyv_s32 q = vec_sra(vec_add(a, mulhi), (npyv_u32)divisor.val[1]); + q = vec_sub(q, vec_sra(a, npyv_setall_u32(31))); + q = vec_sub(vec_xor(q, divisor.val[2]), divisor.val[2]); + return q; +} +// divide each unsigned 64-bit element by a precomputed divisor +NPY_FINLINE npyv_u64 npyv_divc_u64(npyv_u64 a, const npyv_u64x3 divisor) +{ + const npy_uint64 d = vec_extract(divisor.val[0], 0); + return npyv_set_u64(vec_extract(a, 0) / d, vec_extract(a, 1) / d); +} +// divide each signed 64-bit element by a precomputed divisor (round towards zero) +NPY_FINLINE npyv_s64 npyv_divc_s64(npyv_s64 a, const npyv_s64x3 divisor) +{ + npyv_b64 overflow = npyv_and_b64(vec_cmpeq(a, npyv_setall_s64(-1LL << 63)), (npyv_b64)divisor.val[1]); + npyv_s64 d = vec_sel(divisor.val[0], npyv_setall_s64(1), overflow); + return vec_div(a, d); +} +/*************************** + * Division + ***************************/ +#define npyv_div_f32 vec_div +#define npyv_div_f64 vec_div + +/*************************** + * FUSED + ***************************/ +// multiply and add, a*b + c +#define npyv_muladd_f32 vec_madd +#define npyv_muladd_f64 vec_madd +// multiply and subtract, a*b - c +#define npyv_mulsub_f32 vec_msub +#define npyv_mulsub_f64 vec_msub +// negate multiply and add, -(a*b) + c +#define npyv_nmuladd_f32 vec_nmsub // equivalent to -(a*b - c) +#define npyv_nmuladd_f64 vec_nmsub +// negate multiply and subtract, -(a*b) - c +#define npyv_nmulsub_f32 vec_nmadd // equivalent to -(a*b + c) +#define npyv_nmulsub_f64 vec_nmadd + +/*************************** + * Summation + ***************************/ +// reduce sum across vector +NPY_FINLINE npy_uint64 npyv_sum_u64(npyv_u64 a) +{ + return vec_extract(vec_add(a, vec_mergel(a, a)), 0); +} + +NPY_FINLINE npy_uint32 npyv_sum_u32(npyv_u32 a) +{ + const npyv_u32 rs = vec_add(a, vec_sld(a, a, 8)); + return vec_extract(vec_add(rs, vec_sld(rs, rs, 4)), 0); +} + +NPY_FINLINE float npyv_sum_f32(npyv_f32 a) +{ + npyv_f32 sum = vec_add(a, npyv_combineh_f32(a, a)); + return vec_extract(sum, 0) + vec_extract(sum, 1); +} + +NPY_FINLINE double npyv_sum_f64(npyv_f64 a) +{ + return vec_extract(a, 0) + vec_extract(a, 1); +} + +// expand the source vector and performs sum reduce +NPY_FINLINE npy_uint16 npyv_sumup_u8(npyv_u8 a) +{ + const npyv_u32 zero = npyv_zero_u32(); + npyv_u32 four = vec_sum4s(a, zero); + npyv_s32 one = vec_sums((npyv_s32)four, (npyv_s32)zero); + return (npy_uint16)vec_extract(one, 3); +} + +NPY_FINLINE npy_uint32 npyv_sumup_u16(npyv_u16 a) +{ + const npyv_s32 zero = npyv_zero_s32(); + npyv_u32x2 eight = npyv_expand_u32_u16(a); + npyv_u32 four = vec_add(eight.val[0], eight.val[1]); + npyv_s32 one = vec_sums((npyv_s32)four, zero); + return (npy_uint32)vec_extract(one, 3); +} + +#endif // _NPY_SIMD_VSX_ARITHMETIC_H diff --git a/numpy/core/src/common/simd/vsx/conversion.h b/numpy/core/src/common/simd/vsx/conversion.h new file mode 100644 index 000000000000..36bea7bbaddf --- /dev/null +++ b/numpy/core/src/common/simd/vsx/conversion.h @@ -0,0 +1,123 @@ +#ifndef NPY_SIMD + #error "Not a standalone header" +#endif + +#ifndef _NPY_SIMD_VSX_CVT_H +#define _NPY_SIMD_VSX_CVT_H + +// convert boolean vectors to integer vectors +#define npyv_cvt_u8_b8(BL) ((npyv_u8) BL) +#define npyv_cvt_s8_b8(BL) ((npyv_s8) BL) +#define npyv_cvt_u16_b16(BL) ((npyv_u16) BL) +#define npyv_cvt_s16_b16(BL) ((npyv_s16) BL) +#define npyv_cvt_u32_b32(BL) ((npyv_u32) BL) +#define npyv_cvt_s32_b32(BL) ((npyv_s32) BL) +#define npyv_cvt_u64_b64(BL) ((npyv_u64) BL) +#define npyv_cvt_s64_b64(BL) ((npyv_s64) BL) +#define npyv_cvt_f32_b32(BL) ((npyv_f32) BL) +#define npyv_cvt_f64_b64(BL) ((npyv_f64) BL) + +// convert integer vectors to boolean vectors +#define npyv_cvt_b8_u8(A) ((npyv_b8) A) +#define npyv_cvt_b8_s8(A) ((npyv_b8) A) +#define npyv_cvt_b16_u16(A) ((npyv_b16) A) +#define npyv_cvt_b16_s16(A) ((npyv_b16) A) +#define npyv_cvt_b32_u32(A) ((npyv_b32) A) +#define npyv_cvt_b32_s32(A) ((npyv_b32) A) +#define npyv_cvt_b64_u64(A) ((npyv_b64) A) +#define npyv_cvt_b64_s64(A) ((npyv_b64) A) +#define npyv_cvt_b32_f32(A) ((npyv_b32) A) +#define npyv_cvt_b64_f64(A) ((npyv_b64) A) + +//expand +NPY_FINLINE npyv_u16x2 npyv_expand_u16_u8(npyv_u8 data) +{ + npyv_u16x2 r; + npyv_u8 zero = npyv_zero_u8(); + r.val[0] = (npyv_u16)vec_mergeh(data, zero); + r.val[1] = (npyv_u16)vec_mergel(data, zero); + return r; +} + +NPY_FINLINE npyv_u32x2 npyv_expand_u32_u16(npyv_u16 data) +{ + npyv_u32x2 r; + npyv_u16 zero = npyv_zero_u16(); + r.val[0] = (npyv_u32)vec_mergeh(data, zero); + r.val[1] = (npyv_u32)vec_mergel(data, zero); + return r; +} + +// convert boolean vector to integer bitfield +NPY_FINLINE npy_uint64 npyv_tobits_b8(npyv_b8 a) +{ + const npyv_u8 qperm = npyv_set_u8(120, 112, 104, 96, 88, 80, 72, 64, 56, 48, 40, 32, 24, 16, 8, 0); + return vec_extract((npyv_u32)vec_vbpermq((npyv_u8)a, qperm), 2); +} +NPY_FINLINE npy_uint64 npyv_tobits_b16(npyv_b16 a) +{ + const npyv_u8 qperm = npyv_setf_u8(128, 112, 96, 80, 64, 48, 32, 16, 0); + return vec_extract((npyv_u32)vec_vbpermq((npyv_u8)a, qperm), 2); +} +NPY_FINLINE npy_uint64 npyv_tobits_b32(npyv_b32 a) +{ + const npyv_u8 qperm = npyv_setf_u8(128, 96, 64, 32, 0); + return vec_extract((npyv_u32)vec_vbpermq((npyv_u8)a, qperm), 2); +} +NPY_FINLINE npy_uint64 npyv_tobits_b64(npyv_b64 a) +{ + npyv_u64 bit = npyv_shri_u64((npyv_u64)a, 63); + return vec_extract(bit, 0) | (int)vec_extract(bit, 1) << 1; +} + +// truncate compatible with all compilers(internal use for now) +NPY_FINLINE npyv_s32 npyv__trunc_s32_f32(npyv_f32 a) +{ +#ifdef __IBMC__ + return vec_cts(a, 0); +#elif defined(__clang__) + /** + * old versions of CLANG doesn't support %x<n> in the inline asm template + * which fixes register number when using any of the register constraints wa, wd, wf. + * therefore, we count on built-in functions. + */ + return __builtin_convertvector(a, npyv_s32); +#else // gcc + npyv_s32 ret; + __asm__ ("xvcvspsxws %x0,%x1" : "=wa" (ret) : "wa" (a)); + return ret; +#endif +} +NPY_FINLINE npyv_s32 npyv__trunc_s32_f64(npyv_f64 a, npyv_f64 b) +{ +#ifdef __IBMC__ + const npyv_u8 seq_even = npyv_set_u8(0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27); + // unfortunately, XLC missing asm register vsx fixer + // hopefully, xlc can optimize around big-endian compatibility + npyv_s32 lo_even = vec_cts(a, 0); + npyv_s32 hi_even = vec_cts(b, 0); + return vec_perm(lo_even, hi_even, seq_even); +#else + const npyv_u8 seq_odd = npyv_set_u8(4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23, 28, 29, 30, 31); + #ifdef __clang__ + // __builtin_convertvector doesn't support this conversion on wide range of versions + // fortunately, almost all versions have direct builtin of 'xvcvdpsxws' + npyv_s32 lo_odd = __builtin_vsx_xvcvdpsxws(a); + npyv_s32 hi_odd = __builtin_vsx_xvcvdpsxws(b); + #else // gcc + npyv_s32 lo_odd, hi_odd; + __asm__ ("xvcvdpsxws %x0,%x1" : "=wa" (lo_odd) : "wa" (a)); + __asm__ ("xvcvdpsxws %x0,%x1" : "=wa" (hi_odd) : "wa" (b)); + #endif + return vec_perm(lo_odd, hi_odd, seq_odd); +#endif +} + +// round to nearest integer (assuming even) +NPY_FINLINE npyv_s32 npyv_round_s32_f32(npyv_f32 a) +{ return npyv__trunc_s32_f32(vec_rint(a)); } + +NPY_FINLINE npyv_s32 npyv_round_s32_f64(npyv_f64 a, npyv_f64 b) +{ return npyv__trunc_s32_f64(vec_rint(a), vec_rint(b)); } + +#endif // _NPY_SIMD_VSX_CVT_H diff --git a/numpy/core/src/common/simd/vsx/math.h b/numpy/core/src/common/simd/vsx/math.h new file mode 100644 index 000000000000..d138cae8a24d --- /dev/null +++ b/numpy/core/src/common/simd/vsx/math.h @@ -0,0 +1,80 @@ +#ifndef NPY_SIMD + #error "Not a standalone header" +#endif + +#ifndef _NPY_SIMD_VSX_MATH_H +#define _NPY_SIMD_VSX_MATH_H +/*************************** + * Elementary + ***************************/ +// Square root +#define npyv_sqrt_f32 vec_sqrt +#define npyv_sqrt_f64 vec_sqrt + +// Reciprocal +NPY_FINLINE npyv_f32 npyv_recip_f32(npyv_f32 a) +{ + const npyv_f32 one = npyv_setall_f32(1.0f); + return vec_div(one, a); +} +NPY_FINLINE npyv_f64 npyv_recip_f64(npyv_f64 a) +{ + const npyv_f64 one = npyv_setall_f64(1.0); + return vec_div(one, a); +} + +// Absolute +#define npyv_abs_f32 vec_abs +#define npyv_abs_f64 vec_abs + +// Square +NPY_FINLINE npyv_f32 npyv_square_f32(npyv_f32 a) +{ return vec_mul(a, a); } +NPY_FINLINE npyv_f64 npyv_square_f64(npyv_f64 a) +{ return vec_mul(a, a); } + +// Maximum, natively mapping with no guarantees to handle NaN. +#define npyv_max_f32 vec_max +#define npyv_max_f64 vec_max +// Maximum, supports IEEE floating-point arithmetic (IEC 60559), +// - If one of the two vectors contains NaN, the equivalent element of the other vector is set +// - Only if both corresponded elements are NaN, NaN is set. +#define npyv_maxp_f32 vec_max +#define npyv_maxp_f64 vec_max +// Maximum, integer operations +#define npyv_max_u8 vec_max +#define npyv_max_s8 vec_max +#define npyv_max_u16 vec_max +#define npyv_max_s16 vec_max +#define npyv_max_u32 vec_max +#define npyv_max_s32 vec_max +#define npyv_max_u64 vec_max +#define npyv_max_s64 vec_max + +// Minimum, natively mapping with no guarantees to handle NaN. +#define npyv_min_f32 vec_min +#define npyv_min_f64 vec_min +// Minimum, supports IEEE floating-point arithmetic (IEC 60559), +// - If one of the two vectors contains NaN, the equivalent element of the other vector is set +// - Only if both corresponded elements are NaN, NaN is set. +#define npyv_minp_f32 vec_min +#define npyv_minp_f64 vec_min +// Minimum, integer operations +#define npyv_min_u8 vec_min +#define npyv_min_s8 vec_min +#define npyv_min_u16 vec_min +#define npyv_min_s16 vec_min +#define npyv_min_u32 vec_min +#define npyv_min_s32 vec_min +#define npyv_min_u64 vec_min +#define npyv_min_s64 vec_min + +// ceil +#define npyv_ceil_f32 vec_ceil +#define npyv_ceil_f64 vec_ceil + +// trunc +#define npyv_trunc_f32 vec_trunc +#define npyv_trunc_f64 vec_trunc + +#endif // _NPY_SIMD_VSX_MATH_H diff --git a/numpy/core/src/common/simd/vsx/memory.h b/numpy/core/src/common/simd/vsx/memory.h new file mode 100644 index 000000000000..08a0a9276cc6 --- /dev/null +++ b/numpy/core/src/common/simd/vsx/memory.h @@ -0,0 +1,346 @@ +#ifndef NPY_SIMD + #error "Not a standalone header" +#endif + +#ifndef _NPY_SIMD_VSX_MEMORY_H +#define _NPY_SIMD_VSX_MEMORY_H + +#include "misc.h" + +/**************************** + * Private utilities + ****************************/ +// TODO: test load by cast +#define VSX__CAST_lOAD 0 +#if VSX__CAST_lOAD + #define npyv__load(T_VEC, PTR) (*((T_VEC*)(PTR))) +#else + /** + * CLANG fails to load unaligned addresses via vec_xl, vec_xst + * so we failback to vec_vsx_ld, vec_vsx_st + */ + #if (defined(__GNUC__) && !defined(vec_xl)) || (defined(__clang__) && !defined(__IBMC__)) + #define npyv__load(T_VEC, PTR) vec_vsx_ld(0, PTR) + #else + #define npyv__load(T_VEC, PTR) vec_xl(0, PTR) + #endif +#endif +// unaligned store +#if (defined(__GNUC__) && !defined(vec_xl)) || (defined(__clang__) && !defined(__IBMC__)) + #define npyv__store(PTR, VEC) vec_vsx_st(VEC, 0, PTR) +#else + #define npyv__store(PTR, VEC) vec_xst(VEC, 0, PTR) +#endif + +// avoid aliasing rules +#ifdef __cplusplus + template<typename T_PTR> + NPY_FINLINE npy_uint64 *npyv__ptr2u64(const T_PTR *ptr) + { npy_uint64 *ptr64 = (npy_uint64*)ptr; return ptr64; } +#else + NPY_FINLINE npy_uint64 *npyv__ptr2u64(const void *ptr) + { npy_uint64 *ptr64 = (npy_uint64*)ptr; return ptr64; } +#endif // __cplusplus + +// load lower part +NPY_FINLINE npyv_u64 npyv__loadl(const void *ptr) +{ + #if defined(__clang__) && !defined(__IBMC__) + // vec_promote doesn't support doubleword on clang + return npyv_setall_u64(*npyv__ptr2u64(ptr)); + #else + return vec_promote(*npyv__ptr2u64(ptr), 0); + #endif +} +// store lower part +#define npyv__storel(PTR, VEC) \ + *npyv__ptr2u64(PTR) = vec_extract(((npyv_u64)VEC), 0) + +#define npyv__storeh(PTR, VEC) \ + *npyv__ptr2u64(PTR) = vec_extract(((npyv_u64)VEC), 1) + +/**************************** + * load/store + ****************************/ +#define NPYV_IMPL_VSX_MEM(SFX, DW_CAST) \ + NPY_FINLINE npyv_##SFX npyv_load_##SFX(const npyv_lanetype_##SFX *ptr) \ + { return (npyv_##SFX)npyv__load(npyv_##SFX, (const npyv_lanetype_##DW_CAST*)ptr); } \ + NPY_FINLINE npyv_##SFX npyv_loada_##SFX(const npyv_lanetype_##SFX *ptr) \ + { return (npyv_##SFX)vec_ld(0, (const npyv_lanetype_u32*)ptr); } \ + NPY_FINLINE npyv_##SFX npyv_loads_##SFX(const npyv_lanetype_##SFX *ptr) \ + { return npyv_loada_##SFX(ptr); } \ + NPY_FINLINE npyv_##SFX npyv_loadl_##SFX(const npyv_lanetype_##SFX *ptr) \ + { return (npyv_##SFX)npyv__loadl(ptr); } \ + NPY_FINLINE void npyv_store_##SFX(npyv_lanetype_##SFX *ptr, npyv_##SFX vec) \ + { npyv__store((npyv_lanetype_##DW_CAST*)ptr, (npyv_##DW_CAST)vec); } \ + NPY_FINLINE void npyv_storea_##SFX(npyv_lanetype_##SFX *ptr, npyv_##SFX vec) \ + { vec_st((npyv_u32)vec, 0, (npyv_lanetype_u32*)ptr); } \ + NPY_FINLINE void npyv_stores_##SFX(npyv_lanetype_##SFX *ptr, npyv_##SFX vec) \ + { npyv_storea_##SFX(ptr, vec); } \ + NPY_FINLINE void npyv_storel_##SFX(npyv_lanetype_##SFX *ptr, npyv_##SFX vec) \ + { npyv__storel(ptr, vec); } \ + NPY_FINLINE void npyv_storeh_##SFX(npyv_lanetype_##SFX *ptr, npyv_##SFX vec) \ + { npyv__storeh(ptr, vec); } + +NPYV_IMPL_VSX_MEM(u8, u8) +NPYV_IMPL_VSX_MEM(s8, s8) +NPYV_IMPL_VSX_MEM(u16, u16) +NPYV_IMPL_VSX_MEM(s16, s16) +NPYV_IMPL_VSX_MEM(u32, u32) +NPYV_IMPL_VSX_MEM(s32, s32) +NPYV_IMPL_VSX_MEM(u64, f64) +NPYV_IMPL_VSX_MEM(s64, f64) +NPYV_IMPL_VSX_MEM(f32, f32) +NPYV_IMPL_VSX_MEM(f64, f64) + +/*************************** + * Non-contiguous Load + ***************************/ +//// 32 +NPY_FINLINE npyv_u32 npyv_loadn_u32(const npy_uint32 *ptr, npy_intp stride) +{ + return npyv_set_u32( + ptr[stride * 0], ptr[stride * 1], + ptr[stride * 2], ptr[stride * 3] + ); +} +NPY_FINLINE npyv_s32 npyv_loadn_s32(const npy_int32 *ptr, npy_intp stride) +{ return (npyv_s32)npyv_loadn_u32((const npy_uint32*)ptr, stride); } +NPY_FINLINE npyv_f32 npyv_loadn_f32(const float *ptr, npy_intp stride) +{ return (npyv_f32)npyv_loadn_u32((const npy_uint32*)ptr, stride); } +//// 64 +NPY_FINLINE npyv_u64 npyv_loadn_u64(const npy_uint64 *ptr, npy_intp stride) +{ return npyv_set_u64(ptr[0], ptr[stride]); } +NPY_FINLINE npyv_s64 npyv_loadn_s64(const npy_int64 *ptr, npy_intp stride) +{ return npyv_set_s64(ptr[0], ptr[stride]); } +NPY_FINLINE npyv_f64 npyv_loadn_f64(const double *ptr, npy_intp stride) +{ return npyv_set_f64(ptr[0], ptr[stride]); } +/*************************** + * Non-contiguous Store + ***************************/ +//// 32 +NPY_FINLINE void npyv_storen_u32(npy_uint32 *ptr, npy_intp stride, npyv_u32 a) +{ + ptr[stride * 0] = vec_extract(a, 0); + ptr[stride * 1] = vec_extract(a, 1); + ptr[stride * 2] = vec_extract(a, 2); + ptr[stride * 3] = vec_extract(a, 3); +} +NPY_FINLINE void npyv_storen_s32(npy_int32 *ptr, npy_intp stride, npyv_s32 a) +{ npyv_storen_u32((npy_uint32*)ptr, stride, (npyv_u32)a); } +NPY_FINLINE void npyv_storen_f32(float *ptr, npy_intp stride, npyv_f32 a) +{ npyv_storen_u32((npy_uint32*)ptr, stride, (npyv_u32)a); } +//// 64 +NPY_FINLINE void npyv_storen_u64(npy_uint64 *ptr, npy_intp stride, npyv_u64 a) +{ + ptr[stride * 0] = vec_extract(a, 0); + ptr[stride * 1] = vec_extract(a, 1); +} +NPY_FINLINE void npyv_storen_s64(npy_int64 *ptr, npy_intp stride, npyv_s64 a) +{ npyv_storen_u64((npy_uint64*)ptr, stride, (npyv_u64)a); } +NPY_FINLINE void npyv_storen_f64(double *ptr, npy_intp stride, npyv_f64 a) +{ npyv_storen_u64((npy_uint64*)ptr, stride, (npyv_u64)a); } + +/********************************* + * Partial Load + *********************************/ +//// 32 +NPY_FINLINE npyv_s32 npyv_load_till_s32(const npy_int32 *ptr, npy_uintp nlane, npy_int32 fill) +{ + assert(nlane > 0); + npyv_s32 vfill = npyv_setall_s32(fill); + switch(nlane) { + case 1: + return vec_insert(ptr[0], vfill, 0); + case 2: + return (npyv_s32)vec_insert( + *npyv__ptr2u64(ptr), (npyv_u64)vfill, 0 + ); + case 3: + vfill = vec_insert(ptr[2], vfill, 2); + return (npyv_s32)vec_insert( + *npyv__ptr2u64(ptr), (npyv_u64)vfill, 0 + ); + default: + return npyv_load_s32(ptr); + } +} +// fill zero to rest lanes +NPY_FINLINE npyv_s32 npyv_load_tillz_s32(const npy_int32 *ptr, npy_uintp nlane) +{ return npyv_load_till_s32(ptr, nlane, 0); } +//// 64 +NPY_FINLINE npyv_s64 npyv_load_till_s64(const npy_int64 *ptr, npy_uintp nlane, npy_int64 fill) +{ + assert(nlane > 0); + if (nlane == 1) { + return npyv_set_s64(ptr[0], fill); + } + return npyv_load_s64(ptr); +} +// fill zero to rest lanes +NPY_FINLINE npyv_s64 npyv_load_tillz_s64(const npy_int64 *ptr, npy_uintp nlane) +{ return npyv_load_till_s64(ptr, nlane, 0); } +/********************************* + * Non-contiguous partial load + *********************************/ +//// 32 +NPY_FINLINE npyv_s32 +npyv_loadn_till_s32(const npy_int32 *ptr, npy_intp stride, npy_uintp nlane, npy_int32 fill) +{ + assert(nlane > 0); + npyv_s32 vfill = npyv_setall_s32(fill); + switch(nlane) { + case 3: + vfill = vec_insert(ptr[stride*2], vfill, 2); + case 2: + vfill = vec_insert(ptr[stride], vfill, 1); + case 1: + vfill = vec_insert(*ptr, vfill, 0); + break; + default: + return npyv_loadn_s32(ptr, stride); + } // switch + return vfill; +} +// fill zero to rest lanes +NPY_FINLINE npyv_s32 +npyv_loadn_tillz_s32(const npy_int32 *ptr, npy_intp stride, npy_uintp nlane) +{ return npyv_loadn_till_s32(ptr, stride, nlane, 0); } +//// 64 +NPY_FINLINE npyv_s64 +npyv_loadn_till_s64(const npy_int64 *ptr, npy_intp stride, npy_uintp nlane, npy_int64 fill) +{ + assert(nlane > 0); + if (nlane == 1) { + return npyv_set_s64(*ptr, fill); + } + return npyv_loadn_s64(ptr, stride); +} +// fill zero to rest lanes +NPY_FINLINE npyv_s64 npyv_loadn_tillz_s64(const npy_int64 *ptr, npy_intp stride, npy_uintp nlane) +{ return npyv_loadn_till_s64(ptr, stride, nlane, 0); } +/********************************* + * Partial store + *********************************/ +//// 32 +NPY_FINLINE void npyv_store_till_s32(npy_int32 *ptr, npy_uintp nlane, npyv_s32 a) +{ + assert(nlane > 0); + switch(nlane) { + case 1: + *ptr = vec_extract(a, 0); + break; + case 2: + npyv_storel_s32(ptr, a); + break; + case 3: + npyv_storel_s32(ptr, a); + ptr[2] = vec_extract(a, 2); + break; + default: + npyv_store_s32(ptr, a); + } +} +//// 64 +NPY_FINLINE void npyv_store_till_s64(npy_int64 *ptr, npy_uintp nlane, npyv_s64 a) +{ + assert(nlane > 0); + if (nlane == 1) { + npyv_storel_s64(ptr, a); + return; + } + npyv_store_s64(ptr, a); +} +/********************************* + * Non-contiguous partial store + *********************************/ +//// 32 +NPY_FINLINE void npyv_storen_till_s32(npy_int32 *ptr, npy_intp stride, npy_uintp nlane, npyv_s32 a) +{ + assert(nlane > 0); + switch(nlane) { + default: + ptr[stride*3] = vec_extract(a, 3); + case 3: + ptr[stride*2] = vec_extract(a, 2); + case 2: + ptr[stride*1] = vec_extract(a, 1); + case 1: + ptr[stride*0] = vec_extract(a, 0); + break; + } +} +//// 64 +NPY_FINLINE void npyv_storen_till_s64(npy_int64 *ptr, npy_intp stride, npy_uintp nlane, npyv_s64 a) +{ + assert(nlane > 0); + if (nlane == 1) { + npyv_storel_s64(ptr, a); + return; + } + npyv_storen_s64(ptr, stride, a); +} +/***************************************************************** + * Implement partial load/store for u32/f32/u64/f64... via casting + *****************************************************************/ +#define NPYV_IMPL_VSX_REST_PARTIAL_TYPES(F_SFX, T_SFX) \ + NPY_FINLINE npyv_##F_SFX npyv_load_till_##F_SFX \ + (const npyv_lanetype_##F_SFX *ptr, npy_uintp nlane, npyv_lanetype_##F_SFX fill) \ + { \ + union { \ + npyv_lanetype_##F_SFX from_##F_SFX; \ + npyv_lanetype_##T_SFX to_##T_SFX; \ + } pun = {.from_##F_SFX = fill}; \ + return npyv_reinterpret_##F_SFX##_##T_SFX(npyv_load_till_##T_SFX( \ + (const npyv_lanetype_##T_SFX *)ptr, nlane, pun.to_##T_SFX \ + )); \ + } \ + NPY_FINLINE npyv_##F_SFX npyv_loadn_till_##F_SFX \ + (const npyv_lanetype_##F_SFX *ptr, npy_intp stride, npy_uintp nlane, \ + npyv_lanetype_##F_SFX fill) \ + { \ + union { \ + npyv_lanetype_##F_SFX from_##F_SFX; \ + npyv_lanetype_##T_SFX to_##T_SFX; \ + } pun = {.from_##F_SFX = fill}; \ + return npyv_reinterpret_##F_SFX##_##T_SFX(npyv_loadn_till_##T_SFX( \ + (const npyv_lanetype_##T_SFX *)ptr, stride, nlane, pun.to_##T_SFX \ + )); \ + } \ + NPY_FINLINE npyv_##F_SFX npyv_load_tillz_##F_SFX \ + (const npyv_lanetype_##F_SFX *ptr, npy_uintp nlane) \ + { \ + return npyv_reinterpret_##F_SFX##_##T_SFX(npyv_load_tillz_##T_SFX( \ + (const npyv_lanetype_##T_SFX *)ptr, nlane \ + )); \ + } \ + NPY_FINLINE npyv_##F_SFX npyv_loadn_tillz_##F_SFX \ + (const npyv_lanetype_##F_SFX *ptr, npy_intp stride, npy_uintp nlane) \ + { \ + return npyv_reinterpret_##F_SFX##_##T_SFX(npyv_loadn_tillz_##T_SFX( \ + (const npyv_lanetype_##T_SFX *)ptr, stride, nlane \ + )); \ + } \ + NPY_FINLINE void npyv_store_till_##F_SFX \ + (npyv_lanetype_##F_SFX *ptr, npy_uintp nlane, npyv_##F_SFX a) \ + { \ + npyv_store_till_##T_SFX( \ + (npyv_lanetype_##T_SFX *)ptr, nlane, \ + npyv_reinterpret_##T_SFX##_##F_SFX(a) \ + ); \ + } \ + NPY_FINLINE void npyv_storen_till_##F_SFX \ + (npyv_lanetype_##F_SFX *ptr, npy_intp stride, npy_uintp nlane, npyv_##F_SFX a) \ + { \ + npyv_storen_till_##T_SFX( \ + (npyv_lanetype_##T_SFX *)ptr, stride, nlane, \ + npyv_reinterpret_##T_SFX##_##F_SFX(a) \ + ); \ + } + +NPYV_IMPL_VSX_REST_PARTIAL_TYPES(u32, s32) +NPYV_IMPL_VSX_REST_PARTIAL_TYPES(f32, s32) +NPYV_IMPL_VSX_REST_PARTIAL_TYPES(u64, s64) +NPYV_IMPL_VSX_REST_PARTIAL_TYPES(f64, s64) + +#endif // _NPY_SIMD_VSX_MEMORY_H diff --git a/numpy/core/src/common/simd/vsx/misc.h b/numpy/core/src/common/simd/vsx/misc.h new file mode 100644 index 000000000000..f7a0cdd5c137 --- /dev/null +++ b/numpy/core/src/common/simd/vsx/misc.h @@ -0,0 +1,190 @@ +#ifndef NPY_SIMD + #error "Not a standalone header" +#endif + +#ifndef _NPY_SIMD_VSX_MISC_H +#define _NPY_SIMD_VSX_MISC_H + +// vector with zero lanes +#define npyv_zero_u8() ((npyv_u8) npyv_setall_s32(0)) +#define npyv_zero_s8() ((npyv_s8) npyv_setall_s32(0)) +#define npyv_zero_u16() ((npyv_u16) npyv_setall_s32(0)) +#define npyv_zero_s16() ((npyv_s16) npyv_setall_s32(0)) +#define npyv_zero_u32() npyv_setall_u32(0) +#define npyv_zero_s32() npyv_setall_s32(0) +#define npyv_zero_u64() ((npyv_u64) npyv_setall_s32(0)) +#define npyv_zero_s64() ((npyv_s64) npyv_setall_s32(0)) +#define npyv_zero_f32() npyv_setall_f32(0.0f) +#define npyv_zero_f64() npyv_setall_f64(0.0) + +// vector with a specific value set to all lanes +// the safest way to generate vsplti* and vsplt* instructions +#define NPYV_IMPL_VSX_SPLTB(T_VEC, V) ((T_VEC){V, V, V, V, V, V, V, V, V, V, V, V, V, V, V, V}) +#define NPYV_IMPL_VSX_SPLTH(T_VEC, V) ((T_VEC){V, V, V, V, V, V, V, V}) +#define NPYV_IMPL_VSX_SPLTW(T_VEC, V) ((T_VEC){V, V, V, V}) +#define NPYV_IMPL_VSX_SPLTD(T_VEC, V) ((T_VEC){V, V}) + +#define npyv_setall_u8(VAL) NPYV_IMPL_VSX_SPLTB(npyv_u8, (unsigned char)VAL) +#define npyv_setall_s8(VAL) NPYV_IMPL_VSX_SPLTB(npyv_s8, (signed char)VAL) +#define npyv_setall_u16(VAL) NPYV_IMPL_VSX_SPLTH(npyv_u16, (unsigned short)VAL) +#define npyv_setall_s16(VAL) NPYV_IMPL_VSX_SPLTH(npyv_s16, (short)VAL) +#define npyv_setall_u32(VAL) NPYV_IMPL_VSX_SPLTW(npyv_u32, (unsigned int)VAL) +#define npyv_setall_s32(VAL) NPYV_IMPL_VSX_SPLTW(npyv_s32, (int)VAL) +#define npyv_setall_f32(VAL) NPYV_IMPL_VSX_SPLTW(npyv_f32, VAL) +#define npyv_setall_u64(VAL) NPYV_IMPL_VSX_SPLTD(npyv_u64, (npy_uint64)VAL) +#define npyv_setall_s64(VAL) NPYV_IMPL_VSX_SPLTD(npyv_s64, (npy_int64)VAL) +#define npyv_setall_f64(VAL) NPYV_IMPL_VSX_SPLTD(npyv_f64, VAL) + +// vector with specific values set to each lane and +// set a specific value to all remained lanes +#define npyv_setf_u8(FILL, ...) ((npyv_u8){NPYV__SET_FILL_16(char, FILL, __VA_ARGS__)}) +#define npyv_setf_s8(FILL, ...) ((npyv_s8){NPYV__SET_FILL_16(char, FILL, __VA_ARGS__)}) +#define npyv_setf_u16(FILL, ...) ((npyv_u16){NPYV__SET_FILL_8(short, FILL, __VA_ARGS__)}) +#define npyv_setf_s16(FILL, ...) ((npyv_s16){NPYV__SET_FILL_8(short, FILL, __VA_ARGS__)}) +#define npyv_setf_u32(FILL, ...) ((npyv_u32){NPYV__SET_FILL_4(int, FILL, __VA_ARGS__)}) +#define npyv_setf_s32(FILL, ...) ((npyv_s32){NPYV__SET_FILL_4(int, FILL, __VA_ARGS__)}) +#define npyv_setf_u64(FILL, ...) ((npyv_u64){NPYV__SET_FILL_2(npy_int64, FILL, __VA_ARGS__)}) +#define npyv_setf_s64(FILL, ...) ((npyv_s64){NPYV__SET_FILL_2(npy_int64, FILL, __VA_ARGS__)}) +#define npyv_setf_f32(FILL, ...) ((npyv_f32){NPYV__SET_FILL_4(float, FILL, __VA_ARGS__)}) +#define npyv_setf_f64(FILL, ...) ((npyv_f64){NPYV__SET_FILL_2(double, FILL, __VA_ARGS__)}) + +// vector with specific values set to each lane and +// set zero to all remained lanes +#define npyv_set_u8(...) npyv_setf_u8(0, __VA_ARGS__) +#define npyv_set_s8(...) npyv_setf_s8(0, __VA_ARGS__) +#define npyv_set_u16(...) npyv_setf_u16(0, __VA_ARGS__) +#define npyv_set_s16(...) npyv_setf_s16(0, __VA_ARGS__) +#define npyv_set_u32(...) npyv_setf_u32(0, __VA_ARGS__) +#define npyv_set_s32(...) npyv_setf_s32(0, __VA_ARGS__) +#define npyv_set_u64(...) npyv_setf_u64(0, __VA_ARGS__) +#define npyv_set_s64(...) npyv_setf_s64(0, __VA_ARGS__) +#define npyv_set_f32(...) npyv_setf_f32(0, __VA_ARGS__) +#define npyv_set_f64(...) npyv_setf_f64(0, __VA_ARGS__) + +// Per lane select +#define npyv_select_u8(MASK, A, B) vec_sel(B, A, MASK) +#define npyv_select_s8 npyv_select_u8 +#define npyv_select_u16 npyv_select_u8 +#define npyv_select_s16 npyv_select_u8 +#define npyv_select_u32 npyv_select_u8 +#define npyv_select_s32 npyv_select_u8 +#define npyv_select_u64 npyv_select_u8 +#define npyv_select_s64 npyv_select_u8 +#define npyv_select_f32 npyv_select_u8 +#define npyv_select_f64 npyv_select_u8 + +// Reinterpret +#define npyv_reinterpret_u8_u8(X) X +#define npyv_reinterpret_u8_s8(X) ((npyv_u8)X) +#define npyv_reinterpret_u8_u16 npyv_reinterpret_u8_s8 +#define npyv_reinterpret_u8_s16 npyv_reinterpret_u8_s8 +#define npyv_reinterpret_u8_u32 npyv_reinterpret_u8_s8 +#define npyv_reinterpret_u8_s32 npyv_reinterpret_u8_s8 +#define npyv_reinterpret_u8_u64 npyv_reinterpret_u8_s8 +#define npyv_reinterpret_u8_s64 npyv_reinterpret_u8_s8 +#define npyv_reinterpret_u8_f32 npyv_reinterpret_u8_s8 +#define npyv_reinterpret_u8_f64 npyv_reinterpret_u8_s8 + +#define npyv_reinterpret_s8_s8(X) X +#define npyv_reinterpret_s8_u8(X) ((npyv_s8)X) +#define npyv_reinterpret_s8_u16 npyv_reinterpret_s8_u8 +#define npyv_reinterpret_s8_s16 npyv_reinterpret_s8_u8 +#define npyv_reinterpret_s8_u32 npyv_reinterpret_s8_u8 +#define npyv_reinterpret_s8_s32 npyv_reinterpret_s8_u8 +#define npyv_reinterpret_s8_u64 npyv_reinterpret_s8_u8 +#define npyv_reinterpret_s8_s64 npyv_reinterpret_s8_u8 +#define npyv_reinterpret_s8_f32 npyv_reinterpret_s8_u8 +#define npyv_reinterpret_s8_f64 npyv_reinterpret_s8_u8 + +#define npyv_reinterpret_u16_u16(X) X +#define npyv_reinterpret_u16_u8(X) ((npyv_u16)X) +#define npyv_reinterpret_u16_s8 npyv_reinterpret_u16_u8 +#define npyv_reinterpret_u16_s16 npyv_reinterpret_u16_u8 +#define npyv_reinterpret_u16_u32 npyv_reinterpret_u16_u8 +#define npyv_reinterpret_u16_s32 npyv_reinterpret_u16_u8 +#define npyv_reinterpret_u16_u64 npyv_reinterpret_u16_u8 +#define npyv_reinterpret_u16_s64 npyv_reinterpret_u16_u8 +#define npyv_reinterpret_u16_f32 npyv_reinterpret_u16_u8 +#define npyv_reinterpret_u16_f64 npyv_reinterpret_u16_u8 + +#define npyv_reinterpret_s16_s16(X) X +#define npyv_reinterpret_s16_u8(X) ((npyv_s16)X) +#define npyv_reinterpret_s16_s8 npyv_reinterpret_s16_u8 +#define npyv_reinterpret_s16_u16 npyv_reinterpret_s16_u8 +#define npyv_reinterpret_s16_u32 npyv_reinterpret_s16_u8 +#define npyv_reinterpret_s16_s32 npyv_reinterpret_s16_u8 +#define npyv_reinterpret_s16_u64 npyv_reinterpret_s16_u8 +#define npyv_reinterpret_s16_s64 npyv_reinterpret_s16_u8 +#define npyv_reinterpret_s16_f32 npyv_reinterpret_s16_u8 +#define npyv_reinterpret_s16_f64 npyv_reinterpret_s16_u8 + +#define npyv_reinterpret_u32_u32(X) X +#define npyv_reinterpret_u32_u8(X) ((npyv_u32)X) +#define npyv_reinterpret_u32_s8 npyv_reinterpret_u32_u8 +#define npyv_reinterpret_u32_u16 npyv_reinterpret_u32_u8 +#define npyv_reinterpret_u32_s16 npyv_reinterpret_u32_u8 +#define npyv_reinterpret_u32_s32 npyv_reinterpret_u32_u8 +#define npyv_reinterpret_u32_u64 npyv_reinterpret_u32_u8 +#define npyv_reinterpret_u32_s64 npyv_reinterpret_u32_u8 +#define npyv_reinterpret_u32_f32 npyv_reinterpret_u32_u8 +#define npyv_reinterpret_u32_f64 npyv_reinterpret_u32_u8 + +#define npyv_reinterpret_s32_s32(X) X +#define npyv_reinterpret_s32_u8(X) ((npyv_s32)X) +#define npyv_reinterpret_s32_s8 npyv_reinterpret_s32_u8 +#define npyv_reinterpret_s32_u16 npyv_reinterpret_s32_u8 +#define npyv_reinterpret_s32_s16 npyv_reinterpret_s32_u8 +#define npyv_reinterpret_s32_u32 npyv_reinterpret_s32_u8 +#define npyv_reinterpret_s32_u64 npyv_reinterpret_s32_u8 +#define npyv_reinterpret_s32_s64 npyv_reinterpret_s32_u8 +#define npyv_reinterpret_s32_f32 npyv_reinterpret_s32_u8 +#define npyv_reinterpret_s32_f64 npyv_reinterpret_s32_u8 + +#define npyv_reinterpret_u64_u64(X) X +#define npyv_reinterpret_u64_u8(X) ((npyv_u64)X) +#define npyv_reinterpret_u64_s8 npyv_reinterpret_u64_u8 +#define npyv_reinterpret_u64_u16 npyv_reinterpret_u64_u8 +#define npyv_reinterpret_u64_s16 npyv_reinterpret_u64_u8 +#define npyv_reinterpret_u64_u32 npyv_reinterpret_u64_u8 +#define npyv_reinterpret_u64_s32 npyv_reinterpret_u64_u8 +#define npyv_reinterpret_u64_s64 npyv_reinterpret_u64_u8 +#define npyv_reinterpret_u64_f32 npyv_reinterpret_u64_u8 +#define npyv_reinterpret_u64_f64 npyv_reinterpret_u64_u8 + +#define npyv_reinterpret_s64_s64(X) X +#define npyv_reinterpret_s64_u8(X) ((npyv_s64)X) +#define npyv_reinterpret_s64_s8 npyv_reinterpret_s64_u8 +#define npyv_reinterpret_s64_u16 npyv_reinterpret_s64_u8 +#define npyv_reinterpret_s64_s16 npyv_reinterpret_s64_u8 +#define npyv_reinterpret_s64_u32 npyv_reinterpret_s64_u8 +#define npyv_reinterpret_s64_s32 npyv_reinterpret_s64_u8 +#define npyv_reinterpret_s64_u64 npyv_reinterpret_s64_u8 +#define npyv_reinterpret_s64_f32 npyv_reinterpret_s64_u8 +#define npyv_reinterpret_s64_f64 npyv_reinterpret_s64_u8 + +#define npyv_reinterpret_f32_f32(X) X +#define npyv_reinterpret_f32_u8(X) ((npyv_f32)X) +#define npyv_reinterpret_f32_s8 npyv_reinterpret_f32_u8 +#define npyv_reinterpret_f32_u16 npyv_reinterpret_f32_u8 +#define npyv_reinterpret_f32_s16 npyv_reinterpret_f32_u8 +#define npyv_reinterpret_f32_u32 npyv_reinterpret_f32_u8 +#define npyv_reinterpret_f32_s32 npyv_reinterpret_f32_u8 +#define npyv_reinterpret_f32_u64 npyv_reinterpret_f32_u8 +#define npyv_reinterpret_f32_s64 npyv_reinterpret_f32_u8 +#define npyv_reinterpret_f32_f64 npyv_reinterpret_f32_u8 + +#define npyv_reinterpret_f64_f64(X) X +#define npyv_reinterpret_f64_u8(X) ((npyv_f64)X) +#define npyv_reinterpret_f64_s8 npyv_reinterpret_f64_u8 +#define npyv_reinterpret_f64_u16 npyv_reinterpret_f64_u8 +#define npyv_reinterpret_f64_s16 npyv_reinterpret_f64_u8 +#define npyv_reinterpret_f64_u32 npyv_reinterpret_f64_u8 +#define npyv_reinterpret_f64_s32 npyv_reinterpret_f64_u8 +#define npyv_reinterpret_f64_u64 npyv_reinterpret_f64_u8 +#define npyv_reinterpret_f64_s64 npyv_reinterpret_f64_u8 +#define npyv_reinterpret_f64_f32 npyv_reinterpret_f64_u8 + +// Only required by AVX2/AVX512 +#define npyv_cleanup() ((void)0) + +#endif // _NPY_SIMD_VSX_MISC_H diff --git a/numpy/core/src/common/simd/vsx/operators.h b/numpy/core/src/common/simd/vsx/operators.h new file mode 100644 index 000000000000..d34057ff3f38 --- /dev/null +++ b/numpy/core/src/common/simd/vsx/operators.h @@ -0,0 +1,244 @@ +#ifndef NPY_SIMD + #error "Not a standalone header" +#endif + +#ifndef _NPY_SIMD_VSX_OPERATORS_H +#define _NPY_SIMD_VSX_OPERATORS_H + +/*************************** + * Shifting + ***************************/ + +// Left +#define npyv_shl_u16(A, C) vec_sl(A, npyv_setall_u16(C)) +#define npyv_shl_s16(A, C) vec_sl(A, npyv_setall_u16(C)) +#define npyv_shl_u32(A, C) vec_sl(A, npyv_setall_u32(C)) +#define npyv_shl_s32(A, C) vec_sl(A, npyv_setall_u32(C)) +#define npyv_shl_u64(A, C) vec_sl(A, npyv_setall_u64(C)) +#define npyv_shl_s64(A, C) vec_sl(A, npyv_setall_u64(C)) + +// Left by an immediate constant +#define npyv_shli_u16 npyv_shl_u16 +#define npyv_shli_s16 npyv_shl_s16 +#define npyv_shli_u32 npyv_shl_u32 +#define npyv_shli_s32 npyv_shl_s32 +#define npyv_shli_u64 npyv_shl_u64 +#define npyv_shli_s64 npyv_shl_s64 + +// Right +#define npyv_shr_u16(A, C) vec_sr(A, npyv_setall_u16(C)) +#define npyv_shr_s16(A, C) vec_sra(A, npyv_setall_u16(C)) +#define npyv_shr_u32(A, C) vec_sr(A, npyv_setall_u32(C)) +#define npyv_shr_s32(A, C) vec_sra(A, npyv_setall_u32(C)) +#define npyv_shr_u64(A, C) vec_sr(A, npyv_setall_u64(C)) +#define npyv_shr_s64(A, C) vec_sra(A, npyv_setall_u64(C)) + +// Right by an immediate constant +#define npyv_shri_u16 npyv_shr_u16 +#define npyv_shri_s16 npyv_shr_s16 +#define npyv_shri_u32 npyv_shr_u32 +#define npyv_shri_s32 npyv_shr_s32 +#define npyv_shri_u64 npyv_shr_u64 +#define npyv_shri_s64 npyv_shr_s64 + +/*************************** + * Logical + ***************************/ +#define NPYV_IMPL_VSX_BIN_CAST(INTRIN, SFX, CAST) \ + NPY_FINLINE npyv_##SFX npyv_##INTRIN##_##SFX(npyv_##SFX a, npyv_##SFX b) \ + { return (npyv_##SFX)vec_##INTRIN((CAST)a, (CAST)b); } + +// Up to GCC 6 logical intrinsics don't support bool long long +#if defined(__GNUC__) && __GNUC__ <= 6 + #define NPYV_IMPL_VSX_BIN_B64(INTRIN) NPYV_IMPL_VSX_BIN_CAST(INTRIN, b64, npyv_u64) +#else + #define NPYV_IMPL_VSX_BIN_B64(INTRIN) NPYV_IMPL_VSX_BIN_CAST(INTRIN, b64, npyv_b64) +#endif +// AND +#define npyv_and_u8 vec_and +#define npyv_and_s8 vec_and +#define npyv_and_u16 vec_and +#define npyv_and_s16 vec_and +#define npyv_and_u32 vec_and +#define npyv_and_s32 vec_and +#define npyv_and_u64 vec_and +#define npyv_and_s64 vec_and +#define npyv_and_f32 vec_and +#define npyv_and_f64 vec_and +#define npyv_and_b8 vec_and +#define npyv_and_b16 vec_and +#define npyv_and_b32 vec_and +NPYV_IMPL_VSX_BIN_B64(and) + +// OR +#define npyv_or_u8 vec_or +#define npyv_or_s8 vec_or +#define npyv_or_u16 vec_or +#define npyv_or_s16 vec_or +#define npyv_or_u32 vec_or +#define npyv_or_s32 vec_or +#define npyv_or_u64 vec_or +#define npyv_or_s64 vec_or +#define npyv_or_f32 vec_or +#define npyv_or_f64 vec_or +#define npyv_or_b8 vec_or +#define npyv_or_b16 vec_or +#define npyv_or_b32 vec_or +NPYV_IMPL_VSX_BIN_B64(or) + +// XOR +#define npyv_xor_u8 vec_xor +#define npyv_xor_s8 vec_xor +#define npyv_xor_u16 vec_xor +#define npyv_xor_s16 vec_xor +#define npyv_xor_u32 vec_xor +#define npyv_xor_s32 vec_xor +#define npyv_xor_u64 vec_xor +#define npyv_xor_s64 vec_xor +#define npyv_xor_f32 vec_xor +#define npyv_xor_f64 vec_xor +#define npyv_xor_b8 vec_xor +#define npyv_xor_b16 vec_xor +#define npyv_xor_b32 vec_xor +NPYV_IMPL_VSX_BIN_B64(xor) + +// NOT +// note: we implement npyv_not_b*(boolean types) for internal use*/ +#define NPYV_IMPL_VSX_NOT_INT(VEC_LEN) \ + NPY_FINLINE npyv_u##VEC_LEN npyv_not_u##VEC_LEN(npyv_u##VEC_LEN a) \ + { return vec_nor(a, a); } \ + NPY_FINLINE npyv_s##VEC_LEN npyv_not_s##VEC_LEN(npyv_s##VEC_LEN a) \ + { return vec_nor(a, a); } \ + NPY_FINLINE npyv_b##VEC_LEN npyv_not_b##VEC_LEN(npyv_b##VEC_LEN a) \ + { return vec_nor(a, a); } + +NPYV_IMPL_VSX_NOT_INT(8) +NPYV_IMPL_VSX_NOT_INT(16) +NPYV_IMPL_VSX_NOT_INT(32) + +// up to gcc5 vec_nor doesn't support bool long long +#if defined(__GNUC__) && __GNUC__ > 5 + NPYV_IMPL_VSX_NOT_INT(64) +#else + NPY_FINLINE npyv_u64 npyv_not_u64(npyv_u64 a) + { return vec_nor(a, a); } + NPY_FINLINE npyv_s64 npyv_not_s64(npyv_s64 a) + { return vec_nor(a, a); } + NPY_FINLINE npyv_b64 npyv_not_b64(npyv_b64 a) + { return (npyv_b64)vec_nor((npyv_u64)a, (npyv_u64)a); } +#endif + +NPY_FINLINE npyv_f32 npyv_not_f32(npyv_f32 a) +{ return vec_nor(a, a); } +NPY_FINLINE npyv_f64 npyv_not_f64(npyv_f64 a) +{ return vec_nor(a, a); } + +/*************************** + * Comparison + ***************************/ + +// Int Equal +#define npyv_cmpeq_u8 vec_cmpeq +#define npyv_cmpeq_s8 vec_cmpeq +#define npyv_cmpeq_u16 vec_cmpeq +#define npyv_cmpeq_s16 vec_cmpeq +#define npyv_cmpeq_u32 vec_cmpeq +#define npyv_cmpeq_s32 vec_cmpeq +#define npyv_cmpeq_u64 vec_cmpeq +#define npyv_cmpeq_s64 vec_cmpeq +#define npyv_cmpeq_f32 vec_cmpeq +#define npyv_cmpeq_f64 vec_cmpeq + +// Int Not Equal +#if defined(NPY_HAVE_VSX3) && (!defined(__GNUC__) || defined(vec_cmpne)) + // vec_cmpne supported by gcc since version 7 + #define npyv_cmpneq_u8 vec_cmpne + #define npyv_cmpneq_s8 vec_cmpne + #define npyv_cmpneq_u16 vec_cmpne + #define npyv_cmpneq_s16 vec_cmpne + #define npyv_cmpneq_u32 vec_cmpne + #define npyv_cmpneq_s32 vec_cmpne + #define npyv_cmpneq_u64 vec_cmpne + #define npyv_cmpneq_s64 vec_cmpne + #define npyv_cmpneq_f32 vec_cmpne + #define npyv_cmpneq_f64 vec_cmpne +#else + #define npyv_cmpneq_u8(A, B) npyv_not_b8(vec_cmpeq(A, B)) + #define npyv_cmpneq_s8(A, B) npyv_not_b8(vec_cmpeq(A, B)) + #define npyv_cmpneq_u16(A, B) npyv_not_b16(vec_cmpeq(A, B)) + #define npyv_cmpneq_s16(A, B) npyv_not_b16(vec_cmpeq(A, B)) + #define npyv_cmpneq_u32(A, B) npyv_not_b32(vec_cmpeq(A, B)) + #define npyv_cmpneq_s32(A, B) npyv_not_b32(vec_cmpeq(A, B)) + #define npyv_cmpneq_u64(A, B) npyv_not_b64(vec_cmpeq(A, B)) + #define npyv_cmpneq_s64(A, B) npyv_not_b64(vec_cmpeq(A, B)) + #define npyv_cmpneq_f32(A, B) npyv_not_b32(vec_cmpeq(A, B)) + #define npyv_cmpneq_f64(A, B) npyv_not_b64(vec_cmpeq(A, B)) +#endif + +// Greater than +#define npyv_cmpgt_u8 vec_cmpgt +#define npyv_cmpgt_s8 vec_cmpgt +#define npyv_cmpgt_u16 vec_cmpgt +#define npyv_cmpgt_s16 vec_cmpgt +#define npyv_cmpgt_u32 vec_cmpgt +#define npyv_cmpgt_s32 vec_cmpgt +#define npyv_cmpgt_u64 vec_cmpgt +#define npyv_cmpgt_s64 vec_cmpgt +#define npyv_cmpgt_f32 vec_cmpgt +#define npyv_cmpgt_f64 vec_cmpgt + +// Greater than or equal +// up to gcc5 vec_cmpge only supports single and double precision +#if defined(__GNUC__) && __GNUC__ > 5 + #define npyv_cmpge_u8 vec_cmpge + #define npyv_cmpge_s8 vec_cmpge + #define npyv_cmpge_u16 vec_cmpge + #define npyv_cmpge_s16 vec_cmpge + #define npyv_cmpge_u32 vec_cmpge + #define npyv_cmpge_s32 vec_cmpge + #define npyv_cmpge_u64 vec_cmpge + #define npyv_cmpge_s64 vec_cmpge +#else + #define npyv_cmpge_u8(A, B) npyv_not_b8(vec_cmpgt(B, A)) + #define npyv_cmpge_s8(A, B) npyv_not_b8(vec_cmpgt(B, A)) + #define npyv_cmpge_u16(A, B) npyv_not_b16(vec_cmpgt(B, A)) + #define npyv_cmpge_s16(A, B) npyv_not_b16(vec_cmpgt(B, A)) + #define npyv_cmpge_u32(A, B) npyv_not_b32(vec_cmpgt(B, A)) + #define npyv_cmpge_s32(A, B) npyv_not_b32(vec_cmpgt(B, A)) + #define npyv_cmpge_u64(A, B) npyv_not_b64(vec_cmpgt(B, A)) + #define npyv_cmpge_s64(A, B) npyv_not_b64(vec_cmpgt(B, A)) +#endif +#define npyv_cmpge_f32 vec_cmpge +#define npyv_cmpge_f64 vec_cmpge + +// Less than +#define npyv_cmplt_u8(A, B) npyv_cmpgt_u8(B, A) +#define npyv_cmplt_s8(A, B) npyv_cmpgt_s8(B, A) +#define npyv_cmplt_u16(A, B) npyv_cmpgt_u16(B, A) +#define npyv_cmplt_s16(A, B) npyv_cmpgt_s16(B, A) +#define npyv_cmplt_u32(A, B) npyv_cmpgt_u32(B, A) +#define npyv_cmplt_s32(A, B) npyv_cmpgt_s32(B, A) +#define npyv_cmplt_u64(A, B) npyv_cmpgt_u64(B, A) +#define npyv_cmplt_s64(A, B) npyv_cmpgt_s64(B, A) +#define npyv_cmplt_f32(A, B) npyv_cmpgt_f32(B, A) +#define npyv_cmplt_f64(A, B) npyv_cmpgt_f64(B, A) + +// Less than or equal +#define npyv_cmple_u8(A, B) npyv_cmpge_u8(B, A) +#define npyv_cmple_s8(A, B) npyv_cmpge_s8(B, A) +#define npyv_cmple_u16(A, B) npyv_cmpge_u16(B, A) +#define npyv_cmple_s16(A, B) npyv_cmpge_s16(B, A) +#define npyv_cmple_u32(A, B) npyv_cmpge_u32(B, A) +#define npyv_cmple_s32(A, B) npyv_cmpge_s32(B, A) +#define npyv_cmple_u64(A, B) npyv_cmpge_u64(B, A) +#define npyv_cmple_s64(A, B) npyv_cmpge_s64(B, A) +#define npyv_cmple_f32(A, B) npyv_cmpge_f32(B, A) +#define npyv_cmple_f64(A, B) npyv_cmpge_f64(B, A) + +// check special cases +NPY_FINLINE npyv_b32 npyv_notnan_f32(npyv_f32 a) +{ return vec_cmpeq(a, a); } +NPY_FINLINE npyv_b64 npyv_notnan_f64(npyv_f64 a) +{ return vec_cmpeq(a, a); } + +#endif // _NPY_SIMD_VSX_OPERATORS_H diff --git a/numpy/core/src/common/simd/vsx/reorder.h b/numpy/core/src/common/simd/vsx/reorder.h new file mode 100644 index 000000000000..6533e50933d2 --- /dev/null +++ b/numpy/core/src/common/simd/vsx/reorder.h @@ -0,0 +1,106 @@ +#ifndef NPY_SIMD + #error "Not a standalone header" +#endif + +#ifndef _NPY_SIMD_VSX_REORDER_H +#define _NPY_SIMD_VSX_REORDER_H + +// combine lower part of two vectors +#define npyv__combinel(A, B) vec_mergeh((npyv_u64)(A), (npyv_u64)(B)) +#define npyv_combinel_u8(A, B) ((npyv_u8) npyv__combinel(A, B)) +#define npyv_combinel_s8(A, B) ((npyv_s8) npyv__combinel(A, B)) +#define npyv_combinel_u16(A, B) ((npyv_u16)npyv__combinel(A, B)) +#define npyv_combinel_s16(A, B) ((npyv_s16)npyv__combinel(A, B)) +#define npyv_combinel_u32(A, B) ((npyv_u32)npyv__combinel(A, B)) +#define npyv_combinel_s32(A, B) ((npyv_s32)npyv__combinel(A, B)) +#define npyv_combinel_u64 vec_mergeh +#define npyv_combinel_s64 vec_mergeh +#define npyv_combinel_f32(A, B) ((npyv_f32)npyv__combinel(A, B)) +#define npyv_combinel_f64 vec_mergeh + +// combine higher part of two vectors +#define npyv__combineh(A, B) vec_mergel((npyv_u64)(A), (npyv_u64)(B)) +#define npyv_combineh_u8(A, B) ((npyv_u8) npyv__combineh(A, B)) +#define npyv_combineh_s8(A, B) ((npyv_s8) npyv__combineh(A, B)) +#define npyv_combineh_u16(A, B) ((npyv_u16)npyv__combineh(A, B)) +#define npyv_combineh_s16(A, B) ((npyv_s16)npyv__combineh(A, B)) +#define npyv_combineh_u32(A, B) ((npyv_u32)npyv__combineh(A, B)) +#define npyv_combineh_s32(A, B) ((npyv_s32)npyv__combineh(A, B)) +#define npyv_combineh_u64 vec_mergel +#define npyv_combineh_s64 vec_mergel +#define npyv_combineh_f32(A, B) ((npyv_f32)npyv__combineh(A, B)) +#define npyv_combineh_f64 vec_mergel + +/* + * combine: combine two vectors from lower and higher parts of two other vectors + * zip: interleave two vectors +*/ +#define NPYV_IMPL_VSX_COMBINE_ZIP(T_VEC, SFX) \ + NPY_FINLINE T_VEC##x2 npyv_combine_##SFX(T_VEC a, T_VEC b) \ + { \ + T_VEC##x2 r; \ + r.val[0] = NPY_CAT(npyv_combinel_, SFX)(a, b); \ + r.val[1] = NPY_CAT(npyv_combineh_, SFX)(a, b); \ + return r; \ + } \ + NPY_FINLINE T_VEC##x2 npyv_zip_##SFX(T_VEC a, T_VEC b) \ + { \ + T_VEC##x2 r; \ + r.val[0] = vec_mergeh(a, b); \ + r.val[1] = vec_mergel(a, b); \ + return r; \ + } + +NPYV_IMPL_VSX_COMBINE_ZIP(npyv_u8, u8) +NPYV_IMPL_VSX_COMBINE_ZIP(npyv_s8, s8) +NPYV_IMPL_VSX_COMBINE_ZIP(npyv_u16, u16) +NPYV_IMPL_VSX_COMBINE_ZIP(npyv_s16, s16) +NPYV_IMPL_VSX_COMBINE_ZIP(npyv_u32, u32) +NPYV_IMPL_VSX_COMBINE_ZIP(npyv_s32, s32) +NPYV_IMPL_VSX_COMBINE_ZIP(npyv_u64, u64) +NPYV_IMPL_VSX_COMBINE_ZIP(npyv_s64, s64) +NPYV_IMPL_VSX_COMBINE_ZIP(npyv_f32, f32) +NPYV_IMPL_VSX_COMBINE_ZIP(npyv_f64, f64) + +// Reverse elements of each 64-bit lane +NPY_FINLINE npyv_u8 npyv_rev64_u8(npyv_u8 a) +{ +#if defined(NPY_HAVE_VSX3) && ((defined(__GNUC__) && __GNUC__ > 7) || defined(__IBMC__)) + return (npyv_u8)vec_revb((npyv_u64)a); +#elif defined(NPY_HAVE_VSX3) && defined(NPY_HAVE_VSX_ASM) + npyv_u8 ret; + __asm__ ("xxbrd %x0,%x1" : "=wa" (ret) : "wa" (a)); + return ret; +#else + const npyv_u8 idx = npyv_set_u8( + 7, 6, 5, 4, 3, 2, 1, 0,/*64*/15, 14, 13, 12, 11, 10, 9, 8 + ); + return vec_perm(a, a, idx); +#endif +} +NPY_FINLINE npyv_s8 npyv_rev64_s8(npyv_s8 a) +{ return (npyv_s8)npyv_rev64_u8((npyv_u8)a); } + +NPY_FINLINE npyv_u16 npyv_rev64_u16(npyv_u16 a) +{ + const npyv_u8 idx = npyv_set_u8( + 6, 7, 4, 5, 2, 3, 0, 1,/*64*/14, 15, 12, 13, 10, 11, 8, 9 + ); + return vec_perm(a, a, idx); +} +NPY_FINLINE npyv_s16 npyv_rev64_s16(npyv_s16 a) +{ return (npyv_s16)npyv_rev64_u16((npyv_u16)a); } + +NPY_FINLINE npyv_u32 npyv_rev64_u32(npyv_u32 a) +{ + const npyv_u8 idx = npyv_set_u8( + 4, 5, 6, 7, 0, 1, 2, 3,/*64*/12, 13, 14, 15, 8, 9, 10, 11 + ); + return vec_perm(a, a, idx); +} +NPY_FINLINE npyv_s32 npyv_rev64_s32(npyv_s32 a) +{ return (npyv_s32)npyv_rev64_u32((npyv_u32)a); } +NPY_FINLINE npyv_f32 npyv_rev64_f32(npyv_f32 a) +{ return (npyv_f32)npyv_rev64_u32((npyv_u32)a); } + +#endif // _NPY_SIMD_VSX_REORDER_H diff --git a/numpy/core/src/common/simd/vsx/vsx.h b/numpy/core/src/common/simd/vsx/vsx.h new file mode 100644 index 000000000000..66b76208f042 --- /dev/null +++ b/numpy/core/src/common/simd/vsx/vsx.h @@ -0,0 +1,76 @@ +#ifndef _NPY_SIMD_H_ + #error "Not a standalone header" +#endif + +#if defined(__GNUC__) && __GNUC__ <= 7 + /** + * GCC <= 7 produces ambiguous warning caused by -Werror=maybe-uninitialized, + * when certain intrinsics involved. `vec_ld` is one of them but it seemed to work fine, + * and suppressing the warning wouldn't affect its functionality. + */ + #pragma GCC diagnostic ignored "-Wuninitialized" + #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif + +#define NPY_SIMD 128 +#define NPY_SIMD_WIDTH 16 +#define NPY_SIMD_F64 1 +#define NPY_SIMD_FMA3 1 // native support + +typedef __vector unsigned char npyv_u8; +typedef __vector signed char npyv_s8; +typedef __vector unsigned short npyv_u16; +typedef __vector signed short npyv_s16; +typedef __vector unsigned int npyv_u32; +typedef __vector signed int npyv_s32; +typedef __vector unsigned long long npyv_u64; +typedef __vector signed long long npyv_s64; +typedef __vector float npyv_f32; +typedef __vector double npyv_f64; + +typedef struct { npyv_u8 val[2]; } npyv_u8x2; +typedef struct { npyv_s8 val[2]; } npyv_s8x2; +typedef struct { npyv_u16 val[2]; } npyv_u16x2; +typedef struct { npyv_s16 val[2]; } npyv_s16x2; +typedef struct { npyv_u32 val[2]; } npyv_u32x2; +typedef struct { npyv_s32 val[2]; } npyv_s32x2; +typedef struct { npyv_u64 val[2]; } npyv_u64x2; +typedef struct { npyv_s64 val[2]; } npyv_s64x2; +typedef struct { npyv_f32 val[2]; } npyv_f32x2; +typedef struct { npyv_f64 val[2]; } npyv_f64x2; + +typedef struct { npyv_u8 val[3]; } npyv_u8x3; +typedef struct { npyv_s8 val[3]; } npyv_s8x3; +typedef struct { npyv_u16 val[3]; } npyv_u16x3; +typedef struct { npyv_s16 val[3]; } npyv_s16x3; +typedef struct { npyv_u32 val[3]; } npyv_u32x3; +typedef struct { npyv_s32 val[3]; } npyv_s32x3; +typedef struct { npyv_u64 val[3]; } npyv_u64x3; +typedef struct { npyv_s64 val[3]; } npyv_s64x3; +typedef struct { npyv_f32 val[3]; } npyv_f32x3; +typedef struct { npyv_f64 val[3]; } npyv_f64x3; + +#define npyv_nlanes_u8 16 +#define npyv_nlanes_s8 16 +#define npyv_nlanes_u16 8 +#define npyv_nlanes_s16 8 +#define npyv_nlanes_u32 4 +#define npyv_nlanes_s32 4 +#define npyv_nlanes_u64 2 +#define npyv_nlanes_s64 2 +#define npyv_nlanes_f32 4 +#define npyv_nlanes_f64 2 + +// using __bool with typdef cause ambiguous errors +#define npyv_b8 __vector __bool char +#define npyv_b16 __vector __bool short +#define npyv_b32 __vector __bool int +#define npyv_b64 __vector __bool long long + +#include "memory.h" +#include "misc.h" +#include "reorder.h" +#include "operators.h" +#include "conversion.h" +#include "arithmetic.h" +#include "math.h" diff --git a/numpy/core/src/private/templ_common.h.src b/numpy/core/src/common/templ_common.h.src similarity index 100% rename from numpy/core/src/private/templ_common.h.src rename to numpy/core/src/common/templ_common.h.src diff --git a/numpy/core/src/common/ucsnarrow.c b/numpy/core/src/common/ucsnarrow.c new file mode 100644 index 000000000000..4bea4beee384 --- /dev/null +++ b/numpy/core/src/common/ucsnarrow.c @@ -0,0 +1,71 @@ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE + +#define PY_SSIZE_T_CLEAN +#include <Python.h> + +#include "numpy/arrayobject.h" +#include "numpy/npy_math.h" + +#include "npy_config.h" + +#include "npy_pycompat.h" +#include "ctors.h" + +/* + * This file originally contained functions only needed on narrow builds of + * Python for converting back and forth between the NumPy Unicode data-type + * (always 4-bytes) and the Python Unicode scalar (2-bytes on a narrow build). + * + * This "narrow" interface is now deprecated in python and unused in NumPy. + */ + +/* + * Returns a PyUnicodeObject initialized from a buffer containing + * UCS4 unicode. + * + * Parameters + * ---------- + * src: char * + * Pointer to buffer containing UCS4 unicode. + * size: Py_ssize_t + * Size of buffer in bytes. + * swap: int + * If true, the data will be swapped. + * align: int + * If true, the data will be aligned. + * + * Returns + * ------- + * new_reference: PyUnicodeObject + */ +NPY_NO_EXPORT PyUnicodeObject * +PyUnicode_FromUCS4(char const *src_char, Py_ssize_t size, int swap, int align) +{ + Py_ssize_t ucs4len = size / sizeof(npy_ucs4); + npy_ucs4 const *src = (npy_ucs4 const *)src_char; + npy_ucs4 *buf = NULL; + + /* swap and align if needed */ + if (swap || align) { + buf = (npy_ucs4 *)malloc(size); + if (buf == NULL) { + PyErr_NoMemory(); + return NULL; + } + memcpy(buf, src, size); + if (swap) { + byte_swap_vector(buf, ucs4len, sizeof(npy_ucs4)); + } + src = buf; + } + + /* trim trailing zeros */ + while (ucs4len > 0 && src[ucs4len - 1] == 0) { + ucs4len--; + } + PyUnicodeObject *ret = (PyUnicodeObject *)PyUnicode_FromKindAndData( + PyUnicode_4BYTE_KIND, src, ucs4len); + free(buf); + return ret; +} diff --git a/numpy/core/src/common/ucsnarrow.h b/numpy/core/src/common/ucsnarrow.h new file mode 100644 index 000000000000..6fe157199877 --- /dev/null +++ b/numpy/core/src/common/ucsnarrow.h @@ -0,0 +1,7 @@ +#ifndef NUMPY_CORE_SRC_COMMON_NPY_UCSNARROW_H_ +#define NUMPY_CORE_SRC_COMMON_NPY_UCSNARROW_H_ + +NPY_NO_EXPORT PyUnicodeObject * +PyUnicode_FromUCS4(char *src, Py_ssize_t size, int swap, int align); + +#endif /* NUMPY_CORE_SRC_COMMON_NPY_UCSNARROW_H_ */ diff --git a/numpy/core/src/common/ufunc_override.c b/numpy/core/src/common/ufunc_override.c new file mode 100644 index 000000000000..d510f185acf3 --- /dev/null +++ b/numpy/core/src/common/ufunc_override.c @@ -0,0 +1,127 @@ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE + +#include "npy_pycompat.h" +#include "get_attr_string.h" +#include "npy_import.h" +#include "ufunc_override.h" + +/* + * Check whether an object has __array_ufunc__ defined on its class and it + * is not the default, i.e., the object is not an ndarray, and its + * __array_ufunc__ is not the same as that of ndarray. + * + * Returns a new reference, the value of type(obj).__array_ufunc__ if it + * exists and is different from that of ndarray, and NULL otherwise. + */ +NPY_NO_EXPORT PyObject * +PyUFuncOverride_GetNonDefaultArrayUfunc(PyObject *obj) +{ + static PyObject *ndarray_array_ufunc = NULL; + PyObject *cls_array_ufunc; + + /* On first entry, cache ndarray's __array_ufunc__ */ + if (ndarray_array_ufunc == NULL) { + ndarray_array_ufunc = PyObject_GetAttrString((PyObject *)&PyArray_Type, + "__array_ufunc__"); + } + + /* Fast return for ndarray */ + if (PyArray_CheckExact(obj)) { + return NULL; + } + /* + * Does the class define __array_ufunc__? (Note that LookupSpecial has fast + * return for basic python types, so no need to worry about those here) + */ + cls_array_ufunc = PyArray_LookupSpecial(obj, "__array_ufunc__"); + if (cls_array_ufunc == NULL) { + if (PyErr_Occurred()) { + PyErr_Clear(); /* TODO[gh-14801]: propagate crashes during attribute access? */ + } + return NULL; + } + /* Ignore if the same as ndarray.__array_ufunc__ */ + if (cls_array_ufunc == ndarray_array_ufunc) { + Py_DECREF(cls_array_ufunc); + return NULL; + } + return cls_array_ufunc; +} + +/* + * Check whether an object has __array_ufunc__ defined on its class and it + * is not the default, i.e., the object is not an ndarray, and its + * __array_ufunc__ is not the same as that of ndarray. + * + * Returns 1 if this is the case, 0 if not. + */ + +NPY_NO_EXPORT int +PyUFunc_HasOverride(PyObject * obj) +{ + PyObject *method = PyUFuncOverride_GetNonDefaultArrayUfunc(obj); + if (method) { + Py_DECREF(method); + return 1; + } + else { + return 0; + } +} + +/* + * Get possible out argument from kwds, and returns the number of outputs + * contained within it: if a tuple, the number of elements in it, 1 otherwise. + * The out argument itself is returned in out_kwd_obj, and the outputs + * in the out_obj array (as borrowed references). + * + * Returns 0 if no outputs found, -1 if kwds is not a dict (with an error set). + */ +NPY_NO_EXPORT int +PyUFuncOverride_GetOutObjects(PyObject *kwds, PyObject **out_kwd_obj, PyObject ***out_objs) +{ + if (kwds == NULL) { + Py_INCREF(Py_None); + *out_kwd_obj = Py_None; + return 0; + } + if (!PyDict_CheckExact(kwds)) { + PyErr_SetString(PyExc_TypeError, + "Internal Numpy error: call to PyUFuncOverride_GetOutObjects " + "with non-dict kwds"); + *out_kwd_obj = NULL; + return -1; + } + /* borrowed reference */ + *out_kwd_obj = _PyDict_GetItemStringWithError(kwds, "out"); + if (*out_kwd_obj == NULL) { + if (PyErr_Occurred()) { + return -1; + } + Py_INCREF(Py_None); + *out_kwd_obj = Py_None; + return 0; + } + if (PyTuple_CheckExact(*out_kwd_obj)) { + /* + * The C-API recommends calling PySequence_Fast before any of the other + * PySequence_Fast* functions. This is required for PyPy + */ + PyObject *seq; + seq = PySequence_Fast(*out_kwd_obj, + "Could not convert object to sequence"); + if (seq == NULL) { + *out_kwd_obj = NULL; + return -1; + } + *out_objs = PySequence_Fast_ITEMS(seq); + *out_kwd_obj = seq; + return PySequence_Fast_GET_SIZE(seq); + } + else { + Py_INCREF(*out_kwd_obj); + *out_objs = out_kwd_obj; + return 1; + } +} diff --git a/numpy/core/src/common/ufunc_override.h b/numpy/core/src/common/ufunc_override.h new file mode 100644 index 000000000000..5da95fb29803 --- /dev/null +++ b/numpy/core/src/common/ufunc_override.h @@ -0,0 +1,38 @@ +#ifndef NUMPY_CORE_SRC_COMMON_UFUNC_OVERRIDE_H_ +#define NUMPY_CORE_SRC_COMMON_UFUNC_OVERRIDE_H_ + +#include "npy_config.h" + +/* + * Check whether an object has __array_ufunc__ defined on its class and it + * is not the default, i.e., the object is not an ndarray, and its + * __array_ufunc__ is not the same as that of ndarray. + * + * Returns a new reference, the value of type(obj).__array_ufunc__ if it + * exists and is different from that of ndarray, and NULL otherwise. + */ +NPY_NO_EXPORT PyObject * +PyUFuncOverride_GetNonDefaultArrayUfunc(PyObject *obj); + +/* + * Check whether an object has __array_ufunc__ defined on its class and it + * is not the default, i.e., the object is not an ndarray, and its + * __array_ufunc__ is not the same as that of ndarray. + * + * Returns 1 if this is the case, 0 if not. + */ +NPY_NO_EXPORT int +PyUFunc_HasOverride(PyObject *obj); + +/* + * Get possible out argument from kwds, and returns the number of outputs + * contained within it: if a tuple, the number of elements in it, 1 otherwise. + * The out argument itself is returned in out_kwd_obj, and the outputs + * in the out_obj array (as borrowed references). + * + * Returns 0 if no outputs found, -1 if kwds is not a dict (with an error set). + */ +NPY_NO_EXPORT int +PyUFuncOverride_GetOutObjects(PyObject *kwds, PyObject **out_kwd_obj, PyObject ***out_objs); + +#endif /* NUMPY_CORE_SRC_COMMON_UFUNC_OVERRIDE_H_ */ diff --git a/numpy/core/src/common/umathmodule.h b/numpy/core/src/common/umathmodule.h new file mode 100644 index 000000000000..6d4169ad5f8a --- /dev/null +++ b/numpy/core/src/common/umathmodule.h @@ -0,0 +1,14 @@ +#ifndef NUMPY_CORE_SRC_COMMON_UMATHMODULE_H_ +#define NUMPY_CORE_SRC_COMMON_UMATHMODULE_H_ + +#include "__umath_generated.c" +#include "__ufunc_api.c" + +NPY_NO_EXPORT PyObject * +get_sfloat_dtype(PyObject *NPY_UNUSED(mod), PyObject *NPY_UNUSED(args)); + +PyObject * add_newdoc_ufunc(PyObject *NPY_UNUSED(dummy), PyObject *args); +PyObject * ufunc_frompyfunc(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *NPY_UNUSED(kwds)); +int initumath(PyObject *m); + +#endif /* NUMPY_CORE_SRC_COMMON_UMATHMODULE_H_ */ diff --git a/numpy/core/src/dummymodule.c b/numpy/core/src/dummymodule.c index 718199f704a7..7284ffd68545 100644 --- a/numpy/core/src/dummymodule.c +++ b/numpy/core/src/dummymodule.c @@ -4,19 +4,19 @@ * This is a dummy module whose purpose is to get distutils to generate the * configuration files before the libraries are made. */ - #define NPY_NO_DEPRECATED_API NPY_API_VERSION #define NO_IMPORT_ARRAY +#define PY_SSIZE_T_CLEAN #include <Python.h> -#include <npy_pycompat.h> + +#include "npy_pycompat.h" static struct PyMethodDef methods[] = { {NULL, NULL, 0, NULL} }; -#if defined(NPY_PY3K) static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "dummy", @@ -28,10 +28,8 @@ static struct PyModuleDef moduledef = { NULL, NULL }; -#endif /* Initialization function for the module */ -#if defined(NPY_PY3K) PyMODINIT_FUNC PyInit__dummy(void) { PyObject *m; m = PyModule_Create(&moduledef); @@ -40,9 +38,3 @@ PyMODINIT_FUNC PyInit__dummy(void) { } return m; } -#else -PyMODINIT_FUNC -init_dummy(void) { - Py_InitModule("_dummy", methods); -} -#endif diff --git a/numpy/core/src/multiarray/_datetime.h b/numpy/core/src/multiarray/_datetime.h index 3db1254d488a..2ebeb1dff988 100644 --- a/numpy/core/src/multiarray/_datetime.h +++ b/numpy/core/src/multiarray/_datetime.h @@ -1,7 +1,7 @@ -#ifndef _NPY_PRIVATE__DATETIME_H_ -#define _NPY_PRIVATE__DATETIME_H_ +#ifndef NUMPY_CORE_SRC_MULTIARRAY__DATETIME_H_ +#define NUMPY_CORE_SRC_MULTIARRAY__DATETIME_H_ -extern NPY_NO_EXPORT char *_datetime_strings[NPY_DATETIME_NUMUNITS]; +extern NPY_NO_EXPORT char const *_datetime_strings[NPY_DATETIME_NUMUNITS]; extern NPY_NO_EXPORT int _days_per_month_table[2][12]; NPY_NO_EXPORT void @@ -38,6 +38,10 @@ create_datetime_dtype_with_unit(int type_num, NPY_DATETIMEUNIT unit); NPY_NO_EXPORT PyArray_DatetimeMetaData * get_datetime_metadata_from_dtype(PyArray_Descr *dtype); +NPY_NO_EXPORT int +find_string_array_datetime64_type(PyArrayObject *arr, + PyArray_DatetimeMetaData *meta); + /* * Both type1 and type2 must be either NPY_DATETIME or NPY_TIMEDELTA. * Applies the type promotion rules between the two types, returning @@ -68,7 +72,7 @@ days_to_month_number(npy_datetime days); * Returns 0 on success, -1 on failure. */ NPY_NO_EXPORT int -parse_datetime_metadata_from_metastr(char *metastr, Py_ssize_t len, +parse_datetime_metadata_from_metastr(char const *metastr, Py_ssize_t len, PyArray_DatetimeMetaData *out_meta); @@ -78,7 +82,7 @@ parse_datetime_metadata_from_metastr(char *metastr, Py_ssize_t len, * contain its string length. */ NPY_NO_EXPORT PyArray_Descr * -parse_dtype_from_datetime_typestr(char *typestr, Py_ssize_t len); +parse_dtype_from_datetime_typestr(char const *typestr, Py_ssize_t len); /* * Converts a substring given by 'str' and 'len' into @@ -88,7 +92,7 @@ parse_dtype_from_datetime_typestr(char *typestr, Py_ssize_t len); * Returns 0 on success, -1 on failure. */ NPY_NO_EXPORT NPY_DATETIMEUNIT -parse_datetime_unit_from_string(char *str, Py_ssize_t len, char *metastr); +parse_datetime_unit_from_string(char const *str, Py_ssize_t len, char const *metastr); /* * Translate divisors into multiples of smaller units. @@ -99,7 +103,7 @@ parse_datetime_unit_from_string(char *str, Py_ssize_t len, char *metastr); */ NPY_NO_EXPORT int convert_datetime_divisor_to_multiple(PyArray_DatetimeMetaData *meta, - int den, char *metastr); + int den, char const *metastr); /* * Determines whether the 'divisor' metadata divides evenly into @@ -196,17 +200,15 @@ convert_pyobject_to_datetime_metadata(PyObject *obj, PyArray_DatetimeMetaData *out_meta); /* - * 'ret' is a PyUString containing the datetime string, and this - * function appends the metadata string to it. + * Returns datetime metadata as a new reference a Unicode object. + * Returns NULL on error. * * If 'skip_brackets' is true, skips the '[]'. * - * This function steals the reference 'ret' */ NPY_NO_EXPORT PyObject * -append_metastr_to_string(PyArray_DatetimeMetaData *meta, - int skip_brackets, - PyObject *ret); +metastr_to_unicode(PyArray_DatetimeMetaData *meta, int skip_brackets); + /* * Tests for and converts a Python datetime.datetime or datetime.date @@ -371,4 +373,7 @@ datetime_arange(PyObject *start, PyObject *stop, PyObject *step, NPY_NO_EXPORT PyArray_Descr * find_object_datetime_type(PyObject *obj, int type_num); -#endif +NPY_NO_EXPORT int +PyArray_InitializeDatetimeCasts(void); + +#endif /* NUMPY_CORE_SRC_MULTIARRAY__DATETIME_H_ */ diff --git a/numpy/core/src/multiarray/_multiarray_tests.c.src b/numpy/core/src/multiarray/_multiarray_tests.c.src index cba96a4c2b5e..9486b7cffa5b 100644 --- a/numpy/core/src/multiarray/_multiarray_tests.c.src +++ b/numpy/core/src/multiarray/_multiarray_tests.c.src @@ -1,17 +1,48 @@ /* -*-c-*- */ -#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define PY_SSIZE_T_CLEAN #include <Python.h> + +#define NPY_NO_DEPRECATED_API NPY_API_VERSION #define _NPY_NO_DEPRECATIONS /* for NPY_CHAR */ #include "numpy/arrayobject.h" #include "numpy/arrayscalars.h" #include "numpy/npy_math.h" #include "numpy/halffloat.h" +#include "common.h" +#include "npy_argparse.h" #include "mem_overlap.h" #include "npy_extint128.h" -#include "common.h" +#include "array_method.h" +#include "npy_hashtable.h" +#include "dtypemeta.h" + +#if defined(MS_WIN32) || defined(__CYGWIN__) +#define EXPORT(x) __declspec(dllexport) x +#else +#define EXPORT(x) x +#endif #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) + +static PyObject * +argparse_example_function(PyObject *NPY_UNUSED(mod), + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) +{ + NPY_PREPARE_ARGPARSER; + int arg1; + PyObject *arg2, *arg3, *arg4; + if (npy_parse_arguments("func", args, len_args, kwnames, + "", &PyArray_PythonPyIntFromInt, &arg1, + "arg2", NULL, &arg2, + "|arg3", NULL, &arg3, + "$arg3", NULL, &arg4, + NULL, NULL, NULL) < 0) { + return NULL; + } + Py_RETURN_NONE; +} + /* test PyArray_IsPythonScalar, before including private py3 compat header */ static PyObject * IsPythonScalar(PyObject * dummy, PyObject *args) @@ -30,6 +61,13 @@ IsPythonScalar(PyObject * dummy, PyObject *args) #include "npy_pycompat.h" + +/** Function to test calling via ctypes */ +EXPORT(void*) forward_pointer(void *x) +{ + return x; +} + /* * TODO: * - Handle mode @@ -41,7 +79,7 @@ IsPythonScalar(PyObject * dummy, PyObject *args) * #typenum = NPY_DOUBLE, NPY_INT# */ static int copy_@name@(PyArrayIterObject *itx, PyArrayNeighborhoodIterObject *niterx, - npy_intp *bounds, + npy_intp const *bounds, PyObject **out) { npy_intp i, j; @@ -53,7 +91,7 @@ static int copy_@name@(PyArrayIterObject *itx, PyArrayNeighborhoodIterObject *ni * For each point in itx, copy the current neighborhood into an array which * is appended at the output list */ - for (i = 0; i < itx->size; ++i) { + for (i = itx->index; i < itx->size; ++i) { PyArrayNeighborhoodIter_Reset(niterx); for (j = 0; j < PyArray_NDIM(itx->ao); ++j) { @@ -83,7 +121,7 @@ static int copy_@name@(PyArrayIterObject *itx, PyArrayNeighborhoodIterObject *ni /**end repeat**/ static int copy_object(PyArrayIterObject *itx, PyArrayNeighborhoodIterObject *niterx, - npy_intp *bounds, + npy_intp const *bounds, PyObject **out) { npy_intp i, j; @@ -96,7 +134,7 @@ static int copy_object(PyArrayIterObject *itx, PyArrayNeighborhoodIterObject *ni * For each point in itx, copy the current neighborhood into an array which * is appended at the output list */ - for (i = 0; i < itx->size; ++i) { + for (i = itx->index; i < itx->size; ++i) { PyArrayNeighborhoodIter_Reset(niterx); for (j = 0; j < PyArray_NDIM(itx->ao); ++j) { @@ -127,10 +165,11 @@ test_neighborhood_iterator(PyObject* NPY_UNUSED(self), PyObject* args) PyArrayObject *ax, *afill; PyArrayIterObject *itx; int i, typenum, mode, st; + Py_ssize_t idxstart = 0; npy_intp bounds[NPY_MAXDIMS*2]; PyArrayNeighborhoodIterObject *niterx; - if (!PyArg_ParseTuple(args, "OOOi", &x, &b, &fill, &mode)) { + if (!PyArg_ParseTuple(args, "OOOi|n", &x, &b, &fill, &mode, &idxstart)) { return NULL; } @@ -164,17 +203,20 @@ test_neighborhood_iterator(PyObject* NPY_UNUSED(self), PyObject* args) /* Compute boundaries for the neighborhood iterator */ for (i = 0; i < 2 * PyArray_NDIM(ax); ++i) { PyObject* bound; + bound = PySequence_GetItem(b, i); if (bound == NULL) { goto clean_itx; } - if (!PyInt_Check(bound)) { + /* PyLong_AsSsize checks for PyLong */ + bounds[i] = PyLong_AsSsize_t(bound); + if (error_converting(bounds[i])) { + PyErr_Clear(); PyErr_SetString(PyExc_ValueError, - "bound not long"); + "bound is invalid"); Py_DECREF(bound); goto clean_itx; } - bounds[i] = PyInt_AsLong(bound); Py_DECREF(bound); } @@ -187,12 +229,20 @@ test_neighborhood_iterator(PyObject* NPY_UNUSED(self), PyObject* args) } } + if (idxstart >= itx->size) { + PyErr_SetString(PyExc_ValueError, + "start index not compatible with x input"); + goto clean_itx; + } + niterx = (PyArrayNeighborhoodIterObject*)PyArray_NeighborhoodIterNew( (PyArrayIterObject*)itx, bounds, mode, afill); if (niterx == NULL) { goto clean_afill; } + PyArray_ITER_GOTO1D((PyArrayIterObject*)itx, idxstart); + switch (typenum) { case NPY_OBJECT: st = copy_object(itx, niterx, bounds, &out); @@ -237,7 +287,7 @@ clean_ax: static int copy_double_double(PyArrayNeighborhoodIterObject *itx, PyArrayNeighborhoodIterObject *niterx, - npy_intp *bounds, + npy_intp const *bounds, PyObject **out) { npy_intp i, j; @@ -323,17 +373,20 @@ test_neighborhood_iterator_oob(PyObject* NPY_UNUSED(self), PyObject* args) /* Compute boundaries for the neighborhood iterator */ for (i = 0; i < 2 * PyArray_NDIM(ax); ++i) { PyObject* bound; + bound = PySequence_GetItem(b1, i); if (bound == NULL) { goto clean_itx; } - if (!PyInt_Check(bound)) { + /* PyLong_AsSsize checks for PyLong */ + bounds[i] = PyLong_AsSsize_t(bound); + if (error_converting(bounds[i])) { + PyErr_Clear(); PyErr_SetString(PyExc_ValueError, - "bound not long"); + "bound is invalid"); Py_DECREF(bound); goto clean_itx; } - bounds[i] = PyInt_AsLong(bound); Py_DECREF(bound); } @@ -347,24 +400,27 @@ test_neighborhood_iterator_oob(PyObject* NPY_UNUSED(self), PyObject* args) for (i = 0; i < 2 * PyArray_NDIM(ax); ++i) { PyObject* bound; + bound = PySequence_GetItem(b2, i); if (bound == NULL) { goto clean_itx; } - if (!PyInt_Check(bound)) { + /* PyLong_AsSsize checks for PyLong */ + bounds[i] = PyLong_AsSsize_t(bound); + if (error_converting(bounds[i])) { + PyErr_Clear(); PyErr_SetString(PyExc_ValueError, - "bound not long"); + "bound is invalid"); Py_DECREF(bound); goto clean_itx; } - bounds[i] = PyInt_AsLong(bound); Py_DECREF(bound); } niterx2 = (PyArrayNeighborhoodIterObject*)PyArray_NeighborhoodIterNew( (PyArrayIterObject*)niterx1, bounds, mode2, NULL); - if (niterx1 == NULL) { + if (niterx2 == NULL) { goto clean_niterx1; } @@ -579,6 +635,124 @@ fail: return NULL; } +/* + * Helper to test fromstring of 0 terminated strings, as the C-API supports + * the -1 length identifier. + */ +static PyObject * +fromstring_null_term_c_api(PyObject *dummy, PyObject *byte_obj) +{ + char *string; + PyArray_Descr *descr; + + string = PyBytes_AsString(byte_obj); + if (string == NULL) { + return NULL; + } + descr = PyArray_DescrNewFromType(NPY_FLOAT64); + return PyArray_FromString(string, -1, descr, -1, " "); +} + + +/* + * Create a custom field dtype from an existing void one (and test some errors). + * The dtypes created by this function may be not be usable (or even crash + * while using). + */ +static PyObject * +create_custom_field_dtype(PyObject *NPY_UNUSED(mod), PyObject *args) +{ + PyArray_Descr *dtype; + PyTypeObject *scalar_type; + PyTypeObject *original_type = NULL; + int error_path; + + if (!PyArg_ParseTuple(args, "O!O!i", + &PyArrayDescr_Type, &dtype, + &PyType_Type, &scalar_type, + &error_path)) { + return NULL; + } + /* check that the result should be more or less valid */ + if (dtype->type_num != NPY_VOID || dtype->fields == NULL || + !PyDict_CheckExact(dtype->fields) || + PyTuple_Size(dtype->names) != 1 || + !PyDataType_REFCHK(dtype) || + dtype->elsize != sizeof(PyObject *)) { + PyErr_SetString(PyExc_ValueError, + "Bad dtype passed to test function, must be an object " + "containing void with a single field."); + return NULL; + } + + /* Copy and then appropriate this dtype */ + original_type = Py_TYPE(dtype); + dtype = PyArray_DescrNew(dtype); + if (dtype == NULL) { + return NULL; + } + + Py_INCREF(scalar_type); + Py_SETREF(dtype->typeobj, scalar_type); + if (error_path == 1) { + /* Test that we reject this, if fields was not already set */ + Py_SETREF(dtype->fields, NULL); + } + else if (error_path == 2) { + /* + * Test that we reject this if the type is not set to something that + * we are pretty sure can be safely replaced. + */ + Py_SET_TYPE(dtype, scalar_type); + } + else if (error_path != 0) { + PyErr_SetString(PyExc_ValueError, + "invalid error argument to test function."); + } + if (PyArray_RegisterDataType(dtype) < 0) { + /* Fix original type in the error_path == 2 case and delete it */ + Py_SET_TYPE(dtype, original_type); + Py_DECREF(dtype); + return NULL; + } + Py_INCREF(dtype); /* hold on to the original (leaks a reference) */ + return (PyObject *)dtype; +} + + +PyObject * +corrupt_or_fix_bufferinfo(PyObject *dummy, PyObject *obj) +{ + void **buffer_info_ptr; + if (PyArray_Check(obj)) { + buffer_info_ptr = &((PyArrayObject_fields *)obj)->_buffer_info; + } + else if (PyArray_IsScalar(obj, Void)) { + buffer_info_ptr = &((PyVoidScalarObject *)obj)->_buffer_info; + } + else { + PyErr_SetString(PyExc_TypeError, + "argument must be an array or void scalar"); + return NULL; + } + if (*buffer_info_ptr == NULL) { + /* set to an invalid value (as a subclass might accidentally) */ + *buffer_info_ptr = obj; + assert(((uintptr_t)obj & 7) == 0); + } + else if (*buffer_info_ptr == obj) { + /* Reset to a NULL (good value) */ + *buffer_info_ptr = NULL; + } + else { + PyErr_SetString(PyExc_TypeError, + "buffer was already exported, this test doesn't support that"); + return NULL; + } + Py_RETURN_NONE; +} + + /* check no elison for avoided increfs */ static PyObject * incref_elide(PyObject *dummy, PyObject *args) @@ -642,6 +816,43 @@ npy_updateifcopy_deprecation(PyObject* NPY_UNUSED(self), PyObject* args) Py_RETURN_NONE; } +/* used to test PyArray_As1D usage emits not implemented error */ +static PyObject* +npy_pyarrayas1d_deprecation(PyObject* NPY_UNUSED(self), PyObject* NPY_UNUSED(args)) +{ + PyObject *op = Py_BuildValue("i", 42); + PyObject *result = op; + int dim = 4; + double arg[2] = {1, 2}; + int temp = PyArray_As1D(&result, (char **)&arg, &dim, NPY_DOUBLE); + if (temp < 0) { + Py_DECREF(op); + return NULL; + } + /* op != result */ + Py_DECREF(op); + return result; +} + +/* used to test PyArray_As2D usage emits not implemented error */ +static PyObject* +npy_pyarrayas2d_deprecation(PyObject* NPY_UNUSED(self), PyObject* NPY_UNUSED(args)) +{ + PyObject *op = Py_BuildValue("i", 42); + PyObject *result = op; + int dim1 = 4; + int dim2 = 6; + double arg[2][2] = {{1, 2}, {3, 4}}; + int temp = PyArray_As2D(&result, (char ***)&arg, &dim1, &dim2, NPY_DOUBLE); + if (temp < 0) { + Py_DECREF(op); + return NULL; + } + /* op != result */ + Py_DECREF(op); + return result; +} + /* used to create array with WRITEBACKIFCOPY flag */ static PyObject* npy_create_writebackifcopy(PyObject* NPY_UNUSED(self), PyObject* args) @@ -701,30 +912,6 @@ npy_discard(PyObject* NPY_UNUSED(self), PyObject* args) Py_RETURN_NONE; } -#if !defined(NPY_PY3K) -static PyObject * -int_subclass(PyObject *dummy, PyObject *args) -{ - - PyObject *result = NULL; - PyObject *scalar_object = NULL; - - if (!PyArg_UnpackTuple(args, "test_int_subclass", 1, 1, &scalar_object)) - return NULL; - - if (PyInt_Check(scalar_object)) - result = Py_True; - else - result = Py_False; - - Py_INCREF(result); - - return result; - -} -#endif - - /* * Create python string from a FLAG and or the corresponding PyBuf flag * for the use in get_buffer_info. @@ -833,6 +1020,191 @@ get_buffer_info(PyObject *NPY_UNUSED(self), PyObject *args) #undef GET_PYBUF_FLAG +/* + * Return a new array object wrapping existing C-allocated (dummy) data. + * Such an array does not own its data (must not free it), but because it + * wraps C data, it also has no base object. Used to test arr.flags.writeable + * setting behaviour. + */ +static PyObject* +get_c_wrapping_array(PyObject* NPY_UNUSED(self), PyObject* arg) +{ + int writeable, flags; + PyArray_Descr *descr; + npy_intp zero = 0; + + writeable = PyObject_IsTrue(arg); + if (error_converting(writeable)) { + return NULL; + } + + flags = writeable ? NPY_ARRAY_WRITEABLE : 0; + /* Create an empty array (which points to a random place) */ + descr = PyArray_DescrNewFromType(NPY_INTP); + return PyArray_NewFromDescr(&PyArray_Type, descr, + 1, &zero, NULL, &zero, flags, NULL); +} + + +static PyObject * +get_all_cast_information(PyObject *NPY_UNUSED(mod), PyObject *NPY_UNUSED(args)) +{ + PyObject *result = PyList_New(0); + if (result == NULL) { + return NULL; + } + PyObject *classes = PyObject_CallMethod( + (PyObject *)&PyArrayDescr_Type, "__subclasses__", ""); + if (classes == NULL) { + return NULL; + } + Py_SETREF(classes, PySequence_Fast(classes, NULL)); + if (classes == NULL) { + goto fail; + } + + Py_ssize_t nclass = PySequence_Length(classes); + for (Py_ssize_t i = 0; i < nclass; i++) { + PyArray_DTypeMeta *from_dtype = ( + (PyArray_DTypeMeta *)PySequence_Fast_GET_ITEM(classes, i)); + if (NPY_DT_is_abstract(from_dtype)) { + /* + * TODO: In principle probably needs to recursively check this, + * also we may allow casts to abstract dtypes at some point. + */ + continue; + } + + PyObject *to_dtype, *cast_obj; + Py_ssize_t pos = 0; + + while (PyDict_Next(NPY_DT_SLOTS(from_dtype)->castingimpls, + &pos, &to_dtype, &cast_obj)) { + if (cast_obj == Py_None) { + continue; + } + PyArrayMethodObject *cast = (PyArrayMethodObject *)cast_obj; + + /* Pass some information about this cast out! */ + PyObject *cast_info = Py_BuildValue("{sOsOsisisisisisssi}", + "from", from_dtype, + "to", to_dtype, + "legacy", (cast->name != NULL && + strncmp(cast->name, "legacy_", 7) == 0), + "casting", cast->casting & ~_NPY_CAST_IS_VIEW, + "requires_pyapi", cast->flags & NPY_METH_REQUIRES_PYAPI, + "supports_unaligned", + cast->flags & NPY_METH_SUPPORTS_UNALIGNED, + "no_floatingpoint_errors", + cast->flags & NPY_METH_NO_FLOATINGPOINT_ERRORS, + "name", cast->name, + "cast_is_view", + cast->casting & _NPY_CAST_IS_VIEW); + if (cast_info == NULL) { + goto fail; + } + int res = PyList_Append(result, cast_info); + Py_DECREF(cast_info); + if (res < 0) { + goto fail; + } + } + } + Py_DECREF(classes); + return result; + + fail: + Py_XDECREF(classes); + Py_XDECREF(result); + return NULL; +} + + +/* + * Helper to test the identity cache, takes a list of values and adds + * all to the cache except the last key/value pair. The last value is + * ignored, instead the last key is looked up. + * None is returned, if the key is not found. + * If `replace` is True, duplicate entries are ignored when adding to the + * hashtable. + */ +static PyObject * +identityhash_tester(PyObject *NPY_UNUSED(mod), + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) +{ + NPY_PREPARE_ARGPARSER; + + int key_len; + int replace; + PyObject *replace_obj = Py_False; + PyObject *sequence; + PyObject *result = NULL; + + if (npy_parse_arguments("identityhash_tester", args, len_args, kwnames, + "key_len", &PyArray_PythonPyIntFromInt, &key_len, + "sequence", NULL, &sequence, + "|replace", NULL, &replace_obj, + NULL, NULL, NULL) < 0) { + return NULL; + } + replace = PyObject_IsTrue(replace_obj); + if (error_converting(replace)) { + return NULL; + } + + if (key_len < 1 || key_len >= NPY_MAXARGS) { + PyErr_SetString(PyExc_ValueError, "must have 1 to max-args keys."); + return NULL; + } + PyArrayIdentityHash *tb = PyArrayIdentityHash_New(key_len); + if (tb == NULL) { + return NULL; + } + + /* Replace the sequence with a guaranteed fast-sequence */ + sequence = PySequence_Fast(sequence, "converting sequence."); + if (sequence == NULL) { + goto finish; + } + + Py_ssize_t length = PySequence_Fast_GET_SIZE(sequence); + for (Py_ssize_t i = 0; i < length; i++) { + PyObject *key_val = PySequence_Fast_GET_ITEM(sequence, i); + if (!PyTuple_CheckExact(key_val) || PyTuple_GET_SIZE(key_val) != 2) { + PyErr_SetString(PyExc_TypeError, "bad key-value pair."); + goto finish; + } + PyObject *key = PyTuple_GET_ITEM(key_val, 0); + PyObject *value = PyTuple_GET_ITEM(key_val, 1); + if (!PyTuple_CheckExact(key) || PyTuple_GET_SIZE(key) != key_len) { + PyErr_SetString(PyExc_TypeError, "bad key tuple."); + goto finish; + } + + PyObject *keys[NPY_MAXARGS]; + for (int j = 0; j < key_len; j++) { + keys[j] = PyTuple_GET_ITEM(key, j); + } + if (i != length - 1) { + if (PyArrayIdentityHash_SetItem(tb, keys, value, replace) < 0) { + goto finish; + } + } + else { + result = PyArrayIdentityHash_GetItem(tb, keys); + if (result == NULL) { + result = Py_None; + } + Py_INCREF(result); + } + } + + finish: + Py_DECREF(sequence); + PyArrayIdentityHash_Dealloc(tb); + return result; +} + /* * Test C-api level item getting. @@ -891,6 +1263,7 @@ test_as_c_array(PyObject *NPY_UNUSED(self), PyObject *args) num_dims = PyArray_NDIM(array_obj); descr = PyArray_DESCR(array_obj); + Py_INCREF(descr); /* PyArray_AsCArray steals a reference to this */ switch (num_dims) { case 1: @@ -933,6 +1306,7 @@ test_as_c_array(PyObject *NPY_UNUSED(self), PyObject *args) PyArray_Free((PyObject *) array_obj, (void *) array3); break; default: + Py_DECREF(descr); PyErr_SetString(PyExc_ValueError, "array.ndim not in [1, 3]"); return NULL; } @@ -1041,76 +1415,6 @@ test_nditer_too_large(PyObject *NPY_UNUSED(self), PyObject *args) { return NULL; } -static PyObject * -test_nditer_writeback(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kwds) -{ - /* like npyiter_init */ - PyObject *op_in = NULL, *op_dtypes_in = NULL, *value = NULL; - PyArrayObject * opview; - int iop, nop = 0; - PyArrayObject *op[NPY_MAXARGS]; - npy_uint32 flags = 0; - NPY_ORDER order = NPY_KEEPORDER; - NPY_CASTING casting = NPY_EQUIV_CASTING; - npy_uint32 op_flags[NPY_MAXARGS]; - PyArray_Descr *op_request_dtypes[NPY_MAXARGS]; - int retval; - unsigned char do_close; - int buffersize = 0; - NpyIter *iter = NULL; - static char *kwlist[] = {"value", "do_close", "input", "op_dtypes", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "ObO|O:test_nditer_writeback", kwlist, - &value, - &do_close, - &op_in, - &op_dtypes_in)) { - return NULL; - } - /* op and op_flags */ - if (! PyArray_Check(op_in)) { - return NULL; - } - nop = 1; - op[0] = (PyArrayObject*)op_in; - op_flags[0] = NPY_ITER_READWRITE|NPY_ITER_UPDATEIFCOPY; - - /* Set the dtypes */ - for (iop=0; iop<nop; iop++) { - PyObject *dtype = PySequence_GetItem(op_dtypes_in, iop); - PyArray_DescrConverter2(dtype, &op_request_dtypes[iop]); - } - - iter = NpyIter_AdvancedNew(nop, op, flags, order, casting, op_flags, - op_request_dtypes, - -1, NULL, NULL, - buffersize); - if (iter == NULL) { - goto fail; - } - - opview = NpyIter_GetIterView(iter, 0); - retval = PyArray_FillWithScalar(opview, value); - Py_DECREF(opview); - if (retval < 0) { - NpyIter_Deallocate(iter); - return NULL; - } - if (do_close != 0) { - NpyIter_Close(iter); - } - NpyIter_Deallocate(iter); - Py_RETURN_NONE; - -fail: - for (iop = 0; iop < nop; ++iop) { - Py_XDECREF(op[iop]); - Py_XDECREF(op_request_dtypes[iop]); - } - return NULL; -} - static PyObject * array_solve_diophantine(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kwds) { @@ -1152,11 +1456,11 @@ array_solve_diophantine(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject } for (j = 0; j < nterms; ++j) { - terms[j].a = (npy_int64)PyInt_AsSsize_t(PyTuple_GET_ITEM(A, j)); + terms[j].a = (npy_int64)PyLong_AsSsize_t(PyTuple_GET_ITEM(A, j)); if (error_converting(terms[j].a)) { goto fail; } - terms[j].ub = (npy_int64)PyInt_AsSsize_t(PyTuple_GET_ITEM(U, j)); + terms[j].ub = (npy_int64)PyLong_AsSsize_t(PyTuple_GET_ITEM(U, j)); if (error_converting(terms[j].ub)) { goto fail; } @@ -1183,11 +1487,7 @@ array_solve_diophantine(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject for (j = 0; j < nterms; ++j) { PyObject *obj; -#if defined(NPY_PY3K) obj = PyLong_FromSsize_t(x[j]); -#else - obj = PyInt_FromSsize_t(x[j]); -#endif if (obj == NULL) { goto fail; } @@ -1296,7 +1596,9 @@ pylong_from_int128(npy_extint128_t value) } Py_DECREF(val); + Py_DECREF(val_64); val = tmp; + val_64 = NULL; tmp = PyLong_FromUnsignedLongLong(value.lo); if (tmp == NULL) { @@ -1711,6 +2013,42 @@ extint_ceildiv_128_64(PyObject *NPY_UNUSED(self), PyObject *args) { return pylong_from_int128(c); } +struct TestStruct1 { + npy_uint8 a; + npy_complex64 b; +}; + +struct TestStruct2 { + npy_uint32 a; + npy_complex64 b; +}; + +struct TestStruct3 { + npy_uint8 a; + struct TestStruct1 b; +}; + +static PyObject * +get_struct_alignments(PyObject *NPY_UNUSED(self), PyObject *args) { + PyObject *ret = PyTuple_New(3); + PyObject *alignment, *size, *val; + +/**begin repeat + * #N = 1,2,3# + */ + alignment = PyLong_FromLong(_ALIGN(struct TestStruct@N@)); + size = PyLong_FromLong(sizeof(struct TestStruct@N@)); + val = PyTuple_Pack(2, alignment, size); + Py_DECREF(alignment); + Py_DECREF(size); + if (val == NULL) { + return NULL; + } + PyTuple_SET_ITEM(ret, @N@-1, val); +/**end repeat**/ + return ret; +} + static char get_fpu_mode_doc[] = ( "get_fpu_mode()\n" @@ -1840,22 +2178,22 @@ PrintFloat_Printf_g(PyObject *obj, int precision) char str[1024]; if (PyArray_IsScalar(obj, Half)) { - npy_half x = ((PyHalfScalarObject *)obj)->obval; + npy_half x = PyArrayScalar_VAL(obj, Half); PyOS_snprintf(str, sizeof(str), "%.*g", precision, npy_half_to_double(x)); } else if (PyArray_IsScalar(obj, Float)) { - npy_float x = ((PyFloatScalarObject *)obj)->obval; + npy_float x = PyArrayScalar_VAL(obj, Float); PyOS_snprintf(str, sizeof(str), "%.*g", precision, x); } else if (PyArray_IsScalar(obj, Double)) { - npy_double x = ((PyDoubleScalarObject *)obj)->obval; + npy_double x = PyArrayScalar_VAL(obj, Double); PyOS_snprintf(str, sizeof(str), "%.*g", precision, x); /* would be better to use lg, but not available in C90 */ } else if (PyArray_IsScalar(obj, LongDouble)) { - npy_longdouble x = ((PyLongDoubleScalarObject *)obj)->obval; - PyOS_snprintf(str, sizeof(str), "%.*Lg", precision, x); + npy_longdouble x = PyArrayScalar_VAL(obj, LongDouble); + PyOS_snprintf(str, sizeof(str), "%.*" NPY_LONGDOUBLE_FMT, precision, x); } else{ double val = PyFloat_AsDouble(obj); @@ -1865,7 +2203,7 @@ PrintFloat_Printf_g(PyObject *obj, int precision) PyOS_snprintf(str, sizeof(str), "%.*g", precision, val); } - return PyUString_FromString(str); + return PyUnicode_FromString(str); } @@ -1888,7 +2226,158 @@ printf_float_g(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds) return PrintFloat_Printf_g(obj, precision); } +static PyObject * +getset_numericops(PyObject* NPY_UNUSED(self), PyObject* NPY_UNUSED(args)) +{ + PyObject *ret; + PyObject *ops = PyArray_GetNumericOps(); + if (ops == NULL) { + return NULL; + } + ret = PyLong_FromLong(PyArray_SetNumericOps(ops)); + Py_DECREF(ops); + return ret; +} + + +static PyObject * +run_byteorder_converter(PyObject* NPY_UNUSED(self), PyObject *args) +{ + char byteorder; + if (!PyArg_ParseTuple(args, "O&", PyArray_ByteorderConverter, &byteorder)) { + return NULL; + } + switch (byteorder) { + case NPY_BIG: return PyUnicode_FromString("NPY_BIG"); + case NPY_LITTLE: return PyUnicode_FromString("NPY_LITTLE"); + case NPY_NATIVE: return PyUnicode_FromString("NPY_NATIVE"); + case NPY_SWAP: return PyUnicode_FromString("NPY_SWAP"); + case NPY_IGNORE: return PyUnicode_FromString("NPY_IGNORE"); + } + return PyLong_FromLong(byteorder); +} + +static PyObject * +run_sortkind_converter(PyObject* NPY_UNUSED(self), PyObject *args) +{ + NPY_SORTKIND kind; + if (!PyArg_ParseTuple(args, "O&", PyArray_SortkindConverter, &kind)) { + return NULL; + } + switch (kind) { + case NPY_QUICKSORT: return PyUnicode_FromString("NPY_QUICKSORT"); + case NPY_HEAPSORT: return PyUnicode_FromString("NPY_HEAPSORT"); + case NPY_STABLESORT: return PyUnicode_FromString("NPY_STABLESORT"); + } + return PyLong_FromLong(kind); +} + +static PyObject * +run_selectkind_converter(PyObject* NPY_UNUSED(self), PyObject *args) +{ + NPY_SELECTKIND kind; + if (!PyArg_ParseTuple(args, "O&", PyArray_SelectkindConverter, &kind)) { + return NULL; + } + switch (kind) { + case NPY_INTROSELECT: return PyUnicode_FromString("NPY_INTROSELECT"); + } + return PyLong_FromLong(kind); +} + +static PyObject * +run_searchside_converter(PyObject* NPY_UNUSED(self), PyObject *args) +{ + NPY_SEARCHSIDE side; + if (!PyArg_ParseTuple(args, "O&", PyArray_SearchsideConverter, &side)) { + return NULL; + } + switch (side) { + case NPY_SEARCHLEFT: return PyUnicode_FromString("NPY_SEARCHLEFT"); + case NPY_SEARCHRIGHT: return PyUnicode_FromString("NPY_SEARCHRIGHT"); + } + return PyLong_FromLong(side); +} + +static PyObject * +run_order_converter(PyObject* NPY_UNUSED(self), PyObject *args) +{ + NPY_ORDER order; + if (!PyArg_ParseTuple(args, "O&", PyArray_OrderConverter, &order)) { + return NULL; + } + switch (order) { + case NPY_ANYORDER: return PyUnicode_FromString("NPY_ANYORDER"); + case NPY_CORDER: return PyUnicode_FromString("NPY_CORDER"); + case NPY_FORTRANORDER: return PyUnicode_FromString("NPY_FORTRANORDER"); + case NPY_KEEPORDER: return PyUnicode_FromString("NPY_KEEPORDER"); + } + return PyLong_FromLong(order); +} + +static PyObject * +run_clipmode_converter(PyObject* NPY_UNUSED(self), PyObject *args) +{ + NPY_CLIPMODE mode; + if (!PyArg_ParseTuple(args, "O&", PyArray_ClipmodeConverter, &mode)) { + return NULL; + } + switch (mode) { + case NPY_CLIP: return PyUnicode_FromString("NPY_CLIP"); + case NPY_WRAP: return PyUnicode_FromString("NPY_WRAP"); + case NPY_RAISE: return PyUnicode_FromString("NPY_RAISE"); + } + return PyLong_FromLong(mode); +} + +static PyObject * +run_casting_converter(PyObject* NPY_UNUSED(self), PyObject *args) +{ + NPY_CASTING casting; + if (!PyArg_ParseTuple(args, "O&", PyArray_CastingConverter, &casting)) { + return NULL; + } + switch (casting) { + case NPY_NO_CASTING: return PyUnicode_FromString("NPY_NO_CASTING"); + case NPY_EQUIV_CASTING: return PyUnicode_FromString("NPY_EQUIV_CASTING"); + case NPY_SAFE_CASTING: return PyUnicode_FromString("NPY_SAFE_CASTING"); + case NPY_SAME_KIND_CASTING: return PyUnicode_FromString("NPY_SAME_KIND_CASTING"); + case NPY_UNSAFE_CASTING: return PyUnicode_FromString("NPY_UNSAFE_CASTING"); + default: return PyLong_FromLong(casting); + } +} + +static PyObject * +run_intp_converter(PyObject* NPY_UNUSED(self), PyObject *args) +{ + PyArray_Dims dims = {NULL, -1}; + if (!PyArg_ParseTuple(args, "O&", PyArray_IntpConverter, &dims)) { + return NULL; + } + if (dims.len == -1) { + Py_RETURN_NONE; + } + + PyObject *tup = PyArray_IntTupleFromIntp(dims.len, dims.ptr); + PyDimMem_FREE(dims.ptr); + return tup; +} + +/* used to test NPY_ARRAY_ENSURENOCOPY raises ValueError */ +static PyObject* +npy_ensurenocopy(PyObject* NPY_UNUSED(self), PyObject* args) +{ + int flags = NPY_ARRAY_ENSURENOCOPY; + if (!PyArray_CheckFromAny(args, NULL, 0, 0, flags, NULL)) { + return NULL; + } + Py_RETURN_NONE; +} + static PyMethodDef Multiarray_TestsMethods[] = { + {"argparse_example_function", + (PyCFunction)argparse_example_function, + METH_KEYWORDS | METH_FASTCALL, NULL}, {"IsPythonScalar", IsPythonScalar, METH_VARARGS, NULL}, @@ -1907,6 +2396,15 @@ static PyMethodDef Multiarray_TestsMethods[] = { {"test_inplace_increment", inplace_increment, METH_VARARGS, NULL}, + {"fromstring_null_term_c_api", + fromstring_null_term_c_api, + METH_O, NULL}, + {"create_custom_field_dtype", + create_custom_field_dtype, + METH_VARARGS, NULL}, + {"corrupt_or_fix_bufferinfo", + corrupt_or_fix_bufferinfo, + METH_O, NULL}, {"incref_elide", incref_elide, METH_VARARGS, NULL}, @@ -1919,6 +2417,12 @@ static PyMethodDef Multiarray_TestsMethods[] = { {"npy_updateifcopy_deprecation", npy_updateifcopy_deprecation, METH_O, NULL}, + {"npy_pyarrayas1d_deprecation", + npy_pyarrayas1d_deprecation, + METH_NOARGS, NULL}, + {"npy_pyarrayas2d_deprecation", + npy_pyarrayas2d_deprecation, + METH_NOARGS, NULL}, {"npy_create_writebackifcopy", npy_create_writebackifcopy, METH_O, NULL}, @@ -1931,14 +2435,24 @@ static PyMethodDef Multiarray_TestsMethods[] = { {"npy_discard", npy_discard, METH_O, NULL}, -#if !defined(NPY_PY3K) - {"test_int_subclass", - int_subclass, - METH_VARARGS, NULL}, -#endif + {"npy_ensurenocopy", + npy_ensurenocopy, + METH_O, NULL}, {"get_buffer_info", get_buffer_info, METH_VARARGS, NULL}, + {"get_c_wrapping_array", + get_c_wrapping_array, + METH_O, NULL}, + {"get_all_cast_information", + get_all_cast_information, + METH_NOARGS, + "Return a list with info on all available casts. Some of the info" + "may differ for an actual cast if it uses value-based casting " + "(flexible types)."}, + {"identityhash_tester", + (PyCFunction)identityhash_tester, + METH_KEYWORDS | METH_FASTCALL, NULL}, {"array_indexing", array_indexing, METH_VARARGS, NULL}, @@ -1948,9 +2462,6 @@ static PyMethodDef Multiarray_TestsMethods[] = { {"test_nditer_too_large", test_nditer_too_large, METH_VARARGS, NULL}, - {"test_nditer_writeback", - (PyCFunction)test_nditer_writeback, - METH_VARARGS | METH_KEYWORDS, NULL}, {"solve_diophantine", (PyCFunction)array_solve_diophantine, METH_VARARGS | METH_KEYWORDS, NULL}, @@ -1999,6 +2510,9 @@ static PyMethodDef Multiarray_TestsMethods[] = { {"get_fpu_mode", get_fpu_mode, METH_VARARGS, get_fpu_mode_doc}, + {"getset_numericops", + getset_numericops, + METH_NOARGS, NULL}, /**begin repeat * #name = cabs, carg# */ @@ -2029,11 +2543,37 @@ static PyMethodDef Multiarray_TestsMethods[] = { {"format_float_OSprintf_g", (PyCFunction)printf_float_g, METH_VARARGS , NULL}, + {"get_struct_alignments", + get_struct_alignments, + METH_VARARGS, NULL}, + {"run_byteorder_converter", + run_byteorder_converter, + METH_VARARGS, NULL}, + {"run_sortkind_converter", + run_sortkind_converter, + METH_VARARGS, NULL}, + {"run_selectkind_converter", + run_selectkind_converter, + METH_VARARGS, NULL}, + {"run_searchside_converter", + run_searchside_converter, + METH_VARARGS, NULL}, + {"run_order_converter", + run_order_converter, + METH_VARARGS, NULL}, + {"run_clipmode_converter", + run_clipmode_converter, + METH_VARARGS, NULL}, + {"run_casting_converter", + run_casting_converter, + METH_VARARGS, NULL}, + {"run_intp_converter", + run_intp_converter, + METH_VARARGS, NULL}, {NULL, NULL, 0, NULL} /* Sentinel */ }; -#if defined(NPY_PY3K) static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "_multiarray_tests", @@ -2045,31 +2585,25 @@ static struct PyModuleDef moduledef = { NULL, NULL }; -#endif -#if defined(NPY_PY3K) -#define RETVAL m PyMODINIT_FUNC PyInit__multiarray_tests(void) -#else -#define RETVAL -PyMODINIT_FUNC -init_multiarray_tests(void) -#endif { PyObject *m; -#if defined(NPY_PY3K) m = PyModule_Create(&moduledef); -#else - m = Py_InitModule("_multiarray_tests", Multiarray_TestsMethods); -#endif if (m == NULL) { - return RETVAL; + return m; } import_array(); if (PyErr_Occurred()) { PyErr_SetString(PyExc_RuntimeError, "cannot load _multiarray_tests module."); } - return RETVAL; + return m; +} + +NPY_NO_EXPORT int +test_not_exported(void) +{ + return 1; } diff --git a/numpy/core/src/multiarray/abstractdtypes.c b/numpy/core/src/multiarray/abstractdtypes.c new file mode 100644 index 000000000000..cc1d7fad8233 --- /dev/null +++ b/numpy/core/src/multiarray/abstractdtypes.c @@ -0,0 +1,297 @@ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE + +#define PY_SSIZE_T_CLEAN +#include <Python.h> +#include <structmember.h> + +#include "numpy/ndarraytypes.h" +#include "numpy/arrayobject.h" + +#include "abstractdtypes.h" +#include "array_coercion.h" +#include "common.h" + + +static NPY_INLINE PyArray_Descr * +int_default_descriptor(PyArray_DTypeMeta* NPY_UNUSED(cls)) +{ + return PyArray_DescrFromType(NPY_LONG); +} + +static PyArray_Descr * +discover_descriptor_from_pyint( + PyArray_DTypeMeta *NPY_UNUSED(cls), PyObject *obj) +{ + assert(PyLong_Check(obj)); + /* + * We check whether long is good enough. If not, check longlong and + * unsigned long before falling back to `object`. + */ + long long value = PyLong_AsLongLong(obj); + if (error_converting(value)) { + PyErr_Clear(); + } + else { + if (NPY_MIN_LONG <= value && value <= NPY_MAX_LONG) { + return PyArray_DescrFromType(NPY_LONG); + } + return PyArray_DescrFromType(NPY_LONGLONG); + } + + unsigned long long uvalue = PyLong_AsUnsignedLongLong(obj); + if (uvalue == (unsigned long long)-1 && PyErr_Occurred()){ + PyErr_Clear(); + } + else { + return PyArray_DescrFromType(NPY_ULONGLONG); + } + + return PyArray_DescrFromType(NPY_OBJECT); +} + + +static NPY_INLINE PyArray_Descr * +float_default_descriptor(PyArray_DTypeMeta* NPY_UNUSED(cls)) +{ + return PyArray_DescrFromType(NPY_DOUBLE); +} + + +static PyArray_Descr* +discover_descriptor_from_pyfloat( + PyArray_DTypeMeta* NPY_UNUSED(cls), PyObject *obj) +{ + assert(PyFloat_CheckExact(obj)); + return PyArray_DescrFromType(NPY_DOUBLE); +} + +static NPY_INLINE PyArray_Descr * +complex_default_descriptor(PyArray_DTypeMeta* NPY_UNUSED(cls)) +{ + return PyArray_DescrFromType(NPY_CDOUBLE); +} + +static PyArray_Descr* +discover_descriptor_from_pycomplex( + PyArray_DTypeMeta* NPY_UNUSED(cls), PyObject *obj) +{ + assert(PyComplex_CheckExact(obj)); + return PyArray_DescrFromType(NPY_COMPLEX128); +} + + +NPY_NO_EXPORT int +initialize_and_map_pytypes_to_dtypes() +{ + ((PyTypeObject *)&PyArray_PyIntAbstractDType)->tp_base = &PyArrayDescr_Type; + PyArray_PyIntAbstractDType.scalar_type = &PyLong_Type; + if (PyType_Ready((PyTypeObject *)&PyArray_PyIntAbstractDType) < 0) { + return -1; + } + ((PyTypeObject *)&PyArray_PyFloatAbstractDType)->tp_base = &PyArrayDescr_Type; + PyArray_PyFloatAbstractDType.scalar_type = &PyFloat_Type; + if (PyType_Ready((PyTypeObject *)&PyArray_PyFloatAbstractDType) < 0) { + return -1; + } + ((PyTypeObject *)&PyArray_PyComplexAbstractDType)->tp_base = &PyArrayDescr_Type; + PyArray_PyComplexAbstractDType.scalar_type = &PyComplex_Type; + if (PyType_Ready((PyTypeObject *)&PyArray_PyComplexAbstractDType) < 0) { + return -1; + } + + /* Register the new DTypes for discovery */ + if (_PyArray_MapPyTypeToDType( + &PyArray_PyIntAbstractDType, &PyLong_Type, NPY_FALSE) < 0) { + return -1; + } + if (_PyArray_MapPyTypeToDType( + &PyArray_PyFloatAbstractDType, &PyFloat_Type, NPY_FALSE) < 0) { + return -1; + } + if (_PyArray_MapPyTypeToDType( + &PyArray_PyComplexAbstractDType, &PyComplex_Type, NPY_FALSE) < 0) { + return -1; + } + + /* + * Map str, bytes, and bool, for which we do not need abstract versions + * to the NumPy DTypes. This is done here using the `is_known_scalar_type` + * function. + * TODO: The `is_known_scalar_type` function is considered preliminary, + * the same could be achieved e.g. with additional abstract DTypes. + */ + PyArray_DTypeMeta *dtype; + dtype = NPY_DTYPE(PyArray_DescrFromType(NPY_UNICODE)); + if (_PyArray_MapPyTypeToDType(dtype, &PyUnicode_Type, NPY_FALSE) < 0) { + return -1; + } + + dtype = NPY_DTYPE(PyArray_DescrFromType(NPY_STRING)); + if (_PyArray_MapPyTypeToDType(dtype, &PyBytes_Type, NPY_FALSE) < 0) { + return -1; + } + dtype = NPY_DTYPE(PyArray_DescrFromType(NPY_BOOL)); + if (_PyArray_MapPyTypeToDType(dtype, &PyBool_Type, NPY_FALSE) < 0) { + return -1; + } + + return 0; +} + + +/* + * The following functions define the "common DType" for the abstract dtypes. + * + * Note that the logic with respect to the "higher" dtypes such as floats + * could likely be more logically defined for them, but since NumPy dtypes + * largely "know" each other, that is not necessary. + */ +static PyArray_DTypeMeta * +int_common_dtype(PyArray_DTypeMeta *NPY_UNUSED(cls), PyArray_DTypeMeta *other) +{ + if (NPY_DT_is_legacy(other) && other->type_num < NPY_NTYPES) { + if (other->type_num == NPY_BOOL) { + /* Use the default integer for bools: */ + return PyArray_DTypeFromTypeNum(NPY_LONG); + } + else if (PyTypeNum_ISNUMBER(other->type_num) || + other->type_num == NPY_TIMEDELTA) { + /* All other numeric types (ant timedelta) are preserved: */ + Py_INCREF(other); + return other; + } + } + else if (NPY_DT_is_legacy(other)) { + /* This is a back-compat fallback to usually do the right thing... */ + return PyArray_DTypeFromTypeNum(NPY_UINT8); + } + Py_INCREF(Py_NotImplemented); + return (PyArray_DTypeMeta *)Py_NotImplemented; +} + + +static PyArray_DTypeMeta * +float_common_dtype(PyArray_DTypeMeta *cls, PyArray_DTypeMeta *other) +{ + if (NPY_DT_is_legacy(other) && other->type_num < NPY_NTYPES) { + if (other->type_num == NPY_BOOL || PyTypeNum_ISINTEGER(other->type_num)) { + /* Use the default integer for bools and ints: */ + return PyArray_DTypeFromTypeNum(NPY_DOUBLE); + } + else if (PyTypeNum_ISNUMBER(other->type_num)) { + /* All other numeric types (float+complex) are preserved: */ + Py_INCREF(other); + return other; + } + } + else if (other == &PyArray_PyIntAbstractDType) { + Py_INCREF(cls); + return cls; + } + else if (NPY_DT_is_legacy(other)) { + /* This is a back-compat fallback to usually do the right thing... */ + return PyArray_DTypeFromTypeNum(NPY_HALF); + } + Py_INCREF(Py_NotImplemented); + return (PyArray_DTypeMeta *)Py_NotImplemented; +} + + +static PyArray_DTypeMeta * +complex_common_dtype(PyArray_DTypeMeta *cls, PyArray_DTypeMeta *other) +{ + if (NPY_DT_is_legacy(other) && other->type_num < NPY_NTYPES) { + if (other->type_num == NPY_BOOL || + PyTypeNum_ISINTEGER(other->type_num)) { + /* Use the default integer for bools and ints: */ + return PyArray_DTypeFromTypeNum(NPY_CDOUBLE); + } + else if (PyTypeNum_ISFLOAT(other->type_num)) { + /* + * For floats we choose the equivalent precision complex, although + * there is no CHALF, so half also goes to CFLOAT. + */ + if (other->type_num == NPY_HALF || other->type_num == NPY_FLOAT) { + return PyArray_DTypeFromTypeNum(NPY_CFLOAT); + } + if (other->type_num == NPY_DOUBLE) { + return PyArray_DTypeFromTypeNum(NPY_CDOUBLE); + } + assert(other->type_num == NPY_LONGDOUBLE); + return PyArray_DTypeFromTypeNum(NPY_CLONGDOUBLE); + } + else if (PyTypeNum_ISCOMPLEX(other->type_num)) { + /* All other numeric types are preserved: */ + Py_INCREF(other); + return other; + } + } + else if (NPY_DT_is_legacy(other)) { + /* This is a back-compat fallback to usually do the right thing... */ + return PyArray_DTypeFromTypeNum(NPY_CFLOAT); + } + else if (other == &PyArray_PyIntAbstractDType || + other == &PyArray_PyFloatAbstractDType) { + Py_INCREF(cls); + return cls; + } + Py_INCREF(Py_NotImplemented); + return (PyArray_DTypeMeta *)Py_NotImplemented; +} + + +/* + * TODO: These abstract DTypes also carry the dual role of representing + * `Floating`, `Complex`, and `Integer` (both signed and unsigned). + * They will have to be renamed and exposed in that capacity. + */ +NPY_DType_Slots pyintabstractdtype_slots = { + .default_descr = int_default_descriptor, + .discover_descr_from_pyobject = discover_descriptor_from_pyint, + .common_dtype = int_common_dtype, +}; + +NPY_NO_EXPORT PyArray_DTypeMeta PyArray_PyIntAbstractDType = {{{ + PyVarObject_HEAD_INIT(&PyArrayDTypeMeta_Type, 0) + .tp_basicsize = sizeof(PyArray_Descr), + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_name = "numpy._IntegerAbstractDType", + },}, + .flags = NPY_DT_ABSTRACT, + .dt_slots = &pyintabstractdtype_slots, +}; + + +NPY_DType_Slots pyfloatabstractdtype_slots = { + .default_descr = float_default_descriptor, + .discover_descr_from_pyobject = discover_descriptor_from_pyfloat, + .common_dtype = float_common_dtype, +}; + +NPY_NO_EXPORT PyArray_DTypeMeta PyArray_PyFloatAbstractDType = {{{ + PyVarObject_HEAD_INIT(&PyArrayDTypeMeta_Type, 0) + .tp_basicsize = sizeof(PyArray_Descr), + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_name = "numpy._FloatAbstractDType", + },}, + .flags = NPY_DT_ABSTRACT, + .dt_slots = &pyfloatabstractdtype_slots, +}; + + +NPY_DType_Slots pycomplexabstractdtype_slots = { + .default_descr = complex_default_descriptor, + .discover_descr_from_pyobject = discover_descriptor_from_pycomplex, + .common_dtype = complex_common_dtype, +}; + +NPY_NO_EXPORT PyArray_DTypeMeta PyArray_PyComplexAbstractDType = {{{ + PyVarObject_HEAD_INIT(&PyArrayDTypeMeta_Type, 0) + .tp_basicsize = sizeof(PyArray_Descr), + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_name = "numpy._ComplexAbstractDType", + },}, + .flags = NPY_DT_ABSTRACT, + .dt_slots = &pycomplexabstractdtype_slots, +}; diff --git a/numpy/core/src/multiarray/abstractdtypes.h b/numpy/core/src/multiarray/abstractdtypes.h new file mode 100644 index 000000000000..42c192cac7b8 --- /dev/null +++ b/numpy/core/src/multiarray/abstractdtypes.h @@ -0,0 +1,19 @@ +#ifndef NUMPY_CORE_SRC_MULTIARRAY_ABSTRACTDTYPES_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_ABSTRACTDTYPES_H_ + +#include "dtypemeta.h" + + +/* + * These are mainly needed for value based promotion in ufuncs. It + * may be necessary to make them (partially) public, to allow user-defined + * dtypes to perform value based casting. + */ +NPY_NO_EXPORT extern PyArray_DTypeMeta PyArray_PyIntAbstractDType; +NPY_NO_EXPORT extern PyArray_DTypeMeta PyArray_PyFloatAbstractDType; +NPY_NO_EXPORT extern PyArray_DTypeMeta PyArray_PyComplexAbstractDType; + +NPY_NO_EXPORT int +initialize_and_map_pytypes_to_dtypes(void); + +#endif /* NUMPY_CORE_SRC_MULTIARRAY_ABSTRACTDTYPES_H_ */ diff --git a/numpy/core/src/multiarray/alloc.c b/numpy/core/src/multiarray/alloc.c index fe957bf168d5..0a694cf62662 100644 --- a/numpy/core/src/multiarray/alloc.c +++ b/numpy/core/src/multiarray/alloc.c @@ -1,28 +1,28 @@ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE + #define PY_SSIZE_T_CLEAN #include <Python.h> -#include "structmember.h" - -#if PY_VERSION_HEX >= 0x03060000 +#include <structmember.h> #include <pymem.h> -/* public api in 3.7 */ -#if PY_VERSION_HEX < 0x03070000 -#define PyTraceMalloc_Track _PyTraceMalloc_Track -#define PyTraceMalloc_Untrack _PyTraceMalloc_Untrack -#endif -#else -#define PyTraceMalloc_Track(...) -#define PyTraceMalloc_Untrack(...) -#endif -#define NPY_NO_DEPRECATED_API NPY_API_VERSION -#define _MULTIARRAYMODULE -#include <numpy/ndarraytypes.h> +#include "numpy/ndarraytypes.h" #include "numpy/arrayobject.h" -#include <numpy/npy_common.h> +#include "numpy/npy_common.h" #include "npy_config.h" #include "alloc.h" #include <assert.h> +#ifdef NPY_OS_LINUX +#include <sys/mman.h> +#ifndef MADV_HUGEPAGE +/* + * Use code 14 (MADV_HUGEPAGE) if it isn't defined. This gives a chance of + * enabling huge pages even if built with linux kernel < 2.6.38 + */ +#define MADV_HUGEPAGE 14 +#endif +#endif #define NBUCKETS 1024 /* number of buckets for data*/ #define NBUCKETS_DIM 16 /* number of buckets for dimensions/strides */ @@ -35,12 +35,33 @@ typedef struct { static cache_bucket datacache[NBUCKETS]; static cache_bucket dimcache[NBUCKETS_DIM]; +static int _madvise_hugepage = 1; + + +/* + * This function enables or disables the use of `MADV_HUGEPAGE` on Linux + * by modifying the global static `_madvise_hugepage`. + * It returns the previous value of `_madvise_hugepage`. + * + * It is exposed to Python as `np.core.multiarray._set_madvise_hugepage`. + */ +NPY_NO_EXPORT PyObject * +_set_madvise_hugepage(PyObject *NPY_UNUSED(self), PyObject *enabled_obj) +{ + int was_enabled = _madvise_hugepage; + int enabled = PyObject_IsTrue(enabled_obj); + if (enabled < 0) { + return NULL; + } + _madvise_hugepage = enabled; + if (was_enabled) { + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; +} + + /* as the cache is managed in global variables verify the GIL is held */ -#if defined(NPY_PY3K) -#define NPY_CHECK_GIL_HELD() PyGILState_Check() -#else -#define NPY_CHECK_GIL_HELD() 1 -#endif /* * very simplistic small memory block cache to avoid more expensive libc @@ -52,27 +73,34 @@ static NPY_INLINE void * _npy_alloc_cache(npy_uintp nelem, npy_uintp esz, npy_uint msz, cache_bucket * cache, void * (*alloc)(size_t)) { + void * p; assert((esz == 1 && cache == datacache) || (esz == sizeof(npy_intp) && cache == dimcache)); - assert(NPY_CHECK_GIL_HELD()); + assert(PyGILState_Check()); if (nelem < msz) { if (cache[nelem].available > 0) { return cache[nelem].ptrs[--(cache[nelem].available)]; } } + p = alloc(nelem * esz); + if (p) { #ifdef _PyPyGC_AddMemoryPressure - { - size_t size = nelem * esz; - void * ret = alloc(size); - if (ret != NULL) - { - _PyPyPyGC_AddMemoryPressure(size); + _PyPyPyGC_AddMemoryPressure(nelem * esz); +#endif +#ifdef NPY_OS_LINUX + /* allow kernel allocating huge pages for large arrays */ + if (NPY_UNLIKELY(nelem * esz >= ((1u<<22u))) && _madvise_hugepage) { + npy_uintp offset = 4096u - (npy_uintp)p % (4096u); + npy_uintp length = nelem * esz - offset; + /** + * Intentionally not checking for errors that may be returned by + * older kernel versions; optimistically tries enabling huge pages. + */ + madvise((void*)((npy_uintp)p + offset), length, MADV_HUGEPAGE); } - return ret; - } -#else - return alloc(nelem * esz); #endif + } + return p; } /* @@ -83,7 +111,7 @@ static NPY_INLINE void _npy_free_cache(void * p, npy_uintp nelem, npy_uint msz, cache_bucket * cache, void (*dealloc)(void *)) { - assert(NPY_CHECK_GIL_HELD()); + assert(PyGILState_Check()); if (p != NULL && nelem < msz) { if (cache[nelem].available < NCACHE) { cache[nelem].ptrs[cache[nelem].available++] = p; @@ -105,9 +133,10 @@ npy_alloc_cache(npy_uintp sz) /* zero initialized data, sz is number of bytes to allocate */ NPY_NO_EXPORT void * -npy_alloc_cache_zero(npy_uintp sz) +npy_alloc_cache_zero(size_t nmemb, size_t size) { void * p; + size_t sz = nmemb * size; NPY_BEGIN_THREADS_DEF; if (sz < NBUCKETS) { p = _npy_alloc_cache(sz, 1, NBUCKETS, datacache, &PyDataMem_NEW); @@ -117,7 +146,7 @@ npy_alloc_cache_zero(npy_uintp sz) return p; } NPY_BEGIN_THREADS; - p = PyDataMem_NEW_ZEROED(sz, 1); + p = PyDataMem_NEW_ZEROED(nmemb, size); NPY_END_THREADS; return p; } @@ -159,8 +188,8 @@ npy_free_cache_dim(void * p, npy_uintp sz) /* malloc/free/realloc hook */ -NPY_NO_EXPORT PyDataMem_EventHookFunc *_PyDataMem_eventhook; -NPY_NO_EXPORT void *_PyDataMem_eventhook_user_data; +NPY_NO_EXPORT PyDataMem_EventHookFunc *_PyDataMem_eventhook = NULL; +NPY_NO_EXPORT void *_PyDataMem_eventhook_user_data = NULL; /*NUMPY_API * Sets the allocation event hook for numpy array data. @@ -207,6 +236,7 @@ PyDataMem_NEW(size_t size) { void *result; + assert(size != 0); result = malloc(size); if (_PyDataMem_eventhook != NULL) { NPY_ALLOW_C_API_DEF @@ -225,21 +255,21 @@ PyDataMem_NEW(size_t size) * Allocates zeroed memory for array data. */ NPY_NO_EXPORT void * -PyDataMem_NEW_ZEROED(size_t size, size_t elsize) +PyDataMem_NEW_ZEROED(size_t nmemb, size_t size) { void *result; - result = calloc(size, elsize); + result = calloc(nmemb, size); if (_PyDataMem_eventhook != NULL) { NPY_ALLOW_C_API_DEF NPY_ALLOW_C_API if (_PyDataMem_eventhook != NULL) { - (*_PyDataMem_eventhook)(NULL, result, size * elsize, + (*_PyDataMem_eventhook)(NULL, result, nmemb * size, _PyDataMem_eventhook_user_data); } NPY_DISABLE_C_API } - PyTraceMalloc_Track(NPY_TRACE_DOMAIN, (npy_uintp)result, size); + PyTraceMalloc_Track(NPY_TRACE_DOMAIN, (npy_uintp)result, nmemb * size); return result; } @@ -270,6 +300,7 @@ PyDataMem_RENEW(void *ptr, size_t size) { void *result; + assert(size != 0); result = realloc(ptr, size); if (result != ptr) { PyTraceMalloc_Untrack(NPY_TRACE_DOMAIN, (npy_uintp)ptr); @@ -286,3 +317,344 @@ PyDataMem_RENEW(void *ptr, size_t size) } return result; } + +// The default data mem allocator malloc routine does not make use of a ctx. +// It should be called only through PyDataMem_UserNEW +// since itself does not handle eventhook and tracemalloc logic. +static NPY_INLINE void * +default_malloc(void *NPY_UNUSED(ctx), size_t size) +{ + return _npy_alloc_cache(size, 1, NBUCKETS, datacache, &malloc); +} + +// The default data mem allocator calloc routine does not make use of a ctx. +// It should be called only through PyDataMem_UserNEW_ZEROED +// since itself does not handle eventhook and tracemalloc logic. +static NPY_INLINE void * +default_calloc(void *NPY_UNUSED(ctx), size_t nelem, size_t elsize) +{ + void * p; + size_t sz = nelem * elsize; + NPY_BEGIN_THREADS_DEF; + if (sz < NBUCKETS) { + p = _npy_alloc_cache(sz, 1, NBUCKETS, datacache, &malloc); + if (p) { + memset(p, 0, sz); + } + return p; + } + NPY_BEGIN_THREADS; + p = calloc(nelem, elsize); + NPY_END_THREADS; + return p; +} + +// The default data mem allocator realloc routine does not make use of a ctx. +// It should be called only through PyDataMem_UserRENEW +// since itself does not handle eventhook and tracemalloc logic. +static NPY_INLINE void * +default_realloc(void *NPY_UNUSED(ctx), void *ptr, size_t new_size) +{ + return realloc(ptr, new_size); +} + +// The default data mem allocator free routine does not make use of a ctx. +// It should be called only through PyDataMem_UserFREE +// since itself does not handle eventhook and tracemalloc logic. +static NPY_INLINE void +default_free(void *NPY_UNUSED(ctx), void *ptr, size_t size) +{ + _npy_free_cache(ptr, size, NBUCKETS, datacache, &free); +} + +/* Memory handler global default */ +PyDataMem_Handler default_handler = { + "default_allocator", + 1, + { + NULL, /* ctx */ + default_malloc, /* malloc */ + default_calloc, /* calloc */ + default_realloc, /* realloc */ + default_free /* free */ + } +}; +/* singleton capsule of the default handler */ +PyObject *PyDataMem_DefaultHandler; + +#if (!defined(PYPY_VERSION_NUM) || PYPY_VERSION_NUM >= 0x07030600) +PyObject *current_handler; +#endif + +int uo_index=0; /* user_override index */ + +/* Wrappers for the default or any user-assigned PyDataMem_Handler */ + +NPY_NO_EXPORT void * +PyDataMem_UserNEW(size_t size, PyObject *mem_handler) +{ + void *result; + PyDataMem_Handler *handler = (PyDataMem_Handler *) PyCapsule_GetPointer(mem_handler, "mem_handler"); + if (handler == NULL) { + return NULL; + } + assert(size != 0); + result = handler->allocator.malloc(handler->allocator.ctx, size); + if (_PyDataMem_eventhook != NULL) { + NPY_ALLOW_C_API_DEF + NPY_ALLOW_C_API + if (_PyDataMem_eventhook != NULL) { + (*_PyDataMem_eventhook)(NULL, result, size, + _PyDataMem_eventhook_user_data); + } + NPY_DISABLE_C_API + } + PyTraceMalloc_Track(NPY_TRACE_DOMAIN, (npy_uintp)result, size); + return result; +} + +NPY_NO_EXPORT void * +PyDataMem_UserNEW_ZEROED(size_t nmemb, size_t size, PyObject *mem_handler) +{ + void *result; + PyDataMem_Handler *handler = (PyDataMem_Handler *) PyCapsule_GetPointer(mem_handler, "mem_handler"); + if (handler == NULL) { + return NULL; + } + result = handler->allocator.calloc(handler->allocator.ctx, nmemb, size); + if (_PyDataMem_eventhook != NULL) { + NPY_ALLOW_C_API_DEF + NPY_ALLOW_C_API + if (_PyDataMem_eventhook != NULL) { + (*_PyDataMem_eventhook)(NULL, result, nmemb * size, + _PyDataMem_eventhook_user_data); + } + NPY_DISABLE_C_API + } + PyTraceMalloc_Track(NPY_TRACE_DOMAIN, (npy_uintp)result, nmemb * size); + return result; +} + +/* Similar to array_dealloc in arrayobject.c */ +static NPY_INLINE void +WARN_IN_FREE(PyObject* warning, const char * msg) { + if (PyErr_WarnEx(warning, msg, 1) < 0) { + PyObject * s; + + s = PyUnicode_FromString("PyDataMem_UserFREE"); + if (s) { + PyErr_WriteUnraisable(s); + Py_DECREF(s); + } + else { + PyErr_WriteUnraisable(Py_None); + } + } +} + + + +NPY_NO_EXPORT void +PyDataMem_UserFREE(void *ptr, size_t size, PyObject *mem_handler) +{ + PyDataMem_Handler *handler = (PyDataMem_Handler *) PyCapsule_GetPointer(mem_handler, "mem_handler"); + if (handler == NULL) { + WARN_IN_FREE(PyExc_RuntimeWarning, + "Could not get pointer to 'mem_handler' from PyCapsule"); + PyErr_Clear(); + return; + } + PyTraceMalloc_Untrack(NPY_TRACE_DOMAIN, (npy_uintp)ptr); + handler->allocator.free(handler->allocator.ctx, ptr, size); + if (_PyDataMem_eventhook != NULL) { + NPY_ALLOW_C_API_DEF + NPY_ALLOW_C_API + if (_PyDataMem_eventhook != NULL) { + (*_PyDataMem_eventhook)(ptr, NULL, 0, + _PyDataMem_eventhook_user_data); + } + NPY_DISABLE_C_API + } +} + +NPY_NO_EXPORT void * +PyDataMem_UserRENEW(void *ptr, size_t size, PyObject *mem_handler) +{ + void *result; + PyDataMem_Handler *handler = (PyDataMem_Handler *) PyCapsule_GetPointer(mem_handler, "mem_handler"); + if (handler == NULL) { + return NULL; + } + + assert(size != 0); + result = handler->allocator.realloc(handler->allocator.ctx, ptr, size); + if (result != ptr) { + PyTraceMalloc_Untrack(NPY_TRACE_DOMAIN, (npy_uintp)ptr); + } + PyTraceMalloc_Track(NPY_TRACE_DOMAIN, (npy_uintp)result, size); + if (_PyDataMem_eventhook != NULL) { + NPY_ALLOW_C_API_DEF + NPY_ALLOW_C_API + if (_PyDataMem_eventhook != NULL) { + (*_PyDataMem_eventhook)(ptr, result, size, + _PyDataMem_eventhook_user_data); + } + NPY_DISABLE_C_API + } + return result; +} + +/*NUMPY_API + * Set a new allocation policy. If the input value is NULL, will reset + * the policy to the default. Return the previous policy, or + * return NULL if an error has occurred. We wrap the user-provided + * functions so they will still call the python and numpy + * memory management callback hooks. + */ +NPY_NO_EXPORT PyObject * +PyDataMem_SetHandler(PyObject *handler) +{ + PyObject *old_handler; +#if (!defined(PYPY_VERSION_NUM) || PYPY_VERSION_NUM >= 0x07030600) + PyObject *token; + if (PyContextVar_Get(current_handler, NULL, &old_handler)) { + return NULL; + } + if (handler == NULL) { + handler = PyDataMem_DefaultHandler; + } + token = PyContextVar_Set(current_handler, handler); + if (token == NULL) { + Py_DECREF(old_handler); + return NULL; + } + Py_DECREF(token); + return old_handler; +#else + PyObject *p; + p = PyThreadState_GetDict(); + if (p == NULL) { + return NULL; + } + old_handler = PyDict_GetItemString(p, "current_allocator"); + if (old_handler == NULL) { + old_handler = PyDataMem_DefaultHandler + } + Py_INCREF(old_handler); + if (handler == NULL) { + handler = PyDataMem_DefaultHandler; + } + const int error = PyDict_SetItemString(p, "current_allocator", handler); + if (error) { + Py_DECREF(old_handler); + return NULL; + } + return old_handler; +#endif +} + +/*NUMPY_API + * Return the policy that will be used to allocate data + * for the next PyArrayObject. On failure, return NULL. + */ +NPY_NO_EXPORT PyObject * +PyDataMem_GetHandler() +{ + PyObject *handler; +#if (!defined(PYPY_VERSION_NUM) || PYPY_VERSION_NUM >= 0x07030600) + if (PyContextVar_Get(current_handler, NULL, &handler)) { + return NULL; + } + return handler; +#else + PyObject *p = PyThreadState_GetDict(); + if (p == NULL) { + return NULL; + } + handler = PyDict_GetItemString(p, "current_allocator"); + if (handler == NULL) { + handler = PyCapsule_New(&default_handler, "mem_handler", NULL); + if (handler == NULL) { + return NULL; + } + } + else { + Py_INCREF(handler); + } + return handler; +#endif +} + +NPY_NO_EXPORT PyObject * +get_handler_name(PyObject *NPY_UNUSED(self), PyObject *args) +{ + PyObject *arr=NULL; + if (!PyArg_ParseTuple(args, "|O:get_handler_name", &arr)) { + return NULL; + } + if (arr != NULL && !PyArray_Check(arr)) { + PyErr_SetString(PyExc_ValueError, "if supplied, argument must be an ndarray"); + return NULL; + } + PyObject *mem_handler; + PyDataMem_Handler *handler; + PyObject *name; + if (arr != NULL) { + mem_handler = PyArray_HANDLER((PyArrayObject *) arr); + if (mem_handler == NULL) { + Py_RETURN_NONE; + } + Py_INCREF(mem_handler); + } + else { + mem_handler = PyDataMem_GetHandler(); + if (mem_handler == NULL) { + return NULL; + } + } + handler = (PyDataMem_Handler *) PyCapsule_GetPointer(mem_handler, "mem_handler"); + if (handler == NULL) { + Py_DECREF(mem_handler); + return NULL; + } + name = PyUnicode_FromString(handler->name); + Py_DECREF(mem_handler); + return name; +} + +NPY_NO_EXPORT PyObject * +get_handler_version(PyObject *NPY_UNUSED(self), PyObject *args) +{ + PyObject *arr=NULL; + if (!PyArg_ParseTuple(args, "|O:get_handler_version", &arr)) { + return NULL; + } + if (arr != NULL && !PyArray_Check(arr)) { + PyErr_SetString(PyExc_ValueError, "if supplied, argument must be an ndarray"); + return NULL; + } + PyObject *mem_handler; + PyDataMem_Handler *handler; + PyObject *version; + if (arr != NULL) { + mem_handler = PyArray_HANDLER((PyArrayObject *) arr); + if (mem_handler == NULL) { + Py_RETURN_NONE; + } + Py_INCREF(mem_handler); + } + else { + mem_handler = PyDataMem_GetHandler(); + if (mem_handler == NULL) { + return NULL; + } + } + handler = (PyDataMem_Handler *) PyCapsule_GetPointer(mem_handler, "mem_handler"); + if (handler == NULL) { + Py_DECREF(mem_handler); + return NULL; + } + version = PyLong_FromLong(handler->version); + Py_DECREF(mem_handler); + return version; +} diff --git a/numpy/core/src/multiarray/alloc.h b/numpy/core/src/multiarray/alloc.h index 2b69efc35205..13c82845813d 100644 --- a/numpy/core/src/multiarray/alloc.h +++ b/numpy/core/src/multiarray/alloc.h @@ -1,19 +1,26 @@ -#ifndef _NPY_ARRAY_ALLOC_H_ -#define _NPY_ARRAY_ALLOC_H_ +#ifndef NUMPY_CORE_SRC_MULTIARRAY_ALLOC_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_ALLOC_H_ + #define NPY_NO_DEPRECATED_API NPY_API_VERSION #define _MULTIARRAYMODULE -#include <numpy/ndarraytypes.h> +#include "numpy/ndarraytypes.h" #define NPY_TRACE_DOMAIN 389047 +NPY_NO_EXPORT PyObject * +_set_madvise_hugepage(PyObject *NPY_UNUSED(self), PyObject *enabled_obj); + NPY_NO_EXPORT void * -npy_alloc_cache(npy_uintp sz); +PyDataMem_UserNEW(npy_uintp sz, PyObject *mem_handler); NPY_NO_EXPORT void * -npy_alloc_cache_zero(npy_uintp sz); +PyDataMem_UserNEW_ZEROED(size_t nmemb, size_t size, PyObject *mem_handler); NPY_NO_EXPORT void -npy_free_cache(void * p, npy_uintp sd); +PyDataMem_UserFREE(void * p, npy_uintp sd, PyObject *mem_handler); + +NPY_NO_EXPORT void * +PyDataMem_UserRENEW(void *ptr, size_t size, PyObject *mem_handler); NPY_NO_EXPORT void * npy_alloc_cache_dim(npy_uintp sz); @@ -33,4 +40,14 @@ npy_free_cache_dim_array(PyArrayObject * arr) npy_free_cache_dim(PyArray_DIMS(arr), PyArray_NDIM(arr)); } +extern PyDataMem_Handler default_handler; +#if (!defined(PYPY_VERSION_NUM) || PYPY_VERSION_NUM >= 0x07030600) +extern PyObject *current_handler; /* PyContextVar/PyCapsule */ #endif + +NPY_NO_EXPORT PyObject * +get_handler_name(PyObject *NPY_UNUSED(self), PyObject *obj); +NPY_NO_EXPORT PyObject * +get_handler_version(PyObject *NPY_UNUSED(self), PyObject *obj); + +#endif /* NUMPY_CORE_SRC_MULTIARRAY_ALLOC_H_ */ diff --git a/numpy/core/src/multiarray/array_assign.c b/numpy/core/src/multiarray/array_assign.c deleted file mode 100644 index a48e245d8470..000000000000 --- a/numpy/core/src/multiarray/array_assign.c +++ /dev/null @@ -1,118 +0,0 @@ -/* - * This file implements some helper functions for the array assignment - * routines. The actual assignment routines are in array_assign_*.c - * - * Written by Mark Wiebe (mwwiebe@gmail.com) - * Copyright (c) 2011 by Enthought, Inc. - * - * See LICENSE.txt for the license. - */ - -#define PY_SSIZE_T_CLEAN -#include <Python.h> - -#define NPY_NO_DEPRECATED_API NPY_API_VERSION -#define _MULTIARRAYMODULE -#include <numpy/ndarraytypes.h> - -#include "npy_config.h" -#include "npy_pycompat.h" - -#include "shape.h" - -#include "array_assign.h" -#include "common.h" -#include "lowlevel_strided_loops.h" -#include "mem_overlap.h" - -/* See array_assign.h for parameter documentation */ -NPY_NO_EXPORT int -broadcast_strides(int ndim, npy_intp *shape, - int strides_ndim, npy_intp *strides_shape, npy_intp *strides, - char *strides_name, - npy_intp *out_strides) -{ - int idim, idim_start = ndim - strides_ndim; - - /* Can't broadcast to fewer dimensions */ - if (idim_start < 0) { - goto broadcast_error; - } - - /* - * Process from the end to the start, so that 'strides' and 'out_strides' - * can point to the same memory. - */ - for (idim = ndim - 1; idim >= idim_start; --idim) { - npy_intp strides_shape_value = strides_shape[idim - idim_start]; - /* If it doesn't have dimension one, it must match */ - if (strides_shape_value == 1) { - out_strides[idim] = 0; - } - else if (strides_shape_value != shape[idim]) { - goto broadcast_error; - } - else { - out_strides[idim] = strides[idim - idim_start]; - } - } - - /* New dimensions get a zero stride */ - for (idim = 0; idim < idim_start; ++idim) { - out_strides[idim] = 0; - } - - return 0; - -broadcast_error: { - PyObject *errmsg; - - errmsg = PyUString_FromFormat("could not broadcast %s from shape ", - strides_name); - PyUString_ConcatAndDel(&errmsg, - build_shape_string(strides_ndim, strides_shape)); - PyUString_ConcatAndDel(&errmsg, - PyUString_FromString(" into shape ")); - PyUString_ConcatAndDel(&errmsg, - build_shape_string(ndim, shape)); - PyErr_SetObject(PyExc_ValueError, errmsg); - Py_DECREF(errmsg); - - return -1; - } -} - -/* See array_assign.h for parameter documentation */ -NPY_NO_EXPORT int -raw_array_is_aligned(int ndim, char *data, npy_intp *strides, int alignment) -{ - if (alignment > 1) { - npy_intp align_check = (npy_intp)data; - int idim; - - for (idim = 0; idim < ndim; ++idim) { - align_check |= strides[idim]; - } - - return npy_is_aligned((void *)align_check, alignment); - } - else { - return 1; - } -} - - -/* Returns 1 if the arrays have overlapping data, 0 otherwise */ -NPY_NO_EXPORT int -arrays_overlap(PyArrayObject *arr1, PyArrayObject *arr2) -{ - mem_overlap_t result; - - result = solve_may_share_memory(arr1, arr2, NPY_MAY_SHARE_BOUNDS); - if (result == MEM_OVERLAP_NO) { - return 0; - } - else { - return 1; - } -} diff --git a/numpy/core/src/multiarray/array_assign_array.c b/numpy/core/src/multiarray/array_assign_array.c index 74fbb88c298c..020a7f29a615 100644 --- a/numpy/core/src/multiarray/array_assign_array.c +++ b/numpy/core/src/multiarray/array_assign_array.c @@ -6,13 +6,13 @@ * * See LICENSE.txt for the license. */ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE #define PY_SSIZE_T_CLEAN #include <Python.h> -#define NPY_NO_DEPRECATED_API NPY_API_VERSION -#define _MULTIARRAYMODULE -#include <numpy/ndarraytypes.h> +#include "numpy/ndarraytypes.h" #include "npy_config.h" #include "npy_pycompat.h" @@ -23,6 +23,48 @@ #include "lowlevel_strided_loops.h" #include "array_assign.h" +#include "dtype_transfer.h" + +/* + * Check that array data is both uint-aligned and true-aligned for all array + * elements, as required by the copy/casting code in lowlevel_strided_loops.c + */ +NPY_NO_EXPORT int +copycast_isaligned(int ndim, npy_intp const *shape, + PyArray_Descr *dtype, char *data, npy_intp const *strides) +{ + int aligned; + int big_aln, small_aln; + + int uint_aln = npy_uint_alignment(dtype->elsize); + int true_aln = dtype->alignment; + + /* uint alignment can be 0, meaning not uint alignable */ + if (uint_aln == 0) { + return 0; + } + + /* + * As an optimization, it is unnecessary to check the alignment to the + * smaller of (uint_aln, true_aln) if the data is aligned to the bigger of + * the two and the big is a multiple of the small aln. We check the bigger + * one first and only check the smaller if necessary. + */ + if (true_aln >= uint_aln) { + big_aln = true_aln; + small_aln = uint_aln; + } + else { + big_aln = uint_aln; + small_aln = true_aln; + } + + aligned = raw_array_is_aligned(ndim, shape, data, strides, big_aln); + if (aligned && big_aln % small_aln != 0) { + aligned = raw_array_is_aligned(ndim, shape, data, strides, small_aln); + } + return aligned; +} /* * Assigns the array from 'src' to 'dst'. The strides must already have @@ -31,9 +73,9 @@ * Returns 0 on success, -1 on failure. */ NPY_NO_EXPORT int -raw_array_assign_array(int ndim, npy_intp *shape, - PyArray_Descr *dst_dtype, char *dst_data, npy_intp *dst_strides, - PyArray_Descr *src_dtype, char *src_data, npy_intp *src_strides) +raw_array_assign_array(int ndim, npy_intp const *shape, + PyArray_Descr *dst_dtype, char *dst_data, npy_intp const *dst_strides, + PyArray_Descr *src_dtype, char *src_data, npy_intp const *src_strides) { int idim; npy_intp shape_it[NPY_MAXDIMS]; @@ -41,18 +83,13 @@ raw_array_assign_array(int ndim, npy_intp *shape, npy_intp src_strides_it[NPY_MAXDIMS]; npy_intp coord[NPY_MAXDIMS]; - PyArray_StridedUnaryOp *stransfer = NULL; - NpyAuxData *transferdata = NULL; int aligned, needs_api = 0; - npy_intp src_itemsize = src_dtype->elsize; NPY_BEGIN_THREADS_DEF; - /* Check alignment */ - aligned = raw_array_is_aligned(ndim, - dst_data, dst_strides, dst_dtype->alignment) && - raw_array_is_aligned(ndim, - src_data, src_strides, src_dtype->alignment); + aligned = + copycast_isaligned(ndim, shape, dst_dtype, dst_data, dst_strides) && + copycast_isaligned(ndim, shape, src_dtype, src_data, src_strides); /* Use raw iteration with no heap allocation */ if (PyArray_PrepareTwoRawArrayIter( @@ -78,12 +115,12 @@ raw_array_assign_array(int ndim, npy_intp *shape, } /* Get the function to do the casting */ + NPY_cast_info cast_info; if (PyArray_GetDTypeTransferFunction(aligned, src_strides_it[0], dst_strides_it[0], src_dtype, dst_dtype, 0, - &stransfer, &transferdata, - &needs_api) != NPY_SUCCEED) { + &cast_info, &needs_api) != NPY_SUCCEED) { return -1; } @@ -91,19 +128,26 @@ raw_array_assign_array(int ndim, npy_intp *shape, NPY_BEGIN_THREADS; } + npy_intp strides[2] = {src_strides_it[0], dst_strides_it[0]}; + NPY_RAW_ITER_START(idim, ndim, coord, shape_it) { /* Process the innermost dimension */ - stransfer(dst_data, dst_strides_it[0], src_data, src_strides_it[0], - shape_it[0], src_itemsize, transferdata); + char *args[2] = {src_data, dst_data}; + if (cast_info.func(&cast_info.context, + args, &shape_it[0], strides, cast_info.auxdata) < 0) { + goto fail; + } } NPY_RAW_ITER_TWO_NEXT(idim, ndim, coord, shape_it, dst_data, dst_strides_it, src_data, src_strides_it); NPY_END_THREADS; - - NPY_AUXDATA_FREE(transferdata); - - return (needs_api && PyErr_Occurred()) ? -1 : 0; + NPY_cast_info_xfree(&cast_info); + return 0; +fail: + NPY_END_THREADS; + NPY_cast_info_xfree(&cast_info); + return -1; } /* @@ -113,11 +157,11 @@ raw_array_assign_array(int ndim, npy_intp *shape, * Returns 0 on success, -1 on failure. */ NPY_NO_EXPORT int -raw_array_wheremasked_assign_array(int ndim, npy_intp *shape, - PyArray_Descr *dst_dtype, char *dst_data, npy_intp *dst_strides, - PyArray_Descr *src_dtype, char *src_data, npy_intp *src_strides, +raw_array_wheremasked_assign_array(int ndim, npy_intp const *shape, + PyArray_Descr *dst_dtype, char *dst_data, npy_intp const *dst_strides, + PyArray_Descr *src_dtype, char *src_data, npy_intp const *src_strides, PyArray_Descr *wheremask_dtype, char *wheremask_data, - npy_intp *wheremask_strides) + npy_intp const *wheremask_strides) { int idim; npy_intp shape_it[NPY_MAXDIMS]; @@ -126,18 +170,13 @@ raw_array_wheremasked_assign_array(int ndim, npy_intp *shape, npy_intp wheremask_strides_it[NPY_MAXDIMS]; npy_intp coord[NPY_MAXDIMS]; - PyArray_MaskedStridedUnaryOp *stransfer = NULL; - NpyAuxData *transferdata = NULL; int aligned, needs_api = 0; - npy_intp src_itemsize = src_dtype->elsize; NPY_BEGIN_THREADS_DEF; - /* Check alignment */ - aligned = raw_array_is_aligned(ndim, - dst_data, dst_strides, dst_dtype->alignment) && - raw_array_is_aligned(ndim, - src_data, src_strides, src_dtype->alignment); + aligned = + copycast_isaligned(ndim, shape, dst_dtype, dst_data, dst_strides) && + copycast_isaligned(ndim, shape, src_dtype, src_data, src_strides); /* Use raw iteration with no heap allocation */ if (PyArray_PrepareThreeRawArrayIter( @@ -167,35 +206,41 @@ raw_array_wheremasked_assign_array(int ndim, npy_intp *shape, } /* Get the function to do the casting */ + NPY_cast_info cast_info; if (PyArray_GetMaskedDTypeTransferFunction(aligned, src_strides_it[0], dst_strides_it[0], wheremask_strides_it[0], src_dtype, dst_dtype, wheremask_dtype, 0, - &stransfer, &transferdata, - &needs_api) != NPY_SUCCEED) { + &cast_info, &needs_api) != NPY_SUCCEED) { return -1; } if (!needs_api) { NPY_BEGIN_THREADS; } + npy_intp strides[2] = {src_strides_it[0], dst_strides_it[0]}; NPY_RAW_ITER_START(idim, ndim, coord, shape_it) { + PyArray_MaskedStridedUnaryOp *stransfer; + stransfer = (PyArray_MaskedStridedUnaryOp *)cast_info.func; + /* Process the innermost dimension */ - stransfer(dst_data, dst_strides_it[0], src_data, src_strides_it[0], - (npy_bool *)wheremask_data, wheremask_strides_it[0], - shape_it[0], src_itemsize, transferdata); + char *args[2] = {src_data, dst_data}; + if (stransfer(&cast_info.context, + args, &shape_it[0], strides, + (npy_bool *)wheremask_data, wheremask_strides_it[0], + cast_info.auxdata) < 0) { + break; + } } NPY_RAW_ITER_THREE_NEXT(idim, ndim, coord, shape_it, dst_data, dst_strides_it, src_data, src_strides_it, wheremask_data, wheremask_strides_it); NPY_END_THREADS; - - NPY_AUXDATA_FREE(transferdata); - + NPY_cast_info_xfree(&cast_info); return (needs_api && PyErr_Occurred()) ? -1 : 0; } @@ -268,19 +313,8 @@ PyArray_AssignArray(PyArrayObject *dst, PyArrayObject *src, /* Check the casting rule */ if (!PyArray_CanCastTypeTo(PyArray_DESCR(src), PyArray_DESCR(dst), casting)) { - PyObject *errmsg; - errmsg = PyUString_FromString("Cannot cast scalar from "); - PyUString_ConcatAndDel(&errmsg, - PyObject_Repr((PyObject *)PyArray_DESCR(src))); - PyUString_ConcatAndDel(&errmsg, - PyUString_FromString(" to ")); - PyUString_ConcatAndDel(&errmsg, - PyObject_Repr((PyObject *)PyArray_DESCR(dst))); - PyUString_ConcatAndDel(&errmsg, - PyUString_FromFormat(" according to the rule %s", - npy_casting_to_string(casting))); - PyErr_SetObject(PyExc_TypeError, errmsg); - Py_DECREF(errmsg); + npy_set_invalid_cast_error( + PyArray_DESCR(src), PyArray_DESCR(dst), casting, NPY_FALSE); goto fail; } @@ -383,14 +417,14 @@ PyArray_AssignArray(PyArrayObject *dst, PyArrayObject *src, /* A straightforward where-masked assignment */ /* Do the masked assignment with raw array iteration */ - if (raw_array_wheremasked_assign_array( - PyArray_NDIM(dst), PyArray_DIMS(dst), - PyArray_DESCR(dst), PyArray_DATA(dst), PyArray_STRIDES(dst), - PyArray_DESCR(src), PyArray_DATA(src), src_strides, - PyArray_DESCR(wheremask), PyArray_DATA(wheremask), - wheremask_strides) < 0) { - goto fail; - } + if (raw_array_wheremasked_assign_array( + PyArray_NDIM(dst), PyArray_DIMS(dst), + PyArray_DESCR(dst), PyArray_DATA(dst), PyArray_STRIDES(dst), + PyArray_DESCR(src), PyArray_DATA(src), src_strides, + PyArray_DESCR(wheremask), PyArray_DATA(wheremask), + wheremask_strides) < 0) { + goto fail; + } } if (copied_src) { diff --git a/numpy/core/src/multiarray/array_assign_scalar.c b/numpy/core/src/multiarray/array_assign_scalar.c index 17de99cb9a5a..4ffef7ecc96e 100644 --- a/numpy/core/src/multiarray/array_assign_scalar.c +++ b/numpy/core/src/multiarray/array_assign_scalar.c @@ -6,12 +6,12 @@ * * See LICENSE.txt for the license. */ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE #define PY_SSIZE_T_CLEAN #include <Python.h> -#define NPY_NO_DEPRECATED_API NPY_API_VERSION -#define _MULTIARRAYMODULE #include <numpy/ndarraytypes.h> #include "npy_config.h" @@ -23,6 +23,7 @@ #include "lowlevel_strided_loops.h" #include "array_assign.h" +#include "dtype_transfer.h" /* * Assigns the scalar value to every element of the destination raw array. @@ -30,27 +31,25 @@ * Returns 0 on success, -1 on failure. */ NPY_NO_EXPORT int -raw_array_assign_scalar(int ndim, npy_intp *shape, - PyArray_Descr *dst_dtype, char *dst_data, npy_intp *dst_strides, +raw_array_assign_scalar(int ndim, npy_intp const *shape, + PyArray_Descr *dst_dtype, char *dst_data, npy_intp const *dst_strides, PyArray_Descr *src_dtype, char *src_data) { int idim; npy_intp shape_it[NPY_MAXDIMS], dst_strides_it[NPY_MAXDIMS]; npy_intp coord[NPY_MAXDIMS]; - PyArray_StridedUnaryOp *stransfer = NULL; - NpyAuxData *transferdata = NULL; int aligned, needs_api = 0; - npy_intp src_itemsize = src_dtype->elsize; NPY_BEGIN_THREADS_DEF; - /* Check alignment */ - aligned = raw_array_is_aligned(ndim, dst_data, dst_strides, - dst_dtype->alignment); - if (!npy_is_aligned(src_data, src_dtype->alignment)) { - aligned = 0; - } + /* Check both uint and true alignment */ + aligned = raw_array_is_aligned(ndim, shape, dst_data, dst_strides, + npy_uint_alignment(dst_dtype->elsize)) && + raw_array_is_aligned(ndim, shape, dst_data, dst_strides, + dst_dtype->alignment) && + npy_is_aligned(src_data, npy_uint_alignment(src_dtype->elsize) && + npy_is_aligned(src_data, src_dtype->alignment)); /* Use raw iteration with no heap allocation */ if (PyArray_PrepareOneRawArrayIter( @@ -62,12 +61,12 @@ raw_array_assign_scalar(int ndim, npy_intp *shape, } /* Get the function to do the casting */ + NPY_cast_info cast_info; if (PyArray_GetDTypeTransferFunction(aligned, 0, dst_strides_it[0], src_dtype, dst_dtype, 0, - &stransfer, &transferdata, - &needs_api) != NPY_SUCCEED) { + &cast_info, &needs_api) != NPY_SUCCEED) { return -1; } @@ -79,18 +78,25 @@ raw_array_assign_scalar(int ndim, npy_intp *shape, NPY_BEGIN_THREADS_THRESHOLDED(nitems); } + npy_intp strides[2] = {0, dst_strides_it[0]}; + NPY_RAW_ITER_START(idim, ndim, coord, shape_it) { /* Process the innermost dimension */ - stransfer(dst_data, dst_strides_it[0], src_data, 0, - shape_it[0], src_itemsize, transferdata); + char *args[2] = {src_data, dst_data}; + if (cast_info.func(&cast_info.context, + args, &shape_it[0], strides, cast_info.auxdata) < 0) { + goto fail; + } } NPY_RAW_ITER_ONE_NEXT(idim, ndim, coord, shape_it, dst_data, dst_strides_it); NPY_END_THREADS; - - NPY_AUXDATA_FREE(transferdata); - - return (needs_api && PyErr_Occurred()) ? -1 : 0; + NPY_cast_info_xfree(&cast_info); + return 0; +fail: + NPY_END_THREADS; + NPY_cast_info_xfree(&cast_info); + return -1; } /* @@ -100,30 +106,28 @@ raw_array_assign_scalar(int ndim, npy_intp *shape, * Returns 0 on success, -1 on failure. */ NPY_NO_EXPORT int -raw_array_wheremasked_assign_scalar(int ndim, npy_intp *shape, - PyArray_Descr *dst_dtype, char *dst_data, npy_intp *dst_strides, +raw_array_wheremasked_assign_scalar(int ndim, npy_intp const *shape, + PyArray_Descr *dst_dtype, char *dst_data, npy_intp const *dst_strides, PyArray_Descr *src_dtype, char *src_data, PyArray_Descr *wheremask_dtype, char *wheremask_data, - npy_intp *wheremask_strides) + npy_intp const *wheremask_strides) { int idim; npy_intp shape_it[NPY_MAXDIMS], dst_strides_it[NPY_MAXDIMS]; npy_intp wheremask_strides_it[NPY_MAXDIMS]; npy_intp coord[NPY_MAXDIMS]; - PyArray_MaskedStridedUnaryOp *stransfer = NULL; - NpyAuxData *transferdata = NULL; int aligned, needs_api = 0; - npy_intp src_itemsize = src_dtype->elsize; NPY_BEGIN_THREADS_DEF; - /* Check alignment */ - aligned = raw_array_is_aligned(ndim, dst_data, dst_strides, - dst_dtype->alignment); - if (!npy_is_aligned(src_data, src_dtype->alignment)) { - aligned = 0; - } + /* Check both uint and true alignment */ + aligned = raw_array_is_aligned(ndim, shape, dst_data, dst_strides, + npy_uint_alignment(dst_dtype->elsize)) && + raw_array_is_aligned(ndim, shape, dst_data, dst_strides, + dst_dtype->alignment) && + npy_is_aligned(src_data, npy_uint_alignment(src_dtype->elsize) && + npy_is_aligned(src_data, src_dtype->alignment)); /* Use raw iteration with no heap allocation */ if (PyArray_PrepareTwoRawArrayIter( @@ -137,12 +141,12 @@ raw_array_wheremasked_assign_scalar(int ndim, npy_intp *shape, } /* Get the function to do the casting */ + NPY_cast_info cast_info; if (PyArray_GetMaskedDTypeTransferFunction(aligned, 0, dst_strides_it[0], wheremask_strides_it[0], src_dtype, dst_dtype, wheremask_dtype, 0, - &stransfer, &transferdata, - &needs_api) != NPY_SUCCEED) { + &cast_info, &needs_api) != NPY_SUCCEED) { return -1; } @@ -154,19 +158,26 @@ raw_array_wheremasked_assign_scalar(int ndim, npy_intp *shape, NPY_BEGIN_THREADS_THRESHOLDED(nitems); } + npy_intp strides[2] = {0, dst_strides_it[0]}; + NPY_RAW_ITER_START(idim, ndim, coord, shape_it) { /* Process the innermost dimension */ - stransfer(dst_data, dst_strides_it[0], src_data, 0, - (npy_bool *)wheremask_data, wheremask_strides_it[0], - shape_it[0], src_itemsize, transferdata); + PyArray_MaskedStridedUnaryOp *stransfer; + stransfer = (PyArray_MaskedStridedUnaryOp *)cast_info.func; + + char *args[2] = {src_data, dst_data}; + if (stransfer(&cast_info.context, + args, &shape_it[0], strides, + (npy_bool *)wheremask_data, wheremask_strides_it[0], + cast_info.auxdata) < 0) { + break; + } } NPY_RAW_ITER_TWO_NEXT(idim, ndim, coord, shape_it, dst_data, dst_strides_it, wheremask_data, wheremask_strides_it); NPY_END_THREADS; - - NPY_AUXDATA_FREE(transferdata); - + NPY_cast_info_xfree(&cast_info); return (needs_api && PyErr_Occurred()) ? -1 : 0; } @@ -201,19 +212,8 @@ PyArray_AssignRawScalar(PyArrayObject *dst, /* Check the casting rule */ if (!can_cast_scalar_to(src_dtype, src_data, PyArray_DESCR(dst), casting)) { - PyObject *errmsg; - errmsg = PyUString_FromString("Cannot cast scalar from "); - PyUString_ConcatAndDel(&errmsg, - PyObject_Repr((PyObject *)src_dtype)); - PyUString_ConcatAndDel(&errmsg, - PyUString_FromString(" to ")); - PyUString_ConcatAndDel(&errmsg, - PyObject_Repr((PyObject *)PyArray_DESCR(dst))); - PyUString_ConcatAndDel(&errmsg, - PyUString_FromFormat(" according to the rule %s", - npy_casting_to_string(casting))); - PyErr_SetObject(PyExc_TypeError, errmsg); - Py_DECREF(errmsg); + npy_set_invalid_cast_error( + src_dtype, PyArray_DESCR(dst), casting, NPY_TRUE); return -1; } @@ -224,7 +224,8 @@ PyArray_AssignRawScalar(PyArrayObject *dst, * we also skip this if 'dst' has an object dtype. */ if ((!PyArray_EquivTypes(PyArray_DESCR(dst), src_dtype) || - !npy_is_aligned(src_data, src_dtype->alignment)) && + !(npy_is_aligned(src_data, npy_uint_alignment(src_dtype->elsize)) && + npy_is_aligned(src_data, src_dtype->alignment))) && PyArray_SIZE(dst) > 1 && !PyDataType_REFCHK(PyArray_DESCR(dst))) { char *tmp_src_data; diff --git a/numpy/core/src/multiarray/array_coercion.c b/numpy/core/src/multiarray/array_coercion.c new file mode 100644 index 000000000000..2598e4bde6ea --- /dev/null +++ b/numpy/core/src/multiarray/array_coercion.c @@ -0,0 +1,1528 @@ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE +#define _UMATHMODULE + +#define PY_SSIZE_T_CLEAN +#include <Python.h> + +#include "numpy/npy_3kcompat.h" + +#include "lowlevel_strided_loops.h" +#include "numpy/arrayobject.h" + +#include "descriptor.h" +#include "convert_datatype.h" +#include "common_dtype.h" +#include "dtypemeta.h" + +#include "array_coercion.h" +#include "ctors.h" +#include "common.h" +#include "_datetime.h" +#include "npy_import.h" + + +/* + * This file defines helpers for some of the ctors.c functions which + * create an array from Python sequences and types. + * When creating an array with ``np.array(...)`` we have to do two main things: + * + * 1. Find the exact shape of the resulting array + * 2. Find the correct dtype of the resulting array. + * + * In most cases these two things are can be done in a single processing step. + * There are in principle three different calls that should be distinguished: + * + * 1. The user calls ``np.array(..., dtype=np.dtype("<f8"))`` + * 2. The user calls ``np.array(..., dtype="S")`` + * 3. The user calls ``np.array(...)`` + * + * In the first case, in principle only the shape needs to be found. In the + * second case, the DType class (e.g. string) is already known but the DType + * instance (e.g. length of the string) has to be found. + * In the last case the DType class needs to be found as well. Note that + * it is not necessary to find the DType class of the entire array, but + * the DType class needs to be found for each element before the actual + * dtype instance can be found. + * + * Further, there are a few other things to keep in mind when coercing arrays: + * + * * For UFunc promotion, Python scalars need to be handled specially to + * allow value based casting. This requires python complex/float to + * have their own DTypes. + * * It is necessary to decide whether or not a sequence is an element. + * For example tuples are considered elements for structured dtypes, but + * otherwise are considered sequences. + * This means that if a dtype is given (either as a class or instance), + * it can effect the dimension discovery part. + * For the "special" NumPy types structured void and "c" (single character) + * this is special cased. For future user-types, this is currently + * handled by providing calling an `is_known_scalar` method. This method + * currently ensures that Python numerical types are handled quickly. + * + * In the initial version of this implementation, it is assumed that dtype + * discovery can be implemented sufficiently fast. That is, it is not + * necessary to create fast paths that only find the correct shape e.g. when + * ``dtype=np.dtype("f8")`` is given. + * + * The code here avoid multiple conversion of array-like objects (including + * sequences). These objects are cached after conversion, which will require + * additional memory, but can drastically speed up coercion from from array + * like objects. + */ + + +/* + * For finding a DType quickly from a type, it is easiest to have a + * a mapping of pytype -> DType. + * TODO: This mapping means that it is currently impossible to delete a + * pair of pytype <-> DType. To resolve this, it is necessary to + * weakly reference the pytype. As long as the pytype is alive, we + * want to be able to use `np.array([pytype()])`. + * It should be possible to retrofit this without too much trouble + * (all type objects support weak references). + */ +PyObject *_global_pytype_to_type_dict = NULL; + + +/* Enum to track or signal some things during dtype and shape discovery */ +enum _dtype_discovery_flags { + FOUND_RAGGED_ARRAY = 1 << 0, + GAVE_SUBCLASS_WARNING = 1 << 1, + PROMOTION_FAILED = 1 << 2, + DISCOVER_STRINGS_AS_SEQUENCES = 1 << 3, + DISCOVER_TUPLES_AS_ELEMENTS = 1 << 4, + MAX_DIMS_WAS_REACHED = 1 << 5, + DESCRIPTOR_WAS_SET = 1 << 6, +}; + + +/** + * Adds known sequence types to the global type dictionary, note that when + * a DType is passed in, this lookup may be ignored. + * + * @return -1 on error 0 on success + */ +static int +_prime_global_pytype_to_type_dict(void) +{ + int res; + + /* Add the basic Python sequence types */ + res = PyDict_SetItem(_global_pytype_to_type_dict, + (PyObject *)&PyList_Type, Py_None); + if (res < 0) { + return -1; + } + res = PyDict_SetItem(_global_pytype_to_type_dict, + (PyObject *)&PyTuple_Type, Py_None); + if (res < 0) { + return -1; + } + /* NumPy Arrays are not handled as scalars */ + res = PyDict_SetItem(_global_pytype_to_type_dict, + (PyObject *)&PyArray_Type, Py_None); + if (res < 0) { + return -1; + } + return 0; +} + + +/** + * Add a new mapping from a python type to the DType class. For a user + * defined legacy dtype, this function does nothing unless the pytype + * subclass from `np.generic`. + * + * This assumes that the DType class is guaranteed to hold on the + * python type (this assumption is guaranteed). + * This functionality supersedes ``_typenum_fromtypeobj``. + * + * @param DType DType to map the python type to + * @param pytype Python type to map from + * @param userdef Whether or not it is user defined. We ensure that user + * defined scalars subclass from our scalars (for now). + */ +NPY_NO_EXPORT int +_PyArray_MapPyTypeToDType( + PyArray_DTypeMeta *DType, PyTypeObject *pytype, npy_bool userdef) +{ + PyObject *Dtype_obj = (PyObject *)DType; + + if (userdef && !PyObject_IsSubclass( + (PyObject *)pytype, (PyObject *)&PyGenericArrType_Type)) { + /* + * We expect that user dtypes (for now) will subclass some numpy + * scalar class to allow automatic discovery. + */ + if (NPY_DT_is_legacy(DType)) { + /* + * For legacy user dtypes, discovery relied on subclassing, but + * arbitrary type objects are supported, so do nothing. + */ + return 0; + } + /* + * We currently enforce that user DTypes subclass from `np.generic` + * (this should become a `np.generic` base class and may be lifted + * entirely). + */ + PyErr_Format(PyExc_RuntimeError, + "currently it is only possible to register a DType " + "for scalars deriving from `np.generic`, got '%S'.", + (PyObject *)pytype); + return -1; + } + + /* Create the global dictionary if it does not exist */ + if (NPY_UNLIKELY(_global_pytype_to_type_dict == NULL)) { + _global_pytype_to_type_dict = PyDict_New(); + if (_global_pytype_to_type_dict == NULL) { + return -1; + } + if (_prime_global_pytype_to_type_dict() < 0) { + return -1; + } + } + + int res = PyDict_Contains(_global_pytype_to_type_dict, (PyObject *)pytype); + if (res < 0) { + return -1; + } + else if (res) { + PyErr_SetString(PyExc_RuntimeError, + "Can only map one python type to DType."); + return -1; + } + + return PyDict_SetItem(_global_pytype_to_type_dict, + (PyObject *)pytype, Dtype_obj); +} + + +/** + * Lookup the DType for a registered known python scalar type. + * + * @param pytype Python Type to look up + * @return DType, None if it a known non-scalar, or NULL if an unknown object. + */ +static NPY_INLINE PyArray_DTypeMeta * +npy_discover_dtype_from_pytype(PyTypeObject *pytype) +{ + PyObject *DType; + + if (pytype == &PyArray_Type) { + Py_INCREF(Py_None); + return (PyArray_DTypeMeta *)Py_None; + } + + DType = PyDict_GetItem(_global_pytype_to_type_dict, (PyObject *)pytype); + if (DType == NULL) { + /* the python type is not known */ + return NULL; + } + + Py_INCREF(DType); + if (DType == Py_None) { + return (PyArray_DTypeMeta *)Py_None; + } + assert(PyObject_TypeCheck(DType, (PyTypeObject *)&PyArrayDTypeMeta_Type)); + return (PyArray_DTypeMeta *)DType; +} + + +/** + * Find the correct DType class for the given python type. If flags is NULL + * this is not used to discover a dtype, but only for conversion to an + * existing dtype. In that case the Python (not NumPy) scalar subclass + * checks are skipped. + * + * @param obj The python object, mainly type(pyobj) is used, the object + * is passed to reuse existing code at this time only. + * @param flags Flags used to know if warnings were already given. If + * flags is NULL, this is not + * @param fixed_DType if not NULL, will be checked first for whether or not + * it can/wants to handle the (possible) scalar value. + * @return New reference to either a DType class, Py_None, or NULL on error. + */ +static NPY_INLINE PyArray_DTypeMeta * +discover_dtype_from_pyobject( + PyObject *obj, enum _dtype_discovery_flags *flags, + PyArray_DTypeMeta *fixed_DType) +{ + if (fixed_DType != NULL) { + /* + * Let the given DType handle the discovery. This is when the + * scalar-type matches exactly, or the DType signals that it can + * handle the scalar-type. (Even if it cannot handle here it may be + * asked to attempt to do so later, if no other matching DType exists.) + */ + if ((Py_TYPE(obj) == fixed_DType->scalar_type) || + NPY_DT_CALL_is_known_scalar_type(fixed_DType, Py_TYPE(obj))) { + Py_INCREF(fixed_DType); + return fixed_DType; + } + } + + PyArray_DTypeMeta *DType = npy_discover_dtype_from_pytype(Py_TYPE(obj)); + if (DType != NULL) { + return DType; + } + /* + * At this point we have not found a clear mapping, but mainly for + * backward compatibility we have to make some further attempts at + * interpreting the input as a known scalar type. + */ + PyArray_Descr *legacy_descr; + if (PyArray_IsScalar(obj, Generic)) { + legacy_descr = PyArray_DescrFromScalar(obj); + if (legacy_descr == NULL) { + return NULL; + } + } + else if (flags == NULL) { + Py_INCREF(Py_None); + return (PyArray_DTypeMeta *)Py_None; + } + else if (PyBytes_Check(obj)) { + legacy_descr = PyArray_DescrFromType(NPY_BYTE); + } + else if (PyUnicode_Check(obj)) { + legacy_descr = PyArray_DescrFromType(NPY_UNICODE); + } + else { + legacy_descr = _array_find_python_scalar_type(obj); + } + + if (legacy_descr != NULL) { + DType = NPY_DTYPE(legacy_descr); + Py_INCREF(DType); + Py_DECREF(legacy_descr); + /* TODO: Enable warning about subclass handling */ + if ((0) && !((*flags) & GAVE_SUBCLASS_WARNING)) { + if (DEPRECATE_FUTUREWARNING( + "in the future NumPy will not automatically find the " + "dtype for subclasses of scalars known to NumPy (i.e. " + "python types). Use the appropriate `dtype=...` to create " + "this array. This will use the `object` dtype or raise " + "an error in the future.") < 0) { + return NULL; + } + *flags |= GAVE_SUBCLASS_WARNING; + } + return DType; + } + Py_INCREF(Py_None); + return (PyArray_DTypeMeta *)Py_None; +} + + +/** + * Discover the correct descriptor from a known DType class and scalar. + * If the fixed DType can discover a dtype instance/descr all is fine, + * if it cannot and DType is used instead, a cast will have to be tried. + * + * @param fixed_DType A user provided fixed DType, can be NULL + * @param DType A discovered DType (by discover_dtype_from_pyobject); + * this can be identical to `fixed_DType`, if it obj is a + * known scalar. Can be `NULL` indicating no known type. + * @param obj The Python scalar object. At the time of calling this function + * it must be known that `obj` should represent a scalar. + */ +static NPY_INLINE PyArray_Descr * +find_scalar_descriptor( + PyArray_DTypeMeta *fixed_DType, PyArray_DTypeMeta *DType, + PyObject *obj) +{ + PyArray_Descr *descr; + + if (DType == NULL && fixed_DType == NULL) { + /* No known DType and no fixed one means we go to object. */ + return PyArray_DescrFromType(NPY_OBJECT); + } + else if (DType == NULL) { + /* + * If no DType is known/found, give the fixed give one a second + * chance. This allows for example string, to call `str(obj)` to + * figure out the length for arbitrary objects. + */ + descr = NPY_DT_CALL_discover_descr_from_pyobject(fixed_DType, obj); + } + else { + descr = NPY_DT_CALL_discover_descr_from_pyobject(DType, obj); + } + if (descr == NULL) { + return NULL; + } + if (fixed_DType == NULL) { + return descr; + } + + Py_SETREF(descr, PyArray_CastDescrToDType(descr, fixed_DType)); + return descr; +} + + +/** + * Assign a single element in an array from a python value. + * + * The dtypes SETITEM should only be trusted to generally do the right + * thing if something is known to be a scalar *and* is of a python type known + * to the DType (which should include all basic Python math types), but in + * general a cast may be necessary. + * This function handles the cast, which is for example hit when assigning + * a float128 to complex128. + * + * At this time, this function does not support arrays (historically we + * mainly supported arrays through `__float__()`, etc.). Such support should + * possibly be added (although when called from `PyArray_AssignFromCache` + * the input cannot be an array). + * Note that this is also problematic for some array-likes, such as + * `astropy.units.Quantity` and `np.ma.masked`. These are used to us calling + * `__float__`/`__int__` for 0-D instances in many cases. + * Eventually, we may want to define this as wrong: They must use DTypes + * instead of (only) subclasses. Until then, here as well as in + * `PyArray_AssignFromCache` (which already does this), we need to special + * case 0-D array-likes to behave like arbitrary (unknown!) Python objects. + * + * @param descr + * @param item + * @param value + * @return 0 on success -1 on failure. + */ +/* + * TODO: This function should possibly be public API. + */ +NPY_NO_EXPORT int +PyArray_Pack(PyArray_Descr *descr, char *item, PyObject *value) +{ + PyArrayObject_fields arr_fields = { + .flags = NPY_ARRAY_WRITEABLE, /* assume array is not behaved. */ + }; + Py_SET_TYPE(&arr_fields, &PyArray_Type); + Py_SET_REFCNT(&arr_fields, 1); + + if (NPY_UNLIKELY(descr->type_num == NPY_OBJECT)) { + /* + * We always have store objects directly, casting will lose some + * type information. Any other dtype discards the type information. + * TODO: For a Categorical[object] this path may be necessary? + */ + arr_fields.descr = descr; + return descr->f->setitem(value, item, &arr_fields); + } + + /* discover_dtype_from_pyobject includes a check for is_known_scalar_type */ + PyArray_DTypeMeta *DType = discover_dtype_from_pyobject( + value, NULL, NPY_DTYPE(descr)); + if (DType == NULL) { + return -1; + } + if (DType == NPY_DTYPE(descr) || DType == (PyArray_DTypeMeta *)Py_None) { + /* We can set the element directly (or at least will try to) */ + Py_XDECREF(DType); + arr_fields.descr = descr; + return descr->f->setitem(value, item, &arr_fields); + } + PyArray_Descr *tmp_descr; + tmp_descr = NPY_DT_CALL_discover_descr_from_pyobject(DType, value); + Py_DECREF(DType); + if (tmp_descr == NULL) { + return -1; + } + + char *data = PyObject_Malloc(tmp_descr->elsize); + if (data == NULL) { + PyErr_NoMemory(); + Py_DECREF(tmp_descr); + return -1; + } + if (PyDataType_FLAGCHK(tmp_descr, NPY_NEEDS_INIT)) { + memset(data, 0, tmp_descr->elsize); + } + arr_fields.descr = tmp_descr; + if (tmp_descr->f->setitem(value, data, &arr_fields) < 0) { + PyObject_Free(data); + Py_DECREF(tmp_descr); + return -1; + } + if (PyDataType_REFCHK(tmp_descr)) { + /* We could probably use move-references above */ + PyArray_Item_INCREF(data, tmp_descr); + } + + int res = 0; + int needs_api = 0; + NPY_cast_info cast_info; + if (PyArray_GetDTypeTransferFunction( + 0, 0, 0, tmp_descr, descr, 0, &cast_info, + &needs_api) == NPY_FAIL) { + res = -1; + goto finish; + } + char *args[2] = {data, item}; + const npy_intp strides[2] = {0, 0}; + const npy_intp length = 1; + if (cast_info.func(&cast_info.context, + args, &length, strides, cast_info.auxdata) < 0) { + res = -1; + } + NPY_cast_info_xfree(&cast_info); + + finish: + if (PyDataType_REFCHK(tmp_descr)) { + /* We could probably use move-references above */ + PyArray_Item_XDECREF(data, tmp_descr); + } + PyObject_Free(data); + Py_DECREF(tmp_descr); + return res; +} + + +static int +update_shape(int curr_ndim, int *max_ndim, + npy_intp out_shape[NPY_MAXDIMS], int new_ndim, + const npy_intp new_shape[NPY_MAXDIMS], npy_bool sequence, + enum _dtype_discovery_flags *flags) +{ + int success = 0; /* unsuccessful if array is ragged */ + const npy_bool max_dims_reached = *flags & MAX_DIMS_WAS_REACHED; + + if (curr_ndim + new_ndim > *max_ndim) { + success = -1; + /* Only update/check as many dims as possible, max_ndim is unchanged */ + new_ndim = *max_ndim - curr_ndim; + } + else if (!sequence && (*max_ndim != curr_ndim + new_ndim)) { + /* + * Sequences do not update max_ndim, otherwise shrink and check. + * This is depth first, so if it is already set, `out_shape` is filled. + */ + *max_ndim = curr_ndim + new_ndim; + /* If a shape was already set, this is also ragged */ + if (max_dims_reached) { + success = -1; + } + } + for (int i = 0; i < new_ndim; i++) { + npy_intp curr_dim = out_shape[curr_ndim + i]; + npy_intp new_dim = new_shape[i]; + + if (!max_dims_reached) { + out_shape[curr_ndim + i] = new_dim; + } + else if (new_dim != curr_dim) { + /* The array is ragged, and this dimension is unusable already */ + success = -1; + if (!sequence) { + /* Remove dimensions that we cannot use: */ + *max_ndim -= new_ndim - i; + } + else { + assert(i == 0); + /* max_ndim is usually not updated for sequences, so set now: */ + *max_ndim = curr_ndim; + } + break; + } + } + if (!sequence) { + *flags |= MAX_DIMS_WAS_REACHED; + } + return success; +} + + +#define COERCION_CACHE_CACHE_SIZE 5 +static int _coercion_cache_num = 0; +static coercion_cache_obj *_coercion_cache_cache[COERCION_CACHE_CACHE_SIZE]; + +/* + * Steals a reference to the object. + */ +static NPY_INLINE int +npy_new_coercion_cache( + PyObject *converted_obj, PyObject *arr_or_sequence, npy_bool sequence, + coercion_cache_obj ***next_ptr, int ndim) +{ + coercion_cache_obj *cache; + if (_coercion_cache_num > 0) { + _coercion_cache_num--; + cache = _coercion_cache_cache[_coercion_cache_num]; + } + else { + cache = PyMem_Malloc(sizeof(coercion_cache_obj)); + } + if (cache == NULL) { + Py_DECREF(arr_or_sequence); + PyErr_NoMemory(); + return -1; + } + cache->converted_obj = converted_obj; + cache->arr_or_sequence = arr_or_sequence; + cache->sequence = sequence; + cache->depth = ndim; + cache->next = NULL; + **next_ptr = cache; + *next_ptr = &(cache->next); + return 0; +} + +/** + * Unlink coercion cache item. + * + * @param current + * @return next coercion cache object (or NULL) + */ +NPY_NO_EXPORT coercion_cache_obj * +npy_unlink_coercion_cache(coercion_cache_obj *current) +{ + coercion_cache_obj *next = current->next; + Py_DECREF(current->arr_or_sequence); + if (_coercion_cache_num < COERCION_CACHE_CACHE_SIZE) { + _coercion_cache_cache[_coercion_cache_num] = current; + _coercion_cache_num++; + } + else { + PyMem_Free(current); + } + return next; +} + +NPY_NO_EXPORT void +npy_free_coercion_cache(coercion_cache_obj *next) { + /* We only need to check from the last used cache pos */ + while (next != NULL) { + next = npy_unlink_coercion_cache(next); + } +} + +#undef COERCION_CACHE_CACHE_SIZE + +/** + * Do the promotion step and possible casting. This function should + * never be called if a descriptor was requested. In that case the output + * dtype is not of importance, so we must not risk promotion errors. + * + * @param out_descr The current descriptor. + * @param descr The newly found descriptor to promote with + * @param fixed_DType The user provided (fixed) DType or NULL + * @param flags dtype discover flags to signal failed promotion. + * @return -1 on error, 0 on success. + */ +static NPY_INLINE int +handle_promotion(PyArray_Descr **out_descr, PyArray_Descr *descr, + PyArray_DTypeMeta *fixed_DType, enum _dtype_discovery_flags *flags) +{ + assert(!(*flags & DESCRIPTOR_WAS_SET)); + + if (*out_descr == NULL) { + Py_INCREF(descr); + *out_descr = descr; + return 0; + } + PyArray_Descr *new_descr = PyArray_PromoteTypes(descr, *out_descr); + if (NPY_UNLIKELY(new_descr == NULL)) { + if (fixed_DType != NULL || PyErr_ExceptionMatches(PyExc_FutureWarning)) { + /* + * If a DType is fixed, promotion must not fail. Do not catch + * FutureWarning (raised for string+numeric promotions). We could + * only catch TypeError here or even always raise the error. + */ + return -1; + } + PyErr_Clear(); + *flags |= PROMOTION_FAILED; + /* Continue with object, since we may need the dimensionality */ + new_descr = PyArray_DescrFromType(NPY_OBJECT); + } + Py_SETREF(*out_descr, new_descr); + return 0; +} + + +/** + * Handle a leave node (known scalar) during dtype and shape discovery. + * + * @param obj The python object or nested sequence to convert + * @param curr_dims The current number of dimensions (depth in the recursion) + * @param max_dims The maximum number of dimensions. + * @param out_shape The discovered output shape, will be filled + * @param fixed_DType The user provided (fixed) DType or NULL + * @param flags used signal that this is a ragged array, used internally and + * can be expanded if necessary. + * @param DType the DType class that should be used, or NULL, if not provided. + * + * @return 0 on success -1 on error + */ +static NPY_INLINE int +handle_scalar( + PyObject *obj, int curr_dims, int *max_dims, + PyArray_Descr **out_descr, npy_intp *out_shape, + PyArray_DTypeMeta *fixed_DType, + enum _dtype_discovery_flags *flags, PyArray_DTypeMeta *DType) +{ + PyArray_Descr *descr; + + if (update_shape(curr_dims, max_dims, out_shape, + 0, NULL, NPY_FALSE, flags) < 0) { + *flags |= FOUND_RAGGED_ARRAY; + return *max_dims; + } + if (*flags & DESCRIPTOR_WAS_SET) { + /* no need to do any promotion */ + return *max_dims; + } + /* This is a scalar, so find the descriptor */ + descr = find_scalar_descriptor(fixed_DType, DType, obj); + if (descr == NULL) { + return -1; + } + if (handle_promotion(out_descr, descr, fixed_DType, flags) < 0) { + Py_DECREF(descr); + return -1; + } + Py_DECREF(descr); + return *max_dims; +} + + +/** + * Return the correct descriptor given an array object and a DType class. + * + * This is identical to casting the arrays descriptor/dtype to the new + * DType class + * + * @param arr The array object. + * @param DType The DType class to cast to (or NULL for convenience) + * @param out_descr The output descriptor will set. The result can be NULL + * when the array is of object dtype and has no elements. + * + * @return -1 on failure, 0 on success. + */ +static int +find_descriptor_from_array( + PyArrayObject *arr, PyArray_DTypeMeta *DType, PyArray_Descr **out_descr) +{ + enum _dtype_discovery_flags flags = 0; + *out_descr = NULL; + + if (DType == NULL) { + *out_descr = PyArray_DESCR(arr); + Py_INCREF(*out_descr); + return 0; + } + + if (NPY_UNLIKELY(NPY_DT_is_parametric(DType) && PyArray_ISOBJECT(arr))) { + /* + * We have one special case, if (and only if) the input array is of + * object DType and the dtype is not fixed already but parametric. + * Then, we allow inspection of all elements, treating them as + * elements. We do this recursively, so nested 0-D arrays can work, + * but nested higher dimensional arrays will lead to an error. + */ + assert(DType->type_num != NPY_OBJECT); /* not parametric */ + + PyArrayIterObject *iter; + iter = (PyArrayIterObject *)PyArray_IterNew((PyObject *)arr); + if (iter == NULL) { + return -1; + } + while (iter->index < iter->size) { + PyArray_DTypeMeta *item_DType; + /* + * Note: If the array contains typed objects we may need to use + * the dtype to use casting for finding the correct instance. + */ + PyObject *elem = PyArray_GETITEM(arr, iter->dataptr); + if (elem == NULL) { + Py_DECREF(iter); + return -1; + } + item_DType = discover_dtype_from_pyobject(elem, &flags, DType); + if (item_DType == NULL) { + Py_DECREF(iter); + Py_DECREF(elem); + return -1; + } + if (item_DType == (PyArray_DTypeMeta *)Py_None) { + Py_SETREF(item_DType, NULL); + } + int flat_max_dims = 0; + if (handle_scalar(elem, 0, &flat_max_dims, out_descr, + NULL, DType, &flags, item_DType) < 0) { + Py_DECREF(iter); + Py_DECREF(elem); + Py_XDECREF(*out_descr); + Py_XDECREF(item_DType); + return -1; + } + Py_XDECREF(item_DType); + Py_DECREF(elem); + PyArray_ITER_NEXT(iter); + } + Py_DECREF(iter); + } + else if (NPY_UNLIKELY(DType->type_num == NPY_DATETIME) && + PyArray_ISSTRING(arr)) { + /* + * TODO: This branch should be deprecated IMO, the workaround is + * to cast to the object to a string array. Although a specific + * function (if there is even any need) would be better. + * This is value based casting! + * Unless of course we actually want to support this kind of thing + * in general (not just for object dtype)... + */ + PyArray_DatetimeMetaData meta; + meta.base = NPY_FR_GENERIC; + meta.num = 1; + + if (find_string_array_datetime64_type(arr, &meta) < 0) { + return -1; + } + else { + *out_descr = create_datetime_dtype(NPY_DATETIME, &meta); + if (*out_descr == NULL) { + return -1; + } + } + } + else { + /* + * If this is not an object array figure out the dtype cast, + * or simply use the returned DType. + */ + *out_descr = PyArray_CastDescrToDType(PyArray_DESCR(arr), DType); + if (*out_descr == NULL) { + return -1; + } + } + return 0; +} + +/** + * Given a dtype or DType object, find the correct descriptor to cast the + * array to. + * + * This function is identical to normal casting using only the dtype, however, + * it supports inspecting the elements when the array has object dtype + * (and the given datatype describes a parametric DType class). + * + * @param arr + * @param dtype A dtype instance or class. + * @return A concrete dtype instance or NULL + */ +NPY_NO_EXPORT PyArray_Descr * +PyArray_AdaptDescriptorToArray(PyArrayObject *arr, PyObject *dtype) +{ + /* If the requested dtype is flexible, adapt it */ + PyArray_Descr *new_dtype; + PyArray_DTypeMeta *new_DType; + int res; + + res = PyArray_ExtractDTypeAndDescriptor((PyObject *)dtype, + &new_dtype, &new_DType); + if (res < 0) { + return NULL; + } + if (new_dtype == NULL) { + res = find_descriptor_from_array(arr, new_DType, &new_dtype); + if (res < 0) { + Py_DECREF(new_DType); + return NULL; + } + if (new_dtype == NULL) { + /* This is an object array but contained no elements, use default */ + new_dtype = NPY_DT_CALL_default_descr(new_DType); + } + } + Py_DECREF(new_DType); + return new_dtype; +} + + +/** + * Recursion helper for `PyArray_DiscoverDTypeAndShape`. See its + * documentation for additional details. + * + * @param obj The current (possibly nested) object + * @param curr_dims The current depth, i.e. initially 0 and increasing. + * @param max_dims Maximum number of dimensions, modified during discovery. + * @param out_descr dtype instance (or NULL) to promoted and update. + * @param out_shape The current shape (updated) + * @param coercion_cache_tail_ptr The tail of the linked list of coercion + * cache objects, which hold on to converted sequences and arrays. + * This is a pointer to the `->next` slot of the previous cache so + * that we can append a new cache object (and update this pointer). + * (Initially it is a pointer to the user-provided head pointer). + * @param fixed_DType User provided fixed DType class + * @param flags Discovery flags (reporting and behaviour flags, see def.) + * @param never_copy Specifies if a copy is allowed during array creation. + * @return The updated number of maximum dimensions (i.e. scalars will set + * this to the current dimensions). + */ +NPY_NO_EXPORT int +PyArray_DiscoverDTypeAndShape_Recursive( + PyObject *obj, int curr_dims, int max_dims, PyArray_Descr**out_descr, + npy_intp out_shape[NPY_MAXDIMS], + coercion_cache_obj ***coercion_cache_tail_ptr, + PyArray_DTypeMeta *fixed_DType, enum _dtype_discovery_flags *flags, + int never_copy) +{ + PyArrayObject *arr = NULL; + PyObject *seq; + + /* + * The first step is to find the DType class if it was not provided, + * alternatively we have to find out that this is not a scalar at all + * (which could fail and lead us to `object` dtype). + */ + PyArray_DTypeMeta *DType = NULL; + + if (NPY_UNLIKELY(*flags & DISCOVER_STRINGS_AS_SEQUENCES)) { + /* + * We currently support that bytes/strings are considered sequences, + * if the dtype is np.dtype('c'), this should be deprecated probably, + * but requires hacks right now. + */ + if (PyBytes_Check(obj) && PyBytes_Size(obj) != 1) { + goto force_sequence_due_to_char_dtype; + } + else if (PyUnicode_Check(obj) && PyUnicode_GetLength(obj) != 1) { + goto force_sequence_due_to_char_dtype; + } + } + + /* If this is a known scalar, find the corresponding DType class */ + DType = discover_dtype_from_pyobject(obj, flags, fixed_DType); + if (DType == NULL) { + return -1; + } + else if (DType == (PyArray_DTypeMeta *)Py_None) { + Py_DECREF(Py_None); + } + else { + max_dims = handle_scalar( + obj, curr_dims, &max_dims, out_descr, out_shape, fixed_DType, + flags, DType); + Py_DECREF(DType); + return max_dims; + } + + /* + * At this point we expect to find either a sequence, or an array-like. + * Although it is still possible that this fails and we have to use + * `object`. + */ + if (PyArray_Check(obj)) { + arr = (PyArrayObject *)obj; + Py_INCREF(arr); + } + else { + PyArray_Descr *requested_descr = NULL; + if (*flags & DESCRIPTOR_WAS_SET) { + /* __array__ may be passed the requested descriptor if provided */ + requested_descr = *out_descr; + } + arr = (PyArrayObject *)_array_from_array_like(obj, + requested_descr, 0, NULL, never_copy); + if (arr == NULL) { + return -1; + } + else if (arr == (PyArrayObject *)Py_NotImplemented) { + Py_DECREF(arr); + arr = NULL; + } + else if (curr_dims > 0 && curr_dims != max_dims) { + /* + * Deprecated 2020-12-09, NumPy 1.20 + * + * See https://github.com/numpy/numpy/issues/17965 + * Shapely had objects which are not sequences but did export + * the array-interface (and so are arguably array-like). + * Previously numpy would not use array-like information during + * shape discovery, so that it ended up acting as if this was + * an (unknown) scalar but with the specified dtype. + * Thus we ignore "scalars" here, as the value stored in the + * array should be acceptable. + */ + if (PyArray_NDIM(arr) > 0 && NPY_UNLIKELY(!PySequence_Check(obj))) { + if (PyErr_WarnFormat(PyExc_FutureWarning, 1, + "The input object of type '%s' is an array-like " + "implementing one of the corresponding protocols " + "(`__array__`, `__array_interface__` or " + "`__array_struct__`); but not a sequence (or 0-D). " + "In the future, this object will be coerced as if it " + "was first converted using `np.array(obj)`. " + "To retain the old behaviour, you have to either " + "modify the type '%s', or assign to an empty array " + "created with `np.empty(correct_shape, dtype=object)`.", + Py_TYPE(obj)->tp_name, Py_TYPE(obj)->tp_name) < 0) { + Py_DECREF(arr); + return -1; + } + /* + * Strangely enough, even though we threw away the result here, + * we did use it during descriptor discovery, so promote it: + */ + if (update_shape(curr_dims, &max_dims, out_shape, + 0, NULL, NPY_FALSE, flags) < 0) { + *flags |= FOUND_RAGGED_ARRAY; + Py_DECREF(arr); + return max_dims; + } + if (!(*flags & DESCRIPTOR_WAS_SET) && handle_promotion( + out_descr, PyArray_DESCR(arr), fixed_DType, flags) < 0) { + Py_DECREF(arr); + return -1; + } + Py_DECREF(arr); + return max_dims; + } + } + } + if (arr != NULL) { + /* + * This is an array object which will be added to the cache, keeps + * the reference to the array alive (takes ownership). + */ + if (npy_new_coercion_cache(obj, (PyObject *)arr, + 0, coercion_cache_tail_ptr, curr_dims) < 0) { + return -1; + } + + if (curr_dims == 0) { + /* + * Special case for reverse broadcasting, ignore max_dims if this + * is a single array-like object; needed for PyArray_CopyObject. + */ + memcpy(out_shape, PyArray_SHAPE(arr), + PyArray_NDIM(arr) * sizeof(npy_intp)); + max_dims = PyArray_NDIM(arr); + } + else if (update_shape(curr_dims, &max_dims, out_shape, + PyArray_NDIM(arr), PyArray_SHAPE(arr), NPY_FALSE, flags) < 0) { + *flags |= FOUND_RAGGED_ARRAY; + return max_dims; + } + + if (*flags & DESCRIPTOR_WAS_SET) { + return max_dims; + } + /* + * For arrays we may not just need to cast the dtype to the user + * provided fixed_DType. If this is an object array, the elements + * may need to be inspected individually. + * Note, this finds the descriptor of the array first and only then + * promotes here (different associativity). + */ + PyArray_Descr *cast_descr; + if (find_descriptor_from_array(arr, fixed_DType, &cast_descr) < 0) { + return -1; + } + if (cast_descr == NULL) { + /* object array with no elements, no need to promote/adjust. */ + return max_dims; + } + if (handle_promotion(out_descr, cast_descr, fixed_DType, flags) < 0) { + Py_DECREF(cast_descr); + return -1; + } + Py_DECREF(cast_descr); + return max_dims; + } + + /* + * The last step is to assume the input should be handled as a sequence + * and to handle it recursively. That is, unless we have hit the + * dimension limit. + */ + npy_bool is_sequence = PySequence_Check(obj); + if (is_sequence) { + is_sequence = PySequence_Size(obj) >= 0; + if (NPY_UNLIKELY(!is_sequence)) { + /* NOTE: This should likely just raise all errors */ + if (PyErr_ExceptionMatches(PyExc_RecursionError) || + PyErr_ExceptionMatches(PyExc_MemoryError)) { + /* + * Consider these unrecoverable errors, continuing execution + * might crash the interpreter. + */ + return -1; + } + PyErr_Clear(); + } + } + if (NPY_UNLIKELY(*flags & DISCOVER_TUPLES_AS_ELEMENTS) && + PyTuple_Check(obj)) { + is_sequence = NPY_FALSE; + } + if (curr_dims == max_dims || !is_sequence) { + /* Clear any PySequence_Size error which would corrupts further calls */ + max_dims = handle_scalar( + obj, curr_dims, &max_dims, out_descr, out_shape, fixed_DType, + flags, NULL); + if (is_sequence) { + /* Flag as ragged or too deep array */ + *flags |= FOUND_RAGGED_ARRAY; + } + return max_dims; + } + /* If we stop supporting bytes/str subclasses, more may be required here: */ + assert(!PyBytes_Check(obj) && !PyUnicode_Check(obj)); + + force_sequence_due_to_char_dtype: + + /* Ensure we have a sequence (required for PyPy) */ + seq = PySequence_Fast(obj, "Could not convert object to sequence"); + if (seq == NULL) { + /* + * Specifically do not fail on things that look like a dictionary, + * instead treat them as scalar. + */ + if (PyErr_ExceptionMatches(PyExc_KeyError)) { + PyErr_Clear(); + max_dims = handle_scalar( + obj, curr_dims, &max_dims, out_descr, out_shape, fixed_DType, + flags, NULL); + return max_dims; + } + return -1; + } + /* The cache takes ownership of the sequence here. */ + if (npy_new_coercion_cache(obj, seq, 1, coercion_cache_tail_ptr, curr_dims) < 0) { + return -1; + } + + npy_intp size = PySequence_Fast_GET_SIZE(seq); + PyObject **objects = PySequence_Fast_ITEMS(seq); + + if (update_shape(curr_dims, &max_dims, + out_shape, 1, &size, NPY_TRUE, flags) < 0) { + /* But do update, if there this is a ragged case */ + *flags |= FOUND_RAGGED_ARRAY; + return max_dims; + } + if (size == 0) { + /* If the sequence is empty, this must be the last dimension */ + *flags |= MAX_DIMS_WAS_REACHED; + return curr_dims + 1; + } + + /* Allow keyboard interrupts. See gh issue 18117. */ + if (PyErr_CheckSignals() < 0) { + return -1; + } + + /* Recursive call for each sequence item */ + for (Py_ssize_t i = 0; i < size; i++) { + max_dims = PyArray_DiscoverDTypeAndShape_Recursive( + objects[i], curr_dims + 1, max_dims, + out_descr, out_shape, coercion_cache_tail_ptr, fixed_DType, + flags, never_copy); + + if (max_dims < 0) { + return -1; + } + } + return max_dims; +} + + +/** + * Finds the DType and shape of an arbitrary nested sequence. This is the + * general purpose function to find the parameters of the array (but not + * the array itself) as returned by `np.array()` + * + * Note: Before considering to make part of this public, we should consider + * whether things such as `out_descr != NULL` should be supported in + * a public API. + * + * @param obj Scalar or nested sequences. + * @param max_dims Maximum number of dimensions (after this scalars are forced) + * @param out_shape Will be filled with the output shape (more than the actual + * shape may be written). + * @param coercion_cache NULL initialized reference to a cache pointer. + * May be set to the first coercion_cache, and has to be freed using + * npy_free_coercion_cache. + * This should be stored in a thread-safe manner (i.e. function static) + * and is designed to be consumed by `PyArray_AssignFromCache`. + * If not consumed, must be freed using `npy_free_coercion_cache`. + * @param fixed_DType A user provided fixed DType class. + * @param requested_descr A user provided fixed descriptor. This is always + * returned as the discovered descriptor, but currently only used + * for the ``__array__`` protocol. + * @param out_descr Set to the discovered output descriptor. This may be + * non NULL but only when fixed_DType/requested_descr are not given. + * If non NULL, it is the first dtype being promoted and used if there + * are no elements. + * The result may be unchanged (remain NULL) when converting a + * sequence with no elements. In this case it is callers responsibility + * to choose a default. + * @param never_copy Specifies that a copy is not allowed. + * @return dimensions of the discovered object or -1 on error. + * WARNING: If (and only if) the output is a single array, the ndim + * returned _can_ exceed the maximum allowed number of dimensions. + * It might be nice to deprecate this? But it allows things such as + * `arr1d[...] = np.array([[1,2,3,4]])` + */ +NPY_NO_EXPORT int +PyArray_DiscoverDTypeAndShape( + PyObject *obj, int max_dims, + npy_intp out_shape[NPY_MAXDIMS], + coercion_cache_obj **coercion_cache, + PyArray_DTypeMeta *fixed_DType, PyArray_Descr *requested_descr, + PyArray_Descr **out_descr, int never_copy) +{ + coercion_cache_obj **coercion_cache_head = coercion_cache; + *coercion_cache = NULL; + enum _dtype_discovery_flags flags = 0; + + /* + * Support a passed in descriptor (but only if nothing was specified). + */ + assert(*out_descr == NULL || fixed_DType == NULL); + /* Validate input of requested descriptor and DType */ + if (fixed_DType != NULL) { + assert(PyObject_TypeCheck( + (PyObject *)fixed_DType, (PyTypeObject *)&PyArrayDTypeMeta_Type)); + } + + if (requested_descr != NULL) { + assert(fixed_DType == NPY_DTYPE(requested_descr)); + /* The output descriptor must be the input. */ + Py_INCREF(requested_descr); + *out_descr = requested_descr; + flags |= DESCRIPTOR_WAS_SET; + } + + /* + * Call the recursive function, the setup for this may need expanding + * to handle caching better. + */ + + /* Legacy discovery flags */ + if (requested_descr != NULL) { + if (requested_descr->type_num == NPY_STRING && + requested_descr->type == 'c') { + /* Character dtype variation of string (should be deprecated...) */ + flags |= DISCOVER_STRINGS_AS_SEQUENCES; + } + else if (requested_descr->type_num == NPY_VOID && + (requested_descr->names || requested_descr->subarray)) { + /* Void is a chimera, in that it may or may not be structured... */ + flags |= DISCOVER_TUPLES_AS_ELEMENTS; + } + } + + int ndim = PyArray_DiscoverDTypeAndShape_Recursive( + obj, 0, max_dims, out_descr, out_shape, &coercion_cache, + fixed_DType, &flags, never_copy); + if (ndim < 0) { + goto fail; + } + + if (NPY_UNLIKELY(flags & FOUND_RAGGED_ARRAY)) { + /* + * If max-dims was reached and the dimensions reduced, this is ragged. + * Otherwise, we merely reached the maximum dimensions, which is + * slightly different. This happens for example for `[1, [2, 3]]` + * where the maximum dimensions is 1, but then a sequence found. + * + * In this case we need to inform the user and clean out the cache + * since it may be too deep. + */ + + /* Handle reaching the maximum depth differently: */ + int too_deep = ndim == max_dims; + + if (fixed_DType == NULL) { + /* This is discovered as object, but deprecated */ + static PyObject *visibleDeprecationWarning = NULL; + npy_cache_import( + "numpy", "VisibleDeprecationWarning", + &visibleDeprecationWarning); + if (visibleDeprecationWarning == NULL) { + goto fail; + } + if (!too_deep) { + /* NumPy 1.19, 2019-11-01 */ + if (PyErr_WarnEx(visibleDeprecationWarning, + "Creating an ndarray from ragged nested sequences (which " + "is a list-or-tuple of lists-or-tuples-or ndarrays with " + "different lengths or shapes) is deprecated. If you " + "meant to do this, you must specify 'dtype=object' " + "when creating the ndarray.", 1) < 0) { + goto fail; + } + } + else { + /* NumPy 1.20, 2020-05-08 */ + /* Note, max_dims should normally always be NPY_MAXDIMS here */ + if (PyErr_WarnFormat(visibleDeprecationWarning, 1, + "Creating an ndarray from nested sequences exceeding " + "the maximum number of dimensions of %d is deprecated. " + "If you mean to do this, you must specify " + "'dtype=object' when creating the ndarray.", + max_dims) < 0) { + goto fail; + } + } + /* Ensure that ragged arrays always return object dtype */ + Py_XSETREF(*out_descr, PyArray_DescrFromType(NPY_OBJECT)); + } + else if (fixed_DType->type_num != NPY_OBJECT) { + /* Only object DType supports ragged cases unify error */ + + /* + * We used to let certain ragged arrays pass if they also + * support e.g. conversion using `float(arr)`, which currently + * works for arrays with only one element. + * Thus we catch at least most of such cases here and give a + * DeprecationWarning instead of an error. + * Note that some of these will actually error later on when + * attempting to do the actual assign. + */ + int deprecate_single_element_ragged = 0; + coercion_cache_obj *current = *coercion_cache_head; + while (current != NULL) { + if (current->sequence) { + if (current->depth == ndim) { + /* + * Assume that only array-likes will allow the deprecated + * behaviour + */ + deprecate_single_element_ragged = 0; + break; + } + /* check next converted sequence/array-like */ + current = current->next; + continue; + } + PyArrayObject *arr = (PyArrayObject *)(current->arr_or_sequence); + assert(PyArray_NDIM(arr) + current->depth >= ndim); + if (PyArray_NDIM(arr) != ndim - current->depth) { + /* This array is not compatible with the final shape */ + if (PyArray_SIZE(arr) != 1) { + deprecate_single_element_ragged = 0; + break; + } + deprecate_single_element_ragged = 1; + } + current = current->next; + } + + if (deprecate_single_element_ragged) { + /* Deprecated 2020-07-24, NumPy 1.20 */ + if (DEPRECATE( + "setting an array element with a sequence. " + "This was supported in some cases where the elements " + "are arrays with a single element. For example " + "`np.array([1, np.array([2])], dtype=int)`. " + "In the future this will raise the same ValueError as " + "`np.array([1, [2]], dtype=int)`.") < 0) { + goto fail; + } + } + else if (!too_deep) { + PyObject *shape = PyArray_IntTupleFromIntp(ndim, out_shape); + PyErr_Format(PyExc_ValueError, + "setting an array element with a sequence. The " + "requested array has an inhomogeneous shape after " + "%d dimensions. The detected shape was " + "%R + inhomogeneous part.", + ndim, shape); + Py_DECREF(shape); + goto fail; + } + else { + PyErr_Format(PyExc_ValueError, + "setting an array element with a sequence. The " + "requested array would exceed the maximum number of " + "dimension of %d.", + max_dims); + goto fail; + } + } + + /* + * If the array is ragged, the cache may be too deep, so clean it. + * The cache is left at the same depth as the array though. + */ + coercion_cache_obj **next_ptr = coercion_cache_head; + coercion_cache_obj *current = *coercion_cache_head; /* item to check */ + while (current != NULL) { + if (current->depth > ndim) { + /* delete "next" cache item and advanced it (unlike later) */ + current = npy_unlink_coercion_cache(current); + continue; + } + /* advance both prev and next, and set prev->next to new item */ + *next_ptr = current; + next_ptr = &(current->next); + current = current->next; + } + *next_ptr = NULL; + } + /* We could check here for max-ndims being reached as well */ + + if (requested_descr != NULL) { + /* descriptor was provided, we did not accidentally change it */ + assert(*out_descr == requested_descr); + } + else if (NPY_UNLIKELY(*out_descr == NULL)) { + /* + * When the object contained no elements (sequence of length zero), + * the no descriptor may have been found. When a DType was requested + * we use it to define the output dtype. + * Otherwise, out_descr will remain NULL and the caller has to set + * the correct default. + */ + if (fixed_DType != NULL) { + *out_descr = NPY_DT_CALL_default_descr(fixed_DType); + if (*out_descr == NULL) { + goto fail; + } + } + } + return ndim; + + fail: + npy_free_coercion_cache(*coercion_cache_head); + *coercion_cache_head = NULL; + Py_XSETREF(*out_descr, NULL); + return -1; +} + + + +/** + * Check the descriptor is a legacy "flexible" DType instance, this is + * an instance which is (normally) not attached to an array, such as a string + * of length 0 or a datetime with no unit. + * These should be largely deprecated, and represent only the DType class + * for most `dtype` parameters. + * + * TODO: This function should eventually receive a deprecation warning and + * be removed. + * + * @param descr + * @return 1 if this is not a concrete dtype instance 0 otherwise + */ +static int +descr_is_legacy_parametric_instance(PyArray_Descr *descr) +{ + if (PyDataType_ISUNSIZED(descr)) { + return 1; + } + /* Flexible descr with generic time unit (which can be adapted) */ + if (PyDataType_ISDATETIME(descr)) { + PyArray_DatetimeMetaData *meta; + meta = get_datetime_metadata_from_dtype(descr); + if (meta->base == NPY_FR_GENERIC) { + return 1; + } + } + return 0; +} + + +/** + * Given either a DType instance or class, (or legacy flexible instance), + * ands sets output dtype instance and DType class. Both results may be + * NULL, but if `out_descr` is set `out_DType` will always be the + * corresponding class. + * + * @param dtype + * @param out_descr + * @param out_DType + * @return 0 on success -1 on failure + */ +NPY_NO_EXPORT int +PyArray_ExtractDTypeAndDescriptor(PyObject *dtype, + PyArray_Descr **out_descr, PyArray_DTypeMeta **out_DType) +{ + *out_DType = NULL; + *out_descr = NULL; + + if (dtype != NULL) { + if (PyObject_TypeCheck(dtype, (PyTypeObject *)&PyArrayDTypeMeta_Type)) { + assert(dtype != (PyObject * )&PyArrayDescr_Type); /* not np.dtype */ + *out_DType = (PyArray_DTypeMeta *)dtype; + Py_INCREF(*out_DType); + } + else if (PyObject_TypeCheck((PyObject *)Py_TYPE(dtype), + (PyTypeObject *)&PyArrayDTypeMeta_Type)) { + *out_DType = NPY_DTYPE(dtype); + Py_INCREF(*out_DType); + if (!descr_is_legacy_parametric_instance((PyArray_Descr *)dtype)) { + *out_descr = (PyArray_Descr *)dtype; + Py_INCREF(*out_descr); + } + } + else { + PyErr_SetString(PyExc_TypeError, + "dtype parameter must be a DType instance or class."); + return -1; + } + } + return 0; +} + + +/* + * Python API function to expose the dtype+shape discovery functionality + * directly. + */ +NPY_NO_EXPORT PyObject * +_discover_array_parameters(PyObject *NPY_UNUSED(self), + PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"obj", "dtype", NULL}; + + PyObject *obj; + PyObject *dtype = NULL; + PyArray_Descr *fixed_descriptor = NULL; + PyArray_DTypeMeta *fixed_DType = NULL; + npy_intp shape[NPY_MAXDIMS]; + + if (!PyArg_ParseTupleAndKeywords( + args, kwargs, "O|O:_discover_array_parameters", kwlist, + &obj, &dtype)) { + return NULL; + } + + if (PyArray_ExtractDTypeAndDescriptor(dtype, + &fixed_descriptor, &fixed_DType) < 0) { + return NULL; + } + + coercion_cache_obj *coercion_cache = NULL; + PyObject *out_dtype = NULL; + int ndim = PyArray_DiscoverDTypeAndShape( + obj, NPY_MAXDIMS, shape, + &coercion_cache, + fixed_DType, fixed_descriptor, (PyArray_Descr **)&out_dtype, 0); + Py_XDECREF(fixed_DType); + Py_XDECREF(fixed_descriptor); + if (ndim < 0) { + return NULL; + } + npy_free_coercion_cache(coercion_cache); + if (out_dtype == NULL) { + /* Empty sequence, report this as None. */ + out_dtype = Py_None; + Py_INCREF(Py_None); + } + + PyObject *shape_tuple = PyArray_IntTupleFromIntp(ndim, shape); + if (shape_tuple == NULL) { + return NULL; + } + + PyObject *res = PyTuple_Pack(2, (PyObject *)out_dtype, shape_tuple); + Py_DECREF(out_dtype); + Py_DECREF(shape_tuple); + return res; +} diff --git a/numpy/core/src/multiarray/array_coercion.h b/numpy/core/src/multiarray/array_coercion.h new file mode 100644 index 000000000000..f2482cecc005 --- /dev/null +++ b/numpy/core/src/multiarray/array_coercion.h @@ -0,0 +1,57 @@ +#ifndef NUMPY_CORE_SRC_MULTIARRAY_ARRAY_COERCION_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_ARRAY_COERCION_H_ + + +/* + * We do not want to coerce arrays many times unless absolutely necessary. + * The same goes for sequences, so everything we have seen, we will have + * to store somehow. This is a linked list of these objects. + */ +typedef struct coercion_cache_obj { + PyObject *converted_obj; + PyObject *arr_or_sequence; + struct coercion_cache_obj *next; + npy_bool sequence; + int depth; /* the dimension at which this object was found. */ +} coercion_cache_obj; + +NPY_NO_EXPORT int +_PyArray_MapPyTypeToDType( + PyArray_DTypeMeta *DType, PyTypeObject *pytype, npy_bool userdef); + +NPY_NO_EXPORT int +PyArray_Pack(PyArray_Descr *descr, char *item, PyObject *value); + +NPY_NO_EXPORT PyArray_Descr * +PyArray_AdaptDescriptorToArray(PyArrayObject *arr, PyObject *dtype); + +NPY_NO_EXPORT int +PyArray_DiscoverDTypeAndShape( + PyObject *obj, int max_dims, + npy_intp out_shape[NPY_MAXDIMS], + coercion_cache_obj **coercion_cache, + PyArray_DTypeMeta *fixed_DType, PyArray_Descr *requested_descr, + PyArray_Descr **out_descr, int never_copy); + +NPY_NO_EXPORT int +PyArray_ExtractDTypeAndDescriptor(PyObject *dtype, + PyArray_Descr **out_descr, PyArray_DTypeMeta **out_DType); + +NPY_NO_EXPORT PyObject * +_discover_array_parameters(PyObject *NPY_UNUSED(self), + PyObject *args, PyObject *kwargs); + + +/* Would make sense to inline the freeing functions everywhere */ +/* Frees the coercion cache object recursively. */ +NPY_NO_EXPORT void +npy_free_coercion_cache(coercion_cache_obj *first); + +/* unlink a single item and return the next */ +NPY_NO_EXPORT coercion_cache_obj * +npy_unlink_coercion_cache(coercion_cache_obj *current); + +NPY_NO_EXPORT int +PyArray_AssignFromCache(PyArrayObject *self, coercion_cache_obj *cache); + +#endif /* NUMPY_CORE_SRC_MULTIARRAY_ARRAY_COERCION_H_ */ diff --git a/numpy/core/src/multiarray/array_method.c b/numpy/core/src/multiarray/array_method.c new file mode 100644 index 000000000000..d93dac506949 --- /dev/null +++ b/numpy/core/src/multiarray/array_method.c @@ -0,0 +1,911 @@ +/* + * This file implements an abstraction layer for "Array methods", which + * work with a specific DType class input and provide low-level C function + * pointers to do fast operations on the given input functions. + * It thus adds an abstraction layer around individual ufunc loops. + * + * Unlike methods, a ArrayMethod can have multiple inputs and outputs. + * This has some serious implication for garbage collection, and as far + * as I (@seberg) understands, it is not possible to always guarantee correct + * cyclic garbage collection of dynamically created DTypes with methods. + * The keyword (or rather the solution) for this seems to be an "ephemeron" + * which I believe should allow correct garbage collection but seems + * not implemented in Python at this time. + * The vast majority of use-cases will not require correct garbage collection. + * Some use cases may require the user to be careful. + * + * Generally there are two main ways to solve this issue: + * + * 1. A method with a single input (or inputs of all the same DTypes) can + * be "owned" by that DType (it becomes unusable when the DType is deleted). + * This holds especially for all casts, which must have a defined output + * DType and must hold on to it strongly. + * 2. A method which can infer the output DType(s) from the input types does + * not need to keep the output type alive. (It can use NULL for the type, + * or an abstract base class which is known to be persistent.) + * It is then sufficient for a ufunc (or other owner) to only hold a + * weak reference to the input DTypes. + */ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE + +#include <npy_pycompat.h> +#include "arrayobject.h" +#include "array_method.h" +#include "dtypemeta.h" +#include "common_dtype.h" +#include "convert_datatype.h" +#include "common.h" + + +/* + * The default descriptor resolution function. The logic is as follows: + * + * 1. The output is ensured to be canonical (currently native byte order), + * if it is of the correct DType. + * 2. If any DType is was not defined, it is replaced by the common DType + * of all inputs. (If that common DType is parametric, this is an error.) + * + * We could allow setting the output descriptors specifically to simplify + * this step. + */ +static NPY_CASTING +default_resolve_descriptors( + PyArrayMethodObject *method, + PyArray_DTypeMeta **dtypes, + PyArray_Descr **input_descrs, + PyArray_Descr **output_descrs) +{ + int nin = method->nin; + int nout = method->nout; + + for (int i = 0; i < nin + nout; i++) { + PyArray_DTypeMeta *dtype = dtypes[i]; + if (input_descrs[i] != NULL) { + output_descrs[i] = ensure_dtype_nbo(input_descrs[i]); + } + else { + output_descrs[i] = NPY_DT_CALL_default_descr(dtype); + } + if (NPY_UNLIKELY(output_descrs[i] == NULL)) { + goto fail; + } + } + /* + * If we relax the requirement for specifying all `dtypes` (e.g. allow + * abstract ones or unspecified outputs). We can use the common-dtype + * operation to provide a default here. + */ + return method->casting; + + fail: + for (int i = 0; i < nin + nout; i++) { + Py_XDECREF(output_descrs[i]); + } + return -1; +} + + +NPY_INLINE static int +is_contiguous( + npy_intp const *strides, PyArray_Descr *const *descriptors, int nargs) +{ + for (int i = 0; i < nargs; i++) { + if (strides[i] != descriptors[i]->elsize) { + return 0; + } + } + return 1; +} + + +/** + * The default method to fetch the correct loop for a cast or ufunc + * (at the time of writing only casts). + * The default version can return loops explicitly registered during method + * creation. It does specialize contiguous loops, although has to check + * all descriptors itemsizes for this. + * + * @param context + * @param aligned + * @param move_references UNUSED. + * @param strides + * @param descriptors + * @param out_loop + * @param out_transferdata + * @param flags + * @return 0 on success -1 on failure. + */ +NPY_NO_EXPORT int +npy_default_get_strided_loop( + PyArrayMethod_Context *context, + int aligned, int NPY_UNUSED(move_references), npy_intp *strides, + PyArrayMethod_StridedLoop **out_loop, NpyAuxData **out_transferdata, + NPY_ARRAYMETHOD_FLAGS *flags) +{ + PyArray_Descr **descrs = context->descriptors; + PyArrayMethodObject *meth = context->method; + *flags = meth->flags & NPY_METH_RUNTIME_FLAGS; + *out_transferdata = NULL; + + int nargs = meth->nin + meth->nout; + if (aligned) { + if (meth->contiguous_loop == NULL || + !is_contiguous(strides, descrs, nargs)) { + *out_loop = meth->strided_loop; + return 0; + } + *out_loop = meth->contiguous_loop; + } + else { + if (meth->unaligned_contiguous_loop == NULL || + !is_contiguous(strides, descrs, nargs)) { + *out_loop = meth->unaligned_strided_loop; + return 0; + } + *out_loop = meth->unaligned_contiguous_loop; + } + return 0; +} + + +/** + * Validate that the input is usable to create a new ArrayMethod. + * + * @param spec + * @return 0 on success -1 on error. + */ +static int +validate_spec(PyArrayMethod_Spec *spec) +{ + int nargs = spec->nin + spec->nout; + /* Check the passed spec for invalid fields/values */ + if (spec->nin < 0 || spec->nout < 0 || nargs > NPY_MAXARGS) { + PyErr_Format(PyExc_ValueError, + "ArrayMethod inputs and outputs must be greater zero and" + "not exceed %d. (method: %s)", NPY_MAXARGS, spec->name); + return -1; + } + switch (spec->casting & ~_NPY_CAST_IS_VIEW) { + case NPY_NO_CASTING: + case NPY_EQUIV_CASTING: + case NPY_SAFE_CASTING: + case NPY_SAME_KIND_CASTING: + case NPY_UNSAFE_CASTING: + break; + default: + if (spec->casting != -1) { + PyErr_Format(PyExc_TypeError, + "ArrayMethod has invalid casting `%d`. (method: %s)", + spec->casting, spec->name); + return -1; + } + } + + for (int i = 0; i < nargs; i++) { + /* + * Note that we could allow for output dtypes to not be specified + * (the array-method would have to make sure to support this). + * We could even allow for some dtypes to be abstract. + * For now, assume that this is better handled in a promotion step. + * One problem with providing all DTypes is the definite need to + * hold references. We probably, eventually, have to implement + * traversal and trust the GC to deal with it. + */ + if (spec->dtypes[i] == NULL) { + PyErr_Format(PyExc_TypeError, + "ArrayMethod must provide all input and output DTypes. " + "(method: %s)", spec->name); + return -1; + } + if (!PyObject_TypeCheck(spec->dtypes[i], &PyArrayDTypeMeta_Type)) { + PyErr_Format(PyExc_TypeError, + "ArrayMethod provided object %R is not a DType." + "(method: %s)", spec->dtypes[i], spec->name); + return -1; + } + if (NPY_DT_is_abstract(spec->dtypes[i])) { + PyErr_Format(PyExc_TypeError, + "abstract DType %S are currently not supported." + "(method: %s)", spec->dtypes[i], spec->name); + return -1; + } + } + return 0; +} + + +/** + * Initialize a new BoundArrayMethodObject from slots. Slots which are + * not provided may be filled with defaults. + * + * @param res The new PyBoundArrayMethodObject to be filled. + * @param spec The specification list passed by the user. + * @param private Private flag to limit certain slots to use in NumPy. + * @return -1 on error 0 on success + */ +static int +fill_arraymethod_from_slots( + PyBoundArrayMethodObject *res, PyArrayMethod_Spec *spec, + int private) +{ + PyArrayMethodObject *meth = res->method; + + /* Set the defaults */ + meth->get_strided_loop = &npy_default_get_strided_loop; + meth->resolve_descriptors = &default_resolve_descriptors; + + /* Fill in the slots passed by the user */ + /* + * TODO: This is reasonable for now, but it would be nice to find a + * shorter solution, and add some additional error checking (e.g. + * the same slot used twice). Python uses an array of slot offsets. + */ + for (PyType_Slot *slot = &spec->slots[0]; slot->slot != 0; slot++) { + switch (slot->slot) { + case NPY_METH_resolve_descriptors: + meth->resolve_descriptors = slot->pfunc; + continue; + case NPY_METH_get_loop: + if (private) { + /* Only allow override for private functions initially */ + meth->get_strided_loop = slot->pfunc; + continue; + } + break; + case NPY_METH_strided_loop: + meth->strided_loop = slot->pfunc; + continue; + case NPY_METH_contiguous_loop: + meth->contiguous_loop = slot->pfunc; + continue; + case NPY_METH_unaligned_strided_loop: + meth->unaligned_strided_loop = slot->pfunc; + continue; + case NPY_METH_unaligned_contiguous_loop: + meth->unaligned_contiguous_loop = slot->pfunc; + continue; + default: + break; + } + PyErr_Format(PyExc_RuntimeError, + "invalid slot number %d to ArrayMethod: %s", + slot->slot, spec->name); + return -1; + } + + /* Check whether the slots are valid: */ + if (meth->resolve_descriptors == &default_resolve_descriptors) { + if (spec->casting == -1) { + PyErr_Format(PyExc_TypeError, + "Cannot set casting to -1 (invalid) when not providing " + "the default `resolve_descriptors` function. " + "(method: %s)", spec->name); + return -1; + } + for (int i = 0; i < meth->nin + meth->nout; i++) { + if (res->dtypes[i] == NULL) { + if (i < meth->nin) { + PyErr_Format(PyExc_TypeError, + "All input DTypes must be specified when using " + "the default `resolve_descriptors` function. " + "(method: %s)", spec->name); + return -1; + } + else if (meth->nin == 0) { + PyErr_Format(PyExc_TypeError, + "Must specify output DTypes or use custom " + "`resolve_descriptors` when there are no inputs. " + "(method: %s)", spec->name); + return -1; + } + } + if (i >= meth->nin && NPY_DT_is_parametric(res->dtypes[i])) { + PyErr_Format(PyExc_TypeError, + "must provide a `resolve_descriptors` function if any " + "output DType is parametric. (method: %s)", + spec->name); + return -1; + } + } + } + if (meth->get_strided_loop != &npy_default_get_strided_loop) { + /* Do not check the actual loop fields. */ + return 0; + } + + /* Check whether the provided loops make sense. */ + if (meth->strided_loop == NULL) { + PyErr_Format(PyExc_TypeError, + "Must provide a strided inner loop function. (method: %s)", + spec->name); + return -1; + } + if (meth->contiguous_loop == NULL) { + meth->contiguous_loop = meth->strided_loop; + } + if (meth->unaligned_contiguous_loop != NULL && + meth->unaligned_strided_loop == NULL) { + PyErr_Format(PyExc_TypeError, + "Must provide unaligned strided inner loop when providing " + "a contiguous version. (method: %s)", spec->name); + return -1; + } + if ((meth->unaligned_strided_loop == NULL) != + !(meth->flags & NPY_METH_SUPPORTS_UNALIGNED)) { + PyErr_Format(PyExc_TypeError, + "Must provide unaligned strided inner loop when providing " + "a contiguous version. (method: %s)", spec->name); + return -1; + } + + return 0; +} + + +/* + * Public version of `PyArrayMethod_FromSpec_int` (see below). + * + * TODO: Error paths will probably need to be improved before a release into + * the non-experimental public API. + */ +NPY_NO_EXPORT PyObject * +PyArrayMethod_FromSpec(PyArrayMethod_Spec *spec) +{ + for (int i = 0; i < spec->nin + spec->nout; i++) { + if (!PyObject_TypeCheck(spec->dtypes[i], &PyArrayDTypeMeta_Type)) { + PyErr_SetString(PyExc_RuntimeError, + "ArrayMethod spec contained a non DType."); + return NULL; + } + } + return (PyObject *)PyArrayMethod_FromSpec_int(spec, 0); +} + + +/** + * Create a new ArrayMethod (internal version). + * + * @param name A name for the individual method, may be NULL. + * @param spec A filled context object to pass generic information about + * the method (such as usually needing the API, and the DTypes). + * Unused fields must be NULL. + * @param slots Slots with the correct pair of IDs and (function) pointers. + * @param private Some slots are currently considered private, if not true, + * these will be rejected. + * + * @returns A new (bound) ArrayMethod object. + */ +NPY_NO_EXPORT PyBoundArrayMethodObject * +PyArrayMethod_FromSpec_int(PyArrayMethod_Spec *spec, int private) +{ + int nargs = spec->nin + spec->nout; + + if (spec->name == NULL) { + spec->name = "<unknown>"; + } + + if (validate_spec(spec) < 0) { + return NULL; + } + + PyBoundArrayMethodObject *res; + res = PyObject_New(PyBoundArrayMethodObject, &PyBoundArrayMethod_Type); + if (res == NULL) { + return NULL; + } + res->method = NULL; + + res->dtypes = PyMem_Malloc(sizeof(PyArray_DTypeMeta *) * nargs); + if (res->dtypes == NULL) { + Py_DECREF(res); + PyErr_NoMemory(); + return NULL; + } + for (int i = 0; i < nargs ; i++) { + Py_XINCREF(spec->dtypes[i]); + res->dtypes[i] = spec->dtypes[i]; + } + + res->method = PyObject_New(PyArrayMethodObject, &PyArrayMethod_Type); + if (res->method == NULL) { + Py_DECREF(res); + PyErr_NoMemory(); + return NULL; + } + memset((char *)(res->method) + sizeof(PyObject), 0, + sizeof(PyArrayMethodObject) - sizeof(PyObject)); + + res->method->nin = spec->nin; + res->method->nout = spec->nout; + res->method->flags = spec->flags; + res->method->casting = spec->casting; + if (fill_arraymethod_from_slots(res, spec, private) < 0) { + Py_DECREF(res); + return NULL; + } + + Py_ssize_t length = strlen(spec->name); + res->method->name = PyMem_Malloc(length + 1); + if (res->method->name == NULL) { + Py_DECREF(res); + PyErr_NoMemory(); + return NULL; + } + strcpy(res->method->name, spec->name); + + return res; +} + + +static void +arraymethod_dealloc(PyObject *self) +{ + PyArrayMethodObject *meth; + meth = ((PyArrayMethodObject *)self); + + PyMem_Free(meth->name); + + Py_TYPE(self)->tp_free(self); +} + + +NPY_NO_EXPORT PyTypeObject PyArrayMethod_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "numpy._ArrayMethod", + .tp_basicsize = sizeof(PyArrayMethodObject), + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_dealloc = arraymethod_dealloc, +}; + + +static PyObject * +boundarraymethod_repr(PyBoundArrayMethodObject *self) +{ + int nargs = self->method->nin + self->method->nout; + PyObject *dtypes = PyArray_TupleFromItems( + nargs, (PyObject **)self->dtypes, 0); + if (dtypes == NULL) { + return NULL; + } + PyObject *repr = PyUnicode_FromFormat( + "<np._BoundArrayMethod `%s` for dtypes %S>", + self->method->name, dtypes); + Py_DECREF(dtypes); + return repr; +} + + +static void +boundarraymethod_dealloc(PyObject *self) +{ + PyBoundArrayMethodObject *meth; + meth = ((PyBoundArrayMethodObject *)self); + int nargs = meth->method->nin + meth->method->nout; + + for (int i = 0; i < nargs; i++) { + Py_XDECREF(meth->dtypes[i]); + } + PyMem_Free(meth->dtypes); + + Py_XDECREF(meth->method); + + Py_TYPE(self)->tp_free(self); +} + + +/* + * Calls resolve_descriptors() and returns the casting level and the resolved + * descriptors as a tuple. If the operation is impossible returns (-1, None). + * May raise an error, but usually should not. + * The function validates the casting attribute compared to the returned + * casting level. + * + * TODO: This function is not public API, and certain code paths will need + * changes and especially testing if they were to be made public. + */ +static PyObject * +boundarraymethod__resolve_descripors( + PyBoundArrayMethodObject *self, PyObject *descr_tuple) +{ + int nin = self->method->nin; + int nout = self->method->nout; + + PyArray_Descr *given_descrs[NPY_MAXARGS]; + PyArray_Descr *loop_descrs[NPY_MAXARGS]; + + if (!PyTuple_CheckExact(descr_tuple) || + PyTuple_Size(descr_tuple) != nin + nout) { + PyErr_Format(PyExc_TypeError, + "_resolve_descriptors() takes exactly one tuple with as many " + "elements as the method takes arguments (%d+%d).", nin, nout); + return NULL; + } + + for (int i = 0; i < nin + nout; i++) { + PyObject *tmp = PyTuple_GetItem(descr_tuple, i); + if (tmp == NULL) { + return NULL; + } + else if (tmp == Py_None) { + if (i < nin) { + PyErr_SetString(PyExc_TypeError, + "only output dtypes may be omitted (set to None)."); + return NULL; + } + given_descrs[i] = NULL; + } + else if (PyArray_DescrCheck(tmp)) { + if (Py_TYPE(tmp) != (PyTypeObject *)self->dtypes[i]) { + PyErr_Format(PyExc_TypeError, + "input dtype %S was not an exact instance of the bound " + "DType class %S.", tmp, self->dtypes[i]); + return NULL; + } + given_descrs[i] = (PyArray_Descr *)tmp; + } + else { + PyErr_SetString(PyExc_TypeError, + "dtype tuple can only contain dtype instances or None."); + return NULL; + } + } + + NPY_CASTING casting = self->method->resolve_descriptors( + self->method, self->dtypes, given_descrs, loop_descrs); + + if (casting < 0 && PyErr_Occurred()) { + return NULL; + } + else if (casting < 0) { + return Py_BuildValue("iO", casting, Py_None); + } + + PyObject *result_tuple = PyTuple_New(nin + nout); + if (result_tuple == NULL) { + return NULL; + } + for (int i = 0; i < nin + nout; i++) { + /* transfer ownership to the tuple. */ + PyTuple_SET_ITEM(result_tuple, i, (PyObject *)loop_descrs[i]); + } + + /* + * The casting flags should be the most generic casting level (except the + * cast-is-view flag. If no input is parametric, it must match exactly. + * + * (Note that these checks are only debugging checks.) + */ + int parametric = 0; + for (int i = 0; i < nin + nout; i++) { + if (NPY_DT_is_parametric(self->dtypes[i])) { + parametric = 1; + break; + } + } + if (self->method->casting != -1) { + NPY_CASTING cast = casting & ~_NPY_CAST_IS_VIEW; + if (self->method->casting != + PyArray_MinCastSafety(cast, self->method->casting)) { + PyErr_Format(PyExc_RuntimeError, + "resolve_descriptors cast level did not match stored one. " + "(set level is %d, got %d for method %s)", + self->method->casting, cast, self->method->name); + Py_DECREF(result_tuple); + return NULL; + } + if (!parametric) { + /* + * Non-parametric can only mismatch if it switches from equiv to no + * (e.g. due to byteorder changes). + */ + if (cast != self->method->casting && + self->method->casting != NPY_EQUIV_CASTING) { + PyErr_Format(PyExc_RuntimeError, + "resolve_descriptors cast level changed even though " + "the cast is non-parametric where the only possible " + "change should be from equivalent to no casting. " + "(set level is %d, got %d for method %s)", + self->method->casting, cast, self->method->name); + Py_DECREF(result_tuple); + return NULL; + } + } + } + + return Py_BuildValue("iN", casting, result_tuple); +} + + +/* + * TODO: This function is not public API, and certain code paths will need + * changes and especially testing if they were to be made public. + */ +static PyObject * +boundarraymethod__simple_strided_call( + PyBoundArrayMethodObject *self, PyObject *arr_tuple) +{ + PyArrayObject *arrays[NPY_MAXARGS]; + PyArray_Descr *descrs[NPY_MAXARGS]; + PyArray_Descr *out_descrs[NPY_MAXARGS]; + Py_ssize_t length = -1; + int aligned = 1; + char *args[NPY_MAXARGS]; + npy_intp strides[NPY_MAXARGS]; + int nin = self->method->nin; + int nout = self->method->nout; + + if (!PyTuple_CheckExact(arr_tuple) || + PyTuple_Size(arr_tuple) != nin + nout) { + PyErr_Format(PyExc_TypeError, + "_simple_strided_call() takes exactly one tuple with as many " + "arrays as the method takes arguments (%d+%d).", nin, nout); + return NULL; + } + + for (int i = 0; i < nin + nout; i++) { + PyObject *tmp = PyTuple_GetItem(arr_tuple, i); + if (tmp == NULL) { + return NULL; + } + else if (!PyArray_CheckExact(tmp)) { + PyErr_SetString(PyExc_TypeError, + "All inputs must be NumPy arrays."); + return NULL; + } + arrays[i] = (PyArrayObject *)tmp; + descrs[i] = PyArray_DESCR(arrays[i]); + + /* Check that the input is compatible with a simple method call. */ + if (Py_TYPE(descrs[i]) != (PyTypeObject *)self->dtypes[i]) { + PyErr_Format(PyExc_TypeError, + "input dtype %S was not an exact instance of the bound " + "DType class %S.", descrs[i], self->dtypes[i]); + return NULL; + } + if (PyArray_NDIM(arrays[i]) != 1) { + PyErr_SetString(PyExc_ValueError, + "All arrays must be one dimensional."); + return NULL; + } + if (i == 0) { + length = PyArray_SIZE(arrays[i]); + } + else if (PyArray_SIZE(arrays[i]) != length) { + PyErr_SetString(PyExc_ValueError, + "All arrays must have the same length."); + return NULL; + } + if (i >= nin) { + if (PyArray_FailUnlessWriteable( + arrays[i], "_simple_strided_call() output") < 0) { + return NULL; + } + } + + args[i] = PyArray_BYTES(arrays[i]); + strides[i] = PyArray_STRIDES(arrays[i])[0]; + /* TODO: We may need to distinguish aligned and itemsize-aligned */ + aligned &= PyArray_ISALIGNED(arrays[i]); + } + if (!aligned && !(self->method->flags & NPY_METH_SUPPORTS_UNALIGNED)) { + PyErr_SetString(PyExc_ValueError, + "method does not support unaligned input."); + return NULL; + } + + NPY_CASTING casting = self->method->resolve_descriptors( + self->method, self->dtypes, descrs, out_descrs); + + if (casting < 0) { + PyObject *err_type = NULL, *err_value = NULL, *err_traceback = NULL; + PyErr_Fetch(&err_type, &err_value, &err_traceback); + PyErr_SetString(PyExc_TypeError, + "cannot perform method call with the given dtypes."); + npy_PyErr_ChainExceptions(err_type, err_value, err_traceback); + return NULL; + } + + int dtypes_were_adapted = 0; + for (int i = 0; i < nin + nout; i++) { + /* NOTE: This check is probably much stricter than necessary... */ + dtypes_were_adapted |= descrs[i] != out_descrs[i]; + Py_DECREF(out_descrs[i]); + } + if (dtypes_were_adapted) { + PyErr_SetString(PyExc_TypeError, + "_simple_strided_call(): requires dtypes to not require a cast " + "(must match exactly with `_resolve_descriptors()`)."); + return NULL; + } + + PyArrayMethod_Context context = { + .caller = NULL, + .method = self->method, + .descriptors = descrs, + }; + PyArrayMethod_StridedLoop *strided_loop = NULL; + NpyAuxData *loop_data = NULL; + NPY_ARRAYMETHOD_FLAGS flags = 0; + + if (self->method->get_strided_loop( + &context, aligned, 0, strides, + &strided_loop, &loop_data, &flags) < 0) { + return NULL; + } + + /* + * TODO: Add floating point error checks if requested and + * possibly release GIL if allowed by the flags. + */ + int res = strided_loop(&context, args, &length, strides, loop_data); + if (loop_data != NULL) { + loop_data->free(loop_data); + } + if (res < 0) { + return NULL; + } + Py_RETURN_NONE; +} + + +/* + * Support for masked inner-strided loops. Masked inner-strided loops are + * only used in the ufunc machinery. So this special cases them. + * In the future it probably makes sense to create an:: + * + * Arraymethod->get_masked_strided_loop() + * + * Function which this can wrap instead. + */ +typedef struct { + NpyAuxData base; + PyArrayMethod_StridedLoop *unmasked_stridedloop; + NpyAuxData *unmasked_auxdata; + int nargs; + char *dataptrs[]; +} _masked_stridedloop_data; + + +static void +_masked_stridedloop_data_free(NpyAuxData *auxdata) +{ + _masked_stridedloop_data *data = (_masked_stridedloop_data *)auxdata; + NPY_AUXDATA_FREE(data->unmasked_auxdata); + PyMem_Free(data); +} + + +/* + * This function wraps a regular unmasked strided-loop as a + * masked strided-loop, only calling the function for elements + * where the mask is True. + * + * TODO: Reductions also use this code to implement masked reductions. + * Before consolidating them, reductions had a special case for + * broadcasts: when the mask stride was 0 the code does not check all + * elements as `npy_memchr` currently does. + * It may be worthwhile to add such an optimization again if broadcasted + * masks are common enough. + */ +static int +generic_masked_strided_loop(PyArrayMethod_Context *context, + char *const *data, const npy_intp *dimensions, + const npy_intp *strides, NpyAuxData *_auxdata) +{ + _masked_stridedloop_data *auxdata = (_masked_stridedloop_data *)_auxdata; + int nargs = auxdata->nargs; + PyArrayMethod_StridedLoop *strided_loop = auxdata->unmasked_stridedloop; + NpyAuxData *strided_loop_auxdata = auxdata->unmasked_auxdata; + + char **dataptrs = auxdata->dataptrs; + memcpy(dataptrs, data, nargs * sizeof(char *)); + char *mask = data[nargs]; + npy_intp mask_stride = strides[nargs]; + + npy_intp N = dimensions[0]; + /* Process the data as runs of unmasked values */ + do { + Py_ssize_t subloopsize; + + /* Skip masked values */ + mask = npy_memchr(mask, 0, mask_stride, N, &subloopsize, 1); + for (int i = 0; i < nargs; i++) { + dataptrs[i] += subloopsize * strides[i]; + } + N -= subloopsize; + + /* Process unmasked values */ + mask = npy_memchr(mask, 0, mask_stride, N, &subloopsize, 0); + int res = strided_loop(context, + dataptrs, &subloopsize, strides, strided_loop_auxdata); + if (res != 0) { + return res; + } + for (int i = 0; i < nargs; i++) { + dataptrs[i] += subloopsize * strides[i]; + } + N -= subloopsize; + } while (N > 0); + + return 0; +} + + +/* + * Fetches a strided-loop function that supports a boolean mask as additional + * (last) operand to the strided-loop. It is otherwise largely identical to + * the `get_loop` method which it wraps. + * This is the core implementation for the ufunc `where=...` keyword argument. + * + * NOTE: This function does not support `move_references` or inner dimensions. + */ +NPY_NO_EXPORT int +PyArrayMethod_GetMaskedStridedLoop( + PyArrayMethod_Context *context, + int aligned, npy_intp *fixed_strides, + PyArrayMethod_StridedLoop **out_loop, + NpyAuxData **out_transferdata, + NPY_ARRAYMETHOD_FLAGS *flags) +{ + _masked_stridedloop_data *data; + int nargs = context->method->nin + context->method->nout; + + /* Add working memory for the data pointers, to modify them in-place */ + data = PyMem_Malloc(sizeof(_masked_stridedloop_data) + + sizeof(char *) * nargs); + if (data == NULL) { + PyErr_NoMemory(); + return -1; + } + data->base.free = _masked_stridedloop_data_free; + data->base.clone = NULL; /* not currently used */ + data->unmasked_stridedloop = NULL; + data->nargs = nargs; + + if (context->method->get_strided_loop(context, + aligned, 0, fixed_strides, + &data->unmasked_stridedloop, &data->unmasked_auxdata, flags) < 0) { + PyMem_Free(data); + return -1; + } + *out_transferdata = (NpyAuxData *)data; + *out_loop = generic_masked_strided_loop; + return 0; +} + + +PyMethodDef boundarraymethod_methods[] = { + {"_resolve_descriptors", (PyCFunction)boundarraymethod__resolve_descripors, + METH_O, "Resolve the given dtypes."}, + {"_simple_strided_call", (PyCFunction)boundarraymethod__simple_strided_call, + METH_O, "call on 1-d inputs and pre-allocated outputs (single call)."}, + {NULL, 0, 0, NULL}, +}; + + +static PyObject * +boundarraymethod__supports_unaligned(PyBoundArrayMethodObject *self) +{ + return PyBool_FromLong(self->method->flags & NPY_METH_SUPPORTS_UNALIGNED); +} + + +PyGetSetDef boundarraymethods_getters[] = { + {"_supports_unaligned", + (getter)boundarraymethod__supports_unaligned, NULL, + "whether the method supports unaligned inputs/outputs.", NULL}, + {NULL, NULL, NULL, NULL, NULL}, +}; + + +NPY_NO_EXPORT PyTypeObject PyBoundArrayMethod_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "numpy._BoundArrayMethod", + .tp_basicsize = sizeof(PyBoundArrayMethodObject), + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_repr = (reprfunc)boundarraymethod_repr, + .tp_dealloc = boundarraymethod_dealloc, + .tp_methods = boundarraymethod_methods, + .tp_getset = boundarraymethods_getters, +}; diff --git a/numpy/core/src/multiarray/array_method.h b/numpy/core/src/multiarray/array_method.h new file mode 100644 index 000000000000..7b7372bd0b59 --- /dev/null +++ b/numpy/core/src/multiarray/array_method.h @@ -0,0 +1,196 @@ +#ifndef NUMPY_CORE_SRC_MULTIARRAY_ARRAY_METHOD_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_ARRAY_METHOD_H_ + +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE + +#include <Python.h> +#include <numpy/ndarraytypes.h> + + +typedef enum { + /* Flag for whether the GIL is required */ + NPY_METH_REQUIRES_PYAPI = 1 << 1, + /* + * Some functions cannot set floating point error flags, this flag + * gives us the option (not requirement) to skip floating point error + * setup/check. No function should set error flags and ignore them + * since it would interfere with chaining operations (e.g. casting). + */ + /* TODO: Change this into a positive flag */ + NPY_METH_NO_FLOATINGPOINT_ERRORS = 1 << 2, + /* Whether the method supports unaligned access (not runtime) */ + NPY_METH_SUPPORTS_UNALIGNED = 1 << 3, + /* + * Private flag for now for *logic* functions. The logical functions + * `logical_or` and `logical_and` can always cast the inputs to booleans + * "safely" (because that is how the cast to bool is defined). + * @seberg: I am not sure this is the best way to handle this, so its + * private for now (also it is very limited anyway). + * There is one "exception". NA aware dtypes cannot cast to bool + * (hopefully), so the `??->?` loop should error even with this flag. + * But a second NA fallback loop will be necessary. + */ + _NPY_METH_FORCE_CAST_INPUTS = 1 << 17, + + /* All flags which can change at runtime */ + NPY_METH_RUNTIME_FLAGS = ( + NPY_METH_REQUIRES_PYAPI | + NPY_METH_NO_FLOATINGPOINT_ERRORS), +} NPY_ARRAYMETHOD_FLAGS; + + +struct PyArrayMethodObject_tag; + +/* + * This struct is specific to an individual (possibly repeated) call of + * the ArrayMethods strided operator, and as such is passed into the various + * methods of the ArrayMethod object (the resolve_descriptors function, + * the get_loop function and the individual lowlevel strided operator calls). + * It thus has to be persistent for one end-user call, and then be discarded. + * + * TODO: Before making this public, we should review which information should + * be stored on the Context/BoundArrayMethod vs. the ArrayMethod. + */ +typedef struct { + PyObject *caller; /* E.g. the original ufunc, may be NULL */ + struct PyArrayMethodObject_tag *method; + + /* Operand descriptors, filled in by resolve_descriptors */ + PyArray_Descr **descriptors; +} PyArrayMethod_Context; + + +typedef int (PyArrayMethod_StridedLoop)(PyArrayMethod_Context *context, + char *const *data, const npy_intp *dimensions, const npy_intp *strides, + NpyAuxData *transferdata); + + +typedef NPY_CASTING (resolve_descriptors_function)( + struct PyArrayMethodObject_tag *method, + PyArray_DTypeMeta **dtypes, + PyArray_Descr **given_descrs, + PyArray_Descr **loop_descrs); + + +typedef int (get_loop_function)( + PyArrayMethod_Context *context, + int aligned, int move_references, + npy_intp *strides, + PyArrayMethod_StridedLoop **out_loop, + NpyAuxData **out_transferdata, + NPY_ARRAYMETHOD_FLAGS *flags); + + +/* + * This struct will be public and necessary for creating a new ArrayMethod + * object (casting and ufuncs). + * We could version the struct, although since we allow passing arbitrary + * data using the slots, and have flags, that may be enough? + * (See also PyBoundArrayMethodObject.) + */ +typedef struct { + const char *name; + int nin, nout; + NPY_CASTING casting; + NPY_ARRAYMETHOD_FLAGS flags; + PyArray_DTypeMeta **dtypes; + PyType_Slot *slots; +} PyArrayMethod_Spec; + + +/* + * Structure of the ArrayMethod. This structure should probably not be made + * public. If necessary, we can make certain operations on it public + * (e.g. to allow users indirect access to `get_strided_loop`). + * + * NOTE: In some cases, it may not be clear whether information should be + * stored here or on the bound version. E.g. `nin` and `nout` (and in the + * future the gufunc `signature`) is already stored on the ufunc so that + * storing these here duplicates the information. + */ +typedef struct PyArrayMethodObject_tag { + PyObject_HEAD + char *name; + int nin, nout; + /* Casting is normally "safe" for functions, but is important for casts */ + NPY_CASTING casting; + /* default flags. The get_strided_loop function can override these */ + NPY_ARRAYMETHOD_FLAGS flags; + resolve_descriptors_function *resolve_descriptors; + get_loop_function *get_strided_loop; + /* Typical loop functions (contiguous ones are used in current casts) */ + PyArrayMethod_StridedLoop *strided_loop; + PyArrayMethod_StridedLoop *contiguous_loop; + PyArrayMethod_StridedLoop *unaligned_strided_loop; + PyArrayMethod_StridedLoop *unaligned_contiguous_loop; +} PyArrayMethodObject; + + +/* + * We will sometimes have to create a ArrayMethod and allow passing it around, + * similar to `instance.method` returning a bound method, e.g. a function like + * `ufunc.resolve()` can return a bound object. + * The current main purpose of the BoundArrayMethod is that it holds on to the + * `dtypes` (the classes), so that the `ArrayMethod` (e.g. for casts) will + * not create references cycles. In principle, it could hold any information + * which is also stored on the ufunc (and thus does not need to be repeated + * on the `ArrayMethod` itself. + */ +typedef struct { + PyObject_HEAD + PyArray_DTypeMeta **dtypes; + PyArrayMethodObject *method; +} PyBoundArrayMethodObject; + + +extern NPY_NO_EXPORT PyTypeObject PyArrayMethod_Type; +extern NPY_NO_EXPORT PyTypeObject PyBoundArrayMethod_Type; + +/* + * SLOTS IDs For the ArrayMethod creation, one public, the IDs are fixed. + * TODO: Before making it public, consider adding a large constant to private + * slots. + */ +#define NPY_METH_resolve_descriptors 1 +#define NPY_METH_get_loop 2 +#define NPY_METH_strided_loop 3 +#define NPY_METH_contiguous_loop 4 +#define NPY_METH_unaligned_strided_loop 5 +#define NPY_METH_unaligned_contiguous_loop 6 + + +/* + * Used internally (initially) for real to complex loops only + */ +NPY_NO_EXPORT int +npy_default_get_strided_loop( + PyArrayMethod_Context *context, + int aligned, int NPY_UNUSED(move_references), npy_intp *strides, + PyArrayMethod_StridedLoop **out_loop, NpyAuxData **out_transferdata, + NPY_ARRAYMETHOD_FLAGS *flags); + + +NPY_NO_EXPORT int +PyArrayMethod_GetMaskedStridedLoop( + PyArrayMethod_Context *context, + int aligned, + npy_intp *fixed_strides, + PyArrayMethod_StridedLoop **out_loop, + NpyAuxData **out_transferdata, + NPY_ARRAYMETHOD_FLAGS *flags); + + + +NPY_NO_EXPORT PyObject * +PyArrayMethod_FromSpec(PyArrayMethod_Spec *spec); + + +/* + * TODO: This function is the internal version, and its error paths may + * need better tests when a public version is exposed. + */ +NPY_NO_EXPORT PyBoundArrayMethodObject * +PyArrayMethod_FromSpec_int(PyArrayMethod_Spec *spec, int private); + +#endif /* NUMPY_CORE_SRC_MULTIARRAY_ARRAY_METHOD_H_ */ diff --git a/numpy/core/src/multiarray/arrayfunction_override.c b/numpy/core/src/multiarray/arrayfunction_override.c new file mode 100644 index 000000000000..463a2d4d8724 --- /dev/null +++ b/numpy/core/src/multiarray/arrayfunction_override.c @@ -0,0 +1,522 @@ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE + +#include "npy_pycompat.h" +#include "get_attr_string.h" +#include "npy_import.h" +#include "multiarraymodule.h" + + +/* Return the ndarray.__array_function__ method. */ +static PyObject * +get_ndarray_array_function(void) +{ + PyObject* method = PyObject_GetAttrString((PyObject *)&PyArray_Type, + "__array_function__"); + assert(method != NULL); + return method; +} + + +/* + * Get an object's __array_function__ method in the fastest way possible. + * Never raises an exception. Returns NULL if the method doesn't exist. + */ +static PyObject * +get_array_function(PyObject *obj) +{ + static PyObject *ndarray_array_function = NULL; + + if (ndarray_array_function == NULL) { + ndarray_array_function = get_ndarray_array_function(); + } + + /* Fast return for ndarray */ + if (PyArray_CheckExact(obj)) { + Py_INCREF(ndarray_array_function); + return ndarray_array_function; + } + + PyObject *array_function = PyArray_LookupSpecial(obj, "__array_function__"); + if (array_function == NULL && PyErr_Occurred()) { + PyErr_Clear(); /* TODO[gh-14801]: propagate crashes during attribute access? */ + } + + return array_function; +} + + +/* + * Like list.insert(), but for C arrays of PyObject*. Skips error checking. + */ +static void +pyobject_array_insert(PyObject **array, int length, int index, PyObject *item) +{ + for (int j = length; j > index; j--) { + array[j] = array[j - 1]; + } + array[index] = item; +} + + +/* + * Collects arguments with __array_function__ and their corresponding methods + * in the order in which they should be tried (i.e., skipping redundant types). + * `relevant_args` is expected to have been produced by PySequence_Fast. + * Returns the number of arguments, or -1 on failure. + */ +static int +get_implementing_args_and_methods(PyObject *relevant_args, + PyObject **implementing_args, + PyObject **methods) +{ + int num_implementing_args = 0; + + PyObject **items = PySequence_Fast_ITEMS(relevant_args); + Py_ssize_t length = PySequence_Fast_GET_SIZE(relevant_args); + + for (Py_ssize_t i = 0; i < length; i++) { + int new_class = 1; + PyObject *argument = items[i]; + + /* Have we seen this type before? */ + for (int j = 0; j < num_implementing_args; j++) { + if (Py_TYPE(argument) == Py_TYPE(implementing_args[j])) { + new_class = 0; + break; + } + } + if (new_class) { + PyObject *method = get_array_function(argument); + + if (method != NULL) { + int arg_index; + + if (num_implementing_args >= NPY_MAXARGS) { + PyErr_Format( + PyExc_TypeError, + "maximum number (%d) of distinct argument types " \ + "implementing __array_function__ exceeded", + NPY_MAXARGS); + Py_DECREF(method); + goto fail; + } + + /* "subclasses before superclasses, otherwise left to right" */ + arg_index = num_implementing_args; + for (int j = 0; j < num_implementing_args; j++) { + PyObject *other_type; + other_type = (PyObject *)Py_TYPE(implementing_args[j]); + if (PyObject_IsInstance(argument, other_type)) { + arg_index = j; + break; + } + } + Py_INCREF(argument); + pyobject_array_insert(implementing_args, num_implementing_args, + arg_index, argument); + pyobject_array_insert(methods, num_implementing_args, + arg_index, method); + ++num_implementing_args; + } + } + } + return num_implementing_args; + +fail: + for (int j = 0; j < num_implementing_args; j++) { + Py_DECREF(implementing_args[j]); + Py_DECREF(methods[j]); + } + return -1; +} + + +/* + * Is this object ndarray.__array_function__? + */ +static int +is_default_array_function(PyObject *obj) +{ + static PyObject *ndarray_array_function = NULL; + + if (ndarray_array_function == NULL) { + ndarray_array_function = get_ndarray_array_function(); + } + return obj == ndarray_array_function; +} + + +/* + * Core implementation of ndarray.__array_function__. This is exposed + * separately so we can avoid the overhead of a Python method call from + * within `implement_array_function`. + */ +NPY_NO_EXPORT PyObject * +array_function_method_impl(PyObject *func, PyObject *types, PyObject *args, + PyObject *kwargs) +{ + PyObject **items = PySequence_Fast_ITEMS(types); + Py_ssize_t length = PySequence_Fast_GET_SIZE(types); + + for (Py_ssize_t j = 0; j < length; j++) { + int is_subclass = PyObject_IsSubclass( + items[j], (PyObject *)&PyArray_Type); + if (is_subclass == -1) { + return NULL; + } + if (!is_subclass) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + } + + PyObject *implementation = PyObject_GetAttr(func, npy_ma_str_implementation); + if (implementation == NULL) { + return NULL; + } + PyObject *result = PyObject_Call(implementation, args, kwargs); + Py_DECREF(implementation); + return result; +} + + +/* + * Calls __array_function__ on the provided argument, with a fast-path for + * ndarray. + */ +static PyObject * +call_array_function(PyObject* argument, PyObject* method, + PyObject* public_api, PyObject* types, + PyObject* args, PyObject* kwargs) +{ + if (is_default_array_function(method)) { + return array_function_method_impl(public_api, types, args, kwargs); + } + else { + return PyObject_CallFunctionObjArgs( + method, argument, public_api, types, args, kwargs, NULL); + } +} + + +/** + * Internal handler for the array-function dispatching. The helper returns + * either the result, or NotImplemented (as a borrowed reference). + * + * @param public_api The public API symbol used for dispatching + * @param relevant_args Arguments which may implement __array_function__ + * @param args Original arguments + * @param kwargs Original keyword arguments + * + * @returns The result of the dispatched version, or a borrowed reference + * to NotImplemented to indicate the default implementation should + * be used. + */ +static PyObject * +array_implement_array_function_internal( + PyObject *public_api, PyObject *relevant_args, + PyObject *args, PyObject *kwargs) +{ + PyObject *implementing_args[NPY_MAXARGS]; + PyObject *array_function_methods[NPY_MAXARGS]; + PyObject *types = NULL; + + PyObject *result = NULL; + + static PyObject *errmsg_formatter = NULL; + + relevant_args = PySequence_Fast( + relevant_args, + "dispatcher for __array_function__ did not return an iterable"); + if (relevant_args == NULL) { + return NULL; + } + + /* Collect __array_function__ implementations */ + int num_implementing_args = get_implementing_args_and_methods( + relevant_args, implementing_args, array_function_methods); + if (num_implementing_args == -1) { + goto cleanup; + } + + /* + * Handle the typical case of no overrides. This is merely an optimization + * if some arguments are ndarray objects, but is also necessary if no + * arguments implement __array_function__ at all (e.g., if they are all + * built-in types). + */ + int any_overrides = 0; + for (int j = 0; j < num_implementing_args; j++) { + if (!is_default_array_function(array_function_methods[j])) { + any_overrides = 1; + break; + } + } + if (!any_overrides) { + /* + * When the default implementation should be called, return + * `Py_NotImplemented` to indicate this. + */ + result = Py_NotImplemented; + goto cleanup; + } + + /* + * Create a Python object for types. + * We use a tuple, because it's the fastest Python collection to create + * and has the bonus of being immutable. + */ + types = PyTuple_New(num_implementing_args); + if (types == NULL) { + goto cleanup; + } + for (int j = 0; j < num_implementing_args; j++) { + PyObject *arg_type = (PyObject *)Py_TYPE(implementing_args[j]); + Py_INCREF(arg_type); + PyTuple_SET_ITEM(types, j, arg_type); + } + + /* Call __array_function__ methods */ + for (int j = 0; j < num_implementing_args; j++) { + PyObject *argument = implementing_args[j]; + PyObject *method = array_function_methods[j]; + + /* + * We use `public_api` instead of `implementation` here so + * __array_function__ implementations can do equality/identity + * comparisons. + */ + result = call_array_function( + argument, method, public_api, types, args, kwargs); + + if (result == Py_NotImplemented) { + /* Try the next one */ + Py_DECREF(result); + result = NULL; + } + else { + /* Either a good result, or an exception was raised. */ + goto cleanup; + } + } + + /* No acceptable override found, raise TypeError. */ + npy_cache_import("numpy.core._internal", + "array_function_errmsg_formatter", + &errmsg_formatter); + if (errmsg_formatter != NULL) { + PyObject *errmsg = PyObject_CallFunctionObjArgs( + errmsg_formatter, public_api, types, NULL); + if (errmsg != NULL) { + PyErr_SetObject(PyExc_TypeError, errmsg); + Py_DECREF(errmsg); + } + } + +cleanup: + for (int j = 0; j < num_implementing_args; j++) { + Py_DECREF(implementing_args[j]); + Py_DECREF(array_function_methods[j]); + } + Py_XDECREF(types); + Py_DECREF(relevant_args); + return result; +} + + +/* + * Implements the __array_function__ protocol for a Python function, as described in + * in NEP-18. See numpy.core.overrides for a full docstring. + */ +NPY_NO_EXPORT PyObject * +array_implement_array_function( + PyObject *NPY_UNUSED(dummy), PyObject *positional_args) +{ + PyObject *implementation, *public_api, *relevant_args, *args, *kwargs; + + if (!PyArg_UnpackTuple( + positional_args, "implement_array_function", 5, 5, + &implementation, &public_api, &relevant_args, &args, &kwargs)) { + return NULL; + } + + /* + * Remove `like=` kwarg, which is NumPy-exclusive and thus not present + * in downstream libraries. If `like=` is specified but doesn't + * implement `__array_function__`, raise a `TypeError`. + */ + if (kwargs != NULL && PyDict_Contains(kwargs, npy_ma_str_like)) { + PyObject *like_arg = PyDict_GetItem(kwargs, npy_ma_str_like); + if (like_arg != NULL) { + PyObject *tmp_has_override = get_array_function(like_arg); + if (tmp_has_override == NULL) { + return PyErr_Format(PyExc_TypeError, + "The `like` argument must be an array-like that " + "implements the `__array_function__` protocol."); + } + Py_DECREF(tmp_has_override); + PyDict_DelItem(kwargs, npy_ma_str_like); + } + } + + PyObject *res = array_implement_array_function_internal( + public_api, relevant_args, args, kwargs); + + if (res == Py_NotImplemented) { + return PyObject_Call(implementation, args, kwargs); + } + return res; +} + +/* + * Implements the __array_function__ protocol for C array creation functions + * only. Added as an extension to NEP-18 in an effort to bring NEP-35 to + * life with minimal dispatch overhead. + * + * The caller must ensure that `like != NULL`. + */ +NPY_NO_EXPORT PyObject * +array_implement_c_array_function_creation( + const char *function_name, PyObject *like, + PyObject *args, PyObject *kwargs, + PyObject *const *fast_args, Py_ssize_t len_args, PyObject *kwnames) +{ + PyObject *relevant_args = NULL; + PyObject *numpy_module = NULL; + PyObject *public_api = NULL; + PyObject *result = NULL; + + /* If `like` doesn't implement `__array_function__`, raise a `TypeError` */ + PyObject *tmp_has_override = get_array_function(like); + if (tmp_has_override == NULL) { + return PyErr_Format(PyExc_TypeError, + "The `like` argument must be an array-like that " + "implements the `__array_function__` protocol."); + } + Py_DECREF(tmp_has_override); + + if (fast_args != NULL) { + /* + * Convert from vectorcall convention, since the protocol requires + * the normal convention. We have to do this late to ensure the + * normal path where NotImplemented is returned is fast. + */ + assert(args == NULL); + assert(kwargs == NULL); + args = PyTuple_New(len_args); + if (args == NULL) { + return NULL; + } + for (Py_ssize_t i = 0; i < len_args; i++) { + Py_INCREF(fast_args[i]); + PyTuple_SET_ITEM(args, i, fast_args[i]); + } + if (kwnames != NULL) { + kwargs = PyDict_New(); + if (kwargs == NULL) { + Py_DECREF(args); + return NULL; + } + Py_ssize_t nkwargs = PyTuple_GET_SIZE(kwnames); + for (Py_ssize_t i = 0; i < nkwargs; i++) { + PyObject *key = PyTuple_GET_ITEM(kwnames, i); + PyObject *value = fast_args[i+len_args]; + if (PyDict_SetItem(kwargs, key, value) < 0) { + Py_DECREF(args); + Py_DECREF(kwargs); + return NULL; + } + } + } + } + + relevant_args = PyTuple_Pack(1, like); + if (relevant_args == NULL) { + goto finish; + } + /* The like argument must be present in the keyword arguments, remove it */ + if (PyDict_DelItem(kwargs, npy_ma_str_like) < 0) { + goto finish; + } + + numpy_module = PyImport_Import(npy_ma_str_numpy); + if (numpy_module == NULL) { + goto finish; + } + + public_api = PyObject_GetAttrString(numpy_module, function_name); + Py_DECREF(numpy_module); + if (public_api == NULL) { + goto finish; + } + if (!PyCallable_Check(public_api)) { + PyErr_Format(PyExc_RuntimeError, + "numpy.%s is not callable.", function_name); + goto finish; + } + + result = array_implement_array_function_internal( + public_api, relevant_args, args, kwargs); + + finish: + if (kwnames != NULL) { + /* args and kwargs were converted from vectorcall convention */ + Py_XDECREF(args); + Py_XDECREF(kwargs); + } + Py_XDECREF(relevant_args); + Py_XDECREF(public_api); + return result; +} + + +/* + * Python wrapper for get_implementing_args_and_methods, for testing purposes. + */ +NPY_NO_EXPORT PyObject * +array__get_implementing_args( + PyObject *NPY_UNUSED(dummy), PyObject *positional_args) +{ + PyObject *relevant_args; + PyObject *implementing_args[NPY_MAXARGS]; + PyObject *array_function_methods[NPY_MAXARGS]; + PyObject *result = NULL; + + if (!PyArg_ParseTuple(positional_args, "O:array__get_implementing_args", + &relevant_args)) { + return NULL; + } + + relevant_args = PySequence_Fast( + relevant_args, + "dispatcher for __array_function__ did not return an iterable"); + if (relevant_args == NULL) { + return NULL; + } + + int num_implementing_args = get_implementing_args_and_methods( + relevant_args, implementing_args, array_function_methods); + if (num_implementing_args == -1) { + goto cleanup; + } + + /* create a Python object for implementing_args */ + result = PyList_New(num_implementing_args); + if (result == NULL) { + goto cleanup; + } + for (int j = 0; j < num_implementing_args; j++) { + PyObject *argument = implementing_args[j]; + Py_INCREF(argument); + PyList_SET_ITEM(result, j, argument); + } + +cleanup: + for (int j = 0; j < num_implementing_args; j++) { + Py_DECREF(implementing_args[j]); + Py_DECREF(array_function_methods[j]); + } + Py_DECREF(relevant_args); + return result; +} diff --git a/numpy/core/src/multiarray/arrayfunction_override.h b/numpy/core/src/multiarray/arrayfunction_override.h new file mode 100644 index 000000000000..09f7ee5480ed --- /dev/null +++ b/numpy/core/src/multiarray/arrayfunction_override.h @@ -0,0 +1,22 @@ +#ifndef NUMPY_CORE_SRC_MULTIARRAY_ARRAYFUNCTION_OVERRIDE_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_ARRAYFUNCTION_OVERRIDE_H_ + +NPY_NO_EXPORT PyObject * +array_implement_array_function( + PyObject *NPY_UNUSED(dummy), PyObject *positional_args); + +NPY_NO_EXPORT PyObject * +array__get_implementing_args( + PyObject *NPY_UNUSED(dummy), PyObject *positional_args); + +NPY_NO_EXPORT PyObject * +array_implement_c_array_function_creation( + const char *function_name, PyObject *like, + PyObject *args, PyObject *kwargs, + PyObject *const *fast_args, Py_ssize_t len_args, PyObject *kwnames); + +NPY_NO_EXPORT PyObject * +array_function_method_impl(PyObject *func, PyObject *types, PyObject *args, + PyObject *kwargs); + +#endif /* NUMPY_CORE_SRC_MULTIARRAY_ARRAYFUNCTION_OVERRIDE_H_ */ diff --git a/numpy/core/src/multiarray/arrayobject.c b/numpy/core/src/multiarray/arrayobject.c index 943edc772c9c..f99de2a39b4f 100644 --- a/numpy/core/src/multiarray/arrayobject.c +++ b/numpy/core/src/multiarray/arrayobject.c @@ -20,13 +20,13 @@ maintainer email: oliphant.travis@ieee.org Space Science Telescope Institute (J. Todd Miller, Perry Greenfield, Rick White) */ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE + #define PY_SSIZE_T_CLEAN #include <Python.h> -#include "structmember.h" +#include <structmember.h> -/*#include <stdio.h>*/ -#define NPY_NO_DEPRECATED_API NPY_API_VERSION -#define _MULTIARRAYMODULE #include "numpy/arrayobject.h" #include "numpy/arrayscalars.h" @@ -41,14 +41,17 @@ maintainer email: oliphant.travis@ieee.org #include "arraytypes.h" #include "scalartypes.h" #include "arrayobject.h" +#include "convert_datatype.h" +#include "conversion_utils.h" #include "ctors.h" +#include "dtypemeta.h" #include "methods.h" #include "descriptor.h" #include "iterators.h" #include "mapping.h" #include "getset.h" #include "sequence.h" -#include "buffer.h" +#include "npy_buffer.h" #include "array_assign.h" #include "alloc.h" #include "mem_overlap.h" @@ -56,6 +59,7 @@ maintainer email: oliphant.travis@ieee.org #include "strfuncs.h" #include "binop_override.h" +#include "array_coercion.h" /*NUMPY_API Compute the size of an array (in number of items) @@ -234,136 +238,96 @@ PyArray_SetBaseObject(PyArrayObject *arr, PyObject *obj) } +/** + * Assign an arbitrary object a NumPy array. This is largely basically + * identical to PyArray_FromAny, but assigns directly to the output array. + * + * @param dest Array to be written to + * @param src_object Object to be assigned, array-coercion rules apply. + * @return 0 on success -1 on failures. + */ /*NUMPY_API*/ NPY_NO_EXPORT int PyArray_CopyObject(PyArrayObject *dest, PyObject *src_object) { int ret = 0; - PyArrayObject *src; + PyArrayObject *view; PyArray_Descr *dtype = NULL; - int ndim = 0; + int ndim; npy_intp dims[NPY_MAXDIMS]; + coercion_cache_obj *cache = NULL; - Py_INCREF(src_object); /* - * Special code to mimic Numeric behavior for - * character arrays. + * We have to set the maximum number of dimensions here to support + * sequences within object arrays. */ - if (PyArray_DESCR(dest)->type == NPY_CHARLTR && - PyArray_NDIM(dest) > 0 && - PyString_Check(src_object)) { - npy_intp n_new, n_old; - char *new_string; - PyObject *tmp; + ndim = PyArray_DiscoverDTypeAndShape(src_object, + PyArray_NDIM(dest), dims, &cache, + NPY_DTYPE(PyArray_DESCR(dest)), PyArray_DESCR(dest), &dtype, 0); + if (ndim < 0) { + return -1; + } - n_new = PyArray_DIMS(dest)[PyArray_NDIM(dest)-1]; - n_old = PyString_Size(src_object); - if (n_new > n_old) { - new_string = malloc(n_new); - if (new_string == NULL) { - Py_DECREF(src_object); - PyErr_NoMemory(); - return -1; - } - memcpy(new_string, PyString_AS_STRING(src_object), n_old); - memset(new_string + n_old, ' ', n_new - n_old); - tmp = PyString_FromStringAndSize(new_string, n_new); - free(new_string); - Py_DECREF(src_object); - src_object = tmp; - } + if (cache != NULL && !(cache->sequence)) { + /* The input is an array or array object, so assign directly */ + assert(cache->converted_obj == src_object); + view = (PyArrayObject *)cache->arr_or_sequence; + Py_DECREF(dtype); + ret = PyArray_AssignArray(dest, view, NULL, NPY_UNSAFE_CASTING); + npy_free_coercion_cache(cache); + return ret; } /* - * Get either an array object we can copy from, or its parameters - * if there isn't a convenient array available. + * We may need to broadcast, due to shape mismatches, in this case + * create a temporary array first, and assign that after filling + * it from the sequences/scalar. */ - if (PyArray_GetArrayParamsFromObject(src_object, PyArray_DESCR(dest), - 0, &dtype, &ndim, dims, &src, NULL) < 0) { - Py_DECREF(src_object); - return -1; + if (ndim != PyArray_NDIM(dest) || + !PyArray_CompareLists(PyArray_DIMS(dest), dims, ndim)) { + /* + * Broadcasting may be necessary, so assign to a view first. + * This branch could lead to a shape mismatch error later. + */ + assert (ndim <= PyArray_NDIM(dest)); /* would error during discovery */ + view = (PyArrayObject *) PyArray_NewFromDescr( + &PyArray_Type, dtype, ndim, dims, NULL, NULL, + PyArray_FLAGS(dest) & NPY_ARRAY_F_CONTIGUOUS, NULL); + if (view == NULL) { + npy_free_coercion_cache(cache); + return -1; + } + } + else { + Py_DECREF(dtype); + view = dest; } - /* If it's not an array, either assign from a sequence or as a scalar */ - if (src == NULL) { - /* If the input is scalar */ - if (ndim == 0) { - /* If there's one dest element and src is a Python scalar */ - if (PyArray_IsScalar(src_object, Generic)) { - char *value; - int retcode; - - value = scalar_value(src_object, dtype); - if (value == NULL) { - Py_DECREF(dtype); - Py_DECREF(src_object); - return -1; - } - - /* TODO: switch to SAME_KIND casting */ - retcode = PyArray_AssignRawScalar(dest, dtype, value, - NULL, NPY_UNSAFE_CASTING); - Py_DECREF(dtype); - Py_DECREF(src_object); - return retcode; - } - /* Otherwise use the dtype's setitem function */ - else { - if (PyArray_SIZE(dest) == 1) { - Py_DECREF(dtype); - Py_DECREF(src_object); - ret = PyArray_SETITEM(dest, PyArray_DATA(dest), src_object); - return ret; - } - else { - src = (PyArrayObject *)PyArray_NewFromDescr(&PyArray_Type, - dtype, 0, NULL, NULL, - NULL, 0, NULL); - if (src == NULL) { - Py_DECREF(src_object); - return -1; - } - if (PyArray_SETITEM(src, PyArray_DATA(src), src_object) < 0) { - Py_DECREF(src_object); - Py_DECREF(src); - return -1; - } - } - } + /* Assign the values to `view` (whichever array that is) */ + if (cache == NULL) { + /* single (non-array) item, assign immediately */ + if (PyArray_Pack( + PyArray_DESCR(view), PyArray_DATA(view), src_object) < 0) { + goto fail; } - else { - /* - * If there are more than enough dims, use AssignFromSequence - * because it can handle this style of broadcasting. - */ - if (ndim >= PyArray_NDIM(dest)) { - int res; - Py_DECREF(dtype); - res = PyArray_AssignFromSequence(dest, src_object); - Py_DECREF(src_object); - return res; - } - /* Otherwise convert to an array and do an array-based copy */ - src = (PyArrayObject *)PyArray_NewFromDescr(&PyArray_Type, - dtype, ndim, dims, NULL, NULL, - PyArray_ISFORTRAN(dest), NULL); - if (src == NULL) { - Py_DECREF(src_object); - return -1; - } - if (PyArray_AssignFromSequence(src, src_object) < 0) { - Py_DECREF(src); - Py_DECREF(src_object); - return -1; - } + } + else { + if (PyArray_AssignFromCache(view, cache) < 0) { + goto fail; } } - - /* If it's an array, do a move (handling possible overlapping data) */ - ret = PyArray_MoveInto(dest, src); - Py_DECREF(src); - Py_DECREF(src_object); + if (view == dest) { + return 0; + } + ret = PyArray_AssignArray(dest, view, NULL, NPY_UNSAFE_CASTING); + Py_DECREF(view); return ret; + + fail: + if (view != dest) { + Py_DECREF(view); + } + return -1; } @@ -388,7 +352,7 @@ PyArray_CopyObject(PyArrayObject *dest, PyObject *src_object) /*NUMPY_API */ NPY_NO_EXPORT int -PyArray_TypeNumFromName(char *str) +PyArray_TypeNumFromName(char const *str) { int i; PyArray_Descr *descr; @@ -453,7 +417,7 @@ WARN_IN_DEALLOC(PyObject* warning, const char * msg) { if (PyErr_WarnEx(warning, msg, 1) < 0) { PyObject * s; - s = PyUString_FromString("array_dealloc"); + s = PyUnicode_FromString("array_dealloc"); if (s) { PyErr_WriteUnraisable(s); Py_DECREF(s); @@ -462,7 +426,7 @@ WARN_IN_DEALLOC(PyObject* warning, const char * msg) { PyErr_WriteUnraisable(Py_None); } } -}; +} /* array object functions */ @@ -471,7 +435,9 @@ array_dealloc(PyArrayObject *self) { PyArrayObject_fields *fa = (PyArrayObject_fields *)self; - _array_dealloc_buffer_info(self); + if (_buffer_info_free(fa->_buffer_info, (PyObject *)self) < 0) { + PyErr_WriteUnraisable(NULL); + } if (fa->weakreflist != NULL) { PyObject_ClearWeakRefs((PyObject *)self); @@ -482,12 +448,12 @@ array_dealloc(PyArrayObject *self) { char const * msg = "WRITEBACKIFCOPY detected in array_dealloc. " " Required call to PyArray_ResolveWritebackIfCopy or " - "PyArray_DiscardWritebackIfCopy is missing. This could also " - "be caused by using a nditer without a context manager"; - Py_INCREF(self); /* hold on to self in next call since if - * refcount == 0 it will recurse back into - *array_dealloc - */ + "PyArray_DiscardWritebackIfCopy is missing."; + /* + * prevent reaching 0 twice and thus recursing into dealloc. + * Increasing sys.gettotalrefcount, but path should not be taken. + */ + Py_INCREF(self); WARN_IN_DEALLOC(PyExc_RuntimeWarning, msg); retval = PyArray_ResolveWritebackIfCopy(self); if (retval < 0) @@ -501,10 +467,11 @@ array_dealloc(PyArrayObject *self) char const * msg = "UPDATEIFCOPY detected in array_dealloc. " " Required call to PyArray_ResolveWritebackIfCopy or " "PyArray_DiscardWritebackIfCopy is missing"; - Py_INCREF(self); /* hold on to self in next call since if - * refcount == 0 it will recurse back into - *array_dealloc - */ + /* + * prevent reaching 0 twice and thus recursing into dealloc. + * Increasing sys.gettotalrefcount, but path should not be taken. + */ + Py_INCREF(self); /* 2017-Nov-10 1.14 */ WARN_IN_DEALLOC(PyExc_DeprecationWarning, msg); retval = PyArray_ResolveWritebackIfCopy(self); @@ -524,14 +491,31 @@ array_dealloc(PyArrayObject *self) if ((fa->flags & NPY_ARRAY_OWNDATA) && fa->data) { /* Free internal references if an Object array */ if (PyDataType_FLAGCHK(fa->descr, NPY_ITEM_REFCOUNT)) { - Py_INCREF(self); /*hold on to self */ PyArray_XDECREF(self); + } + if (fa->mem_handler == NULL) { + char *env = getenv("NUMPY_WARN_IF_NO_MEM_POLICY"); + if ((env != NULL) && (strncmp(env, "1", 1) == 0)) { + char const * msg = "Trying to dealloc data, but a memory policy " + "is not set. If you take ownership of the data, you must " + "set a base owning the data (e.g. a PyCapsule)."; + WARN_IN_DEALLOC(PyExc_RuntimeWarning, msg); + } + // Guess at malloc/free ??? + free(fa->data); + } + else { /* - * Don't need to DECREF -- because we are deleting - * self already... + * In theory `PyArray_NBYTES_ALLOCATED`, but differs somewhere? + * So instead just use the knowledge that 0 is impossible. */ + size_t nbytes = PyArray_NBYTES(self); + if (nbytes == 0) { + nbytes = 1; + } + PyDataMem_UserFREE(fa->data, nbytes, fa->mem_handler); + Py_DECREF(fa->mem_handler); } - npy_free_cache(fa->data, PyArray_NBYTES(self)); } /* must match allocation in PyArray_NewFromDescr */ @@ -561,7 +545,7 @@ PyArray_DebugPrint(PyArrayObject *obj) printf(" ndim : %d\n", fobj->nd); printf(" shape :"); for (i = 0; i < fobj->nd; ++i) { - printf(" %d", (int)fobj->dimensions[i]); + printf(" %" NPY_INTP_FMT, fobj->dimensions[i]); } printf("\n"); @@ -571,7 +555,7 @@ PyArray_DebugPrint(PyArrayObject *obj) printf(" data : %p\n", fobj->data); printf(" strides:"); for (i = 0; i < fobj->nd; ++i) { - printf(" %d", (int)fobj->strides[i]); + printf(" %" NPY_INTP_FMT, fobj->strides[i]); } printf("\n"); @@ -611,14 +595,14 @@ PyArray_DebugPrint(PyArrayObject *obj) * TO BE REMOVED - NOT USED INTERNALLY. */ NPY_NO_EXPORT void -PyArray_SetDatetimeParseFunction(PyObject *op) +PyArray_SetDatetimeParseFunction(PyObject *NPY_UNUSED(op)) { } /*NUMPY_API */ NPY_NO_EXPORT int -PyArray_CompareUCS4(npy_ucs4 *s1, npy_ucs4 *s2, size_t len) +PyArray_CompareUCS4(npy_ucs4 const *s1, npy_ucs4 const *s2, size_t len) { npy_ucs4 c1, c2; while(len-- > 0) { @@ -634,7 +618,7 @@ PyArray_CompareUCS4(npy_ucs4 *s1, npy_ucs4 *s2, size_t len) /*NUMPY_API */ NPY_NO_EXPORT int -PyArray_CompareString(char *s1, char *s2, size_t len) +PyArray_CompareString(const char *s1, const char *s2, size_t len) { const unsigned char *c1 = (unsigned char *)s1; const unsigned char *c2 = (unsigned char *)s2; @@ -656,15 +640,11 @@ NPY_NO_EXPORT int array_might_be_written(PyArrayObject *obj) { const char *msg = - "Numpy has detected that you (may be) writing to an array returned\n" - "by numpy.diagonal or by selecting multiple fields in a structured\n" - "array. This code will likely break in a future numpy release --\n" - "see numpy.diagonal or arrays.indexing reference docs for details.\n" - "The quick fix is to make an explicit copy (e.g., do\n" - "arr.diagonal().copy() or arr[['f0','f1']].copy())."; + "Numpy has detected that you (may be) writing to an array with\n" + "overlapping memory from np.broadcast_arrays. If this is intentional\n" + "set the WRITEABLE flag True or make a copy immediately before writing."; if (PyArray_FLAGS(obj) & NPY_ARRAY_WARN_ON_WRITE) { - /* 2012-07-17, 1.7 */ - if (DEPRECATE_FUTUREWARNING(msg) < 0) { + if (DEPRECATE(msg) < 0) { return -1; } /* Only warn once per array */ @@ -711,35 +691,40 @@ PyArray_FailUnlessWriteable(PyArrayObject *obj, const char *name) If they are NULL terminated, then stop comparison. */ static int -_myunincmp(npy_ucs4 *s1, npy_ucs4 *s2, int len1, int len2) +_myunincmp(npy_ucs4 const *s1, npy_ucs4 const *s2, int len1, int len2) { - npy_ucs4 *sptr; - npy_ucs4 *s1t=s1, *s2t=s2; + npy_ucs4 const *sptr; + npy_ucs4 *s1t = NULL; + npy_ucs4 *s2t = NULL; int val; npy_intp size; int diff; + /* Replace `s1` and `s2` with aligned copies if needed */ if ((npy_intp)s1 % sizeof(npy_ucs4) != 0) { size = len1*sizeof(npy_ucs4); s1t = malloc(size); memcpy(s1t, s1, size); + s1 = s1t; } if ((npy_intp)s2 % sizeof(npy_ucs4) != 0) { size = len2*sizeof(npy_ucs4); s2t = malloc(size); memcpy(s2t, s2, size); + s2 = s1t; } - val = PyArray_CompareUCS4(s1t, s2t, PyArray_MIN(len1,len2)); + + val = PyArray_CompareUCS4(s1, s2, PyArray_MIN(len1,len2)); if ((val != 0) || (len1 == len2)) { goto finish; } if (len2 > len1) { - sptr = s2t+len1; + sptr = s2+len1; val = -1; diff = len2-len1; } else { - sptr = s1t+len2; + sptr = s1+len2; val = 1; diff=len1-len2; } @@ -752,10 +737,11 @@ _myunincmp(npy_ucs4 *s1, npy_ucs4 *s2, int len1, int len2) val = 0; finish: - if (s1t != s1) { + /* Cleanup the aligned copies */ + if (s1t) { free(s1t); } - if (s2t != s2) { + if (s2t) { free(s2t); } return val; @@ -771,9 +757,9 @@ _myunincmp(npy_ucs4 *s1, npy_ucs4 *s2, int len1, int len2) * If they are NULL terminated, then stop comparison. */ static int -_mystrncmp(char *s1, char *s2, int len1, int len2) +_mystrncmp(char const *s1, char const *s2, int len1, int len2) { - char *sptr; + char const *sptr; int val; int diff; @@ -835,7 +821,7 @@ static void _unistripw(npy_ucs4 *s, int n) static char * -_char_copy_n_strip(char *original, char *temp, int nc) +_char_copy_n_strip(char const *original, char *temp, int nc) { if (nc > SMALL_STRING) { temp = malloc(nc); @@ -858,7 +844,7 @@ _char_release(char *ptr, int nc) } static char * -_uni_copy_n_strip(char *original, char *temp, int nc) +_uni_copy_n_strip(char const *original, char *temp, int nc) { if (nc*sizeof(npy_ucs4) > SMALL_STRING) { temp = malloc(nc*sizeof(npy_ucs4)); @@ -894,7 +880,7 @@ _uni_release(char *ptr, int nc) relfunc(aptr, N1); \ return -1; \ } \ - val = compfunc(aptr, bptr, N1, N2); \ + val = compfunc(aptr, bptr, N1, N2); \ *dptr = (val CMP 0); \ PyArray_ITER_NEXT(iself); \ PyArray_ITER_NEXT(iother); \ @@ -906,7 +892,7 @@ _uni_release(char *ptr, int nc) #define _reg_loop(CMP) { \ while(size--) { \ - val = compfunc((void *)iself->dataptr, \ + val = compfunc((void *)iself->dataptr, \ (void *)iother->dataptr, \ N1, N2); \ *dptr = (val CMP 0); \ @@ -927,7 +913,7 @@ _compare_strings(PyArrayObject *result, PyArrayMultiIterObject *multi, int N1, N2; int (*compfunc)(void *, void *, int, int); void (*relfunc)(char *, int); - char* (*stripfunc)(char *, char *, int); + char* (*stripfunc)(char const *, char *, int); compfunc = func; dptr = (npy_bool *)PyArray_DATA(result); @@ -1006,69 +992,33 @@ _strings_richcompare(PyArrayObject *self, PyArrayObject *other, int cmp_op, { PyArrayObject *result; PyArrayMultiIterObject *mit; - int val, cast = 0; + int val; - /* Cast arrays to a common type */ - if (PyArray_TYPE(self) != PyArray_DESCR(other)->type_num) { -#if defined(NPY_PY3K) + if (PyArray_TYPE(self) != PyArray_TYPE(other)) { /* * Comparison between Bytes and Unicode is not defined in Py3K; * we follow. */ Py_INCREF(Py_NotImplemented); return Py_NotImplemented; -#else - cast = 1; -#endif /* define(NPY_PY3K) */ - } - if (cast || (PyArray_ISNOTSWAPPED(self) != PyArray_ISNOTSWAPPED(other))) { - PyObject *new; - if (PyArray_TYPE(self) == NPY_STRING && - PyArray_DESCR(other)->type_num == NPY_UNICODE) { - PyArray_Descr* unicode = PyArray_DescrNew(PyArray_DESCR(other)); - unicode->elsize = PyArray_DESCR(self)->elsize << 2; - new = PyArray_FromAny((PyObject *)self, unicode, - 0, 0, 0, NULL); - if (new == NULL) { - return NULL; - } - Py_INCREF(other); - self = (PyArrayObject *)new; - } - else if ((PyArray_TYPE(self) == NPY_UNICODE) && - ((PyArray_DESCR(other)->type_num == NPY_STRING) || - (PyArray_ISNOTSWAPPED(self) != PyArray_ISNOTSWAPPED(other)))) { - PyArray_Descr* unicode = PyArray_DescrNew(PyArray_DESCR(self)); - - if (PyArray_DESCR(other)->type_num == NPY_STRING) { - unicode->elsize = PyArray_DESCR(other)->elsize << 2; - } - else { - unicode->elsize = PyArray_DESCR(other)->elsize; - } - new = PyArray_FromAny((PyObject *)other, unicode, - 0, 0, 0, NULL); - if (new == NULL) { - return NULL; - } - Py_INCREF(self); - other = (PyArrayObject *)new; - } - else { - PyErr_SetString(PyExc_TypeError, - "invalid string data-types " - "in comparison"); + } + if (PyArray_ISNOTSWAPPED(self) != PyArray_ISNOTSWAPPED(other)) { + /* Cast `other` to the same byte order as `self` (both unicode here) */ + PyArray_Descr* unicode = PyArray_DescrNew(PyArray_DESCR(self)); + unicode->elsize = PyArray_DESCR(other)->elsize; + PyObject *new = PyArray_FromAny((PyObject *)other, + unicode, 0, 0, 0, NULL); + if (new == NULL) { return NULL; } + other = (PyArrayObject *)new; } else { - Py_INCREF(self); Py_INCREF(other); } /* Broad-cast the arrays to a common shape */ mit = (PyArrayMultiIterObject *)PyArray_MultiIterNew(2, self, other); - Py_DECREF(self); Py_DECREF(other); if (mit == NULL) { return NULL; @@ -1129,7 +1079,7 @@ _void_compare(PyArrayObject *self, PyArrayObject *other, int cmp_op) op = (cmp_op == Py_EQ ? n_ops.logical_and : n_ops.logical_or); while (PyDict_Next(PyArray_DESCR(self)->fields, &pos, &key, &value)) { - if NPY_TITLE_KEY(key, value) { + if (NPY_TITLE_KEY(key, value)) { continue; } a = array_subscript_asarray(self, key); @@ -1166,8 +1116,10 @@ _void_compare(PyArrayObject *self, PyArrayObject *other, int cmp_op) newdims.ptr = dimensions; newdims.len = result_ndim+1; - memcpy(dimensions, PyArray_DIMS((PyArrayObject *)temp), - sizeof(npy_intp)*result_ndim); + if (result_ndim) { + memcpy(dimensions, PyArray_DIMS((PyArrayObject *)temp), + sizeof(npy_intp)*result_ndim); + } dimensions[result_ndim] = -1; temp2 = PyArray_Newshape((PyArrayObject *)temp, &newdims, NPY_ANYORDER); @@ -1206,51 +1158,34 @@ _void_compare(PyArrayObject *self, PyArrayObject *other, int cmp_op) } } if (res == NULL && !PyErr_Occurred()) { - PyErr_SetString(PyExc_ValueError, "No fields found."); + /* these dtypes had no fields. Use a MultiIter to broadcast them + * to an output array, and fill with True (for EQ)*/ + PyArrayMultiIterObject *mit = (PyArrayMultiIterObject *) + PyArray_MultiIterNew(2, self, other); + if (mit == NULL) { + return NULL; + } + + res = PyArray_NewFromDescr(&PyArray_Type, + PyArray_DescrFromType(NPY_BOOL), + mit->nd, mit->dimensions, + NULL, NULL, 0, NULL); + Py_DECREF(mit); + if (res) { + PyArray_FILLWBYTE((PyArrayObject *)res, + cmp_op == Py_EQ ? 1 : 0); + } } return res; } else { - /* - * compare as a string. Assumes self and - * other have same descr->type - */ + /* compare as a string. Assumes self and other have same descr->type */ return _strings_richcompare(self, other, cmp_op, 0); } } -/* This is a copy of _PyErr_ChainExceptions, with: - * - a minimal implementation for python 2 - * - __cause__ used instead of __context__ - */ -NPY_NO_EXPORT void -PyArray_ChainExceptionsCause(PyObject *exc, PyObject *val, PyObject *tb) -{ - if (exc == NULL) - return; - - if (PyErr_Occurred()) { - /* only py3 supports this anyway */ - #ifdef NPY_PY3K - PyObject *exc2, *val2, *tb2; - PyErr_Fetch(&exc2, &val2, &tb2); - PyErr_NormalizeException(&exc, &val, &tb); - if (tb != NULL) { - PyException_SetTraceback(val, tb); - Py_DECREF(tb); - } - Py_DECREF(exc); - PyErr_NormalizeException(&exc2, &val2, &tb2); - PyException_SetCause(val2, val); - PyErr_Restore(exc2, val2, tb2); - #endif - } - else { - PyErr_Restore(exc, val, tb); - } -} - -/* Silence the current error and emit a deprecation warning instead. +/* + * Silence the current error and emit a deprecation warning instead. * * If warnings are raised as errors, this sets the warning __cause__ to the * silenced error. @@ -1260,7 +1195,7 @@ DEPRECATE_silence_error(const char *msg) { PyObject *exc, *val, *tb; PyErr_Fetch(&exc, &val, &tb); if (DEPRECATE(msg) < 0) { - PyArray_ChainExceptionsCause(exc, val, tb); + npy_PyErr_ChainExceptionsCause(exc, val, tb); return -1; } Py_XDECREF(exc); @@ -1269,6 +1204,109 @@ DEPRECATE_silence_error(const char *msg) { return 0; } +/* + * Comparisons can fail, but we do not always want to pass on the exception + * (see comment in array_richcompare below), but rather return NotImplemented. + * Here, an exception should be set on entrance. + * Returns either NotImplemented with the exception cleared, or NULL + * with the exception set. + * Raises deprecation warnings for cases where behaviour is meant to change + * (2015-05-14, 1.10) + */ + +NPY_NO_EXPORT PyObject * +_failed_comparison_workaround(PyArrayObject *self, PyObject *other, int cmp_op) +{ + PyObject *exc, *val, *tb; + PyArrayObject *array_other; + int other_is_flexible, ndim_other; + int self_is_flexible = PyTypeNum_ISFLEXIBLE(PyArray_DESCR(self)->type_num); + + PyErr_Fetch(&exc, &val, &tb); + /* + * Determine whether other has a flexible dtype; here, inconvertible + * is counted as inflexible. (This repeats work done in the ufunc, + * but OK to waste some time in an unlikely path.) + */ + array_other = (PyArrayObject *)PyArray_FROM_O(other); + if (array_other) { + other_is_flexible = PyTypeNum_ISFLEXIBLE( + PyArray_DESCR(array_other)->type_num); + ndim_other = PyArray_NDIM(array_other); + Py_DECREF(array_other); + } + else { + PyErr_Clear(); /* we restore the original error if needed */ + other_is_flexible = 0; + ndim_other = 0; + } + if (cmp_op == Py_EQ || cmp_op == Py_NE) { + /* + * note: for == and !=, a structured dtype self cannot get here, + * but a string can. Other can be string or structured. + */ + if (other_is_flexible || self_is_flexible) { + /* + * For scalars, returning NotImplemented is correct. + * For arrays, we emit a future deprecation warning. + * When this warning is removed, a correctly shaped + * array of bool should be returned. + */ + if (ndim_other != 0 || PyArray_NDIM(self) != 0) { + /* 2015-05-14, 1.10 */ + if (DEPRECATE_FUTUREWARNING( + "elementwise comparison failed; returning scalar " + "instead, but in the future will perform " + "elementwise comparison") < 0) { + goto fail; + } + } + } + else { + /* + * If neither self nor other had a flexible dtype, the error cannot + * have been caused by a lack of implementation in the ufunc. + * + * 2015-05-14, 1.10 + */ + if (DEPRECATE( + "elementwise comparison failed; " + "this will raise an error in the future.") < 0) { + goto fail; + } + } + Py_XDECREF(exc); + Py_XDECREF(val); + Py_XDECREF(tb); + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + else if (other_is_flexible || self_is_flexible) { + /* + * For LE, LT, GT, GE and a flexible self or other, we return + * NotImplemented, which is the correct answer since the ufuncs do + * not in fact implement loops for those. This will get us the + * desired TypeError. + */ + Py_XDECREF(exc); + Py_XDECREF(val); + Py_XDECREF(tb); + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + else { + /* LE, LT, GT, or GE with non-flexible other; just pass on error */ + goto fail; + } + +fail: + /* + * Reraise the original exception, possibly chaining with a new one. + */ + npy_PyErr_ChainExceptionsCause(exc, val, tb); + return NULL; +} + NPY_NO_EXPORT PyObject * array_richcompare(PyArrayObject *self, PyObject *other, int cmp_op) { @@ -1309,11 +1347,13 @@ array_richcompare(PyArrayObject *self, PyObject *other, int cmp_op) switch (cmp_op) { case Py_LT: RICHCMP_GIVE_UP_IF_NEEDED(obj_self, other); - result = PyArray_GenericBinaryFunction(self, other, n_ops.less); + result = PyArray_GenericBinaryFunction( + (PyObject *)self, other, n_ops.less); break; case Py_LE: RICHCMP_GIVE_UP_IF_NEEDED(obj_self, other); - result = PyArray_GenericBinaryFunction(self, other, n_ops.less_equal); + result = PyArray_GenericBinaryFunction( + (PyObject *)self, other, n_ops.less_equal); break; case Py_EQ: RICHCMP_GIVE_UP_IF_NEEDED(obj_self, other); @@ -1341,9 +1381,13 @@ array_richcompare(PyArrayObject *self, PyObject *other, int cmp_op) return Py_NotImplemented; } - _res = PyArray_CanCastTypeTo(PyArray_DESCR(self), - PyArray_DESCR(array_other), - NPY_EQUIV_CASTING); + _res = PyArray_CheckCastSafety( + NPY_EQUIV_CASTING, + PyArray_DESCR(self), PyArray_DESCR(array_other), NULL); + if (_res < 0) { + PyErr_Clear(); + _res = 0; + } if (_res == 0) { /* 2015-05-07, 1.10 */ Py_DECREF(array_other); @@ -1363,29 +1407,8 @@ array_richcompare(PyArrayObject *self, PyObject *other, int cmp_op) return result; } - result = PyArray_GenericBinaryFunction(self, - (PyObject *)other, - n_ops.equal); - /* - * If the comparison results in NULL, then the - * two array objects can not be compared together; - * indicate that - */ - if (result == NULL) { - /* - * Comparisons should raise errors when element-wise comparison - * is not possible. - */ - /* 2015-05-14, 1.10 */ - if (DEPRECATE_silence_error( - "elementwise == comparison failed; " - "this will raise an error in the future.") < 0) { - return NULL; - } - - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } + result = PyArray_GenericBinaryFunction( + (PyObject *)self, (PyObject *)other, n_ops.equal); break; case Py_NE: RICHCMP_GIVE_UP_IF_NEEDED(obj_self, other); @@ -1413,9 +1436,13 @@ array_richcompare(PyArrayObject *self, PyObject *other, int cmp_op) return Py_NotImplemented; } - _res = PyArray_CanCastTypeTo(PyArray_DESCR(self), - PyArray_DESCR(array_other), - NPY_EQUIV_CASTING); + _res = PyArray_CheckCastSafety( + NPY_EQUIV_CASTING, + PyArray_DESCR(self), PyArray_DESCR(array_other), NULL); + if (_res < 0) { + PyErr_Clear(); + _res = 0; + } if (_res == 0) { /* 2015-05-07, 1.10 */ Py_DECREF(array_other); @@ -1435,37 +1462,50 @@ array_richcompare(PyArrayObject *self, PyObject *other, int cmp_op) return result; } - result = PyArray_GenericBinaryFunction(self, (PyObject *)other, - n_ops.not_equal); - if (result == NULL) { - /* - * Comparisons should raise errors when element-wise comparison - * is not possible. - */ - /* 2015-05-14, 1.10 */ - if (DEPRECATE_silence_error( - "elementwise != comparison failed; " - "this will raise an error in the future.") < 0) { - return NULL; - } - - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } + result = PyArray_GenericBinaryFunction( + (PyObject *)self, (PyObject *)other, n_ops.not_equal); break; case Py_GT: RICHCMP_GIVE_UP_IF_NEEDED(obj_self, other); - result = PyArray_GenericBinaryFunction(self, other, - n_ops.greater); + result = PyArray_GenericBinaryFunction( + (PyObject *)self, other, n_ops.greater); break; case Py_GE: RICHCMP_GIVE_UP_IF_NEEDED(obj_self, other); - result = PyArray_GenericBinaryFunction(self, other, - n_ops.greater_equal); + result = PyArray_GenericBinaryFunction( + (PyObject *)self, other, n_ops.greater_equal); break; default: - result = Py_NotImplemented; - Py_INCREF(result); + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + if (result == NULL) { + /* + * 2015-05-14, 1.10; updated 2018-06-18, 1.16. + * + * Comparisons can raise errors when element-wise comparison is not + * possible. Some of these, though, should not be passed on. + * In particular, the ufuncs do not have loops for flexible dtype, + * so those should be treated separately. Furthermore, for EQ and NE, + * we should never fail. + * + * Our ideal behaviour would be: + * + * 1. For EQ and NE: + * - If self and other are scalars, return NotImplemented, + * so that python can assign True of False as appropriate. + * - If either is an array, return an array of False or True. + * + * 2. For LT, LE, GE, GT: + * - If self or other was flexible, return NotImplemented + * (as is in fact the case), so python can raise a TypeError. + * - If other is not convertible to an array, pass on the error + * (MHvK, 2018-06-18: not sure about this, but it's what we have). + * + * However, for backwards compatibility, we cannot yet return arrays, + * so we raise warnings instead. + */ + result = _failed_comparison_workaround(self, other, cmp_op); } return result; } @@ -1518,7 +1558,7 @@ PyArray_ElementStrides(PyObject *obj) /*NUMPY_API*/ NPY_NO_EXPORT npy_bool PyArray_CheckStrides(int elsize, int nd, npy_intp numbytes, npy_intp offset, - npy_intp *dims, npy_intp *newstrides) + npy_intp const *dims, npy_intp const *newstrides) { npy_intp begin, end; npy_intp lower_offset; @@ -1549,7 +1589,7 @@ array_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds) PyArray_Descr *descr = NULL; int itemsize; PyArray_Dims dims = {NULL, 0}; - PyArray_Dims strides = {NULL, 0}; + PyArray_Dims strides = {NULL, -1}; PyArray_Chunk buffer; npy_longlong offset = 0; NPY_ORDER order = NPY_CORDER; @@ -1570,7 +1610,7 @@ array_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds) PyArray_BufferConverter, &buffer, &offset, - &PyArray_IntpConverter, + &PyArray_OptionalIntpConverter, &strides, &PyArray_OrderConverter, &order)) { @@ -1585,7 +1625,7 @@ array_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds) itemsize = descr->elsize; - if (strides.ptr != NULL) { + if (strides.len != -1) { npy_intp nb, off; if (strides.len != dims.len) { PyErr_SetString(PyExc_ValueError, @@ -1687,89 +1727,25 @@ array_iter(PyArrayObject *arr) return PySeqIter_New((PyObject *)arr); } -static PyObject * -array_alloc(PyTypeObject *type, Py_ssize_t NPY_UNUSED(nitems)) -{ - /* nitems will always be 0 */ - PyObject *obj = PyObject_Malloc(type->tp_basicsize); - PyObject_Init(obj, type); - return obj; -} - -static void -array_free(PyObject * v) -{ - /* avoid same deallocator as PyBaseObject, see gentype_free */ - PyObject_Free(v); -} - NPY_NO_EXPORT PyTypeObject PyArray_Type = { -#if defined(NPY_PY3K) PyVarObject_HEAD_INIT(NULL, 0) -#else - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ -#endif - "numpy.ndarray", /* tp_name */ - NPY_SIZEOF_PYARRAYOBJECT, /* tp_basicsize */ - 0, /* tp_itemsize */ + .tp_name = "numpy.ndarray", + .tp_basicsize = sizeof(PyArrayObject_fields), /* methods */ - (destructor)array_dealloc, /* tp_dealloc */ - (printfunc)NULL, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ -#if defined(NPY_PY3K) - 0, /* tp_reserved */ -#else - 0, /* tp_compare */ -#endif - (reprfunc)array_repr, /* tp_repr */ - &array_as_number, /* tp_as_number */ - &array_as_sequence, /* tp_as_sequence */ - &array_as_mapping, /* tp_as_mapping */ - /* - * The tp_hash slot will be set PyObject_HashNotImplemented when the - * module is loaded. - */ - (hashfunc)0, /* tp_hash */ - (ternaryfunc)0, /* tp_call */ - (reprfunc)array_str, /* tp_str */ - (getattrofunc)0, /* tp_getattro */ - (setattrofunc)0, /* tp_setattro */ - &array_as_buffer, /* tp_as_buffer */ - (Py_TPFLAGS_DEFAULT -#if !defined(NPY_PY3K) - | Py_TPFLAGS_CHECKTYPES - | Py_TPFLAGS_HAVE_NEWBUFFER -#endif - | Py_TPFLAGS_BASETYPE), /* tp_flags */ - 0, /* tp_doc */ - - (traverseproc)0, /* tp_traverse */ - (inquiry)0, /* tp_clear */ - (richcmpfunc)array_richcompare, /* tp_richcompare */ - offsetof(PyArrayObject_fields, weakreflist), /* tp_weaklistoffset */ - (getiterfunc)array_iter, /* tp_iter */ - (iternextfunc)0, /* tp_iternext */ - array_methods, /* tp_methods */ - 0, /* tp_members */ - array_getsetlist, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)0, /* tp_init */ - (allocfunc)array_alloc, /* tp_alloc */ - (newfunc)array_new, /* tp_new */ - (freefunc)array_free, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - 0, /* tp_del */ - 0, /* tp_version_tag */ + .tp_dealloc = (destructor)array_dealloc, + .tp_repr = (reprfunc)array_repr, + .tp_as_number = &array_as_number, + .tp_as_sequence = &array_as_sequence, + .tp_as_mapping = &array_as_mapping, + .tp_str = (reprfunc)array_str, + .tp_as_buffer = &array_as_buffer, + .tp_flags =(Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE), + + .tp_richcompare = (richcmpfunc)array_richcompare, + .tp_weaklistoffset = offsetof(PyArrayObject_fields, weakreflist), + .tp_iter = (getiterfunc)array_iter, + .tp_methods = array_methods, + .tp_getset = array_getsetlist, + .tp_new = (newfunc)array_new, }; diff --git a/numpy/core/src/multiarray/arrayobject.h b/numpy/core/src/multiarray/arrayobject.h index 9b74944ffe9f..fb9b0bd8120f 100644 --- a/numpy/core/src/multiarray/arrayobject.h +++ b/numpy/core/src/multiarray/arrayobject.h @@ -1,10 +1,10 @@ -#ifndef _NPY_INTERNAL_ARRAYOBJECT_H_ -#define _NPY_INTERNAL_ARRAYOBJECT_H_ - #ifndef _MULTIARRAYMODULE #error You should not include this #endif +#ifndef NUMPY_CORE_SRC_MULTIARRAY_ARRAYOBJECT_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_ARRAYOBJECT_H_ + NPY_NO_EXPORT PyObject * _strings_richcompare(PyArrayObject *self, PyArrayObject *other, int cmp_op, int rstrip); @@ -26,4 +26,4 @@ array_might_be_written(PyArrayObject *obj); */ static const int NPY_ARRAY_WARN_ON_WRITE = (1 << 31); -#endif +#endif /* NUMPY_CORE_SRC_MULTIARRAY_ARRAYOBJECT_H_ */ diff --git a/numpy/core/src/multiarray/arraytypes.c.src b/numpy/core/src/multiarray/arraytypes.c.src index b4158ec8edf8..71808cc48aa3 100644 --- a/numpy/core/src/multiarray/arraytypes.c.src +++ b/numpy/core/src/multiarray/arraytypes.c.src @@ -1,8 +1,9 @@ /* -*- c -*- */ #define PY_SSIZE_T_CLEAN -#include "Python.h" -#include "structmember.h" - +#include <Python.h> +#include <structmember.h> +#include <limits.h> +#include <assert.h> #define NPY_NO_DEPRECATED_API NPY_API_VERSION #define _MULTIARRAYMODULE @@ -19,30 +20,60 @@ #include "npy_sort.h" #include "common.h" #include "ctors.h" +#include "dtypemeta.h" #include "lowlevel_strided_loops.h" #include "usertypes.h" #include "_datetime.h" #include "arrayobject.h" #include "alloc.h" #include "typeinfo.h" +#if defined(__ARM_NEON__) || defined (__ARM_NEON) +#include <arm_neon.h> +#endif #ifdef NPY_HAVE_SSE2_INTRINSICS #include <emmintrin.h> #endif +#include "npy_longdouble.h" #include "numpyos.h" #include <string.h> #include "cblasfuncs.h" #include "npy_cblas.h" -#include <limits.h> -#include <assert.h> +#include "npy_buffer.h" + + +/* + * Define a stack allocated dummy array with only the minimum information set: + * 1. The descr, the main field interesting here. + * 2. The flags, which are needed for alignment;. + * 3. The type is set to NULL and the base is the original array, if this + * is used within a subarray getitem to create a new view, the base + * must be walked until the type is not NULL. + * + * The following should create errors in debug mode (if deallocated + * incorrectly), since base would be incorrectly decref'd as well. + * This is especially important for nonzero and copyswap, which may run with + * the GIL released. + */ +static NPY_INLINE PyArrayObject_fields +get_dummy_stack_array(PyArrayObject *orig) +{ + PyArrayObject_fields new_fields; + new_fields.flags = PyArray_FLAGS(orig); + /* Set to NULL so the dummy object can be distinguished from the real one */ + Py_SET_TYPE(&new_fields, NULL); + new_fields.base = (PyObject *)orig; + return new_fields; +} + /* check for sequences, but ignore the types numpy considers scalars */ static NPY_INLINE npy_bool PySequence_NoString_Check(PyObject *op) { return PySequence_Check(op) && - !PyString_Check(op) && + !PyBytes_Check(op) && !PyUnicode_Check(op) && !PyArray_IsZeroDim(op); } @@ -150,32 +181,6 @@ MyPyLong_AsUnsigned@Type@ (PyObject *obj) /**end repeat**/ -static npy_longlong -npy_strtoll(const char *str, char **endptr, int base) -{ -#if defined HAVE_STRTOLL - return strtoll(str, endptr, base); -#elif defined _MSC_VER - return _strtoi64(str, endptr, base); -#else - /* ok on 64 bit posix */ - return PyOS_strtol(str, endptr, base); -#endif -} - -static npy_ulonglong -npy_strtoull(const char *str, char **endptr, int base) -{ -#if defined HAVE_STRTOULL - return strtoull(str, endptr, base); -#elif defined _MSC_VER - return _strtoui64(str, endptr, base); -#else - /* ok on 64 bit posix */ - return PyOS_strtoul(str, endptr, base); -#endif -} - /* ***************************************************************************** ** GETITEM AND SETITEM ** @@ -196,7 +201,7 @@ npy_strtoull(const char *str, char **endptr, int base) * * #TYPE = BOOL, BYTE, UBYTE, SHORT, USHORT, INT, LONG, UINT, ULONG, * LONGLONG, ULONGLONG, HALF, FLOAT, DOUBLE# - * #func1 = PyBool_FromLong, PyInt_FromLong*6, PyLong_FromUnsignedLong*2, + * #func1 = PyBool_FromLong, PyLong_FromLong*6, PyLong_FromUnsignedLong*2, * PyLong_FromLongLong, PyLong_FromUnsignedLongLong, * MyPyFloat_FromHalf, PyFloat_FromDouble*2# * #func2 = PyObject_IsTrue, MyPyLong_AsLong*6, MyPyLong_AsUnsignedLong*2, @@ -235,7 +240,7 @@ static int @type@ temp; /* ensures alignment */ if (PyArray_IsScalar(op, @kind@)) { - temp = ((Py@kind@ScalarObject *)op)->obval; + temp = PyArrayScalar_VAL(op, @kind@); } else { temp = (@type@)@func2@(op); @@ -246,9 +251,7 @@ static int if (PySequence_NoString_Check(op)) { PyErr_SetString(PyExc_ValueError, "setting an array element with a sequence."); - Py_DECREF(type); - Py_XDECREF(value); - Py_XDECREF(traceback); + npy_PyErr_ChainExceptionsCause(type, value, traceback); } else { PyErr_Restore(type, value, traceback); @@ -318,16 +321,49 @@ static int } if (PyArray_IsScalar(op, @kind@)){ - temp = ((Py@kind@ScalarObject *)op)->obval; + temp = PyArrayScalar_VAL(op, @kind@); } else { if (op == Py_None) { oop.real = NPY_NAN; oop.imag = NPY_NAN; } + else if (PyBytes_Check(op) || PyUnicode_Check(op)) { + /* + * Unlike most numeric conversion functions PyComplex_AsCComplex + * does not handle strings, so we have to use its constructor. + */ + PyObject *pycomplex, *args; + if (PyBytes_Check(op)) { + /* The complex constructor expects unicode */ + PyObject *unicode; + unicode = PyUnicode_FromEncodedObject(op, NULL, NULL); + if (unicode == NULL) { + return -1; + } + args = PyTuple_Pack(1, unicode); + Py_DECREF(unicode); + } + else { + args = PyTuple_Pack(1, op); + } + if (args == NULL) { + return -1; + } + pycomplex = PyComplex_Type.tp_new(&PyComplex_Type, args, NULL); + Py_DECREF(args); + if (pycomplex == NULL) { + return -1; + } + oop = PyComplex_AsCComplex(pycomplex); + Py_DECREF(pycomplex); + if (error_converting(oop.real)) { + return -1; + } + } else { - oop = PyComplex_AsCComplex (op); - if (PyErr_Occurred()) { + oop = PyComplex_AsCComplex(op); + if (error_converting(oop.real)) { return -1; } } @@ -354,6 +390,13 @@ string_to_long_double(PyObject*op) npy_longdouble temp; PyObject* b; + /* Convert python long objects to a longdouble, without precision or range + * loss via a double. + */ + if ((PyLong_Check(op) && !PyBool_Check(op))) { + return npy_longdouble_from_PyLong(op); + } + if (PyUnicode_Check(op)) { b = PyUnicode_AsUTF8String(op); if (!b) { @@ -426,7 +469,7 @@ LONGDOUBLE_setitem(PyObject *op, void *ov, void *vap) } if (PyArray_IsScalar(op, LongDouble)) { - temp = ((PyLongDoubleScalarObject *)op)->obval; + temp = PyArrayScalar_VAL(op, LongDouble); } else { /* In case something funny happened in PyArray_IsScalar */ @@ -470,12 +513,6 @@ static int UNICODE_setitem(PyObject *op, void *ov, void *vap) { PyArrayObject *ap = vap; - PyObject *temp; - Py_UNICODE *ptr; - int datalen; -#ifndef Py_UNICODE_WIDE - char *buffer; -#endif if (PyArray_IsZeroDim(op)) { return convert_to_scalar_and_retry(op, ov, vap, UNICODE_setitem); @@ -486,7 +523,8 @@ UNICODE_setitem(PyObject *op, void *ov, void *vap) "setting an array element with a sequence"); return -1; } -#if defined(NPY_PY3K) + + PyObject *temp; if (PyBytes_Check(op)) { /* Try to decode from ASCII */ temp = PyUnicode_FromEncodedObject(op, "ASCII", "strict"); @@ -495,23 +533,29 @@ UNICODE_setitem(PyObject *op, void *ov, void *vap) } } else if ((temp=PyObject_Str(op)) == NULL) { -#else - if ((temp=PyObject_Unicode(op)) == NULL) { -#endif return -1; } - ptr = PyUnicode_AS_UNICODE(temp); - if ((ptr == NULL) || (PyErr_Occurred())) { + + /* truncate if needed */ + Py_ssize_t max_len = PyArray_DESCR(ap)->elsize >> 2; + Py_ssize_t actual_len = PyUnicode_GetLength(temp); + if (actual_len < 0) { Py_DECREF(temp); return -1; } - datalen = PyUnicode_GET_DATA_SIZE(temp); + if (actual_len > max_len) { + Py_SETREF(temp, PyUnicode_Substring(temp, 0, max_len)); + if (temp == NULL) { + return -1; + } + actual_len = max_len; + } -#ifdef Py_UNICODE_WIDE - memcpy(ov, ptr, PyArray_MIN(PyArray_DESCR(ap)->elsize, datalen)); -#else + Py_ssize_t num_bytes = actual_len * 4; + + char *buffer; if (!PyArray_ISALIGNED(ap)) { - buffer = PyArray_malloc(PyArray_DESCR(ap)->elsize); + buffer = PyArray_malloc(num_bytes); if (buffer == NULL) { Py_DECREF(temp); PyErr_NoMemory(); @@ -521,20 +565,23 @@ UNICODE_setitem(PyObject *op, void *ov, void *vap) else { buffer = ov; } - datalen = PyUCS2Buffer_AsUCS4(ptr, (npy_ucs4 *)buffer, - datalen >> 1, PyArray_DESCR(ap)->elsize >> 2); - datalen <<= 2; + if (PyUnicode_AsUCS4(temp, (Py_UCS4 *)buffer, actual_len, 0) == NULL) { + PyArray_free(buffer); + Py_DECREF(temp); + return -1; + } + if (!PyArray_ISALIGNED(ap)) { - memcpy(ov, buffer, datalen); + memcpy(ov, buffer, num_bytes); PyArray_free(buffer); } -#endif + /* Fill in the rest of the space with 0 */ - if (PyArray_DESCR(ap)->elsize > datalen) { - memset((char*)ov + datalen, 0, (PyArray_DESCR(ap)->elsize - datalen)); + if (PyArray_DESCR(ap)->elsize > num_bytes) { + memset((char*)ov + num_bytes, 0, (PyArray_DESCR(ap)->elsize - num_bytes)); } if (PyArray_ISBYTESWAPPED(ap)) { - byte_swap_vector(ov, PyArray_DESCR(ap)->elsize >> 2, 4); + byte_swap_vector(ov, actual_len, 4); } Py_DECREF(temp); return 0; @@ -577,7 +624,6 @@ STRING_setitem(PyObject *op, void *ov, void *vap) "setting an array element with a sequence"); return -1; } -#if defined(NPY_PY3K) if (PyUnicode_Check(op)) { /* Assume ASCII codec -- function similarly as Python 2 */ temp = PyUnicode_AsASCIIString(op); @@ -604,11 +650,6 @@ STRING_setitem(PyObject *op, void *ov, void *vap) return -1; } } -#else - if ((temp = PyObject_Str(op)) == NULL) { - return -1; - } -#endif if (PyBytes_AsStringAndSize(temp, &ptr, &len) < 0) { Py_DECREF(temp); return -1; @@ -633,7 +674,7 @@ static PyObject * OBJECT_getitem(void *ip, void *NPY_UNUSED(ap)) { PyObject *obj; - NPY_COPY_PYOBJECT_PTR(&obj, ip); + memcpy(&obj, ip, sizeof(obj)); if (obj == NULL) { Py_RETURN_NONE; } @@ -649,16 +690,17 @@ OBJECT_setitem(PyObject *op, void *ov, void *NPY_UNUSED(ap)) { PyObject *obj; - NPY_COPY_PYOBJECT_PTR(&obj, ov); + memcpy(&obj, ov, sizeof(obj)); Py_INCREF(op); Py_XDECREF(obj); - NPY_COPY_PYOBJECT_PTR(ov, &op); + memcpy(ov, &op, sizeof(op)); return PyErr_Occurred() ? -1 : 0; } + /* VOID */ static PyObject * @@ -666,22 +708,21 @@ VOID_getitem(void *input, void *vap) { PyArrayObject *ap = vap; char *ip = input; - PyArray_Descr* descr; + PyArray_Descr* descr = PyArray_DESCR(vap); - descr = PyArray_DESCR(ap); if (PyDataType_HASFIELDS(descr)) { PyObject *key; PyObject *names; int i, n; PyObject *ret; PyObject *tup; - int savedflags; + PyArrayObject_fields dummy_fields = get_dummy_stack_array(ap); + PyArrayObject *dummy_arr = (PyArrayObject *)&dummy_fields; /* get the names from the fields dictionary*/ names = descr->names; n = PyTuple_GET_SIZE(names); ret = PyTuple_New(n); - savedflags = PyArray_FLAGS(ap); for (i = 0; i < n; i++) { npy_intp offset; PyArray_Descr *new; @@ -689,26 +730,19 @@ VOID_getitem(void *input, void *vap) tup = PyDict_GetItem(descr->fields, key); if (_unpack_field(tup, &new, &offset) < 0) { Py_DECREF(ret); - ((PyArrayObject_fields *)ap)->descr = descr; return NULL; } - /* - * TODO: temporarily modifying the array like this - * is bad coding style, should be changed. - */ - ((PyArrayObject_fields *)ap)->descr = new; + dummy_fields.descr = new; /* update alignment based on offset */ if ((new->alignment > 1) && ((((npy_intp)(ip+offset)) % new->alignment) != 0)) { - PyArray_CLEARFLAGS(ap, NPY_ARRAY_ALIGNED); + PyArray_CLEARFLAGS(dummy_arr, NPY_ARRAY_ALIGNED); } else { - PyArray_ENABLEFLAGS(ap, NPY_ARRAY_ALIGNED); + PyArray_ENABLEFLAGS(dummy_arr, NPY_ARRAY_ALIGNED); } - PyTuple_SET_ITEM(ret, i, PyArray_GETITEM(ap, ip+offset)); - ((PyArrayObject_fields *)ap)->flags = savedflags; + PyTuple_SET_ITEM(ret, i, PyArray_GETITEM(dummy_arr, ip+offset)); } - ((PyArrayObject_fields *)ap)->descr = descr; return ret; } @@ -724,16 +758,33 @@ VOID_getitem(void *input, void *vap) return NULL; } Py_INCREF(descr->subarray->base); + + /* + * NOTE: There is the possibility of recursive calls from the above + * field branch. These calls use a dummy arr for thread + * (and general) safety. However, we must set the base array, + * so if such a dummy array was passed (its type is NULL), + * we have walk its base until the initial array is found. + * + * TODO: This should be fixed, the next "generation" of GETITEM will + * probably need to pass in the original array (in addition + * to the dtype as a method). Alternatively, VOID dtypes + * could have special handling. + */ + PyObject *base = (PyObject *)ap; + while (Py_TYPE(base) == NULL) { + base = PyArray_BASE((PyArrayObject *)base); + } ret = (PyArrayObject *)PyArray_NewFromDescrAndBase( &PyArray_Type, descr->subarray->base, shape.len, shape.ptr, NULL, ip, PyArray_FLAGS(ap) & ~NPY_ARRAY_F_CONTIGUOUS, - NULL, (PyObject *)ap); + NULL, base); npy_free_cache_dim_obj(shape); return (PyObject *)ret; } - return PyBytes_FromStringAndSize(PyArray_DATA(ap), descr->elsize); + return PyBytes_FromStringAndSize(ip, descr->elsize); } @@ -746,7 +797,8 @@ NPY_NO_EXPORT int PyArray_CopyObject(PyArrayObject *, PyObject *); * individual fields of a numpy structure, in VOID_setitem. Compare to inner * loops in VOID_getitem and VOID_nonzero. * - * WARNING: Clobbers arr's dtype and alignment flag. + * WARNING: Clobbers arr's dtype and alignment flag, should not be used + * on the original array! */ NPY_NO_EXPORT int _setup_field(int i, PyArray_Descr *descr, PyArrayObject *arr, @@ -764,7 +816,7 @@ _setup_field(int i, PyArray_Descr *descr, PyArrayObject *arr, } ((PyArrayObject_fields *)(arr))->descr = new; - if ((new->alignment > 1) && + if ((new->alignment > 1) && ((((uintptr_t)dstdata + offset) % new->alignment) != 0)) { PyArray_CLEARFLAGS(arr, NPY_ARRAY_ALIGNED); } @@ -783,7 +835,7 @@ static int _copy_and_return_void_setitem(PyArray_Descr *dstdescr, char *dstdata, PyArray_Descr *srcdescr, char *srcdata){ PyArrayObject_fields dummy_struct; - PyArrayObject *dummy = (PyArrayObject *)&dummy_struct; + PyArrayObject *dummy_arr = (PyArrayObject *)&dummy_struct; npy_int names_size = PyTuple_GET_SIZE(dstdescr->names); npy_intp offset; npy_int i; @@ -793,11 +845,11 @@ _copy_and_return_void_setitem(PyArray_Descr *dstdescr, char *dstdata, if (PyArray_EquivTypes(srcdescr, dstdescr)) { for (i = 0; i < names_size; i++) { /* neither line can ever fail, in principle */ - if (_setup_field(i, dstdescr, dummy, &offset, dstdata)) { + if (_setup_field(i, dstdescr, dummy_arr, &offset, dstdata)) { return -1; } - PyArray_DESCR(dummy)->f->copyswap(dstdata + offset, - srcdata + offset, 0, dummy); + PyArray_DESCR(dummy_arr)->f->copyswap(dstdata + offset, + srcdata + offset, 0, dummy_arr); } return 0; } @@ -816,13 +868,10 @@ VOID_setitem(PyObject *op, void *input, void *vap) { char *ip = input; PyArrayObject *ap = vap; - PyArray_Descr *descr; - int flags; - int itemsize=PyArray_DESCR(ap)->elsize; + int itemsize = PyArray_DESCR(ap)->elsize; int res; + PyArray_Descr *descr = PyArray_DESCR(ap); - descr = PyArray_DESCR(ap); - flags = PyArray_FLAGS(ap); if (PyDataType_HASFIELDS(descr)) { PyObject *errmsg; npy_int i; @@ -850,20 +899,22 @@ VOID_setitem(PyObject *op, void *input, void *vap) npy_intp names_size = PyTuple_GET_SIZE(descr->names); if (names_size != PyTuple_Size(op)) { - errmsg = PyUString_FromFormat( + errmsg = PyUnicode_FromFormat( "could not assign tuple of length %zd to structure " - "with %" NPY_INTP_FMT " fields.", + "with %" NPY_INTP_FMT " fields.", PyTuple_Size(op), names_size); PyErr_SetObject(PyExc_ValueError, errmsg); Py_DECREF(errmsg); return -1; } + PyArrayObject_fields dummy_fields = get_dummy_stack_array(ap); + PyArrayObject *dummy_arr = (PyArrayObject *)&dummy_fields; + for (i = 0; i < names_size; i++) { PyObject *item; - /* temporarily make ap have only this field */ - if (_setup_field(i, descr, ap, &offset, ip) == -1) { + if (_setup_field(i, descr, dummy_arr, &offset, ip) == -1) { failed = 1; break; } @@ -873,7 +924,7 @@ VOID_setitem(PyObject *op, void *input, void *vap) break; } /* use setitem to set this field */ - if (PyArray_SETITEM(ap, ip + offset, item) < 0) { + if (PyArray_SETITEM(dummy_arr, ip + offset, item) < 0) { failed = 1; break; } @@ -883,24 +934,23 @@ VOID_setitem(PyObject *op, void *input, void *vap) /* Otherwise must be non-void scalar. Try to assign to each field */ npy_intp names_size = PyTuple_GET_SIZE(descr->names); + PyArrayObject_fields dummy_fields = get_dummy_stack_array(ap); + PyArrayObject *dummy_arr = (PyArrayObject *)&dummy_fields; + for (i = 0; i < names_size; i++) { /* temporarily make ap have only this field */ - if (_setup_field(i, descr, ap, &offset, ip) == -1) { + if (_setup_field(i, descr, dummy_arr, &offset, ip) == -1) { failed = 1; break; } /* use setitem to set this field */ - if (PyArray_SETITEM(ap, ip + offset, op) < 0) { + if (PyArray_SETITEM(dummy_arr, ip + offset, op) < 0) { failed = 1; break; } } } - /* reset clobbered attributes */ - ((PyArrayObject_fields *)(ap))->descr = descr; - ((PyArrayObject_fields *)(ap))->flags = flags; - if (failed) { return -1; } @@ -909,7 +959,6 @@ VOID_setitem(PyObject *op, void *input, void *vap) else if (PyDataType_HASSUBARRAY(descr)) { /* copy into an array of the same basic type */ PyArray_Dims shape = {NULL, -1}; - PyArrayObject *ret; if (!(PyArray_IntpConverter(descr->subarray->shape, &shape))) { npy_free_cache_dim_obj(shape); PyErr_SetString(PyExc_ValueError, @@ -917,10 +966,15 @@ VOID_setitem(PyObject *op, void *input, void *vap) return -1; } Py_INCREF(descr->subarray->base); - ret = (PyArrayObject *)PyArray_NewFromDescrAndBase( + /* + * Note we set no base object here, as to not rely on the input + * being a valid object for base setting. `ret` nevertheless does + * does not own its data, this is generally not good, but localized. + */ + PyArrayObject *ret = (PyArrayObject *)PyArray_NewFromDescrAndBase( &PyArray_Type, descr->subarray->base, shape.len, shape.ptr, NULL, ip, - PyArray_FLAGS(ap), NULL, (PyObject *)ap); + PyArray_FLAGS(ap), NULL, NULL); npy_free_cache_dim_obj(shape); if (!ret) { return -1; @@ -935,7 +989,6 @@ VOID_setitem(PyObject *op, void *input, void *vap) * undiscerning case: It interprets any object as a buffer * and reads as many bytes as possible, padding with 0. */ -#if defined(NPY_PY3K) { Py_buffer view; @@ -948,20 +1001,6 @@ VOID_setitem(PyObject *op, void *input, void *vap) } PyBuffer_Release(&view); } -#else - { - const void *buffer; - Py_ssize_t buflen; - - if (PyObject_AsReadBuffer(op, &buffer, &buflen) < 0) { - return -1; - } - memcpy(ip, buffer, PyArray_MIN(buflen, itemsize)); - if (itemsize > buflen) { - memset(ip + buflen, 0, itemsize - buflen); - } - } -#endif return 0; } @@ -1096,6 +1135,7 @@ TIMEDELTA_setitem(PyObject *op, void *ov, void *vap) * npy_long, npy_ulong, npy_longlong, npy_ulonglong, * npy_float, npy_double, npy_longdouble, * npy_datetime, npy_timedelta# + * #supports_nat = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1# */ /**begin repeat1 @@ -1107,6 +1147,7 @@ TIMEDELTA_setitem(PyObject *op, void *ov, void *vap) * npy_long, npy_ulong, npy_longlong, npy_ulonglong, * npy_float, npy_double, npy_longdouble, * npy_datetime, npy_timedelta# + * #floatingpoint = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0# */ static void @FROMTYPE@_to_@TOTYPE@(void *input, void *output, npy_intp n, @@ -1116,7 +1157,15 @@ static void @totype@ *op = output; while (n--) { - *op++ = (@totype@)*ip++; + @fromtype@ f = *ip++; + @totype@ t = (@totype@)f; +#if @supports_nat@ && @floatingpoint@ + /* Avoid undefined behaviour for NaN -> NaT */ + if (npy_isnan(f)) { + t = (@totype@)NPY_DATETIME_NAT; + } +#endif + *op++ = t; } } /**end repeat1**/ @@ -1134,7 +1183,15 @@ static void @totype@ *op = output; while (n--) { - *op++ = (@totype@)*ip; + @fromtype@ f = *ip; + @totype@ t = (@totype@)f; +#if @supports_nat@ + /* Avoid undefined behaviour for NaN -> NaT */ + if (npy_isnan(f)) { + t = (@totype@)NPY_DATETIME_NAT; + } +#endif + *op++ = t; ip += 2; } } @@ -1489,10 +1546,14 @@ OBJECT_to_@TOTYPE@(void *input, void *output, npy_intp n, for (i = 0; i < n; i++, ip++, op += skip) { if (*ip == NULL) { - @TOTYPE@_setitem(Py_False, op, aop); + if (@TOTYPE@_setitem(Py_False, op, aop) < 0) { + return; + } } else { - @TOTYPE@_setitem(*ip, op, aop); + if (@TOTYPE@_setitem(*ip, op, aop) < 0) { + return; + } } } } @@ -1503,6 +1564,7 @@ OBJECT_to_@TOTYPE@(void *input, void *output, npy_intp n, * * #from = STRING*23, UNICODE*23, VOID*23# * #fromtyp = npy_char*69# + * #is_string_to_bool = 1, 0*22, 1, 0*22, 0*23# * #to = (BOOL, * BYTE, UBYTE, SHORT, USHORT, INT, UINT, * LONG, ULONG, LONGLONG, ULONGLONG, @@ -1520,16 +1582,8 @@ OBJECT_to_@TOTYPE@(void *input, void *output, npy_intp n, * #oskip = 1*18,(PyArray_DESCR(aop)->elsize)*3,1*2, * 1*18,(PyArray_DESCR(aop)->elsize)*3,1*2, * 1*18,(PyArray_DESCR(aop)->elsize)*3,1*2# - * #convert = 1*18, 0*3, 1*2, - * 1*18, 0*3, 1*2, - * 0*23# - * #convstr = (Int*9, Long*2, Float*4, Complex*3, Tuple*3, Long*2)*3# */ -#if @convert@ - -#define IS_@from@ - static void @from@_to_@to@(void *input, void *output, npy_intp n, void *vaip, void *aop) @@ -1543,70 +1597,17 @@ static void int oskip = @oskip@; for (i = 0; i < n; i++, ip+=skip, op+=oskip) { - PyObject *new; PyObject *temp = PyArray_Scalar(ip, PyArray_DESCR(aip), (PyObject *)aip); if (temp == NULL) { return; } - -#if defined(NPY_PY3K) && defined(IS_STRING) - /* Work around some Python 3K */ - new = PyUnicode_FromEncodedObject(temp, "ascii", "strict"); - Py_DECREF(temp); - temp = new; +#if @is_string_to_bool@ + /* Legacy behaviour converts strings to integers before going to bool */ + Py_SETREF(temp, PyNumber_Long(temp)); if (temp == NULL) { return; } #endif - /* convert from Python object to needed one */ - { - PyObject *args; - - /* call out to the Python builtin given by convstr */ - args = Py_BuildValue("(N)", temp); -#if defined(NPY_PY3K) -#define PyInt_Type PyLong_Type -#endif - new = Py@convstr@_Type.tp_new(&Py@convstr@_Type, args, NULL); -#if defined(NPY_PY3K) -#undef PyInt_Type -#endif - Py_DECREF(args); - temp = new; - if (temp == NULL) { - return; - } - } - - if (@to@_setitem(temp, op, aop)) { - Py_DECREF(temp); - return; - } - Py_DECREF(temp); - } -} - -#undef IS_@from@ - -#else - -static void -@from@_to_@to@(void *input, void *output, npy_intp n, - void *vaip, void *aop) -{ - @fromtyp@ *ip = input; - @totyp@ *op = output; - PyArrayObject *aip = vaip; - - npy_intp i; - int skip = PyArray_DESCR(aip)->elsize; - int oskip = @oskip@; - - for (i = 0; i < n; i++, ip+=skip, op+=oskip) { - PyObject *temp = PyArray_Scalar(ip, PyArray_DESCR(aip), (PyObject *)aip); - if (temp == NULL) { - return; - } if (@to@_setitem(temp, op, aop)) { Py_DECREF(temp); return; @@ -1615,7 +1616,6 @@ static void } } -#endif /**end repeat**/ @@ -1768,7 +1768,58 @@ BOOL_scan(FILE *fp, npy_bool *ip, void *NPY_UNUSED(ignore), } /**begin repeat - * #fname = CFLOAT, CDOUBLE, CLONGDOUBLE, + * #fname = CFLOAT, CDOUBLE# + * #type = npy_cfloat, npy_cdouble# + */ +static int +@fname@_scan(FILE *fp, @type@ *ip, void *NPY_UNUSED(ignore), + PyArray_Descr *NPY_UNUSED(ignored)) +{ + double result; + int ret_real, ret_imag; + + ret_real = NumPyOS_ascii_ftolf(fp, &result); + @type@ output; + // Peek next character + char next = getc(fp); + if ((next == '+') || (next == '-')) { + // Imaginary component specified + output.real = result; + // Revert peek and read imaginary component + ungetc(next, fp); + ret_imag = NumPyOS_ascii_ftolf(fp, &result); + // Peak next character + next = getc(fp); + if ((ret_imag == 1) && (next == 'j')) { + // If read is successful and the immediate following char is j + output.imag = result; + } + else { + output.imag = 0; + // Push an invalid char to trigger the not everything is read error + ungetc('a', fp); + } + } + else if (next == 'j') { + // Real component not specified + output.real = 0; + output.imag = result; + } + else { + // Imaginary component not specified + output.real = result; + output.imag = 0.; + // Next character is not + / - / j. Revert peek. + ungetc(next, fp); + } + *(@type@ *)ip = output; + return ret_real; +} +/**end repeat**/ + + +/**begin repeat + * #fname = CLONGDOUBLE, * OBJECT, STRING, UNICODE, VOID, * DATETIME, TIMEDELTA# */ @@ -1792,8 +1843,8 @@ BOOL_scan(FILE *fp, npy_bool *ip, void *NPY_UNUSED(ignore), * #type = npy_byte, npy_ubyte, npy_short, npy_ushort, npy_int, npy_uint, * npy_long, npy_ulong, npy_longlong, npy_ulonglong, * npy_datetime, npy_timedelta# - * #func = (PyOS_strtol, PyOS_strtoul)*4, npy_strtoll, npy_strtoull, - * npy_strtoll*2# + * #func = (PyOS_strtol, PyOS_strtoul)*4, NumPyOS_strtoll, NumPyOS_strtoull, + * NumPyOS_strtoll*2# * #btype = (npy_long, npy_ulong)*4, npy_longlong, npy_ulonglong, * npy_longlong*2# */ @@ -1860,7 +1911,60 @@ BOOL_fromstr(char *str, void *ip, char **endptr, } /**begin repeat - * #fname = CFLOAT, CDOUBLE, CLONGDOUBLE, + * #fname = CFLOAT, CDOUBLE# + * #type = npy_cfloat, npy_cdouble# + */ +static int +@fname@_fromstr(char *str, void *ip, char **endptr, + PyArray_Descr *NPY_UNUSED(ignore)) +{ + double result; + + result = NumPyOS_ascii_strtod(str, endptr); + @type@ output; + + if (endptr && ((*endptr[0] == '+') || (*endptr[0] == '-'))) { + // Imaginary component specified + output.real = result; + // Reading imaginary component + char **prev = endptr; + str = *endptr; + result = NumPyOS_ascii_strtod(str, endptr); + if (endptr && *endptr[0] == 'j') { + // Read is successful if the immediate following char is j + output.imag = result; + // Skip j + ++*endptr; + } + else { + /* + * Set endptr to previous char to trigger the not everything is + * read error + */ + endptr = prev; + output.imag = 0; + } + } + else if (endptr && *endptr[0] == 'j') { + // Real component not specified + output.real = 0; + output.imag = result; + // Skip j + ++*endptr; + } + else { + // Imaginary component not specified + output.real = result; + output.imag = 0.; + } + *(@type@ *)ip = output; + return 0; +} +/**end repeat**/ + + +/**begin repeat + * #fname = CLONGDOUBLE, * OBJECT, STRING, UNICODE, VOID# */ @@ -2172,11 +2276,11 @@ OBJECT_copyswapn(PyObject **dst, npy_intp dstride, PyObject **src, dstp = (unsigned char*)dst; srcp = (unsigned char*)src; for (i = 0; i < n; i++) { - NPY_COPY_PYOBJECT_PTR(&tmp, srcp); + memcpy(&tmp, srcp, sizeof(tmp)); Py_XINCREF(tmp); - NPY_COPY_PYOBJECT_PTR(&tmp, dstp); + memcpy(&tmp, dstp, sizeof(tmp)); Py_XDECREF(tmp); - NPY_COPY_PYOBJECT_PTR(dstp, srcp); + memcpy(dstp, srcp, sizeof(tmp)); dstp += dstride; srcp += sstride; } @@ -2200,11 +2304,11 @@ OBJECT_copyswap(PyObject **dst, PyObject **src, int NPY_UNUSED(swap), } else { PyObject *tmp; - NPY_COPY_PYOBJECT_PTR(&tmp, src); + memcpy(&tmp, src, sizeof(tmp)); Py_XINCREF(tmp); - NPY_COPY_PYOBJECT_PTR(&tmp, dst); + memcpy(&tmp, dst, sizeof(tmp)); Py_XDECREF(tmp); - NPY_COPY_PYOBJECT_PTR(dst, src); + memcpy(dst, src, sizeof(tmp)); } } } @@ -2214,6 +2318,7 @@ static void STRING_copyswapn (char *dst, npy_intp dstride, char *src, npy_intp sstride, npy_intp n, int NPY_UNUSED(swap), PyArrayObject *arr) { + assert(arr != NULL); if (arr == NULL) { return; } @@ -2221,131 +2326,170 @@ STRING_copyswapn (char *dst, npy_intp dstride, char *src, npy_intp sstride, return; } + /* */ static void VOID_copyswapn (char *dst, npy_intp dstride, char *src, npy_intp sstride, npy_intp n, int swap, PyArrayObject *arr) { + PyArray_Descr *descr; + + assert(arr != NULL); if (arr == NULL) { return; } + + descr = PyArray_DESCR(arr); + if (PyArray_HASFIELDS(arr)) { PyObject *key, *value; - PyArray_Descr *descr; Py_ssize_t pos = 0; - descr = PyArray_DESCR(arr); + PyArrayObject_fields dummy_fields = get_dummy_stack_array(arr); + PyArrayObject *dummy_arr = (PyArrayObject *)&dummy_fields; + while (PyDict_Next(descr->fields, &pos, &key, &value)) { npy_intp offset; - PyArray_Descr * new; + PyArray_Descr *new; if (NPY_TITLE_KEY(key, value)) { continue; } if (_unpack_field(value, &new, &offset) < 0) { - ((PyArrayObject_fields *)arr)->descr = descr; return; } - /* - * TODO: temporarily modifying the array like this - * is bad coding style, should be changed. - */ - ((PyArrayObject_fields *)arr)->descr = new; + + dummy_fields.descr = new; new->f->copyswapn(dst+offset, dstride, (src != NULL ? src+offset : NULL), - sstride, n, swap, arr); + sstride, n, swap, dummy_arr); } - ((PyArrayObject_fields *)arr)->descr = descr; return; } - if (swap && PyArray_DESCR(arr)->subarray != NULL) { - PyArray_Descr *descr, *new; + if (PyDataType_HASSUBARRAY(descr)) { + PyArray_Descr *new; npy_intp num; npy_intp i; int subitemsize; char *dstptr, *srcptr; - - descr = PyArray_DESCR(arr); - new = descr->subarray->base; /* - * TODO: temporarily modifying the array like this - * is bad coding style, should be changed. + * In certain cases subarray copy can be optimized. This is when + * swapping is unnecessary and the subarrays data type can certainly + * be simply copied (no object, fields, subarray, and not a user dtype). */ - ((PyArrayObject_fields *)arr)->descr = new; + npy_bool can_optimize_subarray = (!swap && + !PyDataType_HASFIELDS(descr->subarray->base) && + !PyDataType_HASSUBARRAY(descr->subarray->base) && + !PyDataType_REFCHK(descr->subarray->base) && + (descr->subarray->base->type_num < NPY_NTYPES)); + + if (can_optimize_subarray) { + _basic_copyn(dst, dstride, src, sstride, n, descr->elsize); + return; + } + + new = descr->subarray->base; dstptr = dst; srcptr = src; subitemsize = new->elsize; + if (subitemsize == 0) { + /* There cannot be any elements, so return */ + return; + } + + PyArrayObject_fields dummy_fields = get_dummy_stack_array(arr); + PyArrayObject *dummy_arr = (PyArrayObject *)&dummy_fields; + ((PyArrayObject_fields *)dummy_arr)->descr = new; + num = descr->elsize / subitemsize; for (i = 0; i < n; i++) { new->f->copyswapn(dstptr, subitemsize, srcptr, - subitemsize, num, swap, arr); + subitemsize, num, swap, dummy_arr); dstptr += dstride; if (srcptr) { srcptr += sstride; } } - ((PyArrayObject_fields *)arr)->descr = descr; return; } - _basic_copyn(dst, dstride, src, sstride, n, PyArray_DESCR(arr)->elsize); + /* Must be a naive Void type (e.g. a "V8") so simple copy is sufficient. */ + _basic_copyn(dst, dstride, src, sstride, n, descr->elsize); return; } static void VOID_copyswap (char *dst, char *src, int swap, PyArrayObject *arr) { + PyArray_Descr *descr; + + assert(arr != NULL); if (arr == NULL) { return; } + + descr = PyArray_DESCR(arr); + if (PyArray_HASFIELDS(arr)) { PyObject *key, *value; - PyArray_Descr *descr; Py_ssize_t pos = 0; - descr = PyArray_DESCR(arr); + PyArrayObject_fields dummy_fields = get_dummy_stack_array(arr); + PyArrayObject *dummy_arr = (PyArrayObject *)&dummy_fields; + while (PyDict_Next(descr->fields, &pos, &key, &value)) { npy_intp offset; + PyArray_Descr * new; if (NPY_TITLE_KEY(key, value)) { continue; } if (_unpack_field(value, &new, &offset) < 0) { - ((PyArrayObject_fields *)arr)->descr = descr; return; } - /* - * TODO: temporarily modifying the array like this - * is bad coding style, should be changed. - */ - ((PyArrayObject_fields *)arr)->descr = new; + dummy_fields.descr = new; new->f->copyswap(dst+offset, (src != NULL ? src+offset : NULL), - swap, arr); + swap, dummy_arr); } - ((PyArrayObject_fields *)arr)->descr = descr; return; } - if (swap && PyArray_DESCR(arr)->subarray != NULL) { - PyArray_Descr *descr, *new; + if (PyDataType_HASSUBARRAY(descr)) { + PyArray_Descr *new; npy_intp num; - int itemsize; - - descr = PyArray_DESCR(arr); - new = descr->subarray->base; + int subitemsize; /* - * TODO: temporarily modifying the array like this - * is bad coding style, should be changed. + * In certain cases subarray copy can be optimized. This is when + * swapping is unnecessary and the subarrays data type can certainly + * be simply copied (no object, fields, subarray, and not a user dtype). */ - ((PyArrayObject_fields *)arr)->descr = new; - itemsize = new->elsize; - num = descr->elsize / itemsize; - new->f->copyswapn(dst, itemsize, src, - itemsize, num, swap, arr); - ((PyArrayObject_fields *)arr)->descr = descr; + npy_bool can_optimize_subarray = (!swap && + !PyDataType_HASFIELDS(descr->subarray->base) && + !PyDataType_HASSUBARRAY(descr->subarray->base) && + !PyDataType_REFCHK(descr->subarray->base) && + (descr->subarray->base->type_num < NPY_NTYPES)); + + if (can_optimize_subarray) { + _basic_copy(dst, src, descr->elsize); + return; + } + + new = descr->subarray->base; + subitemsize = new->elsize; + if (subitemsize == 0) { + /* There cannot be any elements, so return */ + return; + } + + PyArrayObject_fields dummy_fields = get_dummy_stack_array(arr); + PyArrayObject *dummy_arr = (PyArrayObject *)&dummy_fields; + dummy_fields.descr = new; + + num = descr->elsize / subitemsize; + new->f->copyswapn(dst, subitemsize, src, + subitemsize, num, swap, dummy_arr); return; } - - /* copy first if needed */ - _basic_copy(dst, src, PyArray_DESCR(arr)->elsize); + /* Must be a naive Void type (e.g. a "V8") so simple copy is sufficient. */ + _basic_copy(dst, src, descr->elsize); return; } @@ -2356,6 +2500,7 @@ UNICODE_copyswapn (char *dst, npy_intp dstride, char *src, npy_intp sstride, { int itemsize; + assert(arr != NULL); if (arr == NULL) { return; } @@ -2383,6 +2528,7 @@ UNICODE_copyswapn (char *dst, npy_intp dstride, char *src, npy_intp sstride, static void STRING_copyswap(char *dst, char *src, int NPY_UNUSED(swap), PyArrayObject *arr) { + assert(arr != NULL); if (arr == NULL) { return; } @@ -2395,6 +2541,7 @@ UNICODE_copyswap (char *dst, char *src, int swap, PyArrayObject *arr) { int itemsize; + assert(arr != NULL); if (arr == NULL) { return; } @@ -2525,12 +2672,6 @@ STRING_nonzero (char *ip, PyArrayObject *ap) return nonz; } -#ifdef Py_UNICODE_WIDE -#define PyArray_UCS4_ISSPACE Py_UNICODE_ISSPACE -#else -#define PyArray_UCS4_ISSPACE(ch) Py_STRING_ISSPACE((char)ch) -#endif - static npy_bool UNICODE_nonzero (npy_ucs4 *ip, PyArrayObject *ap) { @@ -2556,7 +2697,7 @@ UNICODE_nonzero (npy_ucs4 *ip, PyArrayObject *ap) if (*ip == '\0') { seen_null = NPY_TRUE; } - else if (seen_null || !PyArray_UCS4_ISSPACE(*ip)) { + else if (seen_null || !Py_UNICODE_ISSPACE(*ip)) { nonz = NPY_TRUE; break; } @@ -2578,7 +2719,7 @@ OBJECT_nonzero (PyObject **ip, PyArrayObject *ap) } else { PyObject *obj; - NPY_COPY_PYOBJECT_PTR(&obj, ip); + memcpy(&obj, ip, sizeof(obj)); if (obj == NULL) { return NPY_FALSE; } @@ -2599,11 +2740,11 @@ VOID_nonzero (char *ip, PyArrayObject *ap) if (PyArray_HASFIELDS(ap)) { PyArray_Descr *descr; PyObject *key, *value; - int savedflags; Py_ssize_t pos = 0; + PyArrayObject_fields dummy_fields = get_dummy_stack_array(ap); + PyArrayObject *dummy_arr = (PyArrayObject *)&dummy_fields; descr = PyArray_DESCR(ap); - savedflags = PyArray_FLAGS(ap); while (PyDict_Next(descr->fields, &pos, &key, &value)) { PyArray_Descr * new; npy_intp offset; @@ -2614,26 +2755,20 @@ VOID_nonzero (char *ip, PyArrayObject *ap) PyErr_Clear(); continue; } - /* - * TODO: temporarily modifying the array like this - * is bad coding style, should be changed. - */ - ((PyArrayObject_fields *)ap)->descr = new; - ((PyArrayObject_fields *)ap)->flags = savedflags; + + dummy_fields.descr = new; if ((new->alignment > 1) && !__ALIGNED(ip + offset, new->alignment)) { - PyArray_CLEARFLAGS(ap, NPY_ARRAY_ALIGNED); + PyArray_CLEARFLAGS(dummy_arr, NPY_ARRAY_ALIGNED); } else { - PyArray_ENABLEFLAGS(ap, NPY_ARRAY_ALIGNED); + PyArray_ENABLEFLAGS(dummy_arr, NPY_ARRAY_ALIGNED); } - if (new->f->nonzero(ip+offset, ap)) { + if (new->f->nonzero(ip+offset, dummy_arr)) { nonz = NPY_TRUE; break; } } - ((PyArrayObject_fields *)ap)->descr = descr; - ((PyArrayObject_fields *)ap)->flags = savedflags; return nonz; } len = PyArray_DESCR(ap)->elsize; @@ -2670,11 +2805,9 @@ BOOL_compare(npy_bool *ip1, npy_bool *ip2, PyArrayObject *NPY_UNUSED(ap)) /**begin repeat * #TYPE = BYTE, UBYTE, SHORT, USHORT, INT, UINT, - * LONG, ULONG, LONGLONG, ULONGLONG, - * DATETIME, TIMEDELTA# + * LONG, ULONG, LONGLONG, ULONGLONG# * #type = npy_byte, npy_ubyte, npy_short, npy_ushort, npy_int, npy_uint, - * npy_long, npy_ulong, npy_longlong, npy_ulonglong, - * npy_datetime, npy_timedelta# + * npy_long, npy_ulong, npy_longlong, npy_ulonglong# */ static int @@ -2785,12 +2918,43 @@ C@TYPE@_compare(@type@ *pa, @type@ *pb) /**end repeat**/ +/**begin repeat + * #TYPE = DATETIME, TIMEDELTA# + * #type = npy_datetime, npy_timedelta# + */ + static int -HALF_compare (npy_half *pa, npy_half *pb, PyArrayObject *NPY_UNUSED(ap)) +@TYPE@_compare(@type@ *pa, @type@ *pb) { - npy_half a = *pa, b = *pb; - npy_bool a_isnan, b_isnan; - int ret; + const @type@ a = *pa; + const @type@ b = *pb; + int ret; + + if (a == NPY_DATETIME_NAT) { + if (b == NPY_DATETIME_NAT) { + ret = 0; + } + else { + ret = 1; + } + } + else if (b == NPY_DATETIME_NAT) { + ret = -1; + } + else { + ret = a < b ? -1 : a == b ? 0 : 1; + } + return ret; +} + +/**end repeat**/ + +static int +HALF_compare (npy_half *pa, npy_half *pb, PyArrayObject *NPY_UNUSED(ap)) +{ + npy_half a = *pa, b = *pb; + npy_bool a_isnan, b_isnan; + int ret; a_isnan = npy_half_isnan(a); b_isnan = npy_half_isnan(b); @@ -2846,7 +3010,7 @@ OBJECT_compare(PyObject **ip1, PyObject **ip2, PyArrayObject *NPY_UNUSED(ap)) ret = PyObject_RichCompareBool(*ip1, *ip2, Py_LT); if (ret < 0) { - /* error occurred, avoid the next call to PyObject_RichCompareBool */ + /* error occurred, avoid the next call to PyObject_RichCompareBool */ return 0; } if (ret == 1) { @@ -2929,6 +3093,10 @@ VOID_compare(char *ip1, char *ip2, PyArrayObject *ap) if (!PyArray_HASFIELDS(ap)) { return STRING_compare(ip1, ip2, ap); } + PyObject *mem_handler = PyDataMem_GetHandler(); + if (mem_handler == NULL) { + goto finish; + } descr = PyArray_DESCR(ap); /* * Compare on the first-field. If equal, then @@ -2943,15 +3111,19 @@ VOID_compare(char *ip1, char *ip2, PyArrayObject *ap) if (_unpack_field(tup, &new, &offset) < 0) { goto finish; } - /* descr is the only field checked by compare or copyswap */ + /* Set the fields needed by compare or copyswap */ dummy_struct.descr = new; + swap = PyArray_ISBYTESWAPPED(dummy); nip1 = ip1 + offset; nip2 = ip2 + offset; if (swap || new->alignment > 1) { if (swap || !npy_is_aligned(nip1, new->alignment)) { - /* create buffer and copy */ - nip1 = npy_alloc_cache(new->elsize); + /* + * create temporary buffer and copy, + * always use the current handler for internal allocations + */ + nip1 = PyDataMem_UserNEW(new->elsize, mem_handler); if (nip1 == NULL) { goto finish; } @@ -2960,11 +3132,15 @@ VOID_compare(char *ip1, char *ip2, PyArrayObject *ap) new->f->copyswap(nip1, NULL, swap, dummy); } if (swap || !npy_is_aligned(nip2, new->alignment)) { - /* create buffer and copy */ - nip2 = npy_alloc_cache(new->elsize); + /* + * create temporary buffer and copy, + * always use the current handler for internal allocations + */ + nip2 = PyDataMem_UserNEW(new->elsize, mem_handler); if (nip2 == NULL) { if (nip1 != ip1 + offset) { - npy_free_cache(nip1, new->elsize); + /* destroy temporary buffer */ + PyDataMem_UserFREE(nip1, new->elsize, mem_handler); } goto finish; } @@ -2976,10 +3152,12 @@ VOID_compare(char *ip1, char *ip2, PyArrayObject *ap) res = new->f->compare(nip1, nip2, dummy); if (swap || new->alignment > 1) { if (nip1 != ip1 + offset) { - npy_free_cache(nip1, new->elsize); + /* destroy temporary buffer */ + PyDataMem_UserFREE(nip1, new->elsize, mem_handler); } if (nip2 != ip2 + offset) { - npy_free_cache(nip2, new->elsize); + /* destroy temporary buffer */ + PyDataMem_UserFREE(nip2, new->elsize, mem_handler); } } if (res != 0) { @@ -2988,6 +3166,7 @@ VOID_compare(char *ip1, char *ip2, PyArrayObject *ap) } finish: + Py_XDECREF(mem_handler); return res; } @@ -2997,7 +3176,15 @@ finish: ** ARGFUNC ** ***************************************************************************** */ - +#if defined(__ARM_NEON__) || defined (__ARM_NEON) + int32_t _mm_movemask_epi8_neon(uint8x16_t input) + { + int8x8_t m0 = vcreate_s8(0x0706050403020100ULL); + uint8x16_t v0 = vshlq_u8(vshrq_n_u8(input, 7), vcombine_s8(m0, m0)); + uint64x2_t v1 = vpaddlq_u32(vpaddlq_u16(vpaddlq_u8(v0))); + return (int)vgetq_lane_u64(v1, 0) + ((int)vgetq_lane_u64(v1, 1) << 8); + } +#endif #define _LESS_THAN_OR_EQUAL(a,b) ((a) <= (b)) static int @@ -3018,6 +3205,19 @@ BOOL_argmax(npy_bool *ip, npy_intp n, npy_intp *max_ind, break; } } +#else + #if defined(__ARM_NEON__) || defined (__ARM_NEON) + uint8x16_t zero = vdupq_n_u8(0); + for(; i < n - (n % 32); i+=32) { + uint8x16_t d1 = vld1q_u8((uint8_t *)&ip[i]); + uint8x16_t d2 = vld1q_u8((uint8_t *)&ip[i + 16]); + d1 = vceqq_u8(d1, zero); + d2 = vceqq_u8(d2, zero); + if(_mm_movemask_epi8_neon(vminq_u8(d1, d2)) != 0xFFFF) { + break; + } + } + #endif #endif for (; i < n; i++) { if (ip[i]) { @@ -3046,6 +3246,7 @@ BOOL_argmax(npy_bool *ip, npy_intp n, npy_intp *max_ind, * #le = _LESS_THAN_OR_EQUAL*10, npy_half_le, _LESS_THAN_OR_EQUAL*8# * #iscomplex = 0*14, 1*3, 0*2# * #incr = ip++*14, ip+=2*3, ip++*2# + * #isdatetime = 0*17, 1*2# */ static int @fname@_argmax(@type@ *ip, npy_intp n, npy_intp *max_ind, @@ -3071,6 +3272,12 @@ static int return 0; } #endif +#if @isdatetime@ + if (mp == NPY_DATETIME_NAT) { + /* NaT encountered, it's maximal */ + return 0; + } +#endif for (i = 1; i < n; i++) { @incr@; @@ -3090,6 +3297,13 @@ static int } } #else +#if @isdatetime@ + if (*ip == NPY_DATETIME_NAT) { + /* NaT encountered, it's maximal */ + *max_ind = i; + break; + } +#endif if (!@le@(*ip, mp)) { /* negated, for correct nan handling */ mp = *ip; *max_ind = i; @@ -3126,16 +3340,19 @@ BOOL_argmin(npy_bool *ip, npy_intp n, npy_intp *min_ind, * #fname = BYTE, UBYTE, SHORT, USHORT, INT, UINT, * LONG, ULONG, LONGLONG, ULONGLONG, * HALF, FLOAT, DOUBLE, LONGDOUBLE, - * CFLOAT, CDOUBLE, CLONGDOUBLE# + * CFLOAT, CDOUBLE, CLONGDOUBLE, + * DATETIME, TIMEDELTA# * #type = npy_byte, npy_ubyte, npy_short, npy_ushort, npy_int, npy_uint, * npy_long, npy_ulong, npy_longlong, npy_ulonglong, * npy_half, npy_float, npy_double, npy_longdouble, - * npy_float, npy_double, npy_longdouble# - * #isfloat = 0*10, 1*7# - * #isnan = nop*10, npy_half_isnan, npy_isnan*6# - * #le = _LESS_THAN_OR_EQUAL*10, npy_half_le, _LESS_THAN_OR_EQUAL*6# - * #iscomplex = 0*14, 1*3# - * #incr = ip++*14, ip+=2*3# + * npy_float, npy_double, npy_longdouble, + * npy_datetime, npy_timedelta# + * #isfloat = 0*10, 1*7, 0*2# + * #isnan = nop*10, npy_half_isnan, npy_isnan*6, nop*2# + * #le = _LESS_THAN_OR_EQUAL*10, npy_half_le, _LESS_THAN_OR_EQUAL*8# + * #iscomplex = 0*14, 1*3, 0*2# + * #incr = ip++*14, ip+=2*3, ip++*2# + * #isdatetime = 0*17, 1*2# */ static int @fname@_argmin(@type@ *ip, npy_intp n, npy_intp *min_ind, @@ -3161,6 +3378,12 @@ static int return 0; } #endif +#if @isdatetime@ + if (mp == NPY_DATETIME_NAT) { + /* NaT encountered, it's minimal */ + return 0; + } +#endif for (i = 1; i < n; i++) { @incr@; @@ -3180,6 +3403,13 @@ static int } } #else +#if @isdatetime@ + if (*ip == NPY_DATETIME_NAT) { + /* NaT encountered, it's minimal */ + *min_ind = i; + break; + } +#endif if (!@le@(mp, *ip)) { /* negated, for correct nan handling */ mp = *ip; *min_ind = i; @@ -3199,43 +3429,6 @@ static int #undef _LESS_THAN_OR_EQUAL -/**begin repeat - * - * #fname = DATETIME, TIMEDELTA# - * #type = npy_datetime, npy_timedelta# - */ -static int -@fname@_argmin(@type@ *ip, npy_intp n, npy_intp *min_ind, - PyArrayObject *NPY_UNUSED(aip)) -{ - /* NPY_DATETIME_NAT is smaller than every other value, we skip - * it for consistency with min(). - */ - npy_intp i; - @type@ mp = NPY_DATETIME_NAT; - - i = 0; - while (i < n && mp == NPY_DATETIME_NAT) { - mp = ip[i]; - i++; - } - if (i == n) { - /* All NaTs: return 0 */ - *min_ind = 0; - return 0; - } - *min_ind = i - 1; - for (; i < n; i++) { - if (mp > ip[i] && ip[i] != NPY_DATETIME_NAT) { - mp = ip[i]; - *min_ind = i; - } - } - return 0; -} - -/**end repeat**/ - static int OBJECT_argmax(PyObject **ip, npy_intp n, npy_intp *max_ind, PyArrayObject *NPY_UNUSED(aip)) @@ -3388,17 +3581,17 @@ NPY_NO_EXPORT void npy_intp n, void *NPY_UNUSED(ignore)) { #if defined(HAVE_CBLAS) - int is1b = blas_stride(is1, sizeof(@type@)); - int is2b = blas_stride(is2, sizeof(@type@)); + CBLAS_INT is1b = blas_stride(is1, sizeof(@type@)); + CBLAS_INT is2b = blas_stride(is2, sizeof(@type@)); if (is1b && is2b) { double sum = 0.; /* double for stability */ while (n > 0) { - int chunk = n < NPY_CBLAS_CHUNK ? n : NPY_CBLAS_CHUNK; + CBLAS_INT chunk = n < NPY_CBLAS_CHUNK ? n : NPY_CBLAS_CHUNK; - sum += cblas_@prefix@dot(chunk, + sum += CBLAS_FUNC(cblas_@prefix@dot)(chunk, (@type@ *) ip1, is1b, (@type@ *) ip2, is2b); /* use char strides here */ @@ -3437,17 +3630,17 @@ NPY_NO_EXPORT void char *op, npy_intp n, void *NPY_UNUSED(ignore)) { #if defined(HAVE_CBLAS) - int is1b = blas_stride(is1, sizeof(@ctype@)); - int is2b = blas_stride(is2, sizeof(@ctype@)); + CBLAS_INT is1b = blas_stride(is1, sizeof(@ctype@)); + CBLAS_INT is2b = blas_stride(is2, sizeof(@ctype@)); if (is1b && is2b) { double sum[2] = {0., 0.}; /* double for stability */ while (n > 0) { - int chunk = n < NPY_CBLAS_CHUNK ? n : NPY_CBLAS_CHUNK; + CBLAS_INT chunk = n < NPY_CBLAS_CHUNK ? n : NPY_CBLAS_CHUNK; @type@ tmp[2]; - cblas_@prefix@dotu_sub((int)n, ip1, is1b, ip2, is2b, tmp); + CBLAS_FUNC(cblas_@prefix@dotu_sub)((CBLAS_INT)n, ip1, is1b, ip2, is2b, tmp); sum[0] += (double)tmp[0]; sum[1] += (double)tmp[1]; /* use char strides here */ @@ -3615,9 +3808,10 @@ OBJECT_dot(char *ip1, npy_intp is1, char *ip2, npy_intp is2, char *op, npy_intp #define BOOL_fill NULL /* this requires buffer to be filled with objects or NULL */ -static void +static int OBJECT_fill(PyObject **buffer, npy_intp length, void *NPY_UNUSED(ignored)) { + int retval = 0; npy_intp i; PyObject *start = buffer[0]; PyObject *delta = buffer[1]; @@ -3625,27 +3819,31 @@ OBJECT_fill(PyObject **buffer, npy_intp length, void *NPY_UNUSED(ignored)) delta = PyNumber_Subtract(delta, start); if (!delta) { - return; + return -1; } second = start = PyNumber_Add(start, delta); if (!start) { - goto finish; + goto error; } buffer += 2; for (i = 2; i < length; i++, buffer++) { start = PyNumber_Add(start, delta); if (!start) { - goto finish; + goto error; } Py_XDECREF(*buffer); *buffer = start; } + goto finish; + +error: + retval = -1; finish: Py_XDECREF(second); Py_DECREF(delta); - return; + return retval; } /**begin repeat @@ -3659,7 +3857,7 @@ finish: * npy_float, npy_double, npy_longdouble, * npy_datetime, npy_timedelta# */ -static void +static int @NAME@_fill(@type@ *buffer, npy_intp length, void *NPY_UNUSED(ignored)) { npy_intp i; @@ -3670,10 +3868,11 @@ static void for (i = 2; i < length; ++i) { buffer[i] = start + i*delta; } + return 0; } /**end repeat**/ -static void +static int HALF_fill(npy_half *buffer, npy_intp length, void *NPY_UNUSED(ignored)) { npy_intp i; @@ -3684,6 +3883,7 @@ HALF_fill(npy_half *buffer, npy_intp length, void *NPY_UNUSED(ignored)) for (i = 2; i < length; ++i) { buffer[i] = npy_float_to_half(start + i*delta); } + return 0; } /**begin repeat @@ -3691,7 +3891,7 @@ HALF_fill(npy_half *buffer, npy_intp length, void *NPY_UNUSED(ignored)) * #NAME = CFLOAT, CDOUBLE, CLONGDOUBLE# * #type = npy_cfloat, npy_cdouble, npy_clongdouble# */ -static void +static int @NAME@_fill(@type@ *buffer, npy_intp length, void *NPY_UNUSED(ignore)) { npy_intp i; @@ -3709,6 +3909,7 @@ static void buffer->real = start.real + i*delta.real; buffer->imag = start.imag + i*delta.imag; } + return 0; } /**end repeat**/ @@ -3766,342 +3967,6 @@ static void /**end repeat**/ -/* - ***************************************************************************** - ** FASTCLIP ** - ***************************************************************************** - */ - -#define _LESS_THAN(a, b) ((a) < (b)) -#define _GREATER_THAN(a, b) ((a) > (b)) - -/* - * In fastclip, 'b' was already checked for NaN, so the half comparison - * only needs to check 'a' for NaN. - */ - -#define _HALF_LESS_THAN(a, b) (!npy_half_isnan(a) && npy_half_lt_nonan(a, b)) -#define _HALF_GREATER_THAN(a, b) (!npy_half_isnan(a) && npy_half_lt_nonan(b, a)) - -/**begin repeat - * - * #name = BOOL, - * BYTE, UBYTE, SHORT, USHORT, INT, UINT, - * LONG, ULONG, LONGLONG, ULONGLONG, - * HALF, FLOAT, DOUBLE, LONGDOUBLE, - * DATETIME, TIMEDELTA# - * #type = npy_bool, - * npy_byte, npy_ubyte, npy_short, npy_ushort, npy_int, npy_uint, - * npy_long, npy_ulong, npy_longlong, npy_ulonglong, - * npy_half, npy_float, npy_double, npy_longdouble, - * npy_datetime, npy_timedelta# - * #isfloat = 0*11, 1*4, 0*2# - * #isnan = nop*11, npy_half_isnan, npy_isnan*3, nop*2# - * #lt = _LESS_THAN*11, _HALF_LESS_THAN, _LESS_THAN*5# - * #gt = _GREATER_THAN*11, _HALF_GREATER_THAN, _GREATER_THAN*5# - */ -static void -@name@_fastclip(@type@ *in, npy_intp ni, @type@ *min, @type@ *max, @type@ *out) -{ - npy_intp i; - @type@ max_val = 0, min_val = 0; - - if (max != NULL) { - max_val = *max; -#if @isfloat@ - /* NaNs result in no clipping, so optimize the case away */ - if (@isnan@(max_val)) { - if (min == NULL) { - memmove(out, in, ni * sizeof(@type@)); - return; - } - max = NULL; - } -#endif - } - if (min != NULL) { - min_val = *min; -#if @isfloat@ - if (@isnan@(min_val)) { - if (max == NULL) { - memmove(out, in, ni * sizeof(@type@)); - return; - } - min = NULL; - } -#endif - } - if (max == NULL) { - for (i = 0; i < ni; i++) { - if (@lt@(in[i], min_val)) { - out[i] = min_val; - } - else { - out[i] = in[i]; - } - } - } - else if (min == NULL) { - for (i = 0; i < ni; i++) { - if (@gt@(in[i], max_val)) { - out[i] = max_val; - } - else { - out[i] = in[i]; - } - } - } - else { - /* - * Visual Studio 2015 loop vectorizer handles NaN in an unexpected - * manner, see: https://github.com/numpy/numpy/issues/7601 - */ - #if (_MSC_VER == 1900) - #pragma loop( no_vector ) - #endif - for (i = 0; i < ni; i++) { - if (@lt@(in[i], min_val)) { - out[i] = min_val; - } - else if (@gt@(in[i], max_val)) { - out[i] = max_val; - } - else { - out[i] = in[i]; - } - } - } -} -/**end repeat**/ - -#undef _LESS_THAN -#undef _GREATER_THAN -#undef _HALF_LESS_THAN -#undef _HALF_GREATER_THAN - -/**begin repeat - * - * #name = CFLOAT, CDOUBLE, CLONGDOUBLE# - * #type = npy_cfloat, npy_cdouble, npy_clongdouble# - */ -static void -@name@_fastclip(@type@ *in, npy_intp ni, @type@ *min, @type@ *max, @type@ *out) -{ - npy_intp i; - @type@ max_val, min_val; - - if (max != NULL) { - max_val = *max; - } - if (min != NULL) { - min_val = *min; - } - if (max == NULL) { - for (i = 0; i < ni; i++) { - if (PyArray_CLT(in[i],min_val)) { - out[i] = min_val; - } - else { - out[i] = in[i]; - } - } - } - else if (min == NULL) { - for (i = 0; i < ni; i++) { - if (PyArray_CGT(in[i], max_val)) { - out[i] = max_val; - } - else { - out[i] = in[i]; - } - } - } - else { - for (i = 0; i < ni; i++) { - if (PyArray_CLT(in[i], min_val)) { - out[i] = min_val; - } - else if (PyArray_CGT(in[i], max_val)) { - out[i] = max_val; - } - else { - out[i] = in[i]; - } - } - } -} - -/**end repeat**/ - -#define OBJECT_fastclip NULL - - -/* - ***************************************************************************** - ** FASTPUTMASK ** - ***************************************************************************** - */ - - -/**begin repeat - * - * #name = BOOL, - * BYTE, UBYTE, SHORT, USHORT, INT, UINT, - * LONG, ULONG, LONGLONG, ULONGLONG, - * HALF, FLOAT, DOUBLE, LONGDOUBLE, - * CFLOAT, CDOUBLE, CLONGDOUBLE, - * DATETIME, TIMEDELTA# - * #type = npy_bool, npy_byte, npy_ubyte, npy_short, npy_ushort, npy_int, npy_uint, - * npy_long, npy_ulong, npy_longlong, npy_ulonglong, - * npy_half, npy_float, npy_double, npy_longdouble, - * npy_cfloat, npy_cdouble, npy_clongdouble, - * npy_datetime, npy_timedelta# -*/ -static void -@name@_fastputmask(@type@ *in, npy_bool *mask, npy_intp ni, @type@ *vals, - npy_intp nv) -{ - npy_intp i, j; - - if (nv == 1) { - @type@ s_val = *vals; - for (i = 0; i < ni; i++) { - if (mask[i]) { - in[i] = s_val; - } - } - } - else { - for (i = 0, j = 0; i < ni; i++, j++) { - if (j >= nv) { - j = 0; - } - if (mask[i]) { - in[i] = vals[j]; - } - } - } - return; -} -/**end repeat**/ - -#define OBJECT_fastputmask NULL - - -/* - ***************************************************************************** - ** FASTTAKE ** - ***************************************************************************** - */ - - -/**begin repeat - * - * #name = BOOL, - * BYTE, UBYTE, SHORT, USHORT, INT, UINT, - * LONG, ULONG, LONGLONG, ULONGLONG, - * HALF, FLOAT, DOUBLE, LONGDOUBLE, - * CFLOAT, CDOUBLE, CLONGDOUBLE, - * DATETIME, TIMEDELTA# - * #type = npy_bool, - * npy_byte, npy_ubyte, npy_short, npy_ushort, npy_int, npy_uint, - * npy_long, npy_ulong, npy_longlong, npy_ulonglong, - * npy_half, npy_float, npy_double, npy_longdouble, - * npy_cfloat, npy_cdouble, npy_clongdouble, - * npy_datetime, npy_timedelta# -*/ -static int -@name@_fasttake(@type@ *dest, @type@ *src, npy_intp *indarray, - npy_intp nindarray, npy_intp n_outer, - npy_intp m_middle, npy_intp nelem, - NPY_CLIPMODE clipmode) -{ - npy_intp i, j, k, tmp; - NPY_BEGIN_THREADS_DEF; - - NPY_BEGIN_THREADS; - - switch(clipmode) { - case NPY_RAISE: - for (i = 0; i < n_outer; i++) { - for (j = 0; j < m_middle; j++) { - tmp = indarray[j]; - /* - * We don't know what axis we're operating on, - * so don't report it in case of an error. - */ - if (check_and_adjust_index(&tmp, nindarray, -1, _save) < 0) { - return 1; - } - if (NPY_LIKELY(nelem == 1)) { - *dest++ = *(src + tmp); - } - else { - for (k = 0; k < nelem; k++) { - *dest++ = *(src + tmp*nelem + k); - } - } - } - src += nelem*nindarray; - } - break; - case NPY_WRAP: - for (i = 0; i < n_outer; i++) { - for (j = 0; j < m_middle; j++) { - tmp = indarray[j]; - if (tmp < 0) { - while (tmp < 0) { - tmp += nindarray; - } - } - else if (tmp >= nindarray) { - while (tmp >= nindarray) { - tmp -= nindarray; - } - } - if (NPY_LIKELY(nelem == 1)) { - *dest++ = *(src+tmp); - } - else { - for (k = 0; k < nelem; k++) { - *dest++ = *(src+tmp*nelem+k); - } - } - } - src += nelem*nindarray; - } - break; - case NPY_CLIP: - for (i = 0; i < n_outer; i++) { - for (j = 0; j < m_middle; j++) { - tmp = indarray[j]; - if (tmp < 0) { - tmp = 0; - } - else if (tmp >= nindarray) { - tmp = nindarray - 1; - } - if (NPY_LIKELY(nelem == 1)) { - *dest++ = *(src + tmp); - } - else { - for (k = 0; k < nelem; k++) { - *dest++ = *(src + tmp*nelem + k); - } - } - } - src += nelem*nindarray; - } - break; - } - - NPY_END_THREADS; - return 0; -} -/**end repeat**/ - -#define OBJECT_fasttake NULL - /* ***************************************************************************** ** small correlate ** @@ -4109,7 +3974,7 @@ static int */ /* - * Compute correlation of data with with small kernels + * Compute correlation of data with small kernels * Calling a BLAS dot product for the inner loop of the correlation is overkill * for small kernels. It is faster to compute it directly. * Intended to be used by _pyarray_correlate so no input verifications is done @@ -4187,6 +4052,53 @@ small_correlate(const char * d_, npy_intp dstride, } } +/* +*/ + +/* A clone function for the datetime dtype c_metadata */ +static NpyAuxData * +_datetime_dtype_metadata_clone(NpyAuxData *data) +{ + PyArray_DatetimeDTypeMetaData *newdata = + (PyArray_DatetimeDTypeMetaData *)PyArray_malloc( + sizeof(*newdata)); + if (newdata == NULL) { + PyErr_NoMemory(); + return NULL; + } + + memcpy(newdata, data, sizeof(*newdata)); + + return (NpyAuxData *)newdata; +} + +/* + * Allocate and initialize a PyArray_DatetimeDTypeMetaData object + */ +static NpyAuxData* +_create_datetime_metadata(NPY_DATETIMEUNIT base, int num) +{ + PyArray_DatetimeDTypeMetaData *data; + + /* Allocate memory for the metadata */ + data = PyArray_malloc(sizeof(*data)); + if (data == NULL) { + PyErr_NoMemory(); + return NULL; + } + + /* Initialize the base aux data */ + memset(data, 0, sizeof(PyArray_DatetimeDTypeMetaData)); + data->base.free = (NpyAuxData_FreeFunc *)PyArray_free; + data->base.clone = _datetime_dtype_metadata_clone; + + data->meta.base = base; + data->meta.num = num; + + return (NpyAuxData*)data; +} + + /* ***************************************************************************** ** SETUP FUNCTION POINTERS ** @@ -4243,12 +4155,12 @@ static PyArray_ArrFuncs _Py@NAME@_ArrFuncs = { { quicksort_@suff@, heapsort_@suff@, - mergesort_@suff@ + timsort_@suff@ }, { aquicksort_@suff@, aheapsort_@suff@, - amergesort_@suff@ + atimsort_@suff@ }, #else { @@ -4322,13 +4234,13 @@ static PyArray_Descr @from@_Descr = { * cfloat, cdouble, clongdouble, * object, datetime, timedelta# * #sort = 1*18, 0*1, 1*2# - * #num = 1*15, 2*3, 1*3# * #fromtype = npy_bool, * npy_byte, npy_ubyte, npy_short, npy_ushort, npy_int, npy_uint, * npy_long, npy_ulong, npy_longlong, npy_ulonglong, * npy_half, npy_float, npy_double, npy_longdouble, - * npy_float, npy_double, npy_longdouble, + * npy_cfloat, npy_cdouble, npy_clongdouble, * PyObject *, npy_datetime, npy_timedelta# + * #rsort = 1*5, 0*16# * #NAME = Bool, * Byte, UByte, Short, UShort, Int, UInt, * Long, ULong, LongLong, ULongLong, @@ -4385,12 +4297,20 @@ static PyArray_ArrFuncs _Py@NAME@_ArrFuncs = { { quicksort_@suff@, heapsort_@suff@, - mergesort_@suff@ + #if @rsort@ + radixsort_@suff@ + #else + timsort_@suff@ + #endif }, { aquicksort_@suff@, aheapsort_@suff@, - amergesort_@suff@ + #if @rsort@ + aradixsort_@suff@ + #else + atimsort_@suff@ + #endif }, #else { @@ -4404,9 +4324,9 @@ static PyArray_ArrFuncs _Py@NAME@_ArrFuncs = { (PyArray_ScalarKindFunc*)NULL, NULL, NULL, - (PyArray_FastClipFunc*)@from@_fastclip, - (PyArray_FastPutmaskFunc*)@from@_fastputmask, - (PyArray_FastTakeFunc*)@from@_fasttake, + (PyArray_FastClipFunc*)NULL, + (PyArray_FastPutmaskFunc*)NULL, + (PyArray_FastTakeFunc*)NULL, (PyArray_ArgFunc*)@from@_argmin }; @@ -4428,10 +4348,9 @@ NPY_NO_EXPORT PyArray_Descr @from@_Descr = { /* type_num */ NPY_@from@, /* elsize */ - @num@ * sizeof(@fromtype@), + sizeof(@fromtype@), /* alignment */ - @num@ * _ALIGN(@fromtype@) > NPY_MAX_COPY_ALIGNMENT ? - NPY_MAX_COPY_ALIGNMENT : @num@ * _ALIGN(@fromtype@), + _ALIGN(@fromtype@), /* subarray */ NULL, /* fields */ @@ -4488,7 +4407,17 @@ PyArray_DescrFromType(int type) { PyArray_Descr *ret = NULL; - if (type < NPY_NTYPES) { + if (type < 0) { + /* + * It's not valid for type to be less than 0. + * If that happens, then no other branch of + * this if/else chain should be followed. + * This is effectively a no-op that ensures + * the default error is raised. + */ + ret = NULL; + } + else if (type < NPY_NTYPES) { ret = _builtin_descrs[type]; } else if (type == NPY_NOTYPE) { @@ -4545,66 +4474,6 @@ PyArray_DescrFromType(int type) return ret; } -/* A clone function for the datetime dtype metadata */ -static NpyAuxData * -datetime_dtype_metadata_clone(NpyAuxData *data) -{ - PyArray_DatetimeDTypeMetaData *newdata = - (PyArray_DatetimeDTypeMetaData *)PyArray_malloc( - sizeof(PyArray_DatetimeDTypeMetaData)); - if (newdata == NULL) { - return NULL; - } - - memcpy(newdata, data, sizeof(PyArray_DatetimeDTypeMetaData)); - - return (NpyAuxData *)newdata; -} - -/* - * Initializes the c_metadata field for the _builtin_descrs DATETIME - * and TIMEDELTA. - * - * must not be static, gcc 4.1.2 on redhat 5 then miscompiles this function - * see gh-5163 - * - */ -NPY_NO_EXPORT int -initialize_builtin_datetime_metadata(void) -{ - PyArray_DatetimeDTypeMetaData *data1, *data2; - - /* Allocate memory for the metadata */ - data1 = PyArray_malloc(sizeof(PyArray_DatetimeDTypeMetaData)); - if (data1 == NULL) { - return -1; - } - data2 = PyArray_malloc(sizeof(PyArray_DatetimeDTypeMetaData)); - if (data2 == NULL) { - PyArray_free(data1); - return -1; - } - - /* Initialize the base aux data */ - memset(data1, 0, sizeof(PyArray_DatetimeDTypeMetaData)); - memset(data2, 0, sizeof(PyArray_DatetimeDTypeMetaData)); - data1->base.free = (NpyAuxData_FreeFunc *)PyArray_free; - data2->base.free = (NpyAuxData_FreeFunc *)PyArray_free; - data1->base.clone = datetime_dtype_metadata_clone; - data2->base.clone = datetime_dtype_metadata_clone; - - /* Set to the default metadata */ - data1->meta.base = NPY_DATETIME_DEFAULTUNIT; - data1->meta.num = 1; - data2->meta.base = NPY_DATETIME_DEFAULTUNIT; - data2->meta.num = 1; - - _builtin_descrs[NPY_DATETIME]->c_metadata = (NpyAuxData *)data1; - _builtin_descrs[NPY_TIMEDELTA]->c_metadata = (NpyAuxData *)data2; - - return 0; -} - /* ***************************************************************************** ** SETUP TYPE INFO ** @@ -4625,6 +4494,17 @@ set_typeinfo(PyObject *dict) PyArray_Descr *dtype; PyObject *cobj, *key; + /* + * Override the base class for all types, eventually all of this logic + * should be defined on the class and inherited to the scalar. + * (NPY_HALF is the largest builtin one.) + */ + for (i = 0; i <= NPY_HALF; i++) { + if (dtypemeta_wrap_legacy_descriptor(_builtin_descrs[i]) < 0) { + return -1; + } + } + /* * Add cast functions for the new types */ @@ -4652,7 +4532,7 @@ set_typeinfo(PyObject *dict) return -1; } } - key = PyInt_FromLong(NPY_@name2@); + key = PyLong_FromLong(NPY_@name2@); if (key == NULL) { return -1; } @@ -4673,7 +4553,14 @@ set_typeinfo(PyObject *dict) /**end repeat**/ - if (initialize_builtin_datetime_metadata() < 0) { + _builtin_descrs[NPY_DATETIME]->c_metadata = _create_datetime_metadata( + NPY_DATETIME_DEFAULTUNIT, 1); + if (_builtin_descrs[NPY_DATETIME]->c_metadata == NULL) { + return -1; + } + _builtin_descrs[NPY_TIMEDELTA]->c_metadata = _create_datetime_metadata( + NPY_DATETIME_DEFAULTUNIT, 1); + if (_builtin_descrs[NPY_DATETIME]->c_metadata == NULL) { return -1; } @@ -4726,7 +4613,7 @@ set_typeinfo(PyObject *dict) infodict = PyDict_New(); if (infodict == NULL) return -1; - + int ret; /**begin repeat * * #name = BOOL, @@ -4769,10 +4656,15 @@ set_typeinfo(PyObject *dict) &Py@Name@ArrType_Type ); if (s == NULL) { + Py_DECREF(infodict); return -1; } - PyDict_SetItemString(infodict, "@name@", s); + ret = PyDict_SetItemString(infodict, "@name@", s); Py_DECREF(s); + if (ret < 0) { + Py_DECREF(infodict); + return -1; + } /**end repeat**/ @@ -4786,19 +4678,21 @@ set_typeinfo(PyObject *dict) * CFLOAT, CDOUBLE, CLONGDOUBLE# * #Name = Half, Float, Double, LongDouble, * CFloat, CDouble, CLongDouble# - * #num = 1, 1, 1, 1, 2, 2, 2# */ s = PyArray_typeinfo( NPY_@name@LTR, NPY_@name@, NPY_BITSOF_@name@, - @num@ * _ALIGN(@type@) > NPY_MAX_COPY_ALIGNMENT ? - NPY_MAX_COPY_ALIGNMENT : @num@ * _ALIGN(@type@), - &Py@Name@ArrType_Type + _ALIGN(@type@), &Py@Name@ArrType_Type ); if (s == NULL) { + Py_DECREF(infodict); return -1; } - PyDict_SetItemString(infodict, "@name@", s); + ret = PyDict_SetItemString(infodict, "@name@", s); Py_DECREF(s); + if (ret < 0) { + Py_DECREF(infodict); + return -1; + } /**end repeat**/ @@ -4808,37 +4702,57 @@ set_typeinfo(PyObject *dict) &PyObjectArrType_Type ); if (s == NULL) { + Py_DECREF(infodict); return -1; } - PyDict_SetItemString(infodict, "OBJECT", s); + ret = PyDict_SetItemString(infodict, "OBJECT", s); Py_DECREF(s); + if (ret < 0) { + Py_DECREF(infodict); + return -1; + } s = PyArray_typeinfo( NPY_STRINGLTR, NPY_STRING, 0, _ALIGN(char), &PyStringArrType_Type ); if (s == NULL) { + Py_DECREF(infodict); return -1; } - PyDict_SetItemString(infodict, "STRING", s); + ret = PyDict_SetItemString(infodict, "STRING", s); Py_DECREF(s); + if (ret < 0) { + Py_DECREF(infodict); + return -1; + } s = PyArray_typeinfo( NPY_UNICODELTR, NPY_UNICODE, 0, _ALIGN(npy_ucs4), &PyUnicodeArrType_Type ); if (s == NULL) { + Py_DECREF(infodict); return -1; } - PyDict_SetItemString(infodict, "UNICODE", s); + ret = PyDict_SetItemString(infodict, "UNICODE", s); Py_DECREF(s); + if (ret < 0) { + Py_DECREF(infodict); + return -1; + } s = PyArray_typeinfo( NPY_VOIDLTR, NPY_VOID, 0, _ALIGN(char), &PyVoidArrType_Type ); if (s == NULL) { + Py_DECREF(infodict); return -1; } - PyDict_SetItemString(infodict, "VOID", s); + ret = PyDict_SetItemString(infodict, "VOID", s); Py_DECREF(s); + if (ret < 0) { + Py_DECREF(infodict); + return -1; + } s = PyArray_typeinforanged( NPY_DATETIMELTR, NPY_DATETIME, NPY_BITSOF_DATETIME, _ALIGN(npy_datetime), @@ -4847,10 +4761,15 @@ set_typeinfo(PyObject *dict) &PyDatetimeArrType_Type ); if (s == NULL) { + Py_DECREF(infodict); return -1; } - PyDict_SetItemString(infodict, "DATETIME", s); + ret = PyDict_SetItemString(infodict, "DATETIME", s); Py_DECREF(s); + if (ret < 0) { + Py_DECREF(infodict); + return -1; + } s = PyArray_typeinforanged( NPY_TIMEDELTALTR, NPY_TIMEDELTA, NPY_BITSOF_TIMEDELTA, _ALIGN(npy_timedelta), @@ -4859,15 +4778,23 @@ set_typeinfo(PyObject *dict) &PyTimedeltaArrType_Type ); if (s == NULL) { + Py_DECREF(infodict); return -1; } - PyDict_SetItemString(infodict, "TIMEDELTA", s); + ret = PyDict_SetItemString(infodict, "TIMEDELTA", s); Py_DECREF(s); + if (ret < 0) { + Py_DECREF(infodict); + return -1; + } -#define SETTYPE(name) \ - Py_INCREF(&Py##name##ArrType_Type); \ - PyDict_SetItemString(infodict, #name, \ - (PyObject *)&Py##name##ArrType_Type) +#define SETTYPE(name) \ + Py_INCREF(&Py##name##ArrType_Type); \ + if (PyDict_SetItemString(infodict, #name, \ + (PyObject *)&Py##name##ArrType_Type) < 0) { \ + Py_DECREF(infodict); \ + return -1; \ + } SETTYPE(Generic); SETTYPE(Number); @@ -4882,8 +4809,11 @@ set_typeinfo(PyObject *dict) #undef SETTYPE - PyDict_SetItemString(dict, "typeinfo", infodict); + ret = PyDict_SetItemString(dict, "typeinfo", infodict); Py_DECREF(infodict); + if (ret < 0) { + return -1; + } return 0; } diff --git a/numpy/core/src/multiarray/arraytypes.h b/numpy/core/src/multiarray/arraytypes.h index a9469aef737d..b3a13b297da1 100644 --- a/numpy/core/src/multiarray/arraytypes.h +++ b/numpy/core/src/multiarray/arraytypes.h @@ -1,5 +1,5 @@ -#ifndef _NPY_ARRAYTYPES_H_ -#define _NPY_ARRAYTYPES_H_ +#ifndef NUMPY_CORE_SRC_MULTIARRAY_ARRAYTYPES_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_ARRAYTYPES_H_ #include "common.h" @@ -28,4 +28,4 @@ small_correlate(const char * d_, npy_intp dstride, npy_intp nk, enum NPY_TYPES ktype, char * out_, npy_intp ostride); -#endif +#endif /* NUMPY_CORE_SRC_MULTIARRAY_ARRAYTYPES_H_ */ diff --git a/numpy/core/src/multiarray/buffer.c b/numpy/core/src/multiarray/buffer.c index 21dbdefd6f16..d10122c4f190 100644 --- a/numpy/core/src/multiarray/buffer.c +++ b/numpy/core/src/multiarray/buffer.c @@ -1,9 +1,10 @@ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE + #define PY_SSIZE_T_CLEAN #include <Python.h> -#include "structmember.h" +#include <structmember.h> -#define NPY_NO_DEPRECATED_API NPY_API_VERSION -#define _MULTIARRAYMODULE #include "numpy/arrayobject.h" #include "numpy/arrayscalars.h" @@ -11,7 +12,7 @@ #include "npy_pycompat.h" -#include "buffer.h" +#include "npy_buffer.h" #include "common.h" #include "numpyos.h" #include "arrayobject.h" @@ -21,59 +22,6 @@ **************** Implement Buffer Protocol **************************** *************************************************************************/ -/* removed multiple segment interface */ - -#if !defined(NPY_PY3K) -static Py_ssize_t -array_getsegcount(PyArrayObject *self, Py_ssize_t *lenp) -{ - if (lenp) { - *lenp = PyArray_NBYTES(self); - } - if (PyArray_ISONESEGMENT(self)) { - return 1; - } - if (lenp) { - *lenp = 0; - } - return 0; -} - -static Py_ssize_t -array_getreadbuf(PyArrayObject *self, Py_ssize_t segment, void **ptrptr) -{ - if (segment != 0) { - PyErr_SetString(PyExc_ValueError, - "accessing non-existing array segment"); - return -1; - } - if (PyArray_ISONESEGMENT(self)) { - *ptrptr = PyArray_DATA(self); - return PyArray_NBYTES(self); - } - PyErr_SetString(PyExc_ValueError, "array is not a single segment"); - *ptrptr = NULL; - return -1; -} - - -static Py_ssize_t -array_getwritebuf(PyArrayObject *self, Py_ssize_t segment, void **ptrptr) -{ - if (PyArray_FailUnlessWriteable(self, "buffer source array") < 0) { - return -1; - } - return array_getreadbuf(self, segment, (void **) ptrptr); -} - -static Py_ssize_t -array_getcharbuf(PyArrayObject *self, Py_ssize_t segment, constchar **ptrptr) -{ - return array_getreadbuf(self, segment, (void **) ptrptr); -} -#endif /* !defined(NPY_PY3K) */ - - /************************************************************************* * PEP 3118 buffer protocol * @@ -117,7 +65,7 @@ _append_char(_tmp_string_t *s, char c) char *p; size_t to_alloc = (s->allocated == 0) ? INIT_SIZE : (2 * s->allocated); - p = realloc(s->s, to_alloc); + p = PyObject_Realloc(s->s, to_alloc); if (p == NULL) { PyErr_SetString(PyExc_MemoryError, "memory allocation failed"); return -1; @@ -134,24 +82,79 @@ static int _append_str(_tmp_string_t *s, char const *p) { for (; *p != '\0'; p++) { - if (_append_char(s, *p) != 0) { + if (_append_char(s, *p) < 0) { return -1; } } return 0; } +/* + * Append a PEP3118-formatted field name, ":name:", to str + */ +static int +_append_field_name(_tmp_string_t *str, PyObject *name) +{ + int ret = -1; + char *p; + Py_ssize_t len; + PyObject *tmp; + /* FIXME: XXX -- should it use UTF-8 here? */ + tmp = PyUnicode_AsUTF8String(name); + if (tmp == NULL || PyBytes_AsStringAndSize(tmp, &p, &len) < 0) { + PyErr_Clear(); + PyErr_SetString(PyExc_ValueError, "invalid field name"); + goto fail; + } + if (_append_char(str, ':') < 0) { + goto fail; + } + while (len > 0) { + if (*p == ':') { + PyErr_SetString(PyExc_ValueError, + "':' is not an allowed character in buffer " + "field names"); + goto fail; + } + if (_append_char(str, *p) < 0) { + goto fail; + } + ++p; + --len; + } + if (_append_char(str, ':') < 0) { + goto fail; + } + ret = 0; +fail: + Py_XDECREF(tmp); + return ret; +} + /* * Return non-zero if a type is aligned in each item in the given array, * AND, the descr element size is a multiple of the alignment, * AND, the array data is positioned to alignment granularity. */ -static int +static NPY_INLINE int _is_natively_aligned_at(PyArray_Descr *descr, PyArrayObject *arr, Py_ssize_t offset) { int k; + if (NPY_LIKELY(descr == PyArray_DESCR(arr))) { + /* + * If the descriptor is the arrays descriptor we can assume the + * array's alignment is correct. + */ + assert(offset == 0); + if (PyArray_ISALIGNED(arr)) { + assert(descr->elsize % descr->alignment == 0); + return 1; + } + return 0; + } + if ((Py_ssize_t)(PyArray_DATA(arr)) % descr->alignment != 0) { return 0; } @@ -175,6 +178,14 @@ _is_natively_aligned_at(PyArray_Descr *descr, return 1; } +/* + * Fill in str with an appropriate PEP 3118 format string, based on + * descr. For structured dtypes, calls itself recursively. Each call extends + * str at offset then updates offset, and uses descr->byteorder, (and + * possibly the byte order in obj) to determine the byte-order char. + * + * Returns 0 for success, -1 for failure + */ static int _buffer_format_string(PyArray_Descr *descr, _tmp_string_t *str, PyObject* obj, Py_ssize_t *offset, @@ -195,8 +206,8 @@ _buffer_format_string(PyArray_Descr *descr, _tmp_string_t *str, PyObject *item, *subarray_tuple; Py_ssize_t total_count = 1; Py_ssize_t dim_size; + Py_ssize_t old_offset; char buf[128]; - int old_offset; int ret; if (PyTuple_Check(descr->subarray->shape)) { @@ -207,45 +218,57 @@ _buffer_format_string(PyArray_Descr *descr, _tmp_string_t *str, subarray_tuple = Py_BuildValue("(O)", descr->subarray->shape); } - _append_char(str, '('); + if (_append_char(str, '(') < 0) { + ret = -1; + goto subarray_fail; + } for (k = 0; k < PyTuple_GET_SIZE(subarray_tuple); ++k) { if (k > 0) { - _append_char(str, ','); + if (_append_char(str, ',') < 0) { + ret = -1; + goto subarray_fail; + } } item = PyTuple_GET_ITEM(subarray_tuple, k); dim_size = PyNumber_AsSsize_t(item, NULL); PyOS_snprintf(buf, sizeof(buf), "%ld", (long)dim_size); - _append_str(str, buf); + if (_append_str(str, buf) < 0) { + ret = -1; + goto subarray_fail; + } total_count *= dim_size; } - _append_char(str, ')'); - - Py_DECREF(subarray_tuple); + if (_append_char(str, ')') < 0) { + ret = -1; + goto subarray_fail; + } old_offset = *offset; ret = _buffer_format_string(descr->subarray->base, str, obj, offset, active_byteorder); *offset = old_offset + (*offset - old_offset) * total_count; + + subarray_fail: + Py_DECREF(subarray_tuple); return ret; } else if (PyDataType_HASFIELDS(descr)) { - int base_offset = *offset; + Py_ssize_t base_offset = *offset; - _append_str(str, "T{"); + if (_append_str(str, "T{") < 0) return -1; for (k = 0; k < PyTuple_GET_SIZE(descr->names); ++k) { - PyObject *name, *item, *offset_obj, *tmp; + PyObject *name, *item, *offset_obj; PyArray_Descr *child; - char *p; - Py_ssize_t len; - int new_offset; + Py_ssize_t new_offset; + int ret; name = PyTuple_GET_ITEM(descr->names, k); item = PyDict_GetItem(descr->fields, name); child = (PyArray_Descr*)PyTuple_GetItem(item, 0); offset_obj = PyTuple_GetItem(item, 1); - new_offset = PyInt_AsLong(offset_obj); + new_offset = PyLong_AsLong(offset_obj); if (error_converting(new_offset)) { return -1; } @@ -261,45 +284,21 @@ _buffer_format_string(PyArray_Descr *descr, _tmp_string_t *str, return -1; } while (*offset < new_offset) { - _append_char(str, 'x'); + if (_append_char(str, 'x') < 0) return -1; ++*offset; } /* Insert child item */ - _buffer_format_string(child, str, obj, offset, + ret = _buffer_format_string(child, str, obj, offset, active_byteorder); - - /* Insert field name */ -#if defined(NPY_PY3K) - /* FIXME: XXX -- should it use UTF-8 here? */ - tmp = PyUnicode_AsUTF8String(name); -#else - tmp = name; -#endif - if (tmp == NULL || PyBytes_AsStringAndSize(tmp, &p, &len) < 0) { - PyErr_Clear(); - PyErr_SetString(PyExc_ValueError, "invalid field name"); + if (ret < 0) { return -1; } - _append_char(str, ':'); - while (len > 0) { - if (*p == ':') { - Py_DECREF(tmp); - PyErr_SetString(PyExc_ValueError, - "':' is not an allowed character in buffer " - "field names"); - return -1; - } - _append_char(str, *p); - ++p; - --len; - } - _append_char(str, ':'); -#if defined(NPY_PY3K) - Py_DECREF(tmp); -#endif + + /* Insert field name */ + if (_append_field_name(str, name) < 0) return -1; } - _append_char(str, '}'); + if (_append_char(str, '}') < 0) return -1; } else { int is_standard_size = 1; @@ -312,8 +311,6 @@ _buffer_format_string(PyArray_Descr *descr, _tmp_string_t *str, descr->type_num == NPY_ULONGLONG); } - *offset += descr->elsize; - if (PyArray_IsScalar(obj, Generic)) { /* scalars are always natively aligned */ is_natively_aligned = 1; @@ -323,11 +320,13 @@ _buffer_format_string(PyArray_Descr *descr, _tmp_string_t *str, (PyArrayObject*)obj, *offset); } + *offset += descr->elsize; + if (descr->byteorder == '=' && is_natively_aligned) { /* Prefer native types, to cater for Cython */ is_standard_size = 0; if (*active_byteorder != '@') { - _append_char(str, '@'); + if (_append_char(str, '@') < 0) return -1; *active_byteorder = '@'; } } @@ -335,7 +334,7 @@ _buffer_format_string(PyArray_Descr *descr, _tmp_string_t *str, /* Data types that have no standard size */ is_standard_size = 0; if (*active_byteorder != '^') { - _append_char(str, '^'); + if (_append_char(str, '^') < 0) return -1; *active_byteorder = '^'; } } @@ -343,7 +342,7 @@ _buffer_format_string(PyArray_Descr *descr, _tmp_string_t *str, descr->byteorder == '=') { is_standard_size = 1; if (*active_byteorder != descr->byteorder) { - _append_char(str, descr->byteorder); + if (_append_char(str, descr->byteorder) < 0) return -1; *active_byteorder = descr->byteorder; } @@ -361,45 +360,45 @@ _buffer_format_string(PyArray_Descr *descr, _tmp_string_t *str, } switch (descr->type_num) { - case NPY_BOOL: if (_append_char(str, '?')) return -1; break; - case NPY_BYTE: if (_append_char(str, 'b')) return -1; break; - case NPY_UBYTE: if (_append_char(str, 'B')) return -1; break; - case NPY_SHORT: if (_append_char(str, 'h')) return -1; break; - case NPY_USHORT: if (_append_char(str, 'H')) return -1; break; - case NPY_INT: if (_append_char(str, 'i')) return -1; break; - case NPY_UINT: if (_append_char(str, 'I')) return -1; break; + case NPY_BOOL: if (_append_char(str, '?') < 0) return -1; break; + case NPY_BYTE: if (_append_char(str, 'b') < 0) return -1; break; + case NPY_UBYTE: if (_append_char(str, 'B') < 0) return -1; break; + case NPY_SHORT: if (_append_char(str, 'h') < 0) return -1; break; + case NPY_USHORT: if (_append_char(str, 'H') < 0) return -1; break; + case NPY_INT: if (_append_char(str, 'i') < 0) return -1; break; + case NPY_UINT: if (_append_char(str, 'I') < 0) return -1; break; case NPY_LONG: if (is_standard_size && (NPY_SIZEOF_LONG == 8)) { - if (_append_char(str, 'q')) return -1; + if (_append_char(str, 'q') < 0) return -1; } else { - if (_append_char(str, 'l')) return -1; + if (_append_char(str, 'l') < 0) return -1; } break; case NPY_ULONG: if (is_standard_size && (NPY_SIZEOF_LONG == 8)) { - if (_append_char(str, 'Q')) return -1; + if (_append_char(str, 'Q') < 0) return -1; } else { - if (_append_char(str, 'L')) return -1; + if (_append_char(str, 'L') < 0) return -1; } break; - case NPY_LONGLONG: if (_append_char(str, 'q')) return -1; break; - case NPY_ULONGLONG: if (_append_char(str, 'Q')) return -1; break; - case NPY_HALF: if (_append_char(str, 'e')) return -1; break; - case NPY_FLOAT: if (_append_char(str, 'f')) return -1; break; - case NPY_DOUBLE: if (_append_char(str, 'd')) return -1; break; - case NPY_LONGDOUBLE: if (_append_char(str, 'g')) return -1; break; - case NPY_CFLOAT: if (_append_str(str, "Zf")) return -1; break; - case NPY_CDOUBLE: if (_append_str(str, "Zd")) return -1; break; - case NPY_CLONGDOUBLE: if (_append_str(str, "Zg")) return -1; break; - /* XXX: datetime */ - /* XXX: timedelta */ - case NPY_OBJECT: if (_append_char(str, 'O')) return -1; break; + case NPY_LONGLONG: if (_append_char(str, 'q') < 0) return -1; break; + case NPY_ULONGLONG: if (_append_char(str, 'Q') < 0) return -1; break; + case NPY_HALF: if (_append_char(str, 'e') < 0) return -1; break; + case NPY_FLOAT: if (_append_char(str, 'f') < 0) return -1; break; + case NPY_DOUBLE: if (_append_char(str, 'd') < 0) return -1; break; + case NPY_LONGDOUBLE: if (_append_char(str, 'g') < 0) return -1; break; + case NPY_CFLOAT: if (_append_str(str, "Zf") < 0) return -1; break; + case NPY_CDOUBLE: if (_append_str(str, "Zd") < 0) return -1; break; + case NPY_CLONGDOUBLE: if (_append_str(str, "Zg") < 0) return -1; break; + /* XXX NPY_DATETIME */ + /* XXX NPY_TIMEDELTA */ + case NPY_OBJECT: if (_append_char(str, 'O') < 0) return -1; break; case NPY_STRING: { char buf[128]; PyOS_snprintf(buf, sizeof(buf), "%ds", descr->elsize); - if (_append_str(str, buf)) return -1; + if (_append_str(str, buf) < 0) return -1; break; } case NPY_UNICODE: { @@ -407,14 +406,14 @@ _buffer_format_string(PyArray_Descr *descr, _tmp_string_t *str, char buf[128]; assert(descr->elsize % 4 == 0); PyOS_snprintf(buf, sizeof(buf), "%dw", descr->elsize / 4); - if (_append_str(str, buf)) return -1; + if (_append_str(str, buf) < 0) return -1; break; } case NPY_VOID: { /* Insert padding bytes */ char buf[128]; PyOS_snprintf(buf, sizeof(buf), "%dx", descr->elsize); - if (_append_str(str, buf)) return -1; + if (_append_str(str, buf) < 0) return -1; break; } default: @@ -430,59 +429,63 @@ _buffer_format_string(PyArray_Descr *descr, _tmp_string_t *str, /* - * Global information about all active buffers + * Information about all active buffers is stored as a linked list on + * the ndarray. The initial pointer is currently tagged to have a chance of + * detecting incompatible subclasses. * * Note: because for backward compatibility we cannot define bf_releasebuffer, * we must manually keep track of the additional data required by the buffers. */ /* Additional per-array data required for providing the buffer interface */ -typedef struct { +typedef struct _buffer_info_t_tag { char *format; int ndim; Py_ssize_t *strides; Py_ssize_t *shape; + struct _buffer_info_t_tag *next; } _buffer_info_t; -/* - * { id(array): [list of pointers to _buffer_info_t, the last one is latest] } - * - * Because shape, strides, and format can be different for different buffers, - * we may need to keep track of multiple buffer infos for each array. - * - * However, when none of them has changed, the same buffer info may be reused. - * - * Thread-safety is provided by GIL. - */ -static PyObject *_buffer_info_cache = NULL; /* Fill in the info structure */ static _buffer_info_t* -_buffer_info_new(PyObject *obj) +_buffer_info_new(PyObject *obj, int flags) { + /* + * Note that the buffer info is cached as PyLongObjects making them appear + * like unreachable lost memory to valgrind. + */ _buffer_info_t *info; _tmp_string_t fmt = {NULL, 0, 0}; int k; PyArray_Descr *descr = NULL; int err = 0; - info = malloc(sizeof(_buffer_info_t)); - if (info == NULL) { - goto fail; - } - - if (PyArray_IsScalar(obj, Generic)) { - descr = PyArray_DescrFromScalar(obj); - if (descr == NULL) { + if (PyArray_IsScalar(obj, Void)) { + info = PyObject_Malloc(sizeof(_buffer_info_t)); + if (info == NULL) { + PyErr_NoMemory(); goto fail; } info->ndim = 0; info->shape = NULL; info->strides = NULL; + + descr = PyArray_DescrFromScalar(obj); + if (descr == NULL) { + goto fail; + } } else { + assert(PyArray_Check(obj)); PyArrayObject * arr = (PyArrayObject *)obj; - descr = PyArray_DESCR(arr); + + info = PyObject_Malloc(sizeof(_buffer_info_t) + + sizeof(Py_ssize_t) * PyArray_NDIM(arr) * 2); + if (info == NULL) { + PyErr_NoMemory(); + goto fail; + } /* Fill in shape and strides */ info->ndim = PyArray_NDIM(arr); @@ -491,33 +494,75 @@ _buffer_info_new(PyObject *obj) info->strides = NULL; } else { - info->shape = malloc(sizeof(Py_ssize_t) * PyArray_NDIM(arr) * 2 + 1); - if (info->shape == NULL) { - goto fail; - } + info->shape = (npy_intp *)((char *)info + sizeof(_buffer_info_t)); + assert((size_t)info->shape % sizeof(npy_intp) == 0); info->strides = info->shape + PyArray_NDIM(arr); - for (k = 0; k < PyArray_NDIM(arr); ++k) { - info->shape[k] = PyArray_DIMS(arr)[k]; - info->strides[k] = PyArray_STRIDES(arr)[k]; + +#if NPY_RELAXED_STRIDES_CHECKING + /* + * When NPY_RELAXED_STRIDES_CHECKING is used, some buffer users + * may expect a contiguous buffer to have well formatted strides + * also when a dimension is 1, but we do not guarantee this + * internally. Thus, recalculate strides for contiguous arrays. + * (This is unnecessary, but has no effect in the case where + * NPY_RELAXED_STRIDES CHECKING is disabled.) + */ + int f_contiguous = (flags & PyBUF_F_CONTIGUOUS) == PyBUF_F_CONTIGUOUS; + if (PyArray_IS_C_CONTIGUOUS(arr) && !( + f_contiguous && PyArray_IS_F_CONTIGUOUS(arr))) { + Py_ssize_t sd = PyArray_ITEMSIZE(arr); + for (k = info->ndim-1; k >= 0; --k) { + info->shape[k] = PyArray_DIMS(arr)[k]; + info->strides[k] = sd; + sd *= info->shape[k]; + } + } + else if (PyArray_IS_F_CONTIGUOUS(arr)) { + Py_ssize_t sd = PyArray_ITEMSIZE(arr); + for (k = 0; k < info->ndim; ++k) { + info->shape[k] = PyArray_DIMS(arr)[k]; + info->strides[k] = sd; + sd *= info->shape[k]; + } + } + else { +#else /* NPY_RELAXED_STRIDES_CHECKING */ + /* We can always use the arrays strides directly */ + { +#endif + + for (k = 0; k < PyArray_NDIM(arr); ++k) { + info->shape[k] = PyArray_DIMS(arr)[k]; + info->strides[k] = PyArray_STRIDES(arr)[k]; + } } } + descr = PyArray_DESCR(arr); Py_INCREF(descr); } /* Fill in format */ - err = _buffer_format_string(descr, &fmt, obj, NULL, NULL); - Py_DECREF(descr); - if (err != 0) { - free(fmt.s); - goto fail; + if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) { + err = _buffer_format_string(descr, &fmt, obj, NULL, NULL); + Py_DECREF(descr); + if (err != 0) { + goto fail; + } + if (_append_char(&fmt, '\0') < 0) { + goto fail; + } + info->format = fmt.s; } - _append_char(&fmt, '\0'); - info->format = fmt.s; - + else { + Py_DECREF(descr); + info->format = NULL; + } + info->next = NULL; return info; fail: - free(info); + PyObject_Free(fmt.s); + PyObject_Free(info); return NULL; } @@ -528,9 +573,10 @@ _buffer_info_cmp(_buffer_info_t *a, _buffer_info_t *b) Py_ssize_t c; int k; - c = strcmp(a->format, b->format); - if (c != 0) return c; - + if (a->format != NULL && b->format != NULL) { + c = strcmp(a->format, b->format); + if (c != 0) return c; + } c = a->ndim - b->ndim; if (c != 0) return c; @@ -544,116 +590,161 @@ _buffer_info_cmp(_buffer_info_t *a, _buffer_info_t *b) return 0; } -static void -_buffer_info_free(_buffer_info_t *info) + +/* + * Tag the buffer info pointer by adding 2 (unless it is NULL to simplify + * object initialization). + * The linked list of buffer-infos was appended to the array struct in + * NumPy 1.20. Tagging the pointer gives us a chance to raise/print + * a useful error message instead of crashing hard if a C-subclass uses + * the same field. + */ +static NPY_INLINE void * +buffer_info_tag(void *buffer_info) { - if (info->format) { - free(info->format); + if (buffer_info == NULL) { + return buffer_info; } - if (info->shape) { - free(info->shape); + else { + return (void *)((uintptr_t)buffer_info + 3); } - free(info); } -/* Get buffer info from the global dictionary */ -static _buffer_info_t* -_buffer_get_info(PyObject *obj) -{ - PyObject *key = NULL, *item_list = NULL, *item = NULL; - _buffer_info_t *info = NULL, *old_info = NULL; - - if (_buffer_info_cache == NULL) { - _buffer_info_cache = PyDict_New(); - if (_buffer_info_cache == NULL) { - return NULL; - } - } - /* Compute information */ - info = _buffer_info_new(obj); - if (info == NULL) { - return NULL; +static NPY_INLINE int +_buffer_info_untag( + void *tagged_buffer_info, _buffer_info_t **buffer_info, PyObject *obj) +{ + if (tagged_buffer_info == NULL) { + *buffer_info = NULL; + return 0; } - - /* Check if it is identical with an old one; reuse old one, if yes */ - key = PyLong_FromVoidPtr((void*)obj); - if (key == NULL) { - goto fail; + if (NPY_UNLIKELY(((uintptr_t)tagged_buffer_info & 0x7) != 3)) { + PyErr_Format(PyExc_RuntimeError, + "Object of type %S appears to be C subclassed NumPy array, " + "void scalar, or allocated in a non-standard way." + "NumPy reserves the right to change the size of these " + "structures. Projects are required to take this into account " + "by either recompiling against a specific NumPy version or " + "padding the struct and enforcing a maximum NumPy version.", + Py_TYPE(obj)); + return -1; } - item_list = PyDict_GetItem(_buffer_info_cache, key); + *buffer_info = (void *)((uintptr_t)tagged_buffer_info - 3); + return 0; +} - if (item_list != NULL) { - Py_INCREF(item_list); - if (PyList_GET_SIZE(item_list) > 0) { - item = PyList_GetItem(item_list, PyList_GET_SIZE(item_list) - 1); - old_info = (_buffer_info_t*)PyLong_AsVoidPtr(item); - if (_buffer_info_cmp(info, old_info) == 0) { - _buffer_info_free(info); - info = old_info; - } - } - } - else { - item_list = PyList_New(0); - if (item_list == NULL) { - goto fail; - } - if (PyDict_SetItem(_buffer_info_cache, key, item_list) != 0) { - goto fail; - } - } - - if (info != old_info) { - /* Needs insertion */ - item = PyLong_FromVoidPtr((void*)info); - if (item == NULL) { - goto fail; +/* + * NOTE: for backward compatibility (esp. with PyArg_ParseTuple("s#", ...)) + * we do *not* define bf_releasebuffer at all. + * + * Instead, any extra data allocated with the buffer is released only in + * array_dealloc. + * + * Ensuring that the buffer stays in place is taken care by refcounting; + * ndarrays do not reallocate if there are references to them, and a buffer + * view holds one reference. + * + * This is stored in the array's _buffer_info slot (currently as a void *). + */ +static void +_buffer_info_free_untagged(void *_buffer_info) +{ + _buffer_info_t *next = _buffer_info; + while (next != NULL) { + _buffer_info_t *curr = next; + next = curr->next; + if (curr->format) { + PyObject_Free(curr->format); } - PyList_Append(item_list, item); - Py_DECREF(item); + /* Shape is allocated as part of info */ + PyObject_Free(curr); } +} - Py_DECREF(item_list); - Py_DECREF(key); - return info; -fail: - if (info != NULL && info != old_info) { - _buffer_info_free(info); +/* + * Checks whether the pointer is tagged, and then frees the cache list. + * (The tag check is only for transition due to changed structure size in 1.20) + */ +NPY_NO_EXPORT int +_buffer_info_free(void *buffer_info, PyObject *obj) +{ + _buffer_info_t *untagged_buffer_info; + if (_buffer_info_untag(buffer_info, &untagged_buffer_info, obj) < 0) { + return -1; } - Py_XDECREF(item_list); - Py_XDECREF(key); - return NULL; + _buffer_info_free_untagged(untagged_buffer_info); + return 0; } -/* Clear buffer info from the global dictionary */ -static void -_buffer_clear_info(PyObject *arr) + +/* + * Get the buffer info returning either the old one (passed in) or a new + * buffer info which adds holds on to (and thus replaces) the old one. + */ +static _buffer_info_t* +_buffer_get_info(void **buffer_info_cache_ptr, PyObject *obj, int flags) { - PyObject *key, *item_list, *item; - _buffer_info_t *info; - int k; + _buffer_info_t *info = NULL; + _buffer_info_t *stored_info; /* First currently stored buffer info */ - if (_buffer_info_cache == NULL) { - return; + if (_buffer_info_untag(*buffer_info_cache_ptr, &stored_info, obj) < 0) { + return NULL; + } + _buffer_info_t *old_info = stored_info; + + /* Compute information (it would be nice to skip this in simple cases) */ + info = _buffer_info_new(obj, flags); + if (info == NULL) { + return NULL; } - key = PyLong_FromVoidPtr((void*)arr); - item_list = PyDict_GetItem(_buffer_info_cache, key); - if (item_list != NULL) { - for (k = 0; k < PyList_GET_SIZE(item_list); ++k) { - item = PyList_GET_ITEM(item_list, k); - info = (_buffer_info_t*)PyLong_AsVoidPtr(item); - _buffer_info_free(info); + if (old_info != NULL && _buffer_info_cmp(info, old_info) != 0) { + _buffer_info_t *next_info = old_info->next; + old_info = NULL; /* Can't use this one, but possibly next */ + + if (info->ndim > 1 && next_info != NULL) { + /* + * Some arrays are C- and F-contiguous and if they have more + * than one dimension, the buffer-info may differ between + * the two due to RELAXED_STRIDES_CHECKING. + * If we export both buffers, the first stored one may be + * the one for the other contiguity, so check both. + * This is generally very unlikely in all other cases, since + * in all other cases the first one will match unless array + * metadata was modified in-place (which is discouraged). + */ + if (_buffer_info_cmp(info, next_info) == 0) { + old_info = next_info; + } + } + } + if (old_info != NULL) { + /* + * The two info->format are considered equal if one of them + * has no format set (meaning the format is arbitrary and can + * be modified). If the new info has a format, but we reuse + * the old one, this transfers the ownership to the old one. + */ + if (old_info->format == NULL) { + old_info->format = info->format; + info->format = NULL; } - PyDict_DelItem(_buffer_info_cache, key); + _buffer_info_free_untagged(info); + info = old_info; + } + else { + /* Insert new info as first item in the linked buffer-info list. */ + info->next = stored_info; + *buffer_info_cache_ptr = buffer_info_tag(info); } - Py_DECREF(key); + return info; } + /* * Retrieving buffers for ndarray */ @@ -692,25 +783,15 @@ array_getbuffer(PyObject *obj, Py_buffer *view, int flags) goto fail; } } - /* - * If a read-only buffer is requested on a read-write array, we return a - * read-write buffer, which is dubious behavior. But that's why this call - * is guarded by PyArray_ISWRITEABLE rather than (flags & - * PyBUF_WRITEABLE). - */ - if (PyArray_ISWRITEABLE(self)) { - if (array_might_be_written(self) < 0) { - goto fail; - } - } if (view == NULL) { PyErr_SetString(PyExc_ValueError, "NULL view in getbuffer"); goto fail; } - /* Fill in information */ - info = _buffer_get_info(obj); + /* Fill in information (and add it to _buffer_info if necessary) */ + info = _buffer_get_info( + &((PyArrayObject_fields *)self)->_buffer_info, obj, flags); if (info == NULL) { goto fail; } @@ -718,7 +799,17 @@ array_getbuffer(PyObject *obj, Py_buffer *view, int flags) view->buf = PyArray_DATA(self); view->suboffsets = NULL; view->itemsize = PyArray_ITEMSIZE(self); - view->readonly = !PyArray_ISWRITEABLE(self); + /* + * If a read-only buffer is requested on a read-write array, we return a + * read-write buffer as per buffer protocol. + * We set a requested buffer to readonly also if the array will be readonly + * after a deprecation. This jumps the deprecation, but avoiding the + * warning is not convenient here. A warning is given if a writeable + * buffer is requested since `PyArray_FailUnlessWriteable` is called above + * (and clears the `NPY_ARRAY_WARN_ON_WRITE` flag). + */ + view->readonly = (!PyArray_ISWRITEABLE(self) || + PyArray_CHKFLAGS(self, NPY_ARRAY_WARN_ON_WRITE)); view->internal = NULL; view->len = PyArray_NBYTES(self); if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) { @@ -736,35 +827,6 @@ array_getbuffer(PyObject *obj, Py_buffer *view, int flags) } if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) { view->strides = info->strides; - -#ifdef NPY_RELAXED_STRIDES_CHECKING - /* - * If NPY_RELAXED_STRIDES_CHECKING is on, the array may be - * contiguous, but it won't look that way to Python when it - * tries to determine contiguity by looking at the strides - * (since one of the elements may be -1). In that case, just - * regenerate strides from shape. - */ - if (PyArray_CHKFLAGS(self, NPY_ARRAY_C_CONTIGUOUS) && - !((flags & PyBUF_F_CONTIGUOUS) == PyBUF_F_CONTIGUOUS)) { - Py_ssize_t sd = view->itemsize; - int i; - - for (i = view->ndim-1; i >= 0; --i) { - view->strides[i] = sd; - sd *= view->shape[i]; - } - } - else if (PyArray_CHKFLAGS(self, NPY_ARRAY_F_CONTIGUOUS)) { - Py_ssize_t sd = view->itemsize; - int i; - - for (i = 0; i < view->ndim; ++i) { - view->strides[i] = sd; - sd *= view->shape[i]; - } - } -#endif } else { view->strides = NULL; @@ -779,106 +841,55 @@ array_getbuffer(PyObject *obj, Py_buffer *view, int flags) } /* - * Retrieving buffers for scalars + * Retrieving buffers for void scalar (which can contain any complex types), + * defined in buffer.c since it requires the complex format building logic. */ -int -gentype_getbuffer(PyObject *self, Py_buffer *view, int flags) +NPY_NO_EXPORT int +void_getbuffer(PyObject *self, Py_buffer *view, int flags) { - _buffer_info_t *info = NULL; - PyArray_Descr *descr = NULL; - int elsize; + PyVoidScalarObject *scalar = (PyVoidScalarObject *)self; if (flags & PyBUF_WRITABLE) { PyErr_SetString(PyExc_BufferError, "scalar buffer is readonly"); - goto fail; - } - - /* Fill in information */ - info = _buffer_get_info(self); - if (info == NULL) { - PyErr_SetString(PyExc_BufferError, - "could not get scalar buffer information"); - goto fail; - } - - view->ndim = info->ndim; - view->shape = info->shape; - view->strides = info->strides; - - if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) { - view->format = info->format; - } else { - view->format = NULL; - } - - descr = PyArray_DescrFromScalar(self); - view->buf = (void *)scalar_value(self, descr); - elsize = descr->elsize; -#ifndef Py_UNICODE_WIDE - if (descr->type_num == NPY_UNICODE) { - elsize >>= 1; + return -1; } -#endif - view->len = elsize; - view->itemsize = elsize; - - Py_DECREF(descr); + view->ndim = 0; + view->shape = NULL; + view->strides = NULL; + view->suboffsets = NULL; + view->len = scalar->descr->elsize; + view->itemsize = scalar->descr->elsize; view->readonly = 1; view->suboffsets = NULL; - view->obj = self; Py_INCREF(self); - return 0; - -fail: - view->obj = NULL; - return -1; -} - -/* - * NOTE: for backward compatibility (esp. with PyArg_ParseTuple("s#", ...)) - * we do *not* define bf_releasebuffer at all. - * - * Instead, any extra data allocated with the buffer is released only in - * array_dealloc. - * - * Ensuring that the buffer stays in place is taken care by refcounting; - * ndarrays do not reallocate if there are references to them, and a buffer - * view holds one reference. - */ - -NPY_NO_EXPORT void -_array_dealloc_buffer_info(PyArrayObject *self) -{ - int reset_error_state = 0; - PyObject *ptype, *pvalue, *ptraceback; - - /* This function may be called when processing an exception -- - * we need to stash the error state to avoid confusing PyDict - */ + view->obj = self; + view->buf = scalar->obval; - if (PyErr_Occurred()) { - reset_error_state = 1; - PyErr_Fetch(&ptype, &pvalue, &ptraceback); + if (((flags & PyBUF_FORMAT) != PyBUF_FORMAT)) { + /* It is unnecessary to find the correct format */ + view->format = NULL; + return 0; } - _buffer_clear_info((PyObject*)self); - - if (reset_error_state) { - PyErr_Restore(ptype, pvalue, ptraceback); + /* + * If a format is being exported, we need to use _buffer_get_info + * to find the correct format. This format must also be stored, since + * at least in theory it can change (in practice it should never change). + */ + _buffer_info_t *info = _buffer_get_info(&scalar->_buffer_info, self, flags); + if (info == NULL) { + Py_DECREF(self); + return -1; } + view->format = info->format; + return 0; } /*************************************************************************/ NPY_NO_EXPORT PyBufferProcs array_as_buffer = { -#if !defined(NPY_PY3K) - (readbufferproc)array_getreadbuf, /*bf_getreadbuffer*/ - (writebufferproc)array_getwritebuf, /*bf_getwritebuffer*/ - (segcountproc)array_getsegcount, /*bf_getsegcount*/ - (charbufferproc)array_getcharbuf, /*bf_getcharbuffer*/ -#endif (getbufferproc)array_getbuffer, (releasebufferproc)0, }; @@ -889,13 +900,13 @@ NPY_NO_EXPORT PyBufferProcs array_as_buffer = { */ static int -_descriptor_from_pep3118_format_fast(char *s, PyObject **result); +_descriptor_from_pep3118_format_fast(char const *s, PyObject **result); static int _pep3118_letter_to_type(char letter, int native, int complex); NPY_NO_EXPORT PyArray_Descr* -_descriptor_from_pep3118_format(char *s) +_descriptor_from_pep3118_format(char const *s) { char *buf, *p; int in_name = 0; @@ -935,7 +946,7 @@ _descriptor_from_pep3118_format(char *s) } *p = '\0'; - str = PyUString_FromStringAndSize(buf, strlen(buf)); + str = PyUnicode_FromStringAndSize(buf, strlen(buf)); if (str == NULL) { free(buf); return NULL; @@ -953,8 +964,11 @@ _descriptor_from_pep3118_format(char *s) Py_DECREF(str); Py_DECREF(_numpy_internal); if (descr == NULL) { + PyObject *exc, *val, *tb; + PyErr_Fetch(&exc, &val, &tb); PyErr_Format(PyExc_ValueError, "'%s' is not a valid PEP 3118 buffer format string", buf); + npy_PyErr_ChainExceptionsCause(exc, val, tb); free(buf); return NULL; } @@ -977,7 +991,7 @@ _descriptor_from_pep3118_format(char *s) */ static int -_descriptor_from_pep3118_format_fast(char *s, PyObject **result) +_descriptor_from_pep3118_format_fast(char const *s, PyObject **result) { PyArray_Descr *descr; diff --git a/numpy/core/src/multiarray/buffer.h b/numpy/core/src/multiarray/buffer.h deleted file mode 100644 index d5da8f440946..000000000000 --- a/numpy/core/src/multiarray/buffer.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef _NPY_PRIVATE_BUFFER_H_ -#define _NPY_PRIVATE_BUFFER_H_ - -extern NPY_NO_EXPORT PyBufferProcs array_as_buffer; - -NPY_NO_EXPORT void -_array_dealloc_buffer_info(PyArrayObject *self); - -NPY_NO_EXPORT PyArray_Descr* -_descriptor_from_pep3118_format(char *s); - -NPY_NO_EXPORT int -gentype_getbuffer(PyObject *obj, Py_buffer *view, int flags); - -#endif diff --git a/numpy/core/src/multiarray/calculation.c b/numpy/core/src/multiarray/calculation.c index e47dd81b957a..327f685d4ffc 100644 --- a/numpy/core/src/multiarray/calculation.c +++ b/numpy/core/src/multiarray/calculation.c @@ -1,10 +1,12 @@ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE + #define PY_SSIZE_T_CLEAN #include <Python.h> -#include "structmember.h" +#include <structmember.h> -#define NPY_NO_DEPRECATED_API NPY_API_VERSION -#define _MULTIARRAYMODULE #include "numpy/arrayobject.h" +#include "lowlevel_strided_loops.h" #include "npy_config.h" @@ -33,18 +35,25 @@ power_of_ten(int n) return ret; } -/*NUMPY_API - * ArgMax - */ NPY_NO_EXPORT PyObject * -PyArray_ArgMax(PyArrayObject *op, int axis, PyArrayObject *out) +_PyArray_ArgMinMaxCommon(PyArrayObject *op, + int axis, PyArrayObject *out, int keepdims, + npy_bool is_argmax) { PyArrayObject *ap = NULL, *rp = NULL; - PyArray_ArgFunc* arg_func; - char *ip; + PyArray_ArgFunc* arg_func = NULL; + char *ip, *func_name; npy_intp *rptr; npy_intp i, n, m; int elsize; + // Keep a copy because axis changes via call to PyArray_CheckAxis + int axis_copy = axis; + npy_intp _shape_buf[NPY_MAXDIMS]; + npy_intp *out_shape; + // Keep the number of dimensions and shape of + // original array. Helps when `keepdims` is True. + npy_intp* original_op_shape = PyArray_DIMS(op); + int out_ndim = PyArray_NDIM(op); NPY_BEGIN_THREADS_DEF; if ((ap = (PyArrayObject *)PyArray_CheckAxis(op, &axis, 0)) == NULL) { @@ -85,123 +94,37 @@ PyArray_ArgMax(PyArrayObject *op, int axis, PyArrayObject *out) if (ap == NULL) { return NULL; } - arg_func = PyArray_DESCR(ap)->f->argmax; - if (arg_func == NULL) { - PyErr_SetString(PyExc_TypeError, - "data type not ordered"); - goto fail; - } - elsize = PyArray_DESCR(ap)->elsize; - m = PyArray_DIMS(ap)[PyArray_NDIM(ap)-1]; - if (m == 0) { - PyErr_SetString(PyExc_ValueError, - "attempt to get argmax of an empty sequence"); - goto fail; - } - if (!out) { - rp = (PyArrayObject *)PyArray_NewFromDescr( - Py_TYPE(ap), PyArray_DescrFromType(NPY_INTP), - PyArray_NDIM(ap) - 1, PyArray_DIMS(ap), NULL, NULL, - 0, (PyObject *)ap); - if (rp == NULL) { - goto fail; - } + // Decides the shape of the output array. + if (!keepdims) { + out_ndim = PyArray_NDIM(ap) - 1; + out_shape = PyArray_DIMS(ap); } else { - if ((PyArray_NDIM(out) != PyArray_NDIM(ap) - 1) || - !PyArray_CompareLists(PyArray_DIMS(out), PyArray_DIMS(ap), - PyArray_NDIM(out))) { - PyErr_SetString(PyExc_ValueError, - "output array does not match result of np.argmax."); - goto fail; - } - rp = (PyArrayObject *)PyArray_FromArray(out, - PyArray_DescrFromType(NPY_INTP), - NPY_ARRAY_CARRAY | NPY_ARRAY_WRITEBACKIFCOPY); - if (rp == NULL) { - goto fail; + out_shape = _shape_buf; + if (axis_copy == NPY_MAXDIMS) { + for (int i = 0; i < out_ndim; i++) { + out_shape[i] = 1; + } + } + else { + /* + * While `ap` may be transposed, we can ignore this for `out` because the + * transpose only reorders the size 1 `axis` (not changing memory layout). + */ + memcpy(out_shape, original_op_shape, out_ndim * sizeof(npy_intp)); + out_shape[axis] = 1; } } - NPY_BEGIN_THREADS_DESCR(PyArray_DESCR(ap)); - n = PyArray_SIZE(ap)/m; - rptr = (npy_intp *)PyArray_DATA(rp); - for (ip = PyArray_DATA(ap), i = 0; i < n; i++, ip += elsize*m) { - arg_func(ip, m, rptr, ap); - rptr += 1; - } - NPY_END_THREADS_DESCR(PyArray_DESCR(ap)); - - Py_DECREF(ap); - /* Trigger the UPDATEIFCOPY/WRTIEBACKIFCOPY if necessary */ - if (out != NULL && out != rp) { - PyArray_ResolveWritebackIfCopy(rp); - Py_DECREF(rp); - rp = out; - Py_INCREF(rp); - } - return (PyObject *)rp; - - fail: - Py_DECREF(ap); - Py_XDECREF(rp); - return NULL; -} - -/*NUMPY_API - * ArgMin - */ -NPY_NO_EXPORT PyObject * -PyArray_ArgMin(PyArrayObject *op, int axis, PyArrayObject *out) -{ - PyArrayObject *ap = NULL, *rp = NULL; - PyArray_ArgFunc* arg_func; - char *ip; - npy_intp *rptr; - npy_intp i, n, m; - int elsize; - NPY_BEGIN_THREADS_DEF; - - if ((ap = (PyArrayObject *)PyArray_CheckAxis(op, &axis, 0)) == NULL) { - return NULL; - } - /* - * We need to permute the array so that axis is placed at the end. - * And all other dimensions are shifted left. - */ - if (axis != PyArray_NDIM(ap)-1) { - PyArray_Dims newaxes; - npy_intp dims[NPY_MAXDIMS]; - int i; - - newaxes.ptr = dims; - newaxes.len = PyArray_NDIM(ap); - for (i = 0; i < axis; i++) { - dims[i] = i; - } - for (i = axis; i < PyArray_NDIM(ap) - 1; i++) { - dims[i] = i + 1; - } - dims[PyArray_NDIM(ap) - 1] = axis; - op = (PyArrayObject *)PyArray_Transpose(ap, &newaxes); - Py_DECREF(ap); - if (op == NULL) { - return NULL; - } + if (is_argmax) { + func_name = "argmax"; + arg_func = PyArray_DESCR(ap)->f->argmax; } else { - op = ap; + func_name = "argmin"; + arg_func = PyArray_DESCR(ap)->f->argmin; } - - /* Will get native-byte order contiguous copy. */ - ap = (PyArrayObject *)PyArray_ContiguousFromAny((PyObject *)op, - PyArray_DESCR(op)->type_num, 1, 0); - Py_DECREF(op); - if (ap == NULL) { - return NULL; - } - arg_func = PyArray_DESCR(ap)->f->argmin; if (arg_func == NULL) { PyErr_SetString(PyExc_TypeError, "data type not ordered"); @@ -210,26 +133,28 @@ PyArray_ArgMin(PyArrayObject *op, int axis, PyArrayObject *out) elsize = PyArray_DESCR(ap)->elsize; m = PyArray_DIMS(ap)[PyArray_NDIM(ap)-1]; if (m == 0) { - PyErr_SetString(PyExc_ValueError, - "attempt to get argmin of an empty sequence"); + PyErr_Format(PyExc_ValueError, + "attempt to get %s of an empty sequence", + func_name); goto fail; } if (!out) { rp = (PyArrayObject *)PyArray_NewFromDescr( Py_TYPE(ap), PyArray_DescrFromType(NPY_INTP), - PyArray_NDIM(ap) - 1, PyArray_DIMS(ap), NULL, NULL, + out_ndim, out_shape, NULL, NULL, 0, (PyObject *)ap); if (rp == NULL) { goto fail; } } else { - if ((PyArray_NDIM(out) != PyArray_NDIM(ap) - 1) || - !PyArray_CompareLists(PyArray_DIMS(out), PyArray_DIMS(ap), - PyArray_NDIM(out))) { - PyErr_SetString(PyExc_ValueError, - "output array does not match result of np.argmin."); + if ((PyArray_NDIM(out) != out_ndim) || + !PyArray_CompareLists(PyArray_DIMS(out), out_shape, + out_ndim)) { + PyErr_Format(PyExc_ValueError, + "output array does not match result of np.%s.", + func_name); goto fail; } rp = (PyArrayObject *)PyArray_FromArray(out, @@ -265,6 +190,38 @@ PyArray_ArgMin(PyArrayObject *op, int axis, PyArrayObject *out) return NULL; } +NPY_NO_EXPORT PyObject* +_PyArray_ArgMaxWithKeepdims(PyArrayObject *op, + int axis, PyArrayObject *out, int keepdims) +{ + return _PyArray_ArgMinMaxCommon(op, axis, out, keepdims, 1); +} + +/*NUMPY_API + * ArgMax + */ +NPY_NO_EXPORT PyObject * +PyArray_ArgMax(PyArrayObject *op, int axis, PyArrayObject *out) +{ + return _PyArray_ArgMinMaxCommon(op, axis, out, 0, 1); +} + +NPY_NO_EXPORT PyObject * +_PyArray_ArgMinWithKeepdims(PyArrayObject *op, + int axis, PyArrayObject *out, int keepdims) +{ + return _PyArray_ArgMinMaxCommon(op, axis, out, keepdims, 0); +} + +/*NUMPY_API + * ArgMin + */ +NPY_NO_EXPORT PyObject * +PyArray_ArgMin(PyArrayObject *op, int axis, PyArrayObject *out) +{ + return _PyArray_ArgMinMaxCommon(op, axis, out, 0, 0); +} + /*NUMPY_API * Max */ @@ -391,7 +348,7 @@ __New_PyArray_Std(PyArrayObject *self, int axis, int rtype, PyArrayObject *out, else { val = PyArray_DIM(arrnew,i); } - PyTuple_SET_ITEM(newshape, i, PyInt_FromLong((long)val)); + PyTuple_SET_ITEM(newshape, i, PyLong_FromSsize_t(val)); } arr2 = (PyArrayObject *)PyArray_Reshape(arr1, newshape); Py_DECREF(arr1); @@ -422,7 +379,8 @@ __New_PyArray_Std(PyArrayObject *self, int axis, int rtype, PyArrayObject *out, return NULL; } arr2 = (PyArrayObject *)PyArray_EnsureAnyArray( - PyArray_GenericBinaryFunction(arr1, obj3, n_ops.multiply)); + PyArray_GenericBinaryFunction((PyObject *)arr1, obj3, + n_ops.multiply)); Py_DECREF(arr1); Py_DECREF(obj3); if (arr2 == NULL) { @@ -771,11 +729,7 @@ PyArray_Mean(PyArrayObject *self, int axis, int rtype, PyArrayObject *out) return NULL; } if (!out) { -#if defined(NPY_PY3K) ret = PyNumber_TrueDivide(obj1, obj2); -#else - ret = PyNumber_Divide(obj1, obj2); -#endif } else { ret = PyObject_CallFunction(n_ops.divide, "OOO", out, obj2, out); @@ -917,6 +871,28 @@ PyArray_Clip(PyArrayObject *self, PyObject *min, PyObject *max, PyArrayObject *o } func = PyArray_DESCR(self)->f->fastclip; + if (func == NULL) { + if (min == NULL) { + return PyObject_CallFunctionObjArgs(n_ops.minimum, self, max, out, NULL); + } + else if (max == NULL) { + return PyObject_CallFunctionObjArgs(n_ops.maximum, self, min, out, NULL); + } + else { + return PyObject_CallFunctionObjArgs(n_ops.clip, self, min, max, out, NULL); + } + } + + /* + * NumPy 1.17.0, 2019-02-24 + * NumPy 1.19.0, 2020-01-15 + * + * Setting `->f->fastclip to anything but NULL has been deprecated in 1.19 + * the code path below was previously deprecated since 1.17. + * (the deprecation moved to registration time instead of execution time) + * everything below can be removed once this deprecation completes + */ + if (func == NULL || (min != NULL && !PyArray_CheckAnyScalar(min)) || (max != NULL && !PyArray_CheckAnyScalar(max)) @@ -1004,7 +980,7 @@ PyArray_Clip(PyArrayObject *self, PyObject *min, PyObject *max, PyArrayObject *o if (min != NULL) { if (PyArray_ISUNSIGNED(self)) { int cmp; - zero = PyInt_FromLong(0); + zero = PyLong_FromLong(0); cmp = PyObject_RichCompareBool(min, zero, Py_LT); if (cmp == -1) { Py_DECREF(zero); @@ -1102,7 +1078,18 @@ PyArray_Clip(PyArrayObject *self, PyObject *min, PyObject *max, PyArrayObject *o if (out == newin) { outgood = 1; } - if (!outgood && PyArray_ISONESEGMENT(out) && + + + /* make sure the shape of the output array is the same */ + if (!PyArray_SAMESHAPE(newin, out)) { + PyErr_SetString(PyExc_ValueError, "clip: Output array must have the" + "same shape as the input."); + goto fail; + } + + if (!outgood && PyArray_EQUIVALENTLY_ITERABLE( + self, out, PyArray_TRIVIALLY_ITERABLE_OP_READ, + PyArray_TRIVIALLY_ITERABLE_OP_NOREAD) && PyArray_CHKFLAGS(out, NPY_ARRAY_ALIGNED) && PyArray_ISNOTSWAPPED(out) && PyArray_EquivTypes(PyArray_DESCR(out), indescr)) { @@ -1111,15 +1098,19 @@ PyArray_Clip(PyArrayObject *self, PyObject *min, PyObject *max, PyArrayObject *o /* * Do we still not have a suitable output array? - * Create one, now + * Create one, now. No matter why the array is not suitable a copy has + * to be made. This may be just to avoid memory overlap though. */ if (!outgood) { int oflags; - if (PyArray_ISFORTRAN(out)) + if (PyArray_ISFORTRAN(self)) { oflags = NPY_ARRAY_FARRAY; - else + } + else { oflags = NPY_ARRAY_CARRAY; - oflags |= NPY_ARRAY_WRITEBACKIFCOPY | NPY_ARRAY_FORCECAST; + } + oflags |= (NPY_ARRAY_WRITEBACKIFCOPY | NPY_ARRAY_FORCECAST | + NPY_ARRAY_ENSURECOPY); Py_INCREF(indescr); newout = (PyArrayObject*)PyArray_FromArray(out, indescr, oflags); if (newout == NULL) { @@ -1131,13 +1122,6 @@ PyArray_Clip(PyArrayObject *self, PyObject *min, PyObject *max, PyArrayObject *o Py_INCREF(newout); } - /* make sure the shape of the output array is the same */ - if (!PyArray_SAMESHAPE(newin, newout)) { - PyErr_SetString(PyExc_ValueError, "clip: Output array must have the" - "same shape as the input."); - goto fail; - } - /* Now we can call the fast-clip function */ min_data = max_data = NULL; if (mina != NULL) { @@ -1184,7 +1168,7 @@ PyArray_Conjugate(PyArrayObject *self, PyArrayObject *out) n_ops.conjugate); } else { - return PyArray_GenericBinaryFunction(self, + return PyArray_GenericBinaryFunction((PyObject *)self, (PyObject *)out, n_ops.conjugate); } diff --git a/numpy/core/src/multiarray/calculation.h b/numpy/core/src/multiarray/calculation.h index 34bc31f69806..6a9c3c7c9d2b 100644 --- a/numpy/core/src/multiarray/calculation.h +++ b/numpy/core/src/multiarray/calculation.h @@ -1,12 +1,18 @@ -#ifndef _NPY_CALCULATION_H_ -#define _NPY_CALCULATION_H_ +#ifndef NUMPY_CORE_SRC_MULTIARRAY_CALCULATION_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_CALCULATION_H_ NPY_NO_EXPORT PyObject* PyArray_ArgMax(PyArrayObject* self, int axis, PyArrayObject *out); +NPY_NO_EXPORT PyObject* +_PyArray_ArgMaxWithKeepdims(PyArrayObject* self, int axis, PyArrayObject *out, int keepdims); + NPY_NO_EXPORT PyObject* PyArray_ArgMin(PyArrayObject* self, int axis, PyArrayObject *out); +NPY_NO_EXPORT PyObject* +_PyArray_ArgMinWithKeepdims(PyArrayObject* self, int axis, PyArrayObject *out, int keepdims); + NPY_NO_EXPORT PyObject* PyArray_Max(PyArrayObject* self, int axis, PyArrayObject* out); @@ -61,4 +67,4 @@ PyArray_All(PyArrayObject* self, int axis, PyArrayObject* out); NPY_NO_EXPORT PyObject* PyArray_Any(PyArrayObject* self, int axis, PyArrayObject* out); -#endif +#endif /* NUMPY_CORE_SRC_MULTIARRAY_CALCULATION_H_ */ diff --git a/numpy/core/src/multiarray/cblasfuncs.h b/numpy/core/src/multiarray/cblasfuncs.h deleted file mode 100644 index 66ce4ca5becb..000000000000 --- a/numpy/core/src/multiarray/cblasfuncs.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef _NPY_CBLASFUNCS_H_ -#define _NPY_CBLASFUNCS_H_ - -NPY_NO_EXPORT PyObject * -cblas_matrixproduct(int, PyArrayObject *, PyArrayObject *, PyArrayObject *); - -#endif diff --git a/numpy/core/src/multiarray/common.c b/numpy/core/src/multiarray/common.c index c70f8526eb1c..aa95d285a8ca 100644 --- a/numpy/core/src/multiarray/common.c +++ b/numpy/core/src/multiarray/common.c @@ -1,20 +1,23 @@ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE + #define PY_SSIZE_T_CLEAN #include <Python.h> -#define NPY_NO_DEPRECATED_API NPY_API_VERSION -#define _MULTIARRAYMODULE #include "numpy/arrayobject.h" #include "npy_config.h" #include "npy_pycompat.h" #include "common.h" +#include "abstractdtypes.h" #include "usertypes.h" -#include "common.h" -#include "buffer.h" +#include "npy_buffer.h" #include "get_attr_string.h" +#include "mem_overlap.h" +#include "array_coercion.h" /* * The casting to use for implicit assignment operations resulting from @@ -43,512 +46,87 @@ _array_find_python_scalar_type(PyObject *op) else if (PyComplex_Check(op)) { return PyArray_DescrFromType(NPY_CDOUBLE); } - else if (PyInt_Check(op)) { - /* bools are a subclass of int */ - if (PyBool_Check(op)) { - return PyArray_DescrFromType(NPY_BOOL); - } - else { - return PyArray_DescrFromType(NPY_LONG); - } - } else if (PyLong_Check(op)) { - /* check to see if integer can fit into a longlong or ulonglong - and return that --- otherwise return object */ - if ((PyLong_AsLongLong(op) == -1) && PyErr_Occurred()) { - PyErr_Clear(); - } - else { - return PyArray_DescrFromType(NPY_LONGLONG); - } - - if ((PyLong_AsUnsignedLongLong(op) == (unsigned long long) -1) - && PyErr_Occurred()){ - PyErr_Clear(); - } - else { - return PyArray_DescrFromType(NPY_ULONGLONG); - } - - return PyArray_DescrFromType(NPY_OBJECT); + return NPY_DT_CALL_discover_descr_from_pyobject( + &PyArray_PyIntAbstractDType, op); } return NULL; } -/* - * These constants are used to signal that the recursive dtype determination in - * PyArray_DTypeFromObject encountered a string type, and that the recursive - * search must be restarted so that string representation lengths can be - * computed for all scalar types. - */ -#define RETRY_WITH_STRING 1 -#define RETRY_WITH_UNICODE 2 /* - * Recursively examines the object to determine an appropriate dtype - * to use for converting to an ndarray. - * - * 'obj' is the object to be converted to an ndarray. - * - * 'maxdims' is the maximum recursion depth. - * - * 'out_dtype' should be either NULL or a minimal starting dtype when - * the function is called. It is updated with the results of type - * promotion. This dtype does not get updated when processing NA objects. - * This is reset to NULL on failure. - * - * Returns 0 on success, -1 on failure. + * Get a suitable string dtype by calling `__str__`. + * For `np.bytes_`, this assumes an ASCII encoding. */ - NPY_NO_EXPORT int -PyArray_DTypeFromObject(PyObject *obj, int maxdims, PyArray_Descr **out_dtype) -{ - int res; - - res = PyArray_DTypeFromObjectHelper(obj, maxdims, out_dtype, 0); - if (res == RETRY_WITH_STRING) { - res = PyArray_DTypeFromObjectHelper(obj, maxdims, - out_dtype, NPY_STRING); - if (res == RETRY_WITH_UNICODE) { - res = PyArray_DTypeFromObjectHelper(obj, maxdims, - out_dtype, NPY_UNICODE); - } - } - else if (res == RETRY_WITH_UNICODE) { - res = PyArray_DTypeFromObjectHelper(obj, maxdims, - out_dtype, NPY_UNICODE); - } - return res; -} - -NPY_NO_EXPORT int -PyArray_DTypeFromObjectHelper(PyObject *obj, int maxdims, - PyArray_Descr **out_dtype, int string_type) +NPY_NO_EXPORT PyArray_Descr * +PyArray_DTypeFromObjectStringDiscovery( + PyObject *obj, PyArray_Descr *last_dtype, int string_type) { - int i, size; - PyArray_Descr *dtype = NULL; - PyObject *ip; - Py_buffer buffer_view; - /* types for sequence handling */ - PyObject ** objects; - PyObject * seq; - PyTypeObject * common_type; - - /* Check if it's an ndarray */ - if (PyArray_Check(obj)) { - dtype = PyArray_DESCR((PyArrayObject *)obj); - Py_INCREF(dtype); - goto promote_types; - } - - /* See if it's a python None */ - if (obj == Py_None) { - dtype = PyArray_DescrFromType(NPY_OBJECT); - if (dtype == NULL) { - goto fail; - } - Py_INCREF(dtype); - goto promote_types; - } - /* Check if it's a NumPy scalar */ - else if (PyArray_IsScalar(obj, Generic)) { - if (!string_type) { - dtype = PyArray_DescrFromScalar(obj); - if (dtype == NULL) { - goto fail; - } - } - else { - int itemsize; - PyObject *temp; - - if (string_type == NPY_STRING) { - if ((temp = PyObject_Str(obj)) == NULL) { - return -1; - } -#if defined(NPY_PY3K) - #if PY_VERSION_HEX >= 0x03030000 - itemsize = PyUnicode_GetLength(temp); - #else - itemsize = PyUnicode_GET_SIZE(temp); - #endif -#else - itemsize = PyString_GET_SIZE(temp); -#endif - } - else if (string_type == NPY_UNICODE) { -#if defined(NPY_PY3K) - if ((temp = PyObject_Str(obj)) == NULL) { -#else - if ((temp = PyObject_Unicode(obj)) == NULL) { -#endif - return -1; - } - itemsize = PyUnicode_GET_DATA_SIZE(temp); -#ifndef Py_UNICODE_WIDE - itemsize <<= 1; -#endif - } - else { - goto fail; - } - Py_DECREF(temp); - if (*out_dtype != NULL && - (*out_dtype)->type_num == string_type && - (*out_dtype)->elsize >= itemsize) { - return 0; - } - dtype = PyArray_DescrNewFromType(string_type); - if (dtype == NULL) { - goto fail; - } - dtype->elsize = itemsize; - } - goto promote_types; - } + int itemsize; - /* Check if it's a Python scalar */ - dtype = _array_find_python_scalar_type(obj); - if (dtype != NULL) { - if (string_type) { - int itemsize; - PyObject *temp; - - if (string_type == NPY_STRING) { - if ((temp = PyObject_Str(obj)) == NULL) { - return -1; - } -#if defined(NPY_PY3K) - #if PY_VERSION_HEX >= 0x03030000 - itemsize = PyUnicode_GetLength(temp); - #else - itemsize = PyUnicode_GET_SIZE(temp); - #endif -#else - itemsize = PyString_GET_SIZE(temp); -#endif - } - else if (string_type == NPY_UNICODE) { -#if defined(NPY_PY3K) - if ((temp = PyObject_Str(obj)) == NULL) { -#else - if ((temp = PyObject_Unicode(obj)) == NULL) { -#endif - return -1; - } - itemsize = PyUnicode_GET_DATA_SIZE(temp); -#ifndef Py_UNICODE_WIDE - itemsize <<= 1; -#endif - } - else { - goto fail; - } - Py_DECREF(temp); - if (*out_dtype != NULL && - (*out_dtype)->type_num == string_type && - (*out_dtype)->elsize >= itemsize) { - return 0; - } - dtype = PyArray_DescrNewFromType(string_type); - if (dtype == NULL) { - goto fail; - } - dtype->elsize = itemsize; - } - goto promote_types; - } - - /* Check if it's an ASCII string */ - if (PyBytes_Check(obj)) { - int itemsize = PyString_GET_SIZE(obj); - - /* If it's already a big enough string, don't bother type promoting */ - if (*out_dtype != NULL && - (*out_dtype)->type_num == NPY_STRING && - (*out_dtype)->elsize >= itemsize) { - return 0; - } - dtype = PyArray_DescrNewFromType(NPY_STRING); - if (dtype == NULL) { - goto fail; - } - dtype->elsize = itemsize; - goto promote_types; - } - - /* Check if it's a Unicode string */ - if (PyUnicode_Check(obj)) { - int itemsize = PyUnicode_GET_DATA_SIZE(obj); -#ifndef Py_UNICODE_WIDE - itemsize <<= 1; -#endif - - /* - * If it's already a big enough unicode object, - * don't bother type promoting - */ - if (*out_dtype != NULL && - (*out_dtype)->type_num == NPY_UNICODE && - (*out_dtype)->elsize >= itemsize) { - return 0; - } - dtype = PyArray_DescrNewFromType(NPY_UNICODE); - if (dtype == NULL) { - goto fail; - } - dtype->elsize = itemsize; - goto promote_types; - } - - /* PEP 3118 buffer interface */ - if (PyObject_CheckBuffer(obj) == 1) { - memset(&buffer_view, 0, sizeof(Py_buffer)); - if (PyObject_GetBuffer(obj, &buffer_view, - PyBUF_FORMAT|PyBUF_STRIDES) == 0 || - PyObject_GetBuffer(obj, &buffer_view, - PyBUF_FORMAT|PyBUF_SIMPLE) == 0) { - - PyErr_Clear(); - dtype = _descriptor_from_pep3118_format(buffer_view.format); - PyBuffer_Release(&buffer_view); - if (dtype) { - goto promote_types; - } - } - else if (PyObject_GetBuffer(obj, &buffer_view, PyBUF_STRIDES) == 0 || - PyObject_GetBuffer(obj, &buffer_view, PyBUF_SIMPLE) == 0) { - - PyErr_Clear(); - dtype = PyArray_DescrNewFromType(NPY_VOID); - dtype->elsize = buffer_view.itemsize; - PyBuffer_Release(&buffer_view); - goto promote_types; - } - else { - PyErr_Clear(); - } - } - - /* The array interface */ - ip = PyArray_LookupSpecial_OnInstance(obj, "__array_interface__"); - if (ip != NULL) { - if (PyDict_Check(ip)) { - PyObject *typestr; -#if defined(NPY_PY3K) - PyObject *tmp = NULL; -#endif - typestr = PyDict_GetItemString(ip, "typestr"); -#if defined(NPY_PY3K) - /* Allow unicode type strings */ - if (PyUnicode_Check(typestr)) { - tmp = PyUnicode_AsASCIIString(typestr); - typestr = tmp; - } -#endif - if (typestr && PyBytes_Check(typestr)) { - dtype =_array_typedescr_fromstr(PyBytes_AS_STRING(typestr)); -#if defined(NPY_PY3K) - if (tmp == typestr) { - Py_DECREF(tmp); - } -#endif - Py_DECREF(ip); - if (dtype == NULL) { - goto fail; - } - goto promote_types; - } - } - Py_DECREF(ip); - } - - /* The array struct interface */ - ip = PyArray_LookupSpecial_OnInstance(obj, "__array_struct__"); - if (ip != NULL) { - PyArrayInterface *inter; - char buf[40]; - - if (NpyCapsule_Check(ip)) { - inter = (PyArrayInterface *)NpyCapsule_AsVoidPtr(ip); - if (inter->two == 2) { - PyOS_snprintf(buf, sizeof(buf), - "|%c%d", inter->typekind, inter->itemsize); - dtype = _array_typedescr_fromstr(buf); - Py_DECREF(ip); - if (dtype == NULL) { - goto fail; - } - goto promote_types; - } - } - Py_DECREF(ip); - } - - /* The old buffer interface */ -#if !defined(NPY_PY3K) - if (PyBuffer_Check(obj)) { - dtype = PyArray_DescrNewFromType(NPY_VOID); - if (dtype == NULL) { - goto fail; - } - dtype->elsize = Py_TYPE(obj)->tp_as_sequence->sq_length(obj); - PyErr_Clear(); - goto promote_types; - } -#endif - - /* The __array__ attribute */ - ip = PyArray_LookupSpecial_OnInstance(obj, "__array__"); - if (ip != NULL) { - Py_DECREF(ip); - ip = PyObject_CallMethod(obj, "__array__", NULL); - if(ip && PyArray_Check(ip)) { - dtype = PyArray_DESCR((PyArrayObject *)ip); - Py_INCREF(dtype); - Py_DECREF(ip); - goto promote_types; + if (string_type == NPY_STRING) { + PyObject *temp = PyObject_Str(obj); + if (temp == NULL) { + return NULL; } - Py_XDECREF(ip); - if (PyErr_Occurred()) { - goto fail; + /* assume that when we do the encoding elsewhere we'll use ASCII */ + itemsize = PyUnicode_GetLength(temp); + Py_DECREF(temp); + if (itemsize < 0) { + return NULL; } } - - /* - * If we reached the maximum recursion depth without hitting one - * of the above cases, and obj isn't a sequence-like object, the output - * dtype should be either OBJECT or a user-defined type. - * - * Note that some libraries define sequence-like classes but want them to - * be treated as objects, and they expect numpy to treat it as an object if - * __len__ is not defined. - */ - if (maxdims == 0 || !PySequence_Check(obj) || PySequence_Size(obj) < 0) { - /* clear any PySequence_Size error which corrupts further calls */ - PyErr_Clear(); - - if (*out_dtype == NULL || (*out_dtype)->type_num != NPY_OBJECT) { - Py_XDECREF(*out_dtype); - *out_dtype = PyArray_DescrFromType(NPY_OBJECT); - if (*out_dtype == NULL) { - return -1; - } + else if (string_type == NPY_UNICODE) { + PyObject *temp = PyObject_Str(obj); + if (temp == NULL) { + return NULL; } - return 0; - } - - /* Recursive case, first check the sequence contains only one type */ - seq = PySequence_Fast(obj, "Could not convert object to sequence"); - if (seq == NULL) { - goto fail; - } - size = PySequence_Fast_GET_SIZE(seq); - objects = PySequence_Fast_ITEMS(seq); - common_type = size > 0 ? Py_TYPE(objects[0]) : NULL; - for (i = 1; i < size; ++i) { - if (Py_TYPE(objects[i]) != common_type) { - common_type = NULL; - break; + itemsize = PyUnicode_GetLength(temp); + Py_DECREF(temp); + if (itemsize < 0) { + return NULL; } + itemsize *= 4; /* convert UCS4 codepoints to bytes */ } - - /* all types are the same and scalar, one recursive call is enough */ - if (common_type != NULL && !string_type && - (common_type == &PyFloat_Type || -/* TODO: we could add longs if we add a range check */ -#if !defined(NPY_PY3K) - common_type == &PyInt_Type || -#endif - common_type == &PyBool_Type || - common_type == &PyComplex_Type)) { - size = 1; - } - - /* Recursive call for each sequence item */ - for (i = 0; i < size; ++i) { - int res = PyArray_DTypeFromObjectHelper(objects[i], maxdims - 1, - out_dtype, string_type); - if (res < 0) { - Py_DECREF(seq); - goto fail; - } - else if (res > 0) { - Py_DECREF(seq); - return res; - } + else { + return NULL; } - - Py_DECREF(seq); - - return 0; - - -promote_types: - /* Set 'out_dtype' if it's NULL */ - if (*out_dtype == NULL) { - if (!string_type && dtype->type_num == NPY_STRING) { - Py_DECREF(dtype); - return RETRY_WITH_STRING; - } - if (!string_type && dtype->type_num == NPY_UNICODE) { - Py_DECREF(dtype); - return RETRY_WITH_UNICODE; - } - *out_dtype = dtype; - return 0; + if (last_dtype != NULL && + last_dtype->type_num == string_type && + last_dtype->elsize >= itemsize) { + Py_INCREF(last_dtype); + return last_dtype; } - /* Do type promotion with 'out_dtype' */ - else { - PyArray_Descr *res_dtype = PyArray_PromoteTypes(dtype, *out_dtype); - Py_DECREF(dtype); - if (res_dtype == NULL) { - return -1; - } - if (!string_type && - res_dtype->type_num == NPY_UNICODE && - (*out_dtype)->type_num != NPY_UNICODE) { - Py_DECREF(res_dtype); - return RETRY_WITH_UNICODE; - } - if (!string_type && - res_dtype->type_num == NPY_STRING && - (*out_dtype)->type_num != NPY_STRING) { - Py_DECREF(res_dtype); - return RETRY_WITH_STRING; - } - Py_DECREF(*out_dtype); - *out_dtype = res_dtype; - return 0; + PyArray_Descr *dtype = PyArray_DescrNewFromType(string_type); + if (dtype == NULL) { + return NULL; } - -fail: - Py_XDECREF(*out_dtype); - *out_dtype = NULL; - return -1; + dtype->elsize = itemsize; + return dtype; } -#undef RETRY_WITH_STRING -#undef RETRY_WITH_UNICODE -/* new reference */ -NPY_NO_EXPORT PyArray_Descr * -_array_typedescr_fromstr(char *c_str) +/* + * This function is now identical to the new PyArray_DiscoverDTypeAndShape + * but only returns the the dtype. It should in most cases be slowly phased + * out. (Which may need some refactoring to PyArray_FromAny to make it simpler) + */ +NPY_NO_EXPORT int +PyArray_DTypeFromObject(PyObject *obj, int maxdims, PyArray_Descr **out_dtype) { - PyArray_Descr *descr = NULL; - PyObject *stringobj = PyString_FromString(c_str); + coercion_cache_obj *cache = NULL; + npy_intp shape[NPY_MAXDIMS]; + int ndim; - if (stringobj == NULL) { - return NULL; - } - if (PyArray_DescrConverter(stringobj, &descr) != NPY_SUCCEED) { - Py_DECREF(stringobj); - return NULL; + ndim = PyArray_DiscoverDTypeAndShape( + obj, maxdims, shape, &cache, NULL, NULL, out_dtype, 0); + if (ndim < 0) { + return -1; } - Py_DECREF(stringobj); - return descr; + npy_free_coercion_cache(cache); + return 0; } - NPY_NO_EXPORT char * index2ptr(PyArrayObject *mp, npy_intp i) { @@ -571,7 +149,7 @@ NPY_NO_EXPORT int _zerofill(PyArrayObject *ret) { if (PyDataType_REFCHK(PyArray_DESCR(ret))) { - PyObject *zero = PyInt_FromLong(0); + PyObject *zero = PyLong_FromLong(0); PyArray_FillObjectArray(ret, zero); Py_DECREF(zero); if (PyErr_Occurred()) { @@ -586,105 +164,59 @@ _zerofill(PyArrayObject *ret) return 0; } -NPY_NO_EXPORT int -_IsAligned(PyArrayObject *ap) -{ - int i; - npy_uintp aligned; - npy_uintp alignment = PyArray_DESCR(ap)->alignment; - - /* alignment 1 types should have a efficient alignment for copy loops */ - if (PyArray_ISFLEXIBLE(ap) || PyArray_ISSTRING(ap)) { - npy_intp itemsize = PyArray_ITEMSIZE(ap); - /* power of two sizes may be loaded in larger moves */ - if (((itemsize & (itemsize - 1)) == 0)) { - alignment = itemsize > NPY_MAX_COPY_ALIGNMENT ? - NPY_MAX_COPY_ALIGNMENT : itemsize; - } - else { - /* if not power of two it will be accessed bytewise */ - alignment = 1; - } - } - - if (alignment == 1) { - return 1; - } - aligned = (npy_uintp)PyArray_DATA(ap); - - for (i = 0; i < PyArray_NDIM(ap); i++) { -#if NPY_RELAXED_STRIDES_CHECKING - /* skip dim == 1 as it is not required to have stride 0 */ - if (PyArray_DIM(ap, i) > 1) { - /* if shape[i] == 1, the stride is never used */ - aligned |= (npy_uintp)PyArray_STRIDES(ap)[i]; - } - else if (PyArray_DIM(ap, i) == 0) { - /* an array with zero elements is always aligned */ - return 1; - } -#else /* not NPY_RELAXED_STRIDES_CHECKING */ - aligned |= (npy_uintp)PyArray_STRIDES(ap)[i]; -#endif /* not NPY_RELAXED_STRIDES_CHECKING */ - } - return npy_is_aligned((void *)aligned, alignment); -} - NPY_NO_EXPORT npy_bool _IsWriteable(PyArrayObject *ap) { - PyObject *base=PyArray_BASE(ap); -#if defined(NPY_PY3K) + PyObject *base = PyArray_BASE(ap); Py_buffer view; -#else - void *dummy; - Py_ssize_t n; -#endif - /* If we own our own data, then no-problem */ - if ((base == NULL) || (PyArray_FLAGS(ap) & NPY_ARRAY_OWNDATA)) { + /* + * C-data wrapping arrays may not own their data while not having a base; + * WRITEBACKIFCOPY arrays have a base, but do own their data. + */ + if (base == NULL || PyArray_CHKFLAGS(ap, NPY_ARRAY_OWNDATA)) { + /* + * This is somewhat unsafe for directly wrapped non-writable C-arrays, + * which do not know whether the memory area is writable or not and + * do not own their data (but have no base). + * It would be better if this returned PyArray_ISWRITEABLE(ap). + * Since it is hard to deprecate, this is deprecated only on the Python + * side, but not on in PyArray_UpdateFlags. + */ return NPY_TRUE; } + /* - * Get to the final base object - * If it is a writeable array, then return TRUE - * If we can find an array object - * or a writeable buffer object as the final base object - * or a string object (for pickling support memory savings). - * - this last could be removed if a proper pickleable - * buffer was added to Python. - * - * MW: I think it would better to disallow switching from READONLY - * to WRITEABLE like this... + * Get to the final base object. + * If it is a writeable array, then return True if we can + * find an array object or a writeable buffer object as + * the final base object. */ + while (PyArray_Check(base)) { + ap = (PyArrayObject *)base; + base = PyArray_BASE(ap); - while(PyArray_Check(base)) { - if (PyArray_CHKFLAGS((PyArrayObject *)base, NPY_ARRAY_OWNDATA)) { - return (npy_bool) (PyArray_ISWRITEABLE((PyArrayObject *)base)); + if (PyArray_ISWRITEABLE(ap)) { + /* + * If any base is writeable, it must be OK to switch, note that + * bases are typically collapsed to always point to the most + * general one. + */ + return NPY_TRUE; } - base = PyArray_BASE((PyArrayObject *)base); - } - /* - * here so pickle support works seamlessly - * and unpickled array can be set and reset writeable - * -- could be abused -- - */ - if (PyString_Check(base)) { - return NPY_TRUE; + if (base == NULL || PyArray_CHKFLAGS(ap, NPY_ARRAY_OWNDATA)) { + /* there is no further base to test the writeable flag for */ + return NPY_FALSE; + } + assert(!PyArray_CHKFLAGS(ap, NPY_ARRAY_OWNDATA)); } -#if defined(NPY_PY3K) + if (PyObject_GetBuffer(base, &view, PyBUF_WRITABLE|PyBUF_SIMPLE) < 0) { PyErr_Clear(); return NPY_FALSE; } PyBuffer_Release(&view); -#else - if (PyObject_AsWriteBuffer(base, &dummy, &n) < 0) { - PyErr_Clear(); - return NPY_FALSE; - } -#endif return NPY_TRUE; } @@ -699,10 +231,9 @@ _IsWriteable(PyArrayObject *ap) * @return Python unicode string */ NPY_NO_EXPORT PyObject * -convert_shape_to_string(npy_intp n, npy_intp *vals, char *ending) +convert_shape_to_string(npy_intp n, npy_intp const *vals, char *ending) { npy_intp i; - PyObject *ret, *tmp; /* * Negative dimension indicates "newaxis", which can @@ -712,40 +243,40 @@ convert_shape_to_string(npy_intp n, npy_intp *vals, char *ending) for (i = 0; i < n && vals[i] < 0; i++); if (i == n) { - return PyUString_FromFormat("()%s", ending); - } - else { - ret = PyUString_FromFormat("(%" NPY_INTP_FMT, vals[i++]); - if (ret == NULL) { - return NULL; - } + return PyUnicode_FromFormat("()%s", ending); } + PyObject *ret = PyUnicode_FromFormat("%" NPY_INTP_FMT, vals[i++]); + if (ret == NULL) { + return NULL; + } for (; i < n; ++i) { + PyObject *tmp; + if (vals[i] < 0) { - tmp = PyUString_FromString(",newaxis"); + tmp = PyUnicode_FromString(",newaxis"); } else { - tmp = PyUString_FromFormat(",%" NPY_INTP_FMT, vals[i]); + tmp = PyUnicode_FromFormat(",%" NPY_INTP_FMT, vals[i]); } if (tmp == NULL) { Py_DECREF(ret); return NULL; } - PyUString_ConcatAndDel(&ret, tmp); + Py_SETREF(ret, PyUnicode_Concat(ret, tmp)); + Py_DECREF(tmp); if (ret == NULL) { return NULL; } } if (i == 1) { - tmp = PyUString_FromFormat(",)%s", ending); + Py_SETREF(ret, PyUnicode_FromFormat("(%S,)%s", ret, ending)); } else { - tmp = PyUString_FromFormat(")%s", ending); + Py_SETREF(ret, PyUnicode_FromFormat("(%S)%s", ret, ending)); } - PyUString_ConcatAndDel(&ret, tmp); return ret; } @@ -758,7 +289,7 @@ dot_alignment_error(PyArrayObject *a, int i, PyArrayObject *b, int j) *shape1 = NULL, *shape2 = NULL, *shape1_i = NULL, *shape2_j = NULL; - format = PyUString_FromString("shapes %s and %s not aligned:" + format = PyUnicode_FromString("shapes %s and %s not aligned:" " %d (dim %d) != %d (dim %d)"); shape1 = convert_shape_to_string(PyArray_NDIM(a), PyArray_DIMS(a), ""); @@ -781,7 +312,7 @@ dot_alignment_error(PyArrayObject *a, int i, PyArrayObject *b, int j) goto end; } - errmsg = PyUString_Format(format, fmt_args); + errmsg = PyUnicode_Format(format, fmt_args); if (errmsg != NULL) { PyErr_SetObject(PyExc_ValueError, errmsg); } @@ -821,10 +352,7 @@ _unpack_field(PyObject *value, PyArray_Descr **descr, npy_intp *offset) *descr = (PyArray_Descr *)PyTuple_GET_ITEM(value, 0); off = PyTuple_GET_ITEM(value, 1); - if (PyInt_Check(off)) { - *offset = PyInt_AsSsize_t(off); - } - else if (PyLong_Check(off)) { + if (PyLong_Check(off)) { *offset = PyLong_AsSsize_t(off); } else { @@ -852,3 +380,100 @@ _may_have_objects(PyArray_Descr *dtype) return (PyDataType_HASFIELDS(base) || PyDataType_FLAGCHK(base, NPY_ITEM_HASOBJECT) ); } + +/* + * Make a new empty array, of the passed size, of a type that takes the + * priority of ap1 and ap2 into account. + * + * If `out` is non-NULL, memory overlap is checked with ap1 and ap2, and an + * updateifcopy temporary array may be returned. If `result` is non-NULL, the + * output array to be returned (`out` if non-NULL and the newly allocated array + * otherwise) is incref'd and put to *result. + */ +NPY_NO_EXPORT PyArrayObject * +new_array_for_sum(PyArrayObject *ap1, PyArrayObject *ap2, PyArrayObject* out, + int nd, npy_intp dimensions[], int typenum, PyArrayObject **result) +{ + PyArrayObject *out_buf; + + if (out) { + int d; + + /* verify that out is usable */ + if (PyArray_NDIM(out) != nd || + PyArray_TYPE(out) != typenum || + !PyArray_ISCARRAY(out)) { + PyErr_SetString(PyExc_ValueError, + "output array is not acceptable (must have the right datatype, " + "number of dimensions, and be a C-Array)"); + return 0; + } + for (d = 0; d < nd; ++d) { + if (dimensions[d] != PyArray_DIM(out, d)) { + PyErr_SetString(PyExc_ValueError, + "output array has wrong dimensions"); + return 0; + } + } + + /* check for memory overlap */ + if (!(solve_may_share_memory(out, ap1, 1) == 0 && + solve_may_share_memory(out, ap2, 1) == 0)) { + /* allocate temporary output array */ + out_buf = (PyArrayObject *)PyArray_NewLikeArray(out, NPY_CORDER, + NULL, 0); + if (out_buf == NULL) { + return NULL; + } + + /* set copy-back */ + Py_INCREF(out); + if (PyArray_SetWritebackIfCopyBase(out_buf, out) < 0) { + Py_DECREF(out); + Py_DECREF(out_buf); + return NULL; + } + } + else { + Py_INCREF(out); + out_buf = out; + } + + if (result) { + Py_INCREF(out); + *result = out; + } + + return out_buf; + } + else { + PyTypeObject *subtype; + double prior1, prior2; + /* + * Need to choose an output array that can hold a sum + * -- use priority to determine which subtype. + */ + if (Py_TYPE(ap2) != Py_TYPE(ap1)) { + prior2 = PyArray_GetPriority((PyObject *)ap2, 0.0); + prior1 = PyArray_GetPriority((PyObject *)ap1, 0.0); + subtype = (prior2 > prior1 ? Py_TYPE(ap2) : Py_TYPE(ap1)); + } + else { + prior1 = prior2 = 0.0; + subtype = Py_TYPE(ap1); + } + + out_buf = (PyArrayObject *)PyArray_New(subtype, nd, dimensions, + typenum, NULL, NULL, 0, 0, + (PyObject *) + (prior2 > prior1 ? ap2 : ap1)); + + if (out_buf != NULL && result) { + Py_INCREF(out_buf); + *result = out_buf; + } + + return out_buf; + } +} + diff --git a/numpy/core/src/multiarray/common.h b/numpy/core/src/multiarray/common.h index ae9b960c86f0..85fd3aab1f40 100644 --- a/numpy/core/src/multiarray/common.h +++ b/numpy/core/src/multiarray/common.h @@ -1,8 +1,10 @@ -#ifndef _NPY_PRIVATE_COMMON_H_ -#define _NPY_PRIVATE_COMMON_H_ -#include <numpy/npy_common.h> -#include <numpy/npy_cpu.h> -#include <numpy/ndarraytypes.h> +#ifndef NUMPY_CORE_SRC_MULTIARRAY_COMMON_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_COMMON_H_ + +#include <structmember.h> +#include "numpy/npy_common.h" +#include "numpy/ndarraytypes.h" +#include "npy_import.h" #include <limits.h> #define error_converting(x) (((x) == -1) && PyErr_Occurred()) @@ -18,6 +20,11 @@ #define NPY_BEGIN_THREADS_NDITER(iter) #endif + +NPY_NO_EXPORT PyArray_Descr * +PyArray_DTypeFromObjectStringDiscovery( + PyObject *obj, PyArray_Descr *last_dtype, int string_type); + /* * Recursively examines the object to determine an appropriate dtype * to use for converting to an ndarray. @@ -48,7 +55,7 @@ NPY_NO_EXPORT PyArray_Descr * _array_find_python_scalar_type(PyObject *op); NPY_NO_EXPORT PyArray_Descr * -_array_typedescr_fromstr(char *str); +_array_typedescr_fromstr(char const *str); NPY_NO_EXPORT char * index2ptr(PyArrayObject *mp, npy_intp i); @@ -56,14 +63,11 @@ index2ptr(PyArrayObject *mp, npy_intp i); NPY_NO_EXPORT int _zerofill(PyArrayObject *ret); -NPY_NO_EXPORT int -_IsAligned(PyArrayObject *ap); - NPY_NO_EXPORT npy_bool _IsWriteable(PyArrayObject *ap); NPY_NO_EXPORT PyObject * -convert_shape_to_string(npy_intp n, npy_intp *vals, char *ending); +convert_shape_to_string(npy_intp n, npy_intp const *vals, char *ending); /* * Sets ValueError with "matrices not aligned" message for np.dot and friends @@ -150,13 +154,9 @@ check_and_adjust_axis_msg(int *axis, int ndim, PyObject *msg_prefix) static PyObject *AxisError_cls = NULL; PyObject *exc; + npy_cache_import("numpy.core._exceptions", "AxisError", &AxisError_cls); if (AxisError_cls == NULL) { - PyObject *mod = PyImport_ImportModule("numpy.core._internal"); - - if (mod != NULL) { - AxisError_cls = PyObject_GetAttrString(mod, "AxisError"); - Py_DECREF(mod); - } + return -1; } /* Invoke the AxisError constructor */ @@ -182,6 +182,16 @@ check_and_adjust_axis(int *axis, int ndim) return check_and_adjust_axis_msg(axis, ndim, Py_None); } +/* used for some alignment checks */ +#define _ALIGN(type) offsetof(struct {char c; type v;}, v) +#define _UINT_ALIGN(type) npy_uint_alignment(sizeof(type)) +/* + * Disable harmless compiler warning "4116: unnamed type definition in + * parentheses" which is caused by the _ALIGN macro. + */ +#if defined(_MSC_VER) +#pragma warning(disable:4116) +#endif /* * return true if pointer is aligned to 'alignment' @@ -190,15 +200,45 @@ static NPY_INLINE int npy_is_aligned(const void * p, const npy_uintp alignment) { /* - * alignment is usually a power of two - * the test is faster than a direct modulo + * Assumes alignment is a power of two, as required by the C standard. + * Assumes cast from pointer to uintp gives a sensible representation we + * can use bitwise & on (not required by C standard, but used by glibc). + * This test is faster than a direct modulo. + * Note alignment value of 0 is allowed and returns False. */ - if (NPY_LIKELY((alignment & (alignment - 1)) == 0)) { - return ((npy_uintp)(p) & ((alignment) - 1)) == 0; - } - else { - return ((npy_uintp)(p) % alignment) == 0; + return ((npy_uintp)(p) & ((alignment) - 1)) == 0; +} + +/* Get equivalent "uint" alignment given an itemsize, for use in copy code */ +static NPY_INLINE npy_uintp +npy_uint_alignment(int itemsize) +{ + npy_uintp alignment = 0; /* return value of 0 means unaligned */ + + switch(itemsize){ + case 1: + return 1; + case 2: + alignment = _ALIGN(npy_uint16); + break; + case 4: + alignment = _ALIGN(npy_uint32); + break; + case 8: + alignment = _ALIGN(npy_uint64); + break; + case 16: + /* + * 16 byte types are copied using 2 uint64 assignments. + * See the strided copy function in lowlevel_strided_loops.c. + */ + alignment = _ALIGN(npy_uint64); + break; + default: + break; } + + return alignment; } /* @@ -228,7 +268,7 @@ npy_memchr(char * haystack, char needle, } else { /* usually find elements to skip path */ - if (NPY_CPU_HAVE_UNALIGNED_ACCESS && needle == 0 && stride == 1) { + if (!NPY_ALIGNMENT_REQUIRED && needle == 0 && stride == 1) { /* iterate until last multiple of 4 */ char * block_end = haystack + size - (size % sizeof(unsigned int)); while (p < block_end) { @@ -253,34 +293,84 @@ npy_memchr(char * haystack, char needle, } /* - * Convert NumPy stride to BLAS stride. Returns 0 if conversion cannot be done - * (BLAS won't handle negative or zero strides the way we want). + * Helper to work around issues with the allocation strategy currently + * allocating not 1 byte for empty arrays, but enough for an array where + * all 0 dimensions are replaced with size 1 (if the itemsize is not 0). + * + * This means that we can fill in nice (nonzero) strides and still handle + * slicing direct math without being in danger of leaving the allocated byte + * bounds. + * In practice, that probably does not matter, but in principle this would be + * undefined behaviour in C. Another solution may be to force the strides + * to 0 in these cases. See also gh-15788. + * + * Unlike the code in `PyArray_NewFromDescr` does no overflow checks. */ -static NPY_INLINE int -blas_stride(npy_intp stride, unsigned itemsize) +static NPY_INLINE npy_intp +PyArray_NBYTES_ALLOCATED(PyArrayObject *arr) { - /* - * Should probably check pointer alignment also, but this may cause - * problems if we require complex to be 16 byte aligned. - */ - if (stride > 0 && npy_is_aligned((void *)stride, itemsize)) { - stride /= itemsize; - if (stride <= INT_MAX) { - return stride; + if (PyArray_ITEMSIZE(arr) == 0) { + return 1; + } + npy_intp nbytes = PyArray_ITEMSIZE(arr); + for (int i = 0; i < PyArray_NDIM(arr); i++) { + if (PyArray_DIMS(arr)[i] != 0) { + nbytes *= PyArray_DIMS(arr)[i]; } } - return 0; + return nbytes; } + /* - * Define a chunksize for CBLAS. CBLAS counts in integers. + * Simple helper to create a tuple from an array of items. The `make_null_none` + * flag means that NULL entries are replaced with None, which is occasionally + * useful. */ -#if NPY_MAX_INTP > INT_MAX -# define NPY_CBLAS_CHUNK (INT_MAX / 2 + 1) -#else -# define NPY_CBLAS_CHUNK NPY_MAX_INTP -#endif +static NPY_INLINE PyObject * +PyArray_TupleFromItems(int n, PyObject *const *items, int make_null_none) +{ + PyObject *tuple = PyTuple_New(n); + if (tuple == NULL) { + return NULL; + } + for (int i = 0; i < n; i ++) { + PyObject *tmp; + if (!make_null_none || items[i] != NULL) { + tmp = items[i]; + } + else { + tmp = Py_None; + } + Py_INCREF(tmp); + PyTuple_SET_ITEM(tuple, i, tmp); + } + return tuple; +} + #include "ucsnarrow.h" -#endif +/* + * Make a new empty array, of the passed size, of a type that takes the + * priority of ap1 and ap2 into account. + * + * If `out` is non-NULL, memory overlap is checked with ap1 and ap2, and an + * updateifcopy temporary array may be returned. If `result` is non-NULL, the + * output array to be returned (`out` if non-NULL and the newly allocated array + * otherwise) is incref'd and put to *result. + */ +NPY_NO_EXPORT PyArrayObject * +new_array_for_sum(PyArrayObject *ap1, PyArrayObject *ap2, PyArrayObject* out, + int nd, npy_intp dimensions[], int typenum, PyArrayObject **result); + + +/* + * Used to indicate a broadcast axis, see also `npyiter_get_op_axis` in + * `nditer_constr.c`. This may be the preferred API for reduction axes + * probably. So we should consider making this public either as a macro or + * function (so that the way we flag the axis can be changed). + */ +#define NPY_ITER_REDUCTION_AXIS(axis) (axis + (1 << (NPY_BITSOF_INT - 2))) + +#endif /* NUMPY_CORE_SRC_MULTIARRAY_COMMON_H_ */ diff --git a/numpy/core/src/multiarray/common_dtype.c b/numpy/core/src/multiarray/common_dtype.c new file mode 100644 index 000000000000..ca80b1ed7002 --- /dev/null +++ b/numpy/core/src/multiarray/common_dtype.c @@ -0,0 +1,319 @@ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE + +#define PY_SSIZE_T_CLEAN +#include <Python.h> + +#include "numpy/npy_common.h" +#include "numpy/arrayobject.h" + +#include "common_dtype.h" +#include "dtypemeta.h" +#include "abstractdtypes.h" + + +/* + * This file defines all logic necessary for generic "common dtype" + * operations. This is unfortunately surprisingly complicated to get right + * due to the value based logic NumPy uses and the fact that NumPy has + * no clear (non-transitive) type promotion hierarchy. + * Unlike most languages `int32 + float32 -> float64` instead of `float32`. + * The other complicated thing is value-based-promotion, which means that + * in many cases a Python 1, may end up as an `int8` or `uint8`. + * + * This file implements the necessary logic so that `np.result_type(...)` + * can give the correct result for any order of inputs and can further + * generalize to user DTypes. + */ + + +/** + * This function defines the common DType operator. + * + * Note that the common DType will not be "object" (unless one of the dtypes + * is object), even though object can technically represent all values + * correctly. + * + * TODO: Before exposure, we should review the return value (e.g. no error + * when no common DType is found). + * + * @param dtype1 DType class to find the common type for. + * @param dtype2 Second DType class. + * @return The common DType or NULL with an error set + */ +NPY_NO_EXPORT NPY_INLINE PyArray_DTypeMeta * +PyArray_CommonDType(PyArray_DTypeMeta *dtype1, PyArray_DTypeMeta *dtype2) +{ + if (dtype1 == dtype2) { + Py_INCREF(dtype1); + return dtype1; + } + + PyArray_DTypeMeta *common_dtype; + + common_dtype = NPY_DT_CALL_common_dtype(dtype1, dtype2); + if (common_dtype == (PyArray_DTypeMeta *)Py_NotImplemented) { + Py_DECREF(common_dtype); + common_dtype = NPY_DT_CALL_common_dtype(dtype2, dtype1); + } + if (common_dtype == NULL) { + return NULL; + } + if (common_dtype == (PyArray_DTypeMeta *)Py_NotImplemented) { + Py_DECREF(Py_NotImplemented); + PyErr_Format(PyExc_TypeError, + "The DTypes %S and %S do not have a common DType. " + "For example they cannot be stored in a single array unless " + "the dtype is `object`.", dtype1, dtype2); + return NULL; + } + return common_dtype; +} + + +/** + * This function takes a list of dtypes and "reduces" them (in a sense, + * it finds the maximal dtype). Note that "maximum" here is defined by + * knowledge (or category or domain). A user DType must always "know" + * about all NumPy dtypes, floats "know" about integers, integers "know" + * about unsigned integers. + * + * c + * / \ + * a \ <-- The actual promote(a, b) may be c or unknown. + * / \ \ + * a b c + * + * The reduction is done "pairwise". In the above `a.__common_dtype__(b)` + * has a result (so `a` knows more) and `a.__common_dtype__(c)` returns + * NotImplemented (so `c` knows more). You may notice that the result + * `res = a.__common_dtype__(b)` is not important. We could try to use it + * to remove the whole branch if `res is c` or by checking if + * `c.__common_dtype(res) is c`. + * Right now, we only clear initial elements in the most simple case where + * `a.__common_dtype(b) is a` (and thus `b` cannot alter the end-result). + * Clearing means, we do not have to worry about them later. + * + * There is one further subtlety. If we have an abstract DType and a + * non-abstract one, we "prioritize" the non-abstract DType here. + * In this sense "prioritizing" means that we use: + * abstract.__common_dtype__(other) + * If both return NotImplemented (which is acceptable and even expected in + * this case, see later) then `other` will be considered to know more. + * + * The reason why this may be acceptable for abstract DTypes, is that + * the value-dependent abstract DTypes may provide default fall-backs. + * The priority inversion effectively means that abstract DTypes are ordered + * just below their concrete counterparts. + * (This fall-back is convenient but not perfect, it can lead to + * non-minimal promotions: e.g. `np.uint24 + 2**20 -> int32`. And such + * cases may also be possible in some mixed type scenarios; they can be + * avoided by defining the promotion explicitly in the user DType.) + * + * @param length Number of DTypes + * @param dtypes + */ +static PyArray_DTypeMeta * +reduce_dtypes_to_most_knowledgeable( + npy_intp length, PyArray_DTypeMeta **dtypes) +{ + assert(length >= 2); + npy_intp half = length / 2; + + PyArray_DTypeMeta *res = NULL; + + for (npy_intp low = 0; low < half; low++) { + npy_intp high = length - 1 - low; + if (dtypes[high] == dtypes[low]) { + Py_INCREF(dtypes[low]); + Py_XSETREF(res, dtypes[low]); + } + else { + if (NPY_DT_is_abstract(dtypes[high])) { + /* + * Priority inversion, start with abstract, because if it + * returns `other`, we can let other pass instead. + */ + PyArray_DTypeMeta *tmp = dtypes[low]; + dtypes[low] = dtypes[high]; + dtypes[high] = tmp; + } + + Py_XSETREF(res, NPY_DT_CALL_common_dtype(dtypes[low], dtypes[high])); + if (res == NULL) { + return NULL; + } + } + + if (res == (PyArray_DTypeMeta *)Py_NotImplemented) { + PyArray_DTypeMeta *tmp = dtypes[low]; + dtypes[low] = dtypes[high]; + dtypes[high] = tmp; + } + if (res == dtypes[low]) { + /* `dtypes[high]` cannot influence the final result, so clear: */ + dtypes[high] = NULL; + } + } + + if (length == 2) { + return res; + } + Py_DECREF(res); + return reduce_dtypes_to_most_knowledgeable(length - half, dtypes); +} + + +/** + * Promotes a list of DTypes with each other in a way that should guarantee + * stable results even when changing the order. + * + * In general this approach always works as long as the most generic dtype + * is either strictly larger, or compatible with all other dtypes. + * For example promoting float16 with any other float, integer, or unsigned + * integer again gives a floating point number. And any floating point number + * promotes in the "same way" as `float16`. + * If a user inserts more than one type into the NumPy type hierarchy, this + * can break. Given: + * uint24 + int32 -> int48 # Promotes to a *new* dtype! + * + * The following becomes problematic (order does not matter): + * uint24 + int16 + uint32 -> int64 + * <== (uint24 + int16) + (uint24 + uint32) -> int64 + * <== int32 + uint32 -> int64 + * + * It is impossible to achieve an `int48` result in the above. + * + * This is probably only resolvable by asking `uint24` to take over the + * whole reduction step; which we currently do not do. + * (It may be possible to notice the last up-cast and implement use something + * like: `uint24.nextafter(int32).__common_dtype__(uint32)`, but that seems + * even harder to grasp.) + * + * Note that a case where two dtypes are mixed (and know nothing about each + * other) will always generate an error: + * uint24 + int48 + int64 -> Error + * + * Even though `int64` is a safe solution, since `uint24 + int64 -> int64` and + * `int48 + int64 -> int64` and `int64` and there cannot be a smaller solution. + * + * //TODO: Maybe this function should allow not setting an error? + * + * @param length Number of dtypes (and values) must be at least 1 + * @param dtypes The concrete or abstract DTypes to promote + * @return NULL or the promoted DType. + */ +NPY_NO_EXPORT PyArray_DTypeMeta * +PyArray_PromoteDTypeSequence( + npy_intp length, PyArray_DTypeMeta **dtypes_in) +{ + if (length == 1) { + Py_INCREF(dtypes_in[0]); + return dtypes_in[0]; + } + PyArray_DTypeMeta *result = NULL; + + /* Copy dtypes so that we can reorder them (only allocate when many) */ + PyObject *_scratch_stack[NPY_MAXARGS]; + PyObject **_scratch_heap = NULL; + PyArray_DTypeMeta **dtypes = (PyArray_DTypeMeta **)_scratch_stack; + + if (length > NPY_MAXARGS) { + _scratch_heap = PyMem_Malloc(length * sizeof(PyObject *)); + if (_scratch_heap == NULL) { + PyErr_NoMemory(); + return NULL; + } + dtypes = (PyArray_DTypeMeta **)_scratch_heap; + } + + memcpy(dtypes, dtypes_in, length * sizeof(PyObject *)); + + /* + * `result` is the last promotion result, which can usually be reused if + * it is not NotImplemneted. + * The passed in dtypes are partially sorted (and cleared, when clearly + * not relevant anymore). + * `dtypes[0]` will be the most knowledgeable (highest category) which + * we consider the "main_dtype" here. + */ + result = reduce_dtypes_to_most_knowledgeable(length, dtypes); + if (result == NULL) { + goto finish; + } + PyArray_DTypeMeta *main_dtype = dtypes[0]; + + npy_intp reduce_start = 1; + if (result == (PyArray_DTypeMeta *)Py_NotImplemented) { + Py_SETREF(result, NULL); + } + else { + /* (new) first value is already taken care of in `result` */ + reduce_start = 2; + } + /* + * At this point, we have only looked at every DType at most once. + * The `main_dtype` must know all others (or it will be a failure) and + * all dtypes returned by its `common_dtype` must be guaranteed to succeed + * promotion with one another. + * It is the job of the "main DType" to ensure that at this point order + * is irrelevant. + * If this turns out to be a limitation, this "reduction" will have to + * become a default version and we have to allow DTypes to override it. + */ + PyArray_DTypeMeta *prev = NULL; + for (npy_intp i = reduce_start; i < length; i++) { + if (dtypes[i] == NULL || dtypes[i] == prev) { + continue; + } + /* + * "Promote" the current dtype with the main one (which should be + * a higher category). We assume that the result is not in a lower + * category. + */ + PyArray_DTypeMeta *promotion = NPY_DT_CALL_common_dtype( + main_dtype, dtypes[i]); + if (promotion == NULL) { + Py_XSETREF(result, NULL); + goto finish; + } + else if ((PyObject *)promotion == Py_NotImplemented) { + Py_DECREF(Py_NotImplemented); + Py_XSETREF(result, NULL); + PyObject *dtypes_in_tuple = PyTuple_New(length); + if (dtypes_in_tuple == NULL) { + goto finish; + } + for (npy_intp l=0; l < length; l++) { + Py_INCREF(dtypes_in[l]); + PyTuple_SET_ITEM(dtypes_in_tuple, l, (PyObject *)dtypes_in[l]); + } + PyErr_Format(PyExc_TypeError, + "The DType %S could not be promoted by %S. This means that " + "no common DType exists for the given inputs. " + "For example they cannot be stored in a single array unless " + "the dtype is `object`. The full list of DTypes is: %S", + dtypes[i], main_dtype, dtypes_in_tuple); + Py_DECREF(dtypes_in_tuple); + goto finish; + } + if (result == NULL) { + result = promotion; + continue; + } + + /* + * The above promoted, now "reduce" with the current result; note that + * in the typical cases we expect this step to be a no-op. + */ + Py_SETREF(result, PyArray_CommonDType(result, promotion)); + Py_DECREF(promotion); + if (result == NULL) { + goto finish; + } + } + + finish: + PyMem_Free(_scratch_heap); + return result; +} diff --git a/numpy/core/src/multiarray/common_dtype.h b/numpy/core/src/multiarray/common_dtype.h new file mode 100644 index 000000000000..13d38ddf816a --- /dev/null +++ b/numpy/core/src/multiarray/common_dtype.h @@ -0,0 +1,17 @@ +#ifndef NUMPY_CORE_SRC_MULTIARRAY_COMMON_DTYPE_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_COMMON_DTYPE_H_ + +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE + +#include <numpy/ndarraytypes.h> +#include "dtypemeta.h" + +NPY_NO_EXPORT PyArray_DTypeMeta * +PyArray_CommonDType(PyArray_DTypeMeta *dtype1, PyArray_DTypeMeta *dtype2); + +NPY_NO_EXPORT PyArray_DTypeMeta * +PyArray_PromoteDTypeSequence( + npy_intp length, PyArray_DTypeMeta **dtypes_in); + +#endif /* NUMPY_CORE_SRC_MULTIARRAY_COMMON_DTYPE_H_ */ diff --git a/numpy/core/src/multiarray/compiled_base.c b/numpy/core/src/multiarray/compiled_base.c index bcb44f6d1054..5853e068b0b7 100644 --- a/numpy/core/src/multiarray/compiled_base.c +++ b/numpy/core/src/multiarray/compiled_base.c @@ -1,9 +1,10 @@ #define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE + +#define PY_SSIZE_T_CLEAN #include <Python.h> #include <structmember.h> -#include <string.h> -#define _MULTIARRAYMODULE #include "numpy/arrayobject.h" #include "numpy/npy_3kcompat.h" #include "numpy/npy_math.h" @@ -13,7 +14,14 @@ #include "alloc.h" #include "ctors.h" #include "common.h" +#include "simd/simd.h" +#include <string.h> + +typedef enum { + PACK_ORDER_LITTLE = 0, + PACK_ORDER_BIG +} PACK_ORDER; /* * Returns -1 if the array is monotonic decreasing, @@ -21,11 +29,17 @@ * and 0 if the array is not monotonic. */ static int -check_array_monotonic(const double *a, npy_int lena) +check_array_monotonic(const double *a, npy_intp lena) { npy_intp i; double next; - double last = a[0]; + double last; + + if (lena == 0) { + /* all bin edges hold the same value */ + return 1; + } + last = a[0]; /* Skip repeated values at the beginning of the array */ for (i = 1; (i < lena) && (a[i] == last); i++); @@ -209,106 +223,41 @@ arr_bincount(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds) return NULL; } -/* - * digitize(x, bins, right=False) returns an array of integers the same length - * as x. The values i returned are such that bins[i - 1] <= x < bins[i] if - * bins is monotonically increasing, or bins[i - 1] > x >= bins[i] if bins - * is monotonically decreasing. Beyond the bounds of bins, returns either - * i = 0 or i = len(bins) as appropriate. If right == True the comparison - * is bins [i - 1] < x <= bins[i] or bins [i - 1] >= x > bins[i] - */ +/* Internal function to expose check_array_monotonic to python */ NPY_NO_EXPORT PyObject * -arr_digitize(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds) +arr__monotonicity(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds) { + static char *kwlist[] = {"x", NULL}; PyObject *obj_x = NULL; - PyObject *obj_bins = NULL; PyArrayObject *arr_x = NULL; - PyArrayObject *arr_bins = NULL; - PyObject *ret = NULL; - npy_intp len_bins; - int monotonic, right = 0; - NPY_BEGIN_THREADS_DEF - - static char *kwlist[] = {"x", "bins", "right", NULL}; + long monotonic; + npy_intp len_x; + NPY_BEGIN_THREADS_DEF; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|i:digitize", kwlist, - &obj_x, &obj_bins, &right)) { - goto fail; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|_monotonicity", kwlist, + &obj_x)) { + return NULL; } - /* PyArray_SearchSorted will make `x` contiguous even if we don't */ - arr_x = (PyArrayObject *)PyArray_FROMANY(obj_x, NPY_DOUBLE, 0, 0, - NPY_ARRAY_CARRAY_RO); + /* + * TODO: + * `x` could be strided, needs change to check_array_monotonic + * `x` is forced to double for this check + */ + arr_x = (PyArrayObject *)PyArray_FROMANY( + obj_x, NPY_DOUBLE, 1, 1, NPY_ARRAY_CARRAY_RO); if (arr_x == NULL) { - goto fail; - } - - /* TODO: `bins` could be strided, needs change to check_array_monotonic */ - arr_bins = (PyArrayObject *)PyArray_FROMANY(obj_bins, NPY_DOUBLE, 1, 1, - NPY_ARRAY_CARRAY_RO); - if (arr_bins == NULL) { - goto fail; - } - - len_bins = PyArray_SIZE(arr_bins); - if (len_bins == 0) { - PyErr_SetString(PyExc_ValueError, "bins must have non-zero length"); - goto fail; + return NULL; } - NPY_BEGIN_THREADS_THRESHOLDED(len_bins) - monotonic = check_array_monotonic((const double *)PyArray_DATA(arr_bins), - len_bins); + len_x = PyArray_SIZE(arr_x); + NPY_BEGIN_THREADS_THRESHOLDED(len_x) + monotonic = check_array_monotonic( + (const double *)PyArray_DATA(arr_x), len_x); NPY_END_THREADS + Py_DECREF(arr_x); - if (monotonic == 0) { - PyErr_SetString(PyExc_ValueError, - "bins must be monotonically increasing or decreasing"); - goto fail; - } - - /* PyArray_SearchSorted needs an increasing array */ - if (monotonic == - 1) { - PyArrayObject *arr_tmp = NULL; - npy_intp shape = PyArray_DIM(arr_bins, 0); - npy_intp stride = -PyArray_STRIDE(arr_bins, 0); - void *data = (void *)(PyArray_BYTES(arr_bins) - stride * (shape - 1)); - - arr_tmp = (PyArrayObject *)PyArray_NewFromDescrAndBase( - &PyArray_Type, PyArray_DescrFromType(NPY_DOUBLE), - 1, &shape, &stride, data, - PyArray_FLAGS(arr_bins), NULL, (PyObject *)arr_bins); - Py_DECREF(arr_bins); - if (!arr_tmp) { - goto fail; - } - arr_bins = arr_tmp; - } - - ret = PyArray_SearchSorted(arr_bins, (PyObject *)arr_x, - right ? NPY_SEARCHLEFT : NPY_SEARCHRIGHT, NULL); - if (!ret) { - goto fail; - } - - /* If bins is decreasing, ret has bins from end, not start */ - if (monotonic == -1) { - npy_intp *ret_data = - (npy_intp *)PyArray_DATA((PyArrayObject *)ret); - npy_intp len_ret = PyArray_SIZE((PyArrayObject *)ret); - - NPY_BEGIN_THREADS_THRESHOLDED(len_ret) - while (len_ret--) { - *ret_data = len_bins - *ret_data; - ret_data++; - } - NPY_END_THREADS - } - - fail: - Py_XDECREF(arr_x); - Py_XDECREF(arr_bins); - return ret; + return PyLong_FromLong(monotonic); } /* @@ -387,6 +336,7 @@ arr_insert(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwdict) } else { Py_XDECREF(values); Py_XDECREF(mask); + PyArray_ResolveWritebackIfCopy(array); Py_XDECREF(array); Py_RETURN_NONE; } @@ -417,6 +367,7 @@ arr_insert(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwdict) fail: Py_XDECREF(mask); + PyArray_ResolveWritebackIfCopy(array); Py_XDECREF(array); Py_XDECREF(values); return NULL; @@ -424,6 +375,18 @@ arr_insert(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwdict) #define LIKELY_IN_CACHE_SIZE 8 +#ifdef __INTEL_COMPILER +#pragma intel optimization_level 0 +#endif +static NPY_INLINE npy_intp +_linear_search(const npy_double key, const npy_double *arr, const npy_intp len, const npy_intp i0) +{ + npy_intp i; + + for (i = i0; i < len && key >= arr[i]; i++); + return i - 1; +} + /** @brief find index of a sorted array such that arr[i] <= key < arr[i + 1]. * * If an starting index guess is in-range, the array values around this @@ -463,10 +426,7 @@ binary_search_with_guess(const npy_double key, const npy_double *arr, * From above we know key >= arr[0] when we start. */ if (len <= 4) { - npy_intp i; - - for (i = 1; i < len && key >= arr[i]; ++i); - return i - 1; + return _linear_search(key, arr, len, 1); } if (guess > len - 3) { @@ -624,6 +584,7 @@ arr_interp(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwdict) if (lenxp <= lenx) { slopes = PyArray_malloc((lenxp - 1) * sizeof(npy_double)); if (slopes == NULL) { + PyErr_NoMemory(); goto fail; } } @@ -654,10 +615,23 @@ arr_interp(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwdict) else if (j == lenxp - 1) { dres[i] = dy[j]; } + else if (dx[j] == x_val) { + /* Avoid potential non-finite interpolation */ + dres[i] = dy[j]; + } else { - const npy_double slope = (slopes != NULL) ? slopes[j] : - (dy[j+1] - dy[j]) / (dx[j+1] - dx[j]); + const npy_double slope = + (slopes != NULL) ? slopes[j] : + (dy[j+1] - dy[j]) / (dx[j+1] - dx[j]); + + /* If we get nan in one direction, try the other */ dres[i] = slope*(x_val - dx[j]) + dy[j]; + if (NPY_UNLIKELY(npy_isnan(dres[i]))) { + dres[i] = slope*(x_val - dx[j+1]) + dy[j+1]; + if (NPY_UNLIKELY(npy_isnan(dres[i])) && dy[j] == dy[j+1]) { + dres[i] = dy[j]; + } + } } } @@ -789,6 +763,7 @@ arr_interp_complex(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwdict) if (lenxp <= lenx) { slopes = PyArray_malloc((lenxp - 1) * sizeof(npy_cdouble)); if (slopes == NULL) { + PyErr_NoMemory(); goto fail; } } @@ -822,17 +797,37 @@ arr_interp_complex(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwdict) else if (j == lenxp - 1) { dres[i] = dy[j]; } + else if (dx[j] == x_val) { + /* Avoid potential non-finite interpolation */ + dres[i] = dy[j]; + } else { - if (slopes!=NULL) { - dres[i].real = slopes[j].real*(x_val - dx[j]) + dy[j].real; - dres[i].imag = slopes[j].imag*(x_val - dx[j]) + dy[j].imag; + npy_cdouble slope; + if (slopes != NULL) { + slope = slopes[j]; } else { const npy_double inv_dx = 1.0 / (dx[j+1] - dx[j]); - dres[i].real = (dy[j+1].real - dy[j].real)*(x_val - dx[j])* - inv_dx + dy[j].real; - dres[i].imag = (dy[j+1].imag - dy[j].imag)*(x_val - dx[j])* - inv_dx + dy[j].imag; + slope.real = (dy[j+1].real - dy[j].real) * inv_dx; + slope.imag = (dy[j+1].imag - dy[j].imag) * inv_dx; + } + + /* If we get nan in one direction, try the other */ + dres[i].real = slope.real*(x_val - dx[j]) + dy[j].real; + if (NPY_UNLIKELY(npy_isnan(dres[i].real))) { + dres[i].real = slope.real*(x_val - dx[j+1]) + dy[j+1].real; + if (NPY_UNLIKELY(npy_isnan(dres[i].real)) && + dy[j].real == dy[j+1].real) { + dres[i].real = dy[j].real; + } + } + dres[i].imag = slope.imag*(x_val - dx[j]) + dy[j].imag; + if (NPY_UNLIKELY(npy_isnan(dres[i].imag))) { + dres[i].imag = slope.imag*(x_val - dx[j+1]) + dy[j+1].imag; + if (NPY_UNLIKELY(npy_isnan(dres[i].imag)) && + dy[j].imag == dy[j+1].imag) { + dres[i].imag = dy[j].imag; + } } } } @@ -854,17 +849,63 @@ arr_interp_complex(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwdict) return NULL; } +static const char *EMPTY_SEQUENCE_ERR_MSG = "indices must be integral: the provided " \ + "empty sequence was inferred as float. Wrap it with " \ + "'np.array(indices, dtype=np.intp)'"; + +static const char *NON_INTEGRAL_ERROR_MSG = "only int indices permitted"; + +/* Convert obj to an ndarray with integer dtype or fail */ +static PyArrayObject * +astype_anyint(PyObject *obj) { + PyArrayObject *ret; + + if (!PyArray_Check(obj)) { + /* prefer int dtype */ + PyArray_Descr *dtype_guess = NULL; + if (PyArray_DTypeFromObject(obj, NPY_MAXDIMS, &dtype_guess) < 0) { + return NULL; + } + if (dtype_guess == NULL) { + if (PySequence_Check(obj) && PySequence_Size(obj) == 0) { + PyErr_SetString(PyExc_TypeError, EMPTY_SEQUENCE_ERR_MSG); + } + return NULL; + } + ret = (PyArrayObject*)PyArray_FromAny(obj, dtype_guess, 0, 0, 0, NULL); + if (ret == NULL) { + return NULL; + } + } + else { + ret = (PyArrayObject *)obj; + Py_INCREF(ret); + } + + if (!(PyArray_ISINTEGER(ret) || PyArray_ISBOOL(ret))) { + /* ensure dtype is int-based */ + PyErr_SetString(PyExc_TypeError, NON_INTEGRAL_ERROR_MSG); + Py_DECREF(ret); + return NULL; + } + + return ret; +} + /* * Converts a Python sequence into 'count' PyArrayObjects * - * seq - Input Python object, usually a tuple but any sequence works. - * op - Where the arrays are placed. - * count - How many arrays there should be (errors if it doesn't match). - * paramname - The name of the parameter that produced 'seq'. + * seq - Input Python object, usually a tuple but any sequence works. + * Must have integral content. + * paramname - The name of the parameter that produced 'seq'. + * count - How many arrays there should be (errors if it doesn't match). + * op - Where the arrays are placed. */ -static int sequence_to_arrays(PyObject *seq, - PyArrayObject **op, int count, - char *paramname) +static int int_sequence_to_arrays(PyObject *seq, + char *paramname, + int count, + PyArrayObject **op + ) { int i; @@ -878,30 +919,26 @@ static int sequence_to_arrays(PyObject *seq, for (i = 0; i < count; ++i) { PyObject *item = PySequence_GetItem(seq, i); if (item == NULL) { - while (--i >= 0) { - Py_DECREF(op[i]); - op[i] = NULL; - } - return -1; + goto fail; } - - op[i] = (PyArrayObject *)PyArray_FROM_O(item); + op[i] = astype_anyint(item); + Py_DECREF(item); if (op[i] == NULL) { - while (--i >= 0) { - Py_DECREF(op[i]); - op[i] = NULL; - } - Py_DECREF(item); - return -1; + goto fail; } - - Py_DECREF(item); } return 0; + +fail: + while (--i >= 0) { + Py_XDECREF(op[i]); + op[i] = NULL; + } + return -1; } -/* Inner loop for unravel_index */ +/* Inner loop for ravel_multi_index */ static int ravel_multi_index_loop(int ravel_ndim, npy_intp *ravel_dims, npy_intp *ravel_strides, @@ -913,6 +950,20 @@ ravel_multi_index_loop(int ravel_ndim, npy_intp *ravel_dims, char invalid; npy_intp j, m; + /* + * Check for 0-dimensional axes unless there is nothing to do. + * An empty array/shape cannot be indexed at all. + */ + if (count != 0) { + for (i = 0; i < ravel_ndim; ++i) { + if (ravel_dims[i] == 0) { + PyErr_SetString(PyExc_ValueError, + "cannot unravel if shape has zero entries (is empty)."); + return NPY_FAIL; + } + } + } + NPY_BEGIN_ALLOW_THREADS; invalid = 0; while (count--) { @@ -989,7 +1040,7 @@ arr_ravel_multi_index(PyObject *self, PyObject *args, PyObject *kwds) NpyIter *iter = NULL; - char *kwlist[] = {"multi_index", "dims", "mode", "order", NULL}; + static char *kwlist[] = {"multi_index", "dims", "mode", "order", NULL}; memset(op, 0, sizeof(op)); dtype[0] = NULL; @@ -1045,11 +1096,10 @@ arr_ravel_multi_index(PyObject *self, PyObject *args, PyObject *kwds) } /* Get the multi_index into op */ - if (sequence_to_arrays(coords0, op, dimensions.len, "multi_index") < 0) { + if (int_sequence_to_arrays(coords0, "multi_index", dimensions.len, op) < 0) { goto fail; } - for (i = 0; i < dimensions.len; ++i) { op_flags[i] = NPY_ITER_READONLY| NPY_ITER_ALIGNED; @@ -1116,67 +1166,44 @@ arr_ravel_multi_index(PyObject *self, PyObject *args, PyObject *kwds) return NULL; } -/* C-order inner loop for unravel_index */ -static int -unravel_index_loop_corder(int unravel_ndim, npy_intp *unravel_dims, - npy_intp unravel_size, npy_intp count, - char *indices, npy_intp indices_stride, - npy_intp *coords) -{ - int i; - char invalid; - npy_intp val; - NPY_BEGIN_ALLOW_THREADS; - invalid = 0; - while (count--) { - val = *(npy_intp *)indices; - if (val < 0 || val >= unravel_size) { - invalid = 1; - break; - } - for (i = unravel_ndim-1; i >= 0; --i) { - coords[i] = val % unravel_dims[i]; - val /= unravel_dims[i]; - } - coords += unravel_ndim; - indices += indices_stride; - } - NPY_END_ALLOW_THREADS; - if (invalid) { - PyErr_Format(PyExc_ValueError, - "index %" NPY_INTP_FMT " is out of bounds for array with size " - "%" NPY_INTP_FMT, - val, unravel_size - ); - return NPY_FAIL; - } - return NPY_SUCCEED; -} - -/* Fortran-order inner loop for unravel_index */ +/* + * Inner loop for unravel_index + * order must be NPY_CORDER or NPY_FORTRANORDER + */ static int -unravel_index_loop_forder(int unravel_ndim, npy_intp *unravel_dims, - npy_intp unravel_size, npy_intp count, - char *indices, npy_intp indices_stride, - npy_intp *coords) +unravel_index_loop(int unravel_ndim, npy_intp const *unravel_dims, + npy_intp unravel_size, npy_intp count, + char *indices, npy_intp indices_stride, + npy_intp *coords, NPY_ORDER order) { - int i; - char invalid; - npy_intp val; + int i, idx; + int idx_start = (order == NPY_CORDER) ? unravel_ndim - 1: 0; + int idx_step = (order == NPY_CORDER) ? -1 : 1; + char invalid = 0; + npy_intp val = 0; NPY_BEGIN_ALLOW_THREADS; - invalid = 0; + /* NPY_KEEPORDER or NPY_ANYORDER have no meaning in this setting */ + assert(order == NPY_CORDER || order == NPY_FORTRANORDER); while (count--) { val = *(npy_intp *)indices; if (val < 0 || val >= unravel_size) { invalid = 1; break; } + idx = idx_start; for (i = 0; i < unravel_ndim; ++i) { - *coords++ = val % unravel_dims[i]; - val /= unravel_dims[i]; + /* + * Using a local seems to enable single-divide optimization + * but only if the / precedes the % + */ + npy_intp tmp = val / unravel_dims[idx]; + coords[idx] = val % unravel_dims[idx]; + val = tmp; + idx += idx_step; } + coords += unravel_ndim; indices += indices_stride; } NPY_END_ALLOW_THREADS; @@ -1195,11 +1222,12 @@ unravel_index_loop_forder(int unravel_ndim, npy_intp *unravel_dims, NPY_NO_EXPORT PyObject * arr_unravel_index(PyObject *self, PyObject *args, PyObject *kwds) { - PyObject *indices0 = NULL, *ret_tuple = NULL; + PyObject *indices0 = NULL; + PyObject *ret_tuple = NULL; PyArrayObject *ret_arr = NULL; PyArrayObject *indices = NULL; PyArray_Descr *dtype = NULL; - PyArray_Dims dimensions={0,0}; + PyArray_Dims dimensions = {0, 0}; NPY_ORDER order = NPY_CORDER; npy_intp unravel_size; @@ -1207,7 +1235,7 @@ arr_unravel_index(PyObject *self, PyObject *args, PyObject *kwds) int i, ret_ndim; npy_intp ret_dims[NPY_MAXDIMS], ret_strides[NPY_MAXDIMS]; - char *kwlist[] = {"indices", "dims", "order", NULL}; + static char *kwlist[] = {"indices", "shape", "order", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO&|O&:unravel_index", kwlist, @@ -1217,17 +1245,17 @@ arr_unravel_index(PyObject *self, PyObject *args, PyObject *kwds) goto fail; } - unravel_size = PyArray_MultiplyList(dimensions.ptr, dimensions.len); - - if (!PyArray_Check(indices0)) { - indices = (PyArrayObject*)PyArray_FROM_O(indices0); - if (indices == NULL) { - goto fail; - } + unravel_size = PyArray_OverflowMultiplyList(dimensions.ptr, dimensions.len); + if (unravel_size == -1) { + PyErr_SetString(PyExc_ValueError, + "dimensions are too large; arrays and shapes with " + "a total size greater than 'intp' are not supported."); + goto fail; } - else { - indices = (PyArrayObject *)indices0; - Py_INCREF(indices); + + indices = astype_anyint(indices0); + if (indices == NULL) { + goto fail; } dtype = PyArray_DescrFromType(NPY_INTP); @@ -1277,64 +1305,35 @@ arr_unravel_index(PyObject *self, PyObject *args, PyObject *kwds) goto fail; } - if (order == NPY_CORDER) { - if (NpyIter_GetIterSize(iter) != 0) { - NpyIter_IterNextFunc *iternext; - char **dataptr; - npy_intp *strides; - npy_intp *countptr, count; - npy_intp *coordsptr = (npy_intp *)PyArray_DATA(ret_arr); + if (order != NPY_CORDER && order != NPY_FORTRANORDER) { + PyErr_SetString(PyExc_ValueError, + "only 'C' or 'F' order is permitted"); + goto fail; + } + if (NpyIter_GetIterSize(iter) != 0) { + NpyIter_IterNextFunc *iternext; + char **dataptr; + npy_intp *strides; + npy_intp *countptr, count; + npy_intp *coordsptr = (npy_intp *)PyArray_DATA(ret_arr); - iternext = NpyIter_GetIterNext(iter, NULL); - if (iternext == NULL) { - goto fail; - } - dataptr = NpyIter_GetDataPtrArray(iter); - strides = NpyIter_GetInnerStrideArray(iter); - countptr = NpyIter_GetInnerLoopSizePtr(iter); - - do { - count = *countptr; - if (unravel_index_loop_corder(dimensions.len, dimensions.ptr, - unravel_size, count, *dataptr, *strides, - coordsptr) != NPY_SUCCEED) { - goto fail; - } - coordsptr += count*dimensions.len; - } while(iternext(iter)); + iternext = NpyIter_GetIterNext(iter, NULL); + if (iternext == NULL) { + goto fail; } - } - else if (order == NPY_FORTRANORDER) { - if (NpyIter_GetIterSize(iter) != 0) { - NpyIter_IterNextFunc *iternext; - char **dataptr; - npy_intp *strides; - npy_intp *countptr, count; - npy_intp *coordsptr = (npy_intp *)PyArray_DATA(ret_arr); + dataptr = NpyIter_GetDataPtrArray(iter); + strides = NpyIter_GetInnerStrideArray(iter); + countptr = NpyIter_GetInnerLoopSizePtr(iter); - iternext = NpyIter_GetIterNext(iter, NULL); - if (iternext == NULL) { + do { + count = *countptr; + if (unravel_index_loop(dimensions.len, dimensions.ptr, + unravel_size, count, *dataptr, *strides, + coordsptr, order) != NPY_SUCCEED) { goto fail; } - dataptr = NpyIter_GetDataPtrArray(iter); - strides = NpyIter_GetInnerStrideArray(iter); - countptr = NpyIter_GetInnerLoopSizePtr(iter); - - do { - count = *countptr; - if (unravel_index_loop_forder(dimensions.len, dimensions.ptr, - unravel_size, count, *dataptr, *strides, - coordsptr) != NPY_SUCCEED) { - goto fail; - } - coordsptr += count*dimensions.len; - } while(iternext(iter)); - } - } - else { - PyErr_SetString(PyExc_ValueError, - "only 'C' or 'F' order is permitted"); - goto fail; + coordsptr += count * dimensions.len; + } while (iternext(iter)); } @@ -1394,44 +1393,18 @@ arr_add_docstring(PyObject *NPY_UNUSED(dummy), PyObject *args) { PyObject *obj; PyObject *str; - #if (PY_VERSION_HEX >= 0x030700A2) + #if !defined(PYPY_VERSION_NUM) || PYPY_VERSION_NUM > 0x07030300 const char *docstr; #else char *docstr; #endif - static char *msg = "already has a docstring"; - PyObject *tp_dict = PyArrayDescr_Type.tp_dict; - PyObject *myobj; - static PyTypeObject *PyMemberDescr_TypePtr = NULL; - static PyTypeObject *PyGetSetDescr_TypePtr = NULL; - static PyTypeObject *PyMethodDescr_TypePtr = NULL; + static char *msg = "already has a different docstring"; /* Don't add docstrings */ if (Py_OptimizeFlag > 1) { Py_RETURN_NONE; } - if (PyGetSetDescr_TypePtr == NULL) { - /* Get "subdescr" */ - myobj = PyDict_GetItemString(tp_dict, "fields"); - if (myobj != NULL) { - PyGetSetDescr_TypePtr = Py_TYPE(myobj); - } - } - if (PyMemberDescr_TypePtr == NULL) { - myobj = PyDict_GetItemString(tp_dict, "alignment"); - if (myobj != NULL) { - PyMemberDescr_TypePtr = Py_TYPE(myobj); - } - } - if (PyMethodDescr_TypePtr == NULL) { - myobj = PyDict_GetItemString(tp_dict, "newbyteorder"); - if (myobj != NULL) { - PyMethodDescr_TypePtr = Py_TYPE(myobj); - } - } - -#if defined(NPY_PY3K) if (!PyArg_ParseTuple(args, "OO!:add_docstring", &obj, &PyUnicode_Type, &str)) { return NULL; } @@ -1440,47 +1413,65 @@ arr_add_docstring(PyObject *NPY_UNUSED(dummy), PyObject *args) if (docstr == NULL) { return NULL; } -#else - if (!PyArg_ParseTuple(args, "OO!:add_docstring", &obj, &PyString_Type, &str)) { - return NULL; - } - - docstr = PyString_AS_STRING(str); -#endif -#define _TESTDOC1(typebase) (Py_TYPE(obj) == &Py##typebase##_Type) -#define _TESTDOC2(typebase) (Py_TYPE(obj) == Py##typebase##_TypePtr) -#define _ADDDOC(typebase, doc, name) do { \ - Py##typebase##Object *new = (Py##typebase##Object *)obj; \ +#define _ADDDOC(doc, name) \ if (!(doc)) { \ doc = docstr; \ + Py_INCREF(str); /* hold on to string (leaks reference) */ \ } \ - else { \ + else if (strcmp(doc, docstr) != 0) { \ PyErr_Format(PyExc_RuntimeError, "%s method %s", name, msg); \ return NULL; \ - } \ - } while (0) + } - if (_TESTDOC1(CFunction)) { - _ADDDOC(CFunction, new->m_ml->ml_doc, new->m_ml->ml_name); + if (Py_TYPE(obj) == &PyCFunction_Type) { + PyCFunctionObject *new = (PyCFunctionObject *)obj; + _ADDDOC(new->m_ml->ml_doc, new->m_ml->ml_name); } - else if (_TESTDOC1(Type)) { - _ADDDOC(Type, new->tp_doc, new->tp_name); + else if (PyObject_TypeCheck(obj, &PyType_Type)) { + /* + * We add it to both `tp_doc` and `__doc__` here. Note that in theory + * `tp_doc` extracts the signature line, but we currently do not use + * it. It may make sense to only add it as `__doc__` and + * `__text_signature__` to the dict in the future. + * The dictionary path is only necessary for heaptypes (currently not + * used) and metaclasses. + * If `__doc__` as stored in `tp_dict` is None, we assume this was + * filled in by `PyType_Ready()` and should also be replaced. + */ + PyTypeObject *new = (PyTypeObject *)obj; + _ADDDOC(new->tp_doc, new->tp_name); + if (new->tp_dict != NULL && PyDict_CheckExact(new->tp_dict) && + PyDict_GetItemString(new->tp_dict, "__doc__") == Py_None) { + /* Warning: Modifying `tp_dict` is not generally safe! */ + if (PyDict_SetItemString(new->tp_dict, "__doc__", str) < 0) { + return NULL; + } + } } - else if (_TESTDOC2(MemberDescr)) { - _ADDDOC(MemberDescr, new->d_member->doc, new->d_member->name); + else if (Py_TYPE(obj) == &PyMemberDescr_Type) { + PyMemberDescrObject *new = (PyMemberDescrObject *)obj; + _ADDDOC(new->d_member->doc, new->d_member->name); } - else if (_TESTDOC2(GetSetDescr)) { - _ADDDOC(GetSetDescr, new->d_getset->doc, new->d_getset->name); + else if (Py_TYPE(obj) == &PyGetSetDescr_Type) { + PyGetSetDescrObject *new = (PyGetSetDescrObject *)obj; + _ADDDOC(new->d_getset->doc, new->d_getset->name); } - else if (_TESTDOC2(MethodDescr)) { - _ADDDOC(MethodDescr, new->d_method->ml_doc, new->d_method->ml_name); + else if (Py_TYPE(obj) == &PyMethodDescr_Type) { + PyMethodDescrObject *new = (PyMethodDescrObject *)obj; + _ADDDOC(new->d_method->ml_doc, new->d_method->ml_name); } else { PyObject *doc_attr; doc_attr = PyObject_GetAttrString(obj, "__doc__"); - if (doc_attr != NULL && doc_attr != Py_None) { + if (doc_attr != NULL && doc_attr != Py_None && + (PyUnicode_Compare(doc_attr, str) != 0)) { + Py_DECREF(doc_attr); + if (PyErr_Occurred()) { + /* error during PyUnicode_Compare */ + return NULL; + } PyErr_Format(PyExc_RuntimeError, "object %s", msg); return NULL; } @@ -1494,31 +1485,25 @@ arr_add_docstring(PyObject *NPY_UNUSED(dummy), PyObject *args) Py_RETURN_NONE; } -#undef _TESTDOC1 -#undef _TESTDOC2 #undef _ADDDOC - Py_INCREF(str); Py_RETURN_NONE; } -#if defined NPY_HAVE_SSE2_INTRINSICS -#include <emmintrin.h> -#endif - /* * This function packs boolean values in the input array into the bits of a * byte array. Truth values are determined as usual: 0 is false, everything * else is true. */ -static NPY_INLINE void +static NPY_GCC_OPT_3 NPY_INLINE void pack_inner(const char *inptr, npy_intp element_size, /* in bytes */ npy_intp n_in, npy_intp in_stride, char *outptr, npy_intp n_out, - npy_intp out_stride) + npy_intp out_stride, + PACK_ORDER order) { /* * Loop through the elements of inptr. @@ -1530,29 +1515,64 @@ pack_inner(const char *inptr, npy_intp index = 0; int remain = n_in % 8; /* uneven bits */ -#if defined NPY_HAVE_SSE2_INTRINSICS && defined HAVE__M_FROM_INT64 +#if NPY_SIMD if (in_stride == 1 && element_size == 1 && n_out > 2) { - __m128i zero = _mm_setzero_si128(); + npyv_u8 v_zero = npyv_zero_u8(); /* don't handle non-full 8-byte remainder */ npy_intp vn_out = n_out - (remain ? 1 : 0); - vn_out -= (vn_out & 1); - for (index = 0; index < vn_out; index += 2) { - unsigned int r; - /* swap as packbits is "big endian", note x86 can load unaligned */ - npy_uint64 a = npy_bswap8(*(npy_uint64*)inptr); - npy_uint64 b = npy_bswap8(*(npy_uint64*)(inptr + 8)); - __m128i v = _mm_set_epi64(_m_from_int64(b), _m_from_int64(a)); - /* false -> 0x00 and true -> 0xFF (there is no cmpneq) */ - v = _mm_cmpeq_epi8(v, zero); - v = _mm_cmpeq_epi8(v, zero); - /* extract msb of 16 bytes and pack it into 16 bit */ - r = _mm_movemask_epi8(v); - /* store result */ - memcpy(outptr, &r, 1); - outptr += out_stride; - memcpy(outptr, (char*)&r + 1, 1); - outptr += out_stride; - inptr += 16; + const int vstep = npyv_nlanes_u64; + const int vstepx4 = vstep * 4; + const int isAligned = npy_is_aligned(outptr, sizeof(npy_uint64)); + vn_out -= (vn_out & (vstep - 1)); + for (; index <= vn_out - vstepx4; index += vstepx4, inptr += npyv_nlanes_u8 * 4) { + npyv_u8 v0 = npyv_load_u8((const npy_uint8*)inptr); + npyv_u8 v1 = npyv_load_u8((const npy_uint8*)inptr + npyv_nlanes_u8 * 1); + npyv_u8 v2 = npyv_load_u8((const npy_uint8*)inptr + npyv_nlanes_u8 * 2); + npyv_u8 v3 = npyv_load_u8((const npy_uint8*)inptr + npyv_nlanes_u8 * 3); + if (order == PACK_ORDER_BIG) { + v0 = npyv_rev64_u8(v0); + v1 = npyv_rev64_u8(v1); + v2 = npyv_rev64_u8(v2); + v3 = npyv_rev64_u8(v3); + } + npy_uint64 bb[4]; + bb[0] = npyv_tobits_b8(npyv_cmpneq_u8(v0, v_zero)); + bb[1] = npyv_tobits_b8(npyv_cmpneq_u8(v1, v_zero)); + bb[2] = npyv_tobits_b8(npyv_cmpneq_u8(v2, v_zero)); + bb[3] = npyv_tobits_b8(npyv_cmpneq_u8(v3, v_zero)); + if(out_stride == 1 && + (!NPY_ALIGNMENT_REQUIRED || isAligned)) { + npy_uint64 *ptr64 = (npy_uint64*)outptr; + #if NPY_SIMD_WIDTH == 16 + npy_uint64 bcomp = bb[0] | (bb[1] << 16) | (bb[2] << 32) | (bb[3] << 48); + ptr64[0] = bcomp; + #elif NPY_SIMD_WIDTH == 32 + ptr64[0] = bb[0] | (bb[1] << 32); + ptr64[1] = bb[2] | (bb[3] << 32); + #else + ptr64[0] = bb[0]; ptr64[1] = bb[1]; + ptr64[2] = bb[2]; ptr64[3] = bb[3]; + #endif + outptr += vstepx4; + } else { + for(int i = 0; i < 4; i++) { + for (int j = 0; j < vstep; j++) { + memcpy(outptr, (char*)&bb[i] + j, 1); + outptr += out_stride; + } + } + } + } + for (; index < vn_out; index += vstep, inptr += npyv_nlanes_u8) { + npyv_u8 va = npyv_load_u8((const npy_uint8*)inptr); + if (order == PACK_ORDER_BIG) { + va = npyv_rev64_u8(va); + } + npy_uint64 bb = npyv_tobits_b8(npyv_cmpneq_u8(va, v_zero)); + for (int i = 0; i < vstep; ++i) { + memcpy(outptr, (char*)&bb + i, 1); + outptr += out_stride; + } } } #endif @@ -1560,30 +1580,42 @@ pack_inner(const char *inptr, if (remain == 0) { /* assumes n_in > 0 */ remain = 8; } - /* don't reset index to handle remainder of above block */ + /* Don't reset index. Just handle remainder of above block */ for (; index < n_out; index++) { - char build = 0; - int i, maxi; - npy_intp j; - - maxi = (index == n_out - 1) ? remain : 8; - for (i = 0; i < maxi; i++) { - build <<= 1; - for (j = 0; j < element_size; j++) { - build |= (inptr[j] != 0); + unsigned char build = 0; + int maxi = (index == n_out - 1) ? remain : 8; + if (order == PACK_ORDER_BIG) { + for (int i = 0; i < maxi; i++) { + build <<= 1; + for (npy_intp j = 0; j < element_size; j++) { + build |= (inptr[j] != 0); + } + inptr += in_stride; + } + if (index == n_out - 1) { + build <<= 8 - remain; } - inptr += in_stride; } - if (index == n_out - 1) { - build <<= 8 - remain; + else + { + for (int i = 0; i < maxi; i++) { + build >>= 1; + for (npy_intp j = 0; j < element_size; j++) { + build |= (inptr[j] != 0) ? 128 : 0; + } + inptr += in_stride; + } + if (index == n_out - 1) { + build >>= 8 - remain; + } } - *outptr = build; + *outptr = (char)build; outptr += out_stride; } } static PyObject * -pack_bits(PyObject *input, int axis) +pack_bits(PyObject *input, int axis, char order) { PyArrayObject *inp; PyArrayObject *new = NULL; @@ -1601,6 +1633,7 @@ pack_bits(PyObject *input, int axis) if (!PyArray_ISBOOL(inp) && !PyArray_ISINTEGER(inp)) { PyErr_SetString(PyExc_TypeError, "Expected an input array of integer or boolean data type"); + Py_DECREF(inp); goto fail; } @@ -1661,13 +1694,13 @@ pack_bits(PyObject *input, int axis) Py_XDECREF(ot); goto fail; } - + const PACK_ORDER ordere = order == 'b' ? PACK_ORDER_BIG : PACK_ORDER_LITTLE; NPY_BEGIN_THREADS_THRESHOLDED(PyArray_DIM(out, axis)); while (PyArray_ITER_NOTDONE(it)) { pack_inner(PyArray_ITER_DATA(it), PyArray_ITEMSIZE(new), PyArray_DIM(new, axis), PyArray_STRIDE(new, axis), PyArray_ITER_DATA(ot), PyArray_DIM(out, axis), - PyArray_STRIDE(out, axis)); + PyArray_STRIDE(out, axis), ordere); PyArray_ITER_NEXT(it); PyArray_ITER_NEXT(ot); } @@ -1687,17 +1720,24 @@ pack_bits(PyObject *input, int axis) } static PyObject * -unpack_bits(PyObject *input, int axis) +unpack_bits(PyObject *input, int axis, PyObject *count_obj, char order) { static int unpack_init = 0; - static char unpack_lookup[256][8]; + /* + * lookuptable for bitorder big as it has been around longer + * bitorder little is handled via byteswapping in the loop + */ + static union { + npy_uint8 bytes[8]; + npy_uint64 uint64; + } unpack_lookup_big[256]; PyArrayObject *inp; PyArrayObject *new = NULL; PyArrayObject *out = NULL; npy_intp outdims[NPY_MAXDIMS]; int i; PyArrayIterObject *it, *ot; - npy_intp n_in, in_stride, out_stride; + npy_intp count, in_n, in_tail, out_pad, in_stride, out_stride; NPY_BEGIN_THREADS_DEF; inp = (PyArrayObject *)PyArray_FROM_O(input); @@ -1708,6 +1748,7 @@ unpack_bits(PyObject *input, int axis) if (PyArray_TYPE(inp) != NPY_UBYTE) { PyErr_SetString(PyExc_TypeError, "Expected an input array of unsigned byte data type"); + Py_DECREF(inp); goto fail; } @@ -1725,20 +1766,37 @@ unpack_bits(PyObject *input, int axis) newdim.ptr = &shape; temp = (PyArrayObject *)PyArray_Newshape(new, &newdim, NPY_CORDER); + Py_DECREF(new); if (temp == NULL) { - goto fail; + return NULL; } - Py_DECREF(new); new = temp; } /* Setup output shape */ - for (i=0; i<PyArray_NDIM(new); i++) { + for (i = 0; i < PyArray_NDIM(new); i++) { outdims[i] = PyArray_DIM(new, i); } /* Multiply axis dimension by 8 */ - outdims[axis] <<= 3; + outdims[axis] *= 8; + if (count_obj != Py_None) { + count = PyArray_PyIntAsIntp(count_obj); + if (error_converting(count)) { + goto fail; + } + if (count < 0) { + outdims[axis] += count; + if (outdims[axis] < 0) { + PyErr_Format(PyExc_ValueError, + "-count larger than number of elements"); + goto fail; + } + } + else { + outdims[axis] = count; + } + } /* Create output array */ out = (PyArrayObject *)PyArray_NewFromDescr( @@ -1748,6 +1806,7 @@ unpack_bits(PyObject *input, int axis) if (out == NULL) { goto fail; } + /* Setup iterators to iterate over all but given axis */ it = (PyArrayIterObject *)PyArray_IterAllButAxis((PyObject *)new, &axis); ot = (PyArrayIterObject *)PyArray_IterAllButAxis((PyObject *)out, &axis); @@ -1757,34 +1816,39 @@ unpack_bits(PyObject *input, int axis) goto fail; } - /* setup lookup table under GIL, big endian 0..256 as bytes */ + /* + * setup lookup table under GIL, 256 8 byte blocks representing 8 bits + * expanded to 1/0 bytes + */ if (unpack_init == 0) { - npy_uint64 j; - npy_uint64 * unpack_lookup_64 = (npy_uint64 *)unpack_lookup; + npy_intp j; for (j=0; j < 256; j++) { - npy_uint64 v = 0; - v |= (npy_uint64)((j & 1) == 1); - v |= (npy_uint64)((j & 2) == 2) << 8; - v |= (npy_uint64)((j & 4) == 4) << 16; - v |= (npy_uint64)((j & 8) == 8) << 24; - v |= (npy_uint64)((j & 16) == 16) << 32; - v |= (npy_uint64)((j & 32) == 32) << 40; - v |= (npy_uint64)((j & 64) == 64) << 48; - v |= (npy_uint64)((j & 128) == 128) << 56; -#if NPY_BYTE_ORDER == NPY_LITTLE_ENDIAN - v = npy_bswap8(v); -#endif - unpack_lookup_64[j] = v; + npy_intp k; + for (k=0; k < 8; k++) { + npy_uint8 v = (j & (1 << k)) == (1 << k); + unpack_lookup_big[j].bytes[7 - k] = v; + } } unpack_init = 1; } - NPY_BEGIN_THREADS_THRESHOLDED(PyArray_DIM(new, axis)); + count = PyArray_DIM(new, axis) * 8; + if (outdims[axis] > count) { + in_n = count / 8; + in_tail = 0; + out_pad = outdims[axis] - count; + } + else { + in_n = outdims[axis] / 8; + in_tail = outdims[axis] % 8; + out_pad = 0; + } - n_in = PyArray_DIM(new, axis); in_stride = PyArray_STRIDE(new, axis); out_stride = PyArray_STRIDE(out, axis); + NPY_BEGIN_THREADS_THRESHOLDED(PyArray_Size((PyObject *)out) / 8); + while (PyArray_ITER_NOTDONE(it)) { npy_intp index; unsigned const char *inptr = PyArray_ITER_DATA(it); @@ -1792,24 +1856,74 @@ unpack_bits(PyObject *input, int axis) if (out_stride == 1) { /* for unity stride we can just copy out of the lookup table */ - for (index = 0; index < n_in; index++) { - memcpy(outptr, unpack_lookup[*inptr], 8); - outptr += 8; - inptr += in_stride; + if (order == 'b') { + for (index = 0; index < in_n; index++) { + npy_uint64 v = unpack_lookup_big[*inptr].uint64; + memcpy(outptr, &v, 8); + outptr += 8; + inptr += in_stride; + } + } + else { + for (index = 0; index < in_n; index++) { + npy_uint64 v = unpack_lookup_big[*inptr].uint64; + if (order != 'b') { + v = npy_bswap8(v); + } + memcpy(outptr, &v, 8); + outptr += 8; + inptr += in_stride; + } + } + /* Clean up the tail portion */ + if (in_tail) { + npy_uint64 v = unpack_lookup_big[*inptr].uint64; + if (order != 'b') { + v = npy_bswap8(v); + } + memcpy(outptr, &v, in_tail); + } + /* Add padding */ + else if (out_pad) { + memset(outptr, 0, out_pad); } } else { - for (index = 0; index < n_in; index++) { - unsigned char mask = 128; - - for (i = 0; i < 8; i++) { - *outptr = ((mask & (*inptr)) != 0); + if (order == 'b') { + for (index = 0; index < in_n; index++) { + for (i = 0; i < 8; i++) { + *outptr = ((*inptr & (128 >> i)) != 0); + outptr += out_stride; + } + inptr += in_stride; + } + /* Clean up the tail portion */ + for (i = 0; i < in_tail; i++) { + *outptr = ((*inptr & (128 >> i)) != 0); outptr += out_stride; - mask >>= 1; } - inptr += in_stride; + } + else { + for (index = 0; index < in_n; index++) { + for (i = 0; i < 8; i++) { + *outptr = ((*inptr & (1 << i)) != 0); + outptr += out_stride; + } + inptr += in_stride; + } + /* Clean up the tail portion */ + for (i = 0; i < in_tail; i++) { + *outptr = ((*inptr & (1 << i)) != 0); + outptr += out_stride; + } + } + /* Add padding */ + for (index = 0; index < out_pad; index++) { + *outptr = 0; + outptr += out_stride; } } + PyArray_ITER_NEXT(it); PyArray_ITER_NEXT(ot); } @@ -1833,25 +1947,49 @@ io_pack(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds) { PyObject *obj; int axis = NPY_MAXDIMS; - static char *kwlist[] = {"in", "axis", NULL}; + static char *kwlist[] = {"in", "axis", "bitorder", NULL}; + char c = 'b'; + const char * order_str = NULL; - if (!PyArg_ParseTupleAndKeywords( args, kwds, "O|O&:pack" , kwlist, - &obj, PyArray_AxisConverter, &axis)) { + if (!PyArg_ParseTupleAndKeywords( args, kwds, "O|O&s:pack" , kwlist, + &obj, PyArray_AxisConverter, &axis, &order_str)) { return NULL; } - return pack_bits(obj, axis); + if (order_str != NULL) { + if (strncmp(order_str, "little", 6) == 0) + c = 'l'; + else if (strncmp(order_str, "big", 3) == 0) + c = 'b'; + else { + PyErr_SetString(PyExc_ValueError, + "'order' must be either 'little' or 'big'"); + return NULL; + } + } + return pack_bits(obj, axis, c); } + NPY_NO_EXPORT PyObject * io_unpack(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds) { PyObject *obj; int axis = NPY_MAXDIMS; - static char *kwlist[] = {"in", "axis", NULL}; + PyObject *count = Py_None; + static char *kwlist[] = {"in", "axis", "count", "bitorder", NULL}; + const char * c = NULL; - if (!PyArg_ParseTupleAndKeywords( args, kwds, "O|O&:unpack" , kwlist, - &obj, PyArray_AxisConverter, &axis)) { + if (!PyArg_ParseTupleAndKeywords( args, kwds, "O|O&Os:unpack" , kwlist, + &obj, PyArray_AxisConverter, &axis, &count, &c)) { + return NULL; + } + if (c == NULL) { + c = "b"; + } + if (c[0] != 'l' && c[0] != 'b') { + PyErr_SetString(PyExc_ValueError, + "'order' must begin with 'l' or 'b'"); return NULL; } - return unpack_bits(obj, axis); + return unpack_bits(obj, axis, count, c[0]); } diff --git a/numpy/core/src/multiarray/compiled_base.h b/numpy/core/src/multiarray/compiled_base.h index 51508531c5a3..d3bc08cb233b 100644 --- a/numpy/core/src/multiarray/compiled_base.h +++ b/numpy/core/src/multiarray/compiled_base.h @@ -1,13 +1,14 @@ -#ifndef _NPY_PRIVATE__COMPILED_BASE_H_ -#define _NPY_PRIVATE__COMPILED_BASE_H_ -#include <numpy/ndarraytypes.h> +#ifndef NUMPY_CORE_SRC_MULTIARRAY_COMPILED_BASE_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_COMPILED_BASE_H_ + +#include "numpy/ndarraytypes.h" NPY_NO_EXPORT PyObject * arr_insert(PyObject *, PyObject *, PyObject *); NPY_NO_EXPORT PyObject * arr_bincount(PyObject *, PyObject *, PyObject *); NPY_NO_EXPORT PyObject * -arr_digitize(PyObject *, PyObject *, PyObject *kwds); +arr__monotonicity(PyObject *, PyObject *, PyObject *kwds); NPY_NO_EXPORT PyObject * arr_interp(PyObject *, PyObject *, PyObject *); NPY_NO_EXPORT PyObject * @@ -23,4 +24,4 @@ io_pack(PyObject *, PyObject *, PyObject *); NPY_NO_EXPORT PyObject * io_unpack(PyObject *, PyObject *, PyObject *); -#endif +#endif /* NUMPY_CORE_SRC_MULTIARRAY_COMPILED_BASE_H_ */ diff --git a/numpy/core/src/multiarray/conversion_utils.c b/numpy/core/src/multiarray/conversion_utils.c index 7e92e59916bd..a1de580d9537 100644 --- a/numpy/core/src/multiarray/conversion_utils.c +++ b/numpy/core/src/multiarray/conversion_utils.c @@ -1,12 +1,12 @@ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE + #define PY_SSIZE_T_CLEAN #include <Python.h> -#include "structmember.h" +#include <structmember.h> -#define NPY_NO_DEPRECATED_API NPY_API_VERSION -#define _MULTIARRAYMODULE #include "numpy/arrayobject.h" #include "numpy/arrayscalars.h" -#include "numpy/arrayobject.h" #include "npy_config.h" #include "npy_pycompat.h" @@ -16,6 +16,7 @@ #include "conversion_utils.h" #include "alloc.h" +#include "npy_buffer.h" static int PyArray_PyIntAsInt_ErrMsg(PyObject *o, const char * msg) NPY_GCC_NONNULL(2); @@ -94,9 +95,21 @@ PyArray_IntpConverter(PyObject *obj, PyArray_Dims *seq) seq->ptr = NULL; seq->len = 0; + + /* + * When the deprecation below expires, remove the `if` statement, and + * update the comment for PyArray_OptionalIntpConverter. + */ if (obj == Py_None) { + /* Numpy 1.20, 2020-05-31 */ + if (DEPRECATE( + "Passing None into shape arguments as an alias for () is " + "deprecated.") < 0){ + return NPY_FAIL; + } return NPY_SUCCEED; } + len = PySequence_Size(obj); if (len == -1) { /* Check to see if it is an integer number */ @@ -115,8 +128,8 @@ PyArray_IntpConverter(PyObject *obj, PyArray_Dims *seq) return NPY_FAIL; } if (len > NPY_MAXDIMS) { - PyErr_Format(PyExc_ValueError, "sequence too large; " - "cannot be greater than %d", NPY_MAXDIMS); + PyErr_Format(PyExc_ValueError, "maximum supported dimension for an ndarray is %d" + ", found %d", NPY_MAXDIMS, len); return NPY_FAIL; } if (len > 0) { @@ -136,6 +149,56 @@ PyArray_IntpConverter(PyObject *obj, PyArray_Dims *seq) return NPY_SUCCEED; } +/* + * Like PyArray_IntpConverter, but leaves `seq` untouched if `None` is passed + * rather than treating `None` as `()`. + */ +NPY_NO_EXPORT int +PyArray_OptionalIntpConverter(PyObject *obj, PyArray_Dims *seq) +{ + if (obj == Py_None) { + return NPY_SUCCEED; + } + + return PyArray_IntpConverter(obj, seq); +} + +NPY_NO_EXPORT int +PyArray_CopyConverter(PyObject *obj, _PyArray_CopyMode *copymode) { + if (obj == Py_None) { + PyErr_SetString(PyExc_ValueError, + "NoneType copy mode not allowed."); + return NPY_FAIL; + } + + int int_copymode; + static PyObject* numpy_CopyMode = NULL; + npy_cache_import("numpy", "_CopyMode", &numpy_CopyMode); + + if (numpy_CopyMode != NULL && (PyObject *)Py_TYPE(obj) == numpy_CopyMode) { + PyObject* mode_value = PyObject_GetAttrString(obj, "value"); + if (mode_value == NULL) { + return NPY_FAIL; + } + + int_copymode = (int)PyLong_AsLong(mode_value); + Py_DECREF(mode_value); + if (error_converting(int_copymode)) { + return NPY_FAIL; + } + } + else { + npy_bool bool_copymode; + if (!PyArray_BoolConverter(obj, &bool_copymode)) { + return NPY_FAIL; + } + int_copymode = (int)bool_copymode; + } + + *copymode = (_PyArray_CopyMode)int_copymode; + return NPY_SUCCEED; +} + /*NUMPY_API * Get buffer chunk from object * @@ -151,11 +214,7 @@ PyArray_IntpConverter(PyObject *obj, PyArray_Dims *seq) NPY_NO_EXPORT int PyArray_BufferConverter(PyObject *obj, PyArray_Chunk *buf) { -#if defined(NPY_PY3K) Py_buffer view; -#else - Py_ssize_t buflen; -#endif buf->ptr = NULL; buf->flags = NPY_ARRAY_BEHAVED; @@ -164,7 +223,6 @@ PyArray_BufferConverter(PyObject *obj, PyArray_Chunk *buf) return NPY_SUCCEED; } -#if defined(NPY_PY3K) if (PyObject_GetBuffer(obj, &view, PyBUF_ANY_CONTIGUOUS|PyBUF_WRITABLE|PyBUF_SIMPLE) != 0) { PyErr_Clear(); @@ -190,22 +248,6 @@ PyArray_BufferConverter(PyObject *obj, PyArray_Chunk *buf) if (PyMemoryView_Check(obj)) { buf->base = PyMemoryView_GET_BASE(obj); } -#else - if (PyObject_AsWriteBuffer(obj, &(buf->ptr), &buflen) < 0) { - PyErr_Clear(); - buf->flags &= ~NPY_ARRAY_WRITEABLE; - if (PyObject_AsReadBuffer(obj, (const void **)&(buf->ptr), - &buflen) < 0) { - return NPY_FAIL; - } - } - buf->len = (npy_intp) buflen; - - /* Point to the base of the buffer object if present */ - if (PyBuffer_Check(obj)) { - buf->base = ((PyArray_Chunk *)obj)->base; - } -#endif if (buf->base == NULL) { buf->base = obj; } @@ -327,111 +369,168 @@ PyArray_BoolConverter(PyObject *object, npy_bool *val) return NPY_SUCCEED; } -/*NUMPY_API - * Convert object to endian - */ -NPY_NO_EXPORT int -PyArray_ByteorderConverter(PyObject *obj, char *endian) +static int +string_converter_helper( + PyObject *object, + void *out, + int (*str_func)(char const*, Py_ssize_t, void*), + char const *name, + char const *message) { - char *str; - PyObject *tmp = NULL; - - if (PyUnicode_Check(obj)) { - obj = tmp = PyUnicode_AsASCIIString(obj); + /* allow bytes for compatibility */ + PyObject *str_object = NULL; + if (PyBytes_Check(object)) { + str_object = PyUnicode_FromEncodedObject(object, NULL, NULL); + if (str_object == NULL) { + PyErr_Format(PyExc_ValueError, + "%s %s (got %R)", name, message, object); + return NPY_FAIL; + } } - - *endian = NPY_SWAP; - str = PyBytes_AsString(obj); - if (!str) { - Py_XDECREF(tmp); + else if (PyUnicode_Check(object)) { + str_object = object; + Py_INCREF(str_object); + } + else { + PyErr_Format(PyExc_TypeError, + "%s must be str, not %s", name, Py_TYPE(object)->tp_name); return NPY_FAIL; } - if (strlen(str) < 1) { - PyErr_SetString(PyExc_ValueError, - "Byteorder string must be at least length 1"); - Py_XDECREF(tmp); + + Py_ssize_t length; + char const *str = PyUnicode_AsUTF8AndSize(str_object, &length); + if (str == NULL) { + Py_DECREF(str_object); return NPY_FAIL; } - *endian = str[0]; - if (str[0] != NPY_BIG && str[0] != NPY_LITTLE - && str[0] != NPY_NATIVE && str[0] != NPY_IGNORE) { - if (str[0] == 'b' || str[0] == 'B') { - *endian = NPY_BIG; - } - else if (str[0] == 'l' || str[0] == 'L') { - *endian = NPY_LITTLE; - } - else if (str[0] == 'n' || str[0] == 'N') { - *endian = NPY_NATIVE; - } - else if (str[0] == 'i' || str[0] == 'I') { - *endian = NPY_IGNORE; - } - else if (str[0] == 's' || str[0] == 'S') { - *endian = NPY_SWAP; - } - else { + + int ret = str_func(str, length, out); + Py_DECREF(str_object); + if (ret < 0) { + /* str_func returns -1 without an exception if the value is wrong */ + if (!PyErr_Occurred()) { PyErr_Format(PyExc_ValueError, - "%s is an unrecognized byteorder", - str); - Py_XDECREF(tmp); - return NPY_FAIL; + "%s %s (got %R)", name, message, object); } + return NPY_FAIL; } - Py_XDECREF(tmp); return NPY_SUCCEED; } +static int byteorder_parser(char const *str, Py_ssize_t length, void *data) +{ + char *endian = (char *)data; + + if (length < 1) { + return -1; + } + else if (str[0] == NPY_BIG || str[0] == NPY_LITTLE || + str[0] == NPY_NATIVE || str[0] == NPY_IGNORE) { + *endian = str[0]; + return 0; + } + else if (str[0] == 'b' || str[0] == 'B') { + *endian = NPY_BIG; + return 0; + } + else if (str[0] == 'l' || str[0] == 'L') { + *endian = NPY_LITTLE; + return 0; + } + else if (str[0] == 'n' || str[0] == 'N') { + *endian = NPY_NATIVE; + return 0; + } + else if (str[0] == 'i' || str[0] == 'I') { + *endian = NPY_IGNORE; + return 0; + } + else if (str[0] == 's' || str[0] == 'S') { + *endian = NPY_SWAP; + return 0; + } + else { + return -1; + } +} + /*NUMPY_API - * Convert object to sort kind + * Convert object to endian */ NPY_NO_EXPORT int -PyArray_SortkindConverter(PyObject *obj, NPY_SORTKIND *sortkind) +PyArray_ByteorderConverter(PyObject *obj, char *endian) { - char *str; - PyObject *tmp = NULL; + return string_converter_helper( + obj, (void *)endian, byteorder_parser, "byteorder", "not recognized"); +} - if (PyUnicode_Check(obj)) { - obj = tmp = PyUnicode_AsASCIIString(obj); - if (obj == NULL) { - return NPY_FAIL; - } - } +static int sortkind_parser(char const *str, Py_ssize_t length, void *data) +{ + NPY_SORTKIND *sortkind = (NPY_SORTKIND *)data; - *sortkind = NPY_QUICKSORT; - str = PyBytes_AsString(obj); - if (!str) { - Py_XDECREF(tmp); - return NPY_FAIL; - } - if (strlen(str) < 1) { - PyErr_SetString(PyExc_ValueError, - "Sort kind string must be at least length 1"); - Py_XDECREF(tmp); - return NPY_FAIL; + if (length < 1) { + return -1; } if (str[0] == 'q' || str[0] == 'Q') { *sortkind = NPY_QUICKSORT; + return 0; } else if (str[0] == 'h' || str[0] == 'H') { *sortkind = NPY_HEAPSORT; + return 0; } else if (str[0] == 'm' || str[0] == 'M') { + /* + * Mergesort is an alias for NPY_STABLESORT. + * That maintains backwards compatibility while + * allowing other types of stable sorts to be used. + */ *sortkind = NPY_MERGESORT; + return 0; } else if (str[0] == 's' || str[0] == 'S') { - /* mergesort is the only stable sorting method in numpy */ - *sortkind = NPY_MERGESORT; + /* + * NPY_STABLESORT is one of + * + * - mergesort + * - timsort + * + * Which one is used depends on the data type. + */ + *sortkind = NPY_STABLESORT; + return 0; } else { - PyErr_Format(PyExc_ValueError, - "%s is an unrecognized kind of sort", - str); - Py_XDECREF(tmp); - return NPY_FAIL; + return -1; + } +} + +/*NUMPY_API + * Convert object to sort kind + */ +NPY_NO_EXPORT int +PyArray_SortkindConverter(PyObject *obj, NPY_SORTKIND *sortkind) +{ + /* Leave the desired default from the caller for Py_None */ + if (obj == Py_None) { + return NPY_SUCCEED; + } + return string_converter_helper( + obj, (void *)sortkind, sortkind_parser, "sort kind", + "must be one of 'quick', 'heap', or 'stable'"); +} + +static int selectkind_parser(char const *str, Py_ssize_t length, void *data) +{ + NPY_SELECTKIND *selectkind = (NPY_SELECTKIND *)data; + + if (length == 11 && strcmp(str, "introselect") == 0) { + *selectkind = NPY_INTROSELECT; + return 0; + } + else { + return -1; } - Py_XDECREF(tmp); - return NPY_SUCCEED; } /*NUMPY_API @@ -440,40 +539,44 @@ PyArray_SortkindConverter(PyObject *obj, NPY_SORTKIND *sortkind) NPY_NO_EXPORT int PyArray_SelectkindConverter(PyObject *obj, NPY_SELECTKIND *selectkind) { - char *str; - PyObject *tmp = NULL; + return string_converter_helper( + obj, (void *)selectkind, selectkind_parser, "select kind", + "must be 'introselect'"); +} - if (PyUnicode_Check(obj)) { - obj = tmp = PyUnicode_AsASCIIString(obj); - if (obj == NULL) { - return NPY_FAIL; - } - } +static int searchside_parser(char const *str, Py_ssize_t length, void *data) +{ + NPY_SEARCHSIDE *side = (NPY_SEARCHSIDE *)data; + int is_exact = 0; - *selectkind = NPY_INTROSELECT; - str = PyBytes_AsString(obj); - if (!str) { - Py_XDECREF(tmp); - return NPY_FAIL; + if (length < 1) { + return -1; } - if (strlen(str) < 1) { - PyErr_SetString(PyExc_ValueError, - "Select kind string must be at least length 1"); - Py_XDECREF(tmp); - return NPY_FAIL; + else if (str[0] == 'l' || str[0] == 'L') { + *side = NPY_SEARCHLEFT; + is_exact = (length == 4 && strcmp(str, "left") == 0); } - if (strcmp(str, "introselect") == 0) { - *selectkind = NPY_INTROSELECT; + else if (str[0] == 'r' || str[0] == 'R') { + *side = NPY_SEARCHRIGHT; + is_exact = (length == 5 && strcmp(str, "right") == 0); } else { - PyErr_Format(PyExc_ValueError, - "%s is an unrecognized kind of select", - str); - Py_XDECREF(tmp); - return NPY_FAIL; + return -1; } - Py_XDECREF(tmp); - return NPY_SUCCEED; + + /* Filters out the case sensitive/non-exact + * match inputs and other inputs and outputs DeprecationWarning + */ + if (!is_exact) { + /* NumPy 1.20, 2020-05-19 */ + if (DEPRECATE("inexact matches and case insensitive matches " + "for search side are deprecated, please use " + "one of 'left' or 'right' instead.") < 0) { + return -1; + } + } + + return 0; } /*NUMPY_API @@ -482,36 +585,36 @@ PyArray_SelectkindConverter(PyObject *obj, NPY_SELECTKIND *selectkind) NPY_NO_EXPORT int PyArray_SearchsideConverter(PyObject *obj, void *addr) { - NPY_SEARCHSIDE *side = (NPY_SEARCHSIDE *)addr; - char *str; - PyObject *tmp = NULL; + return string_converter_helper( + obj, addr, searchside_parser, "search side", + "must be 'left' or 'right'"); +} - if (PyUnicode_Check(obj)) { - obj = tmp = PyUnicode_AsASCIIString(obj); +static int order_parser(char const *str, Py_ssize_t length, void *data) +{ + NPY_ORDER *val = (NPY_ORDER *)data; + if (length != 1) { + return -1; } - - str = PyBytes_AsString(obj); - if (!str || strlen(str) < 1) { - PyErr_SetString(PyExc_ValueError, - "expected nonempty string for keyword 'side'"); - Py_XDECREF(tmp); - return NPY_FAIL; + if (str[0] == 'C' || str[0] == 'c') { + *val = NPY_CORDER; + return 0; } - - if (str[0] == 'l' || str[0] == 'L') { - *side = NPY_SEARCHLEFT; + else if (str[0] == 'F' || str[0] == 'f') { + *val = NPY_FORTRANORDER; + return 0; } - else if (str[0] == 'r' || str[0] == 'R') { - *side = NPY_SEARCHRIGHT; + else if (str[0] == 'A' || str[0] == 'a') { + *val = NPY_ANYORDER; + return 0; + } + else if (str[0] == 'K' || str[0] == 'k') { + *val = NPY_KEEPORDER; + return 0; } else { - PyErr_Format(PyExc_ValueError, - "'%s' is an invalid value for keyword 'side'", str); - Py_XDECREF(tmp); - return NPY_FAIL; + return -1; } - Py_XDECREF(tmp); - return NPY_SUCCEED; } /*NUMPY_API @@ -520,80 +623,52 @@ PyArray_SearchsideConverter(PyObject *obj, void *addr) NPY_NO_EXPORT int PyArray_OrderConverter(PyObject *object, NPY_ORDER *val) { - char *str; - /* Leave the desired default from the caller for NULL/Py_None */ - if (object == NULL || object == Py_None) { + /* Leave the desired default from the caller for Py_None */ + if (object == Py_None) { return NPY_SUCCEED; } - else if (PyUnicode_Check(object)) { - PyObject *tmp; - int ret; - tmp = PyUnicode_AsASCIIString(object); - if (tmp == NULL) { - PyErr_SetString(PyExc_ValueError, "Invalid unicode string passed in " - "for the array ordering. " - "Please pass in 'C', 'F', 'A' " - "or 'K' instead"); - return NPY_FAIL; - } - ret = PyArray_OrderConverter(tmp, val); - Py_DECREF(tmp); - return ret; - } - else if (!PyBytes_Check(object) || PyBytes_GET_SIZE(object) < 1) { - /* 2015-12-14, 1.11 */ - int ret = DEPRECATE("Non-string object detected for " - "the array ordering. Please pass " - "in 'C', 'F', 'A', or 'K' instead"); + return string_converter_helper( + object, (void *)val, order_parser, "order", + "must be one of 'C', 'F', 'A', or 'K'"); +} - if (ret < 0) { - return -1; - } +static int clipmode_parser(char const *str, Py_ssize_t length, void *data) +{ + NPY_CLIPMODE *val = (NPY_CLIPMODE *)data; + int is_exact = 0; - if (PyObject_IsTrue(object)) { - *val = NPY_FORTRANORDER; - } - else { - *val = NPY_CORDER; - } - if (PyErr_Occurred()) { - return NPY_FAIL; - } - return NPY_SUCCEED; + if (length < 1) { + return -1; + } + if (str[0] == 'C' || str[0] == 'c') { + *val = NPY_CLIP; + is_exact = (length == 4 && strcmp(str, "clip") == 0); + } + else if (str[0] == 'W' || str[0] == 'w') { + *val = NPY_WRAP; + is_exact = (length == 4 && strcmp(str, "wrap") == 0); + } + else if (str[0] == 'R' || str[0] == 'r') { + *val = NPY_RAISE; + is_exact = (length == 5 && strcmp(str, "raise") == 0); } else { - str = PyBytes_AS_STRING(object); - if (strlen(str) != 1) { - /* 2015-12-14, 1.11 */ - int ret = DEPRECATE("Non length-one string passed " - "in for the array ordering. " - "Please pass in 'C', 'F', 'A', " - "or 'K' instead"); - - if (ret < 0) { - return -1; - } - } + return -1; + } - if (str[0] == 'C' || str[0] == 'c') { - *val = NPY_CORDER; - } - else if (str[0] == 'F' || str[0] == 'f') { - *val = NPY_FORTRANORDER; - } - else if (str[0] == 'A' || str[0] == 'a') { - *val = NPY_ANYORDER; - } - else if (str[0] == 'K' || str[0] == 'k') { - *val = NPY_KEEPORDER; - } - else { - PyErr_SetString(PyExc_TypeError, - "order not understood"); - return NPY_FAIL; + /* Filters out the case sensitive/non-exact + * match inputs and other inputs and outputs DeprecationWarning + */ + if (!is_exact) { + /* Numpy 1.20, 2020-05-19 */ + if (DEPRECATE("inexact matches and case insensitive matches " + "for clip mode are deprecated, please use " + "one of 'clip', 'raise', or 'wrap' instead.") < 0) { + return -1; } } - return NPY_SUCCEED; + + return 0; } /*NUMPY_API @@ -605,36 +680,14 @@ PyArray_ClipmodeConverter(PyObject *object, NPY_CLIPMODE *val) if (object == NULL || object == Py_None) { *val = NPY_RAISE; } - else if (PyBytes_Check(object)) { - char *str; - str = PyBytes_AS_STRING(object); - if (str[0] == 'C' || str[0] == 'c') { - *val = NPY_CLIP; - } - else if (str[0] == 'W' || str[0] == 'w') { - *val = NPY_WRAP; - } - else if (str[0] == 'R' || str[0] == 'r') { - *val = NPY_RAISE; - } - else { - PyErr_SetString(PyExc_TypeError, - "clipmode not understood"); - return NPY_FAIL; - } - } - else if (PyUnicode_Check(object)) { - PyObject *tmp; - int ret; - tmp = PyUnicode_AsASCIIString(object); - if (tmp == NULL) { - return NPY_FAIL; - } - ret = PyArray_ClipmodeConverter(tmp, val); - Py_DECREF(tmp); - return ret; + + else if (PyBytes_Check(object) || PyUnicode_Check(object)) { + return string_converter_helper( + object, (void *)val, clipmode_parser, "clipmode", + "must be one of 'clip', 'raise', or 'wrap'"); } else { + /* For users passing `np.RAISE`, `np.WRAP`, `np.CLIP` */ int number = PyArray_PyIntAsInt(object); if (error_converting(number)) { goto fail; @@ -644,7 +697,8 @@ PyArray_ClipmodeConverter(PyObject *object, NPY_CLIPMODE *val) *val = (NPY_CLIPMODE) number; } else { - goto fail; + PyErr_Format(PyExc_ValueError, + "integer clipmode must be np.RAISE, np.WRAP, or np.CLIP"); } } return NPY_SUCCEED; @@ -668,8 +722,8 @@ PyArray_ConvertClipmodeSequence(PyObject *object, NPY_CLIPMODE *modes, int n) if (object && (PyTuple_Check(object) || PyList_Check(object))) { if (PySequence_Size(object) != n) { PyErr_Format(PyExc_ValueError, - "list of clipmodes has wrong length (%d instead of %d)", - (int)PySequence_Size(object), n); + "list of clipmodes has wrong length (%zd instead of %d)", + PySequence_Size(object), n); return NPY_FAIL; } @@ -698,66 +752,128 @@ PyArray_ConvertClipmodeSequence(PyObject *object, NPY_CLIPMODE *modes, int n) return NPY_SUCCEED; } -/*NUMPY_API - * Convert any Python object, *obj*, to an NPY_CASTING enum. +static int correlatemode_parser(char const *str, Py_ssize_t length, void *data) +{ + NPY_CORRELATEMODE *val = (NPY_CORRELATEMODE *)data; + int is_exact = 0; + + if (length < 1) { + return -1; + } + if (str[0] == 'V' || str[0] == 'v') { + *val = NPY_VALID; + is_exact = (length == 5 && strcmp(str, "valid") == 0); + } + else if (str[0] == 'S' || str[0] == 's') { + *val = NPY_SAME; + is_exact = (length == 4 && strcmp(str, "same") == 0); + } + else if (str[0] == 'F' || str[0] == 'f') { + *val = NPY_FULL; + is_exact = (length == 4 && strcmp(str, "full") == 0); + } + else { + return -1; + } + + /* Filters out the case sensitive/non-exact + * match inputs and other inputs and outputs DeprecationWarning + */ + if (!is_exact) { + /* Numpy 1.21, 2021-01-19 */ + if (DEPRECATE("inexact matches and case insensitive matches for " + "convolve/correlate mode are deprecated, please " + "use one of 'valid', 'same', or 'full' instead.") < 0) { + return -1; + } + } + + return 0; +} + +/* + * Convert an object to NPY_VALID / NPY_SAME / NPY_FULL */ NPY_NO_EXPORT int -PyArray_CastingConverter(PyObject *obj, NPY_CASTING *casting) +PyArray_CorrelatemodeConverter(PyObject *object, NPY_CORRELATEMODE *val) { - char *str = NULL; - Py_ssize_t length = 0; - - if (PyUnicode_Check(obj)) { - PyObject *str_obj; - int ret; - str_obj = PyUnicode_AsASCIIString(obj); - if (str_obj == NULL) { - return 0; - } - ret = PyArray_CastingConverter(str_obj, casting); - Py_DECREF(str_obj); - return ret; + if (PyUnicode_Check(object)) { + return string_converter_helper( + object, (void *)val, correlatemode_parser, "mode", + "must be one of 'valid', 'same', or 'full'"); } - if (PyBytes_AsStringAndSize(obj, &str, &length) < 0) { - return 0; + else { + /* For users passing integers */ + int number = PyArray_PyIntAsInt(object); + if (error_converting(number)) { + PyErr_SetString(PyExc_TypeError, + "convolve/correlate mode not understood"); + return NPY_FAIL; + } + if (number <= (int) NPY_FULL + && number >= (int) NPY_VALID) { + *val = (NPY_CORRELATEMODE) number; + return NPY_SUCCEED; + } + else { + PyErr_Format(PyExc_ValueError, + "integer convolve/correlate mode must be 0, 1, or 2"); + return NPY_FAIL; + } } +} - if (length >= 2) switch (str[2]) { - case 0: - if (strcmp(str, "no") == 0) { - *casting = NPY_NO_CASTING; - return 1; - } - break; - case 'u': - if (strcmp(str, "equiv") == 0) { - *casting = NPY_EQUIV_CASTING; - return 1; - } - break; - case 'f': - if (strcmp(str, "safe") == 0) { - *casting = NPY_SAFE_CASTING; - return 1; - } - break; - case 'm': - if (strcmp(str, "same_kind") == 0) { - *casting = NPY_SAME_KIND_CASTING; - return 1; - } - break; - case 's': - if (strcmp(str, "unsafe") == 0) { - *casting = NPY_UNSAFE_CASTING; - return 1; - } - break; +static int casting_parser(char const *str, Py_ssize_t length, void *data) +{ + NPY_CASTING *casting = (NPY_CASTING *)data; + if (length < 2) { + return -1; } + switch (str[2]) { + case 0: + if (length == 2 && strcmp(str, "no") == 0) { + *casting = NPY_NO_CASTING; + return 0; + } + break; + case 'u': + if (length == 5 && strcmp(str, "equiv") == 0) { + *casting = NPY_EQUIV_CASTING; + return 0; + } + break; + case 'f': + if (length == 4 && strcmp(str, "safe") == 0) { + *casting = NPY_SAFE_CASTING; + return 0; + } + break; + case 'm': + if (length == 9 && strcmp(str, "same_kind") == 0) { + *casting = NPY_SAME_KIND_CASTING; + return 0; + } + break; + case 's': + if (length == 6 && strcmp(str, "unsafe") == 0) { + *casting = NPY_UNSAFE_CASTING; + return 0; + } + break; + } + return -1; +} - PyErr_SetString(PyExc_ValueError, - "casting must be one of 'no', 'equiv', 'safe', " +/*NUMPY_API + * Convert any Python object, *obj*, to an NPY_CASTING enum. + */ +NPY_NO_EXPORT int +PyArray_CastingConverter(PyObject *obj, NPY_CASTING *casting) +{ + return string_converter_helper( + obj, (void *)casting, casting_parser, "casting", + "must be one of 'no', 'equiv', 'safe', " "'same_kind', or 'unsafe'"); return 0; } @@ -813,18 +929,6 @@ PyArray_PyIntAsIntp_ErrMsg(PyObject *o, const char * msg) * Since it is the usual case, first check if o is an integer. This is * an exact check, since otherwise __index__ is used. */ -#if !defined(NPY_PY3K) - if (PyInt_CheckExact(o)) { - #if (NPY_SIZEOF_LONG <= NPY_SIZEOF_INTP) - /* No overflow is possible, so we can just return */ - return PyInt_AS_LONG(o); - #else - long_value = PyInt_AS_LONG(o); - goto overflow_check; - #endif - } - else -#endif if (PyLong_CheckExact(o)) { #if (NPY_SIZEOF_LONG < NPY_SIZEOF_INTP) long_value = PyLong_AsLongLong(o); @@ -1146,7 +1250,7 @@ PyArray_TypestrConvert(int itemsize, int gentype) PyArray_IntTupleFromIntp */ NPY_NO_EXPORT PyObject * -PyArray_IntTupleFromIntp(int len, npy_intp *vals) +PyArray_IntTupleFromIntp(int len, npy_intp const *vals) { int i; PyObject *intTuple = PyTuple_New(len); @@ -1155,11 +1259,7 @@ PyArray_IntTupleFromIntp(int len, npy_intp *vals) goto fail; } for (i = 0; i < len; i++) { -#if NPY_SIZEOF_INTP <= NPY_SIZEOF_LONG - PyObject *o = PyInt_FromLong((long) vals[i]); -#else - PyObject *o = PyLong_FromLongLong((npy_longlong) vals[i]); -#endif + PyObject *o = PyArray_PyIntFromIntp(vals[i]); if (!o) { Py_DECREF(intTuple); intTuple = NULL; diff --git a/numpy/core/src/multiarray/conversion_utils.h b/numpy/core/src/multiarray/conversion_utils.h index cd43f25c38d3..4072841ee1c7 100644 --- a/numpy/core/src/multiarray/conversion_utils.h +++ b/numpy/core/src/multiarray/conversion_utils.h @@ -1,11 +1,23 @@ -#ifndef _NPY_PRIVATE_CONVERSION_UTILS_H_ -#define _NPY_PRIVATE_CONVERSION_UTILS_H_ +#ifndef NUMPY_CORE_SRC_MULTIARRAY_CONVERSION_UTILS_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_CONVERSION_UTILS_H_ -#include <numpy/ndarraytypes.h> +#include "numpy/ndarraytypes.h" NPY_NO_EXPORT int PyArray_IntpConverter(PyObject *obj, PyArray_Dims *seq); +NPY_NO_EXPORT int +PyArray_OptionalIntpConverter(PyObject *obj, PyArray_Dims *seq); + +typedef enum { + NPY_COPY_IF_NEEDED = 0, + NPY_COPY_ALWAYS = 1, + NPY_COPY_NEVER = 2, +} _PyArray_CopyMode; + +NPY_NO_EXPORT int +PyArray_CopyConverter(PyObject *obj, _PyArray_CopyMode *copyflag); + NPY_NO_EXPORT int PyArray_BufferConverter(PyObject *obj, PyArray_Chunk *buf); @@ -36,8 +48,22 @@ PyArray_IntpFromSequence(PyObject *seq, npy_intp *vals, int maxvals); NPY_NO_EXPORT int PyArray_TypestrConvert(int itemsize, int gentype); + +static NPY_INLINE PyObject * +PyArray_PyIntFromIntp(npy_intp const value) +{ +#if NPY_SIZEOF_INTP <= NPY_SIZEOF_LONG + return PyLong_FromLong((long)value); +#else + return PyLong_FromLongLong((npy_longlong)value); +#endif +} + NPY_NO_EXPORT PyObject * -PyArray_IntTupleFromIntp(int len, npy_intp *vals); +PyArray_IntTupleFromIntp(int len, npy_intp const *vals); + +NPY_NO_EXPORT int +PyArray_CorrelatemodeConverter(PyObject *object, NPY_CORRELATEMODE *val); NPY_NO_EXPORT int PyArray_SelectkindConverter(PyObject *obj, NPY_SELECTKIND *selectkind); @@ -65,4 +91,4 @@ PyArray_ConvertMultiAxis(PyObject *axis_in, int ndim, npy_bool *out_axis_flags); */ extern NPY_NO_EXPORT int evil_global_disable_warn_O4O8_flag; -#endif +#endif /* NUMPY_CORE_SRC_MULTIARRAY_CONVERSION_UTILS_H_ */ diff --git a/numpy/core/src/multiarray/convert.c b/numpy/core/src/multiarray/convert.c index e88582a514a1..2f68db07c988 100644 --- a/numpy/core/src/multiarray/convert.c +++ b/numpy/core/src/multiarray/convert.c @@ -1,16 +1,14 @@ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE + #define PY_SSIZE_T_CLEAN #include <Python.h> -#include "structmember.h" +#include <structmember.h> -#include <npy_config.h> +#include "npy_config.h" -#define NPY_NO_DEPRECATED_API NPY_API_VERSION -#define _MULTIARRAYMODULE #include "numpy/arrayobject.h" #include "numpy/arrayscalars.h" - -#include "npy_config.h" - #include "npy_pycompat.h" #include "common.h" @@ -64,7 +62,7 @@ npy_fallocate(npy_intp nbytes, FILE * fp) * early exit on no space, other errors will also get found during fwrite */ if (r == -1 && errno == ENOSPC) { - PyErr_Format(PyExc_IOError, "Not enough free space to write " + PyErr_Format(PyExc_OSError, "Not enough free space to write " "%"NPY_INTP_FMT" bytes", nbytes); return -1; } @@ -141,7 +139,7 @@ PyArray_ToFile(PyArrayObject *self, FILE *fp, char *sep, char *format) if (n3 == 0) { /* binary data */ if (PyDataType_FLAGCHK(PyArray_DESCR(self), NPY_LIST_PICKLE)) { - PyErr_SetString(PyExc_IOError, + PyErr_SetString(PyExc_OSError, "cannot write object arrays to a file in binary mode"); return -1; } @@ -185,7 +183,7 @@ PyArray_ToFile(PyArrayObject *self, FILE *fp, char *sep, char *format) #endif NPY_END_ALLOW_THREADS; if (n < size) { - PyErr_Format(PyExc_IOError, + PyErr_Format(PyExc_OSError, "%ld requested and %ld written", (long) size, (long) n); return -1; @@ -201,7 +199,7 @@ PyArray_ToFile(PyArrayObject *self, FILE *fp, char *sep, char *format) (size_t) PyArray_DESCR(self)->elsize, 1, fp) < 1) { NPY_END_THREADS; - PyErr_Format(PyExc_IOError, + PyErr_Format(PyExc_OSError, "problem writing element %" NPY_INTP_FMT " to file", it->index); Py_DECREF(it); @@ -248,13 +246,13 @@ PyArray_ToFile(PyArrayObject *self, FILE *fp, char *sep, char *format) return -1; } PyTuple_SET_ITEM(tupobj,0,obj); - obj = PyUString_FromString((const char *)format); + obj = PyUnicode_FromString((const char *)format); if (obj == NULL) { Py_DECREF(tupobj); Py_DECREF(it); return -1; } - strobj = PyUString_Format(obj, tupobj); + strobj = PyUnicode_Format(obj, tupobj); Py_DECREF(obj); Py_DECREF(tupobj); if (strobj == NULL) { @@ -262,20 +260,14 @@ PyArray_ToFile(PyArrayObject *self, FILE *fp, char *sep, char *format) return -1; } } -#if defined(NPY_PY3K) byteobj = PyUnicode_AsASCIIString(strobj); -#else - byteobj = strobj; -#endif NPY_BEGIN_ALLOW_THREADS; n2 = PyBytes_GET_SIZE(byteobj); n = fwrite(PyBytes_AS_STRING(byteobj), 1, n2, fp); NPY_END_ALLOW_THREADS; -#if defined(NPY_PY3K) Py_DECREF(byteobj); -#endif if (n < n2) { - PyErr_Format(PyExc_IOError, + PyErr_Format(PyExc_OSError, "problem writing element %" NPY_INTP_FMT " to file", it->index); Py_DECREF(strobj); @@ -285,7 +277,7 @@ PyArray_ToFile(PyArrayObject *self, FILE *fp, char *sep, char *format) /* write separator for all but last one */ if (it->index != it->size-1) { if (fwrite(sep, 1, n3, fp) < n3) { - PyErr_Format(PyExc_IOError, + PyErr_Format(PyExc_OSError, "problem writing separator to file"); Py_DECREF(strobj); Py_DECREF(it); @@ -409,7 +401,7 @@ PyArray_FillWithScalar(PyArrayObject *arr, PyObject *obj) } } /* Python integer */ - else if (PyLong_Check(obj) || PyInt_Check(obj)) { + else if (PyLong_Check(obj)) { /* Try long long before unsigned long long */ npy_longlong ll_v = PyLong_AsLongLong(obj); if (error_converting(ll_v)) { @@ -543,35 +535,6 @@ PyArray_AssignZero(PyArrayObject *dst, return retcode; } -/* - * Fills an array with ones. - * - * dst: The destination array. - * wheremask: If non-NULL, a boolean mask specifying where to set the values. - * - * Returns 0 on success, -1 on failure. - */ -NPY_NO_EXPORT int -PyArray_AssignOne(PyArrayObject *dst, - PyArrayObject *wheremask) -{ - npy_bool value; - PyArray_Descr *bool_dtype; - int retcode; - - /* Create a raw bool scalar with the value True */ - bool_dtype = PyArray_DescrFromType(NPY_BOOL); - if (bool_dtype == NULL) { - return -1; - } - value = 1; - - retcode = PyArray_AssignRawScalar(dst, bool_dtype, (char *)&value, - wheremask, NPY_SAFE_CASTING); - - Py_DECREF(bool_dtype); - return retcode; -} /*NUMPY_API * Copy an array. @@ -614,22 +577,6 @@ PyArray_View(PyArrayObject *self, PyArray_Descr *type, PyTypeObject *pytype) } dtype = PyArray_DESCR(self); - - if (type != NULL && !PyArray_EquivTypes(dtype, type) && - (PyArray_FLAGS(self) & NPY_ARRAY_WARN_ON_WRITE)) { - const char *msg = - "Numpy has detected that you may be viewing or writing to an array " - "returned by selecting multiple fields in a structured array. \n\n" - "This code may break in numpy 1.16 because this will return a view " - "instead of a copy -- see release notes for details."; - /* 2016-09-19, 1.12 */ - if (DEPRECATE_FUTUREWARNING(msg) < 0) { - return NULL; - } - /* Only warn once per array */ - PyArray_CLEARFLAGS(self, NPY_ARRAY_WARN_ON_WRITE); - } - flags = PyArray_FLAGS(self); Py_INCREF(dtype); diff --git a/numpy/core/src/multiarray/convert.h b/numpy/core/src/multiarray/convert.h index 96df1971193e..d64d9be3fa09 100644 --- a/numpy/core/src/multiarray/convert.h +++ b/numpy/core/src/multiarray/convert.h @@ -1,8 +1,8 @@ -#ifndef _NPY_ARRAYOBJECT_CONVERT_H_ -#define _NPY_ARRAYOBJECT_CONVERT_H_ +#ifndef NUMPY_CORE_SRC_MULTIARRAY_CONVERT_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_CONVERT_H_ NPY_NO_EXPORT int PyArray_AssignZero(PyArrayObject *dst, PyArrayObject *wheremask); -#endif +#endif /* NUMPY_CORE_SRC_MULTIARRAY_CONVERT_H_ */ diff --git a/numpy/core/src/multiarray/convert_datatype.c b/numpy/core/src/multiarray/convert_datatype.c index 0d79f294cae3..3135d69894b3 100644 --- a/numpy/core/src/multiarray/convert_datatype.c +++ b/numpy/core/src/multiarray/convert_datatype.c @@ -1,24 +1,35 @@ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE + #define PY_SSIZE_T_CLEAN #include <Python.h> -#include "structmember.h" +#include <structmember.h> -#define NPY_NO_DEPRECATED_API NPY_API_VERSION -#define _MULTIARRAYMODULE #include "numpy/arrayobject.h" #include "numpy/arrayscalars.h" #include "npy_config.h" +#include "lowlevel_strided_loops.h" #include "npy_pycompat.h" #include "numpy/npy_math.h" +#include "array_coercion.h" #include "common.h" +#include "ctors.h" +#include "dtypemeta.h" +#include "common_dtype.h" #include "scalartypes.h" #include "mapping.h" +#include "legacy_dtype_implementation.h" +#include "abstractdtypes.h" #include "convert_datatype.h" #include "_datetime.h" #include "datetime_strings.h" +#include "array_method.h" +#include "usertypes.h" +#include "dtype_transfer.h" /* @@ -32,6 +43,197 @@ */ NPY_NO_EXPORT npy_intp REQUIRED_STR_LEN[] = {0, 3, 5, 10, 10, 20, 20, 20, 20}; + +static PyObject * +PyArray_GetGenericToVoidCastingImpl(void); + +static PyObject * +PyArray_GetVoidToGenericCastingImpl(void); + +static PyObject * +PyArray_GetGenericToObjectCastingImpl(void); + +static PyObject * +PyArray_GetObjectToGenericCastingImpl(void); + + +/** + * Fetch the casting implementation from one DType to another. + * + * @params from + * @params to + * + * @returns A castingimpl (PyArrayDTypeMethod *), None or NULL with an + * error set. + */ +NPY_NO_EXPORT PyObject * +PyArray_GetCastingImpl(PyArray_DTypeMeta *from, PyArray_DTypeMeta *to) +{ + PyObject *res; + if (from == to) { + res = NPY_DT_SLOTS(from)->within_dtype_castingimpl; + } + else { + res = PyDict_GetItemWithError(NPY_DT_SLOTS(from)->castingimpls, (PyObject *)to); + } + if (res != NULL || PyErr_Occurred()) { + Py_XINCREF(res); + return res; + } + /* + * The following code looks up CastingImpl based on the fact that anything + * can be cast to and from objects or structured (void) dtypes. + * + * The last part adds casts dynamically based on legacy definition + */ + if (from->type_num == NPY_OBJECT) { + res = PyArray_GetObjectToGenericCastingImpl(); + } + else if (to->type_num == NPY_OBJECT) { + res = PyArray_GetGenericToObjectCastingImpl(); + } + else if (from->type_num == NPY_VOID) { + res = PyArray_GetVoidToGenericCastingImpl(); + } + else if (to->type_num == NPY_VOID) { + res = PyArray_GetGenericToVoidCastingImpl(); + } + else if (from->type_num < NPY_NTYPES && to->type_num < NPY_NTYPES) { + /* All builtin dtypes have their casts explicitly defined. */ + PyErr_Format(PyExc_RuntimeError, + "builtin cast from %S to %S not found, this should not " + "be possible.", from, to); + return NULL; + } + else { + if (NPY_DT_is_parametric(from) || NPY_DT_is_parametric(to)) { + Py_RETURN_NONE; + } + /* Reject non-legacy dtypes (they need to use the new API) */ + if (!NPY_DT_is_legacy(from) || !NPY_DT_is_legacy(to)) { + Py_RETURN_NONE; + } + if (from != to) { + /* A cast function must have been registered */ + PyArray_VectorUnaryFunc *castfunc = PyArray_GetCastFunc( + from->singleton, to->type_num); + if (castfunc == NULL) { + PyErr_Clear(); + /* Remember that this cast is not possible */ + if (PyDict_SetItem(NPY_DT_SLOTS(from)->castingimpls, + (PyObject *) to, Py_None) < 0) { + return NULL; + } + Py_RETURN_NONE; + } + } + + /* PyArray_AddLegacyWrapping_CastingImpl find the correct casting level: */ + /* + * TODO: Possibly move this to the cast registration time. But if we do + * that, we have to also update the cast when the casting safety + * is registered. + */ + if (PyArray_AddLegacyWrapping_CastingImpl(from, to, -1) < 0) { + return NULL; + } + return PyArray_GetCastingImpl(from, to); + } + + if (res == NULL) { + return NULL; + } + if (from == to) { + PyErr_Format(PyExc_RuntimeError, + "Internal NumPy error, within-DType cast missing for %S!", from); + Py_DECREF(res); + return NULL; + } + if (PyDict_SetItem(NPY_DT_SLOTS(from)->castingimpls, + (PyObject *)to, res) < 0) { + Py_DECREF(res); + return NULL; + } + return res; +} + + +/** + * Fetch the (bound) casting implementation from one DType to another. + * + * @params from + * @params to + * + * @returns A bound casting implementation or None (or NULL for error). + */ +static PyObject * +PyArray_GetBoundCastingImpl(PyArray_DTypeMeta *from, PyArray_DTypeMeta *to) +{ + PyObject *method = PyArray_GetCastingImpl(from, to); + if (method == NULL || method == Py_None) { + return method; + } + + /* TODO: Create better way to wrap method into bound method */ + PyBoundArrayMethodObject *res; + res = PyObject_New(PyBoundArrayMethodObject, &PyBoundArrayMethod_Type); + if (res == NULL) { + return NULL; + } + res->method = (PyArrayMethodObject *)method; + res->dtypes = PyMem_Malloc(2 * sizeof(PyArray_DTypeMeta *)); + if (res->dtypes == NULL) { + Py_DECREF(res); + return NULL; + } + Py_INCREF(from); + res->dtypes[0] = from; + Py_INCREF(to); + res->dtypes[1] = to; + + return (PyObject *)res; +} + + +NPY_NO_EXPORT PyObject * +_get_castingimpl(PyObject *NPY_UNUSED(module), PyObject *args) +{ + PyArray_DTypeMeta *from, *to; + if (!PyArg_ParseTuple(args, "O!O!:_get_castingimpl", + &PyArrayDTypeMeta_Type, &from, &PyArrayDTypeMeta_Type, &to)) { + return NULL; + } + return PyArray_GetBoundCastingImpl(from, to); +} + + +/** + * Find the minimal cast safety level given two cast-levels as input. + * Supports the NPY_CAST_IS_VIEW check, and should be preferred to allow + * extending cast-levels if necessary. + * It is not valid for one of the arguments to be -1 to indicate an error. + * + * @param casting1 + * @param casting2 + * @return The minimal casting error (can be -1). + */ +NPY_NO_EXPORT NPY_CASTING +PyArray_MinCastSafety(NPY_CASTING casting1, NPY_CASTING casting2) +{ + if (casting1 < 0 || casting2 < 0) { + return -1; + } + NPY_CASTING view = casting1 & casting2 & _NPY_CAST_IS_VIEW; + casting1 = casting1 & ~_NPY_CAST_IS_VIEW; + casting2 = casting2 & ~_NPY_CAST_IS_VIEW; + /* larger casting values are less safe */ + if (casting1 > casting2) { + return casting1 | view; + } + return casting2 | view; +} + + /*NUMPY_API * For backward compatibility * @@ -46,8 +248,7 @@ PyArray_CastToType(PyArrayObject *arr, PyArray_Descr *dtype, int is_f_order) { PyObject *out; - /* If the requested dtype is flexible, adapt it */ - PyArray_AdaptFlexibleDType((PyObject *)arr, PyArray_DESCR(arr), &dtype); + Py_SETREF(dtype, PyArray_AdaptDescriptorToArray(arr, (PyObject *)dtype)); if (dtype == NULL) { return NULL; } @@ -90,11 +291,14 @@ PyArray_GetCastFunc(PyArray_Descr *descr, int type_num) PyObject *key; PyObject *cobj; - key = PyInt_FromLong(type_num); + key = PyLong_FromLong(type_num); cobj = PyDict_GetItem(obj, key); Py_DECREF(key); - if (cobj && NpyCapsule_Check(cobj)) { - castfunc = NpyCapsule_AsVoidPtr(cobj); + if (cobj && PyCapsule_CheckExact(cobj)) { + castfunc = PyCapsule_GetPointer(cobj, NULL); + if (castfunc == NULL) { + return NULL; + } } } } @@ -127,239 +331,6 @@ PyArray_GetCastFunc(PyArray_Descr *descr, int type_num) return NULL; } -/* - * This function calls Py_DECREF on flex_dtype, and replaces it with - * a new dtype that has been adapted based on the values in data_dtype - * and data_obj. If the flex_dtype is not flexible, it leaves it as is. - * - * Usually, if data_obj is not an array, dtype should be the result - * given by the PyArray_GetArrayParamsFromObject function. - * - * The data_obj may be NULL if just a dtype is known for the source. - * - * If *flex_dtype is NULL, returns immediately, without setting an - * exception. This basically assumes an error was already set previously. - * - * The current flexible dtypes include NPY_STRING, NPY_UNICODE, NPY_VOID, - * and NPY_DATETIME with generic units. - */ -NPY_NO_EXPORT void -PyArray_AdaptFlexibleDType(PyObject *data_obj, PyArray_Descr *data_dtype, - PyArray_Descr **flex_dtype) -{ - PyArray_DatetimeMetaData *meta; - int flex_type_num; - PyArrayObject *arr = NULL; - PyArray_Descr *dtype = NULL; - int ndim = 0; - npy_intp dims[NPY_MAXDIMS]; - int result; - - if (*flex_dtype == NULL) { - if (!PyErr_Occurred()) { - PyErr_SetString(PyExc_RuntimeError, - "NumPy AdaptFlexibleDType was called with NULL flex_dtype " - "but no error set"); - } - return; - } - - flex_type_num = (*flex_dtype)->type_num; - - /* Flexible types with expandable size */ - if (PyDataType_ISUNSIZED(*flex_dtype)) { - /* First replace the flex dtype */ - PyArray_DESCR_REPLACE(*flex_dtype); - if (*flex_dtype == NULL) { - return; - } - - if (data_dtype->type_num == flex_type_num || - flex_type_num == NPY_VOID) { - (*flex_dtype)->elsize = data_dtype->elsize; - } - else if (flex_type_num == NPY_STRING || flex_type_num == NPY_UNICODE) { - npy_intp size = 8; - - /* - * Get a string-size estimate of the input. These - * are generallly the size needed, rounded up to - * a multiple of eight. - */ - switch (data_dtype->type_num) { - case NPY_BOOL: - case NPY_UBYTE: - case NPY_BYTE: - case NPY_USHORT: - case NPY_SHORT: - case NPY_UINT: - case NPY_INT: - case NPY_ULONG: - case NPY_LONG: - case NPY_ULONGLONG: - case NPY_LONGLONG: - if (data_dtype->kind == 'b') { - /* 5 chars needed for cast to 'True' or 'False' */ - size = 5; - } - else if (data_dtype->elsize > 8 || - data_dtype->elsize < 0) { - /* - * Element size should never be greater than 8 or - * less than 0 for integer type, but just in case... - */ - break; - } - else if (data_dtype->kind == 'u') { - size = REQUIRED_STR_LEN[data_dtype->elsize]; - } - else if (data_dtype->kind == 'i') { - /* Add character for sign symbol */ - size = REQUIRED_STR_LEN[data_dtype->elsize] + 1; - } - break; - case NPY_HALF: - case NPY_FLOAT: - case NPY_DOUBLE: - case NPY_LONGDOUBLE: - size = 32; - break; - case NPY_CFLOAT: - case NPY_CDOUBLE: - case NPY_CLONGDOUBLE: - size = 64; - break; - case NPY_OBJECT: - size = 64; - if ((flex_type_num == NPY_STRING || - flex_type_num == NPY_UNICODE) && - data_obj != NULL) { - PyObject *list; - - if (PyArray_CheckScalar(data_obj)) { - list = PyArray_ToList((PyArrayObject *)data_obj); - if (list != NULL) { - PyObject *s = PyObject_Str(list); - if (s == NULL) { - Py_DECREF(list); - Py_DECREF(*flex_dtype); - *flex_dtype = NULL; - return; - } - else { - size = PyObject_Length(s); - Py_DECREF(s); - } - Py_DECREF(list); - } - } - else if (PyArray_Check(data_obj)) { - /* - * Convert data array to list of objects since - * GetArrayParamsFromObject won't iterate over - * array. - */ - list = PyArray_ToList((PyArrayObject *)data_obj); - result = PyArray_GetArrayParamsFromObject( - list, - *flex_dtype, - 0, &dtype, - &ndim, dims, &arr, NULL); - if (result == 0 && dtype != NULL) { - if (flex_type_num == NPY_UNICODE) { - size = dtype->elsize / 4; - } - else { - size = dtype->elsize; - } - } - Py_DECREF(list); - } - else if (PyArray_IsPythonScalar(data_obj)) { - PyObject *s = PyObject_Str(data_obj); - if (s == NULL) { - Py_DECREF(*flex_dtype); - *flex_dtype = NULL; - return; - } - else { - size = PyObject_Length(s); - Py_DECREF(s); - } - } - } - break; - case NPY_STRING: - case NPY_VOID: - size = data_dtype->elsize; - break; - case NPY_UNICODE: - size = data_dtype->elsize / 4; - break; - case NPY_DATETIME: - meta = get_datetime_metadata_from_dtype(data_dtype); - if (meta == NULL) { - Py_DECREF(*flex_dtype); - *flex_dtype = NULL; - return; - } - size = get_datetime_iso_8601_strlen(0, meta->base); - break; - case NPY_TIMEDELTA: - size = 21; - break; - } - - if (flex_type_num == NPY_STRING) { - (*flex_dtype)->elsize = size; - } - else if (flex_type_num == NPY_UNICODE) { - (*flex_dtype)->elsize = size * 4; - } - } - else { - /* - * We should never get here, but just in case someone adds - * a new flex dtype... - */ - PyErr_SetString(PyExc_TypeError, - "don't know how to adapt flex dtype"); - *flex_dtype = NULL; - return; - } - } - /* Flexible type with generic time unit that adapts */ - else if (flex_type_num == NPY_DATETIME || - flex_type_num == NPY_TIMEDELTA) { - meta = get_datetime_metadata_from_dtype(*flex_dtype); - if (meta == NULL) { - Py_DECREF(*flex_dtype); - *flex_dtype = NULL; - return; - } - - if (meta->base == NPY_FR_GENERIC) { - if (data_dtype->type_num == NPY_DATETIME || - data_dtype->type_num == NPY_TIMEDELTA) { - meta = get_datetime_metadata_from_dtype(data_dtype); - if (meta == NULL) { - Py_DECREF(*flex_dtype); - *flex_dtype = NULL; - return; - } - - Py_DECREF(*flex_dtype); - *flex_dtype = create_datetime_dtype(flex_type_num, meta); - } - else if (data_obj != NULL) { - /* Detect the unit from the input's data */ - Py_DECREF(*flex_dtype); - *flex_dtype = find_object_datetime_type(data_obj, - flex_type_num); - } - } - } -} /* * Must be broadcastable. @@ -389,61 +360,204 @@ PyArray_CastAnyTo(PyArrayObject *out, PyArrayObject *mp) return PyArray_CopyAnyInto(out, mp); } -/*NUMPY_API - *Check the type coercion rules. + +static NPY_CASTING +_get_cast_safety_from_castingimpl(PyArrayMethodObject *castingimpl, + PyArray_DTypeMeta *dtypes[2], PyArray_Descr *from, PyArray_Descr *to) +{ + PyArray_Descr *descrs[2] = {from, to}; + PyArray_Descr *out_descrs[2]; + + NPY_CASTING casting = castingimpl->resolve_descriptors( + castingimpl, dtypes, descrs, out_descrs); + if (casting < 0) { + return -1; + } + /* The returned descriptors may not match, requiring a second check */ + if (out_descrs[0] != descrs[0]) { + NPY_CASTING from_casting = PyArray_GetCastSafety( + descrs[0], out_descrs[0], NULL); + casting = PyArray_MinCastSafety(casting, from_casting); + if (casting < 0) { + goto finish; + } + } + if (descrs[1] != NULL && out_descrs[1] != descrs[1]) { + NPY_CASTING from_casting = PyArray_GetCastSafety( + descrs[1], out_descrs[1], NULL); + casting = PyArray_MinCastSafety(casting, from_casting); + if (casting < 0) { + goto finish; + } + } + + finish: + Py_DECREF(out_descrs[0]); + Py_DECREF(out_descrs[1]); + /* + * Check for less harmful non-standard returns. The following two returns + * should never happen. They would be roughly equivalent, but less precise, + * versions of `(NPY_NO_CASTING|_NPY_CAST_IS_VIEW)`. + * 1. No-casting must imply cast-is-view. + * 2. Equivalent-casting + cast-is-view is (currently) the definition + * of a "no" cast (there may be reasons to relax this). + * Note that e.g. `(NPY_UNSAFE_CASTING|_NPY_CAST_IS_VIEW)` is valid. + */ + assert(casting != NPY_NO_CASTING); + assert(casting != (NPY_EQUIV_CASTING|_NPY_CAST_IS_VIEW)); + return casting; +} + + +/** + * Given two dtype instances, find the correct casting safety. + * + * Note that in many cases, it may be preferable to fetch the casting + * implementations fully to have them available for doing the actual cast + * later. + * + * @param from + * @param to The descriptor to cast to (may be NULL) + * @param to_dtype If `to` is NULL, must pass the to_dtype (otherwise this + * is ignored). + * @return NPY_CASTING or -1 on error or if the cast is not possible. + */ +NPY_NO_EXPORT NPY_CASTING +PyArray_GetCastSafety( + PyArray_Descr *from, PyArray_Descr *to, PyArray_DTypeMeta *to_dtype) +{ + if (to != NULL) { + to_dtype = NPY_DTYPE(to); + } + PyObject *meth = PyArray_GetCastingImpl(NPY_DTYPE(from), to_dtype); + if (meth == NULL) { + return -1; + } + if (meth == Py_None) { + Py_DECREF(Py_None); + return -1; + } + + PyArrayMethodObject *castingimpl = (PyArrayMethodObject *)meth; + PyArray_DTypeMeta *dtypes[2] = {NPY_DTYPE(from), to_dtype}; + NPY_CASTING casting = _get_cast_safety_from_castingimpl(castingimpl, + dtypes, from, to); + Py_DECREF(meth); + + return casting; +} + + +/** + * Check whether a cast is safe, see also `PyArray_GetCastSafety` for + * a similar function. Unlike GetCastSafety, this function checks the + * `castingimpl->casting` when available. This allows for two things: + * + * 1. It avoids calling `resolve_descriptors` in some cases. + * 2. Strings need to discover the length, but in some cases we know that the + * cast is valid (assuming the string length is discovered first). + * + * The latter means that a `can_cast` could return True, but the cast fail + * because the parametric type cannot guess the correct output descriptor. + * (I.e. if `object_arr.astype("S")` did _not_ inspect the objects, and the + * user would have to guess the string length.) + * + * @param casting the requested casting safety. + * @param from + * @param to The descriptor to cast to (may be NULL) + * @param to_dtype If `to` is NULL, must pass the to_dtype (otherwise this + * is ignored). + * @return 0 for an invalid cast, 1 for a valid and -1 for an error. */ NPY_NO_EXPORT int -PyArray_CanCastSafely(int fromtype, int totype) +PyArray_CheckCastSafety(NPY_CASTING casting, + PyArray_Descr *from, PyArray_Descr *to, PyArray_DTypeMeta *to_dtype) { - PyArray_Descr *from; + if (to != NULL) { + to_dtype = NPY_DTYPE(to); + } + PyObject *meth = PyArray_GetCastingImpl(NPY_DTYPE(from), to_dtype); + if (meth == NULL) { + return -1; + } + if (meth == Py_None) { + Py_DECREF(Py_None); + return -1; + } + PyArrayMethodObject *castingimpl = (PyArrayMethodObject *)meth; - /* Fast table lookup for small type numbers */ - if ((unsigned int)fromtype < NPY_NTYPES && - (unsigned int)totype < NPY_NTYPES) { - return _npy_can_cast_safely_table[fromtype][totype]; + if (PyArray_MinCastSafety(castingimpl->casting, casting) == casting) { + /* No need to check using `castingimpl.resolve_descriptors()` */ + Py_DECREF(meth); + return 1; + } + + PyArray_DTypeMeta *dtypes[2] = {NPY_DTYPE(from), to_dtype}; + NPY_CASTING safety = _get_cast_safety_from_castingimpl(castingimpl, + dtypes, from, to); + Py_DECREF(meth); + /* If casting is the smaller (or equal) safety we match */ + if (safety < 0) { + return -1; } + return PyArray_MinCastSafety(safety, casting) == casting; +} + +/*NUMPY_API + *Check the type coercion rules. + */ +NPY_NO_EXPORT int +PyArray_CanCastSafely(int fromtype, int totype) +{ /* Identity */ if (fromtype == totype) { return 1; } - /* Special-cases for some types */ - switch (fromtype) { - case NPY_DATETIME: - case NPY_TIMEDELTA: - case NPY_OBJECT: - case NPY_VOID: - return 0; - case NPY_BOOL: - return 1; - } - switch (totype) { - case NPY_BOOL: - case NPY_DATETIME: - case NPY_TIMEDELTA: - return 0; - case NPY_OBJECT: - case NPY_VOID: - return 1; - } - - from = PyArray_DescrFromType(fromtype); /* - * cancastto is a NPY_NOTYPE terminated C-int-array of types that - * the data-type can be cast to safely. + * As a micro-optimization, keep the cast table around. This can probably + * be removed as soon as the ufunc loop lookup is modified (presumably + * before the 1.21 release). It does no harm, but the main user of this + * function is the ufunc-loop lookup calling it until a loop matches! + * + * (The table extends further, but is not strictly correct for void). + * TODO: Check this! */ - if (from->f->cancastto) { - int *curtype = from->f->cancastto; + if ((unsigned int)fromtype <= NPY_CLONGDOUBLE && + (unsigned int)totype <= NPY_CLONGDOUBLE) { + return _npy_can_cast_safely_table[fromtype][totype]; + } - while (*curtype != NPY_NOTYPE) { - if (*curtype++ == totype) { - return 1; - } - } + PyArray_DTypeMeta *from = PyArray_DTypeFromTypeNum(fromtype); + if (from == NULL) { + PyErr_WriteUnraisable(NULL); + return 0; } - return 0; + PyArray_DTypeMeta *to = PyArray_DTypeFromTypeNum(totype); + if (to == NULL) { + PyErr_WriteUnraisable(NULL); + return 0; + } + PyObject *castingimpl = PyArray_GetCastingImpl(from, to); + Py_DECREF(from); + Py_DECREF(to); + + if (castingimpl == NULL) { + PyErr_WriteUnraisable(NULL); + return 0; + } + else if (castingimpl == Py_None) { + Py_DECREF(Py_None); + return 0; + } + NPY_CASTING safety = ((PyArrayMethodObject *)castingimpl)->casting; + int res = PyArray_MinCastSafety(safety, NPY_SAFE_CASTING) == NPY_SAFE_CASTING; + Py_DECREF(castingimpl); + return res; } + + /*NUMPY_API * leaves reference count alone --- cannot be NULL * @@ -453,117 +567,12 @@ PyArray_CanCastSafely(int fromtype, int totype) NPY_NO_EXPORT npy_bool PyArray_CanCastTo(PyArray_Descr *from, PyArray_Descr *to) { - int from_type_num = from->type_num; - int to_type_num = to->type_num; - npy_bool ret; - - ret = (npy_bool) PyArray_CanCastSafely(from_type_num, to_type_num); - if (ret) { - /* Check String and Unicode more closely */ - if (from_type_num == NPY_STRING) { - if (to_type_num == NPY_STRING) { - ret = (from->elsize <= to->elsize); - } - else if (to_type_num == NPY_UNICODE) { - ret = (from->elsize << 2 <= to->elsize); - } - } - else if (from_type_num == NPY_UNICODE) { - if (to_type_num == NPY_UNICODE) { - ret = (from->elsize <= to->elsize); - } - } - /* - * For datetime/timedelta, only treat casts moving towards - * more precision as safe. - */ - else if (from_type_num == NPY_DATETIME && to_type_num == NPY_DATETIME) { - PyArray_DatetimeMetaData *meta1, *meta2; - meta1 = get_datetime_metadata_from_dtype(from); - if (meta1 == NULL) { - PyErr_Clear(); - return 0; - } - meta2 = get_datetime_metadata_from_dtype(to); - if (meta2 == NULL) { - PyErr_Clear(); - return 0; - } - - return can_cast_datetime64_metadata(meta1, meta2, - NPY_SAFE_CASTING); - } - else if (from_type_num == NPY_TIMEDELTA && - to_type_num == NPY_TIMEDELTA) { - PyArray_DatetimeMetaData *meta1, *meta2; - meta1 = get_datetime_metadata_from_dtype(from); - if (meta1 == NULL) { - PyErr_Clear(); - return 0; - } - meta2 = get_datetime_metadata_from_dtype(to); - if (meta2 == NULL) { - PyErr_Clear(); - return 0; - } - - return can_cast_timedelta64_metadata(meta1, meta2, - NPY_SAFE_CASTING); - } - /* - * If to_type_num is STRING or unicode - * see if the length is long enough to hold the - * stringified value of the object. - */ - else if (to_type_num == NPY_STRING || to_type_num == NPY_UNICODE) { - /* - * Boolean value cast to string type is 5 characters max - * for string 'False'. - */ - int char_size = 1; - if (to_type_num == NPY_UNICODE) { - char_size = 4; - } - - ret = 0; - if (PyDataType_ISUNSIZED(to)) { - ret = 1; - } - /* - * Need at least 5 characters to convert from boolean - * to 'True' or 'False'. - */ - else if (from->kind == 'b' && to->elsize >= 5 * char_size) { - ret = 1; - } - else if (from->kind == 'u') { - /* Guard against unexpected integer size */ - if (from->elsize > 8 || from->elsize < 0) { - ret = 0; - } - else if (to->elsize >= - REQUIRED_STR_LEN[from->elsize] * char_size) { - ret = 1; - } - } - else if (from->kind == 'i') { - /* Guard against unexpected integer size */ - if (from->elsize > 8 || from->elsize < 0) { - ret = 0; - } - /* Extra character needed for sign */ - else if (to->elsize >= - (REQUIRED_STR_LEN[from->elsize] + 1) * char_size) { - ret = 1; - } - } - } - } - return ret; + return PyArray_CanCastTypeTo(from, to, NPY_SAFE_CASTING); } + /* Provides an ordering for the dtype 'kind' character codes */ -static int +NPY_NO_EXPORT int dtype_kind_to_ordering(char kind) { switch (kind) { @@ -624,237 +633,97 @@ type_num_unsigned_to_signed(int type_num) } } -/* - * Compare two field dictionaries for castability. - * - * Return 1 if 'field1' can be cast to 'field2' according to the rule - * 'casting', 0 if not. - * - * Castabiliy of field dictionaries is defined recursively: 'field1' and - * 'field2' must have the same field names (possibly in different - * orders), and the corresponding field types must be castable according - * to the given casting rule. + +/*NUMPY_API + * Returns true if data of type 'from' may be cast to data of type + * 'to' according to the rule 'casting'. */ -static int -can_cast_fields(PyObject *field1, PyObject *field2, NPY_CASTING casting) +NPY_NO_EXPORT npy_bool +PyArray_CanCastTypeTo(PyArray_Descr *from, PyArray_Descr *to, + NPY_CASTING casting) { - Py_ssize_t ppos; - PyObject *key; - PyObject *tuple1, *tuple2; + PyArray_DTypeMeta *to_dtype = NPY_DTYPE(to); - if (field1 == field2) { - return 1; - } - if (field1 == NULL || field2 == NULL) { - return 0; - } - if (PyDict_Size(field1) != PyDict_Size(field2)) { - return 0; - } - - /* Iterate over all the fields and compare for castability */ - ppos = 0; - while (PyDict_Next(field1, &ppos, &key, &tuple1)) { - if ((tuple2 = PyDict_GetItem(field2, key)) == NULL) { - return 0; - } - /* Compare the dtype of the field for castability */ - if (!PyArray_CanCastTypeTo( - (PyArray_Descr *)PyTuple_GET_ITEM(tuple1, 0), - (PyArray_Descr *)PyTuple_GET_ITEM(tuple2, 0), - casting)) { - return 0; - } - } - - return 1; -} - -/*NUMPY_API - * Returns true if data of type 'from' may be cast to data of type - * 'to' according to the rule 'casting'. - */ -NPY_NO_EXPORT npy_bool -PyArray_CanCastTypeTo(PyArray_Descr *from, PyArray_Descr *to, - NPY_CASTING casting) -{ - /* Fast path for unsafe casts or basic types */ - if (casting == NPY_UNSAFE_CASTING || - (NPY_LIKELY(from->type_num < NPY_OBJECT) && - NPY_LIKELY(from->type_num == to->type_num) && - NPY_LIKELY(from->byteorder == to->byteorder))) { - return 1; - } - /* Equivalent types can be cast with any value of 'casting' */ - else if (PyArray_EquivTypenums(from->type_num, to->type_num)) { - /* For complicated case, use EquivTypes (for now) */ - if (PyTypeNum_ISUSERDEF(from->type_num) || - from->subarray != NULL) { - int ret; - - /* Only NPY_NO_CASTING prevents byte order conversion */ - if ((casting != NPY_NO_CASTING) && - (!PyArray_ISNBO(from->byteorder) || - !PyArray_ISNBO(to->byteorder))) { - PyArray_Descr *nbo_from, *nbo_to; - - nbo_from = PyArray_DescrNewByteorder(from, NPY_NATIVE); - nbo_to = PyArray_DescrNewByteorder(to, NPY_NATIVE); - if (nbo_from == NULL || nbo_to == NULL) { - Py_XDECREF(nbo_from); - Py_XDECREF(nbo_to); - PyErr_Clear(); - return 0; - } - ret = PyArray_EquivTypes(nbo_from, nbo_to); - Py_DECREF(nbo_from); - Py_DECREF(nbo_to); - } - else { - ret = PyArray_EquivTypes(from, to); - } - return ret; - } - - if (PyDataType_HASFIELDS(from)) { - switch (casting) { - case NPY_EQUIV_CASTING: - case NPY_SAFE_CASTING: - case NPY_SAME_KIND_CASTING: - /* - * `from' and `to' must have the same fields, and - * corresponding fields must be (recursively) castable. - */ - return can_cast_fields(from->fields, to->fields, casting); - - case NPY_NO_CASTING: - default: - return PyArray_EquivTypes(from, to); - } - } - - switch (from->type_num) { - case NPY_DATETIME: { - PyArray_DatetimeMetaData *meta1, *meta2; - meta1 = get_datetime_metadata_from_dtype(from); - if (meta1 == NULL) { - PyErr_Clear(); - return 0; - } - meta2 = get_datetime_metadata_from_dtype(to); - if (meta2 == NULL) { - PyErr_Clear(); - return 0; - } - - if (casting == NPY_NO_CASTING) { - return PyArray_ISNBO(from->byteorder) == - PyArray_ISNBO(to->byteorder) && - can_cast_datetime64_metadata(meta1, meta2, casting); - } - else { - return can_cast_datetime64_metadata(meta1, meta2, casting); - } - } - case NPY_TIMEDELTA: { - PyArray_DatetimeMetaData *meta1, *meta2; - meta1 = get_datetime_metadata_from_dtype(from); - if (meta1 == NULL) { - PyErr_Clear(); - return 0; - } - meta2 = get_datetime_metadata_from_dtype(to); - if (meta2 == NULL) { - PyErr_Clear(); - return 0; - } - - if (casting == NPY_NO_CASTING) { - return PyArray_ISNBO(from->byteorder) == - PyArray_ISNBO(to->byteorder) && - can_cast_timedelta64_metadata(meta1, meta2, casting); - } - else { - return can_cast_timedelta64_metadata(meta1, meta2, casting); - } - } - default: - switch (casting) { - case NPY_NO_CASTING: - return PyArray_EquivTypes(from, to); - case NPY_EQUIV_CASTING: - return (from->elsize == to->elsize); - case NPY_SAFE_CASTING: - return (from->elsize <= to->elsize); - default: - return 1; - } - break; - } + /* + * NOTE: This code supports U and S, this is identical to the code + * in `ctors.c` which does not allow these dtypes to be attached + * to an array. Unlike the code for `np.array(..., dtype=)` + * which uses `PyArray_ExtractDTypeAndDescriptor` it rejects "m8" + * as a flexible dtype instance representing a DType. + */ + /* + * TODO: We should grow support for `np.can_cast("d", "S")` being + * different from `np.can_cast("d", "S0")` here, at least for + * the python side API. + * The `to = NULL` branch, which considers "S0" to be "flexible" + * should probably be deprecated. + * (This logic is duplicated in `PyArray_CanCastArrayTo`) + */ + if (PyDataType_ISUNSIZED(to) && to->subarray == NULL) { + to = NULL; /* consider mainly S0 and U0 as S and U */ } - /* If safe or same-kind casts are allowed */ - else if (casting == NPY_SAFE_CASTING || casting == NPY_SAME_KIND_CASTING) { - if (PyArray_CanCastTo(from, to)) { - return 1; - } - else if(casting == NPY_SAME_KIND_CASTING) { - /* - * Also allow casting from lower to higher kinds, according - * to the ordering provided by dtype_kind_to_ordering. - * Some kinds, like datetime, don't fit in the hierarchy, - * and are special cased as -1. - */ - int from_order, to_order; - from_order = dtype_kind_to_ordering(from->kind); - to_order = dtype_kind_to_ordering(to->kind); - - return from_order != -1 && from_order <= to_order; - } - else { - return 0; - } - } - /* NPY_NO_CASTING or NPY_EQUIV_CASTING was specified */ - else { + int is_valid = PyArray_CheckCastSafety(casting, from, to, to_dtype); + /* Clear any errors and consider this unsafe (should likely be changed) */ + if (is_valid < 0) { + PyErr_Clear(); return 0; } + return is_valid; } + /* CanCastArrayTo needs this function */ static int min_scalar_type_num(char *valueptr, int type_num, int *is_small_unsigned); + +/* + * NOTE: This function uses value based casting logic for scalars. It will + * require updates when we phase out value-based-casting. + */ NPY_NO_EXPORT npy_bool can_cast_scalar_to(PyArray_Descr *scal_type, char *scal_data, PyArray_Descr *to, NPY_CASTING casting) { - int swap; - int is_small_unsigned = 0, type_num; - npy_bool ret; - PyArray_Descr *dtype; - - /* An aligned memory buffer large enough to hold any type */ - npy_longlong value[4]; - /* * If the two dtypes are actually references to the same object * or if casting type is forced unsafe then always OK. + * + * TODO: Assuming that unsafe casting always works is not actually correct */ if (scal_type == to || casting == NPY_UNSAFE_CASTING ) { return 1; } + int valid = PyArray_CheckCastSafety(casting, scal_type, to, NPY_DTYPE(to)); + if (valid == 1) { + /* This is definitely a valid cast. */ + return 1; + } + if (valid < 0) { + /* Probably must return 0, but just keep trying for now. */ + PyErr_Clear(); + } + /* - * If the scalar isn't a number, or the rule is stricter than - * NPY_SAFE_CASTING, use the straight type-based rules + * If the scalar isn't a number, value-based casting cannot kick in and + * we must not attempt it. + * (Additional fast-checks would be possible, but probably unnecessary.) */ - if (!PyTypeNum_ISNUMBER(scal_type->type_num) || - casting < NPY_SAFE_CASTING) { - return PyArray_CanCastTypeTo(scal_type, to, casting); + if (!PyTypeNum_ISNUMBER(scal_type->type_num)) { + return 0; } - swap = !PyArray_ISNBO(scal_type->byteorder); + /* + * At this point we have to check value-based casting. + */ + PyArray_Descr *dtype; + int is_small_unsigned = 0, type_num; + /* An aligned memory buffer large enough to hold any builtin numeric type */ + npy_longlong value[4]; + + int swap = !PyArray_ISNBO(scal_type->byteorder); scal_type->f->copyswap(&value, scal_data, swap, NULL); type_num = min_scalar_type_num((char *)&value, scal_type->type_num, @@ -880,7 +749,7 @@ can_cast_scalar_to(PyArray_Descr *scal_type, char *scal_data, PyObject_Print(to, stdout, 0); printf("\n"); #endif - ret = PyArray_CanCastTypeTo(dtype, to, casting); + npy_bool ret = PyArray_CanCastTypeTo(dtype, to, casting); Py_DECREF(dtype); return ret; } @@ -896,16 +765,79 @@ PyArray_CanCastArrayTo(PyArrayObject *arr, PyArray_Descr *to, NPY_CASTING casting) { PyArray_Descr *from = PyArray_DESCR(arr); + PyArray_DTypeMeta *to_dtype = NPY_DTYPE(to); - /* If it's a scalar, check the value */ - if (PyArray_NDIM(arr) == 0 && !PyArray_HASFIELDS(arr)) { + /* NOTE, TODO: The same logic as `PyArray_CanCastTypeTo`: */ + if (PyDataType_ISUNSIZED(to) && to->subarray == NULL) { + to = NULL; + } + + /* + * If it's a scalar, check the value. (This only currently matters for + * numeric types and for `to == NULL` it can't be numeric.) + */ + if (PyArray_NDIM(arr) == 0 && !PyArray_HASFIELDS(arr) && to != NULL) { return can_cast_scalar_to(from, PyArray_DATA(arr), to, casting); } - /* Otherwise, use the standard rules */ - return PyArray_CanCastTypeTo(from, to, casting); + /* Otherwise, use the standard rules (same as `PyArray_CanCastTypeTo`) */ + int is_valid = PyArray_CheckCastSafety(casting, from, to, to_dtype); + /* Clear any errors and consider this unsafe (should likely be changed) */ + if (is_valid < 0) { + PyErr_Clear(); + return 0; + } + return is_valid; +} + + +NPY_NO_EXPORT const char * +npy_casting_to_string(NPY_CASTING casting) +{ + switch (casting) { + case NPY_NO_CASTING: + return "'no'"; + case NPY_EQUIV_CASTING: + return "'equiv'"; + case NPY_SAFE_CASTING: + return "'safe'"; + case NPY_SAME_KIND_CASTING: + return "'same_kind'"; + case NPY_UNSAFE_CASTING: + return "'unsafe'"; + default: + return "<unknown>"; + } +} + + +/** + * Helper function to set a useful error when casting is not possible. + * + * @param src_dtype + * @param dst_dtype + * @param casting + * @param scalar Whether this was a "scalar" cast (includes 0-D array with + * PyArray_CanCastArrayTo result). + */ +NPY_NO_EXPORT void +npy_set_invalid_cast_error( + PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype, + NPY_CASTING casting, npy_bool scalar) +{ + char *msg; + + if (!scalar) { + msg = "Cannot cast array data from %R to %R according to the rule %s"; + } + else { + msg = "Cannot cast scalar from %R to %R according to the rule %s"; + } + PyErr_Format(PyExc_TypeError, + msg, src_dtype, dst_dtype, npy_casting_to_string(casting)); } + /*NUMPY_API * See if array scalars can be cast. * @@ -981,7 +913,7 @@ promote_types(PyArray_Descr *type1, PyArray_Descr *type2, * Returns a new reference to type if it is already NBO, otherwise * returns a copy converted to NBO. */ -static PyArray_Descr * +NPY_NO_EXPORT PyArray_Descr * ensure_dtype_nbo(PyArray_Descr *type) { if (PyArray_ISNBO(type->byteorder)) { @@ -993,343 +925,204 @@ ensure_dtype_nbo(PyArray_Descr *type) } } -/*NUMPY_API - * Produces the smallest size and lowest kind type to which both - * input types can be cast. + +/** + * This function should possibly become public API eventually. At this + * time it is implemented by falling back to `PyArray_AdaptFlexibleDType`. + * We will use `CastingImpl[from, to].resolve_descriptors(...)` to implement + * this logic. + * Before that, the API needs to be reviewed though. + * + * WARNING: This function currently does not guarantee that `descr` can + * actually be cast to the given DType. + * + * @param descr The dtype instance to adapt "cast" + * @param given_DType The DType class for which we wish to find an instance able + * to represent `descr`. + * @returns Instance of `given_DType`. If `given_DType` is parametric the + * descr may be adapted to hold it. */ NPY_NO_EXPORT PyArray_Descr * -PyArray_PromoteTypes(PyArray_Descr *type1, PyArray_Descr *type2) +PyArray_CastDescrToDType(PyArray_Descr *descr, PyArray_DTypeMeta *given_DType) { - int type_num1, type_num2, ret_type_num; + if (NPY_DTYPE(descr) == given_DType) { + Py_INCREF(descr); + return descr; + } + if (!NPY_DT_is_parametric(given_DType)) { + /* + * Don't actually do anything, the default is always the result + * of any cast. + */ + return NPY_DT_CALL_default_descr(given_DType); + } + if (PyObject_TypeCheck((PyObject *)descr, (PyTypeObject *)given_DType)) { + Py_INCREF(descr); + return descr; + } + + PyObject *tmp = PyArray_GetCastingImpl(NPY_DTYPE(descr), given_DType); + if (tmp == NULL || tmp == Py_None) { + Py_XDECREF(tmp); + goto error; + } + PyArray_DTypeMeta *dtypes[2] = {NPY_DTYPE(descr), given_DType}; + PyArray_Descr *given_descrs[2] = {descr, NULL}; + PyArray_Descr *loop_descrs[2]; + + PyArrayMethodObject *meth = (PyArrayMethodObject *)tmp; + NPY_CASTING casting = meth->resolve_descriptors( + meth, dtypes, given_descrs, loop_descrs); + Py_DECREF(tmp); + if (casting < 0) { + goto error; + } + Py_DECREF(loop_descrs[0]); + return loop_descrs[1]; + + error:; /* (; due to compiler limitations) */ + PyObject *err_type = NULL, *err_value = NULL, *err_traceback = NULL; + PyErr_Fetch(&err_type, &err_value, &err_traceback); + PyErr_Format(PyExc_TypeError, + "cannot cast dtype %S to %S.", descr, given_DType); + npy_PyErr_ChainExceptionsCause(err_type, err_value, err_traceback); + return NULL; +} - /* - * Fast path for identical dtypes. - * - * Non-native-byte-order types are converted to native ones below, so we - * can't quit early. - */ - if (type1 == type2 && PyArray_ISNBO(type1->byteorder)) { - Py_INCREF(type1); - return type1; + +/* + * Helper to find the target descriptor for multiple arrays given an input + * one that may be a DType class (e.g. "U" or "S"). + * Works with arrays, since that is what `concatenate` works with. However, + * unlike `np.array(...)` or `arr.astype()` we will never inspect the array's + * content, which means that object arrays can only be cast to strings if a + * fixed width is provided (same for string -> generic datetime). + * + * As this function uses `PyArray_ExtractDTypeAndDescriptor`, it should + * eventually be refactored to move the step to an earlier point. + */ +NPY_NO_EXPORT PyArray_Descr * +PyArray_FindConcatenationDescriptor( + npy_intp n, PyArrayObject **arrays, PyObject *requested_dtype) +{ + if (requested_dtype == NULL) { + return PyArray_LegacyResultType(n, arrays, 0, NULL); } - type_num1 = type1->type_num; - type_num2 = type2->type_num; + PyArray_DTypeMeta *common_dtype; + PyArray_Descr *result = NULL; + if (PyArray_ExtractDTypeAndDescriptor( + requested_dtype, &result, &common_dtype) < 0) { + return NULL; + } + if (result != NULL) { + if (result->subarray != NULL) { + PyErr_Format(PyExc_TypeError, + "The dtype `%R` is not a valid dtype for concatenation " + "since it is a subarray dtype (the subarray dimensions " + "would be added as array dimensions).", result); + Py_SETREF(result, NULL); + } + goto finish; + } + assert(n > 0); /* concatenate requires at least one array input. */ - /* If they're built-in types, use the promotion table */ - if (type_num1 < NPY_NTYPES && type_num2 < NPY_NTYPES) { - ret_type_num = _npy_type_promotion_table[type_num1][type_num2]; - /* - * The table doesn't handle string/unicode/void/datetime/timedelta, - * so check the result - */ - if (ret_type_num >= 0) { - return PyArray_DescrFromType(ret_type_num); + /* + * NOTE: This code duplicates `PyArray_CastToDTypeAndPromoteDescriptors` + * to use arrays, copying the descriptors seems not better. + */ + PyArray_Descr *descr = PyArray_DESCR(arrays[0]); + result = PyArray_CastDescrToDType(descr, common_dtype); + if (result == NULL || n == 1) { + goto finish; + } + for (npy_intp i = 1; i < n; i++) { + descr = PyArray_DESCR(arrays[i]); + PyArray_Descr *curr = PyArray_CastDescrToDType(descr, common_dtype); + if (curr == NULL) { + Py_SETREF(result, NULL); + goto finish; + } + Py_SETREF(result, NPY_DT_SLOTS(common_dtype)->common_instance(result, curr)); + Py_DECREF(curr); + if (result == NULL) { + goto finish; } } - /* If one or both are user defined, calculate it */ - else { - int skind1 = NPY_NOSCALAR, skind2 = NPY_NOSCALAR, skind; - - if (PyArray_CanCastTo(type2, type1)) { - /* Promoted types are always native byte order */ - return ensure_dtype_nbo(type1); - } - else if (PyArray_CanCastTo(type1, type2)) { - /* Promoted types are always native byte order */ - return ensure_dtype_nbo(type2); - } - - /* Convert the 'kind' char into a scalar kind */ - switch (type1->kind) { - case 'b': - skind1 = NPY_BOOL_SCALAR; - break; - case 'u': - skind1 = NPY_INTPOS_SCALAR; - break; - case 'i': - skind1 = NPY_INTNEG_SCALAR; - break; - case 'f': - skind1 = NPY_FLOAT_SCALAR; - break; - case 'c': - skind1 = NPY_COMPLEX_SCALAR; - break; - } - switch (type2->kind) { - case 'b': - skind2 = NPY_BOOL_SCALAR; - break; - case 'u': - skind2 = NPY_INTPOS_SCALAR; - break; - case 'i': - skind2 = NPY_INTNEG_SCALAR; - break; - case 'f': - skind2 = NPY_FLOAT_SCALAR; - break; - case 'c': - skind2 = NPY_COMPLEX_SCALAR; - break; - } - - /* If both are scalars, there may be a promotion possible */ - if (skind1 != NPY_NOSCALAR && skind2 != NPY_NOSCALAR) { - - /* Start with the larger scalar kind */ - skind = (skind1 > skind2) ? skind1 : skind2; - ret_type_num = _npy_smallest_type_of_kind_table[skind]; - - for (;;) { - - /* If there is no larger type of this kind, try a larger kind */ - if (ret_type_num < 0) { - ++skind; - /* Use -1 to signal no promoted type found */ - if (skind < NPY_NSCALARKINDS) { - ret_type_num = _npy_smallest_type_of_kind_table[skind]; - } - else { - break; - } - } - /* If we found a type to which we can promote both, done! */ - if (PyArray_CanCastSafely(type_num1, ret_type_num) && - PyArray_CanCastSafely(type_num2, ret_type_num)) { - return PyArray_DescrFromType(ret_type_num); - } + finish: + Py_DECREF(common_dtype); + return result; +} - /* Try the next larger type of this kind */ - ret_type_num = _npy_next_larger_type_table[ret_type_num]; - } - } +/*NUMPY_API + * Produces the smallest size and lowest kind type to which both + * input types can be cast. + */ +NPY_NO_EXPORT PyArray_Descr * +PyArray_PromoteTypes(PyArray_Descr *type1, PyArray_Descr *type2) +{ + PyArray_DTypeMeta *common_dtype; + PyArray_Descr *res; - PyErr_SetString(PyExc_TypeError, - "invalid type promotion with custom data type"); - return NULL; + /* Fast path for identical inputs (NOTE: This path preserves metadata!) */ + if (type1 == type2 && PyArray_ISNBO(type1->byteorder)) { + Py_INCREF(type1); + return type1; } - switch (type_num1) { - /* BOOL can convert to anything except datetime/void */ - case NPY_BOOL: - if (type_num2 == NPY_STRING || type_num2 == NPY_UNICODE) { - int char_size = 1; - if (type_num2 == NPY_UNICODE) { - char_size = 4; - } - if (type2->elsize < 5 * char_size) { - PyArray_Descr *ret = NULL; - PyArray_Descr *temp = PyArray_DescrNew(type2); - ret = ensure_dtype_nbo(temp); - ret->elsize = 5 * char_size; - Py_DECREF(temp); - return ret; - } - return ensure_dtype_nbo(type2); - } - else if (type_num2 != NPY_DATETIME && type_num2 != NPY_VOID) { - return ensure_dtype_nbo(type2); - } - break; - /* For strings and unicodes, take the larger size */ - case NPY_STRING: - if (type_num2 == NPY_STRING) { - if (type1->elsize > type2->elsize) { - return ensure_dtype_nbo(type1); - } - else { - return ensure_dtype_nbo(type2); - } - } - else if (type_num2 == NPY_UNICODE) { - if (type2->elsize >= type1->elsize * 4) { - return ensure_dtype_nbo(type2); - } - else { - PyArray_Descr *d = PyArray_DescrNewFromType(NPY_UNICODE); - if (d == NULL) { - return NULL; - } - d->elsize = type1->elsize * 4; - return d; - } - } - /* Allow NUMBER -> STRING */ - else if (PyTypeNum_ISNUMBER(type_num2)) { - PyArray_Descr *ret = NULL; - PyArray_Descr *temp = PyArray_DescrNew(type1); - PyDataType_MAKEUNSIZED(temp); - PyArray_AdaptFlexibleDType(NULL, type2, &temp); - if (temp->elsize > type1->elsize) { - ret = ensure_dtype_nbo(temp); - } - else { - ret = ensure_dtype_nbo(type1); - } - Py_DECREF(temp); - return ret; - } - break; - case NPY_UNICODE: - if (type_num2 == NPY_UNICODE) { - if (type1->elsize > type2->elsize) { - return ensure_dtype_nbo(type1); - } - else { - return ensure_dtype_nbo(type2); - } - } - else if (type_num2 == NPY_STRING) { - if (type1->elsize >= type2->elsize * 4) { - return ensure_dtype_nbo(type1); - } - else { - PyArray_Descr *d = PyArray_DescrNewFromType(NPY_UNICODE); - if (d == NULL) { - return NULL; - } - d->elsize = type2->elsize * 4; - return d; - } - } - /* Allow NUMBER -> UNICODE */ - else if (PyTypeNum_ISNUMBER(type_num2)) { - PyArray_Descr *ret = NULL; - PyArray_Descr *temp = PyArray_DescrNew(type1); - PyDataType_MAKEUNSIZED(temp); - PyArray_AdaptFlexibleDType(NULL, type2, &temp); - if (temp->elsize > type1->elsize) { - ret = ensure_dtype_nbo(temp); - } - else { - ret = ensure_dtype_nbo(type1); - } - Py_DECREF(temp); - return ret; - } - break; - case NPY_DATETIME: - case NPY_TIMEDELTA: - if (type_num2 == NPY_DATETIME || type_num2 == NPY_TIMEDELTA) { - return datetime_type_promotion(type1, type2); - } - break; + common_dtype = PyArray_CommonDType(NPY_DTYPE(type1), NPY_DTYPE(type2)); + if (common_dtype == NULL) { + return NULL; } - switch (type_num2) { - /* BOOL can convert to almost anything */ - case NPY_BOOL: - if (type_num2 == NPY_STRING || type_num2 == NPY_UNICODE) { - int char_size = 1; - if (type_num2 == NPY_UNICODE) { - char_size = 4; - } - if (type2->elsize < 5 * char_size) { - PyArray_Descr *ret = NULL; - PyArray_Descr *temp = PyArray_DescrNew(type2); - ret = ensure_dtype_nbo(temp); - ret->elsize = 5 * char_size; - Py_DECREF(temp); - return ret; - } - return ensure_dtype_nbo(type2); - } - else if (type_num1 != NPY_DATETIME && type_num1 != NPY_TIMEDELTA && - type_num1 != NPY_VOID) { - return ensure_dtype_nbo(type1); - } - break; - case NPY_STRING: - /* Allow NUMBER -> STRING */ - if (PyTypeNum_ISNUMBER(type_num1)) { - PyArray_Descr *ret = NULL; - PyArray_Descr *temp = PyArray_DescrNew(type2); - PyDataType_MAKEUNSIZED(temp); - PyArray_AdaptFlexibleDType(NULL, type1, &temp); - if (temp->elsize > type2->elsize) { - ret = ensure_dtype_nbo(temp); - } - else { - ret = ensure_dtype_nbo(type2); - } - Py_DECREF(temp); - return ret; - } - break; - case NPY_UNICODE: - /* Allow NUMBER -> UNICODE */ - if (PyTypeNum_ISNUMBER(type_num1)) { - PyArray_Descr *ret = NULL; - PyArray_Descr *temp = PyArray_DescrNew(type2); - PyDataType_MAKEUNSIZED(temp); - PyArray_AdaptFlexibleDType(NULL, type1, &temp); - if (temp->elsize > type2->elsize) { - ret = ensure_dtype_nbo(temp); - } - else { - ret = ensure_dtype_nbo(type2); - } - Py_DECREF(temp); - return ret; - } - break; - case NPY_TIMEDELTA: - if (PyTypeNum_ISINTEGER(type_num1) || - PyTypeNum_ISFLOAT(type_num1)) { - return ensure_dtype_nbo(type2); - } - break; + if (!NPY_DT_is_parametric(common_dtype)) { + /* Note that this path loses all metadata */ + res = NPY_DT_CALL_default_descr(common_dtype); + Py_DECREF(common_dtype); + return res; } - /* For types equivalent up to endianness, can return either */ - if (PyArray_CanCastTypeTo(type1, type2, NPY_EQUIV_CASTING)) { - return ensure_dtype_nbo(type1); + /* Cast the input types to the common DType if necessary */ + type1 = PyArray_CastDescrToDType(type1, common_dtype); + if (type1 == NULL) { + Py_DECREF(common_dtype); + return NULL; + } + type2 = PyArray_CastDescrToDType(type2, common_dtype); + if (type2 == NULL) { + Py_DECREF(type1); + Py_DECREF(common_dtype); + return NULL; } - - /* TODO: Also combine fields, subarrays, strings, etc */ /* - printf("invalid type promotion: "); - PyObject_Print(type1, stdout, 0); - printf(" "); - PyObject_Print(type2, stdout, 0); - printf("\n"); - */ - PyErr_SetString(PyExc_TypeError, "invalid type promotion"); - return NULL; + * And find the common instance of the two inputs + * NOTE: Common instance preserves metadata (normally and of one input) + */ + res = NPY_DT_SLOTS(common_dtype)->common_instance(type1, type2); + Py_DECREF(type1); + Py_DECREF(type2); + Py_DECREF(common_dtype); + return res; } /* * Produces the smallest size and lowest kind type to which all * input types can be cast. * - * Equivalent to functools.reduce(PyArray_PromoteTypes, types) + * Roughly equivalent to functools.reduce(PyArray_PromoteTypes, types) + * but uses a more complex pairwise approach. */ NPY_NO_EXPORT PyArray_Descr * PyArray_PromoteTypeSequence(PyArray_Descr **types, npy_intp ntypes) { - npy_intp i; - PyArray_Descr *ret = NULL; if (ntypes == 0) { PyErr_SetString(PyExc_TypeError, "at least one type needed to promote"); return NULL; } - ret = types[0]; - Py_INCREF(ret); - for (i = 1; i < ntypes; ++i) { - PyArray_Descr *tmp = PyArray_PromoteTypes(types[i], ret); - Py_DECREF(ret); - ret = tmp; - if (ret == NULL) { - return NULL; - } - } - return ret; + return PyArray_ResultType(0, NULL, ntypes, types); } /* @@ -1686,46 +1479,24 @@ dtype_kind_to_simplified_ordering(char kind) } } -/*NUMPY_API - * Produces the result type of a bunch of inputs, using the UFunc - * type promotion rules. Use this function when you have a set of - * input arrays, and need to determine an output array dtype. - * - * If all the inputs are scalars (have 0 dimensions) or the maximum "kind" - * of the scalars is greater than the maximum "kind" of the arrays, does - * a regular type promotion. - * - * Otherwise, does a type promotion on the MinScalarType - * of all the inputs. Data types passed directly are treated as array - * types. + +/* + * Determine if there is a mix of scalars and arrays/dtypes. + * If this is the case, the scalars should be handled as the minimum type + * capable of holding the value when the maximum "category" of the scalars + * surpasses the maximum "category" of the arrays/dtypes. + * If the scalars are of a lower or same category as the arrays, they may be + * demoted to a lower type within their category (the lowest type they can + * be cast to safely according to scalar casting rules). * + * If any new style dtype is involved (non-legacy), always returns 0. */ -NPY_NO_EXPORT PyArray_Descr * -PyArray_ResultType(npy_intp narrs, PyArrayObject **arr, - npy_intp ndtypes, PyArray_Descr **dtypes) +NPY_NO_EXPORT int +should_use_min_scalar(npy_intp narrs, PyArrayObject **arr, + npy_intp ndtypes, PyArray_Descr **dtypes) { - npy_intp i; - int use_min_scalar; - - /* If there's just one type, pass it through */ - if (narrs + ndtypes == 1) { - PyArray_Descr *ret = NULL; - if (narrs == 1) { - ret = PyArray_DESCR(arr[0]); - } - else { - ret = dtypes[0]; - } - Py_INCREF(ret); - return ret; - } + int use_min_scalar = 0; - /* - * Determine if there are any scalars, and if so, whether - * the maximum "kind" of the scalars surpasses the maximum - * "kind" of the arrays - */ - use_min_scalar = 0; if (narrs > 0) { int all_scalars; int max_scalar_kind = -1; @@ -1734,7 +1505,10 @@ PyArray_ResultType(npy_intp narrs, PyArrayObject **arr, all_scalars = (ndtypes > 0) ? 0 : 1; /* Compute the maximum "kinds" and whether everything is scalar */ - for (i = 0; i < narrs; ++i) { + for (npy_intp i = 0; i < narrs; ++i) { + if (!NPY_DT_is_legacy(NPY_DTYPE(PyArray_DESCR(arr[i])))) { + return 0; + } if (PyArray_NDIM(arr[i]) == 0) { int kind = dtype_kind_to_simplified_ordering( PyArray_DESCR(arr[i])->kind); @@ -1755,7 +1529,10 @@ PyArray_ResultType(npy_intp narrs, PyArrayObject **arr, * If the max scalar kind is bigger than the max array kind, * finish computing the max array kind */ - for (i = 0; i < ndtypes; ++i) { + for (npy_intp i = 0; i < ndtypes; ++i) { + if (!NPY_DT_is_legacy(NPY_DTYPE(dtypes[i]))) { + return 0; + } int kind = dtype_kind_to_simplified_ordering(dtypes[i]->kind); if (kind > max_array_kind) { max_array_kind = kind; @@ -1767,6 +1544,268 @@ PyArray_ResultType(npy_intp narrs, PyArrayObject **arr, use_min_scalar = 1; } } + return use_min_scalar; +} + + +/* + * Utility function used only in PyArray_ResultType for value-based logic. + * See that function for the meaning and contents of the parameters. + */ +static PyArray_Descr * +get_descr_from_cast_or_value( + npy_intp i, + PyArrayObject *arrs[], + npy_intp ndtypes, + PyArray_Descr *descriptor, + PyArray_DTypeMeta *common_dtype) +{ + PyArray_Descr *curr; + if (NPY_LIKELY(i < ndtypes || + !(PyArray_FLAGS(arrs[i-ndtypes]) & _NPY_ARRAY_WAS_PYSCALAR))) { + curr = PyArray_CastDescrToDType(descriptor, common_dtype); + } + else { + /* + * Unlike `PyArray_CastToDTypeAndPromoteDescriptors`, deal with + * plain Python values "graciously". This recovers the original + * value the long route, but it should almost never happen... + */ + PyObject *tmp = PyArray_GETITEM(arrs[i-ndtypes], + PyArray_BYTES(arrs[i-ndtypes])); + if (tmp == NULL) { + return NULL; + } + curr = NPY_DT_CALL_discover_descr_from_pyobject(common_dtype, tmp); + Py_DECREF(tmp); + } + return curr; +} + +/*NUMPY_API + * + * Produces the result type of a bunch of inputs, using the same rules + * as `np.result_type`. + * + * NOTE: This function is expected to through a transitional period or + * change behaviour. DTypes should always be strictly enforced for + * 0-D arrays, while "weak DTypes" will be used to represent Python + * integers, floats, and complex in all cases. + * (Within this function, these are currently flagged on the array + * object to work through `np.result_type`, this may change.) + * + * Until a time where this transition is complete, we probably cannot + * add new "weak DTypes" or allow users to create their own. + */ +NPY_NO_EXPORT PyArray_Descr * +PyArray_ResultType( + npy_intp narrs, PyArrayObject *arrs[], + npy_intp ndtypes, PyArray_Descr *descrs[]) +{ + PyArray_Descr *result = NULL; + + if (narrs + ndtypes <= 1) { + /* If the input is a single value, skip promotion. */ + if (narrs == 1) { + result = PyArray_DTYPE(arrs[0]); + } + else if (ndtypes == 1) { + result = descrs[0]; + } + else { + PyErr_SetString(PyExc_TypeError, + "no arrays or types available to calculate result type"); + return NULL; + } + return ensure_dtype_nbo(result); + } + + void **info_on_heap = NULL; + void *_info_on_stack[NPY_MAXARGS * 2]; + PyArray_DTypeMeta **all_DTypes; + PyArray_Descr **all_descriptors; + + if (narrs + ndtypes > NPY_MAXARGS) { + info_on_heap = PyMem_Malloc(2 * (narrs+ndtypes) * sizeof(PyObject *)); + if (info_on_heap == NULL) { + PyErr_NoMemory(); + return NULL; + } + all_DTypes = (PyArray_DTypeMeta **)info_on_heap; + all_descriptors = (PyArray_Descr **)(info_on_heap + narrs + ndtypes); + } + else { + all_DTypes = (PyArray_DTypeMeta **)_info_on_stack; + all_descriptors = (PyArray_Descr **)(_info_on_stack + narrs + ndtypes); + } + + /* Copy all dtypes into a single array defining non-value-based behaviour */ + for (npy_intp i=0; i < ndtypes; i++) { + all_DTypes[i] = NPY_DTYPE(descrs[i]); + Py_INCREF(all_DTypes[i]); + all_descriptors[i] = descrs[i]; + } + + int at_least_one_scalar = 0; + int all_pyscalar = ndtypes == 0; + for (npy_intp i=0, i_all=ndtypes; i < narrs; i++, i_all++) { + /* Array descr is also the correct "default" for scalars: */ + if (PyArray_NDIM(arrs[i]) == 0) { + at_least_one_scalar = 1; + } + + if (!(PyArray_FLAGS(arrs[i]) & _NPY_ARRAY_WAS_PYSCALAR)) { + /* This was not a scalar with an abstract DType */ + all_descriptors[i_all] = PyArray_DTYPE(arrs[i]); + all_DTypes[i_all] = NPY_DTYPE(all_descriptors[i_all]); + Py_INCREF(all_DTypes[i_all]); + all_pyscalar = 0; + continue; + } + + /* + * The original was a Python scalar with an abstract DType. + * In a future world, this type of code may need to work on the + * DType level first and discover those from the original value. + * But, right now we limit the logic to int, float, and complex + * and do it here to allow for a transition without losing all of + * our remaining sanity. + */ + if (PyArray_ISFLOAT(arrs[i])) { + all_DTypes[i_all] = &PyArray_PyFloatAbstractDType; + } + else if (PyArray_ISCOMPLEX(arrs[i])) { + all_DTypes[i_all] = &PyArray_PyComplexAbstractDType; + } + else { + /* N.B.: Could even be an object dtype here for large ints */ + all_DTypes[i_all] = &PyArray_PyIntAbstractDType; + } + Py_INCREF(all_DTypes[i_all]); + /* + * Leave the descriptor empty, if we need it, we will have to go + * to more extreme lengths unfortunately. + */ + all_descriptors[i_all] = NULL; + } + + PyArray_DTypeMeta *common_dtype = PyArray_PromoteDTypeSequence( + narrs+ndtypes, all_DTypes); + for (npy_intp i=0; i < narrs+ndtypes; i++) { + Py_DECREF(all_DTypes[i]); + } + if (common_dtype == NULL) { + goto error; + } + + if (NPY_DT_is_abstract(common_dtype)) { + /* (ab)use default descriptor to define a default */ + PyArray_Descr *tmp_descr = NPY_DT_CALL_default_descr(common_dtype); + if (tmp_descr == NULL) { + goto error; + } + Py_INCREF(NPY_DTYPE(tmp_descr)); + Py_SETREF(common_dtype, NPY_DTYPE(tmp_descr)); + Py_DECREF(tmp_descr); + } + + /* + * NOTE: Code duplicates `PyArray_CastToDTypeAndPromoteDescriptors`, but + * supports special handling of the abstract values. + */ + if (!NPY_DT_is_parametric(common_dtype)) { + /* Note that this "fast" path loses all metadata */ + result = NPY_DT_CALL_default_descr(common_dtype); + } + else { + result = get_descr_from_cast_or_value( + 0, arrs, ndtypes, all_descriptors[0], common_dtype); + if (result == NULL) { + goto error; + } + + for (npy_intp i = 1; i < ndtypes+narrs; i++) { + PyArray_Descr *curr = get_descr_from_cast_or_value( + i, arrs, ndtypes, all_descriptors[i], common_dtype); + if (curr == NULL) { + goto error; + } + Py_SETREF(result, NPY_DT_SLOTS(common_dtype)->common_instance(result, curr)); + Py_DECREF(curr); + if (result == NULL) { + goto error; + } + } + } + + /* + * Unfortunately, when 0-D "scalar" arrays are involved and mixed, we + * have to use the value-based logic. The intention is to move away from + * the complex logic arising from it. We thus fall back to the legacy + * version here. + * It may be possible to micro-optimize this to skip some of the above + * logic when this path is necessary. + */ + if (at_least_one_scalar && !all_pyscalar && result->type_num < NPY_NTYPES) { + PyArray_Descr *legacy_result = PyArray_LegacyResultType( + narrs, arrs, ndtypes, descrs); + if (legacy_result == NULL) { + /* + * Going from error to success should not really happen, but is + * probably OK if it does. + */ + goto error; + } + /* Return the old "legacy" result (could warn here if different) */ + Py_SETREF(result, legacy_result); + } + + Py_DECREF(common_dtype); + PyMem_Free(info_on_heap); + return result; + + error: + Py_XDECREF(result); + Py_XDECREF(common_dtype); + PyMem_Free(info_on_heap); + return NULL; +} + + +/* + * Produces the result type of a bunch of inputs, using the UFunc + * type promotion rules. Use this function when you have a set of + * input arrays, and need to determine an output array dtype. + * + * If all the inputs are scalars (have 0 dimensions) or the maximum "kind" + * of the scalars is greater than the maximum "kind" of the arrays, does + * a regular type promotion. + * + * Otherwise, does a type promotion on the MinScalarType + * of all the inputs. Data types passed directly are treated as array + * types. + */ +NPY_NO_EXPORT PyArray_Descr * +PyArray_LegacyResultType( + npy_intp narrs, PyArrayObject **arr, + npy_intp ndtypes, PyArray_Descr **dtypes) +{ + npy_intp i; + + /* If there's just one type, pass it through */ + if (narrs + ndtypes == 1) { + PyArray_Descr *ret = NULL; + if (narrs == 1) { + ret = PyArray_DESCR(arr[0]); + } + else { + ret = dtypes[0]; + } + Py_INCREF(ret); + return ret; + } + + int use_min_scalar = should_use_min_scalar(narrs, arr, ndtypes, dtypes); /* Loop through all the types, promoting them */ if (!use_min_scalar) { @@ -1848,6 +1887,49 @@ PyArray_ResultType(npy_intp narrs, PyArrayObject **arr, } } +/** + * Promotion of descriptors (of arbitrary DType) to their correctly + * promoted instances of the given DType. + * I.e. the given DType could be a string, which then finds the correct + * string length, given all `descrs`. + * + * @param ndescrs number of descriptors to cast and find the common instance. + * At least one must be passed in. + * @param descrs The descriptors to work with. + * @param DType The DType of the desired output descriptor. + */ +NPY_NO_EXPORT PyArray_Descr * +PyArray_CastToDTypeAndPromoteDescriptors( + npy_intp ndescr, PyArray_Descr *descrs[], PyArray_DTypeMeta *DType) +{ + assert(ndescr > 0); + + PyArray_Descr *result = PyArray_CastDescrToDType(descrs[0], DType); + if (result == NULL || ndescr == 1) { + return result; + } + if (!NPY_DT_is_parametric(DType)) { + /* Note that this "fast" path loses all metadata */ + Py_DECREF(result); + return NPY_DT_CALL_default_descr(DType); + } + + for (npy_intp i = 1; i < ndescr; i++) { + PyArray_Descr *curr = PyArray_CastDescrToDType(descrs[i], DType); + if (curr == NULL) { + Py_DECREF(result); + return NULL; + } + Py_SETREF(result, NPY_DT_SLOTS(DType)->common_instance(result, curr)); + Py_DECREF(curr); + if (result == NULL) { + return NULL; + } + } + return result; +} + + /*NUMPY_API * Is the typenum valid? */ @@ -1909,7 +1991,7 @@ PyArray_Zero(PyArrayObject *arr) } if (zero_obj == NULL) { - zero_obj = PyInt_FromLong((long) 0); + zero_obj = PyLong_FromLong((long) 0); if (zero_obj == NULL) { return NULL; } @@ -1955,7 +2037,7 @@ PyArray_One(PyArrayObject *arr) } if (one_obj == NULL) { - one_obj = PyInt_FromLong((long) 1); + one_obj = PyLong_FromLong((long) 1); if (one_obj == NULL) { return NULL; } @@ -2001,7 +2083,6 @@ PyArray_ObjectType(PyObject *op, int minimum_type) return NPY_NOTYPE; } } - if (PyArray_DTypeFromObject(op, NPY_MAXDIMS, &dtype) < 0) { return NPY_NOTYPE; } @@ -2009,6 +2090,19 @@ PyArray_ObjectType(PyObject *op, int minimum_type) if (dtype == NULL) { ret = NPY_DEFAULT_TYPE; } + else if (!NPY_DT_is_legacy(NPY_DTYPE(dtype))) { + /* + * TODO: If we keep all type number style API working, by defining + * type numbers always. We may be able to allow this again. + */ + PyErr_Format(PyExc_TypeError, + "This function currently only supports native NumPy dtypes " + "and old-style user dtypes, but the dtype was %S.\n" + "(The function may need to be updated to support arbitrary" + "user dtypes.)", + dtype); + ret = NPY_NOTYPE; + } else { ret = dtype->type_num; } @@ -2020,16 +2114,19 @@ PyArray_ObjectType(PyObject *op, int minimum_type) /* Raises error when len(op) == 0 */ -/*NUMPY_API*/ +/*NUMPY_API + * + * This function is only used in one place within NumPy and should + * generally be avoided. It is provided mainly for backward compatibility. + * + * The user of the function has to free the returned array with PyDataMem_FREE. + */ NPY_NO_EXPORT PyArrayObject ** PyArray_ConvertToCommonType(PyObject *op, int *retn) { - int i, n, allscalars = 0; + int i, n; + PyArray_Descr *common_descr = NULL; PyArrayObject **mps = NULL; - PyObject *otmp; - PyArray_Descr *intype = NULL, *stype = NULL; - PyArray_Descr *newtype = NULL; - NPY_SCALARKIND scalarkind = NPY_NOSCALAR, intypekind = NPY_NOSCALAR; *retn = n = PySequence_Length(op); if (n == 0) { @@ -2065,86 +2162,41 @@ PyArray_ConvertToCommonType(PyObject *op, int *retn) } for (i = 0; i < n; i++) { - otmp = PySequence_GetItem(op, i); - if (!PyArray_CheckAnyScalar(otmp)) { - newtype = PyArray_DescrFromObject(otmp, intype); - Py_XDECREF(intype); - if (newtype == NULL) { - goto fail; - } - intype = newtype; - intypekind = PyArray_ScalarKind(intype->type_num, NULL); - } - else { - newtype = PyArray_DescrFromObject(otmp, stype); - Py_XDECREF(stype); - if (newtype == NULL) { - goto fail; - } - stype = newtype; - scalarkind = PyArray_ScalarKind(newtype->type_num, NULL); - mps[i] = (PyArrayObject *)Py_None; - Py_INCREF(Py_None); - } - Py_XDECREF(otmp); - } - if (intype == NULL) { - /* all scalars */ - allscalars = 1; - intype = stype; - Py_INCREF(intype); - for (i = 0; i < n; i++) { - Py_XDECREF(mps[i]); - mps[i] = NULL; - } - } - else if ((stype != NULL) && (intypekind != scalarkind)) { - /* - * we need to upconvert to type that - * handles both intype and stype - * also don't forcecast the scalars. - */ - if (!PyArray_CanCoerceScalar(stype->type_num, - intype->type_num, - scalarkind)) { - newtype = PyArray_PromoteTypes(intype, stype); - Py_XDECREF(intype); - intype = newtype; + /* Convert everything to an array, this could be optimized away */ + PyObject *tmp = PySequence_GetItem(op, i); + if (tmp == NULL) { + goto fail; } - for (i = 0; i < n; i++) { - Py_XDECREF(mps[i]); - mps[i] = NULL; + + mps[i] = (PyArrayObject *)PyArray_FROM_O(tmp); + Py_DECREF(tmp); + if (mps[i] == NULL) { + goto fail; } } + common_descr = PyArray_ResultType(n, mps, 0, NULL); + if (common_descr == NULL) { + goto fail; + } - /* Make sure all arrays are actual array objects. */ + /* Make sure all arrays are contiguous and have the correct dtype. */ for (i = 0; i < n; i++) { int flags = NPY_ARRAY_CARRAY; + PyArrayObject *tmp = mps[i]; - if ((otmp = PySequence_GetItem(op, i)) == NULL) { - goto fail; - } - if (!allscalars && ((PyObject *)(mps[i]) == Py_None)) { - /* forcecast scalars */ - flags |= NPY_ARRAY_FORCECAST; - Py_DECREF(Py_None); - } - Py_INCREF(intype); - mps[i] = (PyArrayObject*) - PyArray_FromAny(otmp, intype, 0, 0, flags, NULL); - Py_DECREF(otmp); + Py_INCREF(common_descr); + mps[i] = (PyArrayObject *)PyArray_FromArray(tmp, common_descr, flags); + Py_DECREF(tmp); if (mps[i] == NULL) { goto fail; } } - Py_DECREF(intype); - Py_XDECREF(stype); + Py_DECREF(common_descr); return mps; fail: - Py_XDECREF(intype); - Py_XDECREF(stype); + Py_XDECREF(common_descr); *retn = 0; for (i = 0; i < n; i++) { Py_XDECREF(mps[i]); @@ -2152,3 +2204,1436 @@ PyArray_ConvertToCommonType(PyObject *op, int *retn) PyDataMem_FREE(mps); return NULL; } + + +/** + * Private function to add a casting implementation by unwrapping a bound + * array method. + * + * @param meth + * @return 0 on success -1 on failure. + */ +NPY_NO_EXPORT int +PyArray_AddCastingImplementation(PyBoundArrayMethodObject *meth) +{ + if (meth->method->nin != 1 || meth->method->nout != 1) { + PyErr_SetString(PyExc_TypeError, + "A cast must have one input and one output."); + return -1; + } + if (meth->dtypes[0] == meth->dtypes[1]) { + /* + * The method casting between instances of the same dtype is special, + * since it is common, it is stored explicitly (currently) and must + * obey additional constraints to ensure convenient casting. + */ + if (!(meth->method->flags & NPY_METH_SUPPORTS_UNALIGNED)) { + PyErr_Format(PyExc_TypeError, + "A cast where input and output DType (class) are identical " + "must currently support unaligned data. (method: %s)", + meth->method->name); + return -1; + } + if (NPY_DT_SLOTS(meth->dtypes[0])->within_dtype_castingimpl != NULL) { + PyErr_Format(PyExc_RuntimeError, + "A cast was already added for %S -> %S. (method: %s)", + meth->dtypes[0], meth->dtypes[1], meth->method->name); + return -1; + } + Py_INCREF(meth->method); + NPY_DT_SLOTS(meth->dtypes[0])->within_dtype_castingimpl = ( + (PyObject *)meth->method); + + return 0; + } + if (PyDict_Contains(NPY_DT_SLOTS(meth->dtypes[0])->castingimpls, + (PyObject *)meth->dtypes[1])) { + PyErr_Format(PyExc_RuntimeError, + "A cast was already added for %S -> %S. (method: %s)", + meth->dtypes[0], meth->dtypes[1], meth->method->name); + return -1; + } + if (PyDict_SetItem(NPY_DT_SLOTS(meth->dtypes[0])->castingimpls, + (PyObject *)meth->dtypes[1], (PyObject *)meth->method) < 0) { + return -1; + } + return 0; +} + +/** + * Add a new casting implementation using a PyArrayMethod_Spec. + * + * @param spec + * @param private If private, allow slots not publicly exposed. + * @return 0 on success -1 on failure + */ +NPY_NO_EXPORT int +PyArray_AddCastingImplementation_FromSpec(PyArrayMethod_Spec *spec, int private) +{ + /* Create a bound method, unbind and store it */ + PyBoundArrayMethodObject *meth = PyArrayMethod_FromSpec_int(spec, private); + if (meth == NULL) { + return -1; + } + int res = PyArray_AddCastingImplementation(meth); + Py_DECREF(meth); + if (res < 0) { + return -1; + } + return 0; +} + + +NPY_NO_EXPORT NPY_CASTING +legacy_same_dtype_resolve_descriptors( + PyArrayMethodObject *NPY_UNUSED(self), + PyArray_DTypeMeta *NPY_UNUSED(dtypes[2]), + PyArray_Descr *given_descrs[2], + PyArray_Descr *loop_descrs[2]) +{ + Py_INCREF(given_descrs[0]); + loop_descrs[0] = given_descrs[0]; + + if (given_descrs[1] == NULL) { + loop_descrs[1] = ensure_dtype_nbo(loop_descrs[0]); + if (loop_descrs[1] == NULL) { + Py_DECREF(loop_descrs[0]); + return -1; + } + } + else { + Py_INCREF(given_descrs[1]); + loop_descrs[1] = given_descrs[1]; + } + + /* this function only makes sense for non-flexible legacy dtypes: */ + assert(loop_descrs[0]->elsize == loop_descrs[1]->elsize); + + /* + * Legacy dtypes (except datetime) only have byte-order and elsize as + * storage parameters. + */ + if (PyDataType_ISNOTSWAPPED(loop_descrs[0]) == + PyDataType_ISNOTSWAPPED(loop_descrs[1])) { + return NPY_NO_CASTING | _NPY_CAST_IS_VIEW; + } + return NPY_EQUIV_CASTING; +} + + +NPY_NO_EXPORT int +legacy_cast_get_strided_loop( + PyArrayMethod_Context *context, + int aligned, int move_references, npy_intp *strides, + PyArrayMethod_StridedLoop **out_loop, NpyAuxData **out_transferdata, + NPY_ARRAYMETHOD_FLAGS *flags) +{ + PyArray_Descr **descrs = context->descriptors; + int out_needs_api = 0; + + *flags = context->method->flags & NPY_METH_RUNTIME_FLAGS; + + if (get_wrapped_legacy_cast_function( + aligned, strides[0], strides[1], descrs[0], descrs[1], + move_references, out_loop, out_transferdata, &out_needs_api, 0) < 0) { + return -1; + } + if (!out_needs_api) { + *flags &= ~NPY_METH_REQUIRES_PYAPI; + } + return 0; +} + + +/* + * Simple dtype resolver for casting between two different (non-parametric) + * (legacy) dtypes. + */ +NPY_NO_EXPORT NPY_CASTING +simple_cast_resolve_descriptors( + PyArrayMethodObject *self, + PyArray_DTypeMeta *dtypes[2], + PyArray_Descr *given_descrs[2], + PyArray_Descr *loop_descrs[2]) +{ + assert(NPY_DT_is_legacy(dtypes[0]) && NPY_DT_is_legacy(dtypes[1])); + + loop_descrs[0] = ensure_dtype_nbo(given_descrs[0]); + if (loop_descrs[0] == NULL) { + return -1; + } + if (given_descrs[1] != NULL) { + loop_descrs[1] = ensure_dtype_nbo(given_descrs[1]); + if (loop_descrs[1] == NULL) { + Py_DECREF(loop_descrs[0]); + return -1; + } + } + else { + loop_descrs[1] = NPY_DT_CALL_default_descr(dtypes[1]); + } + + if (self->casting != NPY_NO_CASTING) { + return self->casting; + } + if (PyDataType_ISNOTSWAPPED(loop_descrs[0]) == + PyDataType_ISNOTSWAPPED(loop_descrs[1])) { + return NPY_NO_CASTING | _NPY_CAST_IS_VIEW; + } + return NPY_EQUIV_CASTING; +} + + +NPY_NO_EXPORT int +get_byteswap_loop( + PyArrayMethod_Context *context, + int aligned, int NPY_UNUSED(move_references), npy_intp *strides, + PyArrayMethod_StridedLoop **out_loop, NpyAuxData **out_transferdata, + NPY_ARRAYMETHOD_FLAGS *flags) +{ + PyArray_Descr **descrs = context->descriptors; + assert(descrs[0]->kind == descrs[1]->kind); + assert(descrs[0]->elsize == descrs[1]->elsize); + int itemsize = descrs[0]->elsize; + *flags = NPY_METH_NO_FLOATINGPOINT_ERRORS; + *out_transferdata = NULL; + if (descrs[0]->kind == 'c') { + /* + * TODO: we have an issue with complex, since the below loops + * use the itemsize, the complex alignment would be too small. + * Using aligned = 0, might cause slow downs in some cases. + */ + aligned = 0; + } + + if (PyDataType_ISNOTSWAPPED(descrs[0]) == + PyDataType_ISNOTSWAPPED(descrs[1])) { + *out_loop = PyArray_GetStridedCopyFn( + aligned, strides[0], strides[1], itemsize); + } + else if (!PyTypeNum_ISCOMPLEX(descrs[0]->type_num)) { + *out_loop = PyArray_GetStridedCopySwapFn( + aligned, strides[0], strides[1], itemsize); + } + else { + *out_loop = PyArray_GetStridedCopySwapPairFn( + aligned, strides[0], strides[1], itemsize); + } + if (*out_loop == NULL) { + return -1; + } + return 0; +} + + +NPY_NO_EXPORT int +complex_to_noncomplex_get_loop( + PyArrayMethod_Context *context, + int aligned, int move_references, npy_intp *strides, + PyArrayMethod_StridedLoop **out_loop, NpyAuxData **out_transferdata, + NPY_ARRAYMETHOD_FLAGS *flags) +{ + static PyObject *cls = NULL; + int ret; + npy_cache_import("numpy.core", "ComplexWarning", &cls); + if (cls == NULL) { + return -1; + } + ret = PyErr_WarnEx(cls, + "Casting complex values to real discards " + "the imaginary part", 1); + if (ret < 0) { + return -1; + } + return npy_default_get_strided_loop( + context, aligned, move_references, strides, + out_loop, out_transferdata, flags); +} + + +static int +add_numeric_cast(PyArray_DTypeMeta *from, PyArray_DTypeMeta *to) +{ + PyType_Slot slots[7]; + PyArray_DTypeMeta *dtypes[2] = {from, to}; + PyArrayMethod_Spec spec = { + .name = "numeric_cast", + .nin = 1, + .nout = 1, + .flags = NPY_METH_SUPPORTS_UNALIGNED, + .slots = slots, + .dtypes = dtypes, + }; + + npy_intp from_itemsize = from->singleton->elsize; + npy_intp to_itemsize = to->singleton->elsize; + + slots[0].slot = NPY_METH_resolve_descriptors; + slots[0].pfunc = &simple_cast_resolve_descriptors; + /* Fetch the optimized loops (2<<10 is a non-contiguous stride) */ + slots[1].slot = NPY_METH_strided_loop; + slots[1].pfunc = PyArray_GetStridedNumericCastFn( + 1, 2<<10, 2<<10, from->type_num, to->type_num); + slots[2].slot = NPY_METH_contiguous_loop; + slots[2].pfunc = PyArray_GetStridedNumericCastFn( + 1, from_itemsize, to_itemsize, from->type_num, to->type_num); + slots[3].slot = NPY_METH_unaligned_strided_loop; + slots[3].pfunc = PyArray_GetStridedNumericCastFn( + 0, 2<<10, 2<<10, from->type_num, to->type_num); + slots[4].slot = NPY_METH_unaligned_contiguous_loop; + slots[4].pfunc = PyArray_GetStridedNumericCastFn( + 0, from_itemsize, to_itemsize, from->type_num, to->type_num); + if (PyTypeNum_ISCOMPLEX(from->type_num) && + !PyTypeNum_ISCOMPLEX(to->type_num) && + !PyTypeNum_ISBOOL(to->type_num)) { + /* + * The get_loop function must also give a ComplexWarning. We could + * consider moving this warning into the inner-loop at some point + * for simplicity (this requires ensuring it is only emitted once). + */ + slots[5].slot = NPY_METH_get_loop; + slots[5].pfunc = &complex_to_noncomplex_get_loop; + slots[6].slot = 0; + slots[6].pfunc = NULL; + } + else { + /* Use the default get loop function. */ + slots[5].slot = 0; + slots[5].pfunc = NULL; + } + + assert(slots[1].pfunc && slots[2].pfunc && slots[3].pfunc && slots[4].pfunc); + + /* Find the correct casting level, and special case no-cast */ + if (dtypes[0]->singleton->kind == dtypes[1]->singleton->kind + && from_itemsize == to_itemsize) { + spec.casting = NPY_EQUIV_CASTING; + + /* When there is no casting (equivalent C-types) use byteswap loops */ + slots[0].slot = NPY_METH_resolve_descriptors; + slots[0].pfunc = &legacy_same_dtype_resolve_descriptors; + slots[1].slot = NPY_METH_get_loop; + slots[1].pfunc = &get_byteswap_loop; + slots[2].slot = 0; + slots[2].pfunc = NULL; + + spec.name = "numeric_copy_or_byteswap"; + spec.flags |= NPY_METH_NO_FLOATINGPOINT_ERRORS; + } + else if (_npy_can_cast_safely_table[from->type_num][to->type_num]) { + spec.casting = NPY_SAFE_CASTING; + } + else if (dtype_kind_to_ordering(dtypes[0]->singleton->kind) <= + dtype_kind_to_ordering(dtypes[1]->singleton->kind)) { + spec.casting = NPY_SAME_KIND_CASTING; + } + else { + spec.casting = NPY_UNSAFE_CASTING; + } + + /* Create a bound method, unbind and store it */ + return PyArray_AddCastingImplementation_FromSpec(&spec, 1); +} + + +/* + * This registers the castingimpl for all casts between numeric types. + * Eventually, this function should likely be defined as part of a .c.src + * file to remove `PyArray_GetStridedNumericCastFn` entirely. + */ +static int +PyArray_InitializeNumericCasts(void) +{ + for (int from = 0; from < NPY_NTYPES; from++) { + if (!PyTypeNum_ISNUMBER(from) && from != NPY_BOOL) { + continue; + } + PyArray_DTypeMeta *from_dt = PyArray_DTypeFromTypeNum(from); + + for (int to = 0; to < NPY_NTYPES; to++) { + if (!PyTypeNum_ISNUMBER(to) && to != NPY_BOOL) { + continue; + } + PyArray_DTypeMeta *to_dt = PyArray_DTypeFromTypeNum(to); + int res = add_numeric_cast(from_dt, to_dt); + Py_DECREF(to_dt); + if (res < 0) { + Py_DECREF(from_dt); + return -1; + } + } + } + return 0; +} + + +static int +cast_to_string_resolve_descriptors( + PyArrayMethodObject *self, + PyArray_DTypeMeta *dtypes[2], + PyArray_Descr *given_descrs[2], + PyArray_Descr *loop_descrs[2]) +{ + /* + * NOTE: The following code used to be part of PyArray_AdaptFlexibleDType + * + * Get a string-size estimate of the input. These + * are generallly the size needed, rounded up to + * a multiple of eight. + */ + npy_intp size = -1; + switch (given_descrs[0]->type_num) { + case NPY_BOOL: + case NPY_UBYTE: + case NPY_BYTE: + case NPY_USHORT: + case NPY_SHORT: + case NPY_UINT: + case NPY_INT: + case NPY_ULONG: + case NPY_LONG: + case NPY_ULONGLONG: + case NPY_LONGLONG: + assert(given_descrs[0]->elsize <= 8); + assert(given_descrs[0]->elsize > 0); + if (given_descrs[0]->kind == 'b') { + /* 5 chars needed for cast to 'True' or 'False' */ + size = 5; + } + else if (given_descrs[0]->kind == 'u') { + size = REQUIRED_STR_LEN[given_descrs[0]->elsize]; + } + else if (given_descrs[0]->kind == 'i') { + /* Add character for sign symbol */ + size = REQUIRED_STR_LEN[given_descrs[0]->elsize] + 1; + } + break; + case NPY_HALF: + case NPY_FLOAT: + case NPY_DOUBLE: + size = 32; + break; + case NPY_LONGDOUBLE: + size = 48; + break; + case NPY_CFLOAT: + case NPY_CDOUBLE: + size = 2 * 32; + break; + case NPY_CLONGDOUBLE: + size = 2 * 48; + break; + case NPY_STRING: + case NPY_VOID: + size = given_descrs[0]->elsize; + break; + case NPY_UNICODE: + size = given_descrs[0]->elsize / 4; + break; + default: + PyErr_SetString(PyExc_SystemError, + "Impossible cast to string path requested."); + return -1; + } + if (dtypes[1]->type_num == NPY_UNICODE) { + size *= 4; + } + + if (given_descrs[1] == NULL) { + loop_descrs[1] = PyArray_DescrNewFromType(dtypes[1]->type_num); + if (loop_descrs[1] == NULL) { + return -1; + } + loop_descrs[1]->elsize = size; + } + else { + /* The legacy loop can handle mismatching itemsizes */ + loop_descrs[1] = ensure_dtype_nbo(given_descrs[1]); + if (loop_descrs[1] == NULL) { + return -1; + } + } + + /* Set the input one as well (late for easier error management) */ + loop_descrs[0] = ensure_dtype_nbo(given_descrs[0]); + if (loop_descrs[0] == NULL) { + return -1; + } + + if (self->casting == NPY_UNSAFE_CASTING) { + assert(dtypes[0]->type_num == NPY_UNICODE && + dtypes[1]->type_num == NPY_STRING); + return NPY_UNSAFE_CASTING; + } + + if (loop_descrs[1]->elsize >= size) { + return NPY_SAFE_CASTING; + } + return NPY_SAME_KIND_CASTING; +} + + +static int +add_other_to_and_from_string_cast( + PyArray_DTypeMeta *string, PyArray_DTypeMeta *other) +{ + if (string == other) { + return 0; + } + + /* Casting from string, is always a simple legacy-style cast */ + if (other->type_num != NPY_STRING && other->type_num != NPY_UNICODE) { + if (PyArray_AddLegacyWrapping_CastingImpl( + string, other, NPY_UNSAFE_CASTING) < 0) { + return -1; + } + } + /* + * Casting to strings, is almost the same, but requires a custom resolver + * to define the correct string length. Right now we use a generic function + * for this. + */ + PyArray_DTypeMeta *dtypes[2] = {other, string}; + PyType_Slot slots[] = { + {NPY_METH_get_loop, &legacy_cast_get_strided_loop}, + {NPY_METH_resolve_descriptors, &cast_to_string_resolve_descriptors}, + {0, NULL}}; + PyArrayMethod_Spec spec = { + .name = "legacy_cast_to_string", + .nin = 1, + .nout = 1, + .flags = NPY_METH_REQUIRES_PYAPI, + .dtypes = dtypes, + .slots = slots, + }; + /* Almost everything can be same-kind cast to string (except unicode) */ + if (other->type_num != NPY_UNICODE) { + spec.casting = NPY_SAME_KIND_CASTING; /* same-kind if too short */ + } + else { + spec.casting = NPY_UNSAFE_CASTING; + } + + return PyArray_AddCastingImplementation_FromSpec(&spec, 1); +} + + +NPY_NO_EXPORT NPY_CASTING +string_to_string_resolve_descriptors( + PyArrayMethodObject *NPY_UNUSED(self), + PyArray_DTypeMeta *NPY_UNUSED(dtypes[2]), + PyArray_Descr *given_descrs[2], + PyArray_Descr *loop_descrs[2]) +{ + Py_INCREF(given_descrs[0]); + loop_descrs[0] = given_descrs[0]; + + if (given_descrs[1] == NULL) { + loop_descrs[1] = ensure_dtype_nbo(loop_descrs[0]); + if (loop_descrs[1] == NULL) { + return -1; + } + } + else { + Py_INCREF(given_descrs[1]); + loop_descrs[1] = given_descrs[1]; + } + + if (loop_descrs[0]->elsize == loop_descrs[1]->elsize) { + if (PyDataType_ISNOTSWAPPED(loop_descrs[0]) == + PyDataType_ISNOTSWAPPED(loop_descrs[1])) { + return NPY_NO_CASTING | _NPY_CAST_IS_VIEW; + } + else { + return NPY_EQUIV_CASTING; + } + } + else if (loop_descrs[0]->elsize <= loop_descrs[1]->elsize) { + return NPY_SAFE_CASTING; + } + return NPY_SAME_KIND_CASTING; +} + + +NPY_NO_EXPORT int +string_to_string_get_loop( + PyArrayMethod_Context *context, + int aligned, int NPY_UNUSED(move_references), npy_intp *strides, + PyArrayMethod_StridedLoop **out_loop, NpyAuxData **out_transferdata, + NPY_ARRAYMETHOD_FLAGS *flags) +{ + int unicode_swap = 0; + PyArray_Descr **descrs = context->descriptors; + + assert(NPY_DTYPE(descrs[0]) == NPY_DTYPE(descrs[1])); + *flags = context->method->flags & NPY_METH_RUNTIME_FLAGS; + if (descrs[0]->type_num == NPY_UNICODE) { + if (PyDataType_ISNOTSWAPPED(descrs[0]) != + PyDataType_ISNOTSWAPPED(descrs[1])) { + unicode_swap = 1; + } + } + + if (PyArray_GetStridedZeroPadCopyFn( + aligned, unicode_swap, strides[0], strides[1], + descrs[0]->elsize, descrs[1]->elsize, + out_loop, out_transferdata) == NPY_FAIL) { + return -1; + } + return 0; +} + + +/* + * Add string casts. Right now all string casts are just legacy-wrapped ones + * (except string<->string and unicode<->unicode), but they do require + * custom type resolution for the string length. + * + * A bit like `object`, it could make sense to define a simpler protocol for + * string casts, however, we also need to remember that the itemsize of the + * output has to be found. + */ +static int +PyArray_InitializeStringCasts(void) +{ + int result = -1; + PyArray_DTypeMeta *string = PyArray_DTypeFromTypeNum(NPY_STRING); + PyArray_DTypeMeta *unicode = PyArray_DTypeFromTypeNum(NPY_UNICODE); + PyArray_DTypeMeta *other_dt = NULL; + + /* Add most casts as legacy ones */ + for (int other = 0; other < NPY_NTYPES; other++) { + if (PyTypeNum_ISDATETIME(other) || other == NPY_VOID || + other == NPY_OBJECT) { + continue; + } + other_dt = PyArray_DTypeFromTypeNum(other); + + /* The functions skip string == other_dt or unicode == other_dt */ + if (add_other_to_and_from_string_cast(string, other_dt) < 0) { + goto finish; + } + if (add_other_to_and_from_string_cast(unicode, other_dt) < 0) { + goto finish; + } + + Py_SETREF(other_dt, NULL); + } + + /* string<->string and unicode<->unicode have their own specialized casts */ + PyArray_DTypeMeta *dtypes[2]; + PyType_Slot slots[] = { + {NPY_METH_get_loop, &string_to_string_get_loop}, + {NPY_METH_resolve_descriptors, &string_to_string_resolve_descriptors}, + {0, NULL}}; + PyArrayMethod_Spec spec = { + .name = "string_to_string_cast", + .casting = NPY_UNSAFE_CASTING, + .nin = 1, + .nout = 1, + .flags = (NPY_METH_REQUIRES_PYAPI | + NPY_METH_NO_FLOATINGPOINT_ERRORS | + NPY_METH_SUPPORTS_UNALIGNED), + .dtypes = dtypes, + .slots = slots, + }; + + dtypes[0] = string; + dtypes[1] = string; + if (PyArray_AddCastingImplementation_FromSpec(&spec, 1) < 0) { + goto finish; + } + + dtypes[0] = unicode; + dtypes[1] = unicode; + if (PyArray_AddCastingImplementation_FromSpec(&spec, 1) < 0) { + goto finish; + } + + result = 0; + finish: + Py_DECREF(string); + Py_DECREF(unicode); + Py_XDECREF(other_dt); + return result; +} + + +/* + * Small helper function to handle the case of `arr.astype(dtype="V")`. + * When the output descriptor is not passed, we always use `V<itemsize>` + * of the other dtype. + */ +static NPY_CASTING +cast_to_void_dtype_class( + PyArray_Descr **given_descrs, PyArray_Descr **loop_descrs) +{ + /* `dtype="V"` means unstructured currently (compare final path) */ + loop_descrs[1] = PyArray_DescrNewFromType(NPY_VOID); + if (loop_descrs[1] == NULL) { + return -1; + } + loop_descrs[1]->elsize = given_descrs[0]->elsize; + Py_INCREF(given_descrs[0]); + loop_descrs[0] = given_descrs[0]; + if (loop_descrs[0]->type_num == NPY_VOID && + loop_descrs[0]->subarray == NULL && loop_descrs[1]->names == NULL) { + return NPY_NO_CASTING | _NPY_CAST_IS_VIEW; + } + return NPY_SAFE_CASTING | _NPY_CAST_IS_VIEW; +} + + +static NPY_CASTING +nonstructured_to_structured_resolve_descriptors( + PyArrayMethodObject *NPY_UNUSED(self), + PyArray_DTypeMeta *NPY_UNUSED(dtypes[2]), + PyArray_Descr *given_descrs[2], + PyArray_Descr *loop_descrs[2]) +{ + NPY_CASTING casting; + + if (given_descrs[1] == NULL) { + return cast_to_void_dtype_class(given_descrs, loop_descrs); + } + + if (given_descrs[1]->subarray != NULL) { + /* + * We currently consider this at most a safe cast. It would be + * possible to allow a view if the field has exactly one element. + */ + casting = NPY_SAFE_CASTING; + /* Subarray dtype */ + NPY_CASTING base_casting = PyArray_GetCastSafety( + given_descrs[0], given_descrs[1]->subarray->base, NULL); + if (base_casting < 0) { + return -1; + } + casting = PyArray_MinCastSafety(casting, base_casting); + } + else if (given_descrs[1]->names != NULL) { + /* Structured dtype */ + if (PyTuple_Size(given_descrs[1]->names) == 0) { + /* TODO: This retained behaviour, but likely should be changed. */ + casting = NPY_UNSAFE_CASTING; + } + else { + /* Considered at most unsafe casting (but this could be changed) */ + casting = NPY_UNSAFE_CASTING; + if (PyTuple_Size(given_descrs[1]->names) == 1) { + /* A view may be acceptable */ + casting |= _NPY_CAST_IS_VIEW; + } + + Py_ssize_t pos = 0; + PyObject *key, *tuple; + while (PyDict_Next(given_descrs[1]->fields, &pos, &key, &tuple)) { + PyArray_Descr *field_descr = (PyArray_Descr *)PyTuple_GET_ITEM(tuple, 0); + NPY_CASTING field_casting = PyArray_GetCastSafety( + given_descrs[0], field_descr, NULL); + casting = PyArray_MinCastSafety(casting, field_casting); + if (casting < 0) { + return -1; + } + } + } + } + else { + /* Plain void type. This behaves much like a "view" */ + if (given_descrs[0]->elsize == given_descrs[1]->elsize && + !PyDataType_REFCHK(given_descrs[0])) { + /* + * A simple view, at the moment considered "safe" (the refcheck is + * probably not necessary, but more future proof + */ + casting = NPY_SAFE_CASTING | _NPY_CAST_IS_VIEW; + } + else if (given_descrs[0]->elsize <= given_descrs[1]->elsize) { + casting = NPY_SAFE_CASTING; + } + else { + casting = NPY_UNSAFE_CASTING; + } + } + + /* Void dtypes always do the full cast. */ + Py_INCREF(given_descrs[0]); + loop_descrs[0] = given_descrs[0]; + Py_INCREF(given_descrs[1]); + loop_descrs[1] = given_descrs[1]; + + return casting; +} + + +int give_bad_field_error(PyObject *key) +{ + if (!PyErr_Occurred()) { + PyErr_Format(PyExc_RuntimeError, + "Invalid or missing field %R, this should be impossible " + "and indicates a NumPy bug.", key); + } + return -1; +} + + +static int +nonstructured_to_structured_get_loop( + PyArrayMethod_Context *context, + int aligned, int move_references, + npy_intp *strides, + PyArrayMethod_StridedLoop **out_loop, + NpyAuxData **out_transferdata, + NPY_ARRAYMETHOD_FLAGS *flags) +{ + if (context->descriptors[1]->names != NULL) { + int needs_api = 0; + if (get_fields_transfer_function( + aligned, strides[0], strides[1], + context->descriptors[0], context->descriptors[1], + move_references, out_loop, out_transferdata, + &needs_api) == NPY_FAIL) { + return -1; + } + *flags = needs_api ? NPY_METH_REQUIRES_PYAPI : 0; + } + else if (context->descriptors[1]->subarray != NULL) { + int needs_api = 0; + if (get_subarray_transfer_function( + aligned, strides[0], strides[1], + context->descriptors[0], context->descriptors[1], + move_references, out_loop, out_transferdata, + &needs_api) == NPY_FAIL) { + return -1; + } + *flags = needs_api ? NPY_METH_REQUIRES_PYAPI : 0; + } + else { + /* + * TODO: This could be a simple zero padded cast, adding a decref + * in case of `move_references`. But for now use legacy casts + * (which is the behaviour at least up to 1.20). + */ + int needs_api = 0; + if (get_wrapped_legacy_cast_function( + 1, strides[0], strides[1], + context->descriptors[0], context->descriptors[1], + move_references, out_loop, out_transferdata, + &needs_api, 1) < 0) { + return -1; + } + *flags = needs_api ? NPY_METH_REQUIRES_PYAPI : 0; + } + return 0; +} + + +static PyObject * +PyArray_GetGenericToVoidCastingImpl(void) +{ + static PyArrayMethodObject *method = NULL; + + if (method != NULL) { + Py_INCREF(method); + return (PyObject *)method; + } + + method = PyObject_New(PyArrayMethodObject, &PyArrayMethod_Type); + if (method == NULL) { + return PyErr_NoMemory(); + } + + method->name = "any_to_void_cast"; + method->flags = NPY_METH_SUPPORTS_UNALIGNED | NPY_METH_REQUIRES_PYAPI; + method->casting = -1; + method->resolve_descriptors = &nonstructured_to_structured_resolve_descriptors; + method->get_strided_loop = &nonstructured_to_structured_get_loop; + + return (PyObject *)method; +} + + +static NPY_CASTING +structured_to_nonstructured_resolve_descriptors( + PyArrayMethodObject *NPY_UNUSED(self), + PyArray_DTypeMeta *dtypes[2], + PyArray_Descr *given_descrs[2], + PyArray_Descr *loop_descrs[2]) +{ + PyArray_Descr *base_descr; + + if (given_descrs[0]->subarray != NULL) { + base_descr = given_descrs[0]->subarray->base; + } + else if (given_descrs[0]->names != NULL) { + if (PyTuple_Size(given_descrs[0]->names) != 1) { + /* Only allow casting a single field */ + return -1; + } + PyObject *key = PyTuple_GetItem(given_descrs[0]->names, 0); + PyObject *base_tup = PyDict_GetItem(given_descrs[0]->fields, key); + base_descr = (PyArray_Descr *)PyTuple_GET_ITEM(base_tup, 0); + } + else { + /* + * unstructured voids are considered unsafe casts and defined, albeit, + * at this time they go back to legacy behaviour using getitem/setitem. + */ + base_descr = NULL; + } + + /* + * The cast is always considered unsafe, so the PyArray_GetCastSafety + * result currently does not matter. + */ + if (base_descr != NULL && PyArray_GetCastSafety( + base_descr, given_descrs[1], dtypes[1]) < 0) { + return -1; + } + + /* Void dtypes always do the full cast. */ + if (given_descrs[1] == NULL) { + loop_descrs[1] = NPY_DT_CALL_default_descr(dtypes[1]); + /* + * Special case strings here, it should be useless (and only actually + * work for empty arrays). Possibly this should simply raise for + * all parametric DTypes. + */ + if (dtypes[1]->type_num == NPY_STRING) { + loop_descrs[1]->elsize = given_descrs[0]->elsize; + } + else if (dtypes[1]->type_num == NPY_UNICODE) { + loop_descrs[1]->elsize = given_descrs[0]->elsize * 4; + } + } + else { + Py_INCREF(given_descrs[1]); + loop_descrs[1] = given_descrs[1]; + } + Py_INCREF(given_descrs[0]); + loop_descrs[0] = given_descrs[0]; + + return NPY_UNSAFE_CASTING; +} + + +static int +structured_to_nonstructured_get_loop( + PyArrayMethod_Context *context, + int aligned, int move_references, + npy_intp *strides, + PyArrayMethod_StridedLoop **out_loop, + NpyAuxData **out_transferdata, + NPY_ARRAYMETHOD_FLAGS *flags) +{ + if (context->descriptors[0]->names != NULL) { + int needs_api = 0; + if (get_fields_transfer_function( + aligned, strides[0], strides[1], + context->descriptors[0], context->descriptors[1], + move_references, out_loop, out_transferdata, + &needs_api) == NPY_FAIL) { + return -1; + } + *flags = needs_api ? NPY_METH_REQUIRES_PYAPI : 0; + } + else if (context->descriptors[0]->subarray != NULL) { + int needs_api = 0; + if (get_subarray_transfer_function( + aligned, strides[0], strides[1], + context->descriptors[0], context->descriptors[1], + move_references, out_loop, out_transferdata, + &needs_api) == NPY_FAIL) { + return -1; + } + *flags = needs_api ? NPY_METH_REQUIRES_PYAPI : 0; + } + else { + /* + * In general this is currently defined through legacy behaviour via + * scalars, and should likely just not be allowed. + */ + int needs_api = 0; + if (get_wrapped_legacy_cast_function( + aligned, strides[0], strides[1], + context->descriptors[0], context->descriptors[1], + move_references, out_loop, out_transferdata, + &needs_api, 1) < 0) { + return -1; + } + *flags = needs_api ? NPY_METH_REQUIRES_PYAPI : 0; + } + return 0; +} + + +static PyObject * +PyArray_GetVoidToGenericCastingImpl(void) +{ + static PyArrayMethodObject *method = NULL; + + if (method != NULL) { + Py_INCREF(method); + return (PyObject *)method; + } + + method = PyObject_New(PyArrayMethodObject, &PyArrayMethod_Type); + if (method == NULL) { + return PyErr_NoMemory(); + } + + method->name = "void_to_any_cast"; + method->flags = NPY_METH_SUPPORTS_UNALIGNED | NPY_METH_REQUIRES_PYAPI; + method->casting = -1; + method->resolve_descriptors = &structured_to_nonstructured_resolve_descriptors; + method->get_strided_loop = &structured_to_nonstructured_get_loop; + + return (PyObject *)method; +} + + +/* + * Find the correct field casting safety. See the TODO note below, including + * in 1.20 (and later) this was based on field names rather than field order + * which it should be using. + * + * NOTE: In theory it would be possible to cache the all the field casting + * implementations on the dtype, to avoid duplicate work. + */ +static NPY_CASTING +can_cast_fields_safety(PyArray_Descr *from, PyArray_Descr *to) +{ + NPY_CASTING casting = NPY_NO_CASTING | _NPY_CAST_IS_VIEW; + + Py_ssize_t field_count = PyTuple_Size(from->names); + if (field_count != PyTuple_Size(to->names)) { + /* TODO: This should be rejected! */ + return NPY_UNSAFE_CASTING; + } + for (Py_ssize_t i = 0; i < field_count; i++) { + PyObject *from_key = PyTuple_GET_ITEM(from->names, i); + PyObject *from_tup = PyDict_GetItemWithError(from->fields, from_key); + if (from_tup == NULL) { + return give_bad_field_error(from_key); + } + PyArray_Descr *from_base = (PyArray_Descr*)PyTuple_GET_ITEM(from_tup, 0); + + /* + * TODO: This should use to_key (order), compare gh-15509 by + * by Allan Haldane. And raise an error on failure. + * (Fixing that may also requires fixing/changing promotion.) + */ + PyObject *to_tup = PyDict_GetItem(to->fields, from_key); + if (to_tup == NULL) { + return NPY_UNSAFE_CASTING; + } + PyArray_Descr *to_base = (PyArray_Descr*)PyTuple_GET_ITEM(to_tup, 0); + + NPY_CASTING field_casting = PyArray_GetCastSafety(from_base, to_base, NULL); + if (field_casting < 0) { + return -1; + } + casting = PyArray_MinCastSafety(casting, field_casting); + } + if (!(casting & _NPY_CAST_IS_VIEW)) { + assert((casting & ~_NPY_CAST_IS_VIEW) != NPY_NO_CASTING); + return casting; + } + + /* + * If the itemsize (includes padding at the end), fields, or names + * do not match, this cannot be a view and also not a "no" cast + * (identical dtypes). + * It may be possible that this can be relaxed in some cases. + */ + if (from->elsize != to->elsize) { + /* + * The itemsize may mismatch even if all fields and formats match + * (due to additional padding). + */ + return PyArray_MinCastSafety(casting, NPY_EQUIV_CASTING); + } + + int cmp = PyObject_RichCompareBool(from->fields, to->fields, Py_EQ); + if (cmp != 1) { + if (cmp == -1) { + PyErr_Clear(); + } + return PyArray_MinCastSafety(casting, NPY_EQUIV_CASTING); + } + cmp = PyObject_RichCompareBool(from->names, to->names, Py_EQ); + if (cmp != 1) { + if (cmp == -1) { + PyErr_Clear(); + } + return PyArray_MinCastSafety(casting, NPY_EQUIV_CASTING); + } + return casting; +} + + +static NPY_CASTING +void_to_void_resolve_descriptors( + PyArrayMethodObject *self, + PyArray_DTypeMeta *dtypes[2], + PyArray_Descr *given_descrs[2], + PyArray_Descr *loop_descrs[2]) +{ + NPY_CASTING casting; + + if (given_descrs[1] == NULL) { + /* This is weird, since it doesn't return the original descr, but... */ + return cast_to_void_dtype_class(given_descrs, loop_descrs); + } + + if (given_descrs[0]->names != NULL && given_descrs[1]->names != NULL) { + /* From structured to structured, need to check fields */ + casting = can_cast_fields_safety(given_descrs[0], given_descrs[1]); + } + else if (given_descrs[0]->names != NULL) { + return structured_to_nonstructured_resolve_descriptors( + self, dtypes, given_descrs, loop_descrs); + } + else if (given_descrs[1]->names != NULL) { + return nonstructured_to_structured_resolve_descriptors( + self, dtypes, given_descrs, loop_descrs); + } + else if (given_descrs[0]->subarray == NULL && + given_descrs[1]->subarray == NULL) { + /* Both are plain void dtypes */ + if (given_descrs[0]->elsize == given_descrs[1]->elsize) { + casting = NPY_NO_CASTING | _NPY_CAST_IS_VIEW; + } + else if (given_descrs[0]->elsize < given_descrs[1]->elsize) { + casting = NPY_SAFE_CASTING; + } + else { + casting = NPY_SAME_KIND_CASTING; + } + } + else { + /* + * At this point, one of the dtypes must be a subarray dtype, the + * other is definitely not a structured one. + */ + PyArray_ArrayDescr *from_sub = given_descrs[0]->subarray; + PyArray_ArrayDescr *to_sub = given_descrs[1]->subarray; + assert(from_sub || to_sub); + + /* If the shapes do not match, this is at most an unsafe cast */ + casting = NPY_UNSAFE_CASTING; + if (from_sub && to_sub) { + int res = PyObject_RichCompareBool(from_sub->shape, to_sub->shape, Py_EQ); + if (res < 0) { + return -1; + } + else if (res) { + /* Both are subarrays and the shape matches */ + casting = NPY_NO_CASTING | _NPY_CAST_IS_VIEW; + } + } + + PyArray_Descr *from_base = (from_sub == NULL) ? given_descrs[0] : from_sub->base; + PyArray_Descr *to_base = (to_sub == NULL) ? given_descrs[1] : to_sub->base; + NPY_CASTING field_casting = PyArray_GetCastSafety(from_base, to_base, NULL); + if (field_casting < 0) { + return -1; + } + casting = PyArray_MinCastSafety(casting, field_casting); + } + + /* Void dtypes always do the full cast. */ + Py_INCREF(given_descrs[0]); + loop_descrs[0] = given_descrs[0]; + Py_INCREF(given_descrs[1]); + loop_descrs[1] = given_descrs[1]; + + return casting; +} + + +NPY_NO_EXPORT int +void_to_void_get_loop( + PyArrayMethod_Context *context, + int aligned, int move_references, + npy_intp *strides, + PyArrayMethod_StridedLoop **out_loop, + NpyAuxData **out_transferdata, + NPY_ARRAYMETHOD_FLAGS *flags) +{ + if (context->descriptors[0]->names != NULL || + context->descriptors[1]->names != NULL) { + int needs_api = 0; + if (get_fields_transfer_function( + aligned, strides[0], strides[1], + context->descriptors[0], context->descriptors[1], + move_references, out_loop, out_transferdata, + &needs_api) == NPY_FAIL) { + return -1; + } + *flags = needs_api ? NPY_METH_REQUIRES_PYAPI : 0; + } + else if (context->descriptors[0]->subarray != NULL || + context->descriptors[1]->subarray != NULL) { + int needs_api = 0; + if (get_subarray_transfer_function( + aligned, strides[0], strides[1], + context->descriptors[0], context->descriptors[1], + move_references, out_loop, out_transferdata, + &needs_api) == NPY_FAIL) { + return -1; + } + *flags = needs_api ? NPY_METH_REQUIRES_PYAPI : 0; + } + else { + /* + * This is a string-like copy of the two bytes (zero padding if + * necessary) + */ + if (PyArray_GetStridedZeroPadCopyFn( + 0, 0, strides[0], strides[1], + context->descriptors[0]->elsize, context->descriptors[1]->elsize, + out_loop, out_transferdata) == NPY_FAIL) { + return -1; + } + *flags = 0; + } + return 0; +} + + +/* + * This initializes the void to void cast. Voids include structured dtypes, + * which means that they can cast from and to any other dtype and, in that + * sense, are special (similar to Object). + */ +static int +PyArray_InitializeVoidToVoidCast(void) +{ + PyArray_DTypeMeta *Void = PyArray_DTypeFromTypeNum(NPY_VOID); + PyArray_DTypeMeta *dtypes[2] = {Void, Void}; + PyType_Slot slots[] = { + {NPY_METH_get_loop, &void_to_void_get_loop}, + {NPY_METH_resolve_descriptors, &void_to_void_resolve_descriptors}, + {0, NULL}}; + PyArrayMethod_Spec spec = { + .name = "void_to_void_cast", + .casting = -1, /* may not cast at all */ + .nin = 1, + .nout = 1, + .flags = NPY_METH_REQUIRES_PYAPI | NPY_METH_SUPPORTS_UNALIGNED, + .dtypes = dtypes, + .slots = slots, + }; + + int res = PyArray_AddCastingImplementation_FromSpec(&spec, 1); + Py_DECREF(Void); + return res; +} + + +/* + * Implement object to any casting implementation. Casting from object may + * require inspecting of all array elements (for parametric dtypes), and + * the resolver will thus reject all parametric dtypes if the out dtype + * is not provided. + */ +static NPY_CASTING +object_to_any_resolve_descriptors( + PyArrayMethodObject *NPY_UNUSED(self), + PyArray_DTypeMeta *dtypes[2], + PyArray_Descr *given_descrs[2], + PyArray_Descr *loop_descrs[2]) +{ + if (given_descrs[1] == NULL) { + /* + * This should not really be called, since object -> parametric casts + * require inspecting the object array. Allow legacy ones, the path + * here is that e.g. "M8" input is considered to be the DType class, + * and by allowing it here, we go back to the "M8" instance. + */ + if (NPY_DT_is_parametric(dtypes[1])) { + PyErr_Format(PyExc_TypeError, + "casting from object to the parametric DType %S requires " + "the specified output dtype instance. " + "This may be a NumPy issue, since the correct instance " + "should be discovered automatically, however.", dtypes[1]); + return -1; + } + loop_descrs[1] = NPY_DT_CALL_default_descr(dtypes[1]); + if (loop_descrs[1] == NULL) { + return -1; + } + } + else { + Py_INCREF(given_descrs[1]); + loop_descrs[1] = given_descrs[1]; + } + + Py_INCREF(given_descrs[0]); + loop_descrs[0] = given_descrs[0]; + return NPY_UNSAFE_CASTING; +} + + +/* + * Casting to object is special since it is generic to all input dtypes. + */ +static PyObject * +PyArray_GetObjectToGenericCastingImpl(void) +{ + static PyArrayMethodObject *method = NULL; + + if (method != NULL) { + Py_INCREF(method); + return (PyObject *)method; + } + + method = PyObject_New(PyArrayMethodObject, &PyArrayMethod_Type); + if (method == NULL) { + return PyErr_NoMemory(); + } + + method->nin = 1; + method->nout = 1; + method->name = "object_to_any_cast"; + method->flags = NPY_METH_SUPPORTS_UNALIGNED | NPY_METH_REQUIRES_PYAPI; + method->casting = NPY_UNSAFE_CASTING; + method->resolve_descriptors = &object_to_any_resolve_descriptors; + method->get_strided_loop = &object_to_any_get_loop; + + return (PyObject *)method; +} + + + +/* Any object object is simple (could even use the default) */ +static NPY_CASTING +any_to_object_resolve_descriptors( + PyArrayMethodObject *NPY_UNUSED(self), + PyArray_DTypeMeta *dtypes[2], + PyArray_Descr *given_descrs[2], + PyArray_Descr *loop_descrs[2]) +{ + if (given_descrs[1] == NULL) { + loop_descrs[1] = NPY_DT_CALL_default_descr(dtypes[1]); + if (loop_descrs[1] == NULL) { + return -1; + } + } + else { + Py_INCREF(given_descrs[1]); + loop_descrs[1] = given_descrs[1]; + } + + Py_INCREF(given_descrs[0]); + loop_descrs[0] = given_descrs[0]; + return NPY_SAFE_CASTING; +} + + +/* + * Casting to object is special since it is generic to all input dtypes. + */ +static PyObject * +PyArray_GetGenericToObjectCastingImpl(void) +{ + static PyArrayMethodObject *method = NULL; + + if (method != NULL) { + Py_INCREF(method); + return (PyObject *)method; + } + + method = PyObject_New(PyArrayMethodObject, &PyArrayMethod_Type); + if (method == NULL) { + return PyErr_NoMemory(); + } + + method->nin = 1; + method->nout = 1; + method->name = "any_to_object_cast"; + method->flags = NPY_METH_SUPPORTS_UNALIGNED | NPY_METH_REQUIRES_PYAPI; + method->casting = NPY_SAFE_CASTING; + method->resolve_descriptors = &any_to_object_resolve_descriptors; + method->get_strided_loop = &any_to_object_get_loop; + + return (PyObject *)method; +} + + +/* + * Casts within the object dtype is always just a plain copy/view. + * For that reason, this function might remain unimplemented. + */ +static int +object_to_object_get_loop( + PyArrayMethod_Context *NPY_UNUSED(context), + int NPY_UNUSED(aligned), int move_references, + npy_intp *NPY_UNUSED(strides), + PyArrayMethod_StridedLoop **out_loop, + NpyAuxData **out_transferdata, + NPY_ARRAYMETHOD_FLAGS *flags) +{ + *flags = NPY_METH_REQUIRES_PYAPI | NPY_METH_NO_FLOATINGPOINT_ERRORS; + if (move_references) { + *out_loop = &_strided_to_strided_move_references; + *out_transferdata = NULL; + } + else { + *out_loop = &_strided_to_strided_copy_references; + *out_transferdata = NULL; + } + return 0; +} + + +static int +PyArray_InitializeObjectToObjectCast(void) +{ + /* + * The object dtype does not support byte order changes, so its cast + * is always a direct view. + */ + PyArray_DTypeMeta *Object = PyArray_DTypeFromTypeNum(NPY_OBJECT); + PyArray_DTypeMeta *dtypes[2] = {Object, Object}; + PyType_Slot slots[] = { + {NPY_METH_get_loop, &object_to_object_get_loop}, + {0, NULL}}; + PyArrayMethod_Spec spec = { + .name = "object_to_object_cast", + .casting = NPY_NO_CASTING | _NPY_CAST_IS_VIEW, + .nin = 1, + .nout = 1, + .flags = NPY_METH_REQUIRES_PYAPI | NPY_METH_SUPPORTS_UNALIGNED, + .dtypes = dtypes, + .slots = slots, + }; + + int res = PyArray_AddCastingImplementation_FromSpec(&spec, 1); + Py_DECREF(Object); + return res; +} + + +NPY_NO_EXPORT int +PyArray_InitializeCasts() +{ + if (PyArray_InitializeNumericCasts() < 0) { + return -1; + } + if (PyArray_InitializeStringCasts() < 0) { + return -1; + } + if (PyArray_InitializeVoidToVoidCast() < 0) { + return -1; + } + if (PyArray_InitializeObjectToObjectCast() < 0) { + return -1; + } + /* Datetime casts are defined in datetime.c */ + if (PyArray_InitializeDatetimeCasts() < 0) { + return -1; + } + return 0; +} diff --git a/numpy/core/src/multiarray/convert_datatype.h b/numpy/core/src/multiarray/convert_datatype.h index bf77d699a7f8..5e0682f2267d 100644 --- a/numpy/core/src/multiarray/convert_datatype.h +++ b/numpy/core/src/multiarray/convert_datatype.h @@ -1,5 +1,15 @@ -#ifndef _NPY_ARRAY_CONVERT_DATATYPE_H_ -#define _NPY_ARRAY_CONVERT_DATATYPE_H_ +#ifndef NUMPY_CORE_SRC_MULTIARRAY_CONVERT_DATATYPE_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_CONVERT_DATATYPE_H_ + +#include "array_method.h" + +extern NPY_NO_EXPORT npy_intp REQUIRED_STR_LEN[]; + +NPY_NO_EXPORT PyObject * +PyArray_GetCastingImpl(PyArray_DTypeMeta *from, PyArray_DTypeMeta *to); + +NPY_NO_EXPORT PyObject * +_get_castingimpl(PyObject *NPY_UNUSED(module), PyObject *args); NPY_NO_EXPORT PyArray_VectorUnaryFunc * PyArray_GetCastFunc(PyArray_Descr *descr, int type_num); @@ -10,24 +20,83 @@ PyArray_ObjectType(PyObject *op, int minimum_type); NPY_NO_EXPORT PyArrayObject ** PyArray_ConvertToCommonType(PyObject *op, int *retn); +NPY_NO_EXPORT PyArray_Descr * +PyArray_LegacyResultType( + npy_intp narrs, PyArrayObject **arr, + npy_intp ndtypes, PyArray_Descr **dtypes); + NPY_NO_EXPORT int PyArray_ValidType(int type); -/* Like PyArray_CanCastArrayTo */ +NPY_NO_EXPORT int +dtype_kind_to_ordering(char kind); + +/* Used by PyArray_CanCastArrayTo and in the legacy ufunc type resolution */ NPY_NO_EXPORT npy_bool can_cast_scalar_to(PyArray_Descr *scal_type, char *scal_data, PyArray_Descr *to, NPY_CASTING casting); -/* - * This function calls Py_DECREF on flex_dtype, and replaces it with - * a new dtype that has been adapted based on the values in data_dtype - * and data_obj. If the flex_dtype is not flexible, it leaves it as is. - * - * The current flexible dtypes include NPY_STRING, NPY_UNICODE, NPY_VOID, - * and NPY_DATETIME with generic units. - */ +NPY_NO_EXPORT PyArray_Descr * +ensure_dtype_nbo(PyArray_Descr *type); + +NPY_NO_EXPORT int +should_use_min_scalar(npy_intp narrs, PyArrayObject **arr, + npy_intp ndtypes, PyArray_Descr **dtypes); + +NPY_NO_EXPORT const char * +npy_casting_to_string(NPY_CASTING casting); + NPY_NO_EXPORT void -PyArray_AdaptFlexibleDType(PyObject *data_obj, PyArray_Descr *data_dtype, - PyArray_Descr **flex_dtype); +npy_set_invalid_cast_error( + PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype, + NPY_CASTING casting, npy_bool scalar); + +NPY_NO_EXPORT PyArray_Descr * +PyArray_CastDescrToDType(PyArray_Descr *descr, PyArray_DTypeMeta *given_DType); + +NPY_NO_EXPORT PyArray_Descr * +PyArray_FindConcatenationDescriptor( + npy_intp n, PyArrayObject **arrays, PyObject *requested_dtype); + +NPY_NO_EXPORT int +PyArray_AddCastingImplementation(PyBoundArrayMethodObject *meth); + +NPY_NO_EXPORT int +PyArray_AddCastingImplementation_FromSpec(PyArrayMethod_Spec *spec, int private); + +NPY_NO_EXPORT NPY_CASTING +PyArray_MinCastSafety(NPY_CASTING casting1, NPY_CASTING casting2); + +NPY_NO_EXPORT NPY_CASTING +PyArray_GetCastSafety( + PyArray_Descr *from, PyArray_Descr *to, PyArray_DTypeMeta *to_dtype); + +NPY_NO_EXPORT int +PyArray_CheckCastSafety(NPY_CASTING casting, + PyArray_Descr *from, PyArray_Descr *to, PyArray_DTypeMeta *to_dtype); + +NPY_NO_EXPORT NPY_CASTING +legacy_same_dtype_resolve_descriptors( + PyArrayMethodObject *self, + PyArray_DTypeMeta *dtypes[2], + PyArray_Descr *given_descrs[2], + PyArray_Descr *loop_descrs[2]); + +NPY_NO_EXPORT int +legacy_cast_get_strided_loop( + PyArrayMethod_Context *context, + int aligned, int move_references, npy_intp *strides, + PyArrayMethod_StridedLoop **out_loop, NpyAuxData **out_transferdata, + NPY_ARRAYMETHOD_FLAGS *flags); + +NPY_NO_EXPORT NPY_CASTING +simple_cast_resolve_descriptors( + PyArrayMethodObject *self, + PyArray_DTypeMeta *dtypes[2], + PyArray_Descr *input_descrs[2], + PyArray_Descr *loop_descrs[2]); + +NPY_NO_EXPORT int +PyArray_InitializeCasts(void); -#endif +#endif /* NUMPY_CORE_SRC_MULTIARRAY_CONVERT_DATATYPE_H_ */ diff --git a/numpy/core/src/multiarray/ctors.c b/numpy/core/src/multiarray/ctors.c index 7367902cc44a..78003306afe9 100644 --- a/numpy/core/src/multiarray/ctors.c +++ b/numpy/core/src/multiarray/ctors.c @@ -1,9 +1,10 @@ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE + #define PY_SSIZE_T_CLEAN #include <Python.h> -#include "structmember.h" +#include <structmember.h> -#define NPY_NO_DEPRECATED_API NPY_API_VERSION -#define _MULTIARRAYMODULE #include "numpy/arrayobject.h" #include "numpy/arrayscalars.h" @@ -11,7 +12,7 @@ #include "npy_config.h" -#include "npy_import.h" +#include "npy_ctypes.h" #include "npy_pycompat.h" #include "multiarraymodule.h" @@ -19,9 +20,8 @@ #include "ctors.h" #include "convert_datatype.h" #include "shape.h" -#include "buffer.h" +#include "npy_buffer.h" #include "lowlevel_strided_loops.h" -#include "methods.h" #include "_datetime.h" #include "datetime_strings.h" #include "array_assign.h" @@ -31,6 +31,7 @@ #include <assert.h> #include "get_attr_string.h" +#include "array_coercion.h" /* * Reading from a file or a string. @@ -40,9 +41,31 @@ * regards to the handling of text representations. */ +/* + * Scanning function for next element parsing and separator skipping. + * These functions return: + * - 0 to indicate more data to read + * - -1 when reading stopped at the end of the string/file + * - -2 when reading stopped before the end was reached. + * + * The dtype specific parsing functions may set the python error state + * (they have to get the GIL first) additionally. + */ typedef int (*next_element)(void **, void *, PyArray_Descr *, void *); typedef int (*skip_separator)(void **, const char *, void *); + +static npy_bool +string_is_fully_read(char const* start, char const* end) { + if (end == NULL) { + return *start == '\0'; /* null terminated */ + } + else { + return start >= end; /* fixed length */ + } +} + + static int fromstr_next_element(char **s, void *dptr, PyArray_Descr *dtype, const char *end) @@ -50,19 +73,23 @@ fromstr_next_element(char **s, void *dptr, PyArray_Descr *dtype, char *e = *s; int r = dtype->f->fromstr(*s, dptr, &e, dtype); /* - * fromstr always returns 0 for basic dtypes - * s points to the end of the parsed string - * if an error occurs s is not changed + * fromstr always returns 0 for basic dtypes; s points to the end of the + * parsed string. If s is not changed an error occurred or the end was + * reached. */ - if (*s == e) { - /* Nothing read */ - return -1; + if (*s == e || r < 0) { + /* Nothing read, could be end of string or an error (or both) */ + if (string_is_fully_read(*s, end)) { + return -1; + } + return -2; } *s = e; if (end != NULL && *s > end) { + /* Stop the iteration if we read far enough */ return -1; } - return r; + return 0; } static int @@ -75,9 +102,13 @@ fromfile_next_element(FILE **fp, void *dptr, PyArray_Descr *dtype, if (r == 1) { return 0; } - else { + else if (r == EOF) { return -1; } + else { + /* unable to read more, but EOF not reached indicating an error. */ + return -2; + } } /* @@ -92,6 +123,7 @@ swab_separator(const char *sep) s = start = malloc(strlen(sep)+3); if (s == NULL) { + PyErr_NoMemory(); return NULL; } /* add space to front if there isn't one */ @@ -142,9 +174,10 @@ fromstr_skip_separator(char **s, const char *sep, const char *end) { char *string = *s; int result = 0; + while (1) { char c = *string; - if (c == '\0' || (end != NULL && string >= end)) { + if (string_is_fully_read(string, end)) { result = -1; break; } @@ -268,12 +301,12 @@ _update_descr_and_dimensions(PyArray_Descr **des, npy_intp *newdims, } if (tuple) { for (i = 0; i < numnew; i++) { - mydim[i] = (npy_intp) PyInt_AsLong( + mydim[i] = (npy_intp) PyLong_AsLong( PyTuple_GET_ITEM(old->subarray->shape, i)); } } else { - mydim[0] = (npy_intp) PyInt_AsLong(old->subarray->shape); + mydim[0] = (npy_intp) PyLong_AsLong(old->subarray->shape); } if (newstrides) { @@ -421,579 +454,267 @@ copy_and_swap(void *dst, void *src, int itemsize, npy_intp numitems, } } + /* - * adapted from Numarray, - * a: destination array - * s: source object, array or sequence - * dim: current recursion dimension, must be 0 on first call - * dst: must be NULL on first call - * it is a view on the destination array viewing the place where to put the - * data of the current recursion + * Recursive helper to assign using a coercion cache. This function + * must consume the cache depth first, just as the cache was originally + * produced. */ -static int -setArrayFromSequence(PyArrayObject *a, PyObject *s, - int dim, PyArrayObject * dst) +NPY_NO_EXPORT int +PyArray_AssignFromCache_Recursive( + PyArrayObject *self, const int ndim, coercion_cache_obj **cache) { - Py_ssize_t i, slen; - int res = -1; - - /* first recursion, view equal destination */ - if (dst == NULL) - dst = a; + /* Consume first cache element by extracting information and freeing it */ + PyObject *original_obj = (*cache)->converted_obj; + PyObject *obj = (*cache)->arr_or_sequence; + Py_INCREF(obj); + npy_bool sequence = (*cache)->sequence; + int depth = (*cache)->depth; + *cache = npy_unlink_coercion_cache(*cache); /* - * This code is to ensure that the sequence access below will - * return a lower-dimensional sequence. + * The maximum depth is special (specifically for objects), but usually + * unrolled in the sequence branch below. */ - - /* INCREF on entry DECREF on exit */ - Py_INCREF(s); - - if (PyArray_Check(s)) { - if (!(PyArray_CheckExact(s))) { + if (NPY_UNLIKELY(depth == ndim)) { + /* + * We have reached the maximum depth. We should simply assign to the + * element in principle. There is one exception. If this is a 0-D + * array being stored into a 0-D array (but we do not reach here then). + */ + if (PyArray_ISOBJECT(self)) { + assert(ndim != 0); /* guaranteed by PyArray_AssignFromCache */ + assert(PyArray_NDIM(self) == 0); + Py_DECREF(obj); + return PyArray_Pack(PyArray_DESCR(self), PyArray_BYTES(self), + original_obj); + } + if (sequence) { /* - * make sure a base-class array is used so that the dimensionality - * reduction assumption is correct. + * Sanity check which may be removed, the error is raised already + * in `PyArray_DiscoverDTypeAndShape`. */ - /* This will DECREF(s) if replaced */ - s = PyArray_EnsureArray(s); - if (s == NULL) { - goto fail; - } - } - - /* dst points to correct array subsection */ - if (PyArray_CopyInto(dst, (PyArrayObject *)s) < 0) { + assert(0); + PyErr_SetString(PyExc_RuntimeError, + "setting an array element with a sequence"); goto fail; } - - Py_DECREF(s); - return 0; - } - - if (dim > PyArray_NDIM(a)) { - PyErr_Format(PyExc_ValueError, - "setArrayFromSequence: sequence/array dimensions mismatch."); - goto fail; - } - - slen = PySequence_Length(s); - if (slen < 0) { - goto fail; - } - /* - * Either the dimensions match, or the sequence has length 1 and can - * be broadcast to the destination. - */ - if (slen != PyArray_DIMS(a)[dim] && slen != 1) { - PyErr_Format(PyExc_ValueError, - "cannot copy sequence with size %d to array axis " - "with dimension %d", (int)slen, (int)PyArray_DIMS(a)[dim]); - goto fail; + else if (original_obj != obj || !PyArray_CheckExact(obj)) { + /* + * If the leave node is an array-like, but not a numpy array, + * we pretend it is an arbitrary scalar. This means that in + * most cases (where the dtype is int or float), we will end + * up using float(array-like), or int(array-like). That does + * not support general casting, but helps Quantity and masked + * arrays, because it allows them to raise an error when + * `__float__()` or `__int__()` is called. + */ + Py_DECREF(obj); + return PyArray_SETITEM(self, PyArray_BYTES(self), original_obj); + } } - /* Broadcast the one element from the sequence to all the outputs */ - if (slen == 1) { - PyObject *o; - npy_intp alen = PyArray_DIM(a, dim); - - o = PySequence_GetItem(s, 0); - if (o == NULL) { + /* The element is either a sequence, or an array */ + if (!sequence) { + /* Straight forward array assignment */ + assert(PyArray_Check(obj)); + if (PyArray_CopyInto(self, (PyArrayObject *)obj) < 0) { goto fail; } - - for (i = 0; i < alen; i++) { - if ((PyArray_NDIM(a) - dim) > 1) { - PyArrayObject * tmp = - (PyArrayObject *)array_item_asarray(dst, i); - if (tmp == NULL) { - goto fail; - } - - res = setArrayFromSequence(a, o, dim+1, tmp); - Py_DECREF(tmp); - } - else { - char * b = (PyArray_BYTES(dst) + i * PyArray_STRIDES(dst)[0]); - res = PyArray_SETITEM(dst, b, o); - } - if (res < 0) { - Py_DECREF(o); - goto fail; - } - } - Py_DECREF(o); } - /* Copy element by element */ else { - PyObject * seq; - seq = PySequence_Fast(s, "Could not convert object to sequence"); - if (seq == NULL) { + assert(depth != ndim); + npy_intp length = PySequence_Length(obj); + if (length != PyArray_DIMS(self)[0]) { + PyErr_SetString(PyExc_RuntimeError, + "Inconsistent object during array creation? " + "Content of sequences changed (length inconsistent)."); goto fail; } - for (i = 0; i < slen; i++) { - PyObject * o = PySequence_Fast_GET_ITEM(seq, i); - if ((PyArray_NDIM(a) - dim) > 1) { - PyArrayObject * tmp = - (PyArrayObject *)array_item_asarray(dst, i); - if (tmp == NULL) { - Py_DECREF(seq); + + for (npy_intp i = 0; i < length; i++) { + PyObject *value = PySequence_Fast_GET_ITEM(obj, i); + + if (*cache == NULL || (*cache)->converted_obj != value || + (*cache)->depth != depth + 1) { + if (ndim != depth + 1) { + PyErr_SetString(PyExc_RuntimeError, + "Inconsistent object during array creation? " + "Content of sequences changed (now too shallow)."); + goto fail; + } + /* Straight forward assignment of elements */ + char *item; + item = (PyArray_BYTES(self) + i * PyArray_STRIDES(self)[0]); + if (PyArray_Pack(PyArray_DESCR(self), item, value) < 0) { goto fail; } - - res = setArrayFromSequence(a, o, dim+1, tmp); - Py_DECREF(tmp); } else { - char * b = (PyArray_BYTES(dst) + i * PyArray_STRIDES(dst)[0]); - res = PyArray_SETITEM(dst, b, o); - } - if (res < 0) { - Py_DECREF(seq); - goto fail; + PyArrayObject *view; + view = (PyArrayObject *)array_item_asarray(self, i); + if (view == NULL) { + goto fail; + } + if (PyArray_AssignFromCache_Recursive(view, ndim, cache) < 0) { + Py_DECREF(view); + goto fail; + } + Py_DECREF(view); } } - Py_DECREF(seq); } - - Py_DECREF(s); + Py_DECREF(obj); return 0; - fail: - Py_DECREF(s); - return res; + fail: + Py_DECREF(obj); + return -1; } + +/** + * Fills an item based on a coercion cache object. It consumes the cache + * object while doing so. + * + * @param self Array to fill. + * @param cache coercion_cache_object, will be consumed. The cache must not + * contain a single array (must start with a sequence). The array case + * should be handled by `PyArray_FromArray()` before. + * @return 0 on success -1 on failure. + */ NPY_NO_EXPORT int -PyArray_AssignFromSequence(PyArrayObject *self, PyObject *v) -{ - if (!PySequence_Check(v)) { - PyErr_SetString(PyExc_ValueError, - "assignment from non-sequence"); +PyArray_AssignFromCache(PyArrayObject *self, coercion_cache_obj *cache) { + int ndim = PyArray_NDIM(self); + /* + * Do not support ndim == 0 now with an array in the cache. + * The ndim == 0 is special because np.array(np.array(0), dtype=object) + * should unpack the inner array. + * Since the single-array case is special, it is handled previously + * in either case. + */ + assert(cache->sequence); + assert(ndim != 0); /* guaranteed if cache contains a sequence */ + + if (PyArray_AssignFromCache_Recursive(self, ndim, &cache) < 0) { + /* free the remaining cache. */ + npy_free_coercion_cache(cache); return -1; } - if (PyArray_NDIM(self) == 0) { - PyErr_SetString(PyExc_ValueError, - "assignment to 0-d array"); + + /* + * Sanity check, this is the initial call, and when it returns, the + * cache has to be fully consumed, otherwise something is wrong. + * NOTE: May be nicer to put into a recursion helper. + */ + if (cache != NULL) { + PyErr_SetString(PyExc_RuntimeError, + "Inconsistent object during array creation? " + "Content of sequences changed (cache not consumed)."); + npy_free_coercion_cache(cache); return -1; } - return setArrayFromSequence(self, v, 0, NULL); + return 0; } -/* - * The rest of this code is to build the right kind of array - * from a python object. - */ -static int -discover_itemsize(PyObject *s, int nd, int *itemsize, int string_type) +static void +raise_memory_error(int nd, npy_intp const *dims, PyArray_Descr *descr) { - int r; - npy_intp n, i; + static PyObject *exc_type = NULL; - if (PyArray_Check(s)) { - *itemsize = PyArray_MAX(*itemsize, PyArray_ITEMSIZE((PyArrayObject *)s)); - return 0; + npy_cache_import( + "numpy.core._exceptions", "_ArrayMemoryError", + &exc_type); + if (exc_type == NULL) { + goto fail; } - if ((nd == 0) || PyString_Check(s) || -#if defined(NPY_PY3K) - PyMemoryView_Check(s) || -#else - PyBuffer_Check(s) || -#endif - PyUnicode_Check(s)) { - - /* If an object has no length, leave it be */ - if (string_type && s != NULL && - !PyString_Check(s) && !PyUnicode_Check(s)) { - PyObject *s_string = NULL; - if (string_type == NPY_STRING) { - s_string = PyObject_Str(s); - } - else { -#if defined(NPY_PY3K) - s_string = PyObject_Str(s); -#else - s_string = PyObject_Unicode(s); -#endif - } - if (s_string) { - n = PyObject_Length(s_string); - Py_DECREF(s_string); - } - else { - n = -1; - } - } - else { - n = PyObject_Length(s); - } - if (n == -1) { - PyErr_Clear(); - } - else { - *itemsize = PyArray_MAX(*itemsize, n); - } - return 0; + PyObject *shape = PyArray_IntTupleFromIntp(nd, dims); + if (shape == NULL) { + goto fail; } - n = PySequence_Length(s); - for (i = 0; i < n; i++) { - PyObject *e = PySequence_GetItem(s,i); - - if (e == NULL) { - return -1; - } - - r = discover_itemsize(e, nd - 1, itemsize, string_type); - Py_DECREF(e); - if (r == -1) { - return -1; - } + /* produce an error object */ + PyObject *exc_value = PyTuple_Pack(2, shape, (PyObject *)descr); + Py_DECREF(shape); + if (exc_value == NULL){ + goto fail; } + PyErr_SetObject(exc_type, exc_value); + Py_DECREF(exc_value); + return; - return 0; +fail: + /* we couldn't raise the formatted exception for some reason */ + PyErr_WriteUnraisable(NULL); + PyErr_NoMemory(); } /* - * Take an arbitrary object and discover how many dimensions it - * has, filling in the dimensions as we go. + * Generic new array creation routine. + * Internal variant with calloc argument for PyArray_Zeros. + * + * steals a reference to descr. On failure or descr->subarray, descr will + * be decrefed. */ -static int -discover_dimensions(PyObject *obj, int *maxndim, npy_intp *d, int check_it, - int stop_at_string, int stop_at_tuple, - int *out_is_object) +NPY_NO_EXPORT PyObject * +PyArray_NewFromDescr_int( + PyTypeObject *subtype, PyArray_Descr *descr, int nd, + npy_intp const *dims, npy_intp const *strides, void *data, + int flags, PyObject *obj, PyObject *base, int zeroed, + int allow_emptystring) { - PyObject *e; - int r; - npy_intp n, i; - Py_buffer buffer_view; - PyObject * seq; + PyArrayObject_fields *fa; + npy_intp nbytes; - if (*maxndim == 0) { - return 0; + if (nd > NPY_MAXDIMS || nd < 0) { + PyErr_Format(PyExc_ValueError, + "number of dimensions must be within [0, %d]", NPY_MAXDIMS); + Py_DECREF(descr); + return NULL; } - /* obj is an Array */ - if (PyArray_Check(obj)) { - PyArrayObject *arr = (PyArrayObject *)obj; - - if (PyArray_NDIM(arr) < *maxndim) { - *maxndim = PyArray_NDIM(arr); - } - - for (i=0; i<*maxndim; i++) { - d[i] = PyArray_DIM(arr,i); + if (descr->subarray) { + PyObject *ret; + npy_intp newdims[2*NPY_MAXDIMS]; + npy_intp *newstrides = NULL; + memcpy(newdims, dims, nd*sizeof(npy_intp)); + if (strides) { + newstrides = newdims + NPY_MAXDIMS; + memcpy(newstrides, strides, nd*sizeof(npy_intp)); } - return 0; - } - - /* obj is a Scalar */ - if (PyArray_IsScalar(obj, Generic)) { - *maxndim = 0; - return 0; - } - - /* obj is not a Sequence */ - if (!PySequence_Check(obj) || - PySequence_Length(obj) < 0) { - *maxndim = 0; - PyErr_Clear(); - return 0; + nd =_update_descr_and_dimensions(&descr, newdims, + newstrides, nd); + ret = PyArray_NewFromDescr_int( + subtype, descr, + nd, newdims, newstrides, data, + flags, obj, base, + zeroed, allow_emptystring); + return ret; } - /* obj is a String */ - if (PyString_Check(obj) || -#if defined(NPY_PY3K) -#else - PyBuffer_Check(obj) || -#endif - PyUnicode_Check(obj)) { - if (stop_at_string) { - *maxndim = 0; - } - else { - d[0] = PySequence_Length(obj); - *maxndim = 1; + /* Check datatype element size */ + nbytes = descr->elsize; + if (PyDataType_ISUNSIZED(descr)) { + if (!PyDataType_ISFLEXIBLE(descr)) { + PyErr_SetString(PyExc_TypeError, "Empty data-type"); + Py_DECREF(descr); + return NULL; } - return 0; - } - - /* obj is a Tuple, but tuples aren't expanded */ - if (stop_at_tuple && PyTuple_Check(obj)) { - *maxndim = 0; - return 0; - } - - /* obj is a PEP 3118 buffer */ - /* PEP 3118 buffer interface */ - if (PyObject_CheckBuffer(obj) == 1) { - memset(&buffer_view, 0, sizeof(Py_buffer)); - if (PyObject_GetBuffer(obj, &buffer_view, - PyBUF_STRIDES|PyBUF_SIMPLE) == 0 || - PyObject_GetBuffer(obj, &buffer_view, - PyBUF_ND|PyBUF_SIMPLE) == 0) { - int nd = buffer_view.ndim; - - if (nd < *maxndim) { - *maxndim = nd; + else if (PyDataType_ISSTRING(descr) && !allow_emptystring && + data == NULL) { + PyArray_DESCR_REPLACE(descr); + if (descr == NULL) { + return NULL; } - for (i = 0; i < *maxndim; i++) { - d[i] = buffer_view.shape[i]; + if (descr->type_num == NPY_STRING) { + nbytes = descr->elsize = 1; } - PyBuffer_Release(&buffer_view); - return 0; - } - else if (PyObject_GetBuffer(obj, &buffer_view, PyBUF_SIMPLE) == 0) { - d[0] = buffer_view.len; - *maxndim = 1; - PyBuffer_Release(&buffer_view); - return 0; - } - else { - PyErr_Clear(); - } - } - - /* obj has the __array_struct__ interface */ - e = PyArray_LookupSpecial_OnInstance(obj, "__array_struct__"); - if (e != NULL) { - int nd = -1; - - if (NpyCapsule_Check(e)) { - PyArrayInterface *inter; - inter = (PyArrayInterface *)NpyCapsule_AsVoidPtr(e); - if (inter->two == 2) { - nd = inter->nd; - if (nd >= 0) { - if (nd < *maxndim) { - *maxndim = nd; - } - for (i=0; i<*maxndim; i++) { - d[i] = inter->shape[i]; - } - } + else { + nbytes = descr->elsize = sizeof(npy_ucs4); } } - Py_DECREF(e); - if (nd >= 0) { - return 0; - } - } - - /* obj has the __array_interface__ interface */ - e = PyArray_LookupSpecial_OnInstance(obj, "__array_interface__"); - if (e != NULL) { - int nd = -1; - if (PyDict_Check(e)) { - PyObject *new; - new = PyDict_GetItemString(e, "shape"); - if (new && PyTuple_Check(new)) { - nd = PyTuple_GET_SIZE(new); - if (nd < *maxndim) { - *maxndim = nd; - } - for (i=0; i<*maxndim; i++) { - d[i] = PyInt_AsSsize_t(PyTuple_GET_ITEM(new, i)); - if (d[i] < 0) { - PyErr_SetString(PyExc_RuntimeError, - "Invalid shape in __array_interface__"); - Py_DECREF(e); - return -1; - } - } - } - } - Py_DECREF(e); - if (nd >= 0) { - return 0; - } - } - - seq = PySequence_Fast(obj, "Could not convert object to sequence"); - if (seq == NULL) { - /* - * PySequence_Check detects whether an old type object is a - * sequence by the presence of the __getitem__ attribute, and - * for new type objects that aren't dictionaries by the - * presence of the __len__ attribute as well. In either case it - * is possible to have an object that tests as a sequence but - * doesn't behave as a sequence and consequently, the - * PySequence_GetItem call can fail. When that happens and the - * object looks like a dictionary, we truncate the dimensions - * and set the object creation flag, otherwise we pass the - * error back up the call chain. - */ - if (PyErr_ExceptionMatches(PyExc_KeyError)) { - PyErr_Clear(); - *maxndim = 0; - *out_is_object = 1; - return 0; - } - else { - return -1; - } - } - n = PySequence_Fast_GET_SIZE(seq); - - d[0] = n; - - /* 1-dimensional sequence */ - if (n == 0 || *maxndim == 1) { - *maxndim = 1; - Py_DECREF(seq); - return 0; - } - else { - npy_intp dtmp[NPY_MAXDIMS]; - int j, maxndim_m1 = *maxndim - 1; - e = PySequence_Fast_GET_ITEM(seq, 0); - - r = discover_dimensions(e, &maxndim_m1, d + 1, check_it, - stop_at_string, stop_at_tuple, - out_is_object); - if (r < 0) { - Py_DECREF(seq); - return r; - } - - /* For the dimension truncation check below */ - *maxndim = maxndim_m1 + 1; - for (i = 1; i < n; ++i) { - e = PySequence_Fast_GET_ITEM(seq, i); - /* Get the dimensions of the first item */ - r = discover_dimensions(e, &maxndim_m1, dtmp, check_it, - stop_at_string, stop_at_tuple, - out_is_object); - if (r < 0) { - Py_DECREF(seq); - return r; - } - - /* Reduce max_ndim_m1 to just items which match */ - for (j = 0; j < maxndim_m1; ++j) { - if (dtmp[j] != d[j+1]) { - maxndim_m1 = j; - break; - } - } - } - /* - * If the dimensions are truncated, need to produce - * an object array. - */ - if (maxndim_m1 + 1 < *maxndim) { - *out_is_object = 1; - *maxndim = maxndim_m1 + 1; - } - } - - Py_DECREF(seq); - - return 0; -} - -/* - * Generic new array creation routine. - * Internal variant with calloc argument for PyArray_Zeros. - * - * steals a reference to descr. On failure or descr->subarray, descr will - * be decrefed. - */ -NPY_NO_EXPORT PyObject * -PyArray_NewFromDescr_int(PyTypeObject *subtype, PyArray_Descr *descr, int nd, - npy_intp *dims, npy_intp *strides, void *data, - int flags, PyObject *obj, PyObject *base, int zeroed, - int allow_emptystring) -{ - PyArrayObject_fields *fa; - int i, is_empty; - npy_intp nbytes; - - if (descr->subarray) { - PyObject *ret; - npy_intp newdims[2*NPY_MAXDIMS]; - npy_intp *newstrides = NULL; - memcpy(newdims, dims, nd*sizeof(npy_intp)); - if (strides) { - newstrides = newdims + NPY_MAXDIMS; - memcpy(newstrides, strides, nd*sizeof(npy_intp)); - } - nd =_update_descr_and_dimensions(&descr, newdims, - newstrides, nd); - ret = PyArray_NewFromDescr_int( - subtype, descr, - nd, newdims, newstrides, data, - flags, obj, base, - zeroed, allow_emptystring); - return ret; - } - - if ((unsigned int)nd > (unsigned int)NPY_MAXDIMS) { - PyErr_Format(PyExc_ValueError, - "number of dimensions must be within [0, %d]", - NPY_MAXDIMS); - Py_DECREF(descr); - return NULL; - } - - /* Check datatype element size */ - nbytes = descr->elsize; - if (PyDataType_ISUNSIZED(descr)) { - if (!PyDataType_ISFLEXIBLE(descr)) { - PyErr_SetString(PyExc_TypeError, "Empty data-type"); - Py_DECREF(descr); - return NULL; - } - else if (PyDataType_ISSTRING(descr) && !allow_emptystring && - data == NULL) { - PyArray_DESCR_REPLACE(descr); - if (descr == NULL) { - return NULL; - } - if (descr->type_num == NPY_STRING) { - nbytes = descr->elsize = 1; - } - else { - nbytes = descr->elsize = sizeof(npy_ucs4); - } - } - } - - /* Check dimensions and multiply them to nbytes */ - is_empty = 0; - for (i = 0; i < nd; i++) { - npy_intp dim = dims[i]; - - if (dim == 0) { - /* - * Compare to PyArray_OverflowMultiplyList that - * returns 0 in this case. - */ - is_empty = 1; - continue; - } - - if (dim < 0) { - PyErr_SetString(PyExc_ValueError, - "negative dimensions are not allowed"); - Py_DECREF(descr); - return NULL; - } - - /* - * Care needs to be taken to avoid integer overflow when - * multiplying the dimensions together to get the total size of the - * array. - */ - if (npy_mul_with_overflow_intp(&nbytes, nbytes, dim)) { - PyErr_SetString(PyExc_ValueError, - "array is too big; `arr.size * arr.dtype.itemsize` " - "is larger than the maximum possible size."); - Py_DECREF(descr); - return NULL; - } } fa = (PyArrayObject_fields *) subtype->tp_alloc(subtype, 0); @@ -1001,9 +722,12 @@ PyArray_NewFromDescr_int(PyTypeObject *subtype, PyArray_Descr *descr, int nd, Py_DECREF(descr); return NULL; } + fa->_buffer_info = NULL; fa->nd = nd; fa->dimensions = NULL; fa->data = NULL; + fa->mem_handler = NULL; + if (data == NULL) { fa->flags = NPY_ARRAY_DEFAULT; if (flags) { @@ -1029,67 +753,115 @@ PyArray_NewFromDescr_int(PyTypeObject *subtype, PyArray_Descr *descr, int nd, goto fail; } fa->strides = fa->dimensions + nd; - memcpy(fa->dimensions, dims, sizeof(npy_intp)*nd); - if (strides == NULL) { /* fill it in */ + + /* + * Copy dimensions, check them, and find total array size `nbytes` + * + * Note that we ignore 0-length dimensions, to match this in the `free` + * calls, `PyArray_NBYTES_ALLOCATED` is a private helper matching this + * behaviour, but without overflow checking. + */ + for (int i = 0; i < nd; i++) { + fa->dimensions[i] = dims[i]; + + if (fa->dimensions[i] == 0) { + /* + * Compare to PyArray_OverflowMultiplyList that + * returns 0 in this case. See also `PyArray_NBYTES_ALLOCATED`. + */ + continue; + } + + if (fa->dimensions[i] < 0) { + PyErr_SetString(PyExc_ValueError, + "negative dimensions are not allowed"); + goto fail; + } + + /* + * Care needs to be taken to avoid integer overflow when multiplying + * the dimensions together to get the total size of the array. + */ + if (npy_mul_with_overflow_intp(&nbytes, nbytes, fa->dimensions[i])) { + PyErr_SetString(PyExc_ValueError, + "array is too big; `arr.size * arr.dtype.itemsize` " + "is larger than the maximum possible size."); + goto fail; + } + } + + /* Fill the strides (or copy them if they were passed in) */ + if (strides == NULL) { + /* fill the strides and set the contiguity flags */ _array_fill_strides(fa->strides, dims, nd, descr->elsize, flags, &(fa->flags)); } else { - /* - * we allow strides even when we create - * the memory, but be careful with this... - */ - memcpy(fa->strides, strides, sizeof(npy_intp)*nd); + /* User to provided strides (user is responsible for correctness) */ + for (int i = 0; i < nd; i++) { + fa->strides[i] = strides[i]; + } + /* Since the strides were passed in must update contiguity */ + PyArray_UpdateFlags((PyArrayObject *)fa, + NPY_ARRAY_C_CONTIGUOUS|NPY_ARRAY_F_CONTIGUOUS); } } else { - fa->dimensions = fa->strides = NULL; - fa->flags |= NPY_ARRAY_F_CONTIGUOUS; + fa->dimensions = NULL; + fa->strides = NULL; + fa->flags |= NPY_ARRAY_C_CONTIGUOUS|NPY_ARRAY_F_CONTIGUOUS; } + if (data == NULL) { + /* Store the handler in case the default is modified */ + fa->mem_handler = PyDataMem_GetHandler(); + if (fa->mem_handler == NULL) { + goto fail; + } /* * Allocate something even for zero-space arrays * e.g. shape=(0,) -- otherwise buffer exposure * (a.data) doesn't work as it should. * Could probably just allocate a few bytes here. -- Chuck + * Note: always sync this with calls to PyDataMem_UserFREE */ - if (is_empty) { - nbytes = descr->elsize; + if (nbytes == 0) { + nbytes = descr->elsize ? descr->elsize : 1; } /* * It is bad to have uninitialized OBJECT pointers * which could also be sub-fields of a VOID array */ if (zeroed || PyDataType_FLAGCHK(descr, NPY_NEEDS_INIT)) { - data = npy_alloc_cache_zero(nbytes); + data = PyDataMem_UserNEW_ZEROED(nbytes, 1, fa->mem_handler); } else { - data = npy_alloc_cache(nbytes); + data = PyDataMem_UserNEW(nbytes, fa->mem_handler); } if (data == NULL) { - PyErr_NoMemory(); + raise_memory_error(fa->nd, fa->dimensions, descr); goto fail; } - fa->flags |= NPY_ARRAY_OWNDATA; + fa->flags |= NPY_ARRAY_OWNDATA; } else { + /* The handlers should never be called in this case */ + fa->mem_handler = NULL; /* - * If data is passed in, this object won't own it by default. - * Caller must arrange for this to be reset if truly desired + * If data is passed in, this object won't own it. */ fa->flags &= ~NPY_ARRAY_OWNDATA; } fa->data = data; /* - * always update the flags to get the right CONTIGUOUS, ALIGN properties - * not owned data and input strides may not be aligned and on some - * platforms (debian sparc) malloc does not provide enough alignment for - * long double types + * Always update the aligned flag. Not owned data or input strides may + * not be aligned. Also on some platforms (debian sparc) malloc does not + * provide enough alignment for long double types. */ - PyArray_UpdateFlags((PyArrayObject *)fa, NPY_ARRAY_UPDATE_ALL); + PyArray_UpdateFlags((PyArrayObject *)fa, NPY_ARRAY_ALIGNED); /* Set the base object. It's important to do it here so that * __array_finalize__ below receives it @@ -1102,33 +874,37 @@ PyArray_NewFromDescr_int(PyTypeObject *subtype, PyArray_Descr *descr, int nd, } /* - * call the __array_finalize__ - * method if a subtype. - * If obj is NULL, then call method with Py_None + * call the __array_finalize__ method if a subtype was requested. + * If obj is NULL use Py_None for the Python callback. */ - if ((subtype != &PyArray_Type)) { - PyObject *res, *func, *args; + if (subtype != &PyArray_Type) { + PyObject *res, *func; func = PyObject_GetAttr((PyObject *)fa, npy_ma_str_array_finalize); - if (func && func != Py_None) { - if (NpyCapsule_Check(func)) { + if (func == NULL) { + goto fail; + } + else if (func == Py_None) { + Py_DECREF(func); + } + else { + if (PyCapsule_CheckExact(func)) { /* A C-function is stored here */ PyArray_FinalizeFunc *cfunc; - cfunc = NpyCapsule_AsVoidPtr(func); + cfunc = PyCapsule_GetPointer(func, NULL); Py_DECREF(func); + if (cfunc == NULL) { + goto fail; + } if (cfunc((PyArrayObject *)fa, obj) < 0) { goto fail; } } else { - args = PyTuple_New(1); if (obj == NULL) { - obj=Py_None; + obj = Py_None; } - Py_INCREF(obj); - PyTuple_SET_ITEM(args, 0, obj); - res = PyObject_Call(func, args, NULL); - Py_DECREF(args); + res = PyObject_CallFunctionObjArgs(func, obj, NULL); Py_DECREF(func); if (res == NULL) { goto fail; @@ -1138,11 +914,11 @@ PyArray_NewFromDescr_int(PyTypeObject *subtype, PyArray_Descr *descr, int nd, } } } - else Py_XDECREF(func); } return (PyObject *)fa; fail: + Py_XDECREF(fa->mem_handler); Py_DECREF(fa); return NULL; } @@ -1155,9 +931,10 @@ PyArray_NewFromDescr_int(PyTypeObject *subtype, PyArray_Descr *descr, int nd, * true, dtype will be decrefed. */ NPY_NO_EXPORT PyObject * -PyArray_NewFromDescr(PyTypeObject *subtype, PyArray_Descr *descr, - int nd, npy_intp *dims, npy_intp *strides, void *data, - int flags, PyObject *obj) +PyArray_NewFromDescr( + PyTypeObject *subtype, PyArray_Descr *descr, + int nd, npy_intp const *dims, npy_intp const *strides, void *data, + int flags, PyObject *obj) { return PyArray_NewFromDescrAndBase( subtype, descr, @@ -1171,7 +948,7 @@ PyArray_NewFromDescr(PyTypeObject *subtype, PyArray_Descr *descr, NPY_NO_EXPORT PyObject * PyArray_NewFromDescrAndBase( PyTypeObject *subtype, PyArray_Descr *descr, - int nd, npy_intp *dims, npy_intp *strides, void *data, + int nd, npy_intp const *dims, npy_intp const *strides, void *data, int flags, PyObject *obj, PyObject *base) { return PyArray_NewFromDescr_int(subtype, descr, nd, @@ -1179,9 +956,9 @@ PyArray_NewFromDescrAndBase( flags, obj, base, 0, 0); } -/*NUMPY_API +/* * Creates a new array with the same shape as the provided one, - * with possible memory layout order and data type changes. + * with possible memory layout order, data type and shape changes. * * prototype - The array the new one should be like. * order - NPY_CORDER - C-contiguous result. @@ -1189,6 +966,8 @@ PyArray_NewFromDescrAndBase( * NPY_ANYORDER - Fortran if prototype is Fortran, C otherwise. * NPY_KEEPORDER - Keeps the axis ordering of prototype. * dtype - If not NULL, overrides the data type of the result. + * ndim - If not -1, overrides the shape of the result. + * dims - If ndim is not -1, overrides the shape of the result. * subok - If 1, use the prototype's array subtype, otherwise * always create a base-class array. * @@ -1196,11 +975,18 @@ PyArray_NewFromDescrAndBase( * dtype->subarray is true, dtype will be decrefed. */ NPY_NO_EXPORT PyObject * -PyArray_NewLikeArray(PyArrayObject *prototype, NPY_ORDER order, - PyArray_Descr *dtype, int subok) +PyArray_NewLikeArrayWithShape(PyArrayObject *prototype, NPY_ORDER order, + PyArray_Descr *dtype, int ndim, npy_intp const *dims, int subok) { PyObject *ret = NULL; - int ndim = PyArray_NDIM(prototype); + + if (ndim == -1) { + ndim = PyArray_NDIM(prototype); + dims = PyArray_DIMS(prototype); + } + else if (order == NPY_KEEPORDER && (ndim != PyArray_NDIM(prototype))) { + order = NPY_CORDER; + } /* If no override data type, use the one from the prototype */ if (dtype == NULL) { @@ -1233,7 +1019,7 @@ PyArray_NewLikeArray(PyArrayObject *prototype, NPY_ORDER order, ret = PyArray_NewFromDescr(subok ? Py_TYPE(prototype) : &PyArray_Type, dtype, ndim, - PyArray_DIMS(prototype), + dims, NULL, NULL, order, @@ -1242,27 +1028,37 @@ PyArray_NewLikeArray(PyArrayObject *prototype, NPY_ORDER order, /* KEEPORDER needs some analysis of the strides */ else { npy_intp strides[NPY_MAXDIMS], stride; - npy_intp *shape = PyArray_DIMS(prototype); npy_stride_sort_item strideperm[NPY_MAXDIMS]; int idim; - PyArray_CreateSortedStridePerm(PyArray_NDIM(prototype), + PyArray_CreateSortedStridePerm(ndim, PyArray_STRIDES(prototype), strideperm); /* Build the new strides */ stride = dtype->elsize; + if (stride == 0 && PyDataType_ISSTRING(dtype)) { + /* Special case for dtype=str or dtype=bytes. */ + if (dtype->type_num == NPY_STRING) { + /* dtype is bytes */ + stride = 1; + } + else { + /* dtype is str (type_num is NPY_UNICODE) */ + stride = 4; + } + } for (idim = ndim-1; idim >= 0; --idim) { npy_intp i_perm = strideperm[idim].perm; strides[i_perm] = stride; - stride *= shape[i_perm]; + stride *= dims[i_perm]; } /* Finally, allocate the array */ ret = PyArray_NewFromDescr(subok ? Py_TYPE(prototype) : &PyArray_Type, dtype, ndim, - shape, + dims, strides, NULL, 0, @@ -1272,13 +1068,37 @@ PyArray_NewLikeArray(PyArrayObject *prototype, NPY_ORDER order, return ret; } +/*NUMPY_API + * Creates a new array with the same shape as the provided one, + * with possible memory layout order and data type changes. + * + * prototype - The array the new one should be like. + * order - NPY_CORDER - C-contiguous result. + * NPY_FORTRANORDER - Fortran-contiguous result. + * NPY_ANYORDER - Fortran if prototype is Fortran, C otherwise. + * NPY_KEEPORDER - Keeps the axis ordering of prototype. + * dtype - If not NULL, overrides the data type of the result. + * subok - If 1, use the prototype's array subtype, otherwise + * always create a base-class array. + * + * NOTE: If dtype is not NULL, steals the dtype reference. On failure or when + * dtype->subarray is true, dtype will be decrefed. + */ +NPY_NO_EXPORT PyObject * +PyArray_NewLikeArray(PyArrayObject *prototype, NPY_ORDER order, + PyArray_Descr *dtype, int subok) +{ + return PyArray_NewLikeArrayWithShape(prototype, order, dtype, -1, NULL, subok); +} + /*NUMPY_API * Generic new array creation routine. */ NPY_NO_EXPORT PyObject * -PyArray_New(PyTypeObject *subtype, int nd, npy_intp *dims, int type_num, - npy_intp *strides, void *data, int itemsize, int flags, - PyObject *obj) +PyArray_New( + PyTypeObject *subtype, int nd, npy_intp const *dims, int type_num, + npy_intp const *strides, void *data, int itemsize, int flags, + PyObject *obj) { PyArray_Descr *descr; PyObject *new; @@ -1326,28 +1146,6 @@ _dtype_from_buffer_3118(PyObject *memoryview) } -/* - * Call the python _is_from_ctypes - */ -NPY_NO_EXPORT int -_is_from_ctypes(PyObject *obj) { - PyObject *ret_obj; - static PyObject *py_func = NULL; - - npy_cache_import("numpy.core._internal", "_is_from_ctypes", &py_func); - - if (py_func == NULL) { - return -1; - } - ret_obj = PyObject_CallFunctionObjArgs(py_func, obj, NULL); - if (ret_obj == NULL) { - return -1; - } - - return PyObject_IsTrue(ret_obj); -} - - NPY_NO_EXPORT PyObject * _array_from_buffer_3118(PyObject *memoryview) { @@ -1376,23 +1174,17 @@ _array_from_buffer_3118(PyObject *memoryview) * bpo-32780 * bpo-32782 * - * Note that even if the above are fixed in master, we have to drop the + * Note that even if the above are fixed in main, we have to drop the * early patch versions of python to actually make use of the fixes. */ - - int is_ctypes = _is_from_ctypes(view->obj); - if (is_ctypes < 0) { - /* This error is not useful */ - PyErr_WriteUnraisable(view->obj); - is_ctypes = 0; - } - - if (!is_ctypes) { + if (!npy_ctypes_check(Py_TYPE(view->obj))) { /* This object has no excuse for a broken PEP3118 buffer */ - PyErr_SetString( + PyErr_Format( PyExc_RuntimeError, - "Item size computed from the PEP 3118 buffer format " - "string does not match the actual item size."); + "Item size %zd for PEP 3118 buffer format " + "string %s does not match the dtype %c item size %d.", + view->itemsize, view->format, descr->type, + descr->elsize); Py_DECREF(descr); return NULL; } @@ -1412,6 +1204,7 @@ _array_from_buffer_3118(PyObject *memoryview) * dimensions, so the array is now 0d. */ nd = 0; + Py_DECREF(descr); descr = (PyArray_Descr *)PyObject_CallFunctionObjArgs( (PyObject *)&PyArrayDescr_Type, Py_TYPE(view->obj), NULL); if (descr == NULL) { @@ -1482,156 +1275,75 @@ _array_from_buffer_3118(PyObject *memoryview) } -/*NUMPY_API - * Retrieves the array parameters for viewing/converting an arbitrary - * PyObject* to a NumPy array. This allows the "innate type and shape" - * of Python list-of-lists to be discovered without - * actually converting to an array. - * - * In some cases, such as structured arrays and the __array__ interface, - * a data type needs to be used to make sense of the object. When - * this is needed, provide a Descr for 'requested_dtype', otherwise - * provide NULL. This reference is not stolen. Also, if the requested - * dtype doesn't modify the interpretation of the input, out_dtype will - * still get the "innate" dtype of the object, not the dtype passed - * in 'requested_dtype'. - * - * If writing to the value in 'op' is desired, set the boolean - * 'writeable' to 1. This raises an error when 'op' is a scalar, list - * of lists, or other non-writeable 'op'. + +/** + * Attempts to extract an array from an array-like object. * - * Result: When success (0 return value) is returned, either out_arr - * is filled with a non-NULL PyArrayObject and - * the rest of the parameters are untouched, or out_arr is - * filled with NULL, and the rest of the parameters are - * filled. + * array-like is defined as either * - * Typical usage: + * * an object implementing the PEP 3118 buffer interface; + * * an object with __array_struct__ or __array_interface__ attributes; + * * an object with an __array__ function. * - * PyArrayObject *arr = NULL; - * PyArray_Descr *dtype = NULL; - * int ndim = 0; - * npy_intp dims[NPY_MAXDIMS]; + * @param op The object to convert to an array + * @param requested_type a requested dtype instance, may be NULL; The result + * DType may be used, but is not enforced. + * @param writeable whether the result must be writeable. + * @param context Unused parameter, must be NULL (should be removed later). + * @param never_copy Specifies that a copy is not allowed. * - * if (PyArray_GetArrayParamsFromObject(op, NULL, 1, &dtype, - * &ndim, dims, &arr, NULL) < 0) { - * return NULL; - * } - * if (arr == NULL) { - * ... validate/change dtype, validate flags, ndim, etc ... - * // Could make custom strides here too - * arr = PyArray_NewFromDescr(&PyArray_Type, dtype, ndim, - * dims, NULL, - * is_f_order ? NPY_ARRAY_F_CONTIGUOUS : 0, - * NULL); - * if (arr == NULL) { - * return NULL; - * } - * if (PyArray_CopyObject(arr, op) < 0) { - * Py_DECREF(arr); - * return NULL; - * } - * } - * else { - * ... in this case the other parameters weren't filled, just - * validate and possibly copy arr itself ... - * } - * ... use arr ... + * @returns The array object, Py_NotImplemented if op is not array-like, + * or NULL with an error set. (A new reference to Py_NotImplemented + * is returned.) */ -NPY_NO_EXPORT int -PyArray_GetArrayParamsFromObject(PyObject *op, - PyArray_Descr *requested_dtype, - npy_bool writeable, - PyArray_Descr **out_dtype, - int *out_ndim, npy_intp *out_dims, - PyArrayObject **out_arr, PyObject *context) -{ - PyObject *tmp; - - /* If op is an array */ - if (PyArray_Check(op)) { - if (writeable - && PyArray_FailUnlessWriteable((PyArrayObject *)op, "array") < 0) { - return -1; - } - Py_INCREF(op); - *out_arr = (PyArrayObject *)op; - return 0; - } - - /* If op is a NumPy scalar */ - if (PyArray_IsScalar(op, Generic)) { - if (writeable) { - PyErr_SetString(PyExc_RuntimeError, - "cannot write to scalar"); - return -1; - } - *out_dtype = PyArray_DescrFromScalar(op); - if (*out_dtype == NULL) { - return -1; - } - *out_ndim = 0; - *out_arr = NULL; - return 0; - } - - /* If op is a Python scalar */ - *out_dtype = _array_find_python_scalar_type(op); - if (*out_dtype != NULL) { - if (writeable) { - PyErr_SetString(PyExc_RuntimeError, - "cannot write to scalar"); - Py_DECREF(*out_dtype); - return -1; - } - *out_ndim = 0; - *out_arr = NULL; - return 0; - } - - /* If op supports the PEP 3118 buffer interface */ - if (!PyBytes_Check(op) && !PyUnicode_Check(op)) { +NPY_NO_EXPORT PyObject * +_array_from_array_like(PyObject *op, + PyArray_Descr *requested_dtype, npy_bool writeable, PyObject *context, + int never_copy) { + PyObject* tmp; + /* + * If op supports the PEP 3118 buffer interface. + * We skip bytes and unicode since they are considered scalars. Unicode + * would fail but bytes would be incorrectly converted to a uint8 array. + */ + if (PyObject_CheckBuffer(op) && !PyBytes_Check(op) && !PyUnicode_Check(op)) { PyObject *memoryview = PyMemoryView_FromObject(op); if (memoryview == NULL) { + /* TODO: Should probably not blanket ignore errors. */ PyErr_Clear(); } else { - PyObject *arr = _array_from_buffer_3118(memoryview); + tmp = _array_from_buffer_3118(memoryview); Py_DECREF(memoryview); - if (arr == NULL) { - return -1; + if (tmp == NULL) { + return NULL; } + if (writeable - && PyArray_FailUnlessWriteable((PyArrayObject *)arr, "PEP 3118 buffer") < 0) { - Py_DECREF(arr); - return -1; + && PyArray_FailUnlessWriteable( + (PyArrayObject *)tmp, "PEP 3118 buffer") < 0) { + Py_DECREF(tmp); + return NULL; } - *out_arr = (PyArrayObject *)arr; - return 0; + + return tmp; } } - /* If op supports the __array_struct__ or __array_interface__ interface */ + /* + * If op supports the __array_struct__ or __array_interface__ interface. + */ tmp = PyArray_FromStructInterface(op); if (tmp == NULL) { - return -1; + return NULL; } if (tmp == Py_NotImplemented) { + /* Until the return, NotImplemented is always a borrowed reference*/ tmp = PyArray_FromInterface(op); if (tmp == NULL) { - return -1; - } - } - if (tmp != Py_NotImplemented) { - if (writeable - && PyArray_FailUnlessWriteable((PyArrayObject *)tmp, - "array interface object") < 0) { - Py_DECREF(tmp); - return -1; + return NULL; } - *out_arr = (PyArrayObject *)tmp; - return (*out_arr) == NULL ? -1 : 0; } /* @@ -1643,149 +1355,202 @@ PyArray_GetArrayParamsFromObject(PyObject *op, * usage requires this behave differently, * this should be changed! */ - if (!writeable) { - tmp = PyArray_FromArrayAttr(op, requested_dtype, context); - if (tmp != Py_NotImplemented) { - *out_arr = (PyArrayObject *)tmp; - return (*out_arr) == NULL ? -1 : 0; + if (!writeable && tmp == Py_NotImplemented) { + tmp = PyArray_FromArrayAttr_int(op, requested_dtype, never_copy); + if (tmp == NULL) { + return NULL; } } - /* Try to treat op as a list of lists */ - if (!writeable && PySequence_Check(op)) { - int check_it, stop_at_string, stop_at_tuple, is_object; - int type_num, type; - - /* - * Determine the type, using the requested data type if - * it will affect how the array is retrieved - */ - if (requested_dtype != NULL && ( - requested_dtype->type_num == NPY_STRING || - requested_dtype->type_num == NPY_UNICODE || - (requested_dtype->type_num == NPY_VOID && - (requested_dtype->names || requested_dtype->subarray)) || - requested_dtype->type == NPY_CHARLTR || - requested_dtype->type_num == NPY_OBJECT)) { - Py_INCREF(requested_dtype); - *out_dtype = requested_dtype; - } - else { - *out_dtype = NULL; - if (PyArray_DTypeFromObject(op, NPY_MAXDIMS, out_dtype) < 0) { - if (PyErr_ExceptionMatches(PyExc_MemoryError)) { - return -1; - } - /* Return NPY_OBJECT for most exceptions */ - else { - PyErr_Clear(); - *out_dtype = PyArray_DescrFromType(NPY_OBJECT); - if (*out_dtype == NULL) { - return -1; - } - } - } - if (*out_dtype == NULL) { - *out_dtype = PyArray_DescrFromType(NPY_DEFAULT_TYPE); - if (*out_dtype == NULL) { - return -1; - } - } + if (tmp != Py_NotImplemented) { + if (writeable && + PyArray_FailUnlessWriteable((PyArrayObject *)tmp, + "array interface object") < 0) { + Py_DECREF(tmp); + return NULL; } + return tmp; + } - type_num = (*out_dtype)->type_num; - type = (*out_dtype)->type; + /* Until here Py_NotImplemented was borrowed */ + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; +} - check_it = (type != NPY_CHARLTR); - stop_at_string = (type_num != NPY_STRING) || - (type == NPY_STRINGLTR); - stop_at_tuple = (type_num == NPY_VOID && - ((*out_dtype)->names || (*out_dtype)->subarray)); - *out_ndim = NPY_MAXDIMS; - is_object = 0; - if (discover_dimensions(op, out_ndim, out_dims, check_it, - stop_at_string, stop_at_tuple, - &is_object) < 0) { - Py_DECREF(*out_dtype); - if (PyErr_Occurred()) { - return -1; - } - *out_dtype = PyArray_DescrFromType(NPY_OBJECT); - if (*out_dtype == NULL) { - return -1; - } - *out_ndim = 0; - *out_arr = NULL; - return 0; - } - /* If object arrays are forced */ - if (is_object) { - Py_DECREF(*out_dtype); - *out_dtype = PyArray_DescrFromType(NPY_OBJECT); - if (*out_dtype == NULL) { - return -1; +/*NUMPY_API*/ +NPY_NO_EXPORT int +PyArray_GetArrayParamsFromObject(PyObject *NPY_UNUSED(op), + PyArray_Descr *NPY_UNUSED(requested_dtype), + npy_bool NPY_UNUSED(writeable), + PyArray_Descr **NPY_UNUSED(out_dtype), + int *NPY_UNUSED(out_ndim), npy_intp *NPY_UNUSED(out_dims), + PyArrayObject **NPY_UNUSED(out_arr), PyObject *NPY_UNUSED(context)) +{ + /* Deprecated in NumPy 1.19, removed in NumPy 1.20. */ + PyErr_SetString(PyExc_RuntimeError, + "PyArray_GetArrayParamsFromObject() C-API function is removed " + "`PyArray_FromAny()` should be used at this time. New C-API " + "may be exposed in the future (please do request this if it " + "would help you)."); + return -1; +} + + +/* + * This function is a legacy implementation to retain subarray dtype + * behaviour in array coercion. The behaviour here makes sense if tuples + * of matching dimensionality are being coerced. Due to the difficulty + * that the result is ill-defined for lists of array-likes, this is deprecated. + * + * WARNING: Do not use this function, it exists purely to support a deprecated + * code path. + */ +static int +setArrayFromSequence(PyArrayObject *a, PyObject *s, + int dim, PyArrayObject * dst) +{ + Py_ssize_t i, slen; + int res = -1; + + /* first recursion, view equal destination */ + if (dst == NULL) + dst = a; + + /* + * This code is to ensure that the sequence access below will + * return a lower-dimensional sequence. + */ + + /* INCREF on entry DECREF on exit */ + Py_INCREF(s); + + PyObject *seq = NULL; + + if (PyArray_Check(s)) { + if (!(PyArray_CheckExact(s))) { + /* + * make sure a base-class array is used so that the dimensionality + * reduction assumption is correct. + */ + /* This will DECREF(s) if replaced */ + s = PyArray_EnsureArray(s); + if (s == NULL) { + goto fail; } } - if ((*out_dtype)->type == NPY_CHARLTR && (*out_ndim) > 0 && - out_dims[(*out_ndim) - 1] == 1) { - (*out_ndim) -= 1; + /* dst points to correct array subsection */ + if (PyArray_CopyInto(dst, (PyArrayObject *)s) < 0) { + goto fail; } - /* If the type is flexible, determine its size */ - if (PyDataType_ISUNSIZED(*out_dtype) && - PyTypeNum_ISEXTENDED((*out_dtype)->type_num)) { - int itemsize = 0; - int string_type = 0; - if ((*out_dtype)->type_num == NPY_STRING || - (*out_dtype)->type_num == NPY_UNICODE) { - string_type = (*out_dtype)->type_num; - } - if (discover_itemsize(op, *out_ndim, &itemsize, string_type) < 0) { - Py_DECREF(*out_dtype); - if (PyErr_Occurred() && - PyErr_GivenExceptionMatches(PyErr_Occurred(), - PyExc_MemoryError)) { - return -1; + Py_DECREF(s); + return 0; + } + + if (dim > PyArray_NDIM(a)) { + PyErr_Format(PyExc_ValueError, + "setArrayFromSequence: sequence/array dimensions mismatch."); + goto fail; + } + + /* Try __array__ before using s as a sequence */ + PyObject *tmp = _array_from_array_like(s, NULL, 0, NULL, 0); + if (tmp == NULL) { + goto fail; + } + else if (tmp == Py_NotImplemented) { + Py_DECREF(tmp); + } + else { + int r = PyArray_CopyInto(dst, (PyArrayObject *)tmp); + Py_DECREF(tmp); + if (r < 0) { + goto fail; + } + Py_DECREF(s); + return 0; + } + + seq = PySequence_Fast(s, "Could not convert object to sequence"); + if (seq == NULL) { + goto fail; + } + slen = PySequence_Fast_GET_SIZE(seq); + + /* + * Either the dimensions match, or the sequence has length 1 and can + * be broadcast to the destination. + */ + if (slen != PyArray_DIMS(a)[dim] && slen != 1) { + PyErr_Format(PyExc_ValueError, + "cannot copy sequence with size %zd to array axis " + "with dimension %" NPY_INTP_FMT, slen, PyArray_DIMS(a)[dim]); + goto fail; + } + + /* Broadcast the one element from the sequence to all the outputs */ + if (slen == 1) { + PyObject *o = PySequence_Fast_GET_ITEM(seq, 0); + npy_intp alen = PyArray_DIM(a, dim); + + for (i = 0; i < alen; i++) { + if ((PyArray_NDIM(a) - dim) > 1) { + PyArrayObject * tmp = + (PyArrayObject *)array_item_asarray(dst, i); + if (tmp == NULL) { + goto fail; } - /* Say it's an OBJECT scalar if there's an error */ - PyErr_Clear(); - *out_dtype = PyArray_DescrFromType(NPY_OBJECT); - *out_ndim = 0; - *out_arr = NULL; - return 0; + + res = setArrayFromSequence(a, o, dim+1, tmp); + Py_DECREF(tmp); } - if ((*out_dtype)->type_num == NPY_UNICODE) { - itemsize *= 4; + else { + char * b = (PyArray_BYTES(dst) + i * PyArray_STRIDES(dst)[0]); + res = PyArray_SETITEM(dst, b, o); } - - if (itemsize != (*out_dtype)->elsize) { - PyArray_DESCR_REPLACE(*out_dtype); - (*out_dtype)->elsize = itemsize; + if (res < 0) { + goto fail; } } - - *out_arr = NULL; - return 0; } + /* Copy element by element */ + else { + for (i = 0; i < slen; i++) { + PyObject * o = PySequence_Fast_GET_ITEM(seq, i); + if ((PyArray_NDIM(a) - dim) > 1) { + PyArrayObject * tmp = + (PyArrayObject *)array_item_asarray(dst, i); + if (tmp == NULL) { + goto fail; + } - /* Anything can be viewed as an object, unless it needs to be writeable */ - if (!writeable) { - *out_dtype = PyArray_DescrFromType(NPY_OBJECT); - if (*out_dtype == NULL) { - return -1; + res = setArrayFromSequence(a, o, dim+1, tmp); + Py_DECREF(tmp); + } + else { + char * b = (PyArray_BYTES(dst) + i * PyArray_STRIDES(dst)[0]); + res = PyArray_SETITEM(dst, b, o); + } + if (res < 0) { + goto fail; + } } - *out_ndim = 0; - *out_arr = NULL; - return 0; } - PyErr_SetString(PyExc_RuntimeError, - "object cannot be viewed as a writeable numpy array"); - return -1; + Py_DECREF(seq); + Py_DECREF(s); + return 0; + + fail: + Py_XDECREF(seq); + Py_DECREF(s); + return res; } + + /*NUMPY_API * Does not check for NPY_ARRAY_ENSURECOPY and NPY_ARRAY_NOTSWAPPED in flags * Steals a reference to newtype --- which can be NULL @@ -1800,122 +1565,249 @@ PyArray_FromAny(PyObject *op, PyArray_Descr *newtype, int min_depth, */ PyArrayObject *arr = NULL, *ret; PyArray_Descr *dtype = NULL; + coercion_cache_obj *cache = NULL; int ndim = 0; npy_intp dims[NPY_MAXDIMS]; - /* Get either the array or its parameters if it isn't an array */ - if (PyArray_GetArrayParamsFromObject(op, newtype, - 0, &dtype, - &ndim, dims, &arr, context) < 0) { + if (context != NULL) { + PyErr_SetString(PyExc_RuntimeError, "'context' must be NULL"); + return NULL; + } + + PyArray_Descr *fixed_descriptor; + PyArray_DTypeMeta *fixed_DType; + if (PyArray_ExtractDTypeAndDescriptor((PyObject *)newtype, + &fixed_descriptor, &fixed_DType) < 0) { Py_XDECREF(newtype); return NULL; } + Py_XDECREF(newtype); - /* If the requested dtype is flexible, adapt it */ - if (newtype != NULL) { - PyArray_AdaptFlexibleDType(op, - (dtype == NULL) ? PyArray_DESCR(arr) : dtype, - &newtype); + ndim = PyArray_DiscoverDTypeAndShape(op, + NPY_MAXDIMS, dims, &cache, fixed_DType, fixed_descriptor, &dtype, + flags & NPY_ARRAY_ENSURENOCOPY); + + Py_XDECREF(fixed_descriptor); + Py_XDECREF(fixed_DType); + if (ndim < 0) { + return NULL; } - /* If we got dimensions and dtype instead of an array */ - if (arr == NULL) { - if ((flags & NPY_ARRAY_WRITEBACKIFCOPY) || - (flags & NPY_ARRAY_UPDATEIFCOPY)) { - Py_XDECREF(newtype); - PyErr_SetString(PyExc_TypeError, - "WRITEBACKIFCOPY used for non-array input."); - return NULL; - } - else if (min_depth != 0 && ndim < min_depth) { - Py_DECREF(dtype); - Py_XDECREF(newtype); - PyErr_SetString(PyExc_ValueError, - "object of too small depth for desired array"); - ret = NULL; - } - else if (max_depth != 0 && ndim > max_depth) { - Py_DECREF(dtype); - Py_XDECREF(newtype); - PyErr_SetString(PyExc_ValueError, - "object too deep for desired array"); - ret = NULL; - } - else if (ndim == 0 && PyArray_IsScalar(op, Generic)) { - ret = (PyArrayObject *)PyArray_FromScalar(op, newtype); - Py_DECREF(dtype); - } - else { - if (newtype == NULL) { - newtype = dtype; - } - else { - /* - * TODO: would be nice to do this too, but it's - * a behavior change. It's also a bit tricky - * for downcasting to small integer and float - * types, and might be better to modify - * PyArray_AssignFromSequence and descr->f->setitem - * to have a 'casting' parameter and - * to check each value with scalar rules like - * in PyArray_MinScalarType. - */ - /* - if (!(flags&NPY_ARRAY_FORCECAST) && ndim > 0 && - !PyArray_CanCastTo(dtype, newtype)) { - Py_DECREF(dtype); - Py_XDECREF(newtype); - PyErr_SetString(PyExc_TypeError, - "object cannot be safely cast to array " - "of required type"); - return NULL; + if (NPY_UNLIKELY(fixed_descriptor != NULL && PyDataType_HASSUBARRAY(dtype))) { + /* + * When a subarray dtype was passed in, its dimensions are appended + * to the array dimension (causing a dimension mismatch). + * There is a problem with that, because if we coerce from non-arrays + * we do this correctly by element (as defined by tuples), but for + * arrays we first append the dimensions and then assign to the base + * dtype and then assign which causes the problem. + * + * Thus, we check if there is an array included, in that case we + * give a FutureWarning. + * When the warning is removed, PyArray_Pack will have to ensure + * that that it does not append the dimensions when creating the + * subarrays to assign `arr[0] = obj[0]`. + */ + int includes_array = 0; + if (cache != NULL) { + /* This is not ideal, but it is a pretty special case */ + coercion_cache_obj *next = cache; + while (next != NULL) { + if (!next->sequence) { + includes_array = 1; + break; } - */ - Py_DECREF(dtype); + next = next->next; } + } + if (includes_array) { + npy_free_coercion_cache(cache); - /* Create an array and copy the data */ - ret = (PyArrayObject *)PyArray_NewFromDescr(&PyArray_Type, newtype, - ndim, dims, - NULL, NULL, - flags&NPY_ARRAY_F_CONTIGUOUS, NULL); + ret = (PyArrayObject *) PyArray_NewFromDescr( + &PyArray_Type, dtype, ndim, dims, NULL, NULL, + flags & NPY_ARRAY_F_CONTIGUOUS, NULL); if (ret == NULL) { return NULL; } - - if (ndim > 0) { - if (PyArray_AssignFromSequence(ret, op) < 0) { - Py_DECREF(ret); - ret = NULL; - } + assert(PyArray_NDIM(ret) != ndim); + + /* NumPy 1.20, 2020-10-01 */ + if (DEPRECATE_FUTUREWARNING( + "creating an array with a subarray dtype will behave " + "differently when the `np.array()` (or `asarray`, etc.) " + "call includes an array or array object.\n" + "If you are converting a single array or a list of arrays," + "you can opt-in to the future behaviour using:\n" + " np.array(arr, dtype=np.dtype(['f', dtype]))['f']\n" + " np.array([arr1, arr2], dtype=np.dtype(['f', dtype]))['f']\n" + "\n" + "By including a new field and indexing it after the " + "conversion.\n" + "This may lead to a different result or to current failures " + "succeeding. (FutureWarning since NumPy 1.20)") < 0) { + Py_DECREF(ret); + return NULL; } - else { - if (PyArray_SETITEM(ret, PyArray_DATA(ret), op) < 0) { - Py_DECREF(ret); - ret = NULL; - } + + if (setArrayFromSequence(ret, op, 0, NULL) < 0) { + Py_DECREF(ret); + return NULL; } + return (PyObject *)ret; } } - else { - if (min_depth != 0 && PyArray_NDIM(arr) < min_depth) { - PyErr_SetString(PyExc_ValueError, - "object of too small depth for desired array"); - Py_DECREF(arr); - ret = NULL; - } - else if (max_depth != 0 && PyArray_NDIM(arr) > max_depth) { - PyErr_SetString(PyExc_ValueError, - "object too deep for desired array"); - Py_DECREF(arr); - ret = NULL; - } - else { - ret = (PyArrayObject *)PyArray_FromArray(arr, newtype, flags); - Py_DECREF(arr); + + if (dtype == NULL) { + dtype = PyArray_DescrFromType(NPY_DEFAULT_TYPE); + } + + if (min_depth != 0 && ndim < min_depth) { + PyErr_SetString(PyExc_ValueError, + "object of too small depth for desired array"); + Py_DECREF(dtype); + npy_free_coercion_cache(cache); + return NULL; + } + if (max_depth != 0 && ndim > max_depth) { + PyErr_SetString(PyExc_ValueError, + "object too deep for desired array"); + Py_DECREF(dtype); + npy_free_coercion_cache(cache); + return NULL; + } + + /* Got the correct parameters, but the cache may already hold the result */ + if (cache != NULL && !(cache->sequence)) { + /* + * There is only a single array-like and it was converted, it + * may still have the incorrect type, but that is handled below. + */ + assert(cache->converted_obj == op); + arr = (PyArrayObject *)(cache->arr_or_sequence); + /* we may need to cast or assert flags (e.g. copy) */ + PyObject *res = PyArray_FromArray(arr, dtype, flags); + npy_unlink_coercion_cache(cache); + return res; + } + else if (cache == NULL && PyArray_IsScalar(op, Void) && + !(((PyVoidScalarObject *)op)->flags & NPY_ARRAY_OWNDATA) && + newtype == NULL) { + /* + * Special case, we return a *view* into void scalars, mainly to + * allow things similar to the "reversed" assignment: + * arr[indx]["field"] = val # instead of arr["field"][indx] = val + * + * It is unclear that this is necessary in this particular code path. + * Note that this path is only activated when the user did _not_ + * provide a dtype (newtype is NULL). + */ + assert(ndim == 0); + + return PyArray_NewFromDescrAndBase( + &PyArray_Type, dtype, + 0, NULL, NULL, + ((PyVoidScalarObject *)op)->obval, + ((PyVoidScalarObject *)op)->flags, + NULL, op); + } + /* + * If we got this far, we definitely have to create a copy, since we are + * converting either from a scalar (cache == NULL) or a (nested) sequence. + */ + if (flags & NPY_ARRAY_ENSURENOCOPY ) { + PyErr_SetString(PyExc_ValueError, + "Unable to avoid copy while creating an array."); + Py_DECREF(dtype); + npy_free_coercion_cache(cache); + return NULL; + } + + if (cache == NULL && newtype != NULL && + PyDataType_ISSIGNED(newtype) && PyArray_IsScalar(op, Generic)) { + assert(ndim == 0); + /* + * This is an (possible) inconsistency where: + * + * np.array(np.float64(np.nan), dtype=np.int64) + * + * behaves differently from: + * + * np.array([np.float64(np.nan)], dtype=np.int64) + * arr1d_int64[0] = np.float64(np.nan) + * np.array(np.array(np.nan), dtype=np.int64) + * + * by not raising an error instead of using typical casting. + * The error is desirable, but to always error seems like a + * larger change to be considered at some other time and it is + * undesirable that 0-D arrays behave differently from scalars. + * This retains the behaviour, largely due to issues in pandas + * which relied on a try/except (although hopefully that will + * have a better solution at some point): + * https://github.com/pandas-dev/pandas/issues/35481 + */ + return PyArray_FromScalar(op, dtype); + } + + /* There was no array (or array-like) passed in directly. */ + if ((flags & NPY_ARRAY_WRITEBACKIFCOPY) || + (flags & NPY_ARRAY_UPDATEIFCOPY)) { + PyErr_SetString(PyExc_TypeError, + "WRITEBACKIFCOPY used for non-array input."); + Py_DECREF(dtype); + npy_free_coercion_cache(cache); + return NULL; + } + + /* Create a new array and copy the data */ + Py_INCREF(dtype); /* hold on in case of a subarray that is replaced */ + ret = (PyArrayObject *)PyArray_NewFromDescr( + &PyArray_Type, dtype, ndim, dims, NULL, NULL, + flags&NPY_ARRAY_F_CONTIGUOUS, NULL); + if (ret == NULL) { + npy_free_coercion_cache(cache); + Py_DECREF(dtype); + return NULL; + } + if (ndim == PyArray_NDIM(ret)) { + /* + * Appending of dimensions did not occur, so use the actual dtype + * below. This is relevant for S0 or U0 which can be replaced with + * S1 or U1, although that should likely change. + */ + Py_SETREF(dtype, PyArray_DESCR(ret)); + Py_INCREF(dtype); + } + + if (cache == NULL) { + /* This is a single item. Set it directly. */ + assert(ndim == 0); + + if (PyArray_Pack(dtype, PyArray_BYTES(ret), op) < 0) { + Py_DECREF(dtype); + Py_DECREF(ret); + return NULL; } + Py_DECREF(dtype); + return (PyObject *)ret; } + assert(ndim != 0); + assert(op == cache->converted_obj); + /* Decrease the number of dimensions to the detected ones */ + int out_ndim = PyArray_NDIM(ret); + PyArray_Descr *out_descr = PyArray_DESCR(ret); + ((PyArrayObject_fields *)ret)->nd = ndim; + ((PyArrayObject_fields *)ret)->descr = dtype; + + int success = PyArray_AssignFromCache(ret, cache); + + ((PyArrayObject_fields *)ret)->nd = out_ndim; + ((PyArrayObject_fields *)ret)->descr = out_descr; + Py_DECREF(dtype); + if (success < 0) { + Py_DECREF(ret); + return NULL; + } return (PyObject *)ret; } @@ -1931,7 +1823,8 @@ PyArray_FromAny(PyObject *op, PyArray_Descr *newtype, int min_depth, * NPY_ARRAY_WRITEBACKIFCOPY, * NPY_ARRAY_FORCECAST, * NPY_ARRAY_ENSUREARRAY, - * NPY_ARRAY_ELEMENTSTRIDES + * NPY_ARRAY_ELEMENTSTRIDES, + * NPY_ARRAY_ENSURENOCOPY * * or'd (|) together * @@ -1963,6 +1856,8 @@ PyArray_FromAny(PyObject *op, PyArray_Descr *newtype, int min_depth, * * NPY_ARRAY_FORCECAST will cause a cast to occur regardless of whether or not * it is safe. + * + * context is passed through to PyArray_GetArrayParamsFromObject */ /*NUMPY_API @@ -1990,9 +1885,15 @@ PyArray_CheckFromAny(PyObject *op, PyArray_Descr *descr, int min_depth, if (obj == NULL) { return NULL; } - if ((requires & NPY_ARRAY_ELEMENTSTRIDES) && - !PyArray_ElementStrides(obj)) { + + if ((requires & NPY_ARRAY_ELEMENTSTRIDES) + && !PyArray_ElementStrides(obj)) { PyObject *ret; + if (requires & NPY_ARRAY_ENSURENOCOPY) { + PyErr_SetString(PyExc_ValueError, + "Unable to avoid copy while creating a new array."); + return NULL; + } ret = PyArray_NewCopy((PyArrayObject *)obj, NPY_ANYORDER); Py_DECREF(obj); obj = ret; @@ -2000,6 +1901,7 @@ PyArray_CheckFromAny(PyObject *op, PyArray_Descr *descr, int min_depth, return obj; } + /*NUMPY_API * steals reference to newtype --- acc. NULL */ @@ -2026,7 +1928,7 @@ PyArray_FromArray(PyArrayObject *arr, PyArray_Descr *newtype, int flags) newtype = oldtype; Py_INCREF(oldtype); } - if (PyDataType_ISUNSIZED(newtype)) { + else if (PyDataType_ISUNSIZED(newtype)) { PyArray_DESCR_REPLACE(newtype); if (newtype == NULL) { return NULL; @@ -2041,41 +1943,9 @@ PyArray_FromArray(PyArrayObject *arr, PyArray_Descr *newtype, int flags) /* Raise an error if the casting rule isn't followed */ if (!PyArray_CanCastArrayTo(arr, newtype, casting)) { - PyObject *errmsg; - PyArray_Descr *arr_descr = NULL; - PyObject *arr_descr_repr = NULL; - PyObject *newtype_repr = NULL; - PyErr_Clear(); - errmsg = PyUString_FromString("Cannot cast array data from "); - arr_descr = PyArray_DESCR(arr); - if (arr_descr == NULL) { - Py_DECREF(newtype); - Py_DECREF(errmsg); - return NULL; - } - arr_descr_repr = PyObject_Repr((PyObject *)arr_descr); - if (arr_descr_repr == NULL) { - Py_DECREF(newtype); - Py_DECREF(errmsg); - return NULL; - } - PyUString_ConcatAndDel(&errmsg, arr_descr_repr); - PyUString_ConcatAndDel(&errmsg, - PyUString_FromString(" to ")); - newtype_repr = PyObject_Repr((PyObject *)newtype); - if (newtype_repr == NULL) { - Py_DECREF(newtype); - Py_DECREF(errmsg); - return NULL; - } - PyUString_ConcatAndDel(&errmsg, newtype_repr); - PyUString_ConcatAndDel(&errmsg, - PyUString_FromFormat(" according to the rule %s", - npy_casting_to_string(casting))); - PyErr_SetObject(PyExc_TypeError, errmsg); - Py_DECREF(errmsg); - + npy_set_invalid_cast_error( + PyArray_DESCR(arr), newtype, casting, PyArray_NDIM(arr) == 0); Py_DECREF(newtype); return NULL; } @@ -2098,6 +1968,13 @@ PyArray_FromArray(PyArrayObject *arr, PyArray_Descr *newtype, int flags) !PyArray_EquivTypes(oldtype, newtype); if (copy) { + if (flags & NPY_ARRAY_ENSURENOCOPY ) { + PyErr_SetString(PyExc_ValueError, + "Unable to avoid copy while creating an array from given array."); + Py_DECREF(newtype); + return NULL; + } + NPY_ORDER order = NPY_KEEPORDER; int subok = 1; @@ -2130,12 +2007,15 @@ PyArray_FromArray(PyArrayObject *arr, PyArray_Descr *newtype, int flags) */ /* 2017-Nov-10 1.14 */ - if (DEPRECATE("NPY_ARRAY_UPDATEIFCOPY, NPY_ARRAY_INOUT_ARRAY, and " - "NPY_ARRAY_INOUT_FARRAY are deprecated, use NPY_WRITEBACKIFCOPY, " - "NPY_ARRAY_INOUT_ARRAY2, or NPY_ARRAY_INOUT_FARRAY2 respectively " - "instead, and call PyArray_ResolveWritebackIfCopy before the " - "array is deallocated, i.e. before the last call to Py_DECREF.") < 0) + if (DEPRECATE( + "NPY_ARRAY_UPDATEIFCOPY, NPY_ARRAY_INOUT_ARRAY, and " + "NPY_ARRAY_INOUT_FARRAY are deprecated, use NPY_WRITEBACKIFCOPY, " + "NPY_ARRAY_INOUT_ARRAY2, or NPY_ARRAY_INOUT_FARRAY2 respectively " + "instead, and call PyArray_ResolveWritebackIfCopy before the " + "array is deallocated, i.e. before the last call to Py_DECREF.") < 0) { + Py_DECREF(ret); return NULL; + } Py_INCREF(arr); if (PyArray_SetWritebackIfCopyBase(ret, arr) < 0) { Py_DECREF(ret); @@ -2162,14 +2042,11 @@ PyArray_FromArray(PyArrayObject *arr, PyArray_Descr *newtype, int flags) Py_DECREF(newtype); if (needview) { - PyArray_Descr *dtype = PyArray_DESCR(arr); PyTypeObject *subtype = NULL; if (flags & NPY_ARRAY_ENSUREARRAY) { subtype = &PyArray_Type; } - - Py_INCREF(dtype); ret = (PyArrayObject *)PyArray_View(arr, NULL, subtype); if (ret == NULL) { return NULL; @@ -2189,20 +2066,34 @@ NPY_NO_EXPORT PyObject * PyArray_FromStructInterface(PyObject *input) { PyArray_Descr *thetype = NULL; - char buf[40]; PyArrayInterface *inter; PyObject *attr; - PyArrayObject *ret; char endian = NPY_NATBYTE; attr = PyArray_LookupSpecial_OnInstance(input, "__array_struct__"); if (attr == NULL) { - return Py_NotImplemented; + if (PyErr_Occurred()) { + return NULL; + } else { + return Py_NotImplemented; + } + } + if (!PyCapsule_CheckExact(attr)) { + if (PyType_Check(input) && PyObject_HasAttrString(attr, "__get__")) { + /* + * If the input is a class `attr` should be a property-like object. + * This cannot be interpreted as an array, but is a valid. + * (Needed due to the lookup being on the instance rather than type) + */ + Py_DECREF(attr); + return Py_NotImplemented; + } + goto fail; } - if (!NpyCapsule_Check(attr)) { + inter = PyCapsule_GetPointer(attr, NULL); + if (inter == NULL) { goto fail; } - inter = NpyCapsule_AsVoidPtr(attr); if (inter->two != 2) { goto fail; } @@ -2219,20 +2110,26 @@ PyArray_FromStructInterface(PyObject *input) } if (thetype == NULL) { - PyOS_snprintf(buf, sizeof(buf), - "%c%c%d", endian, inter->typekind, inter->itemsize); - if (!(thetype=_array_typedescr_fromstr(buf))) { + PyObject *type_str = PyUnicode_FromFormat( + "%c%c%d", endian, inter->typekind, inter->itemsize); + if (type_str == NULL) { + Py_DECREF(attr); + return NULL; + } + int ok = PyArray_DescrConverter(type_str, &thetype); + Py_DECREF(type_str); + if (ok != NPY_SUCCEED) { Py_DECREF(attr); return NULL; } } - ret = (PyArrayObject *)PyArray_NewFromDescrAndBase( + PyObject *ret = PyArray_NewFromDescrAndBase( &PyArray_Type, thetype, inter->nd, inter->shape, inter->strides, inter->data, inter->flags, NULL, input); Py_DECREF(attr); - return (PyObject *)ret; + return ret; fail: PyErr_SetString(PyExc_ValueError, "invalid __array_struct__"); @@ -2246,46 +2143,53 @@ PyArray_FromStructInterface(PyObject *input) */ NPY_NO_EXPORT int _is_default_descr(PyObject *descr, PyObject *typestr) { - PyObject *tuple, *name, *typestr2; -#if defined(NPY_PY3K) - PyObject *tmp = NULL; -#endif - int ret = 0; - if (!PyList_Check(descr) || PyList_GET_SIZE(descr) != 1) { return 0; } - tuple = PyList_GET_ITEM(descr, 0); + PyObject *tuple = PyList_GET_ITEM(descr, 0); if (!(PyTuple_Check(tuple) && PyTuple_GET_SIZE(tuple) == 2)) { return 0; } - name = PyTuple_GET_ITEM(tuple, 0); - if (!(PyUString_Check(name) && PyUString_GET_SIZE(name) == 0)) { + PyObject *name = PyTuple_GET_ITEM(tuple, 0); + if (!(PyUnicode_Check(name) && PyUnicode_GetLength(name) == 0)) { return 0; } - typestr2 = PyTuple_GET_ITEM(tuple, 1); -#if defined(NPY_PY3K) - /* Allow unicode type strings */ - if (PyUnicode_Check(typestr2)) { - tmp = PyUnicode_AsASCIIString(typestr2); - if (tmp == NULL) { - return 0; - } - typestr2 = tmp; + PyObject *typestr2 = PyTuple_GET_ITEM(tuple, 1); + return PyObject_RichCompareBool(typestr, typestr2, Py_EQ); +} + + +/* + * A helper function to transition away from ignoring errors during + * special attribute lookups during array coercion. + */ +static NPY_INLINE int +deprecated_lookup_error_clearing(PyTypeObject *type, char *attribute) +{ + PyObject *exc_type, *exc_value, *traceback; + PyErr_Fetch(&exc_type, &exc_value, &traceback); + + /* DEPRECATED 2021-05-12, NumPy 1.21. */ + int res = PyErr_WarnFormat(PyExc_DeprecationWarning, 1, + "An exception was ignored while fetching the attribute `%s` from " + "an object of type '%s'. With the exception of `AttributeError` " + "NumPy will always raise this exception in the future. Raise this " + "deprecation warning to see the original exception. " + "(Warning added NumPy 1.21)", attribute, type->tp_name); + + if (res < 0) { + npy_PyErr_ChainExceptionsCause(exc_type, exc_value, traceback); + return -1; } -#endif - if (PyBytes_Check(typestr2) && - PyObject_RichCompareBool(typestr, typestr2, Py_EQ)) { - ret = 1; + else { + /* `PyErr_Fetch` cleared the original error, delete the references */ + Py_DECREF(exc_type); + Py_XDECREF(exc_value); + Py_XDECREF(traceback); + return 0; } -#if defined(NPY_PY3K) - Py_XDECREF(tmp); -#endif - - return ret; } -#define PyIntOrLong_Check(obj) (PyInt_Check(obj) || PyLong_Check(obj)) /*NUMPY_API*/ NPY_NO_EXPORT PyObject * @@ -2297,21 +2201,38 @@ PyArray_FromInterface(PyObject *origin) PyArrayObject *ret; PyArray_Descr *dtype = NULL; char *data = NULL; -#if defined(NPY_PY3K) Py_buffer view; -#else - Py_ssize_t buffer_len; -#endif - int res, i, n; + int i, n; npy_intp dims[NPY_MAXDIMS], strides[NPY_MAXDIMS]; int dataflags = NPY_ARRAY_BEHAVED; - iface = PyArray_LookupSpecial_OnInstance(origin, - "__array_interface__"); + iface = PyArray_LookupSpecial_OnInstance(origin, "__array_interface__"); + if (iface == NULL) { + if (PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_RecursionError) || + PyErr_ExceptionMatches(PyExc_MemoryError)) { + /* RecursionError and MemoryError are considered fatal */ + return NULL; + } + if (deprecated_lookup_error_clearing( + Py_TYPE(origin), "__array_interface__") < 0) { + return NULL; + } + } return Py_NotImplemented; } if (!PyDict_Check(iface)) { + if (PyType_Check(origin) && PyObject_HasAttrString(iface, "__get__")) { + /* + * If the input is a class `iface` should be a property-like object. + * This cannot be interpreted as an array, but is a valid. + * (Needed due to the lookup being on the instance rather than type) + */ + Py_DECREF(iface); + return Py_NotImplemented; + } + Py_DECREF(iface); PyErr_SetString(PyExc_ValueError, "Invalid __array_interface__ value, must be a dict"); @@ -2319,34 +2240,25 @@ PyArray_FromInterface(PyObject *origin) } /* Get type string from interface specification */ - attr = PyDict_GetItemString(iface, "typestr"); + attr = _PyDict_GetItemStringWithError(iface, "typestr"); if (attr == NULL) { Py_DECREF(iface); - PyErr_SetString(PyExc_ValueError, - "Missing __array_interface__ typestr"); - return NULL; - } -#if defined(NPY_PY3K) - /* Allow unicode type strings */ - if (PyUnicode_Check(attr)) { - PyObject *tmp = PyUnicode_AsASCIIString(attr); - if (tmp == NULL) { - goto fail; + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_ValueError, + "Missing __array_interface__ typestr"); } - attr = tmp; - } - else { - Py_INCREF(attr); + return NULL; } -#endif - if (!PyBytes_Check(attr)) { + + /* allow bytes for backwards compatibility */ + if (!PyBytes_Check(attr) && !PyUnicode_Check(attr)) { PyErr_SetString(PyExc_TypeError, "__array_interface__ typestr must be a string"); goto fail; } + /* Get dtype from type string */ - dtype = _array_typedescr_fromstr(PyString_AS_STRING(attr)); - if (dtype == NULL) { + if (PyArray_DescrConverter(attr, &dtype) != NPY_SUCCEED) { goto fail; } @@ -2355,26 +2267,42 @@ PyArray_FromInterface(PyObject *origin) * the 'descr' attribute. */ if (dtype->type_num == NPY_VOID) { - PyObject *descr = PyDict_GetItemString(iface, "descr"); + PyObject *descr = _PyDict_GetItemStringWithError(iface, "descr"); + if (descr == NULL && PyErr_Occurred()) { + goto fail; + } PyArray_Descr *new_dtype = NULL; + if (descr != NULL) { + int is_default = _is_default_descr(descr, attr); + if (is_default < 0) { + goto fail; + } + if (!is_default) { + if (PyArray_DescrConverter2(descr, &new_dtype) != NPY_SUCCEED) { + goto fail; + } + if (new_dtype != NULL) { + Py_DECREF(dtype); + dtype = new_dtype; + } + } - if (descr != NULL && !_is_default_descr(descr, attr) && - PyArray_DescrConverter2(descr, &new_dtype) == NPY_SUCCEED && - new_dtype != NULL) { - Py_DECREF(dtype); - dtype = new_dtype; } - } -#if defined(NPY_PY3K) - Py_DECREF(attr); /* Pairs with the unicode handling above */ -#endif + } /* Get shape tuple from interface specification */ - attr = PyDict_GetItemString(iface, "shape"); + attr = _PyDict_GetItemStringWithError(iface, "shape"); if (attr == NULL) { + if (PyErr_Occurred()) { + return NULL; + } /* Shape must be specified when 'data' is specified */ - if (PyDict_GetItemString(iface, "data") != NULL) { + PyObject *data = _PyDict_GetItemStringWithError(iface, "data"); + if (data == NULL && PyErr_Occurred()) { + return NULL; + } + else if (data != NULL) { Py_DECREF(iface); PyErr_SetString(PyExc_ValueError, "Missing __array_interface__ shape"); @@ -2405,7 +2333,10 @@ PyArray_FromInterface(PyObject *origin) } /* Get data buffer from interface specification */ - attr = PyDict_GetItemString(iface, "data"); + attr = _PyDict_GetItemStringWithError(iface, "data"); + if (attr == NULL && PyErr_Occurred()){ + return NULL; + } /* Case for data access through pointer */ if (attr && PyTuple_Check(attr)) { @@ -2417,22 +2348,16 @@ PyArray_FromInterface(PyObject *origin) goto fail; } dataptr = PyTuple_GET_ITEM(attr, 0); - if (PyString_Check(dataptr)) { - res = sscanf(PyString_AsString(dataptr), - "%p", (void **)&data); - if (res < 1) { - PyErr_SetString(PyExc_TypeError, - "__array_interface__ data string cannot be converted"); + if (PyLong_Check(dataptr)) { + data = PyLong_AsVoidPtr(dataptr); + if (data == NULL && PyErr_Occurred()) { goto fail; } } - else if (PyIntOrLong_Check(dataptr)) { - data = PyLong_AsVoidPtr(dataptr); - } else { PyErr_SetString(PyExc_TypeError, "first element of __array_interface__ data tuple " - "must be integer or string."); + "must be an integer."); goto fail; } if (PyObject_IsTrue(PyTuple_GET_ITEM(attr,1))) { @@ -2449,7 +2374,6 @@ PyArray_FromInterface(PyObject *origin) else { base = origin; } -#if defined(NPY_PY3K) if (PyObject_GetBuffer(base, &view, PyBUF_WRITABLE|PyBUF_SIMPLE) < 0) { PyErr_Clear(); @@ -2467,21 +2391,13 @@ PyArray_FromInterface(PyObject *origin) * sticks around after the release. */ PyBuffer_Release(&view); -#else - res = PyObject_AsWriteBuffer(base, (void **)&data, &buffer_len); - if (res < 0) { - PyErr_Clear(); - res = PyObject_AsReadBuffer( - base, (const void **)&data, &buffer_len); - if (res < 0) { - goto fail; - } - dataflags &= ~NPY_ARRAY_WRITEABLE; - } -#endif + /* Get offset number from interface specification */ - attr = PyDict_GetItemString(origin, "offset"); - if (attr) { + attr = _PyDict_GetItemStringWithError(iface, "offset"); + if (attr == NULL && PyErr_Occurred()) { + goto fail; + } + else if (attr) { npy_longlong num = PyLong_AsLongLong(attr); if (error_converting(num)) { PyErr_SetString(PyExc_TypeError, @@ -2496,6 +2412,11 @@ PyArray_FromInterface(PyObject *origin) &PyArray_Type, dtype, n, dims, NULL, data, dataflags, NULL, base); + /* + * Ref to dtype was stolen by PyArray_NewFromDescrAndBase + * Prevent DECREFing dtype in fail codepath by setting to NULL + */ + dtype = NULL; if (ret == NULL) { goto fail; } @@ -2511,7 +2432,10 @@ PyArray_FromInterface(PyObject *origin) goto fail; } } - attr = PyDict_GetItemString(iface, "strides"); + attr = _PyDict_GetItemStringWithError(iface, "strides"); + if (attr == NULL && PyErr_Occurred()){ + return NULL; + } if (attr != NULL && attr != Py_None) { if (!PyTuple_Check(attr)) { PyErr_SetString(PyExc_TypeError, @@ -2533,7 +2457,9 @@ PyArray_FromInterface(PyObject *origin) goto fail; } } - memcpy(PyArray_STRIDES(ret), strides, n*sizeof(npy_intp)); + if (n) { + memcpy(PyArray_STRIDES(ret), strides, n*sizeof(npy_intp)); + } } PyArray_UpdateFlags(ret, NPY_ARRAY_UPDATE_ALL); Py_DECREF(iface); @@ -2545,40 +2471,70 @@ PyArray_FromInterface(PyObject *origin) return NULL; } -/*NUMPY_API*/ + +/** + * Check for an __array__ attribute and call it when it exists. + * + * .. warning: + * If returned, `NotImplemented` is borrowed and must not be Decref'd + * + * @param op The Python object to convert to an array. + * @param descr The desired `arr.dtype`, passed into the `__array__` call, + * as information but is not checked/enforced! + * @param never_copy Specifies that a copy is not allowed. + * NOTE: Currently, this means an error is raised instead of calling + * `op.__array__()`. In the future we could call for example call + * `op.__array__(never_copy=True)` instead. + * @returns NotImplemented if `__array__` is not defined or a NumPy array + * (or subclass). On error, return NULL. + */ NPY_NO_EXPORT PyObject * -PyArray_FromArrayAttr(PyObject *op, PyArray_Descr *typecode, PyObject *context) +PyArray_FromArrayAttr_int( + PyObject *op, PyArray_Descr *descr, int never_copy) { PyObject *new; PyObject *array_meth; array_meth = PyArray_LookupSpecial_OnInstance(op, "__array__"); if (array_meth == NULL) { + if (PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_RecursionError) || + PyErr_ExceptionMatches(PyExc_MemoryError)) { + /* RecursionError and MemoryError are considered fatal */ + return NULL; + } + if (deprecated_lookup_error_clearing( + Py_TYPE(op), "__array__") < 0) { + return NULL; + } + } return Py_NotImplemented; } - if (context == NULL) { - if (typecode == NULL) { - new = PyObject_CallFunction(array_meth, NULL); - } - else { - new = PyObject_CallFunction(array_meth, "O", typecode); - } + if (never_copy) { + /* Currently, we must always assume that `__array__` returns a copy */ + PyErr_SetString(PyExc_ValueError, + "Unable to avoid copy while converting from an object " + "implementing the `__array__` protocol. NumPy cannot ensure " + "that no copy will be made."); + Py_DECREF(array_meth); + return NULL; + } + + if (PyType_Check(op) && PyObject_HasAttrString(array_meth, "__get__")) { + /* + * If the input is a class `array_meth` may be a property-like object. + * This cannot be interpreted as an array (called), but is a valid. + * Trying `array_meth.__call__()` on this should not be useful. + * (Needed due to the lookup being on the instance rather than type) + */ + Py_DECREF(array_meth); + return Py_NotImplemented; + } + if (descr == NULL) { + new = PyObject_CallFunction(array_meth, NULL); } else { - if (typecode == NULL) { - new = PyObject_CallFunction(array_meth, "OO", Py_None, context); - if (new == NULL && PyErr_ExceptionMatches(PyExc_TypeError)) { - PyErr_Clear(); - new = PyObject_CallFunction(array_meth, ""); - } - } - else { - new = PyObject_CallFunction(array_meth, "OO", typecode, context); - if (new == NULL && PyErr_ExceptionMatches(PyExc_TypeError)) { - PyErr_Clear(); - new = PyObject_CallFunction(array_meth, "O", typecode); - } - } + new = PyObject_CallFunction(array_meth, "O", descr); } Py_DECREF(array_meth); if (new == NULL) { @@ -2594,6 +2550,21 @@ PyArray_FromArrayAttr(PyObject *op, PyArray_Descr *typecode, PyObject *context) return new; } + +/*NUMPY_API + */ +NPY_NO_EXPORT PyObject * +PyArray_FromArrayAttr(PyObject *op, PyArray_Descr *typecode, PyObject *context) +{ + if (context != NULL) { + PyErr_SetString(PyExc_RuntimeError, "'context' must be NULL"); + return NULL; + } + + return PyArray_FromArrayAttr_int(op, typecode, 0); +} + + /*NUMPY_API * new reference -- accepts NULL for mintype */ @@ -2622,61 +2593,30 @@ PyArray_DescrFromObject(PyObject *op, PyArray_Descr *mintype) /* They all zero-out the memory as previously done */ /* steals reference to descr -- and enforces native byteorder on it.*/ + /*NUMPY_API - Like FromDimsAndData but uses the Descr structure instead of typecode - as input. + Deprecated, use PyArray_NewFromDescr instead. */ NPY_NO_EXPORT PyObject * -PyArray_FromDimsAndDataAndDescr(int nd, int *d, +PyArray_FromDimsAndDataAndDescr(int NPY_UNUSED(nd), int *NPY_UNUSED(d), PyArray_Descr *descr, - char *data) + char *NPY_UNUSED(data)) { - PyObject *ret; - int i; - npy_intp newd[NPY_MAXDIMS]; - char msg[] = "PyArray_FromDimsAndDataAndDescr: use PyArray_NewFromDescr."; - - if (DEPRECATE(msg) < 0) { - /* 2009-04-30, 1.5 */ - return NULL; - } - if (!PyArray_ISNBO(descr->byteorder)) - descr->byteorder = '='; - for (i = 0; i < nd; i++) { - newd[i] = (npy_intp) d[i]; - } - ret = PyArray_NewFromDescr(&PyArray_Type, descr, - nd, newd, - NULL, data, - (data ? NPY_ARRAY_CARRAY : 0), NULL); - return ret; + PyErr_SetString(PyExc_NotImplementedError, + "PyArray_FromDimsAndDataAndDescr: use PyArray_NewFromDescr."); + Py_DECREF(descr); + return NULL; } /*NUMPY_API - Construct an empty array from dimensions and typenum + Deprecated, use PyArray_SimpleNew instead. */ NPY_NO_EXPORT PyObject * -PyArray_FromDims(int nd, int *d, int type) +PyArray_FromDims(int NPY_UNUSED(nd), int *NPY_UNUSED(d), int NPY_UNUSED(type)) { - PyArrayObject *ret; - char msg[] = "PyArray_FromDims: use PyArray_SimpleNew."; - - if (DEPRECATE(msg) < 0) { - /* 2009-04-30, 1.5 */ - return NULL; - } - ret = (PyArrayObject *)PyArray_FromDimsAndDataAndDescr(nd, d, - PyArray_DescrFromType(type), - NULL); - /* - * Old FromDims set memory to zero --- some algorithms - * relied on that. Better keep it the same. If - * Object type, then it's already been set to zero, though. - */ - if (ret && (PyArray_DESCR(ret)->type_num != NPY_OBJECT)) { - memset(PyArray_DATA(ret), 0, PyArray_NBYTES(ret)); - } - return (PyObject *)ret; + PyErr_SetString(PyExc_NotImplementedError, + "PyArray_FromDims: use PyArray_SimpleNew."); + return NULL; } /* end old calls */ @@ -2722,12 +2662,13 @@ PyArray_EnsureAnyArray(PyObject *op) return PyArray_EnsureArray(op); } -/* TODO: Put the order parameter in PyArray_CopyAnyInto and remove this */ +/* + * Private implementation of PyArray_CopyAnyInto with an additional order + * parameter. + */ NPY_NO_EXPORT int PyArray_CopyAsFlat(PyArrayObject *dst, PyArrayObject *src, NPY_ORDER order) { - PyArray_StridedUnaryOp *stransfer = NULL; - NpyAuxData *transferdata = NULL; NpyIter *dst_iter, *src_iter; NpyIter_IterNextFunc *dst_iternext, *src_iternext; @@ -2736,9 +2677,7 @@ PyArray_CopyAsFlat(PyArrayObject *dst, PyArrayObject *src, NPY_ORDER order) npy_intp *dst_countptr, *src_countptr; npy_uint32 baseflags; - char *dst_data, *src_data; npy_intp dst_count, src_count, count; - npy_intp src_itemsize; npy_intp dst_size, src_size; int needs_api; @@ -2763,8 +2702,8 @@ PyArray_CopyAsFlat(PyArrayObject *dst, PyArrayObject *src, NPY_ORDER order) src_size = PyArray_SIZE(src); if (dst_size != src_size) { PyErr_Format(PyExc_ValueError, - "cannot copy from array of size %d into an array " - "of size %d", (int)src_size, (int)dst_size); + "cannot copy from array of size %" NPY_INTP_FMT " into an array " + "of size %" NPY_INTP_FMT, src_size, dst_size); return -1; } @@ -2810,7 +2749,6 @@ PyArray_CopyAsFlat(PyArrayObject *dst, PyArrayObject *src, NPY_ORDER order) /* Since buffering is disabled, we can cache the stride */ src_stride = NpyIter_GetInnerStrideArray(src_iter)[0]; src_countptr = NpyIter_GetInnerLoopSizePtr(src_iter); - src_itemsize = PyArray_DESCR(src)->elsize; if (dst_iternext == NULL || src_iternext == NULL) { NpyIter_Deallocate(dst_iter); @@ -2827,13 +2765,14 @@ PyArray_CopyAsFlat(PyArrayObject *dst, PyArrayObject *src, NPY_ORDER order) * we can pass them to this function to take advantage of * contiguous strides, etc. */ + NPY_cast_info cast_info; if (PyArray_GetDTypeTransferFunction( - PyArray_ISALIGNED(src) && PyArray_ISALIGNED(dst), + IsUintAligned(src) && IsAligned(src) && + IsUintAligned(dst) && IsAligned(dst), src_stride, dst_stride, PyArray_DESCR(src), PyArray_DESCR(dst), 0, - &stransfer, &transferdata, - &needs_api) != NPY_SUCCEED) { + &cast_info, &needs_api) != NPY_SUCCEED) { NpyIter_Deallocate(dst_iter); NpyIter_Deallocate(src_iter); return -1; @@ -2845,49 +2784,54 @@ PyArray_CopyAsFlat(PyArrayObject *dst, PyArrayObject *src, NPY_ORDER order) dst_count = *dst_countptr; src_count = *src_countptr; - dst_data = dst_dataptr[0]; - src_data = src_dataptr[0]; + char *args[2] = {src_dataptr[0], dst_dataptr[0]}; + npy_intp strides[2] = {src_stride, dst_stride}; + + int res = 0; for(;;) { /* Transfer the biggest amount that fits both */ count = (src_count < dst_count) ? src_count : dst_count; - stransfer(dst_data, dst_stride, - src_data, src_stride, - count, src_itemsize, transferdata); + if (cast_info.func(&cast_info.context, + args, &count, strides, cast_info.auxdata) < 0) { + res = -1; + break; + } /* If we exhausted the dst block, refresh it */ if (dst_count == count) { - if (!dst_iternext(dst_iter)) { + res = dst_iternext(dst_iter); + if (res == 0) { break; } dst_count = *dst_countptr; - dst_data = dst_dataptr[0]; + args[1] = dst_dataptr[0]; } else { dst_count -= count; - dst_data += count*dst_stride; + args[1] += count*dst_stride; } /* If we exhausted the src block, refresh it */ if (src_count == count) { - if (!src_iternext(src_iter)) { + res = src_iternext(src_iter); + if (res == 0) { break; } src_count = *src_countptr; - src_data = src_dataptr[0]; + args[0] = src_dataptr[0]; } else { src_count -= count; - src_data += count*src_stride; + args[0] += count*src_stride; } } NPY_END_THREADS; - NPY_AUXDATA_FREE(transferdata); + NPY_cast_info_xfree(&cast_info); NpyIter_Deallocate(dst_iter); NpyIter_Deallocate(src_iter); - - return PyErr_Occurred() ? -1 : 0; + return res; } /*NUMPY_API @@ -2993,7 +2937,7 @@ PyArray_CheckAxis(PyArrayObject *arr, int *axis, int flags) * accepts NULL type */ NPY_NO_EXPORT PyObject * -PyArray_Zeros(int nd, npy_intp *dims, PyArray_Descr *type, int is_f_order) +PyArray_Zeros(int nd, npy_intp const *dims, PyArray_Descr *type, int is_f_order) { PyArrayObject *ret; @@ -3028,10 +2972,10 @@ PyArray_Zeros(int nd, npy_intp *dims, PyArray_Descr *type, int is_f_order) * Empty * * accepts NULL type - * steals referenct to type + * steals a reference to type */ NPY_NO_EXPORT PyObject * -PyArray_Empty(int nd, npy_intp *dims, PyArray_Descr *type, int is_f_order) +PyArray_Empty(int nd, npy_intp const *dims, PyArray_Descr *type, int is_f_order) { PyArrayObject *ret; @@ -3078,7 +3022,7 @@ _arange_safe_ceil_to_intp(double value) "arange: cannot compute length"); return -1; } - if (!(NPY_MIN_INTP <= ivalue && ivalue <= NPY_MAX_INTP)) { + if (!((double)NPY_MIN_INTP <= ivalue && ivalue <= (double)NPY_MAX_INTP)) { PyErr_SetString(PyExc_OverflowError, "arange: overflow while computing length"); return -1; @@ -3197,7 +3141,7 @@ _calc_length(PyObject *start, PyObject *stop, PyObject *step, PyObject **next, i return -1; } - zero = PyInt_FromLong(0); + zero = PyLong_FromLong(0); if (!zero) { Py_DECREF(*next); *next = NULL; @@ -3342,14 +3286,14 @@ PyArray_ArangeObj(PyObject *start, PyObject *stop, PyObject *step, PyArray_Descr Py_INCREF(dtype); } if (!step || step == Py_None) { - step = PyInt_FromLong(1); + step = PyLong_FromLong(1); } else { Py_XINCREF(step); } if (!stop || stop == Py_None) { stop = start; - start = PyInt_FromLong(0); + start = PyLong_FromLong(0); } else { Py_INCREF(start); @@ -3442,11 +3386,13 @@ PyArray_ArangeObj(PyObject *start, PyObject *stop, PyObject *step, PyArray_Descr return NULL; } +/* This array creation function does not steal the reference to dtype. */ static PyArrayObject * array_fromfile_binary(FILE *fp, PyArray_Descr *dtype, npy_intp num, size_t *nread) { PyArrayObject *r; npy_off_t start, numbytes; + int elsize; if (num < 0) { int fail = 0; @@ -3466,38 +3412,40 @@ array_fromfile_binary(FILE *fp, PyArray_Descr *dtype, npy_intp num, size_t *nrea fail = 1; } if (fail) { - PyErr_SetString(PyExc_IOError, + PyErr_SetString(PyExc_OSError, "could not seek in file"); - Py_DECREF(dtype); return NULL; } num = numbytes / dtype->elsize; } + /* - * When dtype->subarray is true, PyArray_NewFromDescr will decref dtype - * even on success, so make sure it stays around until exit. + * Array creation may move sub-array dimensions from the dtype to array + * dimensions, so we need to use the original element size when reading. */ - Py_INCREF(dtype); + elsize = dtype->elsize; + + Py_INCREF(dtype); /* do not steal the original dtype. */ r = (PyArrayObject *)PyArray_NewFromDescr(&PyArray_Type, dtype, 1, &num, NULL, NULL, 0, NULL); if (r == NULL) { - Py_DECREF(dtype); return NULL; } + NPY_BEGIN_ALLOW_THREADS; - *nread = fread(PyArray_DATA(r), dtype->elsize, num, fp); + *nread = fread(PyArray_DATA(r), elsize, num, fp); NPY_END_ALLOW_THREADS; - Py_DECREF(dtype); return r; } /* * Create an array by reading from the given stream, using the passed * next_element and skip_separator functions. + * Does not steal the reference to dtype. */ #define FROM_BUFFER_SIZE 4096 static PyArrayObject * -array_from_text(PyArray_Descr *dtype, npy_intp num, char *sep, size_t *nread, +array_from_text(PyArray_Descr *dtype, npy_intp num, char const *sep, size_t *nread, void *stream, next_element next, skip_separator skip_sep, void *stream_data) { @@ -3505,6 +3453,7 @@ array_from_text(PyArray_Descr *dtype, npy_intp num, char *sep, size_t *nread, npy_intp i; char *dptr, *clean_sep, *tmp; int err = 0; + int stop_reading_flag = 0; /* -1 means end reached; -2 a parsing error */ npy_intp thisbuf = 0; npy_intp size; npy_intp bytes, totalbytes; @@ -3512,17 +3461,18 @@ array_from_text(PyArray_Descr *dtype, npy_intp num, char *sep, size_t *nread, size = (num >= 0) ? num : FROM_BUFFER_SIZE; /* - * When dtype->subarray is true, PyArray_NewFromDescr will decref dtype - * even on success, so make sure it stays around until exit. + * Array creation may move sub-array dimensions from the dtype to array + * dimensions, so we need to use the original dtype when reading. */ Py_INCREF(dtype); + r = (PyArrayObject *) PyArray_NewFromDescr(&PyArray_Type, dtype, 1, &size, NULL, NULL, 0, NULL); if (r == NULL) { - Py_DECREF(dtype); return NULL; } + clean_sep = swab_separator(sep); if (clean_sep == NULL) { err = 1; @@ -3532,9 +3482,9 @@ array_from_text(PyArray_Descr *dtype, npy_intp num, char *sep, size_t *nread, NPY_BEGIN_ALLOW_THREADS; totalbytes = bytes = size * dtype->elsize; dptr = PyArray_DATA(r); - for (i= 0; num < 0 || i < num; i++) { - if (next(&stream, dptr, dtype, stream_data) < 0) { - /* EOF */ + for (i = 0; num < 0 || i < num; i++) { + stop_reading_flag = next(&stream, dptr, dtype, stream_data); + if (stop_reading_flag < 0) { break; } *nread += 1; @@ -3542,7 +3492,9 @@ array_from_text(PyArray_Descr *dtype, npy_intp num, char *sep, size_t *nread, dptr += dtype->elsize; if (num < 0 && thisbuf == size) { totalbytes += bytes; - tmp = PyDataMem_RENEW(PyArray_DATA(r), totalbytes); + /* The handler is always valid */ + tmp = PyDataMem_UserRENEW(PyArray_DATA(r), totalbytes, + PyArray_HANDLER(r)); if (tmp == NULL) { err = 1; break; @@ -3551,25 +3503,50 @@ array_from_text(PyArray_Descr *dtype, npy_intp num, char *sep, size_t *nread, dptr = tmp + (totalbytes - bytes); thisbuf = 0; } - if (skip_sep(&stream, clean_sep, stream_data) < 0) { + stop_reading_flag = skip_sep(&stream, clean_sep, stream_data); + if (stop_reading_flag < 0) { + if (num == i + 1) { + /* if we read as much as requested sep is optional */ + stop_reading_flag = -1; + } break; } } if (num < 0) { - tmp = PyDataMem_RENEW(PyArray_DATA(r), PyArray_MAX(*nread,1)*dtype->elsize); - if (tmp == NULL) { - err = 1; - } - else { - PyArray_DIMS(r)[0] = *nread; - ((PyArrayObject_fields *)r)->data = tmp; + const size_t nsize = PyArray_MAX(*nread,1)*dtype->elsize; + + if (nsize != 0) { + /* The handler is always valid */ + tmp = PyDataMem_UserRENEW(PyArray_DATA(r), nsize, + PyArray_HANDLER(r)); + if (tmp == NULL) { + err = 1; + } + else { + PyArray_DIMS(r)[0] = *nread; + ((PyArrayObject_fields *)r)->data = tmp; + } } } NPY_END_ALLOW_THREADS; + free(clean_sep); + if (stop_reading_flag == -2) { + if (PyErr_Occurred()) { + /* If an error is already set (unlikely), do not create new one */ + Py_DECREF(r); + return NULL; + } + /* 2019-09-12, NumPy 1.18 */ + if (DEPRECATE( + "string or file could not be read to its end due to unmatched " + "data; this will raise a ValueError in the future.") < 0) { + goto fail; + } + } + fail: - Py_DECREF(dtype); if (err == 1) { PyErr_NoMemory(); } @@ -3586,9 +3563,8 @@ array_from_text(PyArray_Descr *dtype, npy_intp num, char *sep, size_t *nread, * Given a ``FILE *`` pointer ``fp``, and a ``PyArray_Descr``, return an * array corresponding to the data encoded in that file. * - * If the dtype is NULL, the default array type is used (double). - * If non-null, the reference is stolen and if dtype->subarray is true dtype - * will be decrefed even on success. + * The reference to `dtype` is stolen (it is possible that the passed in + * dtype is not held on to). * * The number of elements to read is given as ``num``; if it is < 0, then * then as many as possible are read. @@ -3640,17 +3616,24 @@ PyArray_FromFile(FILE *fp, PyArray_Descr *dtype, npy_intp num, char *sep) return NULL; } if (((npy_intp) nread) < num) { - /* Realloc memory for smaller number of elements */ - const size_t nsize = PyArray_MAX(nread,1)*PyArray_DESCR(ret)->elsize; + /* + * Realloc memory for smaller number of elements, use original dtype + * which may have include a subarray (and is used for `nread`). + */ + const size_t nsize = PyArray_MAX(nread,1) * dtype->elsize; char *tmp; - if((tmp = PyDataMem_RENEW(PyArray_DATA(ret), nsize)) == NULL) { + /* The handler is always valid */ + if((tmp = PyDataMem_UserRENEW(PyArray_DATA(ret), nsize, + PyArray_HANDLER(ret))) == NULL) { + Py_DECREF(dtype); Py_DECREF(ret); return PyErr_NoMemory(); } ((PyArrayObject_fields *)ret)->data = tmp; PyArray_DIMS(ret)[0] = nread; } + Py_DECREF(dtype); return (PyObject *)ret; } @@ -3661,9 +3644,7 @@ PyArray_FromBuffer(PyObject *buf, PyArray_Descr *type, { PyArrayObject *ret; char *data; -#if defined(NPY_PY3K) Py_buffer view; -#endif Py_ssize_t ts; npy_intp s, n; int itemsize; @@ -3683,32 +3664,11 @@ PyArray_FromBuffer(PyObject *buf, PyArray_Descr *type, Py_DECREF(type); return NULL; } - if (Py_TYPE(buf)->tp_as_buffer == NULL -#if defined(NPY_PY3K) - || Py_TYPE(buf)->tp_as_buffer->bf_getbuffer == NULL -#else - || (Py_TYPE(buf)->tp_as_buffer->bf_getwritebuffer == NULL - && Py_TYPE(buf)->tp_as_buffer->bf_getreadbuffer == NULL) -#endif - ) { - PyObject *newbuf; - newbuf = PyObject_GetAttr(buf, npy_ma_str_buffer); - if (newbuf == NULL) { - Py_DECREF(type); - return NULL; - } - buf = newbuf; - } - else { - Py_INCREF(buf); - } -#if defined(NPY_PY3K) if (PyObject_GetBuffer(buf, &view, PyBUF_WRITABLE|PyBUF_SIMPLE) < 0) { writeable = 0; PyErr_Clear(); if (PyObject_GetBuffer(buf, &view, PyBUF_SIMPLE) < 0) { - Py_DECREF(buf); Py_DECREF(type); return NULL; } @@ -3722,23 +3682,11 @@ PyArray_FromBuffer(PyObject *buf, PyArray_Descr *type, * sticks around after the release. */ PyBuffer_Release(&view); -#else - if (PyObject_AsWriteBuffer(buf, (void *)&data, &ts) == -1) { - writeable = 0; - PyErr_Clear(); - if (PyObject_AsReadBuffer(buf, (void *)&data, &ts) == -1) { - Py_DECREF(buf); - Py_DECREF(type); - return NULL; - } - } -#endif if ((offset < 0) || (offset > ts)) { PyErr_Format(PyExc_ValueError, "offset must be non-negative and no greater than buffer "\ "length (%" NPY_INTP_FMT ")", (npy_intp)ts); - Py_DECREF(buf); Py_DECREF(type); return NULL; } @@ -3747,12 +3695,17 @@ PyArray_FromBuffer(PyObject *buf, PyArray_Descr *type, s = (npy_intp)ts - offset; n = (npy_intp)count; itemsize = type->elsize; - if (n < 0 ) { + if (n < 0) { + if (itemsize == 0) { + PyErr_SetString(PyExc_ValueError, + "cannot determine count if itemsize is 0"); + Py_DECREF(type); + return NULL; + } if (s % itemsize != 0) { PyErr_SetString(PyExc_ValueError, "buffer size must be a multiple"\ " of element size"); - Py_DECREF(buf); Py_DECREF(type); return NULL; } @@ -3763,7 +3716,6 @@ PyArray_FromBuffer(PyObject *buf, PyArray_Descr *type, PyErr_SetString(PyExc_ValueError, "buffer is smaller than requested"\ " size"); - Py_DECREF(buf); Py_DECREF(type); return NULL; } @@ -3773,7 +3725,6 @@ PyArray_FromBuffer(PyObject *buf, PyArray_Descr *type, &PyArray_Type, type, 1, &n, NULL, data, NPY_ARRAY_DEFAULT, NULL, buf); - Py_DECREF(buf); if (ret == NULL) { return NULL; } @@ -3855,6 +3806,11 @@ PyArray_FromString(char *data, npy_intp slen, PyArray_Descr *dtype, return NULL; } } + /* + * NewFromDescr may replace dtype to absorb subarray shape + * into the array, so get size beforehand. + */ + npy_intp size_to_copy = num*dtype->elsize; ret = (PyArrayObject *) PyArray_NewFromDescr(&PyArray_Type, dtype, 1, &num, NULL, NULL, @@ -3862,14 +3818,14 @@ PyArray_FromString(char *data, npy_intp slen, PyArray_Descr *dtype, if (ret == NULL) { return NULL; } - memcpy(PyArray_DATA(ret), data, num*dtype->elsize); + memcpy(PyArray_DATA(ret), data, size_to_copy); } else { /* read from character-based string */ size_t nread = 0; char *end; - if (dtype->f->scanfunc == NULL) { + if (dtype->f->fromstr == NULL) { PyErr_SetString(PyExc_ValueError, "don't know how to read " \ "character strings with that " \ @@ -3888,6 +3844,7 @@ PyArray_FromString(char *data, npy_intp slen, PyArray_Descr *dtype, (next_element) fromstr_next_element, (skip_separator) fromstr_skip_separator, end); + Py_DECREF(dtype); } return (PyObject *)ret; } @@ -3913,7 +3870,16 @@ PyArray_FromIter(PyObject *obj, PyArray_Descr *dtype, npy_intp count) "Must specify length when using variable-size data-type."); goto done; } - elcount = (count < 0) ? 0 : count; + if (count < 0) { + elcount = PyObject_LengthHint(obj, 0); + if (elcount < 0) { + goto done; + } + } + else { + elcount = count; + } + elsize = dtype->elsize; /* @@ -3934,7 +3900,7 @@ PyArray_FromIter(PyObject *obj, PyArray_Descr *dtype, npy_intp count) } for (i = 0; (i < count || count == -1) && (value = PyIter_Next(iter)); i++) { - if (i >= elcount) { + if (i >= elcount && elsize != 0) { npy_intp nbytes; /* Grow PyArray_DATA(ret): @@ -3943,7 +3909,9 @@ PyArray_FromIter(PyObject *obj, PyArray_Descr *dtype, npy_intp count) */ elcount = (i >> 1) + (i < 4 ? 4 : 2) + i; if (!npy_mul_with_overflow_intp(&nbytes, elcount, elsize)) { - new_data = PyDataMem_RENEW(PyArray_DATA(ret), nbytes); + /* The handler is always valid */ + new_data = PyDataMem_UserRENEW(PyArray_DATA(ret), nbytes, + PyArray_HANDLER(ret)); } else { new_data = NULL; @@ -3980,11 +3948,13 @@ PyArray_FromIter(PyObject *obj, PyArray_Descr *dtype, npy_intp count) * Realloc the data so that don't keep extra memory tied up * (assuming realloc is reasonably good about reusing space...) */ - if (i == 0) { - /* The size cannot be zero for PyDataMem_RENEW. */ - i = 1; + if (i == 0 || elsize == 0) { + /* The size cannot be zero for realloc. */ + goto done; } - new_data = PyDataMem_RENEW(PyArray_DATA(ret), i * elsize); + /* The handler is always valid */ + new_data = PyDataMem_UserRENEW(PyArray_DATA(ret), i * elsize, + PyArray_HANDLER(ret)); if (new_data == NULL) { PyErr_SetString(PyExc_MemoryError, "cannot allocate array memory"); @@ -4022,7 +3992,7 @@ PyArray_FromIter(PyObject *obj, PyArray_Descr *dtype, npy_intp count) */ NPY_NO_EXPORT void -_array_fill_strides(npy_intp *strides, npy_intp *dims, int nd, size_t itemsize, +_array_fill_strides(npy_intp *strides, npy_intp const *dims, int nd, size_t itemsize, int inflag, int *objflags) { int i; @@ -4114,8 +4084,8 @@ _array_fill_strides(npy_intp *strides, npy_intp *dims, int nd, size_t itemsize, NPY_NO_EXPORT PyArrayObject * PyArray_SubclassWrap(PyArrayObject *arr_of_subclass, PyArrayObject *towrap) { - PyObject *wrapped = PyObject_CallMethod((PyObject *)arr_of_subclass, - "__array_wrap__", "O", towrap); + PyObject *wrapped = PyObject_CallMethodObjArgs((PyObject *)arr_of_subclass, + npy_ma_str_array_wrap, (PyObject *)towrap, NULL); if (wrapped == NULL) { return NULL; } diff --git a/numpy/core/src/multiarray/ctors.h b/numpy/core/src/multiarray/ctors.h index e9a2532daa2f..98160b1cc48f 100644 --- a/numpy/core/src/multiarray/ctors.h +++ b/numpy/core/src/multiarray/ctors.h @@ -1,25 +1,39 @@ -#ifndef _NPY_ARRAY_CTORS_H_ -#define _NPY_ARRAY_CTORS_H_ +#ifndef NUMPY_CORE_SRC_MULTIARRAY_CTORS_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_CTORS_H_ NPY_NO_EXPORT PyObject * -PyArray_NewFromDescr(PyTypeObject *subtype, PyArray_Descr *descr, int nd, - npy_intp *dims, npy_intp *strides, void *data, - int flags, PyObject *obj); +PyArray_NewFromDescr( + PyTypeObject *subtype, PyArray_Descr *descr, int nd, + npy_intp const *dims, npy_intp const *strides, void *data, + int flags, PyObject *obj); NPY_NO_EXPORT PyObject * PyArray_NewFromDescrAndBase( - PyTypeObject *subtype, PyArray_Descr *descr, - int nd, npy_intp *dims, npy_intp *strides, void *data, + PyTypeObject *subtype, PyArray_Descr *descr, int nd, + npy_intp const *dims, npy_intp const *strides, void *data, int flags, PyObject *obj, PyObject *base); NPY_NO_EXPORT PyObject * -PyArray_NewFromDescr_int(PyTypeObject *subtype, PyArray_Descr *descr, int nd, - npy_intp *dims, npy_intp *strides, void *data, - int flags, PyObject *obj, PyObject *base, int zeroed, - int allow_emptystring); +PyArray_NewFromDescr_int( + PyTypeObject *subtype, PyArray_Descr *descr, int nd, + npy_intp const *dims, npy_intp const *strides, void *data, + int flags, PyObject *obj, PyObject *base, int zeroed, + int allow_emptystring); -NPY_NO_EXPORT PyObject *PyArray_New(PyTypeObject *, int nd, npy_intp *, - int, npy_intp *, void *, int, int, PyObject *); +NPY_NO_EXPORT PyObject * +PyArray_NewLikeArrayWithShape( + PyArrayObject *prototype, NPY_ORDER order, + PyArray_Descr *dtype, int ndim, npy_intp const *dims, int subok); + +NPY_NO_EXPORT PyObject * +PyArray_New( + PyTypeObject *, int nd, npy_intp const *, + int, npy_intp const*, void *, int, int, PyObject *); + +NPY_NO_EXPORT PyObject * +_array_from_array_like(PyObject *op, + PyArray_Descr *requested_dtype, npy_bool writeable, PyObject *context, + int never_copy); NPY_NO_EXPORT PyObject * PyArray_FromAny(PyObject *op, PyArray_Descr *newtype, int min_depth, @@ -38,6 +52,10 @@ PyArray_FromStructInterface(PyObject *input); NPY_NO_EXPORT PyObject * PyArray_FromInterface(PyObject *input); +NPY_NO_EXPORT PyObject * +PyArray_FromArrayAttr_int( + PyObject *op, PyArray_Descr *descr, int never_copy); + NPY_NO_EXPORT PyObject * PyArray_FromArrayAttr(PyObject *op, PyArray_Descr *typecode, PyObject *context); @@ -64,7 +82,7 @@ PyArray_CopyAsFlat(PyArrayObject *dst, PyArrayObject *src, /* FIXME: remove those from here */ NPY_NO_EXPORT void -_array_fill_strides(npy_intp *strides, npy_intp *dims, int nd, size_t itemsize, +_array_fill_strides(npy_intp *strides, npy_intp const *dims, int nd, size_t itemsize, int inflag, int *objflags); NPY_NO_EXPORT void @@ -81,9 +99,6 @@ copy_and_swap(void *dst, void *src, int itemsize, npy_intp numitems, NPY_NO_EXPORT void byte_swap_vector(void *p, npy_intp n, int size); -NPY_NO_EXPORT int -PyArray_AssignFromSequence(PyArrayObject *self, PyObject *v); - /* * Calls arr_of_subclass.__array_wrap__(towrap), in order to make 'towrap' * have the same ndarray subclass as 'arr_of_subclass'. @@ -92,4 +107,4 @@ NPY_NO_EXPORT PyArrayObject * PyArray_SubclassWrap(PyArrayObject *arr_of_subclass, PyArrayObject *towrap); -#endif +#endif /* NUMPY_CORE_SRC_MULTIARRAY_CTORS_H_ */ diff --git a/numpy/core/src/multiarray/datetime.c b/numpy/core/src/multiarray/datetime.c index df9b9cec4948..e0064c017361 100644 --- a/numpy/core/src/multiarray/datetime.c +++ b/numpy/core/src/multiarray/datetime.c @@ -6,25 +6,67 @@ * * See LICENSE.txt for the license. */ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE #define PY_SSIZE_T_CLEAN #include <Python.h> -#include <datetime.h> -#include <time.h> - -#define NPY_NO_DEPRECATED_API NPY_API_VERSION -#define _MULTIARRAYMODULE -#include <numpy/arrayobject.h> +#include "numpy/arrayobject.h" +#include "numpyos.h" #include "npy_config.h" #include "npy_pycompat.h" #include "common.h" #include "numpy/arrayscalars.h" -#include "methods.h" #include "_datetime.h" #include "datetime_strings.h" +#include "convert_datatype.h" +#include "array_method.h" +#include "dtypemeta.h" +#include "usertypes.h" + +#include "dtype_transfer.h" +#include "lowlevel_strided_loops.h" + +#include <datetime.h> +#include <time.h> + + +/* + * Computes the python `ret, d = divmod(d, unit)`. + * + * Note that GCC is smart enough at -O2 to eliminate the `if(*d < 0)` branch + * for subsequent calls to this command - it is able to deduce that `*d >= 0`. + */ +static inline +npy_int64 extract_unit_64(npy_int64 *d, npy_int64 unit) { + assert(unit > 0); + npy_int64 div = *d / unit; + npy_int64 mod = *d % unit; + if (mod < 0) { + mod += unit; + div -= 1; + } + assert(mod >= 0); + *d = mod; + return div; +} + +static inline +npy_int32 extract_unit_32(npy_int32 *d, npy_int32 unit) { + assert(unit > 0); + npy_int32 div = *d / unit; + npy_int32 mod = *d % unit; + if (mod < 0) { + mod += unit; + div -= 1; + } + assert(mod >= 0); + *d = mod; + return div; +} /* * Imports the PyDateTime functions so we can create these objects. @@ -37,7 +79,7 @@ numpy_pydatetime_import(void) } /* Exported as DATETIMEUNITS in multiarraymodule.c */ -NPY_NO_EXPORT char *_datetime_strings[NPY_DATETIME_NUMUNITS] = { +NPY_NO_EXPORT char const *_datetime_strings[NPY_DATETIME_NUMUNITS] = { "Y", "M", "W", @@ -160,17 +202,7 @@ days_to_yearsdays(npy_int64 *days_) npy_int64 year; /* Break down the 400 year cycle to get the year and day within the year */ - if (days >= 0) { - year = 400 * (days / days_per_400years); - days = days % days_per_400years; - } - else { - year = 400 * ((days - (days_per_400years - 1)) / days_per_400years); - days = days % days_per_400years; - if (days < 0) { - days += days_per_400years; - } - } + year = 400 * extract_unit_64(&days, days_per_400years); /* Work out the year/day within the 400 year cycle */ if (days >= 366) { @@ -386,7 +418,8 @@ convert_datetimestruct_to_datetime(PyArray_DatetimeMetaData *meta, * TO BE REMOVED - NOT USED INTERNALLY. */ NPY_NO_EXPORT npy_datetime -PyArray_DatetimeStructToDatetime(NPY_DATETIMEUNIT fr, npy_datetimestruct *d) +PyArray_DatetimeStructToDatetime( + NPY_DATETIMEUNIT NPY_UNUSED(fr), npy_datetimestruct *NPY_UNUSED(d)) { PyErr_SetString(PyExc_RuntimeError, "The NumPy PyArray_DatetimeStructToDatetime function has " @@ -395,12 +428,13 @@ PyArray_DatetimeStructToDatetime(NPY_DATETIMEUNIT fr, npy_datetimestruct *d) } /*NUMPY_API - * Create a timdelta value from a filled timedelta struct and resolution unit. + * Create a timedelta value from a filled timedelta struct and resolution unit. * * TO BE REMOVED - NOT USED INTERNALLY. */ NPY_NO_EXPORT npy_datetime -PyArray_TimedeltaStructToTimedelta(NPY_DATETIMEUNIT fr, npy_timedeltastruct *d) +PyArray_TimedeltaStructToTimedelta( + NPY_DATETIMEUNIT NPY_UNUSED(fr), npy_timedeltastruct *NPY_UNUSED(d)) { PyErr_SetString(PyExc_RuntimeError, "The NumPy PyArray_TimedeltaStructToTimedelta function has " @@ -416,7 +450,7 @@ convert_datetime_to_datetimestruct(PyArray_DatetimeMetaData *meta, npy_datetime dt, npy_datetimestruct *out) { - npy_int64 perday; + npy_int64 days; /* Initialize the output to all zeros */ memset(out, 0, sizeof(npy_datetimestruct)); @@ -451,14 +485,8 @@ convert_datetime_to_datetimestruct(PyArray_DatetimeMetaData *meta, break; case NPY_FR_M: - if (dt >= 0) { - out->year = 1970 + dt / 12; - out->month = dt % 12 + 1; - } - else { - out->year = 1969 + (dt + 1) / 12; - out->month = 12 + (dt + 1)% 12; - } + out->year = 1970 + extract_unit_64(&dt, 12); + out->month = dt + 1; break; case NPY_FR_W: @@ -471,171 +499,96 @@ convert_datetime_to_datetimestruct(PyArray_DatetimeMetaData *meta, break; case NPY_FR_h: - perday = 24LL; - - if (dt >= 0) { - set_datetimestruct_days(dt / perday, out); - dt = dt % perday; - } - else { - set_datetimestruct_days((dt - (perday-1)) / perday, out); - dt = (perday-1) + (dt + 1) % perday; - } + days = extract_unit_64(&dt, 24LL); + set_datetimestruct_days(days, out); out->hour = (int)dt; break; case NPY_FR_m: - perday = 24LL * 60; - - if (dt >= 0) { - set_datetimestruct_days(dt / perday, out); - dt = dt % perday; - } - else { - set_datetimestruct_days((dt - (perday-1)) / perday, out); - dt = (perday-1) + (dt + 1) % perday; - } - out->hour = (int)(dt / 60); - out->min = (int)(dt % 60); + days = extract_unit_64(&dt, 60LL*24); + set_datetimestruct_days(days, out); + out->hour = (int)extract_unit_64(&dt, 60LL); + out->min = (int)dt; break; case NPY_FR_s: - perday = 24LL * 60 * 60; - - if (dt >= 0) { - set_datetimestruct_days(dt / perday, out); - dt = dt % perday; - } - else { - set_datetimestruct_days((dt - (perday-1)) / perday, out); - dt = (perday-1) + (dt + 1) % perday; - } - out->hour = (int)(dt / (60*60)); - out->min = (int)((dt / 60) % 60); - out->sec = (int)(dt % 60); + days = extract_unit_64(&dt, 60LL*60*24); + set_datetimestruct_days(days, out); + out->hour = (int)extract_unit_64(&dt, 60LL*60); + out->min = (int)extract_unit_64(&dt, 60LL); + out->sec = (int)dt; break; case NPY_FR_ms: - perday = 24LL * 60 * 60 * 1000; - - if (dt >= 0) { - set_datetimestruct_days(dt / perday, out); - dt = dt % perday; - } - else { - set_datetimestruct_days((dt - (perday-1)) / perday, out); - dt = (perday-1) + (dt + 1) % perday; - } - out->hour = (int)(dt / (60*60*1000LL)); - out->min = (int)((dt / (60*1000LL)) % 60); - out->sec = (int)((dt / 1000LL) % 60); - out->us = (int)((dt % 1000LL) * 1000); + days = extract_unit_64(&dt, 1000LL*60*60*24); + set_datetimestruct_days(days, out); + out->hour = (int)extract_unit_64(&dt, 1000LL*60*60); + out->min = (int)extract_unit_64(&dt, 1000LL*60); + out->sec = (int)extract_unit_64(&dt, 1000LL); + out->us = (int)(dt * 1000); break; case NPY_FR_us: - perday = 24LL * 60LL * 60LL * 1000LL * 1000LL; - - if (dt >= 0) { - set_datetimestruct_days(dt / perday, out); - dt = dt % perday; - } - else { - set_datetimestruct_days((dt - (perday-1)) / perday, out); - dt = (perday-1) + (dt + 1) % perday; - } - out->hour = (int)(dt / (60*60*1000000LL)); - out->min = (int)((dt / (60*1000000LL)) % 60); - out->sec = (int)((dt / 1000000LL) % 60); - out->us = (int)(dt % 1000000LL); + days = extract_unit_64(&dt, 1000LL*1000*60*60*24); + set_datetimestruct_days(days, out); + out->hour = (int)extract_unit_64(&dt, 1000LL*1000*60*60); + out->min = (int)extract_unit_64(&dt, 1000LL*1000*60); + out->sec = (int)extract_unit_64(&dt, 1000LL*1000); + out->us = (int)dt; break; case NPY_FR_ns: - perday = 24LL * 60LL * 60LL * 1000LL * 1000LL * 1000LL; - - if (dt >= 0) { - set_datetimestruct_days(dt / perday, out); - dt = dt % perday; - } - else { - set_datetimestruct_days((dt - (perday-1)) / perday, out); - dt = (perday-1) + (dt + 1) % perday; - } - out->hour = (int)(dt / (60*60*1000000000LL)); - out->min = (int)((dt / (60*1000000000LL)) % 60); - out->sec = (int)((dt / 1000000000LL) % 60); - out->us = (int)((dt / 1000LL) % 1000000LL); - out->ps = (int)((dt % 1000LL) * 1000); + days = extract_unit_64(&dt, 1000LL*1000*1000*60*60*24); + set_datetimestruct_days(days, out); + out->hour = (int)extract_unit_64(&dt, 1000LL*1000*1000*60*60); + out->min = (int)extract_unit_64(&dt, 1000LL*1000*1000*60); + out->sec = (int)extract_unit_64(&dt, 1000LL*1000*1000); + out->us = (int)extract_unit_64(&dt, 1000LL); + out->ps = (int)(dt * 1000); break; case NPY_FR_ps: - perday = 24LL * 60 * 60 * 1000 * 1000 * 1000 * 1000; - - if (dt >= 0) { - set_datetimestruct_days(dt / perday, out); - dt = dt % perday; - } - else { - set_datetimestruct_days((dt - (perday-1)) / perday, out); - dt = (perday-1) + (dt + 1) % perday; - } - out->hour = (int)(dt / (60*60*1000000000000LL)); - out->min = (int)((dt / (60*1000000000000LL)) % 60); - out->sec = (int)((dt / 1000000000000LL) % 60); - out->us = (int)((dt / 1000000LL) % 1000000LL); - out->ps = (int)(dt % 1000000LL); + days = extract_unit_64(&dt, 1000LL*1000*1000*1000*60*60*24); + set_datetimestruct_days(days, out); + out->hour = (int)extract_unit_64(&dt, 1000LL*1000*1000*1000*60*60); + out->min = (int)extract_unit_64(&dt, 1000LL*1000*1000*1000*60); + out->sec = (int)extract_unit_64(&dt, 1000LL*1000*1000*1000); + out->us = (int)extract_unit_64(&dt, 1000LL*1000); + out->ps = (int)(dt); break; case NPY_FR_fs: /* entire range is only +- 2.6 hours */ - if (dt >= 0) { - out->hour = (int)(dt / (60*60*1000000000000000LL)); - out->min = (int)((dt / (60*1000000000000000LL)) % 60); - out->sec = (int)((dt / 1000000000000000LL) % 60); - out->us = (int)((dt / 1000000000LL) % 1000000LL); - out->ps = (int)((dt / 1000LL) % 1000000LL); - out->as = (int)((dt % 1000LL) * 1000); - } - else { - npy_datetime minutes; - - minutes = dt / (60*1000000000000000LL); - dt = dt % (60*1000000000000000LL); - if (dt < 0) { - dt += (60*1000000000000000LL); - --minutes; - } - /* Offset the negative minutes */ - add_minutes_to_datetimestruct(out, minutes); - out->sec = (int)((dt / 1000000000000000LL) % 60); - out->us = (int)((dt / 1000000000LL) % 1000000LL); - out->ps = (int)((dt / 1000LL) % 1000000LL); - out->as = (int)((dt % 1000LL) * 1000); + out->hour = (int)extract_unit_64(&dt, 1000LL*1000*1000*1000*1000*60*60); + if (out->hour < 0) { + out->year = 1969; + out->month = 12; + out->day = 31; + out->hour += 24; + assert(out->hour >= 0); } + out->min = (int)extract_unit_64(&dt, 1000LL*1000*1000*1000*1000*60); + out->sec = (int)extract_unit_64(&dt, 1000LL*1000*1000*1000*1000); + out->us = (int)extract_unit_64(&dt, 1000LL*1000*1000); + out->ps = (int)extract_unit_64(&dt, 1000LL); + out->as = (int)(dt * 1000); break; case NPY_FR_as: /* entire range is only +- 9.2 seconds */ - if (dt >= 0) { - out->sec = (int)((dt / 1000000000000000000LL) % 60); - out->us = (int)((dt / 1000000000000LL) % 1000000LL); - out->ps = (int)((dt / 1000000LL) % 1000000LL); - out->as = (int)(dt % 1000000LL); - } - else { - npy_datetime seconds; - - seconds = dt / 1000000000000000000LL; - dt = dt % 1000000000000000000LL; - if (dt < 0) { - dt += 1000000000000000000LL; - --seconds; - } - /* Offset the negative seconds */ - add_seconds_to_datetimestruct(out, seconds); - out->us = (int)((dt / 1000000000000LL) % 1000000LL); - out->ps = (int)((dt / 1000000LL) % 1000000LL); - out->as = (int)(dt % 1000000LL); + out->sec = (int)extract_unit_64(&dt, 1000LL*1000*1000*1000*1000*1000); + if (out->sec < 0) { + out->year = 1969; + out->month = 12; + out->day = 31; + out->hour = 23; + out->min = 59; + out->sec += 60; + assert(out->sec >= 0); } + out->us = (int)extract_unit_64(&dt, 1000LL*1000*1000*1000); + out->ps = (int)extract_unit_64(&dt, 1000LL*1000); + out->as = (int)dt; break; default: @@ -655,8 +608,9 @@ convert_datetime_to_datetimestruct(PyArray_DatetimeMetaData *meta, * TO BE REMOVED - NOT USED INTERNALLY. */ NPY_NO_EXPORT void -PyArray_DatetimeToDatetimeStruct(npy_datetime val, NPY_DATETIMEUNIT fr, - npy_datetimestruct *result) +PyArray_DatetimeToDatetimeStruct( + npy_datetime NPY_UNUSED(val), NPY_DATETIMEUNIT NPY_UNUSED(fr), + npy_datetimestruct *result) { PyErr_SetString(PyExc_RuntimeError, "The NumPy PyArray_DatetimeToDatetimeStruct function has " @@ -676,8 +630,9 @@ PyArray_DatetimeToDatetimeStruct(npy_datetime val, NPY_DATETIMEUNIT fr, * TO BE REMOVED - NOT USED INTERNALLY. */ NPY_NO_EXPORT void -PyArray_TimedeltaToTimedeltaStruct(npy_timedelta val, NPY_DATETIMEUNIT fr, - npy_timedeltastruct *result) +PyArray_TimedeltaToTimedeltaStruct( + npy_timedelta NPY_UNUSED(val), NPY_DATETIMEUNIT NPY_UNUSED(fr), + npy_timedeltastruct *result) { PyErr_SetString(PyExc_RuntimeError, "The NumPy PyArray_TimedeltaToTimedeltaStruct function has " @@ -745,6 +700,14 @@ get_datetime_metadata_from_dtype(PyArray_Descr *dtype) return &(((PyArray_DatetimeDTypeMetaData *)dtype->c_metadata)->meta); } +/* strtol does not know whether to put a const qualifier on endptr, wrap + * it so we can put this cast in one place. + */ +NPY_NO_EXPORT long int +strtol_const(char const *str, char const **endptr, int base) { + return strtol(str, (char**)endptr, base); +} + /* * Converts a substring given by 'str' and 'len' into * a date time unit multiplier + enum value, which are populated @@ -755,18 +718,27 @@ get_datetime_metadata_from_dtype(PyArray_Descr *dtype) * Returns 0 on success, -1 on failure. */ NPY_NO_EXPORT int -parse_datetime_extended_unit_from_string(char *str, Py_ssize_t len, - char *metastr, +parse_datetime_extended_unit_from_string(char const *str, Py_ssize_t len, + char const *metastr, PyArray_DatetimeMetaData *out_meta) { - char *substr = str, *substrend = NULL; + char const *substr = str, *substrend = NULL; int den = 1; + npy_longlong true_meta_val; /* First comes an optional integer multiplier */ - out_meta->num = (int)strtol(substr, &substrend, 10); + out_meta->num = (int)strtol_const(substr, &substrend, 10); if (substr == substrend) { out_meta->num = 1; } + else { + // check for 32-bit integer overflow + char *endptr = NULL; + true_meta_val = NumPyOS_strtoll(substr, &endptr, 10); + if (true_meta_val > INT_MAX || true_meta_val < 0) { + goto bad_input; + } + } substr = substrend; /* Next comes the unit itself, followed by either '/' or the string end */ @@ -788,7 +760,7 @@ parse_datetime_extended_unit_from_string(char *str, Py_ssize_t len, /* Next comes an optional integer denominator */ if (substr-str < len && *substr == '/') { substr++; - den = (int)strtol(substr, &substrend, 10); + den = (int)strtol_const(substr, &substrend, 10); /* If the '/' exists, there must be a number followed by ']' */ if (substr == substrend || *substrend != ']') { goto bad_input; @@ -811,8 +783,8 @@ parse_datetime_extended_unit_from_string(char *str, Py_ssize_t len, bad_input: if (metastr != NULL) { PyErr_Format(PyExc_TypeError, - "Invalid datetime metadata string \"%s\" at position %d", - metastr, (int)(substr-metastr)); + "Invalid datetime metadata string \"%s\" at position %zd", + metastr, substr-metastr); } else { PyErr_Format(PyExc_TypeError, @@ -829,10 +801,10 @@ parse_datetime_extended_unit_from_string(char *str, Py_ssize_t len, * Returns 0 on success, -1 on failure. */ NPY_NO_EXPORT int -parse_datetime_metadata_from_metastr(char *metastr, Py_ssize_t len, +parse_datetime_metadata_from_metastr(char const *metastr, Py_ssize_t len, PyArray_DatetimeMetaData *out_meta) { - char *substr = metastr, *substrend = NULL; + char const *substr = metastr, *substrend = NULL; /* Treat the empty string as generic units */ if (len == 0) { @@ -873,8 +845,8 @@ parse_datetime_metadata_from_metastr(char *metastr, Py_ssize_t len, bad_input: if (substr != metastr) { PyErr_Format(PyExc_TypeError, - "Invalid datetime metadata string \"%s\" at position %d", - metastr, (int)(substr-metastr)); + "Invalid datetime metadata string \"%s\" at position %zd", + metastr, substr - metastr); } else { PyErr_Format(PyExc_TypeError, @@ -890,10 +862,10 @@ parse_datetime_metadata_from_metastr(char *metastr, Py_ssize_t len, * The "type" string should be NULL-terminated. */ NPY_NO_EXPORT PyArray_Descr * -parse_dtype_from_datetime_typestr(char *typestr, Py_ssize_t len) +parse_dtype_from_datetime_typestr(char const *typestr, Py_ssize_t len) { PyArray_DatetimeMetaData meta; - char *metastr = NULL; + char const *metastr = NULL; int is_timedelta = 0; Py_ssize_t metalen = 0; @@ -976,7 +948,7 @@ static NPY_DATETIMEUNIT _multiples_table[16][4] = { */ NPY_NO_EXPORT int convert_datetime_divisor_to_multiple(PyArray_DatetimeMetaData *meta, - int den, char *metastr) + int den, char const *metastr) { int i, num, ind; NPY_DATETIMEUNIT *totry; @@ -989,10 +961,6 @@ convert_datetime_divisor_to_multiple(PyArray_DatetimeMetaData *meta, return -1; } - ind = ((int)meta->base - (int)NPY_FR_Y)*2; - totry = _multiples_table[ind]; - baseunit = _multiples_table[ind + 1]; - num = 3; if (meta->base == NPY_FR_W) { num = 4; @@ -1001,6 +969,7 @@ convert_datetime_divisor_to_multiple(PyArray_DatetimeMetaData *meta, num = 2; } if (meta->base >= NPY_FR_s) { + /* _multiplies_table only has entries up to NPY_FR_s */ ind = ((int)NPY_FR_s - (int)NPY_FR_Y)*2; totry = _multiples_table[ind]; baseunit = _multiples_table[ind + 1]; @@ -1013,6 +982,11 @@ convert_datetime_divisor_to_multiple(PyArray_DatetimeMetaData *meta, num = 0; } } + else { + ind = ((int)meta->base - (int)NPY_FR_Y)*2; + totry = _multiples_table[ind]; + baseunit = _multiples_table[ind + 1]; + } for (i = 0; i < num; i++) { q = totry[i] / den; @@ -1196,7 +1170,7 @@ get_datetime_conversion_factor(PyArray_DatetimeMetaData *src_meta, } /* If something overflowed, make both num and denom 0 */ - if (denom == 0 || num == 0) { + if (num == 0) { PyErr_Format(PyExc_OverflowError, "Integer overflow while computing the conversion " "factor between NumPy datetime units %s and %s", @@ -1479,18 +1453,20 @@ raise_if_datetime64_metadata_cast_error(char *object_type, return 0; } else { - PyObject *errmsg; - errmsg = PyUString_FromFormat("Cannot cast %s " - "from metadata ", object_type); - errmsg = append_metastr_to_string(src_meta, 0, errmsg); - PyUString_ConcatAndDel(&errmsg, - PyUString_FromString(" to ")); - errmsg = append_metastr_to_string(dst_meta, 0, errmsg); - PyUString_ConcatAndDel(&errmsg, - PyUString_FromFormat(" according to the rule %s", - npy_casting_to_string(casting))); - PyErr_SetObject(PyExc_TypeError, errmsg); - Py_DECREF(errmsg); + PyObject *src = metastr_to_unicode(src_meta, 0); + if (src == NULL) { + return -1; + } + PyObject *dst = metastr_to_unicode(dst_meta, 0); + if (dst == NULL) { + Py_DECREF(src); + return -1; + } + PyErr_Format(PyExc_TypeError, + "Cannot cast %s from metadata %S to %S according to the rule %s", + object_type, src, dst, npy_casting_to_string(casting)); + Py_DECREF(src); + Py_DECREF(dst); return -1; } } @@ -1511,18 +1487,20 @@ raise_if_timedelta64_metadata_cast_error(char *object_type, return 0; } else { - PyObject *errmsg; - errmsg = PyUString_FromFormat("Cannot cast %s " - "from metadata ", object_type); - errmsg = append_metastr_to_string(src_meta, 0, errmsg); - PyUString_ConcatAndDel(&errmsg, - PyUString_FromString(" to ")); - errmsg = append_metastr_to_string(dst_meta, 0, errmsg); - PyUString_ConcatAndDel(&errmsg, - PyUString_FromFormat(" according to the rule %s", - npy_casting_to_string(casting))); - PyErr_SetObject(PyExc_TypeError, errmsg); - Py_DECREF(errmsg); + PyObject *src = metastr_to_unicode(src_meta, 0); + if (src == NULL) { + return -1; + } + PyObject *dst = metastr_to_unicode(dst_meta, 0); + if (dst == NULL) { + Py_DECREF(src); + return -1; + } + PyErr_Format(PyExc_TypeError, + "Cannot cast %s from metadata %S to %S according to the rule %s", + object_type, src, dst, npy_casting_to_string(casting)); + Py_DECREF(src); + Py_DECREF(dst); return -1; } } @@ -1645,32 +1623,38 @@ compute_datetime_metadata_greatest_common_divisor( return 0; incompatible_units: { - PyObject *errmsg; - errmsg = PyUString_FromString("Cannot get " - "a common metadata divisor for " - "NumPy datetime metadata "); - errmsg = append_metastr_to_string(meta1, 0, errmsg); - PyUString_ConcatAndDel(&errmsg, - PyUString_FromString(" and ")); - errmsg = append_metastr_to_string(meta2, 0, errmsg); - PyUString_ConcatAndDel(&errmsg, - PyUString_FromString(" because they have " - "incompatible nonlinear base time units")); - PyErr_SetObject(PyExc_TypeError, errmsg); - Py_DECREF(errmsg); + PyObject *umeta1 = metastr_to_unicode(meta1, 0); + if (umeta1 == NULL) { + return -1; + } + PyObject *umeta2 = metastr_to_unicode(meta2, 0); + if (umeta2 == NULL) { + Py_DECREF(umeta1); + return -1; + } + PyErr_Format(PyExc_TypeError, + "Cannot get a common metadata divisor for Numpy datatime " + "metadata %S and %S because they have incompatible nonlinear " + "base time units.", umeta1, umeta2); + Py_DECREF(umeta1); + Py_DECREF(umeta2); return -1; } units_overflow: { - PyObject *errmsg; - errmsg = PyUString_FromString("Integer overflow " - "getting a common metadata divisor for " - "NumPy datetime metadata "); - errmsg = append_metastr_to_string(meta1, 0, errmsg); - PyUString_ConcatAndDel(&errmsg, - PyUString_FromString(" and ")); - errmsg = append_metastr_to_string(meta2, 0, errmsg); - PyErr_SetObject(PyExc_OverflowError, errmsg); - Py_DECREF(errmsg); + PyObject *umeta1 = metastr_to_unicode(meta1, 0); + if (umeta1 == NULL) { + return -1; + } + PyObject *umeta2 = metastr_to_unicode(meta2, 0); + if (umeta2 == NULL) { + Py_DECREF(umeta1); + return -1; + } + PyErr_Format(PyExc_OverflowError, + "Integer overflow getting a common metadata divisor for " + "NumPy datetime metadata %S and %S.", umeta1, umeta2); + Py_DECREF(umeta1); + Py_DECREF(umeta2); return -1; } } @@ -1724,7 +1708,7 @@ datetime_type_promotion(PyArray_Descr *type1, PyArray_Descr *type2) * Returns NPY_DATETIMEUNIT on success, NPY_FR_ERROR on failure. */ NPY_NO_EXPORT NPY_DATETIMEUNIT -parse_datetime_unit_from_string(char *str, Py_ssize_t len, char *metastr) +parse_datetime_unit_from_string(char const *str, Py_ssize_t len, char const *metastr) { /* Use switch statements so the compiler can make it fast */ if (len == 1) { @@ -1762,6 +1746,10 @@ parse_datetime_unit_from_string(char *str, Py_ssize_t len, char *metastr) return NPY_FR_as; } } + else if (len == 3 && !strncmp(str, "\xce\xbcs", 3)) { + /* greek small letter mu, utf8-encoded */ + return NPY_FR_us; + } else if (len == 7 && !strncmp(str, "generic", 7)) { return NPY_FR_GENERIC; } @@ -1792,9 +1780,9 @@ convert_datetime_metadata_to_tuple(PyArray_DatetimeMetaData *meta) } PyTuple_SET_ITEM(dt_tuple, 0, - PyUString_FromString(_datetime_strings[meta->base])); + PyUnicode_FromString(_datetime_strings[meta->base])); PyTuple_SET_ITEM(dt_tuple, 1, - PyInt_FromLong(meta->num)); + PyLong_FromLong(meta->num)); return dt_tuple; } @@ -1809,22 +1797,16 @@ convert_datetime_metadata_tuple_to_datetime_metadata(PyObject *tuple, PyArray_DatetimeMetaData *out_meta, npy_bool from_pickle) { - char *basestr = NULL; - Py_ssize_t len = 0, tuple_size; int den = 1; - PyObject *unit_str = NULL; if (!PyTuple_Check(tuple)) { - PyObject *errmsg; - errmsg = PyUString_FromString("Require tuple for tuple to NumPy " - "datetime metadata conversion, not "); - PyUString_ConcatAndDel(&errmsg, PyObject_Repr(tuple)); - PyErr_SetObject(PyExc_TypeError, errmsg); - Py_DECREF(errmsg); + PyErr_Format(PyExc_TypeError, + "Require tuple for tuple to NumPy " + "datetime metadata conversion, not %R", tuple); return -1; } - tuple_size = PyTuple_GET_SIZE(tuple); + Py_ssize_t tuple_size = PyTuple_GET_SIZE(tuple); if (tuple_size < 2 || tuple_size > 4) { PyErr_SetString(PyExc_TypeError, "Require tuple of size 2 to 4 for " @@ -1832,18 +1814,22 @@ convert_datetime_metadata_tuple_to_datetime_metadata(PyObject *tuple, return -1; } - unit_str = PyTuple_GET_ITEM(tuple, 0); - Py_INCREF(unit_str); - if (PyUnicode_Check(unit_str)) { - /* Allow unicode format strings: convert to bytes */ - PyObject *tmp = PyUnicode_AsASCIIString(unit_str); - Py_DECREF(unit_str); + PyObject *unit_str = PyTuple_GET_ITEM(tuple, 0); + if (PyBytes_Check(unit_str)) { + /* Allow bytes format strings: convert to unicode */ + PyObject *tmp = PyUnicode_FromEncodedObject(unit_str, NULL, NULL); if (tmp == NULL) { return -1; } unit_str = tmp; } - if (PyBytes_AsStringAndSize(unit_str, &basestr, &len) < 0) { + else { + Py_INCREF(unit_str); + } + + Py_ssize_t len; + char const *basestr = PyUnicode_AsUTF8AndSize(unit_str, &len); + if (basestr == NULL) { Py_DECREF(unit_str); return -1; } @@ -1857,7 +1843,7 @@ convert_datetime_metadata_tuple_to_datetime_metadata(PyObject *tuple, Py_DECREF(unit_str); /* Convert the values to longs */ - out_meta->num = PyInt_AsLong(PyTuple_GET_ITEM(tuple, 1)); + out_meta->num = PyLong_AsLong(PyTuple_GET_ITEM(tuple, 1)); if (error_converting(out_meta->num)) { return -1; } @@ -1882,11 +1868,11 @@ convert_datetime_metadata_tuple_to_datetime_metadata(PyObject *tuple, if (from_pickle) { /* if (event == 1) */ PyObject *one = PyLong_FromLong(1); - int equal_one; if (one == NULL) { return -1; } - equal_one = PyObject_RichCompareBool(event, one, Py_EQ); + int equal_one = PyObject_RichCompareBool(event, one, Py_EQ); + Py_DECREF(one); if (equal_one == -1) { return -1; } @@ -1912,7 +1898,7 @@ convert_datetime_metadata_tuple_to_datetime_metadata(PyObject *tuple, return -1; } } - den = PyInt_AsLong(PyTuple_GET_ITEM(tuple, 2)); + den = PyLong_AsLong(PyTuple_GET_ITEM(tuple, 2)); if (error_converting(den)) { return -1; } @@ -1944,26 +1930,23 @@ NPY_NO_EXPORT int convert_pyobject_to_datetime_metadata(PyObject *obj, PyArray_DatetimeMetaData *out_meta) { - PyObject *ascii = NULL; - char *str = NULL; - Py_ssize_t len = 0; - if (PyTuple_Check(obj)) { return convert_datetime_metadata_tuple_to_datetime_metadata( obj, out_meta, NPY_FALSE); } - /* Get an ASCII string */ - if (PyUnicode_Check(obj)) { - /* Allow unicode format strings: convert to bytes */ - ascii = PyUnicode_AsASCIIString(obj); - if (ascii == NULL) { + /* Get a UTF8 string */ + PyObject *utf8 = NULL; + if (PyBytes_Check(obj)) { + /* Allow bytes format strings: convert to unicode */ + utf8 = PyUnicode_FromEncodedObject(obj, NULL, NULL); + if (utf8 == NULL) { return -1; } } - else if (PyBytes_Check(obj)) { - ascii = obj; - Py_INCREF(ascii); + else if (PyUnicode_Check(obj)) { + utf8 = obj; + Py_INCREF(utf8); } else { PyErr_SetString(PyExc_TypeError, @@ -1971,58 +1954,52 @@ convert_pyobject_to_datetime_metadata(PyObject *obj, return -1; } - if (PyBytes_AsStringAndSize(ascii, &str, &len) < 0) { - Py_DECREF(ascii); + Py_ssize_t len = 0; + char const *str = PyUnicode_AsUTF8AndSize(utf8, &len); + if (str == NULL) { + Py_DECREF(utf8); return -1; } if (len > 0 && str[0] == '[') { int r = parse_datetime_metadata_from_metastr(str, len, out_meta); - Py_DECREF(ascii); + Py_DECREF(utf8); return r; } else { if (parse_datetime_extended_unit_from_string(str, len, NULL, out_meta) < 0) { - Py_DECREF(ascii); + Py_DECREF(utf8); return -1; } - Py_DECREF(ascii); + Py_DECREF(utf8); return 0; } } /* - * 'ret' is a PyUString containing the datetime string, and this - * function appends the metadata string to it. + * Return the datetime metadata as a Unicode object. + * + * Returns new reference, NULL on error. * * If 'skip_brackets' is true, skips the '[]'. * - * This function steals the reference 'ret' */ NPY_NO_EXPORT PyObject * -append_metastr_to_string(PyArray_DatetimeMetaData *meta, - int skip_brackets, - PyObject *ret) +metastr_to_unicode(PyArray_DatetimeMetaData *meta, int skip_brackets) { - PyObject *res; int num; - char *basestr; - - if (ret == NULL) { - return NULL; - } + char const *basestr; if (meta->base == NPY_FR_GENERIC) { /* Without brackets, give a string "generic" */ if (skip_brackets) { - PyUString_ConcatAndDel(&ret, PyUString_FromString("generic")); - return ret; + return PyUnicode_FromString("generic"); } - /* But with brackets, append nothing */ + /* But with brackets, return nothing */ else { - return ret; + return PyUnicode_FromString(""); } } @@ -2038,25 +2015,23 @@ append_metastr_to_string(PyArray_DatetimeMetaData *meta, if (num == 1) { if (skip_brackets) { - res = PyUString_FromFormat("%s", basestr); + return PyUnicode_FromFormat("%s", basestr); } else { - res = PyUString_FromFormat("[%s]", basestr); + return PyUnicode_FromFormat("[%s]", basestr); } } else { if (skip_brackets) { - res = PyUString_FromFormat("%d%s", num, basestr); + return PyUnicode_FromFormat("%d%s", num, basestr); } else { - res = PyUString_FromFormat("[%d%s]", num, basestr); + return PyUnicode_FromFormat("[%d%s]", num, basestr); } } - - PyUString_ConcatAndDel(&ret, res); - return ret; } + /* * Adjusts a datetimestruct based on a seconds offset. Assumes * the current values are valid. @@ -2067,20 +2042,8 @@ add_seconds_to_datetimestruct(npy_datetimestruct *dts, int seconds) int minutes; dts->sec += seconds; - if (dts->sec < 0) { - minutes = dts->sec / 60; - dts->sec = dts->sec % 60; - if (dts->sec < 0) { - --minutes; - dts->sec += 60; - } - add_minutes_to_datetimestruct(dts, minutes); - } - else if (dts->sec >= 60) { - minutes = dts->sec / 60; - dts->sec = dts->sec % 60; - add_minutes_to_datetimestruct(dts, minutes); - } + minutes = extract_unit_32(&dts->sec, 60); + add_minutes_to_datetimestruct(dts, minutes); } /* @@ -2092,28 +2055,13 @@ add_minutes_to_datetimestruct(npy_datetimestruct *dts, int minutes) { int isleap; - /* MINUTES */ dts->min += minutes; - while (dts->min < 0) { - dts->min += 60; - dts->hour--; - } - while (dts->min >= 60) { - dts->min -= 60; - dts->hour++; - } - /* HOURS */ - while (dts->hour < 0) { - dts->hour += 24; - dts->day--; - } - while (dts->hour >= 24) { - dts->hour -= 24; - dts->day++; - } + /* propagate invalid minutes into hour and day changes */ + dts->hour += extract_unit_32(&dts->min, 60); + dts->day += extract_unit_32(&dts->hour, 24); - /* DAYS */ + /* propagate invalid days into month and year changes */ if (dts->day < 1) { dts->month--; if (dts->month < 1) { @@ -2179,7 +2127,7 @@ convert_pydatetime_to_datetimestruct(PyObject *obj, npy_datetimestruct *out, if (tmp == NULL) { return -1; } - out->year = PyInt_AsLong(tmp); + out->year = PyLong_AsLong(tmp); if (error_converting(out->year)) { Py_DECREF(tmp); return -1; @@ -2191,7 +2139,7 @@ convert_pydatetime_to_datetimestruct(PyObject *obj, npy_datetimestruct *out, if (tmp == NULL) { return -1; } - out->month = PyInt_AsLong(tmp); + out->month = PyLong_AsLong(tmp); if (error_converting(out->month)) { Py_DECREF(tmp); return -1; @@ -2203,7 +2151,7 @@ convert_pydatetime_to_datetimestruct(PyObject *obj, npy_datetimestruct *out, if (tmp == NULL) { return -1; } - out->day = PyInt_AsLong(tmp); + out->day = PyLong_AsLong(tmp); if (error_converting(out->day)) { Py_DECREF(tmp); return -1; @@ -2237,7 +2185,7 @@ convert_pydatetime_to_datetimestruct(PyObject *obj, npy_datetimestruct *out, if (tmp == NULL) { return -1; } - out->hour = PyInt_AsLong(tmp); + out->hour = PyLong_AsLong(tmp); if (error_converting(out->hour)) { Py_DECREF(tmp); return -1; @@ -2249,7 +2197,7 @@ convert_pydatetime_to_datetimestruct(PyObject *obj, npy_datetimestruct *out, if (tmp == NULL) { return -1; } - out->min = PyInt_AsLong(tmp); + out->min = PyLong_AsLong(tmp); if (error_converting(out->min)) { Py_DECREF(tmp); return -1; @@ -2261,7 +2209,7 @@ convert_pydatetime_to_datetimestruct(PyObject *obj, npy_datetimestruct *out, if (tmp == NULL) { return -1; } - out->sec = PyInt_AsLong(tmp); + out->sec = PyLong_AsLong(tmp); if (error_converting(out->sec)) { Py_DECREF(tmp); return -1; @@ -2273,7 +2221,7 @@ convert_pydatetime_to_datetimestruct(PyObject *obj, npy_datetimestruct *out, if (tmp == NULL) { return -1; } - out->us = PyInt_AsLong(tmp); + out->us = PyLong_AsLong(tmp); if (error_converting(out->us)) { Py_DECREF(tmp); return -1; @@ -2305,6 +2253,7 @@ convert_pydatetime_to_datetimestruct(PyObject *obj, npy_datetimestruct *out, if (DEPRECATE( "parsing timezone aware datetimes is deprecated; " "this will raise an error in the future") < 0) { + Py_DECREF(tmp); return -1; } @@ -2321,10 +2270,14 @@ convert_pydatetime_to_datetimestruct(PyObject *obj, npy_datetimestruct *out, * which contains the value we want. */ tmp = PyObject_CallMethod(offset, "total_seconds", ""); + Py_DECREF(offset); if (tmp == NULL) { return -1; } - seconds_offset = PyInt_AsLong(tmp); + /* Rounding here is no worse than the integer division below. + * Only whole minute offsets are supported by numpy anyway. + */ + seconds_offset = (int)PyFloat_AsDouble(tmp); if (error_converting(seconds_offset)) { Py_DECREF(tmp); return -1; @@ -2347,15 +2300,15 @@ convert_pydatetime_to_datetimestruct(PyObject *obj, npy_datetimestruct *out, invalid_date: PyErr_Format(PyExc_ValueError, - "Invalid date (%d,%d,%d) when converting to NumPy datetime", - (int)out->year, (int)out->month, (int)out->day); + "Invalid date (%" NPY_INT64_FMT ",%" NPY_INT32_FMT ",%" NPY_INT32_FMT ") when converting to NumPy datetime", + out->year, out->month, out->day); return -1; invalid_time: PyErr_Format(PyExc_ValueError, - "Invalid time (%d,%d,%d,%d) when converting " + "Invalid time (%" NPY_INT32_FMT ",%" NPY_INT32_FMT ",%" NPY_INT32_FMT ",%" NPY_INT32_FMT ") when converting " "to NumPy datetime", - (int)out->hour, (int)out->min, (int)out->sec, (int)out->us); + out->hour, out->min, out->sec, out->us); return -1; } @@ -2416,32 +2369,33 @@ convert_pyobject_to_datetime(PyArray_DatetimeMetaData *meta, PyObject *obj, NPY_CASTING casting, npy_datetime *out) { if (PyBytes_Check(obj) || PyUnicode_Check(obj)) { - PyObject *bytes = NULL; - char *str = NULL; - Py_ssize_t len = 0; - npy_datetimestruct dts; - NPY_DATETIMEUNIT bestunit = NPY_FR_ERROR; + PyObject *utf8 = NULL; - /* Convert to an ASCII string for the date parser */ - if (PyUnicode_Check(obj)) { - bytes = PyUnicode_AsASCIIString(obj); - if (bytes == NULL) { + /* Convert to an UTF8 string for the date parser */ + if (PyBytes_Check(obj)) { + utf8 = PyUnicode_FromEncodedObject(obj, NULL, NULL); + if (utf8 == NULL) { return -1; } } else { - bytes = obj; - Py_INCREF(bytes); + utf8 = obj; + Py_INCREF(utf8); } - if (PyBytes_AsStringAndSize(bytes, &str, &len) < 0) { - Py_DECREF(bytes); + + Py_ssize_t len = 0; + char const *str = PyUnicode_AsUTF8AndSize(utf8, &len); + if (str == NULL) { + Py_DECREF(utf8); return -1; } /* Parse the ISO date */ + npy_datetimestruct dts; + NPY_DATETIMEUNIT bestunit = NPY_FR_ERROR; if (parse_iso_8601_datetime(str, len, meta->base, casting, &dts, &bestunit, NULL) < 0) { - Py_DECREF(bytes); + Py_DECREF(utf8); return -1; } @@ -2452,15 +2406,15 @@ convert_pyobject_to_datetime(PyArray_DatetimeMetaData *meta, PyObject *obj, } if (convert_datetimestruct_to_datetime(meta, &dts, out) < 0) { - Py_DECREF(bytes); + Py_DECREF(utf8); return -1; } - Py_DECREF(bytes); + Py_DECREF(utf8); return 0; } /* Do no conversion on raw integers */ - else if (PyInt_Check(obj) || PyLong_Check(obj)) { + else if (PyLong_Check(obj)) { /* Don't allow conversion from an integer without specifying a unit */ if (meta->base == NPY_FR_ERROR || meta->base == NPY_FR_GENERIC) { PyErr_SetString(PyExc_ValueError, "Converting an integer to a " @@ -2468,6 +2422,9 @@ convert_pyobject_to_datetime(PyArray_DatetimeMetaData *meta, PyObject *obj, return -1; } *out = PyLong_AsLongLong(obj); + if (error_converting(*out)) { + return -1; + } return 0; } /* Datetime scalar */ @@ -2607,24 +2564,25 @@ convert_pyobject_to_timedelta(PyArray_DatetimeMetaData *meta, PyObject *obj, NPY_CASTING casting, npy_timedelta *out) { if (PyBytes_Check(obj) || PyUnicode_Check(obj)) { - PyObject *bytes = NULL; - char *str = NULL; - Py_ssize_t len = 0; + PyObject *utf8 = NULL; int succeeded = 0; - /* Convert to an ASCII string for the date parser */ - if (PyUnicode_Check(obj)) { - bytes = PyUnicode_AsASCIIString(obj); - if (bytes == NULL) { + /* Convert to an UTF8 string for the date parser */ + if (PyBytes_Check(obj)) { + utf8 = PyUnicode_FromEncodedObject(obj, NULL, NULL); + if (utf8 == NULL) { return -1; } } else { - bytes = obj; - Py_INCREF(bytes); + utf8 = obj; + Py_INCREF(utf8); } - if (PyBytes_AsStringAndSize(bytes, &str, &len) < 0) { - Py_DECREF(bytes); + + Py_ssize_t len = 0; + char const *str = PyUnicode_AsUTF8AndSize(utf8, &len); + if (str == NULL) { + Py_DECREF(utf8); return -1; } @@ -2645,7 +2603,7 @@ convert_pyobject_to_timedelta(PyArray_DatetimeMetaData *meta, PyObject *obj, succeeded = 1; } } - Py_DECREF(bytes); + Py_DECREF(utf8); if (succeeded) { /* Use generic units if none was specified */ @@ -2658,7 +2616,7 @@ convert_pyobject_to_timedelta(PyArray_DatetimeMetaData *meta, PyObject *obj, } } /* Do no conversion on raw integers */ - else if (PyInt_Check(obj) || PyLong_Check(obj)) { + else if (PyLong_Check(obj)) { /* Use the default unit if none was specified */ if (meta->base == NPY_FR_ERROR) { meta->base = NPY_DATETIME_DEFAULTUNIT; @@ -2666,6 +2624,9 @@ convert_pyobject_to_timedelta(PyArray_DatetimeMetaData *meta, PyObject *obj, } *out = PyLong_AsLongLong(obj); + if (error_converting(*out)) { + return -1; + } return 0; } /* Timedelta scalar */ @@ -2759,7 +2720,7 @@ convert_pyobject_to_timedelta(PyArray_DatetimeMetaData *meta, PyObject *obj, if (tmp == NULL) { return -1; } - seconds = PyInt_AsLong(tmp); + seconds = PyLong_AsLong(tmp); if (error_converting(seconds)) { Py_DECREF(tmp); return -1; @@ -2771,7 +2732,7 @@ convert_pyobject_to_timedelta(PyArray_DatetimeMetaData *meta, PyObject *obj, if (tmp == NULL) { return -1; } - useconds = PyInt_AsLong(tmp); + useconds = PyLong_AsLong(tmp); if (error_converting(useconds)) { Py_DECREF(tmp); return -1; @@ -2845,6 +2806,19 @@ convert_pyobject_to_timedelta(PyArray_DatetimeMetaData *meta, PyObject *obj, *out = NPY_DATETIME_NAT; return 0; } + else if (PyArray_IsScalar(obj, Integer)) { + /* Use the default unit if none was specified */ + if (meta->base == NPY_FR_ERROR) { + meta->base = NPY_DATETIME_DEFAULTUNIT; + meta->num = 1; + } + + *out = PyLong_AsLongLong(obj); + if (error_converting(*out)) { + return -1; + } + return 0; + } else { PyErr_SetString(PyExc_ValueError, "Could not convert object to NumPy timedelta"); @@ -2916,7 +2890,6 @@ convert_datetime_to_pyobject(npy_datetime dt, PyArray_DatetimeMetaData *meta) NPY_NO_EXPORT PyObject * convert_timedelta_to_pyobject(npy_timedelta td, PyArray_DatetimeMetaData *meta) { - PyObject *ret = NULL; npy_timedelta value; int days = 0, seconds = 0, useconds = 0; @@ -2946,54 +2919,47 @@ convert_timedelta_to_pyobject(npy_timedelta td, PyArray_DatetimeMetaData *meta) /* Convert to days/seconds/useconds */ switch (meta->base) { case NPY_FR_W: - value *= 7; + days = value * 7; break; case NPY_FR_D: + days = value; break; case NPY_FR_h: - seconds = (int)((value % 24) * (60*60)); - value = value / 24; + days = extract_unit_64(&value, 24ULL); + seconds = value*60*60; break; case NPY_FR_m: - seconds = (int)(value % (24*60)) * 60; - value = value / (24*60); + days = extract_unit_64(&value, 60ULL*24); + seconds = value*60; break; case NPY_FR_s: - seconds = (int)(value % (24*60*60)); - value = value / (24*60*60); + days = extract_unit_64(&value, 60ULL*60*24); + seconds = value; break; case NPY_FR_ms: - useconds = (int)(value % 1000) * 1000; - value = value / 1000; - seconds = (int)(value % (24*60*60)); - value = value / (24*60*60); + days = extract_unit_64(&value, 1000ULL*60*60*24); + seconds = extract_unit_64(&value, 1000ULL); + useconds = value*1000; break; case NPY_FR_us: - useconds = (int)(value % (1000*1000)); - value = value / (1000*1000); - seconds = (int)(value % (24*60*60)); - value = value / (24*60*60); + days = extract_unit_64(&value, 1000ULL*1000*60*60*24); + seconds = extract_unit_64(&value, 1000ULL*1000); + useconds = value; break; default: + // unreachable, handled by the `if` above + assert(NPY_FALSE); break; } /* - * 'value' represents days, and seconds/useconds are filled. - * * If it would overflow the datetime.timedelta days, return a raw int */ - if (value < -999999999 || value > 999999999) { + if (days < -999999999 || days > 999999999) { return PyLong_FromLongLong(td); } else { - days = (int)value; - ret = PyDelta_FromDSU(days, seconds, useconds); - if (ret == NULL) { - return NULL; - } + return PyDelta_FromDSU(days, seconds, useconds); } - - return ret; } /* @@ -3158,7 +3124,7 @@ is_any_numpy_datetime_or_timedelta(PyObject *obj) */ NPY_NO_EXPORT int convert_pyobjects_to_datetimes(int count, - PyObject **objs, int *type_nums, + PyObject **objs, const int *type_nums, NPY_CASTING casting, npy_int64 *out_values, PyArray_DatetimeMetaData *inout_meta) @@ -3284,18 +3250,6 @@ NPY_NO_EXPORT PyArrayObject * datetime_arange(PyObject *start, PyObject *stop, PyObject *step, PyArray_Descr *dtype) { - PyArray_DatetimeMetaData meta; - /* - * Both datetime and timedelta are stored as int64, so they can - * share value variables. - */ - npy_int64 values[3]; - PyObject *objs[3]; - int type_nums[3]; - - npy_intp i, length; - PyArrayObject *ret; - npy_int64 *ret_data; /* * First normalize the input parameters so there is no Py_None, @@ -3328,6 +3282,8 @@ datetime_arange(PyObject *start, PyObject *stop, PyObject *step, /* Check if the units of the given dtype are generic, in which * case we use the code path that detects the units */ + int type_nums[3]; + PyArray_DatetimeMetaData meta; if (dtype != NULL) { PyArray_DatetimeMetaData *meta_tmp; @@ -3376,6 +3332,7 @@ datetime_arange(PyObject *start, PyObject *stop, PyObject *step, } /* Set up to convert the objects to a common datetime unit metadata */ + PyObject *objs[3]; objs[0] = start; objs[1] = stop; objs[2] = step; @@ -3384,8 +3341,7 @@ datetime_arange(PyObject *start, PyObject *stop, PyObject *step, type_nums[2] = NPY_TIMEDELTA; } else { - if (PyInt_Check(objs[1]) || - PyLong_Check(objs[1]) || + if (PyLong_Check(objs[1]) || PyArray_IsScalar(objs[1], Integer) || is_any_numpy_timedelta(objs[1])) { type_nums[1] = NPY_TIMEDELTA; @@ -3396,11 +3352,22 @@ datetime_arange(PyObject *start, PyObject *stop, PyObject *step, type_nums[2] = NPY_TIMEDELTA; } - /* Convert all the arguments */ + /* Convert all the arguments + * + * Both datetime and timedelta are stored as int64, so they can + * share value variables. + */ + npy_int64 values[3]; if (convert_pyobjects_to_datetimes(3, objs, type_nums, NPY_SAME_KIND_CASTING, values, &meta) < 0) { return NULL; } + /* If no start was provided, default to 0 */ + if (start == NULL) { + /* enforced above */ + assert(type_nums[0] == NPY_TIMEDELTA); + values[0] = 0; + } /* If no step was provided, default to 1 */ if (step == NULL) { @@ -3425,6 +3392,7 @@ datetime_arange(PyObject *start, PyObject *stop, PyObject *step, } /* Calculate the array length */ + npy_intp length; if (values[2] > 0 && values[1] > values[0]) { length = (values[1] - values[0] + (values[2] - 1)) / values[2]; } @@ -3452,19 +3420,20 @@ datetime_arange(PyObject *start, PyObject *stop, PyObject *step, } /* Create the result array */ - ret = (PyArrayObject *)PyArray_NewFromDescr( - &PyArray_Type, dtype, 1, &length, NULL, - NULL, 0, NULL); + PyArrayObject *ret = (PyArrayObject *)PyArray_NewFromDescr( + &PyArray_Type, dtype, 1, &length, NULL, + NULL, 0, NULL); + if (ret == NULL) { return NULL; } if (length > 0) { /* Extract the data pointer */ - ret_data = (npy_int64 *)PyArray_DATA(ret); + npy_int64 *ret_data = (npy_int64 *)PyArray_DATA(ret); /* Create the timedeltas or datetimes */ - for (i = 0; i < length; ++i) { + for (npy_intp i = 0; i < length; ++i) { *ret_data = values[0]; values[0] += values[2]; ret_data++; @@ -3480,7 +3449,7 @@ datetime_arange(PyObject *start, PyObject *stop, PyObject *step, * * Returns 0 on success, -1 on failure. */ -static int +NPY_NO_EXPORT int find_string_array_datetime64_type(PyArrayObject *arr, PyArray_DatetimeMetaData *meta) { @@ -3603,44 +3572,9 @@ find_string_array_datetime64_type(PyArrayObject *arr, * Returns 0 on success, -1 on failure. */ static int -recursive_find_object_datetime64_type(PyObject *obj, - PyArray_DatetimeMetaData *meta) +find_object_datetime64_meta(PyObject *obj, PyArray_DatetimeMetaData *meta) { - /* Array -> use its metadata */ - if (PyArray_Check(obj)) { - PyArrayObject *arr = (PyArrayObject *)obj; - PyArray_Descr *arr_dtype = PyArray_DESCR(arr); - - if (arr_dtype->type_num == NPY_STRING || - arr_dtype->type_num == NPY_UNICODE) { - return find_string_array_datetime64_type(arr, meta); - } - /* If the array has metadata, use it */ - else if (arr_dtype->type_num == NPY_DATETIME || - arr_dtype->type_num == NPY_TIMEDELTA) { - PyArray_DatetimeMetaData *tmp_meta; - - /* Get the metadata from the type */ - tmp_meta = get_datetime_metadata_from_dtype(arr_dtype); - if (tmp_meta == NULL) { - return -1; - } - - /* Combine it with 'meta' */ - if (compute_datetime_metadata_greatest_common_divisor(meta, - tmp_meta, meta, 0, 0) < 0) { - return -1; - } - - return 0; - } - /* If it's not an object array, stop looking */ - else if (arr_dtype->type_num != NPY_OBJECT) { - return 0; - } - } - /* Datetime scalar -> use its metadata */ - else if (PyArray_IsScalar(obj, Datetime)) { + if (PyArray_IsScalar(obj, Datetime)) { PyDatetimeScalarObject *dts = (PyDatetimeScalarObject *)obj; /* Combine it with 'meta' */ @@ -3712,34 +3646,6 @@ recursive_find_object_datetime64_type(PyObject *obj, return 0; } - - /* Now check if what we have left is a sequence for recursion */ - if (PySequence_Check(obj)) { - Py_ssize_t i, len = PySequence_Size(obj); - if (len < 0 && PyErr_Occurred()) { - return -1; - } - - for (i = 0; i < len; ++i) { - int ret; - PyObject *f = PySequence_GetItem(obj, i); - if (f == NULL) { - return -1; - } - if (Npy_EnterRecursiveCall(" in recursive_find_object_datetime64_type") != 0) { - Py_DECREF(f); - return -1; - } - ret = recursive_find_object_datetime64_type(f, meta); - Py_LeaveRecursiveCall(); - Py_DECREF(f); - if (ret < 0) { - return ret; - } - } - - return 0; - } /* Otherwise ignore it */ else { return 0; @@ -3747,45 +3653,36 @@ recursive_find_object_datetime64_type(PyObject *obj, } /* - * Recursively determines the metadata for an NPY_TIMEDELTA dtype. - * - * Returns 0 on success, -1 on failure. + * handler function for PyDelta values + * which may also be in a 0 dimensional + * NumPy array */ static int -recursive_find_object_timedelta64_type(PyObject *obj, - PyArray_DatetimeMetaData *meta) +delta_checker(PyArray_DatetimeMetaData *meta) { - /* Array -> use its metadata */ - if (PyArray_Check(obj)) { - PyArrayObject *arr = (PyArrayObject *)obj; - PyArray_Descr *arr_dtype = PyArray_DESCR(arr); - - /* If the array has metadata, use it */ - if (arr_dtype->type_num == NPY_DATETIME || - arr_dtype->type_num == NPY_TIMEDELTA) { - PyArray_DatetimeMetaData *tmp_meta; + PyArray_DatetimeMetaData tmp_meta; - /* Get the metadata from the type */ - tmp_meta = get_datetime_metadata_from_dtype(arr_dtype); - if (tmp_meta == NULL) { - return -1; - } + tmp_meta.base = NPY_FR_us; + tmp_meta.num = 1; - /* Combine it with 'meta' */ - if (compute_datetime_metadata_greatest_common_divisor(meta, - tmp_meta, meta, 0, 0) < 0) { - return -1; - } - - return 0; - } - /* If it's not an object array, stop looking */ - else if (arr_dtype->type_num != NPY_OBJECT) { - return 0; - } + /* Combine it with 'meta' */ + if (compute_datetime_metadata_greatest_common_divisor( + meta, &tmp_meta, meta, 0, 0) < 0) { + return -1; } + return 0; +} + +/* + * Recursively determines the metadata for an NPY_TIMEDELTA dtype. + * + * Returns 0 on success, -1 on failure. + */ +static int +find_object_timedelta64_meta(PyObject *obj, PyArray_DatetimeMetaData *meta) +{ /* Datetime scalar -> use its metadata */ - else if (PyArray_IsScalar(obj, Timedelta)) { + if (PyArray_IsScalar(obj, Timedelta)) { PyTimedeltaScalarObject *dts = (PyTimedeltaScalarObject *)obj; /* Combine it with 'meta' */ @@ -3803,46 +3700,7 @@ recursive_find_object_timedelta64_type(PyObject *obj, } /* Python timedelta object -> 'us' */ else if (PyDelta_Check(obj)) { - PyArray_DatetimeMetaData tmp_meta; - - tmp_meta.base = NPY_FR_us; - tmp_meta.num = 1; - - /* Combine it with 'meta' */ - if (compute_datetime_metadata_greatest_common_divisor(meta, - &tmp_meta, meta, 0, 0) < 0) { - return -1; - } - - return 0; - } - - /* Now check if what we have left is a sequence for recursion */ - if (PySequence_Check(obj)) { - Py_ssize_t i, len = PySequence_Size(obj); - if (len < 0 && PyErr_Occurred()) { - return -1; - } - - for (i = 0; i < len; ++i) { - int ret; - PyObject *f = PySequence_GetItem(obj, i); - if (f == NULL) { - return -1; - } - if (Npy_EnterRecursiveCall(" in recursive_find_object_timedelta64_type") != 0) { - Py_DECREF(f); - return -1; - } - ret = recursive_find_object_timedelta64_type(f, meta); - Py_LeaveRecursiveCall(); - Py_DECREF(f); - if (ret < 0) { - return ret; - } - } - - return 0; + return delta_checker(meta); } /* Otherwise ignore it */ else { @@ -3864,7 +3722,7 @@ find_object_datetime_type(PyObject *obj, int type_num) meta.num = 1; if (type_num == NPY_DATETIME) { - if (recursive_find_object_datetime64_type(obj, &meta) < 0) { + if (find_object_datetime64_meta(obj, &meta) < 0) { return NULL; } else { @@ -3872,7 +3730,7 @@ find_object_datetime_type(PyObject *obj, int type_num) } } else if (type_num == NPY_TIMEDELTA) { - if (recursive_find_object_timedelta64_type(obj, &meta) < 0) { + if (find_object_timedelta64_meta(obj, &meta) < 0) { return NULL; } else { @@ -3886,3 +3744,530 @@ find_object_datetime_type(PyObject *obj, int type_num) return NULL; } } + + + + +/* + * Describes casting within datetimes or timedelta + */ +static NPY_CASTING +time_to_time_resolve_descriptors( + PyArrayMethodObject *NPY_UNUSED(self), + PyArray_DTypeMeta *NPY_UNUSED(dtypes[2]), + PyArray_Descr *given_descrs[2], + PyArray_Descr *loop_descrs[2]) +{ + /* This is a within-dtype cast, which currently must handle byteswapping */ + Py_INCREF(given_descrs[0]); + loop_descrs[0] = given_descrs[0]; + if (given_descrs[1] == NULL) { + loop_descrs[1] = ensure_dtype_nbo(given_descrs[0]); + } + else { + Py_INCREF(given_descrs[1]); + loop_descrs[1] = given_descrs[1]; + } + + int is_timedelta = given_descrs[0]->type_num == NPY_TIMEDELTA; + + if (given_descrs[0] == given_descrs[1]) { + return NPY_NO_CASTING | _NPY_CAST_IS_VIEW; + } + + NPY_CASTING byteorder_may_allow_view = 0; + if (PyDataType_ISNOTSWAPPED(loop_descrs[0]) == + PyDataType_ISNOTSWAPPED(loop_descrs[1])) { + byteorder_may_allow_view = _NPY_CAST_IS_VIEW; + } + PyArray_DatetimeMetaData *meta1, *meta2; + meta1 = get_datetime_metadata_from_dtype(loop_descrs[0]); + assert(meta1 != NULL); + meta2 = get_datetime_metadata_from_dtype(loop_descrs[1]); + assert(meta2 != NULL); + + if ((meta1->base == meta2->base && meta1->num == meta2->num) || + // handle some common metric prefix conversions + // 1000 fold conversions + ((meta2->base >= 7) && (meta1->base - meta2->base == 1) + && ((meta1->num / meta2->num) == 1000)) || + // 10^6 fold conversions + ((meta2->base >= 7) && (meta1->base - meta2->base == 2) + && ((meta1->num / meta2->num) == 1000000)) || + // 10^9 fold conversions + ((meta2->base >= 7) && (meta1->base - meta2->base == 3) + && ((meta1->num / meta2->num) == 1000000000))) { + if (byteorder_may_allow_view) { + return NPY_NO_CASTING | byteorder_may_allow_view; + } + return NPY_EQUIV_CASTING; + } + else if (meta1->base == NPY_FR_GENERIC) { + return NPY_SAFE_CASTING | byteorder_may_allow_view; + } + else if (meta2->base == NPY_FR_GENERIC) { + /* TODO: This is actually an invalid cast (casting will error) */ + return NPY_UNSAFE_CASTING; + } + else if (is_timedelta && ( + /* jump between time units and date units is unsafe for timedelta */ + (meta1->base <= NPY_FR_M && meta2->base > NPY_FR_M) || + (meta1->base > NPY_FR_M && meta2->base <= NPY_FR_M))) { + return NPY_UNSAFE_CASTING; + } + else if (meta1->base <= meta2->base) { + /* Casting to a more precise unit is currently considered safe */ + if (datetime_metadata_divides(meta1, meta2, is_timedelta)) { + /* If it divides, we consider it to be a safe cast */ + return NPY_SAFE_CASTING; + } + else { + return NPY_SAME_KIND_CASTING; + } + } + return NPY_SAME_KIND_CASTING; +} + + +static int +time_to_time_get_loop( + PyArrayMethod_Context *context, + int aligned, int NPY_UNUSED(move_references), npy_intp *strides, + PyArrayMethod_StridedLoop **out_loop, NpyAuxData **out_transferdata, + NPY_ARRAYMETHOD_FLAGS *flags) +{ + int requires_wrap = 0; + int inner_aligned = aligned; + PyArray_Descr **descrs = context->descriptors; + *flags = NPY_METH_NO_FLOATINGPOINT_ERRORS; + + PyArray_DatetimeMetaData *meta1 = get_datetime_metadata_from_dtype(descrs[0]); + PyArray_DatetimeMetaData *meta2 = get_datetime_metadata_from_dtype(descrs[1]); + + if (meta1->base == meta2->base && meta1->num == meta2->num) { + /* + * If the metadata matches, use the low-level copy or copy-swap + * functions. (If they do not match, but swapping is necessary this + * path is hit recursively.) + */ + if (PyDataType_ISNOTSWAPPED(descrs[0]) == + PyDataType_ISNOTSWAPPED(descrs[1])) { + *out_loop = PyArray_GetStridedCopyFn( + aligned, strides[0], strides[1], NPY_SIZEOF_DATETIME); + } + else { + *out_loop = PyArray_GetStridedCopySwapFn( + aligned, strides[0], strides[1], NPY_SIZEOF_DATETIME); + } + return 0; + } + + if (!PyDataType_ISNOTSWAPPED(descrs[0]) || + !PyDataType_ISNOTSWAPPED(descrs[1])) { + inner_aligned = 1; + requires_wrap = 1; + } + if (get_nbo_cast_datetime_transfer_function( + inner_aligned, descrs[0], descrs[1], + out_loop, out_transferdata) == NPY_FAIL) { + return -1; + } + + if (!requires_wrap) { + return 0; + } + + PyArray_Descr *src_wrapped_dtype = ensure_dtype_nbo(descrs[0]); + PyArray_Descr *dst_wrapped_dtype = ensure_dtype_nbo(descrs[1]); + + int needs_api = 0; + int res = wrap_aligned_transferfunction( + aligned, 0, + strides[0], strides[1], + descrs[0], descrs[1], + src_wrapped_dtype, dst_wrapped_dtype, + out_loop, out_transferdata, &needs_api); + Py_DECREF(src_wrapped_dtype); + Py_DECREF(dst_wrapped_dtype); + + assert(needs_api == 0); + return res; +} + + +/* Handles datetime<->timedelta type resolution (both directions) */ +static NPY_CASTING +datetime_to_timedelta_resolve_descriptors( + PyArrayMethodObject *NPY_UNUSED(self), + PyArray_DTypeMeta *dtypes[2], + PyArray_Descr *given_descrs[2], + PyArray_Descr *loop_descrs[2]) +{ + loop_descrs[0] = ensure_dtype_nbo(given_descrs[0]); + if (loop_descrs[0] == NULL) { + return -1; + } + if (given_descrs[1] == NULL) { + PyArray_DatetimeMetaData *meta = get_datetime_metadata_from_dtype(given_descrs[0]); + assert(meta != NULL); + loop_descrs[1] = create_datetime_dtype(dtypes[1]->type_num, meta); + } + else { + loop_descrs[1] = ensure_dtype_nbo(given_descrs[1]); + } + if (loop_descrs[1] == NULL) { + Py_DECREF(loop_descrs[0]); + return -1; + } + /* + * Mostly NPY_UNSAFE_CASTING is not true, the cast will fail. + * TODO: Once ufuncs use dtype specific promotion rules, + * this is likely unnecessary + */ + return NPY_UNSAFE_CASTING; +} + + +/* In the current setup both strings and unicode casts support all outputs */ +static NPY_CASTING +time_to_string_resolve_descriptors( + PyArrayMethodObject *self, + PyArray_DTypeMeta *dtypes[2], + PyArray_Descr **given_descrs, + PyArray_Descr **loop_descrs) +{ + if (given_descrs[1] != NULL && dtypes[0]->type_num == NPY_DATETIME) { + /* + * At the time of writing, NumPy does not check the length here, + * but will error if filling fails. + */ + Py_INCREF(given_descrs[1]); + loop_descrs[1] = given_descrs[1]; + } + else { + /* Find the correct string length, possibly based on the unit */ + int size; + if (given_descrs[0]->type_num == NPY_DATETIME) { + PyArray_DatetimeMetaData *meta = get_datetime_metadata_from_dtype(given_descrs[0]); + assert(meta != NULL); + size = get_datetime_iso_8601_strlen(0, meta->base); + } + else { + /* + * This is arguably missing space for the unit, e.g. for: + * `np.timedelta64(1231234342124, 'ms')` + */ + size = 21; + } + if (dtypes[1]->type_num == NPY_UNICODE) { + size *= 4; + } + loop_descrs[1] = PyArray_DescrNewFromType(dtypes[1]->type_num); + if (loop_descrs[1] == NULL) { + return -1; + } + loop_descrs[1]->elsize = size; + } + + loop_descrs[0] = ensure_dtype_nbo(given_descrs[0]); + if (loop_descrs[0] == NULL) { + Py_DECREF(loop_descrs[1]); + return -1; + } + + return NPY_UNSAFE_CASTING; +} + +static int +datetime_to_string_get_loop( + PyArrayMethod_Context *context, + int aligned, int NPY_UNUSED(move_references), npy_intp *strides, + PyArrayMethod_StridedLoop **out_loop, NpyAuxData **out_transferdata, + NPY_ARRAYMETHOD_FLAGS *flags) +{ + PyArray_Descr **descrs = context->descriptors; + *flags = context->method->flags & NPY_METH_RUNTIME_FLAGS; + + if (descrs[1]->type_num == NPY_STRING) { + if (get_nbo_datetime_to_string_transfer_function( + descrs[0], descrs[1], + out_loop, out_transferdata) == NPY_FAIL) { + return -1; + } + } + else { + assert(descrs[1]->type_num == NPY_UNICODE); + int out_needs_api; + if (get_datetime_to_unicode_transfer_function( + aligned, strides[0], strides[1], descrs[0], descrs[1], + out_loop, out_transferdata, &out_needs_api) == NPY_FAIL) { + return -1; + } + } + return 0; +} + + +static NPY_CASTING +string_to_datetime_cast_resolve_descriptors( + PyArrayMethodObject *NPY_UNUSED(self), + PyArray_DTypeMeta *dtypes[2], + PyArray_Descr *given_descrs[2], + PyArray_Descr *loop_descrs[2]) +{ + if (given_descrs[1] == NULL) { + /* NOTE: This doesn't actually work, and will error during the cast */ + loop_descrs[1] = NPY_DT_CALL_default_descr(dtypes[1]); + if (loop_descrs[1] == NULL) { + return -1; + } + } + else { + loop_descrs[1] = ensure_dtype_nbo(given_descrs[1]); + if (loop_descrs[1] == NULL) { + return -1; + } + } + + /* We currently support byte-swapping, so any (unicode) string is OK */ + Py_INCREF(given_descrs[0]); + loop_descrs[0] = given_descrs[0]; + + return NPY_UNSAFE_CASTING; +} + + +static int +string_to_datetime_cast_get_loop( + PyArrayMethod_Context *context, + int aligned, int NPY_UNUSED(move_references), npy_intp *strides, + PyArrayMethod_StridedLoop **out_loop, NpyAuxData **out_transferdata, + NPY_ARRAYMETHOD_FLAGS *flags) +{ + PyArray_Descr **descrs = context->descriptors; + *flags = context->method->flags & NPY_METH_RUNTIME_FLAGS; + + if (descrs[0]->type_num == NPY_STRING) { + if (get_nbo_string_to_datetime_transfer_function( + descrs[0], descrs[1], out_loop, out_transferdata) == NPY_FAIL) { + return -1; + } + } + else { + assert(descrs[0]->type_num == NPY_UNICODE); + int out_needs_api; + if (get_unicode_to_datetime_transfer_function( + aligned, strides[0], strides[1], descrs[0], descrs[1], + out_loop, out_transferdata, &out_needs_api) == NPY_FAIL) { + return -1; + } + } + return 0; +} + + + +/* + * This registers the castingimpl for all datetime related casts. + */ +NPY_NO_EXPORT int +PyArray_InitializeDatetimeCasts() +{ + int result = -1; + + PyType_Slot slots[3]; + PyArray_DTypeMeta *dtypes[2]; + PyArrayMethod_Spec spec = { + .name = "datetime_casts", + .nin = 1, + .nout = 1, + .casting = NPY_UNSAFE_CASTING, + .flags = NPY_METH_SUPPORTS_UNALIGNED, + .slots = slots, + .dtypes = dtypes, + }; + slots[0].slot = NPY_METH_resolve_descriptors; + slots[0].pfunc = &time_to_time_resolve_descriptors; + slots[1].slot = NPY_METH_get_loop; + slots[1].pfunc = &time_to_time_get_loop; + slots[2].slot = 0; + slots[2].pfunc = NULL; + + PyArray_DTypeMeta *datetime = PyArray_DTypeFromTypeNum(NPY_DATETIME); + PyArray_DTypeMeta *timedelta = PyArray_DTypeFromTypeNum(NPY_TIMEDELTA); + PyArray_DTypeMeta *string = PyArray_DTypeFromTypeNum(NPY_STRING); + PyArray_DTypeMeta *unicode = PyArray_DTypeFromTypeNum(NPY_UNICODE); + PyArray_DTypeMeta *tmp = NULL; + + dtypes[0] = datetime; + dtypes[1] = datetime; + if (PyArray_AddCastingImplementation_FromSpec(&spec, 1) < 0) { + goto fail; + } + dtypes[0] = timedelta; + dtypes[1] = timedelta; + if (PyArray_AddCastingImplementation_FromSpec(&spec, 1) < 0) { + goto fail; + } + + /* + * Casting between timedelta and datetime uses legacy casting loops, but + * custom dtype resolution (to handle copying of the time unit). + */ + spec.flags = NPY_METH_REQUIRES_PYAPI; + + slots[0].slot = NPY_METH_resolve_descriptors; + slots[0].pfunc = &datetime_to_timedelta_resolve_descriptors; + slots[1].slot = NPY_METH_get_loop; + slots[1].pfunc = &legacy_cast_get_strided_loop; + slots[2].slot = 0; + slots[2].pfunc = NULL; + + spec.name = "timedelta_and_datetime_cast"; + dtypes[0] = timedelta; + dtypes[1] = datetime; + if (PyArray_AddCastingImplementation_FromSpec(&spec, 1) < 0) { + goto fail; + } + spec.name = "datetime_to_timedelta_cast"; + dtypes[0] = datetime; + dtypes[1] = timedelta; + if (PyArray_AddCastingImplementation_FromSpec(&spec, 1) < 0) { + goto fail; + } + + /* + * Cast from numeric types to times. These use the cast functions + * as stored on the datatype, which should be replaced at some point. + * Some of these casts can fail (casting to unitless datetime), but these + * are rather special. + */ + for (int num = 0; num < NPY_NTYPES; num++) { + if (!PyTypeNum_ISNUMBER(num) && num != NPY_BOOL) { + continue; + } + + Py_XSETREF(tmp, PyArray_DTypeFromTypeNum(num)); + + if (PyArray_AddLegacyWrapping_CastingImpl( + tmp, datetime, NPY_UNSAFE_CASTING) < 0) { + goto fail; + } + if (PyArray_AddLegacyWrapping_CastingImpl( + datetime, tmp, NPY_UNSAFE_CASTING) < 0) { + goto fail; + } + + NPY_CASTING to_timedelta_casting = NPY_UNSAFE_CASTING; + if (PyTypeNum_ISINTEGER(num) || num == NPY_BOOL) { + /* timedelta casts like int64 right now... */ + if (PyTypeNum_ISUNSIGNED(num) && tmp->singleton->elsize == 8) { + to_timedelta_casting = NPY_SAME_KIND_CASTING; + } + else { + to_timedelta_casting = NPY_SAFE_CASTING; + } + } + if (PyArray_AddLegacyWrapping_CastingImpl( + tmp, timedelta, to_timedelta_casting) < 0) { + goto fail; + } + if (PyArray_AddLegacyWrapping_CastingImpl( + timedelta, tmp, NPY_UNSAFE_CASTING) < 0) { + goto fail; + } + } + + /* + * Cast times to string and unicode + */ + spec.casting = NPY_UNSAFE_CASTING; + /* + * Casts can error and need API (unicodes needs it for string->unicode). + * Unicode handling is currently implemented via a legacy cast. + * Datetime->string has its own fast cast while timedelta->string uses + * the legacy fallback. + */ + slots[0].slot = NPY_METH_resolve_descriptors; + slots[0].pfunc = &time_to_string_resolve_descriptors; + /* Strided loop differs for the two */ + slots[1].slot = NPY_METH_get_loop; + slots[2].slot = 0; + slots[2].pfunc = NULL; + + dtypes[0] = datetime; + for (int num = NPY_DATETIME; num <= NPY_TIMEDELTA; num++) { + if (num == NPY_DATETIME) { + dtypes[0] = datetime; + spec.flags = NPY_METH_SUPPORTS_UNALIGNED | NPY_METH_REQUIRES_PYAPI; + slots[1].pfunc = &datetime_to_string_get_loop; + } + else { + dtypes[0] = timedelta; + spec.flags = NPY_METH_REQUIRES_PYAPI; + slots[1].pfunc = &legacy_cast_get_strided_loop; + } + + for (int str = NPY_STRING; str <= NPY_UNICODE; str++) { + dtypes[1] = PyArray_DTypeFromTypeNum(str); + + int res = PyArray_AddCastingImplementation_FromSpec(&spec, 1); + Py_SETREF(dtypes[1], NULL); + if (res < 0) { + goto fail; + } + } + } + + /* + * Cast strings to timedelta are currently only legacy casts + */ + if (PyArray_AddLegacyWrapping_CastingImpl( + string, timedelta, NPY_UNSAFE_CASTING) < 0) { + goto fail; + } + if (PyArray_AddLegacyWrapping_CastingImpl( + unicode, timedelta, NPY_UNSAFE_CASTING) < 0) { + goto fail; + } + + /* + * Cast strings to datetime + */ + dtypes[1] = datetime; + spec.casting = NPY_UNSAFE_CASTING; + + /* The default type resolution should work fine. */ + slots[0].slot = NPY_METH_resolve_descriptors; + slots[0].pfunc = &string_to_datetime_cast_resolve_descriptors; + slots[1].slot = NPY_METH_get_loop; + slots[1].pfunc = &string_to_datetime_cast_get_loop; + slots[2].slot = 0; + slots[2].pfunc = NULL; + + dtypes[0] = string; + spec.flags = NPY_METH_SUPPORTS_UNALIGNED; + if (PyArray_AddCastingImplementation_FromSpec(&spec, 1) < 0) { + goto fail; + } + + dtypes[0] = unicode; + /* + * Unicode handling is currently implemented via a legacy cast, which + * requires the Python API. + */ + spec.flags = NPY_METH_SUPPORTS_UNALIGNED | NPY_METH_REQUIRES_PYAPI; + if (PyArray_AddCastingImplementation_FromSpec(&spec, 1) < 0) { + goto fail; + } + + result = 0; + fail: + Py_DECREF(datetime); + Py_DECREF(timedelta); + Py_DECREF(string); + Py_DECREF(unicode); + Py_XDECREF(tmp); + return result; +} + diff --git a/numpy/core/src/multiarray/datetime_busday.c b/numpy/core/src/multiarray/datetime_busday.c index c04a6c125ca2..d3e9e1451301 100644 --- a/numpy/core/src/multiarray/datetime_busday.c +++ b/numpy/core/src/multiarray/datetime_busday.c @@ -6,12 +6,12 @@ * * See LICENSE.txt for the license. */ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE #define PY_SSIZE_T_CLEAN #include <Python.h> -#define NPY_NO_DEPRECATED_API NPY_API_VERSION -#define _MULTIARRAYMODULE #include <numpy/arrayobject.h> #include "npy_config.h" @@ -48,7 +48,7 @@ get_day_of_week(npy_datetime date) */ static int is_holiday(npy_datetime date, - npy_datetime *holidays_begin, npy_datetime *holidays_end) + npy_datetime *holidays_begin, const npy_datetime *holidays_end) { npy_datetime *trial; @@ -88,7 +88,7 @@ is_holiday(npy_datetime date, */ static npy_datetime * find_earliest_holiday_on_or_after(npy_datetime date, - npy_datetime *holidays_begin, npy_datetime *holidays_end) + npy_datetime *holidays_begin, const npy_datetime *holidays_end) { npy_datetime *trial; @@ -127,7 +127,7 @@ find_earliest_holiday_on_or_after(npy_datetime date, */ static npy_datetime * find_earliest_holiday_after(npy_datetime date, - npy_datetime *holidays_begin, npy_datetime *holidays_end) + npy_datetime *holidays_begin, const npy_datetime *holidays_end) { npy_datetime *trial; @@ -159,7 +159,7 @@ static int apply_business_day_roll(npy_datetime date, npy_datetime *out, int *out_day_of_week, NPY_BUSDAY_ROLL roll, - npy_bool *weekmask, + const npy_bool *weekmask, npy_datetime *holidays_begin, npy_datetime *holidays_end) { int day_of_week; @@ -361,7 +361,7 @@ apply_business_day_offset(npy_datetime date, npy_int64 offset, static int apply_business_day_count(npy_datetime date_begin, npy_datetime date_end, npy_int64 *out, - npy_bool *weekmask, int busdays_in_weekmask, + const npy_bool *weekmask, int busdays_in_weekmask, npy_datetime *holidays_begin, npy_datetime *holidays_end) { npy_int64 count, whole_weeks; @@ -722,7 +722,7 @@ business_day_count(PyArrayObject *dates_begin, PyArrayObject *dates_end, */ NPY_NO_EXPORT PyArrayObject * is_business_day(PyArrayObject *dates, PyArrayObject *out, - npy_bool *weekmask, int busdays_in_weekmask, + const npy_bool *weekmask, int busdays_in_weekmask, npy_datetime *holidays_begin, npy_datetime *holidays_end) { PyArray_DatetimeMetaData temp_meta; @@ -834,24 +834,23 @@ static int PyArray_BusDayRollConverter(PyObject *roll_in, NPY_BUSDAY_ROLL *roll) { PyObject *obj = roll_in; - char *str; - Py_ssize_t len; - /* Make obj into an ASCII string */ - Py_INCREF(obj); - if (PyUnicode_Check(obj)) { - /* accept unicode input */ - PyObject *obj_str; - obj_str = PyUnicode_AsASCIIString(obj); + /* Make obj into an UTF8 string */ + if (PyBytes_Check(obj)) { + /* accept bytes input */ + PyObject *obj_str = PyUnicode_FromEncodedObject(obj, NULL, NULL); if (obj_str == NULL) { - Py_DECREF(obj); return 0; } - Py_DECREF(obj); obj = obj_str; } + else { + Py_INCREF(obj); + } - if (PyBytes_AsStringAndSize(obj, &str, &len) < 0) { + Py_ssize_t len; + char const *str = PyUnicode_AsUTF8AndSize(obj, &len); + if (str == NULL) { Py_DECREF(obj); return 0; } @@ -935,8 +934,8 @@ NPY_NO_EXPORT PyObject * array_busday_offset(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds) { - char *kwlist[] = {"dates", "offsets", "roll", - "weekmask", "holidays", "busdaycal", "out", NULL}; + static char *kwlist[] = {"dates", "offsets", "roll", + "weekmask", "holidays", "busdaycal", "out", NULL}; PyObject *dates_in = NULL, *offsets_in = NULL, *out_in = NULL; @@ -1012,7 +1011,7 @@ array_busday_offset(PyObject *NPY_UNUSED(self), /* This steals the datetime_dtype reference */ dates = (PyArrayObject *)PyArray_FromAny(dates_in, datetime_dtype, - 0, 0, 0, dates_in); + 0, 0, 0, NULL); if (dates == NULL) { goto fail; } @@ -1021,7 +1020,7 @@ array_busday_offset(PyObject *NPY_UNUSED(self), /* Make 'offsets' into an array */ offsets = (PyArrayObject *)PyArray_FromAny(offsets_in, PyArray_DescrFromType(NPY_INT64), - 0, 0, 0, offsets_in); + 0, 0, 0, NULL); if (offsets == NULL) { goto fail; } @@ -1066,8 +1065,8 @@ NPY_NO_EXPORT PyObject * array_busday_count(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds) { - char *kwlist[] = {"begindates", "enddates", - "weekmask", "holidays", "busdaycal", "out", NULL}; + static char *kwlist[] = {"begindates", "enddates", + "weekmask", "holidays", "busdaycal", "out", NULL}; PyObject *dates_begin_in = NULL, *dates_end_in = NULL, *out_in = NULL; @@ -1142,7 +1141,7 @@ array_busday_count(PyObject *NPY_UNUSED(self), /* This steals the datetime_dtype reference */ dates_begin = (PyArrayObject *)PyArray_FromAny(dates_begin_in, datetime_dtype, - 0, 0, 0, dates_begin_in); + 0, 0, 0, NULL); if (dates_begin == NULL) { goto fail; } @@ -1165,7 +1164,7 @@ array_busday_count(PyObject *NPY_UNUSED(self), /* This steals the datetime_dtype reference */ dates_end = (PyArrayObject *)PyArray_FromAny(dates_end_in, datetime_dtype, - 0, 0, 0, dates_end_in); + 0, 0, 0, NULL); if (dates_end == NULL) { goto fail; } @@ -1211,8 +1210,8 @@ NPY_NO_EXPORT PyObject * array_is_busday(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds) { - char *kwlist[] = {"dates", - "weekmask", "holidays", "busdaycal", "out", NULL}; + static char *kwlist[] = {"dates", + "weekmask", "holidays", "busdaycal", "out", NULL}; PyObject *dates_in = NULL, *out_in = NULL; @@ -1286,7 +1285,7 @@ array_is_busday(PyObject *NPY_UNUSED(self), /* This steals the datetime_dtype reference */ dates = (PyArrayObject *)PyArray_FromAny(dates_in, datetime_dtype, - 0, 0, 0, dates_in); + 0, 0, 0, NULL); if (dates == NULL) { goto fail; } diff --git a/numpy/core/src/multiarray/datetime_busday.h b/numpy/core/src/multiarray/datetime_busday.h index 483151122b2a..b53a25010247 100644 --- a/numpy/core/src/multiarray/datetime_busday.h +++ b/numpy/core/src/multiarray/datetime_busday.h @@ -1,5 +1,5 @@ -#ifndef _NPY_PRIVATE__DATETIME_BUSDAY_H_ -#define _NPY_PRIVATE__DATETIME_BUSDAY_H_ +#ifndef NUMPY_CORE_SRC_MULTIARRAY_DATETIME_BUSDAY_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_DATETIME_BUSDAY_H_ /* * This is the 'busday_offset' function exposed for calling @@ -25,4 +25,4 @@ NPY_NO_EXPORT PyObject * array_is_busday(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds); -#endif +#endif /* NUMPY_CORE_SRC_MULTIARRAY_DATETIME_BUSDAY_H_ */ diff --git a/numpy/core/src/multiarray/datetime_busdaycal.c b/numpy/core/src/multiarray/datetime_busdaycal.c index 7a26868e8d38..880efe934c09 100644 --- a/numpy/core/src/multiarray/datetime_busdaycal.c +++ b/numpy/core/src/multiarray/datetime_busdaycal.c @@ -7,19 +7,19 @@ * * See LICENSE.txt for the license. */ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE #define PY_SSIZE_T_CLEAN #include <Python.h> -#define NPY_NO_DEPRECATED_API NPY_API_VERSION -#define _MULTIARRAYMODULE -#include <numpy/arrayobject.h> +#include "numpy/arrayobject.h" +#include "numpy/arrayscalars.h" #include "npy_config.h" #include "npy_pycompat.h" #include "common.h" -#include "numpy/arrayscalars.h" #include "lowlevel_strided_loops.h" #include "_datetime.h" #include "datetime_busday.h" @@ -30,33 +30,31 @@ PyArray_WeekMaskConverter(PyObject *weekmask_in, npy_bool *weekmask) { PyObject *obj = weekmask_in; - /* Make obj into an ASCII string if it is UNICODE */ - Py_INCREF(obj); - if (PyUnicode_Check(obj)) { - /* accept unicode input */ - PyObject *obj_str; - obj_str = PyUnicode_AsASCIIString(obj); + /* Make obj into an UTF8 string */ + if (PyBytes_Check(obj)) { + /* accept bytes input */ + PyObject *obj_str = PyUnicode_FromEncodedObject(obj, NULL, NULL); if (obj_str == NULL) { - Py_DECREF(obj); return 0; } - Py_DECREF(obj); obj = obj_str; } + else { + Py_INCREF(obj); + } - if (PyBytes_Check(obj)) { - char *str; - Py_ssize_t len; - int i; - if (PyBytes_AsStringAndSize(obj, &str, &len) < 0) { + if (PyUnicode_Check(obj)) { + Py_ssize_t len; + char const *str = PyUnicode_AsUTF8AndSize(obj, &len); + if (str == NULL) { Py_DECREF(obj); return 0; } /* Length 7 is a string like "1111100" */ if (len == 7) { - for (i = 0; i < 7; ++i) { + for (int i = 0; i < 7; ++i) { switch(str[i]) { case '0': weekmask[i] = 0; @@ -75,7 +73,7 @@ PyArray_WeekMaskConverter(PyObject *weekmask_in, npy_bool *weekmask) general_weekmask_string: /* a string like "SatSun" or "Mon Tue Wed" */ memset(weekmask, 0, 7); - for (i = 0; i < len; i += 3) { + for (Py_ssize_t i = 0; i < len; i += 3) { while (isspace(str[i])) ++i; @@ -168,7 +166,7 @@ PyArray_WeekMaskConverter(PyObject *weekmask_in, npy_bool *weekmask) return 0; } - val = PyInt_AsLong(f); + val = PyLong_AsLong(f); if (error_converting(val)) { Py_DECREF(f); Py_DECREF(obj); @@ -293,7 +291,7 @@ PyArray_HolidaysConverter(PyObject *dates_in, npy_holidayslist *holidays) /* This steals the datetime_dtype reference */ dates = (PyArrayObject *)PyArray_FromAny(dates_in, datetime_dtype, - 0, 0, 0, dates_in); + 0, 0, 0, NULL); if (dates == NULL) { goto fail; } @@ -436,7 +434,7 @@ busdaycalendar_dealloc(NpyBusDayCalendar *self) } static PyObject * -busdaycalendar_weekmask_get(NpyBusDayCalendar *self) +busdaycalendar_weekmask_get(NpyBusDayCalendar *self, void *NPY_UNUSED(ignored)) { PyArrayObject *ret; npy_intp size = 7; @@ -454,7 +452,7 @@ busdaycalendar_weekmask_get(NpyBusDayCalendar *self) } static PyObject * -busdaycalendar_holidays_get(NpyBusDayCalendar *self) +busdaycalendar_holidays_get(NpyBusDayCalendar *self, void *NPY_UNUSED(ignored)) { PyArrayObject *ret; PyArray_Descr *date_dtype; @@ -493,61 +491,12 @@ static PyGetSetDef busdaycalendar_getsets[] = { }; NPY_NO_EXPORT PyTypeObject NpyBusDayCalendar_Type = { -#if defined(NPY_PY3K) PyVarObject_HEAD_INIT(NULL, 0) -#else - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ -#endif - "numpy.busdaycalendar", /* tp_name */ - sizeof(NpyBusDayCalendar), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)busdaycalendar_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ -#if defined(NPY_PY3K) - 0, /* tp_reserved */ -#else - 0, /* tp_compare */ -#endif - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - 0, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - 0, /* tp_methods */ - 0, /* tp_members */ - busdaycalendar_getsets, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)busdaycalendar_init, /* tp_init */ - 0, /* tp_alloc */ - busdaycalendar_new, /* tp_new */ - 0, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - 0, /* tp_del */ - 0, /* tp_version_tag */ + .tp_name = "numpy.busdaycalendar", + .tp_basicsize = sizeof(NpyBusDayCalendar), + .tp_dealloc = (destructor)busdaycalendar_dealloc, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_getset = busdaycalendar_getsets, + .tp_init = (initproc)busdaycalendar_init, + .tp_new = busdaycalendar_new, }; diff --git a/numpy/core/src/multiarray/datetime_busdaycal.h b/numpy/core/src/multiarray/datetime_busdaycal.h index 02903e3d20a0..20efebe0a3b4 100644 --- a/numpy/core/src/multiarray/datetime_busdaycal.h +++ b/numpy/core/src/multiarray/datetime_busdaycal.h @@ -1,5 +1,5 @@ -#ifndef _NPY_PRIVATE__DATETIME_BUSDAYDEF_H_ -#define _NPY_PRIVATE__DATETIME_BUSDAYDEF_H_ +#ifndef NUMPY_CORE_SRC_MULTIARRAY_DATETIME_BUSDAYCAL_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_DATETIME_BUSDAYCAL_H_ /* * A list of holidays, which should be sorted, not contain any @@ -59,4 +59,4 @@ PyArray_HolidaysConverter(PyObject *dates_in, npy_holidayslist *holidays); -#endif +#endif /* NUMPY_CORE_SRC_MULTIARRAY_DATETIME_BUSDAYCAL_H_ */ diff --git a/numpy/core/src/multiarray/datetime_strings.c b/numpy/core/src/multiarray/datetime_strings.c index 4f9d8fa41682..5080647cb1b4 100644 --- a/numpy/core/src/multiarray/datetime_strings.c +++ b/numpy/core/src/multiarray/datetime_strings.c @@ -6,24 +6,25 @@ * * See LICENSE.txt for the license. */ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE #define PY_SSIZE_T_CLEAN #include <Python.h> -#include <time.h> -#define NPY_NO_DEPRECATED_API NPY_API_VERSION -#define _MULTIARRAYMODULE -#include <numpy/arrayobject.h> +#include "numpy/arrayobject.h" #include "npy_config.h" #include "npy_pycompat.h" #include "numpy/arrayscalars.h" -#include "methods.h" +#include "convert_datatype.h" #include "_datetime.h" #include "datetime_strings.h" +#include <time.h> + /* * Platform-specific time_t typedef. Some platforms use 32 bit, some use 64 bit * and we just use the default with the exception of mingw, where we must use @@ -69,7 +70,7 @@ * multiplatform code, get_localtime() should never be used outside of this * range. * - * [1] http://en.wikipedia.org/wiki/Year_2038_problem + * [1] https://en.wikipedia.org/wiki/Year_2038_problem */ static int get_localtime(NPY_TIME_T *ts, struct tm *tms) @@ -218,7 +219,7 @@ convert_datetimestruct_utc_to_local(npy_datetimestruct *out_dts_local, * Returns 0 on success, -1 on failure. */ NPY_NO_EXPORT int -parse_iso_8601_datetime(char *str, Py_ssize_t len, +parse_iso_8601_datetime(char const *str, Py_ssize_t len, NPY_DATETIMEUNIT unit, NPY_CASTING casting, npy_datetimestruct *out, @@ -227,7 +228,7 @@ parse_iso_8601_datetime(char *str, Py_ssize_t len, { int year_leap = 0; int i, numdigits; - char *substr; + char const *substr; Py_ssize_t sublen; NPY_DATETIMEUNIT bestunit; @@ -743,8 +744,8 @@ parse_iso_8601_datetime(char *str, Py_ssize_t len, parse_error: PyErr_Format(PyExc_ValueError, - "Error parsing datetime string \"%s\" at position %d", - str, (int)(substr-str)); + "Error parsing datetime string \"%s\" at position %zd", + str, substr - str); return -1; error: @@ -1385,21 +1386,23 @@ array_datetime_as_string(PyObject *NPY_UNUSED(self), PyObject *args, /* Parse the input unit if provided */ if (unit_in != NULL && unit_in != Py_None) { PyObject *strobj; - char *str = NULL; - Py_ssize_t len = 0; - if (PyUnicode_Check(unit_in)) { - strobj = PyUnicode_AsASCIIString(unit_in); - if (strobj == NULL) { - goto fail; + if (PyBytes_Check(unit_in)) { + /* accept bytes input */ + PyObject *obj_str = PyUnicode_FromEncodedObject(unit_in, NULL, NULL); + if (obj_str == NULL) { + return 0; } + strobj = obj_str; } else { + Py_INCREF(unit_in); strobj = unit_in; - Py_INCREF(strobj); } - if (PyBytes_AsStringAndSize(strobj, &str, &len) < 0) { + Py_ssize_t len; + char const *str = PyUnicode_AsUTF8AndSize(strobj, &len); + if (str == NULL) { Py_DECREF(strobj); goto fail; } @@ -1434,24 +1437,27 @@ array_datetime_as_string(PyObject *NPY_UNUSED(self), PyObject *args, /* Get the input time zone */ if (timezone_obj != NULL) { - /* Convert to ASCII if it's unicode */ - if (PyUnicode_Check(timezone_obj)) { - /* accept unicode input */ - PyObject *obj_str; - obj_str = PyUnicode_AsASCIIString(timezone_obj); + PyObject *strobj; + if (PyBytes_Check(timezone_obj)) { + /* accept bytes input */ + PyObject *obj_str = PyUnicode_FromEncodedObject(timezone_obj, NULL, NULL); if (obj_str == NULL) { goto fail; } - Py_DECREF(timezone_obj); - timezone_obj = obj_str; + strobj = obj_str; } + else { + Py_INCREF(timezone_obj); + strobj = timezone_obj; + } + + Py_SETREF(timezone_obj, strobj); /* Check for the supported string inputs */ - if (PyBytes_Check(timezone_obj)) { - char *str; + if (PyUnicode_Check(timezone_obj)) { Py_ssize_t len; - - if (PyBytes_AsStringAndSize(timezone_obj, &str, &len) < 0) { + char const *str = PyUnicode_AsUTF8AndSize(timezone_obj, &len); + if (str == NULL) { goto fail; } @@ -1487,7 +1493,6 @@ array_datetime_as_string(PyObject *NPY_UNUSED(self), PyObject *args, /* Get a string size long enough for any datetimes we're given */ strsize = get_datetime_iso_8601_strlen(local, unit); -#if defined(NPY_PY3K) /* * For Python3, allocate the output array as a UNICODE array, so * that it will behave as strings properly @@ -1504,7 +1509,6 @@ array_datetime_as_string(PyObject *NPY_UNUSED(self), PyObject *args, op_dtypes[1] = NULL; goto fail; } -#endif /* Create the iteration string data type (always ASCII string) */ op_dtypes[1] = PyArray_DescrNewFromType(NPY_STRING); if (op_dtypes[1] == NULL) { diff --git a/numpy/core/src/multiarray/datetime_strings.h b/numpy/core/src/multiarray/datetime_strings.h index 4e60ce9298fe..ca35d29c8fc7 100644 --- a/numpy/core/src/multiarray/datetime_strings.h +++ b/numpy/core/src/multiarray/datetime_strings.h @@ -1,5 +1,5 @@ -#ifndef _NPY_PRIVATE__DATETIME_STRINGS_H_ -#define _NPY_PRIVATE__DATETIME_STRINGS_H_ +#ifndef NUMPY_CORE_SRC_MULTIARRAY_DATETIME_STRINGS_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_DATETIME_STRINGS_H_ /* * Parses (almost) standard ISO 8601 date strings. The differences are: @@ -33,7 +33,7 @@ * Returns 0 on success, -1 on failure. */ NPY_NO_EXPORT int -parse_iso_8601_datetime(char *str, Py_ssize_t len, +parse_iso_8601_datetime(char const *str, Py_ssize_t len, NPY_DATETIMEUNIT unit, NPY_CASTING casting, npy_datetimestruct *out, @@ -81,4 +81,4 @@ NPY_NO_EXPORT PyObject * array_datetime_as_string(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds); -#endif +#endif /* NUMPY_CORE_SRC_MULTIARRAY_DATETIME_STRINGS_H_ */ diff --git a/numpy/core/src/multiarray/descriptor.c b/numpy/core/src/multiarray/descriptor.c index bb3cc9d4e138..0c539053c9e7 100644 --- a/numpy/core/src/multiarray/descriptor.c +++ b/numpy/core/src/multiarray/descriptor.c @@ -1,16 +1,16 @@ /* Array Descr Object */ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE #define PY_SSIZE_T_CLEAN #include <Python.h> -#include "structmember.h" +#include <structmember.h> -#define NPY_NO_DEPRECATED_API NPY_API_VERSION -#define _MULTIARRAYMODULE #include "numpy/arrayobject.h" #include "numpy/arrayscalars.h" #include "npy_config.h" - +#include "npy_ctypes.h" #include "npy_pycompat.h" #include "_datetime.h" @@ -19,6 +19,8 @@ #include "descriptor.h" #include "alloc.h" #include "assert.h" +#include "npy_buffer.h" +#include "dtypemeta.h" /* * offset: A starting offset. @@ -39,111 +41,119 @@ static PyObject *typeDict = NULL; /* Must be explicitly loaded */ static PyArray_Descr * -_use_inherit(PyArray_Descr *type, PyObject *newobj, int *errflag); +_try_convert_from_inherit_tuple(PyArray_Descr *type, PyObject *newobj); +static PyArray_Descr * +_convert_from_any(PyObject *obj, int align); /* - * Returns value of PyMapping_GetItemString but as a borrowed reference instead - * of a new reference. + * This function creates a dtype object when the object is a ctypes subclass. + * + * Returns `Py_NotImplemented` if the type is not a ctypes subclass. */ -static PyObject * -Borrowed_PyMapping_GetItemString(PyObject *o, char *key) +static PyArray_Descr * +_try_convert_from_ctypes_type(PyTypeObject *type) { - PyObject *ret = PyMapping_GetItemString(o, key); - Py_XDECREF(ret); - return ret; + PyObject *_numpy_dtype_ctypes; + PyObject *res; + + if (!npy_ctypes_check(type)) { + Py_INCREF(Py_NotImplemented); + return (PyArray_Descr *)Py_NotImplemented; + } + + /* Call the python function of the same name. */ + _numpy_dtype_ctypes = PyImport_ImportModule("numpy.core._dtype_ctypes"); + if (_numpy_dtype_ctypes == NULL) { + return NULL; + } + res = PyObject_CallMethod(_numpy_dtype_ctypes, "dtype_from_ctypes_type", "O", (PyObject *)type); + Py_DECREF(_numpy_dtype_ctypes); + if (res == NULL) { + return NULL; + } + + /* + * sanity check that dtype_from_ctypes_type returned the right type, + * since getting it wrong would give segfaults. + */ + if (!PyObject_TypeCheck(res, &PyArrayDescr_Type)) { + Py_DECREF(res); + PyErr_BadInternalCall(); + return NULL; + } + + return (PyArray_Descr *)res; } +static PyArray_Descr * +_convert_from_any(PyObject *obj, int align); + /* - * Creates a dtype object from ctypes inputs. + * This function creates a dtype object when the object has a "dtype" attribute, + * and it can be converted to a dtype object. * - * Returns a new reference to a dtype object, or NULL - * if this is not possible. When it returns NULL, it does - * not set a Python exception. + * Returns `Py_NotImplemented` if this is not possible. + * Currently the only failure mode for a NULL return is a RecursionError. */ static PyArray_Descr * -_arraydescr_fromctypes(PyObject *obj) +_try_convert_from_dtype_attr(PyObject *obj) { - PyObject *dtypedescr; - PyArray_Descr *newdescr; - int ret; - - /* Understand basic ctypes */ - dtypedescr = PyObject_GetAttrString(obj, "_type_"); - PyErr_Clear(); - if (dtypedescr) { - ret = PyArray_DescrConverter(dtypedescr, &newdescr); + /* For arbitrary objects that have a "dtype" attribute */ + PyObject *dtypedescr = PyObject_GetAttrString(obj, "dtype"); + if (dtypedescr == NULL) { + /* + * This can be reached due to recursion limit being hit while fetching + * the attribute (tested for py3.7). This removes the custom message. + */ + goto fail; + } + + if (PyArray_DescrCheck(dtypedescr)) { + /* The dtype attribute is already a valid descriptor */ + return (PyArray_Descr *)dtypedescr; + } + + if (Py_EnterRecursiveCall( + " while trying to convert the given data type from its " + "`.dtype` attribute.") != 0) { Py_DECREF(dtypedescr); - if (ret == NPY_SUCCEED) { - PyObject *length; - /* Check for ctypes arrays */ - length = PyObject_GetAttrString(obj, "_length_"); - PyErr_Clear(); - if (length) { - /* derived type */ - PyObject *newtup; - PyArray_Descr *derived; - newtup = Py_BuildValue("NN", newdescr, length); - ret = PyArray_DescrConverter(newtup, &derived); - Py_DECREF(newtup); - if (ret == NPY_SUCCEED) { - return derived; - } - PyErr_Clear(); - return NULL; - } - return newdescr; - } - PyErr_Clear(); return NULL; } - /* Understand ctypes structures -- - bit-fields are not supported - automatically aligns */ - dtypedescr = PyObject_GetAttrString(obj, "_fields_"); - PyErr_Clear(); - if (dtypedescr) { - ret = PyArray_DescrAlignConverter(dtypedescr, &newdescr); - Py_DECREF(dtypedescr); - if (ret == NPY_SUCCEED) { - return newdescr; - } - PyErr_Clear(); + + PyArray_Descr *newdescr = _convert_from_any(dtypedescr, 0); + Py_DECREF(dtypedescr); + Py_LeaveRecursiveCall(); + if (newdescr == NULL) { + goto fail; } + /* Deprecated 2021-01-05, NumPy 1.21 */ + if (DEPRECATE("in the future the `.dtype` attribute of a given data" + "type object must be a valid dtype instance. " + "`data_type.dtype` may need to be coerced using " + "`np.dtype(data_type.dtype)`. (Deprecated NumPy 1.20)") < 0) { + Py_DECREF(newdescr); + return NULL; + } + + return newdescr; + + fail: + /* Ignore all but recursion errors, to give ctypes a full try. */ + if (!PyErr_ExceptionMatches(PyExc_RecursionError)) { + PyErr_Clear(); + Py_INCREF(Py_NotImplemented); + return (PyArray_Descr *)Py_NotImplemented; + } return NULL; } -/* - * This function creates a dtype object when: - * - The object has a "dtype" attribute, and it can be converted - * to a dtype object. - * - The object is a ctypes type object, including array - * and structure types. - * - * Returns a new reference to a dtype object, or NULL - * if this is not possible. When it returns NULL, it does - * not set a Python exception. - */ +/* Expose to another file with a prefixed name */ NPY_NO_EXPORT PyArray_Descr * -_arraydescr_fromobj(PyObject *obj) +_arraydescr_try_convert_from_dtype_attr(PyObject *obj) { - PyObject *dtypedescr; - PyArray_Descr *newdescr = NULL; - int ret; - - /* For arbitrary objects that have a "dtype" attribute */ - dtypedescr = PyObject_GetAttrString(obj, "dtype"); - PyErr_Clear(); - if (dtypedescr != NULL) { - ret = PyArray_DescrConverter(dtypedescr, &newdescr); - Py_DECREF(dtypedescr); - if (ret == NPY_SUCCEED) { - return newdescr; - } - PyErr_Clear(); - } - return _arraydescr_fromctypes(obj); + return _try_convert_from_dtype_attr(obj); } /* @@ -170,7 +180,7 @@ array_set_typeDict(PyObject *NPY_UNUSED(ignored), PyObject *args) arg == '|' || arg == '=') static int -_check_for_commastring(char *type, Py_ssize_t len) +_check_for_commastring(const char *type, Py_ssize_t len) { Py_ssize_t i; int sqbracket; @@ -220,7 +230,7 @@ _check_for_commastring(char *type, Py_ssize_t len) #undef _chk_byteorder static int -is_datetime_typestr(char *type, Py_ssize_t len) +is_datetime_typestr(char const *type, Py_ssize_t len) { if (len < 2) { return 0; @@ -246,34 +256,26 @@ is_datetime_typestr(char *type, Py_ssize_t len) static PyArray_Descr * _convert_from_tuple(PyObject *obj, int align) { - PyArray_Descr *type, *res; - PyObject *val; - int errflag; - if (PyTuple_GET_SIZE(obj) != 2) { + PyErr_Format(PyExc_TypeError, + "Tuple must have size 2, but has size %zd", + PyTuple_GET_SIZE(obj)); return NULL; } - if (align) { - if (!PyArray_DescrAlignConverter(PyTuple_GET_ITEM(obj, 0), &type)) { - return NULL; - } + PyArray_Descr *type = _convert_from_any(PyTuple_GET_ITEM(obj, 0), align); + if (type == NULL) { + return NULL; } - else { - if (!PyArray_DescrConverter(PyTuple_GET_ITEM(obj, 0), &type)) { - return NULL; - } - } - val = PyTuple_GET_ITEM(obj,1); + PyObject *val = PyTuple_GET_ITEM(obj,1); /* try to interpret next item as a type */ - res = _use_inherit(type, val, &errflag); - if (res || errflag) { + PyArray_Descr *res = _try_convert_from_inherit_tuple(type, val); + if ((PyObject *)res != Py_NotImplemented) { Py_DECREF(type); return res; } - PyErr_Clear(); + Py_DECREF(res); /* - * We get here if res was NULL but errflag wasn't set - * --- i.e. the conversion to a data-descr failed in _use_inherit + * We get here if _try_convert_from_inherit_tuple failed without crashing */ if (PyDataType_ISUNSIZED(type)) { /* interpret next item as a typesize */ @@ -286,6 +288,9 @@ _convert_from_tuple(PyObject *obj, int align) return NULL; } PyArray_DESCR_REPLACE(type); + if (type == NULL) { + return NULL; + } if (type->type_num == NPY_UNICODE) { type->elsize = itemsize << 2; } @@ -309,31 +314,33 @@ _convert_from_tuple(PyObject *obj, int align) * a new fields attribute. */ PyArray_Dims shape = {NULL, -1}; - PyArray_Descr *newdescr = NULL; - npy_intp items; - int i, overflowed; - int nbytes; - if (!(PyArray_IntpConverter(val, &shape)) || (shape.len > NPY_MAXDIMS)) { PyErr_SetString(PyExc_ValueError, "invalid shape in fixed-type tuple."); goto fail; } - /* - * If (type, 1) was given, it is equivalent to type... - * or (type, ()) was given it is equivalent to type... - */ - if ((shape.len == 1 - && shape.ptr[0] == 1 - && PyNumber_Check(val)) - || (shape.len == 0 - && PyTuple_Check(val))) { + /* if (type, ()) was given it is equivalent to type... */ + if (shape.len == 0 && PyTuple_Check(val)) { + npy_free_cache_dim_obj(shape); + return type; + } + /* (type, 1) use to be equivalent to type, but is deprecated */ + if (shape.len == 1 + && shape.ptr[0] == 1 + && PyNumber_Check(val)) { + /* 2019-05-20, 1.17 */ + if (DEPRECATE_FUTUREWARNING( + "Passing (type, 1) or '1type' as a synonym of type is " + "deprecated; in a future version of numpy, it will be " + "understood as (type, (1,)) / '(1,)type'.") < 0) { + goto fail; + } npy_free_cache_dim_obj(shape); return type; } /* validate and set shape */ - for (i=0; i < shape.len; i++) { + for (int i=0; i < shape.len; i++) { if (shape.ptr[i] < 0) { PyErr_SetString(PyExc_ValueError, "invalid shape in fixed-type tuple: " @@ -347,7 +354,9 @@ _convert_from_tuple(PyObject *obj, int align) goto fail; } } - items = PyArray_OverflowMultiplyList(shape.ptr, shape.len); + npy_intp items = PyArray_OverflowMultiplyList(shape.ptr, shape.len); + int overflowed; + int nbytes; if (items < 0 || items > NPY_MAX_INT) { overflowed = 1; } @@ -361,13 +370,14 @@ _convert_from_tuple(PyObject *obj, int align) "bytes must fit into a C int."); goto fail; } - newdescr = PyArray_DescrNewFromType(NPY_VOID); + PyArray_Descr *newdescr = PyArray_DescrNewFromType(NPY_VOID); if (newdescr == NULL) { goto fail; } newdescr->elsize = nbytes; newdescr->subarray = PyArray_malloc(sizeof(PyArray_ArrayDescr)); if (newdescr->subarray == NULL) { + Py_DECREF(newdescr); PyErr_NoMemory(); goto fail; } @@ -386,13 +396,15 @@ _convert_from_tuple(PyObject *obj, int align) */ newdescr->subarray->shape = PyTuple_New(shape.len); if (newdescr->subarray->shape == NULL) { + Py_DECREF(newdescr); goto fail; } - for (i=0; i < shape.len; i++) { + for (int i=0; i < shape.len; i++) { PyTuple_SET_ITEM(newdescr->subarray->shape, i, - PyInt_FromLong((long)shape.ptr[i])); + PyLong_FromLong((long)shape.ptr[i])); if (PyTuple_GET_ITEM(newdescr->subarray->shape, i) == NULL) { + Py_DECREF(newdescr); goto fail; } } @@ -402,7 +414,6 @@ _convert_from_tuple(PyObject *obj, int align) fail: Py_XDECREF(type); - Py_XDECREF(newdescr); npy_free_cache_dim_obj(shape); return NULL; } @@ -421,141 +432,127 @@ _convert_from_tuple(PyObject *obj, int align) static PyArray_Descr * _convert_from_array_descr(PyObject *obj, int align) { - int n, i, totalsize; - int ret; - PyObject *fields, *item, *newobj; - PyObject *name, *tup, *title; - PyObject *nameslist; - PyArray_Descr *new; - PyArray_Descr *conv; + int n = PyList_GET_SIZE(obj); + PyObject *nameslist = PyTuple_New(n); + if (!nameslist) { + return NULL; + } + /* Types with fields need the Python C API for field access */ char dtypeflags = NPY_NEEDS_PYAPI; int maxalign = 0; - - n = PyList_GET_SIZE(obj); - nameslist = PyTuple_New(n); - if (!nameslist) { + int totalsize = 0; + PyObject *fields = PyDict_New(); + if (!fields) { return NULL; } - totalsize = 0; - fields = PyDict_New(); - for (i = 0; i < n; i++) { - item = PyList_GET_ITEM(obj, i); + for (int i = 0; i < n; i++) { + PyObject *item = PyList_GET_ITEM(obj, i); if (!PyTuple_Check(item) || (PyTuple_GET_SIZE(item) < 2)) { + PyErr_Format(PyExc_TypeError, + "Field elements must be 2- or 3-tuples, got '%R'", + item); goto fail; } - name = PyTuple_GET_ITEM(item, 0); - if (PyBaseString_Check(name)) { + PyObject *name = PyTuple_GET_ITEM(item, 0); + PyObject *title; + if (PyUnicode_Check(name)) { title = NULL; } else if (PyTuple_Check(name)) { if (PyTuple_GET_SIZE(name) != 2) { + PyErr_Format(PyExc_TypeError, + "If a tuple, the first element of a field tuple must have " + "two elements, not %zd", + PyTuple_GET_SIZE(name)); goto fail; } title = PyTuple_GET_ITEM(name, 0); name = PyTuple_GET_ITEM(name, 1); - if (!PyBaseString_Check(name)) { + if (!PyUnicode_Check(name)) { + PyErr_SetString(PyExc_TypeError, "Field name must be a str"); goto fail; } } else { + PyErr_SetString(PyExc_TypeError, + "First element of field tuple is " + "neither a tuple nor str"); goto fail; } /* Insert name into nameslist */ Py_INCREF(name); -#if !defined(NPY_PY3K) - /* convert unicode name to ascii on Python 2 if possible */ - if (PyUnicode_Check(name)) { - PyObject *tmp = PyUnicode_AsASCIIString(name); - Py_DECREF(name); - if (tmp == NULL) { - goto fail; - } - name = tmp; - } -#endif - if (PyUString_GET_SIZE(name) == 0) { + if (PyUnicode_GetLength(name) == 0) { Py_DECREF(name); if (title == NULL) { - name = PyUString_FromFormat("f%d", i); + name = PyUnicode_FromFormat("f%d", i); + if (name == NULL) { + goto fail; + } } -#if defined(NPY_PY3K) /* On Py3, allow only non-empty Unicode strings as field names */ - else if (PyUString_Check(title) && PyUString_GET_SIZE(title) > 0) { + else if (PyUnicode_Check(title) && PyUnicode_GetLength(title) > 0) { name = title; Py_INCREF(name); } else { + PyErr_SetString(PyExc_TypeError, "Field titles must be non-empty strings"); goto fail; } -#else - else { - name = title; - Py_INCREF(name); - } -#endif } PyTuple_SET_ITEM(nameslist, i, name); /* Process rest */ - + PyArray_Descr *conv; if (PyTuple_GET_SIZE(item) == 2) { - if (align) { - ret = PyArray_DescrAlignConverter(PyTuple_GET_ITEM(item, 1), - &conv); - } - else { - ret = PyArray_DescrConverter(PyTuple_GET_ITEM(item, 1), &conv); - } - if (ret == NPY_FAIL) { - PyObject_Print(PyTuple_GET_ITEM(item, 1), stderr, 0); + conv = _convert_from_any(PyTuple_GET_ITEM(item, 1), align); + if (conv == NULL) { + goto fail; } } else if (PyTuple_GET_SIZE(item) == 3) { - newobj = PyTuple_GetSlice(item, 1, 3); - if (align) { - ret = PyArray_DescrAlignConverter(newobj, &conv); - } - else { - ret = PyArray_DescrConverter(newobj, &conv); - } + PyObject *newobj = PyTuple_GetSlice(item, 1, 3); + conv = _convert_from_any(newobj, align); Py_DECREF(newobj); + if (conv == NULL) { + goto fail; + } } else { + PyErr_Format(PyExc_TypeError, + "Field elements must be tuples with at most 3 elements, got '%R'", item); goto fail; } - if (ret == NPY_FAIL) { - goto fail; - } - if ((PyDict_GetItem(fields, name) != NULL) + if ((PyDict_GetItemWithError(fields, name) != NULL) || (title - && PyBaseString_Check(title) - && (PyDict_GetItem(fields, title) != NULL))) { -#if defined(NPY_PY3K) - name = PyUnicode_AsUTF8String(name); -#endif + && PyUnicode_Check(title) + && (PyDict_GetItemWithError(fields, title) != NULL))) { PyErr_Format(PyExc_ValueError, - "field '%s' occurs more than once", PyString_AsString(name)); -#if defined(NPY_PY3K) - Py_DECREF(name); -#endif + "field %R occurs more than once", name); + Py_DECREF(conv); + goto fail; + } + else if (PyErr_Occurred()) { + /* Dict lookup crashed */ + Py_DECREF(conv); goto fail; } dtypeflags |= (conv->flags & NPY_FROM_FIELDS); if (align) { - int _align; - - _align = conv->alignment; + int _align = conv->alignment; if (_align > 1) { totalsize = NPY_NEXT_ALIGNED_OFFSET(totalsize, _align); } maxalign = PyArray_MAX(maxalign, _align); } - tup = PyTuple_New((title == NULL ? 2 : 3)); + PyObject *tup = PyTuple_New((title == NULL ? 2 : 3)); + if (tup == NULL) { + goto fail; + } PyTuple_SET_ITEM(tup, 0, (PyObject *)conv); - PyTuple_SET_ITEM(tup, 1, PyInt_FromLong((long) totalsize)); + PyTuple_SET_ITEM(tup, 1, PyLong_FromLong((long) totalsize)); /* * Title can be "meta-data". Only insert it @@ -565,19 +562,29 @@ _convert_from_array_descr(PyObject *obj, int align) if (title != NULL) { Py_INCREF(title); PyTuple_SET_ITEM(tup, 2, title); - PyDict_SetItem(fields, name, tup); - if (PyBaseString_Check(title)) { - if (PyDict_GetItem(fields, title) != NULL) { + if (PyDict_SetItem(fields, name, tup) < 0) { + goto fail; + } + if (PyUnicode_Check(title)) { + PyObject *existing = PyDict_GetItemWithError(fields, title); + if (existing == NULL && PyErr_Occurred()) { + goto fail; + } + if (existing != NULL) { PyErr_SetString(PyExc_ValueError, "title already used as a name or title."); Py_DECREF(tup); goto fail; } - PyDict_SetItem(fields, title, tup); + if (PyDict_SetItem(fields, title, tup) < 0) { + goto fail; + } } } else { - PyDict_SetItem(fields, name, tup); + if (PyDict_SetItem(fields, name, tup) < 0) { + goto fail; + } } totalsize += conv->elsize; @@ -588,7 +595,7 @@ _convert_from_array_descr(PyObject *obj, int align) totalsize = NPY_NEXT_ALIGNED_OFFSET(totalsize, maxalign); } - new = PyArray_DescrNewFromType(NPY_VOID); + PyArray_Descr *new = PyArray_DescrNewFromType(NPY_VOID); if (new == NULL) { Py_XDECREF(fields); Py_XDECREF(nameslist); @@ -621,69 +628,81 @@ _convert_from_array_descr(PyObject *obj, int align) static PyArray_Descr * _convert_from_list(PyObject *obj, int align) { - int n, i; - int totalsize; - PyObject *fields; - PyArray_Descr *conv = NULL; - PyArray_Descr *new; - PyObject *key, *tup; - PyObject *nameslist = NULL; - int ret; - int maxalign = 0; - /* Types with fields need the Python C API for field access */ - char dtypeflags = NPY_NEEDS_PYAPI; - - n = PyList_GET_SIZE(obj); + int n = PyList_GET_SIZE(obj); /* * Ignore any empty string at end which _internal._commastring * can produce */ - key = PyList_GET_ITEM(obj, n-1); - if (PyBytes_Check(key) && PyBytes_GET_SIZE(key) == 0) { - n = n - 1; + PyObject *last_item = PyList_GET_ITEM(obj, n-1); + if (PyUnicode_Check(last_item)) { + Py_ssize_t s = PySequence_Size(last_item); + if (s < 0) { + return NULL; + } + if (s == 0) { + n = n - 1; + } } - /* End ignore code.*/ - totalsize = 0; if (n == 0) { + PyErr_SetString(PyExc_ValueError, "Expected at least one field name"); return NULL; } - nameslist = PyTuple_New(n); + PyObject *nameslist = PyTuple_New(n); if (!nameslist) { return NULL; } - fields = PyDict_New(); - for (i = 0; i < n; i++) { - tup = PyTuple_New(2); - key = PyUString_FromFormat("f%d", i); - if (align) { - ret = PyArray_DescrAlignConverter(PyList_GET_ITEM(obj, i), &conv); - } - else { - ret = PyArray_DescrConverter(PyList_GET_ITEM(obj, i), &conv); - } - if (ret == NPY_FAIL) { - Py_DECREF(tup); - Py_DECREF(key); + PyObject *fields = PyDict_New(); + if (!fields) { + Py_DECREF(nameslist); + return NULL; + } + + /* Types with fields need the Python C API for field access */ + char dtypeflags = NPY_NEEDS_PYAPI; + int maxalign = 0; + int totalsize = 0; + for (int i = 0; i < n; i++) { + PyArray_Descr *conv = _convert_from_any( + PyList_GET_ITEM(obj, i), align); + if (conv == NULL) { goto fail; } dtypeflags |= (conv->flags & NPY_FROM_FIELDS); - PyTuple_SET_ITEM(tup, 0, (PyObject *)conv); if (align) { - int _align; - - _align = conv->alignment; + int _align = conv->alignment; if (_align > 1) { totalsize = NPY_NEXT_ALIGNED_OFFSET(totalsize, _align); } maxalign = PyArray_MAX(maxalign, _align); } - PyTuple_SET_ITEM(tup, 1, PyInt_FromLong((long) totalsize)); - PyDict_SetItem(fields, key, tup); - Py_DECREF(tup); + PyObject *size_obj = PyLong_FromLong((long) totalsize); + if (!size_obj) { + Py_DECREF(conv); + goto fail; + } + PyObject *tup = PyTuple_New(2); + if (!tup) { + Py_DECREF(size_obj); + Py_DECREF(conv); + goto fail; + } + PyTuple_SET_ITEM(tup, 0, (PyObject *)conv); + PyTuple_SET_ITEM(tup, 1, size_obj); + PyObject *key = PyUnicode_FromFormat("f%d", i); + if (!key) { + Py_DECREF(tup); + goto fail; + } + /* steals a reference to key */ PyTuple_SET_ITEM(nameslist, i, key); + int ret = PyDict_SetItem(fields, key, tup); + Py_DECREF(tup); + if (ret < 0) { + goto fail; + } totalsize += conv->elsize; } - new = PyArray_DescrNewFromType(NPY_VOID); + PyArray_Descr *new = PyArray_DescrNewFromType(NPY_VOID); new->fields = fields; new->names = nameslist; new->flags = dtypeflags; @@ -722,10 +741,7 @@ _convert_from_commastring(PyObject *obj, int align) PyObject *listobj; PyArray_Descr *res; PyObject *_numpy_internal; - - if (!PyBytes_Check(obj)) { - return NULL; - } + assert(PyUnicode_Check(obj)); _numpy_internal = PyImport_ImportModule("numpy.core._internal"); if (_numpy_internal == NULL) { return NULL; @@ -742,22 +758,12 @@ _convert_from_commastring(PyObject *obj, int align) return NULL; } if (PyList_GET_SIZE(listobj) == 1) { - int retcode; - retcode = PyArray_DescrConverter(PyList_GET_ITEM(listobj, 0), - &res); - if (retcode == NPY_FAIL) { - res = NULL; - } + res = _convert_from_any(PyList_GET_ITEM(listobj, 0), align); } else { res = _convert_from_list(listobj, align); } Py_DECREF(listobj); - if (!res && !PyErr_Occurred()) { - PyErr_SetString(PyExc_ValueError, - "invalid data-type"); - return NULL; - } return res; } @@ -778,7 +784,7 @@ _is_tuple_of_integers(PyObject *obj) } /* - * helper function for _use_inherit to disallow dtypes of the form + * helper function for _try_convert_from_inherit_tuple to disallow dtypes of the form * (old_dtype, new_dtype) where either of the dtypes contains python * objects - these dtypes are not useful and can be a source of segfaults, * when an attempt is made to interpret a python object as a different dtype @@ -787,7 +793,7 @@ _is_tuple_of_integers(PyObject *obj) * people have been using to add a field to an object array without fields */ static int -invalid_union_object_dtype(PyArray_Descr *new, PyArray_Descr *conv) +_validate_union_object_dtype(PyArray_Descr *new, PyArray_Descr *conv) { PyObject *name, *tup; PyArray_Descr *dtype; @@ -805,8 +811,12 @@ invalid_union_object_dtype(PyArray_Descr *new, PyArray_Descr *conv) if (name == NULL) { return -1; } - tup = PyDict_GetItem(conv->fields, name); + tup = PyDict_GetItemWithError(conv->fields, name); if (tup == NULL) { + if (!PyErr_Occurred()) { + /* fields was missing the name it claimed to contain */ + PyErr_BadInternalCall(); + } return -1; } dtype = (PyArray_Descr *)PyTuple_GET_ITEM(tup, 0); @@ -839,21 +849,26 @@ invalid_union_object_dtype(PyArray_Descr *new, PyArray_Descr *conv) * a['real'] and a['imag'] to an int32 array. * * leave type reference alone + * + * Returns `Py_NotImplemented` if the second tuple item is not + * appropriate. */ static PyArray_Descr * -_use_inherit(PyArray_Descr *type, PyObject *newobj, int *errflag) +_try_convert_from_inherit_tuple(PyArray_Descr *type, PyObject *newobj) { - PyArray_Descr *new; - PyArray_Descr *conv; - - *errflag = 0; - if (PyArray_IsScalar(newobj, Integer) - || _is_tuple_of_integers(newobj) - || !PyArray_DescrConverter(newobj, &conv)) { - return NULL; + if (PyArray_IsScalar(newobj, Integer) || _is_tuple_of_integers(newobj)) { + /* It's a subarray or flexible type instead */ + Py_INCREF(Py_NotImplemented); + return (PyArray_Descr *)Py_NotImplemented; + } + PyArray_Descr *conv = _convert_from_any(newobj, 0); + if (conv == NULL) { + /* Let someone else try to convert this */ + PyErr_Clear(); + Py_INCREF(Py_NotImplemented); + return (PyArray_Descr *)Py_NotImplemented; } - *errflag = 1; - new = PyArray_DescrNew(type); + PyArray_Descr *new = PyArray_DescrNew(type); if (new == NULL) { goto fail; } @@ -863,9 +878,11 @@ _use_inherit(PyArray_Descr *type, PyObject *newobj, int *errflag) else if (new->elsize != conv->elsize) { PyErr_SetString(PyExc_ValueError, "mismatch in size of old and new data-descriptor"); + Py_DECREF(new); goto fail; } - else if (invalid_union_object_dtype(new, conv)) { + else if (_validate_union_object_dtype(new, conv) < 0) { + Py_DECREF(new); goto fail; } @@ -883,9 +900,18 @@ _use_inherit(PyArray_Descr *type, PyObject *newobj, int *errflag) new->metadata = conv->metadata; Py_XINCREF(new->metadata); } - new->flags = conv->flags; + /* + * Certain flags must be inherited from the fields. This is needed + * only for void dtypes (or subclasses of it such as a record dtype). + * For other dtypes, the field part will only be used for direct field + * access and thus flag inheritance should not be necessary. + * (We only allow object fields if the dtype is object as well.) + * This ensures copying over of the NPY_FROM_FIELDS "inherited" flags. + */ + if (new->type_num == NPY_VOID) { + new->flags = conv->flags; + } Py_DECREF(conv); - *errflag = 0; return new; fail: @@ -905,7 +931,7 @@ _use_inherit(PyArray_Descr *type, PyObject *newobj, int *errflag) * Returns 0 on success, -1 if an exception is raised. */ static int -validate_object_field_overlap(PyArray_Descr *dtype) +_validate_object_field_overlap(PyArray_Descr *dtype) { PyObject *names, *fields, *key, *tup, *title; Py_ssize_t i, j, names_size; @@ -922,8 +948,12 @@ validate_object_field_overlap(PyArray_Descr *dtype) if (key == NULL) { return -1; } - tup = PyDict_GetItem(fields, key); + tup = PyDict_GetItemWithError(fields, key); if (tup == NULL) { + if (!PyErr_Occurred()) { + /* fields was missing the name it claimed to contain */ + PyErr_BadInternalCall(); + } return -1; } if (!PyArg_ParseTuple(tup, "Oi|O", &fld_dtype, &fld_offset, &title)) { @@ -938,8 +968,12 @@ validate_object_field_overlap(PyArray_Descr *dtype) if (key == NULL) { return -1; } - tup = PyDict_GetItem(fields, key); + tup = PyDict_GetItemWithError(fields, key); if (tup == NULL) { + if (!PyErr_Occurred()) { + /* fields was missing the name it claimed to contain */ + PyErr_BadInternalCall(); + } return -1; } if (!PyArg_ParseTuple(tup, "Oi|O", &fld2_dtype, @@ -1001,7 +1035,7 @@ validate_object_field_overlap(PyArray_Descr *dtype) * then it will be checked for conformity and used directly. */ static PyArray_Descr * -_use_fields_dict(PyObject *obj, int align) +_convert_from_field_dict(PyObject *obj, int align) { PyObject *_numpy_internal; PyArray_Descr *res; @@ -1022,37 +1056,34 @@ _use_fields_dict(PyObject *obj, int align) static PyArray_Descr * _convert_from_dict(PyObject *obj, int align) { - PyArray_Descr *new; - PyObject *fields = NULL; - PyObject *names, *offsets, *descrs, *titles, *tmp; - PyObject *metadata; - int n, i; - int totalsize, itemsize; - int maxalign = 0; - /* Types with fields need the Python C API for field access */ - char dtypeflags = NPY_NEEDS_PYAPI; - int has_out_of_order_fields = 0; - - fields = PyDict_New(); + PyObject *fields = PyDict_New(); if (fields == NULL) { return (PyArray_Descr *)PyErr_NoMemory(); } /* * Use PyMapping_GetItemString to support dictproxy objects as well. */ - names = Borrowed_PyMapping_GetItemString(obj, "names"); - descrs = Borrowed_PyMapping_GetItemString(obj, "formats"); - if (!names || !descrs) { + PyObject *names = PyMapping_GetItemString(obj, "names"); + if (names == NULL) { + Py_DECREF(fields); + /* XXX should check this is a KeyError */ + PyErr_Clear(); + return _convert_from_field_dict(obj, align); + } + PyObject *descrs = PyMapping_GetItemString(obj, "formats"); + if (descrs == NULL) { Py_DECREF(fields); + /* XXX should check this is a KeyError */ PyErr_Clear(); - return _use_fields_dict(obj, align); + Py_DECREF(names); + return _convert_from_field_dict(obj, align); } - n = PyObject_Length(names); - offsets = Borrowed_PyMapping_GetItemString(obj, "offsets"); + int n = PyObject_Length(names); + PyObject *offsets = PyMapping_GetItemString(obj, "offsets"); if (!offsets) { PyErr_Clear(); } - titles = Borrowed_PyMapping_GetItemString(obj, "titles"); + PyObject *titles = PyMapping_GetItemString(obj, "titles"); if (!titles) { PyErr_Clear(); } @@ -1070,7 +1101,7 @@ _convert_from_dict(PyObject *obj, int align) * If a property 'aligned' is in the dict, it overrides the align flag * to be True if it not already true. */ - tmp = Borrowed_PyMapping_GetItemString(obj, "aligned"); + PyObject *tmp = PyMapping_GetItemString(obj, "aligned"); if (tmp == NULL) { PyErr_Clear(); } else { @@ -1078,23 +1109,25 @@ _convert_from_dict(PyObject *obj, int align) align = 1; } else if (tmp != Py_False) { + Py_DECREF(tmp); PyErr_SetString(PyExc_ValueError, "NumPy dtype descriptor includes 'aligned' entry, " "but its value is neither True nor False"); - return NULL; + goto fail; } + Py_DECREF(tmp); } - totalsize = 0; - for (i = 0; i < n; i++) { - PyObject *tup, *descr, *ind, *title, *name, *off; - int len, ret, _align = 1; - PyArray_Descr *newdescr; - + /* Types with fields need the Python C API for field access */ + char dtypeflags = NPY_NEEDS_PYAPI; + int totalsize = 0; + int maxalign = 0; + int has_out_of_order_fields = 0; + for (int i = 0; i < n; i++) { /* Build item to insert (descr, offset, [title])*/ - len = 2; - title = NULL; - ind = PyInt_FromLong(i); + int len = 2; + PyObject *title = NULL; + PyObject *ind = PyLong_FromLong(i); if (titles) { title=PyObject_GetItem(titles, ind); if (title && title != Py_None) { @@ -1105,39 +1138,34 @@ _convert_from_dict(PyObject *obj, int align) } PyErr_Clear(); } - tup = PyTuple_New(len); - descr = PyObject_GetItem(descrs, ind); + PyObject *tup = PyTuple_New(len); + PyObject *descr = PyObject_GetItem(descrs, ind); if (!descr) { Py_DECREF(tup); Py_DECREF(ind); goto fail; } - if (align) { - ret = PyArray_DescrAlignConverter(descr, &newdescr); - } - else { - ret = PyArray_DescrConverter(descr, &newdescr); - } + PyArray_Descr *newdescr = _convert_from_any(descr, align); Py_DECREF(descr); - if (ret == NPY_FAIL) { + if (newdescr == NULL) { Py_DECREF(tup); Py_DECREF(ind); goto fail; } PyTuple_SET_ITEM(tup, 0, (PyObject *)newdescr); + int _align = 1; if (align) { _align = newdescr->alignment; maxalign = PyArray_MAX(maxalign,_align); } if (offsets) { - long offset; - off = PyObject_GetItem(offsets, ind); + PyObject *off = PyObject_GetItem(offsets, ind); if (!off) { Py_DECREF(tup); Py_DECREF(ind); goto fail; } - offset = PyArray_PyIntAsInt(off); + long offset = PyArray_PyIntAsInt(off); if (error_converting(offset)) { Py_DECREF(off); Py_DECREF(tup); @@ -1146,14 +1174,14 @@ _convert_from_dict(PyObject *obj, int align) } Py_DECREF(off); if (offset < 0) { - PyErr_Format(PyExc_ValueError, "offset %d cannot be negative", - (int)offset); + PyErr_Format(PyExc_ValueError, "offset %ld cannot be negative", + offset); Py_DECREF(tup); Py_DECREF(ind); goto fail; } - PyTuple_SET_ITEM(tup, 1, PyInt_FromLong(offset)); + PyTuple_SET_ITEM(tup, 1, PyLong_FromLong(offset)); /* Flag whether the fields are specified out of order */ if (offset < totalsize) { has_out_of_order_fields = 1; @@ -1161,11 +1189,13 @@ _convert_from_dict(PyObject *obj, int align) /* If align=True, enforce field alignment */ if (align && offset % newdescr->alignment != 0) { PyErr_Format(PyExc_ValueError, - "offset %d for NumPy dtype with fields is " + "offset %ld for NumPy dtype with fields is " "not divisible by the field alignment %d " "with align=True", - (int)offset, (int)newdescr->alignment); - ret = NPY_FAIL; + offset, newdescr->alignment); + Py_DECREF(ind); + Py_DECREF(tup); + goto fail; } else if (offset + newdescr->elsize > totalsize) { totalsize = offset + newdescr->elsize; @@ -1175,24 +1205,19 @@ _convert_from_dict(PyObject *obj, int align) if (align && _align > 1) { totalsize = NPY_NEXT_ALIGNED_OFFSET(totalsize, _align); } - PyTuple_SET_ITEM(tup, 1, PyInt_FromLong(totalsize)); + PyTuple_SET_ITEM(tup, 1, PyLong_FromLong(totalsize)); totalsize += newdescr->elsize; } - if (ret == NPY_FAIL) { - Py_DECREF(ind); - Py_DECREF(tup); - goto fail; - } if (len == 3) { PyTuple_SET_ITEM(tup, 2, title); } - name = PyObject_GetItem(names, ind); + PyObject *name = PyObject_GetItem(names, ind); Py_DECREF(ind); if (!name) { Py_DECREF(tup); goto fail; } - if (!PyBaseString_Check(name)) { + if (!PyUnicode_Check(name)) { PyErr_SetString(PyExc_ValueError, "field names must be strings"); Py_DECREF(tup); @@ -1200,33 +1225,46 @@ _convert_from_dict(PyObject *obj, int align) } /* Insert into dictionary */ - if (PyDict_GetItem(fields, name) != NULL) { + if (PyDict_GetItemWithError(fields, name) != NULL) { PyErr_SetString(PyExc_ValueError, "name already used as a name or title"); Py_DECREF(tup); goto fail; } - PyDict_SetItem(fields, name, tup); + else if (PyErr_Occurred()) { + /* MemoryError during dict lookup */ + Py_DECREF(tup); + goto fail; + } + int ret = PyDict_SetItem(fields, name, tup); Py_DECREF(name); + if (ret < 0) { + Py_DECREF(tup); + goto fail; + } if (len == 3) { - if (PyBaseString_Check(title)) { - if (PyDict_GetItem(fields, title) != NULL) { + if (PyUnicode_Check(title)) { + if (PyDict_GetItemWithError(fields, title) != NULL) { PyErr_SetString(PyExc_ValueError, "title already used as a name or title."); Py_DECREF(tup); goto fail; } - PyDict_SetItem(fields, title, tup); + else if (PyErr_Occurred()) { + /* MemoryError during dict lookup */ + goto fail; + } + if (PyDict_SetItem(fields, title, tup) < 0) { + Py_DECREF(tup); + goto fail; + } } } Py_DECREF(tup); - if (ret == NPY_FAIL) { - goto fail; - } dtypeflags |= (newdescr->flags & NPY_FROM_FIELDS); } - new = PyArray_DescrNewFromType(NPY_VOID); + PyArray_Descr *new = PyArray_DescrNewFromType(NPY_VOID); if (new == NULL) { goto fail; } @@ -1238,23 +1276,27 @@ _convert_from_dict(PyObject *obj, int align) } new->elsize = totalsize; if (!PyTuple_Check(names)) { - names = PySequence_Tuple(names); - } - else { - Py_INCREF(names); + Py_SETREF(names, PySequence_Tuple(names)); + if (names == NULL) { + Py_DECREF(new); + goto fail; + } } new->names = names; new->fields = fields; new->flags = dtypeflags; + /* new takes responsibility for DECREFing names, fields */ + names = NULL; + fields = NULL; /* * If the fields weren't in order, and there was an OBJECT type, * need to verify that no OBJECT types overlap with something else. */ if (has_out_of_order_fields && PyDataType_REFCHK(new)) { - if (validate_object_field_overlap(new) < 0) { + if (_validate_object_field_overlap(new) < 0) { Py_DECREF(new); - return NULL; + goto fail; } } @@ -1264,55 +1306,69 @@ _convert_from_dict(PyObject *obj, int align) } /* Override the itemsize if provided */ - tmp = Borrowed_PyMapping_GetItemString(obj, "itemsize"); + tmp = PyMapping_GetItemString(obj, "itemsize"); if (tmp == NULL) { PyErr_Clear(); } else { - itemsize = (int)PyArray_PyIntAsInt(tmp); + int itemsize = (int)PyArray_PyIntAsInt(tmp); + Py_DECREF(tmp); if (error_converting(itemsize)) { Py_DECREF(new); - return NULL; + goto fail; } /* Make sure the itemsize isn't made too small */ if (itemsize < new->elsize) { PyErr_Format(PyExc_ValueError, "NumPy dtype descriptor requires %d bytes, " "cannot override to smaller itemsize of %d", - (int)new->elsize, (int)itemsize); + new->elsize, itemsize); Py_DECREF(new); - return NULL; + goto fail; } /* If align is set, make sure the alignment divides into the size */ - if (align && itemsize % new->alignment != 0) { + if (align && new->alignment > 0 && itemsize % new->alignment != 0) { PyErr_Format(PyExc_ValueError, "NumPy dtype descriptor requires alignment of %d bytes, " "which is not divisible into the specified itemsize %d", - (int)new->alignment, (int)itemsize); + new->alignment, itemsize); Py_DECREF(new); - return NULL; + goto fail; } /* Set the itemsize */ new->elsize = itemsize; } /* Add the metadata if provided */ - metadata = Borrowed_PyMapping_GetItemString(obj, "metadata"); + PyObject *metadata = PyMapping_GetItemString(obj, "metadata"); if (metadata == NULL) { PyErr_Clear(); } else if (new->metadata == NULL) { new->metadata = metadata; - Py_XINCREF(new->metadata); } - else if (PyDict_Merge(new->metadata, metadata, 0) == -1) { - Py_DECREF(new); - return NULL; + else { + int ret = PyDict_Merge(new->metadata, metadata, 0); + Py_DECREF(metadata); + if (ret < 0) { + Py_DECREF(new); + goto fail; + } } + + Py_XDECREF(fields); + Py_XDECREF(names); + Py_XDECREF(descrs); + Py_XDECREF(offsets); + Py_XDECREF(titles); return new; fail: Py_XDECREF(fields); + Py_XDECREF(names); + Py_XDECREF(descrs); + Py_XDECREF(offsets); + Py_XDECREF(titles); return NULL; } @@ -1345,6 +1401,174 @@ PyArray_DescrConverter2(PyObject *obj, PyArray_Descr **at) } } +/** + * Get a dtype instance from a python type + */ +static PyArray_Descr * +_convert_from_type(PyObject *obj) { + PyTypeObject *typ = (PyTypeObject*)obj; + + if (PyType_IsSubtype(typ, &PyGenericArrType_Type)) { + return PyArray_DescrFromTypeObject(obj); + } + else if (typ == &PyLong_Type) { + return PyArray_DescrFromType(NPY_LONG); + } + else if (typ == &PyFloat_Type) { + return PyArray_DescrFromType(NPY_DOUBLE); + } + else if (typ == &PyComplex_Type) { + return PyArray_DescrFromType(NPY_CDOUBLE); + } + else if (typ == &PyBool_Type) { + return PyArray_DescrFromType(NPY_BOOL); + } + else if (typ == &PyBytes_Type) { + /* + * TODO: This should be deprecated, and have special handling for + * dtype=bytes/"S" in coercion: It should not rely on "S0". + */ + return PyArray_DescrFromType(NPY_STRING); + } + else if (typ == &PyUnicode_Type) { + /* + * TODO: This should be deprecated, and have special handling for + * dtype=str/"U" in coercion: It should not rely on "U0". + */ + return PyArray_DescrFromType(NPY_UNICODE); + } + else if (typ == &PyMemoryView_Type) { + return PyArray_DescrFromType(NPY_VOID); + } + else if (typ == &PyBaseObject_Type) { + return PyArray_DescrFromType(NPY_OBJECT); + } + else { + PyArray_Descr *ret = _try_convert_from_dtype_attr(obj); + if ((PyObject *)ret != Py_NotImplemented) { + return ret; + } + Py_DECREF(ret); + + /* + * Note: this comes after _try_convert_from_dtype_attr because the ctypes + * type might override the dtype if numpy does not otherwise + * support it. + */ + ret = _try_convert_from_ctypes_type(typ); + if ((PyObject *)ret != Py_NotImplemented) { + return ret; + } + Py_DECREF(ret); + + /* + * All other classes are treated as object. This can be convenient + * to convey an intention of using it for a specific python type + * and possibly allow converting to a new type-specific dtype in the future. It may make sense to + * only allow this only within `dtype=...` keyword argument context + * in the future. + */ + return PyArray_DescrFromType(NPY_OBJECT); + } +} + + +static PyArray_Descr * +_convert_from_str(PyObject *obj, int align); + +static PyArray_Descr * +_convert_from_any(PyObject *obj, int align) +{ + /* default */ + if (obj == Py_None) { + return PyArray_DescrFromType(NPY_DEFAULT_TYPE); + } + else if (PyArray_DescrCheck(obj)) { + PyArray_Descr *ret = (PyArray_Descr *)obj; + Py_INCREF(ret); + return ret; + } + else if (PyType_Check(obj)) { + return _convert_from_type(obj); + } + /* or a typecode string */ + else if (PyBytes_Check(obj)) { + /* Allow bytes format strings: convert to unicode */ + PyObject *obj2 = PyUnicode_FromEncodedObject(obj, NULL, NULL); + if (obj2 == NULL) { + /* Convert the exception into a TypeError */ + if (PyErr_ExceptionMatches(PyExc_UnicodeDecodeError)) { + PyErr_SetString(PyExc_TypeError, + "data type not understood"); + } + return NULL; + } + PyArray_Descr *ret = _convert_from_str(obj2, align); + Py_DECREF(obj2); + return ret; + } + else if (PyUnicode_Check(obj)) { + return _convert_from_str(obj, align); + } + else if (PyTuple_Check(obj)) { + /* or a tuple */ + if (Py_EnterRecursiveCall( + " while trying to convert the given data type from" + " a tuple object" ) != 0) { + return NULL; + } + PyArray_Descr *ret = _convert_from_tuple(obj, align); + Py_LeaveRecursiveCall(); + return ret; + } + else if (PyList_Check(obj)) { + /* or a list */ + if (Py_EnterRecursiveCall( + " while trying to convert the given data type from" + " a list object" ) != 0) { + return NULL; + } + PyArray_Descr *ret = _convert_from_array_descr(obj, align); + Py_LeaveRecursiveCall(); + return ret; + } + else if (PyDict_Check(obj) || PyDictProxy_Check(obj)) { + /* or a dictionary */ + if (Py_EnterRecursiveCall( + " while trying to convert the given data type from" + " a dict object" ) != 0) { + return NULL; + } + PyArray_Descr *ret = _convert_from_dict(obj, align); + Py_LeaveRecursiveCall(); + return ret; + } + else if (PyArray_Check(obj)) { + PyErr_SetString(PyExc_TypeError, "Cannot construct a dtype from an array"); + return NULL; + } + else { + PyArray_Descr *ret = _try_convert_from_dtype_attr(obj); + if ((PyObject *)ret != Py_NotImplemented) { + return ret; + } + Py_DECREF(ret); + /* + * Note: this comes after _try_convert_from_dtype_attr because the ctypes + * type might override the dtype if numpy does not otherwise + * support it. + */ + ret = _try_convert_from_ctypes_type(Py_TYPE(obj)); + if ((PyObject *)ret != Py_NotImplemented) { + return ret; + } + Py_DECREF(ret); + PyErr_Format(PyExc_TypeError, "Cannot interpret '%R' as a data type", obj); + return NULL; + } +} + + /*NUMPY_API * Get typenum from an object -- None goes to NPY_DEFAULT_TYPE * This function takes a Python object representing a type and converts it @@ -1363,322 +1587,171 @@ PyArray_DescrConverter2(PyObject *obj, PyArray_Descr **at) NPY_NO_EXPORT int PyArray_DescrConverter(PyObject *obj, PyArray_Descr **at) { - int check_num = NPY_NOTYPE + 10; - PyObject *item; - int elsize = 0; - char endian = '='; - - *at = NULL; + *at = _convert_from_any(obj, 0); + return (*at) ? NPY_SUCCEED : NPY_FAIL; +} - /* default */ - if (obj == Py_None) { - *at = PyArray_DescrFromType(NPY_DEFAULT_TYPE); - return NPY_SUCCEED; +/** Convert a bytestring specification into a dtype */ +static PyArray_Descr * +_convert_from_str(PyObject *obj, int align) +{ + /* Check for a string typecode. */ + Py_ssize_t len = 0; + char const *type = PyUnicode_AsUTF8AndSize(obj, &len); + if (type == NULL) { + return NULL; } - if (PyArray_DescrCheck(obj)) { - *at = (PyArray_Descr *)obj; - Py_INCREF(*at); - return NPY_SUCCEED; + /* Empty string is invalid */ + if (len == 0) { + goto fail; } - if (PyType_Check(obj)) { - if (PyType_IsSubtype((PyTypeObject *)obj, &PyGenericArrType_Type)) { - *at = PyArray_DescrFromTypeObject(obj); - return (*at) ? NPY_SUCCEED : NPY_FAIL; - } - check_num = NPY_OBJECT; -#if !defined(NPY_PY3K) - if (obj == (PyObject *)(&PyInt_Type)) { - check_num = NPY_LONG; - } - else if (obj == (PyObject *)(&PyLong_Type)) { - check_num = NPY_LONGLONG; - } -#else - if (obj == (PyObject *)(&PyLong_Type)) { - check_num = NPY_LONG; - } -#endif - else if (obj == (PyObject *)(&PyFloat_Type)) { - check_num = NPY_DOUBLE; - } - else if (obj == (PyObject *)(&PyComplex_Type)) { - check_num = NPY_CDOUBLE; - } - else if (obj == (PyObject *)(&PyBool_Type)) { - check_num = NPY_BOOL; - } - else if (obj == (PyObject *)(&PyBytes_Type)) { - check_num = NPY_STRING; - } - else if (obj == (PyObject *)(&PyUnicode_Type)) { - check_num = NPY_UNICODE; - } -#if defined(NPY_PY3K) - else if (obj == (PyObject *)(&PyMemoryView_Type)) { -#else - else if (obj == (PyObject *)(&PyBuffer_Type)) { -#endif - check_num = NPY_VOID; - } - else { - *at = _arraydescr_fromobj(obj); - if (*at) { - return NPY_SUCCEED; - } - } - goto finish; + /* check for commas present or first (or second) element a digit */ + if (_check_for_commastring(type, len)) { + return _convert_from_commastring(obj, align); } - /* or a typecode string */ + /* Process the endian character. '|' is replaced by '='*/ + char endian = '='; + switch (type[0]) { + case '>': + case '<': + case '=': + endian = type[0]; + ++type; + --len; + break; - if (PyUnicode_Check(obj)) { - /* Allow unicode format strings: convert to bytes */ - int retval; - PyObject *obj2; - obj2 = PyUnicode_AsASCIIString(obj); - if (obj2 == NULL) { - return NPY_FAIL; - } - retval = PyArray_DescrConverter(obj2, at); - Py_DECREF(obj2); - return retval; + case '|': + endian = '='; + ++type; + --len; + break; } - if (PyBytes_Check(obj)) { - char *type = NULL; - Py_ssize_t len = 0; - - /* Check for a string typecode. */ - if (PyBytes_AsStringAndSize(obj, &type, &len) < 0) { - goto error; - } + /* Just an endian character is invalid */ + if (len == 0) { + goto fail; + } - /* Empty string is invalid */ - if (len == 0) { - goto fail; + /* Check for datetime format */ + if (is_datetime_typestr(type, len)) { + PyArray_Descr *ret = parse_dtype_from_datetime_typestr(type, len); + if (ret == NULL) { + return NULL; } - - /* check for commas present or first (or second) element a digit */ - if (_check_for_commastring(type, len)) { - *at = _convert_from_commastring(obj, 0); - return (*at) ? NPY_SUCCEED : NPY_FAIL; + /* ret has byte order '=' at this point */ + if (!PyArray_ISNBO(endian)) { + ret->byteorder = endian; } + return ret; + } - /* Process the endian character. '|' is replaced by '='*/ - switch (type[0]) { - case '>': - case '<': - case '=': - endian = type[0]; - ++type; - --len; - break; - - case '|': - endian = '='; - ++type; - --len; - break; - } + int check_num = NPY_NOTYPE + 10; + int elsize = 0; + /* A typecode like 'd' */ + if (len == 1) { + /* Python byte string characters are unsigned */ + check_num = (unsigned char) type[0]; + } + /* A kind + size like 'f8' */ + else { + char *typeend = NULL; + int kind; + + /* Parse the integer, make sure it's the rest of the string */ + elsize = (int)strtol(type + 1, &typeend, 10); + if (typeend - type == len) { + + kind = type[0]; + switch (kind) { + case NPY_STRINGLTR: + case NPY_STRINGLTR2: + check_num = NPY_STRING; + break; - /* Just an endian character is invalid */ - if (len == 0) { - goto fail; - } + /* + * When specifying length of UNICODE + * the number of characters is given to match + * the STRING interface. Each character can be + * more than one byte and itemsize must be + * the number of bytes. + */ + case NPY_UNICODELTR: + check_num = NPY_UNICODE; + elsize <<= 2; + break; - /* Check for datetime format */ - if (is_datetime_typestr(type, len)) { - *at = parse_dtype_from_datetime_typestr(type, len); - if (*at == NULL) { - return NPY_FAIL; - } - /* *at has byte order '=' at this point */ - if (!PyArray_ISNBO(endian)) { - (*at)->byteorder = endian; - } - return NPY_SUCCEED; - } + case NPY_VOIDLTR: + check_num = NPY_VOID; + break; - /* A typecode like 'd' */ - if (len == 1) { - check_num = type[0]; - } - /* A kind + size like 'f8' */ - else { - char *typeend = NULL; - int kind; - - /* Parse the integer, make sure it's the rest of the string */ - elsize = (int)strtol(type + 1, &typeend, 10); - if (typeend - type == len) { - - kind = type[0]; - switch (kind) { - case NPY_STRINGLTR: - case NPY_STRINGLTR2: - check_num = NPY_STRING; - break; - - /* - * When specifying length of UNICODE - * the number of characters is given to match - * the STRING interface. Each character can be - * more than one byte and itemsize must be - * the number of bytes. - */ - case NPY_UNICODELTR: - check_num = NPY_UNICODE; - elsize <<= 2; - break; - - case NPY_VOIDLTR: - check_num = NPY_VOID; - break; - - default: - if (elsize == 0) { - check_num = NPY_NOTYPE+10; - } - /* Support for generic processing c8, i4, f8, etc...*/ - else { - check_num = PyArray_TypestrConvert(elsize, kind); - if (check_num == NPY_NOTYPE) { - check_num += 10; - } - elsize = 0; + default: + if (elsize == 0) { + check_num = NPY_NOTYPE+10; + } + /* Support for generic processing c8, i4, f8, etc...*/ + else { + check_num = PyArray_TypestrConvert(elsize, kind); + if (check_num == NPY_NOTYPE) { + check_num += 10; } - } - } - } - } - else if (PyTuple_Check(obj)) { - /* or a tuple */ - *at = _convert_from_tuple(obj, 0); - if (*at == NULL){ - if (PyErr_Occurred()) { - return NPY_FAIL; - } - goto fail; - } - return NPY_SUCCEED; - } - else if (PyList_Check(obj)) { - /* or a list */ - *at = _convert_from_array_descr(obj,0); - if (*at == NULL) { - if (PyErr_Occurred()) { - return NPY_FAIL; - } - goto fail; - } - return NPY_SUCCEED; - } - else if (PyDict_Check(obj) || PyDictProxy_Check(obj)) { - /* or a dictionary */ - *at = _convert_from_dict(obj,0); - if (*at == NULL) { - if (PyErr_Occurred()) { - return NPY_FAIL; + elsize = 0; + } } - goto fail; - } - return NPY_SUCCEED; - } - else if (PyArray_Check(obj)) { - goto fail; - } - else { - *at = _arraydescr_fromobj(obj); - if (*at) { - return NPY_SUCCEED; - } - if (PyErr_Occurred()) { - return NPY_FAIL; } - goto fail; } + if (PyErr_Occurred()) { goto fail; } -finish: + PyArray_Descr *ret; if ((check_num == NPY_NOTYPE + 10) || - (*at = PyArray_DescrFromType(check_num)) == NULL) { + (ret = PyArray_DescrFromType(check_num)) == NULL) { PyErr_Clear(); /* Now check to see if the object is registered in typeDict */ - if (typeDict != NULL) { - item = PyDict_GetItem(typeDict, obj); -#if defined(NPY_PY3K) - if (!item && PyBytes_Check(obj)) { - PyObject *tmp; - tmp = PyUnicode_FromEncodedObject(obj, "ascii", "strict"); - if (tmp != NULL) { - item = PyDict_GetItem(typeDict, tmp); - Py_DECREF(tmp); - } - } -#endif - if (item) { - /* Check for a deprecated Numeric-style typecode */ - if (PyBytes_Check(obj)) { - char *type = NULL; - Py_ssize_t len = 0; - char *dep_tps[] = {"Bool", "Complex", "Float", "Int", - "Object0", "String0", "Timedelta64", - "Unicode0", "UInt", "Void0"}; - int ndep_tps = sizeof(dep_tps) / sizeof(dep_tps[0]); - int i; - - if (PyBytes_AsStringAndSize(obj, &type, &len) < 0) { - goto error; - } - for (i = 0; i < ndep_tps; ++i) { - char *dep_tp = dep_tps[i]; - - if (strncmp(type, dep_tp, strlen(dep_tp)) == 0) { - if (DEPRECATE("Numeric-style type codes are " - "deprecated and will result in " - "an error in the future.") < 0) { - goto fail; - } - } - } - } - return PyArray_DescrConverter(item, at); + if (typeDict == NULL) { + goto fail; + } + PyObject *item = PyDict_GetItemWithError(typeDict, obj); + if (item == NULL) { + if (PyErr_Occurred()) { + return NULL; } + goto fail; } - goto fail; + + /* + * Probably only ever dispatches to `_convert_from_type`, but who + * knows what users are injecting into `np.typeDict`. + */ + return _convert_from_any(item, align); } - if (PyDataType_ISUNSIZED(*at) && (*at)->elsize != elsize) { - PyArray_DESCR_REPLACE(*at); - (*at)->elsize = elsize; + if (PyDataType_ISUNSIZED(ret) && ret->elsize != elsize) { + PyArray_DESCR_REPLACE(ret); + if (ret == NULL) { + return NULL; + } + ret->elsize = elsize; } if (endian != '=' && PyArray_ISNBO(endian)) { endian = '='; } - if (endian != '=' && (*at)->byteorder != '|' - && (*at)->byteorder != endian) { - PyArray_DESCR_REPLACE(*at); - (*at)->byteorder = endian; + if (endian != '=' && ret->byteorder != '|' && ret->byteorder != endian) { + PyArray_DESCR_REPLACE(ret); + if (ret == NULL) { + return NULL; + } + ret->byteorder = endian; } - return NPY_SUCCEED; + return ret; fail: - if (PyBytes_Check(obj)) { - PyErr_Format(PyExc_TypeError, - "data type \"%s\" not understood", PyBytes_AS_STRING(obj)); - } - else { - PyErr_SetString(PyExc_TypeError, - "data type not understood"); - } - -error: - *at = NULL; - return NPY_FAIL; + PyErr_Format(PyExc_TypeError, "data type %R not understood", obj); + return NULL; } /** Array Descr Objects for dynamic types **/ @@ -1701,7 +1774,7 @@ PyArray_DescrConverter(PyObject *obj, PyArray_Descr **at) NPY_NO_EXPORT PyArray_Descr * PyArray_DescrNew(PyArray_Descr *base) { - PyArray_Descr *newdescr = PyObject_New(PyArray_Descr, &PyArrayDescr_Type); + PyArray_Descr *newdescr = PyObject_New(PyArray_Descr, Py_TYPE(base)); if (newdescr == NULL) { return NULL; @@ -1722,6 +1795,7 @@ PyArray_DescrNew(PyArray_Descr *base) newdescr->c_metadata = NPY_AUXDATA_CLONE(base->c_metadata); if (newdescr->c_metadata == NULL) { PyErr_NoMemory(); + /* TODO: This seems wrong, as the old fields get decref'd? */ Py_DECREF(newdescr); return NULL; } @@ -1757,9 +1831,10 @@ static void arraydescr_dealloc(PyArray_Descr *self) { if (self->fields == Py_None) { - fprintf(stderr, "*** Reference count error detected: \n" \ - "an attempt was made to deallocate %d (%c) ***\n", + fprintf(stderr, "*** Reference count error detected: " + "an attempt was made to deallocate the dtype %d (%c) ***\n", self->type_num, self->type); + assert(0); Py_INCREF(self); Py_INCREF(self); return; @@ -1805,7 +1880,7 @@ static PyMemberDef arraydescr_members[] = { }; static PyObject * -arraydescr_subdescr_get(PyArray_Descr *self) +arraydescr_subdescr_get(PyArray_Descr *self, void *NPY_UNUSED(ignored)) { if (!PyDataType_HASSUBARRAY(self)) { Py_RETURN_NONE; @@ -1815,7 +1890,7 @@ arraydescr_subdescr_get(PyArray_Descr *self) } NPY_NO_EXPORT PyObject * -arraydescr_protocol_typestr_get(PyArray_Descr *self) +arraydescr_protocol_typestr_get(PyArray_Descr *self, void *NPY_UNUSED(ignored)) { char basic_ = self->kind; char endian = self->byteorder; @@ -1832,98 +1907,51 @@ arraydescr_protocol_typestr_get(PyArray_Descr *self) size >>= 2; } if (self->type_num == NPY_OBJECT) { - ret = PyUString_FromFormat("%c%c", endian, basic_); + ret = PyUnicode_FromFormat("%c%c", endian, basic_); } else { - ret = PyUString_FromFormat("%c%c%d", endian, basic_, size); + ret = PyUnicode_FromFormat("%c%c%d", endian, basic_, size); + } + if (ret == NULL) { + return NULL; } + if (PyDataType_ISDATETIME(self)) { PyArray_DatetimeMetaData *meta; - meta = get_datetime_metadata_from_dtype(self); if (meta == NULL) { Py_DECREF(ret); return NULL; } + PyObject *umeta = metastr_to_unicode(meta, 0); + if (umeta == NULL) { + Py_DECREF(ret); + return NULL; + } - ret = append_metastr_to_string(meta, 0, ret); + Py_SETREF(ret, PyUnicode_Concat(ret, umeta)); + Py_DECREF(umeta); } - return ret; } static PyObject * -arraydescr_typename_get(PyArray_Descr *self) +arraydescr_name_get(PyArray_Descr *self, void *NPY_UNUSED(ignored)) { - static const char np_prefix[] = "numpy."; - const int np_prefix_len = sizeof(np_prefix) - 1; - PyTypeObject *typeobj = self->typeobj; + /* let python handle this */ + PyObject *_numpy_dtype; PyObject *res; - char *s; - int len; - int prefix_len; - int suffix_len; - - if (PyTypeNum_ISUSERDEF(self->type_num)) { - s = strrchr(typeobj->tp_name, '.'); - if (s == NULL) { - res = PyUString_FromString(typeobj->tp_name); - } - else { - res = PyUString_FromStringAndSize(s + 1, strlen(s) - 1); - } - return res; + _numpy_dtype = PyImport_ImportModule("numpy.core._dtype"); + if (_numpy_dtype == NULL) { + return NULL; } - else { - /* - * NumPy type or subclass - * - * res is derived from typeobj->tp_name with the following rules: - * - if starts with "numpy.", that prefix is removed - * - if ends with "_", that suffix is removed - */ - len = strlen(typeobj->tp_name); - - if (! strncmp(typeobj->tp_name, np_prefix, np_prefix_len)) { - prefix_len = np_prefix_len; - } - else { - prefix_len = 0; - } - - if (typeobj->tp_name[len - 1] == '_') { - suffix_len = 1; - } - else { - suffix_len = 0; - } - - len -= prefix_len; - len -= suffix_len; - res = PyUString_FromStringAndSize(typeobj->tp_name+prefix_len, len); - } - if (PyTypeNum_ISFLEXIBLE(self->type_num) && !PyDataType_ISUNSIZED(self)) { - PyObject *p; - p = PyUString_FromFormat("%d", self->elsize * 8); - PyUString_ConcatAndDel(&res, p); - } - if (PyDataType_ISDATETIME(self)) { - PyArray_DatetimeMetaData *meta; - - meta = get_datetime_metadata_from_dtype(self); - if (meta == NULL) { - Py_DECREF(res); - return NULL; - } - - res = append_metastr_to_string(meta, 0, res); - } - - return res; -} + res = PyObject_CallMethod(_numpy_dtype, "_name_get", "O", self); + Py_DECREF(_numpy_dtype); + return res; +} static PyObject * -arraydescr_base_get(PyArray_Descr *self) +arraydescr_base_get(PyArray_Descr *self, void *NPY_UNUSED(ignored)) { if (!PyDataType_HASSUBARRAY(self)) { Py_INCREF(self); @@ -1934,7 +1962,7 @@ arraydescr_base_get(PyArray_Descr *self) } static PyObject * -arraydescr_shape_get(PyArray_Descr *self) +arraydescr_shape_get(PyArray_Descr *self, void *NPY_UNUSED(ignored)) { if (!PyDataType_HASSUBARRAY(self)) { return PyTuple_New(0); @@ -1945,12 +1973,12 @@ arraydescr_shape_get(PyArray_Descr *self) } static PyObject * -arraydescr_ndim_get(PyArray_Descr *self) +arraydescr_ndim_get(PyArray_Descr *self, void *NPY_UNUSED(ignored)) { Py_ssize_t ndim; if (!PyDataType_HASSUBARRAY(self)) { - return PyInt_FromLong(0); + return PyLong_FromLong(0); } /* @@ -1958,12 +1986,12 @@ arraydescr_ndim_get(PyArray_Descr *self) * for tuple argument */ ndim = PyTuple_Size(self->subarray->shape); - return PyInt_FromLong(ndim); + return PyLong_FromLong(ndim); } NPY_NO_EXPORT PyObject * -arraydescr_protocol_descr_get(PyArray_Descr *self) +arraydescr_protocol_descr_get(PyArray_Descr *self, void *NPY_UNUSED(ignored)) { PyObject *dobj, *res; PyObject *_numpy_internal; @@ -1974,8 +2002,8 @@ arraydescr_protocol_descr_get(PyArray_Descr *self) if (dobj == NULL) { return NULL; } - PyTuple_SET_ITEM(dobj, 0, PyUString_FromString("")); - PyTuple_SET_ITEM(dobj, 1, arraydescr_protocol_typestr_get(self)); + PyTuple_SET_ITEM(dobj, 0, PyUnicode_FromString("")); + PyTuple_SET_ITEM(dobj, 1, arraydescr_protocol_typestr_get(self, NULL)); res = PyList_New(1); if (res == NULL) { Py_DECREF(dobj); @@ -2000,7 +2028,7 @@ arraydescr_protocol_descr_get(PyArray_Descr *self) * return 0 if neither (i.e. it's a copy of one) */ static PyObject * -arraydescr_isbuiltin_get(PyArray_Descr *self) +arraydescr_isbuiltin_get(PyArray_Descr *self, void *NPY_UNUSED(ignored)) { long val; val = 0; @@ -2010,7 +2038,7 @@ arraydescr_isbuiltin_get(PyArray_Descr *self) if (PyTypeNum_ISUSERDEF(self->type_num)) { val = 2; } - return PyInt_FromLong(val); + return PyLong_FromLong(val); } static int @@ -2025,7 +2053,7 @@ _arraydescr_isnative(PyArray_Descr *self) int offset; Py_ssize_t pos = 0; while (PyDict_Next(self->fields, &pos, &key, &value)) { - if NPY_TITLE_KEY(key, value) { + if (NPY_TITLE_KEY(key, value)) { continue; } if (!PyArg_ParseTuple(value, "Oi|O", &new, &offset, &title)) { @@ -2047,7 +2075,7 @@ _arraydescr_isnative(PyArray_Descr *self) * fields are defined */ static PyObject * -arraydescr_isnative_get(PyArray_Descr *self) +arraydescr_isnative_get(PyArray_Descr *self, void *NPY_UNUSED(ignored)) { PyObject *ret; int retval; @@ -2061,7 +2089,7 @@ arraydescr_isnative_get(PyArray_Descr *self) } static PyObject * -arraydescr_isalignedstruct_get(PyArray_Descr *self) +arraydescr_isalignedstruct_get(PyArray_Descr *self, void *NPY_UNUSED(ignored)) { PyObject *ret; ret = (self->flags&NPY_ALIGNED_STRUCT) ? Py_True : Py_False; @@ -2070,7 +2098,7 @@ arraydescr_isalignedstruct_get(PyArray_Descr *self) } static PyObject * -arraydescr_fields_get(PyArray_Descr *self) +arraydescr_fields_get(PyArray_Descr *self, void *NPY_UNUSED(ignored)) { if (!PyDataType_HASFIELDS(self)) { Py_RETURN_NONE; @@ -2079,7 +2107,7 @@ arraydescr_fields_get(PyArray_Descr *self) } static PyObject * -arraydescr_metadata_get(PyArray_Descr *self) +arraydescr_metadata_get(PyArray_Descr *self, void *NPY_UNUSED(ignored)) { if (self->metadata == NULL) { Py_RETURN_NONE; @@ -2088,7 +2116,7 @@ arraydescr_metadata_get(PyArray_Descr *self) } static PyObject * -arraydescr_hasobject_get(PyArray_Descr *self) +arraydescr_hasobject_get(PyArray_Descr *self, void *NPY_UNUSED(ignored)) { if (PyDataType_FLAGCHK(self, NPY_ITEM_HASOBJECT)) { Py_RETURN_TRUE; @@ -2099,7 +2127,7 @@ arraydescr_hasobject_get(PyArray_Descr *self) } static PyObject * -arraydescr_names_get(PyArray_Descr *self) +arraydescr_names_get(PyArray_Descr *self, void *NPY_UNUSED(ignored)) { if (!PyDataType_HASFIELDS(self)) { Py_RETURN_NONE; @@ -2109,7 +2137,8 @@ arraydescr_names_get(PyArray_Descr *self) } static int -arraydescr_names_set(PyArray_Descr *self, PyObject *val) +arraydescr_names_set( + PyArray_Descr *self, PyObject *val, void *NPY_UNUSED(ignored)) { int N = 0; int i; @@ -2153,7 +2182,7 @@ arraydescr_names_set(PyArray_Descr *self, PyObject *val) PyObject *item; int valid = 1; item = PySequence_GetItem(val, i); - valid = PyUString_Check(item); + valid = PyUnicode_Check(item); Py_DECREF(item); if (!valid) { PyErr_Format(PyExc_ValueError, @@ -2166,7 +2195,14 @@ arraydescr_names_set(PyArray_Descr *self, PyObject *val) self->hash = -1; /* Update dictionary keys in fields */ new_names = PySequence_Tuple(val); + if (new_names == NULL) { + return -1; + } new_fields = PyDict_New(); + if (new_fields == NULL) { + Py_DECREF(new_names); + return -1; + } for (i = 0; i < N; i++) { PyObject *key; PyObject *item; @@ -2174,20 +2210,35 @@ arraydescr_names_set(PyArray_Descr *self, PyObject *val) int ret; key = PyTuple_GET_ITEM(self->names, i); /* Borrowed references to item and new_key */ - item = PyDict_GetItem(self->fields, key); + item = PyDict_GetItemWithError(self->fields, key); + if (item == NULL) { + if (!PyErr_Occurred()) { + /* fields was missing the name it claimed to contain */ + PyErr_BadInternalCall(); + } + Py_DECREF(new_names); + Py_DECREF(new_fields); + return -1; + } new_key = PyTuple_GET_ITEM(new_names, i); /* Check for duplicates */ ret = PyDict_Contains(new_fields, new_key); - if (ret != 0) { - if (ret < 0) { - PyErr_Clear(); - } + if (ret < 0) { + Py_DECREF(new_names); + Py_DECREF(new_fields); + return -1; + } + else if (ret != 0) { PyErr_SetString(PyExc_ValueError, "Duplicate field names given."); Py_DECREF(new_names); Py_DECREF(new_fields); return -1; } - PyDict_SetItem(new_fields, new_key, item); + if (PyDict_SetItem(new_fields, new_key, item) < 0) { + Py_DECREF(new_names); + Py_DECREF(new_fields); + return -1; + } } /* Replace names */ @@ -2212,7 +2263,7 @@ static PyGetSetDef arraydescr_getsets[] = { (getter)arraydescr_protocol_typestr_get, NULL, NULL, NULL}, {"name", - (getter)arraydescr_typename_get, + (getter)arraydescr_name_get, NULL, NULL, NULL}, {"base", (getter)arraydescr_base_get, @@ -2249,9 +2300,46 @@ static PyGetSetDef arraydescr_getsets[] = { }; static PyObject * -arraydescr_new(PyTypeObject *NPY_UNUSED(subtype), +arraydescr_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds) { + if (subtype != &PyArrayDescr_Type) { + if (Py_TYPE(subtype) == &PyArrayDTypeMeta_Type && + (NPY_DT_SLOTS((PyArray_DTypeMeta *)subtype)) != NULL && + !NPY_DT_is_legacy((PyArray_DTypeMeta *)subtype) && + subtype->tp_new != PyArrayDescr_Type.tp_new) { + /* + * Appears to be a properly initialized user DType. Allocate + * it and initialize the main part as best we can. + * TODO: This should probably be a user function, and enforce + * things like the `elsize` being correctly set. + * TODO: This is EXPERIMENTAL API! + */ + PyArray_DTypeMeta *DType = (PyArray_DTypeMeta *)subtype; + PyArray_Descr *descr = (PyArray_Descr *)subtype->tp_alloc(subtype, 0); + if (descr == 0) { + PyErr_NoMemory(); + return NULL; + } + PyObject_Init((PyObject *)descr, subtype); + descr->f = &NPY_DT_SLOTS(DType)->f; + Py_XINCREF(DType->scalar_type); + descr->typeobj = DType->scalar_type; + descr->type_num = DType->type_num; + descr->flags = NPY_USE_GETITEM|NPY_USE_SETITEM; + descr->byteorder = '|'; /* If DType uses it, let it override */ + descr->elsize = -1; /* Initialize to invalid value */ + descr->hash = -1; + return (PyObject *)descr; + } + /* The DTypeMeta class should prevent this from happening. */ + PyErr_Format(PyExc_SystemError, + "'%S' must not inherit np.dtype.__new__(). User DTypes should " + "currently call `PyArrayDescr_Type.tp_new` from their new.", + subtype); + return NULL; + } + PyObject *odescr, *metadata=NULL; PyArray_Descr *descr, *conv; npy_bool align = NPY_FALSE; @@ -2268,12 +2356,8 @@ arraydescr_new(PyTypeObject *NPY_UNUSED(subtype), return NULL; } - if (align) { - if (!PyArray_DescrAlignConverter(odescr, &conv)) { - return NULL; - } - } - else if (!PyArray_DescrConverter(odescr, &conv)) { + conv = _convert_from_any(odescr, align); + if (conv == NULL) { return NULL; } @@ -2326,6 +2410,7 @@ arraydescr_new(PyTypeObject *NPY_UNUSED(subtype), return (PyObject *)conv; } + /* * Return a tuple of * (cleaned metadata dictionary, tuple with (str, num)) @@ -2365,11 +2450,11 @@ _get_pickleabletype_from_datetime_metadata(PyArray_Descr *dtype) PyTuple_SET_ITEM(dt_tuple, 0, PyBytes_FromString(_datetime_strings[meta->base])); PyTuple_SET_ITEM(dt_tuple, 1, - PyInt_FromLong(meta->num)); + PyLong_FromLong(meta->num)); PyTuple_SET_ITEM(dt_tuple, 2, - PyInt_FromLong(1)); + PyLong_FromLong(1)); PyTuple_SET_ITEM(dt_tuple, 3, - PyInt_FromLong(1)); + PyLong_FromLong(1)); PyTuple_SET_ITEM(ret, 1, dt_tuple); @@ -2401,7 +2486,7 @@ arraydescr_reduce(PyArray_Descr *self, PyObject *NPY_UNUSED(args)) if (ret == NULL) { return NULL; } - mod = PyImport_ImportModule("numpy.core.multiarray"); + mod = PyImport_ImportModule("numpy.core._multiarray_umath"); if (mod == NULL) { Py_DECREF(ret); return NULL; @@ -2424,9 +2509,9 @@ arraydescr_reduce(PyArray_Descr *self, PyObject *NPY_UNUSED(args)) if (self->type_num == NPY_UNICODE) { elsize >>= 2; } - obj = PyUString_FromFormat("%c%d",self->kind, elsize); + obj = PyUnicode_FromFormat("%c%d",self->kind, elsize); } - PyTuple_SET_ITEM(ret, 1, Py_BuildValue("(Nii)", obj, 0, 1)); + PyTuple_SET_ITEM(ret, 1, Py_BuildValue("(NOO)", obj, Py_False, Py_True)); /* * Now return the state which is at least byteorder, @@ -2442,7 +2527,7 @@ arraydescr_reduce(PyArray_Descr *self, PyObject *NPY_UNUSED(args)) if (PyDataType_ISDATETIME(self)) { PyObject *newobj; state = PyTuple_New(9); - PyTuple_SET_ITEM(state, 0, PyInt_FromLong(version)); + PyTuple_SET_ITEM(state, 0, PyLong_FromLong(version)); /* * newobj is a tuple of the Python metadata dictionary * and tuple of date_time info (str, num) @@ -2457,17 +2542,17 @@ arraydescr_reduce(PyArray_Descr *self, PyObject *NPY_UNUSED(args)) } else if (self->metadata) { state = PyTuple_New(9); - PyTuple_SET_ITEM(state, 0, PyInt_FromLong(version)); + PyTuple_SET_ITEM(state, 0, PyLong_FromLong(version)); Py_INCREF(self->metadata); PyTuple_SET_ITEM(state, 8, self->metadata); } else { /* Use version 3 pickle format */ state = PyTuple_New(8); - PyTuple_SET_ITEM(state, 0, PyInt_FromLong(3)); + PyTuple_SET_ITEM(state, 0, PyLong_FromLong(3)); } - PyTuple_SET_ITEM(state, 1, PyUString_FromFormat("%c", endian)); - PyTuple_SET_ITEM(state, 2, arraydescr_subdescr_get(self)); + PyTuple_SET_ITEM(state, 1, PyUnicode_FromFormat("%c", endian)); + PyTuple_SET_ITEM(state, 2, arraydescr_subdescr_get(self, NULL)); if (PyDataType_HASFIELDS(self)) { Py_INCREF(self->names); Py_INCREF(self->fields); @@ -2490,9 +2575,9 @@ arraydescr_reduce(PyArray_Descr *self, PyObject *NPY_UNUSED(args)) elsize = -1; alignment = -1; } - PyTuple_SET_ITEM(state, 5, PyInt_FromLong(elsize)); - PyTuple_SET_ITEM(state, 6, PyInt_FromLong(alignment)); - PyTuple_SET_ITEM(state, 7, PyInt_FromLong(self->flags)); + PyTuple_SET_ITEM(state, 5, PyLong_FromLong(elsize)); + PyTuple_SET_ITEM(state, 6, PyLong_FromLong(alignment)); + PyTuple_SET_ITEM(state, 7, PyLong_FromLong(self->flags)); PyTuple_SET_ITEM(ret, 2, state); return ret; @@ -2517,7 +2602,7 @@ _descr_find_object(PyArray_Descr *self) Py_ssize_t pos = 0; while (PyDict_Next(self->fields, &pos, &key, &value)) { - if NPY_TITLE_KEY(key, value) { + if (NPY_TITLE_KEY(key, value)) { continue; } if (!PyArg_ParseTuple(value, "Oi|O", &new, &offset, &title)) { @@ -2602,7 +2687,7 @@ arraydescr_setstate(PyArray_Descr *self, PyObject *args) default: /* raise an error */ if (PyTuple_GET_SIZE(PyTuple_GET_ITEM(args,0)) > 5) { - version = PyInt_AsLong(PyTuple_GET_ITEM(args, 0)); + version = PyLong_AsLong(PyTuple_GET_ITEM(args, 0)); } else { version = -1; @@ -2625,9 +2710,13 @@ arraydescr_setstate(PyArray_Descr *self, PyObject *args) if (version == 1 || version == 0) { if (fields != Py_None) { PyObject *key, *list; - key = PyInt_FromLong(-1); - list = PyDict_GetItem(fields, key); + key = PyLong_FromLong(-1); + list = PyDict_GetItemWithError(fields, key); if (!list) { + if (!PyErr_Occurred()) { + /* fields was missing the name it claimed to contain */ + PyErr_BadInternalCall(); + } return NULL; } Py_INCREF(list); @@ -2720,11 +2809,7 @@ arraydescr_setstate(PyArray_Descr *self, PyObject *args) subarray_shape = PyTuple_GET_ITEM(subarray, 1); if (PyNumber_Check(subarray_shape)) { PyObject *tmp; -#if defined(NPY_PY3K) tmp = PyNumber_Long(subarray_shape); -#else - tmp = PyNumber_Int(subarray_shape); -#endif if (tmp == NULL) { return NULL; } @@ -2762,7 +2847,7 @@ arraydescr_setstate(PyArray_Descr *self, PyObject *args) for (i = 0; i < PyTuple_GET_SIZE(names); ++i) { name = PyTuple_GET_ITEM(names, i); - if (!PyUString_Check(name)) { + if (!PyUnicode_Check(name)) { names_ok = 0; break; } @@ -2779,7 +2864,6 @@ arraydescr_setstate(PyArray_Descr *self, PyObject *args) } } else { -#if defined(NPY_PY3K) /* * To support pickle.load(f, encoding='bytes') for loading Py2 * generated pickles on Py3, we need to be more lenient and convert @@ -2803,8 +2887,12 @@ arraydescr_setstate(PyArray_Descr *self, PyObject *args) for (i = 0; i < PyTuple_GET_SIZE(names); ++i) { name = PyTuple_GET_ITEM(names, i); - field = PyDict_GetItem(fields, name); + field = PyDict_GetItemWithError(fields, name); if (!field) { + if (!PyErr_Occurred()) { + /* fields was missing the name it claimed to contain */ + PyErr_BadInternalCall(); + } return NULL; } @@ -2824,11 +2912,6 @@ arraydescr_setstate(PyArray_Descr *self, PyObject *args) return NULL; } } -#else - PyErr_Format(PyExc_ValueError, - "non-string names in Numpy dtype unpickling"); - return NULL; -#endif } } @@ -2866,14 +2949,13 @@ arraydescr_setstate(PyArray_Descr *self, PyObject *args) } if (PyDataType_ISDATETIME(self) && (metadata != NULL)) { - PyObject *old_metadata, *errmsg; + PyObject *old_metadata; PyArray_DatetimeMetaData temp_dt_data; if ((! PyTuple_Check(metadata)) || (PyTuple_Size(metadata) != 2)) { - errmsg = PyUString_FromString("Invalid datetime dtype (metadata, c_metadata): "); - PyUString_ConcatAndDel(&errmsg, PyObject_Repr(metadata)); - PyErr_SetObject(PyExc_ValueError, errmsg); - Py_DECREF(errmsg); + PyErr_Format(PyExc_ValueError, + "Invalid datetime dtype (metadata, c_metadata): %R", + metadata); return NULL; } @@ -2917,35 +2999,8 @@ arraydescr_setstate(PyArray_Descr *self, PyObject *args) NPY_NO_EXPORT int PyArray_DescrAlignConverter(PyObject *obj, PyArray_Descr **at) { - if (PyDict_Check(obj) || PyDictProxy_Check(obj)) { - *at = _convert_from_dict(obj, 1); - } - else if (PyBytes_Check(obj)) { - *at = _convert_from_commastring(obj, 1); - } - else if (PyUnicode_Check(obj)) { - PyObject *tmp; - tmp = PyUnicode_AsASCIIString(obj); - *at = _convert_from_commastring(tmp, 1); - Py_DECREF(tmp); - } - else if (PyTuple_Check(obj)) { - *at = _convert_from_tuple(obj, 1); - } - else if (PyList_Check(obj)) { - *at = _convert_from_array_descr(obj, 1); - } - else { - return PyArray_DescrConverter(obj, at); - } - if (*at == NULL) { - if (!PyErr_Occurred()) { - PyErr_SetString(PyExc_ValueError, - "data-type-descriptor not understood"); - } - return NPY_FAIL; - } - return NPY_SUCCEED; + *at = _convert_from_any(obj, 1); + return (*at) ? NPY_SUCCEED : NPY_FAIL; } /*NUMPY_API @@ -2956,32 +3011,13 @@ PyArray_DescrAlignConverter(PyObject *obj, PyArray_Descr **at) NPY_NO_EXPORT int PyArray_DescrAlignConverter2(PyObject *obj, PyArray_Descr **at) { - if (PyDict_Check(obj) || PyDictProxy_Check(obj)) { - *at = _convert_from_dict(obj, 1); - } - else if (PyBytes_Check(obj)) { - *at = _convert_from_commastring(obj, 1); - } - else if (PyUnicode_Check(obj)) { - PyObject *tmp; - tmp = PyUnicode_AsASCIIString(obj); - *at = _convert_from_commastring(tmp, 1); - Py_DECREF(tmp); - } - else if (PyList_Check(obj)) { - *at = _convert_from_array_descr(obj, 1); + if (obj == Py_None) { + *at = NULL; + return NPY_SUCCEED; } else { - return PyArray_DescrConverter2(obj, at); + return PyArray_DescrAlignConverter(obj, at); } - if (*at == NULL) { - if (!PyErr_Occurred()) { - PyErr_SetString(PyExc_ValueError, - "data-type-descriptor not understood"); - } - return NPY_FAIL; - } - return NPY_SUCCEED; } @@ -3039,10 +3075,10 @@ PyArray_DescrNewByteorder(PyArray_Descr *self, char newendian) newfields = PyDict_New(); /* make new dictionary with replaced PyArray_Descr Objects */ while (PyDict_Next(self->fields, &pos, &key, &value)) { - if NPY_TITLE_KEY(key, value) { + if (NPY_TITLE_KEY(key, value)) { continue; } - if (!PyUString_Check(key) || !PyTuple_Check(value) || + if (!PyUnicode_Check(key) || !PyTuple_Check(value) || ((len=PyTuple_GET_SIZE(value)) < 2)) { continue; } @@ -3063,8 +3099,13 @@ PyArray_DescrNewByteorder(PyArray_Descr *self, char newendian) Py_INCREF(old); PyTuple_SET_ITEM(newvalue, i, old); } - PyDict_SetItem(newfields, key, newvalue); + int ret = PyDict_SetItem(newfields, key, newvalue); Py_DECREF(newvalue); + if (ret < 0) { + Py_DECREF(newfields); + Py_DECREF(new); + return NULL; + } } Py_DECREF(new->fields); new->fields = newfields; @@ -3090,6 +3131,30 @@ arraydescr_newbyteorder(PyArray_Descr *self, PyObject *args) return (PyObject *)PyArray_DescrNewByteorder(self, endian); } +static PyObject * +arraydescr_class_getitem(PyObject *cls, PyObject *args) +{ + PyObject *generic_alias; + +#ifdef Py_GENERICALIASOBJECT_H + Py_ssize_t args_len; + + args_len = PyTuple_Check(args) ? PyTuple_Size(args) : 1; + if (args_len != 1) { + return PyErr_Format(PyExc_TypeError, + "Too %s arguments for %s", + args_len > 1 ? "many" : "few", + ((PyTypeObject *)cls)->tp_name); + } + generic_alias = Py_GenericAlias(cls, args); +#else + PyErr_SetString(PyExc_TypeError, + "Type subscription requires python >= 3.9"); + generic_alias = NULL; +#endif + return generic_alias; +} + static PyMethodDef arraydescr_methods[] = { /* for pickling */ {"__reduce__", @@ -3101,6 +3166,10 @@ static PyMethodDef arraydescr_methods[] = { {"newbyteorder", (PyCFunction)arraydescr_newbyteorder, METH_VARARGS, NULL}, + /* for typing; requires python >= 3.9 */ + {"__class_getitem__", + (PyCFunction)arraydescr_class_getitem, + METH_CLASS | METH_O, NULL}, {NULL, NULL, 0, NULL} /* sentinel */ }; @@ -3166,536 +3235,82 @@ is_dtype_struct_simple_unaligned_layout(PyArray_Descr *dtype) } /* - * Returns a string representation of a structured array, - * in a list format. + * The general dtype repr function. */ static PyObject * -arraydescr_struct_list_str(PyArray_Descr *dtype) +arraydescr_repr(PyArray_Descr *dtype) { - PyObject *names, *key, *fields, *ret, *tmp, *tup, *title; - Py_ssize_t i, names_size; - PyArray_Descr *fld_dtype; - int fld_offset; - - names = dtype->names; - names_size = PyTuple_GET_SIZE(names); - fields = dtype->fields; - - /* Build up a string to make the list */ - - /* Go through all the names */ - ret = PyUString_FromString("["); - for (i = 0; i < names_size; ++i) { - key = PyTuple_GET_ITEM(names, i); - tup = PyDict_GetItem(fields, key); - if (tup == NULL) { - return 0; - } - title = NULL; - if (!PyArg_ParseTuple(tup, "Oi|O", &fld_dtype, &fld_offset, &title)) { - PyErr_Clear(); - return 0; - } - PyUString_ConcatAndDel(&ret, PyUString_FromString("(")); - /* Check for whether to do titles as well */ - if (title != NULL && title != Py_None) { - PyUString_ConcatAndDel(&ret, PyUString_FromString("(")); - PyUString_ConcatAndDel(&ret, PyObject_Repr(title)); - PyUString_ConcatAndDel(&ret, PyUString_FromString(", ")); - PyUString_ConcatAndDel(&ret, PyObject_Repr(key)); - PyUString_ConcatAndDel(&ret, PyUString_FromString("), ")); - } - else { - PyUString_ConcatAndDel(&ret, PyObject_Repr(key)); - PyUString_ConcatAndDel(&ret, PyUString_FromString(", ")); - } - /* Special case subarray handling here */ - if (PyDataType_HASSUBARRAY(fld_dtype)) { - tmp = arraydescr_construction_repr( - fld_dtype->subarray->base, 0, 1); - PyUString_ConcatAndDel(&ret, tmp); - PyUString_ConcatAndDel(&ret, PyUString_FromString(", ")); - PyUString_ConcatAndDel(&ret, - PyObject_Str(fld_dtype->subarray->shape)); - } - else { - tmp = arraydescr_construction_repr(fld_dtype, 0, 1); - PyUString_ConcatAndDel(&ret, tmp); - } - PyUString_ConcatAndDel(&ret, PyUString_FromString(")")); - if (i != names_size - 1) { - PyUString_ConcatAndDel(&ret, PyUString_FromString(", ")); - } + PyObject *_numpy_dtype; + PyObject *res; + _numpy_dtype = PyImport_ImportModule("numpy.core._dtype"); + if (_numpy_dtype == NULL) { + return NULL; } - PyUString_ConcatAndDel(&ret, PyUString_FromString("]")); - - return ret; + res = PyObject_CallMethod(_numpy_dtype, "__repr__", "O", dtype); + Py_DECREF(_numpy_dtype); + return res; } - /* - * Returns a string representation of a structured array, - * in a dict format. + * The general dtype str function. */ -static PyObject * -arraydescr_struct_dict_str(PyArray_Descr *dtype, int includealignedflag) -{ - PyObject *names, *key, *fields, *ret, *tmp, *tup, *title; - Py_ssize_t i, names_size; - PyArray_Descr *fld_dtype; - int fld_offset, has_titles; - - names = dtype->names; - names_size = PyTuple_GET_SIZE(names); - fields = dtype->fields; - has_titles = 0; - - /* Build up a string to make the dictionary */ - - /* First, the names */ - ret = PyUString_FromString("{'names':["); - for (i = 0; i < names_size; ++i) { - key = PyTuple_GET_ITEM(names, i); - PyUString_ConcatAndDel(&ret, PyObject_Repr(key)); - if (i != names_size - 1) { - PyUString_ConcatAndDel(&ret, PyUString_FromString(",")); - } - } - /* Second, the formats */ - PyUString_ConcatAndDel(&ret, PyUString_FromString("], 'formats':[")); - for (i = 0; i < names_size; ++i) { - key = PyTuple_GET_ITEM(names, i); - tup = PyDict_GetItem(fields, key); - if (tup == NULL) { - return 0; - } - title = NULL; - if (!PyArg_ParseTuple(tup, "Oi|O", &fld_dtype, &fld_offset, &title)) { - PyErr_Clear(); - return 0; - } - /* Check for whether to do titles as well */ - if (title != NULL && title != Py_None) { - has_titles = 1; - } - tmp = arraydescr_construction_repr(fld_dtype, 0, 1); - PyUString_ConcatAndDel(&ret, tmp); - if (i != names_size - 1) { - PyUString_ConcatAndDel(&ret, PyUString_FromString(",")); - } - } - /* Third, the offsets */ - PyUString_ConcatAndDel(&ret, PyUString_FromString("], 'offsets':[")); - for (i = 0; i < names_size; ++i) { - key = PyTuple_GET_ITEM(names, i); - tup = PyDict_GetItem(fields, key); - if (tup == NULL) { - return 0; - } - if (!PyArg_ParseTuple(tup, "Oi|O", &fld_dtype, &fld_offset, &title)) { - PyErr_Clear(); - return 0; - } - PyUString_ConcatAndDel(&ret, PyUString_FromFormat("%d", fld_offset)); - if (i != names_size - 1) { - PyUString_ConcatAndDel(&ret, PyUString_FromString(",")); - } - } - /* Fourth, the titles */ - if (has_titles) { - PyUString_ConcatAndDel(&ret, PyUString_FromString("], 'titles':[")); - for (i = 0; i < names_size; ++i) { - key = PyTuple_GET_ITEM(names, i); - tup = PyDict_GetItem(fields, key); - if (tup == NULL) { - return 0; - } - title = Py_None; - if (!PyArg_ParseTuple(tup, "Oi|O", &fld_dtype, - &fld_offset, &title)) { - PyErr_Clear(); - return 0; - } - PyUString_ConcatAndDel(&ret, PyObject_Repr(title)); - if (i != names_size - 1) { - PyUString_ConcatAndDel(&ret, PyUString_FromString(",")); - } - } - } - if (includealignedflag && (dtype->flags&NPY_ALIGNED_STRUCT)) { - /* Finally, the itemsize/itemsize and aligned flag */ - PyUString_ConcatAndDel(&ret, - PyUString_FromFormat("], 'itemsize':%d, 'aligned':True}", - (int)dtype->elsize)); - } - else { - /* Finally, the itemsize/itemsize*/ - PyUString_ConcatAndDel(&ret, - PyUString_FromFormat("], 'itemsize':%d}", (int)dtype->elsize)); - } - - return ret; -} - -/* Produces a string representation for a structured dtype */ -static PyObject * -arraydescr_struct_str(PyArray_Descr *dtype, int includealignflag) -{ - PyObject *sub; - - /* - * The list str representation can't include the 'align=' flag, - * so if it is requested and the struct has the aligned flag set, - * we must use the dict str instead. - */ - if (!(includealignflag && (dtype->flags&NPY_ALIGNED_STRUCT)) && - is_dtype_struct_simple_unaligned_layout(dtype)) { - sub = arraydescr_struct_list_str(dtype); - } - else { - sub = arraydescr_struct_dict_str(dtype, includealignflag); - } - - /* If the data type isn't the default, void, show it */ - if (dtype->typeobj != &PyVoidArrType_Type) { - /* - * Note: We cannot get the type name from dtype->typeobj->tp_name - * because its value depends on whether the type is dynamically or - * statically allocated. Instead use __name__ and __module__. - * See https://docs.python.org/2/c-api/typeobj.html. - */ - - PyObject *str_name, *namestr, *str_module, *modulestr, *ret; - - str_name = PyUString_FromString("__name__"); - namestr = PyObject_GetAttr((PyObject*)(dtype->typeobj), str_name); - Py_DECREF(str_name); - - if (namestr == NULL) { - /* this should never happen since types always have __name__ */ - PyErr_Format(PyExc_RuntimeError, - "dtype does not have a __name__ attribute"); - return NULL; - } - - str_module = PyUString_FromString("__module__"); - modulestr = PyObject_GetAttr((PyObject*)(dtype->typeobj), str_module); - Py_DECREF(str_module); - - ret = PyUString_FromString("("); - if (modulestr != NULL) { - /* Note: if modulestr == NULL, the type is unpicklable */ - PyUString_ConcatAndDel(&ret, modulestr); - PyUString_ConcatAndDel(&ret, PyUString_FromString(".")); - } - PyUString_ConcatAndDel(&ret, namestr); - PyUString_ConcatAndDel(&ret, PyUString_FromString(", ")); - PyUString_ConcatAndDel(&ret, sub); - PyUString_ConcatAndDel(&ret, PyUString_FromString(")")); - return ret; - } - else { - return sub; - } -} - -/* Produces a string representation for a subarray dtype */ -static PyObject * -arraydescr_subarray_str(PyArray_Descr *dtype) -{ - PyObject *p, *ret; - - ret = PyUString_FromString("("); - p = arraydescr_construction_repr(dtype->subarray->base, 0, 1); - PyUString_ConcatAndDel(&ret, p); - PyUString_ConcatAndDel(&ret, PyUString_FromString(", ")); - PyUString_ConcatAndDel(&ret, PyObject_Str(dtype->subarray->shape)); - PyUString_ConcatAndDel(&ret, PyUString_FromString(")")); - - return ret; -} - static PyObject * arraydescr_str(PyArray_Descr *dtype) { - PyObject *sub; - - if (PyDataType_HASFIELDS(dtype)) { - sub = arraydescr_struct_str(dtype, 1); - } - else if (PyDataType_HASSUBARRAY(dtype)) { - sub = arraydescr_subarray_str(dtype); - } - else if (PyDataType_ISFLEXIBLE(dtype) || !PyArray_ISNBO(dtype->byteorder)) { - sub = arraydescr_protocol_typestr_get(dtype); - } - else { - sub = arraydescr_typename_get(dtype); - } - return sub; -} - -/* - * The dtype repr function specifically for structured arrays. - */ -static PyObject * -arraydescr_struct_repr(PyArray_Descr *dtype) -{ - PyObject *sub, *s; - - s = PyUString_FromString("dtype("); - sub = arraydescr_struct_str(dtype, 0); - if (sub == NULL) { + PyObject *_numpy_dtype; + PyObject *res; + _numpy_dtype = PyImport_ImportModule("numpy.core._dtype"); + if (_numpy_dtype == NULL) { return NULL; } - - PyUString_ConcatAndDel(&s, sub); - - /* If it's an aligned structure, add the align=True parameter */ - if (dtype->flags&NPY_ALIGNED_STRUCT) { - PyUString_ConcatAndDel(&s, PyUString_FromString(", align=True")); - } - - PyUString_ConcatAndDel(&s, PyUString_FromString(")")); - return s; -} - -/* See descriptor.h for documentation */ -NPY_NO_EXPORT PyObject * -arraydescr_construction_repr(PyArray_Descr *dtype, int includealignflag, - int shortrepr) -{ - PyObject *ret; - PyArray_DatetimeMetaData *meta; - char byteorder[2]; - - if (PyDataType_HASFIELDS(dtype)) { - return arraydescr_struct_str(dtype, includealignflag); - } - else if (PyDataType_HASSUBARRAY(dtype)) { - return arraydescr_subarray_str(dtype); - } - - /* Normalize byteorder to '<' or '>' */ - switch (dtype->byteorder) { - case NPY_NATIVE: - byteorder[0] = NPY_NATBYTE; - break; - case NPY_SWAP: - byteorder[0] = NPY_OPPBYTE; - break; - case NPY_IGNORE: - byteorder[0] = '\0'; - break; - default: - byteorder[0] = dtype->byteorder; - break; - } - byteorder[1] = '\0'; - - /* Handle booleans, numbers, and custom dtypes */ - if (dtype->type_num == NPY_BOOL) { - if (shortrepr) { - return PyUString_FromString("'?'"); - } - else { - return PyUString_FromString("'bool'"); - } - } - else if (PyTypeNum_ISNUMBER(dtype->type_num)) { - /* Short repr with endianness, like '<f8' */ - if (shortrepr || (dtype->byteorder != NPY_NATIVE && - dtype->byteorder != NPY_IGNORE)) { - return PyUString_FromFormat("'%s%c%d'", byteorder, - (int)dtype->kind, dtype->elsize); - } - /* Longer repr, like 'float64' */ - else { - char *kindstr; - switch (dtype->kind) { - case 'u': - kindstr = "uint"; - break; - case 'i': - kindstr = "int"; - break; - case 'f': - kindstr = "float"; - break; - case 'c': - kindstr = "complex"; - break; - default: - PyErr_Format(PyExc_RuntimeError, - "internal dtype repr error, unknown kind '%c'", - (int)dtype->kind); - return NULL; - } - return PyUString_FromFormat("'%s%d'", kindstr, 8*dtype->elsize); - } - } - else if (PyTypeNum_ISUSERDEF(dtype->type_num)) { - char *s = strrchr(dtype->typeobj->tp_name, '.'); - if (s == NULL) { - return PyUString_FromString(dtype->typeobj->tp_name); - } - else { - return PyUString_FromStringAndSize(s + 1, strlen(s) - 1); - } - } - - /* All the rest which don't fit in the same pattern */ - switch (dtype->type_num) { - /* - * The object reference may be different sizes on different - * platforms, so it should never include the itemsize here. - */ - case NPY_OBJECT: - return PyUString_FromString("'O'"); - - case NPY_STRING: - if (PyDataType_ISUNSIZED(dtype)) { - return PyUString_FromString("'S'"); - } - else { - return PyUString_FromFormat("'S%d'", (int)dtype->elsize); - } - - case NPY_UNICODE: - if (PyDataType_ISUNSIZED(dtype)) { - return PyUString_FromFormat("'%sU'", byteorder); - } - else { - return PyUString_FromFormat("'%sU%d'", byteorder, - (int)dtype->elsize / 4); - } - - case NPY_VOID: - if (PyDataType_ISUNSIZED(dtype)) { - return PyUString_FromString("'V'"); - } - else { - return PyUString_FromFormat("'V%d'", (int)dtype->elsize); - } - - case NPY_DATETIME: - meta = get_datetime_metadata_from_dtype(dtype); - if (meta == NULL) { - return NULL; - } - ret = PyUString_FromFormat("'%sM8", byteorder); - ret = append_metastr_to_string(meta, 0, ret); - PyUString_ConcatAndDel(&ret, PyUString_FromString("'")); - return ret; - - case NPY_TIMEDELTA: - meta = get_datetime_metadata_from_dtype(dtype); - if (meta == NULL) { - return NULL; - } - ret = PyUString_FromFormat("'%sm8", byteorder); - ret = append_metastr_to_string(meta, 0, ret); - PyUString_ConcatAndDel(&ret, PyUString_FromString("'")); - return ret; - - default: - PyErr_SetString(PyExc_RuntimeError, "Internal error: NumPy dtype " - "unrecognized type number"); - return NULL; - } -} - -/* - * The general dtype repr function. - */ -static PyObject * -arraydescr_repr(PyArray_Descr *dtype) -{ - PyObject *ret; - - if (PyDataType_HASFIELDS(dtype)) { - return arraydescr_struct_repr(dtype); - } - else { - ret = PyUString_FromString("dtype("); - PyUString_ConcatAndDel(&ret, - arraydescr_construction_repr(dtype, 1, 0)); - PyUString_ConcatAndDel(&ret, PyUString_FromString(")")); - return ret; - } + res = PyObject_CallMethod(_numpy_dtype, "__str__", "O", dtype); + Py_DECREF(_numpy_dtype); + return res; } static PyObject * arraydescr_richcompare(PyArray_Descr *self, PyObject *other, int cmp_op) { - PyArray_Descr *new = NULL; - PyObject *result = Py_NotImplemented; - if (!PyArray_DescrCheck(other)) { - if (PyArray_DescrConverter(other, &new) == NPY_FAIL) { - return NULL; - } - } - else { - new = (PyArray_Descr *)other; - Py_INCREF(new); + PyArray_Descr *new = _convert_from_any(other, 0); + if (new == NULL) { + /* Cannot convert `other` to dtype */ + PyErr_Clear(); + Py_RETURN_NOTIMPLEMENTED; } + + npy_bool ret; switch (cmp_op) { case Py_LT: - if (!PyArray_EquivTypes(self, new) && PyArray_CanCastTo(self, new)) { - result = Py_True; - } - else { - result = Py_False; - } - break; + ret = !PyArray_EquivTypes(self, new) && PyArray_CanCastTo(self, new); + Py_DECREF(new); + return PyBool_FromLong(ret); case Py_LE: - if (PyArray_CanCastTo(self, new)) { - result = Py_True; - } - else { - result = Py_False; - } - break; + ret = PyArray_CanCastTo(self, new); + Py_DECREF(new); + return PyBool_FromLong(ret); case Py_EQ: - if (PyArray_EquivTypes(self, new)) { - result = Py_True; - } - else { - result = Py_False; - } - break; + ret = PyArray_EquivTypes(self, new); + Py_DECREF(new); + return PyBool_FromLong(ret); case Py_NE: - if (PyArray_EquivTypes(self, new)) - result = Py_False; - else - result = Py_True; - break; + ret = !PyArray_EquivTypes(self, new); + Py_DECREF(new); + return PyBool_FromLong(ret); case Py_GT: - if (!PyArray_EquivTypes(self, new) && PyArray_CanCastTo(new, self)) { - result = Py_True; - } - else { - result = Py_False; - } - break; + ret = !PyArray_EquivTypes(self, new) && PyArray_CanCastTo(new, self); + Py_DECREF(new); + return PyBool_FromLong(ret); case Py_GE: - if (PyArray_CanCastTo(new, self)) { - result = Py_True; - } - else { - result = Py_False; - } - break; + ret = PyArray_CanCastTo(new, self); + Py_DECREF(new); + return PyBool_FromLong(ret); default: - result = Py_NotImplemented; + Py_DECREF(new); + Py_RETURN_NOTIMPLEMENTED; } - - Py_XDECREF(new); - Py_INCREF(result); - return result; } static int -descr_nonzero(PyObject *self) +descr_nonzero(PyObject *NPY_UNUSED(self)) { /* `bool(np.dtype(...)) == True` for all dtypes. Needed to override default * nonzero implementation, which checks if `len(object) > 0`. */ @@ -3703,20 +3318,7 @@ descr_nonzero(PyObject *self) } static PyNumberMethods descr_as_number = { - (binaryfunc)0, /* nb_add */ - (binaryfunc)0, /* nb_subtract */ - (binaryfunc)0, /* nb_multiply */ - #if defined(NPY_PY3K) - #else - (binaryfunc)0, /* nb_divide */ - #endif - (binaryfunc)0, /* nb_remainder */ - (binaryfunc)0, /* nb_divmod */ - (ternaryfunc)0, /* nb_power */ - (unaryfunc)0, /* nb_negative */ - (unaryfunc)0, /* nb_positive */ - (unaryfunc)0, /* nb_absolute */ - (inquiry)descr_nonzero, /* nb_nonzero */ + .nb_bool = (inquiry)descr_nonzero, }; /************************************************************************* @@ -3749,7 +3351,7 @@ descr_repeat(PyObject *self, Py_ssize_t length) if (tup == NULL) { return NULL; } - PyArray_DescrConverter(tup, &new); + new = _convert_from_any(tup, 0); Py_DECREF(tup); return (PyObject *)new; } @@ -3758,15 +3360,7 @@ static int _check_has_fields(PyArray_Descr *self) { if (!PyDataType_HASFIELDS(self)) { - PyObject *astr = arraydescr_str(self); -#if defined(NPY_PY3K) - PyObject *bstr = PyUnicode_AsUnicodeEscapeString(astr); - Py_DECREF(astr); - astr = bstr; -#endif - PyErr_Format(PyExc_KeyError, - "There are no fields in dtype %s.", PyBytes_AsString(astr)); - Py_DECREF(astr); + PyErr_Format(PyExc_KeyError, "There are no fields in dtype %S.", self); return -1; } else { @@ -3777,26 +3371,15 @@ _check_has_fields(PyArray_Descr *self) static PyObject * _subscript_by_name(PyArray_Descr *self, PyObject *op) { - PyObject *obj = PyDict_GetItem(self->fields, op); - PyObject *descr; - PyObject *s; - + PyObject *obj = PyDict_GetItemWithError(self->fields, op); if (obj == NULL) { - if (PyUnicode_Check(op)) { - s = PyUnicode_AsUnicodeEscapeString(op); - } - else { - s = op; - } - - PyErr_Format(PyExc_KeyError, - "Field named \'%s\' not found.", PyBytes_AsString(s)); - if (s != op) { - Py_DECREF(s); + if (!PyErr_Occurred()) { + PyErr_Format(PyExc_KeyError, + "Field named %R not found.", op); } return NULL; } - descr = PyTuple_GET_ITEM(obj, 0); + PyObject *descr = PyTuple_GET_ITEM(obj, 0); Py_INCREF(descr); return descr; } @@ -3805,12 +3388,128 @@ static PyObject * _subscript_by_index(PyArray_Descr *self, Py_ssize_t i) { PyObject *name = PySequence_GetItem(self->names, i); + PyObject *ret; if (name == NULL) { PyErr_Format(PyExc_IndexError, "Field index %zd out of range.", i); return NULL; } - return _subscript_by_name(self, name); + ret = _subscript_by_name(self, name); + Py_DECREF(name); + return ret; +} + +static npy_bool +_is_list_of_strings(PyObject *obj) +{ + int seqlen, i; + if (!PyList_CheckExact(obj)) { + return NPY_FALSE; + } + seqlen = PyList_GET_SIZE(obj); + for (i = 0; i < seqlen; i++) { + PyObject *item = PyList_GET_ITEM(obj, i); + if (!PyUnicode_Check(item)) { + return NPY_FALSE; + } + } + + return NPY_TRUE; +} + +NPY_NO_EXPORT PyArray_Descr * +arraydescr_field_subset_view(PyArray_Descr *self, PyObject *ind) +{ + int seqlen, i; + PyObject *fields = NULL; + PyObject *names = NULL; + PyArray_Descr *view_dtype; + + seqlen = PySequence_Size(ind); + if (seqlen == -1) { + return NULL; + } + + fields = PyDict_New(); + if (fields == NULL) { + goto fail; + } + names = PyTuple_New(seqlen); + if (names == NULL) { + goto fail; + } + + for (i = 0; i < seqlen; i++) { + PyObject *name; + PyObject *tup; + + name = PySequence_GetItem(ind, i); + if (name == NULL) { + goto fail; + } + + /* Let the names tuple steal a reference now, so we don't need to + * decref name if an error occurs further on. + */ + PyTuple_SET_ITEM(names, i, name); + + tup = PyDict_GetItemWithError(self->fields, name); + if (tup == NULL) { + if (!PyErr_Occurred()) { + PyErr_SetObject(PyExc_KeyError, name); + } + goto fail; + } + + /* disallow use of titles as index */ + if (PyTuple_Size(tup) == 3) { + PyObject *title = PyTuple_GET_ITEM(tup, 2); + int titlecmp = PyObject_RichCompareBool(title, name, Py_EQ); + if (titlecmp < 0) { + goto fail; + } + if (titlecmp == 1) { + /* if title == name, we were given a title, not a field name */ + PyErr_SetString(PyExc_KeyError, + "cannot use field titles in multi-field index"); + goto fail; + } + if (PyDict_SetItem(fields, title, tup) < 0) { + goto fail; + } + } + /* disallow duplicate field indices */ + if (PyDict_Contains(fields, name)) { + PyObject *msg = NULL; + PyObject *fmt = PyUnicode_FromString( + "duplicate field of name {!r}"); + if (fmt != NULL) { + msg = PyObject_CallMethod(fmt, "format", "O", name); + Py_DECREF(fmt); + } + PyErr_SetObject(PyExc_ValueError, msg); + Py_XDECREF(msg); + goto fail; + } + if (PyDict_SetItem(fields, name, tup) < 0) { + goto fail; + } + } + + view_dtype = PyArray_DescrNewFromType(NPY_VOID); + if (view_dtype == NULL) { + goto fail; + } + view_dtype->elsize = self->elsize; + view_dtype->names = names; + view_dtype->fields = fields; + view_dtype->flags = self->flags; + return view_dtype; + +fail: + Py_XDECREF(fields); + Py_XDECREF(names); + return NULL; } static PyObject * @@ -3820,9 +3519,12 @@ descr_subscript(PyArray_Descr *self, PyObject *op) return NULL; } - if (PyBaseString_Check(op)) { + if (PyUnicode_Check(op)) { return _subscript_by_name(self, op); } + else if (_is_list_of_strings(op)) { + return (PyObject *)arraydescr_field_subset_view(self, op); + } else { Py_ssize_t i = PyArray_PyIntAsIntp(op); if (error_converting(i)) { @@ -3830,7 +3532,8 @@ descr_subscript(PyArray_Descr *self, PyObject *op) PyObject *err = PyErr_Occurred(); if (PyErr_GivenExceptionMatches(err, PyExc_TypeError)) { PyErr_SetString(PyExc_TypeError, - "Field key must be an integer, string, or unicode."); + "Field key must be an integer field offset, " + "single field name, or list of field names."); } return NULL; } @@ -3859,62 +3562,32 @@ static PyMappingMethods descr_as_mapping = { /****************** End of Mapping Protocol ******************************/ -NPY_NO_EXPORT PyTypeObject PyArrayDescr_Type = { -#if defined(NPY_PY3K) - PyVarObject_HEAD_INIT(NULL, 0) -#else - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ -#endif - "numpy.dtype", /* tp_name */ - sizeof(PyArray_Descr), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)arraydescr_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ -#if defined(NPY_PY3K) - (void *)0, /* tp_reserved */ -#else - 0, /* tp_compare */ -#endif - (reprfunc)arraydescr_repr, /* tp_repr */ - &descr_as_number, /* tp_as_number */ - &descr_as_sequence, /* tp_as_sequence */ - &descr_as_mapping, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - (reprfunc)arraydescr_str, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - 0, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - (richcmpfunc)arraydescr_richcompare, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - arraydescr_methods, /* tp_methods */ - arraydescr_members, /* tp_members */ - arraydescr_getsets, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - arraydescr_new, /* tp_new */ - 0, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - 0, /* tp_del */ - 0, /* tp_version_tag */ + +/* + * NOTE: Since this is a MetaClass, the name has Full appended here, the + * correct name of the type is PyArrayDescr_Type. + */ +NPY_NO_EXPORT PyArray_DTypeMeta PyArrayDescr_TypeFull = { + {{ + /* NULL represents `type`, this is set to DTypeMeta at import time */ + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "numpy.dtype", + .tp_basicsize = sizeof(PyArray_Descr), + .tp_dealloc = (destructor)arraydescr_dealloc, + .tp_repr = (reprfunc)arraydescr_repr, + .tp_as_number = &descr_as_number, + .tp_as_sequence = &descr_as_sequence, + .tp_as_mapping = &descr_as_mapping, + .tp_str = (reprfunc)arraydescr_str, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .tp_richcompare = (richcmpfunc)arraydescr_richcompare, + .tp_methods = arraydescr_methods, + .tp_members = arraydescr_members, + .tp_getset = arraydescr_getsets, + .tp_new = arraydescr_new, + },}, + .type_num = -1, + .flags = NPY_DT_ABSTRACT, + .singleton = NULL, + .scalar_type = NULL, }; diff --git a/numpy/core/src/multiarray/descriptor.h b/numpy/core/src/multiarray/descriptor.h index f950411955b7..f832958dae90 100644 --- a/numpy/core/src/multiarray/descriptor.h +++ b/numpy/core/src/multiarray/descriptor.h @@ -1,45 +1,33 @@ -#ifndef _NPY_ARRAYDESCR_H_ -#define _NPY_ARRAYDESCR_H_ +#ifndef NUMPY_CORE_SRC_MULTIARRAY_DESCRIPTOR_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_DESCRIPTOR_H_ -NPY_NO_EXPORT PyObject *arraydescr_protocol_typestr_get(PyArray_Descr *); -NPY_NO_EXPORT PyObject *arraydescr_protocol_descr_get(PyArray_Descr *self); +NPY_NO_EXPORT PyObject *arraydescr_protocol_typestr_get( + PyArray_Descr *, void *); +NPY_NO_EXPORT PyObject *arraydescr_protocol_descr_get( + PyArray_Descr *self, void *); NPY_NO_EXPORT PyObject * array_set_typeDict(PyObject *NPY_UNUSED(ignored), PyObject *args); NPY_NO_EXPORT PyArray_Descr * -_arraydescr_fromobj(PyObject *obj); +_arraydescr_try_convert_from_dtype_attr(PyObject *obj); NPY_NO_EXPORT int is_dtype_struct_simple_unaligned_layout(PyArray_Descr *dtype); /* - * Creates a string repr of the dtype, excluding the 'dtype()' part - * surrounding the object. This object may be a string, a list, or - * a dict depending on the nature of the dtype. This - * is the object passed as the first parameter to the dtype - * constructor, and if no additional constructor parameters are - * given, will reproduce the exact memory layout. + * Filter the fields of a dtype to only those in the list of strings, ind. * - * If 'shortrepr' is non-zero, this creates a shorter repr using - * 'kind' and 'itemsize', instead of the longer type name. + * No type checking is performed on the input. * - * If 'includealignflag' is true, this includes the 'align=True' parameter - * inside the struct dtype construction dict when needed. Use this flag - * if you want a proper repr string without the 'dtype()' part around it. - * - * If 'includealignflag' is false, this does not preserve the - * 'align=True' parameter or sticky NPY_ALIGNED_STRUCT flag for - * struct arrays like the regular repr does, because the 'align' - * flag is not part of first dtype constructor parameter. This - * mode is intended for a full 'repr', where the 'align=True' is - * provided as the second parameter. + * Raises: + * ValueError - if a field is repeated + * KeyError - if an invalid field name (or any field title) is used */ -NPY_NO_EXPORT PyObject * -arraydescr_construction_repr(PyArray_Descr *dtype, int includealignflag, - int shortrepr); +NPY_NO_EXPORT PyArray_Descr * +arraydescr_field_subset_view(PyArray_Descr *self, PyObject *ind); -extern NPY_NO_EXPORT char *_datetime_strings[]; +extern NPY_NO_EXPORT char const *_datetime_strings[]; -#endif +#endif /* NUMPY_CORE_SRC_MULTIARRAY_DESCRIPTOR_H_ */ diff --git a/numpy/core/src/multiarray/dlpack.c b/numpy/core/src/multiarray/dlpack.c new file mode 100644 index 000000000000..291e60a226a7 --- /dev/null +++ b/numpy/core/src/multiarray/dlpack.c @@ -0,0 +1,408 @@ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE + +#define PY_SSIZE_T_CLEAN +#include <Python.h> +#include <dlpack/dlpack.h> + +#include "numpy/arrayobject.h" +#include "common/npy_argparse.h" + +#include "common/dlpack/dlpack.h" +#include "common/npy_dlpack.h" + +static void +array_dlpack_deleter(DLManagedTensor *self) +{ + PyArrayObject *array = (PyArrayObject *)self->manager_ctx; + // This will also free the strides as it's one allocation. + PyMem_Free(self->dl_tensor.shape); + PyMem_Free(self); + Py_XDECREF(array); +} + +/* This is exactly as mandated by dlpack */ +static void dlpack_capsule_deleter(PyObject *self) { + if (PyCapsule_IsValid(self, NPY_DLPACK_USED_CAPSULE_NAME)) { + return; + } + + /* an exception may be in-flight, we must save it in case we create another one */ + PyObject *type, *value, *traceback; + PyErr_Fetch(&type, &value, &traceback); + + DLManagedTensor *managed = + (DLManagedTensor *)PyCapsule_GetPointer(self, NPY_DLPACK_CAPSULE_NAME); + if (managed == NULL) { + PyErr_WriteUnraisable(self); + goto done; + } + /* + * the spec says the deleter can be NULL if there is no way for the caller + * to provide a reasonable destructor. + */ + if (managed->deleter) { + managed->deleter(managed); + /* TODO: is the deleter allowed to set a python exception? */ + assert(!PyErr_Occurred()); + } + +done: + PyErr_Restore(type, value, traceback); +} + +/* used internally, almost identical to dlpack_capsule_deleter() */ +static void array_dlpack_internal_capsule_deleter(PyObject *self) +{ + /* an exception may be in-flight, we must save it in case we create another one */ + PyObject *type, *value, *traceback; + PyErr_Fetch(&type, &value, &traceback); + + DLManagedTensor *managed = + (DLManagedTensor *)PyCapsule_GetPointer(self, NPY_DLPACK_INTERNAL_CAPSULE_NAME); + if (managed == NULL) { + PyErr_WriteUnraisable(self); + goto done; + } + /* + * the spec says the deleter can be NULL if there is no way for the caller + * to provide a reasonable destructor. + */ + if (managed->deleter) { + managed->deleter(managed); + /* TODO: is the deleter allowed to set a python exception? */ + assert(!PyErr_Occurred()); + } + +done: + PyErr_Restore(type, value, traceback); +} + + +// This function cannot return NULL, but it can fail, +// So call PyErr_Occurred to check if it failed after +// calling it. +static DLDevice +array_get_dl_device(PyArrayObject *self) { + DLDevice ret; + ret.device_type = kDLCPU; + ret.device_id = 0; + PyObject *base = PyArray_BASE(self); + // The outer if is due to the fact that NumPy arrays are on the CPU + // by default (if not created from DLPack). + if (PyCapsule_IsValid(base, NPY_DLPACK_INTERNAL_CAPSULE_NAME)) { + DLManagedTensor *managed = PyCapsule_GetPointer( + base, NPY_DLPACK_INTERNAL_CAPSULE_NAME); + if (managed == NULL) { + return ret; + } + return managed->dl_tensor.device; + } + return ret; +} + + +PyObject * +array_dlpack(PyArrayObject *self, + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) +{ + PyObject *stream = Py_None; + NPY_PREPARE_ARGPARSER; + if (npy_parse_arguments("__dlpack__", args, len_args, kwnames, + "$stream", NULL, &stream, NULL, NULL, NULL)) { + return NULL; + } + + if (stream != Py_None) { + PyErr_SetString(PyExc_RuntimeError, "NumPy only supports " + "stream=None."); + return NULL; + } + + if ( !(PyArray_FLAGS(self) & NPY_ARRAY_WRITEABLE)) { + PyErr_SetString(PyExc_TypeError, "NumPy currently only supports " + "dlpack for writeable arrays"); + return NULL; + } + + npy_intp itemsize = PyArray_ITEMSIZE(self); + int ndim = PyArray_NDIM(self); + npy_intp *strides = PyArray_STRIDES(self); + npy_intp *shape = PyArray_SHAPE(self); + + if (!PyArray_IS_C_CONTIGUOUS(self) && PyArray_SIZE(self) != 1) { + for (int i = 0; i < ndim; ++i) { + if (strides[i] % itemsize != 0) { + PyErr_SetString(PyExc_RuntimeError, + "DLPack only supports strides which are a multiple of " + "itemsize."); + return NULL; + } + } + } + + DLDataType managed_dtype; + PyArray_Descr *dtype = PyArray_DESCR(self); + + if (PyDataType_ISBYTESWAPPED(dtype)) { + PyErr_SetString(PyExc_TypeError, "DLPack only supports native " + "byte swapping."); + return NULL; + } + + managed_dtype.bits = 8 * itemsize; + managed_dtype.lanes = 1; + + if (PyDataType_ISSIGNED(dtype)) { + managed_dtype.code = kDLInt; + } + else if (PyDataType_ISUNSIGNED(dtype)) { + managed_dtype.code = kDLUInt; + } + else if (PyDataType_ISFLOAT(dtype)) { + // We can't be sure that the dtype is + // IEEE or padded. + if (itemsize > 8) { + PyErr_SetString(PyExc_TypeError, "DLPack only supports IEEE " + "floating point types without padding."); + return NULL; + } + managed_dtype.code = kDLFloat; + } + else if (PyDataType_ISCOMPLEX(dtype)) { + // We can't be sure that the dtype is + // IEEE or padded. + if (itemsize > 16) { + PyErr_SetString(PyExc_TypeError, "DLPack only supports IEEE " + "complex point types without padding."); + return NULL; + } + managed_dtype.code = kDLComplex; + } + else { + PyErr_SetString(PyExc_TypeError, + "DLPack only supports signed/unsigned integers, float " + "and complex dtypes."); + return NULL; + } + + DLDevice device = array_get_dl_device(self); + if (PyErr_Occurred()) { + return NULL; + } + + DLManagedTensor *managed = PyMem_Malloc(sizeof(DLManagedTensor)); + if (managed == NULL) { + PyErr_NoMemory(); + return NULL; + } + + /* + * Note: the `dlpack.h` header suggests/standardizes that `data` must be + * 256-byte aligned. We ignore this intentionally, because `__dlpack__` + * standardizes that `byte_offset` must be 0 (for now) to not break pytorch: + * https://github.com/data-apis/array-api/issues/293#issuecomment-964111413 + * + * We further assume that exporting fully unaligned data is OK even without + * `byte_offset` since the standard does not reject it. + * Presumably, pytorch will support importing `byte_offset != 0` and NumPy + * can choose to use it starting about 2023. At that point, it may be + * that NumPy MUST use `byte_offset` to adhere to the standard (as + * specified in the header)! + */ + managed->dl_tensor.data = PyArray_DATA(self); + managed->dl_tensor.byte_offset = 0; + managed->dl_tensor.device = device; + managed->dl_tensor.dtype = managed_dtype; + + int64_t *managed_shape_strides = PyMem_Malloc(sizeof(int64_t) * ndim * 2); + if (managed_shape_strides == NULL) { + PyErr_NoMemory(); + PyMem_Free(managed); + return NULL; + } + + int64_t *managed_shape = managed_shape_strides; + int64_t *managed_strides = managed_shape_strides + ndim; + for (int i = 0; i < ndim; ++i) { + managed_shape[i] = shape[i]; + // Strides in DLPack are items; in NumPy are bytes. + managed_strides[i] = strides[i] / itemsize; + } + + managed->dl_tensor.ndim = ndim; + managed->dl_tensor.shape = managed_shape; + managed->dl_tensor.strides = NULL; + if (PyArray_SIZE(self) != 1 && !PyArray_IS_C_CONTIGUOUS(self)) { + managed->dl_tensor.strides = managed_strides; + } + managed->dl_tensor.byte_offset = 0; + managed->manager_ctx = self; + managed->deleter = array_dlpack_deleter; + + PyObject *capsule = PyCapsule_New(managed, NPY_DLPACK_CAPSULE_NAME, + dlpack_capsule_deleter); + if (capsule == NULL) { + PyMem_Free(managed); + PyMem_Free(managed_shape_strides); + return NULL; + } + + // the capsule holds a reference + Py_INCREF(self); + return capsule; +} + +PyObject * +array_dlpack_device(PyArrayObject *self, PyObject *NPY_UNUSED(args)) +{ + DLDevice device = array_get_dl_device(self); + if (PyErr_Occurred()) { + return NULL; + } + return Py_BuildValue("ii", device.device_type, device.device_id); +} + +NPY_NO_EXPORT PyObject * +_from_dlpack(PyObject *NPY_UNUSED(self), PyObject *obj) { + PyObject *capsule = PyObject_CallMethod((PyObject *)obj->ob_type, + "__dlpack__", "O", obj); + if (capsule == NULL) { + return NULL; + } + + DLManagedTensor *managed = + (DLManagedTensor *)PyCapsule_GetPointer(capsule, + NPY_DLPACK_CAPSULE_NAME); + + if (managed == NULL) { + Py_DECREF(capsule); + return NULL; + } + + const int ndim = managed->dl_tensor.ndim; + if (ndim > NPY_MAXDIMS) { + PyErr_SetString(PyExc_RuntimeError, + "maxdims of DLPack tensor is higher than the supported " + "maxdims."); + Py_DECREF(capsule); + return NULL; + } + + DLDeviceType device_type = managed->dl_tensor.device.device_type; + if (device_type != kDLCPU && + device_type != kDLCUDAHost && + device_type != kDLROCMHost && + device_type != kDLCUDAManaged) { + PyErr_SetString(PyExc_RuntimeError, + "Unsupported device in DLTensor."); + Py_DECREF(capsule); + return NULL; + } + + if (managed->dl_tensor.dtype.lanes != 1) { + PyErr_SetString(PyExc_RuntimeError, + "Unsupported lanes in DLTensor dtype."); + Py_DECREF(capsule); + return NULL; + } + + int typenum = -1; + const uint8_t bits = managed->dl_tensor.dtype.bits; + const npy_intp itemsize = bits / 8; + switch (managed->dl_tensor.dtype.code) { + case kDLInt: + switch (bits) + { + case 8: typenum = NPY_INT8; break; + case 16: typenum = NPY_INT16; break; + case 32: typenum = NPY_INT32; break; + case 64: typenum = NPY_INT64; break; + } + break; + case kDLUInt: + switch (bits) + { + case 8: typenum = NPY_UINT8; break; + case 16: typenum = NPY_UINT16; break; + case 32: typenum = NPY_UINT32; break; + case 64: typenum = NPY_UINT64; break; + } + break; + case kDLFloat: + switch (bits) + { + case 16: typenum = NPY_FLOAT16; break; + case 32: typenum = NPY_FLOAT32; break; + case 64: typenum = NPY_FLOAT64; break; + } + break; + case kDLComplex: + switch (bits) + { + case 64: typenum = NPY_COMPLEX64; break; + case 128: typenum = NPY_COMPLEX128; break; + } + break; + } + + if (typenum == -1) { + PyErr_SetString(PyExc_RuntimeError, + "Unsupported dtype in DLTensor."); + Py_DECREF(capsule); + return NULL; + } + + npy_intp shape[NPY_MAXDIMS]; + npy_intp strides[NPY_MAXDIMS]; + + for (int i = 0; i < ndim; ++i) { + shape[i] = managed->dl_tensor.shape[i]; + // DLPack has elements as stride units, NumPy has bytes. + if (managed->dl_tensor.strides != NULL) { + strides[i] = managed->dl_tensor.strides[i] * itemsize; + } + } + + char *data = (char *)managed->dl_tensor.data + + managed->dl_tensor.byte_offset; + + PyArray_Descr *descr = PyArray_DescrFromType(typenum); + if (descr == NULL) { + Py_DECREF(capsule); + return NULL; + } + + PyObject *ret = PyArray_NewFromDescr(&PyArray_Type, descr, ndim, shape, + managed->dl_tensor.strides != NULL ? strides : NULL, data, 0, NULL); + if (ret == NULL) { + Py_DECREF(capsule); + return NULL; + } + + PyObject *new_capsule = PyCapsule_New(managed, + NPY_DLPACK_INTERNAL_CAPSULE_NAME, + array_dlpack_internal_capsule_deleter); + if (new_capsule == NULL) { + Py_DECREF(capsule); + Py_DECREF(ret); + return NULL; + } + + if (PyArray_SetBaseObject((PyArrayObject *)ret, new_capsule) < 0) { + Py_DECREF(capsule); + Py_DECREF(ret); + return NULL; + } + + if (PyCapsule_SetName(capsule, NPY_DLPACK_USED_CAPSULE_NAME) < 0) { + Py_DECREF(capsule); + Py_DECREF(ret); + return NULL; + } + + Py_DECREF(capsule); + return ret; +} + + diff --git a/numpy/core/src/multiarray/dragon4.c b/numpy/core/src/multiarray/dragon4.c index c14653ac570a..ce0293615228 100644 --- a/numpy/core/src/multiarray/dragon4.c +++ b/numpy/core/src/multiarray/dragon4.c @@ -1,31 +1,33 @@ /* * Copyright (c) 2014 Ryan Juckett - * http://www.ryanjuckett.com/ * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * - * 3. This notice may not be removed or altered from any source - * distribution. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. */ /* * This file contains a modified version of Ryan Juckett's Dragon4 - * implementation, which has been ported from C++ to C and which has + * implementation, obtained from http://www.ryanjuckett.com, + * which has been ported from C++ to C and which has * modifications specific to printing floats in numpy. + * + * Ryan Juckett's original code was under the Zlib license; he gave numpy + * permission to include it under the MIT license instead. */ #include "dragon4.h" @@ -114,7 +116,7 @@ LogBase2_64(npy_uint64 val) return LogBase2_32((npy_uint32)val); } -#if defined(HAVE_LDOUBLE_IEEE_QUAD_LE) +#if defined(HAVE_LDOUBLE_IEEE_QUAD_LE) || defined(HAVE_LDOUBLE_IEEE_QUAD_BE) static npy_uint32 LogBase2_128(npy_uint64 hi, npy_uint64 lo) { @@ -217,7 +219,8 @@ BigInt_Set_uint64(BigInt *i, npy_uint64 val) #if (defined(HAVE_LDOUBLE_IBM_DOUBLE_DOUBLE_LE) || \ defined(HAVE_LDOUBLE_IBM_DOUBLE_DOUBLE_BE) || \ - defined(HAVE_LDOUBLE_IEEE_QUAD_LE)) + defined(HAVE_LDOUBLE_IEEE_QUAD_LE) || \ + defined(HAVE_LDOUBLE_IEEE_QUAD_BE)) static void BigInt_Set_2x_uint64(BigInt *i, npy_uint64 hi, npy_uint64 lo) { @@ -873,7 +876,7 @@ BigInt_Pow2(BigInt *result, npy_uint32 exponent) result->length = blockIdx + 1; bitIdx = (exponent % 32); - result->blocks[blockIdx] |= (1 << bitIdx); + result->blocks[blockIdx] |= ((npy_uint32)1 << bitIdx); } /* @@ -1127,8 +1130,9 @@ BigInt_ShiftLeft(BigInt *result, npy_uint32 shift) * * exponent - value exponent in base 2 * * mantissaBit - index of the highest set mantissa bit * * hasUnequalMargins - is the high margin twice as large as the low margin - * * cutoffMode - how to interpret cutoffNumber: fractional or total digits? - * * cutoffNumber - cut off printing after this many digits. -1 for no cutoff + * * cutoffMode - how to interpret cutoff_*: fractional or total digits? + * * cutoff_max - cut off printing after this many digits. -1 for no cutoff + * * cutoff_min - print at least this many digits. -1 for no cutoff * * pOutBuffer - buffer to output into * * bufferSize - maximum characters that can be printed to pOutBuffer * * pOutExponent - the base 10 exponent of the first digit @@ -1139,7 +1143,7 @@ static npy_uint32 Dragon4(BigInt *bigints, const npy_int32 exponent, const npy_uint32 mantissaBit, const npy_bool hasUnequalMargins, const DigitMode digitMode, const CutoffMode cutoffMode, - npy_int32 cutoffNumber, char *pOutBuffer, + npy_int32 cutoff_max, npy_int32 cutoff_min, char *pOutBuffer, npy_uint32 bufferSize, npy_int32 *pOutExponent) { char *curDigit = pOutBuffer; @@ -1166,7 +1170,8 @@ Dragon4(BigInt *bigints, const npy_int32 exponent, BigInt *temp2 = &bigints[6]; const npy_float64 log10_2 = 0.30102999566398119521373889472449; - npy_int32 digitExponent, cutoffExponent, hiBlock; + npy_int32 digitExponent, hiBlock; + npy_int32 cutoff_max_Exponent, cutoff_min_Exponent; npy_uint32 outputDigit; /* current digit being output */ npy_uint32 outputLen; npy_bool isEven = BigInt_IsEven(mantissa); @@ -1291,9 +1296,9 @@ Dragon4(BigInt *bigints, const npy_int32 exponent, * increases the number. This will either correct digitExponent to an * accurate value or it will clamp it above the accurate value. */ - if (cutoffNumber >= 0 && cutoffMode == CutoffMode_FractionLength && - digitExponent <= -cutoffNumber) { - digitExponent = -cutoffNumber + 1; + if (cutoff_max >= 0 && cutoffMode == CutoffMode_FractionLength && + digitExponent <= -cutoff_max) { + digitExponent = -cutoff_max + 1; } @@ -1344,26 +1349,44 @@ Dragon4(BigInt *bigints, const npy_int32 exponent, } /* - * Compute the cutoff exponent (the exponent of the final digit to print). - * Default to the maximum size of the output buffer. + * Compute the cutoff_max exponent (the exponent of the final digit to + * print). Default to the maximum size of the output buffer. */ - cutoffExponent = digitExponent - bufferSize; - if (cutoffNumber >= 0) { + cutoff_max_Exponent = digitExponent - bufferSize; + if (cutoff_max >= 0) { npy_int32 desiredCutoffExponent; if (cutoffMode == CutoffMode_TotalLength) { - desiredCutoffExponent = digitExponent - cutoffNumber; - if (desiredCutoffExponent > cutoffExponent) { - cutoffExponent = desiredCutoffExponent; + desiredCutoffExponent = digitExponent - cutoff_max; + if (desiredCutoffExponent > cutoff_max_Exponent) { + cutoff_max_Exponent = desiredCutoffExponent; } } - /* Otherwise it's CutoffMode_FractionLength. Print cutoffNumber digits + /* Otherwise it's CutoffMode_FractionLength. Print cutoff_max digits * past the decimal point or until we reach the buffer size */ else { - desiredCutoffExponent = -cutoffNumber; - if (desiredCutoffExponent > cutoffExponent) { - cutoffExponent = desiredCutoffExponent; + desiredCutoffExponent = -cutoff_max; + if (desiredCutoffExponent > cutoff_max_Exponent) { + cutoff_max_Exponent = desiredCutoffExponent; + } + } + } + /* Also compute the cutoff_min exponent. */ + cutoff_min_Exponent = digitExponent; + if (cutoff_min >= 0) { + npy_int32 desiredCutoffExponent; + + if (cutoffMode == CutoffMode_TotalLength) { + desiredCutoffExponent = digitExponent - cutoff_min; + if (desiredCutoffExponent < cutoff_min_Exponent) { + cutoff_min_Exponent = desiredCutoffExponent; + } + } + else { + desiredCutoffExponent = -cutoff_min; + if (desiredCutoffExponent < cutoff_min_Exponent) { + cutoff_min_Exponent = desiredCutoffExponent; } } } @@ -1429,14 +1452,17 @@ Dragon4(BigInt *bigints, const npy_int32 exponent, /* * stop looping if we are far enough away from our neighboring - * values or if we have reached the cutoff digit + * values (and we have printed at least the requested minimum + * digits) or if we have reached the cutoff digit */ cmp = BigInt_Compare(scaledValue, scaledMarginLow); low = isEven ? (cmp <= 0) : (cmp < 0); cmp = BigInt_Compare(scaledValueHigh, scale); high = isEven ? (cmp >= 0) : (cmp > 0); - if (low | high | (digitExponent == cutoffExponent)) + if (((low | high) & (digitExponent <= cutoff_min_Exponent)) | + (digitExponent == cutoff_max_Exponent)) { break; + } /* store the output digit */ *curDigit = (char)('0' + outputDigit); @@ -1468,7 +1494,7 @@ Dragon4(BigInt *bigints, const npy_int32 exponent, DEBUG_ASSERT(outputDigit < 10); if ((scaledValue->length == 0) | - (digitExponent == cutoffExponent)) { + (digitExponent == cutoff_max_Exponent)) { break; } @@ -1562,8 +1588,8 @@ Dragon4(BigInt *bigints, const npy_int32 exponent, /* Options struct for easy passing of Dragon4 options. * * scientific - boolean controlling whether scientific notation is used - * digit_mode - whether to use unique or fixed fracional output - * cutoff_mode - whether 'precision' refers to toal digits, or digits past + * digit_mode - whether to use unique or fixed fractional output + * cutoff_mode - whether 'precision' refers to all digits, or digits past * the decimal point. * precision - When negative, prints as many digits as needed for a unique * number. When positive specifies the maximum number of @@ -1586,6 +1612,7 @@ typedef struct Dragon4_Options { DigitMode digit_mode; CutoffMode cutoff_mode; npy_int32 precision; + npy_int32 min_digits; npy_bool sign; TrimMode trim_mode; npy_int32 digits_left; @@ -1614,11 +1641,12 @@ FormatPositional(char *buffer, npy_uint32 bufferSize, BigInt *mantissa, npy_int32 exponent, char signbit, npy_uint32 mantissaBit, npy_bool hasUnequalMargins, DigitMode digit_mode, CutoffMode cutoff_mode, npy_int32 precision, - TrimMode trim_mode, npy_int32 digits_left, - npy_int32 digits_right) + npy_int32 min_digits, TrimMode trim_mode, + npy_int32 digits_left, npy_int32 digits_right) { npy_int32 printExponent; npy_int32 numDigits, numWholeDigits=0, has_sign=0; + npy_int32 add_digits; npy_int32 maxPrintLen = (npy_int32)bufferSize - 1, pos = 0; @@ -1641,8 +1669,9 @@ FormatPositional(char *buffer, npy_uint32 bufferSize, BigInt *mantissa, } numDigits = Dragon4(mantissa, exponent, mantissaBit, hasUnequalMargins, - digit_mode, cutoff_mode, precision, buffer + has_sign, - maxPrintLen - has_sign, &printExponent); + digit_mode, cutoff_mode, precision, min_digits, + buffer + has_sign, maxPrintLen - has_sign, + &printExponent); DEBUG_ASSERT(numDigits > 0); DEBUG_ASSERT(numDigits <= bufferSize); @@ -1741,9 +1770,10 @@ FormatPositional(char *buffer, npy_uint32 bufferSize, BigInt *mantissa, buffer[pos++] = '.'; } - desiredFractionalDigits = precision; - if (cutoff_mode == CutoffMode_TotalLength && precision >= 0) { - desiredFractionalDigits = precision - numWholeDigits; + add_digits = digit_mode == DigitMode_Unique ? min_digits : precision; + desiredFractionalDigits = add_digits < 0 ? 0 : add_digits; + if (cutoff_mode == CutoffMode_TotalLength) { + desiredFractionalDigits = add_digits - numWholeDigits; } if (trim_mode == TrimMode_LeaveOneZero) { @@ -1754,10 +1784,9 @@ FormatPositional(char *buffer, npy_uint32 bufferSize, BigInt *mantissa, } } else if (trim_mode == TrimMode_None && - digit_mode != DigitMode_Unique && desiredFractionalDigits > numFractionDigits && pos < maxPrintLen) { - /* add trailing zeros up to precision length */ + /* add trailing zeros up to add_digits length */ /* compute the number of trailing zeros needed */ npy_int32 count = desiredFractionalDigits - numFractionDigits; if (pos + count > maxPrintLen) { @@ -1775,7 +1804,7 @@ FormatPositional(char *buffer, npy_uint32 bufferSize, BigInt *mantissa, * when rounding, we may still end up with trailing zeros. Remove them * depending on trim settings. */ - if (precision >= 0 && trim_mode != TrimMode_None && numFractionDigits > 0) { + if (trim_mode != TrimMode_None && numFractionDigits > 0) { while (buffer[pos-1] == '0') { pos--; numFractionDigits--; @@ -1849,7 +1878,7 @@ static npy_uint32 FormatScientific (char *buffer, npy_uint32 bufferSize, BigInt *mantissa, npy_int32 exponent, char signbit, npy_uint32 mantissaBit, npy_bool hasUnequalMargins, DigitMode digit_mode, - npy_int32 precision, TrimMode trim_mode, + npy_int32 precision, npy_int32 min_digits, TrimMode trim_mode, npy_int32 digits_left, npy_int32 exp_digits) { npy_int32 printExponent; @@ -1857,12 +1886,12 @@ FormatScientific (char *buffer, npy_uint32 bufferSize, BigInt *mantissa, char *pCurOut; npy_int32 numFractionDigits; npy_int32 leftchars; + npy_int32 add_digits; if (digit_mode != DigitMode_Unique) { DEBUG_ASSERT(precision >= 0); } - DEBUG_ASSERT(bufferSize > 0); pCurOut = buffer; @@ -1890,7 +1919,9 @@ FormatScientific (char *buffer, npy_uint32 bufferSize, BigInt *mantissa, } numDigits = Dragon4(mantissa, exponent, mantissaBit, hasUnequalMargins, - digit_mode, CutoffMode_TotalLength, precision + 1, + digit_mode, CutoffMode_TotalLength, + precision < 0 ? -1 : precision + 1, + min_digits < 0 ? -1 : min_digits + 1, pCurOut, bufferSize, &printExponent); DEBUG_ASSERT(numDigits > 0); @@ -1925,6 +1956,8 @@ FormatScientific (char *buffer, npy_uint32 bufferSize, BigInt *mantissa, --bufferSize; } + add_digits = digit_mode == DigitMode_Unique ? min_digits : precision; + add_digits = add_digits < 0 ? 0 : add_digits; if (trim_mode == TrimMode_LeaveOneZero) { /* if we didn't print any fractional digits, add the 0 */ if (numFractionDigits == 0 && bufferSize > 1) { @@ -1934,13 +1967,12 @@ FormatScientific (char *buffer, npy_uint32 bufferSize, BigInt *mantissa, ++numFractionDigits; } } - else if (trim_mode == TrimMode_None && - digit_mode != DigitMode_Unique) { - /* add trailing zeros up to precision length */ - if (precision > (npy_int32)numFractionDigits) { + else if (trim_mode == TrimMode_None) { + /* add trailing zeros up to add_digits length */ + if (add_digits > (npy_int32)numFractionDigits) { char *pEnd; /* compute the number of trailing zeros needed */ - npy_int32 numZeros = (precision - numFractionDigits); + npy_int32 numZeros = (add_digits - numFractionDigits); if (numZeros > (npy_int32)bufferSize - 1) { numZeros = (npy_int32)bufferSize - 1; @@ -1958,7 +1990,7 @@ FormatScientific (char *buffer, npy_uint32 bufferSize, BigInt *mantissa, * when rounding, we may still end up with trailing zeros. Remove them * depending on trim settings. */ - if (precision >= 0 && trim_mode != TrimMode_None && numFractionDigits > 0) { + if (trim_mode != TrimMode_None && numFractionDigits > 0) { --pCurOut; while (*pCurOut == '0') { --pCurOut; @@ -2150,14 +2182,14 @@ Format_floatbits(char *buffer, npy_uint32 bufferSize, BigInt *mantissa, return FormatScientific(buffer, bufferSize, mantissa, exponent, signbit, mantissaBit, hasUnequalMargins, opt->digit_mode, opt->precision, - opt->trim_mode, opt->digits_left, - opt->exp_digits); + opt->min_digits, opt->trim_mode, + opt->digits_left, opt->exp_digits); } else { return FormatPositional(buffer, bufferSize, mantissa, exponent, signbit, mantissaBit, hasUnequalMargins, opt->digit_mode, opt->cutoff_mode, - opt->precision, opt->trim_mode, + opt->precision, opt->min_digits, opt->trim_mode, opt->digits_left, opt->digits_right); } } @@ -2174,7 +2206,7 @@ Dragon4_PrintFloat_IEEE_binary16( Dragon4_Scratch *scratch, npy_half *value, Dragon4_Options *opt) { char *buffer = scratch->repr; - npy_uint32 bufferSize = sizeof(scratch->repr); + const npy_uint32 bufferSize = sizeof(scratch->repr); BigInt *bigints = scratch->bigints; npy_uint16 val = *value; @@ -2186,15 +2218,6 @@ Dragon4_PrintFloat_IEEE_binary16( npy_bool hasUnequalMargins; char signbit = '\0'; - if (bufferSize == 0) { - return 0; - } - - if (bufferSize == 1) { - buffer[0] = '\0'; - return 0; - } - /* deconstruct the floating point value */ floatMantissa = val & bitmask_u32(10); floatExponent = (val >> 10) & bitmask_u32(5); @@ -2271,7 +2294,7 @@ Dragon4_PrintFloat_IEEE_binary32( Dragon4_Options *opt) { char *buffer = scratch->repr; - npy_uint32 bufferSize = sizeof(scratch->repr); + const npy_uint32 bufferSize = sizeof(scratch->repr); BigInt *bigints = scratch->bigints; union @@ -2287,15 +2310,6 @@ Dragon4_PrintFloat_IEEE_binary32( npy_bool hasUnequalMargins; char signbit = '\0'; - if (bufferSize == 0) { - return 0; - } - - if (bufferSize == 1) { - buffer[0] = '\0'; - return 0; - } - /* deconstruct the floating point value */ floatUnion.floatingPoint = *value; floatMantissa = floatUnion.integer & bitmask_u32(23); @@ -2372,7 +2386,7 @@ Dragon4_PrintFloat_IEEE_binary64( Dragon4_Scratch *scratch, npy_float64 *value, Dragon4_Options *opt) { char *buffer = scratch->repr; - npy_uint32 bufferSize = sizeof(scratch->repr); + const npy_uint32 bufferSize = sizeof(scratch->repr); BigInt *bigints = scratch->bigints; union @@ -2389,14 +2403,6 @@ Dragon4_PrintFloat_IEEE_binary64( npy_bool hasUnequalMargins; char signbit = '\0'; - if (bufferSize == 0) { - return 0; - } - - if (bufferSize == 1) { - buffer[0] = '\0'; - return 0; - } /* deconstruct the floating point value */ floatUnion.floatingPoint = *value; @@ -2495,7 +2501,7 @@ Dragon4_PrintFloat_Intel_extended( Dragon4_Scratch *scratch, FloatVal128 value, Dragon4_Options *opt) { char *buffer = scratch->repr; - npy_uint32 bufferSize = sizeof(scratch->repr); + const npy_uint32 bufferSize = sizeof(scratch->repr); BigInt *bigints = scratch->bigints; npy_uint32 floatExponent, floatSign; @@ -2507,15 +2513,6 @@ Dragon4_PrintFloat_Intel_extended( npy_bool hasUnequalMargins; char signbit = '\0'; - if (bufferSize == 0) { - return 0; - } - - if (bufferSize == 1) { - buffer[0] = '\0'; - return 0; - } - /* deconstruct the floating point value (we ignore the intbit) */ floatMantissa = value.lo & bitmask_u64(63); floatExponent = value.hi & bitmask_u32(15); @@ -2698,7 +2695,7 @@ Dragon4_PrintFloat_Intel_extended128( } #endif /* HAVE_LDOUBLE_INTEL_EXTENDED_16_BYTES_LE */ -#if defined(HAVE_LDOUBLE_IEEE_QUAD_LE) +#if defined(HAVE_LDOUBLE_IEEE_QUAD_LE) || defined(HAVE_LDOUBLE_IEEE_QUAD_BE) /* * IEEE binary128 floating-point format * @@ -2707,20 +2704,16 @@ Dragon4_PrintFloat_Intel_extended128( * mantissa: 112 bits * * Currently binary128 format exists on only a few CPUs, such as on the POWER9 - * arch. Because of this, this code has not been tested. I am not sure if the - * arch also supports uint128, and C does not seem to support int128 literals. - * So we use uint64 to do manipulation. Unfortunately this means we are endian - * dependent. Assume little-endian for now, can fix later once binary128 - * becomes more common. + * arch or aarch64. Because of this, this code has not been extensively tested. + * I am not sure if the arch also supports uint128, and C does not seem to + * support int128 literals. So we use uint64 to do manipulation. */ static npy_uint32 Dragon4_PrintFloat_IEEE_binary128( - Dragon4_Scratch *scratch, npy_float128 *value, Dragon4_Options *opt) + Dragon4_Scratch *scratch, FloatVal128 val128, Dragon4_Options *opt) { - FloatUnion128 buf128; - char *buffer = scratch->repr; - npy_uint32 bufferSize = sizeof(scratch->repr); + const npy_uint32 bufferSize = sizeof(scratch->repr); BigInt *bigints = scratch->bigints; npy_uint32 floatExponent, floatSign; @@ -2731,22 +2724,10 @@ Dragon4_PrintFloat_IEEE_binary128( npy_bool hasUnequalMargins; char signbit = '\0'; - buf128.floatingPoint = *value; - - if (bufferSize == 0) { - return 0; - } - - if (bufferSize == 1) { - buffer[0] = '\0'; - return 0; - } - - /* Assumes little-endian !!! */ - mantissa_hi = buf128.integer.a & bitmask_u64(48); - mantissa_lo = buf128.integer.b; - floatExponent = (buf128.integer.a >> 48) & bitmask_u32(15); - floatSign = buf128.integer.a >> 63; + mantissa_hi = val128.hi & bitmask_u64(48); + mantissa_lo = val128.lo; + floatExponent = (val128.hi >> 48) & bitmask_u32(15); + floatSign = val128.hi >> 63; /* output the sign */ if (floatSign != 0) { @@ -2810,12 +2791,49 @@ Dragon4_PrintFloat_IEEE_binary128( return Format_floatbits(buffer, bufferSize, bigints, exponent, signbit, mantissaBit, hasUnequalMargins, opt); } + +#if defined(HAVE_LDOUBLE_IEEE_QUAD_LE) +static npy_uint32 +Dragon4_PrintFloat_IEEE_binary128_le( + Dragon4_Scratch *scratch, npy_float128 *value, Dragon4_Options *opt) +{ + FloatVal128 val128; + FloatUnion128 buf128; + + buf128.floatingPoint = *value; + val128.lo = buf128.integer.a; + val128.hi = buf128.integer.b; + + return Dragon4_PrintFloat_IEEE_binary128(scratch, val128, opt); +} #endif /* HAVE_LDOUBLE_IEEE_QUAD_LE */ +#if defined(HAVE_LDOUBLE_IEEE_QUAD_BE) +/* + * This function is untested, very few, if any, architectures implement + * big endian IEEE binary128 floating point. + */ +static npy_uint32 +Dragon4_PrintFloat_IEEE_binary128_be( + Dragon4_Scratch *scratch, npy_float128 *value, Dragon4_Options *opt) +{ + FloatVal128 val128; + FloatUnion128 buf128; + + buf128.floatingPoint = *value; + val128.lo = buf128.integer.b; + val128.hi = buf128.integer.a; + + return Dragon4_PrintFloat_IEEE_binary128(scratch, val128, opt); +} +#endif /* HAVE_LDOUBLE_IEEE_QUAD_BE */ + +#endif /* HAVE_LDOUBLE_IEEE_QUAD_LE | HAVE_LDOUBLE_IEEE_BE*/ + #if (defined(HAVE_LDOUBLE_IBM_DOUBLE_DOUBLE_LE) || \ defined(HAVE_LDOUBLE_IBM_DOUBLE_DOUBLE_BE)) /* - * IBM extended precision 128-bit floating-point format, aka IBM double-dobule + * IBM extended precision 128-bit floating-point format, aka IBM double-double * * IBM's double-double type is a pair of IEEE binary64 values, which you add * together to get a total value. The exponents are arranged so that the lower @@ -2852,12 +2870,15 @@ Dragon4_PrintFloat_IEEE_binary128( */ static npy_uint32 Dragon4_PrintFloat_IBM_double_double( - Dragon4_Scratch *scratch, FloatVal128 val128, Dragon4_Options *opt) + Dragon4_Scratch *scratch, npy_float128 *value, Dragon4_Options *opt) { char *buffer = scratch->repr; - npy_uint32 bufferSize = sizeof(scratch->repr); + const npy_uint32 bufferSize = sizeof(scratch->repr); BigInt *bigints = scratch->bigints; + FloatVal128 val128; + FloatUnion128 buf128; + npy_uint32 floatExponent1, floatExponent2; npy_uint64 floatMantissa1, floatMantissa2; npy_uint32 floatSign1, floatSign2; @@ -2869,14 +2890,11 @@ Dragon4_PrintFloat_IBM_double_double( npy_bool hasUnequalMargins; char signbit = '\0'; - if (bufferSize == 0) { - return 0; - } - - if (bufferSize == 1) { - buffer[0] = '\0'; - return 0; - } + /* The high part always comes before the low part, regardless of the + * endianness of the system. */ + buf128.floatingPoint = *value; + val128.hi = buf128.integer.a; + val128.lo = buf128.integer.b; /* deconstruct the floating point values */ floatMantissa1 = val128.hi & bitmask_u64(52); @@ -3022,39 +3040,6 @@ Dragon4_PrintFloat_IBM_double_double( signbit, mantissaBit, hasUnequalMargins, opt); } -#if defined(HAVE_LDOUBLE_IBM_DOUBLE_DOUBLE_LE) -static npy_uint32 -Dragon4_PrintFloat_IBM_double_double_le( - Dragon4_Scratch *scratch, npy_float128 *value, Dragon4_Options *opt) -{ - FloatVal128 val128; - FloatUnion128 buf128; - - buf128.floatingPoint = *value; - val128.lo = buf128.integer.a; - val128.hi = buf128.integer.b; - - return Dragon4_PrintFloat_IBM_double_double(scratch, val128, opt); -} -#endif /* HAVE_LDOUBLE_IBM_DOUBLE_DOUBLE_LE */ - -#if defined(HAVE_LDOUBLE_IBM_DOUBLE_DOUBLE_BE) -static npy_uint32 -Dragon4_PrintFloat_IBM_double_double_be( - Dragon4_Scratch *scratch, npy_float128 *value, Dragon4_Options *opt) -{ - FloatVal128 val128; - FloatUnion128 buf128; - - buf128.floatingPoint = *value; - val128.hi = buf128.integer.a; - val128.lo = buf128.integer.b; - - return Dragon4_PrintFloat_IBM_double_double(scratch, val128, opt); -} - -#endif /* HAVE_LDOUBLE_IBM_DOUBLE_DOUBLE_BE */ - #endif /* HAVE_LDOUBLE_IBM_DOUBLE_DOUBLE_LE | HAVE_LDOUBLE_IBM_DOUBLE_DOUBLE_BE */ #endif /* NPY_FLOAT128 */ @@ -3084,14 +3069,14 @@ Dragon4_Positional_##Type##_opt(npy_type *val, Dragon4_Options *opt)\ free_dragon4_bigint_scratch(scratch);\ return NULL;\ }\ - ret = PyUString_FromString(scratch->repr);\ + ret = PyUnicode_FromString(scratch->repr);\ free_dragon4_bigint_scratch(scratch);\ return ret;\ }\ \ PyObject *\ Dragon4_Positional_##Type(npy_type *val, DigitMode digit_mode,\ - CutoffMode cutoff_mode, int precision,\ + CutoffMode cutoff_mode, int precision, int min_digits, \ int sign, TrimMode trim, int pad_left, int pad_right)\ {\ Dragon4_Options opt;\ @@ -3100,6 +3085,7 @@ Dragon4_Positional_##Type(npy_type *val, DigitMode digit_mode,\ opt.digit_mode = digit_mode;\ opt.cutoff_mode = cutoff_mode;\ opt.precision = precision;\ + opt.min_digits = min_digits;\ opt.sign = sign;\ opt.trim_mode = trim;\ opt.digits_left = pad_left;\ @@ -3121,13 +3107,14 @@ Dragon4_Scientific_##Type##_opt(npy_type *val, Dragon4_Options *opt)\ free_dragon4_bigint_scratch(scratch);\ return NULL;\ }\ - ret = PyUString_FromString(scratch->repr);\ + ret = PyUnicode_FromString(scratch->repr);\ free_dragon4_bigint_scratch(scratch);\ return ret;\ }\ PyObject *\ Dragon4_Scientific_##Type(npy_type *val, DigitMode digit_mode, int precision,\ - int sign, TrimMode trim, int pad_left, int exp_digits)\ + int min_digits, int sign, TrimMode trim, int pad_left, \ + int exp_digits)\ {\ Dragon4_Options opt;\ \ @@ -3135,6 +3122,7 @@ Dragon4_Scientific_##Type(npy_type *val, DigitMode digit_mode, int precision,\ opt.digit_mode = digit_mode;\ opt.cutoff_mode = CutoffMode_TotalLength;\ opt.precision = precision;\ + opt.min_digits = min_digits;\ opt.sign = sign;\ opt.trim_mode = trim;\ opt.digits_left = pad_left;\ @@ -3157,8 +3145,8 @@ make_dragon4_typefuncs(LongDouble, npy_longdouble, NPY_LONGDOUBLE_BINFMT_NAME) PyObject * Dragon4_Positional(PyObject *obj, DigitMode digit_mode, CutoffMode cutoff_mode, - int precision, int sign, TrimMode trim, int pad_left, - int pad_right) + int precision, int min_digits, int sign, TrimMode trim, + int pad_left, int pad_right) { npy_double val; Dragon4_Options opt; @@ -3167,6 +3155,7 @@ Dragon4_Positional(PyObject *obj, DigitMode digit_mode, CutoffMode cutoff_mode, opt.digit_mode = digit_mode; opt.cutoff_mode = cutoff_mode; opt.precision = precision; + opt.min_digits = min_digits; opt.sign = sign; opt.trim_mode = trim; opt.digits_left = pad_left; @@ -3174,19 +3163,19 @@ Dragon4_Positional(PyObject *obj, DigitMode digit_mode, CutoffMode cutoff_mode, opt.exp_digits = -1; if (PyArray_IsScalar(obj, Half)) { - npy_half x = ((PyHalfScalarObject *)obj)->obval; + npy_half x = PyArrayScalar_VAL(obj, Half); return Dragon4_Positional_Half_opt(&x, &opt); } else if (PyArray_IsScalar(obj, Float)) { - npy_float x = ((PyFloatScalarObject *)obj)->obval; + npy_float x = PyArrayScalar_VAL(obj, Float); return Dragon4_Positional_Float_opt(&x, &opt); } else if (PyArray_IsScalar(obj, Double)) { - npy_double x = ((PyDoubleScalarObject *)obj)->obval; + npy_double x = PyArrayScalar_VAL(obj, Double); return Dragon4_Positional_Double_opt(&x, &opt); } else if (PyArray_IsScalar(obj, LongDouble)) { - npy_longdouble x = ((PyLongDoubleScalarObject *)obj)->obval; + npy_longdouble x = PyArrayScalar_VAL(obj, LongDouble); return Dragon4_Positional_LongDouble_opt(&x, &opt); } @@ -3199,7 +3188,8 @@ Dragon4_Positional(PyObject *obj, DigitMode digit_mode, CutoffMode cutoff_mode, PyObject * Dragon4_Scientific(PyObject *obj, DigitMode digit_mode, int precision, - int sign, TrimMode trim, int pad_left, int exp_digits) + int min_digits, int sign, TrimMode trim, int pad_left, + int exp_digits) { npy_double val; Dragon4_Options opt; @@ -3208,6 +3198,7 @@ Dragon4_Scientific(PyObject *obj, DigitMode digit_mode, int precision, opt.digit_mode = digit_mode; opt.cutoff_mode = CutoffMode_TotalLength; opt.precision = precision; + opt.min_digits = min_digits; opt.sign = sign; opt.trim_mode = trim; opt.digits_left = pad_left; @@ -3215,19 +3206,19 @@ Dragon4_Scientific(PyObject *obj, DigitMode digit_mode, int precision, opt.exp_digits = exp_digits; if (PyArray_IsScalar(obj, Half)) { - npy_half x = ((PyHalfScalarObject *)obj)->obval; + npy_half x = PyArrayScalar_VAL(obj, Half); return Dragon4_Scientific_Half_opt(&x, &opt); } else if (PyArray_IsScalar(obj, Float)) { - npy_float x = ((PyFloatScalarObject *)obj)->obval; + npy_float x = PyArrayScalar_VAL(obj, Float); return Dragon4_Scientific_Float_opt(&x, &opt); } else if (PyArray_IsScalar(obj, Double)) { - npy_double x = ((PyDoubleScalarObject *)obj)->obval; + npy_double x = PyArrayScalar_VAL(obj, Double); return Dragon4_Scientific_Double_opt(&x, &opt); } else if (PyArray_IsScalar(obj, LongDouble)) { - npy_longdouble x = ((PyLongDoubleScalarObject *)obj)->obval; + npy_longdouble x = PyArrayScalar_VAL(obj, LongDouble); return Dragon4_Scientific_LongDouble_opt(&x, &opt); } diff --git a/numpy/core/src/multiarray/dragon4.h b/numpy/core/src/multiarray/dragon4.h index 383a0949dd03..e3325bfa2ca8 100644 --- a/numpy/core/src/multiarray/dragon4.h +++ b/numpy/core/src/multiarray/dragon4.h @@ -1,38 +1,39 @@ /* * Copyright (c) 2014 Ryan Juckett - * http://www.ryanjuckett.com/ * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * - * 3. This notice may not be removed or altered from any source - * distribution. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. */ /* * This file contains a modified version of Ryan Juckett's Dragon4 - * implementation, which has been ported from C++ to C and which has + * implementation, obtained from http://www.ryanjuckett.com, + * which has been ported from C++ to C and which has * modifications specific to printing floats in numpy. + * + * Ryan Juckett's original code was under the Zlib license; he gave numpy + * permission to include it under the MIT license instead. */ +#ifndef NUMPY_CORE_SRC_MULTIARRAY_DRAGON4_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_DRAGON4_H_ -#ifndef _NPY_DRAGON4_H_ -#define _NPY_DRAGON4_H_ - -#include "Python.h" -#include "structmember.h" +#include <Python.h> +#include <structmember.h> #define NPY_NO_DEPRECATED_API NPY_API_VERSION #define _MULTIARRAYMODULE #include "numpy/arrayobject.h" @@ -75,10 +76,9 @@ #define NPY_LONGDOUBLE_BINFMT_NAME Intel_extended128 #elif defined(HAVE_LDOUBLE_MOTOROLA_EXTENDED_12_BYTES_BE) #define NPY_LONGDOUBLE_BINFMT_NAME Motorola_extended96 -#elif defined(HAVE_LDOUBLE_IBM_DOUBLE_DOUBLE_LE) - #define NPY_LONGDOUBLE_BINFMT_NAME IBM_double_double_le -#elif defined(HAVE_LDOUBLE_IBM_DOUBLE_DOUBLE_BE) - #define NPY_LONGDOUBLE_BINFMT_NAME IBM_double_double_be +#elif (defined(HAVE_LDOUBLE_IBM_DOUBLE_DOUBLE_LE) || \ + defined(HAVE_LDOUBLE_IBM_DOUBLE_DOUBLE_BE)) + #define NPY_LONGDOUBLE_BINFMT_NAME IBM_double_double #else #error No long double representation defined #endif @@ -111,12 +111,12 @@ typedef enum TrimMode PyObject *\ Dragon4_Positional_##Type(npy_type *val, DigitMode digit_mode,\ CutoffMode cutoff_mode, int precision,\ - int sign, TrimMode trim, int pad_left,\ - int pad_right);\ + int min_digits, int sign, TrimMode trim, \ + int pad_left, int pad_right);\ PyObject *\ Dragon4_Scientific_##Type(npy_type *val, DigitMode digit_mode,\ - int precision, int sign, TrimMode trim,\ - int pad_left, int exp_digits); + int precision, int min_digits, int sign, \ + TrimMode trim, int pad_left, int exp_digits); make_dragon4_typedecl(Half, npy_half) make_dragon4_typedecl(Float, npy_float) @@ -127,12 +127,12 @@ make_dragon4_typedecl(LongDouble, npy_longdouble) PyObject * Dragon4_Positional(PyObject *obj, DigitMode digit_mode, CutoffMode cutoff_mode, - int precision, int sign, TrimMode trim, int pad_left, - int pad_right); + int precision, int min_digits, int sign, TrimMode trim, + int pad_left, int pad_right); PyObject * Dragon4_Scientific(PyObject *obj, DigitMode digit_mode, int precision, - int sign, TrimMode trim, int pad_left, int exp_digits); - -#endif + int min_digits, int sign, TrimMode trim, int pad_left, + int exp_digits); +#endif /* NUMPY_CORE_SRC_MULTIARRAY_DRAGON4_H_ */ diff --git a/numpy/core/src/multiarray/dtype_transfer.c b/numpy/core/src/multiarray/dtype_transfer.c index 2cb1e0a95b19..8fb44c4f6c5b 100644 --- a/numpy/core/src/multiarray/dtype_transfer.c +++ b/numpy/core/src/multiarray/dtype_transfer.c @@ -7,18 +7,18 @@ * The University of British Columbia * * See LICENSE.txt for the license. - + * */ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE #define PY_SSIZE_T_CLEAN -#include "Python.h" -#include "structmember.h" +#include <Python.h> +#include <structmember.h> -#define NPY_NO_DEPRECATED_API NPY_API_VERSION -#define _MULTIARRAYMODULE -#include <numpy/arrayobject.h> -#include <numpy/npy_cpu.h> +#include "numpy/arrayobject.h" +#include "lowlevel_strided_loops.h" #include "npy_pycompat.h" #include "convert_datatype.h" @@ -26,10 +26,14 @@ #include "_datetime.h" #include "datetime_strings.h" #include "descriptor.h" +#include "array_assign.h" #include "shape.h" -#include "lowlevel_strided_loops.h" +#include "dtype_transfer.h" #include "alloc.h" +#include "dtypemeta.h" +#include "array_method.h" +#include "array_coercion.h" #define NPY_LOWLEVEL_BUFFER_BLOCKSIZE 128 @@ -51,88 +55,87 @@ #endif /**********************************************/ +#if NPY_DT_DBG_TRACING /* - * Returns a transfer function which DECREFs any references in src_type. - * - * Returns NPY_SUCCEED or NPY_FAIL. + * Thin wrapper around print that ignores exceptions */ -static int -get_decsrcref_transfer_function(int aligned, - npy_intp src_stride, - PyArray_Descr *src_dtype, - PyArray_StridedUnaryOp **out_stransfer, - NpyAuxData **out_transferdata, - int *out_needs_api); +static void +_safe_print(PyObject *obj) +{ + if (PyObject_Print(obj, stdout, 0) < 0) { + PyErr_Clear(); + printf("<error during print>"); + } +} +#endif /* - * Returns a transfer function which zeros out the dest values. + * Returns a transfer function which DECREFs any references in src_type. * * Returns NPY_SUCCEED or NPY_FAIL. */ static int -get_setdstzero_transfer_function(int aligned, - npy_intp dst_stride, - PyArray_Descr *dst_dtype, - PyArray_StridedUnaryOp **out_stransfer, - NpyAuxData **out_transferdata, +get_decref_transfer_function(int aligned, + npy_intp src_stride, + PyArray_Descr *src_dtype, + NPY_cast_info *cast_info, int *out_needs_api); -/* - * Returns a transfer function which sets a boolean type to ones. - * - * Returns NPY_SUCCEED or NPY_FAIL. - */ -NPY_NO_EXPORT int -get_bool_setdstone_transfer_function(npy_intp dst_stride, - PyArray_StridedUnaryOp **out_stransfer, - NpyAuxData **out_transferdata, - int *NPY_UNUSED(out_needs_api)); /*************************** COPY REFERENCES *******************************/ /* Moves references from src to dst */ -static void -_strided_to_strided_move_references(char *dst, npy_intp dst_stride, - char *src, npy_intp src_stride, - npy_intp N, npy_intp src_itemsize, - NpyAuxData *data) +NPY_NO_EXPORT int +_strided_to_strided_move_references( + PyArrayMethod_Context *NPY_UNUSED(context), char *const *args, + const npy_intp *dimensions, const npy_intp *strides, + NpyAuxData *NPY_UNUSED(auxdata)) { + npy_intp N = dimensions[0]; + char *src = args[0], *dst = args[1]; + npy_intp src_stride = strides[0], dst_stride = strides[1]; + PyObject *src_ref = NULL, *dst_ref = NULL; while (N > 0) { - NPY_COPY_PYOBJECT_PTR(&src_ref, src); - NPY_COPY_PYOBJECT_PTR(&dst_ref, dst); + memcpy(&src_ref, src, sizeof(src_ref)); + memcpy(&dst_ref, dst, sizeof(dst_ref)); /* Release the reference in dst */ NPY_DT_DBG_REFTRACE("dec dst ref", dst_ref); Py_XDECREF(dst_ref); /* Move the reference */ NPY_DT_DBG_REFTRACE("move src ref", src_ref); - NPY_COPY_PYOBJECT_PTR(dst, &src_ref); + memcpy(dst, &src_ref, sizeof(src_ref)); /* Set the source reference to NULL */ src_ref = NULL; - NPY_COPY_PYOBJECT_PTR(src, &src_ref); + memcpy(src, &src_ref, sizeof(src_ref)); src += src_stride; dst += dst_stride; --N; } + return 0; } /* Copies references from src to dst */ -static void -_strided_to_strided_copy_references(char *dst, npy_intp dst_stride, - char *src, npy_intp src_stride, - npy_intp N, npy_intp src_itemsize, - NpyAuxData *data) +NPY_NO_EXPORT int +_strided_to_strided_copy_references( + PyArrayMethod_Context *NPY_UNUSED(context), char *const *args, + const npy_intp *dimensions, const npy_intp *strides, + NpyAuxData *NPY_UNUSED(auxdata)) { + npy_intp N = dimensions[0]; + char *src = args[0], *dst = args[1]; + npy_intp src_stride = strides[0], dst_stride = strides[1]; + PyObject *src_ref = NULL, *dst_ref = NULL; while (N > 0) { - NPY_COPY_PYOBJECT_PTR(&src_ref, src); - NPY_COPY_PYOBJECT_PTR(&dst_ref, dst); + memcpy(&src_ref, src, sizeof(src_ref)); + memcpy(&dst_ref, dst, sizeof(dst_ref)); /* Copy the reference */ NPY_DT_DBG_REFTRACE("copy src ref", src_ref); - NPY_COPY_PYOBJECT_PTR(dst, &src_ref); + memcpy(dst, &src_ref, sizeof(src_ref)); /* Claim the reference */ Py_XINCREF(src_ref); /* Release the reference in dst */ @@ -143,44 +146,245 @@ _strided_to_strided_copy_references(char *dst, npy_intp dst_stride, dst += dst_stride; --N; } + return 0; } +/************************** ANY TO OBJECT *********************************/ + +typedef struct { + NpyAuxData base; + PyArray_GetItemFunc *getitem; + PyArrayObject_fields arr_fields; + NPY_cast_info decref_src; +} _any_to_object_auxdata; + + +static void +_any_to_object_auxdata_free(NpyAuxData *auxdata) +{ + _any_to_object_auxdata *data = (_any_to_object_auxdata *)auxdata; + + Py_DECREF(data->arr_fields.descr); + NPY_cast_info_xfree(&data->decref_src); + PyMem_Free(data); +} + + +static NpyAuxData * +_any_to_object_auxdata_clone(NpyAuxData *auxdata) +{ + _any_to_object_auxdata *data = (_any_to_object_auxdata *)auxdata; + + _any_to_object_auxdata *res = PyMem_Malloc(sizeof(_any_to_object_auxdata)); + + res->base = data->base; + res->getitem = data->getitem; + res->arr_fields = data->arr_fields; + Py_INCREF(res->arr_fields.descr); + + if (data->decref_src.func != NULL) { + if (NPY_cast_info_copy(&res->decref_src, &data->decref_src) < 0) { + NPY_AUXDATA_FREE((NpyAuxData *)res); + return NULL; + } + } + else { + res->decref_src.func = NULL; + } + return (NpyAuxData *)res; +} -/************************** ZERO-PADDED COPY ******************************/ -/* Does a zero-padded copy */ +static int +_strided_to_strided_any_to_object( + PyArrayMethod_Context *NPY_UNUSED(context), char *const *args, + const npy_intp *dimensions, const npy_intp *strides, + NpyAuxData *auxdata) +{ + npy_intp N = dimensions[0]; + char *src = args[0], *dst = args[1]; + npy_intp src_stride = strides[0], dst_stride = strides[1]; + + _any_to_object_auxdata *data = (_any_to_object_auxdata *)auxdata; + + PyObject *dst_ref = NULL; + char *orig_src = src; + while (N > 0) { + memcpy(&dst_ref, dst, sizeof(dst_ref)); + Py_XDECREF(dst_ref); + dst_ref = data->getitem(src, &data->arr_fields); + memcpy(dst, &dst_ref, sizeof(PyObject *)); + + if (dst_ref == NULL) { + return -1; + } + src += src_stride; + dst += dst_stride; + --N; + } + if (data->decref_src.func != NULL) { + /* If necessary, clear the input buffer (`move_references`) */ + if (data->decref_src.func(&data->decref_src.context, + &orig_src, &N, &src_stride, data->decref_src.auxdata) < 0) { + return -1; + } + } + return 0; +} + + +NPY_NO_EXPORT int +any_to_object_get_loop( + PyArrayMethod_Context *context, + int aligned, int move_references, + npy_intp *strides, + PyArrayMethod_StridedLoop **out_loop, + NpyAuxData **out_transferdata, + NPY_ARRAYMETHOD_FLAGS *flags) +{ + + *flags = NPY_METH_REQUIRES_PYAPI; /* No need for floating point errors */ + + *out_loop = _strided_to_strided_any_to_object; + *out_transferdata = PyMem_Malloc(sizeof(_any_to_object_auxdata)); + if (*out_transferdata == NULL) { + return -1; + } + _any_to_object_auxdata *data = (_any_to_object_auxdata *)*out_transferdata; + data->base.free = &_any_to_object_auxdata_free; + data->base.clone = &_any_to_object_auxdata_clone; + data->arr_fields.base = NULL; + data->arr_fields.descr = context->descriptors[0]; + Py_INCREF(data->arr_fields.descr); + data->arr_fields.flags = aligned ? NPY_ARRAY_ALIGNED : 0; + data->arr_fields.nd = 0; + + data->getitem = context->descriptors[0]->f->getitem; + NPY_cast_info_init(&data->decref_src); + + if (move_references && PyDataType_REFCHK(context->descriptors[0])) { + int needs_api; + if (get_decref_transfer_function( + aligned, strides[0], context->descriptors[0], + &data->decref_src, + &needs_api) == NPY_FAIL) { + NPY_AUXDATA_FREE(*out_transferdata); + *out_transferdata = NULL; + return -1; + } + } + return 0; +} + + +/************************** OBJECT TO ANY *********************************/ + typedef struct { NpyAuxData base; - npy_intp dst_itemsize; -} _strided_zero_pad_data; + PyArray_Descr *descr; + int move_references; +} _object_to_any_auxdata; -/* zero-padded data copy function */ -static NpyAuxData *_strided_zero_pad_data_clone(NpyAuxData *data) + +static void +_object_to_any_auxdata_free(NpyAuxData *auxdata) { - _strided_zero_pad_data *newdata = - (_strided_zero_pad_data *)PyArray_malloc( - sizeof(_strided_zero_pad_data)); - if (newdata == NULL) { + _object_to_any_auxdata *data = (_object_to_any_auxdata *)auxdata; + Py_DECREF(data->descr); + PyMem_Free(data); +} + +static NpyAuxData * +_object_to_any_auxdata_clone(NpyAuxData *data) +{ + _object_to_any_auxdata *res = PyMem_Malloc(sizeof(*res)); + if (res == NULL) { return NULL; } + memcpy(res, data, sizeof(*res)); + Py_INCREF(res->descr); + return (NpyAuxData *)res; +} - memcpy(newdata, data, sizeof(_strided_zero_pad_data)); - return (NpyAuxData *)newdata; +static int +strided_to_strided_object_to_any( + PyArrayMethod_Context *NPY_UNUSED(context), char *const *args, + const npy_intp *dimensions, const npy_intp *strides, + NpyAuxData *auxdata) +{ + npy_intp N = dimensions[0]; + char *src = args[0], *dst = args[1]; + npy_intp src_stride = strides[0], dst_stride = strides[1]; + _object_to_any_auxdata *data = (_object_to_any_auxdata *)auxdata; + + PyObject *src_ref; + + while (N > 0) { + memcpy(&src_ref, src, sizeof(src_ref)); + if (PyArray_Pack(data->descr, dst, src_ref ? src_ref : Py_None) < 0) { + return -1; + } + + if (data->move_references && src_ref != NULL) { + Py_DECREF(src_ref); + memset(src, 0, sizeof(src_ref)); + } + + N--; + dst += dst_stride; + src += src_stride; + } + return 0; +} + + +NPY_NO_EXPORT int +object_to_any_get_loop( + PyArrayMethod_Context *context, + int NPY_UNUSED(aligned), int move_references, + npy_intp *NPY_UNUSED(strides), + PyArrayMethod_StridedLoop **out_loop, + NpyAuxData **out_transferdata, + NPY_ARRAYMETHOD_FLAGS *flags) +{ + *flags = NPY_METH_REQUIRES_PYAPI; + + /* NOTE: auxdata is only really necessary to flag `move_references` */ + _object_to_any_auxdata *data = PyMem_Malloc(sizeof(*data)); + if (data == NULL) { + return -1; + } + data->base.free = &_object_to_any_auxdata_free; + data->base.clone = &_object_to_any_auxdata_clone; + + Py_INCREF(context->descriptors[1]); + data->descr = context->descriptors[1]; + data->move_references = move_references; + *out_transferdata = (NpyAuxData *)data; + *out_loop = &strided_to_strided_object_to_any; + return 0; } + +/************************** ZERO-PADDED COPY ******************************/ + /* * Does a strided to strided zero-padded copy for the case where * dst_itemsize > src_itemsize */ -static void -_strided_to_strided_zero_pad_copy(char *dst, npy_intp dst_stride, - char *src, npy_intp src_stride, - npy_intp N, npy_intp src_itemsize, - NpyAuxData *data) +static int +_strided_to_strided_zero_pad_copy( + PyArrayMethod_Context *context, char *const *args, + const npy_intp *dimensions, const npy_intp *strides, + NpyAuxData *NPY_UNUSED(auxdata)) { - _strided_zero_pad_data *d = (_strided_zero_pad_data *)data; - npy_intp dst_itemsize = d->dst_itemsize; + npy_intp N = dimensions[0]; + char *src = args[0], *dst = args[1]; + npy_intp src_stride = strides[0], dst_stride = strides[1]; + npy_intp src_itemsize = context->descriptors[0]->elsize; + npy_intp dst_itemsize = context->descriptors[1]->elsize; + npy_intp zero_size = dst_itemsize-src_itemsize; while (N > 0) { @@ -190,20 +394,23 @@ _strided_to_strided_zero_pad_copy(char *dst, npy_intp dst_stride, dst += dst_stride; --N; } + return 0; } /* * Does a strided to strided zero-padded copy for the case where * dst_itemsize < src_itemsize */ -static void -_strided_to_strided_truncate_copy(char *dst, npy_intp dst_stride, - char *src, npy_intp src_stride, - npy_intp N, npy_intp src_itemsize, - NpyAuxData *data) +static int +_strided_to_strided_truncate_copy( + PyArrayMethod_Context *context, char *const *args, + const npy_intp *dimensions, const npy_intp *strides, + NpyAuxData *NPY_UNUSED(auxdata)) { - _strided_zero_pad_data *d = (_strided_zero_pad_data *)data; - npy_intp dst_itemsize = d->dst_itemsize; + npy_intp N = dimensions[0]; + char *src = args[0], *dst = args[1]; + npy_intp src_stride = strides[0], dst_stride = strides[1]; + npy_intp dst_itemsize = context->descriptors[1]->elsize; while (N > 0) { memcpy(dst, src, dst_itemsize); @@ -211,20 +418,25 @@ _strided_to_strided_truncate_copy(char *dst, npy_intp dst_stride, dst += dst_stride; --N; } + return 0; } /* * Does a strided to strided zero-padded or truncated copy for the case where * unicode swapping is needed. */ -static void -_strided_to_strided_unicode_copyswap(char *dst, npy_intp dst_stride, - char *src, npy_intp src_stride, - npy_intp N, npy_intp src_itemsize, - NpyAuxData *data) +static int +_strided_to_strided_unicode_copyswap( + PyArrayMethod_Context *context, char *const *args, + const npy_intp *dimensions, const npy_intp *strides, + NpyAuxData *NPY_UNUSED(auxdata)) { - _strided_zero_pad_data *d = (_strided_zero_pad_data *)data; - npy_intp dst_itemsize = d->dst_itemsize; + npy_intp N = dimensions[0]; + char *src = args[0], *dst = args[1]; + npy_intp src_stride = strides[0], dst_stride = strides[1]; + npy_intp src_itemsize = context->descriptors[0]->elsize; + npy_intp dst_itemsize = context->descriptors[1]->elsize; + npy_intp zero_size = dst_itemsize - src_itemsize; npy_intp copy_size = zero_size > 0 ? src_itemsize : dst_itemsize; char *_dst; @@ -245,6 +457,7 @@ _strided_to_strided_unicode_copyswap(char *dst, npy_intp dst_stride, dst += dst_stride; --N; } + return 0; } @@ -252,26 +465,16 @@ NPY_NO_EXPORT int PyArray_GetStridedZeroPadCopyFn(int aligned, int unicode_swap, npy_intp src_stride, npy_intp dst_stride, npy_intp src_itemsize, npy_intp dst_itemsize, - PyArray_StridedUnaryOp **out_stransfer, + PyArrayMethod_StridedLoop **out_stransfer, NpyAuxData **out_transferdata) { + *out_transferdata = NULL; if ((src_itemsize == dst_itemsize) && !unicode_swap) { *out_stransfer = PyArray_GetStridedCopyFn(aligned, src_stride, dst_stride, src_itemsize); - *out_transferdata = NULL; return (*out_stransfer == NULL) ? NPY_FAIL : NPY_SUCCEED; } else { - _strided_zero_pad_data *d = PyArray_malloc( - sizeof(_strided_zero_pad_data)); - if (d == NULL) { - PyErr_NoMemory(); - return NPY_FAIL; - } - d->dst_itemsize = dst_itemsize; - d->base.free = (NpyAuxData_FreeFunc *)&PyArray_free; - d->base.clone = &_strided_zero_pad_data_clone; - if (unicode_swap) { *out_stransfer = &_strided_to_strided_unicode_copyswap; } @@ -281,241 +484,10 @@ PyArray_GetStridedZeroPadCopyFn(int aligned, int unicode_swap, else { *out_stransfer = &_strided_to_strided_truncate_copy; } - - *out_transferdata = (NpyAuxData *)d; return NPY_SUCCEED; } } -/***************** WRAP ALIGNED CONTIGUOUS TRANSFER FUNCTION **************/ - -/* Wraps a transfer function + data in alignment code */ -typedef struct { - NpyAuxData base; - PyArray_StridedUnaryOp *wrapped, - *tobuffer, *frombuffer; - NpyAuxData *wrappeddata, *todata, *fromdata; - npy_intp src_itemsize, dst_itemsize; - char *bufferin, *bufferout; -} _align_wrap_data; - -/* transfer data free function */ -static void _align_wrap_data_free(NpyAuxData *data) -{ - _align_wrap_data *d = (_align_wrap_data *)data; - NPY_AUXDATA_FREE(d->wrappeddata); - NPY_AUXDATA_FREE(d->todata); - NPY_AUXDATA_FREE(d->fromdata); - PyArray_free(data); -} - -/* transfer data copy function */ -static NpyAuxData *_align_wrap_data_clone(NpyAuxData *data) -{ - _align_wrap_data *d = (_align_wrap_data *)data; - _align_wrap_data *newdata; - npy_intp basedatasize, datasize; - - /* Round up the structure size to 16-byte boundary */ - basedatasize = (sizeof(_align_wrap_data)+15)&(-0x10); - /* Add space for two low level buffers */ - datasize = basedatasize + - NPY_LOWLEVEL_BUFFER_BLOCKSIZE*d->src_itemsize + - NPY_LOWLEVEL_BUFFER_BLOCKSIZE*d->dst_itemsize; - - /* Allocate the data, and populate it */ - newdata = (_align_wrap_data *)PyArray_malloc(datasize); - if (newdata == NULL) { - return NULL; - } - memcpy(newdata, data, basedatasize); - newdata->bufferin = (char *)newdata + basedatasize; - newdata->bufferout = newdata->bufferin + - NPY_LOWLEVEL_BUFFER_BLOCKSIZE*newdata->src_itemsize; - if (newdata->wrappeddata != NULL) { - newdata->wrappeddata = NPY_AUXDATA_CLONE(d->wrappeddata); - if (newdata->wrappeddata == NULL) { - PyArray_free(newdata); - return NULL; - } - } - if (newdata->todata != NULL) { - newdata->todata = NPY_AUXDATA_CLONE(d->todata); - if (newdata->todata == NULL) { - NPY_AUXDATA_FREE(newdata->wrappeddata); - PyArray_free(newdata); - return NULL; - } - } - if (newdata->fromdata != NULL) { - newdata->fromdata = NPY_AUXDATA_CLONE(d->fromdata); - if (newdata->fromdata == NULL) { - NPY_AUXDATA_FREE(newdata->wrappeddata); - NPY_AUXDATA_FREE(newdata->todata); - PyArray_free(newdata); - return NULL; - } - } - - return (NpyAuxData *)newdata; -} - -static void -_strided_to_strided_contig_align_wrap(char *dst, npy_intp dst_stride, - char *src, npy_intp src_stride, - npy_intp N, npy_intp src_itemsize, - NpyAuxData *data) -{ - _align_wrap_data *d = (_align_wrap_data *)data; - PyArray_StridedUnaryOp *wrapped = d->wrapped, - *tobuffer = d->tobuffer, - *frombuffer = d->frombuffer; - npy_intp inner_src_itemsize = d->src_itemsize, - dst_itemsize = d->dst_itemsize; - NpyAuxData *wrappeddata = d->wrappeddata, - *todata = d->todata, - *fromdata = d->fromdata; - char *bufferin = d->bufferin, *bufferout = d->bufferout; - - for(;;) { - if (N > NPY_LOWLEVEL_BUFFER_BLOCKSIZE) { - tobuffer(bufferin, inner_src_itemsize, src, src_stride, - NPY_LOWLEVEL_BUFFER_BLOCKSIZE, - src_itemsize, todata); - wrapped(bufferout, dst_itemsize, bufferin, inner_src_itemsize, - NPY_LOWLEVEL_BUFFER_BLOCKSIZE, - inner_src_itemsize, wrappeddata); - frombuffer(dst, dst_stride, bufferout, dst_itemsize, - NPY_LOWLEVEL_BUFFER_BLOCKSIZE, - dst_itemsize, fromdata); - N -= NPY_LOWLEVEL_BUFFER_BLOCKSIZE; - src += NPY_LOWLEVEL_BUFFER_BLOCKSIZE*src_stride; - dst += NPY_LOWLEVEL_BUFFER_BLOCKSIZE*dst_stride; - } - else { - tobuffer(bufferin, inner_src_itemsize, src, src_stride, N, - src_itemsize, todata); - wrapped(bufferout, dst_itemsize, bufferin, inner_src_itemsize, N, - inner_src_itemsize, wrappeddata); - frombuffer(dst, dst_stride, bufferout, dst_itemsize, N, - dst_itemsize, fromdata); - return; - } - } -} - -static void -_strided_to_strided_contig_align_wrap_init_dest(char *dst, npy_intp dst_stride, - char *src, npy_intp src_stride, - npy_intp N, npy_intp src_itemsize, - NpyAuxData *data) -{ - _align_wrap_data *d = (_align_wrap_data *)data; - PyArray_StridedUnaryOp *wrapped = d->wrapped, - *tobuffer = d->tobuffer, - *frombuffer = d->frombuffer; - npy_intp inner_src_itemsize = d->src_itemsize, - dst_itemsize = d->dst_itemsize; - NpyAuxData *wrappeddata = d->wrappeddata, - *todata = d->todata, - *fromdata = d->fromdata; - char *bufferin = d->bufferin, *bufferout = d->bufferout; - - for(;;) { - if (N > NPY_LOWLEVEL_BUFFER_BLOCKSIZE) { - tobuffer(bufferin, inner_src_itemsize, src, src_stride, - NPY_LOWLEVEL_BUFFER_BLOCKSIZE, - src_itemsize, todata); - memset(bufferout, 0, dst_itemsize*NPY_LOWLEVEL_BUFFER_BLOCKSIZE); - wrapped(bufferout, dst_itemsize, bufferin, inner_src_itemsize, - NPY_LOWLEVEL_BUFFER_BLOCKSIZE, - inner_src_itemsize, wrappeddata); - frombuffer(dst, dst_stride, bufferout, dst_itemsize, - NPY_LOWLEVEL_BUFFER_BLOCKSIZE, - dst_itemsize, fromdata); - N -= NPY_LOWLEVEL_BUFFER_BLOCKSIZE; - src += NPY_LOWLEVEL_BUFFER_BLOCKSIZE*src_stride; - dst += NPY_LOWLEVEL_BUFFER_BLOCKSIZE*dst_stride; - } - else { - tobuffer(bufferin, inner_src_itemsize, src, src_stride, N, - src_itemsize, todata); - memset(bufferout, 0, dst_itemsize*N); - wrapped(bufferout, dst_itemsize, bufferin, inner_src_itemsize, N, - inner_src_itemsize, wrappeddata); - frombuffer(dst, dst_stride, bufferout, dst_itemsize, N, - dst_itemsize, fromdata); - return; - } - } -} - -/* - * Wraps an aligned contig to contig transfer function between either - * copies or byte swaps to temporary buffers. - * - * src_itemsize/dst_itemsize - The sizes of the src and dst datatypes. - * tobuffer - copy/swap function from src to an aligned contiguous buffer. - * todata - data for tobuffer - * frombuffer - copy/swap function from an aligned contiguous buffer to dst. - * fromdata - data for frombuffer - * wrapped - contig to contig transfer function being wrapped - * wrappeddata - data for wrapped - * init_dest - 1 means to memset the dest buffer to 0 before calling wrapped. - * - * Returns NPY_SUCCEED or NPY_FAIL. - */ -NPY_NO_EXPORT int -wrap_aligned_contig_transfer_function( - npy_intp src_itemsize, npy_intp dst_itemsize, - PyArray_StridedUnaryOp *tobuffer, NpyAuxData *todata, - PyArray_StridedUnaryOp *frombuffer, NpyAuxData *fromdata, - PyArray_StridedUnaryOp *wrapped, NpyAuxData *wrappeddata, - int init_dest, - PyArray_StridedUnaryOp **out_stransfer, - NpyAuxData **out_transferdata) -{ - _align_wrap_data *data; - npy_intp basedatasize, datasize; - - /* Round up the structure size to 16-byte boundary */ - basedatasize = (sizeof(_align_wrap_data)+15)&(-0x10); - /* Add space for two low level buffers */ - datasize = basedatasize + - NPY_LOWLEVEL_BUFFER_BLOCKSIZE*src_itemsize + - NPY_LOWLEVEL_BUFFER_BLOCKSIZE*dst_itemsize; - - /* Allocate the data, and populate it */ - data = (_align_wrap_data *)PyArray_malloc(datasize); - if (data == NULL) { - PyErr_NoMemory(); - return NPY_FAIL; - } - data->base.free = &_align_wrap_data_free; - data->base.clone = &_align_wrap_data_clone; - data->tobuffer = tobuffer; - data->todata = todata; - data->frombuffer = frombuffer; - data->fromdata = fromdata; - data->wrapped = wrapped; - data->wrappeddata = wrappeddata; - data->src_itemsize = src_itemsize; - data->dst_itemsize = dst_itemsize; - data->bufferin = (char *)data + basedatasize; - data->bufferout = data->bufferin + - NPY_LOWLEVEL_BUFFER_BLOCKSIZE*src_itemsize; - - /* Set the function and data */ - if (init_dest) { - *out_stransfer = &_strided_to_strided_contig_align_wrap_init_dest; - } - else { - *out_stransfer = &_strided_to_strided_contig_align_wrap; - } - *out_transferdata = (NpyAuxData *)data; - - return NPY_SUCCEED; -} /*************************** WRAP DTYPE COPY/SWAP *************************/ /* Wraps the dtype copy swap function */ @@ -531,14 +503,14 @@ static void _wrap_copy_swap_data_free(NpyAuxData *data) { _wrap_copy_swap_data *d = (_wrap_copy_swap_data *)data; Py_DECREF(d->arr); - PyArray_free(data); + PyMem_Free(data); } /* wrap copy swap data copy function */ static NpyAuxData *_wrap_copy_swap_data_clone(NpyAuxData *data) { _wrap_copy_swap_data *newdata = - (_wrap_copy_swap_data *)PyArray_malloc(sizeof(_wrap_copy_swap_data)); + (_wrap_copy_swap_data *)PyMem_Malloc(sizeof(_wrap_copy_swap_data)); if (newdata == NULL) { return NULL; } @@ -549,31 +521,36 @@ static NpyAuxData *_wrap_copy_swap_data_clone(NpyAuxData *data) return (NpyAuxData *)newdata; } -static void -_strided_to_strided_wrap_copy_swap(char *dst, npy_intp dst_stride, - char *src, npy_intp src_stride, - npy_intp N, npy_intp NPY_UNUSED(src_itemsize), - NpyAuxData *data) +static int +_strided_to_strided_wrap_copy_swap( + PyArrayMethod_Context *NPY_UNUSED(context), char *const *args, + const npy_intp *dimensions, const npy_intp *strides, + NpyAuxData *auxdata) { - _wrap_copy_swap_data *d = (_wrap_copy_swap_data *)data; + npy_intp N = dimensions[0]; + char *src = args[0], *dst = args[1]; + npy_intp src_stride = strides[0], dst_stride = strides[1]; + _wrap_copy_swap_data *d = (_wrap_copy_swap_data *)auxdata; + + /* We assume that d->copyswapn should not be able to error. */ d->copyswapn(dst, dst_stride, src, src_stride, N, d->swap, d->arr); + return 0; } -/* This only gets used for custom data types and for Unicode when swapping */ +/* + * This function is used only via `get_wrapped_legacy_cast_function` + * when we wrap a legacy DType (or explicitly fall back to the legacy + * wrapping) for an internal cast. + */ static int -wrap_copy_swap_function(int aligned, - npy_intp src_stride, npy_intp dst_stride, - PyArray_Descr *dtype, - int should_swap, - PyArray_StridedUnaryOp **out_stransfer, - NpyAuxData **out_transferdata) +wrap_copy_swap_function( + PyArray_Descr *dtype, int should_swap, + PyArrayMethod_StridedLoop **out_stransfer, + NpyAuxData **out_transferdata) { - _wrap_copy_swap_data *data; - npy_intp shape = 1; - /* Allocate the data for the copy swap */ - data = (_wrap_copy_swap_data *)PyArray_malloc(sizeof(_wrap_copy_swap_data)); + _wrap_copy_swap_data *data = PyMem_Malloc(sizeof(_wrap_copy_swap_data)); if (data == NULL) { PyErr_NoMemory(); *out_stransfer = NULL; @@ -591,13 +568,14 @@ wrap_copy_swap_function(int aligned, * The copyswap functions shouldn't need that. */ Py_INCREF(dtype); + npy_intp shape = 1; data->arr = (PyArrayObject *)PyArray_NewFromDescr_int( &PyArray_Type, dtype, 1, &shape, NULL, NULL, 0, NULL, NULL, 0, 1); if (data->arr == NULL) { - PyArray_free(data); + PyMem_Free(data); return NPY_FAIL; } @@ -614,6 +592,7 @@ typedef struct { NpyAuxData base; PyArray_VectorUnaryFunc *castfunc; PyArrayObject *aip, *aop; + npy_bool needs_api; } _strided_cast_data; /* strided cast data free function */ @@ -622,14 +601,14 @@ static void _strided_cast_data_free(NpyAuxData *data) _strided_cast_data *d = (_strided_cast_data *)data; Py_DECREF(d->aip); Py_DECREF(d->aop); - PyArray_free(data); + PyMem_Free(data); } /* strided cast data copy function */ static NpyAuxData *_strided_cast_data_clone(NpyAuxData *data) { _strided_cast_data *newdata = - (_strided_cast_data *)PyArray_malloc(sizeof(_strided_cast_data)); + (_strided_cast_data *)PyMem_Malloc(sizeof(_strided_cast_data)); if (newdata == NULL) { return NULL; } @@ -641,101 +620,104 @@ static NpyAuxData *_strided_cast_data_clone(NpyAuxData *data) return (NpyAuxData *)newdata; } -static void -_aligned_strided_to_strided_cast(char *dst, npy_intp dst_stride, - char *src, npy_intp src_stride, - npy_intp N, npy_intp src_itemsize, - NpyAuxData *data) +static int +_aligned_strided_to_strided_cast( + PyArrayMethod_Context *NPY_UNUSED(context), char *const *args, + const npy_intp *dimensions, const npy_intp *strides, + NpyAuxData *auxdata) { - _strided_cast_data *d = (_strided_cast_data *)data; + npy_intp N = dimensions[0]; + char *src = args[0], *dst = args[1]; + npy_intp src_stride = strides[0], dst_stride = strides[1]; + + _strided_cast_data *d = (_strided_cast_data *)auxdata; PyArray_VectorUnaryFunc *castfunc = d->castfunc; PyArrayObject *aip = d->aip, *aop = d->aop; + npy_bool needs_api = d->needs_api; while (N > 0) { castfunc(src, dst, 1, aip, aop); + /* + * Since error handling in ufuncs is not ideal (at the time of + * writing this, an error could be in process before calling this + * function. For most of NumPy history these checks were completely + * missing, so this is hopefully OK for the time being (until ufuncs + * are fixed). + */ + if (needs_api && PyErr_Occurred()) { + return -1; + } dst += dst_stride; src += src_stride; --N; } + return 0; } /* This one requires src be of type NPY_OBJECT */ -static void -_aligned_strided_to_strided_cast_decref_src(char *dst, npy_intp dst_stride, - char *src, npy_intp src_stride, - npy_intp N, npy_intp src_itemsize, - NpyAuxData *data) +static int +_aligned_strided_to_strided_cast_decref_src( + PyArrayMethod_Context *NPY_UNUSED(context), char *const *args, + const npy_intp *dimensions, const npy_intp *strides, + NpyAuxData *auxdata) { + npy_intp N = dimensions[0]; + char *src = args[0], *dst = args[1]; + npy_intp src_stride = strides[0], dst_stride = strides[1]; + + _any_to_object_auxdata *data = (_any_to_object_auxdata *)auxdata; _strided_cast_data *d = (_strided_cast_data *)data; PyArray_VectorUnaryFunc *castfunc = d->castfunc; PyArrayObject *aip = d->aip, *aop = d->aop; + npy_bool needs_api = d->needs_api; PyObject *src_ref; while (N > 0) { castfunc(src, dst, 1, aip, aop); - - /* After casting, decrement the source ref */ - NPY_COPY_PYOBJECT_PTR(&src_ref, src); - NPY_DT_DBG_REFTRACE("dec src ref (cast object -> not object)", src_ref); + /* + * See comment in `_aligned_strided_to_strided_cast`, an error could + * in principle be set before `castfunc` is called. + */ + if (needs_api && PyErr_Occurred()) { + return -1; + } + /* After casting, decrement the source ref and set it to NULL */ + memcpy(&src_ref, src, sizeof(src_ref)); Py_XDECREF(src_ref); + memset(src, 0, sizeof(PyObject *)); + NPY_DT_DBG_REFTRACE("dec src ref (cast object -> not object)", src_ref); dst += dst_stride; src += src_stride; --N; } + return 0; } -static void -_aligned_contig_to_contig_cast(char *dst, npy_intp NPY_UNUSED(dst_stride), - char *src, npy_intp NPY_UNUSED(src_stride), - npy_intp N, npy_intp NPY_UNUSED(itemsize), - NpyAuxData *data) +static int +_aligned_contig_to_contig_cast( + PyArrayMethod_Context *NPY_UNUSED(context), char * const*args, + const npy_intp *dimensions, const npy_intp *NPY_UNUSED(strides), + NpyAuxData *auxdata) { - _strided_cast_data *d = (_strided_cast_data *)data; + npy_intp N = dimensions[0]; + char *src = args[0], *dst = args[1]; - d->castfunc(src, dst, N, d->aip, d->aop); -} + _strided_cast_data *d = (_strided_cast_data *)auxdata; + npy_bool needs_api = d->needs_api; -static int -get_nbo_cast_numeric_transfer_function(int aligned, - npy_intp src_stride, npy_intp dst_stride, - int src_type_num, int dst_type_num, - PyArray_StridedUnaryOp **out_stransfer, - NpyAuxData **out_transferdata) -{ - /* Emit a warning if complex imaginary is being cast away */ - if (PyTypeNum_ISCOMPLEX(src_type_num) && - !PyTypeNum_ISCOMPLEX(dst_type_num) && - !PyTypeNum_ISBOOL(dst_type_num)) { - PyObject *cls = NULL, *obj = NULL; - int ret; - obj = PyImport_ImportModule("numpy.core"); - if (obj) { - cls = PyObject_GetAttrString(obj, "ComplexWarning"); - Py_DECREF(obj); - } - ret = PyErr_WarnEx(cls, - "Casting complex values to real discards " - "the imaginary part", 1); - Py_XDECREF(cls); - if (ret < 0) { - return NPY_FAIL; - } - } - - *out_stransfer = PyArray_GetStridedNumericCastFn(aligned, - src_stride, dst_stride, - src_type_num, dst_type_num); - *out_transferdata = NULL; - if (*out_stransfer == NULL) { - PyErr_SetString(PyExc_ValueError, - "unexpected error in GetStridedNumericCastFn"); - return NPY_FAIL; + d->castfunc(src, dst, N, d->aip, d->aop); + /* + * See comment in `_aligned_strided_to_strided_cast`, an error could + * in principle be set before `castfunc` is called. + */ + if (needs_api && PyErr_Occurred()) { + return -1; } - - return NPY_SUCCEED; + return 0; } + /* * Does a datetime->datetime, timedelta->timedelta, * datetime->ascii, or ascii->datetime cast @@ -764,15 +746,15 @@ typedef struct { static void _strided_datetime_cast_data_free(NpyAuxData *data) { _strided_datetime_cast_data *d = (_strided_datetime_cast_data *)data; - PyArray_free(d->tmp_buffer); - PyArray_free(data); + PyMem_Free(d->tmp_buffer); + PyMem_Free(data); } /* strided datetime cast data copy function */ static NpyAuxData *_strided_datetime_cast_data_clone(NpyAuxData *data) { _strided_datetime_cast_data *newdata = - (_strided_datetime_cast_data *)PyArray_malloc( + (_strided_datetime_cast_data *)PyMem_Malloc( sizeof(_strided_datetime_cast_data)); if (newdata == NULL) { return NULL; @@ -780,9 +762,9 @@ static NpyAuxData *_strided_datetime_cast_data_clone(NpyAuxData *data) memcpy(newdata, data, sizeof(_strided_datetime_cast_data)); if (newdata->tmp_buffer != NULL) { - newdata->tmp_buffer = PyArray_malloc(newdata->src_itemsize + 1); + newdata->tmp_buffer = PyMem_Malloc(newdata->src_itemsize + 1); if (newdata->tmp_buffer == NULL) { - PyArray_free(newdata); + PyMem_Free(newdata); return NULL; } } @@ -790,13 +772,17 @@ static NpyAuxData *_strided_datetime_cast_data_clone(NpyAuxData *data) return (NpyAuxData *)newdata; } -static void -_strided_to_strided_datetime_general_cast(char *dst, npy_intp dst_stride, - char *src, npy_intp src_stride, - npy_intp N, npy_intp src_itemsize, - NpyAuxData *data) +static int +_strided_to_strided_datetime_general_cast( + PyArrayMethod_Context *NPY_UNUSED(context), char * const*args, + const npy_intp *dimensions, const npy_intp *strides, + NpyAuxData *auxdata) { - _strided_datetime_cast_data *d = (_strided_datetime_cast_data *)data; + npy_intp N = dimensions[0]; + char *src = args[0], *dst = args[1]; + npy_intp src_stride = strides[0], dst_stride = strides[1]; + + _strided_datetime_cast_data *d = (_strided_datetime_cast_data *)auxdata; npy_int64 dt; npy_datetimestruct dts; @@ -805,12 +791,12 @@ _strided_to_strided_datetime_general_cast(char *dst, npy_intp dst_stride, if (convert_datetime_to_datetimestruct(&d->src_meta, dt, &dts) < 0) { - dt = NPY_DATETIME_NAT; + return -1; } else { if (convert_datetimestruct_to_datetime(&d->dst_meta, &dts, &dt) < 0) { - dt = NPY_DATETIME_NAT; + return -1; } } @@ -820,15 +806,20 @@ _strided_to_strided_datetime_general_cast(char *dst, npy_intp dst_stride, src += src_stride; --N; } + return 0; } -static void -_strided_to_strided_datetime_cast(char *dst, npy_intp dst_stride, - char *src, npy_intp src_stride, - npy_intp N, npy_intp src_itemsize, - NpyAuxData *data) +static int +_strided_to_strided_datetime_cast( + PyArrayMethod_Context *NPY_UNUSED(context), char * const*args, + const npy_intp *dimensions, const npy_intp *strides, + NpyAuxData *auxdata) { - _strided_datetime_cast_data *d = (_strided_datetime_cast_data *)data; + npy_intp N = dimensions[0]; + char *src = args[0], *dst = args[1]; + npy_intp src_stride = strides[0], dst_stride = strides[1]; + + _strided_datetime_cast_data *d = (_strided_datetime_cast_data *)auxdata; npy_int64 num = d->num, denom = d->denom; npy_int64 dt; @@ -851,16 +842,20 @@ _strided_to_strided_datetime_cast(char *dst, npy_intp dst_stride, src += src_stride; --N; } + return 0; } -static void -_aligned_strided_to_strided_datetime_cast(char *dst, - npy_intp dst_stride, - char *src, npy_intp src_stride, - npy_intp N, npy_intp src_itemsize, - NpyAuxData *data) +static int +_aligned_strided_to_strided_datetime_cast( + PyArrayMethod_Context *NPY_UNUSED(context), char *const *args, + const npy_intp *dimensions, const npy_intp *strides, + NpyAuxData *auxdata) { - _strided_datetime_cast_data *d = (_strided_datetime_cast_data *)data; + npy_intp N = dimensions[0]; + char *src = args[0], *dst = args[1]; + npy_intp src_stride = strides[0], dst_stride = strides[1]; + + _strided_datetime_cast_data *d = (_strided_datetime_cast_data *)auxdata; npy_int64 num = d->num, denom = d->denom; npy_int64 dt; @@ -883,15 +878,20 @@ _aligned_strided_to_strided_datetime_cast(char *dst, src += src_stride; --N; } + return 0; } -static void -_strided_to_strided_datetime_to_string(char *dst, npy_intp dst_stride, - char *src, npy_intp src_stride, - npy_intp N, npy_intp NPY_UNUSED(src_itemsize), - NpyAuxData *data) +static int +_strided_to_strided_datetime_to_string( + PyArrayMethod_Context *NPY_UNUSED(context), char *const *args, + const npy_intp *dimensions, const npy_intp *strides, + NpyAuxData *auxdata) { - _strided_datetime_cast_data *d = (_strided_datetime_cast_data *)data; + npy_intp N = dimensions[0]; + char *src = args[0], *dst = args[1]; + npy_intp src_stride = strides[0], dst_stride = strides[1]; + + _strided_datetime_cast_data *d = (_strided_datetime_cast_data *)auxdata; npy_intp dst_itemsize = d->dst_itemsize; npy_int64 dt; npy_datetimestruct dts; @@ -901,34 +901,37 @@ _strided_to_strided_datetime_to_string(char *dst, npy_intp dst_stride, if (convert_datetime_to_datetimestruct(&d->src_meta, dt, &dts) < 0) { - /* For an error, produce a 'NaT' string */ - dts.year = NPY_DATETIME_NAT; + return -1; } /* Initialize the destination to all zeros */ memset(dst, 0, dst_itemsize); - /* - * This may also raise an error, but the caller needs - * to use PyErr_Occurred(). - */ - make_iso_8601_datetime(&dts, dst, dst_itemsize, + if (make_iso_8601_datetime(&dts, dst, dst_itemsize, 0, 0, d->src_meta.base, -1, - NPY_UNSAFE_CASTING); + NPY_UNSAFE_CASTING) < 0) { + return -1; + } dst += dst_stride; src += src_stride; --N; } + return 0; } -static void -_strided_to_strided_string_to_datetime(char *dst, npy_intp dst_stride, - char *src, npy_intp src_stride, - npy_intp N, npy_intp src_itemsize, - NpyAuxData *data) +static int +_strided_to_strided_string_to_datetime( + PyArrayMethod_Context *context, char *const *args, + const npy_intp *dimensions, const npy_intp *strides, + NpyAuxData *auxdata) { - _strided_datetime_cast_data *d = (_strided_datetime_cast_data *)data; + npy_intp N = dimensions[0]; + char *src = args[0], *dst = args[1]; + npy_intp src_itemsize = context->descriptors[0]->elsize; + npy_intp src_stride = strides[0], dst_stride = strides[1]; + + _strided_datetime_cast_data *d = (_strided_datetime_cast_data *)auxdata; npy_datetimestruct dts; char *tmp_buffer = d->tmp_buffer; char *tmp; @@ -947,7 +950,7 @@ _strided_to_strided_string_to_datetime(char *dst, npy_intp dst_stride, if (parse_iso_8601_datetime(tmp_buffer, src_itemsize, d->dst_meta.base, NPY_SAME_KIND_CASTING, &dts, NULL, NULL) < 0) { - dt = NPY_DATETIME_NAT; + return -1; } } /* Otherwise parse the data in place */ @@ -955,7 +958,7 @@ _strided_to_strided_string_to_datetime(char *dst, npy_intp dst_stride, if (parse_iso_8601_datetime(src, tmp - src, d->dst_meta.base, NPY_SAME_KIND_CASTING, &dts, NULL, NULL) < 0) { - dt = NPY_DATETIME_NAT; + return -1; } } @@ -963,7 +966,7 @@ _strided_to_strided_string_to_datetime(char *dst, npy_intp dst_stride, if (dt != NPY_DATETIME_NAT && convert_datetimestruct_to_datetime(&d->dst_meta, &dts, &dt) < 0) { - dt = NPY_DATETIME_NAT; + return -1; } memcpy(dst, &dt, sizeof(dt)); @@ -972,16 +975,16 @@ _strided_to_strided_string_to_datetime(char *dst, npy_intp dst_stride, src += src_stride; --N; } + return 0; } /* * Assumes src_dtype and dst_dtype are both datetimes or both timedeltas */ -static int +NPY_NO_EXPORT int get_nbo_cast_datetime_transfer_function(int aligned, - npy_intp src_stride, npy_intp dst_stride, PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype, - PyArray_StridedUnaryOp **out_stransfer, + PyArrayMethod_StridedLoop **out_stransfer, NpyAuxData **out_transferdata) { PyArray_DatetimeMetaData *src_meta, *dst_meta; @@ -1004,7 +1007,7 @@ get_nbo_cast_datetime_transfer_function(int aligned, } /* Allocate the data for the casting */ - data = (_strided_datetime_cast_data *)PyArray_malloc( + data = (_strided_datetime_cast_data *)PyMem_Malloc( sizeof(_strided_datetime_cast_data)); if (data == NULL) { PyErr_NoMemory(); @@ -1042,9 +1045,9 @@ get_nbo_cast_datetime_transfer_function(int aligned, #if NPY_DT_DBG_TRACING printf("Dtype transfer from "); - PyObject_Print((PyObject *)src_dtype, stdout, 0); + _safe_print((PyObject *)src_dtype); printf(" to "); - PyObject_Print((PyObject *)dst_dtype, stdout, 0); + _safe_print((PyObject *)dst_dtype); printf("\n"); printf("has conversion fraction %lld/%lld\n", num, denom); #endif @@ -1053,12 +1056,10 @@ get_nbo_cast_datetime_transfer_function(int aligned, return NPY_SUCCEED; } -static int -get_nbo_datetime_to_string_transfer_function(int aligned, - npy_intp src_stride, npy_intp dst_stride, - PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype, - PyArray_StridedUnaryOp **out_stransfer, - NpyAuxData **out_transferdata) +NPY_NO_EXPORT int +get_nbo_datetime_to_string_transfer_function( + PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype, + PyArrayMethod_StridedLoop **out_stransfer, NpyAuxData **out_transferdata) { PyArray_DatetimeMetaData *src_meta; _strided_datetime_cast_data *data; @@ -1069,7 +1070,7 @@ get_nbo_datetime_to_string_transfer_function(int aligned, } /* Allocate the data for the casting */ - data = (_strided_datetime_cast_data *)PyArray_malloc( + data = (_strided_datetime_cast_data *)PyMem_Malloc( sizeof(_strided_datetime_cast_data)); if (data == NULL) { PyErr_NoMemory(); @@ -1089,91 +1090,62 @@ get_nbo_datetime_to_string_transfer_function(int aligned, #if NPY_DT_DBG_TRACING printf("Dtype transfer from "); - PyObject_Print((PyObject *)src_dtype, stdout, 0); + _safe_print((PyObject *)src_dtype); printf(" to "); - PyObject_Print((PyObject *)dst_dtype, stdout, 0); + _safe_print((PyObject *)dst_dtype); printf("\n"); #endif return NPY_SUCCEED; } -static int + +NPY_NO_EXPORT int get_datetime_to_unicode_transfer_function(int aligned, npy_intp src_stride, npy_intp dst_stride, PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype, - PyArray_StridedUnaryOp **out_stransfer, + PyArrayMethod_StridedLoop **out_stransfer, NpyAuxData **out_transferdata, int *out_needs_api) { - NpyAuxData *castdata = NULL, *todata = NULL, *fromdata = NULL; - PyArray_StridedUnaryOp *caststransfer, *tobuffer, *frombuffer; PyArray_Descr *str_dtype; /* Get an ASCII string data type, adapted to match the UNICODE one */ - str_dtype = PyArray_DescrFromType(NPY_STRING); - PyArray_AdaptFlexibleDType(NULL, dst_dtype, &str_dtype); + str_dtype = PyArray_DescrNewFromType(NPY_STRING); if (str_dtype == NULL) { return NPY_FAIL; } + str_dtype->elsize = dst_dtype->elsize / 4; - /* Get the copy/swap operation to dst */ - if (PyArray_GetDTypeCopySwapFn(aligned, - src_stride, src_dtype->elsize, - src_dtype, - &tobuffer, &todata) != NPY_SUCCEED) { - Py_DECREF(str_dtype); - return NPY_FAIL; - } + /* ensured in resolve_descriptors for simplicity */ + assert(PyDataType_ISNOTSWAPPED(src_dtype)); /* Get the NBO datetime to string aligned contig function */ - if (get_nbo_datetime_to_string_transfer_function(1, - src_dtype->elsize, str_dtype->elsize, - src_dtype, str_dtype, - &caststransfer, &castdata) != NPY_SUCCEED) { - Py_DECREF(str_dtype); - NPY_AUXDATA_FREE(todata); - return NPY_FAIL; - } - - /* Get the cast operation to dst */ - if (PyArray_GetDTypeTransferFunction(aligned, - str_dtype->elsize, dst_stride, - str_dtype, dst_dtype, - 0, - &frombuffer, &fromdata, - out_needs_api) != NPY_SUCCEED) { + if (get_nbo_datetime_to_string_transfer_function( + src_dtype, str_dtype, + out_stransfer, out_transferdata) != NPY_SUCCEED) { Py_DECREF(str_dtype); - NPY_AUXDATA_FREE(todata); - NPY_AUXDATA_FREE(castdata); return NPY_FAIL; } - /* Wrap it all up in a new transfer function + data */ - if (wrap_aligned_contig_transfer_function( - src_dtype->elsize, str_dtype->elsize, - tobuffer, todata, - frombuffer, fromdata, - caststransfer, castdata, - PyDataType_FLAGCHK(str_dtype, NPY_NEEDS_INIT), - out_stransfer, out_transferdata) != NPY_SUCCEED) { - NPY_AUXDATA_FREE(castdata); - NPY_AUXDATA_FREE(todata); - NPY_AUXDATA_FREE(fromdata); + int res = wrap_aligned_transferfunction( + aligned, 0, /* no need to ensure contiguous */ + src_stride, dst_stride, + src_dtype, dst_dtype, + src_dtype, str_dtype, + out_stransfer, out_transferdata, out_needs_api); + Py_DECREF(str_dtype); + if (res < 0) { return NPY_FAIL; } - Py_DECREF(str_dtype); - return NPY_SUCCEED; } -static int -get_nbo_string_to_datetime_transfer_function(int aligned, - npy_intp src_stride, npy_intp dst_stride, - PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype, - PyArray_StridedUnaryOp **out_stransfer, - NpyAuxData **out_transferdata) +NPY_NO_EXPORT int +get_nbo_string_to_datetime_transfer_function( + PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype, + PyArrayMethod_StridedLoop **out_stransfer, NpyAuxData **out_transferdata) { PyArray_DatetimeMetaData *dst_meta; _strided_datetime_cast_data *data; @@ -1184,7 +1156,7 @@ get_nbo_string_to_datetime_transfer_function(int aligned, } /* Allocate the data for the casting */ - data = (_strided_datetime_cast_data *)PyArray_malloc( + data = (_strided_datetime_cast_data *)PyMem_Malloc( sizeof(_strided_datetime_cast_data)); if (data == NULL) { PyErr_NoMemory(); @@ -1195,10 +1167,10 @@ get_nbo_string_to_datetime_transfer_function(int aligned, data->base.free = &_strided_datetime_cast_data_free; data->base.clone = &_strided_datetime_cast_data_clone; data->src_itemsize = src_dtype->elsize; - data->tmp_buffer = PyArray_malloc(data->src_itemsize + 1); + data->tmp_buffer = PyMem_Malloc(data->src_itemsize + 1); if (data->tmp_buffer == NULL) { PyErr_NoMemory(); - PyArray_free(data); + PyMem_Free(data); *out_stransfer = NULL; *out_transferdata = NULL; return NPY_FAIL; @@ -1211,175 +1183,70 @@ get_nbo_string_to_datetime_transfer_function(int aligned, #if NPY_DT_DBG_TRACING printf("Dtype transfer from "); - PyObject_Print((PyObject *)src_dtype, stdout, 0); + _safe_print((PyObject *)src_dtype); printf(" to "); - PyObject_Print((PyObject *)dst_dtype, stdout, 0); + _safe_print((PyObject *)dst_dtype); printf("\n"); #endif return NPY_SUCCEED; } -static int +NPY_NO_EXPORT int get_unicode_to_datetime_transfer_function(int aligned, npy_intp src_stride, npy_intp dst_stride, PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype, - PyArray_StridedUnaryOp **out_stransfer, + PyArrayMethod_StridedLoop **out_stransfer, NpyAuxData **out_transferdata, int *out_needs_api) { - NpyAuxData *castdata = NULL, *todata = NULL, *fromdata = NULL; - PyArray_StridedUnaryOp *caststransfer, *tobuffer, *frombuffer; PyArray_Descr *str_dtype; /* Get an ASCII string data type, adapted to match the UNICODE one */ - str_dtype = PyArray_DescrFromType(NPY_STRING); - PyArray_AdaptFlexibleDType(NULL, src_dtype, &str_dtype); + str_dtype = PyArray_DescrNewFromType(NPY_STRING); if (str_dtype == NULL) { return NPY_FAIL; } + assert(src_dtype->type_num == NPY_UNICODE); + str_dtype->elsize = src_dtype->elsize / 4; - /* Get the cast operation from src */ - if (PyArray_GetDTypeTransferFunction(aligned, - src_stride, str_dtype->elsize, - src_dtype, str_dtype, - 0, - &tobuffer, &todata, - out_needs_api) != NPY_SUCCEED) { - Py_DECREF(str_dtype); - return NPY_FAIL; - } - - /* Get the string to NBO datetime aligned contig function */ - if (get_nbo_string_to_datetime_transfer_function(1, - str_dtype->elsize, dst_dtype->elsize, - str_dtype, dst_dtype, - &caststransfer, &castdata) != NPY_SUCCEED) { + /* Get the string to NBO datetime aligned function */ + if (get_nbo_string_to_datetime_transfer_function( + str_dtype, dst_dtype, + out_stransfer, out_transferdata) != NPY_SUCCEED) { Py_DECREF(str_dtype); - NPY_AUXDATA_FREE(todata); return NPY_FAIL; } - /* Get the copy/swap operation to dst */ - if (PyArray_GetDTypeCopySwapFn(aligned, - dst_dtype->elsize, dst_stride, - dst_dtype, - &frombuffer, &fromdata) != NPY_SUCCEED) { - Py_DECREF(str_dtype); - NPY_AUXDATA_FREE(todata); - NPY_AUXDATA_FREE(castdata); - return NPY_FAIL; - } + int res = wrap_aligned_transferfunction( + aligned, 0, /* no need to ensure contiguous */ + src_stride, dst_stride, + src_dtype, dst_dtype, + str_dtype, dst_dtype, + out_stransfer, out_transferdata, out_needs_api); + Py_DECREF(str_dtype); - /* Wrap it all up in a new transfer function + data */ - if (wrap_aligned_contig_transfer_function( - str_dtype->elsize, dst_dtype->elsize, - tobuffer, todata, - frombuffer, fromdata, - caststransfer, castdata, - PyDataType_FLAGCHK(dst_dtype, NPY_NEEDS_INIT), - out_stransfer, out_transferdata) != NPY_SUCCEED) { - Py_DECREF(str_dtype); - NPY_AUXDATA_FREE(castdata); - NPY_AUXDATA_FREE(todata); - NPY_AUXDATA_FREE(fromdata); + if (res < 0) { return NPY_FAIL; } - - Py_DECREF(str_dtype); - return NPY_SUCCEED; } -static int -get_nbo_cast_transfer_function(int aligned, - npy_intp src_stride, npy_intp dst_stride, - PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype, - int move_references, - PyArray_StridedUnaryOp **out_stransfer, - NpyAuxData **out_transferdata, - int *out_needs_api, - int *out_needs_wrap) + +NPY_NO_EXPORT int +get_legacy_dtype_cast_function( + int aligned, npy_intp src_stride, npy_intp dst_stride, + PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype, + int move_references, + PyArrayMethod_StridedLoop **out_stransfer, NpyAuxData **out_transferdata, + int *out_needs_api, int *out_needs_wrap) { _strided_cast_data *data; PyArray_VectorUnaryFunc *castfunc; PyArray_Descr *tmp_dtype; - npy_intp shape = 1, src_itemsize = src_dtype->elsize, - dst_itemsize = dst_dtype->elsize; - - if (PyTypeNum_ISNUMBER(src_dtype->type_num) && - PyTypeNum_ISNUMBER(dst_dtype->type_num)) { - *out_needs_wrap = !PyArray_ISNBO(src_dtype->byteorder) || - !PyArray_ISNBO(dst_dtype->byteorder); - return get_nbo_cast_numeric_transfer_function(aligned, - src_stride, dst_stride, - src_dtype->type_num, dst_dtype->type_num, - out_stransfer, out_transferdata); - } - - if (src_dtype->type_num == NPY_DATETIME || - src_dtype->type_num == NPY_TIMEDELTA || - dst_dtype->type_num == NPY_DATETIME || - dst_dtype->type_num == NPY_TIMEDELTA) { - /* A parameterized type, datetime->datetime sometimes needs casting */ - if ((src_dtype->type_num == NPY_DATETIME && - dst_dtype->type_num == NPY_DATETIME) || - (src_dtype->type_num == NPY_TIMEDELTA && - dst_dtype->type_num == NPY_TIMEDELTA)) { - *out_needs_wrap = !PyArray_ISNBO(src_dtype->byteorder) || - !PyArray_ISNBO(dst_dtype->byteorder); - return get_nbo_cast_datetime_transfer_function(aligned, - src_stride, dst_stride, - src_dtype, dst_dtype, - out_stransfer, out_transferdata); - } - - /* - * Datetime <-> string conversions can be handled specially. - * The functions may raise an error if the strings have no - * space, or can't be parsed properly. - */ - if (src_dtype->type_num == NPY_DATETIME) { - switch (dst_dtype->type_num) { - case NPY_STRING: - *out_needs_api = 1; - *out_needs_wrap = !PyArray_ISNBO(src_dtype->byteorder); - return get_nbo_datetime_to_string_transfer_function( - aligned, - src_stride, dst_stride, - src_dtype, dst_dtype, - out_stransfer, out_transferdata); - - case NPY_UNICODE: - return get_datetime_to_unicode_transfer_function( - aligned, - src_stride, dst_stride, - src_dtype, dst_dtype, - out_stransfer, out_transferdata, - out_needs_api); - } - } - else if (dst_dtype->type_num == NPY_DATETIME) { - switch (src_dtype->type_num) { - case NPY_STRING: - *out_needs_api = 1; - *out_needs_wrap = !PyArray_ISNBO(dst_dtype->byteorder); - return get_nbo_string_to_datetime_transfer_function( - aligned, - src_stride, dst_stride, - src_dtype, dst_dtype, - out_stransfer, out_transferdata); - - case NPY_UNICODE: - return get_unicode_to_datetime_transfer_function( - aligned, - src_stride, dst_stride, - src_dtype, dst_dtype, - out_stransfer, out_transferdata, - out_needs_api); - } - } - } + npy_intp shape = 1; + npy_intp src_itemsize = src_dtype->elsize; + npy_intp dst_itemsize = dst_dtype->elsize; *out_needs_wrap = !aligned || !PyArray_ISNBO(src_dtype->byteorder) || @@ -1423,7 +1290,7 @@ get_nbo_cast_transfer_function(int aligned, } /* Allocate the data for the casting */ - data = (_strided_cast_data *)PyArray_malloc(sizeof(_strided_cast_data)); + data = (_strided_cast_data *)PyMem_Malloc(sizeof(_strided_cast_data)); if (data == NULL) { PyErr_NoMemory(); *out_stransfer = NULL; @@ -1433,6 +1300,7 @@ get_nbo_cast_transfer_function(int aligned, data->base.free = &_strided_cast_data_free; data->base.clone = &_strided_cast_data_clone; data->castfunc = castfunc; + data->needs_api = *out_needs_api; /* * TODO: This is a hack so the cast functions have an array. * The cast functions shouldn't need that. Also, since we @@ -1446,7 +1314,7 @@ get_nbo_cast_transfer_function(int aligned, else { tmp_dtype = PyArray_DescrNewByteorder(src_dtype, NPY_NATIVE); if (tmp_dtype == NULL) { - PyArray_free(data); + PyMem_Free(data); return NPY_FAIL; } } @@ -1456,7 +1324,7 @@ get_nbo_cast_transfer_function(int aligned, 0, NULL, NULL, 0, 1); if (data->aip == NULL) { - PyArray_free(data); + PyMem_Free(data); return NPY_FAIL; } /* @@ -1473,7 +1341,7 @@ get_nbo_cast_transfer_function(int aligned, tmp_dtype = PyArray_DescrNewByteorder(dst_dtype, NPY_NATIVE); if (tmp_dtype == NULL) { Py_DECREF(data->aip); - PyArray_free(data); + PyMem_Free(data); return NPY_FAIL; } } @@ -1484,7 +1352,7 @@ get_nbo_cast_transfer_function(int aligned, 0, 1); if (data->aop == NULL) { Py_DECREF(data->aip); - PyArray_free(data); + PyMem_Free(data); return NPY_FAIL; } @@ -1511,106 +1379,25 @@ get_nbo_cast_transfer_function(int aligned, return NPY_SUCCEED; } -static int -get_cast_transfer_function(int aligned, - npy_intp src_stride, npy_intp dst_stride, - PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype, - int move_references, - PyArray_StridedUnaryOp **out_stransfer, - NpyAuxData **out_transferdata, - int *out_needs_api) -{ - PyArray_StridedUnaryOp *caststransfer; - NpyAuxData *castdata, *todata = NULL, *fromdata = NULL; - int needs_wrap = 0; - npy_intp src_itemsize = src_dtype->elsize, - dst_itemsize = dst_dtype->elsize; - - if (get_nbo_cast_transfer_function(aligned, - src_stride, dst_stride, - src_dtype, dst_dtype, - move_references, - &caststransfer, - &castdata, - out_needs_api, - &needs_wrap) != NPY_SUCCEED) { - return NPY_FAIL; - } - - /* - * If all native byte order and doesn't need alignment wrapping, - * return the function - */ - if (!needs_wrap) { - *out_stransfer = caststransfer; - *out_transferdata = castdata; - - return NPY_SUCCEED; - } - /* Otherwise, we have to copy and/or swap to aligned temporaries */ - else { - PyArray_StridedUnaryOp *tobuffer, *frombuffer; - - /* Get the copy/swap operation from src */ - PyArray_GetDTypeCopySwapFn(aligned, - src_stride, src_itemsize, - src_dtype, - &tobuffer, &todata); - - - /* Get the copy/swap operation to dst */ - PyArray_GetDTypeCopySwapFn(aligned, - dst_itemsize, dst_stride, - dst_dtype, - &frombuffer, &fromdata); - - if (frombuffer == NULL || tobuffer == NULL) { - NPY_AUXDATA_FREE(castdata); - NPY_AUXDATA_FREE(todata); - NPY_AUXDATA_FREE(fromdata); - return NPY_FAIL; - } - - *out_stransfer = caststransfer; - - /* Wrap it all up in a new transfer function + data */ - if (wrap_aligned_contig_transfer_function( - src_itemsize, dst_itemsize, - tobuffer, todata, - frombuffer, fromdata, - caststransfer, castdata, - PyDataType_FLAGCHK(dst_dtype, NPY_NEEDS_INIT), - out_stransfer, out_transferdata) != NPY_SUCCEED) { - NPY_AUXDATA_FREE(castdata); - NPY_AUXDATA_FREE(todata); - NPY_AUXDATA_FREE(fromdata); - return NPY_FAIL; - } - - return NPY_SUCCEED; - } -} /**************************** COPY 1 TO N CONTIGUOUS ************************/ /* Copies 1 element to N contiguous elements */ typedef struct { NpyAuxData base; - PyArray_StridedUnaryOp *stransfer; - NpyAuxData *data; - npy_intp N, dst_itemsize; - /* If this is non-NULL the source type has references needing a decref */ - PyArray_StridedUnaryOp *stransfer_finish_src; - NpyAuxData *data_finish_src; + npy_intp N; + NPY_cast_info wrapped; + /* If finish->func is non-NULL the source needs a decref */ + NPY_cast_info decref_src; } _one_to_n_data; /* transfer data free function */ static void _one_to_n_data_free(NpyAuxData *data) { _one_to_n_data *d = (_one_to_n_data *)data; - NPY_AUXDATA_FREE(d->data); - NPY_AUXDATA_FREE(d->data_finish_src); - PyArray_free(data); + NPY_cast_info_xfree(&d->wrapped); + NPY_cast_info_xfree(&d->decref_src); + PyMem_Free(data); } /* transfer data copy function */ @@ -1620,104 +1407,108 @@ static NpyAuxData *_one_to_n_data_clone(NpyAuxData *data) _one_to_n_data *newdata; /* Allocate the data, and populate it */ - newdata = (_one_to_n_data *)PyArray_malloc(sizeof(_one_to_n_data)); + newdata = (_one_to_n_data *)PyMem_Malloc(sizeof(_one_to_n_data)); if (newdata == NULL) { return NULL; } - memcpy(newdata, data, sizeof(_one_to_n_data)); - if (d->data != NULL) { - newdata->data = NPY_AUXDATA_CLONE(d->data); - if (newdata->data == NULL) { - PyArray_free(newdata); - return NULL; - } + newdata->base.free = &_one_to_n_data_free; + newdata->base.clone = &_one_to_n_data_clone; + newdata->N = d->N; + /* Initialize in case of error, or if it is unused */ + NPY_cast_info_init(&newdata->decref_src); + + if (NPY_cast_info_copy(&newdata->wrapped, &d->wrapped) < 0) { + _one_to_n_data_free((NpyAuxData *)newdata); + return NULL; } - if (d->data_finish_src != NULL) { - newdata->data_finish_src = NPY_AUXDATA_CLONE(d->data_finish_src); - if (newdata->data_finish_src == NULL) { - NPY_AUXDATA_FREE(newdata->data); - PyArray_free(newdata); - return NULL; - } + if (d->decref_src.func == NULL) { + return (NpyAuxData *)newdata; + } + + if (NPY_cast_info_copy(&newdata->decref_src, &d->decref_src) < 0) { + _one_to_n_data_free((NpyAuxData *)newdata); + return NULL; } return (NpyAuxData *)newdata; } -static void -_strided_to_strided_one_to_n(char *dst, npy_intp dst_stride, - char *src, npy_intp src_stride, - npy_intp N, npy_intp src_itemsize, - NpyAuxData *data) +static int +_strided_to_strided_one_to_n( + PyArrayMethod_Context *NPY_UNUSED(context), char *const *args, + const npy_intp *dimensions, const npy_intp *strides, + NpyAuxData *auxdata) { - _one_to_n_data *d = (_one_to_n_data *)data; - PyArray_StridedUnaryOp *subtransfer = d->stransfer; - NpyAuxData *subdata = d->data; - npy_intp subN = d->N, dst_itemsize = d->dst_itemsize; + npy_intp N = dimensions[0]; + char *src = args[0], *dst = args[1]; + npy_intp src_stride = strides[0], dst_stride = strides[1]; + + _one_to_n_data *d = (_one_to_n_data *)auxdata; + + const npy_intp subN = d->N; + npy_intp sub_strides[2] = {0, d->wrapped.descriptors[1]->elsize}; while (N > 0) { - subtransfer(dst, dst_itemsize, - src, 0, - subN, src_itemsize, - subdata); + char *sub_args[2] = {src, dst}; + if (d->wrapped.func(&d->wrapped.context, + sub_args, &subN, sub_strides, d->wrapped.auxdata) < 0) { + return -1; + } src += src_stride; dst += dst_stride; --N; } + return 0; } -static void -_strided_to_strided_one_to_n_with_finish(char *dst, npy_intp dst_stride, - char *src, npy_intp src_stride, - npy_intp N, npy_intp src_itemsize, - NpyAuxData *data) +static int +_strided_to_strided_one_to_n_with_finish( + PyArrayMethod_Context *NPY_UNUSED(context), char *const *args, + const npy_intp *dimensions, const npy_intp *strides, + NpyAuxData *auxdata) { - _one_to_n_data *d = (_one_to_n_data *)data; - PyArray_StridedUnaryOp *subtransfer = d->stransfer, - *stransfer_finish_src = d->stransfer_finish_src; - NpyAuxData *subdata = d->data, *data_finish_src = d->data_finish_src; - npy_intp subN = d->N, dst_itemsize = d->dst_itemsize; + npy_intp N = dimensions[0]; + char *src = args[0], *dst = args[1]; + npy_intp src_stride = strides[0], dst_stride = strides[1]; - while (N > 0) { - subtransfer(dst, dst_itemsize, - src, 0, - subN, src_itemsize, - subdata); + _one_to_n_data *d = (_one_to_n_data *)auxdata; + + const npy_intp subN = d->N; + const npy_intp one_item = 1, zero_stride = 0; + npy_intp sub_strides[2] = {0, d->wrapped.descriptors[1]->elsize}; + while (N > 0) { + char *sub_args[2] = {src, dst}; + if (d->wrapped.func(&d->wrapped.context, + sub_args, &subN, sub_strides, d->wrapped.auxdata) < 0) { + return -1; + } - stransfer_finish_src(NULL, 0, - src, 0, - 1, src_itemsize, - data_finish_src); + if (d->decref_src.func(&d->decref_src.context, + &src, &one_item, &zero_stride, d->decref_src.auxdata) < 0) { + return -1; + } src += src_stride; dst += dst_stride; --N; } + return 0; } -/* - * Wraps a transfer function to produce one that copies one element - * of src to N contiguous elements of dst. If stransfer_finish_src is - * not NULL, it should be a transfer function which just affects - * src, for example to do a final DECREF operation for references. - */ + static int -wrap_transfer_function_one_to_n( - PyArray_StridedUnaryOp *stransfer_inner, - NpyAuxData *data_inner, - PyArray_StridedUnaryOp *stransfer_finish_src, - NpyAuxData *data_finish_src, - npy_intp dst_itemsize, +get_one_to_n_transfer_function(int aligned, + npy_intp src_stride, npy_intp dst_stride, + PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype, + int move_references, npy_intp N, - PyArray_StridedUnaryOp **out_stransfer, - NpyAuxData **out_transferdata) + PyArrayMethod_StridedLoop **out_stransfer, + NpyAuxData **out_transferdata, + int *out_needs_api) { - _one_to_n_data *data; - - - data = PyArray_malloc(sizeof(_one_to_n_data)); + _one_to_n_data *data = PyMem_Malloc(sizeof(_one_to_n_data)); if (data == NULL) { PyErr_NoMemory(); return NPY_FAIL; @@ -1725,36 +1516,8 @@ wrap_transfer_function_one_to_n( data->base.free = &_one_to_n_data_free; data->base.clone = &_one_to_n_data_clone; - data->stransfer = stransfer_inner; - data->data = data_inner; - data->stransfer_finish_src = stransfer_finish_src; - data->data_finish_src = data_finish_src; data->N = N; - data->dst_itemsize = dst_itemsize; - - if (stransfer_finish_src == NULL) { - *out_stransfer = &_strided_to_strided_one_to_n; - } - else { - *out_stransfer = &_strided_to_strided_one_to_n_with_finish; - } - *out_transferdata = (NpyAuxData *)data; - - return NPY_SUCCEED; -} - -static int -get_one_to_n_transfer_function(int aligned, - npy_intp src_stride, npy_intp dst_stride, - PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype, - int move_references, - npy_intp N, - PyArray_StridedUnaryOp **out_stransfer, - NpyAuxData **out_transferdata, - int *out_needs_api) -{ - PyArray_StridedUnaryOp *stransfer, *stransfer_finish_src = NULL; - NpyAuxData *data, *data_finish_src = NULL; + NPY_cast_info_init(&data->decref_src); /* In case of error */ /* * move_references is set to 0, handled in the wrapping transfer fn, @@ -1766,33 +1529,31 @@ get_one_to_n_transfer_function(int aligned, 0, dst_dtype->elsize, src_dtype, dst_dtype, 0, - &stransfer, &data, + &data->wrapped, out_needs_api) != NPY_SUCCEED) { + NPY_AUXDATA_FREE((NpyAuxData *)data); return NPY_FAIL; } /* If the src object will need a DECREF, set src_dtype */ if (move_references && PyDataType_REFCHK(src_dtype)) { - if (get_decsrcref_transfer_function(aligned, + if (get_decref_transfer_function(aligned, src_stride, src_dtype, - &stransfer_finish_src, - &data_finish_src, + &data->decref_src, out_needs_api) != NPY_SUCCEED) { - NPY_AUXDATA_FREE(data); + NPY_AUXDATA_FREE((NpyAuxData *)data); return NPY_FAIL; } } - if (wrap_transfer_function_one_to_n(stransfer, data, - stransfer_finish_src, data_finish_src, - dst_dtype->elsize, - N, - out_stransfer, out_transferdata) != NPY_SUCCEED) { - NPY_AUXDATA_FREE(data); - NPY_AUXDATA_FREE(data_finish_src); - return NPY_FAIL; + if (data->decref_src.func == NULL) { + *out_stransfer = &_strided_to_strided_one_to_n; + } + else { + *out_stransfer = &_strided_to_strided_one_to_n_with_finish; } + *out_transferdata = (NpyAuxData *)data; return NPY_SUCCEED; } @@ -1802,17 +1563,17 @@ get_one_to_n_transfer_function(int aligned, /* Copies N contiguous elements to N contiguous elements */ typedef struct { NpyAuxData base; - PyArray_StridedUnaryOp *stransfer; - NpyAuxData *data; - npy_intp N, src_itemsize, dst_itemsize; + NPY_cast_info wrapped; + npy_intp N; + npy_intp strides[2]; /* avoid look up on the dtype (dst can be NULL) */ } _n_to_n_data; /* transfer data free function */ static void _n_to_n_data_free(NpyAuxData *data) { _n_to_n_data *d = (_n_to_n_data *)data; - NPY_AUXDATA_FREE(d->data); - PyArray_free(data); + NPY_cast_info_xfree(&d->wrapped); + PyMem_Free(data); } /* transfer data copy function */ @@ -1822,100 +1583,138 @@ static NpyAuxData *_n_to_n_data_clone(NpyAuxData *data) _n_to_n_data *newdata; /* Allocate the data, and populate it */ - newdata = (_n_to_n_data *)PyArray_malloc(sizeof(_n_to_n_data)); + newdata = (_n_to_n_data *)PyMem_Malloc(sizeof(_n_to_n_data)); if (newdata == NULL) { return NULL; } - memcpy(newdata, data, sizeof(_n_to_n_data)); - if (newdata->data != NULL) { - newdata->data = NPY_AUXDATA_CLONE(d->data); - if (newdata->data == NULL) { - PyArray_free(newdata); - return NULL; - } + *newdata = *d; + + if (NPY_cast_info_copy(&newdata->wrapped, &d->wrapped) < 0) { + _n_to_n_data_free((NpyAuxData *)newdata); } return (NpyAuxData *)newdata; } -static void -_strided_to_strided_n_to_n(char *dst, npy_intp dst_stride, - char *src, npy_intp src_stride, - npy_intp N, npy_intp src_itemsize, - NpyAuxData *data) +static int +_strided_to_strided_1_to_1( + PyArrayMethod_Context *NPY_UNUSED(context), char *const *args, + const npy_intp *dimensions, const npy_intp *strides, + NpyAuxData *auxdata) { - _n_to_n_data *d = (_n_to_n_data *)data; - PyArray_StridedUnaryOp *subtransfer = d->stransfer; - NpyAuxData *subdata = d->data; - npy_intp subN = d->N, src_subitemsize = d->src_itemsize, - dst_subitemsize = d->dst_itemsize; + _n_to_n_data *d = (_n_to_n_data *)auxdata; + return d->wrapped.func(&d->wrapped.context, + args, dimensions, strides, d->wrapped.auxdata); +} - while (N > 0) { - subtransfer(dst, dst_subitemsize, - src, src_subitemsize, - subN, src_subitemsize, - subdata); +static int +_strided_to_strided_n_to_n( + PyArrayMethod_Context *NPY_UNUSED(context), char *const *args, + const npy_intp *dimensions, const npy_intp *strides, + NpyAuxData *auxdata) +{ + npy_intp N = dimensions[0]; + char *src = args[0], *dst = args[1]; + npy_intp src_stride = strides[0], dst_stride = strides[1]; + + _n_to_n_data *d = (_n_to_n_data *)auxdata; + npy_intp subN = d->N; + while (N > 0) { + char *sub_args[2] = {src, dst}; + if (d->wrapped.func(&d->wrapped.context, + sub_args, &subN, d->strides, d->wrapped.auxdata) < 0) { + return -1; + } src += src_stride; dst += dst_stride; --N; } + return 0; } -static void -_contig_to_contig_n_to_n(char *dst, npy_intp NPY_UNUSED(dst_stride), - char *src, npy_intp NPY_UNUSED(src_stride), - npy_intp N, npy_intp NPY_UNUSED(src_itemsize), - NpyAuxData *data) +static int +_contig_to_contig_n_to_n( + PyArrayMethod_Context *NPY_UNUSED(context), char *const *args, + const npy_intp *dimensions, const npy_intp *NPY_UNUSED(strides), + NpyAuxData *auxdata) { - _n_to_n_data *d = (_n_to_n_data *)data; - PyArray_StridedUnaryOp *subtransfer = d->stransfer; - NpyAuxData *subdata = d->data; - npy_intp subN = d->N, src_subitemsize = d->src_itemsize, - dst_subitemsize = d->dst_itemsize; - - subtransfer(dst, dst_subitemsize, - src, src_subitemsize, - subN*N, src_subitemsize, - subdata); + npy_intp N = dimensions[0]; + char *src = args[0], *dst = args[1]; + + _n_to_n_data *d = (_n_to_n_data *)auxdata; + /* Make one large transfer including both outer and inner iteration: */ + npy_intp subN = N * d->N; + + char *sub_args[2] = {src, dst}; + if (d->wrapped.func(&d->wrapped.context, + sub_args, &subN, d->strides, d->wrapped.auxdata) < 0) { + return -1; + } + return 0; } + /* - * Wraps a transfer function to produce one that copies N contiguous elements - * of src to N contiguous elements of dst. + * Note that this function is currently both used for structured dtype + * casting as well as a decref function (with `dst_dtype == NULL`) */ static int -wrap_transfer_function_n_to_n( - PyArray_StridedUnaryOp *stransfer_inner, - NpyAuxData *data_inner, +get_n_to_n_transfer_function(int aligned, npy_intp src_stride, npy_intp dst_stride, - npy_intp src_itemsize, npy_intp dst_itemsize, + PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype, + int move_references, npy_intp N, - PyArray_StridedUnaryOp **out_stransfer, - NpyAuxData **out_transferdata) + PyArrayMethod_StridedLoop **out_stransfer, + NpyAuxData **out_transferdata, + int *out_needs_api) { - _n_to_n_data *data; - - data = PyArray_malloc(sizeof(_n_to_n_data)); + _n_to_n_data *data = PyMem_Malloc(sizeof(_n_to_n_data)); if (data == NULL) { PyErr_NoMemory(); return NPY_FAIL; } - data->base.free = &_n_to_n_data_free; data->base.clone = &_n_to_n_data_clone; - data->stransfer = stransfer_inner; - data->data = data_inner; data->N = N; - data->src_itemsize = src_itemsize; - data->dst_itemsize = dst_itemsize; + + if (N != 1) { + /* + * If N == 1, we can use the original strides, + * otherwise fields are contiguous + */ + src_stride = src_dtype->elsize; + dst_stride = dst_dtype != NULL ? dst_dtype->elsize : 0; + /* Store the wrapped strides for easier access */ + data->strides[0] = src_stride; + data->strides[1] = dst_stride; + } /* - * If the N subarray elements exactly fit in the strides, - * then can do a faster contiguous transfer. + * src_stride and dst_stride are set to contiguous, because + * subarrays are always contiguous. */ - if (src_stride == N * src_itemsize && - dst_stride == N * dst_itemsize) { + if (PyArray_GetDTypeTransferFunction(aligned, + src_stride, dst_stride, + src_dtype, dst_dtype, + move_references, + &data->wrapped, + out_needs_api) != NPY_SUCCEED) { + NPY_AUXDATA_FREE((NpyAuxData *)data); + return NPY_FAIL; + } + + if (N == 1) { + /* + * No need for wrapping, we can just copy directly. In principle + * this step could be optimized away entirely, but it requires + * replacing the context (to have the unpacked dtypes). + */ + *out_stransfer = &_strided_to_strided_1_to_1; + } + else if (src_stride == N * src_stride && + dst_stride == N * dst_stride) { + /* The subarrays can be coalesced (probably very rarely) */ *out_stransfer = &_contig_to_contig_n_to_n; } else { @@ -1926,45 +1725,6 @@ wrap_transfer_function_n_to_n( return NPY_SUCCEED; } -static int -get_n_to_n_transfer_function(int aligned, - npy_intp src_stride, npy_intp dst_stride, - PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype, - int move_references, - npy_intp N, - PyArray_StridedUnaryOp **out_stransfer, - NpyAuxData **out_transferdata, - int *out_needs_api) -{ - PyArray_StridedUnaryOp *stransfer; - NpyAuxData *data; - - /* - * src_stride and dst_stride are set to contiguous, because - * subarrays are always contiguous. - */ - if (PyArray_GetDTypeTransferFunction(aligned, - src_dtype->elsize, dst_dtype->elsize, - src_dtype, dst_dtype, - move_references, - &stransfer, &data, - out_needs_api) != NPY_SUCCEED) { - return NPY_FAIL; - } - - if (wrap_transfer_function_n_to_n(stransfer, data, - src_stride, dst_stride, - src_dtype->elsize, dst_dtype->elsize, - N, - out_stransfer, - out_transferdata) != NPY_SUCCEED) { - NPY_AUXDATA_FREE(data); - return NPY_FAIL; - } - - return NPY_SUCCEED; -} - /********************** COPY WITH SUBARRAY BROADCAST ************************/ typedef struct { @@ -1974,65 +1734,62 @@ typedef struct { /* Copies element with subarray broadcasting */ typedef struct { NpyAuxData base; - PyArray_StridedUnaryOp *stransfer; - NpyAuxData *data; - npy_intp src_N, dst_N, src_itemsize, dst_itemsize; - PyArray_StridedUnaryOp *stransfer_decsrcref; - NpyAuxData *data_decsrcref; - PyArray_StridedUnaryOp *stransfer_decdstref; - NpyAuxData *data_decdstref; + NPY_cast_info wrapped; + NPY_cast_info decref_src; + NPY_cast_info decref_dst; /* The use-case should probably be deprecated */ + npy_intp src_N, dst_N; /* This gets a run-length encoded representation of the transfer */ npy_intp run_count; - _subarray_broadcast_offsetrun offsetruns; + _subarray_broadcast_offsetrun offsetruns[]; } _subarray_broadcast_data; + /* transfer data free function */ static void _subarray_broadcast_data_free(NpyAuxData *data) { _subarray_broadcast_data *d = (_subarray_broadcast_data *)data; - NPY_AUXDATA_FREE(d->data); - NPY_AUXDATA_FREE(d->data_decsrcref); - NPY_AUXDATA_FREE(d->data_decdstref); - PyArray_free(data); + NPY_cast_info_xfree(&d->wrapped); + NPY_cast_info_xfree(&d->decref_src); + NPY_cast_info_xfree(&d->decref_dst); + PyMem_Free(data); } /* transfer data copy function */ -static NpyAuxData *_subarray_broadcast_data_clone( NpyAuxData *data) +static NpyAuxData *_subarray_broadcast_data_clone(NpyAuxData *data) { _subarray_broadcast_data *d = (_subarray_broadcast_data *)data; - _subarray_broadcast_data *newdata; - npy_intp run_count = d->run_count, structsize; - structsize = sizeof(_subarray_broadcast_data) + - run_count*sizeof(_subarray_broadcast_offsetrun); + npy_intp offsetruns_size = d->run_count*sizeof(_subarray_broadcast_offsetrun); + npy_intp structsize = sizeof(_subarray_broadcast_data) + offsetruns_size; /* Allocate the data and populate it */ - newdata = (_subarray_broadcast_data *)PyArray_malloc(structsize); + _subarray_broadcast_data *newdata = PyMem_Malloc(structsize); if (newdata == NULL) { return NULL; } - memcpy(newdata, data, structsize); - if (d->data != NULL) { - newdata->data = NPY_AUXDATA_CLONE(d->data); - if (newdata->data == NULL) { - PyArray_free(newdata); - return NULL; - } + newdata->base.free = &_subarray_broadcast_data_free; + newdata->base.clone = &_subarray_broadcast_data_clone; + newdata->src_N = d->src_N; + newdata->dst_N = d->dst_N; + newdata->run_count = d->run_count; + memcpy(newdata->offsetruns, d->offsetruns, offsetruns_size); + + NPY_cast_info_init(&newdata->decref_src); + NPY_cast_info_init(&newdata->decref_dst); + + if (NPY_cast_info_copy(&newdata->wrapped, &d->wrapped) < 0) { + _subarray_broadcast_data_free((NpyAuxData *)newdata); + return NULL; } - if (d->data_decsrcref != NULL) { - newdata->data_decsrcref = NPY_AUXDATA_CLONE(d->data_decsrcref); - if (newdata->data_decsrcref == NULL) { - NPY_AUXDATA_FREE(newdata->data); - PyArray_free(newdata); + if (d->decref_src.func != NULL) { + if (NPY_cast_info_copy(&newdata->decref_src, &d->decref_src) < 0) { + _subarray_broadcast_data_free((NpyAuxData *) newdata); return NULL; } } - if (d->data_decdstref != NULL) { - newdata->data_decdstref = NPY_AUXDATA_CLONE(d->data_decdstref); - if (newdata->data_decdstref == NULL) { - NPY_AUXDATA_FREE(newdata->data); - NPY_AUXDATA_FREE(newdata->data_decsrcref); - PyArray_free(newdata); + if (d->decref_dst.func != NULL) { + if (NPY_cast_info_copy(&newdata->decref_dst, &d->decref_dst) < 0) { + _subarray_broadcast_data_free((NpyAuxData *) newdata); return NULL; } } @@ -2040,33 +1797,37 @@ static NpyAuxData *_subarray_broadcast_data_clone( NpyAuxData *data) return (NpyAuxData *)newdata; } -static void -_strided_to_strided_subarray_broadcast(char *dst, npy_intp dst_stride, - char *src, npy_intp src_stride, - npy_intp N, npy_intp NPY_UNUSED(src_itemsize), - NpyAuxData *data) +static int +_strided_to_strided_subarray_broadcast( + PyArrayMethod_Context *NPY_UNUSED(context), char *const *args, + const npy_intp *dimensions, const npy_intp *strides, + NpyAuxData *auxdata) { - _subarray_broadcast_data *d = (_subarray_broadcast_data *)data; - PyArray_StridedUnaryOp *subtransfer = d->stransfer; - NpyAuxData *subdata = d->data; - npy_intp run, run_count = d->run_count, - src_subitemsize = d->src_itemsize, - dst_subitemsize = d->dst_itemsize; + npy_intp N = dimensions[0]; + char *src = args[0], *dst = args[1]; + npy_intp src_stride = strides[0], dst_stride = strides[1]; + + _subarray_broadcast_data *d = (_subarray_broadcast_data *)auxdata; + npy_intp run, run_count = d->run_count; npy_intp loop_index, offset, count; - char *dst_ptr; - _subarray_broadcast_offsetrun *offsetruns = &d->offsetruns; + + npy_intp src_subitemsize = d->wrapped.descriptors[0]->elsize; + npy_intp dst_subitemsize = d->wrapped.descriptors[1]->elsize; + + npy_intp sub_strides[2] = {src_subitemsize, dst_subitemsize}; while (N > 0) { loop_index = 0; for (run = 0; run < run_count; ++run) { - offset = offsetruns[run].offset; - count = offsetruns[run].count; - dst_ptr = dst + loop_index*dst_subitemsize; + offset = d->offsetruns[run].offset; + count = d->offsetruns[run].count; + char *dst_ptr = dst + loop_index*dst_subitemsize; + char *sub_args[2] = {src + offset, dst_ptr}; if (offset != -1) { - subtransfer(dst_ptr, dst_subitemsize, - src + offset, src_subitemsize, - count, src_subitemsize, - subdata); + if (d->wrapped.func(&d->wrapped.context, + sub_args, &count, sub_strides, d->wrapped.auxdata) < 0) { + return -1; + } } else { memset(dst_ptr, 0, count*dst_subitemsize); @@ -2078,63 +1839,68 @@ _strided_to_strided_subarray_broadcast(char *dst, npy_intp dst_stride, dst += dst_stride; --N; } + return 0; } -static void -_strided_to_strided_subarray_broadcast_withrefs(char *dst, npy_intp dst_stride, - char *src, npy_intp src_stride, - npy_intp N, npy_intp NPY_UNUSED(src_itemsize), - NpyAuxData *data) +static int +_strided_to_strided_subarray_broadcast_withrefs( + PyArrayMethod_Context *NPY_UNUSED(context), char *const *args, + const npy_intp *dimensions, const npy_intp *strides, + NpyAuxData *auxdata) { - _subarray_broadcast_data *d = (_subarray_broadcast_data *)data; - PyArray_StridedUnaryOp *subtransfer = d->stransfer; - NpyAuxData *subdata = d->data; - PyArray_StridedUnaryOp *stransfer_decsrcref = d->stransfer_decsrcref; - NpyAuxData *data_decsrcref = d->data_decsrcref; - PyArray_StridedUnaryOp *stransfer_decdstref = d->stransfer_decdstref; - NpyAuxData *data_decdstref = d->data_decdstref; - npy_intp run, run_count = d->run_count, - src_subitemsize = d->src_itemsize, - dst_subitemsize = d->dst_itemsize, - src_subN = d->src_N; + npy_intp N = dimensions[0]; + char *src = args[0], *dst = args[1]; + npy_intp src_stride = strides[0], dst_stride = strides[1]; + + _subarray_broadcast_data *d = (_subarray_broadcast_data *)auxdata; + npy_intp run, run_count = d->run_count; npy_intp loop_index, offset, count; - char *dst_ptr; - _subarray_broadcast_offsetrun *offsetruns = &d->offsetruns; + + npy_intp src_subitemsize = d->wrapped.descriptors[0]->elsize; + npy_intp dst_subitemsize = d->wrapped.descriptors[1]->elsize; + + npy_intp sub_strides[2] = {src_subitemsize, dst_subitemsize}; while (N > 0) { loop_index = 0; for (run = 0; run < run_count; ++run) { - offset = offsetruns[run].offset; - count = offsetruns[run].count; - dst_ptr = dst + loop_index*dst_subitemsize; + offset = d->offsetruns[run].offset; + count = d->offsetruns[run].count; + char *dst_ptr = dst + loop_index*dst_subitemsize; + char *sub_args[2] = {src + offset, dst_ptr}; if (offset != -1) { - subtransfer(dst_ptr, dst_subitemsize, - src + offset, src_subitemsize, - count, src_subitemsize, - subdata); + if (d->wrapped.func(&d->wrapped.context, + sub_args, &count, sub_strides, d->wrapped.auxdata) < 0) { + return -1; + } } else { - if (stransfer_decdstref != NULL) { - stransfer_decdstref(NULL, 0, dst_ptr, dst_subitemsize, - count, dst_subitemsize, - data_decdstref); + if (d->decref_dst.func != NULL) { + if (d->decref_dst.func(&d->decref_dst.context, + &dst_ptr, &count, &dst_subitemsize, + d->decref_dst.auxdata) < 0) { + return -1; + } } memset(dst_ptr, 0, count*dst_subitemsize); } loop_index += count; } - if (stransfer_decsrcref != NULL) { - stransfer_decsrcref(NULL, 0, src, src_subitemsize, - src_subN, src_subitemsize, - data_decsrcref); + if (d->decref_src.func != NULL) { + if (d->decref_src.func(&d->decref_src.context, + &src, &d->src_N, &src_subitemsize, + d->decref_src.auxdata) < 0) { + return -1; + } } src += src_stride; dst += dst_stride; --N; } + return 0; } @@ -2145,24 +1911,30 @@ get_subarray_broadcast_transfer_function(int aligned, npy_intp src_size, npy_intp dst_size, PyArray_Dims src_shape, PyArray_Dims dst_shape, int move_references, - PyArray_StridedUnaryOp **out_stransfer, + PyArrayMethod_StridedLoop **out_stransfer, NpyAuxData **out_transferdata, int *out_needs_api) { _subarray_broadcast_data *data; npy_intp structsize, loop_index, run, run_size, src_index, dst_index, i, ndim; - _subarray_broadcast_offsetrun *offsetruns; structsize = sizeof(_subarray_broadcast_data) + dst_size*sizeof(_subarray_broadcast_offsetrun); /* Allocate the data and populate it */ - data = (_subarray_broadcast_data *)PyArray_malloc(structsize); + data = (_subarray_broadcast_data *)PyMem_Malloc(structsize); if (data == NULL) { PyErr_NoMemory(); return NPY_FAIL; } + data->base.free = &_subarray_broadcast_data_free; + data->base.clone = &_subarray_broadcast_data_clone; + data->src_N = src_size; + data->dst_N = dst_size; + + NPY_cast_info_init(&data->decref_src); + NPY_cast_info_init(&data->decref_dst); /* * move_references is set to 0, handled in the wrapping transfer fn, @@ -2173,17 +1945,11 @@ get_subarray_broadcast_transfer_function(int aligned, src_dtype->elsize, dst_dtype->elsize, src_dtype, dst_dtype, 0, - &data->stransfer, &data->data, + &data->wrapped, out_needs_api) != NPY_SUCCEED) { - PyArray_free(data); + NPY_AUXDATA_FREE((NpyAuxData *)data); return NPY_FAIL; } - data->base.free = &_subarray_broadcast_data_free; - data->base.clone = &_subarray_broadcast_data_clone; - data->src_N = src_size; - data->dst_N = dst_size; - data->src_itemsize = src_dtype->elsize; - data->dst_itemsize = dst_dtype->elsize; /* If the src object will need a DECREF */ if (move_references && PyDataType_REFCHK(src_dtype)) { @@ -2191,18 +1957,12 @@ get_subarray_broadcast_transfer_function(int aligned, src_dtype->elsize, 0, src_dtype, NULL, 1, - &data->stransfer_decsrcref, - &data->data_decsrcref, + &data->decref_src, out_needs_api) != NPY_SUCCEED) { - NPY_AUXDATA_FREE(data->data); - PyArray_free(data); + NPY_AUXDATA_FREE((NpyAuxData *)data); return NPY_FAIL; } } - else { - data->stransfer_decsrcref = NULL; - data->data_decsrcref = NULL; - } /* If the dst object needs a DECREF to set it to NULL */ if (PyDataType_REFCHK(dst_dtype)) { @@ -2210,22 +1970,15 @@ get_subarray_broadcast_transfer_function(int aligned, dst_dtype->elsize, 0, dst_dtype, NULL, 1, - &data->stransfer_decdstref, - &data->data_decdstref, + &data->decref_dst, out_needs_api) != NPY_SUCCEED) { - NPY_AUXDATA_FREE(data->data); - NPY_AUXDATA_FREE(data->data_decsrcref); - PyArray_free(data); + NPY_AUXDATA_FREE((NpyAuxData *)data); return NPY_FAIL; } } - else { - data->stransfer_decdstref = NULL; - data->data_decdstref = NULL; - } /* Calculate the broadcasting and set the offsets */ - offsetruns = &data->offsetruns; + _subarray_broadcast_offsetrun *offsetruns = data->offsetruns; ndim = (src_shape.len > dst_shape.len) ? src_shape.len : dst_shape.len; for (loop_index = 0; loop_index < dst_size; ++loop_index) { npy_intp src_factor = 1; @@ -2311,8 +2064,8 @@ get_subarray_broadcast_transfer_function(int aligned, } } - if (data->stransfer_decsrcref == NULL && - data->stransfer_decdstref == NULL) { + if (data->decref_src.func == NULL && + data->decref_dst.func == NULL) { *out_stransfer = &_strided_to_strided_subarray_broadcast; } else { @@ -2327,12 +2080,12 @@ get_subarray_broadcast_transfer_function(int aligned, * Handles subarray transfer. To call this, at least one of the dtype's * subarrays must be non-NULL */ -static int +NPY_NO_EXPORT int get_subarray_transfer_function(int aligned, npy_intp src_stride, npy_intp dst_stride, PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype, int move_references, - PyArray_StridedUnaryOp **out_stransfer, + PyArrayMethod_StridedLoop **out_stransfer, NpyAuxData **out_transferdata, int *out_needs_api) { @@ -2341,7 +2094,7 @@ get_subarray_transfer_function(int aligned, /* Get the subarray shapes and sizes */ if (PyDataType_HASSUBARRAY(src_dtype)) { - if (!(PyArray_IntpConverter(src_dtype->subarray->shape, + if (!(PyArray_IntpConverter(src_dtype->subarray->shape, &src_shape))) { PyErr_SetString(PyExc_ValueError, "invalid subarray shape"); @@ -2351,7 +2104,7 @@ get_subarray_transfer_function(int aligned, src_dtype = src_dtype->subarray->base; } if (PyDataType_HASSUBARRAY(dst_dtype)) { - if (!(PyArray_IntpConverter(dst_dtype->subarray->shape, + if (!(PyArray_IntpConverter(dst_dtype->subarray->shape, &dst_shape))) { npy_free_cache_dim_obj(src_shape); PyErr_SetString(PyExc_ValueError, @@ -2363,46 +2116,36 @@ get_subarray_transfer_function(int aligned, } /* - * Just a straight one-element copy. + * Copy the src value to all the dst values, the size one can be + * special cased for speed. */ - if (dst_size == 1 && src_size == 1) { - npy_free_cache_dim_obj(src_shape); - npy_free_cache_dim_obj(dst_shape); + if ((dst_size == 1 && src_size == 1) || ( + src_shape.len == dst_shape.len && PyArray_CompareLists( + src_shape.ptr, dst_shape.ptr, src_shape.len))) { - return PyArray_GetDTypeTransferFunction(aligned, - src_stride, dst_stride, - src_dtype, dst_dtype, - move_references, - out_stransfer, out_transferdata, - out_needs_api); - } - /* Copy the src value to all the dst values */ - else if (src_size == 1) { npy_free_cache_dim_obj(src_shape); npy_free_cache_dim_obj(dst_shape); - return get_one_to_n_transfer_function(aligned, + return get_n_to_n_transfer_function(aligned, src_stride, dst_stride, src_dtype, dst_dtype, move_references, - dst_size, + src_size, out_stransfer, out_transferdata, out_needs_api); } - /* If the shapes match exactly, do an n to n copy */ - else if (src_shape.len == dst_shape.len && - PyArray_CompareLists(src_shape.ptr, dst_shape.ptr, - src_shape.len)) { + /* Copy the src value to all the dst values */ + else if (src_size == 1) { npy_free_cache_dim_obj(src_shape); npy_free_cache_dim_obj(dst_shape); - return get_n_to_n_transfer_function(aligned, - src_stride, dst_stride, - src_dtype, dst_dtype, - move_references, - src_size, - out_stransfer, out_transferdata, - out_needs_api); + return get_one_to_n_transfer_function(aligned, + src_stride, dst_stride, + src_dtype, dst_dtype, + move_references, + dst_size, + out_stransfer, out_transferdata, + out_needs_api); } /* * Copy the subarray with broadcasting, truncating, and zero-padding @@ -2426,105 +2169,99 @@ get_subarray_transfer_function(int aligned, /**************************** COPY FIELDS *******************************/ typedef struct { - npy_intp src_offset, dst_offset, src_itemsize; - PyArray_StridedUnaryOp *stransfer; - NpyAuxData *data; + npy_intp src_offset, dst_offset; + NPY_cast_info info; } _single_field_transfer; typedef struct { NpyAuxData base; npy_intp field_count; - - _single_field_transfer fields; + _single_field_transfer fields[]; } _field_transfer_data; + /* transfer data free function */ static void _field_transfer_data_free(NpyAuxData *data) { _field_transfer_data *d = (_field_transfer_data *)data; - npy_intp i, field_count; - _single_field_transfer *fields; - - field_count = d->field_count; - fields = &d->fields; - for (i = 0; i < field_count; ++i) { - NPY_AUXDATA_FREE(fields[i].data); + for (npy_intp i = 0; i < d->field_count; ++i) { + NPY_cast_info_xfree(&d->fields[i].info); } - PyArray_free(d); + PyMem_Free(d); } /* transfer data copy function */ static NpyAuxData *_field_transfer_data_clone(NpyAuxData *data) { _field_transfer_data *d = (_field_transfer_data *)data; - _field_transfer_data *newdata; - npy_intp i, field_count = d->field_count, structsize; - _single_field_transfer *fields, *newfields; - structsize = sizeof(_field_transfer_data) + + npy_intp field_count = d->field_count; + npy_intp structsize = sizeof(_field_transfer_data) + field_count * sizeof(_single_field_transfer); /* Allocate the data and populate it */ - newdata = (_field_transfer_data *)PyArray_malloc(structsize); + _field_transfer_data *newdata = PyMem_Malloc(structsize); if (newdata == NULL) { return NULL; } - memcpy(newdata, d, structsize); + newdata->base = d->base; + newdata->field_count = 0; + /* Copy all the fields transfer data */ - fields = &d->fields; - newfields = &newdata->fields; - for (i = 0; i < field_count; ++i) { - if (fields[i].data != NULL) { - newfields[i].data = NPY_AUXDATA_CLONE(fields[i].data); - if (newfields[i].data == NULL) { - for (i = i-1; i >= 0; --i) { - NPY_AUXDATA_FREE(newfields[i].data); - } - PyArray_free(newdata); - return NULL; - } + for (npy_intp i = 0; i < field_count; ++i) { + if (NPY_cast_info_copy(&newdata->fields[i].info, &d->fields[i].info) < 0) { + NPY_AUXDATA_FREE((NpyAuxData *)newdata); + return NULL; } - + newdata->fields[i].src_offset = d->fields[i].src_offset; + newdata->fields[i].dst_offset = d->fields[i].dst_offset; + newdata->field_count++; } return (NpyAuxData *)newdata; } -static void -_strided_to_strided_field_transfer(char *dst, npy_intp dst_stride, - char *src, npy_intp src_stride, - npy_intp N, npy_intp NPY_UNUSED(src_itemsize), - NpyAuxData *data) + +static int +_strided_to_strided_field_transfer( + PyArrayMethod_Context *NPY_UNUSED(context), char *const *args, + const npy_intp *dimensions, const npy_intp *strides, + NpyAuxData *auxdata) { - _field_transfer_data *d = (_field_transfer_data *)data; + npy_intp N = dimensions[0]; + char *src = args[0], *dst = args[1]; + npy_intp src_stride = strides[0], dst_stride = strides[1]; + + _field_transfer_data *d = (_field_transfer_data *)auxdata; npy_intp i, field_count = d->field_count; - _single_field_transfer *field; + const npy_intp blocksize = NPY_LOWLEVEL_BUFFER_BLOCKSIZE; /* Do the transfer a block at a time */ for (;;) { - field = &d->fields; - if (N > NPY_LOWLEVEL_BUFFER_BLOCKSIZE) { - for (i = 0; i < field_count; ++i, ++field) { - field->stransfer(dst + field->dst_offset, dst_stride, - src + field->src_offset, src_stride, - NPY_LOWLEVEL_BUFFER_BLOCKSIZE, - field->src_itemsize, - field->data); + if (N > blocksize) { + for (i = 0; i < field_count; ++i) { + _single_field_transfer field = d->fields[i]; + char *fargs[2] = {src + field.src_offset, dst + field.dst_offset}; + if (field.info.func(&field.info.context, + fargs, &blocksize, strides, field.info.auxdata) < 0) { + return -1; + } } N -= NPY_LOWLEVEL_BUFFER_BLOCKSIZE; src += NPY_LOWLEVEL_BUFFER_BLOCKSIZE*src_stride; dst += NPY_LOWLEVEL_BUFFER_BLOCKSIZE*dst_stride; } else { - for (i = 0; i < field_count; ++i, ++field) { - field->stransfer(dst + field->dst_offset, dst_stride, - src + field->src_offset, src_stride, - N, - field->src_itemsize, - field->data); + for (i = 0; i < field_count; ++i) { + _single_field_transfer field = d->fields[i]; + char *fargs[2] = {src + field.src_offset, dst + field.dst_offset}; + if (field.info.func(&field.info.context, + fargs, &N, strides, field.info.auxdata) < 0) { + return -1; + } } - return; + return 0; } } } @@ -2533,22 +2270,22 @@ _strided_to_strided_field_transfer(char *dst, npy_intp dst_stride, * Handles fields transfer. To call this, at least one of the dtypes * must have fields. Does not take care of object<->structure conversion */ -static int -get_fields_transfer_function(int aligned, +NPY_NO_EXPORT int +get_fields_transfer_function(int NPY_UNUSED(aligned), npy_intp src_stride, npy_intp dst_stride, PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype, int move_references, - PyArray_StridedUnaryOp **out_stransfer, + PyArrayMethod_StridedLoop **out_stransfer, NpyAuxData **out_transferdata, int *out_needs_api) { PyObject *key, *tup, *title; PyArray_Descr *src_fld_dtype, *dst_fld_dtype; - npy_int i, field_count, structsize; + npy_int i; + size_t structsize; + Py_ssize_t field_count; int src_offset, dst_offset; _field_transfer_data *data; - _single_field_transfer *fields; - int failed = 0; /* * There are three cases to take care of: 1. src is non-structured, @@ -2562,64 +2299,55 @@ get_fields_transfer_function(int aligned, /* Allocate the field-data structure and populate it */ structsize = sizeof(_field_transfer_data) + (field_count + 1) * sizeof(_single_field_transfer); - data = (_field_transfer_data *)PyArray_malloc(structsize); + data = PyMem_Malloc(structsize); if (data == NULL) { PyErr_NoMemory(); return NPY_FAIL; } data->base.free = &_field_transfer_data_free; data->base.clone = &_field_transfer_data_clone; - fields = &data->fields; + data->field_count = 0; for (i = 0; i < field_count; ++i) { key = PyTuple_GET_ITEM(dst_dtype->names, i); tup = PyDict_GetItem(dst_dtype->fields, key); if (!PyArg_ParseTuple(tup, "Oi|O", &dst_fld_dtype, &dst_offset, &title)) { - PyArray_free(data); + PyMem_Free(data); return NPY_FAIL; } if (PyArray_GetDTypeTransferFunction(0, src_stride, dst_stride, src_dtype, dst_fld_dtype, 0, - &fields[i].stransfer, - &fields[i].data, + &data->fields[i].info, out_needs_api) != NPY_SUCCEED) { - for (i = i-1; i >= 0; --i) { - NPY_AUXDATA_FREE(fields[i].data); - } - PyArray_free(data); + NPY_AUXDATA_FREE((NpyAuxData *)data); return NPY_FAIL; } - fields[i].src_offset = 0; - fields[i].dst_offset = dst_offset; - fields[i].src_itemsize = src_dtype->elsize; + data->fields[i].src_offset = 0; + data->fields[i].dst_offset = dst_offset; + data->field_count++; } /* - * If references should be decrefd in src, add - * another transfer function to do that. + * If references should be decrefd in src, add another transfer + * function to do that. Since a decref function only uses a single + * input, the second one (normally output) just does not matter here. */ if (move_references && PyDataType_REFCHK(src_dtype)) { - if (get_decsrcref_transfer_function(0, + if (get_decref_transfer_function(0, src_stride, src_dtype, - &fields[field_count].stransfer, - &fields[field_count].data, + &data->fields[field_count].info, out_needs_api) != NPY_SUCCEED) { - for (i = 0; i < field_count; ++i) { - NPY_AUXDATA_FREE(fields[i].data); - } - PyArray_free(data); + NPY_AUXDATA_FREE((NpyAuxData *)data); return NPY_FAIL; } - fields[field_count].src_offset = 0; - fields[field_count].dst_offset = 0; - fields[field_count].src_itemsize = src_dtype->elsize; - field_count++; + data->fields[field_count].src_offset = 0; + data->fields[field_count].dst_offset = 0; + data->field_count = field_count; } - data->field_count = field_count; *out_stransfer = &_strided_to_strided_field_transfer; *out_transferdata = (NpyAuxData *)data; @@ -2639,19 +2367,19 @@ get_fields_transfer_function(int aligned, /* Allocate the field-data structure and populate it */ structsize = sizeof(_field_transfer_data) + 1 * sizeof(_single_field_transfer); - data = (_field_transfer_data *)PyArray_malloc(structsize); + data = PyMem_Malloc(structsize); if (data == NULL) { PyErr_NoMemory(); return NPY_FAIL; } data->base.free = &_field_transfer_data_free; data->base.clone = &_field_transfer_data_clone; - fields = &data->fields; key = PyTuple_GET_ITEM(src_dtype->names, 0); tup = PyDict_GetItem(src_dtype->fields, key); if (!PyArg_ParseTuple(tup, "Oi|O", &src_fld_dtype, &src_offset, &title)) { + PyMem_Free(data); return NPY_FAIL; } @@ -2659,16 +2387,13 @@ get_fields_transfer_function(int aligned, src_stride, dst_stride, src_fld_dtype, dst_dtype, move_references, - &fields[0].stransfer, - &fields[0].data, + &data->fields[0].info, out_needs_api) != NPY_SUCCEED) { - PyArray_free(data); + PyMem_Free(data); return NPY_FAIL; } - fields[0].src_offset = src_offset; - fields[0].dst_offset = 0; - fields[0].src_itemsize = src_fld_dtype->elsize; - + data->fields[0].src_offset = src_offset; + data->fields[0].dst_offset = 0; data->field_count = 1; *out_stransfer = &_strided_to_strided_field_transfer; @@ -2689,14 +2414,14 @@ get_fields_transfer_function(int aligned, /* Allocate the field-data structure and populate it */ structsize = sizeof(_field_transfer_data) + field_count * sizeof(_single_field_transfer); - data = (_field_transfer_data *)PyArray_malloc(structsize); + data = PyMem_Malloc(structsize); if (data == NULL) { PyErr_NoMemory(); return NPY_FAIL; } data->base.free = &_field_transfer_data_free; data->base.clone = &_field_transfer_data_clone; - fields = &data->fields; + data->field_count = 0; /* set up the transfer function for each field */ for (i = 0; i < field_count; ++i) { @@ -2704,42 +2429,31 @@ get_fields_transfer_function(int aligned, tup = PyDict_GetItem(dst_dtype->fields, key); if (!PyArg_ParseTuple(tup, "Oi|O", &dst_fld_dtype, &dst_offset, &title)) { - failed = 1; - break; + NPY_AUXDATA_FREE((NpyAuxData *)data); + return NPY_FAIL; } key = PyTuple_GET_ITEM(src_dtype->names, i); tup = PyDict_GetItem(src_dtype->fields, key); if (!PyArg_ParseTuple(tup, "Oi|O", &src_fld_dtype, &src_offset, &title)) { - failed = 1; - break; + NPY_AUXDATA_FREE((NpyAuxData *)data); + return NPY_FAIL; } if (PyArray_GetDTypeTransferFunction(0, src_stride, dst_stride, src_fld_dtype, dst_fld_dtype, move_references, - &fields[i].stransfer, - &fields[i].data, + &data->fields[i].info, out_needs_api) != NPY_SUCCEED) { - failed = 1; - break; - } - fields[i].src_offset = src_offset; - fields[i].dst_offset = dst_offset; - fields[i].src_itemsize = src_fld_dtype->elsize; - } - - if (failed) { - for (i = i-1; i >= 0; --i) { - NPY_AUXDATA_FREE(fields[i].data); + NPY_AUXDATA_FREE((NpyAuxData *)data); + return NPY_FAIL; } - PyArray_free(data); - return NPY_FAIL; + data->fields[i].src_offset = src_offset; + data->fields[i].dst_offset = dst_offset; + data->field_count++; } - data->field_count = field_count; - *out_stransfer = &_strided_to_strided_field_transfer; *out_transferdata = (NpyAuxData *)data; @@ -2747,190 +2461,110 @@ get_fields_transfer_function(int aligned, } static int -get_decsrcref_fields_transfer_function(int aligned, +get_decref_fields_transfer_function(int NPY_UNUSED(aligned), npy_intp src_stride, PyArray_Descr *src_dtype, - PyArray_StridedUnaryOp **out_stransfer, + PyArrayMethod_StridedLoop **out_stransfer, NpyAuxData **out_transferdata, int *out_needs_api) { PyObject *names, *key, *tup, *title; PyArray_Descr *src_fld_dtype; - npy_int i, names_size, field_count, structsize; + npy_int i, structsize; + Py_ssize_t field_count; int src_offset; - _field_transfer_data *data; - _single_field_transfer *fields; names = src_dtype->names; - names_size = PyTuple_GET_SIZE(src_dtype->names); + field_count = PyTuple_GET_SIZE(src_dtype->names); - field_count = names_size; + /* Over-allocating here: less fields may be used */ structsize = sizeof(_field_transfer_data) + field_count * sizeof(_single_field_transfer); /* Allocate the data and populate it */ - data = (_field_transfer_data *)PyArray_malloc(structsize); + _field_transfer_data *data = PyMem_Malloc(structsize); if (data == NULL) { PyErr_NoMemory(); return NPY_FAIL; } data->base.free = &_field_transfer_data_free; data->base.clone = &_field_transfer_data_clone; - fields = &data->fields; + data->field_count = 0; - field_count = 0; - for (i = 0; i < names_size; ++i) { + _single_field_transfer *field = data->fields; + for (i = 0; i < field_count; ++i) { key = PyTuple_GET_ITEM(names, i); tup = PyDict_GetItem(src_dtype->fields, key); if (!PyArg_ParseTuple(tup, "Oi|O", &src_fld_dtype, &src_offset, &title)) { - PyArray_free(data); + NPY_AUXDATA_FREE((NpyAuxData *)data); return NPY_FAIL; } if (PyDataType_REFCHK(src_fld_dtype)) { if (out_needs_api) { *out_needs_api = 1; } - if (get_decsrcref_transfer_function(0, + if (get_decref_transfer_function(0, src_stride, src_fld_dtype, - &fields[field_count].stransfer, - &fields[field_count].data, + &field->info, out_needs_api) != NPY_SUCCEED) { - for (i = field_count-1; i >= 0; --i) { - NPY_AUXDATA_FREE(fields[i].data); - } - PyArray_free(data); + NPY_AUXDATA_FREE((NpyAuxData *)data); return NPY_FAIL; } - fields[field_count].src_offset = src_offset; - fields[field_count].dst_offset = 0; - fields[field_count].src_itemsize = src_dtype->elsize; - field_count++; + field->src_offset = src_offset; + data->field_count++; + field++; } } - data->field_count = field_count; - *out_stransfer = &_strided_to_strided_field_transfer; *out_transferdata = (NpyAuxData *)data; return NPY_SUCCEED; } -static int -get_setdestzero_fields_transfer_function(int aligned, - npy_intp dst_stride, - PyArray_Descr *dst_dtype, - PyArray_StridedUnaryOp **out_stransfer, - NpyAuxData **out_transferdata, - int *out_needs_api) -{ - PyObject *names, *key, *tup, *title; - PyArray_Descr *dst_fld_dtype; - npy_int i, names_size, field_count, structsize; - int dst_offset; - _field_transfer_data *data; - _single_field_transfer *fields; - - names = dst_dtype->names; - names_size = PyTuple_GET_SIZE(dst_dtype->names); - - field_count = names_size; - structsize = sizeof(_field_transfer_data) + - field_count * sizeof(_single_field_transfer); - /* Allocate the data and populate it */ - data = (_field_transfer_data *)PyArray_malloc(structsize); - if (data == NULL) { - PyErr_NoMemory(); - return NPY_FAIL; - } - data->base.free = &_field_transfer_data_free; - data->base.clone = &_field_transfer_data_clone; - fields = &data->fields; - - for (i = 0; i < names_size; ++i) { - key = PyTuple_GET_ITEM(names, i); - tup = PyDict_GetItem(dst_dtype->fields, key); - if (!PyArg_ParseTuple(tup, "Oi|O", &dst_fld_dtype, - &dst_offset, &title)) { - PyArray_free(data); - return NPY_FAIL; - } - if (get_setdstzero_transfer_function(0, - dst_stride, - dst_fld_dtype, - &fields[i].stransfer, - &fields[i].data, - out_needs_api) != NPY_SUCCEED) { - for (i = i-1; i >= 0; --i) { - NPY_AUXDATA_FREE(fields[i].data); - } - PyArray_free(data); - return NPY_FAIL; - } - fields[i].src_offset = 0; - fields[i].dst_offset = dst_offset; - fields[i].src_itemsize = 0; - } - - data->field_count = field_count; - - *out_stransfer = &_strided_to_strided_field_transfer; - *out_transferdata = (NpyAuxData *)data; - - return NPY_SUCCEED; -} /************************* MASKED TRANSFER WRAPPER *************************/ typedef struct { NpyAuxData base; - /* The transfer function being wrapped */ - PyArray_StridedUnaryOp *stransfer; - NpyAuxData *transferdata; - + /* The transfer function being wrapped (could likely be stored directly) */ + NPY_cast_info wrapped; /* The src decref function if necessary */ - PyArray_StridedUnaryOp *decsrcref_stransfer; - NpyAuxData *decsrcref_transferdata; + NPY_cast_info decref_src; } _masked_wrapper_transfer_data; /* transfer data free function */ -static void _masked_wrapper_transfer_data_free(NpyAuxData *data) +static void +_masked_wrapper_transfer_data_free(NpyAuxData *data) { _masked_wrapper_transfer_data *d = (_masked_wrapper_transfer_data *)data; - NPY_AUXDATA_FREE(d->transferdata); - NPY_AUXDATA_FREE(d->decsrcref_transferdata); - PyArray_free(data); + NPY_cast_info_xfree(&d->wrapped); + NPY_cast_info_xfree(&d->decref_src); + PyMem_Free(data); } /* transfer data copy function */ -static NpyAuxData *_masked_wrapper_transfer_data_clone(NpyAuxData *data) +static NpyAuxData * +_masked_wrapper_transfer_data_clone(NpyAuxData *data) { _masked_wrapper_transfer_data *d = (_masked_wrapper_transfer_data *)data; _masked_wrapper_transfer_data *newdata; /* Allocate the data and populate it */ - newdata = (_masked_wrapper_transfer_data *)PyArray_malloc( - sizeof(_masked_wrapper_transfer_data)); + newdata = PyMem_Malloc(sizeof(*newdata)); if (newdata == NULL) { return NULL; } - memcpy(newdata, d, sizeof(_masked_wrapper_transfer_data)); + newdata->base = d->base; - /* Clone all the owned auxdata as well */ - if (newdata->transferdata != NULL) { - newdata->transferdata = NPY_AUXDATA_CLONE(newdata->transferdata); - if (newdata->transferdata == NULL) { - PyArray_free(newdata); - return NULL; - } + if (NPY_cast_info_copy(&newdata->wrapped, &d->wrapped) < 0) { + PyMem_Free(newdata); + return NULL; } - if (newdata->decsrcref_transferdata != NULL) { - newdata->decsrcref_transferdata = - NPY_AUXDATA_CLONE(newdata->decsrcref_transferdata); - if (newdata->decsrcref_transferdata == NULL) { - NPY_AUXDATA_FREE(newdata->transferdata); - PyArray_free(newdata); + if (d->decref_src.func != NULL) { + if (NPY_cast_info_copy(&newdata->decref_src, &d->decref_src) < 0) { + NPY_AUXDATA_FREE((NpyAuxData *)newdata); return NULL; } } @@ -2938,60 +2572,63 @@ static NpyAuxData *_masked_wrapper_transfer_data_clone(NpyAuxData *data) return (NpyAuxData *)newdata; } -static void _strided_masked_wrapper_decsrcref_transfer_function( - char *dst, npy_intp dst_stride, - char *src, npy_intp src_stride, - npy_bool *mask, npy_intp mask_stride, - npy_intp N, npy_intp src_itemsize, - NpyAuxData *transferdata) +static int +_strided_masked_wrapper_decref_transfer_function( + PyArrayMethod_Context *NPY_UNUSED(context), char *const *args, + const npy_intp *dimensions, const npy_intp *strides, + npy_bool *mask, npy_intp mask_stride, + NpyAuxData *auxdata) { - _masked_wrapper_transfer_data *d = - (_masked_wrapper_transfer_data *)transferdata; - npy_intp subloopsize; - PyArray_StridedUnaryOp *unmasked_stransfer, *decsrcref_stransfer; - NpyAuxData *unmasked_transferdata, *decsrcref_transferdata; + npy_intp N = dimensions[0]; + char *src = args[0], *dst = args[1]; + npy_intp src_stride = strides[0], dst_stride = strides[1]; - unmasked_stransfer = d->stransfer; - unmasked_transferdata = d->transferdata; - decsrcref_stransfer = d->decsrcref_stransfer; - decsrcref_transferdata = d->decsrcref_transferdata; + _masked_wrapper_transfer_data *d = (_masked_wrapper_transfer_data *)auxdata; + npy_intp subloopsize; while (N > 0) { - /* Skip masked values, still calling decsrcref for move_references */ + /* Skip masked values, still calling decref for move_references */ mask = (npy_bool*)npy_memchr((char *)mask, 0, mask_stride, N, &subloopsize, 1); - decsrcref_stransfer(NULL, 0, src, src_stride, - subloopsize, src_itemsize, decsrcref_transferdata); + if (d->decref_src.func(&d->decref_src.context, + &src, &subloopsize, &src_stride, d->decref_src.auxdata) < 0) { + return -1; + } dst += subloopsize * dst_stride; src += subloopsize * src_stride; N -= subloopsize; + if (N <= 0) { + break; + } + /* Process unmasked values */ mask = (npy_bool*)npy_memchr((char *)mask, 0, mask_stride, N, &subloopsize, 0); - unmasked_stransfer(dst, dst_stride, src, src_stride, - subloopsize, src_itemsize, unmasked_transferdata); + char *wrapped_args[2] = {src, dst}; + if (d->wrapped.func(&d->wrapped.context, + wrapped_args, &subloopsize, strides, d->wrapped.auxdata) < 0) { + return -1; + } dst += subloopsize * dst_stride; src += subloopsize * src_stride; N -= subloopsize; } + return 0; } -static void _strided_masked_wrapper_transfer_function( - char *dst, npy_intp dst_stride, - char *src, npy_intp src_stride, - npy_bool *mask, npy_intp mask_stride, - npy_intp N, npy_intp src_itemsize, - NpyAuxData *transferdata) +static int +_strided_masked_wrapper_transfer_function( + PyArrayMethod_Context *NPY_UNUSED(context), char *const *args, + const npy_intp *dimensions, const npy_intp *strides, + npy_bool *mask, npy_intp mask_stride, + NpyAuxData *auxdata) { + npy_intp N = dimensions[0]; + char *src = args[0], *dst = args[1]; + npy_intp src_stride = strides[0], dst_stride = strides[1]; - _masked_wrapper_transfer_data *d = - (_masked_wrapper_transfer_data *)transferdata; + _masked_wrapper_transfer_data *d = (_masked_wrapper_transfer_data *)auxdata; npy_intp subloopsize; - PyArray_StridedUnaryOp *unmasked_stransfer; - NpyAuxData *unmasked_transferdata; - - unmasked_stransfer = d->stransfer; - unmasked_transferdata = d->transferdata; while (N > 0) { /* Skip masked values */ @@ -3000,646 +2637,841 @@ static void _strided_masked_wrapper_transfer_function( dst += subloopsize * dst_stride; src += subloopsize * src_stride; N -= subloopsize; + if (N <= 0) { + break; + } + /* Process unmasked values */ mask = (npy_bool*)npy_memchr((char *)mask, 0, mask_stride, N, &subloopsize, 0); - unmasked_stransfer(dst, dst_stride, src, src_stride, - subloopsize, src_itemsize, unmasked_transferdata); + char *wrapped_args[2] = {src, dst}; + if (d->wrapped.func(&d->wrapped.context, + wrapped_args, &subloopsize, strides, d->wrapped.auxdata) < 0) { + return -1; + } dst += subloopsize * dst_stride; src += subloopsize * src_stride; N -= subloopsize; } + return 0; } -/************************* DEST BOOL SETONE *******************************/ +/*************************** CLEAR SRC *******************************/ -static void -_null_to_strided_set_bool_one(char *dst, - npy_intp dst_stride, - char *NPY_UNUSED(src), npy_intp NPY_UNUSED(src_stride), - npy_intp N, npy_intp NPY_UNUSED(src_itemsize), - NpyAuxData *NPY_UNUSED(data)) +static int +_dec_src_ref_nop( + PyArrayMethod_Context *NPY_UNUSED(context), + char *const *NPY_UNUSED(args), const npy_intp *NPY_UNUSED(dimensions), + const npy_intp *NPY_UNUSED(strides), NpyAuxData *NPY_UNUSED(auxdata)) +{ + /* NOP */ + return 0; +} + +static int +_strided_to_null_dec_src_ref_reference( + PyArrayMethod_Context *NPY_UNUSED(context), + char *const *args, const npy_intp *dimensions, + const npy_intp *strides, NpyAuxData *NPY_UNUSED(auxdata)) { - /* bool type is one byte, so can just use the char */ + char *src = args[0]; + npy_intp N = dimensions[0]; + npy_intp stride = strides[0]; + PyObject *src_ref = NULL; while (N > 0) { - *dst = 1; + /* Release the reference in src and set it to NULL */ + NPY_DT_DBG_REFTRACE("dec src ref (null dst)", src_ref); + memcpy(&src_ref, src, sizeof(src_ref)); + Py_XDECREF(src_ref); + memset(src, 0, sizeof(PyObject *)); - dst += dst_stride; + src += stride; --N; } + return 0; } -static void -_null_to_contig_set_bool_one(char *dst, - npy_intp NPY_UNUSED(dst_stride), - char *NPY_UNUSED(src), npy_intp NPY_UNUSED(src_stride), - npy_intp N, npy_intp NPY_UNUSED(src_itemsize), - NpyAuxData *NPY_UNUSED(data)) -{ - /* bool type is one byte, so can just use the char */ - - memset(dst, 1, N); -} -/* Only for the bool type, sets the destination to 1 */ -NPY_NO_EXPORT int -get_bool_setdstone_transfer_function(npy_intp dst_stride, - PyArray_StridedUnaryOp **out_stransfer, - NpyAuxData **out_transferdata, - int *NPY_UNUSED(out_needs_api)) +/* + * Get a function to decref. Currently, this uses a cast info slot, which + * means that the second (destination) descriptor is always set to NULL + * and generally does not have to be passed. + * Since we do not currently have an `ArrayMethod` representing this, the + * method is also set to NULL. + * + * TODO: this function should probably be moved onto the DType eventually, + * which would allow for user DTypes to include dynamic allocated + * memory or Python objects. + */ +static int +get_decref_transfer_function(int aligned, + npy_intp src_stride, + PyArray_Descr *src_dtype, + NPY_cast_info *cast_info, + int *out_needs_api) { - if (dst_stride == 1) { - *out_stransfer = &_null_to_contig_set_bool_one; - } - else { - *out_stransfer = &_null_to_strided_set_bool_one; - } - *out_transferdata = NULL; - - return NPY_SUCCEED; -} + NPY_cast_info_init(cast_info); -/*************************** DEST SETZERO *******************************/ - -/* Sets dest to zero */ -typedef struct { - NpyAuxData base; - npy_intp dst_itemsize; -} _dst_memset_zero_data; + /* If there are no references, it's a nop */ + if (!PyDataType_REFCHK(src_dtype)) { + cast_info->func = &_dec_src_ref_nop; + cast_info->auxdata = NULL; + goto finalize; + } + /* If it's a single reference, it's one decref */ + else if (src_dtype->type_num == NPY_OBJECT) { + if (out_needs_api) { + *out_needs_api = 1; + } -/* zero-padded data copy function */ -static NpyAuxData *_dst_memset_zero_data_clone(NpyAuxData *data) -{ - _dst_memset_zero_data *newdata = - (_dst_memset_zero_data *)PyArray_malloc( - sizeof(_dst_memset_zero_data)); - if (newdata == NULL) { - return NULL; + cast_info->func = &_strided_to_null_dec_src_ref_reference; + cast_info->auxdata = NULL; + goto finalize; } + /* If there are subarrays, need to wrap it */ + else if (PyDataType_HASSUBARRAY(src_dtype)) { + PyArray_Dims src_shape = {NULL, -1}; + npy_intp src_size; - memcpy(newdata, data, sizeof(_dst_memset_zero_data)); + if (out_needs_api) { + *out_needs_api = 1; + } - return (NpyAuxData *)newdata; -} + if (!(PyArray_IntpConverter(src_dtype->subarray->shape, + &src_shape))) { + PyErr_SetString(PyExc_ValueError, + "invalid subarray shape"); + return NPY_FAIL; + } + src_size = PyArray_MultiplyList(src_shape.ptr, src_shape.len); + npy_free_cache_dim_obj(src_shape); -static void -_null_to_strided_memset_zero(char *dst, - npy_intp dst_stride, - char *NPY_UNUSED(src), npy_intp NPY_UNUSED(src_stride), - npy_intp N, npy_intp NPY_UNUSED(src_itemsize), - NpyAuxData *data) -{ - _dst_memset_zero_data *d = (_dst_memset_zero_data *)data; - npy_intp dst_itemsize = d->dst_itemsize; + if (get_n_to_n_transfer_function(aligned, + src_stride, 0, + src_dtype->subarray->base, NULL, 1, src_size, + &cast_info->func, &cast_info->auxdata, + out_needs_api) != NPY_SUCCEED) { + return NPY_FAIL; + } - while (N > 0) { - memset(dst, 0, dst_itemsize); - dst += dst_stride; - --N; + goto finalize; } -} + /* If there are fields, need to do each field */ + else if (PyDataType_HASFIELDS(src_dtype)) { + if (out_needs_api) { + *out_needs_api = 1; + } -static void -_null_to_contig_memset_zero(char *dst, - npy_intp dst_stride, - char *NPY_UNUSED(src), npy_intp NPY_UNUSED(src_stride), - npy_intp N, npy_intp NPY_UNUSED(src_itemsize), - NpyAuxData *data) -{ - _dst_memset_zero_data *d = (_dst_memset_zero_data *)data; - npy_intp dst_itemsize = d->dst_itemsize; + if (get_decref_fields_transfer_function(aligned, + src_stride, src_dtype, + &cast_info->func, &cast_info->auxdata, + out_needs_api) < 0) { + return NPY_FAIL; + } + goto finalize; + } + else { + PyErr_Format(PyExc_RuntimeError, + "Internal error, tried to fetch decref function for the " + "unsupported DType '%S'.", src_dtype); + return NPY_FAIL; + } - memset(dst, 0, N*dst_itemsize); + finalize: + /* Make sure all important fields are either set or cleared */ + Py_INCREF(src_dtype); + cast_info->descriptors[0] = src_dtype; + cast_info->descriptors[1] = NULL; + cast_info->context.method = NULL; + cast_info->context.caller = NULL; + return NPY_SUCCEED; } -static void -_null_to_strided_reference_setzero(char *dst, - npy_intp dst_stride, - char *NPY_UNUSED(src), npy_intp NPY_UNUSED(src_stride), - npy_intp N, npy_intp NPY_UNUSED(src_itemsize), - NpyAuxData *NPY_UNUSED(data)) -{ - PyObject *dst_ref = NULL; - while (N > 0) { - NPY_COPY_PYOBJECT_PTR(&dst_ref, dst); +/* + * ********************* Generalized Multistep Cast ************************ + * + * New general purpose multiple step cast function when resolve descriptors + * implies that multiple cast steps are necessary. + */ - /* Release the reference in dst */ - NPY_DT_DBG_REFTRACE("dec dest ref (to set zero)", dst_ref); - Py_XDECREF(dst_ref); +typedef struct { + NpyAuxData base; + /* Information for main cast */ + NPY_cast_info main; + /* Information for input preparation cast */ + NPY_cast_info from; + /* Information for output finalization cast */ + NPY_cast_info to; + char *from_buffer; + char *to_buffer; +} _multistep_castdata; - /* Set it to zero */ - dst_ref = NULL; - NPY_COPY_PYOBJECT_PTR(dst, &dst_ref); - dst += dst_stride; - --N; +/* zero-padded data copy function */ +static void +_multistep_cast_auxdata_free(NpyAuxData *auxdata) +{ + _multistep_castdata *data = (_multistep_castdata *)auxdata; + NPY_cast_info_xfree(&data->main); + if (data->from.func != NULL) { + NPY_cast_info_xfree(&data->from); } + if (data->to.func != NULL) { + NPY_cast_info_xfree(&data->to); + } + PyMem_Free(data); } -NPY_NO_EXPORT int -get_setdstzero_transfer_function(int aligned, - npy_intp dst_stride, - PyArray_Descr *dst_dtype, - PyArray_StridedUnaryOp **out_stransfer, - NpyAuxData **out_transferdata, - int *out_needs_api) -{ - _dst_memset_zero_data *data; - /* If there are no references, just set the whole thing to zero */ - if (!PyDataType_REFCHK(dst_dtype)) { - data = (_dst_memset_zero_data *) - PyArray_malloc(sizeof(_dst_memset_zero_data)); - if (data == NULL) { - PyErr_NoMemory(); - return NPY_FAIL; - } +static NpyAuxData * +_multistep_cast_auxdata_clone(NpyAuxData *auxdata_old); - data->base.free = (NpyAuxData_FreeFunc *)(&PyArray_free); - data->base.clone = &_dst_memset_zero_data_clone; - data->dst_itemsize = dst_dtype->elsize; - if (dst_stride == data->dst_itemsize) { - *out_stransfer = &_null_to_contig_memset_zero; - } - else { - *out_stransfer = &_null_to_strided_memset_zero; - } - *out_transferdata = (NpyAuxData *)data; +static NpyAuxData * +_multistep_cast_auxdata_clone_int(_multistep_castdata *castdata, int move_info) +{ + /* Round up the structure size to 16-byte boundary for the buffers */ + Py_ssize_t datasize = (sizeof(_multistep_castdata) + 15) & ~0xf; + + Py_ssize_t from_buffer_offset = datasize; + if (castdata->from.func != NULL) { + Py_ssize_t src_itemsize = castdata->main.context.descriptors[0]->elsize; + datasize += NPY_LOWLEVEL_BUFFER_BLOCKSIZE * src_itemsize; + datasize = (datasize + 15) & ~0xf; + } + Py_ssize_t to_buffer_offset = datasize; + if (castdata->to.func != NULL) { + Py_ssize_t dst_itemsize = castdata->main.context.descriptors[1]->elsize; + datasize += NPY_LOWLEVEL_BUFFER_BLOCKSIZE * dst_itemsize; } - /* If it's exactly one reference, use the decref function */ - else if (dst_dtype->type_num == NPY_OBJECT) { - if (out_needs_api) { - *out_needs_api = 1; - } - *out_stransfer = &_null_to_strided_reference_setzero; - *out_transferdata = NULL; + char *char_data = PyMem_Malloc(datasize); + if (char_data == NULL) { + return NULL; } - /* If there are subarrays, need to wrap it */ - else if (PyDataType_HASSUBARRAY(dst_dtype)) { - PyArray_Dims dst_shape = {NULL, -1}; - npy_intp dst_size = 1; - PyArray_StridedUnaryOp *contig_stransfer; - NpyAuxData *contig_data; - if (out_needs_api) { - *out_needs_api = 1; - } + _multistep_castdata *newdata = (_multistep_castdata *)char_data; - if (!(PyArray_IntpConverter(dst_dtype->subarray->shape, - &dst_shape))) { - PyErr_SetString(PyExc_ValueError, - "invalid subarray shape"); - return NPY_FAIL; - } - dst_size = PyArray_MultiplyList(dst_shape.ptr, dst_shape.len); - npy_free_cache_dim_obj(dst_shape); + /* Fix up the basic information: */ + newdata->base.free = &_multistep_cast_auxdata_free; + newdata->base.clone = &_multistep_cast_auxdata_clone; + /* And buffer information: */ + newdata->from_buffer = char_data + from_buffer_offset; + newdata->to_buffer = char_data + to_buffer_offset; - /* Get a function for contiguous dst of the subarray type */ - if (get_setdstzero_transfer_function(aligned, - dst_dtype->subarray->base->elsize, - dst_dtype->subarray->base, - &contig_stransfer, &contig_data, - out_needs_api) != NPY_SUCCEED) { - return NPY_FAIL; + /* Initialize funcs to NULL to signal no-cleanup in case of an error. */ + newdata->from.func = NULL; + newdata->to.func = NULL; + + if (move_info) { + NPY_cast_info_move(&newdata->main, &castdata->main); + } + else if (NPY_cast_info_copy(&newdata->main, &castdata->main) < 0) { + goto fail; + } + + if (castdata->from.func != NULL) { + if (move_info) { + NPY_cast_info_move(&newdata->from, &castdata->from); + } + else if (NPY_cast_info_copy(&newdata->from, &castdata->from) < 0) { + goto fail; } - if (wrap_transfer_function_n_to_n(contig_stransfer, contig_data, - 0, dst_stride, - 0, dst_dtype->subarray->base->elsize, - dst_size, - out_stransfer, out_transferdata) != NPY_SUCCEED) { - NPY_AUXDATA_FREE(contig_data); - return NPY_FAIL; + if (PyDataType_FLAGCHK(newdata->main.descriptors[0], NPY_NEEDS_INIT)) { + memset(newdata->from_buffer, 0, to_buffer_offset - from_buffer_offset); } } - /* If there are fields, need to do each field */ - else if (PyDataType_HASFIELDS(dst_dtype)) { - if (out_needs_api) { - *out_needs_api = 1; + if (castdata->to.func != NULL) { + if (move_info) { + NPY_cast_info_move(&newdata->to, &castdata->to); + } + else if (NPY_cast_info_copy(&newdata->to, &castdata->to) < 0) { + goto fail; } - return get_setdestzero_fields_transfer_function(aligned, - dst_stride, dst_dtype, - out_stransfer, - out_transferdata, - out_needs_api); + if (PyDataType_FLAGCHK(newdata->main.descriptors[1], NPY_NEEDS_INIT)) { + memset(newdata->to_buffer, 0, datasize - to_buffer_offset); + } } - return NPY_SUCCEED; + return (NpyAuxData *)newdata; + + fail: + NPY_AUXDATA_FREE((NpyAuxData *)newdata); + return NULL; } -static void -_dec_src_ref_nop(char *NPY_UNUSED(dst), - npy_intp NPY_UNUSED(dst_stride), - char *NPY_UNUSED(src), npy_intp NPY_UNUSED(src_stride), - npy_intp NPY_UNUSED(N), - npy_intp NPY_UNUSED(src_itemsize), - NpyAuxData *NPY_UNUSED(data)) + +static NpyAuxData * +_multistep_cast_auxdata_clone(NpyAuxData *auxdata_old) { - /* NOP */ + return _multistep_cast_auxdata_clone_int( + (_multistep_castdata *)auxdata_old, 0); } -static void -_strided_to_null_dec_src_ref_reference(char *NPY_UNUSED(dst), - npy_intp NPY_UNUSED(dst_stride), - char *src, npy_intp src_stride, - npy_intp N, - npy_intp NPY_UNUSED(src_itemsize), - NpyAuxData *NPY_UNUSED(data)) + +static int +_strided_to_strided_multistep_cast( + /* The context is always stored explicitly in auxdata */ + PyArrayMethod_Context *NPY_UNUSED(context), char *const *args, + const npy_intp *dimensions, const npy_intp *strides, + NpyAuxData *auxdata) { - PyObject *src_ref = NULL; + npy_intp N = dimensions[0]; + char *src = args[0], *dst = args[1]; + _multistep_castdata *castdata = (_multistep_castdata *)auxdata; + npy_intp src_stride = strides[0], dst_stride = strides[1]; + + char *main_src, *main_dst; + npy_intp main_src_stride, main_dst_stride; + + npy_intp block_size = NPY_LOWLEVEL_BUFFER_BLOCKSIZE; while (N > 0) { - NPY_COPY_PYOBJECT_PTR(&src_ref, src); + if (block_size > N) { + block_size = N; + } + + if (castdata->from.func != NULL) { + npy_intp out_stride = castdata->from.descriptors[1]->elsize; + if (castdata->from.func(&castdata->from.context, + (char *[2]){src, castdata->from_buffer}, &block_size, + (npy_intp [2]){src_stride, out_stride}, + castdata->from.auxdata) != 0) { + /* TODO: Internal buffer may require cleanup on error. */ + return -1; + } + main_src = castdata->from_buffer; + main_src_stride = out_stride; + } + else { + main_src = src; + main_src_stride = src_stride; + } - /* Release the reference in src */ - NPY_DT_DBG_REFTRACE("dec src ref (null dst)", src_ref); - Py_XDECREF(src_ref); + if (castdata->to.func != NULL) { + main_dst = castdata->to_buffer; + main_dst_stride = castdata->main.descriptors[1]->elsize; + } + else { + main_dst = dst; + main_dst_stride = dst_stride; + } - src += src_stride; - --N; + if (castdata->main.func(&castdata->main.context, + (char *[2]){main_src, main_dst}, &block_size, + (npy_intp [2]){main_src_stride, main_dst_stride}, + castdata->main.auxdata) != 0) { + /* TODO: Internal buffer may require cleanup on error. */ + return -1; + } + + if (castdata->to.func != NULL) { + if (castdata->to.func(&castdata->to.context, + (char *[2]){main_dst, dst}, &block_size, + (npy_intp [2]){main_dst_stride, dst_stride}, + castdata->to.auxdata) != 0) { + return -1; + } + } + + N -= block_size; + src += block_size * src_stride; + dst += block_size * dst_stride; } + return 0; } -NPY_NO_EXPORT int -get_decsrcref_transfer_function(int aligned, - npy_intp src_stride, - PyArray_Descr *src_dtype, - PyArray_StridedUnaryOp **out_stransfer, - NpyAuxData **out_transferdata, - int *out_needs_api) +/* + * Initialize most of a cast-info structure, this step does not fetch the + * transferfunction and transferdata. + */ +static NPY_INLINE int +init_cast_info(NPY_cast_info *cast_info, NPY_CASTING *casting, + PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype, int main_step) +{ + PyObject *meth = PyArray_GetCastingImpl( + NPY_DTYPE(src_dtype), NPY_DTYPE(dst_dtype)); + if (meth == NULL) { + return -1; + } + if (meth == Py_None) { + Py_DECREF(Py_None); + PyErr_Format(PyExc_TypeError, + "Cannot cast data from %S to %S.", src_dtype, dst_dtype); + return -1; + } + /* Initialize the context and related data */ + NPY_cast_info_init(cast_info); + cast_info->auxdata = NULL; + + cast_info->context.caller = NULL; + cast_info->context.method = (PyArrayMethodObject *)meth; + + PyArray_DTypeMeta *dtypes[2] = {NPY_DTYPE(src_dtype), NPY_DTYPE(dst_dtype)}; + PyArray_Descr *in_descr[2] = {src_dtype, dst_dtype}; + + *casting = cast_info->context.method->resolve_descriptors( + cast_info->context.method, dtypes, in_descr, cast_info->descriptors); + if (NPY_UNLIKELY(*casting < 0)) { + if (!PyErr_Occurred()) { + PyErr_Format(PyExc_TypeError, + "Cannot cast array data from %R to %R.", src_dtype, dst_dtype); + Py_DECREF(meth); + return -1; + } + } + assert(PyArray_DescrCheck(cast_info->descriptors[0])); + assert(PyArray_DescrCheck(cast_info->descriptors[1])); + + if (!main_step && NPY_UNLIKELY(src_dtype != cast_info->descriptors[0] || + dst_dtype != cast_info->descriptors[1])) { + /* + * We currently do not resolve recursively, but require a non + * main cast (within the same DType) to be done in a single step. + * This could be expanded at some point if the need arises. + */ + PyErr_Format(PyExc_RuntimeError, + "Required internal cast from %R to %R was not done in a single " + "step (a secondary cast must currently be between instances of " + "the same DType class and such a cast must currently return " + "the input descriptors unmodified).", + src_dtype, dst_dtype); + NPY_cast_info_xfree(cast_info); + return -1; + } + + return 0; +} + + +/* + * When there is a failure in ArrayMethod.get_loop(...) we still have + * to clean up references, but assume that `auxdata` and `func` + * have undefined values. + * NOTE: This should possibly be moved, but is only necessary here + */ +static void +_clear_cast_info_after_get_loop_failure(NPY_cast_info *cast_info) { - /* If there are no references, it's a nop */ - if (!PyDataType_REFCHK(src_dtype)) { - *out_stransfer = &_dec_src_ref_nop; - *out_transferdata = NULL; + /* As public API we could choose to clear auxdata != NULL */ + assert(cast_info->auxdata == NULL); + /* Set func to be non-null so that `NPY_cats_info_xfree` does not skip */ + cast_info->func = &_dec_src_ref_nop; + NPY_cast_info_xfree(cast_info); +} - return NPY_SUCCEED; - } - /* If it's a single reference, it's one decref */ - else if (src_dtype->type_num == NPY_OBJECT) { - if (out_needs_api) { - *out_needs_api = 1; - } - *out_stransfer = &_strided_to_null_dec_src_ref_reference; - *out_transferdata = NULL; +/* + * Helper for PyArray_GetDTypeTransferFunction, which fetches a single + * transfer function from the each casting implementation (ArrayMethod). + * May set the transfer function to NULL when the cast can be achieved using + * a view. + * The `out_needs_api` flag must be initialized. + * + * NOTE: In theory casting errors here could be slightly misleading in case + * of a multi-step casting scenario. It should be possible to improve + * this in the future. + * + * Note about `move_references`: Move references means stealing of + * references. It is useful to clear buffers immediately. No matter the + * input all copies from a buffer must use `move_references`. Move references + * is thus used: + * * For the added initial "from" cast if it was passed in + * * Always in the main step if a "from" cast is made (it casts from a buffer) + * * Always for the "to" cast, as it always cast from a buffer to the output. + * + * Returns -1 on failure, 0 on success + */ +static int +define_cast_for_descrs( + int aligned, + npy_intp src_stride, npy_intp dst_stride, + PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype, + int move_references, + NPY_cast_info *cast_info, int *out_needs_api) +{ + /* Storage for all cast info in case multi-step casting is necessary */ + _multistep_castdata castdata; + /* Initialize funcs to NULL to simplify cleanup on error. */ + castdata.main.func = NULL; + castdata.to.func = NULL; + castdata.from.func = NULL; + NPY_CASTING casting = -1; - return NPY_SUCCEED; + if (init_cast_info(cast_info, &casting, src_dtype, dst_dtype, 1) < 0) { + return -1; } - /* If there are subarrays, need to wrap it */ - else if (PyDataType_HASSUBARRAY(src_dtype)) { - PyArray_Dims src_shape = {NULL, -1}; - npy_intp src_size = 1; - PyArray_StridedUnaryOp *stransfer; - NpyAuxData *data; - if (out_needs_api) { - *out_needs_api = 1; - } - - if (!(PyArray_IntpConverter(src_dtype->subarray->shape, - &src_shape))) { - PyErr_SetString(PyExc_ValueError, - "invalid subarray shape"); - return NPY_FAIL; - } - src_size = PyArray_MultiplyList(src_shape.ptr, src_shape.len); - npy_free_cache_dim_obj(src_shape); + /* + * Both input and output must be wrapped in case they may be unaligned + * and the method does not support unaligned data. + * NOTE: It is probable that most/all legacy loops actually do support + * unaligned output, we could move the wrapping there if we wanted + * to. It probably isn't speed relevant though and they should be + * deleted in any case. + */ + int must_wrap = (!aligned && + (cast_info->context.method->flags & NPY_METH_SUPPORTS_UNALIGNED) == 0); - /* Get a function for contiguous src of the subarray type */ - if (get_decsrcref_transfer_function(aligned, - src_dtype->subarray->base->elsize, - src_dtype->subarray->base, - &stransfer, &data, - out_needs_api) != NPY_SUCCEED) { - return NPY_FAIL; + /* + * Wrap the input with an additional cast if necessary. + */ + if (NPY_UNLIKELY(src_dtype != cast_info->descriptors[0] || must_wrap)) { + NPY_CASTING from_casting = -1; + /* Cast function may not support the input, wrap if necessary */ + if (init_cast_info( + &castdata.from, &from_casting, + src_dtype, cast_info->descriptors[0], 0) < 0) { + goto fail; + } + casting = PyArray_MinCastSafety(casting, from_casting); + + /* Prepare the actual cast (if necessary): */ + if (from_casting & _NPY_CAST_IS_VIEW && !must_wrap) { + /* This step is not necessary and can be skipped. */ + castdata.from.func = &_dec_src_ref_nop; /* avoid NULL */ + NPY_cast_info_xfree(&castdata.from); } + else { + /* Fetch the cast function and set up */ + PyArrayMethod_Context *context = &castdata.from.context; + npy_intp strides[2] = {src_stride, cast_info->descriptors[0]->elsize}; + NPY_ARRAYMETHOD_FLAGS flags; + if (context->method->get_strided_loop( + context, aligned, move_references, strides, + &castdata.from.func, &castdata.from.auxdata, &flags) < 0) { + _clear_cast_info_after_get_loop_failure(&castdata.from); + goto fail; + } + assert(castdata.from.func != NULL); - if (wrap_transfer_function_n_to_n(stransfer, data, - src_stride, 0, - src_dtype->subarray->base->elsize, 0, - src_size, - out_stransfer, out_transferdata) != NPY_SUCCEED) { - NPY_AUXDATA_FREE(data); - return NPY_FAIL; + *out_needs_api |= (flags & NPY_METH_REQUIRES_PYAPI) != 0; + /* The main cast now uses a buffered input: */ + src_stride = strides[1]; + move_references = 1; /* main cast has to clear the buffer */ } - - return NPY_SUCCEED; } - /* If there are fields, need to do each field */ - else { - if (out_needs_api) { - *out_needs_api = 1; + /* + * Wrap the output with an additional cast if necessary. + */ + if (NPY_UNLIKELY(dst_dtype != cast_info->descriptors[1] || must_wrap)) { + NPY_CASTING to_casting = -1; + /* Cast function may not support the output, wrap if necessary */ + if (init_cast_info( + &castdata.to, &to_casting, + cast_info->descriptors[1], dst_dtype, 0) < 0) { + goto fail; + } + casting = PyArray_MinCastSafety(casting, to_casting); + + /* Prepare the actual cast (if necessary): */ + if (to_casting & _NPY_CAST_IS_VIEW && !must_wrap) { + /* This step is not necessary and can be skipped. */ + castdata.to.func = &_dec_src_ref_nop; /* avoid NULL */ + NPY_cast_info_xfree(&castdata.to); + } + else { + /* Fetch the cast function and set up */ + PyArrayMethod_Context *context = &castdata.to.context; + npy_intp strides[2] = {cast_info->descriptors[1]->elsize, dst_stride}; + NPY_ARRAYMETHOD_FLAGS flags; + if (context->method->get_strided_loop( + context, aligned, 1 /* clear buffer */, strides, + &castdata.to.func, &castdata.to.auxdata, &flags) < 0) { + _clear_cast_info_after_get_loop_failure(&castdata.to); + goto fail; + } + assert(castdata.to.func != NULL); + + *out_needs_api |= (flags & NPY_METH_REQUIRES_PYAPI) != 0; + /* The main cast now uses a buffered input: */ + dst_stride = strides[0]; + if (castdata.from.func != NULL) { + /* Both input and output are wrapped, now always aligned */ + aligned = 1; + } } + } - return get_decsrcref_fields_transfer_function(aligned, - src_stride, src_dtype, - out_stransfer, - out_transferdata, - out_needs_api); + /* Fetch the main cast function (with updated values) */ + PyArrayMethod_Context *context = &cast_info->context; + npy_intp strides[2] = {src_stride, dst_stride}; + NPY_ARRAYMETHOD_FLAGS flags; + if (context->method->get_strided_loop( + context, aligned, move_references, strides, + &cast_info->func, &cast_info->auxdata, &flags) < 0) { + _clear_cast_info_after_get_loop_failure(cast_info); + goto fail; } -} -/********************* DTYPE COPY SWAP FUNCTION ***********************/ + *out_needs_api |= (flags & NPY_METH_REQUIRES_PYAPI) != 0; -NPY_NO_EXPORT int -PyArray_GetDTypeCopySwapFn(int aligned, - npy_intp src_stride, npy_intp dst_stride, - PyArray_Descr *dtype, - PyArray_StridedUnaryOp **outstransfer, - NpyAuxData **outtransferdata) -{ - npy_intp itemsize = dtype->elsize; - - /* If it's a custom data type, wrap its copy swap function */ - if (dtype->type_num >= NPY_NTYPES) { - *outstransfer = NULL; - wrap_copy_swap_function(aligned, - src_stride, dst_stride, - dtype, - !PyArray_ISNBO(dtype->byteorder), - outstransfer, outtransferdata); - } - /* A straight copy */ - else if (itemsize == 1 || PyArray_ISNBO(dtype->byteorder)) { - *outstransfer = PyArray_GetStridedCopyFn(aligned, - src_stride, dst_stride, - itemsize); - *outtransferdata = NULL; - } - else if (dtype->kind == 'U') { - return wrap_copy_swap_function(aligned, - src_stride, dst_stride, dtype, 1, - outstransfer, outtransferdata); - } - /* If it's not complex, one swap */ - else if (dtype->kind != 'c') { - *outstransfer = PyArray_GetStridedCopySwapFn(aligned, - src_stride, dst_stride, - itemsize); - *outtransferdata = NULL; + if (castdata.from.func == NULL && castdata.to.func == NULL) { + /* Most of the time, there will be only one step required. */ + return 0; } - /* If complex, a paired swap */ - else { - *outstransfer = PyArray_GetStridedCopySwapPairFn(aligned, - src_stride, dst_stride, - itemsize); - *outtransferdata = NULL; + /* The full cast passed in is only the "main" step, copy cast_info there */ + NPY_cast_info_move(&castdata.main, cast_info); + Py_INCREF(src_dtype); + cast_info->descriptors[0] = src_dtype; + Py_INCREF(dst_dtype); + cast_info->descriptors[1] = dst_dtype; + cast_info->context.method = NULL; + + cast_info->func = &_strided_to_strided_multistep_cast; + cast_info->auxdata = _multistep_cast_auxdata_clone_int(&castdata, 1); + if (cast_info->auxdata == NULL) { + PyErr_NoMemory(); + goto fail; } - return (*outstransfer == NULL) ? NPY_FAIL : NPY_SUCCEED; + return 0; + + fail: + NPY_cast_info_xfree(&castdata.main); + NPY_cast_info_xfree(&castdata.from); + NPY_cast_info_xfree(&castdata.to); + return -1; } -/********************* MAIN DTYPE TRANSFER FUNCTION ***********************/ NPY_NO_EXPORT int PyArray_GetDTypeTransferFunction(int aligned, npy_intp src_stride, npy_intp dst_stride, PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype, int move_references, - PyArray_StridedUnaryOp **out_stransfer, - NpyAuxData **out_transferdata, + NPY_cast_info *cast_info, int *out_needs_api) { - npy_intp src_itemsize, dst_itemsize; - int src_type_num, dst_type_num; - int is_builtin; - -#if NPY_DT_DBG_TRACING - printf("Calculating dtype transfer from "); - PyObject_Print((PyObject *)src_dtype, stdout, 0); - printf(" to "); - PyObject_Print((PyObject *)dst_dtype, stdout, 0); - printf("\n"); -#endif + assert(src_dtype != NULL); /* * If one of the dtypes is NULL, we give back either a src decref * function or a dst setzero function + * + * TODO: Eventually, we may wish to support user dtype with references + * (including and beyond bare `PyObject *` this may require extending + * the ArrayMethod API and those paths should likely be split out + * from this function.) */ if (dst_dtype == NULL) { - if (move_references) { - return get_decsrcref_transfer_function(aligned, + assert(move_references); + return get_decref_transfer_function(aligned, src_dtype->elsize, src_dtype, - out_stransfer, out_transferdata, + cast_info, out_needs_api); - } - else { - *out_stransfer = &_dec_src_ref_nop; - *out_transferdata = NULL; - return NPY_SUCCEED; - } } - else if (src_dtype == NULL) { - return get_setdstzero_transfer_function(aligned, - dst_dtype->elsize, - dst_dtype, - out_stransfer, out_transferdata, - out_needs_api); + + if (define_cast_for_descrs(aligned, + src_stride, dst_stride, + src_dtype, dst_dtype, move_references, + cast_info, out_needs_api) < 0) { + return NPY_FAIL; } - src_itemsize = src_dtype->elsize; - dst_itemsize = dst_dtype->elsize; - src_type_num = src_dtype->type_num; - dst_type_num = dst_dtype->type_num; - is_builtin = src_type_num < NPY_NTYPES && dst_type_num < NPY_NTYPES; + return NPY_SUCCEED; +} - /* Common special case - number -> number NBO cast */ - if (PyTypeNum_ISNUMBER(src_type_num) && - PyTypeNum_ISNUMBER(dst_type_num) && - PyArray_ISNBO(src_dtype->byteorder) && - PyArray_ISNBO(dst_dtype->byteorder)) { - if (PyArray_EquivTypenums(src_type_num, dst_type_num)) { - *out_stransfer = PyArray_GetStridedCopyFn(aligned, - src_stride, dst_stride, - src_itemsize); - *out_transferdata = NULL; - return (*out_stransfer == NULL) ? NPY_FAIL : NPY_SUCCEED; - } - else { - return get_nbo_cast_numeric_transfer_function (aligned, - src_stride, dst_stride, - src_type_num, dst_type_num, - out_stransfer, out_transferdata); - } +/* + * Internal wrapping of casts that have to be performed in a "single" + * function (i.e. not by the generic multi-step-cast), but rely on it + * internally. There are only two occasions where this is used: + * + * 1. Void advertises that it handles unaligned casts, but has to wrap the + * legacy cast which (probably) does not. + * 2. Datetime to unicode casts are implemented via bytes "U" vs. "S". If + * we relax the chaining rules to allow "recursive" cast chaining where + * `resolve_descriptors` can return a descriptor with a different type, + * this would become unnecessary. + * 3. Time <-> Time casts, which currently must support byte swapping, but + * have a non-trivial inner-loop (due to units) which does not support + * it. + * + * When wrapping is performed (guaranteed for `aligned == 0` and if the + * wrapped dtype is not identical to the input dtype), the wrapped transfer + * function can assume a contiguous input. + * Otherwise use `must_wrap` to ensure that wrapping occurs, which guarantees + * a contiguous, aligned, call of the wrapped function. + */ +NPY_NO_EXPORT int +wrap_aligned_transferfunction( + int aligned, int must_wrap, + npy_intp src_stride, npy_intp dst_stride, + PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype, + PyArray_Descr *src_wrapped_dtype, PyArray_Descr *dst_wrapped_dtype, + PyArrayMethod_StridedLoop **out_stransfer, + NpyAuxData **out_transferdata, int *out_needs_api) +{ + must_wrap = must_wrap | !aligned; + + _multistep_castdata castdata; + NPY_cast_info_init(&castdata.main); + NPY_cast_info_init(&castdata.from); + NPY_cast_info_init(&castdata.to); + + /* Finalize the existing cast information: */ + castdata.main.func = *out_stransfer; + *out_stransfer = NULL; + castdata.main.auxdata = *out_transferdata; + *out_transferdata = NULL; + castdata.main.context.method = NULL; + /* These are always legacy casts that only support native-byte-order: */ + Py_INCREF(src_wrapped_dtype); + castdata.main.descriptors[0] = src_wrapped_dtype; + if (castdata.main.descriptors[0] == NULL) { + castdata.main.descriptors[1] = NULL; + goto fail; + } + Py_INCREF(dst_wrapped_dtype); + castdata.main.descriptors[1] = dst_wrapped_dtype; + if (castdata.main.descriptors[1] == NULL) { + goto fail; } /* - * If there are no references and the data types are equivalent and builtin, - * return a simple copy + * Similar to the normal multi-step cast, but we always have to wrap + * it all up, but we can simply do this via a "recursive" call. + * TODO: This is slightly wasteful, since it unnecessarily checks casting, + * but this whole function is about corner cases, which should rather + * have an explicit implementation instead if we want performance. */ - if (PyArray_EquivTypes(src_dtype, dst_dtype) && - !PyDataType_REFCHK(src_dtype) && !PyDataType_REFCHK(dst_dtype) && - ( !PyDataType_HASFIELDS(dst_dtype) || - is_dtype_struct_simple_unaligned_layout(dst_dtype)) && - is_builtin) { - /* - * We can't pass through the aligned flag because it's not - * appropriate. Consider a size-8 string, it will say it's - * aligned because strings only need alignment 1, but the - * copy function wants to know if it's alignment 8. - * - * TODO: Change align from a flag to a "best power of 2 alignment" - * which holds the strongest alignment value for all - * the data which will be used. - */ - *out_stransfer = PyArray_GetStridedCopyFn(0, - src_stride, dst_stride, - src_dtype->elsize); - *out_transferdata = NULL; - return NPY_SUCCEED; - } - - /* First look at the possibilities of just a copy or swap */ - if (src_itemsize == dst_itemsize && src_dtype->kind == dst_dtype->kind && - !PyDataType_HASFIELDS(src_dtype) && - !PyDataType_HASFIELDS(dst_dtype) && - !PyDataType_HASSUBARRAY(src_dtype) && - !PyDataType_HASSUBARRAY(dst_dtype) && - src_type_num != NPY_DATETIME && src_type_num != NPY_TIMEDELTA) { - /* A custom data type requires that we use its copy/swap */ - if (!is_builtin) { - /* - * If the sizes and kinds are identical, but they're different - * custom types, then get a cast function - */ - if (src_type_num != dst_type_num) { - return get_cast_transfer_function(aligned, - src_stride, dst_stride, - src_dtype, dst_dtype, - move_references, - out_stransfer, out_transferdata, - out_needs_api); - } - else { - return wrap_copy_swap_function(aligned, - src_stride, dst_stride, - src_dtype, - PyArray_ISNBO(src_dtype->byteorder) != - PyArray_ISNBO(dst_dtype->byteorder), - out_stransfer, out_transferdata); - } - } - - /* The special types, which have no or subelement byte-order */ - switch (src_type_num) { - case NPY_UNICODE: - /* Wrap the copy swap function when swapping is necessary */ - if (PyArray_ISNBO(src_dtype->byteorder) != - PyArray_ISNBO(dst_dtype->byteorder)) { - return wrap_copy_swap_function(aligned, - src_stride, dst_stride, - src_dtype, 1, - out_stransfer, out_transferdata); - } - case NPY_VOID: - case NPY_STRING: - *out_stransfer = PyArray_GetStridedCopyFn(0, - src_stride, dst_stride, - src_itemsize); - *out_transferdata = NULL; - return NPY_SUCCEED; - case NPY_OBJECT: - if (out_needs_api) { - *out_needs_api = 1; - } - if (move_references) { - *out_stransfer = &_strided_to_strided_move_references; - *out_transferdata = NULL; - } - else { - *out_stransfer = &_strided_to_strided_copy_references; - *out_transferdata = NULL; - } - return NPY_SUCCEED; - } - - /* This is a straight copy */ - if (src_itemsize == 1 || PyArray_ISNBO(src_dtype->byteorder) == - PyArray_ISNBO(dst_dtype->byteorder)) { - *out_stransfer = PyArray_GetStridedCopyFn(aligned, - src_stride, dst_stride, - src_itemsize); - *out_transferdata = NULL; - return (*out_stransfer == NULL) ? NPY_FAIL : NPY_SUCCEED; - } - /* This is a straight copy + byte swap */ - else if (!PyTypeNum_ISCOMPLEX(src_type_num)) { - *out_stransfer = PyArray_GetStridedCopySwapFn(aligned, - src_stride, dst_stride, - src_itemsize); - *out_transferdata = NULL; - return (*out_stransfer == NULL) ? NPY_FAIL : NPY_SUCCEED; + if (must_wrap || src_wrapped_dtype != src_dtype) { + if (PyArray_GetDTypeTransferFunction(aligned, + src_stride, castdata.main.descriptors[0]->elsize, + src_dtype, castdata.main.descriptors[0], 0, + &castdata.from, out_needs_api) != NPY_SUCCEED) { + goto fail; } - /* This is a straight copy + element pair byte swap */ - else { - *out_stransfer = PyArray_GetStridedCopySwapPairFn(aligned, - src_stride, dst_stride, - src_itemsize); - *out_transferdata = NULL; - return (*out_stransfer == NULL) ? NPY_FAIL : NPY_SUCCEED; + } + if (must_wrap || dst_wrapped_dtype != dst_dtype) { + if (PyArray_GetDTypeTransferFunction(aligned, + castdata.main.descriptors[1]->elsize, dst_stride, + castdata.main.descriptors[1], dst_dtype, + 1, /* clear buffer if it includes references */ + &castdata.to, out_needs_api) != NPY_SUCCEED) { + goto fail; } } - /* Handle subarrays */ - if (PyDataType_HASSUBARRAY(src_dtype) || - PyDataType_HASSUBARRAY(dst_dtype)) { - return get_subarray_transfer_function(aligned, - src_stride, dst_stride, - src_dtype, dst_dtype, - move_references, - out_stransfer, out_transferdata, - out_needs_api); + *out_transferdata = _multistep_cast_auxdata_clone_int(&castdata, 1); + if (*out_transferdata == NULL) { + PyErr_NoMemory(); + goto fail; } + *out_stransfer = &_strided_to_strided_multistep_cast; + return 0; - /* Handle fields */ - if ((PyDataType_HASFIELDS(src_dtype) || PyDataType_HASFIELDS(dst_dtype)) && - src_type_num != NPY_OBJECT && dst_type_num != NPY_OBJECT) { - return get_fields_transfer_function(aligned, - src_stride, dst_stride, - src_dtype, dst_dtype, - move_references, - out_stransfer, out_transferdata, - out_needs_api); - } + fail: + NPY_cast_info_xfree(&castdata.main); + NPY_cast_info_xfree(&castdata.from); + NPY_cast_info_xfree(&castdata.to); - /* Check for different-sized strings, unicodes, or voids */ - if (src_type_num == dst_type_num) { - switch (src_type_num) { - case NPY_UNICODE: - if (PyArray_ISNBO(src_dtype->byteorder) != - PyArray_ISNBO(dst_dtype->byteorder)) { - return PyArray_GetStridedZeroPadCopyFn(0, 1, - src_stride, dst_stride, - src_dtype->elsize, dst_dtype->elsize, - out_stransfer, out_transferdata); - } - case NPY_STRING: - case NPY_VOID: - return PyArray_GetStridedZeroPadCopyFn(0, 0, - src_stride, dst_stride, - src_dtype->elsize, dst_dtype->elsize, - out_stransfer, out_transferdata); - } + return -1; +} + + +/* + * This function wraps the legacy casts stored on the `dtype->f->cast` + * or registered with `PyArray_RegisterCastFunc`. + * For casts between two dtypes with the same type (within DType casts) + * it also wraps the `copyswapn` function. + * + * This function is called called from `ArrayMethod.get_loop()` when a + * specialized cast function is missing. + * + * In general, the legacy cast functions do not support unaligned access, + * so an ArrayMethod using this must signal that. In a few places we do + * signal support for unaligned access (or byte swapping). + * In this case `allow_wrapped=1` will wrap it into an additional multi-step + * cast as necessary. + */ +NPY_NO_EXPORT int +get_wrapped_legacy_cast_function(int aligned, + npy_intp src_stride, npy_intp dst_stride, + PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype, + int move_references, + PyArrayMethod_StridedLoop **out_stransfer, + NpyAuxData **out_transferdata, + int *out_needs_api, int allow_wrapped) +{ + /* Note: We ignore `needs_wrap`; needs-wrap is handled by another cast */ + int needs_wrap = 0; + + if (src_dtype->type_num == dst_dtype->type_num) { + /* + * This is a cast within the same dtype. For legacy user-dtypes, + * it is always valid to handle this using the copy swap function. + */ + return wrap_copy_swap_function(src_dtype, + PyDataType_ISNOTSWAPPED(src_dtype) != + PyDataType_ISNOTSWAPPED(dst_dtype), + out_stransfer, out_transferdata); + } + + if (get_legacy_dtype_cast_function( + aligned, + src_stride, dst_stride, + src_dtype, dst_dtype, + move_references, + out_stransfer, + out_transferdata, + out_needs_api, + &needs_wrap) != NPY_SUCCEED) { + return -1; + } + if (!needs_wrap) { + return 0; + } + if (NPY_UNLIKELY(!allow_wrapped)) { + /* + * Legacy casts do not support unaligned which requires wrapping. + * However, normally we ensure that wrapping happens before calling + * this function, so this path should never happen. + */ + PyErr_Format(PyExc_RuntimeError, + "Internal NumPy error, casting %S to %S required wrapping, " + "probably it incorrectly flagged support for unaligned data. " + "(aligned passed to discovery is %d)", + src_dtype, dst_dtype, aligned); + goto fail; } - /* Otherwise a cast is necessary */ - return get_cast_transfer_function(aligned, - src_stride, dst_stride, - src_dtype, dst_dtype, - move_references, - out_stransfer, out_transferdata, - out_needs_api); + /* + * If we are here, use the legacy code to wrap the above cast (which + * does not support unaligned data) into copyswapn. + */ + PyArray_Descr *src_wrapped_dtype = ensure_dtype_nbo(src_dtype); + if (src_wrapped_dtype == NULL) { + goto fail; + } + PyArray_Descr *dst_wrapped_dtype = ensure_dtype_nbo(dst_dtype); + if (dst_wrapped_dtype == NULL) { + goto fail; + } + int res = wrap_aligned_transferfunction( + aligned, 1, /* We assume wrapped is contiguous here */ + src_stride, dst_stride, + src_dtype, dst_dtype, + src_wrapped_dtype, dst_wrapped_dtype, + out_stransfer, out_transferdata, out_needs_api); + Py_DECREF(src_wrapped_dtype); + Py_DECREF(dst_wrapped_dtype); + return res; + + fail: + NPY_AUXDATA_FREE(*out_transferdata); + *out_transferdata = NULL; + return -1; } + NPY_NO_EXPORT int PyArray_GetMaskedDTypeTransferFunction(int aligned, npy_intp src_stride, @@ -3649,71 +3481,66 @@ PyArray_GetMaskedDTypeTransferFunction(int aligned, PyArray_Descr *dst_dtype, PyArray_Descr *mask_dtype, int move_references, - PyArray_MaskedStridedUnaryOp **out_stransfer, - NpyAuxData **out_transferdata, + NPY_cast_info *cast_info, int *out_needs_api) { - PyArray_StridedUnaryOp *stransfer = NULL; - NpyAuxData *transferdata = NULL; - _masked_wrapper_transfer_data *data; + NPY_cast_info_init(cast_info); - /* TODO: Add struct-based mask_dtype support later */ if (mask_dtype->type_num != NPY_BOOL && mask_dtype->type_num != NPY_UINT8) { PyErr_SetString(PyExc_TypeError, - "Only bool and uint8 masks are supported at the moment, " - "structs of bool/uint8 is planned for the future"); + "Only bool and uint8 masks are supported."); return NPY_FAIL; } - /* TODO: Special case some important cases so they're fast */ + /* Create the wrapper function's auxdata */ + _masked_wrapper_transfer_data *data; + data = PyMem_Malloc(sizeof(_masked_wrapper_transfer_data)); + if (data == NULL) { + PyErr_NoMemory(); + return NPY_FAIL; + } + data->base.free = &_masked_wrapper_transfer_data_free; + data->base.clone = &_masked_wrapper_transfer_data_clone; /* Fall back to wrapping a non-masked transfer function */ + assert(dst_dtype != NULL); if (PyArray_GetDTypeTransferFunction(aligned, src_stride, dst_stride, src_dtype, dst_dtype, move_references, - &stransfer, &transferdata, + &data->wrapped, out_needs_api) != NPY_SUCCEED) { + PyMem_Free(data); return NPY_FAIL; } - /* Create the wrapper function's auxdata */ - data = (_masked_wrapper_transfer_data *)PyArray_malloc( - sizeof(_masked_wrapper_transfer_data)); - if (data == NULL) { - PyErr_NoMemory(); - NPY_AUXDATA_FREE(transferdata); - return NPY_FAIL; - } - - /* Fill in the auxdata object */ - memset(data, 0, sizeof(_masked_wrapper_transfer_data)); - data->base.free = &_masked_wrapper_transfer_data_free; - data->base.clone = &_masked_wrapper_transfer_data_clone; - - data->stransfer = stransfer; - data->transferdata = transferdata; - /* If the src object will need a DECREF, get a function to handle that */ if (move_references && PyDataType_REFCHK(src_dtype)) { - if (get_decsrcref_transfer_function(aligned, + if (get_decref_transfer_function(aligned, src_stride, src_dtype, - &data->decsrcref_stransfer, - &data->decsrcref_transferdata, + &data->decref_src, out_needs_api) != NPY_SUCCEED) { NPY_AUXDATA_FREE((NpyAuxData *)data); return NPY_FAIL; } - - *out_stransfer = &_strided_masked_wrapper_decsrcref_transfer_function; + cast_info->func = (PyArrayMethod_StridedLoop *) + &_strided_masked_wrapper_decref_transfer_function; } else { - *out_stransfer = &_strided_masked_wrapper_transfer_function; - } - - *out_transferdata = (NpyAuxData *)data; + NPY_cast_info_init(&data->decref_src); + cast_info->func = (PyArrayMethod_StridedLoop *) + &_strided_masked_wrapper_transfer_function; + } + cast_info->auxdata = (NpyAuxData *)data; + /* The context is almost unused, but clear it for cleanup. */ + Py_INCREF(src_dtype); + cast_info->descriptors[0] = src_dtype; + Py_INCREF(dst_dtype); + cast_info->descriptors[1] = dst_dtype; + cast_info->context.caller = NULL; + cast_info->context.method = NULL; return NPY_SUCCEED; } @@ -3725,8 +3552,6 @@ PyArray_CastRawArrays(npy_intp count, PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype, int move_references) { - PyArray_StridedUnaryOp *stransfer = NULL; - NpyAuxData *transferdata = NULL; int aligned = 1, needs_api = 0; /* Make sure the copy is reasonable */ @@ -3739,28 +3564,34 @@ PyArray_CastRawArrays(npy_intp count, return NPY_SUCCEED; } - /* Check data alignment */ - aligned = (((npy_intp)src | src_stride) & - (src_dtype->alignment - 1)) == 0 && - (((npy_intp)dst | dst_stride) & - (dst_dtype->alignment - 1)) == 0; + /* Check data alignment, both uint and true */ + aligned = raw_array_is_aligned(1, &count, dst, &dst_stride, + npy_uint_alignment(dst_dtype->elsize)) && + raw_array_is_aligned(1, &count, dst, &dst_stride, + dst_dtype->alignment) && + raw_array_is_aligned(1, &count, src, &src_stride, + npy_uint_alignment(src_dtype->elsize)) && + raw_array_is_aligned(1, &count, src, &src_stride, + src_dtype->alignment); /* Get the function to do the casting */ + NPY_cast_info cast_info; if (PyArray_GetDTypeTransferFunction(aligned, src_stride, dst_stride, src_dtype, dst_dtype, move_references, - &stransfer, &transferdata, + &cast_info, &needs_api) != NPY_SUCCEED) { return NPY_FAIL; } /* Cast */ - stransfer(dst, dst_stride, src, src_stride, count, - src_dtype->elsize, transferdata); + char *args[2] = {src, dst}; + npy_intp strides[2] = {src_stride, dst_stride}; + cast_info.func(&cast_info.context, args, &count, strides, cast_info.auxdata); /* Cleanup */ - NPY_AUXDATA_FREE(transferdata); + NPY_cast_info_xfree(&cast_info); /* If needs_api was set to 1, it may have raised a Python exception */ return (needs_api && PyErr_Occurred()) ? NPY_FAIL : NPY_SUCCEED; @@ -3782,8 +3613,8 @@ PyArray_CastRawArrays(npy_intp count, * Returns 0 on success, -1 on failure. */ NPY_NO_EXPORT int -PyArray_PrepareOneRawArrayIter(int ndim, npy_intp *shape, - char *data, npy_intp *strides, +PyArray_PrepareOneRawArrayIter(int ndim, npy_intp const *shape, + char *data, npy_intp const *strides, int *out_ndim, npy_intp *out_shape, char **out_data, npy_intp *out_strides) { @@ -3903,9 +3734,9 @@ PyArray_PrepareOneRawArrayIter(int ndim, npy_intp *shape, * Returns 0 on success, -1 on failure. */ NPY_NO_EXPORT int -PyArray_PrepareTwoRawArrayIter(int ndim, npy_intp *shape, - char *dataA, npy_intp *stridesA, - char *dataB, npy_intp *stridesB, +PyArray_PrepareTwoRawArrayIter(int ndim, npy_intp const *shape, + char *dataA, npy_intp const *stridesA, + char *dataB, npy_intp const *stridesB, int *out_ndim, npy_intp *out_shape, char **out_dataA, npy_intp *out_stridesA, char **out_dataB, npy_intp *out_stridesB) @@ -4027,10 +3858,10 @@ PyArray_PrepareTwoRawArrayIter(int ndim, npy_intp *shape, * Returns 0 on success, -1 on failure. */ NPY_NO_EXPORT int -PyArray_PrepareThreeRawArrayIter(int ndim, npy_intp *shape, - char *dataA, npy_intp *stridesA, - char *dataB, npy_intp *stridesB, - char *dataC, npy_intp *stridesC, +PyArray_PrepareThreeRawArrayIter(int ndim, npy_intp const *shape, + char *dataA, npy_intp const *stridesA, + char *dataB, npy_intp const *stridesB, + char *dataC, npy_intp const *stridesC, int *out_ndim, npy_intp *out_shape, char **out_dataA, npy_intp *out_stridesA, char **out_dataB, npy_intp *out_stridesB, diff --git a/numpy/core/src/multiarray/dtype_transfer.h b/numpy/core/src/multiarray/dtype_transfer.h new file mode 100644 index 000000000000..c7e0a029f990 --- /dev/null +++ b/numpy/core/src/multiarray/dtype_transfer.h @@ -0,0 +1,205 @@ +#ifndef NUMPY_CORE_SRC_MULTIARRAY_DTYPE_TRANSFER_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_DTYPE_TRANSFER_H_ + +#include "array_method.h" + + +/* + * More than for most functions, cast information needs to be stored in + * a few places. Most importantly, in many cases we need to chain or wrap + * casts (e.g. structured dtypes). + * + * This struct provides a place to store all necessary information as + * compact as possible. It must be used with the inline functions below + * to ensure correct setup and teardown. + * + * In general, the casting machinery currently handles the correct set up + * of the struct. + */ +typedef struct { + PyArrayMethod_StridedLoop *func; + NpyAuxData *auxdata; + PyArrayMethod_Context context; + /* Storage to be linked from "context" */ + PyArray_Descr *descriptors[2]; +} NPY_cast_info; + + +/* + * Create a new cast-info struct with cast_info->context.descriptors linked. + * Compilers should inline this to ensure the whole struct is not actually + * copied. + * If set up otherwise, func must be NULL'ed to indicate no-cleanup necessary. + */ +static NPY_INLINE void +NPY_cast_info_init(NPY_cast_info *cast_info) +{ + cast_info->func = NULL; /* mark as uninitialized. */ + /* + * Support for auxdata being unchanged, in the future, we might add + * a scratch space to `NPY_cast_info` and link to that instead. + */ + cast_info->auxdata = NULL; + cast_info->context.descriptors = cast_info->descriptors; + + // TODO: Delete this again probably maybe create a new minimal init macro + cast_info->context.caller = NULL; +} + + +/* + * Free's all references and data held inside the struct (not the struct). + * First checks whether `cast_info.func == NULL`, and assume it is + * uninitialized in that case. + */ +static NPY_INLINE void +NPY_cast_info_xfree(NPY_cast_info *cast_info) +{ + if (cast_info->func == NULL) { + return; + } + assert(cast_info->context.descriptors == cast_info->descriptors); + NPY_AUXDATA_FREE(cast_info->auxdata); + Py_DECREF(cast_info->descriptors[0]); + Py_XDECREF(cast_info->descriptors[1]); + Py_XDECREF(cast_info->context.method); + cast_info->func = NULL; +} + + +/* + * Move the data from `original` to `cast_info`. Original is cleared + * (its func set to NULL). + */ +static NPY_INLINE void +NPY_cast_info_move(NPY_cast_info *cast_info, NPY_cast_info *original) +{ + *cast_info = *original; + /* Fix internal pointer: */ + cast_info->context.descriptors = cast_info->descriptors; + /* Mark original to not be cleaned up: */ + original->func = NULL; +} + +/* + * Finalize a copy (INCREF+auxdata clone). This assumes a previous `memcpy` + * of the struct. + * NOTE: It is acceptable to call this with the same struct if the struct + * has been filled by a valid memcpy from an initialized one. + */ +static NPY_INLINE int +NPY_cast_info_copy(NPY_cast_info *cast_info, NPY_cast_info *original) +{ + cast_info->context.descriptors = cast_info->descriptors; + + assert(original->func != NULL); + cast_info->func = original->func; + cast_info->descriptors[0] = original->descriptors[0]; + Py_XINCREF(cast_info->descriptors[0]); + cast_info->descriptors[1] = original->descriptors[1]; + Py_XINCREF(cast_info->descriptors[1]); + cast_info->context.caller = original->context.caller; + Py_XINCREF(cast_info->context.caller); + cast_info->context.method = original->context.method; + Py_XINCREF(cast_info->context.method); + if (original->auxdata == NULL) { + cast_info->auxdata = NULL; + return 0; + } + cast_info->auxdata = NPY_AUXDATA_CLONE(original->auxdata); + if (NPY_UNLIKELY(cast_info->auxdata == NULL)) { + /* No need for cleanup, everything but auxdata is initialized fine. */ + return -1; + } + return 0; +} + + +NPY_NO_EXPORT int +_strided_to_strided_move_references( + PyArrayMethod_Context *NPY_UNUSED(context), char *const *args, + const npy_intp *dimensions, const npy_intp *strides, + NpyAuxData *NPY_UNUSED(auxdata)); + +NPY_NO_EXPORT int +_strided_to_strided_copy_references( + PyArrayMethod_Context *NPY_UNUSED(context), char *const *args, + const npy_intp *dimensions, const npy_intp *strides, + NpyAuxData *NPY_UNUSED(auxdata)); + + +NPY_NO_EXPORT int +any_to_object_get_loop( + PyArrayMethod_Context *context, + int aligned, int move_references, + npy_intp *strides, + PyArrayMethod_StridedLoop **out_loop, + NpyAuxData **out_transferdata, + NPY_ARRAYMETHOD_FLAGS *flags); + +NPY_NO_EXPORT int +object_to_any_get_loop( + PyArrayMethod_Context *context, + int NPY_UNUSED(aligned), int move_references, + npy_intp *NPY_UNUSED(strides), + PyArrayMethod_StridedLoop **out_loop, + NpyAuxData **out_transferdata, + NPY_ARRAYMETHOD_FLAGS *flags); + + +NPY_NO_EXPORT int +wrap_aligned_transferfunction( + int aligned, int must_wrap, + npy_intp src_stride, npy_intp dst_stride, + PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype, + PyArray_Descr *src_wrapped_dtype, PyArray_Descr *dst_wrapped_dtype, + PyArrayMethod_StridedLoop **out_stransfer, + NpyAuxData **out_transferdata, int *out_needs_api); + + +NPY_NO_EXPORT int +get_nbo_cast_datetime_transfer_function(int aligned, + PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype, + PyArrayMethod_StridedLoop **out_stransfer, + NpyAuxData **out_transferdata); + +NPY_NO_EXPORT int +get_nbo_datetime_to_string_transfer_function( + PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype, + PyArrayMethod_StridedLoop **out_stransfer, + NpyAuxData **out_transferdata); + +NPY_NO_EXPORT int +get_nbo_string_to_datetime_transfer_function( + PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype, + PyArrayMethod_StridedLoop **out_stransfer, + NpyAuxData **out_transferdata); + +NPY_NO_EXPORT int +get_datetime_to_unicode_transfer_function(int aligned, + npy_intp src_stride, npy_intp dst_stride, + PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype, + PyArrayMethod_StridedLoop **out_stransfer, + NpyAuxData **out_transferdata, + int *out_needs_api); + +NPY_NO_EXPORT int +get_unicode_to_datetime_transfer_function(int aligned, + npy_intp src_stride, npy_intp dst_stride, + PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype, + PyArrayMethod_StridedLoop **out_stransfer, + NpyAuxData **out_transferdata, + int *out_needs_api); + +/* Creates a wrapper around copyswapn or legacy cast functions */ +NPY_NO_EXPORT int +get_wrapped_legacy_cast_function(int aligned, + npy_intp src_stride, npy_intp dst_stride, + PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype, + int move_references, + PyArrayMethod_StridedLoop **out_stransfer, + NpyAuxData **out_transferdata, + int *out_needs_api, int allow_wrapped); + + +#endif /* NUMPY_CORE_SRC_MULTIARRAY_DTYPE_TRANSFER_H_ */ diff --git a/numpy/core/src/multiarray/dtypemeta.c b/numpy/core/src/multiarray/dtypemeta.c new file mode 100644 index 000000000000..cd489d5e7c9d --- /dev/null +++ b/numpy/core/src/multiarray/dtypemeta.c @@ -0,0 +1,713 @@ +/* Array Descr Object */ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE + +#define PY_SSIZE_T_CLEAN +#include <Python.h> +#include <structmember.h> + +#include <numpy/ndarraytypes.h> +#include <numpy/arrayscalars.h> +#include "npy_pycompat.h" + +#include "common.h" +#include "dtypemeta.h" +#include "_datetime.h" +#include "array_coercion.h" +#include "scalartypes.h" +#include "convert_datatype.h" +#include "usertypes.h" + +#include <assert.h> + +static void +dtypemeta_dealloc(PyArray_DTypeMeta *self) { + /* Do not accidentally delete a statically defined DType: */ + assert(((PyTypeObject *)self)->tp_flags & Py_TPFLAGS_HEAPTYPE); + + Py_XDECREF(self->scalar_type); + Py_XDECREF(self->singleton); + Py_XDECREF(NPY_DT_SLOTS(self)->castingimpls); + PyMem_Free(self->dt_slots); + PyType_Type.tp_dealloc((PyObject *) self); +} + +static PyObject * +dtypemeta_alloc(PyTypeObject *NPY_UNUSED(type), Py_ssize_t NPY_UNUSED(items)) +{ + PyErr_SetString(PyExc_TypeError, + "DTypes can only be created using the NumPy API."); + return NULL; +} + +static PyObject * +dtypemeta_new(PyTypeObject *NPY_UNUSED(type), + PyObject *NPY_UNUSED(args), PyObject *NPY_UNUSED(kwds)) +{ + PyErr_SetString(PyExc_TypeError, + "Preliminary-API: Cannot subclass DType."); + return NULL; +} + +static int +dtypemeta_init(PyTypeObject *NPY_UNUSED(type), + PyObject *NPY_UNUSED(args), PyObject *NPY_UNUSED(kwds)) +{ + PyErr_SetString(PyExc_TypeError, + "Preliminary-API: Cannot __init__ DType class."); + return -1; +} + +/** + * tp_is_gc slot of Python types. This is implemented only for documentation + * purposes to indicate and document the subtleties involved. + * + * Python Type objects are either statically created (typical C-Extension type) + * or HeapTypes (typically created in Python). + * HeapTypes have the Py_TPFLAGS_HEAPTYPE flag and are garbage collected. + * Our DTypeMeta instances (`np.dtype` and its subclasses) *may* be HeapTypes + * if the Py_TPFLAGS_HEAPTYPE flag is set (they are created from Python). + * They are not for legacy DTypes or np.dtype itself. + * + * @param self + * @return nonzero if the object is garbage collected + */ +static NPY_INLINE int +dtypemeta_is_gc(PyObject *dtype_class) +{ + return PyType_Type.tp_is_gc(dtype_class); +} + + +static int +dtypemeta_traverse(PyArray_DTypeMeta *type, visitproc visit, void *arg) +{ + /* + * We have to traverse the base class (if it is a HeapType). + * PyType_Type will handle this logic for us. + * This function is currently not used, but will probably be necessary + * in the future when we implement HeapTypes (python/dynamically + * defined types). It should be revised at that time. + */ + assert(0); + assert(!NPY_DT_is_legacy(type) && (PyTypeObject *)type != &PyArrayDescr_Type); + Py_VISIT(type->singleton); + Py_VISIT(type->scalar_type); + return PyType_Type.tp_traverse((PyObject *)type, visit, arg); +} + + +static PyObject * +legacy_dtype_default_new(PyArray_DTypeMeta *self, + PyObject *args, PyObject *kwargs) +{ + /* TODO: This should allow endianness and possibly metadata */ + if (NPY_DT_is_parametric(self)) { + /* reject parametric ones since we would need to get unit, etc. info */ + PyErr_Format(PyExc_TypeError, + "Preliminary-API: Flexible/Parametric legacy DType '%S' can " + "only be instantiated using `np.dtype(...)`", self); + return NULL; + } + + if (PyTuple_GET_SIZE(args) != 0 || + (kwargs != NULL && PyDict_Size(kwargs))) { + PyErr_Format(PyExc_TypeError, + "currently only the no-argument instantiation is supported; " + "use `np.dtype` instead."); + return NULL; + } + Py_INCREF(self->singleton); + return (PyObject *)self->singleton; +} + + +static PyArray_Descr * +nonparametric_discover_descr_from_pyobject( + PyArray_DTypeMeta *cls, PyObject *obj) +{ + /* If the object is of the correct scalar type return our singleton */ + assert(!NPY_DT_is_parametric(cls)); + Py_INCREF(cls->singleton); + return cls->singleton; +} + + +static PyArray_Descr * +string_discover_descr_from_pyobject( + PyArray_DTypeMeta *cls, PyObject *obj) +{ + npy_intp itemsize = -1; + if (PyBytes_Check(obj)) { + itemsize = PyBytes_Size(obj); + } + else if (PyUnicode_Check(obj)) { + itemsize = PyUnicode_GetLength(obj); + } + if (itemsize != -1) { + if (cls->type_num == NPY_UNICODE) { + itemsize *= 4; + } + if (itemsize > NPY_MAX_INT) { + PyErr_SetString(PyExc_TypeError, + "string to large to store inside array."); + } + PyArray_Descr *res = PyArray_DescrNewFromType(cls->type_num); + res->elsize = (int)itemsize; + return res; + } + return PyArray_DTypeFromObjectStringDiscovery(obj, NULL, cls->type_num); +} + + +static PyArray_Descr * +void_discover_descr_from_pyobject( + PyArray_DTypeMeta *NPY_UNUSED(cls), PyObject *obj) +{ + if (PyArray_IsScalar(obj, Void)) { + PyVoidScalarObject *void_obj = (PyVoidScalarObject *)obj; + Py_INCREF(void_obj->descr); + return void_obj->descr; + } + if (PyBytes_Check(obj)) { + PyArray_Descr *descr = PyArray_DescrNewFromType(NPY_VOID); + Py_ssize_t itemsize = PyBytes_Size(obj); + if (itemsize > NPY_MAX_INT) { + PyErr_SetString(PyExc_TypeError, + "byte-like to large to store inside array."); + } + descr->elsize = (int)itemsize; + return descr; + } + PyErr_Format(PyExc_TypeError, + "A bytes-like object is required, not '%s'", Py_TYPE(obj)->tp_name); + return NULL; +} + + +static PyArray_Descr * +discover_datetime_and_timedelta_from_pyobject( + PyArray_DTypeMeta *cls, PyObject *obj) { + if (PyArray_IsScalar(obj, Datetime) || + PyArray_IsScalar(obj, Timedelta)) { + PyArray_DatetimeMetaData *meta; + PyArray_Descr *descr = PyArray_DescrFromScalar(obj); + meta = get_datetime_metadata_from_dtype(descr); + if (meta == NULL) { + return NULL; + } + PyArray_Descr *new_descr = create_datetime_dtype(cls->type_num, meta); + Py_DECREF(descr); + return new_descr; + } + else { + return find_object_datetime_type(obj, cls->type_num); + } +} + + +static PyArray_Descr * +nonparametric_default_descr(PyArray_DTypeMeta *cls) +{ + Py_INCREF(cls->singleton); + return cls->singleton; +} + + +/* Ensure a copy of the singleton (just in case we do adapt it somewhere) */ +static PyArray_Descr * +datetime_and_timedelta_default_descr(PyArray_DTypeMeta *cls) +{ + return PyArray_DescrNew(cls->singleton); +} + + +static PyArray_Descr * +void_default_descr(PyArray_DTypeMeta *cls) +{ + PyArray_Descr *res = PyArray_DescrNew(cls->singleton); + if (res == NULL) { + return NULL; + } + /* + * The legacy behaviour for `np.array([], dtype="V")` is to use "V8". + * This is because `[]` uses `float64` as dtype, and then that is used + * for the size of the requested void. + */ + res->elsize = 8; + return res; +} + +static PyArray_Descr * +string_and_unicode_default_descr(PyArray_DTypeMeta *cls) +{ + PyArray_Descr *res = PyArray_DescrNewFromType(cls->type_num); + if (res == NULL) { + return NULL; + } + res->elsize = 1; + if (cls->type_num == NPY_UNICODE) { + res->elsize *= 4; + } + return res; +} + + +static PyArray_Descr * +string_unicode_common_instance(PyArray_Descr *descr1, PyArray_Descr *descr2) +{ + if (descr1->elsize >= descr2->elsize) { + return ensure_dtype_nbo(descr1); + } + else { + return ensure_dtype_nbo(descr2); + } +} + + +static PyArray_Descr * +void_common_instance(PyArray_Descr *descr1, PyArray_Descr *descr2) +{ + /* + * We currently do not support promotion of void types unless they + * are equivalent. + */ + if (!PyArray_CanCastTypeTo(descr1, descr2, NPY_EQUIV_CASTING)) { + if (descr1->subarray == NULL && descr1->names == NULL && + descr2->subarray == NULL && descr2->names == NULL) { + PyErr_SetString(PyExc_TypeError, + "Invalid type promotion with void datatypes of different " + "lengths. Use the `np.bytes_` datatype instead to pad the " + "shorter value with trailing zero bytes."); + } + else { + PyErr_SetString(PyExc_TypeError, + "invalid type promotion with structured datatype(s)."); + } + return NULL; + } + Py_INCREF(descr1); + return descr1; +} + +NPY_NO_EXPORT int +python_builtins_are_known_scalar_types( + PyArray_DTypeMeta *NPY_UNUSED(cls), PyTypeObject *pytype) +{ + /* + * Always accept the common Python types, this ensures that we do not + * convert pyfloat->float64->integers. Subclasses are hopefully rejected + * as being discovered. + * This is necessary only for python scalar classes which we discover + * as valid DTypes. + */ + if (pytype == &PyFloat_Type) { + return 1; + } + if (pytype == &PyLong_Type) { + return 1; + } + if (pytype == &PyBool_Type) { + return 1; + } + if (pytype == &PyComplex_Type) { + return 1; + } + if (pytype == &PyUnicode_Type) { + return 1; + } + if (pytype == &PyBytes_Type) { + return 1; + } + return 0; +} + + +static int +signed_integers_is_known_scalar_types( + PyArray_DTypeMeta *cls, PyTypeObject *pytype) +{ + if (python_builtins_are_known_scalar_types(cls, pytype)) { + return 1; + } + /* Convert our scalars (raise on too large unsigned and NaN, etc.) */ + return PyType_IsSubtype(pytype, &PyGenericArrType_Type); +} + + +static int +datetime_known_scalar_types( + PyArray_DTypeMeta *cls, PyTypeObject *pytype) +{ + if (python_builtins_are_known_scalar_types(cls, pytype)) { + return 1; + } + /* + * To be able to identify the descriptor from e.g. any string, datetime + * must take charge. Otherwise we would attempt casting which does not + * truly support this. Only object arrays are special cased in this way. + */ + return (PyType_IsSubtype(pytype, &PyBytes_Type) || + PyType_IsSubtype(pytype, &PyUnicode_Type)); +} + + +static int +string_known_scalar_types( + PyArray_DTypeMeta *cls, PyTypeObject *pytype) { + if (python_builtins_are_known_scalar_types(cls, pytype)) { + return 1; + } + if (PyType_IsSubtype(pytype, &PyDatetimeArrType_Type)) { + /* + * TODO: This should likely be deprecated or otherwise resolved. + * Deprecation has to occur in `String->setitem` unfortunately. + * + * Datetime currently do not cast to shorter strings, but string + * coercion for arbitrary values uses `str(obj)[:len]` so it works. + * This means `np.array(np.datetime64("2020-01-01"), "U9")` + * and `np.array(np.datetime64("2020-01-01")).astype("U9")` behave + * differently. + */ + return 1; + } + return 0; +} + + +/* + * The following set of functions define the common dtype operator for + * the builtin types. + */ +static PyArray_DTypeMeta * +default_builtin_common_dtype(PyArray_DTypeMeta *cls, PyArray_DTypeMeta *other) +{ + assert(cls->type_num < NPY_NTYPES); + if (!NPY_DT_is_legacy(other) || other->type_num > cls->type_num) { + /* + * Let the more generic (larger type number) DType handle this + * (note that half is after all others, which works out here.) + */ + Py_INCREF(Py_NotImplemented); + return (PyArray_DTypeMeta *)Py_NotImplemented; + } + + /* + * Note: The use of the promotion table should probably be revised at + * some point. It may be most useful to remove it entirely and then + * consider adding a fast path/cache `PyArray_CommonDType()` itself. + */ + int common_num = _npy_type_promotion_table[cls->type_num][other->type_num]; + if (common_num < 0) { + Py_INCREF(Py_NotImplemented); + return (PyArray_DTypeMeta *)Py_NotImplemented; + } + return PyArray_DTypeFromTypeNum(common_num); +} + + +static PyArray_DTypeMeta * +string_unicode_common_dtype(PyArray_DTypeMeta *cls, PyArray_DTypeMeta *other) +{ + assert(cls->type_num < NPY_NTYPES && cls != other); + if (!NPY_DT_is_legacy(other) || (!PyTypeNum_ISNUMBER(other->type_num) && + /* Not numeric so defer unless cls is unicode and other is string */ + !(cls->type_num == NPY_UNICODE && other->type_num == NPY_STRING))) { + Py_INCREF(Py_NotImplemented); + return (PyArray_DTypeMeta *)Py_NotImplemented; + } + /* + * The builtin types are ordered by complexity (aside from object) here. + * Arguably, we should not consider numbers and strings "common", but + * we currently do. + */ + Py_INCREF(cls); + return cls; +} + +static PyArray_DTypeMeta * +datetime_common_dtype(PyArray_DTypeMeta *cls, PyArray_DTypeMeta *other) +{ + if (cls->type_num == NPY_DATETIME && other->type_num == NPY_TIMEDELTA) { + /* + * TODO: We actually currently do allow promotion here. This is + * currently relied on within `np.add(datetime, timedelta)`, + * while for concatenation the cast step will fail. + */ + Py_INCREF(cls); + return cls; + } + return default_builtin_common_dtype(cls, other); +} + + + +static PyArray_DTypeMeta * +object_common_dtype( + PyArray_DTypeMeta *cls, PyArray_DTypeMeta *NPY_UNUSED(other)) +{ + /* + * The object DType is special in that it can represent everything, + * including all potential user DTypes. + * One reason to defer (or error) here might be if the other DType + * does not support scalars so that e.g. `arr1d[0]` returns a 0-D array + * and `arr.astype(object)` would fail. But object casts are special. + */ + Py_INCREF(cls); + return cls; +} + + +/** + * This function takes a PyArray_Descr and replaces its base class with + * a newly created dtype subclass (DTypeMeta instances). + * There are some subtleties that need to be remembered when doing this, + * first for the class objects itself it could be either a HeapType or not. + * Since we are defining the DType from C, we will not make it a HeapType, + * thus making it identical to a typical *static* type (except that we + * malloc it). We could do it the other way, but there seems no reason to + * do so. + * + * The DType instances (the actual dtypes or descriptors), are based on + * prototypes which are passed in. These should not be garbage collected + * and thus Py_TPFLAGS_HAVE_GC is not set. (We could allow this, but than + * would have to allocate a new object, since the GC needs information before + * the actual struct). + * + * The above is the reason why we should works exactly like we would for a + * static type here. + * Otherwise, we blurry the lines between C-defined extension classes + * and Python subclasses. e.g. `class MyInt(int): pass` is very different + * from our `class Float64(np.dtype): pass`, because the latter should not + * be a HeapType and its instances should be exact PyArray_Descr structs. + * + * @param descr The descriptor that should be wrapped. + * @param name The name for the DType, if NULL the type character is used. + * + * @returns 0 on success, -1 on failure. + */ +NPY_NO_EXPORT int +dtypemeta_wrap_legacy_descriptor(PyArray_Descr *descr) +{ + int has_type_set = Py_TYPE(descr) == &PyArrayDescr_Type; + + if (!has_type_set) { + /* Accept if the type was filled in from an existing builtin dtype */ + for (int i = 0; i < NPY_NTYPES; i++) { + PyArray_Descr *builtin = PyArray_DescrFromType(i); + has_type_set = Py_TYPE(descr) == Py_TYPE(builtin); + Py_DECREF(builtin); + if (has_type_set) { + break; + } + } + } + if (!has_type_set) { + PyErr_Format(PyExc_RuntimeError, + "During creation/wrapping of legacy DType, the original class " + "was not of PyArrayDescr_Type (it is replaced in this step). " + "The extension creating a custom DType for type %S must be " + "modified to ensure `Py_TYPE(descr) == &PyArrayDescr_Type` or " + "that of an existing dtype (with the assumption it is just " + "copied over and can be replaced).", + descr->typeobj, Py_TYPE(descr)); + return -1; + } + + /* + * Note: we have no intention of freeing the memory again since this + * behaves identically to static type definition (see comment above). + * This is seems cleaner for the legacy API, in the new API both static + * and heap types are possible (some difficulty arises from the fact that + * these are instances of DTypeMeta and not type). + * In particular our own DTypes can be true static declarations. + * However, this function remains necessary for legacy user dtypes. + */ + + const char *scalar_name = descr->typeobj->tp_name; + /* + * We have to take only the name, and ignore the module to get + * a reasonable __name__, since static types are limited in this regard + * (this is not ideal, but not a big issue in practice). + * This is what Python does to print __name__ for static types. + */ + const char *dot = strrchr(scalar_name, '.'); + if (dot) { + scalar_name = dot + 1; + } + Py_ssize_t name_length = strlen(scalar_name) + 14; + + char *tp_name = PyMem_Malloc(name_length); + if (tp_name == NULL) { + PyErr_NoMemory(); + return -1; + } + + snprintf(tp_name, name_length, "numpy.dtype[%s]", scalar_name); + + NPY_DType_Slots *dt_slots = PyMem_Malloc(sizeof(NPY_DType_Slots)); + if (dt_slots == NULL) { + PyMem_Free(tp_name); + return -1; + } + memset(dt_slots, '\0', sizeof(NPY_DType_Slots)); + + PyArray_DTypeMeta *dtype_class = PyMem_Malloc(sizeof(PyArray_DTypeMeta)); + if (dtype_class == NULL) { + PyMem_Free(tp_name); + PyMem_Free(dt_slots); + return -1; + } + + /* + * Initialize the struct fields identically to static code by copying + * a prototype instances for everything except our own fields which + * vary between the DTypes. + * In particular any Object initialization must be strictly copied from + * the untouched prototype to avoid complexities (e.g. with PyPy). + * Any Type slots need to be fixed before PyType_Ready, although most + * will be inherited automatically there. + */ + static PyArray_DTypeMeta prototype = { + {{ + PyVarObject_HEAD_INIT(&PyArrayDTypeMeta_Type, 0) + .tp_name = NULL, /* set below */ + .tp_basicsize = sizeof(PyArray_Descr), + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_base = &PyArrayDescr_Type, + .tp_new = (newfunc)legacy_dtype_default_new, + },}, + .flags = NPY_DT_LEGACY, + /* Further fields are not common between DTypes */ + }; + memcpy(dtype_class, &prototype, sizeof(PyArray_DTypeMeta)); + /* Fix name of the Type*/ + ((PyTypeObject *)dtype_class)->tp_name = tp_name; + dtype_class->dt_slots = dt_slots; + + /* Let python finish the initialization (probably unnecessary) */ + if (PyType_Ready((PyTypeObject *)dtype_class) < 0) { + Py_DECREF(dtype_class); + return -1; + } + dt_slots->castingimpls = PyDict_New(); + if (dt_slots->castingimpls == NULL) { + Py_DECREF(dtype_class); + return -1; + } + + /* + * Fill DTypeMeta information that varies between DTypes, any variable + * type information would need to be set before PyType_Ready(). + */ + dtype_class->singleton = descr; + Py_INCREF(descr->typeobj); + dtype_class->scalar_type = descr->typeobj; + dtype_class->type_num = descr->type_num; + dt_slots->f = *(descr->f); + + /* Set default functions (correct for most dtypes, override below) */ + dt_slots->default_descr = nonparametric_default_descr; + dt_slots->discover_descr_from_pyobject = ( + nonparametric_discover_descr_from_pyobject); + dt_slots->is_known_scalar_type = python_builtins_are_known_scalar_types; + dt_slots->common_dtype = default_builtin_common_dtype; + dt_slots->common_instance = NULL; + + if (PyTypeNum_ISSIGNED(dtype_class->type_num)) { + /* Convert our scalars (raise on too large unsigned and NaN, etc.) */ + dt_slots->is_known_scalar_type = signed_integers_is_known_scalar_types; + } + + if (PyTypeNum_ISUSERDEF(descr->type_num)) { + dt_slots->common_dtype = legacy_userdtype_common_dtype_function; + } + else if (descr->type_num == NPY_OBJECT) { + dt_slots->common_dtype = object_common_dtype; + } + else if (PyTypeNum_ISDATETIME(descr->type_num)) { + /* Datetimes are flexible, but were not considered previously */ + dtype_class->flags |= NPY_DT_PARAMETRIC; + dt_slots->default_descr = datetime_and_timedelta_default_descr; + dt_slots->discover_descr_from_pyobject = ( + discover_datetime_and_timedelta_from_pyobject); + dt_slots->common_dtype = datetime_common_dtype; + dt_slots->common_instance = datetime_type_promotion; + if (descr->type_num == NPY_DATETIME) { + dt_slots->is_known_scalar_type = datetime_known_scalar_types; + } + } + else if (PyTypeNum_ISFLEXIBLE(descr->type_num)) { + dtype_class->flags |= NPY_DT_PARAMETRIC; + if (descr->type_num == NPY_VOID) { + dt_slots->default_descr = void_default_descr; + dt_slots->discover_descr_from_pyobject = ( + void_discover_descr_from_pyobject); + dt_slots->common_instance = void_common_instance; + } + else { + dt_slots->default_descr = string_and_unicode_default_descr; + dt_slots->is_known_scalar_type = string_known_scalar_types; + dt_slots->discover_descr_from_pyobject = ( + string_discover_descr_from_pyobject); + dt_slots->common_dtype = string_unicode_common_dtype; + dt_slots->common_instance = string_unicode_common_instance; + } + } + + if (_PyArray_MapPyTypeToDType(dtype_class, descr->typeobj, + PyTypeNum_ISUSERDEF(dtype_class->type_num)) < 0) { + Py_DECREF(dtype_class); + return -1; + } + + /* Finally, replace the current class of the descr */ + Py_SET_TYPE(descr, (PyTypeObject *)dtype_class); + + return 0; +} + + +static PyObject * +dtypemeta_get_abstract(PyArray_DTypeMeta *self) { + return PyBool_FromLong(NPY_DT_is_abstract(self)); +} + +static PyObject * +dtypemeta_get_parametric(PyArray_DTypeMeta *self) { + return PyBool_FromLong(NPY_DT_is_parametric(self)); +} + +/* + * Simple exposed information, defined for each DType (class). + */ +static PyGetSetDef dtypemeta_getset[] = { + {"_abstract", (getter)dtypemeta_get_abstract, NULL, NULL, NULL}, + {"_parametric", (getter)dtypemeta_get_parametric, NULL, NULL, NULL}, + {NULL, NULL, NULL, NULL, NULL} +}; + +static PyMemberDef dtypemeta_members[] = { + {"type", + T_OBJECT, offsetof(PyArray_DTypeMeta, scalar_type), READONLY, NULL}, + {NULL, 0, 0, 0, NULL}, +}; + + +NPY_NO_EXPORT PyTypeObject PyArrayDTypeMeta_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "numpy._DTypeMeta", + .tp_basicsize = sizeof(PyArray_DTypeMeta), + .tp_dealloc = (destructor)dtypemeta_dealloc, + /* Types are garbage collected (see dtypemeta_is_gc documentation) */ + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, + .tp_doc = "Preliminary NumPy API: The Type of NumPy DTypes (metaclass)", + .tp_getset = dtypemeta_getset, + .tp_members = dtypemeta_members, + .tp_base = NULL, /* set to PyType_Type at import time */ + .tp_alloc = dtypemeta_alloc, + .tp_init = (initproc)dtypemeta_init, + .tp_new = dtypemeta_new, + .tp_is_gc = dtypemeta_is_gc, + .tp_traverse = (traverseproc)dtypemeta_traverse, +}; diff --git a/numpy/core/src/multiarray/dtypemeta.h b/numpy/core/src/multiarray/dtypemeta.h new file mode 100644 index 000000000000..2a61fe39de37 --- /dev/null +++ b/numpy/core/src/multiarray/dtypemeta.h @@ -0,0 +1,125 @@ +#ifndef NUMPY_CORE_SRC_MULTIARRAY_DTYPEMETA_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_DTYPEMETA_H_ + + +/* DType flags, currently private, since we may just expose functions */ +#define NPY_DT_LEGACY 1 << 0 +#define NPY_DT_ABSTRACT 1 << 1 +#define NPY_DT_PARAMETRIC 1 << 2 + + +typedef PyArray_Descr *(discover_descr_from_pyobject_function)( + PyArray_DTypeMeta *cls, PyObject *obj); + +/* + * Before making this public, we should decide whether it should pass + * the type, or allow looking at the object. A possible use-case: + * `np.array(np.array([0]), dtype=np.ndarray)` + * Could consider arrays that are not `dtype=ndarray` "scalars". + */ +typedef int (is_known_scalar_type_function)( + PyArray_DTypeMeta *cls, PyTypeObject *obj); + +typedef PyArray_Descr *(default_descr_function)(PyArray_DTypeMeta *cls); +typedef PyArray_DTypeMeta *(common_dtype_function)( + PyArray_DTypeMeta *dtype1, PyArray_DTypeMeta *dtype2); +typedef PyArray_Descr *(common_instance_function)( + PyArray_Descr *dtype1, PyArray_Descr *dtype2); + +/* + * TODO: These two functions are currently only used for experimental DType + * API support. Their relation should be "reversed": NumPy should + * always use them internally. + * There are open points about "casting safety" though, e.g. setting + * elements is currently always unsafe. + */ +typedef int(setitemfunction)(PyArray_Descr *, PyObject *, char *); +typedef PyObject *(getitemfunction)(PyArray_Descr *, char *); + + +typedef struct { + /* DType methods, these could be moved into its own struct */ + discover_descr_from_pyobject_function *discover_descr_from_pyobject; + is_known_scalar_type_function *is_known_scalar_type; + default_descr_function *default_descr; + common_dtype_function *common_dtype; + common_instance_function *common_instance; + /* + * Currently only used for experimental user DTypes. + * Typing as `void *` until NumPy itself uses these (directly). + */ + setitemfunction *setitem; + getitemfunction *getitem; + /* + * The casting implementation (ArrayMethod) to convert between two + * instances of this DType, stored explicitly for fast access: + */ + PyObject *within_dtype_castingimpl; + /* + * Dictionary of ArrayMethods representing most possible casts + * (structured and object are exceptions). + * This should potentially become a weak mapping in the future. + */ + PyObject *castingimpls; + + /* + * Storage for `descr->f`, since we may need to allow some customizatoin + * here at least in a transition period and we need to set it on every + * dtype instance for backward compatibility. (Keep this at end) + */ + PyArray_ArrFuncs f; +} NPY_DType_Slots; + + +#define NPY_DTYPE(descr) ((PyArray_DTypeMeta *)Py_TYPE(descr)) +#define NPY_DT_SLOTS(dtype) ((NPY_DType_Slots *)(dtype)->dt_slots) + +#define NPY_DT_is_legacy(dtype) (((dtype)->flags & NPY_DT_LEGACY) != 0) +#define NPY_DT_is_abstract(dtype) (((dtype)->flags & NPY_DT_ABSTRACT) != 0) +#define NPY_DT_is_parametric(dtype) (((dtype)->flags & NPY_DT_PARAMETRIC) != 0) + +/* + * Macros for convenient classmethod calls, since these require + * the DType both for the slot lookup and as first arguments. + * + * (Macros may include NULL checks where appropriate) + */ +#define NPY_DT_CALL_discover_descr_from_pyobject(dtype, obj) \ + NPY_DT_SLOTS(dtype)->discover_descr_from_pyobject(dtype, obj) +#define NPY_DT_CALL_is_known_scalar_type(dtype, obj) \ + (NPY_DT_SLOTS(dtype)->is_known_scalar_type != NULL \ + && NPY_DT_SLOTS(dtype)->is_known_scalar_type(dtype, obj)) +#define NPY_DT_CALL_default_descr(dtype) \ + NPY_DT_SLOTS(dtype)->default_descr(dtype) +#define NPY_DT_CALL_common_dtype(dtype, other) \ + NPY_DT_SLOTS(dtype)->common_dtype(dtype, other) +#define NPY_DT_CALL_getitem(descr, data_ptr) \ + NPY_DT_SLOTS(NPY_DTYPE(descr))->getitem(descr, data_ptr) +#define NPY_DT_CALL_setitem(descr, value, data_ptr) \ + NPY_DT_SLOTS(NPY_DTYPE(descr))->setitem(descr, value, data_ptr) + +/* + * This function will hopefully be phased out or replaced, but was convenient + * for incremental implementation of new DTypes based on DTypeMeta. + * (Error checking is not required for DescrFromType, assuming that the + * type is valid.) + */ +static NPY_INLINE PyArray_DTypeMeta * +PyArray_DTypeFromTypeNum(int typenum) +{ + PyArray_Descr *descr = PyArray_DescrFromType(typenum); + PyArray_DTypeMeta *dtype = NPY_DTYPE(descr); + Py_INCREF(dtype); + Py_DECREF(descr); + return dtype; +} + + +NPY_NO_EXPORT int +python_builtins_are_known_scalar_types( + PyArray_DTypeMeta *cls, PyTypeObject *pytype); + +NPY_NO_EXPORT int +dtypemeta_wrap_legacy_descriptor(PyArray_Descr *dtypem); + +#endif /* NUMPY_CORE_SRC_MULTIARRAY_DTYPEMETA_H_ */ diff --git a/numpy/core/src/multiarray/einsum.c.src b/numpy/core/src/multiarray/einsum.c.src index 33184d99a853..cd1a5898269a 100644 --- a/numpy/core/src/multiarray/einsum.c.src +++ b/numpy/core/src/multiarray/einsum.c.src @@ -9,14 +9,13 @@ */ #define PY_SSIZE_T_CLEAN -#include "Python.h" -#include "structmember.h" +#include <Python.h> +#include <structmember.h> #define NPY_NO_DEPRECATED_API NPY_API_VERSION #define _MULTIARRAYMODULE #include <numpy/npy_common.h> #include <numpy/arrayobject.h> -#include <numpy/halffloat.h> #include <npy_pycompat.h> #include <ctype.h> @@ -25,1757 +24,8 @@ #include "common.h" #include "ctors.h" -#ifdef NPY_HAVE_SSE_INTRINSICS -#define EINSUM_USE_SSE1 1 -#else -#define EINSUM_USE_SSE1 0 -#endif - -/* - * TODO: Only some SSE2 for float64 is implemented. - */ -#ifdef NPY_HAVE_SSE2_INTRINSICS -#define EINSUM_USE_SSE2 1 -#else -#define EINSUM_USE_SSE2 0 -#endif - -#if EINSUM_USE_SSE1 -#include <xmmintrin.h> -#endif - -#if EINSUM_USE_SSE2 -#include <emmintrin.h> -#endif - -#define EINSUM_IS_SSE_ALIGNED(x) ((((npy_intp)x)&0xf) == 0) - -/********** PRINTF DEBUG TRACING **************/ -#define NPY_EINSUM_DBG_TRACING 0 - -#if NPY_EINSUM_DBG_TRACING -#define NPY_EINSUM_DBG_PRINT(s) printf("%s", s); -#define NPY_EINSUM_DBG_PRINT1(s, p1) printf(s, p1); -#define NPY_EINSUM_DBG_PRINT2(s, p1, p2) printf(s, p1, p2); -#define NPY_EINSUM_DBG_PRINT3(s, p1, p2, p3) printf(s); -#else -#define NPY_EINSUM_DBG_PRINT(s) -#define NPY_EINSUM_DBG_PRINT1(s, p1) -#define NPY_EINSUM_DBG_PRINT2(s, p1, p2) -#define NPY_EINSUM_DBG_PRINT3(s, p1, p2, p3) -#endif -/**********************************************/ - -/**begin repeat - * #name = byte, short, int, long, longlong, - * ubyte, ushort, uint, ulong, ulonglong, - * half, float, double, longdouble, - * cfloat, cdouble, clongdouble# - * #type = npy_byte, npy_short, npy_int, npy_long, npy_longlong, - * npy_ubyte, npy_ushort, npy_uint, npy_ulong, npy_ulonglong, - * npy_half, npy_float, npy_double, npy_longdouble, - * npy_cfloat, npy_cdouble, npy_clongdouble# - * #temptype = npy_byte, npy_short, npy_int, npy_long, npy_longlong, - * npy_ubyte, npy_ushort, npy_uint, npy_ulong, npy_ulonglong, - * npy_float, npy_float, npy_double, npy_longdouble, - * npy_float, npy_double, npy_longdouble# - * #to = ,,,,, - * ,,,,, - * npy_float_to_half,,,, - * ,,# - * #from = ,,,,, - * ,,,,, - * npy_half_to_float,,,, - * ,,# - * #complex = 0*5, - * 0*5, - * 0*4, - * 1*3# - * #float32 = 0*5, - * 0*5, - * 0,1,0,0, - * 0*3# - * #float64 = 0*5, - * 0*5, - * 0,0,1,0, - * 0*3# - */ - -/**begin repeat1 - * #nop = 1, 2, 3, 1000# - * #noplabel = one, two, three, any# - */ -static void -@name@_sum_of_products_@noplabel@(int nop, char **dataptr, - npy_intp *strides, npy_intp count) -{ -#if (@nop@ == 1) || (@nop@ <= 3 && !@complex@) - char *data0 = dataptr[0]; - npy_intp stride0 = strides[0]; -#endif -#if (@nop@ == 2 || @nop@ == 3) && !@complex@ - char *data1 = dataptr[1]; - npy_intp stride1 = strides[1]; -#endif -#if (@nop@ == 3) && !@complex@ - char *data2 = dataptr[2]; - npy_intp stride2 = strides[2]; -#endif -#if (@nop@ == 1) || (@nop@ <= 3 && !@complex@) - char *data_out = dataptr[@nop@]; - npy_intp stride_out = strides[@nop@]; -#endif - - NPY_EINSUM_DBG_PRINT1("@name@_sum_of_products_@noplabel@ (%d)\n", (int)count); - - while (count--) { -#if !@complex@ -# if @nop@ == 1 - *(@type@ *)data_out = @to@(@from@(*(@type@ *)data0) + - @from@(*(@type@ *)data_out)); - data0 += stride0; - data_out += stride_out; -# elif @nop@ == 2 - *(@type@ *)data_out = @to@(@from@(*(@type@ *)data0) * - @from@(*(@type@ *)data1) + - @from@(*(@type@ *)data_out)); - data0 += stride0; - data1 += stride1; - data_out += stride_out; -# elif @nop@ == 3 - *(@type@ *)data_out = @to@(@from@(*(@type@ *)data0) * - @from@(*(@type@ *)data1) * - @from@(*(@type@ *)data2) + - @from@(*(@type@ *)data_out)); - data0 += stride0; - data1 += stride1; - data2 += stride2; - data_out += stride_out; -# else - @temptype@ temp = @from@(*(@type@ *)dataptr[0]); - int i; - for (i = 1; i < nop; ++i) { - temp *= @from@(*(@type@ *)dataptr[i]); - } - *(@type@ *)dataptr[nop] = @to@(temp + - @from@(*(@type@ *)dataptr[i])); - for (i = 0; i <= nop; ++i) { - dataptr[i] += strides[i]; - } -# endif -#else /* complex */ -# if @nop@ == 1 - ((@temptype@ *)data_out)[0] = ((@temptype@ *)data0)[0] + - ((@temptype@ *)data_out)[0]; - ((@temptype@ *)data_out)[1] = ((@temptype@ *)data0)[1] + - ((@temptype@ *)data_out)[1]; - data0 += stride0; - data_out += stride_out; -# else -# if @nop@ <= 3 -#define _SUMPROD_NOP @nop@ -# else -#define _SUMPROD_NOP nop -# endif - @temptype@ re, im, tmp; - int i; - re = ((@temptype@ *)dataptr[0])[0]; - im = ((@temptype@ *)dataptr[0])[1]; - for (i = 1; i < _SUMPROD_NOP; ++i) { - tmp = re * ((@temptype@ *)dataptr[i])[0] - - im * ((@temptype@ *)dataptr[i])[1]; - im = re * ((@temptype@ *)dataptr[i])[1] + - im * ((@temptype@ *)dataptr[i])[0]; - re = tmp; - } - ((@temptype@ *)dataptr[_SUMPROD_NOP])[0] = re + - ((@temptype@ *)dataptr[_SUMPROD_NOP])[0]; - ((@temptype@ *)dataptr[_SUMPROD_NOP])[1] = im + - ((@temptype@ *)dataptr[_SUMPROD_NOP])[1]; - - for (i = 0; i <= _SUMPROD_NOP; ++i) { - dataptr[i] += strides[i]; - } -#undef _SUMPROD_NOP -# endif -#endif - } -} - -#if @nop@ == 1 - -static void -@name@_sum_of_products_contig_one(int nop, char **dataptr, - npy_intp *NPY_UNUSED(strides), npy_intp count) -{ - @type@ *data0 = (@type@ *)dataptr[0]; - @type@ *data_out = (@type@ *)dataptr[1]; - - NPY_EINSUM_DBG_PRINT1("@name@_sum_of_products_contig_one (%d)\n", - (int)count); - -/* This is placed before the main loop to make small counts faster */ -finish_after_unrolled_loop: - switch (count) { -/**begin repeat2 - * #i = 6, 5, 4, 3, 2, 1, 0# - */ - case @i@+1: -#if !@complex@ - data_out[@i@] = @to@(@from@(data0[@i@]) + - @from@(data_out[@i@])); -#else - ((@temptype@ *)data_out + 2*@i@)[0] = - ((@temptype@ *)data0 + 2*@i@)[0] + - ((@temptype@ *)data_out + 2*@i@)[0]; - ((@temptype@ *)data_out + 2*@i@)[1] = - ((@temptype@ *)data0 + 2*@i@)[1] + - ((@temptype@ *)data_out + 2*@i@)[1]; -#endif -/**end repeat2**/ - case 0: - return; - } - - /* Unroll the loop by 8 */ - while (count >= 8) { - count -= 8; - -/**begin repeat2 - * #i = 0, 1, 2, 3, 4, 5, 6, 7# - */ -#if !@complex@ - data_out[@i@] = @to@(@from@(data0[@i@]) + - @from@(data_out[@i@])); -#else /* complex */ - ((@temptype@ *)data_out + 2*@i@)[0] = - ((@temptype@ *)data0 + 2*@i@)[0] + - ((@temptype@ *)data_out + 2*@i@)[0]; - ((@temptype@ *)data_out + 2*@i@)[1] = - ((@temptype@ *)data0 + 2*@i@)[1] + - ((@temptype@ *)data_out + 2*@i@)[1]; -#endif -/**end repeat2**/ - data0 += 8; - data_out += 8; - } - - /* Finish off the loop */ - goto finish_after_unrolled_loop; -} - -#elif @nop@ == 2 && !@complex@ - -static void -@name@_sum_of_products_contig_two(int nop, char **dataptr, - npy_intp *NPY_UNUSED(strides), npy_intp count) -{ - @type@ *data0 = (@type@ *)dataptr[0]; - @type@ *data1 = (@type@ *)dataptr[1]; - @type@ *data_out = (@type@ *)dataptr[2]; - -#if EINSUM_USE_SSE1 && @float32@ - __m128 a, b; -#endif - - NPY_EINSUM_DBG_PRINT1("@name@_sum_of_products_contig_two (%d)\n", - (int)count); - -/* This is placed before the main loop to make small counts faster */ -finish_after_unrolled_loop: - switch (count) { -/**begin repeat2 - * #i = 6, 5, 4, 3, 2, 1, 0# - */ - case @i@+1: - data_out[@i@] = @to@(@from@(data0[@i@]) * - @from@(data1[@i@]) + - @from@(data_out[@i@])); -/**end repeat2**/ - case 0: - return; - } - -#if EINSUM_USE_SSE1 && @float32@ - /* Use aligned instructions if possible */ - if (EINSUM_IS_SSE_ALIGNED(data0) && EINSUM_IS_SSE_ALIGNED(data1) && - EINSUM_IS_SSE_ALIGNED(data_out)) { - /* Unroll the loop by 8 */ - while (count >= 8) { - count -= 8; - -/**begin repeat2 - * #i = 0, 4# - */ - a = _mm_mul_ps(_mm_load_ps(data0+@i@), _mm_load_ps(data1+@i@)); - b = _mm_add_ps(a, _mm_load_ps(data_out+@i@)); - _mm_store_ps(data_out+@i@, b); -/**end repeat2**/ - data0 += 8; - data1 += 8; - data_out += 8; - } - - /* Finish off the loop */ - goto finish_after_unrolled_loop; - } -#endif - - /* Unroll the loop by 8 */ - while (count >= 8) { - count -= 8; - -#if EINSUM_USE_SSE1 && @float32@ -/**begin repeat2 - * #i = 0, 4# - */ - a = _mm_mul_ps(_mm_loadu_ps(data0+@i@), _mm_loadu_ps(data1+@i@)); - b = _mm_add_ps(a, _mm_loadu_ps(data_out+@i@)); - _mm_storeu_ps(data_out+@i@, b); -/**end repeat2**/ -#else -/**begin repeat2 - * #i = 0, 1, 2, 3, 4, 5, 6, 7# - */ - data_out[@i@] = @to@(@from@(data0[@i@]) * - @from@(data1[@i@]) + - @from@(data_out[@i@])); -/**end repeat2**/ -#endif - data0 += 8; - data1 += 8; - data_out += 8; - } - - /* Finish off the loop */ - goto finish_after_unrolled_loop; -} - -/* Some extra specializations for the two operand case */ -static void -@name@_sum_of_products_stride0_contig_outcontig_two(int nop, char **dataptr, - npy_intp *NPY_UNUSED(strides), npy_intp count) -{ - @temptype@ value0 = @from@(*(@type@ *)dataptr[0]); - @type@ *data1 = (@type@ *)dataptr[1]; - @type@ *data_out = (@type@ *)dataptr[2]; - -#if EINSUM_USE_SSE1 && @float32@ - __m128 a, b, value0_sse; -#elif EINSUM_USE_SSE2 && @float64@ - __m128d a, b, value0_sse; -#endif - - NPY_EINSUM_DBG_PRINT1("@name@_sum_of_products_stride0_contig_outcontig_two (%d)\n", - (int)count); - -/* This is placed before the main loop to make small counts faster */ -finish_after_unrolled_loop: - switch (count) { -/**begin repeat2 - * #i = 6, 5, 4, 3, 2, 1, 0# - */ - case @i@+1: - data_out[@i@] = @to@(value0 * - @from@(data1[@i@]) + - @from@(data_out[@i@])); -/**end repeat2**/ - case 0: - return; - } - -#if EINSUM_USE_SSE1 && @float32@ - value0_sse = _mm_set_ps1(value0); - - /* Use aligned instructions if possible */ - if (EINSUM_IS_SSE_ALIGNED(data1) && EINSUM_IS_SSE_ALIGNED(data_out)) { - /* Unroll the loop by 8 */ - while (count >= 8) { - count -= 8; - -/**begin repeat2 - * #i = 0, 4# - */ - a = _mm_mul_ps(value0_sse, _mm_load_ps(data1+@i@)); - b = _mm_add_ps(a, _mm_load_ps(data_out+@i@)); - _mm_store_ps(data_out+@i@, b); -/**end repeat2**/ - data1 += 8; - data_out += 8; - } - - /* Finish off the loop */ - if (count > 0) { - goto finish_after_unrolled_loop; - } - else { - return; - } - } -#elif EINSUM_USE_SSE2 && @float64@ - value0_sse = _mm_set1_pd(value0); - - /* Use aligned instructions if possible */ - if (EINSUM_IS_SSE_ALIGNED(data1) && EINSUM_IS_SSE_ALIGNED(data_out)) { - /* Unroll the loop by 8 */ - while (count >= 8) { - count -= 8; - -/**begin repeat2 - * #i = 0, 2, 4, 6# - */ - a = _mm_mul_pd(value0_sse, _mm_load_pd(data1+@i@)); - b = _mm_add_pd(a, _mm_load_pd(data_out+@i@)); - _mm_store_pd(data_out+@i@, b); -/**end repeat2**/ - data1 += 8; - data_out += 8; - } - - /* Finish off the loop */ - if (count > 0) { - goto finish_after_unrolled_loop; - } - else { - return; - } - } -#endif - - /* Unroll the loop by 8 */ - while (count >= 8) { - count -= 8; - -#if EINSUM_USE_SSE1 && @float32@ -/**begin repeat2 - * #i = 0, 4# - */ - a = _mm_mul_ps(value0_sse, _mm_loadu_ps(data1+@i@)); - b = _mm_add_ps(a, _mm_loadu_ps(data_out+@i@)); - _mm_storeu_ps(data_out+@i@, b); -/**end repeat2**/ -#elif EINSUM_USE_SSE2 && @float64@ -/**begin repeat2 - * #i = 0, 2, 4, 6# - */ - a = _mm_mul_pd(value0_sse, _mm_loadu_pd(data1+@i@)); - b = _mm_add_pd(a, _mm_loadu_pd(data_out+@i@)); - _mm_storeu_pd(data_out+@i@, b); -/**end repeat2**/ -#else -/**begin repeat2 - * #i = 0, 1, 2, 3, 4, 5, 6, 7# - */ - data_out[@i@] = @to@(value0 * - @from@(data1[@i@]) + - @from@(data_out[@i@])); -/**end repeat2**/ -#endif - data1 += 8; - data_out += 8; - } - - /* Finish off the loop */ - if (count > 0) { - goto finish_after_unrolled_loop; - } -} - -static void -@name@_sum_of_products_contig_stride0_outcontig_two(int nop, char **dataptr, - npy_intp *NPY_UNUSED(strides), npy_intp count) -{ - @type@ *data0 = (@type@ *)dataptr[0]; - @temptype@ value1 = @from@(*(@type@ *)dataptr[1]); - @type@ *data_out = (@type@ *)dataptr[2]; - -#if EINSUM_USE_SSE1 && @float32@ - __m128 a, b, value1_sse; -#endif - - NPY_EINSUM_DBG_PRINT1("@name@_sum_of_products_contig_stride0_outcontig_two (%d)\n", - (int)count); - -/* This is placed before the main loop to make small counts faster */ -finish_after_unrolled_loop: - switch (count) { -/**begin repeat2 - * #i = 6, 5, 4, 3, 2, 1, 0# - */ - case @i@+1: - data_out[@i@] = @to@(@from@(data0[@i@])* - value1 + - @from@(data_out[@i@])); -/**end repeat2**/ - case 0: - return; - } - -#if EINSUM_USE_SSE1 && @float32@ - value1_sse = _mm_set_ps1(value1); - - /* Use aligned instructions if possible */ - if (EINSUM_IS_SSE_ALIGNED(data0) && EINSUM_IS_SSE_ALIGNED(data_out)) { - /* Unroll the loop by 8 */ - while (count >= 8) { - count -= 8; - -/**begin repeat2 - * #i = 0, 4# - */ - a = _mm_mul_ps(_mm_load_ps(data0+@i@), value1_sse); - b = _mm_add_ps(a, _mm_load_ps(data_out+@i@)); - _mm_store_ps(data_out+@i@, b); -/**end repeat2**/ - data0 += 8; - data_out += 8; - } - - /* Finish off the loop */ - goto finish_after_unrolled_loop; - } -#endif - - /* Unroll the loop by 8 */ - while (count >= 8) { - count -= 8; - -#if EINSUM_USE_SSE1 && @float32@ -/**begin repeat2 - * #i = 0, 4# - */ - a = _mm_mul_ps(_mm_loadu_ps(data0+@i@), value1_sse); - b = _mm_add_ps(a, _mm_loadu_ps(data_out+@i@)); - _mm_storeu_ps(data_out+@i@, b); -/**end repeat2**/ -#else -/**begin repeat2 - * #i = 0, 1, 2, 3, 4, 5, 6, 7# - */ - data_out[@i@] = @to@(@from@(data0[@i@])* - value1 + - @from@(data_out[@i@])); -/**end repeat2**/ -#endif - data0 += 8; - data_out += 8; - } - - /* Finish off the loop */ - goto finish_after_unrolled_loop; -} - -static void -@name@_sum_of_products_contig_contig_outstride0_two(int nop, char **dataptr, - npy_intp *NPY_UNUSED(strides), npy_intp count) -{ - @type@ *data0 = (@type@ *)dataptr[0]; - @type@ *data1 = (@type@ *)dataptr[1]; - @temptype@ accum = 0; - -#if EINSUM_USE_SSE1 && @float32@ - __m128 a, accum_sse = _mm_setzero_ps(); -#elif EINSUM_USE_SSE2 && @float64@ - __m128d a, accum_sse = _mm_setzero_pd(); -#endif - - NPY_EINSUM_DBG_PRINT1("@name@_sum_of_products_contig_contig_outstride0_two (%d)\n", - (int)count); - -/* This is placed before the main loop to make small counts faster */ -finish_after_unrolled_loop: - switch (count) { -/**begin repeat2 - * #i = 6, 5, 4, 3, 2, 1, 0# - */ - case @i@+1: - accum += @from@(data0[@i@]) * @from@(data1[@i@]); -/**end repeat2**/ - case 0: - *(@type@ *)dataptr[2] = @to@(@from@(*(@type@ *)dataptr[2]) + accum); - return; - } - -#if EINSUM_USE_SSE1 && @float32@ - /* Use aligned instructions if possible */ - if (EINSUM_IS_SSE_ALIGNED(data0) && EINSUM_IS_SSE_ALIGNED(data1)) { - /* Unroll the loop by 8 */ - while (count >= 8) { - count -= 8; - - _mm_prefetch(data0 + 512, _MM_HINT_T0); - _mm_prefetch(data1 + 512, _MM_HINT_T0); - -/**begin repeat2 - * #i = 0, 4# - */ - /* - * NOTE: This accumulation changes the order, so will likely - * produce slightly different results. - */ - a = _mm_mul_ps(_mm_load_ps(data0+@i@), _mm_load_ps(data1+@i@)); - accum_sse = _mm_add_ps(accum_sse, a); -/**end repeat2**/ - data0 += 8; - data1 += 8; - } - - /* Add the four SSE values and put in accum */ - a = _mm_shuffle_ps(accum_sse, accum_sse, _MM_SHUFFLE(2,3,0,1)); - accum_sse = _mm_add_ps(a, accum_sse); - a = _mm_shuffle_ps(accum_sse, accum_sse, _MM_SHUFFLE(1,0,3,2)); - accum_sse = _mm_add_ps(a, accum_sse); - _mm_store_ss(&accum, accum_sse); - - /* Finish off the loop */ - goto finish_after_unrolled_loop; - } -#elif EINSUM_USE_SSE2 && @float64@ - /* Use aligned instructions if possible */ - if (EINSUM_IS_SSE_ALIGNED(data0) && EINSUM_IS_SSE_ALIGNED(data1)) { - /* Unroll the loop by 8 */ - while (count >= 8) { - count -= 8; - - _mm_prefetch(data0 + 512, _MM_HINT_T0); - _mm_prefetch(data1 + 512, _MM_HINT_T0); - -/**begin repeat2 - * #i = 0, 2, 4, 6# - */ - /* - * NOTE: This accumulation changes the order, so will likely - * produce slightly different results. - */ - a = _mm_mul_pd(_mm_load_pd(data0+@i@), _mm_load_pd(data1+@i@)); - accum_sse = _mm_add_pd(accum_sse, a); -/**end repeat2**/ - data0 += 8; - data1 += 8; - } - - /* Add the two SSE2 values and put in accum */ - a = _mm_shuffle_pd(accum_sse, accum_sse, _MM_SHUFFLE2(0,1)); - accum_sse = _mm_add_pd(a, accum_sse); - _mm_store_sd(&accum, accum_sse); - - /* Finish off the loop */ - goto finish_after_unrolled_loop; - } -#endif - - /* Unroll the loop by 8 */ - while (count >= 8) { - count -= 8; - -#if EINSUM_USE_SSE1 && @float32@ - _mm_prefetch(data0 + 512, _MM_HINT_T0); - _mm_prefetch(data1 + 512, _MM_HINT_T0); - -/**begin repeat2 - * #i = 0, 4# - */ - /* - * NOTE: This accumulation changes the order, so will likely - * produce slightly different results. - */ - a = _mm_mul_ps(_mm_loadu_ps(data0+@i@), _mm_loadu_ps(data1+@i@)); - accum_sse = _mm_add_ps(accum_sse, a); -/**end repeat2**/ -#elif EINSUM_USE_SSE2 && @float64@ - _mm_prefetch(data0 + 512, _MM_HINT_T0); - _mm_prefetch(data1 + 512, _MM_HINT_T0); - -/**begin repeat2 - * #i = 0, 2, 4, 6# - */ - /* - * NOTE: This accumulation changes the order, so will likely - * produce slightly different results. - */ - a = _mm_mul_pd(_mm_loadu_pd(data0+@i@), _mm_loadu_pd(data1+@i@)); - accum_sse = _mm_add_pd(accum_sse, a); -/**end repeat2**/ -#else -/**begin repeat2 - * #i = 0, 1, 2, 3, 4, 5, 6, 7# - */ - accum += @from@(data0[@i@]) * @from@(data1[@i@]); -/**end repeat2**/ -#endif - data0 += 8; - data1 += 8; - } - -#if EINSUM_USE_SSE1 && @float32@ - /* Add the four SSE values and put in accum */ - a = _mm_shuffle_ps(accum_sse, accum_sse, _MM_SHUFFLE(2,3,0,1)); - accum_sse = _mm_add_ps(a, accum_sse); - a = _mm_shuffle_ps(accum_sse, accum_sse, _MM_SHUFFLE(1,0,3,2)); - accum_sse = _mm_add_ps(a, accum_sse); - _mm_store_ss(&accum, accum_sse); -#elif EINSUM_USE_SSE2 && @float64@ - /* Add the two SSE2 values and put in accum */ - a = _mm_shuffle_pd(accum_sse, accum_sse, _MM_SHUFFLE2(0,1)); - accum_sse = _mm_add_pd(a, accum_sse); - _mm_store_sd(&accum, accum_sse); -#endif - - /* Finish off the loop */ - goto finish_after_unrolled_loop; -} - -static void -@name@_sum_of_products_stride0_contig_outstride0_two(int nop, char **dataptr, - npy_intp *NPY_UNUSED(strides), npy_intp count) -{ - @temptype@ value0 = @from@(*(@type@ *)dataptr[0]); - @type@ *data1 = (@type@ *)dataptr[1]; - @temptype@ accum = 0; - -#if EINSUM_USE_SSE1 && @float32@ - __m128 a, accum_sse = _mm_setzero_ps(); -#endif - - NPY_EINSUM_DBG_PRINT1("@name@_sum_of_products_stride0_contig_outstride0_two (%d)\n", - (int)count); - -/* This is placed before the main loop to make small counts faster */ -finish_after_unrolled_loop: - switch (count) { -/**begin repeat2 - * #i = 6, 5, 4, 3, 2, 1, 0# - */ - case @i@+1: - accum += @from@(data1[@i@]); -/**end repeat2**/ - case 0: - *(@type@ *)dataptr[2] = @to@(@from@(*(@type@ *)dataptr[2]) + value0 * accum); - return; - } - -#if EINSUM_USE_SSE1 && @float32@ - /* Use aligned instructions if possible */ - if (EINSUM_IS_SSE_ALIGNED(data1)) { - /* Unroll the loop by 8 */ - while (count >= 8) { - count -= 8; - -/**begin repeat2 - * #i = 0, 4# - */ - /* - * NOTE: This accumulation changes the order, so will likely - * produce slightly different results. - */ - accum_sse = _mm_add_ps(accum_sse, _mm_load_ps(data1+@i@)); -/**end repeat2**/ - data1 += 8; - } - -#if EINSUM_USE_SSE1 && @float32@ - /* Add the four SSE values and put in accum */ - a = _mm_shuffle_ps(accum_sse, accum_sse, _MM_SHUFFLE(2,3,0,1)); - accum_sse = _mm_add_ps(a, accum_sse); - a = _mm_shuffle_ps(accum_sse, accum_sse, _MM_SHUFFLE(1,0,3,2)); - accum_sse = _mm_add_ps(a, accum_sse); - _mm_store_ss(&accum, accum_sse); -#endif - - /* Finish off the loop */ - goto finish_after_unrolled_loop; - } -#endif - - /* Unroll the loop by 8 */ - while (count >= 8) { - count -= 8; - -#if EINSUM_USE_SSE1 && @float32@ -/**begin repeat2 - * #i = 0, 4# - */ - /* - * NOTE: This accumulation changes the order, so will likely - * produce slightly different results. - */ - accum_sse = _mm_add_ps(accum_sse, _mm_loadu_ps(data1+@i@)); -/**end repeat2**/ -#else -/**begin repeat2 - * #i = 0, 1, 2, 3, 4, 5, 6, 7# - */ - accum += @from@(data1[@i@]); -/**end repeat2**/ -#endif - data1 += 8; - } - -#if EINSUM_USE_SSE1 && @float32@ - /* Add the four SSE values and put in accum */ - a = _mm_shuffle_ps(accum_sse, accum_sse, _MM_SHUFFLE(2,3,0,1)); - accum_sse = _mm_add_ps(a, accum_sse); - a = _mm_shuffle_ps(accum_sse, accum_sse, _MM_SHUFFLE(1,0,3,2)); - accum_sse = _mm_add_ps(a, accum_sse); - _mm_store_ss(&accum, accum_sse); -#endif - - /* Finish off the loop */ - goto finish_after_unrolled_loop; -} - -static void -@name@_sum_of_products_contig_stride0_outstride0_two(int nop, char **dataptr, - npy_intp *NPY_UNUSED(strides), npy_intp count) -{ - @type@ *data0 = (@type@ *)dataptr[0]; - @temptype@ value1 = @from@(*(@type@ *)dataptr[1]); - @temptype@ accum = 0; - -#if EINSUM_USE_SSE1 && @float32@ - __m128 a, accum_sse = _mm_setzero_ps(); -#endif - - NPY_EINSUM_DBG_PRINT1("@name@_sum_of_products_contig_stride0_outstride0_two (%d)\n", - (int)count); - -/* This is placed before the main loop to make small counts faster */ -finish_after_unrolled_loop: - switch (count) { -/**begin repeat2 - * #i = 6, 5, 4, 3, 2, 1, 0# - */ - case @i@+1: - accum += @from@(data0[@i@]); -/**end repeat2**/ - case 0: - *(@type@ *)dataptr[2] = @to@(@from@(*(@type@ *)dataptr[2]) + accum * value1); - return; - } - -#if EINSUM_USE_SSE1 && @float32@ - /* Use aligned instructions if possible */ - if (EINSUM_IS_SSE_ALIGNED(data0)) { - /* Unroll the loop by 8 */ - while (count >= 8) { - count -= 8; - -/**begin repeat2 - * #i = 0, 4# - */ - /* - * NOTE: This accumulation changes the order, so will likely - * produce slightly different results. - */ - accum_sse = _mm_add_ps(accum_sse, _mm_load_ps(data0+@i@)); -/**end repeat2**/ - data0 += 8; - } - -#if EINSUM_USE_SSE1 && @float32@ - /* Add the four SSE values and put in accum */ - a = _mm_shuffle_ps(accum_sse, accum_sse, _MM_SHUFFLE(2,3,0,1)); - accum_sse = _mm_add_ps(a, accum_sse); - a = _mm_shuffle_ps(accum_sse, accum_sse, _MM_SHUFFLE(1,0,3,2)); - accum_sse = _mm_add_ps(a, accum_sse); - _mm_store_ss(&accum, accum_sse); -#endif - - /* Finish off the loop */ - goto finish_after_unrolled_loop; - } -#endif - - /* Unroll the loop by 8 */ - while (count >= 8) { - count -= 8; - -#if EINSUM_USE_SSE1 && @float32@ -/**begin repeat2 - * #i = 0, 4# - */ - /* - * NOTE: This accumulation changes the order, so will likely - * produce slightly different results. - */ - accum_sse = _mm_add_ps(accum_sse, _mm_loadu_ps(data0+@i@)); -/**end repeat2**/ -#else -/**begin repeat2 - * #i = 0, 1, 2, 3, 4, 5, 6, 7# - */ - accum += @from@(data0[@i@]); -/**end repeat2**/ -#endif - data0 += 8; - } - -#if EINSUM_USE_SSE1 && @float32@ - /* Add the four SSE values and put in accum */ - a = _mm_shuffle_ps(accum_sse, accum_sse, _MM_SHUFFLE(2,3,0,1)); - accum_sse = _mm_add_ps(a, accum_sse); - a = _mm_shuffle_ps(accum_sse, accum_sse, _MM_SHUFFLE(1,0,3,2)); - accum_sse = _mm_add_ps(a, accum_sse); - _mm_store_ss(&accum, accum_sse); -#endif - - /* Finish off the loop */ - goto finish_after_unrolled_loop; -} - -#elif @nop@ == 3 && !@complex@ - -static void -@name@_sum_of_products_contig_three(int nop, char **dataptr, - npy_intp *NPY_UNUSED(strides), npy_intp count) -{ - @type@ *data0 = (@type@ *)dataptr[0]; - @type@ *data1 = (@type@ *)dataptr[1]; - @type@ *data2 = (@type@ *)dataptr[2]; - @type@ *data_out = (@type@ *)dataptr[3]; - - /* Unroll the loop by 8 */ - while (count >= 8) { - count -= 8; - -/**begin repeat2 - * #i = 0, 1, 2, 3, 4, 5, 6, 7# - */ - data_out[@i@] = @to@(@from@(data0[@i@]) * - @from@(data1[@i@]) * - @from@(data2[@i@]) + - @from@(data_out[@i@])); -/**end repeat2**/ - data0 += 8; - data1 += 8; - data2 += 8; - data_out += 8; - } - - /* Finish off the loop */ - -/**begin repeat2 - * #i = 0, 1, 2, 3, 4, 5, 6, 7# - */ - if (count-- == 0) { - return; - } - data_out[@i@] = @to@(@from@(data0[@i@]) * - @from@(data1[@i@]) * - @from@(data2[@i@]) + - @from@(data_out[@i@])); -/**end repeat2**/ -} - -#else /* @nop@ > 3 || @complex */ - -static void -@name@_sum_of_products_contig_@noplabel@(int nop, char **dataptr, - npy_intp *NPY_UNUSED(strides), npy_intp count) -{ - NPY_EINSUM_DBG_PRINT1("@name@_sum_of_products_contig_@noplabel@ (%d)\n", - (int)count); - - while (count--) { -#if !@complex@ - @temptype@ temp = @from@(*(@type@ *)dataptr[0]); - int i; - for (i = 1; i < nop; ++i) { - temp *= @from@(*(@type@ *)dataptr[i]); - } - *(@type@ *)dataptr[nop] = @to@(temp + - @from@(*(@type@ *)dataptr[i])); - for (i = 0; i <= nop; ++i) { - dataptr[i] += sizeof(@type@); - } -#else /* complex */ -# if @nop@ <= 3 -# define _SUMPROD_NOP @nop@ -# else -# define _SUMPROD_NOP nop -# endif - @temptype@ re, im, tmp; - int i; - re = ((@temptype@ *)dataptr[0])[0]; - im = ((@temptype@ *)dataptr[0])[1]; - for (i = 1; i < _SUMPROD_NOP; ++i) { - tmp = re * ((@temptype@ *)dataptr[i])[0] - - im * ((@temptype@ *)dataptr[i])[1]; - im = re * ((@temptype@ *)dataptr[i])[1] + - im * ((@temptype@ *)dataptr[i])[0]; - re = tmp; - } - ((@temptype@ *)dataptr[_SUMPROD_NOP])[0] = re + - ((@temptype@ *)dataptr[_SUMPROD_NOP])[0]; - ((@temptype@ *)dataptr[_SUMPROD_NOP])[1] = im + - ((@temptype@ *)dataptr[_SUMPROD_NOP])[1]; - - for (i = 0; i <= _SUMPROD_NOP; ++i) { - dataptr[i] += sizeof(@type@); - } -# undef _SUMPROD_NOP -#endif - } -} - -#endif /* functions for various @nop@ */ - -#if @nop@ == 1 - -static void -@name@_sum_of_products_contig_outstride0_one(int nop, char **dataptr, - npy_intp *strides, npy_intp count) -{ -#if @complex@ - @temptype@ accum_re = 0, accum_im = 0; - @temptype@ *data0 = (@temptype@ *)dataptr[0]; -#else - @temptype@ accum = 0; - @type@ *data0 = (@type@ *)dataptr[0]; -#endif - -#if EINSUM_USE_SSE1 && @float32@ - __m128 a, accum_sse = _mm_setzero_ps(); -#elif EINSUM_USE_SSE2 && @float64@ - __m128d a, accum_sse = _mm_setzero_pd(); -#endif - - - NPY_EINSUM_DBG_PRINT1("@name@_sum_of_products_contig_outstride0_one (%d)\n", - (int)count); - -/* This is placed before the main loop to make small counts faster */ -finish_after_unrolled_loop: - switch (count) { -/**begin repeat2 - * #i = 6, 5, 4, 3, 2, 1, 0# - */ - case @i@+1: -#if !@complex@ - accum += @from@(data0[@i@]); -#else /* complex */ - accum_re += data0[2*@i@+0]; - accum_im += data0[2*@i@+1]; -#endif -/**end repeat2**/ - case 0: -#if @complex@ - ((@temptype@ *)dataptr[1])[0] += accum_re; - ((@temptype@ *)dataptr[1])[1] += accum_im; -#else - *((@type@ *)dataptr[1]) = @to@(accum + - @from@(*((@type@ *)dataptr[1]))); -#endif - return; - } - -#if EINSUM_USE_SSE1 && @float32@ - /* Use aligned instructions if possible */ - if (EINSUM_IS_SSE_ALIGNED(data0)) { - /* Unroll the loop by 8 */ - while (count >= 8) { - count -= 8; - - _mm_prefetch(data0 + 512, _MM_HINT_T0); - -/**begin repeat2 - * #i = 0, 4# - */ - /* - * NOTE: This accumulation changes the order, so will likely - * produce slightly different results. - */ - accum_sse = _mm_add_ps(accum_sse, _mm_load_ps(data0+@i@)); -/**end repeat2**/ - data0 += 8; - } - - /* Add the four SSE values and put in accum */ - a = _mm_shuffle_ps(accum_sse, accum_sse, _MM_SHUFFLE(2,3,0,1)); - accum_sse = _mm_add_ps(a, accum_sse); - a = _mm_shuffle_ps(accum_sse, accum_sse, _MM_SHUFFLE(1,0,3,2)); - accum_sse = _mm_add_ps(a, accum_sse); - _mm_store_ss(&accum, accum_sse); - - /* Finish off the loop */ - goto finish_after_unrolled_loop; - } -#elif EINSUM_USE_SSE2 && @float64@ - /* Use aligned instructions if possible */ - if (EINSUM_IS_SSE_ALIGNED(data0)) { - /* Unroll the loop by 8 */ - while (count >= 8) { - count -= 8; - - _mm_prefetch(data0 + 512, _MM_HINT_T0); - -/**begin repeat2 - * #i = 0, 2, 4, 6# - */ - /* - * NOTE: This accumulation changes the order, so will likely - * produce slightly different results. - */ - accum_sse = _mm_add_pd(accum_sse, _mm_load_pd(data0+@i@)); -/**end repeat2**/ - data0 += 8; - } - - /* Add the two SSE2 values and put in accum */ - a = _mm_shuffle_pd(accum_sse, accum_sse, _MM_SHUFFLE2(0,1)); - accum_sse = _mm_add_pd(a, accum_sse); - _mm_store_sd(&accum, accum_sse); - - /* Finish off the loop */ - goto finish_after_unrolled_loop; - } -#endif - - /* Unroll the loop by 8 */ - while (count >= 8) { - count -= 8; - -#if EINSUM_USE_SSE1 && @float32@ - _mm_prefetch(data0 + 512, _MM_HINT_T0); - -/**begin repeat2 - * #i = 0, 4# - */ - /* - * NOTE: This accumulation changes the order, so will likely - * produce slightly different results. - */ - accum_sse = _mm_add_ps(accum_sse, _mm_loadu_ps(data0+@i@)); -/**end repeat2**/ -#elif EINSUM_USE_SSE2 && @float64@ - _mm_prefetch(data0 + 512, _MM_HINT_T0); - -/**begin repeat2 - * #i = 0, 2, 4, 6# - */ - /* - * NOTE: This accumulation changes the order, so will likely - * produce slightly different results. - */ - accum_sse = _mm_add_pd(accum_sse, _mm_loadu_pd(data0+@i@)); -/**end repeat2**/ -#else -/**begin repeat2 - * #i = 0, 1, 2, 3, 4, 5, 6, 7# - */ -# if !@complex@ - accum += @from@(data0[@i@]); -# else /* complex */ - accum_re += data0[2*@i@+0]; - accum_im += data0[2*@i@+1]; -# endif -/**end repeat2**/ -#endif - -#if !@complex@ - data0 += 8; -#else - data0 += 8*2; -#endif - } - -#if EINSUM_USE_SSE1 && @float32@ - /* Add the four SSE values and put in accum */ - a = _mm_shuffle_ps(accum_sse, accum_sse, _MM_SHUFFLE(2,3,0,1)); - accum_sse = _mm_add_ps(a, accum_sse); - a = _mm_shuffle_ps(accum_sse, accum_sse, _MM_SHUFFLE(1,0,3,2)); - accum_sse = _mm_add_ps(a, accum_sse); - _mm_store_ss(&accum, accum_sse); -#elif EINSUM_USE_SSE2 && @float64@ - /* Add the two SSE2 values and put in accum */ - a = _mm_shuffle_pd(accum_sse, accum_sse, _MM_SHUFFLE2(0,1)); - accum_sse = _mm_add_pd(a, accum_sse); - _mm_store_sd(&accum, accum_sse); -#endif - - /* Finish off the loop */ - goto finish_after_unrolled_loop; -} - -#endif /* @nop@ == 1 */ - -static void -@name@_sum_of_products_outstride0_@noplabel@(int nop, char **dataptr, - npy_intp *strides, npy_intp count) -{ -#if @complex@ - @temptype@ accum_re = 0, accum_im = 0; -#else - @temptype@ accum = 0; -#endif - -#if (@nop@ == 1) || (@nop@ <= 3 && !@complex@) - char *data0 = dataptr[0]; - npy_intp stride0 = strides[0]; -#endif -#if (@nop@ == 2 || @nop@ == 3) && !@complex@ - char *data1 = dataptr[1]; - npy_intp stride1 = strides[1]; -#endif -#if (@nop@ == 3) && !@complex@ - char *data2 = dataptr[2]; - npy_intp stride2 = strides[2]; -#endif - - NPY_EINSUM_DBG_PRINT1("@name@_sum_of_products_outstride0_@noplabel@ (%d)\n", - (int)count); - - while (count--) { -#if !@complex@ -# if @nop@ == 1 - accum += @from@(*(@type@ *)data0); - data0 += stride0; -# elif @nop@ == 2 - accum += @from@(*(@type@ *)data0) * - @from@(*(@type@ *)data1); - data0 += stride0; - data1 += stride1; -# elif @nop@ == 3 - accum += @from@(*(@type@ *)data0) * - @from@(*(@type@ *)data1) * - @from@(*(@type@ *)data2); - data0 += stride0; - data1 += stride1; - data2 += stride2; -# else - @temptype@ temp = @from@(*(@type@ *)dataptr[0]); - int i; - for (i = 1; i < nop; ++i) { - temp *= @from@(*(@type@ *)dataptr[i]); - } - accum += temp; - for (i = 0; i < nop; ++i) { - dataptr[i] += strides[i]; - } -# endif -#else /* complex */ -# if @nop@ == 1 - accum_re += ((@temptype@ *)data0)[0]; - accum_im += ((@temptype@ *)data0)[1]; - data0 += stride0; -# else -# if @nop@ <= 3 -#define _SUMPROD_NOP @nop@ -# else -#define _SUMPROD_NOP nop -# endif - @temptype@ re, im, tmp; - int i; - re = ((@temptype@ *)dataptr[0])[0]; - im = ((@temptype@ *)dataptr[0])[1]; - for (i = 1; i < _SUMPROD_NOP; ++i) { - tmp = re * ((@temptype@ *)dataptr[i])[0] - - im * ((@temptype@ *)dataptr[i])[1]; - im = re * ((@temptype@ *)dataptr[i])[1] + - im * ((@temptype@ *)dataptr[i])[0]; - re = tmp; - } - accum_re += re; - accum_im += im; - for (i = 0; i < _SUMPROD_NOP; ++i) { - dataptr[i] += strides[i]; - } -#undef _SUMPROD_NOP -# endif -#endif - } - -#if @complex@ -# if @nop@ <= 3 - ((@temptype@ *)dataptr[@nop@])[0] += accum_re; - ((@temptype@ *)dataptr[@nop@])[1] += accum_im; -# else - ((@temptype@ *)dataptr[nop])[0] += accum_re; - ((@temptype@ *)dataptr[nop])[1] += accum_im; -# endif -#else -# if @nop@ <= 3 - *((@type@ *)dataptr[@nop@]) = @to@(accum + - @from@(*((@type@ *)dataptr[@nop@]))); -# else - *((@type@ *)dataptr[nop]) = @to@(accum + - @from@(*((@type@ *)dataptr[nop]))); -# endif -#endif - -} - -/**end repeat1**/ - -/**end repeat**/ - - -/* Do OR of ANDs for the boolean type */ - -/**begin repeat - * #nop = 1, 2, 3, 1000# - * #noplabel = one, two, three, any# - */ - -static void -bool_sum_of_products_@noplabel@(int nop, char **dataptr, - npy_intp *strides, npy_intp count) -{ -#if (@nop@ <= 3) - char *data0 = dataptr[0]; - npy_intp stride0 = strides[0]; -#endif -#if (@nop@ == 2 || @nop@ == 3) - char *data1 = dataptr[1]; - npy_intp stride1 = strides[1]; -#endif -#if (@nop@ == 3) - char *data2 = dataptr[2]; - npy_intp stride2 = strides[2]; -#endif -#if (@nop@ <= 3) - char *data_out = dataptr[@nop@]; - npy_intp stride_out = strides[@nop@]; -#endif - - while (count--) { -#if @nop@ == 1 - *(npy_bool *)data_out = *(npy_bool *)data0 || - *(npy_bool *)data_out; - data0 += stride0; - data_out += stride_out; -#elif @nop@ == 2 - *(npy_bool *)data_out = (*(npy_bool *)data0 && - *(npy_bool *)data1) || - *(npy_bool *)data_out; - data0 += stride0; - data1 += stride1; - data_out += stride_out; -#elif @nop@ == 3 - *(npy_bool *)data_out = (*(npy_bool *)data0 && - *(npy_bool *)data1 && - *(npy_bool *)data2) || - *(npy_bool *)data_out; - data0 += stride0; - data1 += stride1; - data2 += stride2; - data_out += stride_out; -#else - npy_bool temp = *(npy_bool *)dataptr[0]; - int i; - for (i = 1; i < nop; ++i) { - temp = temp && *(npy_bool *)dataptr[i]; - } - *(npy_bool *)dataptr[nop] = temp || *(npy_bool *)dataptr[i]; - for (i = 0; i <= nop; ++i) { - dataptr[i] += strides[i]; - } -#endif - } -} - -static void -bool_sum_of_products_contig_@noplabel@(int nop, char **dataptr, - npy_intp *strides, npy_intp count) -{ -#if (@nop@ <= 3) - char *data0 = dataptr[0]; -#endif -#if (@nop@ == 2 || @nop@ == 3) - char *data1 = dataptr[1]; -#endif -#if (@nop@ == 3) - char *data2 = dataptr[2]; -#endif -#if (@nop@ <= 3) - char *data_out = dataptr[@nop@]; -#endif - -#if (@nop@ <= 3) -/* This is placed before the main loop to make small counts faster */ -finish_after_unrolled_loop: - switch (count) { -/**begin repeat1 - * #i = 6, 5, 4, 3, 2, 1, 0# - */ - case @i@+1: -# if @nop@ == 1 - ((npy_bool *)data_out)[@i@] = ((npy_bool *)data0)[@i@] || - ((npy_bool *)data_out)[@i@]; -# elif @nop@ == 2 - ((npy_bool *)data_out)[@i@] = - (((npy_bool *)data0)[@i@] && - ((npy_bool *)data1)[@i@]) || - ((npy_bool *)data_out)[@i@]; -# elif @nop@ == 3 - ((npy_bool *)data_out)[@i@] = - (((npy_bool *)data0)[@i@] && - ((npy_bool *)data1)[@i@] && - ((npy_bool *)data2)[@i@]) || - ((npy_bool *)data_out)[@i@]; -# endif -/**end repeat1**/ - case 0: - return; - } -#endif - -/* Unroll the loop by 8 for fixed-size nop */ -#if (@nop@ <= 3) - while (count >= 8) { - count -= 8; -#else - while (count--) { -#endif - -# if @nop@ == 1 -/**begin repeat1 - * #i = 0, 1, 2, 3, 4, 5, 6, 7# - */ - *((npy_bool *)data_out + @i@) = (*((npy_bool *)data0 + @i@)) || - (*((npy_bool *)data_out + @i@)); -/**end repeat1**/ - data0 += 8*sizeof(npy_bool); - data_out += 8*sizeof(npy_bool); -# elif @nop@ == 2 -/**begin repeat1 - * #i = 0, 1, 2, 3, 4, 5, 6, 7# - */ - *((npy_bool *)data_out + @i@) = - ((*((npy_bool *)data0 + @i@)) && - (*((npy_bool *)data1 + @i@))) || - (*((npy_bool *)data_out + @i@)); -/**end repeat1**/ - data0 += 8*sizeof(npy_bool); - data1 += 8*sizeof(npy_bool); - data_out += 8*sizeof(npy_bool); -# elif @nop@ == 3 -/**begin repeat1 - * #i = 0, 1, 2, 3, 4, 5, 6, 7# - */ - *((npy_bool *)data_out + @i@) = - ((*((npy_bool *)data0 + @i@)) && - (*((npy_bool *)data1 + @i@)) && - (*((npy_bool *)data2 + @i@))) || - (*((npy_bool *)data_out + @i@)); -/**end repeat1**/ - data0 += 8*sizeof(npy_bool); - data1 += 8*sizeof(npy_bool); - data2 += 8*sizeof(npy_bool); - data_out += 8*sizeof(npy_bool); -# else - npy_bool temp = *(npy_bool *)dataptr[0]; - int i; - for (i = 1; i < nop; ++i) { - temp = temp && *(npy_bool *)dataptr[i]; - } - *(npy_bool *)dataptr[nop] = temp || *(npy_bool *)dataptr[i]; - for (i = 0; i <= nop; ++i) { - dataptr[i] += sizeof(npy_bool); - } -# endif - } - - /* If the loop was unrolled, we need to finish it off */ -#if (@nop@ <= 3) - goto finish_after_unrolled_loop; -#endif -} - -static void -bool_sum_of_products_outstride0_@noplabel@(int nop, char **dataptr, - npy_intp *strides, npy_intp count) -{ - npy_bool accum = 0; - -#if (@nop@ <= 3) - char *data0 = dataptr[0]; - npy_intp stride0 = strides[0]; -#endif -#if (@nop@ == 2 || @nop@ == 3) - char *data1 = dataptr[1]; - npy_intp stride1 = strides[1]; -#endif -#if (@nop@ == 3) - char *data2 = dataptr[2]; - npy_intp stride2 = strides[2]; -#endif - - while (count--) { -#if @nop@ == 1 - accum = *(npy_bool *)data0 || accum; - data0 += stride0; -#elif @nop@ == 2 - accum = (*(npy_bool *)data0 && *(npy_bool *)data1) || accum; - data0 += stride0; - data1 += stride1; -#elif @nop@ == 3 - accum = (*(npy_bool *)data0 && - *(npy_bool *)data1 && - *(npy_bool *)data2) || accum; - data0 += stride0; - data1 += stride1; - data2 += stride2; -#else - npy_bool temp = *(npy_bool *)dataptr[0]; - int i; - for (i = 1; i < nop; ++i) { - temp = temp && *(npy_bool *)dataptr[i]; - } - accum = temp || accum; - for (i = 0; i <= nop; ++i) { - dataptr[i] += strides[i]; - } -#endif - } - -# if @nop@ <= 3 - *((npy_bool *)dataptr[@nop@]) = accum || *((npy_bool *)dataptr[@nop@]); -# else - *((npy_bool *)dataptr[nop]) = accum || *((npy_bool *)dataptr[nop]); -# endif -} - -/**end repeat**/ - -typedef void (*sum_of_products_fn)(int, char **, npy_intp *, npy_intp); - -/* These tables need to match up with the type enum */ -static sum_of_products_fn -_contig_outstride0_unary_specialization_table[NPY_NTYPES] = { -/**begin repeat - * #name = bool, - * byte, ubyte, - * short, ushort, - * int, uint, - * long, ulong, - * longlong, ulonglong, - * float, double, longdouble, - * cfloat, cdouble, clongdouble, - * object, string, unicode, void, - * datetime, timedelta, half# - * #use = 0, - * 1, 1, - * 1, 1, - * 1, 1, - * 1, 1, - * 1, 1, - * 1, 1, 1, - * 1, 1, 1, - * 0, 0, 0, 0, - * 0, 0, 1# - */ -#if @use@ - &@name@_sum_of_products_contig_outstride0_one, -#else - NULL, -#endif -/**end repeat**/ -}; /* End of _contig_outstride0_unary_specialization_table */ - -static sum_of_products_fn _binary_specialization_table[NPY_NTYPES][5] = { -/**begin repeat - * #name = bool, - * byte, ubyte, - * short, ushort, - * int, uint, - * long, ulong, - * longlong, ulonglong, - * float, double, longdouble, - * cfloat, cdouble, clongdouble, - * object, string, unicode, void, - * datetime, timedelta, half# - * #use = 0, - * 1, 1, - * 1, 1, - * 1, 1, - * 1, 1, - * 1, 1, - * 1, 1, 1, - * 0, 0, 0, - * 0, 0, 0, 0, - * 0, 0, 1# - */ -#if @use@ -{ - &@name@_sum_of_products_stride0_contig_outstride0_two, - &@name@_sum_of_products_stride0_contig_outcontig_two, - &@name@_sum_of_products_contig_stride0_outstride0_two, - &@name@_sum_of_products_contig_stride0_outcontig_two, - &@name@_sum_of_products_contig_contig_outstride0_two, -}, -#else - {NULL, NULL, NULL, NULL, NULL}, -#endif -/**end repeat**/ -}; /* End of _binary_specialization_table */ - -static sum_of_products_fn _outstride0_specialized_table[NPY_NTYPES][4] = { -/**begin repeat - * #name = bool, - * byte, ubyte, - * short, ushort, - * int, uint, - * long, ulong, - * longlong, ulonglong, - * float, double, longdouble, - * cfloat, cdouble, clongdouble, - * object, string, unicode, void, - * datetime, timedelta, half# - * #use = 1, - * 1, 1, - * 1, 1, - * 1, 1, - * 1, 1, - * 1, 1, - * 1, 1, 1, - * 1, 1, 1, - * 0, 0, 0, 0, - * 0, 0, 1# - */ -#if @use@ -{ - &@name@_sum_of_products_outstride0_any, - &@name@_sum_of_products_outstride0_one, - &@name@_sum_of_products_outstride0_two, - &@name@_sum_of_products_outstride0_three -}, -#else - {NULL, NULL, NULL, NULL}, -#endif -/**end repeat**/ -}; /* End of _outstride0_specialized_table */ - -static sum_of_products_fn _allcontig_specialized_table[NPY_NTYPES][4] = { -/**begin repeat - * #name = bool, - * byte, ubyte, - * short, ushort, - * int, uint, - * long, ulong, - * longlong, ulonglong, - * float, double, longdouble, - * cfloat, cdouble, clongdouble, - * object, string, unicode, void, - * datetime, timedelta, half# - * #use = 1, - * 1, 1, - * 1, 1, - * 1, 1, - * 1, 1, - * 1, 1, - * 1, 1, 1, - * 1, 1, 1, - * 0, 0, 0, 0, - * 0, 0, 1# - */ -#if @use@ -{ - &@name@_sum_of_products_contig_any, - &@name@_sum_of_products_contig_one, - &@name@_sum_of_products_contig_two, - &@name@_sum_of_products_contig_three -}, -#else - {NULL, NULL, NULL, NULL}, -#endif -/**end repeat**/ -}; /* End of _allcontig_specialized_table */ - -static sum_of_products_fn _unspecialized_table[NPY_NTYPES][4] = { -/**begin repeat - * #name = bool, - * byte, ubyte, - * short, ushort, - * int, uint, - * long, ulong, - * longlong, ulonglong, - * float, double, longdouble, - * cfloat, cdouble, clongdouble, - * object, string, unicode, void, - * datetime, timedelta, half# - * #use = 1, - * 1, 1, - * 1, 1, - * 1, 1, - * 1, 1, - * 1, 1, - * 1, 1, 1, - * 1, 1, 1, - * 0, 0, 0, 0, - * 0, 0, 1# - */ -#if @use@ -{ - &@name@_sum_of_products_any, - &@name@_sum_of_products_one, - &@name@_sum_of_products_two, - &@name@_sum_of_products_three -}, -#else - {NULL, NULL, NULL, NULL}, -#endif -/**end repeat**/ -}; /* End of _unnspecialized_table */ - -static sum_of_products_fn -get_sum_of_products_function(int nop, int type_num, - npy_intp itemsize, npy_intp *fixed_strides) -{ - int iop; - - if (type_num >= NPY_NTYPES) { - return NULL; - } - - /* contiguous reduction */ - if (nop == 1 && fixed_strides[0] == itemsize && fixed_strides[1] == 0) { - sum_of_products_fn ret = - _contig_outstride0_unary_specialization_table[type_num]; - if (ret != NULL) { - return ret; - } - } - - /* nop of 2 has more specializations */ - if (nop == 2) { - /* Encode the zero/contiguous strides */ - int code; - code = (fixed_strides[0] == 0) ? 0 : - (fixed_strides[0] == itemsize) ? 2*2*1 : 8; - code += (fixed_strides[1] == 0) ? 0 : - (fixed_strides[1] == itemsize) ? 2*1 : 8; - code += (fixed_strides[2] == 0) ? 0 : - (fixed_strides[2] == itemsize) ? 1 : 8; - if (code >= 2 && code < 7) { - sum_of_products_fn ret = - _binary_specialization_table[type_num][code-2]; - if (ret != NULL) { - return ret; - } - } - } - - /* Inner loop with an output stride of 0 */ - if (fixed_strides[nop] == 0) { - return _outstride0_specialized_table[type_num][nop <= 3 ? nop : 0]; - } - - /* Check for all contiguous */ - for (iop = 0; iop < nop + 1; ++iop) { - if (fixed_strides[iop] != itemsize) { - break; - } - } - - /* Contiguous loop */ - if (iop == nop + 1) { - return _allcontig_specialized_table[type_num][nop <= 3 ? nop : 0]; - } - - /* None of the above specializations caught it, general loops */ - return _unspecialized_table[type_num][nop <= 3 ? nop : 0]; -} +#include "einsum_sumprod.h" +#include "einsum_debug.h" /* @@ -1876,7 +126,7 @@ parse_operand_subscripts(char *subscripts, int length, * later where it matters the char is cast to a signed char. */ for (idim = 0; idim < ndim - 1; ++idim) { - int label = op_labels[idim]; + int label = (signed char)op_labels[idim]; /* If it is a proper label, find any duplicates of it. */ if (label > 0) { /* Search for the next matching label. */ @@ -1992,12 +242,13 @@ parse_output_subscripts(char *subscripts, int length, /* - * When there's just one operand and no reduction, we - * can return a view into op. This calculates the view - * if possible. + * When there's just one operand and no reduction we can return a view + * into 'op'. This calculates the view and stores it in 'ret', if + * possible. Returns -1 on error, 0 otherwise. Note that a 0 return + * does not mean that a view was successfully created. */ static int -get_single_op_view(PyArrayObject *op, int iop, char *labels, +get_single_op_view(PyArrayObject *op, char *labels, int ndim_output, char *output_labels, PyArrayObject **ret) { @@ -2052,13 +303,11 @@ get_single_op_view(PyArrayObject *op, int iop, char *labels, } /* Update the dimensions and strides of the output */ i = out_label - output_labels; - if (new_dims[i] != 0 && - new_dims[i] != PyArray_DIM(op, idim)) { + if (new_dims[i] != 0 && new_dims[i] != PyArray_DIM(op, idim)) { PyErr_Format(PyExc_ValueError, - "dimensions in operand %d for collapsing " + "dimensions in single operand for collapsing " "index '%c' don't match (%d != %d)", - iop, label, (int)new_dims[i], - (int)PyArray_DIM(op, idim)); + label, (int)new_dims[i], (int)PyArray_DIM(op, idim)); return -1; } new_dims[i] = PyArray_DIM(op, idim); @@ -2086,80 +335,116 @@ get_single_op_view(PyArrayObject *op, int iop, char *labels, return 0; } + +/* + * The char type may be either signed or unsigned, we need it to be + * signed here. + */ +static int +_any_labels_are_negative(signed char *labels, int ndim) +{ + int idim; + + for (idim = 0; idim < ndim; ++idim) { + if (labels[idim] < 0) { + return 1; + } + } + + return 0; +} + +/* + * Given the labels for an operand array, returns a view of the array + * with all repeated labels collapsed into a single dimension along + * the corresponding diagonal. The labels are also updated to match + * the dimensions of the new array. If no label is repeated, the + * original array is reference increased and returned unchanged. + */ static PyArrayObject * get_combined_dims_view(PyArrayObject *op, int iop, char *labels) { npy_intp new_strides[NPY_MAXDIMS]; npy_intp new_dims[NPY_MAXDIMS]; - int idim, ndim, icombine, combineoffset; + int idim, icombine; int icombinemap[NPY_MAXDIMS]; - + int ndim = PyArray_NDIM(op); PyArrayObject *ret = NULL; - ndim = PyArray_NDIM(op); + /* A fast path to avoid unnecessary calculations. */ + if (!_any_labels_are_negative((signed char *)labels, ndim)) { + Py_INCREF(op); - /* Initialize the dimensions and strides to zero */ - for (idim = 0; idim < ndim; ++idim) { - new_dims[idim] = 0; - new_strides[idim] = 0; + return op; } - /* Copy the dimensions and strides, except when collapsing */ + /* Combine repeated labels. */ icombine = 0; - for (idim = 0; idim < ndim; ++idim) { + for(idim = 0; idim < ndim; ++idim) { /* * The char type may be either signed or unsigned, we * need it to be signed here. */ int label = (signed char)labels[idim]; - /* If this label says to merge axes, get the actual label */ - if (label < 0) { - combineoffset = label; - label = labels[idim+label]; - } - else { - combineoffset = 0; - if (icombine != idim) { - labels[icombine] = labels[idim]; - } + npy_intp dim = PyArray_DIM(op, idim); + npy_intp stride = PyArray_STRIDE(op, idim); + + /* A label seen for the first time, add it to the op view. */ + if (label >= 0) { + /* + * icombinemap maps dimensions in the original array to + * their position in the combined dimensions view. + */ icombinemap[idim] = icombine; + new_dims[icombine] = dim; + new_strides[icombine] = stride; + ++icombine; } - /* If the label is 0, it's an unlabeled broadcast dimension */ - if (label == 0) { - new_dims[icombine] = PyArray_DIM(op, idim); - new_strides[icombine] = PyArray_STRIDE(op, idim); - } + /* A repeated label, find the original one and merge them. */ else { - /* Update the combined axis dimensions and strides */ - int i = icombinemap[idim + combineoffset]; - if (combineoffset < 0 && new_dims[i] != 0 && - new_dims[i] != PyArray_DIM(op, idim)) { +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wuninitialized" +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif + int i = icombinemap[idim + label]; + + icombinemap[idim] = -1; + if (new_dims[i] != dim) { + char orig_label = labels[idim + label]; PyErr_Format(PyExc_ValueError, - "dimensions in operand %d for collapsing " - "index '%c' don't match (%d != %d)", - iop, label, (int)new_dims[i], - (int)PyArray_DIM(op, idim)); + "dimensions in operand %d for collapsing " + "index '%c' don't match (%d != %d)", + iop, orig_label, (int)new_dims[i], (int)dim); return NULL; } - new_dims[i] = PyArray_DIM(op, idim); - new_strides[i] += PyArray_STRIDE(op, idim); + new_strides[i] += stride; +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif } + } + + /* Overwrite labels to match the new operand view. */ + for (idim = 0; idim < ndim; ++idim) { + int i = icombinemap[idim]; - /* If the label didn't say to combine axes, increment dest i */ - if (combineoffset == 0) { - icombine++; + if (i >= 0) { + labels[i] = labels[idim]; } } - /* The compressed number of dimensions */ + /* The number of dimensions of the combined view. */ ndim = icombine; + /* Create a view of the operand with the compressed dimensions. */ Py_INCREF(PyArray_DESCR(op)); ret = (PyArrayObject *)PyArray_NewFromDescrAndBase( Py_TYPE(op), PyArray_DESCR(op), ndim, new_dims, new_strides, PyArray_DATA(op), PyArray_ISWRITEABLE(op) ? NPY_ARRAY_WRITEABLE : 0, (PyObject *)op, (PyObject *)op); + return ret; } @@ -2620,6 +905,24 @@ PyArray_EinsteinSum(char *subscripts, npy_intp nop, return NULL; } + /* + * If there's just one operand and no output parameter, + * first try remapping the axes to the output to return + * a view instead of a copy. + */ + if (nop == 1 && out == NULL) { + ret = NULL; + + if (get_single_op_view(op_in[0], op_labels[0], ndim_output, + output_labels, &ret) < 0) { + return NULL; + } + + if (ret != NULL) { + return ret; + } + } + /* Set all the op references to NULL */ for (iop = 0; iop < nop; ++iop) { op[iop] = NULL; @@ -2631,53 +934,10 @@ PyArray_EinsteinSum(char *subscripts, npy_intp nop, */ for (iop = 0; iop < nop; ++iop) { char *labels = op_labels[iop]; - int combine, ndim; - - ndim = PyArray_NDIM(op_in[iop]); - /* - * If there's just one operand and no output parameter, - * first try remapping the axes to the output to return - * a view instead of a copy. - */ - if (iop == 0 && nop == 1 && out == NULL) { - ret = NULL; - - if (get_single_op_view(op_in[iop], iop, labels, - ndim_output, output_labels, - &ret) < 0) { - return NULL; - } - - if (ret != NULL) { - return ret; - } - } - - /* - * Check whether any dimensions need to be combined - * - * The char type may be either signed or unsigned, we - * need it to be signed here. - */ - combine = 0; - for (idim = 0; idim < ndim; ++idim) { - if ((signed char)labels[idim] < 0) { - combine = 1; - } - } - - /* If any dimensions are combined, create a view which combines them */ - if (combine) { - op[iop] = get_combined_dims_view(op_in[iop], iop, labels); - if (op[iop] == NULL) { - goto fail; - } - } - /* No combining needed */ - else { - Py_INCREF(op_in[iop]); - op[iop] = op_in[iop]; + op[iop] = get_combined_dims_view(op_in[iop], iop, labels); + if (op[iop] == NULL) { + goto fail; } } @@ -2730,7 +990,7 @@ PyArray_EinsteinSum(char *subscripts, npy_intp nop, op_axes[nop][idim] = idim; } for (idim = ndim_output; idim < ndim_iter; ++idim) { - op_axes[nop][idim] = -1; + op_axes[nop][idim] = NPY_ITER_REDUCTION_AXIS(-1); } /* Set the iterator per-op flags */ @@ -2743,13 +1003,11 @@ PyArray_EinsteinSum(char *subscripts, npy_intp nop, op_flags[nop] = NPY_ITER_READWRITE| NPY_ITER_NBO| NPY_ITER_ALIGNED| - NPY_ITER_ALLOCATE| - NPY_ITER_NO_BROADCAST; + NPY_ITER_ALLOCATE; iter_flags = NPY_ITER_EXTERNAL_LOOP| NPY_ITER_BUFFERED| NPY_ITER_DELAY_BUFALLOC| NPY_ITER_GROWINNER| - NPY_ITER_REDUCE_OK| NPY_ITER_REFS_OK| NPY_ITER_ZEROSIZE_OK; if (out != NULL) { @@ -2767,11 +1025,11 @@ PyArray_EinsteinSum(char *subscripts, npy_intp nop, goto fail; } - /* Initialize the output to all zeros and reset the iterator */ + /* Initialize the output to all zeros */ ret = NpyIter_GetOperandArray(iter)[nop]; - Py_INCREF(ret); - PyArray_AssignZero(ret, NULL); - + if (PyArray_AssignZero(ret, NULL) < 0) { + goto fail; + } /***************************/ /* @@ -2785,16 +1043,12 @@ PyArray_EinsteinSum(char *subscripts, npy_intp nop, case 1: if (ndim == 2) { if (unbuffered_loop_nop1_ndim2(iter) < 0) { - Py_DECREF(ret); - ret = NULL; goto fail; } goto finish; } else if (ndim == 3) { if (unbuffered_loop_nop1_ndim3(iter) < 0) { - Py_DECREF(ret); - ret = NULL; goto fail; } goto finish; @@ -2803,16 +1057,12 @@ PyArray_EinsteinSum(char *subscripts, npy_intp nop, case 2: if (ndim == 2) { if (unbuffered_loop_nop2_ndim2(iter) < 0) { - Py_DECREF(ret); - ret = NULL; goto fail; } goto finish; } else if (ndim == 3) { if (unbuffered_loop_nop2_ndim3(iter) < 0) { - Py_DECREF(ret); - ret = NULL; goto fail; } goto finish; @@ -2823,7 +1073,6 @@ PyArray_EinsteinSum(char *subscripts, npy_intp nop, /***************************/ if (NpyIter_Reset(iter, NULL) != NPY_SUCCEED) { - Py_DECREF(ret); goto fail; } @@ -2845,41 +1094,44 @@ PyArray_EinsteinSum(char *subscripts, npy_intp nop, if (sop == NULL) { PyErr_SetString(PyExc_TypeError, "invalid data type for einsum"); - Py_DECREF(ret); - ret = NULL; } else if (NpyIter_GetIterSize(iter) != 0) { NpyIter_IterNextFunc *iternext; char **dataptr; npy_intp *stride; npy_intp *countptr; + int needs_api; NPY_BEGIN_THREADS_DEF; iternext = NpyIter_GetIterNext(iter, NULL); if (iternext == NULL) { NpyIter_Deallocate(iter); - Py_DECREF(ret); goto fail; } dataptr = NpyIter_GetDataPtrArray(iter); stride = NpyIter_GetInnerStrideArray(iter); countptr = NpyIter_GetInnerLoopSizePtr(iter); + needs_api = NpyIter_IterationNeedsAPI(iter); NPY_BEGIN_THREADS_NDITER(iter); NPY_EINSUM_DBG_PRINT("Einsum loop\n"); do { sop(nop, dataptr, stride, *countptr); - } while(iternext(iter)); + } while (!(needs_api && PyErr_Occurred()) && iternext(iter)); NPY_END_THREADS; /* If the API was needed, it may have thrown an error */ if (NpyIter_IterationNeedsAPI(iter) && PyErr_Occurred()) { - Py_DECREF(ret); - ret = NULL; + goto fail; } } finish: + if (out != NULL) { + ret = out; + } + Py_INCREF(ret); + NpyIter_Deallocate(iter); for (iop = 0; iop < nop; ++iop) { Py_DECREF(op[iop]); diff --git a/numpy/core/src/multiarray/einsum_debug.h b/numpy/core/src/multiarray/einsum_debug.h new file mode 100644 index 000000000000..964964743141 --- /dev/null +++ b/numpy/core/src/multiarray/einsum_debug.h @@ -0,0 +1,28 @@ +/* + * This file provides debug macros used by the other einsum files. + * + * Copyright (c) 2011 by Mark Wiebe (mwwiebe@gmail.com) + * The University of British Columbia + * + * See LICENSE.txt for the license. + */ +#ifndef NUMPY_CORE_SRC_MULTIARRAY_EINSUM_DEBUG_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_EINSUM_DEBUG_H_ + +/********** PRINTF DEBUG TRACING **************/ +#define NPY_EINSUM_DBG_TRACING 0 + +#if NPY_EINSUM_DBG_TRACING +#include <cstdio> +#define NPY_EINSUM_DBG_PRINT(s) printf("%s", s); +#define NPY_EINSUM_DBG_PRINT1(s, p1) printf(s, p1); +#define NPY_EINSUM_DBG_PRINT2(s, p1, p2) printf(s, p1, p2); +#define NPY_EINSUM_DBG_PRINT3(s, p1, p2, p3) printf(s); +#else +#define NPY_EINSUM_DBG_PRINT(s) +#define NPY_EINSUM_DBG_PRINT1(s, p1) +#define NPY_EINSUM_DBG_PRINT2(s, p1, p2) +#define NPY_EINSUM_DBG_PRINT3(s, p1, p2, p3) +#endif + +#endif /* NUMPY_CORE_SRC_MULTIARRAY_EINSUM_DEBUG_H_ */ diff --git a/numpy/core/src/multiarray/einsum_sumprod.c.src b/numpy/core/src/multiarray/einsum_sumprod.c.src new file mode 100644 index 000000000000..3114a58960ef --- /dev/null +++ b/numpy/core/src/multiarray/einsum_sumprod.c.src @@ -0,0 +1,1264 @@ +/* + * This file provides optimized sum of product implementations used internally + * by einsum. + * + * Copyright (c) 2011 by Mark Wiebe (mwwiebe@gmail.com) + * The University of British Columbia + * + * See LICENSE.txt for the license. + */ + +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE + +#include <numpy/npy_common.h> +#include <numpy/ndarraytypes.h> /* for NPY_NTYPES */ +#include <numpy/halffloat.h> + +#include "einsum_sumprod.h" +#include "einsum_debug.h" +#include "simd/simd.h" +#include "common.h" + +// ARM/Neon don't have instructions for aligned memory access +#ifdef NPY_HAVE_NEON + #define EINSUM_IS_ALIGNED(x) 0 +#else + #define EINSUM_IS_ALIGNED(x) npy_is_aligned(x, NPY_SIMD_WIDTH) +#endif + +/**********************************************/ + +/**begin repeat + * #name = byte, short, int, long, longlong, + * ubyte, ushort, uint, ulong, ulonglong, + * half, float, double, longdouble, + * cfloat, cdouble, clongdouble# + * #type = npy_byte, npy_short, npy_int, npy_long, npy_longlong, + * npy_ubyte, npy_ushort, npy_uint, npy_ulong, npy_ulonglong, + * npy_half, npy_float, npy_double, npy_longdouble, + * npy_cfloat, npy_cdouble, npy_clongdouble# + * #temptype = npy_byte, npy_short, npy_int, npy_long, npy_longlong, + * npy_ubyte, npy_ushort, npy_uint, npy_ulong, npy_ulonglong, + * npy_float, npy_float, npy_double, npy_longdouble, + * npy_float, npy_double, npy_longdouble# + * #sfx = s8, s16, s32, long, s64, + * u8, u16, u32, ulong, u64, + * half, f32, f64, longdouble, + * f32, f64, clongdouble# + * #to = ,,,,, + * ,,,,, + * npy_float_to_half,,,, + * ,,# + * #from = ,,,,, + * ,,,,, + * npy_half_to_float,,,, + * ,,# + * #complex = 0*5, + * 0*5, + * 0*4, + * 1*3# + * #float32 = 0*5, + * 0*5, + * 0,1,0,0, + * 0*3# + * #float64 = 0*5, + * 0*5, + * 0,0,1,0, + * 0*3# + * #NPYV_CHK = 0*5, + * 0*5, + * 0, NPY_SIMD, NPY_SIMD_F64, 0, + * 0*3# + */ + +#if !@complex@ +static NPY_GCC_OPT_3 @temptype@ @name@_sum_of_arr(@type@ *data, npy_intp count) +{ + @temptype@ accum = 0; +#if @NPYV_CHK@ // NPYV check for @type@ + /* Use aligned instructions if possible */ + const int is_aligned = EINSUM_IS_ALIGNED(data); + const int vstep = npyv_nlanes_@sfx@; + npyv_@sfx@ v_accum = npyv_zero_@sfx@(); + const npy_intp vstepx4 = vstep * 4; + + /**begin repeat1 + * #cond = if(is_aligned), else# + * #ld = loada, load# + * #st = storea, store# + */ + @cond@ { + for (; count >= vstepx4; count -= vstepx4, data += vstepx4) { + /**begin repeat2 + * #i = 0, 1, 2, 3# + */ + npyv_@sfx@ a@i@ = npyv_@ld@_@sfx@(data + vstep * @i@); + /**end repeat2**/ + npyv_@sfx@ a01 = npyv_add_@sfx@(a0, a1); + npyv_@sfx@ a23 = npyv_add_@sfx@(a2, a3); + npyv_@sfx@ a0123 = npyv_add_@sfx@(a01, a23); + v_accum = npyv_add_@sfx@(a0123, v_accum); + } + } + /**end repeat1**/ + for (; count > 0; count -= vstep, data += vstep) { + npyv_@sfx@ a = npyv_load_tillz_@sfx@(data, count); + v_accum = npyv_add_@sfx@(a, v_accum); + } + accum = npyv_sum_@sfx@(v_accum); + npyv_cleanup(); +#else +#ifndef NPY_DISABLE_OPTIMIZATION + for (; count > 4; count -= 4, data += 4) { + const @temptype@ a01 = @from@(*data) + @from@(data[1]); + const @temptype@ a23 = @from@(data[2]) + @from@(data[3]); + accum += a01 + a23; + } +#endif // !NPY_DISABLE_OPTIMIZATION + for (; count > 0; --count, data++) { + accum += @from@(*data); + } +#endif // NPYV check for @type@ + return accum; +} +#endif + +/**begin repeat1 + * #nop = 1, 2, 3, 1000# + * #noplabel = one, two, three, any# + */ +static void +@name@_sum_of_products_@noplabel@(int nop, char **dataptr, + npy_intp const *strides, npy_intp count) +{ +#if (@nop@ == 1) || (@nop@ <= 3 && !@complex@) + char *data0 = dataptr[0]; + npy_intp stride0 = strides[0]; +#endif +#if (@nop@ == 2 || @nop@ == 3) && !@complex@ + char *data1 = dataptr[1]; + npy_intp stride1 = strides[1]; +#endif +#if (@nop@ == 3) && !@complex@ + char *data2 = dataptr[2]; + npy_intp stride2 = strides[2]; +#endif +#if (@nop@ == 1) || (@nop@ <= 3 && !@complex@) + char *data_out = dataptr[@nop@]; + npy_intp stride_out = strides[@nop@]; +#endif + + NPY_EINSUM_DBG_PRINT1("@name@_sum_of_products_@noplabel@ (%d)\n", (int)count); + + while (count--) { +#if !@complex@ +# if @nop@ == 1 + *(@type@ *)data_out = @to@(@from@(*(@type@ *)data0) + + @from@(*(@type@ *)data_out)); + data0 += stride0; + data_out += stride_out; +# elif @nop@ == 2 + *(@type@ *)data_out = @to@(@from@(*(@type@ *)data0) * + @from@(*(@type@ *)data1) + + @from@(*(@type@ *)data_out)); + data0 += stride0; + data1 += stride1; + data_out += stride_out; +# elif @nop@ == 3 + *(@type@ *)data_out = @to@(@from@(*(@type@ *)data0) * + @from@(*(@type@ *)data1) * + @from@(*(@type@ *)data2) + + @from@(*(@type@ *)data_out)); + data0 += stride0; + data1 += stride1; + data2 += stride2; + data_out += stride_out; +# else + @temptype@ temp = @from@(*(@type@ *)dataptr[0]); + int i; + for (i = 1; i < nop; ++i) { + temp *= @from@(*(@type@ *)dataptr[i]); + } + *(@type@ *)dataptr[nop] = @to@(temp + + @from@(*(@type@ *)dataptr[i])); + for (i = 0; i <= nop; ++i) { + dataptr[i] += strides[i]; + } +# endif +#else /* complex */ +# if @nop@ == 1 + ((@temptype@ *)data_out)[0] = ((@temptype@ *)data0)[0] + + ((@temptype@ *)data_out)[0]; + ((@temptype@ *)data_out)[1] = ((@temptype@ *)data0)[1] + + ((@temptype@ *)data_out)[1]; + data0 += stride0; + data_out += stride_out; +# else +# if @nop@ <= 3 +#define _SUMPROD_NOP @nop@ +# else +#define _SUMPROD_NOP nop +# endif + @temptype@ re, im, tmp; + int i; + re = ((@temptype@ *)dataptr[0])[0]; + im = ((@temptype@ *)dataptr[0])[1]; + for (i = 1; i < _SUMPROD_NOP; ++i) { + tmp = re * ((@temptype@ *)dataptr[i])[0] - + im * ((@temptype@ *)dataptr[i])[1]; + im = re * ((@temptype@ *)dataptr[i])[1] + + im * ((@temptype@ *)dataptr[i])[0]; + re = tmp; + } + ((@temptype@ *)dataptr[_SUMPROD_NOP])[0] = re + + ((@temptype@ *)dataptr[_SUMPROD_NOP])[0]; + ((@temptype@ *)dataptr[_SUMPROD_NOP])[1] = im + + ((@temptype@ *)dataptr[_SUMPROD_NOP])[1]; + + for (i = 0; i <= _SUMPROD_NOP; ++i) { + dataptr[i] += strides[i]; + } +#undef _SUMPROD_NOP +# endif +#endif + } +} + +#if @nop@ == 1 + +static void +@name@_sum_of_products_contig_one(int nop, char **dataptr, + npy_intp const *NPY_UNUSED(strides), npy_intp count) +{ + @type@ *data0 = (@type@ *)dataptr[0]; + @type@ *data_out = (@type@ *)dataptr[1]; + + NPY_EINSUM_DBG_PRINT1("@name@_sum_of_products_contig_one (%d)\n", + (int)count); + +/* This is placed before the main loop to make small counts faster */ +finish_after_unrolled_loop: + switch (count) { +/**begin repeat2 + * #i = 6, 5, 4, 3, 2, 1, 0# + */ + case @i@+1: +#if !@complex@ + data_out[@i@] = @to@(@from@(data0[@i@]) + + @from@(data_out[@i@])); +#else + ((@temptype@ *)data_out + 2*@i@)[0] = + ((@temptype@ *)data0 + 2*@i@)[0] + + ((@temptype@ *)data_out + 2*@i@)[0]; + ((@temptype@ *)data_out + 2*@i@)[1] = + ((@temptype@ *)data0 + 2*@i@)[1] + + ((@temptype@ *)data_out + 2*@i@)[1]; +#endif +/**end repeat2**/ + case 0: + return; + } + + /* Unroll the loop by 8 */ + while (count >= 8) { + count -= 8; + +/**begin repeat2 + * #i = 0, 1, 2, 3, 4, 5, 6, 7# + */ +#if !@complex@ + data_out[@i@] = @to@(@from@(data0[@i@]) + + @from@(data_out[@i@])); +#else /* complex */ + ((@temptype@ *)data_out + 2*@i@)[0] = + ((@temptype@ *)data0 + 2*@i@)[0] + + ((@temptype@ *)data_out + 2*@i@)[0]; + ((@temptype@ *)data_out + 2*@i@)[1] = + ((@temptype@ *)data0 + 2*@i@)[1] + + ((@temptype@ *)data_out + 2*@i@)[1]; +#endif +/**end repeat2**/ + data0 += 8; + data_out += 8; + } + + /* Finish off the loop */ + goto finish_after_unrolled_loop; +} + +#elif @nop@ == 2 && !@complex@ + +// calculate the multiply and add operation such as dataout = data*scalar+dataout +static NPY_GCC_OPT_3 void +@name@_sum_of_products_muladd(@type@ *data, @type@ *data_out, @temptype@ scalar, npy_intp count) +{ +#if @NPYV_CHK@ // NPYV check for @type@ + /* Use aligned instructions if possible */ + const int is_aligned = EINSUM_IS_ALIGNED(data) && EINSUM_IS_ALIGNED(data_out); + const int vstep = npyv_nlanes_@sfx@; + const npyv_@sfx@ v_scalar = npyv_setall_@sfx@(scalar); + /**begin repeat2 + * #cond = if(is_aligned), else# + * #ld = loada, load# + * #st = storea, store# + */ + @cond@ { + const npy_intp vstepx4 = vstep * 4; + for (; count >= vstepx4; count -= vstepx4, data += vstepx4, data_out += vstepx4) { + /**begin repeat3 + * #i = 0, 1, 2, 3# + */ + npyv_@sfx@ b@i@ = npyv_@ld@_@sfx@(data + vstep * @i@); + npyv_@sfx@ c@i@ = npyv_@ld@_@sfx@(data_out + vstep * @i@); + /**end repeat3**/ + /**begin repeat3 + * #i = 0, 1, 2, 3# + */ + npyv_@sfx@ abc@i@ = npyv_muladd_@sfx@(v_scalar, b@i@, c@i@); + /**end repeat3**/ + /**begin repeat3 + * #i = 0, 1, 2, 3# + */ + npyv_@st@_@sfx@(data_out + vstep * @i@, abc@i@); + /**end repeat3**/ + } + } + /**end repeat2**/ + for (; count > 0; count -= vstep, data += vstep, data_out += vstep) { + npyv_@sfx@ a = npyv_load_tillz_@sfx@(data, count); + npyv_@sfx@ b = npyv_load_tillz_@sfx@(data_out, count); + npyv_store_till_@sfx@(data_out, count, npyv_muladd_@sfx@(a, v_scalar, b)); + } + npyv_cleanup(); +#else +#ifndef NPY_DISABLE_OPTIMIZATION + for (; count >= 4; count -= 4, data += 4, data_out += 4) { + /**begin repeat2 + * #i = 0, 1, 2, 3# + */ + const @temptype@ b@i@ = @from@(data[@i@]); + const @temptype@ c@i@ = @from@(data_out[@i@]); + /**end repeat2**/ + /**begin repeat2 + * #i = 0, 1, 2, 3# + */ + const @temptype@ abc@i@ = scalar * b@i@ + c@i@; + /**end repeat2**/ + /**begin repeat2 + * #i = 0, 1, 2, 3# + */ + data_out[@i@] = @to@(abc@i@); + /**end repeat2**/ + } +#endif // !NPY_DISABLE_OPTIMIZATION + for (; count > 0; --count, ++data, ++data_out) { + const @temptype@ b = @from@(*data); + const @temptype@ c = @from@(*data_out); + *data_out = @to@(scalar * b + c); + } +#endif // NPYV check for @type@ +} + +static void +@name@_sum_of_products_contig_two(int nop, char **dataptr, + npy_intp const *NPY_UNUSED(strides), npy_intp count) +{ + @type@ *data0 = (@type@ *)dataptr[0]; + @type@ *data1 = (@type@ *)dataptr[1]; + @type@ *data_out = (@type@ *)dataptr[2]; + NPY_EINSUM_DBG_PRINT1("@name@_sum_of_products_contig_two (%d)\n", + (int)count); + // NPYV check for @type@ +#if @NPYV_CHK@ + /* Use aligned instructions if possible */ + const int is_aligned = EINSUM_IS_ALIGNED(data0) && EINSUM_IS_ALIGNED(data1) && + EINSUM_IS_ALIGNED(data_out); + const int vstep = npyv_nlanes_@sfx@; + + /**begin repeat2 + * #cond = if(is_aligned), else# + * #ld = loada, load# + * #st = storea, store# + */ + @cond@ { + const npy_intp vstepx4 = vstep * 4; + for (; count >= vstepx4; count -= vstepx4, data0 += vstepx4, data1 += vstepx4, data_out += vstepx4) { + /**begin repeat3 + * #i = 0, 1, 2, 3# + */ + npyv_@sfx@ a@i@ = npyv_@ld@_@sfx@(data0 + vstep * @i@); + npyv_@sfx@ b@i@ = npyv_@ld@_@sfx@(data1 + vstep * @i@); + npyv_@sfx@ c@i@ = npyv_@ld@_@sfx@(data_out + vstep * @i@); + /**end repeat3**/ + /**begin repeat3 + * #i = 0, 1, 2, 3# + */ + npyv_@sfx@ abc@i@ = npyv_muladd_@sfx@(a@i@, b@i@, c@i@); + /**end repeat3**/ + /**begin repeat3 + * #i = 0, 1, 2, 3# + */ + npyv_@st@_@sfx@(data_out + vstep * @i@, abc@i@); + /**end repeat3**/ + } + } + /**end repeat2**/ + for (; count > 0; count -= vstep, data0 += vstep, data1 += vstep, data_out += vstep) { + npyv_@sfx@ a = npyv_load_tillz_@sfx@(data0, count); + npyv_@sfx@ b = npyv_load_tillz_@sfx@(data1, count); + npyv_@sfx@ c = npyv_load_tillz_@sfx@(data_out, count); + npyv_store_till_@sfx@(data_out, count, npyv_muladd_@sfx@(a, b, c)); + } + npyv_cleanup(); +#else +#ifndef NPY_DISABLE_OPTIMIZATION + for (; count >= 4; count -= 4, data0 += 4, data1 += 4, data_out += 4) { + /**begin repeat2 + * #i = 0, 1, 2, 3# + */ + const @temptype@ a@i@ = @from@(data0[@i@]); + const @temptype@ b@i@ = @from@(data1[@i@]); + const @temptype@ c@i@ = @from@(data_out[@i@]); + /**end repeat2**/ + /**begin repeat2 + * #i = 0, 1, 2, 3# + */ + const @temptype@ abc@i@ = a@i@ * b@i@ + c@i@; + /**end repeat2**/ + /**begin repeat2 + * #i = 0, 1, 2, 3# + */ + data_out[@i@] = @to@(abc@i@); + /**end repeat2**/ + } +#endif // !NPY_DISABLE_OPTIMIZATION + for (; count > 0; --count, ++data0, ++data1, ++data_out) { + const @temptype@ a = @from@(*data0); + const @temptype@ b = @from@(*data1); + const @temptype@ c = @from@(*data_out); + *data_out = @to@(a * b + c); + } +#endif // NPYV check for @type@ + +} + +/* Some extra specializations for the two operand case */ +static void +@name@_sum_of_products_stride0_contig_outcontig_two(int nop, char **dataptr, + npy_intp const *NPY_UNUSED(strides), npy_intp count) +{ + @temptype@ value0 = @from@(*(@type@ *)dataptr[0]); + @type@ *data1 = (@type@ *)dataptr[1]; + @type@ *data_out = (@type@ *)dataptr[2]; + + NPY_EINSUM_DBG_PRINT1("@name@_sum_of_products_stride0_contig_outcontig_two (%d)\n", + (int)count); + @name@_sum_of_products_muladd(data1, data_out, value0, count); + +} + +static void +@name@_sum_of_products_contig_stride0_outcontig_two(int nop, char **dataptr, + npy_intp const *NPY_UNUSED(strides), npy_intp count) +{ + @temptype@ value1 = @from@(*(@type@ *)dataptr[1]); + @type@ *data0 = (@type@ *)dataptr[0]; + @type@ *data_out = (@type@ *)dataptr[2]; + + NPY_EINSUM_DBG_PRINT1("@name@_sum_of_products_contig_stride0_outcontig_two (%d)\n", + (int)count); + @name@_sum_of_products_muladd(data0, data_out, value1, count); +} + +static NPY_GCC_OPT_3 void +@name@_sum_of_products_contig_contig_outstride0_two(int nop, char **dataptr, + npy_intp const *NPY_UNUSED(strides), npy_intp count) +{ + @type@ *data0 = (@type@ *)dataptr[0]; + @type@ *data1 = (@type@ *)dataptr[1]; + @temptype@ accum = 0; + + NPY_EINSUM_DBG_PRINT1("@name@_sum_of_products_contig_contig_outstride0_two (%d)\n", + (int)count); +#if @NPYV_CHK@ // NPYV check for @type@ + /* Use aligned instructions if possible */ + const int is_aligned = EINSUM_IS_ALIGNED(data0) && EINSUM_IS_ALIGNED(data1); + const int vstep = npyv_nlanes_@sfx@; + npyv_@sfx@ v_accum = npyv_zero_@sfx@(); + + /**begin repeat2 + * #cond = if(is_aligned), else# + * #ld = loada, load# + * #st = storea, store# + */ + @cond@ { + const npy_intp vstepx4 = vstep * 4; + for (; count >= vstepx4; count -= vstepx4, data0 += vstepx4, data1 += vstepx4) { + /**begin repeat3 + * #i = 0, 1, 2, 3# + */ + npyv_@sfx@ a@i@ = npyv_@ld@_@sfx@(data0 + vstep * @i@); + npyv_@sfx@ b@i@ = npyv_@ld@_@sfx@(data1 + vstep * @i@); + /**end repeat3**/ + npyv_@sfx@ ab3 = npyv_muladd_@sfx@(a3, b3, v_accum); + npyv_@sfx@ ab2 = npyv_muladd_@sfx@(a2, b2, ab3); + npyv_@sfx@ ab1 = npyv_muladd_@sfx@(a1, b1, ab2); + v_accum = npyv_muladd_@sfx@(a0, b0, ab1); + } + } + /**end repeat2**/ + for (; count > 0; count -= vstep, data0 += vstep, data1 += vstep) { + npyv_@sfx@ a = npyv_load_tillz_@sfx@(data0, count); + npyv_@sfx@ b = npyv_load_tillz_@sfx@(data1, count); + v_accum = npyv_muladd_@sfx@(a, b, v_accum); + } + accum = npyv_sum_@sfx@(v_accum); + npyv_cleanup(); +#else +#ifndef NPY_DISABLE_OPTIMIZATION + for (; count >= 4; count -= 4, data0 += 4, data1 += 4) { + /**begin repeat2 + * #i = 0, 1, 2, 3# + */ + const @temptype@ ab@i@ = @from@(data0[@i@]) * @from@(data1[@i@]); + /**end repeat2**/ + accum += ab0 + ab1 + ab2 + ab3; + } +#endif // !NPY_DISABLE_OPTIMIZATION + for (; count > 0; --count, ++data0, ++data1) { + const @temptype@ a = @from@(*data0); + const @temptype@ b = @from@(*data1); + accum += a * b; + } +#endif // NPYV check for @type@ + *(@type@ *)dataptr[2] = @to@(@from@(*(@type@ *)dataptr[2]) + accum); +} + +static NPY_GCC_OPT_3 void +@name@_sum_of_products_stride0_contig_outstride0_two(int nop, char **dataptr, + npy_intp const *NPY_UNUSED(strides), npy_intp count) +{ + @type@ *data1 = (@type@ *)dataptr[1]; + @temptype@ value0 = @from@(*(@type@ *)dataptr[0]); + @temptype@ accum = @name@_sum_of_arr(data1, count); + *(@type@ *)dataptr[2] = @to@(@from@(*(@type@ *)dataptr[2]) + value0 * accum); +} + +static NPY_GCC_OPT_3 void +@name@_sum_of_products_contig_stride0_outstride0_two(int nop, char **dataptr, + npy_intp const *NPY_UNUSED(strides), npy_intp count) +{ + @type@ *data0 = (@type@ *)dataptr[0]; + @temptype@ value1 = @from@(*(@type@ *)dataptr[1]); + @temptype@ accum = @name@_sum_of_arr(data0, count); + *(@type@ *)dataptr[2] = @to@(@from@(*(@type@ *)dataptr[2]) + value1 * accum); +} + +#elif @nop@ == 3 && !@complex@ + +static void +@name@_sum_of_products_contig_three(int nop, char **dataptr, + npy_intp const *NPY_UNUSED(strides), npy_intp count) +{ + @type@ *data0 = (@type@ *)dataptr[0]; + @type@ *data1 = (@type@ *)dataptr[1]; + @type@ *data2 = (@type@ *)dataptr[2]; + @type@ *data_out = (@type@ *)dataptr[3]; + + /* Unroll the loop by 8 */ + while (count >= 8) { + count -= 8; + +/**begin repeat2 + * #i = 0, 1, 2, 3, 4, 5, 6, 7# + */ + data_out[@i@] = @to@(@from@(data0[@i@]) * + @from@(data1[@i@]) * + @from@(data2[@i@]) + + @from@(data_out[@i@])); +/**end repeat2**/ + data0 += 8; + data1 += 8; + data2 += 8; + data_out += 8; + } + + /* Finish off the loop */ + +/**begin repeat2 + * #i = 0, 1, 2, 3, 4, 5, 6, 7# + */ + if (count-- == 0) { + return; + } + data_out[@i@] = @to@(@from@(data0[@i@]) * + @from@(data1[@i@]) * + @from@(data2[@i@]) + + @from@(data_out[@i@])); +/**end repeat2**/ +} + +#else /* @nop@ > 3 || @complex */ + +static void +@name@_sum_of_products_contig_@noplabel@(int nop, char **dataptr, + npy_intp const *NPY_UNUSED(strides), npy_intp count) +{ + NPY_EINSUM_DBG_PRINT1("@name@_sum_of_products_contig_@noplabel@ (%d)\n", + (int)count); + + while (count--) { +#if !@complex@ + @temptype@ temp = @from@(*(@type@ *)dataptr[0]); + int i; + for (i = 1; i < nop; ++i) { + temp *= @from@(*(@type@ *)dataptr[i]); + } + *(@type@ *)dataptr[nop] = @to@(temp + + @from@(*(@type@ *)dataptr[i])); + for (i = 0; i <= nop; ++i) { + dataptr[i] += sizeof(@type@); + } +#else /* complex */ +# if @nop@ <= 3 +# define _SUMPROD_NOP @nop@ +# else +# define _SUMPROD_NOP nop +# endif + @temptype@ re, im, tmp; + int i; + re = ((@temptype@ *)dataptr[0])[0]; + im = ((@temptype@ *)dataptr[0])[1]; + for (i = 1; i < _SUMPROD_NOP; ++i) { + tmp = re * ((@temptype@ *)dataptr[i])[0] - + im * ((@temptype@ *)dataptr[i])[1]; + im = re * ((@temptype@ *)dataptr[i])[1] + + im * ((@temptype@ *)dataptr[i])[0]; + re = tmp; + } + ((@temptype@ *)dataptr[_SUMPROD_NOP])[0] = re + + ((@temptype@ *)dataptr[_SUMPROD_NOP])[0]; + ((@temptype@ *)dataptr[_SUMPROD_NOP])[1] = im + + ((@temptype@ *)dataptr[_SUMPROD_NOP])[1]; + + for (i = 0; i <= _SUMPROD_NOP; ++i) { + dataptr[i] += sizeof(@type@); + } +# undef _SUMPROD_NOP +#endif + } +} + +#endif /* functions for various @nop@ */ + +#if @nop@ == 1 + +static NPY_GCC_OPT_3 void +@name@_sum_of_products_contig_outstride0_one(int nop, char **dataptr, + npy_intp const *strides, npy_intp count) +{ + NPY_EINSUM_DBG_PRINT1("@name@_sum_of_products_contig_outstride0_one (%d)\n", (int)count); +#if !@complex@ + @type@ *data = (@type@ *)dataptr[0]; + @temptype@ accum = @name@_sum_of_arr(data, count); + *((@type@ *)dataptr[1]) = @to@(accum + @from@(*((@type@ *)dataptr[1]))); +#else + @temptype@ accum_re = 0, accum_im = 0; + @temptype@ *data0 = (@temptype@ *)dataptr[0]; +#ifndef NPY_DISABLE_OPTIMIZATION + for (; count > 4; count -= 4, data0 += 4*2) { + const @temptype@ re01 = data0[0] + data0[2]; + const @temptype@ re23 = data0[4] + data0[6]; + const @temptype@ im13 = data0[1] + data0[3]; + const @temptype@ im57 = data0[5] + data0[7]; + accum_re += re01 + re23; + accum_im += im13 + im57; + } +#endif // !NPY_DISABLE_OPTIMIZATION + for (; count > 0; --count, data0 += 2) { + accum_re += data0[0]; + accum_im += data0[1]; + } + ((@temptype@ *)dataptr[1])[0] += accum_re; + ((@temptype@ *)dataptr[1])[1] += accum_im; +#endif // !@complex@ +} + +#endif /* @nop@ == 1 */ + +static void +@name@_sum_of_products_outstride0_@noplabel@(int nop, char **dataptr, + npy_intp const *strides, npy_intp count) +{ +#if @complex@ + @temptype@ accum_re = 0, accum_im = 0; +#else + @temptype@ accum = 0; +#endif + +#if (@nop@ == 1) || (@nop@ <= 3 && !@complex@) + char *data0 = dataptr[0]; + npy_intp stride0 = strides[0]; +#endif +#if (@nop@ == 2 || @nop@ == 3) && !@complex@ + char *data1 = dataptr[1]; + npy_intp stride1 = strides[1]; +#endif +#if (@nop@ == 3) && !@complex@ + char *data2 = dataptr[2]; + npy_intp stride2 = strides[2]; +#endif + + NPY_EINSUM_DBG_PRINT1("@name@_sum_of_products_outstride0_@noplabel@ (%d)\n", + (int)count); + + while (count--) { +#if !@complex@ +# if @nop@ == 1 + accum += @from@(*(@type@ *)data0); + data0 += stride0; +# elif @nop@ == 2 + accum += @from@(*(@type@ *)data0) * + @from@(*(@type@ *)data1); + data0 += stride0; + data1 += stride1; +# elif @nop@ == 3 + accum += @from@(*(@type@ *)data0) * + @from@(*(@type@ *)data1) * + @from@(*(@type@ *)data2); + data0 += stride0; + data1 += stride1; + data2 += stride2; +# else + @temptype@ temp = @from@(*(@type@ *)dataptr[0]); + int i; + for (i = 1; i < nop; ++i) { + temp *= @from@(*(@type@ *)dataptr[i]); + } + accum += temp; + for (i = 0; i < nop; ++i) { + dataptr[i] += strides[i]; + } +# endif +#else /* complex */ +# if @nop@ == 1 + accum_re += ((@temptype@ *)data0)[0]; + accum_im += ((@temptype@ *)data0)[1]; + data0 += stride0; +# else +# if @nop@ <= 3 +#define _SUMPROD_NOP @nop@ +# else +#define _SUMPROD_NOP nop +# endif + @temptype@ re, im, tmp; + int i; + re = ((@temptype@ *)dataptr[0])[0]; + im = ((@temptype@ *)dataptr[0])[1]; + for (i = 1; i < _SUMPROD_NOP; ++i) { + tmp = re * ((@temptype@ *)dataptr[i])[0] - + im * ((@temptype@ *)dataptr[i])[1]; + im = re * ((@temptype@ *)dataptr[i])[1] + + im * ((@temptype@ *)dataptr[i])[0]; + re = tmp; + } + accum_re += re; + accum_im += im; + for (i = 0; i < _SUMPROD_NOP; ++i) { + dataptr[i] += strides[i]; + } +#undef _SUMPROD_NOP +# endif +#endif + } + +#if @complex@ +# if @nop@ <= 3 + ((@temptype@ *)dataptr[@nop@])[0] += accum_re; + ((@temptype@ *)dataptr[@nop@])[1] += accum_im; +# else + ((@temptype@ *)dataptr[nop])[0] += accum_re; + ((@temptype@ *)dataptr[nop])[1] += accum_im; +# endif +#else +# if @nop@ <= 3 + *((@type@ *)dataptr[@nop@]) = @to@(accum + + @from@(*((@type@ *)dataptr[@nop@]))); +# else + *((@type@ *)dataptr[nop]) = @to@(accum + + @from@(*((@type@ *)dataptr[nop]))); +# endif +#endif + +} + +/**end repeat1**/ + +/**end repeat**/ + + +/* Do OR of ANDs for the boolean type */ + +/**begin repeat + * #nop = 1, 2, 3, 1000# + * #noplabel = one, two, three, any# + */ + +static void +bool_sum_of_products_@noplabel@(int nop, char **dataptr, + npy_intp const *strides, npy_intp count) +{ +#if (@nop@ <= 3) + char *data0 = dataptr[0]; + npy_intp stride0 = strides[0]; +#endif +#if (@nop@ == 2 || @nop@ == 3) + char *data1 = dataptr[1]; + npy_intp stride1 = strides[1]; +#endif +#if (@nop@ == 3) + char *data2 = dataptr[2]; + npy_intp stride2 = strides[2]; +#endif +#if (@nop@ <= 3) + char *data_out = dataptr[@nop@]; + npy_intp stride_out = strides[@nop@]; +#endif + + while (count--) { +#if @nop@ == 1 + *(npy_bool *)data_out = *(npy_bool *)data0 || + *(npy_bool *)data_out; + data0 += stride0; + data_out += stride_out; +#elif @nop@ == 2 + *(npy_bool *)data_out = (*(npy_bool *)data0 && + *(npy_bool *)data1) || + *(npy_bool *)data_out; + data0 += stride0; + data1 += stride1; + data_out += stride_out; +#elif @nop@ == 3 + *(npy_bool *)data_out = (*(npy_bool *)data0 && + *(npy_bool *)data1 && + *(npy_bool *)data2) || + *(npy_bool *)data_out; + data0 += stride0; + data1 += stride1; + data2 += stride2; + data_out += stride_out; +#else + npy_bool temp = *(npy_bool *)dataptr[0]; + int i; + for (i = 1; i < nop; ++i) { + temp = temp && *(npy_bool *)dataptr[i]; + } + *(npy_bool *)dataptr[nop] = temp || *(npy_bool *)dataptr[i]; + for (i = 0; i <= nop; ++i) { + dataptr[i] += strides[i]; + } +#endif + } +} + +static void +bool_sum_of_products_contig_@noplabel@(int nop, char **dataptr, + npy_intp const *strides, npy_intp count) +{ +#if (@nop@ <= 3) + char *data0 = dataptr[0]; +#endif +#if (@nop@ == 2 || @nop@ == 3) + char *data1 = dataptr[1]; +#endif +#if (@nop@ == 3) + char *data2 = dataptr[2]; +#endif +#if (@nop@ <= 3) + char *data_out = dataptr[@nop@]; +#endif + +#if (@nop@ <= 3) +/* This is placed before the main loop to make small counts faster */ +finish_after_unrolled_loop: + switch (count) { +/**begin repeat1 + * #i = 6, 5, 4, 3, 2, 1, 0# + */ + case @i@+1: +# if @nop@ == 1 + ((npy_bool *)data_out)[@i@] = ((npy_bool *)data0)[@i@] || + ((npy_bool *)data_out)[@i@]; +# elif @nop@ == 2 + ((npy_bool *)data_out)[@i@] = + (((npy_bool *)data0)[@i@] && + ((npy_bool *)data1)[@i@]) || + ((npy_bool *)data_out)[@i@]; +# elif @nop@ == 3 + ((npy_bool *)data_out)[@i@] = + (((npy_bool *)data0)[@i@] && + ((npy_bool *)data1)[@i@] && + ((npy_bool *)data2)[@i@]) || + ((npy_bool *)data_out)[@i@]; +# endif +/**end repeat1**/ + case 0: + return; + } +#endif + +/* Unroll the loop by 8 for fixed-size nop */ +#if (@nop@ <= 3) + while (count >= 8) { + count -= 8; +#else + while (count--) { +#endif + +# if @nop@ == 1 +/**begin repeat1 + * #i = 0, 1, 2, 3, 4, 5, 6, 7# + */ + *((npy_bool *)data_out + @i@) = (*((npy_bool *)data0 + @i@)) || + (*((npy_bool *)data_out + @i@)); +/**end repeat1**/ + data0 += 8*sizeof(npy_bool); + data_out += 8*sizeof(npy_bool); +# elif @nop@ == 2 +/**begin repeat1 + * #i = 0, 1, 2, 3, 4, 5, 6, 7# + */ + *((npy_bool *)data_out + @i@) = + ((*((npy_bool *)data0 + @i@)) && + (*((npy_bool *)data1 + @i@))) || + (*((npy_bool *)data_out + @i@)); +/**end repeat1**/ + data0 += 8*sizeof(npy_bool); + data1 += 8*sizeof(npy_bool); + data_out += 8*sizeof(npy_bool); +# elif @nop@ == 3 +/**begin repeat1 + * #i = 0, 1, 2, 3, 4, 5, 6, 7# + */ + *((npy_bool *)data_out + @i@) = + ((*((npy_bool *)data0 + @i@)) && + (*((npy_bool *)data1 + @i@)) && + (*((npy_bool *)data2 + @i@))) || + (*((npy_bool *)data_out + @i@)); +/**end repeat1**/ + data0 += 8*sizeof(npy_bool); + data1 += 8*sizeof(npy_bool); + data2 += 8*sizeof(npy_bool); + data_out += 8*sizeof(npy_bool); +# else + npy_bool temp = *(npy_bool *)dataptr[0]; + int i; + for (i = 1; i < nop; ++i) { + temp = temp && *(npy_bool *)dataptr[i]; + } + *(npy_bool *)dataptr[nop] = temp || *(npy_bool *)dataptr[i]; + for (i = 0; i <= nop; ++i) { + dataptr[i] += sizeof(npy_bool); + } +# endif + } + + /* If the loop was unrolled, we need to finish it off */ +#if (@nop@ <= 3) + goto finish_after_unrolled_loop; +#endif +} + +static void +bool_sum_of_products_outstride0_@noplabel@(int nop, char **dataptr, + npy_intp const *strides, npy_intp count) +{ + npy_bool accum = 0; + +#if (@nop@ <= 3) + char *data0 = dataptr[0]; + npy_intp stride0 = strides[0]; +#endif +#if (@nop@ == 2 || @nop@ == 3) + char *data1 = dataptr[1]; + npy_intp stride1 = strides[1]; +#endif +#if (@nop@ == 3) + char *data2 = dataptr[2]; + npy_intp stride2 = strides[2]; +#endif + + while (count--) { +#if @nop@ == 1 + accum = *(npy_bool *)data0 || accum; + data0 += stride0; +#elif @nop@ == 2 + accum = (*(npy_bool *)data0 && *(npy_bool *)data1) || accum; + data0 += stride0; + data1 += stride1; +#elif @nop@ == 3 + accum = (*(npy_bool *)data0 && + *(npy_bool *)data1 && + *(npy_bool *)data2) || accum; + data0 += stride0; + data1 += stride1; + data2 += stride2; +#else + npy_bool temp = *(npy_bool *)dataptr[0]; + int i; + for (i = 1; i < nop; ++i) { + temp = temp && *(npy_bool *)dataptr[i]; + } + accum = temp || accum; + for (i = 0; i <= nop; ++i) { + dataptr[i] += strides[i]; + } +#endif + } + +# if @nop@ <= 3 + *((npy_bool *)dataptr[@nop@]) = accum || *((npy_bool *)dataptr[@nop@]); +# else + *((npy_bool *)dataptr[nop]) = accum || *((npy_bool *)dataptr[nop]); +# endif +} + +/**end repeat**/ + +/* These tables need to match up with the type enum */ +static sum_of_products_fn +_contig_outstride0_unary_specialization_table[NPY_NTYPES] = { +/**begin repeat + * #name = bool, + * byte, ubyte, + * short, ushort, + * int, uint, + * long, ulong, + * longlong, ulonglong, + * float, double, longdouble, + * cfloat, cdouble, clongdouble, + * object, string, unicode, void, + * datetime, timedelta, half# + * #use = 0, + * 1, 1, + * 1, 1, + * 1, 1, + * 1, 1, + * 1, 1, + * 1, 1, 1, + * 1, 1, 1, + * 0, 0, 0, 0, + * 0, 0, 1# + */ +#if @use@ + &@name@_sum_of_products_contig_outstride0_one, +#else + NULL, +#endif +/**end repeat**/ +}; /* End of _contig_outstride0_unary_specialization_table */ + +static sum_of_products_fn _binary_specialization_table[NPY_NTYPES][5] = { +/**begin repeat + * #name = bool, + * byte, ubyte, + * short, ushort, + * int, uint, + * long, ulong, + * longlong, ulonglong, + * float, double, longdouble, + * cfloat, cdouble, clongdouble, + * object, string, unicode, void, + * datetime, timedelta, half# + * #use = 0, + * 1, 1, + * 1, 1, + * 1, 1, + * 1, 1, + * 1, 1, + * 1, 1, 1, + * 0, 0, 0, + * 0, 0, 0, 0, + * 0, 0, 1# + */ +#if @use@ +{ + &@name@_sum_of_products_stride0_contig_outstride0_two, + &@name@_sum_of_products_stride0_contig_outcontig_two, + &@name@_sum_of_products_contig_stride0_outstride0_two, + &@name@_sum_of_products_contig_stride0_outcontig_two, + &@name@_sum_of_products_contig_contig_outstride0_two, +}, +#else + {NULL, NULL, NULL, NULL, NULL}, +#endif +/**end repeat**/ +}; /* End of _binary_specialization_table */ + +static sum_of_products_fn _outstride0_specialized_table[NPY_NTYPES][4] = { +/**begin repeat + * #name = bool, + * byte, ubyte, + * short, ushort, + * int, uint, + * long, ulong, + * longlong, ulonglong, + * float, double, longdouble, + * cfloat, cdouble, clongdouble, + * object, string, unicode, void, + * datetime, timedelta, half# + * #use = 1, + * 1, 1, + * 1, 1, + * 1, 1, + * 1, 1, + * 1, 1, + * 1, 1, 1, + * 1, 1, 1, + * 0, 0, 0, 0, + * 0, 0, 1# + */ +#if @use@ +{ + &@name@_sum_of_products_outstride0_any, + &@name@_sum_of_products_outstride0_one, + &@name@_sum_of_products_outstride0_two, + &@name@_sum_of_products_outstride0_three +}, +#else + {NULL, NULL, NULL, NULL}, +#endif +/**end repeat**/ +}; /* End of _outstride0_specialized_table */ + +static sum_of_products_fn _allcontig_specialized_table[NPY_NTYPES][4] = { +/**begin repeat + * #name = bool, + * byte, ubyte, + * short, ushort, + * int, uint, + * long, ulong, + * longlong, ulonglong, + * float, double, longdouble, + * cfloat, cdouble, clongdouble, + * object, string, unicode, void, + * datetime, timedelta, half# + * #use = 1, + * 1, 1, + * 1, 1, + * 1, 1, + * 1, 1, + * 1, 1, + * 1, 1, 1, + * 1, 1, 1, + * 0, 0, 0, 0, + * 0, 0, 1# + */ +#if @use@ +{ + &@name@_sum_of_products_contig_any, + &@name@_sum_of_products_contig_one, + &@name@_sum_of_products_contig_two, + &@name@_sum_of_products_contig_three +}, +#else + {NULL, NULL, NULL, NULL}, +#endif +/**end repeat**/ +}; /* End of _allcontig_specialized_table */ + +static sum_of_products_fn _unspecialized_table[NPY_NTYPES][4] = { +/**begin repeat + * #name = bool, + * byte, ubyte, + * short, ushort, + * int, uint, + * long, ulong, + * longlong, ulonglong, + * float, double, longdouble, + * cfloat, cdouble, clongdouble, + * object, string, unicode, void, + * datetime, timedelta, half# + * #use = 1, + * 1, 1, + * 1, 1, + * 1, 1, + * 1, 1, + * 1, 1, + * 1, 1, 1, + * 1, 1, 1, + * 0, 0, 0, 0, + * 0, 0, 1# + */ +#if @use@ +{ + &@name@_sum_of_products_any, + &@name@_sum_of_products_one, + &@name@_sum_of_products_two, + &@name@_sum_of_products_three +}, +#else + {NULL, NULL, NULL, NULL}, +#endif +/**end repeat**/ +}; /* End of _unnspecialized_table */ + +NPY_VISIBILITY_HIDDEN sum_of_products_fn +get_sum_of_products_function(int nop, int type_num, + npy_intp itemsize, npy_intp const *fixed_strides) +{ + int iop; + + if (type_num >= NPY_NTYPES) { + return NULL; + } + + /* contiguous reduction */ + if (nop == 1 && fixed_strides[0] == itemsize && fixed_strides[1] == 0) { + sum_of_products_fn ret = + _contig_outstride0_unary_specialization_table[type_num]; + if (ret != NULL) { + return ret; + } + } + + /* nop of 2 has more specializations */ + if (nop == 2) { + /* Encode the zero/contiguous strides */ + int code; + code = (fixed_strides[0] == 0) ? 0 : + (fixed_strides[0] == itemsize) ? 2*2*1 : 8; + code += (fixed_strides[1] == 0) ? 0 : + (fixed_strides[1] == itemsize) ? 2*1 : 8; + code += (fixed_strides[2] == 0) ? 0 : + (fixed_strides[2] == itemsize) ? 1 : 8; + if (code >= 2 && code < 7) { + sum_of_products_fn ret = + _binary_specialization_table[type_num][code-2]; + if (ret != NULL) { + return ret; + } + } + } + + /* Inner loop with an output stride of 0 */ + if (fixed_strides[nop] == 0) { + return _outstride0_specialized_table[type_num][nop <= 3 ? nop : 0]; + } + + /* Check for all contiguous */ + for (iop = 0; iop < nop + 1; ++iop) { + if (fixed_strides[iop] != itemsize) { + break; + } + } + + /* Contiguous loop */ + if (iop == nop + 1) { + return _allcontig_specialized_table[type_num][nop <= 3 ? nop : 0]; + } + + /* None of the above specializations caught it, general loops */ + return _unspecialized_table[type_num][nop <= 3 ? nop : 0]; +} diff --git a/numpy/core/src/multiarray/einsum_sumprod.h b/numpy/core/src/multiarray/einsum_sumprod.h new file mode 100644 index 000000000000..29ddaea14e2b --- /dev/null +++ b/numpy/core/src/multiarray/einsum_sumprod.h @@ -0,0 +1,12 @@ +#ifndef NUMPY_CORE_SRC_MULTIARRAY_EINSUM_SUMPROD_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_EINSUM_SUMPROD_H_ + +#include <numpy/npy_common.h> + +typedef void (*sum_of_products_fn)(int, char **, npy_intp const*, npy_intp); + +NPY_VISIBILITY_HIDDEN sum_of_products_fn +get_sum_of_products_function(int nop, int type_num, + npy_intp itemsize, npy_intp const *fixed_strides); + +#endif /* NUMPY_CORE_SRC_MULTIARRAY_EINSUM_SUMPROD_H_ */ diff --git a/numpy/core/src/multiarray/experimental_public_dtype_api.c b/numpy/core/src/multiarray/experimental_public_dtype_api.c new file mode 100644 index 000000000000..4b9c7199b167 --- /dev/null +++ b/numpy/core/src/multiarray/experimental_public_dtype_api.c @@ -0,0 +1,400 @@ +#include <Python.h> + +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _UMATHMODULE +#define _MULTIARRAYMODULE +#include <numpy/npy_common.h> +#include "numpy/arrayobject.h" +#include "numpy/ufuncobject.h" +#include "common.h" + +#include "experimental_public_dtype_api.h" +#include "array_method.h" +#include "dtypemeta.h" +#include "array_coercion.h" +#include "convert_datatype.h" +#include "common_dtype.h" + + +#define EXPERIMENTAL_DTYPE_API_VERSION 2 + + +typedef struct{ + PyTypeObject *typeobj; /* type of python scalar or NULL */ + int flags; /* flags, including parametric and abstract */ + /* NULL terminated cast definitions. Use NULL for the newly created DType */ + PyArrayMethod_Spec **casts; + PyType_Slot *slots; +} PyArrayDTypeMeta_Spec; + + + +static PyArray_DTypeMeta * +dtype_does_not_promote( + PyArray_DTypeMeta *NPY_UNUSED(self), PyArray_DTypeMeta *NPY_UNUSED(other)) +{ + /* `other` is guaranteed not to be `self`, so we don't have to do much... */ + Py_INCREF(Py_NotImplemented); + return (PyArray_DTypeMeta *)Py_NotImplemented; +} + + +static PyArray_Descr * +discover_as_default(PyArray_DTypeMeta *cls, PyObject *NPY_UNUSED(obj)) +{ + return NPY_DT_CALL_default_descr(cls); +} + + +static PyArray_Descr * +use_new_as_default(PyArray_DTypeMeta *self) +{ + PyObject *res = PyObject_CallObject((PyObject *)self, NULL); + if (res == NULL) { + return NULL; + } + /* + * Lets not trust that the DType is implemented correctly + * TODO: Should probably do an exact type-check (at least unless this is + * an abstract DType). + */ + if (!PyArray_DescrCheck(res)) { + PyErr_Format(PyExc_RuntimeError, + "Instantiating %S did not return a dtype instance, this is " + "invalid (especially without a custom `default_descr()`).", + self); + Py_DECREF(res); + return NULL; + } + PyArray_Descr *descr = (PyArray_Descr *)res; + /* + * Should probably do some more sanity checks here on the descriptor + * to ensure the user is not being naughty. But in the end, we have + * only limited control anyway. + */ + return descr; +} + + +static int +legacy_setitem_using_DType(PyObject *obj, void *data, void *arr) +{ + if (arr == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "Using legacy SETITEM with NULL array object is only " + "supported for basic NumPy DTypes."); + return -1; + } + setitemfunction *setitem; + setitem = NPY_DT_SLOTS(NPY_DTYPE(PyArray_DESCR(arr)))->setitem; + return setitem(PyArray_DESCR(arr), obj, data); +} + + +static PyObject * +legacy_getitem_using_DType(void *data, void *arr) +{ + if (arr == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "Using legacy SETITEM with NULL array object is only " + "supported for basic NumPy DTypes."); + return NULL; + } + getitemfunction *getitem; + getitem = NPY_DT_SLOTS(NPY_DTYPE(PyArray_DESCR(arr)))->getitem; + return getitem(PyArray_DESCR(arr), data); +} + + +/* + * The descr->f structure used user-DTypes. Some functions may be filled + * from the user in the future and more could get defaults for compatibility. + */ +PyArray_ArrFuncs default_funcs = { + .setitem = &legacy_setitem_using_DType, + .getitem = &legacy_getitem_using_DType +}; + + +/* other slots are in order, so keep only last around: */ +#define NUM_DTYPE_SLOTS 7 + + +int +PyArrayInitDTypeMeta_FromSpec( + PyArray_DTypeMeta *DType, PyArrayDTypeMeta_Spec *spec) +{ + if (!PyObject_TypeCheck(DType, &PyArrayDTypeMeta_Type)) { + PyErr_SetString(PyExc_RuntimeError, + "Passed in DType must be a valid (initialized) DTypeMeta " + "instance!"); + return -1; + } + + if (((PyTypeObject *)DType)->tp_repr == PyArrayDescr_Type.tp_repr + || ((PyTypeObject *)DType)->tp_str == PyArrayDescr_Type.tp_str) { + PyErr_SetString(PyExc_TypeError, + "A custom DType must implement `__repr__` and `__str__` since " + "the default inherited version (currently) fails."); + return -1; + } + + if (spec->typeobj == NULL || !PyType_Check(spec->typeobj)) { + PyErr_SetString(PyExc_TypeError, + "Not giving a type object is currently not supported, but " + "is expected to be supported eventually. This would mean " + "that e.g. indexing a NumPy array will return a 0-D array " + "and not a scalar."); + return -1; + } + + if (DType->dt_slots != NULL) { + PyErr_Format(PyExc_RuntimeError, + "DType %R appears already registered?", DType); + return -1; + } + + /* Check and handle flags: */ + if (spec->flags & ~(NPY_DT_PARAMETRIC|NPY_DT_ABSTRACT)) { + PyErr_SetString(PyExc_RuntimeError, + "invalid DType flags specified, only parametric and abstract " + "are valid flags for user DTypes."); + return -1; + } + + DType->flags = spec->flags; + DType->dt_slots = PyMem_Calloc(1, sizeof(NPY_DType_Slots)); + if (DType->dt_slots == NULL) { + return -1; + } + + /* Set default values (where applicable) */ + NPY_DT_SLOTS(DType)->discover_descr_from_pyobject = &discover_as_default; + NPY_DT_SLOTS(DType)->is_known_scalar_type = ( + &python_builtins_are_known_scalar_types); + NPY_DT_SLOTS(DType)->default_descr = use_new_as_default; + NPY_DT_SLOTS(DType)->common_dtype = dtype_does_not_promote; + /* May need a default for non-parametric? */ + NPY_DT_SLOTS(DType)->common_instance = NULL; + NPY_DT_SLOTS(DType)->setitem = NULL; + NPY_DT_SLOTS(DType)->getitem = NULL; + + PyType_Slot *spec_slot = spec->slots; + while (1) { + int slot = spec_slot->slot; + void *pfunc = spec_slot->pfunc; + spec_slot++; + if (slot == 0) { + break; + } + if (slot > NUM_DTYPE_SLOTS || slot < 0) { + PyErr_Format(PyExc_RuntimeError, + "Invalid slot with value %d passed in.", slot); + return -1; + } + /* + * It is up to the user to get this right, and slots are sorted + * exactly like they are stored right now: + */ + void **current = (void **)(&( + NPY_DT_SLOTS(DType)->discover_descr_from_pyobject)); + current += slot - 1; + *current = pfunc; + } + if (NPY_DT_SLOTS(DType)->setitem == NULL + || NPY_DT_SLOTS(DType)->getitem == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "A DType must provide a getitem/setitem (there may be an " + "exception here in the future if no scalar type is provided)"); + return -1; + } + + /* + * Now that the spec is read we can check that all required functions were + * defined by the user. + */ + if (spec->flags & NPY_DT_PARAMETRIC) { + if (NPY_DT_SLOTS(DType)->common_instance == NULL || + NPY_DT_SLOTS(DType)->discover_descr_from_pyobject + == &discover_as_default) { + PyErr_SetString(PyExc_RuntimeError, + "Parametric DType must define a common-instance and " + "descriptor discovery function!"); + return -1; + } + } + NPY_DT_SLOTS(DType)->f = default_funcs; + /* invalid type num. Ideally, we get away with it! */ + DType->type_num = -1; + + /* + * Handle the scalar type mapping. + */ + Py_INCREF(spec->typeobj); + DType->scalar_type = spec->typeobj; + if (PyType_GetFlags(spec->typeobj) & Py_TPFLAGS_HEAPTYPE) { + if (PyObject_SetAttrString((PyObject *)DType->scalar_type, + "__associated_array_dtype__", (PyObject *)DType) < 0) { + Py_DECREF(DType); + return -1; + } + } + if (_PyArray_MapPyTypeToDType(DType, DType->scalar_type, 0) < 0) { + Py_DECREF(DType); + return -1; + } + + /* Ensure cast dict is defined (not sure we have to do it here) */ + NPY_DT_SLOTS(DType)->castingimpls = PyDict_New(); + if (NPY_DT_SLOTS(DType)->castingimpls == NULL) { + return -1; + } + /* + * And now, register all the casts that are currently defined! + */ + PyArrayMethod_Spec **next_meth_spec = spec->casts; + while (1) { + PyArrayMethod_Spec *meth_spec = *next_meth_spec; + next_meth_spec++; + if (meth_spec == NULL) { + break; + } + /* + * The user doesn't know the name of DType yet, so we have to fill it + * in for them! + */ + for (int i=0; i < meth_spec->nin + meth_spec->nout; i++) { + if (meth_spec->dtypes[i] == NULL) { + meth_spec->dtypes[i] = DType; + } + } + /* Register the cast! */ + int res = PyArray_AddCastingImplementation_FromSpec(meth_spec, 0); + + /* Also clean up again, so nobody can get bad ideas... */ + for (int i=0; i < meth_spec->nin + meth_spec->nout; i++) { + if (meth_spec->dtypes[i] == DType) { + meth_spec->dtypes[i] = NULL; + } + } + + if (res < 0) { + return -1; + } + } + + if (NPY_DT_SLOTS(DType)->within_dtype_castingimpl == NULL) { + /* + * We expect this for now. We should have a default for DType that + * only supports simple copy (and possibly byte-order assuming that + * they swap the full itemsize). + */ + PyErr_SetString(PyExc_RuntimeError, + "DType must provide a function to cast (or just copy) between " + "its own instances!"); + return -1; + } + + /* And finally, we have to register all the casts! */ + return 0; +} + + +/* Function is defined in umath/dispatching.c (same/one compilation unit) */ +NPY_NO_EXPORT int +PyUFunc_AddLoop(PyUFuncObject *ufunc, PyObject *info, int ignore_duplicate); + +static int +PyUFunc_AddLoopFromSpec(PyObject *ufunc, PyArrayMethod_Spec *spec) +{ + if (!PyObject_TypeCheck(ufunc, &PyUFunc_Type)) { + PyErr_SetString(PyExc_TypeError, + "ufunc object passed is not a ufunc!"); + return -1; + } + PyBoundArrayMethodObject *bmeth = + (PyBoundArrayMethodObject *)PyArrayMethod_FromSpec(spec); + if (bmeth == NULL) { + return -1; + } + int nargs = bmeth->method->nin + bmeth->method->nout; + PyObject *dtypes = PyArray_TupleFromItems( + nargs, (PyObject **)bmeth->dtypes, 1); + if (dtypes == NULL) { + return -1; + } + PyObject *info = PyTuple_Pack(2, dtypes, bmeth->method); + Py_DECREF(bmeth); + Py_DECREF(dtypes); + if (info == NULL) { + return -1; + } + return PyUFunc_AddLoop((PyUFuncObject *)ufunc, info, 0); +} + + +static int +PyUFunc_AddPromoter( + PyObject *ufunc, PyObject *DType_tuple, PyObject *promoter) +{ + if (!PyObject_TypeCheck(ufunc, &PyUFunc_Type)) { + PyErr_SetString(PyExc_TypeError, + "ufunc object passed is not a ufunc!"); + return -1; + } + if (!PyCapsule_CheckExact(promoter)) { + PyErr_SetString(PyExc_TypeError, + "promoter must (currently) be a PyCapsule."); + return -1; + } + if (PyCapsule_GetPointer(promoter, "numpy._ufunc_promoter") == NULL) { + return -1; + } + PyObject *info = PyTuple_Pack(2, DType_tuple, promoter); + if (info == NULL) { + return -1; + } + return PyUFunc_AddLoop((PyUFuncObject *)ufunc, info, 0); +} + + +NPY_NO_EXPORT PyObject * +_get_experimental_dtype_api(PyObject *NPY_UNUSED(mod), PyObject *arg) +{ + static void *experimental_api_table[] = { + &PyUFunc_AddLoopFromSpec, + &PyUFunc_AddPromoter, + &PyArrayDTypeMeta_Type, + &PyArrayInitDTypeMeta_FromSpec, + &PyArray_CommonDType, + &PyArray_PromoteDTypeSequence, + NULL, + }; + + char *env = getenv("NUMPY_EXPERIMENTAL_DTYPE_API"); + if (env == NULL || strcmp(env, "1") != 0) { + PyErr_Format(PyExc_RuntimeError, + "The new DType API is currently in an exploratory phase and " + "should NOT be used for production code. " + "Expect modifications and crashes! " + "To experiment with the new API you must set " + "`NUMPY_EXPERIMENTAL_DTYPE_API=1` as an environment variable."); + return NULL; + } + + long version = PyLong_AsLong(arg); + if (error_converting(version)) { + return NULL; + } + if (version != EXPERIMENTAL_DTYPE_API_VERSION) { + PyErr_Format(PyExc_RuntimeError, + "Experimental DType API version %d requested, but NumPy " + "is exporting version %d. Recompile your DType and/or upgrade " + "NumPy to match.", + version, EXPERIMENTAL_DTYPE_API_VERSION); + return NULL; + } + + return PyCapsule_New(&experimental_api_table, + "experimental_dtype_api_table", NULL); +} diff --git a/numpy/core/src/multiarray/experimental_public_dtype_api.h b/numpy/core/src/multiarray/experimental_public_dtype_api.h new file mode 100644 index 000000000000..270cb82bf6a2 --- /dev/null +++ b/numpy/core/src/multiarray/experimental_public_dtype_api.h @@ -0,0 +1,18 @@ +/* + * This file exports the experimental dtype API as exposed via the + * `numpy/core/include/numpy/experimental_dtype_api.h` + * header file. + * + * This file is a stub, all important definitions are in the code file. + * + * NOTE: This file is considered in-flux, exploratory and transitional. + */ + +#ifndef NUMPY_CORE_SRC_MULTIARRAY_EXPERIMENTAL_PUBLIC_DTYPE_API_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_EXPERIMENTAL_PUBLIC_DTYPE_API_H_ + +NPY_NO_EXPORT PyObject * +_get_experimental_dtype_api(PyObject *mod, PyObject *arg); + + +#endif /* NUMPY_CORE_SRC_MULTIARRAY_EXPERIMENTAL_PUBLIC_DTYPE_API_H_ */ diff --git a/numpy/core/src/multiarray/flagsobject.c b/numpy/core/src/multiarray/flagsobject.c index a78bedccb9cc..3b1b4f406194 100644 --- a/numpy/core/src/multiarray/flagsobject.c +++ b/numpy/core/src/multiarray/flagsobject.c @@ -1,17 +1,19 @@ /* Array Flags Object */ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE #define PY_SSIZE_T_CLEAN #include <Python.h> -#include "structmember.h" +#include <structmember.h> -#define NPY_NO_DEPRECATED_API NPY_API_VERSION -#define _MULTIARRAYMODULE #include "numpy/arrayobject.h" +#include "arrayobject.h" #include "numpy/arrayscalars.h" #include "npy_config.h" #include "npy_pycompat.h" +#include "array_assign.h" #include "common.h" @@ -64,7 +66,7 @@ PyArray_UpdateFlags(PyArrayObject *ret, int flagmask) _UpdateContiguousFlags(ret); } if (flagmask & NPY_ARRAY_ALIGNED) { - if (_IsAligned(ret)) { + if (IsAligned(ret)) { PyArray_ENABLEFLAGS(ret, NPY_ARRAY_ALIGNED); } else { @@ -146,7 +148,7 @@ _UpdateContiguousFlags(PyArrayObject *ap) if (PyArray_STRIDES(ap)[i] != sd) { is_c_contig = 0; break; - } + } /* contiguous, if it got this far */ if (dim == 0) { break; @@ -198,28 +200,45 @@ arrayflags_dealloc(PyArrayFlagsObject *self) #define _define_get(UPPER, lower) \ static PyObject * \ - arrayflags_ ## lower ## _get(PyArrayFlagsObject *self) \ + arrayflags_ ## lower ## _get( \ + PyArrayFlagsObject *self, void *NPY_UNUSED(ignored)) \ { \ - PyObject *item; \ - item = ((self->flags & (UPPER)) == (UPPER)) ? Py_True : Py_False; \ - Py_INCREF(item); \ - return item; \ + return PyBool_FromLong((self->flags & (UPPER)) == (UPPER)); \ } +static char *msg = "future versions will not create a writeable " + "array from broadcast_array. Set the writable flag explicitly to " + "avoid this warning."; + +#define _define_get_warn(UPPER, lower) \ + static PyObject * \ + arrayflags_ ## lower ## _get( \ + PyArrayFlagsObject *self, void *NPY_UNUSED(ignored)) \ + { \ + if (self->flags & NPY_ARRAY_WARN_ON_WRITE) { \ + if (PyErr_Warn(PyExc_FutureWarning, msg) < 0) {\ + return NULL; \ + } \ + }\ + return PyBool_FromLong((self->flags & (UPPER)) == (UPPER)); \ + } + + _define_get(NPY_ARRAY_C_CONTIGUOUS, contiguous) _define_get(NPY_ARRAY_F_CONTIGUOUS, fortran) _define_get(NPY_ARRAY_WRITEBACKIFCOPY, writebackifcopy) _define_get(NPY_ARRAY_OWNDATA, owndata) _define_get(NPY_ARRAY_ALIGNED, aligned) -_define_get(NPY_ARRAY_WRITEABLE, writeable) -_define_get(NPY_ARRAY_ALIGNED| +_define_get(NPY_ARRAY_WRITEABLE, writeable_no_warn) +_define_get_warn(NPY_ARRAY_WRITEABLE, writeable) +_define_get_warn(NPY_ARRAY_ALIGNED| NPY_ARRAY_WRITEABLE, behaved) -_define_get(NPY_ARRAY_ALIGNED| +_define_get_warn(NPY_ARRAY_ALIGNED| NPY_ARRAY_WRITEABLE| NPY_ARRAY_C_CONTIGUOUS, carray) static PyObject * -arrayflags_updateifcopy_get(PyArrayFlagsObject *self) +arrayflags_updateifcopy_get(PyArrayFlagsObject *self, void *NPY_UNUSED(ignored)) { PyObject *item; /* 2017-Nov-10 1.14 */ @@ -238,7 +257,7 @@ arrayflags_updateifcopy_get(PyArrayFlagsObject *self) static PyObject * -arrayflags_forc_get(PyArrayFlagsObject *self) +arrayflags_forc_get(PyArrayFlagsObject *self, void *NPY_UNUSED(ignored)) { PyObject *item; @@ -254,7 +273,7 @@ arrayflags_forc_get(PyArrayFlagsObject *self) } static PyObject * -arrayflags_fnc_get(PyArrayFlagsObject *self) +arrayflags_fnc_get(PyArrayFlagsObject *self, void *NPY_UNUSED(ignored)) { PyObject *item; @@ -270,7 +289,7 @@ arrayflags_fnc_get(PyArrayFlagsObject *self) } static PyObject * -arrayflags_farray_get(PyArrayFlagsObject *self) +arrayflags_farray_get(PyArrayFlagsObject *self, void *NPY_UNUSED(ignored)) { PyObject *item; @@ -288,14 +307,15 @@ arrayflags_farray_get(PyArrayFlagsObject *self) } static PyObject * -arrayflags_num_get(PyArrayFlagsObject *self) +arrayflags_num_get(PyArrayFlagsObject *self, void *NPY_UNUSED(ignored)) { - return PyInt_FromLong(self->flags); + return PyLong_FromLong(self->flags); } /* relies on setflags order being write, align, uic */ static int -arrayflags_updateifcopy_set(PyArrayFlagsObject *self, PyObject *obj) +arrayflags_updateifcopy_set( + PyArrayFlagsObject *self, PyObject *obj, void *NPY_UNUSED(ignored)) { PyObject *res; @@ -324,7 +344,8 @@ arrayflags_updateifcopy_set(PyArrayFlagsObject *self, PyObject *obj) /* relies on setflags order being write, align, uic */ static int -arrayflags_writebackifcopy_set(PyArrayFlagsObject *self, PyObject *obj) +arrayflags_writebackifcopy_set( + PyArrayFlagsObject *self, PyObject *obj, void *NPY_UNUSED(ignored)) { PyObject *res; @@ -348,7 +369,8 @@ arrayflags_writebackifcopy_set(PyArrayFlagsObject *self, PyObject *obj) } static int -arrayflags_aligned_set(PyArrayFlagsObject *self, PyObject *obj) +arrayflags_aligned_set( + PyArrayFlagsObject *self, PyObject *obj, void *NPY_UNUSED(ignored)) { PyObject *res; @@ -373,7 +395,8 @@ arrayflags_aligned_set(PyArrayFlagsObject *self, PyObject *obj) } static int -arrayflags_writeable_set(PyArrayFlagsObject *self, PyObject *obj) +arrayflags_writeable_set( + PyArrayFlagsObject *self, PyObject *obj, void *NPY_UNUSED(ignored)) { PyObject *res; @@ -397,6 +420,41 @@ arrayflags_writeable_set(PyArrayFlagsObject *self, PyObject *obj) return 0; } +static int +arrayflags_warn_on_write_set( + PyArrayFlagsObject *self, PyObject *obj, void *NPY_UNUSED(ignored)) +{ + /* + * This code should go away in a future release, so do not mangle the + * array_setflags function with an extra kwarg + */ + int ret; + if (obj == NULL) { + PyErr_SetString(PyExc_AttributeError, + "Cannot delete flags _warn_on_write attribute"); + return -1; + } + ret = PyObject_IsTrue(obj); + if (ret > 0) { + if (!(PyArray_FLAGS((PyArrayObject*)self->arr) & NPY_ARRAY_WRITEABLE)) { + PyErr_SetString(PyExc_ValueError, + "cannot set '_warn_on_write' flag when 'writable' is " + "False"); + return -1; + } + PyArray_ENABLEFLAGS((PyArrayObject*)self->arr, NPY_ARRAY_WARN_ON_WRITE); + } + else if (ret < 0) { + return -1; + } + else { + PyErr_SetString(PyExc_ValueError, + "cannot clear '_warn_on_write', set " + "writeable True to clear this private flag"); + return -1; + } + return 0; +} static PyGetSetDef arrayflags_getsets[] = { {"contiguous", @@ -435,6 +493,14 @@ static PyGetSetDef arrayflags_getsets[] = { (getter)arrayflags_writeable_get, (setter)arrayflags_writeable_set, NULL, NULL}, + {"_writeable_no_warn", + (getter)arrayflags_writeable_no_warn_get, + (setter)NULL, + NULL, NULL}, + {"_warn_on_write", + (getter)NULL, + (setter)arrayflags_warn_on_write_set, + NULL, NULL}, {"fnc", (getter)arrayflags_fnc_get, NULL, @@ -495,89 +561,89 @@ arrayflags_getitem(PyArrayFlagsObject *self, PyObject *ind) case 1: switch(key[0]) { case 'C': - return arrayflags_contiguous_get(self); + return arrayflags_contiguous_get(self, NULL); case 'F': - return arrayflags_fortran_get(self); + return arrayflags_fortran_get(self, NULL); case 'W': - return arrayflags_writeable_get(self); + return arrayflags_writeable_get(self, NULL); case 'B': - return arrayflags_behaved_get(self); + return arrayflags_behaved_get(self, NULL); case 'O': - return arrayflags_owndata_get(self); + return arrayflags_owndata_get(self, NULL); case 'A': - return arrayflags_aligned_get(self); + return arrayflags_aligned_get(self, NULL); case 'X': - return arrayflags_writebackifcopy_get(self); + return arrayflags_writebackifcopy_get(self, NULL); case 'U': - return arrayflags_updateifcopy_get(self); + return arrayflags_updateifcopy_get(self, NULL); default: goto fail; } break; case 2: if (strncmp(key, "CA", n) == 0) { - return arrayflags_carray_get(self); + return arrayflags_carray_get(self, NULL); } if (strncmp(key, "FA", n) == 0) { - return arrayflags_farray_get(self); + return arrayflags_farray_get(self, NULL); } break; case 3: if (strncmp(key, "FNC", n) == 0) { - return arrayflags_fnc_get(self); + return arrayflags_fnc_get(self, NULL); } break; case 4: if (strncmp(key, "FORC", n) == 0) { - return arrayflags_forc_get(self); + return arrayflags_forc_get(self, NULL); } break; case 6: if (strncmp(key, "CARRAY", n) == 0) { - return arrayflags_carray_get(self); + return arrayflags_carray_get(self, NULL); } if (strncmp(key, "FARRAY", n) == 0) { - return arrayflags_farray_get(self); + return arrayflags_farray_get(self, NULL); } break; case 7: if (strncmp(key,"FORTRAN",n) == 0) { - return arrayflags_fortran_get(self); + return arrayflags_fortran_get(self, NULL); } if (strncmp(key,"BEHAVED",n) == 0) { - return arrayflags_behaved_get(self); + return arrayflags_behaved_get(self, NULL); } if (strncmp(key,"OWNDATA",n) == 0) { - return arrayflags_owndata_get(self); + return arrayflags_owndata_get(self, NULL); } if (strncmp(key,"ALIGNED",n) == 0) { - return arrayflags_aligned_get(self); + return arrayflags_aligned_get(self, NULL); } break; case 9: if (strncmp(key,"WRITEABLE",n) == 0) { - return arrayflags_writeable_get(self); + return arrayflags_writeable_get(self, NULL); } break; case 10: if (strncmp(key,"CONTIGUOUS",n) == 0) { - return arrayflags_contiguous_get(self); + return arrayflags_contiguous_get(self, NULL); } break; case 12: if (strncmp(key, "UPDATEIFCOPY", n) == 0) { - return arrayflags_updateifcopy_get(self); + return arrayflags_updateifcopy_get(self, NULL); } if (strncmp(key, "C_CONTIGUOUS", n) == 0) { - return arrayflags_contiguous_get(self); + return arrayflags_contiguous_get(self, NULL); } if (strncmp(key, "F_CONTIGUOUS", n) == 0) { - return arrayflags_fortran_get(self); + return arrayflags_fortran_get(self, NULL); } break; case 15: if (strncmp(key, "WRITEBACKIFCOPY", n) == 0) { - return arrayflags_writebackifcopy_get(self); + return arrayflags_writebackifcopy_get(self, NULL); } break; } @@ -612,19 +678,19 @@ arrayflags_setitem(PyArrayFlagsObject *self, PyObject *ind, PyObject *item) } if (((n==9) && (strncmp(key, "WRITEABLE", n) == 0)) || ((n==1) && (strncmp(key, "W", n) == 0))) { - return arrayflags_writeable_set(self, item); + return arrayflags_writeable_set(self, item, NULL); } else if (((n==7) && (strncmp(key, "ALIGNED", n) == 0)) || ((n==1) && (strncmp(key, "A", n) == 0))) { - return arrayflags_aligned_set(self, item); + return arrayflags_aligned_set(self, item, NULL); } else if (((n==12) && (strncmp(key, "UPDATEIFCOPY", n) == 0)) || ((n==1) && (strncmp(key, "U", n) == 0))) { - return arrayflags_updateifcopy_set(self, item); + return arrayflags_updateifcopy_set(self, item, NULL); } - else if (((n==14) && (strncmp(key, "WRITEBACKIFCOPY", n) == 0)) || + else if (((n==15) && (strncmp(key, "WRITEBACKIFCOPY", n) == 0)) || ((n==1) && (strncmp(key, "X", n) == 0))) { - return arrayflags_writebackifcopy_set(self, item); + return arrayflags_writebackifcopy_set(self, item, NULL); } fail: @@ -647,62 +713,46 @@ static PyObject * arrayflags_print(PyArrayFlagsObject *self) { int fl = self->flags; + const char *_warn_on_write = ""; - return PyUString_FromFormat( - " %s : %s\n %s : %s\n" + if (fl & NPY_ARRAY_WARN_ON_WRITE) { + _warn_on_write = " (with WARN_ON_WRITE=True)"; + } + return PyUnicode_FromFormat( " %s : %s\n %s : %s\n" + " %s : %s\n %s : %s%s\n" " %s : %s\n %s : %s\n" - " %s : %s", + " %s : %s\n", "C_CONTIGUOUS", _torf_(fl, NPY_ARRAY_C_CONTIGUOUS), "F_CONTIGUOUS", _torf_(fl, NPY_ARRAY_F_CONTIGUOUS), "OWNDATA", _torf_(fl, NPY_ARRAY_OWNDATA), "WRITEABLE", _torf_(fl, NPY_ARRAY_WRITEABLE), + _warn_on_write, "ALIGNED", _torf_(fl, NPY_ARRAY_ALIGNED), "WRITEBACKIFCOPY", _torf_(fl, NPY_ARRAY_WRITEBACKIFCOPY), - "UPDATEIFCOPY", _torf_(fl, NPY_ARRAY_UPDATEIFCOPY)); -} - -static int -arrayflags_compare(PyArrayFlagsObject *self, PyArrayFlagsObject *other) -{ - if (self->flags == other->flags) { - return 0; - } - else if (self->flags < other->flags) { - return -1; - } - else { - return 1; - } + "UPDATEIFCOPY", _torf_(fl, NPY_ARRAY_UPDATEIFCOPY) + ); } - static PyObject* arrayflags_richcompare(PyObject *self, PyObject *other, int cmp_op) { - PyObject *result = Py_NotImplemented; - int cmp; - - if (cmp_op != Py_EQ && cmp_op != Py_NE) { - PyErr_SetString(PyExc_TypeError, - "undefined comparison for flag object"); - return NULL; + if (!PyObject_TypeCheck(other, &PyArrayFlags_Type)) { + Py_RETURN_NOTIMPLEMENTED; } - if (PyObject_TypeCheck(other, &PyArrayFlags_Type)) { - cmp = arrayflags_compare((PyArrayFlagsObject *)self, - (PyArrayFlagsObject *)other); + npy_bool eq = ((PyArrayFlagsObject*) self)->flags == + ((PyArrayFlagsObject*) other)->flags; - if (cmp_op == Py_EQ) { - result = (cmp == 0) ? Py_True : Py_False; - } - else if (cmp_op == Py_NE) { - result = (cmp != 0) ? Py_True : Py_False; - } + if (cmp_op == Py_EQ) { + return PyBool_FromLong(eq); + } + else if (cmp_op == Py_NE) { + return PyBool_FromLong(!eq); + } + else { + Py_RETURN_NOTIMPLEMENTED; } - - Py_INCREF(result); - return result; } static PyMappingMethods arrayflags_as_mapping = { @@ -728,61 +778,15 @@ arrayflags_new(PyTypeObject *NPY_UNUSED(self), PyObject *args, PyObject *NPY_UNU } NPY_NO_EXPORT PyTypeObject PyArrayFlags_Type = { -#if defined(NPY_PY3K) PyVarObject_HEAD_INIT(NULL, 0) -#else - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ -#endif - "numpy.flagsobj", - sizeof(PyArrayFlagsObject), - 0, /* tp_itemsize */ - /* methods */ - (destructor)arrayflags_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ -#if defined(NPY_PY3K) - 0, /* tp_reserved */ -#else - (cmpfunc)arrayflags_compare, /* tp_compare */ -#endif - (reprfunc)arrayflags_print, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - &arrayflags_as_mapping, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - (reprfunc)arrayflags_print, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - 0, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - arrayflags_richcompare, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - 0, /* tp_methods */ - 0, /* tp_members */ - arrayflags_getsets, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - arrayflags_new, /* tp_new */ - 0, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - 0, /* tp_del */ - 0, /* tp_version_tag */ + .tp_name = "numpy.core.multiarray.flagsobj", + .tp_basicsize = sizeof(PyArrayFlagsObject), + .tp_dealloc = (destructor)arrayflags_dealloc, + .tp_repr = (reprfunc)arrayflags_print, + .tp_as_mapping = &arrayflags_as_mapping, + .tp_str = (reprfunc)arrayflags_print, + .tp_flags =Py_TPFLAGS_DEFAULT, + .tp_richcompare = arrayflags_richcompare, + .tp_getset = arrayflags_getsets, + .tp_new = arrayflags_new, }; diff --git a/numpy/core/src/multiarray/getset.c b/numpy/core/src/multiarray/getset.c index cae4273fffa9..a92ac44b7846 100644 --- a/numpy/core/src/multiarray/getset.c +++ b/numpy/core/src/multiarray/getset.c @@ -1,11 +1,11 @@ /* Array Descr Object */ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE #define PY_SSIZE_T_CLEAN #include <Python.h> -#include "structmember.h" +#include <structmember.h> -#define NPY_NO_DEPRECATED_API NPY_API_VERSION -#define _MULTIARRAYMODULE #include "numpy/arrayobject.h" #include "npy_config.h" @@ -13,6 +13,7 @@ #include "npy_import.h" #include "common.h" +#include "conversion_utils.h" #include "ctors.h" #include "scalartypes.h" #include "descriptor.h" @@ -20,30 +21,31 @@ #include "arrayobject.h" #include "mem_overlap.h" #include "alloc.h" +#include "npy_buffer.h" /******************* array attribute get and set routines ******************/ static PyObject * -array_ndim_get(PyArrayObject *self) +array_ndim_get(PyArrayObject *self, void *NPY_UNUSED(ignored)) { - return PyInt_FromLong(PyArray_NDIM(self)); + return PyLong_FromLong(PyArray_NDIM(self)); } static PyObject * -array_flags_get(PyArrayObject *self) +array_flags_get(PyArrayObject *self, void *NPY_UNUSED(ignored)) { return PyArray_NewFlagsObject((PyObject *)self); } static PyObject * -array_shape_get(PyArrayObject *self) +array_shape_get(PyArrayObject *self, void *NPY_UNUSED(ignored)) { return PyArray_IntTupleFromIntp(PyArray_NDIM(self), PyArray_DIMS(self)); } static int -array_shape_set(PyArrayObject *self, PyObject *val) +array_shape_set(PyArrayObject *self, PyObject *val, void* NPY_UNUSED(ignored)) { int nd; PyArrayObject *ret; @@ -61,31 +63,39 @@ array_shape_set(PyArrayObject *self, PyObject *val) if (PyArray_DATA(ret) != PyArray_DATA(self)) { Py_DECREF(ret); PyErr_SetString(PyExc_AttributeError, - "incompatible shape for a non-contiguous "\ - "array"); + "Incompatible shape for in-place modification. Use " + "`.reshape()` to make a copy with the desired shape."); return -1; } - /* Free old dimensions and strides */ - npy_free_cache_dim_array(self); nd = PyArray_NDIM(ret); - ((PyArrayObject_fields *)self)->nd = nd; if (nd > 0) { /* create new dimensions and strides */ - ((PyArrayObject_fields *)self)->dimensions = npy_alloc_cache_dim(3*nd); - if (PyArray_DIMS(self) == NULL) { + npy_intp *_dimensions = npy_alloc_cache_dim(2 * nd); + if (_dimensions == NULL) { Py_DECREF(ret); - PyErr_SetString(PyExc_MemoryError,""); + PyErr_NoMemory(); return -1; } - ((PyArrayObject_fields *)self)->strides = PyArray_DIMS(self) + nd; - memcpy(PyArray_DIMS(self), PyArray_DIMS(ret), nd*sizeof(npy_intp)); - memcpy(PyArray_STRIDES(self), PyArray_STRIDES(ret), nd*sizeof(npy_intp)); + /* Free old dimensions and strides */ + npy_free_cache_dim_array(self); + ((PyArrayObject_fields *)self)->nd = nd; + ((PyArrayObject_fields *)self)->dimensions = _dimensions; + ((PyArrayObject_fields *)self)->strides = _dimensions + nd; + + if (nd) { + memcpy(PyArray_DIMS(self), PyArray_DIMS(ret), nd*sizeof(npy_intp)); + memcpy(PyArray_STRIDES(self), PyArray_STRIDES(ret), nd*sizeof(npy_intp)); + } } else { + /* Free old dimensions and strides */ + npy_free_cache_dim_array(self); + ((PyArrayObject_fields *)self)->nd = 0; ((PyArrayObject_fields *)self)->dimensions = NULL; ((PyArrayObject_fields *)self)->strides = NULL; } + Py_DECREF(ret); PyArray_UpdateFlags(self, NPY_ARRAY_C_CONTIGUOUS | NPY_ARRAY_F_CONTIGUOUS); return 0; @@ -93,34 +103,29 @@ array_shape_set(PyArrayObject *self, PyObject *val) static PyObject * -array_strides_get(PyArrayObject *self) +array_strides_get(PyArrayObject *self, void *NPY_UNUSED(ignored)) { return PyArray_IntTupleFromIntp(PyArray_NDIM(self), PyArray_STRIDES(self)); } static int -array_strides_set(PyArrayObject *self, PyObject *obj) +array_strides_set(PyArrayObject *self, PyObject *obj, void *NPY_UNUSED(ignored)) { - PyArray_Dims newstrides = {NULL, 0}; + PyArray_Dims newstrides = {NULL, -1}; PyArrayObject *new; npy_intp numbytes = 0; npy_intp offset = 0; npy_intp lower_offset = 0; npy_intp upper_offset = 0; -#if defined(NPY_PY3K) Py_buffer view; -#else - Py_ssize_t buf_len; - char *buf; -#endif if (obj == NULL) { PyErr_SetString(PyExc_AttributeError, "Cannot delete array strides"); return -1; } - if (!PyArray_IntpConverter(obj, &newstrides) || - newstrides.ptr == NULL) { + if (!PyArray_OptionalIntpConverter(obj, &newstrides) || + newstrides.len == -1) { PyErr_SetString(PyExc_TypeError, "invalid strides"); return -1; } @@ -137,21 +142,12 @@ array_strides_set(PyArrayObject *self, PyObject *obj) * Get the available memory through the buffer interface on * PyArray_BASE(new) or if that fails from the current new */ -#if defined(NPY_PY3K) if (PyArray_BASE(new) && PyObject_GetBuffer(PyArray_BASE(new), &view, PyBUF_SIMPLE) >= 0) { offset = PyArray_BYTES(self) - (char *)view.buf; numbytes = view.len + offset; PyBuffer_Release(&view); } -#else - if (PyArray_BASE(new) && - PyObject_AsReadBuffer(PyArray_BASE(new), (const void **)&buf, - &buf_len) >= 0) { - offset = PyArray_BYTES(self) - buf; - numbytes = buf_len + offset; - } -#endif else { PyErr_Clear(); offset_bounds_from_strides(PyArray_ITEMSIZE(new), PyArray_NDIM(new), @@ -170,7 +166,9 @@ array_strides_set(PyArrayObject *self, PyObject *obj) "compatible with available memory"); goto fail; } - memcpy(PyArray_STRIDES(self), newstrides.ptr, sizeof(npy_intp)*newstrides.len); + if (newstrides.len) { + memcpy(PyArray_STRIDES(self), newstrides.ptr, sizeof(npy_intp)*newstrides.len); + } PyArray_UpdateFlags(self, NPY_ARRAY_C_CONTIGUOUS | NPY_ARRAY_F_CONTIGUOUS | NPY_ARRAY_ALIGNED); npy_free_cache_dim_obj(newstrides); @@ -184,24 +182,19 @@ array_strides_set(PyArrayObject *self, PyObject *obj) static PyObject * -array_priority_get(PyArrayObject *self) +array_priority_get(PyArrayObject *NPY_UNUSED(self), void *NPY_UNUSED(ignored)) { - if (PyArray_CheckExact(self)) { - return PyFloat_FromDouble(NPY_PRIORITY); - } - else { - return PyFloat_FromDouble(NPY_PRIORITY); - } + return PyFloat_FromDouble(NPY_PRIORITY); } static PyObject * array_typestr_get(PyArrayObject *self) { - return arraydescr_protocol_typestr_get(PyArray_DESCR(self)); + return arraydescr_protocol_typestr_get(PyArray_DESCR(self), NULL); } static PyObject * -array_descr_get(PyArrayObject *self) +array_descr_get(PyArrayObject *self, void *NPY_UNUSED(ignored)) { Py_INCREF(PyArray_DESCR(self)); return (PyObject *)PyArray_DESCR(self); @@ -213,7 +206,7 @@ array_protocol_descr_get(PyArrayObject *self) PyObject *res; PyObject *dobj; - res = arraydescr_protocol_descr_get(PyArray_DESCR(self)); + res = arraydescr_protocol_descr_get(PyArray_DESCR(self), NULL); if (res) { return res; } @@ -224,7 +217,7 @@ array_protocol_descr_get(PyArrayObject *self) if (dobj == NULL) { return NULL; } - PyTuple_SET_ITEM(dobj, 0, PyString_FromString("")); + PyTuple_SET_ITEM(dobj, 0, PyUnicode_FromString("")); PyTuple_SET_ITEM(dobj, 1, array_typestr_get(self)); res = PyList_New(1); if (res == NULL) { @@ -247,16 +240,17 @@ array_protocol_strides_get(PyArrayObject *self) static PyObject * -array_dataptr_get(PyArrayObject *self) +array_dataptr_get(PyArrayObject *self, void *NPY_UNUSED(ignored)) { return Py_BuildValue("NO", PyLong_FromVoidPtr(PyArray_DATA(self)), - (PyArray_FLAGS(self) & NPY_ARRAY_WRITEABLE ? Py_False : - Py_True)); + ((PyArray_FLAGS(self) & NPY_ARRAY_WRITEABLE) && + !(PyArray_FLAGS(self) & NPY_ARRAY_WARN_ON_WRITE)) ? + Py_False : Py_True); } static PyObject * -array_ctypes_get(PyArrayObject *self) +array_ctypes_get(PyArrayObject *self, void *NPY_UNUSED(ignored)) { PyObject *_numpy_internal; PyObject *ret; @@ -271,7 +265,7 @@ array_ctypes_get(PyArrayObject *self) } static PyObject * -array_interface_get(PyArrayObject *self) +array_interface_get(PyArrayObject *self, void *NPY_UNUSED(ignored)) { PyObject *dict; PyObject *obj; @@ -281,70 +275,73 @@ array_interface_get(PyArrayObject *self) return NULL; } - if (array_might_be_written(self) < 0) { - Py_DECREF(dict); - return NULL; - } + int ret; /* dataptr */ - obj = array_dataptr_get(self); - PyDict_SetItemString(dict, "data", obj); + obj = array_dataptr_get(self, NULL); + ret = PyDict_SetItemString(dict, "data", obj); Py_DECREF(obj); + if (ret < 0) { + Py_DECREF(dict); + return NULL; + } obj = array_protocol_strides_get(self); - PyDict_SetItemString(dict, "strides", obj); + ret = PyDict_SetItemString(dict, "strides", obj); Py_DECREF(obj); + if (ret < 0) { + Py_DECREF(dict); + return NULL; + } obj = array_protocol_descr_get(self); - PyDict_SetItemString(dict, "descr", obj); + ret = PyDict_SetItemString(dict, "descr", obj); Py_DECREF(obj); + if (ret < 0) { + Py_DECREF(dict); + return NULL; + } - obj = arraydescr_protocol_typestr_get(PyArray_DESCR(self)); - PyDict_SetItemString(dict, "typestr", obj); + obj = arraydescr_protocol_typestr_get(PyArray_DESCR(self), NULL); + ret = PyDict_SetItemString(dict, "typestr", obj); Py_DECREF(obj); + if (ret < 0) { + Py_DECREF(dict); + return NULL; + } - obj = array_shape_get(self); - PyDict_SetItemString(dict, "shape", obj); + obj = array_shape_get(self, NULL); + ret = PyDict_SetItemString(dict, "shape", obj); Py_DECREF(obj); + if (ret < 0) { + Py_DECREF(dict); + return NULL; + } - obj = PyInt_FromLong(3); - PyDict_SetItemString(dict, "version", obj); + obj = PyLong_FromLong(3); + ret = PyDict_SetItemString(dict, "version", obj); Py_DECREF(obj); + if (ret < 0) { + Py_DECREF(dict); + return NULL; + } return dict; } static PyObject * -array_data_get(PyArrayObject *self) +array_data_get(PyArrayObject *self, void *NPY_UNUSED(ignored)) { -#if defined(NPY_PY3K) return PyMemoryView_FromObject((PyObject *)self); -#else - npy_intp nbytes; - if (!(PyArray_ISONESEGMENT(self))) { - PyErr_SetString(PyExc_AttributeError, "cannot get single-"\ - "segment buffer for discontiguous array"); - return NULL; - } - nbytes = PyArray_NBYTES(self); - if (PyArray_ISWRITEABLE(self)) { - return PyBuffer_FromReadWriteObject((PyObject *)self, 0, (Py_ssize_t) nbytes); - } - else { - return PyBuffer_FromObject((PyObject *)self, 0, (Py_ssize_t) nbytes); - } -#endif } static int -array_data_set(PyArrayObject *self, PyObject *op) +array_data_set(PyArrayObject *self, PyObject *op, void *NPY_UNUSED(ignored)) { void *buf; Py_ssize_t buf_len; int writeable=1; -#if defined(NPY_PY3K) Py_buffer view; -#endif /* 2016-19-02, 1.12 */ int ret = DEPRECATE("Assigning the 'data' attribute is an " @@ -359,7 +356,6 @@ array_data_set(PyArrayObject *self, PyObject *op) "Cannot delete array data"); return -1; } -#if defined(NPY_PY3K) if (PyObject_GetBuffer(op, &view, PyBUF_WRITABLE|PyBUF_SIMPLE) < 0) { writeable = 0; PyErr_Clear(); @@ -376,18 +372,7 @@ array_data_set(PyArrayObject *self, PyObject *op) * sticks around after the release. */ PyBuffer_Release(&view); -#else - if (PyObject_AsWriteBuffer(op, &buf, &buf_len) < 0) { - PyErr_Clear(); - writeable = 0; - if (PyObject_AsReadBuffer(op, (const void **)&buf, &buf_len) < 0) { - PyErr_Clear(); - PyErr_SetString(PyExc_AttributeError, - "object does not have single-segment buffer interface"); - return -1; - } - } -#endif + if (!PyArray_ISONESEGMENT(self)) { PyErr_SetString(PyExc_AttributeError, "cannot set single-segment buffer for discontiguous array"); @@ -399,7 +384,16 @@ array_data_set(PyArrayObject *self, PyObject *op) } if (PyArray_FLAGS(self) & NPY_ARRAY_OWNDATA) { PyArray_XDECREF(self); - PyDataMem_FREE(PyArray_DATA(self)); + size_t nbytes = PyArray_NBYTES_ALLOCATED(self); + PyObject *handler = PyArray_HANDLER(self); + if (handler == NULL) { + /* This can happen if someone arbitrarily sets NPY_ARRAY_OWNDATA */ + PyErr_SetString(PyExc_RuntimeError, + "no memory handler found but OWNDATA flag set"); + return -1; + } + PyDataMem_UserFREE(PyArray_DATA(self), nbytes, handler); + Py_CLEAR(((PyArrayObject_fields *)self)->mem_handler); } if (PyArray_BASE(self)) { if ((PyArray_FLAGS(self) & NPY_ARRAY_WRITEBACKIFCOPY) || @@ -426,41 +420,21 @@ array_data_set(PyArrayObject *self, PyObject *op) static PyObject * -array_itemsize_get(PyArrayObject *self) +array_itemsize_get(PyArrayObject *self, void* NPY_UNUSED(ignored)) { - return PyInt_FromLong((long) PyArray_DESCR(self)->elsize); + return PyLong_FromLong((long) PyArray_DESCR(self)->elsize); } static PyObject * -array_size_get(PyArrayObject *self) +array_size_get(PyArrayObject *self, void* NPY_UNUSED(ignored)) { - npy_intp size=PyArray_SIZE(self); -#if NPY_SIZEOF_INTP <= NPY_SIZEOF_LONG - return PyInt_FromLong((long) size); -#else - if (size > NPY_MAX_LONG || size < NPY_MIN_LONG) { - return PyLong_FromLongLong(size); - } - else { - return PyInt_FromLong((long) size); - } -#endif + return PyArray_PyIntFromIntp(PyArray_SIZE(self)); } static PyObject * -array_nbytes_get(PyArrayObject *self) +array_nbytes_get(PyArrayObject *self, void *NPY_UNUSED(ignored)) { - npy_intp nbytes = PyArray_NBYTES(self); -#if NPY_SIZEOF_INTP <= NPY_SIZEOF_LONG - return PyInt_FromLong((long) nbytes); -#else - if (nbytes > NPY_MAX_LONG || nbytes < NPY_MIN_LONG) { - return PyLong_FromLongLong(nbytes); - } - else { - return PyInt_FromLong((long) nbytes); - } -#endif + return PyArray_PyIntFromIntp(PyArray_NBYTES(self)); } @@ -473,7 +447,7 @@ array_nbytes_get(PyArrayObject *self) * will be adjusted in that case as well. */ static int -array_descr_set(PyArrayObject *self, PyObject *arg) +array_descr_set(PyArrayObject *self, PyObject *arg, void *NPY_UNUSED(ignored)) { PyArray_Descr *newtype = NULL; @@ -633,16 +607,10 @@ array_descr_set(PyArrayObject *self, PyObject *arg) } static PyObject * -array_struct_get(PyArrayObject *self) +array_struct_get(PyArrayObject *self, void *NPY_UNUSED(ignored)) { PyArrayInterface *inter; - PyObject *ret; - if (PyArray_ISWRITEABLE(self)) { - if (array_might_be_written(self) < 0) { - return NULL; - } - } inter = (PyArrayInterface *)PyArray_malloc(sizeof(PyArrayInterface)); if (inter==NULL) { return PyErr_NoMemory(); @@ -652,6 +620,11 @@ array_struct_get(PyArrayObject *self) inter->typekind = PyArray_DESCR(self)->kind; inter->itemsize = PyArray_DESCR(self)->elsize; inter->flags = PyArray_FLAGS(self); + if (inter->flags & NPY_ARRAY_WARN_ON_WRITE) { + /* Export a warn-on-write array as read-only */ + inter->flags = inter->flags & ~NPY_ARRAY_WARN_ON_WRITE; + inter->flags = inter->flags & ~NPY_ARRAY_WRITEABLE; + } /* reset unused flags */ inter->flags &= ~(NPY_ARRAY_WRITEBACKIFCOPY | NPY_ARRAY_UPDATEIFCOPY |NPY_ARRAY_OWNDATA); if (PyArray_ISNOTSWAPPED(self)) inter->flags |= NPY_ARRAY_NOTSWAPPED; @@ -666,8 +639,10 @@ array_struct_get(PyArrayObject *self) return PyErr_NoMemory(); } inter->strides = inter->shape + PyArray_NDIM(self); - memcpy(inter->shape, PyArray_DIMS(self), sizeof(npy_intp)*PyArray_NDIM(self)); - memcpy(inter->strides, PyArray_STRIDES(self), sizeof(npy_intp)*PyArray_NDIM(self)); + if (PyArray_NDIM(self)) { + memcpy(inter->shape, PyArray_DIMS(self), sizeof(npy_intp)*PyArray_NDIM(self)); + memcpy(inter->strides, PyArray_STRIDES(self), sizeof(npy_intp)*PyArray_NDIM(self)); + } } else { inter->shape = NULL; @@ -675,7 +650,7 @@ array_struct_get(PyArrayObject *self) } inter->data = PyArray_DATA(self); if (PyDataType_HASFIELDS(PyArray_DESCR(self))) { - inter->descr = arraydescr_protocol_descr_get(PyArray_DESCR(self)); + inter->descr = arraydescr_protocol_descr_get(PyArray_DESCR(self), NULL); if (inter->descr == NULL) { PyErr_Clear(); } @@ -686,13 +661,19 @@ array_struct_get(PyArrayObject *self) else { inter->descr = NULL; } + PyObject *ret = PyCapsule_New(inter, NULL, gentype_struct_free); + if (ret == NULL) { + return NULL; + } Py_INCREF(self); - ret = NpyCapsule_FromVoidPtrAndDesc(inter, self, gentype_struct_free); + if (PyCapsule_SetContext(ret, self) < 0) { + return NULL; + } return ret; } static PyObject * -array_base_get(PyArrayObject *self) +array_base_get(PyArrayObject *self, void *NPY_UNUSED(ignored)) { if (PyArray_BASE(self) == NULL) { Py_RETURN_NONE; @@ -762,7 +743,7 @@ _get_part(PyArrayObject *self, int imag) */ static PyObject * -array_real_get(PyArrayObject *self) +array_real_get(PyArrayObject *self, void *NPY_UNUSED(ignored)) { PyArrayObject *ret; @@ -778,7 +759,7 @@ array_real_get(PyArrayObject *self) static int -array_real_set(PyArrayObject *self, PyObject *val) +array_real_set(PyArrayObject *self, PyObject *val, void *NPY_UNUSED(ignored)) { PyArrayObject *ret; PyArrayObject *new; @@ -816,7 +797,7 @@ array_real_set(PyArrayObject *self, PyObject *val) */ static PyObject * -array_imag_get(PyArrayObject *self) +array_imag_get(PyArrayObject *self, void *NPY_UNUSED(ignored)) { PyArrayObject *ret; @@ -844,7 +825,7 @@ array_imag_get(PyArrayObject *self) } static int -array_imag_set(PyArrayObject *self, PyObject *val) +array_imag_set(PyArrayObject *self, PyObject *val, void *NPY_UNUSED(ignored)) { if (val == NULL) { PyErr_SetString(PyExc_AttributeError, @@ -878,13 +859,13 @@ array_imag_set(PyArrayObject *self, PyObject *val) } static PyObject * -array_flat_get(PyArrayObject *self) +array_flat_get(PyArrayObject *self, void *NPY_UNUSED(ignored)) { return PyArray_IterNew((PyObject *)self); } static int -array_flat_set(PyArrayObject *self, PyObject *val) +array_flat_set(PyArrayObject *self, PyObject *val, void *NPY_UNUSED(ignored)) { PyArrayObject *arr = NULL; int retval = -1; @@ -956,7 +937,7 @@ array_flat_set(PyArrayObject *self, PyObject *val) } static PyObject * -array_transpose_get(PyArrayObject *self) +array_transpose_get(PyArrayObject *self, void *NPY_UNUSED(ignored)) { return PyArray_Transpose(self, NULL); } @@ -965,7 +946,7 @@ array_transpose_get(PyArrayObject *self) --- default sub-class behavior */ static PyObject * -array_finalize_get(PyArrayObject *NPY_UNUSED(self)) +array_finalize_get(PyArrayObject *NPY_UNUSED(self), void *NPY_UNUSED(ignored)) { Py_RETURN_NONE; } diff --git a/numpy/core/src/multiarray/getset.h b/numpy/core/src/multiarray/getset.h index 4f1209de5a64..a95c98020a18 100644 --- a/numpy/core/src/multiarray/getset.h +++ b/numpy/core/src/multiarray/getset.h @@ -1,6 +1,6 @@ -#ifndef _NPY_ARRAY_GETSET_H_ -#define _NPY_ARRAY_GETSET_H_ +#ifndef NUMPY_CORE_SRC_MULTIARRAY_GETSET_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_GETSET_H_ extern NPY_NO_EXPORT PyGetSetDef array_getsetlist[]; -#endif +#endif /* NUMPY_CORE_SRC_MULTIARRAY_GETSET_H_ */ diff --git a/numpy/core/src/multiarray/hashdescr.c b/numpy/core/src/multiarray/hashdescr.c index 8465093b9ca2..a3c9e986bf40 100644 --- a/numpy/core/src/multiarray/hashdescr.c +++ b/numpy/core/src/multiarray/hashdescr.c @@ -1,7 +1,9 @@ -#define PY_SSIZE_T_CLEAN -#include <Python.h> #define NPY_NO_DEPRECATED_API NPY_API_VERSION #define _MULTIARRAYMODULE + +#define PY_SSIZE_T_CLEAN +#include <Python.h> + #include <numpy/arrayobject.h> #include "npy_config.h" @@ -36,17 +38,17 @@ static int _array_descr_builtin(PyArray_Descr* descr, PyObject *l); */ static char _normalize_byteorder(char byteorder) { - switch(byteorder) { - case '=': - if (PyArray_GetEndianness() == NPY_CPU_BIG) { - return '>'; - } - else { - return '<'; - } - default: - return byteorder; - } + switch(byteorder) { + case '=': + if (PyArray_GetEndianness() == NPY_CPU_BIG) { + return '>'; + } + else { + return '<'; + } + default: + return byteorder; + } } /* @@ -132,7 +134,7 @@ static int _array_descr_walk_fields(PyObject *names, PyObject* fields, PyObject* "(Hash) names and fields inconsistent ???"); return -1; } - if (!PyUString_Check(key)) { + if (!PyUnicode_Check(key)) { PyErr_SetString(PyExc_SystemError, "(Hash) key of dtype dict not a string ???"); return -1; @@ -165,7 +167,7 @@ static int _array_descr_walk_fields(PyObject *names, PyObject* fields, PyObject* } foffset = PyTuple_GET_ITEM(value, 1); - if (!PyInt_Check(foffset)) { + if (!PyLong_Check(foffset)) { PyErr_SetString(PyExc_SystemError, "(Hash) Second item in compound dtype tuple not an int ???"); return -1; @@ -208,7 +210,7 @@ static int _array_descr_walk_subarray(PyArray_ArrayDescr* adescr, PyObject *l) PyList_Append(l, item); } } - else if (PyInt_Check(adescr->shape)) { + else if (PyLong_Check(adescr->shape)) { PyList_Append(l, adescr->shape); } else { diff --git a/numpy/core/src/multiarray/hashdescr.h b/numpy/core/src/multiarray/hashdescr.h index 8d577e7b0fdc..97375b4afab3 100644 --- a/numpy/core/src/multiarray/hashdescr.h +++ b/numpy/core/src/multiarray/hashdescr.h @@ -1,7 +1,7 @@ -#ifndef _NPY_HASHDESCR_H_ -#define _NPY_HASHDESCR_H_ +#ifndef NUMPY_CORE_SRC_MULTIARRAY_HASHDESCR_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_HASHDESCR_H_ NPY_NO_EXPORT npy_hash_t PyArray_DescrHash(PyObject* odescr); -#endif +#endif /* NUMPY_CORE_SRC_MULTIARRAY_HASHDESCR_H_ */ diff --git a/numpy/core/src/multiarray/item_selection.c b/numpy/core/src/multiarray/item_selection.c index 92558570421f..086b674c809e 100644 --- a/numpy/core/src/multiarray/item_selection.c +++ b/numpy/core/src/multiarray/item_selection.c @@ -1,9 +1,10 @@ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE + #define PY_SSIZE_T_CLEAN #include <Python.h> -#include "structmember.h" +#include <structmember.h> -#define NPY_NO_DEPRECATED_API NPY_API_VERSION -#define _MULTIARRAYMODULE #include "numpy/arrayobject.h" #include "numpy/arrayscalars.h" @@ -19,12 +20,170 @@ #include "arrayobject.h" #include "ctors.h" #include "lowlevel_strided_loops.h" +#include "array_assign.h" -#include "item_selection.h" #include "npy_sort.h" #include "npy_partition.h" #include "npy_binsearch.h" #include "alloc.h" +#include "arraytypes.h" +#include "array_coercion.h" +#include "simd/simd.h" + +static NPY_GCC_OPT_3 NPY_INLINE int +npy_fasttake_impl( + char *dest, char *src, const npy_intp *indices, + npy_intp n, npy_intp m, npy_intp max_item, + npy_intp nelem, npy_intp chunk, + NPY_CLIPMODE clipmode, npy_intp itemsize, int needs_refcounting, + PyArray_Descr *dtype, int axis) +{ + NPY_BEGIN_THREADS_DEF; + NPY_BEGIN_THREADS_DESCR(dtype); + switch (clipmode) { + case NPY_RAISE: + for (npy_intp i = 0; i < n; i++) { + for (npy_intp j = 0; j < m; j++) { + npy_intp tmp = indices[j]; + if (check_and_adjust_index(&tmp, max_item, axis, + _save) < 0) { + return -1; + } + char *tmp_src = src + tmp * chunk; + if (needs_refcounting) { + for (npy_intp k = 0; k < nelem; k++) { + PyArray_Item_INCREF(tmp_src, dtype); + PyArray_Item_XDECREF(dest, dtype); + memmove(dest, tmp_src, itemsize); + dest += itemsize; + tmp_src += itemsize; + } + } + else { + memmove(dest, tmp_src, chunk); + dest += chunk; + } + } + src += chunk*max_item; + } + break; + case NPY_WRAP: + for (npy_intp i = 0; i < n; i++) { + for (npy_intp j = 0; j < m; j++) { + npy_intp tmp = indices[j]; + if (tmp < 0) { + while (tmp < 0) { + tmp += max_item; + } + } + else if (tmp >= max_item) { + while (tmp >= max_item) { + tmp -= max_item; + } + } + char *tmp_src = src + tmp * chunk; + if (needs_refcounting) { + for (npy_intp k = 0; k < nelem; k++) { + PyArray_Item_INCREF(tmp_src, dtype); + PyArray_Item_XDECREF(dest, dtype); + memmove(dest, tmp_src, itemsize); + dest += itemsize; + tmp_src += itemsize; + } + } + else { + memmove(dest, tmp_src, chunk); + dest += chunk; + } + } + src += chunk*max_item; + } + break; + case NPY_CLIP: + for (npy_intp i = 0; i < n; i++) { + for (npy_intp j = 0; j < m; j++) { + npy_intp tmp = indices[j]; + if (tmp < 0) { + tmp = 0; + } + else if (tmp >= max_item) { + tmp = max_item - 1; + } + char *tmp_src = src + tmp * chunk; + if (needs_refcounting) { + for (npy_intp k = 0; k < nelem; k++) { + PyArray_Item_INCREF(tmp_src, dtype); + PyArray_Item_XDECREF(dest, dtype); + memmove(dest, tmp_src, itemsize); + dest += itemsize; + tmp_src += itemsize; + } + } + else { + memmove(dest, tmp_src, chunk); + dest += chunk; + } + } + src += chunk*max_item; + } + break; + } + + NPY_END_THREADS; + return 0; +} + + +/* + * Helper function instantiating npy_fasttake_impl in different branches + * to allow the compiler to optimize each to the specific itemsize. + */ +static NPY_GCC_OPT_3 int +npy_fasttake( + char *dest, char *src, const npy_intp *indices, + npy_intp n, npy_intp m, npy_intp max_item, + npy_intp nelem, npy_intp chunk, + NPY_CLIPMODE clipmode, npy_intp itemsize, int needs_refcounting, + PyArray_Descr *dtype, int axis) +{ + if (!needs_refcounting) { + if (chunk == 1) { + return npy_fasttake_impl( + dest, src, indices, n, m, max_item, nelem, chunk, + clipmode, itemsize, needs_refcounting, dtype, axis); + } + if (chunk == 2) { + return npy_fasttake_impl( + dest, src, indices, n, m, max_item, nelem, chunk, + clipmode, itemsize, needs_refcounting, dtype, axis); + } + if (chunk == 4) { + return npy_fasttake_impl( + dest, src, indices, n, m, max_item, nelem, chunk, + clipmode, itemsize, needs_refcounting, dtype, axis); + } + if (chunk == 8) { + return npy_fasttake_impl( + dest, src, indices, n, m, max_item, nelem, chunk, + clipmode, itemsize, needs_refcounting, dtype, axis); + } + if (chunk == 16) { + return npy_fasttake_impl( + dest, src, indices, n, m, max_item, nelem, chunk, + clipmode, itemsize, needs_refcounting, dtype, axis); + } + if (chunk == 32) { + return npy_fasttake_impl( + dest, src, indices, n, m, max_item, nelem, chunk, + clipmode, itemsize, needs_refcounting, dtype, axis); + } + } + + return npy_fasttake_impl( + dest, src, indices, n, m, max_item, nelem, chunk, + clipmode, itemsize, needs_refcounting, dtype, axis); +} + /*NUMPY_API * Take @@ -34,17 +193,15 @@ PyArray_TakeFrom(PyArrayObject *self0, PyObject *indices0, int axis, PyArrayObject *out, NPY_CLIPMODE clipmode) { PyArray_Descr *dtype; - PyArray_FastTakeFunc *func; PyArrayObject *obj = NULL, *self, *indices; - npy_intp nd, i, j, n, m, k, max_item, tmp, chunk, itemsize, nelem; + npy_intp nd, i, n, m, max_item, chunk, itemsize, nelem; npy_intp shape[NPY_MAXDIMS]; - char *src, *dest, *tmp_src; - int err; + npy_bool needs_refcounting; indices = NULL; self = (PyArrayObject *)PyArray_CheckAxis(self0, &axis, - NPY_ARRAY_CARRAY); + NPY_ARRAY_CARRAY_RO); if (self == NULL) { return NULL; } @@ -97,6 +254,10 @@ PyArray_TakeFrom(PyArrayObject *self0, PyObject *indices0, int axis, goto fail; } + if (arrays_overlap(out, self)) { + flags |= NPY_ARRAY_ENSURECOPY; + } + if (clipmode == NPY_RAISE) { /* * we need to make sure and get a copy @@ -117,9 +278,10 @@ PyArray_TakeFrom(PyArrayObject *self0, PyObject *indices0, int axis, nelem = chunk; itemsize = PyArray_ITEMSIZE(obj); chunk = chunk * itemsize; - src = PyArray_DATA(self); - dest = PyArray_DATA(obj); + char *src = PyArray_DATA(self); + char *dest = PyArray_DATA(obj); needs_refcounting = PyDataType_REFCHK(PyArray_DESCR(self)); + npy_intp *indices_data = (npy_intp *)PyArray_DATA(indices); if ((max_item == 0) && (PyArray_SIZE(obj) != 0)) { /* Index error, since that is the usual error for raise mode */ @@ -128,107 +290,10 @@ PyArray_TakeFrom(PyArrayObject *self0, PyObject *indices0, int axis, goto fail; } - func = PyArray_DESCR(self)->f->fasttake; - if (func == NULL) { - NPY_BEGIN_THREADS_DEF; - NPY_BEGIN_THREADS_DESCR(PyArray_DESCR(self)); - switch(clipmode) { - case NPY_RAISE: - for (i = 0; i < n; i++) { - for (j = 0; j < m; j++) { - tmp = ((npy_intp *)(PyArray_DATA(indices)))[j]; - if (check_and_adjust_index(&tmp, max_item, axis, - _save) < 0) { - goto fail; - } - tmp_src = src + tmp * chunk; - if (needs_refcounting) { - for (k=0; k < nelem; k++) { - PyArray_Item_INCREF(tmp_src, PyArray_DESCR(self)); - PyArray_Item_XDECREF(dest, PyArray_DESCR(self)); - memmove(dest, tmp_src, itemsize); - dest += itemsize; - tmp_src += itemsize; - } - } - else { - memmove(dest, tmp_src, chunk); - dest += chunk; - } - } - src += chunk*max_item; - } - break; - case NPY_WRAP: - for (i = 0; i < n; i++) { - for (j = 0; j < m; j++) { - tmp = ((npy_intp *)(PyArray_DATA(indices)))[j]; - if (tmp < 0) { - while (tmp < 0) { - tmp += max_item; - } - } - else if (tmp >= max_item) { - while (tmp >= max_item) { - tmp -= max_item; - } - } - tmp_src = src + tmp * chunk; - if (needs_refcounting) { - for (k=0; k < nelem; k++) { - PyArray_Item_INCREF(tmp_src, PyArray_DESCR(self)); - PyArray_Item_XDECREF(dest, PyArray_DESCR(self)); - memmove(dest, tmp_src, itemsize); - dest += itemsize; - tmp_src += itemsize; - } - } - else { - memmove(dest, tmp_src, chunk); - dest += chunk; - } - } - src += chunk*max_item; - } - break; - case NPY_CLIP: - for (i = 0; i < n; i++) { - for (j = 0; j < m; j++) { - tmp = ((npy_intp *)(PyArray_DATA(indices)))[j]; - if (tmp < 0) { - tmp = 0; - } - else if (tmp >= max_item) { - tmp = max_item - 1; - } - tmp_src = src + tmp * chunk; - if (needs_refcounting) { - for (k=0; k < nelem; k++) { - PyArray_Item_INCREF(tmp_src, PyArray_DESCR(self)); - PyArray_Item_XDECREF(dest, PyArray_DESCR(self)); - memmove(dest, tmp_src, itemsize); - dest += itemsize; - tmp_src += itemsize; - } - } - else { - memmove(dest, tmp_src, chunk); - dest += chunk; - } - } - src += chunk*max_item; - } - break; - } - NPY_END_THREADS; - } - else { - /* no gil release, need it for error reporting */ - err = func(dest, src, (npy_intp *)(PyArray_DATA(indices)), - max_item, n, m, nelem, clipmode); - if (err) { - goto fail; - } + if (npy_fasttake( + dest, src, indices_data, n, m, max_item, nelem, chunk, + clipmode, itemsize, needs_refcounting, dtype, axis) < 0) { + goto fail; } Py_XDECREF(indices); @@ -260,6 +325,7 @@ PyArray_PutTo(PyArrayObject *self, PyObject* values0, PyObject *indices0, npy_intp i, chunk, ni, max_item, nv, tmp; char *src, *dest; int copied = 0; + int overlap = 0; indices = NULL; values = NULL; @@ -273,24 +339,6 @@ PyArray_PutTo(PyArrayObject *self, PyObject* values0, PyObject *indices0, return NULL; } - if (!PyArray_ISCONTIGUOUS(self)) { - PyArrayObject *obj; - int flags = NPY_ARRAY_CARRAY | NPY_ARRAY_WRITEBACKIFCOPY; - - if (clipmode == NPY_RAISE) { - flags |= NPY_ARRAY_ENSURECOPY; - } - Py_INCREF(PyArray_DESCR(self)); - obj = (PyArrayObject *)PyArray_FromArray(self, - PyArray_DESCR(self), flags); - if (obj != self) { - copied = 1; - } - self = obj; - } - max_item = PyArray_SIZE(self); - dest = PyArray_DATA(self); - chunk = PyArray_DESCR(self)->elsize; indices = (PyArrayObject *)PyArray_ContiguousFromAny(indices0, NPY_INTP, 0, 0); if (indices == NULL) { @@ -307,6 +355,25 @@ PyArray_PutTo(PyArrayObject *self, PyObject* values0, PyObject *indices0, if (nv <= 0) { goto finish; } + + overlap = arrays_overlap(self, values) || arrays_overlap(self, indices); + if (overlap || !PyArray_ISCONTIGUOUS(self)) { + PyArrayObject *obj; + int flags = NPY_ARRAY_CARRAY | NPY_ARRAY_WRITEBACKIFCOPY | + NPY_ARRAY_ENSURECOPY; + + Py_INCREF(PyArray_DESCR(self)); + obj = (PyArrayObject *)PyArray_FromArray(self, + PyArray_DESCR(self), flags); + if (obj != self) { + copied = 1; + } + self = obj; + } + max_item = PyArray_SIZE(self); + dest = PyArray_DATA(self); + chunk = PyArray_DESCR(self)->elsize; + if (PyDataType_REFCHK(PyArray_DESCR(self))) { switch(clipmode) { case NPY_RAISE: @@ -424,19 +491,82 @@ PyArray_PutTo(PyArrayObject *self, PyObject* values0, PyObject *indices0, return NULL; } + +static NPY_GCC_OPT_3 NPY_INLINE void +npy_fastputmask_impl( + char *dest, char *src, const npy_bool *mask_data, + npy_intp ni, npy_intp nv, npy_intp chunk) +{ + if (nv == 1) { + for (npy_intp i = 0; i < ni; i++) { + if (mask_data[i]) { + memmove(dest, src, chunk); + } + dest += chunk; + } + } + else { + char *tmp_src = src; + for (npy_intp i = 0, j = 0; i < ni; i++, j++) { + if (NPY_UNLIKELY(j >= nv)) { + j = 0; + tmp_src = src; + } + if (mask_data[i]) { + memmove(dest, tmp_src, chunk); + } + dest += chunk; + tmp_src += chunk; + } + } +} + + +/* + * Helper function instantiating npy_fastput_impl in different branches + * to allow the compiler to optimize each to the specific itemsize. + */ +static NPY_GCC_OPT_3 void +npy_fastputmask( + char *dest, char *src, npy_bool *mask_data, + npy_intp ni, npy_intp nv, npy_intp chunk) +{ + if (chunk == 1) { + return npy_fastputmask_impl(dest, src, mask_data, ni, nv, chunk); + } + if (chunk == 2) { + return npy_fastputmask_impl(dest, src, mask_data, ni, nv, chunk); + } + if (chunk == 4) { + return npy_fastputmask_impl(dest, src, mask_data, ni, nv, chunk); + } + if (chunk == 8) { + return npy_fastputmask_impl(dest, src, mask_data, ni, nv, chunk); + } + if (chunk == 16) { + return npy_fastputmask_impl(dest, src, mask_data, ni, nv, chunk); + } + if (chunk == 32) { + return npy_fastputmask_impl(dest, src, mask_data, ni, nv, chunk); + } + + return npy_fastputmask_impl(dest, src, mask_data, ni, nv, chunk); +} + + /*NUMPY_API * Put values into an array according to a mask. */ NPY_NO_EXPORT PyObject * PyArray_PutMask(PyArrayObject *self, PyObject* values0, PyObject* mask0) { - PyArray_FastPutmaskFunc *func; PyArrayObject *mask, *values; PyArray_Descr *dtype; - npy_intp i, j, chunk, ni, max_item, nv; + npy_intp chunk, ni, nv; char *src, *dest; npy_bool *mask_data; int copied = 0; + int overlap = 0; mask = NULL; values = NULL; @@ -446,29 +576,18 @@ PyArray_PutMask(PyArrayObject *self, PyObject* values0, PyObject* mask0) "be an array"); return NULL; } - if (!PyArray_ISCONTIGUOUS(self)) { - PyArrayObject *obj; - dtype = PyArray_DESCR(self); - Py_INCREF(dtype); - obj = (PyArrayObject *)PyArray_FromArray(self, dtype, - NPY_ARRAY_CARRAY | NPY_ARRAY_WRITEBACKIFCOPY); - if (obj != self) { - copied = 1; - } - self = obj; + if (PyArray_FailUnlessWriteable(self, "putmask: output array") < 0) { + return NULL; } - max_item = PyArray_SIZE(self); - dest = PyArray_DATA(self); - chunk = PyArray_DESCR(self)->elsize; mask = (PyArrayObject *)PyArray_FROM_OTF(mask0, NPY_BOOL, NPY_ARRAY_CARRAY | NPY_ARRAY_FORCECAST); if (mask == NULL) { goto fail; } ni = PyArray_SIZE(mask); - if (ni != max_item) { + if (ni != PyArray_SIZE(self)) { PyErr_SetString(PyExc_ValueError, "putmask: mask and data must be " "the same size"); @@ -490,8 +609,29 @@ PyArray_PutMask(PyArrayObject *self, PyObject* values0, PyObject* mask0) } src = PyArray_DATA(values); + overlap = arrays_overlap(self, values) || arrays_overlap(self, mask); + if (overlap || !PyArray_ISCONTIGUOUS(self)) { + int flags = NPY_ARRAY_CARRAY | NPY_ARRAY_WRITEBACKIFCOPY; + PyArrayObject *obj; + + if (overlap) { + flags |= NPY_ARRAY_ENSURECOPY; + } + + dtype = PyArray_DESCR(self); + Py_INCREF(dtype); + obj = (PyArrayObject *)PyArray_FromArray(self, dtype, flags); + if (obj != self) { + copied = 1; + } + self = obj; + } + + chunk = PyArray_DESCR(self)->elsize; + dest = PyArray_DATA(self); + if (PyDataType_REFCHK(PyArray_DESCR(self))) { - for (i = 0, j = 0; i < ni; i++, j++) { + for (npy_intp i = 0, j = 0; i < ni; i++, j++) { if (j >= nv) { j = 0; } @@ -508,20 +648,7 @@ PyArray_PutMask(PyArrayObject *self, PyObject* values0, PyObject* mask0) else { NPY_BEGIN_THREADS_DEF; NPY_BEGIN_THREADS_DESCR(PyArray_DESCR(self)); - func = PyArray_DESCR(self)->f->fastputmask; - if (func == NULL) { - for (i = 0, j = 0; i < ni; i++, j++) { - if (j >= nv) { - j = 0; - } - if (mask_data[i]) { - memmove(dest + i*chunk, src + j*chunk, chunk); - } - } - } - else { - func(dest, mask_data, ni, src, nv); - } + npy_fastputmask(dest, src, mask_data, ni, nv, chunk); NPY_END_THREADS; } @@ -593,7 +720,8 @@ PyArray_Repeat(PyArrayObject *aop, PyObject *op, int axis) else { for (j = 0; j < n; j++) { if (counts[j] < 0) { - PyErr_SetString(PyExc_ValueError, "count < 0"); + PyErr_SetString(PyExc_ValueError, + "repeats may not contain negative values."); goto fail; } total += counts[j]; @@ -648,6 +776,7 @@ PyArray_Repeat(PyArrayObject *aop, PyObject *op, int axis) return NULL; } + /*NUMPY_API */ NPY_NO_EXPORT PyObject * @@ -711,6 +840,13 @@ PyArray_Choose(PyArrayObject *ip, PyObject *op, PyArrayObject *out, "choose: invalid shape for output array."); goto fail; } + + for (i = 0; i < n; i++) { + if (arrays_overlap(out, mps[i])) { + flags |= NPY_ARRAY_ENSURECOPY; + } + } + if (clipmode == NPY_RAISE) { /* * we need to make sure and get a copy @@ -772,7 +908,7 @@ PyArray_Choose(PyArrayObject *ip, PyObject *op, PyArrayObject *out, Py_XDECREF(mps[i]); } Py_DECREF(ap); - npy_free_cache(mps, n * sizeof(mps[0])); + PyDataMem_FREE(mps); if (out != NULL && out != obj) { Py_INCREF(out); PyArray_ResolveWritebackIfCopy(obj); @@ -787,7 +923,7 @@ PyArray_Choose(PyArrayObject *ip, PyObject *op, PyArrayObject *out, Py_XDECREF(mps[i]); } Py_XDECREF(ap); - npy_free_cache(mps, n * sizeof(mps[0])); + PyDataMem_FREE(mps); PyArray_DiscardWritebackIfCopy(obj); Py_XDECREF(obj); return NULL; @@ -803,13 +939,13 @@ PyArray_Choose(PyArrayObject *ip, PyObject *op, PyArrayObject *out, */ static int _new_sortlike(PyArrayObject *op, int axis, PyArray_SortFunc *sort, - PyArray_PartitionFunc *part, npy_intp *kth, npy_intp nkth) + PyArray_PartitionFunc *part, npy_intp const *kth, npy_intp nkth) { npy_intp N = PyArray_DIM(op, axis); npy_intp elsize = (npy_intp)PyArray_ITEMSIZE(op); npy_intp astride = PyArray_STRIDE(op, axis); int swap = PyArray_ISBYTESWAPPED(op); - int needcopy = !PyArray_ISALIGNED(op) || swap || astride != elsize; + int needcopy = !IsAligned(op) || swap || astride != elsize; int hasrefs = PyDataType_REFCHK(PyArray_DESCR(op)); PyArray_CopySwapNFunc *copyswapn = PyArray_DESCR(op)->f->copyswapn; @@ -827,14 +963,19 @@ _new_sortlike(PyArrayObject *op, int axis, PyArray_SortFunc *sort, return 0; } + PyObject *mem_handler = PyDataMem_GetHandler(); + if (mem_handler == NULL) { + return -1; + } it = (PyArrayIterObject *)PyArray_IterAllButAxis((PyObject *)op, &axis); if (it == NULL) { + Py_DECREF(mem_handler); return -1; } size = it->size; if (needcopy) { - buffer = npy_alloc_cache(N * elsize); + buffer = PyDataMem_UserNEW(N * elsize, mem_handler); if (buffer == NULL) { ret = -1; goto fail; @@ -918,12 +1059,14 @@ _new_sortlike(PyArrayObject *op, int axis, PyArray_SortFunc *sort, fail: NPY_END_THREADS_DESCR(PyArray_DESCR(op)); - npy_free_cache(buffer, N * elsize); + /* cleanup internal buffer */ + PyDataMem_UserFREE(buffer, N * elsize, mem_handler); if (ret < 0 && !PyErr_Occurred()) { /* Out of memory during sorting or buffer creation */ PyErr_NoMemory(); } Py_DECREF(it); + Py_DECREF(mem_handler); return ret; } @@ -931,13 +1074,13 @@ _new_sortlike(PyArrayObject *op, int axis, PyArray_SortFunc *sort, static PyObject* _new_argsortlike(PyArrayObject *op, int axis, PyArray_ArgSortFunc *argsort, PyArray_ArgPartitionFunc *argpart, - npy_intp *kth, npy_intp nkth) + npy_intp const *kth, npy_intp nkth) { npy_intp N = PyArray_DIM(op, axis); npy_intp elsize = (npy_intp)PyArray_ITEMSIZE(op); npy_intp astride = PyArray_STRIDE(op, axis); int swap = PyArray_ISBYTESWAPPED(op); - int needcopy = !PyArray_ISALIGNED(op) || swap || astride != elsize; + int needcopy = !IsAligned(op) || swap || astride != elsize; int hasrefs = PyDataType_REFCHK(PyArray_DESCR(op)); int needidxbuffer; @@ -955,11 +1098,16 @@ _new_argsortlike(PyArrayObject *op, int axis, PyArray_ArgSortFunc *argsort, NPY_BEGIN_THREADS_DEF; + PyObject *mem_handler = PyDataMem_GetHandler(); + if (mem_handler == NULL) { + return NULL; + } rop = (PyArrayObject *)PyArray_NewFromDescr( Py_TYPE(op), PyArray_DescrFromType(NPY_INTP), PyArray_NDIM(op), PyArray_DIMS(op), NULL, NULL, 0, (PyObject *)op); if (rop == NULL) { + Py_DECREF(mem_handler); return NULL; } rstride = PyArray_STRIDE(rop, axis); @@ -967,6 +1115,7 @@ _new_argsortlike(PyArrayObject *op, int axis, PyArray_ArgSortFunc *argsort, /* Check if there is any argsorting to do */ if (N <= 1 || PyArray_SIZE(op) == 0) { + Py_DECREF(mem_handler); memset(PyArray_DATA(rop), 0, PyArray_NBYTES(rop)); return (PyObject *)rop; } @@ -980,7 +1129,7 @@ _new_argsortlike(PyArrayObject *op, int axis, PyArray_ArgSortFunc *argsort, size = it->size; if (needcopy) { - valbuffer = npy_alloc_cache(N * elsize); + valbuffer = PyDataMem_UserNEW(N * elsize, mem_handler); if (valbuffer == NULL) { ret = -1; goto fail; @@ -988,7 +1137,8 @@ _new_argsortlike(PyArrayObject *op, int axis, PyArray_ArgSortFunc *argsort, } if (needidxbuffer) { - idxbuffer = (npy_intp *)npy_alloc_cache(N * sizeof(npy_intp)); + idxbuffer = (npy_intp *)PyDataMem_UserNEW(N * sizeof(npy_intp), + mem_handler); if (idxbuffer == NULL) { ret = -1; goto fail; @@ -1037,12 +1187,10 @@ _new_argsortlike(PyArrayObject *op, int axis, PyArray_ArgSortFunc *argsort, if (argpart == NULL) { ret = argsort(valptr, idxptr, N, op); -#if defined(NPY_PY3K) /* Object comparisons may raise an exception in Python 3 */ if (hasrefs && PyErr_Occurred()) { ret = -1; } -#endif if (ret < 0) { goto fail; } @@ -1053,12 +1201,10 @@ _new_argsortlike(PyArrayObject *op, int axis, PyArray_ArgSortFunc *argsort, for (i = 0; i < nkth; ++i) { ret = argpart(valptr, idxptr, N, kth[i], pivots, &npiv, op); -#if defined(NPY_PY3K) /* Object comparisons may raise an exception in Python 3 */ if (hasrefs && PyErr_Occurred()) { ret = -1; } -#endif if (ret < 0) { goto fail; } @@ -1081,8 +1227,9 @@ _new_argsortlike(PyArrayObject *op, int axis, PyArray_ArgSortFunc *argsort, fail: NPY_END_THREADS_DESCR(PyArray_DESCR(op)); - npy_free_cache(valbuffer, N * elsize); - npy_free_cache(idxbuffer, N * sizeof(npy_intp)); + /* cleanup internal buffers */ + PyDataMem_UserFREE(valbuffer, N * elsize, mem_handler); + PyDataMem_UserFREE(idxbuffer, N * sizeof(npy_intp), mem_handler); if (ret < 0) { if (!PyErr_Occurred()) { /* Out of memory during sorting or buffer creation */ @@ -1093,6 +1240,7 @@ _new_argsortlike(PyArrayObject *op, int axis, PyArray_ArgSortFunc *argsort, } Py_XDECREF(it); Py_XDECREF(rit); + Py_DECREF(mem_handler); return (PyObject *)rop; } @@ -1104,7 +1252,7 @@ _new_argsortlike(PyArrayObject *op, int axis, PyArray_ArgSortFunc *argsort, NPY_NO_EXPORT int PyArray_Sort(PyArrayObject *op, int axis, NPY_SORTKIND which) { - PyArray_SortFunc *sort; + PyArray_SortFunc *sort = NULL; int n = PyArray_NDIM(op); if (check_and_adjust_axis(&axis, n) < 0) { @@ -1121,6 +1269,7 @@ PyArray_Sort(PyArrayObject *op, int axis, NPY_SORTKIND which) } sort = PyArray_DESCR(op)->f->sort[which]; + if (sort == NULL) { if (PyArray_DESCR(op)->f->compare) { switch (which) { @@ -1131,8 +1280,8 @@ PyArray_Sort(PyArrayObject *op, int axis, NPY_SORTKIND which) case NPY_HEAPSORT: sort = npy_heapsort; break; - case NPY_MERGESORT: - sort = npy_mergesort; + case NPY_STABLESORT: + sort = npy_timsort; break; } } @@ -1160,7 +1309,15 @@ partition_prep_kth_array(PyArrayObject * ktharray, npy_intp * kth; npy_intp nkth, i; - if (!PyArray_CanCastSafely(PyArray_TYPE(ktharray), NPY_INTP)) { + if (PyArray_ISBOOL(ktharray)) { + /* 2021-09-29, NumPy 1.22 */ + if (DEPRECATE( + "Passing booleans as partition index is deprecated" + " (warning added in NumPy 1.22)") < 0) { + return NULL; + } + } + else if (!PyArray_ISINTEGER(ktharray)) { PyErr_Format(PyExc_TypeError, "Partition index must be integer"); return NULL; } @@ -1262,16 +1419,11 @@ NPY_NO_EXPORT PyObject * PyArray_ArgSort(PyArrayObject *op, int axis, NPY_SORTKIND which) { PyArrayObject *op2; - PyArray_ArgSortFunc *argsort; + PyArray_ArgSortFunc *argsort = NULL; PyObject *ret; - if (which < 0 || which >= NPY_NSORTS) { - PyErr_SetString(PyExc_ValueError, - "not a valid sort kind"); - return NULL; - } - argsort = PyArray_DESCR(op)->f->argsort[which]; + if (argsort == NULL) { if (PyArray_DESCR(op)->f->compare) { switch (which) { @@ -1282,8 +1434,8 @@ PyArray_ArgSort(PyArrayObject *op, int axis, NPY_SORTKIND which) case NPY_HEAPSORT: argsort = npy_aheapsort; break; - case NPY_MERGESORT: - argsort = npy_amergesort; + case NPY_STABLESORT: + argsort = npy_atimsort; break; } } @@ -1318,7 +1470,11 @@ PyArray_ArgPartition(PyArrayObject *op, PyArrayObject *ktharray, int axis, PyArray_ArgSortFunc *argsort; PyObject *ret; - if (which < 0 || which >= NPY_NSELECTS) { + /* + * As a C-exported function, enum NPY_SELECTKIND loses its enum property + * Check the values to make sure they are in range + */ + if ((int)which < 0 || (int)which >= NPY_NSELECTS) { PyErr_SetString(PyExc_ValueError, "not a valid partition kind"); return NULL; @@ -1424,7 +1580,7 @@ PyArray_LexSort(PyObject *sort_keys, int axis) goto fail; } } - if (!PyArray_DESCR(mps[i])->f->argsort[NPY_MERGESORT] + if (!PyArray_DESCR(mps[i])->f->argsort[NPY_STABLESORT] && !PyArray_DESCR(mps[i])->f->compare) { PyErr_Format(PyExc_TypeError, "item %zd type does not have compare function", i); @@ -1438,8 +1594,18 @@ PyArray_LexSort(PyObject *sort_keys, int axis) /* Now we can check the axis */ nd = PyArray_NDIM(mps[0]); - if ((nd == 0) || (PyArray_SIZE(mps[0]) == 1)) { - /* single element case */ + /* + * Special case letting axis={-1,0} slip through for scalars, + * for backwards compatibility reasons. + */ + if (nd == 0 && (axis == 0 || axis == -1)) { + /* TODO: can we deprecate this? */ + } + else if (check_and_adjust_axis(&axis, nd) < 0) { + goto fail; + } + if ((nd == 0) || (PyArray_SIZE(mps[0]) <= 1)) { + /* empty/single element case */ ret = (PyArrayObject *)PyArray_NewFromDescr( &PyArray_Type, PyArray_DescrFromType(NPY_INTP), PyArray_NDIM(mps[0]), PyArray_DIMS(mps[0]), NULL, NULL, @@ -1448,12 +1614,11 @@ PyArray_LexSort(PyObject *sort_keys, int axis) if (ret == NULL) { goto fail; } - *((npy_intp *)(PyArray_DATA(ret))) = 0; + if (PyArray_SIZE(mps[0]) > 0) { + *((npy_intp *)(PyArray_DATA(ret))) = 0; + } goto finish; } - if (check_and_adjust_axis(&axis, nd) < 0) { - goto fail; - } for (i = 0; i < n; i++) { its[i] = (PyArrayIterObject *)PyArray_IterAllButAxis( @@ -1498,16 +1663,28 @@ PyArray_LexSort(PyObject *sort_keys, int axis) char *valbuffer, *indbuffer; int *swaps; - valbuffer = PyDataMem_NEW(N * maxelsize); + assert(N > 0); /* Guaranteed and assumed by indbuffer */ + npy_intp valbufsize = N * maxelsize; + if (NPY_UNLIKELY(valbufsize) == 0) { + valbufsize = 1; /* Ensure allocation is not empty */ + } + + valbuffer = PyDataMem_NEW(valbufsize); if (valbuffer == NULL) { goto fail; } indbuffer = PyDataMem_NEW(N * sizeof(npy_intp)); if (indbuffer == NULL) { + PyDataMem_FREE(valbuffer); + goto fail; + } + swaps = malloc(NPY_LIKELY(n > 0) ? n * sizeof(int) : 1); + if (swaps == NULL) { + PyDataMem_FREE(valbuffer); PyDataMem_FREE(indbuffer); goto fail; } - swaps = malloc(n*sizeof(int)); + for (j = 0; j < n; j++) { swaps[j] = PyArray_ISBYTESWAPPED(mps[j]); } @@ -1520,9 +1697,9 @@ PyArray_LexSort(PyObject *sort_keys, int axis) int rcode; elsize = PyArray_DESCR(mps[j])->elsize; astride = PyArray_STRIDES(mps[j])[axis]; - argsort = PyArray_DESCR(mps[j])->f->argsort[NPY_MERGESORT]; + argsort = PyArray_DESCR(mps[j])->f->argsort[NPY_STABLESORT]; if(argsort == NULL) { - argsort = npy_amergesort; + argsort = npy_atimsort; } _unaligned_strided_byte_copy(valbuffer, (npy_intp) elsize, its[j]->dataptr, astride, N, elsize); @@ -1530,14 +1707,10 @@ PyArray_LexSort(PyObject *sort_keys, int axis) _strided_byte_swap(valbuffer, (npy_intp) elsize, N, elsize); } rcode = argsort(valbuffer, (npy_intp *)indbuffer, N, mps[j]); -#if defined(NPY_PY3K) if (rcode < 0 || (PyDataType_REFCHK(PyArray_DESCR(mps[j])) && PyErr_Occurred())) { -#else - if (rcode < 0) { -#endif - npy_free_cache(valbuffer, N * maxelsize); - npy_free_cache(indbuffer, N * sizeof(npy_intp)); + PyDataMem_FREE(valbuffer); + PyDataMem_FREE(indbuffer); free(swaps); goto fail; } @@ -1559,18 +1732,14 @@ PyArray_LexSort(PyObject *sort_keys, int axis) } for (j = 0; j < n; j++) { int rcode; - argsort = PyArray_DESCR(mps[j])->f->argsort[NPY_MERGESORT]; + argsort = PyArray_DESCR(mps[j])->f->argsort[NPY_STABLESORT]; if(argsort == NULL) { - argsort = npy_amergesort; + argsort = npy_atimsort; } rcode = argsort(its[j]->dataptr, (npy_intp *)rit->dataptr, N, mps[j]); -#if defined(NPY_PY3K) if (rcode < 0 || (PyDataType_REFCHK(PyArray_DESCR(mps[j])) && PyErr_Occurred())) { -#else - if (rcode < 0) { -#endif goto fail; } PyArray_ITER_NEXT(its[j]); @@ -1985,22 +2154,199 @@ count_nonzero_bytes_384(const npy_uint64 * w) return r; } +#if NPY_SIMD +/* Count the zero bytes between `*d` and `end`, updating `*d` to point to where to keep counting from. */ +NPY_FINLINE NPY_GCC_OPT_3 npyv_u8 +count_zero_bytes_u8(const npy_uint8 **d, const npy_uint8 *end, npy_uint8 max_count) +{ + const npyv_u8 vone = npyv_setall_u8(1); + const npyv_u8 vzero = npyv_zero_u8(); + + npy_intp lane_max = 0; + npyv_u8 vsum8 = npyv_zero_u8(); + while (*d < end && lane_max <= max_count - 1) { + // we count zeros because `cmpeq` cheaper than `cmpneq` for most archs + npyv_u8 vt = npyv_cvt_u8_b8(npyv_cmpeq_u8(npyv_load_u8(*d), vzero)); + vt = npyv_and_u8(vt, vone); + vsum8 = npyv_add_u8(vsum8, vt); + *d += npyv_nlanes_u8; + lane_max += 1; + } + return vsum8; +} + +NPY_FINLINE NPY_GCC_OPT_3 npyv_u16x2 +count_zero_bytes_u16(const npy_uint8 **d, const npy_uint8 *end, npy_uint16 max_count) +{ + npyv_u16x2 vsum16; + vsum16.val[0] = vsum16.val[1] = npyv_zero_u16(); + npy_intp lane_max = 0; + while (*d < end && lane_max <= max_count - NPY_MAX_UINT8) { + npyv_u8 vsum8 = count_zero_bytes_u8(d, end, NPY_MAX_UINT8); + npyv_u16x2 part = npyv_expand_u16_u8(vsum8); + vsum16.val[0] = npyv_add_u16(vsum16.val[0], part.val[0]); + vsum16.val[1] = npyv_add_u16(vsum16.val[1], part.val[1]); + lane_max += NPY_MAX_UINT8; + } + return vsum16; +} +#endif // NPY_SIMD +/* + * Counts the number of non-zero values in a raw array. + * The one loop process is shown below(take SSE2 with 128bits vector for example): + * |------------16 lanes---------| + *[vsum8] 255 255 255 ... 255 255 255 255 count_zero_bytes_u8: counting 255*16 elements + * !! + * |------------8 lanes---------| + *[vsum16] 65535 65535 65535 ... 65535 count_zero_bytes_u16: counting (2*16-1)*16 elements + * 65535 65535 65535 ... 65535 + * !! + * |------------4 lanes---------| + *[sum_32_0] 65535 65535 65535 65535 count_nonzero_bytes + * 65535 65535 65535 65535 + *[sum_32_1] 65535 65535 65535 65535 + * 65535 65535 65535 65535 + * !! + * (2*16-1)*16 +*/ +static NPY_INLINE NPY_GCC_OPT_3 npy_intp +count_nonzero_u8(const char *data, npy_intp bstride, npy_uintp len) +{ + npy_intp count = 0; + if (bstride == 1) { + #if NPY_SIMD + npy_uintp len_m = len & -npyv_nlanes_u8; + npy_uintp zcount = 0; + for (const char *end = data + len_m; data < end;) { + npyv_u16x2 vsum16 = count_zero_bytes_u16((const npy_uint8**)&data, (const npy_uint8*)end, NPY_MAX_UINT16); + npyv_u32x2 sum_32_0 = npyv_expand_u32_u16(vsum16.val[0]); + npyv_u32x2 sum_32_1 = npyv_expand_u32_u16(vsum16.val[1]); + zcount += npyv_sum_u32(npyv_add_u32( + npyv_add_u32(sum_32_0.val[0], sum_32_0.val[1]), + npyv_add_u32(sum_32_1.val[0], sum_32_1.val[1]) + )); + } + len -= len_m; + count = len_m - zcount; + #else + if (!NPY_ALIGNMENT_REQUIRED || npy_is_aligned(data, sizeof(npy_uint64))) { + int step = 6 * sizeof(npy_uint64); + int left_bytes = len % step; + for (const char *end = data + len; data < end - left_bytes; data += step) { + count += count_nonzero_bytes_384((const npy_uint64 *)data); + } + len = left_bytes; + } + #endif // NPY_SIMD + } + for (; len > 0; --len, data += bstride) { + count += (*data != 0); + } + return count; +} + +static NPY_INLINE NPY_GCC_OPT_3 npy_intp +count_nonzero_u16(const char *data, npy_intp bstride, npy_uintp len) +{ + npy_intp count = 0; +#if NPY_SIMD + if (bstride == sizeof(npy_uint16)) { + npy_uintp zcount = 0, len_m = len & -npyv_nlanes_u16; + const npyv_u16 vone = npyv_setall_u16(1); + const npyv_u16 vzero = npyv_zero_u16(); + + for (npy_uintp lenx = len_m; lenx > 0;) { + npyv_u16 vsum16 = npyv_zero_u16(); + npy_uintp max16 = PyArray_MIN(lenx, NPY_MAX_UINT16*npyv_nlanes_u16); + + for (const char *end = data + max16*bstride; data < end; data += NPY_SIMD_WIDTH) { + npyv_u16 mask = npyv_cvt_u16_b16(npyv_cmpeq_u16(npyv_load_u16((npy_uint16*)data), vzero)); + mask = npyv_and_u16(mask, vone); + vsum16 = npyv_add_u16(vsum16, mask); + } + lenx -= max16; + zcount += npyv_sumup_u16(vsum16); + } + len -= len_m; + count = len_m - zcount; + } +#endif + for (; len > 0; --len, data += bstride) { + count += (*(npy_uint16*)data != 0); + } + return count; +} + +static NPY_INLINE NPY_GCC_OPT_3 npy_intp +count_nonzero_u32(const char *data, npy_intp bstride, npy_uintp len) +{ + npy_intp count = 0; +#if NPY_SIMD + if (bstride == sizeof(npy_uint32)) { + const npy_uintp max_iter = NPY_MAX_UINT32*npyv_nlanes_u32; + const npy_uintp len_m = (len > max_iter ? max_iter : len) & -npyv_nlanes_u32; + const npyv_u32 vone = npyv_setall_u32(1); + const npyv_u32 vzero = npyv_zero_u32(); + + npyv_u32 vsum32 = npyv_zero_u32(); + for (const char *end = data + len_m*bstride; data < end; data += NPY_SIMD_WIDTH) { + npyv_u32 mask = npyv_cvt_u32_b32(npyv_cmpeq_u32(npyv_load_u32((npy_uint32*)data), vzero)); + mask = npyv_and_u32(mask, vone); + vsum32 = npyv_add_u32(vsum32, mask); + } + const npyv_u32 maskevn = npyv_reinterpret_u32_u64(npyv_setall_u64(0xffffffffULL)); + npyv_u64 odd = npyv_shri_u64(npyv_reinterpret_u64_u32(vsum32), 32); + npyv_u64 even = npyv_reinterpret_u64_u32(npyv_and_u32(vsum32, maskevn)); + count = len_m - npyv_sum_u64(npyv_add_u64(odd, even)); + len -= len_m; + } +#endif + for (; len > 0; --len, data += bstride) { + count += (*(npy_uint32*)data != 0); + } + return count; +} + +static NPY_INLINE NPY_GCC_OPT_3 npy_intp +count_nonzero_u64(const char *data, npy_intp bstride, npy_uintp len) +{ + npy_intp count = 0; +#if NPY_SIMD + if (bstride == sizeof(npy_uint64)) { + const npy_uintp len_m = len & -npyv_nlanes_u64; + const npyv_u64 vone = npyv_setall_u64(1); + const npyv_u64 vzero = npyv_zero_u64(); + + npyv_u64 vsum64 = npyv_zero_u64(); + for (const char *end = data + len_m*bstride; data < end; data += NPY_SIMD_WIDTH) { + npyv_u64 mask = npyv_cvt_u64_b64(npyv_cmpeq_u64(npyv_load_u64((npy_uint64*)data), vzero)); + mask = npyv_and_u64(mask, vone); + vsum64 = npyv_add_u64(vsum64, mask); + } + len -= len_m; + count = len_m - npyv_sum_u64(vsum64); + } +#endif + for (; len > 0; --len, data += bstride) { + count += (*(npy_uint64*)data != 0); + } + return count; +} /* * Counts the number of True values in a raw boolean array. This * is a low-overhead function which does no heap allocations. * * Returns -1 on error. */ -NPY_NO_EXPORT npy_intp -count_boolean_trues(int ndim, char *data, npy_intp *ashape, npy_intp *astrides) +static NPY_GCC_OPT_3 npy_intp +count_nonzero_int(int ndim, char *data, const npy_intp *ashape, const npy_intp *astrides, int elsize) { + assert(elsize <= 8); int idim; npy_intp shape[NPY_MAXDIMS], strides[NPY_MAXDIMS]; - npy_intp i, coord[NPY_MAXDIMS]; - npy_intp count = 0; - NPY_BEGIN_THREADS_DEF; + npy_intp coord[NPY_MAXDIMS]; - /* Use raw iteration with no heap memory allocation */ + // Use raw iteration with no heap memory allocation if (PyArray_PrepareOneRawArrayIter( ndim, ashape, data, astrides, @@ -2009,46 +2355,44 @@ count_boolean_trues(int ndim, char *data, npy_intp *ashape, npy_intp *astrides) return -1; } - /* Handle zero-sized array */ + // Handle zero-sized array if (shape[0] == 0) { return 0; } + NPY_BEGIN_THREADS_DEF; NPY_BEGIN_THREADS_THRESHOLDED(shape[0]); - /* Special case for contiguous inner loop */ - if (strides[0] == 1) { - NPY_RAW_ITER_START(idim, ndim, coord, shape) { - /* Process the innermost dimension */ - const char *d = data; - const char *e = data + shape[0]; - if (NPY_CPU_HAVE_UNALIGNED_ACCESS || - npy_is_aligned(d, sizeof(npy_uint64))) { - npy_uintp stride = 6 * sizeof(npy_uint64); - for (; d < e - (shape[0] % stride); d += stride) { - count += count_nonzero_bytes_384((const npy_uint64 *)d); - } - } - for (; d < e; ++d) { - count += (*d != 0); - } - } NPY_RAW_ITER_ONE_NEXT(idim, ndim, coord, shape, data, strides); - } - /* General inner loop */ - else { - NPY_RAW_ITER_START(idim, ndim, coord, shape) { - char *d = data; - /* Process the innermost dimension */ - for (i = 0; i < shape[0]; ++i, d += strides[0]) { - count += (*d != 0); - } - } NPY_RAW_ITER_ONE_NEXT(idim, ndim, coord, shape, data, strides); + #define NONZERO_CASE(LEN, SFX) \ + case LEN: \ + NPY_RAW_ITER_START(idim, ndim, coord, shape) { \ + count += count_nonzero_##SFX(data, strides[0], shape[0]); \ + } NPY_RAW_ITER_ONE_NEXT(idim, ndim, coord, shape, data, strides); \ + break + + npy_intp count = 0; + switch(elsize) { + NONZERO_CASE(1, u8); + NONZERO_CASE(2, u16); + NONZERO_CASE(4, u32); + NONZERO_CASE(8, u64); } + #undef NONZERO_CASE NPY_END_THREADS; - return count; } +/* + * Counts the number of True values in a raw boolean array. This + * is a low-overhead function which does no heap allocations. + * + * Returns -1 on error. + */ +NPY_NO_EXPORT NPY_GCC_OPT_3 npy_intp +count_boolean_trues(int ndim, char *data, npy_intp const *ashape, npy_intp const *astrides) +{ + return count_nonzero_int(ndim, data, ashape, astrides, 1); +} /*NUMPY_API * Counts the number of non-zero elements in the array. @@ -2071,14 +2415,17 @@ PyArray_CountNonzero(PyArrayObject *self) npy_intp *strideptr, *innersizeptr; NPY_BEGIN_THREADS_DEF; - /* Special low-overhead version specific to the boolean type */ dtype = PyArray_DESCR(self); - if (dtype->type_num == NPY_BOOL) { - return count_boolean_trues(PyArray_NDIM(self), PyArray_DATA(self), - PyArray_DIMS(self), PyArray_STRIDES(self)); + /* Special low-overhead version specific to the boolean/int types */ + if (PyArray_ISALIGNED(self) && ( + PyDataType_ISBOOL(dtype) || PyDataType_ISINTEGER(dtype))) { + return count_nonzero_int( + PyArray_NDIM(self), PyArray_BYTES(self), PyArray_DIMS(self), + PyArray_STRIDES(self), dtype->elsize + ); } - nonzero = PyArray_DESCR(self)->f->nonzero; + nonzero = PyArray_DESCR(self)->f->nonzero; /* If it's a trivial one-dimensional loop, don't use an iterator */ if (PyArray_TRIVIALLY_ITERABLE(self)) { needs_api = PyDataType_FLAGCHK(dtype, NPY_NEEDS_PYAPI); @@ -2182,14 +2529,59 @@ PyArray_Nonzero(PyArrayObject *self) PyArrayObject *ret = NULL; PyObject *ret_tuple; npy_intp ret_dims[2]; - PyArray_NonzeroFunc *nonzero = PyArray_DESCR(self)->f->nonzero; + + PyArray_NonzeroFunc *nonzero; + PyArray_Descr *dtype; + npy_intp nonzero_count; + npy_intp added_count = 0; + int needs_api; + int is_bool; NpyIter *iter; NpyIter_IterNextFunc *iternext; NpyIter_GetMultiIndexFunc *get_multi_index; char **dataptr; - int is_empty = 0; + + dtype = PyArray_DESCR(self); + nonzero = dtype->f->nonzero; + needs_api = PyDataType_FLAGCHK(dtype, NPY_NEEDS_PYAPI); + + /* Special case - nonzero(zero_d) is nonzero(atleast_1d(zero_d)) */ + if (ndim == 0) { + char const* msg; + if (PyArray_ISBOOL(self)) { + msg = + "Calling nonzero on 0d arrays is deprecated, as it behaves " + "surprisingly. Use `atleast_1d(cond).nonzero()` if the old " + "behavior was intended. If the context of this warning is of " + "the form `arr[nonzero(cond)]`, just use `arr[cond]`."; + } + else { + msg = + "Calling nonzero on 0d arrays is deprecated, as it behaves " + "surprisingly. Use `atleast_1d(arr).nonzero()` if the old " + "behavior was intended."; + } + if (DEPRECATE(msg) < 0) { + return NULL; + } + + static npy_intp const zero_dim_shape[1] = {1}; + static npy_intp const zero_dim_strides[1] = {0}; + + Py_INCREF(PyArray_DESCR(self)); /* array creation steals reference */ + PyArrayObject *self_1d = (PyArrayObject *)PyArray_NewFromDescrAndBase( + Py_TYPE(self), PyArray_DESCR(self), + 1, zero_dim_shape, zero_dim_strides, PyArray_BYTES(self), + PyArray_FLAGS(self), (PyObject *)self, (PyObject *)self); + if (self_1d == NULL) { + return NULL; + } + ret_tuple = PyArray_Nonzero(self_1d); + Py_DECREF(self_1d); + return ret_tuple; + } /* * First count the number of non-zeros in 'self'. @@ -2199,9 +2591,11 @@ PyArray_Nonzero(PyArrayObject *self) return NULL; } + is_bool = PyArray_ISBOOL(self); + /* Allocate the result as a 2D array */ ret_dims[0] = nonzero_count; - ret_dims[1] = (ndim == 0) ? 1 : ndim; + ret_dims[1] = ndim; ret = (PyArrayObject *)PyArray_NewFromDescr( &PyArray_Type, PyArray_DescrFromType(NPY_INTP), 2, ret_dims, NULL, NULL, @@ -2211,11 +2605,11 @@ PyArray_Nonzero(PyArrayObject *self) } /* If it's a one-dimensional result, don't use an iterator */ - if (ndim <= 1) { + if (ndim == 1) { npy_intp * multi_index = (npy_intp *)PyArray_DATA(ret); char * data = PyArray_BYTES(self); - npy_intp stride = (ndim == 0) ? 0 : PyArray_STRIDE(self, 0); - npy_intp count = (ndim == 0) ? 1 : PyArray_DIM(self, 0); + npy_intp stride = PyArray_STRIDE(self, 0); + npy_intp count = PyArray_DIM(self, 0); NPY_BEGIN_THREADS_DEF; /* nothing to do */ @@ -2223,10 +2617,12 @@ PyArray_Nonzero(PyArrayObject *self) goto finish; } - NPY_BEGIN_THREADS_THRESHOLDED(count); + if (!needs_api) { + NPY_BEGIN_THREADS_THRESHOLDED(count); + } /* avoid function call for bool */ - if (PyArray_ISBOOL(self)) { + if (is_bool) { /* * use fast memchr variant for sparse data, see gh-4370 * the fast bool count is followed by this sparse path is faster @@ -2259,8 +2655,14 @@ PyArray_Nonzero(PyArrayObject *self) npy_intp j; for (j = 0; j < count; ++j) { if (nonzero(data, self)) { + if (++added_count > nonzero_count) { + break; + } *multi_index++ = j; } + if (needs_api && PyErr_Occurred()) { + break; + } data += stride; } } @@ -2302,6 +2704,8 @@ PyArray_Nonzero(PyArrayObject *self) return NULL; } + needs_api = NpyIter_IterationNeedsAPI(iter); + NPY_BEGIN_THREADS_NDITER(iter); dataptr = NpyIter_GetDataPtrArray(iter); @@ -2309,7 +2713,7 @@ PyArray_Nonzero(PyArrayObject *self) multi_index = (npy_intp *)PyArray_DATA(ret); /* Get the multi-index for each non-zero element */ - if (PyArray_ISBOOL(self)) { + if (is_bool) { /* avoid function call for bool */ do { if (**dataptr != 0) { @@ -2321,9 +2725,15 @@ PyArray_Nonzero(PyArrayObject *self) else { do { if (nonzero(*dataptr, self)) { + if (++added_count > nonzero_count) { + break; + } get_multi_index(iter, multi_index); multi_index += ndim; } + if (needs_api && PyErr_Occurred()) { + break; + } } while(iternext(iter)); } @@ -2333,29 +2743,31 @@ PyArray_Nonzero(PyArrayObject *self) NpyIter_Deallocate(iter); finish: - /* Treat zero-dimensional as shape (1,) */ - if (ndim == 0) { - ndim = 1; + if (PyErr_Occurred()) { + Py_DECREF(ret); + return NULL; } - ret_tuple = PyTuple_New(ndim); - if (ret_tuple == NULL) { + /* if executed `nonzero()` check for miscount due to side-effect */ + if (!is_bool && added_count != nonzero_count) { + PyErr_SetString(PyExc_RuntimeError, + "number of non-zero array elements " + "changed during function execution."); Py_DECREF(ret); return NULL; } - for (i = 0; i < PyArray_NDIM(ret); ++i) { - if (PyArray_DIMS(ret)[i] == 0) { - is_empty = 1; - break; - } + ret_tuple = PyTuple_New(ndim); + if (ret_tuple == NULL) { + Py_DECREF(ret); + return NULL; } /* Create views into ret, one for each dimension */ for (i = 0; i < ndim; ++i) { npy_intp stride = ndim * NPY_SIZEOF_INTP; /* the result is an empty array, the view must point to valid memory */ - npy_intp data_offset = is_empty ? 0 : i * NPY_SIZEOF_INTP; + npy_intp data_offset = nonzero_count == 0 ? 0 : i * NPY_SIZEOF_INTP; PyArrayObject *view = (PyArrayObject *)PyArray_NewFromDescrAndBase( Py_TYPE(ret), PyArray_DescrFromType(NPY_INTP), @@ -2378,7 +2790,7 @@ PyArray_Nonzero(PyArrayObject *self) * array of values, which must be of length PyArray_NDIM(self). */ NPY_NO_EXPORT PyObject * -PyArray_MultiIndexGetItem(PyArrayObject *self, npy_intp *multi_index) +PyArray_MultiIndexGetItem(PyArrayObject *self, const npy_intp *multi_index) { int idim, ndim = PyArray_NDIM(self); char *data = PyArray_DATA(self); @@ -2391,7 +2803,7 @@ PyArray_MultiIndexGetItem(PyArrayObject *self, npy_intp *multi_index) npy_intp ind = multi_index[idim]; if (check_and_adjust_index(&ind, shapevalue, idim, NULL) < 0) { - return NULL; + return NULL; } data += ind * strides[idim]; } @@ -2406,7 +2818,7 @@ PyArray_MultiIndexGetItem(PyArrayObject *self, npy_intp *multi_index) * Returns 0 on success, -1 on failure. */ NPY_NO_EXPORT int -PyArray_MultiIndexSetItem(PyArrayObject *self, npy_intp *multi_index, +PyArray_MultiIndexSetItem(PyArrayObject *self, const npy_intp *multi_index, PyObject *obj) { int idim, ndim = PyArray_NDIM(self); @@ -2425,5 +2837,5 @@ PyArray_MultiIndexSetItem(PyArrayObject *self, npy_intp *multi_index, data += ind * strides[idim]; } - return PyArray_SETITEM(self, data, obj); + return PyArray_Pack(PyArray_DESCR(self), data, obj); } diff --git a/numpy/core/src/multiarray/item_selection.h b/numpy/core/src/multiarray/item_selection.h index 90bb5100d956..40d9eb298f48 100644 --- a/numpy/core/src/multiarray/item_selection.h +++ b/numpy/core/src/multiarray/item_selection.h @@ -1,5 +1,5 @@ -#ifndef _NPY_PRIVATE__ITEM_SELECTION_H_ -#define _NPY_PRIVATE__ITEM_SELECTION_H_ +#ifndef NUMPY_CORE_SRC_MULTIARRAY_ITEM_SELECTION_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_ITEM_SELECTION_H_ /* * Counts the number of True values in a raw boolean array. This @@ -8,14 +8,14 @@ * Returns -1 on error. */ NPY_NO_EXPORT npy_intp -count_boolean_trues(int ndim, char *data, npy_intp *ashape, npy_intp *astrides); +count_boolean_trues(int ndim, char *data, npy_intp const *ashape, npy_intp const *astrides); /* * Gets a single item from the array, based on a single multi-index * array of values, which must be of length PyArray_NDIM(self). */ NPY_NO_EXPORT PyObject * -PyArray_MultiIndexGetItem(PyArrayObject *self, npy_intp *multi_index); +PyArray_MultiIndexGetItem(PyArrayObject *self, const npy_intp *multi_index); /* * Sets a single item in the array, based on a single multi-index @@ -24,7 +24,7 @@ PyArray_MultiIndexGetItem(PyArrayObject *self, npy_intp *multi_index); * Returns 0 on success, -1 on failure. */ NPY_NO_EXPORT int -PyArray_MultiIndexSetItem(PyArrayObject *self, npy_intp *multi_index, +PyArray_MultiIndexSetItem(PyArrayObject *self, const npy_intp *multi_index, PyObject *obj); -#endif +#endif /* NUMPY_CORE_SRC_MULTIARRAY_ITEM_SELECTION_H_ */ diff --git a/numpy/core/src/multiarray/iterators.c b/numpy/core/src/multiarray/iterators.c index 3e3248f53bbc..f959162fd015 100644 --- a/numpy/core/src/multiarray/iterators.c +++ b/numpy/core/src/multiarray/iterators.c @@ -1,9 +1,10 @@ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE + #define PY_SSIZE_T_CLEAN #include <Python.h> -#include "structmember.h" +#include <structmember.h> -#define NPY_NO_DEPRECATED_API NPY_API_VERSION -#define _MULTIARRAYMODULE #include "numpy/arrayobject.h" #include "numpy/arrayscalars.h" @@ -15,6 +16,8 @@ #include "iterators.h" #include "ctors.h" #include "common.h" +#include "conversion_utils.h" +#include "array_coercion.h" #define NEWAXIS_INDEX -1 #define ELLIPSIS_INDEX -2 @@ -60,7 +63,7 @@ parse_index_entry(PyObject *op, npy_intp *step_size, } else if (PySlice_Check(op)) { npy_intp stop; - if (NpySlice_GetIndicesEx(op, max, &i, &stop, step_size, n_steps) < 0) { + if (PySlice_GetIndicesEx(op, max, &i, &stop, step_size, n_steps) < 0) { goto fail; } if (*n_steps <= 0) { @@ -92,121 +95,13 @@ parse_index_entry(PyObject *op, npy_intp *step_size, } -/* - * Parses an index that has no fancy indexing. Populates - * out_dimensions, out_strides, and out_offset. - */ -NPY_NO_EXPORT int -parse_index(PyArrayObject *self, PyObject *op, - npy_intp *out_dimensions, - npy_intp *out_strides, - npy_intp *out_offset, - int check_index) -{ - int i, j, n; - int nd_old, nd_new, n_add, n_ellipsis; - npy_intp n_steps, start, offset, step_size; - PyObject *op1 = NULL; - int is_slice; - - if (PySlice_Check(op) || op == Py_Ellipsis || op == Py_None) { - n = 1; - op1 = op; - Py_INCREF(op); - /* this relies on the fact that n==1 for loop below */ - is_slice = 1; - } - else { - if (!PySequence_Check(op)) { - PyErr_SetString(PyExc_IndexError, - "index must be either an int " - "or a sequence"); - return -1; - } - n = PySequence_Length(op); - is_slice = 0; - } - - nd_old = nd_new = 0; - - offset = 0; - for (i = 0; i < n; i++) { - if (!is_slice) { - op1 = PySequence_GetItem(op, i); - if (op1 == NULL) { - return -1; - } - } - start = parse_index_entry(op1, &step_size, &n_steps, - nd_old < PyArray_NDIM(self) ? - PyArray_DIMS(self)[nd_old] : 0, - nd_old, check_index ? - nd_old < PyArray_NDIM(self) : 0); - Py_DECREF(op1); - if (start == -1) { - break; - } - if (n_steps == NEWAXIS_INDEX) { - out_dimensions[nd_new] = 1; - out_strides[nd_new] = 0; - nd_new++; - } - else if (n_steps == ELLIPSIS_INDEX) { - for (j = i + 1, n_ellipsis = 0; j < n; j++) { - op1 = PySequence_GetItem(op, j); - if (op1 == Py_None) { - n_ellipsis++; - } - Py_DECREF(op1); - } - n_add = PyArray_NDIM(self)-(n-i-n_ellipsis-1+nd_old); - if (n_add < 0) { - PyErr_SetString(PyExc_IndexError, "too many indices"); - return -1; - } - for (j = 0; j < n_add; j++) { - out_dimensions[nd_new] = PyArray_DIMS(self)[nd_old]; - out_strides[nd_new] = PyArray_STRIDES(self)[nd_old]; - nd_new++; nd_old++; - } - } - else { - if (nd_old >= PyArray_NDIM(self)) { - PyErr_SetString(PyExc_IndexError, "too many indices"); - return -1; - } - offset += PyArray_STRIDES(self)[nd_old]*start; - nd_old++; - if (n_steps != SINGLE_INDEX) { - out_dimensions[nd_new] = n_steps; - out_strides[nd_new] = step_size * - PyArray_STRIDES(self)[nd_old-1]; - nd_new++; - } - } - } - if (i < n) { - return -1; - } - n_add = PyArray_NDIM(self)-nd_old; - for (j = 0; j < n_add; j++) { - out_dimensions[nd_new] = PyArray_DIMS(self)[nd_old]; - out_strides[nd_new] = PyArray_STRIDES(self)[nd_old]; - nd_new++; - nd_old++; - } - *out_offset = offset; - return nd_new; -} - - /*********************** Element-wise Array Iterator ***********************/ /* Aided by Peter J. Verveer's nd_image package and numpy's arraymap ****/ /* and Python's array iterator ***/ /* get the dataptr from its current coordinates for simple iterator */ static char* -get_ptr_simple(PyArrayIterObject* iter, npy_intp *coordinates) +get_ptr_simple(PyArrayIterObject* iter, const npy_intp *coordinates) { npy_intp i; char *ret; @@ -224,10 +119,12 @@ get_ptr_simple(PyArrayIterObject* iter, npy_intp *coordinates) * This is common initialization code between PyArrayIterObject and * PyArrayNeighborhoodIterObject * - * Increase ao refcount + * Steals a reference to the array object which gets removed at deallocation, + * if the iterator is allocated statically and its dealloc not called, it + * can be thought of as borrowing the reference. */ -static PyObject * -array_iter_base_init(PyArrayIterObject *it, PyArrayObject *ao) +NPY_NO_EXPORT void +PyArray_RawIterBaseInit(PyArrayIterObject *it, PyArrayObject *ao) { int nd, i; @@ -239,7 +136,6 @@ array_iter_base_init(PyArrayIterObject *it, PyArrayObject *ao) else { it->contiguous = 0; } - Py_INCREF(ao); it->ao = ao; it->size = PyArray_SIZE(ao); it->nd_m1 = nd - 1; @@ -263,7 +159,7 @@ array_iter_base_init(PyArrayIterObject *it, PyArrayObject *ao) it->translate = &get_ptr_simple; PyArray_ITER_RESET(it); - return (PyObject *)it; + return; } static void @@ -278,6 +174,10 @@ array_iter_base_dealloc(PyArrayIterObject *it) NPY_NO_EXPORT PyObject * PyArray_IterNew(PyObject *obj) { + /* + * Note that internally PyArray_RawIterBaseInit may be called directly on a + * statically allocated PyArrayIterObject. + */ PyArrayIterObject *it; PyArrayObject *ao; @@ -294,7 +194,8 @@ PyArray_IterNew(PyObject *obj) return NULL; } - array_iter_base_init(it, ao); + Py_INCREF(ao); /* PyArray_RawIterBaseInit steals a reference */ + PyArray_RawIterBaseInit(it, ao); return (PyObject *)it; } @@ -498,6 +399,10 @@ arrayiter_next(PyArrayIterObject *it) static void arrayiter_dealloc(PyArrayIterObject *it) { + /* + * Note that it is possible to statically allocate a PyArrayIterObject, + * which does not call this function. + */ array_iter_base_dealloc(it); PyArray_free(it); } @@ -647,6 +552,7 @@ iter_subscript(PyArrayIterObject *self, PyObject *ind) char *dptr; int size; PyObject *obj = NULL; + PyObject *new; PyArray_CopySwapFunc *copyswap; if (ind == Py_Ellipsis) { @@ -693,7 +599,7 @@ iter_subscript(PyArrayIterObject *self, PyObject *ind) } /* Check for Integer or Slice */ - if (PyLong_Check(ind) || PyInt_Check(ind) || PySlice_Check(ind)) { + if (PyLong_Check(ind) || PySlice_Check(ind)) { start = parse_index_entry(ind, &step_size, &n_steps, self->size, 0, 1); if (start == -1) { @@ -748,36 +654,36 @@ iter_subscript(PyArrayIterObject *self, PyObject *ind) obj = ind; } - if (PyArray_Check(obj)) { - /* Check for Boolean object */ - if (PyArray_TYPE((PyArrayObject *)obj) == NPY_BOOL) { - ret = iter_subscript_Bool(self, (PyArrayObject *)obj); - Py_DECREF(indtype); - } - /* Check for integer array */ - else if (PyArray_ISINTEGER((PyArrayObject *)obj)) { - PyObject *new; - new = PyArray_FromAny(obj, indtype, 0, 0, - NPY_ARRAY_FORCECAST | NPY_ARRAY_ALIGNED, NULL); - if (new == NULL) { - goto fail; - } - Py_DECREF(obj); - obj = new; - new = iter_subscript_int(self, (PyArrayObject *)obj); - Py_DECREF(obj); - return new; - } - else { - goto fail; - } + /* Any remaining valid input is an array or has been turned into one */ + if (!PyArray_Check(obj)) { + goto fail; + } + + /* Check for Boolean array */ + if (PyArray_TYPE((PyArrayObject *)obj) == NPY_BOOL) { + ret = iter_subscript_Bool(self, (PyArrayObject *)obj); + Py_DECREF(indtype); Py_DECREF(obj); return (PyObject *)ret; } - else { - Py_DECREF(indtype); + + /* Only integer arrays left */ + if (!PyArray_ISINTEGER((PyArrayObject *)obj)) { + goto fail; } + Py_INCREF(indtype); + new = PyArray_FromAny(obj, indtype, 0, 0, + NPY_ARRAY_FORCECAST | NPY_ARRAY_ALIGNED, NULL); + if (new == NULL) { + goto fail; + } + Py_DECREF(indtype); + Py_DECREF(obj); + ret = (PyArrayObject *)iter_subscript_int(self, (PyArrayObject *)new); + Py_DECREF(new); + return (PyObject *)ret; + fail: if (!PyErr_Occurred()) { @@ -921,7 +827,7 @@ iter_ass_subscript(PyArrayIterObject *self, PyObject *ind, PyObject *val) if (PyBool_Check(ind)) { retval = 0; if (PyObject_IsTrue(ind)) { - retval = PyArray_SETITEM(self->ao, self->dataptr, val); + retval = PyArray_Pack(PyArray_DESCR(self->ao), self->dataptr, val); } goto finish; } @@ -937,9 +843,8 @@ iter_ass_subscript(PyArrayIterObject *self, PyObject *ind, PyObject *val) if (check_and_adjust_index(&start, self->size, -1, NULL) < 0) { goto finish; } - retval = 0; PyArray_ITER_GOTO1D(self, start); - retval = type->f->setitem(val, self->dataptr, self->ao); + retval = PyArray_Pack(PyArray_DESCR(self->ao), self->dataptr, val); PyArray_ITER_RESET(self); if (retval < 0) { PyErr_SetString(PyExc_ValueError, @@ -1159,15 +1064,17 @@ static PyMemberDef iter_members[] = { T_OBJECT, offsetof(PyArrayIterObject, ao), READONLY, NULL}, - {"index", - T_INT, - offsetof(PyArrayIterObject, index), - READONLY, NULL}, {NULL, 0, 0, 0, NULL}, }; static PyObject * -iter_coords_get(PyArrayIterObject *self) +iter_index_get(PyArrayIterObject *self, void *NPY_UNUSED(ignored)) +{ + return PyArray_PyIntFromIntp(self->index); +} + +static PyObject * +iter_coords_get(PyArrayIterObject *self, void *NPY_UNUSED(ignored)) { int nd; nd = PyArray_NDIM(self->ao); @@ -1192,75 +1099,60 @@ iter_coords_get(PyArrayIterObject *self) } static PyGetSetDef iter_getsets[] = { + {"index", + (getter)iter_index_get, + NULL, NULL, NULL}, {"coords", (getter)iter_coords_get, - NULL, - NULL, NULL}, + NULL, NULL, NULL}, {NULL, NULL, NULL, NULL, NULL}, }; NPY_NO_EXPORT PyTypeObject PyArrayIter_Type = { -#if defined(NPY_PY3K) PyVarObject_HEAD_INIT(NULL, 0) -#else - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ -#endif - "numpy.flatiter", /* tp_name */ - sizeof(PyArrayIterObject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)arrayiter_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ -#if defined(NPY_PY3K) - 0, /* tp_reserved */ -#else - 0, /* tp_compare */ -#endif - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - &iter_as_mapping, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - 0, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - (richcmpfunc)iter_richcompare, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - (iternextfunc)arrayiter_next, /* tp_iternext */ - iter_methods, /* tp_methods */ - iter_members, /* tp_members */ - iter_getsets, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ - 0, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - 0, /* tp_del */ - 0, /* tp_version_tag */ + .tp_name = "numpy.flatiter", + .tp_basicsize = sizeof(PyArrayIterObject), + .tp_dealloc = (destructor)arrayiter_dealloc, + .tp_as_mapping = &iter_as_mapping, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_richcompare = (richcmpfunc)iter_richcompare, + .tp_iternext = (iternextfunc)arrayiter_next, + .tp_methods = iter_methods, + .tp_members = iter_members, + .tp_getset = iter_getsets, }; /** END of Array Iterator **/ + +static int +set_shape_mismatch_exception(PyArrayMultiIterObject *mit, int i1, int i2) +{ + PyObject *shape1, *shape2, *msg; + + shape1 = PyObject_GetAttrString((PyObject *) mit->iters[i1]->ao, "shape"); + if (shape1 == NULL) { + return -1; + } + shape2 = PyObject_GetAttrString((PyObject *) mit->iters[i2]->ao, "shape"); + if (shape2 == NULL) { + Py_DECREF(shape1); + return -1; + } + msg = PyUnicode_FromFormat("shape mismatch: objects cannot be broadcast " + "to a single shape. Mismatch is between arg %d " + "with shape %S and arg %d with shape %S.", + i1, shape1, i2, shape2); + Py_DECREF(shape1); + Py_DECREF(shape2); + if (msg == NULL) { + return -1; + } + PyErr_SetObject(PyExc_ValueError, msg); + Py_DECREF(msg); + return 0; +} + /* Adjust dimensionality and strides for index object iterators --- i.e. broadcast */ @@ -1269,6 +1161,7 @@ NPY_NO_EXPORT int PyArray_Broadcast(PyArrayMultiIterObject *mit) { int i, nd, k, j; + int src_iter = -1; /* Initializing avoids a compiler warning. */ npy_intp tmp; PyArrayIterObject *it; @@ -1292,12 +1185,10 @@ PyArray_Broadcast(PyArrayMultiIterObject *mit) } if (mit->dimensions[i] == 1) { mit->dimensions[i] = tmp; + src_iter = j; } else if (mit->dimensions[i] != tmp) { - PyErr_SetString(PyExc_ValueError, - "shape mismatch: objects" \ - " cannot be broadcast" \ - " to a single shape"); + set_shape_mismatch_exception(mit, src_iter, j); return -1; } } @@ -1349,232 +1240,169 @@ PyArray_Broadcast(PyArrayMultiIterObject *mit) return 0; } -/*NUMPY_API - * Get MultiIterator from array of Python objects and any additional - * - * PyObject **mps -- array of PyObjects - * int n - number of PyObjects in the array - * int nadd - number of additional arrays to include in the iterator. - * - * Returns a multi-iterator object. +static NPY_INLINE PyObject* +multiiter_wrong_number_of_args(void) +{ + return PyErr_Format(PyExc_ValueError, + "Need at least 0 and at most %d " + "array objects.", NPY_MAXARGS); +} + +/* + * Common implementation for all PyArrayMultiIterObject constructors. */ -NPY_NO_EXPORT PyObject * -PyArray_MultiIterFromObjects(PyObject **mps, int n, int nadd, ...) +static PyObject* +multiiter_new_impl(int n_args, PyObject **args) { - va_list va; PyArrayMultiIterObject *multi; - PyObject *current; - PyObject *arr; - - int i, ntot, err=0; + int i; - ntot = n + nadd; - if (ntot < 1 || ntot > NPY_MAXARGS) { - PyErr_Format(PyExc_ValueError, - "Need at least 1 and at most %d " - "array objects.", NPY_MAXARGS); - return NULL; - } multi = PyArray_malloc(sizeof(PyArrayMultiIterObject)); if (multi == NULL) { return PyErr_NoMemory(); } PyObject_Init((PyObject *)multi, &PyArrayMultiIter_Type); + multi->numiter = 0; - for (i = 0; i < ntot; i++) { - multi->iters[i] = NULL; - } - multi->numiter = ntot; - multi->index = 0; + for (i = 0; i < n_args; ++i) { + PyObject *obj = args[i]; + PyObject *arr; + PyArrayIterObject *it; - va_start(va, nadd); - for (i = 0; i < ntot; i++) { - if (i < n) { - current = mps[i]; - } - else { - current = va_arg(va, PyObject *); - } - arr = PyArray_FROM_O(current); - if (arr == NULL) { - err = 1; - break; + if (PyObject_IsInstance(obj, (PyObject *)&PyArrayMultiIter_Type)) { + PyArrayMultiIterObject *mit = (PyArrayMultiIterObject *)obj; + int j; + + if (multi->numiter + mit->numiter > NPY_MAXARGS) { + multiiter_wrong_number_of_args(); + goto fail; + } + for (j = 0; j < mit->numiter; ++j) { + arr = (PyObject *)mit->iters[j]->ao; + it = (PyArrayIterObject *)PyArray_IterNew(arr); + if (it == NULL) { + goto fail; + } + multi->iters[multi->numiter++] = it; + } } - else { - multi->iters[i] = (PyArrayIterObject *)PyArray_IterNew(arr); - if (multi->iters[i] == NULL) { - err = 1; - break; + else if (multi->numiter < NPY_MAXARGS) { + arr = PyArray_FromAny(obj, NULL, 0, 0, 0, NULL); + if (arr == NULL) { + goto fail; } + it = (PyArrayIterObject *)PyArray_IterNew(arr); Py_DECREF(arr); + if (it == NULL) { + goto fail; + } + multi->iters[multi->numiter++] = it; + } + else { + multiiter_wrong_number_of_args(); + goto fail; } } - va_end(va); - if (!err && PyArray_Broadcast(multi) < 0) { - err = 1; + if (multi->numiter < 0) { + multiiter_wrong_number_of_args(); + goto fail; } - if (err) { - Py_DECREF(multi); - return NULL; + if (PyArray_Broadcast(multi) < 0) { + goto fail; } PyArray_MultiIter_RESET(multi); + return (PyObject *)multi; + +fail: + Py_DECREF(multi); + + return NULL; } /*NUMPY_API - * Get MultiIterator, + * Get MultiIterator from array of Python objects and any additional + * + * PyObject **mps - array of PyObjects + * int n - number of PyObjects in the array + * int nadd - number of additional arrays to include in the iterator. + * + * Returns a multi-iterator object. */ -NPY_NO_EXPORT PyObject * -PyArray_MultiIterNew(int n, ...) +NPY_NO_EXPORT PyObject* +PyArray_MultiIterFromObjects(PyObject **mps, int n, int nadd, ...) { + PyObject *args_impl[NPY_MAXARGS]; + int ntot = n + nadd; + int i; va_list va; - PyArrayMultiIterObject *multi; - PyObject *current; - PyObject *arr; - int i, err = 0; - - if (n < 1 || n > NPY_MAXARGS) { - PyErr_Format(PyExc_ValueError, - "Need at least 1 and at most %d " - "array objects.", NPY_MAXARGS); - return NULL; + if ((ntot > NPY_MAXARGS) || (ntot < 0)) { + return multiiter_wrong_number_of_args(); } - /* fprintf(stderr, "multi new...");*/ + for (i = 0; i < n; ++i) { + args_impl[i] = mps[i]; + } - multi = PyArray_malloc(sizeof(PyArrayMultiIterObject)); - if (multi == NULL) { - return PyErr_NoMemory(); + va_start(va, nadd); + for (; i < ntot; ++i) { + args_impl[i] = va_arg(va, PyObject *); } - PyObject_Init((PyObject *)multi, &PyArrayMultiIter_Type); + va_end(va); - for (i = 0; i < n; i++) { - multi->iters[i] = NULL; + return multiiter_new_impl(ntot, args_impl); +} + +/*NUMPY_API + * Get MultiIterator, + */ +NPY_NO_EXPORT PyObject* +PyArray_MultiIterNew(int n, ...) +{ + PyObject *args_impl[NPY_MAXARGS]; + int i; + va_list va; + + if ((n > NPY_MAXARGS) || (n < 0)) { + return multiiter_wrong_number_of_args(); } - multi->numiter = n; - multi->index = 0; va_start(va, n); - for (i = 0; i < n; i++) { - current = va_arg(va, PyObject *); - arr = PyArray_FROM_O(current); - if (arr == NULL) { - err = 1; - break; - } - else { - multi->iters[i] = (PyArrayIterObject *)PyArray_IterNew(arr); - if (multi->iters[i] == NULL) { - err = 1; - break; - } - Py_DECREF(arr); - } + for (i = 0; i < n; ++i) { + args_impl[i] = va_arg(va, PyObject *); } va_end(va); - if (!err && PyArray_Broadcast(multi) < 0) { - err = 1; - } - if (err) { - Py_DECREF(multi); - return NULL; - } - PyArray_MultiIter_RESET(multi); - return (PyObject *)multi; + return multiiter_new_impl(n, args_impl); } -static PyObject * -arraymultiter_new(PyTypeObject *NPY_UNUSED(subtype), PyObject *args, PyObject *kwds) +static PyObject* +arraymultiter_new(PyTypeObject *NPY_UNUSED(subtype), PyObject *args, + PyObject *kwds) { + PyObject *ret, *fast_seq; + Py_ssize_t n; - Py_ssize_t n = 0; - Py_ssize_t i, j, k; - PyArrayMultiIterObject *multi; - PyObject *arr; - - if (kwds != NULL) { + if (kwds != NULL && PyDict_Size(kwds) > 0) { PyErr_SetString(PyExc_ValueError, "keyword arguments not accepted."); return NULL; } - for (j = 0; j < PyTuple_Size(args); ++j) { - PyObject *obj = PyTuple_GET_ITEM(args, j); - - if (PyObject_IsInstance(obj, (PyObject *)&PyArrayMultiIter_Type)) { - /* - * If obj is a multi-iterator, all its arrays will be added - * to the new multi-iterator. - */ - n += ((PyArrayMultiIterObject *)obj)->numiter; - } - else { - /* If not, will try to convert it to a single array */ - ++n; - } - } - if (n < 1 || n > NPY_MAXARGS) { - if (PyErr_Occurred()) { - return NULL; - } - PyErr_Format(PyExc_ValueError, - "Need at least 1 and at most %d " - "array objects.", NPY_MAXARGS); + fast_seq = PySequence_Fast(args, ""); // needed for pypy + if (fast_seq == NULL) { return NULL; } - - multi = PyArray_malloc(sizeof(PyArrayMultiIterObject)); - if (multi == NULL) { - return PyErr_NoMemory(); - } - PyObject_Init((PyObject *)multi, &PyArrayMultiIter_Type); - - multi->numiter = n; - multi->index = 0; - i = 0; - for (j = 0; j < PyTuple_GET_SIZE(args); ++j) { - PyObject *obj = PyTuple_GET_ITEM(args, j); - PyArrayIterObject *it; - - if (PyObject_IsInstance(obj, (PyObject *)&PyArrayMultiIter_Type)) { - PyArrayMultiIterObject *mit = (PyArrayMultiIterObject *)obj; - - for (k = 0; k < mit->numiter; ++k) { - arr = (PyObject *)mit->iters[k]->ao; - assert (arr != NULL); - it = (PyArrayIterObject *)PyArray_IterNew(arr); - if (it == NULL) { - goto fail; - } - multi->iters[i++] = it; - } - } - else { - arr = PyArray_FROM_O(obj); - if (arr == NULL) { - goto fail; - } - it = (PyArrayIterObject *)PyArray_IterNew(arr); - if (it == NULL) { - goto fail; - } - multi->iters[i++] = it; - Py_DECREF(arr); - } - } - assert (i == n); - if (PyArray_Broadcast(multi) < 0) { - goto fail; + n = PySequence_Fast_GET_SIZE(fast_seq); + if (n > NPY_MAXARGS) { + Py_DECREF(fast_seq); + return multiiter_wrong_number_of_args(); } - PyArray_MultiIter_RESET(multi); - return (PyObject *)multi; - - fail: - Py_DECREF(multi); - return NULL; + ret = multiiter_new_impl(n, PySequence_Fast_ITEMS(fast_seq)); + Py_DECREF(fast_seq); + return ret; } static PyObject * @@ -1614,43 +1442,25 @@ arraymultiter_dealloc(PyArrayMultiIterObject *multi) } static PyObject * -arraymultiter_size_get(PyArrayMultiIterObject *self) +arraymultiter_size_get(PyArrayMultiIterObject *self, void *NPY_UNUSED(ignored)) { -#if NPY_SIZEOF_INTP <= NPY_SIZEOF_LONG - return PyInt_FromLong((long) self->size); -#else - if (self->size < NPY_MAX_LONG) { - return PyInt_FromLong((long) self->size); - } - else { - return PyLong_FromLongLong((npy_longlong) self->size); - } -#endif + return PyArray_PyIntFromIntp(self->size); } static PyObject * -arraymultiter_index_get(PyArrayMultiIterObject *self) +arraymultiter_index_get(PyArrayMultiIterObject *self, void *NPY_UNUSED(ignored)) { -#if NPY_SIZEOF_INTP <= NPY_SIZEOF_LONG - return PyInt_FromLong((long) self->index); -#else - if (self->size < NPY_MAX_LONG) { - return PyInt_FromLong((long) self->index); - } - else { - return PyLong_FromLongLong((npy_longlong) self->index); - } -#endif + return PyArray_PyIntFromIntp(self->index); } static PyObject * -arraymultiter_shape_get(PyArrayMultiIterObject *self) +arraymultiter_shape_get(PyArrayMultiIterObject *self, void *NPY_UNUSED(ignored)) { return PyArray_IntTupleFromIntp(self->nd, self->dimensions); } static PyObject * -arraymultiter_iters_get(PyArrayMultiIterObject *self) +arraymultiter_iters_get(PyArrayMultiIterObject *self, void *NPY_UNUSED(ignored)) { PyObject *res; int i, n; @@ -1721,63 +1531,16 @@ static PyMethodDef arraymultiter_methods[] = { }; NPY_NO_EXPORT PyTypeObject PyArrayMultiIter_Type = { -#if defined(NPY_PY3K) PyVarObject_HEAD_INIT(NULL, 0) -#else - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ -#endif - "numpy.broadcast", /* tp_name */ - sizeof(PyArrayMultiIterObject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)arraymultiter_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ -#if defined(NPY_PY3K) - 0, /* tp_reserved */ -#else - 0, /* tp_compare */ -#endif - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - 0, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - (iternextfunc)arraymultiter_next, /* tp_iternext */ - arraymultiter_methods, /* tp_methods */ - arraymultiter_members, /* tp_members */ - arraymultiter_getsetlist, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)0, /* tp_init */ - 0, /* tp_alloc */ - arraymultiter_new, /* tp_new */ - 0, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - 0, /* tp_del */ - 0, /* tp_version_tag */ + .tp_name = "numpy.broadcast", + .tp_basicsize = sizeof(PyArrayMultiIterObject), + .tp_dealloc = (destructor)arraymultiter_dealloc, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_iternext = (iternextfunc)arraymultiter_next, + .tp_methods = arraymultiter_methods, + .tp_members = arraymultiter_members, + .tp_getset = arraymultiter_getsetlist, + .tp_new = arraymultiter_new, }; /*========================= Neighborhood iterator ======================*/ @@ -1826,7 +1589,7 @@ static char* _set_constant(PyArrayNeighborhoodIterObject* iter, /* set the dataptr from its current coordinates */ static char* -get_ptr_constant(PyArrayIterObject* _iter, npy_intp *coordinates) +get_ptr_constant(PyArrayIterObject* _iter, const npy_intp *coordinates) { int i; npy_intp bd, _coordinates[NPY_MAXDIMS]; @@ -1881,7 +1644,7 @@ __npy_pos_remainder(npy_intp i, npy_intp n) /* set the dataptr from its current coordinates */ static char* -get_ptr_mirror(PyArrayIterObject* _iter, npy_intp *coordinates) +get_ptr_mirror(PyArrayIterObject* _iter, const npy_intp *coordinates) { int i; npy_intp bd, _coordinates[NPY_MAXDIMS], lb; @@ -1915,7 +1678,7 @@ __npy_euclidean_division(npy_intp i, npy_intp n) _coordinates[c] = lb + __npy_euclidean_division(bd, p->limits_sizes[c]); static char* -get_ptr_circular(PyArrayIterObject* _iter, npy_intp *coordinates) +get_ptr_circular(PyArrayIterObject* _iter, const npy_intp *coordinates) { int i; npy_intp bd, _coordinates[NPY_MAXDIMS], lb; @@ -1937,7 +1700,7 @@ get_ptr_circular(PyArrayIterObject* _iter, npy_intp *coordinates) * A Neighborhood Iterator object. */ NPY_NO_EXPORT PyObject* -PyArray_NeighborhoodIterNew(PyArrayIterObject *x, npy_intp *bounds, +PyArray_NeighborhoodIterNew(PyArrayIterObject *x, const npy_intp *bounds, int mode, PyArrayObject* fill) { int i; @@ -1949,7 +1712,8 @@ PyArray_NeighborhoodIterNew(PyArrayIterObject *x, npy_intp *bounds, } PyObject_Init((PyObject *)ret, &PyArrayNeighborhoodIter_Type); - array_iter_base_init((PyArrayIterObject*)ret, x->ao); + Py_INCREF(x->ao); /* PyArray_RawIterBaseInit steals a reference */ + PyArray_RawIterBaseInit((PyArrayIterObject*)ret, x->ao); Py_INCREF(x); ret->_internal_iter = x; @@ -2050,60 +1814,9 @@ static void neighiter_dealloc(PyArrayNeighborhoodIterObject* iter) } NPY_NO_EXPORT PyTypeObject PyArrayNeighborhoodIter_Type = { -#if defined(NPY_PY3K) PyVarObject_HEAD_INIT(NULL, 0) -#else - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ -#endif - "numpy.neigh_internal_iter", /* tp_name*/ - sizeof(PyArrayNeighborhoodIterObject), /* tp_basicsize*/ - 0, /* tp_itemsize*/ - (destructor)neighiter_dealloc, /* tp_dealloc*/ - 0, /* tp_print*/ - 0, /* tp_getattr*/ - 0, /* tp_setattr*/ -#if defined(NPY_PY3K) - 0, /* tp_reserved */ -#else - 0, /* tp_compare */ -#endif - 0, /* tp_repr*/ - 0, /* tp_as_number*/ - 0, /* tp_as_sequence*/ - 0, /* tp_as_mapping*/ - 0, /* tp_hash */ - 0, /* tp_call*/ - 0, /* tp_str*/ - 0, /* tp_getattro*/ - 0, /* tp_setattro*/ - 0, /* tp_as_buffer*/ - Py_TPFLAGS_DEFAULT, /* tp_flags*/ - 0, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - (iternextfunc)0, /* tp_iternext */ - 0, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)0, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ - 0, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - 0, /* tp_del */ - 0, /* tp_version_tag */ + .tp_name = "numpy.neigh_internal_iter", + .tp_basicsize = sizeof(PyArrayNeighborhoodIterObject), + .tp_dealloc = (destructor)neighiter_dealloc, + .tp_flags = Py_TPFLAGS_DEFAULT, }; diff --git a/numpy/core/src/multiarray/iterators.h b/numpy/core/src/multiarray/iterators.h index 04f57c885fda..883615cc9993 100644 --- a/numpy/core/src/multiarray/iterators.h +++ b/numpy/core/src/multiarray/iterators.h @@ -1,16 +1,5 @@ -#ifndef _NPY_ARRAYITERATORS_H_ -#define _NPY_ARRAYITERATORS_H_ - -/* - * Parses an index that has no fancy indexing. Populates - * out_dimensions, out_strides, and out_offset. - */ -NPY_NO_EXPORT int -parse_index(PyArrayObject *self, PyObject *op, - npy_intp *out_dimensions, - npy_intp *out_strides, - npy_intp *out_offset, - int check_index); +#ifndef NUMPY_CORE_SRC_MULTIARRAY_ITERATORS_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_ITERATORS_H_ NPY_NO_EXPORT PyObject *iter_subscript(PyArrayIterObject *, PyObject *); @@ -18,4 +7,7 @@ NPY_NO_EXPORT PyObject NPY_NO_EXPORT int iter_ass_subscript(PyArrayIterObject *, PyObject *, PyObject *); -#endif +NPY_NO_EXPORT void +PyArray_RawIterBaseInit(PyArrayIterObject *it, PyArrayObject *ao); + +#endif /* NUMPY_CORE_SRC_MULTIARRAY_ITERATORS_H_ */ diff --git a/numpy/core/src/multiarray/legacy_dtype_implementation.c b/numpy/core/src/multiarray/legacy_dtype_implementation.c new file mode 100644 index 000000000000..72a52d7a87c0 --- /dev/null +++ b/numpy/core/src/multiarray/legacy_dtype_implementation.c @@ -0,0 +1,553 @@ +/* + * The only function exported here is `PyArray_LegacyCanCastTypeTo`, which + * is currently still in use when first registering a userdtype. + * + * The extremely limited use means that it can probably remain unmaintained + * until such a time where legay user dtypes are deprecated and removed + * entirely. + */ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE + +#include "numpy/arrayobject.h" +#include "scalartypes.h" +#include "_datetime.h" +#include "datetime_strings.h" +#include "convert_datatype.h" + +#include "legacy_dtype_implementation.h" + + +/* + * Compare the field dictionaries for two types. + * + * Return 1 if the field types and field names of the two descrs are equal and + * in the same order, 0 if not. + */ +static int +_equivalent_fields(PyArray_Descr *type1, PyArray_Descr *type2) { + + int val; + + if (type1->fields == type2->fields && type1->names == type2->names) { + return 1; + } + if (type1->fields == NULL || type2->fields == NULL) { + return 0; + } + + val = PyObject_RichCompareBool(type1->fields, type2->fields, Py_EQ); + if (val != 1 || PyErr_Occurred()) { + PyErr_Clear(); + return 0; + } + + val = PyObject_RichCompareBool(type1->names, type2->names, Py_EQ); + if (val != 1 || PyErr_Occurred()) { + PyErr_Clear(); + return 0; + } + + return 1; +} + +/* + * Compare the subarray data for two types. + * Return 1 if they are the same, 0 if not. + */ +static int +_equivalent_subarrays(PyArray_ArrayDescr *sub1, PyArray_ArrayDescr *sub2) +{ + int val; + + if (sub1 == sub2) { + return 1; + + } + if (sub1 == NULL || sub2 == NULL) { + return 0; + } + + val = PyObject_RichCompareBool(sub1->shape, sub2->shape, Py_EQ); + if (val != 1 || PyErr_Occurred()) { + PyErr_Clear(); + return 0; + } + + return PyArray_EquivTypes(sub1->base, sub2->base); +} + + +static unsigned char +PyArray_LegacyEquivTypes(PyArray_Descr *type1, PyArray_Descr *type2) +{ + int type_num1, type_num2, size1, size2; + + if (type1 == type2) { + return NPY_TRUE; + } + + type_num1 = type1->type_num; + type_num2 = type2->type_num; + size1 = type1->elsize; + size2 = type2->elsize; + + if (size1 != size2) { + return NPY_FALSE; + } + if (PyArray_ISNBO(type1->byteorder) != PyArray_ISNBO(type2->byteorder)) { + return NPY_FALSE; + } + if (type1->subarray || type2->subarray) { + return ((type_num1 == type_num2) + && _equivalent_subarrays(type1->subarray, type2->subarray)); + } + if (type_num1 == NPY_VOID || type_num2 == NPY_VOID) { + return ((type_num1 == type_num2) && _equivalent_fields(type1, type2)); + } + if (type_num1 == NPY_DATETIME + || type_num1 == NPY_TIMEDELTA + || type_num2 == NPY_DATETIME + || type_num2 == NPY_TIMEDELTA) { + return ((type_num1 == type_num2) + && has_equivalent_datetime_metadata(type1, type2)); + } + return type1->kind == type2->kind; +} + + +static unsigned char +PyArray_LegacyEquivTypenums(int typenum1, int typenum2) +{ + PyArray_Descr *d1, *d2; + npy_bool ret; + + if (typenum1 == typenum2) { + return NPY_SUCCEED; + } + + d1 = PyArray_DescrFromType(typenum1); + d2 = PyArray_DescrFromType(typenum2); + ret = PyArray_LegacyEquivTypes(d1, d2); + Py_DECREF(d1); + Py_DECREF(d2); + return ret; +} + + +static int +PyArray_LegacyCanCastSafely(int fromtype, int totype) +{ + PyArray_Descr *from; + + /* Fast table lookup for small type numbers */ + if ((unsigned int)fromtype < NPY_NTYPES && + (unsigned int)totype < NPY_NTYPES) { + return _npy_can_cast_safely_table[fromtype][totype]; + } + + /* Identity */ + if (fromtype == totype) { + return 1; + } + + from = PyArray_DescrFromType(fromtype); + /* + * cancastto is a NPY_NOTYPE terminated C-int-array of types that + * the data-type can be cast to safely. + */ + if (from->f->cancastto) { + int *curtype = from->f->cancastto; + + while (*curtype != NPY_NOTYPE) { + if (*curtype++ == totype) { + Py_DECREF(from); + return 1; + } + } + } + Py_DECREF(from); + return 0; +} + + +static npy_bool +PyArray_LegacyCanCastTo(PyArray_Descr *from, PyArray_Descr *to) +{ + int from_type_num = from->type_num; + int to_type_num = to->type_num; + npy_bool ret; + + ret = (npy_bool) PyArray_LegacyCanCastSafely(from_type_num, to_type_num); + if (ret) { + /* Check String and Unicode more closely */ + if (from_type_num == NPY_STRING) { + if (to_type_num == NPY_STRING) { + ret = (from->elsize <= to->elsize); + } + else if (to_type_num == NPY_UNICODE) { + ret = (from->elsize << 2 <= to->elsize); + } + } + else if (from_type_num == NPY_UNICODE) { + if (to_type_num == NPY_UNICODE) { + ret = (from->elsize <= to->elsize); + } + } + /* + * For datetime/timedelta, only treat casts moving towards + * more precision as safe. + */ + else if (from_type_num == NPY_DATETIME && to_type_num == NPY_DATETIME) { + PyArray_DatetimeMetaData *meta1, *meta2; + meta1 = get_datetime_metadata_from_dtype(from); + if (meta1 == NULL) { + PyErr_Clear(); + return 0; + } + meta2 = get_datetime_metadata_from_dtype(to); + if (meta2 == NULL) { + PyErr_Clear(); + return 0; + } + + return can_cast_datetime64_metadata(meta1, meta2, + NPY_SAFE_CASTING); + } + else if (from_type_num == NPY_TIMEDELTA && + to_type_num == NPY_TIMEDELTA) { + PyArray_DatetimeMetaData *meta1, *meta2; + meta1 = get_datetime_metadata_from_dtype(from); + if (meta1 == NULL) { + PyErr_Clear(); + return 0; + } + meta2 = get_datetime_metadata_from_dtype(to); + if (meta2 == NULL) { + PyErr_Clear(); + return 0; + } + + return can_cast_timedelta64_metadata(meta1, meta2, + NPY_SAFE_CASTING); + } + /* + * If to_type_num is STRING or unicode + * see if the length is long enough to hold the + * stringified value of the object. + */ + else if (to_type_num == NPY_STRING || to_type_num == NPY_UNICODE) { + /* + * Boolean value cast to string type is 5 characters max + * for string 'False'. + */ + int char_size = 1; + if (to_type_num == NPY_UNICODE) { + char_size = 4; + } + + ret = 0; + if (PyDataType_ISUNSIZED(to)) { + ret = 1; + } + /* + * Need at least 5 characters to convert from boolean + * to 'True' or 'False'. + */ + else if (from->kind == 'b' && to->elsize >= 5 * char_size) { + ret = 1; + } + else if (from->kind == 'u') { + /* Guard against unexpected integer size */ + if (from->elsize > 8 || from->elsize < 0) { + ret = 0; + } + else if (to->elsize >= + REQUIRED_STR_LEN[from->elsize] * char_size) { + ret = 1; + } + } + else if (from->kind == 'i') { + /* Guard against unexpected integer size */ + if (from->elsize > 8 || from->elsize < 0) { + ret = 0; + } + /* Extra character needed for sign */ + else if (to->elsize >= + (REQUIRED_STR_LEN[from->elsize] + 1) * char_size) { + ret = 1; + } + } + } + } + return ret; +} + + +/* + * Compare two field dictionaries for castability. + * + * Return 1 if 'field1' can be cast to 'field2' according to the rule + * 'casting', 0 if not. + * + * Castabiliy of field dictionaries is defined recursively: 'field1' and + * 'field2' must have the same field names (possibly in different + * orders), and the corresponding field types must be castable according + * to the given casting rule. + */ +static int +can_cast_fields(PyObject *field1, PyObject *field2, NPY_CASTING casting) +{ + Py_ssize_t ppos; + PyObject *key; + PyObject *tuple1, *tuple2; + + if (field1 == field2) { + return 1; + } + if (field1 == NULL || field2 == NULL) { + return 0; + } + if (PyDict_Size(field1) != PyDict_Size(field2)) { + return 0; + } + + /* Iterate over all the fields and compare for castability */ + ppos = 0; + while (PyDict_Next(field1, &ppos, &key, &tuple1)) { + if ((tuple2 = PyDict_GetItem(field2, key)) == NULL) { + return 0; + } + /* Compare the dtype of the field for castability */ + if (!PyArray_CanCastTypeTo( + (PyArray_Descr *)PyTuple_GET_ITEM(tuple1, 0), + (PyArray_Descr *)PyTuple_GET_ITEM(tuple2, 0), + casting)) { + return 0; + } + } + + return 1; +} + + +NPY_NO_EXPORT npy_bool +PyArray_LegacyCanCastTypeTo(PyArray_Descr *from, PyArray_Descr *to, + NPY_CASTING casting) +{ + /* + * Fast paths for equality and for basic types. + */ + if (from == to || + ((NPY_LIKELY(PyDataType_ISNUMBER(from)) || + PyDataType_ISOBJECT(from)) && + NPY_LIKELY(from->type_num == to->type_num) && + NPY_LIKELY(from->byteorder == to->byteorder))) { + return 1; + } + /* + * Cases with subarrays and fields need special treatment. + */ + if (PyDataType_HASFIELDS(from)) { + /* + * If from is a structured data type, then it can be cast to a simple + * non-object one only for unsafe casting *and* if it has a single + * field; recurse just in case the single field is itself structured. + */ + if (!PyDataType_HASFIELDS(to) && !PyDataType_ISOBJECT(to)) { + if (casting == NPY_UNSAFE_CASTING && + PyDict_Size(from->fields) == 1) { + Py_ssize_t ppos = 0; + PyObject *tuple; + PyArray_Descr *field; + PyDict_Next(from->fields, &ppos, NULL, &tuple); + field = (PyArray_Descr *)PyTuple_GET_ITEM(tuple, 0); + /* + * For a subarray, we need to get the underlying type; + * since we already are casting unsafely, we can ignore + * the shape. + */ + if (PyDataType_HASSUBARRAY(field)) { + field = field->subarray->base; + } + return PyArray_LegacyCanCastTypeTo(field, to, casting); + } + else { + return 0; + } + } + /* + * Casting from one structured data type to another depends on the fields; + * we pass that case on to the EquivTypenums case below. + * + * TODO: move that part up here? Need to check whether equivalent type + * numbers is an addition constraint that is needed. + * + * TODO/FIXME: For now, always allow structured to structured for unsafe + * casting; this is not correct, but needed since the treatment in can_cast + * below got out of sync with astype; see gh-13667. + */ + if (casting == NPY_UNSAFE_CASTING) { + return 1; + } + } + else if (PyDataType_HASFIELDS(to)) { + /* + * If "from" is a simple data type and "to" has fields, then only + * unsafe casting works (and that works always, even to multiple fields). + */ + return casting == NPY_UNSAFE_CASTING; + } + /* + * Everything else we consider castable for unsafe for now. + * FIXME: ensure what we do here is consistent with "astype", + * i.e., deal more correctly with subarrays and user-defined dtype. + */ + else if (casting == NPY_UNSAFE_CASTING) { + return 1; + } + /* + * Equivalent simple types can be cast with any value of 'casting', but + * we need to be careful about structured to structured. + */ + if (PyArray_LegacyEquivTypenums(from->type_num, to->type_num)) { + /* For complicated case, use EquivTypes (for now) */ + if (PyTypeNum_ISUSERDEF(from->type_num) || + from->subarray != NULL) { + int ret; + + /* Only NPY_NO_CASTING prevents byte order conversion */ + if ((casting != NPY_NO_CASTING) && + (!PyArray_ISNBO(from->byteorder) || + !PyArray_ISNBO(to->byteorder))) { + PyArray_Descr *nbo_from, *nbo_to; + + nbo_from = PyArray_DescrNewByteorder(from, NPY_NATIVE); + nbo_to = PyArray_DescrNewByteorder(to, NPY_NATIVE); + if (nbo_from == NULL || nbo_to == NULL) { + Py_XDECREF(nbo_from); + Py_XDECREF(nbo_to); + PyErr_Clear(); + return 0; + } + ret = PyArray_LegacyEquivTypes(nbo_from, nbo_to); + Py_DECREF(nbo_from); + Py_DECREF(nbo_to); + } + else { + ret = PyArray_LegacyEquivTypes(from, to); + } + return ret; + } + + if (PyDataType_HASFIELDS(from)) { + switch (casting) { + case NPY_EQUIV_CASTING: + case NPY_SAFE_CASTING: + case NPY_SAME_KIND_CASTING: + /* + * `from' and `to' must have the same fields, and + * corresponding fields must be (recursively) castable. + */ + return can_cast_fields(from->fields, to->fields, casting); + + case NPY_NO_CASTING: + default: + return PyArray_LegacyEquivTypes(from, to); + } + } + + switch (from->type_num) { + case NPY_DATETIME: { + PyArray_DatetimeMetaData *meta1, *meta2; + meta1 = get_datetime_metadata_from_dtype(from); + if (meta1 == NULL) { + PyErr_Clear(); + return 0; + } + meta2 = get_datetime_metadata_from_dtype(to); + if (meta2 == NULL) { + PyErr_Clear(); + return 0; + } + + if (casting == NPY_NO_CASTING) { + return PyArray_ISNBO(from->byteorder) == + PyArray_ISNBO(to->byteorder) && + can_cast_datetime64_metadata(meta1, meta2, casting); + } + else { + return can_cast_datetime64_metadata(meta1, meta2, casting); + } + } + case NPY_TIMEDELTA: { + PyArray_DatetimeMetaData *meta1, *meta2; + meta1 = get_datetime_metadata_from_dtype(from); + if (meta1 == NULL) { + PyErr_Clear(); + return 0; + } + meta2 = get_datetime_metadata_from_dtype(to); + if (meta2 == NULL) { + PyErr_Clear(); + return 0; + } + + if (casting == NPY_NO_CASTING) { + return PyArray_ISNBO(from->byteorder) == + PyArray_ISNBO(to->byteorder) && + can_cast_timedelta64_metadata(meta1, meta2, casting); + } + else { + return can_cast_timedelta64_metadata(meta1, meta2, casting); + } + } + default: + switch (casting) { + case NPY_NO_CASTING: + return PyArray_LegacyEquivTypes(from, to); + case NPY_EQUIV_CASTING: + return (from->elsize == to->elsize); + case NPY_SAFE_CASTING: + return (from->elsize <= to->elsize); + default: + return 1; + } + break; + } + } + /* If safe or same-kind casts are allowed */ + else if (casting == NPY_SAFE_CASTING || casting == NPY_SAME_KIND_CASTING) { + if (PyArray_LegacyCanCastTo(from, to)) { + return 1; + } + else if(casting == NPY_SAME_KIND_CASTING) { + /* + * Also allow casting from lower to higher kinds, according + * to the ordering provided by dtype_kind_to_ordering. + * Some kinds, like datetime, don't fit in the hierarchy, + * and are special cased as -1. + */ + int from_order, to_order; + + from_order = dtype_kind_to_ordering(from->kind); + to_order = dtype_kind_to_ordering(to->kind); + + if (to->kind == 'm') { + /* both types being timedelta is already handled before. */ + int integer_order = dtype_kind_to_ordering('i'); + return (from_order != -1) && (from_order <= integer_order); + } + + return (from_order != -1) && (from_order <= to_order); + } + else { + return 0; + } + } + /* NPY_NO_CASTING or NPY_EQUIV_CASTING was specified */ + else { + return 0; + } +} + diff --git a/numpy/core/src/multiarray/legacy_dtype_implementation.h b/numpy/core/src/multiarray/legacy_dtype_implementation.h new file mode 100644 index 000000000000..04f455cde0bd --- /dev/null +++ b/numpy/core/src/multiarray/legacy_dtype_implementation.h @@ -0,0 +1,8 @@ +#ifndef NUMPY_CORE_SRC_MULTIARRAY_LEGACY_DTYPE_IMPLEMENTATION_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_LEGACY_DTYPE_IMPLEMENTATION_H_ + +NPY_NO_EXPORT npy_bool +PyArray_LegacyCanCastTypeTo(PyArray_Descr *from, PyArray_Descr *to, + NPY_CASTING casting); + +#endif /* NUMPY_CORE_SRC_MULTIARRAY_LEGACY_DTYPE_IMPLEMENTATION_H_ */ diff --git a/numpy/core/src/multiarray/lowlevel_strided_loops.c.src b/numpy/core/src/multiarray/lowlevel_strided_loops.c.src index fa68af19a865..e313d244768d 100644 --- a/numpy/core/src/multiarray/lowlevel_strided_loops.c.src +++ b/numpy/core/src/multiarray/lowlevel_strided_loops.c.src @@ -9,8 +9,7 @@ */ #define PY_SSIZE_T_CLEAN -#include "Python.h" -#include "structmember.h" +#include <Python.h> #define NPY_NO_DEPRECATED_API NPY_API_VERSION #define _MULTIARRAYMODULE @@ -19,16 +18,9 @@ #include <numpy/halffloat.h> #include "lowlevel_strided_loops.h" - -/* used for some alignment checks */ -#define _ALIGN(type) offsetof(struct {char c; type v;}, v) -/* - * Disable harmless compiler warning "4116: unnamed type definition in - * parentheses" which is caused by the _ALIGN macro. - */ -#if defined(_MSC_VER) -#pragma warning(disable:4116) -#endif +#include "array_assign.h" +#include "array_method.h" +#include "usertypes.h" /* @@ -39,7 +31,7 @@ * instructions (16 byte). * So this flag can only be enabled if autovectorization is disabled. */ -#if NPY_CPU_HAVE_UNALIGNED_ACCESS +#if NPY_ALIGNMENT_REQUIRED # define NPY_USE_UNALIGNED_ACCESS 0 #else # define NPY_USE_UNALIGNED_ACCESS 0 @@ -92,7 +84,7 @@ /**begin repeat * #elsize = 1, 2, 4, 8, 16# * #elsize_half = 0, 1, 2, 4, 8# - * #type = npy_uint8, npy_uint16, npy_uint32, npy_uint64, npy_uint128# + * #type = npy_uint8, npy_uint16, npy_uint32, npy_uint64, npy_uint64# */ /**begin repeat1 * #oper = strided_to_strided, strided_to_contig, @@ -120,19 +112,28 @@ * if not it can decrease performance * tested to improve performance on intel xeon 5x/7x, core2duo, amd phenom x4 */ -static void +static int #if @is_aligned@ && @is_swap@ == 0 && @elsize@ <= NPY_SIZEOF_INTP NPY_GCC_UNROLL_LOOPS #endif -@prefix@_@oper@_size@elsize@(char *dst, npy_intp dst_stride, - char *src, npy_intp src_stride, - npy_intp N, npy_intp NPY_UNUSED(src_itemsize), - NpyAuxData *NPY_UNUSED(data)) +@prefix@_@oper@_size@elsize@( + PyArrayMethod_Context *context, char *const *args, + const npy_intp *dimensions, const npy_intp *strides, + NpyAuxData *NPY_UNUSED(auxdata)) { -#if @is_aligned@ && @elsize@ != 16 + npy_intp N = dimensions[0]; + char *src = args[0], *dst = args[1]; +#if !@src_contig@ + npy_intp src_stride = strides[0]; +#endif +#if !@dst_contig@ + npy_intp dst_stride = strides[1]; +#endif + +#if @is_aligned@ /* sanity check */ - assert(npy_is_aligned(dst, _ALIGN(@type@))); - assert(npy_is_aligned(src, _ALIGN(@type@))); + assert(N == 0 || npy_is_aligned(dst, _UINT_ALIGN(@type@))); + assert(N == 0 || npy_is_aligned(src, _UINT_ALIGN(@type@))); #endif /*printf("fn @prefix@_@oper@_size@elsize@\n");*/ while (N > 0) { @@ -181,6 +182,7 @@ static void --N; } + return 0; } #endif @@ -192,13 +194,18 @@ static void * but it profits from vectorization enabled with -O3 */ #if (@src_contig@ == 0) && @is_aligned@ -static NPY_GCC_OPT_3 void -@prefix@_@oper@_size@elsize@_srcstride0(char *dst, - npy_intp dst_stride, - char *src, npy_intp NPY_UNUSED(src_stride), - npy_intp N, npy_intp NPY_UNUSED(src_itemsize), - NpyAuxData *NPY_UNUSED(data)) +static NPY_GCC_OPT_3 int +@prefix@_@oper@_size@elsize@_srcstride0( + PyArrayMethod_Context *context, char *const *args, + const npy_intp *dimensions, const npy_intp *strides, + NpyAuxData *NPY_UNUSED(auxdata)) { + npy_intp N = dimensions[0]; + char *src = args[0], *dst = args[1]; +#if !@dst_contig@ + npy_intp dst_stride = strides[1]; +#endif + #if @elsize@ != 16 # if !(@elsize@ == 1 && @dst_contig@) @type@ temp; @@ -207,12 +214,12 @@ static NPY_GCC_OPT_3 void npy_uint64 temp0, temp1; #endif if (N == 0) { - return; + return 0; } #if @is_aligned@ && @elsize@ != 16 /* sanity check */ - assert(npy_is_aligned(dst, _ALIGN(@type@))); - assert(npy_is_aligned(src, _ALIGN(@type@))); + assert(N == 0 || npy_is_aligned(dst, _UINT_ALIGN(@type@))); + assert(N == 0 || npy_is_aligned(src, _UINT_ALIGN(@type@))); #endif #if @elsize@ == 1 && @dst_contig@ memset(dst, *src, N); @@ -248,6 +255,7 @@ static NPY_GCC_OPT_3 void --N; } #endif/* @elsize == 1 && @dst_contig@ -- else */ + return 0; } #endif/* (@src_contig@ == 0) && @is_aligned@ */ @@ -257,26 +265,42 @@ static NPY_GCC_OPT_3 void /**end repeat1**/ /**end repeat**/ -static void -_strided_to_strided(char *dst, npy_intp dst_stride, - char *src, npy_intp src_stride, - npy_intp N, npy_intp src_itemsize, - NpyAuxData *NPY_UNUSED(data)) +static int +_strided_to_strided( + PyArrayMethod_Context *context, char *const *args, + const npy_intp *dimensions, const npy_intp *strides, + NpyAuxData *NPY_UNUSED(data)) { + npy_intp N = dimensions[0]; + char *src = args[0], *dst = args[1]; + npy_intp src_stride = strides[0], dst_stride = strides[1]; + npy_intp src_itemsize = context->descriptors[0]->elsize; + while (N > 0) { memmove(dst, src, src_itemsize); dst += dst_stride; src += src_stride; --N; } + return 0; } -static void -_swap_strided_to_strided(char *dst, npy_intp dst_stride, - char *src, npy_intp src_stride, - npy_intp N, npy_intp src_itemsize, - NpyAuxData *NPY_UNUSED(data)) +/* + * NOTE: This function is currently unused. It would currently be used for + * builtin dtypes that have an elsize other than 2, 4, 8, or 16 bytes. + * Since unicode and complex swap differently, no such dtype exists. + */ +static int +_swap_strided_to_strided( + PyArrayMethod_Context *context, char *const *args, + const npy_intp *dimensions, const npy_intp *strides, + NpyAuxData *NPY_UNUSED(data)) { + npy_intp N = dimensions[0]; + char *src = args[0], *dst = args[1]; + npy_intp src_stride = strides[0], dst_stride = strides[1]; + npy_intp src_itemsize = context->descriptors[0]->elsize; + char *a, *b, c; while (N > 0) { @@ -294,14 +318,20 @@ _swap_strided_to_strided(char *dst, npy_intp dst_stride, src += src_stride; --N; } + return 0; } -static void -_swap_pair_strided_to_strided(char *dst, npy_intp dst_stride, - char *src, npy_intp src_stride, - npy_intp N, npy_intp src_itemsize, - NpyAuxData *NPY_UNUSED(data)) +static int +_swap_pair_strided_to_strided( + PyArrayMethod_Context *context, char *const *args, + const npy_intp *dimensions, const npy_intp *strides, + NpyAuxData *NPY_UNUSED(data)) { + npy_intp N = dimensions[0]; + char *src = args[0], *dst = args[1]; + npy_intp src_stride = strides[0], dst_stride = strides[1]; + npy_intp src_itemsize = context->descriptors[0]->elsize; + char *a, *b, c; npy_intp itemsize_half = src_itemsize / 2; @@ -329,19 +359,25 @@ _swap_pair_strided_to_strided(char *dst, npy_intp dst_stride, src += src_stride; --N; } + return 0; } -static void -_contig_to_contig(char *dst, npy_intp NPY_UNUSED(dst_stride), - char *src, npy_intp NPY_UNUSED(src_stride), - npy_intp N, npy_intp src_itemsize, - NpyAuxData *NPY_UNUSED(data)) +static int +_contig_to_contig( + PyArrayMethod_Context *context, char *const *args, + const npy_intp *dimensions, const npy_intp *NPY_UNUSED(strides), + NpyAuxData *NPY_UNUSED(data)) { + npy_intp N = dimensions[0]; + char *src = args[0], *dst = args[1]; + npy_intp src_itemsize = context->descriptors[0]->elsize; + memmove(dst, src, src_itemsize*N); + return 0; } -NPY_NO_EXPORT PyArray_StridedUnaryOp * +NPY_NO_EXPORT PyArrayMethod_StridedLoop * PyArray_GetStridedCopyFn(int aligned, npy_intp src_stride, npy_intp dst_stride, npy_intp itemsize) { @@ -427,32 +463,31 @@ PyArray_GetStridedCopyFn(int aligned, npy_intp src_stride, #if !NPY_USE_UNALIGNED_ACCESS } else { - /* contiguous dst */ - if (itemsize != 0 && dst_stride == itemsize) { - /* contiguous src */ - if (itemsize != 0 && src_stride == itemsize) { - return &_contig_to_contig; - } - /* general src */ - else { - switch (itemsize) { - case 1: - return &_aligned_strided_to_contig_size1; + if (itemsize != 0) { + if (dst_stride == itemsize) { + /* contiguous dst */ + if (src_stride == itemsize) { + /* contiguous src, dst */ + return &_contig_to_contig; + } + else { + /* general src */ + switch (itemsize) { + case 1: + return &_aligned_strided_to_contig_size1; /**begin repeat * #elsize = 2, 4, 8, 16# */ - case @elsize@: - return &_strided_to_contig_size@elsize@; + case @elsize@: + return &_strided_to_contig_size@elsize@; /**end repeat**/ + } } - } - return &_strided_to_strided; - } - /* general dst */ - else { - /* contiguous src */ - if (itemsize != 0 && src_stride == itemsize) { + return &_strided_to_strided; + } + else if (src_stride == itemsize) { + /* contiguous src, general dst */ switch (itemsize) { case 1: return &_aligned_contig_to_strided_size1; @@ -466,18 +501,18 @@ PyArray_GetStridedCopyFn(int aligned, npy_intp src_stride, return &_strided_to_strided; } - /* general src */ - else { - switch (itemsize) { - case 1: - return &_aligned_strided_to_strided_size1; + } + else { + /* general src, dst */ + switch (itemsize) { + case 1: + return &_aligned_strided_to_strided_size1; /**begin repeat * #elsize = 2, 4, 8, 16# */ - case @elsize@: - return &_strided_to_strided_size@elsize@; + case @elsize@: + return &_strided_to_strided_size@elsize@; /**end repeat**/ - } } } } @@ -496,7 +531,7 @@ PyArray_GetStridedCopyFn(int aligned, npy_intp src_stride, * #not_pair = 1, 0# */ -NPY_NO_EXPORT PyArray_StridedUnaryOp * +NPY_NO_EXPORT PyArrayMethod_StridedLoop * @function@(int aligned, npy_intp src_stride, npy_intp dst_stride, npy_intp itemsize) { @@ -602,7 +637,7 @@ NPY_NO_EXPORT PyArray_StridedUnaryOp * /* contiguous dst */ if (itemsize != 0 && dst_stride == itemsize) { /* contiguous src */ - if (itemsize != 0 && src_stride == itemsize) { + if (src_stride == itemsize) { switch (itemsize) { /**begin repeat1 * #elsize = 2, 4, 8, 16# @@ -784,6 +819,10 @@ NPY_NO_EXPORT PyArray_StridedUnaryOp * # define _CONVERT_FN(x) npy_floatbits_to_halfbits(x) # elif @is_double1@ # define _CONVERT_FN(x) npy_doublebits_to_halfbits(x) +# elif @is_half1@ +# define _CONVERT_FN(x) (x) +# elif @is_bool1@ +# define _CONVERT_FN(x) npy_float_to_half((float)(x!=0)) # else # define _CONVERT_FN(x) npy_float_to_half((float)x) # endif @@ -798,13 +837,18 @@ NPY_NO_EXPORT PyArray_StridedUnaryOp * #endif -static NPY_GCC_OPT_3 void +static NPY_GCC_OPT_3 int @prefix@_cast_@name1@_to_@name2@( - char *dst, npy_intp dst_stride, - char *src, npy_intp src_stride, - npy_intp N, npy_intp NPY_UNUSED(src_itemsize), - NpyAuxData *NPY_UNUSED(data)) + PyArrayMethod_Context *context, char *const *args, + const npy_intp *dimensions, const npy_intp *strides, + NpyAuxData *NPY_UNUSED(data)) { + npy_intp N = dimensions[0]; + char *src = args[0], *dst = args[1]; +#if !@contig@ + npy_intp src_stride = strides[0], dst_stride = strides[1]; +#endif + #if @is_complex1@ _TYPE1 src_value[2]; #elif !@aligned@ @@ -818,12 +862,8 @@ static NPY_GCC_OPT_3 void #if @aligned@ /* sanity check */ -# if !@is_complex1@ - assert(npy_is_aligned(src, _ALIGN(_TYPE1))); -# endif -# if !@is_complex2@ - assert(npy_is_aligned(dst, _ALIGN(_TYPE2))); -# endif + assert(N == 0 || npy_is_aligned(src, _ALIGN(_TYPE1))); + assert(N == 0 || npy_is_aligned(dst, _ALIGN(_TYPE2))); #endif /*printf("@prefix@_cast_@name1@_to_@name2@\n");*/ @@ -888,6 +928,7 @@ static NPY_GCC_OPT_3 void src += src_stride; #endif } + return 0; } #undef _CONVERT_FN @@ -902,7 +943,7 @@ static NPY_GCC_OPT_3 void /**end repeat**/ -NPY_NO_EXPORT PyArray_StridedUnaryOp * +NPY_NO_EXPORT PyArrayMethod_StridedLoop * PyArray_GetStridedNumericCastFn(int aligned, npy_intp src_stride, npy_intp dst_stride, int src_type_num, int dst_type_num) @@ -989,12 +1030,11 @@ PyArray_GetStridedNumericCastFn(int aligned, npy_intp src_stride, NPY_NO_EXPORT npy_intp PyArray_TransferNDimToStrided(npy_intp ndim, char *dst, npy_intp dst_stride, - char *src, npy_intp *src_strides, npy_intp src_strides_inc, - npy_intp *coords, npy_intp coords_inc, - npy_intp *shape, npy_intp shape_inc, + char *src, npy_intp const *src_strides, npy_intp src_strides_inc, + npy_intp const *coords, npy_intp coords_inc, + npy_intp const *shape, npy_intp shape_inc, npy_intp count, npy_intp src_itemsize, - PyArray_StridedUnaryOp *stransfer, - NpyAuxData *data) + NPY_cast_info *cast_info) { npy_intp i, M, N, coord0, shape0, src_stride0, coord1, shape1, src_stride1; @@ -1003,11 +1043,20 @@ PyArray_TransferNDimToStrided(npy_intp ndim, shape0 = shape[0]; src_stride0 = src_strides[0]; N = shape0 - coord0; + + npy_intp strides[2] = {src_stride0, dst_stride}; + + char *args[2] = {src, dst}; if (N >= count) { - stransfer(dst, dst_stride, src, src_stride0, count, src_itemsize, data); - return 0; + return cast_info->func(&cast_info->context, + args, &count, strides, cast_info->auxdata); + } + int res = cast_info->func(&cast_info->context, + args, &N, strides, cast_info->auxdata); + + if (res < 0) { + return -1; } - stransfer(dst, dst_stride, src, src_stride0, N, src_itemsize, data); count -= N; /* If it's 1-dimensional, there's no more to copy */ @@ -1026,14 +1075,17 @@ PyArray_TransferNDimToStrided(npy_intp ndim, M = (shape1 - coord1 - 1); N = shape0*M; for (i = 0; i < M; ++i) { + args[0] = src; args[1] = dst; if (shape0 >= count) { - stransfer(dst, dst_stride, src, src_stride0, - count, src_itemsize, data); - return 0; + return cast_info->func(&cast_info->context, + args, &count, strides, cast_info->auxdata); } else { - stransfer(dst, dst_stride, src, src_stride0, - shape0, src_itemsize, data); + res = cast_info->func(&cast_info->context, + args, &shape0, strides, cast_info->auxdata); + if (res < 0) { + return -1; + } } count -= shape0; src += src_stride1; @@ -1087,14 +1139,17 @@ PyArray_TransferNDimToStrided(npy_intp ndim, /* A loop for dimensions 0 and 1 */ for (i = 0; i < shape1; ++i) { + args[0] = src; args[1] = dst; if (shape0 >= count) { - stransfer(dst, dst_stride, src, src_stride0, - count, src_itemsize, data); - return 0; + return cast_info->func(&cast_info->context, + args, &count, strides, cast_info->auxdata); } else { - stransfer(dst, dst_stride, src, src_stride0, - shape0, src_itemsize, data); + res = cast_info->func(&cast_info->context, + args, &shape0, strides, cast_info->auxdata); + if (res < 0) { + return -1; + } } count -= shape0; src += src_stride1; @@ -1107,13 +1162,12 @@ PyArray_TransferNDimToStrided(npy_intp ndim, /* See documentation of arguments in lowlevel_strided_loops.h */ NPY_NO_EXPORT npy_intp PyArray_TransferStridedToNDim(npy_intp ndim, - char *dst, npy_intp *dst_strides, npy_intp dst_strides_inc, + char *dst, npy_intp const *dst_strides, npy_intp dst_strides_inc, char *src, npy_intp src_stride, - npy_intp *coords, npy_intp coords_inc, - npy_intp *shape, npy_intp shape_inc, + npy_intp const *coords, npy_intp coords_inc, + npy_intp const *shape, npy_intp shape_inc, npy_intp count, npy_intp src_itemsize, - PyArray_StridedUnaryOp *stransfer, - NpyAuxData *data) + NPY_cast_info *cast_info) { npy_intp i, M, N, coord0, shape0, dst_stride0, coord1, shape1, dst_stride1; @@ -1122,11 +1176,19 @@ PyArray_TransferStridedToNDim(npy_intp ndim, shape0 = shape[0]; dst_stride0 = dst_strides[0]; N = shape0 - coord0; + + npy_intp strides[2] = {src_stride, dst_stride0}; + + char *args[2] = {src, dst}; if (N >= count) { - stransfer(dst, dst_stride0, src, src_stride, count, src_itemsize, data); - return 0; + return cast_info->func(&cast_info->context, + args, &count, strides, cast_info->auxdata); + } + int res = cast_info->func(&cast_info->context, + args, &N, strides, cast_info->auxdata); + if (res < 0) { + return -1; } - stransfer(dst, dst_stride0, src, src_stride, N, src_itemsize, data); count -= N; /* If it's 1-dimensional, there's no more to copy */ @@ -1145,14 +1207,17 @@ PyArray_TransferStridedToNDim(npy_intp ndim, M = (shape1 - coord1 - 1); N = shape0*M; for (i = 0; i < M; ++i) { + args[0] = src; args[1] = dst; if (shape0 >= count) { - stransfer(dst, dst_stride0, src, src_stride, - count, src_itemsize, data); - return 0; + return cast_info->func(&cast_info->context, + args, &count, strides, cast_info->auxdata); } else { - stransfer(dst, dst_stride0, src, src_stride, - shape0, src_itemsize, data); + res = cast_info->func(&cast_info->context, + args, &shape0, strides, cast_info->auxdata); + if (res < 0) { + return -1; + } } count -= shape0; dst += dst_stride1; @@ -1206,14 +1271,17 @@ PyArray_TransferStridedToNDim(npy_intp ndim, /* A loop for dimensions 0 and 1 */ for (i = 0; i < shape1; ++i) { + args[0] = src; args[1] = dst; if (shape0 >= count) { - stransfer(dst, dst_stride0, src, src_stride, - count, src_itemsize, data); - return 0; + return cast_info->func(&cast_info->context, + args, &count, strides, cast_info->auxdata); } else { - stransfer(dst, dst_stride0, src, src_stride, - shape0, src_itemsize, data); + res = cast_info->func(&cast_info->context, + args, &shape0, strides, cast_info->auxdata); + if (res < 0) { + return -1; + } } count -= shape0; dst += dst_stride1; @@ -1226,33 +1294,36 @@ PyArray_TransferStridedToNDim(npy_intp ndim, /* See documentation of arguments in lowlevel_strided_loops.h */ NPY_NO_EXPORT npy_intp PyArray_TransferMaskedStridedToNDim(npy_intp ndim, - char *dst, npy_intp *dst_strides, npy_intp dst_strides_inc, + char *dst, npy_intp const *dst_strides, npy_intp dst_strides_inc, char *src, npy_intp src_stride, npy_uint8 *mask, npy_intp mask_stride, - npy_intp *coords, npy_intp coords_inc, - npy_intp *shape, npy_intp shape_inc, + npy_intp const *coords, npy_intp coords_inc, + npy_intp const *shape, npy_intp shape_inc, npy_intp count, npy_intp src_itemsize, - PyArray_MaskedStridedUnaryOp *stransfer, - NpyAuxData *data) + NPY_cast_info *cast_info) { npy_intp i, M, N, coord0, shape0, dst_stride0, coord1, shape1, dst_stride1; + PyArray_MaskedStridedUnaryOp *stransfer = + (PyArray_MaskedStridedUnaryOp*)cast_info->func; /* Finish off dimension 0 */ coord0 = coords[0]; shape0 = shape[0]; dst_stride0 = dst_strides[0]; N = shape0 - coord0; + + npy_intp strides[2] = {src_stride, dst_stride0}; + + char *args[2] = {src, dst}; if (N >= count) { - stransfer(dst, dst_stride0, - src, src_stride, - mask, mask_stride, - count, src_itemsize, data); - return 0; + return stransfer(&cast_info->context, + args, &count, strides, mask, mask_stride, cast_info->auxdata); + } + int res = stransfer(&cast_info->context, + args, &N, strides, mask, mask_stride, cast_info->auxdata); + if (res < 0) { + return -1; } - stransfer(dst, dst_stride0, - src, src_stride, - mask, mask_stride, - N, src_itemsize, data); count -= N; /* If it's 1-dimensional, there's no more to copy */ @@ -1272,18 +1343,19 @@ PyArray_TransferMaskedStridedToNDim(npy_intp ndim, M = (shape1 - coord1 - 1); N = shape0*M; for (i = 0; i < M; ++i) { + args[0] = src; args[1] = dst; if (shape0 >= count) { - stransfer(dst, dst_stride0, - src, src_stride, - mask, mask_stride, - count, src_itemsize, data); - return 0; + return stransfer(&cast_info->context, + args, &count, strides, + mask, mask_stride, cast_info->auxdata); } else { - stransfer(dst, dst_stride0, - src, src_stride, - mask, mask_stride, - shape0, src_itemsize, data); + int res = stransfer(&cast_info->context, + args, &shape0, strides, + mask, mask_stride, cast_info->auxdata); + if (res < 0) { + return -1; + } } count -= shape0; dst += dst_stride1; @@ -1338,18 +1410,19 @@ PyArray_TransferMaskedStridedToNDim(npy_intp ndim, /* A loop for dimensions 0 and 1 */ for (i = 0; i < shape1; ++i) { + args[0] = src; args[1] = dst; if (shape0 >= count) { - stransfer(dst, dst_stride0, - src, src_stride, - mask, mask_stride, - count, src_itemsize, data); - return 0; + return stransfer(&cast_info->context, + args, &count, strides, mask, + mask_stride, cast_info->auxdata); } else { - stransfer(dst, dst_stride0, - src, src_stride, - mask, mask_stride, - shape0, src_itemsize, data); + int res = stransfer(&cast_info->context, + args, &shape0, strides, + mask, mask_stride, cast_info->auxdata); + if (res < 0) { + return -1; + } } count -= shape0; dst += dst_stride1; @@ -1371,7 +1444,7 @@ PyArray_TransferMaskedStridedToNDim(npy_intp ndim, */ /* - * Advanded indexing iteration of arrays when there is a single indexing + * Advanced indexing iteration of arrays when there is a single indexing * array which has the same memory order as the value array and both * can be trivially iterated (single stride, aligned, no casting necessary). */ @@ -1385,7 +1458,7 @@ mapiter_trivial_@name@(PyArrayObject *self, PyArrayObject *ind, npy_intp itersize; - int is_aligned = PyArray_ISALIGNED(self) && PyArray_ISALIGNED(result); + int is_aligned = IsUintAligned(self) && IsUintAligned(result); int needs_api = PyDataType_REFCHK(PyArray_DESCR(self)); PyArray_CopySwapFunc *copyswap = PyArray_DESCR(self)->f->copyswap; @@ -1405,7 +1478,7 @@ mapiter_trivial_@name@(PyArrayObject *self, PyArrayObject *ind, /* Check the indices beforehand */ while (itersize--) { npy_intp indval = *((npy_intp*)ind_ptr); - if (check_and_adjust_index(&indval, fancy_dim, 1, _save) < 0 ) { + if (check_and_adjust_index(&indval, fancy_dim, 0, _save) < 0 ) { return -1; } ind_ptr += ind_stride; @@ -1435,9 +1508,9 @@ mapiter_trivial_@name@(PyArrayObject *self, PyArrayObject *ind, while (itersize--) { char * self_ptr; npy_intp indval = *((npy_intp*)ind_ptr); - assert(npy_is_aligned(ind_ptr, _ALIGN(npy_intp))); + assert(npy_is_aligned(ind_ptr, _UINT_ALIGN(npy_intp))); #if @isget@ - if (check_and_adjust_index(&indval, fancy_dim, 1, _save) < 0 ) { + if (check_and_adjust_index(&indval, fancy_dim, 0, _save) < 0 ) { return -1; } #else @@ -1449,8 +1522,8 @@ mapiter_trivial_@name@(PyArrayObject *self, PyArrayObject *ind, #if @isget@ #if @elsize@ - assert(npy_is_aligned(result_ptr, _ALIGN(@copytype@))); - assert(npy_is_aligned(self_ptr, _ALIGN(@copytype@))); + assert(npy_is_aligned(result_ptr, _UINT_ALIGN(@copytype@))); + assert(npy_is_aligned(self_ptr, _UINT_ALIGN(@copytype@))); *(@copytype@ *)result_ptr = *(@copytype@ *)self_ptr; #else copyswap(result_ptr, self_ptr, 0, self); @@ -1458,8 +1531,8 @@ mapiter_trivial_@name@(PyArrayObject *self, PyArrayObject *ind, #else /* !@isget@ */ #if @elsize@ - assert(npy_is_aligned(result_ptr, _ALIGN(@copytype@))); - assert(npy_is_aligned(self_ptr, _ALIGN(@copytype@))); + assert(npy_is_aligned(result_ptr, _UINT_ALIGN(@copytype@))); + assert(npy_is_aligned(self_ptr, _UINT_ALIGN(@copytype@))); *(@copytype@ *)self_ptr = *(@copytype@ *)result_ptr; #else copyswap(self_ptr, result_ptr, 0, self); @@ -1518,7 +1591,7 @@ mapiter_@name@(PyArrayMapIterObject *mit) * could also check extra_op is buffered, but it should rarely matter. */ - is_aligned = PyArray_ISALIGNED(array) && PyArray_ISALIGNED(mit->extra_op); + is_aligned = IsUintAligned(array) && IsUintAligned(mit->extra_op); if (mit->size == 0) { return 0; @@ -1581,7 +1654,7 @@ mapiter_@name@(PyArrayMapIterObject *mit) for (i=0; i < @numiter@; i++) { npy_intp indval = *((npy_intp*)outer_ptrs[i]); assert(npy_is_aligned(outer_ptrs[i], - _ALIGN(npy_intp))); + _UINT_ALIGN(npy_intp))); #if @isget@ && @one_iter@ if (check_and_adjust_index(&indval, fancy_dims[i], @@ -1601,16 +1674,20 @@ mapiter_@name@(PyArrayMapIterObject *mit) #if @isget@ #if @elsize@ - assert(npy_is_aligned(outer_ptrs[i], _ALIGN(@copytype@))); - assert(npy_is_aligned(self_ptr, _ALIGN(@copytype@))); + assert(npy_is_aligned(outer_ptrs[i], + _UINT_ALIGN(@copytype@))); + assert(npy_is_aligned(self_ptr, + _UINT_ALIGN(@copytype@))); *(@copytype@ *)(outer_ptrs[i]) = *(@copytype@ *)self_ptr; #else copyswap(outer_ptrs[i], self_ptr, 0, array); #endif #else /* !@isget@ */ #if @elsize@ - assert(npy_is_aligned(outer_ptrs[i], _ALIGN(@copytype@))); - assert(npy_is_aligned(self_ptr, _ALIGN(@copytype@))); + assert(npy_is_aligned(outer_ptrs[i], + _UINT_ALIGN(@copytype@))); + assert(npy_is_aligned(self_ptr, + _UINT_ALIGN(@copytype@))); *(@copytype@ *)self_ptr = *(@copytype@ *)(outer_ptrs[i]); #else copyswap(self_ptr, outer_ptrs[i], 0, array); @@ -1639,22 +1716,15 @@ mapiter_@name@(PyArrayMapIterObject *mit) npy_intp reset_offsets[2] = {0, 0}; /* Use strided transfer functions for the inner loop */ - PyArray_StridedUnaryOp *stransfer = NULL; - NpyAuxData *transferdata = NULL; npy_intp fixed_strides[2]; -#if @isget@ - npy_intp src_itemsize = PyArray_ITEMSIZE(array); -#else - npy_intp src_itemsize = PyArray_ITEMSIZE(mit->extra_op); -#endif - /* * Get a dtype transfer function, since there are no * buffers, this is safe. */ NpyIter_GetInnerFixedStrideArray(mit->subspace_iter, fixed_strides); + NPY_cast_info cast_info; if (PyArray_GetDTypeTransferFunction(is_aligned, #if @isget@ fixed_strides[0], fixed_strides[1], @@ -1664,7 +1734,7 @@ mapiter_@name@(PyArrayMapIterObject *mit) PyArray_DESCR(mit->extra_op), PyArray_DESCR(array), #endif 0, - &stransfer, &transferdata, + &cast_info, &needs_api) != NPY_SUCCEED) { return -1; } @@ -1701,7 +1771,7 @@ mapiter_@name@(PyArrayMapIterObject *mit) #if @isget@ && @one_iter@ if (check_and_adjust_index(&indval, fancy_dims[i], iteraxis, _save) < 0 ) { - NPY_AUXDATA_FREE(transferdata); + NPY_cast_info_xfree(&cast_info); return -1; } #else @@ -1733,7 +1803,7 @@ mapiter_@name@(PyArrayMapIterObject *mit) &errmsg)) { NPY_END_THREADS; PyErr_SetString(PyExc_ValueError, errmsg); - NPY_AUXDATA_FREE(transferdata); + NPY_cast_info_xfree(&cast_info); return -1; } if (is_subiter_trivial != 0) { @@ -1763,7 +1833,7 @@ mapiter_@name@(PyArrayMapIterObject *mit) * not at all... */ if (needs_api && PyErr_Occurred()) { - NPY_AUXDATA_FREE(transferdata); + NPY_cast_info_xfree(&cast_info); return -1; } #endif @@ -1771,13 +1841,23 @@ mapiter_@name@(PyArrayMapIterObject *mit) do { #if @isget@ - stransfer(subspace_ptrs[1], subspace_strides[1], - subspace_ptrs[0], subspace_strides[0], - *counter, src_itemsize, transferdata); + if (NPY_UNLIKELY(cast_info.func(&cast_info.context, + subspace_ptrs, counter, subspace_strides, + cast_info.auxdata) < 0)) { + NPY_END_THREADS; + NPY_cast_info_xfree(&cast_info); + return -1; + } #else - stransfer(subspace_ptrs[0], subspace_strides[0], - subspace_ptrs[1], subspace_strides[1], - *counter, src_itemsize, transferdata); + /* The operand order is reversed here */ + char *args[2] = {subspace_ptrs[1], subspace_ptrs[0]}; + npy_intp strides[2] = {subspace_strides[1], subspace_strides[0]}; + if (NPY_UNLIKELY(cast_info.func(&cast_info.context, + args, counter, strides, cast_info.auxdata) < 0)) { + NPY_END_THREADS; + NPY_cast_info_xfree(&cast_info); + return -1; + } #endif } while (mit->subspace_next(mit->subspace_iter)); @@ -1787,7 +1867,7 @@ mapiter_@name@(PyArrayMapIterObject *mit) } /**end repeat1**/ - NPY_AUXDATA_FREE(transferdata); + NPY_cast_info_xfree(&cast_info); } return 0; } diff --git a/numpy/core/src/multiarray/mapping.c b/numpy/core/src/multiarray/mapping.c index 82a0683f9e7d..014a863d5471 100644 --- a/numpy/core/src/multiarray/mapping.c +++ b/numpy/core/src/multiarray/mapping.c @@ -1,10 +1,10 @@ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE + #define PY_SSIZE_T_CLEAN #include <Python.h> -#include "structmember.h" +#include <structmember.h> -/*#include <stdio.h>*/ -#define NPY_NO_DEPRECATED_API NPY_API_VERSION -#define _MULTIARRAYMODULE #include "numpy/arrayobject.h" #include "arrayobject.h" @@ -15,11 +15,14 @@ #include "common.h" #include "ctors.h" +#include "descriptor.h" #include "iterators.h" #include "mapping.h" #include "lowlevel_strided_loops.h" #include "item_selection.h" #include "mem_overlap.h" +#include "array_assign.h" +#include "array_coercion.h" #define HAS_INTEGER 1 @@ -60,15 +63,61 @@ array_length(PyArrayObject *self) /* -------------------------------------------------------------- */ + +/* + * Helper for `PyArray_MapIterSwapAxes` (and related), see its documentation. + */ +static void +_get_transpose(int fancy_ndim, int consec, int ndim, int getmap, npy_intp *dims) +{ + /* + * For getting the array the tuple for transpose is + * (n1,...,n1+n2-1,0,...,n1-1,n1+n2,...,n3-1) + * n1 is the number of dimensions of the broadcast index array + * n2 is the number of dimensions skipped at the start + * n3 is the number of dimensions of the result + */ + + /* + * For setting the array the tuple for transpose is + * (n2,...,n1+n2-1,0,...,n2-1,n1+n2,...n3-1) + */ + int n1 = fancy_ndim; + int n2 = consec; /* axes to insert at */ + int n3 = ndim; + + /* use n1 as the boundary if getting but n2 if setting */ + int bnd = getmap ? n1 : n2; + int val = bnd; + int i = 0; + while (val < n1 + n2) { + dims[i++] = val++; + } + val = 0; + while (val < bnd) { + dims[i++] = val++; + } + val = n1 + n2; + while (val < n3) { + dims[i++] = val++; + } +} + + /*NUMPY_API * + * Swap the axes to or from their inserted form. MapIter always puts the + * advanced (array) indices first in the iteration. But if they are + * consecutive, will insert/transpose them back before returning. + * This is stored as `mit->consec != 0` (the place where they are inserted) + * For assignments, the opposite happens: The values to be assigned are + * transposed (getmap=1 instead of getmap=0). `getmap=0` and `getmap=1` + * undo the other operation. */ NPY_NO_EXPORT void PyArray_MapIterSwapAxes(PyArrayMapIterObject *mit, PyArrayObject **ret, int getmap) { PyObject *new; - int n1, n2, n3, val, bnd; - int i; PyArray_Dims permute; npy_intp d[NPY_MAXDIMS]; PyArrayObject *arr; @@ -82,10 +131,10 @@ PyArray_MapIterSwapAxes(PyArrayMapIterObject *mit, PyArrayObject **ret, int getm */ arr = *ret; if (PyArray_NDIM(arr) != mit->nd) { - for (i = 1; i <= PyArray_NDIM(arr); i++) { + for (int i = 1; i <= PyArray_NDIM(arr); i++) { permute.ptr[mit->nd-i] = PyArray_DIMS(arr)[PyArray_NDIM(arr)-i]; } - for (i = 0; i < mit->nd-PyArray_NDIM(arr); i++) { + for (int i = 0; i < mit->nd-PyArray_NDIM(arr); i++) { permute.ptr[i] = 1; } new = PyArray_Newshape(arr, &permute, NPY_ANYORDER); @@ -96,44 +145,8 @@ PyArray_MapIterSwapAxes(PyArrayMapIterObject *mit, PyArrayObject **ret, int getm } } - /* - * Setting and getting need to have different permutations. - * On the get we are permuting the returned object, but on - * setting we are permuting the object-to-be-set. - * The set permutation is the inverse of the get permutation. - */ - - /* - * For getting the array the tuple for transpose is - * (n1,...,n1+n2-1,0,...,n1-1,n1+n2,...,n3-1) - * n1 is the number of dimensions of the broadcast index array - * n2 is the number of dimensions skipped at the start - * n3 is the number of dimensions of the result - */ - - /* - * For setting the array the tuple for transpose is - * (n2,...,n1+n2-1,0,...,n2-1,n1+n2,...n3-1) - */ - n1 = mit->nd_fancy; - n2 = mit->consec; /* axes to insert at */ - n3 = mit->nd; + _get_transpose(mit->nd_fancy, mit->consec, mit->nd, getmap, permute.ptr); - /* use n1 as the boundary if getting but n2 if setting */ - bnd = getmap ? n1 : n2; - val = bnd; - i = 0; - while (val < n1 + n2) { - permute.ptr[i++] = val++; - } - val = 0; - while (val < bnd) { - permute.ptr[i++] = val++; - } - val = n1 + n2; - while (val < n3) { - permute.ptr[i++] = val++; - } new = PyArray_Transpose(*ret, &permute); Py_DECREF(*ret); *ret = (PyArrayObject *)new; @@ -174,7 +187,7 @@ unpack_tuple(PyTupleObject *index, PyObject **result, npy_intp result_n) /* Unpack a single scalar index, taking a new reference to match unpack_tuple */ static NPY_INLINE npy_intp -unpack_scalar(PyObject *index, PyObject **result, npy_intp result_n) +unpack_scalar(PyObject *index, PyObject **result, npy_intp NPY_UNUSED(result_n)) { Py_INCREF(index); result[0] = index; @@ -225,15 +238,12 @@ unpack_indices(PyObject *index, PyObject **result, npy_intp result_n) /* Obvious single-entry cases */ if (0 /* to aid macros below */ -#if !defined(NPY_PY3K) - || PyInt_CheckExact(index) -#else || PyLong_CheckExact(index) -#endif || index == Py_None || PySlice_Check(index) || PyArray_Check(index) - || !PySequence_Check(index)) { + || !PySequence_Check(index) + || PyUnicode_Check(index)) { return unpack_scalar(index, result, result_n); } @@ -478,11 +488,7 @@ prepare_index(PyArrayObject *self, PyObject *index, * * Check for integers first, purely for performance */ -#if !defined(NPY_PY3K) - if (PyInt_CheckExact(obj) || !PyArray_Check(obj)) { -#else if (PyLong_CheckExact(obj) || !PyArray_Check(obj)) { -#endif npy_intp ind = PyArray_PyIntAsIntp(obj); if (error_converting(ind)) { @@ -543,22 +549,22 @@ prepare_index(PyArrayObject *self, PyObject *index, /* * There are two types of boolean indices (which are equivalent, * for the most part though). A single boolean index of matching - * dimensionality and size is a boolean index. - * If this is not the case, it is instead expanded into (multiple) - * integer array indices. + * shape is a boolean index. If this is not the case, it is + * instead expanded into (multiple) integer array indices. */ PyArrayObject *nonzero_result[NPY_MAXDIMS]; if ((index_ndim == 1) && allow_boolean) { /* - * If ndim and size match, this can be optimized as a single - * boolean index. The size check is necessary only to support - * old non-matching sizes by using fancy indexing instead. - * The reason for that is that fancy indexing uses nonzero, - * and only the result of nonzero is checked for legality. + * If shapes match exactly, this can be optimized as a single + * boolean index. When the dimensions are identical but the shapes are not, + * this is always an error. The check ensures that these errors are raised + * and match those of the generic path. */ if ((PyArray_NDIM(arr) == PyArray_NDIM(self)) - && PyArray_SIZE(arr) == PyArray_SIZE(self)) { + && PyArray_CompareLists(PyArray_DIMS(arr), + PyArray_DIMS(self), + PyArray_NDIM(arr))) { index_type = HAS_BOOL; indices[curr_idx].type = HAS_BOOL; @@ -609,9 +615,9 @@ prepare_index(PyArrayObject *self, PyObject *index, /* Convert the boolean array into multiple integer ones */ n = _nonzero_indices((PyObject *)arr, nonzero_result); - Py_DECREF(arr); if (n < 0) { + Py_DECREF(arr); goto failed_building_indices; } @@ -622,6 +628,7 @@ prepare_index(PyArrayObject *self, PyObject *index, for (i=0; i < n; i++) { Py_DECREF(nonzero_result[i]); } + Py_DECREF(arr); goto failed_building_indices; } @@ -635,6 +642,7 @@ prepare_index(PyArrayObject *self, PyObject *index, used_ndim += 1; curr_idx += 1; } + Py_DECREF(arr); /* All added indices have 1 dimension */ if (fancy_ndim < 1) { @@ -708,30 +716,33 @@ prepare_index(PyArrayObject *self, PyObject *index, * to find the ellipsis value or append an ellipsis if necessary. */ if (used_ndim < PyArray_NDIM(self)) { - if (index_type & HAS_ELLIPSIS) { - indices[ellipsis_pos].value = PyArray_NDIM(self) - used_ndim; - used_ndim = PyArray_NDIM(self); - new_ndim += indices[ellipsis_pos].value; - } - else { - /* - * There is no ellipsis yet, but it is not a full index - * so we append an ellipsis to the end. - */ - index_type |= HAS_ELLIPSIS; - indices[curr_idx].object = NULL; - indices[curr_idx].type = HAS_ELLIPSIS; - indices[curr_idx].value = PyArray_NDIM(self) - used_ndim; - ellipsis_pos = curr_idx; - - used_ndim = PyArray_NDIM(self); - new_ndim += indices[curr_idx].value; - curr_idx += 1; - } + if (index_type & HAS_ELLIPSIS) { + indices[ellipsis_pos].value = PyArray_NDIM(self) - used_ndim; + used_ndim = PyArray_NDIM(self); + new_ndim += indices[ellipsis_pos].value; + } + else { + /* + * There is no ellipsis yet, but it is not a full index + * so we append an ellipsis to the end. + */ + index_type |= HAS_ELLIPSIS; + indices[curr_idx].object = NULL; + indices[curr_idx].type = HAS_ELLIPSIS; + indices[curr_idx].value = PyArray_NDIM(self) - used_ndim; + ellipsis_pos = curr_idx; + + used_ndim = PyArray_NDIM(self); + new_ndim += indices[curr_idx].value; + curr_idx += 1; + } } else if (used_ndim > PyArray_NDIM(self)) { - PyErr_SetString(PyExc_IndexError, - "too many indices for array"); + PyErr_Format(PyExc_IndexError, + "too many indices for array: " + "array is %d-dimensional, but %d were indexed", + PyArray_NDIM(self), + used_ndim); goto failed_building_indices; } else if (index_ndim == 0) { @@ -945,9 +956,9 @@ get_view_from_index(PyArrayObject *self, PyArrayObject **view, } break; case HAS_SLICE: - if (NpySlice_GetIndicesEx(indices[i].object, - PyArray_DIMS(self)[orig_dim], - &start, &stop, &step, &n_steps) < 0) { + if (PySlice_GetIndicesEx(indices[i].object, + PyArray_DIMS(self)[orig_dim], + &start, &stop, &step, &n_steps) < 0) { return -1; } if (n_steps <= 0) { @@ -1036,8 +1047,6 @@ array_boolean_subscript(PyArrayObject *self, PyArrayObject *op[2] = {self, bmask}; npy_uint32 flags, op_flags[2]; npy_intp fixed_strides[3]; - PyArray_StridedUnaryOp *stransfer = NULL; - NpyAuxData *transferdata = NULL; NpyIter_IterNextFunc *iternext; npy_intp innersize, *innerstrides; @@ -1062,11 +1071,13 @@ array_boolean_subscript(PyArrayObject *self, /* Get a dtype transfer function */ NpyIter_GetInnerFixedStrideArray(iter, fixed_strides); - if (PyArray_GetDTypeTransferFunction(PyArray_ISALIGNED(self), + NPY_cast_info cast_info; + if (PyArray_GetDTypeTransferFunction( + IsUintAligned(self) && IsAligned(self), fixed_strides[0], itemsize, dtype, dtype, 0, - &stransfer, &transferdata, + &cast_info, &needs_api) != NPY_SUCCEED) { Py_DECREF(ret); NpyIter_Deallocate(iter); @@ -1078,7 +1089,7 @@ array_boolean_subscript(PyArrayObject *self, if (iternext == NULL) { Py_DECREF(ret); NpyIter_Deallocate(iter); - NPY_AUXDATA_FREE(transferdata); + NPY_cast_info_xfree(&cast_info); return NULL; } @@ -1089,6 +1100,9 @@ array_boolean_subscript(PyArrayObject *self, self_stride = innerstrides[0]; bmask_stride = innerstrides[1]; + npy_intp strides[2] = {self_stride, itemsize}; + + int res = 0; do { innersize = *NpyIter_GetInnerLoopSizePtr(iter); self_data = dataptrs[0]; @@ -1103,8 +1117,12 @@ array_boolean_subscript(PyArrayObject *self, /* Process unmasked values */ bmask_data = npy_memchr(bmask_data, 0, bmask_stride, innersize, &subloopsize, 0); - stransfer(ret_data, itemsize, self_data, self_stride, - subloopsize, itemsize, transferdata); + char *args[2] = {self_data, ret_data}; + res = cast_info.func(&cast_info.context, + args, &subloopsize, strides, cast_info.auxdata); + if (res < 0) { + break; + } innersize -= subloopsize; self_data += subloopsize * self_stride; ret_data += subloopsize * itemsize; @@ -1113,8 +1131,15 @@ array_boolean_subscript(PyArrayObject *self, NPY_END_THREADS; - NpyIter_Deallocate(iter); - NPY_AUXDATA_FREE(transferdata); + if (!NpyIter_Deallocate(iter)) { + res = -1; + } + NPY_cast_info_xfree(&cast_info); + if (res < 0) { + /* Should be practically impossible, since there is no cast */ + Py_DECREF(ret); + return NULL; + } } if (!PyArray_CheckExact(self)) { @@ -1124,10 +1149,10 @@ array_boolean_subscript(PyArrayObject *self, ret = (PyArrayObject *)PyArray_NewFromDescrAndBase( Py_TYPE(self), dtype, 1, &size, PyArray_STRIDES(ret), PyArray_BYTES(ret), - PyArray_FLAGS(self), (PyObject *)self, (PyObject *)self); + PyArray_FLAGS(self), (PyObject *)self, (PyObject *)tmp); + Py_DECREF(tmp); if (ret == NULL) { - Py_DECREF(tmp); return NULL; } } @@ -1151,7 +1176,7 @@ NPY_NO_EXPORT int array_assign_boolean_subscript(PyArrayObject *self, PyArrayObject *bmask, PyArrayObject *v, NPY_ORDER order) { - npy_intp size, src_itemsize, v_stride; + npy_intp size, v_stride; char *v_data; int needs_api = 0; npy_intp bmask_size; @@ -1192,9 +1217,9 @@ array_assign_boolean_subscript(PyArrayObject *self, if (size != PyArray_DIMS(v)[0]) { PyErr_Format(PyExc_ValueError, "NumPy boolean array indexing assignment " - "cannot assign %d input values to " - "the %d output values where the mask is true", - (int)PyArray_DIMS(v)[0], (int)size); + "cannot assign %" NPY_INTP_FMT " input values to " + "the %" NPY_INTP_FMT " output values where the mask is true", + PyArray_DIMS(v)[0], size); return -1; } v_stride = PyArray_STRIDES(v)[0]; @@ -1203,10 +1228,10 @@ array_assign_boolean_subscript(PyArrayObject *self, v_stride = 0; } - src_itemsize = PyArray_DESCR(v)->elsize; v_data = PyArray_DATA(v); /* Create an iterator for the data */ + int res = 0; if (size > 0) { NpyIter *iter; PyArrayObject *op[2] = {self, bmask}; @@ -1217,8 +1242,6 @@ array_assign_boolean_subscript(PyArrayObject *self, npy_intp innersize, *innerstrides; char **dataptrs; - PyArray_StridedUnaryOp *stransfer = NULL; - NpyAuxData *transferdata = NULL; npy_intp self_stride, bmask_stride, subloopsize; char *self_data; char *bmask_data; @@ -1250,12 +1273,14 @@ array_assign_boolean_subscript(PyArrayObject *self, /* Get a dtype transfer function */ NpyIter_GetInnerFixedStrideArray(iter, fixed_strides); + NPY_cast_info cast_info; if (PyArray_GetDTypeTransferFunction( - PyArray_ISALIGNED(self) && PyArray_ISALIGNED(v), + IsUintAligned(self) && IsAligned(self) && + IsUintAligned(v) && IsAligned(v), v_stride, fixed_strides[0], PyArray_DESCR(v), PyArray_DESCR(self), 0, - &stransfer, &transferdata, + &cast_info, &needs_api) != NPY_SUCCEED) { NpyIter_Deallocate(iter); return -1; @@ -1265,6 +1290,8 @@ array_assign_boolean_subscript(PyArrayObject *self, NPY_BEGIN_THREADS_NDITER(iter); } + npy_intp strides[2] = {v_stride, self_stride}; + do { innersize = *NpyIter_GetInnerLoopSizePtr(iter); self_data = dataptrs[0]; @@ -1279,8 +1306,13 @@ array_assign_boolean_subscript(PyArrayObject *self, /* Process unmasked values */ bmask_data = npy_memchr(bmask_data, 0, bmask_stride, innersize, &subloopsize, 0); - stransfer(self_data, self_stride, v_data, v_stride, - subloopsize, src_itemsize, transferdata); + + char *args[2] = {v_data, self_data}; + res = cast_info.func(&cast_info.context, + args, &subloopsize, strides, cast_info.auxdata); + if (res < 0) { + break; + } innersize -= subloopsize; self_data += subloopsize * self_stride; v_data += subloopsize * v_stride; @@ -1291,23 +1323,13 @@ array_assign_boolean_subscript(PyArrayObject *self, NPY_END_THREADS; } - NPY_AUXDATA_FREE(transferdata); - NpyIter_Deallocate(iter); - } - - if (needs_api) { - /* - * FIXME?: most assignment operations stop after the first occurrence - * of an error. Boolean does not currently, but should at least - * report the error. (This is only relevant for things like str->int - * casts which call into python) - */ - if (PyErr_Occurred()) { - return -1; + NPY_cast_info_xfree(&cast_info); + if (!NpyIter_Deallocate(iter)) { + res = -1; } } - return 0; + return res; } @@ -1386,71 +1408,31 @@ array_subscript_asarray(PyArrayObject *self, PyObject *op) return PyArray_EnsureAnyArray(array_subscript(self, op)); } -/* - * Helper function for _get_field_view which turns a multifield - * view into a "packed" copy, as done in numpy 1.15 and before. - * In numpy 1.16 this function should be removed. - */ -NPY_NO_EXPORT int -_multifield_view_to_copy(PyArrayObject **view) { - static PyObject *copyfunc = NULL; - PyObject *viewcopy; - - /* return a repacked copy of the view */ - npy_cache_import("numpy.lib.recfunctions", "repack_fields", ©func); - if (copyfunc == NULL) { - goto view_fail; - } - - PyArray_CLEARFLAGS(*view, NPY_ARRAY_WARN_ON_WRITE); - viewcopy = PyObject_CallFunction(copyfunc, "O", *view); - if (viewcopy == NULL) { - goto view_fail; - } - Py_DECREF(*view); - *view = (PyArrayObject*)viewcopy; - - /* warn when writing to the copy */ - PyArray_ENABLEFLAGS(*view, NPY_ARRAY_WARN_ON_WRITE); - return 0; - -view_fail: - Py_DECREF(*view); - *view = NULL; - return 0; -} - /* * Attempts to subscript an array using a field name or list of field names. * - * If an error occurred, return 0 and set view to NULL. If the subscript is not - * a string or list of strings, return -1 and set view to NULL. Otherwise - * return 0 and set view to point to a new view into arr for the given fields. - * - * In numpy 1.15 and before, in the case of a list of field names the returned - * view will actually be a copy by default, with fields packed together. - * The `force_view` argument causes a view to be returned. This argument can be - * removed in 1.16 when we plan to return a view always. + * ret = 0, view != NULL: view points to the requested fields of arr + * ret = 0, view == NULL: an error occurred + * ret = -1, view == NULL: unrecognized input, this is not a field index. */ NPY_NO_EXPORT int -_get_field_view(PyArrayObject *arr, PyObject *ind, PyArrayObject **view, - int force_view) +_get_field_view(PyArrayObject *arr, PyObject *ind, PyArrayObject **view) { *view = NULL; /* first check for a single field name */ - if (PyBaseString_Check(ind)) { + if (PyUnicode_Check(ind)) { PyObject *tup; PyArray_Descr *fieldtype; npy_intp offset; /* get the field offset and dtype */ - tup = PyDict_GetItem(PyArray_DESCR(arr)->fields, ind); - if (tup == NULL){ - PyObject *errmsg = PyUString_FromString("no field of name "); - PyUString_Concat(&errmsg, ind); - PyErr_SetObject(PyExc_ValueError, errmsg); - Py_DECREF(errmsg); + tup = PyDict_GetItemWithError(PyArray_DESCR(arr)->fields, ind); + if (tup == NULL && PyErr_Occurred()) { + return 0; + } + else if (tup == NULL){ + PyErr_Format(PyExc_ValueError, "no field of name %S", ind); return 0; } if (_unpack_field(tup, &fieldtype, &offset) < 0) { @@ -1474,113 +1456,44 @@ _get_field_view(PyArrayObject *arr, PyObject *ind, PyArrayObject **view, } return 0; } + /* next check for a list of field names */ else if (PySequence_Check(ind) && !PyTuple_Check(ind)) { - int seqlen, i; - PyObject *name = NULL, *tup; - PyObject *fields, *names; + npy_intp seqlen, i; PyArray_Descr *view_dtype; seqlen = PySequence_Size(ind); - /* quit if have a 0-d array (seqlen==-1) or a 0-len array */ + /* quit if have a fake sequence-like, which errors on len()*/ if (seqlen == -1) { PyErr_Clear(); return -1; } + /* 0-len list is handled elsewhere as an integer index */ if (seqlen == 0) { return -1; } - fields = PyDict_New(); - if (fields == NULL) { - return 0; - } - names = PyTuple_New(seqlen); - if (names == NULL) { - Py_DECREF(fields); - return 0; - } - + /* check the items are strings */ for (i = 0; i < seqlen; i++) { - name = PySequence_GetItem(ind, i); - if (name == NULL) { - /* only happens for strange sequence objects */ + npy_bool is_string; + PyObject *item = PySequence_GetItem(ind, i); + if (item == NULL) { PyErr_Clear(); - Py_DECREF(fields); - Py_DECREF(names); return -1; } - - if (!PyBaseString_Check(name)) { - Py_DECREF(name); - Py_DECREF(fields); - Py_DECREF(names); + is_string = PyUnicode_Check(item); + Py_DECREF(item); + if (!is_string) { return -1; } - - tup = PyDict_GetItem(PyArray_DESCR(arr)->fields, name); - if (tup == NULL){ - PyObject *errmsg = PyUString_FromString("no field of name "); - PyUString_ConcatAndDel(&errmsg, name); - PyErr_SetObject(PyExc_ValueError, errmsg); - Py_DECREF(errmsg); - Py_DECREF(fields); - Py_DECREF(names); - return 0; - } - /* disallow use of titles as index */ - if (PyTuple_Size(tup) == 3) { - PyObject *title = PyTuple_GET_ITEM(tup, 2); - int titlecmp = PyObject_RichCompareBool(title, name, Py_EQ); - if (titlecmp == 1) { - /* if title == name, we got a title, not a field name */ - PyErr_SetString(PyExc_KeyError, - "cannot use field titles in multi-field index"); - } - if (titlecmp != 0 || PyDict_SetItem(fields, title, tup) < 0) { - Py_DECREF(title); - Py_DECREF(name); - Py_DECREF(fields); - Py_DECREF(names); - return 0; - } - Py_DECREF(title); - } - /* disallow duplicate field indices */ - if (PyDict_Contains(fields, name)) { - PyObject *errmsg = PyUString_FromString( - "duplicate field of name "); - PyUString_ConcatAndDel(&errmsg, name); - PyErr_SetObject(PyExc_ValueError, errmsg); - Py_DECREF(errmsg); - Py_DECREF(fields); - Py_DECREF(names); - return 0; - } - if (PyDict_SetItem(fields, name, tup) < 0) { - Py_DECREF(name); - Py_DECREF(fields); - Py_DECREF(names); - return 0; - } - if (PyTuple_SetItem(names, i, name) < 0) { - Py_DECREF(fields); - Py_DECREF(names); - return 0; - } } - view_dtype = PyArray_DescrNewFromType(NPY_VOID); + /* Call into the dtype subscript */ + view_dtype = arraydescr_field_subset_view(PyArray_DESCR(arr), ind); if (view_dtype == NULL) { - Py_DECREF(fields); - Py_DECREF(names); return 0; } - view_dtype->elsize = PyArray_DESCR(arr)->elsize; - view_dtype->names = names; - view_dtype->fields = fields; - view_dtype->flags = PyArray_DESCR(arr)->flags; *view = (PyArrayObject*)PyArray_NewFromDescr_int( Py_TYPE(arr), @@ -1597,11 +1510,7 @@ _get_field_view(PyArrayObject *arr, PyObject *ind, PyArrayObject **view, return 0; } - /* the code below can be replaced by "return 0" in 1.16 */ - if (force_view) { - return 0; - } - return _multifield_view_to_copy(view); + return 0; } return -1; } @@ -1629,7 +1538,7 @@ array_subscript(PyArrayObject *self, PyObject *op) /* return fields if op is a string index */ if (PyDataType_HASFIELDS(PyArray_DESCR(self))) { PyArrayObject *view; - int ret = _get_field_view(self, op, &view, 0); + int ret = _get_field_view(self, op, &view); if (ret == 0){ if (view == NULL) { return NULL; @@ -1724,7 +1633,7 @@ array_subscript(PyArrayObject *self, PyObject *op) /* Check if the type is equivalent to INTP */ PyArray_ITEMSIZE(ind) == sizeof(npy_intp) && PyArray_DESCR(ind)->kind == 'i' && - PyArray_ISALIGNED(ind) && + IsUintAligned(ind) && PyDataType_ISNOTSWAPPED(PyArray_DESCR(ind))) { Py_INCREF(PyArray_DESCR(self)); @@ -1763,11 +1672,12 @@ array_subscript(PyArrayObject *self, PyObject *op) goto finish; } - if (mit->numiter > 1) { + if (mit->numiter > 1 || mit->size == 0) { /* * If it is one, the inner loop checks indices, otherwise * check indices beforehand, because it is much faster if - * broadcasting occurs and most likely no big overhead + * broadcasting occurs and most likely no big overhead. + * The inner loop optimization skips index checks for size == 0 though. */ if (PyArray_MapIterCheckIndices(mit) < 0) { goto finish; @@ -1805,7 +1715,7 @@ array_subscript(PyArrayObject *self, PyObject *op) PyArray_SHAPE(tmp_arr), PyArray_STRIDES(tmp_arr), PyArray_BYTES(tmp_arr), - PyArray_FLAGS(self), + PyArray_FLAGS(tmp_arr), (PyObject *)self, (PyObject *)tmp_arr); Py_DECREF(tmp_arr); if (result == NULL) { @@ -1861,7 +1771,7 @@ array_assign_item(PyArrayObject *self, Py_ssize_t i, PyObject *op) if (get_item_pointer(self, &item, indices, 1) < 0) { return -1; } - if (PyArray_SETITEM(self, item, op) < 0) { + if (PyArray_Pack(PyArray_DESCR(self), item, op) < 0) { return -1; } } @@ -1911,7 +1821,7 @@ array_assign_subscript(PyArrayObject *self, PyObject *ind, PyObject *op) /* field access */ if (PyDataType_HASFIELDS(PyArray_DESCR(self))){ PyArrayObject *view; - int ret = _get_field_view(self, ind, &view, 1); + int ret = _get_field_view(self, ind, &view); if (ret == 0){ if (view == NULL) { return -1; @@ -1939,7 +1849,7 @@ array_assign_subscript(PyArrayObject *self, PyObject *ind, PyObject *op) if (get_item_pointer(self, &item, indices, index_num) < 0) { return -1; } - if (PyArray_SETITEM(self, item, op) < 0) { + if (PyArray_Pack(PyArray_DESCR(self), item, op) < 0) { return -1; } /* integers do not store objects in indices */ @@ -2083,11 +1993,11 @@ array_assign_subscript(PyArrayObject *self, PyObject *ind, PyObject *op) PyArray_TRIVIALLY_ITERABLE_OP_READ, PyArray_TRIVIALLY_ITERABLE_OP_READ) || (PyArray_NDIM(tmp_arr) == 0 && - PyArray_TRIVIALLY_ITERABLE(tmp_arr))) && + PyArray_TRIVIALLY_ITERABLE(ind))) && /* Check if the type is equivalent to INTP */ PyArray_ITEMSIZE(ind) == sizeof(npy_intp) && PyArray_DESCR(ind)->kind == 'i' && - PyArray_ISALIGNED(ind) && + IsUintAligned(ind) && PyDataType_ISNOTSWAPPED(PyArray_DESCR(ind))) { /* trivial_set checks the index for us */ @@ -2432,7 +2342,7 @@ PyArray_MapIterNext(PyArrayMapIterObject *mit) * @param Number of indices * @param The array that is being iterated * - * @return 0 on success -1 on failure + * @return 0 on success -1 on failure (broadcasting or too many fancy indices) */ static int mapiter_fill_info(PyArrayMapIterObject *mit, npy_index_info *indices, @@ -2446,7 +2356,6 @@ mapiter_fill_info(PyArrayMapIterObject *mit, npy_index_info *indices, int consec_status = -1; int axis, broadcast_axis; npy_intp dimension; - PyObject *errmsg, *tmp; for (i = 0; i < mit->nd_fancy; i++) { mit->dimensions[i] = 1; @@ -2474,6 +2383,17 @@ mapiter_fill_info(PyArrayMapIterObject *mit, npy_index_info *indices, } } + /* Before contunuing, ensure that there are not too fancy indices */ + if (indices[i].type & HAS_FANCY) { + if (NPY_UNLIKELY(j >= NPY_MAXDIMS)) { + PyErr_Format(PyExc_IndexError, + "too many advanced (array) indices. This probably " + "means you are indexing with too many booleans. " + "(more than %d found)", NPY_MAXDIMS); + return -1; + } + } + /* (iterating) fancy index, store the iterator */ if (indices[i].type == HAS_FANCY) { mit->fancy_strides[j] = PyArray_STRIDE(arr, curr_dim); @@ -2534,35 +2454,38 @@ mapiter_fill_info(PyArrayMapIterObject *mit, npy_index_info *indices, return 0; - broadcast_error: +broadcast_error: ; // Declarations cannot follow labels, add empty statement. /* * Attempt to set a meaningful exception. Could also find out * if a boolean index was converted. */ - errmsg = PyUString_FromString("shape mismatch: indexing arrays could not " - "be broadcast together with shapes "); + PyObject *errmsg = PyUnicode_FromString(""); if (errmsg == NULL) { return -1; } - for (i = 0; i < index_num; i++) { if (!(indices[i].type & HAS_FANCY)) { continue; } - tmp = convert_shape_to_string( - PyArray_NDIM((PyArrayObject *)indices[i].object), - PyArray_SHAPE((PyArrayObject *)indices[i].object), - " "); + + int ndim = PyArray_NDIM((PyArrayObject *)indices[i].object); + npy_intp *shape = PyArray_SHAPE((PyArrayObject *)indices[i].object); + PyObject *tmp = convert_shape_to_string(ndim, shape, " "); if (tmp == NULL) { + Py_DECREF(errmsg); return -1; } - PyUString_ConcatAndDel(&errmsg, tmp); + + Py_SETREF(errmsg, PyUnicode_Concat(errmsg, tmp)); + Py_DECREF(tmp); if (errmsg == NULL) { return -1; } } - PyErr_SetObject(PyExc_IndexError, errmsg); + PyErr_Format(PyExc_IndexError, + "shape mismatch: indexing arrays could not " + "be broadcast together with shapes %S", errmsg); Py_DECREF(errmsg); return -1; } @@ -2587,8 +2510,14 @@ PyArray_MapIterCheckIndices(PyArrayMapIterObject *mit) int i; NPY_BEGIN_THREADS_DEF; - if (mit->size == 0) { - /* All indices got broadcast away, do *not* check as it always was */ + if (NpyIter_GetIterSize(mit->outer) == 0) { + /* + * When the outer iteration is empty, the indices broadcast to an + * empty shape, and in this case we do not check if there are out + * of bounds indices. + * The code below does use the indices without broadcasting since + * broadcasting only repeats values. + */ return 0; } @@ -2607,7 +2536,7 @@ PyArray_MapIterCheckIndices(PyArrayMapIterObject *mit) /* Check if the type is equivalent to INTP */ PyArray_ITEMSIZE(op) == sizeof(npy_intp) && PyArray_DESCR(op)->kind == 'i' && - PyArray_ISALIGNED(op) && + IsUintAligned(op) && PyDataType_ISNOTSWAPPED(PyArray_DESCR(op))) { char *data; npy_intp stride; @@ -2622,7 +2551,8 @@ PyArray_MapIterCheckIndices(PyArrayMapIterObject *mit) indval = *((npy_intp*)data); if (check_and_adjust_index(&indval, outer_dim, outer_axis, _save) < 0) { - return -1; + Py_DECREF(intp_type); + goto indexing_error; } data += stride; } @@ -2635,13 +2565,17 @@ PyArray_MapIterCheckIndices(PyArrayMapIterObject *mit) op_iter = NpyIter_New(op, NPY_ITER_BUFFERED | NPY_ITER_NBO | NPY_ITER_ALIGNED | NPY_ITER_EXTERNAL_LOOP | NPY_ITER_GROWINNER | - NPY_ITER_READONLY, + NPY_ITER_READONLY | NPY_ITER_ZEROSIZE_OK, NPY_KEEPORDER, NPY_SAME_KIND_CASTING, intp_type); if (op_iter == NULL) { Py_DECREF(intp_type); return -1; } + if (NpyIter_GetIterSize(op_iter) == 0) { + NpyIter_Deallocate(op_iter); + continue; + } op_iternext = NpyIter_GetIterNext(op_iter, NULL); if (op_iternext == NULL) { @@ -2661,7 +2595,7 @@ PyArray_MapIterCheckIndices(PyArrayMapIterObject *mit) outer_dim, outer_axis, _save) < 0) { Py_DECREF(intp_type); NpyIter_Deallocate(op_iter); - return -1; + goto indexing_error; } *iterptr += *iterstride; } @@ -2674,6 +2608,32 @@ PyArray_MapIterCheckIndices(PyArrayMapIterObject *mit) NPY_END_THREADS; Py_DECREF(intp_type); return 0; + +indexing_error: + + if (mit->size == 0) { + PyObject *err_type = NULL, *err_value = NULL, *err_traceback = NULL; + PyErr_Fetch(&err_type, &err_value, &err_traceback); + /* 2020-05-27, NumPy 1.20 */ + if (DEPRECATE( + "Out of bound index found. This was previously ignored " + "when the indexing result contained no elements. " + "In the future the index error will be raised. This error " + "occurs either due to an empty slice, or if an array has zero " + "elements even before indexing.\n" + "(Use `warnings.simplefilter('error')` to turn this " + "DeprecationWarning into an error and get more details on " + "the invalid index.)") < 0) { + npy_PyErr_ChainExceptions(err_type, err_value, err_traceback); + return -1; + } + Py_DECREF(err_type); + Py_DECREF(err_value); + Py_XDECREF(err_traceback); + return 0; + } + + return -1; } @@ -2717,12 +2677,13 @@ PyArray_MapIterNew(npy_index_info *indices , int index_num, int index_type, npy_uint32 extra_op_flags, PyArrayObject *extra_op, PyArray_Descr *extra_op_dtype) { - PyObject *errmsg, *tmp; /* For shape reporting on error */ PyArrayObject *original_extra_op = extra_op; + /* NOTE: MAXARGS is the actual limit (2*NPY_MAXDIMS is index number one) */ PyArrayObject *index_arrays[NPY_MAXDIMS]; - PyArray_Descr *dtypes[NPY_MAXDIMS]; + PyArray_Descr *intp_descr; + PyArray_Descr *dtypes[NPY_MAXDIMS]; /* borrowed references */ npy_uint32 op_flags[NPY_MAXDIMS]; npy_uint32 outer_flags; @@ -2735,9 +2696,15 @@ PyArray_MapIterNew(npy_index_info *indices , int index_num, int index_type, int nops; int uses_subspace; + intp_descr = PyArray_DescrFromType(NPY_INTP); + if (intp_descr == NULL) { + return NULL; + } + /* create new MapIter object */ mit = (PyArrayMapIterObject *)PyArray_malloc(sizeof(PyArrayMapIterObject)); if (mit == NULL) { + Py_DECREF(intp_descr); return NULL; } /* set all attributes of mapiter to zero */ @@ -2767,6 +2734,7 @@ PyArray_MapIterNew(npy_index_info *indices , int index_num, int index_type, mit->nd_fancy = fancy_ndim; if (mapiter_fill_info(mit, indices, index_num, arr) < 0) { Py_DECREF(mit); + Py_DECREF(intp_descr); return NULL; } @@ -2776,7 +2744,7 @@ PyArray_MapIterNew(npy_index_info *indices , int index_num, int index_type, for (i=0; i < index_num; i++) { if (indices[i].type & HAS_FANCY) { index_arrays[mit->numiter] = (PyArrayObject *)indices[i].object; - dtypes[mit->numiter] = PyArray_DescrFromType(NPY_INTP); + dtypes[mit->numiter] = intp_descr; op_flags[mit->numiter] = (NPY_ITER_NBO | NPY_ITER_ALIGNED | @@ -2788,7 +2756,7 @@ PyArray_MapIterNew(npy_index_info *indices , int index_num, int index_type, if (mit->numiter == 0) { /* * For MapIterArray, it is possible that there is no fancy index. - * to support this case, add a a dummy iterator. + * to support this case, add a dummy iterator. * Since it is 0-d its transpose, etc. does not matter. */ @@ -2799,9 +2767,10 @@ PyArray_MapIterNew(npy_index_info *indices , int index_num, int index_type, PyArray_DescrFromType(NPY_INTP), 0); if (index_arrays[0] == NULL) { Py_DECREF(mit); + Py_DECREF(intp_descr); return NULL; } - dtypes[0] = PyArray_DescrFromType(NPY_INTP); + dtypes[0] = intp_descr; op_flags[0] = NPY_ITER_NBO | NPY_ITER_ALIGNED | NPY_ITER_READONLY; mit->fancy_dims[0] = 1; @@ -2916,20 +2885,20 @@ PyArray_MapIterNew(npy_index_info *indices , int index_num, int index_type, Py_INCREF(extra_op_dtype); mit->extra_op_dtype = extra_op_dtype; - /* Create an iterator, just to broadcast the arrays?! */ - tmp_iter = NpyIter_MultiNew(mit->numiter, index_arrays, - NPY_ITER_ZEROSIZE_OK | - NPY_ITER_REFS_OK | - NPY_ITER_MULTI_INDEX | - NPY_ITER_DONT_NEGATE_STRIDES, - NPY_KEEPORDER, - NPY_UNSAFE_CASTING, - tmp_op_flags, NULL); - if (tmp_iter == NULL) { - goto fail; - } - if (PyArray_SIZE(subspace) == 1) { + /* Create an iterator, just to broadcast the arrays?! */ + tmp_iter = NpyIter_MultiNew(mit->numiter, index_arrays, + NPY_ITER_ZEROSIZE_OK | + NPY_ITER_REFS_OK | + NPY_ITER_MULTI_INDEX | + NPY_ITER_DONT_NEGATE_STRIDES, + NPY_KEEPORDER, + NPY_UNSAFE_CASTING, + tmp_op_flags, NULL); + if (tmp_iter == NULL) { + goto fail; + } + /* * nditer allows itemsize with npy_intp type, so it works * here, but it would *not* work directly, since elsize @@ -2942,6 +2911,7 @@ PyArray_MapIterNew(npy_index_info *indices , int index_num, int index_type, "internal error: failed to find output array strides"); goto fail; } + NpyIter_Deallocate(tmp_iter); } else { /* Just use C-order strides (TODO: allow also F-order) */ @@ -2951,7 +2921,6 @@ PyArray_MapIterNew(npy_index_info *indices , int index_num, int index_type, stride *= mit->dimensions[i]; } } - NpyIter_Deallocate(tmp_iter); /* shape is set, and strides is set up to mit->nd, set rest */ PyArray_CreateSortedStridePerm(PyArray_NDIM(subspace), @@ -3031,7 +3000,6 @@ PyArray_MapIterNew(npy_index_info *indices , int index_num, int index_type, nops += 1; index_arrays[mit->numiter] = extra_op; - Py_INCREF(extra_op_dtype); dtypes[mit->numiter] = extra_op_dtype; op_flags[mit->numiter] = (extra_op_flags | NPY_ITER_ALLOCATE | @@ -3057,9 +3025,6 @@ PyArray_MapIterNew(npy_index_info *indices , int index_num, int index_type, } /* NpyIter cleanup and information: */ - for (i=0; i < nops; i++) { - Py_DECREF(dtypes[i]); - } if (dummy_array) { Py_DECREF(index_arrays[0]); } @@ -3145,6 +3110,7 @@ PyArray_MapIterNew(npy_index_info *indices , int index_num, int index_type, /* Can now return early if no subspace is being used */ if (!uses_subspace) { Py_XDECREF(extra_op); + Py_DECREF(intp_descr); return (PyObject *)mit; } @@ -3214,6 +3180,7 @@ PyArray_MapIterNew(npy_index_info *indices , int index_num, int index_type, } Py_XDECREF(extra_op); + Py_DECREF(intp_descr); return (PyObject *)mit; fail: @@ -3240,48 +3207,41 @@ PyArray_MapIterNew(npy_index_info *indices , int index_num, int index_type, goto finish; broadcast_error: - errmsg = PyUString_FromString("shape mismatch: value array " - "of shape "); - if (errmsg == NULL) { - goto finish; - } - /* Report the shape of the original array if it exists */ if (original_extra_op == NULL) { original_extra_op = extra_op; } - tmp = convert_shape_to_string(PyArray_NDIM(original_extra_op), - PyArray_DIMS(original_extra_op), " "); - if (tmp == NULL) { - goto finish; - } - PyUString_ConcatAndDel(&errmsg, tmp); - if (errmsg == NULL) { + int extra_ndim = PyArray_NDIM(original_extra_op); + npy_intp *extra_dims = PyArray_DIMS(original_extra_op); + PyObject *shape1 = convert_shape_to_string(extra_ndim, extra_dims, ""); + if (shape1 == NULL) { goto finish; } - tmp = PyUString_FromString("could not be broadcast to indexing " - "result of shape "); - PyUString_ConcatAndDel(&errmsg, tmp); - if (errmsg == NULL) { - goto finish; + /* Unscramble the iterator shape for reporting when `mit->consec` is used */ + npy_intp transposed[NPY_MAXDIMS]; + _get_transpose(mit->nd_fancy, mit->consec, mit->nd, 1, transposed); + for (i = 0; i < mit->nd; i++) { + transposed[i] = mit->dimensions[transposed[i]]; } - tmp = convert_shape_to_string(mit->nd, mit->dimensions, ""); - if (tmp == NULL) { - goto finish; - } - PyUString_ConcatAndDel(&errmsg, tmp); - if (errmsg == NULL) { + PyObject *shape2 = convert_shape_to_string(mit->nd, transposed, ""); + if (shape2 == NULL) { + Py_DECREF(shape1); goto finish; } - PyErr_SetObject(PyExc_ValueError, errmsg); - Py_DECREF(errmsg); + PyErr_Format(PyExc_ValueError, + "shape mismatch: value array of shape %S could not be broadcast " + "to indexing result of shape %S", shape1, shape2); + + Py_DECREF(shape1); + Py_DECREF(shape2); finish: Py_XDECREF(extra_op); + Py_DECREF(intp_descr); Py_DECREF(mit); return NULL; } @@ -3376,7 +3336,7 @@ PyArray_MapIterArrayCopyIfOverlap(PyArrayObject * a, PyObject * index, Py_XDECREF(a_copy); Py_XDECREF(subspace); Py_XDECREF((PyObject *)mit); - for (i=0; i < index_num; i++) { + for (i = 0; i < index_num; i++) { Py_XDECREF(indices[i].object); } return NULL; @@ -3437,61 +3397,9 @@ arraymapiter_dealloc(PyArrayMapIterObject *mit) * to a[indexobj].flat but the latter gets to use slice syntax. */ NPY_NO_EXPORT PyTypeObject PyArrayMapIter_Type = { -#if defined(NPY_PY3K) PyVarObject_HEAD_INIT(NULL, 0) -#else - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ -#endif - "numpy.mapiter", /* tp_name */ - sizeof(PyArrayMapIterObject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)arraymapiter_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ -#if defined(NPY_PY3K) - 0, /* tp_reserved */ -#else - 0, /* tp_compare */ -#endif - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - 0, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - 0, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ - 0, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - 0, /* tp_del */ - 0, /* tp_version_tag */ + .tp_name = "numpy.mapiter", + .tp_basicsize = sizeof(PyArrayMapIterObject), + .tp_dealloc = (destructor)arraymapiter_dealloc, + .tp_flags = Py_TPFLAGS_DEFAULT, }; diff --git a/numpy/core/src/multiarray/mapping.h b/numpy/core/src/multiarray/mapping.h index 4e22f79df2c9..e929b8b3f729 100644 --- a/numpy/core/src/multiarray/mapping.h +++ b/numpy/core/src/multiarray/mapping.h @@ -1,5 +1,5 @@ -#ifndef _NPY_ARRAYMAPPING_H_ -#define _NPY_ARRAYMAPPING_H_ +#ifndef NUMPY_CORE_SRC_MULTIARRAY_MAPPING_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_MAPPING_H_ extern NPY_NO_EXPORT PyMappingMethods array_as_mapping; @@ -70,4 +70,4 @@ PyArray_MapIterNew(npy_index_info *indices , int index_num, int index_type, npy_uint32 subspace_iter_flags, npy_uint32 subspace_flags, npy_uint32 extra_op_flags, PyArrayObject *extra_op, PyArray_Descr *extra_op_dtype); -#endif +#endif /* NUMPY_CORE_SRC_MULTIARRAY_MAPPING_H_ */ diff --git a/numpy/core/src/multiarray/methods.c b/numpy/core/src/multiarray/methods.c index d6f2577a3a35..c31a8292ce32 100644 --- a/numpy/core/src/multiarray/methods.c +++ b/numpy/core/src/multiarray/methods.c @@ -1,17 +1,21 @@ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE + #define PY_SSIZE_T_CLEAN -#include <stdarg.h> #include <Python.h> -#include "structmember.h" +#include <structmember.h> -#define NPY_NO_DEPRECATED_API NPY_API_VERSION -#define _MULTIARRAYMODULE #include "numpy/arrayobject.h" #include "numpy/arrayscalars.h" +#include "arrayobject.h" +#include "arrayfunction_override.h" +#include "npy_argparse.h" #include "npy_config.h" #include "npy_pycompat.h" #include "npy_import.h" #include "ufunc_override.h" +#include "array_coercion.h" #include "common.h" #include "templ_common.h" /* for npy_mul_with_overflow_intp */ #include "ctors.h" @@ -21,10 +25,14 @@ #include "conversion_utils.h" #include "shape.h" #include "strfuncs.h" +#include "array_assign.h" +#include "npy_dlpack.h" #include "methods.h" #include "alloc.h" +#include <stdarg.h> + /* NpyArg_ParseKeywords * @@ -51,29 +59,6 @@ NpyArg_ParseKeywords(PyObject *keys, const char *format, char **kwlist, ...) return ret; } -static PyObject * -get_forwarding_ndarray_method(const char *name) -{ - PyObject *module_methods, *callable; - - /* Get a reference to the function we're calling */ - module_methods = PyImport_ImportModule("numpy.core._methods"); - if (module_methods == NULL) { - return NULL; - } - callable = PyDict_GetItemString(PyModule_GetDict(module_methods), name); - if (callable == NULL) { - Py_DECREF(module_methods); - PyErr_Format(PyExc_RuntimeError, - "NumPy internal error: could not find function " - "numpy.core._methods.%s", name); - } - else { - Py_INCREF(callable); - } - Py_DECREF(module_methods); - return callable; -} /* * Forwards an ndarray method to a the Python function @@ -114,33 +99,41 @@ forward_ndarray_method(PyArrayObject *self, PyObject *args, PyObject *kwds, */ #define NPY_FORWARD_NDARRAY_METHOD(name) \ static PyObject *callable = NULL; \ + npy_cache_import("numpy.core._methods", name, &callable); \ if (callable == NULL) { \ - callable = get_forwarding_ndarray_method(name); \ - if (callable == NULL) { \ - return NULL; \ - } \ + return NULL; \ } \ return forward_ndarray_method(self, args, kwds, callable) static PyObject * -array_take(PyArrayObject *self, PyObject *args, PyObject *kwds) +array_take(PyArrayObject *self, + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) { int dimension = NPY_MAXDIMS; PyObject *indices; PyArrayObject *out = NULL; NPY_CLIPMODE mode = NPY_RAISE; - static char *kwlist[] = {"indices", "axis", "out", "mode", NULL}; + NPY_PREPARE_ARGPARSER; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O&O&O&:take", kwlist, - &indices, - PyArray_AxisConverter, &dimension, - PyArray_OutputConverter, &out, - PyArray_ClipmodeConverter, &mode)) + if (npy_parse_arguments("take", args, len_args, kwnames, + "indices", NULL, &indices, + "|axis", &PyArray_AxisConverter, &dimension, + "|out", &PyArray_OutputConverter, &out, + "|mode", &PyArray_ClipmodeConverter, &mode, + NULL, NULL, NULL) < 0) { return NULL; + } + + PyObject *ret = PyArray_TakeFrom(self, indices, dimension, out, mode); - return PyArray_Return((PyArrayObject *) - PyArray_TakeFrom(self, indices, dimension, out, mode)); + /* this matches the unpacking behavior of ufuncs */ + if (out == NULL) { + return PyArray_Return((PyArrayObject *)ret); + } + else { + return ret; + } } static PyObject * @@ -186,7 +179,7 @@ array_reshape(PyArrayObject *self, PyObject *args, PyObject *kwds) } if (n <= 1) { - if (PyTuple_GET_ITEM(args, 0) == Py_None) { + if (n != 0 && PyTuple_GET_ITEM(args, 0) == Py_None) { return PyArray_View(self, NULL, NULL); } if (!PyArg_ParseTuple(args, "O&:reshape", PyArray_IntpConverter, @@ -213,14 +206,16 @@ array_reshape(PyArrayObject *self, PyObject *args, PyObject *kwds) } static PyObject * -array_squeeze(PyArrayObject *self, PyObject *args, PyObject *kwds) +array_squeeze(PyArrayObject *self, + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) { PyObject *axis_in = NULL; npy_bool axis_flags[NPY_MAXDIMS]; + NPY_PREPARE_ARGPARSER; - static char *kwlist[] = {"axis", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O:squeeze", kwlist, - &axis_in)) { + if (npy_parse_arguments("squeeze", args, len_args, kwnames, + "|axis", NULL, &axis_in, + NULL, NULL, NULL) < 0) { return NULL; } @@ -238,16 +233,18 @@ array_squeeze(PyArrayObject *self, PyObject *args, PyObject *kwds) } static PyObject * -array_view(PyArrayObject *self, PyObject *args, PyObject *kwds) +array_view(PyArrayObject *self, + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) { PyObject *out_dtype = NULL; PyObject *out_type = NULL; PyArray_Descr *dtype = NULL; + NPY_PREPARE_ARGPARSER; - static char *kwlist[] = {"dtype", "type", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO:view", kwlist, - &out_dtype, - &out_type)) { + if (npy_parse_arguments("view", args, len_args, kwnames, + "|dtype", NULL, &out_dtype, + "|type", NULL, &out_type, + NULL, NULL, NULL) < 0) { return NULL; } @@ -285,33 +282,58 @@ array_view(PyArrayObject *self, PyObject *args, PyObject *kwds) } static PyObject * -array_argmax(PyArrayObject *self, PyObject *args, PyObject *kwds) +array_argmax(PyArrayObject *self, + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) { int axis = NPY_MAXDIMS; PyArrayObject *out = NULL; - static char *kwlist[] = {"axis", "out", NULL}; + npy_bool keepdims = NPY_FALSE; + NPY_PREPARE_ARGPARSER; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&O&:argmax", kwlist, - PyArray_AxisConverter, &axis, - PyArray_OutputConverter, &out)) + if (npy_parse_arguments("argmax", args, len_args, kwnames, + "|axis", &PyArray_AxisConverter, &axis, + "|out", &PyArray_OutputConverter, &out, + "$keepdims", &PyArray_BoolConverter, &keepdims, + NULL, NULL, NULL) < 0) { return NULL; + } - return PyArray_Return((PyArrayObject *)PyArray_ArgMax(self, axis, out)); + PyObject *ret = _PyArray_ArgMaxWithKeepdims(self, axis, out, keepdims); + + /* this matches the unpacking behavior of ufuncs */ + if (out == NULL) { + return PyArray_Return((PyArrayObject *)ret); + } + else { + return ret; + } } static PyObject * -array_argmin(PyArrayObject *self, PyObject *args, PyObject *kwds) +array_argmin(PyArrayObject *self, + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) { int axis = NPY_MAXDIMS; PyArrayObject *out = NULL; - static char *kwlist[] = {"axis", "out", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&O&:argmin", kwlist, - PyArray_AxisConverter, &axis, - PyArray_OutputConverter, &out)) + npy_bool keepdims = NPY_FALSE; + NPY_PREPARE_ARGPARSER; + if (npy_parse_arguments("argmin", args, len_args, kwnames, + "|axis", &PyArray_AxisConverter, &axis, + "|out", &PyArray_OutputConverter, &out, + "$keepdims", &PyArray_BoolConverter, &keepdims, + NULL, NULL, NULL) < 0) { return NULL; + } + + PyObject *ret = _PyArray_ArgMinWithKeepdims(self, axis, out, keepdims); - return PyArray_Return((PyArrayObject *)PyArray_ArgMin(self, axis, out)); + /* this matches the unpacking behavior of ufuncs */ + if (out == NULL) { + return PyArray_Return((PyArrayObject *)ret); + } + else { + return ret; + } } static PyObject * @@ -355,12 +377,14 @@ PyArray_GetField(PyArrayObject *self, PyArray_Descr *typed, int offset) PyObject *ret = NULL; PyObject *safe; static PyObject *checkfunc = NULL; + int self_elsize, typed_elsize; /* check that we are not reinterpreting memory containing Objects. */ if (_may_have_objects(PyArray_DESCR(self)) || _may_have_objects(typed)) { npy_cache_import("numpy.core._internal", "_getfield_is_safe", &checkfunc); if (checkfunc == NULL) { + Py_DECREF(typed); return NULL; } @@ -368,10 +392,30 @@ PyArray_GetField(PyArrayObject *self, PyArray_Descr *typed, int offset) safe = PyObject_CallFunction(checkfunc, "OOi", PyArray_DESCR(self), typed, offset); if (safe == NULL) { + Py_DECREF(typed); return NULL; } Py_DECREF(safe); } + self_elsize = PyArray_ITEMSIZE(self); + typed_elsize = typed->elsize; + + /* check that values are valid */ + if (typed_elsize > self_elsize) { + PyErr_SetString(PyExc_ValueError, "new type is larger than original type"); + Py_DECREF(typed); + return NULL; + } + if (offset < 0) { + PyErr_SetString(PyExc_ValueError, "offset is negative"); + Py_DECREF(typed); + return NULL; + } + if (offset > self_elsize - typed_elsize) { + PyErr_SetString(PyExc_ValueError, "new type plus offset is larger than original type"); + Py_DECREF(typed); + return NULL; + } ret = PyArray_NewFromDescr_int( Py_TYPE(self), typed, @@ -414,6 +458,7 @@ PyArray_SetField(PyArrayObject *self, PyArray_Descr *dtype, int retval = 0; if (PyArray_FailUnlessWriteable(self, "assignment destination") < 0) { + Py_DECREF(dtype); return -1; } @@ -536,6 +581,45 @@ array_tobytes(PyArrayObject *self, PyObject *args, PyObject *kwds) return PyArray_ToString(self, order); } +static PyObject * +array_tostring(PyArrayObject *self, PyObject *args, PyObject *kwds) +{ + NPY_ORDER order = NPY_CORDER; + static char *kwlist[] = {"order", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&:tostring", kwlist, + PyArray_OrderConverter, &order)) { + return NULL; + } + /* 2020-03-30, NumPy 1.19 */ + if (DEPRECATE("tostring() is deprecated. Use tobytes() instead.") < 0) { + return NULL; + } + return PyArray_ToString(self, order); +} + +/* Like PyArray_ToFile but takes the file as a python object */ +static int +PyArray_ToFileObject(PyArrayObject *self, PyObject *file, char *sep, char *format) +{ + npy_off_t orig_pos = 0; + FILE *fd = npy_PyFile_Dup2(file, "wb", &orig_pos); + + if (fd == NULL) { + return -1; + } + + int write_ret = PyArray_ToFile(self, fd, sep, format); + PyObject *err_type, *err_value, *err_traceback; + PyErr_Fetch(&err_type, &err_value, &err_traceback); + int close_ret = npy_PyFile_DupClose2(file, fd, orig_pos); + npy_PyErr_ChainExceptions(err_type, err_value, err_traceback); + + if (write_ret || close_ret) { + return -1; + } + return 0; +} /* This should grow an order= keyword to be consistent */ @@ -545,10 +629,8 @@ array_tofile(PyArrayObject *self, PyObject *args, PyObject *kwds) { int own; PyObject *file; - FILE *fd; char *sep = ""; char *format = ""; - npy_off_t orig_pos = 0; static char *kwlist[] = {"file", "sep", "format", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|ss:tofile", kwlist, @@ -558,37 +640,37 @@ array_tofile(PyArrayObject *self, PyObject *args, PyObject *kwds) return NULL; } + file = NpyPath_PathlikeToFspath(file); + if (file == NULL) { + return NULL; + } if (PyBytes_Check(file) || PyUnicode_Check(file)) { - file = npy_PyFile_OpenFile(file, "wb"); + Py_SETREF(file, npy_PyFile_OpenFile(file, "wb")); if (file == NULL) { return NULL; } own = 1; } else { - Py_INCREF(file); own = 0; } - fd = npy_PyFile_Dup2(file, "wb", &orig_pos); - if (fd == NULL) { - goto fail; - } - if (PyArray_ToFile(self, fd, sep, format) < 0) { - goto fail; - } - if (npy_PyFile_DupClose2(file, fd, orig_pos) < 0) { - goto fail; - } - if (own && npy_PyFile_CloseFile(file) < 0) { - goto fail; + int file_ret = PyArray_ToFileObject(self, file, sep, format); + int close_ret = 0; + + if (own) { + PyObject *err_type, *err_value, *err_traceback; + PyErr_Fetch(&err_type, &err_value, &err_traceback); + close_ret = npy_PyFile_CloseFile(file); + npy_PyErr_ChainExceptions(err_type, err_value, err_traceback); } - Py_DECREF(file); - Py_RETURN_NONE; -fail: Py_DECREF(file); - return NULL; + + if (file_ret || close_ret) { + return NULL; + } + Py_RETURN_NONE; } static PyObject * @@ -691,6 +773,7 @@ array_setscalar(PyArrayObject *self, PyObject *args) else { PyErr_SetString(PyExc_ValueError, "can only convert an array of size 1 to a Python scalar"); + return NULL; } } /* Special case of C-order flat indexing... :| */ @@ -739,30 +822,11 @@ array_setscalar(PyArrayObject *self, PyObject *args) } } -NPY_NO_EXPORT const char * -npy_casting_to_string(NPY_CASTING casting) -{ - switch (casting) { - case NPY_NO_CASTING: - return "'no'"; - case NPY_EQUIV_CASTING: - return "'equiv'"; - case NPY_SAFE_CASTING: - return "'safe'"; - case NPY_SAME_KIND_CASTING: - return "'same_kind'"; - case NPY_UNSAFE_CASTING: - return "'unsafe'"; - default: - return "<unknown>"; - } -} static PyObject * -array_astype(PyArrayObject *self, PyObject *args, PyObject *kwds) +array_astype(PyArrayObject *self, + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) { - static char *kwlist[] = {"dtype", "order", "casting", - "subok", "copy", NULL}; PyArray_Descr *dtype = NULL; /* * TODO: UNSAFE default for compatibility, I think @@ -770,78 +834,92 @@ array_astype(PyArrayObject *self, PyObject *args, PyObject *kwds) */ NPY_CASTING casting = NPY_UNSAFE_CASTING; NPY_ORDER order = NPY_KEEPORDER; - int forcecopy = 1, subok = 1; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|O&O&ii:astype", kwlist, - PyArray_DescrConverter, &dtype, - PyArray_OrderConverter, &order, - PyArray_CastingConverter, &casting, - &subok, - &forcecopy)) { + _PyArray_CopyMode forcecopy = 1; + int subok = 1; + NPY_PREPARE_ARGPARSER; + if (npy_parse_arguments("astype", args, len_args, kwnames, + "dtype", &PyArray_DescrConverter, &dtype, + "|order", &PyArray_OrderConverter, &order, + "|casting", &PyArray_CastingConverter, &casting, + "|subok", &PyArray_PythonPyIntFromInt, &subok, + "|copy", &PyArray_CopyConverter, &forcecopy, + NULL, NULL, NULL) < 0) { Py_XDECREF(dtype); return NULL; } + /* If it is not a concrete dtype instance find the best one for the array */ + Py_SETREF(dtype, PyArray_AdaptDescriptorToArray(self, (PyObject *)dtype)); + if (dtype == NULL) { + return NULL; + } + /* * If the memory layout matches and, data types are equivalent, * and it's not a subtype if subok is False, then we * can skip the copy. */ - if (!forcecopy && (order == NPY_KEEPORDER || - (order == NPY_ANYORDER && - (PyArray_IS_C_CONTIGUOUS(self) || - PyArray_IS_F_CONTIGUOUS(self))) || - (order == NPY_CORDER && - PyArray_IS_C_CONTIGUOUS(self)) || - (order == NPY_FORTRANORDER && - PyArray_IS_F_CONTIGUOUS(self))) && - (subok || PyArray_CheckExact(self)) && - PyArray_EquivTypes(dtype, PyArray_DESCR(self))) { + if (forcecopy != NPY_COPY_ALWAYS && + (order == NPY_KEEPORDER || + (order == NPY_ANYORDER && + (PyArray_IS_C_CONTIGUOUS(self) || + PyArray_IS_F_CONTIGUOUS(self))) || + (order == NPY_CORDER && + PyArray_IS_C_CONTIGUOUS(self)) || + (order == NPY_FORTRANORDER && + PyArray_IS_F_CONTIGUOUS(self))) && + (subok || PyArray_CheckExact(self)) && + PyArray_EquivTypes(dtype, PyArray_DESCR(self))) { Py_DECREF(dtype); Py_INCREF(self); return (PyObject *)self; } - else if (PyArray_CanCastArrayTo(self, dtype, casting)) { - PyArrayObject *ret; - - /* If the requested dtype is flexible, adapt it */ - PyArray_AdaptFlexibleDType((PyObject *)self, PyArray_DESCR(self), - &dtype); - if (dtype == NULL) { - return NULL; - } - /* This steals the reference to dtype, so no DECREF of dtype */ - ret = (PyArrayObject *)PyArray_NewLikeArray( - self, order, dtype, subok); - if (ret == NULL) { - return NULL; - } + if (forcecopy == NPY_COPY_NEVER) { + PyErr_SetString(PyExc_ValueError, + "Unable to avoid copy while casting in never copy mode."); + Py_DECREF(dtype); + return NULL; + } + + if (!PyArray_CanCastArrayTo(self, dtype, casting)) { + PyErr_Clear(); + npy_set_invalid_cast_error( + PyArray_DESCR(self), dtype, casting, PyArray_NDIM(self) == 0); + Py_DECREF(dtype); + return NULL; + } - if (PyArray_CopyInto(ret, self) < 0) { - Py_DECREF(ret); - return NULL; - } + PyArrayObject *ret; - return (PyObject *)ret; + /* This steals the reference to dtype, so no DECREF of dtype */ + ret = (PyArrayObject *)PyArray_NewLikeArray( + self, order, dtype, subok); + if (ret == NULL) { + return NULL; } - else { - PyObject *errmsg; - errmsg = PyUString_FromString("Cannot cast array from "); - PyUString_ConcatAndDel(&errmsg, - PyObject_Repr((PyObject *)PyArray_DESCR(self))); - PyUString_ConcatAndDel(&errmsg, - PyUString_FromString(" to ")); - PyUString_ConcatAndDel(&errmsg, - PyObject_Repr((PyObject *)dtype)); - PyUString_ConcatAndDel(&errmsg, - PyUString_FromFormat(" according to the rule %s", - npy_casting_to_string(casting))); - PyErr_SetObject(PyExc_TypeError, errmsg); - Py_DECREF(errmsg); - Py_DECREF(dtype); + /* NumPy 1.20, 2020-10-01 */ + if ((PyArray_NDIM(self) != PyArray_NDIM(ret)) && + DEPRECATE_FUTUREWARNING( + "casting an array to a subarray dtype " + "will not use broadcasting in the future, but cast each " + "element to the new dtype and then append the dtype's shape " + "to the new array. You can opt-in to the new behaviour, by " + "additional field to the cast: " + "`arr.astype(np.dtype([('f', dtype)]))['f']`.\n" + "This may lead to a different result or to current failures " + "succeeding. " + "(FutureWarning since NumPy 1.20)") < 0) { + Py_DECREF(ret); return NULL; } + + if (PyArray_CopyInto(ret, self) < 0) { + Py_DECREF(ret); + return NULL; + } + + return (PyObject *)ret; } /* default sub-type implementation */ @@ -970,15 +1048,68 @@ array_getarray(PyArrayObject *self, PyObject *args) } } +/* + * Check whether any of the input and output args have a non-default + * __array_ufunc__ method. Return 1 if so, 0 if not, and -1 on error. + * + * This function primarily exists to help ndarray.__array_ufunc__ determine + * whether it can support a ufunc (which is the case only if none of the + * operands have an override). Thus, unlike in umath/override.c, the + * actual overrides are not needed and one can stop looking once one is found. + */ +static int +any_array_ufunc_overrides(PyObject *args, PyObject *kwds) +{ + int i; + int nin, nout; + PyObject *out_kwd_obj; + PyObject *fast; + PyObject **in_objs, **out_objs; -static PyObject * -array_ufunc(PyArrayObject *self, PyObject *args, PyObject *kwds) + /* check inputs */ + nin = PyTuple_Size(args); + if (nin < 0) { + return -1; + } + fast = PySequence_Fast(args, "Could not convert object to sequence"); + if (fast == NULL) { + return -1; + } + in_objs = PySequence_Fast_ITEMS(fast); + for (i = 0; i < nin; ++i) { + if (PyUFunc_HasOverride(in_objs[i])) { + Py_DECREF(fast); + return 1; + } + } + Py_DECREF(fast); + /* check outputs, if any */ + nout = PyUFuncOverride_GetOutObjects(kwds, &out_kwd_obj, &out_objs); + if (nout < 0) { + return -1; + } + for (i = 0; i < nout; i++) { + if (PyUFunc_HasOverride(out_objs[i])) { + Py_DECREF(out_kwd_obj); + return 1; + } + } + Py_DECREF(out_kwd_obj); + return 0; +} + + +NPY_NO_EXPORT PyObject * +array_ufunc(PyArrayObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds) { PyObject *ufunc, *method_name, *normal_args, *ufunc_method; PyObject *result = NULL; - int num_override_args; + int has_override; + + assert(PyTuple_CheckExact(args)); + assert(kwds == NULL || PyDict_CheckExact(kwds)); - if (PyTuple_Size(args) < 2) { + if (PyTuple_GET_SIZE(args) < 2) { PyErr_SetString(PyExc_TypeError, "__array_ufunc__ requires at least 2 arguments"); return NULL; @@ -988,11 +1119,11 @@ array_ufunc(PyArrayObject *self, PyObject *args, PyObject *kwds) return NULL; } /* ndarray cannot handle overrides itself */ - num_override_args = PyUFunc_WithOverride(normal_args, kwds, NULL, NULL); - if (num_override_args == -1) { - return NULL; + has_override = any_array_ufunc_overrides(normal_args, kwds); + if (has_override < 0) { + goto cleanup; } - if (num_override_args) { + else if (has_override) { result = Py_NotImplemented; Py_INCREF(Py_NotImplemented); goto cleanup; @@ -1017,15 +1148,40 @@ array_ufunc(PyArrayObject *self, PyObject *args, PyObject *kwds) return result; } +static PyObject * +array_function(PyArrayObject *NPY_UNUSED(self), PyObject *c_args, PyObject *c_kwds) +{ + PyObject *func, *types, *args, *kwargs, *result; + static char *kwlist[] = {"func", "types", "args", "kwargs", NULL}; + + if (!PyArg_ParseTupleAndKeywords( + c_args, c_kwds, "OOOO:__array_function__", kwlist, + &func, &types, &args, &kwargs)) { + return NULL; + } + + types = PySequence_Fast( + types, + "types argument to ndarray.__array_function__ must be iterable"); + if (types == NULL) { + return NULL; + } + + result = array_function_method_impl(func, types, args, kwargs); + Py_DECREF(types); + return result; +} static PyObject * -array_copy(PyArrayObject *self, PyObject *args, PyObject *kwds) +array_copy(PyArrayObject *self, + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) { NPY_ORDER order = NPY_CORDER; - static char *kwlist[] = {"order", NULL}; + NPY_PREPARE_ARGPARSER; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&:copy", kwlist, - PyArray_OrderConverter, &order)) { + if (npy_parse_arguments("copy", args, len_args, kwnames, + "|order", PyArray_OrderConverter, &order, + NULL, NULL, NULL) < 0) { return NULL; } @@ -1074,7 +1230,7 @@ array_resize(PyArrayObject *self, PyObject *args, PyObject *kwds) return NULL; } - ret = PyArray_Resize(self, &newshape, refcheck, NPY_CORDER); + ret = PyArray_Resize(self, &newshape, refcheck, NPY_ANYORDER); npy_free_cache_dim_obj(newshape); if (ret == NULL) { return NULL; @@ -1121,11 +1277,20 @@ array_choose(PyArrayObject *self, PyObject *args, PyObject *kwds) return NULL; } - return PyArray_Return((PyArrayObject *)PyArray_Choose(self, choices, out, clipmode)); + PyObject *ret = PyArray_Choose(self, choices, out, clipmode); + + /* this matches the unpacking behavior of ufuncs */ + if (out == NULL) { + return PyArray_Return((PyArrayObject *)ret); + } + else { + return ret; + } } static PyObject * -array_sort(PyArrayObject *self, PyObject *args, PyObject *kwds) +array_sort(PyArrayObject *self, + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) { int axis=-1; int val; @@ -1133,12 +1298,13 @@ array_sort(PyArrayObject *self, PyObject *args, PyObject *kwds) PyObject *order = NULL; PyArray_Descr *saved = NULL; PyArray_Descr *newd; - static char *kwlist[] = {"axis", "kind", "order", NULL}; + NPY_PREPARE_ARGPARSER; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iO&O:sort", kwlist, - &axis, - PyArray_SortkindConverter, &sortkind, - &order)) { + if (npy_parse_arguments("sort", args, len_args, kwnames, + "|axis", &PyArray_PythonPyIntFromInt, &axis, + "|kind", &PyArray_SortkindConverter, &sortkind, + "|order", NULL, &order, + NULL, NULL, NULL) < 0) { return NULL; } if (order == Py_None) { @@ -1181,7 +1347,8 @@ array_sort(PyArrayObject *self, PyObject *args, PyObject *kwds) } static PyObject * -array_partition(PyArrayObject *self, PyObject *args, PyObject *kwds) +array_partition(PyArrayObject *self, + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) { int axis=-1; int val; @@ -1189,16 +1356,16 @@ array_partition(PyArrayObject *self, PyObject *args, PyObject *kwds) PyObject *order = NULL; PyArray_Descr *saved = NULL; PyArray_Descr *newd; - static char *kwlist[] = {"kth", "axis", "kind", "order", NULL}; PyArrayObject * ktharray; PyObject * kthobj; + NPY_PREPARE_ARGPARSER; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|iO&O:partition", kwlist, - &kthobj, - &axis, - PyArray_SelectkindConverter, &sortkind, - &order)) { + if (npy_parse_arguments("partition", args, len_args, kwnames, + "kth", NULL, &kthobj, + "|axis", &PyArray_PythonPyIntFromInt, &axis, + "|kind", &PyArray_SelectkindConverter, &sortkind, + "|order", NULL, &order, + NULL, NULL, NULL) < 0) { return NULL; } @@ -1249,18 +1416,20 @@ array_partition(PyArrayObject *self, PyObject *args, PyObject *kwds) } static PyObject * -array_argsort(PyArrayObject *self, PyObject *args, PyObject *kwds) +array_argsort(PyArrayObject *self, + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) { int axis = -1; NPY_SORTKIND sortkind = NPY_QUICKSORT; PyObject *order = NULL, *res; PyArray_Descr *newd, *saved=NULL; - static char *kwlist[] = {"axis", "kind", "order", NULL}; + NPY_PREPARE_ARGPARSER; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&O&O:argsort", kwlist, - PyArray_AxisConverter, &axis, - PyArray_SortkindConverter, &sortkind, - &order)) { + if (npy_parse_arguments("argsort", args, len_args, kwnames, + "|axis", &PyArray_AxisConverter, &axis, + "|kind", &PyArray_SortkindConverter, &sortkind, + "|order", NULL, &order, + NULL, NULL, NULL) < 0) { return NULL; } if (order == Py_None) { @@ -1286,6 +1455,7 @@ array_argsort(PyArrayObject *self, PyObject *args, PyObject *kwds) return NULL; } newd = PyArray_DescrNew(saved); + Py_DECREF(newd->names); newd->names = new_name; ((PyArrayObject_fields *)self)->descr = newd; } @@ -1300,21 +1470,23 @@ array_argsort(PyArrayObject *self, PyObject *args, PyObject *kwds) static PyObject * -array_argpartition(PyArrayObject *self, PyObject *args, PyObject *kwds) +array_argpartition(PyArrayObject *self, + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) { int axis = -1; NPY_SELECTKIND sortkind = NPY_INTROSELECT; PyObject *order = NULL, *res; PyArray_Descr *newd, *saved=NULL; - static char *kwlist[] = {"kth", "axis", "kind", "order", NULL}; PyObject * kthobj; PyArrayObject * ktharray; + NPY_PREPARE_ARGPARSER; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O&O&O:argpartition", kwlist, - &kthobj, - PyArray_AxisConverter, &axis, - PyArray_SelectkindConverter, &sortkind, - &order)) { + if (npy_parse_arguments("argpartition", args, len_args, kwnames, + "kth", NULL, &kthobj, + "|axis", &PyArray_AxisConverter, &axis, + "|kind", &PyArray_SelectkindConverter, &sortkind, + "|order", NULL, &order, + NULL, NULL, NULL) < 0) { return NULL; } if (order == Py_None) { @@ -1340,6 +1512,7 @@ array_argpartition(PyArrayObject *self, PyObject *args, PyObject *kwds) return NULL; } newd = PyArray_DescrNew(saved); + Py_DECREF(newd->names); newd->names = new_name; ((PyArrayObject_fields *)self)->descr = newd; } @@ -1360,17 +1533,20 @@ array_argpartition(PyArrayObject *self, PyObject *args, PyObject *kwds) } static PyObject * -array_searchsorted(PyArrayObject *self, PyObject *args, PyObject *kwds) +array_searchsorted(PyArrayObject *self, + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) { - static char *kwlist[] = {"keys", "side", "sorter", NULL}; PyObject *keys; PyObject *sorter; NPY_SEARCHSIDE side = NPY_SEARCHLEFT; + NPY_PREPARE_ARGPARSER; sorter = NULL; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O&O:searchsorted", - kwlist, &keys, - PyArray_SearchsideConverter, &side, &sorter)) { + if (npy_parse_arguments("searchsorted", args, len_args, kwnames, + "v", NULL, &keys, + "|side", &PyArray_SearchsideConverter, &side, + "|sorter", NULL, &sorter, + NULL, NULL, NULL) < 0) { return NULL; } if (sorter == Py_None) { @@ -1392,7 +1568,7 @@ _deepcopy_call(char *iptr, char *optr, PyArray_Descr *dtype, int offset; Py_ssize_t pos = 0; while (PyDict_Next(dtype->fields, &pos, &key, &value)) { - if NPY_TITLE_KEY(key, value) { + if (NPY_TITLE_KEY(key, value)) { continue; } if (!PyArg_ParseTuple(value, "Oi|O", &new, &offset, @@ -1406,14 +1582,14 @@ _deepcopy_call(char *iptr, char *optr, PyArray_Descr *dtype, else { PyObject *itemp, *otemp; PyObject *res; - NPY_COPY_PYOBJECT_PTR(&itemp, iptr); - NPY_COPY_PYOBJECT_PTR(&otemp, optr); + memcpy(&itemp, iptr, sizeof(itemp)); + memcpy(&otemp, optr, sizeof(otemp)); Py_XINCREF(itemp); /* call deepcopy on this argument */ res = PyObject_CallFunctionObjArgs(deepcopy, itemp, visit, NULL); Py_XDECREF(itemp); Py_XDECREF(otemp); - NPY_COPY_PYOBJECT_PTR(optr, &res); + memcpy(optr, &res, sizeof(res)); } } @@ -1443,7 +1619,6 @@ array_deepcopy(PyArrayObject *self, PyObject *args) copy = PyImport_ImportModule("copy"); if (copy == NULL) { Py_DECREF(copied_array); - Py_DECREF(copy); return NULL; } deepcopy = PyObject_GetAttrString(copy, "deepcopy"); @@ -1515,7 +1690,7 @@ _getlist_pkl(PyArrayObject *self) } while (iter->index < iter->size) { theobject = getitem(iter->dataptr, self); - PyList_SET_ITEM(list, (int) iter->index, theobject); + PyList_SET_ITEM(list, iter->index, theobject); PyArray_ITER_NEXT(iter); } Py_DECREF(iter); @@ -1535,7 +1710,7 @@ _setlist_pkl(PyArrayObject *self, PyObject *list) return -1; } while(iter->index < iter->size) { - theobject = PyList_GET_ITEM(list, (int) iter->index); + theobject = PyList_GET_ITEM(list, iter->index); setitem(theobject, iter->dataptr, self); PyArray_ITER_NEXT(iter); } @@ -1563,7 +1738,7 @@ array_reduce(PyArrayObject *self, PyObject *NPY_UNUSED(args)) if (ret == NULL) { return NULL; } - mod = PyImport_ImportModule("numpy.core.multiarray"); + mod = PyImport_ImportModule("numpy.core._multiarray_umath"); if (mod == NULL) { Py_DECREF(ret); return NULL; @@ -1575,7 +1750,7 @@ array_reduce(PyArrayObject *self, PyObject *NPY_UNUSED(args)) Py_BuildValue("ONc", (PyObject *)Py_TYPE(self), Py_BuildValue("(N)", - PyInt_FromLong(0)), + PyLong_FromLong(0)), /* dummy data-type */ 'b')); @@ -1591,6 +1766,8 @@ array_reduce(PyArrayObject *self, PyObject *NPY_UNUSED(args)) Notice because Python does not describe a mechanism to write raw data to the pickle, this performs a copy to a string first + This issue is now addressed in protocol 5, where a buffer is serialized + instead of a string, */ state = PyTuple_New(5); @@ -1598,7 +1775,7 @@ array_reduce(PyArrayObject *self, PyObject *NPY_UNUSED(args)) Py_DECREF(ret); return NULL; } - PyTuple_SET_ITEM(state, 0, PyInt_FromLong(version)); + PyTuple_SET_ITEM(state, 0, PyLong_FromLong(version)); PyTuple_SET_ITEM(state, 1, PyObject_GetAttrString((PyObject *)self, "shape")); descr = PyArray_DESCR(self); @@ -1623,6 +1800,130 @@ array_reduce(PyArrayObject *self, PyObject *NPY_UNUSED(args)) return ret; } +static PyObject * +array_reduce_ex_regular(PyArrayObject *self, int NPY_UNUSED(protocol)) +{ + PyObject *subclass_array_reduce = NULL; + PyObject *ret; + + /* We do not call array_reduce directly but instead lookup and call + * the __reduce__ method to make sure that it's possible to customize + * pickling in sub-classes. */ + subclass_array_reduce = PyObject_GetAttrString((PyObject *)self, + "__reduce__"); + if (subclass_array_reduce == NULL) { + return NULL; + } + ret = PyObject_CallObject(subclass_array_reduce, NULL); + Py_DECREF(subclass_array_reduce); + return ret; +} + +static PyObject * +array_reduce_ex_picklebuffer(PyArrayObject *self, int protocol) +{ + PyObject *numeric_mod = NULL, *from_buffer_func = NULL; + PyObject *pickle_module = NULL, *picklebuf_class = NULL; + PyObject *picklebuf_args = NULL; + PyObject *buffer = NULL, *transposed_array = NULL; + PyArray_Descr *descr = NULL; + char order; + + descr = PyArray_DESCR(self); + + /* we expect protocol 5 to be available in Python 3.8 */ + pickle_module = PyImport_ImportModule("pickle"); + if (pickle_module == NULL){ + return NULL; + } + picklebuf_class = PyObject_GetAttrString(pickle_module, "PickleBuffer"); + Py_DECREF(pickle_module); + if (picklebuf_class == NULL) { + return NULL; + } + + /* Construct a PickleBuffer of the array */ + + if (!PyArray_IS_C_CONTIGUOUS((PyArrayObject*) self) && + PyArray_IS_F_CONTIGUOUS((PyArrayObject*) self)) { + /* if the array if Fortran-contiguous and not C-contiguous, + * the PickleBuffer instance will hold a view on the transpose + * of the initial array, that is C-contiguous. */ + order = 'F'; + transposed_array = PyArray_Transpose((PyArrayObject*)self, NULL); + picklebuf_args = Py_BuildValue("(N)", transposed_array); + } + else { + order = 'C'; + picklebuf_args = Py_BuildValue("(O)", self); + } + if (picklebuf_args == NULL) { + Py_DECREF(picklebuf_class); + return NULL; + } + + buffer = PyObject_CallObject(picklebuf_class, picklebuf_args); + Py_DECREF(picklebuf_class); + Py_DECREF(picklebuf_args); + if (buffer == NULL) { + /* Some arrays may refuse to export a buffer, in which case + * just fall back on regular __reduce_ex__ implementation + * (gh-12745). + */ + PyErr_Clear(); + return array_reduce_ex_regular(self, protocol); + } + + /* Get the _frombuffer() function for reconstruction */ + + numeric_mod = PyImport_ImportModule("numpy.core.numeric"); + if (numeric_mod == NULL) { + Py_DECREF(buffer); + return NULL; + } + from_buffer_func = PyObject_GetAttrString(numeric_mod, + "_frombuffer"); + Py_DECREF(numeric_mod); + if (from_buffer_func == NULL) { + Py_DECREF(buffer); + return NULL; + } + + return Py_BuildValue("N(NONN)", + from_buffer_func, buffer, (PyObject *)descr, + PyObject_GetAttrString((PyObject *)self, "shape"), + PyUnicode_FromStringAndSize(&order, 1)); +} + +static PyObject * +array_reduce_ex(PyArrayObject *self, PyObject *args) +{ + int protocol; + PyArray_Descr *descr = NULL; + + if (!PyArg_ParseTuple(args, "i", &protocol)) { + return NULL; + } + + descr = PyArray_DESCR(self); + if ((protocol < 5) || + (!PyArray_IS_C_CONTIGUOUS((PyArrayObject*)self) && + !PyArray_IS_F_CONTIGUOUS((PyArrayObject*)self)) || + PyDataType_FLAGCHK(descr, NPY_ITEM_HASOBJECT) || + (PyType_IsSubtype(((PyObject*)self)->ob_type, &PyArray_Type) && + ((PyObject*)self)->ob_type != &PyArray_Type) || + descr->elsize == 0) { + /* The PickleBuffer class from version 5 of the pickle protocol + * can only be used for arrays backed by a contiguous data buffer. + * For all other cases we fallback to the generic array_reduce + * method that involves using a temporary bytes allocation. */ + return array_reduce_ex_regular(self, protocol); + } + else { + return array_reduce_ex_picklebuffer(self, protocol); + } +} + static PyObject * array_setstate(PyArrayObject *self, PyObject *args) { @@ -1633,7 +1934,7 @@ array_setstate(PyArrayObject *self, PyObject *args) PyObject *rawdata = NULL; char *datastr; Py_ssize_t len; - npy_intp size, dimensions[NPY_MAXDIMS]; + npy_intp dimensions[NPY_MAXDIMS]; int nd; npy_intp nbytes; int overflowed; @@ -1670,6 +1971,12 @@ array_setstate(PyArrayObject *self, PyObject *args) return NULL; } + /* + * Reassigning fa->descr messes with the reallocation strategy, + * since fa could be a 0-d or scalar, and then + * PyDataMem_UserFREE will be confused + */ + size_t n_tofree = PyArray_NBYTES_ALLOCATED(self); Py_XDECREF(PyArray_DESCR(self)); fa->descr = typecode; Py_INCREF(typecode); @@ -1677,17 +1984,39 @@ array_setstate(PyArrayObject *self, PyObject *args) if (nd < 0) { return NULL; } - size = PyArray_MultiplyList(dimensions, nd); - if (size < 0) { - /* More items than are addressable */ - return PyErr_NoMemory(); + /* + * We should do two things here: + * 1. Validate the input, that it is neither invalid, nor "too big" + * ("too big" ignores dimensios of size 0). + * 2. Find `PyArray_NBYTES` of the result, as this is what we may need to + * copy from the pickled data (may not match allocation currently if 0). + * Compare with `PyArray_NewFromDescr`, raise MemoryError for simplicity. + */ + npy_bool empty = NPY_FALSE; + nbytes = 1; + for (int i = 0; i < nd; i++) { + if (dimensions[i] < 0) { + PyErr_SetString(PyExc_TypeError, + "impossible dimension while unpickling array"); + return NULL; + } + if (dimensions[i] == 0) { + empty = NPY_TRUE; + } + overflowed = npy_mul_with_overflow_intp( + &nbytes, nbytes, dimensions[i]); + if (overflowed) { + return PyErr_NoMemory(); + } } overflowed = npy_mul_with_overflow_intp( - &nbytes, size, PyArray_DESCR(self)->elsize); + &nbytes, nbytes, PyArray_DESCR(self)->elsize); if (overflowed) { - /* More bytes than are addressable */ return PyErr_NoMemory(); } + if (empty) { + nbytes = 0; + } if (PyDataType_FLAGCHK(typecode, NPY_LIST_PICKLE)) { if (!PyList_Check(rawdata)) { @@ -1699,7 +2028,6 @@ array_setstate(PyArrayObject *self, PyObject *args) else { Py_INCREF(rawdata); -#if defined(NPY_PY3K) /* Backward compatibility with Python 2 NumPy pickles */ if (PyUnicode_Check(rawdata)) { PyObject *tmp; @@ -1714,7 +2042,6 @@ array_setstate(PyArrayObject *self, PyObject *args) return NULL; } } -#endif if (!PyBytes_Check(rawdata)) { PyErr_SetString(PyExc_TypeError, @@ -1730,15 +2057,25 @@ array_setstate(PyArrayObject *self, PyObject *args) if (len != nbytes) { PyErr_SetString(PyExc_ValueError, - "buffer size does not" \ - " match array size"); + "buffer size does not match array size"); Py_DECREF(rawdata); return NULL; } } if ((PyArray_FLAGS(self) & NPY_ARRAY_OWNDATA)) { - PyDataMem_FREE(PyArray_DATA(self)); + /* + * Allocation will never be 0, see comment in ctors.c + * line 820 + */ + PyObject *handler = PyArray_HANDLER(self); + if (handler == NULL) { + /* This can happen if someone arbitrarily sets NPY_ARRAY_OWNDATA */ + PyErr_SetString(PyExc_RuntimeError, + "no memory handler found but OWNDATA flag set"); + return NULL; + } + PyDataMem_UserFREE(PyArray_DATA(self), n_tofree, handler); PyArray_CLEARFLAGS(self, NPY_ARRAY_OWNDATA); } Py_XDECREF(PyArray_BASE(self)); @@ -1757,12 +2094,14 @@ array_setstate(PyArrayObject *self, PyObject *args) fa->nd = nd; if (nd > 0) { - fa->dimensions = npy_alloc_cache_dim(3*nd); + fa->dimensions = npy_alloc_cache_dim(2 * nd); if (fa->dimensions == NULL) { return PyErr_NoMemory(); } fa->strides = PyArray_DIMS(self) + nd; - memcpy(PyArray_DIMS(self), dimensions, sizeof(npy_intp)*nd); + if (nd) { + memcpy(PyArray_DIMS(self), dimensions, sizeof(npy_intp)*nd); + } _array_fill_strides(PyArray_STRIDES(self), dimensions, nd, PyArray_DESCR(self)->elsize, (is_f_order ? NPY_ARRAY_F_CONTIGUOUS : @@ -1772,20 +2111,21 @@ array_setstate(PyArrayObject *self, PyObject *args) if (!PyDataType_FLAGCHK(typecode, NPY_LIST_PICKLE)) { int swap = PyArray_ISBYTESWAPPED(self); - fa->data = datastr; -#ifndef NPY_PY3K - /* Check that the string is not interned */ - if (!_IsAligned(self) || swap || PyString_CHECK_INTERNED(rawdata)) { -#else /* Bytes should always be considered immutable, but we just grab the * pointer if they are large, to save memory. */ - if (!_IsAligned(self) || swap || (len <= 1000)) { -#endif - npy_intp num = PyArray_NBYTES(self); - fa->data = PyDataMem_NEW(num); + if (!IsAligned(self) || swap || (len <= 1000)) { + npy_intp num = PyArray_NBYTES_ALLOCATED(self); + /* Store the handler in case the default is modified */ + Py_XDECREF(fa->mem_handler); + fa->mem_handler = PyDataMem_GetHandler(); + if (fa->mem_handler == NULL) { + Py_CLEAR(fa->mem_handler); + Py_DECREF(rawdata); + return NULL; + } + fa->data = PyDataMem_UserNEW(num, PyArray_HANDLER(self)); if (PyArray_DATA(self) == NULL) { - fa->nd = 0; - npy_free_cache_dim_array(self); + Py_CLEAR(fa->mem_handler); Py_DECREF(rawdata); return PyErr_NoMemory(); } @@ -1796,7 +2136,9 @@ array_setstate(PyArrayObject *self, PyObject *args) PyArray_DESCR(self)->elsize, datastr, PyArray_DESCR(self)->elsize, numels, 1, self); - if (!PyArray_ISEXTENDED(self)) { + if (!(PyArray_ISEXTENDED(self) || + PyArray_DESCR(self)->metadata || + PyArray_DESCR(self)->c_metadata)) { fa->descr = PyArray_DescrFromType( PyArray_DESCR(self)->type_num); } @@ -1819,17 +2161,28 @@ array_setstate(PyArrayObject *self, PyObject *args) Py_DECREF(rawdata); } else { + /* The handlers should never be called in this case */ + Py_XDECREF(fa->mem_handler); + fa->mem_handler = NULL; + fa->data = datastr; if (PyArray_SetBaseObject(self, rawdata) < 0) { + Py_DECREF(rawdata); return NULL; } } } else { - fa->data = PyDataMem_NEW(PyArray_NBYTES(self)); + npy_intp num = PyArray_NBYTES_ALLOCATED(self); + + /* Store the functions in case the default handler is modified */ + Py_XDECREF(fa->mem_handler); + fa->mem_handler = PyDataMem_GetHandler(); + if (fa->mem_handler == NULL) { + return NULL; + } + fa->data = PyDataMem_UserNEW(num, PyArray_HANDLER(self)); if (PyArray_DATA(self) == NULL) { - fa->nd = 0; - fa->data = PyDataMem_NEW(PyArray_DESCR(self)->elsize); - npy_free_cache_dim_array(self); + Py_CLEAR(fa->mem_handler); return PyErr_NoMemory(); } if (PyDataType_FLAGCHK(PyArray_DESCR(self), NPY_NEEDS_INIT)) { @@ -1851,37 +2204,22 @@ array_setstate(PyArrayObject *self, PyObject *args) NPY_NO_EXPORT int PyArray_Dump(PyObject *self, PyObject *file, int protocol) { - PyObject *cpick = NULL; + static PyObject *method = NULL; PyObject *ret; - if (protocol < 0) { - protocol = 2; - } - -#if defined(NPY_PY3K) - cpick = PyImport_ImportModule("pickle"); -#else - cpick = PyImport_ImportModule("cPickle"); -#endif - if (cpick == NULL) { + npy_cache_import("numpy.core._methods", "_dump", &method); + if (method == NULL) { return -1; } - if (PyBytes_Check(file) || PyUnicode_Check(file)) { - file = npy_PyFile_OpenFile(file, "wb"); - if (file == NULL) { - Py_DECREF(cpick); - return -1; - } + if (protocol < 0) { + ret = PyObject_CallFunction(method, "OO", self, file); } else { - Py_INCREF(file); + ret = PyObject_CallFunction(method, "OOi", self, file, protocol); } - ret = PyObject_CallMethod(cpick, "dump", "OOi", self, file, protocol); - Py_XDECREF(ret); - Py_DECREF(file); - Py_DECREF(cpick); - if (PyErr_Occurred()) { + if (ret == NULL) { return -1; } + Py_DECREF(ret); return 0; } @@ -1889,57 +2227,39 @@ PyArray_Dump(PyObject *self, PyObject *file, int protocol) NPY_NO_EXPORT PyObject * PyArray_Dumps(PyObject *self, int protocol) { - PyObject *cpick = NULL; - PyObject *ret; + static PyObject *method = NULL; + npy_cache_import("numpy.core._methods", "_dumps", &method); + if (method == NULL) { + return NULL; + } if (protocol < 0) { - protocol = 2; + return PyObject_CallFunction(method, "O", self); } -#if defined(NPY_PY3K) - cpick = PyImport_ImportModule("pickle"); -#else - cpick = PyImport_ImportModule("cPickle"); -#endif - if (cpick == NULL) { - return NULL; + else { + return PyObject_CallFunction(method, "Oi", self, protocol); } - ret = PyObject_CallMethod(cpick, "dumps", "Oi", self, protocol); - Py_DECREF(cpick); - return ret; } static PyObject * -array_dump(PyArrayObject *self, PyObject *args) +array_dump(PyArrayObject *self, PyObject *args, PyObject *kwds) { - PyObject *file = NULL; - int ret; - - if (!PyArg_ParseTuple(args, "O:dump", &file)) { - return NULL; - } - ret = PyArray_Dump((PyObject *)self, file, 2); - if (ret < 0) { - return NULL; - } - Py_RETURN_NONE; + NPY_FORWARD_NDARRAY_METHOD("_dump"); } static PyObject * -array_dumps(PyArrayObject *self, PyObject *args) +array_dumps(PyArrayObject *self, PyObject *args, PyObject *kwds) { - if (!PyArg_ParseTuple(args, "")) { - return NULL; - } - return PyArray_Dumps((PyObject *)self, 2); + NPY_FORWARD_NDARRAY_METHOD("_dumps"); } static PyObject * -array_sizeof(PyArrayObject *self) +array_sizeof(PyArrayObject *self, PyObject *NPY_UNUSED(args)) { /* object + dimension and strides */ - Py_ssize_t nbytes = NPY_SIZEOF_PYARRAYOBJECT + + Py_ssize_t nbytes = Py_TYPE(self)->tp_basicsize + PyArray_NDIM(self) * sizeof(npy_intp) * 2; if (PyArray_CHKFLAGS(self, NPY_ARRAY_OWNDATA)) { nbytes += PyArray_NBYTES(self); @@ -2044,14 +2364,17 @@ array_cumprod(PyArrayObject *self, PyObject *args, PyObject *kwds) static PyObject * -array_dot(PyArrayObject *self, PyObject *args, PyObject *kwds) +array_dot(PyArrayObject *self, + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) { PyObject *a = (PyObject *)self, *b, *o = NULL; PyArrayObject *ret; - char* kwlist[] = {"b", "out", NULL }; - + NPY_PREPARE_ARGPARSER; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:dot", kwlist, &b, &o)) { + if (npy_parse_arguments("dot", args, len_args, kwnames, + "b", NULL, &b, + "|out", NULL, &o, + NULL, NULL, NULL) < 0) { return NULL; } @@ -2109,8 +2432,16 @@ array_compress(PyArrayObject *self, PyObject *args, PyObject *kwds) PyArray_OutputConverter, &out)) { return NULL; } - return PyArray_Return( - (PyArrayObject *)PyArray_Compress(self, condition, axis, out)); + + PyObject *ret = PyArray_Compress(self, condition, axis, out); + + /* this matches the unpacking behavior of ufuncs */ + if (out == NULL) { + return PyArray_Return((PyArrayObject *)ret); + } + else { + return ret; + } } @@ -2125,27 +2456,37 @@ array_nonzero(PyArrayObject *self, PyObject *args) static PyObject * -array_trace(PyArrayObject *self, PyObject *args, PyObject *kwds) +array_trace(PyArrayObject *self, + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) { int axis1 = 0, axis2 = 1, offset = 0; PyArray_Descr *dtype = NULL; PyArrayObject *out = NULL; int rtype; - static char *kwlist[] = {"offset", "axis1", "axis2", "dtype", "out", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iiiO&O&:trace", kwlist, - &offset, - &axis1, - &axis2, - PyArray_DescrConverter2, &dtype, - PyArray_OutputConverter, &out)) { + NPY_PREPARE_ARGPARSER; + + if (npy_parse_arguments("trace", args, len_args, kwnames, + "|offset", &PyArray_PythonPyIntFromInt, &offset, + "|axis1", &PyArray_PythonPyIntFromInt, &axis1, + "|axis2", &PyArray_PythonPyIntFromInt, &axis2, + "|dtype", &PyArray_DescrConverter2, &dtype, + "|out", &PyArray_OutputConverter, &out, + NULL, NULL, NULL) < 0) { Py_XDECREF(dtype); return NULL; } rtype = _CHKTYPENUM(dtype); Py_XDECREF(dtype); - return PyArray_Return((PyArrayObject *)PyArray_Trace(self, offset, axis1, axis2, rtype, out)); + PyObject *ret = PyArray_Trace(self, offset, axis1, axis2, rtype, out); + + /* this matches the unpacking behavior of ufuncs */ + if (out == NULL) { + return PyArray_Return((PyArrayObject *)ret); + } + else { + return ret; + } } #undef _CHKTYPENUM @@ -2154,28 +2495,13 @@ array_trace(PyArrayObject *self, PyObject *args, PyObject *kwds) static PyObject * array_clip(PyArrayObject *self, PyObject *args, PyObject *kwds) { - PyObject *min = NULL, *max = NULL; - PyArrayObject *out = NULL; - static char *kwlist[] = {"min", "max", "out", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOO&:clip", kwlist, - &min, - &max, - PyArray_OutputConverter, &out)) { - return NULL; - } - if (max == NULL && min == NULL) { - PyErr_SetString(PyExc_ValueError, "One of max or min must be given."); - return NULL; - } - return PyArray_Return((PyArrayObject *)PyArray_Clip(self, min, max, out)); + NPY_FORWARD_NDARRAY_METHOD("_clip"); } static PyObject * array_conjugate(PyArrayObject *self, PyObject *args) { - PyArrayObject *out = NULL; if (!PyArg_ParseTuple(args, "|O&:conjugate", PyArray_OutputConverter, @@ -2206,13 +2532,15 @@ array_diagonal(PyArrayObject *self, PyObject *args, PyObject *kwds) static PyObject * -array_flatten(PyArrayObject *self, PyObject *args, PyObject *kwds) +array_flatten(PyArrayObject *self, + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) { NPY_ORDER order = NPY_CORDER; - static char *kwlist[] = {"order", NULL}; + NPY_PREPARE_ARGPARSER; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&:flatten", kwlist, - PyArray_OrderConverter, &order)) { + if (npy_parse_arguments("flatten", args, len_args, kwnames, + "|order", PyArray_OrderConverter, &order, + NULL, NULL, NULL) < 0) { return NULL; } return PyArray_Flatten(self, order); @@ -2220,13 +2548,15 @@ array_flatten(PyArrayObject *self, PyObject *args, PyObject *kwds) static PyObject * -array_ravel(PyArrayObject *self, PyObject *args, PyObject *kwds) +array_ravel(PyArrayObject *self, + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) { NPY_ORDER order = NPY_CORDER; - static char *kwlist[] = {"order", NULL}; + NPY_PREPARE_ARGPARSER; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&:ravel", kwlist, - PyArray_OrderConverter, &order)) { + if (npy_parse_arguments("ravel", args, len_args, kwnames, + "|order", PyArray_OrderConverter, &order, + NULL, NULL, NULL) < 0) { return NULL; } return PyArray_Ravel(self, order); @@ -2245,7 +2575,16 @@ array_round(PyArrayObject *self, PyObject *args, PyObject *kwds) PyArray_OutputConverter, &out)) { return NULL; } - return PyArray_Return((PyArrayObject *)PyArray_Round(self, decimals, out)); + + PyObject *ret = PyArray_Round(self, decimals, out); + + /* this matches the unpacking behavior of ufuncs */ + if (out == NULL) { + return PyArray_Return((PyArrayObject *)ret); + } + else { + return ret; + } } @@ -2271,7 +2610,7 @@ array_setflags(PyArrayObject *self, PyObject *args, PyObject *kwds) if (PyObject_Not(align_flag)) { PyArray_CLEARFLAGS(self, NPY_ARRAY_ALIGNED); } - else if (_IsAligned(self)) { + else if (IsAligned(self)) { PyArray_ENABLEFLAGS(self, NPY_ARRAY_ALIGNED); } else { @@ -2301,7 +2640,24 @@ array_setflags(PyArrayObject *self, PyObject *args, PyObject *kwds) if (write_flag != Py_None) { if (PyObject_IsTrue(write_flag)) { if (_IsWriteable(self)) { + /* + * _IsWritable (and PyArray_UpdateFlags) allows flipping this, + * although the C-Api user who created the array may have + * chosen to make it non-writable for a good reason, so + * deprecate. + */ + if ((PyArray_BASE(self) == NULL) && + !PyArray_CHKFLAGS(self, NPY_ARRAY_OWNDATA) && + !PyArray_CHKFLAGS(self, NPY_ARRAY_WRITEABLE)) { + /* 2017-05-03, NumPy 1.17.0 */ + if (DEPRECATE("making a non-writeable array writeable " + "is deprecated for arrays without a base " + "which do not own their data.") < 0) { + return NULL; + } + } PyArray_ENABLEFLAGS(self, NPY_ARRAY_WRITEABLE); + PyArray_CLEARFLAGS(self, NPY_ARRAY_WARN_ON_WRITE); } else { fa->flags = flagback; @@ -2314,9 +2670,9 @@ array_setflags(PyArrayObject *self, PyObject *args, PyObject *kwds) } else { PyArray_CLEARFLAGS(self, NPY_ARRAY_WRITEABLE); + PyArray_CLEARFLAGS(self, NPY_ARRAY_WARN_ON_WRITE); } } - Py_RETURN_NONE; } @@ -2345,9 +2701,10 @@ array_complex(PyArrayObject *self, PyObject *NPY_UNUSED(args)) PyArrayObject *arr; PyArray_Descr *dtype; PyObject *c; + if (PyArray_SIZE(self) != 1) { - PyErr_SetString(PyExc_TypeError, "only length-1 arrays can "\ - "be converted to Python scalars"); + PyErr_SetString(PyExc_TypeError, + "only length-1 arrays can be converted to Python scalars"); return NULL; } @@ -2358,38 +2715,18 @@ array_complex(PyArrayObject *self, PyObject *NPY_UNUSED(args)) if (!PyArray_CanCastArrayTo(self, dtype, NPY_SAME_KIND_CASTING) && !(PyArray_TYPE(self) == NPY_OBJECT)) { - PyObject *err, *msg_part; + PyObject *descr = (PyObject*)PyArray_DESCR(self); + Py_DECREF(dtype); - err = PyString_FromString("unable to convert "); - if (err == NULL) { - return NULL; - } - msg_part = PyObject_Repr((PyObject*)PyArray_DESCR(self)); - if (msg_part == NULL) { - Py_DECREF(err); - return NULL; - } - PyString_ConcatAndDel(&err, msg_part); - if (err == NULL) { - return NULL; - } - msg_part = PyString_FromString(", to complex."); - if (msg_part == NULL) { - Py_DECREF(err); - return NULL; - } - PyString_ConcatAndDel(&err, msg_part); - if (err == NULL) { - return NULL; - } - PyErr_SetObject(PyExc_TypeError, err); - Py_DECREF(err); + PyErr_Format(PyExc_TypeError, + "Unable to convert %R to complex", descr); return NULL; } if (PyArray_TYPE(self) == NPY_OBJECT) { /* let python try calling __complex__ on the object. */ PyObject *args, *res; + Py_DECREF(dtype); args = Py_BuildValue("(O)", *((PyObject**)PyArray_DATA(self))); if (args == NULL) { @@ -2409,50 +2746,29 @@ array_complex(PyArrayObject *self, PyObject *NPY_UNUSED(args)) return c; } -#ifndef NPY_PY3K - -static PyObject * -array_getslice(PyArrayObject *self, PyObject *args) -{ - PyObject *start, *stop, *slice, *result; - if (!PyArg_ParseTuple(args, "OO:__getslice__", &start, &stop)) { - return NULL; - } - - slice = PySlice_New(start, stop, NULL); - if (slice == NULL) { - return NULL; - } - - /* Deliberately delegate to subclasses */ - result = PyObject_GetItem((PyObject *)self, slice); - Py_DECREF(slice); - return result; -} - static PyObject * -array_setslice(PyArrayObject *self, PyObject *args) +array_class_getitem(PyObject *cls, PyObject *args) { - PyObject *start, *stop, *value, *slice; - if (!PyArg_ParseTuple(args, "OOO:__setslice__", &start, &stop, &value)) { - return NULL; - } + PyObject *generic_alias; - slice = PySlice_New(start, stop, NULL); - if (slice == NULL) { - return NULL; - } +#ifdef Py_GENERICALIASOBJECT_H + Py_ssize_t args_len; - /* Deliberately delegate to subclasses */ - if (PyObject_SetItem((PyObject *)self, slice, value) < 0) { - Py_DECREF(slice); - return NULL; + args_len = PyTuple_Check(args) ? PyTuple_Size(args) : 1; + if (args_len != 2) { + return PyErr_Format(PyExc_TypeError, + "Too %s arguments for %s", + args_len > 2 ? "many" : "few", + ((PyTypeObject *)cls)->tp_name); } - Py_DECREF(slice); - Py_RETURN_NONE; -} - + generic_alias = Py_GenericAlias(cls, args); +#else + PyErr_SetString(PyExc_TypeError, + "Type subscription requires python >= 3.9"); + generic_alias = NULL; #endif + return generic_alias; +} NPY_NO_EXPORT PyMethodDef array_methods[] = { @@ -2469,12 +2785,9 @@ NPY_NO_EXPORT PyMethodDef array_methods[] = { {"__array_ufunc__", (PyCFunction)array_ufunc, METH_VARARGS | METH_KEYWORDS, NULL}, - -#ifndef NPY_PY3K - {"__unicode__", - (PyCFunction)array_unicode, - METH_NOARGS, NULL}, -#endif + {"__array_function__", + (PyCFunction)array_function, + METH_VARARGS | METH_KEYWORDS, NULL}, /* for the sys module */ {"__sizeof__", @@ -2493,15 +2806,18 @@ NPY_NO_EXPORT PyMethodDef array_methods[] = { {"__reduce__", (PyCFunction) array_reduce, METH_VARARGS, NULL}, + {"__reduce_ex__", + (PyCFunction) array_reduce_ex, + METH_VARARGS, NULL}, {"__setstate__", (PyCFunction) array_setstate, METH_VARARGS, NULL}, {"dumps", (PyCFunction) array_dumps, - METH_VARARGS, NULL}, + METH_VARARGS | METH_KEYWORDS, NULL}, {"dump", (PyCFunction) array_dump, - METH_VARARGS, NULL}, + METH_VARARGS | METH_KEYWORDS, NULL}, {"__complex__", (PyCFunction) array_complex, @@ -2511,22 +2827,10 @@ NPY_NO_EXPORT PyMethodDef array_methods[] = { (PyCFunction) array_format, METH_VARARGS, NULL}, -#ifndef NPY_PY3K - /* - * While we could put these in `tp_sequence`, its' easier to define them - * in terms of PyObject* arguments. - * - * We must provide these for compatibility with code that calls them - * directly. They are already deprecated at a language level in python 2.7, - * but are removed outright in python 3. - */ - {"__getslice__", - (PyCFunction) array_getslice, - METH_VARARGS, NULL}, - {"__setslice__", - (PyCFunction) array_setslice, - METH_VARARGS, NULL}, -#endif + /* for typing; requires python >= 3.9 */ + {"__class_getitem__", + (PyCFunction)array_class_getitem, + METH_CLASS | METH_O, NULL}, /* Original and Extended methods added 2005 */ {"all", @@ -2537,19 +2841,19 @@ NPY_NO_EXPORT PyMethodDef array_methods[] = { METH_VARARGS | METH_KEYWORDS, NULL}, {"argmax", (PyCFunction)array_argmax, - METH_VARARGS | METH_KEYWORDS, NULL}, + METH_FASTCALL | METH_KEYWORDS, NULL}, {"argmin", (PyCFunction)array_argmin, - METH_VARARGS | METH_KEYWORDS, NULL}, + METH_FASTCALL | METH_KEYWORDS, NULL}, {"argpartition", (PyCFunction)array_argpartition, - METH_VARARGS | METH_KEYWORDS, NULL}, + METH_FASTCALL | METH_KEYWORDS, NULL}, {"argsort", (PyCFunction)array_argsort, - METH_VARARGS | METH_KEYWORDS, NULL}, + METH_FASTCALL | METH_KEYWORDS, NULL}, {"astype", (PyCFunction)array_astype, - METH_VARARGS | METH_KEYWORDS, NULL}, + METH_FASTCALL | METH_KEYWORDS, NULL}, {"byteswap", (PyCFunction)array_byteswap, METH_VARARGS | METH_KEYWORDS, NULL}, @@ -2570,7 +2874,7 @@ NPY_NO_EXPORT PyMethodDef array_methods[] = { METH_VARARGS, NULL}, {"copy", (PyCFunction)array_copy, - METH_VARARGS | METH_KEYWORDS, NULL}, + METH_FASTCALL | METH_KEYWORDS, NULL}, {"cumprod", (PyCFunction)array_cumprod, METH_VARARGS | METH_KEYWORDS, NULL}, @@ -2582,13 +2886,13 @@ NPY_NO_EXPORT PyMethodDef array_methods[] = { METH_VARARGS | METH_KEYWORDS, NULL}, {"dot", (PyCFunction)array_dot, - METH_VARARGS | METH_KEYWORDS, NULL}, + METH_FASTCALL | METH_KEYWORDS, NULL}, {"fill", (PyCFunction)array_fill, METH_VARARGS, NULL}, {"flatten", (PyCFunction)array_flatten, - METH_VARARGS | METH_KEYWORDS, NULL}, + METH_FASTCALL | METH_KEYWORDS, NULL}, {"getfield", (PyCFunction)array_getfield, METH_VARARGS | METH_KEYWORDS, NULL}, @@ -2615,7 +2919,7 @@ NPY_NO_EXPORT PyMethodDef array_methods[] = { METH_VARARGS, NULL}, {"partition", (PyCFunction)array_partition, - METH_VARARGS | METH_KEYWORDS, NULL}, + METH_FASTCALL | METH_KEYWORDS, NULL}, {"prod", (PyCFunction)array_prod, METH_VARARGS | METH_KEYWORDS, NULL}, @@ -2627,7 +2931,7 @@ NPY_NO_EXPORT PyMethodDef array_methods[] = { METH_VARARGS | METH_KEYWORDS, NULL}, {"ravel", (PyCFunction)array_ravel, - METH_VARARGS | METH_KEYWORDS, NULL}, + METH_FASTCALL | METH_KEYWORDS, NULL}, {"repeat", (PyCFunction)array_repeat, METH_VARARGS | METH_KEYWORDS, NULL}, @@ -2642,7 +2946,7 @@ NPY_NO_EXPORT PyMethodDef array_methods[] = { METH_VARARGS | METH_KEYWORDS, NULL}, {"searchsorted", (PyCFunction)array_searchsorted, - METH_VARARGS | METH_KEYWORDS, NULL}, + METH_FASTCALL | METH_KEYWORDS, NULL}, {"setfield", (PyCFunction)array_setfield, METH_VARARGS | METH_KEYWORDS, NULL}, @@ -2651,10 +2955,10 @@ NPY_NO_EXPORT PyMethodDef array_methods[] = { METH_VARARGS | METH_KEYWORDS, NULL}, {"sort", (PyCFunction)array_sort, - METH_VARARGS | METH_KEYWORDS, NULL}, + METH_FASTCALL | METH_KEYWORDS, NULL}, {"squeeze", (PyCFunction)array_squeeze, - METH_VARARGS | METH_KEYWORDS, NULL}, + METH_FASTCALL | METH_KEYWORDS, NULL}, {"std", (PyCFunction)array_stddev, METH_VARARGS | METH_KEYWORDS, NULL}, @@ -2666,7 +2970,7 @@ NPY_NO_EXPORT PyMethodDef array_methods[] = { METH_VARARGS, NULL}, {"take", (PyCFunction)array_take, - METH_VARARGS | METH_KEYWORDS, NULL}, + METH_FASTCALL | METH_KEYWORDS, NULL}, {"tobytes", (PyCFunction)array_tobytes, METH_VARARGS | METH_KEYWORDS, NULL}, @@ -2677,11 +2981,11 @@ NPY_NO_EXPORT PyMethodDef array_methods[] = { (PyCFunction)array_tolist, METH_VARARGS, NULL}, {"tostring", - (PyCFunction)array_tobytes, + (PyCFunction)array_tostring, METH_VARARGS | METH_KEYWORDS, NULL}, {"trace", (PyCFunction)array_trace, - METH_VARARGS | METH_KEYWORDS, NULL}, + METH_FASTCALL | METH_KEYWORDS, NULL}, {"transpose", (PyCFunction)array_transpose, METH_VARARGS, NULL}, @@ -2690,6 +2994,14 @@ NPY_NO_EXPORT PyMethodDef array_methods[] = { METH_VARARGS | METH_KEYWORDS, NULL}, {"view", (PyCFunction)array_view, - METH_VARARGS | METH_KEYWORDS, NULL}, + METH_FASTCALL | METH_KEYWORDS, NULL}, + // For data interchange between libraries + {"__dlpack__", + (PyCFunction)array_dlpack, + METH_FASTCALL | METH_KEYWORDS, NULL}, + + {"__dlpack_device__", + (PyCFunction)array_dlpack_device, + METH_NOARGS, NULL}, {NULL, NULL, 0, NULL} /* sentinel */ }; diff --git a/numpy/core/src/multiarray/methods.h b/numpy/core/src/multiarray/methods.h index 7bf87f42d53f..bcada0fea897 100644 --- a/numpy/core/src/multiarray/methods.h +++ b/numpy/core/src/multiarray/methods.h @@ -1,9 +1,34 @@ -#ifndef _NPY_ARRAY_METHODS_H_ -#define _NPY_ARRAY_METHODS_H_ +#ifndef NUMPY_CORE_SRC_MULTIARRAY_METHODS_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_METHODS_H_ + +#include "npy_import.h" extern NPY_NO_EXPORT PyMethodDef array_methods[]; -NPY_NO_EXPORT const char * -npy_casting_to_string(NPY_CASTING casting); -#endif +/* + * Pathlib support, takes a borrowed reference and returns a new one. + * The new object may be the same as the old. + */ +static inline PyObject * +NpyPath_PathlikeToFspath(PyObject *file) +{ + static PyObject *os_PathLike = NULL; + static PyObject *os_fspath = NULL; + npy_cache_import("numpy.compat", "os_PathLike", &os_PathLike); + if (os_PathLike == NULL) { + return NULL; + } + npy_cache_import("numpy.compat", "os_fspath", &os_fspath); + if (os_fspath == NULL) { + return NULL; + } + + if (!PyObject_IsInstance(file, os_PathLike)) { + Py_INCREF(file); + return file; + } + return PyObject_CallFunctionObjArgs(os_fspath, file, NULL); +} + +#endif /* NUMPY_CORE_SRC_MULTIARRAY_METHODS_H_ */ diff --git a/numpy/core/src/multiarray/multiarraymodule.c b/numpy/core/src/multiarray/multiarraymodule.c index f78a748c0779..576c39f5d9ec 100644 --- a/numpy/core/src/multiarray/multiarraymodule.c +++ b/numpy/core/src/multiarray/multiarraymodule.c @@ -11,28 +11,33 @@ oliphant@ee.byu.edu Brigham Young University */ - -/* $Id: multiarraymodule.c,v 1.36 2005/09/14 00:14:00 teoliphant Exp $ */ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _UMATHMODULE +#define _MULTIARRAYMODULE #define PY_SSIZE_T_CLEAN -#include "Python.h" -#include "structmember.h" +#include <Python.h> +#include <structmember.h> -#define NPY_NO_DEPRECATED_API NPY_API_VERSION -#define _MULTIARRAYMODULE #include <numpy/npy_common.h> #include "numpy/arrayobject.h" #include "numpy/arrayscalars.h" #include "numpy/npy_math.h" - +#include "npy_argparse.h" #include "npy_config.h" #include "npy_pycompat.h" #include "npy_import.h" +#include "convert_datatype.h" +#include "legacy_dtype_implementation.h" NPY_NO_EXPORT int NPY_NUMUSERTYPES = 0; /* Internal APIs */ +#include "alloc.h" +#include "abstractdtypes.h" +#include "array_coercion.h" +#include "arrayfunction_override.h" #include "arraytypes.h" #include "arrayobject.h" #include "hashdescr.h" @@ -54,25 +59,38 @@ NPY_NO_EXPORT int NPY_NUMUSERTYPES = 0; #include "ctors.h" #include "array_assign.h" #include "common.h" -#include "ufunc_override.h" #include "multiarraymodule.h" #include "cblasfuncs.h" #include "vdot.h" #include "templ_common.h" /* for npy_mul_with_overflow_intp */ #include "compiled_base.h" #include "mem_overlap.h" -#include "alloc.h" #include "typeinfo.h" #include "get_attr_string.h" +#include "experimental_public_dtype_api.h" /* _get_experimental_dtype_api */ + +#include "npy_dlpack.h" + +/* + ***************************************************************************** + ** INCLUDE GENERATED CODE ** + ***************************************************************************** + */ +#include "funcs.inc" +#include "umathmodule.h" + +NPY_NO_EXPORT int initscalarmath(PyObject *); +NPY_NO_EXPORT int set_matmul_flags(PyObject *d); /* in ufunc_object.c */ /* * global variable to determine if legacy printing is enabled, accessible from - * C. For simplicity the mode is encoded as an integer where '0' means no - * legacy mode, and '113' means 1.13 legacy mode. We can upgrade this if we - * have more complex requirements in the future. + * C. For simplicity the mode is encoded as an integer where INT_MAX means no + * legacy mode, and '113'/'121' means 1.13/1.21 legacy mode; and 0 maps to + * INT_MAX. We can upgrade this if we have more complex requirements in the + * future. */ -int npy_legacy_print_mode = 0; +int npy_legacy_print_mode = INT_MAX; static PyObject * set_legacy_print_mode(PyObject *NPY_UNUSED(self), PyObject *args) @@ -80,6 +98,9 @@ set_legacy_print_mode(PyObject *NPY_UNUSED(self), PyObject *args) if (!PyArg_ParseTuple(args, "i", &npy_legacy_print_mode)) { return NULL; } + if (!npy_legacy_print_mode) { + npy_legacy_print_mode = INT_MAX; + } Py_RETURN_NONE; } @@ -106,6 +127,9 @@ PyArray_GetPriority(PyObject *obj, double default_) ret = PyArray_LookupSpecial_OnInstance(obj, "__array_priority__"); if (ret == NULL) { + if (PyErr_Occurred()) { + PyErr_Clear(); /* TODO[gh-14801]: propagate crashes during attribute access? */ + } return default_; } @@ -118,7 +142,7 @@ PyArray_GetPriority(PyObject *obj, double default_) * Multiply a List of ints */ NPY_NO_EXPORT int -PyArray_MultiplyIntList(int *l1, int n) +PyArray_MultiplyIntList(int const *l1, int n) { int s = 1; @@ -132,7 +156,7 @@ PyArray_MultiplyIntList(int *l1, int n) * Multiply a List */ NPY_NO_EXPORT npy_intp -PyArray_MultiplyList(npy_intp *l1, int n) +PyArray_MultiplyList(npy_intp const *l1, int n) { npy_intp s = 1; @@ -146,7 +170,7 @@ PyArray_MultiplyList(npy_intp *l1, int n) * Multiply a List of Non-negative numbers with over-flow detection. */ NPY_NO_EXPORT npy_intp -PyArray_OverflowMultiplyList(npy_intp *l1, int n) +PyArray_OverflowMultiplyList(npy_intp const *l1, int n) { npy_intp prod = 1; int i; @@ -168,7 +192,7 @@ PyArray_OverflowMultiplyList(npy_intp *l1, int n) * Produce a pointer into array */ NPY_NO_EXPORT void * -PyArray_GetPtr(PyArrayObject *obj, npy_intp* ind) +PyArray_GetPtr(PyArrayObject *obj, npy_intp const* ind) { int n = PyArray_NDIM(obj); npy_intp *strides = PyArray_STRIDES(obj); @@ -184,7 +208,7 @@ PyArray_GetPtr(PyArrayObject *obj, npy_intp* ind) * Compare Lists */ NPY_NO_EXPORT int -PyArray_CompareLists(npy_intp *l1, npy_intp *l2, int n) +PyArray_CompareLists(npy_intp const *l1, npy_intp const *l2, int n) { int i; @@ -261,7 +285,9 @@ PyArray_AsCArray(PyObject **op, void *ptr, npy_intp *dims, int nd, } *((char ****)ptr) = ptr3; } - memcpy(dims, PyArray_DIMS(ap), nd*sizeof(npy_intp)); + if (nd) { + memcpy(dims, PyArray_DIMS(ap), nd*sizeof(npy_intp)); + } *op = (PyObject *)ap; return 0; } @@ -272,45 +298,26 @@ PyArray_AsCArray(PyObject **op, void *ptr, npy_intp *dims, int nd, * Convert to a 1D C-array */ NPY_NO_EXPORT int -PyArray_As1D(PyObject **op, char **ptr, int *d1, int typecode) +PyArray_As1D(PyObject **NPY_UNUSED(op), char **NPY_UNUSED(ptr), + int *NPY_UNUSED(d1), int NPY_UNUSED(typecode)) { - npy_intp newd1; - PyArray_Descr *descr; - static const char msg[] = "PyArray_As1D: use PyArray_AsCArray."; - /* 2008-07-14, 1.5 */ - if (DEPRECATE(msg) < 0) { - return -1; - } - descr = PyArray_DescrFromType(typecode); - if (PyArray_AsCArray(op, (void *)ptr, &newd1, 1, descr) == -1) { - return -1; - } - *d1 = (int) newd1; - return 0; + PyErr_SetString(PyExc_NotImplementedError, + "PyArray_As1D: use PyArray_AsCArray."); + return -1; } /*NUMPY_API * Convert to a 2D C-array */ NPY_NO_EXPORT int -PyArray_As2D(PyObject **op, char ***ptr, int *d1, int *d2, int typecode) +PyArray_As2D(PyObject **NPY_UNUSED(op), char ***NPY_UNUSED(ptr), + int *NPY_UNUSED(d1), int *NPY_UNUSED(d2), int NPY_UNUSED(typecode)) { - npy_intp newdims[2]; - PyArray_Descr *descr; - static const char msg[] = "PyArray_As1D: use PyArray_AsCArray."; - /* 2008-07-14, 1.5 */ - if (DEPRECATE(msg) < 0) { - return -1; - } - descr = PyArray_DescrFromType(typecode); - if (PyArray_AsCArray(op, (void *)ptr, newdims, 2, descr) == -1) { - return -1; - } - *d1 = (int ) newdims[0]; - *d2 = (int ) newdims[1]; - return 0; + PyErr_SetString(PyExc_NotImplementedError, + "PyArray_As2D: use PyArray_AsCArray."); + return -1; } /* End Deprecated */ @@ -362,7 +369,8 @@ PyArray_GetSubType(int narrays, PyArrayObject **arrays) { */ NPY_NO_EXPORT PyArrayObject * PyArray_ConcatenateArrays(int narrays, PyArrayObject **arrays, int axis, - PyArrayObject* ret) + PyArrayObject* ret, PyArray_Descr *dtype, + NPY_CASTING casting) { int iarrays, idim, ndim; npy_intp shape[NPY_MAXDIMS]; @@ -397,9 +405,12 @@ PyArray_ConcatenateArrays(int narrays, PyArrayObject **arrays, int axis, npy_intp *arr_shape; if (PyArray_NDIM(arrays[iarrays]) != ndim) { - PyErr_SetString(PyExc_ValueError, - "all the input arrays must have same " - "number of dimensions"); + PyErr_Format(PyExc_ValueError, + "all the input arrays must have same number of " + "dimensions, but the array at index %d has %d " + "dimension(s) and the array at index %d has %d " + "dimension(s)", + 0, ndim, iarrays, PyArray_NDIM(arrays[iarrays])); return NULL; } arr_shape = PyArray_SHAPE(arrays[iarrays]); @@ -411,16 +422,19 @@ PyArray_ConcatenateArrays(int narrays, PyArrayObject **arrays, int axis, } /* Validate that the rest of the dimensions match */ else if (shape[idim] != arr_shape[idim]) { - PyErr_SetString(PyExc_ValueError, - "all the input array dimensions " - "except for the concatenation axis " - "must match exactly"); + PyErr_Format(PyExc_ValueError, + "all the input array dimensions for the " + "concatenation axis must match exactly, but " + "along dimension %d, the array at index %d has " + "size %d and the array at index %d has size %d", + idim, 0, shape[idim], iarrays, arr_shape[idim]); return NULL; } } } if (ret != NULL) { + assert(dtype == NULL); if (PyArray_NDIM(ret) != ndim) { PyErr_SetString(PyExc_ValueError, "Output array has wrong dimensionality"); @@ -439,10 +453,9 @@ PyArray_ConcatenateArrays(int narrays, PyArrayObject **arrays, int axis, /* Get the priority subtype for the array */ PyTypeObject *subtype = PyArray_GetSubType(narrays, arrays); - - /* Get the resulting dtype from combining all the arrays */ - PyArray_Descr *dtype = PyArray_ResultType(narrays, arrays, 0, NULL); - if (dtype == NULL) { + PyArray_Descr *descr = PyArray_FindConcatenationDescriptor( + narrays, arrays, (PyObject *)dtype); + if (descr == NULL) { return NULL; } @@ -452,7 +465,7 @@ PyArray_ConcatenateArrays(int narrays, PyArrayObject **arrays, int axis, * resolution rules matching that of the NpyIter. */ PyArray_CreateMultiSortedStridePerm(narrays, arrays, ndim, strideperm); - s = dtype->elsize; + s = descr->elsize; for (idim = ndim-1; idim >= 0; --idim) { int iperm = strideperm[idim]; strides[iperm] = s; @@ -460,17 +473,13 @@ PyArray_ConcatenateArrays(int narrays, PyArrayObject **arrays, int axis, } /* Allocate the array for the result. This steals the 'dtype' reference. */ - ret = (PyArrayObject *)PyArray_NewFromDescr(subtype, - dtype, - ndim, - shape, - strides, - NULL, - 0, - NULL); + ret = (PyArrayObject *)PyArray_NewFromDescr_int( + subtype, descr, ndim, shape, strides, NULL, 0, NULL, + NULL, 0, 1); if (ret == NULL) { return NULL; } + assert(PyArray_DESCR(ret) == descr); } /* @@ -489,7 +498,7 @@ PyArray_ConcatenateArrays(int narrays, PyArrayObject **arrays, int axis, /* Copy the data for this array */ if (PyArray_AssignArray((PyArrayObject *)sliding_view, arrays[iarrays], - NULL, NPY_SAME_KIND_CASTING) < 0) { + NULL, casting) < 0) { Py_DECREF(sliding_view); Py_DECREF(ret); return NULL; @@ -509,7 +518,9 @@ PyArray_ConcatenateArrays(int narrays, PyArrayObject **arrays, int axis, */ NPY_NO_EXPORT PyArrayObject * PyArray_ConcatenateFlattenedArrays(int narrays, PyArrayObject **arrays, - NPY_ORDER order, PyArrayObject *ret) + NPY_ORDER order, PyArrayObject *ret, + PyArray_Descr *dtype, NPY_CASTING casting, + npy_bool casting_not_passed) { int iarrays; npy_intp shape = 0; @@ -536,7 +547,10 @@ PyArray_ConcatenateFlattenedArrays(int narrays, PyArrayObject **arrays, } } + int out_passed = 0; if (ret != NULL) { + assert(dtype == NULL); + out_passed = 1; if (PyArray_NDIM(ret) != 1) { PyErr_SetString(PyExc_ValueError, "Output array must be 1D"); @@ -555,26 +569,22 @@ PyArray_ConcatenateFlattenedArrays(int narrays, PyArrayObject **arrays, /* Get the priority subtype for the array */ PyTypeObject *subtype = PyArray_GetSubType(narrays, arrays); - /* Get the resulting dtype from combining all the arrays */ - PyArray_Descr *dtype = PyArray_ResultType(narrays, arrays, 0, NULL); - if (dtype == NULL) { + PyArray_Descr *descr = PyArray_FindConcatenationDescriptor( + narrays, arrays, (PyObject *)dtype); + if (descr == NULL) { return NULL; } - stride = dtype->elsize; + stride = descr->elsize; /* Allocate the array for the result. This steals the 'dtype' reference. */ - ret = (PyArrayObject *)PyArray_NewFromDescr(subtype, - dtype, - 1, - &shape, - &stride, - NULL, - 0, - NULL); + ret = (PyArrayObject *)PyArray_NewFromDescr_int( + subtype, descr, 1, &shape, &stride, NULL, 0, NULL, + NULL, 0, 1); if (ret == NULL) { return NULL; } + assert(PyArray_DESCR(ret) == descr); } /* @@ -588,10 +598,37 @@ PyArray_ConcatenateFlattenedArrays(int narrays, PyArrayObject **arrays, return NULL; } + int give_deprecation_warning = 1; /* To give warning for just one input array. */ for (iarrays = 0; iarrays < narrays; ++iarrays) { /* Adjust the window dimensions for this array */ sliding_view->dimensions[0] = PyArray_SIZE(arrays[iarrays]); + if (!PyArray_CanCastArrayTo( + arrays[iarrays], PyArray_DESCR(ret), casting)) { + /* This should be an error, but was previously allowed here. */ + if (casting_not_passed && out_passed) { + /* NumPy 1.20, 2020-09-03 */ + if (give_deprecation_warning && DEPRECATE( + "concatenate() with `axis=None` will use same-kind " + "casting by default in the future. Please use " + "`casting='unsafe'` to retain the old behaviour. " + "In the future this will be a TypeError.") < 0) { + Py_DECREF(sliding_view); + Py_DECREF(ret); + return NULL; + } + give_deprecation_warning = 0; + } + else { + npy_set_invalid_cast_error( + PyArray_DESCR(arrays[iarrays]), PyArray_DESCR(ret), + casting, PyArray_NDIM(arrays[iarrays]) == 0); + Py_DECREF(sliding_view); + Py_DECREF(ret); + return NULL; + } + } + /* Copy the data for this array */ if (PyArray_CopyAsFlat((PyArrayObject *)sliding_view, arrays[iarrays], order) < 0) { @@ -609,8 +646,21 @@ PyArray_ConcatenateFlattenedArrays(int narrays, PyArrayObject **arrays, return ret; } + +/** + * Implementation for np.concatenate + * + * @param op Sequence of arrays to concatenate + * @param axis Axis to concatenate along + * @param ret output array to fill + * @param dtype Forced output array dtype (cannot be combined with ret) + * @param casting Casting mode used + * @param casting_not_passed Deprecation helper + */ NPY_NO_EXPORT PyObject * -PyArray_ConcatenateInto(PyObject *op, int axis, PyArrayObject *ret) +PyArray_ConcatenateInto(PyObject *op, + int axis, PyArrayObject *ret, PyArray_Descr *dtype, + NPY_CASTING casting, npy_bool casting_not_passed) { int iarrays, narrays; PyArrayObject **arrays; @@ -620,6 +670,12 @@ PyArray_ConcatenateInto(PyObject *op, int axis, PyArrayObject *ret) "The first input argument needs to be a sequence"); return NULL; } + if (ret != NULL && dtype != NULL) { + PyErr_SetString(PyExc_TypeError, + "concatenate() only takes `out` or `dtype` as an " + "argument, but both were provided."); + return NULL; + } /* Convert the input list into arrays */ narrays = PySequence_Size(op); @@ -646,10 +702,13 @@ PyArray_ConcatenateInto(PyObject *op, int axis, PyArrayObject *ret) } if (axis >= NPY_MAXDIMS) { - ret = PyArray_ConcatenateFlattenedArrays(narrays, arrays, NPY_CORDER, ret); + ret = PyArray_ConcatenateFlattenedArrays( + narrays, arrays, NPY_CORDER, ret, dtype, + casting, casting_not_passed); } else { - ret = PyArray_ConcatenateArrays(narrays, arrays, axis, ret); + ret = PyArray_ConcatenateArrays( + narrays, arrays, axis, ret, dtype, casting); } for (iarrays = 0; iarrays < narrays; ++iarrays) { @@ -681,7 +740,16 @@ PyArray_ConcatenateInto(PyObject *op, int axis, PyArrayObject *ret) NPY_NO_EXPORT PyObject * PyArray_Concatenate(PyObject *op, int axis) { - return PyArray_ConcatenateInto(op, axis, NULL); + /* retain legacy behaviour for casting */ + NPY_CASTING casting; + if (axis >= NPY_MAXDIMS) { + casting = NPY_UNSAFE_CASTING; + } + else { + casting = NPY_SAME_KIND_CASTING; + } + return PyArray_ConcatenateInto( + op, axis, NULL, NULL, casting, 0); } static int @@ -800,102 +868,6 @@ PyArray_CanCoerceScalar(int thistype, int neededtype, return 0; } -/* - * Make a new empty array, of the passed size, of a type that takes the - * priority of ap1 and ap2 into account. - * - * If `out` is non-NULL, memory overlap is checked with ap1 and ap2, and an - * updateifcopy temporary array may be returned. If `result` is non-NULL, the - * output array to be returned (`out` if non-NULL and the newly allocated array - * otherwise) is incref'd and put to *result. - */ -static PyArrayObject * -new_array_for_sum(PyArrayObject *ap1, PyArrayObject *ap2, PyArrayObject* out, - int nd, npy_intp dimensions[], int typenum, PyArrayObject **result) -{ - PyArrayObject *out_buf; - - if (out) { - int d; - - /* verify that out is usable */ - if (PyArray_NDIM(out) != nd || - PyArray_TYPE(out) != typenum || - !PyArray_ISCARRAY(out)) { - PyErr_SetString(PyExc_ValueError, - "output array is not acceptable (must have the right datatype, " - "number of dimensions, and be a C-Array)"); - return 0; - } - for (d = 0; d < nd; ++d) { - if (dimensions[d] != PyArray_DIM(out, d)) { - PyErr_SetString(PyExc_ValueError, - "output array has wrong dimensions"); - return 0; - } - } - - /* check for memory overlap */ - if (!(solve_may_share_memory(out, ap1, 1) == 0 && - solve_may_share_memory(out, ap2, 1) == 0)) { - /* allocate temporary output array */ - out_buf = (PyArrayObject *)PyArray_NewLikeArray(out, NPY_CORDER, - NULL, 0); - if (out_buf == NULL) { - return NULL; - } - - /* set copy-back */ - Py_INCREF(out); - if (PyArray_SetWritebackIfCopyBase(out_buf, out) < 0) { - Py_DECREF(out); - Py_DECREF(out_buf); - return NULL; - } - } - else { - Py_INCREF(out); - out_buf = out; - } - - if (result) { - Py_INCREF(out); - *result = out; - } - - return out_buf; - } - else { - PyTypeObject *subtype; - double prior1, prior2; - /* - * Need to choose an output array that can hold a sum - * -- use priority to determine which subtype. - */ - if (Py_TYPE(ap2) != Py_TYPE(ap1)) { - prior2 = PyArray_GetPriority((PyObject *)ap2, 0.0); - prior1 = PyArray_GetPriority((PyObject *)ap1, 0.0); - subtype = (prior2 > prior1 ? Py_TYPE(ap2) : Py_TYPE(ap1)); - } - else { - prior1 = prior2 = 0.0; - subtype = Py_TYPE(ap1); - } - - out_buf = (PyArrayObject *)PyArray_New(subtype, nd, dimensions, - typenum, NULL, NULL, 0, 0, - (PyObject *) - (prior2 > prior1 ? ap2 : ap1)); - - if (out_buf != NULL && result) { - Py_INCREF(out_buf); - *result = out_buf; - } - - return out_buf; - } -} - /* Could perhaps be redone to not make contiguous arrays */ /*NUMPY_API @@ -915,10 +887,16 @@ PyArray_InnerProduct(PyObject *op1, PyObject *op2) PyObject* ret = NULL; typenum = PyArray_ObjectType(op1, 0); + if (typenum == NPY_NOTYPE && PyErr_Occurred()) { + return NULL; + } typenum = PyArray_ObjectType(op2, typenum); typec = PyArray_DescrFromType(typenum); if (typec == NULL) { - PyErr_SetString(PyExc_TypeError, "Cannot find a common data type."); + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_TypeError, + "Cannot find a common data type."); + } goto fail; } @@ -1001,10 +979,16 @@ PyArray_MatrixProduct2(PyObject *op1, PyObject *op2, PyArrayObject* out) NPY_BEGIN_THREADS_DEF; typenum = PyArray_ObjectType(op1, 0); + if (typenum == NPY_NOTYPE && PyErr_Occurred()) { + return NULL; + } typenum = PyArray_ObjectType(op2, typenum); typec = PyArray_DescrFromType(typenum); if (typec == NULL) { - PyErr_SetString(PyExc_TypeError, "Cannot find a common data type."); + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_TypeError, + "Cannot find a common data type."); + } return NULL; } @@ -1061,7 +1045,7 @@ PyArray_MatrixProduct2(PyObject *op1, PyObject *op2, PyArrayObject* out) for (i = 0; i < PyArray_NDIM(ap2) - 2; i++) { dimensions[j++] = PyArray_DIMS(ap2)[i]; } - if(PyArray_NDIM(ap2) > 1) { + if (PyArray_NDIM(ap2) > 1) { dimensions[j++] = PyArray_DIMS(ap2)[PyArray_NDIM(ap2)-1]; } @@ -1101,7 +1085,7 @@ PyArray_MatrixProduct2(PyObject *op1, PyObject *op2, PyArrayObject* out) NPY_BEGIN_THREADS_DESCR(PyArray_DESCR(ap2)); while (it1->index < it1->size) { while (it2->index < it2->size) { - dot(it1->dataptr, is1, it2->dataptr, is2, op, l, out_buf); + dot(it1->dataptr, is1, it2->dataptr, is2, op, l, NULL); op += os; PyArray_ITER_NEXT(it2); } @@ -1202,6 +1186,14 @@ _pyarray_correlate(PyArrayObject *ap1, PyArrayObject *ap2, int typenum, n1 = PyArray_DIMS(ap1)[0]; n2 = PyArray_DIMS(ap2)[0]; + if (n1 == 0) { + PyErr_SetString(PyExc_ValueError, "first array argument cannot be empty"); + return NULL; + } + if (n2 == 0) { + PyErr_SetString(PyExc_ValueError, "second array argument cannot be empty"); + return NULL; + } if (n1 < n2) { ret = ap1; ap1 = ap2; @@ -1397,7 +1389,7 @@ PyArray_Correlate2(PyObject *op1, PyObject *op2, int mode) */ if (inverted) { st = _pyarray_revert(ret); - if(st) { + if (st) { goto clean_ret; } } @@ -1444,7 +1436,7 @@ PyArray_Correlate(PyObject *op1, PyObject *op2, int mode) } ret = _pyarray_correlate(ap1, ap2, typenum, mode, &unused); - if(ret == NULL) { + if (ret == NULL) { goto fail; } Py_DECREF(ap1); @@ -1474,65 +1466,6 @@ array_putmask(PyObject *NPY_UNUSED(module), PyObject *args, PyObject *kwds) return PyArray_PutMask((PyArrayObject *)array, values, mask); } -/* - * Compare the field dictionaries for two types. - * - * Return 1 if the field types and field names of the two descrs are equal and - * in the same order, 0 if not. - */ -static int -_equivalent_fields(PyArray_Descr *type1, PyArray_Descr *type2) { - - int val; - - if (type1->fields == type2->fields && type1->names == type2->names) { - return 1; - } - if (type1->fields == NULL || type2->fields == NULL) { - return 0; - } - - val = PyObject_RichCompareBool(type1->fields, type2->fields, Py_EQ); - if (val != 1 || PyErr_Occurred()) { - PyErr_Clear(); - return 0; - } - - val = PyObject_RichCompareBool(type1->names, type2->names, Py_EQ); - if (val != 1 || PyErr_Occurred()) { - PyErr_Clear(); - return 0; - } - - return 1; -} - -/* - * Compare the subarray data for two types. - * Return 1 if they are the same, 0 if not. - */ -static int -_equivalent_subarrays(PyArray_ArrayDescr *sub1, PyArray_ArrayDescr *sub2) -{ - int val; - - if (sub1 == sub2) { - return 1; - - } - if (sub1 == NULL || sub2 == NULL) { - return 0; - } - - val = PyObject_RichCompareBool(sub1->shape, sub2->shape, Py_EQ); - if (val != 1 || PyErr_Occurred()) { - PyErr_Clear(); - return 0; - } - - return PyArray_EquivTypes(sub1->base, sub2->base); -} - /*NUMPY_API * @@ -1542,40 +1475,41 @@ _equivalent_subarrays(PyArray_ArrayDescr *sub1, PyArray_ArrayDescr *sub2) NPY_NO_EXPORT unsigned char PyArray_EquivTypes(PyArray_Descr *type1, PyArray_Descr *type2) { - int type_num1, type_num2, size1, size2; - if (type1 == type2) { - return NPY_TRUE; + return 1; } - type_num1 = type1->type_num; - type_num2 = type2->type_num; - size1 = type1->elsize; - size2 = type2->elsize; - - if (size1 != size2) { - return NPY_FALSE; - } - if (PyArray_ISNBO(type1->byteorder) != PyArray_ISNBO(type2->byteorder)) { - return NPY_FALSE; - } - if (type1->subarray || type2->subarray) { - return ((type_num1 == type_num2) - && _equivalent_subarrays(type1->subarray, type2->subarray)); - } - if (type_num1 == NPY_VOID || type_num2 == NPY_VOID) { - return ((type_num1 == type_num2) && _equivalent_fields(type1, type2)); + if (Py_TYPE(Py_TYPE(type1)) == &PyType_Type) { + /* + * 2021-12-17: This case is nonsense and should be removed eventually! + * + * boost::python has/had a bug effectively using EquivTypes with + * `type(arbitrary_obj)`. That is clearly wrong as that cannot be a + * `PyArray_Descr *`. We assume that `type(type(type(arbitrary_obj))` + * is always in practice `type` (this is the type of the metaclass), + * but for our descriptors, `type(type(descr))` is DTypeMeta. + * + * In that case, we just return False. There is a possibility that + * this actually _worked_ effectively (returning 1 sometimes). + * We ignore that possibility for simplicity; it really is not our bug. + */ + return 0; } - if (type_num1 == NPY_DATETIME - || type_num1 == NPY_TIMEDELTA - || type_num2 == NPY_DATETIME - || type_num2 == NPY_TIMEDELTA) { - return ((type_num1 == type_num2) - && has_equivalent_datetime_metadata(type1, type2)); + + /* + * Do not use PyArray_CanCastTypeTo because it supports legacy flexible + * dtypes as input. + */ + NPY_CASTING safety = PyArray_GetCastSafety(type1, type2, NULL); + if (safety < 0) { + PyErr_Clear(); + return 0; } - return type1->kind == type2->kind; + /* If casting is "no casting" this dtypes are considered equivalent. */ + return PyArray_MinCastSafety(safety, NPY_NO_CASTING) == NPY_NO_CASTING; } + /*NUMPY_API*/ NPY_NO_EXPORT unsigned char PyArray_EquivTypenums(int typenum1, int typenum2) @@ -1638,128 +1572,42 @@ _prepend_ones(PyArrayObject *arr, int nd, int ndmin, NPY_ORDER order) return ret; } - #define STRIDING_OK(op, order) \ ((order) == NPY_ANYORDER || \ (order) == NPY_KEEPORDER || \ ((order) == NPY_CORDER && PyArray_IS_C_CONTIGUOUS(op)) || \ ((order) == NPY_FORTRANORDER && PyArray_IS_F_CONTIGUOUS(op))) -static PyObject * -_array_fromobject(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kws) +static NPY_INLINE PyObject * +_array_fromobject_generic( + PyObject *op, PyArray_Descr *type, _PyArray_CopyMode copy, NPY_ORDER order, + npy_bool subok, int ndmin) { - PyObject *op; PyArrayObject *oparr = NULL, *ret = NULL; - npy_bool subok = NPY_FALSE; - npy_bool copy = NPY_TRUE; - int ndmin = 0, nd; - PyArray_Descr *type = NULL; PyArray_Descr *oldtype = NULL; - NPY_ORDER order = NPY_KEEPORDER; - int flags = 0; - - static char *kwd[]= {"object", "dtype", "copy", "order", "subok", - "ndmin", NULL}; - - if (PyTuple_GET_SIZE(args) > 2) { - PyErr_SetString(PyExc_ValueError, - "only 2 non-keyword arguments accepted"); - return NULL; - } - - /* super-fast path for ndarray argument calls */ - if (PyTuple_GET_SIZE(args) == 0) { - goto full_path; - } - op = PyTuple_GET_ITEM(args, 0); - if (PyArray_CheckExact(op)) { - PyObject * dtype_obj = Py_None; - oparr = (PyArrayObject *)op; - /* get dtype which can be positional */ - if (PyTuple_GET_SIZE(args) == 2) { - dtype_obj = PyTuple_GET_ITEM(args, 1); - } - else if (kws) { - dtype_obj = PyDict_GetItem(kws, npy_ma_str_dtype); - if (dtype_obj == NULL) { - dtype_obj = Py_None; - } - } - if (dtype_obj != Py_None) { - goto full_path; - } - - /* array(ndarray) */ - if (kws == NULL) { - ret = (PyArrayObject *)PyArray_NewCopy(oparr, order); - goto finish; - } - else { - /* fast path for copy=False rest default (np.asarray) */ - PyObject * copy_obj, * order_obj, *ndmin_obj; - copy_obj = PyDict_GetItem(kws, npy_ma_str_copy); - if (copy_obj != Py_False) { - goto full_path; - } - copy = NPY_FALSE; - - /* order does not matter for contiguous 1d arrays */ - if (PyArray_NDIM((PyArrayObject*)op) > 1 || - !PyArray_IS_C_CONTIGUOUS((PyArrayObject*)op)) { - order_obj = PyDict_GetItem(kws, npy_ma_str_order); - if (order_obj != Py_None && order_obj != NULL) { - goto full_path; - } - } - - ndmin_obj = PyDict_GetItem(kws, npy_ma_str_ndmin); - if (ndmin_obj) { - ndmin = PyLong_AsLong(ndmin_obj); - if (error_converting(ndmin)) { - goto clean_type; - } - else if (ndmin > NPY_MAXDIMS) { - goto full_path; - } - } - - /* copy=False with default dtype, order and ndim */ - if (STRIDING_OK(oparr, order)) { - ret = oparr; - Py_INCREF(ret); - goto finish; - } - } - } - -full_path: - if(!PyArg_ParseTupleAndKeywords(args, kws, "O|O&O&O&O&i:array", kwd, - &op, - PyArray_DescrConverter2, &type, - PyArray_BoolConverter, ©, - PyArray_OrderConverter, &order, - PyArray_BoolConverter, &subok, - &ndmin)) { - goto clean_type; - } + int nd, flags = 0; if (ndmin > NPY_MAXDIMS) { PyErr_Format(PyExc_ValueError, "ndmin bigger than allowable number of dimensions " "NPY_MAXDIMS (=%d)", NPY_MAXDIMS); - goto clean_type; + return NULL; } /* fast exit if simple call */ - if ((subok && PyArray_Check(op)) || - (!subok && PyArray_CheckExact(op))) { + if (PyArray_CheckExact(op) || (subok && PyArray_Check(op))) { oparr = (PyArrayObject *)op; if (type == NULL) { - if (!copy && STRIDING_OK(oparr, order)) { + if (copy != NPY_COPY_ALWAYS && STRIDING_OK(oparr, order)) { ret = oparr; Py_INCREF(ret); goto finish; } else { + if (copy == NPY_COPY_NEVER) { + PyErr_SetString(PyExc_ValueError, + "Unable to avoid copy while creating a new array."); + return NULL; + } ret = (PyArrayObject *)PyArray_NewCopy(oparr, order); goto finish; } @@ -1767,12 +1615,17 @@ _array_fromobject(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kws) /* One more chance */ oldtype = PyArray_DESCR(oparr); if (PyArray_EquivTypes(oldtype, type)) { - if (!copy && STRIDING_OK(oparr, order)) { + if (copy != NPY_COPY_ALWAYS && STRIDING_OK(oparr, order)) { Py_INCREF(op); ret = oparr; goto finish; } else { + if (copy == NPY_COPY_NEVER) { + PyErr_SetString(PyExc_ValueError, + "Unable to avoid copy while creating a new array."); + return NULL; + } ret = (PyArrayObject *)PyArray_NewCopy(oparr, order); if (oldtype == type || ret == NULL) { goto finish; @@ -1785,9 +1638,12 @@ _array_fromobject(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kws) } } - if (copy) { + if (copy == NPY_COPY_ALWAYS) { flags = NPY_ARRAY_ENSURECOPY; } + else if (copy == NPY_COPY_NEVER ) { + flags = NPY_ARRAY_ENSURENOCOPY; + } if (order == NPY_CORDER) { flags |= NPY_ARRAY_C_CONTIGUOUS; } @@ -1806,8 +1662,7 @@ _array_fromobject(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kws) ret = (PyArrayObject *)PyArray_CheckFromAny(op, type, 0, 0, flags, NULL); - finish: - Py_XDECREF(type); +finish: if (ret == NULL) { return NULL; } @@ -1821,50 +1676,249 @@ _array_fromobject(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kws) * steals a reference to ret */ return _prepend_ones(ret, nd, ndmin, order); - -clean_type: - Py_XDECREF(type); - return NULL; } -static PyObject * -array_copyto(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kwds) -{ - - static char *kwlist[] = {"dst","src","casting","where",NULL}; - PyObject *wheremask_in = NULL; - PyArrayObject *dst = NULL, *src = NULL, *wheremask = NULL; - NPY_CASTING casting = NPY_SAME_KIND_CASTING; +#undef STRIDING_OK - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O&|O&O:copyto", kwlist, - &PyArray_Type, &dst, - &PyArray_Converter, &src, - &PyArray_CastingConverter, &casting, - &wheremask_in)) { - goto fail; - } - if (wheremask_in != NULL) { - /* Get the boolean where mask */ - PyArray_Descr *dtype = PyArray_DescrFromType(NPY_BOOL); - if (dtype == NULL) { - goto fail; +static PyObject * +array_array(PyObject *NPY_UNUSED(ignored), + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) +{ + PyObject *op; + npy_bool subok = NPY_FALSE; + _PyArray_CopyMode copy = NPY_COPY_ALWAYS; + int ndmin = 0; + PyArray_Descr *type = NULL; + NPY_ORDER order = NPY_KEEPORDER; + PyObject *like = NULL; + NPY_PREPARE_ARGPARSER; + + if (len_args != 1 || (kwnames != NULL)) { + if (npy_parse_arguments("array", args, len_args, kwnames, + "object", NULL, &op, + "|dtype", &PyArray_DescrConverter2, &type, + "$copy", &PyArray_CopyConverter, ©, + "$order", &PyArray_OrderConverter, &order, + "$subok", &PyArray_BoolConverter, &subok, + "$ndmin", &PyArray_PythonPyIntFromInt, &ndmin, + "$like", NULL, &like, + NULL, NULL, NULL) < 0) { + Py_XDECREF(type); + return NULL; } - wheremask = (PyArrayObject *)PyArray_FromAny(wheremask_in, - dtype, 0, 0, 0, NULL); - if (wheremask == NULL) { - goto fail; + if (like != NULL) { + PyObject *deferred = array_implement_c_array_function_creation( + "array", like, NULL, NULL, args, len_args, kwnames); + if (deferred != Py_NotImplemented) { + Py_XDECREF(type); + return deferred; + } } } - - if (PyArray_AssignArray(dst, src, wheremask, casting) < 0) { - goto fail; + else { + /* Fast path for symmetry (we copy by default which is slow) */ + op = args[0]; } - Py_XDECREF(src); - Py_XDECREF(wheremask); - - Py_RETURN_NONE; + PyObject *res = _array_fromobject_generic( + op, type, copy, order, subok, ndmin); + Py_XDECREF(type); + return res; +} + +static PyObject * +array_asarray(PyObject *NPY_UNUSED(ignored), + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) +{ + PyObject *op; + PyArray_Descr *type = NULL; + NPY_ORDER order = NPY_KEEPORDER; + PyObject *like = NULL; + NPY_PREPARE_ARGPARSER; + + if (len_args != 1 || (kwnames != NULL)) { + if (npy_parse_arguments("asarray", args, len_args, kwnames, + "a", NULL, &op, + "|dtype", &PyArray_DescrConverter2, &type, + "|order", &PyArray_OrderConverter, &order, + "$like", NULL, &like, + NULL, NULL, NULL) < 0) { + Py_XDECREF(type); + return NULL; + } + if (like != NULL) { + PyObject *deferred = array_implement_c_array_function_creation( + "asarray", like, NULL, NULL, args, len_args, kwnames); + if (deferred != Py_NotImplemented) { + Py_XDECREF(type); + return deferred; + } + } + } + else { + op = args[0]; + } + + PyObject *res = _array_fromobject_generic( + op, type, NPY_FALSE, order, NPY_FALSE, 0); + Py_XDECREF(type); + return res; +} + +static PyObject * +array_asanyarray(PyObject *NPY_UNUSED(ignored), + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) +{ + PyObject *op; + PyArray_Descr *type = NULL; + NPY_ORDER order = NPY_KEEPORDER; + PyObject *like = NULL; + NPY_PREPARE_ARGPARSER; + + if (len_args != 1 || (kwnames != NULL)) { + if (npy_parse_arguments("asanyarray", args, len_args, kwnames, + "a", NULL, &op, + "|dtype", &PyArray_DescrConverter2, &type, + "|order", &PyArray_OrderConverter, &order, + "$like", NULL, &like, + NULL, NULL, NULL) < 0) { + Py_XDECREF(type); + return NULL; + } + if (like != NULL) { + PyObject *deferred = array_implement_c_array_function_creation( + "asanyarray", like, NULL, NULL, args, len_args, kwnames); + if (deferred != Py_NotImplemented) { + Py_XDECREF(type); + return deferred; + } + } + } + else { + op = args[0]; + } + + PyObject *res = _array_fromobject_generic( + op, type, NPY_FALSE, order, NPY_TRUE, 0); + Py_XDECREF(type); + return res; +} + + +static PyObject * +array_ascontiguousarray(PyObject *NPY_UNUSED(ignored), + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) +{ + PyObject *op; + PyArray_Descr *type = NULL; + PyObject *like = NULL; + NPY_PREPARE_ARGPARSER; + + if (len_args != 1 || (kwnames != NULL)) { + if (npy_parse_arguments("ascontiguousarray", args, len_args, kwnames, + "a", NULL, &op, + "|dtype", &PyArray_DescrConverter2, &type, + "$like", NULL, &like, + NULL, NULL, NULL) < 0) { + Py_XDECREF(type); + return NULL; + } + if (like != NULL) { + PyObject *deferred = array_implement_c_array_function_creation( + "ascontiguousarray", like, NULL, NULL, args, len_args, kwnames); + if (deferred != Py_NotImplemented) { + Py_XDECREF(type); + return deferred; + } + } + } + else { + op = args[0]; + } + + PyObject *res = _array_fromobject_generic( + op, type, NPY_FALSE, NPY_CORDER, NPY_FALSE, 1); + Py_XDECREF(type); + return res; +} + + +static PyObject * +array_asfortranarray(PyObject *NPY_UNUSED(ignored), + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) +{ + PyObject *op; + PyArray_Descr *type = NULL; + PyObject *like = NULL; + NPY_PREPARE_ARGPARSER; + + if (len_args != 1 || (kwnames != NULL)) { + if (npy_parse_arguments("asfortranarray", args, len_args, kwnames, + "a", NULL, &op, + "|dtype", &PyArray_DescrConverter2, &type, + "$like", NULL, &like, + NULL, NULL, NULL) < 0) { + Py_XDECREF(type); + return NULL; + } + if (like != NULL) { + PyObject *deferred = array_implement_c_array_function_creation( + "asfortranarray", like, NULL, NULL, args, len_args, kwnames); + if (deferred != Py_NotImplemented) { + Py_XDECREF(type); + return deferred; + } + } + } + else { + op = args[0]; + } + + PyObject *res = _array_fromobject_generic( + op, type, NPY_FALSE, NPY_FORTRANORDER, NPY_FALSE, 1); + Py_XDECREF(type); + return res; +} + + +static PyObject * +array_copyto(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"dst", "src", "casting", "where", NULL}; + PyObject *wheremask_in = NULL; + PyArrayObject *dst = NULL, *src = NULL, *wheremask = NULL; + NPY_CASTING casting = NPY_SAME_KIND_CASTING; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O&|O&O:copyto", kwlist, + &PyArray_Type, &dst, + &PyArray_Converter, &src, + &PyArray_CastingConverter, &casting, + &wheremask_in)) { + goto fail; + } + + if (wheremask_in != NULL) { + /* Get the boolean where mask */ + PyArray_Descr *dtype = PyArray_DescrFromType(NPY_BOOL); + if (dtype == NULL) { + goto fail; + } + wheremask = (PyArrayObject *)PyArray_FromAny(wheremask_in, + dtype, 0, 0, 0, NULL); + if (wheremask == NULL) { + goto fail; + } + } + + if (PyArray_AssignArray(dst, src, wheremask, casting) < 0) { + goto fail; + } + + Py_XDECREF(src); + Py_XDECREF(wheremask); + + Py_RETURN_NONE; fail: Py_XDECREF(src); @@ -1873,23 +1927,36 @@ array_copyto(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kwds) } static PyObject * -array_empty(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kwds) +array_empty(PyObject *NPY_UNUSED(ignored), + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) { - - static char *kwlist[] = {"shape","dtype","order",NULL}; PyArray_Descr *typecode = NULL; PyArray_Dims shape = {NULL, 0}; NPY_ORDER order = NPY_CORDER; npy_bool is_f_order; PyArrayObject *ret = NULL; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|O&O&:empty", kwlist, - PyArray_IntpConverter, &shape, - PyArray_DescrConverter, &typecode, - PyArray_OrderConverter, &order)) { + PyObject *like = NULL; + NPY_PREPARE_ARGPARSER; + + if (npy_parse_arguments("empty", args, len_args, kwnames, + "shape", &PyArray_IntpConverter, &shape, + "|dtype", &PyArray_DescrConverter, &typecode, + "|order", &PyArray_OrderConverter, &order, + "$like", NULL, &like, + NULL, NULL, NULL) < 0) { goto fail; } + if (like != NULL) { + PyObject *deferred = array_implement_c_array_function_creation( + "empty", like, NULL, NULL, args, len_args, kwnames); + if (deferred != Py_NotImplemented) { + Py_XDECREF(typecode); + npy_free_cache_dim_obj(shape); + return deferred; + } + } + switch (order) { case NPY_CORDER: is_f_order = NPY_FALSE; @@ -1919,23 +1986,30 @@ static PyObject * array_empty_like(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kwds) { - static char *kwlist[] = {"prototype","dtype","order","subok",NULL}; + static char *kwlist[] = {"prototype", "dtype", "order", "subok", "shape", NULL}; PyArrayObject *prototype = NULL; PyArray_Descr *dtype = NULL; NPY_ORDER order = NPY_KEEPORDER; PyArrayObject *ret = NULL; int subok = 1; + /* -1 is a special value meaning "not specified" */ + PyArray_Dims shape = {NULL, -1}; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|O&O&i:empty_like", kwlist, + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|O&O&iO&:empty_like", kwlist, &PyArray_Converter, &prototype, &PyArray_DescrConverter2, &dtype, &PyArray_OrderConverter, &order, - &subok)) { + &subok, + &PyArray_OptionalIntpConverter, &shape)) { goto fail; } /* steals the reference to dtype if it's not NULL */ - ret = (PyArrayObject *)PyArray_NewLikeArray(prototype, - order, dtype, subok); + ret = (PyArrayObject *)PyArray_NewLikeArrayWithShape(prototype, order, dtype, + shape.len, shape.ptr, subok); + npy_free_cache_dim_obj(shape); + if (!ret) { + goto fail; + } Py_DECREF(prototype); return (PyObject *)ret; @@ -1954,20 +2028,50 @@ static PyObject * array_scalar(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kwds) { - static char *kwlist[] = {"dtype","obj", NULL}; + static char *kwlist[] = {"dtype", "obj", NULL}; PyArray_Descr *typecode; PyObject *obj = NULL, *tmpobj = NULL; int alloc = 0; void *dptr; PyObject *ret; - + PyObject *base = NULL; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|O:scalar", kwlist, &PyArrayDescr_Type, &typecode, &obj)) { return NULL; } + if (PyDataType_FLAGCHK(typecode, NPY_LIST_PICKLE)) { + if (typecode->type_num == NPY_OBJECT) { + /* Deprecated 2020-11-24, NumPy 1.20 */ + if (DEPRECATE( + "Unpickling a scalar with object dtype is deprecated. " + "Object scalars should never be created. If this was a " + "properly created pickle, please open a NumPy issue. In " + "a best effort this returns the original object.") < 0) { + return NULL; + } + Py_INCREF(obj); + return obj; + } + /* We store the full array to unpack it here: */ + if (!PyArray_CheckExact(obj)) { + /* We pickle structured voids as arrays currently */ + PyErr_SetString(PyExc_RuntimeError, + "Unpickling NPY_LIST_PICKLE (structured void) scalar " + "requires an array. The pickle file may be corrupted?"); + return NULL; + } + if (!PyArray_EquivTypes(PyArray_DESCR((PyArrayObject *)obj), typecode)) { + PyErr_SetString(PyExc_RuntimeError, + "Pickled array is not compatible with requested scalar " + "dtype. The pickle file may be corrupted?"); + return NULL; + } + base = obj; + dptr = PyArray_BYTES((PyArrayObject *)obj); + } - if (PyDataType_FLAGCHK(typecode, NPY_ITEM_IS_POINTER)) { + else if (PyDataType_FLAGCHK(typecode, NPY_ITEM_IS_POINTER)) { if (obj == NULL) { obj = Py_None; } @@ -1986,7 +2090,6 @@ array_scalar(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kwds) alloc = 1; } else { -#if defined(NPY_PY3K) /* Backward compatibility with Python 2 NumPy pickles */ if (PyUnicode_Check(obj)) { tmpobj = PyUnicode_AsLatin1String(obj); @@ -2000,24 +2103,22 @@ array_scalar(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kwds) return NULL; } } -#endif - - if (!PyString_Check(obj)) { + if (!PyBytes_Check(obj)) { PyErr_SetString(PyExc_TypeError, - "initializing object must be a string"); + "initializing object must be a bytes object"); Py_XDECREF(tmpobj); return NULL; } - if (PyString_GET_SIZE(obj) < typecode->elsize) { + if (PyBytes_GET_SIZE(obj) < typecode->elsize) { PyErr_SetString(PyExc_ValueError, "initialization string is too small"); Py_XDECREF(tmpobj); return NULL; } - dptr = PyString_AS_STRING(obj); + dptr = PyBytes_AS_STRING(obj); } } - ret = PyArray_Scalar(dptr, typecode, NULL); + ret = PyArray_Scalar(dptr, typecode, base); /* free dptr which contains zeros */ if (alloc) { @@ -2028,22 +2129,37 @@ array_scalar(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kwds) } static PyObject * -array_zeros(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kwds) +array_zeros(PyObject *NPY_UNUSED(ignored), + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) { - static char *kwlist[] = {"shape","dtype","order",NULL}; PyArray_Descr *typecode = NULL; PyArray_Dims shape = {NULL, 0}; NPY_ORDER order = NPY_CORDER; npy_bool is_f_order = NPY_FALSE; PyArrayObject *ret = NULL; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|O&O&:zeros", kwlist, - PyArray_IntpConverter, &shape, - PyArray_DescrConverter, &typecode, - PyArray_OrderConverter, &order)) { + PyObject *like = NULL; + NPY_PREPARE_ARGPARSER; + + if (npy_parse_arguments("zeros", args, len_args, kwnames, + "shape", &PyArray_IntpConverter, &shape, + "|dtype", &PyArray_DescrConverter, &typecode, + "|order", &PyArray_OrderConverter, &order, + "$like", NULL, &like, + NULL, NULL, NULL) < 0) { goto fail; } + + if (like != NULL) { + PyObject *deferred = array_implement_c_array_function_creation( + "zeros", like, NULL, NULL, args, len_args, kwnames); + if (deferred != Py_NotImplemented) { + Py_XDECREF(typecode); + npy_free_cache_dim_obj(shape); + return deferred; + } + } + switch (order) { case NPY_CORDER: is_f_order = NPY_FALSE; @@ -2086,11 +2202,7 @@ array_count_nonzero(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds) if (count == -1) { return NULL; } -#if defined(NPY_PY3K) return PyLong_FromSsize_t(count); -#else - return PyInt_FromSsize_t(count); -#endif } static PyObject * @@ -2100,15 +2212,24 @@ array_fromstring(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *keywds Py_ssize_t nin = -1; char *sep = NULL; Py_ssize_t s; - static char *kwlist[] = {"string", "dtype", "count", "sep", NULL}; + static char *kwlist[] = {"string", "dtype", "count", "sep", "like", NULL}; + PyObject *like = NULL; PyArray_Descr *descr = NULL; if (!PyArg_ParseTupleAndKeywords(args, keywds, - "s#|O&" NPY_SSIZE_T_PYFMT "s:fromstring", kwlist, - &data, &s, PyArray_DescrConverter, &descr, &nin, &sep)) { + "s#|O&" NPY_SSIZE_T_PYFMT "s$O:fromstring", kwlist, + &data, &s, PyArray_DescrConverter, &descr, &nin, &sep, &like)) { Py_XDECREF(descr); return NULL; } + if (like != NULL) { + PyObject *deferred = array_implement_c_array_function_creation( + "fromstring", like, args, keywds, NULL, 0, NULL); + if (deferred != Py_NotImplemented) { + Py_XDECREF(descr); + return deferred; + } + } /* binary mode, condition copied from PyArray_FromString */ if (sep == NULL || strlen(sep) == 0) { @@ -2116,7 +2237,7 @@ array_fromstring(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *keywds if (DEPRECATE( "The binary mode of fromstring is deprecated, as it behaves " "surprisingly on unicode inputs. Use frombuffer instead") < 0) { - Py_DECREF(descr); + Py_XDECREF(descr); return NULL; } } @@ -2128,54 +2249,92 @@ array_fromstring(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *keywds static PyObject * array_fromfile(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *keywds) { - PyObject *file = NULL, *ret; + PyObject *file = NULL, *ret = NULL; + PyObject *err_type = NULL, *err_value = NULL, *err_traceback = NULL; char *sep = ""; Py_ssize_t nin = -1; - static char *kwlist[] = {"file", "dtype", "count", "sep", NULL}; + static char *kwlist[] = {"file", "dtype", "count", "sep", "offset", "like", NULL}; + PyObject *like = NULL; PyArray_Descr *type = NULL; int own; - npy_off_t orig_pos = 0; + npy_off_t orig_pos = 0, offset = 0; FILE *fp; if (!PyArg_ParseTupleAndKeywords(args, keywds, - "O|O&" NPY_SSIZE_T_PYFMT "s:fromfile", kwlist, - &file, PyArray_DescrConverter, &type, &nin, &sep)) { + "O|O&" NPY_SSIZE_T_PYFMT "s" NPY_OFF_T_PYFMT "$O:fromfile", kwlist, + &file, PyArray_DescrConverter, &type, &nin, &sep, &offset, &like)) { Py_XDECREF(type); return NULL; } - if (PyString_Check(file) || PyUnicode_Check(file)) { - file = npy_PyFile_OpenFile(file, "rb"); + + if (like != NULL) { + PyObject *deferred = array_implement_c_array_function_creation( + "fromfile", like, args, keywds, NULL, 0, NULL); + if (deferred != Py_NotImplemented) { + Py_XDECREF(type); + return deferred; + } + } + + file = NpyPath_PathlikeToFspath(file); + if (file == NULL) { + Py_XDECREF(type); + return NULL; + } + + if (offset != 0 && strcmp(sep, "") != 0) { + PyErr_SetString(PyExc_TypeError, "'offset' argument only permitted for binary files"); + Py_XDECREF(type); + Py_DECREF(file); + return NULL; + } + if (PyBytes_Check(file) || PyUnicode_Check(file)) { + Py_SETREF(file, npy_PyFile_OpenFile(file, "rb")); if (file == NULL) { + Py_XDECREF(type); return NULL; } own = 1; } else { - Py_INCREF(file); own = 0; } fp = npy_PyFile_Dup2(file, "rb", &orig_pos); if (fp == NULL) { Py_DECREF(file); + Py_XDECREF(type); return NULL; } + if (npy_fseek(fp, offset, SEEK_CUR) != 0) { + PyErr_SetFromErrno(PyExc_OSError); + goto cleanup; + } if (type == NULL) { type = PyArray_DescrFromType(NPY_DEFAULT_TYPE); } ret = PyArray_FromFile(fp, type, (npy_intp) nin, sep); + /* If an exception is thrown in the call to PyArray_FromFile + * we need to clear it, and restore it later to ensure that + * we can cleanup the duplicated file descriptor properly. + */ +cleanup: + PyErr_Fetch(&err_type, &err_value, &err_traceback); if (npy_PyFile_DupClose2(file, fp, orig_pos) < 0) { + npy_PyErr_ChainExceptions(err_type, err_value, err_traceback); goto fail; } if (own && npy_PyFile_CloseFile(file) < 0) { + npy_PyErr_ChainExceptions(err_type, err_value, err_traceback); goto fail; } + PyErr_Restore(err_type, err_value, err_traceback); Py_DECREF(file); return ret; fail: Py_DECREF(file); - Py_DECREF(ret); + Py_XDECREF(ret); return NULL; } @@ -2184,15 +2343,25 @@ array_fromiter(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *keywds) { PyObject *iter; Py_ssize_t nin = -1; - static char *kwlist[] = {"iter", "dtype", "count", NULL}; + static char *kwlist[] = {"iter", "dtype", "count", "like", NULL}; + PyObject *like = NULL; PyArray_Descr *descr = NULL; if (!PyArg_ParseTupleAndKeywords(args, keywds, - "OO&|" NPY_SSIZE_T_PYFMT ":fromiter", kwlist, - &iter, PyArray_DescrConverter, &descr, &nin)) { + "OO&|" NPY_SSIZE_T_PYFMT "$O:fromiter", kwlist, + &iter, PyArray_DescrConverter, &descr, &nin, &like)) { Py_XDECREF(descr); return NULL; } + if (like != NULL) { + PyObject *deferred = array_implement_c_array_function_creation( + "fromiter", like, args, keywds, NULL, 0, NULL); + if (deferred != Py_NotImplemented) { + Py_XDECREF(descr); + return deferred; + } + } + return PyArray_FromIter(iter, descr, (npy_intp)nin); } @@ -2201,15 +2370,26 @@ array_frombuffer(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *keywds { PyObject *obj = NULL; Py_ssize_t nin = -1, offset = 0; - static char *kwlist[] = {"buffer", "dtype", "count", "offset", NULL}; + static char *kwlist[] = {"buffer", "dtype", "count", "offset", "like", NULL}; + PyObject *like = NULL; PyArray_Descr *type = NULL; if (!PyArg_ParseTupleAndKeywords(args, keywds, - "O|O&" NPY_SSIZE_T_PYFMT NPY_SSIZE_T_PYFMT ":frombuffer", kwlist, - &obj, PyArray_DescrConverter, &type, &nin, &offset)) { + "O|O&" NPY_SSIZE_T_PYFMT NPY_SSIZE_T_PYFMT "$O:frombuffer", kwlist, + &obj, PyArray_DescrConverter, &type, &nin, &offset, &like)) { Py_XDECREF(type); return NULL; } + + if (like != NULL) { + PyObject *deferred = array_implement_c_array_function_creation( + "frombuffer", like, args, keywds, NULL, 0, NULL); + if (deferred != Py_NotImplemented) { + Py_XDECREF(type); + return deferred; + } + } + if (type == NULL) { type = PyArray_DescrFromType(NPY_DEFAULT_TYPE); } @@ -2221,11 +2401,27 @@ array_concatenate(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds) { PyObject *a0; PyObject *out = NULL; + PyArray_Descr *dtype = NULL; + NPY_CASTING casting = NPY_SAME_KIND_CASTING; + PyObject *casting_obj = NULL; + PyObject *res; int axis = 0; - static char *kwlist[] = {"seq", "axis", "out", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O&O:concatenate", kwlist, - &a0, PyArray_AxisConverter, &axis, &out)) { + static char *kwlist[] = {"seq", "axis", "out", "dtype", "casting", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O&O$O&O:concatenate", kwlist, + &a0, PyArray_AxisConverter, &axis, &out, + PyArray_DescrConverter2, &dtype, &casting_obj)) { + return NULL; + } + int casting_not_passed = 0; + if (casting_obj == NULL) { + /* + * Casting was not passed in, needed for deprecation only. + * This should be simplified once the deprecation is finished. + */ + casting_not_passed = 1; + } + else if (!PyArray_CastingConverter(casting_obj, &casting)) { + Py_XDECREF(dtype); return NULL; } if (out != NULL) { @@ -2234,10 +2430,14 @@ array_concatenate(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds) } else if (!PyArray_Check(out)) { PyErr_SetString(PyExc_TypeError, "'out' must be an array"); + Py_XDECREF(dtype); return NULL; } } - return PyArray_ConcatenateInto(a0, axis, (PyArrayObject *)out); + res = PyArray_ConcatenateInto(a0, axis, (PyArrayObject *)out, dtype, + casting, casting_not_passed); + Py_XDECREF(dtype); + return res; } static PyObject * @@ -2256,7 +2456,7 @@ array_matrixproduct(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject* kwds) { PyObject *v, *a, *o = NULL; PyArrayObject *ret; - char* kwlist[] = {"a", "b", "out", NULL }; + static char* kwlist[] = {"a", "b", "out", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|O:matrixproduct", kwlist, &a, &v, &o)) { @@ -2388,154 +2588,6 @@ array_vdot(PyObject *NPY_UNUSED(dummy), PyObject *args) return NULL; } - - -/* - * matmul - * - * Implements the protocol used by the '@' operator defined in PEP 364. - * Not in the NUMPY API at this time, maybe later. - * - * - * in1: Left hand side operand - * in2: Right hand side operand - * out: Either NULL, or an array into which the output should be placed. - * - * Returns NULL on error. - */ -static PyObject * -array_matmul(PyObject *NPY_UNUSED(m), PyObject *args, PyObject* kwds) -{ - PyObject *in1, *in2, *out = NULL; - char* kwlist[] = {"a", "b", "out", NULL }; - PyArrayObject *ap1, *ap2, *ret = NULL; - NPY_ORDER order = NPY_KEEPORDER; - NPY_CASTING casting = NPY_SAFE_CASTING; - PyArray_Descr *dtype; - int nd1, nd2, typenum; - char *subscripts; - PyArrayObject *ops[2]; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|O:matmul", kwlist, - &in1, &in2, &out)) { - return NULL; - } - - if (out != NULL) { - if (out == Py_None) { - out = NULL; - } - else if (!PyArray_Check(out)) { - PyErr_SetString(PyExc_TypeError, "'out' must be an array"); - return NULL; - } - } - - dtype = PyArray_DescrFromObject(in1, NULL); - dtype = PyArray_DescrFromObject(in2, dtype); - if (dtype == NULL) { - PyErr_SetString(PyExc_ValueError, "Cannot find a common data type."); - return NULL; - } - typenum = dtype->type_num; - - if (typenum == NPY_OBJECT) { - /* matmul is not currently implemented for object arrays */ - PyErr_SetString(PyExc_TypeError, - "Object arrays are not currently supported"); - Py_DECREF(dtype); - return NULL; - } - - ap1 = (PyArrayObject *)PyArray_FromAny(in1, dtype, 0, 0, - NPY_ARRAY_ALIGNED, NULL); - if (ap1 == NULL) { - return NULL; - } - - Py_INCREF(dtype); - ap2 = (PyArrayObject *)PyArray_FromAny(in2, dtype, 0, 0, - NPY_ARRAY_ALIGNED, NULL); - if (ap2 == NULL) { - Py_DECREF(ap1); - return NULL; - } - - if (PyArray_NDIM(ap1) == 0 || PyArray_NDIM(ap2) == 0) { - /* Scalars are rejected */ - PyErr_SetString(PyExc_ValueError, - "Scalar operands are not allowed, use '*' instead"); - return NULL; - } - - nd1 = PyArray_NDIM(ap1); - nd2 = PyArray_NDIM(ap2); - -#if defined(HAVE_CBLAS) - if (nd1 <= 2 && nd2 <= 2 && - (NPY_DOUBLE == typenum || NPY_CDOUBLE == typenum || - NPY_FLOAT == typenum || NPY_CFLOAT == typenum)) { - return cblas_matrixproduct(typenum, ap1, ap2, (PyArrayObject *)out); - } -#endif - - /* - * Use einsum for the stacked cases. This is a quick implementation - * to avoid setting up the proper iterators. Einsum broadcasts, so - * we need to check dimensions before the call. - */ - if (nd1 == 1 && nd2 == 1) { - /* vector vector */ - if (PyArray_DIM(ap1, 0) != PyArray_DIM(ap2, 0)) { - dot_alignment_error(ap1, 0, ap2, 0); - goto fail; - } - subscripts = "i, i"; - } - else if (nd1 == 1) { - /* vector matrix */ - if (PyArray_DIM(ap1, 0) != PyArray_DIM(ap2, nd2 - 2)) { - dot_alignment_error(ap1, 0, ap2, nd2 - 2); - goto fail; - } - subscripts = "i, ...ij"; - } - else if (nd2 == 1) { - /* matrix vector */ - if (PyArray_DIM(ap1, nd1 - 1) != PyArray_DIM(ap2, 0)) { - dot_alignment_error(ap1, nd1 - 1, ap2, 0); - goto fail; - } - subscripts = "...i, i"; - } - else { - /* matrix * matrix */ - if (PyArray_DIM(ap1, nd1 - 1) != PyArray_DIM(ap2, nd2 - 2)) { - dot_alignment_error(ap1, nd1 - 1, ap2, nd2 - 2); - goto fail; - } - subscripts = "...ij, ...jk"; - } - ops[0] = ap1; - ops[1] = ap2; - ret = PyArray_EinsteinSum(subscripts, 2, ops, NULL, order, casting, - (PyArrayObject *)out); - Py_DECREF(ap1); - Py_DECREF(ap2); - - /* If no output was supplied, possibly convert to a scalar */ - if (ret != NULL && out == NULL) { - return PyArray_Return((PyArrayObject *)ret); - } - return (PyObject *)ret; - -fail: - Py_XDECREF(ap1); - Py_XDECREF(ap2); - return NULL; -} - - static int einsum_sub_op_from_str(PyObject *args, PyObject **str_obj, char **subscripts, PyArrayObject **op) @@ -2618,7 +2670,6 @@ einsum_list_to_subscripts(PyObject *obj, char *subscripts, int subsize) } size = PySequence_Size(obj); - for (i = 0; i < size; ++i) { item = PySequence_Fast_GET_ITEM(obj, i); /* Ellipsis */ @@ -2641,8 +2692,16 @@ einsum_list_to_subscripts(PyObject *obj, char *subscripts, int subsize) ellipsis = 1; } /* Subscript */ - else if (PyInt_Check(item) || PyLong_Check(item)) { - long s = PyInt_AsLong(item); + else { + npy_intp s = PyArray_PyIntAsIntp(item); + /* Invalid */ + if (error_converting(s)) { + PyErr_SetString(PyExc_TypeError, + "each subscript must be either an integer " + "or an ellipsis"); + Py_DECREF(obj); + return -1; + } npy_bool bad_input = 0; if (subindex + 1 >= subsize) { @@ -2652,7 +2711,7 @@ einsum_list_to_subscripts(PyObject *obj, char *subscripts, int subsize) return -1; } - if ( s < 0 ) { + if (s < 0) { bad_input = 1; } else if (s < 26) { @@ -2672,14 +2731,7 @@ einsum_list_to_subscripts(PyObject *obj, char *subscripts, int subsize) return -1; } } - /* Invalid */ - else { - PyErr_SetString(PyExc_ValueError, - "each subscript must be either an integer " - "or an ellipsis"); - Py_DECREF(obj); - return -1; - } + } Py_DECREF(obj); @@ -2707,7 +2759,7 @@ einsum_sub_op_from_lists(PyObject *args, "operand and a subscripts list to einsum"); return -1; } - else if(nop >= NPY_MAXARGS) { + else if (nop >= NPY_MAXARGS) { PyErr_SetString(PyExc_ValueError, "too many operands"); return -1; } @@ -2806,7 +2858,7 @@ array_einsum(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds) arg0 = PyTuple_GET_ITEM(args, 0); /* einsum('i,j', a, b), einsum('i,j->ij', a, b) */ - if (PyString_Check(arg0) || PyUnicode_Check(arg0)) { + if (PyBytes_Check(arg0) || PyUnicode_Check(arg0)) { nop = einsum_sub_op_from_str(args, &str_obj, &subscripts, op); } /* einsum(a, [0], b, [1]), einsum(a, [0], b, [1], [0,1]) */ @@ -2826,13 +2878,11 @@ array_einsum(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds) while (PyDict_Next(kwds, &pos, &key, &value)) { char *str = NULL; -#if defined(NPY_PY3K) Py_XDECREF(str_key_obj); str_key_obj = PyUnicode_AsASCIIString(key); if (str_key_obj != NULL) { key = str_key_obj; } -#endif str = PyBytes_AsString(key); @@ -2909,47 +2959,82 @@ array_fastCopyAndTranspose(PyObject *NPY_UNUSED(dummy), PyObject *args) } static PyObject * -array_correlate(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds) +array_correlate(PyObject *NPY_UNUSED(dummy), + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) { PyObject *shape, *a0; int mode = 0; - static char *kwlist[] = {"a", "v", "mode", NULL}; + NPY_PREPARE_ARGPARSER; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|i:correlate", kwlist, - &a0, &shape, &mode)) { + if (npy_parse_arguments("correlate", args, len_args, kwnames, + "a", NULL, &a0, + "v", NULL, &shape, + "|mode", &PyArray_CorrelatemodeConverter, &mode, + NULL, NULL, NULL) < 0) { return NULL; } return PyArray_Correlate(a0, shape, mode); } static PyObject* -array_correlate2(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds) +array_correlate2(PyObject *NPY_UNUSED(dummy), + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) { PyObject *shape, *a0; int mode = 0; - static char *kwlist[] = {"a", "v", "mode", NULL}; + NPY_PREPARE_ARGPARSER; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|i:correlate2", kwlist, - &a0, &shape, &mode)) { + if (npy_parse_arguments("correlate2", args, len_args, kwnames, + "a", NULL, &a0, + "v", NULL, &shape, + "|mode", &PyArray_CorrelatemodeConverter, &mode, + NULL, NULL, NULL) < 0) { return NULL; } return PyArray_Correlate2(a0, shape, mode); } static PyObject * -array_arange(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kws) { +array_arange(PyObject *NPY_UNUSED(ignored), + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) +{ PyObject *o_start = NULL, *o_stop = NULL, *o_step = NULL, *range=NULL; - static char *kwd[]= {"start", "stop", "step", "dtype", NULL}; PyArray_Descr *typecode = NULL; - - if(!PyArg_ParseTupleAndKeywords(args, kws, "O|OOO&:arange", kwd, - &o_start, - &o_stop, - &o_step, - PyArray_DescrConverter2, &typecode)) { + PyObject *like = NULL; + NPY_PREPARE_ARGPARSER; + + if (npy_parse_arguments("arange", args, len_args, kwnames, + "|start", NULL, &o_start, + "|stop", NULL, &o_stop, + "|step", NULL, &o_step, + "|dtype", &PyArray_DescrConverter2, &typecode, + "$like", NULL, &like, + NULL, NULL, NULL) < 0) { Py_XDECREF(typecode); return NULL; } + if (like != NULL) { + PyObject *deferred = array_implement_c_array_function_creation( + "arange", like, NULL, NULL, args, len_args, kwnames); + if (deferred != Py_NotImplemented) { + Py_XDECREF(typecode); + return deferred; + } + } + + if (o_stop == NULL) { + if (len_args == 0){ + PyErr_SetString(PyExc_TypeError, + "arange() requires stop to be specified."); + Py_XDECREF(typecode); + return NULL; + } + } + else if (o_start == NULL) { + o_start = o_stop; + o_stop = NULL; + } + range = PyArray_ArangeObj(o_start, o_stop, o_step, typecode); Py_XDECREF(typecode); @@ -2980,10 +3065,10 @@ array__get_ndarray_c_version(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObje { static char *kwlist[] = {NULL}; - if(!PyArg_ParseTupleAndKeywords(args, kwds, "", kwlist )) { + if (!PyArg_ParseTupleAndKeywords(args, kwds, "", kwlist )) { return NULL; } - return PyInt_FromLong( (long) PyArray_GetNDArrayCVersion() ); + return PyLong_FromLong( (long) PyArray_GetNDArrayCVersion() ); } /*NUMPY_API @@ -3053,7 +3138,7 @@ array_set_string_function(PyObject *NPY_UNUSED(self), PyObject *args, int repr = 1; static char *kwlist[] = {"f", "repr", NULL}; - if(!PyArg_ParseTupleAndKeywords(args, kwds, "|Oi:set_string_function", kwlist, &op, &repr)) { + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oi:set_string_function", kwlist, &op, &repr)) { return NULL; } /* reset the array_repr function to built-in */ @@ -3075,7 +3160,7 @@ array_set_ops_function(PyObject *NPY_UNUSED(self), PyObject *NPY_UNUSED(args), { PyObject *oldops = NULL; - if ((oldops = PyArray_GetNumericOps()) == NULL) { + if ((oldops = _PyArray_GetNumericOps()) == NULL) { return NULL; } /* @@ -3085,8 +3170,10 @@ array_set_ops_function(PyObject *NPY_UNUSED(self), PyObject *NPY_UNUSED(args), */ if (kwds && PyArray_SetNumericOps(kwds) == -1) { Py_DECREF(oldops); - PyErr_SetString(PyExc_ValueError, + if (PyErr_Occurred() == NULL) { + PyErr_SetString(PyExc_ValueError, "one or more objects not callable"); + } return NULL; } return oldops; @@ -3305,7 +3392,7 @@ array_can_cast_safely(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *from_obj = NULL; PyArray_Descr *d1 = NULL; PyArray_Descr *d2 = NULL; - npy_bool ret; + int ret; PyObject *retobj = NULL; NPY_CASTING casting = NPY_SAFE_CASTING; static char *kwlist[] = {"from_", "to", "casting", NULL}; @@ -3361,7 +3448,7 @@ array_promote_types(PyObject *NPY_UNUSED(dummy), PyObject *args) PyArray_Descr *d1 = NULL; PyArray_Descr *d2 = NULL; PyObject *ret = NULL; - if(!PyArg_ParseTuple(args, "O&O&:promote_types", + if (!PyArg_ParseTuple(args, "O&O&:promote_types", PyArray_DescrConverter2, &d1, PyArray_DescrConverter2, &d2)) { goto finish; } @@ -3387,7 +3474,7 @@ array_min_scalar_type(PyObject *NPY_UNUSED(dummy), PyObject *args) PyArrayObject *array; PyObject *ret = NULL; - if(!PyArg_ParseTuple(args, "O:min_scalar_type", &array_in)) { + if (!PyArg_ParseTuple(args, "O:min_scalar_type", &array_in)) { return NULL; } @@ -3435,6 +3522,10 @@ array_result_type(PyObject *NPY_UNUSED(dummy), PyObject *args) if (arr[narr] == NULL) { goto finish; } + if (PyLong_CheckExact(obj) || PyFloat_CheckExact(obj) || + PyComplex_CheckExact(obj)) { + ((PyArrayObject_fields *)arr[narr])->flags |= _NPY_ARRAY_WAS_PYSCALAR; + } ++narr; } else { @@ -3459,151 +3550,62 @@ array_result_type(PyObject *NPY_UNUSED(dummy), PyObject *args) } static PyObject * -array_datetime_data(PyObject *NPY_UNUSED(dummy), PyObject *args) -{ - PyArray_Descr *dtype; - PyArray_DatetimeMetaData *meta; - - if(!PyArg_ParseTuple(args, "O&:datetime_data", - PyArray_DescrConverter, &dtype)) { - return NULL; - } - - meta = get_datetime_metadata_from_dtype(dtype); - if (meta == NULL) { - return NULL; - } - - return convert_datetime_metadata_to_tuple(meta); -} - -#if !defined(NPY_PY3K) -static PyObject * -new_buffer(PyObject *NPY_UNUSED(dummy), PyObject *args) -{ - int size; - - if(!PyArg_ParseTuple(args, "i:buffer", &size)) { - return NULL; - } - return PyBuffer_New(size); -} - -static PyObject * -buffer_buffer(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds) +array_datetime_data(PyObject *NPY_UNUSED(dummy), PyObject *args) { - PyObject *obj; - Py_ssize_t offset = 0, n; - Py_ssize_t size = Py_END_OF_BUFFER; - void *unused; - static char *kwlist[] = {"object", "offset", "size", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "O|" NPY_SSIZE_T_PYFMT NPY_SSIZE_T_PYFMT ":get_buffer", kwlist, - &obj, &offset, &size)) { + PyArray_Descr *dtype; + PyArray_DatetimeMetaData *meta; + + if (!PyArg_ParseTuple(args, "O&:datetime_data", + PyArray_DescrConverter, &dtype)) { return NULL; } - if (PyObject_AsWriteBuffer(obj, &unused, &n) < 0) { - PyErr_Clear(); - return PyBuffer_FromObject(obj, offset, size); - } - else { - return PyBuffer_FromReadWriteObject(obj, offset, size); + + meta = get_datetime_metadata_from_dtype(dtype); + if (meta == NULL) { + Py_DECREF(dtype); + return NULL; } -} -#endif -#ifndef _MSC_VER -#include <setjmp.h> -#include <signal.h> -jmp_buf _NPY_SIGSEGV_BUF; -static void -_SigSegv_Handler(int signum) -{ - longjmp(_NPY_SIGSEGV_BUF, signum); + PyObject *res = convert_datetime_metadata_to_tuple(meta); + Py_DECREF(dtype); + return res; } -#endif -#define _test_code() { \ - test = *((char*)memptr); \ - if (!ro) { \ - *((char *)memptr) = '\0'; \ - *((char *)memptr) = test; \ - } \ - test = *((char*)memptr+size-1); \ - if (!ro) { \ - *((char *)memptr+size-1) = '\0'; \ - *((char *)memptr+size-1) = test; \ - } \ - } -static PyObject * -as_buffer(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds) +static int +trimmode_converter(PyObject *obj, TrimMode *trim) { - PyObject *mem; - Py_ssize_t size; - npy_bool ro = NPY_FALSE, check = NPY_TRUE; - void *memptr; - static char *kwlist[] = {"mem", "size", "readonly", "check", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "O" NPY_SSIZE_T_PYFMT "|O&O&:int_asbuffer", kwlist, - &mem, &size, PyArray_BoolConverter, &ro, - PyArray_BoolConverter, &check)) { - return NULL; - } - memptr = PyLong_AsVoidPtr(mem); - if (memptr == NULL) { - return NULL; + if (!PyUnicode_Check(obj) || PyUnicode_GetLength(obj) != 1) { + goto error; } - if (check) { - /* - * Try to dereference the start and end of the memory region - * Catch segfault and report error if it occurs - */ - char test; - int err = 0; + const char *trimstr = PyUnicode_AsUTF8AndSize(obj, NULL); -#ifdef _MSC_VER - __try { - _test_code(); + if (trimstr != NULL) { + if (trimstr[0] == 'k') { + *trim = TrimMode_None; } - __except(1) { - err = 1; + else if (trimstr[0] == '.') { + *trim = TrimMode_Zeros; } -#else - PyOS_sighandler_t _npy_sig_save; - _npy_sig_save = PyOS_setsig(SIGSEGV, _SigSegv_Handler); - if (setjmp(_NPY_SIGSEGV_BUF) == 0) { - _test_code(); + else if (trimstr[0] == '0') { + *trim = TrimMode_LeaveOneZero; } - else { - err = 1; + else if (trimstr[0] == '-') { + *trim = TrimMode_DptZeros; } - PyOS_setsig(SIGSEGV, _npy_sig_save); -#endif - if (err) { - PyErr_SetString(PyExc_ValueError, - "cannot use memory location as a buffer."); - return NULL; + else { + goto error; } } + return NPY_SUCCEED; - -#if defined(NPY_PY3K) - PyErr_SetString(PyExc_RuntimeError, - "XXX -- not implemented!"); - return NULL; -#else - if (ro) { - return PyBuffer_FromMemory(memptr, size); - } - return PyBuffer_FromReadWriteMemory(memptr, size); -#endif +error: + PyErr_Format(PyExc_TypeError, + "if supplied, trim must be 'k', '.', '0' or '-' found `%100S`", + obj); + return NPY_FAIL; } -#undef _test_code - /* * Prints floating-point scalars using the Dragon4 algorithm, scientific mode. @@ -3612,43 +3614,29 @@ as_buffer(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds) * precision, which is equivalent to `None`. */ static PyObject * -dragon4_scientific(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds) +dragon4_scientific(PyObject *NPY_UNUSED(dummy), + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) { PyObject *obj; - static char *kwlist[] = {"x", "precision", "unique", "sign", "trim", - "pad_left", "exp_digits", NULL}; - int precision=-1, pad_left=-1, exp_digits=-1; - char *trimstr=NULL; + int precision=-1, pad_left=-1, exp_digits=-1, min_digits=-1; DigitMode digit_mode; TrimMode trim = TrimMode_None; int sign=0, unique=1; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|iiisii:dragon4_scientific", - kwlist, &obj, &precision, &unique, &sign, &trimstr, &pad_left, - &exp_digits)) { + NPY_PREPARE_ARGPARSER; + + if (npy_parse_arguments("dragon4_scientific", args, len_args, kwnames, + "x", NULL , &obj, + "|precision", &PyArray_PythonPyIntFromInt, &precision, + "|unique", &PyArray_PythonPyIntFromInt, &unique, + "|sign", &PyArray_PythonPyIntFromInt, &sign, + "|trim", &trimmode_converter, &trim, + "|pad_left", &PyArray_PythonPyIntFromInt, &pad_left, + "|exp_digits", &PyArray_PythonPyIntFromInt, &exp_digits, + "|min_digits", &PyArray_PythonPyIntFromInt, &min_digits, + NULL, NULL, NULL) < 0) { return NULL; } - if (trimstr != NULL) { - if (strcmp(trimstr, "k") == 0) { - trim = TrimMode_None; - } - else if (strcmp(trimstr, ".") == 0) { - trim = TrimMode_Zeros; - } - else if (strcmp(trimstr, "0") == 0) { - trim = TrimMode_LeaveOneZero; - } - else if (strcmp(trimstr, "-") == 0) { - trim = TrimMode_DptZeros; - } - else { - PyErr_SetString(PyExc_TypeError, - "if supplied, trim must be 'k', '.', '0' or '-'"); - return NULL; - } - } - digit_mode = unique ? DigitMode_Unique : DigitMode_Exact; if (unique == 0 && precision < 0) { @@ -3657,7 +3645,7 @@ dragon4_scientific(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds) return NULL; } - return Dragon4_Scientific(obj, digit_mode, precision, sign, trim, + return Dragon4_Scientific(obj, digit_mode, precision, min_digits, sign, trim, pad_left, exp_digits); } @@ -3668,44 +3656,31 @@ dragon4_scientific(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds) * precision, which is equivalent to `None`. */ static PyObject * -dragon4_positional(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds) +dragon4_positional(PyObject *NPY_UNUSED(dummy), + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) { PyObject *obj; - static char *kwlist[] = {"x", "precision", "unique", "fractional", - "sign", "trim", "pad_left", "pad_right", NULL}; - int precision=-1, pad_left=-1, pad_right=-1; - char *trimstr=NULL; + int precision=-1, pad_left=-1, pad_right=-1, min_digits=-1; CutoffMode cutoff_mode; DigitMode digit_mode; TrimMode trim = TrimMode_None; int sign=0, unique=1, fractional=0; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|iiiisii:dragon4_positional", - kwlist, &obj, &precision, &unique, &fractional, &sign, &trimstr, - &pad_left, &pad_right)) { + NPY_PREPARE_ARGPARSER; + + if (npy_parse_arguments("dragon4_positional", args, len_args, kwnames, + "x", NULL , &obj, + "|precision", &PyArray_PythonPyIntFromInt, &precision, + "|unique", &PyArray_PythonPyIntFromInt, &unique, + "|fractional", &PyArray_PythonPyIntFromInt, &fractional, + "|sign", &PyArray_PythonPyIntFromInt, &sign, + "|trim", &trimmode_converter, &trim, + "|pad_left", &PyArray_PythonPyIntFromInt, &pad_left, + "|pad_right", &PyArray_PythonPyIntFromInt, &pad_right, + "|min_digits", &PyArray_PythonPyIntFromInt, &min_digits, + NULL, NULL, NULL) < 0) { return NULL; } - if (trimstr != NULL) { - if (strcmp(trimstr, "k") == 0) { - trim = TrimMode_None; - } - else if (strcmp(trimstr, ".") == 0) { - trim = TrimMode_Zeros; - } - else if (strcmp(trimstr, "0") == 0) { - trim = TrimMode_LeaveOneZero; - } - else if (strcmp(trimstr, "-") == 0) { - trim = TrimMode_DptZeros; - } - else { - PyErr_SetString(PyExc_TypeError, - "if supplied, trim must be 'k', '.', '0' or '-'"); - return NULL; - } - } - digit_mode = unique ? DigitMode_Unique : DigitMode_Exact; cutoff_mode = fractional ? CutoffMode_FractionLength : CutoffMode_TotalLength; @@ -3716,8 +3691,8 @@ dragon4_positional(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds) return NULL; } - return Dragon4_Positional(obj, digit_mode, cutoff_mode, precision, sign, - trim, pad_left, pad_right); + return Dragon4_Positional(obj, digit_mode, cutoff_mode, precision, + min_digits, sign, trim, pad_left, pad_right); } static PyObject * @@ -3736,7 +3711,7 @@ format_longfloat(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds) "not a longfloat"); return NULL; } - return Dragon4_Scientific(obj, DigitMode_Unique, precision, 0, + return Dragon4_Scientific(obj, DigitMode_Unique, precision, -1, 0, TrimMode_LeaveOneZero, -1, -1); } @@ -3834,6 +3809,7 @@ _vec_string_with_args(PyArrayObject* char_array, PyArray_Descr* type, if (nargs == -1 || nargs > NPY_MAXARGS) { PyErr_Format(PyExc_ValueError, "len(args) must be < %d", NPY_MAXARGS - 1); + Py_DECREF(type); goto err; } @@ -3841,6 +3817,7 @@ _vec_string_with_args(PyArrayObject* char_array, PyArray_Descr* type, for (i = 1; i < nargs; i++) { PyObject* item = PySequence_GetItem(args, i-1); if (item == NULL) { + Py_DECREF(type); goto err; } broadcast_args[i] = item; @@ -3849,6 +3826,7 @@ _vec_string_with_args(PyArrayObject* char_array, PyArray_Descr* type, in_iter = (PyArrayMultiIterObject*)PyArray_MultiIterFromObjects (broadcast_args, nargs, 0); if (in_iter == NULL) { + Py_DECREF(type); goto err; } n = in_iter->numiter; @@ -3929,6 +3907,7 @@ _vec_string_no_args(PyArrayObject* char_array, in_iter = (PyArrayIterObject*)PyArray_IterNew((PyObject*)char_array); if (in_iter == NULL) { + Py_DECREF(type); goto err; } @@ -3982,10 +3961,10 @@ _vec_string_no_args(PyArrayObject* char_array, } static PyObject * -_vec_string(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds) +_vec_string(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *NPY_UNUSED(kwds)) { PyArrayObject* char_array = NULL; - PyArray_Descr *type = NULL; + PyArray_Descr *type; PyObject* method_name; PyObject* args_seq = NULL; @@ -4000,7 +3979,7 @@ _vec_string(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds) } if (PyArray_TYPE(char_array) == NPY_STRING) { - method = PyObject_GetAttr((PyObject *)&PyString_Type, method_name); + method = PyObject_GetAttr((PyObject *)&PyBytes_Type, method_name); } else if (PyArray_TYPE(char_array) == NPY_UNICODE) { method = PyObject_GetAttr((PyObject *)&PyUnicode_Type, method_name); @@ -4008,9 +3987,11 @@ _vec_string(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds) else { PyErr_SetString(PyExc_TypeError, "string operation on non-string array"); + Py_DECREF(type); goto err; } if (method == NULL) { + Py_DECREF(type); goto err; } @@ -4022,6 +4003,7 @@ _vec_string(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds) result = _vec_string_with_args(char_array, type, method, args_seq); } else { + Py_DECREF(type); PyErr_SetString(PyExc_TypeError, "'args' must be a sequence of arguments"); goto err; @@ -4092,36 +4074,6 @@ _PyArray_GetSigintBuf(void) #endif -static PyObject * -test_interrupt(PyObject *NPY_UNUSED(self), PyObject *args) -{ - int kind = 0; - int a = 0; - - if (!PyArg_ParseTuple(args, "|i:test_interrupt", &kind)) { - return NULL; - } - if (kind) { - Py_BEGIN_ALLOW_THREADS; - while (a >= 0) { - if ((a % 1000 == 0) && PyOS_InterruptOccurred()) { - break; - } - a += 1; - } - Py_END_ALLOW_THREADS; - } - else { - NPY_SIGINT_ON - while(a >= 0) { - a += 1; - } - NPY_SIGINT_OFF - } - return PyInt_FromLong(a); -} - - static PyObject * array_shares_memory_impl(PyObject *args, PyObject *kwds, Py_ssize_t default_max_work, int raise_exceptions) @@ -4178,11 +4130,6 @@ array_shares_memory_impl(PyObject *args, PyObject *kwds, Py_ssize_t default_max_ goto fail; } } -#if !defined(NPY_PY3K) - else if (PyInt_Check(max_work_obj)) { - max_work = PyInt_AsSsize_t(max_work_obj); - } -#endif else { PyErr_SetString(PyExc_ValueError, "max_work must be an integer"); goto fail; @@ -4219,7 +4166,7 @@ array_shares_memory_impl(PyObject *args, PyObject *kwds, Py_ssize_t default_max_ } else if (result == MEM_OVERLAP_TOO_HARD) { if (raise_exceptions) { - npy_cache_import("numpy.core._internal", "TooHardError", + npy_cache_import("numpy.core._exceptions", "TooHardError", &too_hard_cls); if (too_hard_cls) { PyErr_SetString(too_hard_cls, "Exceeded max_work"); @@ -4259,25 +4206,68 @@ array_may_share_memory(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject * } static PyObject * -normalize_axis_index(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds) +normalize_axis_index(PyObject *NPY_UNUSED(self), + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) { - static char *kwlist[] = {"axis", "ndim", "msg_prefix", NULL}; int axis; int ndim; PyObject *msg_prefix = Py_None; + NPY_PREPARE_ARGPARSER; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "ii|O:normalize_axis_index", - kwlist, &axis, &ndim, &msg_prefix)) { + if (npy_parse_arguments("normalize_axis_index", args, len_args, kwnames, + "axis", &PyArray_PythonPyIntFromInt, &axis, + "ndim", &PyArray_PythonPyIntFromInt, &ndim, + "|msg_prefix", NULL, &msg_prefix, + NULL, NULL, NULL) < 0) { return NULL; } if (check_and_adjust_axis_msg(&axis, ndim, msg_prefix) < 0) { return NULL; } - return PyInt_FromLong(axis); + return PyLong_FromLong(axis); +} + + +static PyObject * +_reload_guard(PyObject *NPY_UNUSED(self), PyObject *NPY_UNUSED(args)) { + static int initialized = 0; + +#if !defined(PYPY_VERSION) + if (PyThreadState_Get()->interp != PyInterpreterState_Main()) { + if (PyErr_WarnEx(PyExc_UserWarning, + "NumPy was imported from a Python sub-interpreter but " + "NumPy does not properly support sub-interpreters. " + "This will likely work for most users but might cause hard to " + "track down issues or subtle bugs. " + "A common user of the rare sub-interpreter feature is wsgi " + "which also allows single-interpreter mode.\n" + "Improvements in the case of bugs are welcome, but is not " + "on the NumPy roadmap, and full support may require " + "significant effort to achieve.", 2) < 0) { + return NULL; + } + /* No need to give the other warning in a sub-interpreter as well... */ + initialized = 1; + Py_RETURN_NONE; + } +#endif + if (initialized) { + if (PyErr_WarnEx(PyExc_UserWarning, + "The NumPy module was reloaded (imported a second time). " + "This can in some cases result in small but subtle issues " + "and is discouraged.", 2) < 0) { + return NULL; + } + } + initialized = 1; + Py_RETURN_NONE; } static struct PyMethodDef array_module_methods[] = { + {"_get_implementing_args", + (PyCFunction)array__get_implementing_args, + METH_VARARGS, NULL}, {"_get_ndarray_c_version", (PyCFunction)array__get_ndarray_c_version, METH_VARARGS|METH_KEYWORDS, NULL}, @@ -4297,8 +4287,20 @@ static struct PyMethodDef array_module_methods[] = { (PyCFunction)array_set_typeDict, METH_VARARGS, NULL}, {"array", - (PyCFunction)_array_fromobject, - METH_VARARGS|METH_KEYWORDS, NULL}, + (PyCFunction)array_array, + METH_FASTCALL | METH_KEYWORDS, NULL}, + {"asarray", + (PyCFunction)array_asarray, + METH_FASTCALL | METH_KEYWORDS, NULL}, + {"asanyarray", + (PyCFunction)array_asanyarray, + METH_FASTCALL | METH_KEYWORDS, NULL}, + {"ascontiguousarray", + (PyCFunction)array_ascontiguousarray, + METH_FASTCALL | METH_KEYWORDS, NULL}, + {"asfortranarray", + (PyCFunction)array_asfortranarray, + METH_FASTCALL | METH_KEYWORDS, NULL}, {"copyto", (PyCFunction)array_copyto, METH_VARARGS|METH_KEYWORDS, NULL}, @@ -4307,16 +4309,16 @@ static struct PyMethodDef array_module_methods[] = { METH_VARARGS|METH_KEYWORDS, NULL}, {"arange", (PyCFunction)array_arange, - METH_VARARGS|METH_KEYWORDS, NULL}, + METH_FASTCALL | METH_KEYWORDS, NULL}, {"zeros", (PyCFunction)array_zeros, - METH_VARARGS|METH_KEYWORDS, NULL}, + METH_FASTCALL | METH_KEYWORDS, NULL}, {"count_nonzero", (PyCFunction)array_count_nonzero, METH_VARARGS|METH_KEYWORDS, NULL}, {"empty", (PyCFunction)array_empty, - METH_VARARGS|METH_KEYWORDS, NULL}, + METH_FASTCALL | METH_KEYWORDS, NULL}, {"empty_like", (PyCFunction)array_empty_like, METH_VARARGS|METH_KEYWORDS, NULL}, @@ -4350,9 +4352,6 @@ static struct PyMethodDef array_module_methods[] = { {"vdot", (PyCFunction)array_vdot, METH_VARARGS | METH_KEYWORDS, NULL}, - {"matmul", - (PyCFunction)array_matmul, - METH_VARARGS | METH_KEYWORDS, NULL}, {"c_einsum", (PyCFunction)array_einsum, METH_VARARGS|METH_KEYWORDS, NULL}, @@ -4361,10 +4360,10 @@ static struct PyMethodDef array_module_methods[] = { METH_VARARGS, NULL}, {"correlate", (PyCFunction)array_correlate, - METH_VARARGS | METH_KEYWORDS, NULL}, + METH_FASTCALL | METH_KEYWORDS, NULL}, {"correlate2", (PyCFunction)array_correlate2, - METH_VARARGS | METH_KEYWORDS, NULL}, + METH_FASTCALL | METH_KEYWORDS, NULL}, {"frombuffer", (PyCFunction)array_frombuffer, METH_VARARGS | METH_KEYWORDS, NULL}, @@ -4406,43 +4405,32 @@ static struct PyMethodDef array_module_methods[] = { {"is_busday", (PyCFunction)array_is_busday, METH_VARARGS | METH_KEYWORDS, NULL}, -#if !defined(NPY_PY3K) - {"newbuffer", - (PyCFunction)new_buffer, - METH_VARARGS, NULL}, - {"getbuffer", - (PyCFunction)buffer_buffer, - METH_VARARGS | METH_KEYWORDS, NULL}, -#endif - {"int_asbuffer", - (PyCFunction)as_buffer, - METH_VARARGS | METH_KEYWORDS, NULL}, {"format_longfloat", (PyCFunction)format_longfloat, METH_VARARGS | METH_KEYWORDS, NULL}, {"dragon4_positional", (PyCFunction)dragon4_positional, - METH_VARARGS | METH_KEYWORDS, NULL}, + METH_FASTCALL | METH_KEYWORDS, NULL}, {"dragon4_scientific", (PyCFunction)dragon4_scientific, - METH_VARARGS | METH_KEYWORDS, NULL}, + METH_FASTCALL | METH_KEYWORDS, NULL}, {"compare_chararrays", (PyCFunction)compare_chararrays, METH_VARARGS | METH_KEYWORDS, NULL}, {"_vec_string", (PyCFunction)_vec_string, METH_VARARGS | METH_KEYWORDS, NULL}, - {"test_interrupt", - (PyCFunction)test_interrupt, - METH_VARARGS, NULL}, {"_insert", (PyCFunction)arr_insert, METH_VARARGS | METH_KEYWORDS, "Insert vals sequentially into equivalent 1-d positions " "indicated by mask."}, {"bincount", (PyCFunction)arr_bincount, METH_VARARGS | METH_KEYWORDS, NULL}, - {"digitize", (PyCFunction)arr_digitize, + {"_monotonicity", (PyCFunction)arr__monotonicity, METH_VARARGS | METH_KEYWORDS, NULL}, + {"implement_array_function", + (PyCFunction)array_implement_array_function, + METH_VARARGS, NULL}, {"interp", (PyCFunction)arr_interp, METH_VARARGS | METH_KEYWORDS, NULL}, {"interp_complex", (PyCFunction)arr_interp_complex, @@ -4458,13 +4446,47 @@ static struct PyMethodDef array_module_methods[] = { {"unpackbits", (PyCFunction)io_unpack, METH_VARARGS | METH_KEYWORDS, NULL}, {"normalize_axis_index", (PyCFunction)normalize_axis_index, - METH_VARARGS | METH_KEYWORDS, NULL}, + METH_FASTCALL | METH_KEYWORDS, NULL}, {"set_legacy_print_mode", (PyCFunction)set_legacy_print_mode, METH_VARARGS, NULL}, + {"_discover_array_parameters", (PyCFunction)_discover_array_parameters, + METH_VARARGS | METH_KEYWORDS, NULL}, + {"_get_castingimpl", (PyCFunction)_get_castingimpl, + METH_VARARGS | METH_KEYWORDS, NULL}, + {"_get_experimental_dtype_api", (PyCFunction)_get_experimental_dtype_api, + METH_O, NULL}, + /* from umath */ + {"frompyfunc", + (PyCFunction) ufunc_frompyfunc, + METH_VARARGS | METH_KEYWORDS, NULL}, + {"seterrobj", + (PyCFunction) ufunc_seterr, + METH_VARARGS, NULL}, + {"geterrobj", + (PyCFunction) ufunc_geterr, + METH_VARARGS, NULL}, + {"get_handler_name", + (PyCFunction) get_handler_name, + METH_VARARGS, NULL}, + {"get_handler_version", + (PyCFunction) get_handler_version, + METH_VARARGS, NULL}, + {"_add_newdoc_ufunc", (PyCFunction)add_newdoc_ufunc, + METH_VARARGS, NULL}, + {"_get_sfloat_dtype", + get_sfloat_dtype, METH_NOARGS, NULL}, + {"_set_madvise_hugepage", (PyCFunction)_set_madvise_hugepage, + METH_O, NULL}, + {"_reload_guard", (PyCFunction)_reload_guard, + METH_NOARGS, + "Give a warning on reload and big warning in sub-interpreters."}, + {"_from_dlpack", (PyCFunction)_from_dlpack, + METH_O, NULL}, {NULL, NULL, 0, NULL} /* sentinel */ }; #include "__multiarray_api.c" +#include "array_method.h" /* Establish scalar-type hierarchy * @@ -4478,24 +4500,16 @@ static struct PyMethodDef array_module_methods[] = { static int setup_scalartypes(PyObject *NPY_UNUSED(dict)) { - initialize_casting_tables(); - initialize_numeric_types(); - if (PyType_Ready(&PyBool_Type) < 0) { return -1; } -#if !defined(NPY_PY3K) - if (PyType_Ready(&PyInt_Type) < 0) { - return -1; - } -#endif if (PyType_Ready(&PyFloat_Type) < 0) { return -1; } if (PyType_Ready(&PyComplex_Type) < 0) { return -1; } - if (PyType_Ready(&PyString_Type) < 0) { + if (PyType_Ready(&PyBytes_Type) < 0) { return -1; } if (PyType_Ready(&PyUnicode_Type) < 0) { @@ -4539,27 +4553,6 @@ setup_scalartypes(PyObject *NPY_UNUSED(dict)) return -1; \ } -/* - * In Py3K, int is no longer a fixed-width integer type, so don't - * inherit numpy.int_ from it. - */ -#if defined(NPY_PY3K) -#define INHERIT_INT(child, parent2) \ - SINGLE_INHERIT(child, parent2); -#else -#define INHERIT_INT(child, parent2) \ - Py##child##ArrType_Type.tp_flags |= Py_TPFLAGS_INT_SUBCLASS; \ - DUAL_INHERIT(child, Int, parent2); -#endif - -#if defined(NPY_PY3K) -#define DUAL_INHERIT_COMPARE(child, parent1, parent2) -#else -#define DUAL_INHERIT_COMPARE(child, parent1, parent2) \ - Py##child##ArrType_Type.tp_compare = \ - Py##parent1##_Type.tp_compare; -#endif - #define DUAL_INHERIT2(child, parent1, parent2) \ Py##child##ArrType_Type.tp_base = &Py##parent1##_Type; \ Py##child##ArrType_Type.tp_bases = \ @@ -4567,7 +4560,6 @@ setup_scalartypes(PyObject *NPY_UNUSED(dict)) &Py##parent2##ArrType_Type); \ Py##child##ArrType_Type.tp_richcompare = \ Py##parent1##_Type.tp_richcompare; \ - DUAL_INHERIT_COMPARE(child, parent1, parent2) \ Py##child##ArrType_Type.tp_hash = Py##parent1##_Type.tp_hash; \ if (PyType_Ready(&Py##child##ArrType_Type) < 0) { \ PyErr_Print(); \ @@ -4580,32 +4572,15 @@ setup_scalartypes(PyObject *NPY_UNUSED(dict)) SINGLE_INHERIT(Bool, Generic); SINGLE_INHERIT(Byte, SignedInteger); SINGLE_INHERIT(Short, SignedInteger); - -#if NPY_SIZEOF_INT == NPY_SIZEOF_LONG - INHERIT_INT(Int, SignedInteger); -#else SINGLE_INHERIT(Int, SignedInteger); -#endif - - INHERIT_INT(Long, SignedInteger); - -#if NPY_SIZEOF_LONGLONG == NPY_SIZEOF_LONG - INHERIT_INT(LongLong, SignedInteger); -#else + SINGLE_INHERIT(Long, SignedInteger); SINGLE_INHERIT(LongLong, SignedInteger); -#endif /* Datetime doesn't fit in any category */ SINGLE_INHERIT(Datetime, Generic); /* Timedelta is an integer with an associated unit */ SINGLE_INHERIT(Timedelta, SignedInteger); - /* - fprintf(stderr, - "tp_free = %p, PyObject_Del = %p, int_tp_free = %p, base.tp_free = %p\n", - PyIntArrType_Type.tp_free, PyObject_Del, PyInt_Type.tp_free, - PySignedIntegerArrType_Type.tp_free); - */ SINGLE_INHERIT(UByte, UnsignedInteger); SINGLE_INHERIT(UShort, UnsignedInteger); SINGLE_INHERIT(UInt, UnsignedInteger); @@ -4632,9 +4607,7 @@ setup_scalartypes(PyObject *NPY_UNUSED(dict)) #undef SINGLE_INHERIT #undef DUAL_INHERIT -#undef INHERIT_INT #undef DUAL_INHERIT2 -#undef DUAL_INHERIT_COMPARE /* * Clean up string and unicode array types so they act more like @@ -4653,13 +4626,13 @@ set_flaginfo(PyObject *d) newd = PyDict_New(); #define _addnew(key, val, one) \ - PyDict_SetItemString(newd, #key, s=PyInt_FromLong(val)); \ + PyDict_SetItemString(newd, #key, s=PyLong_FromLong(val)); \ Py_DECREF(s); \ - PyDict_SetItemString(newd, #one, s=PyInt_FromLong(val)); \ + PyDict_SetItemString(newd, #one, s=PyLong_FromLong(val)); \ Py_DECREF(s) #define _addone(key, val) \ - PyDict_SetItemString(newd, #key, s=PyInt_FromLong(val)); \ + PyDict_SetItemString(newd, #key, s=PyLong_FromLong(val)); \ Py_DECREF(s) _addnew(OWNDATA, NPY_ARRAY_OWNDATA, O); @@ -4680,47 +4653,51 @@ set_flaginfo(PyObject *d) return; } -NPY_VISIBILITY_HIDDEN PyObject * npy_ma_str_array = NULL; -NPY_VISIBILITY_HIDDEN PyObject * npy_ma_str_array_prepare = NULL; NPY_VISIBILITY_HIDDEN PyObject * npy_ma_str_array_wrap = NULL; NPY_VISIBILITY_HIDDEN PyObject * npy_ma_str_array_finalize = NULL; -NPY_VISIBILITY_HIDDEN PyObject * npy_ma_str_buffer = NULL; -NPY_VISIBILITY_HIDDEN PyObject * npy_ma_str_ufunc = NULL; -NPY_VISIBILITY_HIDDEN PyObject * npy_ma_str_order = NULL; -NPY_VISIBILITY_HIDDEN PyObject * npy_ma_str_copy = NULL; -NPY_VISIBILITY_HIDDEN PyObject * npy_ma_str_dtype = NULL; -NPY_VISIBILITY_HIDDEN PyObject * npy_ma_str_ndmin = NULL; +NPY_VISIBILITY_HIDDEN PyObject * npy_ma_str_implementation = NULL; NPY_VISIBILITY_HIDDEN PyObject * npy_ma_str_axis1 = NULL; NPY_VISIBILITY_HIDDEN PyObject * npy_ma_str_axis2 = NULL; +NPY_VISIBILITY_HIDDEN PyObject * npy_ma_str_like = NULL; +NPY_VISIBILITY_HIDDEN PyObject * npy_ma_str_numpy = NULL; static int intern_strings(void) { - npy_ma_str_array = PyUString_InternFromString("__array__"); - npy_ma_str_array_prepare = PyUString_InternFromString("__array_prepare__"); - npy_ma_str_array_wrap = PyUString_InternFromString("__array_wrap__"); - npy_ma_str_array_finalize = PyUString_InternFromString("__array_finalize__"); - npy_ma_str_buffer = PyUString_InternFromString("__buffer__"); - npy_ma_str_ufunc = PyUString_InternFromString("__array_ufunc__"); - npy_ma_str_order = PyUString_InternFromString("order"); - npy_ma_str_copy = PyUString_InternFromString("copy"); - npy_ma_str_dtype = PyUString_InternFromString("dtype"); - npy_ma_str_ndmin = PyUString_InternFromString("ndmin"); - npy_ma_str_axis1 = PyUString_InternFromString("axis1"); - npy_ma_str_axis2 = PyUString_InternFromString("axis2"); - - return npy_ma_str_array && npy_ma_str_array_prepare && - npy_ma_str_array_wrap && npy_ma_str_array_finalize && - npy_ma_str_buffer && npy_ma_str_ufunc && - npy_ma_str_order && npy_ma_str_copy && npy_ma_str_dtype && - npy_ma_str_ndmin && npy_ma_str_axis1 && npy_ma_str_axis2; + npy_ma_str_array_wrap = PyUnicode_InternFromString("__array_wrap__"); + if (npy_ma_str_array_wrap == NULL) { + return -1; + } + npy_ma_str_array_finalize = PyUnicode_InternFromString("__array_finalize__"); + if (npy_ma_str_array_finalize == NULL) { + return -1; + } + npy_ma_str_implementation = PyUnicode_InternFromString("_implementation"); + if (npy_ma_str_implementation == NULL) { + return -1; + } + npy_ma_str_axis1 = PyUnicode_InternFromString("axis1"); + if (npy_ma_str_axis1 == NULL) { + return -1; + } + npy_ma_str_axis2 = PyUnicode_InternFromString("axis2"); + if (npy_ma_str_axis2 == NULL) { + return -1; + } + npy_ma_str_like = PyUnicode_InternFromString("like"); + if (npy_ma_str_like == NULL) { + return -1; + } + npy_ma_str_numpy = PyUnicode_InternFromString("numpy"); + if (npy_ma_str_numpy == NULL) { + return -1; + } + return 0; } - -#if defined(NPY_PY3K) static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, - "multiarray", + "_multiarray_umath", NULL, -1, array_module_methods, @@ -4729,26 +4706,20 @@ static struct PyModuleDef moduledef = { NULL, NULL }; -#endif /* Initialization function for the module */ -#if defined(NPY_PY3K) -#define RETVAL(x) x -PyMODINIT_FUNC PyInit_multiarray(void) { -#else -#define RETVAL(x) -PyMODINIT_FUNC initmultiarray(void) { -#endif +PyMODINIT_FUNC PyInit__multiarray_umath(void) { PyObject *m, *d, *s; PyObject *c_api; /* Create the module and add the functions */ -#if defined(NPY_PY3K) m = PyModule_Create(&moduledef); -#else - m = Py_InitModule("multiarray", array_module_methods); -#endif if (!m) { + return NULL; + } + + /* Initialize CPU features */ + if (npy_cpu_init() < 0) { goto err; } @@ -4774,18 +4745,35 @@ PyMODINIT_FUNC initmultiarray(void) { goto err; } - /* - * Before calling PyType_Ready, initialize the tp_hash slot in - * PyArray_Type to work around mingw32 not being able initialize - * static structure slots with functions from the Python C_API. - */ - PyArray_Type.tp_hash = PyObject_HashNotImplemented; + if (PyType_Ready(&PyUFunc_Type) < 0) { + goto err; + } + + PyArrayDTypeMeta_Type.tp_base = &PyType_Type; + if (PyType_Ready(&PyArrayDTypeMeta_Type) < 0) { + goto err; + } + + PyArrayDescr_Type.tp_hash = PyArray_DescrHash; + Py_SET_TYPE(&PyArrayDescr_Type, &PyArrayDTypeMeta_Type); + if (PyType_Ready(&PyArrayDescr_Type) < 0) { + goto err; + } + + initialize_casting_tables(); + initialize_numeric_types(); + + if (initscalarmath(m) < 0) { + goto err; + } + if (PyType_Ready(&PyArray_Type) < 0) { goto err; } if (setup_scalartypes(d) < 0) { goto err; } + PyArrayIter_Type.tp_iter = PyObject_SelfIter; NpyIter_Type.tp_iter = PyObject_SelfIter; PyArrayMultiIter_Type.tp_iter = PyObject_SelfIter; @@ -4807,10 +4795,6 @@ PyMODINIT_FUNC initmultiarray(void) { goto err; } - PyArrayDescr_Type.tp_hash = PyArray_DescrHash; - if (PyType_Ready(&PyArrayDescr_Type) < 0) { - goto err; - } if (PyType_Ready(&PyArrayFlags_Type) < 0) { goto err; } @@ -4819,13 +4803,23 @@ PyMODINIT_FUNC initmultiarray(void) { goto err; } - c_api = NpyCapsule_FromVoidPtr((void *)PyArray_API, NULL); + c_api = PyCapsule_New((void *)PyArray_API, NULL, NULL); if (c_api == NULL) { goto err; } PyDict_SetItemString(d, "_ARRAY_API", c_api); Py_DECREF(c_api); + c_api = PyCapsule_New((void *)PyUFunc_API, NULL, NULL); + if (c_api == NULL) { + goto err; + } + PyDict_SetItemString(d, "_UFUNC_API", c_api); + Py_DECREF(c_api); + if (PyErr_Occurred()) { + goto err; + } + /* * PyExc_Exception should catch all the standard errors that are * now raised instead of the string exception "multiarray.error" @@ -4834,15 +4828,45 @@ PyMODINIT_FUNC initmultiarray(void) { */ PyDict_SetItemString (d, "error", PyExc_Exception); - s = PyInt_FromLong(NPY_TRACE_DOMAIN); + s = PyLong_FromLong(NPY_TRACE_DOMAIN); PyDict_SetItemString(d, "tracemalloc_domain", s); Py_DECREF(s); - s = PyUString_FromString("3.1"); + s = PyUnicode_FromString("3.1"); PyDict_SetItemString(d, "__version__", s); Py_DECREF(s); - s = NpyCapsule_FromVoidPtr((void *)_datetime_strings, NULL); + s = npy_cpu_features_dict(); + if (s == NULL) { + goto err; + } + if (PyDict_SetItemString(d, "__cpu_features__", s) < 0) { + Py_DECREF(s); + goto err; + } + Py_DECREF(s); + + s = npy_cpu_baseline_list(); + if (s == NULL) { + goto err; + } + if (PyDict_SetItemString(d, "__cpu_baseline__", s) < 0) { + Py_DECREF(s); + goto err; + } + Py_DECREF(s); + + s = npy_cpu_dispatch_list(); + if (s == NULL) { + goto err; + } + if (PyDict_SetItemString(d, "__cpu_dispatch__", s) < 0) { + Py_DECREF(s); + goto err; + } + Py_DECREF(s); + + s = PyCapsule_New((void *)_datetime_strings, NULL, NULL); if (s == NULL) { goto err; } @@ -4850,7 +4874,7 @@ PyMODINIT_FUNC initmultiarray(void) { Py_DECREF(s); #define ADDCONST(NAME) \ - s = PyInt_FromLong(NPY_##NAME); \ + s = PyLong_FromLong(NPY_##NAME); \ PyDict_SetItemString(d, #NAME, s); \ Py_DECREF(s) @@ -4889,26 +4913,67 @@ PyMODINIT_FUNC initmultiarray(void) { set_flaginfo(d); /* Create the typeinfo types */ - typeinfo_init_structsequences(); - PyDict_SetItemString(d, - "typeinfo", (PyObject *)&PyArray_typeinfoType); - PyDict_SetItemString(d, - "typeinforanged", (PyObject *)&PyArray_typeinforangedType); + if (typeinfo_init_structsequences(d) < 0) { + goto err; + } - if (!intern_strings()) { + if (intern_strings() < 0) { goto err; } if (set_typeinfo(d) != 0) { goto err; } + if (PyType_Ready(&PyArrayMethod_Type) < 0) { + goto err; + } + if (PyType_Ready(&PyBoundArrayMethod_Type) < 0) { + goto err; + } + if (initialize_and_map_pytypes_to_dtypes() < 0) { + goto err; + } + + if (PyArray_InitializeCasts() < 0) { + goto err; + } + + /* Load the ufunc operators into the array module's namespace */ + if (InitOperators(d) < 0) { + goto err; + } + + if (set_matmul_flags(d) < 0) { + goto err; + } - return RETVAL(m); + if (initumath(m) != 0) { + goto err; + } + /* + * Initialize the default PyDataMem_Handler capsule singleton. + */ + PyDataMem_DefaultHandler = PyCapsule_New(&default_handler, "mem_handler", NULL); + if (PyDataMem_DefaultHandler == NULL) { + goto err; + } +#if (!defined(PYPY_VERSION_NUM) || PYPY_VERSION_NUM >= 0x07030600) + /* + * Initialize the context-local current handler + * with the default PyDataMem_Handler capsule. + */ + current_handler = PyContextVar_New("current_allocator", PyDataMem_DefaultHandler); + if (current_handler == NULL) { + goto err; + } +#endif + return m; err: if (!PyErr_Occurred()) { PyErr_SetString(PyExc_RuntimeError, "cannot load multiarray module."); } - return RETVAL(NULL); + Py_DECREF(m); + return NULL; } diff --git a/numpy/core/src/multiarray/multiarraymodule.h b/numpy/core/src/multiarray/multiarraymodule.h index 3de68c549810..640940d2a978 100644 --- a/numpy/core/src/multiarray/multiarraymodule.h +++ b/numpy/core/src/multiarray/multiarraymodule.h @@ -1,17 +1,12 @@ -#ifndef _NPY_MULTIARRAY_H_ -#define _NPY_MULTIARRAY_H_ +#ifndef NUMPY_CORE_SRC_MULTIARRAY_MULTIARRAYMODULE_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_MULTIARRAYMODULE_H_ -NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_array; -NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_array_prepare; NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_array_wrap; NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_array_finalize; -NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_buffer; -NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_ufunc; -NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_order; -NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_copy; -NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_dtype; -NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_ndmin; +NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_implementation; NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_axis1; NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_axis2; +NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_like; +NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_numpy; -#endif +#endif /* NUMPY_CORE_SRC_MULTIARRAY_MULTIARRAYMODULE_H_ */ diff --git a/numpy/core/src/multiarray/nditer_api.c b/numpy/core/src/multiarray/nditer_api.c index ba9e9f273eac..860c8c1f65fa 100644 --- a/numpy/core/src/multiarray/nditer_api.c +++ b/numpy/core/src/multiarray/nditer_api.c @@ -11,8 +11,9 @@ */ #define NPY_NO_DEPRECATED_API NPY_API_VERSION -/* Indicate that this .c file is allowed to include the header */ +/* Allow this .c file to include nditer_impl.h */ #define NPY_ITERATOR_IMPLEMENTATION_CODE + #include "nditer_impl.h" #include "templ_common.h" #include "ctors.h" @@ -115,7 +116,7 @@ NpyIter_RemoveAxis(NpyIter *iter, int axis) --p; } } - else if (p <= 0) { + else { if (p < -1-axis) { ++p; } @@ -229,13 +230,22 @@ NpyIter_EnableExternalLoop(NpyIter *iter) return NpyIter_Reset(iter, NULL); } + +static char *_reset_cast_error = ( + "Iterator reset failed due to a casting failure. " + "This error is set as a Python error."); + /*NUMPY_API * Resets the iterator to its initial state * + * The use of errmsg is discouraged, it cannot be guaranteed that the GIL + * will not be grabbed on casting errors even when this is passed. + * * If errmsg is non-NULL, it should point to a variable which will * receive the error message, and no Python exception will be set. * This is so that the function can be called from code not holding - * the GIL. + * the GIL. Note that cast errors may still lead to the GIL being + * grabbed temporarily. */ NPY_NO_EXPORT int NpyIter_Reset(NpyIter *iter, char **errmsg) @@ -250,6 +260,9 @@ NpyIter_Reset(NpyIter *iter, char **errmsg) /* If buffer allocation was delayed, do it now */ if (itflags&NPY_ITFLAG_DELAYBUF) { if (!npyiter_allocate_buffers(iter, errmsg)) { + if (errmsg != NULL) { + *errmsg = _reset_cast_error; + } return NPY_FAIL; } NIT_ITFLAGS(iter) &= ~NPY_ITFLAG_DELAYBUF; @@ -257,7 +270,7 @@ NpyIter_Reset(NpyIter *iter, char **errmsg) else { /* * If the iterindex is already right, no need to - * do anything + * do anything (and no cast error has previously occurred). */ bufferdata = NIT_BUFFERDATA(iter); if (NIT_ITERINDEX(iter) == NIT_ITERSTART(iter) && @@ -265,9 +278,12 @@ NpyIter_Reset(NpyIter *iter, char **errmsg) NBF_SIZE(bufferdata) > 0) { return NPY_SUCCEED; } - - /* Copy any data from the buffers back to the arrays */ - npyiter_copy_from_buffers(iter); + if (npyiter_copy_from_buffers(iter) < 0) { + if (errmsg != NULL) { + *errmsg = _reset_cast_error; + } + return NPY_FAIL; + } } } @@ -275,7 +291,12 @@ NpyIter_Reset(NpyIter *iter, char **errmsg) if (itflags&NPY_ITFLAG_BUFFER) { /* Prepare the next buffers and set iterend/size */ - npyiter_copy_to_buffers(iter, NULL); + if (npyiter_copy_to_buffers(iter, NULL) < 0) { + if (errmsg != NULL) { + *errmsg = _reset_cast_error; + } + return NPY_FAIL; + } } return NPY_SUCCEED; @@ -288,7 +309,8 @@ NpyIter_Reset(NpyIter *iter, char **errmsg) * If errmsg is non-NULL, it should point to a variable which will * receive the error message, and no Python exception will be set. * This is so that the function can be called from code not holding - * the GIL. + * the GIL. Note that cast errors may still lead to the GIL being + * grabbed temporarily. */ NPY_NO_EXPORT int NpyIter_ResetBasePointers(NpyIter *iter, char **baseptrs, char **errmsg) @@ -309,8 +331,12 @@ NpyIter_ResetBasePointers(NpyIter *iter, char **baseptrs, char **errmsg) NIT_ITFLAGS(iter) &= ~NPY_ITFLAG_DELAYBUF; } else { - /* Copy any data from the buffers back to the arrays */ - npyiter_copy_from_buffers(iter); + if (npyiter_copy_from_buffers(iter) < 0) { + if (errmsg != NULL) { + *errmsg = _reset_cast_error; + } + return NPY_FAIL; + } } } @@ -323,7 +349,12 @@ NpyIter_ResetBasePointers(NpyIter *iter, char **baseptrs, char **errmsg) if (itflags&NPY_ITFLAG_BUFFER) { /* Prepare the next buffers and set iterend/size */ - npyiter_copy_to_buffers(iter, NULL); + if (npyiter_copy_to_buffers(iter, NULL) < 0) { + if (errmsg != NULL) { + *errmsg = _reset_cast_error; + } + return NPY_FAIL; + } } return NPY_SUCCEED; @@ -335,7 +366,8 @@ NpyIter_ResetBasePointers(NpyIter *iter, char **baseptrs, char **errmsg) * If errmsg is non-NULL, it should point to a variable which will * receive the error message, and no Python exception will be set. * This is so that the function can be called from code not holding - * the GIL. + * the GIL. Note that cast errors may still lead to the GIL being + * grabbed temporarily. */ NPY_NO_EXPORT int NpyIter_ResetToIterIndexRange(NpyIter *iter, @@ -371,8 +403,8 @@ NpyIter_ResetToIterIndexRange(NpyIter *iter, } if (errmsg == NULL) { PyErr_Format(PyExc_ValueError, - "Out-of-bounds range [%d, %d) passed to " - "ResetToIterIndexRange", (int)istart, (int)iend); + "Out-of-bounds range [%" NPY_INTP_FMT ", %" NPY_INTP_FMT ") passed to " + "ResetToIterIndexRange", istart, iend); } else { *errmsg = "Out-of-bounds range passed to ResetToIterIndexRange"; @@ -382,8 +414,8 @@ NpyIter_ResetToIterIndexRange(NpyIter *iter, else if (iend < istart) { if (errmsg == NULL) { PyErr_Format(PyExc_ValueError, - "Invalid range [%d, %d) passed to ResetToIterIndexRange", - (int)istart, (int)iend); + "Invalid range [%" NPY_INTP_FMT ", %" NPY_INTP_FMT ") passed to ResetToIterIndexRange", + istart, iend); } else { *errmsg = "Invalid range passed to ResetToIterIndexRange"; @@ -406,7 +438,7 @@ NpyIter_ResetToIterIndexRange(NpyIter *iter, * Returns NPY_SUCCEED on success, NPY_FAIL on failure. */ NPY_NO_EXPORT int -NpyIter_GotoMultiIndex(NpyIter *iter, npy_intp *multi_index) +NpyIter_GotoMultiIndex(NpyIter *iter, npy_intp const *multi_index) { npy_uint32 itflags = NIT_ITFLAGS(iter); int idim, ndim = NIT_NDIM(iter); @@ -633,12 +665,16 @@ NpyIter_GotoIterIndex(NpyIter *iter, npy_intp iterindex) /* Start the buffer at the provided iterindex */ else { /* Write back to the arrays */ - npyiter_copy_from_buffers(iter); + if (npyiter_copy_from_buffers(iter) < 0) { + return NPY_FAIL; + } npyiter_goto_iterindex(iter, iterindex); /* Prepare the next buffers and set iterend/size */ - npyiter_copy_to_buffers(iter, NULL); + if (npyiter_copy_to_buffers(iter, NULL) < 0) { + return NPY_FAIL; + } } } else { @@ -935,13 +971,8 @@ NpyIter_GetShape(NpyIter *iter, npy_intp *outshape) if (itflags&NPY_ITFLAG_HASMULTIINDEX) { perm = NIT_PERM(iter); for(idim = 0; idim < ndim; ++idim) { - npy_int8 p = perm[idim]; - if (p < 0) { - outshape[ndim+p] = NAD_SHAPE(axisdata); - } - else { - outshape[ndim-p-1] = NAD_SHAPE(axisdata); - } + int axis = npyiter_undo_iter_axis_perm(idim, ndim, perm, NULL); + outshape[axis] = NAD_SHAPE(axisdata); NIT_ADVANCE_AXISDATA(axisdata, 1); } @@ -1005,8 +1036,9 @@ NpyIter_CreateCompatibleStrides(NpyIter *iter, perm = NIT_PERM(iter); for(idim = 0; idim < ndim; ++idim) { - npy_int8 p = perm[idim]; - if (p < 0) { + npy_bool flipped; + npy_int8 axis = npyiter_undo_iter_axis_perm(idim, ndim, perm, &flipped); + if (flipped) { PyErr_SetString(PyExc_RuntimeError, "Iterator CreateCompatibleStrides may only be called " "if DONT_NEGATE_STRIDES was used to prevent reverse " @@ -1014,7 +1046,7 @@ NpyIter_CreateCompatibleStrides(NpyIter *iter, return NPY_FAIL; } else { - outstrides[ndim-p-1] = itemsize; + outstrides[axis] = itemsize; } itemsize *= NAD_SHAPE(axisdata); @@ -1380,46 +1412,6 @@ NpyIter_GetInnerLoopSizePtr(NpyIter *iter) } } -/*NUMPY_API - * Resolves all writebackifcopy scratch buffers, not safe to use iterator - * operands after this call, in this iterator as well as any copies. - * Returns 0 on success, -1 on failure - */ -NPY_NO_EXPORT int -NpyIter_Close(NpyIter *iter) -{ - int ret=0, iop, nop; - PyArrayObject ** operands; - npyiter_opitflags *op_itflags; - if (iter == NULL) { - return 0; - } - nop = NIT_NOP(iter); - operands = NIT_OPERANDS(iter); - op_itflags = NIT_OPITFLAGS(iter); - /* If NPY_OP_ITFLAG_HAS_WRITEBACK flag set on operand, resolve it. - * If the resolution fails (should never happen), continue from the - * next operand and discard the writeback scratch buffers, and return - * failure status - */ - for (iop=0; iop<nop; iop++) { - if (op_itflags[iop] & NPY_OP_ITFLAG_HAS_WRITEBACK) { - op_itflags[iop] &= ~NPY_OP_ITFLAG_HAS_WRITEBACK; - if (PyArray_ResolveWritebackIfCopy(operands[iop]) < 0) { - ret = -1; - iop++; - break; - } - } - } - for (; iop<nop; iop++) { - if (op_itflags[iop] & NPY_OP_ITFLAG_HAS_WRITEBACK) { - op_itflags[iop] &= ~NPY_OP_ITFLAG_HAS_WRITEBACK; - PyArray_DiscardWritebackIfCopy(operands[iop]); - } - } - return ret; -} /*NUMPY_API * For debugging @@ -1470,8 +1462,8 @@ NpyIter_DebugPrint(NpyIter *iter) printf("REUSE_REDUCE_LOOPS "); printf("\n"); - printf("| NDim: %d\n", (int)ndim); - printf("| NOp: %d\n", (int)nop); + printf("| NDim: %d\n", ndim); + printf("| NOp: %d\n", nop); if (NIT_MASKOP(iter) >= 0) { printf("| MaskOp: %d\n", (int)NIT_MASKOP(iter)); } @@ -1566,6 +1558,8 @@ NpyIter_DebugPrint(NpyIter *iter) if (itflags&NPY_ITFLAG_BUFFER) { NpyIter_BufferData *bufferdata = NIT_BUFFERDATA(iter); + NpyIter_TransferInfo *transferinfo = NBF_TRANSFERINFO(bufferdata); + printf("| BufferData:\n"); printf("| BufferSize: %d\n", (int)NBF_BUFFERSIZE(bufferdata)); printf("| Size: %d\n", (int)NBF_SIZE(bufferdata)); @@ -1607,19 +1601,19 @@ NpyIter_DebugPrint(NpyIter *iter) } printf("| ReadTransferFn: "); for (iop = 0; iop < nop; ++iop) - printf("%p ", (void *)NBF_READTRANSFERFN(bufferdata)[iop]); + printf("%p ", (void *)transferinfo[iop].read.func); printf("\n"); printf("| ReadTransferData: "); for (iop = 0; iop < nop; ++iop) - printf("%p ", (void *)NBF_READTRANSFERDATA(bufferdata)[iop]); + printf("%p ", (void *)transferinfo[iop].read.auxdata); printf("\n"); printf("| WriteTransferFn: "); for (iop = 0; iop < nop; ++iop) - printf("%p ", (void *)NBF_WRITETRANSFERFN(bufferdata)[iop]); + printf("%p ", (void *)transferinfo[iop].write.func); printf("\n"); printf("| WriteTransferData: "); for (iop = 0; iop < nop; ++iop) - printf("%p ", (void *)NBF_WRITETRANSFERDATA(bufferdata)[iop]); + printf("%p ", (void *)transferinfo[iop].write.auxdata); printf("\n"); printf("| Buffers: "); for (iop = 0; iop < nop; ++iop) @@ -1669,15 +1663,12 @@ npyiter_coalesce_axes(NpyIter *iter) npy_intp istrides, nstrides = NAD_NSTRIDES(); NpyIter_AxisData *axisdata = NIT_AXISDATA(iter); npy_intp sizeof_axisdata = NIT_AXISDATA_SIZEOF(itflags, ndim, nop); - NpyIter_AxisData *ad_compress; + NpyIter_AxisData *ad_compress = axisdata; npy_intp new_ndim = 1; /* The HASMULTIINDEX or IDENTPERM flags do not apply after coalescing */ NIT_ITFLAGS(iter) &= ~(NPY_ITFLAG_IDENTPERM|NPY_ITFLAG_HASMULTIINDEX); - axisdata = NIT_AXISDATA(iter); - ad_compress = axisdata; - for (idim = 0; idim < ndim-1; ++idim) { int can_coalesce = 1; npy_intp shape0 = NAD_SHAPE(ad_compress); @@ -1770,6 +1761,9 @@ npyiter_allocate_buffers(NpyIter *iter, char **errmsg) } goto fail; } + if (PyDataType_FLAGCHK(op_dtype[iop], NPY_NEEDS_INIT)) { + memset(buffer, '\0', itemsize*buffersize); + } buffers[iop] = buffer; } } @@ -1876,7 +1870,7 @@ npyiter_goto_iterindex(NpyIter *iter, npy_intp iterindex) * their data needs to be written back to the arrays. The multi-index * must be positioned for the beginning of the buffer. */ -NPY_NO_EXPORT void +NPY_NO_EXPORT int npyiter_copy_from_buffers(NpyIter *iter) { npy_uint32 itflags = NIT_ITFLAGS(iter); @@ -1901,15 +1895,12 @@ npyiter_copy_from_buffers(NpyIter *iter) npy_intp reduce_outerdim = 0; npy_intp *reduce_outerstrides = NULL; - PyArray_StridedUnaryOp *stransfer = NULL; - NpyAuxData *transferdata = NULL; - npy_intp axisdata_incr = NIT_AXISDATA_SIZEOF(itflags, ndim, nop) / NPY_SIZEOF_INTP; /* If we're past the end, nothing to copy */ if (NBF_SIZE(bufferdata) == 0) { - return; + return 0; } NPY_IT_DBG_PRINT("Iterator: Copying buffers to outputs\n"); @@ -1921,9 +1912,8 @@ npyiter_copy_from_buffers(NpyIter *iter) transfersize *= NBF_REDUCE_OUTERSIZE(bufferdata); } + NpyIter_TransferInfo *transferinfo = NBF_TRANSFERINFO(bufferdata); for (iop = 0; iop < nop; ++iop) { - stransfer = NBF_WRITETRANSFERFN(bufferdata)[iop]; - transferdata = NBF_WRITETRANSFERDATA(bufferdata)[iop]; buffer = buffers[iop]; /* * Copy the data back to the arrays. If the type has refs, @@ -1932,7 +1922,7 @@ npyiter_copy_from_buffers(NpyIter *iter) * The flag USINGBUFFER is set when the buffer was used, so * only copy back when this flag is on. */ - if ((stransfer != NULL) && + if ((transferinfo[iop].write.func != NULL) && (op_itflags[iop]&(NPY_OP_ITFLAG_WRITE|NPY_OP_ITFLAG_USINGBUFFER)) == (NPY_OP_ITFLAG_WRITE|NPY_OP_ITFLAG_USINGBUFFER)) { npy_intp op_transfersize; @@ -2016,43 +2006,51 @@ npyiter_copy_from_buffers(NpyIter *iter) maskptr = (npy_bool *)ad_ptrs[maskop]; } - PyArray_TransferMaskedStridedToNDim(ndim_transfer, + if (PyArray_TransferMaskedStridedToNDim(ndim_transfer, ad_ptrs[iop], dst_strides, axisdata_incr, buffer, src_stride, maskptr, strides[maskop], dst_coords, axisdata_incr, dst_shape, axisdata_incr, op_transfersize, dtypes[iop]->elsize, - (PyArray_MaskedStridedUnaryOp *)stransfer, - transferdata); + &transferinfo[iop].write) < 0) { + return -1; + } } /* Regular operand */ else { - PyArray_TransferStridedToNDim(ndim_transfer, + if (PyArray_TransferStridedToNDim(ndim_transfer, ad_ptrs[iop], dst_strides, axisdata_incr, buffer, src_stride, dst_coords, axisdata_incr, dst_shape, axisdata_incr, op_transfersize, dtypes[iop]->elsize, - stransfer, - transferdata); + &transferinfo[iop].write) < 0) { + return -1; + } } } /* If there's no copy back, we may have to decrement refs. In - * this case, the transfer function has a 'decsrcref' transfer - * function, so we can use it to do the decrement. + * this case, the transfer is instead a function which clears + * (DECREFs) the single input. * * The flag USINGBUFFER is set when the buffer was used, so * only decrement refs when this flag is on. */ - else if (stransfer != NULL && + else if (transferinfo[iop].write.func != NULL && (op_itflags[iop]&NPY_OP_ITFLAG_USINGBUFFER) != 0) { NPY_IT_DBG_PRINT1("Iterator: Freeing refs and zeroing buffer " "of operand %d\n", (int)iop); /* Decrement refs */ - stransfer(NULL, 0, buffer, dtypes[iop]->elsize, - transfersize, dtypes[iop]->elsize, - transferdata); + npy_intp buf_stride = dtypes[iop]->elsize; + if (transferinfo[iop].write.func( + &transferinfo[iop].write.context, + &buffer, &transfersize, &buf_stride, + transferinfo[iop].write.auxdata) < 0) { + /* Since this should only decrement, it should never error */ + assert(0); + return -1; + } /* * Zero out the memory for safety. For instance, * if during iteration some Python code copied an @@ -2064,6 +2062,7 @@ npyiter_copy_from_buffers(NpyIter *iter) } NPY_IT_DBG_PRINT("Iterator: Finished copying buffers to outputs\n"); + return 0; } /* @@ -2071,7 +2070,7 @@ npyiter_copy_from_buffers(NpyIter *iter) * for the start of a buffer. It decides which operands need a buffer, * and copies the data into the buffers. */ -NPY_NO_EXPORT void +NPY_NO_EXPORT int npyiter_copy_to_buffers(NpyIter *iter, char **prev_dataptrs) { npy_uint32 itflags = NIT_ITFLAGS(iter); @@ -2097,9 +2096,6 @@ npyiter_copy_to_buffers(NpyIter *iter, char **prev_dataptrs) npy_intp *reduce_outerstrides = NULL; char **reduce_outerptrs = NULL; - PyArray_StridedUnaryOp *stransfer = NULL; - NpyAuxData *transferdata = NULL; - /* * Have to get this flag before npyiter_checkreducesize sets * it for the next iteration. @@ -2134,7 +2130,7 @@ npyiter_copy_to_buffers(NpyIter *iter, char **prev_dataptrs) /* * Try to do make the outersize as big as possible. This allows * it to shrink when processing the last bit of the outer reduce loop, - * then grow again at the beginnning of the next outer reduce loop. + * then grow again at the beginning of the next outer reduce loop. */ NBF_REDUCE_OUTERSIZE(bufferdata) = (NAD_SHAPE(reduce_outeraxisdata)- NAD_INDEX(reduce_outeraxisdata)); @@ -2190,7 +2186,7 @@ npyiter_copy_to_buffers(NpyIter *iter, char **prev_dataptrs) NBF_BUFITEREND(bufferdata) = iterindex + reduce_innersize; if (reduce_innersize == 0) { NBF_REDUCE_OUTERSIZE(bufferdata) = 0; - return; + return 0; } else { NBF_REDUCE_OUTERSIZE(bufferdata) = transfersize/reduce_innersize; @@ -2210,13 +2206,9 @@ npyiter_copy_to_buffers(NpyIter *iter, char **prev_dataptrs) is_onestride = 1; } + NpyIter_TransferInfo *transferinfo = NBF_TRANSFERINFO(bufferdata); for (iop = 0; iop < nop; ++iop) { - /* - * If the buffer is write-only, these two are NULL, and the buffer - * pointers will be set up but the read copy won't be done - */ - stransfer = NBF_READTRANSFERFN(bufferdata)[iop]; - transferdata = NBF_READTRANSFERDATA(bufferdata)[iop]; + switch (op_itflags[iop]& (NPY_OP_ITFLAG_BUFNEVER| NPY_OP_ITFLAG_CAST| @@ -2234,8 +2226,8 @@ npyiter_copy_to_buffers(NpyIter *iter, char **prev_dataptrs) * could be zero, but strides[iop] was initialized * to the first non-trivial stride. */ - stransfer = NULL; /* The flag NPY_OP_ITFLAG_USINGBUFFER can be ignored here */ + assert(!(op_itflags[iop] & NPY_OP_ITFLAG_USINGBUFFER)); break; /* Never need to buffer this operand */ case NPY_OP_ITFLAG_BUFNEVER|NPY_OP_ITFLAG_REDUCE: @@ -2247,8 +2239,8 @@ npyiter_copy_to_buffers(NpyIter *iter, char **prev_dataptrs) * could be zero, but strides[iop] was initialized * to the first non-trivial stride. */ - stransfer = NULL; /* The flag NPY_OP_ITFLAG_USINGBUFFER can be ignored here */ + assert(!(op_itflags[iop] & NPY_OP_ITFLAG_USINGBUFFER)); break; /* Just a copy */ case 0: @@ -2266,7 +2258,6 @@ npyiter_copy_to_buffers(NpyIter *iter, char **prev_dataptrs) if (is_onestride) { ptrs[iop] = ad_ptrs[iop]; strides[iop] = ad_strides[iop]; - stransfer = NULL; /* Signal that the buffer is not being used */ op_itflags[iop] &= (~NPY_OP_ITFLAG_USINGBUFFER); } @@ -2281,7 +2272,6 @@ npyiter_copy_to_buffers(NpyIter *iter, char **prev_dataptrs) strides[iop] = ad_strides[iop]; reduce_outerstrides[iop] = NAD_STRIDES(reduce_outeraxisdata)[iop]; - stransfer = NULL; /* Signal that the buffer is not being used */ op_itflags[iop] &= (~NPY_OP_ITFLAG_USINGBUFFER); } @@ -2312,7 +2302,6 @@ npyiter_copy_to_buffers(NpyIter *iter, char **prev_dataptrs) NPY_IT_DBG_PRINT1("reduce op %d all one stride\n", (int)iop); ptrs[iop] = ad_ptrs[iop]; reduce_outerstrides[iop] = 0; - stransfer = NULL; /* Signal that the buffer is not being used */ op_itflags[iop] &= (~NPY_OP_ITFLAG_USINGBUFFER); } @@ -2327,7 +2316,6 @@ npyiter_copy_to_buffers(NpyIter *iter, char **prev_dataptrs) /* Outer reduce loop advances by one item */ reduce_outerstrides[iop] = NAD_STRIDES(reduce_outeraxisdata)[iop]; - stransfer = NULL; /* Signal that the buffer is not being used */ op_itflags[iop] &= (~NPY_OP_ITFLAG_USINGBUFFER); } @@ -2353,7 +2341,6 @@ npyiter_copy_to_buffers(NpyIter *iter, char **prev_dataptrs) ptrs[iop] = ad_ptrs[iop]; strides[iop] = ad_strides[iop]; reduce_outerstrides[iop] = 0; - stransfer = NULL; /* Signal that the buffer is not being used */ op_itflags[iop] &= (~NPY_OP_ITFLAG_USINGBUFFER); } @@ -2368,7 +2355,6 @@ npyiter_copy_to_buffers(NpyIter *iter, char **prev_dataptrs) /* Outer reduce loop advances by one item */ reduce_outerstrides[iop] = NAD_STRIDES(reduce_outeraxisdata)[iop]; - stransfer = NULL; /* Signal that the buffer is not being used */ op_itflags[iop] &= (~NPY_OP_ITFLAG_USINGBUFFER); } @@ -2446,7 +2432,12 @@ npyiter_copy_to_buffers(NpyIter *iter, char **prev_dataptrs) break; } - if (stransfer != NULL) { + /* + * If OP_ITFLAG_USINGBUFFER is enabled and the read func is not NULL, + * the buffer needs to be read. + */ + if (op_itflags[iop] & NPY_OP_ITFLAG_USINGBUFFER && + transferinfo[iop].read.func != NULL) { npy_intp src_itemsize; npy_intp op_transfersize; @@ -2457,7 +2448,7 @@ npyiter_copy_to_buffers(NpyIter *iter, char **prev_dataptrs) src_itemsize = PyArray_DTYPE(operands[iop])->elsize; - /* If stransfer wasn't set to NULL, buffering is required */ + /* If we reach here, buffering is required */ any_buffered = 1; /* @@ -2542,40 +2533,33 @@ npyiter_copy_to_buffers(NpyIter *iter, char **prev_dataptrs) skip_transfer = 1; } - /* If the data type requires zero-inititialization */ - if (PyDataType_FLAGCHK(dtypes[iop], NPY_NEEDS_INIT)) { - NPY_IT_DBG_PRINT("Iterator: Buffer requires init, " - "memsetting to 0\n"); - memset(ptrs[iop], 0, dtypes[iop]->elsize*op_transfersize); - /* Can't skip the transfer in this case */ - skip_transfer = 0; - } - - if (!skip_transfer) { + /* + * Copy data to the buffers if necessary. + * + * We always copy if the operand has references. In that case + * a "write" function must be in use that either copies or clears + * the buffer. + * This write from buffer call does not check for skip-transfer + * so we have to assume the buffer is cleared. For dtypes that + * do not have references, we can assume that the write function + * will leave the source (buffer) unmodified. + */ + if (!skip_transfer || PyDataType_REFCHK(dtypes[iop])) { NPY_IT_DBG_PRINT2("Iterator: Copying operand %d to " "buffer (%d items)\n", (int)iop, (int)op_transfersize); - PyArray_TransferNDimToStrided(ndim_transfer, - ptrs[iop], dst_stride, + if (PyArray_TransferNDimToStrided( + ndim_transfer, ptrs[iop], dst_stride, ad_ptrs[iop], src_strides, axisdata_incr, src_coords, axisdata_incr, src_shape, axisdata_incr, op_transfersize, src_itemsize, - stransfer, - transferdata); - } - } - else if (ptrs[iop] == buffers[iop]) { - /* If the data type requires zero-inititialization */ - if (PyDataType_FLAGCHK(dtypes[iop], NPY_NEEDS_INIT)) { - NPY_IT_DBG_PRINT1("Iterator: Write-only buffer for " - "operand %d requires init, " - "memsetting to 0\n", (int)iop); - memset(ptrs[iop], 0, dtypes[iop]->elsize*transfersize); + &transferinfo[iop].read) < 0) { + return -1; + } } } - } /* @@ -2599,8 +2583,82 @@ npyiter_copy_to_buffers(NpyIter *iter, char **prev_dataptrs) NPY_IT_DBG_PRINT1("Iterator: Finished copying inputs to buffers " "(buffered size is %d)\n", (int)NBF_SIZE(bufferdata)); + return 0; } + +/** + * This function clears any references still held by the buffers and should + * only be used to discard buffers if an error occurred. + * + * @param iter Iterator + */ +NPY_NO_EXPORT void +npyiter_clear_buffers(NpyIter *iter) +{ + int nop = iter->nop; + NpyIter_BufferData *bufferdata = NIT_BUFFERDATA(iter); + + if (NBF_SIZE(bufferdata) == 0) { + /* if the buffers are empty already, there is nothing to do */ + return; + } + + if (!(NIT_ITFLAGS(iter) & NPY_ITFLAG_NEEDSAPI)) { + /* Buffers do not require clearing, but should not be copied back */ + NBF_SIZE(bufferdata) = 0; + return; + } + + /* + * The iterator may be using a dtype with references, which always + * requires the API. In that case, further cleanup may be necessary. + * + * TODO: At this time, we assume that a dtype having references + * implies the need to hold the GIL at all times. In theory + * we could broaden this definition for a new + * `PyArray_Item_XDECREF` API and the assumption may become + * incorrect. + */ + PyObject *type, *value, *traceback; + PyErr_Fetch(&type, &value, &traceback); + + /* Cleanup any buffers with references */ + char **buffers = NBF_BUFFERS(bufferdata); + PyArray_Descr **dtypes = NIT_DTYPES(iter); + npyiter_opitflags *op_itflags = NIT_OPITFLAGS(iter); + for (int iop = 0; iop < nop; ++iop, ++buffers) { + /* + * We may want to find a better way to do this, on the other hand, + * this cleanup seems rare and fairly special. A dtype using + * references (right now only us) must always keep the buffer in + * a well defined state (either NULL or owning the reference). + * Only we implement cleanup + */ + if (!PyDataType_REFCHK(dtypes[iop]) || + !(op_itflags[iop]&NPY_OP_ITFLAG_USINGBUFFER)) { + continue; + } + if (*buffers == 0) { + continue; + } + int itemsize = dtypes[iop]->elsize; + for (npy_intp i = 0; i < NBF_SIZE(bufferdata); i++) { + /* + * See above comment, if this API is expanded the GIL assumption + * could become incorrect. + */ + PyArray_Item_XDECREF(*buffers + (itemsize * i), dtypes[iop]); + } + /* Clear out the buffer just to be sure */ + memset(*buffers, 0, NBF_SIZE(bufferdata) * itemsize); + } + /* Signal that the buffers are empty */ + NBF_SIZE(bufferdata) = 0; + PyErr_Restore(type, value, traceback); +} + + /* * This checks how much space can be buffered without encountering the * same value twice, or for operands whose innermost stride is zero, @@ -2746,9 +2804,9 @@ npyiter_checkreducesize(NpyIter *iter, npy_intp count, if (coord != 0) { /* * In this case, it is only safe to reuse the buffer if the amount - * of data copied is not more then the current axes, as is the + * of data copied is not more than the current axes, as is the * case when reuse_reduce_loops was active already. - * It should be in principle OK when the idim loop returns immidiatly. + * It should be in principle OK when the idim loop returns immediately. */ NIT_ITFLAGS(iter) &= ~NPY_ITFLAG_REUSE_REDUCE_LOOPS; } @@ -2830,4 +2888,23 @@ npyiter_checkreducesize(NpyIter *iter, npy_intp count, } return count * (*reduce_innersize); } + +NPY_NO_EXPORT npy_bool +npyiter_has_writeback(NpyIter *iter) +{ + int iop, nop; + npyiter_opitflags *op_itflags; + if (iter == NULL) { + return 0; + } + nop = NIT_NOP(iter); + op_itflags = NIT_OPITFLAGS(iter); + + for (iop=0; iop<nop; iop++) { + if (op_itflags[iop] & NPY_OP_ITFLAG_HAS_WRITEBACK) { + return NPY_TRUE; + } + } + return NPY_FALSE; +} #undef NPY_ITERATOR_IMPLEMENTATION_CODE diff --git a/numpy/core/src/multiarray/nditer_constr.c b/numpy/core/src/multiarray/nditer_constr.c index b07137858cf5..bf32e1f6b706 100644 --- a/numpy/core/src/multiarray/nditer_constr.c +++ b/numpy/core/src/multiarray/nditer_constr.c @@ -11,21 +11,21 @@ */ #define NPY_NO_DEPRECATED_API NPY_API_VERSION -/* Indicate that this .c file is allowed to include the header */ +/* Allow this .c file to include nditer_impl.h */ #define NPY_ITERATOR_IMPLEMENTATION_CODE -#include "nditer_impl.h" +#include "nditer_impl.h" #include "arrayobject.h" +#include "array_coercion.h" #include "templ_common.h" -#include "mem_overlap.h" - +#include "array_assign.h" /* Internal helper functions private to this file */ static int npyiter_check_global_flags(npy_uint32 flags, npy_uint32* itflags); static int npyiter_check_op_axes(int nop, int oa_ndim, int **op_axes, - npy_intp *itershape); + const npy_intp *itershape); static int npyiter_calculate_ndim(int nop, PyArrayObject **op_in, int oa_ndim); @@ -56,13 +56,14 @@ npyiter_check_casting(int nop, PyArrayObject **op, static int npyiter_fill_axisdata(NpyIter *iter, npy_uint32 flags, npyiter_opitflags *op_itflags, char **op_dataptr, - npy_uint32 *op_flags, int **op_axes, - npy_intp *itershape); + const npy_uint32 *op_flags, int **op_axes, + npy_intp const *itershape); +static NPY_INLINE int +npyiter_get_op_axis(int axis, npy_bool *reduction_axis); static void -npyiter_replace_axisdata(NpyIter *iter, int iop, - PyArrayObject *op, - int op_ndim, char *op_dataptr, - int *op_axes); +npyiter_replace_axisdata( + NpyIter *iter, int iop, PyArrayObject *op, + int orig_op_ndim, const int *op_axes); static void npyiter_compute_index_strides(NpyIter *iter, npy_uint32 flags); static void @@ -75,23 +76,23 @@ static void npyiter_find_best_axis_ordering(NpyIter *iter); static PyArray_Descr * npyiter_get_common_dtype(int nop, PyArrayObject **op, - npyiter_opitflags *op_itflags, PyArray_Descr **op_dtype, + const npyiter_opitflags *op_itflags, PyArray_Descr **op_dtype, PyArray_Descr **op_request_dtypes, int only_inputs); static PyArrayObject * npyiter_new_temp_array(NpyIter *iter, PyTypeObject *subtype, npy_uint32 flags, npyiter_opitflags *op_itflags, - int op_ndim, npy_intp *shape, - PyArray_Descr *op_dtype, int *op_axes); + int op_ndim, npy_intp const *shape, + PyArray_Descr *op_dtype, const int *op_axes); static int npyiter_allocate_arrays(NpyIter *iter, npy_uint32 flags, PyArray_Descr **op_dtype, PyTypeObject *subtype, - npy_uint32 *op_flags, npyiter_opitflags *op_itflags, + const npy_uint32 *op_flags, npyiter_opitflags *op_itflags, int **op_axes); static void npyiter_get_priority_subtype(int nop, PyArrayObject **op, - npyiter_opitflags *op_itflags, + const npyiter_opitflags *op_itflags, double *subtype_priority, PyTypeObject **subtype); static int npyiter_allocate_transfer_functions(NpyIter *iter); @@ -155,7 +156,7 @@ NpyIter_AdvancedNew(int nop, PyArrayObject **op_in, npy_uint32 flags, if (nop > NPY_MAXARGS) { PyErr_Format(PyExc_ValueError, "Cannot construct an iterator with more than %d operands " - "(%d were requested)", (int)NPY_MAXARGS, (int)nop); + "(%d were requested)", NPY_MAXARGS, nop); return NULL; } @@ -234,8 +235,8 @@ NpyIter_AdvancedNew(int nop, PyArrayObject **op_in, npy_uint32 flags, NBF_SIZE(bufferdata) = 0; memset(NBF_BUFFERS(bufferdata), 0, nop*NPY_SIZEOF_INTP); memset(NBF_PTRS(bufferdata), 0, nop*NPY_SIZEOF_INTP); - memset(NBF_READTRANSFERDATA(bufferdata), 0, nop*NPY_SIZEOF_INTP); - memset(NBF_WRITETRANSFERDATA(bufferdata), 0, nop*NPY_SIZEOF_INTP); + /* Ensure that the transferdata/auxdata is NULLed */ + memset(NBF_TRANSFERINFO(bufferdata), 0, nop * sizeof(NpyIter_TransferInfo)); } /* Fill in the AXISDATA arrays and set the ITERSIZE field */ @@ -403,7 +404,6 @@ NpyIter_AdvancedNew(int nop, PyArrayObject **op_in, npy_uint32 flags, */ if (!npyiter_allocate_arrays(iter, flags, op_dtype, subtype, op_flags, op_itflags, op_axes)) { - NpyIter_Close(iter); NpyIter_Deallocate(iter); return NULL; } @@ -449,6 +449,11 @@ NpyIter_AdvancedNew(int nop, PyArrayObject **op_in, npy_uint32 flags, /* * If REFS_OK was specified, check whether there are any * reference arrays and flag it if so. + * + * NOTE: This really should be unnecessary, but chances are someone relies + * on it. The iterator itself does not require the API here + * as it only does so for casting/buffering. But in almost all + * use-cases the API will be required for whatever operation is done. */ if (flags & NPY_ITER_REFS_OK) { for (iop = 0; iop < nop; ++iop) { @@ -465,20 +470,21 @@ NpyIter_AdvancedNew(int nop, PyArrayObject **op_in, npy_uint32 flags, /* If buffering is set without delayed allocation */ if (itflags & NPY_ITFLAG_BUFFER) { if (!npyiter_allocate_transfer_functions(iter)) { - NpyIter_Close(iter); NpyIter_Deallocate(iter); return NULL; } if (!(itflags & NPY_ITFLAG_DELAYBUF)) { /* Allocate the buffers */ if (!npyiter_allocate_buffers(iter, NULL)) { - NpyIter_Close(iter); NpyIter_Deallocate(iter); return NULL; } /* Prepare the next buffers and set iterend/size */ - npyiter_copy_to_buffers(iter, NULL); + if (npyiter_copy_to_buffers(iter, NULL) < 0) { + NpyIter_Deallocate(iter); + return NULL; + } } } @@ -576,13 +582,11 @@ NpyIter_Copy(NpyIter *iter) NpyIter_BufferData *bufferdata; npy_intp buffersize, itemsize; char **buffers; - NpyAuxData **readtransferdata, **writetransferdata; bufferdata = NIT_BUFFERDATA(newiter); buffers = NBF_BUFFERS(bufferdata); - readtransferdata = NBF_READTRANSFERDATA(bufferdata); - writetransferdata = NBF_WRITETRANSFERDATA(bufferdata); buffersize = NBF_BUFFERSIZE(bufferdata); + NpyIter_TransferInfo *transferinfo = NBF_TRANSFERINFO(bufferdata); for (iop = 0; iop < nop; ++iop) { if (buffers[iop] != NULL) { @@ -595,30 +599,33 @@ NpyIter_Copy(NpyIter *iter) if (buffers[iop] == NULL) { out_of_memory = 1; } + else { + if (PyDataType_FLAGCHK(dtypes[iop], NPY_NEEDS_INIT)) { + memset(buffers[iop], '\0', itemsize*buffersize); + } + } } } - if (readtransferdata[iop] != NULL) { + if (transferinfo[iop].read.func != NULL) { if (out_of_memory) { - readtransferdata[iop] = NULL; + transferinfo[iop].read.func = NULL; /* No cleanup */ } else { - readtransferdata[iop] = - NPY_AUXDATA_CLONE(readtransferdata[iop]); - if (readtransferdata[iop] == NULL) { + if (NPY_cast_info_copy(&transferinfo[iop].read, + &transferinfo[iop].read) < 0) { out_of_memory = 1; } } } - if (writetransferdata[iop] != NULL) { + if (transferinfo[iop].write.func != NULL) { if (out_of_memory) { - writetransferdata[iop] = NULL; + transferinfo[iop].write.func = NULL; /* No cleanup */ } else { - writetransferdata[iop] = - NPY_AUXDATA_CLONE(writetransferdata[iop]); - if (writetransferdata[iop] == NULL) { + if (NPY_cast_info_copy(&transferinfo[iop].write, + &transferinfo[iop].write) < 0) { out_of_memory = 1; } } @@ -644,65 +651,87 @@ NpyIter_Copy(NpyIter *iter) } /*NUMPY_API - * Deallocate an iterator + * Deallocate an iterator. + * + * To correctly work when an error is in progress, we have to check + * `PyErr_Occurred()`. This is necessary when buffers are not finalized + * or WritebackIfCopy is used. We could avoid that check by exposing a new + * function which is passed in whether or not a Python error is already set. */ NPY_NO_EXPORT int NpyIter_Deallocate(NpyIter *iter) { + int success = PyErr_Occurred() == NULL; + npy_uint32 itflags; /*int ndim = NIT_NDIM(iter);*/ int iop, nop; PyArray_Descr **dtype; PyArrayObject **object; + npyiter_opitflags *op_itflags; if (iter == NULL) { - return NPY_SUCCEED; + return success; } itflags = NIT_ITFLAGS(iter); nop = NIT_NOP(iter); dtype = NIT_DTYPES(iter); object = NIT_OPERANDS(iter); + op_itflags = NIT_OPITFLAGS(iter); /* Deallocate any buffers and buffering data */ if (itflags & NPY_ITFLAG_BUFFER) { + /* Ensure no data is held by the buffers before they are cleared */ + if (success) { + if (npyiter_copy_from_buffers(iter) < 0) { + success = NPY_FAIL; + } + } + else { + npyiter_clear_buffers(iter); + } + NpyIter_BufferData *bufferdata = NIT_BUFFERDATA(iter); char **buffers; - NpyAuxData **transferdata; /* buffers */ buffers = NBF_BUFFERS(bufferdata); - for(iop = 0; iop < nop; ++iop, ++buffers) { + for (iop = 0; iop < nop; ++iop, ++buffers) { PyArray_free(*buffers); } + + NpyIter_TransferInfo *transferinfo = NBF_TRANSFERINFO(bufferdata); /* read bufferdata */ - transferdata = NBF_READTRANSFERDATA(bufferdata); - for(iop = 0; iop < nop; ++iop, ++transferdata) { - if (*transferdata) { - NPY_AUXDATA_FREE(*transferdata); - } - } - /* write bufferdata */ - transferdata = NBF_WRITETRANSFERDATA(bufferdata); - for(iop = 0; iop < nop; ++iop, ++transferdata) { - if (*transferdata) { - NPY_AUXDATA_FREE(*transferdata); - } + for (iop = 0; iop < nop; ++iop, ++transferinfo) { + NPY_cast_info_xfree(&transferinfo->read); + NPY_cast_info_xfree(&transferinfo->write); } } - /* Deallocate all the dtypes and objects that were iterated */ - for(iop = 0; iop < nop; ++iop, ++dtype, ++object) { + /* + * Deallocate all the dtypes and objects that were iterated and resolve + * any writeback buffers created by the iterator. + */ + for (iop = 0; iop < nop; ++iop, ++dtype, ++object) { + if (op_itflags[iop] & NPY_OP_ITFLAG_HAS_WRITEBACK) { + if (success && PyArray_ResolveWritebackIfCopy(*object) < 0) { + success = 0; + } + else { + PyArray_DiscardWritebackIfCopy(*object); + } + } Py_XDECREF(*dtype); Py_XDECREF(*object); } /* Deallocate the iterator memory */ PyObject_Free(iter); - - return NPY_SUCCEED; + return success; } + /* Checks 'flags' for (C|F)_ORDER_INDEX, MULTI_INDEX, and EXTERNAL_LOOP, * setting the appropriate internal flags in 'itflags'. * @@ -775,7 +804,7 @@ npyiter_check_global_flags(npy_uint32 flags, npy_uint32* itflags) static int npyiter_check_op_axes(int nop, int oa_ndim, int **op_axes, - npy_intp *itershape) + const npy_intp *itershape) { char axes_dupcheck[NPY_MAXDIMS]; int iop, idim; @@ -798,7 +827,7 @@ npyiter_check_op_axes(int nop, int oa_ndim, int **op_axes, PyErr_Format(PyExc_ValueError, "Cannot construct an iterator with more than %d dimensions " "(%d were requested for op_axes)", - (int)NPY_MAXDIMS, oa_ndim); + NPY_MAXDIMS, oa_ndim); return 0; } if (op_axes == NULL) { @@ -814,14 +843,15 @@ npyiter_check_op_axes(int nop, int oa_ndim, int **op_axes, if (axes != NULL) { memset(axes_dupcheck, 0, NPY_MAXDIMS); for (idim = 0; idim < oa_ndim; ++idim) { - npy_intp i = axes[idim]; + int i = npyiter_get_op_axis(axes[idim], NULL); + if (i >= 0) { if (i >= NPY_MAXDIMS) { PyErr_Format(PyExc_ValueError, "The 'op_axes' provided to the iterator " "constructor for operand %d " "contained invalid " - "values %d", (int)iop, (int)i); + "values %d", iop, i); return 0; } else if (axes_dupcheck[i] == 1) { @@ -829,7 +859,7 @@ npyiter_check_op_axes(int nop, int oa_ndim, int **op_axes, "The 'op_axes' provided to the iterator " "constructor for operand %d " "contained duplicate " - "value %d", (int)iop, (int)i); + "value %d", iop, i); return 0; } else { @@ -1087,17 +1117,11 @@ npyiter_prepare_one_operand(PyArrayObject **op, */ if (op_request_dtype != NULL) { /* We just have a borrowed reference to op_request_dtype */ - Py_INCREF(op_request_dtype); - /* If the requested dtype is flexible, adapt it */ - PyArray_AdaptFlexibleDType((PyObject *)(*op), PyArray_DESCR(*op), - &op_request_dtype); - if (op_request_dtype == NULL) { + Py_SETREF(*op_dtype, PyArray_AdaptDescriptorToArray( + *op, (PyObject *)op_request_dtype)); + if (*op_dtype == NULL) { return 0; } - - /* Store the requested dtype */ - Py_DECREF(*op_dtype); - *op_dtype = op_request_dtype; } /* Check if the operand is in the byte order requested */ @@ -1120,7 +1144,7 @@ npyiter_prepare_one_operand(PyArrayObject **op, /* Check if the operand is aligned */ if (op_flags & NPY_ITER_ALIGNED) { /* Check alignment */ - if (!PyArray_ISALIGNED(*op)) { + if (!IsAligned(*op)) { NPY_IT_DBG_PRINT("Iterator: Setting NPY_OP_ITFLAG_CAST " "because of NPY_ITER_ALIGNED\n"); *op_itflags |= NPY_OP_ITFLAG_CAST; @@ -1236,9 +1260,9 @@ npyiter_prepare_operands(int nop, PyArrayObject **op_in, return 1; fail_nop: - iop = nop; + iop = nop - 1; fail_iop: - for (i = 0; i < iop; ++i) { + for (i = 0; i < iop+1; ++i) { Py_XDECREF(op[i]); Py_XDECREF(op_dtype[i]); } @@ -1296,21 +1320,11 @@ npyiter_check_casting(int nop, PyArrayObject **op, !PyArray_CanCastArrayTo(op[iop], op_dtype[iop], casting)) { - PyObject *errmsg; - errmsg = PyUString_FromFormat( - "Iterator operand %d dtype could not be cast from ", - (int)iop); - PyUString_ConcatAndDel(&errmsg, - PyObject_Repr((PyObject *)PyArray_DESCR(op[iop]))); - PyUString_ConcatAndDel(&errmsg, - PyUString_FromString(" to ")); - PyUString_ConcatAndDel(&errmsg, - PyObject_Repr((PyObject *)op_dtype[iop])); - PyUString_ConcatAndDel(&errmsg, - PyUString_FromFormat(" according to the rule %s", - npyiter_casting_to_string(casting))); - PyErr_SetObject(PyExc_TypeError, errmsg); - Py_DECREF(errmsg); + PyErr_Format(PyExc_TypeError, + "Iterator operand %d dtype could not be cast from " + "%R to %R according to the rule %s", + iop, PyArray_DESCR(op[iop]), op_dtype[iop], + npyiter_casting_to_string(casting)); return 0; } /* Check write (temp -> op) casting */ @@ -1318,22 +1332,12 @@ npyiter_check_casting(int nop, PyArrayObject **op, !PyArray_CanCastTypeTo(op_dtype[iop], PyArray_DESCR(op[iop]), casting)) { - PyObject *errmsg; - errmsg = PyUString_FromString( - "Iterator requested dtype could not be cast from "); - PyUString_ConcatAndDel(&errmsg, - PyObject_Repr((PyObject *)op_dtype[iop])); - PyUString_ConcatAndDel(&errmsg, - PyUString_FromString(" to ")); - PyUString_ConcatAndDel(&errmsg, - PyObject_Repr((PyObject *)PyArray_DESCR(op[iop]))); - PyUString_ConcatAndDel(&errmsg, - PyUString_FromFormat(", the operand %d dtype, " - "according to the rule %s", - (int)iop, - npyiter_casting_to_string(casting))); - PyErr_SetObject(PyExc_TypeError, errmsg); - Py_DECREF(errmsg); + PyErr_Format(PyExc_TypeError, + "Iterator requested dtype could not be cast from " + "%R to %R, the operand %d dtype, " + "according to the rule %s", + op_dtype[iop], PyArray_DESCR(op[iop]), iop, + npyiter_casting_to_string(casting)); return 0; } @@ -1398,6 +1402,61 @@ check_mask_for_writemasked_reduction(NpyIter *iter, int iop) return 1; } +/* + * Check whether a reduction is OK based on the flags and the operand being + * readwrite. This path is deprecated, since usually only specific axes + * should be reduced. If axes are specified explicitly, the flag is + * unnecessary. + */ +static int +npyiter_check_reduce_ok_and_set_flags( + NpyIter *iter, npy_uint32 flags, npyiter_opitflags *op_itflags, + int dim) { + /* If it's writeable, this means a reduction */ + if (*op_itflags & NPY_OP_ITFLAG_WRITE) { + if (!(flags & NPY_ITER_REDUCE_OK)) { + PyErr_Format(PyExc_ValueError, + "output operand requires a reduction along dimension %d, " + "but the reduction is not enabled. The dimension size of 1 " + "does not match the expected output shape.", dim); + return 0; + } + if (!(*op_itflags & NPY_OP_ITFLAG_READ)) { + PyErr_SetString(PyExc_ValueError, + "output operand requires a reduction, but is flagged as " + "write-only, not read-write"); + return 0; + } + NPY_IT_DBG_PRINT("Iterator: Indicating that a reduction is" + "occurring\n"); + + NIT_ITFLAGS(iter) |= NPY_ITFLAG_REDUCE; + *op_itflags |= NPY_OP_ITFLAG_REDUCE; + } + return 1; +} + +/** + * Removes the (additive) NPY_ITER_REDUCTION_AXIS indication and sets + * is_forced_broadcast to 1 if it is set. Otherwise to 0. + * + * @param axis The op_axes[i] to normalize. + * @param reduction_axis Output 1 if a reduction axis, otherwise 0. + * @returns The normalized axis (without reduce axis flag). + */ +static NPY_INLINE int +npyiter_get_op_axis(int axis, npy_bool *reduction_axis) { + npy_bool forced_broadcast = axis >= NPY_ITER_REDUCTION_AXIS(-1); + + if (reduction_axis != NULL) { + *reduction_axis = forced_broadcast; + } + if (forced_broadcast) { + return axis - NPY_ITER_REDUCTION_AXIS(0); + } + return axis; +} + /* * Fills in the AXISDATA for the 'nop' operands, broadcasting * the dimensionas as necessary. Also fills @@ -1411,8 +1470,8 @@ check_mask_for_writemasked_reduction(NpyIter *iter, int iop) static int npyiter_fill_axisdata(NpyIter *iter, npy_uint32 flags, npyiter_opitflags *op_itflags, char **op_dataptr, - npy_uint32 *op_flags, int **op_axes, - npy_intp *itershape) + const npy_uint32 *op_flags, int **op_axes, + npy_intp const *itershape) { npy_uint32 itflags = NIT_ITFLAGS(iter); int idim, ndim = NIT_NDIM(iter); @@ -1458,8 +1517,9 @@ npyiter_fill_axisdata(NpyIter *iter, npy_uint32 flags, npyiter_opitflags *op_itf return 0; } for (idim = 0; idim < ondim; ++idim) { - npy_intp bshape = broadcast_shape[idim+ndim-ondim], - op_shape = shape[idim]; + npy_intp bshape = broadcast_shape[idim+ndim-ondim]; + npy_intp op_shape = shape[idim]; + if (bshape == 1) { broadcast_shape[idim+ndim-ondim] = op_shape; } @@ -1471,11 +1531,13 @@ npyiter_fill_axisdata(NpyIter *iter, npy_uint32 flags, npyiter_opitflags *op_itf else { int *axes = op_axes[iop]; for (idim = 0; idim < ndim; ++idim) { - int i = axes[idim]; + int i = npyiter_get_op_axis(axes[idim], NULL); + if (i >= 0) { if (i < ondim) { - npy_intp bshape = broadcast_shape[idim], - op_shape = shape[i]; + npy_intp bshape = broadcast_shape[idim]; + npy_intp op_shape = shape[i]; + if (bshape == 1) { broadcast_shape[idim] = op_shape; } @@ -1488,8 +1550,8 @@ npyiter_fill_axisdata(NpyIter *iter, npy_uint32 flags, npyiter_opitflags *op_itf "Iterator input op_axes[%d][%d] (==%d) " "is not a valid axis of op[%d], which " "has %d dimensions ", - (int)iop, (int)(ndim-idim-1), (int)i, - (int)iop, (int)ondim); + iop, (ndim-idim-1), i, + iop, ondim); return 0; } } @@ -1597,9 +1659,37 @@ npyiter_fill_axisdata(NpyIter *iter, npy_uint32 flags, npyiter_opitflags *op_itf } else { int *axes = op_axes[iop]; - int i = axes[ndim-idim-1]; - if (i >= 0) { - if (bshape == 1 || op_cur == NULL) { + npy_bool reduction_axis; + int i; + i = npyiter_get_op_axis(axes[ndim - idim - 1], &reduction_axis); + + if (reduction_axis) { + /* This is explicitly a reduction axis */ + strides[iop] = 0; + NIT_ITFLAGS(iter) |= NPY_ITFLAG_REDUCE; + op_itflags[iop] |= NPY_OP_ITFLAG_REDUCE; + + if (NPY_UNLIKELY((i >= 0) && (op_cur != NULL) && + (PyArray_DIM(op_cur, i) != 1))) { + PyErr_Format(PyExc_ValueError, + "operand was set up as a reduction along axis " + "%d, but the length of the axis is %zd " + "(it has to be 1)", + i, (Py_ssize_t)PyArray_DIM(op_cur, i)); + return 0; + } + } + else if (bshape == 1) { + /* + * If the full iterator shape is 1, zero always works. + * NOTE: We thus always allow broadcast dimensions (i = -1) + * if the shape is 1. + */ + strides[iop] = 0; + } + else if (i >= 0) { + if (op_cur == NULL) { + /* stride is filled later, shape will match `bshape` */ strides[iop] = 0; } else if (PyArray_DIM(op_cur, i) == 1) { @@ -1607,51 +1697,20 @@ npyiter_fill_axisdata(NpyIter *iter, npy_uint32 flags, npyiter_opitflags *op_itf if (op_flags[iop] & NPY_ITER_NO_BROADCAST) { goto operand_different_than_broadcast; } - /* If it's writeable, this means a reduction */ - if (op_itflags[iop] & NPY_OP_ITFLAG_WRITE) { - if (!(flags & NPY_ITER_REDUCE_OK)) { - PyErr_SetString(PyExc_ValueError, - "output operand requires a reduction, but " - "reduction is not enabled"); - return 0; - } - if (!(op_itflags[iop] & NPY_OP_ITFLAG_READ)) { - PyErr_SetString(PyExc_ValueError, - "output operand requires a reduction, but " - "is flagged as write-only, not " - "read-write"); - return 0; - } - NIT_ITFLAGS(iter) |= NPY_ITFLAG_REDUCE; - op_itflags[iop] |= NPY_OP_ITFLAG_REDUCE; + if (!npyiter_check_reduce_ok_and_set_flags( + iter, flags, &op_itflags[iop], i)) { + return 0; } } else { strides[iop] = PyArray_STRIDE(op_cur, i); } } - else if (bshape == 1) { - strides[iop] = 0; - } else { strides[iop] = 0; - /* If it's writeable, this means a reduction */ - if (op_itflags[iop] & NPY_OP_ITFLAG_WRITE) { - if (!(flags & NPY_ITER_REDUCE_OK)) { - PyErr_SetString(PyExc_ValueError, - "output operand requires a reduction, but " - "reduction is not enabled"); - return 0; - } - if (!(op_itflags[iop] & NPY_OP_ITFLAG_READ)) { - PyErr_SetString(PyExc_ValueError, - "output operand requires a reduction, but " - "is flagged as write-only, not " - "read-write"); - return 0; - } - NIT_ITFLAGS(iter) |= NPY_ITFLAG_REDUCE; - op_itflags[iop] |= NPY_OP_ITFLAG_REDUCE; + if (!npyiter_check_reduce_ok_and_set_flags( + iter, flags, &op_itflags[iop], i)) { + return 0; } } } @@ -1689,79 +1748,76 @@ npyiter_fill_axisdata(NpyIter *iter, npy_uint32 flags, npyiter_opitflags *op_itf return 1; broadcast_error: { - PyObject *errmsg, *tmp; npy_intp remdims[NPY_MAXDIMS]; - char *tmpstr; if (op_axes == NULL) { - errmsg = PyUString_FromString("operands could not be broadcast " - "together with shapes "); - if (errmsg == NULL) { + PyObject *shape1 = PyUnicode_FromString(""); + if (shape1 == NULL) { return 0; } for (iop = 0; iop < nop; ++iop) { if (op[iop] != NULL) { - tmp = convert_shape_to_string(PyArray_NDIM(op[iop]), - PyArray_DIMS(op[iop]), - " "); + int ndims = PyArray_NDIM(op[iop]); + npy_intp *dims = PyArray_DIMS(op[iop]); + PyObject *tmp = convert_shape_to_string(ndims, dims, " "); if (tmp == NULL) { - Py_DECREF(errmsg); + Py_DECREF(shape1); return 0; } - PyUString_ConcatAndDel(&errmsg, tmp); - if (errmsg == NULL) { + Py_SETREF(shape1, PyUnicode_Concat(shape1, tmp)); + Py_DECREF(tmp); + if (shape1 == NULL) { return 0; } } } - if (itershape != NULL) { - tmp = PyUString_FromString("and requested shape "); - if (tmp == NULL) { - Py_DECREF(errmsg); - return 0; - } - PyUString_ConcatAndDel(&errmsg, tmp); - if (errmsg == NULL) { - return 0; - } - - tmp = convert_shape_to_string(ndim, itershape, ""); - if (tmp == NULL) { - Py_DECREF(errmsg); - return 0; - } - PyUString_ConcatAndDel(&errmsg, tmp); - if (errmsg == NULL) { + if (itershape == NULL) { + PyErr_Format(PyExc_ValueError, + "operands could not be broadcast together with " + "shapes %S", shape1); + Py_DECREF(shape1); + return 0; + } + else { + PyObject *shape2 = convert_shape_to_string(ndim, itershape, ""); + if (shape2 == NULL) { + Py_DECREF(shape1); return 0; } - + PyErr_Format(PyExc_ValueError, + "operands could not be broadcast together with " + "shapes %S and requested shape %S", shape1, shape2); + Py_DECREF(shape1); + Py_DECREF(shape2); + return 0; } - PyErr_SetObject(PyExc_ValueError, errmsg); - Py_DECREF(errmsg); } else { - errmsg = PyUString_FromString("operands could not be broadcast " - "together with remapped shapes " - "[original->remapped]: "); + PyObject *shape1 = PyUnicode_FromString(""); + if (shape1 == NULL) { + return 0; + } for (iop = 0; iop < nop; ++iop) { if (op[iop] != NULL) { int *axes = op_axes[iop]; + int ndims = PyArray_NDIM(op[iop]); + npy_intp *dims = PyArray_DIMS(op[iop]); + char *tmpstr = (axes == NULL) ? " " : "->"; - tmpstr = (axes == NULL) ? " " : "->"; - tmp = convert_shape_to_string(PyArray_NDIM(op[iop]), - PyArray_DIMS(op[iop]), - tmpstr); + PyObject *tmp = convert_shape_to_string(ndims, dims, tmpstr); if (tmp == NULL) { + Py_DECREF(shape1); return 0; } - PyUString_ConcatAndDel(&errmsg, tmp); - if (errmsg == NULL) { + Py_SETREF(shape1, PyUnicode_Concat(shape1, tmp)); + Py_DECREF(tmp); + if (shape1 == NULL) { return 0; } if (axes != NULL) { for (idim = 0; idim < ndim; ++idim) { - npy_intp i = axes[idim]; + int i = npyiter_get_op_axis(axes[idim], NULL); if (i >= 0 && i < PyArray_NDIM(op[iop])) { remdims[idim] = PyArray_DIM(op[iop], i); @@ -1770,80 +1826,83 @@ broadcast_error: { remdims[idim] = -1; } } - tmp = convert_shape_to_string(ndim, remdims, " "); + PyObject *tmp = convert_shape_to_string(ndim, remdims, " "); if (tmp == NULL) { + Py_DECREF(shape1); return 0; } - PyUString_ConcatAndDel(&errmsg, tmp); - if (errmsg == NULL) { + Py_SETREF(shape1, PyUnicode_Concat(shape1, tmp)); + Py_DECREF(tmp); + if (shape1 == NULL) { return 0; } } } } - if (itershape != NULL) { - tmp = PyUString_FromString("and requested shape "); - if (tmp == NULL) { - Py_DECREF(errmsg); - return 0; - } - PyUString_ConcatAndDel(&errmsg, tmp); - if (errmsg == NULL) { - return 0; - } - - tmp = convert_shape_to_string(ndim, itershape, ""); - if (tmp == NULL) { - Py_DECREF(errmsg); - return 0; - } - PyUString_ConcatAndDel(&errmsg, tmp); - if (errmsg == NULL) { + if (itershape == NULL) { + PyErr_Format(PyExc_ValueError, + "operands could not be broadcast together with " + "remapped shapes [original->remapped]: %S", shape1); + Py_DECREF(shape1); + return 0; + } + else { + PyObject *shape2 = convert_shape_to_string(ndim, itershape, ""); + if (shape2 == NULL) { + Py_DECREF(shape1); return 0; } - + PyErr_Format(PyExc_ValueError, + "operands could not be broadcast together with " + "remapped shapes [original->remapped]: %S and " + "requested shape %S", shape1, shape2); + Py_DECREF(shape1); + Py_DECREF(shape2); + return 0; } - PyErr_SetObject(PyExc_ValueError, errmsg); - Py_DECREF(errmsg); } - - return 0; } operand_different_than_broadcast: { - npy_intp remdims[NPY_MAXDIMS]; - PyObject *errmsg, *tmp; - - /* Start of error message */ - if (op_flags[iop] & NPY_ITER_READONLY) { - errmsg = PyUString_FromString("non-broadcastable operand " - "with shape "); - } - else { - errmsg = PyUString_FromString("non-broadcastable output " - "operand with shape "); - } - if (errmsg == NULL) { + /* operand shape */ + int ndims = PyArray_NDIM(op[iop]); + npy_intp *dims = PyArray_DIMS(op[iop]); + PyObject *shape1 = convert_shape_to_string(ndims, dims, ""); + if (shape1 == NULL) { return 0; } - /* Operand shape */ - tmp = convert_shape_to_string(PyArray_NDIM(op[iop]), - PyArray_DIMS(op[iop]), ""); - if (tmp == NULL) { + /* Broadcast shape */ + PyObject *shape2 = convert_shape_to_string(ndim, broadcast_shape, ""); + if (shape2 == NULL) { + Py_DECREF(shape1); return 0; } - PyUString_ConcatAndDel(&errmsg, tmp); - if (errmsg == NULL) { + + if (op_axes == NULL || op_axes[iop] == NULL) { + /* operand shape not remapped */ + + if (op_flags[iop] & NPY_ITER_READONLY) { + PyErr_Format(PyExc_ValueError, + "non-broadcastable operand with shape %S doesn't " + "match the broadcast shape %S", shape1, shape2); + } + else { + PyErr_Format(PyExc_ValueError, + "non-broadcastable output operand with shape %S doesn't " + "match the broadcast shape %S", shape1, shape2); + } + Py_DECREF(shape1); + Py_DECREF(shape2); return 0; } - /* Remapped operand shape */ - if (op_axes != NULL && op_axes[iop] != NULL) { - int *axes = op_axes[iop]; + else { + /* operand shape remapped */ + npy_intp remdims[NPY_MAXDIMS]; + int *axes = op_axes[iop]; for (idim = 0; idim < ndim; ++idim) { - npy_intp i = axes[ndim-idim-1]; - + npy_intp i = axes[ndim - idim - 1]; if (i >= 0 && i < PyArray_NDIM(op[iop])) { remdims[idim] = PyArray_DIM(op[iop], i); } @@ -1852,48 +1911,30 @@ operand_different_than_broadcast: { } } - tmp = PyUString_FromString(" [remapped to "); - if (tmp == NULL) { - return 0; - } - PyUString_ConcatAndDel(&errmsg, tmp); - if (errmsg == NULL) { + PyObject *shape3 = convert_shape_to_string(ndim, remdims, ""); + if (shape3 == NULL) { + Py_DECREF(shape1); + Py_DECREF(shape2); return 0; } - tmp = convert_shape_to_string(ndim, remdims, "]"); - if (tmp == NULL) { - return 0; - } - PyUString_ConcatAndDel(&errmsg, tmp); - if (errmsg == NULL) { - return 0; + if (op_flags[iop] & NPY_ITER_READONLY) { + PyErr_Format(PyExc_ValueError, + "non-broadcastable operand with shape %S " + "[remapped to %S] doesn't match the broadcast shape %S", + shape1, shape3, shape2); } - } - - tmp = PyUString_FromString(" doesn't match the broadcast shape "); - if (tmp == NULL) { - return 0; - } - PyUString_ConcatAndDel(&errmsg, tmp); - if (errmsg == NULL) { - return 0; - } - - /* Broadcast shape */ - tmp = convert_shape_to_string(ndim, broadcast_shape, ""); - if (tmp == NULL) { - return 0; - } - PyUString_ConcatAndDel(&errmsg, tmp); - if (errmsg == NULL) { + else { + PyErr_Format(PyExc_ValueError, + "non-broadcastable output operand with shape %S " + "[remapped to %S] doesn't match the broadcast shape %S", + shape1, shape3, shape2); + } + Py_DECREF(shape1); + Py_DECREF(shape2); + Py_DECREF(shape3); return 0; } - - PyErr_SetObject(PyExc_ValueError, errmsg); - Py_DECREF(errmsg); - - return 0; } } @@ -1907,14 +1948,14 @@ operand_different_than_broadcast: { * array. */ static void -npyiter_replace_axisdata(NpyIter *iter, int iop, - PyArrayObject *op, - int op_ndim, char *op_dataptr, - int *op_axes) +npyiter_replace_axisdata( + NpyIter *iter, int iop, PyArrayObject *op, + int orig_op_ndim, const int *op_axes) { npy_uint32 itflags = NIT_ITFLAGS(iter); int idim, ndim = NIT_NDIM(iter); int nop = NIT_NOP(iter); + char *op_dataptr = PyArray_DATA(op); NpyIter_AxisData *axisdata0, *axisdata; npy_intp sizeof_axisdata; @@ -1933,25 +1974,20 @@ npyiter_replace_axisdata(NpyIter *iter, int iop, if (op_axes != NULL) { for (idim = 0; idim < ndim; ++idim, NIT_ADVANCE_AXISDATA(axisdata, 1)) { - npy_int8 p; int i; + npy_bool axis_flipped; npy_intp shape; - /* Apply the perm to get the original axis */ - p = perm[idim]; - if (p < 0) { - i = op_axes[ndim+p]; - } - else { - i = op_axes[ndim-p-1]; - } + /* Apply perm to get the original axis, and check if its flipped */ + i = npyiter_undo_iter_axis_perm(idim, ndim, perm, &axis_flipped); - if (0 <= i && i < op_ndim) { + i = npyiter_get_op_axis(op_axes[i], NULL); + assert(i < orig_op_ndim); + if (i >= 0) { shape = PyArray_DIM(op, i); if (shape != 1) { npy_intp stride = PyArray_STRIDE(op, i); - if (p < 0) { - /* If the perm entry is negative, flip the axis */ + if (axis_flipped) { NAD_STRIDES(axisdata)[iop] = -stride; baseoffset += stride*(shape-1); } @@ -1964,25 +2000,18 @@ npyiter_replace_axisdata(NpyIter *iter, int iop, } else { for (idim = 0; idim < ndim; ++idim, NIT_ADVANCE_AXISDATA(axisdata, 1)) { - npy_int8 p; int i; + npy_bool axis_flipped; npy_intp shape; - /* Apply the perm to get the original axis */ - p = perm[idim]; - if (p < 0) { - i = op_ndim+p; - } - else { - i = op_ndim-p-1; - } + i = npyiter_undo_iter_axis_perm( + idim, orig_op_ndim, perm, &axis_flipped); if (i >= 0) { shape = PyArray_DIM(op, i); if (shape != 1) { npy_intp stride = PyArray_STRIDE(op, i); - if (p < 0) { - /* If the perm entry is negative, flip the axis */ + if (axis_flipped) { NAD_STRIDES(axisdata)[iop] = -stride; baseoffset += stride*(shape-1); } @@ -2108,8 +2137,8 @@ npyiter_apply_forced_iteration_order(NpyIter *iter, NPY_ORDER order) /* Check that all the array inputs are fortran order */ for (iop = 0; iop < nop; ++iop, ++op) { if (*op && !PyArray_CHKFLAGS(*op, NPY_ARRAY_F_CONTIGUOUS)) { - forder = 0; - break; + forder = 0; + break; } } @@ -2397,7 +2426,7 @@ npyiter_find_best_axis_ordering(NpyIter *iter) */ static PyArray_Descr * npyiter_get_common_dtype(int nop, PyArrayObject **op, - npyiter_opitflags *op_itflags, PyArray_Descr **op_dtype, + const npyiter_opitflags *op_itflags, PyArray_Descr **op_dtype, PyArray_Descr **op_request_dtypes, int only_inputs) { @@ -2464,19 +2493,20 @@ npyiter_get_common_dtype(int nop, PyArrayObject **op, static PyArrayObject * npyiter_new_temp_array(NpyIter *iter, PyTypeObject *subtype, npy_uint32 flags, npyiter_opitflags *op_itflags, - int op_ndim, npy_intp *shape, - PyArray_Descr *op_dtype, int *op_axes) + int op_ndim, npy_intp const *shape, + PyArray_Descr *op_dtype, const int *op_axes) { npy_uint32 itflags = NIT_ITFLAGS(iter); int idim, ndim = NIT_NDIM(iter); + int used_op_ndim; int nop = NIT_NOP(iter); npy_int8 *perm = NIT_PERM(iter); - npy_intp new_shape[NPY_MAXDIMS], strides[NPY_MAXDIMS], - stride = op_dtype->elsize; + npy_intp new_shape[NPY_MAXDIMS], strides[NPY_MAXDIMS]; + npy_intp stride = op_dtype->elsize; NpyIter_AxisData *axisdata; npy_intp sizeof_axisdata; - npy_intp i; + int i; PyArrayObject *ret; @@ -2503,39 +2533,46 @@ npyiter_new_temp_array(NpyIter *iter, PyTypeObject *subtype, sizeof_axisdata = NIT_AXISDATA_SIZEOF(itflags, ndim, nop); /* Initialize the strides to invalid values */ - for (i = 0; i < NPY_MAXDIMS; ++i) { + for (i = 0; i < op_ndim; ++i) { strides[i] = NPY_MAX_INTP; } if (op_axes != NULL) { + used_op_ndim = 0; for (idim = 0; idim < ndim; ++idim, NIT_ADVANCE_AXISDATA(axisdata, 1)) { - npy_int8 p; + npy_bool reduction_axis; /* Apply the perm to get the original axis */ - p = perm[idim]; - if (p < 0) { - i = op_axes[ndim+p]; - } - else { - i = op_axes[ndim-p-1]; - } + i = npyiter_undo_iter_axis_perm(idim, ndim, perm, NULL); + i = npyiter_get_op_axis(op_axes[i], &reduction_axis); if (i >= 0) { NPY_IT_DBG_PRINT3("Iterator: Setting allocated stride %d " "for iterator dimension %d to %d\n", (int)i, (int)idim, (int)stride); + used_op_ndim += 1; strides[i] = stride; if (shape == NULL) { - new_shape[i] = NAD_SHAPE(axisdata); + if (reduction_axis) { + /* reduction axes always have a length of 1 */ + new_shape[i] = 1; + } + else { + new_shape[i] = NAD_SHAPE(axisdata); + } stride *= new_shape[i]; if (i >= ndim) { - PyErr_SetString(PyExc_ValueError, + PyErr_Format(PyExc_ValueError, "automatically allocated output array " - "specified with an inconsistent axis mapping"); + "specified with an inconsistent axis mapping; " + "the axis mapping cannot include dimension %d " + "which is too large for the iterator dimension " + "of %d.", i, ndim); return NULL; } } else { + assert(!reduction_axis || shape[i] == 1); stride *= shape[i]; } } @@ -2543,44 +2580,25 @@ npyiter_new_temp_array(NpyIter *iter, PyTypeObject *subtype, if (shape == NULL) { /* * If deleting this axis produces a reduction, but - * reduction wasn't enabled, throw an error + * reduction wasn't enabled, throw an error. + * NOTE: We currently always allow new-axis if the iteration + * size is 1 (thus allowing broadcasting sometimes). */ - if (NAD_SHAPE(axisdata) != 1) { - if (!(flags & NPY_ITER_REDUCE_OK)) { - PyErr_SetString(PyExc_ValueError, - "output requires a reduction, but " - "reduction is not enabled"); - return NULL; - } - if (!((*op_itflags) & NPY_OP_ITFLAG_READ)) { - PyErr_SetString(PyExc_ValueError, - "output requires a reduction, but " - "is flagged as write-only, not read-write"); + if (!reduction_axis && NAD_SHAPE(axisdata) != 1) { + if (!npyiter_check_reduce_ok_and_set_flags( + iter, flags, op_itflags, i)) { return NULL; } - - NPY_IT_DBG_PRINT("Iterator: Indicating that a " - "reduction is occurring\n"); - /* Indicate that a reduction is occurring */ - NIT_ITFLAGS(iter) |= NPY_ITFLAG_REDUCE; - (*op_itflags) |= NPY_OP_ITFLAG_REDUCE; } } } } } else { + used_op_ndim = ndim; for (idim = 0; idim < ndim; ++idim, NIT_ADVANCE_AXISDATA(axisdata, 1)) { - npy_int8 p; - /* Apply the perm to get the original axis */ - p = perm[idim]; - if (p < 0) { - i = op_ndim + p; - } - else { - i = op_ndim - p - 1; - } + i = npyiter_undo_iter_axis_perm(idim, op_ndim, perm, NULL); if (i >= 0) { NPY_IT_DBG_PRINT3("Iterator: Setting allocated stride %d " @@ -2598,73 +2616,58 @@ npyiter_new_temp_array(NpyIter *iter, PyTypeObject *subtype, } } - /* - * If custom axes were specified, some dimensions may not have been used. - * Add the REDUCE itflag if this creates a reduction situation. - */ if (shape == NULL) { - /* Ensure there are no dimension gaps in op_axes, and find op_ndim */ - op_ndim = ndim; - if (op_axes != NULL) { - for (i = 0; i < ndim; ++i) { - if (strides[i] == NPY_MAX_INTP) { - if (op_ndim == ndim) { - op_ndim = i; - } - } - /* - * If there's a gap in the array's dimensions, it's an error. - * For example, op_axes of [0,2] for the automatically - * allocated output. - */ - else if (op_ndim != ndim) { - PyErr_SetString(PyExc_ValueError, - "automatically allocated output array " - "specified with an inconsistent axis mapping"); - return NULL; - } + /* If shape was NULL, use the shape we calculated */ + op_ndim = used_op_ndim; + shape = new_shape; + /* + * If there's a gap in the array's dimensions, it's an error. + * For instance, if op_axes [0, 2] is specified, there will a place + * in the strides array where the value is not set. + */ + for (i = 0; i < op_ndim; i++) { + if (strides[i] == NPY_MAX_INTP) { + PyErr_Format(PyExc_ValueError, + "automatically allocated output array " + "specified with an inconsistent axis mapping; " + "the axis mapping is missing an entry for " + "dimension %d.", i); + return NULL; } } } - else { - for (i = 0; i < op_ndim; ++i) { - if (strides[i] == NPY_MAX_INTP) { - npy_intp factor, new_strides[NPY_MAXDIMS], - itemsize; - - /* Fill in the missing strides in C order */ - factor = 1; - itemsize = op_dtype->elsize; - for (i = op_ndim-1; i >= 0; --i) { - if (strides[i] == NPY_MAX_INTP) { - new_strides[i] = factor * itemsize; - factor *= shape[i]; - } - } - - /* - * Copy the missing strides, and multiply the existing strides - * by the calculated factor. This way, the missing strides - * are tighter together in memory, which is good for nested - * loops. - */ - for (i = 0; i < op_ndim; ++i) { - if (strides[i] == NPY_MAX_INTP) { - strides[i] = new_strides[i]; - } - else { - strides[i] *= factor; - } - } + else if (used_op_ndim < op_ndim) { + /* + * If custom axes were specified, some dimensions may not have + * been used. These are additional axes which are ignored in the + * iterator but need to be handled here. + */ + npy_intp factor, itemsize, new_strides[NPY_MAXDIMS]; - break; + /* Fill in the missing strides in C order */ + factor = 1; + itemsize = op_dtype->elsize; + for (i = op_ndim-1; i >= 0; --i) { + if (strides[i] == NPY_MAX_INTP) { + new_strides[i] = factor * itemsize; + factor *= shape[i]; } } - } - /* If shape was NULL, set it to the shape we calculated */ - if (shape == NULL) { - shape = new_shape; + /* + * Copy the missing strides, and multiply the existing strides + * by the calculated factor. This way, the missing strides + * are tighter together in memory, which is good for nested + * loops. + */ + for (i = 0; i < op_ndim; ++i) { + if (strides[i] == NPY_MAX_INTP) { + strides[i] = new_strides[i]; + } + else { + strides[i] *= factor; + } + } } /* Allocate the temporary array */ @@ -2677,6 +2680,11 @@ npyiter_new_temp_array(NpyIter *iter, PyTypeObject *subtype, /* Double-check that the subtype didn't mess with the dimensions */ if (subtype != &PyArray_Type) { + /* + * TODO: the dtype could have a subarray, which adds new dimensions + * to `ret`, that should typically be fine, but will break + * in this branch. + */ if (PyArray_NDIM(ret) != op_ndim || !PyArray_CompareLists(shape, PyArray_DIMS(ret), op_ndim)) { PyErr_SetString(PyExc_RuntimeError, @@ -2694,7 +2702,7 @@ static int npyiter_allocate_arrays(NpyIter *iter, npy_uint32 flags, PyArray_Descr **op_dtype, PyTypeObject *subtype, - npy_uint32 *op_flags, npyiter_opitflags *op_itflags, + const npy_uint32 *op_flags, npyiter_opitflags *op_itflags, int **op_axes) { npy_uint32 itflags = NIT_ITFLAGS(iter); @@ -2813,16 +2821,21 @@ npyiter_allocate_arrays(NpyIter *iter, if (op[iop] == NULL) { PyArrayObject *out; PyTypeObject *op_subtype; - int ondim = ndim; /* Check whether the subtype was disabled */ op_subtype = (op_flags[iop] & NPY_ITER_NO_SUBTYPE) ? &PyArray_Type : subtype; - /* Allocate the output array */ + /* + * Allocate the output array. + * + * Note that here, ndim is always correct if no op_axes was given + * (but the actual dimension of op can be larger). If op_axes + * is given, ndim is not actually used. + */ out = npyiter_new_temp_array(iter, op_subtype, flags, &op_itflags[iop], - ondim, + ndim, NULL, op_dtype[iop], op_axes ? op_axes[iop] : NULL); @@ -2836,11 +2849,17 @@ npyiter_allocate_arrays(NpyIter *iter, * Now we need to replace the pointers and strides with values * from the new array. */ - npyiter_replace_axisdata(iter, iop, op[iop], ondim, - PyArray_DATA(op[iop]), op_axes ? op_axes[iop] : NULL); + npyiter_replace_axisdata(iter, iop, op[iop], ndim, + op_axes ? op_axes[iop] : NULL); - /* New arrays are aligned and need no cast */ - op_itflags[iop] |= NPY_OP_ITFLAG_ALIGNED; + /* + * New arrays are guaranteed true-aligned, but copy/cast code + * needs uint-alignment in addition. + */ + if (IsUintAligned(out)) { + op_itflags[iop] |= NPY_OP_ITFLAG_ALIGNED; + } + /* New arrays need no cast */ op_itflags[iop] &= ~NPY_OP_ITFLAG_CAST; } /* @@ -2872,15 +2891,20 @@ npyiter_allocate_arrays(NpyIter *iter, * Now we need to replace the pointers and strides with values * from the temporary array. */ - npyiter_replace_axisdata(iter, iop, op[iop], 0, - PyArray_DATA(op[iop]), NULL); + npyiter_replace_axisdata(iter, iop, op[iop], 0, NULL); /* - * New arrays are aligned need no cast, and in the case + * New arrays are guaranteed true-aligned, but copy/cast code + * needs uint-alignment in addition. + */ + if (IsUintAligned(temp)) { + op_itflags[iop] |= NPY_OP_ITFLAG_ALIGNED; + } + /* + * New arrays need no cast, and in the case * of scalars, always have stride 0 so never need buffering */ - op_itflags[iop] |= (NPY_OP_ITFLAG_ALIGNED | - NPY_OP_ITFLAG_BUFNEVER); + op_itflags[iop] |= NPY_OP_ITFLAG_BUFNEVER; op_itflags[iop] &= ~NPY_OP_ITFLAG_CAST; if (itflags & NPY_ITFLAG_BUFFER) { NBF_STRIDES(bufferdata)[iop] = 0; @@ -2939,10 +2963,16 @@ npyiter_allocate_arrays(NpyIter *iter, * from the temporary array. */ npyiter_replace_axisdata(iter, iop, op[iop], ondim, - PyArray_DATA(op[iop]), op_axes ? op_axes[iop] : NULL); + op_axes ? op_axes[iop] : NULL); - /* The temporary copy is aligned and needs no cast */ - op_itflags[iop] |= NPY_OP_ITFLAG_ALIGNED; + /* + * New arrays are guaranteed true-aligned, but copy/cast code + * additionally needs uint-alignment in addition. + */ + if (IsUintAligned(temp)) { + op_itflags[iop] |= NPY_OP_ITFLAG_ALIGNED; + } + /* The temporary copy needs no cast */ op_itflags[iop] &= ~NPY_OP_ITFLAG_CAST; } else { @@ -2962,7 +2992,7 @@ npyiter_allocate_arrays(NpyIter *iter, * If the operand is aligned, any buffering can use aligned * optimizations. */ - if (PyArray_ISALIGNED(op[iop])) { + if (IsUintAligned(op[iop])) { op_itflags[iop] |= NPY_OP_ITFLAG_ALIGNED; } } @@ -3079,7 +3109,7 @@ npyiter_allocate_arrays(NpyIter *iter, */ static void npyiter_get_priority_subtype(int nop, PyArrayObject **op, - npyiter_opitflags *op_itflags, + const npyiter_opitflags *op_itflags, double *subtype_priority, PyTypeObject **subtype) { @@ -3110,13 +3140,8 @@ npyiter_allocate_transfer_functions(NpyIter *iter) PyArrayObject **op = NIT_OPERANDS(iter); PyArray_Descr **op_dtype = NIT_DTYPES(iter); npy_intp *strides = NAD_STRIDES(axisdata), op_stride; - PyArray_StridedUnaryOp **readtransferfn = NBF_READTRANSFERFN(bufferdata), - **writetransferfn = NBF_WRITETRANSFERFN(bufferdata); - NpyAuxData **readtransferdata = NBF_READTRANSFERDATA(bufferdata), - **writetransferdata = NBF_WRITETRANSFERDATA(bufferdata); + NpyIter_TransferInfo *transferinfo = NBF_TRANSFERINFO(bufferdata); - PyArray_StridedUnaryOp *stransfer = NULL; - NpyAuxData *transferdata = NULL; int needs_api = 0; for (iop = 0; iop < nop; ++iop) { @@ -3142,16 +3167,14 @@ npyiter_allocate_transfer_functions(NpyIter *iter) PyArray_DESCR(op[iop]), op_dtype[iop], move_references, - &stransfer, - &transferdata, + &transferinfo[iop].read, &needs_api) != NPY_SUCCEED) { + iop -= 1; /* This one cannot be cleaned up yet. */ goto fail; } - readtransferfn[iop] = stransfer; - readtransferdata[iop] = transferdata; } else { - readtransferfn[iop] = NULL; + transferinfo[iop].read.func = NULL; } if (flags & NPY_OP_ITFLAG_WRITE) { int move_references = 1; @@ -3167,38 +3190,33 @@ npyiter_allocate_transfer_functions(NpyIter *iter) * could be inconsistent. */ if (PyArray_GetMaskedDTypeTransferFunction( - (flags & NPY_OP_ITFLAG_ALIGNED) != 0, - op_dtype[iop]->elsize, - op_stride, - (strides[maskop] == mask_dtype->elsize) ? - mask_dtype->elsize : - NPY_MAX_INTP, - op_dtype[iop], - PyArray_DESCR(op[iop]), - mask_dtype, - move_references, - (PyArray_MaskedStridedUnaryOp **)&stransfer, - &transferdata, - &needs_api) != NPY_SUCCEED) { + (flags & NPY_OP_ITFLAG_ALIGNED) != 0, + op_dtype[iop]->elsize, + op_stride, + (strides[maskop] == mask_dtype->elsize) ? + mask_dtype->elsize : NPY_MAX_INTP, + op_dtype[iop], + PyArray_DESCR(op[iop]), + mask_dtype, + move_references, + &transferinfo[iop].write, + &needs_api) != NPY_SUCCEED) { goto fail; } } else { if (PyArray_GetDTypeTransferFunction( - (flags & NPY_OP_ITFLAG_ALIGNED) != 0, - op_dtype[iop]->elsize, - op_stride, - op_dtype[iop], - PyArray_DESCR(op[iop]), - move_references, - &stransfer, - &transferdata, - &needs_api) != NPY_SUCCEED) { + (flags & NPY_OP_ITFLAG_ALIGNED) != 0, + op_dtype[iop]->elsize, + op_stride, + op_dtype[iop], + PyArray_DESCR(op[iop]), + move_references, + &transferinfo[iop].write, + &needs_api) != NPY_SUCCEED) { goto fail; } } - writetransferfn[iop] = stransfer; - writetransferdata[iop] = transferdata; } /* If no write back but there are references make a decref fn */ else if (PyDataType_REFCHK(op_dtype[iop])) { @@ -3208,25 +3226,22 @@ npyiter_allocate_transfer_functions(NpyIter *iter) * src references. */ if (PyArray_GetDTypeTransferFunction( - (flags & NPY_OP_ITFLAG_ALIGNED) != 0, - op_dtype[iop]->elsize, 0, - op_dtype[iop], NULL, - 1, - &stransfer, - &transferdata, - &needs_api) != NPY_SUCCEED) { + (flags & NPY_OP_ITFLAG_ALIGNED) != 0, + op_dtype[iop]->elsize, 0, + op_dtype[iop], NULL, + 1, + &transferinfo[iop].write, + &needs_api) != NPY_SUCCEED) { goto fail; } - writetransferfn[iop] = stransfer; - writetransferdata[iop] = transferdata; } else { - writetransferfn[iop] = NULL; + transferinfo[iop].write.func = NULL; } } else { - readtransferfn[iop] = NULL; - writetransferfn[iop] = NULL; + transferinfo[iop].read.func = NULL; + transferinfo[iop].write.func = NULL; } } @@ -3238,15 +3253,9 @@ npyiter_allocate_transfer_functions(NpyIter *iter) return 1; fail: - for (i = 0; i < iop; ++i) { - if (readtransferdata[iop] != NULL) { - NPY_AUXDATA_FREE(readtransferdata[iop]); - readtransferdata[iop] = NULL; - } - if (writetransferdata[iop] != NULL) { - NPY_AUXDATA_FREE(writetransferdata[iop]); - writetransferdata[iop] = NULL; - } + for (i = 0; i < iop+1; ++i) { + NPY_cast_info_xfree(&transferinfo[iop].read); + NPY_cast_info_xfree(&transferinfo[iop].write); } return 0; } diff --git a/numpy/core/src/multiarray/nditer_impl.h b/numpy/core/src/multiarray/nditer_impl.h index 5fb146026f74..2a82b7e5410d 100644 --- a/numpy/core/src/multiarray/nditer_impl.h +++ b/numpy/core/src/multiarray/nditer_impl.h @@ -4,23 +4,25 @@ * should use the exposed iterator API. */ #ifndef NPY_ITERATOR_IMPLEMENTATION_CODE -#error "This header is intended for use ONLY by iterator implementation code." +#error This header is intended for use ONLY by iterator implementation code. #endif -#ifndef _NPY_PRIVATE__NDITER_IMPL_H_ -#define _NPY_PRIVATE__NDITER_IMPL_H_ - -#define PY_SSIZE_T_CLEAN -#include "Python.h" -#include "structmember.h" +#ifndef NUMPY_CORE_SRC_MULTIARRAY_NDITER_IMPL_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_NDITER_IMPL_H_ #define NPY_NO_DEPRECATED_API NPY_API_VERSION #define _MULTIARRAYMODULE -#include <numpy/arrayobject.h> -#include <npy_pycompat.h> + +#define PY_SSIZE_T_CLEAN +#include <Python.h> +#include <structmember.h> + +#include "numpy/arrayobject.h" +#include "npy_pycompat.h" #include "convert_datatype.h" #include "lowlevel_strided_loops.h" +#include "dtype_transfer.h" /********** ITERATOR CONSTRUCTION TIMING **************/ #define NPY_IT_CONSTRUCTION_TIMING 0 @@ -148,8 +150,9 @@ struct NpyIter_InternalOnly { char iter_flexdata; }; -typedef struct NpyIter_AD NpyIter_AxisData; -typedef struct NpyIter_BD NpyIter_BufferData; +typedef struct NpyIter_AxisData_tag NpyIter_AxisData; +typedef struct NpyIter_TransferInfo_tag NpyIter_TransferInfo; +typedef struct NpyIter_BufferData_tag NpyIter_BufferData; typedef npy_int16 npyiter_opitflags; @@ -167,7 +170,8 @@ typedef npy_int16 npyiter_opitflags; #define NIT_OPITFLAGS_SIZEOF(itflags, ndim, nop) \ (NPY_INTP_ALIGNED(sizeof(npyiter_opitflags) * nop)) #define NIT_BUFFERDATA_SIZEOF(itflags, ndim, nop) \ - ((itflags&NPY_ITFLAG_BUFFER) ? ((NPY_SIZEOF_INTP)*(6 + 9*nop)) : 0) + ((itflags&NPY_ITFLAG_BUFFER) ? ( \ + (NPY_SIZEOF_INTP)*(6 + 5*nop) + sizeof(NpyIter_TransferInfo) * nop) : 0) /* Byte offsets of the iterator members starting from iter->iter_flexdata */ #define NIT_PERM_OFFSET() \ @@ -229,11 +233,20 @@ typedef npy_int16 npyiter_opitflags; &(iter)->iter_flexdata + NIT_AXISDATA_OFFSET(itflags, ndim, nop))) /* Internal-only BUFFERDATA MEMBER ACCESS */ -struct NpyIter_BD { + +struct NpyIter_TransferInfo_tag { + NPY_cast_info read; + NPY_cast_info write; + /* Probably unnecessary, but make sure what follows is intp aligned: */ + npy_intp _unused_ensure_alignment[]; +}; + +struct NpyIter_BufferData_tag { npy_intp buffersize, size, bufiterend, reduce_pos, reduce_outersize, reduce_outerdim; npy_intp bd_flexdata; }; + #define NBF_BUFFERSIZE(bufferdata) ((bufferdata)->buffersize) #define NBF_SIZE(bufferdata) ((bufferdata)->size) #define NBF_BUFITEREND(bufferdata) ((bufferdata)->bufiterend) @@ -248,19 +261,13 @@ struct NpyIter_BD { (&(bufferdata)->bd_flexdata + 2*(nop))) #define NBF_REDUCE_OUTERPTRS(bufferdata) ((char **) \ (&(bufferdata)->bd_flexdata + 3*(nop))) -#define NBF_READTRANSFERFN(bufferdata) ((PyArray_StridedUnaryOp **) \ +#define NBF_BUFFERS(bufferdata) ((char **) \ (&(bufferdata)->bd_flexdata + 4*(nop))) -#define NBF_READTRANSFERDATA(bufferdata) ((NpyAuxData **) \ +#define NBF_TRANSFERINFO(bufferdata) ((NpyIter_TransferInfo *) \ (&(bufferdata)->bd_flexdata + 5*(nop))) -#define NBF_WRITETRANSFERFN(bufferdata) ((PyArray_StridedUnaryOp **) \ - (&(bufferdata)->bd_flexdata + 6*(nop))) -#define NBF_WRITETRANSFERDATA(bufferdata) ((NpyAuxData **) \ - (&(bufferdata)->bd_flexdata + 7*(nop))) -#define NBF_BUFFERS(bufferdata) ((char **) \ - (&(bufferdata)->bd_flexdata + 8*(nop))) /* Internal-only AXISDATA MEMBER ACCESS. */ -struct NpyIter_AD { +struct NpyIter_AxisData_tag { npy_intp shape, index; npy_intp ad_flexdata; }; @@ -269,7 +276,7 @@ struct NpyIter_AD { #define NAD_STRIDES(axisdata) ( \ &(axisdata)->ad_flexdata + 0) #define NAD_PTRS(axisdata) ((char **) \ - &(axisdata)->ad_flexdata + 1*(nop+1)) + (&(axisdata)->ad_flexdata + 1*(nop+1))) #define NAD_NSTRIDES() \ ((nop) + ((itflags&NPY_ITFLAG_HASINDEX) ? 1 : 0)) @@ -282,7 +289,7 @@ struct NpyIter_AD { 1 + \ /* intp stride[nop+1] AND char* ptr[nop+1] */ \ 2*((nop)+1) \ - )*NPY_SIZEOF_INTP ) + )*(size_t)NPY_SIZEOF_INTP) /* * Macro to advance an AXISDATA pointer by a specified count. @@ -301,16 +308,52 @@ struct NpyIter_AD { NIT_AXISDATA_SIZEOF(itflags, ndim, nop)*(ndim ? ndim : 1)) /* Internal helper functions shared between implementation files */ + +/** + * Undo the axis permutation of the iterator. When the operand has fewer + * dimensions then the iterator, this can return negative values for + * inserted (broadcast) dimensions. + * + * @param axis Axis for which to undo the iterator axis permutation. + * @param ndim If `op_axes` is being used, this is the iterator dimension, + * otherwise this is the operand dimension. + * @param perm The iterator axis permutation NIT_PERM(iter) + * @param axis_flipped Will be set to true if this is a flipped axis + * (i.e. is iterated in reversed order) and otherwise false. + * Can be NULL if the information is not needed. + * @return The unpermuted axis. Without `op_axes` this is correct, with + * `op_axes` this indexes into `op_axes` (unpermuted iterator axis) + */ +static NPY_INLINE int +npyiter_undo_iter_axis_perm( + int axis, int ndim, const npy_int8 *perm, npy_bool *axis_flipped) +{ + npy_int8 p = perm[axis]; + /* The iterator treats axis reversed, thus adjust by ndim */ + npy_bool flipped = p < 0; + if (axis_flipped != NULL) { + *axis_flipped = flipped; + } + if (flipped) { + axis = ndim + p; + } + else { + axis = ndim - p - 1; + } + return axis; +} + NPY_NO_EXPORT void npyiter_coalesce_axes(NpyIter *iter); NPY_NO_EXPORT int npyiter_allocate_buffers(NpyIter *iter, char **errmsg); NPY_NO_EXPORT void npyiter_goto_iterindex(NpyIter *iter, npy_intp iterindex); -NPY_NO_EXPORT void +NPY_NO_EXPORT int npyiter_copy_from_buffers(NpyIter *iter); -NPY_NO_EXPORT void +NPY_NO_EXPORT int npyiter_copy_to_buffers(NpyIter *iter, char **prev_dataptrs); +NPY_NO_EXPORT void +npyiter_clear_buffers(NpyIter *iter); - -#endif +#endif /* NUMPY_CORE_SRC_MULTIARRAY_NDITER_IMPL_H_ */ diff --git a/numpy/core/src/multiarray/nditer_pywrap.c b/numpy/core/src/multiarray/nditer_pywrap.c index 0b6c80c8afb7..2675496ab4d6 100644 --- a/numpy/core/src/multiarray/nditer_pywrap.c +++ b/numpy/core/src/multiarray/nditer_pywrap.c @@ -1,24 +1,30 @@ /* - * This file implements the CPython wrapper of the new NumPy iterator. + * This file implements the CPython wrapper of NpyIter * * Copyright (c) 2010 by Mark Wiebe (mwwiebe@gmail.com) * The University of British Columbia * * See LICENSE.txt for the license. */ -#define PY_SSIZE_T_CLEAN -#include "Python.h" -#include "structmember.h" - #define NPY_NO_DEPRECATED_API NPY_API_VERSION #define _MULTIARRAYMODULE -#include <numpy/arrayobject.h> + +#define PY_SSIZE_T_CLEAN +#include <Python.h> +#include <structmember.h> + +#include "numpy/arrayobject.h" #include "npy_config.h" #include "npy_pycompat.h" #include "alloc.h" #include "common.h" +#include "conversion_utils.h" #include "ctors.h" +/* Functions not part of the public NumPy C API */ +npy_bool npyiter_has_writeback(NpyIter *iter); + + typedef struct NewNpyArrayIterObject_tag NewNpyArrayIterObject; struct NewNpyArrayIterObject_tag { @@ -27,8 +33,6 @@ struct NewNpyArrayIterObject_tag { NpyIter *iter; /* Flag indicating iteration started/stopped */ char started, finished; - /* iter operands cannot be referenced if iter is closed */ - npy_bool is_closed; /* Child to update for nested iteration */ NewNpyArrayIterObject *nested_child; /* Cached values from the iterator */ @@ -80,7 +84,8 @@ static int npyiter_cache_values(NewNpyArrayIterObject *self) } static PyObject * -npyiter_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds) +npyiter_new(PyTypeObject *subtype, PyObject *NPY_UNUSED(args), + PyObject *NPY_UNUSED(kwds)) { NewNpyArrayIterObject *self; @@ -88,7 +93,6 @@ npyiter_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds) if (self != NULL) { self->iter = NULL; self->nested_child = NULL; - self->is_closed = 0; } return (PyObject *)self; @@ -229,50 +233,6 @@ NpyIter_GlobalFlagsConverter(PyObject *flags_in, npy_uint32 *flags) return 1; } -/* TODO: Use PyArray_OrderConverter once 'K' is added there */ -static int -npyiter_order_converter(PyObject *order_in, NPY_ORDER *order) -{ - char *str = NULL; - Py_ssize_t length = 0; - - if (PyUnicode_Check(order_in)) { - /* accept unicode input */ - PyObject *str_obj; - int ret; - str_obj = PyUnicode_AsASCIIString(order_in); - if (str_obj == NULL) { - return 0; - } - ret = npyiter_order_converter(str_obj, order); - Py_DECREF(str_obj); - return ret; - } - - if (PyBytes_AsStringAndSize(order_in, &str, &length) < 0) { - return 0; - } - - if (length == 1) switch (str[0]) { - case 'C': - *order = NPY_CORDER; - return 1; - case 'F': - *order = NPY_FORTRANORDER; - return 1; - case 'A': - *order = NPY_ANYORDER; - return 1; - case 'K': - *order = NPY_KEEPORDER; - return 1; - } - - PyErr_SetString(PyExc_ValueError, - "order must be one of 'C', 'F', 'A', or 'K'"); - return 0; -} - static int NpyIter_OpFlagsConverter(PyObject *op_flags_in, npy_uint32 *op_flags) @@ -534,7 +494,7 @@ npyiter_convert_dtypes(PyObject *op_dtypes_in, } static int -npyiter_convert_op_axes(PyObject *op_axes_in, npy_intp nop, +npyiter_convert_op_axes(PyObject *op_axes_in, int nop, int **op_axes, int *oa_ndim) { PyObject *a; @@ -571,6 +531,7 @@ npyiter_convert_op_axes(PyObject *op_axes_in, npy_intp nop, if (*oa_ndim > NPY_MAXDIMS) { PyErr_SetString(PyExc_ValueError, "Too many dimensions in op_axes"); + Py_DECREF(a); return 0; } } @@ -601,8 +562,8 @@ npyiter_convert_op_axes(PyObject *op_axes_in, npy_intp nop, } Py_DECREF(v); } - Py_DECREF(a); } + Py_DECREF(a); } if (*oa_ndim == -1) { @@ -745,7 +706,7 @@ npyiter_init(NewNpyArrayIterObject *self, PyObject *args, PyObject *kwds) int oa_ndim = -1; int op_axes_arrays[NPY_MAXARGS][NPY_MAXDIMS]; int *op_axes[NPY_MAXARGS]; - PyArray_Dims itershape = {NULL, 0}; + PyArray_Dims itershape = {NULL, -1}; int buffersize = 0; if (self->iter != NULL) { @@ -759,10 +720,10 @@ npyiter_init(NewNpyArrayIterObject *self, PyObject *args, PyObject *kwds) NpyIter_GlobalFlagsConverter, &flags, &op_flags_in, &op_dtypes_in, - npyiter_order_converter, &order, + PyArray_OrderConverter, &order, PyArray_CastingConverter, &casting, &op_axes_in, - PyArray_IntpConverter, &itershape, + PyArray_OptionalIntpConverter, &itershape, &buffersize)) { npy_free_cache_dim_obj(itershape); return -1; @@ -797,7 +758,7 @@ npyiter_init(NewNpyArrayIterObject *self, PyObject *args, PyObject *kwds) } } - if (itershape.len > 0) { + if (itershape.len != -1) { if (oa_ndim == -1) { oa_ndim = itershape.len; memset(op_axes, 0, sizeof(op_axes[0]) * nop); @@ -809,10 +770,6 @@ npyiter_init(NewNpyArrayIterObject *self, PyObject *args, PyObject *kwds) goto fail; } } - else if (itershape.ptr != NULL) { - npy_free_cache_dim_obj(itershape); - itershape.ptr = NULL; - } self->iter = NpyIter_AdvancedNew(nop, op, flags, order, casting, op_flags, op_request_dtypes, @@ -892,7 +849,7 @@ NpyIter_NestedIters(PyObject *NPY_UNUSED(self), NpyIter_GlobalFlagsConverter, &flags, &op_flags_in, &op_dtypes_in, - npyiter_order_converter, &order, + PyArray_OrderConverter, &order, PyArray_CastingConverter, &casting, &buffersize)) { return NULL; @@ -938,7 +895,7 @@ NpyIter_NestedIters(PyObject *NPY_UNUSED(self), Py_DECREF(item); return NULL; } - axis = PyInt_AsLong(v); + axis = PyLong_AsLong(v); Py_DECREF(v); if (axis < 0 || axis >= NPY_MAXDIMS) { PyErr_SetString(PyExc_ValueError, @@ -1173,14 +1130,39 @@ NpyIter_NestedIters(PyObject *NPY_UNUSED(self), return NULL; } + static void npyiter_dealloc(NewNpyArrayIterObject *self) { if (self->iter) { - NpyIter_Deallocate(self->iter); + /* Store error, so that WriteUnraisable cannot clear an existing one */ + PyObject *exc, *val, *tb; + PyErr_Fetch(&exc, &val, &tb); + if (npyiter_has_writeback(self->iter)) { + if (PyErr_WarnEx(PyExc_RuntimeWarning, + "Temporary data has not been written back to one of the " + "operands. Typically nditer is used as a context manager " + "otherwise 'close' must be called before reading iteration " + "results.", 1) < 0) { + PyObject *s; + + s = PyUnicode_FromString("npyiter_dealloc"); + if (s) { + PyErr_WriteUnraisable(s); + Py_DECREF(s); + } + else { + PyErr_WriteUnraisable(Py_None); + } + } + } + if (!NpyIter_Deallocate(self->iter)) { + PyErr_WriteUnraisable(Py_None); + } self->iter = NULL; Py_XDECREF(self->nested_child); self->nested_child = NULL; + PyErr_Restore(exc, val, tb); } Py_TYPE(self)->tp_free((PyObject*)self); } @@ -1208,7 +1190,7 @@ npyiter_resetbasepointers(NewNpyArrayIterObject *self) } static PyObject * -npyiter_reset(NewNpyArrayIterObject *self) +npyiter_reset(NewNpyArrayIterObject *self, PyObject *NPY_UNUSED(args)) { if (self->iter == NULL) { PyErr_SetString(PyExc_ValueError, @@ -1245,7 +1227,7 @@ npyiter_reset(NewNpyArrayIterObject *self) * copied. */ static PyObject * -npyiter_copy(NewNpyArrayIterObject *self) +npyiter_copy(NewNpyArrayIterObject *self, PyObject *NPY_UNUSED(args)) { NewNpyArrayIterObject *iter; @@ -1281,7 +1263,7 @@ npyiter_copy(NewNpyArrayIterObject *self) } static PyObject * -npyiter_iternext(NewNpyArrayIterObject *self) +npyiter_iternext(NewNpyArrayIterObject *self, PyObject *NPY_UNUSED(args)) { if (self->iter != NULL && self->iternext != NULL && !self->finished && self->iternext(self->iter)) { @@ -1293,6 +1275,10 @@ npyiter_iternext(NewNpyArrayIterObject *self) Py_RETURN_TRUE; } else { + if (PyErr_Occurred()) { + /* casting error, buffer cleanup will occur at reset or dealloc */ + return NULL; + } self->finished = 1; Py_RETURN_FALSE; } @@ -1334,7 +1320,8 @@ npyiter_remove_axis(NewNpyArrayIterObject *self, PyObject *args) } static PyObject * -npyiter_remove_multi_index(NewNpyArrayIterObject *self) +npyiter_remove_multi_index( + NewNpyArrayIterObject *self, PyObject *NPY_UNUSED(args)) { if (self->iter == NULL) { PyErr_SetString(PyExc_ValueError, @@ -1359,7 +1346,8 @@ npyiter_remove_multi_index(NewNpyArrayIterObject *self) } static PyObject * -npyiter_enable_external_loop(NewNpyArrayIterObject *self) +npyiter_enable_external_loop( + NewNpyArrayIterObject *self, PyObject *NPY_UNUSED(args)) { if (self->iter == NULL) { PyErr_SetString(PyExc_ValueError, @@ -1384,7 +1372,7 @@ npyiter_enable_external_loop(NewNpyArrayIterObject *self) } static PyObject * -npyiter_debug_print(NewNpyArrayIterObject *self) +npyiter_debug_print(NewNpyArrayIterObject *self, PyObject *NPY_UNUSED(args)) { if (self->iter != NULL) { NpyIter_DebugPrint(self->iter); @@ -1399,7 +1387,8 @@ npyiter_debug_print(NewNpyArrayIterObject *self) NPY_NO_EXPORT PyObject * npyiter_seq_item(NewNpyArrayIterObject *self, Py_ssize_t i); -static PyObject *npyiter_value_get(NewNpyArrayIterObject *self) +static PyObject * +npyiter_value_get(NewNpyArrayIterObject *self, void *NPY_UNUSED(ignored)) { PyObject *ret; @@ -1418,12 +1407,6 @@ static PyObject *npyiter_value_get(NewNpyArrayIterObject *self) ret = npyiter_seq_item(self, 0); } else { - if (self->is_closed) { - PyErr_SetString(PyExc_ValueError, - "Iterator is closed"); - return NULL; - } - ret = PyTuple_New(nop); if (ret == NULL) { return NULL; @@ -1441,7 +1424,8 @@ static PyObject *npyiter_value_get(NewNpyArrayIterObject *self) return ret; } -static PyObject *npyiter_operands_get(NewNpyArrayIterObject *self) +static PyObject * +npyiter_operands_get(NewNpyArrayIterObject *self, void *NPY_UNUSED(ignored)) { PyObject *ret; @@ -1453,12 +1437,6 @@ static PyObject *npyiter_operands_get(NewNpyArrayIterObject *self) "Iterator is invalid"); return NULL; } - if (self->is_closed) { - PyErr_SetString(PyExc_ValueError, - "Iterator is closed"); - return NULL; - } - nop = NpyIter_GetNOp(self->iter); operands = self->operands; @@ -1476,7 +1454,8 @@ static PyObject *npyiter_operands_get(NewNpyArrayIterObject *self) return ret; } -static PyObject *npyiter_itviews_get(NewNpyArrayIterObject *self) +static PyObject * +npyiter_itviews_get(NewNpyArrayIterObject *self, void *NPY_UNUSED(ignored)) { PyObject *ret; @@ -1487,13 +1466,6 @@ static PyObject *npyiter_itviews_get(NewNpyArrayIterObject *self) "Iterator is invalid"); return NULL; } - - if (self->is_closed) { - PyErr_SetString(PyExc_ValueError, - "Iterator is closed"); - return NULL; - } - nop = NpyIter_GetNOp(self->iter); ret = PyTuple_New(nop); @@ -1517,7 +1489,7 @@ static PyObject * npyiter_next(NewNpyArrayIterObject *self) { if (self->iter == NULL || self->iternext == NULL || - self->finished || self->is_closed) { + self->finished) { return NULL; } @@ -1527,6 +1499,10 @@ npyiter_next(NewNpyArrayIterObject *self) */ if (self->started) { if (!self->iternext(self->iter)) { + /* + * A casting error may be set here (or no error causing a + * StopIteration). Buffers may only be cleaned up later. + */ self->finished = 1; return NULL; } @@ -1538,13 +1514,13 @@ npyiter_next(NewNpyArrayIterObject *self) } self->started = 1; - return npyiter_value_get(self); + return npyiter_value_get(self, NULL); }; -static PyObject *npyiter_shape_get(NewNpyArrayIterObject *self) +static PyObject * +npyiter_shape_get(NewNpyArrayIterObject *self, void *NPY_UNUSED(ignored)) { - PyObject *ret; - npy_intp idim, ndim, shape[NPY_MAXDIMS]; + npy_intp ndim, shape[NPY_MAXDIMS]; if (self->iter == NULL || self->finished) { PyErr_SetString(PyExc_ValueError, @@ -1554,23 +1530,16 @@ static PyObject *npyiter_shape_get(NewNpyArrayIterObject *self) if (NpyIter_GetShape(self->iter, shape) == NPY_SUCCEED) { ndim = NpyIter_GetNDim(self->iter); - ret = PyTuple_New(ndim); - if (ret != NULL) { - for (idim = 0; idim < ndim; ++idim) { - PyTuple_SET_ITEM(ret, idim, - PyInt_FromLong(shape[idim])); - } - return ret; - } + return PyArray_IntTupleFromIntp(ndim, shape); } return NULL; } -static PyObject *npyiter_multi_index_get(NewNpyArrayIterObject *self) +static PyObject * +npyiter_multi_index_get(NewNpyArrayIterObject *self, void *NPY_UNUSED(ignored)) { - PyObject *ret; - npy_intp idim, ndim, multi_index[NPY_MAXDIMS]; + npy_intp ndim, multi_index[NPY_MAXDIMS]; if (self->iter == NULL || self->finished) { PyErr_SetString(PyExc_ValueError, @@ -1581,15 +1550,7 @@ static PyObject *npyiter_multi_index_get(NewNpyArrayIterObject *self) if (self->get_multi_index != NULL) { ndim = NpyIter_GetNDim(self->iter); self->get_multi_index(self->iter, multi_index); - ret = PyTuple_New(ndim); - if (ret == NULL) { - return NULL; - } - for (idim = 0; idim < ndim; ++idim) { - PyTuple_SET_ITEM(ret, idim, - PyInt_FromLong(multi_index[idim])); - } - return ret; + return PyArray_IntTupleFromIntp(ndim, multi_index); } else { if (!NpyIter_HasMultiIndex(self->iter)) { @@ -1612,7 +1573,8 @@ static PyObject *npyiter_multi_index_get(NewNpyArrayIterObject *self) } static int -npyiter_multi_index_set(NewNpyArrayIterObject *self, PyObject *value) +npyiter_multi_index_set( + NewNpyArrayIterObject *self, PyObject *value, void *NPY_UNUSED(ignored)) { npy_intp idim, ndim, multi_index[NPY_MAXDIMS]; @@ -1641,9 +1603,9 @@ npyiter_multi_index_set(NewNpyArrayIterObject *self, PyObject *value) } for (idim = 0; idim < ndim; ++idim) { PyObject *v = PySequence_GetItem(value, idim); - multi_index[idim] = PyInt_AsLong(v); + multi_index[idim] = PyLong_AsLong(v); + Py_DECREF(v); if (error_converting(multi_index[idim])) { - Py_XDECREF(v); return -1; } } @@ -1667,7 +1629,8 @@ npyiter_multi_index_set(NewNpyArrayIterObject *self, PyObject *value) } } -static PyObject *npyiter_index_get(NewNpyArrayIterObject *self) +static PyObject * +npyiter_index_get(NewNpyArrayIterObject *self, void *NPY_UNUSED(ignored)) { if (self->iter == NULL || self->finished) { PyErr_SetString(PyExc_ValueError, @@ -1677,7 +1640,7 @@ static PyObject *npyiter_index_get(NewNpyArrayIterObject *self) if (NpyIter_HasIndex(self->iter)) { npy_intp ind = *NpyIter_GetIndexPtr(self->iter); - return PyInt_FromLong(ind); + return PyLong_FromLong(ind); } else { PyErr_SetString(PyExc_ValueError, @@ -1686,7 +1649,9 @@ static PyObject *npyiter_index_get(NewNpyArrayIterObject *self) } } -static int npyiter_index_set(NewNpyArrayIterObject *self, PyObject *value) +static int +npyiter_index_set( + NewNpyArrayIterObject *self, PyObject *value, void *NPY_UNUSED(ignored)) { if (value == NULL) { PyErr_SetString(PyExc_AttributeError, @@ -1701,7 +1666,7 @@ static int npyiter_index_set(NewNpyArrayIterObject *self, PyObject *value) if (NpyIter_HasIndex(self->iter)) { npy_intp ind; - ind = PyInt_AsLong(value); + ind = PyLong_AsLong(value); if (error_converting(ind)) { return -1; } @@ -1725,7 +1690,8 @@ static int npyiter_index_set(NewNpyArrayIterObject *self, PyObject *value) } } -static PyObject *npyiter_iterindex_get(NewNpyArrayIterObject *self) +static PyObject * +npyiter_iterindex_get(NewNpyArrayIterObject *self, void *NPY_UNUSED(ignored)) { if (self->iter == NULL || self->finished) { PyErr_SetString(PyExc_ValueError, @@ -1733,10 +1699,12 @@ static PyObject *npyiter_iterindex_get(NewNpyArrayIterObject *self) return NULL; } - return PyInt_FromLong(NpyIter_GetIterIndex(self->iter)); + return PyLong_FromLong(NpyIter_GetIterIndex(self->iter)); } -static int npyiter_iterindex_set(NewNpyArrayIterObject *self, PyObject *value) +static int +npyiter_iterindex_set( + NewNpyArrayIterObject *self, PyObject *value, void *NPY_UNUSED(ignored)) { npy_intp iterindex; @@ -1751,7 +1719,7 @@ static int npyiter_iterindex_set(NewNpyArrayIterObject *self, PyObject *value) return -1; } - iterindex = PyInt_AsLong(value); + iterindex = PyLong_AsLong(value); if (error_converting(iterindex)) { return -1; } @@ -1769,7 +1737,8 @@ static int npyiter_iterindex_set(NewNpyArrayIterObject *self, PyObject *value) return 0; } -static PyObject *npyiter_iterrange_get(NewNpyArrayIterObject *self) +static PyObject * +npyiter_iterrange_get(NewNpyArrayIterObject *self, void *NPY_UNUSED(ignored)) { npy_intp istart = 0, iend = 0; PyObject *ret; @@ -1787,13 +1756,15 @@ static PyObject *npyiter_iterrange_get(NewNpyArrayIterObject *self) return NULL; } - PyTuple_SET_ITEM(ret, 0, PyInt_FromLong(istart)); - PyTuple_SET_ITEM(ret, 1, PyInt_FromLong(iend)); + PyTuple_SET_ITEM(ret, 0, PyLong_FromLong(istart)); + PyTuple_SET_ITEM(ret, 1, PyLong_FromLong(iend)); return ret; } -static int npyiter_iterrange_set(NewNpyArrayIterObject *self, PyObject *value) +static int +npyiter_iterrange_set( + NewNpyArrayIterObject *self, PyObject *value, void *NPY_UNUSED(ignored)) { npy_intp istart = 0, iend = 0; @@ -1835,7 +1806,9 @@ static int npyiter_iterrange_set(NewNpyArrayIterObject *self, PyObject *value) return 0; } -static PyObject *npyiter_has_delayed_bufalloc_get(NewNpyArrayIterObject *self) +static PyObject * +npyiter_has_delayed_bufalloc_get( + NewNpyArrayIterObject *self, void *NPY_UNUSED(ignored)) { if (self->iter == NULL) { PyErr_SetString(PyExc_ValueError, @@ -1851,7 +1824,9 @@ static PyObject *npyiter_has_delayed_bufalloc_get(NewNpyArrayIterObject *self) } } -static PyObject *npyiter_iterationneedsapi_get(NewNpyArrayIterObject *self) +static PyObject * +npyiter_iterationneedsapi_get( + NewNpyArrayIterObject *self, void *NPY_UNUSED(ignored)) { if (self->iter == NULL) { PyErr_SetString(PyExc_ValueError, @@ -1867,7 +1842,9 @@ static PyObject *npyiter_iterationneedsapi_get(NewNpyArrayIterObject *self) } } -static PyObject *npyiter_has_multi_index_get(NewNpyArrayIterObject *self) +static PyObject * +npyiter_has_multi_index_get( + NewNpyArrayIterObject *self, void *NPY_UNUSED(ignored)) { if (self->iter == NULL) { PyErr_SetString(PyExc_ValueError, @@ -1883,7 +1860,8 @@ static PyObject *npyiter_has_multi_index_get(NewNpyArrayIterObject *self) } } -static PyObject *npyiter_has_index_get(NewNpyArrayIterObject *self) +static PyObject * +npyiter_has_index_get(NewNpyArrayIterObject *self, void *NPY_UNUSED(ignored)) { if (self->iter == NULL) { PyErr_SetString(PyExc_ValueError, @@ -1899,7 +1877,8 @@ static PyObject *npyiter_has_index_get(NewNpyArrayIterObject *self) } } -static PyObject *npyiter_dtypes_get(NewNpyArrayIterObject *self) +static PyObject * +npyiter_dtypes_get(NewNpyArrayIterObject *self, void *NPY_UNUSED(ignored)) { PyObject *ret; @@ -1911,13 +1890,6 @@ static PyObject *npyiter_dtypes_get(NewNpyArrayIterObject *self) "Iterator is invalid"); return NULL; } - - if (self->is_closed) { - PyErr_SetString(PyExc_ValueError, - "Iterator is closed"); - return NULL; - } - nop = NpyIter_GetNOp(self->iter); ret = PyTuple_New(nop); @@ -1935,7 +1907,8 @@ static PyObject *npyiter_dtypes_get(NewNpyArrayIterObject *self) return ret; } -static PyObject *npyiter_ndim_get(NewNpyArrayIterObject *self) +static PyObject * +npyiter_ndim_get(NewNpyArrayIterObject *self, void *NPY_UNUSED(ignored)) { if (self->iter == NULL) { PyErr_SetString(PyExc_ValueError, @@ -1943,10 +1916,11 @@ static PyObject *npyiter_ndim_get(NewNpyArrayIterObject *self) return NULL; } - return PyInt_FromLong(NpyIter_GetNDim(self->iter)); + return PyLong_FromLong(NpyIter_GetNDim(self->iter)); } -static PyObject *npyiter_nop_get(NewNpyArrayIterObject *self) +static PyObject * +npyiter_nop_get(NewNpyArrayIterObject *self, void *NPY_UNUSED(ignored)) { if (self->iter == NULL) { PyErr_SetString(PyExc_ValueError, @@ -1954,10 +1928,11 @@ static PyObject *npyiter_nop_get(NewNpyArrayIterObject *self) return NULL; } - return PyInt_FromLong(NpyIter_GetNOp(self->iter)); + return PyLong_FromLong(NpyIter_GetNOp(self->iter)); } -static PyObject *npyiter_itersize_get(NewNpyArrayIterObject *self) +static PyObject * +npyiter_itersize_get(NewNpyArrayIterObject *self, void *NPY_UNUSED(ignored)) { if (self->iter == NULL) { PyErr_SetString(PyExc_ValueError, @@ -1965,10 +1940,11 @@ static PyObject *npyiter_itersize_get(NewNpyArrayIterObject *self) return NULL; } - return PyInt_FromLong(NpyIter_GetIterSize(self->iter)); + return PyLong_FromLong(NpyIter_GetIterSize(self->iter)); } -static PyObject *npyiter_finished_get(NewNpyArrayIterObject *self) +static PyObject * +npyiter_finished_get(NewNpyArrayIterObject *self, void *NPY_UNUSED(ignored)) { if (self->iter == NULL || !self->finished) { Py_RETURN_FALSE; @@ -2011,13 +1987,6 @@ npyiter_seq_item(NewNpyArrayIterObject *self, Py_ssize_t i) "and no reset has been done yet"); return NULL; } - - if (self->is_closed) { - PyErr_SetString(PyExc_ValueError, - "Iterator is closed"); - return NULL; - } - nop = NpyIter_GetNOp(self->iter); /* Negative indexing */ @@ -2027,7 +1996,7 @@ npyiter_seq_item(NewNpyArrayIterObject *self, Py_ssize_t i) if (i < 0 || i >= nop) { PyErr_Format(PyExc_IndexError, - "Iterator operand index %d is out of bounds", (int)i_orig); + "Iterator operand index %zd is out of bounds", i_orig); return NULL; } @@ -2041,7 +2010,7 @@ npyiter_seq_item(NewNpyArrayIterObject *self, Py_ssize_t i) */ if (!self->readflags[i]) { PyErr_Format(PyExc_RuntimeError, - "Iterator operand %d is write-only", (int)i); + "Iterator operand %zd is write-only", i); return NULL; } #endif @@ -2090,13 +2059,6 @@ npyiter_seq_slice(NewNpyArrayIterObject *self, "and no reset has been done yet"); return NULL; } - - if (self->is_closed) { - PyErr_SetString(PyExc_ValueError, - "Iterator is closed"); - return NULL; - } - nop = NpyIter_GetNOp(self->iter); if (ilow < 0) { ilow = 0; @@ -2156,13 +2118,6 @@ npyiter_seq_ass_item(NewNpyArrayIterObject *self, Py_ssize_t i, PyObject *v) "and no reset has been done yet"); return -1; } - - if (self->is_closed) { - PyErr_SetString(PyExc_ValueError, - "Iterator is closed"); - return -1; - } - nop = NpyIter_GetNOp(self->iter); /* Negative indexing */ @@ -2172,12 +2127,12 @@ npyiter_seq_ass_item(NewNpyArrayIterObject *self, Py_ssize_t i, PyObject *v) if (i < 0 || i >= nop) { PyErr_Format(PyExc_IndexError, - "Iterator operand index %d is out of bounds", (int)i_orig); + "Iterator operand index %zd is out of bounds", i_orig); return -1; } if (!self->writeflags[i]) { PyErr_Format(PyExc_RuntimeError, - "Iterator operand %d is not writeable", (int)i_orig); + "Iterator operand %zd is not writeable", i_orig); return -1; } @@ -2234,13 +2189,6 @@ npyiter_seq_ass_slice(NewNpyArrayIterObject *self, Py_ssize_t ilow, "and no reset has been done yet"); return -1; } - - if (self->is_closed) { - PyErr_SetString(PyExc_ValueError, - "Iterator is closed"); - return -1; - } - nop = NpyIter_GetNOp(self->iter); if (ilow < 0) { ilow = 0; @@ -2292,13 +2240,7 @@ npyiter_subscript(NewNpyArrayIterObject *self, PyObject *op) return NULL; } - if (self->is_closed) { - PyErr_SetString(PyExc_ValueError, - "Iterator is closed"); - return NULL; - } - - if (PyInt_Check(op) || PyLong_Check(op) || + if (PyLong_Check(op) || (PyIndex_Check(op) && !PySequence_Check(op))) { npy_intp i = PyArray_PyIntAsIntp(op); if (error_converting(i)) { @@ -2308,8 +2250,8 @@ npyiter_subscript(NewNpyArrayIterObject *self, PyObject *op) } else if (PySlice_Check(op)) { Py_ssize_t istart = 0, iend = 0, istep = 0, islicelength; - if (NpySlice_GetIndicesEx(op, NpyIter_GetNOp(self->iter), - &istart, &iend, &istep, &islicelength) < 0) { + if (PySlice_GetIndicesEx(op, NpyIter_GetNOp(self->iter), + &istart, &iend, &istep, &islicelength) < 0) { return NULL; } if (istep != 1) { @@ -2347,13 +2289,7 @@ npyiter_ass_subscript(NewNpyArrayIterObject *self, PyObject *op, return -1; } - if (self->is_closed) { - PyErr_SetString(PyExc_ValueError, - "Iterator is closed"); - return -1; - } - - if (PyInt_Check(op) || PyLong_Check(op) || + if (PyLong_Check(op) || (PyIndex_Check(op) && !PySequence_Check(op))) { npy_intp i = PyArray_PyIntAsIntp(op); if (error_converting(i)) { @@ -2363,8 +2299,8 @@ npyiter_ass_subscript(NewNpyArrayIterObject *self, PyObject *op, } else if (PySlice_Check(op)) { Py_ssize_t istart = 0, iend = 0, istep = 0, islicelength = 0; - if (NpySlice_GetIndicesEx(op, NpyIter_GetNOp(self->iter), - &istart, &iend, &istep, &islicelength) < 0) { + if (PySlice_GetIndicesEx(op, NpyIter_GetNOp(self->iter), + &istart, &iend, &istep, &islicelength) < 0) { return -1; } if (istep != 1) { @@ -2381,41 +2317,39 @@ npyiter_ass_subscript(NewNpyArrayIterObject *self, PyObject *op, } static PyObject * -npyiter_enter(NewNpyArrayIterObject *self) +npyiter_enter(NewNpyArrayIterObject *self, PyObject *NPY_UNUSED(args)) { if (self->iter == NULL) { PyErr_SetString(PyExc_RuntimeError, "operation on non-initialized iterator"); return NULL; } - if (self->is_closed) { - PyErr_SetString(PyExc_ValueError, "cannot reuse closed iterator"); - return NULL; - } Py_INCREF(self); return (PyObject *)self; } static PyObject * -npyiter_close(NewNpyArrayIterObject *self) +npyiter_close(NewNpyArrayIterObject *self, PyObject *NPY_UNUSED(args)) { NpyIter *iter = self->iter; int ret; if (self->iter == NULL) { Py_RETURN_NONE; } - ret = NpyIter_Close(iter); - self->is_closed = 1; - if (ret < 0) { + ret = NpyIter_Deallocate(iter); + self->iter = NULL; + Py_XDECREF(self->nested_child); + self->nested_child = NULL; + if (ret != NPY_SUCCEED) { return NULL; } Py_RETURN_NONE; } static PyObject * -npyiter_exit(NewNpyArrayIterObject *self, PyObject *args) +npyiter_exit(NewNpyArrayIterObject *self, PyObject *NPY_UNUSED(args)) { /* even if called via exception handling, writeback any data */ - return npyiter_close(self); + return npyiter_close(self, NULL); } static PyMethodDef npyiter_methods[] = { @@ -2448,7 +2382,7 @@ static PyMethodDef npyiter_methods[] = { {"__exit__", (PyCFunction)npyiter_exit, METH_VARARGS, NULL}, {"close", (PyCFunction)npyiter_close, - METH_VARARGS, NULL}, + METH_NOARGS, NULL}, {NULL, NULL, 0, NULL}, }; @@ -2536,61 +2470,17 @@ NPY_NO_EXPORT PyMappingMethods npyiter_as_mapping = { }; NPY_NO_EXPORT PyTypeObject NpyIter_Type = { -#if defined(NPY_PY3K) PyVarObject_HEAD_INIT(NULL, 0) -#else - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ -#endif - "numpy.nditer", /* tp_name */ - sizeof(NewNpyArrayIterObject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)npyiter_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ -#if defined(NPY_PY3K) - 0, /* tp_reserved */ -#else - 0, /* tp_compare */ -#endif - 0, /* tp_repr */ - 0, /* tp_as_number */ - &npyiter_as_sequence, /* tp_as_sequence */ - &npyiter_as_mapping, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - 0, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - (iternextfunc)npyiter_next, /* tp_iternext */ - npyiter_methods, /* tp_methods */ - npyiter_members, /* tp_members */ - npyiter_getsets, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)npyiter_init, /* tp_init */ - 0, /* tp_alloc */ - npyiter_new, /* tp_new */ - 0, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - 0, /* tp_del */ - 0, /* tp_version_tag */ + .tp_name = "numpy.nditer", + .tp_basicsize = sizeof(NewNpyArrayIterObject), + .tp_dealloc = (destructor)npyiter_dealloc, + .tp_as_sequence = &npyiter_as_sequence, + .tp_as_mapping = &npyiter_as_mapping, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_iternext = (iternextfunc)npyiter_next, + .tp_methods = npyiter_methods, + .tp_members = npyiter_members, + .tp_getset = npyiter_getsets, + .tp_init = (initproc)npyiter_init, + .tp_new = npyiter_new, }; diff --git a/numpy/core/src/multiarray/nditer_pywrap.h b/numpy/core/src/multiarray/nditer_pywrap.h index 49eb5d89de00..d2fcafebd94f 100644 --- a/numpy/core/src/multiarray/nditer_pywrap.h +++ b/numpy/core/src/multiarray/nditer_pywrap.h @@ -1,8 +1,8 @@ -#ifndef __NDITER_PYWRAP_H -#define __NDITER_PYWRAP_H +#ifndef NUMPY_CORE_SRC_MULTIARRAY_NDITER_PYWRAP_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_NDITER_PYWRAP_H_ NPY_NO_EXPORT PyObject * NpyIter_NestedIters(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds); -#endif +#endif /* NUMPY_CORE_SRC_MULTIARRAY_NDITER_PYWRAP_H_ */ diff --git a/numpy/core/src/multiarray/nditer_templ.c.src b/numpy/core/src/multiarray/nditer_templ.c.src index 0f0d59972305..3f91a482b461 100644 --- a/numpy/core/src/multiarray/nditer_templ.c.src +++ b/numpy/core/src/multiarray/nditer_templ.c.src @@ -132,7 +132,7 @@ npyiter_iternext_itflags@tag_itflags@_dims@tag_ndim@_iters@tag_nop@( /* Reset the 1st and 2nd indices to 0 */ NAD_INDEX(axisdata0) = 0; NAD_INDEX(axisdata1) = 0; - /* Reset the 1st and 2nd pointers to the value of the 3nd */ + /* Reset the 1st and 2nd pointers to the value of the 3rd */ for (istrides = 0; istrides < nstrides; ++istrides) { NAD_PTRS(axisdata0)[istrides] = NAD_PTRS(axisdata2)[istrides]; NAD_PTRS(axisdata1)[istrides] = NAD_PTRS(axisdata2)[istrides]; @@ -249,7 +249,10 @@ npyiter_buffered_reduce_iternext_iters@tag_nop@(NpyIter *iter) memcpy(prev_dataptrs, NAD_PTRS(axisdata), NPY_SIZEOF_INTP*nop); /* Write back to the arrays */ - npyiter_copy_from_buffers(iter); + if (npyiter_copy_from_buffers(iter) < 0) { + npyiter_clear_buffers(iter); + return 0; + } /* Check if we're past the end */ if (NIT_ITERINDEX(iter) >= NIT_ITEREND(iter)) { @@ -262,7 +265,10 @@ npyiter_buffered_reduce_iternext_iters@tag_nop@(NpyIter *iter) } /* Prepare the next buffers and set iterend/size */ - npyiter_copy_to_buffers(iter, prev_dataptrs); + if (npyiter_copy_to_buffers(iter, prev_dataptrs) < 0) { + npyiter_clear_buffers(iter); + return 0; + } return 1; } @@ -303,7 +309,10 @@ npyiter_buffered_iternext(NpyIter *iter) } /* Write back to the arrays */ - npyiter_copy_from_buffers(iter); + if (npyiter_copy_from_buffers(iter) < 0) { + npyiter_clear_buffers(iter); + return 0; + } /* Check if we're past the end */ if (NIT_ITERINDEX(iter) >= NIT_ITEREND(iter)) { @@ -316,7 +325,10 @@ npyiter_buffered_iternext(NpyIter *iter) } /* Prepare the next buffers and set iterend/size */ - npyiter_copy_to_buffers(iter, NULL); + if (npyiter_copy_to_buffers(iter, NULL) < 0) { + npyiter_clear_buffers(iter); + return 0; + } return 1; } diff --git a/numpy/core/src/multiarray/npy_buffer.h b/numpy/core/src/multiarray/npy_buffer.h new file mode 100644 index 000000000000..62e08573c74d --- /dev/null +++ b/numpy/core/src/multiarray/npy_buffer.h @@ -0,0 +1,15 @@ +#ifndef NUMPY_CORE_SRC_MULTIARRAY_NPY_BUFFER_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_NPY_BUFFER_H_ + +extern NPY_NO_EXPORT PyBufferProcs array_as_buffer; + +NPY_NO_EXPORT int +_buffer_info_free(void *buffer_info, PyObject *obj); + +NPY_NO_EXPORT PyArray_Descr* +_descriptor_from_pep3118_format(char const *s); + +NPY_NO_EXPORT int +void_getbuffer(PyObject *obj, Py_buffer *view, int flags); + +#endif /* NUMPY_CORE_SRC_MULTIARRAY_NPY_BUFFER_H_ */ diff --git a/numpy/core/src/multiarray/number.c b/numpy/core/src/multiarray/number.c index 448d2d9c2d4e..292ef55a630f 100644 --- a/numpy/core/src/multiarray/number.c +++ b/numpy/core/src/multiarray/number.c @@ -1,10 +1,10 @@ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE + #define PY_SSIZE_T_CLEAN #include <Python.h> -#include "structmember.h" +#include <structmember.h> -/*#include <stdio.h>*/ -#define NPY_NO_DEPRECATED_API NPY_API_VERSION -#define _MULTIARRAYMODULE #include "numpy/arrayobject.h" #include "npy_config.h" @@ -15,6 +15,10 @@ #include "temp_elide.h" #include "binop_override.h" +#include "ufunc_override.h" +#include "abstractdtypes.h" +#include "common_dtype.h" +#include "convert_datatype.h" /************************************************************************* **************** Implement Number Protocol **************************** @@ -31,10 +35,6 @@ static PyObject * array_inplace_subtract(PyArrayObject *m1, PyObject *m2); static PyObject * array_inplace_multiply(PyArrayObject *m1, PyObject *m2); -#if !defined(NPY_PY3K) -static PyObject * -array_inplace_divide(PyArrayObject *m1, PyObject *m2); -#endif static PyObject * array_inplace_true_divide(PyArrayObject *m1, PyObject *m2); static PyObject * @@ -60,8 +60,11 @@ array_inplace_power(PyArrayObject *a1, PyObject *o2, PyObject *NPY_UNUSED(modulo */ /* FIXME - macro contains a return */ -#define SET(op) temp = PyDict_GetItemString(dict, #op); \ - if (temp != NULL) { \ +#define SET(op) temp = _PyDict_GetItemStringWithError(dict, #op); \ + if (temp == NULL && PyErr_Occurred()) { \ + return -1; \ + } \ + else if (temp != NULL) { \ if (!(PyCallable_Check(temp))) { \ return -1; \ } \ @@ -70,12 +73,8 @@ array_inplace_power(PyArrayObject *a1, PyObject *o2, PyObject *NPY_UNUSED(modulo n_ops.op = temp; \ } - -/*NUMPY_API - *Set internal structure with number functions that all arrays will use - */ NPY_NO_EXPORT int -PyArray_SetNumericOps(PyObject *dict) +_PyArray_SetNumericOps(PyObject *dict) { PyObject *temp = NULL; SET(add); @@ -115,19 +114,33 @@ PyArray_SetNumericOps(PyObject *dict) SET(minimum); SET(rint); SET(conjugate); + SET(matmul); + SET(clip); return 0; } -/* FIXME - macro contains goto */ +/*NUMPY_API + *Set internal structure with number functions that all arrays will use + */ +NPY_NO_EXPORT int +PyArray_SetNumericOps(PyObject *dict) +{ + /* 2018-09-09, 1.16 */ + if (DEPRECATE("PyArray_SetNumericOps is deprecated. Use " + "PyUFunc_ReplaceLoopBySignature to replace ufunc inner loop functions " + "instead.") < 0) { + return -1; + } + return _PyArray_SetNumericOps(dict); +} + +/* Note - macro contains goto */ #define GET(op) if (n_ops.op && \ (PyDict_SetItemString(dict, #op, n_ops.op)==-1)) \ goto fail; -/*NUMPY_API - Get dictionary showing number functions that all arrays will use -*/ NPY_NO_EXPORT PyObject * -PyArray_GetNumericOps(void) +_PyArray_GetNumericOps(void) { PyObject *dict; if ((dict = PyDict_New())==NULL) @@ -168,6 +181,8 @@ PyArray_GetNumericOps(void) GET(minimum); GET(rint); GET(conjugate); + GET(matmul); + GET(clip); return dict; fail: @@ -175,6 +190,19 @@ PyArray_GetNumericOps(void) return NULL; } +/*NUMPY_API + Get dictionary showing number functions that all arrays will use +*/ +NPY_NO_EXPORT PyObject * +PyArray_GetNumericOps(void) +{ + /* 2018-09-09, 1.16 */ + if (DEPRECATE("PyArray_GetNumericOps is deprecated.") < 0) { + return NULL; + } + return _PyArray_GetNumericOps(); +} + static PyObject * _get_keywords(int rtype, PyArrayObject *out) { @@ -202,10 +230,7 @@ PyArray_GenericReduceFunction(PyArrayObject *m1, PyObject *op, int axis, { PyObject *args, *ret = NULL, *meth; PyObject *kwds; - if (op == NULL) { - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } + args = Py_BuildValue("(Oi)", m1, axis); kwds = _get_keywords(rtype, out); meth = PyObject_GetAttrString(op, "reduce"); @@ -225,10 +250,7 @@ PyArray_GenericAccumulateFunction(PyArrayObject *m1, PyObject *op, int axis, { PyObject *args, *ret = NULL, *meth; PyObject *kwds; - if (op == NULL) { - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } + args = Py_BuildValue("(Oi)", m1, axis); kwds = _get_keywords(rtype, out); meth = PyObject_GetAttrString(op, "accumulate"); @@ -243,30 +265,14 @@ PyArray_GenericAccumulateFunction(PyArrayObject *m1, PyObject *op, int axis, NPY_NO_EXPORT PyObject * -PyArray_GenericBinaryFunction(PyArrayObject *m1, PyObject *m2, PyObject *op) +PyArray_GenericBinaryFunction(PyObject *m1, PyObject *m2, PyObject *op) { - /* - * I suspect that the next few lines are buggy and cause NotImplemented to - * be returned at weird times... but if we raise an error here, then - * *everything* breaks. (Like, 'arange(10) + 1' and just - * 'repr(arange(10))' both blow up with an error here.) Not sure what's - * going on with that, but I'll leave it alone for now. - njs, 2015-06-21 - */ - if (op == NULL) { - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } - return PyObject_CallFunctionObjArgs(op, m1, m2, NULL); } NPY_NO_EXPORT PyObject * PyArray_GenericUnaryFunction(PyArrayObject *m1, PyObject *op) { - if (op == NULL) { - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } return PyObject_CallFunctionObjArgs(op, m1, NULL); } @@ -274,25 +280,17 @@ static PyObject * PyArray_GenericInplaceBinaryFunction(PyArrayObject *m1, PyObject *m2, PyObject *op) { - if (op == NULL) { - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } return PyObject_CallFunctionObjArgs(op, m1, m2, m1, NULL); } static PyObject * PyArray_GenericInplaceUnaryFunction(PyArrayObject *m1, PyObject *op) { - if (op == NULL) { - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } return PyObject_CallFunctionObjArgs(op, m1, m1, NULL); } static PyObject * -array_add(PyArrayObject *m1, PyObject *m2) +array_add(PyObject *m1, PyObject *m2) { PyObject *res; @@ -304,7 +302,7 @@ array_add(PyArrayObject *m1, PyObject *m2) } static PyObject * -array_subtract(PyArrayObject *m1, PyObject *m2) +array_subtract(PyObject *m1, PyObject *m2) { PyObject *res; @@ -316,7 +314,7 @@ array_subtract(PyArrayObject *m1, PyObject *m2) } static PyObject * -array_multiply(PyArrayObject *m1, PyObject *m2) +array_multiply(PyObject *m1, PyObject *m2) { PyObject *res; @@ -327,58 +325,37 @@ array_multiply(PyArrayObject *m1, PyObject *m2) return PyArray_GenericBinaryFunction(m1, m2, n_ops.multiply); } -#if !defined(NPY_PY3K) -static PyObject * -array_divide(PyArrayObject *m1, PyObject *m2) -{ - PyObject *res; - - BINOP_GIVE_UP_IF_NEEDED(m1, m2, nb_divide, array_divide); - if (try_binary_elide(m1, m2, &array_inplace_divide, &res, 0)) { - return res; - } - return PyArray_GenericBinaryFunction(m1, m2, n_ops.divide); -} -#endif - static PyObject * -array_remainder(PyArrayObject *m1, PyObject *m2) +array_remainder(PyObject *m1, PyObject *m2) { BINOP_GIVE_UP_IF_NEEDED(m1, m2, nb_remainder, array_remainder); return PyArray_GenericBinaryFunction(m1, m2, n_ops.remainder); } static PyObject * -array_divmod(PyArrayObject *m1, PyObject *m2) +array_divmod(PyObject *m1, PyObject *m2) { BINOP_GIVE_UP_IF_NEEDED(m1, m2, nb_divmod, array_divmod); return PyArray_GenericBinaryFunction(m1, m2, n_ops.divmod); } -#if PY_VERSION_HEX >= 0x03050000 /* Need this to be version dependent on account of the slot check */ static PyObject * -array_matrix_multiply(PyArrayObject *m1, PyObject *m2) +array_matrix_multiply(PyObject *m1, PyObject *m2) { - static PyObject *matmul = NULL; - - npy_cache_import("numpy.core.multiarray", "matmul", &matmul); - if (matmul == NULL) { - return NULL; - } BINOP_GIVE_UP_IF_NEEDED(m1, m2, nb_matrix_multiply, array_matrix_multiply); - return PyArray_GenericBinaryFunction(m1, m2, matmul); + return PyArray_GenericBinaryFunction(m1, m2, n_ops.matmul); } static PyObject * -array_inplace_matrix_multiply(PyArrayObject *m1, PyObject *m2) +array_inplace_matrix_multiply( + PyArrayObject *NPY_UNUSED(m1), PyObject *NPY_UNUSED(m2)) { PyErr_SetString(PyExc_TypeError, "In-place matrix multiplication is not (yet) supported. " "Use 'a = a @ b' instead of 'a @= b'."); return NULL; } -#endif /* * Determine if object is a scalar and if so, convert the object @@ -393,14 +370,21 @@ is_scalar_with_conversion(PyObject *o2, double* out_exponent) PyObject *temp; const int optimize_fpexps = 1; - if (PyInt_Check(o2)) { - *out_exponent = (double)PyInt_AsLong(o2); + if (PyLong_Check(o2)) { + long tmp = PyLong_AsLong(o2); + if (error_converting(tmp)) { + PyErr_Clear(); + return NPY_NOSCALAR; + } + *out_exponent = (double)tmp; return NPY_INTPOS_SCALAR; } + if (optimize_fpexps && PyFloat_Check(o2)) { *out_exponent = PyFloat_AsDouble(o2); return NPY_FLOAT_SCALAR; } + if (PyArray_Check(o2)) { if ((PyArray_NDIM((PyArrayObject *)o2) == 0) && ((PyArray_ISINTEGER((PyArrayObject *)o2) || @@ -438,13 +422,14 @@ is_scalar_with_conversion(PyObject *o2, double* out_exponent) else if (PyIndex_Check(o2)) { PyObject* value = PyNumber_Index(o2); Py_ssize_t val; - if (value==NULL) { + if (value == NULL) { if (PyErr_Occurred()) { PyErr_Clear(); } return NPY_NOSCALAR; } - val = PyInt_AsSsize_t(value); + val = PyLong_AsSsize_t(value); + Py_DECREF(value); if (error_converting(val)) { PyErr_Clear(); return NPY_NOSCALAR; @@ -461,15 +446,16 @@ is_scalar_with_conversion(PyObject *o2, double* out_exponent) * the result is in value (can be NULL if an error occurred) */ static int -fast_scalar_power(PyArrayObject *a1, PyObject *o2, int inplace, +fast_scalar_power(PyObject *o1, PyObject *o2, int inplace, PyObject **value) { double exponent; NPY_SCALARKIND kind; /* NPY_NOSCALAR is not scalar */ - if (PyArray_Check(a1) && - !PyArray_ISOBJECT(a1) && + if (PyArray_Check(o1) && + !PyArray_ISOBJECT((PyArrayObject *)o1) && ((kind=is_scalar_with_conversion(o2, &exponent))>0)) { + PyArrayObject *a1 = (PyArrayObject *)o1; PyObject *fastop = NULL; if (PyArray_ISFLOAT(a1) || PyArray_ISCOMPLEX(a1)) { if (exponent == 1.0) { @@ -533,7 +519,7 @@ fast_scalar_power(PyArrayObject *a1, PyObject *o2, int inplace, } static PyObject * -array_power(PyArrayObject *a1, PyObject *o2, PyObject *modulo) +array_power(PyObject *a1, PyObject *o2, PyObject *modulo) { PyObject *value = NULL; @@ -550,6 +536,51 @@ array_power(PyArrayObject *a1, PyObject *o2, PyObject *modulo) return value; } +static PyObject * +array_positive(PyArrayObject *m1) +{ + /* + * For backwards compatibility, where + just implied a copy, + * we cannot just call n_ops.positive. Instead, we do the following + * 1. Try n_ops.positive + * 2. If we get an exception, check whether __array_ufunc__ is + * overridden; if so, we live in the future and we allow the + * TypeError to be passed on. + * 3. If not, give a deprecation warning and return a copy. + */ + PyObject *value; + if (can_elide_temp_unary(m1)) { + value = PyArray_GenericInplaceUnaryFunction(m1, n_ops.positive); + } + else { + value = PyArray_GenericUnaryFunction(m1, n_ops.positive); + } + if (value == NULL) { + /* + * We first fetch the error, as it needs to be clear to check + * for the override. When the deprecation is removed, + * this whole stanza can be deleted. + */ + PyObject *exc, *val, *tb; + PyErr_Fetch(&exc, &val, &tb); + if (PyUFunc_HasOverride((PyObject *)m1)) { + PyErr_Restore(exc, val, tb); + return NULL; + } + Py_XDECREF(exc); + Py_XDECREF(val); + Py_XDECREF(tb); + + /* 2018-06-28, 1.16.0 */ + if (DEPRECATE("Applying '+' to a non-numerical array is " + "ill-defined. Returning a copy, but in the future " + "this will error.") < 0) { + return NULL; + } + value = PyArray_Return((PyArrayObject *)PyArray_Copy(m1)); + } + return value; +} static PyObject * array_negative(PyArrayObject *m1) @@ -579,7 +610,7 @@ array_invert(PyArrayObject *m1) } static PyObject * -array_left_shift(PyArrayObject *m1, PyObject *m2) +array_left_shift(PyObject *m1, PyObject *m2) { PyObject *res; @@ -591,7 +622,7 @@ array_left_shift(PyArrayObject *m1, PyObject *m2) } static PyObject * -array_right_shift(PyArrayObject *m1, PyObject *m2) +array_right_shift(PyObject *m1, PyObject *m2) { PyObject *res; @@ -603,7 +634,7 @@ array_right_shift(PyArrayObject *m1, PyObject *m2) } static PyObject * -array_bitwise_and(PyArrayObject *m1, PyObject *m2) +array_bitwise_and(PyObject *m1, PyObject *m2) { PyObject *res; @@ -615,7 +646,7 @@ array_bitwise_and(PyArrayObject *m1, PyObject *m2) } static PyObject * -array_bitwise_or(PyArrayObject *m1, PyObject *m2) +array_bitwise_or(PyObject *m1, PyObject *m2) { PyObject *res; @@ -627,7 +658,7 @@ array_bitwise_or(PyArrayObject *m1, PyObject *m2) } static PyObject * -array_bitwise_xor(PyArrayObject *m1, PyObject *m2) +array_bitwise_xor(PyObject *m1, PyObject *m2) { PyObject *res; @@ -662,16 +693,6 @@ array_inplace_multiply(PyArrayObject *m1, PyObject *m2) return PyArray_GenericInplaceBinaryFunction(m1, m2, n_ops.multiply); } -#if !defined(NPY_PY3K) -static PyObject * -array_inplace_divide(PyArrayObject *m1, PyObject *m2) -{ - INPLACE_GIVE_UP_IF_NEEDED( - m1, m2, nb_inplace_divide, array_inplace_divide); - return PyArray_GenericInplaceBinaryFunction(m1, m2, n_ops.divide); -} -#endif - static PyObject * array_inplace_remainder(PyArrayObject *m1, PyObject *m2) { @@ -688,7 +709,7 @@ array_inplace_power(PyArrayObject *a1, PyObject *o2, PyObject *NPY_UNUSED(modulo INPLACE_GIVE_UP_IF_NEEDED( a1, o2, nb_inplace_power, array_inplace_power); - if (fast_scalar_power(a1, o2, 1, &value) != 0) { + if (fast_scalar_power((PyObject *)a1, o2, 1, &value) != 0) { value = PyArray_GenericInplaceBinaryFunction(a1, o2, n_ops.power); } return value; @@ -735,7 +756,7 @@ array_inplace_bitwise_xor(PyArrayObject *m1, PyObject *m2) } static PyObject * -array_floor_divide(PyArrayObject *m1, PyObject *m2) +array_floor_divide(PyObject *m1, PyObject *m2) { PyObject *res; @@ -747,13 +768,14 @@ array_floor_divide(PyArrayObject *m1, PyObject *m2) } static PyObject * -array_true_divide(PyArrayObject *m1, PyObject *m2) +array_true_divide(PyObject *m1, PyObject *m2) { PyObject *res; + PyArrayObject *a1 = (PyArrayObject *)m1; BINOP_GIVE_UP_IF_NEEDED(m1, m2, nb_true_divide, array_true_divide); if (PyArray_CheckExact(m1) && - (PyArray_ISFLOAT(m1) || PyArray_ISCOMPLEX(m1)) && + (PyArray_ISFLOAT(a1) || PyArray_ISCOMPLEX(a1)) && try_binary_elide(m1, m2, &array_inplace_true_divide, &res, 0)) { return res; } @@ -787,7 +809,7 @@ _array_nonzero(PyArrayObject *mp) n = PyArray_SIZE(mp); if (n == 1) { int res; - if (Npy_EnterRecursiveCall(" while converting array to bool")) { + if (Py_EnterRecursiveCall(" while converting array to bool")) { return -1; } res = PyArray_DESCR(mp)->f->nonzero(PyArray_DATA(mp), mp); @@ -841,7 +863,7 @@ array_scalar_forward(PyArrayObject *v, /* Need to guard against recursion if our array holds references */ if (PyDataType_REFCHK(PyArray_DESCR(v))) { PyObject *res; - if (Npy_EnterRecursiveCall(where) != 0) { + if (Py_EnterRecursiveCall(where) != 0) { Py_DECREF(scalar); return NULL; } @@ -865,73 +887,12 @@ array_float(PyArrayObject *v) return array_scalar_forward(v, &PyNumber_Float, " in ndarray.__float__"); } -#if defined(NPY_PY3K) - NPY_NO_EXPORT PyObject * array_int(PyArrayObject *v) { return array_scalar_forward(v, &PyNumber_Long, " in ndarray.__int__"); } -#else - -NPY_NO_EXPORT PyObject * -array_int(PyArrayObject *v) -{ - return array_scalar_forward(v, &PyNumber_Int, " in ndarray.__int__"); -} - -NPY_NO_EXPORT PyObject * -array_long(PyArrayObject *v) -{ - return array_scalar_forward(v, &PyNumber_Long, " in ndarray.__long__"); -} - -/* hex and oct aren't exposed to the C api, but we need a function pointer */ -static PyObject * -_PyNumber_Oct(PyObject *o) { - PyObject *res; - PyObject *mod = PyImport_ImportModule("__builtin__"); - if (mod == NULL) { - return NULL; - } - res = PyObject_CallMethod(mod, "oct", "(O)", o); - Py_DECREF(mod); - return res; -} - -static PyObject * -_PyNumber_Hex(PyObject *o) { - PyObject *res; - PyObject *mod = PyImport_ImportModule("__builtin__"); - if (mod == NULL) { - return NULL; - } - res = PyObject_CallMethod(mod, "hex", "(O)", o); - Py_DECREF(mod); - return res; -} - -NPY_NO_EXPORT PyObject * -array_oct(PyArrayObject *v) -{ - return array_scalar_forward(v, &_PyNumber_Oct, " in ndarray.__oct__"); -} - -NPY_NO_EXPORT PyObject * -array_hex(PyArrayObject *v) -{ - return array_scalar_forward(v, &_PyNumber_Hex, " in ndarray.__hex__"); -} - -#endif - -static PyObject * -_array_copy_nice(PyArrayObject *self) -{ - return PyArray_Return((PyArrayObject *) PyArray_Copy(self)); -} - static PyObject * array_index(PyArrayObject *v) { @@ -945,65 +906,43 @@ array_index(PyArrayObject *v) NPY_NO_EXPORT PyNumberMethods array_as_number = { - (binaryfunc)array_add, /*nb_add*/ - (binaryfunc)array_subtract, /*nb_subtract*/ - (binaryfunc)array_multiply, /*nb_multiply*/ -#if !defined(NPY_PY3K) - (binaryfunc)array_divide, /*nb_divide*/ -#endif - (binaryfunc)array_remainder, /*nb_remainder*/ - (binaryfunc)array_divmod, /*nb_divmod*/ - (ternaryfunc)array_power, /*nb_power*/ - (unaryfunc)array_negative, /*nb_neg*/ - (unaryfunc)_array_copy_nice, /*nb_pos*/ - (unaryfunc)array_absolute, /*(unaryfunc)array_abs,*/ - (inquiry)_array_nonzero, /*nb_nonzero*/ - (unaryfunc)array_invert, /*nb_invert*/ - (binaryfunc)array_left_shift, /*nb_lshift*/ - (binaryfunc)array_right_shift, /*nb_rshift*/ - (binaryfunc)array_bitwise_and, /*nb_and*/ - (binaryfunc)array_bitwise_xor, /*nb_xor*/ - (binaryfunc)array_bitwise_or, /*nb_or*/ -#if !defined(NPY_PY3K) - 0, /*nb_coerce*/ -#endif - (unaryfunc)array_int, /*nb_int*/ -#if defined(NPY_PY3K) - 0, /*nb_reserved*/ -#else - (unaryfunc)array_long, /*nb_long*/ -#endif - (unaryfunc)array_float, /*nb_float*/ -#if !defined(NPY_PY3K) - (unaryfunc)array_oct, /*nb_oct*/ - (unaryfunc)array_hex, /*nb_hex*/ -#endif - - /* - * This code adds augmented assignment functionality - * that was made available in Python 2.0 - */ - (binaryfunc)array_inplace_add, /*nb_inplace_add*/ - (binaryfunc)array_inplace_subtract, /*nb_inplace_subtract*/ - (binaryfunc)array_inplace_multiply, /*nb_inplace_multiply*/ -#if !defined(NPY_PY3K) - (binaryfunc)array_inplace_divide, /*nb_inplace_divide*/ -#endif - (binaryfunc)array_inplace_remainder, /*nb_inplace_remainder*/ - (ternaryfunc)array_inplace_power, /*nb_inplace_power*/ - (binaryfunc)array_inplace_left_shift, /*nb_inplace_lshift*/ - (binaryfunc)array_inplace_right_shift, /*nb_inplace_rshift*/ - (binaryfunc)array_inplace_bitwise_and, /*nb_inplace_and*/ - (binaryfunc)array_inplace_bitwise_xor, /*nb_inplace_xor*/ - (binaryfunc)array_inplace_bitwise_or, /*nb_inplace_or*/ - - (binaryfunc)array_floor_divide, /*nb_floor_divide*/ - (binaryfunc)array_true_divide, /*nb_true_divide*/ - (binaryfunc)array_inplace_floor_divide, /*nb_inplace_floor_divide*/ - (binaryfunc)array_inplace_true_divide, /*nb_inplace_true_divide*/ - (unaryfunc)array_index, /*nb_index */ -#if PY_VERSION_HEX >= 0x03050000 - (binaryfunc)array_matrix_multiply, /*nb_matrix_multiply*/ - (binaryfunc)array_inplace_matrix_multiply, /*nb_inplace_matrix_multiply*/ -#endif + .nb_add = array_add, + .nb_subtract = array_subtract, + .nb_multiply = array_multiply, + .nb_remainder = array_remainder, + .nb_divmod = array_divmod, + .nb_power = (ternaryfunc)array_power, + .nb_negative = (unaryfunc)array_negative, + .nb_positive = (unaryfunc)array_positive, + .nb_absolute = (unaryfunc)array_absolute, + .nb_bool = (inquiry)_array_nonzero, + .nb_invert = (unaryfunc)array_invert, + .nb_lshift = array_left_shift, + .nb_rshift = array_right_shift, + .nb_and = array_bitwise_and, + .nb_xor = array_bitwise_xor, + .nb_or = array_bitwise_or, + + .nb_int = (unaryfunc)array_int, + .nb_float = (unaryfunc)array_float, + .nb_index = (unaryfunc)array_index, + + .nb_inplace_add = (binaryfunc)array_inplace_add, + .nb_inplace_subtract = (binaryfunc)array_inplace_subtract, + .nb_inplace_multiply = (binaryfunc)array_inplace_multiply, + .nb_inplace_remainder = (binaryfunc)array_inplace_remainder, + .nb_inplace_power = (ternaryfunc)array_inplace_power, + .nb_inplace_lshift = (binaryfunc)array_inplace_left_shift, + .nb_inplace_rshift = (binaryfunc)array_inplace_right_shift, + .nb_inplace_and = (binaryfunc)array_inplace_bitwise_and, + .nb_inplace_xor = (binaryfunc)array_inplace_bitwise_xor, + .nb_inplace_or = (binaryfunc)array_inplace_bitwise_or, + + .nb_floor_divide = array_floor_divide, + .nb_true_divide = array_true_divide, + .nb_inplace_floor_divide = (binaryfunc)array_inplace_floor_divide, + .nb_inplace_true_divide = (binaryfunc)array_inplace_true_divide, + + .nb_matrix_multiply = array_matrix_multiply, + .nb_inplace_matrix_multiply = (binaryfunc)array_inplace_matrix_multiply, }; diff --git a/numpy/core/src/multiarray/number.h b/numpy/core/src/multiarray/number.h index 99a2a722b6b9..054840305103 100644 --- a/numpy/core/src/multiarray/number.h +++ b/numpy/core/src/multiarray/number.h @@ -1,5 +1,5 @@ -#ifndef _NPY_ARRAY_NUMBER_H_ -#define _NPY_ARRAY_NUMBER_H_ +#ifndef NUMPY_CORE_SRC_MULTIARRAY_NUMBER_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_NUMBER_H_ typedef struct { PyObject *add; @@ -39,6 +39,8 @@ typedef struct { PyObject *minimum; PyObject *rint; PyObject *conjugate; + PyObject *matmul; + PyObject *clip; } NumericOps; extern NPY_NO_EXPORT NumericOps n_ops; @@ -48,13 +50,13 @@ NPY_NO_EXPORT PyObject * array_int(PyArrayObject *v); NPY_NO_EXPORT int -PyArray_SetNumericOps(PyObject *dict); +_PyArray_SetNumericOps(PyObject *dict); NPY_NO_EXPORT PyObject * -PyArray_GetNumericOps(void); +_PyArray_GetNumericOps(void); NPY_NO_EXPORT PyObject * -PyArray_GenericBinaryFunction(PyArrayObject *m1, PyObject *m2, PyObject *op); +PyArray_GenericBinaryFunction(PyObject *m1, PyObject *m2, PyObject *op); NPY_NO_EXPORT PyObject * PyArray_GenericUnaryFunction(PyArrayObject *m1, PyObject *op); @@ -67,4 +69,4 @@ NPY_NO_EXPORT PyObject * PyArray_GenericAccumulateFunction(PyArrayObject *m1, PyObject *op, int axis, int rtype, PyArrayObject *out); -#endif +#endif /* NUMPY_CORE_SRC_MULTIARRAY_NUMBER_H_ */ diff --git a/numpy/core/src/multiarray/refcount.c b/numpy/core/src/multiarray/refcount.c index 4b018b056719..a1c310700fa9 100644 --- a/numpy/core/src/multiarray/refcount.c +++ b/numpy/core/src/multiarray/refcount.c @@ -2,15 +2,16 @@ * This module corresponds to the `Special functions for NPY_OBJECT` * section in the numpy reference for C-API. */ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE #define PY_SSIZE_T_CLEAN #include <Python.h> -#include "structmember.h" +#include <structmember.h> -#define NPY_NO_DEPRECATED_API NPY_API_VERSION -#define _MULTIARRAYMODULE #include "numpy/arrayobject.h" #include "numpy/arrayscalars.h" +#include "iterators.h" #include "npy_config.h" @@ -19,8 +20,12 @@ static void _fillobject(char *optr, PyObject *obj, PyArray_Descr *dtype); -/* Incref all objects found at this record */ + /*NUMPY_API + * XINCREF all objects in a single array item. This is complicated for + * structured datatypes where the position of objects needs to be extracted. + * The function is execute recursively for each nested field or subarrays dtype + * such as as `np.dtype([("field1", "O"), ("field2", "f,O", (3,2))])` */ NPY_NO_EXPORT void PyArray_Item_INCREF(char *data, PyArray_Descr *descr) @@ -31,7 +36,7 @@ PyArray_Item_INCREF(char *data, PyArray_Descr *descr) return; } if (descr->type_num == NPY_OBJECT) { - NPY_COPY_PYOBJECT_PTR(&temp, data); + memcpy(&temp, data, sizeof(temp)); Py_XINCREF(temp); } else if (PyDataType_HASFIELDS(descr)) { @@ -41,7 +46,7 @@ PyArray_Item_INCREF(char *data, PyArray_Descr *descr) Py_ssize_t pos = 0; while (PyDict_Next(descr->fields, &pos, &key, &value)) { - if NPY_TITLE_KEY(key, value) { + if (NPY_TITLE_KEY(key, value)) { continue; } if (!PyArg_ParseTuple(value, "Oi|O", &new, &offset, @@ -51,11 +56,37 @@ PyArray_Item_INCREF(char *data, PyArray_Descr *descr) PyArray_Item_INCREF(data + offset, new); } } + else if (PyDataType_HASSUBARRAY(descr)) { + int size, i, inner_elsize; + + inner_elsize = descr->subarray->base->elsize; + if (inner_elsize == 0) { + /* There cannot be any elements, so return */ + return; + } + /* Subarrays are always contiguous in memory */ + size = descr->elsize / inner_elsize; + + for (i = 0; i < size; i++){ + /* Recursively increment the reference count of subarray elements */ + PyArray_Item_INCREF(data + i * inner_elsize, + descr->subarray->base); + } + } + else { + /* This path should not be reachable. */ + assert(0); + } return; } -/* XDECREF all objects found at this record */ + /*NUMPY_API + * + * XDECREF all objects in a single array item. This is complicated for + * structured datatypes where the position of objects needs to be extracted. + * The function is execute recursively for each nested field or subarrays dtype + * such as as `np.dtype([("field1", "O"), ("field2", "f,O", (3,2))])` */ NPY_NO_EXPORT void PyArray_Item_XDECREF(char *data, PyArray_Descr *descr) @@ -67,7 +98,7 @@ PyArray_Item_XDECREF(char *data, PyArray_Descr *descr) } if (descr->type_num == NPY_OBJECT) { - NPY_COPY_PYOBJECT_PTR(&temp, data); + memcpy(&temp, data, sizeof(temp)); Py_XDECREF(temp); } else if (PyDataType_HASFIELDS(descr)) { @@ -77,7 +108,7 @@ PyArray_Item_XDECREF(char *data, PyArray_Descr *descr) Py_ssize_t pos = 0; while (PyDict_Next(descr->fields, &pos, &key, &value)) { - if NPY_TITLE_KEY(key, value) { + if (NPY_TITLE_KEY(key, value)) { continue; } if (!PyArg_ParseTuple(value, "Oi|O", &new, &offset, @@ -87,6 +118,27 @@ PyArray_Item_XDECREF(char *data, PyArray_Descr *descr) PyArray_Item_XDECREF(data + offset, new); } } + else if (PyDataType_HASSUBARRAY(descr)) { + int size, i, inner_elsize; + + inner_elsize = descr->subarray->base->elsize; + if (inner_elsize == 0) { + /* There cannot be any elements, so return */ + return; + } + /* Subarrays are always contiguous in memory */ + size = descr->elsize / inner_elsize; + + for (i = 0; i < size; i++){ + /* Recursively decrement the reference count of subarray elements */ + PyArray_Item_XDECREF(data + i * inner_elsize, + descr->subarray->base); + } + } + else { + /* This path should not be reachable. */ + assert(0); + } return; } @@ -129,7 +181,7 @@ PyArray_INCREF(PyArrayObject *mp) } else { for( i = 0; i < n; i++, data++) { - NPY_COPY_PYOBJECT_PTR(&temp, data); + memcpy(&temp, data, sizeof(temp)); Py_XINCREF(temp); } } @@ -140,7 +192,7 @@ PyArray_INCREF(PyArrayObject *mp) return -1; } while(it->index < it->size) { - NPY_COPY_PYOBJECT_PTR(&temp, it->dataptr); + memcpy(&temp, it->dataptr, sizeof(temp)); Py_XINCREF(temp); PyArray_ITER_NEXT(it); } @@ -159,21 +211,22 @@ PyArray_XDECREF(PyArrayObject *mp) npy_intp i, n; PyObject **data; PyObject *temp; - PyArrayIterObject *it; + /* + * statically allocating it allows this function to not modify the + * reference count of the array for use during dealloc. + * (statically is not necessary as such) + */ + PyArrayIterObject it; if (!PyDataType_REFCHK(PyArray_DESCR(mp))) { return 0; } if (PyArray_DESCR(mp)->type_num != NPY_OBJECT) { - it = (PyArrayIterObject *)PyArray_IterNew((PyObject *)mp); - if (it == NULL) { - return -1; - } - while(it->index < it->size) { - PyArray_Item_XDECREF(it->dataptr, PyArray_DESCR(mp)); - PyArray_ITER_NEXT(it); + PyArray_RawIterBaseInit(&it, mp); + while(it.index < it.size) { + PyArray_Item_XDECREF(it.dataptr, PyArray_DESCR(mp)); + PyArray_ITER_NEXT(&it); } - Py_DECREF(it); return 0; } @@ -185,22 +238,18 @@ PyArray_XDECREF(PyArrayObject *mp) } else { for (i = 0; i < n; i++, data++) { - NPY_COPY_PYOBJECT_PTR(&temp, data); + memcpy(&temp, data, sizeof(temp)); Py_XDECREF(temp); } } } else { /* handles misaligned data too */ - it = (PyArrayIterObject *)PyArray_IterNew((PyObject *)mp); - if (it == NULL) { - return -1; - } - while(it->index < it->size) { - NPY_COPY_PYOBJECT_PTR(&temp, it->dataptr); + PyArray_RawIterBaseInit(&it, mp); + while(it.index < it.size) { + memcpy(&temp, it.dataptr, sizeof(temp)); Py_XDECREF(temp); - PyArray_ITER_NEXT(it); + PyArray_ITER_NEXT(&it); } - Py_DECREF(it); } return 0; } @@ -243,20 +292,26 @@ static void _fillobject(char *optr, PyObject *obj, PyArray_Descr *dtype) { if (!PyDataType_FLAGCHK(dtype, NPY_ITEM_REFCOUNT)) { - if ((obj == Py_None) || (PyInt_Check(obj) && PyInt_AsLong(obj)==0)) { + PyObject *arr; + + if ((obj == Py_None) || + (PyLong_Check(obj) && PyLong_AsLong(obj) == 0)) { return; } - else { - PyObject *arr; - Py_INCREF(dtype); - arr = PyArray_NewFromDescr(&PyArray_Type, dtype, - 0, NULL, NULL, NULL, - 0, NULL); - if (arr!=NULL) { - dtype->f->setitem(obj, optr, arr); - } - Py_XDECREF(arr); + /* Clear possible long conversion error */ + PyErr_Clear(); + Py_INCREF(dtype); + arr = PyArray_NewFromDescr(&PyArray_Type, dtype, + 0, NULL, NULL, NULL, + 0, NULL); + if (arr!=NULL) { + dtype->f->setitem(obj, optr, arr); } + Py_XDECREF(arr); + } + if (dtype->type_num == NPY_OBJECT) { + Py_XINCREF(obj); + memcpy(optr, &obj, sizeof(obj)); } else if (PyDataType_HASFIELDS(dtype)) { PyObject *key, *value, *title = NULL; @@ -265,7 +320,7 @@ _fillobject(char *optr, PyObject *obj, PyArray_Descr *dtype) Py_ssize_t pos = 0; while (PyDict_Next(dtype->fields, &pos, &key, &value)) { - if NPY_TITLE_KEY(key, value) { + if (NPY_TITLE_KEY(key, value)) { continue; } if (!PyArg_ParseTuple(value, "Oi|O", &new, &offset, &title)) { @@ -274,15 +329,26 @@ _fillobject(char *optr, PyObject *obj, PyArray_Descr *dtype) _fillobject(optr + offset, obj, new); } } - else { - npy_intp i; - npy_intp nsize = dtype->elsize / sizeof(obj); + else if (PyDataType_HASSUBARRAY(dtype)) { + int size, i, inner_elsize; + + inner_elsize = dtype->subarray->base->elsize; + if (inner_elsize == 0) { + /* There cannot be any elements, so return */ + return; + } + /* Subarrays are always contiguous in memory */ + size = dtype->elsize / inner_elsize; - for (i = 0; i < nsize; i++) { - Py_XINCREF(obj); - NPY_COPY_PYOBJECT_PTR(optr, &obj); - optr += sizeof(obj); + /* Call _fillobject on each item recursively. */ + for (i = 0; i < size; i++){ + _fillobject(optr, obj, dtype->subarray->base); + optr += inner_elsize; } - return; } + else { + /* This path should not be reachable. */ + assert(0); + } + return; } diff --git a/numpy/core/src/multiarray/refcount.h b/numpy/core/src/multiarray/refcount.h index 761d53dd0d79..959eef5bacfe 100644 --- a/numpy/core/src/multiarray/refcount.h +++ b/numpy/core/src/multiarray/refcount.h @@ -1,5 +1,5 @@ -#ifndef _NPY_PRIVATE_REFCOUNT_H_ -#define _NPY_PRIVATE_REFCOUNT_H_ +#ifndef NUMPY_CORE_SRC_MULTIARRAY_REFCOUNT_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_REFCOUNT_H_ NPY_NO_EXPORT void PyArray_Item_INCREF(char *data, PyArray_Descr *descr); @@ -16,4 +16,4 @@ PyArray_XDECREF(PyArrayObject *mp); NPY_NO_EXPORT void PyArray_FillObjectArray(PyArrayObject *arr, PyObject *obj); -#endif +#endif /* NUMPY_CORE_SRC_MULTIARRAY_REFCOUNT_H_ */ diff --git a/numpy/core/src/multiarray/scalarapi.c b/numpy/core/src/multiarray/scalarapi.c index 5ef6c0bbf473..564352f1fd3f 100644 --- a/numpy/core/src/multiarray/scalarapi.c +++ b/numpy/core/src/multiarray/scalarapi.c @@ -1,9 +1,10 @@ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE + #define PY_SSIZE_T_CLEAN #include <Python.h> -#include "structmember.h" +#include <structmember.h> -#define NPY_NO_DEPRECATED_API NPY_API_VERSION -#define _MULTIARRAYMODULE #include "numpy/arrayobject.h" #include "numpy/arrayscalars.h" @@ -35,7 +36,7 @@ scalar_value(PyObject *scalar, PyArray_Descr *descr) { int type_num; int align; - npy_intp memloc; + uintptr_t memloc; if (descr == NULL) { descr = PyArray_DescrFromScalar(scalar); type_num = descr->type_num; @@ -45,7 +46,7 @@ scalar_value(PyObject *scalar, PyArray_Descr *descr) type_num = descr->type_num; } switch (type_num) { -#define CASE(ut,lt) case NPY_##ut: return &(((Py##lt##ScalarObject *)scalar)->obval) +#define CASE(ut,lt) case NPY_##ut: return &PyArrayScalar_VAL(scalar, lt) CASE(BOOL, Bool); CASE(BYTE, Byte); CASE(UBYTE, UByte); @@ -69,11 +70,21 @@ scalar_value(PyObject *scalar, PyArray_Descr *descr) CASE(TIMEDELTA, Timedelta); #undef CASE case NPY_STRING: - return (void *)PyString_AS_STRING(scalar); + return (void *)PyBytes_AsString(scalar); case NPY_UNICODE: - return (void *)PyUnicode_AS_DATA(scalar); + /* lazy initialization, to reduce the memory used by string scalars */ + if (PyArrayScalar_VAL(scalar, Unicode) == NULL) { + Py_UCS4 *raw_data = PyUnicode_AsUCS4Copy(scalar); + if (raw_data == NULL) { + return NULL; + } + PyArrayScalar_VAL(scalar, Unicode) = raw_data; + return (void *)raw_data; + } + return PyArrayScalar_VAL(scalar, Unicode); case NPY_VOID: - return ((PyVoidScalarObject *)scalar)->obval; + /* Note: no & needed here, so can't use CASE */ + return PyArrayScalar_VAL(scalar, Void); } /* @@ -81,14 +92,13 @@ scalar_value(PyObject *scalar, PyArray_Descr *descr) * scalar it inherits from. */ -#define _CHK(cls) (PyObject_IsInstance(scalar, \ - (PyObject *)&Py##cls##ArrType_Type)) -#define _OBJ(lt) &(((Py##lt##ScalarObject *)scalar)->obval) -#define _IFCASE(cls) if _CHK(cls) return _OBJ(cls) +#define _CHK(cls) PyObject_IsInstance(scalar, \ + (PyObject *)&Py##cls##ArrType_Type) +#define _IFCASE(cls) if (_CHK(cls)) return &PyArrayScalar_VAL(scalar, cls) - if _CHK(Number) { - if _CHK(Integer) { - if _CHK(SignedInteger) { + if (_CHK(Number)) { + if (_CHK(Integer)) { + if (_CHK(SignedInteger)) { _IFCASE(Byte); _IFCASE(Short); _IFCASE(Int); @@ -107,7 +117,7 @@ scalar_value(PyObject *scalar, PyArray_Descr *descr) } else { /* Inexact */ - if _CHK(Floating) { + if (_CHK(Floating)) { _IFCASE(Half); _IFCASE(Float); _IFCASE(Double); @@ -122,20 +132,32 @@ scalar_value(PyObject *scalar, PyArray_Descr *descr) } } else if (_CHK(Bool)) { - return _OBJ(Bool); + return &PyArrayScalar_VAL(scalar, Bool); } else if (_CHK(Datetime)) { - return _OBJ(Datetime); + return &PyArrayScalar_VAL(scalar, Datetime); } else if (_CHK(Flexible)) { if (_CHK(String)) { - return (void *)PyString_AS_STRING(scalar); + return (void *)PyBytes_AS_STRING(scalar); } if (_CHK(Unicode)) { - return (void *)PyUnicode_AS_DATA(scalar); + /* Treat this the same as the NPY_UNICODE base class */ + + /* lazy initialization, to reduce the memory used by string scalars */ + if (PyArrayScalar_VAL(scalar, Unicode) == NULL) { + Py_UCS4 *raw_data = PyUnicode_AsUCS4Copy(scalar); + if (raw_data == NULL) { + return NULL; + } + PyArrayScalar_VAL(scalar, Unicode) = raw_data; + return (void *)raw_data; + } + return PyArrayScalar_VAL(scalar, Unicode); } if (_CHK(Void)) { - return ((PyVoidScalarObject *)scalar)->obval; + /* Note: no & needed here, so can't use _IFCASE */ + return PyArrayScalar_VAL(scalar, Void); } } else { @@ -147,7 +169,7 @@ scalar_value(PyObject *scalar, PyArray_Descr *descr) * Use the alignment flag to figure out where the data begins * after a PyObject_HEAD */ - memloc = (npy_intp)scalar; + memloc = (uintptr_t)scalar; memloc += sizeof(PyObject); /* now round-up to the nearest alignment value */ align = descr->alignment; @@ -156,7 +178,6 @@ scalar_value(PyObject *scalar, PyArray_Descr *descr) } return (void *)memloc; #undef _IFCASE -#undef _OBJ #undef _CHK } @@ -212,8 +233,12 @@ PyArray_CastScalarToCtype(PyObject *scalar, void *ctypeptr, PyArray_VectorUnaryFunc* castfunc; descr = PyArray_DescrFromScalar(scalar); + if (descr == NULL) { + return -1; + } castfunc = PyArray_GetCastFunc(descr, outcode->type_num); if (castfunc == NULL) { + Py_DECREF(descr); return -1; } if (PyTypeNum_ISEXTENDED(descr->type_num) || @@ -233,6 +258,7 @@ PyArray_CastScalarToCtype(PyObject *scalar, void *ctypeptr, NPY_ARRAY_CARRAY, NULL); if (aout == NULL) { Py_DECREF(ain); + Py_DECREF(descr); return -1; } castfunc(PyArray_DATA(ain), PyArray_DATA(aout), 1, ain, aout); @@ -277,14 +303,10 @@ PyArray_CastScalarDirect(PyObject *scalar, PyArray_Descr *indescr, NPY_NO_EXPORT PyObject * PyArray_FromScalar(PyObject *scalar, PyArray_Descr *outcode) { - PyArray_Descr *typecode; - PyArrayObject *r; - char *memptr; - PyObject *ret; - /* convert to 0-dim array of scalar typecode */ - typecode = PyArray_DescrFromScalar(scalar); + PyArray_Descr *typecode = PyArray_DescrFromScalar(scalar); if (typecode == NULL) { + Py_XDECREF(outcode); return NULL; } if ((typecode->type_num == NPY_VOID) && @@ -298,37 +320,26 @@ PyArray_FromScalar(PyObject *scalar, PyArray_Descr *outcode) NULL, (PyObject *)scalar); } - /* Need to INCREF typecode because PyArray_NewFromDescr steals a - * reference below and we still need to access typecode afterwards. */ - Py_INCREF(typecode); - r = (PyArrayObject *)PyArray_NewFromDescr(&PyArray_Type, + PyArrayObject *r = (PyArrayObject *)PyArray_NewFromDescr(&PyArray_Type, typecode, 0, NULL, NULL, NULL, 0, NULL); - if (r==NULL) { - Py_DECREF(typecode); Py_XDECREF(outcode); + if (r == NULL) { + Py_XDECREF(outcode); return NULL; } + /* the dtype used by the array may be different to the one requested */ + typecode = PyArray_DESCR(r); if (PyDataType_FLAGCHK(typecode, NPY_USE_SETITEM)) { if (typecode->f->setitem(scalar, PyArray_DATA(r), r) < 0) { - Py_DECREF(typecode); Py_XDECREF(outcode); Py_DECREF(r); + Py_DECREF(r); + Py_XDECREF(outcode); return NULL; } - goto finish; } + else { + char *memptr = scalar_value(scalar, typecode); - memptr = scalar_value(scalar, typecode); - -#ifndef Py_UNICODE_WIDE - if (typecode->type_num == NPY_UNICODE) { - PyUCS2Buffer_AsUCS4((Py_UNICODE *)memptr, - (npy_ucs4 *)PyArray_DATA(r), - PyUnicode_GET_SIZE(scalar), - PyArray_ITEMSIZE(r) >> 2); - } - else -#endif - { memcpy(PyArray_DATA(r), memptr, PyArray_ITEMSIZE(r)); if (PyDataType_FLAGCHK(typecode, NPY_ITEM_HASOBJECT)) { /* Need to INCREF just the PyObject portion */ @@ -336,22 +347,26 @@ PyArray_FromScalar(PyObject *scalar, PyArray_Descr *outcode) } } -finish: if (outcode == NULL) { - Py_DECREF(typecode); return (PyObject *)r; } if (PyArray_EquivTypes(outcode, typecode)) { if (!PyTypeNum_ISEXTENDED(typecode->type_num) || (outcode->elsize == typecode->elsize)) { - Py_DECREF(typecode); Py_DECREF(outcode); + /* + * Since the type is equivalent, and we haven't handed the array + * to anyone yet, let's fix the dtype to be what was requested, + * even if it is equivalent to what was passed in. + */ + Py_SETREF(((PyArrayObject_fields *)r)->descr, outcode); + return (PyObject *)r; } } /* cast if necessary to desired output typecode */ - ret = PyArray_CastToType((PyArrayObject *)r, outcode, 0); - Py_DECREF(typecode); Py_DECREF(r); + PyObject *ret = PyArray_CastToType(r, outcode, 0); + Py_DECREF(r); return ret; } @@ -364,14 +379,15 @@ PyArray_FromScalar(PyObject *scalar, PyArray_Descr *outcode) NPY_NO_EXPORT PyObject * PyArray_ScalarFromObject(PyObject *object) { - PyObject *ret=NULL; + PyObject *ret = NULL; + if (PyArray_IsZeroDim(object)) { return PyArray_ToScalar(PyArray_DATA((PyArrayObject *)object), (PyArrayObject *)object); } /* * Booleans in Python are implemented as a subclass of integers, - * so PyBool_Check must be called before PyInt_Check. + * so PyBool_Check must be called before PyLong_Check. */ if (PyBool_Check(object)) { if (object == Py_True) { @@ -381,42 +397,49 @@ PyArray_ScalarFromObject(PyObject *object) PyArrayScalar_RETURN_FALSE; } } - else if (PyInt_Check(object)) { - ret = PyArrayScalar_New(Long); - if (ret == NULL) { - return NULL; + else if (PyLong_Check(object)) { + /* Check if fits in long */ + npy_long val_long = PyLong_AsLong(object); + if (!error_converting(val_long)) { + ret = PyArrayScalar_New(Long); + if (ret != NULL) { + PyArrayScalar_VAL(ret, Long) = val_long; + } + return ret; + } + PyErr_Clear(); + + /* Check if fits in long long */ + npy_longlong val_longlong = PyLong_AsLongLong(object); + if (!error_converting(val_longlong)) { + ret = PyArrayScalar_New(LongLong); + if (ret != NULL) { + PyArrayScalar_VAL(ret, LongLong) = val_longlong; + } + return ret; } - PyArrayScalar_VAL(ret, Long) = PyInt_AS_LONG(object); + PyErr_Clear(); + + return NULL; } else if (PyFloat_Check(object)) { ret = PyArrayScalar_New(Double); - if (ret == NULL) { - return NULL; + if (ret != NULL) { + PyArrayScalar_VAL(ret, Double) = PyFloat_AS_DOUBLE(object); } - PyArrayScalar_VAL(ret, Double) = PyFloat_AS_DOUBLE(object); + return ret; } else if (PyComplex_Check(object)) { ret = PyArrayScalar_New(CDouble); - if (ret == NULL) { - return NULL; + if (ret != NULL) { + PyArrayScalar_VAL(ret, CDouble).real = PyComplex_RealAsDouble(object); + PyArrayScalar_VAL(ret, CDouble).imag = PyComplex_ImagAsDouble(object); } - PyArrayScalar_VAL(ret, CDouble).real = PyComplex_RealAsDouble(object); - PyArrayScalar_VAL(ret, CDouble).imag = PyComplex_ImagAsDouble(object); + return ret; } - else if (PyLong_Check(object)) { - npy_longlong val; - val = PyLong_AsLongLong(object); - if (error_converting(val)) { - PyErr_Clear(); - return NULL; - } - ret = PyArrayScalar_New(LongLong); - if (ret == NULL) { - return NULL; - } - PyArrayScalar_VAL(ret, LongLong) = val; + else { + return NULL; } - return ret; } /*New reference */ @@ -425,37 +448,69 @@ PyArray_ScalarFromObject(PyObject *object) NPY_NO_EXPORT PyArray_Descr * PyArray_DescrFromTypeObject(PyObject *type) { - int typenum; - PyArray_Descr *new, *conv = NULL; - /* if it's a builtin type, then use the typenumber */ - typenum = _typenum_fromtypeobj(type,1); + int typenum = _typenum_fromtypeobj(type,1); if (typenum != NPY_NOTYPE) { - new = PyArray_DescrFromType(typenum); - return new; + return PyArray_DescrFromType(typenum); } /* Check the generic types */ if ((type == (PyObject *) &PyNumberArrType_Type) || (type == (PyObject *) &PyInexactArrType_Type) || (type == (PyObject *) &PyFloatingArrType_Type)) { + if (DEPRECATE("Converting `np.inexact` or `np.floating` to " + "a dtype is deprecated. The current result is `float64` " + "which is not strictly correct.") < 0) { + return NULL; + } typenum = NPY_DOUBLE; } else if (type == (PyObject *)&PyComplexFloatingArrType_Type) { + if (DEPRECATE("Converting `np.complex` to a dtype is deprecated. " + "The current result is `complex128` which is not " + "strictly correct.") < 0) { + return NULL; + } typenum = NPY_CDOUBLE; } else if ((type == (PyObject *)&PyIntegerArrType_Type) || (type == (PyObject *)&PySignedIntegerArrType_Type)) { + if (DEPRECATE("Converting `np.integer` or `np.signedinteger` to " + "a dtype is deprecated. The current result is " + "`np.dtype(np.int_)` which is not strictly correct. " + "Note that the result depends on the system. To ensure " + "stable results use may want to use `np.int64` or " + "`np.int32`.") < 0) { + return NULL; + } typenum = NPY_LONG; } else if (type == (PyObject *) &PyUnsignedIntegerArrType_Type) { + if (DEPRECATE("Converting `np.unsignedinteger` to a dtype is " + "deprecated. The current result is `np.dtype(np.uint)` " + "which is not strictly correct. Note that the result " + "depends on the system. To ensure stable results you may " + "want to use `np.uint64` or `np.uint32`.") < 0) { + return NULL; + } typenum = NPY_ULONG; } else if (type == (PyObject *) &PyCharacterArrType_Type) { + if (DEPRECATE("Converting `np.character` to a dtype is deprecated. " + "The current result is `np.dtype(np.str_)` " + "which is not strictly correct. Note that `np.character` " + "is generally deprecated and 'S1' should be used.") < 0) { + return NULL; + } typenum = NPY_STRING; } else if ((type == (PyObject *) &PyGenericArrType_Type) || (type == (PyObject *) &PyFlexibleArrType_Type)) { + if (DEPRECATE("Converting `np.generic` to a dtype is " + "deprecated. The current result is `np.dtype(np.void)` " + "which is not strictly correct.") < 0) { + return NULL; + } typenum = NPY_VOID; } @@ -470,18 +525,25 @@ PyArray_DescrFromTypeObject(PyObject *type) /* Do special thing for VOID sub-types */ if (PyType_IsSubtype((PyTypeObject *)type, &PyVoidArrType_Type)) { - new = PyArray_DescrNewFromType(NPY_VOID); - conv = _arraydescr_fromobj(type); - if (conv) { + PyArray_Descr *new = PyArray_DescrNewFromType(NPY_VOID); + if (new == NULL) { + return NULL; + } + PyArray_Descr *conv = _arraydescr_try_convert_from_dtype_attr(type); + if ((PyObject *)conv != Py_NotImplemented) { + if (conv == NULL) { + Py_DECREF(new); + return NULL; + } new->fields = conv->fields; - Py_INCREF(new->fields); + Py_XINCREF(new->fields); new->names = conv->names; - Py_INCREF(new->names); + Py_XINCREF(new->names); new->elsize = conv->elsize; new->subarray = conv->subarray; conv->subarray = NULL; - Py_DECREF(conv); } + Py_DECREF(conv); Py_XDECREF(new->typeobj); new->typeobj = (PyTypeObject *)type; Py_INCREF(type); @@ -558,17 +620,17 @@ PyArray_DescrFromScalar(PyObject *sc) } descr = PyArray_DescrFromTypeObject((PyObject *)Py_TYPE(sc)); + if (descr == NULL) { + return NULL; + } if (PyDataType_ISUNSIZED(descr)) { PyArray_DESCR_REPLACE(descr); type_num = descr->type_num; if (type_num == NPY_STRING) { - descr->elsize = PyString_GET_SIZE(sc); + descr->elsize = PyBytes_GET_SIZE(sc); } else if (type_num == NPY_UNICODE) { - descr->elsize = PyUnicode_GET_DATA_SIZE(sc); -#ifndef Py_UNICODE_WIDE - descr->elsize <<= 1; -#endif + descr->elsize = PyUnicode_GET_LENGTH(sc) * 4; } else { PyArray_Descr *dtype; @@ -650,25 +712,31 @@ PyArray_Scalar(void *data, PyArray_Descr *descr, PyObject *base) itemsize = (((itemsize - 1) >> 2) + 1) << 2; } } -#if PY_VERSION_HEX >= 0x03030000 if (type_num == NPY_UNICODE) { - PyObject *u, *args; - int byteorder; - -#if NPY_BYTE_ORDER == NPY_LITTLE_ENDIAN - byteorder = -1; -#elif NPY_BYTE_ORDER == NPY_BIG_ENDIAN - byteorder = +1; -#else - #error Endianness undefined ? -#endif - if (swap) byteorder *= -1; - - u = PyUnicode_DecodeUTF32(data, itemsize, NULL, &byteorder); + /* we need the full string length here, else copyswap will write too + many bytes */ + void *buff = PyArray_malloc(descr->elsize); + if (buff == NULL) { + return PyErr_NoMemory(); + } + /* copyswap needs an array object, but only actually cares about the + * dtype + */ + PyArrayObject_fields dummy_arr; + if (base == NULL) { + dummy_arr.descr = descr; + base = (PyObject *)&dummy_arr; + } + copyswap(buff, data, swap, base); + + /* truncation occurs here */ + PyObject *u = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, buff, itemsize / 4); + PyArray_free(buff); if (u == NULL) { return NULL; } - args = Py_BuildValue("(O)", u); + + PyObject *args = Py_BuildValue("(O)", u); if (args == NULL) { Py_DECREF(u); return NULL; @@ -678,7 +746,6 @@ PyArray_Scalar(void *data, PyArray_Descr *descr, PyObject *base) Py_DECREF(args); return obj; } -#endif if (type->tp_itemsize != 0) { /* String type */ obj = type->tp_alloc(type, itemsize); @@ -702,94 +769,18 @@ PyArray_Scalar(void *data, PyArray_Descr *descr, PyObject *base) } if (PyTypeNum_ISFLEXIBLE(type_num)) { if (type_num == NPY_STRING) { - destptr = PyString_AS_STRING(obj); - ((PyStringObject *)obj)->ob_shash = -1; -#if !defined(NPY_PY3K) - ((PyStringObject *)obj)->ob_sstate = SSTATE_NOT_INTERNED; -#endif + destptr = PyBytes_AS_STRING(obj); + ((PyBytesObject *)obj)->ob_shash = -1; memcpy(destptr, data, itemsize); return obj; } -#if PY_VERSION_HEX < 0x03030000 - else if (type_num == NPY_UNICODE) { - /* tp_alloc inherited from Python PyBaseObject_Type */ - PyUnicodeObject *uni = (PyUnicodeObject*)obj; - size_t length = itemsize >> 2; - Py_UNICODE *dst; -#ifndef Py_UNICODE_WIDE - char *buffer; - Py_UNICODE *tmp; - int alloc = 0; - - length *= 2; -#endif - /* Set uni->str so that object can be deallocated on failure */ - uni->str = NULL; - uni->defenc = NULL; - uni->hash = -1; - dst = PyObject_MALLOC(sizeof(Py_UNICODE) * (length + 1)); - if (dst == NULL) { - Py_DECREF(obj); - PyErr_NoMemory(); - return NULL; - } -#ifdef Py_UNICODE_WIDE - memcpy(dst, data, itemsize); - if (swap) { - byte_swap_vector(dst, length, 4); - } - uni->str = dst; - uni->str[length] = 0; - uni->length = length; -#else - /* need aligned data buffer */ - if ((swap) || ((((npy_intp)data) % descr->alignment) != 0)) { - buffer = malloc(itemsize); - if (buffer == NULL) { - PyObject_FREE(dst); - Py_DECREF(obj); - PyErr_NoMemory(); - } - alloc = 1; - memcpy(buffer, data, itemsize); - if (swap) { - byte_swap_vector(buffer, itemsize >> 2, 4); - } - } - else { - buffer = data; - } - - /* - * Allocated enough for 2-characters per itemsize. - * Now convert from the data-buffer - */ - length = PyUCS2Buffer_FromUCS4(dst, - (npy_ucs4 *)buffer, itemsize >> 2); - if (alloc) { - free(buffer); - } - /* Resize the unicode result */ - tmp = PyObject_REALLOC(dst, sizeof(Py_UNICODE)*(length + 1)); - if (tmp == NULL) { - PyObject_FREE(dst); - Py_DECREF(obj); - return NULL; - } - uni->str = tmp; - uni->str[length] = 0; - uni->length = length; -#endif - return obj; - } -#endif /* PY_VERSION_HEX < 0x03030000 */ else { PyVoidScalarObject *vobj = (PyVoidScalarObject *)obj; vobj->base = NULL; vobj->descr = descr; Py_INCREF(descr); vobj->obval = NULL; - Py_SIZE(vobj) = itemsize; + Py_SET_SIZE(vobj, itemsize); vobj->flags = NPY_ARRAY_CARRAY | NPY_ARRAY_F_CONTIGUOUS | NPY_ARRAY_OWNDATA; swap = 0; if (PyDataType_HASFIELDS(descr)) { @@ -802,6 +793,9 @@ PyArray_Scalar(void *data, PyArray_Descr *descr, PyObject *base) return obj; } } + if (itemsize == 0) { + return obj; + } destptr = PyDataMem_NEW(itemsize); if (destptr == NULL) { Py_DECREF(obj); diff --git a/numpy/core/src/multiarray/scalartypes.c.src b/numpy/core/src/multiarray/scalartypes.c.src index 25e0668edf53..013526ff0c3e 100644 --- a/numpy/core/src/multiarray/scalartypes.c.src +++ b/numpy/core/src/multiarray/scalartypes.c.src @@ -1,7 +1,7 @@ /* -*- c -*- */ #define PY_SSIZE_T_CLEAN -#include "Python.h" -#include "structmember.h" +#include <Python.h> +#include <structmember.h> #define NPY_NO_DEPRECATED_API NPY_API_VERSION #ifndef _MULTIARRAYMODULE @@ -28,12 +28,22 @@ #include "npy_import.h" #include "dragon4.h" #include "npy_longdouble.h" -#include "buffer.h" +#include "npy_buffer.h" #include <stdlib.h> #include "binop_override.h" +/* + * used for allocating a single scalar, so use the default numpy + * memory allocators instead of the (maybe) user overrides + */ +NPY_NO_EXPORT void * +npy_alloc_cache_zero(size_t nmemb, size_t size); + +NPY_NO_EXPORT void +npy_free_cache(void * p, npy_uintp sz); + NPY_NO_EXPORT PyBoolScalarObject _PyArrayScalar_BoolValues[] = { {PyObject_HEAD_INIT(&PyBoolArrType_Type) 0}, {PyObject_HEAD_INIT(&PyBoolArrType_Type) 1}, @@ -54,63 +64,9 @@ NPY_NO_EXPORT PyTypeObject PyTimeIntegerArrType_Type; * Floating, ComplexFloating, Flexible, Character# */ NPY_NO_EXPORT PyTypeObject Py@NAME@ArrType_Type = { -#if defined(NPY_PY3K) PyVarObject_HEAD_INIT(NULL, 0) -#else - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ -#endif - "numpy.@name@", /* tp_name*/ - sizeof(PyObject), /* tp_basicsize*/ - 0, /* tp_itemsize */ - /* methods */ - 0, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ -#if defined(NPY_PY3K) - 0, /* tp_reserved */ -#else - 0, /* tp_compare */ -#endif - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - 0, /* tp_flags */ - 0, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - 0, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ - 0, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - 0, /* tp_del */ - 0, /* tp_version_tag */ + .tp_name = "numpy.@name@", + .tp_basicsize = sizeof(PyObject), }; /**end repeat**/ @@ -121,8 +77,11 @@ gentype_alloc(PyTypeObject *type, Py_ssize_t nitems) const size_t size = _PyObject_VAR_SIZE(type, nitems + 1); obj = (PyObject *)PyObject_Malloc(size); + if (obj == NULL) { + PyErr_NoMemory(); + return NULL; + } /* - * Fixme. Need to check for no memory. * If we don't need to zero memory, we could use * PyObject_{New, NewVar} for this whole function. */ @@ -203,7 +162,7 @@ static PyObject * gentype_add(PyObject *m1, PyObject* m2) { /* special case str.__radd__, which should not call array_add */ - if (PyString_Check(m1) || PyUnicode_Check(m1)) { + if (PyBytes_Check(m1) || PyUnicode_Check(m1)) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } @@ -225,20 +184,6 @@ gentype_@name@(PyObject *m1, PyObject *m2) /**end repeat**/ -#if !defined(NPY_PY3K) -/**begin repeat - * - * #name = divide# - */ -static PyObject * -gentype_@name@(PyObject *m1, PyObject *m2) -{ - BINOP_GIVE_UP_IF_NEEDED(m1, m2, nb_@name@, gentype_@name@); - return PyArray_Type.tp_as_number->nb_@name@(m1, m2); -} -/**end repeat**/ -#endif - /* Get a nested slot, or NULL if absent */ #define GET_NESTED_SLOT(type, group, slot) \ ((type)->group == NULL ? NULL : (type)->group->slot) @@ -274,28 +219,29 @@ gentype_multiply(PyObject *m1, PyObject *m2) } /**begin repeat - * - * #name = positive, negative, absolute, invert, int, float# + * #TYPE = BYTE, UBYTE, SHORT, USHORT, INT, UINT, + * LONG, ULONG, LONGLONG, ULONGLONG# + * #type = npy_byte, npy_ubyte, npy_short, npy_ushort, npy_int, npy_uint, + * npy_long, npy_ulong, npy_longlong, npy_ulonglong# + * #c = hh, uhh, h, uh,, u, l, ul, ll, ull# + * #Name = Byte, UByte, Short, UShort, Int, UInt, + * Long, ULong, LongLong, ULongLong# + * #convert = Long*8, LongLong*2# */ static PyObject * -gentype_@name@(PyObject *m1) +@type@_bit_count(PyObject *self, PyObject *NPY_UNUSED(args)) { - PyObject *arr, *ret; + @type@ scalar = PyArrayScalar_VAL(self, @Name@); + uint8_t count = npy_popcount@c@(scalar); + PyObject *result = PyLong_From@convert@(count); - arr = PyArray_FromScalar(m1, NULL); - if (arr == NULL) { - return NULL; - } - ret = Py_TYPE(arr)->tp_as_number->nb_@name@(arr); - Py_DECREF(arr); - return ret; + return result; } /**end repeat**/ -#if !defined(NPY_PY3K) /**begin repeat * - * #name = long, oct, hex# + * #name = positive, negative, absolute, invert, int, float# */ static PyObject * gentype_@name@(PyObject *m1) @@ -311,7 +257,6 @@ gentype_@name@(PyObject *m1) return ret; } /**end repeat**/ -#endif static int gentype_nonzero_number(PyObject *m1) @@ -323,11 +268,7 @@ gentype_nonzero_number(PyObject *m1) if (arr == NULL) { return -1; } -#if defined(NPY_PY3K) ret = Py_TYPE(arr)->tp_as_number->nb_bool(arr); -#else - ret = Py_TYPE(arr)->tp_as_number->nb_nonzero(arr); -#endif Py_DECREF(arr); return ret; } @@ -355,21 +296,9 @@ gentype_format(PyObject *self, PyObject *args) PyObject *format_spec; PyObject *obj, *ret; -#if defined(NPY_PY3K) if (!PyArg_ParseTuple(args, "U:__format__", &format_spec)) { return NULL; } -#else - if (!PyArg_ParseTuple(args, "O:__format__", &format_spec)) { - return NULL; - } - - if (!PyUnicode_Check(format_spec) && !PyString_Check(format_spec)) { - PyErr_SetString(PyExc_TypeError, - "format must be a string"); - return NULL; - } -#endif /* * Convert to an appropriate Python type and call its format. @@ -377,14 +306,11 @@ gentype_format(PyObject *self, PyObject *args) * because it throws away precision. */ if (Py_TYPE(self) == &PyBoolArrType_Type) { - obj = PyBool_FromLong(((PyBoolScalarObject *)self)->obval); + obj = PyBool_FromLong(PyArrayScalar_VAL(self, Bool)); } - else if (PyArray_IsScalar(self, Integer)) { -#if defined(NPY_PY3K) + else if (PyArray_IsScalar(self, Integer) + && !PyArray_IsScalar(self, Timedelta)) { obj = Py_TYPE(self)->tp_as_number->nb_int(self); -#else - obj = Py_TYPE(self)->tp_as_number->nb_long(self); -#endif } else if (PyArray_IsScalar(self, Floating)) { obj = Py_TYPE(self)->tp_as_number->nb_float(self); @@ -436,13 +362,13 @@ format_@name@(@type@ val, npy_bool scientific, { if (scientific) { return Dragon4_Scientific_@Name@(&val, - DigitMode_Unique, precision, + DigitMode_Unique, precision, -1, sign, trim, pad_left, exp_digits); } else { return Dragon4_Positional_@Name@(&val, DigitMode_Unique, CutoffMode_TotalLength, precision, - sign, trim, pad_left, pad_right); + -1, sign, trim, pad_left, pad_right); } } @@ -450,43 +376,68 @@ format_@name@(@type@ val, npy_bool scientific, /**end repeat**/ /* - * over-ride repr and str of array-scalar strings and unicode to - * remove NULL bytes and then call the corresponding functions - * of string and unicode. + * Over-ride repr and str of array-scalar byte strings to remove NULL bytes and + * then call the corresponding functions of PyBytes_Type to generate the string */ /**begin repeat - * #name = string*2,unicode*2# - * #form = (repr,str)*2# - * #Name = String*2,Unicode*2# - * #NAME = STRING*2,UNICODE*2# - * #extra = AndSize*2,,# - * #type = npy_char*2, Py_UNICODE*2# + * #form = repr, str# */ static PyObject * -@name@type_@form@(PyObject *self) +stringtype_@form@(PyObject *self) { - const @type@ *dptr, *ip; - int len; + const npy_char *dptr, *ip; + Py_ssize_t len; PyObject *new; PyObject *ret; - ip = dptr = Py@Name@_AS_@NAME@(self); - len = Py@Name@_GET_SIZE(self); - dptr += len-1; - while(len > 0 && *dptr-- == 0) { - len--; - } - new = Py@Name@_From@Name@@extra@(ip, len); + ip = PyBytes_AS_STRING(self); + len = PyBytes_GET_SIZE(self); + for(dptr = ip + len - 1; len > 0 && *dptr == 0; len--, dptr--); + new = PyBytes_FromStringAndSize(ip, len); if (new == NULL) { - return PyUString_FromString(""); + return NULL; } - ret = Py@Name@_Type.tp_@form@(new); + ret = PyBytes_Type.tp_@form@(new); Py_DECREF(new); return ret; } /**end repeat**/ +/* + * Over-ride repr and str of array-scalar strings to remove NULL code points and + * then call the corresponding functions of PyUnicode_Type to generate the string + */ + +/**begin repeat + * #form = repr, str# + */ +static PyObject * +unicodetype_@form@(PyObject *self) +{ + Py_UCS4 *dptr, *ip; + Py_ssize_t len; + PyObject *new; + PyObject *ret; + + /* PyUnicode_READY is called by PyUnicode_GetLength */ + len = PyUnicode_GetLength(self); + ip = PyUnicode_AsUCS4Copy(self); + if (ip == NULL) { + return NULL; + } + for(dptr = ip + len - 1; len > 0 && *dptr == 0; len--, dptr--); + new = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, ip, len); + if (new == NULL) { + PyMem_Free(ip); + return NULL; + } + ret = PyUnicode_Type.tp_@form@(new); + Py_DECREF(new); + PyMem_Free(ip); + return ret; +} +/**end repeat**/ /* * Convert array of bytes to a string representation much like bytes.__repr__, @@ -531,26 +482,29 @@ _void_to_hex(const char* argbuf, const Py_ssize_t arglen, } memcpy(&retbuf[j], echars, strlen(echars)); - retval = PyUString_FromStringAndSize(retbuf, slen); + retval = PyUnicode_FromStringAndSize(retbuf, slen); PyMem_Free(retbuf); return retval; } +static PyObject * +_void_scalar_repr(PyObject *obj) { + static PyObject *reprfunc = NULL; + npy_cache_import("numpy.core.arrayprint", + "_void_scalar_repr", &reprfunc); + if (reprfunc == NULL) { + return NULL; + } + return PyObject_CallFunction(reprfunc, "O", obj); +} + static PyObject * voidtype_repr(PyObject *self) { PyVoidScalarObject *s = (PyVoidScalarObject*) self; if (PyDataType_HASFIELDS(s->descr)) { - static PyObject *reprfunc = NULL; - - npy_cache_import("numpy.core.arrayprint", - "_void_scalar_repr", &reprfunc); - if (reprfunc == NULL) { - return NULL; - } - - return PyObject_CallFunction(reprfunc, "O", self); + return _void_scalar_repr(self); } return _void_to_hex(s->obval, s->descr->elsize, "void(b'", "\\x", "')"); } @@ -560,15 +514,7 @@ voidtype_str(PyObject *self) { PyVoidScalarObject *s = (PyVoidScalarObject*) self; if (PyDataType_HASFIELDS(s->descr)) { - static PyObject *reprfunc = NULL; - - npy_cache_import("numpy.core.arrayprint", - "_void_scalar_repr", &reprfunc); - if (reprfunc == NULL) { - return NULL; - } - - return PyObject_CallFunction(reprfunc, "O", self); + return _void_scalar_repr(self); } return _void_to_hex(s->obval, s->descr->elsize, "b'", "\\x", "'"); } @@ -607,21 +553,15 @@ datetimetype_repr(PyObject *self) */ if ((scal->obmeta.num == 1 && scal->obmeta.base != NPY_FR_h) || scal->obmeta.base == NPY_FR_GENERIC) { - ret = PyUString_FromString("numpy.datetime64('"); - PyUString_ConcatAndDel(&ret, - PyUString_FromString(iso)); - PyUString_ConcatAndDel(&ret, - PyUString_FromString("')")); + ret = PyUnicode_FromFormat("numpy.datetime64('%s')", iso); } else { - ret = PyUString_FromString("numpy.datetime64('"); - PyUString_ConcatAndDel(&ret, - PyUString_FromString(iso)); - PyUString_ConcatAndDel(&ret, - PyUString_FromString("','")); - ret = append_metastr_to_string(&scal->obmeta, 1, ret); - PyUString_ConcatAndDel(&ret, - PyUString_FromString("')")); + PyObject *meta = metastr_to_unicode(&scal->obmeta, 1); + if (meta == NULL) { + return NULL; + } + ret = PyUnicode_FromFormat("numpy.datetime64('%s','%S')", iso, meta); + Py_DECREF(meta); } return ret; @@ -631,7 +571,7 @@ static PyObject * timedeltatype_repr(PyObject *self) { PyTimedeltaScalarObject *scal; - PyObject *ret; + PyObject *val, *ret; if (!PyArray_IsScalar(self, Timedelta)) { PyErr_SetString(PyExc_RuntimeError, @@ -643,32 +583,34 @@ timedeltatype_repr(PyObject *self) /* The value */ if (scal->obval == NPY_DATETIME_NAT) { - ret = PyUString_FromString("numpy.timedelta64('NaT'"); + val = PyUnicode_FromString("'NaT'"); } else { - /* - * Can't use "%lld" if HAVE_LONG_LONG is not defined - */ + /* Can't use "%lld" if HAVE_LONG_LONG is not defined */ #if defined(HAVE_LONG_LONG) - ret = PyUString_FromFormat("numpy.timedelta64(%lld", - (long long)scal->obval); + val = PyUnicode_FromFormat("%lld", (long long)scal->obval); #else - ret = PyUString_FromFormat("numpy.timedelta64(%ld", - (long)scal->obval); + val = PyUnicode_FromFormat("%ld", (long)scal->obval); #endif } + if (val == NULL) { + return NULL; + } + /* The metadata unit */ if (scal->obmeta.base == NPY_FR_GENERIC) { - PyUString_ConcatAndDel(&ret, - PyUString_FromString(")")); + ret = PyUnicode_FromFormat("numpy.timedelta64(%S)", val); } else { - PyUString_ConcatAndDel(&ret, - PyUString_FromString(",'")); - ret = append_metastr_to_string(&scal->obmeta, 1, ret); - PyUString_ConcatAndDel(&ret, - PyUString_FromString("')")); + PyObject *meta = metastr_to_unicode(&scal->obmeta, 1); + if (meta == NULL) { + Py_DECREF(val); + return NULL; + } + ret = PyUnicode_FromFormat("numpy.timedelta64(%S,'%S')", val, meta); + Py_DECREF(meta); } + Py_DECREF(val); return ret; } @@ -700,7 +642,7 @@ datetimetype_str(PyObject *self) return NULL; } - return PyUString_FromString(iso); + return PyUnicode_FromString(iso); } static char *_datetime_verbose_strings[NPY_DATETIME_NUMUNITS] = { @@ -746,21 +688,19 @@ timedeltatype_str(PyObject *self) } if (scal->obval == NPY_DATETIME_NAT) { - ret = PyUString_FromString("NaT"); + ret = PyUnicode_FromString("NaT"); } else { /* * Can't use "%lld" if HAVE_LONG_LONG is not defined */ #if defined(HAVE_LONG_LONG) - ret = PyUString_FromFormat("%lld ", - (long long)(scal->obval * scal->obmeta.num)); + ret = PyUnicode_FromFormat("%lld %s", + (long long)(scal->obval * scal->obmeta.num), basestr); #else - ret = PyUString_FromFormat("%ld ", - (long)(scal->obval * scal->obmeta.num)); + ret = PyUnicode_FromFormat("%ld %s", + (long)(scal->obval * scal->obmeta.num), basestr); #endif - PyUString_ConcatAndDel(&ret, - PyUString_FromString(basestr)); } return ret; @@ -829,12 +769,13 @@ legacy_@name@_format@kind@(@type@ val) return NULL; } if (!npy_isfinite(val.imag)) { - strncat(buf, "*", 1); + strncat(buf, "*", sizeof(buf) - strlen(buf) - 1); } - strncat(buf, "j", 1); + strncat(buf, "j", sizeof(buf) - strlen(buf) - 1); } else { char re[64], im[64]; + if (npy_isfinite(val.real)) { PyOS_snprintf(format, sizeof(format), _FMT1, @NAME@PREC_@KIND@); res = NumPyOS_ascii_format@suff@(re, sizeof(re), format, @@ -877,13 +818,13 @@ legacy_@name@_format@kind@(@type@ val) strcpy(im, "-inf"); } if (!npy_isfinite(val.imag)) { - strncat(im, "*", 1); + strncat(im, "*", sizeof(im) - strlen(im) - 1); } } PyOS_snprintf(buf, sizeof(buf), "(%s%sj)", re, im); } - return PyUString_FromString(buf); + return PyUnicode_FromString(buf); } #undef _FMT1 @@ -924,7 +865,7 @@ legacy_@name@_format@kind@(npy_@name@ val){ strcpy(&buf[cnt],".0"); } - return PyUString_FromString(buf); + return PyUnicode_FromString(buf); } #undef _FMT1 @@ -956,7 +897,7 @@ static PyObject * { npy_@name@ absval; - if (npy_legacy_print_mode == 113) { + if (npy_legacy_print_mode <= 113) { return legacy_@name@_format@kind@(val); } @@ -971,18 +912,18 @@ static PyObject * static PyObject * @name@type_@kind@(PyObject *self) { - return @name@type_@kind@_either(((Py@Name@ScalarObject *)self)->obval, + return @name@type_@kind@_either(PyArrayScalar_VAL(self, @Name@), TrimMode_LeaveOneZero, TrimMode_DptZeros, 0); } static PyObject * c@name@type_@kind@(PyObject *self) { - PyObject *rstr, *istr, *ret; - npy_c@name@ val = ((PyC@Name@ScalarObject *)self)->obval; + PyObject *rstr, *istr; + npy_c@name@ val = PyArrayScalar_VAL(self, C@Name@); TrimMode trim = TrimMode_DptZeros; - if (npy_legacy_print_mode == 113) { + if (npy_legacy_print_mode <= 113) { return legacy_c@name@_format@kind@(val); } @@ -991,47 +932,47 @@ c@name@type_@kind@(PyObject *self) if (istr == NULL) { return NULL; } - - PyUString_ConcatAndDel(&istr, PyUString_FromString("j")); - return istr; + PyObject *ret = PyUnicode_FromFormat("%Sj", istr); + Py_DECREF(istr); + return ret; } if (npy_isfinite(val.real)) { rstr = @name@type_@kind@_either(val.real, trim, trim, 0); - if (rstr == NULL) { - return NULL; - } } else if (npy_isnan(val.real)) { - rstr = PyUString_FromString("nan"); + rstr = PyUnicode_FromString("nan"); } else if (val.real > 0){ - rstr = PyUString_FromString("inf"); + rstr = PyUnicode_FromString("inf"); } else { - rstr = PyUString_FromString("-inf"); + rstr = PyUnicode_FromString("-inf"); + } + if (rstr == NULL) { + return NULL; } if (npy_isfinite(val.imag)) { istr = @name@type_@kind@_either(val.imag, trim, trim, 1); - if (istr == NULL) { - return NULL; - } } else if (npy_isnan(val.imag)) { - istr = PyUString_FromString("+nan"); + istr = PyUnicode_FromString("+nan"); } else if (val.imag > 0){ - istr = PyUString_FromString("+inf"); + istr = PyUnicode_FromString("+inf"); } else { - istr = PyUString_FromString("-inf"); + istr = PyUnicode_FromString("-inf"); + } + if (istr == NULL) { + Py_DECREF(rstr); + return NULL; } - ret = PyUString_FromString("("); - PyUString_ConcatAndDel(&ret, rstr); - PyUString_ConcatAndDel(&ret, istr); - PyUString_ConcatAndDel(&ret, PyUString_FromString("j)")); + PyObject *ret = PyUnicode_FromFormat("(%S%Sj)", rstr, istr); + Py_DECREF(rstr); + Py_DECREF(istr); return ret; } @@ -1043,11 +984,11 @@ c@name@type_@kind@(PyObject *self) static PyObject * halftype_@kind@(PyObject *self) { - npy_half val = ((PyHalfScalarObject *)self)->obval; + npy_half val = PyArrayScalar_VAL(self, Half); float floatval = npy_half_to_float(val); float absval; - if (npy_legacy_print_mode == 113) { + if (npy_legacy_print_mode <= 113) { return legacy_float_format@kind@(floatval); } @@ -1081,85 +1022,29 @@ static PyObject * return npy_longdouble_to_PyLong(val); } -#if !defined(NPY_PY3K) - -/**begin repeat1 - * #name = int, hex, oct# - */ -static PyObject * -@char@longdoubletype_@name@(PyObject *self) -{ - PyObject *ret; - PyObject *obj = @char@longdoubletype_long(self); - if (obj == NULL) { - return NULL; - } - ret = Py_TYPE(obj)->tp_as_number->nb_@name@(obj); - Py_DECREF(obj); - return ret; -} -/**end repeat1**/ - -#endif /* !defined(NPY_PY3K) */ - /**end repeat**/ static PyNumberMethods gentype_as_number = { - (binaryfunc)gentype_add, /*nb_add*/ - (binaryfunc)gentype_subtract, /*nb_subtract*/ - (binaryfunc)gentype_multiply, /*nb_multiply*/ -#if defined(NPY_PY3K) -#else - (binaryfunc)gentype_divide, /*nb_divide*/ -#endif - (binaryfunc)gentype_remainder, /*nb_remainder*/ - (binaryfunc)gentype_divmod, /*nb_divmod*/ - (ternaryfunc)gentype_power, /*nb_power*/ - (unaryfunc)gentype_negative, - (unaryfunc)gentype_positive, /*nb_pos*/ - (unaryfunc)gentype_absolute, /*(unaryfunc)gentype_abs,*/ - (inquiry)gentype_nonzero_number, /*nb_nonzero*/ - (unaryfunc)gentype_invert, /*nb_invert*/ - (binaryfunc)gentype_lshift, /*nb_lshift*/ - (binaryfunc)gentype_rshift, /*nb_rshift*/ - (binaryfunc)gentype_and, /*nb_and*/ - (binaryfunc)gentype_xor, /*nb_xor*/ - (binaryfunc)gentype_or, /*nb_or*/ -#if defined(NPY_PY3K) -#else - 0, /*nb_coerce*/ -#endif - (unaryfunc)gentype_int, /*nb_int*/ -#if defined(NPY_PY3K) - 0, /*nb_reserved*/ -#else - (unaryfunc)gentype_long, /*nb_long*/ -#endif - (unaryfunc)gentype_float, /*nb_float*/ -#if defined(NPY_PY3K) -#else - (unaryfunc)gentype_oct, /*nb_oct*/ - (unaryfunc)gentype_hex, /*nb_hex*/ -#endif - 0, /*inplace_add*/ - 0, /*inplace_subtract*/ - 0, /*inplace_multiply*/ -#if defined(NPY_PY3K) -#else - 0, /*inplace_divide*/ -#endif - 0, /*inplace_remainder*/ - 0, /*inplace_power*/ - 0, /*inplace_lshift*/ - 0, /*inplace_rshift*/ - 0, /*inplace_and*/ - 0, /*inplace_xor*/ - 0, /*inplace_or*/ - (binaryfunc)gentype_floor_divide, /*nb_floor_divide*/ - (binaryfunc)gentype_true_divide, /*nb_true_divide*/ - 0, /*nb_inplace_floor_divide*/ - 0, /*nb_inplace_true_divide*/ - (unaryfunc)NULL, /*nb_index*/ + .nb_add = (binaryfunc)gentype_add, + .nb_subtract = (binaryfunc)gentype_subtract, + .nb_multiply = (binaryfunc)gentype_multiply, + .nb_remainder = (binaryfunc)gentype_remainder, + .nb_divmod = (binaryfunc)gentype_divmod, + .nb_power = (ternaryfunc)gentype_power, + .nb_negative = (unaryfunc)gentype_negative, + .nb_positive = (unaryfunc)gentype_positive, + .nb_absolute = (unaryfunc)gentype_absolute, + .nb_bool = (inquiry)gentype_nonzero_number, + .nb_invert = (unaryfunc)gentype_invert, + .nb_lshift = (binaryfunc)gentype_lshift, + .nb_rshift = (binaryfunc)gentype_rshift, + .nb_and = (binaryfunc)gentype_and, + .nb_xor = (binaryfunc)gentype_xor, + .nb_or = (binaryfunc)gentype_or, + .nb_int = (unaryfunc)gentype_int, + .nb_float = (unaryfunc)gentype_float, + .nb_floor_divide = (binaryfunc)gentype_floor_divide, + .nb_true_divide = (binaryfunc)gentype_true_divide, }; @@ -1200,19 +1085,19 @@ gentype_richcompare(PyObject *self, PyObject *other, int cmp_op) } static PyObject * -gentype_ndim_get(PyObject *NPY_UNUSED(self)) +gentype_ndim_get(PyObject *NPY_UNUSED(self), void *NPY_UNUSED(ignored)) { - return PyInt_FromLong(0); + return PyLong_FromLong(0); } static PyObject * -gentype_flags_get(PyObject *NPY_UNUSED(self)) +gentype_flags_get(PyObject *NPY_UNUSED(self), void *NPY_UNUSED(ignored)) { return PyArray_NewFlagsObject(NULL); } static PyObject * -voidtype_flags_get(PyVoidScalarObject *self) +voidtype_flags_get(PyVoidScalarObject *self, void *NPY_UNUSED(ignored)) { PyObject *flagobj; flagobj = PyArrayFlags_Type.tp_alloc(&PyArrayFlags_Type, 0); @@ -1225,7 +1110,7 @@ voidtype_flags_get(PyVoidScalarObject *self) } static PyObject * -voidtype_dtypedescr_get(PyVoidScalarObject *self) +voidtype_dtypedescr_get(PyVoidScalarObject *self, void *NPY_UNUSED(ignored)) { Py_INCREF(self->descr); return (PyObject *)self->descr; @@ -1233,7 +1118,7 @@ voidtype_dtypedescr_get(PyVoidScalarObject *self) static PyObject * -inttype_numerator_get(PyObject *self) +inttype_numerator_get(PyObject *self, void *NPY_UNUSED(ignored)) { Py_INCREF(self); return self; @@ -1241,25 +1126,21 @@ inttype_numerator_get(PyObject *self) static PyObject * -inttype_denominator_get(PyObject *self) +inttype_denominator_get(PyObject *self, void *NPY_UNUSED(ignored)) { - return PyInt_FromLong(1); + return PyLong_FromLong(1); } static PyObject * -gentype_data_get(PyObject *self) +gentype_data_get(PyObject *self, void *NPY_UNUSED(ignored)) { -#if defined(NPY_PY3K) return PyMemoryView_FromObject(self); -#else - return PyBuffer_FromObject(self, 0, Py_END_OF_BUFFER); -#endif } static PyObject * -gentype_itemsize_get(PyObject *self) +gentype_itemsize_get(PyObject *self, void *NPY_UNUSED(ignored)) { PyArray_Descr *typecode; PyObject *ret; @@ -1267,27 +1148,22 @@ gentype_itemsize_get(PyObject *self) typecode = PyArray_DescrFromScalar(self); elsize = typecode->elsize; -#ifndef Py_UNICODE_WIDE - if (typecode->type_num == NPY_UNICODE) { - elsize >>= 1; - } -#endif - ret = PyInt_FromLong((long) elsize); + ret = PyLong_FromLong((long) elsize); Py_DECREF(typecode); return ret; } static PyObject * -gentype_size_get(PyObject *NPY_UNUSED(self)) +gentype_size_get(PyObject *NPY_UNUSED(self), void *NPY_UNUSED(ignored)) { - return PyInt_FromLong(1); + return PyLong_FromLong(1); } static PyObject * -gentype_sizeof(PyObject *self) +gentype_sizeof(PyObject *self, PyObject *NPY_UNUSED(args)) { Py_ssize_t nbytes; - PyObject * isz = gentype_itemsize_get(self); + PyObject * isz = gentype_itemsize_get(self, NULL); if (isz == NULL) { return NULL; } @@ -1297,34 +1173,26 @@ gentype_sizeof(PyObject *self) return PyLong_FromSsize_t(nbytes); } -#if PY_VERSION_HEX >= 0x03000000 NPY_NO_EXPORT void gentype_struct_free(PyObject *ptr) { - PyArrayInterface *arrif; - PyObject *context; - - arrif = (PyArrayInterface*)PyCapsule_GetPointer(ptr, NULL); - context = (PyObject *)PyCapsule_GetContext(ptr); - Py_DECREF(context); - Py_XDECREF(arrif->descr); - PyArray_free(arrif->shape); - PyArray_free(arrif); -} -#else -NPY_NO_EXPORT void -gentype_struct_free(void *ptr, void *arg) -{ - PyArrayInterface *arrif = (PyArrayInterface *)ptr; - Py_DECREF((PyObject *)arg); + PyArrayInterface *arrif = (PyArrayInterface*)PyCapsule_GetPointer(ptr, NULL); + if (arrif == NULL) { + PyErr_WriteUnraisable(ptr); + return; + } + PyObject *context = (PyObject *)PyCapsule_GetContext(ptr); + if (context == NULL && PyErr_Occurred()) { + PyErr_WriteUnraisable(ptr); + } + Py_XDECREF(context); Py_XDECREF(arrif->descr); PyArray_free(arrif->shape); PyArray_free(arrif); } -#endif static PyObject * -gentype_struct_get(PyObject *self) +gentype_struct_get(PyObject *self, void *NPY_UNUSED(ignored)) { PyArrayObject *arr; PyArrayInterface *inter; @@ -1350,20 +1218,20 @@ gentype_struct_get(PyObject *self) } static PyObject * -gentype_priority_get(PyObject *NPY_UNUSED(self)) +gentype_priority_get(PyObject *NPY_UNUSED(self), void *NPY_UNUSED(ignored)) { return PyFloat_FromDouble(NPY_SCALAR_PRIORITY); } static PyObject * -gentype_shape_get(PyObject *NPY_UNUSED(self)) +gentype_shape_get(PyObject *NPY_UNUSED(self), void *NPY_UNUSED(ignored)) { return PyTuple_New(0); } static PyObject * -gentype_interface_get(PyObject *self) +gentype_interface_get(PyObject *self, void *NPY_UNUSED(ignored)) { PyArrayObject *arr; PyObject *inter; @@ -1383,20 +1251,20 @@ gentype_interface_get(PyObject *self) static PyObject * -gentype_typedescr_get(PyObject *self) +gentype_typedescr_get(PyObject *self, void *NPY_UNUSED(ignored)) { return (PyObject *)PyArray_DescrFromScalar(self); } static PyObject * -gentype_base_get(PyObject *NPY_UNUSED(self)) +gentype_base_get(PyObject *NPY_UNUSED(self), void *NPY_UNUSED(ignored)) { Py_RETURN_NONE; } static PyObject * -voidtype_base_get(PyVoidScalarObject *self) +voidtype_base_get(PyVoidScalarObject *self, void *NPY_UNUSED(ignored)) { if (self->base == NULL) { Py_RETURN_NONE; @@ -1427,7 +1295,7 @@ _realdescr_fromcomplexscalar(PyObject *self, int *typenum) } static PyObject * -gentype_real_get(PyObject *self) +gentype_real_get(PyObject *self, void *NPY_UNUSED(ignored)) { PyArray_Descr *typecode; PyObject *ret; @@ -1442,7 +1310,7 @@ gentype_real_get(PyObject *self) return ret; } else if (PyArray_IsScalar(self, Object)) { - PyObject *obj = ((PyObjectScalarObject *)self)->obval; + PyObject *obj = PyArrayScalar_VAL(self, Object); ret = PyObject_GetAttrString(obj, "real"); if (ret != NULL) { return ret; @@ -1454,7 +1322,7 @@ gentype_real_get(PyObject *self) } static PyObject * -gentype_imag_get(PyObject *self) +gentype_imag_get(PyObject *self, void *NPY_UNUSED(ignored)) { PyArray_Descr *typecode=NULL; PyObject *ret; @@ -1467,12 +1335,12 @@ gentype_imag_get(PyObject *self) ret = PyArray_Scalar(ptr + typecode->elsize, typecode, NULL); } else if (PyArray_IsScalar(self, Object)) { - PyObject *obj = ((PyObjectScalarObject *)self)->obval; + PyObject *obj = PyArrayScalar_VAL(self, Object); PyArray_Descr *newtype; ret = PyObject_GetAttrString(obj, "imag"); if (ret == NULL) { PyErr_Clear(); - obj = PyInt_FromLong(0); + obj = PyLong_FromLong(0); newtype = PyArray_DescrFromType(NPY_OBJECT); ret = PyArray_Scalar((char *)&obj, newtype, NULL); Py_DECREF(newtype); @@ -1484,7 +1352,7 @@ gentype_imag_get(PyObject *self) int elsize; typecode = PyArray_DescrFromScalar(self); elsize = typecode->elsize; - temp = npy_alloc_cache_zero(elsize); + temp = npy_alloc_cache_zero(1, elsize); ret = PyArray_Scalar(temp, typecode, NULL); npy_free_cache(temp, elsize); } @@ -1494,7 +1362,7 @@ gentype_imag_get(PyObject *self) } static PyObject * -gentype_flat_get(PyObject *self) +gentype_flat_get(PyObject *self, void *NPY_UNUSED(ignored)) { PyObject *ret, *arr; @@ -1509,7 +1377,7 @@ gentype_flat_get(PyObject *self) static PyObject * -gentype_transpose_get(PyObject *self) +gentype_transpose_get(PyObject *self, void *NPY_UNUSED(ignored)) { Py_INCREF(self); return self; @@ -1519,74 +1387,46 @@ gentype_transpose_get(PyObject *self) static PyGetSetDef gentype_getsets[] = { {"ndim", (getter)gentype_ndim_get, - (setter) 0, - "number of array dimensions", - NULL}, + (setter) 0, NULL, NULL}, {"flags", (getter)gentype_flags_get, - (setter)0, - "integer value of flags", - NULL}, + (setter)0, NULL, NULL}, {"shape", (getter)gentype_shape_get, - (setter)0, - "tuple of array dimensions", - NULL}, + (setter)0, NULL, NULL}, {"strides", (getter)gentype_shape_get, - (setter) 0, - "tuple of bytes steps in each dimension", - NULL}, + (setter) 0, NULL, NULL}, {"data", (getter)gentype_data_get, - (setter) 0, - "pointer to start of data", - NULL}, + (setter) 0, NULL, NULL}, {"itemsize", (getter)gentype_itemsize_get, - (setter)0, - "length of one element in bytes", - NULL}, + (setter)0, NULL, NULL}, {"size", (getter)gentype_size_get, - (setter)0, - "number of elements in the gentype", - NULL}, + (setter)0, NULL, NULL}, {"nbytes", (getter)gentype_itemsize_get, - (setter)0, - "length of item in bytes", - NULL}, + (setter)0, NULL, NULL}, {"base", (getter)gentype_base_get, - (setter)0, - "base object", - NULL}, + (setter)0, NULL, NULL}, {"dtype", (getter)gentype_typedescr_get, - NULL, - "get array data-descriptor", - NULL}, + NULL, NULL, NULL}, {"real", (getter)gentype_real_get, - (setter)0, - "real part of scalar", - NULL}, + (setter)0, NULL, NULL}, {"imag", (getter)gentype_imag_get, - (setter)0, - "imaginary part of scalar", - NULL}, + (setter)0, NULL, NULL}, {"flat", (getter)gentype_flat_get, - (setter)0, - "a 1-d view of scalar", - NULL}, + (setter)0, NULL, NULL}, {"T", (getter)gentype_transpose_get, - (setter)0, - "transpose", - NULL}, + (setter)0, NULL, NULL}, {"__array_interface__", (getter)gentype_interface_get, NULL, @@ -1675,19 +1515,6 @@ gentype_itemset(PyObject *NPY_UNUSED(self), PyObject *NPY_UNUSED(args)) return NULL; } -static PyObject * -gentype_squeeze(PyObject *self, PyObject *args) -{ - if (!PyArg_ParseTuple(args, "")) { - return NULL; - } - Py_INCREF(self); - return self; -} - -static Py_ssize_t -gentype_getreadbuf(PyObject *, Py_ssize_t, void **); - static PyObject * gentype_byteswap(PyObject *self, PyObject *args, PyObject *kwds) { @@ -1710,8 +1537,9 @@ gentype_byteswap(PyObject *self, PyObject *args, PyObject *kwds) PyObject *new; char *newmem; - gentype_getreadbuf(self, 0, (void **)&data); descr = PyArray_DescrFromScalar(self); + data = (void *)scalar_value(self, descr); + newmem = PyObject_Malloc(descr->elsize); if (newmem == NULL) { Py_DECREF(descr); @@ -1738,7 +1566,7 @@ gentype_byteswap(PyObject *self, PyObject *args, PyObject *kwds) * std, var, sum, cumsum, prod, cumprod, compress, sort, argsort, * round, argmax, argmin, max, min, ptp, any, all, astype, resize, * reshape, choose, tostring, tobytes, copy, searchsorted, view, - * flatten, ravel# + * flatten, ravel, squeeze# */ static PyObject * gentype_@name@(PyObject *self, PyObject *args, PyObject *kwds) @@ -1747,6 +1575,58 @@ gentype_@name@(PyObject *self, PyObject *args, PyObject *kwds) } /**end repeat**/ + +/**begin repeat + * #name = integer, floating, complexfloating# + * #complex = 0, 0, 1# + */ +static PyObject * +@name@type_dunder_round(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"ndigits", NULL}; + PyObject *ndigits = Py_None; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O:__round__", kwlist, &ndigits)) { + return NULL; + } + +#if @complex@ + if (DEPRECATE("The Python built-in `round` is deprecated for complex " + "scalars, and will raise a `TypeError` in a future release. " + "Use `np.round` or `scalar.round` instead.") < 0) { + return NULL; + } +#endif + + PyObject *tup; + if (ndigits == Py_None) { + tup = PyTuple_Pack(0); + } + else { + tup = PyTuple_Pack(1, ndigits); + } + + if (tup == NULL) { + return NULL; + } + + PyObject *obj = gentype_round(self, tup, NULL); + Py_DECREF(tup); + if (obj == NULL) { + return NULL; + } + +#if !@complex@ + if (ndigits == Py_None) { + PyObject *ret = PyNumber_Long(obj); + Py_DECREF(obj); + return ret; + } +#endif + + return obj; +} +/**end repeat**/ + static PyObject * voidtype_getfield(PyVoidScalarObject *self, PyObject *args, PyObject *kwds) { @@ -1845,9 +1725,7 @@ static PyObject * gentype_reduce(PyObject *self, PyObject *NPY_UNUSED(args)) { PyObject *ret = NULL, *obj = NULL, *mod = NULL; -#if defined(NPY_PY3K) Py_buffer view; -#endif const char *buffer; Py_ssize_t buflen; @@ -1857,13 +1735,7 @@ gentype_reduce(PyObject *self, PyObject *NPY_UNUSED(args)) return NULL; } -#if defined(NPY_PY3K) - if (PyArray_IsScalar(self, Unicode)) { - /* Unicode on Python 3 does not expose the buffer interface */ - buffer = PyUnicode_AS_DATA(self); - buflen = PyUnicode_GET_DATA_SIZE(self); - } - else if (PyObject_GetBuffer(self, &view, PyBUF_SIMPLE) >= 0) { + if (PyObject_GetBuffer(self, &view, PyBUF_SIMPLE) >= 0) { buffer = view.buf; buflen = view.len; /* @@ -1878,14 +1750,8 @@ gentype_reduce(PyObject *self, PyObject *NPY_UNUSED(args)) Py_DECREF(ret); return NULL; } -#else - if (PyObject_AsReadBuffer(self, (const void **)&buffer, &buflen)<0) { - Py_DECREF(ret); - return NULL; - } -#endif - mod = PyImport_ImportModule("numpy.core.multiarray"); + mod = PyImport_ImportModule("numpy.core._multiarray_umath"); if (mod == NULL) { return NULL; } @@ -1897,52 +1763,34 @@ gentype_reduce(PyObject *self, PyObject *NPY_UNUSED(args)) PyTuple_SET_ITEM(ret, 0, obj); obj = PyObject_GetAttrString((PyObject *)self, "dtype"); if (PyArray_IsScalar(self, Object)) { - mod = ((PyObjectScalarObject *)self)->obval; - PyTuple_SET_ITEM(ret, 1, Py_BuildValue("NO", obj, mod)); + PyObject *val = PyArrayScalar_VAL(self, Object); + PyObject *tup = Py_BuildValue("NO", obj, val); + if (tup == NULL) { + return NULL; + } + PyTuple_SET_ITEM(ret, 1, tup); } - else { -#ifndef Py_UNICODE_WIDE - /* - * We need to expand the buffer so that we always write - * UCS4 to disk for pickle of unicode scalars. - * - * This could be in a unicode_reduce function, but - * that would require re-factoring. - */ - int alloc = 0; - char *tmp; - int newlen; - - if (PyArray_IsScalar(self, Unicode)) { - tmp = PyArray_malloc(buflen*2); - if (tmp == NULL) { - Py_DECREF(ret); - return PyErr_NoMemory(); - } - alloc = 1; - newlen = PyUCS2Buffer_AsUCS4((Py_UNICODE *)buffer, - (npy_ucs4 *)tmp, - buflen / 2, buflen / 2); - buflen = newlen*4; - buffer = tmp; + else if (obj && PyDataType_FLAGCHK((PyArray_Descr *)obj, NPY_LIST_PICKLE)) { + /* a structured dtype with an object in a field */ + PyArrayObject *arr = (PyArrayObject *)PyArray_FromScalar(self, NULL); + if (arr == NULL) { + return NULL; } -#endif + /* Use the whole array which handles sturctured void correctly */ + PyObject *tup = Py_BuildValue("NN", obj, arr); + if (tup == NULL) { + return NULL; + } + PyTuple_SET_ITEM(ret, 1, tup); + } + else { mod = PyBytes_FromStringAndSize(buffer, buflen); if (mod == NULL) { Py_DECREF(ret); -#ifndef Py_UNICODE_WIDE - ret = NULL; - goto fail; -#else return NULL; -#endif } PyTuple_SET_ITEM(ret, 1, Py_BuildValue("NN", obj, mod)); -#ifndef Py_UNICODE_WIDE -fail: - if (alloc) PyArray_free((char *)buffer); -#endif } return ret; } @@ -1988,24 +1836,196 @@ gentype_setflags(PyObject *NPY_UNUSED(self), PyObject *NPY_UNUSED(args), Py_RETURN_NONE; } -/* - * casting complex numbers (that don't inherit from Python complex) - * to Python complex - */ - -/**begin repeat - * #name = cfloat, clongdouble# - * #Name = CFloat, CLongDouble# - */ static PyObject * -@name@_complex(PyObject *self, PyObject *NPY_UNUSED(args), - PyObject *NPY_UNUSED(kwds)) +numbertype_class_getitem_abc(PyObject *cls, PyObject *args) { - return PyComplex_FromDoubles(PyArrayScalar_VAL(self, @Name@).real, - PyArrayScalar_VAL(self, @Name@).imag); -} + PyObject *generic_alias; + +#ifdef Py_GENERICALIASOBJECT_H + Py_ssize_t args_len; + int args_len_expected; + + /* complexfloating should take 2 parameters, all others take 1 */ + if (PyType_IsSubtype((PyTypeObject *)cls, + &PyComplexFloatingArrType_Type)) { + args_len_expected = 2; + } + else { + args_len_expected = 1; + } + + args_len = PyTuple_Check(args) ? PyTuple_Size(args) : 1; + if (args_len != args_len_expected) { + return PyErr_Format(PyExc_TypeError, + "Too %s arguments for %s", + args_len > args_len_expected ? "many" : "few", + ((PyTypeObject *)cls)->tp_name); + } + generic_alias = Py_GenericAlias(cls, args); +#else + PyErr_SetString(PyExc_TypeError, + "Type subscription requires python >= 3.9"); + generic_alias = NULL; +#endif + return generic_alias; +} + +/* + * Use for concrete np.number subclasses, making them act as if they + * were subtyped from e.g. np.signedinteger[object], thus lacking any + * free subscription parameters. Requires python >= 3.9. + */ +static PyObject * +numbertype_class_getitem(PyObject *cls, PyObject *args) +{ +#ifdef Py_GENERICALIASOBJECT_H + PyErr_Format(PyExc_TypeError, + "There are no type variables left in %s", + ((PyTypeObject *)cls)->tp_name); +#else + PyErr_SetString(PyExc_TypeError, + "Type subscription requires python >= 3.9"); +#endif + return NULL; +} + +/* + * casting complex numbers (that don't inherit from Python complex) + * to Python complex + */ + +/**begin repeat + * #name = cfloat, clongdouble# + * #Name = CFloat, CLongDouble# + */ +static PyObject * +@name@_complex(PyObject *self, PyObject *NPY_UNUSED(args), + PyObject *NPY_UNUSED(kwds)) +{ + return PyComplex_FromDoubles(PyArrayScalar_VAL(self, @Name@).real, + PyArrayScalar_VAL(self, @Name@).imag); +} +/**end repeat**/ + +/**begin repeat + * #name = half, float, double, longdouble# + * #Name = Half, Float, Double, LongDouble# + * #is_half = 1,0,0,0# + * #c = f, f, , l# + * #convert = PyLong_FromDouble, PyLong_FromDouble, PyLong_FromDouble, + * npy_longdouble_to_PyLong# + * # + */ +/* Heavily copied from the builtin float.as_integer_ratio */ +static PyObject * +@name@_as_integer_ratio(PyObject *self, PyObject *NPY_UNUSED(args)) +{ +#if @is_half@ + npy_double val = npy_half_to_double(PyArrayScalar_VAL(self, @Name@)); + npy_double frac; +#else + npy_@name@ val = PyArrayScalar_VAL(self, @Name@); + npy_@name@ frac; +#endif + int exponent; + int i; + + PyObject *py_exponent = NULL; + PyObject *numerator = NULL; + PyObject *denominator = NULL; + PyObject *result_pair = NULL; + PyNumberMethods *long_methods = PyLong_Type.tp_as_number; + + if (npy_isnan(val)) { + PyErr_SetString(PyExc_ValueError, + "cannot convert NaN to integer ratio"); + return NULL; + } + if (!npy_isfinite(val)) { + PyErr_SetString(PyExc_OverflowError, + "cannot convert Infinity to integer ratio"); + return NULL; + } + + frac = npy_frexp@c@(val, &exponent); /* val == frac * 2**exponent exactly */ + + /* This relies on the floating point type being base 2 to converge */ + for (i = 0; frac != npy_floor@c@(frac); i++) { + frac *= 2.0; + exponent--; + } + + /* self == frac * 2**exponent exactly and frac is integral. */ + numerator = @convert@(frac); + if (numerator == NULL) + goto error; + denominator = PyLong_FromLong(1); + if (denominator == NULL) + goto error; + py_exponent = PyLong_FromLong(exponent < 0 ? -exponent : exponent); + if (py_exponent == NULL) + goto error; + + /* fold in 2**exponent */ + if (exponent > 0) { + PyObject *temp = long_methods->nb_lshift(numerator, py_exponent); + if (temp == NULL) + goto error; + Py_DECREF(numerator); + numerator = temp; + } + else { + PyObject *temp = long_methods->nb_lshift(denominator, py_exponent); + if (temp == NULL) + goto error; + Py_DECREF(denominator); + denominator = temp; + } + + result_pair = PyTuple_Pack(2, numerator, denominator); + +error: + Py_XDECREF(py_exponent); + Py_XDECREF(denominator); + Py_XDECREF(numerator); + return result_pair; +} +/**end repeat**/ + +/**begin repeat + * #name = half, float, double, longdouble# + * #Name = Half, Float, Double, LongDouble# + * #is_half = 1,0,0,0# + * #c = f, f, , l# + */ +static PyObject * +@name@_is_integer(PyObject *self, PyObject *NPY_UNUSED(args)) +{ +#if @is_half@ + npy_double val = npy_half_to_double(PyArrayScalar_VAL(self, @Name@)); +#else + npy_@name@ val = PyArrayScalar_VAL(self, @Name@); +#endif + PyObject *ret; + + if (npy_isnan(val)) { + Py_RETURN_FALSE; + } + if (!npy_isfinite(val)) { + Py_RETURN_FALSE; + } + + ret = (npy_floor@c@(val) == val) ? Py_True : Py_False; + Py_INCREF(ret); + return ret; +} /**end repeat**/ +static PyObject * +integer_is_integer(PyObject *self, PyObject *NPY_UNUSED(args)) { + Py_RETURN_TRUE; +} + /* * need to fill in doc-strings for these methods on import -- copy from * array docstrings @@ -2121,7 +2141,7 @@ static PyMethodDef gentype_methods[] = { METH_VARARGS | METH_KEYWORDS, NULL}, {"squeeze", (PyCFunction)gentype_squeeze, - METH_VARARGS, NULL}, + METH_VARARGS | METH_KEYWORDS, NULL}, {"view", (PyCFunction)gentype_view, METH_VARARGS | METH_KEYWORDS, NULL}, @@ -2194,12 +2214,6 @@ static PyMethodDef gentype_methods[] = { {"round", (PyCFunction)gentype_round, METH_VARARGS | METH_KEYWORDS, NULL}, -#if defined(NPY_PY3K) - /* Hook for the round() builtin */ - {"__round__", - (PyCFunction)gentype_round, - METH_VARARGS | METH_KEYWORDS, NULL}, -#endif /* For the format function */ {"__format__", gentype_format, @@ -2258,6 +2272,14 @@ static PyGetSetDef inttype_getsets[] = { {NULL, NULL, NULL, NULL, NULL} }; +static PyMethodDef numbertype_methods[] = { + /* for typing; requires python >= 3.9 */ + {"__class_getitem__", + (PyCFunction)numbertype_class_getitem_abc, + METH_CLASS | METH_O, NULL}, + {NULL, NULL, 0, NULL} /* sentinel */ +}; + /**begin repeat * #name = cfloat,clongdouble# */ @@ -2265,10 +2287,84 @@ static PyMethodDef @name@type_methods[] = { {"__complex__", (PyCFunction)@name@_complex, METH_VARARGS | METH_KEYWORDS, NULL}, + /* for typing; requires python >= 3.9 */ + {"__class_getitem__", + (PyCFunction)numbertype_class_getitem, + METH_CLASS | METH_O, NULL}, + {NULL, NULL, 0, NULL} +}; +/**end repeat**/ + +/**begin repeat + * #name = floating, complexfloating# + */ +static PyMethodDef @name@type_methods[] = { + /* Hook for the round() builtin */ + {"__round__", + (PyCFunction)@name@type_dunder_round, + METH_VARARGS | METH_KEYWORDS, NULL}, + {NULL, NULL, 0, NULL} /* sentinel */ +}; +/**end repeat**/ + +static PyMethodDef integertype_methods[] = { + /* Hook for the round() builtin */ + {"__round__", + (PyCFunction)integertype_dunder_round, + METH_VARARGS | METH_KEYWORDS, NULL}, + {"is_integer", + (PyCFunction)integer_is_integer, + METH_NOARGS, NULL}, + {NULL, NULL, 0, NULL} /* sentinel */ +}; + +/**begin repeat + * #name = half,float,double,longdouble# + */ +static PyMethodDef @name@type_methods[] = { + {"as_integer_ratio", + (PyCFunction)@name@_as_integer_ratio, + METH_NOARGS, NULL}, + {"is_integer", + (PyCFunction)@name@_is_integer, + METH_NOARGS, NULL}, + /* for typing; requires python >= 3.9 */ + {"__class_getitem__", + (PyCFunction)numbertype_class_getitem, + METH_CLASS | METH_O, NULL}, + {NULL, NULL, 0, NULL} +}; +/**end repeat**/ + +/**begin repeat + * #name = timedelta, cdouble# + */ +static PyMethodDef @name@type_methods[] = { + /* for typing; requires python >= 3.9 */ + {"__class_getitem__", + (PyCFunction)numbertype_class_getitem, + METH_CLASS | METH_O, NULL}, {NULL, NULL, 0, NULL} }; /**end repeat**/ +/**begin repeat + * #name = byte, ubyte, short, ushort, int, uint, + * long, ulong, longlong, ulonglong# + */ +static PyMethodDef @name@type_methods[] = { + /* for typing; requires python >= 3.9 */ + {"__class_getitem__", + (PyCFunction)numbertype_class_getitem, + METH_CLASS | METH_O, NULL}, + {"bit_count", + (PyCFunction)npy_@name@_bit_count, + METH_NOARGS, NULL}, + {NULL, NULL, 0, NULL} /* sentinel */ +}; +/**end repeat**/ + + /************* As_mapping functions for void array scalar ************/ static Py_ssize_t @@ -2389,7 +2485,7 @@ voidtype_ass_subscript(PyVoidScalarObject *self, PyObject *ind, PyObject *val) return -1; } - if (PyBaseString_Check(ind)) { + if (PyUnicode_Check(ind)) { /* * Much like in voidtype_setfield, we cannot simply use ndarray's * __setitem__ since assignment to void scalars should not broadcast @@ -2449,166 +2545,249 @@ fail: } static PyMappingMethods voidtype_as_mapping = { - (lenfunc)voidtype_length, /*mp_length*/ - (binaryfunc)voidtype_subscript, /*mp_subscript*/ - (objobjargproc)voidtype_ass_subscript, /*mp_ass_subscript*/ + .mp_length = (lenfunc)voidtype_length, + .mp_subscript = (binaryfunc)voidtype_subscript, + .mp_ass_subscript = (objobjargproc)voidtype_ass_subscript, }; static PySequenceMethods voidtype_as_sequence = { - (lenfunc)voidtype_length, /*sq_length*/ - 0, /*sq_concat*/ - 0, /*sq_repeat*/ - (ssizeargfunc)voidtype_item, /*sq_item*/ - 0, /*sq_slice*/ - (ssizeobjargproc)voidtype_ass_item, /*sq_ass_item*/ - 0, /* ssq_ass_slice */ - 0, /* sq_contains */ - 0, /* sq_inplace_concat */ - 0, /* sq_inplace_repeat */ + .sq_length = (lenfunc)voidtype_length, + .sq_item = (ssizeargfunc)voidtype_item, + .sq_ass_item = (ssizeobjargproc)voidtype_ass_item, }; -static Py_ssize_t -gentype_getreadbuf(PyObject *self, Py_ssize_t segment, void **ptrptr) +/* + * This function implements simple buffer export for user defined subclasses + * of `np.generic`. All other scalar types override the buffer export. + */ +static int +gentype_arrtype_getbuffer(PyObject *self, Py_buffer *view, int flags) { - int numbytes; - PyArray_Descr *outcode; + if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) { + PyErr_Format(PyExc_TypeError, + "NumPy scalar %R can only exported as a buffer without format.", + self); + return -1; + } + if ((flags & PyBUF_WRITEABLE) == PyBUF_WRITEABLE) { + PyErr_SetString(PyExc_BufferError, "scalar buffer is readonly"); + return -1; + } + PyArray_Descr *descr = PyArray_DescrFromScalar(self); + if (descr == NULL) { + return -1; + } + if (!PyDataType_ISUSERDEF(descr)) { + /* This path would also reject the (hopefully) impossible "object" */ + PyErr_Format(PyExc_TypeError, + "user-defined scalar %R registered for built-in dtype %S? " + "This should be impossible.", + self, descr); + Py_DECREF(descr); + return -1; + } + view->ndim = 0; + view->len = descr->elsize; + view->itemsize = descr->elsize; + view->shape = NULL; + view->strides = NULL; + view->suboffsets = NULL; + view->readonly = 1; /* assume general (user) scalars are readonly. */ + Py_INCREF(self); + view->obj = self; + view->buf = scalar_value(self, descr); + Py_DECREF(descr); + view->format = NULL; + return 0; +} + + +static PyBufferProcs gentype_arrtype_as_buffer = { + .bf_getbuffer = (getbufferproc)gentype_arrtype_getbuffer, +}; - if (segment != 0) { - PyErr_SetString(PyExc_SystemError, - "Accessing non-existent array segment"); + +/**begin repeat + * #name = bool, byte, short, int, long, longlong, ubyte, ushort, uint, ulong, + * ulonglong, half, float, double, longdouble, cfloat, cdouble, + * clongdouble# + * #Name = Bool, Byte, Short, Int, Long, LongLong, UByte, UShort, UInt, ULong, + * ULongLong, Half, Float, Double, LongDouble, CFloat, CDouble, + * CLongDouble# + * #NAME = BOOL, BYTE, SHORT, INT, LONG, LONGLONG, UBYTE, USHORT, UINT, ULONG, + * ULONGLONG, HALF, FLOAT, DOUBLE, LONGDOUBLE, CFLOAT, CDOUBLE, + * CLONGDOUBLE# + * #fmt = ?, b, h, i, l, q, B, H, I, L, Q, e, f, d, g, Zf, Zd, Zg# + */ + +static int +@name@_getbuffer(PyObject *self, Py_buffer *view, int flags) +{ + if ((flags & PyBUF_WRITEABLE) == PyBUF_WRITEABLE) { + PyErr_SetString(PyExc_BufferError, "scalar buffer is readonly"); return -1; } + Py@Name@ScalarObject *scalar = (Py@Name@ScalarObject *)self; + + static char fmt[3] = "@fmt@"; - outcode = PyArray_DescrFromScalar(self); - numbytes = outcode->elsize; - *ptrptr = (void *)scalar_value(self, outcode); + view->ndim = 0; + view->len = sizeof(scalar->obval); + view->itemsize = sizeof(scalar->obval); + view->shape = NULL; + view->strides = NULL; + view->suboffsets = NULL; + view->readonly = 1; + Py_INCREF(self); + view->obj = self; + view->buf = &(scalar->obval); -#ifndef Py_UNICODE_WIDE - if (outcode->type_num == NPY_UNICODE) { - numbytes >>= 1; + if ((flags & PyBUF_FORMAT) != PyBUF_FORMAT) { + /* It is unnecessary to find the correct format */ + view->format = NULL; + return 0; } -#endif - Py_DECREF(outcode); - return numbytes; + + view->format = fmt; + + return 0; } -#if !defined(NPY_PY3K) -static Py_ssize_t -gentype_getsegcount(PyObject *self, Py_ssize_t *lenp) +static PyBufferProcs @name@_arrtype_as_buffer = { + .bf_getbuffer = @name@_getbuffer, + /* No need to release the buffer */ +}; + +/**end repeat**/ + +static int +unicode_getbuffer(PyObject *self, Py_buffer *view, int flags) { - PyArray_Descr *outcode; + if ((flags & PyBUF_WRITEABLE) == PyBUF_WRITEABLE) { + PyErr_SetString(PyExc_BufferError, "scalar buffer is readonly"); + return -1; + } + PyUnicodeScalarObject *scalar = (PyUnicodeScalarObject *)self; + Py_ssize_t length = PyUnicode_GetLength(self); - outcode = PyArray_DescrFromScalar(self); - if (lenp) { - *lenp = outcode->elsize; -#ifndef Py_UNICODE_WIDE - if (outcode->type_num == NPY_UNICODE) { - *lenp >>= 1; + view->ndim = 0; + view->len = length * 4; + view->itemsize = length * 4; + view->shape = NULL; + view->strides = NULL; + view->suboffsets = NULL; + view->readonly = 1; + Py_INCREF(self); + view->obj = self; + + if (scalar->obval == NULL) { + /* + * Unicode may not have the representation available, `scalar_value` + * ensures materialization. + */ + PyArray_Descr *descr = PyArray_DescrFromType(NPY_UNICODE); + scalar_value(self, descr); + Py_DECREF(descr); + if (scalar->obval == NULL) { + /* allocating memory failed */ + Py_SETREF(view->obj, NULL); + return -1; } -#endif } - Py_DECREF(outcode); - return 1; -} + view->buf = scalar->obval; -static Py_ssize_t -gentype_getcharbuf(PyObject *self, Py_ssize_t segment, constchar **ptrptr) -{ - if (PyArray_IsScalar(self, String) || - PyArray_IsScalar(self, Unicode)) { - return gentype_getreadbuf(self, segment, (void **)ptrptr); + if ((flags & PyBUF_FORMAT) != PyBUF_FORMAT) { + /* It is unnecessary to find the correct format */ + view->format = NULL; + return 0; + } + + if (scalar->buffer_fmt != NULL) { + view->format = scalar->buffer_fmt; } else { - PyErr_SetString(PyExc_TypeError, - "Non-character array cannot be interpreted "\ - "as character buffer."); + scalar->buffer_fmt = PyMem_Malloc(22); + if (scalar->buffer_fmt == NULL) { + Py_SETREF(view->obj, NULL); + return -1; + } + PyOS_snprintf(scalar->buffer_fmt, 22, "%" NPY_INTP_FMT "w", length); + view->format = scalar->buffer_fmt; + } + + return 0; +} + +static PyBufferProcs unicode_arrtype_as_buffer = { + .bf_getbuffer = unicode_getbuffer, + /* No need to release the buffer */ +}; + + +/**begin repeat + * #name = datetime, timedelta# + * #Name = Datetime, Timedelta# + */ + +static int +@name@_getbuffer(PyObject *self, Py_buffer *view, int flags) +{ + if ((flags & PyBUF_WRITEABLE) == PyBUF_WRITEABLE) { + PyErr_SetString(PyExc_BufferError, "scalar buffer is readonly"); return -1; } + Py@Name@ScalarObject *scalar = (Py@Name@ScalarObject *)self; + + view->ndim = 1; + view->len = 8; + view->itemsize = 1; + static Py_ssize_t length = 8; + view->shape = &length; + view->strides = NULL; + view->suboffsets = NULL; + view->readonly = 1; + Py_INCREF(self); + view->obj = self; + + view->buf = &(scalar->obval); + + if ((flags & PyBUF_FORMAT) != PyBUF_FORMAT) { + /* It is unnecessary to find the correct format */ + view->format = NULL; + return 0; + } + + /* export datetime scalars as bytes (although arrays are not exported) */ + view->format = "B"; + + return 0; } -#endif /* !defined(NPY_PY3K) */ -static PyBufferProcs gentype_as_buffer = { -#if !defined(NPY_PY3K) - gentype_getreadbuf, /* bf_getreadbuffer*/ - NULL, /* bf_getwritebuffer*/ - gentype_getsegcount, /* bf_getsegcount*/ - gentype_getcharbuf, /* bf_getcharbuffer*/ -#endif - gentype_getbuffer, /* bf_getbuffer */ - NULL, /* bf_releasebuffer */ +static PyBufferProcs @name@_arrtype_as_buffer = { + .bf_getbuffer = @name@_getbuffer, + /* No need to release the buffer */ +}; + +/**end repeat**/ + +static PyBufferProcs void_arrtype_as_buffer = { + .bf_getbuffer = void_getbuffer, /* defined in buffer.c */ + /* No need to release the buffer */ }; -#if defined(NPY_PY3K) #define BASEFLAGS Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE #define LEAFFLAGS Py_TPFLAGS_DEFAULT -#else -#define BASEFLAGS Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES -#define LEAFFLAGS Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES -#endif NPY_NO_EXPORT PyTypeObject PyGenericArrType_Type = { -#if defined(NPY_PY3K) PyVarObject_HEAD_INIT(NULL, 0) -#else - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ -#endif - "numpy.generic", /* tp_name*/ - sizeof(PyObject), /* tp_basicsize*/ - 0, /* tp_itemsize */ - /* methods */ - 0, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ -#if defined(NPY_PY3K) - 0, /* tp_reserved */ -#else - 0, /* tp_compare */ -#endif - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - 0, /* tp_flags */ - 0, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - 0, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ - 0, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - 0, /* tp_del */ - 0, /* tp_version_tag */ + .tp_name = "numpy.generic", + .tp_basicsize = sizeof(PyObject), }; + static void void_dealloc(PyVoidScalarObject *v) { @@ -2617,189 +2796,198 @@ void_dealloc(PyVoidScalarObject *v) } Py_XDECREF(v->descr); Py_XDECREF(v->base); + if (_buffer_info_free(v->_buffer_info, (PyObject *)v) < 0) { + PyErr_WriteUnraisable(NULL); + } Py_TYPE(v)->tp_free(v); } + +static PyObject * +object_arrtype_alloc(PyTypeObject *type, Py_ssize_t items) +{ + /* + * Object scalars should not actually exist, if they exist we should + * consider it to be a bug. + */ + static PyObject *visibleDeprecationWarning = NULL; + npy_cache_import("numpy", "VisibleDeprecationWarning", + &visibleDeprecationWarning); + if (visibleDeprecationWarning == NULL) { + return NULL; + } + if (PyErr_WarnEx(visibleDeprecationWarning, + "Creating a NumPy object scalar. NumPy object scalars should " + "never be created. If you see this message please inform the " + "NumPy developers. Since this message should never be shown " + "this will raise a TypeError in the future.", 1) < 0) { + return NULL; + } + return gentype_alloc(type, items); +} + + static void object_arrtype_dealloc(PyObject *v) { - Py_XDECREF(((PyObjectScalarObject *)v)->obval); + Py_XDECREF(PyArrayScalar_VAL(v, Object)); Py_TYPE(v)->tp_free(v); } -/* - * string and unicode inherit from Python Type first and so GET_ITEM - * is different to get to the Python Type. - * - * ok is a work-around for a bug in complex_new that doesn't allocate - * memory from the sub-types memory allocator. - */ - -#define _WORK(num) \ - if (type->tp_bases && (PyTuple_GET_SIZE(type->tp_bases)==2)) { \ - PyTypeObject *sup; \ - /* We are inheriting from a Python type as well so \ - give it first dibs on conversion */ \ - sup = (PyTypeObject *)PyTuple_GET_ITEM(type->tp_bases, num); \ - /* Prevent recursion */ \ - if (thisfunc != sup->tp_new) { \ - robj = sup->tp_new(type, args, kwds); \ - if (robj != NULL) goto finish; \ - if (PyTuple_GET_SIZE(args)!=1) return NULL; \ - PyErr_Clear(); \ - } \ - /* now do default conversion */ \ - } - -#define _WORK1 _WORK(1) -#define _WORKz _WORK(0) -#define _WORK0 +static void +unicode_arrtype_dealloc(PyObject *v) +{ + /* note: may be null if it was never requested */ + PyMem_Free(PyArrayScalar_VAL(v, Unicode)); + PyMem_Free(((PyUnicodeScalarObject *)v)->buffer_fmt); + /* delegate to the base class */ + PyUnicode_Type.tp_dealloc(v); +} /**begin repeat * #name = byte, short, int, long, longlong, ubyte, ushort, uint, ulong, * ulonglong, half, float, double, longdouble, cfloat, cdouble, - * clongdouble, string, unicode, object# + * clongdouble, string, unicode# * #Name = Byte, Short, Int, Long, LongLong, UByte, UShort, UInt, ULong, * ULongLong, Half, Float, Double, LongDouble, CFloat, CDouble, - * CLongDouble, String, Unicode, Object# + * CLongDouble, String, Unicode# * #TYPE = BYTE, SHORT, INT, LONG, LONGLONG, UBYTE, USHORT, UINT, ULONG, * ULONGLONG, HALF, FLOAT, DOUBLE, LONGDOUBLE, CFLOAT, CDOUBLE, - * CLONGDOUBLE, STRING, UNICODE, OBJECT# - * #work = 0,0,1,1,1,0,0,0,0,0,0,0,1,0,0,0,0,z,z,0# - * #default = 0*17,1*2,2# + * CLONGDOUBLE, STRING, UNICODE# */ -#define _NPY_UNUSED2_1 -#define _NPY_UNUSED2_z -#define _NPY_UNUSED2_0 NPY_UNUSED -#define _NPY_UNUSED1_0 -#define _NPY_UNUSED1_1 -#define _NPY_UNUSED1_2 NPY_UNUSED +/* used as a pattern for testing token equality */ +#define _@TYPE@_IS_@TYPE@ static PyObject * -@name@_arrtype_new(PyTypeObject *_NPY_UNUSED1_@default@(type), PyObject *args, PyObject *_NPY_UNUSED2_@work@(kwds)) +@name@_arrtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { - PyObject *obj = NULL; - PyObject *robj; - PyArrayObject *arr; - PyArray_Descr *typecode = NULL; -#if (@work@ != 0) || (@default@ == 1) - void *thisfunc = (void *)@name@_arrtype_new; + /* allow base-class (if any) to do conversion */ +#if defined(_@TYPE@_IS_UNICODE) + PyObject *from_superclass = PyUnicode_Type.tp_new(type, args, kwds); +#elif defined(_@TYPE@_IS_STRING) + PyObject *from_superclass = PyBytes_Type.tp_new(type, args, kwds); +#elif defined(_@TYPE@_IS_DOUBLE) + PyObject *from_superclass = PyFloat_Type.tp_new(type, args, kwds); #endif -#if !(@default@ == 2) - int itemsize; - void *dest, *src; +#if defined(_@TYPE@_IS_UNICODE) || defined(_@TYPE@_IS_STRING) || defined(_@TYPE@_IS_DOUBLE) + if (from_superclass == NULL) { + /* don't clear the exception unless numpy can handle the arguments */ + if (PyTuple_GET_SIZE(args) != 1 || (kwds && PyDict_Size(kwds) != 0)) { + return NULL; + } + PyErr_Clear(); + } + else { +#if defined(_@TYPE@_IS_UNICODE) + PyArrayScalar_VAL(from_superclass, Unicode) = NULL; +#endif + return from_superclass; + } #endif - - /* - * allow base-class (if any) to do conversion - * If successful, this will jump to finish: - */ - _WORK@work@ /* TODO: include type name in error message, which is not @name@ */ - if (!PyArg_ParseTuple(args, "|O", &obj)) { + PyObject *obj = NULL; + static char *kwnames[] = {"", NULL}; /* positional-only */ + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwnames, &obj)) { return NULL; } - typecode = PyArray_DescrFromType(NPY_@TYPE@); + PyArray_Descr *typecode = PyArray_DescrFromType(NPY_@TYPE@); if (typecode == NULL) { return NULL; } - /* - * typecode is new reference and stolen by - * PyArray_FromAny but not PyArray_Scalar - */ if (obj == NULL) { -#if @default@ == 0 - robj = PyArray_Scalar(NULL, typecode, NULL); + PyObject *robj = PyArray_Scalar(NULL, typecode, NULL); + Py_DECREF(typecode); if (robj == NULL) { - Py_DECREF(typecode); return NULL; } - memset(&((Py@Name@ScalarObject *)robj)->obval, 0, sizeof(npy_@name@)); -#elif @default@ == 1 - robj = PyArray_Scalar(NULL, typecode, NULL); -#elif @default@ == 2 - Py_INCREF(Py_None); - robj = Py_None; +#if !defined(_@TYPE@_IS_STRING) && !defined(_@TYPE@_IS_UNICODE) + memset(&PyArrayScalar_VAL(robj, @Name@), 0, sizeof(npy_@name@)); #endif - Py_DECREF(typecode); - goto finish; + return robj; } - /* - * It is expected at this point that robj is a PyArrayScalar - * (even for Object Data Type) - */ - arr = (PyArrayObject *)PyArray_FromAny(obj, typecode, - 0, 0, NPY_ARRAY_FORCECAST, NULL); - if ((arr == NULL) || (PyArray_NDIM(arr) > 0)) { + /* PyArray_FromAny steals a reference, reclaim it before it's gone */ + Py_INCREF(typecode); + PyArrayObject *arr = (PyArrayObject *)PyArray_FromAny( + obj, typecode, 0, 0, NPY_ARRAY_FORCECAST, NULL); + if (arr == NULL) { + Py_DECREF(typecode); + return NULL; + } + if (PyArray_NDIM(arr) > 0) { + Py_DECREF(typecode); return (PyObject *)arr; } - /* 0-d array */ - robj = PyArray_ToScalar(PyArray_DATA(arr), arr); + + /* Convert the 0-d array to a scalar*/ + PyObject *robj = PyArray_ToScalar(PyArray_DATA(arr), arr); Py_DECREF(arr); -finish: - /* - * In OBJECT case, robj is no longer a - * PyArrayScalar at this point but the - * remaining code assumes it is - */ -#if @default@ == 2 - return robj; -#else - /* Normal return */ - if ((robj == NULL) || (Py_TYPE(robj) == type)) { + if (robj == NULL || Py_TYPE(robj) == type) { + Py_DECREF(typecode); return robj; } /* - * This return path occurs when the requested type is not created - * but another scalar object is created instead (i.e. when - * the base-class does the conversion in _WORK macro) + * `typecode` does not contain any subclass information, as it was thrown + * out by the call to `PyArray_DescrFromType` - we need to add this back. + * + * FIXME[gh-15467]: This branch is also hit for the "shadowed" builtin + * types like `longdouble` (which on platforms where they are the same size + * is shadowed by `double`), because `PyArray_FromAny` returns the + * shadowing type rather than the requested one. */ /* Need to allocate new type and copy data-area over */ + int itemsize; if (type->tp_itemsize) { itemsize = PyBytes_GET_SIZE(robj); } else { itemsize = 0; } - obj = type->tp_alloc(type, itemsize); - if (obj == NULL) { + PyObject *new_obj = type->tp_alloc(type, itemsize); + if (new_obj == NULL) { Py_DECREF(robj); + Py_DECREF(typecode); return NULL; } - /* typecode will be NULL */ - typecode = PyArray_DescrFromType(NPY_@TYPE@); - dest = scalar_value(obj, typecode); - src = scalar_value(robj, typecode); + void *dest = scalar_value(new_obj, typecode); + void *src = scalar_value(robj, typecode); Py_DECREF(typecode); -#if @default@ == 0 - *((npy_@name@ *)dest) = *((npy_@name@ *)src); -#elif @default@ == 1 /* unicode and strings */ +#if defined(_@TYPE@_IS_STRING) || defined(_@TYPE@_IS_UNICODE) if (itemsize == 0) { /* unicode */ -#if PY_VERSION_HEX >= 0x03030000 itemsize = PyUnicode_GetLength(robj) * PyUnicode_KIND(robj); -#else - itemsize = ((PyUnicodeObject *)robj)->length * sizeof(Py_UNICODE); -#endif } memcpy(dest, src, itemsize); - /* @default@ == 2 won't get here */ +#else + *((npy_@name@ *)dest) = *((npy_@name@ *)src); #endif Py_DECREF(robj); - return obj; -#endif + return new_obj; } +#undef _@TYPE@_IS_@TYPE@ + /**end repeat**/ -#undef _WORK1 -#undef _WORKz -#undef _WORK0 -#undef _WORK +static PyObject * +object_arrtype_new(PyTypeObject *NPY_UNUSED(type), PyObject *args, PyObject *kwds) +{ + PyObject *obj = Py_None; + static char *kwnames[] = {"", NULL}; /* positional-only */ + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O:object_", kwnames, &obj)) { + return NULL; + } + PyArray_Descr *typecode = PyArray_DescrFromType(NPY_OBJECT); + if (typecode == NULL) { + return NULL; + } + PyArrayObject *arr = (PyArrayObject *)PyArray_FromAny(obj, typecode, + 0, 0, NPY_ARRAY_FORCECAST, NULL); + return PyArray_Return(arr); +} /**begin repeat * #name = datetime, timedelta# @@ -2814,8 +3002,8 @@ static PyObject * PyObject *obj = NULL, *meta_obj = NULL; Py@Name@ScalarObject *ret; - /* TODO: include type name in error message, which is not @name@ */ - if (!PyArg_ParseTuple(args, "|OO", &obj, &meta_obj)) { + static char *kwnames[] = {"", "", NULL}; /* positional-only */ + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwnames, &obj, &meta_obj)) { return NULL; } @@ -2868,12 +3056,13 @@ static PyObject * /* bool->tp_new only returns Py_True or Py_False */ static PyObject * -bool_arrtype_new(PyTypeObject *NPY_UNUSED(type), PyObject *args, PyObject *NPY_UNUSED(kwds)) +bool_arrtype_new(PyTypeObject *NPY_UNUSED(type), PyObject *args, PyObject *kwds) { PyObject *obj = NULL; PyArrayObject *arr; - if (!PyArg_ParseTuple(args, "|O:bool_", &obj)) { + static char *kwnames[] = {"", NULL}; /* positional-only */ + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O:bool_", kwnames, &obj)) { return NULL; } if (obj == NULL) { @@ -2936,7 +3125,7 @@ bool_arrtype_nonzero(PyObject *a) * ulong, ulonglong# * #Name = Byte, Short, Int, Long, UByte, UShort, LongLong, UInt, * ULong, ULongLong# - * #type = PyInt_FromLong*6, PyLong_FromLongLong*1, + * #type = PyLong_FromLong*6, PyLong_FromLongLong*1, * PyLong_FromUnsignedLong*2, PyLong_FromUnsignedLongLong# */ static PyNumberMethods @name@_arrtype_as_number; @@ -2965,96 +3154,38 @@ bool_index(PyObject *a) return NULL; } else { - return PyInt_FromLong(PyArrayScalar_VAL(a, Bool)); + return PyLong_FromLong(PyArrayScalar_VAL(a, Bool)); } } /* Arithmetic methods -- only so we can override &, |, ^. */ NPY_NO_EXPORT PyNumberMethods bool_arrtype_as_number = { - 0, /* nb_add */ - 0, /* nb_subtract */ - 0, /* nb_multiply */ -#if defined(NPY_PY3K) -#else - 0, /* nb_divide */ -#endif - 0, /* nb_remainder */ - 0, /* nb_divmod */ - 0, /* nb_power */ - 0, /* nb_negative */ - 0, /* nb_positive */ - 0, /* nb_absolute */ - (inquiry)bool_arrtype_nonzero, /* nb_nonzero / nb_bool */ - 0, /* nb_invert */ - 0, /* nb_lshift */ - 0, /* nb_rshift */ - (binaryfunc)bool_arrtype_and, /* nb_and */ - (binaryfunc)bool_arrtype_xor, /* nb_xor */ - (binaryfunc)bool_arrtype_or, /* nb_or */ -#if defined(NPY_PY3K) -#else - 0, /* nb_coerce */ -#endif - 0, /* nb_int */ -#if defined(NPY_PY3K) - 0, /* nb_reserved */ -#else - 0, /* nb_long */ -#endif - 0, /* nb_float */ -#if defined(NPY_PY3K) -#else - 0, /* nb_oct */ - 0, /* nb_hex */ -#endif - /* Added in release 2.0 */ - 0, /* nb_inplace_add */ - 0, /* nb_inplace_subtract */ - 0, /* nb_inplace_multiply */ -#if defined(NPY_PY3K) -#else - 0, /* nb_inplace_divide */ -#endif - 0, /* nb_inplace_remainder */ - 0, /* nb_inplace_power */ - 0, /* nb_inplace_lshift */ - 0, /* nb_inplace_rshift */ - 0, /* nb_inplace_and */ - 0, /* nb_inplace_xor */ - 0, /* nb_inplace_or */ - /* Added in release 2.2 */ - /* The following require the Py_TPFLAGS_HAVE_CLASS flag */ - 0, /* nb_floor_divide */ - 0, /* nb_true_divide */ - 0, /* nb_inplace_floor_divide */ - 0, /* nb_inplace_true_divide */ - /* Added in release 2.5 */ - 0, /* nb_index */ + .nb_bool = (inquiry)bool_arrtype_nonzero, + .nb_and = (binaryfunc)bool_arrtype_and, + .nb_xor = (binaryfunc)bool_arrtype_xor, + .nb_or = (binaryfunc)bool_arrtype_or, }; static PyObject * -void_arrtype_new(PyTypeObject *type, PyObject *args, PyObject *NPY_UNUSED(kwds)) +void_arrtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyObject *obj, *arr; PyObject *new = NULL; - if (!PyArg_ParseTuple(args, "O:void", &obj)) { + static char *kwnames[] = {"", NULL}; /* positional-only */ + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:void", kwnames, &obj)) { return NULL; } /* * For a VOID scalar first see if obj is an integer or long * and create new memory of that size (filled with 0) for the scalar */ - if (PyLong_Check(obj) || PyInt_Check(obj) || + if (PyLong_Check(obj) || PyArray_IsScalar(obj, Integer) || (PyArray_Check(obj) && PyArray_NDIM((PyArrayObject *)obj)==0 && PyArray_ISINTEGER((PyArrayObject *)obj))) { -#if defined(NPY_PY3K) new = Py_TYPE(obj)->tp_as_number->nb_int(obj); -#else - new = Py_TYPE(obj)->tp_as_number->nb_long(obj); -#endif } if (new && PyLong_Check(new)) { PyObject *ret; @@ -3068,7 +3199,10 @@ void_arrtype_new(PyTypeObject *type, PyObject *args, PyObject *NPY_UNUSED(kwds)) (int) NPY_MAX_INT); return NULL; } - destptr = npy_alloc_cache_zero(memu); + if (memu == 0) { + memu = 1; + } + destptr = npy_alloc_cache_zero(memu, 1); if (destptr == NULL) { return PyErr_NoMemory(); } @@ -3078,7 +3212,7 @@ void_arrtype_new(PyTypeObject *type, PyObject *args, PyObject *NPY_UNUSED(kwds)) return PyErr_NoMemory(); } ((PyVoidScalarObject *)ret)->obval = destptr; - Py_SIZE((PyVoidScalarObject *)ret) = (int) memu; + Py_SET_SIZE((PyVoidScalarObject *)ret, (int) memu); ((PyVoidScalarObject *)ret)->descr = PyArray_DescrNewFromType(NPY_VOID); ((PyVoidScalarObject *)ret)->descr->elsize = (int) memu; @@ -3102,7 +3236,7 @@ void_arrtype_new(PyTypeObject *type, PyObject *args, PyObject *NPY_UNUSED(kwds)) static npy_hash_t @lname@_arrtype_hash(PyObject *obj) { - return (npy_hash_t)(((Py@name@ScalarObject *)obj)->obval); + return (npy_hash_t)(PyArrayScalar_VAL(obj, @name@)); } /**end repeat**/ @@ -3113,7 +3247,7 @@ static npy_hash_t static npy_hash_t @lname@_arrtype_hash(PyObject *obj) { - npy_hash_t x = (npy_hash_t)(((Py@name@ScalarObject *)obj)->obval); + npy_hash_t x = (npy_hash_t)(PyArrayScalar_VAL(obj, @name@)); if (x == -1) { x = -2; } @@ -3124,34 +3258,30 @@ static npy_hash_t static npy_hash_t ulong_arrtype_hash(PyObject *obj) { - PyObject * l = PyLong_FromUnsignedLong(((PyULongScalarObject*)obj)->obval); + PyObject * l = PyLong_FromUnsignedLong(PyArrayScalar_VAL(obj, ULong)); npy_hash_t x = PyObject_Hash(l); Py_DECREF(l); return x; } -#if (NPY_SIZEOF_INT != NPY_SIZEOF_LONG) || defined(NPY_PY3K) static npy_hash_t int_arrtype_hash(PyObject *obj) { - npy_hash_t x = (npy_hash_t)(((PyIntScalarObject *)obj)->obval); + npy_hash_t x = (npy_hash_t)(PyArrayScalar_VAL(obj, Int)); if (x == -1) { x = -2; } return x; } -#endif -#if defined(NPY_PY3K) static npy_hash_t long_arrtype_hash(PyObject *obj) { - PyObject * l = PyLong_FromLong(((PyLongScalarObject*)obj)->obval); + PyObject * l = PyLong_FromLong(PyArrayScalar_VAL(obj, Long)); npy_hash_t x = PyObject_Hash(l); Py_DECREF(l); return x; } -#endif /**begin repeat * #char = ,u# @@ -3162,7 +3292,7 @@ static NPY_INLINE npy_hash_t @char@longlong_arrtype_hash(PyObject *obj) { PyObject * l = PyLong_From@Word@LongLong( - ((Py@Char@LongLongScalarObject*)obj)->obval); + PyArrayScalar_VAL(obj, @Char@LongLong)); npy_hash_t x = PyObject_Hash(l); Py_DECREF(l); return x; @@ -3178,7 +3308,7 @@ static NPY_INLINE npy_hash_t static npy_hash_t @lname@_arrtype_hash(PyObject *obj) { - npy_hash_t x = (npy_hash_t)(((Py@name@ScalarObject *)obj)->obval); + npy_hash_t x = (npy_hash_t)(PyArrayScalar_VAL(obj, @name@)); if (x == -1) { x = -2; } @@ -3189,7 +3319,7 @@ static npy_hash_t @lname@_arrtype_hash(PyObject *obj) { npy_hash_t y; - npy_longlong x = (((Py@name@ScalarObject *)obj)->obval); + npy_longlong x = (PyArrayScalar_VAL(obj, @name@)); if ((x <= LONG_MAX)) { y = (npy_hash_t) x; @@ -3222,7 +3352,7 @@ static npy_hash_t static npy_hash_t @lname@_arrtype_hash(PyObject *obj) { - return _Py_HashDouble((double) ((Py@name@ScalarObject *)obj)->obval); + return Npy_HashDouble(obj, (double)PyArrayScalar_VAL(obj, @name@)); } /* borrowed from complex_hash */ @@ -3230,14 +3360,14 @@ static npy_hash_t c@lname@_arrtype_hash(PyObject *obj) { npy_hash_t hashreal, hashimag, combined; - hashreal = _Py_HashDouble((double) - (((PyC@name@ScalarObject *)obj)->obval).real); + hashreal = Npy_HashDouble( + obj, (double)PyArrayScalar_VAL(obj, C@name@).real); if (hashreal == -1) { return -1; } - hashimag = _Py_HashDouble((double) - (((PyC@name@ScalarObject *)obj)->obval).imag); + hashimag = Npy_HashDouble( + obj, (double)PyArrayScalar_VAL(obj, C@name@).imag); if (hashimag == -1) { return -1; } @@ -3252,13 +3382,14 @@ c@lname@_arrtype_hash(PyObject *obj) static npy_hash_t half_arrtype_hash(PyObject *obj) { - return _Py_HashDouble(npy_half_to_double(((PyHalfScalarObject *)obj)->obval)); + return Npy_HashDouble( + obj, npy_half_to_double(PyArrayScalar_VAL(obj, Half))); } static npy_hash_t object_arrtype_hash(PyObject *obj) { - return PyObject_Hash(((PyObjectScalarObject *)obj)->obval); + return PyObject_Hash(PyArrayScalar_VAL(obj, Object)); } /* we used to just hash the pointer */ @@ -3373,90 +3504,20 @@ object_arrtype_inplace_repeat(PyObjectScalarObject *self, Py_ssize_t count) } static PySequenceMethods object_arrtype_as_sequence = { - (lenfunc)object_arrtype_length, /*sq_length*/ - (binaryfunc)object_arrtype_concat, /*sq_concat*/ - (ssizeargfunc)object_arrtype_repeat, /*sq_repeat*/ - 0, /*sq_item*/ - 0, /*sq_slice*/ - 0, /* sq_ass_item */ - 0, /* sq_ass_slice */ - (objobjproc)object_arrtype_contains, /* sq_contains */ - (binaryfunc)object_arrtype_inplace_concat, /* sq_inplace_concat */ - (ssizeargfunc)object_arrtype_inplace_repeat, /* sq_inplace_repeat */ + .sq_length = (lenfunc)object_arrtype_length, + .sq_concat = (binaryfunc)object_arrtype_concat, + .sq_repeat = (ssizeargfunc)object_arrtype_repeat, + .sq_contains = (objobjproc)object_arrtype_contains, + .sq_inplace_concat = (binaryfunc)object_arrtype_inplace_concat, + .sq_inplace_repeat = (ssizeargfunc)object_arrtype_inplace_repeat, }; static PyMappingMethods object_arrtype_as_mapping = { - (lenfunc)object_arrtype_length, - (binaryfunc)object_arrtype_subscript, - (objobjargproc)object_arrtype_ass_subscript, + .mp_length = (lenfunc)object_arrtype_length, + .mp_subscript = (binaryfunc)object_arrtype_subscript, + .mp_ass_subscript = (objobjargproc)object_arrtype_ass_subscript, }; -#if !defined(NPY_PY3K) -static Py_ssize_t -object_arrtype_getsegcount(PyObjectScalarObject *self, Py_ssize_t *lenp) -{ - Py_ssize_t newlen; - int cnt; - PyBufferProcs *pb = Py_TYPE(self->obval)->tp_as_buffer; - - if (pb == NULL || - pb->bf_getsegcount == NULL || - (cnt = (*pb->bf_getsegcount)(self->obval, &newlen)) != 1) { - return 0; - } - if (lenp) { - *lenp = newlen; - } - return cnt; -} - -static Py_ssize_t -object_arrtype_getreadbuf(PyObjectScalarObject *self, Py_ssize_t segment, void **ptrptr) -{ - PyBufferProcs *pb = Py_TYPE(self->obval)->tp_as_buffer; - - if (pb == NULL || - pb->bf_getreadbuffer == NULL || - pb->bf_getsegcount == NULL) { - PyErr_SetString(PyExc_TypeError, - "expected a readable buffer object"); - return -1; - } - return (*pb->bf_getreadbuffer)(self->obval, segment, ptrptr); -} - -static Py_ssize_t -object_arrtype_getwritebuf(PyObjectScalarObject *self, Py_ssize_t segment, void **ptrptr) -{ - PyBufferProcs *pb = Py_TYPE(self->obval)->tp_as_buffer; - - if (pb == NULL || - pb->bf_getwritebuffer == NULL || - pb->bf_getsegcount == NULL) { - PyErr_SetString(PyExc_TypeError, - "expected a writeable buffer object"); - return -1; - } - return (*pb->bf_getwritebuffer)(self->obval, segment, ptrptr); -} - -static Py_ssize_t -object_arrtype_getcharbuf(PyObjectScalarObject *self, Py_ssize_t segment, - constchar **ptrptr) -{ - PyBufferProcs *pb = Py_TYPE(self->obval)->tp_as_buffer; - - if (pb == NULL || - pb->bf_getcharbuffer == NULL || - pb->bf_getsegcount == NULL) { - PyErr_SetString(PyExc_TypeError, - "expected a character buffer object"); - return -1; - } - return (*pb->bf_getcharbuffer)(self->obval, segment, ptrptr); -} -#endif - static int object_arrtype_getbuffer(PyObjectScalarObject *self, Py_buffer *view, int flags) { @@ -3484,14 +3545,8 @@ object_arrtype_releasebuffer(PyObjectScalarObject *self, Py_buffer *view) } static PyBufferProcs object_arrtype_as_buffer = { -#if !defined(NPY_PY3K) - (readbufferproc)object_arrtype_getreadbuf, - (writebufferproc)object_arrtype_getwritebuf, - (segcountproc)object_arrtype_getsegcount, - (charbufferproc)object_arrtype_getcharbuf, -#endif - (getbufferproc)object_arrtype_getbuffer, - (releasebufferproc)object_arrtype_releasebuffer, + .bf_getbuffer = (getbufferproc)object_arrtype_getbuffer, + .bf_releasebuffer = (releasebufferproc)object_arrtype_releasebuffer, }; static PyObject * @@ -3501,62 +3556,17 @@ object_arrtype_call(PyObjectScalarObject *obj, PyObject *args, PyObject *kwds) } NPY_NO_EXPORT PyTypeObject PyObjectArrType_Type = { -#if defined(NPY_PY3K) PyVarObject_HEAD_INIT(NULL, 0) -#else - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ -#endif - "numpy.object_", /* tp_name*/ - sizeof(PyObjectScalarObject), /* tp_basicsize*/ - 0, /* tp_itemsize */ - (destructor)object_arrtype_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ -#if defined(NPY_PY3K) - 0, /* tp_reserved */ -#else - 0, /* tp_compare */ -#endif - 0, /* tp_repr */ - 0, /* tp_as_number */ - &object_arrtype_as_sequence, /* tp_as_sequence */ - &object_arrtype_as_mapping, /* tp_as_mapping */ - 0, /* tp_hash */ - (ternaryfunc)object_arrtype_call, /* tp_call */ - 0, /* tp_str */ - (getattrofunc)object_arrtype_getattro, /* tp_getattro */ - (setattrofunc)object_arrtype_setattro, /* tp_setattro */ - &object_arrtype_as_buffer, /* tp_as_buffer */ - 0, /* tp_flags */ - 0, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - 0, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ - 0, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - 0, /* tp_del */ - 0, /* tp_version_tag */ + .tp_name = "numpy.object_", + .tp_basicsize = sizeof(PyObjectScalarObject), + .tp_alloc = object_arrtype_alloc, + .tp_dealloc = (destructor)object_arrtype_dealloc, + .tp_as_sequence = &object_arrtype_as_sequence, + .tp_as_mapping = &object_arrtype_as_mapping, + .tp_call = (ternaryfunc)object_arrtype_call, + .tp_getattro = (getattrofunc)object_arrtype_getattro, + .tp_setattro = (setattrofunc)object_arrtype_setattro, + .tp_as_buffer = &object_arrtype_as_buffer, }; static PyObject * @@ -3585,13 +3595,8 @@ gen_arrtype_subscript(PyObject *self, PyObject *key) #define NAME_bool "bool" #define NAME_void "void" -#if defined(NPY_PY3K) #define NAME_string "bytes" #define NAME_unicode "str" -#else -#define NAME_string "string" -#define NAME_unicode "unicode" -#endif /**begin repeat * #name = bool, string, unicode, void# @@ -3599,62 +3604,9 @@ gen_arrtype_subscript(PyObject *self, PyObject *key) * #ex = _,_,_,# */ NPY_NO_EXPORT PyTypeObject Py@NAME@ArrType_Type = { -#if defined(NPY_PY3K) PyVarObject_HEAD_INIT(NULL, 0) -#else - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ -#endif - "numpy." NAME_@name@ "@ex@", /* tp_name*/ - sizeof(Py@NAME@ScalarObject), /* tp_basicsize*/ - 0, /* tp_itemsize */ - 0, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ -#if defined(NPY_PY3K) - 0, /* tp_reserved */ -#else - 0, /* tp_compare */ -#endif - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - 0, /* tp_flags */ - 0, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - 0, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ - 0, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - 0, /* tp_del */ - 0, /* tp_version_tag */ + .tp_name = "numpy." NAME_@name@ "@ex@", + .tp_basicsize = sizeof(Py@NAME@ScalarObject), }; /**end repeat**/ @@ -3688,72 +3640,18 @@ NPY_NO_EXPORT PyTypeObject Py@NAME@ArrType_Type = { #define _THIS_SIZE "256" #endif NPY_NO_EXPORT PyTypeObject Py@NAME@ArrType_Type = { -#if defined(NPY_PY3K) PyVarObject_HEAD_INIT(NULL, 0) -#else - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ -#endif - "numpy.@name@" _THIS_SIZE, /* tp_name*/ - sizeof(Py@NAME@ScalarObject), /* tp_basicsize*/ - 0, /* tp_itemsize */ - 0, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ -#if defined(NPY_PY3K) - 0, /* tp_reserved */ -#else - 0, /* tp_compare */ -#endif - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - 0, /* tp_flags */ - 0, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - 0, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ - 0, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - 0, /* tp_del */ - 0, /* tp_version_tag */ + .tp_name = "numpy.@name@" _THIS_SIZE, + .tp_basicsize = sizeof(Py@NAME@ScalarObject), }; + #undef _THIS_SIZE /**end repeat**/ static PyMappingMethods gentype_as_mapping = { - NULL, - (binaryfunc)gen_arrtype_subscript, - NULL + .mp_subscript = (binaryfunc)gen_arrtype_subscript, }; @@ -3763,91 +3661,28 @@ static PyMappingMethods gentype_as_mapping = { * #CNAME = FLOAT, DOUBLE, LONGDOUBLE# */ #if NPY_BITSOF_@CNAME@ == 16 -#define _THIS_SIZE2 "16" -#define _THIS_SIZE1 "32" +#define _THIS_SIZE "32" #elif NPY_BITSOF_@CNAME@ == 32 -#define _THIS_SIZE2 "32" -#define _THIS_SIZE1 "64" +#define _THIS_SIZE "64" #elif NPY_BITSOF_@CNAME@ == 64 -#define _THIS_SIZE2 "64" -#define _THIS_SIZE1 "128" +#define _THIS_SIZE "128" #elif NPY_BITSOF_@CNAME@ == 80 -#define _THIS_SIZE2 "80" -#define _THIS_SIZE1 "160" +#define _THIS_SIZE "160" #elif NPY_BITSOF_@CNAME@ == 96 -#define _THIS_SIZE2 "96" -#define _THIS_SIZE1 "192" +#define _THIS_SIZE "192" #elif NPY_BITSOF_@CNAME@ == 128 -#define _THIS_SIZE2 "128" -#define _THIS_SIZE1 "256" +#define _THIS_SIZE "256" #elif NPY_BITSOF_@CNAME@ == 256 -#define _THIS_SIZE2 "256" -#define _THIS_SIZE1 "512" +#define _THIS_SIZE "512" #endif -#define _THIS_DOC "Composed of two " _THIS_SIZE2 " bit floats" - NPY_NO_EXPORT PyTypeObject Py@NAME@ArrType_Type = { -#if defined(NPY_PY3K) PyVarObject_HEAD_INIT(0, 0) -#else - PyObject_HEAD_INIT(0) - 0, /* ob_size */ -#endif - "numpy.@name@" _THIS_SIZE1, /* tp_name*/ - sizeof(Py@NAME@ScalarObject), /* tp_basicsize*/ - 0, /* tp_itemsize*/ - 0, /* tp_dealloc*/ - 0, /* tp_print*/ - 0, /* tp_getattr*/ - 0, /* tp_setattr*/ -#if defined(NPY_PY3K) - 0, /* tp_reserved */ -#else - 0, /* tp_compare */ -#endif - 0, /* tp_repr*/ - 0, /* tp_as_number*/ - 0, /* tp_as_sequence*/ - 0, /* tp_as_mapping*/ - 0, /* tp_hash */ - 0, /* tp_call*/ - 0, /* tp_str*/ - 0, /* tp_getattro*/ - 0, /* tp_setattro*/ - 0, /* tp_as_buffer*/ - Py_TPFLAGS_DEFAULT, /* tp_flags*/ - _THIS_DOC, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - 0, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ - 0, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - 0, /* tp_del */ - 0, /* tp_version_tag */ + .tp_name = "numpy.@name@" _THIS_SIZE, + .tp_basicsize = sizeof(Py@NAME@ScalarObject), + .tp_flags = Py_TPFLAGS_DEFAULT, }; -#undef _THIS_SIZE1 -#undef _THIS_SIZE2 -#undef _THIS_DOC +#undef _THIS_SIZE /**end repeat**/ @@ -3949,7 +3784,6 @@ initialize_casting_tables(void) } _npy_can_cast_safely_table[NPY_STRING][NPY_UNICODE] = 1; - _npy_can_cast_safely_table[NPY_BOOL][NPY_TIMEDELTA] = 1; #ifndef NPY_SIZEOF_BYTE #define NPY_SIZEOF_BYTE 1 @@ -3986,8 +3820,11 @@ initialize_casting_tables(void) _npy_can_cast_safely_table[_FROM_NUM][NPY_STRING] = 1; _npy_can_cast_safely_table[_FROM_NUM][NPY_UNICODE] = 1; - /* Allow casts from any integer to the TIMEDELTA type */ -#if @from_isint@ || @from_isuint@ +#if @from_isint@ && NPY_SIZEOF_TIMEDELTA >= _FROM_BSIZE + /* Allow casts from smaller or equal signed integers to the TIMEDELTA type */ + _npy_can_cast_safely_table[_FROM_NUM][NPY_TIMEDELTA] = 1; +#elif @from_isuint@ && NPY_SIZEOF_TIMEDELTA > _FROM_BSIZE + /* Allow casts from smaller unsigned integers to the TIMEDELTA type */ _npy_can_cast_safely_table[_FROM_NUM][NPY_TIMEDELTA] = 1; #endif @@ -4176,38 +4013,6 @@ initialize_casting_tables(void) } } -#ifndef NPY_PY3K -/* - * In python2, the `float` and `complex` types still implement the obsolete - * "tp_print" method, which uses CPython's float-printing routines to print the - * float. Numpy's float_/cfloat inherit from Python float/complex, but - * override its tp_repr and tp_str methods. In order to avoid an inconsistency - * with the inherited tp_print, we need to override it too. - * - * In python3 the tp_print method is reserved/unused. - */ -static int -doubletype_print(PyObject *o, FILE *fp, int flags) -{ - int ret; - PyObject *to_print; - if (flags & Py_PRINT_RAW) { - to_print = PyObject_Str(o); - } - else { - to_print = PyObject_Repr(o); - } - - if (to_print == NULL) { - return -1; - } - - ret = PyObject_Print(to_print, fp, Py_PRINT_RAW); - Py_DECREF(to_print); - return ret; -} -#endif - static PyNumberMethods longdoubletype_as_number; static PyNumberMethods clongdoubletype_as_number; static void init_basetypes(void); @@ -4219,7 +4024,6 @@ initialize_numeric_types(void) init_basetypes(); PyGenericArrType_Type.tp_dealloc = (destructor)gentype_dealloc; PyGenericArrType_Type.tp_as_number = &gentype_as_number; - PyGenericArrType_Type.tp_as_buffer = &gentype_as_buffer; PyGenericArrType_Type.tp_as_mapping = &gentype_as_mapping; PyGenericArrType_Type.tp_flags = BASEFLAGS; PyGenericArrType_Type.tp_methods = gentype_methods; @@ -4228,6 +4032,7 @@ initialize_numeric_types(void) PyGenericArrType_Type.tp_alloc = gentype_alloc; PyGenericArrType_Type.tp_free = (freefunc)gentype_free; PyGenericArrType_Type.tp_richcompare = gentype_richcompare; + PyGenericArrType_Type.tp_as_buffer = &gentype_arrtype_as_buffer; PyBoolArrType_Type.tp_as_number = &bool_arrtype_as_number; /* @@ -4259,12 +4064,6 @@ initialize_numeric_types(void) /**end repeat**/ -#ifndef NPY_PY3K - PyDoubleArrType_Type.tp_print = &doubletype_print; - PyCDoubleArrType_Type.tp_print = &doubletype_print; -#endif - - PyBoolArrType_Type.tp_as_number->nb_index = (unaryfunc)bool_index; PyStringArrType_Type.tp_alloc = NULL; @@ -4285,6 +4084,8 @@ initialize_numeric_types(void) PyIntegerArrType_Type.tp_getset = inttype_getsets; + PyNumberArrType_Type.tp_methods = numbertype_methods; + /**begin repeat * #NAME= Number, Integer, SignedInteger, UnsignedInteger, Inexact, * Floating, ComplexFloating, Flexible, Character# @@ -4309,8 +4110,16 @@ initialize_numeric_types(void) Py@NAME@ArrType_Type.tp_new = @name@_arrtype_new; Py@NAME@ArrType_Type.tp_richcompare = gentype_richcompare; +#define _IS_@NAME@ /* inherit string buffer */ +#if !defined(_IS_String) + Py@NAME@ArrType_Type.tp_as_buffer = &@name@_arrtype_as_buffer; +#endif +#undef _IS_@NAME@ + /**end repeat**/ + PyUnicodeArrType_Type.tp_dealloc = unicode_arrtype_dealloc; + /**begin repeat * #name = bool, byte, short, ubyte, ushort, uint, ulong, ulonglong, * half, float, longdouble, cfloat, clongdouble, void, object, @@ -4325,28 +4134,53 @@ initialize_numeric_types(void) /**end repeat**/ /**begin repeat - * #name = cfloat, clongdouble# - * #NAME = CFloat, CLongDouble# + * #name = cfloat, clongdouble, floating, integer, complexfloating# + * #NAME = CFloat, CLongDouble, Floating, Integer, ComplexFloating# */ Py@NAME@ArrType_Type.tp_methods = @name@type_methods; /**end repeat**/ -#if (NPY_SIZEOF_INT != NPY_SIZEOF_LONG) || defined(NPY_PY3K) + /**begin repeat + * #name = byte, short, int, long, longlong, + * ubyte, ushort, uint, ulong, ulonglong# + * #Name = Byte, Short, Int, Long, LongLong, + * UByte, UShort, UInt, ULong, ULongLong# + */ + + Py@Name@ArrType_Type.tp_methods = @name@type_methods; + + /**end repeat**/ + + /**begin repeat + * #name = half, float, double, longdouble# + * #Name = Half, Float, Double, LongDouble# + */ + + Py@Name@ArrType_Type.tp_methods = @name@type_methods; + + /**end repeat**/ + + /**begin repeat + * #name = byte, short, int, long, longlong, ubyte, ushort, + * uint, ulong, ulonglong, timedelta, cdouble# + * #Name = Byte, Short, Int, Long, LongLong, UByte, UShort, + * UInt, ULong, ULongLong, Timedelta, CDouble# + */ + + Py@Name@ArrType_Type.tp_methods = @name@type_methods; + + /**end repeat**/ + /* We won't be inheriting from Python Int type. */ PyIntArrType_Type.tp_hash = int_arrtype_hash; -#endif -#if defined(NPY_PY3K) /* We won't be inheriting from Python Int type. */ PyLongArrType_Type.tp_hash = long_arrtype_hash; -#endif -#if (NPY_SIZEOF_LONG != NPY_SIZEOF_LONGLONG) || defined(NPY_PY3K) /* We won't be inheriting from Python Int type. */ PyLongLongArrType_Type.tp_hash = longlong_arrtype_hash; -#endif /**begin repeat * #name = repr, str# @@ -4389,14 +4223,7 @@ initialize_numeric_types(void) * does not return a normal Python type */ @char@longdoubletype_as_number.nb_float = @char@longdoubletype_float; -#if defined(NPY_PY3K) @char@longdoubletype_as_number.nb_int = @char@longdoubletype_long; -#else - @char@longdoubletype_as_number.nb_int = @char@longdoubletype_int; - @char@longdoubletype_as_number.nb_long = @char@longdoubletype_long; - @char@longdoubletype_as_number.nb_hex = @char@longdoubletype_hex; - @char@longdoubletype_as_number.nb_oct = @char@longdoubletype_oct; -#endif Py@CHAR@LongDoubleArrType_Type.tp_as_number = &@char@longdoubletype_as_number; Py@CHAR@LongDoubleArrType_Type.tp_repr = @char@longdoubletype_repr; @@ -4409,6 +4236,36 @@ initialize_numeric_types(void) PyArrayIter_Type.tp_iter = PyObject_SelfIter; PyArrayMapIter_Type.tp_iter = PyObject_SelfIter; + + /* + * Give types different names when they are the same size (gh-9799). + * `np.intX` always refers to the first int of that size in the sequence + * `['LONG', 'LONGLONG', 'INT', 'SHORT', 'BYTE']`. + */ +#if (NPY_SIZEOF_BYTE == NPY_SIZEOF_SHORT) + PyByteArrType_Type.tp_name = "numpy.byte"; + PyUByteArrType_Type.tp_name = "numpy.ubyte"; +#endif +#if (NPY_SIZEOF_SHORT == NPY_SIZEOF_INT) + PyShortArrType_Type.tp_name = "numpy.short"; + PyUShortArrType_Type.tp_name = "numpy.ushort"; +#endif +#if (NPY_SIZEOF_INT == NPY_SIZEOF_LONG) + PyIntArrType_Type.tp_name = "numpy.intc"; + PyUIntArrType_Type.tp_name = "numpy.uintc"; +#endif +#if (NPY_SIZEOF_LONGLONG == NPY_SIZEOF_LONG) + PyLongLongArrType_Type.tp_name = "numpy.longlong"; + PyULongLongArrType_Type.tp_name = "numpy.ulonglong"; +#endif + + /* + Do the same for longdouble + */ +#if (NPY_SIZEOF_LONGDOUBLE == NPY_SIZEOF_DOUBLE) + PyLongDoubleArrType_Type.tp_name = "numpy.longdouble"; + PyCLongDoubleArrType_Type.tp_name = "numpy.clongdouble"; +#endif } typedef struct { diff --git a/numpy/core/src/multiarray/scalartypes.h b/numpy/core/src/multiarray/scalartypes.h index 83b188128aaf..95a2f66c6fbc 100644 --- a/numpy/core/src/multiarray/scalartypes.h +++ b/numpy/core/src/multiarray/scalartypes.h @@ -1,5 +1,5 @@ -#ifndef _NPY_SCALARTYPES_H_ -#define _NPY_SCALARTYPES_H_ +#ifndef NUMPY_CORE_SRC_MULTIARRAY_SCALARTYPES_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_SCALARTYPES_H_ /* Internal look-up tables */ extern NPY_NO_EXPORT unsigned char @@ -19,13 +19,8 @@ initialize_casting_tables(void); NPY_NO_EXPORT void initialize_numeric_types(void); -#if PY_VERSION_HEX >= 0x03000000 NPY_NO_EXPORT void gentype_struct_free(PyObject *ptr); -#else -NPY_NO_EXPORT void -gentype_struct_free(void *ptr, void *arg); -#endif NPY_NO_EXPORT int is_anyscalar_exact(PyObject *obj); @@ -36,4 +31,4 @@ _typenum_fromtypeobj(PyObject *type, int user); NPY_NO_EXPORT void * scalar_value(PyObject *scalar, PyArray_Descr *descr); -#endif +#endif /* NUMPY_CORE_SRC_MULTIARRAY_SCALARTYPES_H_ */ diff --git a/numpy/core/src/multiarray/sequence.c b/numpy/core/src/multiarray/sequence.c index 4769bdad9af2..8db0690a1d75 100644 --- a/numpy/core/src/multiarray/sequence.c +++ b/numpy/core/src/multiarray/sequence.c @@ -1,9 +1,10 @@ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE + #define PY_SSIZE_T_CLEAN #include <Python.h> -#include "structmember.h" +#include <structmember.h> -#define NPY_NO_DEPRECATED_API NPY_API_VERSION -#define _MULTIARRAYMODULE #include "numpy/arrayobject.h" #include "numpy/arrayscalars.h" @@ -38,16 +39,36 @@ array_contains(PyArrayObject *self, PyObject *el) if (res == NULL) { return -1; } + any = PyArray_Any((PyArrayObject *)res, NPY_MAXDIMS, NULL); Py_DECREF(res); + if (any == NULL) { + return -1; + } + ret = PyObject_IsTrue(any); Py_DECREF(any); return ret; } +static PyObject * +array_concat(PyObject *self, PyObject *other) +{ + /* + * Throw a type error, when trying to concat NDArrays + * NOTE: This error is not Thrown when running with PyPy + */ + PyErr_SetString(PyExc_TypeError, + "Concatenation operation is not implemented for NumPy arrays, " + "use np.concatenate() instead. Please do not rely on this error; " + "it may not be given on all Python implementations."); + return NULL; +} + + NPY_NO_EXPORT PySequenceMethods array_as_sequence = { (lenfunc)array_length, /*sq_length*/ - (binaryfunc)NULL, /*sq_concat is handled by nb_add*/ + (binaryfunc)array_concat, /*sq_concat for operator.concat*/ (ssizeargfunc)NULL, (ssizeargfunc)array_item, (ssizessizeargfunc)NULL, diff --git a/numpy/core/src/multiarray/sequence.h b/numpy/core/src/multiarray/sequence.h index b28c50d975ca..aff6aeb7ea97 100644 --- a/numpy/core/src/multiarray/sequence.h +++ b/numpy/core/src/multiarray/sequence.h @@ -1,6 +1,6 @@ -#ifndef _NPY_ARRAY_SEQUENCE_H_ -#define _NPY_ARRAY_SEQUENCE_H_ +#ifndef NUMPY_CORE_SRC_MULTIARRAY_SEQUENCE_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_SEQUENCE_H_ extern NPY_NO_EXPORT PySequenceMethods array_as_sequence; -#endif +#endif /* NUMPY_CORE_SRC_MULTIARRAY_SEQUENCE_H_ */ diff --git a/numpy/core/src/multiarray/shape.c b/numpy/core/src/multiarray/shape.c index 3ac71e285b13..162abd6a49c8 100644 --- a/numpy/core/src/multiarray/shape.c +++ b/numpy/core/src/multiarray/shape.c @@ -1,9 +1,10 @@ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE + #define PY_SSIZE_T_CLEAN #include <Python.h> -#include "structmember.h" +#include <structmember.h> -#define NPY_NO_DEPRECATED_API NPY_API_VERSION -#define _MULTIARRAYMODULE #include "numpy/arrayobject.h" #include "numpy/arrayscalars.h" @@ -26,7 +27,7 @@ static int _fix_unknown_dimension(PyArray_Dims *newshape, PyArrayObject *arr); static int -_attempt_nocopy_reshape(PyArrayObject *self, int newnd, npy_intp* newdims, +_attempt_nocopy_reshape(PyArrayObject *self, int newnd, const npy_intp *newdims, npy_intp *newstrides, int is_f_order); static void @@ -40,11 +41,11 @@ _putzero(char *optr, PyObject *zero, PyArray_Descr *dtype); */ NPY_NO_EXPORT PyObject * PyArray_Resize(PyArrayObject *self, PyArray_Dims *newshape, int refcheck, - NPY_ORDER order) + NPY_ORDER NPY_UNUSED(order)) { npy_intp oldnbytes, newnbytes; npy_intp oldsize, newsize; - int new_nd=newshape->len, k, n, elsize; + int new_nd=newshape->len, k, elsize; int refcnt; npy_intp* new_dimensions=newshape->ptr; npy_intp new_strides[NPY_MAXDIMS]; @@ -89,11 +90,19 @@ PyArray_Resize(PyArrayObject *self, PyArray_Dims *newshape, int refcheck, return NULL; } + if (PyArray_BASE(self) != NULL + || (((PyArrayObject_fields *)self)->weakreflist != NULL)) { + PyErr_SetString(PyExc_ValueError, + "cannot resize an array that " + "references or is referenced\n" + "by another array in this way. Use the np.resize function."); + return NULL; + } if (refcheck) { #ifdef PYPY_VERSION PyErr_SetString(PyExc_ValueError, "cannot resize an array with refcheck=True on PyPy.\n" - "Use the resize function or refcheck=False"); + "Use the np.resize function or refcheck=False"); return NULL; #else refcnt = PyArray_REFCOUNT(self); @@ -102,19 +111,26 @@ PyArray_Resize(PyArrayObject *self, PyArray_Dims *newshape, int refcheck, else { refcnt = 1; } - if ((refcnt > 2) - || (PyArray_BASE(self) != NULL) - || (((PyArrayObject_fields *)self)->weakreflist != NULL)) { + if (refcnt > 2) { PyErr_SetString(PyExc_ValueError, "cannot resize an array that " "references or is referenced\n" - "by another array in this way. Use the resize function"); + "by another array in this way.\n" + "Use the np.resize function or refcheck=False"); return NULL; } /* Reallocate space if needed - allocating 0 is forbidden */ - new_data = PyDataMem_RENEW( - PyArray_DATA(self), newnbytes == 0 ? elsize : newnbytes); + PyObject *handler = PyArray_HANDLER(self); + if (handler == NULL) { + /* This can happen if someone arbitrarily sets NPY_ARRAY_OWNDATA */ + PyErr_SetString(PyExc_RuntimeError, + "no memory handler found but OWNDATA flag set"); + return NULL; + } + new_data = PyDataMem_UserRENEW(PyArray_DATA(self), + newnbytes == 0 ? elsize : newnbytes, + handler); if (new_data == NULL) { PyErr_SetString(PyExc_MemoryError, "cannot allocate memory for array"); @@ -126,11 +142,11 @@ PyArray_Resize(PyArrayObject *self, PyArray_Dims *newshape, int refcheck, if (newnbytes > oldnbytes && PyArray_ISWRITEABLE(self)) { /* Fill new memory with zeros */ if (PyDataType_FLAGCHK(PyArray_DESCR(self), NPY_ITEM_REFCOUNT)) { - PyObject *zero = PyInt_FromLong(0); + PyObject *zero = PyLong_FromLong(0); char *optr; optr = PyArray_BYTES(self) + oldnbytes; - n = newsize - oldsize; - for (k = 0; k < n; k++) { + npy_intp n_new = newsize - oldsize; + for (npy_intp i = 0; i < n_new; i++) { _putzero((char *)optr, zero, PyArray_DESCR(self)); optr += elsize; } @@ -310,7 +326,7 @@ _putzero(char *optr, PyObject *zero, PyArray_Descr *dtype) int offset; Py_ssize_t pos = 0; while (PyDict_Next(dtype->fields, &pos, &key, &value)) { - if NPY_TITLE_KEY(key, value) { + if (NPY_TITLE_KEY(key, value)) { continue; } if (!PyArg_ParseTuple(value, "Oi|O", &new, &offset, &title)) { @@ -325,7 +341,7 @@ _putzero(char *optr, PyObject *zero, PyArray_Descr *dtype) for (i = 0; i < nsize; i++) { Py_INCREF(zero); - NPY_COPY_PYOBJECT_PTR(optr, &zero); + memcpy(optr, &zero, sizeof(zero)); optr += sizeof(zero); } } @@ -354,7 +370,7 @@ _putzero(char *optr, PyObject *zero, PyArray_Descr *dtype) * stride of the next-fastest index. */ static int -_attempt_nocopy_reshape(PyArrayObject *self, int newnd, npy_intp* newdims, +_attempt_nocopy_reshape(PyArrayObject *self, int newnd, const npy_intp *newdims, npy_intp *newstrides, int is_f_order) { int oldnd; @@ -451,14 +467,12 @@ _attempt_nocopy_reshape(PyArrayObject *self, int newnd, npy_intp* newdims, static void raise_reshape_size_mismatch(PyArray_Dims *newshape, PyArrayObject *arr) { - PyObject *msg = PyUString_FromFormat("cannot reshape array of size %zd " - "into shape ", PyArray_SIZE(arr)); PyObject *tmp = convert_shape_to_string(newshape->len, newshape->ptr, ""); - - PyUString_ConcatAndDel(&msg, tmp); - if (msg != NULL) { - PyErr_SetObject(PyExc_ValueError, msg); - Py_DECREF(msg); + if (tmp != NULL) { + PyErr_Format(PyExc_ValueError, + "cannot reshape array of size %zd into shape %S", + PyArray_SIZE(arr), tmp); + Py_DECREF(tmp); } } @@ -759,7 +773,7 @@ static int _npy_stride_sort_item_comparator(const void *a, const void *b) * [(2, 12), (0, 4), (1, -2)]. */ NPY_NO_EXPORT void -PyArray_CreateSortedStridePerm(int ndim, npy_intp *strides, +PyArray_CreateSortedStridePerm(int ndim, npy_intp const *strides, npy_stride_sort_item *out_strideperm) { int i; @@ -972,55 +986,6 @@ PyArray_Flatten(PyArrayObject *a, NPY_ORDER order) return (PyObject *)ret; } -/* See shape.h for parameters documentation */ -NPY_NO_EXPORT PyObject * -build_shape_string(npy_intp n, npy_intp *vals) -{ - npy_intp i; - PyObject *ret, *tmp; - - /* - * Negative dimension indicates "newaxis", which can - * be discarded for printing if it's a leading dimension. - * Find the first non-"newaxis" dimension. - */ - i = 0; - while (i < n && vals[i] < 0) { - ++i; - } - - if (i == n) { - return PyUString_FromFormat("()"); - } - else { - ret = PyUString_FromFormat("(%" NPY_INTP_FMT, vals[i++]); - if (ret == NULL) { - return NULL; - } - } - - for (; i < n; ++i) { - if (vals[i] < 0) { - tmp = PyUString_FromString(",newaxis"); - } - else { - tmp = PyUString_FromFormat(",%" NPY_INTP_FMT, vals[i]); - } - if (tmp == NULL) { - Py_DECREF(ret); - return NULL; - } - - PyUString_ConcatAndDel(&ret, tmp); - if (ret == NULL) { - return NULL; - } - } - - tmp = PyUString_FromFormat(")"); - PyUString_ConcatAndDel(&ret, tmp); - return ret; -} /*NUMPY_API * @@ -1041,7 +1006,7 @@ build_shape_string(npy_intp n, npy_intp *vals) * from a reduction result once its computation is complete. */ NPY_NO_EXPORT void -PyArray_RemoveAxesInPlace(PyArrayObject *arr, npy_bool *flags) +PyArray_RemoveAxesInPlace(PyArrayObject *arr, const npy_bool *flags) { PyArrayObject_fields *fa = (PyArrayObject_fields *)arr; npy_intp *shape = fa->dimensions, *strides = fa->strides; diff --git a/numpy/core/src/multiarray/shape.h b/numpy/core/src/multiarray/shape.h index 0451a463e5fa..bef386ed136f 100644 --- a/numpy/core/src/multiarray/shape.h +++ b/numpy/core/src/multiarray/shape.h @@ -1,12 +1,5 @@ -#ifndef _NPY_ARRAY_SHAPE_H_ -#define _NPY_ARRAY_SHAPE_H_ - -/* - * Builds a string representation of the shape given in 'vals'. - * A negative value in 'vals' gets interpreted as newaxis. - */ -NPY_NO_EXPORT PyObject * -build_shape_string(npy_intp n, npy_intp *vals); +#ifndef NUMPY_CORE_SRC_MULTIARRAY_SHAPE_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_SHAPE_H_ /* * Creates a sorted stride perm matching the KEEPORDER behavior @@ -28,4 +21,4 @@ PyArray_CreateMultiSortedStridePerm(int narrays, PyArrayObject **arrays, NPY_NO_EXPORT PyObject * PyArray_SqueezeSelected(PyArrayObject *self, npy_bool *axis_flags); -#endif +#endif /* NUMPY_CORE_SRC_MULTIARRAY_SHAPE_H_ */ diff --git a/numpy/core/src/multiarray/strfuncs.c b/numpy/core/src/multiarray/strfuncs.c index 495d897b23e4..ba457f4f41d4 100644 --- a/numpy/core/src/multiarray/strfuncs.c +++ b/numpy/core/src/multiarray/strfuncs.c @@ -1,16 +1,29 @@ #define NPY_NO_DEPRECATED_API NPY_API_VERSION #define _MULTIARRAYMODULE +#define PY_SSIZE_T_CLEAN #include <Python.h> -#include <numpy/arrayobject.h> +#include "numpy/arrayobject.h" #include "npy_pycompat.h" - +#include "npy_import.h" #include "strfuncs.h" static PyObject *PyArray_StrFunction = NULL; static PyObject *PyArray_ReprFunction = NULL; + +static void +npy_PyErr_SetStringChained(PyObject *type, const char *message) +{ + PyObject *exc, *val, *tb; + + PyErr_Fetch(&exc, &val, &tb); + PyErr_SetString(type, message); + npy_PyErr_ChainExceptionsCause(exc, val, tb); +} + + /*NUMPY_API * Set the array print function to be a Python function. */ @@ -36,168 +49,52 @@ PyArray_SetStringFunction(PyObject *op, int repr) } -/* - * Extend string. On failure, returns NULL and leaves *strp alone. - * XXX we do this in multiple places; time for a string library? - */ -static char * -extend_str(char **strp, Py_ssize_t n, Py_ssize_t *maxp) -{ - char *str = *strp; - Py_ssize_t new_cap; - - if (n >= *maxp - 16) { - new_cap = *maxp * 2; - - if (new_cap <= *maxp) { /* overflow */ - return NULL; - } - str = PyArray_realloc(*strp, new_cap); - if (str != NULL) { - *strp = str; - *maxp = new_cap; - } - } - return str; -} - - -static int -dump_data(char **string, Py_ssize_t *n, Py_ssize_t *max_n, char *data, int nd, - npy_intp *dimensions, npy_intp *strides, PyArrayObject* self) -{ - PyObject *op = NULL, *sp = NULL; - char *ostring; - npy_intp i, N, ret = 0; - -#define CHECK_MEMORY do { \ - if (extend_str(string, *n, max_n) == NULL) { \ - ret = -1; \ - goto end; \ - } \ - } while (0) - - if (nd == 0) { - if ((op = PyArray_GETITEM(self, data)) == NULL) { - return -1; - } - sp = PyObject_Repr(op); - if (sp == NULL) { - ret = -1; - goto end; - } - ostring = PyString_AsString(sp); - N = PyString_Size(sp)*sizeof(char); - *n += N; - CHECK_MEMORY; - memmove(*string + (*n - N), ostring, N); - } - else { - CHECK_MEMORY; - (*string)[*n] = '['; - *n += 1; - for (i = 0; i < dimensions[0]; i++) { - if (dump_data(string, n, max_n, - data + (*strides)*i, - nd - 1, dimensions + 1, - strides + 1, self) < 0) { - return -1; - } - CHECK_MEMORY; - if (i < dimensions[0] - 1) { - (*string)[*n] = ','; - (*string)[*n+1] = ' '; - *n += 2; - } - } - CHECK_MEMORY; - (*string)[*n] = ']'; - *n += 1; - } - -#undef CHECK_MEMORY - -end: - Py_XDECREF(op); - Py_XDECREF(sp); - return ret; -} - - -static PyObject * -array_repr_builtin(PyArrayObject *self, int repr) -{ - PyObject *ret; - char *string; - /* max_n initial value is arbitrary, dump_data will extend it */ - Py_ssize_t n = 0, max_n = PyArray_NBYTES(self) * 4 + 7; - - if ((string = PyArray_malloc(max_n)) == NULL) { - return PyErr_NoMemory(); - } - - if (dump_data(&string, &n, &max_n, PyArray_DATA(self), - PyArray_NDIM(self), PyArray_DIMS(self), - PyArray_STRIDES(self), self) < 0) { - PyArray_free(string); - return NULL; - } - - if (repr) { - if (PyArray_ISEXTENDED(self)) { - ret = PyUString_FromFormat("array(%s, '%c%d')", - string, - PyArray_DESCR(self)->type, - PyArray_DESCR(self)->elsize); - } - else { - ret = PyUString_FromFormat("array(%s, '%c')", - string, - PyArray_DESCR(self)->type); - } - } - else { - ret = PyUString_FromStringAndSize(string, n); - } - - PyArray_free(string); - return ret; -} - - NPY_NO_EXPORT PyObject * array_repr(PyArrayObject *self) { - PyObject *s, *arglist; + static PyObject *repr = NULL; - if (PyArray_ReprFunction == NULL) { - s = array_repr_builtin(self, 1); + if (PyArray_ReprFunction != NULL) { + return PyObject_CallFunctionObjArgs(PyArray_ReprFunction, self, NULL); } - else { - arglist = Py_BuildValue("(O)", self); - s = PyEval_CallObject(PyArray_ReprFunction, arglist); - Py_DECREF(arglist); + + /* + * We need to do a delayed import here as initialization on module load + * leads to circular import problems. + */ + npy_cache_import("numpy.core.arrayprint", "_default_array_repr", &repr); + if (repr == NULL) { + npy_PyErr_SetStringChained(PyExc_RuntimeError, + "Unable to configure default ndarray.__repr__"); + return NULL; } - return s; + return PyObject_CallFunctionObjArgs(repr, self, NULL); } NPY_NO_EXPORT PyObject * array_str(PyArrayObject *self) { - PyObject *s, *arglist; + static PyObject *str = NULL; - if (PyArray_StrFunction == NULL) { - s = array_repr_builtin(self, 0); + if (PyArray_StrFunction != NULL) { + return PyObject_CallFunctionObjArgs(PyArray_StrFunction, self, NULL); } - else { - arglist = Py_BuildValue("(O)", self); - s = PyEval_CallObject(PyArray_StrFunction, arglist); - Py_DECREF(arglist); + + /* + * We need to do a delayed import here as initialization on module load leads + * to circular import problems. + */ + npy_cache_import("numpy.core.arrayprint", "_default_array_str", &str); + if (str == NULL) { + npy_PyErr_SetStringChained(PyExc_RuntimeError, + "Unable to configure default ndarray.__str__"); + return NULL; } - return s; + return PyObject_CallFunctionObjArgs(str, self, NULL); } + NPY_NO_EXPORT PyObject * array_format(PyArrayObject *self, PyObject *args) { @@ -225,35 +122,3 @@ array_format(PyArrayObject *self, PyObject *args) ); } } - -#ifndef NPY_PY3K - -NPY_NO_EXPORT PyObject * -array_unicode(PyArrayObject *self) -{ - PyObject *uni; - - if (PyArray_NDIM(self) == 0) { - PyObject *item = PyArray_ToScalar(PyArray_DATA(self), self); - if (item == NULL){ - return NULL; - } - - /* defer to invoking `unicode` on the scalar */ - uni = PyObject_CallFunctionObjArgs( - (PyObject *)&PyUnicode_Type, item, NULL); - Py_DECREF(item); - } - else { - /* Do what unicode(self) would normally do */ - PyObject *str = PyObject_Str((PyObject *)self); - if (str == NULL){ - return NULL; - } - uni = PyUnicode_FromObject(str); - Py_DECREF(str); - } - return uni; -} - -#endif diff --git a/numpy/core/src/multiarray/strfuncs.h b/numpy/core/src/multiarray/strfuncs.h index 7e869d926da9..134b56ed3fef 100644 --- a/numpy/core/src/multiarray/strfuncs.h +++ b/numpy/core/src/multiarray/strfuncs.h @@ -1,5 +1,5 @@ -#ifndef _NPY_ARRAY_STRFUNCS_H_ -#define _NPY_ARRAY_STRFUNCS_H_ +#ifndef NUMPY_CORE_SRC_MULTIARRAY_STRFUNCS_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_STRFUNCS_H_ NPY_NO_EXPORT void PyArray_SetStringFunction(PyObject *op, int repr); @@ -13,9 +13,4 @@ array_str(PyArrayObject *self); NPY_NO_EXPORT PyObject * array_format(PyArrayObject *self, PyObject *args); -#ifndef NPY_PY3K - NPY_NO_EXPORT PyObject * - array_unicode(PyArrayObject *self); -#endif - -#endif +#endif /* NUMPY_CORE_SRC_MULTIARRAY_STRFUNCS_H_ */ diff --git a/numpy/core/src/multiarray/temp_elide.c b/numpy/core/src/multiarray/temp_elide.c index 3d2f976f256e..f615aa3360e6 100644 --- a/numpy/core/src/multiarray/temp_elide.c +++ b/numpy/core/src/multiarray/temp_elide.c @@ -1,8 +1,9 @@ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE + #define PY_SSIZE_T_CLEAN #include <Python.h> -#define NPY_NO_DEPRECATED_API NPY_API_VERSION -#define _MULTIARRAYMODULE #include "npy_config.h" #include "numpy/arrayobject.h" @@ -62,12 +63,8 @@ #define NPY_ELIDE_DEBUG 0 #define NPY_MAX_STACKSIZE 10 -#if PY_VERSION_HEX >= 0x03060000 /* TODO can pep523 be used to somehow? */ #define PYFRAMEEVAL_FUNC "_PyEval_EvalFrameDefault" -#else -#define PYFRAMEEVAL_FUNC "PyEval_EvalFrameEx" -#endif /* * Heuristic size of the array in bytes at which backtrace overhead generation * becomes less than speed gained by in-place operations. Depends on stack depth @@ -166,7 +163,7 @@ check_callers(int * cannot) return 0; } /* get multiarray base address */ - if (dladdr(&PyArray_SetNumericOps, &info)) { + if (dladdr(&PyArray_INCREF, &info)) { pos_ma_start = info.dli_fbase; pos_ma_end = info.dli_fbase; } @@ -278,13 +275,14 @@ check_callers(int * cannot) * "cannot" is set to true if it cannot be done even with swapped arguments */ static int -can_elide_temp(PyArrayObject * alhs, PyObject * orhs, int * cannot) +can_elide_temp(PyObject *olhs, PyObject *orhs, int *cannot) { /* * to be a candidate the array needs to have reference count 1, be an exact * array of a basic type, own its data and size larger than threshold */ - if (Py_REFCNT(alhs) != 1 || !PyArray_CheckExact(alhs) || + PyArrayObject *alhs = (PyArrayObject *)olhs; + if (Py_REFCNT(olhs) != 1 || !PyArray_CheckExact(olhs) || !PyArray_ISNUMBER(alhs) || !PyArray_CHKFLAGS(alhs, NPY_ARRAY_OWNDATA) || !PyArray_ISWRITEABLE(alhs) || @@ -332,22 +330,22 @@ can_elide_temp(PyArrayObject * alhs, PyObject * orhs, int * cannot) * try eliding a binary op, if commutative is true also try swapped arguments */ NPY_NO_EXPORT int -try_binary_elide(PyArrayObject * m1, PyObject * m2, +try_binary_elide(PyObject * m1, PyObject * m2, PyObject * (inplace_op)(PyArrayObject * m1, PyObject * m2), PyObject ** res, int commutative) { /* set when no elision can be done independent of argument order */ int cannot = 0; if (can_elide_temp(m1, m2, &cannot)) { - *res = inplace_op(m1, m2); + *res = inplace_op((PyArrayObject *)m1, m2); #if NPY_ELIDE_DEBUG != 0 puts("elided temporary in binary op"); #endif return 1; } else if (commutative && !cannot) { - if (can_elide_temp((PyArrayObject *)m2, (PyObject *)m1, &cannot)) { - *res = inplace_op((PyArrayObject *)m2, (PyObject *)m1); + if (can_elide_temp(m2, m1, &cannot)) { + *res = inplace_op((PyArrayObject *)m2, m1); #if NPY_ELIDE_DEBUG != 0 puts("elided temporary in commutative binary op"); #endif diff --git a/numpy/core/src/multiarray/temp_elide.h b/numpy/core/src/multiarray/temp_elide.h index d073adf28ec6..a1fec98d5a87 100644 --- a/numpy/core/src/multiarray/temp_elide.h +++ b/numpy/core/src/multiarray/temp_elide.h @@ -1,5 +1,6 @@ -#ifndef _NPY_ARRAY_TEMP_AVOID_H_ -#define _NPY_ARRAY_TEMP_AVOID_H_ +#ifndef NUMPY_CORE_SRC_MULTIARRAY_TEMP_ELIDE_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_TEMP_ELIDE_H_ + #define NPY_NO_DEPRECATED_API NPY_API_VERSION #define _MULTIARRAYMODULE #include <numpy/ndarraytypes.h> @@ -8,8 +9,8 @@ NPY_NO_EXPORT int can_elide_temp_unary(PyArrayObject * m1); NPY_NO_EXPORT int -try_binary_elide(PyArrayObject * m1, PyObject * m2, +try_binary_elide(PyObject * m1, PyObject * m2, PyObject * (inplace_op)(PyArrayObject * m1, PyObject * m2), PyObject ** res, int commutative); -#endif +#endif /* NUMPY_CORE_SRC_MULTIARRAY_TEMP_ELIDE_H_ */ diff --git a/numpy/core/src/multiarray/typeinfo.c b/numpy/core/src/multiarray/typeinfo.c index f0af768098c4..8cf6bc1e005d 100644 --- a/numpy/core/src/multiarray/typeinfo.c +++ b/numpy/core/src/multiarray/typeinfo.c @@ -3,19 +3,21 @@ * Unfortunately, we need two different types to cover the cases where min/max * do and do not appear in the tuple. */ -#define PY_SSIZE_T_CLEAN -#include <Python.h> - -/* In python 2, this is not exported from Python.h */ -#include <structseq.h> - #define NPY_NO_DEPRECATED_API NPY_API_VERSION #define _MULTIARRAYMODULE + #include "npy_pycompat.h" +#include "typeinfo.h" + +#if (defined(PYPY_VERSION_NUM) && (PYPY_VERSION_NUM <= 0x07030000)) +/* PyPy issue 3160 */ +#include <structseq.h> +#endif -PyTypeObject PyArray_typeinfoType; -PyTypeObject PyArray_typeinforangedType; + +static PyTypeObject PyArray_typeinfoType; +static PyTypeObject PyArray_typeinforangedType; static PyStructSequence_Field typeinfo_fields[] = { {"char", "The character used to represent the type"}, @@ -51,7 +53,7 @@ static PyStructSequence_Desc typeinforanged_desc = { 7, /* n_in_sequence */ }; -PyObject * +NPY_NO_EXPORT PyObject * PyArray_typeinfo( char typechar, int typenum, int nbits, int align, PyTypeObject *type_obj) @@ -59,11 +61,7 @@ PyArray_typeinfo( PyObject *entry = PyStructSequence_New(&PyArray_typeinfoType); if (entry == NULL) return NULL; -#if defined(NPY_PY3K) PyStructSequence_SET_ITEM(entry, 0, Py_BuildValue("C", typechar)); -#else - PyStructSequence_SET_ITEM(entry, 0, Py_BuildValue("c", typechar)); -#endif PyStructSequence_SET_ITEM(entry, 1, Py_BuildValue("i", typenum)); PyStructSequence_SET_ITEM(entry, 2, Py_BuildValue("i", nbits)); PyStructSequence_SET_ITEM(entry, 3, Py_BuildValue("i", align)); @@ -77,7 +75,7 @@ PyArray_typeinfo( return entry; } -PyObject * +NPY_NO_EXPORT PyObject * PyArray_typeinforanged( char typechar, int typenum, int nbits, int align, PyObject *max, PyObject *min, PyTypeObject *type_obj) @@ -85,11 +83,7 @@ PyArray_typeinforanged( PyObject *entry = PyStructSequence_New(&PyArray_typeinforangedType); if (entry == NULL) return NULL; -#if defined(NPY_PY3K) PyStructSequence_SET_ITEM(entry, 0, Py_BuildValue("C", typechar)); -#else - PyStructSequence_SET_ITEM(entry, 0, Py_BuildValue("c", typechar)); -#endif PyStructSequence_SET_ITEM(entry, 1, Py_BuildValue("i", typenum)); PyStructSequence_SET_ITEM(entry, 2, Py_BuildValue("i", nbits)); PyStructSequence_SET_ITEM(entry, 3, Py_BuildValue("i", align)); @@ -105,10 +99,36 @@ PyArray_typeinforanged( return entry; } -void typeinfo_init_structsequences(void) +/* Python version needed for older PyPy */ +#if (defined(PYPY_VERSION_NUM) && (PYPY_VERSION_NUM < 0x07020000)) + static int + PyStructSequence_InitType2(PyTypeObject *type, PyStructSequence_Desc *desc) { + PyStructSequence_InitType(type, desc); + if (PyErr_Occurred()) { + return -1; + } + return 0; + } +#endif + +NPY_NO_EXPORT int +typeinfo_init_structsequences(PyObject *multiarray_dict) { - PyStructSequence_InitType( - &PyArray_typeinfoType, &typeinfo_desc); - PyStructSequence_InitType( - &PyArray_typeinforangedType, &typeinforanged_desc); + if (PyStructSequence_InitType2( + &PyArray_typeinfoType, &typeinfo_desc) < 0) { + return -1; + } + if (PyStructSequence_InitType2( + &PyArray_typeinforangedType, &typeinforanged_desc) < 0) { + return -1; + } + if (PyDict_SetItemString(multiarray_dict, + "typeinfo", (PyObject *)&PyArray_typeinfoType) < 0) { + return -1; + } + if (PyDict_SetItemString(multiarray_dict, + "typeinforanged", (PyObject *)&PyArray_typeinforangedType) < 0) { + return -1; + } + return 0; } diff --git a/numpy/core/src/multiarray/typeinfo.h b/numpy/core/src/multiarray/typeinfo.h index 5899c2093fb7..af4637fc92df 100644 --- a/numpy/core/src/multiarray/typeinfo.h +++ b/numpy/core/src/multiarray/typeinfo.h @@ -1,19 +1,21 @@ -#ifndef _NPY_PRIVATE_TYPEINFO_H_ -#define _NPY_PRIVATE_TYPEINFO_H_ +#ifndef NUMPY_CORE_SRC_MULTIARRAY_TYPEINFO_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_TYPEINFO_H_ -void typeinfo_init_structsequences(void); +#define PY_SSIZE_T_CLEAN +#include <Python.h> +#include "npy_config.h" -extern PyTypeObject PyArray_typeinfoType; -extern PyTypeObject PyArray_typeinforangedType; +NPY_VISIBILITY_HIDDEN int +typeinfo_init_structsequences(PyObject *multiarray_dict); -PyObject * +NPY_VISIBILITY_HIDDEN PyObject * PyArray_typeinfo( char typechar, int typenum, int nbits, int align, PyTypeObject *type_obj); -PyObject * +NPY_VISIBILITY_HIDDEN PyObject * PyArray_typeinforanged( char typechar, int typenum, int nbits, int align, PyObject *max, PyObject *min, PyTypeObject *type_obj); -#endif +#endif /* NUMPY_CORE_SRC_MULTIARRAY_TYPEINFO_H_ */ diff --git a/numpy/core/src/multiarray/ucsnarrow.c b/numpy/core/src/multiarray/ucsnarrow.c deleted file mode 100644 index 8e293e9f2d9e..000000000000 --- a/numpy/core/src/multiarray/ucsnarrow.c +++ /dev/null @@ -1,174 +0,0 @@ -#define NPY_NO_DEPRECATED_API NPY_API_VERSION - -#define PY_SSIZE_T_CLEAN -#include <Python.h> - -#include <locale.h> -#include <stdio.h> - -#define _MULTIARRAYMODULE -#include "numpy/arrayobject.h" -#include "numpy/npy_math.h" - -#include "npy_config.h" - -#include "npy_pycompat.h" -#include "ctors.h" - -/* - * Functions only needed on narrow builds of Python for converting back and - * forth between the NumPy Unicode data-type (always 4-bytes) and the - * Python Unicode scalar (2-bytes on a narrow build). - */ - -/* - * The ucs2 buffer must be large enough to hold 2*ucs4length characters - * due to the use of surrogate pairs. - * - * The return value is the number of ucs2 bytes used-up which - * is ucs4length + number of surrogate pairs found. - * - * Values above 0xffff are converted to surrogate pairs. - */ -NPY_NO_EXPORT int -PyUCS2Buffer_FromUCS4(Py_UNICODE *ucs2, npy_ucs4 *ucs4, int ucs4length) -{ - int i; - int numucs2 = 0; - npy_ucs4 chr; - for (i = 0; i < ucs4length; i++) { - chr = *ucs4++; - if (chr > 0xffff) { - numucs2++; - chr -= 0x10000L; - *ucs2++ = 0xD800 + (Py_UNICODE) (chr >> 10); - *ucs2++ = 0xDC00 + (Py_UNICODE) (chr & 0x03FF); - } - else { - *ucs2++ = (Py_UNICODE) chr; - } - numucs2++; - } - return numucs2; -} - - -/* - * This converts a UCS2 buffer of the given length to UCS4 buffer. - * It converts up to ucs4len characters of UCS2 - * - * It returns the number of characters converted which can - * be less than ucs2len if there are surrogate pairs in ucs2. - * - * The return value is the actual size of the used part of the ucs4 buffer. - */ -NPY_NO_EXPORT int -PyUCS2Buffer_AsUCS4(Py_UNICODE *ucs2, npy_ucs4 *ucs4, int ucs2len, int ucs4len) -{ - int i; - npy_ucs4 chr; - Py_UNICODE ch; - int numchars=0; - - for (i = 0; (i < ucs2len) && (numchars < ucs4len); i++) { - ch = *ucs2++; - if (ch >= 0xd800 && ch <= 0xdfff) { - /* surrogate pair */ - chr = ((npy_ucs4)(ch-0xd800)) << 10; - chr += *ucs2++ + 0x2400; /* -0xdc00 + 0x10000 */ - i++; - } - else { - chr = (npy_ucs4) ch; - } - *ucs4++ = chr; - numchars++; - } - return numchars; -} - -/* - * Returns a PyUnicodeObject initialized from a buffer containing - * UCS4 unicode. - * - * Parameters - * ---------- - * src: char * - * Pointer to buffer containing UCS4 unicode. - * size: Py_ssize_t - * Size of buffer in bytes. - * swap: int - * If true, the data will be swapped. - * align: int - * If true, the data will be aligned. - * - * Returns - * ------- - * new_reference: PyUnicodeObject - */ -NPY_NO_EXPORT PyUnicodeObject * -PyUnicode_FromUCS4(char *src, Py_ssize_t size, int swap, int align) -{ - Py_ssize_t ucs4len = size / sizeof(npy_ucs4); - npy_ucs4 *buf = (npy_ucs4 *)src; - int alloc = 0; - PyUnicodeObject *ret; - - /* swap and align if needed */ - if (swap || align) { - buf = (npy_ucs4 *)malloc(size); - if (buf == NULL) { - PyErr_NoMemory(); - goto fail; - } - alloc = 1; - memcpy(buf, src, size); - if (swap) { - byte_swap_vector(buf, ucs4len, sizeof(npy_ucs4)); - } - } - - /* trim trailing zeros */ - while (ucs4len > 0 && buf[ucs4len - 1] == 0) { - ucs4len--; - } - - /* produce PyUnicode object */ -#ifdef Py_UNICODE_WIDE - { - ret = (PyUnicodeObject *)PyUnicode_FromUnicode((Py_UNICODE*)buf, - (Py_ssize_t) ucs4len); - if (ret == NULL) { - goto fail; - } - } -#else - { - Py_ssize_t tmpsiz = 2 * sizeof(Py_UNICODE) * ucs4len; - Py_ssize_t ucs2len; - Py_UNICODE *tmp; - - if ((tmp = (Py_UNICODE *)malloc(tmpsiz)) == NULL) { - PyErr_NoMemory(); - goto fail; - } - ucs2len = PyUCS2Buffer_FromUCS4(tmp, buf, ucs4len); - ret = (PyUnicodeObject *)PyUnicode_FromUnicode(tmp, (Py_ssize_t) ucs2len); - free(tmp); - if (ret == NULL) { - goto fail; - } - } -#endif - - if (alloc) { - free(buf); - } - return ret; - -fail: - if (alloc) { - free(buf); - } - return NULL; -} diff --git a/numpy/core/src/multiarray/ucsnarrow.h b/numpy/core/src/multiarray/ucsnarrow.h deleted file mode 100644 index fe31a5e25b43..000000000000 --- a/numpy/core/src/multiarray/ucsnarrow.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef _NPY_UCSNARROW_H_ -#define _NPY_UCSNARROW_H_ - -NPY_NO_EXPORT int -PyUCS2Buffer_FromUCS4(Py_UNICODE *ucs2, npy_ucs4 *ucs4, int ucs4length); - -NPY_NO_EXPORT int -PyUCS2Buffer_AsUCS4(Py_UNICODE *ucs2, npy_ucs4 *ucs4, int ucs2len, int ucs4len); - -NPY_NO_EXPORT PyUnicodeObject * -PyUnicode_FromUCS4(char *src, Py_ssize_t size, int swap, int align); - -#endif diff --git a/numpy/core/src/multiarray/usertypes.c b/numpy/core/src/multiarray/usertypes.c index 8e8090002780..a338d712dad7 100644 --- a/numpy/core/src/multiarray/usertypes.c +++ b/numpy/core/src/multiarray/usertypes.c @@ -20,13 +20,13 @@ maintainer email: oliphant.travis@ieee.org Space Science Telescope Institute (J. Todd Miller, Perry Greenfield, Rick White) */ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE + #define PY_SSIZE_T_CLEAN #include <Python.h> -#include "structmember.h" +#include <structmember.h> -/*#include <stdio.h>*/ -#define NPY_NO_DEPRECATED_API NPY_API_VERSION -#define _MULTIARRAYMODULE #include "numpy/arrayobject.h" #include "numpy/arrayscalars.h" @@ -37,22 +37,36 @@ maintainer email: oliphant.travis@ieee.org #include "npy_pycompat.h" #include "usertypes.h" +#include "dtypemeta.h" +#include "scalartypes.h" +#include "array_method.h" +#include "convert_datatype.h" +#include "legacy_dtype_implementation.h" + NPY_NO_EXPORT PyArray_Descr **userdescrs=NULL; -static int * -_append_new(int *types, int insert) +static int +_append_new(int **p_types, int insert) { int n = 0; int *newtypes; + int *types = *p_types; while (types[n] != NPY_NOTYPE) { n++; } newtypes = (int *)realloc(types, (n + 2)*sizeof(int)); + if (newtypes == NULL) { + PyErr_NoMemory(); + return -1; + } newtypes[n] = insert; newtypes[n + 1] = NPY_NOTYPE; - return newtypes; + + /* Replace the passed-in pointer */ + *p_types = newtypes; + return 0; } static npy_bool @@ -118,6 +132,47 @@ PyArray_InitArrFuncs(PyArray_ArrFuncs *f) f->scalarkind = NULL; f->cancastscalarkindto = NULL; f->cancastto = NULL; + f->fastclip = NULL; + f->fastputmask = NULL; + f->fasttake = NULL; +} + + +static int +test_deprecated_arrfuncs_members(PyArray_ArrFuncs *f) { + /* NumPy 1.19, 2020-01-15 */ + if (f->fastputmask != NULL) { + if (DEPRECATE( + "The ->f->fastputmask member of custom dtypes is ignored; " + "setting it may be an error in the future.\n" + "The custom dtype you are using must be revised, but " + "results will not be affected.") < 0) { + return -1; + } + } + /* NumPy 1.19, 2020-01-15 */ + if (f->fasttake != NULL) { + if (DEPRECATE( + "The ->f->fastputmask member of custom dtypes is ignored; " + "setting it may be an error in the future.\n" + "The custom dtype you are using must be revised, but " + "results will not be affected.") < 0) { + return -1; + } + } + /* NumPy 1.19, 2020-01-15 */ + if (f->fastclip != NULL) { + /* fastclip was already deprecated at execution time in 1.17. */ + if (DEPRECATE( + "The ->f->fastclip member of custom dtypes is deprecated; " + "setting it will be an error in the future.\n" + "The custom dtype you are using must be changed to use " + "PyUFunc_RegisterLoopForDescr to attach a custom loop to " + "np.core.umath.clip, np.minimum, and np.maximum") < 0) { + return -1; + } + } + return 0; } /* @@ -145,7 +200,7 @@ PyArray_RegisterDataType(PyArray_Descr *descr) } } typenum = NPY_USERDEF + NPY_NUMUSERTYPES; - descr->type_num = typenum; + descr->type_num = -1; if (PyDataType_ISUNSIZED(descr)) { PyErr_SetString(PyExc_ValueError, "cannot register a" \ "flexible data-type"); @@ -168,16 +223,101 @@ PyArray_RegisterDataType(PyArray_Descr *descr) PyErr_SetString(PyExc_ValueError, "missing typeobject"); return -1; } + if (descr->flags & (NPY_ITEM_IS_POINTER | NPY_ITEM_REFCOUNT)) { + /* + * User dtype can't actually do reference counting, however, there + * are existing hacks (e.g. xpress), which use a structured one: + * dtype((xpress.var, [('variable', 'O')])) + * so we have to support this. But such a structure must be constant + * (i.e. fixed at registration time, this is the case for `xpress`). + */ + if (descr->names == NULL || descr->fields == NULL || + !PyDict_CheckExact(descr->fields)) { + PyErr_Format(PyExc_ValueError, + "Failed to register dtype for %S: Legacy user dtypes " + "using `NPY_ITEM_IS_POINTER` or `NPY_ITEM_REFCOUNT` are " + "unsupported. It is possible to create such a dtype only " + "if it is a structured dtype with names and fields " + "hardcoded at registration time.\n" + "Please contact the NumPy developers if this used to work " + "but now fails.", descr->typeobj); + return -1; + } + } + + if (test_deprecated_arrfuncs_members(f) < 0) { + return -1; + } + userdescrs = realloc(userdescrs, (NPY_NUMUSERTYPES+1)*sizeof(void *)); if (userdescrs == NULL) { PyErr_SetString(PyExc_MemoryError, "RegisterDataType"); return -1; } + userdescrs[NPY_NUMUSERTYPES++] = descr; + + descr->type_num = typenum; + if (dtypemeta_wrap_legacy_descriptor(descr) < 0) { + descr->type_num = -1; + NPY_NUMUSERTYPES--; + return -1; + } + return typenum; } + +/* + * Checks that there is no cast already cached using the new casting-impl + * mechanism. + * In that case, we do not clear out the cache (but otherwise silently + * continue). Users should not modify casts after they have been used, + * but this may also happen accidentally during setup (and may never have + * mattered). See https://github.com/numpy/numpy/issues/20009 + */ +static int _warn_if_cast_exists_already( + PyArray_Descr *descr, int totype, char *funcname) +{ + PyArray_DTypeMeta *to_DType = PyArray_DTypeFromTypeNum(totype); + if (to_DType == NULL) { + return -1; + } + PyObject *cast_impl = PyDict_GetItemWithError( + NPY_DT_SLOTS(NPY_DTYPE(descr))->castingimpls, (PyObject *)to_DType); + Py_DECREF(to_DType); + if (cast_impl == NULL) { + if (PyErr_Occurred()) { + return -1; + } + } + else { + char *extra_msg; + if (cast_impl == Py_None) { + extra_msg = "the cast will continue to be considered impossible."; + } + else { + extra_msg = "the previous definition will continue to be used."; + } + Py_DECREF(cast_impl); + PyArray_Descr *to_descr = PyArray_DescrFromType(totype); + int ret = PyErr_WarnFormat(PyExc_RuntimeWarning, 1, + "A cast from %R to %R was registered/modified using `%s` " + "after the cast had been used. " + "This registration will have (mostly) no effect: %s\n" + "The most likely fix is to ensure that casts are the first " + "thing initialized after dtype registration. " + "Please contact the NumPy developers with any questions!", + descr, to_descr, funcname, extra_msg); + Py_DECREF(to_descr); + if (ret < 0) { + return -1; + } + } + return 0; +} + /*NUMPY_API Register Casting Function Replaces any function currently stored. @@ -189,25 +329,30 @@ PyArray_RegisterCastFunc(PyArray_Descr *descr, int totype, PyObject *cobj, *key; int ret; - if (totype < NPY_NTYPES_ABI_COMPATIBLE) { - descr->f->cast[totype] = castfunc; - return 0; - } if (totype >= NPY_NTYPES && !PyTypeNum_ISUSERDEF(totype)) { PyErr_SetString(PyExc_TypeError, "invalid type number."); return -1; } + if (_warn_if_cast_exists_already( + descr, totype, "PyArray_RegisterCastFunc") < 0) { + return -1; + } + + if (totype < NPY_NTYPES_ABI_COMPATIBLE) { + descr->f->cast[totype] = castfunc; + return 0; + } if (descr->f->castdict == NULL) { descr->f->castdict = PyDict_New(); if (descr->f->castdict == NULL) { return -1; } } - key = PyInt_FromLong(totype); + key = PyLong_FromLong(totype); if (PyErr_Occurred()) { return -1; } - cobj = NpyCapsule_FromVoidPtr((void *)castfunc, NULL); + cobj = PyCapsule_New((void *)castfunc, NULL, NULL); if (cobj == NULL) { Py_DECREF(key); return -1; @@ -234,10 +379,14 @@ PyArray_RegisterCanCast(PyArray_Descr *descr, int totype, if (!PyTypeNum_ISUSERDEF(descr->type_num) && !PyTypeNum_ISUSERDEF(totype)) { PyErr_SetString(PyExc_ValueError, - "At least one of the types provided to" + "At least one of the types provided to " "RegisterCanCast must be user-defined."); return -1; } + if (_warn_if_cast_exists_already( + descr, totype, "PyArray_RegisterCanCast") < 0) { + return -1; + } if (scalar == NPY_NOSCALAR) { /* @@ -247,10 +396,13 @@ PyArray_RegisterCanCast(PyArray_Descr *descr, int totype, */ if (descr->f->cancastto == NULL) { descr->f->cancastto = (int *)malloc(1*sizeof(int)); + if (descr->f->cancastto == NULL) { + PyErr_NoMemory(); + return -1; + } descr->f->cancastto[0] = NPY_NOTYPE; } - descr->f->cancastto = _append_new(descr->f->cancastto, - totype); + return _append_new(&descr->f->cancastto, totype); } else { /* register with cancastscalarkindto */ @@ -258,6 +410,10 @@ PyArray_RegisterCanCast(PyArray_Descr *descr, int totype, int i; descr->f->cancastscalarkindto = (int **)malloc(NPY_NSCALARKINDS* sizeof(int*)); + if (descr->f->cancastscalarkindto == NULL) { + PyErr_NoMemory(); + return -1; + } for (i = 0; i < NPY_NSCALARKINDS; i++) { descr->f->cancastscalarkindto[i] = NULL; } @@ -265,11 +421,195 @@ PyArray_RegisterCanCast(PyArray_Descr *descr, int totype, if (descr->f->cancastscalarkindto[scalar] == NULL) { descr->f->cancastscalarkindto[scalar] = (int *)malloc(1*sizeof(int)); + if (descr->f->cancastscalarkindto[scalar] == NULL) { + PyErr_NoMemory(); + return -1; + } descr->f->cancastscalarkindto[scalar][0] = NPY_NOTYPE; } - descr->f->cancastscalarkindto[scalar] = - _append_new(descr->f->cancastscalarkindto[scalar], totype); + return _append_new(&descr->f->cancastscalarkindto[scalar], totype); + } +} + + +/* + * Legacy user DTypes implemented the common DType operation + * (as used in type promotion/result_type, and e.g. the type for + * concatenation), by using "safe cast" logic. + * + * New DTypes do have this behaviour generally, but we use can-cast + * when legacy user dtypes are involved. + */ +NPY_NO_EXPORT PyArray_DTypeMeta * +legacy_userdtype_common_dtype_function( + PyArray_DTypeMeta *cls, PyArray_DTypeMeta *other) +{ + int skind1 = NPY_NOSCALAR, skind2 = NPY_NOSCALAR, skind; + + if (!NPY_DT_is_legacy(other)) { + /* legacy DTypes can always defer to new style ones */ + Py_INCREF(Py_NotImplemented); + return (PyArray_DTypeMeta *)Py_NotImplemented; + } + /* Defer so that only one of the types handles the cast */ + if (cls->type_num < other->type_num) { + Py_INCREF(Py_NotImplemented); + return (PyArray_DTypeMeta *)Py_NotImplemented; + } + + /* Check whether casting is possible from one type to the other */ + if (PyArray_CanCastSafely(cls->type_num, other->type_num)) { + Py_INCREF(other); + return other; + } + if (PyArray_CanCastSafely(other->type_num, cls->type_num)) { + Py_INCREF(cls); + return cls; + } + + /* + * The following code used to be part of PyArray_PromoteTypes(). + * We can expect that this code is never used. + * In principle, it allows for promotion of two different user dtypes + * to a single NumPy dtype of the same "kind". In practice + * using the same `kind` as NumPy was never possible due to an + * simplification where `PyArray_EquivTypes(descr1, descr2)` will + * return True if both kind and element size match (e.g. bfloat16 and + * float16 would be equivalent). + * The option is also very obscure and not used in the examples. + */ + + /* Convert the 'kind' char into a scalar kind */ + switch (cls->singleton->kind) { + case 'b': + skind1 = NPY_BOOL_SCALAR; + break; + case 'u': + skind1 = NPY_INTPOS_SCALAR; + break; + case 'i': + skind1 = NPY_INTNEG_SCALAR; + break; + case 'f': + skind1 = NPY_FLOAT_SCALAR; + break; + case 'c': + skind1 = NPY_COMPLEX_SCALAR; + break; + } + switch (other->singleton->kind) { + case 'b': + skind2 = NPY_BOOL_SCALAR; + break; + case 'u': + skind2 = NPY_INTPOS_SCALAR; + break; + case 'i': + skind2 = NPY_INTNEG_SCALAR; + break; + case 'f': + skind2 = NPY_FLOAT_SCALAR; + break; + case 'c': + skind2 = NPY_COMPLEX_SCALAR; + break; + } + + /* If both are scalars, there may be a promotion possible */ + if (skind1 != NPY_NOSCALAR && skind2 != NPY_NOSCALAR) { + + /* Start with the larger scalar kind */ + skind = (skind1 > skind2) ? skind1 : skind2; + int ret_type_num = _npy_smallest_type_of_kind_table[skind]; + + for (;;) { + + /* If there is no larger type of this kind, try a larger kind */ + if (ret_type_num < 0) { + ++skind; + /* Use -1 to signal no promoted type found */ + if (skind < NPY_NSCALARKINDS) { + ret_type_num = _npy_smallest_type_of_kind_table[skind]; + } + else { + break; + } + } + + /* If we found a type to which we can promote both, done! */ + if (PyArray_CanCastSafely(cls->type_num, ret_type_num) && + PyArray_CanCastSafely(other->type_num, ret_type_num)) { + return PyArray_DTypeFromTypeNum(ret_type_num); + } + + /* Try the next larger type of this kind */ + ret_type_num = _npy_next_larger_type_table[ret_type_num]; + } + } + + Py_INCREF(Py_NotImplemented); + return (PyArray_DTypeMeta *)Py_NotImplemented; +} + + +/** + * This function wraps a legacy cast into an array-method. This is mostly + * used for legacy user-dtypes, but for example numeric to/from datetime + * casts were only defined that way as well. + * + * @param from + * @param to + * @param casting If `NPY_NO_CASTING` will check the legacy registered cast, + * otherwise uses the provided cast. + */ +NPY_NO_EXPORT int +PyArray_AddLegacyWrapping_CastingImpl( + PyArray_DTypeMeta *from, PyArray_DTypeMeta *to, NPY_CASTING casting) +{ + if (casting < 0) { + if (from == to) { + casting = NPY_NO_CASTING; + } + else if (PyArray_LegacyCanCastTypeTo( + from->singleton, to->singleton, NPY_SAFE_CASTING)) { + casting = NPY_SAFE_CASTING; + } + else if (PyArray_LegacyCanCastTypeTo( + from->singleton, to->singleton, NPY_SAME_KIND_CASTING)) { + casting = NPY_SAME_KIND_CASTING; + } + else { + casting = NPY_UNSAFE_CASTING; + } + } + + PyArray_DTypeMeta *dtypes[2] = {from, to}; + PyArrayMethod_Spec spec = { + /* Name is not actually used, but allows identifying these. */ + .name = "legacy_cast", + .nin = 1, + .nout = 1, + .casting = casting, + .dtypes = dtypes, + }; + + if (from == to) { + spec.flags = NPY_METH_REQUIRES_PYAPI | NPY_METH_SUPPORTS_UNALIGNED; + PyType_Slot slots[] = { + {NPY_METH_get_loop, &legacy_cast_get_strided_loop}, + {NPY_METH_resolve_descriptors, &legacy_same_dtype_resolve_descriptors}, + {0, NULL}}; + spec.slots = slots; + return PyArray_AddCastingImplementation_FromSpec(&spec, 1); + } + else { + spec.flags = NPY_METH_REQUIRES_PYAPI; + PyType_Slot slots[] = { + {NPY_METH_get_loop, &legacy_cast_get_strided_loop}, + {NPY_METH_resolve_descriptors, &simple_cast_resolve_descriptors}, + {0, NULL}}; + spec.slots = slots; + return PyArray_AddCastingImplementation_FromSpec(&spec, 1); } - return 0; } diff --git a/numpy/core/src/multiarray/usertypes.h b/numpy/core/src/multiarray/usertypes.h index b3e386c5c671..6768e2c4219a 100644 --- a/numpy/core/src/multiarray/usertypes.h +++ b/numpy/core/src/multiarray/usertypes.h @@ -1,5 +1,7 @@ -#ifndef _NPY_PRIVATE_USERTYPES_H_ -#define _NPY_PRIVATE_USERTYPES_H_ +#ifndef NUMPY_CORE_SRC_MULTIARRAY_USERTYPES_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_USERTYPES_H_ + +#include "array_method.h" extern NPY_NO_EXPORT PyArray_Descr **userdescrs; @@ -17,4 +19,12 @@ NPY_NO_EXPORT int PyArray_RegisterCastFunc(PyArray_Descr *descr, int totype, PyArray_VectorUnaryFunc *castfunc); -#endif +NPY_NO_EXPORT PyArray_DTypeMeta * +legacy_userdtype_common_dtype_function( + PyArray_DTypeMeta *cls, PyArray_DTypeMeta *other); + +NPY_NO_EXPORT int +PyArray_AddLegacyWrapping_CastingImpl( + PyArray_DTypeMeta *from, PyArray_DTypeMeta *to, NPY_CASTING casting); + +#endif /* NUMPY_CORE_SRC_MULTIARRAY_USERTYPES_H_ */ diff --git a/numpy/core/src/multiarray/vdot.c b/numpy/core/src/multiarray/vdot.c index 424a21710f25..ff08ed2d4b07 100644 --- a/numpy/core/src/multiarray/vdot.c +++ b/numpy/core/src/multiarray/vdot.c @@ -1,7 +1,9 @@ #define NPY_NO_DEPRECATED_API NPY_API_VERSION #define _MULTIARRAYMODULE +#define PY_SSIZE_T_CLEAN #include <Python.h> + #include "common.h" #include "vdot.h" #include "npy_cblas.h" @@ -15,17 +17,17 @@ CFLOAT_vdot(char *ip1, npy_intp is1, char *ip2, npy_intp is2, char *op, npy_intp n, void *NPY_UNUSED(ignore)) { #if defined(HAVE_CBLAS) - int is1b = blas_stride(is1, sizeof(npy_cfloat)); - int is2b = blas_stride(is2, sizeof(npy_cfloat)); + CBLAS_INT is1b = blas_stride(is1, sizeof(npy_cfloat)); + CBLAS_INT is2b = blas_stride(is2, sizeof(npy_cfloat)); if (is1b && is2b) { double sum[2] = {0., 0.}; /* double for stability */ while (n > 0) { - int chunk = n < NPY_CBLAS_CHUNK ? n : NPY_CBLAS_CHUNK; + CBLAS_INT chunk = n < NPY_CBLAS_CHUNK ? n : NPY_CBLAS_CHUNK; float tmp[2]; - cblas_cdotc_sub((int)n, ip1, is1b, ip2, is2b, tmp); + CBLAS_FUNC(cblas_cdotc_sub)((CBLAS_INT)n, ip1, is1b, ip2, is2b, tmp); sum[0] += (double)tmp[0]; sum[1] += (double)tmp[1]; /* use char strides here */ @@ -66,17 +68,17 @@ CDOUBLE_vdot(char *ip1, npy_intp is1, char *ip2, npy_intp is2, char *op, npy_intp n, void *NPY_UNUSED(ignore)) { #if defined(HAVE_CBLAS) - int is1b = blas_stride(is1, sizeof(npy_cdouble)); - int is2b = blas_stride(is2, sizeof(npy_cdouble)); + CBLAS_INT is1b = blas_stride(is1, sizeof(npy_cdouble)); + CBLAS_INT is2b = blas_stride(is2, sizeof(npy_cdouble)); if (is1b && is2b) { double sum[2] = {0., 0.}; /* double for stability */ while (n > 0) { - int chunk = n < NPY_CBLAS_CHUNK ? n : NPY_CBLAS_CHUNK; + CBLAS_INT chunk = n < NPY_CBLAS_CHUNK ? n : NPY_CBLAS_CHUNK; double tmp[2]; - cblas_zdotc_sub((int)n, ip1, is1b, ip2, is2b, tmp); + CBLAS_FUNC(cblas_zdotc_sub)((CBLAS_INT)n, ip1, is1b, ip2, is2b, tmp); sum[0] += (double)tmp[0]; sum[1] += (double)tmp[1]; /* use char strides here */ diff --git a/numpy/core/src/multiarray/vdot.h b/numpy/core/src/multiarray/vdot.h index 0f60ca6d19a5..f6da5ddea03c 100644 --- a/numpy/core/src/multiarray/vdot.h +++ b/numpy/core/src/multiarray/vdot.h @@ -1,5 +1,5 @@ -#ifndef _NPY_VDOT_H_ -#define _NPY_VDOT_H_ +#ifndef NUMPY_CORE_SRC_MULTIARRAY_VDOT_H_ +#define NUMPY_CORE_SRC_MULTIARRAY_VDOT_H_ #include "common.h" @@ -15,4 +15,4 @@ CLONGDOUBLE_vdot(char *, npy_intp, char *, npy_intp, char *, npy_intp, void *); NPY_NO_EXPORT void OBJECT_vdot(char *, npy_intp, char *, npy_intp, char *, npy_intp, void *); -#endif +#endif /* NUMPY_CORE_SRC_MULTIARRAY_VDOT_H_ */ diff --git a/numpy/core/src/npymath/halffloat.c b/numpy/core/src/npymath/halffloat.c index c2bd28d60fc7..51948c736276 100644 --- a/numpy/core/src/npymath/halffloat.c +++ b/numpy/core/src/npymath/halffloat.c @@ -1,4 +1,5 @@ #define NPY_NO_DEPRECATED_API NPY_API_VERSION + #include "numpy/halffloat.h" /* @@ -115,10 +116,7 @@ npy_half npy_half_nextafter(npy_half x, npy_half y) { npy_half ret; - if (!npy_half_isfinite(x) || npy_half_isnan(y)) { -#if NPY_HALF_GENERATE_INVALID - npy_set_floatstatus_invalid(); -#endif + if (npy_half_isnan(x) || npy_half_isnan(y)) { ret = NPY_HALF_NAN; } else if (npy_half_eq_nonan(x, y)) { ret = x; @@ -138,7 +136,7 @@ npy_half npy_half_nextafter(npy_half x, npy_half y) } } #if NPY_HALF_GENERATE_OVERFLOW - if (npy_half_isinf(ret)) { + if (npy_half_isinf(ret) && npy_half_isfinite(x)) { npy_set_floatstatus_overflow(); } #endif @@ -301,15 +299,23 @@ npy_uint16 npy_floatbits_to_halfbits(npy_uint32 f) npy_set_floatstatus_underflow(); } #endif + /* + * Usually the significand is shifted by 13. For subnormals an + * additional shift needs to occur. This shift is one for the largest + * exponent giving a subnormal `f_exp = 0x38000000 >> 23 = 112`, which + * offsets the new first bit. At most the shift can be 1+10 bits. + */ f_sig >>= (113 - f_exp); /* Handle rounding by adding 1 to the bit beyond half precision */ #if NPY_HALF_ROUND_TIES_TO_EVEN /* * If the last bit in the half significand is 0 (already even), and * the remaining bit pattern is 1000...0, then we do not add one - * to the bit after the half significand. In all other cases, we do. + * to the bit after the half significand. However, the (113 - f_exp) + * shift can lose up to 11 bits, so the || checks them in the original. + * In all other cases, we can just add one. */ - if ((f_sig&0x00003fffu) != 0x00001000u) { + if (((f_sig&0x00003fffu) != 0x00001000u) || (f&0x000007ffu)) { f_sig += 0x00001000u; } #else @@ -416,7 +422,16 @@ npy_uint16 npy_doublebits_to_halfbits(npy_uint64 d) npy_set_floatstatus_underflow(); } #endif - d_sig >>= (1009 - d_exp); + /* + * Unlike floats, doubles have enough room to shift left to align + * the subnormal significand leading to no loss of the last bits. + * The smallest possible exponent giving a subnormal is: + * `d_exp = 0x3e60000000000000 >> 52 = 998`. All larger subnormals are + * shifted with respect to it. This adds a shift of 10+1 bits the final + * right shift when comparing it to the one in the normal branch. + */ + assert(d_exp - 998 >= 0); + d_sig <<= (d_exp - 998); /* Handle rounding by adding 1 to the bit beyond half precision */ #if NPY_HALF_ROUND_TIES_TO_EVEN /* @@ -424,13 +439,13 @@ npy_uint16 npy_doublebits_to_halfbits(npy_uint64 d) * the remaining bit pattern is 1000...0, then we do not add one * to the bit after the half significand. In all other cases, we do. */ - if ((d_sig&0x000007ffffffffffULL) != 0x0000020000000000ULL) { - d_sig += 0x0000020000000000ULL; + if ((d_sig&0x003fffffffffffffULL) != 0x0010000000000000ULL) { + d_sig += 0x0010000000000000ULL; } #else - d_sig += 0x0000020000000000ULL; + d_sig += 0x0010000000000000ULL; #endif - h_sig = (npy_uint16) (d_sig >> 42); + h_sig = (npy_uint16) (d_sig >> 53); /* * If the rounding causes a bit to spill into h_exp, it will * increment h_exp from zero to one and h_sig will be zero. diff --git a/numpy/core/src/npymath/ieee754.c.src b/numpy/core/src/npymath/ieee754.c.src index 8b5eef87ab44..4e6ddb712ca7 100644 --- a/numpy/core/src/npymath/ieee754.c.src +++ b/numpy/core/src/npymath/ieee754.c.src @@ -568,13 +568,21 @@ int npy_get_floatstatus() { /* * Functions to set the floating point status word. - * keep in sync with NO_FLOATING_POINT_SUPPORT in ufuncobject.h */ #if (defined(__unix__) || defined(unix)) && !defined(USG) #include <sys/param.h> #endif + +/* + * Define floating point status functions. We must define + * npy_get_floatstatus_barrier, npy_clear_floatstatus_barrier, + * npy_set_floatstatus_{divbyzero, overflow, underflow, invalid} + * for all supported platforms. + */ + + /* Solaris --------------------------------------------------------*/ /* --------ignoring SunOS ieee_flags approach, someone else can ** deal with that! */ @@ -626,117 +634,95 @@ void npy_set_floatstatus_invalid(void) fpsetsticky(FP_X_INV); } +#elif defined(_AIX) && !defined(__GNUC__) +#include <float.h> +#include <fpxcp.h> -#elif defined(__GLIBC__) || defined(__APPLE__) || \ - defined(__CYGWIN__) || defined(__MINGW32__) || \ - (defined(__FreeBSD__) && (__FreeBSD_version >= 502114)) -# include <fenv.h> - -int npy_get_floatstatus_barrier(char* param) +int npy_get_floatstatus_barrier(char *param) { - int fpstatus = fetestexcept(FE_DIVBYZERO | FE_OVERFLOW | - FE_UNDERFLOW | FE_INVALID); + int fpstatus = fp_read_flag(); /* * By using a volatile, the compiler cannot reorder this call */ if (param != NULL) { volatile char NPY_UNUSED(c) = *(char*)param; } - - return ((FE_DIVBYZERO & fpstatus) ? NPY_FPE_DIVIDEBYZERO : 0) | - ((FE_OVERFLOW & fpstatus) ? NPY_FPE_OVERFLOW : 0) | - ((FE_UNDERFLOW & fpstatus) ? NPY_FPE_UNDERFLOW : 0) | - ((FE_INVALID & fpstatus) ? NPY_FPE_INVALID : 0); + return ((FP_DIV_BY_ZERO & fpstatus) ? NPY_FPE_DIVIDEBYZERO : 0) | + ((FP_OVERFLOW & fpstatus) ? NPY_FPE_OVERFLOW : 0) | + ((FP_UNDERFLOW & fpstatus) ? NPY_FPE_UNDERFLOW : 0) | + ((FP_INVALID & fpstatus) ? NPY_FPE_INVALID : 0); } int npy_clear_floatstatus_barrier(char * param) { - /* testing float status is 50-100 times faster than clearing on x86 */ int fpstatus = npy_get_floatstatus_barrier(param); - if (fpstatus != 0) { - feclearexcept(FE_DIVBYZERO | FE_OVERFLOW | - FE_UNDERFLOW | FE_INVALID); - } + fp_swap_flag(0); return fpstatus; } - void npy_set_floatstatus_divbyzero(void) { - feraiseexcept(FE_DIVBYZERO); + fp_raise_xcp(FP_DIV_BY_ZERO); } void npy_set_floatstatus_overflow(void) { - feraiseexcept(FE_OVERFLOW); + fp_raise_xcp(FP_OVERFLOW); } void npy_set_floatstatus_underflow(void) { - feraiseexcept(FE_UNDERFLOW); + fp_raise_xcp(FP_UNDERFLOW); } void npy_set_floatstatus_invalid(void) { - feraiseexcept(FE_INVALID); -} - -#elif defined(_AIX) -#include <float.h> -#include <fpxcp.h> - -int npy_get_floatstatus_barrier(char *param) -{ - int fpstatus = fp_read_flag(); - /* - * By using a volatile, the compiler cannot reorder this call - */ - if (param != NULL) { - volatile char NPY_UNUSED(c) = *(char*)param; - } - return ((FP_DIV_BY_ZERO & fpstatus) ? NPY_FPE_DIVIDEBYZERO : 0) | - ((FP_OVERFLOW & fpstatus) ? NPY_FPE_OVERFLOW : 0) | - ((FP_UNDERFLOW & fpstatus) ? NPY_FPE_UNDERFLOW : 0) | - ((FP_INVALID & fpstatus) ? NPY_FPE_INVALID : 0); + fp_raise_xcp(FP_INVALID); } -int npy_clear_floatstatus_barrier(char * param) -{ - int fpstatus = npy_get_floatstatus_barrier(param); - fp_swap_flag(0); +#elif defined(_MSC_VER) || (defined(__osf__) && defined(__alpha)) || \ + defined (__UCLIBC__) || (defined(__arc__) && defined(__GLIBC__)) - return fpstatus; -} +/* + * By using a volatile floating point value, + * the compiler is forced to actually do the requested + * operations because of potential concurrency. + * + * We shouldn't write multiple values to a single + * global here, because that would cause + * a race condition. + */ +static volatile double _npy_floatstatus_x, + _npy_floatstatus_zero = 0.0, _npy_floatstatus_big = 1e300, + _npy_floatstatus_small = 1e-300, _npy_floatstatus_inf; void npy_set_floatstatus_divbyzero(void) { - fp_raise_xcp(FP_DIV_BY_ZERO); + _npy_floatstatus_x = 1.0 / _npy_floatstatus_zero; } void npy_set_floatstatus_overflow(void) { - fp_raise_xcp(FP_OVERFLOW); + _npy_floatstatus_x = _npy_floatstatus_big * 1e300; } void npy_set_floatstatus_underflow(void) { - fp_raise_xcp(FP_UNDERFLOW); + _npy_floatstatus_x = _npy_floatstatus_small * 1e-300; } void npy_set_floatstatus_invalid(void) { - fp_raise_xcp(FP_INVALID); + _npy_floatstatus_inf = NPY_INFINITY; + _npy_floatstatus_x = _npy_floatstatus_inf - NPY_INFINITY; } -#else - /* MS Windows -----------------------------------------------------*/ #if defined(_MSC_VER) #include <float.h> - int npy_get_floatstatus_barrier(char *param) { /* @@ -796,53 +782,61 @@ int npy_clear_floatstatus_barrier(char *param) return fpstatus; } +#endif +/* End of defined(_MSC_VER) || (defined(__osf__) && defined(__alpha)) */ + #else +/* General GCC code, should work on most platforms */ +# include <fenv.h> -int npy_get_floatstatus_barrier(char *NPY_UNUSED(param)) +int npy_get_floatstatus_barrier(char* param) { - return 0; + int fpstatus = fetestexcept(FE_DIVBYZERO | FE_OVERFLOW | + FE_UNDERFLOW | FE_INVALID); + /* + * By using a volatile, the compiler cannot reorder this call + */ + if (param != NULL) { + volatile char NPY_UNUSED(c) = *(char*)param; + } + + return ((FE_DIVBYZERO & fpstatus) ? NPY_FPE_DIVIDEBYZERO : 0) | + ((FE_OVERFLOW & fpstatus) ? NPY_FPE_OVERFLOW : 0) | + ((FE_UNDERFLOW & fpstatus) ? NPY_FPE_UNDERFLOW : 0) | + ((FE_INVALID & fpstatus) ? NPY_FPE_INVALID : 0); } -int npy_clear_floatstatus_barrier(char *param) +int npy_clear_floatstatus_barrier(char * param) { + /* testing float status is 50-100 times faster than clearing on x86 */ int fpstatus = npy_get_floatstatus_barrier(param); - return 0; + if (fpstatus != 0) { + feclearexcept(FE_DIVBYZERO | FE_OVERFLOW | + FE_UNDERFLOW | FE_INVALID); + } + + return fpstatus; } -#endif - -/* - * By using a volatile floating point value, - * the compiler is forced to actually do the requested - * operations because of potential concurrency. - * - * We shouldn't write multiple values to a single - * global here, because that would cause - * a race condition. - */ -static volatile double _npy_floatstatus_x, - _npy_floatstatus_zero = 0.0, _npy_floatstatus_big = 1e300, - _npy_floatstatus_small = 1e-300, _npy_floatstatus_inf; void npy_set_floatstatus_divbyzero(void) { - _npy_floatstatus_x = 1.0 / _npy_floatstatus_zero; + feraiseexcept(FE_DIVBYZERO); } void npy_set_floatstatus_overflow(void) { - _npy_floatstatus_x = _npy_floatstatus_big * 1e300; + feraiseexcept(FE_OVERFLOW); } void npy_set_floatstatus_underflow(void) { - _npy_floatstatus_x = _npy_floatstatus_small * 1e-300; + feraiseexcept(FE_UNDERFLOW); } void npy_set_floatstatus_invalid(void) { - _npy_floatstatus_inf = NPY_INFINITY; - _npy_floatstatus_x = _npy_floatstatus_inf - NPY_INFINITY; + feraiseexcept(FE_INVALID); } #endif diff --git a/numpy/core/src/npymath/npy_math_complex.c.src b/numpy/core/src/npymath/npy_math_complex.c.src index cf427dad809f..8c432e483982 100644 --- a/numpy/core/src/npymath/npy_math_complex.c.src +++ b/numpy/core/src/npymath/npy_math_complex.c.src @@ -40,13 +40,14 @@ * flag in an efficient way. The flag is IEEE specific. See * https://github.com/freebsd/freebsd/blob/4c6378299/lib/msun/src/catrig.c#L42 */ +#if !defined(HAVE_CACOSF) || !defined(HAVE_CACOSL) || !defined(HAVE_CASINHF) || !defined(HAVE_CASINHL) #define raise_inexact() do { \ volatile npy_float NPY_UNUSED(junk) = 1 + tiny; \ } while (0) static const volatile npy_float tiny = 3.9443045e-31f; - +#endif /**begin repeat * #type = npy_float, npy_double, npy_longdouble# @@ -64,9 +65,6 @@ static const volatile npy_float tiny = 3.9443045e-31f; * Constants *=========================================================*/ static const @ctype@ c_1@c@ = {1.0@C@, 0.0}; -static const @ctype@ c_half@c@ = {0.5@C@, 0.0}; -static const @ctype@ c_i@c@ = {0.0, 1.0@C@}; -static const @ctype@ c_ihalf@c@ = {0.0, 0.5@C@}; /*========================================================== * Helper functions @@ -74,22 +72,6 @@ static const @ctype@ c_ihalf@c@ = {0.0, 0.5@C@}; * These are necessary because we do not count on using a * C99 compiler. *=========================================================*/ -static NPY_INLINE -@ctype@ -cadd@c@(@ctype@ a, @ctype@ b) -{ - return npy_cpack@c@(npy_creal@c@(a) + npy_creal@c@(b), - npy_cimag@c@(a) + npy_cimag@c@(b)); -} - -static NPY_INLINE -@ctype@ -csub@c@(@ctype@ a, @ctype@ b) -{ - return npy_cpack@c@(npy_creal@c@(a) - npy_creal@c@(b), - npy_cimag@c@(a) - npy_cimag@c@(b)); -} - static NPY_INLINE @ctype@ cmul@c@(@ctype@ a, @ctype@ b) @@ -132,20 +114,6 @@ cdiv@c@(@ctype@ a, @ctype@ b) } } -static NPY_INLINE -@ctype@ -cneg@c@(@ctype@ a) -{ - return npy_cpack@c@(-npy_creal@c@(a), -npy_cimag@c@(a)); -} - -static NPY_INLINE -@ctype@ -cmuli@c@(@ctype@ a) -{ - return npy_cpack@c@(-npy_cimag@c@(a), npy_creal@c@(a)); -} - /*========================================================== * Custom implementation of missing complex C99 functions *=========================================================*/ @@ -1246,7 +1214,7 @@ _clog_for_large_values@c@(@type@ x, @type@ y, * Divide x and y by E, and then add 1 to the logarithm. This depends * on E being larger than sqrt(2). * Dividing by E causes an insignificant loss of accuracy; however - * this method is still poor since it is uneccessarily slow. + * this method is still poor since it is unnecessarily slow. */ if (ax > @TMAX@ / 2) { *rr = npy_log@c@(npy_hypot@c@(x / NPY_E@c@, y / NPY_E@c@)) + 1; @@ -1430,19 +1398,14 @@ npy_casinh@c@(@ctype@ z) #if @precision@ == 1 /* this is sqrt(6*EPS) */ const npy_float SQRT_6_EPSILON = 8.4572793338e-4f; - /* chosen such that pio2_hi + pio2_lo == pio2_hi but causes FE_INEXACT. */ - const volatile npy_float pio2_lo = 7.5497899549e-9f; #endif #if @precision@ == 2 const npy_double SQRT_6_EPSILON = 3.65002414998885671e-08; - const volatile npy_double pio2_lo = 6.1232339957367659e-17; #endif #if @precision@ == 3 const npy_longdouble SQRT_6_EPSILON = 8.0654900873493277169e-10l; - const volatile npy_longdouble pio2_lo = 2.710505431213761085e-20l; #endif const @type@ RECIP_EPSILON = 1.0@c@ / @TEPS@; - const @type@ pio2_hi = NPY_PI_2@c@; @type@ x, y, ax, ay, wx, wy, rx, ry, B, sqrt_A2my2, new_y; npy_int B_is_usable; diff --git a/numpy/core/src/npymath/npy_math_internal.h.src b/numpy/core/src/npymath/npy_math_internal.h.src index f2e5229b0ae1..5b418342f25e 100644 --- a/numpy/core/src/npymath/npy_math_internal.h.src +++ b/numpy/core/src/npymath/npy_math_internal.h.src @@ -55,6 +55,29 @@ */ #include "npy_math_private.h" +/* Magic binary numbers used by bit_count + * For type T, the magic numbers are computed as follows: + * Magic[0]: 01 01 01 01 01 01... = (T)~(T)0/3 + * Magic[1]: 0011 0011 0011... = (T)~(T)0/15 * 3 + * Magic[2]: 00001111 00001111... = (T)~(T)0/255 * 15 + * Magic[3]: 00000001 00000001... = (T)~(T)0/255 + * + * Counting bits set, in parallel + * Based on: http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel + * + * Generic Algorithm for type T: + * a = a - ((a >> 1) & (T)~(T)0/3); + * a = (a & (T)~(T)0/15*3) + ((a >> 2) & (T)~(T)0/15*3); + * a = (a + (a >> 4)) & (T)~(T)0/255*15; + * c = (T)(a * ((T)~(T)0/255)) >> (sizeof(T) - 1) * CHAR_BIT; +*/ + +static const npy_uint8 MAGIC8[] = {0x55u, 0x33u, 0x0Fu, 0x01u}; +static const npy_uint16 MAGIC16[] = {0x5555u, 0x3333u, 0x0F0Fu, 0x0101u}; +static const npy_uint32 MAGIC32[] = {0x55555555ul, 0x33333333ul, 0x0F0F0F0Ful, 0x01010101ul}; +static const npy_uint64 MAGIC64[] = {0x5555555555555555ull, 0x3333333333333333ull, 0x0F0F0F0F0F0F0F0Full, 0x0101010101010101ull}; + + /* ***************************************************************************** ** BASIC MATH FUNCTIONS ** @@ -454,24 +477,49 @@ NPY_INPLACE @type@ npy_frexp@c@(@type@ x, int* exp) /**begin repeat * #type = npy_longdouble, npy_double, npy_float# + * #TYPE = LONGDOUBLE, DOUBLE, FLOAT# * #c = l,,f# * #C = L,,F# */ +#undef NPY__FP_SFX +#if NPY_SIZEOF_@TYPE@ == NPY_SIZEOF_DOUBLE + #define NPY__FP_SFX(X) X +#else + #define NPY__FP_SFX(X) NPY_CAT(X, @c@) +#endif +/* + * On arm64 macOS, there's a bug with sin, cos, and tan where they don't + * raise "invalid" when given INFINITY as input. + */ +#if defined(__APPLE__) && defined(__arm64__) +#define WORKAROUND_APPLE_TRIG_BUG 1 +#else +#define WORKAROUND_APPLE_TRIG_BUG 0 +#endif + /**begin repeat1 * #kind = sin,cos,tan,sinh,cosh,tanh,fabs,floor,ceil,rint,trunc,sqrt,log10, * log,exp,expm1,asin,acos,atan,asinh,acosh,atanh,log1p,exp2,log2# * #KIND = SIN,COS,TAN,SINH,COSH,TANH,FABS,FLOOR,CEIL,RINT,TRUNC,SQRT,LOG10, * LOG,EXP,EXPM1,ASIN,ACOS,ATAN,ASINH,ACOSH,ATANH,LOG1P,EXP2,LOG2# + * #TRIG_WORKAROUND = WORKAROUND_APPLE_TRIG_BUG*3, 0*22# */ #ifdef HAVE_@KIND@@C@ NPY_INPLACE @type@ npy_@kind@@c@(@type@ x) { - return @kind@@c@(x); +#if @TRIG_WORKAROUND@ + if (!npy_isfinite(x)) { + return (x - x); + } +#endif + return NPY__FP_SFX(@kind@)(x); } #endif /**end repeat1**/ +#undef WORKAROUND_APPLE_TRIG_BUG + /**begin repeat1 * #kind = atan2,hypot,pow,fmod,copysign# * #KIND = ATAN2,HYPOT,POW,FMOD,COPYSIGN# @@ -479,7 +527,7 @@ NPY_INPLACE @type@ npy_@kind@@c@(@type@ x) #ifdef HAVE_@KIND@@C@ NPY_INPLACE @type@ npy_@kind@@c@(@type@ x, @type@ y) { - return @kind@@c@(x, y); + return NPY__FP_SFX(@kind@)(x, y); } #endif /**end repeat1**/ @@ -487,21 +535,21 @@ NPY_INPLACE @type@ npy_@kind@@c@(@type@ x, @type@ y) #ifdef HAVE_MODF@C@ NPY_INPLACE @type@ npy_modf@c@(@type@ x, @type@ *iptr) { - return modf@c@(x, iptr); + return NPY__FP_SFX(modf)(x, iptr); } #endif #ifdef HAVE_LDEXP@C@ NPY_INPLACE @type@ npy_ldexp@c@(@type@ x, int exp) { - return ldexp@c@(x, exp); + return NPY__FP_SFX(ldexp)(x, exp); } #endif #ifdef HAVE_FREXP@C@ NPY_INPLACE @type@ npy_frexp@c@(@type@ x, int* exp) { - return frexp@c@(x, exp); + return NPY__FP_SFX(frexp)(x, exp); } #endif @@ -524,10 +572,10 @@ NPY_INPLACE @type@ npy_cbrt@c@(@type@ x) #else NPY_INPLACE @type@ npy_cbrt@c@(@type@ x) { - return cbrt@c@(x); + return NPY__FP_SFX(cbrt)(x); } #endif - +#undef NPY__FP_SFX /**end repeat**/ @@ -537,10 +585,16 @@ NPY_INPLACE @type@ npy_cbrt@c@(@type@ x) /**begin repeat * #type = npy_float, npy_double, npy_longdouble# + * #TYPE = FLOAT, DOUBLE, LONGDOUBLE# * #c = f, ,l# * #C = F, ,L# */ - +#undef NPY__FP_SFX +#if NPY_SIZEOF_@TYPE@ == NPY_SIZEOF_DOUBLE + #define NPY__FP_SFX(X) X +#else + #define NPY__FP_SFX(X) NPY_CAT(X, @c@) +#endif @type@ npy_heaviside@c@(@type@ x, @type@ h0) { if (npy_isnan(x)) { @@ -557,10 +611,10 @@ NPY_INPLACE @type@ npy_cbrt@c@(@type@ x) } } -#define LOGE2 NPY_LOGE2@c@ -#define LOG2E NPY_LOG2E@c@ -#define RAD2DEG (180.0@c@/NPY_PI@c@) -#define DEG2RAD (NPY_PI@c@/180.0@c@) +#define LOGE2 NPY__FP_SFX(NPY_LOGE2) +#define LOG2E NPY__FP_SFX(NPY_LOG2E) +#define RAD2DEG (NPY__FP_SFX(180.0)/NPY__FP_SFX(NPY_PI)) +#define DEG2RAD (NPY__FP_SFX(NPY_PI)/NPY__FP_SFX(180.0)) NPY_INPLACE @type@ npy_rad2deg@c@(@type@ x) { @@ -624,6 +678,45 @@ NPY_INPLACE @type@ npy_logaddexp2@c@(@type@ x, @type@ y) } } +/* + * Wrapper function for remainder edge cases + * Internally calls npy_divmod* + */ +NPY_INPLACE @type@ +npy_remainder@c@(@type@ a, @type@ b) +{ + @type@ mod; + if (NPY_UNLIKELY(!b)) { + /* + * in2 == 0 (and not NaN): normal fmod will give the correct + * result (always NaN). `divmod` may set additional FPE for the + * division by zero creating an inf. + */ + mod = npy_fmod@c@(a, b); + } + else { + npy_divmod@c@(a, b, &mod); + } + return mod; +} + +NPY_INPLACE @type@ +npy_floor_divide@c@(@type@ a, @type@ b) { + @type@ div, mod; + if (NPY_UNLIKELY(!b)) { + /* + * in2 == 0 (and not NaN): normal division will give the correct + * result (Inf or NaN). `divmod` may set additional FPE for the modulo + * evaluating to NaN. + */ + div = a / b; + } + else { + div = npy_divmod@c@(a, b, &mod); + } + return div; +} + /* * Python version of divmod. * @@ -635,11 +728,10 @@ npy_divmod@c@(@type@ a, @type@ b, @type@ *modulus) @type@ div, mod, floordiv; mod = npy_fmod@c@(a, b); - - if (!b) { - /* If b == 0, return result of fmod. For IEEE is nan */ + if (NPY_UNLIKELY(!b)) { + /* b == 0 (not NaN): return result of fmod. For IEEE is nan */ *modulus = mod; - return mod; + return a / b; } /* a - mod should be very nearly an integer multiple of b */ @@ -647,25 +739,25 @@ npy_divmod@c@(@type@ a, @type@ b, @type@ *modulus) /* adjust fmod result to conform to Python convention of remainder */ if (mod) { - if ((b < 0) != (mod < 0)) { + if (isless(b, 0) != isless(mod, 0)) { mod += b; div -= 1.0@c@; } } else { /* if mod is zero ensure correct sign */ - mod = (b > 0) ? 0.0@c@ : -0.0@c@; + mod = npy_copysign@c@(0, b); } /* snap quotient to nearest integral value */ if (div) { floordiv = npy_floor@c@(div); - if (div - floordiv > 0.5@c@) + if (isgreater(div - floordiv, 0.5@c@)) floordiv += 1.0@c@; } else { /* if div is zero ensure correct sign */ - floordiv = (a / b > 0) ? 0.0@c@ : -0.0@c@; + floordiv = npy_copysign@c@(0, a/b); } *modulus = mod; @@ -676,7 +768,7 @@ npy_divmod@c@(@type@ a, @type@ b, @type@ *modulus) #undef LOG2E #undef RAD2DEG #undef DEG2RAD - +#undef NPY__FP_SFX /**end repeat**/ /**begin repeat @@ -716,3 +808,107 @@ npy_@func@@c@(@type@ a, @type@ b) return npy_@func@u@c@(a < 0 ? -a : a, b < 0 ? -b : b); } /**end repeat**/ + +/* Unlike LCM and GCD, we need byte and short variants for the shift operators, + * since the result is dependent on the width of the type + */ +/**begin repeat + * + * #type = byte, short, int, long, longlong# + * #c = hh,h,,l,ll# + */ +/**begin repeat1 + * + * #u = u,# + * #is_signed = 0,1# + */ +NPY_INPLACE npy_@u@@type@ +npy_lshift@u@@c@(npy_@u@@type@ a, npy_@u@@type@ b) +{ + if (NPY_LIKELY((size_t)b < sizeof(a) * CHAR_BIT)) { + return a << b; + } + else { + return 0; + } +} +NPY_INPLACE npy_@u@@type@ +npy_rshift@u@@c@(npy_@u@@type@ a, npy_@u@@type@ b) +{ + if (NPY_LIKELY((size_t)b < sizeof(a) * CHAR_BIT)) { + return a >> b; + } +#if @is_signed@ + else if (a < 0) { + return (npy_@u@@type@)-1; /* preserve the sign bit */ + } +#endif + else { + return 0; + } +} +/**end repeat1**/ +/**end repeat**/ + + +#define __popcnt32 __popcnt +/**begin repeat + * + * #type = ubyte, ushort, uint, ulong, ulonglong# + * #STYPE = BYTE, SHORT, INT, LONG, LONGLONG# + * #c = hh, h, , l, ll# + */ +#undef TO_BITS_LEN +#if 0 +/**begin repeat1 + * #len = 8, 16, 32, 64# + */ +#elif NPY_BITSOF_@STYPE@ == @len@ + #define TO_BITS_LEN(X) X##@len@ +/**end repeat1**/ +#endif + + +NPY_INPLACE uint8_t +npy_popcount_parallel@c@(npy_@type@ a) +{ + a = a - ((a >> 1) & (npy_@type@) TO_BITS_LEN(MAGIC)[0]); + a = ((a & (npy_@type@) TO_BITS_LEN(MAGIC)[1])) + ((a >> 2) & (npy_@type@) TO_BITS_LEN(MAGIC)[1]); + a = (a + (a >> 4)) & (npy_@type@) TO_BITS_LEN(MAGIC)[2]; + return (npy_@type@) (a * (npy_@type@) TO_BITS_LEN(MAGIC)[3]) >> ((NPY_SIZEOF_@STYPE@ - 1) * CHAR_BIT); +} + +NPY_INPLACE uint8_t +npy_popcountu@c@(npy_@type@ a) +{ +/* use built-in popcount if present, else use our implementation */ +#if (defined(__clang__) || defined(__GNUC__)) && NPY_BITSOF_@STYPE@ >= 32 + return __builtin_popcount@c@(a); +#elif defined(_MSC_VER) && NPY_BITSOF_@STYPE@ >= 16 + /* no builtin __popcnt64 for 32 bits */ + #if defined(_WIN64) || (defined(_WIN32) && NPY_BITSOF_@STYPE@ != 64) + return TO_BITS_LEN(__popcnt)(a); + /* split 64 bit number into two 32 bit ints and return sum of counts */ + #elif (defined(_WIN32) && NPY_BITSOF_@STYPE@ == 64) + npy_uint32 left = (npy_uint32) (a>>32); + npy_uint32 right = (npy_uint32) a; + return __popcnt32(left) + __popcnt32(right); + #endif +#else + return npy_popcount_parallel@c@(a); +#endif +} +/**end repeat**/ + +/**begin repeat + * + * #type = byte, short, int, long, longlong# + * #c = hh, h, , l, ll# + */ +NPY_INPLACE uint8_t +npy_popcount@c@(npy_@type@ a) +{ + /* Return popcount of abs(a) */ + return npy_popcountu@c@(a < 0 ? -a : a); +} +/**end repeat**/ diff --git a/numpy/core/src/npymath/npy_math_private.h b/numpy/core/src/npymath/npy_math_private.h index e4a919db692e..7ca0c5ba0f83 100644 --- a/numpy/core/src/npymath/npy_math_private.h +++ b/numpy/core/src/npymath/npy_math_private.h @@ -19,13 +19,18 @@ #define _NPY_MATH_PRIVATE_H_ #include <Python.h> +#ifdef __cplusplus +#include <cmath> +using std::isgreater; +using std::isless; +#else #include <math.h> +#endif #include "npy_config.h" #include "npy_fpmath.h" #include "numpy/npy_math.h" -#include "numpy/npy_cpu.h" #include "numpy/npy_endian.h" #include "numpy/npy_common.h" @@ -508,17 +513,29 @@ typedef union { #else /* !_MSC_VER */ typedef union { npy_cdouble npy_z; +#ifdef __cplusplus + std::complex<double> c99z; +#else complex double c99_z; +#endif } __npy_cdouble_to_c99_cast; typedef union { npy_cfloat npy_z; +#ifdef __cplusplus + std::complex<float> c99z; +#else complex float c99_z; +#endif } __npy_cfloat_to_c99_cast; typedef union { npy_clongdouble npy_z; +#ifdef __cplusplus + std::complex<long double> c99_z; +#else complex long double c99_z; +#endif } __npy_clongdouble_to_c99_cast; #endif /* !_MSC_VER */ diff --git a/numpy/core/src/npysort/binsearch.c.src b/numpy/core/src/npysort/binsearch.c.src index c04e197b7ce3..41165897b4cf 100644 --- a/numpy/core/src/npysort/binsearch.c.src +++ b/numpy/core/src/npysort/binsearch.c.src @@ -35,7 +35,7 @@ * #CMP = LT, LTE# */ -NPY_VISIBILITY_HIDDEN void +NPY_NO_EXPORT void binsearch_@side@_@suff@(const char *arr, const char *key, char *ret, npy_intp arr_len, npy_intp key_len, npy_intp arr_str, npy_intp key_str, npy_intp ret_str, @@ -81,7 +81,7 @@ binsearch_@side@_@suff@(const char *arr, const char *key, char *ret, } } -NPY_VISIBILITY_HIDDEN int +NPY_NO_EXPORT int argbinsearch_@side@_@suff@(const char *arr, const char *key, const char *sort, char *ret, npy_intp arr_len, npy_intp key_len, @@ -153,7 +153,7 @@ argbinsearch_@side@_@suff@(const char *arr, const char *key, * #CMP = <, <=# */ -NPY_VISIBILITY_HIDDEN void +NPY_NO_EXPORT void npy_binsearch_@side@(const char *arr, const char *key, char *ret, npy_intp arr_len, npy_intp key_len, npy_intp arr_str, npy_intp key_str, npy_intp ret_str, @@ -195,7 +195,7 @@ npy_binsearch_@side@(const char *arr, const char *key, char *ret, } } -NPY_VISIBILITY_HIDDEN int +NPY_NO_EXPORT int npy_argbinsearch_@side@(const char *arr, const char *key, const char *sort, char *ret, npy_intp arr_len, npy_intp key_len, diff --git a/numpy/core/src/npysort/heapsort.c.src b/numpy/core/src/npysort/heapsort.c.src index c2e3b63cbeff..4bfea1388442 100644 --- a/numpy/core/src/npysort/heapsort.c.src +++ b/numpy/core/src/npysort/heapsort.c.src @@ -60,7 +60,7 @@ * npy_cdouble, npy_clongdouble, npy_datetime, npy_timedelta# */ -int +NPY_NO_EXPORT int heapsort_@suff@(void *start, npy_intp n, void *NOT_USED) { @type@ tmp, *a; @@ -111,7 +111,7 @@ heapsort_@suff@(void *start, npy_intp n, void *NOT_USED) } -int +NPY_NO_EXPORT int aheapsort_@suff@(void *vv, npy_intp *tosort, npy_intp n, void *NOT_USED) { @type@ *v = vv; @@ -177,7 +177,7 @@ aheapsort_@suff@(void *vv, npy_intp *tosort, npy_intp n, void *NOT_USED) * #type = npy_char, npy_ucs4# */ -int +NPY_NO_EXPORT int heapsort_@suff@(void *start, npy_intp n, void *varr) { PyArrayObject *arr = varr; @@ -231,7 +231,7 @@ heapsort_@suff@(void *start, npy_intp n, void *varr) } -int +NPY_NO_EXPORT int aheapsort_@suff@(void *vv, npy_intp *tosort, npy_intp n, void *varr) { @type@ *v = vv; @@ -291,7 +291,7 @@ aheapsort_@suff@(void *vv, npy_intp *tosort, npy_intp n, void *varr) */ -int +NPY_NO_EXPORT int npy_heapsort(void *start, npy_intp num, void *varr) { PyArrayObject *arr = varr; @@ -348,7 +348,7 @@ npy_heapsort(void *start, npy_intp num, void *varr) } -int +NPY_NO_EXPORT int npy_aheapsort(void *vv, npy_intp *tosort, npy_intp n, void *varr) { char *v = vv; diff --git a/numpy/core/src/npysort/mergesort.c.src b/numpy/core/src/npysort/mergesort.c.src index 6f659617a736..f83fbf758140 100644 --- a/numpy/core/src/npysort/mergesort.c.src +++ b/numpy/core/src/npysort/mergesort.c.src @@ -103,7 +103,7 @@ mergesort0_@suff@(@type@ *pl, @type@ *pr, @type@ *pw) } -int +NPY_NO_EXPORT int mergesort_@suff@(void *start, npy_intp num, void *NOT_USED) { @type@ *pl, *pr, *pw; @@ -166,7 +166,7 @@ amergesort0_@suff@(npy_intp *pl, npy_intp *pr, @type@ *v, npy_intp *pw) } -int +NPY_NO_EXPORT int amergesort_@suff@(void *v, npy_intp *tosort, npy_intp num, void *NOT_USED) { npy_intp *pl, *pr, *pw; @@ -245,7 +245,7 @@ mergesort0_@suff@(@type@ *pl, @type@ *pr, @type@ *pw, @type@ *vp, size_t len) } -int +NPY_NO_EXPORT int mergesort_@suff@(void *start, npy_intp num, void *varr) { PyArrayObject *arr = varr; @@ -326,7 +326,7 @@ amergesort0_@suff@(npy_intp *pl, npy_intp *pr, @type@ *v, npy_intp *pw, size_t l } -int +NPY_NO_EXPORT int amergesort_@suff@(void *v, npy_intp *tosort, npy_intp num, void *varr) { PyArrayObject *arr = varr; @@ -407,7 +407,7 @@ npy_mergesort0(char *pl, char *pr, char *pw, char *vp, npy_intp elsize, } -int +NPY_NO_EXPORT int npy_mergesort(void *start, npy_intp num, void *varr) { PyArrayObject *arr = varr; @@ -485,7 +485,7 @@ npy_amergesort0(npy_intp *pl, npy_intp *pr, char *v, npy_intp *pw, } -int +NPY_NO_EXPORT int npy_amergesort(void *v, npy_intp *tosort, npy_intp num, void *varr) { PyArrayObject *arr = varr; diff --git a/numpy/core/src/npysort/npysort_common.h b/numpy/core/src/npysort/npysort_common.h index a22045b419fb..2a6e4d421234 100644 --- a/numpy/core/src/npysort/npysort_common.h +++ b/numpy/core/src/npysort/npysort_common.h @@ -273,10 +273,10 @@ STRING_SWAP(char *s1, char *s2, size_t len) NPY_INLINE static int -STRING_LT(char *s1, char *s2, size_t len) +STRING_LT(const char *s1, const char *s2, size_t len) { - const unsigned char *c1 = (unsigned char *)s1; - const unsigned char *c2 = (unsigned char *)s2; + const unsigned char *c1 = (const unsigned char *)s1; + const unsigned char *c2 = (const unsigned char *)s2; size_t i; int ret = 0; @@ -311,7 +311,7 @@ UNICODE_SWAP(npy_ucs4 *s1, npy_ucs4 *s2, size_t len) NPY_INLINE static int -UNICODE_LT(npy_ucs4 *s1, npy_ucs4 *s2, size_t len) +UNICODE_LT(const npy_ucs4 *s1, const npy_ucs4 *s2, size_t len) { size_t i; int ret = 0; @@ -329,6 +329,14 @@ UNICODE_LT(npy_ucs4 *s1, npy_ucs4 *s2, size_t len) NPY_INLINE static int DATETIME_LT(npy_datetime a, npy_datetime b) { + if (a == NPY_DATETIME_NAT) { + return 0; + } + + if (b == NPY_DATETIME_NAT) { + return 1; + } + return a < b; } @@ -336,6 +344,14 @@ DATETIME_LT(npy_datetime a, npy_datetime b) NPY_INLINE static int TIMEDELTA_LT(npy_timedelta a, npy_timedelta b) { + if (a == NPY_DATETIME_NAT) { + return 0; + } + + if (b == NPY_DATETIME_NAT) { + return 1; + } + return a < b; } diff --git a/numpy/core/src/npysort/quicksort.c.src b/numpy/core/src/npysort/quicksort.c.src index 49a2c4906807..933f75808b69 100644 --- a/numpy/core/src/npysort/quicksort.c.src +++ b/numpy/core/src/npysort/quicksort.c.src @@ -85,7 +85,7 @@ * npy_cdouble, npy_clongdouble, npy_datetime, npy_timedelta# */ -int +NPY_NO_EXPORT int quicksort_@suff@(void *start, npy_intp num, void *NOT_USED) { @type@ vp; @@ -160,7 +160,7 @@ stack_pop: } -int +NPY_NO_EXPORT int aquicksort_@suff@(void *vv, npy_intp* tosort, npy_intp num, void *NOT_USED) { @type@ *v = vv; @@ -253,7 +253,7 @@ stack_pop: * #type = npy_char, npy_ucs4# */ -int +NPY_NO_EXPORT int quicksort_@suff@(void *start, npy_intp num, void *varr) { PyArrayObject *arr = varr; @@ -341,7 +341,7 @@ stack_pop: } -int +NPY_NO_EXPORT int aquicksort_@suff@(void *vv, npy_intp* tosort, npy_intp num, void *varr) { @type@ *v = vv; @@ -434,7 +434,7 @@ stack_pop: */ -int +NPY_NO_EXPORT int npy_quicksort(void *start, npy_intp num, void *varr) { PyArrayObject *arr = varr; @@ -539,7 +539,7 @@ stack_pop: } -int +NPY_NO_EXPORT int npy_aquicksort(void *vv, npy_intp* tosort, npy_intp num, void *varr) { char *v = vv; diff --git a/numpy/core/src/npysort/radixsort.cpp b/numpy/core/src/npysort/radixsort.cpp new file mode 100644 index 000000000000..5393869eef44 --- /dev/null +++ b/numpy/core/src/npysort/radixsort.cpp @@ -0,0 +1,356 @@ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION + +#include "npy_sort.h" +#include "npysort_common.h" + +#include "../common/numpy_tag.h" +#include <stdlib.h> +#include <type_traits> + +/* + ***************************************************************************** + ** INTEGER SORTS ** + ***************************************************************************** + */ + +// Reference: https://github.com/eloj/radix-sorting#-key-derivation +template <class T, class UT> +UT +KEY_OF(UT x) +{ + // Floating-point is currently disabled. + // Floating-point tests succeed for double and float on macOS but not on + // Windows/Linux. Basic sorting tests succeed but others relying on sort + // fail. Possibly related to floating-point normalisation or multiple NaN + // reprs? Not sure. + if (std::is_floating_point<T>::value) { + // For floats, we invert the key if the sign bit is set, else we invert + // the sign bit. + return ((x) ^ (-((x) >> (sizeof(T) * 8 - 1)) | + ((UT)1 << (sizeof(T) * 8 - 1)))); + } + else if (std::is_signed<T>::value) { + // For signed ints, we flip the sign bit so the negatives are below the + // positives. + return ((x) ^ ((UT)1 << (sizeof(UT) * 8 - 1))); + } + else { + return x; + } +} + +template <class T> +static inline npy_ubyte +nth_byte(T key, npy_intp l) +{ + return (key >> (l << 3)) & 0xFF; +} + +template <class T, class UT> +static UT * +radixsort0(UT *start, UT *aux, npy_intp num) +{ + npy_intp cnt[sizeof(UT)][1 << 8] = {{0}}; + UT key0 = KEY_OF<T>(start[0]); + + for (npy_intp i = 0; i < num; i++) { + UT k = KEY_OF<T>(start[i]); + + for (size_t l = 0; l < sizeof(UT); l++) { + cnt[l][nth_byte(k, l)]++; + } + } + + size_t ncols = 0; + npy_ubyte cols[sizeof(UT)]; + for (size_t l = 0; l < sizeof(UT); l++) { + if (cnt[l][nth_byte(key0, l)] != num) { + cols[ncols++] = l; + } + } + + for (size_t l = 0; l < ncols; l++) { + npy_intp a = 0; + for (npy_intp i = 0; i < 256; i++) { + npy_intp b = cnt[cols[l]][i]; + cnt[cols[l]][i] = a; + a += b; + } + } + + for (size_t l = 0; l < ncols; l++) { + UT *temp; + for (npy_intp i = 0; i < num; i++) { + UT k = KEY_OF<T>(start[i]); + npy_intp dst = cnt[cols[l]][nth_byte(k, cols[l])]++; + aux[dst] = start[i]; + } + + temp = aux; + aux = start; + start = temp; + } + + return start; +} + +template <class T, class UT> +static int +radixsort_(UT *start, npy_intp num) +{ + if (num < 2) { + return 0; + } + + npy_bool all_sorted = 1; + UT k1 = KEY_OF<T>(start[0]); + for (npy_intp i = 1; i < num; i++) { + UT k2 = KEY_OF<T>(start[i]); + if (k1 > k2) { + all_sorted = 0; + break; + } + k1 = k2; + } + + if (all_sorted) { + return 0; + } + + UT *aux = (UT *)malloc(num * sizeof(UT)); + if (aux == nullptr) { + return -NPY_ENOMEM; + } + + UT *sorted = radixsort0<T>(start, aux, num); + if (sorted != start) { + memcpy(start, sorted, num * sizeof(UT)); + } + + free(aux); + return 0; +} + +template <class T> +static int +radixsort(void *start, npy_intp num) +{ + using UT = typename std::make_unsigned<T>::type; + return radixsort_<T>((UT *)start, num); +} + +template <class T, class UT> +static npy_intp * +aradixsort0(UT *start, npy_intp *aux, npy_intp *tosort, npy_intp num) +{ + npy_intp cnt[sizeof(UT)][1 << 8] = {{0}}; + UT key0 = KEY_OF<T>(start[0]); + + for (npy_intp i = 0; i < num; i++) { + UT k = KEY_OF<T>(start[i]); + + for (size_t l = 0; l < sizeof(UT); l++) { + cnt[l][nth_byte(k, l)]++; + } + } + + size_t ncols = 0; + npy_ubyte cols[sizeof(UT)]; + for (size_t l = 0; l < sizeof(UT); l++) { + if (cnt[l][nth_byte(key0, l)] != num) { + cols[ncols++] = l; + } + } + + for (size_t l = 0; l < ncols; l++) { + npy_intp a = 0; + for (npy_intp i = 0; i < 256; i++) { + npy_intp b = cnt[cols[l]][i]; + cnt[cols[l]][i] = a; + a += b; + } + } + + for (size_t l = 0; l < ncols; l++) { + npy_intp *temp; + for (npy_intp i = 0; i < num; i++) { + UT k = KEY_OF<T>(start[tosort[i]]); + npy_intp dst = cnt[cols[l]][nth_byte(k, cols[l])]++; + aux[dst] = tosort[i]; + } + + temp = aux; + aux = tosort; + tosort = temp; + } + + return tosort; +} + +template <class T, class UT> +static int +aradixsort_(UT *start, npy_intp *tosort, npy_intp num) +{ + npy_intp *sorted; + npy_intp *aux; + UT k1, k2; + npy_bool all_sorted = 1; + + if (num < 2) { + return 0; + } + + k1 = KEY_OF<T>(start[tosort[0]]); + for (npy_intp i = 1; i < num; i++) { + k2 = KEY_OF<T>(start[tosort[i]]); + if (k1 > k2) { + all_sorted = 0; + break; + } + k1 = k2; + } + + if (all_sorted) { + return 0; + } + + aux = (npy_intp *)malloc(num * sizeof(npy_intp)); + if (aux == NULL) { + return -NPY_ENOMEM; + } + + sorted = aradixsort0<T>(start, aux, tosort, num); + if (sorted != tosort) { + memcpy(tosort, sorted, num * sizeof(npy_intp)); + } + + free(aux); + return 0; +} + +template <class T> +static int +aradixsort(void *start, npy_intp *tosort, npy_intp num) +{ + using UT = typename std::make_unsigned<T>::type; + return aradixsort_<T>((UT *)start, tosort, num); +} + +extern "C" { +NPY_NO_EXPORT int +radixsort_bool(void *vec, npy_intp cnt, void *NPY_UNUSED(null)) +{ + return radixsort<npy_bool>(vec, cnt); +} +NPY_NO_EXPORT int +radixsort_byte(void *vec, npy_intp cnt, void *NPY_UNUSED(null)) +{ + return radixsort<npy_byte>(vec, cnt); +} +NPY_NO_EXPORT int +radixsort_ubyte(void *vec, npy_intp cnt, void *NPY_UNUSED(null)) +{ + return radixsort<npy_ubyte>(vec, cnt); +} +NPY_NO_EXPORT int +radixsort_short(void *vec, npy_intp cnt, void *NPY_UNUSED(null)) +{ + return radixsort<npy_short>(vec, cnt); +} +NPY_NO_EXPORT int +radixsort_ushort(void *vec, npy_intp cnt, void *NPY_UNUSED(null)) +{ + return radixsort<npy_ushort>(vec, cnt); +} +NPY_NO_EXPORT int +radixsort_int(void *vec, npy_intp cnt, void *NPY_UNUSED(null)) +{ + return radixsort<npy_int>(vec, cnt); +} +NPY_NO_EXPORT int +radixsort_uint(void *vec, npy_intp cnt, void *NPY_UNUSED(null)) +{ + return radixsort<npy_uint>(vec, cnt); +} +NPY_NO_EXPORT int +radixsort_long(void *vec, npy_intp cnt, void *NPY_UNUSED(null)) +{ + return radixsort<npy_long>(vec, cnt); +} +NPY_NO_EXPORT int +radixsort_ulong(void *vec, npy_intp cnt, void *NPY_UNUSED(null)) +{ + return radixsort<npy_ulong>(vec, cnt); +} +NPY_NO_EXPORT int +radixsort_longlong(void *vec, npy_intp cnt, void *NPY_UNUSED(null)) +{ + return radixsort<npy_longlong>(vec, cnt); +} +NPY_NO_EXPORT int +radixsort_ulonglong(void *vec, npy_intp cnt, void *NPY_UNUSED(null)) +{ + return radixsort<npy_ulonglong>(vec, cnt); +} +NPY_NO_EXPORT int +aradixsort_bool(void *vec, npy_intp *ind, npy_intp cnt, void *NPY_UNUSED(null)) +{ + return aradixsort<npy_bool>(vec, ind, cnt); +} +NPY_NO_EXPORT int +aradixsort_byte(void *vec, npy_intp *ind, npy_intp cnt, void *NPY_UNUSED(null)) +{ + return aradixsort<npy_byte>(vec, ind, cnt); +} +NPY_NO_EXPORT int +aradixsort_ubyte(void *vec, npy_intp *ind, npy_intp cnt, + void *NPY_UNUSED(null)) +{ + return aradixsort<npy_ubyte>(vec, ind, cnt); +} +NPY_NO_EXPORT int +aradixsort_short(void *vec, npy_intp *ind, npy_intp cnt, + void *NPY_UNUSED(null)) +{ + return aradixsort<npy_short>(vec, ind, cnt); +} +NPY_NO_EXPORT int +aradixsort_ushort(void *vec, npy_intp *ind, npy_intp cnt, + void *NPY_UNUSED(null)) +{ + return aradixsort<npy_ushort>(vec, ind, cnt); +} +NPY_NO_EXPORT int +aradixsort_int(void *vec, npy_intp *ind, npy_intp cnt, void *NPY_UNUSED(null)) +{ + return aradixsort<npy_int>(vec, ind, cnt); +} +NPY_NO_EXPORT int +aradixsort_uint(void *vec, npy_intp *ind, npy_intp cnt, void *NPY_UNUSED(null)) +{ + return aradixsort<npy_uint>(vec, ind, cnt); +} +NPY_NO_EXPORT int +aradixsort_long(void *vec, npy_intp *ind, npy_intp cnt, void *NPY_UNUSED(null)) +{ + return aradixsort<npy_long>(vec, ind, cnt); +} +NPY_NO_EXPORT int +aradixsort_ulong(void *vec, npy_intp *ind, npy_intp cnt, + void *NPY_UNUSED(null)) +{ + return aradixsort<npy_ulong>(vec, ind, cnt); +} +NPY_NO_EXPORT int +aradixsort_longlong(void *vec, npy_intp *ind, npy_intp cnt, + void *NPY_UNUSED(null)) +{ + return aradixsort<npy_longlong>(vec, ind, cnt); +} +NPY_NO_EXPORT int +aradixsort_ulonglong(void *vec, npy_intp *ind, npy_intp cnt, + void *NPY_UNUSED(null)) +{ + return aradixsort<npy_ulonglong>(vec, ind, cnt); +} +} diff --git a/numpy/core/src/npysort/selection.c.src b/numpy/core/src/npysort/selection.c.src index 1e0934558a5c..0e285b320b91 100644 --- a/numpy/core/src/npysort/selection.c.src +++ b/numpy/core/src/npysort/selection.c.src @@ -40,7 +40,7 @@ static NPY_INLINE void store_pivot(npy_intp pivot, npy_intp kth, } /* - * If pivot is the requested kth store it, overwritting other pivots if + * If pivot is the requested kth store it, overwriting other pivots if * required. This must be done so iterative partition can work without * manually shifting lower data offset by kth each time */ @@ -280,7 +280,7 @@ static int * kth 8: 0 1 2 3 4 5 6 [8 7] -> stack [] * */ -int +NPY_NO_EXPORT int @name@introselect_@suff@(@type@ *v, #if @arg@ npy_intp* tosort, @@ -323,7 +323,8 @@ int store_pivot(kth, kth, pivots, npiv); return 0; } - else if (@inexact@ && kth == num - 1) { + // Parenthesis around @inexact@ tells clang dead code as intentional + else if ((@inexact@) && kth == num - 1) { /* useful to check if NaN present via partition(d, (x, -1)) */ npy_intp k; npy_intp maxidx = low; diff --git a/numpy/core/src/npysort/timsort.c.src b/numpy/core/src/npysort/timsort.c.src new file mode 100644 index 000000000000..5298f5a1d057 --- /dev/null +++ b/numpy/core/src/npysort/timsort.c.src @@ -0,0 +1,2572 @@ +/* -*- c -*- */ + +/* + * The purpose of this module is to add faster sort functions + * that are type-specific. This is done by altering the + * function table for the builtin descriptors. + * + * These sorting functions are copied almost directly from numarray + * with a few modifications (complex comparisons compare the imaginary + * part if the real parts are equal, for example), and the names + * are changed. + * + * The original sorting code is due to Charles R. Harris who wrote + * it for numarray. + */ + +/* + * Quick sort is usually the fastest, but the worst case scenario can + * be slower than the merge and heap sorts. The merge sort requires + * extra memory and so for large arrays may not be useful. + * + * The merge sort is *stable*, meaning that equal components + * are unmoved from their entry versions, so it can be used to + * implement lexigraphic sorting on multiple keys. + * + * The heap sort is included for completeness. + */ + + +/* For details of Timsort, refer to + * https://github.com/python/cpython/blob/3.7/Objects/listsort.txt + */ + +#define NPY_NO_DEPRECATED_API NPY_API_VERSION + +#include "npy_sort.h" +#include "npysort_common.h" +#include <stdlib.h> + +/* enough for 32 * 1.618 ** 128 elements */ +#define TIMSORT_STACK_SIZE 128 + + + +static npy_intp compute_min_run(npy_intp num) +{ + npy_intp r = 0; + + while (64 < num) { + r |= num & 1; + num >>= 1; + } + + return num + r; +} + +typedef struct { + npy_intp s; /* start pointer */ + npy_intp l; /* length */ +} run; + + +/* buffer for argsort. Declared here to avoid multiple declarations. */ +typedef struct { + npy_intp *pw; + npy_intp size; +} buffer_intp; + + +/* buffer method */ +static NPY_INLINE int +resize_buffer_intp(buffer_intp *buffer, npy_intp new_size) +{ + if (new_size <= buffer->size) { + return 0; + } + + if (NPY_UNLIKELY(buffer->pw == NULL)) { + buffer->pw = malloc(new_size * sizeof(npy_intp)); + } else { + buffer->pw = realloc(buffer->pw, new_size * sizeof(npy_intp)); + } + + buffer->size = new_size; + + if (NPY_UNLIKELY(buffer->pw == NULL)) { + return -NPY_ENOMEM; + } else { + return 0; + } +} + +/* + ***************************************************************************** + ** NUMERIC SORTS ** + ***************************************************************************** + */ + + +/**begin repeat + * + * #TYPE = BOOL, BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG, + * LONGLONG, ULONGLONG, HALF, FLOAT, DOUBLE, LONGDOUBLE, + * CFLOAT, CDOUBLE, CLONGDOUBLE, DATETIME, TIMEDELTA# + * #suff = bool, byte, ubyte, short, ushort, int, uint, long, ulong, + * longlong, ulonglong, half, float, double, longdouble, + * cfloat, cdouble, clongdouble, datetime, timedelta# + * #type = npy_bool, npy_byte, npy_ubyte, npy_short, npy_ushort, npy_int, + * npy_uint, npy_long, npy_ulong, npy_longlong, npy_ulonglong, + * npy_ushort, npy_float, npy_double, npy_longdouble, npy_cfloat, + * npy_cdouble, npy_clongdouble, npy_datetime, npy_timedelta# + */ + + +typedef struct { + @type@ * pw; + npy_intp size; +} buffer_@suff@; + + +static NPY_INLINE int +resize_buffer_@suff@(buffer_@suff@ *buffer, npy_intp new_size) +{ + if (new_size <= buffer->size) { + return 0; + } + + if (NPY_UNLIKELY(buffer->pw == NULL)) { + buffer->pw = malloc(new_size * sizeof(@type@)); + } else { + buffer->pw = realloc(buffer->pw, new_size * sizeof(@type@)); + } + + buffer->size = new_size; + + if (NPY_UNLIKELY(buffer->pw == NULL)) { + return -NPY_ENOMEM; + } else { + return 0; + } +} + + +static npy_intp +count_run_@suff@(@type@ *arr, npy_intp l, npy_intp num, npy_intp minrun) +{ + npy_intp sz; + @type@ vc, *pl, *pi, *pj, *pr; + + if (NPY_UNLIKELY(num - l == 1)) { + return 1; + } + + pl = arr + l; + + /* (not strictly) ascending sequence */ + if (!@TYPE@_LT(*(pl + 1), *pl)) { + for (pi = pl + 1; pi < arr + num - 1 && !@TYPE@_LT(*(pi + 1), *pi); ++pi) { + } + } else { /* (strictly) descending sequence */ + for (pi = pl + 1; pi < arr + num - 1 && @TYPE@_LT(*(pi + 1), *pi); ++pi) { + } + + for (pj = pl, pr = pi; pj < pr; ++pj, --pr) { + @TYPE@_SWAP(*pj, *pr); + } + } + + ++pi; + sz = pi - pl; + + if (sz < minrun) { + if (l + minrun < num) { + sz = minrun; + } else { + sz = num - l; + } + + pr = pl + sz; + + /* insertion sort */ + for (; pi < pr; ++pi) { + vc = *pi; + pj = pi; + + while (pl < pj && @TYPE@_LT(vc, *(pj - 1))) { + *pj = *(pj - 1); + --pj; + } + + *pj = vc; + } + } + + return sz; +} + + +/* when the left part of the array (p1) is smaller, copy p1 to buffer + * and merge from left to right + */ +static void +merge_left_@suff@(@type@ *p1, npy_intp l1, @type@ *p2, npy_intp l2, + @type@ *p3) +{ + @type@ *end = p2 + l2; + memcpy(p3, p1, sizeof(@type@) * l1); + /* first element must be in p2 otherwise skipped in the caller */ + *p1++ = *p2++; + + while (p1 < p2 && p2 < end) { + if (@TYPE@_LT(*p2, *p3)) { + *p1++ = *p2++; + } else { + *p1++ = *p3++; + } + } + + if (p1 != p2) { + memcpy(p1, p3, sizeof(@type@) * (p2 - p1)); + } +} + + +/* when the right part of the array (p2) is smaller, copy p2 to buffer + * and merge from right to left + */ +static void +merge_right_@suff@(@type@ *p1, npy_intp l1, @type@ *p2, npy_intp l2, + @type@ *p3) +{ + npy_intp ofs; + @type@ *start = p1 - 1; + memcpy(p3, p2, sizeof(@type@) * l2); + p1 += l1 - 1; + p2 += l2 - 1; + p3 += l2 - 1; + /* first element must be in p1 otherwise skipped in the caller */ + *p2-- = *p1--; + + while (p1 < p2 && start < p1) { + if (@TYPE@_LT(*p3, *p1)) { + *p2-- = *p1--; + } else { + *p2-- = *p3--; + } + } + + if (p1 != p2) { + ofs = p2 - start; + memcpy(start + 1, p3 - ofs + 1, sizeof(@type@) * ofs); + } +} + + +/* Note: the naming convention of gallop functions are different from that of + * CPython. For example, here gallop_right means gallop from left toward right, + * whereas in CPython gallop_right means gallop + * and find the right most element among equal elements + */ +static npy_intp +gallop_right_@suff@(const @type@ *arr, const npy_intp size, const @type@ key) +{ + npy_intp last_ofs, ofs, m; + + if (@TYPE@_LT(key, arr[0])) { + return 0; + } + + last_ofs = 0; + ofs = 1; + + for (;;) { + if (size <= ofs || ofs < 0) { + ofs = size; /* arr[ofs] is never accessed */ + break; + } + + if (@TYPE@_LT(key, arr[ofs])) { + break; + } else { + last_ofs = ofs; + /* ofs = 1, 3, 7, 15... */ + ofs = (ofs << 1) + 1; + } + } + + /* now that arr[last_ofs] <= key < arr[ofs] */ + while (last_ofs + 1 < ofs) { + m = last_ofs + ((ofs - last_ofs) >> 1); + + if (@TYPE@_LT(key, arr[m])) { + ofs = m; + } else { + last_ofs = m; + } + } + + /* now that arr[ofs-1] <= key < arr[ofs] */ + return ofs; +} + + +static npy_intp +gallop_left_@suff@(const @type@ *arr, const npy_intp size, const @type@ key) +{ + npy_intp last_ofs, ofs, l, m, r; + + if (@TYPE@_LT(arr[size - 1], key)) { + return size; + } + + last_ofs = 0; + ofs = 1; + + for (;;) { + if (size <= ofs || ofs < 0) { + ofs = size; + break; + } + + if (@TYPE@_LT(arr[size - ofs - 1], key)) { + break; + } else { + last_ofs = ofs; + ofs = (ofs << 1) + 1; + } + } + + /* now that arr[size-ofs-1] < key <= arr[size-last_ofs-1] */ + l = size - ofs - 1; + r = size - last_ofs - 1; + + while (l + 1 < r) { + m = l + ((r - l) >> 1); + + if (@TYPE@_LT(arr[m], key)) { + l = m; + } else { + r = m; + } + } + + /* now that arr[r-1] < key <= arr[r] */ + return r; +} + + +static int +merge_at_@suff@(@type@ *arr, const run *stack, const npy_intp at, + buffer_@suff@ *buffer) +{ + int ret; + npy_intp s1, l1, s2, l2, k; + @type@ *p1, *p2; + s1 = stack[at].s; + l1 = stack[at].l; + s2 = stack[at + 1].s; + l2 = stack[at + 1].l; + /* arr[s2] belongs to arr[s1+k]. + * if try to comment this out for debugging purpose, remember + * in the merging process the first element is skipped + */ + k = gallop_right_@suff@(arr + s1, l1, arr[s2]); + + if (l1 == k) { + /* already sorted */ + return 0; + } + + p1 = arr + s1 + k; + l1 -= k; + p2 = arr + s2; + /* arr[s2-1] belongs to arr[s2+l2] */ + l2 = gallop_left_@suff@(arr + s2, l2, arr[s2 - 1]); + + if (l2 < l1) { + ret = resize_buffer_@suff@(buffer, l2); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + merge_right_@suff@(p1, l1, p2, l2, buffer->pw); + } else { + ret = resize_buffer_@suff@(buffer, l1); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + merge_left_@suff@(p1, l1, p2, l2, buffer->pw); + } + + return 0; +} + + +static int +try_collapse_@suff@(@type@ *arr, run *stack, npy_intp *stack_ptr, + buffer_@suff@ *buffer) +{ + int ret; + npy_intp A, B, C, top; + top = *stack_ptr; + + while (1 < top) { + B = stack[top - 2].l; + C = stack[top - 1].l; + + if ((2 < top && stack[top - 3].l <= B + C) || + (3 < top && stack[top - 4].l <= stack[top - 3].l + B)) { + A = stack[top - 3].l; + + if (A <= C) { + ret = merge_at_@suff@(arr, stack, top - 3, buffer); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + stack[top - 3].l += B; + stack[top - 2] = stack[top - 1]; + --top; + } else { + ret = merge_at_@suff@(arr, stack, top - 2, buffer); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + stack[top - 2].l += C; + --top; + } + } else if (1 < top && B <= C) { + ret = merge_at_@suff@(arr, stack, top - 2, buffer); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + stack[top - 2].l += C; + --top; + } else { + break; + } + } + + *stack_ptr = top; + return 0; +} + +static int +force_collapse_@suff@(@type@ *arr, run *stack, npy_intp *stack_ptr, + buffer_@suff@ *buffer) +{ + int ret; + npy_intp top = *stack_ptr; + + while (2 < top) { + if (stack[top - 3].l <= stack[top - 1].l) { + ret = merge_at_@suff@(arr, stack, top - 3, buffer); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + stack[top - 3].l += stack[top - 2].l; + stack[top - 2] = stack[top - 1]; + --top; + } else { + ret = merge_at_@suff@(arr, stack, top - 2, buffer); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + stack[top - 2].l += stack[top - 1].l; + --top; + } + } + + if (1 < top) { + ret = merge_at_@suff@(arr, stack, top - 2, buffer); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + } + + return 0; +} + + +NPY_NO_EXPORT int +timsort_@suff@(void *start, npy_intp num, void *NPY_UNUSED(varr)) +{ + int ret; + npy_intp l, n, stack_ptr, minrun; + buffer_@suff@ buffer; + run stack[TIMSORT_STACK_SIZE]; + buffer.pw = NULL; + buffer.size = 0; + stack_ptr = 0; + minrun = compute_min_run(num); + + for (l = 0; l < num;) { + n = count_run_@suff@(start, l, num, minrun); + stack[stack_ptr].s = l; + stack[stack_ptr].l = n; + ++stack_ptr; + ret = try_collapse_@suff@(start, stack, &stack_ptr, &buffer); + + if (NPY_UNLIKELY(ret < 0)) { goto cleanup; } + + l += n; + } + + ret = force_collapse_@suff@(start, stack, &stack_ptr, &buffer); + + if (NPY_UNLIKELY(ret < 0)) { goto cleanup; } + + ret = 0; +cleanup: + + free(buffer.pw); + + return ret; +} + + +/* argsort */ + + +static npy_intp +acount_run_@suff@(@type@ *arr, npy_intp *tosort, npy_intp l, npy_intp num, + npy_intp minrun) +{ + npy_intp sz; + @type@ vc; + npy_intp vi; + npy_intp *pl, *pi, *pj, *pr; + + if (NPY_UNLIKELY(num - l == 1)) { + return 1; + } + + pl = tosort + l; + + /* (not strictly) ascending sequence */ + if (!@TYPE@_LT(arr[*(pl + 1)], arr[*pl])) { + for (pi = pl + 1; pi < tosort + num - 1 + && !@TYPE@_LT(arr[*(pi + 1)], arr[*pi]); ++pi) { + } + } else { /* (strictly) descending sequence */ + for (pi = pl + 1; pi < tosort + num - 1 + && @TYPE@_LT(arr[*(pi + 1)], arr[*pi]); ++pi) { + } + + for (pj = pl, pr = pi; pj < pr; ++pj, --pr) { + INTP_SWAP(*pj, *pr); + } + } + + ++pi; + sz = pi - pl; + + if (sz < minrun) { + if (l + minrun < num) { + sz = minrun; + } else { + sz = num - l; + } + + pr = pl + sz; + + /* insertion sort */ + for (; pi < pr; ++pi) { + vi = *pi; + vc = arr[*pi]; + pj = pi; + + while (pl < pj && @TYPE@_LT(vc, arr[*(pj - 1)])) { + *pj = *(pj - 1); + --pj; + } + + *pj = vi; + } + } + + return sz; +} + + +static npy_intp +agallop_right_@suff@(const @type@ *arr, const npy_intp *tosort, + const npy_intp size, const @type@ key) +{ + npy_intp last_ofs, ofs, m; + + if (@TYPE@_LT(key, arr[tosort[0]])) { + return 0; + } + + last_ofs = 0; + ofs = 1; + + for (;;) { + if (size <= ofs || ofs < 0) { + ofs = size; /* arr[ofs] is never accessed */ + break; + } + + if (@TYPE@_LT(key, arr[tosort[ofs]])) { + break; + } else { + last_ofs = ofs; + /* ofs = 1, 3, 7, 15... */ + ofs = (ofs << 1) + 1; + } + } + + /* now that arr[tosort[last_ofs]] <= key < arr[tosort[ofs]] */ + while (last_ofs + 1 < ofs) { + m = last_ofs + ((ofs - last_ofs) >> 1); + + if (@TYPE@_LT(key, arr[tosort[m]])) { + ofs = m; + } else { + last_ofs = m; + } + } + + /* now that arr[tosort[ofs-1]] <= key < arr[tosort[ofs]] */ + return ofs; +} + + + +static npy_intp +agallop_left_@suff@(const @type@ *arr, const npy_intp *tosort, + const npy_intp size, const @type@ key) +{ + npy_intp last_ofs, ofs, l, m, r; + + if (@TYPE@_LT(arr[tosort[size - 1]], key)) { + return size; + } + + last_ofs = 0; + ofs = 1; + + for (;;) { + if (size <= ofs || ofs < 0) { + ofs = size; + break; + } + + if (@TYPE@_LT(arr[tosort[size - ofs - 1]], key)) { + break; + } else { + last_ofs = ofs; + ofs = (ofs << 1) + 1; + } + } + + /* now that arr[tosort[size-ofs-1]] < key <= arr[tosort[size-last_ofs-1]] */ + l = size - ofs - 1; + r = size - last_ofs - 1; + + while (l + 1 < r) { + m = l + ((r - l) >> 1); + + if (@TYPE@_LT(arr[tosort[m]], key)) { + l = m; + } else { + r = m; + } + } + + /* now that arr[tosort[r-1]] < key <= arr[tosort[r]] */ + return r; +} + + +static void +amerge_left_@suff@(@type@ *arr, npy_intp *p1, npy_intp l1, npy_intp *p2, + npy_intp l2, + npy_intp *p3) +{ + npy_intp *end = p2 + l2; + memcpy(p3, p1, sizeof(npy_intp) * l1); + /* first element must be in p2 otherwise skipped in the caller */ + *p1++ = *p2++; + + while (p1 < p2 && p2 < end) { + if (@TYPE@_LT(arr[*p2], arr[*p3])) { + *p1++ = *p2++; + } else { + *p1++ = *p3++; + } + } + + if (p1 != p2) { + memcpy(p1, p3, sizeof(npy_intp) * (p2 - p1)); + } +} + + +static void +amerge_right_@suff@(@type@ *arr, npy_intp* p1, npy_intp l1, npy_intp *p2, + npy_intp l2, + npy_intp *p3) +{ + npy_intp ofs; + npy_intp *start = p1 - 1; + memcpy(p3, p2, sizeof(npy_intp) * l2); + p1 += l1 - 1; + p2 += l2 - 1; + p3 += l2 - 1; + /* first element must be in p1 otherwise skipped in the caller */ + *p2-- = *p1--; + + while (p1 < p2 && start < p1) { + if (@TYPE@_LT(arr[*p3], arr[*p1])) { + *p2-- = *p1--; + } else { + *p2-- = *p3--; + } + } + + if (p1 != p2) { + ofs = p2 - start; + memcpy(start + 1, p3 - ofs + 1, sizeof(npy_intp) * ofs); + } +} + + +static int +amerge_at_@suff@(@type@ *arr, npy_intp *tosort, const run *stack, + const npy_intp at, + buffer_intp *buffer) +{ + int ret; + npy_intp s1, l1, s2, l2, k; + npy_intp *p1, *p2; + s1 = stack[at].s; + l1 = stack[at].l; + s2 = stack[at + 1].s; + l2 = stack[at + 1].l; + /* tosort[s2] belongs to tosort[s1+k] */ + k = agallop_right_@suff@(arr, tosort + s1, l1, arr[tosort[s2]]); + + if (l1 == k) { + /* already sorted */ + return 0; + } + + p1 = tosort + s1 + k; + l1 -= k; + p2 = tosort + s2; + /* tosort[s2-1] belongs to tosort[s2+l2] */ + l2 = agallop_left_@suff@(arr, tosort + s2, l2, arr[tosort[s2 - 1]]); + + if (l2 < l1) { + ret = resize_buffer_intp(buffer, l2); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + amerge_right_@suff@(arr, p1, l1, p2, l2, buffer->pw); + } else { + ret = resize_buffer_intp(buffer, l1); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + amerge_left_@suff@(arr, p1, l1, p2, l2, buffer->pw); + } + + return 0; +} + + +static int +atry_collapse_@suff@(@type@ *arr, npy_intp *tosort, run *stack, + npy_intp *stack_ptr, + buffer_intp *buffer) +{ + int ret; + npy_intp A, B, C, top; + top = *stack_ptr; + + while (1 < top) { + B = stack[top - 2].l; + C = stack[top - 1].l; + + if ((2 < top && stack[top - 3].l <= B + C) || + (3 < top && stack[top - 4].l <= stack[top - 3].l + B)) { + A = stack[top - 3].l; + + if (A <= C) { + ret = amerge_at_@suff@(arr, tosort, stack, top - 3, buffer); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + stack[top - 3].l += B; + stack[top - 2] = stack[top - 1]; + --top; + } else { + ret = amerge_at_@suff@(arr, tosort, stack, top - 2, buffer); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + stack[top - 2].l += C; + --top; + } + } else if (1 < top && B <= C) { + ret = amerge_at_@suff@(arr, tosort, stack, top - 2, buffer); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + stack[top - 2].l += C; + --top; + } else { + break; + } + } + + *stack_ptr = top; + return 0; +} + + +static int +aforce_collapse_@suff@(@type@ *arr, npy_intp *tosort, run *stack, + npy_intp *stack_ptr, + buffer_intp *buffer) +{ + int ret; + npy_intp top = *stack_ptr; + + while (2 < top) { + if (stack[top - 3].l <= stack[top - 1].l) { + ret = amerge_at_@suff@(arr, tosort, stack, top - 3, buffer); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + stack[top - 3].l += stack[top - 2].l; + stack[top - 2] = stack[top - 1]; + --top; + } else { + ret = amerge_at_@suff@(arr, tosort, stack, top - 2, buffer); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + stack[top - 2].l += stack[top - 1].l; + --top; + } + } + + if (1 < top) { + ret = amerge_at_@suff@(arr, tosort, stack, top - 2, buffer); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + } + + return 0; +} + + +NPY_NO_EXPORT int +atimsort_@suff@(void *v, npy_intp *tosort, npy_intp num, + void *NPY_UNUSED(varr)) +{ + int ret; + npy_intp l, n, stack_ptr, minrun; + buffer_intp buffer; + run stack[TIMSORT_STACK_SIZE]; + buffer.pw = NULL; + buffer.size = 0; + stack_ptr = 0; + minrun = compute_min_run(num); + + for (l = 0; l < num;) { + n = acount_run_@suff@(v, tosort, l, num, minrun); + stack[stack_ptr].s = l; + stack[stack_ptr].l = n; + ++stack_ptr; + ret = atry_collapse_@suff@(v, tosort, stack, &stack_ptr, &buffer); + + if (NPY_UNLIKELY(ret < 0)) { goto cleanup; } + + l += n; + } + + ret = aforce_collapse_@suff@(v, tosort, stack, &stack_ptr, &buffer); + + if (NPY_UNLIKELY(ret < 0)) { goto cleanup; } + + ret = 0; +cleanup: + + if (buffer.pw != NULL) { + free(buffer.pw); + } + + return ret; +} + +/**end repeat**/ + + + +/* For string sorts and generic sort, element comparisons are very expensive, + * and the time cost of insertion sort (involves N**2 comparison) clearly hurts. + * Implementing binary insertion sort and probably gallop mode during merging process + * can hopefully boost the performance. Here as a temporary workaround we use shorter + * run length to reduce the cost of insertion sort. + */ + +static npy_intp compute_min_run_short(npy_intp num) +{ + npy_intp r = 0; + + while (16 < num) { + r |= num & 1; + num >>= 1; + } + + return num + r; +} + +/* + ***************************************************************************** + ** STRING SORTS ** + ***************************************************************************** + */ + + +/**begin repeat + * + * #TYPE = STRING, UNICODE# + * #suff = string, unicode# + * #type = npy_char, npy_ucs4# + */ + + +typedef struct { + @type@ * pw; + npy_intp size; + size_t len; +} buffer_@suff@; + + +static NPY_INLINE int +resize_buffer_@suff@(buffer_@suff@ *buffer, npy_intp new_size) +{ + if (new_size <= buffer->size) { + return 0; + } + + if (NPY_UNLIKELY(buffer->pw == NULL)) { + buffer->pw = malloc(sizeof(@type@) * new_size * buffer->len); + } else { + buffer->pw = realloc(buffer->pw, sizeof(@type@) * new_size * buffer->len); + } + + buffer->size = new_size; + + if (NPY_UNLIKELY(buffer->pw == NULL)) { + return -NPY_ENOMEM; + } else { + return 0; + } +} + + +static npy_intp +count_run_@suff@(@type@ *arr, npy_intp l, npy_intp num, npy_intp minrun, + @type@ *vp, size_t len) +{ + npy_intp sz; + @type@ *pl, *pi, *pj, *pr; + + if (NPY_UNLIKELY(num - l == 1)) { + return 1; + } + + pl = arr + l * len; + + /* (not strictly) ascending sequence */ + if (!@TYPE@_LT(pl + len, pl, len)) { + for (pi = pl + len; pi < arr + (num - 1) * len + && !@TYPE@_LT(pi + len, pi, len); pi += len) { + } + } else { /* (strictly) descending sequence */ + for (pi = pl + len; pi < arr + (num - 1) * len + && @TYPE@_LT(pi + len, pi, len); pi += len) { + } + + for (pj = pl, pr = pi; pj < pr; pj += len, pr -= len) { + @TYPE@_SWAP(pj, pr, len); + } + } + + pi += len; + sz = (pi - pl) / len; + + if (sz < minrun) { + if (l + minrun < num) { + sz = minrun; + } else { + sz = num - l; + } + + pr = pl + sz * len; + + /* insertion sort */ + for (; pi < pr; pi += len) { + @TYPE@_COPY(vp, pi, len); + pj = pi; + + while (pl < pj && @TYPE@_LT(vp, pj - len, len)) { + @TYPE@_COPY(pj, pj - len, len); + pj -= len; + } + + @TYPE@_COPY(pj, vp, len); + } + } + + return sz; +} + + +static npy_intp +gallop_right_@suff@(const @type@ *arr, const npy_intp size, + const @type@ *key, size_t len) +{ + npy_intp last_ofs, ofs, m; + + if (@TYPE@_LT(key, arr, len)) { + return 0; + } + + last_ofs = 0; + ofs = 1; + + for (;;) { + if (size <= ofs || ofs < 0) { + ofs = size; /* arr[ofs] is never accessed */ + break; + } + + if (@TYPE@_LT(key, arr + ofs * len, len)) { + break; + } else { + last_ofs = ofs; + /* ofs = 1, 3, 7, 15... */ + ofs = (ofs << 1) + 1; + } + } + + /* now that arr[last_ofs*len] <= key < arr[ofs*len] */ + while (last_ofs + 1 < ofs) { + m = last_ofs + ((ofs - last_ofs) >> 1); + + if (@TYPE@_LT(key, arr + m * len, len)) { + ofs = m; + } else { + last_ofs = m; + } + } + + /* now that arr[(ofs-1)*len] <= key < arr[ofs*len] */ + return ofs; +} + + + +static npy_intp +gallop_left_@suff@(const @type@ *arr, const npy_intp size, const @type@ *key, + size_t len) +{ + npy_intp last_ofs, ofs, l, m, r; + + if (@TYPE@_LT(arr + (size - 1) * len, key, len)) { + return size; + } + + last_ofs = 0; + ofs = 1; + + for (;;) { + if (size <= ofs || ofs < 0) { + ofs = size; + break; + } + + if (@TYPE@_LT(arr + (size - ofs - 1) * len, key, len)) { + break; + } else { + last_ofs = ofs; + ofs = (ofs << 1) + 1; + } + } + + /* now that arr[(size-ofs-1)*len] < key <= arr[(size-last_ofs-1)*len] */ + l = size - ofs - 1; + r = size - last_ofs - 1; + + while (l + 1 < r) { + m = l + ((r - l) >> 1); + + if (@TYPE@_LT(arr + m * len, key, len)) { + l = m; + } else { + r = m; + } + } + + /* now that arr[(r-1)*len] < key <= arr[r*len] */ + return r; +} + + +static void +merge_left_@suff@(@type@ *p1, npy_intp l1, @type@ *p2, npy_intp l2, + @type@ *p3, size_t len) +{ + @type@ *end = p2 + l2 * len; + memcpy(p3, p1, sizeof(@type@) * l1 * len); + /* first element must be in p2 otherwise skipped in the caller */ + @TYPE@_COPY(p1, p2, len); + p1 += len; + p2 += len; + + while (p1 < p2 && p2 < end) { + if (@TYPE@_LT(p2, p3, len)) { + @TYPE@_COPY(p1, p2, len); + p1 += len; + p2 += len; + } else { + @TYPE@_COPY(p1, p3, len); + p1 += len; + p3 += len; + } + } + + if (p1 != p2) { + memcpy(p1, p3, sizeof(@type@) * (p2 - p1)); + } +} + + +static void +merge_right_@suff@(@type@ *p1, npy_intp l1, @type@ *p2, npy_intp l2, + @type@ *p3, size_t len) +{ + npy_intp ofs; + @type@ *start = p1 - len; + memcpy(p3, p2, sizeof(@type@) * l2 * len); + p1 += (l1 - 1) * len; + p2 += (l2 - 1) * len; + p3 += (l2 - 1) * len; + /* first element must be in p1 otherwise skipped in the caller */ + @TYPE@_COPY(p2, p1, len); + p2 -= len; + p1 -= len; + + while (p1 < p2 && start < p1) { + if (@TYPE@_LT(p3, p1, len)) { + @TYPE@_COPY(p2, p1, len); + p2 -= len; + p1 -= len; + } else { + @TYPE@_COPY(p2, p3, len); + p2 -= len; + p3 -= len; + } + } + + if (p1 != p2) { + ofs = p2 - start; + memcpy(start + len, p3 - ofs + len, sizeof(@type@) * ofs); + } +} + + +static int +merge_at_@suff@(@type@ *arr, const run *stack, const npy_intp at, + buffer_@suff@ *buffer, size_t len) +{ + int ret; + npy_intp s1, l1, s2, l2, k; + @type@ *p1, *p2; + s1 = stack[at].s; + l1 = stack[at].l; + s2 = stack[at + 1].s; + l2 = stack[at + 1].l; + /* arr[s2] belongs to arr[s1+k] */ + @TYPE@_COPY(buffer->pw, arr + s2 * len, len); + k = gallop_right_@suff@(arr + s1 * len, l1, buffer->pw, len); + + if (l1 == k) { + /* already sorted */ + return 0; + } + + p1 = arr + (s1 + k) * len; + l1 -= k; + p2 = arr + s2 * len; + /* arr[s2-1] belongs to arr[s2+l2] */ + @TYPE@_COPY(buffer->pw, arr + (s2 - 1) * len, len); + l2 = gallop_left_@suff@(arr + s2 * len, l2, buffer->pw, len); + + if (l2 < l1) { + ret = resize_buffer_@suff@(buffer, l2); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + merge_right_@suff@(p1, l1, p2, l2, buffer->pw, len); + } else { + ret = resize_buffer_@suff@(buffer, l1); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + merge_left_@suff@(p1, l1, p2, l2, buffer->pw, len); + } + + return 0; +} + + +static int +try_collapse_@suff@(@type@ *arr, run *stack, npy_intp *stack_ptr, + buffer_@suff@ *buffer, size_t len) +{ + int ret; + npy_intp A, B, C, top; + top = *stack_ptr; + + while (1 < top) { + B = stack[top - 2].l; + C = stack[top - 1].l; + + if ((2 < top && stack[top - 3].l <= B + C) || + (3 < top && stack[top - 4].l <= stack[top - 3].l + B)) { + A = stack[top - 3].l; + + if (A <= C) { + ret = merge_at_@suff@(arr, stack, top - 3, buffer, len); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + stack[top - 3].l += B; + stack[top - 2] = stack[top - 1]; + --top; + } else { + ret = merge_at_@suff@(arr, stack, top - 2, buffer, len); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + stack[top - 2].l += C; + --top; + } + } else if (1 < top && B <= C) { + ret = merge_at_@suff@(arr, stack, top - 2, buffer, len); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + stack[top - 2].l += C; + --top; + } else { + break; + } + } + + *stack_ptr = top; + return 0; +} + + +static int +force_collapse_@suff@(@type@ *arr, run *stack, npy_intp *stack_ptr, + buffer_@suff@ *buffer, size_t len) +{ + int ret; + npy_intp top = *stack_ptr; + + while (2 < top) { + if (stack[top - 3].l <= stack[top - 1].l) { + ret = merge_at_@suff@(arr, stack, top - 3, buffer, len); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + stack[top - 3].l += stack[top - 2].l; + stack[top - 2] = stack[top - 1]; + --top; + } else { + ret = merge_at_@suff@(arr, stack, top - 2, buffer, len); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + stack[top - 2].l += stack[top - 1].l; + --top; + } + } + + if (1 < top) { + ret = merge_at_@suff@(arr, stack, top - 2, buffer, len); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + } + + return 0; +} + + +NPY_NO_EXPORT int +timsort_@suff@(void *start, npy_intp num, void *varr) +{ + PyArrayObject *arr = varr; + size_t elsize = PyArray_ITEMSIZE(arr); + size_t len = elsize / sizeof(@type@); + int ret; + npy_intp l, n, stack_ptr, minrun; + run stack[TIMSORT_STACK_SIZE]; + buffer_@suff@ buffer; + + /* Items that have zero size don't make sense to sort */ + if (len == 0) { + return 0; + } + + buffer.pw = NULL; + buffer.size = 0; + buffer.len = len; + stack_ptr = 0; + minrun = compute_min_run_short(num); + /* used for insertion sort and gallop key */ + ret = resize_buffer_@suff@(&buffer, 1); + + if (NPY_UNLIKELY(ret < 0)) { goto cleanup; } + + for (l = 0; l < num;) { + n = count_run_@suff@(start, l, num, minrun, buffer.pw, len); + /* both s and l are scaled by len */ + stack[stack_ptr].s = l; + stack[stack_ptr].l = n; + ++stack_ptr; + ret = try_collapse_@suff@(start, stack, &stack_ptr, &buffer, len); + + if (NPY_UNLIKELY(ret < 0)) { goto cleanup; } + + l += n; + } + + ret = force_collapse_@suff@(start, stack, &stack_ptr, &buffer, len); + + if (NPY_UNLIKELY(ret < 0)) { goto cleanup; } + + ret = 0; + +cleanup: + if (buffer.pw != NULL) { + free(buffer.pw); + } + return ret; +} + + +/* argsort */ + + +static npy_intp +acount_run_@suff@(@type@ *arr, npy_intp *tosort, npy_intp l, npy_intp num, + npy_intp minrun, size_t len) +{ + npy_intp sz; + npy_intp vi; + npy_intp *pl, *pi, *pj, *pr; + + if (NPY_UNLIKELY(num - l == 1)) { + return 1; + } + + pl = tosort + l; + + /* (not strictly) ascending sequence */ + if (!@TYPE@_LT(arr + (*(pl + 1)) * len, arr + (*pl) * len, len)) { + for (pi = pl + 1; pi < tosort + num - 1 + && !@TYPE@_LT(arr + (*(pi + 1)) * len, arr + (*pi) * len, len); ++pi) { + } + } else { /* (strictly) descending sequence */ + for (pi = pl + 1; pi < tosort + num - 1 + && @TYPE@_LT(arr + (*(pi + 1)) * len, arr + (*pi) * len, len); ++pi) { + } + + for (pj = pl, pr = pi; pj < pr; ++pj, --pr) { + INTP_SWAP(*pj, *pr); + } + } + + ++pi; + sz = pi - pl; + + if (sz < minrun) { + if (l + minrun < num) { + sz = minrun; + } else { + sz = num - l; + } + + pr = pl + sz; + + /* insertion sort */ + for (; pi < pr; ++pi) { + vi = *pi; + pj = pi; + + while (pl < pj && @TYPE@_LT(arr + vi * len, arr + (*(pj - 1)) * len, len)) { + *pj = *(pj - 1); + --pj; + } + + *pj = vi; + } + } + + return sz; +} + + +static npy_intp +agallop_left_@suff@(const @type@ *arr, const npy_intp *tosort, + const npy_intp size, const @type@ *key, size_t len) +{ + npy_intp last_ofs, ofs, l, m, r; + + if (@TYPE@_LT(arr + tosort[size - 1] * len, key, len)) { + return size; + } + + last_ofs = 0; + ofs = 1; + + for (;;) { + if (size <= ofs || ofs < 0) { + ofs = size; + break; + } + + if (@TYPE@_LT(arr + tosort[size - ofs - 1] * len, key, len)) { + break; + } else { + last_ofs = ofs; + ofs = (ofs << 1) + 1; + } + } + + /* now that arr[tosort[size-ofs-1]*len] < key <= arr[tosort[size-last_ofs-1]*len] */ + l = size - ofs - 1; + r = size - last_ofs - 1; + + while (l + 1 < r) { + m = l + ((r - l) >> 1); + + if (@TYPE@_LT(arr + tosort[m] * len, key, len)) { + l = m; + } else { + r = m; + } + } + + /* now that arr[tosort[r-1]*len] < key <= arr[tosort[r]*len] */ + return r; +} + + +static npy_intp +agallop_right_@suff@(const @type@ *arr, const npy_intp *tosort, + const npy_intp size, const @type@ *key, size_t len) +{ + npy_intp last_ofs, ofs, m; + + if (@TYPE@_LT(key, arr + tosort[0] * len, len)) { + return 0; + } + + last_ofs = 0; + ofs = 1; + + for (;;) { + if (size <= ofs || ofs < 0) { + ofs = size; /* arr[ofs] is never accessed */ + break; + } + + if (@TYPE@_LT(key, arr + tosort[ofs] * len, len)) { + break; + } else { + last_ofs = ofs; + /* ofs = 1, 3, 7, 15... */ + ofs = (ofs << 1) + 1; + } + } + + /* now that arr[tosort[last_ofs]*len] <= key < arr[tosort[ofs]*len] */ + while (last_ofs + 1 < ofs) { + m = last_ofs + ((ofs - last_ofs) >> 1); + + if (@TYPE@_LT(key, arr + tosort[m] * len, len)) { + ofs = m; + } else { + last_ofs = m; + } + } + + /* now that arr[tosort[ofs-1]*len] <= key < arr[tosort[ofs]*len] */ + return ofs; +} + + + +static void +amerge_left_@suff@(@type@ *arr, npy_intp *p1, npy_intp l1, npy_intp *p2, + npy_intp l2, npy_intp *p3, size_t len) +{ + npy_intp *end = p2 + l2; + memcpy(p3, p1, sizeof(npy_intp) * l1); + /* first element must be in p2 otherwise skipped in the caller */ + *p1++ = *p2++; + + while (p1 < p2 && p2 < end) { + if (@TYPE@_LT(arr + (*p2) * len, arr + (*p3) * len, len)) { + *p1++ = *p2++; + } else { + *p1++ = *p3++; + } + } + + if (p1 != p2) { + memcpy(p1, p3, sizeof(npy_intp) * (p2 - p1)); + } +} + + +static void +amerge_right_@suff@(@type@ *arr, npy_intp* p1, npy_intp l1, npy_intp *p2, + npy_intp l2, npy_intp *p3, size_t len) +{ + npy_intp ofs; + npy_intp *start = p1 - 1; + memcpy(p3, p2, sizeof(npy_intp) * l2); + p1 += l1 - 1; + p2 += l2 - 1; + p3 += l2 - 1; + /* first element must be in p1 otherwise skipped in the caller */ + *p2-- = *p1--; + + while (p1 < p2 && start < p1) { + if (@TYPE@_LT(arr + (*p3) * len, arr + (*p1) * len, len)) { + *p2-- = *p1--; + } else { + *p2-- = *p3--; + } + } + + if (p1 != p2) { + ofs = p2 - start; + memcpy(start + 1, p3 - ofs + 1, sizeof(npy_intp) * ofs); + } +} + + + +static int +amerge_at_@suff@(@type@ *arr, npy_intp *tosort, const run *stack, + const npy_intp at, buffer_intp *buffer, size_t len) +{ + int ret; + npy_intp s1, l1, s2, l2, k; + npy_intp *p1, *p2; + s1 = stack[at].s; + l1 = stack[at].l; + s2 = stack[at + 1].s; + l2 = stack[at + 1].l; + /* tosort[s2] belongs to tosort[s1+k] */ + k = agallop_right_@suff@(arr, tosort + s1, l1, arr + tosort[s2] * len, len); + + if (l1 == k) { + /* already sorted */ + return 0; + } + + p1 = tosort + s1 + k; + l1 -= k; + p2 = tosort + s2; + /* tosort[s2-1] belongs to tosort[s2+l2] */ + l2 = agallop_left_@suff@(arr, tosort + s2, l2, arr + tosort[s2 - 1] * len, + len); + + if (l2 < l1) { + ret = resize_buffer_intp(buffer, l2); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + amerge_right_@suff@(arr, p1, l1, p2, l2, buffer->pw, len); + } else { + ret = resize_buffer_intp(buffer, l1); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + amerge_left_@suff@(arr, p1, l1, p2, l2, buffer->pw, len); + } + + return 0; +} + + +static int +atry_collapse_@suff@(@type@ *arr, npy_intp *tosort, run *stack, + npy_intp *stack_ptr, buffer_intp *buffer, size_t len) +{ + int ret; + npy_intp A, B, C, top; + top = *stack_ptr; + + while (1 < top) { + B = stack[top - 2].l; + C = stack[top - 1].l; + + if ((2 < top && stack[top - 3].l <= B + C) || + (3 < top && stack[top - 4].l <= stack[top - 3].l + B)) { + A = stack[top - 3].l; + + if (A <= C) { + ret = amerge_at_@suff@(arr, tosort, stack, top - 3, buffer, len); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + stack[top - 3].l += B; + stack[top - 2] = stack[top - 1]; + --top; + } else { + ret = amerge_at_@suff@(arr, tosort, stack, top - 2, buffer, len); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + stack[top - 2].l += C; + --top; + } + } else if (1 < top && B <= C) { + ret = amerge_at_@suff@(arr, tosort, stack, top - 2, buffer, len); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + stack[top - 2].l += C; + --top; + } else { + break; + } + } + + *stack_ptr = top; + return 0; +} + + + +static int +aforce_collapse_@suff@(@type@ *arr, npy_intp *tosort, run *stack, + npy_intp *stack_ptr, buffer_intp *buffer, size_t len) +{ + int ret; + npy_intp top = *stack_ptr; + + while (2 < top) { + if (stack[top - 3].l <= stack[top - 1].l) { + ret = amerge_at_@suff@(arr, tosort, stack, top - 3, buffer, len); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + stack[top - 3].l += stack[top - 2].l; + stack[top - 2] = stack[top - 1]; + --top; + } else { + ret = amerge_at_@suff@(arr, tosort, stack, top - 2, buffer, len); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + stack[top - 2].l += stack[top - 1].l; + --top; + } + } + + if (1 < top) { + ret = amerge_at_@suff@(arr, tosort, stack, top - 2, buffer, len); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + } + + return 0; +} + + +NPY_NO_EXPORT int +atimsort_@suff@(void *start, npy_intp *tosort, npy_intp num, void *varr) +{ + PyArrayObject *arr = varr; + size_t elsize = PyArray_ITEMSIZE(arr); + size_t len = elsize / sizeof(@type@); + int ret; + npy_intp l, n, stack_ptr, minrun; + run stack[TIMSORT_STACK_SIZE]; + buffer_intp buffer; + + /* Items that have zero size don't make sense to sort */ + if (len == 0) { + return 0; + } + + buffer.pw = NULL; + buffer.size = 0; + stack_ptr = 0; + minrun = compute_min_run_short(num); + + for (l = 0; l < num;) { + n = acount_run_@suff@(start, tosort, l, num, minrun, len); + /* both s and l are scaled by len */ + stack[stack_ptr].s = l; + stack[stack_ptr].l = n; + ++stack_ptr; + ret = atry_collapse_@suff@(start, tosort, stack, &stack_ptr, &buffer, len); + + if (NPY_UNLIKELY(ret < 0)) { goto cleanup; } + + l += n; + } + + ret = aforce_collapse_@suff@(start, tosort, stack, &stack_ptr, &buffer, len); + + if (NPY_UNLIKELY(ret < 0)) { goto cleanup; } + + ret = 0; + +cleanup: + if (buffer.pw != NULL) { + free(buffer.pw); + } + return ret; +} + + +/**end repeat**/ + + + +/* + ***************************************************************************** + ** GENERIC SORT ** + ***************************************************************************** + */ + + +typedef struct { + char *pw; + npy_intp size; + size_t len; +} buffer_char; + + +static NPY_INLINE int +resize_buffer_char(buffer_char *buffer, npy_intp new_size) +{ + if (new_size <= buffer->size) { + return 0; + } + + if (NPY_UNLIKELY(buffer->pw == NULL)) { + buffer->pw = malloc(sizeof(char) * new_size * buffer->len); + } else { + buffer->pw = realloc(buffer->pw, sizeof(char) * new_size * buffer->len); + } + + buffer->size = new_size; + + if (NPY_UNLIKELY(buffer->pw == NULL)) { + return -NPY_ENOMEM; + } else { + return 0; + } +} + + +static npy_intp +npy_count_run(char *arr, npy_intp l, npy_intp num, npy_intp minrun, + char *vp, size_t len, PyArray_CompareFunc *cmp, PyArrayObject *py_arr) +{ + npy_intp sz; + char *pl, *pi, *pj, *pr; + + if (NPY_UNLIKELY(num - l == 1)) { + return 1; + } + + pl = arr + l * len; + + /* (not strictly) ascending sequence */ + if (cmp(pl, pl + len, py_arr) <= 0) { + for (pi = pl + len; pi < arr + (num - 1) * len + && cmp(pi, pi + len, py_arr) <= 0; pi += len) { + } + } else { /* (strictly) descending sequence */ + for (pi = pl + len; pi < arr + (num - 1) * len + && cmp(pi + len, pi, py_arr) < 0; pi += len) { + } + + for (pj = pl, pr = pi; pj < pr; pj += len, pr -= len) { + GENERIC_SWAP(pj, pr, len); + } + } + + pi += len; + sz = (pi - pl) / len; + + if (sz < minrun) { + if (l + minrun < num) { + sz = minrun; + } else { + sz = num - l; + } + + pr = pl + sz * len; + + /* insertion sort */ + for (; pi < pr; pi += len) { + GENERIC_COPY(vp, pi, len); + pj = pi; + + while (pl < pj && cmp(vp, pj - len, py_arr) < 0) { + GENERIC_COPY(pj, pj - len, len); + pj -= len; + } + + GENERIC_COPY(pj, vp, len); + } + } + + return sz; +} + + +static npy_intp +npy_gallop_right(const char *arr, const npy_intp size, const char *key, + size_t len, PyArray_CompareFunc *cmp, PyArrayObject *py_arr) +{ + npy_intp last_ofs, ofs, m; + + if (cmp(key, arr, py_arr) < 0) { + return 0; + } + + last_ofs = 0; + ofs = 1; + + for (;;) { + if (size <= ofs || ofs < 0) { + ofs = size; /* arr[ofs] is never accessed */ + break; + } + + if (cmp(key, arr + ofs * len, py_arr) < 0) { + break; + } else { + last_ofs = ofs; + /* ofs = 1, 3, 7, 15... */ + ofs = (ofs << 1) + 1; + } + } + + /* now that arr[last_ofs*len] <= key < arr[ofs*len] */ + while (last_ofs + 1 < ofs) { + m = last_ofs + ((ofs - last_ofs) >> 1); + + if (cmp(key, arr + m * len, py_arr) < 0) { + ofs = m; + } else { + last_ofs = m; + } + } + + /* now that arr[(ofs-1)*len] <= key < arr[ofs*len] */ + return ofs; +} + + + +static npy_intp +npy_gallop_left(const char *arr, const npy_intp size, const char *key, + size_t len, PyArray_CompareFunc *cmp, PyArrayObject *py_arr) +{ + npy_intp last_ofs, ofs, l, m, r; + + if (cmp(arr + (size - 1) * len, key, py_arr) < 0) { + return size; + } + + last_ofs = 0; + ofs = 1; + + for (;;) { + if (size <= ofs || ofs < 0) { + ofs = size; + break; + } + + if (cmp(arr + (size - ofs - 1) * len, key, py_arr) < 0) { + break; + } else { + last_ofs = ofs; + ofs = (ofs << 1) + 1; + } + } + + /* now that arr[(size-ofs-1)*len] < key <= arr[(size-last_ofs-1)*len] */ + l = size - ofs - 1; + r = size - last_ofs - 1; + + while (l + 1 < r) { + m = l + ((r - l) >> 1); + + if (cmp(arr + m * len, key, py_arr) < 0) { + l = m; + } else { + r = m; + } + } + + /* now that arr[(r-1)*len] < key <= arr[r*len] */ + return r; +} + + +static void +npy_merge_left(char *p1, npy_intp l1, char *p2, npy_intp l2, + char *p3, size_t len, + PyArray_CompareFunc *cmp, PyArrayObject *py_arr) +{ + char *end = p2 + l2 * len; + memcpy(p3, p1, sizeof(char) * l1 * len); + /* first element must be in p2 otherwise skipped in the caller */ + GENERIC_COPY(p1, p2, len); + p1 += len; + p2 += len; + + while (p1 < p2 && p2 < end) { + if (cmp(p2, p3, py_arr) < 0) { + GENERIC_COPY(p1, p2, len); + p1 += len; + p2 += len; + } else { + GENERIC_COPY(p1, p3, len); + p1 += len; + p3 += len; + } + } + + if (p1 != p2) { + memcpy(p1, p3, sizeof(char) * (p2 - p1)); + } +} + + +static void +npy_merge_right(char *p1, npy_intp l1, char *p2, npy_intp l2, + char *p3, size_t len, + PyArray_CompareFunc *cmp, PyArrayObject *py_arr) +{ + npy_intp ofs; + char *start = p1 - len; + memcpy(p3, p2, sizeof(char) * l2 * len); + p1 += (l1 - 1) * len; + p2 += (l2 - 1) * len; + p3 += (l2 - 1) * len; + /* first element must be in p1 otherwise skipped in the caller */ + GENERIC_COPY(p2, p1, len); + p2 -= len; + p1 -= len; + + while (p1 < p2 && start < p1) { + if (cmp(p3, p1, py_arr) < 0) { + GENERIC_COPY(p2, p1, len); + p2 -= len; + p1 -= len; + } else { + GENERIC_COPY(p2, p3, len); + p2 -= len; + p3 -= len; + } + } + + if (p1 != p2) { + ofs = p2 - start; + memcpy(start + len, p3 - ofs + len, sizeof(char) * ofs); + } +} + + + +static int +npy_merge_at(char *arr, const run *stack, const npy_intp at, + buffer_char *buffer, size_t len, + PyArray_CompareFunc *cmp, PyArrayObject *py_arr) +{ + int ret; + npy_intp s1, l1, s2, l2, k; + char *p1, *p2; + s1 = stack[at].s; + l1 = stack[at].l; + s2 = stack[at + 1].s; + l2 = stack[at + 1].l; + /* arr[s2] belongs to arr[s1+k] */ + GENERIC_COPY(buffer->pw, arr + s2 * len, len); + k = npy_gallop_right(arr + s1 * len, l1, buffer->pw, len, cmp, py_arr); + + if (l1 == k) { + /* already sorted */ + return 0; + } + + p1 = arr + (s1 + k) * len; + l1 -= k; + p2 = arr + s2 * len; + /* arr[s2-1] belongs to arr[s2+l2] */ + GENERIC_COPY(buffer->pw, arr + (s2 - 1) * len, len); + l2 = npy_gallop_left(arr + s2 * len, l2, buffer->pw, len, cmp, py_arr); + + if (l2 < l1) { + ret = resize_buffer_char(buffer, l2); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + npy_merge_right(p1, l1, p2, l2, buffer->pw, len, cmp, py_arr); + } else { + ret = resize_buffer_char(buffer, l1); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + npy_merge_left(p1, l1, p2, l2, buffer->pw, len, cmp, py_arr); + } + + return 0; +} + + +static int +npy_try_collapse(char *arr, run *stack, npy_intp *stack_ptr, + buffer_char *buffer, size_t len, + PyArray_CompareFunc *cmp, PyArrayObject *py_arr) +{ + int ret; + npy_intp A, B, C, top; + top = *stack_ptr; + + while (1 < top) { + B = stack[top - 2].l; + C = stack[top - 1].l; + + if ((2 < top && stack[top - 3].l <= B + C) || + (3 < top && stack[top - 4].l <= stack[top - 3].l + B)) { + A = stack[top - 3].l; + + if (A <= C) { + ret = npy_merge_at(arr, stack, top - 3, buffer, len, cmp, py_arr); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + stack[top - 3].l += B; + stack[top - 2] = stack[top - 1]; + --top; + } else { + ret = npy_merge_at(arr, stack, top - 2, buffer, len, cmp, py_arr); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + stack[top - 2].l += C; + --top; + } + } else if (1 < top && B <= C) { + ret = npy_merge_at(arr, stack, top - 2, buffer, len, cmp, py_arr); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + stack[top - 2].l += C; + --top; + } else { + break; + } + } + + *stack_ptr = top; + return 0; +} + + +static int +npy_force_collapse(char *arr, run *stack, npy_intp *stack_ptr, + buffer_char *buffer, size_t len, + PyArray_CompareFunc *cmp, PyArrayObject *py_arr) +{ + int ret; + npy_intp top = *stack_ptr; + + while (2 < top) { + if (stack[top - 3].l <= stack[top - 1].l) { + ret = npy_merge_at(arr, stack, top - 3, buffer, len, cmp, py_arr); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + stack[top - 3].l += stack[top - 2].l; + stack[top - 2] = stack[top - 1]; + --top; + } else { + ret = npy_merge_at(arr, stack, top - 2, buffer, len, cmp, py_arr); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + stack[top - 2].l += stack[top - 1].l; + --top; + } + } + + if (1 < top) { + ret = npy_merge_at(arr, stack, top - 2, buffer, len, cmp, py_arr); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + } + + return 0; +} + + +NPY_NO_EXPORT int +npy_timsort(void *start, npy_intp num, void *varr) +{ + PyArrayObject *arr = varr; + size_t len = PyArray_ITEMSIZE(arr); + PyArray_CompareFunc *cmp = PyArray_DESCR(arr)->f->compare; + int ret; + npy_intp l, n, stack_ptr, minrun; + run stack[TIMSORT_STACK_SIZE]; + buffer_char buffer; + + /* Items that have zero size don't make sense to sort */ + if (len == 0) { + return 0; + } + + buffer.pw = NULL; + buffer.size = 0; + buffer.len = len; + stack_ptr = 0; + minrun = compute_min_run_short(num); + + /* used for insertion sort and gallop key */ + ret = resize_buffer_char(&buffer, len); + + if (NPY_UNLIKELY(ret < 0)) { goto cleanup; } + + for (l = 0; l < num;) { + n = npy_count_run(start, l, num, minrun, buffer.pw, len, cmp, arr); + + /* both s and l are scaled by len */ + stack[stack_ptr].s = l; + stack[stack_ptr].l = n; + ++stack_ptr; + ret = npy_try_collapse(start, stack, &stack_ptr, &buffer, len, cmp, arr); + + if (NPY_UNLIKELY(ret < 0)) { goto cleanup; } + + l += n; + } + + ret = npy_force_collapse(start, stack, &stack_ptr, &buffer, len, cmp, arr); + + if (NPY_UNLIKELY(ret < 0)) { goto cleanup; } + + ret = 0; + +cleanup: + if (buffer.pw != NULL) { + free(buffer.pw); + } + return ret; +} + + +/* argsort */ + +static npy_intp +npy_acount_run(char *arr, npy_intp *tosort, npy_intp l, npy_intp num, + npy_intp minrun, size_t len, + PyArray_CompareFunc *cmp, PyArrayObject *py_arr) +{ + npy_intp sz; + npy_intp vi; + npy_intp *pl, *pi, *pj, *pr; + + if (NPY_UNLIKELY(num - l == 1)) { + return 1; + } + + pl = tosort + l; + + /* (not strictly) ascending sequence */ + if (cmp(arr + (*pl) * len, arr + (*(pl + 1)) * len, py_arr) <= 0) { + for (pi = pl + 1; pi < tosort + num - 1 + && cmp(arr + (*pi) * len, arr + (*(pi + 1)) * len, py_arr) <= 0; ++pi) { + } + } else { /* (strictly) descending sequence */ + for (pi = pl + 1; pi < tosort + num - 1 + && cmp(arr + (*(pi + 1)) * len, arr + (*pi) * len, py_arr) < 0; ++pi) { + } + + for (pj = pl, pr = pi; pj < pr; ++pj, --pr) { + INTP_SWAP(*pj, *pr); + } + } + + ++pi; + sz = pi - pl; + + if (sz < minrun) { + if (l + minrun < num) { + sz = minrun; + } else { + sz = num - l; + } + + pr = pl + sz; + + /* insertion sort */ + for (; pi < pr; ++pi) { + vi = *pi; + pj = pi; + + while (pl < pj && cmp(arr + vi * len, arr + (*(pj - 1)) * len, py_arr) < 0) { + *pj = *(pj - 1); + --pj; + } + + *pj = vi; + } + } + + return sz; +} + + +static npy_intp +npy_agallop_left(const char *arr, const npy_intp *tosort, + const npy_intp size, const char *key, size_t len, + PyArray_CompareFunc *cmp, PyArrayObject *py_arr) +{ + npy_intp last_ofs, ofs, l, m, r; + + if (cmp(arr + tosort[size - 1] * len, key, py_arr) < 0) { + return size; + } + + last_ofs = 0; + ofs = 1; + + for (;;) { + if (size <= ofs || ofs < 0) { + ofs = size; + break; + } + + if (cmp(arr + tosort[size - ofs - 1] * len, key, py_arr) < 0) { + break; + } else { + last_ofs = ofs; + ofs = (ofs << 1) + 1; + } + } + + /* now that arr[tosort[size-ofs-1]*len] < key <= arr[tosort[size-last_ofs-1]*len] */ + l = size - ofs - 1; + r = size - last_ofs - 1; + + while (l + 1 < r) { + m = l + ((r - l) >> 1); + + if (cmp(arr + tosort[m] * len, key, py_arr) < 0) { + l = m; + } else { + r = m; + } + } + + /* now that arr[tosort[r-1]*len] < key <= arr[tosort[r]*len] */ + return r; +} + + +static npy_intp +npy_agallop_right(const char *arr, const npy_intp *tosort, + const npy_intp size, const char *key, size_t len, + PyArray_CompareFunc *cmp, PyArrayObject *py_arr) +{ + npy_intp last_ofs, ofs, m; + + if (cmp(key, arr + tosort[0] * len, py_arr) < 0) { + return 0; + } + + last_ofs = 0; + ofs = 1; + + for (;;) { + if (size <= ofs || ofs < 0) { + ofs = size; /* arr[ofs] is never accessed */ + break; + } + + if (cmp(key, arr + tosort[ofs] * len, py_arr) < 0) { + break; + } else { + last_ofs = ofs; + /* ofs = 1, 3, 7, 15... */ + ofs = (ofs << 1) + 1; + } + } + + /* now that arr[tosort[last_ofs]*len] <= key < arr[tosort[ofs]*len] */ + while (last_ofs + 1 < ofs) { + m = last_ofs + ((ofs - last_ofs) >> 1); + + if (cmp(key, arr + tosort[m] * len, py_arr) < 0) { + ofs = m; + } else { + last_ofs = m; + } + } + + /* now that arr[tosort[ofs-1]*len] <= key < arr[tosort[ofs]*len] */ + return ofs; +} + + +static void +npy_amerge_left(char *arr, npy_intp *p1, npy_intp l1, npy_intp *p2, + npy_intp l2, npy_intp *p3, size_t len, + PyArray_CompareFunc *cmp, PyArrayObject *py_arr) +{ + npy_intp *end = p2 + l2; + memcpy(p3, p1, sizeof(npy_intp) * l1); + /* first element must be in p2 otherwise skipped in the caller */ + *p1++ = *p2++; + + while (p1 < p2 && p2 < end) { + if (cmp(arr + (*p2) * len, arr + (*p3) * len, py_arr) < 0) { + *p1++ = *p2++; + } else { + *p1++ = *p3++; + } + } + + if (p1 != p2) { + memcpy(p1, p3, sizeof(npy_intp) * (p2 - p1)); + } +} + + +static void +npy_amerge_right(char *arr, npy_intp* p1, npy_intp l1, npy_intp *p2, + npy_intp l2, npy_intp *p3, size_t len, + PyArray_CompareFunc *cmp, PyArrayObject *py_arr) +{ + npy_intp ofs; + npy_intp *start = p1 - 1; + memcpy(p3, p2, sizeof(npy_intp) * l2); + p1 += l1 - 1; + p2 += l2 - 1; + p3 += l2 - 1; + /* first element must be in p1 otherwise skipped in the caller */ + *p2-- = *p1--; + + while (p1 < p2 && start < p1) { + if (cmp(arr + (*p3) * len, arr + (*p1) * len, py_arr) < 0) { + *p2-- = *p1--; + } else { + *p2-- = *p3--; + } + } + + if (p1 != p2) { + ofs = p2 - start; + memcpy(start + 1, p3 - ofs + 1, sizeof(npy_intp) * ofs); + } +} + + + +static int +npy_amerge_at(char *arr, npy_intp *tosort, const run *stack, + const npy_intp at, buffer_intp *buffer, size_t len, + PyArray_CompareFunc *cmp, PyArrayObject *py_arr) +{ + int ret; + npy_intp s1, l1, s2, l2, k; + npy_intp *p1, *p2; + s1 = stack[at].s; + l1 = stack[at].l; + s2 = stack[at + 1].s; + l2 = stack[at + 1].l; + /* tosort[s2] belongs to tosort[s1+k] */ + k = npy_agallop_right(arr, tosort + s1, l1, arr + tosort[s2] * len, len, cmp, + py_arr); + + if (l1 == k) { + /* already sorted */ + return 0; + } + + p1 = tosort + s1 + k; + l1 -= k; + p2 = tosort + s2; + /* tosort[s2-1] belongs to tosort[s2+l2] */ + l2 = npy_agallop_left(arr, tosort + s2, l2, arr + tosort[s2 - 1] * len, + len, cmp, py_arr); + + if (l2 < l1) { + ret = resize_buffer_intp(buffer, l2); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + npy_amerge_right(arr, p1, l1, p2, l2, buffer->pw, len, cmp, py_arr); + } else { + ret = resize_buffer_intp(buffer, l1); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + npy_amerge_left(arr, p1, l1, p2, l2, buffer->pw, len, cmp, py_arr); + } + + return 0; +} + + +static int +npy_atry_collapse(char *arr, npy_intp *tosort, run *stack, + npy_intp *stack_ptr, buffer_intp *buffer, size_t len, + PyArray_CompareFunc *cmp, PyArrayObject *py_arr) +{ + int ret; + npy_intp A, B, C, top; + top = *stack_ptr; + + while (1 < top) { + B = stack[top - 2].l; + C = stack[top - 1].l; + + if ((2 < top && stack[top - 3].l <= B + C) || + (3 < top && stack[top - 4].l <= stack[top - 3].l + B)) { + A = stack[top - 3].l; + + if (A <= C) { + ret = npy_amerge_at(arr, tosort, stack, top - 3, buffer, len, cmp, py_arr); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + stack[top - 3].l += B; + stack[top - 2] = stack[top - 1]; + --top; + } else { + ret = npy_amerge_at(arr, tosort, stack, top - 2, buffer, len, cmp, py_arr); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + stack[top - 2].l += C; + --top; + } + } else if (1 < top && B <= C) { + ret = npy_amerge_at(arr, tosort, stack, top - 2, buffer, len, cmp, py_arr); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + stack[top - 2].l += C; + --top; + } else { + break; + } + } + + *stack_ptr = top; + return 0; +} + + +static int +npy_aforce_collapse(char *arr, npy_intp *tosort, run *stack, + npy_intp *stack_ptr, buffer_intp *buffer, size_t len, + PyArray_CompareFunc *cmp, PyArrayObject *py_arr) +{ + int ret; + npy_intp top = *stack_ptr; + + while (2 < top) { + if (stack[top - 3].l <= stack[top - 1].l) { + ret = npy_amerge_at(arr, tosort, stack, top - 3, buffer, len, cmp, py_arr); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + stack[top - 3].l += stack[top - 2].l; + stack[top - 2] = stack[top - 1]; + --top; + } else { + ret = npy_amerge_at(arr, tosort, stack, top - 2, buffer, len, cmp, py_arr); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + + stack[top - 2].l += stack[top - 1].l; + --top; + } + } + + if (1 < top) { + ret = npy_amerge_at(arr, tosort, stack, top - 2, buffer, len, cmp, py_arr); + + if (NPY_UNLIKELY(ret < 0)) { return ret; } + } + + return 0; +} + + +NPY_NO_EXPORT int +npy_atimsort(void *start, npy_intp *tosort, npy_intp num, void *varr) +{ + PyArrayObject *arr = varr; + size_t len = PyArray_ITEMSIZE(arr); + PyArray_CompareFunc *cmp = PyArray_DESCR(arr)->f->compare; + int ret; + npy_intp l, n, stack_ptr, minrun; + run stack[TIMSORT_STACK_SIZE]; + buffer_intp buffer; + + /* Items that have zero size don't make sense to sort */ + if (len == 0) { + return 0; + } + + buffer.pw = NULL; + buffer.size = 0; + stack_ptr = 0; + minrun = compute_min_run_short(num); + + for (l = 0; l < num;) { + n = npy_acount_run(start, tosort, l, num, minrun, len, cmp, arr); + /* both s and l are scaled by len */ + stack[stack_ptr].s = l; + stack[stack_ptr].l = n; + ++stack_ptr; + ret = npy_atry_collapse(start, tosort, stack, &stack_ptr, &buffer, len, cmp, + arr); + + if (NPY_UNLIKELY(ret < 0)) { goto cleanup; } + + l += n; + } + + ret = npy_aforce_collapse(start, tosort, stack, &stack_ptr, &buffer, len, + cmp, arr); + + if (NPY_UNLIKELY(ret < 0)) { goto cleanup; } + + ret = 0; + +cleanup: + if (buffer.pw != NULL) { + free(buffer.pw); + } + return ret; +} diff --git a/numpy/core/src/private/npy_cblas.h b/numpy/core/src/private/npy_cblas.h deleted file mode 100644 index a083f3bccb4d..000000000000 --- a/numpy/core/src/private/npy_cblas.h +++ /dev/null @@ -1,584 +0,0 @@ -/* - * This header provides numpy a consistent interface to CBLAS code. It is needed - * because not all providers of cblas provide cblas.h. For instance, MKL provides - * mkl_cblas.h and also typedefs the CBLAS_XXX enums. - */ -#ifndef _NPY_CBLAS_H_ -#define _NPY_CBLAS_H_ - -#include <stddef.h> - -/* Allow the use in C++ code. */ -#ifdef __cplusplus -extern "C" -{ -#endif - -/* - * Enumerated and derived types - */ -#define CBLAS_INDEX size_t /* this may vary between platforms */ - -enum CBLAS_ORDER {CblasRowMajor=101, CblasColMajor=102}; -enum CBLAS_TRANSPOSE {CblasNoTrans=111, CblasTrans=112, CblasConjTrans=113}; -enum CBLAS_UPLO {CblasUpper=121, CblasLower=122}; -enum CBLAS_DIAG {CblasNonUnit=131, CblasUnit=132}; -enum CBLAS_SIDE {CblasLeft=141, CblasRight=142}; - -/* - * =========================================================================== - * Prototypes for level 1 BLAS functions (complex are recast as routines) - * =========================================================================== - */ -float cblas_sdsdot(const int N, const float alpha, const float *X, - const int incX, const float *Y, const int incY); -double cblas_dsdot(const int N, const float *X, const int incX, const float *Y, - const int incY); -float cblas_sdot(const int N, const float *X, const int incX, - const float *Y, const int incY); -double cblas_ddot(const int N, const double *X, const int incX, - const double *Y, const int incY); - -/* - * Functions having prefixes Z and C only - */ -void cblas_cdotu_sub(const int N, const void *X, const int incX, - const void *Y, const int incY, void *dotu); -void cblas_cdotc_sub(const int N, const void *X, const int incX, - const void *Y, const int incY, void *dotc); - -void cblas_zdotu_sub(const int N, const void *X, const int incX, - const void *Y, const int incY, void *dotu); -void cblas_zdotc_sub(const int N, const void *X, const int incX, - const void *Y, const int incY, void *dotc); - - -/* - * Functions having prefixes S D SC DZ - */ -float cblas_snrm2(const int N, const float *X, const int incX); -float cblas_sasum(const int N, const float *X, const int incX); - -double cblas_dnrm2(const int N, const double *X, const int incX); -double cblas_dasum(const int N, const double *X, const int incX); - -float cblas_scnrm2(const int N, const void *X, const int incX); -float cblas_scasum(const int N, const void *X, const int incX); - -double cblas_dznrm2(const int N, const void *X, const int incX); -double cblas_dzasum(const int N, const void *X, const int incX); - - -/* - * Functions having standard 4 prefixes (S D C Z) - */ -CBLAS_INDEX cblas_isamax(const int N, const float *X, const int incX); -CBLAS_INDEX cblas_idamax(const int N, const double *X, const int incX); -CBLAS_INDEX cblas_icamax(const int N, const void *X, const int incX); -CBLAS_INDEX cblas_izamax(const int N, const void *X, const int incX); - -/* - * =========================================================================== - * Prototypes for level 1 BLAS routines - * =========================================================================== - */ - -/* - * Routines with standard 4 prefixes (s, d, c, z) - */ -void cblas_sswap(const int N, float *X, const int incX, - float *Y, const int incY); -void cblas_scopy(const int N, const float *X, const int incX, - float *Y, const int incY); -void cblas_saxpy(const int N, const float alpha, const float *X, - const int incX, float *Y, const int incY); - -void cblas_dswap(const int N, double *X, const int incX, - double *Y, const int incY); -void cblas_dcopy(const int N, const double *X, const int incX, - double *Y, const int incY); -void cblas_daxpy(const int N, const double alpha, const double *X, - const int incX, double *Y, const int incY); - -void cblas_cswap(const int N, void *X, const int incX, - void *Y, const int incY); -void cblas_ccopy(const int N, const void *X, const int incX, - void *Y, const int incY); -void cblas_caxpy(const int N, const void *alpha, const void *X, - const int incX, void *Y, const int incY); - -void cblas_zswap(const int N, void *X, const int incX, - void *Y, const int incY); -void cblas_zcopy(const int N, const void *X, const int incX, - void *Y, const int incY); -void cblas_zaxpy(const int N, const void *alpha, const void *X, - const int incX, void *Y, const int incY); - - -/* - * Routines with S and D prefix only - */ -void cblas_srotg(float *a, float *b, float *c, float *s); -void cblas_srotmg(float *d1, float *d2, float *b1, const float b2, float *P); -void cblas_srot(const int N, float *X, const int incX, - float *Y, const int incY, const float c, const float s); -void cblas_srotm(const int N, float *X, const int incX, - float *Y, const int incY, const float *P); - -void cblas_drotg(double *a, double *b, double *c, double *s); -void cblas_drotmg(double *d1, double *d2, double *b1, const double b2, double *P); -void cblas_drot(const int N, double *X, const int incX, - double *Y, const int incY, const double c, const double s); -void cblas_drotm(const int N, double *X, const int incX, - double *Y, const int incY, const double *P); - - -/* - * Routines with S D C Z CS and ZD prefixes - */ -void cblas_sscal(const int N, const float alpha, float *X, const int incX); -void cblas_dscal(const int N, const double alpha, double *X, const int incX); -void cblas_cscal(const int N, const void *alpha, void *X, const int incX); -void cblas_zscal(const int N, const void *alpha, void *X, const int incX); -void cblas_csscal(const int N, const float alpha, void *X, const int incX); -void cblas_zdscal(const int N, const double alpha, void *X, const int incX); - -/* - * =========================================================================== - * Prototypes for level 2 BLAS - * =========================================================================== - */ - -/* - * Routines with standard 4 prefixes (S, D, C, Z) - */ -void cblas_sgemv(const enum CBLAS_ORDER order, - const enum CBLAS_TRANSPOSE TransA, const int M, const int N, - const float alpha, const float *A, const int lda, - const float *X, const int incX, const float beta, - float *Y, const int incY); -void cblas_sgbmv(const enum CBLAS_ORDER order, - const enum CBLAS_TRANSPOSE TransA, const int M, const int N, - const int KL, const int KU, const float alpha, - const float *A, const int lda, const float *X, - const int incX, const float beta, float *Y, const int incY); -void cblas_strmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, - const int N, const float *A, const int lda, - float *X, const int incX); -void cblas_stbmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, - const int N, const int K, const float *A, const int lda, - float *X, const int incX); -void cblas_stpmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, - const int N, const float *Ap, float *X, const int incX); -void cblas_strsv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, - const int N, const float *A, const int lda, float *X, - const int incX); -void cblas_stbsv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, - const int N, const int K, const float *A, const int lda, - float *X, const int incX); -void cblas_stpsv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, - const int N, const float *Ap, float *X, const int incX); - -void cblas_dgemv(const enum CBLAS_ORDER order, - const enum CBLAS_TRANSPOSE TransA, const int M, const int N, - const double alpha, const double *A, const int lda, - const double *X, const int incX, const double beta, - double *Y, const int incY); -void cblas_dgbmv(const enum CBLAS_ORDER order, - const enum CBLAS_TRANSPOSE TransA, const int M, const int N, - const int KL, const int KU, const double alpha, - const double *A, const int lda, const double *X, - const int incX, const double beta, double *Y, const int incY); -void cblas_dtrmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, - const int N, const double *A, const int lda, - double *X, const int incX); -void cblas_dtbmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, - const int N, const int K, const double *A, const int lda, - double *X, const int incX); -void cblas_dtpmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, - const int N, const double *Ap, double *X, const int incX); -void cblas_dtrsv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, - const int N, const double *A, const int lda, double *X, - const int incX); -void cblas_dtbsv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, - const int N, const int K, const double *A, const int lda, - double *X, const int incX); -void cblas_dtpsv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, - const int N, const double *Ap, double *X, const int incX); - -void cblas_cgemv(const enum CBLAS_ORDER order, - const enum CBLAS_TRANSPOSE TransA, const int M, const int N, - const void *alpha, const void *A, const int lda, - const void *X, const int incX, const void *beta, - void *Y, const int incY); -void cblas_cgbmv(const enum CBLAS_ORDER order, - const enum CBLAS_TRANSPOSE TransA, const int M, const int N, - const int KL, const int KU, const void *alpha, - const void *A, const int lda, const void *X, - const int incX, const void *beta, void *Y, const int incY); -void cblas_ctrmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, - const int N, const void *A, const int lda, - void *X, const int incX); -void cblas_ctbmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, - const int N, const int K, const void *A, const int lda, - void *X, const int incX); -void cblas_ctpmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, - const int N, const void *Ap, void *X, const int incX); -void cblas_ctrsv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, - const int N, const void *A, const int lda, void *X, - const int incX); -void cblas_ctbsv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, - const int N, const int K, const void *A, const int lda, - void *X, const int incX); -void cblas_ctpsv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, - const int N, const void *Ap, void *X, const int incX); - -void cblas_zgemv(const enum CBLAS_ORDER order, - const enum CBLAS_TRANSPOSE TransA, const int M, const int N, - const void *alpha, const void *A, const int lda, - const void *X, const int incX, const void *beta, - void *Y, const int incY); -void cblas_zgbmv(const enum CBLAS_ORDER order, - const enum CBLAS_TRANSPOSE TransA, const int M, const int N, - const int KL, const int KU, const void *alpha, - const void *A, const int lda, const void *X, - const int incX, const void *beta, void *Y, const int incY); -void cblas_ztrmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, - const int N, const void *A, const int lda, - void *X, const int incX); -void cblas_ztbmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, - const int N, const int K, const void *A, const int lda, - void *X, const int incX); -void cblas_ztpmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, - const int N, const void *Ap, void *X, const int incX); -void cblas_ztrsv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, - const int N, const void *A, const int lda, void *X, - const int incX); -void cblas_ztbsv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, - const int N, const int K, const void *A, const int lda, - void *X, const int incX); -void cblas_ztpsv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_DIAG Diag, - const int N, const void *Ap, void *X, const int incX); - - -/* - * Routines with S and D prefixes only - */ -void cblas_ssymv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const int N, const float alpha, const float *A, - const int lda, const float *X, const int incX, - const float beta, float *Y, const int incY); -void cblas_ssbmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const int N, const int K, const float alpha, const float *A, - const int lda, const float *X, const int incX, - const float beta, float *Y, const int incY); -void cblas_sspmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const int N, const float alpha, const float *Ap, - const float *X, const int incX, - const float beta, float *Y, const int incY); -void cblas_sger(const enum CBLAS_ORDER order, const int M, const int N, - const float alpha, const float *X, const int incX, - const float *Y, const int incY, float *A, const int lda); -void cblas_ssyr(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const int N, const float alpha, const float *X, - const int incX, float *A, const int lda); -void cblas_sspr(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const int N, const float alpha, const float *X, - const int incX, float *Ap); -void cblas_ssyr2(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const int N, const float alpha, const float *X, - const int incX, const float *Y, const int incY, float *A, - const int lda); -void cblas_sspr2(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const int N, const float alpha, const float *X, - const int incX, const float *Y, const int incY, float *A); - -void cblas_dsymv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const int N, const double alpha, const double *A, - const int lda, const double *X, const int incX, - const double beta, double *Y, const int incY); -void cblas_dsbmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const int N, const int K, const double alpha, const double *A, - const int lda, const double *X, const int incX, - const double beta, double *Y, const int incY); -void cblas_dspmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const int N, const double alpha, const double *Ap, - const double *X, const int incX, - const double beta, double *Y, const int incY); -void cblas_dger(const enum CBLAS_ORDER order, const int M, const int N, - const double alpha, const double *X, const int incX, - const double *Y, const int incY, double *A, const int lda); -void cblas_dsyr(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const int N, const double alpha, const double *X, - const int incX, double *A, const int lda); -void cblas_dspr(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const int N, const double alpha, const double *X, - const int incX, double *Ap); -void cblas_dsyr2(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const int N, const double alpha, const double *X, - const int incX, const double *Y, const int incY, double *A, - const int lda); -void cblas_dspr2(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const int N, const double alpha, const double *X, - const int incX, const double *Y, const int incY, double *A); - - -/* - * Routines with C and Z prefixes only - */ -void cblas_chemv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const int N, const void *alpha, const void *A, - const int lda, const void *X, const int incX, - const void *beta, void *Y, const int incY); -void cblas_chbmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const int N, const int K, const void *alpha, const void *A, - const int lda, const void *X, const int incX, - const void *beta, void *Y, const int incY); -void cblas_chpmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const int N, const void *alpha, const void *Ap, - const void *X, const int incX, - const void *beta, void *Y, const int incY); -void cblas_cgeru(const enum CBLAS_ORDER order, const int M, const int N, - const void *alpha, const void *X, const int incX, - const void *Y, const int incY, void *A, const int lda); -void cblas_cgerc(const enum CBLAS_ORDER order, const int M, const int N, - const void *alpha, const void *X, const int incX, - const void *Y, const int incY, void *A, const int lda); -void cblas_cher(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const int N, const float alpha, const void *X, const int incX, - void *A, const int lda); -void cblas_chpr(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const int N, const float alpha, const void *X, - const int incX, void *A); -void cblas_cher2(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, const int N, - const void *alpha, const void *X, const int incX, - const void *Y, const int incY, void *A, const int lda); -void cblas_chpr2(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, const int N, - const void *alpha, const void *X, const int incX, - const void *Y, const int incY, void *Ap); - -void cblas_zhemv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const int N, const void *alpha, const void *A, - const int lda, const void *X, const int incX, - const void *beta, void *Y, const int incY); -void cblas_zhbmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const int N, const int K, const void *alpha, const void *A, - const int lda, const void *X, const int incX, - const void *beta, void *Y, const int incY); -void cblas_zhpmv(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const int N, const void *alpha, const void *Ap, - const void *X, const int incX, - const void *beta, void *Y, const int incY); -void cblas_zgeru(const enum CBLAS_ORDER order, const int M, const int N, - const void *alpha, const void *X, const int incX, - const void *Y, const int incY, void *A, const int lda); -void cblas_zgerc(const enum CBLAS_ORDER order, const int M, const int N, - const void *alpha, const void *X, const int incX, - const void *Y, const int incY, void *A, const int lda); -void cblas_zher(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const int N, const double alpha, const void *X, const int incX, - void *A, const int lda); -void cblas_zhpr(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, - const int N, const double alpha, const void *X, - const int incX, void *A); -void cblas_zher2(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, const int N, - const void *alpha, const void *X, const int incX, - const void *Y, const int incY, void *A, const int lda); -void cblas_zhpr2(const enum CBLAS_ORDER order, const enum CBLAS_UPLO Uplo, const int N, - const void *alpha, const void *X, const int incX, - const void *Y, const int incY, void *Ap); - -/* - * =========================================================================== - * Prototypes for level 3 BLAS - * =========================================================================== - */ - -/* - * Routines with standard 4 prefixes (S, D, C, Z) - */ -void cblas_sgemm(const enum CBLAS_ORDER Order, const enum CBLAS_TRANSPOSE TransA, - const enum CBLAS_TRANSPOSE TransB, const int M, const int N, - const int K, const float alpha, const float *A, - const int lda, const float *B, const int ldb, - const float beta, float *C, const int ldc); -void cblas_ssymm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, - const enum CBLAS_UPLO Uplo, const int M, const int N, - const float alpha, const float *A, const int lda, - const float *B, const int ldb, const float beta, - float *C, const int ldc); -void cblas_ssyrk(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo, - const enum CBLAS_TRANSPOSE Trans, const int N, const int K, - const float alpha, const float *A, const int lda, - const float beta, float *C, const int ldc); -void cblas_ssyr2k(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo, - const enum CBLAS_TRANSPOSE Trans, const int N, const int K, - const float alpha, const float *A, const int lda, - const float *B, const int ldb, const float beta, - float *C, const int ldc); -void cblas_strmm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, - const enum CBLAS_UPLO Uplo, const enum CBLAS_TRANSPOSE TransA, - const enum CBLAS_DIAG Diag, const int M, const int N, - const float alpha, const float *A, const int lda, - float *B, const int ldb); -void cblas_strsm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, - const enum CBLAS_UPLO Uplo, const enum CBLAS_TRANSPOSE TransA, - const enum CBLAS_DIAG Diag, const int M, const int N, - const float alpha, const float *A, const int lda, - float *B, const int ldb); - -void cblas_dgemm(const enum CBLAS_ORDER Order, const enum CBLAS_TRANSPOSE TransA, - const enum CBLAS_TRANSPOSE TransB, const int M, const int N, - const int K, const double alpha, const double *A, - const int lda, const double *B, const int ldb, - const double beta, double *C, const int ldc); -void cblas_dsymm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, - const enum CBLAS_UPLO Uplo, const int M, const int N, - const double alpha, const double *A, const int lda, - const double *B, const int ldb, const double beta, - double *C, const int ldc); -void cblas_dsyrk(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo, - const enum CBLAS_TRANSPOSE Trans, const int N, const int K, - const double alpha, const double *A, const int lda, - const double beta, double *C, const int ldc); -void cblas_dsyr2k(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo, - const enum CBLAS_TRANSPOSE Trans, const int N, const int K, - const double alpha, const double *A, const int lda, - const double *B, const int ldb, const double beta, - double *C, const int ldc); -void cblas_dtrmm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, - const enum CBLAS_UPLO Uplo, const enum CBLAS_TRANSPOSE TransA, - const enum CBLAS_DIAG Diag, const int M, const int N, - const double alpha, const double *A, const int lda, - double *B, const int ldb); -void cblas_dtrsm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, - const enum CBLAS_UPLO Uplo, const enum CBLAS_TRANSPOSE TransA, - const enum CBLAS_DIAG Diag, const int M, const int N, - const double alpha, const double *A, const int lda, - double *B, const int ldb); - -void cblas_cgemm(const enum CBLAS_ORDER Order, const enum CBLAS_TRANSPOSE TransA, - const enum CBLAS_TRANSPOSE TransB, const int M, const int N, - const int K, const void *alpha, const void *A, - const int lda, const void *B, const int ldb, - const void *beta, void *C, const int ldc); -void cblas_csymm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, - const enum CBLAS_UPLO Uplo, const int M, const int N, - const void *alpha, const void *A, const int lda, - const void *B, const int ldb, const void *beta, - void *C, const int ldc); -void cblas_csyrk(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo, - const enum CBLAS_TRANSPOSE Trans, const int N, const int K, - const void *alpha, const void *A, const int lda, - const void *beta, void *C, const int ldc); -void cblas_csyr2k(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo, - const enum CBLAS_TRANSPOSE Trans, const int N, const int K, - const void *alpha, const void *A, const int lda, - const void *B, const int ldb, const void *beta, - void *C, const int ldc); -void cblas_ctrmm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, - const enum CBLAS_UPLO Uplo, const enum CBLAS_TRANSPOSE TransA, - const enum CBLAS_DIAG Diag, const int M, const int N, - const void *alpha, const void *A, const int lda, - void *B, const int ldb); -void cblas_ctrsm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, - const enum CBLAS_UPLO Uplo, const enum CBLAS_TRANSPOSE TransA, - const enum CBLAS_DIAG Diag, const int M, const int N, - const void *alpha, const void *A, const int lda, - void *B, const int ldb); - -void cblas_zgemm(const enum CBLAS_ORDER Order, const enum CBLAS_TRANSPOSE TransA, - const enum CBLAS_TRANSPOSE TransB, const int M, const int N, - const int K, const void *alpha, const void *A, - const int lda, const void *B, const int ldb, - const void *beta, void *C, const int ldc); -void cblas_zsymm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, - const enum CBLAS_UPLO Uplo, const int M, const int N, - const void *alpha, const void *A, const int lda, - const void *B, const int ldb, const void *beta, - void *C, const int ldc); -void cblas_zsyrk(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo, - const enum CBLAS_TRANSPOSE Trans, const int N, const int K, - const void *alpha, const void *A, const int lda, - const void *beta, void *C, const int ldc); -void cblas_zsyr2k(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo, - const enum CBLAS_TRANSPOSE Trans, const int N, const int K, - const void *alpha, const void *A, const int lda, - const void *B, const int ldb, const void *beta, - void *C, const int ldc); -void cblas_ztrmm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, - const enum CBLAS_UPLO Uplo, const enum CBLAS_TRANSPOSE TransA, - const enum CBLAS_DIAG Diag, const int M, const int N, - const void *alpha, const void *A, const int lda, - void *B, const int ldb); -void cblas_ztrsm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, - const enum CBLAS_UPLO Uplo, const enum CBLAS_TRANSPOSE TransA, - const enum CBLAS_DIAG Diag, const int M, const int N, - const void *alpha, const void *A, const int lda, - void *B, const int ldb); - - -/* - * Routines with prefixes C and Z only - */ -void cblas_chemm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, - const enum CBLAS_UPLO Uplo, const int M, const int N, - const void *alpha, const void *A, const int lda, - const void *B, const int ldb, const void *beta, - void *C, const int ldc); -void cblas_cherk(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo, - const enum CBLAS_TRANSPOSE Trans, const int N, const int K, - const float alpha, const void *A, const int lda, - const float beta, void *C, const int ldc); -void cblas_cher2k(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo, - const enum CBLAS_TRANSPOSE Trans, const int N, const int K, - const void *alpha, const void *A, const int lda, - const void *B, const int ldb, const float beta, - void *C, const int ldc); - -void cblas_zhemm(const enum CBLAS_ORDER Order, const enum CBLAS_SIDE Side, - const enum CBLAS_UPLO Uplo, const int M, const int N, - const void *alpha, const void *A, const int lda, - const void *B, const int ldb, const void *beta, - void *C, const int ldc); -void cblas_zherk(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo, - const enum CBLAS_TRANSPOSE Trans, const int N, const int K, - const double alpha, const void *A, const int lda, - const double beta, void *C, const int ldc); -void cblas_zher2k(const enum CBLAS_ORDER Order, const enum CBLAS_UPLO Uplo, - const enum CBLAS_TRANSPOSE Trans, const int N, const int K, - const void *alpha, const void *A, const int lda, - const void *B, const int ldb, const double beta, - void *C, const int ldc); - -void cblas_xerbla(int p, const char *rout, const char *form, ...); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/numpy/core/src/private/npy_longdouble.c b/numpy/core/src/private/npy_longdouble.c deleted file mode 100644 index 508fbceac29e..000000000000 --- a/numpy/core/src/private/npy_longdouble.c +++ /dev/null @@ -1,102 +0,0 @@ -#include <Python.h> - -#define NPY_NO_DEPRECATED_API NPY_API_VERSION -#include "numpy/ndarraytypes.h" -#include "numpy/npy_math.h" - -/* This is a backport of Py_SETREF */ -#define NPY_SETREF(op, op2) \ - do { \ - PyObject *_py_tmp = (PyObject *)(op); \ - (op) = (op2); \ - Py_DECREF(_py_tmp); \ - } while (0) - - -/* - * Heavily derived from PyLong_FromDouble - * Notably, we can't set the digits directly, so have to shift and or instead. - */ -NPY_VISIBILITY_HIDDEN PyObject * -npy_longdouble_to_PyLong(npy_longdouble ldval) -{ - PyObject *v; - PyObject *l_chunk_size; - /* - * number of bits to extract at a time. CPython uses 30, but that's because - * it's tied to the internal long representation - */ - const int chunk_size = NPY_BITSOF_LONGLONG; - npy_longdouble frac; - int i, ndig, expo, neg; - neg = 0; - - if (npy_isinf(ldval)) { - PyErr_SetString(PyExc_OverflowError, - "cannot convert longdouble infinity to integer"); - return NULL; - } - if (npy_isnan(ldval)) { - PyErr_SetString(PyExc_ValueError, - "cannot convert longdouble NaN to integer"); - return NULL; - } - if (ldval < 0.0) { - neg = 1; - ldval = -ldval; - } - frac = npy_frexpl(ldval, &expo); /* ldval = frac*2**expo; 0.0 <= frac < 1.0 */ - v = PyLong_FromLong(0L); - if (v == NULL) - return NULL; - if (expo <= 0) - return v; - - ndig = (expo-1) / chunk_size + 1; - - l_chunk_size = PyLong_FromLong(chunk_size); - if (l_chunk_size == NULL) { - Py_DECREF(v); - return NULL; - } - - /* Get the MSBs of the integral part of the float */ - frac = npy_ldexpl(frac, (expo-1) % chunk_size + 1); - for (i = ndig; --i >= 0; ) { - npy_ulonglong chunk = (npy_ulonglong)frac; - PyObject *l_chunk; - /* v = v << chunk_size */ - NPY_SETREF(v, PyNumber_Lshift(v, l_chunk_size)); - if (v == NULL) { - goto done; - } - l_chunk = PyLong_FromUnsignedLongLong(chunk); - if (l_chunk == NULL) { - Py_DECREF(v); - v = NULL; - goto done; - } - /* v = v | chunk */ - NPY_SETREF(v, PyNumber_Or(v, l_chunk)); - Py_DECREF(l_chunk); - if (v == NULL) { - goto done; - } - - /* Remove the msbs, and repeat */ - frac = frac - (npy_longdouble) chunk; - frac = npy_ldexpl(frac, chunk_size); - } - - /* v = -v */ - if (neg) { - NPY_SETREF(v, PyNumber_Negative(v)); - if (v == NULL) { - goto done; - } - } - -done: - Py_DECREF(l_chunk_size); - return v; -} diff --git a/numpy/core/src/private/npy_longdouble.h b/numpy/core/src/private/npy_longdouble.h deleted file mode 100644 index 036b530709ce..000000000000 --- a/numpy/core/src/private/npy_longdouble.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef __NPY_LONGDOUBLE_H -#define __NPY_LONGDOUBLE_H - -#include "npy_config.h" -#include "numpy/ndarraytypes.h" - -/* Convert a npy_longdouble to a python `long` integer. - * - * Results are rounded towards zero. - * - * This performs the same task as PyLong_FromDouble, but for long doubles - * which have a greater range. - */ -NPY_VISIBILITY_HIDDEN PyObject * -npy_longdouble_to_PyLong(npy_longdouble ldval); - -#endif diff --git a/numpy/core/src/private/npy_pycompat.h b/numpy/core/src/private/npy_pycompat.h deleted file mode 100644 index aa0b5c1224d3..000000000000 --- a/numpy/core/src/private/npy_pycompat.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _NPY_PYCOMPAT_H_ -#define _NPY_PYCOMPAT_H_ - -#include "numpy/npy_3kcompat.h" - -#endif /* _NPY_COMPAT_H_ */ diff --git a/numpy/core/src/private/npy_sort.h b/numpy/core/src/private/npy_sort.h deleted file mode 100644 index 8c6f056231c6..000000000000 --- a/numpy/core/src/private/npy_sort.h +++ /dev/null @@ -1,204 +0,0 @@ -#ifndef __NPY_SORT_H__ -#define __NPY_SORT_H__ - -/* Python include is for future object sorts */ -#include <Python.h> -#include <numpy/npy_common.h> -#include <numpy/ndarraytypes.h> - -#define NPY_ENOMEM 1 -#define NPY_ECOMP 2 - -static NPY_INLINE int npy_get_msb(npy_uintp unum) -{ - int depth_limit = 0; - while (unum >>= 1) { - depth_limit++; - } - return depth_limit; -} - -int quicksort_bool(void *vec, npy_intp cnt, void *null); -int heapsort_bool(void *vec, npy_intp cnt, void *null); -int mergesort_bool(void *vec, npy_intp cnt, void *null); -int aquicksort_bool(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int aheapsort_bool(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int amergesort_bool(void *vec, npy_intp *ind, npy_intp cnt, void *null); - - -int quicksort_byte(void *vec, npy_intp cnt, void *null); -int heapsort_byte(void *vec, npy_intp cnt, void *null); -int mergesort_byte(void *vec, npy_intp cnt, void *null); -int aquicksort_byte(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int aheapsort_byte(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int amergesort_byte(void *vec, npy_intp *ind, npy_intp cnt, void *null); - - -int quicksort_ubyte(void *vec, npy_intp cnt, void *null); -int heapsort_ubyte(void *vec, npy_intp cnt, void *null); -int mergesort_ubyte(void *vec, npy_intp cnt, void *null); -int aquicksort_ubyte(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int aheapsort_ubyte(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int amergesort_ubyte(void *vec, npy_intp *ind, npy_intp cnt, void *null); - - -int quicksort_short(void *vec, npy_intp cnt, void *null); -int heapsort_short(void *vec, npy_intp cnt, void *null); -int mergesort_short(void *vec, npy_intp cnt, void *null); -int aquicksort_short(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int aheapsort_short(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int amergesort_short(void *vec, npy_intp *ind, npy_intp cnt, void *null); - - -int quicksort_ushort(void *vec, npy_intp cnt, void *null); -int heapsort_ushort(void *vec, npy_intp cnt, void *null); -int mergesort_ushort(void *vec, npy_intp cnt, void *null); -int aquicksort_ushort(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int aheapsort_ushort(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int amergesort_ushort(void *vec, npy_intp *ind, npy_intp cnt, void *null); - - -int quicksort_int(void *vec, npy_intp cnt, void *null); -int heapsort_int(void *vec, npy_intp cnt, void *null); -int mergesort_int(void *vec, npy_intp cnt, void *null); -int aquicksort_int(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int aheapsort_int(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int amergesort_int(void *vec, npy_intp *ind, npy_intp cnt, void *null); - - -int quicksort_uint(void *vec, npy_intp cnt, void *null); -int heapsort_uint(void *vec, npy_intp cnt, void *null); -int mergesort_uint(void *vec, npy_intp cnt, void *null); -int aquicksort_uint(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int aheapsort_uint(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int amergesort_uint(void *vec, npy_intp *ind, npy_intp cnt, void *null); - - -int quicksort_long(void *vec, npy_intp cnt, void *null); -int heapsort_long(void *vec, npy_intp cnt, void *null); -int mergesort_long(void *vec, npy_intp cnt, void *null); -int aquicksort_long(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int aheapsort_long(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int amergesort_long(void *vec, npy_intp *ind, npy_intp cnt, void *null); - - -int quicksort_ulong(void *vec, npy_intp cnt, void *null); -int heapsort_ulong(void *vec, npy_intp cnt, void *null); -int mergesort_ulong(void *vec, npy_intp cnt, void *null); -int aquicksort_ulong(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int aheapsort_ulong(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int amergesort_ulong(void *vec, npy_intp *ind, npy_intp cnt, void *null); - - -int quicksort_longlong(void *vec, npy_intp cnt, void *null); -int heapsort_longlong(void *vec, npy_intp cnt, void *null); -int mergesort_longlong(void *vec, npy_intp cnt, void *null); -int aquicksort_longlong(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int aheapsort_longlong(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int amergesort_longlong(void *vec, npy_intp *ind, npy_intp cnt, void *null); - - -int quicksort_ulonglong(void *vec, npy_intp cnt, void *null); -int heapsort_ulonglong(void *vec, npy_intp cnt, void *null); -int mergesort_ulonglong(void *vec, npy_intp cnt, void *null); -int aquicksort_ulonglong(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int aheapsort_ulonglong(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int amergesort_ulonglong(void *vec, npy_intp *ind, npy_intp cnt, void *null); - - -int quicksort_half(void *vec, npy_intp cnt, void *null); -int heapsort_half(void *vec, npy_intp cnt, void *null); -int mergesort_half(void *vec, npy_intp cnt, void *null); -int aquicksort_half(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int aheapsort_half(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int amergesort_half(void *vec, npy_intp *ind, npy_intp cnt, void *null); - - -int quicksort_float(void *vec, npy_intp cnt, void *null); -int heapsort_float(void *vec, npy_intp cnt, void *null); -int mergesort_float(void *vec, npy_intp cnt, void *null); -int aquicksort_float(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int aheapsort_float(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int amergesort_float(void *vec, npy_intp *ind, npy_intp cnt, void *null); - - -int quicksort_double(void *vec, npy_intp cnt, void *null); -int heapsort_double(void *vec, npy_intp cnt, void *null); -int mergesort_double(void *vec, npy_intp cnt, void *null); -int aquicksort_double(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int aheapsort_double(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int amergesort_double(void *vec, npy_intp *ind, npy_intp cnt, void *null); - - -int quicksort_longdouble(void *vec, npy_intp cnt, void *null); -int heapsort_longdouble(void *vec, npy_intp cnt, void *null); -int mergesort_longdouble(void *vec, npy_intp cnt, void *null); -int aquicksort_longdouble(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int aheapsort_longdouble(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int amergesort_longdouble(void *vec, npy_intp *ind, npy_intp cnt, void *null); - - -int quicksort_cfloat(void *vec, npy_intp cnt, void *null); -int heapsort_cfloat(void *vec, npy_intp cnt, void *null); -int mergesort_cfloat(void *vec, npy_intp cnt, void *null); -int aquicksort_cfloat(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int aheapsort_cfloat(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int amergesort_cfloat(void *vec, npy_intp *ind, npy_intp cnt, void *null); - - -int quicksort_cdouble(void *vec, npy_intp cnt, void *null); -int heapsort_cdouble(void *vec, npy_intp cnt, void *null); -int mergesort_cdouble(void *vec, npy_intp cnt, void *null); -int aquicksort_cdouble(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int aheapsort_cdouble(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int amergesort_cdouble(void *vec, npy_intp *ind, npy_intp cnt, void *null); - - -int quicksort_clongdouble(void *vec, npy_intp cnt, void *null); -int heapsort_clongdouble(void *vec, npy_intp cnt, void *null); -int mergesort_clongdouble(void *vec, npy_intp cnt, void *null); -int aquicksort_clongdouble(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int aheapsort_clongdouble(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int amergesort_clongdouble(void *vec, npy_intp *ind, npy_intp cnt, void *null); - - -int quicksort_string(void *vec, npy_intp cnt, void *arr); -int heapsort_string(void *vec, npy_intp cnt, void *arr); -int mergesort_string(void *vec, npy_intp cnt, void *arr); -int aquicksort_string(void *vec, npy_intp *ind, npy_intp cnt, void *arr); -int aheapsort_string(void *vec, npy_intp *ind, npy_intp cnt, void *arr); -int amergesort_string(void *vec, npy_intp *ind, npy_intp cnt, void *arr); - - -int quicksort_unicode(void *vec, npy_intp cnt, void *arr); -int heapsort_unicode(void *vec, npy_intp cnt, void *arr); -int mergesort_unicode(void *vec, npy_intp cnt, void *arr); -int aquicksort_unicode(void *vec, npy_intp *ind, npy_intp cnt, void *arr); -int aheapsort_unicode(void *vec, npy_intp *ind, npy_intp cnt, void *arr); -int amergesort_unicode(void *vec, npy_intp *ind, npy_intp cnt, void *arr); - - -int quicksort_datetime(void *vec, npy_intp cnt, void *null); -int heapsort_datetime(void *vec, npy_intp cnt, void *null); -int mergesort_datetime(void *vec, npy_intp cnt, void *null); -int aquicksort_datetime(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int aheapsort_datetime(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int amergesort_datetime(void *vec, npy_intp *ind, npy_intp cnt, void *null); - - -int quicksort_timedelta(void *vec, npy_intp cnt, void *null); -int heapsort_timedelta(void *vec, npy_intp cnt, void *null); -int mergesort_timedelta(void *vec, npy_intp cnt, void *null); -int aquicksort_timedelta(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int aheapsort_timedelta(void *vec, npy_intp *ind, npy_intp cnt, void *null); -int amergesort_timedelta(void *vec, npy_intp *ind, npy_intp cnt, void *null); - - -int npy_quicksort(void *vec, npy_intp cnt, void *arr); -int npy_heapsort(void *vec, npy_intp cnt, void *arr); -int npy_mergesort(void *vec, npy_intp cnt, void *arr); -int npy_aquicksort(void *vec, npy_intp *ind, npy_intp cnt, void *arr); -int npy_aheapsort(void *vec, npy_intp *ind, npy_intp cnt, void *arr); -int npy_amergesort(void *vec, npy_intp *ind, npy_intp cnt, void *arr); - -#endif diff --git a/numpy/core/src/private/ufunc_override.c b/numpy/core/src/private/ufunc_override.c deleted file mode 100644 index e405155cf9a5..000000000000 --- a/numpy/core/src/private/ufunc_override.c +++ /dev/null @@ -1,155 +0,0 @@ -#define NPY_NO_DEPRECATED_API NPY_API_VERSION -#define NO_IMPORT_ARRAY - -#include "npy_pycompat.h" -#include "get_attr_string.h" -#include "npy_import.h" - -#include "ufunc_override.h" - -/* - * Check whether an object has __array_ufunc__ defined on its class and it - * is not the default, i.e., the object is not an ndarray, and its - * __array_ufunc__ is not the same as that of ndarray. - * - * Returns a new reference, the value of type(obj).__array_ufunc__ - * - * If the __array_ufunc__ matches that of ndarray, or does not exist, return - * NULL. - * - * Note that since this module is used with both multiarray and umath, we do - * not have access to PyArray_Type and therewith neither to PyArray_CheckExact - * nor to the default __array_ufunc__ method, so instead we import locally. - * TODO: Can this really not be done more smartly? - */ -static PyObject * -get_non_default_array_ufunc(PyObject *obj) -{ - static PyObject *ndarray = NULL; - static PyObject *ndarray_array_ufunc = NULL; - PyObject *cls_array_ufunc; - - /* on first entry, import and cache ndarray and its __array_ufunc__ */ - if (ndarray == NULL) { - npy_cache_import("numpy.core.multiarray", "ndarray", &ndarray); - ndarray_array_ufunc = PyObject_GetAttrString(ndarray, - "__array_ufunc__"); - } - - /* Fast return for ndarray */ - if ((PyObject *)Py_TYPE(obj) == ndarray) { - return NULL; - } - /* does the class define __array_ufunc__? */ - cls_array_ufunc = PyArray_LookupSpecial(obj, "__array_ufunc__"); - if (cls_array_ufunc == NULL) { - return NULL; - } - /* is it different from ndarray.__array_ufunc__? */ - if (cls_array_ufunc != ndarray_array_ufunc) { - return cls_array_ufunc; - } - Py_DECREF(cls_array_ufunc); - return NULL; -} - -/* - * Check whether a set of input and output args have a non-default - * `__array_ufunc__` method. Return the number of overrides, setting - * corresponding objects in PyObject array with_override and the corresponding - * __array_ufunc__ methods in methods (both only if not NULL, and both using - * new references). - * - * returns -1 on failure. - */ -NPY_NO_EXPORT int -PyUFunc_WithOverride(PyObject *args, PyObject *kwds, - PyObject **with_override, PyObject **methods) -{ - int i; - - int nargs; - int nout_kwd = 0; - int out_kwd_is_tuple = 0; - int num_override_args = 0; - - PyObject *obj; - PyObject *out_kwd_obj = NULL; - /* - * Check inputs - */ - if (!PyTuple_Check(args)) { - PyErr_SetString(PyExc_TypeError, - "Internal Numpy error: call to PyUFunc_HasOverride " - "with non-tuple"); - goto fail; - } - nargs = PyTuple_GET_SIZE(args); - if (nargs > NPY_MAXARGS) { - PyErr_SetString(PyExc_TypeError, - "Internal Numpy error: too many arguments in call " - "to PyUFunc_HasOverride"); - goto fail; - } - /* be sure to include possible 'out' keyword argument. */ - if (kwds && PyDict_CheckExact(kwds)) { - out_kwd_obj = PyDict_GetItemString(kwds, "out"); - if (out_kwd_obj != NULL) { - out_kwd_is_tuple = PyTuple_CheckExact(out_kwd_obj); - if (out_kwd_is_tuple) { - nout_kwd = PyTuple_GET_SIZE(out_kwd_obj); - } - else { - nout_kwd = 1; - } - } - } - - for (i = 0; i < nargs + nout_kwd; ++i) { - PyObject *method; - if (i < nargs) { - obj = PyTuple_GET_ITEM(args, i); - } - else { - if (out_kwd_is_tuple) { - obj = PyTuple_GET_ITEM(out_kwd_obj, i - nargs); - } - else { - obj = out_kwd_obj; - } - } - /* - * Now see if the object provides an __array_ufunc__. However, we should - * ignore the base ndarray.__ufunc__, so we skip any ndarray as well as - * any ndarray subclass instances that did not override __array_ufunc__. - */ - method = get_non_default_array_ufunc(obj); - if (method != NULL) { - if (method == Py_None) { - PyErr_Format(PyExc_TypeError, - "operand '%.200s' does not support ufuncs " - "(__array_ufunc__=None)", - obj->ob_type->tp_name); - Py_DECREF(method); - goto fail; - } - if (with_override != NULL) { - Py_INCREF(obj); - with_override[num_override_args] = obj; - } - if (methods != NULL) { - methods[num_override_args] = method; - } - ++num_override_args; - } - } - return num_override_args; - -fail: - if (methods != NULL) { - for (i = 0; i < num_override_args; i++) { - Py_XDECREF(methods[i]); - } - } - return -1; -} diff --git a/numpy/core/src/private/ufunc_override.h b/numpy/core/src/private/ufunc_override.h deleted file mode 100644 index 2ed1c626fd21..000000000000 --- a/numpy/core/src/private/ufunc_override.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef __UFUNC_OVERRIDE_H -#define __UFUNC_OVERRIDE_H - -#include "npy_config.h" - -/* - * Check whether a set of input and output args have a non-default - * `__array_ufunc__` method. Returns the number of overrides, setting - * corresponding objects in PyObject array with_override (if not NULL). - * returns -1 on failure. - */ -NPY_NO_EXPORT int -PyUFunc_WithOverride(PyObject *args, PyObject *kwds, - PyObject **with_override, PyObject **methods); -#endif diff --git a/numpy/core/src/umath/_operand_flag_tests.c.src b/numpy/core/src/umath/_operand_flag_tests.c.src index 551a9c6329b7..c59e13baf6b1 100644 --- a/numpy/core/src/umath/_operand_flag_tests.c.src +++ b/numpy/core/src/umath/_operand_flag_tests.c.src @@ -1,6 +1,7 @@ -#define NPY_NO_DEPRECATED_API NPY_API_VERSION - +#define PY_SSIZE_T_CLEAN #include <Python.h> + +#define NPY_NO_DEPRECATED_API NPY_API_VERSION #include <numpy/arrayobject.h> #include <numpy/ufuncobject.h> #include "numpy/npy_3kcompat.h" @@ -14,7 +15,7 @@ static PyMethodDef TestMethods[] = { static void -inplace_add(char **args, npy_intp *dimensions, npy_intp *steps, void *data) +inplace_add(char **args, npy_intp const *dimensions, npy_intp const *steps, void *data) { npy_intp i; npy_intp n = dimensions[0]; @@ -39,7 +40,6 @@ static char types[2] = {NPY_LONG, NPY_LONG}; static void *data[1] = {NULL}; -#if defined(NPY_PY3K) static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "_operand_flag_tests", @@ -52,22 +52,12 @@ static struct PyModuleDef moduledef = { NULL }; -#define RETVAL m PyMODINIT_FUNC PyInit__operand_flag_tests(void) { -#else -#define RETVAL -PyMODINIT_FUNC init_operand_flag_tests(void) -{ -#endif PyObject *m = NULL; PyObject *ufunc; -#if defined(NPY_PY3K) m = PyModule_Create(&moduledef); -#else - m = Py_InitModule("_operand_flag_tests", TestMethods); -#endif if (m == NULL) { goto fail; } @@ -87,19 +77,16 @@ PyMODINIT_FUNC init_operand_flag_tests(void) ((PyUFuncObject*)ufunc)->iter_flags = NPY_ITER_REDUCE_OK; PyModule_AddObject(m, "inplace_add", (PyObject*)ufunc); - return RETVAL; + return m; fail: if (!PyErr_Occurred()) { PyErr_SetString(PyExc_RuntimeError, "cannot load _operand_flag_tests module."); } -#if defined(NPY_PY3K) if (m) { Py_DECREF(m); m = NULL; } -#endif - return RETVAL; - + return m; } diff --git a/numpy/core/src/umath/_rational_tests.c.src b/numpy/core/src/umath/_rational_tests.c.src index 9e74845df29b..bf50a2226ad1 100644 --- a/numpy/core/src/umath/_rational_tests.c.src +++ b/numpy/core/src/umath/_rational_tests.c.src @@ -1,16 +1,16 @@ /* Fixed size rational numbers exposed to Python */ - -#define NPY_NO_DEPRECATED_API NPY_API_VERSION - +#define PY_SSIZE_T_CLEAN #include <Python.h> #include <structmember.h> -#include <numpy/arrayobject.h> -#include <numpy/ufuncobject.h> -#include <numpy/npy_3kcompat.h> -#include <math.h> +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#include "numpy/arrayobject.h" +#include "numpy/ufuncobject.h" +#include "numpy/npy_3kcompat.h" #include "common.h" /* for error_converting */ +#include <math.h> + /* Relevant arithmetic exceptions */ @@ -406,8 +406,9 @@ pyrational_new(PyTypeObject* type, PyObject* args, PyObject* kwds) { Py_INCREF(x[0]); return x[0]; } - else if (PyString_Check(x[0])) { - const char* s = PyString_AS_STRING(x[0]); + // TODO: allow construction from unicode strings + else if (PyBytes_Check(x[0])) { + const char* s = PyBytes_AS_STRING(x[0]); rational x; if (scan_rational(&s,&x)) { const char* p; @@ -429,7 +430,7 @@ pyrational_new(PyTypeObject* type, PyObject* args, PyObject* kwds) { PyObject* y; int eq; x[i] = PyTuple_GET_ITEM(args, i); - n[i] = PyInt_AsLong(x[i]); + n[i] = PyLong_AsLong(x[i]); if (error_converting(n[i])) { if (PyErr_ExceptionMatches(PyExc_TypeError)) { PyErr_Format(PyExc_TypeError, @@ -440,7 +441,7 @@ pyrational_new(PyTypeObject* type, PyObject* args, PyObject* kwds) { return 0; } /* Check that we had an exact integer */ - y = PyInt_FromLong(n[i]); + y = PyLong_FromLong(n[i]); if (!y) { return 0; } @@ -477,7 +478,7 @@ pyrational_new(PyTypeObject* type, PyObject* args, PyObject* kwds) { else { \ PyObject* y_; \ int eq_; \ - long n_ = PyInt_AsLong(object); \ + long n_ = PyLong_AsLong(object); \ if (error_converting(n_)) { \ if (PyErr_ExceptionMatches(PyExc_TypeError)) { \ PyErr_Clear(); \ @@ -486,7 +487,7 @@ pyrational_new(PyTypeObject* type, PyObject* args, PyObject* kwds) { } \ return 0; \ } \ - y_ = PyInt_FromLong(n_); \ + y_ = PyLong_FromLong(n_); \ if (!y_) { \ return 0; \ } \ @@ -526,11 +527,11 @@ static PyObject* pyrational_repr(PyObject* self) { rational x = ((PyRational*)self)->r; if (d(x)!=1) { - return PyUString_FromFormat( + return PyUnicode_FromFormat( "rational(%ld,%ld)",(long)x.n,(long)d(x)); } else { - return PyUString_FromFormat( + return PyUnicode_FromFormat( "rational(%ld)",(long)x.n); } } @@ -539,11 +540,11 @@ static PyObject* pyrational_str(PyObject* self) { rational x = ((PyRational*)self)->r; if (d(x)!=1) { - return PyString_FromFormat( + return PyUnicode_FromFormat( "%ld/%ld",(long)x.n,(long)d(x)); } else { - return PyString_FromFormat( + return PyUnicode_FromFormat( "%ld",(long)x.n); } } @@ -590,7 +591,7 @@ RATIONAL_BINOP_2(floor_divide, } RATIONAL_UNOP(negative,rational,rational_negative(x),PyRational_FromRational) RATIONAL_UNOP(absolute,rational,rational_abs(x),PyRational_FromRational) -RATIONAL_UNOP(int,long,rational_int(x),PyInt_FromLong) +RATIONAL_UNOP(int,long,rational_int(x),PyLong_FromLong) RATIONAL_UNOP(float,double,rational_double(x),PyFloat_FromDouble) static PyObject* @@ -609,9 +610,6 @@ static PyNumberMethods pyrational_as_number = { pyrational_add, /* nb_add */ pyrational_subtract, /* nb_subtract */ pyrational_multiply, /* nb_multiply */ -#if PY_MAJOR_VERSION < 3 - pyrational_divide, /* nb_divide */ -#endif pyrational_remainder, /* nb_remainder */ 0, /* nb_divmod */ 0, /* nb_power */ @@ -625,27 +623,13 @@ static PyNumberMethods pyrational_as_number = { 0, /* nb_and */ 0, /* nb_xor */ 0, /* nb_or */ -#if PY_MAJOR_VERSION < 3 - 0, /* nb_coerce */ -#endif pyrational_int, /* nb_int */ -#if PY_MAJOR_VERSION < 3 - pyrational_int, /* nb_long */ -#else 0, /* reserved */ -#endif pyrational_float, /* nb_float */ -#if PY_MAJOR_VERSION < 3 - 0, /* nb_oct */ - 0, /* nb_hex */ -#endif 0, /* nb_inplace_add */ 0, /* nb_inplace_subtract */ 0, /* nb_inplace_multiply */ -#if PY_MAJOR_VERSION < 3 - 0, /* nb_inplace_divide */ -#endif 0, /* nb_inplace_remainder */ 0, /* nb_inplace_power */ 0, /* nb_inplace_lshift */ @@ -663,12 +647,12 @@ static PyNumberMethods pyrational_as_number = { static PyObject* pyrational_n(PyObject* self, void* closure) { - return PyInt_FromLong(((PyRational*)self)->r.n); + return PyLong_FromLong(((PyRational*)self)->r.n); } static PyObject* pyrational_d(PyObject* self, void* closure) { - return PyInt_FromLong(d(((PyRational*)self)->r)); + return PyLong_FromLong(d(((PyRational*)self)->r)); } static PyGetSetDef pyrational_getset[] = { @@ -678,24 +662,15 @@ static PyGetSetDef pyrational_getset[] = { }; static PyTypeObject PyRational_Type = { -#if defined(NPY_PY3K) PyVarObject_HEAD_INIT(NULL, 0) -#else - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ -#endif - "rational", /* tp_name */ + "numpy.core._rational_tests.rational", /* tp_name */ sizeof(PyRational), /* tp_basicsize */ 0, /* tp_itemsize */ 0, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ -#if defined(NPY_PY3K) 0, /* tp_reserved */ -#else - 0, /* tp_compare */ -#endif pyrational_repr, /* tp_repr */ &pyrational_as_number, /* tp_as_number */ 0, /* tp_as_sequence */ @@ -752,17 +727,17 @@ npyrational_setitem(PyObject* item, void* data, void* arr) { r = ((PyRational*)item)->r; } else { - long n = PyInt_AsLong(item); + long long n = PyLong_AsLongLong(item); PyObject* y; int eq; if (error_converting(n)) { return -1; } - y = PyInt_FromLong(n); + y = PyLong_FromLongLong(n); if (!y) { return -1; } - eq = PyObject_RichCompareBool(item,y,Py_EQ); + eq = PyObject_RichCompareBool(item, y, Py_EQ); Py_DECREF(y); if (eq<0) { return -1; @@ -774,7 +749,7 @@ npyrational_setitem(PyObject* item, void* data, void* arr) { } r = make_rational_int(n); } - memcpy(data,&r,sizeof(rational)); + memcpy(data, &r, sizeof(rational)); return 0; } @@ -962,8 +937,8 @@ DEFINE_CAST(npy_bool,rational,rational y = make_rational_int(x);) DEFINE_CAST(rational,npy_bool,npy_bool y = rational_nonzero(x);) #define BINARY_UFUNC(name,intype0,intype1,outtype,exp) \ - void name(char** args, npy_intp* dimensions, \ - npy_intp* steps, void* data) { \ + void name(char** args, npy_intp const *dimensions, \ + npy_intp const *steps, void* data) { \ npy_intp is0 = steps[0], is1 = steps[1], \ os = steps[2], n = *dimensions; \ char *i0 = args[0], *i1 = args[1], *o = args[2]; \ @@ -998,8 +973,8 @@ BINARY_UFUNC(gcd_ufunc,npy_int64,npy_int64,npy_int64,gcd(x,y)) BINARY_UFUNC(lcm_ufunc,npy_int64,npy_int64,npy_int64,lcm(x,y)) #define UNARY_UFUNC(name,type,exp) \ - void rational_ufunc_##name(char** args, npy_intp* dimensions, \ - npy_intp* steps, void* data) { \ + void rational_ufunc_##name(char** args, npy_intp const *dimensions, \ + npy_intp const *steps, void* data) { \ npy_intp is = steps[0], os = steps[1], n = *dimensions; \ char *i = args[0], *o = args[1]; \ int k; \ @@ -1022,7 +997,7 @@ UNARY_UFUNC(numerator,npy_int64,x.n) UNARY_UFUNC(denominator,npy_int64,d(x)) static NPY_INLINE void -rational_matrix_multiply(char **args, npy_intp *dimensions, npy_intp *steps) +rational_matrix_multiply(char **args, npy_intp const *dimensions, npy_intp const *steps) { /* pointers to data for input and output arrays */ char *ip1 = args[0]; @@ -1067,8 +1042,8 @@ rational_matrix_multiply(char **args, npy_intp *dimensions, npy_intp *steps) static void -rational_gufunc_matrix_multiply(char **args, npy_intp *dimensions, - npy_intp *steps, void *NPY_UNUSED(func)) +rational_gufunc_matrix_multiply(char **args, npy_intp const *dimensions, + npy_intp const *steps, void *NPY_UNUSED(func)) { /* outer dimensions counter */ npy_intp N_; @@ -1092,8 +1067,8 @@ rational_gufunc_matrix_multiply(char **args, npy_intp *dimensions, static void -rational_ufunc_test_add(char** args, npy_intp* dimensions, - npy_intp* steps, void* data) { +rational_ufunc_test_add(char** args, npy_intp const *dimensions, + npy_intp const *steps, void* data) { npy_intp is0 = steps[0], is1 = steps[1], os = steps[2], n = *dimensions; char *i0 = args[0], *i1 = args[1], *o = args[2]; int k; @@ -1108,8 +1083,8 @@ rational_ufunc_test_add(char** args, npy_intp* dimensions, static void -rational_ufunc_test_add_rationals(char** args, npy_intp* dimensions, - npy_intp* steps, void* data) { +rational_ufunc_test_add_rationals(char** args, npy_intp const *dimensions, + npy_intp const *steps, void* data) { npy_intp is0 = steps[0], is1 = steps[1], os = steps[2], n = *dimensions; char *i0 = args[0], *i1 = args[1], *o = args[2]; int k; @@ -1126,7 +1101,6 @@ PyMethodDef module_methods[] = { {0} /* sentinel */ }; -#if defined(NPY_PY3K) static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "_rational_tests", @@ -1138,16 +1112,8 @@ static struct PyModuleDef moduledef = { NULL, NULL }; -#endif -#if defined(NPY_PY3K) -#define RETVAL m PyMODINIT_FUNC PyInit__rational_tests(void) { -#else -#define RETVAL -PyMODINIT_FUNC init_rational_tests(void) { -#endif - PyObject *m = NULL; PyObject* numpy_str; PyObject* numpy; @@ -1161,7 +1127,7 @@ PyMODINIT_FUNC init_rational_tests(void) { if (PyErr_Occurred()) { goto fail; } - numpy_str = PyUString_FromString("numpy"); + numpy_str = PyUnicode_FromString("numpy"); if (!numpy_str) { goto fail; } @@ -1193,7 +1159,7 @@ PyMODINIT_FUNC init_rational_tests(void) { npyrational_arrfuncs.fill = npyrational_fill; npyrational_arrfuncs.fillwithscalar = npyrational_fillwithscalar; /* Left undefined: scanfunc, fromstr, sort, argsort */ - Py_TYPE(&npyrational_descr) = &PyArrayDescr_Type; + Py_SET_TYPE(&npyrational_descr, &PyArrayDescr_Type); npy_rational = PyArray_RegisterDataType(&npyrational_descr); if (npy_rational<0) { goto fail; @@ -1292,11 +1258,7 @@ PyMODINIT_FUNC init_rational_tests(void) { REGISTER_UFUNC_UNARY(sign) /* Create module */ -#if defined(NPY_PY3K) m = PyModule_Create(&moduledef); -#else - m = Py_InitModule("_rational_tests", module_methods); -#endif if (!m) { goto fail; @@ -1392,18 +1354,16 @@ PyMODINIT_FUNC init_rational_tests(void) { GCD_LCM_UFUNC(gcd,NPY_INT64,"greatest common denominator of two integers"); GCD_LCM_UFUNC(lcm,NPY_INT64,"least common multiple of two integers"); - return RETVAL; + return m; fail: if (!PyErr_Occurred()) { PyErr_SetString(PyExc_RuntimeError, "cannot load _rational_tests module."); } -#if defined(NPY_PY3K) if (m) { Py_DECREF(m); m = NULL; } -#endif - return RETVAL; + return m; } diff --git a/numpy/core/src/umath/_scaled_float_dtype.c b/numpy/core/src/umath/_scaled_float_dtype.c new file mode 100644 index 000000000000..b6c19362a5b4 --- /dev/null +++ b/numpy/core/src/umath/_scaled_float_dtype.c @@ -0,0 +1,821 @@ +/* + * This file implements a basic scaled float64 DType. The reason is to have + * a simple parametric DType for testing. It is not meant to be a useful + * DType by itself, but due to the scaling factor has similar properties as + * a Unit DType. + * + * The code here should be seen as a work in progress. Some choices are made + * to test certain code paths, but that does not mean that they must not + * be modified. + * + * NOTE: The tests were initially written using private API and ABI, ideally + * they should be replaced/modified with versions using public API. + */ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE +#define _UMATHMODULE + +#include "numpy/ndarrayobject.h" +#include "numpy/ufuncobject.h" + +#include "array_method.h" +#include "common.h" +#include "numpy/npy_math.h" +#include "convert_datatype.h" +#include "dtypemeta.h" +#include "dispatching.h" + + +typedef struct { + PyArray_Descr base; + double scaling; +} PyArray_SFloatDescr; + +static PyArray_DTypeMeta PyArray_SFloatDType; +static PyArray_SFloatDescr SFloatSingleton; + + +static int +sfloat_is_known_scalar_type(PyArray_DTypeMeta *NPY_UNUSED(cls), PyTypeObject *type) +{ + /* Accept only floats (some others may work due to normal casting) */ + if (type == &PyFloat_Type) { + return 1; + } + return 0; +} + + +static PyArray_Descr * +sfloat_default_descr(PyArray_DTypeMeta *NPY_UNUSED(cls)) +{ + Py_INCREF(&SFloatSingleton); + return (PyArray_Descr *)&SFloatSingleton; +} + + +static PyArray_Descr * +sfloat_discover_from_pyobject(PyArray_DTypeMeta *cls, PyObject *NPY_UNUSED(obj)) +{ + return sfloat_default_descr(cls); +} + + +static PyArray_DTypeMeta * +sfloat_common_dtype(PyArray_DTypeMeta *cls, PyArray_DTypeMeta *other) +{ + if (NPY_DT_is_legacy(other) && other->type_num == NPY_DOUBLE) { + Py_INCREF(cls); + return cls; + } + Py_INCREF(Py_NotImplemented); + return (PyArray_DTypeMeta *)Py_NotImplemented; +} + + +static PyArray_Descr * +sfloat_common_instance(PyArray_Descr *descr1, PyArray_Descr *descr2) +{ + PyArray_SFloatDescr *sf1 = (PyArray_SFloatDescr *)descr1; + PyArray_SFloatDescr *sf2 = (PyArray_SFloatDescr *)descr2; + /* We make the choice of using the larger scaling */ + if (sf1->scaling >= sf2->scaling) { + Py_INCREF(descr1); + return descr1; + } + Py_INCREF(descr2); + return descr2; +} + + +/* + * Implement minimal getitem and setitem to make this DType mostly(?) safe to + * expose in Python. + * TODO: This should not use the old-style API, but the new-style is missing! +*/ + +static PyObject * +sfloat_getitem(char *data, PyArrayObject *arr) +{ + PyArray_SFloatDescr *descr = (PyArray_SFloatDescr *)PyArray_DESCR(arr); + double value; + + memcpy(&value, data, sizeof(double)); + return PyFloat_FromDouble(value * descr->scaling); +} + + +static int +sfloat_setitem(PyObject *obj, char *data, PyArrayObject *arr) +{ + if (!PyFloat_CheckExact(obj)) { + PyErr_SetString(PyExc_NotImplementedError, + "Currently only accepts floats"); + return -1; + } + + PyArray_SFloatDescr *descr = (PyArray_SFloatDescr *)PyArray_DESCR(arr); + double value = PyFloat_AsDouble(obj); + value /= descr->scaling; + + memcpy(data, &value, sizeof(double)); + return 0; +} + + +/* Special DType methods and the descr->f slot storage */ +NPY_DType_Slots sfloat_slots = { + .default_descr = &sfloat_default_descr, + .discover_descr_from_pyobject = &sfloat_discover_from_pyobject, + .is_known_scalar_type = &sfloat_is_known_scalar_type, + .common_dtype = &sfloat_common_dtype, + .common_instance = &sfloat_common_instance, + .f = { + .getitem = (PyArray_GetItemFunc *)&sfloat_getitem, + .setitem = (PyArray_SetItemFunc *)&sfloat_setitem, + } +}; + + +static PyArray_SFloatDescr SFloatSingleton = {{ + .elsize = sizeof(double), + .alignment = _ALIGN(double), + .flags = NPY_USE_GETITEM|NPY_USE_SETITEM, + .type_num = -1, + .f = &sfloat_slots.f, + .byteorder = '|', /* do not bother with byte-swapping... */ + }, + .scaling = 1, +}; + + +static PyArray_Descr * +sfloat_scaled_copy(PyArray_SFloatDescr *self, double factor) { + PyArray_SFloatDescr *new = PyObject_New( + PyArray_SFloatDescr, (PyTypeObject *)&PyArray_SFloatDType); + if (new == NULL) { + return NULL; + } + /* Don't copy PyObject_HEAD part */ + memcpy((char *)new + sizeof(PyObject), + (char *)self + sizeof(PyObject), + sizeof(PyArray_SFloatDescr) - sizeof(PyObject)); + + new->scaling = new->scaling * factor; + return (PyArray_Descr *)new; +} + + +PyObject * +python_sfloat_scaled_copy(PyArray_SFloatDescr *self, PyObject *arg) +{ + if (!PyFloat_Check(arg)) { + PyErr_SetString(PyExc_TypeError, + "Scaling factor must be a python float."); + return NULL; + } + double factor = PyFloat_AsDouble(arg); + + return (PyObject *)sfloat_scaled_copy(self, factor); +} + + +static PyObject * +sfloat_get_scaling(PyArray_SFloatDescr *self, PyObject *NPY_UNUSED(args)) +{ + return PyFloat_FromDouble(self->scaling); +} + + +PyMethodDef sfloat_methods[] = { + {"scaled_by", + (PyCFunction)python_sfloat_scaled_copy, METH_O, + "Method to get a dtype copy with different scaling, mainly to " + "avoid having to implement many ways to create new instances."}, + {"get_scaling", + (PyCFunction)sfloat_get_scaling, METH_NOARGS, NULL}, + {NULL, NULL, 0, NULL} +}; + + +static PyObject * +sfloat_new(PyTypeObject *NPY_UNUSED(cls), PyObject *args, PyObject *kwds) +{ + double scaling = 1.; + static char *kwargs_strs[] = {"scaling", NULL}; + + if (!PyArg_ParseTupleAndKeywords( + args, kwds, "|d:_ScaledFloatTestDType", kwargs_strs, &scaling)) { + return NULL; + } + if (scaling == 1.) { + Py_INCREF(&SFloatSingleton); + return (PyObject *)&SFloatSingleton; + } + return (PyObject *)sfloat_scaled_copy(&SFloatSingleton, scaling); +} + + +static PyObject * +sfloat_repr(PyArray_SFloatDescr *self) +{ + PyObject *scaling = PyFloat_FromDouble(self->scaling); + if (scaling == NULL) { + return NULL; + } + PyObject *res = PyUnicode_FromFormat( + "_ScaledFloatTestDType(scaling=%R)", scaling); + Py_DECREF(scaling); + return res; +} + + +static PyArray_DTypeMeta PyArray_SFloatDType = {{{ + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "numpy._ScaledFloatTestDType", + .tp_methods = sfloat_methods, + .tp_new = sfloat_new, + .tp_repr = (reprfunc)sfloat_repr, + .tp_str = (reprfunc)sfloat_repr, + .tp_basicsize = sizeof(PyArray_SFloatDescr), + }}, + .type_num = -1, + .scalar_type = NULL, + .flags = NPY_DT_PARAMETRIC, + .dt_slots = &sfloat_slots, +}; + + +/* + * Implement some casts. + */ + +/* + * It would make more sense to test this early on, but this allows testing + * error returns. + */ +static int +check_factor(double factor) { + if (npy_isfinite(factor) && factor != 0.) { + return 0; + } + NPY_ALLOW_C_API_DEF; + NPY_ALLOW_C_API; + PyErr_SetString(PyExc_TypeError, + "error raised inside the core-loop: non-finite factor!"); + NPY_DISABLE_C_API; + return -1; +} + + +static int +cast_sfloat_to_sfloat_unaligned(PyArrayMethod_Context *context, + char *const data[], npy_intp const dimensions[], + npy_intp const strides[], NpyAuxData *NPY_UNUSED(auxdata)) +{ + /* could also be moved into auxdata: */ + double factor = ((PyArray_SFloatDescr *)context->descriptors[0])->scaling; + factor /= ((PyArray_SFloatDescr *)context->descriptors[1])->scaling; + if (check_factor(factor) < 0) { + return -1; + } + + npy_intp N = dimensions[0]; + char *in = data[0]; + char *out = data[1]; + for (npy_intp i = 0; i < N; i++) { + double tmp; + memcpy(&tmp, in, sizeof(double)); + tmp *= factor; + memcpy(out, &tmp, sizeof(double)); + + in += strides[0]; + out += strides[1]; + } + return 0; +} + + +static int +cast_sfloat_to_sfloat_aligned(PyArrayMethod_Context *context, + char *const data[], npy_intp const dimensions[], + npy_intp const strides[], NpyAuxData *NPY_UNUSED(auxdata)) +{ + /* could also be moved into auxdata: */ + double factor = ((PyArray_SFloatDescr *)context->descriptors[0])->scaling; + factor /= ((PyArray_SFloatDescr *)context->descriptors[1])->scaling; + if (check_factor(factor) < 0) { + return -1; + } + + npy_intp N = dimensions[0]; + char *in = data[0]; + char *out = data[1]; + for (npy_intp i = 0; i < N; i++) { + *(double *)out = *(double *)in * factor; + in += strides[0]; + out += strides[1]; + } + return 0; +} + + +static NPY_CASTING +sfloat_to_sfloat_resolve_descriptors( + PyArrayMethodObject *NPY_UNUSED(self), + PyArray_DTypeMeta *NPY_UNUSED(dtypes[2]), + PyArray_Descr *given_descrs[2], + PyArray_Descr *loop_descrs[2]) +{ + loop_descrs[0] = given_descrs[0]; + Py_INCREF(loop_descrs[0]); + + if (given_descrs[1] == NULL) { + loop_descrs[1] = given_descrs[0]; + } + else { + loop_descrs[1] = given_descrs[1]; + } + Py_INCREF(loop_descrs[1]); + + if (((PyArray_SFloatDescr *)loop_descrs[0])->scaling + == ((PyArray_SFloatDescr *)loop_descrs[1])->scaling) { + /* same scaling is just a view */ + return NPY_NO_CASTING | _NPY_CAST_IS_VIEW; + } + else if (-((PyArray_SFloatDescr *)loop_descrs[0])->scaling + == ((PyArray_SFloatDescr *)loop_descrs[1])->scaling) { + /* changing the sign does not lose precision */ + return NPY_EQUIV_CASTING; + } + /* Technically, this is not a safe cast, since over/underflows can occur */ + return NPY_SAME_KIND_CASTING; +} + + +/* + * Casting to and from doubles. + * + * To keep things interesting, we ONLY define the trivial cast with a factor + * of 1. All other casts have to be handled by the sfloat to sfloat cast. + * + * The casting machinery should optimize this step away normally, since we + * flag the this is a view. + */ +static int +cast_float_to_from_sfloat(PyArrayMethod_Context *NPY_UNUSED(context), + char *const data[], npy_intp const dimensions[], + npy_intp const strides[], NpyAuxData *NPY_UNUSED(auxdata)) +{ + npy_intp N = dimensions[0]; + char *in = data[0]; + char *out = data[1]; + for (npy_intp i = 0; i < N; i++) { + *(double *)out = *(double *)in; + in += strides[0]; + out += strides[1]; + } + return 0; +} + + +static NPY_CASTING +float_to_from_sfloat_resolve_descriptors( + PyArrayMethodObject *NPY_UNUSED(self), + PyArray_DTypeMeta *dtypes[2], + PyArray_Descr *NPY_UNUSED(given_descrs[2]), + PyArray_Descr *loop_descrs[2]) +{ + loop_descrs[0] = NPY_DT_CALL_default_descr(dtypes[0]); + if (loop_descrs[0] == NULL) { + return -1; + } + loop_descrs[1] = NPY_DT_CALL_default_descr(dtypes[1]); + if (loop_descrs[1] == NULL) { + return -1; + } + return NPY_NO_CASTING | _NPY_CAST_IS_VIEW; +} + + +/* + * Cast to boolean (for testing the logical functions a bit better). + */ +static int +cast_sfloat_to_bool(PyArrayMethod_Context *NPY_UNUSED(context), + char *const data[], npy_intp const dimensions[], + npy_intp const strides[], NpyAuxData *NPY_UNUSED(auxdata)) +{ + npy_intp N = dimensions[0]; + char *in = data[0]; + char *out = data[1]; + for (npy_intp i = 0; i < N; i++) { + *(npy_bool *)out = *(double *)in != 0; + in += strides[0]; + out += strides[1]; + } + return 0; +} + +static NPY_CASTING +sfloat_to_bool_resolve_descriptors( + PyArrayMethodObject *NPY_UNUSED(self), + PyArray_DTypeMeta *NPY_UNUSED(dtypes[2]), + PyArray_Descr *given_descrs[2], + PyArray_Descr *loop_descrs[2]) +{ + Py_INCREF(given_descrs[0]); + loop_descrs[0] = given_descrs[0]; + if (loop_descrs[0] == NULL) { + return -1; + } + loop_descrs[1] = PyArray_DescrFromType(NPY_BOOL); /* cannot fail */ + return NPY_UNSAFE_CASTING; +} + + +static int +init_casts(void) +{ + PyArray_DTypeMeta *dtypes[2] = {&PyArray_SFloatDType, &PyArray_SFloatDType}; + PyType_Slot slots[4] = {{0, NULL}}; + PyArrayMethod_Spec spec = { + .name = "sfloat_to_sfloat_cast", + .nin = 1, + .nout = 1, + .flags = NPY_METH_SUPPORTS_UNALIGNED, + .dtypes = dtypes, + .slots = slots, + /* minimal guaranteed casting */ + .casting = NPY_SAME_KIND_CASTING, + }; + + slots[0].slot = NPY_METH_resolve_descriptors; + slots[0].pfunc = &sfloat_to_sfloat_resolve_descriptors; + + slots[1].slot = NPY_METH_strided_loop; + slots[1].pfunc = &cast_sfloat_to_sfloat_aligned; + + slots[2].slot = NPY_METH_unaligned_strided_loop; + slots[2].pfunc = &cast_sfloat_to_sfloat_unaligned; + + if (PyArray_AddCastingImplementation_FromSpec(&spec, 0)) { + return -1; + } + + spec.name = "float_to_sfloat_cast"; + /* Technically, it is just a copy currently so this is fine: */ + spec.flags = NPY_METH_NO_FLOATINGPOINT_ERRORS; + PyArray_DTypeMeta *double_DType = PyArray_DTypeFromTypeNum(NPY_DOUBLE); + Py_DECREF(double_DType); /* immortal anyway */ + dtypes[0] = double_DType; + + slots[0].slot = NPY_METH_resolve_descriptors; + slots[0].pfunc = &float_to_from_sfloat_resolve_descriptors; + slots[1].slot = NPY_METH_strided_loop; + slots[1].pfunc = &cast_float_to_from_sfloat; + slots[2].slot = 0; + slots[2].pfunc = NULL; + + if (PyArray_AddCastingImplementation_FromSpec(&spec, 0)) { + return -1; + } + + spec.name = "sfloat_to_float_cast"; + dtypes[0] = &PyArray_SFloatDType; + dtypes[1] = double_DType; + + if (PyArray_AddCastingImplementation_FromSpec(&spec, 0)) { + return -1; + } + + slots[0].slot = NPY_METH_resolve_descriptors; + slots[0].pfunc = &sfloat_to_bool_resolve_descriptors; + slots[1].slot = NPY_METH_strided_loop; + slots[1].pfunc = &cast_sfloat_to_bool; + slots[2].slot = 0; + slots[2].pfunc = NULL; + + spec.name = "sfloat_to_bool_cast"; + dtypes[0] = &PyArray_SFloatDType; + dtypes[1] = PyArray_DTypeFromTypeNum(NPY_BOOL); + Py_DECREF(dtypes[1]); /* immortal anyway */ + + if (PyArray_AddCastingImplementation_FromSpec(&spec, 0)) { + return -1; + } + + return 0; +} + + +/* + * We also wish to test very simple ufunc functionality. So create two + * ufunc loops: + * 1. Multiplication, which can multiply the factors and work with that. + * 2. Addition, which needs to use the common instance, and runs into + * cast safety subtleties since we will implement it without an additional + * cast. + */ +static int +multiply_sfloats(PyArrayMethod_Context *NPY_UNUSED(context), + char *const data[], npy_intp const dimensions[], + npy_intp const strides[], NpyAuxData *NPY_UNUSED(auxdata)) +{ + npy_intp N = dimensions[0]; + char *in1 = data[0]; + char *in2 = data[1]; + char *out = data[2]; + for (npy_intp i = 0; i < N; i++) { + *(double *)out = *(double *)in1 * *(double *)in2; + in1 += strides[0]; + in2 += strides[1]; + out += strides[2]; + } + return 0; +} + + +static NPY_CASTING +multiply_sfloats_resolve_descriptors( + PyArrayMethodObject *NPY_UNUSED(self), + PyArray_DTypeMeta *NPY_UNUSED(dtypes[3]), + PyArray_Descr *given_descrs[3], + PyArray_Descr *loop_descrs[3]) +{ + /* + * Multiply the scaling for the result. If the result was passed in we + * simply ignore it and let the casting machinery fix it up here. + */ + double factor = ((PyArray_SFloatDescr *)given_descrs[1])->scaling; + loop_descrs[2] = sfloat_scaled_copy( + (PyArray_SFloatDescr *)given_descrs[0], factor); + if (loop_descrs[2] == 0) { + return -1; + } + Py_INCREF(given_descrs[0]); + loop_descrs[0] = given_descrs[0]; + Py_INCREF(given_descrs[1]); + loop_descrs[1] = given_descrs[1]; + return NPY_NO_CASTING; +} + + +/* + * Unlike the multiplication implementation above, this loops deals with + * scaling (casting) internally. This allows to test some different paths. + */ +static int +add_sfloats(PyArrayMethod_Context *context, + char *const data[], npy_intp const dimensions[], + npy_intp const strides[], NpyAuxData *NPY_UNUSED(auxdata)) +{ + double fin1 = ((PyArray_SFloatDescr *)context->descriptors[0])->scaling; + double fin2 = ((PyArray_SFloatDescr *)context->descriptors[1])->scaling; + double fout = ((PyArray_SFloatDescr *)context->descriptors[2])->scaling; + + double fact1 = fin1 / fout; + double fact2 = fin2 / fout; + if (check_factor(fact1) < 0) { + return -1; + } + if (check_factor(fact2) < 0) { + return -1; + } + + npy_intp N = dimensions[0]; + char *in1 = data[0]; + char *in2 = data[1]; + char *out = data[2]; + for (npy_intp i = 0; i < N; i++) { + *(double *)out = (*(double *)in1 * fact1) + (*(double *)in2 * fact2); + in1 += strides[0]; + in2 += strides[1]; + out += strides[2]; + } + return 0; +} + + +static NPY_CASTING +add_sfloats_resolve_descriptors( + PyArrayMethodObject *NPY_UNUSED(self), + PyArray_DTypeMeta *NPY_UNUSED(dtypes[3]), + PyArray_Descr *given_descrs[3], + PyArray_Descr *loop_descrs[3]) +{ + /* + * Here we accept an output descriptor (the inner loop can deal with it), + * if none is given, we use the "common instance": + */ + if (given_descrs[2] == NULL) { + loop_descrs[2] = sfloat_common_instance( + given_descrs[0], given_descrs[1]); + if (loop_descrs[2] == 0) { + return -1; + } + } + else { + Py_INCREF(given_descrs[2]); + loop_descrs[2] = given_descrs[2]; + } + Py_INCREF(given_descrs[0]); + loop_descrs[0] = given_descrs[0]; + Py_INCREF(given_descrs[1]); + loop_descrs[1] = given_descrs[1]; + + /* If the factors mismatch, we do implicit casting inside the ufunc! */ + double fin1 = ((PyArray_SFloatDescr *)loop_descrs[0])->scaling; + double fin2 = ((PyArray_SFloatDescr *)loop_descrs[1])->scaling; + double fout = ((PyArray_SFloatDescr *)loop_descrs[2])->scaling; + + if (fin1 == fout && fin2 == fout) { + return NPY_NO_CASTING; + } + if (npy_fabs(fin1) == npy_fabs(fout) && npy_fabs(fin2) == npy_fabs(fout)) { + return NPY_EQUIV_CASTING; + } + return NPY_SAME_KIND_CASTING; +} + + +static int +add_loop(const char *ufunc_name, + PyArray_DTypeMeta *dtypes[3], PyObject *meth_or_promoter) +{ + PyObject *mod = PyImport_ImportModule("numpy"); + if (mod == NULL) { + return -1; + } + PyObject *ufunc = PyObject_GetAttrString(mod, ufunc_name); + Py_DECREF(mod); + if (!PyObject_TypeCheck(ufunc, &PyUFunc_Type)) { + Py_DECREF(ufunc); + PyErr_Format(PyExc_TypeError, + "numpy.%s was not a ufunc!", ufunc_name); + return -1; + } + PyObject *dtype_tup = PyArray_TupleFromItems(3, (PyObject **)dtypes, 1); + if (dtype_tup == NULL) { + Py_DECREF(ufunc); + return -1; + } + PyObject *info = PyTuple_Pack(2, dtype_tup, meth_or_promoter); + Py_DECREF(dtype_tup); + if (info == NULL) { + Py_DECREF(ufunc); + return -1; + } + int res = PyUFunc_AddLoop((PyUFuncObject *)ufunc, info, 0); + Py_DECREF(ufunc); + Py_DECREF(info); + return res; +} + + + +/* + * We add some very basic promoters to allow multiplying normal and scaled + */ +static int +promote_to_sfloat(PyUFuncObject *NPY_UNUSED(ufunc), + PyArray_DTypeMeta *const NPY_UNUSED(dtypes[3]), + PyArray_DTypeMeta *const signature[3], + PyArray_DTypeMeta *new_dtypes[3]) +{ + for (int i = 0; i < 3; i++) { + PyArray_DTypeMeta *new = &PyArray_SFloatDType; + if (signature[i] != NULL) { + new = signature[i]; + } + Py_INCREF(new); + new_dtypes[i] = new; + } + return 0; +} + + +/* + * Add new ufunc loops (this is somewhat clumsy as of writing it, but should + * get less so with the introduction of public API). + */ +static int +init_ufuncs(void) { + PyArray_DTypeMeta *dtypes[3] = { + &PyArray_SFloatDType, &PyArray_SFloatDType, &PyArray_SFloatDType}; + PyType_Slot slots[3] = {{0, NULL}}; + PyArrayMethod_Spec spec = { + .nin = 2, + .nout =1, + .dtypes = dtypes, + .slots = slots, + }; + spec.name = "sfloat_multiply"; + spec.casting = NPY_NO_CASTING; + + slots[0].slot = NPY_METH_resolve_descriptors; + slots[0].pfunc = &multiply_sfloats_resolve_descriptors; + slots[1].slot = NPY_METH_strided_loop; + slots[1].pfunc = &multiply_sfloats; + PyBoundArrayMethodObject *bmeth = PyArrayMethod_FromSpec_int(&spec, 0); + if (bmeth == NULL) { + return -1; + } + int res = add_loop("multiply", + bmeth->dtypes, (PyObject *)bmeth->method); + Py_DECREF(bmeth); + if (res < 0) { + return -1; + } + + spec.name = "sfloat_add"; + spec.casting = NPY_SAME_KIND_CASTING; + + slots[0].slot = NPY_METH_resolve_descriptors; + slots[0].pfunc = &add_sfloats_resolve_descriptors; + slots[1].slot = NPY_METH_strided_loop; + slots[1].pfunc = &add_sfloats; + bmeth = PyArrayMethod_FromSpec_int(&spec, 0); + if (bmeth == NULL) { + return -1; + } + res = add_loop("add", + bmeth->dtypes, (PyObject *)bmeth->method); + Py_DECREF(bmeth); + if (res < 0) { + return -1; + } + + /* + * Add a promoter for both directions of multiply with double. + */ + PyArray_DTypeMeta *double_DType = PyArray_DTypeFromTypeNum(NPY_DOUBLE); + Py_DECREF(double_DType); /* immortal anyway */ + + PyArray_DTypeMeta *promoter_dtypes[3] = { + &PyArray_SFloatDType, double_DType, NULL}; + + PyObject *promoter = PyCapsule_New( + &promote_to_sfloat, "numpy._ufunc_promoter", NULL); + if (promoter == NULL) { + return -1; + } + res = add_loop("multiply", promoter_dtypes, promoter); + if (res < 0) { + Py_DECREF(promoter); + return -1; + } + promoter_dtypes[0] = double_DType; + promoter_dtypes[1] = &PyArray_SFloatDType; + res = add_loop("multiply", promoter_dtypes, promoter); + Py_DECREF(promoter); + if (res < 0) { + return -1; + } + + return 0; +} + + +/* + * Python entry point, exported via `umathmodule.h` and `multiarraymodule.c`. + * TODO: Should be moved when the necessary API is not internal anymore. + */ +NPY_NO_EXPORT PyObject * +get_sfloat_dtype(PyObject *NPY_UNUSED(mod), PyObject *NPY_UNUSED(args)) +{ + /* Allow calling the function multiple times. */ + static npy_bool initialized = NPY_FALSE; + + if (initialized) { + Py_INCREF(&PyArray_SFloatDType); + return (PyObject *)&PyArray_SFloatDType; + } + + PyArray_SFloatDType.super.ht_type.tp_base = &PyArrayDescr_Type; + + if (PyType_Ready((PyTypeObject *)&PyArray_SFloatDType) < 0) { + return NULL; + } + NPY_DT_SLOTS(&PyArray_SFloatDType)->castingimpls = PyDict_New(); + if (NPY_DT_SLOTS(&PyArray_SFloatDType)->castingimpls == NULL) { + return NULL; + } + + PyObject *o = PyObject_Init( + (PyObject *)&SFloatSingleton, (PyTypeObject *)&PyArray_SFloatDType); + if (o == NULL) { + return NULL; + } + + if (init_casts() < 0) { + return NULL; + } + + if (init_ufuncs() < 0) { + return NULL; + } + + initialized = NPY_TRUE; + return (PyObject *)&PyArray_SFloatDType; +} diff --git a/numpy/core/src/umath/_struct_ufunc_tests.c.src b/numpy/core/src/umath/_struct_ufunc_tests.c.src index b831d5c2aa52..ee71c4698f79 100644 --- a/numpy/core/src/umath/_struct_ufunc_tests.c.src +++ b/numpy/core/src/umath/_struct_ufunc_tests.c.src @@ -1,11 +1,13 @@ -#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define PY_SSIZE_T_CLEAN +#include <Python.h> -#include "Python.h" -#include "math.h" +#define NPY_NO_DEPRECATED_API NPY_API_VERSION #include "numpy/ndarraytypes.h" #include "numpy/ufuncobject.h" #include "numpy/npy_3kcompat.h" +#include <math.h> + /* * struct_ufunc_test.c @@ -17,14 +19,10 @@ * docs.python.org . */ -static PyMethodDef StructUfuncTestMethods[] = { - {NULL, NULL, 0, NULL} -}; - -/* The loop definition must precede the PyMODINIT_FUNC. */ - -static void add_uint64_triplet(char **args, npy_intp *dimensions, - npy_intp* steps, void* data) +static void add_uint64_triplet(char **args, + npy_intp const *dimensions, + npy_intp const* steps, + void* data) { npy_intp i; npy_intp is1=steps[0]; @@ -53,7 +51,59 @@ static void add_uint64_triplet(char **args, npy_intp *dimensions, } } -#if defined(NPY_PY3K) +static PyObject* +register_fail(PyObject* NPY_UNUSED(self), PyObject* NPY_UNUSED(args)) +{ + PyObject *add_triplet; + PyObject *dtype_dict; + PyArray_Descr *dtype; + PyArray_Descr *dtypes[3]; + int retval; + + add_triplet = PyUFunc_FromFuncAndData(NULL, NULL, NULL, 0, 2, 1, + PyUFunc_None, "add_triplet", + "add_triplet_docstring", 0); + + dtype_dict = Py_BuildValue("[(s, s), (s, s), (s, s)]", + "f0", "u8", "f1", "u8", "f2", "u8"); + PyArray_DescrConverter(dtype_dict, &dtype); + Py_DECREF(dtype_dict); + + dtypes[0] = dtype; + dtypes[1] = dtype; + dtypes[2] = dtype; + + retval = PyUFunc_RegisterLoopForDescr((PyUFuncObject *)add_triplet, + dtype, + &add_uint64_triplet, + dtypes, + NULL); + + if (retval < 0) { + Py_DECREF(add_triplet); + Py_DECREF(dtype); + return NULL; + } + retval = PyUFunc_RegisterLoopForDescr((PyUFuncObject *)add_triplet, + dtype, + &add_uint64_triplet, + dtypes, + NULL); + Py_DECREF(add_triplet); + Py_DECREF(dtype); + if (retval < 0) { + return NULL; + } + Py_RETURN_NONE; +} + +static PyMethodDef StructUfuncTestMethods[] = { + {"register_fail", + register_fail, + METH_NOARGS, NULL}, + {NULL, NULL, 0, NULL} +}; + static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "_struct_ufunc_tests", @@ -65,31 +115,18 @@ static struct PyModuleDef moduledef = { NULL, NULL }; -#endif -#if defined(NPY_PY3K) PyMODINIT_FUNC PyInit__struct_ufunc_tests(void) -#else -PyMODINIT_FUNC init_struct_ufunc_tests(void) -#endif { PyObject *m, *add_triplet, *d; PyObject *dtype_dict; PyArray_Descr *dtype; PyArray_Descr *dtypes[3]; -#if defined(NPY_PY3K) m = PyModule_Create(&moduledef); -#else - m = Py_InitModule("_struct_ufunc_tests", StructUfuncTestMethods); -#endif if (m == NULL) { -#if defined(NPY_PY3K) return NULL; -#else - return; -#endif } import_array(); @@ -100,7 +137,7 @@ PyMODINIT_FUNC init_struct_ufunc_tests(void) "add_triplet_docstring", 0); dtype_dict = Py_BuildValue("[(s, s), (s, s), (s, s)]", - "f0", "u8", "f1", "u8", "f2", "u8"); + "f0", "u8", "f1", "u8", "f2", "u8"); PyArray_DescrConverter(dtype_dict, &dtype); Py_DECREF(dtype_dict); @@ -114,11 +151,10 @@ PyMODINIT_FUNC init_struct_ufunc_tests(void) dtypes, NULL); + Py_DECREF(dtype); d = PyModule_GetDict(m); PyDict_SetItemString(d, "add_triplet", add_triplet); Py_DECREF(add_triplet); -#if defined(NPY_PY3K) return m; -#endif } diff --git a/numpy/core/src/umath/_umath_tests.c.src b/numpy/core/src/umath/_umath_tests.c.src index fcbdbe330867..ce42fc2711a3 100644 --- a/numpy/core/src/umath/_umath_tests.c.src +++ b/numpy/core/src/umath/_umath_tests.c.src @@ -5,9 +5,10 @@ ** INCLUDES ** ***************************************************************************** */ -#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define PY_SSIZE_T_CLEAN +#include <Python.h> -#include "Python.h" +#define NPY_NO_DEPRECATED_API NPY_API_VERSION #include "numpy/arrayobject.h" #include "numpy/ufuncobject.h" #include "numpy/npy_math.h" @@ -57,6 +58,19 @@ ***************************************************************************** */ +static void +always_error_loop( + char **NPY_UNUSED(args), npy_intp const *NPY_UNUSED(dimensions), + npy_intp const *NPY_UNUSED(steps), void *NPY_UNUSED(func)) +{ + NPY_ALLOW_C_API_DEF + NPY_ALLOW_C_API; + PyErr_SetString(PyExc_RuntimeError, "How unexpected :)!"); + NPY_DISABLE_C_API; + return; +} + + char *inner1d_signature = "(i),(i)->()"; /**begin repeat @@ -71,7 +85,7 @@ char *inner1d_signature = "(i),(i)->()"; */ static void -@TYPE@_inner1d(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_inner1d(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { INIT_OUTER_LOOP_3 npy_intp di = dimensions[0]; @@ -106,7 +120,7 @@ char *innerwt_signature = "(i),(i),(i)->()"; */ static void -@TYPE@_innerwt(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_innerwt(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { INIT_OUTER_LOOP_4 npy_intp di = dimensions[0]; @@ -128,6 +142,8 @@ static void /**end repeat**/ char *matrix_multiply_signature = "(m,n),(n,p)->(m,p)"; +/* for use with matrix_multiply code, but different signature */ +char *matmul_signature = "(m?,n),(n,p?)->(m?,p?)"; /**begin repeat @@ -141,7 +157,7 @@ char *matrix_multiply_signature = "(m,n),(n,p)->(m,p)"; */ static void -@TYPE@_matrix_multiply(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_matrix_multiply(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { /* no BLAS is available */ INIT_OUTER_LOOP_3 @@ -195,6 +211,45 @@ static void /**end repeat**/ +char *cross1d_signature = "(3),(3)->(3)"; + +/**begin repeat + + #TYPE=LONG,DOUBLE# + #typ=npy_long, npy_double# +*/ + +/* + * This implements the cross product: + * out[n, 0] = in1[n, 1]*in2[n, 2] - in1[n, 2]*in2[n, 1] + * out[n, 1] = in1[n, 2]*in2[n, 0] - in1[n, 0]*in2[n, 2] + * out[n, 2] = in1[n, 0]*in2[n, 1] - in1[n, 1]*in2[n, 0] + */ +static void +@TYPE@_cross1d(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) +{ + INIT_OUTER_LOOP_3 + npy_intp is1=steps[0], is2=steps[1], os = steps[2]; + BEGIN_OUTER_LOOP_3 + @typ@ i1_x = *(@typ@ *)(args[0] + 0*is1); + @typ@ i1_y = *(@typ@ *)(args[0] + 1*is1); + @typ@ i1_z = *(@typ@ *)(args[0] + 2*is1); + + @typ@ i2_x = *(@typ@ *)(args[1] + 0*is2); + @typ@ i2_y = *(@typ@ *)(args[1] + 1*is2); + @typ@ i2_z = *(@typ@ *)(args[1] + 2*is2); + char *op = args[2]; + + *(@typ@ *)op = i1_y * i2_z - i1_z * i2_y; + op += os; + *(@typ@ *)op = i1_z * i2_x - i1_x * i2_z; + op += os; + *(@typ@ *)op = i1_x * i2_y - i1_y * i2_x; + END_OUTER_LOOP +} + +/**end repeat**/ + char *euclidean_pdist_signature = "(n,d)->(p)"; /**begin repeat @@ -211,7 +266,7 @@ char *euclidean_pdist_signature = "(n,d)->(p)"; */ static void -@TYPE@_euclidean_pdist(char **args, npy_intp *dimensions, npy_intp *steps, +@TYPE@_euclidean_pdist(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { INIT_OUTER_LOOP_2 @@ -267,7 +322,7 @@ char *cumsum_signature = "(i)->(i)"; */ static void -@TYPE@_cumsum(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_cumsum(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { INIT_OUTER_LOOP_2 npy_intp di = dimensions[0]; @@ -285,17 +340,42 @@ static void /**end repeat**/ +/* The following lines were generated using a slightly modified + version of code_generators/generate_umath.py and adding these + lines to defdict: + +defdict = { +'inner1d' : + Ufunc(2, 1, None_, + r'''inner on the last dimension and broadcast on the rest \n" + " \"(i),(i)->()\" \n''', + TD('ld'), + ), +'innerwt' : + Ufunc(3, 1, None_, + r'''inner1d with a weight argument \n" + " \"(i),(i),(i)->()\" \n''', + TD('ld'), + ), +} + +*/ +static PyUFuncGenericFunction always_error_functions[] = { always_error_loop }; +static void *always_error_data[] = { (void *)NULL }; +static char always_error_signatures[] = { NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE }; static PyUFuncGenericFunction inner1d_functions[] = { LONG_inner1d, DOUBLE_inner1d }; -static void * inner1d_data[] = { (void *)NULL, (void *)NULL }; +static void *inner1d_data[] = { (void *)NULL, (void *)NULL }; static char inner1d_signatures[] = { NPY_LONG, NPY_LONG, NPY_LONG, NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE }; static PyUFuncGenericFunction innerwt_functions[] = { LONG_innerwt, DOUBLE_innerwt }; -static void * innerwt_data[] = { (void *)NULL, (void *)NULL }; +static void *innerwt_data[] = { (void *)NULL, (void *)NULL }; static char innerwt_signatures[] = { NPY_LONG, NPY_LONG, NPY_LONG, NPY_LONG, NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE }; static PyUFuncGenericFunction matrix_multiply_functions[] = { LONG_matrix_multiply, FLOAT_matrix_multiply, DOUBLE_matrix_multiply }; static void *matrix_multiply_data[] = { (void *)NULL, (void *)NULL, (void *)NULL }; static char matrix_multiply_signatures[] = { NPY_LONG, NPY_LONG, NPY_LONG, NPY_FLOAT, NPY_FLOAT, NPY_FLOAT, NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE }; - +static PyUFuncGenericFunction cross1d_functions[] = { LONG_cross1d, DOUBLE_cross1d }; +static void *cross1d_data[] = { (void *)NULL, (void *)NULL }; +static char cross1d_signatures[] = { NPY_LONG, NPY_LONG, NPY_LONG, NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE }; static PyUFuncGenericFunction euclidean_pdist_functions[] = { FLOAT_euclidean_pdist, DOUBLE_euclidean_pdist }; static void *eucldiean_pdist_data[] = { (void *)NULL, (void *)NULL }; @@ -303,7 +383,7 @@ static char euclidean_pdist_signatures[] = { NPY_FLOAT, NPY_FLOAT, NPY_DOUBLE, NPY_DOUBLE }; static PyUFuncGenericFunction cumsum_functions[] = { LONG_cumsum, DOUBLE_cumsum }; -static void * cumsum_data[] = { (void *)NULL, (void *)NULL }; +static void *cumsum_data[] = { (void *)NULL, (void *)NULL }; static char cumsum_signatures[] = { NPY_LONG, NPY_LONG, NPY_DOUBLE, NPY_DOUBLE }; @@ -311,6 +391,25 @@ static int addUfuncs(PyObject *dictionary) { PyObject *f; + f = PyUFunc_FromFuncAndData(always_error_functions, always_error_data, + always_error_signatures, 1, 2, 1, PyUFunc_None, "always_error", + "simply, broken, ufunc that sets an error (but releases the GIL).", + 0); + if (f == NULL) { + return -1; + } + PyDict_SetItemString(dictionary, "always_error", f); + Py_DECREF(f); + f = PyUFunc_FromFuncAndDataAndSignature(always_error_functions, + always_error_data, always_error_signatures, 1, 2, 1, PyUFunc_None, + "always_error_gufunc", + "simply, broken, gufunc that sets an error (but releases the GIL).", + 0, "(i),()->()"); + if (f == NULL) { + return -1; + } + PyDict_SetItemString(dictionary, "always_error_gufunc", f); + Py_DECREF(f); f = PyUFunc_FromFuncAndDataAndSignature(inner1d_functions, inner1d_data, inner1d_signatures, 2, 2, 1, PyUFunc_None, "inner1d", "inner on the last dimension and broadcast on the rest \n" @@ -346,6 +445,17 @@ addUfuncs(PyObject *dictionary) { } PyDict_SetItemString(dictionary, "matrix_multiply", f); Py_DECREF(f); + f = PyUFunc_FromFuncAndDataAndSignature(matrix_multiply_functions, + matrix_multiply_data, matrix_multiply_signatures, + 3, 2, 1, PyUFunc_None, "matmul", + "matmul on last two dimensions, with some being optional\n" + " \"(m?,n),(n,p?)->(m?,p?)\" \n", + 0, matmul_signature); + if (f == NULL) { + return -1; + } + PyDict_SetItemString(dictionary, "matmul", f); + Py_DECREF(f); f = PyUFunc_FromFuncAndDataAndSignature(euclidean_pdist_functions, eucldiean_pdist_data, euclidean_pdist_signatures, 2, 1, 1, PyUFunc_None, "euclidean_pdist", @@ -376,6 +486,25 @@ addUfuncs(PyObject *dictionary) { } PyDict_SetItemString(dictionary, "inner1d_no_doc", f); Py_DECREF(f); + f = PyUFunc_FromFuncAndDataAndSignature(cross1d_functions, cross1d_data, + cross1d_signatures, 2, 2, 1, PyUFunc_None, "cross1d", + "cross product on the last dimension and broadcast on the rest \n"\ + " \"(3),(3)->(3)\" \n", + 0, cross1d_signature); + if (f == NULL) { + return -1; + } + PyDict_SetItemString(dictionary, "cross1d", f); + Py_DECREF(f); + + f = PyUFunc_FromFuncAndDataAndSignature(NULL, NULL, + NULL, 0, 0, 0, PyUFunc_None, "_pickleable_module_global.ufunc", + "A dotted name for pickle testing, does nothing.", 0, NULL); + if (f == NULL) { + return -1; + } + PyDict_SetItemString(dictionary, "_pickleable_module_global_ufunc", f); + Py_DECREF(f); return 0; } @@ -385,9 +514,10 @@ static PyObject * UMath_Tests_test_signature(PyObject *NPY_UNUSED(dummy), PyObject *args) { int nin, nout, i; - PyObject *signature, *sig_str; - PyUFuncObject *f = NULL; - PyObject *core_num_dims = NULL, *core_dim_ixs = NULL; + PyObject *signature=NULL, *sig_str=NULL; + PyUFuncObject *f=NULL; + PyObject *core_num_dims=NULL, *core_dim_ixs=NULL; + PyObject *core_dim_flags=NULL, *core_dim_sizes=NULL; int core_enabled; int core_num_ixs = 0; @@ -395,7 +525,7 @@ UMath_Tests_test_signature(PyObject *NPY_UNUSED(dummy), PyObject *args) return NULL; } - if (PyString_Check(signature)) { + if (PyBytes_Check(signature)) { sig_str = signature; } else if (PyUnicode_Check(signature)) { sig_str = PyUnicode_AsUTF8String(signature); @@ -408,7 +538,7 @@ UMath_Tests_test_signature(PyObject *NPY_UNUSED(dummy), PyObject *args) NULL, NULL, NULL, 0, nin, nout, PyUFunc_None, "no name", "doc:none", - 1, PyString_AS_STRING(sig_str)); + 1, PyBytes_AS_STRING(sig_str)); if (sig_str != signature) { Py_DECREF(sig_str); } @@ -438,11 +568,11 @@ UMath_Tests_test_signature(PyObject *NPY_UNUSED(dummy), PyObject *args) } if (f->core_dim_ixs != NULL) { core_dim_ixs = PyTuple_New(core_num_ixs); - if (core_num_dims == NULL) { + if (core_dim_ixs == NULL) { goto fail; } for (i = 0; i < core_num_ixs; i++) { - PyObject * val = PyLong_FromLong(f->core_dim_ixs[i]); + PyObject *val = PyLong_FromLong(f->core_dim_ixs[i]); PyTuple_SET_ITEM(core_dim_ixs, i, val); } } @@ -450,13 +580,91 @@ UMath_Tests_test_signature(PyObject *NPY_UNUSED(dummy), PyObject *args) Py_INCREF(Py_None); core_dim_ixs = Py_None; } + if (f->core_dim_flags != NULL) { + core_dim_flags = PyTuple_New(f->core_num_dim_ix); + if (core_dim_flags == NULL) { + goto fail; + } + for (i = 0; i < f->core_num_dim_ix; i++) { + PyObject *val = PyLong_FromLong(f->core_dim_flags[i]); + PyTuple_SET_ITEM(core_dim_flags, i, val); + } + } + else { + Py_INCREF(Py_None); + core_dim_flags = Py_None; + } + if (f->core_dim_sizes != NULL) { + core_dim_sizes = PyTuple_New(f->core_num_dim_ix); + if (core_dim_sizes == NULL) { + goto fail; + } + for (i = 0; i < f->core_num_dim_ix; i++) { + PyObject *val = PyLong_FromLong(f->core_dim_sizes[i]); + PyTuple_SET_ITEM(core_dim_sizes, i, val); + } + } + else { + Py_INCREF(Py_None); + core_dim_sizes = Py_None; + } Py_DECREF(f); - return Py_BuildValue("iOO", core_enabled, core_num_dims, core_dim_ixs); + return Py_BuildValue("iNNNN", core_enabled, core_num_dims, + core_dim_ixs, core_dim_flags, core_dim_sizes); fail: Py_XDECREF(f); Py_XDECREF(core_num_dims); Py_XDECREF(core_dim_ixs); + Py_XDECREF(core_dim_flags); + Py_XDECREF(core_dim_sizes); + return NULL; +} + +// Testing the utilities of the CPU dispatcher +#ifndef NPY_DISABLE_OPTIMIZATION + #include "_umath_tests.dispatch.h" +#endif +NPY_CPU_DISPATCH_DECLARE(extern const char *_umath_tests_dispatch_var) +NPY_CPU_DISPATCH_DECLARE(const char *_umath_tests_dispatch_func, (void)) +NPY_CPU_DISPATCH_DECLARE(void _umath_tests_dispatch_attach, (PyObject *list)) + +static PyObject * +UMath_Tests_test_dispatch(PyObject *NPY_UNUSED(dummy), PyObject *NPY_UNUSED(dummy2)) +{ + const char *highest_func, *highest_var; + NPY_CPU_DISPATCH_CALL(highest_func = _umath_tests_dispatch_func, ()); + NPY_CPU_DISPATCH_CALL(highest_var = _umath_tests_dispatch_var); + const char *highest_func_xb = "nobase", *highest_var_xb = "nobase"; + NPY_CPU_DISPATCH_CALL_XB(highest_func_xb = _umath_tests_dispatch_func, ()); + NPY_CPU_DISPATCH_CALL_XB(highest_var_xb = _umath_tests_dispatch_var); + + PyObject *dict = PyDict_New(), *item; + if (dict == NULL) { + return NULL; + } + /**begin repeat + * #str = func, var, func_xb, var_xb# + */ + item = PyUnicode_FromString(highest_@str@); + if (item == NULL || PyDict_SetItemString(dict, "@str@", item) < 0) { + goto err; + } + Py_DECREF(item); + /**end repeat**/ + item = PyList_New(0); + if (item == NULL || PyDict_SetItemString(dict, "all", item) < 0) { + goto err; + } + NPY_CPU_DISPATCH_CALL_ALL(_umath_tests_dispatch_attach, (item)); + Py_SETREF(item, NULL); + if (PyErr_Occurred()) { + goto err; + } + return dict; +err: + Py_XDECREF(item); + Py_DECREF(dict); return NULL; } @@ -464,13 +672,13 @@ static PyMethodDef UMath_TestsMethods[] = { {"test_signature", UMath_Tests_test_signature, METH_VARARGS, "Test signature parsing of ufunc. \n" "Arguments: nin nout signature \n" - "If fails, it returns NULL. Otherwise it will returns 0 for scalar ufunc " - "and 1 for generalized ufunc. \n", + "If fails, it returns NULL. Otherwise it returns a tuple of ufunc " + "internals. \n", }, + {"test_dispatch", UMath_Tests_test_dispatch, METH_NOARGS, NULL}, {NULL, NULL, 0, NULL} /* Sentinel */ }; -#if defined(NPY_PY3K) static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "_umath_tests", @@ -482,34 +690,35 @@ static struct PyModuleDef moduledef = { NULL, NULL }; -#endif /* Initialization function for the module */ -#if defined(NPY_PY3K) -#define RETVAL(x) x PyMODINIT_FUNC PyInit__umath_tests(void) { -#else -#define RETVAL(x) -PyMODINIT_FUNC init_umath_tests(void) { -#endif PyObject *m; PyObject *d; PyObject *version; -#if defined(NPY_PY3K) + // Initialize CPU features + if (npy_cpu_init() < 0) { + return NULL; + } + m = PyModule_Create(&moduledef); -#else - m = Py_InitModule("_umath_tests", UMath_TestsMethods); -#endif if (m == NULL) { - return RETVAL(NULL); + return NULL; } + import_array(); + if (PyErr_Occurred()) { + return NULL; + } import_ufunc(); + if (PyErr_Occurred()) { + return NULL; + } d = PyModule_GetDict(m); - version = PyString_FromString("0.1"); + version = PyUnicode_FromString("0.1"); PyDict_SetItemString(d, "__version__", version); Py_DECREF(version); @@ -519,8 +728,7 @@ PyMODINIT_FUNC init_umath_tests(void) { PyErr_Print(); PyErr_SetString(PyExc_RuntimeError, "cannot load _umath_tests module."); - return RETVAL(NULL); + return NULL; } - - return RETVAL(m); + return m; } diff --git a/numpy/core/src/umath/_umath_tests.dispatch.c b/numpy/core/src/umath/_umath_tests.dispatch.c new file mode 100644 index 000000000000..9d8df4c86d36 --- /dev/null +++ b/numpy/core/src/umath/_umath_tests.dispatch.c @@ -0,0 +1,36 @@ +/** + * Testing the utilities of the CPU dispatcher + * + * @targets $werror baseline + * SSE2 SSE41 AVX2 + * VSX VSX2 VSX3 + * NEON ASIMD ASIMDHP + */ +#define PY_SSIZE_T_CLEAN +#include <Python.h> + +#include "npy_cpu_dispatch.h" + +#ifndef NPY_DISABLE_OPTIMIZATION + #include "_umath_tests.dispatch.h" +#endif + +NPY_CPU_DISPATCH_DECLARE(const char *_umath_tests_dispatch_func, (void)) +NPY_CPU_DISPATCH_DECLARE(extern const char *_umath_tests_dispatch_var) +NPY_CPU_DISPATCH_DECLARE(void _umath_tests_dispatch_attach, (PyObject *list)) + +const char *NPY_CPU_DISPATCH_CURFX(_umath_tests_dispatch_var) = NPY_TOSTRING(NPY_CPU_DISPATCH_CURFX(var)); +const char *NPY_CPU_DISPATCH_CURFX(_umath_tests_dispatch_func)(void) +{ + static const char *current = NPY_TOSTRING(NPY_CPU_DISPATCH_CURFX(func)); + return current; +} + +void NPY_CPU_DISPATCH_CURFX(_umath_tests_dispatch_attach)(PyObject *list) +{ + PyObject *item = PyUnicode_FromString(NPY_TOSTRING(NPY_CPU_DISPATCH_CURFX(func))); + if (item) { + PyList_Append(list, item); + Py_DECREF(item); + } +} diff --git a/numpy/core/src/umath/clip.cpp b/numpy/core/src/umath/clip.cpp new file mode 100644 index 000000000000..19d05c848d9c --- /dev/null +++ b/numpy/core/src/umath/clip.cpp @@ -0,0 +1,282 @@ +/** + * This module provides the inner loops for the clip ufunc + */ +#define _UMATHMODULE +#define _MULTIARRAYMODULE +#define NPY_NO_DEPRECATED_API NPY_API_VERSION + +#define PY_SSIZE_T_CLEAN +#include <Python.h> + +#include "numpy/halffloat.h" +#include "numpy/ndarraytypes.h" +#include "numpy/npy_common.h" +#include "numpy/npy_math.h" +#include "numpy/utils.h" + +#include "fast_loop_macros.h" + +#include "../common/numpy_tag.h" + +template <class T> +T +_NPY_MIN(T a, T b, npy::integral_tag const &) +{ + return PyArray_MIN(a, b); +} +template <class T> +T +_NPY_MAX(T a, T b, npy::integral_tag const &) +{ + return PyArray_MAX(a, b); +} + +npy_half +_NPY_MIN(npy_half a, npy_half b, npy::half_tag const &) +{ + return npy_half_isnan(a) || npy_half_le(a, b) ? (a) : (b); +} +npy_half +_NPY_MAX(npy_half a, npy_half b, npy::half_tag const &) +{ + return npy_half_isnan(a) || npy_half_ge(a, b) ? (a) : (b); +} + +template <class T> +T +_NPY_MIN(T a, T b, npy::floating_point_tag const &) +{ + return npy_isnan(a) ? (a) : PyArray_MIN(a, b); +} +template <class T> +T +_NPY_MAX(T a, T b, npy::floating_point_tag const &) +{ + return npy_isnan(a) ? (a) : PyArray_MAX(a, b); +} + +template <class T> +T +_NPY_MIN(T a, T b, npy::complex_tag const &) +{ + return npy_isnan((a).real) || npy_isnan((a).imag) || PyArray_CLT(a, b) + ? (a) + : (b); +} +template <class T> +T +_NPY_MAX(T a, T b, npy::complex_tag const &) +{ + return npy_isnan((a).real) || npy_isnan((a).imag) || PyArray_CGT(a, b) + ? (a) + : (b); +} + +template <class T> +T +_NPY_MIN(T a, T b, npy::date_tag const &) +{ + return (a) == NPY_DATETIME_NAT ? (a) + : (b) == NPY_DATETIME_NAT ? (b) + : (a) < (b) ? (a) + : (b); +} +template <class T> +T +_NPY_MAX(T a, T b, npy::date_tag const &) +{ + return (a) == NPY_DATETIME_NAT ? (a) + : (b) == NPY_DATETIME_NAT ? (b) + : (a) > (b) ? (a) + : (b); +} + +/* generic dispatcher */ +template <class Tag, class T = typename Tag::type> +T +_NPY_MIN(T const &a, T const &b) +{ + return _NPY_MIN(a, b, Tag{}); +} +template <class Tag, class T = typename Tag::type> +T +_NPY_MAX(T const &a, T const &b) +{ + return _NPY_MAX(a, b, Tag{}); +} + +template <class Tag, class T> +T +_NPY_CLIP(T x, T min, T max) +{ + return _NPY_MIN<Tag>(_NPY_MAX<Tag>((x), (min)), (max)); +} + +template <class Tag, class T = typename Tag::type> +static void +_npy_clip_(T **args, npy_intp const *dimensions, npy_intp const *steps) +{ + npy_intp n = dimensions[0]; + if (steps[1] == 0 && steps[2] == 0) { + /* min and max are constant throughout the loop, the most common case + */ + /* NOTE: it may be possible to optimize these checks for nan */ + T min_val = *args[1]; + T max_val = *args[2]; + + T *ip1 = args[0], *op1 = args[3]; + npy_intp is1 = steps[0] / sizeof(T), os1 = steps[3] / sizeof(T); + + /* contiguous, branch to let the compiler optimize */ + if (is1 == 1 && os1 == 1) { + for (npy_intp i = 0; i < n; i++, ip1++, op1++) { + *op1 = _NPY_CLIP<Tag>(*ip1, min_val, max_val); + } + } + else { + for (npy_intp i = 0; i < n; i++, ip1 += is1, op1 += os1) { + *op1 = _NPY_CLIP<Tag>(*ip1, min_val, max_val); + } + } + } + else { + T *ip1 = args[0], *ip2 = args[1], *ip3 = args[2], *op1 = args[3]; + npy_intp is1 = steps[0] / sizeof(T), is2 = steps[1] / sizeof(T), + is3 = steps[2] / sizeof(T), os1 = steps[3] / sizeof(T); + for (npy_intp i = 0; i < n; + i++, ip1 += is1, ip2 += is2, ip3 += is3, op1 += os1) + *op1 = _NPY_CLIP<Tag>(*ip1, *ip2, *ip3); + } + npy_clear_floatstatus_barrier((char *)dimensions); +} + +template <class Tag> +static void +_npy_clip(char **args, npy_intp const *dimensions, npy_intp const *steps) +{ + using T = typename Tag::type; + return _npy_clip_<Tag>((T **)args, dimensions, steps); +} + +extern "C" { +NPY_NO_EXPORT void +BOOL_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)) +{ + return _npy_clip<npy::bool_tag>(args, dimensions, steps); +} +NPY_NO_EXPORT void +BYTE_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)) +{ + return _npy_clip<npy::byte_tag>(args, dimensions, steps); +} +NPY_NO_EXPORT void +UBYTE_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)) +{ + return _npy_clip<npy::ubyte_tag>(args, dimensions, steps); +} +NPY_NO_EXPORT void +SHORT_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)) +{ + return _npy_clip<npy::short_tag>(args, dimensions, steps); +} +NPY_NO_EXPORT void +USHORT_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)) +{ + return _npy_clip<npy::ushort_tag>(args, dimensions, steps); +} +NPY_NO_EXPORT void +INT_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)) +{ + return _npy_clip<npy::int_tag>(args, dimensions, steps); +} +NPY_NO_EXPORT void +UINT_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)) +{ + return _npy_clip<npy::uint_tag>(args, dimensions, steps); +} +NPY_NO_EXPORT void +LONG_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)) +{ + return _npy_clip<npy::long_tag>(args, dimensions, steps); +} +NPY_NO_EXPORT void +ULONG_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)) +{ + return _npy_clip<npy::ulong_tag>(args, dimensions, steps); +} +NPY_NO_EXPORT void +LONGLONG_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)) +{ + return _npy_clip<npy::longlong_tag>(args, dimensions, steps); +} +NPY_NO_EXPORT void +ULONGLONG_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)) +{ + return _npy_clip<npy::ulonglong_tag>(args, dimensions, steps); +} +NPY_NO_EXPORT void +HALF_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)) +{ + return _npy_clip<npy::half_tag>(args, dimensions, steps); +} +NPY_NO_EXPORT void +FLOAT_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)) +{ + return _npy_clip<npy::float_tag>(args, dimensions, steps); +} +NPY_NO_EXPORT void +DOUBLE_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)) +{ + return _npy_clip<npy::double_tag>(args, dimensions, steps); +} +NPY_NO_EXPORT void +LONGDOUBLE_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)) +{ + return _npy_clip<npy::longdouble_tag>(args, dimensions, steps); +} +NPY_NO_EXPORT void +CFLOAT_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)) +{ + return _npy_clip<npy::cfloat_tag>(args, dimensions, steps); +} +NPY_NO_EXPORT void +CDOUBLE_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)) +{ + return _npy_clip<npy::cdouble_tag>(args, dimensions, steps); +} +NPY_NO_EXPORT void +CLONGDOUBLE_clip(char **args, npy_intp const *dimensions, + npy_intp const *steps, void *NPY_UNUSED(func)) +{ + return _npy_clip<npy::clongdouble_tag>(args, dimensions, steps); +} +NPY_NO_EXPORT void +DATETIME_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)) +{ + return _npy_clip<npy::datetime_tag>(args, dimensions, steps); +} +NPY_NO_EXPORT void +TIMEDELTA_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)) +{ + return _npy_clip<npy::timedelta_tag>(args, dimensions, steps); +} +} diff --git a/numpy/core/src/umath/clip.h b/numpy/core/src/umath/clip.h new file mode 100644 index 000000000000..f69ebd1e310a --- /dev/null +++ b/numpy/core/src/umath/clip.h @@ -0,0 +1,73 @@ +#ifndef _NPY_UMATH_CLIP_H_ +#define _NPY_UMATH_CLIP_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +NPY_NO_EXPORT void +BOOL_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)); +NPY_NO_EXPORT void +BYTE_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)); +NPY_NO_EXPORT void +UBYTE_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)); +NPY_NO_EXPORT void +SHORT_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)); +NPY_NO_EXPORT void +USHORT_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)); +NPY_NO_EXPORT void +INT_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)); +NPY_NO_EXPORT void +UINT_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)); +NPY_NO_EXPORT void +LONG_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)); +NPY_NO_EXPORT void +ULONG_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)); +NPY_NO_EXPORT void +LONGLONG_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)); +NPY_NO_EXPORT void +ULONGLONG_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)); +NPY_NO_EXPORT void +HALF_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)); +NPY_NO_EXPORT void +FLOAT_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)); +NPY_NO_EXPORT void +DOUBLE_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)); +NPY_NO_EXPORT void +LONGDOUBLE_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)); +NPY_NO_EXPORT void +CFLOAT_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)); +NPY_NO_EXPORT void +CDOUBLE_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)); +NPY_NO_EXPORT void +CLONGDOUBLE_clip(char **args, npy_intp const *dimensions, + npy_intp const *steps, void *NPY_UNUSED(func)); +NPY_NO_EXPORT void +DATETIME_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)); +NPY_NO_EXPORT void +TIMEDELTA_clip(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/numpy/core/src/umath/cpuid.c b/numpy/core/src/umath/cpuid.c deleted file mode 100644 index 912d51eeb9aa..000000000000 --- a/numpy/core/src/umath/cpuid.c +++ /dev/null @@ -1,56 +0,0 @@ -#define _UMATHMODULE -#define NPY_NO_DEPRECATED_API NPY_API_VERSION - -#include <Python.h> - -#include "npy_config.h" - -#define PY_ARRAY_UNIQUE_SYMBOL _npy_umathmodule_ARRAY_API -#define NO_IMPORT_ARRAY - -#include "cpuid.h" - -#define XCR_XFEATURE_ENABLED_MASK 0x0 -#define XSTATE_SSE 0x2 -#define XSTATE_YMM 0x4 - -/* - * verify the OS supports avx instructions - * it can be disabled in some OS, e.g. with the nosavex boot option of linux - */ -static NPY_INLINE -int os_avx_support(void) -{ -#if HAVE_XGETBV - /* - * use bytes for xgetbv to avoid issues with compiler not knowing the - * instruction - */ - unsigned int eax, edx; - unsigned int ecx = XCR_XFEATURE_ENABLED_MASK; - __asm__("xgetbv" : "=a" (eax), "=d" (edx) : "c" (ecx)); - return (eax & (XSTATE_SSE | XSTATE_YMM)) == (XSTATE_SSE | XSTATE_YMM); -#else - return 0; -#endif -} - - -/* - * Primitive cpu feature detect function - * Currently only supports checking for avx on gcc compatible compilers. - */ -NPY_NO_EXPORT int -npy_cpu_supports(const char * feature) -{ -#ifdef HAVE___BUILTIN_CPU_SUPPORTS - if (strcmp(feature, "avx2") == 0) { - return __builtin_cpu_supports("avx2") && os_avx_support(); - } - else if (strcmp(feature, "avx") == 0) { - return __builtin_cpu_supports("avx") && os_avx_support(); - } -#endif - - return 0; -} diff --git a/numpy/core/src/umath/cpuid.h b/numpy/core/src/umath/cpuid.h deleted file mode 100644 index 33702ed41f48..000000000000 --- a/numpy/core/src/umath/cpuid.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef _NPY_PRIVATE__CPUID_H_ -#define _NPY_PRIVATE__CPUID_H_ - -#include <numpy/ndarraytypes.h> /* for NPY_NO_EXPORT */ - -NPY_NO_EXPORT int -npy_cpu_supports(const char * feature); - -#endif diff --git a/numpy/core/src/umath/dispatching.c b/numpy/core/src/umath/dispatching.c new file mode 100644 index 000000000000..81d47a0e1520 --- /dev/null +++ b/numpy/core/src/umath/dispatching.c @@ -0,0 +1,1091 @@ +/* + * This file implements universal function dispatching and promotion (which + * is necessary to happen before dispatching). + * This is part of the UFunc object. Promotion and dispatching uses the + * following things: + * + * - operand_DTypes: The datatypes as passed in by the user. + * - signature: The DTypes fixed by the user with `dtype=` or `signature=`. + * - ufunc._loops: A list of all ArrayMethods and promoters, it contains + * tuples `(dtypes, ArrayMethod)` or `(dtypes, promoter)`. + * - ufunc._dispatch_cache: A cache to store previous promotion and/or + * dispatching results. + * - The actual arrays are used to support the old code paths where necessary. + * (this includes any value-based casting/promotion logic) + * + * In general, `operand_Dtypes` is always overridden by `signature`. If a + * DType is included in the `signature` it must match precisely. + * + * The process of dispatching and promotion can be summarized in the following + * steps: + * + * 1. Override any `operand_DTypes` from `signature`. + * 2. Check if the new `operand_Dtypes` is cached (if it is, got to 4.) + * 3. Find the best matching "loop". This is done using multiple dispatching + * on all `operand_DTypes` and loop `dtypes`. A matching loop must be + * one whose DTypes are superclasses of the `operand_DTypes` (that are + * defined). The best matching loop must be better than any other matching + * loop. This result is cached. + * 4. If the found loop is a promoter: We call the promoter. It can modify + * the `operand_DTypes` currently. Then go back to step 2. + * (The promoter can call arbitrary code, so it could even add the matching + * loop first.) + * 5. The final `ArrayMethod` is found, its registered `dtypes` is copied + * into the `signature` so that it is available to the ufunc loop. + * + */ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE +#define _UMATHMODULE + +#define PY_SSIZE_T_CLEAN +#include <Python.h> + +#include "numpy/ndarraytypes.h" +#include "common.h" + +#include "dispatching.h" +#include "dtypemeta.h" +#include "common_dtype.h" +#include "npy_hashtable.h" +#include "legacy_array_method.h" +#include "ufunc_object.h" +#include "ufunc_type_resolution.h" + + +#define PROMOTION_DEBUG_TRACING 0 + + +/* forward declaration */ +static NPY_INLINE PyObject * +promote_and_get_info_and_ufuncimpl(PyUFuncObject *ufunc, + PyArrayObject *const ops[], + PyArray_DTypeMeta *signature[], + PyArray_DTypeMeta *op_dtypes[], + npy_bool allow_legacy_promotion); + + +/** + * Function to add a new loop to the ufunc. This mainly appends it to the + * list (as it currently is just a list). + * + * @param ufunc The universal function to add the loop to. + * @param info The tuple (dtype_tuple, ArrayMethod/promoter). + * @param ignore_duplicate If 1 and a loop with the same `dtype_tuple` is + * found, the function does nothing. + */ +NPY_NO_EXPORT int +PyUFunc_AddLoop(PyUFuncObject *ufunc, PyObject *info, int ignore_duplicate) +{ + /* + * Validate the info object, this should likely move to to a different + * entry-point in the future (and is mostly unnecessary currently). + */ + if (!PyTuple_CheckExact(info) || PyTuple_GET_SIZE(info) != 2) { + PyErr_SetString(PyExc_TypeError, + "Info must be a tuple: " + "(tuple of DTypes or None, ArrayMethod or promoter)"); + return -1; + } + PyObject *DType_tuple = PyTuple_GetItem(info, 0); + if (PyTuple_GET_SIZE(DType_tuple) != ufunc->nargs) { + PyErr_SetString(PyExc_TypeError, + "DType tuple length does not match ufunc number of operands"); + return -1; + } + for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(DType_tuple); i++) { + PyObject *item = PyTuple_GET_ITEM(DType_tuple, i); + if (item != Py_None + && !PyObject_TypeCheck(item, &PyArrayDTypeMeta_Type)) { + PyErr_SetString(PyExc_TypeError, + "DType tuple may only contain None and DType classes"); + return -1; + } + } + PyObject *meth_or_promoter = PyTuple_GET_ITEM(info, 1); + if (!PyObject_TypeCheck(meth_or_promoter, &PyArrayMethod_Type) + && !PyCapsule_IsValid(meth_or_promoter, "numpy._ufunc_promoter")) { + PyErr_SetString(PyExc_TypeError, + "Second argument to info must be an ArrayMethod or promoter"); + return -1; + } + + if (ufunc->_loops == NULL) { + ufunc->_loops = PyList_New(0); + if (ufunc->_loops == NULL) { + return -1; + } + } + + PyObject *loops = ufunc->_loops; + Py_ssize_t length = PyList_Size(loops); + for (Py_ssize_t i = 0; i < length; i++) { + PyObject *item = PyList_GetItem(loops, i); + PyObject *cur_DType_tuple = PyTuple_GetItem(item, 0); + int cmp = PyObject_RichCompareBool(cur_DType_tuple, DType_tuple, Py_EQ); + if (cmp < 0) { + return -1; + } + if (cmp == 0) { + continue; + } + if (ignore_duplicate) { + return 0; + } + PyErr_Format(PyExc_TypeError, + "A loop/promoter has already been registered with '%s' for %R", + ufunc_get_name_cstr(ufunc), DType_tuple); + return -1; + } + + if (PyList_Append(loops, info) < 0) { + return -1; + } + return 0; +} + + +/** + * Resolves the implementation to use, this uses typical multiple dispatching + * methods of finding the best matching implementation or resolver. + * (Based on `isinstance()`, the knowledge that non-abstract DTypes cannot + * be subclassed is used, however.) + * + * NOTE: This currently does not take into account output dtypes which do not + * have to match. The possible extension here is that if an output + * is given (and thus an output dtype), but not part of the signature + * we could ignore it for matching, but *prefer* a loop that matches + * better. + * Why is this not done currently? First, it seems a niche feature that + * loops can only be distinguished based on the output dtype. Second, + * there are some nasty theoretical things because: + * + * np.add(f4, f4, out=f8) + * np.add(f4, f4, out=f8, dtype=f8) + * + * are different, the first uses the f4 loop, the second the f8 loop. + * The problem is, that the current cache only uses the op_dtypes and + * both are `(f4, f4, f8)`. The cache would need to store also which + * output was provided by `dtype=`/`signature=`. + * + * @param ufunc + * @param op_dtypes The DTypes that are either passed in (defined by an + * operand) or defined by the `signature` as also passed in as + * `fixed_DTypes`. + * @param out_info Returns the tuple describing the best implementation + * (consisting of dtypes and ArrayMethod or promoter). + * WARNING: Returns a borrowed reference! + * @returns -1 on error 0 on success. Note that the output can be NULL on + * success if nothing is found. + */ +static int +resolve_implementation_info(PyUFuncObject *ufunc, + PyArray_DTypeMeta *op_dtypes[], npy_bool only_promoters, + PyObject **out_info) +{ + int nin = ufunc->nin, nargs = ufunc->nargs; + Py_ssize_t size = PySequence_Length(ufunc->_loops); + PyObject *best_dtypes = NULL; + PyObject *best_resolver_info = NULL; + +#if PROMOTION_DEBUG_TRACING + printf("Promoting for '%s' promoters only: %d\n", + ufunc->name ? ufunc->name : "<unknown>", (int)only_promoters); + printf(" DTypes: "); + PyObject *tmp = PyArray_TupleFromItems(ufunc->nargs, op_dtypes, 1); + PyObject_Print(tmp, stdout, 0); + Py_DECREF(tmp); + printf("\n"); + Py_DECREF(tmp); +#endif + + for (Py_ssize_t res_idx = 0; res_idx < size; res_idx++) { + /* Test all resolvers */ + PyObject *resolver_info = PySequence_Fast_GET_ITEM( + ufunc->_loops, res_idx); + + if (only_promoters && PyObject_TypeCheck( + PyTuple_GET_ITEM(resolver_info, 1), &PyArrayMethod_Type)) { + continue; + } + + PyObject *curr_dtypes = PyTuple_GET_ITEM(resolver_info, 0); + /* + * Test if the current resolver matches, it could make sense to + * reorder these checks to avoid the IsSubclass check as much as + * possible. + */ + + npy_bool matches = NPY_TRUE; + /* + * NOTE: We currently match the output dtype exactly here, this is + * actually only necessary if the signature includes. + * Currently, we rely that op-dtypes[nin:nout] is NULLed if not. + */ + for (Py_ssize_t i = 0; i < nargs; i++) { + PyArray_DTypeMeta *given_dtype = op_dtypes[i]; + PyArray_DTypeMeta *resolver_dtype = ( + (PyArray_DTypeMeta *)PyTuple_GET_ITEM(curr_dtypes, i)); + assert((PyObject *)given_dtype != Py_None); + if (given_dtype == NULL) { + if (i >= nin) { + /* Unspecified out always matches (see below for inputs) */ + continue; + } + /* + * This is a reduce-like operation, which always have the form + * `(res_DType, op_DType, res_DType)`. If the first and last + * dtype of the loops match, this should be reduce-compatible. + */ + if (PyTuple_GET_ITEM(curr_dtypes, 0) + == PyTuple_GET_ITEM(curr_dtypes, 2)) { + continue; + } + } + + if (resolver_dtype == (PyArray_DTypeMeta *)Py_None) { + /* always matches */ + continue; + } + if (given_dtype == resolver_dtype) { + continue; + } + if (!NPY_DT_is_abstract(resolver_dtype)) { + matches = NPY_FALSE; + break; + } + + int subclass = PyObject_IsSubclass( + (PyObject *)given_dtype, (PyObject *)resolver_dtype); + if (subclass < 0) { + return -1; + } + if (!subclass) { + matches = NPY_FALSE; + break; + } + /* + * TODO: Could consider allowing reverse subclass relation, i.e. + * the operation DType passed in to be abstract. That + * definitely is OK for outputs (and potentially useful, + * you could enforce e.g. an inexact result). + * It might also be useful for some stranger promoters. + */ + } + if (!matches) { + continue; + } + + /* The resolver matches, but we have to check if it is better */ + if (best_dtypes != NULL) { + int current_best = -1; /* -1 neither, 0 current best, 1 new */ + /* + * If both have concrete and None in the same position and + * they are identical, we will continue searching using the + * first best for comparison, in an attempt to find a better + * one. + * In all cases, we give up resolution, since it would be + * necessary to compare to two "best" cases. + */ + for (Py_ssize_t i = 0; i < nargs; i++) { + if (i == ufunc->nin && current_best != -1) { + /* inputs prefer one loop and outputs have lower priority */ + break; + } + + int best; + + PyObject *prev_dtype = PyTuple_GET_ITEM(best_dtypes, i); + PyObject *new_dtype = PyTuple_GET_ITEM(curr_dtypes, i); + + if (prev_dtype == new_dtype) { + /* equivalent, so this entry does not matter */ + continue; + } + if (op_dtypes[i] == NULL) { + /* + * If an a dtype is NULL it always matches, so there is no + * point in defining one as more precise than the other. + */ + continue; + } + /* If either is None, the other is strictly more specific */ + if (prev_dtype == Py_None) { + best = 1; + } + else if (new_dtype == Py_None) { + best = 0; + } + /* + * If both are concrete and not identical, this is + * ambiguous. + */ + else if (!NPY_DT_is_abstract((PyArray_DTypeMeta *)prev_dtype) && + !NPY_DT_is_abstract((PyArray_DTypeMeta *)new_dtype)) { + /* + * Ambiguous unless they are identical (checked above), + * or one matches exactly. + */ + if (prev_dtype == (PyObject *)op_dtypes[i]) { + best = 0; + } + else if (new_dtype == (PyObject *)op_dtypes[i]) { + best = 1; + } + else { + best = -1; + } + } + else if (!NPY_DT_is_abstract((PyArray_DTypeMeta *)prev_dtype)) { + /* old is not abstract, so better (both not possible) */ + best = 0; + } + else if (!NPY_DT_is_abstract((PyArray_DTypeMeta *)new_dtype)) { + /* new is not abstract, so better (both not possible) */ + best = 1; + } + /* + * TODO: This will need logic for abstract DTypes to decide if + * one is a subclass of the other (And their subclass + * relation is well defined). For now, we bail out + * in cas someone manages to get here. + */ + else { + PyErr_SetString(PyExc_NotImplementedError, + "deciding which one of two abstract dtypes is " + "a better match is not yet implemented. This " + "will pick the better (or bail) in the future."); + *out_info = NULL; + return -1; + } + + if (best == -1) { + /* no new info, nothing to update */ + continue; + } + if ((current_best != -1) && (current_best != best)) { + /* + * We need a clear best, this could be tricky, unless + * the signature is identical, we would have to compare + * against both of the found ones until we find a + * better one. + * Instead, only support the case where they are + * identical. + */ + /* TODO: Document the above comment, may need relaxing? */ + current_best = -1; + break; + } + current_best = best; + } + + if (current_best == -1) { + /* + * We could not find a best loop, but promoters should be + * designed in a way to disambiguate such scenarios, so we + * retry the whole lookup using only promoters. + * (There is a small chance we already got two promoters. + * We just redo it anyway for simplicity.) + */ + if (!only_promoters) { + return resolve_implementation_info(ufunc, + op_dtypes, NPY_TRUE, out_info); + } + /* + * If this is already the retry, we are out of luck. Promoters + * should be designed in a way that this cannot happen! + * (It should be noted, that the retry might not find anything + * and we still do a legacy lookup later.) + */ + PyObject *given = PyArray_TupleFromItems( + ufunc->nargs, (PyObject **)op_dtypes, 1); + if (given != NULL) { + PyErr_Format(PyExc_RuntimeError, + "Could not find a loop for the inputs:\n %S\n" + "The two promoters %S and %S matched the input " + "equally well. Promoters must be designed " + "to be unambiguous. NOTE: This indicates an error " + "in NumPy or an extending library and should be " + "reported.", + given, best_dtypes, curr_dtypes); + Py_DECREF(given); + } + *out_info = NULL; + return 0; + } + else if (current_best == 0) { + /* The new match is not better, continue looking. */ + continue; + } + } + /* The new match is better (or there was no previous match) */ + best_dtypes = curr_dtypes; + best_resolver_info = resolver_info; + } + if (best_dtypes == NULL) { + /* The non-legacy lookup failed */ + *out_info = NULL; + return 0; + } + + *out_info = best_resolver_info; + return 0; +} + + +/* + * A promoter can currently be either a C-Capsule containing a promoter + * function pointer, or a Python function. Both of these can at this time + * only return new operation DTypes (i.e. mutate the input while leaving + * those defined by the `signature` unmodified). + */ +static PyObject * +call_promoter_and_recurse(PyUFuncObject *ufunc, PyObject *promoter, + PyArray_DTypeMeta *op_dtypes[], PyArray_DTypeMeta *signature[], + PyArrayObject *const operands[]) +{ + int nargs = ufunc->nargs; + PyObject *resolved_info = NULL; + + int promoter_result; + PyArray_DTypeMeta *new_op_dtypes[NPY_MAXARGS]; + + if (PyCapsule_CheckExact(promoter)) { + /* We could also go the other way and wrap up the python function... */ + promoter_function *promoter_function = PyCapsule_GetPointer(promoter, + "numpy._ufunc_promoter"); + if (promoter_function == NULL) { + return NULL; + } + promoter_result = promoter_function(ufunc, + op_dtypes, signature, new_op_dtypes); + } + else { + PyErr_SetString(PyExc_NotImplementedError, + "Calling python functions for promotion is not implemented."); + return NULL; + } + if (promoter_result < 0) { + return NULL; + } + /* + * If none of the dtypes changes, we would recurse infinitely, abort. + * (Of course it is nevertheless possible to recurse infinitely.) + */ + int dtypes_changed = 0; + for (int i = 0; i < nargs; i++) { + if (new_op_dtypes[i] != op_dtypes[i]) { + dtypes_changed = 1; + break; + } + } + if (!dtypes_changed) { + goto finish; + } + + /* + * Do a recursive call, the promotion function has to ensure that the + * new tuple is strictly more precise (thus guaranteeing eventual finishing) + */ + if (Py_EnterRecursiveCall(" during ufunc promotion.") != 0) { + goto finish; + } + resolved_info = promote_and_get_info_and_ufuncimpl(ufunc, + operands, signature, new_op_dtypes, + /* no legacy promotion */ NPY_FALSE); + + Py_LeaveRecursiveCall(); + + finish: + for (int i = 0; i < nargs; i++) { + Py_XDECREF(new_op_dtypes[i]); + } + return resolved_info; +} + + +/* + * Convert the DType `signature` into the tuple of descriptors that is used + * by the old ufunc type resolvers in `ufunc_type_resolution.c`. + * + * Note that we do not need to pass the type tuple when we use the legacy path + * for type resolution rather than promotion, since the signature is always + * correct in that case. + */ +static int +_make_new_typetup( + int nop, PyArray_DTypeMeta *signature[], PyObject **out_typetup) { + *out_typetup = PyTuple_New(nop); + if (*out_typetup == NULL) { + return -1; + } + + int none_count = 0; + for (int i = 0; i < nop; i++) { + PyObject *item; + if (signature[i] == NULL) { + item = Py_None; + none_count++; + } + else { + if (!NPY_DT_is_legacy(signature[i]) + || NPY_DT_is_abstract(signature[i])) { + /* + * The legacy type resolution can't deal with these. + * This path will return `None` or so in the future to + * set an error later if the legacy type resolution is used. + */ + PyErr_SetString(PyExc_RuntimeError, + "Internal NumPy error: new DType in signature not yet " + "supported. (This should be unreachable code!)"); + Py_SETREF(*out_typetup, NULL); + return -1; + } + item = (PyObject *)signature[i]->singleton; + } + Py_INCREF(item); + PyTuple_SET_ITEM(*out_typetup, i, item); + } + if (none_count == nop) { + /* The whole signature was None, simply ignore type tuple */ + Py_DECREF(*out_typetup); + *out_typetup = NULL; + } + return 0; +} + + +/* + * Fills in the operation_DTypes with borrowed references. This may change + * the content, since it will use the legacy type resolution, which can special + * case 0-D arrays (using value-based logic). + */ +static int +legacy_promote_using_legacy_type_resolver(PyUFuncObject *ufunc, + PyArrayObject *const *ops, PyArray_DTypeMeta *signature[], + PyArray_DTypeMeta *operation_DTypes[], int *out_cacheable) +{ + int nargs = ufunc->nargs; + PyArray_Descr *out_descrs[NPY_MAXARGS] = {NULL}; + + PyObject *type_tuple = NULL; + if (_make_new_typetup(nargs, signature, &type_tuple) < 0) { + return -1; + } + + /* + * We use unsafe casting. This is of course not accurate, but that is OK + * here, because for promotion/dispatching the casting safety makes no + * difference. Whether the actual operands can be casts must be checked + * during the type resolution step (which may _also_ calls this!). + */ + if (ufunc->type_resolver(ufunc, + NPY_UNSAFE_CASTING, (PyArrayObject **)ops, type_tuple, + out_descrs) < 0) { + Py_XDECREF(type_tuple); + /* Not all legacy resolvers clean up on failures: */ + for (int i = 0; i < nargs; i++) { + Py_CLEAR(out_descrs[i]); + } + return -1; + } + Py_XDECREF(type_tuple); + + for (int i = 0; i < nargs; i++) { + Py_XSETREF(operation_DTypes[i], NPY_DTYPE(out_descrs[i])); + Py_INCREF(operation_DTypes[i]); + Py_DECREF(out_descrs[i]); + } + /* + * The PyUFunc_SimpleBinaryComparisonTypeResolver has a deprecation + * warning (ignoring `dtype=`) and cannot be cached. + * All datetime ones *should* have a warning, but currently don't, + * but ignore all signature passing also. So they can also + * not be cached, and they mutate the signature which of course is wrong, + * but not doing it would confuse the code later. + */ + for (int i = 0; i < nargs; i++) { + if (signature[i] != NULL && signature[i] != operation_DTypes[i]) { + Py_INCREF(operation_DTypes[i]); + Py_SETREF(signature[i], operation_DTypes[i]); + *out_cacheable = 0; + } + } + return 0; +} + + +/* + * Note, this function returns a BORROWED references to info since it adds + * it to the loops. + */ +NPY_NO_EXPORT PyObject * +add_and_return_legacy_wrapping_ufunc_loop(PyUFuncObject *ufunc, + PyArray_DTypeMeta *operation_dtypes[], int ignore_duplicate) +{ + PyObject *DType_tuple = PyArray_TupleFromItems(ufunc->nargs, + (PyObject **)operation_dtypes, 0); + if (DType_tuple == NULL) { + return NULL; + } + + PyArrayMethodObject *method = PyArray_NewLegacyWrappingArrayMethod( + ufunc, operation_dtypes); + if (method == NULL) { + Py_DECREF(DType_tuple); + return NULL; + } + PyObject *info = PyTuple_Pack(2, DType_tuple, method); + Py_DECREF(DType_tuple); + Py_DECREF(method); + if (info == NULL) { + return NULL; + } + if (PyUFunc_AddLoop(ufunc, info, ignore_duplicate) < 0) { + Py_DECREF(info); + return NULL; + } + Py_DECREF(info); /* now borrowed from the ufunc's list of loops */ + return info; +} + + +/* + * The main implementation to find the correct DType signature and ArrayMethod + * to use for a ufunc. This function may recurse with `do_legacy_fallback` + * set to False. + * + * If value-based promotion is necessary, this is handled ahead of time by + * `promote_and_get_ufuncimpl`. + */ +static NPY_INLINE PyObject * +promote_and_get_info_and_ufuncimpl(PyUFuncObject *ufunc, + PyArrayObject *const ops[], + PyArray_DTypeMeta *signature[], + PyArray_DTypeMeta *op_dtypes[], + npy_bool allow_legacy_promotion) +{ + /* + * Fetch the dispatching info which consists of the implementation and + * the DType signature tuple. There are three steps: + * + * 1. Check the cache. + * 2. Check all registered loops/promoters to find the best match. + * 3. Fall back to the legacy implementation if no match was found. + */ + PyObject *info = PyArrayIdentityHash_GetItem(ufunc->_dispatch_cache, + (PyObject **)op_dtypes); + if (info != NULL && PyObject_TypeCheck( + PyTuple_GET_ITEM(info, 1), &PyArrayMethod_Type)) { + /* Found the ArrayMethod and NOT a promoter: return it */ + return info; + } + + /* + * If `info == NULL`, loading from cache failed, use the full resolution + * in `resolve_implementation_info` (which caches its result on success). + */ + if (info == NULL) { + if (resolve_implementation_info(ufunc, + op_dtypes, NPY_FALSE, &info) < 0) { + return NULL; + } + if (info != NULL && PyObject_TypeCheck( + PyTuple_GET_ITEM(info, 1), &PyArrayMethod_Type)) { + /* + * Found the ArrayMethod and NOT promoter. Before returning it + * add it to the cache for faster lookup in the future. + */ + if (PyArrayIdentityHash_SetItem(ufunc->_dispatch_cache, + (PyObject **)op_dtypes, info, 0) < 0) { + return NULL; + } + return info; + } + } + + /* + * At this point `info` is NULL if there is no matching loop, or it is + * a promoter that needs to be used/called: + */ + if (info != NULL) { + PyObject *promoter = PyTuple_GET_ITEM(info, 1); + + info = call_promoter_and_recurse(ufunc, + promoter, op_dtypes, signature, ops); + if (info == NULL && PyErr_Occurred()) { + return NULL; + } + else if (info != NULL) { + /* Add result to the cache using the original types: */ + if (PyArrayIdentityHash_SetItem(ufunc->_dispatch_cache, + (PyObject **)op_dtypes, info, 0) < 0) { + return NULL; + } + return info; + } + } + + /* + * Even using promotion no loop was found. + * Using promotion failed, this should normally be an error. + * However, we need to give the legacy implementation a chance here. + * (it will modify `op_dtypes`). + */ + if (!allow_legacy_promotion || ufunc->type_resolver == NULL || + (ufunc->ntypes == 0 && ufunc->userloops == NULL)) { + /* Already tried or not a "legacy" ufunc (no loop found, return) */ + return NULL; + } + + PyArray_DTypeMeta *new_op_dtypes[NPY_MAXARGS] = {NULL}; + int cacheable = 1; /* TODO: only the comparison deprecation needs this */ + if (legacy_promote_using_legacy_type_resolver(ufunc, + ops, signature, new_op_dtypes, &cacheable) < 0) { + return NULL; + } + info = promote_and_get_info_and_ufuncimpl(ufunc, + ops, signature, new_op_dtypes, NPY_FALSE); + for (int i = 0; i < ufunc->nargs; i++) { + Py_XDECREF(new_op_dtypes[i]); + } + + /* Add this to the cache using the original types: */ + if (cacheable && PyArrayIdentityHash_SetItem(ufunc->_dispatch_cache, + (PyObject **)op_dtypes, info, 0) < 0) { + return NULL; + } + return info; +} + + +/** + * The central entry-point for the promotion and dispatching machinery. + * + * It currently may work with the operands (although it would be possible to + * only work with DType (classes/types). This is because it has to ensure + * that legacy (value-based promotion) is used when necessary. + * + * NOTE: The machinery here currently ignores output arguments unless + * they are part of the signature. This slightly limits unsafe loop + * specializations, which is important for the `ensure_reduce_compatible` + * fallback mode. + * To fix this, the caching mechanism (and dispatching) can be extended. + * When/if that happens, the `ensure_reduce_compatible` could be + * deprecated (it should never kick in because promotion kick in first). + * + * @param ufunc The ufunc object, used mainly for the fallback. + * @param ops The array operands (used only for the fallback). + * @param signature As input, the DType signature fixed explicitly by the user. + * The signature is *filled* in with the operation signature we end up + * using. + * @param op_dtypes The operand DTypes (without casting) which are specified + * either by the `signature` or by an `operand`. + * (outputs and the second input can be NULL for reductions). + * NOTE: In some cases, the promotion machinery may currently modify + * these including clearing the output. + * @param force_legacy_promotion If set, we have to use the old type resolution + * to implement value-based promotion/casting. + * @param ensure_reduce_compatible Must be set for reductions, in which case + * the found implementation is checked for reduce-like compatibility. + * If it is *not* compatible and `signature[2] != NULL`, we assume its + * output DType is correct (see NOTE above). + * If removed, promotion may require information about whether this + * is a reduction, so the more likely case is to always keep fixing this + * when necessary, but push down the handling so it can be cached. + */ +NPY_NO_EXPORT PyArrayMethodObject * +promote_and_get_ufuncimpl(PyUFuncObject *ufunc, + PyArrayObject *const ops[], + PyArray_DTypeMeta *signature[], + PyArray_DTypeMeta *op_dtypes[], + npy_bool force_legacy_promotion, + npy_bool allow_legacy_promotion, + npy_bool ensure_reduce_compatible) +{ + int nin = ufunc->nin, nargs = ufunc->nargs; + + /* + * Get the actual DTypes we operate with by mixing the operand array + * ones with the passed signature. + */ + for (int i = 0; i < nargs; i++) { + if (signature[i] != NULL) { + /* + * ignore the operand input, we cannot overwrite signature yet + * since it is fixed (cannot be promoted!) + */ + Py_INCREF(signature[i]); + Py_XSETREF(op_dtypes[i], signature[i]); + assert(i >= ufunc->nin || !NPY_DT_is_abstract(signature[i])); + } + else if (i >= nin) { + /* + * We currently just ignore outputs if not in signature, this will + * always give the/a correct result (limits registering specialized + * loops which include the cast). + * (See also comment in resolve_implementation_info.) + */ + Py_CLEAR(op_dtypes[i]); + } + } + + if (force_legacy_promotion) { + /* + * We must use legacy promotion for value-based logic. Call the old + * resolver once up-front to get the "actual" loop dtypes. + * After this (additional) promotion, we can even use normal caching. + */ + int cacheable = 1; /* unused, as we modify the original `op_dtypes` */ + if (legacy_promote_using_legacy_type_resolver(ufunc, + ops, signature, op_dtypes, &cacheable) < 0) { + return NULL; + } + } + + PyObject *info = promote_and_get_info_and_ufuncimpl(ufunc, + ops, signature, op_dtypes, allow_legacy_promotion); + + if (info == NULL) { + if (!PyErr_Occurred()) { + raise_no_loop_found_error(ufunc, (PyObject **)op_dtypes); + } + return NULL; + } + + PyArrayMethodObject *method = (PyArrayMethodObject *)PyTuple_GET_ITEM(info, 1); + + /* + * In certain cases (only the logical ufuncs really), the loop we found may + * not be reduce-compatible. Since the machinery can't distinguish a + * reduction with an output from a normal ufunc call, we have to assume + * the result DType is correct and force it for the input (if not forced + * already). + * NOTE: This does assume that all loops are "safe" see the NOTE in this + * comment. That could be relaxed, in which case we may need to + * cache if a call was for a reduction. + */ + PyObject *all_dtypes = PyTuple_GET_ITEM(info, 0); + if (ensure_reduce_compatible && signature[0] == NULL && + PyTuple_GET_ITEM(all_dtypes, 0) != PyTuple_GET_ITEM(all_dtypes, 2)) { + signature[0] = (PyArray_DTypeMeta *)PyTuple_GET_ITEM(all_dtypes, 2); + Py_INCREF(signature[0]); + return promote_and_get_ufuncimpl(ufunc, + ops, signature, op_dtypes, + force_legacy_promotion, allow_legacy_promotion, NPY_FALSE); + } + + for (int i = 0; i < nargs; i++) { + if (signature[i] == NULL) { + signature[i] = (PyArray_DTypeMeta *)PyTuple_GET_ITEM(all_dtypes, i); + Py_INCREF(signature[i]); + } + else { + assert((PyObject *)signature[i] == PyTuple_GET_ITEM(all_dtypes, i)); + } + } + + return method; +} + + +/* + * Generic promoter used by as a final fallback on ufuncs. Most operations are + * homogeneous, so we can try to find the homogeneous dtype on the inputs + * and use that. + * We need to special case the reduction case, where op_dtypes[0] == NULL + * is possible. + */ +NPY_NO_EXPORT int +default_ufunc_promoter(PyUFuncObject *ufunc, + PyArray_DTypeMeta *op_dtypes[], PyArray_DTypeMeta *signature[], + PyArray_DTypeMeta *new_op_dtypes[]) +{ + if (ufunc->type_resolver == &PyUFunc_SimpleBinaryComparisonTypeResolver + && signature[0] == NULL && signature[1] == NULL + && signature[2] != NULL && signature[2]->type_num != NPY_BOOL) { + /* bail out, this is _only_ to give future/deprecation warning! */ + return -1; + } + + /* If nin < 2 promotion is a no-op, so it should not be registered */ + assert(ufunc->nin > 1); + if (op_dtypes[0] == NULL) { + assert(ufunc->nin == 2 && ufunc->nout == 1); /* must be reduction */ + Py_INCREF(op_dtypes[1]); + new_op_dtypes[0] = op_dtypes[1]; + Py_INCREF(op_dtypes[1]); + new_op_dtypes[1] = op_dtypes[1]; + Py_INCREF(op_dtypes[1]); + new_op_dtypes[2] = op_dtypes[1]; + return 0; + } + PyArray_DTypeMeta *common = NULL; + /* + * If a signature is used and homogeneous in its outputs use that + * (Could/should likely be rather applied to inputs also, although outs + * only could have some advantage and input dtypes are rarely enforced.) + */ + for (int i = ufunc->nin; i < ufunc->nargs; i++) { + if (signature[i] != NULL) { + if (common == NULL) { + Py_INCREF(signature[i]); + common = signature[i]; + } + else if (common != signature[i]) { + Py_CLEAR(common); /* Not homogeneous, unset common */ + break; + } + } + } + /* Otherwise, use the common DType of all input operands */ + if (common == NULL) { + common = PyArray_PromoteDTypeSequence(ufunc->nin, op_dtypes); + if (common == NULL) { + if (PyErr_ExceptionMatches(PyExc_TypeError)) { + PyErr_Clear(); /* Do not propagate normal promotion errors */ + } + return -1; + } + } + + for (int i = 0; i < ufunc->nargs; i++) { + PyArray_DTypeMeta *tmp = common; + if (signature[i]) { + tmp = signature[i]; /* never replace a fixed one. */ + } + Py_INCREF(tmp); + new_op_dtypes[i] = tmp; + } + for (int i = ufunc->nin; i < ufunc->nargs; i++) { + Py_XINCREF(op_dtypes[i]); + new_op_dtypes[i] = op_dtypes[i]; + } + + Py_DECREF(common); + return 0; +} + + +/* + * In some cases, we assume that there will only ever be object loops, + * and the object loop should *always* be chosen. + * (in those cases more specific loops should not really be registered, but + * we do not check that.) + * + * We default to this for "old-style" ufuncs which have exactly one loop + * consisting only of objects (during registration time, numba mutates this + * but presumably). + */ +NPY_NO_EXPORT int +object_only_ufunc_promoter(PyUFuncObject *ufunc, + PyArray_DTypeMeta *NPY_UNUSED(op_dtypes[]), + PyArray_DTypeMeta *signature[], + PyArray_DTypeMeta *new_op_dtypes[]) +{ + PyArray_DTypeMeta *object_DType = PyArray_DTypeFromTypeNum(NPY_OBJECT); + + for (int i = 0; i < ufunc->nargs; i++) { + if (signature[i] == NULL) { + Py_INCREF(object_DType); + new_op_dtypes[i] = object_DType; + } + } + Py_DECREF(object_DType); + return 0; +} + +/* + * Special promoter for the logical ufuncs. The logical ufuncs can always + * use the ??->? and still get the correct output (as long as the output + * is not supposed to be `object`). + */ +static int +logical_ufunc_promoter(PyUFuncObject *NPY_UNUSED(ufunc), + PyArray_DTypeMeta *op_dtypes[], PyArray_DTypeMeta *signature[], + PyArray_DTypeMeta *new_op_dtypes[]) +{ + /* + * If we find any object DType at all, we currently force to object. + * However, if the output is specified and not object, there is no point, + * it should be just as well to cast the input rather than doing the + * unsafe out cast. + */ + int force_object = 0; + + if (signature[0] == NULL && signature[1] == NULL + && signature[2] != NULL && signature[2]->type_num != NPY_BOOL) { + /* bail out, this is _only_ to give future/deprecation warning! */ + return -1; + } + + for (int i = 0; i < 3; i++) { + PyArray_DTypeMeta *item; + if (signature[i] != NULL) { + item = signature[i]; + Py_INCREF(item); + if (item->type_num == NPY_OBJECT) { + force_object = 1; + } + } + else { + /* Always override to boolean */ + item = PyArray_DTypeFromTypeNum(NPY_BOOL); + if (op_dtypes[i] != NULL && op_dtypes[i]->type_num == NPY_OBJECT) { + force_object = 1; + } + } + new_op_dtypes[i] = item; + } + + if (!force_object || (op_dtypes[2] != NULL + && op_dtypes[2]->type_num != NPY_OBJECT)) { + return 0; + } + /* + * Actually, we have to use the OBJECT loop after all, set all we can + * to object (that might not work out, but try). + * + * NOTE: Change this to check for `op_dtypes[0] == NULL` to STOP + * returning `object` for `np.logical_and.reduce(obj_arr)` + * which will also affect `np.all` and `np.any`! + */ + for (int i = 0; i < 3; i++) { + if (signature[i] != NULL) { + continue; + } + Py_SETREF(new_op_dtypes[i], PyArray_DTypeFromTypeNum(NPY_OBJECT)); + } + return 0; +} + + +NPY_NO_EXPORT int +install_logical_ufunc_promoter(PyObject *ufunc) +{ + if (PyObject_Type(ufunc) != (PyObject *)&PyUFunc_Type) { + PyErr_SetString(PyExc_RuntimeError, + "internal numpy array, logical ufunc was not a ufunc?!"); + return -1; + } + PyObject *dtype_tuple = PyTuple_Pack(3, + &PyArrayDescr_Type, &PyArrayDescr_Type, &PyArrayDescr_Type, NULL); + if (dtype_tuple == NULL) { + return -1; + } + PyObject *promoter = PyCapsule_New(&logical_ufunc_promoter, + "numpy._ufunc_promoter", NULL); + if (promoter == NULL) { + Py_DECREF(dtype_tuple); + return -1; + } + + PyObject *info = PyTuple_Pack(2, dtype_tuple, promoter); + Py_DECREF(dtype_tuple); + Py_DECREF(promoter); + if (info == NULL) { + return -1; + } + + return PyUFunc_AddLoop((PyUFuncObject *)ufunc, info, 0); +} diff --git a/numpy/core/src/umath/dispatching.h b/numpy/core/src/umath/dispatching.h new file mode 100644 index 000000000000..a7e9e88d0d73 --- /dev/null +++ b/numpy/core/src/umath/dispatching.h @@ -0,0 +1,45 @@ +#ifndef _NPY_DISPATCHING_H +#define _NPY_DISPATCHING_H + +#define _UMATHMODULE + +#include <numpy/ufuncobject.h> +#include "array_method.h" + + +typedef int promoter_function(PyUFuncObject *ufunc, + PyArray_DTypeMeta *op_dtypes[], PyArray_DTypeMeta *signature[], + PyArray_DTypeMeta *new_op_dtypes[]); + +NPY_NO_EXPORT int +PyUFunc_AddLoop(PyUFuncObject *ufunc, PyObject *info, int ignore_duplicate); + +NPY_NO_EXPORT PyArrayMethodObject * +promote_and_get_ufuncimpl(PyUFuncObject *ufunc, + PyArrayObject *const ops[], + PyArray_DTypeMeta *signature[], + PyArray_DTypeMeta *op_dtypes[], + npy_bool force_legacy_promotion, + npy_bool allow_legacy_promotion, + npy_bool ensure_reduce_compatible); + +NPY_NO_EXPORT PyObject * +add_and_return_legacy_wrapping_ufunc_loop(PyUFuncObject *ufunc, + PyArray_DTypeMeta *operation_dtypes[], int ignore_duplicate); + +NPY_NO_EXPORT int +default_ufunc_promoter(PyUFuncObject *ufunc, + PyArray_DTypeMeta *op_dtypes[], PyArray_DTypeMeta *signature[], + PyArray_DTypeMeta *new_op_dtypes[]); + +NPY_NO_EXPORT int +object_only_ufunc_promoter(PyUFuncObject *ufunc, + PyArray_DTypeMeta *NPY_UNUSED(op_dtypes[]), + PyArray_DTypeMeta *signature[], + PyArray_DTypeMeta *new_op_dtypes[]); + +NPY_NO_EXPORT int +install_logical_ufunc_promoter(PyObject *ufunc); + + +#endif /*_NPY_DISPATCHING_H */ diff --git a/numpy/core/src/umath/extobj.c b/numpy/core/src/umath/extobj.c index 188054e22fb7..6b9a27e2621a 100644 --- a/numpy/core/src/umath/extobj.c +++ b/numpy/core/src/umath/extobj.c @@ -1,13 +1,12 @@ -#define _UMATHMODULE #define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE +#define _UMATHMODULE +#define PY_SSIZE_T_CLEAN #include <Python.h> #include "npy_config.h" -#define PY_ARRAY_UNIQUE_SYMBOL _npy_umathmodule_ARRAY_API -#define NO_IMPORT_ARRAY - #include "npy_pycompat.h" #include "extobj.h" @@ -111,8 +110,8 @@ _error_handler(int method, PyObject *errobj, char *errtype, int retstatus, int * errtype, name); goto fail; } - args = Py_BuildValue("NN", PyUString_FromString(errtype), - PyInt_FromLong((long) retstatus)); + args = Py_BuildValue("NN", PyUnicode_FromString(errtype), + PyLong_FromLong((long) retstatus)); if (args == NULL) { goto fail; } @@ -167,7 +166,7 @@ get_global_ext_obj(void) if (thedict == NULL) { thedict = PyEval_GetBuiltins(); } - ref = PyDict_GetItem(thedict, npy_um_str_pyvals_name); + ref = PyDict_GetItemWithError(thedict, npy_um_str_pyvals_name); #if USE_USE_DEFAULTS==1 } #endif @@ -214,7 +213,7 @@ _extract_pyvals(PyObject *ref, const char *name, int *bufsize, } if (bufsize != NULL) { - *bufsize = PyInt_AsLong(PyList_GET_ITEM(ref, 0)); + *bufsize = PyLong_AsLong(PyList_GET_ITEM(ref, 0)); if (error_converting(*bufsize)) { return -1; } @@ -231,7 +230,7 @@ _extract_pyvals(PyObject *ref, const char *name, int *bufsize, } if (errmask != NULL) { - *errmask = PyInt_AsLong(PyList_GET_ITEM(ref, 1)); + *errmask = PyLong_AsLong(PyList_GET_ITEM(ref, 1)); if (*errmask < 0) { if (PyErr_Occurred()) { return -1; @@ -292,6 +291,9 @@ _check_ufunc_fperr(int errmask, PyObject *extobj, const char *ufunc_name) { /* Get error object globals */ if (extobj == NULL) { extobj = get_global_ext_obj(); + if (extobj == NULL && PyErr_Occurred()) { + return -1; + } } if (_extract_pyvals(extobj, ufunc_name, NULL, NULL, &errobj) < 0) { @@ -313,6 +315,9 @@ _get_bufsize_errmask(PyObject * extobj, const char *ufunc_name, /* Get the buffersize and errormask */ if (extobj == NULL) { extobj = get_global_ext_obj(); + if (extobj == NULL && PyErr_Occurred()) { + return -1; + } } if (_extract_pyvals(extobj, ufunc_name, buffersize, errormask, NULL) < 0) { diff --git a/numpy/core/src/umath/fast_loop_macros.h b/numpy/core/src/umath/fast_loop_macros.h new file mode 100644 index 000000000000..4a36c9721879 --- /dev/null +++ b/numpy/core/src/umath/fast_loop_macros.h @@ -0,0 +1,368 @@ +/** + * Macros to help build fast ufunc inner loops. + * + * These expect to have access to the arguments of a typical ufunc loop, + * + * char **args + * npy_intp const *dimensions + * npy_intp const *steps + */ +#ifndef _NPY_UMATH_FAST_LOOP_MACROS_H_ +#define _NPY_UMATH_FAST_LOOP_MACROS_H_ + +/* + * MAX_STEP_SIZE is used to determine if we need to use SIMD version of the ufunc. + * Very large step size can be as slow as processing it using scalar. The + * value of 2097152 ( = 2MB) was chosen using 2 considerations: + * 1) Typical linux kernel page size is 4Kb, but sometimes it could also be 2MB + * which is == 2097152 Bytes. For a step size as large as this, surely all + * the loads/stores of gather/scatter instructions falls on 16 different pages + * which one would think would slow down gather/scatter instructions. + * 2) It additionally satisfies MAX_STEP_SIZE*16/esize < NPY_MAX_INT32 which + * allows us to use i32 version of gather/scatter (as opposed to the i64 version) + * without problems (step larger than NPY_MAX_INT32*esize/16 would require use of + * i64gather/scatter). esize = element size = 4/8 bytes for float/double. + */ +#define MAX_STEP_SIZE 2097152 + +static NPY_INLINE npy_uintp +abs_ptrdiff(char *a, char *b) +{ + return (a > b) ? (a - b) : (b - a); +} + +/** + * Simple unoptimized loop macros that iterate over the ufunc arguments in + * parallel. + * @{ + */ + +/** (<ignored>) -> (op1) */ +#define OUTPUT_LOOP\ + char *op1 = args[1];\ + npy_intp os1 = steps[1];\ + npy_intp n = dimensions[0];\ + npy_intp i;\ + for(i = 0; i < n; i++, op1 += os1) + +/** (ip1) -> (op1) */ +#define UNARY_LOOP\ + char *ip1 = args[0], *op1 = args[1];\ + npy_intp is1 = steps[0], os1 = steps[1];\ + npy_intp n = dimensions[0];\ + npy_intp i;\ + for(i = 0; i < n; i++, ip1 += is1, op1 += os1) + +/** (ip1) -> (op1, op2) */ +#define UNARY_LOOP_TWO_OUT\ + char *ip1 = args[0], *op1 = args[1], *op2 = args[2];\ + npy_intp is1 = steps[0], os1 = steps[1], os2 = steps[2];\ + npy_intp n = dimensions[0];\ + npy_intp i;\ + for(i = 0; i < n; i++, ip1 += is1, op1 += os1, op2 += os2) + +#define BINARY_DEFS\ + char *ip1 = args[0], *ip2 = args[1], *op1 = args[2];\ + npy_intp is1 = steps[0], is2 = steps[1], os1 = steps[2];\ + npy_intp n = dimensions[0];\ + npy_intp i;\ + +#define BINARY_LOOP_SLIDING\ + for(i = 0; i < n; i++, ip1 += is1, ip2 += is2, op1 += os1) + +/** (ip1, ip2) -> (op1) */ +#define BINARY_LOOP\ + BINARY_DEFS\ + BINARY_LOOP_SLIDING + +/** (ip1, ip2) -> (op1, op2) */ +#define BINARY_LOOP_TWO_OUT\ + char *ip1 = args[0], *ip2 = args[1], *op1 = args[2], *op2 = args[3];\ + npy_intp is1 = steps[0], is2 = steps[1], os1 = steps[2], os2 = steps[3];\ + npy_intp n = dimensions[0];\ + npy_intp i;\ + for(i = 0; i < n; i++, ip1 += is1, ip2 += is2, op1 += os1, op2 += os2) + +/** (ip1, ip2, ip3) -> (op1) */ +#define TERNARY_LOOP\ + char *ip1 = args[0], *ip2 = args[1], *ip3 = args[2], *op1 = args[3];\ + npy_intp is1 = steps[0], is2 = steps[1], is3 = steps[2], os1 = steps[3];\ + npy_intp n = dimensions[0];\ + npy_intp i;\ + for(i = 0; i < n; i++, ip1 += is1, ip2 += is2, ip3 += is3, op1 += os1) + +/** @} */ + +/* unary loop input and output contiguous */ +#define IS_UNARY_CONT(tin, tout) (steps[0] == sizeof(tin) && \ + steps[1] == sizeof(tout)) + +#define IS_OUTPUT_CONT(tout) (steps[1] == sizeof(tout)) + +#define IS_BINARY_REDUCE ((args[0] == args[2])\ + && (steps[0] == steps[2])\ + && (steps[0] == 0)) + +/* binary loop input and output contiguous */ +#define IS_BINARY_CONT(tin, tout) (steps[0] == sizeof(tin) && \ + steps[1] == sizeof(tin) && \ + steps[2] == sizeof(tout)) + +/* binary loop input and output contiguous with first scalar */ +#define IS_BINARY_CONT_S1(tin, tout) (steps[0] == 0 && \ + steps[1] == sizeof(tin) && \ + steps[2] == sizeof(tout)) + +/* binary loop input and output contiguous with second scalar */ +#define IS_BINARY_CONT_S2(tin, tout) (steps[0] == sizeof(tin) && \ + steps[1] == 0 && \ + steps[2] == sizeof(tout)) + +/* + * loop with contiguous specialization + * op should be the code working on `tin in` and + * storing the result in `tout *out` + * combine with NPY_GCC_OPT_3 to allow autovectorization + * should only be used where its worthwhile to avoid code bloat + */ +#define BASE_UNARY_LOOP(tin, tout, op) \ + UNARY_LOOP { \ + const tin in = *(tin *)ip1; \ + tout *out = (tout *)op1; \ + op; \ + } + +#define UNARY_LOOP_FAST(tin, tout, op) \ + do { \ + /* condition allows compiler to optimize the generic macro */ \ + if (IS_UNARY_CONT(tin, tout)) { \ + if (args[0] == args[1]) { \ + BASE_UNARY_LOOP(tin, tout, op) \ + } \ + else { \ + BASE_UNARY_LOOP(tin, tout, op) \ + } \ + } \ + else { \ + BASE_UNARY_LOOP(tin, tout, op) \ + } \ + } \ + while (0) + +/* + * loop with contiguous specialization + * op should be the code working on `tin in1`, `tin in2` and + * storing the result in `tout *out` + * combine with NPY_GCC_OPT_3 to allow autovectorization + * should only be used where its worthwhile to avoid code bloat + */ +#define BASE_BINARY_LOOP(tin, tout, op) \ + BINARY_LOOP { \ + const tin in1 = *(tin *)ip1; \ + const tin in2 = *(tin *)ip2; \ + tout *out = (tout *)op1; \ + op; \ + } + +/* + * unfortunately gcc 6/7 regressed and we need to give it additional hints to + * vectorize inplace operations (PR80198) + * must only be used after op1 == ip1 or ip2 has been checked + * TODO: using ivdep might allow other compilers to vectorize too + */ +#if __GNUC__ >= 6 +#define IVDEP_LOOP _Pragma("GCC ivdep") +#else +#define IVDEP_LOOP +#endif +#define BASE_BINARY_LOOP_INP(tin, tout, op) \ + BINARY_DEFS\ + IVDEP_LOOP \ + for(i = 0; i < n; i++, ip1 += is1, ip2 += is2, op1 += os1) { \ + const tin in1 = *(tin *)ip1; \ + const tin in2 = *(tin *)ip2; \ + tout *out = (tout *)op1; \ + op; \ + } + +#define BASE_BINARY_LOOP_S(tin, tout, cin, cinp, vin, vinp, op) \ + const tin cin = *(tin *)cinp; \ + BINARY_LOOP { \ + const tin vin = *(tin *)vinp; \ + tout *out = (tout *)op1; \ + op; \ + } + +/* PR80198 again, scalar works without the pragma */ +#define BASE_BINARY_LOOP_S_INP(tin, tout, cin, cinp, vin, vinp, op) \ + const tin cin = *(tin *)cinp; \ + BINARY_LOOP { \ + const tin vin = *(tin *)vinp; \ + tout *out = (tout *)vinp; \ + op; \ + } + +#define BINARY_LOOP_FAST(tin, tout, op) \ + do { \ + /* condition allows compiler to optimize the generic macro */ \ + if (IS_BINARY_CONT(tin, tout)) { \ + if (abs_ptrdiff(args[2], args[0]) == 0 && \ + abs_ptrdiff(args[2], args[1]) >= NPY_MAX_SIMD_SIZE) { \ + BASE_BINARY_LOOP_INP(tin, tout, op) \ + } \ + else if (abs_ptrdiff(args[2], args[1]) == 0 && \ + abs_ptrdiff(args[2], args[0]) >= NPY_MAX_SIMD_SIZE) { \ + BASE_BINARY_LOOP_INP(tin, tout, op) \ + } \ + else { \ + BASE_BINARY_LOOP(tin, tout, op) \ + } \ + } \ + else if (IS_BINARY_CONT_S1(tin, tout)) { \ + if (abs_ptrdiff(args[2], args[1]) == 0) { \ + BASE_BINARY_LOOP_S_INP(tin, tout, in1, args[0], in2, ip2, op) \ + } \ + else { \ + BASE_BINARY_LOOP_S(tin, tout, in1, args[0], in2, ip2, op) \ + } \ + } \ + else if (IS_BINARY_CONT_S2(tin, tout)) { \ + if (abs_ptrdiff(args[2], args[0]) == 0) { \ + BASE_BINARY_LOOP_S_INP(tin, tout, in2, args[1], in1, ip1, op) \ + } \ + else { \ + BASE_BINARY_LOOP_S(tin, tout, in2, args[1], in1, ip1, op) \ + }\ + } \ + else { \ + BASE_BINARY_LOOP(tin, tout, op) \ + } \ + } \ + while (0) + +#define BINARY_REDUCE_LOOP_INNER\ + char *ip2 = args[1]; \ + npy_intp is2 = steps[1]; \ + npy_intp n = dimensions[0]; \ + npy_intp i; \ + for(i = 0; i < n; i++, ip2 += is2) + +#define BINARY_REDUCE_LOOP(TYPE)\ + char *iop1 = args[0]; \ + TYPE io1 = *(TYPE *)iop1; \ + BINARY_REDUCE_LOOP_INNER + +#define IS_BINARY_STRIDE_ONE(esize, vsize) \ + ((steps[0] == esize) && \ + (steps[1] == esize) && \ + (steps[2] == esize) && \ + (abs_ptrdiff(args[2], args[0]) >= vsize) && \ + (abs_ptrdiff(args[2], args[1]) >= vsize)) + +/* + * stride is equal to element size and input and destination are equal or + * don't overlap within one register. The check of the steps against + * esize also quarantees that steps are >= 0. + */ +#define IS_BLOCKABLE_UNARY(esize, vsize) \ + (steps[0] == (esize) && steps[0] == steps[1] && \ + (npy_is_aligned(args[0], esize) && npy_is_aligned(args[1], esize)) && \ + ((abs_ptrdiff(args[1], args[0]) >= (vsize)) || \ + ((abs_ptrdiff(args[1], args[0]) == 0)))) + +/* + * Avoid using SIMD for very large step sizes for several reasons: + * 1) Supporting large step sizes requires use of i64gather/scatter_ps instructions, + * in which case we need two i64gather instructions and an additional vinsertf32x8 + * instruction to load a single zmm register (since one i64gather instruction + * loads into a ymm register). This is not ideal for performance. + * 2) Gather and scatter instructions can be slow when the loads/stores + * cross page boundaries. + * + * We instead rely on i32gather/scatter_ps instructions which use a 32-bit index + * element. The index needs to be < INT_MAX to avoid overflow. MAX_STEP_SIZE + * ensures this. The condition also requires that the input and output arrays + * should have no overlap in memory. + */ +#define IS_BINARY_SMALL_STEPS_AND_NOMEMOVERLAP \ + ((labs(steps[0]) < MAX_STEP_SIZE) && \ + (labs(steps[1]) < MAX_STEP_SIZE) && \ + (labs(steps[2]) < MAX_STEP_SIZE) && \ + (nomemoverlap(args[0], steps[0] * dimensions[0], args[2], steps[2] * dimensions[0])) && \ + (nomemoverlap(args[1], steps[1] * dimensions[0], args[2], steps[2] * dimensions[0]))) + +#define IS_UNARY_TWO_OUT_SMALL_STEPS_AND_NOMEMOVERLAP \ + ((labs(steps[0]) < MAX_STEP_SIZE) && \ + (labs(steps[1]) < MAX_STEP_SIZE) && \ + (labs(steps[2]) < MAX_STEP_SIZE) && \ + (nomemoverlap(args[0], steps[0] * dimensions[0], args[2], steps[2] * dimensions[0])) && \ + (nomemoverlap(args[0], steps[0] * dimensions[0], args[1], steps[1] * dimensions[0]))) + +/* + * 1) Output should be contiguous, can handle strided input data + * 2) Input step should be smaller than MAX_STEP_SIZE for performance + * 3) Input and output arrays should have no overlap in memory + */ +#define IS_OUTPUT_BLOCKABLE_UNARY(esizein, esizeout, vsize) \ + ((steps[0] & (esizein-1)) == 0 && \ + steps[1] == (esizeout) && llabs(steps[0]) < MAX_STEP_SIZE && \ + (nomemoverlap(args[1], steps[1] * dimensions[0], args[0], steps[0] * dimensions[0]))) + +#define IS_BLOCKABLE_REDUCE(esize, vsize) \ + (steps[1] == (esize) && abs_ptrdiff(args[1], args[0]) >= (vsize) && \ + npy_is_aligned(args[1], (esize)) && \ + npy_is_aligned(args[0], (esize))) + +#define IS_BLOCKABLE_BINARY(esize, vsize) \ + (steps[0] == steps[1] && steps[1] == steps[2] && steps[2] == (esize) && \ + npy_is_aligned(args[2], (esize)) && npy_is_aligned(args[1], (esize)) && \ + npy_is_aligned(args[0], (esize)) && \ + (abs_ptrdiff(args[2], args[0]) >= (vsize) || \ + abs_ptrdiff(args[2], args[0]) == 0) && \ + (abs_ptrdiff(args[2], args[1]) >= (vsize) || \ + abs_ptrdiff(args[2], args[1]) >= 0)) + +#define IS_BLOCKABLE_BINARY_SCALAR1(esize, vsize) \ + (steps[0] == 0 && steps[1] == steps[2] && steps[2] == (esize) && \ + npy_is_aligned(args[2], (esize)) && npy_is_aligned(args[1], (esize)) && \ + ((abs_ptrdiff(args[2], args[1]) >= (vsize)) || \ + (abs_ptrdiff(args[2], args[1]) == 0)) && \ + abs_ptrdiff(args[2], args[0]) >= (esize)) + +#define IS_BLOCKABLE_BINARY_SCALAR2(esize, vsize) \ + (steps[1] == 0 && steps[0] == steps[2] && steps[2] == (esize) && \ + npy_is_aligned(args[2], (esize)) && npy_is_aligned(args[0], (esize)) && \ + ((abs_ptrdiff(args[2], args[0]) >= (vsize)) || \ + (abs_ptrdiff(args[2], args[0]) == 0)) && \ + abs_ptrdiff(args[2], args[1]) >= (esize)) + +#undef abs_ptrdiff + +#define IS_BLOCKABLE_BINARY_BOOL(esize, vsize) \ + (steps[0] == (esize) && steps[0] == steps[1] && steps[2] == (1) && \ + npy_is_aligned(args[1], (esize)) && \ + npy_is_aligned(args[0], (esize))) + +#define IS_BLOCKABLE_BINARY_SCALAR1_BOOL(esize, vsize) \ + (steps[0] == 0 && steps[1] == (esize) && steps[2] == (1) && \ + npy_is_aligned(args[1], (esize))) + +#define IS_BLOCKABLE_BINARY_SCALAR2_BOOL(esize, vsize) \ + (steps[0] == (esize) && steps[1] == 0 && steps[2] == (1) && \ + npy_is_aligned(args[0], (esize))) + +/* align var to alignment */ +#define LOOP_BLOCK_ALIGN_VAR(var, type, alignment)\ + npy_intp i, peel = npy_aligned_block_offset(var, sizeof(type),\ + alignment, n);\ + for(i = 0; i < peel; i++) + +#define LOOP_BLOCKED(type, vsize)\ + for(; i < npy_blocked_end(peel, sizeof(type), vsize, n);\ + i += (vsize / sizeof(type))) + +#define LOOP_BLOCKED_END\ + for (; i < n; i++) + + +#endif /* _NPY_UMATH_FAST_LOOP_MACROS_H_ */ diff --git a/numpy/core/src/umath/funcs.inc.src b/numpy/core/src/umath/funcs.inc.src index da2ab07f8b33..9b04dc77912e 100644 --- a/numpy/core/src/umath/funcs.inc.src +++ b/numpy/core/src/umath/funcs.inc.src @@ -26,23 +26,19 @@ Py_square(PyObject *o) static PyObject * Py_get_one(PyObject *NPY_UNUSED(o)) { - return PyInt_FromLong(1); + return PyLong_FromLong(1); } static PyObject * Py_reciprocal(PyObject *o) { - PyObject *one = PyInt_FromLong(1); + PyObject *one = PyLong_FromLong(1); PyObject *result; if (!one) { return NULL; } -#if defined(NPY_PY3K) result = PyNumber_TrueDivide(one, o); -#else - result = PyNumber_Divide(one, o); -#endif Py_DECREF(one); return result; } @@ -159,13 +155,45 @@ npy_ObjectLogicalNot(PyObject *i1) } } +static PyObject * +npy_ObjectFloor(PyObject *obj) { + static PyObject *math_floor_func = NULL; + + npy_cache_import("math", "floor", &math_floor_func); + if (math_floor_func == NULL) { + return NULL; + } + return PyObject_CallFunction(math_floor_func, "O", obj); +} + +static PyObject * +npy_ObjectCeil(PyObject *obj) { + static PyObject *math_ceil_func = NULL; + + npy_cache_import("math", "ceil", &math_ceil_func); + if (math_ceil_func == NULL) { + return NULL; + } + return PyObject_CallFunction(math_ceil_func, "O", obj); +} + +static PyObject * +npy_ObjectTrunc(PyObject *obj) { + static PyObject *math_trunc_func = NULL; + + npy_cache_import("math", "trunc", &math_trunc_func); + if (math_trunc_func == NULL) { + return NULL; + } + return PyObject_CallFunction(math_trunc_func, "O", obj); +} + static PyObject * npy_ObjectGCD(PyObject *i1, PyObject *i2) { PyObject *gcd = NULL; - /* use math.gcd if available, and valid on the provided types */ -#if PY_VERSION_HEX >= 0x03050000 + /* use math.gcd if valid on the provided types */ { static PyObject *math_gcd_func = NULL; @@ -180,7 +208,6 @@ npy_ObjectGCD(PyObject *i1, PyObject *i2) /* silence errors, and fall back on pure-python gcd */ PyErr_Clear(); } -#endif /* otherwise, use our internal one, written in python */ { @@ -195,7 +222,8 @@ npy_ObjectGCD(PyObject *i1, PyObject *i2) return NULL; } /* _gcd has some unusual behaviour regarding sign */ - return PyNumber_Absolute(gcd); + Py_SETREF(gcd, PyNumber_Absolute(gcd)); + return gcd; } } @@ -213,17 +241,30 @@ npy_ObjectLCM(PyObject *i1, PyObject *i2) * no remainder */ tmp = PyNumber_FloorDivide(i1, gcd); + Py_DECREF(gcd); if(tmp == NULL) { return NULL; } - tmp = PyNumber_Multiply(tmp, i2); + Py_SETREF(tmp, PyNumber_Multiply(tmp, i2)); if(tmp == NULL) { return NULL; } /* even though we fix gcd to be positive, we need to do it again here */ - return PyNumber_Absolute(tmp); + Py_SETREF(tmp, PyNumber_Absolute(tmp)); + return tmp; +} + + +static PyObject * +npy_ObjectClip(PyObject *arr, PyObject *min, PyObject *max) { + PyObject *o = npy_ObjectMax(arr, min); + if (o == NULL) { + return NULL; + } + Py_SETREF(o, npy_ObjectMin(o, max)); + return o; } /* @@ -313,9 +354,9 @@ nc_exp2@c@(@ctype@ *x, @ctype@ *r) static void nc_expm1@c@(@ctype@ *x, @ctype@ *r) { - @ftype@ a = npy_exp@c@(x->real); - r->real = a*npy_cos@c@(x->imag) - 1.0@c@; - r->imag = a*npy_sin@c@(x->imag); + @ftype@ a = npy_sin@c@(x->imag / 2); + r->real = npy_expm1@c@(x->real) * npy_cos@c@(x->imag) - 2 * a * a; + r->imag = npy_exp@c@(x->real) * npy_sin@c@(x->imag); return; } diff --git a/numpy/core/src/umath/legacy_array_method.c b/numpy/core/src/umath/legacy_array_method.c new file mode 100644 index 000000000000..ef24edff1c98 --- /dev/null +++ b/numpy/core/src/umath/legacy_array_method.c @@ -0,0 +1,312 @@ +/* + * This file defines most of the machinery in order to wrap legacy style + * ufunc loops into new style arraymethods. + */ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE +#define _UMATHMODULE + +#define PY_SSIZE_T_CLEAN +#include <Python.h> + +#include "numpy/ndarraytypes.h" + +#include "convert_datatype.h" +#include "array_method.h" +#include "dtype_transfer.h" +#include "legacy_array_method.h" +#include "dtypemeta.h" + + +typedef struct { + NpyAuxData base; + /* The legacy loop and additional user data: */ + PyUFuncGenericFunction loop; + void *user_data; + /* Whether to check for PyErr_Occurred(), must require GIL if used */ + int pyerr_check; +} legacy_array_method_auxdata; + + +/* Use a free list, since we should normally only need one at a time */ +#define NPY_LOOP_DATA_CACHE_SIZE 5 +static int loop_data_num_cached = 0; +static legacy_array_method_auxdata *loop_data_cache[NPY_LOOP_DATA_CACHE_SIZE]; + + +static void +legacy_array_method_auxdata_free(NpyAuxData *data) +{ + if (loop_data_num_cached < NPY_LOOP_DATA_CACHE_SIZE) { + loop_data_cache[loop_data_num_cached] = ( + (legacy_array_method_auxdata *)data); + loop_data_num_cached++; + } + else { + PyMem_Free(data); + } +} + +#undef NPY_LOOP_DATA_CACHE_SIZE + + +NpyAuxData * +get_new_loop_data( + PyUFuncGenericFunction loop, void *user_data, int pyerr_check) +{ + legacy_array_method_auxdata *data; + if (NPY_LIKELY(loop_data_num_cached > 0)) { + loop_data_num_cached--; + data = loop_data_cache[loop_data_num_cached]; + } + else { + data = PyMem_Malloc(sizeof(legacy_array_method_auxdata)); + if (data == NULL) { + return NULL; + } + data->base.free = legacy_array_method_auxdata_free; + data->base.clone = NULL; /* no need for cloning (at least for now) */ + } + data->loop = loop; + data->user_data = user_data; + data->pyerr_check = pyerr_check; + return (NpyAuxData *)data; +} + + +/* + * This is a thin wrapper around the legacy loop signature. + */ +static int +generic_wrapped_legacy_loop(PyArrayMethod_Context *NPY_UNUSED(context), + char *const *data, const npy_intp *dimensions, const npy_intp *strides, + NpyAuxData *auxdata) +{ + legacy_array_method_auxdata *ldata = (legacy_array_method_auxdata *)auxdata; + + ldata->loop((char **)data, dimensions, strides, ldata->user_data); + if (ldata->pyerr_check && PyErr_Occurred()) { + return -1; + } + return 0; +} + + +/* + * Signal that the old type-resolution function must be used to resolve + * the descriptors (mainly/only used for datetimes due to the unit). + * + * ArrayMethod's are expected to implement this, but it is too tricky + * to support properly. So we simply set an error that should never be seen. + */ +NPY_NO_EXPORT NPY_CASTING +wrapped_legacy_resolve_descriptors(PyArrayMethodObject *NPY_UNUSED(self), + PyArray_DTypeMeta *NPY_UNUSED(dtypes[]), + PyArray_Descr *NPY_UNUSED(given_descrs[]), + PyArray_Descr *NPY_UNUSED(loop_descrs[])) +{ + PyErr_SetString(PyExc_RuntimeError, + "cannot use legacy wrapping ArrayMethod without calling the ufunc " + "itself. If this error is hit, the solution will be to port the " + "legacy ufunc loop implementation to the new API."); + return -1; +} + +/* + * Much the same as the default type resolver, but tries a bit harder to + * preserve metadata. + */ +static NPY_CASTING +simple_legacy_resolve_descriptors( + PyArrayMethodObject *method, + PyArray_DTypeMeta **dtypes, + PyArray_Descr **given_descrs, + PyArray_Descr **output_descrs) +{ + int i = 0; + int nin = method->nin; + int nout = method->nout; + + if (nin == 2 && nout == 1 && given_descrs[2] != NULL + && dtypes[0] == dtypes[2]) { + /* + * Could be a reduction, which requires `descr[0] is descr[2]` + * (identity) at least currently. This is because `op[0] is op[2]`. + * (If the output descriptor is not passed, the below works.) + */ + output_descrs[2] = ensure_dtype_nbo(given_descrs[2]); + if (output_descrs[2] == NULL) { + Py_CLEAR(output_descrs[2]); + return -1; + } + Py_INCREF(output_descrs[2]); + output_descrs[0] = output_descrs[2]; + if (dtypes[1] == dtypes[2]) { + /* Same for the second one (accumulation is stricter) */ + Py_INCREF(output_descrs[2]); + output_descrs[1] = output_descrs[2]; + } + else { + output_descrs[1] = ensure_dtype_nbo(given_descrs[1]); + if (output_descrs[1] == NULL) { + i = 2; + goto fail; + } + } + return NPY_NO_CASTING; + } + + for (; i < nin + nout; i++) { + if (given_descrs[i] != NULL) { + output_descrs[i] = ensure_dtype_nbo(given_descrs[i]); + } + else if (dtypes[i] == dtypes[0] && i > 0) { + /* Preserve metadata from the first operand if same dtype */ + Py_INCREF(output_descrs[0]); + output_descrs[i] = output_descrs[0]; + } + else { + output_descrs[i] = NPY_DT_CALL_default_descr(dtypes[i]); + } + if (output_descrs[i] == NULL) { + goto fail; + } + } + + return NPY_NO_CASTING; + + fail: + for (; i >= 0; i--) { + Py_CLEAR(output_descrs[i]); + } + return -1; +} + + +/* + * This function grabs the legacy inner-loop. If this turns out to be slow + * we could probably cache it (with some care). + */ +NPY_NO_EXPORT int +get_wrapped_legacy_ufunc_loop(PyArrayMethod_Context *context, + int aligned, int move_references, + npy_intp *NPY_UNUSED(strides), + PyArrayMethod_StridedLoop **out_loop, + NpyAuxData **out_transferdata, + NPY_ARRAYMETHOD_FLAGS *flags) +{ + assert(aligned); + assert(!move_references); + + if (context->caller == NULL || + !PyObject_TypeCheck(context->caller, &PyUFunc_Type)) { + PyErr_Format(PyExc_RuntimeError, + "cannot call %s without its ufunc as caller context.", + context->method->name); + return -1; + } + + PyUFuncObject *ufunc = (PyUFuncObject *)context->caller; + void *user_data; + int needs_api = 0; + + PyUFuncGenericFunction loop = NULL; + /* Note that `needs_api` is not reliable (it was in fact unused normally) */ + if (ufunc->legacy_inner_loop_selector(ufunc, + context->descriptors, &loop, &user_data, &needs_api) < 0) { + return -1; + } + *flags = context->method->flags & NPY_METH_RUNTIME_FLAGS; + if (needs_api) { + *flags |= NPY_METH_REQUIRES_PYAPI; + } + + *out_loop = &generic_wrapped_legacy_loop; + *out_transferdata = get_new_loop_data( + loop, user_data, (*flags & NPY_METH_REQUIRES_PYAPI) != 0); + if (*out_transferdata == NULL) { + PyErr_NoMemory(); + return -1; + } + return 0; +} + + +/* + * Get the unbound ArrayMethod which wraps the instances of the ufunc. + * Note that this function stores the result on the ufunc and then only + * returns the same one. + */ +NPY_NO_EXPORT PyArrayMethodObject * +PyArray_NewLegacyWrappingArrayMethod(PyUFuncObject *ufunc, + PyArray_DTypeMeta *signature[]) +{ + char method_name[101]; + const char *name = ufunc->name ? ufunc->name : "<unknown>"; + snprintf(method_name, 100, "legacy_ufunc_wrapper_for_%s", name); + + /* + * Assume that we require the Python API when any of the (legacy) dtypes + * flags it. + */ + int any_output_flexible = 0; + NPY_ARRAYMETHOD_FLAGS flags = 0; + if (ufunc->nargs == 3 && + signature[0]->type_num == NPY_BOOL && + signature[1]->type_num == NPY_BOOL && + signature[2]->type_num == NPY_BOOL && ( + strcmp(ufunc->name, "logical_or") == 0 || + strcmp(ufunc->name, "logical_and") == 0 || + strcmp(ufunc->name, "logical_xor") == 0)) { + /* + * This is a logical ufunc, and the `??->?` loop`. It is always OK + * to cast any input to bool, because that cast is defined by + * truthiness. + * This allows to ensure two things: + * 1. `np.all`/`np.any` know that force casting the input is OK + * (they must do this since there are no `?l->?`, etc. loops) + * 2. The logical functions automatically work for any DType + * implementing a cast to boolean. + */ + flags = _NPY_METH_FORCE_CAST_INPUTS; + } + + for (int i = 0; i < ufunc->nin+ufunc->nout; i++) { + if (signature[i]->singleton->flags & ( + NPY_ITEM_REFCOUNT | NPY_ITEM_IS_POINTER | NPY_NEEDS_PYAPI)) { + flags |= NPY_METH_REQUIRES_PYAPI; + } + if (NPY_DT_is_parametric(signature[i])) { + any_output_flexible = 1; + } + } + + PyType_Slot slots[3] = { + {NPY_METH_get_loop, &get_wrapped_legacy_ufunc_loop}, + {NPY_METH_resolve_descriptors, &simple_legacy_resolve_descriptors}, + {0, NULL}, + }; + if (any_output_flexible) { + /* We cannot use the default descriptor resolver. */ + slots[1].pfunc = &wrapped_legacy_resolve_descriptors; + } + + PyArrayMethod_Spec spec = { + .name = method_name, + .nin = ufunc->nin, + .nout = ufunc->nout, + .dtypes = signature, + .flags = flags, + .slots = slots, + .casting = NPY_NO_CASTING, + }; + + PyBoundArrayMethodObject *bound_res = PyArrayMethod_FromSpec_int(&spec, 1); + if (bound_res == NULL) { + return NULL; + } + PyArrayMethodObject *res = bound_res->method; + Py_INCREF(res); + Py_DECREF(bound_res); + return res; +} diff --git a/numpy/core/src/umath/legacy_array_method.h b/numpy/core/src/umath/legacy_array_method.h new file mode 100644 index 000000000000..0dec1fb3a485 --- /dev/null +++ b/numpy/core/src/umath/legacy_array_method.h @@ -0,0 +1,33 @@ +#ifndef _NPY_LEGACY_ARRAY_METHOD_H +#define _NPY_LEGACY_ARRAY_METHOD_H + +#include "numpy/ndarraytypes.h" +#include "numpy/ufuncobject.h" +#include "array_method.h" + + +NPY_NO_EXPORT PyArrayMethodObject * +PyArray_NewLegacyWrappingArrayMethod(PyUFuncObject *ufunc, + PyArray_DTypeMeta *signature[]); + + + +/* + * The following two symbols are in the header so that other places can use + * them to probe for special cases (or whether an ArrayMethod is a "legacy" + * one). + */ +NPY_NO_EXPORT int +get_wrapped_legacy_ufunc_loop(PyArrayMethod_Context *context, + int aligned, int move_references, + npy_intp *NPY_UNUSED(strides), + PyArrayMethod_StridedLoop **out_loop, + NpyAuxData **out_transferdata, + NPY_ARRAYMETHOD_FLAGS *flags); + +NPY_NO_EXPORT NPY_CASTING +wrapped_legacy_resolve_descriptors(PyArrayMethodObject *, + PyArray_DTypeMeta **, PyArray_Descr **, PyArray_Descr **); + + +#endif /*_NPY_LEGACY_ARRAY_METHOD_H */ diff --git a/numpy/core/src/umath/loops.c.src b/numpy/core/src/umath/loops.c.src index 1ca298b3004c..aaa694f34dbb 100644 --- a/numpy/core/src/umath/loops.c.src +++ b/numpy/core/src/umath/loops.c.src @@ -1,14 +1,12 @@ /* -*- c -*- */ +#define PY_SSIZE_T_CLEAN +#include <Python.h> #define _UMATHMODULE +#define _MULTIARRAYMODULE #define NPY_NO_DEPRECATED_API NPY_API_VERSION -#include "Python.h" - #include "npy_config.h" -#define PY_ARRAY_UNIQUE_SYMBOL _npy_umathmodule_ARRAY_API -#define NO_IMPORT_ARRAY - #include "numpy/npy_common.h" #include "numpy/arrayobject.h" #include "numpy/ufuncobject.h" @@ -22,6 +20,9 @@ #include <string.h> /* for memchr */ +/* Use Libdivide for faster division */ +#include "numpy/libdivide/libdivide.h" + /* * cutoff blocksize for pairwise summation * decreasing it decreases errors slightly as more pairs are summed but @@ -40,6 +41,9 @@ #define NPY_MAX_SIMD_SIZE 1024 #endif +/** Provides the various *_LOOP macros */ +#include "fast_loop_macros.h" + /* * include vectorized functions and dispatchers * this file is safe to include also for generic builds @@ -48,409 +52,127 @@ */ #include "simd.inc" - -/* - ***************************************************************************** - ** UFUNC LOOPS ** - ***************************************************************************** - */ - -/* unary loop input and output contiguous */ -#define IS_UNARY_CONT(tin, tout) (steps[0] == sizeof(tin) && \ - steps[1] == sizeof(tout)) - -#define IS_BINARY_REDUCE ((args[0] == args[2])\ - && (steps[0] == steps[2])\ - && (steps[0] == 0)) - -/* binary loop input and output contiguous */ -#define IS_BINARY_CONT(tin, tout) (steps[0] == sizeof(tin) && \ - steps[1] == sizeof(tin) && \ - steps[2] == sizeof(tout)) -/* binary loop input and output contiguous with first scalar */ -#define IS_BINARY_CONT_S1(tin, tout) (steps[0] == 0 && \ - steps[1] == sizeof(tin) && \ - steps[2] == sizeof(tout)) -/* binary loop input and output contiguous with second scalar */ -#define IS_BINARY_CONT_S2(tin, tout) (steps[0] == sizeof(tin) && \ - steps[1] == 0 && \ - steps[2] == sizeof(tout)) - -#define OUTPUT_LOOP\ - char *op1 = args[1];\ - npy_intp os1 = steps[1];\ - npy_intp n = dimensions[0];\ - npy_intp i;\ - for(i = 0; i < n; i++, op1 += os1) - -#define UNARY_LOOP\ - char *ip1 = args[0], *op1 = args[1];\ - npy_intp is1 = steps[0], os1 = steps[1];\ - npy_intp n = dimensions[0];\ - npy_intp i;\ - for(i = 0; i < n; i++, ip1 += is1, op1 += os1) - -/* - * loop with contiguous specialization - * op should be the code working on `tin in` and - * storing the result in `tout * out` - * combine with NPY_GCC_OPT_3 to allow autovectorization - * should only be used where its worthwhile to avoid code bloat - */ -#define BASE_UNARY_LOOP(tin, tout, op) \ - UNARY_LOOP { \ - const tin in = *(tin *)ip1; \ - tout * out = (tout *)op1; \ - op; \ - } -#define UNARY_LOOP_FAST(tin, tout, op) \ - do { \ - /* condition allows compiler to optimize the generic macro */ \ - if (IS_UNARY_CONT(tin, tout)) { \ - if (args[0] == args[1]) { \ - BASE_UNARY_LOOP(tin, tout, op) \ - } \ - else { \ - BASE_UNARY_LOOP(tin, tout, op) \ - } \ - } \ - else { \ - BASE_UNARY_LOOP(tin, tout, op) \ - } \ - } \ - while (0) - -#define UNARY_LOOP_TWO_OUT\ - char *ip1 = args[0], *op1 = args[1], *op2 = args[2];\ - npy_intp is1 = steps[0], os1 = steps[1], os2 = steps[2];\ - npy_intp n = dimensions[0];\ - npy_intp i;\ - for(i = 0; i < n; i++, ip1 += is1, op1 += os1, op2 += os2) - -#define BINARY_LOOP\ - char *ip1 = args[0], *ip2 = args[1], *op1 = args[2];\ - npy_intp is1 = steps[0], is2 = steps[1], os1 = steps[2];\ - npy_intp n = dimensions[0];\ - npy_intp i;\ - for(i = 0; i < n; i++, ip1 += is1, ip2 += is2, op1 += os1) - -/* - * loop with contiguous specialization - * op should be the code working on `tin in1`, `tin in2` and - * storing the result in `tout * out` - * combine with NPY_GCC_OPT_3 to allow autovectorization - * should only be used where its worthwhile to avoid code bloat - */ -#define BASE_BINARY_LOOP(tin, tout, op) \ - BINARY_LOOP { \ - const tin in1 = *(tin *)ip1; \ - const tin in2 = *(tin *)ip2; \ - tout * out = (tout *)op1; \ - op; \ - } -/* - * unfortunately gcc 6/7 regressed and we need to give it additional hints to - * vectorize inplace operations (PR80198) - * must only be used after op1 == ip1 or ip2 has been checked - * TODO: using ivdep might allow other compilers to vectorize too - */ -#if __GNUC__ >= 6 -#define IVDEP_LOOP _Pragma("GCC ivdep") -#else -#define IVDEP_LOOP -#endif -#define BASE_BINARY_LOOP_INP(tin, tout, op) \ - char *ip1 = args[0], *ip2 = args[1], *op1 = args[2];\ - npy_intp is1 = steps[0], is2 = steps[1], os1 = steps[2];\ - npy_intp n = dimensions[0];\ - npy_intp i;\ - IVDEP_LOOP \ - for(i = 0; i < n; i++, ip1 += is1, ip2 += is2, op1 += os1) { \ - const tin in1 = *(tin *)ip1; \ - const tin in2 = *(tin *)ip2; \ - tout * out = (tout *)op1; \ - op; \ - } -#define BASE_BINARY_LOOP_S(tin, tout, cin, cinp, vin, vinp, op) \ - const tin cin = *(tin *)cinp; \ - BINARY_LOOP { \ - const tin vin = *(tin *)vinp; \ - tout * out = (tout *)op1; \ - op; \ - } -/* PR80198 again, scalar works without the pragma */ -#define BASE_BINARY_LOOP_S_INP(tin, tout, cin, cinp, vin, vinp, op) \ - const tin cin = *(tin *)cinp; \ - BINARY_LOOP { \ - const tin vin = *(tin *)vinp; \ - tout * out = (tout *)vinp; \ - op; \ - } -#define BINARY_LOOP_FAST(tin, tout, op) \ - do { \ - /* condition allows compiler to optimize the generic macro */ \ - if (IS_BINARY_CONT(tin, tout)) { \ - if (abs_ptrdiff(args[2], args[0]) == 0 && \ - abs_ptrdiff(args[2], args[1]) >= NPY_MAX_SIMD_SIZE) { \ - BASE_BINARY_LOOP_INP(tin, tout, op) \ - } \ - else if (abs_ptrdiff(args[2], args[1]) == 0 && \ - abs_ptrdiff(args[2], args[0]) >= NPY_MAX_SIMD_SIZE) { \ - BASE_BINARY_LOOP_INP(tin, tout, op) \ - } \ - else { \ - BASE_BINARY_LOOP(tin, tout, op) \ - } \ - } \ - else if (IS_BINARY_CONT_S1(tin, tout)) { \ - if (abs_ptrdiff(args[2], args[1]) == 0) { \ - BASE_BINARY_LOOP_S_INP(tin, tout, in1, args[0], in2, ip2, op) \ - } \ - else { \ - BASE_BINARY_LOOP_S(tin, tout, in1, args[0], in2, ip2, op) \ - } \ - } \ - else if (IS_BINARY_CONT_S2(tin, tout)) { \ - if (abs_ptrdiff(args[2], args[0]) == 0) { \ - BASE_BINARY_LOOP_S_INP(tin, tout, in2, args[1], in1, ip1, op) \ - } \ - else { \ - BASE_BINARY_LOOP_S(tin, tout, in2, args[1], in1, ip1, op) \ - }\ - } \ - else { \ - BASE_BINARY_LOOP(tin, tout, op) \ - } \ - } \ - while (0) - -#define BINARY_REDUCE_LOOP_INNER\ - char *ip2 = args[1]; \ - npy_intp is2 = steps[1]; \ - npy_intp n = dimensions[0]; \ - npy_intp i; \ - for(i = 0; i < n; i++, ip2 += is2) - -#define BINARY_REDUCE_LOOP(TYPE)\ - char *iop1 = args[0]; \ - TYPE io1 = *(TYPE *)iop1; \ - BINARY_REDUCE_LOOP_INNER - -#define BINARY_LOOP_TWO_OUT\ - char *ip1 = args[0], *ip2 = args[1], *op1 = args[2], *op2 = args[3];\ - npy_intp is1 = steps[0], is2 = steps[1], os1 = steps[2], os2 = steps[3];\ - npy_intp n = dimensions[0];\ - npy_intp i;\ - for(i = 0; i < n; i++, ip1 += is1, ip2 += is2, op1 += os1, op2 += os2) - /****************************************************************************** ** GENERIC FLOAT LOOPS ** *****************************************************************************/ +/* direct loops using a suitable callback */ -typedef float halfUnaryFunc(npy_half x); -typedef float floatUnaryFunc(float x); -typedef double doubleUnaryFunc(double x); -typedef npy_longdouble longdoubleUnaryFunc(npy_longdouble x); -typedef npy_half halfBinaryFunc(npy_half x, npy_half y); -typedef float floatBinaryFunc(float x, float y); -typedef double doubleBinaryFunc(double x, double y); -typedef npy_longdouble longdoubleBinaryFunc(npy_longdouble x, npy_longdouble y); - +/**begin repeat + * #c = e, f, d, g# + * #type = npy_half, npy_float, npy_double, npy_longdouble# + **/ /*UFUNC_API*/ NPY_NO_EXPORT void -PyUFunc_e_e(char **args, npy_intp *dimensions, npy_intp *steps, void *func) +PyUFunc_@c@_@c@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func) { - halfUnaryFunc *f = (halfUnaryFunc *)func; + typedef @type@ func_type(@type@); + func_type *f = (func_type *)func; UNARY_LOOP { - const npy_half in1 = *(npy_half *)ip1; - *(npy_half *)op1 = f(in1); + const @type@ in1 = *(@type@ *)ip1; + *(@type@ *)op1 = f(in1); } } /*UFUNC_API*/ NPY_NO_EXPORT void -PyUFunc_e_e_As_f_f(char **args, npy_intp *dimensions, npy_intp *steps, void *func) +PyUFunc_@c@@c@_@c@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func) { - floatUnaryFunc *f = (floatUnaryFunc *)func; - UNARY_LOOP { - const float in1 = npy_half_to_float(*(npy_half *)ip1); - *(npy_half *)op1 = npy_float_to_half(f(in1)); + typedef @type@ func_type(@type@, @type@); + func_type *f = (func_type *)func; + BINARY_LOOP { + @type@ in1 = *(@type@ *)ip1; + @type@ in2 = *(@type@ *)ip2; + *(@type@ *)op1 = f(in1, in2); } } -/*UFUNC_API*/ -NPY_NO_EXPORT void -PyUFunc_e_e_As_d_d(char **args, npy_intp *dimensions, npy_intp *steps, void *func) -{ - doubleUnaryFunc *f = (doubleUnaryFunc *)func; - UNARY_LOOP { - const double in1 = npy_half_to_double(*(npy_half *)ip1); - *(npy_half *)op1 = npy_double_to_half(f(in1)); - } -} +/**end repeat**/ -/*UFUNC_API*/ -NPY_NO_EXPORT void -PyUFunc_f_f(char **args, npy_intp *dimensions, npy_intp *steps, void *func) -{ - floatUnaryFunc *f = (floatUnaryFunc *)func; - UNARY_LOOP { - const float in1 = *(float *)ip1; - *(float *)op1 = f(in1); - } -} +/* indirect loops with casting */ +/**begin repeat + * #c1 = e, e, f# + * #type1 = npy_half, npy_half, npy_float# + * #c2 = f, d, d# + * #type2 = npy_float, npy_double, npy_double# + * + * #conv12 = npy_half_to_float, npy_half_to_double, (double)# + * #conv21 = npy_float_to_half, npy_double_to_half, (float)# + **/ /*UFUNC_API*/ NPY_NO_EXPORT void -PyUFunc_f_f_As_d_d(char **args, npy_intp *dimensions, npy_intp *steps, void *func) +PyUFunc_@c1@_@c1@_As_@c2@_@c2@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func) { - doubleUnaryFunc *f = (doubleUnaryFunc *)func; + typedef @type2@ func_type(@type2@); + func_type *f = (func_type *)func; UNARY_LOOP { - const float in1 = *(float *)ip1; - *(float *)op1 = (float)f((double)in1); - } -} - -/*UFUNC_API*/ -NPY_NO_EXPORT void -PyUFunc_ee_e(char **args, npy_intp *dimensions, npy_intp *steps, void *func) -{ - halfBinaryFunc *f = (halfBinaryFunc *)func; - BINARY_LOOP { - npy_half in1 = *(npy_half *)ip1; - npy_half in2 = *(npy_half *)ip2; - *(npy_half *)op1 = f(in1, in2); - } -} - -/*UFUNC_API*/ -NPY_NO_EXPORT void -PyUFunc_ee_e_As_ff_f(char **args, npy_intp *dimensions, npy_intp *steps, void *func) -{ - floatBinaryFunc *f = (floatBinaryFunc *)func; - BINARY_LOOP { - float in1 = npy_half_to_float(*(npy_half *)ip1); - float in2 = npy_half_to_float(*(npy_half *)ip2); - *(npy_half *)op1 = npy_float_to_half(f(in1, in2)); - } -} - -/*UFUNC_API*/ -NPY_NO_EXPORT void -PyUFunc_ee_e_As_dd_d(char **args, npy_intp *dimensions, npy_intp *steps, void *func) -{ - doubleBinaryFunc *f = (doubleBinaryFunc *)func; - BINARY_LOOP { - double in1 = npy_half_to_double(*(npy_half *)ip1); - double in2 = npy_half_to_double(*(npy_half *)ip2); - *(npy_half *)op1 = npy_double_to_half(f(in1, in2)); + const @type2@ in1 = @conv12@(*(@type1@ *)ip1); + *(@type1@ *)op1 = @conv21@(f(in1)); } } - /*UFUNC_API*/ NPY_NO_EXPORT void -PyUFunc_ff_f(char **args, npy_intp *dimensions, npy_intp *steps, void *func) +PyUFunc_@c1@@c1@_@c1@_As_@c2@@c2@_@c2@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func) { - floatBinaryFunc *f = (floatBinaryFunc *)func; + typedef @type2@ func_type(@type2@, @type2@); + func_type *f = (func_type *)func; BINARY_LOOP { - float in1 = *(float *)ip1; - float in2 = *(float *)ip2; - *(float *)op1 = f(in1, in2); + const @type2@ in1 = @conv12@(*(@type1@ *)ip1); + const @type2@ in2 = @conv12@(*(@type1@ *)ip2); + *(@type1@ *)op1 = @conv21@(f(in1, in2)); } } -/*UFUNC_API*/ -NPY_NO_EXPORT void -PyUFunc_ff_f_As_dd_d(char **args, npy_intp *dimensions, npy_intp *steps, void *func) -{ - doubleBinaryFunc *f = (doubleBinaryFunc *)func; - BINARY_LOOP { - float in1 = *(float *)ip1; - float in2 = *(float *)ip2; - *(float *)op1 = (double)f((double)in1, (double)in2); - } -} +/**end repeat**/ -/*UFUNC_API*/ -NPY_NO_EXPORT void -PyUFunc_d_d(char **args, npy_intp *dimensions, npy_intp *steps, void *func) -{ - doubleUnaryFunc *f = (doubleUnaryFunc *)func; - UNARY_LOOP { - double in1 = *(double *)ip1; - *(double *)op1 = f(in1); - } -} +/****************************************************************************** + ** GENERIC COMPLEX LOOPS ** + *****************************************************************************/ -/*UFUNC_API*/ -NPY_NO_EXPORT void -PyUFunc_dd_d(char **args, npy_intp *dimensions, npy_intp *steps, void *func) -{ - doubleBinaryFunc *f = (doubleBinaryFunc *)func; - BINARY_LOOP { - double in1 = *(double *)ip1; - double in2 = *(double *)ip2; - *(double *)op1 = f(in1, in2); - } -} +/* direct loops using a suitable callback */ +/**begin repeat + * #c = F, D, G# + * #type = npy_cfloat, npy_cdouble, npy_clongdouble# + **/ /*UFUNC_API*/ NPY_NO_EXPORT void -PyUFunc_g_g(char **args, npy_intp *dimensions, npy_intp *steps, void *func) +PyUFunc_@c@_@c@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func) { - longdoubleUnaryFunc *f = (longdoubleUnaryFunc *)func; + typedef void func_type(@type@ *, @type@ *); + func_type *f = (func_type *)func; UNARY_LOOP { - npy_longdouble in1 = *(npy_longdouble *)ip1; - *(npy_longdouble *)op1 = f(in1); + @type@ in1 = *(@type@ *)ip1; + @type@ *out = (@type@ *)op1; + f(&in1, out); } } /*UFUNC_API*/ NPY_NO_EXPORT void -PyUFunc_gg_g(char **args, npy_intp *dimensions, npy_intp *steps, void *func) +PyUFunc_@c@@c@_@c@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func) { - longdoubleBinaryFunc *f = (longdoubleBinaryFunc *)func; + typedef void func_type(@type@ *, @type@ *, @type@ *); + func_type *f = (func_type *)func; BINARY_LOOP { - npy_longdouble in1 = *(npy_longdouble *)ip1; - npy_longdouble in2 = *(npy_longdouble *)ip2; - *(npy_longdouble *)op1 = f(in1, in2); + @type@ in1 = *(@type@ *)ip1; + @type@ in2 = *(@type@ *)ip2; + @type@ *out = (@type@ *)op1; + f(&in1, &in2, out); } } +/**end repeat**/ - -/****************************************************************************** - ** GENERIC COMPLEX LOOPS ** - *****************************************************************************/ - - -typedef void cdoubleUnaryFunc(npy_cdouble *x, npy_cdouble *r); -typedef void cfloatUnaryFunc(npy_cfloat *x, npy_cfloat *r); -typedef void clongdoubleUnaryFunc(npy_clongdouble *x, npy_clongdouble *r); -typedef void cdoubleBinaryFunc(npy_cdouble *x, npy_cdouble *y, npy_cdouble *r); -typedef void cfloatBinaryFunc(npy_cfloat *x, npy_cfloat *y, npy_cfloat *r); -typedef void clongdoubleBinaryFunc(npy_clongdouble *x, npy_clongdouble *y, - npy_clongdouble *r); - -/*UFUNC_API*/ -NPY_NO_EXPORT void -PyUFunc_F_F(char **args, npy_intp *dimensions, npy_intp *steps, void *func) -{ - cfloatUnaryFunc *f = (cfloatUnaryFunc *)func; - UNARY_LOOP { - npy_cfloat in1 = *(npy_cfloat *)ip1; - npy_cfloat *out = (npy_cfloat *)op1; - f(&in1, out); - } -} - +/* indirect loops with casting */ /*UFUNC_API*/ NPY_NO_EXPORT void -PyUFunc_F_F_As_D_D(char **args, npy_intp *dimensions, npy_intp *steps, void *func) +PyUFunc_F_F_As_D_D(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func) { - cdoubleUnaryFunc *f = (cdoubleUnaryFunc *)func; + typedef void func_type(npy_cdouble *, npy_cdouble *); + func_type *f = (func_type *)func; UNARY_LOOP { npy_cdouble tmp, out; tmp.real = (double)((float *)ip1)[0]; @@ -463,22 +185,10 @@ PyUFunc_F_F_As_D_D(char **args, npy_intp *dimensions, npy_intp *steps, void *fun /*UFUNC_API*/ NPY_NO_EXPORT void -PyUFunc_FF_F(char **args, npy_intp *dimensions, npy_intp *steps, void *func) -{ - cfloatBinaryFunc *f = (cfloatBinaryFunc *)func; - BINARY_LOOP { - npy_cfloat in1 = *(npy_cfloat *)ip1; - npy_cfloat in2 = *(npy_cfloat *)ip2; - npy_cfloat *out = (npy_cfloat *)op1; - f(&in1, &in2, out); - } -} - -/*UFUNC_API*/ -NPY_NO_EXPORT void -PyUFunc_FF_F_As_DD_D(char **args, npy_intp *dimensions, npy_intp *steps, void *func) +PyUFunc_FF_F_As_DD_D(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func) { - cdoubleBinaryFunc *f = (cdoubleBinaryFunc *)func; + typedef void func_type(npy_cdouble *, npy_cdouble *, npy_cdouble *); + func_type *f = (func_type *)func; BINARY_LOOP { npy_cdouble tmp1, tmp2, out; tmp1.real = (double)((float *)ip1)[0]; @@ -491,56 +201,6 @@ PyUFunc_FF_F_As_DD_D(char **args, npy_intp *dimensions, npy_intp *steps, void *f } } -/*UFUNC_API*/ -NPY_NO_EXPORT void -PyUFunc_D_D(char **args, npy_intp *dimensions, npy_intp *steps, void *func) -{ - cdoubleUnaryFunc *f = (cdoubleUnaryFunc *)func; - UNARY_LOOP { - npy_cdouble in1 = *(npy_cdouble *)ip1; - npy_cdouble *out = (npy_cdouble *)op1; - f(&in1, out); - } -} - -/*UFUNC_API*/ -NPY_NO_EXPORT void -PyUFunc_DD_D(char **args, npy_intp *dimensions, npy_intp *steps, void *func) -{ - cdoubleBinaryFunc *f = (cdoubleBinaryFunc *)func; - BINARY_LOOP { - npy_cdouble in1 = *(npy_cdouble *)ip1; - npy_cdouble in2 = *(npy_cdouble *)ip2; - npy_cdouble *out = (npy_cdouble *)op1; - f(&in1, &in2, out); - } -} - -/*UFUNC_API*/ -NPY_NO_EXPORT void -PyUFunc_G_G(char **args, npy_intp *dimensions, npy_intp *steps, void *func) -{ - clongdoubleUnaryFunc *f = (clongdoubleUnaryFunc *)func; - UNARY_LOOP { - npy_clongdouble in1 = *(npy_clongdouble *)ip1; - npy_clongdouble *out = (npy_clongdouble *)op1; - f(&in1, out); - } -} - -/*UFUNC_API*/ -NPY_NO_EXPORT void -PyUFunc_GG_G(char **args, npy_intp *dimensions, npy_intp *steps, void *func) -{ - clongdoubleBinaryFunc *f = (clongdoubleBinaryFunc *)func; - BINARY_LOOP { - npy_clongdouble in1 = *(npy_clongdouble *)ip1; - npy_clongdouble in2 = *(npy_clongdouble *)ip2; - npy_clongdouble *out = (npy_clongdouble *)op1; - f(&in1, &in2, out); - } -} - /****************************************************************************** ** GENERIC OBJECT lOOPS ** @@ -548,7 +208,7 @@ PyUFunc_GG_G(char **args, npy_intp *dimensions, npy_intp *steps, void *func) /*UFUNC_API*/ NPY_NO_EXPORT void -PyUFunc_O_O(char **args, npy_intp *dimensions, npy_intp *steps, void *func) +PyUFunc_O_O(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func) { unaryfunc f = (unaryfunc)func; UNARY_LOOP { @@ -565,13 +225,32 @@ PyUFunc_O_O(char **args, npy_intp *dimensions, npy_intp *steps, void *func) /*UFUNC_API*/ NPY_NO_EXPORT void -PyUFunc_O_O_method(char **args, npy_intp *dimensions, npy_intp *steps, void *func) +PyUFunc_O_O_method(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func) { char *meth = (char *)func; UNARY_LOOP { PyObject *in1 = *(PyObject **)ip1; PyObject **out = (PyObject **)op1; - PyObject *ret = PyObject_CallMethod(in1 ? in1 : Py_None, meth, NULL); + PyObject *ret, *func; + func = PyObject_GetAttrString(in1 ? in1 : Py_None, meth); + if (func != NULL && !PyCallable_Check(func)) { + Py_DECREF(func); + func = NULL; + } + if (func == NULL) { + PyObject *exc, *val, *tb; + PyTypeObject *type = in1 ? Py_TYPE(in1) : Py_TYPE(Py_None); + PyErr_Fetch(&exc, &val, &tb); + PyErr_Format(PyExc_TypeError, + "loop of ufunc does not support argument %d of " + "type %s which has no callable %s method", + i, type->tp_name, meth); + npy_PyErr_ChainExceptionsCause(exc, val, tb); + Py_XDECREF(func); + return; + } + ret = PyObject_CallObject(func, NULL); + Py_DECREF(func); if (ret == NULL) { return; } @@ -582,7 +261,7 @@ PyUFunc_O_O_method(char **args, npy_intp *dimensions, npy_intp *steps, void *fun /*UFUNC_API*/ NPY_NO_EXPORT void -PyUFunc_OO_O(char **args, npy_intp *dimensions, npy_intp *steps, void *func) +PyUFunc_OO_O(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func) { binaryfunc f = (binaryfunc)func; BINARY_LOOP { @@ -598,9 +277,31 @@ PyUFunc_OO_O(char **args, npy_intp *dimensions, npy_intp *steps, void *func) } } +NPY_NO_EXPORT void +PyUFunc_OOO_O(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func) +{ + ternaryfunc f = (ternaryfunc)func; + TERNARY_LOOP { + PyObject *in1 = *(PyObject **)ip1; + PyObject *in2 = *(PyObject **)ip2; + PyObject *in3 = *(PyObject **)ip3; + PyObject **out = (PyObject **)op1; + PyObject *ret = f( + in1 ? in1 : Py_None, + in2 ? in2 : Py_None, + in3 ? in3 : Py_None + ); + if (ret == NULL) { + return; + } + Py_XDECREF(*out); + *out = ret; + } +} + /*UFUNC_API*/ NPY_NO_EXPORT void -PyUFunc_OO_O_method(char **args, npy_intp *dimensions, npy_intp *steps, void *func) +PyUFunc_OO_O_method(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func) { char *meth = (char *)func; BINARY_LOOP { @@ -624,7 +325,7 @@ PyUFunc_OO_O_method(char **args, npy_intp *dimensions, npy_intp *steps, void *fu /*UFUNC_API*/ NPY_NO_EXPORT void -PyUFunc_On_Om(char **args, npy_intp *dimensions, npy_intp *steps, void *func) +PyUFunc_On_Om(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func) { npy_intp n = dimensions[0]; PyUFunc_PyFuncData *data = (PyUFunc_PyFuncData *)func; @@ -654,7 +355,7 @@ PyUFunc_On_Om(char **args, npy_intp *dimensions, npy_intp *steps, void *func) PyTuple_SET_ITEM(arglist, j, in); Py_INCREF(in); } - result = PyEval_CallObject(tocall, arglist); + result = PyObject_CallObject(tocall, arglist); Py_DECREF(arglist); if (result == NULL) { return; @@ -705,7 +406,7 @@ PyUFunc_On_Om(char **args, npy_intp *dimensions, npy_intp *steps, void *func) **/ NPY_NO_EXPORT void -BOOL_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +BOOL_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { npy_bool in1 = *((npy_bool *)ip1) != 0; @@ -724,7 +425,7 @@ BOOL_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED **/ NPY_NO_EXPORT void -BOOL_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +BOOL_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { if(IS_BINARY_REDUCE) { #ifdef NPY_HAVE_SSE2_INTRINSICS @@ -794,7 +495,7 @@ BOOL_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED * #OP = !=, ==# **/ NPY_NO_EXPORT void -BOOL_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +BOOL_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { if (run_unary_simd_@kind@_BOOL(args, dimensions, steps)) { return; @@ -809,7 +510,7 @@ BOOL_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED /**end repeat**/ NPY_NO_EXPORT void -BOOL__ones_like(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data)) +BOOL__ones_like(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data)) { OUTPUT_LOOP { *((npy_bool *)op1) = 1; @@ -817,6 +518,23 @@ BOOL__ones_like(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UN } +/**begin repeat + * #kind = isnan, isinf, isfinite# + * #func = npy_isnan, npy_isinf, npy_isfinite# + * #val = NPY_FALSE, NPY_FALSE, NPY_TRUE# + **/ +NPY_NO_EXPORT void +BOOL_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) +{ + /* + * The (void)in; suppresses an unused variable warning raised by gcc and allows + * us to re-use this macro even though we do not depend on in + */ + UNARY_LOOP_FAST(npy_bool, npy_bool, (void)in; *out = @val@); +} + +/**end repeat**/ + /* ***************************************************************************** ** INTEGER LOOPS @@ -831,6 +549,7 @@ BOOL__ones_like(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UN * #ftype = npy_float, npy_float, npy_float, npy_float, npy_double, npy_double, * npy_double, npy_double, npy_double, npy_double# * #SIGNED = 1, 0, 1, 0, 1, 0, 1, 0, 1, 0# + * #c = hh,uhh,h,uh,,u,l,ul,ll,ull# */ #define @TYPE@_floor_divide @TYPE@_divide @@ -838,7 +557,7 @@ BOOL__ones_like(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UN #define @TYPE@_fmin @TYPE@_minimum NPY_NO_EXPORT void -@TYPE@__ones_like(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data)) +@TYPE@__ones_like(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data)) { OUTPUT_LOOP { *((@type@ *)op1) = 1; @@ -846,7 +565,7 @@ NPY_NO_EXPORT void } NPY_NO_EXPORT void -@TYPE@_positive(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_positive(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { UNARY_LOOP_FAST(@type@, @type@, *out = +in); } @@ -854,13 +573,13 @@ NPY_NO_EXPORT void /**begin repeat1 * #isa = , _avx2# * #ISA = , AVX2# - * #CHK = 1, HAVE_ATTRIBUTE_TARGET_AVX2# + * #CHK = 1, defined(HAVE_ATTRIBUTE_TARGET_AVX2)# * #ATTR = , NPY_GCC_TARGET_AVX2# */ #if @CHK@ NPY_NO_EXPORT NPY_GCC_OPT_3 @ATTR@ void -@TYPE@_square@isa@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data)) +@TYPE@_square@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data)) { UNARY_LOOP_FAST(@type@, @type@, *out = in * in); } @@ -868,7 +587,7 @@ NPY_NO_EXPORT NPY_GCC_OPT_3 @ATTR@ void #if @CHK@ NPY_NO_EXPORT NPY_GCC_OPT_3 @ATTR@ void -@TYPE@_reciprocal@isa@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data)) +@TYPE@_reciprocal@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data)) { UNARY_LOOP_FAST(@type@, @type@, *out = 1.0 / in); } @@ -876,7 +595,7 @@ NPY_NO_EXPORT NPY_GCC_OPT_3 @ATTR@ void #if @CHK@ NPY_NO_EXPORT NPY_GCC_OPT_3 @ATTR@ void -@TYPE@_conjugate@isa@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_conjugate@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { UNARY_LOOP_FAST(@type@, @type@, *out = in); } @@ -884,7 +603,7 @@ NPY_NO_EXPORT NPY_GCC_OPT_3 @ATTR@ void #if @CHK@ NPY_NO_EXPORT NPY_GCC_OPT_3 @ATTR@ void -@TYPE@_negative@isa@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_negative@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { UNARY_LOOP_FAST(@type@, @type@, *out = -in); } @@ -892,7 +611,7 @@ NPY_NO_EXPORT NPY_GCC_OPT_3 @ATTR@ void #if @CHK@ NPY_NO_EXPORT NPY_GCC_OPT_3 @ATTR@ void -@TYPE@_logical_not@isa@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_logical_not@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { UNARY_LOOP_FAST(@type@, npy_bool, *out = !in); } @@ -900,7 +619,7 @@ NPY_NO_EXPORT NPY_GCC_OPT_3 @ATTR@ void #if @CHK@ NPY_NO_EXPORT NPY_GCC_OPT_3 @ATTR@ void -@TYPE@_invert@isa@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_invert@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { UNARY_LOOP_FAST(@type@, @type@, *out = ~in); } @@ -908,16 +627,15 @@ NPY_NO_EXPORT NPY_GCC_OPT_3 @ATTR@ void /**begin repeat2 * Arithmetic - * #kind = add, subtract, multiply, bitwise_and, bitwise_or, bitwise_xor, - * left_shift, right_shift# - * #OP = +, -,*, &, |, ^, <<, >># + * #kind = add, subtract, multiply, bitwise_and, bitwise_or, bitwise_xor# + * #OP = +, -, *, &, |, ^# */ #if @CHK@ NPY_NO_EXPORT NPY_GCC_OPT_3 @ATTR@ void -@TYPE@_@kind@@isa@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_@kind@@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { - if(IS_BINARY_REDUCE) { + if (IS_BINARY_REDUCE) { BINARY_REDUCE_LOOP(@type@) { io1 @OP@= *(@type@ *)ip2; } @@ -931,6 +649,47 @@ NPY_NO_EXPORT NPY_GCC_OPT_3 @ATTR@ void /**end repeat2**/ +/* + * Arithmetic bit shift operations. + * + * Intel hardware masks bit shift values, so large shifts wrap around + * and can produce surprising results. The special handling ensures that + * behavior is independent of compiler or hardware. + * TODO: We could implement consistent behavior for negative shifts, + * which is undefined in C. + */ + +#define INT_left_shift_needs_clear_floatstatus +#define UINT_left_shift_needs_clear_floatstatus + +NPY_NO_EXPORT NPY_GCC_OPT_3 void +@TYPE@_left_shift@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)) +{ + BINARY_LOOP_FAST(@type@, @type@, *out = npy_lshift@c@(in1, in2)); + +#ifdef @TYPE@_left_shift_needs_clear_floatstatus + // For some reason, our macOS CI sets an "invalid" flag here, but only + // for some types. + npy_clear_floatstatus_barrier((char*)dimensions); +#endif +} + +#undef INT_left_shift_needs_clear_floatstatus +#undef UINT_left_shift_needs_clear_floatstatus + +NPY_NO_EXPORT +#ifndef NPY_DO_NOT_OPTIMIZE_@TYPE@_right_shift +NPY_GCC_OPT_3 +#endif +void +@TYPE@_right_shift@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)) +{ + BINARY_LOOP_FAST(@type@, @type@, *out = npy_rshift@c@(in1, in2)); +} + + /**begin repeat2 * #kind = equal, not_equal, greater, greater_equal, less, less_equal, * logical_and, logical_or# @@ -939,7 +698,7 @@ NPY_NO_EXPORT NPY_GCC_OPT_3 @ATTR@ void #if @CHK@ NPY_NO_EXPORT NPY_GCC_OPT_3 @ATTR@ void -@TYPE@_@kind@@isa@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_@kind@@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { /* * gcc vectorization of this is not good (PR60575) but manual integer @@ -953,7 +712,7 @@ NPY_NO_EXPORT NPY_GCC_OPT_3 @ATTR@ void #if @CHK@ NPY_NO_EXPORT NPY_GCC_OPT_3 @ATTR@ void -@TYPE@_logical_xor@isa@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_logical_xor@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const int t1 = !!*(@type@ *)ip1; @@ -971,7 +730,7 @@ NPY_NO_EXPORT NPY_GCC_OPT_3 @ATTR@ void **/ NPY_NO_EXPORT void -@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { if (IS_BINARY_REDUCE) { BINARY_REDUCE_LOOP(@type@) { @@ -992,7 +751,7 @@ NPY_NO_EXPORT void /**end repeat1**/ NPY_NO_EXPORT void -@TYPE@_power(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_power(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { @type@ in1 = *(@type@ *)ip1; @@ -1032,7 +791,7 @@ NPY_NO_EXPORT void } NPY_NO_EXPORT void -@TYPE@_fmod(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_fmod(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const @type@ in1 = *(@type@ *)ip1; @@ -1048,6 +807,22 @@ NPY_NO_EXPORT void } } +/**begin repeat1 + * #kind = isnan, isinf, isfinite# + * #func = npy_isnan, npy_isinf, npy_isfinite# + * #val = NPY_FALSE, NPY_FALSE, NPY_TRUE# + **/ +NPY_NO_EXPORT void +@TYPE@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) +{ + /* + * The (void)in; suppresses an unused variable warning raised by gcc and allows + * us to re-use this macro even though we do not depend on in + */ + UNARY_LOOP_FAST(@type@, npy_bool, (void)in; *out = @val@); +} +/**end repeat1**/ + /**end repeat**/ /**begin repeat @@ -1057,45 +832,19 @@ NPY_NO_EXPORT void */ NPY_NO_EXPORT NPY_GCC_OPT_3 void -@TYPE@_absolute(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_absolute(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { UNARY_LOOP_FAST(@type@, @type@, *out = (in >= 0) ? in : -in); } NPY_NO_EXPORT NPY_GCC_OPT_3 void -@TYPE@_sign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_sign(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { UNARY_LOOP_FAST(@type@, @type@, *out = in > 0 ? 1 : (in < 0 ? -1 : 0)); } NPY_NO_EXPORT void -@TYPE@_divide(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) -{ - BINARY_LOOP { - const @type@ in1 = *(@type@ *)ip1; - const @type@ in2 = *(@type@ *)ip2; - /* - * FIXME: On x86 at least, dividing the smallest representable integer - * by -1 causes a SIFGPE (division overflow). We treat this case here - * (to avoid a SIGFPE crash at python level), but a good solution would - * be to treat integer division problems separately from FPU exceptions - * (i.e. a different approach than npy_set_floatstatus_divbyzero()). - */ - if (in2 == 0 || (in1 == NPY_MIN_@TYPE@ && in2 == -1)) { - npy_set_floatstatus_divbyzero(); - *((@type@ *)op1) = 0; - } - else if (((in1 > 0) != (in2 > 0)) && (in1 % in2 != 0)) { - *((@type@ *)op1) = in1/in2 - 1; - } - else { - *((@type@ *)op1) = in1/in2; - } - } -} - -NPY_NO_EXPORT void -@TYPE@_remainder(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_remainder(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const @type@ in1 = *(@type@ *)ip1; @@ -1118,7 +867,7 @@ NPY_NO_EXPORT void } NPY_NO_EXPORT void -@TYPE@_divmod(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_divmod(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP_TWO_OUT { const @type@ in1 = *(@type@ *)ip1; @@ -1149,7 +898,7 @@ NPY_NO_EXPORT void * #kind = gcd, lcm# **/ NPY_NO_EXPORT void -@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const @type@ in1 = *(@type@ *)ip1; @@ -1167,39 +916,20 @@ NPY_NO_EXPORT void * #c = u,u,u,ul,ull# */ -NPY_NO_EXPORT void -@TYPE@_absolute(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +NPY_NO_EXPORT NPY_GCC_OPT_3 void +@TYPE@_absolute(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { - UNARY_LOOP { - const @type@ in1 = *(@type@ *)ip1; - *((@type@ *)op1) = in1; - } + UNARY_LOOP_FAST(@type@, @type@, *out = in); } NPY_NO_EXPORT NPY_GCC_OPT_3 void -@TYPE@_sign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_sign(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { UNARY_LOOP_FAST(@type@, @type@, *out = in > 0 ? 1 : 0); } NPY_NO_EXPORT void -@TYPE@_divide(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) -{ - BINARY_LOOP { - const @type@ in1 = *(@type@ *)ip1; - const @type@ in2 = *(@type@ *)ip2; - if (in2 == 0) { - npy_set_floatstatus_divbyzero(); - *((@type@ *)op1) = 0; - } - else { - *((@type@ *)op1)= in1/in2; - } - } -} - -NPY_NO_EXPORT void -@TYPE@_remainder(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_remainder(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const @type@ in1 = *(@type@ *)ip1; @@ -1215,7 +945,7 @@ NPY_NO_EXPORT void } NPY_NO_EXPORT void -@TYPE@_divmod(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_divmod(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP_TWO_OUT { const @type@ in1 = *(@type@ *)ip1; @@ -1236,7 +966,7 @@ NPY_NO_EXPORT void * #kind = gcd, lcm# **/ NPY_NO_EXPORT void -@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const @type@ in1 = *(@type@ *)ip1; @@ -1255,7 +985,7 @@ NPY_NO_EXPORT void */ NPY_NO_EXPORT void -TIMEDELTA_negative(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +TIMEDELTA_negative(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { UNARY_LOOP { const npy_timedelta in1 = *(npy_timedelta *)ip1; @@ -1269,7 +999,7 @@ TIMEDELTA_negative(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY } NPY_NO_EXPORT void -TIMEDELTA_positive(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +TIMEDELTA_positive(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { UNARY_LOOP { const npy_timedelta in1 = *(npy_timedelta *)ip1; @@ -1278,7 +1008,7 @@ TIMEDELTA_positive(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY } NPY_NO_EXPORT void -TIMEDELTA_absolute(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +TIMEDELTA_absolute(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { UNARY_LOOP { const npy_timedelta in1 = *(npy_timedelta *)ip1; @@ -1292,7 +1022,7 @@ TIMEDELTA_absolute(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY } NPY_NO_EXPORT void -TIMEDELTA_sign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +TIMEDELTA_sign(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { UNARY_LOOP { const npy_timedelta in1 = *(npy_timedelta *)ip1; @@ -1306,7 +1036,7 @@ TIMEDELTA_sign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNU */ NPY_NO_EXPORT void -@TYPE@_isnat(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_isnat(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { UNARY_LOOP { const @type@ in1 = *(@type@ *)ip1; @@ -1315,7 +1045,22 @@ NPY_NO_EXPORT void } NPY_NO_EXPORT void -@TYPE@__ones_like(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data)) +@TYPE@_isfinite(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) +{ + UNARY_LOOP { + const @type@ in1 = *(@type@ *)ip1; + *((npy_bool *)op1) = (in1 != NPY_DATETIME_NAT); + } +} + +NPY_NO_EXPORT void +@TYPE@_isinf(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) +{ + UNARY_LOOP_FAST(npy_bool, npy_bool, (void)in; *out = NPY_FALSE); +} + +NPY_NO_EXPORT void +@TYPE@__ones_like(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data)) { OUTPUT_LOOP { *((@type@ *)op1) = 1; @@ -1327,56 +1072,27 @@ NPY_NO_EXPORT void * #OP = ==, >, >=, <, <=# */ NPY_NO_EXPORT void -@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { - npy_bool give_future_warning = 0; BINARY_LOOP { const @type@ in1 = *(@type@ *)ip1; const @type@ in2 = *(@type@ *)ip2; - const npy_bool res = in1 @OP@ in2; - *((npy_bool *)op1) = res; - - if ((in1 == NPY_DATETIME_NAT || in2 == NPY_DATETIME_NAT) && res) { - give_future_warning = 1; - } - } - if (give_future_warning) { - NPY_ALLOW_C_API_DEF - NPY_ALLOW_C_API; - /* 2016-01-18, 1.11 */ - if (DEPRECATE_FUTUREWARNING( - "In the future, 'NAT @OP@ x' and 'x @OP@ NAT' " - "will always be False.") < 0) { - /* nothing to do, we return anyway */ - } - NPY_DISABLE_C_API; + *((npy_bool *)op1) = (in1 @OP@ in2 && + in1 != NPY_DATETIME_NAT && + in2 != NPY_DATETIME_NAT); } } /**end repeat1**/ NPY_NO_EXPORT void -@TYPE@_not_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_not_equal(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { - npy_bool give_future_warning = 0; BINARY_LOOP { const @type@ in1 = *(@type@ *)ip1; const @type@ in2 = *(@type@ *)ip2; - *((npy_bool *)op1) = in1 != in2; - - if (in1 == NPY_DATETIME_NAT && in2 == NPY_DATETIME_NAT) { - give_future_warning = 1; - } - } - if (give_future_warning) { - NPY_ALLOW_C_API_DEF - NPY_ALLOW_C_API; - /* 2016-01-18, 1.11 */ - if (DEPRECATE_FUTUREWARNING( - "In the future, NAT != NAT will be True " - "rather than False.") < 0) { - /* nothing to do, we return anyway */ - } - NPY_DISABLE_C_API; + *((npy_bool *)op1) = (in1 != in2 || + in1 == NPY_DATETIME_NAT || + in2 == NPY_DATETIME_NAT); } } @@ -1386,7 +1102,30 @@ NPY_NO_EXPORT void * #OP = >, <# **/ NPY_NO_EXPORT void -@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) +{ + BINARY_LOOP { + const @type@ in1 = *(@type@ *)ip1; + const @type@ in2 = *(@type@ *)ip2; + if (in1 == NPY_DATETIME_NAT) { + *((@type@ *)op1) = in1; + } + else if (in2 == NPY_DATETIME_NAT) { + *((@type@ *)op1) = in2; + } + else { + *((@type@ *)op1) = (in1 @OP@ in2) ? in1 : in2; + } + } +} +/**end repeat1**/ + +/**begin repeat1 + * #kind = fmax, fmin# + * #OP = >=, <=# + **/ +NPY_NO_EXPORT void +@TYPE@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const @type@ in1 = *(@type@ *)ip1; @@ -1398,7 +1137,7 @@ NPY_NO_EXPORT void *((@type@ *)op1) = in1; } else { - *((@type@ *)op1) = (in1 @OP@ in2) ? in1 : in2; + *((@type@ *)op1) = in1 @OP@ in2 ? in1 : in2; } } } @@ -1407,7 +1146,7 @@ NPY_NO_EXPORT void /**end repeat**/ NPY_NO_EXPORT void -DATETIME_Mm_M_add(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data)) +DATETIME_Mm_M_add(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data)) { BINARY_LOOP { const npy_datetime in1 = *(npy_datetime *)ip1; @@ -1422,7 +1161,7 @@ DATETIME_Mm_M_add(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_ } NPY_NO_EXPORT void -DATETIME_mM_M_add(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +DATETIME_mM_M_add(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const npy_timedelta in1 = *(npy_timedelta *)ip1; @@ -1437,7 +1176,7 @@ DATETIME_mM_M_add(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_ } NPY_NO_EXPORT void -TIMEDELTA_mm_m_add(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +TIMEDELTA_mm_m_add(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const npy_timedelta in1 = *(npy_timedelta *)ip1; @@ -1452,7 +1191,7 @@ TIMEDELTA_mm_m_add(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY } NPY_NO_EXPORT void -DATETIME_Mm_M_subtract(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +DATETIME_Mm_M_subtract(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const npy_datetime in1 = *(npy_datetime *)ip1; @@ -1467,7 +1206,7 @@ DATETIME_Mm_M_subtract(char **args, npy_intp *dimensions, npy_intp *steps, void } NPY_NO_EXPORT void -DATETIME_MM_m_subtract(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +DATETIME_MM_m_subtract(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const npy_datetime in1 = *(npy_datetime *)ip1; @@ -1482,7 +1221,7 @@ DATETIME_MM_m_subtract(char **args, npy_intp *dimensions, npy_intp *steps, void } NPY_NO_EXPORT void -TIMEDELTA_mm_m_subtract(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +TIMEDELTA_mm_m_subtract(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const npy_timedelta in1 = *(npy_timedelta *)ip1; @@ -1498,7 +1237,7 @@ TIMEDELTA_mm_m_subtract(char **args, npy_intp *dimensions, npy_intp *steps, void /* Note: Assuming 'q' == NPY_LONGLONG */ NPY_NO_EXPORT void -TIMEDELTA_mq_m_multiply(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +TIMEDELTA_mq_m_multiply(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const npy_timedelta in1 = *(npy_timedelta *)ip1; @@ -1514,7 +1253,7 @@ TIMEDELTA_mq_m_multiply(char **args, npy_intp *dimensions, npy_intp *steps, void /* Note: Assuming 'q' == NPY_LONGLONG */ NPY_NO_EXPORT void -TIMEDELTA_qm_m_multiply(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +TIMEDELTA_qm_m_multiply(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const npy_int64 in1 = *(npy_int64 *)ip1; @@ -1529,7 +1268,7 @@ TIMEDELTA_qm_m_multiply(char **args, npy_intp *dimensions, npy_intp *steps, void } NPY_NO_EXPORT void -TIMEDELTA_md_m_multiply(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +TIMEDELTA_md_m_multiply(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const npy_timedelta in1 = *(npy_timedelta *)ip1; @@ -1550,7 +1289,7 @@ TIMEDELTA_md_m_multiply(char **args, npy_intp *dimensions, npy_intp *steps, void } NPY_NO_EXPORT void -TIMEDELTA_dm_m_multiply(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +TIMEDELTA_dm_m_multiply(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const double in1 = *(double *)ip1; @@ -1572,22 +1311,56 @@ TIMEDELTA_dm_m_multiply(char **args, npy_intp *dimensions, npy_intp *steps, void /* Note: Assuming 'q' == NPY_LONGLONG */ NPY_NO_EXPORT void -TIMEDELTA_mq_m_divide(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +TIMEDELTA_mq_m_divide(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { - BINARY_LOOP { - const npy_timedelta in1 = *(npy_timedelta *)ip1; + /* NOTE: This code is similar to array floor divide */ + BINARY_DEFS + + /* When the divisor is a constant, use libdivide for faster division */ + if (steps[1] == 0) { + /* In case of empty array, just return */ + if (n == 0) { + return; + } + const npy_int64 in2 = *(npy_int64 *)ip2; - if (in1 == NPY_DATETIME_NAT || in2 == 0) { - *((npy_timedelta *)op1) = NPY_DATETIME_NAT; + + /* If divisor is 0, we need not compute anything */ + if (in2 == 0) { + npy_set_floatstatus_divbyzero(); + BINARY_LOOP_SLIDING { + *((npy_timedelta *)op1) = NPY_DATETIME_NAT; + } } else { - *((npy_timedelta *)op1) = in1 / in2; + struct libdivide_s64_t fast_d = libdivide_s64_gen(in2); + BINARY_LOOP_SLIDING { + const npy_timedelta in1 = *(npy_timedelta *)ip1; + if (in1 == NPY_DATETIME_NAT) { + *((npy_timedelta *)op1) = NPY_DATETIME_NAT; + } + else { + *((npy_timedelta *)op1) = libdivide_s64_do(in1, &fast_d); + } + } + } + } + else { + BINARY_LOOP_SLIDING { + const npy_timedelta in1 = *(npy_timedelta *)ip1; + const npy_int64 in2 = *(npy_int64 *)ip2; + if (in1 == NPY_DATETIME_NAT || in2 == 0) { + *((npy_timedelta *)op1) = NPY_DATETIME_NAT; + } + else { + *((npy_timedelta *)op1) = in1 / in2; + } } } } NPY_NO_EXPORT void -TIMEDELTA_md_m_divide(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +TIMEDELTA_md_m_divide(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const npy_timedelta in1 = *(npy_timedelta *)ip1; @@ -1608,7 +1381,7 @@ TIMEDELTA_md_m_divide(char **args, npy_intp *dimensions, npy_intp *steps, void * } NPY_NO_EXPORT void -TIMEDELTA_mm_d_divide(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +TIMEDELTA_mm_d_divide(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const npy_timedelta in1 = *(npy_timedelta *)ip1; @@ -1622,6 +1395,136 @@ TIMEDELTA_mm_d_divide(char **args, npy_intp *dimensions, npy_intp *steps, void * } } +NPY_NO_EXPORT void +TIMEDELTA_mm_m_remainder(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) +{ + BINARY_LOOP { + const npy_timedelta in1 = *(npy_timedelta *)ip1; + const npy_timedelta in2 = *(npy_timedelta *)ip2; + if (in1 == NPY_DATETIME_NAT || in2 == NPY_DATETIME_NAT) { + *((npy_timedelta *)op1) = NPY_DATETIME_NAT; + } + else { + if (in2 == 0) { + npy_set_floatstatus_divbyzero(); + *((npy_timedelta *)op1) = NPY_DATETIME_NAT; + } + else { + /* handle mixed case the way Python does */ + const npy_timedelta rem = in1 % in2; + if ((in1 > 0) == (in2 > 0) || rem == 0) { + *((npy_timedelta *)op1) = rem; + } + else { + *((npy_timedelta *)op1) = rem + in2; + } + } + } + } +} + +NPY_NO_EXPORT void +TIMEDELTA_mm_q_floor_divide(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) +{ + /* NOTE: This code is similar to array floor divide */ + BINARY_DEFS + + /* When the divisor is a constant, use libdivide for faster division */ + if (steps[1] == 0) { + /* In case of empty array, just return */ + if (n == 0) { + return; + } + + const npy_timedelta in2 = *(npy_timedelta *)ip2; + + /* If divisor is 0 or NAT, we need not compute anything */ + if (in2 == 0) { + npy_set_floatstatus_divbyzero(); + BINARY_LOOP_SLIDING { + *((npy_int64 *)op1) = 0; + } + } + else if (in2 == NPY_DATETIME_NAT) { + npy_set_floatstatus_invalid(); + BINARY_LOOP_SLIDING { + *((npy_int64 *)op1) = 0; + } + } + else { + struct libdivide_s64_t fast_d = libdivide_s64_gen(in2); + BINARY_LOOP_SLIDING { + const npy_timedelta in1 = *(npy_timedelta *)ip1; + if (in1 == NPY_DATETIME_NAT) { + npy_set_floatstatus_invalid(); + *((npy_int64 *)op1) = 0; + } + else { + *((npy_int64 *)op1) = libdivide_s64_do(in1, &fast_d); + + /* Negative quotients needs to be rounded down */ + if (((in1 > 0) != (in2 > 0)) && (*((npy_int64 *)op1) * in2 != in1)) { + *((npy_int64 *)op1) = *((npy_int64 *)op1) - 1; + } + } + } + } + } + else { + BINARY_LOOP_SLIDING { + const npy_timedelta in1 = *(npy_timedelta *)ip1; + const npy_timedelta in2 = *(npy_timedelta *)ip2; + if (in1 == NPY_DATETIME_NAT || in2 == NPY_DATETIME_NAT) { + npy_set_floatstatus_invalid(); + *((npy_int64 *)op1) = 0; + } + else if (in2 == 0) { + npy_set_floatstatus_divbyzero(); + *((npy_int64 *)op1) = 0; + } + else { + *((npy_int64 *)op1) = in1/in2; + + /* Negative quotients needs to be rounded down */ + if (((in1 > 0) != (in2 > 0)) && (*((npy_int64 *)op1) * in2 != in1)) { + *((npy_int64 *)op1) = *((npy_int64 *)op1) - 1; + } + } + } + } +} + +NPY_NO_EXPORT void +TIMEDELTA_mm_qm_divmod(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) +{ + BINARY_LOOP_TWO_OUT { + const npy_timedelta in1 = *(npy_timedelta *)ip1; + const npy_timedelta in2 = *(npy_timedelta *)ip2; + if (in1 == NPY_DATETIME_NAT || in2 == NPY_DATETIME_NAT) { + npy_set_floatstatus_invalid(); + *((npy_int64 *)op1) = 0; + *((npy_timedelta *)op2) = NPY_DATETIME_NAT; + } + else if (in2 == 0) { + npy_set_floatstatus_divbyzero(); + *((npy_int64 *)op1) = 0; + *((npy_timedelta *)op2) = NPY_DATETIME_NAT; + } + else { + const npy_int64 quo = in1 / in2; + const npy_timedelta rem = in1 % in2; + if ((in1 > 0) == (in2 > 0) || rem == 0) { + *((npy_int64 *)op1) = quo; + *((npy_timedelta *)op2) = rem; + } + else { + *((npy_int64 *)op1) = quo - 1; + *((npy_timedelta *)op2) = rem + in2; + } + } + } +} + /* ***************************************************************************** ** FLOAT LOOPS ** @@ -1629,104 +1532,59 @@ TIMEDELTA_mm_d_divide(char **args, npy_intp *dimensions, npy_intp *steps, void * */ /**begin repeat - * Float types - * #type = npy_float, npy_double# - * #TYPE = FLOAT, DOUBLE# - * #scalarf = npy_sqrtf, npy_sqrt# + * #func = rint, floor, trunc# + * #scalarf = npy_rint, npy_floor, npy_trunc# */ -NPY_NO_EXPORT void -@TYPE@_sqrt(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +/**begin repeat1 +* #TYPE = FLOAT, DOUBLE# +* #type = npy_float, npy_double# +* #typesub = f, # +*/ + +NPY_NO_EXPORT NPY_GCC_OPT_3 void +@TYPE@_@func@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data)) { - if (!run_unary_simd_sqrt_@TYPE@(args, dimensions, steps)) { - UNARY_LOOP { - const @type@ in1 = *(@type@ *)ip1; - *(@type@ *)op1 = @scalarf@(in1); - } + UNARY_LOOP { + const @type@ in1 = *(@type@ *)ip1; + *(@type@ *)op1 = @scalarf@@typesub@(in1); } } -/**end repeat**/ +/**end repeat1**/ +/**end repeat**/ /**begin repeat - * Float types - * #type = npy_float, npy_double, npy_longdouble, npy_float# - * #dtype = npy_float, npy_double, npy_longdouble, npy_half# - * #TYPE = FLOAT, DOUBLE, LONGDOUBLE, HALF# - * #c = f, , l, # - * #C = F, , L, # - * #trf = , , , npy_half_to_float# + * #isa = avx512f, fma# + * #ISA = AVX512F, FMA# + * #CHK = HAVE_ATTRIBUTE_TARGET_AVX512F_WITH_INTRINSICS, HAVE_ATTRIBUTE_TARGET_AVX2_WITH_INTRINSICS# */ -/* - * Pairwise summation, rounding error O(lg n) instead of O(n). - * The recursion depth is O(lg n) as well. - * when updating also update similar complex floats summation +/**begin repeat1 + * #TYPE = FLOAT, DOUBLE# + * #type = npy_float, npy_double# + * #typesub = f, # */ -static @type@ -pairwise_sum_@TYPE@(char *a, npy_intp n, npy_intp stride) -{ - if (n < 8) { - npy_intp i; - @type@ res = 0.; - - for (i = 0; i < n; i++) { - res += @trf@(*((@dtype@*)(a + i * stride))); - } - return res; - } - else if (n <= PW_BLOCKSIZE) { - npy_intp i; - @type@ r[8], res; - /* - * sum a block with 8 accumulators - * 8 times unroll reduces blocksize to 16 and allows vectorization with - * avx without changing summation ordering - */ - r[0] = @trf@(*((@dtype@ *)(a + 0 * stride))); - r[1] = @trf@(*((@dtype@ *)(a + 1 * stride))); - r[2] = @trf@(*((@dtype@ *)(a + 2 * stride))); - r[3] = @trf@(*((@dtype@ *)(a + 3 * stride))); - r[4] = @trf@(*((@dtype@ *)(a + 4 * stride))); - r[5] = @trf@(*((@dtype@ *)(a + 5 * stride))); - r[6] = @trf@(*((@dtype@ *)(a + 6 * stride))); - r[7] = @trf@(*((@dtype@ *)(a + 7 * stride))); - - for (i = 8; i < n - (n % 8); i += 8) { - /* small blocksizes seems to mess with hardware prefetch */ - NPY_PREFETCH(a + (i + 512/(npy_intp)sizeof(@dtype@))*stride, 0, 3); - r[0] += @trf@(*((@dtype@ *)(a + (i + 0) * stride))); - r[1] += @trf@(*((@dtype@ *)(a + (i + 1) * stride))); - r[2] += @trf@(*((@dtype@ *)(a + (i + 2) * stride))); - r[3] += @trf@(*((@dtype@ *)(a + (i + 3) * stride))); - r[4] += @trf@(*((@dtype@ *)(a + (i + 4) * stride))); - r[5] += @trf@(*((@dtype@ *)(a + (i + 5) * stride))); - r[6] += @trf@(*((@dtype@ *)(a + (i + 6) * stride))); - r[7] += @trf@(*((@dtype@ *)(a + (i + 7) * stride))); - } - - /* accumulate now to avoid stack spills for single peel loop */ - res = ((r[0] + r[1]) + (r[2] + r[3])) + - ((r[4] + r[5]) + (r[6] + r[7])); - - /* do non multiple of 8 rest */ - for (; i < n; i++) { - res += @trf@(*((@dtype@ *)(a + i * stride))); - } - return res; - } - else { - /* divide by two but avoid non-multiples of unroll factor */ - npy_intp n2 = n / 2; +/**begin repeat2 + * #func = rint, floor, trunc# + * #scalarf = npy_rint, npy_floor, npy_trunc# + */ - n2 -= n2 % 8; - return pairwise_sum_@TYPE@(a, n2, stride) + - pairwise_sum_@TYPE@(a + n2 * stride, n - n2, stride); +NPY_NO_EXPORT NPY_GCC_OPT_3 void +@TYPE@_@func@_@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data)) +{ + if (!run_unary_@isa@_@func@_@TYPE@(args, dimensions, steps)) { + UNARY_LOOP { + const @type@ in1 = *(@type@ *)ip1; + *(@type@ *)op1 = @scalarf@@typesub@(in1); + } } } +/**end repeat2**/ +/**end repeat1**/ /**end repeat**/ /**begin repeat @@ -1736,46 +1594,13 @@ pairwise_sum_@TYPE@(char *a, npy_intp n, npy_intp stride) * #c = f, , l# * #C = F, , L# */ - -/**begin repeat1 - * Arithmetic - * # kind = add, subtract, multiply, divide# - * # OP = +, -, *, /# - * # PW = 1, 0, 0, 0# - */ -NPY_NO_EXPORT void -@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) -{ - if (IS_BINARY_REDUCE) { -#if @PW@ - @type@ * iop1 = (@type@ *)args[0]; - npy_intp n = dimensions[0]; - - *iop1 @OP@= pairwise_sum_@TYPE@(args[1], n, steps[1]); -#else - BINARY_REDUCE_LOOP(@type@) { - io1 @OP@= *(@type@ *)ip2; - } - *((@type@ *)iop1) = io1; -#endif - } - else if (!run_binary_simd_@kind@_@TYPE@(args, dimensions, steps)) { - BINARY_LOOP { - const @type@ in1 = *(@type@ *)ip1; - const @type@ in2 = *(@type@ *)ip2; - *((@type@ *)op1) = in1 @OP@ in2; - } - } -} -/**end repeat1**/ - /**begin repeat1 * #kind = equal, not_equal, less, less_equal, greater, greater_equal, * logical_and, logical_or# * #OP = ==, !=, <, <=, >, >=, &&, ||# */ NPY_NO_EXPORT void -@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { if (!run_binary_simd_@kind@_@TYPE@(args, dimensions, steps)) { BINARY_LOOP { @@ -1784,11 +1609,12 @@ NPY_NO_EXPORT void *((npy_bool *)op1) = in1 @OP@ in2; } } + npy_clear_floatstatus_barrier((char*)dimensions); } /**end repeat1**/ NPY_NO_EXPORT void -@TYPE@_logical_xor(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_logical_xor(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const int t1 = !!*(@type@ *)ip1; @@ -1798,7 +1624,7 @@ NPY_NO_EXPORT void } NPY_NO_EXPORT void -@TYPE@_logical_not(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_logical_not(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { UNARY_LOOP { const @type@ in1 = *(@type@ *)ip1; @@ -1810,10 +1636,15 @@ NPY_NO_EXPORT void * #kind = isnan, isinf, isfinite, signbit# * #func = npy_isnan, npy_isinf, npy_isfinite, npy_signbit# **/ + +/**begin repeat2 + * #ISA = , _avx512_skx# + * #isa = simd, avx512_skx# + **/ NPY_NO_EXPORT void -@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_@kind@@ISA@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { - if (!run_@kind@_simd_@TYPE@(args, dimensions, steps)) { + if (!run_@kind@_@isa@_@TYPE@(args, dimensions, steps)) { UNARY_LOOP { const @type@ in1 = *(@type@ *)ip1; *((npy_bool *)op1) = @func@(in1) != 0; @@ -1821,10 +1652,11 @@ NPY_NO_EXPORT void } npy_clear_floatstatus_barrier((char*)dimensions); } +/**end repeat2**/ /**end repeat1**/ NPY_NO_EXPORT void -@TYPE@_spacing(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_spacing(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { UNARY_LOOP { const @type@ in1 = *(@type@ *)ip1; @@ -1833,7 +1665,7 @@ NPY_NO_EXPORT void } NPY_NO_EXPORT void -@TYPE@_copysign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_copysign(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const @type@ in1 = *(@type@ *)ip1; @@ -1843,7 +1675,7 @@ NPY_NO_EXPORT void } NPY_NO_EXPORT void -@TYPE@_nextafter(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_nextafter(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const @type@ in1 = *(@type@ *)ip1; @@ -1857,28 +1689,57 @@ NPY_NO_EXPORT void * #OP = >=, <=# **/ NPY_NO_EXPORT void -@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_@kind@_avx512f(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { /* */ if (IS_BINARY_REDUCE) { if (!run_unary_reduce_simd_@kind@_@TYPE@(args, dimensions, steps)) { BINARY_REDUCE_LOOP(@type@) { const @type@ in2 = *(@type@ *)ip2; + /* Order of operations important for MSVC 2015 */ io1 = (io1 @OP@ in2 || npy_isnan(io1)) ? io1 : in2; } - if (npy_isnan(io1)) { - npy_set_floatstatus_invalid(); + *((@type@ *)iop1) = io1; + } + } + else { + if (!run_binary_avx512f_@kind@_@TYPE@(args, dimensions, steps)) { + BINARY_LOOP { + @type@ in1 = *(@type@ *)ip1; + const @type@ in2 = *(@type@ *)ip2; + /* Order of operations important for MSVC 2015 */ + in1 = (in1 @OP@ in2 || npy_isnan(in1)) ? in1 : in2; + *((@type@ *)op1) = in1; + } + } + } + npy_clear_floatstatus_barrier((char*)dimensions); +} + +NPY_NO_EXPORT void +@TYPE@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) +{ + /* */ + if (IS_BINARY_REDUCE) { + if (!run_unary_reduce_simd_@kind@_@TYPE@(args, dimensions, steps)) { + BINARY_REDUCE_LOOP(@type@) { + const @type@ in2 = *(@type@ *)ip2; + /* Order of operations important for MSVC 2015 */ + io1 = (io1 @OP@ in2 || npy_isnan(io1)) ? io1 : in2; } *((@type@ *)iop1) = io1; } } else { BINARY_LOOP { - const @type@ in1 = *(@type@ *)ip1; + @type@ in1 = *(@type@ *)ip1; const @type@ in2 = *(@type@ *)ip2; - *((@type@ *)op1) = (in1 @OP@ in2 || npy_isnan(in1)) ? in1 : in2; + /* Order of operations important for MSVC 2015 */ + in1 = (in1 @OP@ in2 || npy_isnan(in1)) ? in1 : in2; + *((@type@ *)op1) = in1; } } + npy_clear_floatstatus_barrier((char*)dimensions); } /**end repeat1**/ @@ -1887,12 +1748,13 @@ NPY_NO_EXPORT void * #OP = >=, <=# **/ NPY_NO_EXPORT void -@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { /* */ if (IS_BINARY_REDUCE) { BINARY_REDUCE_LOOP(@type@) { const @type@ in2 = *(@type@ *)ip2; + /* Order of operations important for MSVC 2015 */ io1 = (io1 @OP@ in2 || npy_isnan(in2)) ? io1 : in2; } *((@type@ *)iop1) = io1; @@ -1901,6 +1763,7 @@ NPY_NO_EXPORT void BINARY_LOOP { const @type@ in1 = *(@type@ *)ip1; const @type@ in2 = *(@type@ *)ip2; + /* Order of operations important for MSVC 2015 */ *((@type@ *)op1) = (in1 @OP@ in2 || npy_isnan(in2)) ? in1 : in2; } } @@ -1909,28 +1772,27 @@ NPY_NO_EXPORT void /**end repeat1**/ NPY_NO_EXPORT void -@TYPE@_floor_divide(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_floor_divide(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const @type@ in1 = *(@type@ *)ip1; const @type@ in2 = *(@type@ *)ip2; - @type@ mod; - *((@type@ *)op1) = npy_divmod@c@(in1, in2, &mod); + *((@type@ *)op1) = npy_floor_divide@c@(in1, in2); } } NPY_NO_EXPORT void -@TYPE@_remainder(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_remainder(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const @type@ in1 = *(@type@ *)ip1; const @type@ in2 = *(@type@ *)ip2; - npy_divmod@c@(in1, in2, (@type@ *)op1); + *((@type@ *) op1) = npy_remainder@c@(in1, in2); } } NPY_NO_EXPORT void -@TYPE@_divmod(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_divmod(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP_TWO_OUT { const @type@ in1 = *(@type@ *)ip1; @@ -1940,34 +1802,7 @@ NPY_NO_EXPORT void } NPY_NO_EXPORT void -@TYPE@_square(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data)) -{ - char * margs[] = {args[0], args[0], args[1]}; - npy_intp msteps[] = {steps[0], steps[0], steps[1]}; - if (!run_binary_simd_multiply_@TYPE@(margs, dimensions, msteps)) { - UNARY_LOOP { - const @type@ in1 = *(@type@ *)ip1; - *((@type@ *)op1) = in1*in1; - } - } -} - -NPY_NO_EXPORT void -@TYPE@_reciprocal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data)) -{ - @type@ one = 1.@c@; - char * margs[] = {(char*)&one, args[0], args[1]}; - npy_intp msteps[] = {0, steps[0], steps[1]}; - if (!run_binary_simd_divide_@TYPE@(margs, dimensions, msteps)) { - UNARY_LOOP { - const @type@ in1 = *(@type@ *)ip1; - *((@type@ *)op1) = 1/in1; - } - } -} - -NPY_NO_EXPORT void -@TYPE@__ones_like(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data)) +@TYPE@__ones_like(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data)) { OUTPUT_LOOP { *((@type@ *)op1) = 1; @@ -1975,7 +1810,7 @@ NPY_NO_EXPORT void } NPY_NO_EXPORT void -@TYPE@_conjugate(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_conjugate(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { UNARY_LOOP { const @type@ in1 = *(@type@ *)ip1; @@ -1984,21 +1819,7 @@ NPY_NO_EXPORT void } NPY_NO_EXPORT void -@TYPE@_absolute(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) -{ - if (!run_unary_simd_absolute_@TYPE@(args, dimensions, steps)) { - UNARY_LOOP { - const @type@ in1 = *(@type@ *)ip1; - const @type@ tmp = in1 > 0 ? in1 : -in1; - /* add 0 to clear -0.0 */ - *((@type@ *)op1) = tmp + 0; - } - } - npy_clear_floatstatus_barrier((char*)dimensions); -} - -NPY_NO_EXPORT void -@TYPE@_negative(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_negative(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { if (!run_unary_simd_negative_@TYPE@(args, dimensions, steps)) { UNARY_LOOP { @@ -2009,7 +1830,7 @@ NPY_NO_EXPORT void } NPY_NO_EXPORT void -@TYPE@_positive(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_positive(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { UNARY_LOOP { const @type@ in1 = *(@type@ *)ip1; @@ -2018,17 +1839,18 @@ NPY_NO_EXPORT void } NPY_NO_EXPORT void -@TYPE@_sign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_sign(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { /* Sign of nan is nan */ UNARY_LOOP { const @type@ in1 = *(@type@ *)ip1; *((@type@ *)op1) = in1 > 0 ? 1 : (in1 < 0 ? -1 : (in1 == 0 ? 0 : in1)); } + npy_clear_floatstatus_barrier((char*)dimensions); } NPY_NO_EXPORT void -@TYPE@_modf(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_modf(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { UNARY_LOOP_TWO_OUT { const @type@ in1 = *(@type@ *)ip1; @@ -2037,26 +1859,7 @@ NPY_NO_EXPORT void } NPY_NO_EXPORT void -@TYPE@_frexp(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) -{ - UNARY_LOOP_TWO_OUT { - const @type@ in1 = *(@type@ *)ip1; - *((@type@ *)op1) = npy_frexp@c@(in1, (int *)op2); - } -} - -NPY_NO_EXPORT void -@TYPE@_ldexp(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) -{ - BINARY_LOOP { - const @type@ in1 = *(@type@ *)ip1; - const int in2 = *(int *)ip2; - *((@type@ *)op1) = npy_ldexp@c@(in1, in2); - } -} - -NPY_NO_EXPORT void -@TYPE@_ldexp_long(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_ldexp_long(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { /* * Additional loop to handle npy_long integer inputs (cf. #866, #1633). @@ -2085,10 +1888,95 @@ NPY_NO_EXPORT void } } -#define @TYPE@_true_divide @TYPE@_divide +/**end repeat**/ + +/* + ***************************************************************************** + ** LONGDOUBLE LOOPS ** + ***************************************************************************** + */ + +/**begin repeat + * Arithmetic + * # kind = add, subtract, multiply, divide# + * # OP = +, -, *, /# + * # PW = 1, 0, 0, 0# + */ +NPY_NO_EXPORT void +LONGDOUBLE_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) +{ + if (IS_BINARY_REDUCE) { +#if @PW@ + npy_longdouble * iop1 = (npy_longdouble *)args[0]; + npy_intp n = dimensions[0]; + *iop1 @OP@= LONGDOUBLE_pairwise_sum(args[1], n, steps[1]); +#else + BINARY_REDUCE_LOOP(npy_longdouble) { + io1 @OP@= *(npy_longdouble *)ip2; + } + *((npy_longdouble *)iop1) = io1; +#endif + } + else { + BINARY_LOOP { + const npy_longdouble in1 = *(npy_longdouble *)ip1; + const npy_longdouble in2 = *(npy_longdouble *)ip2; + *((npy_longdouble *)op1) = in1 @OP@ in2; + } + } +} /**end repeat**/ +NPY_NO_EXPORT void +LONGDOUBLE_reciprocal(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data)) +{ + UNARY_LOOP { + const npy_longdouble in1 = *(npy_longdouble*)ip1; + *((npy_longdouble *)op1) = 1/in1; + } +} + +NPY_NO_EXPORT void +LONGDOUBLE_absolute(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) +{ + UNARY_LOOP { + const npy_longdouble in1 = *(npy_longdouble *)ip1; + const npy_longdouble tmp = in1 > 0 ? in1 : -in1; + /* add 0 to clear -0.0 */ + *((npy_longdouble *)op1) = tmp + 0; + } + npy_clear_floatstatus_barrier((char*)dimensions); +} + +NPY_NO_EXPORT void +LONGDOUBLE_square(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data)) +{ + UNARY_LOOP { + const npy_longdouble in1 = *(npy_longdouble *)ip1; + *((npy_longdouble *)op1) = in1*in1; + } +} + +NPY_NO_EXPORT void +LONGDOUBLE_frexp(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) +{ + UNARY_LOOP_TWO_OUT { + const npy_longdouble in1 = *(npy_longdouble *)ip1; + *((npy_longdouble *)op1) = npy_frexpl(in1, (int *)op2); + } +} + +NPY_NO_EXPORT void +LONGDOUBLE_ldexp(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) +{ + BINARY_LOOP { + const npy_longdouble in1 = *(npy_longdouble *)ip1; + const int in2 = *(int *)ip2; + *((npy_longdouble *)op1) = npy_ldexpl(in1, in2); + } +} + /* ***************************************************************************** ** HALF-FLOAT LOOPS ** @@ -2103,7 +1991,7 @@ NPY_NO_EXPORT void * # PW = 1, 0, 0, 0# */ NPY_NO_EXPORT void -HALF_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +HALF_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { if (IS_BINARY_REDUCE) { char *iop1 = args[0]; @@ -2111,7 +1999,7 @@ HALF_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED #if @PW@ npy_intp n = dimensions[0]; - io1 @OP@= pairwise_sum_HALF(args[1], n, steps[1]); + io1 @OP@= HALF_pairwise_sum(args[1], n, steps[1]); #else BINARY_REDUCE_LOOP_INNER { io1 @OP@= npy_half_to_float(*(npy_half *)ip2); @@ -2138,7 +2026,7 @@ HALF_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED * npy_half_ge, _HALF_LOGICAL_AND, _HALF_LOGICAL_OR# */ NPY_NO_EXPORT void -HALF_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +HALF_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const npy_half in1 = *(npy_half *)ip1; @@ -2151,7 +2039,7 @@ HALF_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED #undef _HALF_LOGICAL_OR NPY_NO_EXPORT void -HALF_logical_xor(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +HALF_logical_xor(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const int in1 = !npy_half_iszero(*(npy_half *)ip1); @@ -2161,7 +2049,7 @@ HALF_logical_xor(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_U } NPY_NO_EXPORT void -HALF_logical_not(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +HALF_logical_not(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { UNARY_LOOP { const npy_half in1 = *(npy_half *)ip1; @@ -2174,7 +2062,7 @@ HALF_logical_not(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_U * #func = npy_half_isnan, npy_half_isinf, npy_half_isfinite, npy_half_signbit# **/ NPY_NO_EXPORT void -HALF_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +HALF_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { UNARY_LOOP { const npy_half in1 = *(npy_half *)ip1; @@ -2185,7 +2073,7 @@ HALF_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED /**end repeat**/ NPY_NO_EXPORT void -HALF_spacing(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +HALF_spacing(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { UNARY_LOOP { const npy_half in1 = *(npy_half *)ip1; @@ -2194,7 +2082,7 @@ HALF_spacing(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSE } NPY_NO_EXPORT void -HALF_copysign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +HALF_copysign(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const npy_half in1 = *(npy_half *)ip1; @@ -2204,7 +2092,7 @@ HALF_copysign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUS } NPY_NO_EXPORT void -HALF_nextafter(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +HALF_nextafter(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const npy_half in1 = *(npy_half *)ip1; @@ -2218,7 +2106,7 @@ HALF_nextafter(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNU * #OP = npy_half_ge, npy_half_le# **/ NPY_NO_EXPORT void -HALF_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +HALF_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { /* */ BINARY_LOOP { @@ -2226,6 +2114,7 @@ HALF_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED const npy_half in2 = *(npy_half *)ip2; *((npy_half *)op1) = (@OP@(in1, in2) || npy_half_isnan(in1)) ? in1 : in2; } + /* npy_half_isnan will never set floatstatus_invalid, so do not clear */ } /**end repeat**/ @@ -2234,7 +2123,7 @@ HALF_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED * #OP = npy_half_ge, npy_half_le# **/ NPY_NO_EXPORT void -HALF_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +HALF_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { /* */ BINARY_LOOP { @@ -2242,33 +2131,42 @@ HALF_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED const npy_half in2 = *(npy_half *)ip2; *((npy_half *)op1) = (@OP@(in1, in2) || npy_half_isnan(in2)) ? in1 : in2; } - npy_clear_floatstatus_barrier((char*)dimensions); + /* npy_half_isnan will never set floatstatus_invalid, so do not clear */ } /**end repeat**/ NPY_NO_EXPORT void -HALF_floor_divide(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +HALF_floor_divide(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const npy_half in1 = *(npy_half *)ip1; const npy_half in2 = *(npy_half *)ip2; - npy_half mod; - *((npy_half *)op1) = npy_half_divmod(in1, in2, &mod); + + float fh1 = npy_half_to_float(in1); + float fh2 = npy_half_to_float(in2); + float div; + + div = npy_floor_dividef(fh1, fh2); + *((npy_half *)op1) = npy_float_to_half(div); } } NPY_NO_EXPORT void -HALF_remainder(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +HALF_remainder(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const npy_half in1 = *(npy_half *)ip1; const npy_half in2 = *(npy_half *)ip2; - npy_half_divmod(in1, in2, (npy_half *)op1); + float fh1 = npy_half_to_float(in1); + float fh2 = npy_half_to_float(in2); + float mod; + mod = npy_remainderf(fh1, fh2); + *((npy_half *)op1) = npy_float_to_half(mod); } } NPY_NO_EXPORT void -HALF_divmod(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +HALF_divmod(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP_TWO_OUT { const npy_half in1 = *(npy_half *)ip1; @@ -2278,7 +2176,7 @@ HALF_divmod(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED } NPY_NO_EXPORT void -HALF_square(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data)) +HALF_square(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data)) { UNARY_LOOP { const float in1 = npy_half_to_float(*(npy_half *)ip1); @@ -2287,7 +2185,7 @@ HALF_square(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED } NPY_NO_EXPORT void -HALF_reciprocal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data)) +HALF_reciprocal(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data)) { UNARY_LOOP { const float in1 = npy_half_to_float(*(npy_half *)ip1); @@ -2296,7 +2194,7 @@ HALF_reciprocal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UN } NPY_NO_EXPORT void -HALF__ones_like(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data)) +HALF__ones_like(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data)) { OUTPUT_LOOP { *((npy_half *)op1) = NPY_HALF_ONE; @@ -2304,7 +2202,7 @@ HALF__ones_like(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UN } NPY_NO_EXPORT void -HALF_conjugate(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +HALF_conjugate(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { UNARY_LOOP { const npy_half in1 = *(npy_half *)ip1; @@ -2312,17 +2210,14 @@ HALF_conjugate(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNU } } -NPY_NO_EXPORT void -HALF_absolute(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +NPY_NO_EXPORT NPY_GCC_OPT_3 void +HALF_absolute(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { - UNARY_LOOP { - const npy_half in1 = *(npy_half *)ip1; - *((npy_half *)op1) = in1&0x7fffu; - } + UNARY_LOOP_FAST(npy_half, npy_half, *out = in&0x7fffu); } NPY_NO_EXPORT void -HALF_negative(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +HALF_negative(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { UNARY_LOOP { const npy_half in1 = *(npy_half *)ip1; @@ -2331,7 +2226,7 @@ HALF_negative(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUS } NPY_NO_EXPORT void -HALF_positive(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +HALF_positive(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { UNARY_LOOP { const npy_half in1 = *(npy_half *)ip1; @@ -2340,7 +2235,7 @@ HALF_positive(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUS } NPY_NO_EXPORT void -HALF_sign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +HALF_sign(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { /* Sign of nan is nan */ UNARY_LOOP { @@ -2352,7 +2247,7 @@ HALF_sign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(f } NPY_NO_EXPORT void -HALF_modf(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +HALF_modf(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { float temp; @@ -2364,7 +2259,7 @@ HALF_modf(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(f } NPY_NO_EXPORT void -HALF_frexp(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +HALF_frexp(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { UNARY_LOOP_TWO_OUT { const float in1 = npy_half_to_float(*(npy_half *)ip1); @@ -2373,7 +2268,7 @@ HALF_frexp(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED( } NPY_NO_EXPORT void -HALF_ldexp(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +HALF_ldexp(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const float in1 = npy_half_to_float(*(npy_half *)ip1); @@ -2383,7 +2278,7 @@ HALF_ldexp(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED( } NPY_NO_EXPORT void -HALF_ldexp_long(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +HALF_ldexp_long(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { /* * Additional loop to handle npy_long integer inputs (cf. #866, #1633). @@ -2412,9 +2307,6 @@ HALF_ldexp_long(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UN } } -#define HALF_true_divide HALF_divide - - /* ***************************************************************************** ** COMPLEX LOOPS ** @@ -2438,81 +2330,11 @@ HALF_ldexp_long(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UN * #ftype = npy_float, npy_double, npy_longdouble# * #c = f, , l# * #C = F, , L# + * #SIMD = 1, 1, 0# */ -/* similar to pairwise sum of real floats */ -static void -pairwise_sum_@TYPE@(@ftype@ *rr, @ftype@ * ri, char * a, npy_intp n, - npy_intp stride) -{ - assert(n % 2 == 0); - if (n < 8) { - npy_intp i; - - *rr = 0.; - *ri = 0.; - for (i = 0; i < n; i += 2) { - *rr += *((@ftype@ *)(a + i * stride + 0)); - *ri += *((@ftype@ *)(a + i * stride + sizeof(@ftype@))); - } - return; - } - else if (n <= PW_BLOCKSIZE) { - npy_intp i; - @ftype@ r[8]; - - /* - * sum a block with 8 accumulators - * 8 times unroll reduces blocksize to 16 and allows vectorization with - * avx without changing summation ordering - */ - r[0] = *((@ftype@ *)(a + 0 * stride)); - r[1] = *((@ftype@ *)(a + 0 * stride + sizeof(@ftype@))); - r[2] = *((@ftype@ *)(a + 2 * stride)); - r[3] = *((@ftype@ *)(a + 2 * stride + sizeof(@ftype@))); - r[4] = *((@ftype@ *)(a + 4 * stride)); - r[5] = *((@ftype@ *)(a + 4 * stride + sizeof(@ftype@))); - r[6] = *((@ftype@ *)(a + 6 * stride)); - r[7] = *((@ftype@ *)(a + 6 * stride + sizeof(@ftype@))); - - for (i = 8; i < n - (n % 8); i += 8) { - /* small blocksizes seems to mess with hardware prefetch */ - NPY_PREFETCH(a + (i + 512/(npy_intp)sizeof(@ftype@))*stride, 0, 3); - r[0] += *((@ftype@ *)(a + (i + 0) * stride)); - r[1] += *((@ftype@ *)(a + (i + 0) * stride + sizeof(@ftype@))); - r[2] += *((@ftype@ *)(a + (i + 2) * stride)); - r[3] += *((@ftype@ *)(a + (i + 2) * stride + sizeof(@ftype@))); - r[4] += *((@ftype@ *)(a + (i + 4) * stride)); - r[5] += *((@ftype@ *)(a + (i + 4) * stride + sizeof(@ftype@))); - r[6] += *((@ftype@ *)(a + (i + 6) * stride)); - r[7] += *((@ftype@ *)(a + (i + 6) * stride + sizeof(@ftype@))); - } - - /* accumulate now to avoid stack spills for single peel loop */ - *rr = ((r[0] + r[2]) + (r[4] + r[6])); - *ri = ((r[1] + r[3]) + (r[5] + r[7])); - - /* do non multiple of 8 rest */ - for (; i < n; i+=2) { - *rr += *((@ftype@ *)(a + i * stride + 0)); - *ri += *((@ftype@ *)(a + i * stride + sizeof(@ftype@))); - } - return; - } - else { - /* divide by two but avoid non-multiples of unroll factor */ - @ftype@ rr1, ri1, rr2, ri2; - npy_intp n2 = n / 2; - - n2 -= n2 % 8; - pairwise_sum_@TYPE@(&rr1, &ri1, a, n2, stride); - pairwise_sum_@TYPE@(&rr2, &ri2, a + n2 * stride, n - n2, stride); - *rr = rr1 + rr2; - *ri = ri1 + ri2; - return; - } -} - +#if !@SIMD@ +// CFLOAT & CDOUBLE defined by 'loops_arithm_fp.dispatch.c.src' /**begin repeat1 * arithmetic * #kind = add, subtract# @@ -2520,15 +2342,16 @@ pairwise_sum_@TYPE@(@ftype@ *rr, @ftype@ * ri, char * a, npy_intp n, * #PW = 1, 0# */ NPY_NO_EXPORT void -@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { - if (IS_BINARY_REDUCE && @PW@) { + // Parenthesis around @PW@ tells clang dead code is intentional + if (IS_BINARY_REDUCE && (@PW@)) { npy_intp n = dimensions[0]; @ftype@ * or = ((@ftype@ *)args[0]); @ftype@ * oi = ((@ftype@ *)args[0]) + 1; @ftype@ rr, ri; - pairwise_sum_@TYPE@(&rr, &ri, args[1], n * 2, steps[1] / 2); + @TYPE@_pairwise_sum(&rr, &ri, args[1], n * 2, steps[1] / 2); *or @OP@= rr; *oi @OP@= ri; return; @@ -2547,7 +2370,7 @@ NPY_NO_EXPORT void /**end repeat1**/ NPY_NO_EXPORT void -@TYPE@_multiply(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_multiply(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const @ftype@ in1r = ((@ftype@ *)ip1)[0]; @@ -2558,9 +2381,10 @@ NPY_NO_EXPORT void ((@ftype@ *)op1)[1] = in1r*in2i + in1i*in2r; } } +#endif // !SIMD NPY_NO_EXPORT void -@TYPE@_divide(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_divide(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const @ftype@ in1r = ((@ftype@ *)ip1)[0]; @@ -2591,33 +2415,13 @@ NPY_NO_EXPORT void } } -NPY_NO_EXPORT void -@TYPE@_floor_divide(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) -{ - BINARY_LOOP { - const @ftype@ in1r = ((@ftype@ *)ip1)[0]; - const @ftype@ in1i = ((@ftype@ *)ip1)[1]; - const @ftype@ in2r = ((@ftype@ *)ip2)[0]; - const @ftype@ in2i = ((@ftype@ *)ip2)[1]; - if (npy_fabs@c@(in2r) >= npy_fabs@c@(in2i)) { - const @ftype@ rat = in2i/in2r; - ((@ftype@ *)op1)[0] = npy_floor@c@((in1r + in1i*rat)/(in2r + in2i*rat)); - ((@ftype@ *)op1)[1] = 0; - } - else { - const @ftype@ rat = in2r/in2i; - ((@ftype@ *)op1)[0] = npy_floor@c@((in1r*rat + in1i)/(in2i + in2r*rat)); - ((@ftype@ *)op1)[1] = 0; - } - } -} /**begin repeat1 * #kind= greater, greater_equal, less, less_equal, equal, not_equal# * #OP = CGT, CGE, CLT, CLE, CEQ, CNE# */ NPY_NO_EXPORT void -@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const @ftype@ in1r = ((@ftype@ *)ip1)[0]; @@ -2635,7 +2439,7 @@ NPY_NO_EXPORT void #OP2 = &&, ||# */ NPY_NO_EXPORT void -@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const @ftype@ in1r = ((@ftype@ *)ip1)[0]; @@ -2648,7 +2452,7 @@ NPY_NO_EXPORT void /**end repeat1**/ NPY_NO_EXPORT void -@TYPE@_logical_xor(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_logical_xor(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const @ftype@ in1r = ((@ftype@ *)ip1)[0]; @@ -2662,7 +2466,7 @@ NPY_NO_EXPORT void } NPY_NO_EXPORT void -@TYPE@_logical_not(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_logical_not(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { UNARY_LOOP { const @ftype@ in1r = ((@ftype@ *)ip1)[0]; @@ -2677,7 +2481,7 @@ NPY_NO_EXPORT void * #OP = ||, ||, &&# **/ NPY_NO_EXPORT void -@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { UNARY_LOOP { const @ftype@ in1r = ((@ftype@ *)ip1)[0]; @@ -2689,7 +2493,7 @@ NPY_NO_EXPORT void /**end repeat1**/ NPY_NO_EXPORT void -@TYPE@_square(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data)) +@TYPE@_square(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data)) { UNARY_LOOP { const @ftype@ in1r = ((@ftype@ *)ip1)[0]; @@ -2700,7 +2504,7 @@ NPY_NO_EXPORT void } NPY_NO_EXPORT void -@TYPE@_reciprocal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data)) +@TYPE@_reciprocal(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data)) { UNARY_LOOP { const @ftype@ in1r = ((@ftype@ *)ip1)[0]; @@ -2720,7 +2524,7 @@ NPY_NO_EXPORT void } NPY_NO_EXPORT void -@TYPE@__ones_like(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data)) +@TYPE@__ones_like(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data)) { OUTPUT_LOOP { ((@ftype@ *)op1)[0] = 1; @@ -2729,7 +2533,7 @@ NPY_NO_EXPORT void } NPY_NO_EXPORT void -@TYPE@_conjugate(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) { +@TYPE@_conjugate(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { UNARY_LOOP { const @ftype@ in1r = ((@ftype@ *)ip1)[0]; const @ftype@ in1i = ((@ftype@ *)ip1)[1]; @@ -2739,7 +2543,7 @@ NPY_NO_EXPORT void } NPY_NO_EXPORT void -@TYPE@_absolute(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_absolute(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { UNARY_LOOP { const @ftype@ in1r = ((@ftype@ *)ip1)[0]; @@ -2748,8 +2552,23 @@ NPY_NO_EXPORT void } } +#if @SIMD@ +/**begin repeat1 + * arithmetic + * #kind = conjugate, square, absolute# + */ +NPY_NO_EXPORT void +@TYPE@_@kind@_avx512f(char **args, const npy_intp *dimensions, const npy_intp *steps, void *func) +{ + if (!run_unary_avx512f_@kind@_@TYPE@(args, dimensions, steps)) { + @TYPE@_@kind@(args, dimensions, steps, func); + } +} +/**end repeat1**/ +#endif + NPY_NO_EXPORT void -@TYPE@__arg(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@__arg(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { UNARY_LOOP { const @ftype@ in1r = ((@ftype@ *)ip1)[0]; @@ -2759,7 +2578,7 @@ NPY_NO_EXPORT void } NPY_NO_EXPORT void -@TYPE@_sign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_sign(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { /* fixme: sign of nan is currently 0 */ UNARY_LOOP { @@ -2777,21 +2596,19 @@ NPY_NO_EXPORT void * #OP = CGE, CLE# */ NPY_NO_EXPORT void -@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { - const @ftype@ in1r = ((@ftype@ *)ip1)[0]; - const @ftype@ in1i = ((@ftype@ *)ip1)[1]; + @ftype@ in1r = ((@ftype@ *)ip1)[0]; + @ftype@ in1i = ((@ftype@ *)ip1)[1]; const @ftype@ in2r = ((@ftype@ *)ip2)[0]; const @ftype@ in2i = ((@ftype@ *)ip2)[1]; - if (@OP@(in1r, in1i, in2r, in2i) || npy_isnan(in1r) || npy_isnan(in1i)) { - ((@ftype@ *)op1)[0] = in1r; - ((@ftype@ *)op1)[1] = in1i; - } - else { - ((@ftype@ *)op1)[0] = in2r; - ((@ftype@ *)op1)[1] = in2i; + if ( !(npy_isnan(in1r) || npy_isnan(in1i) || @OP@(in1r, in1i, in2r, in2i))) { + in1r = in2r; + in1i = in2i; } + ((@ftype@ *)op1)[0] = in1r; + ((@ftype@ *)op1)[1] = in1i; } npy_clear_floatstatus_barrier((char*)dimensions); } @@ -2802,14 +2619,14 @@ NPY_NO_EXPORT void * #OP = CGE, CLE# */ NPY_NO_EXPORT void -@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +@TYPE@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const @ftype@ in1r = ((@ftype@ *)ip1)[0]; const @ftype@ in1i = ((@ftype@ *)ip1)[1]; const @ftype@ in2r = ((@ftype@ *)ip2)[0]; const @ftype@ in2i = ((@ftype@ *)ip2)[1]; - if (@OP@(in1r, in1i, in2r, in2i) || npy_isnan(in2r) || npy_isnan(in2i)) { + if (npy_isnan(in2r) || npy_isnan(in2i) || @OP@(in1r, in1i, in2r, in2i)) { ((@ftype@ *)op1)[0] = in1r; ((@ftype@ *)op1)[1] = in1i; } @@ -2818,11 +2635,10 @@ NPY_NO_EXPORT void ((@ftype@ *)op1)[1] = in2i; } } + npy_clear_floatstatus_barrier((char*)dimensions); } /**end repeat1**/ -#define @TYPE@_true_divide @TYPE@_divide - /**end repeat**/ #undef CGE @@ -2849,7 +2665,7 @@ NPY_NO_EXPORT void * #as_bool = 1, 0# */ NPY_NO_EXPORT void -OBJECT@suffix@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) { +OBJECT@suffix@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { PyObject *ret_obj; PyObject *in1 = *(PyObject **)ip1; @@ -2885,7 +2701,7 @@ OBJECT@suffix@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void * /**end repeat**/ NPY_NO_EXPORT void -OBJECT_sign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +OBJECT_sign(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { PyObject *zero = PyLong_FromLong(0); diff --git a/numpy/core/src/umath/loops.h.src b/numpy/core/src/umath/loops.h.src index 5c2b2c22c694..081ca99571a1 100644 --- a/numpy/core/src/umath/loops.h.src +++ b/numpy/core/src/umath/loops.h.src @@ -6,15 +6,17 @@ #ifndef _NPY_UMATH_LOOPS_H_ #define _NPY_UMATH_LOOPS_H_ +#ifndef NPY_NO_EXPORT + #define NPY_NO_EXPORT NPY_VISIBILITY_HIDDEN +#endif + #define BOOL_invert BOOL_logical_not -#define BOOL_negative BOOL_logical_not #define BOOL_add BOOL_logical_or #define BOOL_bitwise_and BOOL_logical_and #define BOOL_bitwise_or BOOL_logical_or #define BOOL_logical_xor BOOL_not_equal #define BOOL_bitwise_xor BOOL_logical_xor #define BOOL_multiply BOOL_logical_and -#define BOOL_subtract BOOL_logical_xor #define BOOL_maximum BOOL_logical_or #define BOOL_minimum BOOL_logical_and #define BOOL_fmax BOOL_maximum @@ -32,11 +34,18 @@ * logical_and, logical_or, absolute, logical_not# **/ NPY_NO_EXPORT void -BOOL_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +BOOL_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); /**end repeat**/ NPY_NO_EXPORT void -BOOL__ones_like(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data)); +BOOL__ones_like(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data)); + +/**begin repeat + * #kind = isnan, isinf, isfinite# + **/ +NPY_NO_EXPORT void +BOOL_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); +/**end repeat**/ /* ***************************************************************************** @@ -44,6 +53,18 @@ BOOL__ones_like(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UN ***************************************************************************** */ +#ifndef NPY_DISABLE_OPTIMIZATION + #include "loops_arithmetic.dispatch.h" +#endif + +/**begin repeat + * #TYPE = UBYTE, USHORT, UINT, ULONG, ULONGLONG, + BYTE, SHORT, INT, LONG, LONGLONG# + */ + NPY_CPU_DISPATCH_DECLARE(NPY_NO_EXPORT void @TYPE@_divide, + (char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))) +/**end repeat**/ + /**begin repeat * #TYPE = BYTE, SHORT, INT, LONG, LONGLONG# */ @@ -59,32 +80,32 @@ BOOL__ones_like(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UN #define @S@@TYPE@_fmin @S@@TYPE@_minimum NPY_NO_EXPORT void -@S@@TYPE@__ones_like(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data)); +@S@@TYPE@__ones_like(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data)); NPY_NO_EXPORT void -@S@@TYPE@_positive(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +@S@@TYPE@_positive(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); /**begin repeat2 * #isa = , _avx2# */ NPY_NO_EXPORT void -@S@@TYPE@_square@isa@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data)); +@S@@TYPE@_square@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data)); NPY_NO_EXPORT void -@S@@TYPE@_reciprocal@isa@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data)); +@S@@TYPE@_reciprocal@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data)); NPY_NO_EXPORT void -@S@@TYPE@_conjugate@isa@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +@S@@TYPE@_conjugate@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -@S@@TYPE@_negative@isa@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +@S@@TYPE@_negative@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -@S@@TYPE@_logical_not@isa@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +@S@@TYPE@_logical_not@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -@S@@TYPE@_invert@isa@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +@S@@TYPE@_invert@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); /**begin repeat3 * Arithmetic @@ -93,7 +114,7 @@ NPY_NO_EXPORT void * #OP = +, -,*, &, |, ^, <<, >># */ NPY_NO_EXPORT void -@S@@TYPE@_@kind@@isa@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +@S@@TYPE@_@kind@@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); /**end repeat3**/ @@ -103,12 +124,12 @@ NPY_NO_EXPORT void * #OP = ==, !=, >, >=, <, <=, &&, ||# */ NPY_NO_EXPORT void -@S@@TYPE@_@kind@@isa@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +@S@@TYPE@_@kind@@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); /**end repeat3**/ NPY_NO_EXPORT void -@S@@TYPE@_logical_xor@isa@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +@S@@TYPE@_logical_xor@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); /**end repeat2**/ /**begin repeat2 @@ -116,35 +137,39 @@ NPY_NO_EXPORT void * #OP = >, <# **/ NPY_NO_EXPORT void -@S@@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +@S@@TYPE@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); /**end repeat2**/ NPY_NO_EXPORT void -@S@@TYPE@_power(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +@S@@TYPE@_power(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -@S@@TYPE@_fmod(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +@S@@TYPE@_fmod(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -@S@@TYPE@_absolute(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +@S@@TYPE@_absolute(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -@S@@TYPE@_sign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +@S@@TYPE@_sign(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -@S@@TYPE@_divide(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +@S@@TYPE@_remainder(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -@S@@TYPE@_remainder(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +@S@@TYPE@_divmod(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -@S@@TYPE@_divmod(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +@S@@TYPE@_gcd(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -@S@@TYPE@_gcd(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +@S@@TYPE@_lcm(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); +/**begin repeat2 + * #kind = isnan, isinf, isfinite# + **/ NPY_NO_EXPORT void -@S@@TYPE@_lcm(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +@S@@TYPE@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); +/**end repeat2**/ /**end repeat1**/ @@ -155,12 +180,118 @@ NPY_NO_EXPORT void ** FLOAT LOOPS ** ***************************************************************************** */ +#ifndef NPY_DISABLE_OPTIMIZATION + #include "loops_unary_fp.dispatch.h" +#endif +/**begin repeat + * #TYPE = FLOAT, DOUBLE# + */ +/**begin repeat1 + * #kind = ceil, sqrt, absolute, square, reciprocal# + */ +NPY_CPU_DISPATCH_DECLARE(NPY_NO_EXPORT void @TYPE@_@kind@, + (char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data))) +/**end repeat1**/ +/**end repeat**/ +#ifndef NPY_DISABLE_OPTIMIZATION + #include "loops_arithm_fp.dispatch.h" +#endif /**begin repeat * #TYPE = FLOAT, DOUBLE# */ +/**begin repeat1 + * Arithmetic + * # kind = add, subtract, multiply, divide# + * # OP = +, -, *, /# + */ +NPY_CPU_DISPATCH_DECLARE(NPY_NO_EXPORT void @TYPE@_@kind@, + (char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))) +/**end repeat1**/ +/**end repeat**/ + +#ifndef NPY_DISABLE_OPTIMIZATION + #include "loops_umath_fp.dispatch.h" +#endif + +/**begin repeat + * #TYPE = FLOAT, DOUBLE# + */ +/**begin repeat1 + * #func = tanh, exp2, log2, log10, expm1, log1p, cbrt, tan, arcsin, arccos, arctan, sinh, cosh, arcsinh, arccosh, arctanh# + */ + +NPY_CPU_DISPATCH_DECLARE(NPY_NO_EXPORT void @TYPE@_@func@, + (char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))) + +/**end repeat1**/ +/**end repeat**/ + +/**begin repeat + * #func = sin, cos# + */ + +NPY_CPU_DISPATCH_DECLARE(NPY_NO_EXPORT void DOUBLE_@func@, + (char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))) + +/**end repeat**/ + +/**begin repeat + * #TYPE = FLOAT, DOUBLE# + */ +/**begin repeat1 + * #func = maximum, minimum# + */ NPY_NO_EXPORT void -@TYPE@_sqrt(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +@TYPE@_@func@_avx512f(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); + +/**end repeat1**/ +/**end repeat**/ + +#ifndef NPY_DISABLE_OPTIMIZATION + #include "loops_trigonometric.dispatch.h" +#endif +/**begin repeat + * #func = sin, cos# + */ +NPY_CPU_DISPATCH_DECLARE(NPY_NO_EXPORT void FLOAT_@func@, ( + char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func) +)) +/**end repeat**/ + +#ifndef NPY_DISABLE_OPTIMIZATION + #include "loops_exponent_log.dispatch.h" +#endif +/**begin repeat + * #TYPE = FLOAT, DOUBLE# + */ +/**begin repeat1 + * # kind = exp, log, frexp, ldexp# + */ +NPY_CPU_DISPATCH_DECLARE(NPY_NO_EXPORT void @TYPE@_@kind@, ( + char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func) +)) +/**end repeat1**/ +/**end repeat**/ + +/**begin repeat + * #func = rint, floor, trunc# + */ + +/**begin repeat1 +* #TYPE = FLOAT, DOUBLE# +*/ + +NPY_NO_EXPORT NPY_GCC_OPT_3 void +@TYPE@_@func@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data)); + +/**begin repeat2 + * #isa = avx512f, fma# + */ +NPY_NO_EXPORT NPY_GCC_OPT_3 void +@TYPE@_@func@_@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data)); +/**end repeat2**/ +/**end repeat1**/ /**end repeat**/ /**begin repeat @@ -177,7 +308,7 @@ NPY_NO_EXPORT void * # OP = +, -, *, /# */ NPY_NO_EXPORT void -@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +@TYPE@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); /**end repeat1**/ /**begin repeat1 @@ -186,21 +317,26 @@ NPY_NO_EXPORT void * #OP = ==, !=, <, <=, >, >=, &&, ||# */ NPY_NO_EXPORT void -@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +@TYPE@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); /**end repeat1**/ NPY_NO_EXPORT void -@TYPE@_logical_xor(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +@TYPE@_logical_xor(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -@TYPE@_logical_not(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +@TYPE@_logical_not(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); /**begin repeat1 * #kind = isnan, isinf, isfinite, signbit, copysign, nextafter, spacing# * #func = npy_isnan, npy_isinf, npy_isfinite, npy_signbit, npy_copysign, nextafter, spacing# **/ + +/**begin repeat2 + * #ISA = , _avx512_skx# + **/ NPY_NO_EXPORT void -@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +@TYPE@_@kind@@ISA@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); +/**end repeat2**/ /**end repeat1**/ /**begin repeat1 @@ -208,7 +344,7 @@ NPY_NO_EXPORT void * #OP = >=, <=# **/ NPY_NO_EXPORT void -@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +@TYPE@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); /**end repeat1**/ /**begin repeat1 @@ -216,57 +352,53 @@ NPY_NO_EXPORT void * #OP = >=, <=# **/ NPY_NO_EXPORT void -@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +@TYPE@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); /**end repeat1**/ NPY_NO_EXPORT void -@TYPE@_floor_divide(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +@TYPE@_floor_divide(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -@TYPE@_remainder(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +@TYPE@_remainder(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -@TYPE@_divmod(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +@TYPE@_divmod(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -@TYPE@_square(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data)); +@TYPE@_square(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data)); NPY_NO_EXPORT void -@TYPE@_reciprocal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data)); - +@TYPE@_reciprocal(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data)); NPY_NO_EXPORT void -@TYPE@__ones_like(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data)); +@TYPE@__ones_like(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data)); NPY_NO_EXPORT void -@TYPE@_conjugate(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +@TYPE@_conjugate(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -@TYPE@_absolute(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +@TYPE@_absolute(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -@TYPE@_negative(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +@TYPE@_negative(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -@TYPE@_positive(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +@TYPE@_positive(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -@TYPE@_sign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); - +@TYPE@_sign(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -@TYPE@_modf(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +@TYPE@_modf(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -@TYPE@_frexp(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +@TYPE@_frexp(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -@TYPE@_ldexp(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -NPY_NO_EXPORT void -@TYPE@_ldexp_long(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); - -#define @TYPE@_true_divide @TYPE@_divide +@TYPE@_ldexp(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); +NPY_NO_EXPORT void +@TYPE@_ldexp_long(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); /**end repeat**/ @@ -275,6 +407,19 @@ NPY_NO_EXPORT void ** COMPLEX LOOPS ** ***************************************************************************** */ +#ifndef NPY_DISABLE_OPTIMIZATION + #include "loops_arithm_fp.dispatch.h" +#endif +/**begin repeat + * #TYPE = CFLOAT, CDOUBLE# + */ +/**begin repeat1 + * #kind = add, subtract, multiply# + */ +NPY_CPU_DISPATCH_DECLARE(NPY_NO_EXPORT void @TYPE@_@kind@, + (char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data))) +/**end repeat1**/ +/**end repeat**/ #define CGE(xr,xi,yr,yi) (xr > yr || (xr == yr && xi >= yi)); #define CLE(xr,xi,yr,yi) (xr < yr || (xr == yr && xi <= yi)); @@ -292,29 +437,21 @@ NPY_NO_EXPORT void /**begin repeat1 * arithmetic - * #kind = add, subtract# - * #OP = +, -# + * #kind = add, subtract, multiply# */ NPY_NO_EXPORT void -C@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); - +C@TYPE@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); /**end repeat1**/ NPY_NO_EXPORT void -C@TYPE@_multiply(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); - -NPY_NO_EXPORT void -C@TYPE@_divide(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); - -NPY_NO_EXPORT void -C@TYPE@_floor_divide(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +C@TYPE@_divide(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); /**begin repeat1 * #kind= greater, greater_equal, less, less_equal, equal, not_equal# * #OP = CGT, CGE, CLT, CLE, CEQ, CNE# */ NPY_NO_EXPORT void -C@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +C@TYPE@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); /**end repeat1**/ /**begin repeat1 @@ -323,50 +460,55 @@ C@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNU #OP2 = &&, ||# */ NPY_NO_EXPORT void -C@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +C@TYPE@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); /**end repeat1**/ NPY_NO_EXPORT void -C@TYPE@_logical_xor(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +C@TYPE@_logical_xor(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -C@TYPE@_logical_not(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +C@TYPE@_logical_not(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); /**begin repeat1 * #kind = isnan, isinf, isfinite# * #func = npy_isnan, npy_isinf, npy_isfinite# * #OP = ||, ||, &&# **/ NPY_NO_EXPORT void -C@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +C@TYPE@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); /**end repeat1**/ NPY_NO_EXPORT void -C@TYPE@_square(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data)); +C@TYPE@_reciprocal(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data)); NPY_NO_EXPORT void -C@TYPE@_reciprocal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data)); +C@TYPE@__ones_like(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data)); + +/**begin repeat1 + * #isa = , _avx512f# + */ NPY_NO_EXPORT void -C@TYPE@__ones_like(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data)); +C@TYPE@_conjugate@isa@(char **args, const npy_intp *dimensions, const npy_intp *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -C@TYPE@_conjugate(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +C@TYPE@_absolute@isa@(char **args, const npy_intp *dimensions, const npy_intp *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -C@TYPE@_absolute(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +C@TYPE@_square@isa@(char **args, const npy_intp *dimensions, const npy_intp *steps, void *NPY_UNUSED(data)); +/**end repeat1**/ NPY_NO_EXPORT void -C@TYPE@__arg(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +C@TYPE@__arg(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -C@TYPE@_sign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +C@TYPE@_sign(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); /**begin repeat1 * #kind = maximum, minimum# * #OP = CGE, CLE# */ NPY_NO_EXPORT void -C@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +C@TYPE@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); /**end repeat1**/ /**begin repeat1 @@ -374,11 +516,9 @@ C@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNU * #OP = CGE, CLE# */ NPY_NO_EXPORT void -C@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +C@TYPE@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); /**end repeat1**/ -#define C@TYPE@_true_divide C@TYPE@_divide - /**end repeat**/ #undef CGE @@ -395,96 +535,104 @@ C@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNU */ NPY_NO_EXPORT void -TIMEDELTA_negative(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +TIMEDELTA_negative(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -TIMEDELTA_positive(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +TIMEDELTA_positive(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -TIMEDELTA_absolute(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +TIMEDELTA_absolute(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -TIMEDELTA_sign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +TIMEDELTA_sign(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); /**begin repeat * #TYPE = DATETIME, TIMEDELTA# */ NPY_NO_EXPORT void -@TYPE@_isnat(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +@TYPE@_isnat(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -@TYPE@__ones_like(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data)); +@TYPE@_isfinite(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); + +NPY_NO_EXPORT void +@TYPE@_isinf(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); + +#define @TYPE@_isnan @TYPE@_isnat + +NPY_NO_EXPORT void +@TYPE@__ones_like(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data)); /**begin repeat1 * #kind = equal, not_equal, greater, greater_equal, less, less_equal# * #OP = ==, !=, >, >=, <, <=# */ NPY_NO_EXPORT void -@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +@TYPE@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); /**end repeat1**/ /**begin repeat1 - * #kind = maximum, minimum# - * #OP = >, <# + * #kind = maximum, minimum, fmin, fmax# **/ NPY_NO_EXPORT void -@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +@TYPE@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); /**end repeat1**/ /**end repeat**/ NPY_NO_EXPORT void -DATETIME_Mm_M_add(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data)); +DATETIME_Mm_M_add(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data)); NPY_NO_EXPORT void -DATETIME_mM_M_add(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +DATETIME_mM_M_add(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -TIMEDELTA_mm_m_add(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +TIMEDELTA_mm_m_add(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -DATETIME_Mm_M_subtract(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +DATETIME_Mm_M_subtract(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -DATETIME_MM_m_subtract(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +DATETIME_MM_m_subtract(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -TIMEDELTA_mm_m_subtract(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +TIMEDELTA_mm_m_subtract(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -TIMEDELTA_mq_m_multiply(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +TIMEDELTA_mq_m_multiply(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -TIMEDELTA_qm_m_multiply(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +TIMEDELTA_qm_m_multiply(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -TIMEDELTA_md_m_multiply(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +TIMEDELTA_md_m_multiply(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -TIMEDELTA_dm_m_multiply(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +TIMEDELTA_dm_m_multiply(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -TIMEDELTA_mq_m_divide(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +TIMEDELTA_mq_m_divide(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -TIMEDELTA_md_m_divide(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +TIMEDELTA_md_m_divide(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void -TIMEDELTA_mm_d_divide(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +TIMEDELTA_mm_d_divide(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); -/* Special case equivalents to above functions */ +NPY_NO_EXPORT void +TIMEDELTA_mm_q_floor_divide(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); + +NPY_NO_EXPORT void +TIMEDELTA_mm_m_remainder(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); + +NPY_NO_EXPORT void +TIMEDELTA_mm_qm_divmod(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); -#define TIMEDELTA_mq_m_true_divide TIMEDELTA_mq_m_divide -#define TIMEDELTA_md_m_true_divide TIMEDELTA_md_m_divide -#define TIMEDELTA_mm_d_true_divide TIMEDELTA_mm_d_divide +/* Special case equivalents to above functions */ #define TIMEDELTA_mq_m_floor_divide TIMEDELTA_mq_m_divide #define TIMEDELTA_md_m_floor_divide TIMEDELTA_md_m_divide /* #define TIMEDELTA_mm_d_floor_divide TIMEDELTA_mm_d_divide */ -#define TIMEDELTA_fmin TIMEDELTA_minimum -#define TIMEDELTA_fmax TIMEDELTA_maximum -#define DATETIME_fmin DATETIME_minimum -#define DATETIME_fmax DATETIME_maximum /* ***************************************************************************** @@ -500,12 +648,15 @@ TIMEDELTA_mm_d_divide(char **args, npy_intp *dimensions, npy_intp *steps, void * * #suffix = , _OO_O# */ NPY_NO_EXPORT void -OBJECT@suffix@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +OBJECT@suffix@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); /**end repeat1**/ /**end repeat**/ NPY_NO_EXPORT void -OBJECT_sign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +OBJECT_sign(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); + +NPY_NO_EXPORT void +PyUFunc_OOO_O(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func); /* ***************************************************************************** diff --git a/numpy/core/src/umath/loops_arithm_fp.dispatch.c.src b/numpy/core/src/umath/loops_arithm_fp.dispatch.c.src new file mode 100644 index 000000000000..51b167844097 --- /dev/null +++ b/numpy/core/src/umath/loops_arithm_fp.dispatch.c.src @@ -0,0 +1,777 @@ +/*@targets + ** $maxopt baseline + ** sse2 avx2 avx512f + **/ +#define _UMATHMODULE +#define _MULTIARRAYMODULE +#define NPY_NO_DEPRECATED_API NPY_API_VERSION + +#include "simd/simd.h" +#include "loops_utils.h" +#include "loops.h" +#include "lowlevel_strided_loops.h" +// Provides the various *_LOOP macros +#include "fast_loop_macros.h" + +// TODO: replace raw SIMD with NPYV +//############################################################################### +//## Real Single/Double precision +//############################################################################### +/******************************************************************************** + ** Defining the SIMD kernels + ********************************************************************************/ +#ifdef NPY_HAVE_SSE2 +/**begin repeat + * #type = npy_float, npy_double# + * #TYPE = FLOAT, DOUBLE# + * #scalarf = npy_sqrtf, npy_sqrt# + * #c = f, # + * #vtype = __m128, __m128d# + * #vtype256 = __m256, __m256d# + * #vtype512 = __m512, __m512d# + * #vpre = _mm, _mm# + * #vpre256 = _mm256, _mm256# + * #vpre512 = _mm512, _mm512# + * #vsuf = ps, pd# + * #vsufs = ss, sd# + * #nan = NPY_NANF, NPY_NAN# + * #double = 0, 1# + * #cast = _mm_castps_si128, _mm_castpd_si128# + */ +/**begin repeat1 +* Arithmetic +* # kind = add, subtract, multiply, divide# +* # OP = +, -, *, /# +* # VOP = add, sub, mul, div# +*/ +static void +sse2_binary_@kind@_@TYPE@(@type@ * op, @type@ * ip1, @type@ * ip2, npy_intp n) +{ +#ifdef NPY_HAVE_AVX512F + const npy_intp vector_size_bytes = 64; + LOOP_BLOCK_ALIGN_VAR(op, @type@, vector_size_bytes) + op[i] = ip1[i] @OP@ ip2[i]; + /* lots of specializations, to squeeze out max performance */ + if (npy_is_aligned(&ip1[i], vector_size_bytes) && npy_is_aligned(&ip2[i], vector_size_bytes)) { + if (ip1 == ip2) { + LOOP_BLOCKED(@type@, vector_size_bytes) { + @vtype512@ a = @vpre512@_load_@vsuf@(&ip1[i]); + @vtype512@ c = @vpre512@_@VOP@_@vsuf@(a, a); + @vpre512@_store_@vsuf@(&op[i], c); + } + } + else { + LOOP_BLOCKED(@type@, vector_size_bytes) { + @vtype512@ a = @vpre512@_load_@vsuf@(&ip1[i]); + @vtype512@ b = @vpre512@_load_@vsuf@(&ip2[i]); + @vtype512@ c = @vpre512@_@VOP@_@vsuf@(a, b); + @vpre512@_store_@vsuf@(&op[i], c); + } + } + } + else if (npy_is_aligned(&ip1[i], vector_size_bytes)) { + LOOP_BLOCKED(@type@, vector_size_bytes) { + @vtype512@ a = @vpre512@_load_@vsuf@(&ip1[i]); + @vtype512@ b = @vpre512@_loadu_@vsuf@(&ip2[i]); + @vtype512@ c = @vpre512@_@VOP@_@vsuf@(a, b); + @vpre512@_store_@vsuf@(&op[i], c); + } + } + else if (npy_is_aligned(&ip2[i], vector_size_bytes)) { + LOOP_BLOCKED(@type@, vector_size_bytes) { + @vtype512@ a = @vpre512@_loadu_@vsuf@(&ip1[i]); + @vtype512@ b = @vpre512@_load_@vsuf@(&ip2[i]); + @vtype512@ c = @vpre512@_@VOP@_@vsuf@(a, b); + @vpre512@_store_@vsuf@(&op[i], c); + } + } + else { + if (ip1 == ip2) { + LOOP_BLOCKED(@type@, vector_size_bytes) { + @vtype512@ a = @vpre512@_loadu_@vsuf@(&ip1[i]); + @vtype512@ c = @vpre512@_@VOP@_@vsuf@(a, a); + @vpre512@_store_@vsuf@(&op[i], c); + } + } + else { + LOOP_BLOCKED(@type@, vector_size_bytes) { + @vtype512@ a = @vpre512@_loadu_@vsuf@(&ip1[i]); + @vtype512@ b = @vpre512@_loadu_@vsuf@(&ip2[i]); + @vtype512@ c = @vpre512@_@VOP@_@vsuf@(a, b); + @vpre512@_store_@vsuf@(&op[i], c); + } + } + } +#elif defined NPY_HAVE_AVX2 + const npy_intp vector_size_bytes = 32; + LOOP_BLOCK_ALIGN_VAR(op, @type@, vector_size_bytes) + op[i] = ip1[i] @OP@ ip2[i]; + /* lots of specializations, to squeeze out max performance */ + if (npy_is_aligned(&ip1[i], vector_size_bytes) && + npy_is_aligned(&ip2[i], vector_size_bytes)) { + if (ip1 == ip2) { + LOOP_BLOCKED(@type@, vector_size_bytes) { + @vtype256@ a = @vpre256@_load_@vsuf@(&ip1[i]); + @vtype256@ c = @vpre256@_@VOP@_@vsuf@(a, a); + @vpre256@_store_@vsuf@(&op[i], c); + } + } + else { + LOOP_BLOCKED(@type@, vector_size_bytes) { + @vtype256@ a = @vpre256@_load_@vsuf@(&ip1[i]); + @vtype256@ b = @vpre256@_load_@vsuf@(&ip2[i]); + @vtype256@ c = @vpre256@_@VOP@_@vsuf@(a, b); + @vpre256@_store_@vsuf@(&op[i], c); + } + } + } + else if (npy_is_aligned(&ip1[i], vector_size_bytes)) { + LOOP_BLOCKED(@type@, vector_size_bytes) { + @vtype256@ a = @vpre256@_load_@vsuf@(&ip1[i]); + @vtype256@ b = @vpre256@_loadu_@vsuf@(&ip2[i]); + @vtype256@ c = @vpre256@_@VOP@_@vsuf@(a, b); + @vpre256@_store_@vsuf@(&op[i], c); + } + } + else if (npy_is_aligned(&ip2[i], vector_size_bytes)) { + LOOP_BLOCKED(@type@, vector_size_bytes) { + @vtype256@ a = @vpre256@_loadu_@vsuf@(&ip1[i]); + @vtype256@ b = @vpre256@_load_@vsuf@(&ip2[i]); + @vtype256@ c = @vpre256@_@VOP@_@vsuf@(a, b); + @vpre256@_store_@vsuf@(&op[i], c); + } + } + else { + if (ip1 == ip2) { + LOOP_BLOCKED(@type@, vector_size_bytes) { + @vtype256@ a = @vpre256@_loadu_@vsuf@(&ip1[i]); + @vtype256@ c = @vpre256@_@VOP@_@vsuf@(a, a); + @vpre256@_store_@vsuf@(&op[i], c); + } + } + else { + LOOP_BLOCKED(@type@, vector_size_bytes) { + @vtype256@ a = @vpre256@_loadu_@vsuf@(&ip1[i]); + @vtype256@ b = @vpre256@_loadu_@vsuf@(&ip2[i]); + @vtype256@ c = @vpre256@_@VOP@_@vsuf@(a, b); + @vpre256@_store_@vsuf@(&op[i], c); + } + } + } +#else + const npy_intp vector_size_bytes = 16; + LOOP_BLOCK_ALIGN_VAR(op, @type@, vector_size_bytes) + op[i] = ip1[i] @OP@ ip2[i]; + /* lots of specializations, to squeeze out max performance */ + if (npy_is_aligned(&ip1[i], vector_size_bytes) && + npy_is_aligned(&ip2[i], vector_size_bytes)) { + if (ip1 == ip2) { + LOOP_BLOCKED(@type@, vector_size_bytes) { + @vtype@ a = @vpre@_load_@vsuf@(&ip1[i]); + @vtype@ c = @vpre@_@VOP@_@vsuf@(a, a); + @vpre@_store_@vsuf@(&op[i], c); + } + } + else { + LOOP_BLOCKED(@type@, vector_size_bytes) { + @vtype@ a = @vpre@_load_@vsuf@(&ip1[i]); + @vtype@ b = @vpre@_load_@vsuf@(&ip2[i]); + @vtype@ c = @vpre@_@VOP@_@vsuf@(a, b); + @vpre@_store_@vsuf@(&op[i], c); + } + } + } + else if (npy_is_aligned(&ip1[i], vector_size_bytes)) { + LOOP_BLOCKED(@type@, vector_size_bytes) { + @vtype@ a = @vpre@_load_@vsuf@(&ip1[i]); + @vtype@ b = @vpre@_loadu_@vsuf@(&ip2[i]); + @vtype@ c = @vpre@_@VOP@_@vsuf@(a, b); + @vpre@_store_@vsuf@(&op[i], c); + } + } + else if (npy_is_aligned(&ip2[i], vector_size_bytes)) { + LOOP_BLOCKED(@type@, vector_size_bytes) { + @vtype@ a = @vpre@_loadu_@vsuf@(&ip1[i]); + @vtype@ b = @vpre@_load_@vsuf@(&ip2[i]); + @vtype@ c = @vpre@_@VOP@_@vsuf@(a, b); + @vpre@_store_@vsuf@(&op[i], c); + } + } + else { + if (ip1 == ip2) { + LOOP_BLOCKED(@type@, vector_size_bytes) { + @vtype@ a = @vpre@_loadu_@vsuf@(&ip1[i]); + @vtype@ c = @vpre@_@VOP@_@vsuf@(a, a); + @vpre@_store_@vsuf@(&op[i], c); + } + } + else { + LOOP_BLOCKED(@type@, vector_size_bytes) { + @vtype@ a = @vpre@_loadu_@vsuf@(&ip1[i]); + @vtype@ b = @vpre@_loadu_@vsuf@(&ip2[i]); + @vtype@ c = @vpre@_@VOP@_@vsuf@(a, b); + @vpre@_store_@vsuf@(&op[i], c); + } + } + } +#endif + LOOP_BLOCKED_END { + op[i] = ip1[i] @OP@ ip2[i]; + } +} + +static void +sse2_binary_scalar1_@kind@_@TYPE@(@type@ * op, @type@ * ip1, @type@ * ip2, npy_intp n) +{ +#ifdef NPY_HAVE_AVX512F + const npy_intp vector_size_bytes = 64; + const @vtype512@ a = @vpre512@_set1_@vsuf@(ip1[0]); + LOOP_BLOCK_ALIGN_VAR(op, @type@, vector_size_bytes) + op[i] = ip1[0] @OP@ ip2[i]; + if (npy_is_aligned(&ip2[i], vector_size_bytes)) { + LOOP_BLOCKED(@type@, vector_size_bytes) { + @vtype512@ b = @vpre512@_load_@vsuf@(&ip2[i]); + @vtype512@ c = @vpre512@_@VOP@_@vsuf@(a, b); + @vpre512@_store_@vsuf@(&op[i], c); + } + } + else { + LOOP_BLOCKED(@type@, vector_size_bytes) { + @vtype512@ b = @vpre512@_loadu_@vsuf@(&ip2[i]); + @vtype512@ c = @vpre512@_@VOP@_@vsuf@(a, b); + @vpre512@_store_@vsuf@(&op[i], c); + } + } + + +#elif defined NPY_HAVE_AVX2 + const npy_intp vector_size_bytes = 32; + const @vtype256@ a = @vpre256@_set1_@vsuf@(ip1[0]); + LOOP_BLOCK_ALIGN_VAR(op, @type@, vector_size_bytes) + op[i] = ip1[0] @OP@ ip2[i]; + if (npy_is_aligned(&ip2[i], vector_size_bytes)) { + LOOP_BLOCKED(@type@, vector_size_bytes) { + @vtype256@ b = @vpre256@_load_@vsuf@(&ip2[i]); + @vtype256@ c = @vpre256@_@VOP@_@vsuf@(a, b); + @vpre256@_store_@vsuf@(&op[i], c); + } + } + else { + LOOP_BLOCKED(@type@, vector_size_bytes) { + @vtype256@ b = @vpre256@_loadu_@vsuf@(&ip2[i]); + @vtype256@ c = @vpre256@_@VOP@_@vsuf@(a, b); + @vpre256@_store_@vsuf@(&op[i], c); + } + } +#else + const npy_intp vector_size_bytes = 16; + const @vtype@ a = @vpre@_set1_@vsuf@(ip1[0]); + LOOP_BLOCK_ALIGN_VAR(op, @type@, vector_size_bytes) + op[i] = ip1[0] @OP@ ip2[i]; + if (npy_is_aligned(&ip2[i], vector_size_bytes)) { + LOOP_BLOCKED(@type@, vector_size_bytes) { + @vtype@ b = @vpre@_load_@vsuf@(&ip2[i]); + @vtype@ c = @vpre@_@VOP@_@vsuf@(a, b); + @vpre@_store_@vsuf@(&op[i], c); + } + } + else { + LOOP_BLOCKED(@type@, vector_size_bytes) { + @vtype@ b = @vpre@_loadu_@vsuf@(&ip2[i]); + @vtype@ c = @vpre@_@VOP@_@vsuf@(a, b); + @vpre@_store_@vsuf@(&op[i], c); + } + } +#endif + LOOP_BLOCKED_END { + op[i] = ip1[0] @OP@ ip2[i]; + } +} + +static void +sse2_binary_scalar2_@kind@_@TYPE@(@type@ * op, @type@ * ip1, @type@ * ip2, npy_intp n) +{ +#ifdef NPY_HAVE_AVX512F + const npy_intp vector_size_bytes = 64; + const @vtype512@ b = @vpre512@_set1_@vsuf@(ip2[0]); + LOOP_BLOCK_ALIGN_VAR(op, @type@, vector_size_bytes) + op[i] = ip1[i] @OP@ ip2[0]; + if (npy_is_aligned(&ip1[i], vector_size_bytes)) { + LOOP_BLOCKED(@type@, vector_size_bytes) { + @vtype512@ a = @vpre512@_load_@vsuf@(&ip1[i]); + @vtype512@ c = @vpre512@_@VOP@_@vsuf@(a, b); + @vpre512@_store_@vsuf@(&op[i], c); + } + } + else { + LOOP_BLOCKED(@type@, vector_size_bytes) { + @vtype512@ a = @vpre512@_loadu_@vsuf@(&ip1[i]); + @vtype512@ c = @vpre512@_@VOP@_@vsuf@(a, b); + @vpre512@_store_@vsuf@(&op[i], c); + } + } + +#elif defined NPY_HAVE_AVX2 + const npy_intp vector_size_bytes = 32; + const @vtype256@ b = @vpre256@_set1_@vsuf@(ip2[0]); + LOOP_BLOCK_ALIGN_VAR(op, @type@, vector_size_bytes) + op[i] = ip1[i] @OP@ ip2[0]; + if (npy_is_aligned(&ip1[i], vector_size_bytes)) { + LOOP_BLOCKED(@type@, vector_size_bytes) { + @vtype256@ a = @vpre256@_load_@vsuf@(&ip1[i]); + @vtype256@ c = @vpre256@_@VOP@_@vsuf@(a, b); + @vpre256@_store_@vsuf@(&op[i], c); + } + } + else { + LOOP_BLOCKED(@type@, vector_size_bytes) { + @vtype256@ a = @vpre256@_loadu_@vsuf@(&ip1[i]); + @vtype256@ c = @vpre256@_@VOP@_@vsuf@(a, b); + @vpre256@_store_@vsuf@(&op[i], c); + } + } +#else + const npy_intp vector_size_bytes = 16; + const @vtype@ b = @vpre@_set1_@vsuf@(ip2[0]); + LOOP_BLOCK_ALIGN_VAR(op, @type@, vector_size_bytes) + op[i] = ip1[i] @OP@ ip2[0]; + if (npy_is_aligned(&ip1[i], vector_size_bytes)) { + LOOP_BLOCKED(@type@, vector_size_bytes) { + @vtype@ a = @vpre@_load_@vsuf@(&ip1[i]); + @vtype@ c = @vpre@_@VOP@_@vsuf@(a, b); + @vpre@_store_@vsuf@(&op[i], c); + } + } + else { + LOOP_BLOCKED(@type@, vector_size_bytes) { + @vtype@ a = @vpre@_loadu_@vsuf@(&ip1[i]); + @vtype@ c = @vpre@_@VOP@_@vsuf@(a, b); + @vpre@_store_@vsuf@(&op[i], c); + } + } +#endif + LOOP_BLOCKED_END { + op[i] = ip1[i] @OP@ ip2[0]; + } +} + +/**end repeat1**/ +/**end repeat**/ + +#else // NPY_HAVE_SSE2 + +/**begin repeat + * #type = npy_float, npy_double# + * #TYPE = FLOAT, DOUBLE# + * #sfx = f32, f64# + * #CHK = , _F64# + */ +#if NPY_SIMD@CHK@ +/**begin repeat1 +* Arithmetic +* # kind = add, subtract, multiply, divide# +* # OP = +, -, *, /# +* # VOP = add, sub, mul, div# +*/ + +static void +simd_binary_@kind@_@TYPE@(@type@ * op, @type@ * ip1, @type@ * ip2, npy_intp n) +{ + LOOP_BLOCK_ALIGN_VAR(op, @type@, NPY_SIMD_WIDTH) { + op[i] = ip1[i] @OP@ ip2[i]; + } + /* lots of specializations, to squeeze out max performance */ + if (ip1 == ip2) { + LOOP_BLOCKED(@type@, NPY_SIMD_WIDTH) { + npyv_@sfx@ a = npyv_load_@sfx@(&ip1[i]); + npyv_@sfx@ c = npyv_@VOP@_@sfx@(a, a); + npyv_store_@sfx@(&op[i], c); + } + } + else { + LOOP_BLOCKED(@type@, NPY_SIMD_WIDTH) { + npyv_@sfx@ a = npyv_load_@sfx@(&ip1[i]); + npyv_@sfx@ b = npyv_load_@sfx@(&ip2[i]); + npyv_@sfx@ c = npyv_@VOP@_@sfx@(a, b); + npyv_store_@sfx@(&op[i], c); + } + } + LOOP_BLOCKED_END { + op[i] = ip1[i] @OP@ ip2[i]; + } +} + +static void +simd_binary_scalar1_@kind@_@TYPE@(@type@ * op, @type@ * ip1, @type@ * ip2, npy_intp n) +{ + const npyv_@sfx@ v1 = npyv_setall_@sfx@(ip1[0]); + LOOP_BLOCK_ALIGN_VAR(op, @type@, NPY_SIMD_WIDTH) { + op[i] = ip1[0] @OP@ ip2[i]; + } + LOOP_BLOCKED(@type@, NPY_SIMD_WIDTH) { + npyv_@sfx@ v2 = npyv_load_@sfx@(&ip2[i]); + npyv_@sfx@ v3 = npyv_@VOP@_@sfx@(v1, v2); + npyv_store_@sfx@(&op[i], v3); + } + LOOP_BLOCKED_END { + op[i] = ip1[0] @OP@ ip2[i]; + } +} + +static void +simd_binary_scalar2_@kind@_@TYPE@(@type@ * op, @type@ * ip1, @type@ * ip2, npy_intp n) +{ + const npyv_@sfx@ v2 = npyv_setall_@sfx@(ip2[0]); + LOOP_BLOCK_ALIGN_VAR(op, @type@, NPY_SIMD_WIDTH) { + op[i] = ip1[i] @OP@ ip2[0]; + } + LOOP_BLOCKED(@type@, NPY_SIMD_WIDTH) { + npyv_@sfx@ v1 = npyv_load_@sfx@(&ip1[i]); + npyv_@sfx@ v3 = npyv_@VOP@_@sfx@(v1, v2); + npyv_store_@sfx@(&op[i], v3); + } + LOOP_BLOCKED_END { + op[i] = ip1[i] @OP@ ip2[0]; + } +} +/**end repeat1**/ +#endif /* NPY_SIMD@CHK@ */ +/**end repeat**/ +#endif // NPY_HAVE_SSE2 + +/**begin repeat + * Float types + * #type = npy_float, npy_double, npy_longdouble# + * #TYPE = FLOAT, DOUBLE, LONGDOUBLE# + * #vector = 1, 1, 0# + * #VECTOR = NPY_SIMD, NPY_SIMD_F64, 0 # + */ +/**begin repeat1 + * Arithmetic + * # kind = add, subtract, multiply, divide# + */ +static NPY_INLINE int +run_binary_simd_@kind@_@TYPE@(char **args, npy_intp const *dimensions, npy_intp const *steps) +{ +#if @vector@ && defined NPY_HAVE_SSE2 + @type@ * ip1 = (@type@ *)args[0]; + @type@ * ip2 = (@type@ *)args[1]; + @type@ * op = (@type@ *)args[2]; + npy_intp n = dimensions[0]; +#if defined NPY_HAVE_AVX512F + const npy_uintp vector_size_bytes = 64; +#elif defined NPY_HAVE_AVX2 + const npy_uintp vector_size_bytes = 32; +#else + const npy_uintp vector_size_bytes = 32; +#endif + /* argument one scalar */ + if (IS_BLOCKABLE_BINARY_SCALAR1(sizeof(@type@), vector_size_bytes)) { + sse2_binary_scalar1_@kind@_@TYPE@(op, ip1, ip2, n); + return 1; + } + /* argument two scalar */ + else if (IS_BLOCKABLE_BINARY_SCALAR2(sizeof(@type@), vector_size_bytes)) { + sse2_binary_scalar2_@kind@_@TYPE@(op, ip1, ip2, n); + return 1; + } + else if (IS_BLOCKABLE_BINARY(sizeof(@type@), vector_size_bytes)) { + sse2_binary_@kind@_@TYPE@(op, ip1, ip2, n); + return 1; + } +#elif @VECTOR@ + @type@ * ip1 = (@type@ *)args[0]; + @type@ * ip2 = (@type@ *)args[1]; + @type@ * op = (@type@ *)args[2]; + npy_intp n = dimensions[0]; + /* argument one scalar */ + if (IS_BLOCKABLE_BINARY_SCALAR1(sizeof(@type@), NPY_SIMD_WIDTH)) { + simd_binary_scalar1_@kind@_@TYPE@(op, ip1, ip2, n); + return 1; + } + /* argument two scalar */ + else if (IS_BLOCKABLE_BINARY_SCALAR2(sizeof(@type@), NPY_SIMD_WIDTH)) { + simd_binary_scalar2_@kind@_@TYPE@(op, ip1, ip2, n); + return 1; + } + else if (IS_BLOCKABLE_BINARY(sizeof(@type@), NPY_SIMD_WIDTH)) { + simd_binary_@kind@_@TYPE@(op, ip1, ip2, n); + return 1; + } +#endif + return 0; +} +/**end repeat1**/ +/**end repeat**/ + +/******************************************************************************** + ** Defining ufunc inner functions + ********************************************************************************/ +/**begin repeat + * Float types + * #type = npy_float, npy_double# + * #TYPE = FLOAT, DOUBLE# + * #c = f, # + * #C = F, # + */ +/**begin repeat1 + * Arithmetic + * # kind = add, subtract, multiply, divide# + * # OP = +, -, *, /# + * # PW = 1, 0, 0, 0# + */ +NPY_NO_EXPORT void NPY_CPU_DISPATCH_CURFX(@TYPE@_@kind@) +(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) +{ + if (IS_BINARY_REDUCE) { +#if @PW@ + @type@ * iop1 = (@type@ *)args[0]; + npy_intp n = dimensions[0]; + + *iop1 @OP@= @TYPE@_pairwise_sum(args[1], n, steps[1]); +#else + BINARY_REDUCE_LOOP(@type@) { + io1 @OP@= *(@type@ *)ip2; + } + *((@type@ *)iop1) = io1; +#endif + } + else if (!run_binary_simd_@kind@_@TYPE@(args, dimensions, steps)) { + BINARY_LOOP { + const @type@ in1 = *(@type@ *)ip1; + const @type@ in2 = *(@type@ *)ip2; + *((@type@ *)op1) = in1 @OP@ in2; + } + } +} +/**end repeat1**/ +/**end repeat**/ + +//############################################################################### +//## Complex Single/Double precision +//############################################################################### +/******************************************************************************** + ** Defining the SIMD kernels + ********************************************************************************/ +#if !defined(_MSC_VER) && defined(NPY_HAVE_AVX512F) + /** + * For somehow MSVC commit aggressive optimization lead + * to raises 'RuntimeWarning: invalid value encountered in multiply' + * + * the issue mainly caused by '_mm512_maskz_loadu_ps', we need to + * investigate about it while moving to NPYV. + */ + #define AVX512F_NOMSVC +#endif + +#ifdef AVX512F_NOMSVC +NPY_FINLINE __mmask16 +avx512_get_full_load_mask_ps(void) +{ + return 0xFFFF; +} + +NPY_FINLINE __mmask8 +avx512_get_full_load_mask_pd(void) +{ + return 0xFF; +} +NPY_FINLINE __m512 +avx512_masked_load_ps(__mmask16 mask, npy_float* addr) +{ + return _mm512_maskz_loadu_ps(mask, (__m512 *)addr); +} + +NPY_FINLINE __m512d +avx512_masked_load_pd(__mmask8 mask, npy_double* addr) +{ + return _mm512_maskz_loadu_pd(mask, (__m512d *)addr); +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F __mmask16 +avx512_get_partial_load_mask_ps(const npy_int num_elem, const npy_int total_elem) +{ + return (0x0001 << num_elem) - 0x0001; +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F __mmask8 +avx512_get_partial_load_mask_pd(const npy_int num_elem, const npy_int total_elem) +{ + return (0x01 << num_elem) - 0x01; +} +/**begin repeat + * #vsub = ps, pd# + * #type= npy_float, npy_double# + * #epi_vsub = epi32, epi64# + * #vtype = __m512, __m512d# + * #mask = __mmask16, __mmask8# + * #and_const = 0x7fffffff, 0x7fffffffffffffffLL# + * #neg_mask = 0x80000000, 0x8000000000000000# + * #perm_ = 0xb1, 0x55# + * #cmpx_img_mask = 0xAAAA, 0xAA# + * #cmpx_re_mask = 0x5555, 0x55# + * #INF = NPY_INFINITYF, NPY_INFINITY# + * #NAN = NPY_NANF, NPY_NAN# + */ +NPY_FINLINE @vtype@ +avx512_hadd_@vsub@(const @vtype@ x) +{ + return _mm512_add_@vsub@(x, _mm512_permute_@vsub@(x, @perm_@)); +} + +NPY_FINLINE @vtype@ +avx512_hsub_@vsub@(const @vtype@ x) +{ + return _mm512_sub_@vsub@(x, _mm512_permute_@vsub@(x, @perm_@)); +} +NPY_FINLINE @vtype@ +avx512_cmul_@vsub@(@vtype@ x1, @vtype@ x2) +{ + // x1 = r1, i1 + // x2 = r2, i2 + @vtype@ x3 = _mm512_permute_@vsub@(x2, @perm_@); // i2, r2 + @vtype@ x12 = _mm512_mul_@vsub@(x1, x2); // r1*r2, i1*i2 + @vtype@ x13 = _mm512_mul_@vsub@(x1, x3); // r1*i2, r2*i1 + @vtype@ outreal = avx512_hsub_@vsub@(x12); // r1*r2 - i1*i2, r1*r2 - i1*i2 + @vtype@ outimg = avx512_hadd_@vsub@(x13); // r1*i2 + i1*r2, r1*i2 + i1*r2 + return _mm512_mask_blend_@vsub@(@cmpx_img_mask@, outreal, outimg); +} +/**end repeat**/ +#endif + +/**begin repeat + * #TYPE = CFLOAT, CDOUBLE# + * #type = npy_float, npy_double# + * #num_lanes = 16, 8# + * #vsuffix = ps, pd# + * #epi_vsub = epi32, epi64# + * #mask = __mmask16, __mmask8# + * #vtype = __m512, __m512d# + * #scale = 4, 8# + * #vindextype = __m512i, __m256i# + * #vindexload = _mm512_loadu_si512, _mm256_loadu_si256# + * #storemask = 0xFF, 0xF# + * #IS_FLOAT = 1, 0# + */ +/**begin repeat1 + * #func = add, subtract, multiply# + * #vectorf = _mm512_add, _mm512_sub, avx512_cmul# + */ +#if defined AVX512F_NOMSVC +static NPY_INLINE void +AVX512F_@func@_@TYPE@(char **args, const npy_intp *dimensions, const npy_intp *steps) +{ + const npy_intp array_size = dimensions[0]; + npy_intp num_remaining_elements = 2*array_size; + @type@* ip1 = (@type@*) args[0]; + @type@* ip2 = (@type@*) args[1]; + @type@* op = (@type@*) args[2]; + + @mask@ load_mask = avx512_get_full_load_mask_@vsuffix@(); + + while (num_remaining_elements > 0) { + if (num_remaining_elements < @num_lanes@) { + load_mask = avx512_get_partial_load_mask_@vsuffix@( + num_remaining_elements, @num_lanes@); + } + @vtype@ x1, x2; + x1 = avx512_masked_load_@vsuffix@(load_mask, ip1); + x2 = avx512_masked_load_@vsuffix@(load_mask, ip2); + + @vtype@ out = @vectorf@_@vsuffix@(x1, x2); + + _mm512_mask_storeu_@vsuffix@(op, load_mask, out); + + ip1 += @num_lanes@; + ip2 += @num_lanes@; + op += @num_lanes@; + num_remaining_elements -= @num_lanes@; + } +} +#endif // AVX512F_NOMSVC +/**end repeat1**/ +/**end repeat**/ + +/**begin repeat + * #TYPE = CFLOAT, CDOUBLE# + * #type= npy_float, npy_double# + * #esize = 8, 16# + */ +/**begin repeat1 + * #func = add, subtract, multiply# + */ +static NPY_INLINE int +run_binary_avx512f_@func@_@TYPE@(char **args, const npy_intp *dimensions, const npy_intp *steps) +{ +#if defined AVX512F_NOMSVC + if (IS_BINARY_STRIDE_ONE(@esize@, 64)) { + AVX512F_@func@_@TYPE@(args, dimensions, steps); + return 1; + } + else + return 0; +#endif + return 0; +} +/**end repeat1**/ +/**end repeat**/ + +/******************************************************************************** + ** Defining ufunc inner functions + ********************************************************************************/ +/**begin repeat + * complex types + * #TYPE = CFLOAT, CDOUBLE# + * #ftype = npy_float, npy_double# + * #c = f, # + * #C = F, # + */ +/**begin repeat1 + * arithmetic + * #kind = add, subtract# + * #OP = +, -# + * #PW = 1, 0# + */ +NPY_NO_EXPORT void NPY_CPU_DISPATCH_CURFX(@TYPE@_@kind@) +(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) +{ + // Parenthesis around @PW@ tells clang dead code is intentional + if (IS_BINARY_REDUCE && (@PW@)) { + npy_intp n = dimensions[0]; + @ftype@ * or = ((@ftype@ *)args[0]); + @ftype@ * oi = ((@ftype@ *)args[0]) + 1; + @ftype@ rr, ri; + + @TYPE@_pairwise_sum(&rr, &ri, args[1], n * 2, steps[1] / 2); + *or @OP@= rr; + *oi @OP@= ri; + return; + } + if (!run_binary_avx512f_@kind@_@TYPE@(args, dimensions, steps)) { + BINARY_LOOP { + const @ftype@ in1r = ((@ftype@ *)ip1)[0]; + const @ftype@ in1i = ((@ftype@ *)ip1)[1]; + const @ftype@ in2r = ((@ftype@ *)ip2)[0]; + const @ftype@ in2i = ((@ftype@ *)ip2)[1]; + ((@ftype@ *)op1)[0] = in1r @OP@ in2r; + ((@ftype@ *)op1)[1] = in1i @OP@ in2i; + } + } +} +/**end repeat1**/ + +NPY_NO_EXPORT void NPY_CPU_DISPATCH_CURFX(@TYPE@_multiply) +(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) +{ + if (!run_binary_avx512f_multiply_@TYPE@(args, dimensions, steps)) { + BINARY_LOOP { + const @ftype@ in1r = ((@ftype@ *)ip1)[0]; + const @ftype@ in1i = ((@ftype@ *)ip1)[1]; + const @ftype@ in2r = ((@ftype@ *)ip2)[0]; + const @ftype@ in2i = ((@ftype@ *)ip2)[1]; + ((@ftype@ *)op1)[0] = in1r*in2r - in1i*in2i; + ((@ftype@ *)op1)[1] = in1r*in2i + in1i*in2r; + } + } +} +/**end repeat**/ diff --git a/numpy/core/src/umath/loops_arithmetic.dispatch.c.src b/numpy/core/src/umath/loops_arithmetic.dispatch.c.src new file mode 100644 index 000000000000..1ddf7c3b1a6f --- /dev/null +++ b/numpy/core/src/umath/loops_arithmetic.dispatch.c.src @@ -0,0 +1,262 @@ +/*@targets + ** $maxopt baseline + ** sse2 sse41 avx2 avx512f avx512_skx + ** vsx2 + ** neon + **/ +#define _UMATHMODULE +#define _MULTIARRAYMODULE +#define NPY_NO_DEPRECATED_API NPY_API_VERSION + +#include "simd/simd.h" +#include "loops_utils.h" +#include "loops.h" +#include "lowlevel_strided_loops.h" +// Provides the various *_LOOP macros +#include "fast_loop_macros.h" + +//############################################################################### +//## Division +//############################################################################### +/******************************************************************************** + ** Defining the SIMD kernels + * + * Floor division of signed is based on T. Granlund and P. L. Montgomery + * "Division by invariant integers using multiplication(see [Figure 6.1] + * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.1.2556)" + * For details on TRUNC division see simd/intdiv.h for more clarification + *********************************************************************************** + ** Figure 6.1: Signed division by run-time invariant divisor, rounded towards -INF + *********************************************************************************** + * For q = FLOOR(a/d), all sword: + * sword -dsign = SRL(d, N - 1); + * uword -nsign = (n < -dsign); + * uword -qsign = EOR(-nsign, -dsign); + * q = TRUNC((n - (-dsign ) + (-nsign))/d) - (-qsign); + ********************************************************************************/ + +#if NPY_SIMD +/**begin repeat + * Signed types + * #sfx = s8, s16, s32, s64# + * #len = 8, 16, 32, 64# + */ +static NPY_INLINE void +simd_divide_by_scalar_contig_@sfx@(char **args, npy_intp len) +{ + npyv_lanetype_@sfx@ *src = (npyv_lanetype_@sfx@ *) args[0]; + npyv_lanetype_@sfx@ scalar = *(npyv_lanetype_@sfx@ *) args[1]; + npyv_lanetype_@sfx@ *dst = (npyv_lanetype_@sfx@ *) args[2]; + const int vstep = npyv_nlanes_@sfx@; + const npyv_@sfx@x3 divisor = npyv_divisor_@sfx@(scalar); + + if (scalar == -1) { + npyv_b@len@ noverflow = npyv_cvt_b@len@_@sfx@(npyv_setall_@sfx@(-1)); + npyv_@sfx@ vzero = npyv_zero_@sfx@(); + for (; len >= vstep; len -= vstep, src += vstep, dst += vstep) { + npyv_@sfx@ a = npyv_load_@sfx@(src); + npyv_b@len@ gt_min = npyv_cmpgt_@sfx@(a, npyv_setall_@sfx@(NPY_MIN_INT@len@)); + noverflow = npyv_and_b@len@(noverflow, gt_min); + npyv_@sfx@ neg = npyv_ifsub_@sfx@(gt_min, vzero, a, vzero); + npyv_store_@sfx@(dst, neg); + } + + int raise_err = npyv_tobits_b@len@(npyv_not_b@len@(noverflow)) != 0; + for (; len > 0; --len, ++src, ++dst) { + npyv_lanetype_@sfx@ a = *src; + if (a == NPY_MIN_INT@len@) { + raise_err = 1; + *dst = 0; + } else { + *dst = -a; + } + } + if (raise_err) { + npy_set_floatstatus_divbyzero(); + } + } else { + for (; len >= vstep; len -= vstep, src += vstep, dst += vstep) { + npyv_@sfx@ nsign_d = npyv_setall_@sfx@(scalar < 0); + npyv_@sfx@ a = npyv_load_@sfx@(src); + npyv_@sfx@ nsign_a = npyv_cvt_@sfx@_b@len@(npyv_cmplt_@sfx@(a, nsign_d)); + nsign_a = npyv_and_@sfx@(nsign_a, npyv_setall_@sfx@(1)); + npyv_@sfx@ diff_sign = npyv_sub_@sfx@(nsign_a, nsign_d); + npyv_@sfx@ to_ninf = npyv_xor_@sfx@(nsign_a, nsign_d); + npyv_@sfx@ trunc = npyv_divc_@sfx@(npyv_add_@sfx@(a, diff_sign), divisor); + npyv_@sfx@ floor = npyv_sub_@sfx@(trunc, to_ninf); + npyv_store_@sfx@(dst, floor); + } + + for (; len > 0; --len, ++src, ++dst) { + const npyv_lanetype_@sfx@ a = *src; + npyv_lanetype_@sfx@ r = a / scalar; + // Negative quotients needs to be rounded down + if (((a > 0) != (scalar > 0)) && ((r * scalar) != a)) { + r--; + } + *dst = r; + } + } + npyv_cleanup(); +} +/**end repeat**/ + +/**begin repeat + * Unsigned types + * #sfx = u8, u16, u32, u64# + * #len = 8, 16, 32, 64# + */ +static NPY_INLINE void +simd_divide_by_scalar_contig_@sfx@(char **args, npy_intp len) +{ + npyv_lanetype_@sfx@ *src = (npyv_lanetype_@sfx@ *) args[0]; + npyv_lanetype_@sfx@ scalar = *(npyv_lanetype_@sfx@ *) args[1]; + npyv_lanetype_@sfx@ *dst = (npyv_lanetype_@sfx@ *) args[2]; + const int vstep = npyv_nlanes_@sfx@; + const npyv_@sfx@x3 divisor = npyv_divisor_@sfx@(scalar); + + for (; len >= vstep; len -= vstep, src += vstep, dst += vstep) { + npyv_@sfx@ a = npyv_load_@sfx@(src); + npyv_@sfx@ c = npyv_divc_@sfx@(a, divisor); + npyv_store_@sfx@(dst, c); + } + + for (; len > 0; --len, ++src, ++dst) { + const npyv_lanetype_@sfx@ a = *src; + *dst = a / scalar; + } + npyv_cleanup(); +} +/**end repeat**/ +#endif + +/******************************************************************************** + ** Defining ufunc inner functions + ********************************************************************************/ + +/**begin repeat + * Signed types + * #type = npy_byte, npy_short, npy_int, npy_long, npy_longlong# + * #TYPE = BYTE, SHORT, INT, LONG, LONGLONG# + */ +#undef TO_SIMD_SFX +#if 0 +/**begin repeat1 + * #len = 8, 16, 32, 64# + */ +#elif NPY_BITSOF_@TYPE@ == @len@ + #define TO_SIMD_SFX(X) X##_s@len@ +/**end repeat1**/ +#endif + +#if NPY_BITSOF_@TYPE@ == 64 && !defined(NPY_HAVE_VSX4) && (defined(NPY_HAVE_VSX) || defined(NPY_HAVE_NEON)) + #undef TO_SIMD_SFX +#endif + +NPY_FINLINE @type@ floor_div_@TYPE@(const @type@ n, const @type@ d) +{ + /* + * FIXME: On x86 at least, dividing the smallest representable integer + * by -1 causes a SIFGPE (division overflow). We treat this case here + * (to avoid a SIGFPE crash at python level), but a good solution would + * be to treat integer division problems separately from FPU exceptions + * (i.e. a different approach than npy_set_floatstatus_divbyzero()). + */ + if (NPY_UNLIKELY(d == 0 || (n == NPY_MIN_@TYPE@ && d == -1))) { + npy_set_floatstatus_divbyzero(); + return 0; + } + @type@ r = n / d; + // Negative quotients needs to be rounded down + if (((n > 0) != (d > 0)) && ((r * d) != n)) { + r--; + } + return r; +} + +NPY_NO_EXPORT void NPY_CPU_DISPATCH_CURFX(@TYPE@_divide) +(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) +{ + if (IS_BINARY_REDUCE) { + BINARY_REDUCE_LOOP(@type@) { + io1 = floor_div_@TYPE@(io1, *(@type@*)ip2); + } + *((@type@ *)iop1) = io1; + } +#if NPY_SIMD && defined(TO_SIMD_SFX) + // for contiguous block of memory, divisor is a scalar and not 0 + else if (IS_BLOCKABLE_BINARY_SCALAR2(sizeof(@type@), NPY_SIMD_WIDTH) && + (*(@type@ *)args[1]) != 0) { + TO_SIMD_SFX(simd_divide_by_scalar_contig)(args, dimensions[0]); + } +#endif + else { + BINARY_LOOP { + *((@type@ *)op1) = floor_div_@TYPE@(*(@type@*)ip1, *(@type@*)ip2); + } + } +} +/**end repeat**/ + +/**begin repeat + * Unsigned types + * #type = npy_ubyte, npy_ushort, npy_uint, npy_ulong, npy_ulonglong# + * #TYPE = UBYTE, USHORT, UINT, ULONG, ULONGLONG# + * #STYPE = BYTE, SHORT, INT, LONG, LONGLONG# + */ +#undef TO_SIMD_SFX +#if 0 +/**begin repeat1 + * #len = 8, 16, 32, 64# + */ +#elif NPY_BITSOF_@STYPE@ == @len@ + #define TO_SIMD_SFX(X) X##_u@len@ +/**end repeat1**/ +#endif +/* + * For 64-bit division on Armv7, Aarch64, and IBM/Power, NPYV fall-backs to the scalar division + * because emulating multiply-high on these architectures is going to be expensive comparing + * to the native scalar dividers. + * Therefore it's better to disable NPYV in this special case to avoid any unnecessary shuffles. + * Power10(VSX4) is an exception here since it has native support for integer vector division, + * note neither infrastructure nor NPYV has supported VSX4 yet. + */ +#if NPY_BITSOF_@STYPE@ == 64 && !defined(NPY_HAVE_VSX4) && (defined(NPY_HAVE_VSX) || defined(NPY_HAVE_NEON)) + #undef TO_SIMD_SFX +#endif +NPY_NO_EXPORT void NPY_CPU_DISPATCH_CURFX(@TYPE@_divide) +(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) +{ + if (IS_BINARY_REDUCE) { + BINARY_REDUCE_LOOP(@type@) { + const @type@ d = *(@type@ *)ip2; + if (NPY_UNLIKELY(d == 0)) { + npy_set_floatstatus_divbyzero(); + io1 = 0; + } else { + io1 /= d; + } + } + *((@type@ *)iop1) = io1; + } +#if NPY_SIMD && defined(TO_SIMD_SFX) + // for contiguous block of memory, divisor is a scalar and not 0 + else if (IS_BLOCKABLE_BINARY_SCALAR2(sizeof(@type@), NPY_SIMD_WIDTH) && + (*(@type@ *)args[1]) != 0) { + TO_SIMD_SFX(simd_divide_by_scalar_contig)(args, dimensions[0]); + } +#endif + else { + BINARY_LOOP { + const @type@ in1 = *(@type@ *)ip1; + const @type@ in2 = *(@type@ *)ip2; + if (NPY_UNLIKELY(in2 == 0)) { + npy_set_floatstatus_divbyzero(); + *((@type@ *)op1) = 0; + } else{ + *((@type@ *)op1) = in1 / in2; + } + } + } +} +/**end repeat**/ diff --git a/numpy/core/src/umath/loops_exponent_log.dispatch.c.src b/numpy/core/src/umath/loops_exponent_log.dispatch.c.src new file mode 100644 index 000000000000..2dd43fb85362 --- /dev/null +++ b/numpy/core/src/umath/loops_exponent_log.dispatch.c.src @@ -0,0 +1,1346 @@ +/*@targets + ** $maxopt baseline + ** (avx2 fma3) avx512f avx512_skx + **/ + +#define _UMATHMODULE +#define _MULTIARRAYMODULE +#define NPY_NO_DEPRECATED_API NPY_API_VERSION + +#include <float.h> + +#include "numpy/npy_math.h" +#include "simd/simd.h" +#include "loops_utils.h" +#include "loops.h" +#include "lowlevel_strided_loops.h" +// Provides the various *_LOOP macros +#include "fast_loop_macros.h" +#include "npy_simd_data.h" + +// TODO: tweak & replace raw SIMD with NPYV + +/******************************************************************************** + ** bunch of helper functions used in ISA_exp/log_FLOAT + ********************************************************************************/ +#if !defined(_MSC_VER) && defined(NPY_HAVE_AVX512F) + /** + * For somehow MSVC commit aggressive optimization lead + * to raises 'RuntimeWarning: RuntimeWarning: overflow encountered in exp' + * + * the issue mainly caused by '_mm512_maskz_loadu_ps', we need to + * investigate about it while moving to NPYV. + */ + #define SIMD_AVX512F +#elif defined(NPY_HAVE_AVX2) && defined(NPY_HAVE_FMA3) + #define SIMD_AVX2_FMA3 +#endif +#if !defined(_MSC_VER) && defined(NPY_HAVE_AVX512_SKX) + #define SIMD_AVX512_SKX +#endif +#if defined(SIMD_AVX512F) && !(defined(__clang__) && (__clang_major__ < 10 || \ + (__clang_major__ == 10 && __clang_minor__ < 1))) + #define SIMD_AVX512F_NOCLANG_BUG +#endif + +#ifdef SIMD_AVX2_FMA3 + +NPY_FINLINE __m256 +fma_get_full_load_mask_ps(void) +{ + return _mm256_set1_ps(-1.0); +} + +NPY_FINLINE __m256i +fma_get_full_load_mask_pd(void) +{ + return _mm256_castpd_si256(_mm256_set1_pd(-1.0)); +} + +NPY_FINLINE __m256 +fma_get_partial_load_mask_ps(const npy_int num_elem, const npy_int num_lanes) +{ + float maskint[16] = {-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0, + 1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0}; + float* addr = maskint + num_lanes - num_elem; + return _mm256_loadu_ps(addr); +} + +NPY_FINLINE __m256i +fma_get_partial_load_mask_pd(const npy_int num_elem, const npy_int num_lanes) +{ + npy_int maskint[16] = {-1,-1,-1,-1,-1,-1,-1,-1,1,1,1,1,1,1,1,1}; + npy_int* addr = maskint + 2*num_lanes - 2*num_elem; + return _mm256_loadu_si256((__m256i*) addr); +} + +NPY_FINLINE __m256 +fma_masked_gather_ps(__m256 src, + npy_float* addr, + __m256i vindex, + __m256 mask) +{ + return _mm256_mask_i32gather_ps(src, addr, vindex, mask, 4); +} + +NPY_FINLINE __m256d +fma_masked_gather_pd(__m256d src, + npy_double* addr, + __m128i vindex, + __m256d mask) +{ + return _mm256_mask_i32gather_pd(src, addr, vindex, mask, 8); +} + +NPY_FINLINE __m256 +fma_masked_load_ps(__m256 mask, npy_float* addr) +{ + return _mm256_maskload_ps(addr, _mm256_cvtps_epi32(mask)); +} + +NPY_FINLINE __m256d +fma_masked_load_pd(__m256i mask, npy_double* addr) +{ + return _mm256_maskload_pd(addr, mask); +} + +NPY_FINLINE __m256 +fma_set_masked_lanes_ps(__m256 x, __m256 val, __m256 mask) +{ + return _mm256_blendv_ps(x, val, mask); +} + +NPY_FINLINE __m256d +fma_set_masked_lanes_pd(__m256d x, __m256d val, __m256d mask) +{ + return _mm256_blendv_pd(x, val, mask); +} + +NPY_FINLINE __m256 +fma_blend(__m256 x, __m256 y, __m256 ymask) +{ + return _mm256_blendv_ps(x, y, ymask); +} + +NPY_FINLINE __m256 +fma_invert_mask_ps(__m256 ymask) +{ + return _mm256_andnot_ps(ymask, _mm256_set1_ps(-1.0)); +} + +NPY_FINLINE __m256i +fma_invert_mask_pd(__m256i ymask) +{ + return _mm256_andnot_si256(ymask, _mm256_set1_epi32(0xFFFFFFFF)); +} + +NPY_FINLINE __m256 +fma_get_exponent(__m256 x) +{ + /* + * Special handling of denormals: + * 1) Multiply denormal elements with 2**100 (0x71800000) + * 2) Get the 8 bits of unbiased exponent + * 3) Subtract 100 from exponent of denormals + */ + + __m256 two_power_100 = _mm256_castsi256_ps(_mm256_set1_epi32(0x71800000)); + __m256 denormal_mask = _mm256_cmp_ps(x, _mm256_set1_ps(FLT_MIN), _CMP_LT_OQ); + __m256 normal_mask = _mm256_cmp_ps(x, _mm256_set1_ps(FLT_MIN), _CMP_GE_OQ); + + /* + * The volatile is probably unnecessary now since we compile clang with + * `-ftrapping-math`: https://github.com/numpy/numpy/issues/18005 + */ + volatile __m256 temp1 = _mm256_blendv_ps(x, _mm256_set1_ps(0.0f), normal_mask); + __m256 temp = _mm256_mul_ps(temp1, two_power_100); + x = _mm256_blendv_ps(x, temp, denormal_mask); + + __m256 exp = _mm256_cvtepi32_ps( + _mm256_sub_epi32( + _mm256_srli_epi32( + _mm256_castps_si256(x), 23),_mm256_set1_epi32(0x7E))); + + __m256 denorm_exp = _mm256_sub_ps(exp, _mm256_set1_ps(100.0f)); + return _mm256_blendv_ps(exp, denorm_exp, denormal_mask); +} + +NPY_FINLINE __m256 +fma_get_mantissa(__m256 x) +{ + /* + * Special handling of denormals: + * 1) Multiply denormal elements with 2**100 (0x71800000) + * 2) Get the 23 bits of mantissa + * 3) Mantissa for denormals is not affected by the multiplication + */ + + __m256 two_power_100 = _mm256_castsi256_ps(_mm256_set1_epi32(0x71800000)); + __m256 denormal_mask = _mm256_cmp_ps(x, _mm256_set1_ps(FLT_MIN), _CMP_LT_OQ); + __m256 normal_mask = _mm256_cmp_ps(x, _mm256_set1_ps(FLT_MIN), _CMP_GE_OQ); + + /* + * The volatile is probably unnecessary now since we compile clang with + * `-ftrapping-math`: https://github.com/numpy/numpy/issues/18005 + */ + volatile __m256 temp1 = _mm256_blendv_ps(x, _mm256_set1_ps(0.0f), normal_mask); + __m256 temp = _mm256_mul_ps(temp1, two_power_100); + x = _mm256_blendv_ps(x, temp, denormal_mask); + + __m256i mantissa_bits = _mm256_set1_epi32(0x7fffff); + __m256i exp_126_bits = _mm256_set1_epi32(126 << 23); + return _mm256_castsi256_ps( + _mm256_or_si256( + _mm256_and_si256( + _mm256_castps_si256(x), mantissa_bits), exp_126_bits)); +} + +NPY_FINLINE __m256 +fma_scalef_ps(__m256 poly, __m256 quadrant) +{ + /* + * Handle denormals (which occur when quadrant <= -125): + * 1) This function computes poly*(2^quad) by adding the exponent of + poly to quad + * 2) When quad <= -125, the output is a denormal and the above logic + breaks down + * 3) To handle such cases, we split quadrant: -125 + (quadrant + 125) + * 4) poly*(2^-125) is computed the usual way + * 5) 2^(quad-125) can be computed by: 2 << abs(quad-125) + * 6) The final div operation generates the denormal + */ + __m256 minquadrant = _mm256_set1_ps(-125.0f); + __m256 denormal_mask = _mm256_cmp_ps(quadrant, minquadrant, _CMP_LE_OQ); + if (_mm256_movemask_ps(denormal_mask) != 0x0000) { + __m256 quad_diff = _mm256_sub_ps(quadrant, minquadrant); + quad_diff = _mm256_sub_ps(_mm256_setzero_ps(), quad_diff); + quad_diff = _mm256_blendv_ps(_mm256_setzero_ps(), quad_diff, denormal_mask); + __m256i two_power_diff = _mm256_sllv_epi32( + _mm256_set1_epi32(1), _mm256_cvtps_epi32(quad_diff)); + quadrant = _mm256_max_ps(quadrant, minquadrant); //keep quadrant >= -126 + __m256i exponent = _mm256_slli_epi32(_mm256_cvtps_epi32(quadrant), 23); + poly = _mm256_castsi256_ps( + _mm256_add_epi32( + _mm256_castps_si256(poly), exponent)); + __m256 denorm_poly = _mm256_div_ps(poly, _mm256_cvtepi32_ps(two_power_diff)); + return _mm256_blendv_ps(poly, denorm_poly, denormal_mask); + } + else { + __m256i exponent = _mm256_slli_epi32(_mm256_cvtps_epi32(quadrant), 23); + poly = _mm256_castsi256_ps( + _mm256_add_epi32( + _mm256_castps_si256(poly), exponent)); + return poly; + } +} + +#endif // SIMD_AVX2_FMA3 + +#ifdef SIMD_AVX512F + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F __mmask16 +avx512_get_full_load_mask_ps(void) +{ + return 0xFFFF; +} + +NPY_FINLINE __mmask8 +avx512_get_full_load_mask_pd(void) +{ + return 0xFF; +} + +NPY_FINLINE __mmask16 +avx512_get_partial_load_mask_ps(const npy_int num_elem, const npy_int total_elem) +{ + return (0x0001 << num_elem) - 0x0001; +} + +NPY_FINLINE __mmask8 +avx512_get_partial_load_mask_pd(const npy_int num_elem, const npy_int total_elem) +{ + return (0x01 << num_elem) - 0x01; +} + +NPY_FINLINE __m512 +avx512_masked_gather_ps(__m512 src, + npy_float* addr, + __m512i vindex, + __mmask16 kmask) +{ + return _mm512_mask_i32gather_ps(src, kmask, vindex, addr, 4); +} + +NPY_FINLINE __m512d +avx512_masked_gather_pd(__m512d src, + npy_double* addr, + __m256i vindex, + __mmask8 kmask) +{ + return _mm512_mask_i32gather_pd(src, kmask, vindex, addr, 8); +} + +NPY_FINLINE __m512 +avx512_masked_load_ps(__mmask16 mask, npy_float* addr) +{ + return _mm512_maskz_loadu_ps(mask, (__m512 *)addr); +} + +NPY_FINLINE __m512d +avx512_masked_load_pd(__mmask8 mask, npy_double* addr) +{ + return _mm512_maskz_loadu_pd(mask, (__m512d *)addr); +} + +NPY_FINLINE __m512 +avx512_set_masked_lanes_ps(__m512 x, __m512 val, __mmask16 mask) +{ + return _mm512_mask_blend_ps(mask, x, val); +} + +NPY_FINLINE __m512d +avx512_set_masked_lanes_pd(__m512d x, __m512d val, __mmask8 mask) +{ + return _mm512_mask_blend_pd(mask, x, val); +} + +NPY_FINLINE __m512 +avx512_blend(__m512 x, __m512 y, __mmask16 ymask) +{ + return _mm512_mask_mov_ps(x, ymask, y); +} + +NPY_FINLINE __mmask16 +avx512_invert_mask_ps(__mmask16 ymask) +{ + return _mm512_knot(ymask); +} + +NPY_FINLINE __mmask8 +avx512_invert_mask_pd(__mmask8 ymask) +{ + return _mm512_knot(ymask); +} + +NPY_FINLINE __m512 +avx512_get_exponent(__m512 x) +{ + return _mm512_add_ps(_mm512_getexp_ps(x), _mm512_set1_ps(1.0f)); +} + +NPY_FINLINE __m512 +avx512_get_mantissa(__m512 x) +{ + return _mm512_getmant_ps(x, _MM_MANT_NORM_p5_1, _MM_MANT_SIGN_src); +} + +NPY_FINLINE __m512 +avx512_scalef_ps(__m512 poly, __m512 quadrant) +{ + return _mm512_scalef_ps(poly, quadrant); +} + +NPY_FINLINE __m512d +avx512_permute_x4var_pd(__m512d t0, + __m512d t1, + __m512d t2, + __m512d t3, + __m512i index) +{ + __mmask8 lut_mask = _mm512_cmp_epi64_mask( + _mm512_and_epi64(_mm512_set1_epi64(0x10ULL), index), + _mm512_set1_epi64(0), _MM_CMPINT_GT); + __m512d res1 = _mm512_permutex2var_pd(t0, index, t1); + __m512d res2 = _mm512_permutex2var_pd(t2, index, t3); + return _mm512_mask_blend_pd(lut_mask, res1, res2); +} + +NPY_FINLINE __m512d +avx512_permute_x8var_pd(__m512d t0, __m512d t1, __m512d t2, __m512d t3, + __m512d t4, __m512d t5, __m512d t6, __m512d t7, + __m512i index) +{ + __mmask8 lut_mask = _mm512_cmp_epi64_mask( + _mm512_and_epi64(_mm512_set1_epi64(0x20ULL), index), + _mm512_set1_epi64(0), _MM_CMPINT_GT); + __m512d res1 = avx512_permute_x4var_pd(t0, t1, t2, t3, index); + __m512d res2 = avx512_permute_x4var_pd(t4, t5, t6, t7, index); + return _mm512_mask_blend_pd(lut_mask, res1, res2); +} + +#endif // SIMD_AVX512F + +/******************************************************************************** + ** Defining the SIMD kernels + ********************************************************************************/ +/**begin repeat + * #ISA = FMA, AVX512F# + * #isa = fma, avx512# + * #vtype = __m256, __m512# + * #vsize = 256, 512# + * #BYTES = 32, 64# + * #NUM_LANES = 8, 16# + * #mask = __m256, __mmask16# + * #vsub = , _mask# + * #or_masks =_mm256_or_ps, _mm512_kor# + * #and_masks =_mm256_and_ps, _mm512_kand# + * #xor_masks =_mm256_xor_ps, _mm512_kxor# + * #fmadd = _mm256_fmadd_ps, _mm512_fmadd_ps# + * #mask_to_int = _mm256_movemask_ps, npyv_tobits_b32# + * #full_mask= 0xFF, 0xFFFF# + * #masked_store = _mm256_maskstore_ps, _mm512_mask_storeu_ps# + * #cvtps_epi32 = _mm256_cvtps_epi32, # + * #CHK = SIMD_AVX2_FMA3, SIMD_AVX512F# + */ +#ifdef @CHK@ +/* + * Vectorized Cody-Waite range reduction technique + * Performs the reduction step x* = x - y*C in three steps: + * 1) x* = x - y*c1 + * 2) x* = x - y*c2 + * 3) x* = x - y*c3 + * c1, c2 are exact floating points, c3 = C - c1 - c2 simulates higher precision + */ +NPY_FINLINE @vtype@ +simd_range_reduction(@vtype@ x, @vtype@ y, @vtype@ c1, @vtype@ c2, @vtype@ c3) +{ + @vtype@ reduced_x = @fmadd@(y, c1, x); + reduced_x = @fmadd@(y, c2, reduced_x); + reduced_x = @fmadd@(y, c3, reduced_x); + return reduced_x; +} +/* + * Vectorized implementation of exp using AVX2 and AVX512: + * 1) if x >= xmax; return INF (overflow) + * 2) if x <= xmin; return 0.0f (underflow) + * 3) Range reduction (using Coyd-Waite): + * a) y = x - k*ln(2); k = rint(x/ln(2)); y \in [0, ln(2)] + * 4) Compute exp(y) = P/Q, ratio of 2 polynomials P and Q + * b) P = 5th order and Q = 2nd order polynomials obtained from Remez's + * algorithm (mini-max polynomial approximation) + * 5) Compute exp(x) = exp(y) * 2^k + * 6) Max ULP error measured across all 32-bit FP's = 2.52 (x = 0xc2781e37) + * 7) Max relative error measured across all 32-bit FP's= 2.1264E-07 (for the + * same x = 0xc2781e37) + */ +static void +simd_exp_FLOAT(npy_float * op, + npy_float * ip, + const npy_intp array_size, + const npy_intp steps) +{ + const npy_intp stride = steps/(npy_intp)sizeof(npy_float); + const npy_int num_lanes = @BYTES@/(npy_intp)sizeof(npy_float); + npy_float xmax = 88.72283935546875f; + npy_float xmin = -103.97208404541015625f; + + /* + * Note: while generally indices are npy_intp, we ensure that our maximum index + * will fit in an int32 as a precondition for this function via + * IS_OUTPUT_BLOCKABLE_UNARY + */ + npy_int32 indexarr[16]; + for (npy_int32 ii = 0; ii < 16; ii++) { + indexarr[ii] = ii*stride; + } + + /* Load up frequently used constants */ + @vtype@ codyw_c1 = _mm@vsize@_set1_ps(NPY_CODY_WAITE_LOGE_2_HIGHf); + @vtype@ codyw_c2 = _mm@vsize@_set1_ps(NPY_CODY_WAITE_LOGE_2_LOWf); + @vtype@ exp_p0 = _mm@vsize@_set1_ps(NPY_COEFF_P0_EXPf); + @vtype@ exp_p1 = _mm@vsize@_set1_ps(NPY_COEFF_P1_EXPf); + @vtype@ exp_p2 = _mm@vsize@_set1_ps(NPY_COEFF_P2_EXPf); + @vtype@ exp_p3 = _mm@vsize@_set1_ps(NPY_COEFF_P3_EXPf); + @vtype@ exp_p4 = _mm@vsize@_set1_ps(NPY_COEFF_P4_EXPf); + @vtype@ exp_p5 = _mm@vsize@_set1_ps(NPY_COEFF_P5_EXPf); + @vtype@ exp_q0 = _mm@vsize@_set1_ps(NPY_COEFF_Q0_EXPf); + @vtype@ exp_q1 = _mm@vsize@_set1_ps(NPY_COEFF_Q1_EXPf); + @vtype@ exp_q2 = _mm@vsize@_set1_ps(NPY_COEFF_Q2_EXPf); + @vtype@ cvt_magic = _mm@vsize@_set1_ps(NPY_RINT_CVT_MAGICf); + @vtype@ log2e = _mm@vsize@_set1_ps(NPY_LOG2Ef); + @vtype@ inf = _mm@vsize@_set1_ps(NPY_INFINITYF); + @vtype@ zeros_f = _mm@vsize@_set1_ps(0.0f); + @vtype@ poly, num_poly, denom_poly, quadrant; + @vtype@i vindex = _mm@vsize@_loadu_si@vsize@((@vtype@i*)&indexarr[0]); + + @mask@ xmax_mask, xmin_mask, nan_mask, inf_mask; + @mask@ overflow_mask = @isa@_get_partial_load_mask_ps(0, num_lanes); + @mask@ underflow_mask = @isa@_get_partial_load_mask_ps(0, num_lanes); + @mask@ load_mask = @isa@_get_full_load_mask_ps(); + npy_intp num_remaining_elements = array_size; + + while (num_remaining_elements > 0) { + + if (num_remaining_elements < num_lanes) { + load_mask = @isa@_get_partial_load_mask_ps(num_remaining_elements, + num_lanes); + } + + @vtype@ x; + if (stride == 1) { + x = @isa@_masked_load_ps(load_mask, ip); + } + else { + x = @isa@_masked_gather_ps(zeros_f, ip, vindex, load_mask); + } + + nan_mask = _mm@vsize@_cmp_ps@vsub@(x, x, _CMP_NEQ_UQ); + x = @isa@_set_masked_lanes_ps(x, zeros_f, nan_mask); + + xmax_mask = _mm@vsize@_cmp_ps@vsub@(x, _mm@vsize@_set1_ps(xmax), _CMP_GE_OQ); + xmin_mask = _mm@vsize@_cmp_ps@vsub@(x, _mm@vsize@_set1_ps(xmin), _CMP_LE_OQ); + inf_mask = _mm@vsize@_cmp_ps@vsub@(x, inf, _CMP_EQ_OQ); + overflow_mask = @or_masks@(overflow_mask, + @xor_masks@(xmax_mask, inf_mask)); + underflow_mask = @or_masks@(underflow_mask, xmin_mask); + + x = @isa@_set_masked_lanes_ps(x, zeros_f, @or_masks@( + @or_masks@(nan_mask, xmin_mask), xmax_mask)); + + quadrant = _mm@vsize@_mul_ps(x, log2e); + + /* round to nearest */ + quadrant = _mm@vsize@_add_ps(quadrant, cvt_magic); + quadrant = _mm@vsize@_sub_ps(quadrant, cvt_magic); + + /* Cody-Waite's range reduction algorithm */ + x = simd_range_reduction(x, quadrant, codyw_c1, codyw_c2, zeros_f); + + num_poly = @fmadd@(exp_p5, x, exp_p4); + num_poly = @fmadd@(num_poly, x, exp_p3); + num_poly = @fmadd@(num_poly, x, exp_p2); + num_poly = @fmadd@(num_poly, x, exp_p1); + num_poly = @fmadd@(num_poly, x, exp_p0); + denom_poly = @fmadd@(exp_q2, x, exp_q1); + denom_poly = @fmadd@(denom_poly, x, exp_q0); + poly = _mm@vsize@_div_ps(num_poly, denom_poly); + + /* + * compute val = poly * 2^quadrant; which is same as adding the + * exponent of quadrant to the exponent of poly. quadrant is an int, + * so extracting exponent is simply extracting 8 bits. + */ + poly = @isa@_scalef_ps(poly, quadrant); + + /* + * elem > xmax; return inf + * elem < xmin; return 0.0f + * elem = +/- nan, return nan + */ + poly = @isa@_set_masked_lanes_ps(poly, _mm@vsize@_set1_ps(NPY_NANF), nan_mask); + poly = @isa@_set_masked_lanes_ps(poly, inf, xmax_mask); + poly = @isa@_set_masked_lanes_ps(poly, zeros_f, xmin_mask); + + @masked_store@(op, @cvtps_epi32@(load_mask), poly); + + ip += num_lanes*stride; + op += num_lanes; + num_remaining_elements -= num_lanes; + } + + if (@mask_to_int@(overflow_mask)) { + npy_set_floatstatus_overflow(); + } + + if (@mask_to_int@(underflow_mask)) { + npy_set_floatstatus_underflow(); + } +} + +/* + * Vectorized implementation of log using AVX2 and AVX512 + * 1) if x < 0.0f; return -NAN (invalid input) + * 2) Range reduction: y = x/2^k; + * a) y = normalized mantissa, k is the exponent (0.5 <= y < 1) + * 3) Compute log(y) = P/Q, ratio of 2 polynomials P and Q + * b) P = 5th order and Q = 5th order polynomials obtained from Remez's + * algorithm (mini-max polynomial approximation) + * 5) Compute log(x) = log(y) + k*ln(2) + * 6) Max ULP error measured across all 32-bit FP's = 3.83 (x = 0x3f486945) + * 7) Max relative error measured across all 32-bit FP's = 2.359E-07 (for same + * x = 0x3f486945) + */ + +static void +simd_log_FLOAT(npy_float * op, + npy_float * ip, + const npy_intp array_size, + const npy_intp steps) +{ + const npy_intp stride = steps/(npy_intp)sizeof(npy_float); + const npy_int num_lanes = @BYTES@/(npy_intp)sizeof(npy_float); + + /* + * Note: while generally indices are npy_intp, we ensure that our maximum index + * will fit in an int32 as a precondition for this function via + * IS_OUTPUT_BLOCKABLE_UNARY + */ + npy_int32 indexarr[16]; + for (npy_int32 ii = 0; ii < 16; ii++) { + indexarr[ii] = ii*stride; + } + + /* Load up frequently used constants */ + @vtype@ log_p0 = _mm@vsize@_set1_ps(NPY_COEFF_P0_LOGf); + @vtype@ log_p1 = _mm@vsize@_set1_ps(NPY_COEFF_P1_LOGf); + @vtype@ log_p2 = _mm@vsize@_set1_ps(NPY_COEFF_P2_LOGf); + @vtype@ log_p3 = _mm@vsize@_set1_ps(NPY_COEFF_P3_LOGf); + @vtype@ log_p4 = _mm@vsize@_set1_ps(NPY_COEFF_P4_LOGf); + @vtype@ log_p5 = _mm@vsize@_set1_ps(NPY_COEFF_P5_LOGf); + @vtype@ log_q0 = _mm@vsize@_set1_ps(NPY_COEFF_Q0_LOGf); + @vtype@ log_q1 = _mm@vsize@_set1_ps(NPY_COEFF_Q1_LOGf); + @vtype@ log_q2 = _mm@vsize@_set1_ps(NPY_COEFF_Q2_LOGf); + @vtype@ log_q3 = _mm@vsize@_set1_ps(NPY_COEFF_Q3_LOGf); + @vtype@ log_q4 = _mm@vsize@_set1_ps(NPY_COEFF_Q4_LOGf); + @vtype@ log_q5 = _mm@vsize@_set1_ps(NPY_COEFF_Q5_LOGf); + @vtype@ loge2 = _mm@vsize@_set1_ps(NPY_LOGE2f); + @vtype@ nan = _mm@vsize@_set1_ps(NPY_NANF); + @vtype@ neg_nan = _mm@vsize@_set1_ps(-NPY_NANF); + @vtype@ neg_inf = _mm@vsize@_set1_ps(-NPY_INFINITYF); + @vtype@ inf = _mm@vsize@_set1_ps(NPY_INFINITYF); + @vtype@ zeros_f = _mm@vsize@_set1_ps(0.0f); + @vtype@ ones_f = _mm@vsize@_set1_ps(1.0f); + @vtype@i vindex = _mm@vsize@_loadu_si@vsize@((@vtype@i*)indexarr); + @vtype@ poly, num_poly, denom_poly, exponent; + + @mask@ inf_mask, nan_mask, sqrt2_mask, zero_mask, negx_mask; + @mask@ invalid_mask = @isa@_get_partial_load_mask_ps(0, num_lanes); + @mask@ divide_by_zero_mask = invalid_mask; + @mask@ load_mask = @isa@_get_full_load_mask_ps(); + npy_intp num_remaining_elements = array_size; + + while (num_remaining_elements > 0) { + + if (num_remaining_elements < num_lanes) { + load_mask = @isa@_get_partial_load_mask_ps(num_remaining_elements, + num_lanes); + } + + @vtype@ x_in; + if (stride == 1) { + x_in = @isa@_masked_load_ps(load_mask, ip); + } + else { + x_in = @isa@_masked_gather_ps(zeros_f, ip, vindex, load_mask); + } + + negx_mask = _mm@vsize@_cmp_ps@vsub@(x_in, zeros_f, _CMP_LT_OQ); + zero_mask = _mm@vsize@_cmp_ps@vsub@(x_in, zeros_f, _CMP_EQ_OQ); + inf_mask = _mm@vsize@_cmp_ps@vsub@(x_in, inf, _CMP_EQ_OQ); + nan_mask = _mm@vsize@_cmp_ps@vsub@(x_in, x_in, _CMP_NEQ_UQ); + divide_by_zero_mask = @or_masks@(divide_by_zero_mask, + @and_masks@(zero_mask, load_mask)); + invalid_mask = @or_masks@(invalid_mask, negx_mask); + + @vtype@ x = @isa@_set_masked_lanes_ps(x_in, zeros_f, negx_mask); + + /* set x = normalized mantissa */ + exponent = @isa@_get_exponent(x); + x = @isa@_get_mantissa(x); + + /* if x < sqrt(2) {exp = exp-1; x = 2*x} */ + sqrt2_mask = _mm@vsize@_cmp_ps@vsub@(x, _mm@vsize@_set1_ps(NPY_SQRT1_2f), _CMP_LE_OQ); + x = @isa@_blend(x, _mm@vsize@_add_ps(x,x), sqrt2_mask); + exponent = @isa@_blend(exponent, + _mm@vsize@_sub_ps(exponent,ones_f), sqrt2_mask); + + /* x = x - 1 */ + x = _mm@vsize@_sub_ps(x, ones_f); + + /* Polynomial approximation for log(1+x) */ + num_poly = @fmadd@(log_p5, x, log_p4); + num_poly = @fmadd@(num_poly, x, log_p3); + num_poly = @fmadd@(num_poly, x, log_p2); + num_poly = @fmadd@(num_poly, x, log_p1); + num_poly = @fmadd@(num_poly, x, log_p0); + denom_poly = @fmadd@(log_q5, x, log_q4); + denom_poly = @fmadd@(denom_poly, x, log_q3); + denom_poly = @fmadd@(denom_poly, x, log_q2); + denom_poly = @fmadd@(denom_poly, x, log_q1); + denom_poly = @fmadd@(denom_poly, x, log_q0); + poly = _mm@vsize@_div_ps(num_poly, denom_poly); + poly = @fmadd@(exponent, loge2, poly); + + /* + * x < 0.0f; return -NAN + * x = +/- NAN; return NAN + * x = 0.0f; return -INF + */ + poly = @isa@_set_masked_lanes_ps(poly, nan, nan_mask); + poly = @isa@_set_masked_lanes_ps(poly, neg_nan, negx_mask); + poly = @isa@_set_masked_lanes_ps(poly, neg_inf, zero_mask); + poly = @isa@_set_masked_lanes_ps(poly, inf, inf_mask); + + @masked_store@(op, @cvtps_epi32@(load_mask), poly); + + ip += num_lanes*stride; + op += num_lanes; + num_remaining_elements -= num_lanes; + } + + if (@mask_to_int@(invalid_mask)) { + npy_set_floatstatus_invalid(); + } + if (@mask_to_int@(divide_by_zero_mask)) { + npy_set_floatstatus_divbyzero(); + } +} +#endif // @CHK@ +/**end repeat**/ + +#ifdef SIMD_AVX512F_NOCLANG_BUG +/* + * Vectorized implementation of exp double using AVX512 + * Reference: Tang, P.T.P., "Table-driven implementation of the + * exponential function in IEEE floating-point + * arithmetic," ACM Transactions on Mathematical + * Software, vol. 15, pp. 144-157, 1989. + * 1) if x > mTH_max or x is INF; return INF (overflow) + * 2) if x < mTH_min; return 0.0f (underflow) + * 3) if abs(x) < mTH_nearzero; return 1.0f + x + * 4) if x is Nan; return Nan + * 5) Range reduction: + * x = (32m + j)ln2 / 32 + r; r in [-ln2/64, ln2/64] + * 6) exp(r) - 1 is approximated by a polynomial function p(r) + * exp(x) = 2^m(2^(j/32) + 2^(j/32)p(r)); + */ +static void +AVX512F_exp_DOUBLE(npy_double * op, + npy_double * ip, + const npy_intp array_size, + const npy_intp steps) +{ + npy_intp num_remaining_elements = array_size; + const npy_intp stride = steps / (npy_intp)sizeof(npy_double); + const npy_int num_lanes = 64 / (npy_intp)sizeof(npy_double); + npy_int32 indexarr[8]; + for (npy_int32 ii = 0; ii < 8; ii++) { + indexarr[ii] = ii*stride; + } + + __m512d InvLn2N = _mm512_set1_pd(NPY_INV_LN2_MUL_32); + __m512d mShift = _mm512_set1_pd(NPY_RINT_CVT_MAGIC); + __m512d mNegL1 = _mm512_set1_pd(NPY_TANG_NEG_L1); + __m512d mNegL2 = _mm512_set1_pd(NPY_TANG_NEG_L2); + __m512i mMod = _mm512_set1_epi64(0x1f); + __m512d mA1 = _mm512_set1_pd(NPY_TANG_A1); + __m512d mA2 = _mm512_set1_pd(NPY_TANG_A2); + __m512d mA3 = _mm512_set1_pd(NPY_TANG_A3); + __m512d mA4 = _mm512_set1_pd(NPY_TANG_A4); + __m512d mA5 = _mm512_set1_pd(NPY_TANG_A5); + __m512d mTH_nearzero = _mm512_set1_pd(0x1p-54); + __m512d mTH_max = _mm512_set1_pd(0x1.62e42fefa39efp+9); + __m512d mTH_min = _mm512_set1_pd(-0x1.74910d52d3053p+9); + __m512d mTH_inf = _mm512_set1_pd(NPY_INFINITY); + __m512d zeros_d = _mm512_set1_pd(0.0f); + __m512d ones_d = _mm512_set1_pd(1.0f); + __m256i vindex = _mm256_loadu_si256((__m256i*)&indexarr[0]); + + __m512d mTable_top_0 = _mm512_loadu_pd(&(EXP_Table_top[8*0])); + __m512d mTable_top_1 = _mm512_loadu_pd(&(EXP_Table_top[8*1])); + __m512d mTable_top_2 = _mm512_loadu_pd(&(EXP_Table_top[8*2])); + __m512d mTable_top_3 = _mm512_loadu_pd(&(EXP_Table_top[8*3])); + __m512d mTable_tail_0 = _mm512_loadu_pd(&(EXP_Table_tail[8*0])); + __m512d mTable_tail_1 = _mm512_loadu_pd(&(EXP_Table_tail[8*1])); + __m512d mTable_tail_2 = _mm512_loadu_pd(&(EXP_Table_tail[8*2])); + __m512d mTable_tail_3 = _mm512_loadu_pd(&(EXP_Table_tail[8*3])); + + __mmask8 overflow_mask = avx512_get_partial_load_mask_pd(0, num_lanes); + __mmask8 underflow_mask = avx512_get_partial_load_mask_pd(0, num_lanes); + __mmask8 load_mask = avx512_get_full_load_mask_pd(); + __mmask8 xmin_mask, xmax_mask, inf_mask, nan_mask, nearzero_mask; + + while (num_remaining_elements > 0) { + if (num_remaining_elements < num_lanes) { + load_mask = avx512_get_partial_load_mask_pd(num_remaining_elements, + num_lanes); + } + + __m512d x; + if (1 == stride) { + x = avx512_masked_load_pd(load_mask, ip); + } + else { + x = avx512_masked_gather_pd(zeros_d, ip, vindex, load_mask); + } + + nan_mask = _mm512_cmp_pd_mask(x, x, _CMP_NEQ_UQ); + x = avx512_set_masked_lanes_pd(x, zeros_d, nan_mask); + xmax_mask = _mm512_cmp_pd_mask(x, mTH_max, _CMP_GT_OQ); + xmin_mask = _mm512_cmp_pd_mask(x, mTH_min, _CMP_LT_OQ); + inf_mask = _mm512_cmp_pd_mask(x, mTH_inf, _CMP_EQ_OQ); + __m512i x_abs = _mm512_and_epi64(_mm512_castpd_si512(x), + _mm512_set1_epi64(0x7FFFFFFFFFFFFFFF)); + nearzero_mask = _mm512_cmp_pd_mask(_mm512_castsi512_pd(x_abs), + mTH_nearzero, _CMP_LT_OQ); + nearzero_mask = _mm512_kxor(nearzero_mask, nan_mask); + overflow_mask = _mm512_kor(overflow_mask, + _mm512_kxor(xmax_mask, inf_mask)); + underflow_mask = _mm512_kor(underflow_mask, xmin_mask); + x = avx512_set_masked_lanes_pd(x, zeros_d, + _mm512_kor(_mm512_kor(nan_mask, xmin_mask), + _mm512_kor(xmax_mask, nearzero_mask))); + + /* z = x * 32/ln2 */ + __m512d z = _mm512_mul_pd(x, InvLn2N); + + /* round to nearest */ + __m512d kd = _mm512_add_pd(z, mShift); + __m512i ki = _mm512_castpd_si512(kd); + kd = _mm512_sub_pd(kd, mShift); + + /* r = (x + kd*mNegL1) + kd*mNegL2 */ + __m512d r1 = _mm512_fmadd_pd(kd, mNegL1, x); + __m512d r2 = _mm512_mul_pd(kd, mNegL2); + __m512d r = _mm512_add_pd(r1,r2); + + /* Polynomial approximation for exp(r) - 1 */ + __m512d q = _mm512_fmadd_pd(mA5, r, mA4); + q = _mm512_fmadd_pd(q, r, mA3); + q = _mm512_fmadd_pd(q, r, mA2); + q = _mm512_fmadd_pd(q, r, mA1); + q = _mm512_mul_pd(q, r); + __m512d p = _mm512_fmadd_pd(r, q, r2); + p = _mm512_add_pd(r1, p); + + /* Get 2^(j/32) from lookup table */ + __m512i j = _mm512_and_epi64(ki, mMod); + __m512d top = avx512_permute_x4var_pd(mTable_top_0, mTable_top_1, + mTable_top_2, mTable_top_3, j); + __m512d tail = avx512_permute_x4var_pd(mTable_tail_0, mTable_tail_1, + mTable_tail_2, mTable_tail_3, j); + + /* + * s = top + tail; + * exp(x) = 2^m * (top + (tail + s * p)); + */ + __m512d s = _mm512_add_pd(top, tail); + __m512d res = _mm512_fmadd_pd(s, p, tail); + res = _mm512_add_pd(res, top); + res= _mm512_scalef_pd(res, _mm512_div_pd(kd, _mm512_set1_pd(32))); + + /* return special cases */ + res = avx512_set_masked_lanes_pd(res, _mm512_add_pd(x, ones_d), + nearzero_mask); + res = avx512_set_masked_lanes_pd(res, _mm512_set1_pd(NPY_NAN), + nan_mask); + res = avx512_set_masked_lanes_pd(res, mTH_inf, xmax_mask); + res = avx512_set_masked_lanes_pd(res, zeros_d, xmin_mask); + + _mm512_mask_storeu_pd(op, load_mask, res); + + ip += num_lanes * stride; + op += num_lanes; + num_remaining_elements -= num_lanes; + } + /* + * Don't count on the compiler for cast between mask and int registers. + * On gcc7 with flags -march>=nocona -O3 can cause FP stack overflow + * which may lead to putting NaN into certain HW/FP calculations. + * + * For more details, please check the comments in: + * - https://github.com/numpy/numpy/issues/20356 + */ + if (npyv_tobits_b64(overflow_mask)) { + npy_set_floatstatus_overflow(); + } + + if (npyv_tobits_b64(underflow_mask)) { + npy_set_floatstatus_underflow(); + } +} +/* + * Vectorized implementation of log double using AVX512 + * Reference: + * [1] Tang, Ping Tak Peter. Table-lookup algorithms for elementary functions + * and their error analysis. No. CONF-9106103-1. Argonne National Lab., + * IL (USA), 1991. + * [2] Tang, Ping-Tak Peter. "Table-driven implementation of the logarithm + * function in IEEE floating-point arithmetic." ACM Transactions on + * Mathematical Software (TOMS) 16.4 (1990): 378-400. + * [3] Muller, Jean-Michel. "Elementary functions: algorithms and + * implementation." (2016). + * 1) if x = 0; return -INF + * 2) if x < 0; return NAN + * 3) if x is INF; return INF + * 4) if x is NAN; return NAN + * 5) if x on (1.0 - 0x1p-4, 1.0 + 0x1.09p-4), calling npy_log() + * 6) Range reduction: + * log(x) = log(2^m * z) + * = mln2 + log(z) + * 7) log(z) = log(z / c_k) + log(c_k); + * where c_k = 1 + k/64, k = 0,1,...,64 + * s.t. |x - c_k| <= 1/128 when x on[1,2]. + * 8) r = 2(x - c_k)/(x + c_k) + * log(x/c_k) = log((1 + r/2) / (1 - r/2)) + * = p(r) + * = 2((r/2) + 1/3*(r/2)^3 + 1/5*(r/2)^5 + ...) + */ + +/* LLVM has a bug where AVX-512F intrinsic `_mm512_mask_mul_pd` emits an + * unmasked operation with a masked store. This can cause FP exceptions to + * occur for the lanes that are suppose to have been masked. + * + * See https://bugs.llvm.org/show_bug.cgi?id=51988 + * + * Note, this affects LLVM based compilers like Apple Clang, Clang, and Intel's + * ICX. + */ +#if defined(__clang__) + #if defined(__apple_build_version__) + // Apple Clang + #if __apple_build_version__ > 11000000 + // Apple Clang after v11 + #define WORKAROUND_LLVM__mm512_mask_mul_pd + #endif + #else + // Clang, not Apple Clang + #if __clang_major__ > 9 + // Clang v9+ + #define WORKAROUND_LLVM__mm512_mask_mul_pd + #endif + #endif +#endif + +static void +AVX512F_log_DOUBLE(npy_double * op, + npy_double * ip, + const npy_intp array_size, + const npy_intp steps) +{ + npy_intp num_remaining_elements = array_size; + const npy_intp stride = steps / (npy_intp)sizeof(npy_double); + const npy_int num_lanes = 64 / (npy_intp)sizeof(npy_double); + npy_int32 indexarr[8]; + for (npy_int32 ii = 0; ii < 8; ii++) { + indexarr[ii] = ii*stride; + } + + __m512d zeros_d = _mm512_set1_pd(0.0f); + __m512d ones_d = _mm512_set1_pd(1.0f); + __m512d mInf = _mm512_set1_pd(NPY_INFINITY); + __m512d mInv64 = _mm512_castsi512_pd(_mm512_set1_epi64(0x3f90000000000000)); + __m512d mNeg_nan = _mm512_set1_pd(-NPY_NAN); + __m512d mNan = _mm512_set1_pd(NPY_NAN); + __m512d mNeg_inf = _mm512_set1_pd(-NPY_INFINITY); + __m512d mA1 = _mm512_set1_pd(NPY_TANG_LOG_A1); + __m512d mA2 = _mm512_set1_pd(NPY_TANG_LOG_A2); + __m512d mA3 = _mm512_set1_pd(NPY_TANG_LOG_A3); + __m512d mA4 = _mm512_set1_pd(NPY_TANG_LOG_A4); + __m512d mLN2HI = _mm512_set1_pd(NPY_TANG_LOG_LN2HI); + __m512d mLN2LO = _mm512_set1_pd(NPY_TANG_LOG_LN2LO); + + __m512d mTo_glibc_min = _mm512_set1_pd(1.0 - 0x1p-4); + __m512d mTo_glibc_max = _mm512_set1_pd(1.0 + 0x1.09p-4); + __m256i vindex = _mm256_loadu_si256((__m256i*)&indexarr[0]); + + /* Load lookup table data */ + /**begin repeat + * #i = 0, 1, 2, 3, 4, 5, 6, 7# + */ + + __m512d mLUT_TOP_@i@ = _mm512_loadu_pd(&(LOG_TABLE_TOP[8*@i@])); + __m512d mLUT_TAIL_@i@ = _mm512_loadu_pd(&(LOG_TABLE_TAIL[8*@i@])); + + /**end repeat**/ + + __mmask8 load_mask = avx512_get_full_load_mask_pd(); + __mmask8 invalid_mask = avx512_get_partial_load_mask_pd(0, num_lanes); + __mmask8 divide_by_zero_mask = invalid_mask; + + __mmask8 inf_mask, nan_mask, zero_mask, negx_mask, denormal_mask, + glibc_mask; + + __m512d x_in; + while (num_remaining_elements > 0) { + if (num_remaining_elements < num_lanes) { + load_mask = avx512_get_partial_load_mask_pd(num_remaining_elements, + num_lanes); + } + + if (1 == stride) { + x_in = avx512_masked_load_pd(load_mask, ip); + } + else { + x_in = avx512_masked_gather_pd(zeros_d, ip, vindex, load_mask); + } + + /* call glibc when x on [1.0 - 0x1p-4, 1.0 + 0x1.09p-4] */ + __mmask8 m1 = _mm512_cmp_pd_mask(x_in, mTo_glibc_max, _CMP_LT_OQ); + __mmask8 m2 = _mm512_cmp_pd_mask(x_in, mTo_glibc_min, _CMP_GT_OQ); + glibc_mask = m1 & m2; + + if (glibc_mask != 0xFF) { + zero_mask = _mm512_cmp_pd_mask(x_in, zeros_d, _CMP_EQ_OQ); + inf_mask = _mm512_cmp_pd_mask(x_in, mInf, _CMP_EQ_OQ); + negx_mask = _mm512_cmp_pd_mask(x_in, zeros_d, _CMP_LT_OQ); + nan_mask = _mm512_cmp_pd_mask(x_in, x_in, _CMP_NEQ_UQ); + + divide_by_zero_mask = divide_by_zero_mask | (zero_mask & load_mask); + invalid_mask = invalid_mask | negx_mask; + + __m512d x = avx512_set_masked_lanes_pd(x_in, zeros_d, negx_mask); + __m512i ix = _mm512_castpd_si512(x); + + /* Normalize x when it is denormal */ + __m512i top12 = _mm512_and_epi64(ix, + _mm512_set1_epi64(0xfff0000000000000)); + denormal_mask = _mm512_cmp_epi64_mask(top12, _mm512_set1_epi64(0), + _CMP_EQ_OQ); + denormal_mask = (~zero_mask) & denormal_mask; + __m512d masked_x = x; + #ifdef WORKAROUND_LLVM__mm512_mask_mul_pd + masked_x = avx512_set_masked_lanes_pd(masked_x, zeros_d, (~denormal_mask)); + #endif + ix = _mm512_castpd_si512(_mm512_mask_mul_pd(x, denormal_mask, + masked_x, _mm512_set1_pd(0x1p52))); + ix = _mm512_mask_sub_epi64(ix, denormal_mask, + ix, _mm512_set1_epi64(52ULL << 52)); + + /* + * x = 2^k * z; where z in range [1,2] + */ + __m512i tmp = _mm512_sub_epi64(ix, + _mm512_set1_epi64(0x3ff0000000000000)); + __m512i i = _mm512_and_epi64(_mm512_srai_epi64(tmp, 52 - 6), + _mm512_set1_epi64(0x3fULL)); + __m512i ik = _mm512_srai_epi64(tmp, 52); + __m512d z = _mm512_castsi512_pd(_mm512_sub_epi64(ix, _mm512_and_epi64(tmp, + _mm512_set1_epi64(0xfff0000000000000)))); + /* c = i/64 + 1 */ + __m256i i_32 = _mm512_cvtepi64_epi32(i); + __m512d c = _mm512_fmadd_pd(_mm512_cvtepi32_pd(i_32), mInv64, ones_d); + + /* u = 2 * (z - c) / (z + c) */ + __m512d u = _mm512_div_pd(_mm512_sub_pd(z, c), _mm512_add_pd(z, c)); + u = _mm512_mul_pd(_mm512_set1_pd(2.0), u); + + /* v = u * u */ + __m512d v = _mm512_mul_pd(u,u); + + /* log(z/c) = u + u*v*(A1 + v*(A2 + v*(A3 + v*A4))) */ + __m512d res = _mm512_fmadd_pd(v, mA4, mA3); + res = _mm512_fmadd_pd(v, res, mA2); + res = _mm512_fmadd_pd(v, res, mA1); + res = _mm512_mul_pd(v, res); + res = _mm512_fmadd_pd(u, res, u); + + /* Load lookup table data */ + __m512d c_hi = avx512_permute_x8var_pd(mLUT_TOP_0, mLUT_TOP_1, + mLUT_TOP_2, mLUT_TOP_3, mLUT_TOP_4, mLUT_TOP_5, + mLUT_TOP_6, mLUT_TOP_7, i); + __m512d c_lo = avx512_permute_x8var_pd(mLUT_TAIL_0, mLUT_TAIL_1, + mLUT_TAIL_2, mLUT_TAIL_3, mLUT_TAIL_4, mLUT_TAIL_5, + mLUT_TAIL_6, mLUT_TAIL_7, i); + + /* + * log(x) = k * ln2_hi + c_hi + + * k * ln2_lo + c_lo + + * log(z/c) + */ + __m256i ik_32 = _mm512_cvtepi64_epi32(ik); + __m512d k = _mm512_cvtepi32_pd(ik_32); + __m512d tt = _mm512_fmadd_pd(k, mLN2HI, c_hi); + __m512d tt2 = _mm512_fmadd_pd(k, mLN2LO, c_lo); + tt = _mm512_add_pd(tt, tt2); + res = _mm512_add_pd(tt, res); + + /* return special cases */ + res = avx512_set_masked_lanes_pd(res, mNan, nan_mask); + res = avx512_set_masked_lanes_pd(res, mNeg_nan, negx_mask); + res = avx512_set_masked_lanes_pd(res, mNeg_inf, zero_mask); + res = avx512_set_masked_lanes_pd(res, mInf, inf_mask); + + _mm512_mask_storeu_pd(op, load_mask, res); + } + + /* call glibc's log func when x around 1.0f */ + if (glibc_mask != 0) { + double NPY_DECL_ALIGNED(64) ip_fback[8]; + _mm512_store_pd(ip_fback, x_in); + + for (int ii = 0; ii < 8; ++ii, glibc_mask >>= 1) { + if (glibc_mask & 0x01) { + op[ii] = npy_log(ip_fback[ii]); + } + } + } + ip += num_lanes * stride; + op += num_lanes; + num_remaining_elements -= num_lanes; + } + + if (npyv_tobits_b64(invalid_mask)) { + npy_set_floatstatus_invalid(); + } + if (npyv_tobits_b64(divide_by_zero_mask)) { + npy_set_floatstatus_divbyzero(); + } +} + +#undef WORKAROUND_LLVM__mm512_mask_mul_pd + +#endif // AVX512F_NOCLANG_BUG + +#ifdef SIMD_AVX512_SKX +/**begin repeat + * #type = npy_float, npy_double# + * #TYPE = FLOAT, DOUBLE# + * #num_lanes = 16, 8# + * #vsuffix = ps, pd# + * #mask = __mmask16, __mmask8# + * #vtype1 = __m512, __m512d# + * #vtype2 = __m512i, __m256i# + * #scale = 4, 8# + * #vindextype = __m512i, __m256i# + * #vindexsize = 512, 256# + * #vindexload = _mm512_loadu_si512, _mm256_loadu_si256# + * #vtype2_load = _mm512_maskz_loadu_epi32, _mm256_maskz_loadu_epi32# + * #vtype2_gather = _mm512_mask_i32gather_epi32, _mm256_mmask_i32gather_epi32# + * #vtype2_store = _mm512_mask_storeu_epi32, _mm256_mask_storeu_epi32# + * #vtype2_scatter = _mm512_mask_i32scatter_epi32, _mm256_mask_i32scatter_epi32# + * #setzero = _mm512_setzero_epi32, _mm256_setzero_si256# + */ +static NPY_INLINE void +AVX512_SKX_ldexp_@TYPE@(char **args, npy_intp const *dimensions, npy_intp const *steps) +{ + const npy_intp stride_ip1 = steps[0]/(npy_intp)sizeof(@type@); + const npy_intp stride_ip2 = steps[1]/(npy_intp)sizeof(int); + const npy_intp stride_op = steps[2]/(npy_intp)sizeof(@type@); + const npy_intp array_size = dimensions[0]; + npy_intp num_remaining_elements = array_size; + @type@* ip1 = (@type@*) args[0]; + int* ip2 = (int*) args[1]; + @type@* op = (@type@*) args[2]; + + @mask@ load_mask = avx512_get_full_load_mask_@vsuffix@(); + + /* + * Note: while generally indices are npy_intp, we ensure that our maximum index + * will fit in an int32 as a precondition for this function via + * IS_BINARY_SMALL_STEPS_AND_NOMEMOVERLAP + */ + + npy_int32 index_ip1[@num_lanes@], index_ip2[@num_lanes@], index_op[@num_lanes@]; + for (npy_int32 ii = 0; ii < @num_lanes@; ii++) { + index_ip1[ii] = ii*stride_ip1; + index_ip2[ii] = ii*stride_ip2; + index_op[ii] = ii*stride_op; + } + @vindextype@ vindex_ip1 = @vindexload@((@vindextype@*)&index_ip1[0]); + @vindextype@ vindex_ip2 = @vindexload@((@vindextype@*)&index_ip2[0]); + @vindextype@ vindex_op = @vindexload@((@vindextype@*)&index_op[0]); + @vtype1@ zeros_f = _mm512_setzero_@vsuffix@(); + @vtype2@ zeros = @setzero@(); + + while (num_remaining_elements > 0) { + if (num_remaining_elements < @num_lanes@) { + load_mask = avx512_get_partial_load_mask_@vsuffix@( + num_remaining_elements, @num_lanes@); + } + @vtype1@ x1; + @vtype2@ x2; + if (stride_ip1 == 1) { + x1 = avx512_masked_load_@vsuffix@(load_mask, ip1); + } + else { + x1 = avx512_masked_gather_@vsuffix@(zeros_f, ip1, vindex_ip1, load_mask); + } + if (stride_ip2 == 1) { + x2 = @vtype2_load@(load_mask, ip2); + } + else { + x2 = @vtype2_gather@(zeros, load_mask, vindex_ip2, ip2, 4); + } + + @vtype1@ out = _mm512_scalef_@vsuffix@(x1, _mm512_cvtepi32_@vsuffix@(x2)); + + if (stride_op == 1) { + _mm512_mask_storeu_@vsuffix@(op, load_mask, out); + } + else { + /* scatter! */ + _mm512_mask_i32scatter_@vsuffix@(op, load_mask, vindex_op, out, @scale@); + } + + ip1 += @num_lanes@*stride_ip1; + ip2 += @num_lanes@*stride_ip2; + op += @num_lanes@*stride_op; + num_remaining_elements -= @num_lanes@; + } +} + +static NPY_INLINE void +AVX512_SKX_frexp_@TYPE@(char **args, npy_intp const *dimensions, npy_intp const *steps) +{ + const npy_intp stride_ip1 = steps[0]/(npy_intp)sizeof(@type@); + const npy_intp stride_op1 = steps[1]/(npy_intp)sizeof(@type@); + const npy_intp stride_op2 = steps[2]/(npy_intp)sizeof(int); + const npy_intp array_size = dimensions[0]; + npy_intp num_remaining_elements = array_size; + @type@* ip1 = (@type@*) args[0]; + @type@* op1 = (@type@*) args[1]; + int* op2 = (int*) args[2]; + + @mask@ load_mask = avx512_get_full_load_mask_@vsuffix@(); + + /* + * Note: while generally indices are npy_intp, we ensure that our maximum index + * will fit in an int32 as a precondition for this function via + * IS_BINARY_SMALL_STEPS_AND_NOMEMOVERLAP + */ + + npy_int32 index_ip1[@num_lanes@], index_op1[@num_lanes@], index_op2[@num_lanes@]; + for (npy_int32 ii = 0; ii < @num_lanes@; ii++) { + index_ip1[ii] = ii*stride_ip1; + index_op1[ii] = ii*stride_op1; + index_op2[ii] = ii*stride_op2; + } + @vindextype@ vindex_ip1 = @vindexload@((@vindextype@*)&index_ip1[0]); + @vindextype@ vindex_op1 = @vindexload@((@vindextype@*)&index_op1[0]); + @vindextype@ vindex_op2 = @vindexload@((@vindextype@*)&index_op2[0]); + @vtype1@ zeros_f = _mm512_setzero_@vsuffix@(); + + while (num_remaining_elements > 0) { + if (num_remaining_elements < @num_lanes@) { + load_mask = avx512_get_partial_load_mask_@vsuffix@( + num_remaining_elements, @num_lanes@); + } + @vtype1@ x1; + if (stride_ip1 == 1) { + x1 = avx512_masked_load_@vsuffix@(load_mask, ip1); + } + else { + x1 = avx512_masked_gather_@vsuffix@(zeros_f, ip1, vindex_ip1, load_mask); + } + + /* + * The x86 instructions vpgetmant and vpgetexp do not conform + * with NumPy's output for special floating points: NAN, +/-INF, +/-0.0 + * We mask these values with spmask to avoid invalid exceptions. + */ + @mask@ spmask =_mm512_knot(_mm512_fpclass_@vsuffix@_mask( + x1, 0b10011111)); + @vtype1@ out1 = _mm512_maskz_getmant_@vsuffix@( + spmask, x1, _MM_MANT_NORM_p5_1, _MM_MANT_SIGN_src); + out1 = _mm512_mask_mov_@vsuffix@(x1, spmask, out1); + @vtype2@ out2 = _mm512_cvt@vsuffix@_epi32( + _mm512_maskz_add_@vsuffix@(spmask, _mm512_set1_@vsuffix@(1.0), + _mm512_maskz_getexp_@vsuffix@(spmask, x1))); + if (stride_op1 == 1) { + _mm512_mask_storeu_@vsuffix@(op1, load_mask, out1); + } + else { + _mm512_mask_i32scatter_@vsuffix@(op1, load_mask, vindex_op1, out1, @scale@); + } + if (stride_op2 == 1) { + @vtype2_store@(op2, load_mask, out2); + } + else { + @vtype2_scatter@(op2, load_mask, vindex_op2, out2, 4); + } + + ip1 += @num_lanes@*stride_ip1; + op1 += @num_lanes@*stride_op1; + op2 += @num_lanes@*stride_op2; + num_remaining_elements -= @num_lanes@; + } +} +/**end repeat**/ +#endif // SIMD_AVX512_SKX + + +/******************************************************************************** + ** Defining ufunc inner functions + ********************************************************************************/ +/**begin repeat + * #func = exp, log# + * #scalarf = npy_expf, npy_logf# + */ +NPY_NO_EXPORT void NPY_CPU_DISPATCH_CURFX(FLOAT_@func@) +(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data)) +{ +#if defined(SIMD_AVX2_FMA3) || defined(SIMD_AVX512F) + // third arg in `IS_OUTPUT_BLOCKABLE_UNARY` is dummy + // TODO: get ride of this macro during the move to NPYV + if (IS_OUTPUT_BLOCKABLE_UNARY(sizeof(npy_float), sizeof(npy_float), 64)) { + simd_@func@_FLOAT((npy_float*)args[1], (npy_float*)args[0], dimensions[0], steps[0]); + } + else { + UNARY_LOOP { + /* + * We use the AVX function to compute exp/log for scalar elements as well. + * This is needed to ensure the output of strided and non-strided + * cases match. SIMD code handles strided input cases, but not + * strided output. + */ + simd_@func@_FLOAT((npy_float *)op1, (npy_float *)ip1, 1, steps[0]); + } + } +#else + UNARY_LOOP { + const npy_float in1 = *(npy_float *)ip1; + *(npy_float *)op1 = @scalarf@(in1); + } +#endif +} +/**end repeat**/ + +/**begin repeat + * #func = exp, log# + * #scalar = npy_exp, npy_log# + */ +NPY_NO_EXPORT void NPY_CPU_DISPATCH_CURFX(DOUBLE_@func@) +(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data)) +{ +#ifdef SIMD_AVX512F_NOCLANG_BUG + if (IS_OUTPUT_BLOCKABLE_UNARY(sizeof(npy_double), sizeof(npy_double), 64)) { + AVX512F_@func@_DOUBLE((npy_double*)args[1], (npy_double*)args[0], dimensions[0], steps[0]); + return; + } +#endif + UNARY_LOOP { + const npy_double in1 = *(npy_double *)ip1; + *(npy_double *)op1 = @scalar@(in1); + } +} +/**end repeat**/ + +/**begin repeat + * Float types + * #type = npy_float, npy_double# + * #TYPE = FLOAT, DOUBLE# + * #c = f, # + * #C = F, # + */ +NPY_NO_EXPORT void NPY_CPU_DISPATCH_CURFX(@TYPE@_frexp) +(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) +{ +#ifdef SIMD_AVX512_SKX + if (IS_UNARY_TWO_OUT_SMALL_STEPS_AND_NOMEMOVERLAP) { + AVX512_SKX_frexp_@TYPE@(args, dimensions, steps); + return; + } +#endif + UNARY_LOOP_TWO_OUT { + const @type@ in1 = *(@type@ *)ip1; + *((@type@ *)op1) = npy_frexp@c@(in1, (int *)op2); + } +} + +NPY_NO_EXPORT void NPY_CPU_DISPATCH_CURFX(@TYPE@_ldexp) +(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) +{ +#ifdef SIMD_AVX512_SKX + if (IS_BINARY_SMALL_STEPS_AND_NOMEMOVERLAP) { + AVX512_SKX_ldexp_@TYPE@(args, dimensions, steps); + return; + } +#endif + BINARY_LOOP { + const @type@ in1 = *(@type@ *)ip1; + const int in2 = *(int *)ip2; + *((@type@ *)op1) = npy_ldexp@c@(in1, in2); + } +} +/**end repeat**/ diff --git a/numpy/core/src/umath/loops_trigonometric.dispatch.c.src b/numpy/core/src/umath/loops_trigonometric.dispatch.c.src new file mode 100644 index 000000000000..cd9b2ed547ff --- /dev/null +++ b/numpy/core/src/umath/loops_trigonometric.dispatch.c.src @@ -0,0 +1,230 @@ +/*@targets + ** $maxopt baseline + ** (avx2 fma3) avx512f + ** vsx2 + ** neon_vfpv4 + **/ +#include "numpy/npy_math.h" +#include "simd/simd.h" +#include "loops_utils.h" +#include "loops.h" +/* + * TODO: + * - use vectorized version of Payne-Hanek style reduction for large elements or + * when there's no native FUSED support instead of fallback to libc + */ +#if NPY_SIMD_FMA3 // native support +/* + * Vectorized Cody-Waite range reduction technique + * Performs the reduction step x* = x - y*C in three steps: + * 1) x* = x - y*c1 + * 2) x* = x - y*c2 + * 3) x* = x - y*c3 + * c1, c2 are exact floating points, c3 = C - c1 - c2 simulates higher precision + */ +NPY_FINLINE npyv_f32 +simd_range_reduction_f32(npyv_f32 x, npyv_f32 y, npyv_f32 c1, npyv_f32 c2, npyv_f32 c3) +{ + npyv_f32 reduced_x = npyv_muladd_f32(y, c1, x); + reduced_x = npyv_muladd_f32(y, c2, reduced_x); + reduced_x = npyv_muladd_f32(y, c3, reduced_x); + return reduced_x; +} +/* + * Approximate cosine algorithm for x \in [-PI/4, PI/4] + * Maximum ULP across all 32-bit floats = 0.875 + */ +NPY_FINLINE npyv_f32 +simd_cosine_poly_f32(npyv_f32 x2) +{ + const npyv_f32 invf8 = npyv_setall_f32(0x1.98e616p-16f); + const npyv_f32 invf6 = npyv_setall_f32(-0x1.6c06dcp-10f); + const npyv_f32 invf4 = npyv_setall_f32(0x1.55553cp-05f); + const npyv_f32 invf2 = npyv_setall_f32(-0x1.000000p-01f); + const npyv_f32 invf0 = npyv_setall_f32(0x1.000000p+00f); + + npyv_f32 r = npyv_muladd_f32(invf8, x2, invf6); + r = npyv_muladd_f32(r, x2, invf4); + r = npyv_muladd_f32(r, x2, invf2); + r = npyv_muladd_f32(r, x2, invf0); + return r; +} +/* + * Approximate sine algorithm for x \in [-PI/4, PI/4] + * Maximum ULP across all 32-bit floats = 0.647 + * Polynomial approximation based on unpublished work by T. Myklebust + */ +NPY_FINLINE npyv_f32 +simd_sine_poly_f32(npyv_f32 x, npyv_f32 x2) +{ + const npyv_f32 invf9 = npyv_setall_f32(0x1.7d3bbcp-19f); + const npyv_f32 invf7 = npyv_setall_f32(-0x1.a06bbap-13f); + const npyv_f32 invf5 = npyv_setall_f32(0x1.11119ap-07f); + const npyv_f32 invf3 = npyv_setall_f32(-0x1.555556p-03f); + + npyv_f32 r = npyv_muladd_f32(invf9, x2, invf7); + r = npyv_muladd_f32(r, x2, invf5); + r = npyv_muladd_f32(r, x2, invf3); + r = npyv_muladd_f32(r, x2, npyv_zero_f32()); + r = npyv_muladd_f32(r, x, x); + return r; +} +/* + * Vectorized approximate sine/cosine algorithms: The following code is a + * vectorized version of the algorithm presented here: + * https://stackoverflow.com/questions/30463616/payne-hanek-algorithm-implementation-in-c/30465751#30465751 + * (1) Load data in registers and generate mask for elements that are + * within range [-71476.0625f, 71476.0625f] for cosine and [-117435.992f, + * 117435.992f] for sine. + * (2) For elements within range, perform range reduction using Cody-Waite's + * method: x* = x - y*PI/2, where y = rint(x*2/PI). x* \in [-PI/4, PI/4]. + * (3) Map cos(x) to (+/-)sine or (+/-)cosine of x* based on the quadrant k = + * int(y). + * (4) For elements outside that range, Cody-Waite reduction performs poorly + * leading to catastrophic cancellation. We compute cosine by calling glibc in + * a scalar fashion. + * (5) Vectorized implementation has a max ULP of 1.49 and performs at least + * 5-7x(x86) - 2.5-3x(Power) - 1-2x(Arm) faster than scalar implementations + * when magnitude of all elements in the array < 71476.0625f (117435.992f for sine). + * Worst case performance is when all the elements are large leading to about 1-2% reduction in + * performance. + */ +typedef enum +{ + SIMD_COMPUTE_SIN, + SIMD_COMPUTE_COS +} SIMD_TRIG_OP; + +static void SIMD_MSVC_NOINLINE +simd_sincos_f32(const float *src, npy_intp ssrc, float *dst, npy_intp sdst, + npy_intp len, SIMD_TRIG_OP trig_op) +{ + // Load up frequently used constants + const npyv_f32 zerosf = npyv_zero_f32(); + const npyv_s32 ones = npyv_setall_s32(1); + const npyv_s32 twos = npyv_setall_s32(2); + const npyv_f32 two_over_pi = npyv_setall_f32(0x1.45f306p-1f); + const npyv_f32 codyw_pio2_highf = npyv_setall_f32(-0x1.921fb0p+00f); + const npyv_f32 codyw_pio2_medf = npyv_setall_f32(-0x1.5110b4p-22f); + const npyv_f32 codyw_pio2_lowf = npyv_setall_f32(-0x1.846988p-48f); + const npyv_f32 rint_cvt_magic = npyv_setall_f32(0x1.800000p+23f); + // Cody-Waite's range + float max_codi = 117435.992f; + if (trig_op == SIMD_COMPUTE_COS) { + max_codi = 71476.0625f; + } + const npyv_f32 max_cody = npyv_setall_f32(max_codi); + const int vstep = npyv_nlanes_f32; + + for (; len > 0; len -= vstep, src += ssrc*vstep, dst += sdst*vstep) { + npyv_f32 x_in; + if (ssrc == 1) { + x_in = npyv_load_tillz_f32(src, len); + } else { + x_in = npyv_loadn_tillz_f32(src, ssrc, len); + } + npyv_b32 simd_mask = npyv_cmple_f32(npyv_abs_f32(x_in), max_cody); + npy_uint64 simd_maski = npyv_tobits_b32(simd_mask); + /* + * For elements outside of this range, Cody-Waite's range reduction + * becomes inaccurate and we will call libc to compute cosine for + * these numbers + */ + if (simd_maski != 0) { + npyv_b32 nnan_mask = npyv_notnan_f32(x_in); + npyv_f32 x = npyv_select_f32(npyv_and_b32(nnan_mask, simd_mask), x_in, zerosf); + + npyv_f32 quadrant = npyv_mul_f32(x, two_over_pi); + // round to nearest, -0.0f -> +0.0f, and |a| must be <= 0x1.0p+22 + quadrant = npyv_add_f32(quadrant, rint_cvt_magic); + quadrant = npyv_sub_f32(quadrant, rint_cvt_magic); + + // Cody-Waite's range reduction algorithm + npyv_f32 reduced_x = simd_range_reduction_f32( + x, quadrant, codyw_pio2_highf, codyw_pio2_medf, codyw_pio2_lowf + ); + npyv_f32 reduced_x2 = npyv_square_f32(reduced_x); + + // compute cosine and sine + npyv_f32 cos = simd_cosine_poly_f32(reduced_x2); + npyv_f32 sin = simd_sine_poly_f32(reduced_x, reduced_x2); + + npyv_s32 iquadrant = npyv_round_s32_f32(quadrant); + if (trig_op == SIMD_COMPUTE_COS) { + iquadrant = npyv_add_s32(iquadrant, ones); + } + // blend sin and cos based on the quadrant + npyv_b32 sine_mask = npyv_cmpeq_s32(npyv_and_s32(iquadrant, ones), npyv_zero_s32()); + cos = npyv_select_f32(sine_mask, sin, cos); + + // multiply by -1 for appropriate elements + npyv_b32 negate_mask = npyv_cmpeq_s32(npyv_and_s32(iquadrant, twos), twos); + cos = npyv_ifsub_f32(negate_mask, zerosf, cos, cos); + cos = npyv_select_f32(nnan_mask, cos, npyv_setall_f32(NPY_NANF)); + + if (sdst == 1) { + npyv_store_till_f32(dst, len, cos); + } else { + npyv_storen_till_f32(dst, sdst, len, cos); + } + } + if (simd_maski != ((1 << vstep) - 1)) { + float NPY_DECL_ALIGNED(NPY_SIMD_WIDTH) ip_fback[npyv_nlanes_f32]; + npyv_storea_f32(ip_fback, x_in); + + // process elements using libc for large elements + if (trig_op == SIMD_COMPUTE_COS) { + for (unsigned i = 0; i < npyv_nlanes_f32; ++i) { + if ((simd_maski >> i) & 1) { + continue; + } + dst[sdst*i] = npy_cosf(ip_fback[i]); + } + } + else { + for (unsigned i = 0; i < npyv_nlanes_f32; ++i) { + if ((simd_maski >> i) & 1) { + continue; + } + dst[sdst*i] = npy_sinf(ip_fback[i]); + } + } + } + } + npyv_cleanup(); +} +#endif // NPY_SIMD_FMA3 + +/**begin repeat + * #func = cos, sin# + * #enum = SIMD_COMPUTE_COS, SIMD_COMPUTE_SIN# + */ +NPY_NO_EXPORT void NPY_CPU_DISPATCH_CURFX(FLOAT_@func@) +(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data)) +{ + const float *src = (float*)args[0]; + float *dst = (float*)args[1]; + + const int lsize = sizeof(src[0]); + const npy_intp ssrc = steps[0] / lsize; + const npy_intp sdst = steps[1] / lsize; + npy_intp len = dimensions[0]; + assert(len <= 1 || (steps[0] % lsize == 0 && steps[1] % lsize == 0)); +#if NPY_SIMD_FMA3 + if (is_mem_overlap(src, steps[0], dst, steps[1], len) || + !npyv_loadable_stride_f32(ssrc) || !npyv_storable_stride_f32(sdst) + ) { + for (; len > 0; --len, src += ssrc, dst += sdst) { + simd_sincos_f32(src, 1, dst, 1, 1, @enum@); + } + } else { + simd_sincos_f32(src, ssrc, dst, sdst, len, @enum@); + } +#else + for (; len > 0; --len, src += ssrc, dst += sdst) { + const float src0 = *src; + *dst = npy_@func@f(src0); + } +#endif +} +/**end repeat**/ diff --git a/numpy/core/src/umath/loops_umath_fp.dispatch.c.src b/numpy/core/src/umath/loops_umath_fp.dispatch.c.src new file mode 100644 index 000000000000..a8289fc51092 --- /dev/null +++ b/numpy/core/src/umath/loops_umath_fp.dispatch.c.src @@ -0,0 +1,141 @@ +/*@targets + ** $maxopt baseline avx512_skx + */ +#include "numpy/npy_math.h" +#include "simd/simd.h" +#include "loops_utils.h" +#include "loops.h" +#include "npy_svml.h" +#include "fast_loop_macros.h" + +#if NPY_SIMD && defined(NPY_HAVE_AVX512_SKX) && defined(NPY_CAN_LINK_SVML) +/**begin repeat + * #sfx = f32, f64# + * #func_suffix = f16, 8# + */ +/**begin repeat1 + * #func = tanh, exp2, log2, log10, expm1, log1p, cbrt, tan, asin, acos, atan, sinh, cosh, asinh, acosh, atanh# + * #default_val = 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0# + */ +static void +simd_@func@_@sfx@(const npyv_lanetype_@sfx@ *src, npy_intp ssrc, + npyv_lanetype_@sfx@ *dst, npy_intp sdst, npy_intp len) +{ + const int vstep = npyv_nlanes_@sfx@; + for (; len > 0; len -= vstep, src += ssrc*vstep, dst += sdst*vstep) { + npyv_@sfx@ x; + #if @default_val@ + if (ssrc == 1) { + x = npyv_load_till_@sfx@(src, len, @default_val@); + } else { + x = npyv_loadn_till_@sfx@(src, ssrc, len, @default_val@); + } + #else + if (ssrc == 1) { + x = npyv_load_tillz_@sfx@(src, len); + } else { + x = npyv_loadn_tillz_@sfx@(src, ssrc, len); + } + #endif + npyv_@sfx@ out = __svml_@func@@func_suffix@(x); + if (sdst == 1) { + npyv_store_till_@sfx@(dst, len, out); + } else { + npyv_storen_till_@sfx@(dst, sdst, len, out); + } + } + npyv_cleanup(); +} +/**end repeat1**/ +/**end repeat**/ + +/**begin repeat + * #func = sin, cos# + */ +static void +simd_@func@_f64(const double *src, npy_intp ssrc, + double *dst, npy_intp sdst, npy_intp len) +{ + const int vstep = npyv_nlanes_f64; + for (; len > 0; len -= vstep, src += ssrc*vstep, dst += sdst*vstep) { + npyv_f64 x; + if (ssrc == 1) { + x = npyv_load_tillz_f64(src, len); + } else { + x = npyv_loadn_tillz_f64(src, ssrc, len); + } + npyv_f64 out = __svml_@func@8(x); + if (sdst == 1) { + npyv_store_till_f64(dst, len, out); + } else { + npyv_storen_till_f64(dst, sdst, len, out); + } + } + npyv_cleanup(); +} +/**end repeat**/ +#endif + +/**begin repeat + * #TYPE = DOUBLE, FLOAT# + * #type = npy_double, npy_float# + * #vsub = , f# + * #sfx = f64, f32# + */ +/**begin repeat1 + * #func = tanh, exp2, log2, log10, expm1, log1p, cbrt, tan, arcsin, arccos, arctan, sinh, cosh, arcsinh, arccosh, arctanh# + * #intrin = tanh, exp2, log2, log10, expm1, log1p, cbrt, tan, asin, acos, atan, sinh, cosh, asinh, acosh, atanh# + */ +NPY_NO_EXPORT void NPY_CPU_DISPATCH_CURFX(@TYPE@_@func@) +(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data)) +{ +#if NPY_SIMD && defined(NPY_HAVE_AVX512_SKX) && defined(NPY_CAN_LINK_SVML) + const @type@ *src = (@type@*)args[0]; + @type@ *dst = (@type@*)args[1]; + const int lsize = sizeof(src[0]); + const npy_intp ssrc = steps[0] / lsize; + const npy_intp sdst = steps[1] / lsize; + const npy_intp len = dimensions[0]; + assert(len <= 1 || (steps[0] % lsize == 0 && steps[1] % lsize == 0)); + if (!is_mem_overlap(src, steps[0], dst, steps[1], len) && + npyv_loadable_stride_@sfx@(ssrc) && + npyv_storable_stride_@sfx@(sdst)) { + simd_@intrin@_@sfx@(src, ssrc, dst, sdst, len); + return; + } +#endif + UNARY_LOOP { + const @type@ in1 = *(@type@ *)ip1; + *(@type@ *)op1 = npy_@intrin@@vsub@(in1); + } +} +/**end repeat1**/ +/**end repeat**/ + +/**begin repeat + * #func = sin, cos# + */ +NPY_NO_EXPORT void NPY_CPU_DISPATCH_CURFX(DOUBLE_@func@) +(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data)) +{ +#if NPY_SIMD && defined(NPY_HAVE_AVX512_SKX) && defined(NPY_CAN_LINK_SVML) + const double *src = (double*)args[0]; + double *dst = (double*)args[1]; + const int lsize = sizeof(src[0]); + const npy_intp ssrc = steps[0] / lsize; + const npy_intp sdst = steps[1] / lsize; + const npy_intp len = dimensions[0]; + assert(len <= 1 || (steps[0] % lsize == 0 && steps[1] % lsize == 0)); + if (!is_mem_overlap(src, steps[0], dst, steps[1], len) && + npyv_loadable_stride_f64(ssrc) && + npyv_storable_stride_f64(sdst)) { + simd_@func@_f64(src, ssrc, dst, sdst, len); + return; + } +#endif + UNARY_LOOP { + const npy_double in1 = *(npy_double *)ip1; + *(npy_double *)op1 = npy_@func@(in1); + } +} +/**end repeat**/ diff --git a/numpy/core/src/umath/loops_unary_fp.dispatch.c.src b/numpy/core/src/umath/loops_unary_fp.dispatch.c.src new file mode 100644 index 000000000000..93761b98c04e --- /dev/null +++ b/numpy/core/src/umath/loops_unary_fp.dispatch.c.src @@ -0,0 +1,306 @@ +/*@targets + ** $maxopt baseline + ** sse2 sse41 + ** vsx2 + ** neon asimd + **/ +/** + * Force use SSE only on x86, even if AVX2 or AVX512F are enabled + * through the baseline, since scatter(AVX512F) and gather very costly + * to handle non-contiguous memory access comparing with SSE for + * such small operations that this file covers. +*/ +#define NPY_SIMD_FORCE_128 +#include "numpy/npy_math.h" +#include "simd/simd.h" +#include "loops_utils.h" +#include "loops.h" +/********************************************************** + ** Scalars + **********************************************************/ +#if !NPY_SIMD +NPY_FINLINE float c_recip_f32(float a) +{ return 1.0f / a; } +NPY_FINLINE float c_abs_f32(float a) +{ + const float tmp = a > 0 ? a : -a; + /* add 0 to clear -0.0 */ + return tmp + 0; +} +NPY_FINLINE float c_square_f32(float a) +{ return a * a; } +#endif // !NPY_SIMD + +#if !NPY_SIMD_F64 +NPY_FINLINE double c_recip_f64(double a) +{ return 1.0 / a; } +NPY_FINLINE double c_abs_f64(double a) +{ + const double tmp = a > 0 ? a : -a; + /* add 0 to clear -0.0 */ + return tmp + 0; +} +NPY_FINLINE double c_square_f64(double a) +{ return a * a; } +#endif // !NPY_SIMD_F64 +/** + * MSVC(32-bit mode) requires a clarified contiguous loop + * in order to use SSE, otherwise it uses a soft version of square root + * that doesn't raise a domain error. + */ +#if defined(_MSC_VER) && defined(_M_IX86) && !NPY_SIMD + #include <emmintrin.h> + NPY_FINLINE float c_sqrt_f32(float _a) + { + __m128 a = _mm_load_ss(&_a); + __m128 lower = _mm_sqrt_ss(a); + return _mm_cvtss_f32(lower); + } + NPY_FINLINE double c_sqrt_f64(double _a) + { + __m128d a = _mm_load_sd(&_a); + __m128d lower = _mm_sqrt_pd(a); + return _mm_cvtsd_f64(lower); + } +#else + #define c_sqrt_f32 npy_sqrtf + #define c_sqrt_f64 npy_sqrt +#endif + +#define c_ceil_f32 npy_ceilf +#define c_ceil_f64 npy_ceil + +/******************************************************************************** + ** Defining the SIMD kernels + ********************************************************************************/ +/** Notes: + * - avoid the use of libmath to unify fp/domain errors + * for both scalars and vectors among all compilers/architectures. + * - use intrinsic npyv_load_till_* instead of npyv_load_tillz_ + * to fill the remind lanes with 1.0 to avoid divide by zero fp + * exception in reciprocal. + */ +#define CONTIG 0 +#define NCONTIG 1 + +/* + * clang has a bug that's present at -O1 or greater. When partially loading a + * vector register for a reciprocal operation, the remaining elements are set + * to 1 to avoid divide-by-zero. The partial load is paired with a partial + * store after the reciprocal operation. clang notices that the entire register + * is not needed for the store and optimizes out the fill of 1 to the remaining + * elements. This causes either a divide-by-zero or 0/0 with invalid exception + * that we were trying to avoid by filling. + * + * Using a dummy variable marked 'volatile' convinces clang not to ignore + * the explicit fill of remaining elements. If `-ftrapping-math` is + * supported, then it'll also avoid the bug. `-ftrapping-math` is supported + * on Apple clang v12+ for x86_64. It is not currently supported for arm64. + * `-ftrapping-math` is set by default of Numpy builds in + * numpy/distutils/ccompiler.py. + * + * Note: Apple clang and clang upstream have different versions that overlap + */ +#if defined(__clang__) + #if defined(__apple_build_version__) + // Apple Clang + #if __apple_build_version__ < 12000000 + // Apple Clang before v12 + #define WORKAROUND_CLANG_RECIPROCAL_BUG 1 + #elif defined(NPY_CPU_X86) || defined(NPY_CPU_AMD64) + // Apple Clang after v12, targeting i386 or x86_64 + #define WORKAROUND_CLANG_RECIPROCAL_BUG 0 + #else + // Apple Clang after v12, not targeting i386 or x86_64 + #define WORKAROUND_CLANG_RECIPROCAL_BUG 1 + #endif + #else + // Clang, not Apple Clang + #if __clang_major__ < 10 + // Clang before v10 + #define WORKAROUND_CLANG_RECIPROCAL_BUG 1 + #elif defined(NPY_CPU_X86) || defined(NPY_CPU_AMD64) + // Clang v10+, targeting i386 or x86_64 + #define WORKAROUND_CLANG_RECIPROCAL_BUG 0 + #else + // Clang v10+, not targeting i386 or x86_64 + #define WORKAROUND_CLANG_RECIPROCAL_BUG 1 + #endif + #endif +#else +// Not a Clang compiler +#define WORKAROUND_CLANG_RECIPROCAL_BUG 0 +#endif + +/**begin repeat + * #TYPE = FLOAT, DOUBLE# + * #sfx = f32, f64# + * #VCHK = NPY_SIMD, NPY_SIMD_F64# + */ +#if @VCHK@ +/**begin repeat1 + * #kind = ceil, sqrt, absolute, square, reciprocal# + * #intr = ceil, sqrt, abs, square, recip# + * #repl_0w1 = 0, 0, 0, 0, 1# + * #RECIP_WORKAROUND = 0, 0, 0, 0, WORKAROUND_CLANG_RECIPROCAL_BUG# + */ +/**begin repeat2 + * #STYPE = CONTIG, NCONTIG, CONTIG, NCONTIG# + * #DTYPE = CONTIG, CONTIG, NCONTIG, NCONTIG# + * #unroll = 4, 4, 2, 2# + */ +static void simd_@TYPE@_@kind@_@STYPE@_@DTYPE@ +(const void *_src, npy_intp ssrc, void *_dst, npy_intp sdst, npy_intp len) +{ + const npyv_lanetype_@sfx@ *src = _src; + npyv_lanetype_@sfx@ *dst = _dst; + + const int vstep = npyv_nlanes_@sfx@; + const int wstep = vstep * @unroll@; + + // unrolled iterations + for (; len >= wstep; len -= wstep, src += ssrc*wstep, dst += sdst*wstep) { + /**begin repeat3 + * #N = 0, 1, 2, 3# + */ + #if @unroll@ > @N@ + #if @STYPE@ == CONTIG + npyv_@sfx@ v_src@N@ = npyv_load_@sfx@(src + vstep*@N@); + #else + npyv_@sfx@ v_src@N@ = npyv_loadn_@sfx@(src + ssrc*vstep*@N@, ssrc); + #endif + npyv_@sfx@ v_unary@N@ = npyv_@intr@_@sfx@(v_src@N@); + #endif + /**end repeat3**/ + /**begin repeat3 + * #N = 0, 1, 2, 3# + */ + #if @unroll@ > @N@ + #if @DTYPE@ == CONTIG + npyv_store_@sfx@(dst + vstep*@N@, v_unary@N@); + #else + npyv_storen_@sfx@(dst + sdst*vstep*@N@, sdst, v_unary@N@); + #endif + #endif + /**end repeat3**/ + } + + // vector-sized iterations + for (; len >= vstep; len -= vstep, src += ssrc*vstep, dst += sdst*vstep) { + #if @STYPE@ == CONTIG + npyv_@sfx@ v_src0 = npyv_load_@sfx@(src); + #else + npyv_@sfx@ v_src0 = npyv_loadn_@sfx@(src, ssrc); + #endif + npyv_@sfx@ v_unary0 = npyv_@intr@_@sfx@(v_src0); + #if @DTYPE@ == CONTIG + npyv_store_@sfx@(dst, v_unary0); + #else + npyv_storen_@sfx@(dst, sdst, v_unary0); + #endif + } + + // last partial iteration, if needed + if(len > 0){ + #if @STYPE@ == CONTIG + #if @repl_0w1@ + npyv_@sfx@ v_src0 = npyv_load_till_@sfx@(src, len, 1); + #else + npyv_@sfx@ v_src0 = npyv_load_tillz_@sfx@(src, len); + #endif + #else + #if @repl_0w1@ + npyv_@sfx@ v_src0 = npyv_loadn_till_@sfx@(src, ssrc, len, 1); + #else + npyv_@sfx@ v_src0 = npyv_loadn_tillz_@sfx@(src, ssrc, len); + #endif + #endif + #if @RECIP_WORKAROUND@ + /* + * Workaround clang bug. We use a dummy variable marked 'volatile' + * to convince clang that the entire vector is needed. We only + * want to do this for the last iteration / partial load-store of + * the loop since 'volatile' forces a refresh of the contents. + */ + volatile npyv_@sfx@ unused_but_workaround_bug = v_src0; + #endif // @RECIP_WORKAROUND@ + npyv_@sfx@ v_unary0 = npyv_@intr@_@sfx@(v_src0); + #if @DTYPE@ == CONTIG + npyv_store_till_@sfx@(dst, len, v_unary0); + #else + npyv_storen_till_@sfx@(dst, sdst, len, v_unary0); + #endif + } + + npyv_cleanup(); +} +/**end repeat2**/ +/**end repeat1**/ +#endif // @VCHK@ +/**end repeat**/ + +#undef WORKAROUND_CLANG_RECIPROCAL_BUG + +/******************************************************************************** + ** Defining ufunc inner functions + ********************************************************************************/ +/**begin repeat + * #TYPE = FLOAT, DOUBLE# + * #sfx = f32, f64# + * #VCHK = NPY_SIMD, NPY_SIMD_F64# + */ +/**begin repeat1 + * #kind = ceil, sqrt, absolute, square, reciprocal# + * #intr = ceil, sqrt, abs, square, recip# + * #clear = 0, 0, 1, 0, 0# + */ +NPY_NO_EXPORT void NPY_CPU_DISPATCH_CURFX(@TYPE@_@kind@) +(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) +{ + const char *src = args[0]; char *dst = args[1]; + const npy_intp src_step = steps[0]; + const npy_intp dst_step = steps[1]; + npy_intp len = dimensions[0]; +#if @VCHK@ + const int lsize = sizeof(npyv_lanetype_@sfx@); + assert(len <= 1 || (src_step % lsize == 0 && dst_step % lsize == 0)); + if (is_mem_overlap(src, src_step, dst, dst_step, len)) { + goto no_unroll; + } + const npy_intp ssrc = src_step / lsize; + const npy_intp sdst = dst_step / lsize; + if (!npyv_loadable_stride_@sfx@(ssrc) || !npyv_storable_stride_@sfx@(sdst)) { + goto no_unroll; + } + if (ssrc == 1 && sdst == 1) { + simd_@TYPE@_@kind@_CONTIG_CONTIG(src, 1, dst, 1, len); + } + else if (sdst == 1) { + simd_@TYPE@_@kind@_NCONTIG_CONTIG(src, ssrc, dst, 1, len); + } + else if (ssrc == 1) { + simd_@TYPE@_@kind@_CONTIG_NCONTIG(src, 1, dst, sdst, len); + } else { + simd_@TYPE@_@kind@_NCONTIG_NCONTIG(src, ssrc, dst, sdst, len); + } + goto clear; +no_unroll: +#endif // @VCHK@ + for (; len > 0; --len, src += src_step, dst += dst_step) { + #if @VCHK@ + // to guarantee the same precsion and fp/domain errors for both scalars and vectors + simd_@TYPE@_@kind@_CONTIG_CONTIG(src, 0, dst, 0, 1); + #else + const npyv_lanetype_@sfx@ src0 = *(npyv_lanetype_@sfx@*)src; + *(npyv_lanetype_@sfx@*)dst = c_@intr@_@sfx@(src0); + #endif + } +#if @VCHK@ +clear:; +#endif +#if @clear@ + npy_clear_floatstatus_barrier((char*)dimensions); +#endif +} +/**end repeat1**/ +/**end repeat**/ diff --git a/numpy/core/src/umath/loops_utils.h.src b/numpy/core/src/umath/loops_utils.h.src new file mode 100644 index 000000000000..762e9ee59bed --- /dev/null +++ b/numpy/core/src/umath/loops_utils.h.src @@ -0,0 +1,224 @@ +#ifndef _NPY_UMATH_LOOPS_UTILS_H_ +#define _NPY_UMATH_LOOPS_UTILS_H_ + +#include "numpy/npy_common.h" // NPY_FINLINE +#include "numpy/halffloat.h" // npy_half_to_float + +/** + * Old versions of MSVC causes ambiguous link errors when we deal with large SIMD kernels + * which lead to break the build, probably related to the following bug: + * https://developercommunity.visualstudio.com/content/problem/415095/internal-compiler-error-with-perfectly-forwarded-r.html + */ +#if defined(_MSC_VER) && _MSC_VER < 1916 + #define SIMD_MSVC_NOINLINE __declspec(noinline) +#else + #define SIMD_MSVC_NOINLINE +#endif +/* + * nomemoverlap - returns false if two strided arrays have an overlapping + * region in memory. ip_size/op_size = size of the arrays which can be negative + * indicating negative steps. + */ +NPY_FINLINE npy_bool +nomemoverlap(char *ip, npy_intp ip_size, char *op, npy_intp op_size) +{ + char *ip_start, *ip_end, *op_start, *op_end; + if (ip_size < 0) { + ip_start = ip + ip_size; + ip_end = ip; + } + else { + ip_start = ip; + ip_end = ip + ip_size; + } + if (op_size < 0) { + op_start = op + op_size; + op_end = op; + } + else { + op_start = op; + op_end = op + op_size; + } + return (ip_start == op_start && op_end == ip_end) || + (ip_start > op_end) || (op_start > ip_end); +} + +// returns true if two strided arrays have an overlapping region in memory +// same as `nomemoverlap()` but requires array length and step sizes +NPY_FINLINE npy_bool +is_mem_overlap(const void *src, npy_intp src_step, const void *dst, npy_intp dst_step, npy_intp len) +{ + return !(nomemoverlap((char*)src, src_step*len, (char*)dst, dst_step*len)); +} + +/* + * cutoff blocksize for pairwise summation + * decreasing it decreases errors slightly as more pairs are summed but + * also lowers performance, as the inner loop is unrolled eight times it is + * effectively 16 + */ +#define PW_BLOCKSIZE 128 + +/**begin repeat + * Float types + * #type = npy_float, npy_double, npy_longdouble, npy_float# + * #dtype = npy_float, npy_double, npy_longdouble, npy_half# + * #TYPE = FLOAT, DOUBLE, LONGDOUBLE, HALF# + * #c = f, , l, # + * #C = F, , L, # + * #trf = , , , npy_half_to_float# + */ + +/* + * Pairwise summation, rounding error O(lg n) instead of O(n). + * The recursion depth is O(lg n) as well. + * when updating also update similar complex floats summation + */ +static NPY_INLINE @type@ +@TYPE@_pairwise_sum(char *a, npy_intp n, npy_intp stride) +{ + if (n < 8) { + npy_intp i; + @type@ res = 0.; + + for (i = 0; i < n; i++) { + res += @trf@(*((@dtype@*)(a + i * stride))); + } + return res; + } + else if (n <= PW_BLOCKSIZE) { + npy_intp i; + @type@ r[8], res; + + /* + * sum a block with 8 accumulators + * 8 times unroll reduces blocksize to 16 and allows vectorization with + * avx without changing summation ordering + */ + r[0] = @trf@(*((@dtype@ *)(a + 0 * stride))); + r[1] = @trf@(*((@dtype@ *)(a + 1 * stride))); + r[2] = @trf@(*((@dtype@ *)(a + 2 * stride))); + r[3] = @trf@(*((@dtype@ *)(a + 3 * stride))); + r[4] = @trf@(*((@dtype@ *)(a + 4 * stride))); + r[5] = @trf@(*((@dtype@ *)(a + 5 * stride))); + r[6] = @trf@(*((@dtype@ *)(a + 6 * stride))); + r[7] = @trf@(*((@dtype@ *)(a + 7 * stride))); + + for (i = 8; i < n - (n % 8); i += 8) { + /* small blocksizes seems to mess with hardware prefetch */ + NPY_PREFETCH(a + (i + 512/(npy_intp)sizeof(@dtype@))*stride, 0, 3); + r[0] += @trf@(*((@dtype@ *)(a + (i + 0) * stride))); + r[1] += @trf@(*((@dtype@ *)(a + (i + 1) * stride))); + r[2] += @trf@(*((@dtype@ *)(a + (i + 2) * stride))); + r[3] += @trf@(*((@dtype@ *)(a + (i + 3) * stride))); + r[4] += @trf@(*((@dtype@ *)(a + (i + 4) * stride))); + r[5] += @trf@(*((@dtype@ *)(a + (i + 5) * stride))); + r[6] += @trf@(*((@dtype@ *)(a + (i + 6) * stride))); + r[7] += @trf@(*((@dtype@ *)(a + (i + 7) * stride))); + } + + /* accumulate now to avoid stack spills for single peel loop */ + res = ((r[0] + r[1]) + (r[2] + r[3])) + + ((r[4] + r[5]) + (r[6] + r[7])); + + /* do non multiple of 8 rest */ + for (; i < n; i++) { + res += @trf@(*((@dtype@ *)(a + i * stride))); + } + return res; + } + else { + /* divide by two but avoid non-multiples of unroll factor */ + npy_intp n2 = n / 2; + + n2 -= n2 % 8; + return @TYPE@_pairwise_sum(a, n2, stride) + + @TYPE@_pairwise_sum(a + n2 * stride, n - n2, stride); + } +} + +/**end repeat**/ + +/**begin repeat + * complex types + * #TYPE = CFLOAT, CDOUBLE, CLONGDOUBLE# + * #ftype = npy_float, npy_double, npy_longdouble# + * #c = f, , l# + * #C = F, , L# + * #SIMD = 1, 1, 0# + */ +/* similar to pairwise sum of real floats */ +static NPY_INLINE void +@TYPE@_pairwise_sum(@ftype@ *rr, @ftype@ * ri, char * a, npy_intp n, + npy_intp stride) +{ + assert(n % 2 == 0); + if (n < 8) { + npy_intp i; + + *rr = 0.; + *ri = 0.; + for (i = 0; i < n; i += 2) { + *rr += *((@ftype@ *)(a + i * stride + 0)); + *ri += *((@ftype@ *)(a + i * stride + sizeof(@ftype@))); + } + return; + } + else if (n <= PW_BLOCKSIZE) { + npy_intp i; + @ftype@ r[8]; + + /* + * sum a block with 8 accumulators + * 8 times unroll reduces blocksize to 16 and allows vectorization with + * avx without changing summation ordering + */ + r[0] = *((@ftype@ *)(a + 0 * stride)); + r[1] = *((@ftype@ *)(a + 0 * stride + sizeof(@ftype@))); + r[2] = *((@ftype@ *)(a + 2 * stride)); + r[3] = *((@ftype@ *)(a + 2 * stride + sizeof(@ftype@))); + r[4] = *((@ftype@ *)(a + 4 * stride)); + r[5] = *((@ftype@ *)(a + 4 * stride + sizeof(@ftype@))); + r[6] = *((@ftype@ *)(a + 6 * stride)); + r[7] = *((@ftype@ *)(a + 6 * stride + sizeof(@ftype@))); + + for (i = 8; i < n - (n % 8); i += 8) { + /* small blocksizes seems to mess with hardware prefetch */ + NPY_PREFETCH(a + (i + 512/(npy_intp)sizeof(@ftype@))*stride, 0, 3); + r[0] += *((@ftype@ *)(a + (i + 0) * stride)); + r[1] += *((@ftype@ *)(a + (i + 0) * stride + sizeof(@ftype@))); + r[2] += *((@ftype@ *)(a + (i + 2) * stride)); + r[3] += *((@ftype@ *)(a + (i + 2) * stride + sizeof(@ftype@))); + r[4] += *((@ftype@ *)(a + (i + 4) * stride)); + r[5] += *((@ftype@ *)(a + (i + 4) * stride + sizeof(@ftype@))); + r[6] += *((@ftype@ *)(a + (i + 6) * stride)); + r[7] += *((@ftype@ *)(a + (i + 6) * stride + sizeof(@ftype@))); + } + + /* accumulate now to avoid stack spills for single peel loop */ + *rr = ((r[0] + r[2]) + (r[4] + r[6])); + *ri = ((r[1] + r[3]) + (r[5] + r[7])); + + /* do non multiple of 8 rest */ + for (; i < n; i+=2) { + *rr += *((@ftype@ *)(a + i * stride + 0)); + *ri += *((@ftype@ *)(a + i * stride + sizeof(@ftype@))); + } + return; + } + else { + /* divide by two but avoid non-multiples of unroll factor */ + @ftype@ rr1, ri1, rr2, ri2; + npy_intp n2 = n / 2; + + n2 -= n2 % 8; + @TYPE@_pairwise_sum(&rr1, &ri1, a, n2, stride); + @TYPE@_pairwise_sum(&rr2, &ri2, a + n2 * stride, n - n2, stride); + *rr = rr1 + rr2; + *ri = ri1 + ri2; + return; + } +} +/**end repeat**/ + +#endif // _NPY_UMATH_LOOPS_UTILS_H_ diff --git a/numpy/core/src/umath/matmul.c.src b/numpy/core/src/umath/matmul.c.src new file mode 100644 index 000000000000..4dd0c475968e --- /dev/null +++ b/numpy/core/src/umath/matmul.c.src @@ -0,0 +1,511 @@ +/* -*- c -*- */ +#define PY_SSIZE_T_CLEAN +#include <Python.h> + +#define _UMATHMODULE +#define _MULTIARRAYMODULE +#define NPY_NO_DEPRECATED_API NPY_API_VERSION + +#include "npy_config.h" +#include "numpy/npy_common.h" +#include "numpy/arrayobject.h" +#include "numpy/ufuncobject.h" +#include "numpy/npy_math.h" +#include "numpy/halffloat.h" +#include "lowlevel_strided_loops.h" + +#include "npy_pycompat.h" + +#include "npy_cblas.h" +#include "arraytypes.h" /* For TYPE_dot functions */ + +#include <assert.h> + +/* + ***************************************************************************** + ** BASICS ** + ***************************************************************************** + */ + +#if defined(HAVE_CBLAS) +/* + * -1 to be conservative, in case blas internally uses a for loop with an + * inclusive upper bound + */ +#ifndef HAVE_BLAS_ILP64 +#define BLAS_MAXSIZE (NPY_MAX_INT - 1) +#else +#define BLAS_MAXSIZE (NPY_MAX_INT64 - 1) +#endif + +/* + * Determine if a 2d matrix can be used by BLAS + * 1. Strides must not alias or overlap + * 2. The faster (second) axis must be contiguous + * 3. The slower (first) axis stride, in unit steps, must be larger than + * the faster axis dimension + */ +static NPY_INLINE npy_bool +is_blasable2d(npy_intp byte_stride1, npy_intp byte_stride2, + npy_intp d1, npy_intp d2, npy_intp itemsize) +{ + npy_intp unit_stride1 = byte_stride1 / itemsize; + if (byte_stride2 != itemsize) { + return NPY_FALSE; + } + if ((byte_stride1 % itemsize ==0) && + (unit_stride1 >= d2) && + (unit_stride1 <= BLAS_MAXSIZE)) + { + return NPY_TRUE; + } + return NPY_FALSE; +} + +static const npy_cdouble oneD = {1.0, 0.0}, zeroD = {0.0, 0.0}; +static const npy_cfloat oneF = {1.0, 0.0}, zeroF = {0.0, 0.0}; + +/**begin repeat + * + * #name = FLOAT, DOUBLE, CFLOAT, CDOUBLE# + * #ctype = npy_float, npy_double, npy_cfloat, npy_cdouble# + * #typ = npy_float, npy_double, npy_cfloat, npy_cdouble# + * #prefix = s, d, c, z# + * #step1 = 1.F, 1., &oneF, &oneD# + * #step0 = 0.F, 0., &zeroF, &zeroD# + */ +NPY_NO_EXPORT void +@name@_gemv(void *ip1, npy_intp is1_m, npy_intp is1_n, + void *ip2, npy_intp is2_n, npy_intp NPY_UNUSED(is2_p), + void *op, npy_intp op_m, npy_intp NPY_UNUSED(op_p), + npy_intp m, npy_intp n, npy_intp NPY_UNUSED(p)) +{ + /* + * Vector matrix multiplication -- Level 2 BLAS + * arguments + * ip1: contiguous data, m*n shape + * ip2: data in c order, n*1 shape + * op: data in c order, m shape + */ + enum CBLAS_ORDER order; + CBLAS_INT M, N, lda; + + assert(m <= BLAS_MAXSIZE && n <= BLAS_MAXSIZE); + assert (is_blasable2d(is2_n, sizeof(@typ@), n, 1, sizeof(@typ@))); + M = (CBLAS_INT)m; + N = (CBLAS_INT)n; + + if (is_blasable2d(is1_m, is1_n, m, n, sizeof(@typ@))) { + order = CblasColMajor; + lda = (CBLAS_INT)(is1_m / sizeof(@typ@)); + } + else { + /* If not ColMajor, caller should have ensured we are RowMajor */ + /* will not assert in release mode */ + order = CblasRowMajor; + assert(is_blasable2d(is1_n, is1_m, n, m, sizeof(@typ@))); + lda = (CBLAS_INT)(is1_n / sizeof(@typ@)); + } + CBLAS_FUNC(cblas_@prefix@gemv)(order, CblasTrans, N, M, @step1@, ip1, lda, ip2, + is2_n / sizeof(@typ@), @step0@, op, op_m / sizeof(@typ@)); +} + +NPY_NO_EXPORT void +@name@_matmul_matrixmatrix(void *ip1, npy_intp is1_m, npy_intp is1_n, + void *ip2, npy_intp is2_n, npy_intp is2_p, + void *op, npy_intp os_m, npy_intp os_p, + npy_intp m, npy_intp n, npy_intp p) +{ + /* + * matrix matrix multiplication -- Level 3 BLAS + */ + enum CBLAS_ORDER order = CblasRowMajor; + enum CBLAS_TRANSPOSE trans1, trans2; + CBLAS_INT M, N, P, lda, ldb, ldc; + assert(m <= BLAS_MAXSIZE && n <= BLAS_MAXSIZE && p <= BLAS_MAXSIZE); + M = (CBLAS_INT)m; + N = (CBLAS_INT)n; + P = (CBLAS_INT)p; + + assert(is_blasable2d(os_m, os_p, m, p, sizeof(@typ@))); + ldc = (CBLAS_INT)(os_m / sizeof(@typ@)); + + if (is_blasable2d(is1_m, is1_n, m, n, sizeof(@typ@))) { + trans1 = CblasNoTrans; + lda = (CBLAS_INT)(is1_m / sizeof(@typ@)); + } + else { + /* If not ColMajor, caller should have ensured we are RowMajor */ + /* will not assert in release mode */ + assert(is_blasable2d(is1_n, is1_m, n, m, sizeof(@typ@))); + trans1 = CblasTrans; + lda = (CBLAS_INT)(is1_n / sizeof(@typ@)); + } + + if (is_blasable2d(is2_n, is2_p, n, p, sizeof(@typ@))) { + trans2 = CblasNoTrans; + ldb = (CBLAS_INT)(is2_n / sizeof(@typ@)); + } + else { + /* If not ColMajor, caller should have ensured we are RowMajor */ + /* will not assert in release mode */ + assert(is_blasable2d(is2_p, is2_n, p, n, sizeof(@typ@))); + trans2 = CblasTrans; + ldb = (CBLAS_INT)(is2_p / sizeof(@typ@)); + } + /* + * Use syrk if we have a case of a matrix times its transpose. + * Otherwise, use gemm for all other cases. + */ + if ( + (ip1 == ip2) && + (m == p) && + (is1_m == is2_p) && + (is1_n == is2_n) && + (trans1 != trans2) + ) { + npy_intp i,j; + if (trans1 == CblasNoTrans) { + CBLAS_FUNC(cblas_@prefix@syrk)( + order, CblasUpper, trans1, P, N, @step1@, + ip1, lda, @step0@, op, ldc); + } + else { + CBLAS_FUNC(cblas_@prefix@syrk)( + order, CblasUpper, trans1, P, N, @step1@, + ip1, ldb, @step0@, op, ldc); + } + /* Copy the triangle */ + for (i = 0; i < P; i++) { + for (j = i + 1; j < P; j++) { + ((@typ@*)op)[j * ldc + i] = ((@typ@*)op)[i * ldc + j]; + } + } + + } + else { + CBLAS_FUNC(cblas_@prefix@gemm)( + order, trans1, trans2, M, P, N, @step1@, ip1, lda, + ip2, ldb, @step0@, op, ldc); + } +} + +/**end repeat**/ +#endif + +/* + * matmul loops + * signature is (m?,n),(n,p?)->(m?,p?) + */ + +/**begin repeat + * #TYPE = LONGDOUBLE, + * FLOAT, DOUBLE, HALF, + * CFLOAT, CDOUBLE, CLONGDOUBLE, + * UBYTE, USHORT, UINT, ULONG, ULONGLONG, + * BYTE, SHORT, INT, LONG, LONGLONG# + * #typ = npy_longdouble, + * npy_float,npy_double,npy_half, + * npy_cfloat, npy_cdouble, npy_clongdouble, + * npy_ubyte, npy_ushort, npy_uint, npy_ulong, npy_ulonglong, + * npy_byte, npy_short, npy_int, npy_long, npy_longlong# + * #IS_COMPLEX = 0, 0, 0, 0, 1, 1, 1, 0*10# + * #IS_HALF = 0, 0, 0, 1, 0*13# + */ + +NPY_NO_EXPORT void +@TYPE@_matmul_inner_noblas(void *_ip1, npy_intp is1_m, npy_intp is1_n, + void *_ip2, npy_intp is2_n, npy_intp is2_p, + void *_op, npy_intp os_m, npy_intp os_p, + npy_intp dm, npy_intp dn, npy_intp dp) + +{ + npy_intp m, n, p; + npy_intp ib1_n, ib2_n, ib2_p, ob_p; + char *ip1 = (char *)_ip1, *ip2 = (char *)_ip2, *op = (char *)_op; + + ib1_n = is1_n * dn; + ib2_n = is2_n * dn; + ib2_p = is2_p * dp; + ob_p = os_p * dp; + + for (m = 0; m < dm; m++) { + for (p = 0; p < dp; p++) { +#if @IS_COMPLEX@ == 1 + (*(@typ@ *)op).real = 0; + (*(@typ@ *)op).imag = 0; +#elif @IS_HALF@ + float sum = 0; +#else + *(@typ@ *)op = 0; +#endif + for (n = 0; n < dn; n++) { + @typ@ val1 = (*(@typ@ *)ip1); + @typ@ val2 = (*(@typ@ *)ip2); +#if @IS_HALF@ + sum += npy_half_to_float(val1) * npy_half_to_float(val2); +#elif @IS_COMPLEX@ == 1 + (*(@typ@ *)op).real += (val1.real * val2.real) - + (val1.imag * val2.imag); + (*(@typ@ *)op).imag += (val1.real * val2.imag) + + (val1.imag * val2.real); +#else + *(@typ@ *)op += val1 * val2; +#endif + ip2 += is2_n; + ip1 += is1_n; + } +#if @IS_HALF@ + *(@typ@ *)op = npy_float_to_half(sum); +#endif + ip1 -= ib1_n; + ip2 -= ib2_n; + op += os_p; + ip2 += is2_p; + } + op -= ob_p; + ip2 -= ib2_p; + ip1 += is1_m; + op += os_m; + } +} + +/**end repeat**/ +NPY_NO_EXPORT void +BOOL_matmul_inner_noblas(void *_ip1, npy_intp is1_m, npy_intp is1_n, + void *_ip2, npy_intp is2_n, npy_intp is2_p, + void *_op, npy_intp os_m, npy_intp os_p, + npy_intp dm, npy_intp dn, npy_intp dp) + +{ + npy_intp m, n, p; + npy_intp ib2_p, ob_p; + char *ip1 = (char *)_ip1, *ip2 = (char *)_ip2, *op = (char *)_op; + + ib2_p = is2_p * dp; + ob_p = os_p * dp; + + for (m = 0; m < dm; m++) { + for (p = 0; p < dp; p++) { + char *ip1tmp = ip1; + char *ip2tmp = ip2; + *(npy_bool *)op = NPY_FALSE; + for (n = 0; n < dn; n++) { + npy_bool val1 = (*(npy_bool *)ip1tmp); + npy_bool val2 = (*(npy_bool *)ip2tmp); + if (val1 != 0 && val2 != 0) { + *(npy_bool *)op = NPY_TRUE; + break; + } + ip2tmp += is2_n; + ip1tmp += is1_n; + } + op += os_p; + ip2 += is2_p; + } + op -= ob_p; + ip2 -= ib2_p; + ip1 += is1_m; + op += os_m; + } +} + +NPY_NO_EXPORT void +OBJECT_matmul_inner_noblas(void *_ip1, npy_intp is1_m, npy_intp is1_n, + void *_ip2, npy_intp is2_n, npy_intp is2_p, + void *_op, npy_intp os_m, npy_intp os_p, + npy_intp dm, npy_intp dn, npy_intp dp) +{ + char *ip1 = (char *)_ip1, *ip2 = (char *)_ip2, *op = (char *)_op; + + npy_intp ib1_n = is1_n * dn; + npy_intp ib2_n = is2_n * dn; + npy_intp ib2_p = is2_p * dp; + npy_intp ob_p = os_p * dp; + + PyObject *product, *sum_of_products = NULL; + + for (npy_intp m = 0; m < dm; m++) { + for (npy_intp p = 0; p < dp; p++) { + if ( 0 == dn ) { + sum_of_products = PyLong_FromLong(0); + if (sum_of_products == NULL) { + return; + } + } + + for (npy_intp n = 0; n < dn; n++) { + PyObject *obj1 = *(PyObject**)ip1, *obj2 = *(PyObject**)ip2; + if (obj1 == NULL) { + obj1 = Py_None; + } + if (obj2 == NULL) { + obj2 = Py_None; + } + + product = PyNumber_Multiply(obj1, obj2); + if (product == NULL) { + Py_XDECREF(sum_of_products); + return; + } + + if (n == 0) { + sum_of_products = product; + } + else { + Py_SETREF(sum_of_products, PyNumber_Add(sum_of_products, product)); + Py_DECREF(product); + if (sum_of_products == NULL) { + return; + } + } + + ip2 += is2_n; + ip1 += is1_n; + } + + *((PyObject **)op) = sum_of_products; + ip1 -= ib1_n; + ip2 -= ib2_n; + op += os_p; + ip2 += is2_p; + } + op -= ob_p; + ip2 -= ib2_p; + ip1 += is1_m; + op += os_m; + } +} + + +/**begin repeat + * #TYPE = FLOAT, DOUBLE, LONGDOUBLE, HALF, + * CFLOAT, CDOUBLE, CLONGDOUBLE, + * UBYTE, USHORT, UINT, ULONG, ULONGLONG, + * BYTE, SHORT, INT, LONG, LONGLONG, + * BOOL, OBJECT# + * #typ = npy_float,npy_double,npy_longdouble, npy_half, + * npy_cfloat, npy_cdouble, npy_clongdouble, + * npy_ubyte, npy_ushort, npy_uint, npy_ulong, npy_ulonglong, + * npy_byte, npy_short, npy_int, npy_long, npy_longlong, + * npy_bool,npy_object# + * #IS_COMPLEX = 0, 0, 0, 0, 1, 1, 1, 0*12# + * #USEBLAS = 1, 1, 0, 0, 1, 1, 0*13# + */ + + +NPY_NO_EXPORT void +@TYPE@_matmul(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) +{ + npy_intp dOuter = *dimensions++; + npy_intp iOuter; + npy_intp s0 = *steps++; + npy_intp s1 = *steps++; + npy_intp s2 = *steps++; + npy_intp dm = dimensions[0]; + npy_intp dn = dimensions[1]; + npy_intp dp = dimensions[2]; + npy_intp is1_m=steps[0], is1_n=steps[1], is2_n=steps[2], is2_p=steps[3], + os_m=steps[4], os_p=steps[5]; +#if @USEBLAS@ && defined(HAVE_CBLAS) + npy_intp sz = sizeof(@typ@); + npy_bool special_case = (dm == 1 || dn == 1 || dp == 1); + npy_bool any_zero_dim = (dm == 0 || dn == 0 || dp == 0); + npy_bool scalar_out = (dm == 1 && dp == 1); + npy_bool scalar_vec = (dn == 1 && (dp == 1 || dm == 1)); + npy_bool too_big_for_blas = (dm > BLAS_MAXSIZE || dn > BLAS_MAXSIZE || + dp > BLAS_MAXSIZE); + npy_bool i1_c_blasable = is_blasable2d(is1_m, is1_n, dm, dn, sz); + npy_bool i2_c_blasable = is_blasable2d(is2_n, is2_p, dn, dp, sz); + npy_bool i1_f_blasable = is_blasable2d(is1_n, is1_m, dn, dm, sz); + npy_bool i2_f_blasable = is_blasable2d(is2_p, is2_n, dp, dn, sz); + npy_bool i1blasable = i1_c_blasable || i1_f_blasable; + npy_bool i2blasable = i2_c_blasable || i2_f_blasable; + npy_bool o_c_blasable = is_blasable2d(os_m, os_p, dm, dp, sz); + npy_bool o_f_blasable = is_blasable2d(os_p, os_m, dp, dm, sz); + npy_bool vector_matrix = ((dm == 1) && i2blasable && + is_blasable2d(is1_n, sz, dn, 1, sz)); + npy_bool matrix_vector = ((dp == 1) && i1blasable && + is_blasable2d(is2_n, sz, dn, 1, sz)); +#endif + + for (iOuter = 0; iOuter < dOuter; iOuter++, + args[0] += s0, args[1] += s1, args[2] += s2) { + void *ip1=args[0], *ip2=args[1], *op=args[2]; +#if @USEBLAS@ && defined(HAVE_CBLAS) + /* + * TODO: refactor this out to a inner_loop_selector, in + * PyUFunc_MatmulLoopSelector. But that call does not have access to + * n, m, p and strides. + */ + if (too_big_for_blas || any_zero_dim) { + @TYPE@_matmul_inner_noblas(ip1, is1_m, is1_n, + ip2, is2_n, is2_p, + op, os_m, os_p, dm, dn, dp); + } + else if (special_case) { + /* Special case variants that have a 1 in the core dimensions */ + if (scalar_out) { + /* row @ column, 1,1 output */ + @TYPE@_dot(ip1, is1_n, ip2, is2_n, op, dn, NULL); + } else if (scalar_vec){ + /* + * 1,1d @ vector or vector @ 1,1d + * could use cblas_Xaxy, but that requires 0ing output + * and would not be faster (XXX prove it) + */ + @TYPE@_matmul_inner_noblas(ip1, is1_m, is1_n, + ip2, is2_n, is2_p, + op, os_m, os_p, dm, dn, dp); + } else if (vector_matrix) { + /* vector @ matrix, switch ip1, ip2, p and m */ + @TYPE@_gemv(ip2, is2_p, is2_n, ip1, is1_n, is1_m, + op, os_p, os_m, dp, dn, dm); + } else if (matrix_vector) { + /* matrix @ vector */ + @TYPE@_gemv(ip1, is1_m, is1_n, ip2, is2_n, is2_p, + + op, os_m, os_p, dm, dn, dp); + } else { + /* column @ row, 2d output, no blas needed or non-blas-able input */ + @TYPE@_matmul_inner_noblas(ip1, is1_m, is1_n, + ip2, is2_n, is2_p, + op, os_m, os_p, dm, dn, dp); + } + } else { + /* matrix @ matrix */ + if (i1blasable && i2blasable && o_c_blasable) { + @TYPE@_matmul_matrixmatrix(ip1, is1_m, is1_n, + ip2, is2_n, is2_p, + op, os_m, os_p, + dm, dn, dp); + } else if (i1blasable && i2blasable && o_f_blasable) { + /* + * Use transpose equivalence: + * matmul(a, b, o) == matmul(b.T, a.T, o.T) + */ + @TYPE@_matmul_matrixmatrix(ip2, is2_p, is2_n, + ip1, is1_n, is1_m, + op, os_p, os_m, + dp, dn, dm); + } else { + /* + * If parameters are castable to int and we copy the + * non-blasable (or non-ccontiguous output) + * we could still use BLAS, see gh-12365. + */ + @TYPE@_matmul_inner_noblas(ip1, is1_m, is1_n, + ip2, is2_n, is2_p, + op, os_m, os_p, dm, dn, dp); + } + } +#else + @TYPE@_matmul_inner_noblas(ip1, is1_m, is1_n, + ip2, is2_n, is2_p, + op, os_m, os_p, dm, dn, dp); + +#endif + } +} + +/**end repeat**/ diff --git a/numpy/core/src/umath/matmul.h.src b/numpy/core/src/umath/matmul.h.src new file mode 100644 index 000000000000..18940e2f2c5f --- /dev/null +++ b/numpy/core/src/umath/matmul.h.src @@ -0,0 +1,12 @@ +/**begin repeat + * #TYPE = FLOAT, DOUBLE, LONGDOUBLE, HALF, + * CFLOAT, CDOUBLE, CLONGDOUBLE, + * UBYTE, USHORT, UINT, ULONG, ULONGLONG, + * BYTE, SHORT, INT, LONG, LONGLONG, + * BOOL, OBJECT# + **/ +NPY_NO_EXPORT void +@TYPE@_matmul(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); +/**end repeat**/ + + diff --git a/numpy/core/src/umath/npy_simd_data.h b/numpy/core/src/umath/npy_simd_data.h new file mode 100644 index 000000000000..62438d7a3fa8 --- /dev/null +++ b/numpy/core/src/umath/npy_simd_data.h @@ -0,0 +1,275 @@ +#ifndef __NPY_SIMD_DATA_H_ +#define __NPY_SIMD_DATA_H_ +#if defined NPY_HAVE_AVX512F +#if !(defined(__clang__) && (__clang_major__ < 10 || (__clang_major__ == 10 && __clang_minor__ < 1))) +/* + * Constants used in vector implementation of float64 exp(x) + */ +#define NPY_RINT_CVT_MAGIC 0x1.8p52 +#define NPY_INV_LN2_MUL_32 0x1.71547652b82fep+5 +#define NPY_TANG_NEG_L1 -0x1.62e42fefp-6 +#define NPY_TANG_NEG_L2 -0x1.473de6af278edp-39 +#define NPY_TANG_A1 0x1p-1 +#define NPY_TANG_A2 0x1.5555555548f7cp-3 +#define NPY_TANG_A3 0x1.5555555545d4ep-5 +#define NPY_TANG_A4 0x1.11115b7aa905ep-7 +#define NPY_TANG_A5 0x1.6c1728d739765p-10 + +/* Lookup table for 2^(j/32) */ +static npy_uint64 EXP_Table_top[32] = { + 0x3FF0000000000000, + 0x3FF059B0D3158540, + 0x3FF0B5586CF98900, + 0x3FF11301D0125B40, + 0x3FF172B83C7D5140, + 0x3FF1D4873168B980, + 0x3FF2387A6E756200, + 0x3FF29E9DF51FDEC0, + 0x3FF306FE0A31B700, + 0x3FF371A7373AA9C0, + 0x3FF3DEA64C123400, + 0x3FF44E0860618900, + 0x3FF4BFDAD5362A00, + 0x3FF5342B569D4F80, + 0x3FF5AB07DD485400, + 0x3FF6247EB03A5580, + 0x3FF6A09E667F3BC0, + 0x3FF71F75E8EC5F40, + 0x3FF7A11473EB0180, + 0x3FF82589994CCE00, + 0x3FF8ACE5422AA0C0, + 0x3FF93737B0CDC5C0, + 0x3FF9C49182A3F080, + 0x3FFA5503B23E2540, + 0x3FFAE89F995AD380, + 0x3FFB7F76F2FB5E40, + 0x3FFC199BDD855280, + 0x3FFCB720DCEF9040, + 0x3FFD5818DCFBA480, + 0x3FFDFC97337B9B40, + 0x3FFEA4AFA2A490C0, + 0x3FFF50765B6E4540, +}; + +static npy_uint64 EXP_Table_tail[32] = { + 0x0000000000000000, + 0x3D0A1D73E2A475B4, + 0x3CEEC5317256E308, + 0x3CF0A4EBBF1AED93, + 0x3D0D6E6FBE462876, + 0x3D053C02DC0144C8, + 0x3D0C3360FD6D8E0B, + 0x3D009612E8AFAD12, + 0x3CF52DE8D5A46306, + 0x3CE54E28AA05E8A9, + 0x3D011ADA0911F09F, + 0x3D068189B7A04EF8, + 0x3D038EA1CBD7F621, + 0x3CBDF0A83C49D86A, + 0x3D04AC64980A8C8F, + 0x3CD2C7C3E81BF4B7, + 0x3CE921165F626CDD, + 0x3D09EE91B8797785, + 0x3CDB5F54408FDB37, + 0x3CF28ACF88AFAB35, + 0x3CFB5BA7C55A192D, + 0x3D027A280E1F92A0, + 0x3CF01C7C46B071F3, + 0x3CFC8B424491CAF8, + 0x3D06AF439A68BB99, + 0x3CDBAA9EC206AD4F, + 0x3CFC2220CB12A092, + 0x3D048A81E5E8F4A5, + 0x3CDC976816BAD9B8, + 0x3CFEB968CAC39ED3, + 0x3CF9858F73A18F5E, + 0x3C99D3E12DD8A18B, +}; +#endif +#endif + +/* + * Constants used in vector implementation of exp(x) + */ +#define NPY_RINT_CVT_MAGICf 0x1.800000p+23f +#define NPY_CODY_WAITE_LOGE_2_HIGHf -6.93145752e-1f +#define NPY_CODY_WAITE_LOGE_2_LOWf -1.42860677e-6f +#define NPY_COEFF_P0_EXPf 9.999999999980870924916e-01f +#define NPY_COEFF_P1_EXPf 7.257664613233124478488e-01f +#define NPY_COEFF_P2_EXPf 2.473615434895520810817e-01f +#define NPY_COEFF_P3_EXPf 5.114512081637298353406e-02f +#define NPY_COEFF_P4_EXPf 6.757896990527504603057e-03f +#define NPY_COEFF_P5_EXPf 5.082762527590693718096e-04f +#define NPY_COEFF_Q0_EXPf 1.000000000000000000000e+00f +#define NPY_COEFF_Q1_EXPf -2.742335390411667452936e-01f +#define NPY_COEFF_Q2_EXPf 2.159509375685829852307e-02f + +/* + * Constants used in vector implementation of log(x) + */ +#define NPY_COEFF_P0_LOGf 0.000000000000000000000e+00f +#define NPY_COEFF_P1_LOGf 9.999999999999998702752e-01f +#define NPY_COEFF_P2_LOGf 2.112677543073053063722e+00f +#define NPY_COEFF_P3_LOGf 1.480000633576506585156e+00f +#define NPY_COEFF_P4_LOGf 3.808837741388407920751e-01f +#define NPY_COEFF_P5_LOGf 2.589979117907922693523e-02f +#define NPY_COEFF_Q0_LOGf 1.000000000000000000000e+00f +#define NPY_COEFF_Q1_LOGf 2.612677543073109236779e+00f +#define NPY_COEFF_Q2_LOGf 2.453006071784736363091e+00f +#define NPY_COEFF_Q3_LOGf 9.864942958519418960339e-01f +#define NPY_COEFF_Q4_LOGf 1.546476374983906719538e-01f +#define NPY_COEFF_Q5_LOGf 5.875095403124574342950e-03f + +/* + * Lookup table of log(c_k) + * Reference form: Tang, Ping-Tak Peter. "Table-driven implementation of the + * logarithm function in IEEE floating-point arithmetic." ACM Transactions + * on Mathematical Software (TOMS) 16.4 (1990): 378-400. + */ +#if defined NPY_HAVE_AVX512F +#if !(defined(__clang__) && (__clang_major__ < 10 || (__clang_major__ == 10 && __clang_minor__ < 1))) +static npy_uint64 LOG_TABLE_TOP[64] = { + 0x0000000000000000, + 0x3F8FC0A8B1000000, + 0x3F9F829B0E780000, + 0x3FA77458F6340000, + 0x3FAF0A30C0100000, + 0x3FB341D7961C0000, + 0x3FB6F0D28AE60000, + 0x3FBA926D3A4A0000, + 0x3FBE27076E2A0000, + 0x3FC0D77E7CD10000, + 0x3FC29552F8200000, + 0x3FC44D2B6CCB0000, + 0x3FC5FF3070A80000, + 0x3FC7AB8902110000, + 0x3FC9525A9CF40000, + 0x3FCAF3C94E810000, + 0x3FCC8FF7C79B0000, + 0x3FCE27076E2B0000, + 0x3FCFB9186D5E0000, + 0x3FD0A324E2738000, + 0x3FD1675CABAB8000, + 0x3FD22941FBCF8000, + 0x3FD2E8E2BAE10000, + 0x3FD3A64C55698000, + 0x3FD4618BC21C8000, + 0x3FD51AAD872E0000, + 0x3FD5D1BDBF580000, + 0x3FD686C81E9B0000, + 0x3FD739D7F6BC0000, + 0x3FD7EAF83B828000, + 0x3FD89A3386C18000, + 0x3FD947941C210000, + 0x3FD9F323ECBF8000, + 0x3FDA9CEC9A9A0000, + 0x3FDB44F77BCC8000, + 0x3FDBEB4D9DA70000, + 0x3FDC8FF7C79A8000, + 0x3FDD32FE7E010000, + 0x3FDDD46A04C20000, + 0x3FDE744261D68000, + 0x3FDF128F5FAF0000, + 0x3FDFAF588F790000, + 0x3FE02552A5A5C000, + 0x3FE0723E5C1CC000, + 0x3FE0BE72E4254000, + 0x3FE109F39E2D4000, + 0x3FE154C3D2F4C000, + 0x3FE19EE6B467C000, + 0x3FE1E85F5E704000, + 0x3FE23130D7BEC000, + 0x3FE2795E1289C000, + 0x3FE2C0E9ED448000, + 0x3FE307D7334F0000, + 0x3FE34E289D9D0000, + 0x3FE393E0D3564000, + 0x3FE3D9026A714000, + 0x3FE41D8FE8468000, + 0x3FE4618BC21C4000, + 0x3FE4A4F85DB04000, + 0x3FE4E7D811B74000, + 0x3FE52A2D265BC000, + 0x3FE56BF9D5B40000, + 0x3FE5AD404C358000, + 0x3FE5EE02A9240000, +}; + +static npy_uint64 LOG_TABLE_TAIL[64] = { + 0x0000000000000000, + 0xBD5FE0E183092C59, + 0x3D2980267C7E09E4, + 0xBD62303B9CB0D5E1, + 0x3D662A6617CC9717, + 0xBD4717B6B33E44F8, + 0xBD62968C836CC8C2, + 0x3D6AAC6CA17A4554, + 0x3D6E5CBD3D50FFFC, + 0xBD6C69A65A23A170, + 0xBD35B967F4471DFC, + 0x3D6F4799F4F6543E, + 0xBD6B0B0DE3077D7E, + 0xBD537B720E4A694B, + 0x3D65AD1D904C1D4E, + 0xBD600349CC67F9B2, + 0xBD697794F689F843, + 0xBD3A342C2AF0003C, + 0x3D5F1546AAA3361C, + 0x3D50E35F73F7A018, + 0x3D630701CE63EAB9, + 0xBD3A6976F5EB0963, + 0x3D5D309C2CC91A85, + 0xBD6D0B1C68651946, + 0xBD609EC17A426426, + 0xBD3F4BD8DB0A7CC1, + 0x3D4394A11B1C1EE4, + 0x3D54AEC442BE1015, + 0xBD67FCB18ED9D603, + 0x3D67E1B259D2F3DA, + 0xBD6ED2A52C73BF78, + 0x3D56FABA4CDD147D, + 0x3D584BF2B68D766F, + 0x3D40931A909FEA5E, + 0x3D4EC5197DDB55D3, + 0x3D5B7BF7861D37AC, + 0x3D5A21AC25DB1EF3, + 0xBD542A9E21373414, + 0xBD6DAFA08CECADB1, + 0x3D3E1F8DF68DBCF3, + 0x3D3BB2CD720EC44C, + 0xBD49C24CA098362B, + 0x3D60FEC69C695D7F, + 0x3D6F404E57963891, + 0xBD657D49676844CC, + 0x3D592DFBC7D93617, + 0x3D65E9A98F33A396, + 0x3D52DD98B97BAEF0, + 0x3D1A07BD8B34BE7C, + 0xBD17AFA4392F1BA7, + 0xBD5DCA290F818480, + 0x3D5D1772F5386374, + 0x3D60BE1FB590A1F5, + 0xBD6E2CE9146D271A, + 0xBD65E6563BBD9FC9, + 0x3D66FAA404263D0B, + 0xBD5AA33736867A17, + 0x3D6EC27D0B7B37B3, + 0xBD244FDD840B8591, + 0x3D6BB09CB0985646, + 0x3D46ABB9DF22BC57, + 0xBD58CD7DC73BD194, + 0x3D6F2CFB29AAA5F0, + 0x3D66757006095FD2, +}; + +#define NPY_TANG_LOG_A1 0x1.55555555554e6p-4 +#define NPY_TANG_LOG_A2 0x1.9999999bac6d4p-7 +#define NPY_TANG_LOG_A3 0x1.2492307f1519fp-9 +#define NPY_TANG_LOG_A4 0x1.c8034c85dfffp-12 + +#define NPY_TANG_LOG_LN2HI 0x1.62e42fefa4p-1 +#define NPY_TANG_LOG_LN2LO -0x1.8432a1b0e2634p-43 +#endif +#endif + +#endif diff --git a/numpy/core/src/umath/override.c b/numpy/core/src/umath/override.c index c0bc47b7b3cb..d247c263986f 100644 --- a/numpy/core/src/umath/override.c +++ b/numpy/core/src/umath/override.c @@ -5,324 +5,196 @@ #include "numpy/ufuncobject.h" #include "npy_import.h" -#include "ufunc_override.h" #include "override.h" +#include "ufunc_override.h" -/* - * The following functions normalize ufunc arguments. The work done is similar - * to what is done inside ufunc_object by get_ufunc_arguments for __call__ and - * generalized ufuncs, and by PyUFunc_GenericReduction for the other methods. - * It would be good to unify (see gh-8892). - */ /* - * ufunc() and ufunc.outer() accept 'sig' or 'signature'; - * normalize to 'signature' + * For each positional argument and each argument in a possible "out" + * keyword, look for overrides of the standard ufunc behaviour, i.e., + * non-default __array_ufunc__ methods. + * + * Returns the number of overrides, setting corresponding objects + * in PyObject array ``with_override`` and the corresponding + * __array_ufunc__ methods in ``methods`` (both using new references). + * + * Only the first override for a given class is returned. + * + * Returns -1 on failure. */ static int -normalize_signature_keyword(PyObject *normal_kwds) +get_array_ufunc_overrides(PyObject *in_args, PyObject *out_args, + PyObject **with_override, PyObject **methods) { - PyObject* obj = PyDict_GetItemString(normal_kwds, "sig"); - if (obj != NULL) { - if (PyDict_GetItemString(normal_kwds, "signature")) { - PyErr_SetString(PyExc_TypeError, - "cannot specify both 'sig' and 'signature'"); - return -1; - } - /* - * No INCREF or DECREF needed: got a borrowed reference above, - * and, unlike e.g. PyList_SetItem, PyDict_SetItem INCREF's it. - */ - PyDict_SetItemString(normal_kwds, "signature", obj); - PyDict_DelItemString(normal_kwds, "sig"); - } - return 0; -} + int i; + int num_override_args = 0; + int narg, nout; -static int -normalize___call___args(PyUFuncObject *ufunc, PyObject *args, - PyObject **normal_args, PyObject **normal_kwds) -{ - /* - * ufunc.__call__(*args, **kwds) - */ - npy_intp i; - int not_all_none; - npy_intp nin = ufunc->nin; - npy_intp nout = ufunc->nout; - npy_intp nargs = PyTuple_GET_SIZE(args); - npy_intp nkwds = PyDict_Size(*normal_kwds); - PyObject *obj; - - if (nargs < nin) { - PyErr_Format(PyExc_TypeError, - "ufunc() missing %"NPY_INTP_FMT" of %"NPY_INTP_FMT - "required positional argument(s)", nin - nargs, nin); - return -1; - } - if (nargs > nin+nout) { - PyErr_Format(PyExc_TypeError, - "ufunc() takes from %"NPY_INTP_FMT" to %"NPY_INTP_FMT - "arguments but %"NPY_INTP_FMT" were given", - nin, nin+nout, nargs); - return -1; - } + narg = (int)PyTuple_GET_SIZE(in_args); + /* It is valid for out_args to be NULL: */ + nout = (out_args != NULL) ? (int)PyTuple_GET_SIZE(out_args) : 0; - *normal_args = PyTuple_GetSlice(args, 0, nin); - if (*normal_args == NULL) { - return -1; - } + for (i = 0; i < narg + nout; ++i) { + PyObject *obj; + int j; + int new_class = 1; - /* If we have more args than nin, they must be the output variables.*/ - if (nargs > nin) { - if(nkwds > 0 && PyDict_GetItemString(*normal_kwds, "out")) { - PyErr_Format(PyExc_TypeError, - "argument given by name ('out') and position " - "(%"NPY_INTP_FMT")", nin); - return -1; + if (i < narg) { + obj = PyTuple_GET_ITEM(in_args, i); } - for (i = nin; i < nargs; i++) { - not_all_none = (PyTuple_GET_ITEM(args, i) != Py_None); - if (not_all_none) { + else { + obj = PyTuple_GET_ITEM(out_args, i - narg); + } + /* + * Have we seen this class before? If so, ignore. + */ + for (j = 0; j < num_override_args; j++) { + new_class = (Py_TYPE(obj) != Py_TYPE(with_override[j])); + if (!new_class) { break; } } - if (not_all_none) { - if (nargs - nin == nout) { - obj = PyTuple_GetSlice(args, nin, nargs); + if (new_class) { + /* + * Now see if the object provides an __array_ufunc__. However, we should + * ignore the base ndarray.__ufunc__, so we skip any ndarray as well as + * any ndarray subclass instances that did not override __array_ufunc__. + */ + PyObject *method = PyUFuncOverride_GetNonDefaultArrayUfunc(obj); + if (method == NULL) { + continue; } - else { - PyObject *item; - - obj = PyTuple_New(nout); - if (obj == NULL) { - return -1; - } - for (i = 0; i < nout; i++) { - if (i + nin < nargs) { - item = PyTuple_GET_ITEM(args, nin+i); - } - else { - item = Py_None; - } - Py_INCREF(item); - PyTuple_SET_ITEM(obj, i, item); - } + if (method == Py_None) { + PyErr_Format(PyExc_TypeError, + "operand '%.200s' does not support ufuncs " + "(__array_ufunc__=None)", + obj->ob_type->tp_name); + Py_DECREF(method); + goto fail; } - PyDict_SetItemString(*normal_kwds, "out", obj); - Py_DECREF(obj); + Py_INCREF(obj); + with_override[num_override_args] = obj; + methods[num_override_args] = method; + ++num_override_args; } } - /* gufuncs accept either 'axes' or 'axis', but not both */ - if (nkwds >= 2 && (PyDict_GetItemString(*normal_kwds, "axis") && - PyDict_GetItemString(*normal_kwds, "axes"))) { - PyErr_SetString(PyExc_TypeError, - "cannot specify both 'axis' and 'axes'"); - return -1; + return num_override_args; + +fail: + for (i = 0; i < num_override_args; i++) { + Py_DECREF(with_override[i]); + Py_DECREF(methods[i]); } - /* finally, ufuncs accept 'sig' or 'signature' normalize to 'signature' */ - return nkwds == 0 ? 0 : normalize_signature_keyword(*normal_kwds); + return -1; } + +/* + * Build a dictionary from the keyword arguments, but replace out with the + * normalized version (and always pass it even if it was passed by position). + */ static int -normalize_reduce_args(PyUFuncObject *ufunc, PyObject *args, - PyObject **normal_args, PyObject **normal_kwds) +initialize_normal_kwds(PyObject *out_args, + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames, + PyObject *normal_kwds) { - /* - * ufunc.reduce(a[, axis, dtype, out, keepdims]) - */ - npy_intp nargs = PyTuple_GET_SIZE(args); - npy_intp i; - PyObject *obj; - static PyObject *NoValue = NULL; - static char *kwlist[] = {"array", "axis", "dtype", "out", "keepdims", - "initial"}; - - npy_cache_import("numpy", "_NoValue", &NoValue); - if (NoValue == NULL) return -1; - - if (nargs < 1 || nargs > 6) { - PyErr_Format(PyExc_TypeError, - "ufunc.reduce() takes from 1 to 6 positional " - "arguments but %"NPY_INTP_FMT" were given", nargs); - return -1; + if (kwnames != NULL) { + for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(kwnames); i++) { + if (PyDict_SetItem(normal_kwds, + PyTuple_GET_ITEM(kwnames, i), args[i + len_args]) < 0) { + return -1; + } + } } - *normal_args = PyTuple_GetSlice(args, 0, 1); - if (*normal_args == NULL) { - return -1; + static PyObject *out_str = NULL; + if (out_str == NULL) { + out_str = PyUnicode_InternFromString("out"); + if (out_str == NULL) { + return -1; + } } - for (i = 1; i < nargs; i++) { - if (PyDict_GetItemString(*normal_kwds, kwlist[i])) { - PyErr_Format(PyExc_TypeError, - "argument given by name ('%s') and position " - "(%"NPY_INTP_FMT")", kwlist[i], i); + if (out_args != NULL) { + /* Replace `out` argument with the normalized version */ + int res = PyDict_SetItem(normal_kwds, out_str, out_args); + if (res < 0) { return -1; } - obj = PyTuple_GET_ITEM(args, i); - if (i == 3) { - /* remove out=None */ - if (obj == Py_None) { - continue; - } - obj = PyTuple_GetSlice(args, 3, 4); - } - /* Remove initial=np._NoValue */ - if (i == 5 && obj == NoValue) { - continue; + } + else { + /* Ensure that `out` is not present. */ + int res = PyDict_Contains(normal_kwds, out_str); + if (res < 0) { + return -1; } - PyDict_SetItemString(*normal_kwds, kwlist[i], obj); - if (i == 3) { - Py_DECREF(obj); + if (res) { + return PyDict_DelItem(normal_kwds, out_str); } } return 0; } +/* + * ufunc() and ufunc.outer() accept 'sig' or 'signature'. We guarantee + * that it is passed as 'signature' by renaming 'sig' if present. + * Note that we have already validated that only one of them was passed + * before checking for overrides. + */ static int -normalize_accumulate_args(PyUFuncObject *ufunc, PyObject *args, - PyObject **normal_args, PyObject **normal_kwds) +normalize_signature_keyword(PyObject *normal_kwds) { - /* - * ufunc.accumulate(a[, axis, dtype, out]) - */ - npy_intp nargs = PyTuple_GET_SIZE(args); - npy_intp i; - PyObject *obj; - static char *kwlist[] = {"array", "axis", "dtype", "out", "keepdims"}; - - if (nargs < 1 || nargs > 4) { - PyErr_Format(PyExc_TypeError, - "ufunc.accumulate() takes from 1 to 4 positional " - "arguments but %"NPY_INTP_FMT" were given", nargs); - return -1; - } - *normal_args = PyTuple_GetSlice(args, 0, 1); - if (*normal_args == NULL) { + /* If the keywords include `sig` rename to `signature`. */ + PyObject* obj = _PyDict_GetItemStringWithError(normal_kwds, "sig"); + if (obj == NULL && PyErr_Occurred()) { return -1; } - - for (i = 1; i < nargs; i++) { - if (PyDict_GetItemString(*normal_kwds, kwlist[i])) { - PyErr_Format(PyExc_TypeError, - "argument given by name ('%s') and position " - "(%"NPY_INTP_FMT")", kwlist[i], i); + if (obj != NULL) { + /* + * No INCREF or DECREF needed: got a borrowed reference above, + * and, unlike e.g. PyList_SetItem, PyDict_SetItem INCREF's it. + */ + if (PyDict_SetItemString(normal_kwds, "signature", obj) < 0) { return -1; } - obj = PyTuple_GET_ITEM(args, i); - if (i == 3) { - /* remove out=None */ - if (obj == Py_None) { - continue; - } - obj = PyTuple_GetSlice(args, 3, 4); - } - PyDict_SetItemString(*normal_kwds, kwlist[i], obj); - if (i == 3) { - Py_DECREF(obj); + if (PyDict_DelItemString(normal_kwds, "sig") < 0) { + return -1; } } return 0; } + static int -normalize_reduceat_args(PyUFuncObject *ufunc, PyObject *args, - PyObject **normal_args, PyObject **normal_kwds) +copy_positional_args_to_kwargs(const char **keywords, + PyObject *const *args, Py_ssize_t len_args, + PyObject *normal_kwds) { - /* - * ufunc.reduceat(a, indices[, axis, dtype, out]) - * the number of arguments has been checked in PyUFunc_GenericReduction. - */ - npy_intp i; - npy_intp nargs = PyTuple_GET_SIZE(args); - PyObject *obj; - static char *kwlist[] = {"array", "indices", "axis", "dtype", "out"}; - - if (nargs < 2 || nargs > 5) { - PyErr_Format(PyExc_TypeError, - "ufunc.reduceat() takes from 2 to 4 positional " - "arguments but %"NPY_INTP_FMT" were given", nargs); - return -1; - } - /* a and indices */ - *normal_args = PyTuple_GetSlice(args, 0, 2); - if (*normal_args == NULL) { - return -1; - } - - for (i = 2; i < nargs; i++) { - if (PyDict_GetItemString(*normal_kwds, kwlist[i])) { - PyErr_Format(PyExc_TypeError, - "argument given by name ('%s') and position " - "(%"NPY_INTP_FMT")", kwlist[i], i); - return -1; + for (Py_ssize_t i = 0; i < len_args; i++) { + if (keywords[i] == NULL) { + /* keyword argument is either input or output and not set here */ + continue; } - obj = PyTuple_GET_ITEM(args, i); - if (i == 4) { - /* remove out=None */ - if (obj == Py_None) { + if (NPY_UNLIKELY(i == 5)) { + /* + * This is only relevant for reduce, which is the only one with + * 5 keyword arguments. + */ + static PyObject *NoValue = NULL; + assert(strcmp(keywords[i], "initial") == 0); + npy_cache_import("numpy", "_NoValue", &NoValue); + if (args[i] == NoValue) { continue; } - obj = PyTuple_GetSlice(args, 4, 5); } - PyDict_SetItemString(*normal_kwds, kwlist[i], obj); - if (i == 4) { - Py_DECREF(obj); + + int res = PyDict_SetItemString(normal_kwds, keywords[i], args[i]); + if (res < 0) { + return -1; } } return 0; } -static int -normalize_outer_args(PyUFuncObject *ufunc, PyObject *args, - PyObject **normal_args, PyObject **normal_kwds) -{ - /* - * ufunc.outer(*args, **kwds) - * all positional arguments should be inputs. - * for the keywords, we only need to check 'sig' vs 'signature'. - */ - npy_intp nin = ufunc->nin; - npy_intp nargs = PyTuple_GET_SIZE(args); - - if (nargs < nin) { - PyErr_Format(PyExc_TypeError, - "ufunc.outer() missing %"NPY_INTP_FMT" of %"NPY_INTP_FMT - "required positional " "argument(s)", nin - nargs, nin); - return -1; - } - if (nargs > nin) { - PyErr_Format(PyExc_TypeError, - "ufunc.outer() takes %"NPY_INTP_FMT" arguments but" - "%"NPY_INTP_FMT" were given", nin, nargs); - return -1; - } - - *normal_args = PyTuple_GetSlice(args, 0, nin); - if (*normal_args == NULL) { - return -1; - } - /* ufuncs accept 'sig' or 'signature' normalize to 'signature' */ - return normalize_signature_keyword(*normal_kwds); -} - -static int -normalize_at_args(PyUFuncObject *ufunc, PyObject *args, - PyObject **normal_args, PyObject **normal_kwds) -{ - /* ufunc.at(a, indices[, b]) */ - npy_intp nargs = PyTuple_GET_SIZE(args); - - if (nargs < 2 || nargs > 3) { - PyErr_Format(PyExc_TypeError, - "ufunc.at() takes from 2 to 3 positional " - "arguments but %"NPY_INTP_FMT" were given", nargs); - return -1; - } - *normal_args = PyTuple_GetSlice(args, 0, nargs); - return (*normal_args == NULL); -} - /* * Check a set of args for the `__array_ufunc__` method. If more than one of * the input arguments implements `__array_ufunc__`, they are tried in the @@ -336,33 +208,26 @@ normalize_at_args(PyUFuncObject *ufunc, PyObject *args, */ NPY_NO_EXPORT int PyUFunc_CheckOverride(PyUFuncObject *ufunc, char *method, - PyObject *args, PyObject *kwds, - PyObject **result) + PyObject *in_args, PyObject *out_args, + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames, + PyObject **result) { - int i; - int j; int status; int num_override_args; PyObject *with_override[NPY_MAXARGS]; PyObject *array_ufunc_methods[NPY_MAXARGS]; - PyObject *obj; - PyObject *other_obj; - PyObject *out; - PyObject *method_name = NULL; - PyObject *normal_args = NULL; /* normal_* holds normalized arguments. */ PyObject *normal_kwds = NULL; PyObject *override_args = NULL; - Py_ssize_t len; /* * Check inputs for overrides */ - num_override_args = PyUFunc_WithOverride( - args, kwds, with_override, array_ufunc_methods); + num_override_args = get_array_ufunc_overrides( + in_args, out_args, with_override, array_ufunc_methods); if (num_override_args == -1) { goto fail; } @@ -373,115 +238,58 @@ PyUFunc_CheckOverride(PyUFuncObject *ufunc, char *method, } /* - * Normalize ufunc arguments. + * Normalize ufunc arguments, note that any input and output arguments + * have already been stored in `in_args` and `out_args`. */ - - /* Build new kwds */ - if (kwds && PyDict_CheckExact(kwds)) { - - /* ensure out is always a tuple */ - normal_kwds = PyDict_Copy(kwds); - out = PyDict_GetItemString(normal_kwds, "out"); - if (out != NULL) { - int nout = ufunc->nout; - - if (PyTuple_CheckExact(out)) { - int all_none = 1; - - if (PyTuple_GET_SIZE(out) != nout) { - PyErr_Format(PyExc_ValueError, - "The 'out' tuple must have exactly " - "%d entries: one per ufunc output", nout); - goto fail; - } - for (i = 0; i < PyTuple_GET_SIZE(out); i++) { - all_none = (PyTuple_GET_ITEM(out, i) == Py_None); - if (!all_none) { - break; - } - } - if (all_none) { - PyDict_DelItemString(normal_kwds, "out"); - } - } - else { - /* not a tuple */ - if (nout > 1 && DEPRECATE("passing a single argument to the " - "'out' keyword argument of a " - "ufunc with\n" - "more than one output will " - "result in an error in the " - "future") < 0) { - /* - * If the deprecation is removed, also remove the loop - * below setting tuple items to None (but keep this future - * error message.) - */ - PyErr_SetString(PyExc_TypeError, - "'out' must be a tuple of arguments"); - goto fail; - } - if (out != Py_None) { - /* not already a tuple and not None */ - PyObject *out_tuple = PyTuple_New(nout); - - if (out_tuple == NULL) { - goto fail; - } - for (i = 1; i < nout; i++) { - Py_INCREF(Py_None); - PyTuple_SET_ITEM(out_tuple, i, Py_None); - } - /* out was borrowed ref; make it permanent */ - Py_INCREF(out); - /* steals reference */ - PyTuple_SET_ITEM(out_tuple, 0, out); - PyDict_SetItemString(normal_kwds, "out", out_tuple); - Py_DECREF(out_tuple); - } - else { - /* out=None; remove it */ - PyDict_DelItemString(normal_kwds, "out"); - } - } - } - } - else { - normal_kwds = PyDict_New(); - } + normal_kwds = PyDict_New(); if (normal_kwds == NULL) { goto fail; } + if (initialize_normal_kwds(out_args, + args, len_args, kwnames, normal_kwds) < 0) { + goto fail; + } - /* decide what to do based on the method. */ + /* + * Reduce-like methods can pass keyword arguments also by position, + * in which case the additional positional arguments have to be copied + * into the keyword argument dictionary. The `__call__` and `__outer__` + * method have to normalize sig and signature. + */ /* ufunc.__call__ */ if (strcmp(method, "__call__") == 0) { - status = normalize___call___args(ufunc, args, &normal_args, - &normal_kwds); + status = normalize_signature_keyword(normal_kwds); } /* ufunc.reduce */ else if (strcmp(method, "reduce") == 0) { - status = normalize_reduce_args(ufunc, args, &normal_args, - &normal_kwds); + static const char *keywords[] = { + NULL, "axis", "dtype", NULL, "keepdims", + "initial", "where"}; + status = copy_positional_args_to_kwargs(keywords, + args, len_args, normal_kwds); } /* ufunc.accumulate */ else if (strcmp(method, "accumulate") == 0) { - status = normalize_accumulate_args(ufunc, args, &normal_args, - &normal_kwds); + static const char *keywords[] = { + NULL, "axis", "dtype", NULL}; + status = copy_positional_args_to_kwargs(keywords, + args, len_args, normal_kwds); } /* ufunc.reduceat */ else if (strcmp(method, "reduceat") == 0) { - status = normalize_reduceat_args(ufunc, args, &normal_args, - &normal_kwds); + static const char *keywords[] = { + NULL, NULL, "axis", "dtype", NULL}; + status = copy_positional_args_to_kwargs(keywords, + args, len_args, normal_kwds); } - /* ufunc.outer */ + /* ufunc.outer (identical to call) */ else if (strcmp(method, "outer") == 0) { - status = normalize_outer_args(ufunc, args, &normal_args, &normal_kwds); + status = normalize_signature_keyword(normal_kwds); } /* ufunc.at */ else if (strcmp(method, "at") == 0) { - status = normalize_at_args(ufunc, args, &normal_args, &normal_kwds); + status = 0; } /* unknown method */ else { @@ -494,12 +302,12 @@ PyUFunc_CheckOverride(PyUFuncObject *ufunc, char *method, goto fail; } - method_name = PyUString_FromString(method); + method_name = PyUnicode_FromString(method); if (method_name == NULL) { goto fail; } - len = PyTuple_GET_SIZE(normal_args); + int len = (int)PyTuple_GET_SIZE(in_args); /* Call __array_ufunc__ functions in correct order */ while (1) { @@ -510,22 +318,19 @@ PyUFunc_CheckOverride(PyUFuncObject *ufunc, char *method, *result = NULL; /* Choose an overriding argument */ - for (i = 0; i < num_override_args; i++) { - obj = with_override[i]; - if (obj == NULL) { + for (int i = 0; i < num_override_args; i++) { + override_obj = with_override[i]; + if (override_obj == NULL) { continue; } - /* Get the first instance of an overriding arg.*/ - override_obj = obj; - /* Check for sub-types to the right of obj. */ - for (j = i + 1; j < num_override_args; j++) { - other_obj = with_override[j]; + for (int j = i + 1; j < num_override_args; j++) { + PyObject *other_obj = with_override[j]; if (other_obj != NULL && - PyObject_Type(other_obj) != PyObject_Type(obj) && + Py_TYPE(other_obj) != Py_TYPE(override_obj) && PyObject_IsInstance(other_obj, - PyObject_Type(override_obj))) { + (PyObject *)Py_TYPE(override_obj))) { override_obj = NULL; break; } @@ -554,8 +359,8 @@ PyUFunc_CheckOverride(PyUFuncObject *ufunc, char *method, PyTuple_SET_ITEM(override_args, 1, (PyObject *)ufunc); Py_INCREF(method_name); PyTuple_SET_ITEM(override_args, 2, method_name); - for (i = 0; i < len; i++) { - PyObject *item = PyTuple_GET_ITEM(normal_args, i); + for (int i = 0; i < len; i++) { + PyObject *item = PyTuple_GET_ITEM(in_args, i); Py_INCREF(item); PyTuple_SET_ITEM(override_args, i + 3, item); @@ -616,11 +421,10 @@ PyUFunc_CheckOverride(PyUFuncObject *ufunc, char *method, fail: status = -1; cleanup: - for (i = 0; i < num_override_args; i++) { + for (int i = 0; i < num_override_args; i++) { Py_XDECREF(with_override[i]); Py_XDECREF(array_ufunc_methods[i]); } - Py_XDECREF(normal_args); Py_XDECREF(method_name); Py_XDECREF(normal_kwds); return status; diff --git a/numpy/core/src/umath/override.h b/numpy/core/src/umath/override.h index 68f3c6ef0814..4e9a323ca629 100644 --- a/numpy/core/src/umath/override.h +++ b/numpy/core/src/umath/override.h @@ -6,6 +6,9 @@ NPY_NO_EXPORT int PyUFunc_CheckOverride(PyUFuncObject *ufunc, char *method, - PyObject *args, PyObject *kwds, - PyObject **result); + PyObject *in_args, PyObject *out_args, + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames, + PyObject **result); + + #endif diff --git a/numpy/core/src/umath/reduction.c b/numpy/core/src/umath/reduction.c index 8136d7b3f656..8cb44d4338a7 100644 --- a/numpy/core/src/umath/reduction.c +++ b/numpy/core/src/umath/reduction.c @@ -6,253 +6,30 @@ * * See LICENSE.txt for the license. */ -#define _UMATHMODULE #define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE +#define _UMATHMODULE #define PY_SSIZE_T_CLEAN #include <Python.h> #include "npy_config.h" -#define PY_ARRAY_UNIQUE_SYMBOL _npy_umathmodule_ARRAY_API -#define NO_IMPORT_ARRAY - -#include <numpy/arrayobject.h> +#include "numpy/arrayobject.h" -#include "npy_config.h" #include "npy_pycompat.h" +#include "ctors.h" #include "numpy/ufuncobject.h" #include "lowlevel_strided_loops.h" #include "reduction.h" #include "extobj.h" /* for _check_ufunc_fperr */ -/* - * Allocates a result array for a reduction operation, with - * dimensions matching 'arr' except set to 1 with 0 stride - * wherever axis_flags is True. Dropping the reduction axes - * from the result must be done later by the caller once the - * computation is complete. - * - * This function always allocates a base class ndarray. - * - * If 'dtype' isn't NULL, this function steals its reference. - */ -static PyArrayObject * -allocate_reduce_result(PyArrayObject *arr, npy_bool *axis_flags, - PyArray_Descr *dtype, int subok) -{ - npy_intp strides[NPY_MAXDIMS], stride; - npy_intp shape[NPY_MAXDIMS], *arr_shape = PyArray_DIMS(arr); - npy_stride_sort_item strideperm[NPY_MAXDIMS]; - int idim, ndim = PyArray_NDIM(arr); - - if (dtype == NULL) { - dtype = PyArray_DTYPE(arr); - Py_INCREF(dtype); - } - - PyArray_CreateSortedStridePerm(PyArray_NDIM(arr), - PyArray_STRIDES(arr), strideperm); - - /* Build the new strides and shape */ - stride = dtype->elsize; - memcpy(shape, arr_shape, ndim * sizeof(shape[0])); - for (idim = ndim-1; idim >= 0; --idim) { - npy_intp i_perm = strideperm[idim].perm; - if (axis_flags[i_perm]) { - strides[i_perm] = 0; - shape[i_perm] = 1; - } - else { - strides[i_perm] = stride; - stride *= shape[i_perm]; - } - } - - /* Finally, allocate the array */ - return (PyArrayObject *)PyArray_NewFromDescr( - subok ? Py_TYPE(arr) : &PyArray_Type, - dtype, ndim, shape, strides, - NULL, 0, subok ? (PyObject *)arr : NULL); -} - -/* - * Conforms an output parameter 'out' to have 'ndim' dimensions - * with dimensions of size one added in the appropriate places - * indicated by 'axis_flags'. - * - * The return value is a view into 'out'. - */ -static PyArrayObject * -conform_reduce_result(int ndim, npy_bool *axis_flags, - PyArrayObject *out, int keepdims, const char *funcname, - int need_copy) -{ - npy_intp strides[NPY_MAXDIMS], shape[NPY_MAXDIMS]; - npy_intp *strides_out = PyArray_STRIDES(out); - npy_intp *shape_out = PyArray_DIMS(out); - int idim, idim_out, ndim_out = PyArray_NDIM(out); - PyArray_Descr *dtype; - PyArrayObject_fields *ret; - - /* - * If the 'keepdims' parameter is true, do a simpler validation and - * return a new reference to 'out'. - */ - if (keepdims) { - if (PyArray_NDIM(out) != ndim) { - PyErr_Format(PyExc_ValueError, - "output parameter for reduction operation %s " - "has the wrong number of dimensions (must match " - "the operand's when keepdims=True)", funcname); - return NULL; - } - - for (idim = 0; idim < ndim; ++idim) { - if (axis_flags[idim]) { - if (shape_out[idim] != 1) { - PyErr_Format(PyExc_ValueError, - "output parameter for reduction operation %s " - "has a reduction dimension not equal to one " - "(required when keepdims=True)", funcname); - return NULL; - } - } - } - - Py_INCREF(out); - return out; - } - - /* Construct the strides and shape */ - idim_out = 0; - for (idim = 0; idim < ndim; ++idim) { - if (axis_flags[idim]) { - strides[idim] = 0; - shape[idim] = 1; - } - else { - if (idim_out >= ndim_out) { - PyErr_Format(PyExc_ValueError, - "output parameter for reduction operation %s " - "does not have enough dimensions", funcname); - return NULL; - } - strides[idim] = strides_out[idim_out]; - shape[idim] = shape_out[idim_out]; - ++idim_out; - } - } - - if (idim_out != ndim_out) { - PyErr_Format(PyExc_ValueError, - "output parameter for reduction operation %s " - "has too many dimensions", funcname); - return NULL; - } - - /* Allocate the view */ - dtype = PyArray_DESCR(out); - Py_INCREF(dtype); - - /* TODO: use PyArray_NewFromDescrAndBase here once multiarray and umath - * are merged - */ - ret = (PyArrayObject_fields *)PyArray_NewFromDescr( - &PyArray_Type, dtype, - ndim, shape, strides, PyArray_DATA(out), - PyArray_FLAGS(out), NULL); - if (ret == NULL) { - return NULL; - } - - Py_INCREF(out); - if (PyArray_SetBaseObject((PyArrayObject *)ret, (PyObject *)out) < 0) { - Py_DECREF(ret); - return NULL; - } - - if (need_copy) { - PyArrayObject *ret_copy; - - ret_copy = (PyArrayObject *)PyArray_NewLikeArray( - (PyArrayObject *)ret, NPY_ANYORDER, NULL, 0); - if (ret_copy == NULL) { - Py_DECREF(ret); - return NULL; - } - - if (PyArray_CopyInto(ret_copy, (PyArrayObject *)ret) != 0) { - Py_DECREF(ret); - Py_DECREF(ret_copy); - return NULL; - } - - Py_INCREF(ret); - if (PyArray_SetWritebackIfCopyBase(ret_copy, (PyArrayObject *)ret) < 0) { - Py_DECREF(ret); - Py_DECREF(ret_copy); - return NULL; - } - - return ret_copy; - } - else { - return (PyArrayObject *)ret; - } -} - -/* - * Creates a result for reducing 'operand' along the axes specified - * in 'axis_flags'. If 'dtype' isn't NULL, this function steals a - * reference to 'dtype'. - * - * If 'out' isn't NULL, this function creates a view conforming - * to the number of dimensions of 'operand', adding a singleton dimension - * for each reduction axis specified. In this case, 'dtype' is ignored - * (but its reference is still stolen), and the caller must handle any - * type conversion/validity check for 'out' - * - * If 'subok' is true, creates a result with the subtype of 'operand', - * otherwise creates on with the base ndarray class. - * - * If 'out' is NULL, it allocates a new array whose shape matches that of - * 'operand', except for at the reduction axes. If 'dtype' is NULL, the dtype - * of 'operand' is used for the result. - */ -NPY_NO_EXPORT PyArrayObject * -PyArray_CreateReduceResult(PyArrayObject *operand, PyArrayObject *out, - PyArray_Descr *dtype, npy_bool *axis_flags, - int keepdims, int subok, - const char *funcname) -{ - PyArrayObject *result; - - if (out == NULL) { - /* This function steals the reference to 'dtype' */ - result = allocate_reduce_result(operand, axis_flags, dtype, subok); - } - else { - int need_copy = 0; - - if (solve_may_share_memory(operand, out, 1) != 0) { - need_copy = 1; - } - - /* Steal the dtype reference */ - Py_XDECREF(dtype); - result = conform_reduce_result(PyArray_NDIM(operand), axis_flags, - out, keepdims, funcname, need_copy); - } - - return result; -} /* * Count the number of dimensions selected in 'axis_flags' */ static int -count_axes(int ndim, npy_bool *axis_flags) +count_axes(int ndim, const npy_bool *axis_flags) { int idim; int naxes = 0; @@ -268,18 +45,15 @@ count_axes(int ndim, npy_bool *axis_flags) /* * This function initializes a result array for a reduction operation * which has no identity. This means it needs to copy the first element - * it sees along the reduction axes to result, then return a view of - * the operand which excludes that element. + * it sees along the reduction axes to result. * * If a reduction has an identity, such as 0 or 1, the result should be - * initialized by calling PyArray_AssignZero(result, NULL, NULL) or - * PyArray_AssignOne(result, NULL, NULL), because this function raises an - * exception when there are no elements to reduce (which appropriate iff the - * reduction operation has no identity). + * fully initialized to the identity, because this function raises an + * exception when there are no elements to reduce (which is appropriate if, + * and only if, the reduction operation has no identity). * * This means it copies the subarray indexed at zero along each reduction axis - * into 'result', then returns a view into 'operand' excluding those copied - * elements. + * into 'result'. * * result : The array into which the result is computed. This must have * the same number of dimensions as 'operand', but for each @@ -287,103 +61,83 @@ count_axes(int ndim, npy_bool *axis_flags) * operand : The array being reduced. * axis_flags : An array of boolean flags, one for each axis of 'operand'. * When a flag is True, it indicates to reduce along that axis. - * out_skip_first_count : This gets populated with the number of first-visit - * elements that should be skipped during the - * iteration loop. * funcname : The name of the reduction operation, for the purpose of * better quality error messages. For example, "numpy.max" * would be a good name for NumPy's max function. * - * Returns a view which contains the remaining elements on which to do - * the reduction. + * Returns -1 if an error occurred, and otherwise the reduce arrays size, + * which is the number of elements already initialized. */ -NPY_NO_EXPORT PyArrayObject * -PyArray_InitializeReduceResult( +NPY_NO_EXPORT int +PyArray_CopyInitialReduceValues( PyArrayObject *result, PyArrayObject *operand, - npy_bool *axis_flags, - npy_intp *out_skip_first_count, const char *funcname) + const npy_bool *axis_flags, const char *funcname, + int keepdims) { - npy_intp *strides, *shape, shape_orig[NPY_MAXDIMS]; + npy_intp shape[NPY_MAXDIMS], strides[NPY_MAXDIMS]; + npy_intp *shape_orig = PyArray_SHAPE(operand); + npy_intp *strides_orig = PyArray_STRIDES(operand); PyArrayObject *op_view = NULL; - int idim, ndim, nreduce_axes; - - ndim = PyArray_NDIM(operand); - /* Default to no skipping first-visit elements in the iteration */ - *out_skip_first_count = 0; - - /* Take a view into 'operand' which we can modify. */ - op_view = (PyArrayObject *)PyArray_View(operand, NULL, &PyArray_Type); - if (op_view == NULL) { - return NULL; - } + int ndim = PyArray_NDIM(operand); /* - * Now copy the subarray of the first element along each reduction axis, - * then return a view to the rest. + * Copy the subarray of the first element along each reduction axis. * * Adjust the shape to only look at the first element along - * any of the reduction axes. We count the number of reduction axes - * at the same time. + * any of the reduction axes. If keepdims is False remove the axes + * entirely. */ - shape = PyArray_SHAPE(op_view); - nreduce_axes = 0; - memcpy(shape_orig, shape, ndim * sizeof(npy_intp)); - for (idim = 0; idim < ndim; ++idim) { + int idim_out = 0; + npy_intp size = 1; + for (int idim = 0; idim < ndim; idim++) { if (axis_flags[idim]) { - if (shape[idim] == 0) { + if (NPY_UNLIKELY(shape_orig[idim] == 0)) { PyErr_Format(PyExc_ValueError, - "zero-size array to reduction operation %s " - "which has no identity", - funcname); - Py_DECREF(op_view); - return NULL; + "zero-size array to reduction operation %s " + "which has no identity", funcname); + return -1; + } + if (keepdims) { + shape[idim_out] = 1; + strides[idim_out] = 0; + idim_out++; } - shape[idim] = 1; - ++nreduce_axes; + } + else { + size *= shape_orig[idim]; + shape[idim_out] = shape_orig[idim]; + strides[idim_out] = strides_orig[idim]; + idim_out++; } } - /* - * Copy the elements into the result to start. - */ - if (PyArray_CopyInto(result, op_view) < 0) { - Py_DECREF(op_view); - return NULL; + PyArray_Descr *descr = PyArray_DESCR(operand); + Py_INCREF(descr); + op_view = (PyArrayObject *)PyArray_NewFromDescr( + &PyArray_Type, descr, idim_out, shape, strides, + PyArray_DATA(operand), 0, NULL); + if (op_view == NULL) { + return -1; } /* - * If there is one reduction axis, adjust the view's - * shape to only look at the remaining elements + * Copy the elements into the result to start. */ - if (nreduce_axes == 1) { - strides = PyArray_STRIDES(op_view); - for (idim = 0; idim < ndim; ++idim) { - if (axis_flags[idim]) { - shape[idim] = shape_orig[idim] - 1; - ((PyArrayObject_fields *)op_view)->data += strides[idim]; - } - } - } - /* If there are zero reduction axes, make the view empty */ - else if (nreduce_axes == 0) { - for (idim = 0; idim < ndim; ++idim) { - shape[idim] = 0; - } + int res = PyArray_CopyInto(result, op_view); + Py_DECREF(op_view); + if (res < 0) { + return -1; } + /* - * Otherwise iterate over the whole operand, but tell the inner loop - * to skip the elements we already copied by setting the skip_first_count. + * If there were no reduction axes, we would already be done here. + * Note that if there is only a single reduction axis, in principle the + * iteration could be set up more efficiently here by removing that + * axis before setting up the iterator (simplifying the iteration since + * `skip_first_count` (the returned size) can be set to 0). */ - else { - *out_skip_first_count = PyArray_SIZE(result); - - Py_DECREF(op_view); - Py_INCREF(operand); - op_view = operand; - } - - return op_view; + return size; } /* @@ -391,14 +145,12 @@ PyArray_InitializeReduceResult( * boilerplate code, just calling the appropriate inner loop function where * necessary. * + * context : The ArrayMethod context (with ufunc, method, and descriptors). * operand : The array to be reduced. * out : NULL, or the array into which to place the result. * wheremask : NOT YET SUPPORTED, but this parameter is placed here * so that support can be added in the future without breaking * API compatibility. Pass in NULL. - * operand_dtype : The dtype the inner loop expects for the operand. - * result_dtype : The dtype the inner loop expects for the result. - * casting : The casting rule to apply to the operands. * axis_flags : Flags indicating the reduction axes of 'operand'. * reorderable : If True, the reduction being done is reorderable, which * means specifying multiple axes of reduction at once is ok, @@ -409,10 +161,10 @@ PyArray_InitializeReduceResult( * with size one. * subok : If true, the result uses the subclass of operand, otherwise * it is always a base class ndarray. - * identity : If Py_None, PyArray_InitializeReduceResult is used, otherwise + * identity : If Py_None, PyArray_CopyInitialReduceValues is used, otherwise * this value is used to initialize the result to * the reduction's unit. - * loop : The loop which does the reduction. + * loop : `reduce_loop` from `ufunc_object.c`. TODO: Refactor * data : Data which is passed to the inner loop. * buffersize : Buffer size for the iterator. For the default, pass in 0. * funcname : The name of the reduction function, for error messages. @@ -428,27 +180,23 @@ PyArray_InitializeReduceResult( * generalized ufuncs!) */ NPY_NO_EXPORT PyArrayObject * -PyUFunc_ReduceWrapper(PyArrayObject *operand, PyArrayObject *out, - PyArrayObject *wheremask, - PyArray_Descr *operand_dtype, - PyArray_Descr *result_dtype, - NPY_CASTING casting, - npy_bool *axis_flags, int reorderable, - int keepdims, - int subok, - PyObject *identity, - PyArray_ReduceLoopFunc *loop, - void *data, npy_intp buffersize, const char *funcname, - int errormask) +PyUFunc_ReduceWrapper(PyArrayMethod_Context *context, + PyArrayObject *operand, PyArrayObject *out, PyArrayObject *wheremask, + npy_bool *axis_flags, int reorderable, int keepdims, + PyObject *identity, PyArray_ReduceLoopFunc *loop, + void *data, npy_intp buffersize, const char *funcname, int errormask) { - PyArrayObject *result = NULL, *op_view = NULL; + assert(loop != NULL); + PyArrayObject *result = NULL; npy_intp skip_first_count = 0; /* Iterator parameters */ NpyIter *iter = NULL; - PyArrayObject *op[2]; - PyArray_Descr *op_dtypes[2]; - npy_uint32 flags, op_flags[2]; + PyArrayObject *op[3]; + PyArray_Descr *op_dtypes[3]; + npy_uint32 it_flags, op_flags[3]; + /* Loop auxdata (must be freed on error) */ + NpyAuxData *auxdata = NULL; /* More than one axis means multiple orders are possible */ if (!reorderable && count_axes(PyArray_NDIM(operand), axis_flags) > 1) { @@ -458,31 +206,109 @@ PyUFunc_ReduceWrapper(PyArrayObject *operand, PyArrayObject *out, funcname); return NULL; } + /* Can only use where with an initial ( from identity or argument) */ + if (wheremask != NULL && identity == Py_None) { + PyErr_Format(PyExc_ValueError, + "reduction operation '%s' does not have an identity, " + "so to use a where mask one has to specify 'initial'", + funcname); + return NULL; + } + + /* Set up the iterator */ + op[0] = out; + op[1] = operand; + op_dtypes[0] = context->descriptors[0]; + op_dtypes[1] = context->descriptors[1]; + + it_flags = NPY_ITER_BUFFERED | + NPY_ITER_EXTERNAL_LOOP | + NPY_ITER_GROWINNER | + NPY_ITER_DONT_NEGATE_STRIDES | + NPY_ITER_ZEROSIZE_OK | + NPY_ITER_REFS_OK | + NPY_ITER_DELAY_BUFALLOC | + NPY_ITER_COPY_IF_OVERLAP; + op_flags[0] = NPY_ITER_READWRITE | + NPY_ITER_ALIGNED | + NPY_ITER_ALLOCATE | + NPY_ITER_NO_SUBTYPE; + op_flags[1] = NPY_ITER_READONLY | + NPY_ITER_ALIGNED | + NPY_ITER_NO_BROADCAST; - /* Validate that the parameters for future expansion are NULL */ if (wheremask != NULL) { - PyErr_SetString(PyExc_RuntimeError, - "Reduce operations in NumPy do not yet support " - "a where mask"); - return NULL; + op[2] = wheremask; + /* wheremask is guaranteed to be NPY_BOOL, so borrow its reference */ + op_dtypes[2] = PyArray_DESCR(wheremask); + assert(op_dtypes[2]->type_num == NPY_BOOL); + if (op_dtypes[2] == NULL) { + goto fail; + } + op_flags[2] = NPY_ITER_READONLY; + } + /* Set up result array axes mapping, operand and wheremask use default */ + int result_axes[NPY_MAXDIMS]; + int *op_axes[3] = {result_axes, NULL, NULL}; + + int curr_axis = 0; + for (int i = 0; i < PyArray_NDIM(operand); i++) { + if (axis_flags[i]) { + if (keepdims) { + result_axes[i] = NPY_ITER_REDUCTION_AXIS(curr_axis); + curr_axis++; + } + else { + result_axes[i] = NPY_ITER_REDUCTION_AXIS(-1); + } + } + else { + result_axes[i] = curr_axis; + curr_axis++; + } + } + if (out != NULL) { + /* NpyIter does not raise a good error message in this common case. */ + if (NPY_UNLIKELY(curr_axis != PyArray_NDIM(out))) { + if (keepdims) { + PyErr_Format(PyExc_ValueError, + "output parameter for reduction operation %s has the " + "wrong number of dimensions: Found %d but expected %d " + "(must match the operand's when keepdims=True)", + funcname, PyArray_NDIM(out), curr_axis); + } + else { + PyErr_Format(PyExc_ValueError, + "output parameter for reduction operation %s has the " + "wrong number of dimensions: Found %d but expected %d", + funcname, PyArray_NDIM(out), curr_axis); + } + goto fail; + } } - /* - * This either conforms 'out' to the ndim of 'operand', or allocates - * a new array appropriate for this reduction. - * - * A new array with WRITEBACKIFCOPY is allocated if operand and out have memory - * overlap. - */ - Py_INCREF(result_dtype); - result = PyArray_CreateReduceResult(operand, out, - result_dtype, axis_flags, - keepdims, subok, funcname); - if (result == NULL) { + iter = NpyIter_AdvancedNew(wheremask == NULL ? 2 : 3, op, it_flags, + NPY_KEEPORDER, NPY_UNSAFE_CASTING, + op_flags, + op_dtypes, + PyArray_NDIM(operand), op_axes, NULL, buffersize); + if (iter == NULL) { goto fail; } + result = NpyIter_GetOperandArray(iter)[0]; + + PyArrayMethod_StridedLoop *strided_loop; + NPY_ARRAYMETHOD_FLAGS flags = 0; + + int needs_api = (flags & NPY_METH_REQUIRES_PYAPI) != 0; + needs_api |= NpyIter_IterationNeedsAPI(iter); + if (!(flags & NPY_METH_NO_FLOATINGPOINT_ERRORS)) { + /* Start with the floating-point exception flags cleared */ + npy_clear_floatstatus_barrier((char*)&iter); + } + /* * Initialize the result to the reduction unit if possible, * otherwise copy the initial values and get a view to the rest. @@ -491,60 +317,48 @@ PyUFunc_ReduceWrapper(PyArrayObject *operand, PyArrayObject *out, if (PyArray_FillWithScalar(result, identity) < 0) { goto fail; } - op_view = operand; - Py_INCREF(op_view); } else { - op_view = PyArray_InitializeReduceResult( - result, operand, axis_flags, &skip_first_count, funcname); - if (op_view == NULL) { + /* + * For 1-D skip_first_count could be optimized to 0, but no-identity + * reductions are not super common. + * (see also comment in CopyInitialReduceValues) + */ + skip_first_count = PyArray_CopyInitialReduceValues( + result, operand, axis_flags, funcname, keepdims); + if (skip_first_count < 0) { goto fail; } - /* empty op_view signals no reduction; but 0-d arrays cannot be empty */ - if ((PyArray_SIZE(op_view) == 0) || (PyArray_NDIM(operand) == 0)) { - Py_DECREF(op_view); - op_view = NULL; - goto finish; - } } - /* Set up the iterator */ - op[0] = result; - op[1] = op_view; - op_dtypes[0] = result_dtype; - op_dtypes[1] = operand_dtype; - - flags = NPY_ITER_BUFFERED | - NPY_ITER_EXTERNAL_LOOP | - NPY_ITER_GROWINNER | - NPY_ITER_DONT_NEGATE_STRIDES | - NPY_ITER_ZEROSIZE_OK | - NPY_ITER_REDUCE_OK | - NPY_ITER_REFS_OK; - op_flags[0] = NPY_ITER_READWRITE | - NPY_ITER_ALIGNED | - NPY_ITER_NO_SUBTYPE; - op_flags[1] = NPY_ITER_READONLY | - NPY_ITER_ALIGNED; - - iter = NpyIter_AdvancedNew(2, op, flags, - NPY_KEEPORDER, casting, - op_flags, - op_dtypes, - -1, NULL, NULL, buffersize); - if (iter == NULL) { + if (!NpyIter_Reset(iter, NULL)) { goto fail; } - /* Start with the floating-point exception flags cleared */ - npy_clear_floatstatus_barrier((char*)&iter); + /* + * Note that we need to ensure that the iterator is reset before getting + * the fixed strides. (The buffer information is unitialized before.) + */ + npy_intp fixed_strides[3]; + NpyIter_GetInnerFixedStrideArray(iter, fixed_strides); + if (wheremask != NULL) { + if (PyArrayMethod_GetMaskedStridedLoop(context, + 1, fixed_strides, &strided_loop, &auxdata, &flags) < 0) { + goto fail; + } + } + else { + if (context->method->get_strided_loop(context, + 1, 0, fixed_strides, &strided_loop, &auxdata, &flags) < 0) { + goto fail; + } + } if (NpyIter_GetIterSize(iter) != 0) { NpyIter_IterNextFunc *iternext; char **dataptr; npy_intp *strideptr; npy_intp *countptr; - int needs_api; iternext = NpyIter_GetIterNext(iter, NULL); if (iternext == NULL) { @@ -554,52 +368,34 @@ PyUFunc_ReduceWrapper(PyArrayObject *operand, PyArrayObject *out, strideptr = NpyIter_GetInnerStrideArray(iter); countptr = NpyIter_GetInnerLoopSizePtr(iter); - needs_api = NpyIter_IterationNeedsAPI(iter); - - /* Straightforward reduction */ - if (loop == NULL) { - PyErr_Format(PyExc_RuntimeError, - "reduction operation %s did not supply an " - "inner loop function", funcname); + if (loop(context, strided_loop, auxdata, + iter, dataptr, strideptr, countptr, iternext, + needs_api, skip_first_count) < 0) { goto fail; } + } - if (loop(iter, dataptr, strideptr, countptr, - iternext, needs_api, skip_first_count, data) < 0) { - + if (!(flags & NPY_METH_NO_FLOATINGPOINT_ERRORS)) { + /* NOTE: We could check float errors even on error */ + if (_check_ufunc_fperr(errormask, NULL, "reduce") < 0) { goto fail; } } - - /* Check whether any errors occurred during the loop */ - if (PyErr_Occurred() || - _check_ufunc_fperr(errormask, NULL, "reduce") < 0) { - goto fail; - } - - NpyIter_Deallocate(iter); - Py_DECREF(op_view); -finish: - /* Strip out the extra 'one' dimensions in the result */ - if (out == NULL) { - if (!keepdims) { - PyArray_RemoveAxesInPlace(result, axis_flags); - } - } - else { - PyArray_ResolveWritebackIfCopy(result); /* prevent spurious warnings */ - Py_DECREF(result); + if (out != NULL) { result = out; - Py_INCREF(result); } + Py_INCREF(result); + NPY_AUXDATA_FREE(auxdata); + if (!NpyIter_Deallocate(iter)) { + Py_DECREF(result); + return NULL; + } return result; fail: - PyArray_ResolveWritebackIfCopy(result); /* prevent spurious warnings */ - Py_XDECREF(result); - Py_XDECREF(op_view); + NPY_AUXDATA_FREE(auxdata); if (iter != NULL) { NpyIter_Deallocate(iter); } diff --git a/numpy/core/src/umath/reduction.h b/numpy/core/src/umath/reduction.h index dfaeabcbbdbd..2170e27a7f9e 100644 --- a/numpy/core/src/umath/reduction.h +++ b/numpy/core/src/umath/reduction.h @@ -19,93 +19,17 @@ typedef int (PyArray_AssignReduceIdentityFunc)(PyArrayObject *result, void *data); /* - * This is a function for the reduce loop. + * Inner definition of the reduce loop, only used for a static function. + * At some point around NumPy 1.6, there was probably an intention to make + * the reduce loop customizable at this level (per ufunc?). * - * The needs_api parameter indicates whether it's ok to release the GIL during - * the loop, such as when the iternext() function never calls - * a function which could raise a Python exception. - * - * The skip_first_count parameter indicates how many elements need to be - * skipped based on NpyIter_IsFirstVisit checks. This can only be positive - * when the 'assign_identity' parameter was NULL when calling - * PyArray_ReduceWrapper. - * - * The loop gets two data pointers and two strides, and should - * look roughly like this: - * { - * NPY_BEGIN_THREADS_DEF; - * if (!needs_api) { - * NPY_BEGIN_THREADS; - * } - * // This first-visit loop can be skipped if 'assign_identity' was non-NULL - * if (skip_first_count > 0) { - * do { - * char *data0 = dataptr[0], *data1 = dataptr[1]; - * npy_intp stride0 = strideptr[0], stride1 = strideptr[1]; - * npy_intp count = *countptr; - * - * // Skip any first-visit elements - * if (NpyIter_IsFirstVisit(iter, 0)) { - * if (stride0 == 0) { - * --count; - * --skip_first_count; - * data1 += stride1; - * } - * else { - * skip_first_count -= count; - * count = 0; - * } - * } - * - * while (count--) { - * *(result_t *)data0 = my_reduce_op(*(result_t *)data0, - * *(operand_t *)data1); - * data0 += stride0; - * data1 += stride1; - * } - * - * // Jump to the faster loop when skipping is done - * if (skip_first_count == 0) { - * if (iternext(iter)) { - * break; - * } - * else { - * goto finish_loop; - * } - * } - * } while (iternext(iter)); - * } - * do { - * char *data0 = dataptr[0], *data1 = dataptr[1]; - * npy_intp stride0 = strideptr[0], stride1 = strideptr[1]; - * npy_intp count = *countptr; - * - * while (count--) { - * *(result_t *)data0 = my_reduce_op(*(result_t *)data0, - * *(operand_t *)data1); - * data0 += stride0; - * data1 += stride1; - * } - * } while (iternext(iter)); - * finish_loop: - * if (!needs_api) { - * NPY_END_THREADS; - * } - * return (needs_api && PyErr_Occurred()) ? -1 : 0; - * } - * - * If needs_api is True, this function should call PyErr_Occurred() - * to check if an error occurred during processing, and return -1 for - * error, 0 for success. + * TODO: This should be refactored/removed. */ -typedef int (PyArray_ReduceLoopFunc)(NpyIter *iter, - char **dataptr, - npy_intp *strideptr, - npy_intp *countptr, - NpyIter_IterNextFunc *iternext, - int needs_api, - npy_intp skip_first_count, - void *data); +typedef int (PyArray_ReduceLoopFunc)(PyArrayMethod_Context *context, + PyArrayMethod_StridedLoop *strided_loop, NpyAuxData *auxdata, + NpyIter *iter, char **dataptrs, npy_intp const *strides, + npy_intp const *countptr, NpyIter_IterNextFunc *iternext, + int needs_api, npy_intp skip_first_count); /* * This function executes all the standard NumPy reduction function @@ -128,9 +52,7 @@ typedef int (PyArray_ReduceLoopFunc)(NpyIter *iter, * of cache behavior or multithreading requirements. * keepdims : If true, leaves the reduction dimensions in the result * with size one. - * subok : If true, the result uses the subclass of operand, otherwise - * it is always a base class ndarray. - * identity : If Py_None, PyArray_InitializeReduceResult is used, otherwise + * identity : If Py_None, PyArray_CopyInitialReduceValues is used, otherwise * this value is used to initialize the result to * the reduction's unit. * loop : The loop which does the reduction. @@ -140,17 +62,10 @@ typedef int (PyArray_ReduceLoopFunc)(NpyIter *iter, * errormask : forwarded from _get_bufsize_errmask */ NPY_NO_EXPORT PyArrayObject * -PyUFunc_ReduceWrapper(PyArrayObject *operand, PyArrayObject *out, - PyArrayObject *wheremask, - PyArray_Descr *operand_dtype, - PyArray_Descr *result_dtype, - NPY_CASTING casting, - npy_bool *axis_flags, int reorderable, - int keepdims, - int subok, - PyObject *identity, - PyArray_ReduceLoopFunc *loop, - void *data, npy_intp buffersize, const char *funcname, - int errormask); +PyUFunc_ReduceWrapper(PyArrayMethod_Context *context, + PyArrayObject *operand, PyArrayObject *out, PyArrayObject *wheremask, + npy_bool *axis_flags, int reorderable, int keepdims, + PyObject *identity, PyArray_ReduceLoopFunc *loop, + void *data, npy_intp buffersize, const char *funcname, int errormask); #endif diff --git a/numpy/core/src/umath/scalarmath.c.src b/numpy/core/src/umath/scalarmath.c.src index 3e29c4b4e33f..402e6b561717 100644 --- a/numpy/core/src/umath/scalarmath.c.src +++ b/numpy/core/src/umath/scalarmath.c.src @@ -5,19 +5,19 @@ but still supports error-modes. */ +#define PY_SSIZE_T_CLEAN +#include <Python.h> #define _UMATHMODULE +#define _MULTIARRAYMODULE #define NPY_NO_DEPRECATED_API NPY_API_VERSION -#include "Python.h" #include "npy_config.h" -#define PY_ARRAY_UNIQUE_SYMBOL _npy_umathmodule_ARRAY_API -#define NO_IMPORT_ARRAY - #include "numpy/arrayobject.h" #include "numpy/ufuncobject.h" #include "numpy/arrayscalars.h" +#include "npy_import.h" #include "npy_pycompat.h" #include "numpy/halffloat.h" @@ -248,25 +248,26 @@ static void /**end repeat**/ - -/* QUESTION: Should we check for overflow / underflow in (l,r)shift? */ - /**begin repeat * #name = byte, ubyte, short, ushort, int, uint, * long, ulong, longlong, ulonglong# * #type = npy_byte, npy_ubyte, npy_short, npy_ushort, npy_int, npy_uint, * npy_long, npy_ulong, npy_longlong, npy_ulonglong# + * #suffix = hh,uhh,h,uh,,u,l,ul,ll,ull# */ /**begin repeat1 - * #oper = and, xor, or, lshift, rshift# - * #op = &, ^, |, <<, >># + * #oper = and, xor, or# + * #op = &, ^, |# */ #define @name@_ctype_@oper@(arg1, arg2, out) *(out) = (arg1) @op@ (arg2) /**end repeat1**/ +#define @name@_ctype_lshift(arg1, arg2, out) *(out) = npy_lshift@suffix@(arg1, arg2) +#define @name@_ctype_rshift(arg1, arg2, out) *(out) = npy_rshift@suffix@(arg1, arg2) + /**end repeat**/ /**begin repeat @@ -283,15 +284,13 @@ static void static void @name@_ctype_floor_divide(@type@ a, @type@ b, @type@ *out) { - @type@ mod; - - *out = npy_divmod@c@(a, b, &mod); + *out = npy_floor_divide@c@(a, b); } static void @name@_ctype_remainder(@type@ a, @type@ b, @type@ *out) { - npy_divmod@c@(a, b, out); + *out = npy_remainder@c@(a, b); } @@ -318,7 +317,11 @@ static void half_ctype_floor_divide(npy_half a, npy_half b, npy_half *out) { npy_half mod; - *out = npy_half_divmod(a, b, &mod); + if (!b) { + *out = a / b; + } else { + *out = npy_half_divmod(a, b, &mod); + } } @@ -407,21 +410,22 @@ half_ctype_divmod(npy_half a, npy_half b, npy_half *out1, npy_half *out2) { /**begin repeat * #name = float, double, longdouble# * #type = npy_float, npy_double, npy_longdouble# + * #c = f,,l# */ -static npy_@name@ (*_basic_@name@_pow)(@type@ a, @type@ b); static void @name@_ctype_power(@type@ a, @type@ b, @type@ *out) { - *out = _basic_@name@_pow(a, b); + *out = npy_pow@c@(a, b); } + /**end repeat**/ static void half_ctype_power(npy_half a, npy_half b, npy_half *out) { const npy_float af = npy_half_to_float(a); const npy_float bf = npy_half_to_float(b); - const npy_float outf = _basic_float_pow(af,bf); + const npy_float outf = npy_powf(af,bf); *out = npy_float_to_half(outf); } @@ -478,14 +482,10 @@ static void } /**end repeat**/ -/* - * Get the nc_powf, nc_pow, and nc_powl functions from - * the data area of the power ufunc in umathmodule. - */ - /**begin repeat * #name = cfloat, cdouble, clongdouble# * #type = npy_cfloat, npy_cdouble, npy_clongdouble# + * #c = f,,l# */ static void @name@_ctype_positive(@type@ a, @type@ *out) @@ -494,12 +494,10 @@ static void out->imag = a.imag; } -static void (*_basic_@name@_pow)(@type@ *, @type@ *, @type@ *); - static void @name@_ctype_power(@type@ a, @type@ b, @type@ *out) { - _basic_@name@_pow(&a, &b, out); + *out = npy_cpow@c@(a, b); } /**end repeat**/ @@ -572,7 +570,7 @@ static void * 1) Convert the types to the common type if both are scalars (0 return) * 2) If both are not scalars use ufunc machinery (-2 return) * 3) If both are scalars but cannot be cast to the right type - * return NotImplmented (-1 return) + * return NotImplemented (-1 return) * * 4) Perform the function on the C-type. * 5) If an error condition occurred, check to see @@ -734,6 +732,9 @@ _@name@_convert2_to_ctypes(PyObject *a, @type@ *arg1, { int ret; ret = _@name@_convert_to_ctype(a, arg1); + if (ret == -2) { + ret = -3; + } if (ret < 0) { return ret; } @@ -750,70 +751,57 @@ _@name@_convert2_to_ctypes(PyObject *a, @type@ *arg1, /**end repeat**/ -#if defined(NPY_PY3K) -#define CODEGEN_SKIP_divide_FLAG -#endif - /**begin repeat * * #name = (byte, ubyte, short, ushort, int, uint, - * long, ulong, longlong, ulonglong)*13, + * long, ulong, longlong, ulonglong)*12, * (half, float, double, longdouble, - * cfloat, cdouble, clongdouble)*6, + * cfloat, cdouble, clongdouble)*5, * (half, float, double, longdouble)*2# * #Name = (Byte, UByte, Short, UShort, Int, UInt, - * Long, ULong,LongLong,ULongLong)*13, + * Long, ULong,LongLong,ULongLong)*12, * (Half, Float, Double, LongDouble, - * CFloat, CDouble, CLongDouble)*6, + * CFloat, CDouble, CLongDouble)*5, * (Half, Float, Double, LongDouble)*2# * #type = (npy_byte, npy_ubyte, npy_short, npy_ushort, npy_int, npy_uint, - * npy_long, npy_ulong, npy_longlong, npy_ulonglong)*13, + * npy_long, npy_ulong, npy_longlong, npy_ulonglong)*12, * (npy_half, npy_float, npy_double, npy_longdouble, - * npy_cfloat, npy_cdouble, npy_clongdouble)*6, + * npy_cfloat, npy_cdouble, npy_clongdouble)*5, * (npy_half, npy_float, npy_double, npy_longdouble)*2# * - * #oper = add*10, subtract*10, multiply*10, divide*10, remainder*10, + * #oper = add*10, subtract*10, multiply*10, remainder*10, * divmod*10, floor_divide*10, lshift*10, rshift*10, and*10, * or*10, xor*10, true_divide*10, - * add*7, subtract*7, multiply*7, divide*7, floor_divide*7, true_divide*7, + * add*7, subtract*7, multiply*7, floor_divide*7, true_divide*7, * divmod*4, remainder*4# * - * #fperr = 1*70,0*50,1*10, - * 1*42, + * #fperr = 1*60,0*50,1*10, + * 1*35, * 1*8# - * #twoout = 0*50,1*10,0*70, - * 0*42, + * #twoout = 0*40,1*10,0*70, + * 0*35, * 1*4,0*4# * #otype = (npy_byte, npy_ubyte, npy_short, npy_ushort, npy_int, npy_uint, - * npy_long, npy_ulong, npy_longlong, npy_ulonglong)*12, + * npy_long, npy_ulong, npy_longlong, npy_ulonglong)*11, * npy_float*4, npy_double*6, * (npy_half, npy_float, npy_double, npy_longdouble, - * npy_cfloat, npy_cdouble, npy_clongdouble)*6, + * npy_cfloat, npy_cdouble, npy_clongdouble)*5, * (npy_half, npy_float, npy_double, npy_longdouble)*2# * #OName = (Byte, UByte, Short, UShort, Int, UInt, - * Long, ULong, LongLong, ULongLong)*12, + * Long, ULong, LongLong, ULongLong)*11, * Float*4, Double*6, * (Half, Float, Double, LongDouble, - * CFloat, CDouble, CLongDouble)*6, + * CFloat, CDouble, CLongDouble)*5, * (Half, Float, Double, LongDouble)*2# */ -#if !defined(CODEGEN_SKIP_@oper@_FLAG) - static PyObject * @name@_@oper@(PyObject *a, PyObject *b) { PyObject *ret; @type@ arg1, arg2; - /* - * NOTE: In gcc >= 4.1, the compiler will reorder floating point - * operations and floating point error state checks. In - * particular, the arithmetic operations were being reordered - * so that the errors weren't caught. Declaring this output - * variable volatile was the minimal fix for the issue. - * (Ticket #1671) - */ - volatile @otype@ out; + @otype@ out; + #if @twoout@ @otype@ out2; PyObject *obj; @@ -910,12 +898,9 @@ static PyObject * #endif return ret; } -#endif /**end repeat**/ -#undef CODEGEN_SKIP_divide_FLAG - #define _IS_ZERO(x) (x == 0) /**begin repeat @@ -946,22 +931,19 @@ static PyObject * * Double, LongDouble, * CFloat, CDouble, CLongDouble# * - * #isint = (1,0)*5,0*7# + * #isint = 1*10,0*7# + * #isuint = (0,1)*5,0*7# * #cmplx = 0*14,1*3# * #iszero = _IS_ZERO*10, npy_half_iszero, _IS_ZERO*6# * #zero = 0*10, NPY_HALF_ZERO, 0*6# * #one = 1*10, NPY_HALF_ONE, 1*6# */ -#if @cmplx@ static PyObject * @name@_power(PyObject *a, PyObject *b, PyObject *modulo) { PyObject *ret; - @type@ arg1, arg2; - int retstatus; - int first; - @type@ out = {@zero@, @zero@}; + @type@ arg1, arg2, out; BINOP_GIVE_UP_IF_NEEDED(a, b, nb_power, @name@_power); @@ -993,22 +975,25 @@ static PyObject * return Py_NotImplemented; } +#if !@isint@ npy_clear_floatstatus_barrier((char*)&out); - +#endif /* * here we do the actual calculation with arg1 and arg2 * as a function call. */ - if (@iszero@(arg2.real) && @iszero@(arg2.imag)) { - out.real = @one@; - out.imag = @zero@; - } - else { - @name@_ctype_power(arg1, arg2, &out); +#if @isint@ && !@isuint@ + if (arg2 < 0) { + PyErr_SetString(PyExc_ValueError, + "Integers to negative integer powers are not allowed."); + return NULL; } +#endif + @name@_ctype_power(arg1, arg2, &out); +#if !@isint@ /* Check status flag. If it is set, then look up what to do */ - retstatus = npy_get_floatstatus_barrier((char*)&out); + int retstatus = npy_get_floatstatus_barrier((char*)&out); if (retstatus) { int bufsize, errmask; PyObject *errobj; @@ -1017,13 +1002,14 @@ static PyObject * &errobj) < 0) { return NULL; } - first = 1; + int first = 1; if (PyUFunc_handlefperr(errmask, errobj, retstatus, &first)) { Py_XDECREF(errobj); return NULL; } Py_XDECREF(errobj); } +#endif ret = PyArrayScalar_New(@Name@); if (ret == NULL) { @@ -1034,94 +1020,65 @@ static PyObject * return ret; } -#elif @isint@ -static PyObject * -@name@_power(PyObject *a, PyObject *b, PyObject *modulo) -{ - PyObject *ret; - @type@ arg1, arg2, out; +/**end repeat**/ +#undef _IS_ZERO - BINOP_GIVE_UP_IF_NEEDED(a, b, nb_power, @name@_power); - switch(_@name@_convert2_to_ctypes(a, &arg1, b, &arg2)) { - case 0: - break; - case -1: - /* can't cast both safely mixed-types? */ - return PyArray_Type.tp_as_number->nb_power(a,b,modulo); - case -2: - /* use default handling */ - if (PyErr_Occurred()) { - return NULL; - } - return PyGenericArrType_Type.tp_as_number->nb_power(a,b,modulo); - case -3: - default: - /* - * special case for longdouble and clongdouble - * because they have a recursive getitem in their dtype - */ - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } +/**begin repeat + * + * #name = cfloat, cdouble# + * + */ - if (modulo != Py_None) { - /* modular exponentiation is not implemented (gh-8804) */ - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } +/**begin repeat1 + * + * #oper = divmod, remainder# + * + */ - npy_clear_floatstatus_barrier((char*)&out); +#define @name@_@oper@ NULL - /* - * here we do the actual calculation with arg1 and arg2 - * as a function call. - */ - if (arg2 < 0) { - PyErr_SetString(PyExc_ValueError, - "Integers to negative integer powers are not allowed."); - return NULL; - } - @name@_ctype_power(arg1, arg2, &out); +/**end repeat1**/ - ret = PyArrayScalar_New(@Name@); - if (ret == NULL) { - return NULL; - } - PyArrayScalar_ASSIGN(ret, @Name@, out); +/**end repeat**/ - return ret; -} +/**begin repeat + * + * #oper = divmod, remainder# + * + */ -#else +/* +Complex numbers do not support remainder operations. Unfortunately, +the type inference for long doubles is complicated, and if a remainder +operation is not defined - if the relevant field is left NULL - then +operations between long doubles and objects lead to an infinite recursion +instead of a TypeError. This should ensure that once everything gets +converted to complex long doubles you correctly get a reasonably +informative TypeError. This fixes the last part of bug gh-18548. +*/ static PyObject * -@name@_power(PyObject *a, PyObject *b, PyObject *modulo) +clongdouble_@oper@(PyObject *a, PyObject *b) { - PyObject *ret; - @type@ arg1, arg2; - int retstatus; - int first; - - @type@ out = @zero@; + npy_clongdouble arg1, arg2; - BINOP_GIVE_UP_IF_NEEDED(a, b, nb_power, @name@_power); + BINOP_GIVE_UP_IF_NEEDED(a, b, nb_@oper@, clongdouble_@oper@); - switch(_@name@_convert2_to_ctypes(a, &arg1, b, &arg2)) { + switch(_clongdouble_convert2_to_ctypes(a, &arg1, b, &arg2)) { case 0: break; case -1: - /* can't cast both safely mixed-types? */ - return PyArray_Type.tp_as_number->nb_power(a,b,modulo); + /* one of them can't be cast safely must be mixed-types*/ + return PyArray_Type.tp_as_number->nb_@oper@(a,b); case -2: /* use default handling */ if (PyErr_Occurred()) { return NULL; } - return PyGenericArrType_Type.tp_as_number->nb_power(a,b,modulo); + return PyGenericArrType_Type.tp_as_number->nb_@oper@(a,b); case -3: - default: /* * special case for longdouble and clongdouble * because they have a recursive getitem in their dtype @@ -1130,74 +1087,14 @@ static PyObject * return Py_NotImplemented; } - if (modulo != Py_None) { - /* modular exponentiation is not implemented (gh-8804) */ - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } - - npy_clear_floatstatus_barrier((char*)&out); - /* * here we do the actual calculation with arg1 and arg2 * as a function call. */ - if (@iszero@(arg2)) { - out = @one@; - } - else { - @name@_ctype_power(arg1, arg2, &out); - } - - /* Check status flag. If it is set, then look up what to do */ - retstatus = npy_get_floatstatus_barrier((char*)&out); - if (retstatus) { - int bufsize, errmask; - PyObject *errobj; - - if (PyUFunc_GetPyValues("@name@_scalars", &bufsize, &errmask, - &errobj) < 0) { - return NULL; - } - first = 1; - if (PyUFunc_handlefperr(errmask, errobj, retstatus, &first)) { - Py_XDECREF(errobj); - return NULL; - } - Py_XDECREF(errobj); - } - - ret = PyArrayScalar_New(@Name@); - if (ret == NULL) { - return NULL; - } - PyArrayScalar_ASSIGN(ret, @Name@, out); - - return ret; + PyErr_SetString(PyExc_TypeError, "complex long doubles do not support remainder"); + return NULL; } -#endif - -/**end repeat**/ -#undef _IS_ZERO - - -/**begin repeat - * - * #name = cfloat, cdouble, clongdouble# - * - */ - -/**begin repeat1 - * - * #oper = divmod, remainder# - * - */ - -#define @name@_@oper@ NULL - -/**end repeat1**/ - /**end repeat**/ /**begin repeat @@ -1306,12 +1203,6 @@ static PyObject * /**end repeat**/ -#if defined(NPY_PY3K) -#define NONZERO_NAME(prefix) prefix##bool -#else -#define NONZERO_NAME(prefix) prefix##nonzero -#endif - #define _IS_NONZERO(x) (x != 0) /**begin repeat * @@ -1327,7 +1218,7 @@ static PyObject * * #nonzero = _IS_NONZERO*10, !npy_half_iszero, _IS_NONZERO*6# */ static int -NONZERO_NAME(@name@_)(PyObject *a) +@name@_bool(PyObject *a) { int ret; @type@ arg1; @@ -1336,7 +1227,7 @@ NONZERO_NAME(@name@_)(PyObject *a) if (PyErr_Occurred()) { return -1; } - return PyGenericArrType_Type.tp_as_number->NONZERO_NAME(nb_)(a); + return PyGenericArrType_Type.tp_as_number->nb_bool(a); } /* @@ -1360,13 +1251,9 @@ static int emit_complexwarning(void) { static PyObject *cls = NULL; + npy_cache_import("numpy.core", "ComplexWarning", &cls); if (cls == NULL) { - PyObject *mod; - mod = PyImport_ImportModule("numpy.core"); - assert(mod != NULL); - cls = PyObject_GetAttrString(mod, "ComplexWarning"); - assert(cls != NULL); - Py_DECREF(mod); + return -1; } return PyErr_WarnEx(cls, "Casting complex values to real discards the imaginary part", 1); @@ -1416,39 +1303,26 @@ static PyObject * return NULL; } -#ifndef NPY_PY3K - /* Invoke long.__int__ to try to downcast */ - { - PyObject *before_downcast = long_result; - long_result = Py_TYPE(long_result)->tp_as_number->nb_int(long_result); - Py_DECREF(before_downcast); - } -#endif - return long_result; } /**end repeat**/ /**begin repeat * - * #name = (byte, ubyte, short, ushort, int, uint, + * #name = byte, ubyte, short, ushort, int, uint, * long, ulong, longlong, ulonglong, * half, float, double, longdouble, - * cfloat, cdouble, clongdouble)*2# - * #Name = (Byte, UByte, Short, UShort, Int, UInt, + * cfloat, cdouble, clongdouble# + * #Name = Byte, UByte, Short, UShort, Int, UInt, * Long, ULong, LongLong, ULongLong, * Half, Float, Double, LongDouble, - * CFloat, CDouble, CLongDouble)*2# - * #cmplx = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1)*2# - * #to_ctype = (, , , , , , , , , , npy_half_to_double, , , , , , )*2# - * #which = long*17, float*17# - * #func = (PyLong_FromLongLong, PyLong_FromUnsignedLongLong)*5, - * PyLong_FromDouble*3, npy_longdouble_to_PyLong, - * PyLong_FromDouble*2, npy_longdouble_to_PyLong, - * PyFloat_FromDouble*17# + * CFloat, CDouble, CLongDouble# + * #cmplx = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1# + * #to_ctype = , , , , , , , , , , npy_half_to_double, , , , , , # + * #func = PyFloat_FromDouble*17# */ static NPY_INLINE PyObject * -@name@_@which@(PyObject *obj) +@name@_float(PyObject *obj) { #if @cmplx@ if (emit_complexwarning() < 0) { @@ -1461,32 +1335,6 @@ static NPY_INLINE PyObject * } /**end repeat**/ -#if !defined(NPY_PY3K) - -/**begin repeat - * - * #name = (byte, ubyte, short, ushort, int, uint, - * long, ulong, longlong, ulonglong, - * half, float, double, longdouble, - * cfloat, cdouble, clongdouble)*2# - * #oper = oct*17, hex*17# - * #kind = (int*5, long*5, int*2, long*2, int, long*2)*2# - * #cap = (Int*5, Long*5, Int*2, Long*2, Int, Long*2)*2# - */ -static PyObject * -@name@_@oper@(PyObject *obj) -{ - PyObject *pyint; - pyint = @name@_@kind@(obj); - if (pyint == NULL) { - return NULL; - } - return Py@cap@_Type.tp_as_number->nb_@oper@(pyint); -} -/**end repeat**/ - -#endif - /**begin repeat * #oper = le, ge, lt, gt, eq, ne# * #op = <=, >=, <, >, ==, !=# @@ -1566,7 +1414,6 @@ static PyObject* } /**end repeat**/ - /**begin repeat * #name = byte, ubyte, short, ushort, int, uint, * long, ulong, longlong, ulonglong, @@ -1574,65 +1421,28 @@ static PyObject* * cfloat, cdouble, clongdouble# **/ static PyNumberMethods @name@_as_number = { - (binaryfunc)@name@_add, /*nb_add*/ - (binaryfunc)@name@_subtract, /*nb_subtract*/ - (binaryfunc)@name@_multiply, /*nb_multiply*/ -#if defined(NPY_PY3K) -#else - (binaryfunc)@name@_divide, /*nb_divide*/ -#endif - (binaryfunc)@name@_remainder, /*nb_remainder*/ - (binaryfunc)@name@_divmod, /*nb_divmod*/ - (ternaryfunc)@name@_power, /*nb_power*/ - (unaryfunc)@name@_negative, - (unaryfunc)@name@_positive, /*nb_pos*/ - (unaryfunc)@name@_absolute, /*nb_abs*/ -#if defined(NPY_PY3K) - (inquiry)@name@_bool, /*nb_bool*/ -#else - (inquiry)@name@_nonzero, /*nb_nonzero*/ -#endif - (unaryfunc)@name@_invert, /*nb_invert*/ - (binaryfunc)@name@_lshift, /*nb_lshift*/ - (binaryfunc)@name@_rshift, /*nb_rshift*/ - (binaryfunc)@name@_and, /*nb_and*/ - (binaryfunc)@name@_xor, /*nb_xor*/ - (binaryfunc)@name@_or, /*nb_or*/ -#if defined(NPY_PY3K) -#else - 0, /*nb_coerce*/ -#endif - (unaryfunc)@name@_int, /*nb_int*/ -#if defined(NPY_PY3K) - (unaryfunc)0, /*nb_reserved*/ -#else - (unaryfunc)@name@_long, /*nb_long*/ -#endif - (unaryfunc)@name@_float, /*nb_float*/ -#if defined(NPY_PY3K) -#else - (unaryfunc)@name@_oct, /*nb_oct*/ - (unaryfunc)@name@_hex, /*nb_hex*/ -#endif - 0, /*inplace_add*/ - 0, /*inplace_subtract*/ - 0, /*inplace_multiply*/ -#if defined(NPY_PY3K) -#else - 0, /*inplace_divide*/ -#endif - 0, /*inplace_remainder*/ - 0, /*inplace_power*/ - 0, /*inplace_lshift*/ - 0, /*inplace_rshift*/ - 0, /*inplace_and*/ - 0, /*inplace_xor*/ - 0, /*inplace_or*/ - (binaryfunc)@name@_floor_divide, /*nb_floor_divide*/ - (binaryfunc)@name@_true_divide, /*nb_true_divide*/ - 0, /*nb_inplace_floor_divide*/ - 0, /*nb_inplace_true_divide*/ - (unaryfunc)NULL, /*nb_index*/ + .nb_add = (binaryfunc)@name@_add, + .nb_subtract = (binaryfunc)@name@_subtract, + .nb_multiply = (binaryfunc)@name@_multiply, + .nb_remainder = (binaryfunc)@name@_remainder, + .nb_divmod = (binaryfunc)@name@_divmod, + .nb_power = (ternaryfunc)@name@_power, + .nb_negative = (unaryfunc)@name@_negative, + .nb_positive = (unaryfunc)@name@_positive, + .nb_absolute = (unaryfunc)@name@_absolute, + .nb_bool = (inquiry)@name@_bool, + .nb_invert = (unaryfunc)@name@_invert, + .nb_lshift = (binaryfunc)@name@_lshift, + .nb_rshift = (binaryfunc)@name@_rshift, + .nb_and = (binaryfunc)@name@_and, + .nb_xor = (binaryfunc)@name@_xor, + .nb_or = (binaryfunc)@name@_or, + .nb_int = (unaryfunc)@name@_int, + .nb_float = (unaryfunc)@name@_float, + .nb_floor_divide = (binaryfunc)@name@_floor_divide, + .nb_true_divide = (binaryfunc)@name@_true_divide, + /* TODO: This struct/initialization should not be split between files */ + .nb_index = (unaryfunc)NULL, /* set in add_scalarmath below */ }; /**end repeat**/ @@ -1655,52 +1465,9 @@ add_scalarmath(void) /**end repeat**/ } -static int -get_functions(PyObject * mm) -{ - PyObject *obj; - void **funcdata; - char *signatures; - int i, j; - int ret = -1; - - /* Get the nc_pow functions */ - /* Get the pow functions */ - obj = PyObject_GetAttrString(mm, "power"); - if (obj == NULL) { - goto fail; - } - funcdata = ((PyUFuncObject *)obj)->data; - signatures = ((PyUFuncObject *)obj)->types; - - i = 0; - j = 0; - while (signatures[i] != NPY_FLOAT) { - i += 3; - j++; - } - _basic_float_pow = funcdata[j]; - _basic_double_pow = funcdata[j + 1]; - _basic_longdouble_pow = funcdata[j + 2]; - _basic_cfloat_pow = funcdata[j + 3]; - _basic_cdouble_pow = funcdata[j + 4]; - _basic_clongdouble_pow = funcdata[j + 5]; - Py_DECREF(obj); - - return ret = 0; - - fail: - Py_DECREF(mm); - return ret; -} - NPY_NO_EXPORT int initscalarmath(PyObject * m) { - if (get_functions(m) < 0) { - return -1; - } - add_scalarmath(); return 0; diff --git a/numpy/core/src/umath/simd.inc.src b/numpy/core/src/umath/simd.inc.src index 5c0568c12071..0e2c1ab8b31b 100644 --- a/numpy/core/src/umath/simd.inc.src +++ b/numpy/core/src/umath/simd.inc.src @@ -1,4 +1,4 @@ -/* -*- c -*- */ + /* * This file is for the definitions of simd vectorized operations. @@ -17,9 +17,8 @@ #include "lowlevel_strided_loops.h" #include "numpy/npy_common.h" -/* for NO_FLOATING_POINT_SUPPORT */ -#include "numpy/ufuncobject.h" #include "numpy/npy_math.h" +#include "npy_simd_data.h" #ifdef NPY_HAVE_SSE2_INTRINSICS #include <emmintrin.h> #if !defined(_MSC_VER) || _MSC_VER >= 1600 @@ -29,92 +28,60 @@ #undef __AVX512F__ #endif #endif +#include "loops_utils.h" // nomemoverlap #include <assert.h> #include <stdlib.h> #include <float.h> #include <string.h> /* for memcpy */ -static NPY_INLINE npy_uintp -abs_ptrdiff(char *a, char *b) -{ - return (a > b) ? (a - b) : (b - a); -} +#define VECTOR_SIZE_BYTES 16 +/* + * Dispatcher functions + * decide whether the operation can be vectorized and run it + * if it was run returns true and false if nothing was done + */ /* - * stride is equal to element size and input and destination are equal or - * don't overlap within one register. The check of the steps against - * esize also quarantees that steps are >= 0. + ***************************************************************************** + ** CMPLX DISPATCHERS + ***************************************************************************** */ -#define IS_BLOCKABLE_UNARY(esize, vsize) \ - (steps[0] == (esize) && steps[0] == steps[1] && \ - (npy_is_aligned(args[0], esize) && npy_is_aligned(args[1], esize)) && \ - ((abs_ptrdiff(args[1], args[0]) >= (vsize)) || \ - ((abs_ptrdiff(args[1], args[0]) == 0)))) - -#define IS_BLOCKABLE_REDUCE(esize, vsize) \ - (steps[1] == (esize) && abs_ptrdiff(args[1], args[0]) >= (vsize) && \ - npy_is_aligned(args[1], (esize)) && \ - npy_is_aligned(args[0], (esize))) - -#define IS_BLOCKABLE_BINARY(esize, vsize) \ - (steps[0] == steps[1] && steps[1] == steps[2] && steps[2] == (esize) && \ - npy_is_aligned(args[2], (esize)) && npy_is_aligned(args[1], (esize)) && \ - npy_is_aligned(args[0], (esize)) && \ - (abs_ptrdiff(args[2], args[0]) >= (vsize) || \ - abs_ptrdiff(args[2], args[0]) == 0) && \ - (abs_ptrdiff(args[2], args[1]) >= (vsize) || \ - abs_ptrdiff(args[2], args[1]) >= 0)) - -#define IS_BLOCKABLE_BINARY_SCALAR1(esize, vsize) \ - (steps[0] == 0 && steps[1] == steps[2] && steps[2] == (esize) && \ - npy_is_aligned(args[2], (esize)) && npy_is_aligned(args[1], (esize)) && \ - ((abs_ptrdiff(args[2], args[1]) >= (vsize)) || \ - (abs_ptrdiff(args[2], args[1]) == 0)) && \ - abs_ptrdiff(args[2], args[0]) >= (esize)) - -#define IS_BLOCKABLE_BINARY_SCALAR2(esize, vsize) \ - (steps[1] == 0 && steps[0] == steps[2] && steps[2] == (esize) && \ - npy_is_aligned(args[2], (esize)) && npy_is_aligned(args[0], (esize)) && \ - ((abs_ptrdiff(args[2], args[0]) >= (vsize)) || \ - (abs_ptrdiff(args[2], args[0]) == 0)) && \ - abs_ptrdiff(args[2], args[1]) >= (esize)) - -#undef abs_ptrdiff - -#define IS_BLOCKABLE_BINARY_BOOL(esize, vsize) \ - (steps[0] == (esize) && steps[0] == steps[1] && steps[2] == (1) && \ - npy_is_aligned(args[1], (esize)) && \ - npy_is_aligned(args[0], (esize))) - -#define IS_BLOCKABLE_BINARY_SCALAR1_BOOL(esize, vsize) \ - (steps[0] == 0 && steps[1] == (esize) && steps[2] == (1) && \ - npy_is_aligned(args[1], (esize))) - -#define IS_BLOCKABLE_BINARY_SCALAR2_BOOL(esize, vsize) \ - (steps[0] == (esize) && steps[1] == 0 && steps[2] == (1) && \ - npy_is_aligned(args[0], (esize))) - -/* align var to alignment */ -#define LOOP_BLOCK_ALIGN_VAR(var, type, alignment)\ - npy_intp i, peel = npy_aligned_block_offset(var, sizeof(type),\ - alignment, n);\ - for(i = 0; i < peel; i++) - -#define LOOP_BLOCKED(type, vsize)\ - for(; i < npy_blocked_end(peel, sizeof(type), vsize, n);\ - i += (vsize / sizeof(type))) - -#define LOOP_BLOCKED_END\ - for (; i < n; i++) +/**begin repeat + * #TYPE = CFLOAT, CDOUBLE# + * #type= npy_float, npy_double# + * #esize = 8, 16# + */ -/* - * Dispatcher functions - * decide whether the operation can be vectorized and run it - * if it was run returns true and false if nothing was done +/**begin repeat1 + * #func = square, absolute, conjugate# + * #outsize = 1, 2, 1# + * #max_stride = 2, 8, 8# */ +#if defined HAVE_ATTRIBUTE_TARGET_AVX512F_WITH_INTRINSICS && defined NPY_HAVE_SSE2_INTRINSICS +static NPY_INLINE NPY_GCC_TARGET_AVX512F void +AVX512F_@func@_@TYPE@(@type@*, @type@*, const npy_intp n, const npy_intp stride); +#endif + +static NPY_INLINE int +run_unary_avx512f_@func@_@TYPE@(char **args, const npy_intp *dimensions, const npy_intp *steps) +{ +#if defined HAVE_ATTRIBUTE_TARGET_AVX512F_WITH_INTRINSICS && defined NPY_HAVE_SSE2_INTRINSICS + if ((IS_OUTPUT_BLOCKABLE_UNARY(@esize@, (npy_uint)(@esize@/@outsize@), 64)) && (labs(steps[0]) < 2*@max_stride@*@esize@)) { + AVX512F_@func@_@TYPE@((@type@*)args[1], (@type@*)args[0], dimensions[0], steps[0]); + return 1; + } + else + return 0; +#endif + return 0; +} + +/**end repeat1**/ +/**end repeat**/ + /* ***************************************************************************** ** FLOAT DISPATCHERS @@ -122,85 +89,140 @@ abs_ptrdiff(char *a, char *b) */ /**begin repeat - * Float types - * #type = npy_float, npy_double, npy_longdouble# - * #TYPE = FLOAT, DOUBLE, LONGDOUBLE# - * #vector = 1, 1, 0# + * #type = npy_float, npy_double, npy_longdouble# + * #TYPE = FLOAT, DOUBLE, LONGDOUBLE# + * #EXISTS = 1, 1, 0# */ /**begin repeat1 - * #func = sqrt, absolute, negative, minimum, maximum# - * #check = IS_BLOCKABLE_UNARY*3, IS_BLOCKABLE_REDUCE*2 # - * #name = unary*3, unary_reduce*2# - * #minmax = 0*3, 1*2# + * #func = maximum, minimum# */ -#if @vector@ && defined NPY_HAVE_SSE2_INTRINSICS +#if defined HAVE_ATTRIBUTE_TARGET_AVX512F_WITH_INTRINSICS && defined NPY_HAVE_SSE2_INTRINSICS && @EXISTS@ +static NPY_INLINE NPY_GCC_TARGET_AVX512F void +AVX512F_@func@_@TYPE@(char **args, npy_intp const *dimensions, npy_intp const *steps); +#endif -/* prototypes */ -static void -sse2_@func@_@TYPE@(@type@ *, @type@ *, const npy_intp n); +static NPY_INLINE int +run_binary_avx512f_@func@_@TYPE@(char **args, npy_intp const *dimensions, npy_intp const *steps) +{ +#if defined HAVE_ATTRIBUTE_TARGET_AVX512F_WITH_INTRINSICS && defined NPY_HAVE_SSE2_INTRINSICS && @EXISTS@ + if (IS_BINARY_SMALL_STEPS_AND_NOMEMOVERLAP) { + AVX512F_@func@_@TYPE@(args, dimensions, steps); + return 1; + } + else + return 0; +#endif + return 0; +} +/**end repeat1**/ + +/**end repeat**/ + +/**begin repeat + * #type = npy_float, npy_double, npy_longdouble# + * #TYPE = FLOAT, DOUBLE, LONGDOUBLE# + * #EXISTS = 1, 1, 0# + */ +/**begin repeat1 + * #func = isnan, isfinite, isinf, signbit# + */ + +#if defined HAVE_ATTRIBUTE_TARGET_AVX512_SKX_WITH_INTRINSICS && defined NPY_HAVE_SSE2_INTRINSICS && @EXISTS@ +static NPY_INLINE NPY_GCC_TARGET_AVX512_SKX void +AVX512_SKX_@func@_@TYPE@(npy_bool*, @type@*, const npy_intp n, const npy_intp stride); #endif static NPY_INLINE int -run_@name@_simd_@func@_@TYPE@(char **args, npy_intp *dimensions, npy_intp *steps) +run_@func@_avx512_skx_@TYPE@(char **args, npy_intp const *dimensions, npy_intp const *steps) { -#if @minmax@ && (defined NO_FLOATING_POINT_SUPPORT) - return 0; -#else -#if @vector@ && defined NPY_HAVE_SSE2_INTRINSICS - if (@check@(sizeof(@type@), 16)) { - sse2_@func@_@TYPE@((@type@*)args[1], (@type@*)args[0], dimensions[0]); +#if defined HAVE_ATTRIBUTE_TARGET_AVX512_SKX_WITH_INTRINSICS && defined NPY_HAVE_SSE2_INTRINSICS && @EXISTS@ + if (IS_OUTPUT_BLOCKABLE_UNARY(sizeof(@type@), sizeof(npy_bool), 64)) { + AVX512_SKX_@func@_@TYPE@((npy_bool*)args[1], (@type@*)args[0], dimensions[0], steps[0]); return 1; } + else { + return 0; + } #endif return 0; +} + + +/**end repeat1**/ +/**end repeat**/ + +/**begin repeat + * #ISA = FMA, AVX512F# + * #isa = fma, avx512f# + * #CHK = HAVE_ATTRIBUTE_TARGET_AVX2_WITH_INTRINSICS, HAVE_ATTRIBUTE_TARGET_AVX512F_WITH_INTRINSICS# + * #REGISTER_SIZE = 32, 64# + */ + +/* prototypes */ + +/**begin repeat1 + * #type = npy_float, npy_double# + * #TYPE = FLOAT, DOUBLE# + */ + +/**begin repeat2 + * #func = rint, floor, trunc# + */ + +#if defined @CHK@ && defined NPY_HAVE_SSE2_INTRINSICS +static NPY_INLINE NPY_GCC_TARGET_@ISA@ void +@ISA@_@func@_@TYPE@(@type@ *, @type@ *, const npy_intp n, const npy_intp stride); +#endif + +static NPY_INLINE int +run_unary_@isa@_@func@_@TYPE@(char **args, npy_intp const *dimensions, npy_intp const *steps) +{ +#if defined @CHK@ && defined NPY_HAVE_SSE2_INTRINSICS + if (IS_OUTPUT_BLOCKABLE_UNARY(sizeof(@type@), sizeof(@type@), @REGISTER_SIZE@)) { + @ISA@_@func@_@TYPE@((@type@*)args[1], (@type@*)args[0], dimensions[0], steps[0]); + return 1; + } + else + return 0; #endif + return 0; } +/**end repeat2**/ /**end repeat1**/ +/**end repeat**/ + +/**begin repeat + * Float types + * #type = npy_float, npy_double, npy_longdouble# + * #TYPE = FLOAT, DOUBLE, LONGDOUBLE# + * #vector = 1, 1, 0# + * #VECTOR = NPY_SIMD, NPY_SIMD_F64, 0 # + */ /**begin repeat1 - * Arithmetic - * # kind = add, subtract, multiply, divide# + * #func = negative, minimum, maximum# + * #check = IS_BLOCKABLE_UNARY, IS_BLOCKABLE_REDUCE*2 # + * #name = unary, unary_reduce*2# */ #if @vector@ && defined NPY_HAVE_SSE2_INTRINSICS /* prototypes */ static void -sse2_binary_@kind@_@TYPE@(@type@ * op, @type@ * ip1, @type@ * ip2, - npy_intp n); -static void -sse2_binary_scalar1_@kind@_@TYPE@(@type@ * op, @type@ * ip1, @type@ * ip2, - npy_intp n); -static void -sse2_binary_scalar2_@kind@_@TYPE@(@type@ * op, @type@ * ip1, @type@ * ip2, - npy_intp n); +sse2_@func@_@TYPE@(@type@ *, @type@ *, const npy_intp n); #endif static NPY_INLINE int -run_binary_simd_@kind@_@TYPE@(char **args, npy_intp *dimensions, npy_intp *steps) +run_@name@_simd_@func@_@TYPE@(char **args, npy_intp const *dimensions, npy_intp const *steps) { #if @vector@ && defined NPY_HAVE_SSE2_INTRINSICS - @type@ * ip1 = (@type@ *)args[0]; - @type@ * ip2 = (@type@ *)args[1]; - @type@ * op = (@type@ *)args[2]; - npy_intp n = dimensions[0]; - /* argument one scalar */ - if (IS_BLOCKABLE_BINARY_SCALAR1(sizeof(@type@), 16)) { - sse2_binary_scalar1_@kind@_@TYPE@(op, ip1, ip2, n); - return 1; - } - /* argument two scalar */ - else if (IS_BLOCKABLE_BINARY_SCALAR2(sizeof(@type@), 16)) { - sse2_binary_scalar2_@kind@_@TYPE@(op, ip1, ip2, n); - return 1; - } - else if (IS_BLOCKABLE_BINARY(sizeof(@type@), 16)) { - sse2_binary_@kind@_@TYPE@(op, ip1, ip2, n); + if (@check@(sizeof(@type@), VECTOR_SIZE_BYTES)) { + sse2_@func@_@TYPE@((@type@*)args[1], (@type@*)args[0], dimensions[0]); return 1; } #endif @@ -231,7 +253,7 @@ sse2_binary_scalar2_@kind@_@TYPE@(npy_bool * op, @type@ * ip1, @type@ * ip2, #endif static NPY_INLINE int -run_binary_simd_@kind@_@TYPE@(char **args, npy_intp *dimensions, npy_intp *steps) +run_binary_simd_@kind@_@TYPE@(char **args, npy_intp const *dimensions, npy_intp const *steps) { #if @vector@ && @simd@ && defined NPY_HAVE_SSE2_INTRINSICS @type@ * ip1 = (@type@ *)args[0]; @@ -239,16 +261,16 @@ run_binary_simd_@kind@_@TYPE@(char **args, npy_intp *dimensions, npy_intp *steps npy_bool * op = (npy_bool *)args[2]; npy_intp n = dimensions[0]; /* argument one scalar */ - if (IS_BLOCKABLE_BINARY_SCALAR1_BOOL(sizeof(@type@), 16)) { + if (IS_BLOCKABLE_BINARY_SCALAR1_BOOL(sizeof(@type@), VECTOR_SIZE_BYTES)) { sse2_binary_scalar1_@kind@_@TYPE@(op, ip1, ip2, n); return 1; } /* argument two scalar */ - else if (IS_BLOCKABLE_BINARY_SCALAR2_BOOL(sizeof(@type@), 16)) { + else if (IS_BLOCKABLE_BINARY_SCALAR2_BOOL(sizeof(@type@), VECTOR_SIZE_BYTES)) { sse2_binary_scalar2_@kind@_@TYPE@(op, ip1, ip2, n); return 1; } - else if (IS_BLOCKABLE_BINARY_BOOL(sizeof(@type@), 16)) { + else if (IS_BLOCKABLE_BINARY_BOOL(sizeof(@type@), VECTOR_SIZE_BYTES)) { sse2_binary_@kind@_@TYPE@(op, ip1, ip2, n); return 1; } @@ -270,7 +292,7 @@ sse2_@kind@_@TYPE@(npy_bool * op, @type@ * ip1, npy_intp n); #endif static NPY_INLINE int -run_@kind@_simd_@TYPE@(char **args, npy_intp *dimensions, npy_intp *steps) +run_@kind@_simd_@TYPE@(char **args, npy_intp const *dimensions, npy_intp const *steps) { #if @vector@ && defined NPY_HAVE_SSE2_INTRINSICS if (steps[0] == sizeof(@type@) && steps[1] == 1 && @@ -306,10 +328,11 @@ sse2_reduce_@kind@_BOOL(npy_bool * op, npy_bool * ip, npy_intp n); #endif static NPY_INLINE int -run_binary_simd_@kind@_BOOL(char **args, npy_intp *dimensions, npy_intp *steps) +run_binary_simd_@kind@_BOOL(char **args, npy_intp const *dimensions, npy_intp const *steps) { #if defined NPY_HAVE_SSE2_INTRINSICS - if (sizeof(npy_bool) == 1 && IS_BLOCKABLE_BINARY(sizeof(npy_bool), 16)) { + if (sizeof(npy_bool) == 1 && + IS_BLOCKABLE_BINARY(sizeof(npy_bool), VECTOR_SIZE_BYTES)) { sse2_binary_@kind@_BOOL((npy_bool*)args[2], (npy_bool*)args[0], (npy_bool*)args[1], dimensions[0]); return 1; @@ -320,10 +343,11 @@ run_binary_simd_@kind@_BOOL(char **args, npy_intp *dimensions, npy_intp *steps) static NPY_INLINE int -run_reduce_simd_@kind@_BOOL(char **args, npy_intp *dimensions, npy_intp *steps) +run_reduce_simd_@kind@_BOOL(char **args, npy_intp const *dimensions, npy_intp const *steps) { #if defined NPY_HAVE_SSE2_INTRINSICS - if (sizeof(npy_bool) == 1 && IS_BLOCKABLE_REDUCE(sizeof(npy_bool), 16)) { + if (sizeof(npy_bool) == 1 && + IS_BLOCKABLE_REDUCE(sizeof(npy_bool), VECTOR_SIZE_BYTES)) { sse2_reduce_@kind@_BOOL((npy_bool*)args[0], (npy_bool*)args[1], dimensions[0]); return 1; @@ -344,10 +368,11 @@ sse2_@kind@_BOOL(npy_bool *, npy_bool *, const npy_intp n); #endif static NPY_INLINE int -run_unary_simd_@kind@_BOOL(char **args, npy_intp *dimensions, npy_intp *steps) +run_unary_simd_@kind@_BOOL(char **args, npy_intp const *dimensions, npy_intp const *steps) { #if defined NPY_HAVE_SSE2_INTRINSICS - if (sizeof(npy_bool) == 1 && IS_BLOCKABLE_UNARY(sizeof(npy_bool), 16)) { + if (sizeof(npy_bool) == 1 && + IS_BLOCKABLE_UNARY(sizeof(npy_bool), VECTOR_SIZE_BYTES)) { sse2_@kind@_BOOL((npy_bool*)args[1], (npy_bool*)args[0], dimensions[0]); return 1; } @@ -373,7 +398,7 @@ run_unary_simd_@kind@_BOOL(char **args, npy_intp *dimensions, npy_intp *steps) * # VOP = min, max# */ -static NPY_INLINE npy_float sse2_horizontal_@VOP@___m128(__m128 v) +NPY_FINLINE npy_float sse2_horizontal_@VOP@___m128(__m128 v) { npy_float r; __m128 tmp = _mm_movehl_ps(v, v); /* c d ... */ @@ -383,14 +408,13 @@ static NPY_INLINE npy_float sse2_horizontal_@VOP@___m128(__m128 v) return r; } -static NPY_INLINE npy_double sse2_horizontal_@VOP@___m128d(__m128d v) +NPY_FINLINE npy_double sse2_horizontal_@VOP@___m128d(__m128d v) { npy_double r; __m128d tmp = _mm_unpackhi_pd(v, v); /* b b */ _mm_store_sd(&r, _mm_@VOP@_pd(tmp, v)); /* m(ab) m(bb) */ return r; } - /**end repeat**/ /**begin repeat @@ -410,325 +434,12 @@ static NPY_INLINE npy_double sse2_horizontal_@VOP@___m128d(__m128d v) * #double = 0, 1# * #cast = _mm_castps_si128, _mm_castpd_si128# */ - - -/**begin repeat1 -* Arithmetic -* # kind = add, subtract, multiply, divide# -* # OP = +, -, *, /# -* # VOP = add, sub, mul, div# -*/ - -static void -sse2_binary_@kind@_@TYPE@(@type@ * op, @type@ * ip1, @type@ * ip2, npy_intp n) -{ -#ifdef __AVX512F__ - LOOP_BLOCK_ALIGN_VAR(op, @type@, 64) - op[i] = ip1[i] @OP@ ip2[i]; - /* lots of specializations, to squeeze out max performance */ - if (npy_is_aligned(&ip1[i], 64) && npy_is_aligned(&ip2[i], 64)) { - if (ip1 == ip2) { - LOOP_BLOCKED(@type@, 64) { - @vtype512@ a = @vpre512@_load_@vsuf@(&ip1[i]); - @vtype512@ c = @vpre512@_@VOP@_@vsuf@(a, a); - @vpre512@_store_@vsuf@(&op[i], c); - } - } - else { - LOOP_BLOCKED(@type@, 64) { - @vtype512@ a = @vpre512@_load_@vsuf@(&ip1[i]); - @vtype512@ b = @vpre512@_load_@vsuf@(&ip2[i]); - @vtype512@ c = @vpre512@_@VOP@_@vsuf@(a, b); - @vpre512@_store_@vsuf@(&op[i], c); - } - } - } - else if (npy_is_aligned(&ip1[i], 64)) { - LOOP_BLOCKED(@type@, 64) { - @vtype512@ a = @vpre512@_load_@vsuf@(&ip1[i]); - @vtype512@ b = @vpre512@_loadu_@vsuf@(&ip2[i]); - @vtype512@ c = @vpre512@_@VOP@_@vsuf@(a, b); - @vpre512@_store_@vsuf@(&op[i], c); - } - } - else if (npy_is_aligned(&ip2[i], 64)) { - LOOP_BLOCKED(@type@, 64) { - @vtype512@ a = @vpre512@_loadu_@vsuf@(&ip1[i]); - @vtype512@ b = @vpre512@_load_@vsuf@(&ip2[i]); - @vtype512@ c = @vpre512@_@VOP@_@vsuf@(a, b); - @vpre512@_store_@vsuf@(&op[i], c); - } - } - else { - if (ip1 == ip2) { - LOOP_BLOCKED(@type@, 64) { - @vtype512@ a = @vpre512@_loadu_@vsuf@(&ip1[i]); - @vtype512@ c = @vpre512@_@VOP@_@vsuf@(a, a); - @vpre512@_store_@vsuf@(&op[i], c); - } - } - else { - LOOP_BLOCKED(@type@, 64) { - @vtype512@ a = @vpre512@_loadu_@vsuf@(&ip1[i]); - @vtype512@ b = @vpre512@_loadu_@vsuf@(&ip2[i]); - @vtype512@ c = @vpre512@_@VOP@_@vsuf@(a, b); - @vpre512@_store_@vsuf@(&op[i], c); - } - } - } -#elif __AVX2__ - LOOP_BLOCK_ALIGN_VAR(op, @type@, 32) - op[i] = ip1[i] @OP@ ip2[i]; - /* lots of specializations, to squeeze out max performance */ - if (npy_is_aligned(&ip1[i], 32) && npy_is_aligned(&ip2[i], 32)) { - if (ip1 == ip2) { - LOOP_BLOCKED(@type@, 32) { - @vtype256@ a = @vpre256@_load_@vsuf@(&ip1[i]); - @vtype256@ c = @vpre256@_@VOP@_@vsuf@(a, a); - @vpre256@_store_@vsuf@(&op[i], c); - } - } - else { - LOOP_BLOCKED(@type@, 32) { - @vtype256@ a = @vpre256@_load_@vsuf@(&ip1[i]); - @vtype256@ b = @vpre256@_load_@vsuf@(&ip2[i]); - @vtype256@ c = @vpre256@_@VOP@_@vsuf@(a, b); - @vpre256@_store_@vsuf@(&op[i], c); - } - } - } - else if (npy_is_aligned(&ip1[i], 32)) { - LOOP_BLOCKED(@type@, 32) { - @vtype256@ a = @vpre256@_load_@vsuf@(&ip1[i]); - @vtype256@ b = @vpre256@_loadu_@vsuf@(&ip2[i]); - @vtype256@ c = @vpre256@_@VOP@_@vsuf@(a, b); - @vpre256@_store_@vsuf@(&op[i], c); - } - } - else if (npy_is_aligned(&ip2[i], 32)) { - LOOP_BLOCKED(@type@, 32) { - @vtype256@ a = @vpre256@_loadu_@vsuf@(&ip1[i]); - @vtype256@ b = @vpre256@_load_@vsuf@(&ip2[i]); - @vtype256@ c = @vpre256@_@VOP@_@vsuf@(a, b); - @vpre256@_store_@vsuf@(&op[i], c); - } - } - else { - if (ip1 == ip2) { - LOOP_BLOCKED(@type@, 32) { - @vtype256@ a = @vpre256@_loadu_@vsuf@(&ip1[i]); - @vtype256@ c = @vpre256@_@VOP@_@vsuf@(a, a); - @vpre256@_store_@vsuf@(&op[i], c); - } - } - else { - LOOP_BLOCKED(@type@, 32) { - @vtype256@ a = @vpre256@_loadu_@vsuf@(&ip1[i]); - @vtype256@ b = @vpre256@_loadu_@vsuf@(&ip2[i]); - @vtype256@ c = @vpre256@_@VOP@_@vsuf@(a, b); - @vpre256@_store_@vsuf@(&op[i], c); - } - } - } -#else - LOOP_BLOCK_ALIGN_VAR(op, @type@, 16) - op[i] = ip1[i] @OP@ ip2[i]; - /* lots of specializations, to squeeze out max performance */ - if (npy_is_aligned(&ip1[i], 16) && npy_is_aligned(&ip2[i], 16)) { - if (ip1 == ip2) { - LOOP_BLOCKED(@type@, 16) { - @vtype@ a = @vpre@_load_@vsuf@(&ip1[i]); - @vtype@ c = @vpre@_@VOP@_@vsuf@(a, a); - @vpre@_store_@vsuf@(&op[i], c); - } - } - else { - LOOP_BLOCKED(@type@, 16) { - @vtype@ a = @vpre@_load_@vsuf@(&ip1[i]); - @vtype@ b = @vpre@_load_@vsuf@(&ip2[i]); - @vtype@ c = @vpre@_@VOP@_@vsuf@(a, b); - @vpre@_store_@vsuf@(&op[i], c); - } - } - } - else if (npy_is_aligned(&ip1[i], 16)) { - LOOP_BLOCKED(@type@, 16) { - @vtype@ a = @vpre@_load_@vsuf@(&ip1[i]); - @vtype@ b = @vpre@_loadu_@vsuf@(&ip2[i]); - @vtype@ c = @vpre@_@VOP@_@vsuf@(a, b); - @vpre@_store_@vsuf@(&op[i], c); - } - } - else if (npy_is_aligned(&ip2[i], 16)) { - LOOP_BLOCKED(@type@, 16) { - @vtype@ a = @vpre@_loadu_@vsuf@(&ip1[i]); - @vtype@ b = @vpre@_load_@vsuf@(&ip2[i]); - @vtype@ c = @vpre@_@VOP@_@vsuf@(a, b); - @vpre@_store_@vsuf@(&op[i], c); - } - } - else { - if (ip1 == ip2) { - LOOP_BLOCKED(@type@, 16) { - @vtype@ a = @vpre@_loadu_@vsuf@(&ip1[i]); - @vtype@ c = @vpre@_@VOP@_@vsuf@(a, a); - @vpre@_store_@vsuf@(&op[i], c); - } - } - else { - LOOP_BLOCKED(@type@, 16) { - @vtype@ a = @vpre@_loadu_@vsuf@(&ip1[i]); - @vtype@ b = @vpre@_loadu_@vsuf@(&ip2[i]); - @vtype@ c = @vpre@_@VOP@_@vsuf@(a, b); - @vpre@_store_@vsuf@(&op[i], c); - } - } - } -#endif - LOOP_BLOCKED_END { - op[i] = ip1[i] @OP@ ip2[i]; - } -} - - -static void -sse2_binary_scalar1_@kind@_@TYPE@(@type@ * op, @type@ * ip1, @type@ * ip2, npy_intp n) -{ -#ifdef __AVX512F__ - const @vtype512@ a = @vpre512@_set1_@vsuf@(ip1[0]); - LOOP_BLOCK_ALIGN_VAR(op, @type@, 64) - op[i] = ip1[0] @OP@ ip2[i]; - if (npy_is_aligned(&ip2[i], 64)) { - LOOP_BLOCKED(@type@, 64) { - @vtype512@ b = @vpre512@_load_@vsuf@(&ip2[i]); - @vtype512@ c = @vpre512@_@VOP@_@vsuf@(a, b); - @vpre512@_store_@vsuf@(&op[i], c); - } - } - else { - LOOP_BLOCKED(@type@, 64) { - @vtype512@ b = @vpre512@_loadu_@vsuf@(&ip2[i]); - @vtype512@ c = @vpre512@_@VOP@_@vsuf@(a, b); - @vpre512@_store_@vsuf@(&op[i], c); - } - } - - -#elif __AVX2__ - const @vtype256@ a = @vpre256@_set1_@vsuf@(ip1[0]); - LOOP_BLOCK_ALIGN_VAR(op, @type@, 32) - op[i] = ip1[0] @OP@ ip2[i]; - if (npy_is_aligned(&ip2[i], 32)) { - LOOP_BLOCKED(@type@, 32) { - @vtype256@ b = @vpre256@_load_@vsuf@(&ip2[i]); - @vtype256@ c = @vpre256@_@VOP@_@vsuf@(a, b); - @vpre256@_store_@vsuf@(&op[i], c); - } - } - else { - LOOP_BLOCKED(@type@, 32) { - @vtype256@ b = @vpre256@_loadu_@vsuf@(&ip2[i]); - @vtype256@ c = @vpre256@_@VOP@_@vsuf@(a, b); - @vpre256@_store_@vsuf@(&op[i], c); - } - } -#else - const @vtype@ a = @vpre@_set1_@vsuf@(ip1[0]); - LOOP_BLOCK_ALIGN_VAR(op, @type@, 16) - op[i] = ip1[0] @OP@ ip2[i]; - if (npy_is_aligned(&ip2[i], 16)) { - LOOP_BLOCKED(@type@, 16) { - @vtype@ b = @vpre@_load_@vsuf@(&ip2[i]); - @vtype@ c = @vpre@_@VOP@_@vsuf@(a, b); - @vpre@_store_@vsuf@(&op[i], c); - } - } - else { - LOOP_BLOCKED(@type@, 16) { - @vtype@ b = @vpre@_loadu_@vsuf@(&ip2[i]); - @vtype@ c = @vpre@_@VOP@_@vsuf@(a, b); - @vpre@_store_@vsuf@(&op[i], c); - } - } -#endif - LOOP_BLOCKED_END { - op[i] = ip1[0] @OP@ ip2[i]; - } -} - - -static void -sse2_binary_scalar2_@kind@_@TYPE@(@type@ * op, @type@ * ip1, @type@ * ip2, npy_intp n) -{ -#ifdef __AVX512F__ - const @vtype512@ b = @vpre512@_set1_@vsuf@(ip2[0]); - LOOP_BLOCK_ALIGN_VAR(op, @type@, 64) - op[i] = ip1[i] @OP@ ip2[0]; - if (npy_is_aligned(&ip1[i], 64)) { - LOOP_BLOCKED(@type@, 64) { - @vtype512@ a = @vpre512@_load_@vsuf@(&ip1[i]); - @vtype512@ c = @vpre512@_@VOP@_@vsuf@(a, b); - @vpre512@_store_@vsuf@(&op[i], c); - } - } - else { - LOOP_BLOCKED(@type@, 64) { - @vtype512@ a = @vpre512@_loadu_@vsuf@(&ip1[i]); - @vtype512@ c = @vpre512@_@VOP@_@vsuf@(a, b); - @vpre512@_store_@vsuf@(&op[i], c); - } - } - -#elif __AVX2__ - const @vtype256@ b = @vpre256@_set1_@vsuf@(ip2[0]); - LOOP_BLOCK_ALIGN_VAR(op, @type@, 32) - op[i] = ip1[i] @OP@ ip2[0]; - if (npy_is_aligned(&ip1[i], 32)) { - LOOP_BLOCKED(@type@, 32) { - @vtype256@ a = @vpre256@_load_@vsuf@(&ip1[i]); - @vtype256@ c = @vpre256@_@VOP@_@vsuf@(a, b); - @vpre256@_store_@vsuf@(&op[i], c); - } - } - else { - LOOP_BLOCKED(@type@, 32) { - @vtype256@ a = @vpre256@_loadu_@vsuf@(&ip1[i]); - @vtype256@ c = @vpre256@_@VOP@_@vsuf@(a, b); - @vpre256@_store_@vsuf@(&op[i], c); - } - } -#else - const @vtype@ b = @vpre@_set1_@vsuf@(ip2[0]); - LOOP_BLOCK_ALIGN_VAR(op, @type@, 16) - op[i] = ip1[i] @OP@ ip2[0]; - if (npy_is_aligned(&ip1[i], 16)) { - LOOP_BLOCKED(@type@, 16) { - @vtype@ a = @vpre@_load_@vsuf@(&ip1[i]); - @vtype@ c = @vpre@_@VOP@_@vsuf@(a, b); - @vpre@_store_@vsuf@(&op[i], c); - } - } - else { - LOOP_BLOCKED(@type@, 16) { - @vtype@ a = @vpre@_loadu_@vsuf@(&ip1[i]); - @vtype@ c = @vpre@_@VOP@_@vsuf@(a, b); - @vpre@_store_@vsuf@(&op[i], c); - } - } -#endif - LOOP_BLOCKED_END { - op[i] = ip1[i] @OP@ ip2[0]; - } -} - -/**end repeat1**/ - /* * compress 4 vectors to 4/8 bytes in op with filled with 0 or 1 * the last vector is passed as a pointer as MSVC 2010 is unable to ignore the * calling convention leading to C2719 on 32 bit, see #4795 */ -static NPY_INLINE void +NPY_FINLINE void sse2_compress4_to_byte_@TYPE@(@vtype@ r1, @vtype@ r2, @vtype@ r3, @vtype@ * r4, npy_bool * op) { @@ -749,10 +460,10 @@ sse2_compress4_to_byte_@TYPE@(@vtype@ r1, @vtype@ r2, @vtype@ r3, @vtype@ * r4, static void sse2_signbit_@TYPE@(npy_bool * op, @type@ * ip1, npy_intp n) { - LOOP_BLOCK_ALIGN_VAR(ip1, @type@, 16) { + LOOP_BLOCK_ALIGN_VAR(ip1, @type@, VECTOR_SIZE_BYTES) { op[i] = npy_signbit(ip1[i]) != 0; } - LOOP_BLOCKED(@type@, 16) { + LOOP_BLOCKED(@type@, VECTOR_SIZE_BYTES) { @vtype@ a = @vpre@_load_@vsuf@(&ip1[i]); int r = @vpre@_movemask_@vsuf@(a); if (sizeof(@type@) == 8) { @@ -790,14 +501,14 @@ sse2_@kind@_@TYPE@(npy_bool * op, @type@ * ip1, npy_intp n) const @vtype@ fltmax = @vpre@_set1_@vsuf@(FLT_MAX); #endif #endif - LOOP_BLOCK_ALIGN_VAR(ip1, @type@, 16) { + LOOP_BLOCK_ALIGN_VAR(ip1, @type@, VECTOR_SIZE_BYTES) { op[i] = npy_@kind@(ip1[i]) != 0; } - LOOP_BLOCKED(@type@, 64) { - @vtype@ a = @vpre@_load_@vsuf@(&ip1[i + 0 * 16 / sizeof(@type@)]); - @vtype@ b = @vpre@_load_@vsuf@(&ip1[i + 1 * 16 / sizeof(@type@)]); - @vtype@ c = @vpre@_load_@vsuf@(&ip1[i + 2 * 16 / sizeof(@type@)]); - @vtype@ d = @vpre@_load_@vsuf@(&ip1[i + 3 * 16 / sizeof(@type@)]); + LOOP_BLOCKED(@type@, 4 * VECTOR_SIZE_BYTES) { + @vtype@ a = @vpre@_load_@vsuf@(&ip1[i + 0 * VECTOR_SIZE_BYTES / sizeof(@type@)]); + @vtype@ b = @vpre@_load_@vsuf@(&ip1[i + 1 * VECTOR_SIZE_BYTES / sizeof(@type@)]); + @vtype@ c = @vpre@_load_@vsuf@(&ip1[i + 2 * VECTOR_SIZE_BYTES / sizeof(@type@)]); + @vtype@ d = @vpre@_load_@vsuf@(&ip1[i + 3 * VECTOR_SIZE_BYTES / sizeof(@type@)]); @vtype@ r1, r2, r3, r4; #if @var@ != 0 /* isinf/isfinite */ /* fabs via masking of sign bit */ @@ -845,7 +556,7 @@ sse2_@kind@_@TYPE@(npy_bool * op, @type@ * ip1, npy_intp n) */ /* sets invalid fpu flag on QNaN for consistency with packed compare */ -static NPY_INLINE int +NPY_FINLINE int sse2_ordered_cmp_@kind@_@TYPE@(const @type@ a, const @type@ b) { @vtype@ one = @vpre@_set1_@vsuf@(1); @@ -860,18 +571,18 @@ sse2_ordered_cmp_@kind@_@TYPE@(const @type@ a, const @type@ b) static void sse2_binary_@kind@_@TYPE@(npy_bool * op, @type@ * ip1, @type@ * ip2, npy_intp n) { - LOOP_BLOCK_ALIGN_VAR(ip1, @type@, 16) { + LOOP_BLOCK_ALIGN_VAR(ip1, @type@, VECTOR_SIZE_BYTES) { op[i] = sse2_ordered_cmp_@kind@_@TYPE@(ip1[i], ip2[i]); } - LOOP_BLOCKED(@type@, 64) { - @vtype@ a1 = @vpre@_load_@vsuf@(&ip1[i + 0 * 16 / sizeof(@type@)]); - @vtype@ b1 = @vpre@_load_@vsuf@(&ip1[i + 1 * 16 / sizeof(@type@)]); - @vtype@ c1 = @vpre@_load_@vsuf@(&ip1[i + 2 * 16 / sizeof(@type@)]); - @vtype@ d1 = @vpre@_load_@vsuf@(&ip1[i + 3 * 16 / sizeof(@type@)]); - @vtype@ a2 = @vpre@_loadu_@vsuf@(&ip2[i + 0 * 16 / sizeof(@type@)]); - @vtype@ b2 = @vpre@_loadu_@vsuf@(&ip2[i + 1 * 16 / sizeof(@type@)]); - @vtype@ c2 = @vpre@_loadu_@vsuf@(&ip2[i + 2 * 16 / sizeof(@type@)]); - @vtype@ d2 = @vpre@_loadu_@vsuf@(&ip2[i + 3 * 16 / sizeof(@type@)]); + LOOP_BLOCKED(@type@, 4 * VECTOR_SIZE_BYTES) { + @vtype@ a1 = @vpre@_load_@vsuf@(&ip1[i + 0 * VECTOR_SIZE_BYTES / sizeof(@type@)]); + @vtype@ b1 = @vpre@_load_@vsuf@(&ip1[i + 1 * VECTOR_SIZE_BYTES / sizeof(@type@)]); + @vtype@ c1 = @vpre@_load_@vsuf@(&ip1[i + 2 * VECTOR_SIZE_BYTES / sizeof(@type@)]); + @vtype@ d1 = @vpre@_load_@vsuf@(&ip1[i + 3 * VECTOR_SIZE_BYTES / sizeof(@type@)]); + @vtype@ a2 = @vpre@_loadu_@vsuf@(&ip2[i + 0 * VECTOR_SIZE_BYTES / sizeof(@type@)]); + @vtype@ b2 = @vpre@_loadu_@vsuf@(&ip2[i + 1 * VECTOR_SIZE_BYTES / sizeof(@type@)]); + @vtype@ c2 = @vpre@_loadu_@vsuf@(&ip2[i + 2 * VECTOR_SIZE_BYTES / sizeof(@type@)]); + @vtype@ d2 = @vpre@_loadu_@vsuf@(&ip2[i + 3 * VECTOR_SIZE_BYTES / sizeof(@type@)]); @vtype@ r1 = @vpre@_@VOP@_@vsuf@(a1, a2); @vtype@ r2 = @vpre@_@VOP@_@vsuf@(b1, b2); @vtype@ r3 = @vpre@_@VOP@_@vsuf@(c1, c2); @@ -888,14 +599,14 @@ static void sse2_binary_scalar1_@kind@_@TYPE@(npy_bool * op, @type@ * ip1, @type@ * ip2, npy_intp n) { @vtype@ s = @vpre@_set1_@vsuf@(ip1[0]); - LOOP_BLOCK_ALIGN_VAR(ip2, @type@, 16) { + LOOP_BLOCK_ALIGN_VAR(ip2, @type@, VECTOR_SIZE_BYTES) { op[i] = sse2_ordered_cmp_@kind@_@TYPE@(ip1[0], ip2[i]); } - LOOP_BLOCKED(@type@, 64) { - @vtype@ a = @vpre@_load_@vsuf@(&ip2[i + 0 * 16 / sizeof(@type@)]); - @vtype@ b = @vpre@_load_@vsuf@(&ip2[i + 1 * 16 / sizeof(@type@)]); - @vtype@ c = @vpre@_load_@vsuf@(&ip2[i + 2 * 16 / sizeof(@type@)]); - @vtype@ d = @vpre@_load_@vsuf@(&ip2[i + 3 * 16 / sizeof(@type@)]); + LOOP_BLOCKED(@type@, 4 * VECTOR_SIZE_BYTES) { + @vtype@ a = @vpre@_load_@vsuf@(&ip2[i + 0 * VECTOR_SIZE_BYTES / sizeof(@type@)]); + @vtype@ b = @vpre@_load_@vsuf@(&ip2[i + 1 * VECTOR_SIZE_BYTES / sizeof(@type@)]); + @vtype@ c = @vpre@_load_@vsuf@(&ip2[i + 2 * VECTOR_SIZE_BYTES / sizeof(@type@)]); + @vtype@ d = @vpre@_load_@vsuf@(&ip2[i + 3 * VECTOR_SIZE_BYTES / sizeof(@type@)]); @vtype@ r1 = @vpre@_@VOP@_@vsuf@(s, a); @vtype@ r2 = @vpre@_@VOP@_@vsuf@(s, b); @vtype@ r3 = @vpre@_@VOP@_@vsuf@(s, c); @@ -912,14 +623,14 @@ static void sse2_binary_scalar2_@kind@_@TYPE@(npy_bool * op, @type@ * ip1, @type@ * ip2, npy_intp n) { @vtype@ s = @vpre@_set1_@vsuf@(ip2[0]); - LOOP_BLOCK_ALIGN_VAR(ip1, @type@, 16) { + LOOP_BLOCK_ALIGN_VAR(ip1, @type@, VECTOR_SIZE_BYTES) { op[i] = sse2_ordered_cmp_@kind@_@TYPE@(ip1[i], ip2[0]); } - LOOP_BLOCKED(@type@, 64) { - @vtype@ a = @vpre@_load_@vsuf@(&ip1[i + 0 * 16 / sizeof(@type@)]); - @vtype@ b = @vpre@_load_@vsuf@(&ip1[i + 1 * 16 / sizeof(@type@)]); - @vtype@ c = @vpre@_load_@vsuf@(&ip1[i + 2 * 16 / sizeof(@type@)]); - @vtype@ d = @vpre@_load_@vsuf@(&ip1[i + 3 * 16 / sizeof(@type@)]); + LOOP_BLOCKED(@type@, 4 * VECTOR_SIZE_BYTES) { + @vtype@ a = @vpre@_load_@vsuf@(&ip1[i + 0 * VECTOR_SIZE_BYTES / sizeof(@type@)]); + @vtype@ b = @vpre@_load_@vsuf@(&ip1[i + 1 * VECTOR_SIZE_BYTES / sizeof(@type@)]); + @vtype@ c = @vpre@_load_@vsuf@(&ip1[i + 2 * VECTOR_SIZE_BYTES / sizeof(@type@)]); + @vtype@ d = @vpre@_load_@vsuf@(&ip1[i + 3 * VECTOR_SIZE_BYTES / sizeof(@type@)]); @vtype@ r1 = @vpre@_@VOP@_@vsuf@(a, s); @vtype@ r2 = @vpre@_@VOP@_@vsuf@(b, s); @vtype@ r3 = @vpre@_@VOP@_@vsuf@(c, s); @@ -932,52 +643,9 @@ sse2_binary_scalar2_@kind@_@TYPE@(npy_bool * op, @type@ * ip1, @type@ * ip2, npy } /**end repeat1**/ -static void -sse2_sqrt_@TYPE@(@type@ * op, @type@ * ip, const npy_intp n) -{ - /* align output to 16 bytes */ - LOOP_BLOCK_ALIGN_VAR(op, @type@, 16) { - op[i] = @scalarf@(ip[i]); - } - assert(n < (16 / sizeof(@type@)) || npy_is_aligned(&op[i], 16)); - if (npy_is_aligned(&ip[i], 16)) { - LOOP_BLOCKED(@type@, 16) { - @vtype@ d = @vpre@_load_@vsuf@(&ip[i]); - @vpre@_store_@vsuf@(&op[i], @vpre@_sqrt_@vsuf@(d)); - } - } - else { - LOOP_BLOCKED(@type@, 16) { - @vtype@ d = @vpre@_loadu_@vsuf@(&ip[i]); - @vpre@_store_@vsuf@(&op[i], @vpre@_sqrt_@vsuf@(d)); - } - } - LOOP_BLOCKED_END { - op[i] = @scalarf@(ip[i]); - } -} - - -static NPY_INLINE -@type@ scalar_abs_@type@(@type@ v) -{ - /* add 0 to clear -0.0 */ - return (v > 0 ? v: -v) + 0; -} - -static NPY_INLINE -@type@ scalar_neg_@type@(@type@ v) -{ - return -v; -} -/**begin repeat1 - * #kind = absolute, negative# - * #VOP = andnot, xor# - * #scalar = scalar_abs, scalar_neg# - **/ static void -sse2_@kind@_@TYPE@(@type@ * op, @type@ * ip, const npy_intp n) +sse2_negative_@TYPE@(@type@ * op, @type@ * ip, const npy_intp n) { /* * get 0x7FFFFFFF mask (everything but signbit set) @@ -986,25 +654,26 @@ sse2_@kind@_@TYPE@(@type@ * op, @type@ * ip, const npy_intp n) */ const @vtype@ mask = @vpre@_set1_@vsuf@(-0.@c@); - /* align output to 16 bytes */ - LOOP_BLOCK_ALIGN_VAR(op, @type@, 16) { - op[i] = @scalar@_@type@(ip[i]); + /* align output to VECTOR_SIZE_BYTES bytes */ + LOOP_BLOCK_ALIGN_VAR(op, @type@, VECTOR_SIZE_BYTES) { + op[i] = -ip[i]; } - assert(n < (16 / sizeof(@type@)) || npy_is_aligned(&op[i], 16)); - if (npy_is_aligned(&ip[i], 16)) { - LOOP_BLOCKED(@type@, 16) { + assert((npy_uintp)n < (VECTOR_SIZE_BYTES / sizeof(@type@)) || + npy_is_aligned(&op[i], VECTOR_SIZE_BYTES)); + if (npy_is_aligned(&ip[i], VECTOR_SIZE_BYTES)) { + LOOP_BLOCKED(@type@, VECTOR_SIZE_BYTES) { @vtype@ a = @vpre@_load_@vsuf@(&ip[i]); - @vpre@_store_@vsuf@(&op[i], @vpre@_@VOP@_@vsuf@(mask, a)); + @vpre@_store_@vsuf@(&op[i], @vpre@_xor_@vsuf@(mask, a)); } } else { - LOOP_BLOCKED(@type@, 16) { + LOOP_BLOCKED(@type@, VECTOR_SIZE_BYTES) { @vtype@ a = @vpre@_loadu_@vsuf@(&ip[i]); - @vpre@_store_@vsuf@(&op[i], @vpre@_@VOP@_@vsuf@(mask, a)); + @vpre@_store_@vsuf@(&op[i], @vpre@_xor_@vsuf@(mask, a)); } } LOOP_BLOCKED_END { - op[i] = @scalar@_@type@(ip[i]); + op[i] = -ip[i]; } } /**end repeat1**/ @@ -1019,11 +688,12 @@ sse2_@kind@_@TYPE@(@type@ * op, @type@ * ip, const npy_intp n) static void sse2_@kind@_@TYPE@(@type@ * ip, @type@ * op, const npy_intp n) { - const npy_intp stride = 16 / (npy_intp)sizeof(@type@); - LOOP_BLOCK_ALIGN_VAR(ip, @type@, 16) { + const npy_intp stride = VECTOR_SIZE_BYTES / (npy_intp)sizeof(@type@); + LOOP_BLOCK_ALIGN_VAR(ip, @type@, VECTOR_SIZE_BYTES) { + /* Order of operations important for MSVC 2015 */ *op = (*op @OP@ ip[i] || npy_isnan(*op)) ? *op : ip[i]; } - assert(n < (stride) || npy_is_aligned(&ip[i], 16)); + assert(n < stride || npy_is_aligned(&ip[i], VECTOR_SIZE_BYTES)); if (i + 3 * stride <= n) { /* load the first elements */ @vtype@ c1 = @vpre@_load_@vsuf@((@type@*)&ip[i]); @@ -1032,7 +702,7 @@ sse2_@kind@_@TYPE@(@type@ * ip, @type@ * op, const npy_intp n) /* minps/minpd will set invalid flag if nan is encountered */ npy_clear_floatstatus_barrier((char*)&c1); - LOOP_BLOCKED(@type@, 32) { + LOOP_BLOCKED(@type@, 2 * VECTOR_SIZE_BYTES) { @vtype@ v1 = @vpre@_load_@vsuf@((@type@*)&ip[i]); @vtype@ v2 = @vpre@_load_@vsuf@((@type@*)&ip[i + stride]); c1 = @vpre@_@VOP@_@vsuf@(c1, v1); @@ -1045,39 +715,887 @@ sse2_@kind@_@TYPE@(@type@ * ip, @type@ * op, const npy_intp n) } else { @type@ tmp = sse2_horizontal_@VOP@_@vtype@(c1); + /* Order of operations important for MSVC 2015 */ *op = (*op @OP@ tmp || npy_isnan(*op)) ? *op : tmp; } } LOOP_BLOCKED_END { + /* Order of operations important for MSVC 2015 */ *op = (*op @OP@ ip[i] || npy_isnan(*op)) ? *op : ip[i]; } - if (npy_isnan(*op)) { - npy_set_floatstatus_invalid(); - } + npy_clear_floatstatus_barrier((char*)op); } /**end repeat1**/ /**end repeat**/ -/* - ***************************************************************************** - ** BOOL LOOPS - ***************************************************************************** - */ +/* bunch of helper functions used in ISA_exp/log_FLOAT*/ -/**begin repeat - * # kind = logical_or, logical_and# - * # and = 0, 1# - * # op = ||, &&# - * # sc = !=, ==# - * # vpre = _mm*2# - * # vsuf = si128*2# - * # vtype = __m128i*2# - * # type = npy_bool*2# - * # vload = _mm_load_si128*2# - * # vloadu = _mm_loadu_si128*2# - * # vstore = _mm_store_si128*2# - */ +#if defined HAVE_ATTRIBUTE_TARGET_AVX2_WITH_INTRINSICS +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA __m256 +fma_get_full_load_mask_ps(void) +{ + return _mm256_set1_ps(-1.0); +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA __m256i +fma_get_full_load_mask_pd(void) +{ + return _mm256_castpd_si256(_mm256_set1_pd(-1.0)); +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA __m256 +fma_get_partial_load_mask_ps(const npy_int num_elem, const npy_int num_lanes) +{ + float maskint[16] = {-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0, + 1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0}; + float* addr = maskint + num_lanes - num_elem; + return _mm256_loadu_ps(addr); +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA __m256i +fma_get_partial_load_mask_pd(const npy_int num_elem, const npy_int num_lanes) +{ + npy_int maskint[16] = {-1,-1,-1,-1,-1,-1,-1,-1,1,1,1,1,1,1,1,1}; + npy_int* addr = maskint + 2*num_lanes - 2*num_elem; + return _mm256_loadu_si256((__m256i*) addr); +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA __m256 +fma_masked_gather_ps(__m256 src, + npy_float* addr, + __m256i vindex, + __m256 mask) +{ + return _mm256_mask_i32gather_ps(src, addr, vindex, mask, 4); +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA __m256d +fma_masked_gather_pd(__m256d src, + npy_double* addr, + __m128i vindex, + __m256d mask) +{ + return _mm256_mask_i32gather_pd(src, addr, vindex, mask, 8); +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA __m256 +fma_masked_load_ps(__m256 mask, npy_float* addr) +{ + return _mm256_maskload_ps(addr, _mm256_cvtps_epi32(mask)); +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA __m256d +fma_masked_load_pd(__m256i mask, npy_double* addr) +{ + return _mm256_maskload_pd(addr, mask); +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA __m256 +fma_set_masked_lanes_ps(__m256 x, __m256 val, __m256 mask) +{ + return _mm256_blendv_ps(x, val, mask); +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA __m256d +fma_set_masked_lanes_pd(__m256d x, __m256d val, __m256d mask) +{ + return _mm256_blendv_pd(x, val, mask); +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA __m256 +fma_blend(__m256 x, __m256 y, __m256 ymask) +{ + return _mm256_blendv_ps(x, y, ymask); +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA __m256 +fma_invert_mask_ps(__m256 ymask) +{ + return _mm256_andnot_ps(ymask, _mm256_set1_ps(-1.0)); +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA __m256i +fma_invert_mask_pd(__m256i ymask) +{ + return _mm256_andnot_si256(ymask, _mm256_set1_epi32(0xFFFFFFFF)); +} + +/**begin repeat + * #vsub = ps, pd# + * #vtype = __m256, __m256d# + */ +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA @vtype@ +fma_abs_@vsub@(@vtype@ x) +{ + return _mm256_andnot_@vsub@(_mm256_set1_@vsub@(-0.0), x); +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA @vtype@ +fma_reciprocal_@vsub@(@vtype@ x) +{ + return _mm256_div_@vsub@(_mm256_set1_@vsub@(1.0f), x); +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA @vtype@ +fma_rint_@vsub@(@vtype@ x) +{ + return _mm256_round_@vsub@(x, _MM_FROUND_TO_NEAREST_INT); +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA @vtype@ +fma_floor_@vsub@(@vtype@ x) +{ + return _mm256_round_@vsub@(x, _MM_FROUND_TO_NEG_INF); +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_FMA @vtype@ +fma_trunc_@vsub@(@vtype@ x) +{ + return _mm256_round_@vsub@(x, _MM_FROUND_TO_ZERO); +} +/**end repeat**/ +#endif + +#if defined HAVE_ATTRIBUTE_TARGET_AVX512F_WITH_INTRINSICS +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F __mmask16 +avx512_get_full_load_mask_ps(void) +{ + return 0xFFFF; +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F __mmask8 +avx512_get_full_load_mask_pd(void) +{ + return 0xFF; +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F __mmask16 +avx512_get_partial_load_mask_ps(const npy_int num_elem, const npy_int total_elem) +{ + return (0x0001 << num_elem) - 0x0001; +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F __mmask8 +avx512_get_partial_load_mask_pd(const npy_int num_elem, const npy_int total_elem) +{ + return (0x01 << num_elem) - 0x01; +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F __m512 +avx512_masked_gather_ps(__m512 src, + npy_float* addr, + __m512i vindex, + __mmask16 kmask) +{ + return _mm512_mask_i32gather_ps(src, kmask, vindex, addr, 4); +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F __m512d +avx512_masked_gather_pd(__m512d src, + npy_double* addr, + __m256i vindex, + __mmask8 kmask) +{ + return _mm512_mask_i32gather_pd(src, kmask, vindex, addr, 8); +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F __m512 +avx512_masked_load_ps(__mmask16 mask, npy_float* addr) +{ + return _mm512_maskz_loadu_ps(mask, (__m512 *)addr); +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F __m512d +avx512_masked_load_pd(__mmask8 mask, npy_double* addr) +{ + return _mm512_maskz_loadu_pd(mask, (__m512d *)addr); +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F __m512 +avx512_set_masked_lanes_ps(__m512 x, __m512 val, __mmask16 mask) +{ + return _mm512_mask_blend_ps(mask, x, val); +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F __m512d +avx512_set_masked_lanes_pd(__m512d x, __m512d val, __mmask8 mask) +{ + return _mm512_mask_blend_pd(mask, x, val); +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F __m512 +avx512_blend(__m512 x, __m512 y, __mmask16 ymask) +{ + return _mm512_mask_mov_ps(x, ymask, y); +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F __mmask16 +avx512_invert_mask_ps(__mmask16 ymask) +{ + return _mm512_knot(ymask); +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F __mmask8 +avx512_invert_mask_pd(__mmask8 ymask) +{ + return _mm512_knot(ymask); +} + +/**begin repeat + * #vsub = ps, pd# + * #type= npy_float, npy_double# + * #epi_vsub = epi32, epi64# + * #vtype = __m512, __m512d# + * #mask = __mmask16, __mmask8# + * #and_const = 0x7fffffff, 0x7fffffffffffffffLL# + * #neg_mask = 0x80000000, 0x8000000000000000# + * #perm_ = 0xb1, 0x55# + * #cmpx_img_mask = 0xAAAA, 0xAA# + * #cmpx_re_mask = 0x5555, 0x55# + * #INF = NPY_INFINITYF, NPY_INFINITY# + * #NAN = NPY_NANF, NPY_NAN# + */ +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F @vtype@ +avx512_abs_@vsub@(@vtype@ x) +{ + return (@vtype@) _mm512_and_@epi_vsub@((__m512i) x, + _mm512_set1_@epi_vsub@ (@and_const@)); +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F @vtype@ +avx512_reciprocal_@vsub@(@vtype@ x) +{ + return _mm512_div_@vsub@(_mm512_set1_@vsub@(1.0f), x); +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F @vtype@ +avx512_rint_@vsub@(@vtype@ x) +{ + return _mm512_roundscale_@vsub@(x, 0x08); +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F @vtype@ +avx512_floor_@vsub@(@vtype@ x) +{ + return _mm512_roundscale_@vsub@(x, 0x09); +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F @vtype@ +avx512_trunc_@vsub@(@vtype@ x) +{ + return _mm512_roundscale_@vsub@(x, 0x0B); +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F @vtype@ +avx512_hadd_@vsub@(const @vtype@ x) +{ + return _mm512_add_@vsub@(x, _mm512_permute_@vsub@(x, @perm_@)); +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F @vtype@ +avx512_hsub_@vsub@(const @vtype@ x) +{ + return _mm512_sub_@vsub@(x, _mm512_permute_@vsub@(x, @perm_@)); +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F @vtype@ +avx512_cabsolute_@vsub@(const @vtype@ x1, + const @vtype@ x2, + const __m512i re_indices, + const __m512i im_indices) +{ + @vtype@ inf = _mm512_set1_@vsub@(@INF@); + @vtype@ nan = _mm512_set1_@vsub@(@NAN@); + @vtype@ x1_abs = avx512_abs_@vsub@(x1); + @vtype@ x2_abs = avx512_abs_@vsub@(x2); + @vtype@ re = _mm512_permutex2var_@vsub@(x1_abs, re_indices, x2_abs); + @vtype@ im = _mm512_permutex2var_@vsub@(x1_abs, im_indices , x2_abs); + /* + * If real or imag = INF, then convert it to inf + j*inf + * Handles: inf + j*nan, nan + j*inf + */ + @mask@ re_infmask = _mm512_cmp_@vsub@_mask(re, inf, _CMP_EQ_OQ); + @mask@ im_infmask = _mm512_cmp_@vsub@_mask(im, inf, _CMP_EQ_OQ); + im = _mm512_mask_mov_@vsub@(im, re_infmask, inf); + re = _mm512_mask_mov_@vsub@(re, im_infmask, inf); + + /* + * If real or imag = NAN, then convert it to nan + j*nan + * Handles: x + j*nan, nan + j*x + */ + @mask@ re_nanmask = _mm512_cmp_@vsub@_mask(re, re, _CMP_NEQ_UQ); + @mask@ im_nanmask = _mm512_cmp_@vsub@_mask(im, im, _CMP_NEQ_UQ); + im = _mm512_mask_mov_@vsub@(im, re_nanmask, nan); + re = _mm512_mask_mov_@vsub@(re, im_nanmask, nan); + + @vtype@ larger = _mm512_max_@vsub@(re, im); + @vtype@ smaller = _mm512_min_@vsub@(im, re); + + /* + * Calculate div_mask to prevent 0./0. and inf/inf operations in div + */ + @mask@ zeromask = _mm512_cmp_@vsub@_mask(larger, _mm512_setzero_@vsub@(), _CMP_EQ_OQ); + @mask@ infmask = _mm512_cmp_@vsub@_mask(smaller, inf, _CMP_EQ_OQ); + @mask@ div_mask = _mm512_knot(_mm512_kor(zeromask, infmask)); + @vtype@ ratio = _mm512_maskz_div_@vsub@(div_mask, smaller, larger); + @vtype@ hypot = _mm512_sqrt_@vsub@(_mm512_fmadd_@vsub@( + ratio, ratio, _mm512_set1_@vsub@(1.0f))); + return _mm512_mul_@vsub@(hypot, larger); +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F @vtype@ +avx512_conjugate_@vsub@(const @vtype@ x) +{ + /* + * __mm512_mask_xor_ps/pd requires AVX512DQ. We cast it to __m512i and + * use the xor_epi32/64 uinstruction instead. Cast is a zero latency instruction + */ + __m512i cast_x = _mm512_cast@vsub@_si512(x); + __m512i res = _mm512_mask_xor_@epi_vsub@(cast_x, @cmpx_img_mask@, + cast_x, _mm512_set1_@epi_vsub@(@neg_mask@)); + return _mm512_castsi512_@vsub@(res); +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F @vtype@ +avx512_cmul_@vsub@(@vtype@ x1, @vtype@ x2) +{ + // x1 = r1, i1 + // x2 = r2, i2 + @vtype@ x3 = _mm512_permute_@vsub@(x2, @perm_@); // i2, r2 + @vtype@ x12 = _mm512_mul_@vsub@(x1, x2); // r1*r2, i1*i2 + @vtype@ x13 = _mm512_mul_@vsub@(x1, x3); // r1*i2, r2*i1 + @vtype@ outreal = avx512_hsub_@vsub@(x12); // r1*r2 - i1*i2, r1*r2 - i1*i2 + @vtype@ outimg = avx512_hadd_@vsub@(x13); // r1*i2 + i1*r2, r1*i2 + i1*r2 + return _mm512_mask_blend_@vsub@(@cmpx_img_mask@, outreal, outimg); +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F @vtype@ +avx512_csquare_@vsub@(@vtype@ x) +{ + return avx512_cmul_@vsub@(x, x); +} + +/**end repeat**/ +#endif + +/**begin repeat + * #ISA = FMA, AVX512F# + * #isa = fma, avx512# + * #vtype = __m256, __m512# + * #vsize = 256, 512# + * #or = or_ps, kor# + * #vsub = , _mask# + * #mask = __m256, __mmask16# + * #fmadd = _mm256_fmadd_ps, _mm512_fmadd_ps# + * #CHK = HAVE_ATTRIBUTE_TARGET_AVX2_WITH_INTRINSICS, HAVE_ATTRIBUTE_TARGET_AVX512F_WITH_INTRINSICS# + **/ + +#if defined @CHK@ + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_@ISA@ @vtype@ +@isa@_sqrt_ps(@vtype@ x) +{ + return _mm@vsize@_sqrt_ps(x); +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_@ISA@ @vtype@d +@isa@_sqrt_pd(@vtype@d x) +{ + return _mm@vsize@_sqrt_pd(x); +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_@ISA@ @vtype@ +@isa@_square_ps(@vtype@ x) +{ + return _mm@vsize@_mul_ps(x,x); +} + +NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_@ISA@ @vtype@d +@isa@_square_pd(@vtype@d x) +{ + return _mm@vsize@_mul_pd(x,x); +} + +#endif +/**end repeat**/ + +/**begin repeat + * #type = npy_float, npy_double# + * #TYPE = FLOAT, DOUBLE# + * #num_lanes = 16, 8# + * #vsuffix = ps, pd# + * #mask = __mmask16, __mmask8# + * #vtype = __m512, __m512d# + * #scale = 4, 8# + * #vindextype = __m512i, __m256i# + * #vindexload = _mm512_loadu_si512, _mm256_loadu_si256# + * #episize = epi32, epi64# + */ + +/**begin repeat1 + * #func = isnan, isfinite, isinf, signbit# + * #IMM8 = 0x81, 0x99, 0x18, 0x04# + * #is_finite = 0, 1, 0, 0# + * #is_signbit = 0, 0, 0, 1# + */ + +#if defined HAVE_ATTRIBUTE_TARGET_AVX512_SKX_WITH_INTRINSICS && defined NPY_HAVE_SSE2_INTRINSICS +static NPY_INLINE NPY_GCC_TARGET_AVX512_SKX void +AVX512_SKX_@func@_@TYPE@(npy_bool* op, @type@* ip, const npy_intp array_size, const npy_intp steps) +{ + const npy_intp stride_ip = steps/(npy_intp)sizeof(@type@); + npy_intp num_remaining_elements = array_size; + + @mask@ load_mask = avx512_get_full_load_mask_@vsuffix@(); +#if @is_signbit@ + @vtype@ signbit = _mm512_set1_@vsuffix@(-0.0); +#endif + + /* + * Note: while generally indices are npy_intp, we ensure that our maximum + * index will fit in an int32 as a precondition for this function via + * IS_OUTPUT_BLOCKABLE_UNARY + */ + + npy_int32 index_ip[@num_lanes@]; + for (npy_int32 ii = 0; ii < @num_lanes@; ii++) { + index_ip[ii] = ii*stride_ip; + } + @vindextype@ vindex_ip = @vindexload@((@vindextype@*)&index_ip[0]); + @vtype@ zeros_f = _mm512_setzero_@vsuffix@(); + __m512i ones = _mm512_set1_@episize@(1); + + while (num_remaining_elements > 0) { + if (num_remaining_elements < @num_lanes@) { + load_mask = avx512_get_partial_load_mask_@vsuffix@( + num_remaining_elements, @num_lanes@); + } + @vtype@ x1; + if (stride_ip == 1) { + x1 = avx512_masked_load_@vsuffix@(load_mask, ip); + } + else { + x1 = avx512_masked_gather_@vsuffix@(zeros_f, ip, vindex_ip, load_mask); + } +#if @is_signbit@ + x1 = _mm512_and_@vsuffix@(x1,signbit); +#endif + + @mask@ fpclassmask = _mm512_fpclass_@vsuffix@_mask(x1, @IMM8@); +#if @is_finite@ + fpclassmask = _mm512_knot(fpclassmask); +#endif + + __m128i out =_mm512_maskz_cvts@episize@_epi8(fpclassmask, ones); + _mm_mask_storeu_epi8(op, load_mask, out); + + ip += @num_lanes@*stride_ip; + op += @num_lanes@; + num_remaining_elements -= @num_lanes@; + } +} +#endif +/**end repeat1**/ +/**end repeat**/ + +/**begin repeat + * #type = npy_float, npy_double# + * #TYPE = FLOAT, DOUBLE# + * #num_lanes = 16, 8# + * #vsuffix = ps, pd# + * #mask = __mmask16, __mmask8# + * #vtype1 = __m512, __m512d# + * #vtype2 = __m512i, __m256i# + * #scale = 4, 8# + * #vindextype = __m512i, __m256i# + * #vindexsize = 512, 256# + * #vindexload = _mm512_loadu_si512, _mm256_loadu_si256# + * #vtype2_load = _mm512_maskz_loadu_epi32, _mm256_maskz_loadu_epi32# + * #vtype2_gather = _mm512_mask_i32gather_epi32, _mm256_mmask_i32gather_epi32# + * #vtype2_store = _mm512_mask_storeu_epi32, _mm256_mask_storeu_epi32# + * #vtype2_scatter = _mm512_mask_i32scatter_epi32, _mm256_mask_i32scatter_epi32# + * #setzero = _mm512_setzero_epi32, _mm256_setzero_si256# + */ +/**begin repeat1 + * #func = maximum, minimum# + * #vectorf = max, min# + */ + +#if defined HAVE_ATTRIBUTE_TARGET_AVX512F_WITH_INTRINSICS && defined NPY_HAVE_SSE2_INTRINSICS +static NPY_INLINE NPY_GCC_TARGET_AVX512F void +AVX512F_@func@_@TYPE@(char **args, npy_intp const *dimensions, npy_intp const *steps) +{ + const npy_intp stride_ip1 = steps[0]/(npy_intp)sizeof(@type@); + const npy_intp stride_ip2 = steps[1]/(npy_intp)sizeof(@type@); + const npy_intp stride_op = steps[2]/(npy_intp)sizeof(@type@); + const npy_intp array_size = dimensions[0]; + npy_intp num_remaining_elements = array_size; + @type@* ip1 = (@type@*) args[0]; + @type@* ip2 = (@type@*) args[1]; + @type@* op = (@type@*) args[2]; + + @mask@ load_mask = avx512_get_full_load_mask_@vsuffix@(); + + /* + * Note: while generally indices are npy_intp, we ensure that our maximum index + * will fit in an int32 as a precondition for this function via + * IS_BINARY_SMALL_STEPS_AND_NOMEMOVERLAP + */ + + npy_int32 index_ip1[@num_lanes@], index_ip2[@num_lanes@], index_op[@num_lanes@]; + for (npy_int32 ii = 0; ii < @num_lanes@; ii++) { + index_ip1[ii] = ii*stride_ip1; + index_ip2[ii] = ii*stride_ip2; + index_op[ii] = ii*stride_op; + } + @vindextype@ vindex_ip1 = @vindexload@((@vindextype@*)&index_ip1[0]); + @vindextype@ vindex_ip2 = @vindexload@((@vindextype@*)&index_ip2[0]); + @vindextype@ vindex_op = @vindexload@((@vindextype@*)&index_op[0]); + @vtype1@ zeros_f = _mm512_setzero_@vsuffix@(); + + while (num_remaining_elements > 0) { + if (num_remaining_elements < @num_lanes@) { + load_mask = avx512_get_partial_load_mask_@vsuffix@( + num_remaining_elements, @num_lanes@); + } + @vtype1@ x1, x2; + if (stride_ip1 == 1) { + x1 = avx512_masked_load_@vsuffix@(load_mask, ip1); + } + else { + x1 = avx512_masked_gather_@vsuffix@(zeros_f, ip1, vindex_ip1, load_mask); + } + if (stride_ip2 == 1) { + x2 = avx512_masked_load_@vsuffix@(load_mask, ip2); + } + else { + x2 = avx512_masked_gather_@vsuffix@(zeros_f, ip2, vindex_ip2, load_mask); + } + + /* + * when only one of the argument is a nan, the maxps/maxpd instruction + * returns the second argument. The additional blend instruction fixes + * this issue to conform with NumPy behaviour. + */ + @mask@ nan_mask = _mm512_cmp_@vsuffix@_mask(x1, x1, _CMP_NEQ_UQ); + @vtype1@ out = _mm512_@vectorf@_@vsuffix@(x1, x2); + out = _mm512_mask_blend_@vsuffix@(nan_mask, out, x1); + + if (stride_op == 1) { + _mm512_mask_storeu_@vsuffix@(op, load_mask, out); + } + else { + /* scatter! */ + _mm512_mask_i32scatter_@vsuffix@(op, load_mask, vindex_op, out, @scale@); + } + + ip1 += @num_lanes@*stride_ip1; + ip2 += @num_lanes@*stride_ip2; + op += @num_lanes@*stride_op; + num_remaining_elements -= @num_lanes@; + } +} +#endif +/**end repeat1**/ +/**end repeat**/ + +/**begin repeat + * #ISA = FMA, AVX512F# + * #isa = fma, avx512# + * #vsize = 256, 512# + * #BYTES = 32, 64# + * #cvtps_epi32 = _mm256_cvtps_epi32, # + * #mask = __m256, __mmask16# + * #vsub = , _mask# + * #vtype = __m256, __m512# + * #cvtps_epi32 = _mm256_cvtps_epi32, # + * #masked_store = _mm256_maskstore_ps, _mm512_mask_storeu_ps# + * #CHK = HAVE_ATTRIBUTE_TARGET_AVX2_WITH_INTRINSICS, HAVE_ATTRIBUTE_TARGET_AVX512F_WITH_INTRINSICS# + */ + +/**begin repeat1 + * #func = rint, floor, trunc# + * #vectorf = rint, floor, trunc# + */ + +#if defined @CHK@ +static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_@ISA@ void +@ISA@_@func@_FLOAT(npy_float* op, + npy_float* ip, + const npy_intp array_size, + const npy_intp steps) +{ + const npy_intp stride = steps/(npy_intp)sizeof(npy_float); + const npy_int num_lanes = @BYTES@/(npy_intp)sizeof(npy_float); + npy_intp num_remaining_elements = array_size; + @vtype@ ones_f = _mm@vsize@_set1_ps(1.0f); + @mask@ load_mask = @isa@_get_full_load_mask_ps(); + /* + * Note: while generally indices are npy_intp, we ensure that our maximum index + * will fit in an int32 as a precondition for this function via + * IS_OUTPUT_BLOCKABLE_UNARY + */ + + npy_int32 indexarr[16]; + for (npy_int32 ii = 0; ii < 16; ii++) { + indexarr[ii] = ii*stride; + } + @vtype@i vindex = _mm@vsize@_loadu_si@vsize@((@vtype@i*)&indexarr[0]); + + while (num_remaining_elements > 0) { + if (num_remaining_elements < num_lanes) { + load_mask = @isa@_get_partial_load_mask_ps(num_remaining_elements, + num_lanes); + } + @vtype@ x; + if (stride == 1) { + x = @isa@_masked_load_ps(load_mask, ip); + } + else { + x = @isa@_masked_gather_ps(ones_f, ip, vindex, load_mask); + } + @vtype@ out = @isa@_@vectorf@_ps(x); + @masked_store@(op, @cvtps_epi32@(load_mask), out); + + ip += num_lanes*stride; + op += num_lanes; + num_remaining_elements -= num_lanes; + } +} +#endif +/**end repeat1**/ +/**end repeat**/ + +/**begin repeat + * #ISA = FMA, AVX512F# + * #isa = fma, avx512# + * #vsize = 256, 512# + * #BYTES = 32, 64# + * #cvtps_epi32 = _mm256_cvtps_epi32, # + * #mask = __m256i, __mmask8# + * #vsub = , _mask# + * #vtype = __m256d, __m512d# + * #vindextype = __m128i, __m256i# + * #vindexsize = 128, 256# + * #vindexload = _mm_loadu_si128, _mm256_loadu_si256# + * #cvtps_epi32 = _mm256_cvtpd_epi32, # + * #castmask = _mm256_castsi256_pd, # + * #masked_store = _mm256_maskstore_pd, _mm512_mask_storeu_pd# + * #CHK = HAVE_ATTRIBUTE_TARGET_AVX2_WITH_INTRINSICS, HAVE_ATTRIBUTE_TARGET_AVX512F_WITH_INTRINSICS# + */ + +/**begin repeat1 + * #func = rint, floor, trunc# + * #vectorf = rint, floor, trunc# + */ + +#if defined @CHK@ +static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_@ISA@ void +@ISA@_@func@_DOUBLE(npy_double* op, + npy_double* ip, + const npy_intp array_size, + const npy_intp steps) +{ + const npy_intp stride = steps/(npy_intp)sizeof(npy_double); + const npy_int num_lanes = @BYTES@/(npy_intp)sizeof(npy_double); + npy_intp num_remaining_elements = array_size; + @mask@ load_mask = @isa@_get_full_load_mask_pd(); + @vtype@ ones_d = _mm@vsize@_set1_pd(1.0f); + + /* + * Note: while generally indices are npy_intp, we ensure that our maximum index + * will fit in an int32 as a precondition for this function via + * IS_OUTPUT_BLOCKABLE_UNARY + */ + npy_int32 indexarr[8]; + for (npy_int32 ii = 0; ii < 8; ii++) { + indexarr[ii] = ii*stride; + } + @vindextype@ vindex = @vindexload@((@vindextype@*)&indexarr[0]); + + while (num_remaining_elements > 0) { + if (num_remaining_elements < num_lanes) { + load_mask = @isa@_get_partial_load_mask_pd(num_remaining_elements, + num_lanes); + } + @vtype@ x; + if (stride == 1) { + x = @isa@_masked_load_pd(load_mask, ip); + } + else { + x = @isa@_masked_gather_pd(ones_d, ip, vindex, @castmask@(load_mask)); + } + @vtype@ out = @isa@_@vectorf@_pd(x); + @masked_store@(op, load_mask, out); + + ip += num_lanes*stride; + op += num_lanes; + num_remaining_elements -= num_lanes; + } +} +#endif +/**end repeat1**/ +/**end repeat**/ + +/**begin repeat + * #TYPE = CFLOAT, CDOUBLE# + * #type = npy_float, npy_double# + * #num_lanes = 16, 8# + * #vsuffix = ps, pd# + * #epi_vsub = epi32, epi64# + * #mask = __mmask16, __mmask8# + * #vtype = __m512, __m512d# + * #scale = 4, 8# + * #vindextype = __m512i, __m256i# + * #vindexload = _mm512_loadu_si512, _mm256_loadu_si256# + * #storemask = 0xFF, 0xF# + * #IS_FLOAT = 1, 0# + */ + +/**begin repeat1 + * #func = square, conjugate# + * #vectorf = avx512_csquare, avx512_conjugate# + */ + +#if defined HAVE_ATTRIBUTE_TARGET_AVX512F_WITH_INTRINSICS && defined NPY_HAVE_SSE2_INTRINSICS +static NPY_GCC_OPT_3 NPY_INLINE NPY_GCC_TARGET_AVX512F void +AVX512F_@func@_@TYPE@(@type@ * op, + @type@ * ip, + const npy_intp array_size, + const npy_intp steps) +{ + npy_intp num_remaining_elements = 2*array_size; + const npy_intp stride_ip1 = steps/(npy_intp)sizeof(@type@)/2; + + /* + * Note: while generally indices are npy_intp, we ensure that our maximum index + * will fit in an int32 as a precondition for this function via max_stride + */ + npy_int32 index_ip1[16]; + for (npy_int32 ii = 0; ii < @num_lanes@; ii=ii+2) { + index_ip1[ii] = ii*stride_ip1; + index_ip1[ii+1] = ii*stride_ip1 + 1; + } + @vindextype@ vindex = @vindexload@((@vindextype@*)index_ip1); + @mask@ load_mask = avx512_get_full_load_mask_@vsuffix@(); + @vtype@ zeros = _mm512_setzero_@vsuffix@(); + + while (num_remaining_elements > 0) { + if (num_remaining_elements < @num_lanes@) { + load_mask = avx512_get_partial_load_mask_@vsuffix@( + num_remaining_elements, @num_lanes@); + } + @vtype@ x1; + if (stride_ip1 == 1) { + x1 = avx512_masked_load_@vsuffix@(load_mask, ip); + } + else { + x1 = avx512_masked_gather_@vsuffix@(zeros, ip, vindex, load_mask); + } + + @vtype@ out = @vectorf@_@vsuffix@(x1); + + _mm512_mask_storeu_@vsuffix@(op, load_mask, out); + op += @num_lanes@; + ip += @num_lanes@*stride_ip1; + num_remaining_elements -= @num_lanes@; + } +} +#endif +/**end repeat1**/ + +#if defined HAVE_ATTRIBUTE_TARGET_AVX512F_WITH_INTRINSICS && defined NPY_HAVE_SSE2_INTRINSICS +static NPY_GCC_OPT_3 NPY_INLINE NPY_GCC_TARGET_AVX512F void +AVX512F_absolute_@TYPE@(@type@ * op, + @type@ * ip, + const npy_intp array_size, + const npy_intp steps) +{ + npy_intp num_remaining_elements = 2*array_size; + const npy_intp stride_ip1 = steps/(npy_intp)sizeof(@type@)/2; + + /* + * Note: while generally indices are npy_intp, we ensure that our maximum index + * will fit in an int32 as a precondition for this function via max_stride + */ + npy_int32 index_ip[32]; + for (npy_int32 ii = 0; ii < 2*@num_lanes@; ii=ii+2) { + index_ip[ii] = ii*stride_ip1; + index_ip[ii+1] = ii*stride_ip1 + 1; + } + @vindextype@ vindex1 = @vindexload@((@vindextype@*)index_ip); + @vindextype@ vindex2 = @vindexload@((@vindextype@*)(index_ip+@num_lanes@)); + + @mask@ load_mask1 = avx512_get_full_load_mask_@vsuffix@(); + @mask@ load_mask2 = avx512_get_full_load_mask_@vsuffix@(); + @mask@ store_mask = avx512_get_full_load_mask_@vsuffix@(); + @vtype@ zeros = _mm512_setzero_@vsuffix@(); + +#if @IS_FLOAT@ + __m512i re_index = _mm512_set_epi32(30,28,26,24,22,20,18,16,14,12,10,8,6,4,2,0); + __m512i im_index = _mm512_set_epi32(31,29,27,25,23,21,19,17,15,13,11,9,7,5,3,1); +#else + __m512i re_index = _mm512_set_epi64(14,12,10,8,6,4,2,0); + __m512i im_index = _mm512_set_epi64(15,13,11,9,7,5,3,1); +#endif + + while (num_remaining_elements > 0) { + if (num_remaining_elements < @num_lanes@) { + load_mask1 = avx512_get_partial_load_mask_@vsuffix@( + num_remaining_elements, @num_lanes@); + load_mask2 = 0x0000; + store_mask = avx512_get_partial_load_mask_@vsuffix@( + num_remaining_elements/2, @num_lanes@); + } else if (num_remaining_elements < 2*@num_lanes@) { + load_mask1 = avx512_get_full_load_mask_@vsuffix@(); + load_mask2 = avx512_get_partial_load_mask_@vsuffix@( + num_remaining_elements - @num_lanes@, @num_lanes@); + store_mask = avx512_get_partial_load_mask_@vsuffix@( + num_remaining_elements/2, @num_lanes@); + } + @vtype@ x1, x2; + if (stride_ip1 == 1) { + x1 = avx512_masked_load_@vsuffix@(load_mask1, ip); + x2 = avx512_masked_load_@vsuffix@(load_mask2, ip+@num_lanes@); + } + else { + x1 = avx512_masked_gather_@vsuffix@(zeros, ip, vindex1, load_mask1); + x2 = avx512_masked_gather_@vsuffix@(zeros, ip, vindex2, load_mask2); + } + + @vtype@ out = avx512_cabsolute_@vsuffix@(x1, x2, re_index, im_index); + + _mm512_mask_storeu_@vsuffix@(op, store_mask, out); + op += @num_lanes@; + ip += 2*@num_lanes@*stride_ip1; + num_remaining_elements -= 2*@num_lanes@; + } + npy_clear_floatstatus_barrier((char*)&num_remaining_elements); +} + +#endif +/**end repeat**/ + +/* + ***************************************************************************** + ** BOOL LOOPS + ***************************************************************************** + */ + +/**begin repeat + * # kind = logical_or, logical_and# + * # and = 0, 1# + * # op = ||, &&# + * # sc = !=, ==# + * # vpre = _mm*2# + * # vsuf = si128*2# + * # vtype = __m128i*2# + * # type = npy_bool*2# + * # vload = _mm_load_si128*2# + * # vloadu = _mm_loadu_si128*2# + * # vstore = _mm_store_si128*2# + */ /* * convert any bit set to boolean true so vectorized and normal operations are @@ -1085,7 +1603,7 @@ sse2_@kind@_@TYPE@(@type@ * ip, @type@ * op, const npy_intp n) * you never know */ #if !@and@ -static NPY_INLINE @vtype@ byte_to_true(@vtype@ v) +NPY_FINLINE @vtype@ byte_to_true(@vtype@ v) { const @vtype@ zero = @vpre@_setzero_@vsuf@(); const @vtype@ truemask = @vpre@_set1_epi8(1 == 1); @@ -1099,9 +1617,9 @@ static NPY_INLINE @vtype@ byte_to_true(@vtype@ v) static void sse2_binary_@kind@_BOOL(npy_bool * op, npy_bool * ip1, npy_bool * ip2, npy_intp n) { - LOOP_BLOCK_ALIGN_VAR(op, @type@, 16) + LOOP_BLOCK_ALIGN_VAR(op, @type@, VECTOR_SIZE_BYTES) op[i] = ip1[i] @op@ ip2[i]; - LOOP_BLOCKED(@type@, 16) { + LOOP_BLOCKED(@type@, VECTOR_SIZE_BYTES) { @vtype@ a = @vloadu@((@vtype@*)&ip1[i]); @vtype@ b = @vloadu@((@vtype@*)&ip2[i]); #if @and@ @@ -1126,16 +1644,16 @@ static void sse2_reduce_@kind@_BOOL(npy_bool * op, npy_bool * ip, const npy_intp n) { const @vtype@ zero = @vpre@_setzero_@vsuf@(); - LOOP_BLOCK_ALIGN_VAR(ip, npy_bool, 16) { + LOOP_BLOCK_ALIGN_VAR(ip, npy_bool, VECTOR_SIZE_BYTES) { *op = *op @op@ ip[i]; if (*op @sc@ 0) { return; } } /* unrolled once to replace a slow movmsk with a fast pmaxb */ - LOOP_BLOCKED(npy_bool, 32) { + LOOP_BLOCKED(npy_bool, 2 * VECTOR_SIZE_BYTES) { @vtype@ v = @vload@((@vtype@*)&ip[i]); - @vtype@ v2 = @vload@((@vtype@*)&ip[i + 16]); + @vtype@ v2 = @vload@((@vtype@*)&ip[i + VECTOR_SIZE_BYTES]); v = @vpre@_cmpeq_epi8(v, zero); v2 = @vpre@_cmpeq_epi8(v2, zero); #if @and@ @@ -1173,9 +1691,9 @@ sse2_reduce_@kind@_BOOL(npy_bool * op, npy_bool * ip, const npy_intp n) static void sse2_@kind@_BOOL(@type@ * op, @type@ * ip, const npy_intp n) { - LOOP_BLOCK_ALIGN_VAR(op, @type@, 16) + LOOP_BLOCK_ALIGN_VAR(op, @type@, VECTOR_SIZE_BYTES) op[i] = (ip[i] @op@ 0); - LOOP_BLOCKED(@type@, 16) { + LOOP_BLOCKED(@type@, VECTOR_SIZE_BYTES) { @vtype@ a = @vloadu@((@vtype@*)&ip[i]); #if @not@ const @vtype@ zero = @vpre@_setzero_@vsuf@(); @@ -1196,6 +1714,6 @@ sse2_@kind@_BOOL(@type@ * op, @type@ * ip, const npy_intp n) /**end repeat**/ -#endif /* NPY_HAVE_SSE2_INTRINSICS */ - +#undef VECTOR_SIZE_BYTES +#endif /* NPY_HAVE_SSE2_INTRINSICS */ #endif diff --git a/numpy/core/src/umath/svml b/numpy/core/src/umath/svml new file mode 160000 index 000000000000..1c5260a61e7d --- /dev/null +++ b/numpy/core/src/umath/svml @@ -0,0 +1 @@ +Subproject commit 1c5260a61e7dce6be48073dfa96291edb0a11d79 diff --git a/numpy/core/src/umath/ufunc_object.c b/numpy/core/src/umath/ufunc_object.c index 5e92bc9919f6..78f6f4b5a88d 100644 --- a/numpy/core/src/umath/ufunc_object.c +++ b/numpy/core/src/umath/ufunc_object.c @@ -23,17 +23,18 @@ * Rick White * */ -#define _UMATHMODULE #define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE +#define _UMATHMODULE -#include "Python.h" - -#include "npy_config.h" +#define PY_SSIZE_T_CLEAN +#include <Python.h> -#define PY_ARRAY_UNIQUE_SYMBOL _npy_umathmodule_ARRAY_API -#define NO_IMPORT_ARRAY +#include <stddef.h> +#include "npy_config.h" #include "npy_pycompat.h" +#include "npy_argparse.h" #include "numpy/arrayobject.h" #include "numpy/ufuncobject.h" @@ -42,12 +43,19 @@ #include "ufunc_type_resolution.h" #include "reduction.h" #include "mem_overlap.h" +#include "npy_hashtable.h" #include "ufunc_object.h" #include "override.h" #include "npy_import.h" #include "extobj.h" #include "common.h" +#include "dtypemeta.h" +#include "numpyos.h" +#include "dispatching.h" +#include "convert_datatype.h" +#include "legacy_array_method.h" +#include "abstractdtypes.h" /********** PRINTF DEBUG TRACING **************/ #define NPY_UF_DBG_TRACING 0 @@ -71,6 +79,13 @@ typedef struct { provided, then this is NULL. */ } ufunc_full_args; +/* C representation of the context argument to __array_wrap__ */ +typedef struct { + PyUFuncObject *ufunc; + ufunc_full_args args; + int out_i; +} _ufunc_context; + /* Get the arg tuple to pass in the context argument to __array_wrap__ and * __array_prepare__. * @@ -89,8 +104,15 @@ _get_wrap_prepare_args(ufunc_full_args full_args) { /* ---------------------------------------------------------------- */ +static PyObject * +prepare_input_arguments_for_outer(PyObject *args, PyUFuncObject *ufunc); + static int -_does_loop_use_arrays(void *data); +resolve_descriptors(int nop, + PyUFuncObject *ufunc, PyArrayMethodObject *ufuncimpl, + PyArrayObject *operands[], PyArray_Descr *dtypes[], + PyArray_DTypeMeta *signature[], NPY_CASTING casting); + /*UFUNC_API*/ NPY_NO_EXPORT int @@ -264,7 +286,7 @@ _get_output_array_method(PyObject *obj, PyObject *method, */ static void _find_array_prepare(ufunc_full_args args, - PyObject **output_prep, int nin, int nout) + PyObject **output_prep, int nout) { int i; PyObject *prep; @@ -302,6 +324,207 @@ _find_array_prepare(ufunc_full_args args, return; } +#define NPY_UFUNC_DEFAULT_INPUT_FLAGS \ + NPY_ITER_READONLY | \ + NPY_ITER_ALIGNED | \ + NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE + +#define NPY_UFUNC_DEFAULT_OUTPUT_FLAGS \ + NPY_ITER_ALIGNED | \ + NPY_ITER_ALLOCATE | \ + NPY_ITER_NO_BROADCAST | \ + NPY_ITER_NO_SUBTYPE | \ + NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE + +/* Called at module initialization to set the matmul ufunc output flags */ +NPY_NO_EXPORT int +set_matmul_flags(PyObject *d) +{ + PyObject *matmul = _PyDict_GetItemStringWithError(d, "matmul"); + if (matmul == NULL) { + return -1; + } + /* + * The default output flag NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE allows + * perfectly overlapping input and output (in-place operations). While + * correct for the common mathematical operations, this assumption is + * incorrect in the general case and specifically in the case of matmul. + * + * NPY_ITER_UPDATEIFCOPY is added by default in + * PyUFunc_GeneralizedFunction, which is the variant called for gufuncs + * with a signature + * + * Enabling NPY_ITER_WRITEONLY can prevent a copy in some cases. + */ + ((PyUFuncObject *)matmul)->op_flags[2] = (NPY_ITER_WRITEONLY | + NPY_ITER_UPDATEIFCOPY | + NPY_UFUNC_DEFAULT_OUTPUT_FLAGS) & + ~NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE; + return 0; +} + + +/* + * Set per-operand flags according to desired input or output flags. + * op_flags[i] for i in input (as determined by ufunc->nin) will be + * merged with op_in_flags, perhaps overriding per-operand flags set + * in previous stages. + * op_flags[i] for i in output will be set to op_out_flags only if previously + * unset. + * The input flag behavior preserves backward compatibility, while the + * output flag behaviour is the "correct" one for maximum flexibility. + */ +NPY_NO_EXPORT void +_ufunc_setup_flags(PyUFuncObject *ufunc, npy_uint32 op_in_flags, + npy_uint32 op_out_flags, npy_uint32 *op_flags) +{ + int nin = ufunc->nin; + int nout = ufunc->nout; + int nop = nin + nout, i; + /* Set up the flags */ + for (i = 0; i < nin; ++i) { + op_flags[i] = ufunc->op_flags[i] | op_in_flags; + /* + * If READWRITE flag has been set for this operand, + * then clear default READONLY flag + */ + if (op_flags[i] & (NPY_ITER_READWRITE | NPY_ITER_WRITEONLY)) { + op_flags[i] &= ~NPY_ITER_READONLY; + } + } + for (i = nin; i < nop; ++i) { + op_flags[i] = ufunc->op_flags[i] ? ufunc->op_flags[i] : op_out_flags; + } +} + +/* + * This function analyzes the input arguments + * and determines an appropriate __array_wrap__ function to call + * for the outputs. + * + * If an output argument is provided, then it is wrapped + * with its own __array_wrap__ not with the one determined by + * the input arguments. + * + * if the provided output argument is already an array, + * the wrapping function is None (which means no wrapping will + * be done --- not even PyArray_Return). + * + * A NULL is placed in output_wrap for outputs that + * should just have PyArray_Return called. + */ +static void +_find_array_wrap(ufunc_full_args args, npy_bool subok, + PyObject **output_wrap, int nin, int nout) +{ + int i; + PyObject *wrap = NULL; + + /* + * If a 'subok' parameter is passed and isn't True, don't wrap but put None + * into slots with out arguments which means return the out argument + */ + if (!subok) { + goto handle_out; + } + + /* + * Determine the wrapping function given by the input arrays + * (could be NULL). + */ + wrap = _find_array_method(args.in, npy_um_str_array_wrap); + + /* + * For all the output arrays decide what to do. + * + * 1) Use the wrap function determined from the input arrays + * This is the default if the output array is not + * passed in. + * + * 2) Use the __array_wrap__ method of the output object + * passed in. -- this is special cased for + * exact ndarray so that no PyArray_Return is + * done in that case. + */ +handle_out: + if (args.out == NULL) { + for (i = 0; i < nout; i++) { + Py_XINCREF(wrap); + output_wrap[i] = wrap; + } + } + else { + for (i = 0; i < nout; i++) { + output_wrap[i] = _get_output_array_method( + PyTuple_GET_ITEM(args.out, i), npy_um_str_array_wrap, wrap); + } + } + + Py_XDECREF(wrap); +} + + +/* + * Apply the __array_wrap__ function with the given array and content. + * + * Interprets wrap=None and wrap=NULL as intended by _find_array_wrap + * + * Steals a reference to obj and wrap. + * Pass context=NULL to indicate there is no context. + */ +static PyObject * +_apply_array_wrap( + PyObject *wrap, PyArrayObject *obj, _ufunc_context const *context) { + if (wrap == NULL) { + /* default behavior */ + return PyArray_Return(obj); + } + else if (wrap == Py_None) { + Py_DECREF(wrap); + return (PyObject *)obj; + } + else { + PyObject *res; + PyObject *py_context = NULL; + + /* Convert the context object to a tuple, if present */ + if (context == NULL) { + py_context = Py_None; + Py_INCREF(py_context); + } + else { + PyObject *args_tup; + /* Call the method with appropriate context */ + args_tup = _get_wrap_prepare_args(context->args); + if (args_tup == NULL) { + goto fail; + } + py_context = Py_BuildValue("OOi", + context->ufunc, args_tup, context->out_i); + Py_DECREF(args_tup); + if (py_context == NULL) { + goto fail; + } + } + /* try __array_wrap__(obj, context) */ + res = PyObject_CallFunctionObjArgs(wrap, obj, py_context, NULL); + Py_DECREF(py_context); + + /* try __array_wrap__(obj) if the context argument is not accepted */ + if (res == NULL && PyErr_ExceptionMatches(PyExc_TypeError)) { + PyErr_Clear(); + res = PyObject_CallFunctionObjArgs(wrap, obj, NULL); + } + Py_DECREF(wrap); + Py_DECREF(obj); + return res; + fail: + Py_DECREF(wrap); + Py_DECREF(obj); + return NULL; + } +} + /*UFUNC_API * @@ -340,7 +563,27 @@ _is_alnum_underscore(char ch) } /* - * Return the ending position of a variable name + * Convert a string into a number + */ +static npy_intp +_get_size(const char* str) +{ + char *stop; + npy_longlong size = NumPyOS_strtoll(str, &stop, 10); + + if (stop == str || _is_alpha_underscore(*stop)) { + /* not a well formed number */ + return -1; + } + if (size >= NPY_MAX_INTP || size <= NPY_MIN_INTP) { + /* len(str) too long */ + return -1; + } + return size; +} + +/* + * Return the ending position of a variable name including optional modifier */ static int _get_end_of_name(const char* str, int offset) @@ -349,6 +592,9 @@ _get_end_of_name(const char* str, int offset) while (_is_alnum_underscore(str[ret])) { ret++; } + if (str[ret] == '?') { + ret ++; + } return ret; } @@ -370,9 +616,24 @@ _is_same_name(const char* s1, const char* s2) } /* - * Sets core_num_dim_ix, core_num_dims, core_dim_ixs, core_offsets, - * and core_signature in PyUFuncObject "ufunc". Returns 0 unless an - * error occurred. + * Sets the following fields in the PyUFuncObject 'ufunc': + * + * Field Type Array Length + * core_enabled int (effectively bool) N/A + * core_num_dim_ix int N/A + * core_dim_flags npy_uint32 * core_num_dim_ix + * core_dim_sizes npy_intp * core_num_dim_ix + * core_num_dims int * nargs (i.e. nin+nout) + * core_offsets int * nargs + * core_dim_ixs int * sum(core_num_dims) + * core_signature char * strlen(signature) + 1 + * + * The function assumes that the values that are arrays have not + * been set already, and sets these pointers to memory allocated + * with PyArray_malloc. These are freed when the ufunc dealloc + * method is called. + * + * Returns 0 unless an error occurred. */ static int _parse_signature(PyUFuncObject *ufunc, const char *signature) @@ -390,7 +651,6 @@ _parse_signature(PyUFuncObject *ufunc, const char *signature) "_parse_signature with NULL signature"); return -1; } - len = strlen(signature); ufunc->core_signature = PyArray_malloc(sizeof(char) * (len+1)); if (ufunc->core_signature) { @@ -406,13 +666,22 @@ _parse_signature(PyUFuncObject *ufunc, const char *signature) ufunc->core_enabled = 1; ufunc->core_num_dim_ix = 0; ufunc->core_num_dims = PyArray_malloc(sizeof(int) * ufunc->nargs); - ufunc->core_dim_ixs = PyArray_malloc(sizeof(int) * len); /* shrink this later */ ufunc->core_offsets = PyArray_malloc(sizeof(int) * ufunc->nargs); - if (ufunc->core_num_dims == NULL || ufunc->core_dim_ixs == NULL - || ufunc->core_offsets == NULL) { + /* The next three items will be shrunk later */ + ufunc->core_dim_ixs = PyArray_malloc(sizeof(int) * len); + ufunc->core_dim_sizes = PyArray_malloc(sizeof(npy_intp) * len); + ufunc->core_dim_flags = PyArray_malloc(sizeof(npy_uint32) * len); + + if (ufunc->core_num_dims == NULL || ufunc->core_dim_ixs == NULL || + ufunc->core_offsets == NULL || + ufunc->core_dim_sizes == NULL || + ufunc->core_dim_flags == NULL) { PyErr_NoMemory(); goto fail; } + for (size_t j = 0; j < len; j++) { + ufunc->core_dim_flags[j] = 0; + } i = _next_non_white_space(signature, 0); while (signature[i] != '\0') { @@ -437,26 +706,70 @@ _parse_signature(PyUFuncObject *ufunc, const char *signature) i = _next_non_white_space(signature, i + 1); while (signature[i] != ')') { /* loop over core dimensions */ - int j = 0; - if (!_is_alpha_underscore(signature[i])) { - parse_error = "expect dimension name"; + int ix, i_end; + npy_intp frozen_size; + npy_bool can_ignore; + + if (signature[i] == '\0') { + parse_error = "unexpected end of signature string"; goto fail; } - while (j < ufunc->core_num_dim_ix) { - if (_is_same_name(signature+i, var_names[j])) { + /* + * Is this a variable or a fixed size dimension? + */ + if (_is_alpha_underscore(signature[i])) { + frozen_size = -1; + } + else { + frozen_size = (npy_intp)_get_size(signature + i); + if (frozen_size <= 0) { + parse_error = "expect dimension name or non-zero frozen size"; + goto fail; + } + } + /* Is this dimension flexible? */ + i_end = _get_end_of_name(signature, i); + can_ignore = (i_end > 0 && signature[i_end - 1] == '?'); + /* + * Determine whether we already saw this dimension name, + * get its index, and set its properties + */ + for(ix = 0; ix < ufunc->core_num_dim_ix; ix++) { + if (frozen_size > 0 ? + frozen_size == ufunc->core_dim_sizes[ix] : + _is_same_name(signature + i, var_names[ix])) { break; } - j++; } - if (j >= ufunc->core_num_dim_ix) { - var_names[j] = signature+i; + /* + * If a new dimension, store its properties; if old, check consistency. + */ + if (ix == ufunc->core_num_dim_ix) { ufunc->core_num_dim_ix++; + var_names[ix] = signature + i; + ufunc->core_dim_sizes[ix] = frozen_size; + if (frozen_size < 0) { + ufunc->core_dim_flags[ix] |= UFUNC_CORE_DIM_SIZE_INFERRED; + } + if (can_ignore) { + ufunc->core_dim_flags[ix] |= UFUNC_CORE_DIM_CAN_IGNORE; + } + } else { + if (can_ignore && !(ufunc->core_dim_flags[ix] & + UFUNC_CORE_DIM_CAN_IGNORE)) { + parse_error = "? cannot be used, name already seen without ?"; + goto fail; + } + if (!can_ignore && (ufunc->core_dim_flags[ix] & + UFUNC_CORE_DIM_CAN_IGNORE)) { + parse_error = "? must be used, name already seen with ?"; + goto fail; + } } - ufunc->core_dim_ixs[cur_core_dim] = j; + ufunc->core_dim_ixs[cur_core_dim] = ix; cur_core_dim++; nd++; - i = _get_end_of_name(signature, i); - i = _next_non_white_space(signature, i); + i = _next_non_white_space(signature, i_end); if (signature[i] != ',' && signature[i] != ')') { parse_error = "expect ',' or ')'"; goto fail; @@ -493,7 +806,14 @@ _parse_signature(PyUFuncObject *ufunc, const char *signature) goto fail; } ufunc->core_dim_ixs = PyArray_realloc(ufunc->core_dim_ixs, - sizeof(int)*cur_core_dim); + sizeof(int) * cur_core_dim); + ufunc->core_dim_sizes = PyArray_realloc( + ufunc->core_dim_sizes, + sizeof(npy_intp) * ufunc->core_num_dim_ix); + ufunc->core_dim_flags = PyArray_realloc( + ufunc->core_dim_flags, + sizeof(npy_uint32) * ufunc->core_num_dim_ix); + /* check for trivial core-signature, e.g. "(),()->()" */ if (cur_core_dim == 0) { ufunc->core_enabled = 0; @@ -524,7 +844,7 @@ _set_out_array(PyObject *obj, PyArrayObject **store) /* Translate None to NULL */ return 0; } - if PyArray_Check(obj) { + if (PyArray_Check(obj)) { /* If it's an array, store it */ if (PyArray_FailUnlessWriteable((PyArrayObject *)obj, "output array") < 0) { @@ -551,524 +871,177 @@ ufunc_get_name_cstr(PyUFuncObject *ufunc) { return ufunc->name ? ufunc->name : "<unnamed ufunc>"; } + /* - * Parses the positional and keyword arguments for a generic ufunc call. - * All returned arguments are new references (with optional ones NULL - * if not present) + * Converters for use in parsing of keywords arguments. */ static int -get_ufunc_arguments(PyUFuncObject *ufunc, - PyObject *args, PyObject *kwds, - PyArrayObject **out_op, - NPY_ORDER *out_order, - NPY_CASTING *out_casting, - PyObject **out_extobj, - PyObject **out_typetup, /* type: Tuple[np.dtype] */ - int *out_subok, /* bool */ - PyArrayObject **out_wheremask, /* PyArray of bool */ - PyObject **out_axes, /* type: List[Tuple[T]] */ - PyObject **out_axis, /* type: T */ - int *out_keepdims) /* bool */ +_subok_converter(PyObject *obj, npy_bool *subok) { - int i, nargs; - int nin = ufunc->nin; - int nout = ufunc->nout; - int nop = ufunc->nargs; - PyObject *obj, *context; - PyObject *str_key_obj = NULL; - const char *ufunc_name = ufunc_get_name_cstr(ufunc); - int type_num; + if (PyBool_Check(obj)) { + *subok = (obj == Py_True); + return NPY_SUCCEED; + } + else { + PyErr_SetString(PyExc_TypeError, + "'subok' must be a boolean"); + return NPY_FAIL; + } +} - int any_flexible = 0, any_object = 0, any_flexible_userloops = 0; - int has_sig = 0; +static int +_keepdims_converter(PyObject *obj, int *keepdims) +{ + if (PyBool_Check(obj)) { + *keepdims = (obj == Py_True); + return NPY_SUCCEED; + } + else { + PyErr_SetString(PyExc_TypeError, + "'keepdims' must be a boolean"); + return NPY_FAIL; + } +} +static int +_wheremask_converter(PyObject *obj, PyArrayObject **wheremask) +{ /* - * Initialize objects so caller knows when outputs and other optional - * arguments are set (also means we can safely XDECREF on failure). + * Optimization: where=True is the same as no where argument. + * This lets us document True as the default. */ - for (i = 0; i < nop; i++) { - out_op[i] = NULL; - } - *out_extobj = NULL; - *out_typetup = NULL; - if (out_axes != NULL) { - *out_axes = NULL; + if (obj == Py_True) { + return NPY_SUCCEED; } - if (out_axis != NULL) { - *out_axis = NULL; - } - if (out_wheremask != NULL) { - *out_wheremask = NULL; + else { + PyArray_Descr *dtype = PyArray_DescrFromType(NPY_BOOL); + if (dtype == NULL) { + return NPY_FAIL; + } + /* PyArray_FromAny steals reference to dtype, even on failure */ + *wheremask = (PyArrayObject *)PyArray_FromAny(obj, dtype, 0, 0, 0, NULL); + if ((*wheremask) == NULL) { + return NPY_FAIL; + } + return NPY_SUCCEED; } +} - /* Check number of arguments */ - nargs = PyTuple_Size(args); - if ((nargs < nin) || (nargs > nop)) { - PyErr_SetString(PyExc_ValueError, "invalid number of arguments"); - return -1; - } - /* Get input arguments */ - for (i = 0; i < nin; ++i) { - obj = PyTuple_GET_ITEM(args, i); +/* + * Due to the array override, do the actual parameter conversion + * only in this step. This function takes the reference objects and + * parses them into the desired values. + * This function cleans up after itself and NULLs references on error, + * however, the caller has to ensure that `out_op[0:nargs]` and `out_whermeask` + * are NULL initialized. + */ +static int +convert_ufunc_arguments(PyUFuncObject *ufunc, + ufunc_full_args full_args, PyArrayObject *out_op[], + PyArray_DTypeMeta *out_op_DTypes[], + npy_bool *force_legacy_promotion, npy_bool *allow_legacy_promotion, + PyObject *order_obj, NPY_ORDER *out_order, + PyObject *casting_obj, NPY_CASTING *out_casting, + PyObject *subok_obj, npy_bool *out_subok, + PyObject *where_obj, PyArrayObject **out_wheremask, /* PyArray of bool */ + PyObject *keepdims_obj, int *out_keepdims) +{ + int nin = ufunc->nin; + int nout = ufunc->nout; + int nop = ufunc->nargs; + PyObject *obj; + + /* Convert and fill in input arguments */ + npy_bool all_scalar = NPY_TRUE; + npy_bool any_scalar = NPY_FALSE; + *allow_legacy_promotion = NPY_TRUE; + *force_legacy_promotion = NPY_FALSE; + for (int i = 0; i < nin; i++) { + obj = PyTuple_GET_ITEM(full_args.in, i); if (PyArray_Check(obj)) { - PyArrayObject *obj_a = (PyArrayObject *)obj; - out_op[i] = (PyArrayObject *)PyArray_FromArray(obj_a, NULL, 0); + out_op[i] = (PyArrayObject *)obj; + Py_INCREF(out_op[i]); } else { - if (!PyArray_IsScalar(obj, Generic)) { - /* - * TODO: There should be a comment here explaining what - * context does. - */ - context = Py_BuildValue("OOi", ufunc, args, i); - if (context == NULL) { - goto fail; - } - } - else { - context = NULL; + /* Convert the input to an array and check for special cases */ + out_op[i] = (PyArrayObject *)PyArray_FromAny(obj, NULL, 0, 0, 0, NULL); + if (out_op[i] == NULL) { + goto fail; } - out_op[i] = (PyArrayObject *)PyArray_FromAny(obj, - NULL, 0, 0, 0, context); - Py_XDECREF(context); } + out_op_DTypes[i] = NPY_DTYPE(PyArray_DESCR(out_op[i])); + Py_INCREF(out_op_DTypes[i]); - if (out_op[i] == NULL) { - goto fail; + if (!NPY_DT_is_legacy(out_op_DTypes[i])) { + *allow_legacy_promotion = NPY_FALSE; } - - type_num = PyArray_DESCR(out_op[i])->type_num; - if (!any_flexible && - PyTypeNum_ISFLEXIBLE(type_num)) { - any_flexible = 1; + if (PyArray_NDIM(out_op[i]) == 0) { + any_scalar = NPY_TRUE; } - if (!any_object && - PyTypeNum_ISOBJECT(type_num)) { - any_object = 1; + else { + all_scalar = NPY_FALSE; + continue; } - /* - * If any operand is a flexible dtype, check to see if any - * struct dtype ufuncs are registered. A ufunc has been registered - * for a struct dtype if ufunc's arg_dtypes array is not NULL. + * TODO: we need to special case scalars here, if the input is a + * Python int, float, or complex, we have to use the "weak" + * DTypes: `PyArray_PyIntAbstractDType`, etc. + * This is to allow e.g. `float32(1.) + 1` to return `float32`. + * The correct array dtype can only be found after promotion for + * such a "weak scalar". We could avoid conversion here, but + * must convert it for use in the legacy promotion. + * There is still a small chance that this logic can instead + * happen inside the Python operators. */ - if (PyTypeNum_ISFLEXIBLE(type_num) && - !any_flexible_userloops && - ufunc->userloops != NULL) { - PyUFunc_Loop1d *funcdata; - PyObject *key, *obj; - key = PyInt_FromLong(type_num); - if (key == NULL) { - continue; - } - obj = PyDict_GetItem(ufunc->userloops, key); - Py_DECREF(key); - if (obj == NULL) { - continue; + } + if (*allow_legacy_promotion && (!all_scalar && any_scalar)) { + *force_legacy_promotion = should_use_min_scalar(nin, out_op, 0, NULL); + } + + /* Convert and fill in output arguments */ + memset(out_op_DTypes + nin, 0, nout * sizeof(*out_op_DTypes)); + if (full_args.out != NULL) { + for (int i = 0; i < nout; i++) { + obj = PyTuple_GET_ITEM(full_args.out, i); + if (_set_out_array(obj, out_op + i + nin) < 0) { + goto fail; } - funcdata = (PyUFunc_Loop1d *)NpyCapsule_AsVoidPtr(obj); - while (funcdata != NULL) { - if (funcdata->arg_dtypes != NULL) { - any_flexible_userloops = 1; - break; - } - funcdata = funcdata->next; + if (out_op[i] != NULL) { + out_op_DTypes[i + nin] = NPY_DTYPE(PyArray_DESCR(out_op[i])); + Py_INCREF(out_op_DTypes[i + nin]); } } } - if (any_flexible && !any_flexible_userloops && !any_object && nin == 2) { - /* Traditionally, we return -2 here (meaning "NotImplemented") anytime - * we hit the above condition. - * - * This condition basically means "we are doomed", b/c the "flexible" - * dtypes -- strings and void -- cannot have their own ufunc loops - * registered (except via the special "flexible userloops" mechanism), - * and they can't be cast to anything except object (and we only cast - * to object if any_object is true). So really we should do nothing - * here and continue and let the proper error be raised. But, we can't - * quite yet, b/c of backcompat. - * - * Most of the time, this NotImplemented either got returned directly - * to the user (who can't do anything useful with it), or got passed - * back out of a special function like __mul__. And fortunately, for - * almost all special functions, the end result of this was a - * TypeError. Which is also what we get if we just continue without - * this special case, so this special case is unnecessary. - * - * The only thing that actually depended on the NotImplemented is - * array_richcompare, which did two things with it. First, it needed - * to see this NotImplemented in order to implement the special-case - * comparisons for - * - * string < <= == != >= > string - * void == != void - * - * Now it checks for those cases first, before trying to call the - * ufunc, so that's no problem. What it doesn't handle, though, is - * cases like - * - * float < string - * - * or - * - * float == void - * - * For those, it just let the NotImplemented bubble out, and accepted - * Python's default handling. And unfortunately, for comparisons, - * Python's default is *not* to raise an error. Instead, it returns - * something that depends on the operator: - * - * == return False - * != return True - * < <= >= > Python 2: use "fallback" (= weird and broken) ordering - * Python 3: raise TypeError (hallelujah) - * - * In most cases this is straightforwardly broken, because comparison - * of two arrays should always return an array, and here we end up - * returning a scalar. However, there is an exception: if we are - * comparing two scalars for equality, then it actually is correct to - * return a scalar bool instead of raising an error. If we just - * removed this special check entirely, then "np.float64(1) == 'foo'" - * would raise an error instead of returning False, which is genuinely - * wrong. - * - * The proper end goal here is: - * 1) == and != should be implemented in a proper vectorized way for - * all types. The short-term hack for this is just to add a - * special case to PyUFunc_DefaultLegacyInnerLoopSelector where - * if it can't find a comparison loop for the given types, and - * the ufunc is np.equal or np.not_equal, then it returns a loop - * that just fills the output array with False (resp. True). Then - * array_richcompare could trust that whenever its special cases - * don't apply, simply calling the ufunc will do the right thing, - * even without this special check. - * 2) < <= >= > should raise an error if no comparison function can - * be found. array_richcompare already handles all string <> - * string cases, and void dtypes don't have ordering, so again - * this would mean that array_richcompare could simply call the - * ufunc and it would do the right thing (i.e., raise an error), - * again without needing this special check. - * - * So this means that for the transition period, our goal is: - * == and != on scalars should simply return NotImplemented like - * they always did, since everything ends up working out correctly - * in this case only - * == and != on arrays should issue a FutureWarning and then return - * NotImplemented - * < <= >= > on all flexible dtypes on py2 should raise a - * DeprecationWarning, and then return NotImplemented. On py3 we - * skip the warning, though, b/c it would just be immediately be - * followed by an exception anyway. - * - * And for all other operations, we let things continue as normal. - */ - /* strcmp() is a hack but I think we can get away with it for this - * temporary measure. - */ - if (!strcmp(ufunc_name, "equal") || - !strcmp(ufunc_name, "not_equal")) { - /* Warn on non-scalar, return NotImplemented regardless */ - if (PyArray_NDIM(out_op[0]) != 0 || - PyArray_NDIM(out_op[1]) != 0) { - if (DEPRECATE_FUTUREWARNING( - "elementwise comparison failed; returning scalar " - "instead, but in the future will perform elementwise " - "comparison") < 0) { - goto fail; - } - } - Py_DECREF(out_op[0]); - Py_DECREF(out_op[1]); - return -2; - } - else if (!strcmp(ufunc_name, "less") || - !strcmp(ufunc_name, "less_equal") || - !strcmp(ufunc_name, "greater") || - !strcmp(ufunc_name, "greater_equal")) { -#if !defined(NPY_PY3K) - if (DEPRECATE("unorderable dtypes; returning scalar but in " - "the future this will be an error") < 0) { - goto fail; - } -#endif - Py_DECREF(out_op[0]); - Py_DECREF(out_op[1]); - return -2; - } - } - - /* Get positional output arguments */ - for (i = nin; i < nargs; ++i) { - obj = PyTuple_GET_ITEM(args, i); - if (_set_out_array(obj, out_op + i) < 0) { - goto fail; - } - } - /* - * Get keyword output and other arguments. - * Raise an error if anything else is present in the - * keyword dictionary. + * Convert most arguments manually here, since it is easier to handle + * the ufunc override if we first parse only to objects. */ - if (kwds != NULL) { - PyObject *key, *value; - Py_ssize_t pos = 0; - while (PyDict_Next(kwds, &pos, &key, &value)) { - Py_ssize_t length = 0; - char *str = NULL; - int bad_arg = 1; - -#if defined(NPY_PY3K) - Py_XDECREF(str_key_obj); - str_key_obj = PyUnicode_AsASCIIString(key); - if (str_key_obj != NULL) { - key = str_key_obj; - } -#endif - - if (PyBytes_AsStringAndSize(key, &str, &length) < 0) { - PyErr_Clear(); - PyErr_SetString(PyExc_TypeError, "invalid keyword argument"); - goto fail; - } - - switch (str[0]) { - case 'a': - /* possible axes argument for generalized ufunc */ - if (out_axes != NULL && strcmp(str, "axes") == 0) { - if (out_axis != NULL && *out_axis != NULL) { - PyErr_SetString(PyExc_TypeError, - "cannot specify both 'axis' and 'axes'"); - goto fail; - } - Py_INCREF(value); - *out_axes = value; - bad_arg = 0; - } - else if (out_axis != NULL && strcmp(str, "axis") == 0) { - if (out_axes != NULL && *out_axes != NULL) { - PyErr_SetString(PyExc_TypeError, - "cannot specify both 'axis' and 'axes'"); - goto fail; - } - Py_INCREF(value); - *out_axis = value; - bad_arg = 0; - } - break; - case 'c': - /* Provides a policy for allowed casting */ - if (strcmp(str, "casting") == 0) { - if (!PyArray_CastingConverter(value, out_casting)) { - goto fail; - } - bad_arg = 0; - } - break; - case 'd': - /* Another way to specify 'sig' */ - if (strcmp(str, "dtype") == 0) { - /* Allow this parameter to be None */ - PyArray_Descr *dtype; - if (!PyArray_DescrConverter2(value, &dtype)) { - goto fail; - } - if (dtype != NULL) { - if (*out_typetup != NULL) { - PyErr_SetString(PyExc_RuntimeError, - "cannot specify both 'signature' and 'dtype'"); - goto fail; - } - *out_typetup = Py_BuildValue("(N)", dtype); - } - bad_arg = 0; - } - break; - case 'e': - /* - * Overrides the global parameters buffer size, - * error mask, and error object - */ - if (strcmp(str, "extobj") == 0) { - Py_INCREF(value); - *out_extobj = value; - bad_arg = 0; - } - break; - case 'k': - if (out_keepdims != NULL && strcmp(str, "keepdims") == 0) { - if (!PyBool_Check(value)) { - PyErr_SetString(PyExc_TypeError, - "'keepdims' must be a boolean"); - goto fail; - } - *out_keepdims = (value == Py_True); - bad_arg = 0; - } - break; - case 'o': - /* - * Output arrays may be specified as a keyword argument, - * either as a single array or None for single output - * ufuncs, or as a tuple of arrays and Nones. - */ - if (strcmp(str, "out") == 0) { - if (nargs > nin) { - PyErr_SetString(PyExc_ValueError, - "cannot specify 'out' as both a " - "positional and keyword argument"); - goto fail; - } - if (PyTuple_CheckExact(value)) { - if (PyTuple_GET_SIZE(value) != nout) { - PyErr_SetString(PyExc_ValueError, - "The 'out' tuple must have exactly " - "one entry per ufunc output"); - goto fail; - } - /* 'out' must be a tuple of arrays and Nones */ - for(i = 0; i < nout; ++i) { - PyObject *val = PyTuple_GET_ITEM(value, i); - if (_set_out_array(val, out_op+nin+i) < 0) { - goto fail; - } - } - } - else if (nout == 1) { - /* Can be an array if it only has one output */ - if (_set_out_array(value, out_op + nin) < 0) { - goto fail; - } - } - else { - /* - * If the deprecated behavior is ever removed, - * keep only the else branch of this if-else - */ - if (PyArray_Check(value) || value == Py_None) { - if (DEPRECATE("passing a single array to the " - "'out' keyword argument of a " - "ufunc with\n" - "more than one output will " - "result in an error in the " - "future") < 0) { - /* The future error message */ - PyErr_SetString(PyExc_TypeError, - "'out' must be a tuple of arrays"); - goto fail; - } - if (_set_out_array(value, out_op+nin) < 0) { - goto fail; - } - } - else { - PyErr_SetString(PyExc_TypeError, - nout > 1 ? "'out' must be a tuple " - "of arrays" : - "'out' must be an array or a " - "tuple of a single array"); - goto fail; - } - } - bad_arg = 0; - } - /* Allows the default output layout to be overridden */ - else if (strcmp(str, "order") == 0) { - if (!PyArray_OrderConverter(value, out_order)) { - goto fail; - } - bad_arg = 0; - } - break; - case 's': - /* Allows a specific function inner loop to be selected */ - if (strcmp(str, "sig") == 0 || - strcmp(str, "signature") == 0) { - if (has_sig == 1) { - PyErr_SetString(PyExc_ValueError, - "cannot specify both 'sig' and 'signature'"); - goto fail; - } - if (*out_typetup != NULL) { - PyErr_SetString(PyExc_RuntimeError, - "cannot specify both 'signature' and 'dtype'"); - goto fail; - } - Py_INCREF(value); - *out_typetup = value; - bad_arg = 0; - has_sig = 1; - } - else if (strcmp(str, "subok") == 0) { - if (!PyBool_Check(value)) { - PyErr_SetString(PyExc_TypeError, - "'subok' must be a boolean"); - goto fail; - } - *out_subok = (value == Py_True); - bad_arg = 0; - } - break; - case 'w': - /* - * Provides a boolean array 'where=' mask if - * out_wheremask is supplied. - */ - if (out_wheremask != NULL && strcmp(str, "where") == 0) { - PyArray_Descr *dtype; - dtype = PyArray_DescrFromType(NPY_BOOL); - if (dtype == NULL) { - goto fail; - } - if (value == Py_True) { - /* - * Optimization: where=True is the same as no - * where argument. This lets us document it as a - * default argument - */ - bad_arg = 0; - break; - } - *out_wheremask = (PyArrayObject *)PyArray_FromAny( - value, dtype, - 0, 0, 0, NULL); - if (*out_wheremask == NULL) { - goto fail; - } - bad_arg = 0; - } - break; - } - - if (bad_arg) { - char *format = "'%s' is an invalid keyword to ufunc '%s'"; - PyErr_Format(PyExc_TypeError, format, str, ufunc_name); - goto fail; - } - } + if (where_obj && !_wheremask_converter(where_obj, out_wheremask)) { + goto fail; + } + if (keepdims_obj && !_keepdims_converter(keepdims_obj, out_keepdims)) { + goto fail; + } + if (casting_obj && !PyArray_CastingConverter(casting_obj, out_casting)) { + goto fail; + } + if (order_obj && !PyArray_OrderConverter(order_obj, out_order)) { + goto fail; + } + if (subok_obj && !_subok_converter(subok_obj, out_subok)) { + goto fail; } - Py_XDECREF(str_key_obj); - return 0; fail: - Py_XDECREF(str_key_obj); - Py_XDECREF(*out_typetup); - Py_XDECREF(*out_extobj); if (out_wheremask != NULL) { - Py_XDECREF(*out_wheremask); - } - if (out_axes != NULL) { - Py_XDECREF(*out_axes); - } - if (out_axis != NULL) { - Py_XDECREF(*out_axis); + Py_XSETREF(*out_wheremask, NULL); } - for (i = 0; i < nop; i++) { - Py_XDECREF(out_op[i]); + for (int i = 0; i < nop; i++) { + Py_XSETREF(out_op[i], NULL); } return -1; } @@ -1082,34 +1055,56 @@ get_ufunc_arguments(PyUFuncObject *ufunc, * -1 if there is an error. */ static int -check_for_trivial_loop(PyUFuncObject *ufunc, - PyArrayObject **op, - PyArray_Descr **dtype, - npy_intp buffersize) +check_for_trivial_loop(PyArrayMethodObject *ufuncimpl, + PyArrayObject **op, PyArray_Descr **dtypes, + NPY_CASTING casting, npy_intp buffersize) { - npy_intp i, nin = ufunc->nin, nop = nin + ufunc->nout; + int force_cast_input = ufuncimpl->flags & _NPY_METH_FORCE_CAST_INPUTS; + int i, nin = ufuncimpl->nin, nop = nin + ufuncimpl->nout; for (i = 0; i < nop; ++i) { /* * If the dtype doesn't match, or the array isn't aligned, * indicate that the trivial loop can't be done. */ - if (op[i] != NULL && - (!PyArray_ISALIGNED(op[i]) || - !PyArray_EquivTypes(dtype[i], PyArray_DESCR(op[i])) - )) { + if (op[i] == NULL) { + continue; + } + int must_copy = !PyArray_ISALIGNED(op[i]); + + if (dtypes[i] != PyArray_DESCR(op[i])) { + NPY_CASTING safety = PyArray_GetCastSafety( + PyArray_DESCR(op[i]), dtypes[i], NULL); + if (safety < 0 && PyErr_Occurred()) { + /* A proper error during a cast check, should be rare */ + return -1; + } + if (!(safety & _NPY_CAST_IS_VIEW)) { + must_copy = 1; + } + + if (force_cast_input && i < nin) { + /* + * ArrayMethod flagged to ignore casting (logical funcs + * can force cast to bool) + */ + } + else if (PyArray_MinCastSafety(safety, casting) != casting) { + return 0; /* the cast is not safe enough */ + } + } + if (must_copy) { /* * If op[j] is a scalar or small one dimensional * array input, make a copy to keep the opportunity - * for a trivial loop. + * for a trivial loop. Outputs are not copied here. */ - if (i < nin && (PyArray_NDIM(op[i]) == 0 || - (PyArray_NDIM(op[i]) == 1 && - PyArray_DIM(op[i],0) <= buffersize))) { + if (i < nin && (PyArray_NDIM(op[i]) == 0 + || (PyArray_NDIM(op[i]) == 1 + && PyArray_DIM(op[i], 0) <= buffersize))) { PyArrayObject *tmp; - Py_INCREF(dtype[i]); - tmp = (PyArrayObject *) - PyArray_CastToType(op[i], dtype[i], 0); + Py_INCREF(dtypes[i]); + tmp = (PyArrayObject *)PyArray_CastToType(op[i], dtypes[i], 0); if (tmp == NULL) { return -1; } @@ -1125,65 +1120,6 @@ check_for_trivial_loop(PyUFuncObject *ufunc, return 1; } -static void -trivial_two_operand_loop(PyArrayObject **op, - PyUFuncGenericFunction innerloop, - void *innerloopdata) -{ - char *data[2]; - npy_intp count[2], stride[2]; - int needs_api; - NPY_BEGIN_THREADS_DEF; - - needs_api = PyDataType_REFCHK(PyArray_DESCR(op[0])) || - PyDataType_REFCHK(PyArray_DESCR(op[1])); - - PyArray_PREPARE_TRIVIAL_PAIR_ITERATION(op[0], op[1], - count[0], - data[0], data[1], - stride[0], stride[1]); - count[1] = count[0]; - NPY_UF_DBG_PRINT1("two operand loop count %d\n", (int)count[0]); - - if (!needs_api) { - NPY_BEGIN_THREADS_THRESHOLDED(count[0]); - } - - innerloop(data, count, stride, innerloopdata); - - NPY_END_THREADS; -} - -static void -trivial_three_operand_loop(PyArrayObject **op, - PyUFuncGenericFunction innerloop, - void *innerloopdata) -{ - char *data[3]; - npy_intp count[3], stride[3]; - int needs_api; - NPY_BEGIN_THREADS_DEF; - - needs_api = PyDataType_REFCHK(PyArray_DESCR(op[0])) || - PyDataType_REFCHK(PyArray_DESCR(op[1])) || - PyDataType_REFCHK(PyArray_DESCR(op[2])); - - PyArray_PREPARE_TRIVIAL_TRIPLE_ITERATION(op[0], op[1], op[2], - count[0], - data[0], data[1], data[2], - stride[0], stride[1], stride[2]); - count[1] = count[0]; - count[2] = count[0]; - NPY_UF_DBG_PRINT1("three operand loop count %d\n", (int)count[0]); - - if (!needs_api) { - NPY_BEGIN_THREADS_THRESHOLDED(count[0]); - } - - innerloop(data, count, stride, innerloopdata); - - NPY_END_THREADS; -} /* * Calls the given __array_prepare__ function on the operand *op, @@ -1257,58 +1193,260 @@ prepare_ufunc_output(PyUFuncObject *ufunc, return 0; } + +/* + * Check whether a trivial loop is possible and call the innerloop if it is. + * A trivial loop is defined as one where a single strided inner-loop call + * is possible. + * + * This function only supports a single output (due to the overlap check). + * It always accepts 0-D arrays and will broadcast them. The function + * cannot broadcast any other array (as it requires a single stride). + * The function accepts all 1-D arrays, and N-D arrays that are either all + * C- or all F-contiguous. + * + * Returns -2 if a trivial loop is not possible, 0 on success and -1 on error. + */ static int -iterator_loop(PyUFuncObject *ufunc, - PyArrayObject **op, - PyArray_Descr **dtype, - NPY_ORDER order, - npy_intp buffersize, - PyObject **arr_prep, - ufunc_full_args full_args, - PyUFuncGenericFunction innerloop, - void *innerloopdata) +try_trivial_single_output_loop(PyArrayMethod_Context *context, + PyArrayObject *op[], NPY_ORDER order, + PyObject *arr_prep[], ufunc_full_args full_args, + int errormask, PyObject *extobj) { - npy_intp i, nin = ufunc->nin, nout = ufunc->nout; - npy_intp nop = nin + nout; - npy_uint32 op_flags[NPY_MAXARGS]; - NpyIter *iter; - char *baseptrs[NPY_MAXARGS]; + int nin = context->method->nin; + int nop = nin + 1; + assert(context->method->nout == 1); - NpyIter_IterNextFunc *iternext; - char **dataptr; - npy_intp *stride; - npy_intp *count_ptr; + /* The order of all N-D contiguous operands, can be fixed by `order` */ + int operation_order = 0; + if (order == NPY_CORDER) { + operation_order = NPY_ARRAY_C_CONTIGUOUS; + } + else if (order == NPY_FORTRANORDER) { + operation_order = NPY_ARRAY_F_CONTIGUOUS; + } - PyArrayObject **op_it; - npy_uint32 iter_flags; - int retval; + int operation_ndim = 0; + npy_intp *operation_shape = NULL; + npy_intp fixed_strides[NPY_MAXARGS]; + + for (int iop = 0; iop < nop; iop++) { + if (op[iop] == NULL) { + /* The out argument may be NULL (and only that one); fill later */ + assert(iop == nin); + continue; + } + + int op_ndim = PyArray_NDIM(op[iop]); + + /* Special case 0-D since we can handle broadcasting using a 0-stride */ + if (op_ndim == 0) { + fixed_strides[iop] = 0; + continue; + } + + /* First non 0-D op: fix dimensions, shape (order is fixed later) */ + if (operation_ndim == 0) { + operation_ndim = op_ndim; + operation_shape = PyArray_SHAPE(op[iop]); + } + else if (op_ndim != operation_ndim) { + return -2; /* dimension mismatch (except 0-d ops) */ + } + else if (!PyArray_CompareLists( + operation_shape, PyArray_DIMS(op[iop]), op_ndim)) { + return -2; /* shape mismatch */ + } + + if (op_ndim == 1) { + fixed_strides[iop] = PyArray_STRIDES(op[iop])[0]; + } + else { + fixed_strides[iop] = PyArray_ITEMSIZE(op[iop]); /* contiguous */ + + /* This op must match the operation order (and be contiguous) */ + int op_order = (PyArray_FLAGS(op[iop]) & + (NPY_ARRAY_C_CONTIGUOUS|NPY_ARRAY_F_CONTIGUOUS)); + if (op_order == 0) { + return -2; /* N-dimensional op must be contiguous */ + } + else if (operation_order == 0) { + operation_order = op_order; /* op fixes order */ + } + else if (operation_order != op_order) { + return -2; + } + } + } + + if (op[nin] == NULL) { + Py_INCREF(context->descriptors[nin]); + op[nin] = (PyArrayObject *) PyArray_NewFromDescr(&PyArray_Type, + context->descriptors[nin], operation_ndim, operation_shape, + NULL, NULL, operation_order==NPY_ARRAY_F_CONTIGUOUS, NULL); + if (op[nin] == NULL) { + return -1; + } + fixed_strides[nin] = context->descriptors[nin]->elsize; + } + else { + /* If any input overlaps with the output, we use the full path. */ + for (int iop = 0; iop < nin; iop++) { + if (!PyArray_EQUIVALENTLY_ITERABLE_OVERLAP_OK( + op[iop], op[nin], + PyArray_TRIVIALLY_ITERABLE_OP_READ, + PyArray_TRIVIALLY_ITERABLE_OP_NOREAD)) { + return -2; + } + } + /* Check self-overlap (non 1-D are contiguous, perfect overlap is OK) */ + if (operation_ndim == 1 && + PyArray_STRIDES(op[nin])[0] < PyArray_ITEMSIZE(op[nin]) && + PyArray_STRIDES(op[nin])[0] != 0) { + return -2; + } + } + + /* Call the __prepare_array__ if necessary */ + if (prepare_ufunc_output((PyUFuncObject *)context->caller, &op[nin], + arr_prep[0], full_args, 0) < 0) { + return -1; + } + /* + * We can use the trivial (single inner-loop call) optimization + * and `fixed_strides` holds the strides for that call. + */ + char *data[NPY_MAXARGS]; + npy_intp count = PyArray_MultiplyList(operation_shape, operation_ndim); NPY_BEGIN_THREADS_DEF; - /* Set up the flags */ - for (i = 0; i < nin; ++i) { - op_flags[i] = NPY_ITER_READONLY | - NPY_ITER_ALIGNED | - NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE; + PyArrayMethod_StridedLoop *strided_loop; + NpyAuxData *auxdata = NULL; + NPY_ARRAYMETHOD_FLAGS flags = 0; + if (context->method->get_strided_loop(context, + 1, 0, fixed_strides, + &strided_loop, &auxdata, &flags) < 0) { + return -1; + } + for (int iop=0; iop < nop; iop++) { + data[iop] = PyArray_BYTES(op[iop]); + } + + if (!(flags & NPY_METH_NO_FLOATINGPOINT_ERRORS)) { + npy_clear_floatstatus_barrier((char *)context); + } + if (!(flags & NPY_METH_REQUIRES_PYAPI)) { + NPY_BEGIN_THREADS_THRESHOLDED(count); + } + + int res = strided_loop(context, data, &count, fixed_strides, auxdata); + + NPY_END_THREADS; + NPY_AUXDATA_FREE(auxdata); + /* + * An error should only be possible if `res != 0` is already set. + * But this is not strictly correct for old-style ufuncs (e.g. `power` + * released the GIL but manually set an Exception). + */ + if (PyErr_Occurred()) { + res = -1; + } + + if (res == 0 && !(flags & NPY_METH_NO_FLOATINGPOINT_ERRORS)) { + /* NOTE: We could check float errors even when `res < 0` */ + const char *name = ufunc_get_name_cstr((PyUFuncObject *)context->caller); + res = _check_ufunc_fperr(errormask, extobj, name); + } + return res; +} + + +/* + * Check casting: It would be nice to just move this into the iterator + * or pass in the full cast information. But this can special case + * the logical functions and prints a better error message. + */ +static NPY_INLINE int +validate_casting(PyArrayMethodObject *method, PyUFuncObject *ufunc, + PyArrayObject *ops[], PyArray_Descr *descriptors[], + NPY_CASTING casting) +{ + if (method->resolve_descriptors == &wrapped_legacy_resolve_descriptors) { /* - * If READWRITE flag has been set for this operand, - * then clear default READONLY flag + * In this case the legacy type resolution was definitely called + * and we do not need to check (astropy/pyerfa relied on this). */ - op_flags[i] |= ufunc->op_flags[i]; - if (op_flags[i] & (NPY_ITER_READWRITE | NPY_ITER_WRITEONLY)) { - op_flags[i] &= ~NPY_ITER_READONLY; + return 0; + } + if (method->flags & _NPY_METH_FORCE_CAST_INPUTS) { + if (PyUFunc_ValidateOutCasting(ufunc, casting, ops, descriptors) < 0) { + return -1; } } - for (i = nin; i < nop; ++i) { - op_flags[i] = NPY_ITER_WRITEONLY | - NPY_ITER_ALIGNED | - NPY_ITER_ALLOCATE | - NPY_ITER_NO_BROADCAST | - NPY_ITER_NO_SUBTYPE | - NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE; + else { + if (PyUFunc_ValidateCasting(ufunc, casting, ops, descriptors) < 0) { + return -1; + } } + return 0; +} - iter_flags = ufunc->iter_flags | + +/* + * The ufunc loop implementation for both normal ufunc calls and masked calls + * when the iterator has to be used. + * + * See `PyUFunc_GenericFunctionInternal` for more information (where this is + * called from). + */ +static int +execute_ufunc_loop(PyArrayMethod_Context *context, int masked, + PyArrayObject **op, NPY_ORDER order, npy_intp buffersize, + NPY_CASTING casting, + PyObject **arr_prep, ufunc_full_args full_args, + npy_uint32 *op_flags, int errormask, PyObject *extobj) +{ + PyUFuncObject *ufunc = (PyUFuncObject *)context->caller; + int nin = context->method->nin, nout = context->method->nout; + int nop = nin + nout; + + if (validate_casting(context->method, + ufunc, op, context->descriptors, casting) < 0) { + return -1; + } + + if (masked) { + assert(PyArray_TYPE(op[nop]) == NPY_BOOL); + if (ufunc->_always_null_previously_masked_innerloop_selector != NULL) { + if (PyErr_WarnFormat(PyExc_UserWarning, 1, + "The ufunc %s has a custom masked-inner-loop-selector." + "NumPy assumes that this is NEVER used. If you do make " + "use of this please notify the NumPy developers to discuss " + "future solutions. (See NEP 41 and 43)\n" + "NumPy will continue, but ignore the custom loop selector. " + "This should only affect performance.", + ufunc_get_name_cstr(ufunc)) < 0) { + return -1; + } + } + + /* + * NOTE: In the masked version, we consider the output read-write, + * this gives a best-effort of preserving the input, but does + * not always work. It could allow the operand to be copied + * due to copy-if-overlap, but only if it was passed in. + * In that case `__array_prepare__` is called before it happens. + */ + for (int i = nin; i < nop; ++i) { + op_flags[i] |= (op[i] != NULL ? NPY_ITER_READWRITE : NPY_ITER_WRITEONLY); + } + op_flags[nop] = NPY_ITER_READONLY | NPY_ITER_ARRAYMASK; /* mask */ + } + + NPY_UF_DBG_PRINT("Making iterator\n"); + + npy_uint32 iter_flags = ufunc->iter_flags | NPY_ITER_EXTERNAL_LOOP | NPY_ITER_REFS_OK | NPY_ITER_ZEROSIZE_OK | @@ -1317,16 +1455,17 @@ iterator_loop(PyUFuncObject *ufunc, NPY_ITER_DELAY_BUFALLOC | NPY_ITER_COPY_IF_OVERLAP; - /* Call the __array_prepare__ functions for already existing output arrays. + /* + * Call the __array_prepare__ functions for already existing output arrays. * Do this before creating the iterator, as the iterator may UPDATEIFCOPY * some of them. */ - for (i = 0; i < nout; ++i) { + for (int i = 0; i < nout; i++) { if (op[nin+i] == NULL) { continue; } if (prepare_ufunc_output(ufunc, &op[nin+i], - arr_prep[i], full_args, i) < 0) { + arr_prep[i], full_args, i) < 0) { return -1; } } @@ -1336,26 +1475,29 @@ iterator_loop(PyUFuncObject *ufunc, * were already checked, we use the casting rule 'unsafe' which * is faster to calculate. */ - iter = NpyIter_AdvancedNew(nop, op, + NpyIter *iter = NpyIter_AdvancedNew(nop + masked, op, iter_flags, order, NPY_UNSAFE_CASTING, - op_flags, dtype, + op_flags, context->descriptors, -1, NULL, NULL, buffersize); if (iter == NULL) { return -1; } - /* Copy any allocated outputs */ - op_it = NpyIter_GetOperandArray(iter); - for (i = 0; i < nout; ++i) { - if (op[nin+i] == NULL) { - op[nin+i] = op_it[nin+i]; - Py_INCREF(op[nin+i]); + NPY_UF_DBG_PRINT("Made iterator\n"); + + /* Call the __array_prepare__ functions for newly allocated arrays */ + PyArrayObject **op_it = NpyIter_GetOperandArray(iter); + char *baseptrs[NPY_MAXARGS]; + + for (int i = 0; i < nout; ++i) { + if (op[nin + i] == NULL) { + op[nin + i] = op_it[nin + i]; + Py_INCREF(op[nin + i]); /* Call the __array_prepare__ functions for the new array */ - if (prepare_ufunc_output(ufunc, &op[nin+i], - arr_prep[i], full_args, i) < 0) { - NpyIter_Close(iter); + if (prepare_ufunc_output(ufunc, + &op[nin + i], arr_prep[i], full_args, i) < 0) { NpyIter_Deallocate(iter); return -1; } @@ -1370,535 +1512,170 @@ iterator_loop(PyUFuncObject *ufunc, * with other operands --- the op[nin+i] array passed to it is newly * allocated and doesn't have any overlap. */ - baseptrs[nin+i] = PyArray_BYTES(op[nin+i]); + baseptrs[nin + i] = PyArray_BYTES(op[nin + i]); } else { - baseptrs[nin+i] = PyArray_BYTES(op_it[nin+i]); + baseptrs[nin + i] = PyArray_BYTES(op_it[nin + i]); } } - /* Only do the loop if the iteration size is non-zero */ - if (NpyIter_GetIterSize(iter) != 0) { - /* Reset the iterator with the base pointers from possible __array_prepare__ */ - for (i = 0; i < nin; ++i) { - baseptrs[i] = PyArray_BYTES(op_it[i]); + npy_intp full_size = NpyIter_GetIterSize(iter); + if (full_size == 0) { + if (!NpyIter_Deallocate(iter)) { + return -1; } - if (NpyIter_ResetBasePointers(iter, baseptrs, NULL) != NPY_SUCCEED) { - NpyIter_Close(iter); + return 0; + } + + /* + * Reset the iterator with the base pointers possibly modified by + * `__array_prepare__`. + */ + for (int i = 0; i < nin; i++) { + baseptrs[i] = PyArray_BYTES(op_it[i]); + } + if (masked) { + baseptrs[nop] = PyArray_BYTES(op_it[nop]); + } + if (NpyIter_ResetBasePointers(iter, baseptrs, NULL) != NPY_SUCCEED) { + NpyIter_Deallocate(iter); + return -1; + } + + /* + * Get the inner loop, with the possibility of specialization + * based on the fixed strides. + */ + PyArrayMethod_StridedLoop *strided_loop; + NpyAuxData *auxdata; + npy_intp fixed_strides[NPY_MAXARGS]; + + NpyIter_GetInnerFixedStrideArray(iter, fixed_strides); + NPY_ARRAYMETHOD_FLAGS flags = 0; + if (masked) { + if (PyArrayMethod_GetMaskedStridedLoop(context, + 1, fixed_strides, &strided_loop, &auxdata, &flags) < 0) { NpyIter_Deallocate(iter); return -1; } - - /* Get the variables needed for the loop */ - iternext = NpyIter_GetIterNext(iter, NULL); - if (iternext == NULL) { - NpyIter_Close(iter); + } + else { + if (context->method->get_strided_loop(context, + 1, 0, fixed_strides, &strided_loop, &auxdata, &flags) < 0) { NpyIter_Deallocate(iter); return -1; } - dataptr = NpyIter_GetDataPtrArray(iter); - stride = NpyIter_GetInnerStrideArray(iter); - count_ptr = NpyIter_GetInnerLoopSizePtr(iter); - - NPY_BEGIN_THREADS_NDITER(iter); - - /* Execute the loop */ - do { - NPY_UF_DBG_PRINT1("iterator loop count %d\n", (int)*count_ptr); - innerloop(dataptr, count_ptr, stride, innerloopdata); - } while (iternext(iter)); - - NPY_END_THREADS; } - retval = NpyIter_Close(iter); - NpyIter_Deallocate(iter); - return retval; -} -/* - * trivial_loop_ok - 1 if no alignment, data conversion, etc required - * nin - number of inputs - * nout - number of outputs - * op - the operands (nin + nout of them) - * order - the loop execution order/output memory order - * buffersize - how big of a buffer to use - * arr_prep - the __array_prepare__ functions for the outputs - * innerloop - the inner loop function - * innerloopdata - data to pass to the inner loop - */ -static int -execute_legacy_ufunc_loop(PyUFuncObject *ufunc, - int trivial_loop_ok, - PyArrayObject **op, - PyArray_Descr **dtypes, - NPY_ORDER order, - npy_intp buffersize, - PyObject **arr_prep, - ufunc_full_args full_args) -{ - npy_intp nin = ufunc->nin, nout = ufunc->nout; - PyUFuncGenericFunction innerloop; - void *innerloopdata; - int needs_api = 0; - - if (ufunc->legacy_inner_loop_selector(ufunc, dtypes, - &innerloop, &innerloopdata, &needs_api) < 0) { + /* Get the variables needed for the loop */ + NpyIter_IterNextFunc *iternext = NpyIter_GetIterNext(iter, NULL); + if (iternext == NULL) { + NPY_AUXDATA_FREE(auxdata); + NpyIter_Deallocate(iter); return -1; } - /* If the loop wants the arrays, provide them. */ - if (_does_loop_use_arrays(innerloopdata)) { - innerloopdata = (void*)op; - } - - /* First check for the trivial cases that don't need an iterator */ - if (trivial_loop_ok) { - if (nin == 1 && nout == 1) { - if (op[1] == NULL && - (order == NPY_ANYORDER || order == NPY_KEEPORDER) && - PyArray_TRIVIALLY_ITERABLE(op[0])) { - Py_INCREF(dtypes[1]); - op[1] = (PyArrayObject *)PyArray_NewFromDescr(&PyArray_Type, - dtypes[1], - PyArray_NDIM(op[0]), - PyArray_DIMS(op[0]), - NULL, NULL, - PyArray_ISFORTRAN(op[0]) ? - NPY_ARRAY_F_CONTIGUOUS : 0, - NULL); - if (op[1] == NULL) { - return -1; - } - - /* Call the __prepare_array__ if necessary */ - if (prepare_ufunc_output(ufunc, &op[1], - arr_prep[0], full_args, 0) < 0) { - return -1; - } - - NPY_UF_DBG_PRINT("trivial 1 input with allocated output\n"); - trivial_two_operand_loop(op, innerloop, innerloopdata); - - return 0; - } - else if (op[1] != NULL && - PyArray_NDIM(op[1]) >= PyArray_NDIM(op[0]) && - PyArray_TRIVIALLY_ITERABLE_PAIR(op[0], op[1], - PyArray_TRIVIALLY_ITERABLE_OP_READ, - PyArray_TRIVIALLY_ITERABLE_OP_NOREAD)) { - - /* Call the __prepare_array__ if necessary */ - if (prepare_ufunc_output(ufunc, &op[1], - arr_prep[0], full_args, 0) < 0) { - return -1; - } - - NPY_UF_DBG_PRINT("trivial 1 input\n"); - trivial_two_operand_loop(op, innerloop, innerloopdata); - - return 0; - } - } - else if (nin == 2 && nout == 1) { - if (op[2] == NULL && - (order == NPY_ANYORDER || order == NPY_KEEPORDER) && - PyArray_TRIVIALLY_ITERABLE_PAIR(op[0], op[1], - PyArray_TRIVIALLY_ITERABLE_OP_READ, - PyArray_TRIVIALLY_ITERABLE_OP_READ)) { - PyArrayObject *tmp; - /* - * Have to choose the input with more dimensions to clone, as - * one of them could be a scalar. - */ - if (PyArray_NDIM(op[0]) >= PyArray_NDIM(op[1])) { - tmp = op[0]; - } - else { - tmp = op[1]; - } - Py_INCREF(dtypes[2]); - op[2] = (PyArrayObject *)PyArray_NewFromDescr(&PyArray_Type, - dtypes[2], - PyArray_NDIM(tmp), - PyArray_DIMS(tmp), - NULL, NULL, - PyArray_ISFORTRAN(tmp) ? - NPY_ARRAY_F_CONTIGUOUS : 0, - NULL); - if (op[2] == NULL) { - return -1; - } + char **dataptr = NpyIter_GetDataPtrArray(iter); + npy_intp *strides = NpyIter_GetInnerStrideArray(iter); + npy_intp *countptr = NpyIter_GetInnerLoopSizePtr(iter); + int needs_api = NpyIter_IterationNeedsAPI(iter); - /* Call the __prepare_array__ if necessary */ - if (prepare_ufunc_output(ufunc, &op[2], - arr_prep[0], full_args, 0) < 0) { - return -1; - } - - NPY_UF_DBG_PRINT("trivial 2 input with allocated output\n"); - trivial_three_operand_loop(op, innerloop, innerloopdata); - - return 0; - } - else if (op[2] != NULL && - PyArray_NDIM(op[2]) >= PyArray_NDIM(op[0]) && - PyArray_NDIM(op[2]) >= PyArray_NDIM(op[1]) && - PyArray_TRIVIALLY_ITERABLE_TRIPLE(op[0], op[1], op[2], - PyArray_TRIVIALLY_ITERABLE_OP_READ, - PyArray_TRIVIALLY_ITERABLE_OP_READ, - PyArray_TRIVIALLY_ITERABLE_OP_NOREAD)) { - - /* Call the __prepare_array__ if necessary */ - if (prepare_ufunc_output(ufunc, &op[2], - arr_prep[0], full_args, 0) < 0) { - return -1; - } - - NPY_UF_DBG_PRINT("trivial 2 input\n"); - trivial_three_operand_loop(op, innerloop, innerloopdata); + NPY_BEGIN_THREADS_DEF; - return 0; - } - } + if (!(flags & NPY_METH_NO_FLOATINGPOINT_ERRORS)) { + npy_clear_floatstatus_barrier((char *)context); } - - /* - * If no trivial loop matched, an iterator is required to - * resolve broadcasting, etc - */ - - NPY_UF_DBG_PRINT("iterator loop\n"); - if (iterator_loop(ufunc, op, dtypes, order, - buffersize, arr_prep, full_args, - innerloop, innerloopdata) < 0) { - return -1; + if (!needs_api && !(flags & NPY_METH_REQUIRES_PYAPI)) { + NPY_BEGIN_THREADS_THRESHOLDED(full_size); } - return 0; -} - -/* - * nin - number of inputs - * nout - number of outputs - * wheremask - if not NULL, the 'where=' parameter to the ufunc. - * op - the operands (nin + nout of them) - * order - the loop execution order/output memory order - * buffersize - how big of a buffer to use - * arr_prep - the __array_prepare__ functions for the outputs - * innerloop - the inner loop function - * innerloopdata - data to pass to the inner loop - */ -static int -execute_fancy_ufunc_loop(PyUFuncObject *ufunc, - PyArrayObject *wheremask, - PyArrayObject **op, - PyArray_Descr **dtypes, - NPY_ORDER order, - npy_intp buffersize, - PyObject **arr_prep, - ufunc_full_args full_args) -{ - int retval, i, nin = ufunc->nin, nout = ufunc->nout; - int nop = nin + nout; - npy_uint32 op_flags[NPY_MAXARGS]; - NpyIter *iter; - int needs_api; - npy_intp default_op_in_flags = 0, default_op_out_flags = 0; - - NpyIter_IterNextFunc *iternext; - char **dataptr; - npy_intp *strides; - npy_intp *countptr; - - PyArrayObject **op_it; - npy_uint32 iter_flags; + NPY_UF_DBG_PRINT("Actual inner loop:\n"); + /* Execute the loop */ + int res; + do { + NPY_UF_DBG_PRINT1("iterator loop count %d\n", (int)*countptr); + res = strided_loop(context, dataptr, countptr, strides, auxdata); + } while (res == 0 && iternext(iter)); - if (wheremask != NULL) { - if (nop + 1 > NPY_MAXARGS) { - PyErr_SetString(PyExc_ValueError, - "Too many operands when including where= parameter"); - return -1; - } - op[nop] = wheremask; - dtypes[nop] = NULL; - default_op_out_flags |= NPY_ITER_WRITEMASKED; - } + NPY_END_THREADS; + NPY_AUXDATA_FREE(auxdata); - /* Set up the flags */ - for (i = 0; i < nin; ++i) { - op_flags[i] = default_op_in_flags | - NPY_ITER_READONLY | - NPY_ITER_ALIGNED | - NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE; - /* - * If READWRITE flag has been set for this operand, - * then clear default READONLY flag - */ - op_flags[i] |= ufunc->op_flags[i]; - if (op_flags[i] & (NPY_ITER_READWRITE | NPY_ITER_WRITEONLY)) { - op_flags[i] &= ~NPY_ITER_READONLY; - } - } - for (i = nin; i < nop; ++i) { - /* - * We don't write to all elements, and the iterator may make - * UPDATEIFCOPY temporary copies. The output arrays (unless they are - * allocated by the iterator itself) must be considered READWRITE by the - * iterator, so that the elements we don't write to are copied to the - * possible temporary array. - */ - op_flags[i] = default_op_out_flags | - (op[i] != NULL ? NPY_ITER_READWRITE : NPY_ITER_WRITEONLY) | - NPY_ITER_ALIGNED | - NPY_ITER_ALLOCATE | - NPY_ITER_NO_BROADCAST | - NPY_ITER_NO_SUBTYPE | - NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE; + if (res == 0 && !(flags & NPY_METH_NO_FLOATINGPOINT_ERRORS)) { + /* NOTE: We could check float errors even when `res < 0` */ + const char *name = ufunc_get_name_cstr((PyUFuncObject *)context->caller); + res = _check_ufunc_fperr(errormask, extobj, name); } - if (wheremask != NULL) { - op_flags[nop] = NPY_ITER_READONLY | NPY_ITER_ARRAYMASK; - } - - NPY_UF_DBG_PRINT("Making iterator\n"); - - iter_flags = ufunc->iter_flags | - NPY_ITER_EXTERNAL_LOOP | - NPY_ITER_REFS_OK | - NPY_ITER_ZEROSIZE_OK | - NPY_ITER_BUFFERED | - NPY_ITER_GROWINNER | - NPY_ITER_COPY_IF_OVERLAP; - /* - * Allocate the iterator. Because the types of the inputs - * were already checked, we use the casting rule 'unsafe' which - * is faster to calculate. - */ - iter = NpyIter_AdvancedNew(nop + ((wheremask != NULL) ? 1 : 0), op, - iter_flags, - order, NPY_UNSAFE_CASTING, - op_flags, dtypes, - -1, NULL, NULL, buffersize); - if (iter == NULL) { + if (!NpyIter_Deallocate(iter)) { return -1; } - - NPY_UF_DBG_PRINT("Made iterator\n"); - - needs_api = NpyIter_IterationNeedsAPI(iter); - - /* Call the __array_prepare__ functions where necessary */ - op_it = NpyIter_GetOperandArray(iter); - for (i = nin; i < nop; ++i) { - PyArrayObject *op_tmp, *orig_op_tmp; - - /* - * The array can be allocated by the iterator -- it is placed in op[i] - * and returned to the caller, and this needs an extra incref. - */ - if (op[i] == NULL) { - op_tmp = op_it[i]; - Py_INCREF(op_tmp); - } - else { - op_tmp = op[i]; - } - - /* prepare_ufunc_output may decref & replace the pointer */ - orig_op_tmp = op_tmp; - Py_INCREF(op_tmp); - - if (prepare_ufunc_output(ufunc, &op_tmp, - arr_prep[i], full_args, i) < 0) { - NpyIter_Close(iter); - NpyIter_Deallocate(iter); - return -1; - } - - /* Validate that the prepare_ufunc_output didn't mess with pointers */ - if (PyArray_BYTES(op_tmp) != PyArray_BYTES(orig_op_tmp)) { - PyErr_SetString(PyExc_ValueError, - "The __array_prepare__ functions modified the data " - "pointer addresses in an invalid fashion"); - Py_DECREF(op_tmp); - NpyIter_Close(iter); - NpyIter_Deallocate(iter); - return -1; - } - - /* - * Put the updated operand back and undo the DECREF above. If - * COPY_IF_OVERLAP made a temporary copy, the output will be copied - * by UPDATEIFCOPY even if op[i] was changed by prepare_ufunc_output. - */ - op[i] = op_tmp; - Py_DECREF(op_tmp); - } - - /* Only do the loop if the iteration size is non-zero */ - if (NpyIter_GetIterSize(iter) != 0) { - PyUFunc_MaskedStridedInnerLoopFunc *innerloop; - NpyAuxData *innerloopdata; - npy_intp fixed_strides[2*NPY_MAXARGS]; - PyArray_Descr **iter_dtypes; - NPY_BEGIN_THREADS_DEF; - - /* - * Get the inner loop, with the possibility of specialization - * based on the fixed strides. - */ - NpyIter_GetInnerFixedStrideArray(iter, fixed_strides); - iter_dtypes = NpyIter_GetDescrArray(iter); - if (ufunc->masked_inner_loop_selector(ufunc, dtypes, - wheremask != NULL ? iter_dtypes[nop] - : iter_dtypes[nop + nin], - fixed_strides, - wheremask != NULL ? fixed_strides[nop] - : fixed_strides[nop + nin], - &innerloop, &innerloopdata, &needs_api) < 0) { - NpyIter_Close(iter); - NpyIter_Deallocate(iter); - return -1; - } - - /* Get the variables needed for the loop */ - iternext = NpyIter_GetIterNext(iter, NULL); - if (iternext == NULL) { - NpyIter_Close(iter); - NpyIter_Deallocate(iter); - return -1; - } - dataptr = NpyIter_GetDataPtrArray(iter); - strides = NpyIter_GetInnerStrideArray(iter); - countptr = NpyIter_GetInnerLoopSizePtr(iter); - - NPY_BEGIN_THREADS_NDITER(iter); - - NPY_UF_DBG_PRINT("Actual inner loop:\n"); - /* Execute the loop */ - do { - NPY_UF_DBG_PRINT1("iterator loop count %d\n", (int)*countptr); - innerloop(dataptr, strides, - dataptr[nop], strides[nop], - *countptr, innerloopdata); - } while (iternext(iter)); - - NPY_END_THREADS; - - NPY_AUXDATA_FREE(innerloopdata); - } - - retval = NpyIter_Close(iter); - NpyIter_Deallocate(iter); - return retval; + return res; } -static npy_bool -tuple_all_none(PyObject *tup) { - npy_intp i; - for (i = 0; i < PyTuple_GET_SIZE(tup); ++i) { - if (PyTuple_GET_ITEM(tup, i) != Py_None) { - return NPY_FALSE; - } - } - return NPY_TRUE; -} /* - * Convert positional args and the out kwarg into an input and output tuple. - * - * If the output tuple would be all None, return NULL instead. - * - * This duplicates logic in many places, so further refactoring is needed: - * - get_ufunc_arguments - * - PyUFunc_WithOverride - * - normalize___call___args + * Validate that operands have enough dimensions, accounting for + * possible flexible dimensions that may be absent. */ static int -make_full_arg_tuple( - ufunc_full_args *full_args, - npy_intp nin, npy_intp nout, - PyObject *args, PyObject *kwds) -{ - PyObject *out_kwd = NULL; - npy_intp nargs = PyTuple_GET_SIZE(args); - npy_intp i; - - /* This should have been checked by the caller */ - assert(nin <= nargs && nargs <= nin + nout); - - /* Initialize so we can XDECREF safely */ - full_args->in = NULL; - full_args->out = NULL; - - /* Get the input arguments*/ - full_args->in = PyTuple_GetSlice(args, 0, nin); - if (full_args->in == NULL) { - goto fail; - } - - /* Look for output keyword arguments */ - out_kwd = kwds ? PyDict_GetItem(kwds, npy_um_str_out) : NULL; +_validate_num_dims(PyUFuncObject *ufunc, PyArrayObject **op, + npy_uint32 *core_dim_flags, + int *op_core_num_dims) { + int i, j; + int nin = ufunc->nin; + int nop = ufunc->nargs; - if (out_kwd != NULL) { - assert(nargs == nin); - if (out_kwd == Py_None) { - return 0; - } - else if (PyTuple_Check(out_kwd)) { - assert(PyTuple_GET_SIZE(out_kwd) == nout); - if (tuple_all_none(out_kwd)) { - return 0; - } - Py_INCREF(out_kwd); - full_args->out = out_kwd; - return 0; - } - else { - /* A single argument x is promoted to (x, None, None ...) */ - full_args->out = PyTuple_New(nout); - if (full_args->out == NULL) { - goto fail; - } - Py_INCREF(out_kwd); - PyTuple_SET_ITEM(full_args->out, 0, out_kwd); - for (i = 1; i < nout; ++i) { - Py_INCREF(Py_None); - PyTuple_SET_ITEM(full_args->out, i, Py_None); + for (i = 0; i < nop; i++) { + if (op[i] != NULL) { + int op_ndim = PyArray_NDIM(op[i]); + + if (op_ndim < op_core_num_dims[i]) { + int core_offset = ufunc->core_offsets[i]; + /* We've too few, but some dimensions might be flexible */ + for (j = core_offset; + j < core_offset + ufunc->core_num_dims[i]; j++) { + int core_dim_index = ufunc->core_dim_ixs[j]; + if ((core_dim_flags[core_dim_index] & + UFUNC_CORE_DIM_CAN_IGNORE)) { + int i1, j1, k; + /* + * Found a dimension that can be ignored. Flag that + * it is missing, and unflag that it can be ignored, + * since we are doing so already. + */ + core_dim_flags[core_dim_index] |= UFUNC_CORE_DIM_MISSING; + core_dim_flags[core_dim_index] ^= UFUNC_CORE_DIM_CAN_IGNORE; + /* + * Reduce the number of core dimensions for all + * operands that use this one (including ours), + * and check whether we're now OK. + */ + for (i1 = 0, k=0; i1 < nop; i1++) { + for (j1 = 0; j1 < ufunc->core_num_dims[i1]; j1++) { + if (ufunc->core_dim_ixs[k++] == core_dim_index) { + op_core_num_dims[i1]--; + } + } + } + if (op_ndim == op_core_num_dims[i]) { + break; + } + } + } + if (op_ndim < op_core_num_dims[i]) { + PyErr_Format(PyExc_ValueError, + "%s: %s operand %d does not have enough " + "dimensions (has %d, gufunc core with " + "signature %s requires %d)", + ufunc_get_name_cstr(ufunc), + i < nin ? "Input" : "Output", + i < nin ? i : i - nin, PyArray_NDIM(op[i]), + ufunc->core_signature, op_core_num_dims[i]); + return -1; + } } - return 0; } } - - /* No outputs in kwargs; if also none in args, we're done */ - if (nargs == nin) { - return 0; - } - /* copy across positional output arguments, adding trailing Nones */ - full_args->out = PyTuple_New(nout); - if (full_args->out == NULL) { - goto fail; - } - for (i = nin; i < nargs; ++i) { - PyObject *item = PyTuple_GET_ITEM(args, i); - Py_INCREF(item); - PyTuple_SET_ITEM(full_args->out, i - nin, item); - } - for (i = nargs; i < nin + nout; ++i) { - Py_INCREF(Py_None); - PyTuple_SET_ITEM(full_args->out, i - nin, Py_None); - } - - /* don't return a tuple full of None */ - if (tuple_all_none(full_args->out)) { - Py_DECREF(full_args->out); - full_args->out = NULL; - } return 0; - -fail: - Py_XDECREF(full_args->in); - Py_XDECREF(full_args->out); - return -1; } /* @@ -1974,7 +1751,7 @@ _check_keepdims_support(PyUFuncObject *ufunc) { * Returns 0 on success, and -1 on failure */ static int -_parse_axes_arg(PyUFuncObject *ufunc, int core_num_dims[], PyObject *axes, +_parse_axes_arg(PyUFuncObject *ufunc, int op_core_num_dims[], PyObject *axes, PyArrayObject **op, int broadcast_ndim, int **remap_axis) { int nin = ufunc->nin; int nop = ufunc->nargs; @@ -2004,7 +1781,7 @@ _parse_axes_arg(PyUFuncObject *ufunc, int core_num_dims[], PyObject *axes, PyObject *op_axes_tuple, *axis_item; int axis, op_axis; - op_ncore = core_num_dims[iop]; + op_ncore = op_core_num_dims[iop]; if (op[iop] != NULL) { op_ndim = PyArray_NDIM(op[iop]); op_nbroadcast = op_ndim - op_ncore; @@ -2104,7 +1881,7 @@ _parse_axes_arg(PyUFuncObject *ufunc, int core_num_dims[], PyObject *axes, * Returns 0 on success, and -1 on failure */ static int -_parse_axis_arg(PyUFuncObject *ufunc, int core_num_dims[], PyObject *axis, +_parse_axis_arg(PyUFuncObject *ufunc, const int core_num_dims[], PyObject *axis, PyArrayObject **op, int broadcast_ndim, int **remap_axis) { int nop = ufunc->nargs; int iop, axis_int; @@ -2158,57 +1935,72 @@ _parse_axis_arg(PyUFuncObject *ufunc, int core_num_dims[], PyObject *axis, * * Returns 0 on success, and -1 on failure * - * The behavior has been changed in NumPy 1.10.0, and the following + * The behavior has been changed in NumPy 1.16.0, and the following * requirements must be fulfilled or an error will be raised: * * Arguments, both input and output, must have at least as many * dimensions as the corresponding number of core dimensions. In - * previous versions, 1's were prepended to the shape as needed. + * versions before 1.10, 1's were prepended to the shape as needed. * * Core dimensions with same labels must have exactly matching sizes. - * In previous versions, core dimensions of size 1 would broadcast + * In versions before 1.10, core dimensions of size 1 would broadcast * against other core dimensions with the same label. * * All core dimensions must have their size specified by a passed in - * input or output argument. In previous versions, core dimensions in + * input or output argument. In versions before 1.10, core dimensions in * an output argument that were not specified in an input argument, * and whose size could not be inferred from a passed in output * argument, would have their size set to 1. + * * Core dimensions may be fixed, new in NumPy 1.16 */ static int _get_coredim_sizes(PyUFuncObject *ufunc, PyArrayObject **op, - npy_intp* core_dim_sizes, int **remap_axis) { + const int *op_core_num_dims, npy_uint32 *core_dim_flags, + npy_intp *core_dim_sizes, int **remap_axis) { int i; int nin = ufunc->nin; int nout = ufunc->nout; int nop = nin + nout; - for (i = 0; i < ufunc->core_num_dim_ix; ++i) { - core_dim_sizes[i] = -1; - } for (i = 0; i < nop; ++i) { if (op[i] != NULL) { int idim; int dim_offset = ufunc->core_offsets[i]; - int num_dims = ufunc->core_num_dims[i]; - int core_start_dim = PyArray_NDIM(op[i]) - num_dims; + int core_start_dim = PyArray_NDIM(op[i]) - op_core_num_dims[i]; + int dim_delta = 0; + + /* checked before this routine gets called */ + assert(core_start_dim >= 0); + /* * Make sure every core dimension exactly matches all other core - * dimensions with the same label. + * dimensions with the same label. Note that flexible dimensions + * may have been removed at this point, if so, they are marked + * with UFUNC_CORE_DIM_MISSING. */ - for (idim = 0; idim < num_dims; ++idim) { - int core_dim_index = ufunc->core_dim_ixs[dim_offset+idim]; - npy_intp op_dim_size = PyArray_DIM( - op[i], REMAP_AXIS(i, core_start_dim+idim)); - - if (core_dim_sizes[core_dim_index] == -1) { + for (idim = 0; idim < ufunc->core_num_dims[i]; ++idim) { + int core_index = dim_offset + idim; + int core_dim_index = ufunc->core_dim_ixs[core_index]; + npy_intp core_dim_size = core_dim_sizes[core_dim_index]; + npy_intp op_dim_size; + + /* can only happen if flexible; dimension missing altogether */ + if (core_dim_flags[core_dim_index] & UFUNC_CORE_DIM_MISSING) { + op_dim_size = 1; + dim_delta++; /* for indexing in dimensions */ + } + else { + op_dim_size = PyArray_DIM(op[i], + REMAP_AXIS(i, core_start_dim + idim - dim_delta)); + } + if (core_dim_sizes[core_dim_index] < 0) { core_dim_sizes[core_dim_index] = op_dim_size; } - else if (op_dim_size != core_dim_sizes[core_dim_index]) { + else if (op_dim_size != core_dim_size) { PyErr_Format(PyExc_ValueError, "%s: %s operand %d has a mismatch in its " "core dimension %d, with gufunc " "signature %s (size %zd is different " "from %zd)", ufunc_get_name_cstr(ufunc), i < nin ? "Input" : "Output", - i < nin ? i : i - nin, idim, + i < nin ? i : i - nin, idim - dim_delta, ufunc->core_signature, op_dim_size, core_dim_sizes[core_dim_index]); return -1; @@ -2220,39 +2012,29 @@ _get_coredim_sizes(PyUFuncObject *ufunc, PyArrayObject **op, /* * Make sure no core dimension is unspecified. */ - for (i = 0; i < ufunc->core_num_dim_ix; ++i) { - if (core_dim_sizes[i] == -1) { - break; - } - } - if (i != ufunc->core_num_dim_ix) { - /* - * There is at least one core dimension missing, find in which - * operand it comes up first (it has to be an output operand). - */ - const int missing_core_dim = i; - int out_op; - for (out_op = nin; out_op < nop; ++out_op) { - int first_idx = ufunc->core_offsets[out_op]; - int last_idx = first_idx + ufunc->core_num_dims[out_op]; - for (i = first_idx; i < last_idx; ++i) { - if (ufunc->core_dim_ixs[i] == missing_core_dim) { - break; - } - } - if (i < last_idx) { - /* Change index offsets for error message */ - out_op -= nin; - i -= first_idx; - break; + for (i = nin; i < nop; ++i) { + int idim; + int dim_offset = ufunc->core_offsets[i]; + + for (idim = 0; idim < ufunc->core_num_dims[i]; ++idim) { + int core_dim_index = ufunc->core_dim_ixs[dim_offset + idim]; + + /* check all cases where the size has not yet been set */ + if (core_dim_sizes[core_dim_index] < 0) { + /* + * Oops, this dimension was never specified + * (can only happen if output op not given) + */ + PyErr_Format(PyExc_ValueError, + "%s: Output operand %d has core dimension %d " + "unspecified, with gufunc signature %s", + ufunc_get_name_cstr(ufunc), i - nin, idim, + ufunc->core_signature); + return -1; } } - PyErr_Format(PyExc_ValueError, - "%s: Output operand %d has core dimension %d " - "unspecified, with gufunc signature %s", - ufunc_get_name_cstr(ufunc), out_op, i, ufunc->core_signature); - return -1; } + return 0; } @@ -2266,15 +2048,15 @@ _get_identity(PyUFuncObject *ufunc, npy_bool *reorderable) { switch(ufunc->identity) { case PyUFunc_One: *reorderable = 1; - return PyInt_FromLong(1); + return PyLong_FromLong(1); case PyUFunc_Zero: *reorderable = 1; - return PyInt_FromLong(0); + return PyLong_FromLong(0); case PyUFunc_MinusOne: *reorderable = 1; - return PyInt_FromLong(-1); + return PyLong_FromLong(-1); case PyUFunc_ReorderableNone: *reorderable = 1; @@ -2284,6 +2066,11 @@ _get_identity(PyUFuncObject *ufunc, npy_bool *reorderable) { *reorderable = 0; Py_RETURN_NONE; + case PyUFunc_IdentityValue: + *reorderable = 1; + Py_INCREF(ufunc->identity_value); + return ufunc->identity_value; + default: PyErr_Format(PyExc_ValueError, "ufunc %s has an invalid identity", ufunc_get_name_cstr(ufunc)); @@ -2291,26 +2078,46 @@ _get_identity(PyUFuncObject *ufunc, npy_bool *reorderable) { } } +/* + * Copy over parts of the ufunc structure that may need to be + * changed during execution. Returns 0 on success; -1 otherwise. + */ +static int +_initialize_variable_parts(PyUFuncObject *ufunc, + int op_core_num_dims[], + npy_intp core_dim_sizes[], + npy_uint32 core_dim_flags[]) { + int i; + + for (i = 0; i < ufunc->nargs; i++) { + op_core_num_dims[i] = ufunc->core_num_dims[i]; + } + for (i = 0; i < ufunc->core_num_dim_ix; i++) { + core_dim_sizes[i] = ufunc->core_dim_sizes[i]; + core_dim_flags[i] = ufunc->core_dim_flags[i]; + } + return 0; +} static int -PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc, - PyObject *args, PyObject *kwds, - PyArrayObject **op) +PyUFunc_GeneralizedFunctionInternal(PyUFuncObject *ufunc, + PyArrayMethodObject *ufuncimpl, PyArray_Descr *operation_descrs[], + PyArrayObject *op[], PyObject *extobj, + NPY_CASTING casting, NPY_ORDER order, + PyObject *axis, PyObject *axes, int keepdims) { int nin, nout; int i, j, idim, nop; const char *ufunc_name; - int retval = 0, subok = 1; + int retval; int needs_api = 0; - PyArray_Descr *dtypes[NPY_MAXARGS]; - /* Use remapped axes for generalized ufunc */ int broadcast_ndim, iter_ndim; - int core_num_dims_array[NPY_MAXARGS]; - int *core_num_dims; + int op_core_num_dims[NPY_MAXARGS]; int op_axes_arrays[NPY_MAXARGS][NPY_MAXDIMS]; int *op_axes[NPY_MAXARGS]; + npy_uint32 core_dim_flags[NPY_MAXARGS]; npy_uint32 op_flags[NPY_MAXARGS]; npy_intp iter_shape[NPY_MAXARGS]; @@ -2321,13 +2128,12 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc, /* These parameters come from extobj= or from a TLS global */ int buffersize = 0, errormask = 0; - /* The selected inner loop */ - PyUFuncGenericFunction innerloop = NULL; - void *innerloopdata = NULL; /* The dimensions which get passed to the inner loop */ npy_intp inner_dimensions[NPY_MAXDIMS+1]; /* The strides which get passed to the inner loop */ npy_intp *inner_strides = NULL; + /* Auxiliary data allocated by the ufuncimpl (ArrayMethod) */ + NpyAuxData *auxdata = NULL; /* The sizes of the core dimensions (# entries is ufunc->core_num_dim_ix) */ npy_intp *core_dim_sizes = inner_dimensions + 1; @@ -2335,22 +2141,6 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc, /* swapping around of axes */ int *remap_axis_memory = NULL; int **remap_axis = NULL; - /* The __array_prepare__ function to call for each output */ - PyObject *arr_prep[NPY_MAXARGS]; - /* The separated input and output arguments, parsed from args and kwds */ - ufunc_full_args full_args = {NULL, NULL}; - - NPY_ORDER order = NPY_KEEPORDER; - /* Use the default assignment casting rule */ - NPY_CASTING casting = NPY_DEFAULT_ASSIGN_CASTING; - /* other possible keyword arguments */ - PyObject *extobj, *type_tup, *axes, *axis; - int keepdims = -1; - - if (ufunc == NULL) { - PyErr_SetString(PyExc_ValueError, "function not supported"); - return -1; - } nin = ufunc->nin; nout = ufunc->nout; @@ -2360,24 +2150,18 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc, NPY_UF_DBG_PRINT1("\nEvaluating ufunc %s\n", ufunc_name); - /* Initialize all dtypes and __array_prepare__ call-backs to NULL */ - for (i = 0; i < nop; ++i) { - dtypes[i] = NULL; - arr_prep[i] = NULL; + if (validate_casting(ufuncimpl, + ufunc, op, operation_descrs, casting) < 0) { + return -1; } - NPY_UF_DBG_PRINT("Getting arguments\n"); - - /* - * Get all the arguments. - */ - retval = get_ufunc_arguments(ufunc, args, kwds, - op, &order, &casting, &extobj, - &type_tup, &subok, NULL, &axes, &axis, &keepdims); + /* Initialize possibly variable parts to the values from the ufunc */ + retval = _initialize_variable_parts(ufunc, op_core_num_dims, + core_dim_sizes, core_dim_flags); if (retval < 0) { - NPY_UF_DBG_PRINT("Failure in getting arguments\n"); - return retval; + goto fail; } + /* * If keepdims was passed in (and thus changed from the initial value * on top), check the gufunc is suitable, i.e., that its inputs share @@ -2396,74 +2180,49 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc, } } /* - * If keepdims is set and true, signal all dimensions will be the same. + * If keepdims is set and true, which means all input dimensions are + * the same, signal that all output dimensions will be the same too. */ if (keepdims == 1) { - int num_dims = ufunc->core_num_dims[0]; - for (i = 0; i < nop; ++i) { - core_num_dims_array[i] = num_dims; + int num_dims = op_core_num_dims[0]; + for (i = nin; i < nop; ++i) { + op_core_num_dims[i] = num_dims; } - core_num_dims = core_num_dims_array; } else { /* keepdims was not set or was false; no adjustment necessary */ - core_num_dims = ufunc->core_num_dims; keepdims = 0; } /* * Check that operands have the minimum dimensions required. * (Just checks core; broadcast dimensions are tested by the iterator.) */ - for (i = 0; i < nop; i++) { - if (op[i] != NULL && PyArray_NDIM(op[i]) < core_num_dims[i]) { - PyErr_Format(PyExc_ValueError, - "%s: %s operand %d does not have enough " - "dimensions (has %d, gufunc core with " - "signature %s requires %d)", - ufunc_name, - i < nin ? "Input" : "Output", - i < nin ? i : i - nin, - PyArray_NDIM(op[i]), - ufunc->core_signature, - core_num_dims[i]); - retval = -1; - goto fail; - } + retval = _validate_num_dims(ufunc, op, core_dim_flags, + op_core_num_dims); + if (retval < 0) { + goto fail; } - /* * Figure out the number of iteration dimensions, which - * is the broadcast result of all the input non-core - * dimensions. + * is the broadcast result of all the non-core dimensions. + * (We do allow outputs to broadcast inputs currently, if they are given. + * This is in line with what normal ufuncs do.) */ broadcast_ndim = 0; - for (i = 0; i < nin; ++i) { - int n = PyArray_NDIM(op[i]) - core_num_dims[i]; + for (i = 0; i < nop; ++i) { + if (op[i] == NULL) { + continue; + } + int n = PyArray_NDIM(op[i]) - op_core_num_dims[i]; if (n > broadcast_ndim) { broadcast_ndim = n; } } - /* - * Figure out the number of iterator creation dimensions, - * which is the broadcast dimensions + all the core dimensions of - * the outputs, so that the iterator can allocate those output - * dimensions following the rules of order='F', for example. - */ - iter_ndim = broadcast_ndim; - for (i = nin; i < nop; ++i) { - iter_ndim += core_num_dims[i]; - } - if (iter_ndim > NPY_MAXDIMS) { - PyErr_Format(PyExc_ValueError, - "too many dimensions for generalized ufunc %s", - ufunc_name); - retval = -1; - goto fail; - } - /* Possibly remap axes. */ if (axes != NULL || axis != NULL) { + assert(!(axes != NULL && axis != NULL)); + remap_axis = PyArray_malloc(sizeof(remap_axis[0]) * nop); remap_axis_memory = PyArray_malloc(sizeof(remap_axis_memory[0]) * nop * NPY_MAXDIMS); @@ -2475,11 +2234,11 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc, remap_axis[i] = remap_axis_memory + i * NPY_MAXDIMS; } if (axis) { - retval = _parse_axis_arg(ufunc, core_num_dims, axis, op, + retval = _parse_axis_arg(ufunc, op_core_num_dims, axis, op, broadcast_ndim, remap_axis); } else { - retval = _parse_axes_arg(ufunc, core_num_dims, axes, op, + retval = _parse_axes_arg(ufunc, op_core_num_dims, axes, op, broadcast_ndim, remap_axis); } if(retval < 0) { @@ -2488,10 +2247,28 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc, } /* Collect the lengths of the labelled core dimensions */ - retval = _get_coredim_sizes(ufunc, op, core_dim_sizes, remap_axis); + retval = _get_coredim_sizes(ufunc, op, op_core_num_dims, core_dim_flags, + core_dim_sizes, remap_axis); if(retval < 0) { goto fail; } + /* + * Figure out the number of iterator creation dimensions, + * which is the broadcast dimensions + all the core dimensions of + * the outputs, so that the iterator can allocate those output + * dimensions following the rules of order='F', for example. + */ + iter_ndim = broadcast_ndim; + for (i = nin; i < nop; ++i) { + iter_ndim += op_core_num_dims[i]; + } + if (iter_ndim > NPY_MAXDIMS) { + PyErr_Format(PyExc_ValueError, + "too many dimensions for generalized ufunc %s", + ufunc_name); + retval = -1; + goto fail; + } /* Fill in the initial part of 'iter_shape' */ for (idim = 0; idim < broadcast_ndim; ++idim) { @@ -2504,11 +2281,7 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc, int n; if (op[i]) { - /* - * Note that n may be negative if broadcasting - * extends into the core dimensions. - */ - n = PyArray_NDIM(op[i]) - core_num_dims[i]; + n = PyArray_NDIM(op[i]) - op_core_num_dims[i]; } else { n = broadcast_ndim; @@ -2524,32 +2297,61 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc, } } - /* Any output core dimensions shape should be ignored */ + /* + * Any output core dimensions shape should be ignored, so we add + * it as a Reduce dimension (which can be broadcast with the rest). + * These will be removed before the actual iteration for gufuncs. + */ for (idim = broadcast_ndim; idim < iter_ndim; ++idim) { - op_axes_arrays[i][idim] = -1; + op_axes_arrays[i][idim] = NPY_ITER_REDUCTION_AXIS(-1); } /* Except for when it belongs to this output */ if (i >= nin) { int dim_offset = ufunc->core_offsets[i]; - int num_dims = core_num_dims[i]; + int num_removed = 0; /* * Fill in 'iter_shape' and 'op_axes' for the core dimensions * of this output. Here, we have to be careful: if keepdims - * was used, then this axis is not a real core dimension, - * but is being added back for broadcasting, so its size is 1. + * was used, then the axes are not real core dimensions, but + * are being added back for broadcasting, so their size is 1. + * If the axis was removed, we should skip altogether. */ - for (idim = 0; idim < num_dims; ++idim) { - iter_shape[j] = keepdims ? 1 : core_dim_sizes[ - ufunc->core_dim_ixs[dim_offset + idim]]; - op_axes_arrays[i][j] = REMAP_AXIS(i, n + idim); - ++j; + if (keepdims) { + for (idim = 0; idim < op_core_num_dims[i]; ++idim) { + iter_shape[j] = 1; + op_axes_arrays[i][j] = REMAP_AXIS(i, n + idim); + ++j; + } + } + else { + for (idim = 0; idim < ufunc->core_num_dims[i]; ++idim) { + int core_index = dim_offset + idim; + int core_dim_index = ufunc->core_dim_ixs[core_index]; + if ((core_dim_flags[core_dim_index] & + UFUNC_CORE_DIM_MISSING)) { + /* skip it */ + num_removed++; + continue; + } + iter_shape[j] = core_dim_sizes[ufunc->core_dim_ixs[core_index]]; + op_axes_arrays[i][j] = REMAP_AXIS(i, n + idim - num_removed); + ++j; + } } } op_axes[i] = op_axes_arrays[i]; } +#if NPY_UF_DBG_TRACING + printf("iter shapes:"); + for (j=0; j < iter_ndim; j++) { + printf(" %ld", iter_shape[j]); + } + printf("\n"); +#endif + /* Get the buffersize and errormask */ if (_get_bufsize_errmask(extobj, ufunc_name, &buffersize, &errormask) < 0) { retval = -1; @@ -2558,88 +2360,33 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc, NPY_UF_DBG_PRINT("Finding inner loop\n"); - - retval = ufunc->type_resolver(ufunc, casting, - op, type_tup, dtypes); - if (retval < 0) { - goto fail; - } - /* For the generalized ufunc, we get the loop right away too */ - retval = ufunc->legacy_inner_loop_selector(ufunc, dtypes, - &innerloop, &innerloopdata, &needs_api); - if (retval < 0) { - goto fail; - } - -#if NPY_UF_DBG_TRACING - printf("input types:\n"); - for (i = 0; i < nin; ++i) { - PyObject_Print((PyObject *)dtypes[i], stdout, 0); - printf(" "); - } - printf("\noutput types:\n"); - for (i = nin; i < nop; ++i) { - PyObject_Print((PyObject *)dtypes[i], stdout, 0); - printf(" "); - } - printf("\n"); -#endif - - if (subok) { - if (make_full_arg_tuple(&full_args, nin, nout, args, kwds) < 0) { - goto fail; - } - - /* - * Get the appropriate __array_prepare__ function to call - * for each output - */ - _find_array_prepare(full_args, arr_prep, nin, nout); - } - - /* If the loop wants the arrays, provide them */ - if (_does_loop_use_arrays(innerloopdata)) { - innerloopdata = (void*)op; - } + /* + * We don't write to all elements, and the iterator may make + * UPDATEIFCOPY temporary copies. The output arrays (unless they are + * allocated by the iterator itself) must be considered READWRITE by the + * iterator, so that the elements we don't write to are copied to the + * possible temporary array. + */ + _ufunc_setup_flags(ufunc, NPY_ITER_COPY | NPY_UFUNC_DEFAULT_INPUT_FLAGS, + NPY_ITER_UPDATEIFCOPY | + NPY_ITER_WRITEONLY | + NPY_UFUNC_DEFAULT_OUTPUT_FLAGS, + op_flags); /* * Set up the iterator per-op flags. For generalized ufuncs, we * can't do buffering, so must COPY or UPDATEIFCOPY. */ - for (i = 0; i < nin; ++i) { - op_flags[i] = NPY_ITER_READONLY | - NPY_ITER_COPY | - NPY_ITER_ALIGNED | - NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE; - /* - * If READWRITE flag has been set for this operand, - * then clear default READONLY flag - */ - op_flags[i] |= ufunc->op_flags[i]; - if (op_flags[i] & (NPY_ITER_READWRITE | NPY_ITER_WRITEONLY)) { - op_flags[i] &= ~NPY_ITER_READONLY; - } - } - for (i = nin; i < nop; ++i) { - op_flags[i] = NPY_ITER_READWRITE| - NPY_ITER_UPDATEIFCOPY| - NPY_ITER_ALIGNED| - NPY_ITER_ALLOCATE| - NPY_ITER_NO_BROADCAST| - NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE; - } - iter_flags = ufunc->iter_flags | NPY_ITER_MULTI_INDEX | NPY_ITER_REFS_OK | - NPY_ITER_REDUCE_OK | NPY_ITER_ZEROSIZE_OK | NPY_ITER_COPY_IF_OVERLAP; /* Create the iterator */ iter = NpyIter_AdvancedNew(nop, op, iter_flags, order, NPY_UNSAFE_CASTING, op_flags, - dtypes, iter_ndim, + operation_descrs, iter_ndim, op_axes, iter_shape, 0); if (iter == NULL) { retval = -1; @@ -2647,13 +2394,15 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc, } /* Fill in any allocated outputs */ - for (i = nin; i < nop; ++i) { - if (op[i] == NULL) { - op[i] = NpyIter_GetOperandArray(iter)[i]; - Py_INCREF(op[i]); + { + PyArrayObject **operands = NpyIter_GetOperandArray(iter); + for (i = nin; i < nop; ++i) { + if (op[i] == NULL) { + op[i] = operands[i]; + Py_INCREF(op[i]); + } } } - /* * Set up the inner strides array. Because we're not doing * buffering, the strides are fixed throughout the looping. @@ -2672,8 +2421,6 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc, /* Copy the strides after the first nop */ idim = nop; for (i = 0; i < nop; ++i) { - int num_dims = ufunc->core_num_dims[i]; - int core_start_dim = PyArray_NDIM(op[i]) - num_dims; /* * Need to use the arrays in the iterator, not op, because * a copy with a different-sized type may have been made. @@ -2681,20 +2428,31 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc, PyArrayObject *arr = NpyIter_GetOperandArray(iter)[i]; npy_intp *shape = PyArray_SHAPE(arr); npy_intp *strides = PyArray_STRIDES(arr); - for (j = 0; j < num_dims; ++j) { - if (core_start_dim + j >= 0) { - /* - * Force the stride to zero when the shape is 1, so - * that the broadcasting works right. - */ - int remapped_axis = REMAP_AXIS(i, core_start_dim + j); + /* + * Could be negative if flexible dims are used, but not for + * keepdims, since those dimensions are allocated in arr. + */ + int core_start_dim = PyArray_NDIM(arr) - op_core_num_dims[i]; + int num_removed = 0; + int dim_offset = ufunc->core_offsets[i]; + + for (j = 0; j < ufunc->core_num_dims[i]; ++j) { + int core_dim_index = ufunc->core_dim_ixs[dim_offset + j]; + /* + * Force zero stride when the shape is 1 (always the case for + * for missing dimensions), so that broadcasting works right. + */ + if (core_dim_flags[core_dim_index] & UFUNC_CORE_DIM_MISSING) { + num_removed++; + inner_strides[idim++] = 0; + } + else { + int remapped_axis = REMAP_AXIS(i, core_start_dim + j - num_removed); if (shape[remapped_axis] != 1) { inner_strides[idim++] = strides[remapped_axis]; } else { inner_strides[idim++] = 0; } - } else { - inner_strides[idim++] = 0; } } } @@ -2727,21 +2485,34 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc, /* * The first nop strides are for the inner loop (but only can - * copy them after removing the core axes + * copy them after removing the core axes). The strides will not change + * if the iterator is not buffered (they are effectively fixed). + * Supporting buffering would make sense, but probably would have to be + * done in the inner-loop itself (not the iterator). */ + assert(!NpyIter_IsBuffered(iter)); memcpy(inner_strides, NpyIter_GetInnerStrideArray(iter), NPY_SIZEOF_INTP * nop); -#if 0 - printf("strides: "); - for (i = 0; i < nop+core_dim_ixs_size; ++i) { - printf("%d ", (int)inner_strides[i]); + /* Final preparation of the arraymethod call */ + PyArrayMethod_Context context = { + .caller = (PyObject *)ufunc, + .method = ufuncimpl, + .descriptors = operation_descrs, + }; + PyArrayMethod_StridedLoop *strided_loop; + NPY_ARRAYMETHOD_FLAGS flags = 0; + + if (ufuncimpl->get_strided_loop(&context, 1, 0, inner_strides, + &strided_loop, &auxdata, &flags) < 0) { + goto fail; + } + needs_api = (flags & NPY_METH_REQUIRES_PYAPI) != 0; + needs_api |= NpyIter_IterationNeedsAPI(iter); + if (!(flags & NPY_METH_NO_FLOATINGPOINT_ERRORS)) { + /* Start with the floating-point exception flags cleared */ + npy_clear_floatstatus_barrier((char*)&iter); } - printf("\n"); -#endif - - /* Start with the floating-point exception flags cleared */ - npy_clear_floatstatus_barrier((char*)&iter); NPY_UF_DBG_PRINT("Executing inner loop\n"); @@ -2761,479 +2532,285 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc, dataptr = NpyIter_GetDataPtrArray(iter); count_ptr = NpyIter_GetInnerLoopSizePtr(iter); - if (!needs_api && !NpyIter_IterationNeedsAPI(iter)) { + if (!needs_api) { NPY_BEGIN_THREADS_THRESHOLDED(total_problem_size); } do { inner_dimensions[0] = *count_ptr; - innerloop(dataptr, inner_dimensions, inner_strides, innerloopdata); - } while (iternext(iter)); + retval = strided_loop(&context, + dataptr, inner_dimensions, inner_strides, auxdata); + } while (retval == 0 && iternext(iter)); if (!needs_api && !NpyIter_IterationNeedsAPI(iter)) { NPY_END_THREADS; } - } else { - /** - * For each output operand, check if it has non-zero size, - * and assign the identity if it does. For example, a dot - * product of two zero-length arrays will be a scalar, - * which has size one. - */ - npy_bool reorderable; - PyObject *identity = _get_identity(ufunc, &reorderable); - if (identity == NULL) { - retval = -1; - goto fail; - } - - for (i = nin; i < nop; ++i) { - if (PyArray_SIZE(op[i]) != 0) { - if (identity == Py_None) { - PyErr_Format(PyExc_ValueError, - "ufunc %s ", - ufunc_name); - Py_DECREF(identity); - retval = -1; - goto fail; - } - PyArray_FillWithScalar(op[i], identity); - } - } - Py_DECREF(identity); - } - - /* Check whether any errors occurred during the loop */ - if (PyErr_Occurred() || - _check_ufunc_fperr(errormask, extobj, ufunc_name) < 0) { - retval = -1; - goto fail; } - /* Write back any temporary data from PyArray_SetWritebackIfCopyBase */ - if (NpyIter_Close(iter) < 0) { - goto fail; + if (retval == 0 && !(flags & NPY_METH_NO_FLOATINGPOINT_ERRORS)) { + /* NOTE: We could check float errors even when `res < 0` */ + retval = _check_ufunc_fperr(errormask, extobj, ufunc_name); } PyArray_free(inner_strides); - if (NpyIter_Close(iter) < 0) { - goto fail; - } - NpyIter_Deallocate(iter); - /* The caller takes ownership of all the references in op */ - for (i = 0; i < nop; ++i) { - Py_XDECREF(dtypes[i]); - Py_XDECREF(arr_prep[i]); + NPY_AUXDATA_FREE(auxdata); + if (!NpyIter_Deallocate(iter)) { + retval = -1; } - Py_XDECREF(type_tup); - Py_XDECREF(extobj); - Py_XDECREF(axes); - Py_XDECREF(axis); - Py_XDECREF(full_args.in); - Py_XDECREF(full_args.out); - NPY_UF_DBG_PRINT("Returning Success\n"); + PyArray_free(remap_axis_memory); + PyArray_free(remap_axis); - return 0; + NPY_UF_DBG_PRINT1("Returning code %d\n", retval); + + return retval; fail: NPY_UF_DBG_PRINT1("Returning failure code %d\n", retval); PyArray_free(inner_strides); - NpyIter_Close(iter); + NPY_AUXDATA_FREE(auxdata); NpyIter_Deallocate(iter); - for (i = 0; i < nop; ++i) { - Py_XDECREF(op[i]); - op[i] = NULL; - Py_XDECREF(dtypes[i]); - Py_XDECREF(arr_prep[i]); - } - Py_XDECREF(type_tup); - Py_XDECREF(extobj); - Py_XDECREF(axes); - Py_XDECREF(axis); - Py_XDECREF(full_args.in); - Py_XDECREF(full_args.out); PyArray_free(remap_axis_memory); PyArray_free(remap_axis); return retval; } -/*UFUNC_API - * - * This generic function is called with the ufunc object, the arguments to it, - * and an array of (pointers to) PyArrayObjects which are NULL. - * - * 'op' is an array of at least NPY_MAXARGS PyArrayObject *. - */ -NPY_NO_EXPORT int -PyUFunc_GenericFunction(PyUFuncObject *ufunc, - PyObject *args, PyObject *kwds, - PyArrayObject **op) + +static int +PyUFunc_GenericFunctionInternal(PyUFuncObject *ufunc, + PyArrayMethodObject *ufuncimpl, PyArray_Descr *operation_descrs[], + PyArrayObject *op[], PyObject *extobj, + NPY_CASTING casting, NPY_ORDER order, + PyObject *output_array_prepare[], ufunc_full_args full_args, + PyArrayObject *wheremask) { - int nin, nout; - int i, nop; - const char *ufunc_name; - int retval = -1, subok = 1; - int need_fancy = 0; + int nin = ufunc->nin, nout = ufunc->nout, nop = nin + nout; + + const char *ufunc_name = ufunc_get_name_cstr(ufunc); - PyArray_Descr *dtypes[NPY_MAXARGS]; + npy_intp default_op_out_flags; + npy_uint32 op_flags[NPY_MAXARGS]; /* These parameters come from extobj= or from a TLS global */ int buffersize = 0, errormask = 0; - /* The mask provided in the 'where=' parameter */ - PyArrayObject *wheremask = NULL; - - /* The __array_prepare__ function to call for each output */ - PyObject *arr_prep[NPY_MAXARGS]; - /* - * This is either args, or args with the out= parameter from - * kwds added appropriately. - */ - ufunc_full_args full_args = {NULL, NULL}; - - int trivial_loop_ok = 0; - - NPY_ORDER order = NPY_KEEPORDER; - /* Use the default assignment casting rule */ - NPY_CASTING casting = NPY_DEFAULT_ASSIGN_CASTING; - PyObject *extobj, *type_tup; + NPY_UF_DBG_PRINT1("\nEvaluating ufunc %s\n", ufunc_name); - if (ufunc == NULL) { - PyErr_SetString(PyExc_ValueError, "function not supported"); + /* Get the buffersize and errormask */ + if (_get_bufsize_errmask(extobj, ufunc_name, &buffersize, &errormask) < 0) { return -1; } - if (ufunc->core_enabled) { - return PyUFunc_GeneralizedFunction(ufunc, args, kwds, op); + if (wheremask != NULL) { + /* Set up the flags. */ + default_op_out_flags = NPY_ITER_NO_SUBTYPE | + NPY_ITER_WRITEMASKED | + NPY_UFUNC_DEFAULT_OUTPUT_FLAGS; + _ufunc_setup_flags(ufunc, NPY_UFUNC_DEFAULT_INPUT_FLAGS, + default_op_out_flags, op_flags); + } + else { + /* Set up the flags. */ + default_op_out_flags = NPY_ITER_WRITEONLY | + NPY_UFUNC_DEFAULT_OUTPUT_FLAGS; + _ufunc_setup_flags(ufunc, NPY_UFUNC_DEFAULT_INPUT_FLAGS, + default_op_out_flags, op_flags); } - nin = ufunc->nin; - nout = ufunc->nout; - nop = nin + nout; + /* Final preparation of the arraymethod call */ + PyArrayMethod_Context context = { + .caller = (PyObject *)ufunc, + .method = ufuncimpl, + .descriptors = operation_descrs, + }; - ufunc_name = ufunc_get_name_cstr(ufunc); + /* Do the ufunc loop */ + if (wheremask != NULL) { + NPY_UF_DBG_PRINT("Executing masked inner loop\n"); - NPY_UF_DBG_PRINT1("\nEvaluating ufunc %s\n", ufunc_name); + if (nop + 1 > NPY_MAXARGS) { + PyErr_SetString(PyExc_ValueError, + "Too many operands when including where= parameter"); + return -1; + } + op[nop] = wheremask; + operation_descrs[nop] = NULL; - /* Initialize all the dtypes and __array_prepare__ callbacks to NULL */ - for (i = 0; i < nop; ++i) { - dtypes[i] = NULL; - arr_prep[i] = NULL; + return execute_ufunc_loop(&context, 1, + op, order, buffersize, casting, + output_array_prepare, full_args, op_flags, + errormask, extobj); } + else { + NPY_UF_DBG_PRINT("Executing normal inner loop\n"); - NPY_UF_DBG_PRINT("Getting arguments\n"); - - /* Get all the arguments */ - retval = get_ufunc_arguments(ufunc, args, kwds, - op, &order, &casting, &extobj, - &type_tup, &subok, &wheremask, NULL, NULL, NULL); - if (retval < 0) { - NPY_UF_DBG_PRINT("Failure in getting arguments\n"); - return retval; - } - - /* - * Use the masked loop if a wheremask was specified. - */ - if (wheremask != NULL) { - need_fancy = 1; - } - - /* Get the buffersize and errormask */ - if (_get_bufsize_errmask(extobj, ufunc_name, &buffersize, &errormask) < 0) { - retval = -1; - goto fail; - } - - NPY_UF_DBG_PRINT("Finding inner loop\n"); - - retval = ufunc->type_resolver(ufunc, casting, - op, type_tup, dtypes); - if (retval < 0) { - goto fail; - } - - /* Only do the trivial loop check for the unmasked version. */ - if (!need_fancy) { /* * This checks whether a trivial loop is ok, making copies of - * scalar and one dimensional operands if that will help. + * scalar and one dimensional operands if that should help. */ - trivial_loop_ok = check_for_trivial_loop(ufunc, op, dtypes, buffersize); - if (trivial_loop_ok < 0) { - goto fail; + int trivial_ok = check_for_trivial_loop(ufuncimpl, + op, operation_descrs, casting, buffersize); + if (trivial_ok < 0) { + return -1; } - } - -#if NPY_UF_DBG_TRACING - printf("input types:\n"); - for (i = 0; i < nin; ++i) { - PyObject_Print((PyObject *)dtypes[i], stdout, 0); - printf(" "); - } - printf("\noutput types:\n"); - for (i = nin; i < nop; ++i) { - PyObject_Print((PyObject *)dtypes[i], stdout, 0); - printf(" "); - } - printf("\n"); -#endif - - if (subok) { - if (make_full_arg_tuple(&full_args, nin, nout, args, kwds) < 0) { - goto fail; + if (trivial_ok && context.method->nout == 1) { + /* Try to handle everything without using the (heavy) iterator */ + int retval = try_trivial_single_output_loop(&context, + op, order, output_array_prepare, full_args, + errormask, extobj); + if (retval != -2) { + return retval; + } } - /* - * Get the appropriate __array_prepare__ function to call - * for each output - */ - _find_array_prepare(full_args, arr_prep, nin, nout); - } - - /* Start with the floating-point exception flags cleared */ - npy_clear_floatstatus_barrier((char*)&ufunc); - - /* Do the ufunc loop */ - if (need_fancy) { - NPY_UF_DBG_PRINT("Executing fancy inner loop\n"); - - retval = execute_fancy_ufunc_loop(ufunc, wheremask, - op, dtypes, order, - buffersize, arr_prep, full_args); - } - else { - NPY_UF_DBG_PRINT("Executing legacy inner loop\n"); - - retval = execute_legacy_ufunc_loop(ufunc, trivial_loop_ok, - op, dtypes, order, - buffersize, arr_prep, full_args); - } - if (retval < 0) { - goto fail; - } - /* Check whether any errors occurred during the loop */ - if (PyErr_Occurred() || - _check_ufunc_fperr(errormask, extobj, ufunc_name) < 0) { - retval = -1; - goto fail; - } - - - /* The caller takes ownership of all the references in op */ - for (i = 0; i < nop; ++i) { - Py_XDECREF(dtypes[i]); - Py_XDECREF(arr_prep[i]); + return execute_ufunc_loop(&context, 0, + op, order, buffersize, casting, + output_array_prepare, full_args, op_flags, + errormask, extobj); } - Py_XDECREF(type_tup); - Py_XDECREF(extobj); - Py_XDECREF(full_args.in); - Py_XDECREF(full_args.out); - Py_XDECREF(wheremask); - - NPY_UF_DBG_PRINT("Returning Success\n"); - - return 0; +} -fail: - NPY_UF_DBG_PRINT1("Returning failure code %d\n", retval); - for (i = 0; i < nop; ++i) { - Py_XDECREF(op[i]); - op[i] = NULL; - Py_XDECREF(dtypes[i]); - Py_XDECREF(arr_prep[i]); - } - Py_XDECREF(type_tup); - Py_XDECREF(extobj); - Py_XDECREF(full_args.in); - Py_XDECREF(full_args.out); - Py_XDECREF(wheremask); - return retval; +/*UFUNC_API*/ +NPY_NO_EXPORT int +PyUFunc_GenericFunction(PyUFuncObject *NPY_UNUSED(ufunc), + PyObject *NPY_UNUSED(args), PyObject *NPY_UNUSED(kwds), + PyArrayObject **NPY_UNUSED(op)) +{ + /* NumPy 1.21, 2020-03-29 */ + PyErr_SetString(PyExc_RuntimeError, + "The `PyUFunc_GenericFunction()` C-API function has been disabled. " + "Please use `PyObject_Call(ufunc, args, kwargs)`, which has " + "identical behaviour but allows subclass and `__array_ufunc__` " + "override handling and only returns the normal ufunc result."); + return -1; } + /* - * Given the output type, finds the specified binary op. The - * ufunc must have nin==2 and nout==1. The function may modify - * otype if the given type isn't found. + * Promote and resolve a reduction like operation. * - * Returns 0 on success, -1 on failure. + * @param ufunc + * @param arr The operation array + * @param out The output array or NULL if not provided. Note that NumPy always + * used out to mean the same as `dtype=out.dtype` and never passed + * the array itself to the type-resolution. + * @param signature The DType signature, which may already be set due to the + * dtype passed in by the user, or the special cases (add, multiply). + * (Contains strong references and may be modified.) + * @param enforce_uniform_args If `NPY_TRUE` fully uniform dtypes/descriptors + * are enforced as required for accumulate and (currently) reduceat. + * @param out_descrs New references to the resolved descriptors (on success). + * @param method The ufunc method, "reduce", "reduceat", or "accumulate". + + * @returns ufuncimpl The `ArrayMethod` implemention to use. Or NULL if an + * error occurred. */ -static int -get_binary_op_function(PyUFuncObject *ufunc, int *otype, - PyUFuncGenericFunction *out_innerloop, - void **out_innerloopdata) +static PyArrayMethodObject * +reducelike_promote_and_resolve(PyUFuncObject *ufunc, + PyArrayObject *arr, PyArrayObject *out, + PyArray_DTypeMeta *signature[3], + npy_bool enforce_uniform_args, PyArray_Descr *out_descrs[3], + char *method) { - int i; - PyUFunc_Loop1d *funcdata; - - NPY_UF_DBG_PRINT1("Getting binary op function for type number %d\n", - *otype); - - /* If the type is custom and there are userloops, search for it here */ - if (ufunc->userloops != NULL && PyTypeNum_ISUSERDEF(*otype)) { - PyObject *key, *obj; - key = PyInt_FromLong(*otype); - if (key == NULL) { - return -1; - } - obj = PyDict_GetItem(ufunc->userloops, key); - Py_DECREF(key); - if (obj != NULL) { - funcdata = (PyUFunc_Loop1d *)NpyCapsule_AsVoidPtr(obj); - while (funcdata != NULL) { - int *types = funcdata->arg_types; - - if (types[0] == *otype && types[1] == *otype && - types[2] == *otype) { - *out_innerloop = funcdata->func; - *out_innerloopdata = funcdata->data; - return 0; - } - - funcdata = funcdata->next; - } - } - } - - /* Search for a function with compatible inputs */ - for (i = 0; i < ufunc->ntypes; ++i) { - char *types = ufunc->types + i*ufunc->nargs; - - NPY_UF_DBG_PRINT3("Trying loop with signature %d %d -> %d\n", - types[0], types[1], types[2]); - - if (PyArray_CanCastSafely(*otype, types[0]) && - types[0] == types[1] && - (*otype == NPY_OBJECT || types[0] != NPY_OBJECT)) { - /* If the signature is "xx->x", we found the loop */ - if (types[2] == types[0]) { - *out_innerloop = ufunc->functions[i]; - *out_innerloopdata = ufunc->data[i]; - *otype = types[0]; - return 0; - } - /* - * Otherwise, we found the natural type of the reduction, - * replace otype and search again - */ - else { - *otype = types[2]; - break; - } - } - } - - /* Search for the exact function */ - for (i = 0; i < ufunc->ntypes; ++i) { - char *types = ufunc->types + i*ufunc->nargs; + /* + * Note that the `ops` is not really correct. But legacy resolution + * cannot quite handle the correct ops (e.g. a NULL first item if `out` + * is NULL) so we pass `arr` instead in that case. + */ + PyArrayObject *ops[3] = {out ? out : arr, arr, out}; + /* + * TODO: If `out` is not provided, arguably `initial` could define + * the first DType (and maybe also the out one), that way + * `np.add.reduce([1, 2, 3], initial=3.4)` would return a float + * value. As of 1.20, it returned an integer, so that should + * probably go to an error/warning first. + */ + PyArray_DTypeMeta *operation_DTypes[3] = { + NULL, NPY_DTYPE(PyArray_DESCR(arr)), NULL}; + Py_INCREF(operation_DTypes[1]); - if (PyArray_CanCastSafely(*otype, types[0]) && - types[0] == types[1] && - types[1] == types[2] && - (*otype == NPY_OBJECT || types[0] != NPY_OBJECT)) { - /* Since the signature is "xx->x", we found the loop */ - *out_innerloop = ufunc->functions[i]; - *out_innerloopdata = ufunc->data[i]; - *otype = types[0]; - return 0; - } + if (out != NULL) { + operation_DTypes[0] = NPY_DTYPE(PyArray_DESCR(out)); + Py_INCREF(operation_DTypes[0]); + operation_DTypes[2] = operation_DTypes[0]; + Py_INCREF(operation_DTypes[2]); + } + + PyArrayMethodObject *ufuncimpl = promote_and_get_ufuncimpl(ufunc, + ops, signature, operation_DTypes, NPY_FALSE, NPY_TRUE, NPY_TRUE); + /* DTypes may currently get filled in fallbacks and XDECREF for error: */ + Py_XDECREF(operation_DTypes[0]); + Py_XDECREF(operation_DTypes[1]); + Py_XDECREF(operation_DTypes[2]); + if (ufuncimpl == NULL) { + return NULL; } - return -1; -} - -static int -reduce_type_resolver(PyUFuncObject *ufunc, PyArrayObject *arr, - PyArray_Descr *odtype, PyArray_Descr **out_dtype) -{ - int i, retcode; - PyArrayObject *op[3] = {arr, arr, NULL}; - PyArray_Descr *dtypes[3] = {NULL, NULL, NULL}; - const char *ufunc_name = ufunc_get_name_cstr(ufunc); - PyObject *type_tup = NULL; - - *out_dtype = NULL; - /* - * If odtype is specified, make a type tuple for the type - * resolution. + * Find the correct descriptors for the operation. We use unsafe casting + * for historic reasons: The logic ufuncs required it to cast everything to + * boolean. However, we now special case the logical ufuncs, so that the + * casting safety could in principle be set to the default same-kind. + * (although this should possibly happen through a deprecation) */ - if (odtype != NULL) { - type_tup = PyTuple_Pack(3, odtype, odtype, Py_None); - if (type_tup == NULL) { - return -1; - } - } - - /* Use the type resolution function to find our loop */ - retcode = ufunc->type_resolver( - ufunc, NPY_UNSAFE_CASTING, - op, type_tup, dtypes); - Py_DECREF(type_tup); - if (retcode == -1) { - return -1; - } - else if (retcode == -2) { - PyErr_Format(PyExc_RuntimeError, - "type resolution returned NotImplemented to " - "reduce ufunc %s", ufunc_name); - return -1; + if (resolve_descriptors(3, ufunc, ufuncimpl, + ops, out_descrs, signature, NPY_UNSAFE_CASTING) < 0) { + return NULL; } /* - * The first two type should be equivalent. Because of how - * reduce has historically behaved in NumPy, the return type - * could be different, and it is the return type on which the - * reduction occurs. + * The first operand and output should be the same array, so they should + * be identical. The second argument can be different for reductions, + * but is checked to be identical for accumulate and reduceat. */ - if (!PyArray_EquivTypes(dtypes[0], dtypes[1])) { - for (i = 0; i < 3; ++i) { - Py_DECREF(dtypes[i]); - } - PyErr_Format(PyExc_RuntimeError, - "could not find a type resolution appropriate for " - "reduce ufunc %s", ufunc_name); - return -1; + if (out_descrs[0] != out_descrs[2] || ( + enforce_uniform_args && out_descrs[0] != out_descrs[1])) { + PyErr_Format(PyExc_TypeError, + "the resolved dtypes are not compatible with %s.%s. " + "Resolved (%R, %R, %R)", + ufunc_get_name_cstr(ufunc), method, + out_descrs[0], out_descrs[1], out_descrs[2]); + goto fail; + } + /* TODO: This really should _not_ be unsafe casting (same above)! */ + if (validate_casting(ufuncimpl, + ufunc, ops, out_descrs, NPY_UNSAFE_CASTING) < 0) { + goto fail; } - Py_DECREF(dtypes[0]); - Py_DECREF(dtypes[1]); - *out_dtype = dtypes[2]; + return ufuncimpl; - return 0; + fail: + for (int i = 0; i < 3; ++i) { + Py_DECREF(out_descrs[i]); + } + return NULL; } + static int -reduce_loop(NpyIter *iter, char **dataptrs, npy_intp *strides, - npy_intp *countptr, NpyIter_IterNextFunc *iternext, - int needs_api, npy_intp skip_first_count, void *data) +reduce_loop(PyArrayMethod_Context *context, + PyArrayMethod_StridedLoop *strided_loop, NpyAuxData *auxdata, + NpyIter *iter, char **dataptrs, npy_intp const *strides, + npy_intp const *countptr, NpyIter_IterNextFunc *iternext, + int needs_api, npy_intp skip_first_count) { - PyArray_Descr *dtypes[3], **iter_dtypes; - PyUFuncObject *ufunc = (PyUFuncObject *)data; - char *dataptrs_copy[3]; - npy_intp strides_copy[3]; - - /* The normal selected inner loop */ - PyUFuncGenericFunction innerloop = NULL; - void *innerloopdata = NULL; + int retval; + char *dataptrs_copy[4]; + npy_intp strides_copy[4]; + npy_bool masked; NPY_BEGIN_THREADS_DEF; + /* Get the number of operands, to determine whether "where" is used */ + masked = (NpyIter_GetNOp(iter) == 3); - /* Get the inner loop */ - iter_dtypes = NpyIter_GetDescrArray(iter); - dtypes[0] = iter_dtypes[0]; - dtypes[1] = iter_dtypes[1]; - dtypes[2] = iter_dtypes[0]; - if (ufunc->legacy_inner_loop_selector(ufunc, dtypes, - &innerloop, &innerloopdata, &needs_api) < 0) { - return -1; + if (!needs_api) { + NPY_BEGIN_THREADS_THRESHOLDED(NpyIter_GetIterSize(iter)); } - NPY_BEGIN_THREADS_NDITER(iter); - if (skip_first_count > 0) { - do { + assert(!masked); /* Path currently not available for masked */ + while (1) { npy_intp count = *countptr; /* Skip any first-visit elements */ @@ -3256,20 +2833,25 @@ reduce_loop(NpyIter *iter, char **dataptrs, npy_intp *strides, strides_copy[0] = strides[0]; strides_copy[1] = strides[1]; strides_copy[2] = strides[0]; - innerloop(dataptrs_copy, &count, - strides_copy, innerloopdata); - /* Jump to the faster loop when skipping is done */ + retval = strided_loop(context, + dataptrs_copy, &count, strides_copy, auxdata); + if (retval < 0) { + goto finish_loop; + } + + /* Advance loop, and abort on error (or finish) */ + if (!iternext(iter)) { + goto finish_loop; + } + + /* When skipping is done break and continue with faster loop */ if (skip_first_count == 0) { - if (iternext(iter)) { - break; - } - else { - goto finish_loop; - } + break; } - } while (iternext(iter)); + } } + do { /* Turn the two items into three for the inner loop */ dataptrs_copy[0] = dataptrs[0]; @@ -3278,14 +2860,23 @@ reduce_loop(NpyIter *iter, char **dataptrs, npy_intp *strides, strides_copy[0] = strides[0]; strides_copy[1] = strides[1]; strides_copy[2] = strides[0]; - innerloop(dataptrs_copy, countptr, - strides_copy, innerloopdata); + if (masked) { + dataptrs_copy[3] = dataptrs[2]; + strides_copy[3] = strides[2]; + } + + retval = strided_loop(context, + dataptrs_copy, countptr, strides_copy, auxdata); + if (retval < 0) { + goto finish_loop; + } + } while (iternext(iter)); finish_loop: NPY_END_THREADS; - return (needs_api && PyErr_Occurred()) ? -1 : 0; + return retval; } /* @@ -3306,26 +2897,21 @@ reduce_loop(NpyIter *iter, char **dataptrs, npy_intp *strides, * this function does not validate them. */ static PyArrayObject * -PyUFunc_Reduce(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out, - int naxes, int *axes, PyArray_Descr *odtype, int keepdims, - PyObject *initial) +PyUFunc_Reduce(PyUFuncObject *ufunc, + PyArrayObject *arr, PyArrayObject *out, + int naxes, int *axes, PyArray_DTypeMeta *signature[3], int keepdims, + PyObject *initial, PyArrayObject *wheremask) { int iaxes, ndim; npy_bool reorderable; npy_bool axis_flags[NPY_MAXDIMS]; - PyArray_Descr *dtype; - PyArrayObject *result; PyObject *identity; const char *ufunc_name = ufunc_get_name_cstr(ufunc); /* These parameters come from a TLS global */ int buffersize = 0, errormask = 0; - static PyObject *NoValue = NULL; NPY_UF_DBG_PRINT1("\nEvaluating ufunc %s.reduce\n", ufunc_name); - npy_cache_import("numpy", "_NoValue", &NoValue); - if (NoValue == NULL) return NULL; - ndim = PyArray_NDIM(arr); /* Create an array of flags for reduction */ @@ -3345,13 +2931,14 @@ PyUFunc_Reduce(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out, } /* Get the identity */ + /* TODO: Both of these should be provided by the ArrayMethod! */ identity = _get_identity(ufunc, &reorderable); if (identity == NULL) { return NULL; } /* Get the initial value */ - if (initial == NULL || initial == NoValue) { + if (initial == NULL) { initial = identity; /* @@ -3368,21 +2955,27 @@ PyUFunc_Reduce(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out, Py_INCREF(initial); /* match the reference count in the if above */ } - /* Get the reduction dtype */ - if (reduce_type_resolver(ufunc, arr, odtype, &dtype) < 0) { + PyArray_Descr *descrs[3]; + PyArrayMethodObject *ufuncimpl = reducelike_promote_and_resolve(ufunc, + arr, out, signature, NPY_FALSE, descrs, "reduce"); + if (ufuncimpl == NULL) { Py_DECREF(initial); return NULL; } - result = PyUFunc_ReduceWrapper(arr, out, NULL, dtype, dtype, - NPY_UNSAFE_CASTING, - axis_flags, reorderable, - keepdims, 0, - initial, - reduce_loop, - ufunc, buffersize, ufunc_name, errormask); + PyArrayMethod_Context context = { + .caller = (PyObject *)ufunc, + .method = ufuncimpl, + .descriptors = descrs, + }; - Py_DECREF(dtype); + PyArrayObject *result = PyUFunc_ReduceWrapper(&context, + arr, out, wheremask, axis_flags, reorderable, keepdims, + initial, reduce_loop, ufunc, buffersize, ufunc_name, errormask); + + for (int i = 0; i < 3; i++) { + Py_DECREF(descrs[i]); + } Py_DECREF(initial); return result; } @@ -3390,23 +2983,21 @@ PyUFunc_Reduce(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out, static PyObject * PyUFunc_Accumulate(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out, - int axis, int otype) + int axis, PyArray_DTypeMeta *signature[3]) { PyArrayObject *op[2]; - PyArray_Descr *op_dtypes[2] = {NULL, NULL}; int op_axes_arrays[2][NPY_MAXDIMS]; int *op_axes[2] = {op_axes_arrays[0], op_axes_arrays[1]}; npy_uint32 op_flags[2]; - int idim, ndim, otype_final; + int idim, ndim; int needs_api, need_outer_iterator; - NpyIter *iter = NULL, *iter_inner = NULL; + int res = 0; - /* The selected inner loop */ - PyUFuncGenericFunction innerloop = NULL; - void *innerloopdata = NULL; + PyArrayMethod_StridedLoop *strided_loop; + NpyAuxData *auxdata = NULL; - const char *ufunc_name = ufunc_get_name_cstr(ufunc); + NpyIter *iter = NULL; /* These parameters come from extobj= or from a TLS global */ int buffersize = 0, errormask = 0; @@ -3428,42 +3019,32 @@ PyUFunc_Accumulate(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out, /* Take a reference to out for later returning */ Py_XINCREF(out); - otype_final = otype; - if (get_binary_op_function(ufunc, &otype_final, - &innerloop, &innerloopdata) < 0) { - PyArray_Descr *dtype = PyArray_DescrFromType(otype); - PyErr_Format(PyExc_ValueError, - "could not find a matching type for %s.accumulate, " - "requested type has type code '%c'", - ufunc_name, dtype ? dtype->type : '-'); - Py_XDECREF(dtype); - goto fail; + PyArray_Descr *descrs[3]; + PyArrayMethodObject *ufuncimpl = reducelike_promote_and_resolve(ufunc, + arr, out, signature, NPY_TRUE, descrs, "accumulate"); + if (ufuncimpl == NULL) { + return NULL; } - ndim = PyArray_NDIM(arr); + /* The below code assumes that all descriptors are identical: */ + assert(descrs[0] == descrs[1] && descrs[0] == descrs[2]); - /* - * Set up the output data type, using the input's exact - * data type if the type number didn't change to preserve - * metadata - */ - if (PyArray_DESCR(arr)->type_num == otype_final) { - if (PyArray_ISNBO(PyArray_DESCR(arr)->byteorder)) { - op_dtypes[0] = PyArray_DESCR(arr); - Py_INCREF(op_dtypes[0]); - } - else { - op_dtypes[0] = PyArray_DescrNewByteorder(PyArray_DESCR(arr), - NPY_NATIVE); - } - } - else { - op_dtypes[0] = PyArray_DescrFromType(otype_final); - } - if (op_dtypes[0] == NULL) { + if (PyDataType_REFCHK(descrs[2]) && descrs[2]->type_num != NPY_OBJECT) { + /* This can be removed, but the initial element copy needs fixing */ + PyErr_SetString(PyExc_TypeError, + "accumulation currently only supports `object` dtype with " + "references"); goto fail; } + PyArrayMethod_Context context = { + .caller = (PyObject *)ufunc, + .method = ufuncimpl, + .descriptors = descrs, + }; + + ndim = PyArray_NDIM(arr); + #if NPY_UF_DBG_TRACING printf("Found %s.accumulate inner loop with dtype : ", ufunc_name); PyObject_Print((PyObject *)op_dtypes[0], stdout, 0); @@ -3489,9 +3070,9 @@ PyUFunc_Accumulate(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out, need_outer_iterator = (ndim > 1); /* We can't buffer, so must do UPDATEIFCOPY */ if (!PyArray_ISALIGNED(arr) || (out && !PyArray_ISALIGNED(out)) || - !PyArray_EquivTypes(op_dtypes[0], PyArray_DESCR(arr)) || + !PyArray_EquivTypes(descrs[1], PyArray_DESCR(arr)) || (out && - !PyArray_EquivTypes(op_dtypes[0], PyArray_DESCR(out)))) { + !PyArray_EquivTypes(descrs[0], PyArray_DESCR(out)))) { need_outer_iterator = 1; } /* If input and output overlap in memory, use iterator to figure it out */ @@ -3504,7 +3085,6 @@ PyUFunc_Accumulate(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out, npy_uint32 flags = NPY_ITER_ZEROSIZE_OK| NPY_ITER_REFS_OK| NPY_ITER_COPY_IF_OVERLAP; - PyArray_Descr **op_dtypes_param = NULL; /* * The way accumulate is set up, we can't do buffering, @@ -3521,13 +3101,11 @@ PyUFunc_Accumulate(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out, */ op_flags[0] |= NPY_ITER_UPDATEIFCOPY|NPY_ITER_ALIGNED|NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE; op_flags[1] |= NPY_ITER_COPY|NPY_ITER_ALIGNED|NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE; - op_dtypes_param = op_dtypes; - op_dtypes[1] = op_dtypes[0]; + NPY_UF_DBG_PRINT("Allocating outer iterator\n"); iter = NpyIter_AdvancedNew(2, op, flags, NPY_KEEPORDER, NPY_UNSAFE_CASTING, - op_flags, - op_dtypes_param, + op_flags, descrs, ndim_iter, op_axes, NULL, 0); if (iter == NULL) { goto fail; @@ -3545,14 +3123,14 @@ PyUFunc_Accumulate(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out, } } - /* Get the output */ + /* Get the output from the iterator if it was allocated */ if (out == NULL) { if (iter) { op[0] = out = NpyIter_GetOperandArray(iter)[0]; Py_INCREF(out); } else { - PyArray_Descr *dtype = op_dtypes[0]; + PyArray_Descr *dtype = descrs[0]; Py_INCREF(dtype); op[0] = out = (PyArrayObject *)PyArray_NewFromDescr( &PyArray_Type, dtype, @@ -3561,10 +3139,31 @@ PyUFunc_Accumulate(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out, if (out == NULL) { goto fail; } - } } + npy_intp fixed_strides[3]; + if (need_outer_iterator) { + NpyIter_GetInnerFixedStrideArray(iter, fixed_strides); + } + else { + fixed_strides[0] = PyArray_STRIDES(op[0])[axis]; + fixed_strides[1] = PyArray_STRIDES(op[1])[axis]; + fixed_strides[2] = fixed_strides[0]; + } + + + NPY_ARRAYMETHOD_FLAGS flags = 0; + if (ufuncimpl->get_strided_loop(&context, + 1, 0, fixed_strides, &strided_loop, &auxdata, &flags) < 0) { + goto fail; + } + needs_api = (flags & NPY_METH_REQUIRES_PYAPI) != 0; + if (!(flags & NPY_METH_NO_FLOATINGPOINT_ERRORS)) { + /* Start with the floating-point exception flags cleared */ + npy_clear_floatstatus_barrier((char*)&iter); + } + /* * If the reduction axis has size zero, either return the reduction * unit for UFUNC_REDUCE, or return the zero-sized output array @@ -3585,7 +3184,7 @@ PyUFunc_Accumulate(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out, NpyIter_IterNextFunc *iternext; char **dataptr; - int itemsize = op_dtypes[0]->elsize; + int itemsize = descrs[0]->elsize; /* Get the variables needed for the loop */ iternext = NpyIter_GetIterNext(iter, NULL); @@ -3593,7 +3192,7 @@ PyUFunc_Accumulate(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out, goto fail; } dataptr = NpyIter_GetDataPtrArray(iter); - + needs_api |= NpyIter_IterationNeedsAPI(iter); /* Execute the loop with just the outer iterator */ count_m1 = PyArray_DIM(op[1], axis)-1; @@ -3607,9 +3206,9 @@ PyUFunc_Accumulate(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out, stride_copy[1] = stride1; stride_copy[2] = stride0; - needs_api = NpyIter_IterationNeedsAPI(iter); - - NPY_BEGIN_THREADS_NDITER(iter); + if (!needs_api) { + NPY_BEGIN_THREADS_THRESHOLDED(NpyIter_GetIterSize(iter)); + } do { dataptr_copy[0] = dataptr[0]; @@ -3622,7 +3221,7 @@ PyUFunc_Accumulate(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out, * Output (dataptr[0]) and input (dataptr[1]) may point to * the same memory, e.g. np.add.accumulate(a, out=a). */ - if (otype == NPY_OBJECT) { + if (descrs[2]->type_num == NPY_OBJECT) { /* * Incref before decref to avoid the possibility of the * reference count being zero temporarily. @@ -3642,18 +3241,17 @@ PyUFunc_Accumulate(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out, dataptr_copy[2] += stride0; NPY_UF_DBG_PRINT1("iterator loop count %d\n", (int)count_m1); - innerloop(dataptr_copy, &count_m1, - stride_copy, innerloopdata); + res = strided_loop(&context, + dataptr_copy, &count_m1, stride_copy, auxdata); } - } while (iternext(iter)); + } while (res == 0 && iternext(iter)); NPY_END_THREADS; } else if (iter == NULL) { char *dataptr_copy[3]; - npy_intp stride_copy[3]; - int itemsize = op_dtypes[0]->elsize; + int itemsize = descrs[0]->elsize; /* Execute the loop with no iterators */ npy_intp count = PyArray_DIM(op[1], axis); @@ -3667,15 +3265,11 @@ PyUFunc_Accumulate(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out, PyArray_NDIM(op[0]))) { PyErr_SetString(PyExc_ValueError, "provided out is the wrong size " - "for the reduction"); + "for the accumulation."); goto fail; } stride0 = PyArray_STRIDE(op[0], axis); - stride_copy[0] = stride0; - stride_copy[1] = stride1; - stride_copy[2] = stride0; - /* Turn the two items into three for the inner loop */ dataptr_copy[0] = PyArray_BYTES(op[0]); dataptr_copy[1] = PyArray_BYTES(op[1]); @@ -3687,7 +3281,7 @@ PyUFunc_Accumulate(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out, * Output (dataptr[0]) and input (dataptr[1]) may point to the * same memory, e.g. np.add.accumulate(a, out=a). */ - if (otype == NPY_OBJECT) { + if (descrs[2]->type_num == NPY_OBJECT) { /* * Incref before decref to avoid the possibility of the * reference count being zero temporarily. @@ -3708,38 +3302,50 @@ PyUFunc_Accumulate(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out, NPY_UF_DBG_PRINT1("iterator loop count %d\n", (int)count); - needs_api = PyDataType_REFCHK(op_dtypes[0]); + needs_api = PyDataType_REFCHK(descrs[0]); if (!needs_api) { NPY_BEGIN_THREADS_THRESHOLDED(count); } - innerloop(dataptr_copy, &count, - stride_copy, innerloopdata); + res = strided_loop(&context, + dataptr_copy, &count, fixed_strides, auxdata); NPY_END_THREADS; } } finish: - if (NpyIter_Close(iter) < 0) { - goto fail; + NPY_AUXDATA_FREE(auxdata); + Py_DECREF(descrs[0]); + Py_DECREF(descrs[1]); + Py_DECREF(descrs[2]); + + if (!NpyIter_Deallocate(iter)) { + res = -1; } - if (NpyIter_Close(iter_inner) < 0) { - goto fail; + + if (res == 0 && !(flags & NPY_METH_NO_FLOATINGPOINT_ERRORS)) { + /* NOTE: We could check float errors even when `res < 0` */ + res = _check_ufunc_fperr(errormask, NULL, "accumulate"); + } + + if (res < 0) { + Py_DECREF(out); + return NULL; } - Py_XDECREF(op_dtypes[0]); - NpyIter_Deallocate(iter); - NpyIter_Deallocate(iter_inner); return (PyObject *)out; fail: Py_XDECREF(out); - Py_XDECREF(op_dtypes[0]); + + NPY_AUXDATA_FREE(auxdata); + Py_XDECREF(descrs[0]); + Py_XDECREF(descrs[1]); + Py_XDECREF(descrs[2]); NpyIter_Deallocate(iter); - NpyIter_Deallocate(iter_inner); return NULL; } @@ -3762,28 +3368,31 @@ PyUFunc_Accumulate(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out, * indices[1::2] = range(1,len(array)) * * output shape is based on the size of indices + * + * TODO: Reduceat duplicates too much code from accumulate! */ static PyObject * PyUFunc_Reduceat(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *ind, - PyArrayObject *out, int axis, int otype) + PyArrayObject *out, int axis, PyArray_DTypeMeta *signature[3]) { PyArrayObject *op[3]; - PyArray_Descr *op_dtypes[3] = {NULL, NULL, NULL}; int op_axes_arrays[3][NPY_MAXDIMS]; int *op_axes[3] = {op_axes_arrays[0], op_axes_arrays[1], op_axes_arrays[2]}; npy_uint32 op_flags[3]; - int i, idim, ndim, otype_final; - int need_outer_iterator = 0; + int idim, ndim; + int needs_api, need_outer_iterator = 0; + + int res = 0; NpyIter *iter = NULL; + PyArrayMethod_StridedLoop *strided_loop; + NpyAuxData *auxdata = NULL; + /* The reduceat indices - ind must be validated outside this call */ npy_intp *reduceat_ind; - npy_intp ind_size, red_axis_size; - /* The selected inner loop */ - PyUFuncGenericFunction innerloop = NULL; - void *innerloopdata = NULL; + npy_intp i, ind_size, red_axis_size; const char *ufunc_name = ufunc_get_name_cstr(ufunc); char *opname = "reduceat"; @@ -3801,8 +3410,8 @@ PyUFunc_Reduceat(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *ind, for (i = 0; i < ind_size; ++i) { if (reduceat_ind[i] < 0 || reduceat_ind[i] >= red_axis_size) { PyErr_Format(PyExc_IndexError, - "index %d out-of-bounds in %s.%s [0, %d)", - (int)reduceat_ind[i], ufunc_name, opname, (int)red_axis_size); + "index %" NPY_INTP_FMT " out-of-bounds in %s.%s [0, %" NPY_INTP_FMT ")", + reduceat_ind[i], ufunc_name, opname, red_axis_size); return NULL; } } @@ -3823,42 +3432,32 @@ PyUFunc_Reduceat(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *ind, /* Take a reference to out for later returning */ Py_XINCREF(out); - otype_final = otype; - if (get_binary_op_function(ufunc, &otype_final, - &innerloop, &innerloopdata) < 0) { - PyArray_Descr *dtype = PyArray_DescrFromType(otype); - PyErr_Format(PyExc_ValueError, - "could not find a matching type for %s.%s, " - "requested type has type code '%c'", - ufunc_name, opname, dtype ? dtype->type : '-'); - Py_XDECREF(dtype); - goto fail; + PyArray_Descr *descrs[3]; + PyArrayMethodObject *ufuncimpl = reducelike_promote_and_resolve(ufunc, + arr, out, signature, NPY_TRUE, descrs, "reduceat"); + if (ufuncimpl == NULL) { + return NULL; } - ndim = PyArray_NDIM(arr); + /* The below code assumes that all descriptors are identical: */ + assert(descrs[0] == descrs[1] && descrs[0] == descrs[2]); - /* - * Set up the output data type, using the input's exact - * data type if the type number didn't change to preserve - * metadata - */ - if (PyArray_DESCR(arr)->type_num == otype_final) { - if (PyArray_ISNBO(PyArray_DESCR(arr)->byteorder)) { - op_dtypes[0] = PyArray_DESCR(arr); - Py_INCREF(op_dtypes[0]); - } - else { - op_dtypes[0] = PyArray_DescrNewByteorder(PyArray_DESCR(arr), - NPY_NATIVE); - } - } - else { - op_dtypes[0] = PyArray_DescrFromType(otype_final); - } - if (op_dtypes[0] == NULL) { + if (PyDataType_REFCHK(descrs[2]) && descrs[2]->type_num != NPY_OBJECT) { + /* This can be removed, but the initial element copy needs fixing */ + PyErr_SetString(PyExc_TypeError, + "reduceat currently only supports `object` dtype with " + "references"); goto fail; } + PyArrayMethod_Context context = { + .caller = (PyObject *)ufunc, + .method = ufuncimpl, + .descriptors = descrs, + }; + + ndim = PyArray_NDIM(arr); + #if NPY_UF_DBG_TRACING printf("Found %s.%s inner loop with dtype : ", ufunc_name, opname); PyObject_Print((PyObject *)op_dtypes[0], stdout, 0); @@ -3866,7 +3465,7 @@ PyUFunc_Reduceat(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *ind, #endif /* Set up the op_axes for the outer loop */ - for (i = 0, idim = 0; idim < ndim; ++idim) { + for (idim = 0; idim < ndim; ++idim) { /* Use the i-th iteration dimension to match up ind */ if (idim == axis) { op_axes_arrays[0][idim] = axis; @@ -3885,11 +3484,13 @@ PyUFunc_Reduceat(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *ind, op[2] = ind; if (out != NULL || ndim > 1 || !PyArray_ISALIGNED(arr) || - !PyArray_EquivTypes(op_dtypes[0], PyArray_DESCR(arr))) { + !PyArray_EquivTypes(descrs[0], PyArray_DESCR(arr))) { need_outer_iterator = 1; } if (need_outer_iterator) { + PyArray_Descr *op_dtypes[3] = {descrs[0], descrs[1], NULL}; + npy_uint32 flags = NPY_ITER_ZEROSIZE_OK| NPY_ITER_REFS_OK| NPY_ITER_MULTI_INDEX| @@ -3918,8 +3519,7 @@ PyUFunc_Reduceat(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *ind, NPY_UF_DBG_PRINT("Allocating outer iterator\n"); iter = NpyIter_AdvancedNew(3, op, flags, NPY_KEEPORDER, NPY_UNSAFE_CASTING, - op_flags, - op_dtypes, + op_flags, op_dtypes, ndim, op_axes, NULL, 0); if (iter == NULL) { goto fail; @@ -3943,11 +3543,15 @@ PyUFunc_Reduceat(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *ind, Py_INCREF(out); } } - /* Allocate the output for when there's no outer iterator */ - else if (out == NULL) { - Py_INCREF(op_dtypes[0]); + else { + /* + * Allocate the output for when there's no outer iterator, we always + * use the outer_iteration path when `out` is passed. + */ + assert(out == NULL); + Py_INCREF(descrs[0]); op[0] = out = (PyArrayObject *)PyArray_NewFromDescr( - &PyArray_Type, op_dtypes[0], + &PyArray_Type, descrs[0], 1, &ind_size, NULL, NULL, 0, NULL); if (out == NULL) { @@ -3955,6 +3559,28 @@ PyUFunc_Reduceat(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *ind, } } + npy_intp fixed_strides[3]; + if (need_outer_iterator) { + NpyIter_GetInnerFixedStrideArray(iter, fixed_strides); + } + else { + fixed_strides[1] = PyArray_STRIDES(op[1])[axis]; + } + /* The reduce axis does not advance here in the strided-loop */ + fixed_strides[0] = 0; + fixed_strides[2] = 0; + + NPY_ARRAYMETHOD_FLAGS flags = 0; + if (ufuncimpl->get_strided_loop(&context, + 1, 0, fixed_strides, &strided_loop, &auxdata, &flags) < 0) { + goto fail; + } + needs_api = (flags & NPY_METH_REQUIRES_PYAPI) != 0; + if (!(flags & NPY_METH_NO_FLOATINGPOINT_ERRORS)) { + /* Start with the floating-point exception flags cleared */ + npy_clear_floatstatus_barrier((char*)&iter); + } + /* * If the output has zero elements, return now. */ @@ -3972,7 +3598,8 @@ PyUFunc_Reduceat(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *ind, npy_intp stride0, stride1; npy_intp stride0_ind = PyArray_STRIDE(op[0], axis); - int itemsize = op_dtypes[0]->elsize; + int itemsize = descrs[0]->elsize; + needs_api |= NpyIter_IterationNeedsAPI(iter); /* Get the variables needed for the loop */ iternext = NpyIter_GetIterNext(iter, NULL); @@ -3992,10 +3619,11 @@ PyUFunc_Reduceat(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *ind, stride_copy[1] = stride1; stride_copy[2] = stride0; - NPY_BEGIN_THREADS_NDITER(iter); + if (!needs_api) { + NPY_BEGIN_THREADS_THRESHOLDED(NpyIter_GetIterSize(iter)); + } do { - for (i = 0; i < ind_size; ++i) { npy_intp start = reduceat_ind[i], end = (i == ind_size-1) ? count_m1+1 : @@ -4013,7 +3641,7 @@ PyUFunc_Reduceat(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *ind, * to the same memory, e.g. * np.add.reduceat(a, np.arange(len(a)), out=a). */ - if (otype == NPY_OBJECT) { + if (descrs[2]->type_num == NPY_OBJECT) { /* * Incref before decref to avoid the possibility of * the reference count being zero temporarily. @@ -4033,32 +3661,23 @@ PyUFunc_Reduceat(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *ind, dataptr_copy[1] += stride1; NPY_UF_DBG_PRINT1("iterator loop count %d\n", (int)count); - innerloop(dataptr_copy, &count, - stride_copy, innerloopdata); + res = strided_loop(&context, + dataptr_copy, &count, stride_copy, auxdata); } } - } while (iternext(iter)); + } while (res == 0 && iternext(iter)); NPY_END_THREADS; } else if (iter == NULL) { char *dataptr_copy[3]; - npy_intp stride_copy[3]; - int itemsize = op_dtypes[0]->elsize; + int itemsize = descrs[0]->elsize; npy_intp stride0_ind = PyArray_STRIDE(op[0], axis); + npy_intp stride1 = PyArray_STRIDE(op[1], axis); - /* Execute the loop with no iterators */ - npy_intp stride0 = 0, stride1 = PyArray_STRIDE(op[1], axis); - - int needs_api = PyDataType_REFCHK(op_dtypes[0]); - - NPY_UF_DBG_PRINT("UFunc: Reduce loop with no iterators\n"); - - stride_copy[0] = stride0; - stride_copy[1] = stride1; - stride_copy[2] = stride0; + NPY_UF_DBG_PRINT("UFunc: Reduce loop with no iterators\n"); if (!needs_api) { NPY_BEGIN_THREADS; @@ -4081,7 +3700,7 @@ PyUFunc_Reduceat(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *ind, * the same memory, e.g. * np.add.reduceat(a, np.arange(len(a)), out=a). */ - if (otype == NPY_OBJECT) { + if (descrs[2]->type_num == NPY_OBJECT) { /* * Incref before decref to avoid the possibility of the * reference count being zero temporarily. @@ -4101,8 +3720,11 @@ PyUFunc_Reduceat(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *ind, dataptr_copy[1] += stride1; NPY_UF_DBG_PRINT1("iterator loop count %d\n", (int)count); - innerloop(dataptr_copy, &count, - stride_copy, innerloopdata); + res = strided_loop(&context, + dataptr_copy, &count, fixed_strides, auxdata); + if (res != 0) { + break; + } } } @@ -4110,17 +3732,34 @@ PyUFunc_Reduceat(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *ind, } finish: - if (NpyIter_Close(iter) < 0) { - goto fail; + NPY_AUXDATA_FREE(auxdata); + Py_DECREF(descrs[0]); + Py_DECREF(descrs[1]); + Py_DECREF(descrs[2]); + + if (!NpyIter_Deallocate(iter)) { + res = -1; + } + + if (res == 0 && !(flags & NPY_METH_NO_FLOATINGPOINT_ERRORS)) { + /* NOTE: We could check float errors even when `res < 0` */ + res = _check_ufunc_fperr(errormask, NULL, "reduceat"); + } + + if (res < 0) { + Py_DECREF(out); + return NULL; } - Py_XDECREF(op_dtypes[0]); - NpyIter_Deallocate(iter); return (PyObject *)out; fail: Py_XDECREF(out); - Py_XDECREF(op_dtypes[0]); + + NPY_AUXDATA_FREE(auxdata); + Py_XDECREF(descrs[0]); + Py_XDECREF(descrs[1]); + Py_XDECREF(descrs[2]); NpyIter_Deallocate(iter); @@ -4128,32 +3767,105 @@ PyUFunc_Reduceat(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *ind, } +static npy_bool +tuple_all_none(PyObject *tup) { + npy_intp i; + for (i = 0; i < PyTuple_GET_SIZE(tup); ++i) { + if (PyTuple_GET_ITEM(tup, i) != Py_None) { + return NPY_FALSE; + } + } + return NPY_TRUE; +} + + +static int +_set_full_args_out(int nout, PyObject *out_obj, ufunc_full_args *full_args) +{ + if (PyTuple_CheckExact(out_obj)) { + if (PyTuple_GET_SIZE(out_obj) != nout) { + PyErr_SetString(PyExc_ValueError, + "The 'out' tuple must have exactly " + "one entry per ufunc output"); + return -1; + } + if (tuple_all_none(out_obj)) { + return 0; + } + else { + Py_INCREF(out_obj); + full_args->out = out_obj; + } + } + else if (nout == 1) { + if (out_obj == Py_None) { + return 0; + } + /* Can be an array if it only has one output */ + full_args->out = PyTuple_Pack(1, out_obj); + if (full_args->out == NULL) { + return -1; + } + } + else { + PyErr_SetString(PyExc_TypeError, + nout > 1 ? "'out' must be a tuple of arrays" : + "'out' must be an array or a tuple with " + "a single array"); + return -1; + } + return 0; +} + + +/* + * Convert function which replaces np._NoValue with NULL. + * As a converter returns 0 on error and 1 on success. + */ +static int +_not_NoValue(PyObject *obj, PyObject **out) +{ + static PyObject *NoValue = NULL; + npy_cache_import("numpy", "_NoValue", &NoValue); + if (NoValue == NULL) { + return 0; + } + if (obj == NoValue) { + *out = NULL; + } + else { + *out = obj; + } + return 1; +} + + +/* forward declaration */ +static PyArray_DTypeMeta * _get_dtype(PyObject *dtype_obj); + /* * This code handles reduce, reduceat, and accumulate * (accumulate and reduce are special cases of the more general reduceat * but they are handled separately for speed) */ static PyObject * -PyUFunc_GenericReduction(PyUFuncObject *ufunc, PyObject *args, - PyObject *kwds, int operation) +PyUFunc_GenericReduction(PyUFuncObject *ufunc, + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames, int operation) { int i, naxes=0, ndim; int axes[NPY_MAXDIMS]; - PyObject *axes_in = NULL; - PyArrayObject *mp = NULL, *ret = NULL; - PyObject *op, *res = NULL; - PyObject *obj_ind, *context; + + ufunc_full_args full_args = {NULL, NULL}; + PyObject *axes_obj = NULL; + PyArrayObject *mp = NULL, *wheremask = NULL, *ret = NULL; + PyObject *op = NULL; PyArrayObject *indices = NULL; - PyArray_Descr *otype = NULL; + PyArray_DTypeMeta *signature[3] = {NULL, NULL, NULL}; PyArrayObject *out = NULL; int keepdims = 0; PyObject *initial = NULL; - static char *reduce_kwlist[] = { - "array", "axis", "dtype", "out", "keepdims", "initial", NULL}; - static char *accumulate_kwlist[] = { - "array", "axis", "dtype", "out", NULL}; - static char *reduceat_kwlist[] = { - "array", "indices", "axis", "dtype", "out", NULL}; + npy_bool out_is_passed_by_position; + static char *_reduce_type[] = {"reduce", "accumulate", "reduceat", NULL}; @@ -4179,103 +3891,169 @@ PyUFunc_GenericReduction(PyUFuncObject *ufunc, PyObject *args, _reduce_type[operation]); return NULL; } - /* if there is a tuple of 1 for `out` in kwds, unpack it */ - if (kwds != NULL) { - PyObject *out_obj = PyDict_GetItem(kwds, npy_um_str_out); - if (out_obj != NULL && PyTuple_CheckExact(out_obj)) { - if (PyTuple_GET_SIZE(out_obj) != 1) { - PyErr_SetString(PyExc_ValueError, - "The 'out' tuple must have exactly one entry"); - return NULL; - } - out_obj = PyTuple_GET_ITEM(out_obj, 0); - PyDict_SetItem(kwds, npy_um_str_out, out_obj); - } - } + /* + * Perform argument parsing, but start by only extracting. This is + * just to preserve the behaviour that __array_ufunc__ did not perform + * any checks on arguments, and we could change this or change it for + * certain parameters. + */ + PyObject *otype_obj = NULL, *out_obj = NULL, *indices_obj = NULL; + PyObject *keepdims_obj = NULL, *wheremask_obj = NULL; if (operation == UFUNC_REDUCEAT) { - PyArray_Descr *indtype; - indtype = PyArray_DescrFromType(NPY_INTP); - if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|OO&O&:reduceat", reduceat_kwlist, - &op, - &obj_ind, - &axes_in, - PyArray_DescrConverter2, &otype, - PyArray_OutputConverter, &out)) { + NPY_PREPARE_ARGPARSER; + + if (npy_parse_arguments("reduceat", args, len_args, kwnames, + "array", NULL, &op, + "indices", NULL, &indices_obj, + "|axis", NULL, &axes_obj, + "|dtype", NULL, &otype_obj, + "|out", NULL, &out_obj, + NULL, NULL, NULL) < 0) { goto fail; } - indices = (PyArrayObject *)PyArray_FromAny(obj_ind, indtype, - 1, 1, NPY_ARRAY_CARRAY, NULL); - if (indices == NULL) { + /* Prepare inputs for PyUfunc_CheckOverride */ + full_args.in = PyTuple_Pack(2, op, indices_obj); + if (full_args.in == NULL) { goto fail; } + out_is_passed_by_position = len_args >= 5; } else if (operation == UFUNC_ACCUMULATE) { - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OO&O&:accumulate", - accumulate_kwlist, - &op, - &axes_in, - PyArray_DescrConverter2, &otype, - PyArray_OutputConverter, &out)) { + NPY_PREPARE_ARGPARSER; + + if (npy_parse_arguments("accumulate", args, len_args, kwnames, + "array", NULL, &op, + "|axis", NULL, &axes_obj, + "|dtype", NULL, &otype_obj, + "|out", NULL, &out_obj, + NULL, NULL, NULL) < 0) { goto fail; } + /* Prepare input for PyUfunc_CheckOverride */ + full_args.in = PyTuple_Pack(1, op); + if (full_args.in == NULL) { + goto fail; + } + out_is_passed_by_position = len_args >= 4; } else { - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OO&O&iO:reduce", - reduce_kwlist, - &op, - &axes_in, - PyArray_DescrConverter2, &otype, - PyArray_OutputConverter, &out, - &keepdims, &initial)) { + NPY_PREPARE_ARGPARSER; + + if (npy_parse_arguments("reduce", args, len_args, kwnames, + "array", NULL, &op, + "|axis", NULL, &axes_obj, + "|dtype", NULL, &otype_obj, + "|out", NULL, &out_obj, + "|keepdims", NULL, &keepdims_obj, + "|initial", &_not_NoValue, &initial, + "|where", NULL, &wheremask_obj, + NULL, NULL, NULL) < 0) { + goto fail; + } + /* Prepare input for PyUfunc_CheckOverride */ + full_args.in = PyTuple_Pack(1, op); + if (full_args.in == NULL) { goto fail; } + out_is_passed_by_position = len_args >= 4; } - /* Ensure input is an array */ - if (!PyArray_Check(op) && !PyArray_IsScalar(op, Generic)) { - context = Py_BuildValue("O(O)i", ufunc, op, 0); + + /* Normalize output for PyUFunc_CheckOverride and conversion. */ + if (out_is_passed_by_position) { + /* in this branch, out is always wrapped in a tuple. */ + if (out_obj != Py_None) { + full_args.out = PyTuple_Pack(1, out_obj); + if (full_args.out == NULL) { + goto fail; + } + } } - else { - context = NULL; + else if (out_obj) { + if (_set_full_args_out(1, out_obj, &full_args) < 0) { + goto fail; + } + /* Ensure that out_obj is the array, not the tuple: */ + if (full_args.out != NULL) { + out_obj = PyTuple_GET_ITEM(full_args.out, 0); + } } - mp = (PyArrayObject *)PyArray_FromAny(op, NULL, 0, 0, 0, context); - Py_XDECREF(context); - if (mp == NULL) { - goto fail; + + /* We now have all the information required to check for Overrides */ + PyObject *override = NULL; + int errval = PyUFunc_CheckOverride(ufunc, _reduce_type[operation], + full_args.in, full_args.out, args, len_args, kwnames, &override); + if (errval) { + return NULL; + } + else if (override) { + Py_XDECREF(full_args.in); + Py_XDECREF(full_args.out); + return override; } - ndim = PyArray_NDIM(mp); + /* Finish parsing of all parameters (no matter which reduce-like) */ + if (indices_obj) { + PyArray_Descr *indtype = PyArray_DescrFromType(NPY_INTP); - /* Check to see that type (and otype) is not FLEXIBLE */ - if (PyArray_ISFLEXIBLE(mp) || - (otype && PyTypeNum_ISFLEXIBLE(otype->type_num))) { - PyErr_Format(PyExc_TypeError, - "cannot perform %s with flexible type", - _reduce_type[operation]); + indices = (PyArrayObject *)PyArray_FromAny(indices_obj, + indtype, 1, 1, NPY_ARRAY_CARRAY, NULL); + if (indices == NULL) { + goto fail; + } + } + if (otype_obj && otype_obj != Py_None) { + /* Use `_get_dtype` because `dtype` is a DType and not the instance */ + signature[0] = _get_dtype(otype_obj); + if (signature[0] == NULL) { + goto fail; + } + } + if (out_obj && !PyArray_OutputConverter(out_obj, &out)) { + goto fail; + } + if (keepdims_obj && !PyArray_PythonPyIntFromInt(keepdims_obj, &keepdims)) { + goto fail; + } + if (wheremask_obj && !_wheremask_converter(wheremask_obj, &wheremask)) { + goto fail; + } + + /* Ensure input is an array */ + mp = (PyArrayObject *)PyArray_FromAny(op, NULL, 0, 0, 0, NULL); + if (mp == NULL) { goto fail; } + ndim = PyArray_NDIM(mp); + /* Convert the 'axis' parameter into a list of axes */ - if (axes_in == NULL) { - naxes = 1; - axes[0] = 0; + if (axes_obj == NULL) { + /* apply defaults */ + if (ndim == 0) { + naxes = 0; + } + else { + naxes = 1; + axes[0] = 0; + } } - /* Convert 'None' into all the axes */ - else if (axes_in == Py_None) { + else if (axes_obj == Py_None) { + /* Convert 'None' into all the axes */ naxes = ndim; for (i = 0; i < naxes; ++i) { axes[i] = i; } } - else if (PyTuple_Check(axes_in)) { - naxes = PyTuple_Size(axes_in); + else if (PyTuple_Check(axes_obj)) { + naxes = PyTuple_Size(axes_obj); if (naxes < 0 || naxes > NPY_MAXDIMS) { PyErr_SetString(PyExc_ValueError, "too many values for 'axis'"); goto fail; } for (i = 0; i < naxes; ++i) { - PyObject *tmp = PyTuple_GET_ITEM(axes_in, i); + PyObject *tmp = PyTuple_GET_ITEM(axes_obj, i); int axis = PyArray_PyIntAsInt(tmp); if (error_converting(axis)) { goto fail; @@ -4286,59 +4064,45 @@ PyUFunc_GenericReduction(PyUFuncObject *ufunc, PyObject *args, axes[i] = (int)axis; } } - /* Try to interpret axis as an integer */ else { - int axis = PyArray_PyIntAsInt(axes_in); + /* Try to interpret axis as an integer */ + int axis = PyArray_PyIntAsInt(axes_obj); /* TODO: PyNumber_Index would be good to use here */ if (error_converting(axis)) { goto fail; } - /* Special case letting axis={0 or -1} slip through for scalars */ - if (ndim == 0 && (axis == 0 || axis == -1)) { - axis = 0; - } - else if (check_and_adjust_axis(&axis, ndim) < 0) { - goto fail; - } - axes[0] = (int)axis; - naxes = 1; - } - - /* Check to see if input is zero-dimensional. */ - if (ndim == 0) { /* - * A reduction with no axes is still valid but trivial. * As a special case for backwards compatibility in 'sum', - * 'prod', et al, also allow a reduction where axis=0, even + * 'prod', et al, also allow a reduction for scalars even * though this is technically incorrect. */ - naxes = 0; - - if (!(operation == UFUNC_REDUCE && - (naxes == 0 || (naxes == 1 && axes[0] == 0)))) { - PyErr_Format(PyExc_TypeError, "cannot %s on a scalar", - _reduce_type[operation]); + if (ndim == 0 && (axis == 0 || axis == -1)) { + naxes = 0; + } + else if (check_and_adjust_axis(&axis, ndim) < 0) { goto fail; } + else { + axes[0] = (int)axis; + naxes = 1; + } } /* - * If out is specified it determines otype - * unless otype already specified. + * If no dtype is specified and out is not specified, we override the + * integer and bool dtype used for add and multiply. + * + * TODO: The following should be handled by a promoter! */ - if (otype == NULL && out != NULL) { - otype = PyArray_DESCR(out); - Py_INCREF(otype); - } - if (otype == NULL) { + if (signature[0] == NULL && out == NULL) { /* * For integer types --- make sure at least a long * is used for add and multiply reduction to avoid overflow */ int typenum = PyArray_TYPE(mp); if ((PyTypeNum_ISBOOL(typenum) || PyTypeNum_ISINTEGER(typenum)) - && ((strcmp(ufunc->name,"add") == 0) - || (strcmp(ufunc->name,"multiply") == 0))) { + && ((strcmp(ufunc->name, "add") == 0) + || (strcmp(ufunc->name, "multiply") == 0))) { if (PyTypeNum_ISBOOL(typenum)) { typenum = NPY_LONG; } @@ -4350,274 +4114,836 @@ PyUFunc_GenericReduction(PyUFuncObject *ufunc, PyObject *args, typenum = NPY_LONG; } } + signature[0] = PyArray_DTypeFromTypeNum(typenum); } - otype = PyArray_DescrFromType(typenum); } - + Py_XINCREF(signature[0]); + signature[2] = signature[0]; switch(operation) { case UFUNC_REDUCE: - ret = PyUFunc_Reduce(ufunc, mp, out, naxes, axes, - otype, keepdims, initial); + ret = PyUFunc_Reduce(ufunc, + mp, out, naxes, axes, signature, keepdims, initial, wheremask); + Py_XSETREF(wheremask, NULL); break; case UFUNC_ACCUMULATE: + if (ndim == 0) { + PyErr_SetString(PyExc_TypeError, "cannot accumulate on a scalar"); + goto fail; + } if (naxes != 1) { PyErr_SetString(PyExc_ValueError, "accumulate does not allow multiple axes"); goto fail; } - ret = (PyArrayObject *)PyUFunc_Accumulate(ufunc, mp, out, axes[0], - otype->type_num); + ret = (PyArrayObject *)PyUFunc_Accumulate(ufunc, + mp, out, axes[0], signature); break; case UFUNC_REDUCEAT: + if (ndim == 0) { + PyErr_SetString(PyExc_TypeError, "cannot reduceat on a scalar"); + goto fail; + } if (naxes != 1) { PyErr_SetString(PyExc_ValueError, "reduceat does not allow multiple axes"); goto fail; } - ret = (PyArrayObject *)PyUFunc_Reduceat(ufunc, mp, indices, out, - axes[0], otype->type_num); - Py_DECREF(indices); + ret = (PyArrayObject *)PyUFunc_Reduceat(ufunc, + mp, indices, out, axes[0], signature); + Py_SETREF(indices, NULL); break; } - Py_DECREF(mp); - Py_DECREF(otype); - if (ret == NULL) { - return NULL; + goto fail; } - /* If an output parameter was provided, don't wrap it */ - if (out != NULL) { - return (PyObject *)ret; - } + Py_DECREF(signature[0]); + Py_DECREF(signature[1]); + Py_DECREF(signature[2]); - if (Py_TYPE(op) != Py_TYPE(ret)) { - res = PyObject_CallMethod(op, "__array_wrap__", "O", ret); - if (res == NULL) { - PyErr_Clear(); - } - else if (res == Py_None) { - Py_DECREF(res); + Py_DECREF(mp); + Py_XDECREF(full_args.in); + Py_XDECREF(full_args.out); + + /* Wrap and return the output */ + { + /* Find __array_wrap__ - note that these rules are different to the + * normal ufunc path + */ + PyObject *wrap; + if (out != NULL) { + wrap = Py_None; + Py_INCREF(wrap); + } + else if (Py_TYPE(op) != Py_TYPE(ret)) { + wrap = PyObject_GetAttr(op, npy_um_str_array_wrap); + if (wrap == NULL) { + PyErr_Clear(); + } + else if (!PyCallable_Check(wrap)) { + Py_DECREF(wrap); + wrap = NULL; + } } else { - Py_DECREF(ret); - return res; + wrap = NULL; } + return _apply_array_wrap(wrap, ret, NULL); } - return PyArray_Return(ret); fail: - Py_XDECREF(otype); + Py_XDECREF(signature[0]); + Py_XDECREF(signature[1]); + Py_XDECREF(signature[2]); + Py_XDECREF(mp); + Py_XDECREF(wheremask); + Py_XDECREF(indices); + Py_XDECREF(full_args.in); + Py_XDECREF(full_args.out); return NULL; } + /* - * This function analyzes the input arguments - * and determines an appropriate __array_wrap__ function to call - * for the outputs. + * Perform a basic check on `dtype`, `sig`, and `signature` since only one + * may be set. If `sig` is used, writes it into `out_signature` (which should + * be set to `signature_obj` so that following code only requires to handle + * `signature_obj`). * - * If an output argument is provided, then it is wrapped - * with its own __array_wrap__ not with the one determined by - * the input arguments. + * Does NOT incref the output! This only copies the borrowed references + * gotten during the argument parsing. * - * if the provided output argument is already an array, - * the wrapping function is None (which means no wrapping will - * be done --- not even PyArray_Return). + * This function does not do any normalization of the input dtype tuples, + * this happens after the array-ufunc override check currently. + */ +static int +_check_and_copy_sig_to_signature( + PyObject *sig_obj, PyObject *signature_obj, PyObject *dtype, + PyObject **out_signature) +{ + *out_signature = NULL; + if (signature_obj != NULL) { + *out_signature = signature_obj; + } + + if (sig_obj != NULL) { + if (*out_signature != NULL) { + PyErr_SetString(PyExc_TypeError, + "cannot specify both 'sig' and 'signature'"); + *out_signature = NULL; + return -1; + } + *out_signature = sig_obj; + } + + if (dtype != NULL) { + if (*out_signature != NULL) { + PyErr_SetString(PyExc_TypeError, + "cannot specify both 'signature' and 'dtype'"); + return -1; + } + /* dtype needs to be converted, delay after the override check */ + } + return 0; +} + + +/* + * Note: This function currently lets DType classes pass, but in general + * the class (not the descriptor instance) is the preferred input, so the + * parsing should eventually be adapted to prefer classes and possible + * deprecated instances. (Users should not notice that much, since `np.float64` + * or "float64" usually denotes the DType class rather than the instance.) + */ +static PyArray_DTypeMeta * +_get_dtype(PyObject *dtype_obj) { + if (PyObject_TypeCheck(dtype_obj, &PyArrayDTypeMeta_Type)) { + Py_INCREF(dtype_obj); + return (PyArray_DTypeMeta *)dtype_obj; + } + else { + PyArray_Descr *descr = NULL; + if (!PyArray_DescrConverter(dtype_obj, &descr)) { + return NULL; + } + PyArray_DTypeMeta *out = NPY_DTYPE(descr); + if (NPY_UNLIKELY(!NPY_DT_is_legacy(out))) { + /* TODO: this path was unreachable when added. */ + PyErr_SetString(PyExc_TypeError, + "Cannot pass a new user DType instance to the `dtype` or " + "`signature` arguments of ufuncs. Pass the DType class " + "instead."); + Py_DECREF(descr); + return NULL; + } + else if (NPY_UNLIKELY(out->singleton != descr)) { + /* This does not warn about `metadata`, but units is important. */ + if (!PyArray_EquivTypes(out->singleton, descr)) { + /* Deprecated NumPy 1.21.2 (was an accidental error in 1.21) */ + if (DEPRECATE( + "The `dtype` and `signature` arguments to " + "ufuncs only select the general DType and not details " + "such as the byte order or time unit (with rare " + "exceptions see release notes). To avoid this warning " + "please use the scalar types `np.float64`, or string " + "notation.\n" + "In rare cases where the time unit was preserved, " + "either cast the inputs or provide an output array. " + "In the future NumPy may transition to allow providing " + "`dtype=` to denote the outputs `dtype` as well. " + "(Deprecated NumPy 1.21)") < 0) { + Py_DECREF(descr); + return NULL; + } + } + } + Py_INCREF(out); + Py_DECREF(descr); + return out; + } +} + + +/* + * Finish conversion parsing of the DType signature. NumPy always only + * honored the type number for passed in descriptors/dtypes. + * The `dtype` argument is interpreted as the first output DType (not + * descriptor). + * Unlike the dtype of an `out` array, it influences loop selection! * - * A NULL is placed in output_wrap for outputs that - * should just have PyArray_Return called. + * It is the callers responsibility to clean `signature` and NULL it before + * calling. */ -static void -_find_array_wrap(ufunc_full_args args, PyObject *kwds, - PyObject **output_wrap, int nin, int nout) +static int +_get_fixed_signature(PyUFuncObject *ufunc, + PyObject *dtype_obj, PyObject *signature_obj, + PyArray_DTypeMeta **signature) { - int i; - PyObject *obj; - PyObject *wrap = NULL; + if (dtype_obj == NULL && signature_obj == NULL) { + return 0; + } - /* - * If a 'subok' parameter is passed and isn't True, don't wrap but put None - * into slots with out arguments which means return the out argument - */ - if (kwds != NULL && (obj = PyDict_GetItem(kwds, - npy_um_str_subok)) != NULL) { - if (obj != Py_True) { - /* skip search for wrap members */ - goto handle_out; + int nin = ufunc->nin, nout = ufunc->nout, nop = nin + nout; + + if (dtype_obj != NULL) { + if (dtype_obj == Py_None) { + /* If `dtype=None` is passed, no need to do anything */ + return 0; + } + if (nout == 0) { + /* This may be allowed (NumPy does not do this)? */ + PyErr_SetString(PyExc_TypeError, + "Cannot provide `dtype` when a ufunc has no outputs"); + return -1; + } + PyArray_DTypeMeta *dtype = _get_dtype(dtype_obj); + if (dtype == NULL) { + return -1; } + for (int i = nin; i < nop; i++) { + Py_INCREF(dtype); + signature[i] = dtype; + } + Py_DECREF(dtype); + return 0; } - /* - * Determine the wrapping function given by the input arrays - * (could be NULL). - */ - wrap = _find_array_method(args.in, npy_um_str_array_wrap); + assert(signature_obj != NULL); + /* Fill in specified_types from the tuple or string (signature_obj) */ + if (PyTuple_Check(signature_obj)) { + Py_ssize_t n = PyTuple_GET_SIZE(signature_obj); + if (n == 1 && nop != 1) { + /* + * Special handling, because we deprecate this path. The path + * probably mainly existed since the `dtype=obj` was passed through + * as `(obj,)` and parsed later. + */ + if (PyTuple_GET_ITEM(signature_obj, 0) == Py_None) { + PyErr_SetString(PyExc_TypeError, + "a single item type tuple cannot contain None."); + return -1; + } + if (DEPRECATE("The use of a length 1 tuple for the ufunc " + "`signature` is deprecated. Use `dtype` or fill the" + "tuple with `None`s.") < 0) { + return -1; + } + /* Use the same logic as for `dtype=` */ + return _get_fixed_signature(ufunc, + PyTuple_GET_ITEM(signature_obj, 0), NULL, signature); + } + if (n != nop) { + PyErr_Format(PyExc_ValueError, + "a type-tuple must be specified of length %d for ufunc '%s'", + nop, ufunc_get_name_cstr(ufunc)); + return -1; + } + for (int i = 0; i < nop; ++i) { + PyObject *item = PyTuple_GET_ITEM(signature_obj, i); + if (item == Py_None) { + continue; + } + else { + signature[i] = _get_dtype(item); + if (signature[i] == NULL) { + return -1; + } + else if (i < nin && NPY_DT_is_abstract(signature[i])) { + /* + * We reject abstract input signatures for now. These + * can probably be defined by finding the common DType with + * the actual input and using the result of this for the + * promotion. + */ + PyErr_SetString(PyExc_TypeError, + "Input DTypes to the signature must not be " + "abstract. The behaviour may be defined in the " + "future."); + return -1; + } + } + } + } + else if (PyBytes_Check(signature_obj) || PyUnicode_Check(signature_obj)) { + PyObject *str_object = NULL; - /* - * For all the output arrays decide what to do. - * - * 1) Use the wrap function determined from the input arrays - * This is the default if the output array is not - * passed in. - * - * 2) Use the __array_wrap__ method of the output object - * passed in. -- this is special cased for - * exact ndarray so that no PyArray_Return is - * done in that case. - */ -handle_out: - if (args.out == NULL) { - for (i = 0; i < nout; i++) { - Py_XINCREF(wrap); - output_wrap[i] = wrap; + if (PyBytes_Check(signature_obj)) { + str_object = PyUnicode_FromEncodedObject(signature_obj, NULL, NULL); + if (str_object == NULL) { + return -1; + } + } + else { + Py_INCREF(signature_obj); + str_object = signature_obj; + } + + Py_ssize_t length; + const char *str = PyUnicode_AsUTF8AndSize(str_object, &length); + if (str == NULL) { + Py_DECREF(str_object); + return -1; + } + + if (length != 1 && (length != nin+nout + 2 || + str[nin] != '-' || str[nin+1] != '>')) { + PyErr_Format(PyExc_ValueError, + "a type-string for %s, %d typecode(s) before and %d after " + "the -> sign", ufunc_get_name_cstr(ufunc), nin, nout); + Py_DECREF(str_object); + return -1; + } + if (length == 1 && nin+nout != 1) { + Py_DECREF(str_object); + if (DEPRECATE("The use of a length 1 string for the ufunc " + "`signature` is deprecated. Use `dtype` attribute or " + "pass a tuple with `None`s.") < 0) { + return -1; + } + /* `signature="l"` is the same as `dtype="l"` */ + return _get_fixed_signature(ufunc, str_object, NULL, signature); + } + else { + for (int i = 0; i < nin+nout; ++i) { + npy_intp istr = i < nin ? i : i+2; + PyArray_Descr *descr = PyArray_DescrFromType(str[istr]); + if (descr == NULL) { + Py_DECREF(str_object); + return -1; + } + signature[i] = NPY_DTYPE(descr); + Py_INCREF(signature[i]); + Py_DECREF(descr); + } + Py_DECREF(str_object); } } else { - for (i = 0; i < nout; i++) { - output_wrap[i] = _get_output_array_method( - PyTuple_GET_ITEM(args.out, i), npy_um_str_array_wrap, wrap); + PyErr_SetString(PyExc_TypeError, + "the signature object to ufunc must be a string or a tuple."); + return -1; + } + return 0; +} + + +/* + * Fill in the actual descriptors used for the operation. This function + * supports falling back to the legacy `ufunc->type_resolver`. + * + * We guarantee the array-method that all passed in descriptors are of the + * correct DType instance (i.e. a string can just fetch the length, it doesn't + * need to "cast" to string first). + */ +static int +resolve_descriptors(int nop, + PyUFuncObject *ufunc, PyArrayMethodObject *ufuncimpl, + PyArrayObject *operands[], PyArray_Descr *dtypes[], + PyArray_DTypeMeta *signature[], NPY_CASTING casting) +{ + int retval = -1; + PyArray_Descr *original_dtypes[NPY_MAXARGS]; + + for (int i = 0; i < nop; ++i) { + if (operands[i] == NULL) { + original_dtypes[i] = NULL; + } + else { + /* + * The dtype may mismatch the signature, in which case we need + * to make it fit before calling the resolution. + */ + PyArray_Descr *descr = PyArray_DTYPE(operands[i]); + original_dtypes[i] = PyArray_CastDescrToDType(descr, signature[i]); + if (original_dtypes[i] == NULL) { + nop = i; /* only this much is initialized */ + goto finish; + } } } - Py_XDECREF(wrap); - return; + NPY_UF_DBG_PRINT("Resolving the descriptors\n"); + + if (ufuncimpl->resolve_descriptors != &wrapped_legacy_resolve_descriptors) { + /* The default: use the `ufuncimpl` as nature intended it */ + NPY_CASTING safety = ufuncimpl->resolve_descriptors(ufuncimpl, + signature, original_dtypes, dtypes); + if (safety < 0) { + goto finish; + } + if (NPY_UNLIKELY(PyArray_MinCastSafety(safety, casting) != casting)) { + /* TODO: Currently impossible to reach (specialized unsafe loop) */ + PyErr_Format(PyExc_TypeError, + "The ufunc implementation for %s with the given dtype " + "signature is not possible under the casting rule %s", + ufunc_get_name_cstr(ufunc), npy_casting_to_string(casting)); + goto finish; + } + retval = 0; + } + else { + /* + * Fall-back to legacy resolver using `operands`, used exclusively + * for datetime64/timedelta64 and custom ufuncs (in pyerfa/astropy). + */ + retval = ufunc->type_resolver(ufunc, casting, operands, NULL, dtypes); + } + + finish: + for (int i = 0; i < nop; i++) { + Py_XDECREF(original_dtypes[i]); + } + return retval; } +/** + * Wraps all outputs and returns the result (which may be NULL on error). + * + * Use __array_wrap__ on all outputs + * if present on one of the input arguments. + * If present for multiple inputs: + * use __array_wrap__ of input object with largest + * __array_priority__ (default = 0.0) + * + * Exception: we should not wrap outputs for items already + * passed in as output-arguments. These items should either + * be left unwrapped or wrapped by calling their own __array_wrap__ + * routine. + * + * For each output argument, wrap will be either + * NULL --- call PyArray_Return() -- default if no output arguments given + * None --- array-object passed in don't call PyArray_Return + * method --- the __array_wrap__ method to call. + * + * @param ufunc + * @param full_args Original inputs and outputs + * @param subok Whether subclasses are allowed + * @param result_arrays The ufunc result(s). REFERENCES ARE STOLEN! + */ static PyObject * -ufunc_generic_call(PyUFuncObject *ufunc, PyObject *args, PyObject *kwds) +replace_with_wrapped_result_and_return(PyUFuncObject *ufunc, + ufunc_full_args full_args, npy_bool subok, + PyArrayObject *result_arrays[]) { - int i; - PyArrayObject *mps[NPY_MAXARGS]; PyObject *retobj[NPY_MAXARGS]; PyObject *wraparr[NPY_MAXARGS]; - PyObject *override = NULL; - ufunc_full_args full_args = {NULL, NULL}; - int errval; + _find_array_wrap(full_args, subok, wraparr, ufunc->nin, ufunc->nout); - errval = PyUFunc_CheckOverride(ufunc, "__call__", args, kwds, &override); - if (errval) { - return NULL; - } - else if (override) { - return override; + /* wrap outputs */ + for (int i = 0; i < ufunc->nout; i++) { + _ufunc_context context; + + context.ufunc = ufunc; + context.args = full_args; + context.out_i = i; + + retobj[i] = _apply_array_wrap(wraparr[i], result_arrays[i], &context); + result_arrays[i] = NULL; /* Was DECREF'ed and (probably) wrapped */ + if (retobj[i] == NULL) { + goto fail; + } } - errval = PyUFunc_GenericFunction(ufunc, args, kwds, mps); - if (errval < 0) { - if (errval == -1) { + if (ufunc->nout == 1) { + return retobj[0]; + } + else { + PyObject *result = PyTuple_New(ufunc->nout); + if (result == NULL) { return NULL; } - else if (ufunc->nin == 2 && ufunc->nout == 1) { - /* - * For array_richcompare's benefit -- see the long comment in - * get_ufunc_arguments. - */ - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; + for (int i = 0; i < ufunc->nout; i++) { + PyTuple_SET_ITEM(result, i, retobj[i]); + } + return result; + } + + fail: + for (int i = 0; i < ufunc->nout; i++) { + if (result_arrays[i] != NULL) { + Py_DECREF(result_arrays[i]); } else { - PyErr_SetString(PyExc_TypeError, - "XX can't happen, please report a bug XX"); - return NULL; + Py_XDECREF(retobj[i]); } } + return NULL; +} - /* Free the input references */ - for (i = 0; i < ufunc->nin; i++) { - Py_XDECREF(mps[i]); - } + +/* + * Main ufunc call implementation. + * + * This implementation makes use of the "fastcall" way of passing keyword + * arguments and is called directly from `ufunc_generic_vectorcall` when + * Python has `tp_vectorcall` (Python 3.8+). + * If `tp_vectorcall` is not available, the dictionary `kwargs` are unpacked in + * `ufunc_generic_call` with fairly little overhead. + */ +static PyObject * +ufunc_generic_fastcall(PyUFuncObject *ufunc, + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames, + npy_bool outer) +{ + int errval; + int nin = ufunc->nin, nout = ufunc->nout, nop = ufunc->nargs; + + /* All following variables are cleared in the `fail` error path */ + ufunc_full_args full_args; + PyArrayObject *wheremask = NULL; + + PyArray_DTypeMeta *signature[NPY_MAXARGS]; + PyArrayObject *operands[NPY_MAXARGS]; + PyArray_DTypeMeta *operand_DTypes[NPY_MAXARGS]; + PyArray_Descr *operation_descrs[NPY_MAXARGS]; + PyObject *output_array_prepare[NPY_MAXARGS]; + /* Initialize all arrays (we usually only need a small part) */ + memset(signature, 0, nop * sizeof(*signature)); + memset(operands, 0, nop * sizeof(*operands)); + memset(operand_DTypes, 0, nop * sizeof(*operation_descrs)); + memset(operation_descrs, 0, nop * sizeof(*operation_descrs)); + memset(output_array_prepare, 0, nout * sizeof(*output_array_prepare)); /* - * Use __array_wrap__ on all outputs - * if present on one of the input arguments. - * If present for multiple inputs: - * use __array_wrap__ of input object with largest - * __array_priority__ (default = 0.0) - * - * Exception: we should not wrap outputs for items already - * passed in as output-arguments. These items should either - * be left unwrapped or wrapped by calling their own __array_wrap__ - * routine. - * - * For each output argument, wrap will be either - * NULL --- call PyArray_Return() -- default if no output arguments given - * None --- array-object passed in don't call PyArray_Return - * method --- the __array_wrap__ method to call. + * Note that the input (and possibly output) arguments are passed in as + * positional arguments. We extract these first and check for `out` + * passed by keyword later. + * Outputs and inputs are stored in `full_args.in` and `full_args.out` + * as tuples (or NULL when no outputs are passed). */ - if (make_full_arg_tuple(&full_args, ufunc->nin, ufunc->nout, args, kwds) < 0) { - goto fail; + + /* Check number of arguments */ + if (NPY_UNLIKELY((len_args < nin) || (len_args > nop))) { + PyErr_Format(PyExc_TypeError, + "%s() takes from %d to %d positional arguments but " + "%zd were given", + ufunc_get_name_cstr(ufunc) , nin, nop, len_args); + return NULL; } - _find_array_wrap(full_args, kwds, wraparr, ufunc->nin, ufunc->nout); - /* wrap outputs */ - for (i = 0; i < ufunc->nout; i++) { - int j = ufunc->nin+i; - PyObject *wrap = wraparr[i]; + /* Fetch input arguments. */ + full_args.in = PyArray_TupleFromItems(ufunc->nin, args, 0); + if (full_args.in == NULL) { + return NULL; + } - if (wrap == NULL) { - /* default behavior */ - retobj[i] = PyArray_Return(mps[j]); + /* + * If there are more arguments, they define the out args. Otherwise + * full_args.out is NULL for now, and the `out` kwarg may still be passed. + */ + npy_bool out_is_passed_by_position = len_args > nin; + if (out_is_passed_by_position) { + npy_bool all_none = NPY_TRUE; + + full_args.out = PyTuple_New(nout); + if (full_args.out == NULL) { + goto fail; + } + for (int i = nin; i < nop; i++) { + PyObject *tmp; + if (i < (int)len_args) { + tmp = args[i]; + if (tmp != Py_None) { + all_none = NPY_FALSE; + } + } + else { + tmp = Py_None; + } + Py_INCREF(tmp); + PyTuple_SET_ITEM(full_args.out, i-nin, tmp); } - else if (wrap == Py_None) { - Py_DECREF(wrap); - retobj[i] = (PyObject *)mps[j]; + if (all_none) { + Py_SETREF(full_args.out, NULL); } - else { - PyObject *res; - PyObject *args_tup; + } + else { + full_args.out = NULL; + } - /* Call the method with appropriate context */ - args_tup = _get_wrap_prepare_args(full_args); - if (args_tup == NULL) { + /* + * We have now extracted (but not converted) the input arguments. + * To simplify overrides, extract all other arguments (as objects only) + */ + PyObject *out_obj = NULL, *where_obj = NULL; + PyObject *axes_obj = NULL, *axis_obj = NULL; + PyObject *keepdims_obj = NULL, *casting_obj = NULL, *order_obj = NULL; + PyObject *subok_obj = NULL, *signature_obj = NULL, *sig_obj = NULL; + PyObject *dtype_obj = NULL, *extobj = NULL; + + /* Skip parsing if there are no keyword arguments, nothing left to do */ + if (kwnames != NULL) { + if (!ufunc->core_enabled) { + NPY_PREPARE_ARGPARSER; + + if (npy_parse_arguments(ufunc->name, args + len_args, 0, kwnames, + "$out", NULL, &out_obj, + "$where", NULL, &where_obj, + "$casting", NULL, &casting_obj, + "$order", NULL, &order_obj, + "$subok", NULL, &subok_obj, + "$dtype", NULL, &dtype_obj, + "$signature", NULL, &signature_obj, + "$sig", NULL, &sig_obj, + "$extobj", NULL, &extobj, + NULL, NULL, NULL) < 0) { goto fail; } - res = PyObject_CallFunction( - wrap, "O(OOi)", mps[j], ufunc, args_tup, i); - Py_DECREF(args_tup); + } + else { + NPY_PREPARE_ARGPARSER; + + if (npy_parse_arguments(ufunc->name, args + len_args, 0, kwnames, + "$out", NULL, &out_obj, + "$axes", NULL, &axes_obj, + "$axis", NULL, &axis_obj, + "$keepdims", NULL, &keepdims_obj, + "$casting", NULL, &casting_obj, + "$order", NULL, &order_obj, + "$subok", NULL, &subok_obj, + "$dtype", NULL, &dtype_obj, + "$signature", NULL, &signature_obj, + "$sig", NULL, &sig_obj, + "$extobj", NULL, &extobj, + NULL, NULL, NULL) < 0) { + goto fail; + } + if (NPY_UNLIKELY((axes_obj != NULL) && (axis_obj != NULL))) { + PyErr_SetString(PyExc_TypeError, + "cannot specify both 'axis' and 'axes'"); + goto fail; + } + } - /* Handle __array_wrap__ that does not accept a context argument */ - if (res == NULL && PyErr_ExceptionMatches(PyExc_TypeError)) { - PyErr_Clear(); - res = PyObject_CallFunctionObjArgs(wrap, mps[j], NULL); + /* Handle `out` arguments passed by keyword */ + if (out_obj != NULL) { + if (out_is_passed_by_position) { + PyErr_SetString(PyExc_TypeError, + "cannot specify 'out' as both a " + "positional and keyword argument"); + goto fail; } - Py_DECREF(wrap); - Py_DECREF(mps[j]); - mps[j] = NULL; /* Prevent fail double-freeing this */ - if (res == NULL) { + if (_set_full_args_out(nout, out_obj, &full_args) < 0) { goto fail; } - retobj[i] = res; } + /* + * Only one of signature, sig, and dtype should be passed. If `sig` + * was passed, this puts it into `signature_obj` instead (these + * are borrowed references). + */ + if (_check_and_copy_sig_to_signature( + sig_obj, signature_obj, dtype_obj, &signature_obj) < 0) { + goto fail; + } + } + + char *method; + if (!outer) { + method = "__call__"; + } + else { + method = "outer"; + } + /* We now have all the information required to check for Overrides */ + PyObject *override = NULL; + errval = PyUFunc_CheckOverride(ufunc, method, + full_args.in, full_args.out, + args, len_args, kwnames, &override); + if (errval) { + goto fail; + } + else if (override) { + Py_DECREF(full_args.in); + Py_XDECREF(full_args.out); + return override; + } + + if (outer) { + /* Outer uses special preparation of inputs (expand dims) */ + PyObject *new_in = prepare_input_arguments_for_outer(full_args.in, ufunc); + if (new_in == NULL) { + goto fail; + } + Py_SETREF(full_args.in, new_in); + } + + /* + * Parse the passed `dtype` or `signature` into an array containing + * PyArray_DTypeMeta and/or None. + */ + if (_get_fixed_signature(ufunc, + dtype_obj, signature_obj, signature) < 0) { + goto fail; + } + + NPY_ORDER order = NPY_KEEPORDER; + NPY_CASTING casting = NPY_DEFAULT_ASSIGN_CASTING; + npy_bool subok = NPY_TRUE; + int keepdims = -1; /* We need to know if it was passed */ + npy_bool force_legacy_promotion; + npy_bool allow_legacy_promotion; + if (convert_ufunc_arguments(ufunc, + /* extract operand related information: */ + full_args, operands, + operand_DTypes, &force_legacy_promotion, &allow_legacy_promotion, + /* extract general information: */ + order_obj, &order, + casting_obj, &casting, + subok_obj, &subok, + where_obj, &wheremask, + keepdims_obj, &keepdims) < 0) { + goto fail; + } + + /* + * Note that part of the promotion is to the complete the signature + * (until here it only represents the fixed part and is usually NULLs). + * + * After promotion, we could push the following logic into the ArrayMethod + * in the future. For now, we do it here. The type resolution step can + * be shared between the ufunc and gufunc code. + */ + PyArrayMethodObject *ufuncimpl = promote_and_get_ufuncimpl(ufunc, + operands, signature, + operand_DTypes, force_legacy_promotion, allow_legacy_promotion, + NPY_FALSE); + if (ufuncimpl == NULL) { + goto fail; + } + + /* Find the correct descriptors for the operation */ + if (resolve_descriptors(nop, ufunc, ufuncimpl, + operands, operation_descrs, signature, casting) < 0) { + goto fail; + } + + if (subok) { + _find_array_prepare(full_args, output_array_prepare, nout); } - Py_XDECREF(full_args.in); - Py_XDECREF(full_args.out); - - if (ufunc->nout == 1) { - return retobj[0]; + /* + * Do the final preparations and call the inner-loop. + */ + if (!ufunc->core_enabled) { + errval = PyUFunc_GenericFunctionInternal(ufunc, ufuncimpl, + operation_descrs, operands, extobj, casting, order, + output_array_prepare, full_args, /* for __array_prepare__ */ + wheremask); } else { - PyTupleObject *ret; + errval = PyUFunc_GeneralizedFunctionInternal(ufunc, ufuncimpl, + operation_descrs, operands, extobj, casting, order, + /* GUFuncs never (ever) called __array_prepare__! */ + axis_obj, axes_obj, keepdims); + } + if (errval < 0) { + goto fail; + } - ret = (PyTupleObject *)PyTuple_New(ufunc->nout); - for (i = 0; i < ufunc->nout; i++) { - PyTuple_SET_ITEM(ret, i, retobj[i]); + /* + * Clear all variables which are not needed any further. + * (From here on, we cannot `goto fail` any more.) + */ + Py_XDECREF(wheremask); + for (int i = 0; i < nop; i++) { + Py_DECREF(signature[i]); + Py_XDECREF(operand_DTypes[i]); + Py_DECREF(operation_descrs[i]); + if (i < nin) { + Py_DECREF(operands[i]); + } + else { + Py_XDECREF(output_array_prepare[i-nin]); } - return (PyObject *)ret; } + /* The following steals the references to the outputs: */ + PyObject *result = replace_with_wrapped_result_and_return(ufunc, + full_args, subok, operands+nin); + Py_XDECREF(full_args.in); + Py_XDECREF(full_args.out); + + return result; fail: Py_XDECREF(full_args.in); Py_XDECREF(full_args.out); - for (i = ufunc->nin; i < ufunc->nargs; i++) { - Py_XDECREF(mps[i]); + Py_XDECREF(wheremask); + for (int i = 0; i < ufunc->nargs; i++) { + Py_XDECREF(operands[i]); + Py_XDECREF(signature[i]); + Py_XDECREF(operand_DTypes[i]); + Py_XDECREF(operation_descrs[i]); + if (i < nout) { + Py_XDECREF(output_array_prepare[i]); + } } return NULL; } + +/* + * Implement vectorcallfunc which should be defined with Python 3.8+. + * In principle this could be backported, but the speed gain seems moderate + * since ufunc calls often do not have keyword arguments and always have + * a large overhead. The only user would potentially be cython probably. + */ +static PyObject * +ufunc_generic_vectorcall(PyObject *ufunc, + PyObject *const *args, size_t len_args, PyObject *kwnames) +{ + /* + * Unlike METH_FASTCALL, `len_args` may have a flag to signal that + * args[-1] may be (temporarily) used. So normalize it here. + */ + return ufunc_generic_fastcall((PyUFuncObject *)ufunc, + args, PyVectorcall_NARGS(len_args), kwnames, NPY_FALSE); +} + + NPY_NO_EXPORT PyObject * ufunc_geterr(PyObject *NPY_UNUSED(dummy), PyObject *args) { @@ -4631,8 +4957,11 @@ ufunc_geterr(PyObject *NPY_UNUSED(dummy), PyObject *args) if (thedict == NULL) { thedict = PyEval_GetBuiltins(); } - res = PyDict_GetItem(thedict, npy_um_str_pyvals_name); - if (res != NULL) { + res = PyDict_GetItemWithError(thedict, npy_um_str_pyvals_name); + if (res == NULL && PyErr_Occurred()) { + return NULL; + } + else if (res != NULL) { Py_INCREF(res); return res; } @@ -4641,12 +4970,13 @@ ufunc_geterr(PyObject *NPY_UNUSED(dummy), PyObject *args) if (res == NULL) { return NULL; } - PyList_SET_ITEM(res, 0, PyInt_FromLong(NPY_BUFSIZE)); - PyList_SET_ITEM(res, 1, PyInt_FromLong(UFUNC_ERR_DEFAULT)); + PyList_SET_ITEM(res, 0, PyLong_FromLong(NPY_BUFSIZE)); + PyList_SET_ITEM(res, 1, PyLong_FromLong(UFUNC_ERR_DEFAULT)); PyList_SET_ITEM(res, 2, Py_None); Py_INCREF(Py_None); return res; } + NPY_NO_EXPORT PyObject * ufunc_seterr(PyObject *NPY_UNUSED(dummy), PyObject *args) { @@ -4684,7 +5014,7 @@ ufunc_seterr(PyObject *NPY_UNUSED(dummy), PyObject *args) NPY_NO_EXPORT int PyUFunc_ReplaceLoopBySignature(PyUFuncObject *func, PyUFuncGenericFunction newfunc, - int *signature, + const int *signature, PyUFuncGenericFunction *oldfunc) { int i, j; @@ -4728,8 +5058,21 @@ PyUFunc_FromFuncAndDataAndSignature(PyUFuncGenericFunction *func, void **data, const char *name, const char *doc, int unused, const char *signature) { - PyUFuncObject *ufunc; + return PyUFunc_FromFuncAndDataAndSignatureAndIdentity( + func, data, types, ntypes, nin, nout, identity, name, doc, + unused, signature, NULL); +} +/*UFUNC_API*/ +NPY_NO_EXPORT PyObject * +PyUFunc_FromFuncAndDataAndSignatureAndIdentity(PyUFuncGenericFunction *func, void **data, + char *types, int ntypes, + int nin, int nout, int identity, + const char *name, const char *doc, + const int unused, const char *signature, + PyObject *identity_value) +{ + PyUFuncObject *ufunc; if (nin + nout > NPY_MAXARGS) { PyErr_Format(PyExc_ValueError, "Cannot construct a ufunc with more than %d operands " @@ -4738,32 +5081,73 @@ PyUFunc_FromFuncAndDataAndSignature(PyUFuncGenericFunction *func, void **data, return NULL; } - ufunc = PyArray_malloc(sizeof(PyUFuncObject)); + ufunc = PyObject_GC_New(PyUFuncObject, &PyUFunc_Type); + /* + * We use GC_New here for ufunc->obj, but do not use GC_Track since + * ufunc->obj is still NULL at the end of this function. + * See ufunc_frompyfunc where ufunc->obj is set and GC_Track is called. + */ if (ufunc == NULL) { return NULL; } - PyObject_Init((PyObject *)ufunc, &PyUFunc_Type); - - ufunc->reserved1 = 0; - ufunc->reserved2 = NULL; ufunc->nin = nin; ufunc->nout = nout; ufunc->nargs = nin+nout; ufunc->identity = identity; + if (ufunc->identity == PyUFunc_IdentityValue) { + Py_INCREF(identity_value); + ufunc->identity_value = identity_value; + } + else { + ufunc->identity_value = NULL; + } ufunc->functions = func; ufunc->data = data; ufunc->types = types; ufunc->ntypes = ntypes; - ufunc->ptr = NULL; + ufunc->core_signature = NULL; + ufunc->core_enabled = 0; ufunc->obj = NULL; - ufunc->userloops=NULL; + ufunc->core_num_dims = NULL; + ufunc->core_num_dim_ix = 0; + ufunc->core_offsets = NULL; + ufunc->core_dim_ixs = NULL; + ufunc->core_dim_sizes = NULL; + ufunc->core_dim_flags = NULL; + ufunc->userloops = NULL; + ufunc->ptr = NULL; + ufunc->vectorcall = &ufunc_generic_vectorcall; + ufunc->reserved1 = 0; + ufunc->iter_flags = 0; /* Type resolution and inner loop selection functions */ ufunc->type_resolver = &PyUFunc_DefaultTypeResolver; ufunc->legacy_inner_loop_selector = &PyUFunc_DefaultLegacyInnerLoopSelector; - ufunc->masked_inner_loop_selector = &PyUFunc_DefaultMaskedInnerLoopSelector; + ufunc->_always_null_previously_masked_innerloop_selector = NULL; + + ufunc->op_flags = NULL; + ufunc->_loops = NULL; + if (nin + nout != 0) { + ufunc->_dispatch_cache = PyArrayIdentityHash_New(nin + nout); + if (ufunc->_dispatch_cache == NULL) { + Py_DECREF(ufunc); + return NULL; + } + } + else { + /* + * Work around a test that seems to do this right now, it should not + * be a valid ufunc at all though, so. TODO: Remove... + */ + ufunc->_dispatch_cache = NULL; + } + ufunc->_loops = PyList_New(0); + if (ufunc->_loops == NULL) { + Py_DECREF(ufunc); + return NULL; + } if (name == NULL) { ufunc->name = "?"; @@ -4775,56 +5159,66 @@ PyUFunc_FromFuncAndDataAndSignature(PyUFuncGenericFunction *func, void **data, ufunc->op_flags = PyArray_malloc(sizeof(npy_uint32)*ufunc->nargs); if (ufunc->op_flags == NULL) { + Py_DECREF(ufunc); return PyErr_NoMemory(); } memset(ufunc->op_flags, 0, sizeof(npy_uint32)*ufunc->nargs); - ufunc->iter_flags = 0; - - /* generalized ufunc */ - ufunc->core_enabled = 0; - ufunc->core_num_dim_ix = 0; - ufunc->core_num_dims = NULL; - ufunc->core_dim_ixs = NULL; - ufunc->core_offsets = NULL; - ufunc->core_signature = NULL; if (signature != NULL) { if (_parse_signature(ufunc, signature) != 0) { Py_DECREF(ufunc); return NULL; } } + + char *curr_types = ufunc->types; + for (int i = 0; i < ntypes * (nin + nout); i += nin + nout) { + /* + * Add all legacy wrapping loops here. This is normally not necessary, + * but makes sense. It could also help/be needed to avoid issues with + * ambiguous loops such as: `OO->?` and `OO->O` where in theory the + * wrong loop could be picked if only the second one is added. + */ + PyObject *info; + PyArray_DTypeMeta *op_dtypes[NPY_MAXARGS]; + for (int arg = 0; arg < nin + nout; arg++) { + op_dtypes[arg] = PyArray_DTypeFromTypeNum(curr_types[arg]); + /* These DTypes are immortal and adding INCREFs: so borrow it */ + Py_DECREF(op_dtypes[arg]); + } + curr_types += nin + nout; + + info = add_and_return_legacy_wrapping_ufunc_loop(ufunc, op_dtypes, 1); + if (info == NULL) { + Py_DECREF(ufunc); + return NULL; + } + } + /* + * TODO: I tried adding a default promoter here (either all object for + * some special cases, or all homogeneous). Those are reasonable + * defaults, but short-cut a deprecated SciPy loop, where the + * homogeneous loop `ddd->d` was deprecated, but an inhomogeneous + * one `dld->d` should be picked. + * The default promoter *is* a reasonable default, but switched that + * behaviour. + * Another problem appeared due to buggy type-resolution for + * datetimes, this meant that `timedelta.sum(dtype="f8")` returned + * datetimes (and not floats or error), arguably wrong, but... + */ return (PyObject *)ufunc; } -/* Specify that the loop specified by the given index should use the array of - * input and arrays as the data pointer to the loop. - */ + /*UFUNC_API*/ NPY_NO_EXPORT int -PyUFunc_SetUsesArraysAsData(void **data, size_t i) -{ - data[i] = (void*)PyUFunc_SetUsesArraysAsData; - return 0; -} - -/* - * Return 1 if the given data pointer for the loop specifies that it needs the - * arrays as the data pointer. - * - * NOTE: This is easier to specify with the type_resolver - * in the ufunc object. - * - * TODO: Remove this, since this is already basically broken - * with the addition of the masked inner loops and - * not worth fixing since the new loop selection functions - * have access to the full dtypes and can dynamically allocate - * arbitrary auxiliary data. - */ -static int -_does_loop_use_arrays(void *data) +PyUFunc_SetUsesArraysAsData(void **NPY_UNUSED(data), size_t NPY_UNUSED(i)) { - return (data == PyUFunc_SetUsesArraysAsData); + /* NumPy 1.21, 201-03-29 */ + PyErr_SetString(PyExc_RuntimeError, + "PyUFunc_SetUsesArraysAsData() C-API function has been " + "disabled. It was initially deprecated in NumPy 1.19."); + return -1; } @@ -4885,21 +5279,12 @@ _free_loop1d_list(PyUFunc_Loop1d *data) } } -#if PY_VERSION_HEX >= 0x03000000 static void _loop1d_list_free(PyObject *ptr) { PyUFunc_Loop1d *data = (PyUFunc_Loop1d *)PyCapsule_GetPointer(ptr, NULL); _free_loop1d_list(data); } -#else -static void -_loop1d_list_free(void *ptr) -{ - PyUFunc_Loop1d *data = (PyUFunc_Loop1d *)ptr; - _free_loop1d_list(data); -} -#endif /* @@ -4909,11 +5294,14 @@ _loop1d_list_free(void *ptr) * instead of dtype type num values. This allows a 1-d loop to be registered * for a structured array dtype or a custom dtype. The ufunc is called * whenever any of it's input arguments match the user_dtype argument. - * ufunc - ufunc object created from call to PyUFunc_FromFuncAndData + * + * ufunc - ufunc object created from call to PyUFunc_FromFuncAndData * user_dtype - dtype that ufunc will be registered with - * function - 1-d loop function pointer + * function - 1-d loop function pointer * arg_dtypes - array of dtype objects describing the ufunc operands - * data - arbitrary data pointer passed in to loop function + * data - arbitrary data pointer passed in to loop function + * + * returns 0 on success, -1 for failure */ /*UFUNC_API*/ NPY_NO_EXPORT int @@ -4934,13 +5322,14 @@ PyUFunc_RegisterLoopForDescr(PyUFuncObject *ufunc, return -1; } - key = PyInt_FromLong((long) user_dtype->type_num); + key = PyLong_FromLong((long) user_dtype->type_num); if (key == NULL) { return -1; } arg_typenums = PyArray_malloc(ufunc->nargs * sizeof(int)); if (arg_typenums == NULL) { + Py_DECREF(key); PyErr_NoMemory(); return -1; } @@ -4959,16 +5348,22 @@ PyUFunc_RegisterLoopForDescr(PyUFuncObject *ufunc, function, arg_typenums, data); if (result == 0) { - cobj = PyDict_GetItem(ufunc->userloops, key); - if (cobj == NULL) { + cobj = PyDict_GetItemWithError(ufunc->userloops, key); + if (cobj == NULL && PyErr_Occurred()) { + result = -1; + } + else if (cobj == NULL) { PyErr_SetString(PyExc_KeyError, "userloop for user dtype not found"); result = -1; } else { - PyUFunc_Loop1d *current; int cmp = 1; - current = (PyUFunc_Loop1d *)NpyCapsule_AsVoidPtr(cobj); + PyUFunc_Loop1d *current = PyCapsule_GetPointer(cobj, NULL); + if (current == NULL) { + result = -1; + goto done; + } while (current != NULL) { cmp = cmp_arg_types(current->arg_types, arg_typenums, ufunc->nargs); @@ -4977,10 +5372,15 @@ PyUFunc_RegisterLoopForDescr(PyUFuncObject *ufunc, } current = current->next; } - if (cmp == 0 && current->arg_dtypes == NULL) { + if (cmp == 0 && current != NULL && current->arg_dtypes == NULL) { current->arg_dtypes = PyArray_malloc(ufunc->nargs * sizeof(PyArray_Descr*)); - if (arg_dtypes != NULL) { + if (current->arg_dtypes == NULL) { + PyErr_NoMemory(); + result = -1; + goto done; + } + else if (arg_dtypes != NULL) { for (i = 0; i < ufunc->nargs; i++) { current->arg_dtypes[i] = arg_dtypes[i]; Py_INCREF(current->arg_dtypes[i]); @@ -4995,11 +5395,14 @@ PyUFunc_RegisterLoopForDescr(PyUFuncObject *ufunc, current->nargs = ufunc->nargs; } else { + PyErr_SetString(PyExc_RuntimeError, + "loop already registered"); result = -1; } } } +done: PyArray_free(arg_typenums); Py_DECREF(key); @@ -5012,12 +5415,14 @@ NPY_NO_EXPORT int PyUFunc_RegisterLoopForType(PyUFuncObject *ufunc, int usertype, PyUFuncGenericFunction function, - int *arg_types, + const int *arg_types, void *data) { PyArray_Descr *descr; PyUFunc_Loop1d *funcdata; PyObject *key, *cobj; + PyArray_DTypeMeta *signature[NPY_MAXARGS]; + PyObject *signature_tuple = NULL; int i; int *newtypes=NULL; @@ -5031,7 +5436,7 @@ PyUFunc_RegisterLoopForType(PyUFuncObject *ufunc, if (ufunc->userloops == NULL) { ufunc->userloops = PyDict_New(); } - key = PyInt_FromLong((long) usertype); + key = PyLong_FromLong((long) usertype); if (key == NULL) { return -1; } @@ -5046,13 +5451,67 @@ PyUFunc_RegisterLoopForType(PyUFuncObject *ufunc, if (arg_types != NULL) { for (i = 0; i < ufunc->nargs; i++) { newtypes[i] = arg_types[i]; + signature[i] = PyArray_DTypeFromTypeNum(arg_types[i]); + Py_DECREF(signature[i]); /* DType can't be deleted... */ } } else { for (i = 0; i < ufunc->nargs; i++) { newtypes[i] = usertype; + signature[i] = PyArray_DTypeFromTypeNum(usertype); + Py_DECREF(signature[i]); /* DType can't be deleted... */ + } + } + + signature_tuple = PyArray_TupleFromItems( + ufunc->nargs, (PyObject **)signature, 0); + if (signature_tuple == NULL) { + goto fail; + } + /* + * We add the loop to the list of all loops and promoters. If the + * equivalent loop was already added, skip this. + * Note that even then the ufunc is still modified: The legacy ArrayMethod + * already looks up the inner-loop from the ufunc (and this is replaced + * below!). + * If the existing one is not a legacy ArrayMethod, we raise currently: + * A new-style loop should not be replaced by an old-style one. + */ + int add_new_loop = 1; + for (Py_ssize_t j = 0; j < PyList_GET_SIZE(ufunc->_loops); j++) { + PyObject *item = PyList_GET_ITEM(ufunc->_loops, j); + PyObject *existing_tuple = PyTuple_GET_ITEM(item, 0); + + int cmp = PyObject_RichCompareBool(existing_tuple, signature_tuple, Py_EQ); + if (cmp < 0) { + goto fail; + } + if (!cmp) { + continue; + } + PyObject *registered = PyTuple_GET_ITEM(item, 1); + if (!PyObject_TypeCheck(registered, &PyArrayMethod_Type) || ( + (PyArrayMethodObject *)registered)->get_strided_loop != + &get_wrapped_legacy_ufunc_loop) { + PyErr_Format(PyExc_TypeError, + "A non-compatible loop was already registered for " + "ufunc %s and DTypes %S.", + ufunc_get_name_cstr(ufunc), signature_tuple); + goto fail; + } + /* The loop was already added */ + add_new_loop = 0; + break; + } + if (add_new_loop) { + PyObject *info = add_and_return_legacy_wrapping_ufunc_loop( + ufunc, signature, 0); + if (info == NULL) { + goto fail; } } + /* Clearing sets it to NULL for the error paths */ + Py_CLEAR(signature_tuple); funcdata->func = function; funcdata->arg_types = newtypes; @@ -5062,10 +5521,13 @@ PyUFunc_RegisterLoopForType(PyUFuncObject *ufunc, funcdata->nargs = 0; /* Get entry for this user-defined type*/ - cobj = PyDict_GetItem(ufunc->userloops, key); + cobj = PyDict_GetItemWithError(ufunc->userloops, key); + if (cobj == NULL && PyErr_Occurred()) { + goto fail; + } /* If it's not there, then make one and return. */ - if (cobj == NULL) { - cobj = NpyCapsule_FromVoidPtr((void *)funcdata, _loop1d_list_free); + else if (cobj == NULL) { + cobj = PyCapsule_New((void *)funcdata, NULL, _loop1d_list_free); if (cobj == NULL) { goto fail; } @@ -5083,7 +5545,10 @@ PyUFunc_RegisterLoopForType(PyUFuncObject *ufunc, * is exactly like this one, then just replace. * Otherwise insert. */ - current = (PyUFunc_Loop1d *)NpyCapsule_AsVoidPtr(cobj); + current = PyCapsule_GetPointer(cobj, NULL); + if (current == NULL) { + goto fail; + } while (current != NULL) { cmp = cmp_arg_types(current->arg_types, newtypes, ufunc->nargs); if (cmp >= 0) { @@ -5120,6 +5585,7 @@ PyUFunc_RegisterLoopForType(PyUFuncObject *ufunc, fail: Py_DECREF(key); + Py_XDECREF(signature_tuple); PyArray_free(funcdata); PyArray_free(newtypes); if (!PyErr_Occurred()) PyErr_NoMemory(); @@ -5132,23 +5598,42 @@ PyUFunc_RegisterLoopForType(PyUFuncObject *ufunc, static void ufunc_dealloc(PyUFuncObject *ufunc) { + PyObject_GC_UnTrack((PyObject *)ufunc); PyArray_free(ufunc->core_num_dims); PyArray_free(ufunc->core_dim_ixs); + PyArray_free(ufunc->core_dim_sizes); + PyArray_free(ufunc->core_dim_flags); PyArray_free(ufunc->core_offsets); PyArray_free(ufunc->core_signature); PyArray_free(ufunc->ptr); PyArray_free(ufunc->op_flags); Py_XDECREF(ufunc->userloops); + if (ufunc->identity == PyUFunc_IdentityValue) { + Py_DECREF(ufunc->identity_value); + } Py_XDECREF(ufunc->obj); - PyArray_free(ufunc); + Py_XDECREF(ufunc->_loops); + if (ufunc->_dispatch_cache != NULL) { + PyArrayIdentityHash_Dealloc(ufunc->_dispatch_cache); + } + PyObject_GC_Del(ufunc); } static PyObject * ufunc_repr(PyUFuncObject *ufunc) { - return PyUString_FromFormat("<ufunc '%s'>", ufunc->name); + return PyUnicode_FromFormat("<ufunc '%s'>", ufunc->name); } +static int +ufunc_traverse(PyUFuncObject *self, visitproc visit, void *arg) +{ + Py_VISIT(self->obj); + if (self->identity == PyUFunc_IdentityValue) { + Py_VISIT(self->identity_value); + } + return 0; +} /****************************************************************************** *** UFUNC METHODS *** @@ -5162,24 +5647,9 @@ ufunc_repr(PyUFuncObject *ufunc) * The result has dimensions a.ndim + b.ndim */ static PyObject * -ufunc_outer(PyUFuncObject *ufunc, PyObject *args, PyObject *kwds) +ufunc_outer(PyUFuncObject *ufunc, + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) { - int i; - int errval; - PyObject *override = NULL; - PyObject *ret; - PyArrayObject *ap1 = NULL, *ap2 = NULL, *ap_new = NULL; - PyObject *new_args, *tmp; - PyObject *shape1, *shape2, *newshape; - - errval = PyUFunc_CheckOverride(ufunc, "outer", args, kwds, &override); - if (errval) { - return NULL; - } - else if (override) { - return override; - } - if (ufunc->core_enabled) { PyErr_Format(PyExc_TypeError, "method outer is not allowed in ufunc with non-trivial"\ @@ -5194,120 +5664,138 @@ ufunc_outer(PyUFuncObject *ufunc, PyObject *args, PyObject *kwds) return NULL; } - if (PySequence_Length(args) != 2) { + if (len_args != 2) { PyErr_SetString(PyExc_TypeError, "exactly two arguments expected"); return NULL; } - tmp = PySequence_GetItem(args, 0); - if (tmp == NULL) { - return NULL; + return ufunc_generic_fastcall(ufunc, args, len_args, kwnames, NPY_TRUE); +} + + +static PyObject * +prepare_input_arguments_for_outer(PyObject *args, PyUFuncObject *ufunc) +{ + PyArrayObject *ap1 = NULL; + PyObject *tmp; + static PyObject *_numpy_matrix; + npy_cache_import("numpy", "matrix", &_numpy_matrix); + + const char *matrix_deprecation_msg = ( + "%s.outer() was passed a numpy matrix as %s argument. " + "Special handling of matrix is deprecated and will result in an " + "error in most cases. Please convert the matrix to a NumPy " + "array to retain the old behaviour. You can use `matrix.A` " + "to achieve this."); + + tmp = PyTuple_GET_ITEM(args, 0); + + if (PyObject_IsInstance(tmp, _numpy_matrix)) { + /* DEPRECATED 2020-05-13, NumPy 1.20 */ + if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, + matrix_deprecation_msg, ufunc->name, "first") < 0) { + return NULL; + } + ap1 = (PyArrayObject *) PyArray_FromObject(tmp, NPY_NOTYPE, 0, 0); + } + else { + ap1 = (PyArrayObject *) PyArray_FROM_O(tmp); } - ap1 = (PyArrayObject *) PyArray_FromObject(tmp, NPY_NOTYPE, 0, 0); - Py_DECREF(tmp); if (ap1 == NULL) { return NULL; } - tmp = PySequence_GetItem(args, 1); - if (tmp == NULL) { - return NULL; + + PyArrayObject *ap2 = NULL; + tmp = PyTuple_GET_ITEM(args, 1); + if (PyObject_IsInstance(tmp, _numpy_matrix)) { + /* DEPRECATED 2020-05-13, NumPy 1.20 */ + if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, + matrix_deprecation_msg, ufunc->name, "second") < 0) { + Py_DECREF(ap1); + return NULL; + } + ap2 = (PyArrayObject *) PyArray_FromObject(tmp, NPY_NOTYPE, 0, 0); + } + else { + ap2 = (PyArrayObject *) PyArray_FROM_O(tmp); } - ap2 = (PyArrayObject *)PyArray_FromObject(tmp, NPY_NOTYPE, 0, 0); - Py_DECREF(tmp); if (ap2 == NULL) { Py_DECREF(ap1); return NULL; } - /* Construct new shape tuple */ - shape1 = PyTuple_New(PyArray_NDIM(ap1)); - if (shape1 == NULL) { + /* Construct new shape from ap1 and ap2 and then reshape */ + PyArray_Dims newdims; + npy_intp newshape[NPY_MAXDIMS]; + newdims.len = PyArray_NDIM(ap1) + PyArray_NDIM(ap2); + newdims.ptr = newshape; + + if (newdims.len > NPY_MAXDIMS) { + PyErr_Format(PyExc_ValueError, + "maximum supported dimension for an ndarray is %d, but " + "`%s.outer()` result would have %d.", + NPY_MAXDIMS, ufunc->name, newdims.len); goto fail; } - for (i = 0; i < PyArray_NDIM(ap1); i++) { - PyTuple_SET_ITEM(shape1, i, - PyLong_FromLongLong((npy_longlong)PyArray_DIMS(ap1)[i])); - } - shape2 = PyTuple_New(PyArray_NDIM(ap2)); - for (i = 0; i < PyArray_NDIM(ap2); i++) { - PyTuple_SET_ITEM(shape2, i, PyInt_FromLong((long) 1)); - } - if (shape2 == NULL) { - Py_DECREF(shape1); + if (newdims.ptr == NULL) { goto fail; } - newshape = PyNumber_Add(shape1, shape2); - Py_DECREF(shape1); - Py_DECREF(shape2); - if (newshape == NULL) { - goto fail; + memcpy(newshape, PyArray_DIMS(ap1), PyArray_NDIM(ap1) * sizeof(npy_intp)); + for (int i = PyArray_NDIM(ap1); i < newdims.len; i++) { + newshape[i] = 1; } - ap_new = (PyArrayObject *)PyArray_Reshape(ap1, newshape); - Py_DECREF(newshape); + + PyArrayObject *ap_new; + ap_new = (PyArrayObject *)PyArray_Newshape(ap1, &newdims, NPY_CORDER); if (ap_new == NULL) { goto fail; } - new_args = Py_BuildValue("(OO)", ap_new, ap2); + if (PyArray_NDIM(ap_new) != newdims.len || + !PyArray_CompareLists(PyArray_DIMS(ap_new), newshape, newdims.len)) { + PyErr_Format(PyExc_TypeError, + "%s.outer() called with ndarray-subclass of type '%s' " + "which modified its shape after a reshape. `outer()` relies " + "on reshaping the inputs and is for example not supported for " + "the 'np.matrix' class (the usage of matrix is generally " + "discouraged). " + "To work around this issue, please convert the inputs to " + "numpy arrays.", + ufunc->name, Py_TYPE(ap_new)->tp_name); + Py_DECREF(ap_new); + goto fail; + } + Py_DECREF(ap1); - Py_DECREF(ap2); - Py_DECREF(ap_new); - ret = ufunc_generic_call(ufunc, new_args, kwds); - Py_DECREF(new_args); - return ret; + return Py_BuildValue("(NN)", ap_new, ap2); fail: Py_XDECREF(ap1); Py_XDECREF(ap2); - Py_XDECREF(ap_new); return NULL; } static PyObject * -ufunc_reduce(PyUFuncObject *ufunc, PyObject *args, PyObject *kwds) +ufunc_reduce(PyUFuncObject *ufunc, + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) { - int errval; - PyObject *override = NULL; - - errval = PyUFunc_CheckOverride(ufunc, "reduce", args, kwds, &override); - if (errval) { - return NULL; - } - else if (override) { - return override; - } - return PyUFunc_GenericReduction(ufunc, args, kwds, UFUNC_REDUCE); + return PyUFunc_GenericReduction( + ufunc, args, len_args, kwnames, UFUNC_REDUCE); } static PyObject * -ufunc_accumulate(PyUFuncObject *ufunc, PyObject *args, PyObject *kwds) +ufunc_accumulate(PyUFuncObject *ufunc, + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) { - int errval; - PyObject *override = NULL; - - errval = PyUFunc_CheckOverride(ufunc, "accumulate", args, kwds, &override); - if (errval) { - return NULL; - } - else if (override) { - return override; - } - return PyUFunc_GenericReduction(ufunc, args, kwds, UFUNC_ACCUMULATE); + return PyUFunc_GenericReduction( + ufunc, args, len_args, kwnames, UFUNC_ACCUMULATE); } static PyObject * -ufunc_reduceat(PyUFuncObject *ufunc, PyObject *args, PyObject *kwds) +ufunc_reduceat(PyUFuncObject *ufunc, + PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) { - int errval; - PyObject *override = NULL; - - errval = PyUFunc_CheckOverride(ufunc, "reduceat", args, kwds, &override); - if (errval) { - return NULL; - } - else if (override) { - return override; - } - return PyUFunc_GenericReduction(ufunc, args, kwds, UFUNC_REDUCEAT); + return PyUFunc_GenericReduction( + ufunc, args, len_args, kwnames, UFUNC_REDUCEAT); } /* Helper for ufunc_at, below */ @@ -5341,15 +5829,13 @@ ufunc_at(PyUFuncObject *ufunc, PyObject *args) PyArrayObject *op2_array = NULL; PyArrayMapIterObject *iter = NULL; PyArrayIterObject *iter2 = NULL; - PyArray_Descr *dtypes[3] = {NULL, NULL, NULL}; PyArrayObject *operands[3] = {NULL, NULL, NULL}; PyArrayObject *array_operands[3] = {NULL, NULL, NULL}; - int needs_api = 0; + PyArray_DTypeMeta *signature[3] = {NULL, NULL, NULL}; + PyArray_DTypeMeta *operand_DTypes[3] = {NULL, NULL, NULL}; + PyArray_Descr *operation_descrs[3] = {NULL, NULL, NULL}; - PyUFuncGenericFunction innerloop; - void *innerloopdata; - int i; int nop; /* override vars */ @@ -5362,15 +5848,11 @@ ufunc_at(PyUFuncObject *ufunc, PyObject *args) int buffersize; int errormask = 0; char * err_msg = NULL; - NPY_BEGIN_THREADS_DEF; - errval = PyUFunc_CheckOverride(ufunc, "at", args, NULL, &override); - if (errval) { - return NULL; - } - else if (override) { - return override; - } + PyArrayMethod_StridedLoop *strided_loop; + NpyAuxData *auxdata = NULL; + + NPY_BEGIN_THREADS_DEF; if (ufunc->nin > 2) { PyErr_SetString(PyExc_ValueError, @@ -5393,6 +5875,15 @@ ufunc_at(PyUFuncObject *ufunc, PyObject *args) "second operand needed for ufunc"); return NULL; } + errval = PyUFunc_CheckOverride(ufunc, "at", + args, NULL, NULL, 0, NULL, &override); + + if (errval) { + return NULL; + } + else if (override) { + return override; + } if (!PyArray_Check(op1)) { PyErr_SetString(PyExc_TypeError, @@ -5448,31 +5939,51 @@ ufunc_at(PyUFuncObject *ufunc, PyObject *args) /* * Create dtypes array for either one or two input operands. - * The output operand is set to the first input operand + * Compare to the logic in `convert_ufunc_arguments`. + * TODO: It may be good to review some of this behaviour, since the + * operand array is special (it is written to) similar to reductions. + * Using unsafe-casting as done here, is likely not desirable. */ - dtypes[0] = PyArray_DESCR(op1_array); operands[0] = op1_array; + operand_DTypes[0] = NPY_DTYPE(PyArray_DESCR(op1_array)); + Py_INCREF(operand_DTypes[0]); + int force_legacy_promotion = 0; + int allow_legacy_promotion = NPY_DT_is_legacy(operand_DTypes[0]); + if (op2_array != NULL) { - dtypes[1] = PyArray_DESCR(op2_array); - dtypes[2] = dtypes[0]; operands[1] = op2_array; - operands[2] = op1_array; + operand_DTypes[1] = NPY_DTYPE(PyArray_DESCR(op2_array)); + Py_INCREF(operand_DTypes[1]); + allow_legacy_promotion &= NPY_DT_is_legacy(operand_DTypes[1]); + operands[2] = operands[0]; + operand_DTypes[2] = operand_DTypes[0]; + Py_INCREF(operand_DTypes[2]); + nop = 3; + if (allow_legacy_promotion && ((PyArray_NDIM(op1_array) == 0) + != (PyArray_NDIM(op2_array) == 0))) { + /* both are legacy and only one is 0-D: force legacy */ + force_legacy_promotion = should_use_min_scalar(2, operands, 0, NULL); + } } else { - dtypes[1] = dtypes[0]; - dtypes[2] = NULL; - operands[1] = op1_array; + operands[1] = operands[0]; + operand_DTypes[1] = operand_DTypes[0]; + Py_INCREF(operand_DTypes[1]); operands[2] = NULL; nop = 2; } - if (ufunc->type_resolver(ufunc, NPY_UNSAFE_CASTING, - operands, NULL, dtypes) < 0) { + PyArrayMethodObject *ufuncimpl = promote_and_get_ufuncimpl(ufunc, + operands, signature, operand_DTypes, + force_legacy_promotion, allow_legacy_promotion, NPY_FALSE); + if (ufuncimpl == NULL) { goto fail; } - if (ufunc->legacy_inner_loop_selector(ufunc, dtypes, - &innerloop, &innerloopdata, &needs_api) < 0) { + + /* Find the correct descriptors for the operation */ + if (resolve_descriptors(nop, ufunc, ufuncimpl, + operands, operation_descrs, signature, NPY_UNSAFE_CASTING) < 0) { goto fail; } @@ -5533,22 +6044,44 @@ ufunc_at(PyUFuncObject *ufunc, PyObject *args) NPY_ITER_GROWINNER| NPY_ITER_DELAY_BUFALLOC, NPY_KEEPORDER, NPY_UNSAFE_CASTING, - op_flags, dtypes, + op_flags, operation_descrs, -1, NULL, NULL, buffersize); if (iter_buffer == NULL) { goto fail; } - needs_api = needs_api | NpyIter_IterationNeedsAPI(iter_buffer); - iternext = NpyIter_GetIterNext(iter_buffer, NULL); if (iternext == NULL) { - NpyIter_Close(iter_buffer); NpyIter_Deallocate(iter_buffer); goto fail; } + PyArrayMethod_Context context = { + .caller = (PyObject *)ufunc, + .method = ufuncimpl, + .descriptors = operation_descrs, + }; + + NPY_ARRAYMETHOD_FLAGS flags; + /* Use contiguous strides; if there is such a loop it may be faster */ + npy_intp strides[3] = { + operation_descrs[0]->elsize, operation_descrs[1]->elsize, 0}; + if (nop == 3) { + strides[2] = operation_descrs[2]->elsize; + } + + if (ufuncimpl->get_strided_loop(&context, 1, 0, strides, + &strided_loop, &auxdata, &flags) < 0) { + goto fail; + } + int needs_api = (flags & NPY_METH_REQUIRES_PYAPI) != 0; + needs_api |= NpyIter_IterationNeedsAPI(iter_buffer); + if (!(flags & NPY_METH_NO_FLOATINGPOINT_ERRORS)) { + /* Start with the floating-point exception flags cleared */ + npy_clear_floatstatus_barrier((char*)&iter); + } + if (!needs_api) { NPY_BEGIN_THREADS; } @@ -5557,14 +6090,13 @@ ufunc_at(PyUFuncObject *ufunc, PyObject *args) * Iterate over first and second operands and call ufunc * for each pair of inputs */ - i = iter->size; - while (i > 0) + int res = 0; + for (npy_intp i = iter->size; i > 0; i--) { char *dataptr[3]; char **buffer_dataptr; /* one element at a time, no stride required but read by innerloop */ - npy_intp count[3] = {1, 0xDEADBEEF, 0xDEADBEEF}; - npy_intp stride[3] = {0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF}; + npy_intp count = 1; /* * Set up data pointers for either one or two input operands. @@ -5583,14 +6115,14 @@ ufunc_at(PyUFuncObject *ufunc, PyObject *args) /* Reset NpyIter data pointers which will trigger a buffer copy */ NpyIter_ResetBasePointers(iter_buffer, dataptr, &err_msg); if (err_msg) { + res = -1; break; } buffer_dataptr = NpyIter_GetDataPtrArray(iter_buffer); - innerloop(buffer_dataptr, count, stride, innerloopdata); - - if (needs_api && PyErr_Occurred()) { + res = strided_loop(&context, buffer_dataptr, &count, strides, auxdata); + if (res != 0) { break; } @@ -5604,27 +6136,37 @@ ufunc_at(PyUFuncObject *ufunc, PyObject *args) if (iter2 != NULL) { PyArray_ITER_NEXT(iter2); } - - i--; } NPY_END_THREADS; - if (err_msg) { + if (res != 0 && err_msg) { PyErr_SetString(PyExc_ValueError, err_msg); } + if (res == 0 && !(flags & NPY_METH_NO_FLOATINGPOINT_ERRORS)) { + /* NOTE: We could check float errors even when `res < 0` */ + res = _check_ufunc_fperr(errormask, NULL, "at"); + } - NpyIter_Close(iter_buffer); + NPY_AUXDATA_FREE(auxdata); NpyIter_Deallocate(iter_buffer); Py_XDECREF(op2_array); Py_XDECREF(iter); Py_XDECREF(iter2); - Py_XDECREF(array_operands[0]); - Py_XDECREF(array_operands[1]); - Py_XDECREF(array_operands[2]); + for (int i = 0; i < nop; i++) { + Py_DECREF(signature[i]); + Py_XDECREF(operand_DTypes[i]); + Py_XDECREF(operation_descrs[i]); + Py_XDECREF(array_operands[i]); + } - if (needs_api && PyErr_Occurred()) { + /* + * An error should only be possible if needs_api is true or `res != 0`, + * but this is not strictly correct for old-style ufuncs + * (e.g. `power` released the GIL but manually set an Exception). + */ + if (res != 0 || PyErr_Occurred()) { return NULL; } else { @@ -5632,16 +6174,20 @@ ufunc_at(PyUFuncObject *ufunc, PyObject *args) } fail: - /* iter_buffer has already been deallocated, don't use NpyIter_Close */ + /* iter_buffer has already been deallocated, don't use NpyIter_Dealloc */ if (op1_array != (PyArrayObject*)op1) { PyArray_DiscardWritebackIfCopy(op1_array); } Py_XDECREF(op2_array); Py_XDECREF(iter); Py_XDECREF(iter2); - Py_XDECREF(array_operands[0]); - Py_XDECREF(array_operands[1]); - Py_XDECREF(array_operands[2]); + for (int i = 0; i < 3; i++) { + Py_XDECREF(signature[i]); + Py_XDECREF(operand_DTypes[i]); + Py_XDECREF(operation_descrs[i]); + Py_XDECREF(array_operands[i]); + } + NPY_AUXDATA_FREE(auxdata); return NULL; } @@ -5650,16 +6196,16 @@ ufunc_at(PyUFuncObject *ufunc, PyObject *args) static struct PyMethodDef ufunc_methods[] = { {"reduce", (PyCFunction)ufunc_reduce, - METH_VARARGS | METH_KEYWORDS, NULL }, + METH_FASTCALL | METH_KEYWORDS, NULL }, {"accumulate", (PyCFunction)ufunc_accumulate, - METH_VARARGS | METH_KEYWORDS, NULL }, + METH_FASTCALL | METH_KEYWORDS, NULL }, {"reduceat", (PyCFunction)ufunc_reduceat, - METH_VARARGS | METH_KEYWORDS, NULL }, + METH_FASTCALL | METH_KEYWORDS, NULL }, {"outer", (PyCFunction)ufunc_outer, - METH_VARARGS | METH_KEYWORDS, NULL}, + METH_FASTCALL | METH_KEYWORDS, NULL}, {"at", (PyCFunction)ufunc_at, METH_VARARGS, NULL}, @@ -5683,8 +6229,9 @@ _typecharfromnum(int num) { return ret; } + static PyObject * -ufunc_get_doc(PyUFuncObject *ufunc) +ufunc_get_doc(PyUFuncObject *ufunc, void *NPY_UNUSED(ignored)) { static PyObject *_sig_formatter; PyObject *doc; @@ -5703,44 +6250,44 @@ ufunc_get_doc(PyUFuncObject *ufunc) * introspection on name and nin + nout to automate the first part * of it the doc string shouldn't need the calling convention */ - doc = PyObject_CallFunctionObjArgs( - _sig_formatter, (PyObject *)ufunc, NULL); + doc = PyObject_CallFunctionObjArgs(_sig_formatter, + (PyObject *)ufunc, NULL); if (doc == NULL) { return NULL; } if (ufunc->doc != NULL) { - PyUString_ConcatAndDel(&doc, - PyUString_FromFormat("\n\n%s", ufunc->doc)); + Py_SETREF(doc, PyUnicode_FromFormat("%S\n\n%s", doc, ufunc->doc)); } return doc; } + static PyObject * -ufunc_get_nin(PyUFuncObject *ufunc) +ufunc_get_nin(PyUFuncObject *ufunc, void *NPY_UNUSED(ignored)) { - return PyInt_FromLong(ufunc->nin); + return PyLong_FromLong(ufunc->nin); } static PyObject * -ufunc_get_nout(PyUFuncObject *ufunc) +ufunc_get_nout(PyUFuncObject *ufunc, void *NPY_UNUSED(ignored)) { - return PyInt_FromLong(ufunc->nout); + return PyLong_FromLong(ufunc->nout); } static PyObject * -ufunc_get_nargs(PyUFuncObject *ufunc) +ufunc_get_nargs(PyUFuncObject *ufunc, void *NPY_UNUSED(ignored)) { - return PyInt_FromLong(ufunc->nargs); + return PyLong_FromLong(ufunc->nargs); } static PyObject * -ufunc_get_ntypes(PyUFuncObject *ufunc) +ufunc_get_ntypes(PyUFuncObject *ufunc, void *NPY_UNUSED(ignored)) { - return PyInt_FromLong(ufunc->ntypes); + return PyLong_FromLong(ufunc->ntypes); } static PyObject * -ufunc_get_types(PyUFuncObject *ufunc) +ufunc_get_types(PyUFuncObject *ufunc, void *NPY_UNUSED(ignored)) { /* return a list with types grouped input->output */ PyObject *list; @@ -5766,7 +6313,7 @@ ufunc_get_types(PyUFuncObject *ufunc) t[ni + 2 + j] = _typecharfromnum(ufunc->types[n]); n++; } - str = PyUString_FromStringAndSize(t, no + ni + 2); + str = PyUnicode_FromStringAndSize(t, no + ni + 2); PyList_SET_ITEM(list, k, str); } PyArray_free(t); @@ -5774,25 +6321,25 @@ ufunc_get_types(PyUFuncObject *ufunc) } static PyObject * -ufunc_get_name(PyUFuncObject *ufunc) +ufunc_get_name(PyUFuncObject *ufunc, void *NPY_UNUSED(ignored)) { - return PyUString_FromString(ufunc->name); + return PyUnicode_FromString(ufunc->name); } static PyObject * -ufunc_get_identity(PyUFuncObject *ufunc) +ufunc_get_identity(PyUFuncObject *ufunc, void *NPY_UNUSED(ignored)) { npy_bool reorderable; return _get_identity(ufunc, &reorderable); } static PyObject * -ufunc_get_signature(PyUFuncObject *ufunc) +ufunc_get_signature(PyUFuncObject *ufunc, void *NPY_UNUSED(ignored)) { if (!ufunc->core_enabled) { Py_RETURN_NONE; } - return PyUString_FromString(ufunc->core_signature); + return PyUnicode_FromString(ufunc->core_signature); } #undef _typecharfromnum @@ -5838,63 +6385,20 @@ static PyGetSetDef ufunc_getset[] = { *****************************************************************************/ NPY_NO_EXPORT PyTypeObject PyUFunc_Type = { -#if defined(NPY_PY3K) PyVarObject_HEAD_INIT(NULL, 0) -#else - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ -#endif - "numpy.ufunc", /* tp_name */ - sizeof(PyUFuncObject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)ufunc_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ -#if defined(NPY_PY3K) - 0, /* tp_reserved */ -#else - 0, /* tp_compare */ -#endif - (reprfunc)ufunc_repr, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - (ternaryfunc)ufunc_generic_call, /* tp_call */ - (reprfunc)ufunc_repr, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - 0, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - ufunc_methods, /* tp_methods */ - 0, /* tp_members */ - ufunc_getset, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ - 0, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - 0, /* tp_del */ - 0, /* tp_version_tag */ + .tp_name = "numpy.ufunc", + .tp_basicsize = sizeof(PyUFuncObject), + .tp_dealloc = (destructor)ufunc_dealloc, + .tp_repr = (reprfunc)ufunc_repr, + .tp_call = &PyVectorcall_Call, + .tp_str = (reprfunc)ufunc_repr, + .tp_flags = Py_TPFLAGS_DEFAULT | + _Py_TPFLAGS_HAVE_VECTORCALL | + Py_TPFLAGS_HAVE_GC, + .tp_traverse = (traverseproc)ufunc_traverse, + .tp_methods = ufunc_methods, + .tp_getset = ufunc_getset, + .tp_vectorcall_offset = offsetof(PyUFuncObject, vectorcall), }; /* End of code for ufunc objects */ diff --git a/numpy/core/src/umath/ufunc_object.h b/numpy/core/src/umath/ufunc_object.h index d6fd3837ac11..6d4fed7c02d2 100644 --- a/numpy/core/src/umath/ufunc_object.h +++ b/numpy/core/src/umath/ufunc_object.h @@ -1,6 +1,8 @@ #ifndef _NPY_UMATH_UFUNC_OBJECT_H_ #define _NPY_UMATH_UFUNC_OBJECT_H_ +#include <numpy/ufuncobject.h> + NPY_NO_EXPORT PyObject * ufunc_geterr(PyObject *NPY_UNUSED(dummy), PyObject *args); @@ -10,13 +12,9 @@ ufunc_seterr(PyObject *NPY_UNUSED(dummy), PyObject *args); NPY_NO_EXPORT const char* ufunc_get_name_cstr(PyUFuncObject *ufunc); -/* interned strings (on umath import) */ -NPY_VISIBILITY_HIDDEN extern PyObject * npy_um_str_out; -NPY_VISIBILITY_HIDDEN extern PyObject * npy_um_str_subok; -NPY_VISIBILITY_HIDDEN extern PyObject * npy_um_str_array_prepare; -NPY_VISIBILITY_HIDDEN extern PyObject * npy_um_str_array_wrap; -NPY_VISIBILITY_HIDDEN extern PyObject * npy_um_str_array_finalize; -NPY_VISIBILITY_HIDDEN extern PyObject * npy_um_str_ufunc; -NPY_VISIBILITY_HIDDEN extern PyObject * npy_um_str_pyvals_name; +/* strings from umathmodule.c that are interned on umath import */ +NPY_VISIBILITY_HIDDEN extern PyObject *npy_um_str_array_prepare; +NPY_VISIBILITY_HIDDEN extern PyObject *npy_um_str_array_wrap; +NPY_VISIBILITY_HIDDEN extern PyObject *npy_um_str_pyvals_name; #endif diff --git a/numpy/core/src/umath/ufunc_type_resolution.c b/numpy/core/src/umath/ufunc_type_resolution.c index 1766ba5648c8..9ed923cf56e7 100644 --- a/numpy/core/src/umath/ufunc_type_resolution.c +++ b/numpy/core/src/umath/ufunc_type_resolution.c @@ -1,4 +1,16 @@ /* + * NOTE: The type resolution defined in this file is considered legacy. + * + * The new mechanism separates type resolution and promotion into two + * distinct steps, as per NEP 43. + * Further, the functions in this file rely on the operands rather than + * only the DTypes/descriptors. They are still called and at this point + * vital (NumPy ~1.21), but should hopefully become largely irrelevant very + * quickly. + * + * At that point, this file should be deletable in its entirety. + * + * * This file implements type resolution for NumPy element-wise ufuncs. * This mechanism is still backwards-compatible with the pre-existing * legacy mechanism, so performs much slower than is necessary. @@ -8,40 +20,198 @@ * * See LICENSE.txt for the license. */ -#define _UMATHMODULE #define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE +#define _UMATHMODULE -#include "Python.h" +#define PY_SSIZE_T_CLEAN +#include <Python.h> -#include "npy_config.h" -#define PY_ARRAY_UNIQUE_SYMBOL _npy_umathmodule_ARRAY_API -#define NO_IMPORT_ARRAY +// printif debug tracing +#ifndef NPY_UF_DBG_TRACING + #define NPY_UF_DBG_TRACING 0 +#endif +#include "npy_config.h" #include "npy_pycompat.h" +#include "npy_import.h" #include "numpy/ufuncobject.h" #include "ufunc_type_resolution.h" #include "ufunc_object.h" #include "common.h" +#include "convert_datatype.h" + +#include "mem_overlap.h" +#if defined(HAVE_CBLAS) +#include "cblasfuncs.h" +#endif -static const char * -npy_casting_to_string(NPY_CASTING casting) +#include <stdbool.h> + +static PyObject * +npy_casting_to_py_object(NPY_CASTING casting) { switch (casting) { case NPY_NO_CASTING: - return "'no'"; + return PyUnicode_FromString("no"); case NPY_EQUIV_CASTING: - return "'equiv'"; + return PyUnicode_FromString("equiv"); case NPY_SAFE_CASTING: - return "'safe'"; + return PyUnicode_FromString("safe"); case NPY_SAME_KIND_CASTING: - return "'same_kind'"; + return PyUnicode_FromString("same_kind"); case NPY_UNSAFE_CASTING: - return "'unsafe'"; + return PyUnicode_FromString("unsafe"); default: - return "<unknown>"; + return PyLong_FromLong(casting); } } + + +/** + * Always returns -1 to indicate the exception was raised, for convenience + */ +static int +raise_binary_type_reso_error(PyUFuncObject *ufunc, PyArrayObject **operands) { + static PyObject *exc_type = NULL; + PyObject *exc_value; + + npy_cache_import( + "numpy.core._exceptions", "_UFuncBinaryResolutionError", + &exc_type); + if (exc_type == NULL) { + return -1; + } + + /* produce an error object */ + exc_value = Py_BuildValue( + "O(OO)", ufunc, + (PyObject *)PyArray_DESCR(operands[0]), + (PyObject *)PyArray_DESCR(operands[1]) + ); + if (exc_value == NULL){ + return -1; + } + PyErr_SetObject(exc_type, exc_value); + Py_DECREF(exc_value); + + return -1; +} + +/** Helper function to raise UFuncNoLoopError + * Always returns -1 to indicate the exception was raised, for convenience + */ +NPY_NO_EXPORT int +raise_no_loop_found_error( + PyUFuncObject *ufunc, PyObject **dtypes) +{ + static PyObject *exc_type = NULL; + + npy_cache_import( + "numpy.core._exceptions", "_UFuncNoLoopError", + &exc_type); + if (exc_type == NULL) { + return -1; + } + + PyObject *dtypes_tup = PyArray_TupleFromItems(ufunc->nargs, dtypes, 1); + if (dtypes_tup == NULL) { + return -1; + } + /* produce an error object */ + PyObject *exc_value = PyTuple_Pack(2, ufunc, dtypes_tup); + Py_DECREF(dtypes_tup); + if (exc_value == NULL) { + return -1; + } + PyErr_SetObject(exc_type, exc_value); + Py_DECREF(exc_value); + + return -1; +} + + +static int +raise_casting_error( + PyObject *exc_type, + PyUFuncObject *ufunc, + NPY_CASTING casting, + PyArray_Descr *from, + PyArray_Descr *to, + npy_intp i) +{ + PyObject *exc_value; + PyObject *casting_value; + + casting_value = npy_casting_to_py_object(casting); + if (casting_value == NULL) { + return -1; + } + + exc_value = Py_BuildValue( + "ONOOi", + ufunc, + casting_value, + (PyObject *)from, + (PyObject *)to, + i + ); + if (exc_value == NULL){ + return -1; + } + PyErr_SetObject(exc_type, exc_value); + Py_DECREF(exc_value); + + return -1; +} + +/** Helper function to raise UFuncInputCastingError + * Always returns -1 to indicate the exception was raised, for convenience + */ +static int +raise_input_casting_error( + PyUFuncObject *ufunc, + NPY_CASTING casting, + PyArray_Descr *from, + PyArray_Descr *to, + npy_intp i) +{ + static PyObject *exc_type = NULL; + npy_cache_import( + "numpy.core._exceptions", "_UFuncInputCastingError", + &exc_type); + if (exc_type == NULL) { + return -1; + } + + return raise_casting_error(exc_type, ufunc, casting, from, to, i); +} + + +/** Helper function to raise UFuncOutputCastingError + * Always returns -1 to indicate the exception was raised, for convenience + */ +static int +raise_output_casting_error( + PyUFuncObject *ufunc, + NPY_CASTING casting, + PyArray_Descr *from, + PyArray_Descr *to, + npy_intp i) +{ + static PyObject *exc_type = NULL; + npy_cache_import( + "numpy.core._exceptions", "_UFuncOutputCastingError", + &exc_type); + if (exc_type == NULL) { + return -1; + } + + return raise_casting_error(exc_type, ufunc, casting, from, to, i); +} + + /*UFUNC_API * * Validates that the input operands can be cast to @@ -57,45 +227,18 @@ PyUFunc_ValidateCasting(PyUFuncObject *ufunc, PyArray_Descr **dtypes) { int i, nin = ufunc->nin, nop = nin + ufunc->nout; - const char *ufunc_name = ufunc_get_name_cstr(ufunc); for (i = 0; i < nop; ++i) { if (i < nin) { if (!PyArray_CanCastArrayTo(operands[i], dtypes[i], casting)) { - PyObject *errmsg; - errmsg = PyUString_FromFormat("Cannot cast ufunc %s " - "input from ", ufunc_name); - PyUString_ConcatAndDel(&errmsg, - PyObject_Repr((PyObject *)PyArray_DESCR(operands[i]))); - PyUString_ConcatAndDel(&errmsg, - PyUString_FromString(" to ")); - PyUString_ConcatAndDel(&errmsg, - PyObject_Repr((PyObject *)dtypes[i])); - PyUString_ConcatAndDel(&errmsg, - PyUString_FromFormat(" with casting rule %s", - npy_casting_to_string(casting))); - PyErr_SetObject(PyExc_TypeError, errmsg); - Py_DECREF(errmsg); - return -1; + return raise_input_casting_error( + ufunc, casting, PyArray_DESCR(operands[i]), dtypes[i], i); } } else if (operands[i] != NULL) { if (!PyArray_CanCastTypeTo(dtypes[i], PyArray_DESCR(operands[i]), casting)) { - PyObject *errmsg; - errmsg = PyUString_FromFormat("Cannot cast ufunc %s " - "output from ", ufunc_name); - PyUString_ConcatAndDel(&errmsg, - PyObject_Repr((PyObject *)dtypes[i])); - PyUString_ConcatAndDel(&errmsg, - PyUString_FromString(" to ")); - PyUString_ConcatAndDel(&errmsg, - PyObject_Repr((PyObject *)PyArray_DESCR(operands[i]))); - PyUString_ConcatAndDel(&errmsg, - PyUString_FromFormat(" with casting rule %s", - npy_casting_to_string(casting))); - PyErr_SetObject(PyExc_TypeError, errmsg); - Py_DECREF(errmsg); - return -1; + return raise_output_casting_error( + ufunc, casting, dtypes[i], PyArray_DESCR(operands[i]), i); } } } @@ -103,20 +246,27 @@ PyUFunc_ValidateCasting(PyUFuncObject *ufunc, return 0; } + /* - * Returns a new reference to type if it is already NBO, otherwise - * returns a copy converted to NBO. + * Same as `PyUFunc_ValidateCasting` but only checks output casting. */ -static PyArray_Descr * -ensure_dtype_nbo(PyArray_Descr *type) +NPY_NO_EXPORT int +PyUFunc_ValidateOutCasting(PyUFuncObject *ufunc, + NPY_CASTING casting, PyArrayObject **operands, PyArray_Descr **dtypes) { - if (PyArray_ISNBO(type->byteorder)) { - Py_INCREF(type); - return type; - } - else { - return PyArray_DescrNewByteorder(type, NPY_NATIVE); + int i, nin = ufunc->nin, nop = nin + ufunc->nout; + + for (i = nin; i < nop; ++i) { + if (operands[i] == NULL) { + continue; + } + if (!PyArray_CanCastTypeTo(dtypes[i], + PyArray_DESCR(operands[i]), casting)) { + return raise_output_casting_error( + ufunc, casting, dtypes[i], PyArray_DESCR(operands[i]), i); + } } + return 0; } /*UFUNC_API @@ -161,7 +311,7 @@ PyUFunc_DefaultTypeResolver(PyUFuncObject *ufunc, } else { /* Find the specified ufunc inner loop, and fill in the dtypes */ retval = type_tuple_type_resolver(ufunc, type_tup, - operands, casting, any_object, out_dtypes); + operands, input_casting, casting, any_object, out_dtypes); } return retval; @@ -206,39 +356,72 @@ PyUFunc_SimpleBinaryComparisonTypeResolver(PyUFuncObject *ufunc, } if (type_tup == NULL) { - /* Input types are the result type */ - out_dtypes[0] = PyArray_ResultType(2, operands, 0, NULL); - if (out_dtypes[0] == NULL) { - return -1; + /* + * DEPRECATED NumPy 1.20, 2020-12. + * This check is required to avoid the FutureWarning that + * ResultType will give for number->string promotions. + * (We never supported flexible dtypes here.) + */ + if (!PyArray_ISFLEXIBLE(operands[0]) && + !PyArray_ISFLEXIBLE(operands[1])) { + out_dtypes[0] = PyArray_ResultType(2, operands, 0, NULL); + if (out_dtypes[0] == NULL) { + return -1; + } + out_dtypes[1] = out_dtypes[0]; + Py_INCREF(out_dtypes[1]); + } + else { + /* Not doing anything will lead to a loop no found error. */ + out_dtypes[0] = PyArray_DESCR(operands[0]); + Py_INCREF(out_dtypes[0]); + out_dtypes[1] = PyArray_DESCR(operands[1]); + Py_INCREF(out_dtypes[1]); } - out_dtypes[1] = out_dtypes[0]; - Py_INCREF(out_dtypes[1]); } else { - PyObject *item; - PyArray_Descr *dtype = NULL; - + PyArray_Descr *descr; /* - * If the type tuple isn't a single-element tuple, let the - * default type resolution handle this one. + * DEPRECATED 2021-03, NumPy 1.20 + * + * If the type tuple was originally a single element (probably), + * issue a deprecation warning, but otherwise accept it. Since the + * result dtype is always boolean, this is not actually valid unless it + * is `object` (but if there is an object input we already deferred). + * + * TODO: Once this deprecation is gone, the special case for + * `PyUFunc_SimpleBinaryComparisonTypeResolver` in dispatching.c + * can be removed. */ - if (!PyTuple_Check(type_tup) || PyTuple_GET_SIZE(type_tup) != 1) { + if (PyTuple_Check(type_tup) && PyTuple_GET_SIZE(type_tup) == 3 && + PyTuple_GET_ITEM(type_tup, 0) == Py_None && + PyTuple_GET_ITEM(type_tup, 1) == Py_None && + PyArray_DescrCheck(PyTuple_GET_ITEM(type_tup, 2))) { + descr = (PyArray_Descr *)PyTuple_GET_ITEM(type_tup, 2); + if (descr->type_num == NPY_OBJECT) { + if (DEPRECATE_FUTUREWARNING( + "using `dtype=object` (or equivalent signature) will " + "return object arrays in the future also when the " + "inputs do not already have `object` dtype.") < 0) { + return -1; + } + } + else if (descr->type_num != NPY_BOOL) { + if (DEPRECATE( + "using `dtype=` in comparisons is only useful for " + "`dtype=object` (and will do nothing for bool). " + "This operation will fail in the future.") < 0) { + return -1; + } + } + } + else { + /* Usually a failure, but let the the default version handle it */ return PyUFunc_DefaultTypeResolver(ufunc, casting, operands, type_tup, out_dtypes); } - item = PyTuple_GET_ITEM(type_tup, 0); - - if (item == Py_None) { - PyErr_SetString(PyExc_ValueError, - "require data type in the type tuple"); - return -1; - } - else if (!PyArray_DescrConverter(item, &dtype)) { - return -1; - } - - out_dtypes[0] = ensure_dtype_nbo(dtype); + out_dtypes[0] = ensure_dtype_nbo(descr); if (out_dtypes[0] == NULL) { return -1; } @@ -268,99 +451,6 @@ PyUFunc_SimpleBinaryComparisonTypeResolver(PyUFuncObject *ufunc, return 0; } -/* - * This function applies special type resolution rules for the case - * where all the functions have the pattern X->X, copying - * the input descr directly so that metadata is maintained. - * - * Note that a simpler linear search through the functions loop - * is still done, but switching to a simple array lookup for - * built-in types would be better at some point. - * - * Returns 0 on success, -1 on error. - */ -NPY_NO_EXPORT int -PyUFunc_SimpleUnaryOperationTypeResolver(PyUFuncObject *ufunc, - NPY_CASTING casting, - PyArrayObject **operands, - PyObject *type_tup, - PyArray_Descr **out_dtypes) -{ - int i, type_num1; - const char *ufunc_name = ufunc_get_name_cstr(ufunc); - - if (ufunc->nin != 1 || ufunc->nout != 1) { - PyErr_Format(PyExc_RuntimeError, "ufunc %s is configured " - "to use unary operation type resolution but has " - "the wrong number of inputs or outputs", - ufunc_name); - return -1; - } - - /* - * Use the default type resolution if there's a custom data type - * or object arrays. - */ - type_num1 = PyArray_DESCR(operands[0])->type_num; - if (type_num1 >= NPY_NTYPES || type_num1 == NPY_OBJECT) { - return PyUFunc_DefaultTypeResolver(ufunc, casting, operands, - type_tup, out_dtypes); - } - - if (type_tup == NULL) { - /* Input types are the result type */ - out_dtypes[0] = ensure_dtype_nbo(PyArray_DESCR(operands[0])); - if (out_dtypes[0] == NULL) { - return -1; - } - out_dtypes[1] = out_dtypes[0]; - Py_INCREF(out_dtypes[1]); - } - else { - PyObject *item; - PyArray_Descr *dtype = NULL; - - /* - * If the type tuple isn't a single-element tuple, let the - * default type resolution handle this one. - */ - if (!PyTuple_Check(type_tup) || PyTuple_GET_SIZE(type_tup) != 1) { - return PyUFunc_DefaultTypeResolver(ufunc, casting, - operands, type_tup, out_dtypes); - } - - item = PyTuple_GET_ITEM(type_tup, 0); - - if (item == Py_None) { - PyErr_SetString(PyExc_ValueError, - "require data type in the type tuple"); - return -1; - } - else if (!PyArray_DescrConverter(item, &dtype)) { - return -1; - } - - out_dtypes[0] = ensure_dtype_nbo(dtype); - if (out_dtypes[0] == NULL) { - return -1; - } - out_dtypes[1] = out_dtypes[0]; - Py_INCREF(out_dtypes[1]); - } - - /* Check against the casting rules */ - if (PyUFunc_ValidateCasting(ufunc, casting, operands, out_dtypes) < 0) { - for (i = 0; i < 2; ++i) { - Py_DECREF(out_dtypes[i]); - out_dtypes[i] = NULL; - } - return -1; - } - - return 0; -} - - NPY_NO_EXPORT int PyUFunc_NegativeTypeResolver(PyUFuncObject *ufunc, NPY_CASTING casting, @@ -369,7 +459,7 @@ PyUFunc_NegativeTypeResolver(PyUFuncObject *ufunc, PyArray_Descr **out_dtypes) { int ret; - ret = PyUFunc_SimpleUnaryOperationTypeResolver(ufunc, casting, operands, + ret = PyUFunc_SimpleUniformOperationTypeResolver(ufunc, casting, operands, type_tup, out_dtypes); if (ret < 0) { return ret; @@ -399,16 +489,15 @@ PyUFunc_OnesLikeTypeResolver(PyUFuncObject *ufunc, PyObject *type_tup, PyArray_Descr **out_dtypes) { - return PyUFunc_SimpleUnaryOperationTypeResolver(ufunc, + return PyUFunc_SimpleUniformOperationTypeResolver(ufunc, NPY_UNSAFE_CASTING, operands, type_tup, out_dtypes); } - /* * This function applies special type resolution rules for the case - * where all the functions have the pattern XX->X, using - * PyArray_ResultType instead of a linear search to get the best + * where all of the types in the signature are the same, eg XX->X or XX->XX. + * It uses PyArray_ResultType instead of a linear search to get the best * loop. * * Note that a simpler linear search through the functions loop @@ -418,85 +507,142 @@ PyUFunc_OnesLikeTypeResolver(PyUFuncObject *ufunc, * Returns 0 on success, -1 on error. */ NPY_NO_EXPORT int -PyUFunc_SimpleBinaryOperationTypeResolver(PyUFuncObject *ufunc, - NPY_CASTING casting, - PyArrayObject **operands, - PyObject *type_tup, - PyArray_Descr **out_dtypes) +PyUFunc_SimpleUniformOperationTypeResolver( + PyUFuncObject *ufunc, + NPY_CASTING casting, + PyArrayObject **operands, + PyObject *type_tup, + PyArray_Descr **out_dtypes) { - int i, type_num1, type_num2; const char *ufunc_name = ufunc_get_name_cstr(ufunc); - if (ufunc->nin != 2 || ufunc->nout != 1) { + if (ufunc->nin < 1) { PyErr_Format(PyExc_RuntimeError, "ufunc %s is configured " - "to use binary operation type resolution but has " - "the wrong number of inputs or outputs", + "to use uniform operation type resolution but has " + "no inputs", ufunc_name); return -1; } + int nop = ufunc->nin + ufunc->nout; /* - * Use the default type resolution if there's a custom data type - * or object arrays. + * There's a custom data type or an object array */ - type_num1 = PyArray_DESCR(operands[0])->type_num; - type_num2 = PyArray_DESCR(operands[1])->type_num; - if (type_num1 >= NPY_NTYPES || type_num2 >= NPY_NTYPES || - type_num1 == NPY_OBJECT || type_num2 == NPY_OBJECT) { + bool has_custom_or_object = false; + for (int iop = 0; iop < ufunc->nin; iop++) { + int type_num = PyArray_DESCR(operands[iop])->type_num; + if (type_num >= NPY_NTYPES || type_num == NPY_OBJECT) { + has_custom_or_object = true; + break; + } + } + + if (has_custom_or_object) { return PyUFunc_DefaultTypeResolver(ufunc, casting, operands, type_tup, out_dtypes); } if (type_tup == NULL) { - /* Input types are the result type */ - out_dtypes[0] = PyArray_ResultType(2, operands, 0, NULL); + /* PyArray_ResultType forgets to force a byte order when n == 1 */ + if (ufunc->nin == 1){ + out_dtypes[0] = ensure_dtype_nbo(PyArray_DESCR(operands[0])); + } + else { + int iop; + npy_bool has_flexible = 0; + npy_bool has_object = 0; + for (iop = 0; iop < ufunc->nin; iop++) { + if (PyArray_ISOBJECT(operands[iop])) { + has_object = 1; + } + if (PyArray_ISFLEXIBLE(operands[iop])) { + has_flexible = 1; + } + } + if (NPY_UNLIKELY(has_flexible && !has_object)) { + /* + * DEPRECATED NumPy 1.20, 2020-12. + * This check is required to avoid the FutureWarning that + * ResultType will give for number->string promotions. + * (We never supported flexible dtypes here.) + */ + for (iop = 0; iop < ufunc->nin; iop++) { + out_dtypes[iop] = PyArray_DESCR(operands[iop]); + Py_INCREF(out_dtypes[iop]); + } + raise_no_loop_found_error(ufunc, (PyObject **)out_dtypes); + for (iop = 0; iop < ufunc->nin; iop++) { + Py_DECREF(out_dtypes[iop]); + out_dtypes[iop] = NULL; + } + return -1; + } + out_dtypes[0] = PyArray_ResultType(ufunc->nin, operands, 0, NULL); + } if (out_dtypes[0] == NULL) { return -1; } - out_dtypes[1] = out_dtypes[0]; - Py_INCREF(out_dtypes[1]); - out_dtypes[2] = out_dtypes[0]; - Py_INCREF(out_dtypes[2]); } else { - PyObject *item; - PyArray_Descr *dtype = NULL; - /* - * If the type tuple isn't a single-element tuple, let the - * default type resolution handle this one. + * This is a fast-path, since all descriptors will be identical, mainly + * when only a single descriptor was passed (which would set the out + * one in the tuple), there is no need to check all loops. + * Note that this also allows (None, None, float64) to resolve to + * (float64, float64, float64), even when the inputs do not match, + * i.e. fixing the output part of the signature can fix all of them. + * This is necessary to support `nextafter(1., inf, dtype=float32)`, + * where it is "clear" we want to cast 1. and inf to float32. */ - if (!PyTuple_Check(type_tup) || PyTuple_GET_SIZE(type_tup) != 1) { + PyArray_Descr *descr = NULL; + if (PyTuple_CheckExact(type_tup) && + PyTuple_GET_SIZE(type_tup) == nop) { + for (int i = 0; i < nop; i++) { + PyObject *item = PyTuple_GET_ITEM(type_tup, i); + if (item == Py_None) { + if (i < ufunc->nin) { + continue; + } + /* All outputs must be set (this could be relaxed) */ + descr = NULL; + break; + } + if (!PyArray_DescrCheck(item)) { + /* Defer to default resolver (will raise an error there) */ + descr = NULL; + break; + } + if (descr != NULL && descr != (PyArray_Descr *)item) { + /* Descriptor mismatch: try with default (probable error) */ + descr = NULL; + break; + } + descr = (PyArray_Descr *)item; + } + } + if (descr == NULL) { + /* in all bad/unlikely cases, use the default type resolver: */ return PyUFunc_DefaultTypeResolver(ufunc, casting, operands, type_tup, out_dtypes); } - - item = PyTuple_GET_ITEM(type_tup, 0); - - if (item == Py_None) { - PyErr_SetString(PyExc_ValueError, - "require data type in the type tuple"); - return -1; - } - else if (!PyArray_DescrConverter(item, &dtype)) { - return -1; + else if (descr->type_num == PyArray_DESCR(operands[0])->type_num) { + /* Prefer the input descriptor if it matches (preserve metadata) */ + descr = PyArray_DESCR(operands[0]); } + out_dtypes[0] = ensure_dtype_nbo(descr); + } - out_dtypes[0] = ensure_dtype_nbo(dtype); - if (out_dtypes[0] == NULL) { - return -1; - } - out_dtypes[1] = out_dtypes[0]; - Py_INCREF(out_dtypes[1]); - out_dtypes[2] = out_dtypes[0]; - Py_INCREF(out_dtypes[2]); + /* All types are the same - copy the first one to the rest */ + for (int iop = 1; iop < nop; iop++) { + out_dtypes[iop] = out_dtypes[0]; + Py_INCREF(out_dtypes[iop]); } /* Check against the casting rules */ if (PyUFunc_ValidateCasting(ufunc, casting, operands, out_dtypes) < 0) { - for (i = 0; i < 3; ++i) { - Py_DECREF(out_dtypes[i]); - out_dtypes[i] = NULL; + for (int iop = 0; iop < nop; iop++) { + Py_DECREF(out_dtypes[iop]); + out_dtypes[iop] = NULL; } return -1; } @@ -524,7 +670,7 @@ PyUFunc_AbsoluteTypeResolver(PyUFuncObject *ufunc, type_tup, out_dtypes); } else { - return PyUFunc_SimpleUnaryOperationTypeResolver(ufunc, casting, + return PyUFunc_SimpleUniformOperationTypeResolver(ufunc, casting, operands, type_tup, out_dtypes); } } @@ -555,6 +701,26 @@ PyUFunc_IsNaTTypeResolver(PyUFuncObject *ufunc, return 0; } + +NPY_NO_EXPORT int +PyUFunc_IsFiniteTypeResolver(PyUFuncObject *ufunc, + NPY_CASTING casting, + PyArrayObject **operands, + PyObject *type_tup, + PyArray_Descr **out_dtypes) +{ + if (!PyTypeNum_ISDATETIME(PyArray_DESCR(operands[0])->type_num)) { + return PyUFunc_DefaultTypeResolver(ufunc, casting, operands, + type_tup, out_dtypes); + } + + out_dtypes[0] = ensure_dtype_nbo(PyArray_DESCR(operands[0])); + out_dtypes[1] = PyArray_DescrFromType(NPY_BOOL); + + return 0; +} + + /* * Creates a new NPY_TIMEDELTA dtype, copying the datetime metadata * from the given dtype. @@ -607,14 +773,13 @@ PyUFunc_AdditionTypeResolver(PyUFuncObject *ufunc, { int type_num1, type_num2; int i; - const char *ufunc_name = ufunc_get_name_cstr(ufunc); type_num1 = PyArray_DESCR(operands[0])->type_num; type_num2 = PyArray_DESCR(operands[1])->type_num; /* Use the default when datetime and timedelta are not involved */ if (!PyTypeNum_ISDATETIME(type_num1) && !PyTypeNum_ISDATETIME(type_num2)) { - return PyUFunc_SimpleBinaryOperationTypeResolver(ufunc, casting, + return PyUFunc_SimpleUniformOperationTypeResolver(ufunc, casting, operands, type_tup, out_dtypes); } @@ -663,7 +828,7 @@ PyUFunc_AdditionTypeResolver(PyUFuncObject *ufunc, type_num2 = NPY_TIMEDELTA; } else { - goto type_reso_error; + return raise_binary_type_reso_error(ufunc, operands); } } else if (type_num1 == NPY_DATETIME) { @@ -705,7 +870,7 @@ PyUFunc_AdditionTypeResolver(PyUFuncObject *ufunc, type_num2 = NPY_TIMEDELTA; } else { - goto type_reso_error; + return raise_binary_type_reso_error(ufunc, operands); } } else if (PyTypeNum_ISINTEGER(type_num1) || PyTypeNum_ISBOOL(type_num1)) { @@ -741,11 +906,11 @@ PyUFunc_AdditionTypeResolver(PyUFuncObject *ufunc, type_num1 = NPY_TIMEDELTA; } else { - goto type_reso_error; + return raise_binary_type_reso_error(ufunc, operands); } } else { - goto type_reso_error; + return raise_binary_type_reso_error(ufunc, operands); } /* Check against the casting rules */ @@ -758,21 +923,6 @@ PyUFunc_AdditionTypeResolver(PyUFuncObject *ufunc, } return 0; - -type_reso_error: { - PyObject *errmsg; - errmsg = PyUString_FromFormat("ufunc %s cannot use operands " - "with types ", ufunc_name); - PyUString_ConcatAndDel(&errmsg, - PyObject_Repr((PyObject *)PyArray_DESCR(operands[0]))); - PyUString_ConcatAndDel(&errmsg, - PyUString_FromString(" and ")); - PyUString_ConcatAndDel(&errmsg, - PyObject_Repr((PyObject *)PyArray_DESCR(operands[1]))); - PyErr_SetObject(PyExc_TypeError, errmsg); - Py_DECREF(errmsg); - return -1; - } } /* @@ -795,7 +945,6 @@ PyUFunc_SubtractionTypeResolver(PyUFuncObject *ufunc, { int type_num1, type_num2; int i; - const char *ufunc_name = ufunc_get_name_cstr(ufunc); type_num1 = PyArray_DESCR(operands[0])->type_num; type_num2 = PyArray_DESCR(operands[1])->type_num; @@ -803,7 +952,7 @@ PyUFunc_SubtractionTypeResolver(PyUFuncObject *ufunc, /* Use the default when datetime and timedelta are not involved */ if (!PyTypeNum_ISDATETIME(type_num1) && !PyTypeNum_ISDATETIME(type_num2)) { int ret; - ret = PyUFunc_SimpleBinaryOperationTypeResolver(ufunc, casting, + ret = PyUFunc_SimpleUniformOperationTypeResolver(ufunc, casting, operands, type_tup, out_dtypes); if (ret < 0) { return ret; @@ -812,7 +961,7 @@ PyUFunc_SubtractionTypeResolver(PyUFuncObject *ufunc, /* The type resolver would have upcast already */ if (out_dtypes[0]->type_num == NPY_BOOL) { PyErr_Format(PyExc_TypeError, - "numpy boolean subtract, the `-` operator, is deprecated, " + "numpy boolean subtract, the `-` operator, is not supported, " "use the bitwise_xor, the `^` operator, or the logical_xor " "function instead."); return -1; @@ -848,7 +997,7 @@ PyUFunc_SubtractionTypeResolver(PyUFuncObject *ufunc, type_num2 = NPY_TIMEDELTA; } else { - goto type_reso_error; + return raise_binary_type_reso_error(ufunc, operands); } } else if (type_num1 == NPY_DATETIME) { @@ -906,7 +1055,7 @@ PyUFunc_SubtractionTypeResolver(PyUFuncObject *ufunc, Py_INCREF(out_dtypes[1]); } else { - goto type_reso_error; + return raise_binary_type_reso_error(ufunc, operands); } } else if (PyTypeNum_ISINTEGER(type_num1) || PyTypeNum_ISBOOL(type_num1)) { @@ -924,11 +1073,11 @@ PyUFunc_SubtractionTypeResolver(PyUFuncObject *ufunc, type_num1 = NPY_TIMEDELTA; } else { - goto type_reso_error; + return raise_binary_type_reso_error(ufunc, operands); } } else { - goto type_reso_error; + return raise_binary_type_reso_error(ufunc, operands); } /* Check against the casting rules */ @@ -941,21 +1090,6 @@ PyUFunc_SubtractionTypeResolver(PyUFuncObject *ufunc, } return 0; - -type_reso_error: { - PyObject *errmsg; - errmsg = PyUString_FromFormat("ufunc %s cannot use operands " - "with types ", ufunc_name); - PyUString_ConcatAndDel(&errmsg, - PyObject_Repr((PyObject *)PyArray_DESCR(operands[0]))); - PyUString_ConcatAndDel(&errmsg, - PyUString_FromString(" and ")); - PyUString_ConcatAndDel(&errmsg, - PyObject_Repr((PyObject *)PyArray_DESCR(operands[1]))); - PyErr_SetObject(PyExc_TypeError, errmsg); - Py_DECREF(errmsg); - return -1; - } } /* @@ -975,14 +1109,13 @@ PyUFunc_MultiplicationTypeResolver(PyUFuncObject *ufunc, { int type_num1, type_num2; int i; - const char *ufunc_name = ufunc_get_name_cstr(ufunc); type_num1 = PyArray_DESCR(operands[0])->type_num; type_num2 = PyArray_DESCR(operands[1])->type_num; /* Use the default when datetime and timedelta are not involved */ if (!PyTypeNum_ISDATETIME(type_num1) && !PyTypeNum_ISDATETIME(type_num2)) { - return PyUFunc_SimpleBinaryOperationTypeResolver(ufunc, casting, + return PyUFunc_SimpleUniformOperationTypeResolver(ufunc, casting, operands, type_tup, out_dtypes); } @@ -1022,7 +1155,7 @@ PyUFunc_MultiplicationTypeResolver(PyUFuncObject *ufunc, type_num2 = NPY_DOUBLE; } else { - goto type_reso_error; + return raise_binary_type_reso_error(ufunc, operands); } } else if (PyTypeNum_ISINTEGER(type_num1) || PyTypeNum_ISBOOL(type_num1)) { @@ -1044,7 +1177,7 @@ PyUFunc_MultiplicationTypeResolver(PyUFuncObject *ufunc, type_num1 = NPY_LONGLONG; } else { - goto type_reso_error; + return raise_binary_type_reso_error(ufunc, operands); } } else if (PyTypeNum_ISFLOAT(type_num1)) { @@ -1066,11 +1199,11 @@ PyUFunc_MultiplicationTypeResolver(PyUFuncObject *ufunc, type_num1 = NPY_DOUBLE; } else { - goto type_reso_error; + return raise_binary_type_reso_error(ufunc, operands); } } else { - goto type_reso_error; + return raise_binary_type_reso_error(ufunc, operands); } /* Check against the casting rules */ @@ -1083,21 +1216,6 @@ PyUFunc_MultiplicationTypeResolver(PyUFuncObject *ufunc, } return 0; - -type_reso_error: { - PyObject *errmsg; - errmsg = PyUString_FromFormat("ufunc %s cannot use operands " - "with types ", ufunc_name); - PyUString_ConcatAndDel(&errmsg, - PyObject_Repr((PyObject *)PyArray_DESCR(operands[0]))); - PyUString_ConcatAndDel(&errmsg, - PyUString_FromString(" and ")); - PyUString_ConcatAndDel(&errmsg, - PyObject_Repr((PyObject *)PyArray_DESCR(operands[1]))); - PyErr_SetObject(PyExc_TypeError, errmsg); - Py_DECREF(errmsg); - return -1; - } } @@ -1117,7 +1235,6 @@ PyUFunc_DivisionTypeResolver(PyUFuncObject *ufunc, { int type_num1, type_num2; int i; - const char *ufunc_name = ufunc_get_name_cstr(ufunc); type_num1 = PyArray_DESCR(operands[0])->type_num; type_num2 = PyArray_DESCR(operands[1])->type_num; @@ -1141,7 +1258,16 @@ PyUFunc_DivisionTypeResolver(PyUFuncObject *ufunc, } out_dtypes[1] = out_dtypes[0]; Py_INCREF(out_dtypes[1]); + + /* + * TODO: split function into truediv and floordiv resolvers + */ + if (strcmp(ufunc->name, "floor_divide") == 0) { + out_dtypes[2] = PyArray_DescrFromType(NPY_LONGLONG); + } + else { out_dtypes[2] = PyArray_DescrFromType(NPY_DOUBLE); + } if (out_dtypes[2] == NULL) { Py_DECREF(out_dtypes[0]); out_dtypes[0] = NULL; @@ -1185,11 +1311,11 @@ PyUFunc_DivisionTypeResolver(PyUFuncObject *ufunc, type_num2 = NPY_DOUBLE; } else { - goto type_reso_error; + return raise_binary_type_reso_error(ufunc, operands); } } else { - goto type_reso_error; + return raise_binary_type_reso_error(ufunc, operands); } /* Check against the casting rules */ @@ -1202,21 +1328,57 @@ PyUFunc_DivisionTypeResolver(PyUFuncObject *ufunc, } return 0; +} + + +NPY_NO_EXPORT int +PyUFunc_RemainderTypeResolver(PyUFuncObject *ufunc, + NPY_CASTING casting, + PyArrayObject **operands, + PyObject *type_tup, + PyArray_Descr **out_dtypes) +{ + int type_num1, type_num2; + int i; + + type_num1 = PyArray_DESCR(operands[0])->type_num; + type_num2 = PyArray_DESCR(operands[1])->type_num; + + /* Use the default when datetime and timedelta are not involved */ + if (!PyTypeNum_ISDATETIME(type_num1) && !PyTypeNum_ISDATETIME(type_num2)) { + return PyUFunc_DefaultTypeResolver(ufunc, casting, operands, + type_tup, out_dtypes); + } + if (type_num1 == NPY_TIMEDELTA) { + if (type_num2 == NPY_TIMEDELTA) { + out_dtypes[0] = PyArray_PromoteTypes(PyArray_DESCR(operands[0]), + PyArray_DESCR(operands[1])); + if (out_dtypes[0] == NULL) { + return -1; + } + out_dtypes[1] = out_dtypes[0]; + Py_INCREF(out_dtypes[1]); + out_dtypes[2] = out_dtypes[0]; + Py_INCREF(out_dtypes[2]); + } + else { + return raise_binary_type_reso_error(ufunc, operands); + } + } + else { + return raise_binary_type_reso_error(ufunc, operands); + } -type_reso_error: { - PyObject *errmsg; - errmsg = PyUString_FromFormat("ufunc %s cannot use operands " - "with types ", ufunc_name); - PyUString_ConcatAndDel(&errmsg, - PyObject_Repr((PyObject *)PyArray_DESCR(operands[0]))); - PyUString_ConcatAndDel(&errmsg, - PyUString_FromString(" and ")); - PyUString_ConcatAndDel(&errmsg, - PyObject_Repr((PyObject *)PyArray_DESCR(operands[1]))); - PyErr_SetObject(PyExc_TypeError, errmsg); - Py_DECREF(errmsg); + /* Check against the casting rules */ + if (PyUFunc_ValidateCasting(ufunc, casting, operands, out_dtypes) < 0) { + for (i = 0; i < 3; ++i) { + Py_DECREF(out_dtypes[i]); + out_dtypes[i] = NULL; + } return -1; } + + return 0; } @@ -1263,38 +1425,6 @@ PyUFunc_TrueDivisionTypeResolver(PyUFuncObject *ufunc, return PyUFunc_DivisionTypeResolver(ufunc, casting, operands, type_tup, out_dtypes); } -/* - * Function to check and report floor division warning when python2.x is - * invoked with -3 switch - * See PEP238 and #7949 for numpy - * This function will not be hit for py3 or when __future__ imports division. - * See generate_umath.py for reason -*/ -NPY_NO_EXPORT int -PyUFunc_MixedDivisionTypeResolver(PyUFuncObject *ufunc, - NPY_CASTING casting, - PyArrayObject **operands, - PyObject *type_tup, - PyArray_Descr **out_dtypes) -{ - /* Depreciation checks needed only on python 2 */ -#if !defined(NPY_PY3K) - int type_num1, type_num2; - - type_num1 = PyArray_DESCR(operands[0])->type_num; - type_num2 = PyArray_DESCR(operands[1])->type_num; - - /* If both types are integer, warn the user, same as python does */ - if (Py_DivisionWarningFlag && - (PyTypeNum_ISINTEGER(type_num1) || PyTypeNum_ISBOOL(type_num1)) && - (PyTypeNum_ISINTEGER(type_num2) || PyTypeNum_ISBOOL(type_num2))) { - PyErr_Warn(PyExc_DeprecationWarning, "numpy: classic int division"); - } -#endif - return PyUFunc_DivisionTypeResolver(ufunc, casting, operands, - type_tup, out_dtypes); -} - static int find_userloop(PyUFuncObject *ufunc, @@ -1303,7 +1433,6 @@ find_userloop(PyUFuncObject *ufunc, void **out_innerloopdata) { npy_intp i, nin = ufunc->nin, j, nargs = nin + ufunc->nout; - PyUFunc_Loop1d *funcdata; /* Use this to try to avoid repeating the same userdef loop search */ int last_userdef = -1; @@ -1323,18 +1452,23 @@ find_userloop(PyUFuncObject *ufunc, last_userdef = type_num; - key = PyInt_FromLong(type_num); + key = PyLong_FromLong(type_num); if (key == NULL) { return -1; } - obj = PyDict_GetItem(ufunc->userloops, key); + obj = PyDict_GetItemWithError(ufunc->userloops, key); Py_DECREF(key); - if (obj == NULL) { + if (obj == NULL && PyErr_Occurred()){ + return -1; + } + else if (obj == NULL) { continue; } - for (funcdata = (PyUFunc_Loop1d *)NpyCapsule_AsVoidPtr(obj); - funcdata != NULL; - funcdata = funcdata->next) { + PyUFunc_Loop1d *funcdata = PyCapsule_GetPointer(obj, NULL); + if (funcdata == NULL) { + return -1; + } + for (; funcdata != NULL; funcdata = funcdata->next) { int *types = funcdata->arg_types; for (j = 0; j < nargs; ++j) { @@ -1365,12 +1499,8 @@ PyUFunc_DefaultLegacyInnerLoopSelector(PyUFuncObject *ufunc, { int nargs = ufunc->nargs; char *types; - const char *ufunc_name; - PyObject *errmsg; int i, j; - ufunc_name = ufunc_get_name_cstr(ufunc); - /* * If there are user-loops search them first. * TODO: There needs to be a loop selection acceleration structure, @@ -1405,151 +1535,10 @@ PyUFunc_DefaultLegacyInnerLoopSelector(PyUFuncObject *ufunc, types += nargs; } - errmsg = PyUString_FromFormat("ufunc '%s' did not contain a loop " - "with signature matching types ", ufunc_name); - for (i = 0; i < nargs; ++i) { - PyUString_ConcatAndDel(&errmsg, - PyObject_Repr((PyObject *)dtypes[i])); - if (i < nargs - 1) { - PyUString_ConcatAndDel(&errmsg, PyUString_FromString(" ")); - } - } - PyErr_SetObject(PyExc_TypeError, errmsg); - Py_DECREF(errmsg); - - return -1; -} - -typedef struct { - NpyAuxData base; - PyUFuncGenericFunction unmasked_innerloop; - void *unmasked_innerloopdata; - int nargs; -} _ufunc_masker_data; - -static NpyAuxData * -ufunc_masker_data_clone(NpyAuxData *data) -{ - _ufunc_masker_data *n; - - /* Allocate a new one */ - n = (_ufunc_masker_data *)PyArray_malloc(sizeof(_ufunc_masker_data)); - if (n == NULL) { - return NULL; - } - - /* Copy the data (unmasked data doesn't have object semantics) */ - memcpy(n, data, sizeof(_ufunc_masker_data)); - - return (NpyAuxData *)n; -} - -/* - * This function wraps a regular unmasked ufunc inner loop as a - * masked ufunc inner loop, only calling the function for - * elements where the mask is True. - */ -static void -unmasked_ufunc_loop_as_masked( - char **dataptrs, npy_intp *strides, - char *mask, npy_intp mask_stride, - npy_intp loopsize, - NpyAuxData *innerloopdata) -{ - _ufunc_masker_data *data; - int iargs, nargs; - PyUFuncGenericFunction unmasked_innerloop; - void *unmasked_innerloopdata; - npy_intp subloopsize; - - /* Put the aux data into local variables */ - data = (_ufunc_masker_data *)innerloopdata; - unmasked_innerloop = data->unmasked_innerloop; - unmasked_innerloopdata = data->unmasked_innerloopdata; - nargs = data->nargs; - - /* Process the data as runs of unmasked values */ - do { - /* Skip masked values */ - mask = npy_memchr(mask, 0, mask_stride, loopsize, &subloopsize, 1); - for (iargs = 0; iargs < nargs; ++iargs) { - dataptrs[iargs] += subloopsize * strides[iargs]; - } - loopsize -= subloopsize; - /* - * Process unmasked values (assumes unmasked loop doesn't - * mess with the 'args' pointer values) - */ - mask = npy_memchr(mask, 0, mask_stride, loopsize, &subloopsize, 0); - unmasked_innerloop(dataptrs, &subloopsize, strides, - unmasked_innerloopdata); - for (iargs = 0; iargs < nargs; ++iargs) { - dataptrs[iargs] += subloopsize * strides[iargs]; - } - loopsize -= subloopsize; - } while (loopsize > 0); + return raise_no_loop_found_error(ufunc, (PyObject **)dtypes); } -/* - * This function wraps a legacy inner loop so it becomes masked. - * - * Returns 0 on success, -1 on error. - */ -NPY_NO_EXPORT int -PyUFunc_DefaultMaskedInnerLoopSelector(PyUFuncObject *ufunc, - PyArray_Descr **dtypes, - PyArray_Descr *mask_dtype, - npy_intp *NPY_UNUSED(fixed_strides), - npy_intp NPY_UNUSED(fixed_mask_stride), - PyUFunc_MaskedStridedInnerLoopFunc **out_innerloop, - NpyAuxData **out_innerloopdata, - int *out_needs_api) -{ - int retcode; - _ufunc_masker_data *data; - - if (ufunc->legacy_inner_loop_selector == NULL) { - PyErr_SetString(PyExc_RuntimeError, - "the ufunc default masked inner loop selector doesn't " - "yet support wrapping the new inner loop selector, it " - "still only wraps the legacy inner loop selector"); - return -1; - } - - if (mask_dtype->type_num != NPY_BOOL) { - PyErr_SetString(PyExc_ValueError, - "only boolean masks are supported in ufunc inner loops " - "presently"); - return -1; - } - - /* Create a new NpyAuxData object for the masker data */ - data = (_ufunc_masker_data *)PyArray_malloc(sizeof(_ufunc_masker_data)); - if (data == NULL) { - PyErr_NoMemory(); - return -1; - } - memset(data, 0, sizeof(_ufunc_masker_data)); - data->base.free = (NpyAuxData_FreeFunc *)&PyArray_free; - data->base.clone = &ufunc_masker_data_clone; - data->nargs = ufunc->nin + ufunc->nout; - - /* Get the unmasked ufunc inner loop */ - retcode = ufunc->legacy_inner_loop_selector(ufunc, dtypes, - &data->unmasked_innerloop, &data->unmasked_innerloopdata, - out_needs_api); - if (retcode < 0) { - PyArray_free(data); - return retcode; - } - - /* Return the loop function + aux data */ - *out_innerloop = &unmasked_ufunc_loop_as_masked; - *out_innerloopdata = (NpyAuxData *)data; - return 0; -} - static int ufunc_loop_matches(PyUFuncObject *self, PyArrayObject **op, @@ -1583,6 +1572,9 @@ ufunc_loop_matches(PyUFuncObject *self, if (types[i] == NPY_OBJECT && !any_object && self->ntypes > 1) { return 0; } + if (types[i] == NPY_NOTYPE) { + continue; /* Matched by being explicitly specified. */ + } /* * If type num is NPY_VOID and struct dtypes have been passed in, @@ -1632,6 +1624,9 @@ ufunc_loop_matches(PyUFuncObject *self, * outputs. */ for (i = nin; i < nop; ++i) { + if (types[i] == NPY_NOTYPE) { + continue; /* Matched by being explicitly specified. */ + } if (op[i] != NULL) { PyArray_Descr *tmp = PyArray_DescrFromType(types[i]); if (tmp == NULL) { @@ -1650,7 +1645,6 @@ ufunc_loop_matches(PyUFuncObject *self, Py_DECREF(tmp); } } - return 1; } @@ -1682,7 +1676,7 @@ set_ufunc_loop_data_types(PyUFuncObject *self, PyArrayObject **op, } /* * For outputs, copy the dtype from op[0] if the type_num - * matches, similarly to preserve metdata. + * matches, similarly to preserve metadata. */ else if (i >= nin && op[0] != NULL && PyArray_DESCR(op[0])->type_num == type_nums[i]) { @@ -1724,7 +1718,6 @@ linear_search_userloop_type_resolver(PyUFuncObject *self, char *out_err_dst_typecode) { npy_intp i, nop = self->nin + self->nout; - PyUFunc_Loop1d *funcdata; /* Use this to try to avoid repeating the same userdef loop search */ int last_userdef = -1; @@ -1744,18 +1737,23 @@ linear_search_userloop_type_resolver(PyUFuncObject *self, last_userdef = type_num; - key = PyInt_FromLong(type_num); + key = PyLong_FromLong(type_num); if (key == NULL) { return -1; } - obj = PyDict_GetItem(self->userloops, key); + obj = PyDict_GetItemWithError(self->userloops, key); Py_DECREF(key); - if (obj == NULL) { + if (obj == NULL && PyErr_Occurred()){ + return -1; + } + else if (obj == NULL) { continue; } - for (funcdata = (PyUFunc_Loop1d *)NpyCapsule_AsVoidPtr(obj); - funcdata != NULL; - funcdata = funcdata->next) { + PyUFunc_Loop1d *funcdata = PyCapsule_GetPointer(obj, NULL); + if (funcdata == NULL) { + return -1; + } + for (; funcdata != NULL; funcdata = funcdata->next) { int *types = funcdata->arg_types; switch (ufunc_loop_matches(self, op, input_casting, output_casting, @@ -1787,13 +1785,15 @@ type_tuple_userloop_type_resolver(PyUFuncObject *self, int n_specified, int *specified_types, PyArrayObject **op, + NPY_CASTING input_casting, NPY_CASTING casting, int any_object, int use_min_scalar, PyArray_Descr **out_dtype) { int i, j, nin = self->nin, nop = nin + self->nout; - PyUFunc_Loop1d *funcdata; + assert(n_specified == nop); + int types[NPY_MAXARGS]; /* Use this to try to avoid repeating the same userdef loop search */ int last_userdef = -1; @@ -1808,41 +1808,49 @@ type_tuple_userloop_type_resolver(PyUFuncObject *self, last_userdef = type_num; - key = PyInt_FromLong(type_num); + key = PyLong_FromLong(type_num); if (key == NULL) { return -1; } - obj = PyDict_GetItem(self->userloops, key); + obj = PyDict_GetItemWithError(self->userloops, key); Py_DECREF(key); - if (obj == NULL) { + if (obj == NULL && PyErr_Occurred()){ + return -1; + } + else if (obj == NULL) { continue; } - for (funcdata = (PyUFunc_Loop1d *)NpyCapsule_AsVoidPtr(obj); - funcdata != NULL; - funcdata = funcdata->next) { - int *types = funcdata->arg_types; - int matched = 1; - - if (n_specified == nop) { - for (j = 0; j < nop; ++j) { - if (types[j] != specified_types[j] && - specified_types[j] != NPY_NOTYPE) { - matched = 0; - break; - } + PyUFunc_Loop1d *funcdata = PyCapsule_GetPointer(obj, NULL); + if (funcdata == NULL) { + return -1; + } + for (; funcdata != NULL; funcdata = funcdata->next) { + int *orig_types = funcdata->arg_types; + + /* + * Copy the types into an int array for matching + * (Mostly duplicated in `type_tuple_type_resolver`) + */ + for (j = 0; j < nop; ++j) { + if (specified_types[j] == NPY_NOTYPE) { + types[j] = orig_types[j]; + continue; } - } else { - if (types[nin] != specified_types[0]) { - matched = 0; + if (orig_types[j] != specified_types[j]) { + break; } + /* indicate that we do not have to check this type anymore. */ + types[j] = NPY_NOTYPE; } - if (!matched) { + + if (j != nop) { + /* no match */ continue; } switch (ufunc_loop_matches(self, op, - casting, casting, + input_casting, casting, any_object, use_min_scalar, types, NULL, &no_castable_output, &err_src_typecode, @@ -1850,7 +1858,19 @@ type_tuple_userloop_type_resolver(PyUFuncObject *self, /* It works */ case 1: set_ufunc_loop_data_types(self, op, - out_dtype, types, NULL); + out_dtype, orig_types, NULL); + /* + * In principle, we only need to validate the + * NPY_NOTYPE ones + */ + if (PyUFunc_ValidateCasting(self, + casting, op, out_dtype) < 0) { + for (j = 0; j < self->nargs; j++) { + Py_DECREF(out_dtype[j]); + out_dtype[j] = NULL; + } + return -1; + } return 1; /* Didn't match */ case 0: @@ -1873,73 +1893,6 @@ type_tuple_userloop_type_resolver(PyUFuncObject *self, return 0; } -/* - * Provides an ordering for the dtype 'kind' character codes, to help - * determine when to use the min_scalar_type function. This groups - * 'kind' into boolean, integer, floating point, and everything else. - */ - -static int -dtype_kind_to_simplified_ordering(char kind) -{ - switch (kind) { - /* Boolean kind */ - case 'b': - return 0; - /* Unsigned int kind */ - case 'u': - /* Signed int kind */ - case 'i': - return 1; - /* Float kind */ - case 'f': - /* Complex kind */ - case 'c': - return 2; - /* Anything else */ - default: - return 3; - } -} - -static int -should_use_min_scalar(PyArrayObject **op, int nop) -{ - int i, use_min_scalar, kind; - int all_scalars = 1, max_scalar_kind = -1, max_array_kind = -1; - - /* - * Determine if there are any scalars, and if so, whether - * the maximum "kind" of the scalars surpasses the maximum - * "kind" of the arrays - */ - use_min_scalar = 0; - if (nop > 1) { - for(i = 0; i < nop; ++i) { - kind = dtype_kind_to_simplified_ordering( - PyArray_DESCR(op[i])->kind); - if (PyArray_NDIM(op[i]) == 0) { - if (kind > max_scalar_kind) { - max_scalar_kind = kind; - } - } - else { - all_scalars = 0; - if (kind > max_array_kind) { - max_array_kind = kind; - } - - } - } - - /* Indicate whether to use the min_scalar_type function */ - if (!all_scalars && max_array_kind >= max_scalar_kind) { - use_min_scalar = 1; - } - } - - return use_min_scalar; -} /* * Does a linear search for the best inner loop of the ufunc. @@ -1958,14 +1911,15 @@ linear_search_type_resolver(PyUFuncObject *self, npy_intp i, j, nin = self->nin, nop = nin + self->nout; int types[NPY_MAXARGS]; const char *ufunc_name; - int no_castable_output, use_min_scalar; + int no_castable_output = 0; + int use_min_scalar; /* For making a better error message on coercion error */ char err_dst_typecode = '-', err_src_typecode = '-'; ufunc_name = ufunc_get_name_cstr(self); - use_min_scalar = should_use_min_scalar(op, nin); + use_min_scalar = should_use_min_scalar(nin, op, 0, NULL); /* If the ufunc has userloops, search for them. */ if (self->userloops) { @@ -2049,6 +2003,94 @@ linear_search_type_resolver(PyUFuncObject *self, return -1; } + +static int +type_tuple_type_resolver_core(PyUFuncObject *self, + PyArrayObject **op, + NPY_CASTING input_casting, NPY_CASTING casting, + int specified_types[], + int any_object, + int no_castable_output, int use_min_scalar, + PyArray_Descr **out_dtype) +{ + int i, j; + int nop = self->nargs; + int types[NPY_MAXARGS]; + + /* For making a better error message on coercion error */ + char err_dst_typecode = '-', err_src_typecode = '-'; + + /* If the ufunc has userloops, search for them. */ + if (self->userloops) { + switch (type_tuple_userloop_type_resolver(self, + nop, specified_types, + op, input_casting, casting, + any_object, use_min_scalar, + out_dtype)) { + /* Error */ + case -1: + return -1; + /* Found matching loop */ + case 1: + return 0; + } + } + + for (i = 0; i < self->ntypes; ++i) { + char *orig_types = self->types + i*self->nargs; + + /* + * Check specified types and copy into an int array for matching + * (Mostly duplicated in `type_tuple_userloop_type_resolver`) + */ + for (j = 0; j < nop; ++j) { + if (specified_types[j] == NPY_NOTYPE) { + types[j] = orig_types[j]; + continue; + } + if (orig_types[j] != specified_types[j]) { + break; + } + /* indicate that we do not have to check this type anymore. */ + types[j] = NPY_NOTYPE; + } + if (j < nop) { + /* no match */ + continue; + } + + switch (ufunc_loop_matches(self, op, + input_casting, casting, + any_object, use_min_scalar, + types, NULL, + &no_castable_output, &err_src_typecode, + &err_dst_typecode)) { + case -1: + /* Error */ + return -1; + case 0: + /* Cannot cast inputs */ + continue; + case 1: + /* Success, fill also the NPY_NOTYPE (cast from char to int) */ + for (j = 0; j < nop; j++) { + types[j] = orig_types[j]; + } + set_ufunc_loop_data_types(self, op, out_dtype, types, NULL); + /* In principle, we only need to validate the NPY_NOTYPE ones */ + if (PyUFunc_ValidateCasting(self, casting, op, out_dtype) < 0) { + for (j = 0; j < self->nargs; j++) { + Py_DECREF(out_dtype[j]); + out_dtype[j] = NULL; + } + return -1; + } + return 0; + } + } + return -2; +} + /* * Does a linear search for the inner loop of the ufunc specified by type_tup. * @@ -2059,183 +2101,159 @@ NPY_NO_EXPORT int type_tuple_type_resolver(PyUFuncObject *self, PyObject *type_tup, PyArrayObject **op, + NPY_CASTING input_casting, NPY_CASTING casting, int any_object, PyArray_Descr **out_dtype) { - npy_intp i, j, n, nin = self->nin, nop = nin + self->nout; - int n_specified = 0; - int specified_types[NPY_MAXARGS], types[NPY_MAXARGS]; + int nin = self->nin, nop = nin + self->nout; + int specified_types[NPY_MAXARGS]; const char *ufunc_name; - int no_castable_output, use_min_scalar; - - /* For making a better error message on coercion error */ - char err_dst_typecode = '-', err_src_typecode = '-'; + int no_castable_output = 0, use_min_scalar; ufunc_name = ufunc_get_name_cstr(self); - use_min_scalar = should_use_min_scalar(op, nin); + use_min_scalar = should_use_min_scalar(nin, op, 0, NULL); /* Fill in specified_types from the tuple or string */ - if (PyTuple_Check(type_tup)) { - int nonecount = 0; - n = PyTuple_GET_SIZE(type_tup); - if (n != 1 && n != nop) { - PyErr_Format(PyExc_ValueError, - "a type-tuple must be specified " - "of length 1 or %d for ufunc '%s'", (int)nop, - ufunc_get_name_cstr(self)); + const char *bad_type_tup_msg = ( + "Only NumPy must call `ufunc->type_resolver()` explicitly. " + "NumPy ensures that a type-tuple is normalized now to be a tuple " + "only containing None or descriptors. If anything else is passed " + "(you are seeing this message), the `type_resolver()` was called " + "directly by a third party. " + "This is unexpected, please inform the NumPy developers about it. " + "Also note that `type_resolver` will be phased out, since it must " + "be replaced."); + + if (PyTuple_CheckExact(type_tup)) { + Py_ssize_t n = PyTuple_GET_SIZE(type_tup); + if (n != nop) { + PyErr_SetString(PyExc_RuntimeError, bad_type_tup_msg); return -1; } - - for (i = 0; i < n; ++i) { + for (int i = 0; i < nop; ++i) { PyObject *item = PyTuple_GET_ITEM(type_tup, i); if (item == Py_None) { specified_types[i] = NPY_NOTYPE; - ++nonecount; } else { - PyArray_Descr *dtype = NULL; - if (!PyArray_DescrConverter(item, &dtype)) { + if (!PyArray_DescrCheck(item)) { + PyErr_SetString(PyExc_RuntimeError, bad_type_tup_msg); return -1; } - specified_types[i] = dtype->type_num; - Py_DECREF(dtype); + specified_types[i] = ((PyArray_Descr *)item)->type_num; } } + } + else { + PyErr_SetString(PyExc_RuntimeError, bad_type_tup_msg); + return -1; + } - if (nonecount == n) { - PyErr_SetString(PyExc_ValueError, - "the type-tuple provided to the ufunc " - "must specify at least one none-None dtype"); - return -1; - } + int res = type_tuple_type_resolver_core(self, + op, input_casting, casting, specified_types, any_object, + no_castable_output, use_min_scalar, out_dtype); - n_specified = n; + if (res != -2) { + return res; } - else if (PyBytes_Check(type_tup) || PyUnicode_Check(type_tup)) { - Py_ssize_t length; - char *str; - PyObject *str_obj = NULL; - if (PyUnicode_Check(type_tup)) { - str_obj = PyUnicode_AsASCIIString(type_tup); - if (str_obj == NULL) { - return -1; + /* + * When the user passes `dtype=dtype`, it gets translated to + * `signature=(None,)*nin + (dtype,)*nout`. If the signature matches that + * exactly (could be relaxed but that is not necessary for backcompat), + * we also try `signature=(dtype,)*(nin+nout)`. + * Since reduction pass in `(dtype, None, dtype)` we broaden this to + * replacing all unspecified dtypes with the homogeneous output one. + * Note that this can (and often will) lead to unsafe casting. This is + * normally rejected (but not currently for reductions!). + * This used to be the main meaning for `dtype=dtype`, but some calls broke + * the expectation, and changing it allows for `dtype=dtype` to be useful + * for ufuncs like `np.ldexp` in the future while also normalizing it to + * a `signature` early on. + */ + int homogeneous_type = NPY_NOTYPE; + if (self->nout > 0) { + homogeneous_type = specified_types[nin]; + for (int i = nin+1; i < nop; i++) { + if (specified_types[i] != homogeneous_type) { + homogeneous_type = NPY_NOTYPE; + break; } - type_tup = str_obj; - } - - if (PyBytes_AsStringAndSize(type_tup, &str, &length) < 0) { - Py_XDECREF(str_obj); - return -1; - } - if (length != 1 && (length != nop + 2 || - str[nin] != '-' || str[nin+1] != '>')) { - PyErr_Format(PyExc_ValueError, - "a type-string for %s, " \ - "requires 1 typecode, or " - "%d typecode(s) before " \ - "and %d after the -> sign", - ufunc_get_name_cstr(self), - self->nin, self->nout); - Py_XDECREF(str_obj); - return -1; } - if (length == 1) { - PyArray_Descr *dtype; - n_specified = 1; - dtype = PyArray_DescrFromType(str[0]); - if (dtype == NULL) { - Py_XDECREF(str_obj); - return -1; + } + if (homogeneous_type != NPY_NOTYPE) { + for (int i = 0; i < nin; i++) { + if (specified_types[i] != NPY_NOTYPE) { + /* Never replace a specified type! */ + continue; } - specified_types[0] = dtype->type_num; - Py_DECREF(dtype); + specified_types[i] = homogeneous_type; } - else { - PyArray_Descr *dtype; - n_specified = (int)nop; - for (i = 0; i < nop; ++i) { - npy_intp istr = i < nin ? i : i+2; + /* Try again with the homogeneous specified types. */ + res = type_tuple_type_resolver_core(self, + op, input_casting, casting, specified_types, any_object, + no_castable_output, use_min_scalar, out_dtype); - dtype = PyArray_DescrFromType(str[istr]); - if (dtype == NULL) { - Py_XDECREF(str_obj); - return -1; - } - specified_types[i] = dtype->type_num; - Py_DECREF(dtype); - } + if (res != -2) { + return res; } - Py_XDECREF(str_obj); } - /* If the ufunc has userloops, search for them. */ - if (self->userloops) { - switch (type_tuple_userloop_type_resolver(self, - n_specified, specified_types, - op, casting, - any_object, use_min_scalar, - out_dtype)) { - /* Error */ - case -1: - return -1; - /* Found matching loop */ - case 1: - return 0; - } - } + /* If no function was found, throw an error */ + PyErr_Format(PyExc_TypeError, + "No loop matching the specified signature and casting " + "was found for ufunc %s", ufunc_name); - for (i = 0; i < self->ntypes; ++i) { - char *orig_types = self->types + i*self->nargs; + return -1; +} - /* Copy the types into an int array for matching */ - for (j = 0; j < nop; ++j) { - types[j] = orig_types[j]; - } +NPY_NO_EXPORT int +PyUFunc_DivmodTypeResolver(PyUFuncObject *ufunc, + NPY_CASTING casting, + PyArrayObject **operands, + PyObject *type_tup, + PyArray_Descr **out_dtypes) +{ + int type_num1, type_num2; + int i; - if (n_specified == nop) { - for (j = 0; j < nop; ++j) { - if (types[j] != specified_types[j] && - specified_types[j] != NPY_NOTYPE) { - break; - } - } - if (j < nop) { - /* no match */ - continue; - } + type_num1 = PyArray_DESCR(operands[0])->type_num; + type_num2 = PyArray_DESCR(operands[1])->type_num; + + /* Use the default when datetime and timedelta are not involved */ + if (!PyTypeNum_ISDATETIME(type_num1) && !PyTypeNum_ISDATETIME(type_num2)) { + return PyUFunc_DefaultTypeResolver(ufunc, casting, operands, + type_tup, out_dtypes); + } + if (type_num1 == NPY_TIMEDELTA) { + if (type_num2 == NPY_TIMEDELTA) { + out_dtypes[0] = PyArray_PromoteTypes(PyArray_DESCR(operands[0]), + PyArray_DESCR(operands[1])); + out_dtypes[1] = out_dtypes[0]; + Py_INCREF(out_dtypes[1]); + out_dtypes[2] = PyArray_DescrFromType(NPY_LONGLONG); + out_dtypes[3] = out_dtypes[0]; + Py_INCREF(out_dtypes[3]); } - else if (types[nin] != specified_types[0]) { - /* no match */ - continue; + else { + return raise_binary_type_reso_error(ufunc, operands); } + } + else { + return raise_binary_type_reso_error(ufunc, operands); + } - switch (ufunc_loop_matches(self, op, - casting, casting, - any_object, use_min_scalar, - types, NULL, - &no_castable_output, &err_src_typecode, - &err_dst_typecode)) { - case -1: - /* Error */ - return -1; - case 0: - /* Cannot cast inputs */ - continue; - case 1: - /* Success */ - set_ufunc_loop_data_types(self, op, out_dtype, types, NULL); - return 0; + /* Check against the casting rules */ + if (PyUFunc_ValidateCasting(ufunc, casting, operands, out_dtypes) < 0) { + for (i = 0; i < 4; ++i) { + Py_DECREF(out_dtypes[i]); + out_dtypes[i] = NULL; } + return -1; } - /* If no function was found, throw an error */ - PyErr_Format(PyExc_TypeError, - "No loop matching the specified signature and casting\n" - "was found for ufunc %s", ufunc_name); - - return -1; + return 0; } diff --git a/numpy/core/src/umath/ufunc_type_resolution.h b/numpy/core/src/umath/ufunc_type_resolution.h index fa9f1dbfa53c..84a2593f44c4 100644 --- a/numpy/core/src/umath/ufunc_type_resolution.h +++ b/numpy/core/src/umath/ufunc_type_resolution.h @@ -8,13 +8,6 @@ PyUFunc_SimpleBinaryComparisonTypeResolver(PyUFuncObject *ufunc, PyObject *type_tup, PyArray_Descr **out_dtypes); -NPY_NO_EXPORT int -PyUFunc_SimpleUnaryOperationTypeResolver(PyUFuncObject *ufunc, - NPY_CASTING casting, - PyArrayObject **operands, - PyObject *type_tup, - PyArray_Descr **out_dtypes); - NPY_NO_EXPORT int PyUFunc_NegativeTypeResolver(PyUFuncObject *ufunc, NPY_CASTING casting, @@ -30,7 +23,7 @@ PyUFunc_OnesLikeTypeResolver(PyUFuncObject *ufunc, PyArray_Descr **out_dtypes); NPY_NO_EXPORT int -PyUFunc_SimpleBinaryOperationTypeResolver(PyUFuncObject *ufunc, +PyUFunc_SimpleUniformOperationTypeResolver(PyUFuncObject *ufunc, NPY_CASTING casting, PyArrayObject **operands, PyObject *type_tup, @@ -50,6 +43,13 @@ PyUFunc_IsNaTTypeResolver(PyUFuncObject *ufunc, PyObject *type_tup, PyArray_Descr **out_dtypes); +NPY_NO_EXPORT int +PyUFunc_IsFiniteTypeResolver(PyUFuncObject *ufunc, + NPY_CASTING casting, + PyArrayObject **operands, + PyObject *type_tup, + PyArray_Descr **out_dtypes); + NPY_NO_EXPORT int PyUFunc_AdditionTypeResolver(PyUFuncObject *ufunc, NPY_CASTING casting, @@ -71,13 +71,6 @@ PyUFunc_MultiplicationTypeResolver(PyUFuncObject *ufunc, PyObject *type_tup, PyArray_Descr **out_dtypes); -NPY_NO_EXPORT int -PyUFunc_MixedDivisionTypeResolver(PyUFuncObject *ufunc, - NPY_CASTING casting, - PyArrayObject **operands, - PyObject *type_tup, - PyArray_Descr **out_dtypes); - NPY_NO_EXPORT int PyUFunc_TrueDivisionTypeResolver(PyUFuncObject *ufunc, NPY_CASTING casting, @@ -92,6 +85,24 @@ PyUFunc_DivisionTypeResolver(PyUFuncObject *ufunc, PyObject *type_tup, PyArray_Descr **out_dtypes); +NPY_NO_EXPORT int +PyUFunc_RemainderTypeResolver(PyUFuncObject *ufunc, + NPY_CASTING casting, + PyArrayObject **operands, + PyObject *type_tup, + PyArray_Descr **out_dtypes); + +NPY_NO_EXPORT int +PyUFunc_DivmodTypeResolver(PyUFuncObject *ufunc, + NPY_CASTING casting, + PyArrayObject **operands, + PyObject *type_tup, + PyArray_Descr **out_dtypes); + +NPY_NO_EXPORT int +PyUFunc_ValidateOutCasting(PyUFuncObject *ufunc, + NPY_CASTING casting, PyArrayObject **operands, PyArray_Descr **dtypes); + /* * Does a linear search for the best inner loop of the ufunc. * @@ -116,6 +127,7 @@ NPY_NO_EXPORT int type_tuple_type_resolver(PyUFuncObject *self, PyObject *type_tup, PyArrayObject **op, + NPY_CASTING input_casting, NPY_CASTING casting, int any_object, PyArray_Descr **out_dtype); @@ -128,15 +140,6 @@ PyUFunc_DefaultLegacyInnerLoopSelector(PyUFuncObject *ufunc, int *out_needs_api); NPY_NO_EXPORT int -PyUFunc_DefaultMaskedInnerLoopSelector(PyUFuncObject *ufunc, - PyArray_Descr **dtypes, - PyArray_Descr *mask_dtypes, - npy_intp *NPY_UNUSED(fixed_strides), - npy_intp NPY_UNUSED(fixed_mask_stride), - PyUFunc_MaskedStridedInnerLoopFunc - **out_innerloop, - NpyAuxData **out_innerloopdata, - int *out_needs_api); - +raise_no_loop_found_error(PyUFuncObject *ufunc, PyObject **dtypes); #endif diff --git a/numpy/core/src/umath/umathmodule.c b/numpy/core/src/umath/umathmodule.c index 5567b9bbfab1..272555704cf5 100644 --- a/numpy/core/src/umath/umathmodule.c +++ b/numpy/core/src/umath/umathmodule.c @@ -1,27 +1,19 @@ /* -*- c -*- */ - -/* - * vim:syntax=c - */ - -/* - ***************************************************************************** - ** INCLUDES ** - ***************************************************************************** - */ +/* vim:syntax=c */ /* * _UMATHMODULE IS needed in __ufunc_api.h, included from numpy/ufuncobject.h. * This is a mess and it would be nice to fix it. It has nothing to do with * __ufunc_api.c */ -#define _UMATHMODULE #define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define _MULTIARRAYMODULE +#define _UMATHMODULE -#include "Python.h" +#define PY_SSIZE_T_CLEAN +#include <Python.h> #include "npy_config.h" -#define PY_ARRAY_UNIQUE_SYMBOL _npy_umathmodule_ARRAY_API #include "numpy/arrayobject.h" #include "numpy/ufuncobject.h" @@ -29,20 +21,8 @@ #include "abstract.h" #include "numpy/npy_math.h" - -/* - ***************************************************************************** - ** INCLUDE GENERATED CODE ** - ***************************************************************************** - */ -#include "funcs.inc" -#include "loops.h" -#include "ufunc_object.h" -#include "ufunc_type_resolution.h" -#include "__umath_generated.c" -#include "__ufunc_api.c" - -NPY_NO_EXPORT int initscalarmath(PyObject *); +#include "number.h" +#include "dispatching.h" static PyUFuncGenericFunction pyfunc_functions[] = {PyUFunc_On_Om}; @@ -82,36 +62,39 @@ object_ufunc_loop_selector(PyUFuncObject *ufunc, return 0; } -static PyObject * -ufunc_frompyfunc(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *NPY_UNUSED(kwds)) { - /* Keywords are ignored for now */ - +PyObject * +ufunc_frompyfunc(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds) { PyObject *function, *pyname = NULL; int nin, nout, i, nargs; PyUFunc_PyFuncData *fdata; PyUFuncObject *self; - char *fname, *str, *types, *doc; + const char *fname = NULL; + char *str, *types, *doc; Py_ssize_t fname_len = -1; void * ptr, **data; int offset[2]; + PyObject *identity = NULL; /* note: not the same semantics as Py_None */ + static char *kwlist[] = {"", "nin", "nout", "identity", NULL}; - if (!PyArg_ParseTuple(args, "Oii:frompyfunc", &function, &nin, &nout)) { + if (!PyArg_ParseTupleAndKeywords(args, kwds, "Oii|$O:frompyfunc", kwlist, + &function, &nin, &nout, &identity)) { return NULL; } if (!PyCallable_Check(function)) { PyErr_SetString(PyExc_TypeError, "function must be callable"); return NULL; } + nargs = nin + nout; pyname = PyObject_GetAttrString(function, "__name__"); if (pyname) { - (void) PyString_AsStringAndSize(pyname, &fname, &fname_len); + fname = PyUnicode_AsUTF8AndSize(pyname, &fname_len); } - if (PyErr_Occurred()) { + if (fname == NULL) { + PyErr_Clear(); fname = "?"; fname_len = 1; - PyErr_Clear(); } /* @@ -159,10 +142,10 @@ ufunc_frompyfunc(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *NPY_UNUS /* Do a better job someday */ doc = "dynamic ufunc based on a python function"; - self = (PyUFuncObject *)PyUFunc_FromFuncAndData( + self = (PyUFuncObject *)PyUFunc_FromFuncAndDataAndSignatureAndIdentity( (PyUFuncGenericFunction *)pyfunc_functions, data, - types, /* ntypes */ 1, nin, nout, PyUFunc_None, - str, doc, /* unused */ 0); + types, /* ntypes */ 1, nin, nout, identity ? PyUFunc_IdentityValue : PyUFunc_None, + str, doc, /* unused */ 0, NULL, identity); if (self == NULL) { PyArray_free(ptr); @@ -174,48 +157,48 @@ ufunc_frompyfunc(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *NPY_UNUS self->type_resolver = &object_ufunc_type_resolver; self->legacy_inner_loop_selector = &object_ufunc_loop_selector; + PyObject_GC_Track(self); return (PyObject *)self; } /* docstring in numpy.add_newdocs.py */ -static PyObject * +PyObject * add_newdoc_ufunc(PyObject *NPY_UNUSED(dummy), PyObject *args) { PyUFuncObject *ufunc; PyObject *str; - char *docstr, *newdocstr; - -#if defined(NPY_PY3K) if (!PyArg_ParseTuple(args, "O!O!:_add_newdoc_ufunc", &PyUFunc_Type, &ufunc, &PyUnicode_Type, &str)) { return NULL; } - docstr = PyBytes_AS_STRING(PyUnicode_AsUTF8String(str)); -#else - if (!PyArg_ParseTuple(args, "O!O!:_add_newdoc_ufunc", &PyUFunc_Type, &ufunc, - &PyString_Type, &str)) { - return NULL; - } - docstr = PyString_AS_STRING(str); -#endif - - if (NULL != ufunc->doc) { + if (ufunc->doc != NULL) { PyErr_SetString(PyExc_ValueError, "Cannot change docstring of ufunc with non-NULL docstring"); return NULL; } + PyObject *tmp = PyUnicode_AsUTF8String(str); + if (tmp == NULL) { + return NULL; + } + char *docstr = PyBytes_AS_STRING(tmp); + /* * This introduces a memory leak, as the memory allocated for the doc * will not be freed even if the ufunc itself is deleted. In practice * this should not be a problem since the user would have to * repeatedly create, document, and throw away ufuncs. */ - newdocstr = malloc(strlen(docstr) + 1); + char *newdocstr = malloc(strlen(docstr) + 1); + if (!newdocstr) { + Py_DECREF(tmp); + return PyErr_NoMemory(); + } strcpy(newdocstr, docstr); ufunc->doc = newdocstr; + Py_DECREF(tmp); Py_RETURN_NONE; } @@ -226,121 +209,43 @@ add_newdoc_ufunc(PyObject *NPY_UNUSED(dummy), PyObject *args) ***************************************************************************** */ -NPY_VISIBILITY_HIDDEN PyObject * npy_um_str_out = NULL; -NPY_VISIBILITY_HIDDEN PyObject * npy_um_str_subok = NULL; -NPY_VISIBILITY_HIDDEN PyObject * npy_um_str_array_prepare = NULL; -NPY_VISIBILITY_HIDDEN PyObject * npy_um_str_array_wrap = NULL; -NPY_VISIBILITY_HIDDEN PyObject * npy_um_str_array_finalize = NULL; -NPY_VISIBILITY_HIDDEN PyObject * npy_um_str_ufunc = NULL; -NPY_VISIBILITY_HIDDEN PyObject * npy_um_str_pyvals_name = NULL; +NPY_VISIBILITY_HIDDEN PyObject *npy_um_str_array_prepare = NULL; +NPY_VISIBILITY_HIDDEN PyObject *npy_um_str_array_wrap = NULL; +NPY_VISIBILITY_HIDDEN PyObject *npy_um_str_pyvals_name = NULL; -/* intern some strings used in ufuncs */ +/* intern some strings used in ufuncs, returns 0 on success */ static int intern_strings(void) { - npy_um_str_out = PyUString_InternFromString("out"); - npy_um_str_subok = PyUString_InternFromString("subok"); - npy_um_str_array_prepare = PyUString_InternFromString("__array_prepare__"); - npy_um_str_array_wrap = PyUString_InternFromString("__array_wrap__"); - npy_um_str_array_finalize = PyUString_InternFromString("__array_finalize__"); - npy_um_str_ufunc = PyUString_InternFromString("__array_ufunc__"); - npy_um_str_pyvals_name = PyUString_InternFromString(UFUNC_PYVALS_NAME); - - return npy_um_str_out && npy_um_str_subok && npy_um_str_array_prepare && - npy_um_str_array_wrap && npy_um_str_array_finalize && npy_um_str_ufunc; + npy_um_str_array_prepare = PyUnicode_InternFromString("__array_prepare__"); + if (npy_um_str_array_prepare == NULL) { + return -1; + } + npy_um_str_array_wrap = PyUnicode_InternFromString("__array_wrap__"); + if (npy_um_str_array_wrap == NULL) { + return -1; + } + npy_um_str_pyvals_name = PyUnicode_InternFromString(UFUNC_PYVALS_NAME); + if (npy_um_str_pyvals_name == NULL) { + return -1; + } + return 0; } -/* Setup the umath module */ -/* Remove for time being, it is declared in __ufunc_api.h */ -/*static PyTypeObject PyUFunc_Type;*/ - -static struct PyMethodDef methods[] = { - {"frompyfunc", - (PyCFunction) ufunc_frompyfunc, - METH_VARARGS | METH_KEYWORDS, NULL}, - {"seterrobj", - (PyCFunction) ufunc_seterr, - METH_VARARGS, NULL}, - {"geterrobj", - (PyCFunction) ufunc_geterr, - METH_VARARGS, NULL}, - {"_add_newdoc_ufunc", (PyCFunction)add_newdoc_ufunc, - METH_VARARGS, NULL}, - {NULL, NULL, 0, NULL} /* sentinel */ -}; - - -#if defined(NPY_PY3K) -static struct PyModuleDef moduledef = { - PyModuleDef_HEAD_INIT, - "umath", - NULL, - -1, - methods, - NULL, - NULL, - NULL, - NULL -}; -#endif - -#include <stdio.h> +/* Setup the umath part of the module */ -#if defined(NPY_PY3K) -#define RETVAL(x) x -PyMODINIT_FUNC PyInit_umath(void) -#else -#define RETVAL(x) -PyMODINIT_FUNC initumath(void) -#endif +int initumath(PyObject *m) { - PyObject *m, *d, *s, *s2, *c_api; + PyObject *d, *s, *s2; int UFUNC_FLOATING_POINT_SUPPORT = 1; #ifdef NO_UFUNC_FLOATING_POINT_SUPPORT UFUNC_FLOATING_POINT_SUPPORT = 0; #endif - /* Create the module and add the functions */ -#if defined(NPY_PY3K) - m = PyModule_Create(&moduledef); -#else - m = Py_InitModule("umath", methods); -#endif - if (!m) { - goto err; - } - - /* Import the array */ - if (_import_array() < 0) { - if (!PyErr_Occurred()) { - PyErr_SetString(PyExc_ImportError, - "umath failed: Could not import array core."); - } - goto err; - } - - /* Initialize the types */ - if (PyType_Ready(&PyUFunc_Type) < 0) - goto err; /* Add some symbolic constants to the module */ d = PyModule_GetDict(m); - c_api = NpyCapsule_FromVoidPtr((void *)PyUFunc_API, NULL); - if (PyErr_Occurred()) { - goto err; - } - PyDict_SetItemString(d, "_UFUNC_API", c_api); - Py_DECREF(c_api); - if (PyErr_Occurred()) { - goto err; - } - - /* Load the ufunc operators into the array module's namespace */ - if (InitOperators(d) < 0) { - goto err; - } - PyDict_SetItemString(d, "pi", s = PyFloat_FromDouble(NPY_PI)); Py_DECREF(s); PyDict_SetItemString(d, "e", s = PyFloat_FromDouble(NPY_E)); @@ -383,33 +288,51 @@ PyMODINIT_FUNC initumath(void) PyModule_AddObject(m, "NZERO", PyFloat_FromDouble(NPY_NZERO)); PyModule_AddObject(m, "NAN", PyFloat_FromDouble(NPY_NAN)); -#if defined(NPY_PY3K) s = PyDict_GetItemString(d, "true_divide"); PyDict_SetItemString(d, "divide", s); -#endif s = PyDict_GetItemString(d, "conjugate"); s2 = PyDict_GetItemString(d, "remainder"); /* Setup the array object's numerical structures with appropriate ufuncs in d*/ - PyArray_SetNumericOps(d); + _PyArray_SetNumericOps(d); PyDict_SetItemString(d, "conj", s); PyDict_SetItemString(d, "mod", s2); - initscalarmath(m); + if (intern_strings() < 0) { + PyErr_SetString(PyExc_RuntimeError, + "cannot intern umath strings while initializing _multiarray_umath."); + return -1; + } - if (!intern_strings()) { - goto err; + /* + * Set up promoters for logical functions + * TODO: This should probably be done at a better place, or even in the + * code generator directly. + */ + s = _PyDict_GetItemStringWithError(d, "logical_and"); + if (s == NULL) { + return -1; + } + if (install_logical_ufunc_promoter(s) < 0) { + return -1; } - return RETVAL(m); + s = _PyDict_GetItemStringWithError(d, "logical_or"); + if (s == NULL) { + return -1; + } + if (install_logical_ufunc_promoter(s) < 0) { + return -1; + } - err: - /* Check for errors */ - if (!PyErr_Occurred()) { - PyErr_SetString(PyExc_RuntimeError, - "cannot load umath module."); + s = _PyDict_GetItemStringWithError(d, "logical_xor"); + if (s == NULL) { + return -1; + } + if (install_logical_ufunc_promoter(s) < 0) { + return -1; } - return RETVAL(NULL); + return 0; } diff --git a/numpy/core/tests/_locales.py b/numpy/core/tests/_locales.py index 28eebb14d29f..ce7b81f001b9 100644 --- a/numpy/core/tests/_locales.py +++ b/numpy/core/tests/_locales.py @@ -1,12 +1,10 @@ """Provide class for testing in French locale """ -from __future__ import division, absolute_import, print_function - import sys import locale -from numpy.testing import SkipTest +import pytest __ALL__ = ['CommaDecimalPointLocale'] @@ -45,14 +43,14 @@ def find_comma_decimal_point_locale(): return old_locale, new_locale -class CommaDecimalPointLocale(object): +class CommaDecimalPointLocale: """Sets LC_NUMERIC to a locale with comma as decimal point. Classes derived from this class have setup and teardown methods that run tests with locale.LC_NUMERIC set to a locale where commas (',') are used as the decimal point instead of periods ('.'). On exit the locale is restored to the initial locale. It also serves as context manager with the same - effect. If no such locale is available, it raises SkipTest in both cases. + effect. If no such locale is available, the test is skipped. .. versionadded:: 1.15.0 @@ -61,7 +59,7 @@ class CommaDecimalPointLocale(object): def setup(self): if self.tst_locale is None: - raise SkipTest("No French locale available") + pytest.skip("No French locale available") locale.setlocale(locale.LC_NUMERIC, locale=self.tst_locale) def teardown(self): @@ -69,7 +67,7 @@ def teardown(self): def __enter__(self): if self.tst_locale is None: - raise SkipTest("No French locale available") + pytest.skip("No French locale available") locale.setlocale(locale.LC_NUMERIC, locale=self.tst_locale) def __exit__(self, type, value, traceback): diff --git a/numpy/core/tests/data/generate_umath_validation_data.cpp b/numpy/core/tests/data/generate_umath_validation_data.cpp new file mode 100644 index 000000000000..418eae67006f --- /dev/null +++ b/numpy/core/tests/data/generate_umath_validation_data.cpp @@ -0,0 +1,170 @@ +#include <algorithm> +#include <fstream> +#include <iostream> +#include <math.h> +#include <random> +#include <stdio.h> +#include <time.h> +#include <vector> + +struct ufunc { + std::string name; + double (*f32func)(double); + long double (*f64func)(long double); + float f32ulp; + float f64ulp; +}; + +template <typename T> +T +RandomFloat(T a, T b) +{ + T random = ((T)rand()) / (T)RAND_MAX; + T diff = b - a; + T r = random * diff; + return a + r; +} + +template <typename T> +void +append_random_array(std::vector<T> &arr, T min, T max, size_t N) +{ + for (size_t ii = 0; ii < N; ++ii) + arr.emplace_back(RandomFloat<T>(min, max)); +} + +template <typename T1, typename T2> +std::vector<T1> +computeTrueVal(const std::vector<T1> &in, T2 (*mathfunc)(T2)) +{ + std::vector<T1> out; + for (T1 elem : in) { + T2 elem_d = (T2)elem; + T1 out_elem = (T1)mathfunc(elem_d); + out.emplace_back(out_elem); + } + return out; +} + +/* + * FP range: + * [-inf, -maxflt, -1., -minflt, -minden, 0., minden, minflt, 1., maxflt, inf] + */ + +#define MINDEN std::numeric_limits<T>::denorm_min() +#define MINFLT std::numeric_limits<T>::min() +#define MAXFLT std::numeric_limits<T>::max() +#define INF std::numeric_limits<T>::infinity() +#define qNAN std::numeric_limits<T>::quiet_NaN() +#define sNAN std::numeric_limits<T>::signaling_NaN() + +template <typename T> +std::vector<T> +generate_input_vector(std::string func) +{ + std::vector<T> input = {MINDEN, -MINDEN, MINFLT, -MINFLT, MAXFLT, + -MAXFLT, INF, -INF, qNAN, sNAN, + -1.0, 1.0, 0.0, -0.0}; + + // [-1.0, 1.0] + if ((func == "arcsin") || (func == "arccos") || (func == "arctanh")) { + append_random_array<T>(input, -1.0, 1.0, 700); + } + // (0.0, INF] + else if ((func == "log2") || (func == "log10")) { + append_random_array<T>(input, 0.0, 1.0, 200); + append_random_array<T>(input, MINDEN, MINFLT, 200); + append_random_array<T>(input, MINFLT, 1.0, 200); + append_random_array<T>(input, 1.0, MAXFLT, 200); + } + // (-1.0, INF] + else if (func == "log1p") { + append_random_array<T>(input, -1.0, 1.0, 200); + append_random_array<T>(input, -MINFLT, -MINDEN, 100); + append_random_array<T>(input, -1.0, -MINFLT, 100); + append_random_array<T>(input, MINDEN, MINFLT, 100); + append_random_array<T>(input, MINFLT, 1.0, 100); + append_random_array<T>(input, 1.0, MAXFLT, 100); + } + // [1.0, INF] + else if (func == "arccosh") { + append_random_array<T>(input, 1.0, 2.0, 400); + append_random_array<T>(input, 2.0, MAXFLT, 300); + } + // [-INF, INF] + else { + append_random_array<T>(input, -1.0, 1.0, 100); + append_random_array<T>(input, MINDEN, MINFLT, 100); + append_random_array<T>(input, -MINFLT, -MINDEN, 100); + append_random_array<T>(input, MINFLT, 1.0, 100); + append_random_array<T>(input, -1.0, -MINFLT, 100); + append_random_array<T>(input, 1.0, MAXFLT, 100); + append_random_array<T>(input, -MAXFLT, -100.0, 100); + } + + std::random_shuffle(input.begin(), input.end()); + return input; +} + +int +main() +{ + srand(42); + std::vector<struct ufunc> umathfunc = { + {"sin", sin, sin, 2.37, 3.3}, + {"cos", cos, cos, 2.36, 3.38}, + {"tan", tan, tan, 3.91, 3.93}, + {"arcsin", asin, asin, 3.12, 2.55}, + {"arccos", acos, acos, 2.1, 1.67}, + {"arctan", atan, atan, 2.3, 2.52}, + {"sinh", sinh, sinh, 1.55, 1.89}, + {"cosh", cosh, cosh, 2.48, 1.97}, + {"tanh", tanh, tanh, 1.38, 1.19}, + {"arcsinh", asinh, asinh, 1.01, 1.48}, + {"arccosh", acosh, acosh, 1.16, 1.05}, + {"arctanh", atanh, atanh, 1.45, 1.46}, + {"cbrt", cbrt, cbrt, 1.94, 1.82}, + //{"exp",exp,exp,3.76,1.53}, + {"exp2", exp2, exp2, 1.01, 1.04}, + {"expm1", expm1, expm1, 2.62, 2.1}, + //{"log",log,log,1.84,1.67}, + {"log10", log10, log10, 3.5, 1.92}, + {"log1p", log1p, log1p, 1.96, 1.93}, + {"log2", log2, log2, 2.12, 1.84}, + }; + + for (int ii = 0; ii < umathfunc.size(); ++ii) { + // ignore sin/cos + if ((umathfunc[ii].name != "sin") && (umathfunc[ii].name != "cos")) { + std::string fileName = + "umath-validation-set-" + umathfunc[ii].name + ".csv"; + std::ofstream txtOut; + txtOut.open(fileName, std::ofstream::trunc); + txtOut << "dtype,input,output,ulperrortol" << std::endl; + + // Single Precision + auto f32in = generate_input_vector<float>(umathfunc[ii].name); + auto f32out = computeTrueVal<float, double>(f32in, + umathfunc[ii].f32func); + for (int jj = 0; jj < f32in.size(); ++jj) { + txtOut << "np.float32" << std::hex << ",0x" + << *reinterpret_cast<uint32_t *>(&f32in[jj]) << ",0x" + << *reinterpret_cast<uint32_t *>(&f32out[jj]) << "," + << ceil(umathfunc[ii].f32ulp) << std::endl; + } + + // Double Precision + auto f64in = generate_input_vector<double>(umathfunc[ii].name); + auto f64out = computeTrueVal<double, long double>( + f64in, umathfunc[ii].f64func); + for (int jj = 0; jj < f64in.size(); ++jj) { + txtOut << "np.float64" << std::hex << ",0x" + << *reinterpret_cast<uint64_t *>(&f64in[jj]) << ",0x" + << *reinterpret_cast<uint64_t *>(&f64out[jj]) << "," + << ceil(umathfunc[ii].f64ulp) << std::endl; + } + txtOut.close(); + } + } + return 0; +} diff --git a/numpy/core/tests/data/umath-validation-set-README.txt b/numpy/core/tests/data/umath-validation-set-README.txt new file mode 100644 index 000000000000..cfc9e4145d10 --- /dev/null +++ b/numpy/core/tests/data/umath-validation-set-README.txt @@ -0,0 +1,15 @@ +Steps to validate transcendental functions: +1) Add a file 'umath-validation-set-<ufuncname>.txt', where ufuncname is name of + the function in NumPy you want to validate +2) The file should contain 4 columns: dtype,input,expected output,ulperror + a. dtype: one of np.float16, np.float32, np.float64 + b. input: floating point input to ufunc in hex. Example: 0x414570a4 + represents 12.340000152587890625 + c. expected output: floating point output for the corresponding input in hex. + This should be computed using a high(er) precision library and then rounded to + same format as the input. + d. ulperror: expected maximum ulp error of the function. This + should be same across all rows of the same dtype. Otherwise, the function is + tested for the maximum ulp error among all entries of that dtype. +3) Add file umath-validation-set-<ufuncname>.txt to the test file test_umath_accuracy.py + which will then validate your ufunc. diff --git a/numpy/core/tests/data/umath-validation-set-arccos.csv b/numpy/core/tests/data/umath-validation-set-arccos.csv new file mode 100644 index 000000000000..6697ae9561f3 --- /dev/null +++ b/numpy/core/tests/data/umath-validation-set-arccos.csv @@ -0,0 +1,1429 @@ +dtype,input,output,ulperrortol +np.float32,0xbddd7f50,0x3fd6eec2,3 +np.float32,0xbe32a20c,0x3fdf8182,3 +np.float32,0xbf607c09,0x4028f84f,3 +np.float32,0x3f25d906,0x3f5db544,3 +np.float32,0x3f01cec8,0x3f84febf,3 +np.float32,0x3f1d5c6e,0x3f68a735,3 +np.float32,0xbf0cab89,0x4009c36d,3 +np.float32,0xbf176b40,0x400d0941,3 +np.float32,0x3f3248b2,0x3f4ce6d4,3 +np.float32,0x3f390b48,0x3f434e0d,3 +np.float32,0xbe261698,0x3fddea43,3 +np.float32,0x3f0e1154,0x3f7b848b,3 +np.float32,0xbf379a3c,0x4017b764,3 +np.float32,0xbeda6f2c,0x4000bd62,3 +np.float32,0xbf6a0c3f,0x402e5d5a,3 +np.float32,0x3ef1d700,0x3f8a17b7,3 +np.float32,0xbf6f4f65,0x4031d30d,3 +np.float32,0x3f2c9eee,0x3f54adfd,3 +np.float32,0x3f3cfb18,0x3f3d8a1e,3 +np.float32,0x3ba80800,0x3fc867d2,3 +np.float32,0x3e723b08,0x3faa7e4d,3 +np.float32,0xbf65820f,0x402bb054,3 +np.float32,0xbee64e7a,0x40026410,3 +np.float32,0x3cb15140,0x3fc64a87,3 +np.float32,0x3f193660,0x3f6ddf2a,3 +np.float32,0xbf0e5b52,0x400a44f7,3 +np.float32,0x3ed55f14,0x3f920a4b,3 +np.float32,0x3dd11a80,0x3fbbf85c,3 +np.float32,0xbf4f5c4b,0x4020f4f9,3 +np.float32,0x3f787532,0x3e792e87,3 +np.float32,0x3f40e6ac,0x3f37a74f,3 +np.float32,0x3f1c1318,0x3f6a47b6,3 +np.float32,0xbe3c48d8,0x3fe0bb70,3 +np.float32,0xbe94d4bc,0x3feed08e,3 +np.float32,0xbe5c3688,0x3fe4ce26,3 +np.float32,0xbf6fe026,0x403239cb,3 +np.float32,0x3ea5983c,0x3f9ee7bf,3 +np.float32,0x3f1471e6,0x3f73c5bb,3 +np.float32,0x3f0e2622,0x3f7b6b87,3 +np.float32,0xbf597180,0x40257ad1,3 +np.float32,0xbeb5321c,0x3ff75d34,3 +np.float32,0x3f5afcd2,0x3f0b6012,3 +np.float32,0xbef2ff88,0x40042e14,3 +np.float32,0xbedc747e,0x400104f5,3 +np.float32,0xbee0c2f4,0x40019dfc,3 +np.float32,0xbf152cd8,0x400c57dc,3 +np.float32,0xbf6cf9e2,0x40303bbe,3 +np.float32,0x3ed9cd74,0x3f90d1a1,3 +np.float32,0xbf754406,0x4036767f,3 +np.float32,0x3f59c5c2,0x3f0db42f,3 +np.float32,0x3f2eefd8,0x3f518684,3 +np.float32,0xbf156bf9,0x400c6b49,3 +np.float32,0xbd550790,0x3fcfb8dc,3 +np.float32,0x3ede58fc,0x3f8f8f77,3 +np.float32,0xbf00ac19,0x40063c4b,3 +np.float32,0x3f4d25ba,0x3f24280e,3 +np.float32,0xbe9568be,0x3feef73c,3 +np.float32,0x3f67d154,0x3ee05547,3 +np.float32,0x3f617226,0x3efcb4f4,3 +np.float32,0xbf3ab41a,0x4018d6cc,3 +np.float32,0xbf3186fe,0x401592cd,3 +np.float32,0x3de3ba50,0x3fbacca9,3 +np.float32,0x3e789f98,0x3fa9ab97,3 +np.float32,0x3f016e08,0x3f8536d8,3 +np.float32,0x3e8b618c,0x3fa5c571,3 +np.float32,0x3eff97bc,0x3f8628a9,3 +np.float32,0xbf6729f0,0x402ca32f,3 +np.float32,0xbebec146,0x3ff9eddc,3 +np.float32,0x3ddb2e60,0x3fbb563a,3 +np.float32,0x3caa8e40,0x3fc66595,3 +np.float32,0xbf5973f2,0x40257bfa,3 +np.float32,0xbdd82c70,0x3fd69916,3 +np.float32,0xbedf4c82,0x400169ef,3 +np.float32,0x3ef8f22c,0x3f881184,3 +np.float32,0xbf1d74d4,0x400eedc9,3 +np.float32,0x3f2e10a6,0x3f52b790,3 +np.float32,0xbf08ecc0,0x4008a628,3 +np.float32,0x3ecb7db4,0x3f94be9f,3 +np.float32,0xbf052ded,0x40078bfc,3 +np.float32,0x3f2ee78a,0x3f5191e4,3 +np.float32,0xbf56f4e1,0x40245194,3 +np.float32,0x3f600a3e,0x3f014a25,3 +np.float32,0x3f3836f8,0x3f44808b,3 +np.float32,0x3ecabfbc,0x3f94f25c,3 +np.float32,0x3c70f500,0x3fc72dec,3 +np.float32,0x3f17c444,0x3f6fabf0,3 +np.float32,0xbf4c22a5,0x401f9a09,3 +np.float32,0xbe4205dc,0x3fe1765a,3 +np.float32,0x3ea49138,0x3f9f2d36,3 +np.float32,0xbece0082,0x3ffe106b,3 +np.float32,0xbe387578,0x3fe03eef,3 +np.float32,0xbf2b6466,0x40137a30,3 +np.float32,0xbe9dadb2,0x3ff12204,3 +np.float32,0xbf56b3f2,0x402433bb,3 +np.float32,0xbdf9b4d8,0x3fd8b51f,3 +np.float32,0x3f58a596,0x3f0fd4b4,3 +np.float32,0xbedf5748,0x40016b6e,3 +np.float32,0x3f446442,0x3f32476f,3 +np.float32,0x3f5be886,0x3f099658,3 +np.float32,0x3ea1e44c,0x3f9fe1de,3 +np.float32,0xbf11e9b8,0x400b585f,3 +np.float32,0xbf231f8f,0x4010befb,3 +np.float32,0xbf4395ea,0x401c2dd0,3 +np.float32,0x3e9e7784,0x3fa0c8a6,3 +np.float32,0xbe255184,0x3fddd14c,3 +np.float32,0x3f70d25e,0x3eb13148,3 +np.float32,0x3f220cdc,0x3f62a722,3 +np.float32,0xbd027bf0,0x3fcd23e7,3 +np.float32,0x3e4ef8b8,0x3faf02d2,3 +np.float32,0xbf76fc6b,0x40380728,3 +np.float32,0xbf57e761,0x4024c1cd,3 +np.float32,0x3ed4fc20,0x3f922580,3 +np.float32,0xbf09b64a,0x4008e1db,3 +np.float32,0x3f21ca62,0x3f62fcf5,3 +np.float32,0xbe55f610,0x3fe40170,3 +np.float32,0xbc0def80,0x3fca2bbb,3 +np.float32,0xbebc8764,0x3ff9547b,3 +np.float32,0x3ec1b200,0x3f9766d1,3 +np.float32,0xbf4ee44e,0x4020c1ee,3 +np.float32,0xbea85852,0x3ff3f22a,3 +np.float32,0xbf195c0c,0x400da3d3,3 +np.float32,0xbf754b5d,0x40367ce8,3 +np.float32,0xbdcbfe50,0x3fd5d52b,3 +np.float32,0xbf1adb87,0x400e1be3,3 +np.float32,0xbf6f8491,0x4031f898,3 +np.float32,0xbf6f9ae7,0x4032086e,3 +np.float32,0xbf52b3f0,0x40226790,3 +np.float32,0xbf698452,0x402e09f4,3 +np.float32,0xbf43dc9a,0x401c493a,3 +np.float32,0xbf165f7f,0x400cb664,3 +np.float32,0x3e635468,0x3fac682f,3 +np.float32,0xbe8cf2b6,0x3fecc28a,3 +np.float32,0x7f7fffff,0x7fc00000,3 +np.float32,0xbf4c6513,0x401fb597,3 +np.float32,0xbf02b8f8,0x4006d47e,3 +np.float32,0x3ed3759c,0x3f9290c8,3 +np.float32,0xbf2a7a5f,0x40132b98,3 +np.float32,0xbae65000,0x3fc9496f,3 +np.float32,0x3f65f5ea,0x3ee8ef07,3 +np.float32,0xbe7712fc,0x3fe84106,3 +np.float32,0xbb9ff700,0x3fc9afd2,3 +np.float32,0x3d8d87a0,0x3fc03592,3 +np.float32,0xbefc921c,0x40058c23,3 +np.float32,0xbf286566,0x401279d8,3 +np.float32,0x3f53857e,0x3f192eaf,3 +np.float32,0xbee9b0f4,0x4002dd90,3 +np.float32,0x3f4041f8,0x3f38a14a,3 +np.float32,0x3f54ea96,0x3f16b02d,3 +np.float32,0x3ea50ef8,0x3f9f0c01,3 +np.float32,0xbeaad2dc,0x3ff49a4a,3 +np.float32,0xbec428c8,0x3ffb636f,3 +np.float32,0xbda46178,0x3fd358c7,3 +np.float32,0xbefacfc4,0x40054b7f,3 +np.float32,0xbf7068f9,0x40329c85,3 +np.float32,0x3f70b850,0x3eb1caa7,3 +np.float32,0x7fa00000,0x7fe00000,3 +np.float32,0x80000000,0x3fc90fdb,3 +np.float32,0x3f68d5c8,0x3edb7cf3,3 +np.float32,0x3d9443d0,0x3fbfc98a,3 +np.float32,0xff7fffff,0x7fc00000,3 +np.float32,0xbeee7ba8,0x40038a5e,3 +np.float32,0xbf0aaaba,0x40092a73,3 +np.float32,0x3f36a4e8,0x3f46c0ee,3 +np.float32,0x3ed268e4,0x3f92da82,3 +np.float32,0xbee6002c,0x4002591b,3 +np.float32,0xbe8f2752,0x3fed5576,3 +np.float32,0x3f525912,0x3f1b40e0,3 +np.float32,0xbe8e151e,0x3fed0e16,3 +np.float32,0x1,0x3fc90fdb,3 +np.float32,0x3ee23b84,0x3f8e7ae1,3 +np.float32,0xbf5961ca,0x40257361,3 +np.float32,0x3f6bbca0,0x3ecd14cd,3 +np.float32,0x3e27b230,0x3fb4014d,3 +np.float32,0xbf183bb8,0x400d49fc,3 +np.float32,0x3f57759c,0x3f120b68,3 +np.float32,0xbd6994c0,0x3fd05d84,3 +np.float32,0xbf1dd684,0x400f0cc8,3 +np.float32,0xbececc1c,0x3ffe480a,3 +np.float32,0xbf48855f,0x401e206d,3 +np.float32,0x3f28c922,0x3f59d382,3 +np.float32,0xbf65c094,0x402bd3b0,3 +np.float32,0x3f657d42,0x3eeb11dd,3 +np.float32,0xbed32d4e,0x3fff7b15,3 +np.float32,0xbf31af02,0x4015a0b1,3 +np.float32,0x3d89eb00,0x3fc06f7f,3 +np.float32,0x3dac2830,0x3fbe4a17,3 +np.float32,0x3f7f7cb6,0x3d81a7df,3 +np.float32,0xbedbb570,0x4000ea82,3 +np.float32,0x3db37830,0x3fbdd4a8,3 +np.float32,0xbf376f48,0x4017a7fd,3 +np.float32,0x3f319f12,0x3f4dd2c9,3 +np.float32,0x7fc00000,0x7fc00000,3 +np.float32,0x3f1b4f70,0x3f6b3e31,3 +np.float32,0x3e33c880,0x3fb278d1,3 +np.float32,0x3f2796e0,0x3f5b69bd,3 +np.float32,0x3f4915d6,0x3f2ad4d0,3 +np.float32,0x3e4db120,0x3faf2ca0,3 +np.float32,0x3ef03dd4,0x3f8a8ba9,3 +np.float32,0x3e96ca88,0x3fa2cbf7,3 +np.float32,0xbeb136ce,0x3ff64d2b,3 +np.float32,0xbf2f3938,0x4014c75e,3 +np.float32,0x3f769dde,0x3e8b0d76,3 +np.float32,0x3f67cec8,0x3ee06148,3 +np.float32,0x3f0a1ade,0x3f80204e,3 +np.float32,0x3e4b9718,0x3faf7144,3 +np.float32,0x3cccb480,0x3fc5dcf3,3 +np.float32,0x3caeb740,0x3fc654f0,3 +np.float32,0x3f684e0e,0x3ede0678,3 +np.float32,0x3f0ba93c,0x3f7e6663,3 +np.float32,0xbf12bbc4,0x400b985e,3 +np.float32,0xbf2a8e1a,0x40133235,3 +np.float32,0x3f42029c,0x3f35f5c5,3 +np.float32,0x3eed1728,0x3f8b6f9c,3 +np.float32,0xbe5779ac,0x3fe432fd,3 +np.float32,0x3f6ed8b8,0x3ebc7e4b,3 +np.float32,0x3eea25b0,0x3f8c43c7,3 +np.float32,0x3f1988a4,0x3f6d786b,3 +np.float32,0xbe751674,0x3fe7ff8a,3 +np.float32,0xbe9f7418,0x3ff1997d,3 +np.float32,0x3dca11d0,0x3fbc6979,3 +np.float32,0x3f795226,0x3e6a6cab,3 +np.float32,0xbea780e0,0x3ff3b926,3 +np.float32,0xbed92770,0x4000901e,3 +np.float32,0xbf3e9f8c,0x401a49f8,3 +np.float32,0x3f0f7054,0x3f79ddb2,3 +np.float32,0x3a99d400,0x3fc8e966,3 +np.float32,0xbef082b0,0x4003d3c6,3 +np.float32,0xbf0d0790,0x4009defb,3 +np.float32,0xbf1649da,0x400cafb4,3 +np.float32,0xbea5aca8,0x3ff33d5c,3 +np.float32,0xbf4e1843,0x40206ba1,3 +np.float32,0xbe3d7d5c,0x3fe0e2ad,3 +np.float32,0xbf0e802d,0x400a500e,3 +np.float32,0xbf0de8f0,0x400a2295,3 +np.float32,0xbf3016ba,0x4015137e,3 +np.float32,0x3f36b1ea,0x3f46ae5d,3 +np.float32,0xbd27f170,0x3fce4fc7,3 +np.float32,0x3e96ec54,0x3fa2c31f,3 +np.float32,0x3eb4dfdc,0x3f9ad87d,3 +np.float32,0x3f5cac6c,0x3f0815cc,3 +np.float32,0xbf0489aa,0x40075bf1,3 +np.float32,0x3df010c0,0x3fba05f5,3 +np.float32,0xbf229f4a,0x4010956a,3 +np.float32,0x3f75e474,0x3e905a99,3 +np.float32,0xbcece6a0,0x3fccc397,3 +np.float32,0xbdb41528,0x3fd454e7,3 +np.float32,0x3ec8b2f8,0x3f958118,3 +np.float32,0x3f5eaa70,0x3f041a1d,3 +np.float32,0xbf32e1cc,0x40160b91,3 +np.float32,0xbe8e6026,0x3fed219c,3 +np.float32,0x3e6b3160,0x3fab65e3,3 +np.float32,0x3e6d7460,0x3fab1b81,3 +np.float32,0xbf13fbde,0x400bfa3b,3 +np.float32,0xbe8235ec,0x3fe9f9e3,3 +np.float32,0x3d71c4a0,0x3fc18096,3 +np.float32,0x3eb769d0,0x3f9a2aa0,3 +np.float32,0xbf68cb3b,0x402d99e4,3 +np.float32,0xbd917610,0x3fd22932,3 +np.float32,0x3d3cba60,0x3fc3297f,3 +np.float32,0xbf383cbe,0x4017f1cc,3 +np.float32,0xbeee96d0,0x40038e34,3 +np.float32,0x3ec89cb4,0x3f958725,3 +np.float32,0x3ebf92d8,0x3f97f95f,3 +np.float32,0x3f30f3da,0x3f4ec021,3 +np.float32,0xbd26b560,0x3fce45e4,3 +np.float32,0xbec0eb12,0x3ffa8330,3 +np.float32,0x3f6d592a,0x3ec4a6c1,3 +np.float32,0x3ea6d39c,0x3f9e9463,3 +np.float32,0x3e884184,0x3fa6951e,3 +np.float32,0x3ea566c4,0x3f9ef4d1,3 +np.float32,0x3f0c8f4c,0x3f7d5380,3 +np.float32,0x3f28e1ba,0x3f59b2cb,3 +np.float32,0x3f798538,0x3e66e1c3,3 +np.float32,0xbe2889b8,0x3fde39b8,3 +np.float32,0x3f3da05e,0x3f3c949c,3 +np.float32,0x3f24d700,0x3f5f073e,3 +np.float32,0xbe5b5768,0x3fe4b198,3 +np.float32,0xbed3b03a,0x3fff9f05,3 +np.float32,0x3e8a1c4c,0x3fa619eb,3 +np.float32,0xbf075d24,0x40083030,3 +np.float32,0x3f765648,0x3e8d1f52,3 +np.float32,0xbf70fc5e,0x403308bb,3 +np.float32,0x3f557ae8,0x3f15ab76,3 +np.float32,0x3f02f7ea,0x3f84521c,3 +np.float32,0x3f7ebbde,0x3dcbc5c5,3 +np.float32,0xbefbdfc6,0x40057285,3 +np.float32,0x3ec687ac,0x3f9617d9,3 +np.float32,0x3e4831c8,0x3fafe01b,3 +np.float32,0x3e25cde0,0x3fb43ea8,3 +np.float32,0x3e4f2ab8,0x3faefc70,3 +np.float32,0x3ea60ae4,0x3f9ec973,3 +np.float32,0xbf1ed55f,0x400f5dde,3 +np.float32,0xbf5ad4aa,0x40262479,3 +np.float32,0x3e8b3594,0x3fa5d0de,3 +np.float32,0x3f3a77aa,0x3f413c80,3 +np.float32,0xbf07512b,0x40082ca9,3 +np.float32,0x3f33d990,0x3f4ab5e5,3 +np.float32,0x3f521556,0x3f1bb78f,3 +np.float32,0xbecf6036,0x3ffe7086,3 +np.float32,0x3db91bd0,0x3fbd7a11,3 +np.float32,0x3ef63a74,0x3f88d839,3 +np.float32,0xbf2f1116,0x4014b99c,3 +np.float32,0xbf17fdc0,0x400d36b9,3 +np.float32,0xbe87df2c,0x3feb7117,3 +np.float32,0x80800000,0x3fc90fdb,3 +np.float32,0x3ee24c1c,0x3f8e7641,3 +np.float32,0x3f688dce,0x3edcd644,3 +np.float32,0xbf0f4e1c,0x400a8e1b,3 +np.float32,0x0,0x3fc90fdb,3 +np.float32,0x3f786eba,0x3e7999d4,3 +np.float32,0xbf404f80,0x401aeca8,3 +np.float32,0xbe9ffb6a,0x3ff1bd18,3 +np.float32,0x3f146bfc,0x3f73ccfd,3 +np.float32,0xbe47d630,0x3fe233ee,3 +np.float32,0xbe95847c,0x3feefe7c,3 +np.float32,0xbf135df0,0x400bc9e5,3 +np.float32,0x3ea19f3c,0x3f9ff411,3 +np.float32,0x3f235e20,0x3f60f247,3 +np.float32,0xbec789ec,0x3ffc4def,3 +np.float32,0x3f04b656,0x3f834db6,3 +np.float32,0x3dfaf440,0x3fb95679,3 +np.float32,0xbe4a7f28,0x3fe28abe,3 +np.float32,0x3ed4850c,0x3f92463b,3 +np.float32,0x3ec4ba5c,0x3f9694dd,3 +np.float32,0xbce24ca0,0x3fcc992b,3 +np.float32,0xbf5b7c6e,0x402675a0,3 +np.float32,0xbea3ce2a,0x3ff2bf04,3 +np.float32,0x3db02c60,0x3fbe0998,3 +np.float32,0x3c47b780,0x3fc78069,3 +np.float32,0x3ed33b20,0x3f92a0d5,3 +np.float32,0xbf4556d7,0x401cdcde,3 +np.float32,0xbe1b6e28,0x3fdc90ec,3 +np.float32,0xbf3289b7,0x4015ecd0,3 +np.float32,0x3df3f240,0x3fb9c76d,3 +np.float32,0x3eefa7d0,0x3f8ab61d,3 +np.float32,0xbe945838,0x3feeb006,3 +np.float32,0xbf0b1386,0x400949a3,3 +np.float32,0x3f77e546,0x3e812cc1,3 +np.float32,0x3e804ba0,0x3fa8a480,3 +np.float32,0x3f43dcea,0x3f331a06,3 +np.float32,0x3eb87450,0x3f99e33c,3 +np.float32,0x3e5f4898,0x3facecea,3 +np.float32,0x3f646640,0x3eeff10e,3 +np.float32,0x3f1aa832,0x3f6c1051,3 +np.float32,0xbebf6bfa,0x3ffa1bdc,3 +np.float32,0xbb77f300,0x3fc98bd4,3 +np.float32,0x3f3587fe,0x3f485645,3 +np.float32,0x3ef85f34,0x3f883b8c,3 +np.float32,0x3f50e584,0x3f1dc82c,3 +np.float32,0x3f1d30a8,0x3f68deb0,3 +np.float32,0x3ee75a78,0x3f8d0c86,3 +np.float32,0x3f2c023a,0x3f5581e1,3 +np.float32,0xbf074e34,0x40082bca,3 +np.float32,0xbead71f0,0x3ff54c6d,3 +np.float32,0xbf39ed88,0x40188e69,3 +np.float32,0x3f5d2fe6,0x3f07118b,3 +np.float32,0xbf1f79f8,0x400f9267,3 +np.float32,0x3e900c58,0x3fa48e99,3 +np.float32,0xbf759cb2,0x4036c47b,3 +np.float32,0x3f63329c,0x3ef5359c,3 +np.float32,0xbf5d6755,0x40276709,3 +np.float32,0x3f2ce31c,0x3f54519a,3 +np.float32,0x7f800000,0x7fc00000,3 +np.float32,0x3f1bf50e,0x3f6a6d9a,3 +np.float32,0x3f258334,0x3f5e25d8,3 +np.float32,0xbf661a3f,0x402c06ac,3 +np.float32,0x3d1654c0,0x3fc45cef,3 +np.float32,0xbef14a36,0x4003f009,3 +np.float32,0xbf356051,0x4016ec3a,3 +np.float32,0x3f6ccc42,0x3ec79193,3 +np.float32,0xbf2fe3d6,0x401501f9,3 +np.float32,0x3deedc80,0x3fba195b,3 +np.float32,0x3f2e5a28,0x3f52533e,3 +np.float32,0x3e6b68b8,0x3fab5ec8,3 +np.float32,0x3e458240,0x3fb037b7,3 +np.float32,0xbf24bab0,0x401144cb,3 +np.float32,0x3f600f4c,0x3f013fb2,3 +np.float32,0x3f021a04,0x3f84d316,3 +np.float32,0x3f741732,0x3e9cc948,3 +np.float32,0x3f0788aa,0x3f81a5b0,3 +np.float32,0x3f28802c,0x3f5a347c,3 +np.float32,0x3c9eb400,0x3fc69500,3 +np.float32,0x3e5d11e8,0x3fad357a,3 +np.float32,0x3d921250,0x3fbfecb9,3 +np.float32,0x3f354866,0x3f48b066,3 +np.float32,0xbf72cf43,0x40346d84,3 +np.float32,0x3eecdbb8,0x3f8b805f,3 +np.float32,0xbee585d0,0x400247fd,3 +np.float32,0x3e3607a8,0x3fb22fc6,3 +np.float32,0xbf0cb7d6,0x4009c71c,3 +np.float32,0xbf56b230,0x402432ec,3 +np.float32,0xbf4ced02,0x401fee29,3 +np.float32,0xbf3a325c,0x4018a776,3 +np.float32,0x3ecae8bc,0x3f94e732,3 +np.float32,0xbe48c7e8,0x3fe252bd,3 +np.float32,0xbe175d7c,0x3fdc0d5b,3 +np.float32,0x3ea78dac,0x3f9e632d,3 +np.float32,0xbe7434a8,0x3fe7e279,3 +np.float32,0x3f1f9e02,0x3f65c7b9,3 +np.float32,0xbe150f2c,0x3fdbc2c2,3 +np.float32,0x3ee13480,0x3f8ec423,3 +np.float32,0x3ecb7d54,0x3f94beb9,3 +np.float32,0x3f1cef42,0x3f693181,3 +np.float32,0xbf1ec06a,0x400f5730,3 +np.float32,0xbe112acc,0x3fdb44e8,3 +np.float32,0xbe77b024,0x3fe85545,3 +np.float32,0x3ec86fe0,0x3f959353,3 +np.float32,0x3f36b326,0x3f46ac9a,3 +np.float32,0x3e581a70,0x3fadd829,3 +np.float32,0xbf032c0c,0x4006f5f9,3 +np.float32,0xbf43b1fd,0x401c38b1,3 +np.float32,0x3f3701b4,0x3f463c5c,3 +np.float32,0x3f1a995a,0x3f6c22f1,3 +np.float32,0xbf05de0b,0x4007bf97,3 +np.float32,0x3d4bd960,0x3fc2b063,3 +np.float32,0x3f0e1618,0x3f7b7ed0,3 +np.float32,0x3edfd420,0x3f8f2628,3 +np.float32,0xbf6662fe,0x402c3047,3 +np.float32,0x3ec0690c,0x3f97bf9b,3 +np.float32,0xbeaf4146,0x3ff5c7a0,3 +np.float32,0x3f5e7764,0x3f04816d,3 +np.float32,0xbedd192c,0x40011bc5,3 +np.float32,0x3eb76350,0x3f9a2c5e,3 +np.float32,0xbed8108c,0x400069a5,3 +np.float32,0xbe59f31c,0x3fe48401,3 +np.float32,0xbea3e1e6,0x3ff2c439,3 +np.float32,0x3e26d1f8,0x3fb41db5,3 +np.float32,0x3f3a0a7c,0x3f41dba5,3 +np.float32,0x3ebae068,0x3f993ce4,3 +np.float32,0x3f2d8e30,0x3f536942,3 +np.float32,0xbe838bbe,0x3fea5247,3 +np.float32,0x3ebe4420,0x3f98538f,3 +np.float32,0xbcc59b80,0x3fcc265c,3 +np.float32,0x3eebb5c8,0x3f8bd334,3 +np.float32,0xbafc3400,0x3fc94ee8,3 +np.float32,0xbf63ddc1,0x402ac683,3 +np.float32,0xbeabdf80,0x3ff4e18f,3 +np.float32,0x3ea863f0,0x3f9e2a78,3 +np.float32,0x3f45b292,0x3f303bc1,3 +np.float32,0xbe68aa60,0x3fe666bf,3 +np.float32,0x3eb9de18,0x3f998239,3 +np.float32,0xbf719d85,0x4033815e,3 +np.float32,0x3edef9a8,0x3f8f62db,3 +np.float32,0xbd7781c0,0x3fd0cd1e,3 +np.float32,0x3f0b3b90,0x3f7ee92a,3 +np.float32,0xbe3eb3b4,0x3fe10a27,3 +np.float32,0xbf31a4c4,0x40159d23,3 +np.float32,0x3e929434,0x3fa3e5b0,3 +np.float32,0xbeb1a90e,0x3ff66b9e,3 +np.float32,0xbeba9b5e,0x3ff8d048,3 +np.float32,0xbf272a84,0x4012119e,3 +np.float32,0x3f1ebbd0,0x3f66e889,3 +np.float32,0x3ed3cdc8,0x3f927893,3 +np.float32,0xbf50dfce,0x40219b58,3 +np.float32,0x3f0c02de,0x3f7dfb62,3 +np.float32,0xbf694de3,0x402de8d2,3 +np.float32,0xbeaeb13e,0x3ff5a14f,3 +np.float32,0xbf61aa7a,0x40299702,3 +np.float32,0xbf13d159,0x400bed35,3 +np.float32,0xbeecd034,0x40034e0b,3 +np.float32,0xbe50c2e8,0x3fe35761,3 +np.float32,0x3f714406,0x3eae8e57,3 +np.float32,0xbf1ca486,0x400eabd8,3 +np.float32,0x3f5858cc,0x3f106497,3 +np.float32,0x3f670288,0x3ee41c84,3 +np.float32,0xbf20bd2c,0x400ff9f5,3 +np.float32,0xbe29afd8,0x3fde5eff,3 +np.float32,0xbf635e6a,0x402a80f3,3 +np.float32,0x3e82b7b0,0x3fa80446,3 +np.float32,0x3e982e7c,0x3fa26ece,3 +np.float32,0x3d9f0e00,0x3fbf1c6a,3 +np.float32,0x3e8299b4,0x3fa80c07,3 +np.float32,0xbf0529c1,0x40078ac3,3 +np.float32,0xbf403b8a,0x401ae519,3 +np.float32,0xbe57e09c,0x3fe44027,3 +np.float32,0x3ea1c8f4,0x3f9fe913,3 +np.float32,0xbe216a94,0x3fdd52d0,3 +np.float32,0x3f59c442,0x3f0db709,3 +np.float32,0xbd636260,0x3fd02bdd,3 +np.float32,0xbdbbc788,0x3fd4d08d,3 +np.float32,0x3dd19560,0x3fbbf0a3,3 +np.float32,0x3f060ad4,0x3f828641,3 +np.float32,0x3b102e00,0x3fc8c7c4,3 +np.float32,0x3f42b3b8,0x3f34e5a6,3 +np.float32,0x3f0255ac,0x3f84b071,3 +np.float32,0xbf014898,0x40066996,3 +np.float32,0x3e004dc0,0x3fb8fb51,3 +np.float32,0xbf594ff8,0x40256af2,3 +np.float32,0x3efafddc,0x3f877b80,3 +np.float32,0xbf5f0780,0x40283899,3 +np.float32,0x3ee95e54,0x3f8c7bcc,3 +np.float32,0x3eba2f0c,0x3f996c80,3 +np.float32,0x3f37721c,0x3f459b68,3 +np.float32,0x3e2be780,0x3fb378bf,3 +np.float32,0x3e550270,0x3fae3d69,3 +np.float32,0x3e0f9500,0x3fb70e0a,3 +np.float32,0xbf51974a,0x4021eaf4,3 +np.float32,0x3f393832,0x3f430d05,3 +np.float32,0x3f3df16a,0x3f3c1bd8,3 +np.float32,0xbd662340,0x3fd041ed,3 +np.float32,0x3f7e8418,0x3ddc9fce,3 +np.float32,0xbf392734,0x40184672,3 +np.float32,0x3ee3b278,0x3f8e124e,3 +np.float32,0x3eed4808,0x3f8b61d2,3 +np.float32,0xbf6fccbd,0x40322beb,3 +np.float32,0x3e3ecdd0,0x3fb1123b,3 +np.float32,0x3f4419e0,0x3f32bb45,3 +np.float32,0x3f595e00,0x3f0e7914,3 +np.float32,0xbe8c1486,0x3fec88c6,3 +np.float32,0xbf800000,0x40490fdb,3 +np.float32,0xbdaf5020,0x3fd4084d,3 +np.float32,0xbf407660,0x401afb63,3 +np.float32,0x3f0c3aa8,0x3f7db8b8,3 +np.float32,0xbcdb5980,0x3fcc7d5b,3 +np.float32,0x3f4738d4,0x3f2dd1ed,3 +np.float32,0x3f4d7064,0x3f23ab14,3 +np.float32,0xbeb1d576,0x3ff67774,3 +np.float32,0xbf507166,0x40216bb3,3 +np.float32,0x3e86484c,0x3fa71813,3 +np.float32,0x3f09123e,0x3f80bd35,3 +np.float32,0xbe9abe0e,0x3ff05cb2,3 +np.float32,0x3f3019dc,0x3f4fed21,3 +np.float32,0xbe99e00e,0x3ff0227d,3 +np.float32,0xbf155ec5,0x400c6739,3 +np.float32,0x3f5857ba,0x3f106698,3 +np.float32,0x3edf619c,0x3f8f45fb,3 +np.float32,0xbf5ab76a,0x40261664,3 +np.float32,0x3e54b5a8,0x3fae4738,3 +np.float32,0xbee92772,0x4002ca40,3 +np.float32,0x3f2fd610,0x3f504a7a,3 +np.float32,0xbf38521c,0x4017f97e,3 +np.float32,0xff800000,0x7fc00000,3 +np.float32,0x3e2da348,0x3fb34077,3 +np.float32,0x3f2f85fa,0x3f50b894,3 +np.float32,0x3e88f9c8,0x3fa66551,3 +np.float32,0xbf61e570,0x4029b648,3 +np.float32,0xbeab362c,0x3ff4b4a1,3 +np.float32,0x3ec6c310,0x3f9607bd,3 +np.float32,0x3f0d7bda,0x3f7c3810,3 +np.float32,0xbeba5d36,0x3ff8bf99,3 +np.float32,0x3f4b0554,0x3f27adda,3 +np.float32,0x3f60f5dc,0x3efebfb3,3 +np.float32,0x3f36ce2c,0x3f468603,3 +np.float32,0xbe70afac,0x3fe76e8e,3 +np.float32,0x3f673350,0x3ee339b5,3 +np.float32,0xbe124cf0,0x3fdb698c,3 +np.float32,0xbf1243dc,0x400b73d0,3 +np.float32,0x3f3c8850,0x3f3e3407,3 +np.float32,0x3ea02f24,0x3fa05500,3 +np.float32,0xbeffed34,0x400607db,3 +np.float32,0x3f5c75c2,0x3f08817c,3 +np.float32,0x3f4b2fbe,0x3f27682d,3 +np.float32,0x3ee47c34,0x3f8dd9f9,3 +np.float32,0x3f50d48c,0x3f1de584,3 +np.float32,0x3f12dc5e,0x3f75b628,3 +np.float32,0xbefe7e4a,0x4005d2f4,3 +np.float32,0xbec2e846,0x3ffb0cbc,3 +np.float32,0xbedc3036,0x4000fb80,3 +np.float32,0xbf48aedc,0x401e311f,3 +np.float32,0x3f6e032e,0x3ec11363,3 +np.float32,0xbf60de15,0x40292b72,3 +np.float32,0x3f06585e,0x3f8258ba,3 +np.float32,0x3ef49b98,0x3f894e66,3 +np.float32,0x3cc5fe00,0x3fc5f7cf,3 +np.float32,0xbf7525c5,0x40365c2c,3 +np.float32,0x3f64f9f8,0x3eed5fb2,3 +np.float32,0x3e8849c0,0x3fa692fb,3 +np.float32,0x3e50c878,0x3faec79e,3 +np.float32,0x3ed61530,0x3f91d831,3 +np.float32,0xbf54872e,0x40233724,3 +np.float32,0xbf52ee7f,0x4022815e,3 +np.float32,0xbe708c24,0x3fe769fc,3 +np.float32,0xbf26fc54,0x40120260,3 +np.float32,0x3f226e8a,0x3f6228db,3 +np.float32,0xbef30406,0x40042eb8,3 +np.float32,0x3f5d996c,0x3f063f5f,3 +np.float32,0xbf425f9c,0x401bb618,3 +np.float32,0x3e4bb260,0x3faf6dc9,3 +np.float32,0xbe52d5a4,0x3fe39b29,3 +np.float32,0xbe169cf0,0x3fdbf505,3 +np.float32,0xbedfc422,0x40017a8e,3 +np.float32,0x3d8ffef0,0x3fc00e05,3 +np.float32,0xbf12bdab,0x400b98f2,3 +np.float32,0x3f295d0a,0x3f590e88,3 +np.float32,0x3f49d8e4,0x3f2998aa,3 +np.float32,0xbef914f4,0x40050c12,3 +np.float32,0xbf4ea2b5,0x4020a61e,3 +np.float32,0xbf3a89e5,0x4018c762,3 +np.float32,0x3e8707b4,0x3fa6e67a,3 +np.float32,0x3ac55400,0x3fc8de86,3 +np.float32,0x800000,0x3fc90fdb,3 +np.float32,0xbeb9762c,0x3ff8819b,3 +np.float32,0xbebbe23c,0x3ff92815,3 +np.float32,0xbf598c88,0x402587a1,3 +np.float32,0x3e95d864,0x3fa30b4a,3 +np.float32,0x3f7f6f40,0x3d882486,3 +np.float32,0xbf53658c,0x4022b604,3 +np.float32,0xbf2a35f2,0x401314ad,3 +np.float32,0x3eb14380,0x3f9bcf28,3 +np.float32,0x3f0e0c64,0x3f7b8a7a,3 +np.float32,0x3d349920,0x3fc36a9a,3 +np.float32,0xbec2092c,0x3ffad071,3 +np.float32,0xbe1d08e8,0x3fdcc4e0,3 +np.float32,0xbf008968,0x40063243,3 +np.float32,0xbefad582,0x40054c51,3 +np.float32,0xbe52d010,0x3fe39a72,3 +np.float32,0x3f4afdac,0x3f27ba6b,3 +np.float32,0x3f6c483c,0x3eca4408,3 +np.float32,0xbef3cb68,0x40044b0c,3 +np.float32,0x3e94687c,0x3fa36b6f,3 +np.float32,0xbf64ae5c,0x402b39bb,3 +np.float32,0xbf0022b4,0x40061497,3 +np.float32,0x80000001,0x3fc90fdb,3 +np.float32,0x3f25bcd0,0x3f5dda4b,3 +np.float32,0x3ed91b40,0x3f9102d7,3 +np.float32,0x3f800000,0x0,3 +np.float32,0xbebc6aca,0x3ff94cca,3 +np.float32,0x3f239e9a,0x3f609e7d,3 +np.float32,0xbf7312be,0x4034a305,3 +np.float32,0x3efd16d0,0x3f86e148,3 +np.float32,0x3f52753a,0x3f1b0f72,3 +np.float32,0xbde58960,0x3fd7702c,3 +np.float32,0x3ef88580,0x3f883099,3 +np.float32,0x3eebaefc,0x3f8bd51e,3 +np.float32,0x3e877d2c,0x3fa6c807,3 +np.float32,0x3f1a0324,0x3f6cdf32,3 +np.float32,0xbedfe20a,0x40017eb6,3 +np.float32,0x3f205a3c,0x3f64d69d,3 +np.float32,0xbeed5b7c,0x400361b0,3 +np.float32,0xbf69ba10,0x402e2ad0,3 +np.float32,0x3c4fe200,0x3fc77014,3 +np.float32,0x3f043310,0x3f839a69,3 +np.float32,0xbeaf359a,0x3ff5c485,3 +np.float32,0x3db3f110,0x3fbdcd12,3 +np.float32,0x3e24af88,0x3fb462ed,3 +np.float32,0xbf34e858,0x4016c1c8,3 +np.float32,0x3f3334f2,0x3f4b9cd0,3 +np.float32,0xbf145882,0x400c16a2,3 +np.float32,0xbf541c38,0x40230748,3 +np.float32,0x3eba7e10,0x3f99574b,3 +np.float32,0xbe34c6e0,0x3fdfc731,3 +np.float32,0xbe957abe,0x3feefbf0,3 +np.float32,0xbf595a59,0x40256fdb,3 +np.float32,0xbdedc7b8,0x3fd7f4f0,3 +np.float32,0xbf627c02,0x402a06a9,3 +np.float32,0x3f339b78,0x3f4b0d18,3 +np.float32,0xbf2df6d2,0x40145929,3 +np.float32,0x3f617726,0x3efc9fd8,3 +np.float32,0xbee3a8fc,0x40020561,3 +np.float32,0x3efe9f68,0x3f867043,3 +np.float32,0xbf2c3e76,0x4013c3ba,3 +np.float32,0xbf218f28,0x40103d84,3 +np.float32,0xbf1ea847,0x400f4f7f,3 +np.float32,0x3ded9160,0x3fba2e31,3 +np.float32,0x3bce1b00,0x3fc841bf,3 +np.float32,0xbe90566e,0x3feda46a,3 +np.float32,0xbf5ea2ba,0x4028056b,3 +np.float32,0x3f538e62,0x3f191ee6,3 +np.float32,0xbf59e054,0x4025af74,3 +np.float32,0xbe8c98ba,0x3fecab24,3 +np.float32,0x3ee7bdb0,0x3f8cf0b7,3 +np.float32,0xbf2eb828,0x40149b2b,3 +np.float32,0xbe5eb904,0x3fe52068,3 +np.float32,0xbf16b422,0x400cd08d,3 +np.float32,0x3f1ab9b4,0x3f6bfa58,3 +np.float32,0x3dc23040,0x3fbce82a,3 +np.float32,0xbf29d9e7,0x4012f5e5,3 +np.float32,0xbf38f30a,0x40183393,3 +np.float32,0x3e88e798,0x3fa66a09,3 +np.float32,0x3f1d07e6,0x3f69124f,3 +np.float32,0xbe1d3d34,0x3fdccb7e,3 +np.float32,0xbf1715be,0x400ceec2,3 +np.float32,0x3f7a0eac,0x3e5d11f7,3 +np.float32,0xbe764924,0x3fe82707,3 +np.float32,0xbf01a1f8,0x4006837c,3 +np.float32,0x3f2be730,0x3f55a661,3 +np.float32,0xbf7bb070,0x403d4ce5,3 +np.float32,0xbd602110,0x3fd011c9,3 +np.float32,0x3f5d080c,0x3f07609d,3 +np.float32,0xbda20400,0x3fd332d1,3 +np.float32,0x3f1c62da,0x3f69e308,3 +np.float32,0xbf2c6916,0x4013d223,3 +np.float32,0xbf44f8fd,0x401cb816,3 +np.float32,0x3f4da392,0x3f235539,3 +np.float32,0x3e9e8aa0,0x3fa0c3a0,3 +np.float32,0x3e9633c4,0x3fa2f366,3 +np.float32,0xbf0422ab,0x40073ddd,3 +np.float32,0x3f518386,0x3f1cb603,3 +np.float32,0x3f24307a,0x3f5fe096,3 +np.float32,0xbdfb4220,0x3fd8ce24,3 +np.float32,0x3f179d28,0x3f6fdc7d,3 +np.float32,0xbecc2df0,0x3ffd911e,3 +np.float32,0x3f3dff0c,0x3f3c0782,3 +np.float32,0xbf58c4d8,0x4025295b,3 +np.float32,0xbdcf8438,0x3fd60dd3,3 +np.float32,0xbeeaf1b2,0x40030aa7,3 +np.float32,0xbf298a28,0x4012db45,3 +np.float32,0x3f6c4dec,0x3eca2678,3 +np.float32,0x3f4d1ac8,0x3f243a59,3 +np.float32,0x3f62cdfa,0x3ef6e8f8,3 +np.float32,0xbee8acce,0x4002b909,3 +np.float32,0xbd5f2af0,0x3fd00a15,3 +np.float32,0x3f5fde8e,0x3f01a453,3 +np.float32,0x3e95233c,0x3fa33aa4,3 +np.float32,0x3ecd2a60,0x3f9449be,3 +np.float32,0x3f10aa86,0x3f78619d,3 +np.float32,0x3f3888e8,0x3f440a70,3 +np.float32,0x3eeb5bfc,0x3f8bec7d,3 +np.float32,0xbe12d654,0x3fdb7ae6,3 +np.float32,0x3eca3110,0x3f951931,3 +np.float32,0xbe2d1b7c,0x3fdece05,3 +np.float32,0xbf29e9db,0x4012fb3a,3 +np.float32,0xbf0c50b8,0x4009a845,3 +np.float32,0xbed9f0e4,0x4000abef,3 +np.float64,0x3fd078ec5ba0f1d8,0x3ff4f7c00595a4d3,2 +np.float64,0xbfdbc39743b7872e,0x400027f85bce43b2,2 +np.float64,0xbfacd2707c39a4e0,0x3ffa08ae1075d766,2 +np.float64,0xbfc956890f32ad14,0x3ffc52308e7285fd,2 +np.float64,0xbf939c2298273840,0x3ff9706d18e6ea6b,2 +np.float64,0xbfe0d7048961ae09,0x4000fff4406bd395,2 +np.float64,0xbfe9d19b86f3a337,0x4004139bc683a69f,2 +np.float64,0x3fd35c7f90a6b900,0x3ff437220e9123f8,2 +np.float64,0x3fdddca171bbb944,0x3ff15da61e61ec08,2 +np.float64,0x3feb300de9f6601c,0x3fe1c6fadb68cdca,2 +np.float64,0xbfef1815327e302a,0x400739808fc6f964,2 +np.float64,0xbfe332d78e6665af,0x4001b6c4ef922f7c,2 +np.float64,0xbfedbf4dfb7b7e9c,0x40061cefed62a58b,2 +np.float64,0xbfd8dcc7e3b1b990,0x3fff84307713c2c3,2 +np.float64,0xbfedaf161c7b5e2c,0x400612027c1b2b25,2 +np.float64,0xbfed9bde897b37bd,0x4006053f05bd7d26,2 +np.float64,0xbfe081ebc26103d8,0x4000e70755eb66e0,2 +np.float64,0xbfe0366f9c606cdf,0x4000d11212f29afd,2 +np.float64,0xbfc7c115212f822c,0x3ffc1e8c9d58f7db,2 +np.float64,0x3fd8dd9a78b1bb34,0x3ff2bf8d0f4c9376,2 +np.float64,0xbfe54eff466a9dfe,0x4002655950b611f4,2 +np.float64,0xbfe4aad987e955b3,0x40022efb19882518,2 +np.float64,0x3f70231ca0204600,0x3ff911d834e7abf4,2 +np.float64,0x3fede01d047bc03a,0x3fd773cecbd8561b,2 +np.float64,0xbfd6a00d48ad401a,0x3ffee9fd7051633f,2 +np.float64,0x3fd44f3d50a89e7c,0x3ff3f74dd0fc9c91,2 +np.float64,0x3fe540f0d0ea81e2,0x3feb055a7c7d43d6,2 +np.float64,0xbf3ba2e200374800,0x3ff923b582650c6c,2 +np.float64,0x3fe93b2d3f72765a,0x3fe532fa15331072,2 +np.float64,0x3fee8ce5a17d19cc,0x3fd35666eefbe336,2 +np.float64,0x3fe55d5f8feabac0,0x3feadf3dcfe251d4,2 +np.float64,0xbfd1d2ede8a3a5dc,0x3ffda600041ac884,2 +np.float64,0xbfee41186e7c8231,0x40067a625cc6f64d,2 +np.float64,0x3fe521a8b9ea4352,0x3feb2f1a6c8084e5,2 +np.float64,0x3fc65378ef2ca6f0,0x3ff653dfe81ee9f2,2 +np.float64,0x3fdaba0fbcb57420,0x3ff23d630995c6ba,2 +np.float64,0xbfe6b7441d6d6e88,0x4002e182539a2994,2 +np.float64,0x3fda00b6dcb4016c,0x3ff2703d516f28e7,2 +np.float64,0xbfe8699f01f0d33e,0x400382326920ea9e,2 +np.float64,0xbfef5889367eb112,0x4007832af5983793,2 +np.float64,0x3fefb57c8aff6afa,0x3fc14700ab38dcef,2 +np.float64,0xbfda0dfdaab41bfc,0x3fffd75b6fd497f6,2 +np.float64,0xbfb059c36620b388,0x3ffa27c528b97a42,2 +np.float64,0xbfdd450ab1ba8a16,0x40005dcac6ab50fd,2 +np.float64,0xbfe54d6156ea9ac2,0x400264ce9f3f0fb9,2 +np.float64,0xbfe076e94760edd2,0x4000e3d1374884da,2 +np.float64,0xbfc063286720c650,0x3ffb2fd1d6bff0ef,2 +np.float64,0xbfe24680f2e48d02,0x40016ddfbb5bcc0e,2 +np.float64,0xbfdc9351d2b926a4,0x400044e3756fb765,2 +np.float64,0x3fefb173d8ff62e8,0x3fc1bd5626f80850,2 +np.float64,0x3fe77c117a6ef822,0x3fe7e57089bad2ec,2 +np.float64,0xbfddbcebf7bb79d8,0x40006eadb60406b3,2 +np.float64,0xbfecf6625ff9ecc5,0x40059e6c6961a6db,2 +np.float64,0x3fdc8950b8b912a0,0x3ff1bcfb2e27795b,2 +np.float64,0xbfeb2fa517765f4a,0x4004b00aee3e6888,2 +np.float64,0x3fd0efc88da1df90,0x3ff4d8f7cbd8248a,2 +np.float64,0xbfe6641a2becc834,0x4002c43362c1bd0f,2 +np.float64,0xbfe28aec0fe515d8,0x400182c91d4df039,2 +np.float64,0xbfd5ede8d0abdbd2,0x3ffeba7baef05ae8,2 +np.float64,0xbfbd99702a3b32e0,0x3ffafca21c1053f1,2 +np.float64,0x3f96f043f82de080,0x3ff8c6384d5eb610,2 +np.float64,0xbfe5badbc9eb75b8,0x400289c8cd5873d1,2 +np.float64,0x3fe5c6bf95eb8d80,0x3fea5093e9a3e43e,2 +np.float64,0x3fb1955486232ab0,0x3ff8086d4c3e71d5,2 +np.float64,0xbfea145f397428be,0x4004302237a35871,2 +np.float64,0xbfdabe685db57cd0,0x400003e2e29725fb,2 +np.float64,0xbfefc79758ff8f2f,0x400831814e23bfc8,2 +np.float64,0x3fd7edb66cafdb6c,0x3ff3006c5123bfaf,2 +np.float64,0xbfeaf7644bf5eec8,0x400495a7963ce4ed,2 +np.float64,0x3fdf838d78bf071c,0x3ff0e527eed73800,2 +np.float64,0xbfd1a0165ba3402c,0x3ffd98c5ab76d375,2 +np.float64,0x3fd75b67a9aeb6d0,0x3ff327c8d80b17cf,2 +np.float64,0x3fc2aa9647255530,0x3ff6ca854b157df1,2 +np.float64,0xbfe0957fd4612b00,0x4000ecbf3932becd,2 +np.float64,0x3fda1792c0b42f24,0x3ff269fbb2360487,2 +np.float64,0x3fd480706ca900e0,0x3ff3ea53a6aa3ae8,2 +np.float64,0xbfd0780ed9a0f01e,0x3ffd4bfd544c7d47,2 +np.float64,0x3feeec0cd77dd81a,0x3fd0a8a241fdb441,2 +np.float64,0x3fcfa933e93f5268,0x3ff5223478621a6b,2 +np.float64,0x3fdad2481fb5a490,0x3ff236b86c6b2b49,2 +np.float64,0x3fe03b129de07626,0x3ff09f21fb868451,2 +np.float64,0xbfc01212cd202424,0x3ffb259a07159ae9,2 +np.float64,0x3febdb912df7b722,0x3fe0768e20dac8c9,2 +np.float64,0xbfbf2148763e4290,0x3ffb154c361ce5bf,2 +np.float64,0xbfb1a7eb1e234fd8,0x3ffa3cb37ac4a176,2 +np.float64,0xbfe26ad1ec64d5a4,0x400178f480ecce8d,2 +np.float64,0x3fe6d1cd1b6da39a,0x3fe8dc20ec4dad3b,2 +np.float64,0xbfede0e53dfbc1ca,0x4006340d3bdd7c97,2 +np.float64,0xbfe8fd1bd9f1fa38,0x4003bc3477f93f40,2 +np.float64,0xbfe329d0f26653a2,0x4001b3f345af5648,2 +np.float64,0xbfe4bb20eee97642,0x40023451404d6d08,2 +np.float64,0x3fb574832e2ae900,0x3ff7ca4bed0c7110,2 +np.float64,0xbfdf3c098fbe7814,0x4000a525bb72d659,2 +np.float64,0x3fa453e6d428a7c0,0x3ff87f512bb9b0c6,2 +np.float64,0x3faaec888435d920,0x3ff84a7d9e4def63,2 +np.float64,0xbfcdc240df3b8480,0x3ffce30ece754e7f,2 +np.float64,0xbf8c3220f0386440,0x3ff95a600ae6e157,2 +np.float64,0x3fe806076c700c0e,0x3fe71784a96c76eb,2 +np.float64,0x3fedf9b0e17bf362,0x3fd6e35fc0a7b6c3,2 +np.float64,0xbfe1b48422636908,0x400141bd8ed251bc,2 +np.float64,0xbfe82e2817705c50,0x40036b5a5556d021,2 +np.float64,0xbfc8ef8ff931df20,0x3ffc450ffae7ce58,2 +np.float64,0xbfe919fa94f233f5,0x4003c7cce4697fe8,2 +np.float64,0xbfc3ace4a72759c8,0x3ffb9a197bb22651,2 +np.float64,0x3fe479f71ee8f3ee,0x3fec0bd2f59097aa,2 +np.float64,0xbfeeb54a967d6a95,0x4006da12c83649c5,2 +np.float64,0x3fe5e74ea8ebce9e,0x3fea2407cef0f08c,2 +np.float64,0x3fb382baf2270570,0x3ff7e98213b921ba,2 +np.float64,0xbfdd86fd3cbb0dfa,0x40006712952ddbcf,2 +np.float64,0xbfd250eb52a4a1d6,0x3ffdc6d56253b1cd,2 +np.float64,0x3fea30c4ed74618a,0x3fe3962deba4f30e,2 +np.float64,0x3fc895963d312b30,0x3ff60a5d52fcbccc,2 +np.float64,0x3fe9cc4f6273989e,0x3fe442740942c80f,2 +np.float64,0xbfe8769f5cf0ed3f,0x4003873b4cb5bfce,2 +np.float64,0xbfe382f3726705e7,0x4001cfeb3204d110,2 +np.float64,0x3fbfe9a9163fd350,0x3ff7220bd2b97c8f,2 +np.float64,0xbfca6162bb34c2c4,0x3ffc743f939358f1,2 +np.float64,0x3fe127a014e24f40,0x3ff0147c4bafbc39,2 +np.float64,0x3fee9cdd2a7d39ba,0x3fd2e9ef45ab122f,2 +np.float64,0x3fa9ffb97c33ff80,0x3ff851e69fa3542c,2 +np.float64,0x3fd378f393a6f1e8,0x3ff42faafa77de56,2 +np.float64,0xbfe4df1e1669be3c,0x400240284df1c321,2 +np.float64,0x3fed0ed79bfa1db0,0x3fdba89060aa96fb,2 +np.float64,0x3fdef2ee52bde5dc,0x3ff10e942244f4f1,2 +np.float64,0xbfdab38f3ab5671e,0x40000264d8d5b49b,2 +np.float64,0x3fbe95a96e3d2b50,0x3ff73774cb59ce2d,2 +np.float64,0xbfe945653af28aca,0x4003d9657bf129c2,2 +np.float64,0xbfb18f3f2a231e80,0x3ffa3b27cba23f50,2 +np.float64,0xbfef50bf22fea17e,0x40077998a850082c,2 +np.float64,0xbfc52b8c212a5718,0x3ffbca8d6560a2da,2 +np.float64,0x7ff8000000000000,0x7ff8000000000000,2 +np.float64,0x3fc1e3a02d23c740,0x3ff6e3a5fcac12a4,2 +np.float64,0xbfeb5e4ea5f6bc9d,0x4004c65abef9426f,2 +np.float64,0xbfe425b132684b62,0x400203c29608b00d,2 +np.float64,0xbfbfa1c19e3f4380,0x3ffb1d6367711158,2 +np.float64,0x3fbba2776e3744f0,0x3ff766f6df586fad,2 +np.float64,0xbfb5d0951e2ba128,0x3ffa7f712480b25e,2 +np.float64,0xbfe949fdab7293fb,0x4003db4530a18507,2 +np.float64,0xbfcf13519b3e26a4,0x3ffd0e6f0a6c38ee,2 +np.float64,0x3f91e6d72823cdc0,0x3ff8da5f08909b6e,2 +np.float64,0x3f78a2e360314600,0x3ff909586727caef,2 +np.float64,0xbfe1ae7e8fe35cfd,0x40013fef082caaa3,2 +np.float64,0x3fe97a6dd1f2f4dc,0x3fe4cb4b99863478,2 +np.float64,0xbfcc1e1e69383c3c,0x3ffcad250a949843,2 +np.float64,0x3faccb797c399700,0x3ff83b8066b49330,2 +np.float64,0x3fe7a2647a6f44c8,0x3fe7acceae6ec425,2 +np.float64,0xbfec3bfcf0f877fa,0x4005366af5a7175b,2 +np.float64,0xbfe2310b94646217,0x400167588fceb228,2 +np.float64,0x3feb167372762ce6,0x3fe1f74c0288fad8,2 +np.float64,0xbfb722b4ee2e4568,0x3ffa94a81b94dfca,2 +np.float64,0x3fc58da9712b1b50,0x3ff66cf8f072aa14,2 +np.float64,0xbfe7fff9d6effff4,0x400359d01b8141de,2 +np.float64,0xbfd56691c5aacd24,0x3ffe9686697797e8,2 +np.float64,0x3fe3ab0557e7560a,0x3fed1593959ef8e8,2 +np.float64,0x3fdd458995ba8b14,0x3ff1883d6f22a322,2 +np.float64,0x3fe7bbed2cef77da,0x3fe786d618094cda,2 +np.float64,0x3fa31a30c4263460,0x3ff88920b936fd79,2 +np.float64,0x8010000000000000,0x3ff921fb54442d18,2 +np.float64,0xbfdc5effbdb8be00,0x40003d95fe0dff11,2 +np.float64,0x3febfdad7e77fb5a,0x3fe030b5297dbbdd,2 +np.float64,0x3fe4f3f3b2e9e7e8,0x3feb6bc59eeb2be2,2 +np.float64,0xbfe44469fd6888d4,0x40020daa5488f97a,2 +np.float64,0xbfe19fddb0e33fbc,0x40013b8c902b167b,2 +np.float64,0x3fa36ad17c26d5a0,0x3ff8869b3e828134,2 +np.float64,0x3fcf23e6c93e47d0,0x3ff5336491a65d1e,2 +np.float64,0xffefffffffffffff,0x7ff8000000000000,2 +np.float64,0xbfe375f4cee6ebea,0x4001cbd2ba42e8b5,2 +np.float64,0xbfaef1215c3de240,0x3ffa19ab02081189,2 +np.float64,0xbfec39c59c78738b,0x4005353dc38e3d78,2 +np.float64,0x7ff4000000000000,0x7ffc000000000000,2 +np.float64,0xbfec09bb7b781377,0x40051c0a5754cb3a,2 +np.float64,0x3fe8301f2870603e,0x3fe6d783c5ef0944,2 +np.float64,0xbfed418c987a8319,0x4005cbae1b8693d1,2 +np.float64,0xbfdc16e7adb82dd0,0x4000338b634eaf03,2 +np.float64,0x3fd5d361bdaba6c4,0x3ff390899300a54c,2 +np.float64,0xbff0000000000000,0x400921fb54442d18,2 +np.float64,0x3fd5946232ab28c4,0x3ff3a14767813f29,2 +np.float64,0x3fe833e5fef067cc,0x3fe6d1be720edf2d,2 +np.float64,0x3fedf746a67bee8e,0x3fd6f127fdcadb7b,2 +np.float64,0x3fd90353d3b206a8,0x3ff2b54f7d369ba9,2 +np.float64,0x3fec4b4b72f89696,0x3fdf1b38d2e93532,2 +np.float64,0xbfe9c67596f38ceb,0x40040ee5f524ce03,2 +np.float64,0x3fd350d91aa6a1b4,0x3ff43a303c0da27f,2 +np.float64,0x3fd062603ba0c4c0,0x3ff4fd9514b935d8,2 +np.float64,0xbfe24c075f64980e,0x40016f8e9f2663b3,2 +np.float64,0x3fdaa546eeb54a8c,0x3ff2431a88fef1d5,2 +np.float64,0x3fe92b8151f25702,0x3fe54c67e005cbf9,2 +np.float64,0xbfe1be8b8a637d17,0x400144c078f67c6e,2 +np.float64,0xbfe468a1d7e8d144,0x40021964b118cbf4,2 +np.float64,0xbfdc6de4fab8dbca,0x40003fa9e27893d8,2 +np.float64,0xbfe3c2788ae784f1,0x4001e407ba3aa956,2 +np.float64,0xbfe2bf1542e57e2a,0x400192d4a9072016,2 +np.float64,0xbfe6982f4c6d305e,0x4002d681b1991bbb,2 +np.float64,0x3fdbceb1c4b79d64,0x3ff1f0f117b9d354,2 +np.float64,0x3fdb3705e7b66e0c,0x3ff21af01ca27ace,2 +np.float64,0x3fe3e6358ee7cc6c,0x3fecca4585053983,2 +np.float64,0xbfe16d6a9a62dad5,0x40012c7988aee247,2 +np.float64,0xbfce66e4413ccdc8,0x3ffcf83b08043a0c,2 +np.float64,0xbfeb6cd46876d9a9,0x4004cd61733bfb79,2 +np.float64,0xbfdb1cdd64b639ba,0x400010e6cf087cb7,2 +np.float64,0xbfe09e4e30e13c9c,0x4000ef5277c47721,2 +np.float64,0xbfee88dd127d11ba,0x4006b3cd443643ac,2 +np.float64,0xbf911e06c8223c00,0x3ff966744064fb05,2 +np.float64,0xbfe8f22bc471e458,0x4003b7d5513af295,2 +np.float64,0x3fe3d7329567ae66,0x3fecdd6c241f83ee,2 +np.float64,0x3fc8a9404b315280,0x3ff607dc175edf3f,2 +np.float64,0x3fe7eb80ad6fd702,0x3fe73f8fdb3e6a6c,2 +np.float64,0x3fef0931e37e1264,0x3fcf7fde80a3c5ab,2 +np.float64,0x3fe2ed3c3fe5da78,0x3fee038334cd1860,2 +np.float64,0x3fe251fdb8e4a3fc,0x3feec26dc636ac31,2 +np.float64,0x3feb239436764728,0x3fe1de9462455da7,2 +np.float64,0xbfe63fd7eeec7fb0,0x4002b78cfa3d2fa6,2 +np.float64,0x3fdd639cb5bac738,0x3ff17fc7d92b3eee,2 +np.float64,0x3fd0a7a13fa14f44,0x3ff4eba95c559c84,2 +np.float64,0x3fe804362d70086c,0x3fe71a44cd91ffa4,2 +np.float64,0xbfe0fecf6e61fd9f,0x40010bac8edbdc4f,2 +np.float64,0x3fcb74acfd36e958,0x3ff5ac84437f1b7c,2 +np.float64,0x3fe55053e1eaa0a8,0x3feaf0bf76304c30,2 +np.float64,0x3fc06b508d20d6a0,0x3ff7131da17f3902,2 +np.float64,0x3fdd78750fbaf0ec,0x3ff179e97fbf7f65,2 +np.float64,0x3fe44cb946689972,0x3fec46859b5da6be,2 +np.float64,0xbfeb165a7ff62cb5,0x4004a41c9cc9589e,2 +np.float64,0x3fe01ffb2b603ff6,0x3ff0aed52bf1c3c1,2 +np.float64,0x3f983c60a83078c0,0x3ff8c107805715ab,2 +np.float64,0x3fd8b5ff13b16c00,0x3ff2ca4a837a476a,2 +np.float64,0x3fc80510a1300a20,0x3ff61cc3b4af470b,2 +np.float64,0xbfd3935b06a726b6,0x3ffe1b3a2066f473,2 +np.float64,0xbfdd4a1f31ba943e,0x40005e81979ed445,2 +np.float64,0xbfa76afdd42ed600,0x3ff9dd63ffba72d2,2 +np.float64,0x3fe7e06d496fc0da,0x3fe7503773566707,2 +np.float64,0xbfea5fbfe874bf80,0x40045106af6c538f,2 +np.float64,0x3fee000c487c0018,0x3fd6bef1f8779d88,2 +np.float64,0xbfb39f4ee2273ea0,0x3ffa5c3f2b3888ab,2 +np.float64,0x3feb9247b0772490,0x3fe1092d2905efce,2 +np.float64,0x3fdaa39b4cb54738,0x3ff243901da0da17,2 +np.float64,0x3fcd5b2b493ab658,0x3ff56e262e65b67d,2 +np.float64,0x3fcf82512f3f04a0,0x3ff52738847c55f2,2 +np.float64,0x3fe2af5e0c655ebc,0x3fee4ffab0c82348,2 +np.float64,0xbfec0055d0f800ac,0x4005172d325933e8,2 +np.float64,0x3fe71da9336e3b52,0x3fe86f2e12f6e303,2 +np.float64,0x3fbefab0723df560,0x3ff731188ac716ec,2 +np.float64,0xbfe11dca28623b94,0x400114d3d4ad370d,2 +np.float64,0x3fbcbda8ca397b50,0x3ff755281078abd4,2 +np.float64,0x3fe687c7126d0f8e,0x3fe945099a7855cc,2 +np.float64,0xbfecde510579bca2,0x400590606e244591,2 +np.float64,0xbfd72de681ae5bce,0x3fff0ff797ad1755,2 +np.float64,0xbfe7c0f7386f81ee,0x40034226e0805309,2 +np.float64,0x3fd8d55619b1aaac,0x3ff2c1cb3267b14e,2 +np.float64,0x3fecd7a2ad79af46,0x3fdcabbffeaa279e,2 +np.float64,0x3fee7fb1a8fcff64,0x3fd3ae620286fe19,2 +np.float64,0xbfc5f3a3592be748,0x3ffbe3ed204d9842,2 +np.float64,0x3fec9e5527793caa,0x3fddb00bc8687e4b,2 +np.float64,0x3fc35dc70f26bb90,0x3ff6b3ded7191e33,2 +np.float64,0x3fda91c07ab52380,0x3ff24878848fec8f,2 +np.float64,0xbfe12cde1fe259bc,0x4001194ab99d5134,2 +np.float64,0xbfd35ab736a6b56e,0x3ffe0c5ce8356d16,2 +np.float64,0x3fc9c94123339280,0x3ff5e3239f3ad795,2 +np.float64,0xbfe72f54926e5ea9,0x40030c95d1d02b56,2 +np.float64,0xbfee283186fc5063,0x40066786bd0feb79,2 +np.float64,0xbfe7b383f56f6708,0x40033d23ef0e903d,2 +np.float64,0x3fd6037327ac06e8,0x3ff383bf2f311ddb,2 +np.float64,0x3fe0e344b561c68a,0x3ff03cd90fd4ba65,2 +np.float64,0xbfef0ff54b7e1feb,0x400730fa5fce381e,2 +np.float64,0x3fd269929da4d324,0x3ff476b230136d32,2 +np.float64,0xbfbc5fb9f638bf70,0x3ffae8e63a4e3234,2 +np.float64,0xbfe2e8bc84e5d179,0x40019fb5874f4310,2 +np.float64,0xbfd7017413ae02e8,0x3fff040d843c1531,2 +np.float64,0x3fefd362fa7fa6c6,0x3fbababc3ddbb21d,2 +np.float64,0x3fecb62ed3f96c5e,0x3fdd44ba77ccff94,2 +np.float64,0xbfb16fad5222df58,0x3ffa392d7f02b522,2 +np.float64,0x3fbcf4abc639e950,0x3ff751b23c40e27f,2 +np.float64,0x3fe128adbce2515c,0x3ff013dc91db04b5,2 +np.float64,0x3fa5dd9d842bbb40,0x3ff87300c88d512f,2 +np.float64,0xbfe61efcaf6c3dfa,0x4002ac27117f87c9,2 +np.float64,0x3feffe1233fffc24,0x3f9638d3796a4954,2 +np.float64,0xbfe78548b66f0a92,0x40032c0447b7bfe2,2 +np.float64,0x3fe7bd38416f7a70,0x3fe784e86d6546b6,2 +np.float64,0x3fe0d6bc5961ad78,0x3ff0443899e747ac,2 +np.float64,0xbfd0bb6e47a176dc,0x3ffd5d6dff390d41,2 +np.float64,0xbfec1d16b8f83a2e,0x40052620378d3b78,2 +np.float64,0x3fe9bbec20f377d8,0x3fe45e167c7a3871,2 +np.float64,0xbfeed81d9dfdb03b,0x4006f9dec2db7310,2 +np.float64,0xbfe1e35179e3c6a3,0x40014fd1b1186ac0,2 +np.float64,0xbfc9c7e605338fcc,0x3ffc60a6bd1a7126,2 +np.float64,0x3feec92810fd9250,0x3fd1afde414ab338,2 +np.float64,0xbfeb9f1d90773e3b,0x4004e606b773f5b0,2 +np.float64,0x3fcbabdf6b3757c0,0x3ff5a573866404af,2 +np.float64,0x3fe9f4e1fff3e9c4,0x3fe3fd7b6712dd7b,2 +np.float64,0xbfe6c0175ded802e,0x4002e4a4dc12f3fe,2 +np.float64,0xbfeefc96f37df92e,0x40071d367cd721ff,2 +np.float64,0xbfeaab58dc7556b2,0x400472ce37e31e50,2 +np.float64,0xbfc62668772c4cd0,0x3ffbea5e6c92010a,2 +np.float64,0x3fafe055fc3fc0a0,0x3ff822ce6502519a,2 +np.float64,0x3fd7b648ffaf6c90,0x3ff30f5a42f11418,2 +np.float64,0xbfe934fe827269fd,0x4003d2b9fed9e6ad,2 +np.float64,0xbfe6d691f2edad24,0x4002eca6a4b1797b,2 +np.float64,0x3fc7e62ced2fcc58,0x3ff620b1f44398b7,2 +np.float64,0xbfc89be9f33137d4,0x3ffc3a67a497f59c,2 +np.float64,0xbfe7793d536ef27a,0x40032794bf14dd64,2 +np.float64,0x3fde55a02dbcab40,0x3ff13b5f82d223e4,2 +np.float64,0xbfc8eabd7b31d57c,0x3ffc4472a81cb6d0,2 +np.float64,0x3fddcb5468bb96a8,0x3ff162899c381f2e,2 +np.float64,0xbfec7554d8f8eaaa,0x40055550e18ec463,2 +np.float64,0x3fd0b6e8b6a16dd0,0x3ff4e7b4781a50e3,2 +np.float64,0x3fedaae01b7b55c0,0x3fd8964916cdf53d,2 +np.float64,0x3fe0870f8a610e20,0x3ff072e7db95c2a2,2 +np.float64,0xbfec3e3ce2787c7a,0x4005379d0f6be873,2 +np.float64,0xbfe65502586caa04,0x4002beecff89147f,2 +np.float64,0xbfe0df39a961be74,0x4001025e36d1c061,2 +np.float64,0xbfb5d8edbe2bb1d8,0x3ffa7ff72b7d6a2b,2 +np.float64,0xbfde89574bbd12ae,0x40008ba4cd74544d,2 +np.float64,0xbfe72938f0ee5272,0x40030a5efd1acb6d,2 +np.float64,0xbfcd500d133aa01c,0x3ffcd462f9104689,2 +np.float64,0x3fe0350766606a0e,0x3ff0a2a3664e2c14,2 +np.float64,0xbfc892fb573125f8,0x3ffc3944641cc69d,2 +np.float64,0xbfba7dc7c634fb90,0x3ffaca9a6a0ffe61,2 +np.float64,0xbfeac94478759289,0x40048068a8b83e45,2 +np.float64,0xbfe8f60c1af1ec18,0x4003b961995b6e51,2 +np.float64,0x3fea1c0817743810,0x3fe3ba28c1643cf7,2 +np.float64,0xbfe42a0fefe85420,0x4002052aadd77f01,2 +np.float64,0x3fd2c61c56a58c38,0x3ff45e84cb9a7fa9,2 +np.float64,0xbfd83fb7cdb07f70,0x3fff59ab4790074c,2 +np.float64,0x3fd95e630fb2bcc8,0x3ff29c8bee1335ad,2 +np.float64,0x3feee88f387dd11e,0x3fd0c3ad3ded4094,2 +np.float64,0x3fe061291160c252,0x3ff0890010199bbc,2 +np.float64,0xbfdc7db3b5b8fb68,0x400041dea3759443,2 +np.float64,0x3fee23b320fc4766,0x3fd5ee73d7aa5c56,2 +np.float64,0xbfdc25c590b84b8c,0x4000359cf98a00b4,2 +np.float64,0xbfd63cbfd2ac7980,0x3ffecf7b9cf99b3c,2 +np.float64,0xbfbeb3c29a3d6788,0x3ffb0e66ecc0fc3b,2 +np.float64,0xbfd2f57fd6a5eb00,0x3ffdf1d7c79e1532,2 +np.float64,0xbfab3eda9c367db0,0x3ff9fc0c875f42e9,2 +np.float64,0xbfe12df1c6e25be4,0x4001199c673e698c,2 +np.float64,0x3fef8ab23a7f1564,0x3fc5aff358c59f1c,2 +np.float64,0x3fe562f50feac5ea,0x3fead7bce205f7d9,2 +np.float64,0x3fdc41adbeb8835c,0x3ff1d0f71341b8f2,2 +np.float64,0x3fe2748967e4e912,0x3fee9837f970ff9e,2 +np.float64,0xbfdaa89d57b5513a,0x400000e3889ba4cf,2 +np.float64,0x3fdf2a137dbe5428,0x3ff0fecfbecbbf86,2 +np.float64,0xbfea1fdcd2f43fba,0x4004351974b32163,2 +np.float64,0xbfe34a93a3e69528,0x4001be323946a3e0,2 +np.float64,0x3fe929bacff25376,0x3fe54f47bd7f4cf2,2 +np.float64,0xbfd667fbd6accff8,0x3ffedb04032b3a1a,2 +np.float64,0xbfeb695796f6d2af,0x4004cbb08ec6f525,2 +np.float64,0x3fd204df2ea409c0,0x3ff490f51e6670f5,2 +np.float64,0xbfd89a2757b1344e,0x3fff722127b988c4,2 +np.float64,0xbfd0787187a0f0e4,0x3ffd4c16dbe94f32,2 +np.float64,0x3fd44239bfa88474,0x3ff3fabbfb24b1fa,2 +np.float64,0xbfeb0b3489f61669,0x40049ee33d811d33,2 +np.float64,0x3fdcf04eaab9e09c,0x3ff1a02a29996c4e,2 +np.float64,0x3fd4c51e4fa98a3c,0x3ff3d8302c68fc9a,2 +np.float64,0x3fd1346645a268cc,0x3ff4c72b4970ecaf,2 +np.float64,0x3fd6a89d09ad513c,0x3ff357af6520afac,2 +np.float64,0xbfba0f469a341e90,0x3ffac3a8f41bed23,2 +np.float64,0xbfe13f8ddce27f1c,0x40011ed557719fd6,2 +np.float64,0x3fd43e5e26a87cbc,0x3ff3fbc040fc30dc,2 +np.float64,0x3fe838125a707024,0x3fe6cb5c987248f3,2 +np.float64,0x3fe128c30c625186,0x3ff013cff238dd1b,2 +np.float64,0xbfcd4718833a8e30,0x3ffcd33c96bde6f9,2 +np.float64,0x3fe43fcd08e87f9a,0x3fec573997456ec1,2 +np.float64,0xbfe9a29104734522,0x4003ffd502a1b57f,2 +np.float64,0xbfe4709d7968e13b,0x40021bfc5cd55af4,2 +np.float64,0x3fd21c3925a43874,0x3ff48adf48556cbb,2 +np.float64,0x3fe9a521b2734a44,0x3fe4844fc054e839,2 +np.float64,0xbfdfa6a912bf4d52,0x4000b4730ad8521e,2 +np.float64,0x3fe3740702e6e80e,0x3fed5b106283b6ed,2 +np.float64,0x3fd0a3aa36a14754,0x3ff4ecb02a5e3f49,2 +np.float64,0x3fdcb903d0b97208,0x3ff1afa5d692c5b9,2 +np.float64,0xbfe7d67839efacf0,0x40034a3146abf6f2,2 +np.float64,0x3f9981c6d8330380,0x3ff8bbf1853d7b90,2 +np.float64,0xbfe9d4191673a832,0x400414a9ab453c5d,2 +np.float64,0x3fef0a1e5c7e143c,0x3fcf70b02a54c415,2 +np.float64,0xbfd996dee6b32dbe,0x3fffb6cf707ad8e4,2 +np.float64,0x3fe19bef17e337de,0x3fef9e70d4fcedae,2 +np.float64,0x3fe34a59716694b2,0x3fed8f6d5cfba474,2 +np.float64,0x3fdf27e27cbe4fc4,0x3ff0ff70500e0c7c,2 +np.float64,0xbfe19df87fe33bf1,0x40013afb401de24c,2 +np.float64,0xbfbdfd97ba3bfb30,0x3ffb02ef8c225e57,2 +np.float64,0xbfe3d3417267a683,0x4001e95ed240b0f8,2 +np.float64,0x3fe566498b6acc94,0x3fead342957d4910,2 +np.float64,0x3ff0000000000000,0x0,2 +np.float64,0x3feb329bd8766538,0x3fe1c2225aafe3b4,2 +np.float64,0xbfc19ca703233950,0x3ffb575b5df057b9,2 +np.float64,0x3fe755027d6eaa04,0x3fe81eb99c262e00,2 +np.float64,0xbfe6c2b8306d8570,0x4002e594199f9eec,2 +np.float64,0x3fd69438e6ad2870,0x3ff35d2275ae891d,2 +np.float64,0x3fda3e7285b47ce4,0x3ff25f5573dd47ae,2 +np.float64,0x3fe7928a166f2514,0x3fe7c4490ef4b9a9,2 +np.float64,0xbfd4eb71b9a9d6e4,0x3ffe75e8ccb74be1,2 +np.float64,0xbfcc3a07f1387410,0x3ffcb0b8af914a5b,2 +np.float64,0xbfe6e80225edd004,0x4002f2e26eae8999,2 +np.float64,0xbfb347728a268ee8,0x3ffa56bd526a12db,2 +np.float64,0x3fe5140ead6a281e,0x3feb4132c9140a1c,2 +np.float64,0xbfc147f125228fe4,0x3ffb4cab18b9050f,2 +np.float64,0xbfcb9145b537228c,0x3ffc9b1b6227a8c9,2 +np.float64,0xbfda84ef4bb509de,0x3ffff7f8a674e17d,2 +np.float64,0x3fd2eb6bbfa5d6d8,0x3ff454c225529d7e,2 +np.float64,0x3fe18c95f1e3192c,0x3fefb0cf0efba75a,2 +np.float64,0x3fe78606efef0c0e,0x3fe7d6c3a092d64c,2 +np.float64,0x3fbad5119a35aa20,0x3ff773dffe3ce660,2 +np.float64,0x3fd0cf5903a19eb4,0x3ff4e15fd21fdb42,2 +np.float64,0xbfd85ce90bb0b9d2,0x3fff618ee848e974,2 +np.float64,0x3fe90e11b9f21c24,0x3fe57be62f606f4a,2 +np.float64,0x3fd7a2040faf4408,0x3ff314ce85457ec2,2 +np.float64,0xbfd73fba69ae7f74,0x3fff14bff3504811,2 +np.float64,0x3fa04b4bd42096a0,0x3ff89f9b52f521a2,2 +np.float64,0xbfd7219ce5ae433a,0x3fff0cac0b45cc18,2 +np.float64,0xbfe0cf4661e19e8d,0x4000fdadb14e3c22,2 +np.float64,0x3fd07469fea0e8d4,0x3ff4f8eaa9b2394a,2 +np.float64,0x3f9b05c5d8360b80,0x3ff8b5e10672db5c,2 +np.float64,0x3fe4c25b916984b8,0x3febad29bd0e25e2,2 +np.float64,0xbfde8b4891bd1692,0x40008beb88d5c409,2 +np.float64,0xbfe199a7efe33350,0x400139b089aee21c,2 +np.float64,0x3fecdad25cf9b5a4,0x3fdc9d062867e8c3,2 +np.float64,0xbfe979b277f2f365,0x4003eedb061e25a4,2 +np.float64,0x3fc8c7311f318e60,0x3ff6040b9aeaad9d,2 +np.float64,0x3fd2b605b8a56c0c,0x3ff462b9a955c224,2 +np.float64,0x3fc073b6ad20e770,0x3ff7120e9f2fd63c,2 +np.float64,0xbfec60ede678c1dc,0x40054a3863e24dc2,2 +np.float64,0x3fe225171be44a2e,0x3feef910dca420ea,2 +np.float64,0xbfd7529762aea52e,0x3fff19d00661f650,2 +np.float64,0xbfd781783daf02f0,0x3fff2667b90be461,2 +np.float64,0x3fe3f6ec6d67edd8,0x3fecb4e814a2e33a,2 +np.float64,0x3fece6702df9cce0,0x3fdc6719d92a50d2,2 +np.float64,0xbfb5c602ce2b8c08,0x3ffa7ec761ba856a,2 +np.float64,0xbfd61f0153ac3e02,0x3ffec78e3b1a6c4d,2 +np.float64,0xbfec3462b2f868c5,0x400532630bbd7050,2 +np.float64,0xbfdd248485ba490a,0x400059391c07c1bb,2 +np.float64,0xbfd424921fa84924,0x3ffe416a85d1dcdf,2 +np.float64,0x3fbb23a932364750,0x3ff76eef79209f7f,2 +np.float64,0x3fca248b0f344918,0x3ff5d77c5c1b4e5e,2 +np.float64,0xbfe69af4a4ed35ea,0x4002d77c2e4fbd4e,2 +np.float64,0x3fdafe3cdcb5fc78,0x3ff22a9be6efbbf2,2 +np.float64,0xbfebba3377f77467,0x4004f3836e1fe71a,2 +np.float64,0xbfe650fae06ca1f6,0x4002bd851406377c,2 +np.float64,0x3fda630007b4c600,0x3ff2554f1832bd94,2 +np.float64,0xbfda8107d9b50210,0x3ffff6e6209659f3,2 +np.float64,0x3fea759a02f4eb34,0x3fe31d1a632c9aae,2 +np.float64,0x3fbf88149e3f1030,0x3ff728313aa12ccb,2 +np.float64,0x3f7196d2a0232e00,0x3ff910647e1914c1,2 +np.float64,0x3feeae51d17d5ca4,0x3fd2709698d31f6f,2 +np.float64,0xbfd73cd663ae79ac,0x3fff13f96300b55a,2 +np.float64,0x3fd4fc5f06a9f8c0,0x3ff3c99359854b97,2 +np.float64,0x3fb29f5d6e253ec0,0x3ff7f7c20e396b20,2 +np.float64,0xbfd757c82aaeaf90,0x3fff1b34c6141e98,2 +np.float64,0x3fc56fd4cf2adfa8,0x3ff670c145122909,2 +np.float64,0x3fc609a2f52c1348,0x3ff65d3ef3cade2c,2 +np.float64,0xbfe1de631163bcc6,0x40014e5528fadb73,2 +np.float64,0xbfe7eb4a726fd695,0x40035202f49d95c4,2 +np.float64,0xbfc9223771324470,0x3ffc4b84d5e263b9,2 +np.float64,0x3fee91a8a87d2352,0x3fd3364befde8de6,2 +np.float64,0x3fbc9784fe392f10,0x3ff7578e29f6a1b2,2 +np.float64,0xbfec627c2c78c4f8,0x40054b0ff2cb9c55,2 +np.float64,0xbfb8b406a6316810,0x3ffaadd97062fb8c,2 +np.float64,0xbfecf98384f9f307,0x4005a043d9110d79,2 +np.float64,0xbfe5834bab6b0698,0x400276f114aebee4,2 +np.float64,0xbfd90f391eb21e72,0x3fff91e26a8f48f3,2 +np.float64,0xbfee288ce2fc511a,0x400667cb09aa04b3,2 +np.float64,0x3fd5aa5e32ab54bc,0x3ff39b7080a52214,2 +np.float64,0xbfee7ef907fcfdf2,0x4006ab96a8eba4c5,2 +np.float64,0x3fd6097973ac12f4,0x3ff3822486978bd1,2 +np.float64,0xbfe02d14b8e05a2a,0x4000ce5be53047b1,2 +np.float64,0xbf9c629a6838c540,0x3ff993897728c3f9,2 +np.float64,0xbfee2024667c4049,0x40066188782fb1f0,2 +np.float64,0xbfa42a88fc285510,0x3ff9c35a4bbce104,2 +np.float64,0x3fa407af5c280f60,0x3ff881b360d8eea1,2 +np.float64,0x3fed0ba42cfa1748,0x3fdbb7d55609175f,2 +np.float64,0xbfdd0b5844ba16b0,0x400055b0bb59ebb2,2 +np.float64,0x3fd88d97e6b11b30,0x3ff2d53c1ecb8f8c,2 +np.float64,0xbfeb7a915ef6f523,0x4004d410812eb84c,2 +np.float64,0xbfb5f979ca2bf2f0,0x3ffa8201d73cd4ca,2 +np.float64,0x3fb3b65dd6276cc0,0x3ff7e64576199505,2 +np.float64,0x3fcd47a7793a8f50,0x3ff570a7b672f160,2 +np.float64,0xbfa41dd30c283ba0,0x3ff9c2f488127eb3,2 +np.float64,0x3fe4b1ea1f6963d4,0x3febc2bed7760427,2 +np.float64,0xbfdd0f81d2ba1f04,0x400056463724b768,2 +np.float64,0x3fd15d93f7a2bb28,0x3ff4bc7a24eacfd7,2 +np.float64,0xbfe3213af8e64276,0x4001b14579dfded3,2 +np.float64,0x3fd90dfbeab21bf8,0x3ff2b26a6c2c3bb3,2 +np.float64,0xbfd02d54bca05aaa,0x3ffd38ab3886b203,2 +np.float64,0x3fc218dcad2431b8,0x3ff6dced56d5b417,2 +np.float64,0x3fea5edf71f4bdbe,0x3fe3455ee09f27e6,2 +np.float64,0x3fa74319042e8640,0x3ff867d224545438,2 +np.float64,0x3fd970ad92b2e15c,0x3ff2979084815dc1,2 +np.float64,0x3fce0a4bf73c1498,0x3ff557a4df32df3e,2 +np.float64,0x3fef5c8e10feb91c,0x3fc99ca0eeaaebe4,2 +np.float64,0xbfedae997ffb5d33,0x400611af18f407ab,2 +np.float64,0xbfbcf07d6239e0f8,0x3ffaf201177a2d36,2 +np.float64,0xbfc3c52541278a4c,0x3ffb9d2af0408e4a,2 +np.float64,0x3fe4ef44e4e9de8a,0x3feb71f7331255e5,2 +np.float64,0xbfccd9f5f539b3ec,0x3ffcc53a99339592,2 +np.float64,0xbfda32c745b4658e,0x3fffe16e8727ef89,2 +np.float64,0xbfef54932a7ea926,0x40077e4605e61ca1,2 +np.float64,0x3fe9d4ae3573a95c,0x3fe4344a069a3fd0,2 +np.float64,0x3fda567e73b4acfc,0x3ff258bd77a663c7,2 +np.float64,0xbfd5bcac5eab7958,0x3ffead6379c19c52,2 +np.float64,0xbfee5e56f97cbcae,0x40069131fc54018d,2 +np.float64,0x3fc2d4413925a880,0x3ff6c54163816298,2 +np.float64,0xbfe9ddf6e873bbee,0x400418d8c722f7c5,2 +np.float64,0x3fdaf2a683b5e54c,0x3ff22dcda599d69c,2 +np.float64,0xbfca69789f34d2f0,0x3ffc7547ff10b1a6,2 +np.float64,0x3fed076f62fa0ede,0x3fdbcbda03c1d72a,2 +np.float64,0xbfcb38326f367064,0x3ffc8fb55dadeae5,2 +np.float64,0x3fe1938705e3270e,0x3fefa88130c5adda,2 +np.float64,0x3feaffae3b75ff5c,0x3fe221e3da537c7e,2 +np.float64,0x3fefc94acb7f9296,0x3fbd9a360ace67b4,2 +np.float64,0xbfe8bddeb0f17bbe,0x4003a316685c767e,2 +np.float64,0x3fbe10fbee3c21f0,0x3ff73fceb10650f5,2 +np.float64,0x3fde9126c1bd224c,0x3ff12a742f734d0a,2 +np.float64,0xbfe9686c91f2d0d9,0x4003e7bc6ee77906,2 +np.float64,0xbfb1ba4892237490,0x3ffa3dda064c2509,2 +np.float64,0xbfe2879100e50f22,0x400181c1a5b16f0f,2 +np.float64,0x3fd1cd40b6a39a80,0x3ff49f70e3064e95,2 +np.float64,0xbfc965869132cb0c,0x3ffc5419f3b43701,2 +np.float64,0x3fea7a6f2874f4de,0x3fe31480fb2dd862,2 +np.float64,0x3fc3bc56892778b0,0x3ff6a7e8fa0e8b0e,2 +np.float64,0x3fec1ed451f83da8,0x3fdfd78e564b8ad7,2 +np.float64,0x3feb77d16df6efa2,0x3fe13d083344e45e,2 +np.float64,0xbfe822e7c67045d0,0x400367104a830cf6,2 +np.float64,0x8000000000000001,0x3ff921fb54442d18,2 +np.float64,0xbfd4900918a92012,0x3ffe5dc0e19737b4,2 +np.float64,0x3fed184187fa3084,0x3fdb7b7a39f234f4,2 +np.float64,0x3fecef846179df08,0x3fdc3cb2228c3682,2 +np.float64,0xbfe2d2aed165a55e,0x400198e21c5b861b,2 +np.float64,0x7ff0000000000000,0x7ff8000000000000,2 +np.float64,0xbfee9409a07d2813,0x4006bd358232d073,2 +np.float64,0xbfecedc2baf9db86,0x4005995df566fc21,2 +np.float64,0x3fe6d857396db0ae,0x3fe8d2cb8794aa99,2 +np.float64,0xbf9a579e7834af40,0x3ff98b5cc8021e1c,2 +np.float64,0x3fc664fefb2cca00,0x3ff651a664ccf8fa,2 +np.float64,0xbfe8a7aa0e714f54,0x40039a5b4df938a0,2 +np.float64,0xbfdf27d380be4fa8,0x4000a241074dbae6,2 +np.float64,0x3fe00ddf55e01bbe,0x3ff0b94eb1ea1851,2 +np.float64,0x3feb47edbff68fdc,0x3fe199822d075959,2 +np.float64,0x3fb4993822293270,0x3ff7d80c838186d0,2 +np.float64,0xbfca2cd1473459a4,0x3ffc6d88c8de3d0d,2 +np.float64,0xbfea7d9c7674fb39,0x40045e4559e9e52d,2 +np.float64,0x3fe0dce425e1b9c8,0x3ff04099cab23289,2 +np.float64,0x3fd6bb7e97ad76fc,0x3ff352a30434499c,2 +np.float64,0x3fd4a4f16da949e4,0x3ff3e0b07432c9aa,2 +np.float64,0x8000000000000000,0x3ff921fb54442d18,2 +np.float64,0x3fe688f5b56d11ec,0x3fe9435f63264375,2 +np.float64,0xbfdf5a427ebeb484,0x4000a97a6c5d4abc,2 +np.float64,0xbfd1f3483fa3e690,0x3ffdae6c8a299383,2 +np.float64,0xbfeac920db759242,0x4004805862be51ec,2 +np.float64,0x3fef5bc711feb78e,0x3fc9ac40fba5b93b,2 +np.float64,0x3fe4bd9e12e97b3c,0x3febb363c787d381,2 +np.float64,0x3fef6a59ab7ed4b4,0x3fc880f1324eafce,2 +np.float64,0x3fc07a362120f470,0x3ff7113cf2c672b3,2 +np.float64,0xbfe4d6dbe2e9adb8,0x40023d6f6bea44b7,2 +np.float64,0xbfec2d6a15785ad4,0x40052eb425cc37a2,2 +np.float64,0x3fc90dae05321b60,0x3ff5fb10015d2934,2 +np.float64,0xbfa9239f74324740,0x3ff9eb2d057068ea,2 +np.float64,0xbfeb4fc8baf69f92,0x4004bf5e17fb08a4,2 +np.float64,0x0,0x3ff921fb54442d18,2 +np.float64,0x3faaf1884c35e320,0x3ff84a5591dbe1f3,2 +np.float64,0xbfed842561fb084b,0x4005f5c0a19116ce,2 +np.float64,0xbfc64850c32c90a0,0x3ffbeeac2ee70f9a,2 +np.float64,0x3fd7d879f5afb0f4,0x3ff306254c453436,2 +np.float64,0xbfdabaa586b5754c,0x4000035e6ac83a2b,2 +np.float64,0xbfebfeefa977fddf,0x4005167446fb9faf,2 +np.float64,0xbfe9383462727069,0x4003d407aa6a1577,2 +np.float64,0x3fe108dfb6e211c0,0x3ff026ac924b281d,2 +np.float64,0xbf85096df02a12c0,0x3ff94c0e60a22ede,2 +np.float64,0xbfe3121cd566243a,0x4001ac8f90db5882,2 +np.float64,0xbfd227f62aa44fec,0x3ffdbc26bb175dcc,2 +np.float64,0x3fd931af2cb26360,0x3ff2a8b62dfe003c,2 +np.float64,0xbfd9b794e3b36f2a,0x3fffbfbc89ec013d,2 +np.float64,0x3fc89b2e6f313660,0x3ff609a6e67f15f2,2 +np.float64,0x3fc0b14a8f216298,0x3ff70a4b6905aad2,2 +np.float64,0xbfeda11a657b4235,0x400608b3f9fff574,2 +np.float64,0xbfed2ee9ec7a5dd4,0x4005c040b7c02390,2 +np.float64,0xbfef7819d8fef034,0x4007ac6bf75cf09d,2 +np.float64,0xbfcc4720fb388e40,0x3ffcb2666a00b336,2 +np.float64,0xbfe05dec4be0bbd8,0x4000dc8a25ca3760,2 +np.float64,0x3fb093416e212680,0x3ff81897b6d8b374,2 +np.float64,0xbfc6ab89332d5714,0x3ffbfb4559d143e7,2 +np.float64,0x3fc51948512a3290,0x3ff67bb9df662c0a,2 +np.float64,0x3fed4d94177a9b28,0x3fda76c92f0c0132,2 +np.float64,0x3fdd195fbeba32c0,0x3ff194a5586dd18e,2 +np.float64,0x3fe3f82799e7f050,0x3fecb354c2faf55c,2 +np.float64,0x3fecac2169f95842,0x3fdd7222296cb7a7,2 +np.float64,0x3fe3d3f36fe7a7e6,0x3fece18f45e30dd7,2 +np.float64,0x3fe31ff63d663fec,0x3fedc46c77d30c6a,2 +np.float64,0xbfe3120c83e62419,0x4001ac8a7c4aa742,2 +np.float64,0x3fe7c1a7976f8350,0x3fe77e4a9307c9f8,2 +np.float64,0x3fe226fe9de44dfe,0x3feef6c0f3cb00fa,2 +np.float64,0x3fd5c933baab9268,0x3ff3933e8a37de42,2 +np.float64,0x3feaa98496f5530a,0x3fe2c003832ebf21,2 +np.float64,0xbfc6f80a2f2df014,0x3ffc04fd54cb1317,2 +np.float64,0x3fde5e18d0bcbc30,0x3ff138f7b32a2ca3,2 +np.float64,0xbfe30c8dd566191c,0x4001aad4af935a78,2 +np.float64,0x3fbe8d196e3d1a30,0x3ff737fec8149ecc,2 +np.float64,0x3feaee6731f5dcce,0x3fe241fa42cce22d,2 +np.float64,0x3fef9cc46cff3988,0x3fc3f17b708dbdbb,2 +np.float64,0xbfdb181bdeb63038,0x4000103ecf405602,2 +np.float64,0xbfc58de0ed2b1bc0,0x3ffbd704c14e15cd,2 +np.float64,0xbfee05d5507c0bab,0x40064e480faba6d8,2 +np.float64,0x3fe27d0ffa64fa20,0x3fee8dc71ef79f2c,2 +np.float64,0xbfe4f7ad4c69ef5a,0x400248456cd09a07,2 +np.float64,0xbfe4843e91e9087d,0x4002225f3e139c84,2 +np.float64,0x3fe7158b9c6e2b18,0x3fe87ae845c5ba96,2 +np.float64,0xbfea64316074c863,0x400452fd2bc23a44,2 +np.float64,0xbfc9f3ae4133e75c,0x3ffc663d482afa42,2 +np.float64,0xbfd5e18513abc30a,0x3ffeb72fc76d7071,2 +np.float64,0xbfd52f6438aa5ec8,0x3ffe87e5b18041e5,2 +np.float64,0xbfea970650f52e0d,0x400469a4a6758154,2 +np.float64,0xbfe44321b7e88644,0x40020d404a2141b1,2 +np.float64,0x3fdf5a39bbbeb474,0x3ff0f10453059dbd,2 +np.float64,0xbfa1d4069423a810,0x3ff9b0a2eacd2ce2,2 +np.float64,0xbfc36d16a326da2c,0x3ffb92077d41d26a,2 +np.float64,0x1,0x3ff921fb54442d18,2 +np.float64,0x3feb232a79764654,0x3fe1df5beeb249d0,2 +np.float64,0xbfed2003d5fa4008,0x4005b737c2727583,2 +np.float64,0x3fd5b093a3ab6128,0x3ff399ca2db1d96d,2 +np.float64,0x3fca692c3d34d258,0x3ff5ceb86b79223e,2 +np.float64,0x3fd6bbdf89ad77c0,0x3ff3528916df652d,2 +np.float64,0xbfefdadd46ffb5bb,0x40085ee735e19f19,2 +np.float64,0x3feb69fb2676d3f6,0x3fe157ee0c15691e,2 +np.float64,0x3fe44c931f689926,0x3fec46b6f5e3f265,2 +np.float64,0xbfc43ddbcb287bb8,0x3ffbac71d268d74d,2 +np.float64,0x3fe6e16d43edc2da,0x3fe8c5cf0f0daa66,2 +np.float64,0x3fe489efc76913e0,0x3febf704ca1ac2a6,2 +np.float64,0xbfe590aadceb2156,0x40027b764205cf78,2 +np.float64,0xbf782e8aa0305d00,0x3ff93a29e81928ab,2 +np.float64,0x3fedcb80cffb9702,0x3fd7e5d1f98a418b,2 +np.float64,0x3fe075858060eb0c,0x3ff07d23ab46b60f,2 +np.float64,0x3fe62a68296c54d0,0x3fe9c77f7068043b,2 +np.float64,0x3feff16a3c7fe2d4,0x3fae8e8a739cc67a,2 +np.float64,0xbfd6ed93e3addb28,0x3ffefebab206fa99,2 +np.float64,0x3fe40d8ccf681b1a,0x3fec97e9cd29966d,2 +np.float64,0x3fd6408210ac8104,0x3ff3737a7d374107,2 +np.float64,0x3fec8023b8f90048,0x3fde35ebfb2b3afd,2 +np.float64,0xbfe13babd4627758,0x40011dae5c07c56b,2 +np.float64,0xbfd2183e61a4307c,0x3ffdb80dd747cfbe,2 +np.float64,0x3feae8eb1d75d1d6,0x3fe24c1f6e42ae77,2 +np.float64,0xbfea559b9c74ab37,0x40044c8e5e123b20,2 +np.float64,0xbfd12c9d57a2593a,0x3ffd7ac6222f561c,2 +np.float64,0x3fe32eb697e65d6e,0x3fedb202693875b6,2 +np.float64,0xbfde0808c3bc1012,0x4000794bd8616ea3,2 +np.float64,0x3fe14958a06292b2,0x3ff0007b40ac648a,2 +np.float64,0x3fe3d388a6e7a712,0x3fece21751a6dd7c,2 +np.float64,0x3fe7ad7897ef5af2,0x3fe79c5b3da302a7,2 +np.float64,0x3fec75527e78eaa4,0x3fde655de0cf0508,2 +np.float64,0x3fea920d4c75241a,0x3fe2ea48f031d908,2 +np.float64,0x7fefffffffffffff,0x7ff8000000000000,2 +np.float64,0xbfc17a68cb22f4d0,0x3ffb530925f41aa0,2 +np.float64,0xbfe1c93166e39263,0x400147f3cb435dec,2 +np.float64,0x3feb97c402f72f88,0x3fe0fe5b561bf869,2 +np.float64,0x3fb58ff5162b1ff0,0x3ff7c8933fa969dc,2 +np.float64,0x3fe68e2beded1c58,0x3fe93c075283703b,2 +np.float64,0xbf94564cc828aca0,0x3ff97355e5ee35db,2 +np.float64,0x3fd31061c9a620c4,0x3ff44b150ec96998,2 +np.float64,0xbfc7d0c89f2fa190,0x3ffc208bf4eddc4d,2 +np.float64,0x3fe5736f1d6ae6de,0x3feac18f84992d1e,2 +np.float64,0x3fdb62e480b6c5c8,0x3ff20ecfdc4afe7c,2 +np.float64,0xbfc417228b282e44,0x3ffba78afea35979,2 +np.float64,0x3f8f5ba1303eb780,0x3ff8e343714630ff,2 +np.float64,0x3fe8e99126f1d322,0x3fe5b6511d4c0798,2 +np.float64,0xbfe2ec08a1e5d812,0x4001a0bb28a85875,2 +np.float64,0x3fea3b46cf74768e,0x3fe383dceaa74296,2 +np.float64,0xbfe008b5ed60116c,0x4000c3d62c275d40,2 +np.float64,0xbfcd9f8a4b3b3f14,0x3ffcde98d6484202,2 +np.float64,0xbfdb5fb112b6bf62,0x40001a22137ef1c9,2 +np.float64,0xbfe9079565f20f2b,0x4003c0670c92e401,2 +np.float64,0xbfce250dc53c4a1c,0x3ffcefc2b3dc3332,2 +np.float64,0x3fe9ba85d373750c,0x3fe4607131b28773,2 +np.float64,0x10000000000000,0x3ff921fb54442d18,2 +np.float64,0xbfeb9ef42c773de8,0x4004e5f239203ad8,2 +np.float64,0xbfd6bf457dad7e8a,0x3ffef2563d87b18d,2 +np.float64,0x3fe4de9aa5e9bd36,0x3feb87f97defb04a,2 +np.float64,0x3fedb4f67cfb69ec,0x3fd8603c465bffac,2 +np.float64,0x3fe7b6d9506f6db2,0x3fe78e670c7bdb67,2 +np.float64,0x3fe071717460e2e2,0x3ff07f84472d9cc5,2 +np.float64,0xbfed2e79dbfa5cf4,0x4005bffc6f9ad24f,2 +np.float64,0x3febb8adc377715c,0x3fe0bcebfbd45900,2 +np.float64,0xbfee2cffd87c5a00,0x40066b20a037c478,2 +np.float64,0x3fef7e358d7efc6c,0x3fc6d0ba71a542a8,2 +np.float64,0xbfef027eef7e04fe,0x400723291cb00a7a,2 +np.float64,0x3fac96da34392dc0,0x3ff83d260a936c6a,2 +np.float64,0x3fe9dba94a73b752,0x3fe428736b94885e,2 +np.float64,0x3fed37581efa6eb0,0x3fdae49dcadf1d90,2 +np.float64,0xbfe6e61037edcc20,0x4002f23031b8d522,2 +np.float64,0xbfdea7204dbd4e40,0x40008fe1f37918b7,2 +np.float64,0x3feb9f8edb773f1e,0x3fe0eef20bd4387b,2 +np.float64,0x3feeb0b6ed7d616e,0x3fd25fb3b7a525d6,2 +np.float64,0xbfd7ce9061af9d20,0x3fff3b25d531aa2b,2 +np.float64,0xbfc806b509300d6c,0x3ffc2768743a8360,2 +np.float64,0xbfa283882c250710,0x3ff9b61fda28914a,2 +np.float64,0x3fdec70050bd8e00,0x3ff11b1d769b578f,2 +np.float64,0xbfc858a44930b148,0x3ffc31d6758b4721,2 +np.float64,0x3fdc321150b86424,0x3ff1d5504c3c91e4,2 +np.float64,0x3fd9416870b282d0,0x3ff2a46f3a850f5b,2 +np.float64,0x3fdd756968baead4,0x3ff17ac510a5573f,2 +np.float64,0xbfedfd632cfbfac6,0x400648345a2f89b0,2 +np.float64,0x3fd6874285ad0e84,0x3ff36098ebff763f,2 +np.float64,0x3fe6daacc9edb55a,0x3fe8cf75fae1e35f,2 +np.float64,0x3fe53f19766a7e32,0x3feb07d0e97cd55b,2 +np.float64,0x3fd13cc36ca27988,0x3ff4c4ff801b1faa,2 +np.float64,0x3fe4f21cbce9e43a,0x3feb6e34a72ef529,2 +np.float64,0xbfc21c1cc9243838,0x3ffb67726394ca89,2 +np.float64,0x3fe947a3f2728f48,0x3fe51eae4660e23c,2 +np.float64,0xbfce78cd653cf19c,0x3ffcfa89194b3f5e,2 +np.float64,0x3fe756f049eeade0,0x3fe81be7f2d399e2,2 +np.float64,0xbfcc727cf138e4f8,0x3ffcb7f547841bb0,2 +np.float64,0xbfc2d8d58f25b1ac,0x3ffb7f496cc72458,2 +np.float64,0xbfcfd0e4653fa1c8,0x3ffd26e1309bc80b,2 +np.float64,0xbfe2126c106424d8,0x40015e0e01db6a4a,2 +np.float64,0x3fe580e4306b01c8,0x3feaaf683ce51aa5,2 +np.float64,0x3fcea8a1b93d5140,0x3ff543456c0d28c7,2 +np.float64,0xfff0000000000000,0x7ff8000000000000,2 +np.float64,0xbfd9d5da72b3abb4,0x3fffc8013113f968,2 +np.float64,0xbfe1fdfcea63fbfa,0x400157def2e4808d,2 +np.float64,0xbfc0022e0720045c,0x3ffb239963e7cbf2,2 diff --git a/numpy/core/tests/data/umath-validation-set-arccosh.csv b/numpy/core/tests/data/umath-validation-set-arccosh.csv new file mode 100644 index 000000000000..0defe50be4ae --- /dev/null +++ b/numpy/core/tests/data/umath-validation-set-arccosh.csv @@ -0,0 +1,1429 @@ +dtype,input,output,ulperrortol +np.float32,0x3f83203f,0x3e61d9d6,2 +np.float32,0x3f98dea1,0x3f1d1af6,2 +np.float32,0x7fa00000,0x7fe00000,2 +np.float32,0x7eba99af,0x42b0d032,2 +np.float32,0x3fc95a13,0x3f833650,2 +np.float32,0x3fce9a45,0x3f8771e1,2 +np.float32,0x3fc1bd96,0x3f797811,2 +np.float32,0x7eba2391,0x42b0ceed,2 +np.float32,0x7d4e8f15,0x42acdb8c,2 +np.float32,0x3feca42e,0x3f9cc88e,2 +np.float32,0x7e2b314e,0x42af412e,2 +np.float32,0x7f7fffff,0x42b2d4fc,2 +np.float32,0x3f803687,0x3d6c4380,2 +np.float32,0x3fa0edbd,0x3f33e706,2 +np.float32,0x3faa8074,0x3f4b3d3c,2 +np.float32,0x3fa0c49e,0x3f337af3,2 +np.float32,0x3f8c9ec4,0x3ee18812,2 +np.float32,0x7efef78e,0x42b17006,2 +np.float32,0x3fc75720,0x3f818aa4,2 +np.float32,0x7f52d4c8,0x42b27198,2 +np.float32,0x3f88f21e,0x3ebe52b0,2 +np.float32,0x3ff7a042,0x3fa3a07a,2 +np.float32,0x7f52115c,0x42b26fbd,2 +np.float32,0x3fc6bf6f,0x3f810b42,2 +np.float32,0x3fd105d0,0x3f895649,2 +np.float32,0x3fee7c2a,0x3f9df66e,2 +np.float32,0x7f0ff9a5,0x42b1ae4f,2 +np.float32,0x7e81f075,0x42b016e7,2 +np.float32,0x3fa57d65,0x3f3f70c6,2 +np.float32,0x80800000,0xffc00000,2 +np.float32,0x7da239f5,0x42adc2bf,2 +np.float32,0x3f9e432c,0x3f2cbd80,2 +np.float32,0x3ff2839b,0x3fa07ee4,2 +np.float32,0x3fec8aef,0x3f9cb850,2 +np.float32,0x7d325893,0x42ac905b,2 +np.float32,0x3fa27431,0x3f37dade,2 +np.float32,0x3fce7408,0x3f8753ae,2 +np.float32,0x3fde6684,0x3f93353f,2 +np.float32,0x3feb9a3e,0x3f9c1cff,2 +np.float32,0x7deb34bb,0x42ae80f0,2 +np.float32,0x3fed9300,0x3f9d61b7,2 +np.float32,0x7f35e253,0x42b225fb,2 +np.float32,0x7e6db57f,0x42afe93f,2 +np.float32,0x3fa41f08,0x3f3c10bc,2 +np.float32,0x3fb0d4da,0x3f590de3,2 +np.float32,0x3fb5c690,0x3f632351,2 +np.float32,0x3fcde9ce,0x3f86e638,2 +np.float32,0x3f809c7b,0x3dc81161,2 +np.float32,0x3fd77291,0x3f8e3226,2 +np.float32,0x3fc21a06,0x3f7a1a82,2 +np.float32,0x3fba177e,0x3f6b8139,2 +np.float32,0x7f370dff,0x42b22944,2 +np.float32,0x3fe5bfcc,0x3f9841c1,2 +np.float32,0x3feb0caa,0x3f9bc139,2 +np.float32,0x7f4fe5c3,0x42b26a6c,2 +np.float32,0x7f1e1419,0x42b1de28,2 +np.float32,0x7f5e3c96,0x42b28c92,2 +np.float32,0x3f8cd313,0x3ee3521e,2 +np.float32,0x3fa97824,0x3f48e049,2 +np.float32,0x7d8ca281,0x42ad799e,2 +np.float32,0x3f96b51b,0x3f165193,2 +np.float32,0x3f81328a,0x3e0bf504,2 +np.float32,0x3ff60bf3,0x3fa2ab45,2 +np.float32,0x3ff9b629,0x3fa4e107,2 +np.float32,0x3fecacfc,0x3f9cce37,2 +np.float32,0x3fba8804,0x3f6c5600,2 +np.float32,0x3f81f752,0x3e333fdd,2 +np.float32,0x3fb5b262,0x3f62fb46,2 +np.float32,0x3fa21bc0,0x3f36f7e6,2 +np.float32,0x3fbc87bb,0x3f7011dc,2 +np.float32,0x3fe18b32,0x3f9565ae,2 +np.float32,0x7dfb6dd5,0x42aea316,2 +np.float32,0x3fb7c602,0x3f670ee3,2 +np.float32,0x7efeb6a2,0x42b16f84,2 +np.float32,0x3fa56180,0x3f3f2ca4,2 +np.float32,0x3f8dcaff,0x3eeb9ac0,2 +np.float32,0x7e876238,0x42b02beb,2 +np.float32,0x7f0bb67d,0x42b19eec,2 +np.float32,0x3faca01c,0x3f4fffa5,2 +np.float32,0x3fdb57ee,0x3f9108b8,2 +np.float32,0x3fe3bade,0x3f96e4b7,2 +np.float32,0x7f7aa2dd,0x42b2ca25,2 +np.float32,0x3fed92ec,0x3f9d61aa,2 +np.float32,0x7eb789b1,0x42b0c7b9,2 +np.float32,0x7f7f16e4,0x42b2d329,2 +np.float32,0x3fb6647e,0x3f645b84,2 +np.float32,0x3f99335e,0x3f1e1d96,2 +np.float32,0x7e690a11,0x42afdf17,2 +np.float32,0x7dff2f95,0x42aeaaae,2 +np.float32,0x7f70adfd,0x42b2b564,2 +np.float32,0x3fe92252,0x3f9a80fe,2 +np.float32,0x3fef54ce,0x3f9e7fe5,2 +np.float32,0x3ff24eaa,0x3fa05df9,2 +np.float32,0x7f04565a,0x42b18328,2 +np.float32,0x3fcb8b80,0x3f85007f,2 +np.float32,0x3fcd4d0a,0x3f866983,2 +np.float32,0x3fbe7d82,0x3f73a911,2 +np.float32,0x3f8a7a8a,0x3ecdc8f6,2 +np.float32,0x3f912441,0x3f030d56,2 +np.float32,0x3f9b29d6,0x3f23f663,2 +np.float32,0x3fab7f36,0x3f4d7c6c,2 +np.float32,0x7dfedafc,0x42aeaa04,2 +np.float32,0x3fe190c0,0x3f956982,2 +np.float32,0x3f927515,0x3f07e0bb,2 +np.float32,0x3ff6442a,0x3fa2cd7e,2 +np.float32,0x7f6656d0,0x42b29ee8,2 +np.float32,0x3fe29aa0,0x3f96201f,2 +np.float32,0x3fa4a247,0x3f3d5687,2 +np.float32,0x3fa1cf19,0x3f363226,2 +np.float32,0x3fc20037,0x3f79ed36,2 +np.float32,0x7cc1241a,0x42ab5645,2 +np.float32,0x3fafd540,0x3f56f25a,2 +np.float32,0x7e5b3f5f,0x42afbfdb,2 +np.float32,0x7f48de5f,0x42b258d0,2 +np.float32,0x3fce1ca0,0x3f870e85,2 +np.float32,0x7ee40bb2,0x42b136e4,2 +np.float32,0x7ecdb133,0x42b10212,2 +np.float32,0x3f9f181c,0x3f2f02ca,2 +np.float32,0x3f936cbf,0x3f0b4f63,2 +np.float32,0x3fa4f8ea,0x3f3e2c2f,2 +np.float32,0x3fcc03e2,0x3f8561ac,2 +np.float32,0x3fb801f2,0x3f67831b,2 +np.float32,0x7e141dad,0x42aef70c,2 +np.float32,0x3fe8c04e,0x3f9a4087,2 +np.float32,0x3f8548d5,0x3e929f37,2 +np.float32,0x7f148d7d,0x42b1be56,2 +np.float32,0x3fd2c9a2,0x3f8ab1ed,2 +np.float32,0x7eb374fd,0x42b0bc36,2 +np.float32,0x7f296d36,0x42b201a7,2 +np.float32,0x3ff138e2,0x3f9fb09d,2 +np.float32,0x3ff42898,0x3fa18347,2 +np.float32,0x7da8c5e1,0x42add700,2 +np.float32,0x7dcf72c4,0x42ae40a4,2 +np.float32,0x7ea571fc,0x42b09296,2 +np.float32,0x3fc0953d,0x3f776ba3,2 +np.float32,0x7f1773dd,0x42b1c83c,2 +np.float32,0x7ef53b68,0x42b15c17,2 +np.float32,0x3f85d69f,0x3e9a0f3a,2 +np.float32,0x7e8b9a05,0x42b03ba0,2 +np.float32,0x3ff07d20,0x3f9f3ad2,2 +np.float32,0x7e8da32c,0x42b0430a,2 +np.float32,0x7ef96004,0x42b164ab,2 +np.float32,0x3fdfaa62,0x3f941837,2 +np.float32,0x7f0057c5,0x42b17377,2 +np.float32,0x3fb2663f,0x3f5c5065,2 +np.float32,0x3fd3d8c3,0x3f8b8055,2 +np.float32,0x1,0xffc00000,2 +np.float32,0x3fd536c1,0x3f8c8862,2 +np.float32,0x3f91b953,0x3f053619,2 +np.float32,0x3fb3305c,0x3f5deee1,2 +np.float32,0x7ecd86b9,0x42b101a8,2 +np.float32,0x3fbf71c5,0x3f75624d,2 +np.float32,0x3ff5f0f4,0x3fa29ad2,2 +np.float32,0x3fe50389,0x3f97c328,2 +np.float32,0x3fa325a1,0x3f399e69,2 +np.float32,0x3fe4397a,0x3f973a9f,2 +np.float32,0x3f8684c6,0x3ea2b784,2 +np.float32,0x7f25ae00,0x42b1f634,2 +np.float32,0x3ff7cbf7,0x3fa3badb,2 +np.float32,0x7f73f0e0,0x42b2bc48,2 +np.float32,0x3fc88b70,0x3f828b92,2 +np.float32,0x3fb01c16,0x3f578886,2 +np.float32,0x7e557623,0x42afb229,2 +np.float32,0x3fcbcd5b,0x3f8535b4,2 +np.float32,0x7f7157e4,0x42b2b6cd,2 +np.float32,0x7f51d9d4,0x42b26f36,2 +np.float32,0x7f331a3b,0x42b21e17,2 +np.float32,0x7f777fb5,0x42b2c3b2,2 +np.float32,0x3f832001,0x3e61d11f,2 +np.float32,0x7f2cd055,0x42b20bca,2 +np.float32,0x3f89831f,0x3ec42f76,2 +np.float32,0x7f21da33,0x42b1ea3d,2 +np.float32,0x3f99e416,0x3f20330a,2 +np.float32,0x7f2c8ea1,0x42b20b07,2 +np.float32,0x7f462c98,0x42b251e6,2 +np.float32,0x7f4fdb3f,0x42b26a52,2 +np.float32,0x3fcc1338,0x3f856e07,2 +np.float32,0x3f823673,0x3e3e20da,2 +np.float32,0x7dbfe89d,0x42ae18c6,2 +np.float32,0x3fc9b04c,0x3f837d38,2 +np.float32,0x7dba3213,0x42ae094d,2 +np.float32,0x7ec5a483,0x42b0eda1,2 +np.float32,0x3fbc4d14,0x3f6fa543,2 +np.float32,0x3fc85ce2,0x3f8264f1,2 +np.float32,0x7f77c816,0x42b2c447,2 +np.float32,0x3f9c9281,0x3f280492,2 +np.float32,0x7f49b3e2,0x42b25aef,2 +np.float32,0x3fa7e4da,0x3f45347c,2 +np.float32,0x7e0c9df5,0x42aedc72,2 +np.float32,0x7f21fd1a,0x42b1eaab,2 +np.float32,0x7f7c63ad,0x42b2cdb6,2 +np.float32,0x7f4eb80a,0x42b26783,2 +np.float32,0x7e98038c,0x42b0673c,2 +np.float32,0x7e89ba08,0x42b034b4,2 +np.float32,0x3ffc06ba,0x3fa64094,2 +np.float32,0x3fae63f6,0x3f53db36,2 +np.float32,0x3fbc2d30,0x3f6f6a1c,2 +np.float32,0x7de0e5e5,0x42ae69fe,2 +np.float32,0x7e09ed18,0x42aed28d,2 +np.float32,0x3fea78f8,0x3f9b6129,2 +np.float32,0x7dfe0bcc,0x42aea863,2 +np.float32,0x7ee21d03,0x42b13289,2 +np.float32,0x3fcc3aed,0x3f858dfc,2 +np.float32,0x3fe6b3ba,0x3f98e4ea,2 +np.float32,0x3f90f25f,0x3f025225,2 +np.float32,0x7f1bcaf4,0x42b1d6b3,2 +np.float32,0x3f83ac81,0x3e74c20e,2 +np.float32,0x3f98681d,0x3f1bae16,2 +np.float32,0x3fe1f2d9,0x3f95ad08,2 +np.float32,0x3fa279d7,0x3f37e951,2 +np.float32,0x3feb922a,0x3f9c17c4,2 +np.float32,0x7f1c72e8,0x42b1d8da,2 +np.float32,0x3fea156b,0x3f9b2038,2 +np.float32,0x3fed6bda,0x3f9d48aa,2 +np.float32,0x3fa86142,0x3f46589c,2 +np.float32,0x3ff16bc2,0x3f9fd072,2 +np.float32,0x3fbebf65,0x3f74207b,2 +np.float32,0x7e7b78b5,0x42b00610,2 +np.float32,0x3ff51ab8,0x3fa217f0,2 +np.float32,0x3f8361bb,0x3e6adf07,2 +np.float32,0x7edbceed,0x42b1240e,2 +np.float32,0x7f10e2c0,0x42b1b18a,2 +np.float32,0x3fa7bc58,0x3f44d4ef,2 +np.float32,0x3f813bde,0x3e0e1138,2 +np.float32,0x7f30d5b9,0x42b21791,2 +np.float32,0x3fb4f450,0x3f61806a,2 +np.float32,0x7eee02c4,0x42b14cca,2 +np.float32,0x7ec74b62,0x42b0f1e4,2 +np.float32,0x3ff96bca,0x3fa4b498,2 +np.float32,0x7f50e304,0x42b26cda,2 +np.float32,0x7eb14c57,0x42b0b603,2 +np.float32,0x7c3f0733,0x42a9edbf,2 +np.float32,0x7ea57acb,0x42b092b1,2 +np.float32,0x7f2788dc,0x42b1fbe7,2 +np.float32,0x3fa39f14,0x3f3ad09b,2 +np.float32,0x3fc3a7e0,0x3f7ccfa0,2 +np.float32,0x3fe70a73,0x3f991eb0,2 +np.float32,0x7f4831f7,0x42b25718,2 +np.float32,0x3fe947d0,0x3f9a999c,2 +np.float32,0x7ef2b1c7,0x42b156c4,2 +np.float32,0x3fede0ea,0x3f9d937f,2 +np.float32,0x3f9fef8e,0x3f314637,2 +np.float32,0x3fc313c5,0x3f7bcebd,2 +np.float32,0x7ee99337,0x42b14328,2 +np.float32,0x7eb9042e,0x42b0cbd5,2 +np.float32,0x3fc9d3dc,0x3f839a69,2 +np.float32,0x3fb2c018,0x3f5d091d,2 +np.float32,0x3fcc4e8f,0x3f859dc5,2 +np.float32,0x3fa9363b,0x3f484819,2 +np.float32,0x7f72ce2e,0x42b2b9e4,2 +np.float32,0x7e639326,0x42afd2f1,2 +np.float32,0x7f4595d3,0x42b25060,2 +np.float32,0x7f6d0ac4,0x42b2ad97,2 +np.float32,0x7f1bda0d,0x42b1d6e5,2 +np.float32,0x3fd85ffd,0x3f8ee0ed,2 +np.float32,0x3f91d53f,0x3f059c8e,2 +np.float32,0x7d06e103,0x42ac0155,2 +np.float32,0x3fb83126,0x3f67de6e,2 +np.float32,0x7d81ce1f,0x42ad5097,2 +np.float32,0x7f79cb3b,0x42b2c86b,2 +np.float32,0x7f800000,0x7f800000,2 +np.float32,0x3fdbfffd,0x3f918137,2 +np.float32,0x7f4ecb1c,0x42b267b2,2 +np.float32,0x3fc2c122,0x3f7b3ed3,2 +np.float32,0x7f415854,0x42b24544,2 +np.float32,0x7e3d988b,0x42af7575,2 +np.float32,0x3f83ca99,0x3e789fcb,2 +np.float32,0x7f274f70,0x42b1fb38,2 +np.float32,0x7f0d20e6,0x42b1a416,2 +np.float32,0x3fdf3a1d,0x3f93c9c1,2 +np.float32,0x7efaa13e,0x42b1673d,2 +np.float32,0x3fb20b15,0x3f5b9434,2 +np.float32,0x3f86af9f,0x3ea4c664,2 +np.float32,0x3fe4fcb0,0x3f97be8a,2 +np.float32,0x3f920683,0x3f065085,2 +np.float32,0x3fa4b278,0x3f3d7e8b,2 +np.float32,0x3f8077a8,0x3daef77f,2 +np.float32,0x7e865be4,0x42b02807,2 +np.float32,0x3fcea7e2,0x3f877c9f,2 +np.float32,0x7e7e9db1,0x42b00c6d,2 +np.float32,0x3f9819aa,0x3f1aba7e,2 +np.float32,0x7f2b6c4b,0x42b207a7,2 +np.float32,0x7ef85e3e,0x42b16299,2 +np.float32,0x3fbd8290,0x3f71df8b,2 +np.float32,0x3fbbb615,0x3f6e8c8c,2 +np.float32,0x7f1bc7f5,0x42b1d6a9,2 +np.float32,0x3fbb4fea,0x3f6dcdad,2 +np.float32,0x3fb67e09,0x3f648dd1,2 +np.float32,0x3fc83495,0x3f824374,2 +np.float32,0x3fe52980,0x3f97dcbc,2 +np.float32,0x3f87d893,0x3eb25d7c,2 +np.float32,0x3fdb805a,0x3f9125c0,2 +np.float32,0x3fb33f0f,0x3f5e0ce1,2 +np.float32,0x3facc524,0x3f50516b,2 +np.float32,0x3ff40484,0x3fa16d0e,2 +np.float32,0x3ff078bf,0x3f9f3811,2 +np.float32,0x7f736747,0x42b2bb27,2 +np.float32,0x7f55768b,0x42b277f3,2 +np.float32,0x80000001,0xffc00000,2 +np.float32,0x7f6463d1,0x42b29a8e,2 +np.float32,0x3f8f8b59,0x3ef9d792,2 +np.float32,0x3f8a6f4d,0x3ecd5bf4,2 +np.float32,0x3fe958d9,0x3f9aa4ca,2 +np.float32,0x7f1e2ce2,0x42b1de78,2 +np.float32,0x3fb8584a,0x3f682a05,2 +np.float32,0x7dea3dc6,0x42ae7ed5,2 +np.float32,0x7f53a815,0x42b27399,2 +np.float32,0x7e0cf986,0x42aeddbf,2 +np.float32,0x7f3afb71,0x42b23422,2 +np.float32,0x3fd87d6e,0x3f8ef685,2 +np.float32,0x3ffcaa46,0x3fa6a0d7,2 +np.float32,0x7eecd276,0x42b14a3a,2 +np.float32,0x3ffc30b4,0x3fa65951,2 +np.float32,0x7e9c85e2,0x42b07634,2 +np.float32,0x3f95d862,0x3f1383de,2 +np.float32,0x7ef21410,0x42b15577,2 +np.float32,0x3fbfa1b5,0x3f75b86e,2 +np.float32,0x3fd6d90f,0x3f8dc086,2 +np.float32,0x0,0xffc00000,2 +np.float32,0x7e885dcd,0x42b02f9f,2 +np.float32,0x3fb3e057,0x3f5f54bf,2 +np.float32,0x7f40afdd,0x42b24385,2 +np.float32,0x3fb795c2,0x3f66b120,2 +np.float32,0x3fba7c11,0x3f6c3f73,2 +np.float32,0x3ffef620,0x3fa7f828,2 +np.float32,0x7d430508,0x42acbe1e,2 +np.float32,0x3f8d2892,0x3ee6369f,2 +np.float32,0x3fbea139,0x3f73e9d5,2 +np.float32,0x3ffaa928,0x3fa571b9,2 +np.float32,0x7fc00000,0x7fc00000,2 +np.float32,0x7f16f9ce,0x42b1c69f,2 +np.float32,0x3fa8f753,0x3f47b657,2 +np.float32,0x3fd48a63,0x3f8c06ac,2 +np.float32,0x7f13419e,0x42b1b9d9,2 +np.float32,0x3fdf1526,0x3f93afde,2 +np.float32,0x3f903c8b,0x3eff3be8,2 +np.float32,0x7f085323,0x42b1925b,2 +np.float32,0x7cdbe309,0x42ab98ac,2 +np.float32,0x3fba2cfd,0x3f6ba9f1,2 +np.float32,0x7f5a805d,0x42b283e4,2 +np.float32,0x7f6753dd,0x42b2a119,2 +np.float32,0x3fed9f02,0x3f9d6964,2 +np.float32,0x3f96422c,0x3f14ddba,2 +np.float32,0x7f22f2a9,0x42b1edb1,2 +np.float32,0x3fe3fcfd,0x3f97119d,2 +np.float32,0x7e018ad0,0x42aeb271,2 +np.float32,0x7db896f5,0x42ae04de,2 +np.float32,0x7e55c795,0x42afb2ec,2 +np.float32,0x7f58ef8d,0x42b28036,2 +np.float32,0x7f24a16a,0x42b1f2f3,2 +np.float32,0x3fcf714c,0x3f881b09,2 +np.float32,0x3fcdd056,0x3f86d200,2 +np.float32,0x7f02fad0,0x42b17de0,2 +np.float32,0x7eeab877,0x42b145a9,2 +np.float32,0x3fd6029d,0x3f8d20f7,2 +np.float32,0x3fd4f8cd,0x3f8c59d6,2 +np.float32,0x3fb29d4a,0x3f5cc1a5,2 +np.float32,0x3fb11e2d,0x3f59a77a,2 +np.float32,0x7eded576,0x42b12b0e,2 +np.float32,0x7f26c2a5,0x42b1f988,2 +np.float32,0x3fb6165b,0x3f63c151,2 +np.float32,0x7f3bca47,0x42b23657,2 +np.float32,0x7d8c93bf,0x42ad7968,2 +np.float32,0x3f8ede02,0x3ef47176,2 +np.float32,0x3fbef762,0x3f7485b9,2 +np.float32,0x7f1419af,0x42b1bcc6,2 +np.float32,0x7d9e8c79,0x42adb701,2 +np.float32,0x3fa26336,0x3f37af63,2 +np.float32,0x7f5f5590,0x42b28f18,2 +np.float32,0x3fddc93a,0x3f92c651,2 +np.float32,0x3ff0a5fc,0x3f9f547f,2 +np.float32,0x3fb2f6b8,0x3f5d790e,2 +np.float32,0x3ffe59a4,0x3fa79d2c,2 +np.float32,0x7e4df848,0x42af9fde,2 +np.float32,0x3fb0ab3b,0x3f58b678,2 +np.float32,0x7ea54d47,0x42b09225,2 +np.float32,0x3fdd6404,0x3f927eb2,2 +np.float32,0x3f846dc0,0x3e864caa,2 +np.float32,0x7d046aee,0x42abf7e7,2 +np.float32,0x7f7c5a05,0x42b2cda3,2 +np.float32,0x3faf6126,0x3f55fb21,2 +np.float32,0x7f36a910,0x42b22829,2 +np.float32,0x3fdc7b36,0x3f91d938,2 +np.float32,0x3fff443e,0x3fa82577,2 +np.float32,0x7ee7154a,0x42b13daa,2 +np.float32,0x3f944742,0x3f0e435c,2 +np.float32,0x7f5b510a,0x42b285cc,2 +np.float32,0x3f9bc940,0x3f25c4d2,2 +np.float32,0x3fee4782,0x3f9dd4ea,2 +np.float32,0x3fcfc2dd,0x3f885aea,2 +np.float32,0x7eab65cf,0x42b0a4af,2 +np.float32,0x3f9cf908,0x3f292689,2 +np.float32,0x7ed35501,0x42b10feb,2 +np.float32,0x7dabb70a,0x42addfd9,2 +np.float32,0x7f348919,0x42b2222b,2 +np.float32,0x3fb137d4,0x3f59dd17,2 +np.float32,0x7e7b36c9,0x42b0058a,2 +np.float32,0x7e351fa4,0x42af5e0d,2 +np.float32,0x3f973c0c,0x3f18011e,2 +np.float32,0xff800000,0xffc00000,2 +np.float32,0x3f9b0a4b,0x3f239a33,2 +np.float32,0x3f87c4cf,0x3eb17e7e,2 +np.float32,0x7ef67760,0x42b15eaa,2 +np.float32,0x3fc4d2c8,0x3f7ed20f,2 +np.float32,0x7e940dac,0x42b059b8,2 +np.float32,0x7f6e6a52,0x42b2b08d,2 +np.float32,0x3f838752,0x3e6fe4b2,2 +np.float32,0x3fd8f046,0x3f8f4a94,2 +np.float32,0x3fa82112,0x3f45c223,2 +np.float32,0x3fd49b16,0x3f8c1345,2 +np.float32,0x7f02a941,0x42b17ca1,2 +np.float32,0x3f8a9d2c,0x3ecf1768,2 +np.float32,0x7c9372e3,0x42aacc0f,2 +np.float32,0x3fd260b3,0x3f8a619a,2 +np.float32,0x3f8a1b88,0x3eca27cb,2 +np.float32,0x7d25d510,0x42ac6b1c,2 +np.float32,0x7ef5a578,0x42b15cf5,2 +np.float32,0x3fe6625d,0x3f98ae9a,2 +np.float32,0x3ff53240,0x3fa22658,2 +np.float32,0x3f8bb2e6,0x3ed944cf,2 +np.float32,0x7f4679b1,0x42b252ad,2 +np.float32,0x3fa8db30,0x3f4774fc,2 +np.float32,0x7ee5fafd,0x42b13b37,2 +np.float32,0x3fc405e0,0x3f7d71fb,2 +np.float32,0x3f9303cd,0x3f09ddfd,2 +np.float32,0x7f486e67,0x42b257b2,2 +np.float32,0x7e73f12b,0x42aff680,2 +np.float32,0x3fe80f8b,0x3f99cbe4,2 +np.float32,0x3f84200a,0x3e81a3f3,2 +np.float32,0x3fa14e5c,0x3f34e3ce,2 +np.float32,0x3fda22ec,0x3f9029bb,2 +np.float32,0x3f801772,0x3d1aef98,2 +np.float32,0x7eaa1428,0x42b0a0bb,2 +np.float32,0x3feae0b3,0x3f9ba4aa,2 +np.float32,0x7ea439b4,0x42b08ecc,2 +np.float32,0x3fa28b1c,0x3f381579,2 +np.float32,0x7e8af247,0x42b03937,2 +np.float32,0x3fd19216,0x3f89c2b7,2 +np.float32,0x7f6ea033,0x42b2b100,2 +np.float32,0x3fad4fbf,0x3f518224,2 +np.float32,0x3febd940,0x3f9c45bd,2 +np.float32,0x7f4643a3,0x42b25221,2 +np.float32,0x7ec34478,0x42b0e771,2 +np.float32,0x7f18c83b,0x42b1ccb5,2 +np.float32,0x3fc665ad,0x3f80bf94,2 +np.float32,0x3ff0a999,0x3f9f56c4,2 +np.float32,0x3faf1cd2,0x3f5568fe,2 +np.float32,0x7ecd9dc6,0x42b101e1,2 +np.float32,0x3faad282,0x3f4bf754,2 +np.float32,0x3ff905a0,0x3fa47771,2 +np.float32,0x7f596481,0x42b28149,2 +np.float32,0x7f1cb31f,0x42b1d9ac,2 +np.float32,0x7e266719,0x42af32a6,2 +np.float32,0x7eccce06,0x42b0ffdb,2 +np.float32,0x3f9b6f71,0x3f24c102,2 +np.float32,0x3f80e4ba,0x3df1d6bc,2 +np.float32,0x3f843d51,0x3e836a60,2 +np.float32,0x7f70bd88,0x42b2b585,2 +np.float32,0x3fe4cc96,0x3f979e18,2 +np.float32,0x3ff737c7,0x3fa36151,2 +np.float32,0x3ff1197e,0x3f9f9cf4,2 +np.float32,0x7f08e190,0x42b19471,2 +np.float32,0x3ff1542e,0x3f9fc1b2,2 +np.float32,0x3ff6673c,0x3fa2e2d2,2 +np.float32,0xbf800000,0xffc00000,2 +np.float32,0x7e3f9ba7,0x42af7add,2 +np.float32,0x7f658ff6,0x42b29d2d,2 +np.float32,0x3f93441c,0x3f0ac0d9,2 +np.float32,0x7f526a74,0x42b27096,2 +np.float32,0x7f5b00c8,0x42b28511,2 +np.float32,0x3ff212f8,0x3fa038cf,2 +np.float32,0x7e0bd60d,0x42aed998,2 +np.float32,0x7f71ef7f,0x42b2b80e,2 +np.float32,0x7f7a897e,0x42b2c9f1,2 +np.float32,0x7e8b76a6,0x42b03b1e,2 +np.float32,0x7efa0da3,0x42b1660f,2 +np.float32,0x3fce9166,0x3f876ae0,2 +np.float32,0x3fc4163d,0x3f7d8e30,2 +np.float32,0x3fdb3784,0x3f90f16b,2 +np.float32,0x7c5f177b,0x42aa3d30,2 +np.float32,0x3fc6276d,0x3f808af5,2 +np.float32,0x7bac9cc2,0x42a856f4,2 +np.float32,0x3fe5876f,0x3f981bea,2 +np.float32,0x3fef60e3,0x3f9e878a,2 +np.float32,0x3fb23cd8,0x3f5bfb06,2 +np.float32,0x3fe114e2,0x3f951402,2 +np.float32,0x7ca8ef04,0x42ab11b4,2 +np.float32,0x7d93c2ad,0x42ad92ec,2 +np.float32,0x3fe5bb8a,0x3f983ee6,2 +np.float32,0x7f0182fd,0x42b1781b,2 +np.float32,0x7da63bb2,0x42adcf3d,2 +np.float32,0x3fac46b7,0x3f4f399e,2 +np.float32,0x7f7a5d8f,0x42b2c997,2 +np.float32,0x7f76572e,0x42b2c14b,2 +np.float32,0x7f42d53e,0x42b24931,2 +np.float32,0x7f7ffd00,0x42b2d4f6,2 +np.float32,0x3fc346c3,0x3f7c2756,2 +np.float32,0x7f1f6ae3,0x42b1e27a,2 +np.float32,0x3f87fb56,0x3eb3e2ee,2 +np.float32,0x3fed17a2,0x3f9d12b4,2 +np.float32,0x7f5ea903,0x42b28d8c,2 +np.float32,0x3f967f82,0x3f15a4ab,2 +np.float32,0x7d3b540c,0x42aca984,2 +np.float32,0x7f56711a,0x42b27a4a,2 +np.float32,0x7f122223,0x42b1b5ee,2 +np.float32,0x3fd6fa34,0x3f8dd919,2 +np.float32,0x3fadd62e,0x3f52a7b3,2 +np.float32,0x3fb7bf0c,0x3f67015f,2 +np.float32,0x7edf4ba7,0x42b12c1d,2 +np.float32,0x7e33cc65,0x42af5a4b,2 +np.float32,0x3fa6be17,0x3f427831,2 +np.float32,0x3fa07aa8,0x3f32b7d4,2 +np.float32,0x3fa4a3af,0x3f3d5a01,2 +np.float32,0x3fdbb267,0x3f9149a8,2 +np.float32,0x7ed45e25,0x42b1126c,2 +np.float32,0x3fe3f432,0x3f970ba6,2 +np.float32,0x7f752080,0x42b2bec3,2 +np.float32,0x3f872747,0x3eaa62ea,2 +np.float32,0x7e52175d,0x42afaa03,2 +np.float32,0x3fdc766c,0x3f91d5ce,2 +np.float32,0x7ecd6841,0x42b1015c,2 +np.float32,0x7f3d6c40,0x42b23ac6,2 +np.float32,0x3fb80c14,0x3f6796b9,2 +np.float32,0x3ff6ad56,0x3fa30d68,2 +np.float32,0x3fda44c3,0x3f90423e,2 +np.float32,0x3fdcba0c,0x3f9205fc,2 +np.float32,0x7e14a720,0x42aef8e6,2 +np.float32,0x3fe9e489,0x3f9b0047,2 +np.float32,0x7e69f933,0x42afe123,2 +np.float32,0x3ff3ee6d,0x3fa15f71,2 +np.float32,0x3f8538cd,0x3e91c1a7,2 +np.float32,0x3fdc3f07,0x3f91ae46,2 +np.float32,0x3fba2ef0,0x3f6bada2,2 +np.float32,0x7da64cd8,0x42adcf71,2 +np.float32,0x3fc34bd2,0x3f7c301d,2 +np.float32,0x3fa273aa,0x3f37d984,2 +np.float32,0x3ff0338c,0x3f9f0c86,2 +np.float32,0x7ed62cef,0x42b116c3,2 +np.float32,0x3f911e7e,0x3f02f7c6,2 +np.float32,0x7c8514c9,0x42aa9792,2 +np.float32,0x3fea2a74,0x3f9b2df5,2 +np.float32,0x3fe036f8,0x3f947a25,2 +np.float32,0x7c5654bf,0x42aa28ad,2 +np.float32,0x3fd9e423,0x3f8ffc32,2 +np.float32,0x7eec0439,0x42b1487b,2 +np.float32,0x3fc580f4,0x3f7ffb62,2 +np.float32,0x3fb0e316,0x3f592bbe,2 +np.float32,0x7c4cfb7d,0x42aa11d8,2 +np.float32,0x3faf9704,0x3f566e00,2 +np.float32,0x3fa7cf8a,0x3f45023d,2 +np.float32,0x7f7b724d,0x42b2cbcc,2 +np.float32,0x7f05bfe3,0x42b18897,2 +np.float32,0x3f90bde3,0x3f018bf3,2 +np.float32,0x7c565479,0x42aa28ad,2 +np.float32,0x3f94b517,0x3f0fb8e5,2 +np.float32,0x3fd6aadd,0x3f8d9e3c,2 +np.float32,0x7f09b37c,0x42b1977f,2 +np.float32,0x7f2b45ea,0x42b20734,2 +np.float32,0x3ff1d15e,0x3fa00fe9,2 +np.float32,0x3f99bce6,0x3f1fbd6c,2 +np.float32,0x7ecd1f76,0x42b100a7,2 +np.float32,0x7f443e2b,0x42b24ce2,2 +np.float32,0x7da7d6a5,0x42add428,2 +np.float32,0x7ebe0193,0x42b0d975,2 +np.float32,0x7ee13c43,0x42b1308b,2 +np.float32,0x3f8adf1b,0x3ed18e0c,2 +np.float32,0x7f76ce65,0x42b2c242,2 +np.float32,0x7e34f43d,0x42af5d92,2 +np.float32,0x7f306b76,0x42b2165d,2 +np.float32,0x7e1fd07f,0x42af1df7,2 +np.float32,0x3fab9a41,0x3f4db909,2 +np.float32,0x3fc23d1a,0x3f7a5803,2 +np.float32,0x3f8b7403,0x3ed70245,2 +np.float32,0x3f8c4dd6,0x3edebbae,2 +np.float32,0x3fe5f411,0x3f9864cd,2 +np.float32,0x3f88128b,0x3eb4e508,2 +np.float32,0x3fcb09de,0x3f84976f,2 +np.float32,0x7f32f2f5,0x42b21da6,2 +np.float32,0x3fe75610,0x3f9950f6,2 +np.float32,0x3f993edf,0x3f1e408d,2 +np.float32,0x3fc4a9d7,0x3f7e8be9,2 +np.float32,0x7f74551a,0x42b2bd1a,2 +np.float32,0x7de87129,0x42ae7ae2,2 +np.float32,0x7f18bbbd,0x42b1cc8c,2 +np.float32,0x7e7e1dd4,0x42b00b6c,2 +np.float32,0x3ff6e55b,0x3fa32f64,2 +np.float32,0x3fa634c8,0x3f412df3,2 +np.float32,0x3fd0fb7c,0x3f894e49,2 +np.float32,0x3ff4f6a6,0x3fa201d7,2 +np.float32,0x7f69d418,0x42b2a69a,2 +np.float32,0x7cb9632d,0x42ab414a,2 +np.float32,0x3fc57d36,0x3f7ff503,2 +np.float32,0x7e9e2ed7,0x42b07b9b,2 +np.float32,0x7f2e6868,0x42b2107d,2 +np.float32,0x3fa3169a,0x3f39785d,2 +np.float32,0x7f03cde0,0x42b18117,2 +np.float32,0x7f6d75d2,0x42b2ae7f,2 +np.float32,0x3ff483f2,0x3fa1bb75,2 +np.float32,0x7f1b39f7,0x42b1d4d6,2 +np.float32,0x3f8c7a7d,0x3ee0481e,2 +np.float32,0x3f989095,0x3f1c2b19,2 +np.float32,0x3fa4cbfd,0x3f3dbd87,2 +np.float32,0x7f75b00f,0x42b2bfef,2 +np.float32,0x3f940724,0x3f0d6756,2 +np.float32,0x7f5e5a1a,0x42b28cd6,2 +np.float32,0x800000,0xffc00000,2 +np.float32,0x7edd1d29,0x42b12716,2 +np.float32,0x3fa3e9e4,0x3f3b8c16,2 +np.float32,0x7e46d70e,0x42af8dd5,2 +np.float32,0x3f824745,0x3e40ec1e,2 +np.float32,0x3fd67623,0x3f8d770a,2 +np.float32,0x3fe9a6f3,0x3f9ad7fa,2 +np.float32,0x3fdda67c,0x3f92adc1,2 +np.float32,0x7ccb6c9a,0x42ab70d4,2 +np.float32,0x3ffd364a,0x3fa6f2fe,2 +np.float32,0x7e02424c,0x42aeb545,2 +np.float32,0x3fb6d2f2,0x3f6534a1,2 +np.float32,0x3fe1fe26,0x3f95b4cc,2 +np.float32,0x7e93ac57,0x42b05867,2 +np.float32,0x7f7b3433,0x42b2cb4d,2 +np.float32,0x3fb76803,0x3f66580d,2 +np.float32,0x3f9af881,0x3f23661b,2 +np.float32,0x3fd58062,0x3f8cbf98,2 +np.float32,0x80000000,0xffc00000,2 +np.float32,0x7f1af8f4,0x42b1d3ff,2 +np.float32,0x3fe66bba,0x3f98b4dc,2 +np.float32,0x7f6bd7bf,0x42b2aaff,2 +np.float32,0x3f84f79a,0x3e8e2e49,2 +np.float32,0x7e475b06,0x42af8f28,2 +np.float32,0x3faff89b,0x3f573d5e,2 +np.float32,0x7de5aa77,0x42ae74bb,2 +np.float32,0x3f8e9e42,0x3ef26cd2,2 +np.float32,0x3fb1cec3,0x3f5b1740,2 +np.float32,0x3f8890d6,0x3eba4821,2 +np.float32,0x3f9b39e9,0x3f242547,2 +np.float32,0x3fc895a4,0x3f829407,2 +np.float32,0x7f77943c,0x42b2c3dc,2 +np.float32,0x7f390d58,0x42b22ed2,2 +np.float32,0x3fe7e160,0x3f99ad58,2 +np.float32,0x3f93d2a0,0x3f0cb205,2 +np.float32,0x7f29499b,0x42b2013c,2 +np.float32,0x3f8c11b2,0x3edca10f,2 +np.float32,0x7e898ef8,0x42b03413,2 +np.float32,0x3fdff942,0x3f944f34,2 +np.float32,0x7f3d602f,0x42b23aa5,2 +np.float32,0x3f8a50f3,0x3ecc345b,2 +np.float32,0x3fa1f86d,0x3f369ce4,2 +np.float32,0x3f97ad95,0x3f19681d,2 +np.float32,0x3ffad1e0,0x3fa589e5,2 +np.float32,0x3fa70590,0x3f432311,2 +np.float32,0x7e6840cb,0x42afdd5c,2 +np.float32,0x3fd4036d,0x3f8ba0aa,2 +np.float32,0x7f7cc953,0x42b2ce84,2 +np.float32,0x7f228e1e,0x42b1ec74,2 +np.float32,0x7e37a866,0x42af652a,2 +np.float32,0x3fda22d0,0x3f9029a7,2 +np.float32,0x7f736bff,0x42b2bb31,2 +np.float32,0x3f9833b6,0x3f1b0b8e,2 +np.float32,0x7f466001,0x42b2526a,2 +np.float32,0xff7fffff,0xffc00000,2 +np.float32,0x7dd62bcd,0x42ae50f8,2 +np.float32,0x7f1d2bfe,0x42b1db36,2 +np.float32,0x7ecffe9e,0x42b107c5,2 +np.float32,0x7ebefe0a,0x42b0dc1b,2 +np.float32,0x7f45c63d,0x42b250dd,2 +np.float32,0x7f601af0,0x42b290db,2 +np.float32,0x3fcbb88a,0x3f8524e5,2 +np.float32,0x7ede55ff,0x42b129e8,2 +np.float32,0x7ea5dd5a,0x42b093e2,2 +np.float32,0x3ff53857,0x3fa22a12,2 +np.float32,0x3f8dbd6a,0x3eeb28a4,2 +np.float32,0x3fd1b467,0x3f89dd2c,2 +np.float32,0x3fe0423f,0x3f9481fc,2 +np.float32,0x3f84b421,0x3e8a6174,2 +np.float32,0x7f4efc97,0x42b2682c,2 +np.float32,0x7f601b33,0x42b290dc,2 +np.float32,0x3f94f240,0x3f108719,2 +np.float32,0x7decd251,0x42ae8471,2 +np.float32,0x3fdc457c,0x3f91b2e2,2 +np.float32,0x3f92a966,0x3f089c5a,2 +np.float32,0x3fc9732f,0x3f834afc,2 +np.float32,0x3f97948f,0x3f19194e,2 +np.float32,0x7f0824a1,0x42b191ac,2 +np.float32,0x7f0365a5,0x42b17f81,2 +np.float32,0x3f800000,0x0,2 +np.float32,0x7f0054c6,0x42b1736b,2 +np.float32,0x3fe86544,0x3f9a0484,2 +np.float32,0x7e95f844,0x42b0604e,2 +np.float32,0x3fce8602,0x3f8761e2,2 +np.float32,0x3fc726c8,0x3f81621d,2 +np.float32,0x3fcf6b03,0x3f88161b,2 +np.float32,0x3fceb843,0x3f87898a,2 +np.float32,0x3fe2f8b2,0x3f966071,2 +np.float32,0x7f3c8e7f,0x42b2386d,2 +np.float32,0x3fcee13a,0x3f87a9d2,2 +np.float32,0x3fc4df27,0x3f7ee73c,2 +np.float32,0x3ffde486,0x3fa758e3,2 +np.float32,0x3fa91be0,0x3f480b17,2 +np.float32,0x7f2a5a7d,0x42b20472,2 +np.float32,0x7e278d80,0x42af362d,2 +np.float32,0x3f96d091,0x3f16a9d5,2 +np.float32,0x7e925225,0x42b053b2,2 +np.float32,0x7f7ef83a,0x42b2d2ec,2 +np.float32,0x7eb4923a,0x42b0bf61,2 +np.float32,0x7e98bf19,0x42b069b3,2 +np.float32,0x3fac93a2,0x3f4fe410,2 +np.float32,0x7f46389c,0x42b25205,2 +np.float32,0x3f9fd447,0x3f30fd54,2 +np.float32,0x3fef42d4,0x3f9e7483,2 +np.float32,0x7f482174,0x42b256ed,2 +np.float32,0x3f97aedb,0x3f196c1e,2 +np.float32,0x7f764edd,0x42b2c13a,2 +np.float32,0x3f9117b5,0x3f02de5c,2 +np.float32,0x3fc7984e,0x3f81c12d,2 +np.float64,0x3ff1e2cb7463c597,0x3fdec6caf39e0c0e,2 +np.float64,0x3ffe4f89789c9f13,0x3ff40f4b1da0f3e9,2 +np.float64,0x7f6a5c9ac034b935,0x408605e51703c145,2 +np.float64,0x7fdcb6ece3b96dd9,0x40862d6521e16d60,2 +np.float64,0x3ff6563e182cac7c,0x3feb9d8210f3fa88,2 +np.float64,0x7fde32025f3c6404,0x40862dcc1d1a9b7f,2 +np.float64,0x7fd755ed35aeabd9,0x40862bbc5522b779,2 +np.float64,0x3ff5c81f4bcb903e,0x3fea71f10b954ea3,2 +np.float64,0x3fffe805d35fd00c,0x3ff50463a1ba2938,2 +np.float64,0x7fd045a1c1a08b43,0x408628d9f431f2f5,2 +np.float64,0x3ff49f7dd9893efc,0x3fe7c6736e17ea8e,2 +np.float64,0x7fccfbc1fd39f783,0x408627eca79acf51,2 +np.float64,0x3ff1af0a00035e14,0x3fdd1c0e7d5706ea,2 +np.float64,0x7fe7bd17162f7a2d,0x4086316af683502b,2 +np.float64,0x3ff0941b8d012837,0x3fd128d274065ac0,2 +np.float64,0x3ffa0c5d98b418bb,0x3ff11af9c8edd17f,2 +np.float64,0x3ffad9733355b2e6,0x3ff1b6d1307acb42,2 +np.float64,0x3ffabb2a33d57654,0x3ff1a0442b034e50,2 +np.float64,0x3ff36118b0c6c231,0x3fe472b7dfb23516,2 +np.float64,0x3ff2441d3664883a,0x3fe0d61145608f0c,2 +np.float64,0x7fe039862d20730b,0x40862e5f8ed752d3,2 +np.float64,0x7fb1dde24023bbc4,0x40861e824cdb0664,2 +np.float64,0x7face6335839cc66,0x40861ccf90a26e16,2 +np.float64,0x3ffb5d0e1af6ba1c,0x3ff2170f6f42fafe,2 +np.float64,0x3ff5c2c6a50b858d,0x3fea665aabf04407,2 +np.float64,0x3ffabb409db57681,0x3ff1a054ea32bfc3,2 +np.float64,0x3ff1e054e983c0aa,0x3fdeb30c17286cb6,2 +np.float64,0x7fe467f73268cfed,0x4086303529e52e9b,2 +np.float64,0x7fe0e86bf961d0d7,0x40862eb40788b04a,2 +np.float64,0x3ffb743542f6e86a,0x3ff227b4ea5acee0,2 +np.float64,0x3ff2de6826e5bcd0,0x3fe2e31fcde0a96c,2 +np.float64,0x7fd6b27ccfad64f9,0x40862b8385697c31,2 +np.float64,0x7fe0918e8d21231c,0x40862e8a82d9517a,2 +np.float64,0x7fd0ca0395a19406,0x4086291a0696ed33,2 +np.float64,0x3ffb042496960849,0x3ff1d658c928abfc,2 +np.float64,0x3ffcd0409799a081,0x3ff31877df0cb245,2 +np.float64,0x7fe429bd06685379,0x4086301c9f259934,2 +np.float64,0x3ff933076092660f,0x3ff06d2e5f4d9ab7,2 +np.float64,0x7feaefcb28f5df95,0x4086326dccf88e6f,2 +np.float64,0x7fb5f2c1f82be583,0x40862027ac02a39d,2 +np.float64,0x3ffb5d9e3bd6bb3c,0x3ff21777501d097e,2 +np.float64,0x10000000000000,0xfff8000000000000,2 +np.float64,0x3ff70361596e06c3,0x3fecf675ceda7e19,2 +np.float64,0x3ff71a21b5ee3444,0x3fed224fa048d9a9,2 +np.float64,0x3ffb102b86762057,0x3ff1df2cc9390518,2 +np.float64,0x7feaaeb35c355d66,0x4086325a60704a90,2 +np.float64,0x7fd9a3d0a93347a0,0x40862c7d300fc076,2 +np.float64,0x7fabcf159c379e2a,0x40861c80cdbbff27,2 +np.float64,0x7fd1c066ec2380cd,0x4086298c3006fee6,2 +np.float64,0x3ff3d5ae2d67ab5c,0x3fe5bc16447428db,2 +np.float64,0x3ff4b76add696ed6,0x3fe800f5bbf21376,2 +np.float64,0x3ff60d89ee0c1b14,0x3feb063fdebe1a68,2 +np.float64,0x7f1d2648003a4c8f,0x4085eaf9238af95a,2 +np.float64,0x7fe8b45f6df168be,0x408631bca5abf6d6,2 +np.float64,0x7fe9ea5308f3d4a5,0x4086321ea2bd3af9,2 +np.float64,0x7fcb6ba5a636d74a,0x4086277b208075ed,2 +np.float64,0x3ff621cfd74c43a0,0x3feb30d59baf5919,2 +np.float64,0x3ff7bc8ca0af7919,0x3fee524da8032896,2 +np.float64,0x7fda22dd0c3445b9,0x40862ca47326d063,2 +np.float64,0x7fd02ed4b2a05da8,0x408628ceb6919421,2 +np.float64,0x3ffe64309fdcc861,0x3ff41c1b18940709,2 +np.float64,0x3ffee4042abdc808,0x3ff46a6005bccb41,2 +np.float64,0x3ff078145b00f029,0x3fceeb3d6bfae0eb,2 +np.float64,0x7fda20fd20b441f9,0x40862ca3e03b990b,2 +np.float64,0x3ffa9e9e9af53d3d,0x3ff18ade3cbee789,2 +np.float64,0x3ff0a1062501420c,0x3fd1e32de6d18c0d,2 +np.float64,0x3ff3bdf118477be2,0x3fe57ad89b7fdf8b,2 +np.float64,0x3ff101c0d5c20382,0x3fd6965d3539be47,2 +np.float64,0x7feba3b53b774769,0x408632a28c7aca4d,2 +np.float64,0x3ff598db5d4b31b7,0x3fea0aa65c0b421a,2 +np.float64,0x3ff5fdfbb72bfbf8,0x3feae55accde4a5e,2 +np.float64,0x7fe5bae53aab75c9,0x408630b5e7a5b92a,2 +np.float64,0x3ff8f668afd1ecd2,0x3ff03af686666c9c,2 +np.float64,0x3ff5ba72dd2b74e6,0x3fea5441f223c093,2 +np.float64,0x3ff8498147109302,0x3fef4e45d501601d,2 +np.float64,0x7feddcfa5efbb9f4,0x4086334106a6e76b,2 +np.float64,0x7fd1a30200234603,0x4086297ee5cc562c,2 +np.float64,0x3ffffa8ee07ff51e,0x3ff50f1dc46f1303,2 +np.float64,0x7fef7ed00ebefd9f,0x408633ae01dabe52,2 +np.float64,0x3ffb6e062276dc0c,0x3ff22344c58c2016,2 +np.float64,0x7fcf2b59943e56b2,0x4086288190dd5eeb,2 +np.float64,0x3ffa589f9254b13f,0x3ff155cc081eee0b,2 +np.float64,0x3ff05415ca60a82c,0x3fc9e45565baef0a,2 +np.float64,0x7feb34bed576697d,0x408632822d5a178c,2 +np.float64,0x3ff3993845c73270,0x3fe51423baf246c3,2 +np.float64,0x3ff88367aaf106d0,0x3fefb2d9ca9f1192,2 +np.float64,0x7fef364304fe6c85,0x4086339b7ed82997,2 +np.float64,0x7fcba2c317374585,0x4086278b24e42934,2 +np.float64,0x3ff1aef885e35df1,0x3fdd1b79f55b20c0,2 +np.float64,0x7fe19367886326ce,0x40862f035f867445,2 +np.float64,0x3ff3c8295e279053,0x3fe5970aa670d32e,2 +np.float64,0x3ff6edda164ddbb4,0x3feccca9eb59d6b9,2 +np.float64,0x7fdeaea940bd5d52,0x40862dece02d151b,2 +np.float64,0x7fea9d6324353ac5,0x408632552ddf0d4f,2 +np.float64,0x7fe60e39e66c1c73,0x408630d45b1ad0c4,2 +np.float64,0x7fde06325abc0c64,0x40862dc07910038c,2 +np.float64,0x7f9ec89d303d9139,0x408617c55ea4c576,2 +np.float64,0x3ff9801930530032,0x3ff0abe5be046051,2 +np.float64,0x3ff4d5859689ab0b,0x3fe849a7f7a19fa3,2 +np.float64,0x3ff38afbc48715f8,0x3fe4ebb7710cbab9,2 +np.float64,0x3ffd88a0e77b1142,0x3ff3916964407e21,2 +np.float64,0x1,0xfff8000000000000,2 +np.float64,0x3ff5db59e58bb6b4,0x3fea9b6b5ccc116f,2 +np.float64,0x3ffd4b05b15a960c,0x3ff369792f661a90,2 +np.float64,0x7fdcebc4fb39d789,0x40862d73cd623378,2 +np.float64,0x3ff5b56f944b6adf,0x3fea4955d6b06ca3,2 +np.float64,0x7fd4e4abf2a9c957,0x40862ad9e9da3c61,2 +np.float64,0x7fe08e0d6aa11c1a,0x40862e88d17ef277,2 +np.float64,0x3ff0dfc97da1bf93,0x3fd50f9004136d8f,2 +np.float64,0x7fdec38eaebd871c,0x40862df2511e26b4,2 +np.float64,0x7ff8000000000000,0x7ff8000000000000,2 +np.float64,0x3ff21865504430cb,0x3fe033fe3cf3947a,2 +np.float64,0x7fdc139708b8272d,0x40862d371cfbad03,2 +np.float64,0x7fe1fe3be3a3fc77,0x40862f336e3ba63a,2 +np.float64,0x7fd9fa2493b3f448,0x40862c97f2960be9,2 +np.float64,0x3ff0a027db414050,0x3fd1d6e54a707c87,2 +np.float64,0x3ff568b16f4ad163,0x3fe99f5c6d7b6e18,2 +np.float64,0x3ffe2f82877c5f05,0x3ff3fb54bd0da753,2 +np.float64,0x7fbaf5778435eaee,0x408621ccc9e2c1be,2 +np.float64,0x7fc5aaf8362b55ef,0x40862598e7072a49,2 +np.float64,0x7fe0ebfdd4a1d7fb,0x40862eb5b7bf99d5,2 +np.float64,0x7fd8efeb5931dfd6,0x40862c444636f408,2 +np.float64,0x3ff361a308c6c346,0x3fe4744cae63e6df,2 +np.float64,0x7fef287d39be50f9,0x40863397f65c807e,2 +np.float64,0x7fe72c4a14ae5893,0x4086313992e52082,2 +np.float64,0x3ffd1be44cba37c8,0x3ff34a9a45239eb9,2 +np.float64,0x3ff50369c18a06d4,0x3fe8b69319f091f1,2 +np.float64,0x3ffb333c25766678,0x3ff1f8c78eeb28f1,2 +np.float64,0x7fe12050416240a0,0x40862ece4e2f2f24,2 +np.float64,0x7fe348f5526691ea,0x40862fc16fbe7b6c,2 +np.float64,0x3ff343cc4d068799,0x3fe41c2a30cab7d2,2 +np.float64,0x7fd1b0daaa2361b4,0x408629852b3104ff,2 +np.float64,0x3ff6a41f37ad483e,0x3fec3b36ee6c6d4a,2 +np.float64,0x3ffad9439435b287,0x3ff1b6add9a1b3d7,2 +np.float64,0x7fbeb9a2f23d7345,0x408622d89ac1eaba,2 +np.float64,0x3ffab3d39fb567a7,0x3ff19ac75b4427f3,2 +np.float64,0x3ff890003ed12000,0x3fefc8844471c6ad,2 +np.float64,0x3ffc9f595e593eb2,0x3ff2f7a8699f06d8,2 +np.float64,0x7fe2224ef6e4449d,0x40862f43684a154a,2 +np.float64,0x3ffa67ba08d4cf74,0x3ff161525778df99,2 +np.float64,0x7fe87e24b570fc48,0x408631ab02b159fb,2 +np.float64,0x7fd6e99be92dd337,0x40862b96dba73685,2 +np.float64,0x7fe90f39fdf21e73,0x408631d9dbd36c1e,2 +np.float64,0x3ffb7806abd6f00e,0x3ff22a719b0f4c46,2 +np.float64,0x3ffa511ba3d4a238,0x3ff1500c124f6e17,2 +np.float64,0x3ff5d7a569abaf4b,0x3fea937391c280e8,2 +np.float64,0x7fc4279d20284f39,0x40862504a5cdcb96,2 +np.float64,0x3ffe8791b1fd0f24,0x3ff431f1ed7eaba0,2 +np.float64,0x7fe3b2f5276765e9,0x40862fecf15e2535,2 +np.float64,0x7feeab0e7abd561c,0x408633778044cfbc,2 +np.float64,0x7fdba88531375109,0x40862d1860306d7a,2 +np.float64,0x7fe7b19b3def6335,0x4086316716d6890b,2 +np.float64,0x3ff9e9437413d287,0x3ff0ff89431c748c,2 +np.float64,0x3ff960716a52c0e3,0x3ff092498028f802,2 +np.float64,0x3ff271bf56a4e37f,0x3fe1786fc8dd775d,2 +np.float64,0x3fff2a6578be54cb,0x3ff494bbe303eeb5,2 +np.float64,0x3ffd842eb5fb085e,0x3ff38e8b7ba42bc5,2 +np.float64,0x3ff91600e5d22c02,0x3ff0553c6a6b3d93,2 +np.float64,0x3ff9153f45f22a7e,0x3ff0549c0eaecf95,2 +np.float64,0x7fe0ab319da15662,0x40862e96da3b19f9,2 +np.float64,0x3ff06acd1f60d59a,0x3fcd2aca543d2772,2 +np.float64,0x3ffb3e7a54d67cf4,0x3ff200f288cd391b,2 +np.float64,0x3ffd01356f1a026b,0x3ff339003462a56c,2 +np.float64,0x3ffacd35def59a6c,0x3ff1adb8d32b3ec0,2 +np.float64,0x3ff6f953264df2a6,0x3fece2f992948d6e,2 +np.float64,0x3ff0fa91f5a1f524,0x3fd64609a28f1590,2 +np.float64,0x7fd1b7610ca36ec1,0x408629881e03dc7d,2 +np.float64,0x3ff4317fb7c86300,0x3fe6b086ed265887,2 +np.float64,0x3ff3856198070ac3,0x3fe4dbb6bc88b9e3,2 +np.float64,0x7fed7fc4573aff88,0x40863327e7013a81,2 +np.float64,0x3ffe53cbbf5ca798,0x3ff411f07a29b1f4,2 +np.float64,0x3ff092195b012433,0x3fd10b1c0b4b14fe,2 +np.float64,0x3ff1a3171163462e,0x3fdcb5c301d5d40d,2 +np.float64,0x3ffa1401f1742804,0x3ff120eb319e9faa,2 +np.float64,0x7fd352f6f426a5ed,0x40862a3a048feb6d,2 +np.float64,0x7fd4ee246fa9dc48,0x40862add895d808f,2 +np.float64,0x3ff0675cfa00ceba,0x3fccb2222c5493ca,2 +np.float64,0x3ffe5cb38f3cb967,0x3ff417773483d161,2 +np.float64,0x7fe11469ea2228d3,0x40862ec8bd3e497f,2 +np.float64,0x3fff13cba67e2798,0x3ff4872fe2c26104,2 +np.float64,0x3ffb73d3d316e7a8,0x3ff2276f08612ea2,2 +np.float64,0x7febfb70f237f6e1,0x408632bbc9450721,2 +np.float64,0x3ff84a0d87b0941b,0x3fef4f3b707e3145,2 +np.float64,0x7fd71fd5082e3fa9,0x40862ba9b4091172,2 +np.float64,0x3ff560737d8ac0e7,0x3fe98cc9c9ba2f61,2 +np.float64,0x3ff46a266ae8d44d,0x3fe74190e5234822,2 +np.float64,0x7fe8cc9225719923,0x408631c477db9708,2 +np.float64,0x3ff871de5930e3bc,0x3fef948f7d00fbef,2 +np.float64,0x3ffd0bc7895a178f,0x3ff33ffc18357721,2 +np.float64,0x3ff66099f9ccc134,0x3febb2bc775b4720,2 +np.float64,0x7fe91f1be9723e37,0x408631deec3a5c9e,2 +np.float64,0x7fd60462f12c08c5,0x40862b4537e1c1c6,2 +np.float64,0x3ff053100ba0a620,0x3fc9bc0c21e2284f,2 +np.float64,0x7fd864c611b0c98b,0x40862c1724506255,2 +np.float64,0x7fd191decb2323bd,0x408629771bfb68cc,2 +np.float64,0x3ff792a1656f2543,0x3fee054f2e135fcf,2 +np.float64,0x7fd03625cea06c4b,0x408628d253b840e3,2 +np.float64,0x7fc3967716272ced,0x408624ca35451042,2 +np.float64,0x7fe6636cb32cc6d8,0x408630f3073a22a7,2 +np.float64,0x3ffc2d3976585a73,0x3ff2a9d4c0dae607,2 +np.float64,0x3fffd10ee79fa21e,0x3ff4f70db69888be,2 +np.float64,0x3ff1d4fcae23a9f9,0x3fde57675007b23c,2 +np.float64,0x3ffa5da19e14bb43,0x3ff1599f74d1c113,2 +np.float64,0x3ff7f4eb0d6fe9d6,0x3feeb85189659e99,2 +np.float64,0x7fbcca44d8399489,0x408622536234f7c1,2 +np.float64,0x7fef5f97ec3ebf2f,0x408633a60fdde0d7,2 +np.float64,0x7fde4a66da3c94cd,0x40862dd290ebc184,2 +np.float64,0x3ff072957a40e52b,0x3fce34d913d87613,2 +np.float64,0x3ff2bc4c9dc57899,0x3fe27497e6ebe27d,2 +np.float64,0x7fd7d152b4afa2a4,0x40862be63469eecd,2 +np.float64,0x3ff957d768f2afaf,0x3ff08b4ad8062a73,2 +np.float64,0x7fe4bc5f45a978be,0x40863055fd66e4eb,2 +np.float64,0x7fc90de345321bc6,0x408626c24ce7e370,2 +np.float64,0x3ff2d7a37d85af47,0x3fe2cd6a40b544a0,2 +np.float64,0x7fe536ea1f6a6dd3,0x40863084bade76a3,2 +np.float64,0x3fff970c9cdf2e19,0x3ff4d524572356dd,2 +np.float64,0x3ffe173ae63c2e76,0x3ff3ec1ee35ad28c,2 +np.float64,0x3ff714025cce2805,0x3fed168aedff4a2b,2 +np.float64,0x7fce7b414c3cf682,0x40862853dcdd19d4,2 +np.float64,0x3ff019623f2032c4,0x3fbc7c602df0bbaf,2 +np.float64,0x3ff72f57fd0e5eb0,0x3fed4ae75f697432,2 +np.float64,0x3ff283778e8506ef,0x3fe1b5c5725b0dfd,2 +np.float64,0x3ff685a29aed0b45,0x3febfdfdedd581e2,2 +np.float64,0x3ff942d24fb285a4,0x3ff07a224c3ecfaf,2 +np.float64,0x3ff2e4a9f465c954,0x3fe2f71905399e8f,2 +np.float64,0x7fdfa1c7fa3f438f,0x40862e2b4e06f098,2 +np.float64,0x3ff49b59c26936b4,0x3fe7bc41c8c1e59d,2 +np.float64,0x3ff2102d3704205a,0x3fe014bf7e28924e,2 +np.float64,0x3ff88de3b8311bc8,0x3fefc4e3e0a15a89,2 +np.float64,0x7fea5ba25374b744,0x40863241519c9b66,2 +np.float64,0x3fffe5df637fcbbf,0x3ff5032488f570f9,2 +np.float64,0x7fe67cfefe6cf9fd,0x408630fc25333cb4,2 +np.float64,0x3ff090bf2b01217e,0x3fd0f6fcf1092b4a,2 +np.float64,0x7fecd75bc5f9aeb7,0x408632f9b6c2e013,2 +np.float64,0x7fe15df38c62bbe6,0x40862eeae5ac944b,2 +np.float64,0x3ff4757875a8eaf1,0x3fe75e0eafbe28ce,2 +np.float64,0x7fecca8a51b99514,0x408632f627c23923,2 +np.float64,0x3ff91ca529d2394a,0x3ff05abb327fd1ca,2 +np.float64,0x3ffb962993b72c53,0x3ff23ff831717579,2 +np.float64,0x3ffd548a2c7aa914,0x3ff36fac7f56d716,2 +np.float64,0x7fbafb5cb035f6b8,0x408621ce898a02fb,2 +np.float64,0x3ff1d86daca3b0db,0x3fde73536c29218c,2 +np.float64,0x7fa8d0f8f431a1f1,0x40861b97a03c3a18,2 +np.float64,0x3ff44f1067489e21,0x3fe6fcbd8144ab2a,2 +np.float64,0x7fec062b07380c55,0x408632bed9c6ce85,2 +np.float64,0x3ff7e11e0fcfc23c,0x3fee94ada7efaac4,2 +np.float64,0x7fe77505c1aeea0b,0x4086315287dda0ba,2 +np.float64,0x7fc465af2728cb5d,0x4086251d236107f7,2 +np.float64,0x3ffe811c4a7d0238,0x3ff42df7e8b6cf2d,2 +np.float64,0x7fe05a471260b48d,0x40862e6fa502738b,2 +np.float64,0x7fec32cd9778659a,0x408632cb8d98c5a3,2 +np.float64,0x7fd203a220a40743,0x408629aa43b010c0,2 +np.float64,0x7fed71f7d17ae3ef,0x4086332428207101,2 +np.float64,0x3ff3918999e72313,0x3fe4fe5e8991402f,2 +np.float64,0x3ff3ecae38c7d95c,0x3fe5fa787d887981,2 +np.float64,0x7fd65345b82ca68a,0x40862b61aed8c64e,2 +np.float64,0x3ff1efdd01c3dfba,0x3fdf2eae36139204,2 +np.float64,0x3ffba9344f375268,0x3ff24d7fdcfc313b,2 +np.float64,0x7fd0469b35208d35,0x408628da6ed24bdd,2 +np.float64,0x7fe525782daa4aef,0x4086307e240c8b30,2 +np.float64,0x3ff8e473d371c8e8,0x3ff02beebd4171c7,2 +np.float64,0x3ff59a43898b3487,0x3fea0dc0a6acea0a,2 +np.float64,0x7fef50c7263ea18d,0x408633a247d7cd42,2 +np.float64,0x7fe8b5a301f16b45,0x408631bd0e71c855,2 +np.float64,0x3ff209369de4126d,0x3fdff4264334446b,2 +np.float64,0x3ffbe2ff4437c5fe,0x3ff2763b356814c7,2 +np.float64,0x3ff55938156ab270,0x3fe97c70514f91bf,2 +np.float64,0x3fff5d8bf81ebb18,0x3ff4b333b230672a,2 +np.float64,0x3ff16a317bc2d463,0x3fdab84e7faa468f,2 +np.float64,0x3ff7e64f8dafcc9f,0x3fee9e0bd57e9566,2 +np.float64,0x7fef4dc065be9b80,0x408633a181e25abb,2 +np.float64,0x3ff64a24a62c9449,0x3feb849ced76437e,2 +np.float64,0x7fc3cb85ef27970b,0x408624dfc39c8f74,2 +np.float64,0x7fec2162a77842c4,0x408632c69b0d43b6,2 +np.float64,0x7feccee6dc399dcd,0x408632f75de98c46,2 +np.float64,0x7faff4f5f43fe9eb,0x40861d9d89be14c9,2 +np.float64,0x7fee82df60fd05be,0x4086336cfdeb7317,2 +np.float64,0x3ffe54588d9ca8b1,0x3ff41247eb2f75ca,2 +np.float64,0x3ffe5615b55cac2c,0x3ff4135c4eb11620,2 +np.float64,0x3ffdaf9a6a1b5f35,0x3ff3aa70e50d1692,2 +np.float64,0x3ff69c045f4d3809,0x3fec2b00734e2cde,2 +np.float64,0x7fd049239aa09246,0x408628dbad6dd995,2 +np.float64,0x3ff2acbe8465597d,0x3fe24138652195e1,2 +np.float64,0x3ffb288302365106,0x3ff1f0f86ca7e5d1,2 +np.float64,0x3fff6fe8d87edfd2,0x3ff4be136acf53c5,2 +np.float64,0x3ffc87c8bfb90f92,0x3ff2e7bbd65867cb,2 +np.float64,0x3ff173327ca2e665,0x3fdb0b945abb00d7,2 +np.float64,0x3ff9a5cf7a134b9f,0x3ff0ca2450f07c78,2 +np.float64,0x7faf782b043ef055,0x40861d7e0e9b35ef,2 +np.float64,0x3ffa0874975410e9,0x3ff117ee3dc8f5ba,2 +np.float64,0x7fc710fc7f2e21f8,0x40862618fed167fb,2 +np.float64,0x7feb73f4c876e7e9,0x40863294ae3ac1eb,2 +np.float64,0x8000000000000000,0xfff8000000000000,2 +np.float64,0x7fb46615c028cc2b,0x40861f91bade4dad,2 +np.float64,0x7fc26b064624d60c,0x4086244c1b76c938,2 +np.float64,0x3ff06ab9fa40d574,0x3fcd282fd971d1b4,2 +np.float64,0x3ff61da7410c3b4e,0x3feb28201031af02,2 +np.float64,0x3ffec7ba1b9d8f74,0x3ff459342511f952,2 +np.float64,0x7ff4000000000000,0x7ffc000000000000,2 +np.float64,0x7fe5d570422baae0,0x408630bfa75008c9,2 +np.float64,0x3ffa895832f512b0,0x3ff17ad41555dccb,2 +np.float64,0x7fd343ac21a68757,0x40862a33ad59947a,2 +np.float64,0x3ffc1eeb37383dd6,0x3ff29ff29e55a006,2 +np.float64,0x7fee3c5c507c78b8,0x4086335a6b768090,2 +np.float64,0x7fe96d774a32daee,0x408631f7b9937e36,2 +np.float64,0x7fb878362430f06b,0x40862106603497b6,2 +np.float64,0x7fec0a79c03814f3,0x408632c01479905e,2 +np.float64,0x3ffa2f143c145e28,0x3ff135e25d902e1a,2 +np.float64,0x3ff14ccff80299a0,0x3fd9a0cd3397b14c,2 +np.float64,0x3ff97980dcb2f302,0x3ff0a6942a8133ab,2 +np.float64,0x3ff872e2d1f0e5c6,0x3fef96526eb2f756,2 +np.float64,0x7fdf1c9b46be3936,0x40862e0957fee329,2 +np.float64,0x7fcab6525d356ca4,0x408627458791f029,2 +np.float64,0x3ff964e74a52c9ce,0x3ff095e8845d523c,2 +np.float64,0x3ffb3aa23c967544,0x3ff1fe282d897c13,2 +np.float64,0x7fdd8a36afbb146c,0x40862d9f2b05f61b,2 +np.float64,0x3ffea39f42fd473e,0x3ff4432a48176399,2 +np.float64,0x7fea614f68b4c29e,0x408632430a750385,2 +np.float64,0x7feeafb86abd5f70,0x40863378b79f70cf,2 +np.float64,0x3ff80bc94eb01792,0x3feee138e9d626bd,2 +np.float64,0x7fcaca74743594e8,0x4086274b8ce4d1e1,2 +np.float64,0x3ff8b14815316290,0x3ff000b3526c8321,2 +np.float64,0x7fc698eb5f2d31d6,0x408625eeec86cd2b,2 +np.float64,0x7fe15429a3e2a852,0x40862ee6621205b8,2 +np.float64,0x7fee37f81b7c6fef,0x4086335941ed80dd,2 +np.float64,0x3ff8097ab3f012f6,0x3feedd1bafc3196e,2 +np.float64,0x7fe7c889ceaf9113,0x4086316ed13f2394,2 +np.float64,0x7fceca94513d9528,0x4086286893a06824,2 +np.float64,0x3ff593a103cb2742,0x3fe9ff1af4f63cc9,2 +np.float64,0x7fee237d24bc46f9,0x40863353d4142c87,2 +np.float64,0x3ffbf71e4777ee3c,0x3ff2844c0ed9f4d9,2 +np.float64,0x3ff490c65c09218d,0x3fe7a2216d9f69fd,2 +np.float64,0x3fff5ceaf1feb9d6,0x3ff4b2d430a90110,2 +np.float64,0x3ff55baecceab75e,0x3fe98203980666c4,2 +np.float64,0x3ff511bc306a2378,0x3fe8d81ce7be7b50,2 +np.float64,0x3ff38f83dcc71f08,0x3fe4f89f130d5f87,2 +np.float64,0x3ff73a3676ee746d,0x3fed5f98a65107ee,2 +np.float64,0x7fc27e50c824fca1,0x408624547828bc49,2 +np.float64,0xfff0000000000000,0xfff8000000000000,2 +np.float64,0x3fff38959ebe712b,0x3ff49d362c7ba16a,2 +np.float64,0x3ffad6d23a75ada4,0x3ff1b4dda6394ed0,2 +np.float64,0x3ffe77c6c2dcef8e,0x3ff4283698835ecb,2 +np.float64,0x3fff5feb413ebfd6,0x3ff4b49bcbdb3aa9,2 +np.float64,0x3ff0d30aa161a615,0x3fd4751bcdd7d727,2 +np.float64,0x3ff51e07e00a3c10,0x3fe8f4bd1408d694,2 +np.float64,0x8010000000000000,0xfff8000000000000,2 +np.float64,0x7fd231d2fe2463a5,0x408629beaceafcba,2 +np.float64,0x3fff6b4aee1ed696,0x3ff4bb58544bf8eb,2 +np.float64,0x3ff91fcd2f323f9a,0x3ff05d56e33db6b3,2 +np.float64,0x3ff3b889ab477113,0x3fe56bdeab74cce5,2 +np.float64,0x3ff99bfe30d337fc,0x3ff0c24bbf265561,2 +np.float64,0x3ffbe9e5eaf7d3cc,0x3ff27b0fe60f827a,2 +np.float64,0x7fd65678e92cacf1,0x40862b62d44fe8b6,2 +np.float64,0x7fd9cc477233988e,0x40862c89c638ee48,2 +np.float64,0x3ffc123c72d82479,0x3ff297294d05cbc0,2 +np.float64,0x3ff58abad58b1576,0x3fe9eb65da2a867a,2 +np.float64,0x7fe534887b2a6910,0x40863083d4ec2877,2 +np.float64,0x7fe1d3dcb123a7b8,0x40862f208116c55e,2 +np.float64,0x7fd4d570dba9aae1,0x40862ad412c413cd,2 +np.float64,0x3fffce7d3fdf9cfa,0x3ff4f58f02451928,2 +np.float64,0x3ffa76901c74ed20,0x3ff16c9a5851539c,2 +np.float64,0x7fdd88ffa23b11fe,0x40862d9ed6c6f426,2 +np.float64,0x3ff09fdbb9e13fb7,0x3fd1d2ae4fcbf713,2 +np.float64,0x7fe64567772c8ace,0x408630e845dbc290,2 +np.float64,0x7fb1a849ba235092,0x40861e6a291535b2,2 +np.float64,0x3ffaddb105f5bb62,0x3ff1b9f68f4c419b,2 +np.float64,0x7fd2fc3d5025f87a,0x40862a15cbc1df75,2 +np.float64,0x7fdea7d872bd4fb0,0x40862deb190b2c50,2 +np.float64,0x7fd50ea97eaa1d52,0x40862ae9edc4c812,2 +np.float64,0x3fff659c245ecb38,0x3ff4b7fb18b31aea,2 +np.float64,0x3ff3f1fbb7c7e3f7,0x3fe608bd9d76268c,2 +np.float64,0x3ff76869d9aed0d4,0x3fedb6c23d3a317b,2 +np.float64,0x7fedd4efe93ba9df,0x4086333edeecaa43,2 +np.float64,0x3ff9a5bd4eb34b7a,0x3ff0ca15d02bc960,2 +np.float64,0x3ffd9359cc5b26b4,0x3ff39850cb1a6b6c,2 +np.float64,0x7fe912d0427225a0,0x408631db00e46272,2 +np.float64,0x3ffb3802fe567006,0x3ff1fc4093646465,2 +np.float64,0x3ff02cc38a205987,0x3fc2e8182802a07b,2 +np.float64,0x3ffda953dd1b52a8,0x3ff3a66c504cf207,2 +np.float64,0x7fe0a487e4a1490f,0x40862e93a6f20152,2 +np.float64,0x7fed265ed1fa4cbd,0x4086330f838ae431,2 +np.float64,0x7fd0000114200001,0x408628b76ec48b5c,2 +np.float64,0x3ff2c262786584c5,0x3fe288860d354b0f,2 +np.float64,0x8000000000000001,0xfff8000000000000,2 +np.float64,0x3ffdae9f075b5d3e,0x3ff3a9d006ae55c1,2 +np.float64,0x3ffb69c72156d38e,0x3ff22037cbb85e5b,2 +np.float64,0x7feeae255f7d5c4a,0x408633784e89bc05,2 +np.float64,0x7feb13927c362724,0x408632786630c55d,2 +np.float64,0x7fef49e072be93c0,0x408633a08451d476,2 +np.float64,0x3fff23d6337e47ac,0x3ff490ceb6e634ae,2 +np.float64,0x3ffba82cf8f7505a,0x3ff24cc51c73234d,2 +np.float64,0x7fe948719ef290e2,0x408631ec0b36476e,2 +np.float64,0x3ff41926c5e8324e,0x3fe670e14bbda8cd,2 +np.float64,0x3ff91f09c1523e14,0x3ff05cb5731878da,2 +np.float64,0x3ff6ae6afccd5cd6,0x3fec4fbeca764086,2 +np.float64,0x3ff927f7e0f24ff0,0x3ff06413eeb8eb1e,2 +np.float64,0x3ff19dd2b9e33ba5,0x3fdc882f97994600,2 +np.float64,0x7fe8e502c5b1ca05,0x408631cc56526fff,2 +np.float64,0x7feb49f70fb693ed,0x4086328868486fcd,2 +np.float64,0x3ffd942d535b285a,0x3ff398d8d89f52ca,2 +np.float64,0x7fc3b9c5c627738b,0x408624d893e692ca,2 +np.float64,0x7fea0780ff340f01,0x408632279fa46704,2 +np.float64,0x7fe4c90066a99200,0x4086305adb47a598,2 +np.float64,0x7fdb209113364121,0x40862cf0ab64fd7d,2 +np.float64,0x3ff38617e5470c30,0x3fe4ddc0413b524f,2 +np.float64,0x7fea1b5b803436b6,0x4086322db767f091,2 +np.float64,0x7fe2004898e40090,0x40862f3457795dc5,2 +np.float64,0x3ff3c4360ac7886c,0x3fe58c29843a4c75,2 +np.float64,0x3ff504bc168a0978,0x3fe8b9ada7f698e6,2 +np.float64,0x3ffd3e936fda7d27,0x3ff3615912c5b4ac,2 +np.float64,0x3ffbdc52fb97b8a6,0x3ff2718dae5f1f2b,2 +np.float64,0x3fffef6d84ffdedb,0x3ff508adbc8556cf,2 +np.float64,0x3ff23b65272476ca,0x3fe0b646ed2579eb,2 +np.float64,0x7fe4633068a8c660,0x408630334a4b7ff7,2 +np.float64,0x3ff769b754aed36f,0x3fedb932af0223f9,2 +np.float64,0x7fe7482d92ee905a,0x408631432de1b057,2 +np.float64,0x3ff5dd682aabbad0,0x3fea9fd5e506a86d,2 +np.float64,0x7fd68399a2ad0732,0x40862b72ed89805d,2 +np.float64,0x3ffad7acc3d5af5a,0x3ff1b57fe632c948,2 +np.float64,0x3ffc68e43698d1c8,0x3ff2d2be6f758761,2 +np.float64,0x3ff4e517fbc9ca30,0x3fe86eddf5e63a58,2 +np.float64,0x3ff34c63c56698c8,0x3fe435b74ccd6a13,2 +np.float64,0x7fea9456c17528ad,0x4086325275237015,2 +np.float64,0x7fee6573f2fccae7,0x4086336543760346,2 +np.float64,0x7fd5496fb9aa92de,0x40862b0023235667,2 +np.float64,0x7ff0000000000000,0x7ff0000000000000,2 +np.float64,0x3ffb70e31256e1c6,0x3ff22552f54b13e0,2 +np.float64,0x3ff66a33988cd467,0x3febc656da46a1ca,2 +np.float64,0x3fff0af2eb1e15e6,0x3ff481dec325f5c8,2 +np.float64,0x3ff6a0233d0d4046,0x3fec33400958eda1,2 +np.float64,0x7fdb11e2d5b623c5,0x40862cec55e405f9,2 +np.float64,0x3ffb8a015ad71402,0x3ff2374d7b563a72,2 +np.float64,0x3ff1807d8ce300fb,0x3fdb849e4bce8335,2 +np.float64,0x3ffefd535e3dfaa6,0x3ff479aaac6ffe79,2 +np.float64,0x3ff701e23a6e03c4,0x3fecf39072d96fc7,2 +np.float64,0x3ff4ac809f895901,0x3fe7e6598f2335a5,2 +np.float64,0x3ff0309f26a0613e,0x3fc3b3f4b2783690,2 +np.float64,0x3ff241dd0ce483ba,0x3fe0cde2cb639144,2 +np.float64,0x3ffabce63fb579cc,0x3ff1a18fe2a2da59,2 +np.float64,0x3ffd84b967db0973,0x3ff38ee4f240645d,2 +np.float64,0x7fc3f88b9a27f116,0x408624f1e10cdf3f,2 +np.float64,0x7fe1d5fd5923abfa,0x40862f2175714a3a,2 +np.float64,0x7fe487b145690f62,0x4086304190700183,2 +np.float64,0x7fe7997feaef32ff,0x4086315eeefdddd2,2 +np.float64,0x3ff8f853b671f0a8,0x3ff03c907353a8da,2 +np.float64,0x7fca4c23b5349846,0x408627257ace5778,2 +np.float64,0x7fe0c9bf3a21937d,0x40862ea576c3ea43,2 +np.float64,0x7fc442b389288566,0x4086250f5f126ec9,2 +np.float64,0x7fc6d382ed2da705,0x40862603900431b0,2 +np.float64,0x7fe40b069068160c,0x4086301066468124,2 +np.float64,0x3ff7f62a146fec54,0x3feeba8dfc4363fe,2 +np.float64,0x3ff721e8e94e43d2,0x3fed313a6755d34f,2 +np.float64,0x7fe579feaf2af3fc,0x4086309ddefb6112,2 +np.float64,0x3ffe2c6bde5c58d8,0x3ff3f9665dc9a16e,2 +np.float64,0x7fcf9998ed3f3331,0x4086289dab274788,2 +np.float64,0x7fdb03af2236075d,0x40862ce82252e490,2 +np.float64,0x7fe72799392e4f31,0x40863137f428ee71,2 +np.float64,0x7f9f2190603e4320,0x408617dc5b3b3c3c,2 +np.float64,0x3ff69c56d52d38ae,0x3fec2ba59fe938b2,2 +np.float64,0x7fdcde27bf39bc4e,0x40862d70086cd06d,2 +np.float64,0x3ff654d6b8eca9ae,0x3feb9aa0107609a6,2 +np.float64,0x7fdf69d967bed3b2,0x40862e1d1c2b94c2,2 +np.float64,0xffefffffffffffff,0xfff8000000000000,2 +np.float64,0x7fedfd073f3bfa0d,0x40863349980c2c8b,2 +np.float64,0x7f7c1856803830ac,0x40860bf312b458c7,2 +np.float64,0x7fe9553f1bb2aa7d,0x408631f0173eadd5,2 +np.float64,0x3ff6e92efc2dd25e,0x3fecc38f98e7e1a7,2 +np.float64,0x7fe9719ac532e335,0x408631f906cd79c3,2 +np.float64,0x3ff60e56ae4c1cad,0x3feb07ef8637ec7e,2 +np.float64,0x3ff0d0803501a100,0x3fd455c0af195a9c,2 +np.float64,0x7fe75248a3eea490,0x40863146a614aec1,2 +np.float64,0x7fdff61ead3fec3c,0x40862e408643d7aa,2 +np.float64,0x7fed4ac7a4fa958e,0x408633197b5cf6ea,2 +np.float64,0x7fe58d44562b1a88,0x408630a5098d1bbc,2 +np.float64,0x7fd89dcdb1b13b9a,0x40862c29c2979288,2 +np.float64,0x3ff205deda240bbe,0x3fdfda67c84fd3a8,2 +np.float64,0x7fdf84c15abf0982,0x40862e23f361923d,2 +np.float64,0x3ffe012b3afc0256,0x3ff3de3dfa5f47ce,2 +np.float64,0x3ffe2f3512dc5e6a,0x3ff3fb245206398e,2 +np.float64,0x7fed6174c2bac2e9,0x4086331faa699617,2 +np.float64,0x3ff1f30f8783e61f,0x3fdf47e06f2c40d1,2 +np.float64,0x3ff590da9eab21b5,0x3fe9f8f7b4baf3c2,2 +np.float64,0x3ffb3ca1eb967944,0x3ff1ff9baf66d704,2 +np.float64,0x7fe50ba9a5aa1752,0x408630745ab7fd3c,2 +np.float64,0x3ff43743a4a86e87,0x3fe6bf7ae80b1dda,2 +np.float64,0x3ff47e1a24e8fc34,0x3fe773acca44c7d6,2 +np.float64,0x3ff589ede9eb13dc,0x3fe9e99f28fab3a4,2 +np.float64,0x3ff72f2cbf8e5e5a,0x3fed4a94e7edbf24,2 +np.float64,0x3ffa4f9bbc549f38,0x3ff14ee60aea45d3,2 +np.float64,0x3ff975dae732ebb6,0x3ff0a3a1fbd7284a,2 +np.float64,0x7fbcf14ee039e29d,0x4086225e33f3793e,2 +np.float64,0x3ff10e027f621c05,0x3fd71cce2452b4e0,2 +np.float64,0x3ff33ea193067d43,0x3fe40cbac4daaddc,2 +np.float64,0x7fbef8f2263df1e3,0x408622e905c8e1b4,2 +np.float64,0x3fff7f5bfe3efeb8,0x3ff4c732e83df253,2 +np.float64,0x3ff5700a6b4ae015,0x3fe9afdd7b8b82b0,2 +np.float64,0x3ffd5099da5aa134,0x3ff36d1bf26e55bf,2 +np.float64,0x3ffed8e0f89db1c2,0x3ff4639ff065107a,2 +np.float64,0x3fff9d0c463f3a18,0x3ff4d8a9f297cf52,2 +np.float64,0x3ff23db5b2e47b6b,0x3fe0bebdd48f961a,2 +np.float64,0x3ff042bff1e08580,0x3fc713bf24cc60ef,2 +np.float64,0x7feb4fe97a769fd2,0x4086328a26675646,2 +np.float64,0x3ffeafbfeedd5f80,0x3ff44a955a553b1c,2 +np.float64,0x3ff83fb524507f6a,0x3fef3d1729ae0976,2 +np.float64,0x3ff1992294433245,0x3fdc5f5ce53dd197,2 +np.float64,0x7fe89fe629b13fcb,0x408631b601a83867,2 +np.float64,0x7fe53e4d74aa7c9a,0x40863087839b52f1,2 +np.float64,0x3ff113713e6226e2,0x3fd757631ca7cd09,2 +np.float64,0x7fd4a0b7a629416e,0x40862abfba27a09b,2 +np.float64,0x3ff184c6e2a3098e,0x3fdbab2e3966ae57,2 +np.float64,0x3ffafbbf77f5f77f,0x3ff1d02bb331d9f9,2 +np.float64,0x3ffc6099a358c134,0x3ff2cd16941613d1,2 +np.float64,0x3ffb7c441ef6f888,0x3ff22d7b12e31432,2 +np.float64,0x3ff625ba5eec4b75,0x3feb39060e55fb79,2 +np.float64,0x7fde879acbbd0f35,0x40862de2aab4d72d,2 +np.float64,0x7f930aed982615da,0x408613edb6df8528,2 +np.float64,0x7fa4b82dac29705a,0x40861a261c0a9aae,2 +np.float64,0x7fced5c16b3dab82,0x4086286b7a73e611,2 +np.float64,0x7fe133749d2266e8,0x40862ed73a41b112,2 +np.float64,0x3ff2d8146ea5b029,0x3fe2ced55dbf997d,2 +np.float64,0x3ff60dac77ac1b59,0x3feb0688b0e54c7b,2 +np.float64,0x3ff275d9b024ebb3,0x3fe186b87258b834,2 +np.float64,0x3ff533e6500a67cd,0x3fe92746c8b50ddd,2 +np.float64,0x7fe370896666e112,0x40862fd1ca144736,2 +np.float64,0x7fee7695357ced29,0x40863369c459420e,2 +np.float64,0x7fd1e0528023c0a4,0x4086299a85caffd0,2 +np.float64,0x7fd05c7b24a0b8f5,0x408628e52824386f,2 +np.float64,0x3ff11dcc3b023b98,0x3fd7c56c8cef1be1,2 +np.float64,0x7fc9d9fae933b3f5,0x408627027404bc5f,2 +np.float64,0x7fe2359981246b32,0x40862f4be675e90d,2 +np.float64,0x3ffb10a949962152,0x3ff1df88f83b8cde,2 +np.float64,0x3ffa65b53654cb6a,0x3ff15fc8956ccc87,2 +np.float64,0x3ff0000000000000,0x0,2 +np.float64,0x7fad97ef703b2fde,0x40861d002f3d02da,2 +np.float64,0x3ff57aaf93aaf55f,0x3fe9c7b01f194edb,2 +np.float64,0x7fe9ecd73f33d9ad,0x4086321f69917205,2 +np.float64,0x3ff0dcb79c61b96f,0x3fd4eac86a7a9c38,2 +np.float64,0x7fee9c12ffbd3825,0x4086337396cd706d,2 +np.float64,0x3ff52c40af4a5881,0x3fe915a8a7de8f00,2 +np.float64,0x3ffbcfff59779ffe,0x3ff268e523fe8dda,2 +np.float64,0x7fe014cb4b602996,0x40862e4d5de42a03,2 +np.float64,0x7fae2370e83c46e1,0x40861d258dd5b3ee,2 +np.float64,0x7fe9e33602f3c66b,0x4086321c704ac2bb,2 +np.float64,0x3ff648acd74c915a,0x3feb8195ca53bcaa,2 +np.float64,0x7fe385f507670be9,0x40862fda95ebaf44,2 +np.float64,0x3ffb0e382c361c70,0x3ff1ddbea963e0a7,2 +np.float64,0x3ff47d6b6ae8fad7,0x3fe771f80ad37cd2,2 +np.float64,0x3ffca7d538f94faa,0x3ff2fd5f62e851ac,2 +np.float64,0x3ff83e949c107d29,0x3fef3b1c5bbac99b,2 +np.float64,0x7fc6fb933a2df725,0x408626118e51a286,2 +np.float64,0x7fe43a1454e87428,0x4086302318512d9b,2 +np.float64,0x7fe51fe32aaa3fc5,0x4086307c07271348,2 +np.float64,0x3ff35e563966bcac,0x3fe46aa2856ef85f,2 +np.float64,0x3ff84dd4e4909baa,0x3fef55d86d1d5c2e,2 +np.float64,0x7febe3d84077c7b0,0x408632b507686f03,2 +np.float64,0x3ff6aca2e32d5946,0x3fec4c32a2368ee3,2 +np.float64,0x7fe7070e3e6e0e1b,0x4086312caddb0454,2 +np.float64,0x7fd3657f2aa6cafd,0x40862a41acf47e70,2 +np.float64,0x3ff61534456c2a68,0x3feb1663900af13b,2 +np.float64,0x3ff8bc556eb178ab,0x3ff00a16b5403f88,2 +np.float64,0x3ffa7782e3f4ef06,0x3ff16d529c94a438,2 +np.float64,0x7fc15785ed22af0b,0x408623d0cd94fb86,2 +np.float64,0x3ff2e3eeb6e5c7dd,0x3fe2f4c4876d3edf,2 +np.float64,0x3ff2e4e17e85c9c3,0x3fe2f7c9e437b22e,2 +np.float64,0x7feb3aaf67f6755e,0x40863283ec4a0d76,2 +np.float64,0x7fe89efcf7313df9,0x408631b5b5e41263,2 +np.float64,0x3ffcc6fad4f98df6,0x3ff31245778dff6d,2 +np.float64,0x3ff356114466ac22,0x3fe45253d040a024,2 +np.float64,0x3ff81c70d2d038e2,0x3feefed71ebac776,2 +np.float64,0x7fdb75c96136eb92,0x40862d09a603f03e,2 +np.float64,0x3ff340f91b8681f2,0x3fe413bb6e6d4a54,2 +np.float64,0x3fff906079df20c1,0x3ff4d13869d16bc7,2 +np.float64,0x3ff226a42d644d48,0x3fe0698d316f1ac0,2 +np.float64,0x3ff948abc3b29158,0x3ff07eeb0b3c81ba,2 +np.float64,0x3ffc25df1fb84bbe,0x3ff2a4c13ad4edad,2 +np.float64,0x7fe07ea3b960fd46,0x40862e815b4cf43d,2 +np.float64,0x3ff497d3dae92fa8,0x3fe7b3917bf10311,2 +np.float64,0x7fea561db1f4ac3a,0x4086323fa4aef2a9,2 +np.float64,0x7fd1b49051236920,0x40862986d8759ce5,2 +np.float64,0x7f7ba3bd6037477a,0x40860bd19997fd90,2 +np.float64,0x3ff01126dd00224e,0x3fb76b67938dfb11,2 +np.float64,0x3ff29e1105053c22,0x3fe2102a4c5fa102,2 +np.float64,0x3ff9de2a6553bc55,0x3ff0f6cfe4dea30e,2 +np.float64,0x7fc558e7d42ab1cf,0x4086257a608fc055,2 +np.float64,0x3ff79830a74f3061,0x3fee0f93db153d65,2 +np.float64,0x7fe2661648e4cc2c,0x40862f6117a71eb2,2 +np.float64,0x3ff140cf4262819e,0x3fd92aefedae1ab4,2 +np.float64,0x3ff5f36251abe6c5,0x3feaced481ceaee3,2 +np.float64,0x7fc80911d5301223,0x4086266d4757f768,2 +np.float64,0x3ff9079a6c320f35,0x3ff04949d21ebe1e,2 +np.float64,0x3ffde8d2e09bd1a6,0x3ff3cedca8a5db5d,2 +np.float64,0x3ffadd1de375ba3c,0x3ff1b989790e8d93,2 +np.float64,0x3ffdbc40ee1b7882,0x3ff3b286b1c7da57,2 +np.float64,0x3ff8ff514771fea2,0x3ff04264add00971,2 +np.float64,0x7fefd7d0e63fafa1,0x408633c47d9f7ae4,2 +np.float64,0x3ffc47798c588ef3,0x3ff2bbe441fa783a,2 +np.float64,0x7fe6ebc55b6dd78a,0x408631232d9abf31,2 +np.float64,0xbff0000000000000,0xfff8000000000000,2 +np.float64,0x7fd378e4afa6f1c8,0x40862a49a8f98cb4,2 +np.float64,0x0,0xfff8000000000000,2 +np.float64,0x3ffe88ed7efd11db,0x3ff432c7ecb95492,2 +np.float64,0x3ff4f5509289eaa1,0x3fe8955a11656323,2 +np.float64,0x7fda255b41344ab6,0x40862ca53676a23e,2 +np.float64,0x3ffebe85b9bd7d0c,0x3ff453992cd55dea,2 +np.float64,0x3ff5d6180b8bac30,0x3fea901c2160c3bc,2 +np.float64,0x3ffcdfb8fcf9bf72,0x3ff322c83b3bc735,2 +np.float64,0x3ff3c91c26679238,0x3fe599a652b7cf59,2 +np.float64,0x7fc389f7a62713ee,0x408624c518edef93,2 +np.float64,0x3ffe1245ba1c248c,0x3ff3e901b2c4a47a,2 +np.float64,0x7fe1e76e95e3cedc,0x40862f29446f9eff,2 +np.float64,0x3ff02ae4f92055ca,0x3fc28221abd63daa,2 +np.float64,0x7fbf648a143ec913,0x40862304a0619d03,2 +np.float64,0x3ff2be7ef8657cfe,0x3fe27bcc6c97522e,2 +np.float64,0x3ffa7595e514eb2c,0x3ff16bdc64249ad1,2 +np.float64,0x3ff4ee130049dc26,0x3fe884354cbad8c9,2 +np.float64,0x3ff19211fc232424,0x3fdc2160bf3eae40,2 +np.float64,0x3ffec215aedd842c,0x3ff455c4cdd50c32,2 +np.float64,0x7fe7cb50ffaf96a1,0x4086316fc06a53af,2 +np.float64,0x3fffa679161f4cf2,0x3ff4de30ba7ac5b8,2 +np.float64,0x7fdcb459763968b2,0x40862d646a21011d,2 +np.float64,0x3ff9f338d6d3e672,0x3ff1075835d8f64e,2 +np.float64,0x3ff8de3319d1bc66,0x3ff026ae858c0458,2 +np.float64,0x7fee0199d33c0333,0x4086334ad03ac683,2 +np.float64,0x3ffc06076c380c0f,0x3ff28eaec3814faa,2 +np.float64,0x3ffe9e2e235d3c5c,0x3ff43fd4d2191a7f,2 +np.float64,0x3ffd93b06adb2761,0x3ff398888239cde8,2 +np.float64,0x7fefe4b71cffc96d,0x408633c7ba971b92,2 +np.float64,0x7fb2940352252806,0x40861ed244bcfed6,2 +np.float64,0x3ffba4647e3748c9,0x3ff24a15f02e11b9,2 +np.float64,0x7fd2d9543725b2a7,0x40862a0708446596,2 +np.float64,0x7fc04997f120932f,0x4086235055d35251,2 +np.float64,0x3ff6d14313ada286,0x3fec94b177f5d3fc,2 +np.float64,0x3ff279fc8684f3f9,0x3fe19511c3e5b9a8,2 +np.float64,0x3ff42f4609085e8c,0x3fe6aabe526ce2bc,2 +np.float64,0x7fc1c6c62a238d8b,0x408624037de7f6ec,2 +np.float64,0x7fe31ff4b8e63fe8,0x40862fb05b40fd16,2 +np.float64,0x7fd2a8825fa55104,0x408629f234d460d6,2 +np.float64,0x3ffe8c1d725d183b,0x3ff434bdc444143f,2 +np.float64,0x3ff0e9dc3e21d3b8,0x3fd58676e2c13fc9,2 +np.float64,0x3ffed03172fda063,0x3ff45e59f7aa6c8b,2 +np.float64,0x7fd74621962e8c42,0x40862bb6e90d66f8,2 +np.float64,0x3ff1faa29663f545,0x3fdf833a2c5efde1,2 +np.float64,0x7fda02834db40506,0x40862c9a860d6747,2 +np.float64,0x7f709b2fc021365f,0x408607be328eb3eb,2 +np.float64,0x7fec0d58aa381ab0,0x408632c0e61a1af6,2 +np.float64,0x3ff524d1720a49a3,0x3fe90479968d40fd,2 +np.float64,0x7fd64cb3b32c9966,0x40862b5f53c4b0b4,2 +np.float64,0x3ff9593e3ed2b27c,0x3ff08c6eea5f6e8b,2 +np.float64,0x3ff7de8b1f6fbd16,0x3fee9007abcfdf7b,2 +np.float64,0x7fe8d816d6b1b02d,0x408631c82e38a894,2 +np.float64,0x7fd726bbe22e4d77,0x40862bac16ee8d52,2 +np.float64,0x7fa70b07d42e160f,0x40861affcc4265e2,2 +np.float64,0x7fe18b4091e31680,0x40862effa8bce66f,2 +np.float64,0x3ff830253010604a,0x3fef21b2eaa75758,2 +np.float64,0x3fffcade407f95bc,0x3ff4f3734b24c419,2 +np.float64,0x3ff8c17cecb182fa,0x3ff00e75152d7bda,2 +np.float64,0x7fdad9b9d035b373,0x40862cdbabb793ba,2 +np.float64,0x3ff9f9e154f3f3c2,0x3ff10c8dfdbd2510,2 +np.float64,0x3ff465e162e8cbc3,0x3fe736c751c75b73,2 +np.float64,0x3ff9b4cd8493699b,0x3ff0d616235544b8,2 +np.float64,0x7fe557c4a56aaf88,0x4086309114ed12d9,2 +np.float64,0x7fe5999133eb3321,0x408630a9991a9b54,2 +np.float64,0x7fe7c9009e2f9200,0x4086316ef9359a47,2 +np.float64,0x3ff8545cabd0a8ba,0x3fef6141f1030c36,2 +np.float64,0x3ffa1f1712943e2e,0x3ff129849d492ce3,2 +np.float64,0x7fea803a14750073,0x4086324c652c276c,2 +np.float64,0x3ff5b6f97fcb6df3,0x3fea4cb0b97b18e9,2 +np.float64,0x7fc2efdfc425dfbf,0x40862485036a5c6e,2 +np.float64,0x7fe2c78e5be58f1c,0x40862f8b0a5e7baf,2 +np.float64,0x7fe80d7fff301aff,0x40863185e234060a,2 +np.float64,0x3ffd895d457b12ba,0x3ff391e2cac7a3f8,2 +np.float64,0x3ff44c9764a8992f,0x3fe6f6690396c232,2 +np.float64,0x3ff731688b8e62d1,0x3fed4ed70fac3839,2 +np.float64,0x3ff060200460c040,0x3fcbad4a07d97f0e,2 +np.float64,0x3ffbd2f70a17a5ee,0x3ff26afb46ade929,2 +np.float64,0x7febe9e841f7d3d0,0x408632b6c465ddd9,2 +np.float64,0x3ff2532f8be4a65f,0x3fe10c6cd8d64cf4,2 +np.float64,0x7fefffffffffffff,0x408633ce8fb9f87e,2 +np.float64,0x3ff3a1ae3a47435c,0x3fe52c00210cc459,2 +np.float64,0x7fe9c34ae6b38695,0x408632128d150149,2 +np.float64,0x3fff311029fe6220,0x3ff498b852f30bff,2 +np.float64,0x3ffd4485a1ba890c,0x3ff3653b6fa701cd,2 +np.float64,0x7fd52718b1aa4e30,0x40862af330d9c68c,2 +np.float64,0x3ff10b695a4216d3,0x3fd7009294e367b7,2 +np.float64,0x3ffdf73de59bee7c,0x3ff3d7fa96d2c1ae,2 +np.float64,0x3ff2f1c75965e38f,0x3fe320aaff3db882,2 +np.float64,0x3ff2a56a5a854ad5,0x3fe228cc4ad7e7a5,2 +np.float64,0x7fe60cd1cf6c19a3,0x408630d3d87a04b3,2 +np.float64,0x3ff89fa65c113f4c,0x3fefe3543773180c,2 +np.float64,0x3ffd253130ba4a62,0x3ff350b76ba692a0,2 +np.float64,0x7feaad7051f55ae0,0x40863259ff932d62,2 +np.float64,0x7fd9cc37cf33986f,0x40862c89c15f963b,2 +np.float64,0x3ff8c08de771811c,0x3ff00daa9c17acd7,2 +np.float64,0x7fea58b25d34b164,0x408632406d54cc6f,2 +np.float64,0x7fe5f161fd2be2c3,0x408630c9ddf272a5,2 +np.float64,0x3ff5840dbf8b081c,0x3fe9dc9117b4cbc7,2 +np.float64,0x3ff3fd762307faec,0x3fe6277cd530c640,2 +np.float64,0x3ff9095c98b212b9,0x3ff04abff170ac24,2 +np.float64,0x7feaac66017558cb,0x40863259afb4f8ce,2 +np.float64,0x7fd78f96bcaf1f2c,0x40862bd00175fdf9,2 +np.float64,0x3ffaca27e0959450,0x3ff1ab72b8f8633e,2 +np.float64,0x3ffb7f18cb96fe32,0x3ff22f81bcb8907b,2 +np.float64,0x3ffcce48d1199c92,0x3ff317276f62c0b2,2 +np.float64,0x3ffcb9a7f3797350,0x3ff30958e0d6a34d,2 +np.float64,0x7fda569ef6b4ad3d,0x40862cb43b33275a,2 +np.float64,0x7fde9f0893bd3e10,0x40862de8cc036283,2 +np.float64,0x3ff428be3928517c,0x3fe699bb5ab58904,2 +np.float64,0x7fa4d3344029a668,0x40861a3084989291,2 +np.float64,0x3ff03607bd006c0f,0x3fc4c4840cf35f48,2 +np.float64,0x3ff2b1335c056267,0x3fe25000846b75a2,2 +np.float64,0x7fe0cb8bd8e19717,0x40862ea65237d496,2 +np.float64,0x3fff4b1b7b9e9637,0x3ff4a83fb08e7b24,2 +np.float64,0x7fe7526140aea4c2,0x40863146ae86069c,2 +np.float64,0x7fbfcfb7c23f9f6f,0x4086231fc246ede5,2 diff --git a/numpy/core/tests/data/umath-validation-set-arcsin.csv b/numpy/core/tests/data/umath-validation-set-arcsin.csv new file mode 100644 index 000000000000..cb94c93c9b35 --- /dev/null +++ b/numpy/core/tests/data/umath-validation-set-arcsin.csv @@ -0,0 +1,1429 @@ +dtype,input,output,ulperrortol +np.float32,0xbe7d3a7c,0xbe7fe217,4 +np.float32,0x3dc102f0,0x3dc14c60,4 +np.float32,0xbe119c28,0xbe121aef,4 +np.float32,0xbe51cd68,0xbe534c75,4 +np.float32,0x3c04a300,0x3c04a35f,4 +np.float32,0xbf4f0b62,0xbf712a69,4 +np.float32,0x3ef61a5c,0x3f005cf6,4 +np.float32,0xbf13024c,0xbf1c97df,4 +np.float32,0x3e93b580,0x3e95d6b5,4 +np.float32,0x3e44e7b8,0x3e4623a5,4 +np.float32,0xbe35df20,0xbe36d773,4 +np.float32,0x3eecd2c0,0x3ef633cf,4 +np.float32,0x3f2772ba,0x3f36862a,4 +np.float32,0x3e211ea8,0x3e21cac5,4 +np.float32,0x3e3b3d90,0x3e3c4cc6,4 +np.float32,0x3f37c962,0x3f4d018c,4 +np.float32,0x3e92ad88,0x3e94c31a,4 +np.float32,0x3f356ffc,0x3f49a766,4 +np.float32,0x3f487ba2,0x3f665254,4 +np.float32,0x3f061c46,0x3f0d27ae,4 +np.float32,0xbee340a2,0xbeeb7722,4 +np.float32,0xbe85aede,0xbe874026,4 +np.float32,0x3f34cf9a,0x3f48c474,4 +np.float32,0x3e29a690,0x3e2a6fbd,4 +np.float32,0xbeb29428,0xbeb669d1,4 +np.float32,0xbe606d40,0xbe624370,4 +np.float32,0x3dae6860,0x3dae9e85,4 +np.float32,0xbf04872b,0xbf0b4d25,4 +np.float32,0x3f2080e2,0x3f2d7ab0,4 +np.float32,0xbec77dcc,0xbecceb27,4 +np.float32,0x3e0dda10,0x3e0e4f38,4 +np.float32,0xbefaf970,0xbf03262c,4 +np.float32,0x3f576a0c,0x3f7ffee6,4 +np.float32,0x3f222382,0x3f2f95d6,4 +np.float32,0x7fc00000,0x7fc00000,4 +np.float32,0x3e41c468,0x3e42f14e,4 +np.float32,0xbf2f64dd,0xbf4139a8,4 +np.float32,0xbf60ef90,0xbf895956,4 +np.float32,0xbf67c855,0xbf90eff0,4 +np.float32,0xbed35aee,0xbed9df00,4 +np.float32,0xbf2c7d92,0xbf3d448f,4 +np.float32,0x3f7b1604,0x3faff122,4 +np.float32,0xbf7c758b,0xbfb3bf87,4 +np.float32,0x3ecda1c8,0x3ed39acf,4 +np.float32,0x3f3af8ae,0x3f519fcb,4 +np.float32,0xbf16e6a3,0xbf2160fd,4 +np.float32,0x3f0c97d2,0x3f14d668,4 +np.float32,0x3f0a8060,0x3f1257b9,4 +np.float32,0x3f27905a,0x3f36ad57,4 +np.float32,0x3eeaeba4,0x3ef40efe,4 +np.float32,0x3e58dde0,0x3e5a8580,4 +np.float32,0xbf0cabe2,0xbf14ee6b,4 +np.float32,0xbe805ca8,0xbe81bf03,4 +np.float32,0x3f5462ba,0x3f7a7b85,4 +np.float32,0xbee235d0,0xbeea4d8b,4 +np.float32,0xbe880cb0,0xbe89b426,4 +np.float32,0x80000001,0x80000001,4 +np.float32,0x3f208c00,0x3f2d88f6,4 +np.float32,0xbf34f3d2,0xbf48f7a2,4 +np.float32,0x3f629428,0x3f8b1763,4 +np.float32,0xbf52a900,0xbf776b4a,4 +np.float32,0xbd17f8d0,0xbd1801be,4 +np.float32,0xbef7cada,0xbf0153d1,4 +np.float32,0x3f7d3b90,0x3fb63967,4 +np.float32,0xbd6a20b0,0xbd6a4160,4 +np.float32,0x3f740496,0x3fa1beb7,4 +np.float32,0x3ed8762c,0x3edf7dd9,4 +np.float32,0x3f53b066,0x3f793d42,4 +np.float32,0xbe9de718,0xbea084f9,4 +np.float32,0x3ea3ae90,0x3ea69b4b,4 +np.float32,0x3f1b8f00,0x3f273183,4 +np.float32,0x3f5cd6ac,0x3f852ead,4 +np.float32,0x3f29d510,0x3f39b169,4 +np.float32,0x3ee2a934,0x3eeace33,4 +np.float32,0x3eecac94,0x3ef608c2,4 +np.float32,0xbea915e2,0xbeac5203,4 +np.float32,0xbd316e90,0xbd317cc8,4 +np.float32,0xbf70b495,0xbf9c97b6,4 +np.float32,0xbe80d976,0xbe823ff3,4 +np.float32,0x3e9205f8,0x3e94143f,4 +np.float32,0x3f49247e,0x3f676296,4 +np.float32,0x3d9030c0,0x3d904f50,4 +np.float32,0x3e4df058,0x3e4f5a5c,4 +np.float32,0xbe1fd360,0xbe207b58,4 +np.float32,0xbf69dc7c,0xbf937006,4 +np.float32,0x3f36babe,0x3f4b7df3,4 +np.float32,0xbe8c9758,0xbe8e6bb7,4 +np.float32,0xbf4de72d,0xbf6f3c20,4 +np.float32,0xbecdad68,0xbed3a780,4 +np.float32,0xbf73e2cf,0xbfa18702,4 +np.float32,0xbece16a8,0xbed41a75,4 +np.float32,0x3f618a96,0x3f89fc6d,4 +np.float32,0xbf325853,0xbf454ea9,4 +np.float32,0x3f138568,0x3f1d3828,4 +np.float32,0xbf56a6e9,0xbf7e9748,4 +np.float32,0x3ef5d594,0x3f0035bf,4 +np.float32,0xbf408220,0xbf59dfaa,4 +np.float32,0xbed120e6,0xbed76dd5,4 +np.float32,0xbf6dbda5,0xbf986cee,4 +np.float32,0x3f744a38,0x3fa23282,4 +np.float32,0xbe4b56d8,0xbe4cb329,4 +np.float32,0x3f54c5f2,0x3f7b2d97,4 +np.float32,0xbd8b1c90,0xbd8b3801,4 +np.float32,0x3ee19a48,0x3ee9a03b,4 +np.float32,0x3f48460e,0x3f65fc3d,4 +np.float32,0x3eb541c0,0x3eb9461e,4 +np.float32,0xbea7d098,0xbeaaf98c,4 +np.float32,0xbda99e40,0xbda9d00c,4 +np.float32,0xbefb2ca6,0xbf03438d,4 +np.float32,0x3f4256be,0x3f5cab0b,4 +np.float32,0xbdbdb198,0xbdbdf74d,4 +np.float32,0xbf325b5f,0xbf4552e9,4 +np.float32,0xbf704d1a,0xbf9c00b4,4 +np.float32,0x3ebb1d04,0x3ebf8cf8,4 +np.float32,0xbed03566,0xbed66bf1,4 +np.float32,0x3e8fcee8,0x3e91c501,4 +np.float32,0xbf2e1eec,0xbf3f7b9d,4 +np.float32,0x3f33c4d2,0x3f474cac,4 +np.float32,0x3f598ef4,0x3f8201b4,4 +np.float32,0x3e09bb30,0x3e0a2660,4 +np.float32,0x3ed4e228,0x3edb8cdb,4 +np.float32,0x3eb7a190,0x3ebbd0a1,4 +np.float32,0xbd9ae630,0xbd9b0c18,4 +np.float32,0x3f43020e,0x3f5db2d7,4 +np.float32,0xbec06ac0,0xbec542d4,4 +np.float32,0x3f3dfde0,0x3f561674,4 +np.float32,0xbf64084a,0xbf8cabe6,4 +np.float32,0xbd6f95b0,0xbd6fb8b7,4 +np.float32,0x3f268640,0x3f354e2d,4 +np.float32,0xbe72b4bc,0xbe7509b2,4 +np.float32,0xbf3414fa,0xbf47bd5a,4 +np.float32,0xbf375218,0xbf4c566b,4 +np.float32,0x3f203c1a,0x3f2d2273,4 +np.float32,0xbd503530,0xbd504c2b,4 +np.float32,0xbc45e540,0xbc45e67b,4 +np.float32,0xbf175c4f,0xbf21f2c6,4 +np.float32,0x3f7432a6,0x3fa20b2b,4 +np.float32,0xbf43367f,0xbf5e03d8,4 +np.float32,0x3eb3997c,0x3eb780c4,4 +np.float32,0x3e5574c8,0x3e570878,4 +np.float32,0xbf04b57b,0xbf0b8349,4 +np.float32,0x3f6216d8,0x3f8a914b,4 +np.float32,0xbf57a237,0xbf80337d,4 +np.float32,0xbee1403a,0xbee93bee,4 +np.float32,0xbeaf9b9a,0xbeb33f3b,4 +np.float32,0xbf109374,0xbf19a223,4 +np.float32,0xbeae6824,0xbeb1f810,4 +np.float32,0xbcff9320,0xbcff9dbe,4 +np.float32,0x3ed205c0,0x3ed868a9,4 +np.float32,0x3d897c30,0x3d8996ad,4 +np.float32,0xbf2899d2,0xbf380d4c,4 +np.float32,0xbf54cb0b,0xbf7b36c2,4 +np.float32,0x3ea8e8ec,0x3eac2262,4 +np.float32,0x3ef5e1a0,0x3f003c9d,4 +np.float32,0xbf00c81e,0xbf06f1e2,4 +np.float32,0xbf346775,0xbf483181,4 +np.float32,0x3f7a4fe4,0x3fae077c,4 +np.float32,0x3f00776e,0x3f06948f,4 +np.float32,0xbe0a3078,0xbe0a9cbc,4 +np.float32,0xbeba0b06,0xbebe66be,4 +np.float32,0xbdff4e38,0xbdfff8b2,4 +np.float32,0xbe927f70,0xbe9492ff,4 +np.float32,0x3ebb07e0,0x3ebf7642,4 +np.float32,0x3ebcf8e0,0x3ec18c95,4 +np.float32,0x3f49bdfc,0x3f685b51,4 +np.float32,0x3cbc29c0,0x3cbc2dfd,4 +np.float32,0xbe9e951a,0xbea13bf1,4 +np.float32,0xbe8c237c,0xbe8df33d,4 +np.float32,0x3e17f198,0x3e1881c4,4 +np.float32,0xbd0b5220,0xbd0b5902,4 +np.float32,0xbf34c4a2,0xbf48b4f5,4 +np.float32,0xbedaa814,0xbee1ea94,4 +np.float32,0x3ebf5d6c,0x3ec42053,4 +np.float32,0x3cd04b40,0x3cd050ff,4 +np.float32,0xbec33fe0,0xbec85244,4 +np.float32,0xbf00b27a,0xbf06d8d8,4 +np.float32,0x3f15d7be,0x3f201243,4 +np.float32,0xbe3debd0,0xbe3f06f7,4 +np.float32,0xbea81704,0xbeab4418,4 +np.float32,0x1,0x1,4 +np.float32,0x3f49e6ba,0x3f689d8b,4 +np.float32,0x3f351030,0x3f491fc0,4 +np.float32,0x3e607de8,0x3e625482,4 +np.float32,0xbe8dbbe4,0xbe8f9c0e,4 +np.float32,0x3edbf350,0x3ee35924,4 +np.float32,0xbf0c84c4,0xbf14bf9c,4 +np.float32,0x3eb218b0,0x3eb5e61a,4 +np.float32,0x3e466dd0,0x3e47b138,4 +np.float32,0xbe8ece94,0xbe90ba01,4 +np.float32,0xbe82ec2a,0xbe84649a,4 +np.float32,0xbf7e1f10,0xbfb98b9e,4 +np.float32,0xbf2d00ea,0xbf3df688,4 +np.float32,0x3db7cdd0,0x3db80d36,4 +np.float32,0xbe388b98,0xbe398f25,4 +np.float32,0xbd86cb40,0xbd86e436,4 +np.float32,0x7f7fffff,0x7fc00000,4 +np.float32,0x3f472a60,0x3f6436c6,4 +np.float32,0xbf5b2c1d,0xbf838d87,4 +np.float32,0x3f0409ea,0x3f0abad8,4 +np.float32,0x3f47dd0e,0x3f6553f0,4 +np.float32,0x3e3eab00,0x3e3fc98a,4 +np.float32,0xbf7c2a7f,0xbfb2e19b,4 +np.float32,0xbeda0048,0xbee13112,4 +np.float32,0x3f46600a,0x3f62f5b2,4 +np.float32,0x3f45aef4,0x3f61de43,4 +np.float32,0x3dd40a50,0x3dd46bc4,4 +np.float32,0xbf6cdd0b,0xbf974191,4 +np.float32,0x3f78de4c,0x3faac725,4 +np.float32,0x3f3c39a4,0x3f53777f,4 +np.float32,0xbe2a30ec,0xbe2afc0b,4 +np.float32,0xbf3c0ef0,0xbf533887,4 +np.float32,0x3ecb6548,0x3ed12a53,4 +np.float32,0x3eb994e8,0x3ebde7fc,4 +np.float32,0x3d4c1ee0,0x3d4c3487,4 +np.float32,0xbf52cb6d,0xbf77a7eb,4 +np.float32,0x3eb905d4,0x3ebd4e80,4 +np.float32,0x3e712428,0x3e736d72,4 +np.float32,0xbf79ee6e,0xbfad22be,4 +np.float32,0x3de6f8b0,0x3de776c1,4 +np.float32,0x3e9b2898,0x3e9da325,4 +np.float32,0x3ea09b20,0x3ea35d20,4 +np.float32,0x3d0ea9a0,0x3d0eb103,4 +np.float32,0xbd911500,0xbd913423,4 +np.float32,0x3e004618,0x3e009c97,4 +np.float32,0x3f5e0e5a,0x3f86654c,4 +np.float32,0x3f2e6300,0x3f3fd88b,4 +np.float32,0x3e0cf5d0,0x3e0d68c3,4 +np.float32,0x3d6a16c0,0x3d6a376c,4 +np.float32,0x3f7174aa,0x3f9db53c,4 +np.float32,0xbe04bba0,0xbe051b81,4 +np.float32,0xbe6fdcb4,0xbe721c92,4 +np.float32,0x3f4379f0,0x3f5e6c31,4 +np.float32,0xbf680098,0xbf913257,4 +np.float32,0xbf3c31ca,0xbf536bea,4 +np.float32,0x3f59db58,0x3f824a4e,4 +np.float32,0xbf3ffc84,0xbf591554,4 +np.float32,0x3d1d5160,0x3d1d5b48,4 +np.float32,0x3f6c64ae,0x3f96a3da,4 +np.float32,0xbf1b49fd,0xbf26daaa,4 +np.float32,0x3ec80be0,0x3ecd8576,4 +np.float32,0x3f3becc0,0x3f530629,4 +np.float32,0xbea93890,0xbeac76c1,4 +np.float32,0x3f5b3acc,0x3f839bbd,4 +np.float32,0xbf5d6818,0xbf85bef9,4 +np.float32,0x3f794266,0x3fab9fa6,4 +np.float32,0xbee8eb7c,0xbef1cf3b,4 +np.float32,0xbf360a06,0xbf4a821e,4 +np.float32,0x3f441cf6,0x3f5f693d,4 +np.float32,0x3e60de40,0x3e62b742,4 +np.float32,0xbebb3d7e,0xbebfafdc,4 +np.float32,0x3e56a3a0,0x3e583e28,4 +np.float32,0x3f375bfe,0x3f4c6499,4 +np.float32,0xbf384d7d,0xbf4dbf9a,4 +np.float32,0x3efb03a4,0x3f032c06,4 +np.float32,0x3f1d5d10,0x3f29794d,4 +np.float32,0xbe25f7dc,0xbe26b41d,4 +np.float32,0x3f6d2f88,0x3f97aebb,4 +np.float32,0xbe9fa100,0xbea255cb,4 +np.float32,0xbf21dafa,0xbf2f382a,4 +np.float32,0x3d3870e0,0x3d3880d9,4 +np.float32,0x3eeaf00c,0x3ef413f4,4 +np.float32,0xbc884ea0,0xbc88503c,4 +np.float32,0xbf7dbdad,0xbfb80b6d,4 +np.float32,0xbf4eb713,0xbf709b46,4 +np.float32,0xbf1c0ad4,0xbf27cd92,4 +np.float32,0x3f323088,0x3f451737,4 +np.float32,0x3e405d88,0x3e4183e1,4 +np.float32,0x3d7ad580,0x3d7afdb4,4 +np.float32,0xbf207338,0xbf2d6927,4 +np.float32,0xbecf7948,0xbed59e1a,4 +np.float32,0x3f16ff94,0x3f217fde,4 +np.float32,0xbdf19588,0xbdf225dd,4 +np.float32,0xbf4d9654,0xbf6eb442,4 +np.float32,0xbf390b9b,0xbf4ed220,4 +np.float32,0xbe155a74,0xbe15e354,4 +np.float32,0x3f519e4c,0x3f759850,4 +np.float32,0xbee3f08c,0xbeec3b84,4 +np.float32,0xbf478be7,0xbf64d23b,4 +np.float32,0xbefdee50,0xbf04d92a,4 +np.float32,0x3e8def78,0x3e8fd1bc,4 +np.float32,0x3e3df2a8,0x3e3f0dee,4 +np.float32,0xbf413e22,0xbf5afd97,4 +np.float32,0xbf1b8bc4,0xbf272d71,4 +np.float32,0xbf31e5be,0xbf44af22,4 +np.float32,0x3de7e080,0x3de86010,4 +np.float32,0xbf5ddf7e,0xbf863645,4 +np.float32,0x3f3eba6a,0x3f57306e,4 +np.float32,0xff7fffff,0x7fc00000,4 +np.float32,0x3ec22d5c,0x3ec72973,4 +np.float32,0x80800000,0x80800000,4 +np.float32,0x3f032e0c,0x3f09ba82,4 +np.float32,0x3d74bd60,0x3d74e2b7,4 +np.float32,0xbea0d61e,0xbea39b42,4 +np.float32,0xbefdfa78,0xbf04e02a,4 +np.float32,0x3e5cb220,0x3e5e70ec,4 +np.float32,0xbe239e54,0xbe2452a4,4 +np.float32,0x3f452738,0x3f61090e,4 +np.float32,0x3e99a2e0,0x3e9c0a66,4 +np.float32,0x3e4394d8,0x3e44ca5f,4 +np.float32,0x3f4472e2,0x3f5fef14,4 +np.float32,0xbf46bc70,0xbf638814,4 +np.float32,0xbf0b910f,0xbf139c7a,4 +np.float32,0x3f36b4a6,0x3f4b753f,4 +np.float32,0x3e0bf478,0x3e0c64f6,4 +np.float32,0x3ce02480,0x3ce02ba9,4 +np.float32,0xbd904b10,0xbd9069b1,4 +np.float32,0xbf7f5d72,0xbfc00b70,4 +np.float32,0x3f62127e,0x3f8a8ca8,4 +np.float32,0xbf320253,0xbf44d6e4,4 +np.float32,0x3f2507be,0x3f335833,4 +np.float32,0x3f299284,0x3f395887,4 +np.float32,0xbd8211b0,0xbd82281d,4 +np.float32,0xbd3374c0,0xbd338376,4 +np.float32,0x3f36c56a,0x3f4b8d30,4 +np.float32,0xbf51f704,0xbf76331f,4 +np.float32,0xbe9871ca,0xbe9acab2,4 +np.float32,0xbe818d8c,0xbe82fa0f,4 +np.float32,0x3f08b958,0x3f103c18,4 +np.float32,0x3f22559a,0x3f2fd698,4 +np.float32,0xbf11f388,0xbf1b4db8,4 +np.float32,0x3ebe1990,0x3ec2c359,4 +np.float32,0xbe75ab38,0xbe7816b6,4 +np.float32,0x3e96102c,0x3e984c99,4 +np.float32,0xbe80d9d2,0xbe824052,4 +np.float32,0x3ef47588,0x3efeda7f,4 +np.float32,0xbe45e524,0xbe4725ea,4 +np.float32,0x3f7f9e7a,0x3fc213ff,4 +np.float32,0x3f1d3c36,0x3f294faa,4 +np.float32,0xbf3c58db,0xbf53a591,4 +np.float32,0x3f0d3d20,0x3f159c69,4 +np.float32,0x3f744be6,0x3fa23552,4 +np.float32,0x3f2e0cea,0x3f3f630e,4 +np.float32,0x3e193c10,0x3e19cff7,4 +np.float32,0xbf4150ac,0xbf5b19dd,4 +np.float32,0xbf145f72,0xbf1e4355,4 +np.float32,0xbb76cc00,0xbb76cc26,4 +np.float32,0x3f756780,0x3fa41b3e,4 +np.float32,0x3ea9b868,0x3eacfe3c,4 +np.float32,0x3d07c920,0x3d07cf7f,4 +np.float32,0xbf2263d4,0xbf2fe8ff,4 +np.float32,0x3e53b3f8,0x3e553daa,4 +np.float32,0xbf785be8,0xbfa9b5ba,4 +np.float32,0x3f324f7a,0x3f454254,4 +np.float32,0xbf2188f2,0xbf2ece5b,4 +np.float32,0xbe33781c,0xbe3466a2,4 +np.float32,0xbd3cf120,0xbd3d024c,4 +np.float32,0x3f06b18a,0x3f0dd70f,4 +np.float32,0x3f40d63e,0x3f5a5f6a,4 +np.float32,0x3f752340,0x3fa3a41e,4 +np.float32,0xbe1cf1c0,0xbe1d90bc,4 +np.float32,0xbf02d948,0xbf0957d7,4 +np.float32,0x3f73bed0,0x3fa14bf7,4 +np.float32,0x3d914920,0x3d916864,4 +np.float32,0x7fa00000,0x7fe00000,4 +np.float32,0xbe67a5d8,0xbe69aba7,4 +np.float32,0x3f689c4a,0x3f91eb9f,4 +np.float32,0xbf196e00,0xbf248601,4 +np.float32,0xbf50dacb,0xbf7444fe,4 +np.float32,0x3f628b86,0x3f8b0e1e,4 +np.float32,0x3f6ee2f2,0x3f99fe7f,4 +np.float32,0x3ee5df40,0x3eee6492,4 +np.float32,0x3f501746,0x3f72f41b,4 +np.float32,0xbf1f0f18,0xbf2ba164,4 +np.float32,0xbf1a8bfd,0xbf25ec01,4 +np.float32,0xbd4926f0,0xbd493ba9,4 +np.float32,0xbf4e364f,0xbf6fc17b,4 +np.float32,0x3e50c578,0x3e523ed4,4 +np.float32,0x3f65bf10,0x3f8e95ce,4 +np.float32,0xbe8d75a2,0xbe8f52f2,4 +np.float32,0xbf3f557e,0xbf581962,4 +np.float32,0xbeff2bfc,0xbf05903a,4 +np.float32,0x3f5e8bde,0x3f86e3d8,4 +np.float32,0xbf7a0012,0xbfad4b9b,4 +np.float32,0x3edefce0,0x3ee6b790,4 +np.float32,0xbf0003de,0xbf060f09,4 +np.float32,0x3efc4650,0x3f03e548,4 +np.float32,0x3f4582e4,0x3f6198f5,4 +np.float32,0x3f10086c,0x3f18f9d0,4 +np.float32,0x3f1cd304,0x3f28ca77,4 +np.float32,0x3f683366,0x3f916e8d,4 +np.float32,0xbed49392,0xbedb3675,4 +np.float32,0xbf6fe5f6,0xbf9b6c0e,4 +np.float32,0xbf59b416,0xbf8224f6,4 +np.float32,0x3d20c960,0x3d20d3f4,4 +np.float32,0x3f6b00d6,0x3f94dbe7,4 +np.float32,0x3f6c26ae,0x3f965352,4 +np.float32,0xbf370ea6,0xbf4bf5dd,4 +np.float32,0x3dfe7230,0x3dff1af1,4 +np.float32,0xbefc21a8,0xbf03d038,4 +np.float32,0x3f16a990,0x3f21156a,4 +np.float32,0xbef8ac0c,0xbf01d48f,4 +np.float32,0x3f170de8,0x3f21919d,4 +np.float32,0x3db9ef80,0x3dba3122,4 +np.float32,0x3d696400,0x3d698461,4 +np.float32,0x3f007aa2,0x3f069843,4 +np.float32,0x3f22827c,0x3f3010a9,4 +np.float32,0x3f3650dc,0x3f4ae6f1,4 +np.float32,0xbf1d8037,0xbf29a5e1,4 +np.float32,0xbf08fdc4,0xbf108d0e,4 +np.float32,0xbd8df350,0xbd8e1079,4 +np.float32,0xbf36bb32,0xbf4b7e98,4 +np.float32,0x3f2e3756,0x3f3f9ced,4 +np.float32,0x3d5a6f20,0x3d5a89aa,4 +np.float32,0x3f55d568,0x3f7d1889,4 +np.float32,0x3e1ed110,0x3e1f75d9,4 +np.float32,0x3e7386b8,0x3e75e1dc,4 +np.float32,0x3f48ea0e,0x3f670434,4 +np.float32,0x3e921fb0,0x3e942f14,4 +np.float32,0xbf0d4d0b,0xbf15af7f,4 +np.float32,0x3f179ed2,0x3f224549,4 +np.float32,0xbf3a328e,0xbf507e6d,4 +np.float32,0xbf74591a,0xbfa24b6e,4 +np.float32,0x3ec7d1c4,0x3ecd4657,4 +np.float32,0xbf6ecbed,0xbf99de85,4 +np.float32,0x3db0bd00,0x3db0f559,4 +np.float32,0x7f800000,0x7fc00000,4 +np.float32,0x3e0373b8,0x3e03d0d6,4 +np.float32,0xbf439784,0xbf5e9a04,4 +np.float32,0xbef97a9e,0xbf024ac6,4 +np.float32,0x3e4d71a8,0x3e4ed90a,4 +np.float32,0xbf14d868,0xbf1ed7e3,4 +np.float32,0xbf776870,0xbfa7ce37,4 +np.float32,0xbe32a500,0xbe339038,4 +np.float32,0xbf326d8a,0xbf456c3d,4 +np.float32,0xbe9b758c,0xbe9df3e7,4 +np.float32,0x3d9515a0,0x3d95376a,4 +np.float32,0x3e3f7320,0x3e40953e,4 +np.float32,0xbee57e7e,0xbeedf84f,4 +np.float32,0x3e821e94,0x3e838ffd,4 +np.float32,0x3f74beaa,0x3fa2f721,4 +np.float32,0xbe9b7672,0xbe9df4d9,4 +np.float32,0x3f4041fc,0x3f597e71,4 +np.float32,0xbe9ea7c4,0xbea14f92,4 +np.float32,0xbf800000,0xbfc90fdb,4 +np.float32,0x3e04fb90,0x3e055bfd,4 +np.float32,0xbf14d3d6,0xbf1ed245,4 +np.float32,0xbe84ebec,0xbe86763e,4 +np.float32,0x3f08e568,0x3f107039,4 +np.float32,0x3d8dc9e0,0x3d8de6ef,4 +np.float32,0x3ea4549c,0x3ea74a94,4 +np.float32,0xbebd2806,0xbec1bf51,4 +np.float32,0x3f311a26,0x3f439498,4 +np.float32,0xbf3d2222,0xbf54cf7e,4 +np.float32,0x3e00c500,0x3e011c81,4 +np.float32,0xbe35ed1c,0xbe36e5a9,4 +np.float32,0xbd4ec020,0xbd4ed6a0,4 +np.float32,0x3e1eb088,0x3e1f54eb,4 +np.float32,0x3cf94840,0x3cf9521a,4 +np.float32,0xbf010c5d,0xbf0740e0,4 +np.float32,0xbf3bd63b,0xbf52e502,4 +np.float32,0x3f233f30,0x3f310542,4 +np.float32,0x3ea24128,0x3ea519d7,4 +np.float32,0x3f478b38,0x3f64d124,4 +np.float32,0x3f1e0c6c,0x3f2a57ec,4 +np.float32,0xbf3ad294,0xbf51680a,4 +np.float32,0x3ede0554,0x3ee5a4b4,4 +np.float32,0x3e451a98,0x3e46577d,4 +np.float32,0x3f520164,0x3f764542,4 +np.float32,0x0,0x0,4 +np.float32,0xbd056cd0,0xbd0572db,4 +np.float32,0xbf58b018,0xbf812f5e,4 +np.float32,0x3e036eb0,0x3e03cbc3,4 +np.float32,0x3d1377a0,0x3d137fc9,4 +np.float32,0xbf692d3a,0xbf929a2c,4 +np.float32,0xbec60fb8,0xbecb5dea,4 +np.float32,0x3ed23340,0x3ed89a8e,4 +np.float32,0x3c87f040,0x3c87f1d9,4 +np.float32,0x3dac62f0,0x3dac9737,4 +np.float32,0xbed97c16,0xbee09f02,4 +np.float32,0xbf2d5f3c,0xbf3e769c,4 +np.float32,0xbc3b7c40,0xbc3b7d4c,4 +np.float32,0x3ed998ec,0x3ee0bedd,4 +np.float32,0x3dd86630,0x3dd8cdcb,4 +np.float32,0x3e8b4304,0x3e8d09ea,4 +np.float32,0x3f51e6b0,0x3f761697,4 +np.float32,0x3ec51f24,0x3eca5923,4 +np.float32,0xbf647430,0xbf8d2307,4 +np.float32,0x3f253d9c,0x3f339eb2,4 +np.float32,0x3dc969d0,0x3dc9bd4b,4 +np.float32,0xbc2f1300,0xbc2f13da,4 +np.float32,0xbf170007,0xbf21806d,4 +np.float32,0x3f757d10,0x3fa4412e,4 +np.float32,0xbe7864ac,0xbe7ae564,4 +np.float32,0x3f2ffe90,0x3f420cfb,4 +np.float32,0xbe576138,0xbe590012,4 +np.float32,0xbf517a21,0xbf755959,4 +np.float32,0xbf159cfe,0xbf1fc9d5,4 +np.float32,0xbf638b2a,0xbf8c22cf,4 +np.float32,0xff800000,0x7fc00000,4 +np.float32,0x3ed19ca0,0x3ed7f569,4 +np.float32,0x3f7c4460,0x3fb32d26,4 +np.float32,0x3ebfae6c,0x3ec477ab,4 +np.float32,0x3dd452d0,0x3dd4b4a8,4 +np.float32,0x3f471482,0x3f6413fb,4 +np.float32,0xbf49d704,0xbf6883fe,4 +np.float32,0xbd42c4e0,0xbd42d7af,4 +np.float32,0xbeb02994,0xbeb3d668,4 +np.float32,0x3f4d1fd8,0x3f6dedd2,4 +np.float32,0x3efb591c,0x3f035d11,4 +np.float32,0x80000000,0x80000000,4 +np.float32,0xbf50f782,0xbf7476ad,4 +np.float32,0x3d7232c0,0x3d7256f0,4 +np.float32,0x3f649460,0x3f8d46bb,4 +np.float32,0x3f5561bc,0x3f7c46a9,4 +np.float32,0x3e64f6a0,0x3e66ea5d,4 +np.float32,0x3e5b0470,0x3e5cb8f9,4 +np.float32,0xbe9b6b2c,0xbe9de904,4 +np.float32,0x3f6c33f4,0x3f966486,4 +np.float32,0x3f5cee54,0x3f854613,4 +np.float32,0x3ed3e044,0x3eda716e,4 +np.float32,0xbf3cac7f,0xbf542131,4 +np.float32,0x3c723500,0x3c723742,4 +np.float32,0x3de59900,0x3de614d3,4 +np.float32,0xbdf292f8,0xbdf32517,4 +np.float32,0x3f05c8b2,0x3f0cc59b,4 +np.float32,0xbf1ab182,0xbf261b14,4 +np.float32,0xbda396f0,0xbda3c39a,4 +np.float32,0xbf270ed0,0xbf360231,4 +np.float32,0x3f2063e6,0x3f2d557e,4 +np.float32,0x3c550280,0x3c550409,4 +np.float32,0xbe103b48,0xbe10b679,4 +np.float32,0xbebae390,0xbebf4f40,4 +np.float32,0x3f3bc868,0x3f52d0aa,4 +np.float32,0xbd62f880,0xbd631647,4 +np.float32,0xbe7a38f4,0xbe7cc833,4 +np.float32,0x3f09d796,0x3f118f39,4 +np.float32,0xbf5fa558,0xbf8802d0,4 +np.float32,0x3f111cc8,0x3f1a48b0,4 +np.float32,0x3e831958,0x3e849356,4 +np.float32,0xbf614dbd,0xbf89bc3b,4 +np.float32,0xbd521510,0xbd522cac,4 +np.float32,0x3f05af22,0x3f0ca7a0,4 +np.float32,0xbf1ac60e,0xbf2634df,4 +np.float32,0xbf6bd05e,0xbf95e3fe,4 +np.float32,0xbd1fa6e0,0xbd1fb13b,4 +np.float32,0xbeb82f7a,0xbebc68b1,4 +np.float32,0xbd92aaf8,0xbd92cb23,4 +np.float32,0xbe073a54,0xbe079fbf,4 +np.float32,0xbf198655,0xbf24a468,4 +np.float32,0x3f62f6d8,0x3f8b81ba,4 +np.float32,0x3eef4310,0x3ef8f4f9,4 +np.float32,0x3e8988e0,0x3e8b3eae,4 +np.float32,0xbf3ddba5,0xbf55e367,4 +np.float32,0x3dc6d2e0,0x3dc7232b,4 +np.float32,0xbf31040e,0xbf437601,4 +np.float32,0x3f1bb74a,0x3f276442,4 +np.float32,0xbf0075d2,0xbf0692b3,4 +np.float32,0xbf606ce0,0xbf88d0ff,4 +np.float32,0xbf083856,0xbf0fa39d,4 +np.float32,0xbdb25b20,0xbdb2950a,4 +np.float32,0xbeb86860,0xbebca5ae,4 +np.float32,0x3de83160,0x3de8b176,4 +np.float32,0xbf33a98f,0xbf472664,4 +np.float32,0x3e7795f8,0x3e7a1058,4 +np.float32,0x3e0ca6f8,0x3e0d192a,4 +np.float32,0xbf1aef60,0xbf2668c3,4 +np.float32,0xbda53b58,0xbda5695e,4 +np.float32,0xbf178096,0xbf221fc5,4 +np.float32,0xbf0a4159,0xbf120ccf,4 +np.float32,0x3f7bca36,0x3fb1d0df,4 +np.float32,0xbef94360,0xbf022b26,4 +np.float32,0xbef16f36,0xbefb6ad6,4 +np.float32,0x3f53a7e6,0x3f792e25,4 +np.float32,0xbf7c536f,0xbfb35993,4 +np.float32,0xbe84aaa0,0xbe8632a2,4 +np.float32,0x3ecb3998,0x3ed0fab9,4 +np.float32,0x3f539304,0x3f79090a,4 +np.float32,0xbf3c7816,0xbf53d3b3,4 +np.float32,0xbe7a387c,0xbe7cc7b7,4 +np.float32,0x3f7000e4,0x3f9b92b1,4 +np.float32,0x3e08fd70,0x3e0966e5,4 +np.float32,0x3db97ba0,0x3db9bcc8,4 +np.float32,0xbee99056,0xbef2886a,4 +np.float32,0xbf0668da,0xbf0d819e,4 +np.float32,0x3e58a408,0x3e5a4a51,4 +np.float32,0x3f3440b8,0x3f47faed,4 +np.float32,0xbf19a2ce,0xbf24c7ff,4 +np.float32,0xbe75e990,0xbe7856ee,4 +np.float32,0x3f3c865c,0x3f53e8cb,4 +np.float32,0x3e5e03d0,0x3e5fcac9,4 +np.float32,0x3edb8e34,0x3ee2e932,4 +np.float32,0xbf7e1f5f,0xbfb98ce4,4 +np.float32,0xbf7372ff,0xbfa0d0ae,4 +np.float32,0xbf3ee850,0xbf577548,4 +np.float32,0x3ef19658,0x3efb9737,4 +np.float32,0xbe8088de,0xbe81ecaf,4 +np.float32,0x800000,0x800000,4 +np.float32,0xbde39dd8,0xbde4167a,4 +np.float32,0xbf065d7a,0xbf0d7441,4 +np.float32,0xbde52c78,0xbde5a79b,4 +np.float32,0xbe3a28c0,0xbe3b333e,4 +np.float32,0x3f6e8b3c,0x3f998516,4 +np.float32,0x3f3485c2,0x3f485c39,4 +np.float32,0x3e6f2c68,0x3e71673e,4 +np.float32,0xbe4ec9cc,0xbe50385e,4 +np.float32,0xbf1c3bb0,0xbf280b39,4 +np.float32,0x3ec8ea18,0x3ece76f7,4 +np.float32,0x3e26b5f8,0x3e2774c9,4 +np.float32,0x3e1e4a38,0x3e1eed5c,4 +np.float32,0xbee7a106,0xbef05c6b,4 +np.float32,0xbf305928,0xbf4289d8,4 +np.float32,0x3f0c431c,0x3f147118,4 +np.float32,0xbe57ba6c,0xbe595b52,4 +np.float32,0x3eabc9cc,0x3eaf2fc7,4 +np.float32,0xbef1ed24,0xbefbf9ae,4 +np.float32,0xbf61b576,0xbf8a29cc,4 +np.float32,0x3e9c1ff4,0x3e9ea6cb,4 +np.float32,0x3f6c53b2,0x3f968dbe,4 +np.float32,0x3e2d1b80,0x3e2df156,4 +np.float32,0x3e9f2f70,0x3ea1de4a,4 +np.float32,0xbf5861ee,0xbf80e61a,4 +np.float32,0x3f429144,0x3f5d0505,4 +np.float32,0x3e235cc8,0x3e24103e,4 +np.float32,0xbf354879,0xbf496f6a,4 +np.float32,0xbf20a146,0xbf2da447,4 +np.float32,0x3e8d8968,0x3e8f6785,4 +np.float32,0x3f3fbc94,0x3f58b4c1,4 +np.float32,0x3f2c5f50,0x3f3d1b9f,4 +np.float32,0x3f7bf0f8,0x3fb23d23,4 +np.float32,0xbf218282,0xbf2ec60f,4 +np.float32,0x3f2545aa,0x3f33a93e,4 +np.float32,0xbf4b17be,0xbf6a9018,4 +np.float32,0xbb9df700,0xbb9df728,4 +np.float32,0x3f685d54,0x3f91a06c,4 +np.float32,0x3efdfe2c,0x3f04e24c,4 +np.float32,0x3ef1c5a0,0x3efbccd9,4 +np.float32,0xbf41d731,0xbf5be76e,4 +np.float32,0x3ebd1360,0x3ec1a919,4 +np.float32,0xbf706bd4,0xbf9c2d58,4 +np.float32,0x3ea525e4,0x3ea8279d,4 +np.float32,0xbe51f1b0,0xbe537186,4 +np.float32,0x3f5e8cf6,0x3f86e4f4,4 +np.float32,0xbdad2520,0xbdad5a19,4 +np.float32,0xbf5c5704,0xbf84b0e5,4 +np.float32,0x3f47b54e,0x3f65145e,4 +np.float32,0x3eb4fc78,0x3eb8fc0c,4 +np.float32,0x3dca1450,0x3dca68a1,4 +np.float32,0x3eb02a74,0x3eb3d757,4 +np.float32,0x3f74ae6a,0x3fa2db75,4 +np.float32,0x3f800000,0x3fc90fdb,4 +np.float32,0xbdb46a00,0xbdb4a5f2,4 +np.float32,0xbe9f2ba6,0xbea1da4e,4 +np.float32,0x3f0afa70,0x3f12e8f7,4 +np.float32,0xbf677b20,0xbf909547,4 +np.float32,0x3eff9188,0x3f05cacf,4 +np.float32,0x3f720562,0x3f9e911b,4 +np.float32,0xbf7180d8,0xbf9dc794,4 +np.float32,0xbee7d076,0xbef0919d,4 +np.float32,0x3f0432ce,0x3f0aea95,4 +np.float32,0x3f3bc4c8,0x3f52cb54,4 +np.float32,0xbea72f30,0xbeaa4ebe,4 +np.float32,0x3e90ed00,0x3e92ef33,4 +np.float32,0xbda63670,0xbda6654a,4 +np.float32,0xbf5a6f85,0xbf82d7e0,4 +np.float32,0x3e6e8808,0x3e70be34,4 +np.float32,0xbf4f3822,0xbf71768f,4 +np.float32,0x3e5c8a68,0x3e5e483f,4 +np.float32,0xbf0669d4,0xbf0d82c4,4 +np.float32,0xbf79f77c,0xbfad37b0,4 +np.float32,0x3f25c82c,0x3f345453,4 +np.float32,0x3f1b2948,0x3f26b188,4 +np.float32,0x3ef7e288,0x3f016159,4 +np.float32,0x3c274280,0x3c27433e,4 +np.float32,0xbf4c8fa0,0xbf6cfd5e,4 +np.float32,0x3ea4ccb4,0x3ea7c966,4 +np.float32,0xbf7b157e,0xbfafefca,4 +np.float32,0xbee4c2b0,0xbeed264d,4 +np.float32,0xbc1fd640,0xbc1fd6e6,4 +np.float32,0x3e892308,0x3e8ad4f6,4 +np.float32,0xbf3f69c7,0xbf5837ed,4 +np.float32,0x3ec879e8,0x3ecdfd05,4 +np.float32,0x3f07a8c6,0x3f0efa30,4 +np.float32,0x3f67b880,0x3f90dd4d,4 +np.float32,0x3e8a11c8,0x3e8bccd5,4 +np.float32,0x3f7df6fc,0x3fb8e935,4 +np.float32,0xbef3e498,0xbefe3599,4 +np.float32,0xbf18ad7d,0xbf2395d8,4 +np.float32,0x3f2bce74,0x3f3c57f5,4 +np.float32,0xbf38086e,0xbf4d5c2e,4 +np.float32,0x3f772d7a,0x3fa75c35,4 +np.float32,0xbf3b6e24,0xbf524c00,4 +np.float32,0xbdd39108,0xbdd3f1d4,4 +np.float32,0xbf691f6b,0xbf928974,4 +np.float32,0x3f146188,0x3f1e45e4,4 +np.float32,0xbf56045b,0xbf7d6e03,4 +np.float32,0xbf4b2ee4,0xbf6ab622,4 +np.float32,0xbf3fa3f6,0xbf588f9d,4 +np.float32,0x3f127bb0,0x3f1bf398,4 +np.float32,0x3ed858a0,0x3edf5d3e,4 +np.float32,0xbd6de3b0,0xbd6e05fa,4 +np.float32,0xbecc662c,0xbed24261,4 +np.float32,0xbd6791d0,0xbd67b170,4 +np.float32,0xbf146016,0xbf1e441e,4 +np.float32,0xbf61f04c,0xbf8a6841,4 +np.float32,0xbe7f16d0,0xbe80e6e7,4 +np.float32,0xbebf93e6,0xbec45b10,4 +np.float32,0xbe8a59fc,0xbe8c17d1,4 +np.float32,0xbebc7a0c,0xbec10426,4 +np.float32,0xbf2a682e,0xbf3a7649,4 +np.float32,0xbe18d0cc,0xbe19637b,4 +np.float32,0x3d7f5100,0x3d7f7b66,4 +np.float32,0xbf10f5fa,0xbf1a1998,4 +np.float32,0x3f25e956,0x3f347fdc,4 +np.float32,0x3e6e8658,0x3e70bc78,4 +np.float32,0x3f21a5de,0x3f2ef3a5,4 +np.float32,0xbf4e71d4,0xbf702607,4 +np.float32,0xbf49d6b6,0xbf688380,4 +np.float32,0xbdb729c0,0xbdb7687c,4 +np.float32,0xbf63e1f4,0xbf8c81c7,4 +np.float32,0x3dda6cb0,0x3ddad73e,4 +np.float32,0x3ee1bc40,0x3ee9c612,4 +np.float32,0x3ebdb5f8,0x3ec2581b,4 +np.float32,0x3f7d9576,0x3fb77646,4 +np.float32,0x3e087140,0x3e08d971,4 +np.float64,0xbfdba523cfb74a48,0xbfdc960ddd9c0506,3 +np.float64,0x3fb51773622a2ee0,0x3fb51d93f77089d5,3 +np.float64,0x3fc839f6d33073f0,0x3fc85f9a47dfe8e6,3 +np.float64,0xbfecba2d82f9745b,0xbff1d55416c6c993,3 +np.float64,0x3fd520fe47aa41fc,0x3fd58867f1179634,3 +np.float64,0x3fe1b369c56366d4,0x3fe2c1ac9dd2c45a,3 +np.float64,0xbfec25a7cd784b50,0xbff133417389b12d,3 +np.float64,0xbfd286342ea50c68,0xbfd2cb0bca22e66d,3 +np.float64,0x3fd5f6fe5eabedfc,0x3fd66bad16680d08,3 +np.float64,0xbfe863a87570c751,0xbfebbb9b637eb6dc,3 +np.float64,0x3fc97f5b4d32feb8,0x3fc9ab5066d8eaec,3 +np.float64,0xbfcb667af936ccf4,0xbfcb9d3017047a1d,3 +np.float64,0xbfd1b7b9afa36f74,0xbfd1f3c175706154,3 +np.float64,0x3fef97385b7f2e70,0x3ff6922a1a6c709f,3 +np.float64,0xbfd13e4205a27c84,0xbfd1757c993cdb74,3 +np.float64,0xbfd18d88aca31b12,0xbfd1c7dd75068f7d,3 +np.float64,0x3fe040ce0f60819c,0x3fe10c59d2a27089,3 +np.float64,0xbfddc7deddbb8fbe,0xbfdef9de5baecdda,3 +np.float64,0xbfcf6e96193edd2c,0xbfcfc1bb7396b9a3,3 +np.float64,0x3fd544f494aa89e8,0x3fd5ae850e2b37dd,3 +np.float64,0x3fe15b381fe2b670,0x3fe25841c7bfe2af,3 +np.float64,0xbfde793420bcf268,0xbfdfc2ddc7b4a341,3 +np.float64,0x3fd0d5db30a1abb8,0x3fd1092cef4aa4fb,3 +np.float64,0x3fe386a08c670d42,0x3fe50059bbf7f491,3 +np.float64,0xbfe0aae3a96155c8,0xbfe1880ef13e95ce,3 +np.float64,0xbfe80eeb03f01dd6,0xbfeb39e9f107e944,3 +np.float64,0xbfd531af3caa635e,0xbfd59a178f17552a,3 +np.float64,0x3fcced14ab39da28,0x3fcd2d9a806337ef,3 +np.float64,0xbfdb4c71bcb698e4,0xbfdc33d9d9daf708,3 +np.float64,0xbfde7375ecbce6ec,0xbfdfbc5611bc48ff,3 +np.float64,0x3fecc5707a798ae0,0x3ff1e2268d778017,3 +np.float64,0x3fe8f210a1f1e422,0x3fec9b3349a5baa2,3 +np.float64,0x3fe357f9b8e6aff4,0x3fe4c5a0b89a9228,3 +np.float64,0xbfe0f863b761f0c8,0xbfe1e3283494c3d4,3 +np.float64,0x3fd017c395a02f88,0x3fd044761f2f4a66,3 +np.float64,0x3febeb4746f7d68e,0x3ff0f6b955e7feb6,3 +np.float64,0xbfbdaaeeae3b55e0,0xbfbdbc0950109261,3 +np.float64,0xbfea013095f40261,0xbfee5b8fe8ad8593,3 +np.float64,0xbfe9f87b7973f0f7,0xbfee4ca3a8438d72,3 +np.float64,0x3fd37f77cfa6fef0,0x3fd3d018c825f057,3 +np.float64,0x3fb0799cee20f340,0x3fb07c879e7cb63f,3 +np.float64,0xbfdcfd581cb9fab0,0xbfde15e35314b52d,3 +np.float64,0xbfd49781b8a92f04,0xbfd4f6fa1516fefc,3 +np.float64,0x3fb3fcb6d627f970,0x3fb401ed44a713a8,3 +np.float64,0x3fd5737ef8aae6fc,0x3fd5dfe42d4416c7,3 +np.float64,0x7ff4000000000000,0x7ffc000000000000,3 +np.float64,0xbfe56ae780ead5cf,0xbfe776ea5721b900,3 +np.float64,0x3fd4567786a8acf0,0x3fd4b255421c161a,3 +np.float64,0x3fef6fb58cfedf6c,0x3ff62012dfcf0a33,3 +np.float64,0xbfd1dbcd3da3b79a,0xbfd2194fd628f74d,3 +np.float64,0x3fd9350016b26a00,0x3fd9e8b01eb023e9,3 +np.float64,0xbfe4fb3a69e9f675,0xbfe6e1d2c9eca56c,3 +np.float64,0x3fe9fe0f73f3fc1e,0x3fee5631cfd39772,3 +np.float64,0xbfd51c1bc6aa3838,0xbfd5833b3bd53543,3 +np.float64,0x3fc64158e12c82b0,0x3fc65e7352f237d7,3 +np.float64,0x3fd0d8ee1ba1b1dc,0x3fd10c5c99a16f0e,3 +np.float64,0x3fd5554e15aaaa9c,0x3fd5bfdb9ec9e873,3 +np.float64,0x3fe61ce209ec39c4,0x3fe869bc4c28437d,3 +np.float64,0xbfe4e42c8c69c859,0xbfe6c356dac7e2db,3 +np.float64,0xbfe157021062ae04,0xbfe2533ed39f4212,3 +np.float64,0x3fe844066cf0880c,0x3feb8aea0b7bd0a4,3 +np.float64,0x3fe55016586aa02c,0x3fe752e4b2a67b9f,3 +np.float64,0x3fdabce619b579cc,0x3fdb95809bc789d9,3 +np.float64,0x3fee03bae37c0776,0x3ff3778ba38ca882,3 +np.float64,0xbfeb2f5844f65eb0,0xbff03dd1b767d3c8,3 +np.float64,0x3fedcfdbaffb9fb8,0x3ff32e81d0639164,3 +np.float64,0x3fe06fc63ee0df8c,0x3fe142fc27f92eaf,3 +np.float64,0x3fe7ce90fd6f9d22,0x3fead8f832bbbf5d,3 +np.float64,0xbfbc0015ce380028,0xbfbc0e7470e06e86,3 +np.float64,0xbfe9b3de90f367bd,0xbfedd857931dfc6b,3 +np.float64,0xbfcb588f5936b120,0xbfcb8ef0124a4f21,3 +np.float64,0x3f8d376a503a6f00,0x3f8d37ab43e7988d,3 +np.float64,0xbfdb123a40b62474,0xbfdbf38b6cf5db92,3 +np.float64,0xbfee7da6be7cfb4e,0xbff433042cd9d5eb,3 +np.float64,0xbfc4c9e01b2993c0,0xbfc4e18dbafe37ef,3 +np.float64,0x3fedd42faffba860,0x3ff334790cd18a19,3 +np.float64,0x3fe9cdf772f39bee,0x3fee044f87b856ab,3 +np.float64,0x3fe0245881e048b2,0x3fe0eb5a1f739c8d,3 +np.float64,0xbfe4712bd9e8e258,0xbfe62cb3d82034aa,3 +np.float64,0x3fe9a16b46f342d6,0x3fedb972b2542551,3 +np.float64,0xbfe57ab4536af568,0xbfe78c34b03569c2,3 +np.float64,0x3fb6d6ceb22dada0,0x3fb6de976964d6dd,3 +np.float64,0x3fc3ac23a3275848,0x3fc3c02de53919b8,3 +np.float64,0xbfccb531e7396a64,0xbfccf43ec69f6281,3 +np.float64,0xbfd2f07fc8a5e100,0xbfd33a35a8c41b62,3 +np.float64,0xbfe3e5dd04e7cbba,0xbfe57940157c27ba,3 +np.float64,0x3feefe40757dfc80,0x3ff51bc72b846af6,3 +np.float64,0x8000000000000001,0x8000000000000001,3 +np.float64,0x3fecb7b766796f6e,0x3ff1d28972a0fc7e,3 +np.float64,0xbfea1bf1357437e2,0xbfee89a6532bfd71,3 +np.float64,0xbfca3983b7347308,0xbfca696463b791ef,3 +np.float64,0x10000000000000,0x10000000000000,3 +np.float64,0xbf886b45d030d680,0xbf886b6bbc04314b,3 +np.float64,0x3fd5224bb5aa4498,0x3fd589c92e82218f,3 +np.float64,0xbfec799874f8f331,0xbff18d5158b8e640,3 +np.float64,0xbf88124410302480,0xbf88126863350a16,3 +np.float64,0xbfe37feaaa66ffd6,0xbfe4f7e24382e79d,3 +np.float64,0x3fd777eca1aeefd8,0x3fd8076ead6d55dc,3 +np.float64,0x3fecaaeb3af955d6,0x3ff1c4159fa3e965,3 +np.float64,0xbfeb81e4e6f703ca,0xbff08d4e4c77fada,3 +np.float64,0xbfd7d0a0edafa142,0xbfd866e37010312e,3 +np.float64,0x3feda48c00fb4918,0x3ff2f3fd33c36307,3 +np.float64,0x3feb87ecc4770fda,0x3ff09336e490deda,3 +np.float64,0xbfefd78ad27faf16,0xbff78abbafb50ac1,3 +np.float64,0x3fe58e918c6b1d24,0x3fe7a70b38cbf016,3 +np.float64,0x3fda163b95b42c78,0x3fdade86b88ba4ee,3 +np.float64,0x3fe8fc1aaf71f836,0x3fecab3f93b59df5,3 +np.float64,0xbf8de56f903bcac0,0xbf8de5b527cec797,3 +np.float64,0xbfec112db2f8225b,0xbff11dd648de706f,3 +np.float64,0x3fc3214713264290,0x3fc333b1c862f7d0,3 +np.float64,0xbfeb5e5836f6bcb0,0xbff06ac364b49177,3 +np.float64,0x3fc23d9777247b30,0x3fc24d8ae3bcb615,3 +np.float64,0xbfdf0eed65be1dda,0xbfe036cea9b9dfb6,3 +np.float64,0xbfb2d5c85a25ab90,0xbfb2da24bb409ff3,3 +np.float64,0xbfecdda0c3f9bb42,0xbff1fdf94fc6e89e,3 +np.float64,0x3fdfe79154bfcf24,0x3fe0b338e0476a9d,3 +np.float64,0xbfd712ac6bae2558,0xbfd79abde21f287b,3 +np.float64,0x3fea3f148a747e2a,0x3feec6bed9d4fa04,3 +np.float64,0x3fd4879e4ca90f3c,0x3fd4e632fa4e2edd,3 +np.float64,0x3fe9137a9e7226f6,0x3fecd0c441088d6a,3 +np.float64,0xbfc75bf4ef2eb7e8,0xbfc77da8347d742d,3 +np.float64,0xbfd94090a0b28122,0xbfd9f5458816ed5a,3 +np.float64,0x3fde439cbcbc8738,0x3fdf85fbf496b61f,3 +np.float64,0xbfe18bacdce3175a,0xbfe29210e01237f7,3 +np.float64,0xbfd58ec413ab1d88,0xbfd5fcd838f0a934,3 +np.float64,0xbfeae5af2d75cb5e,0xbfeff1de1b4a06be,3 +np.float64,0x3fb64d1a162c9a30,0x3fb65458fb831354,3 +np.float64,0x3fc18b1e15231640,0x3fc1994c6ffd7a6a,3 +np.float64,0xbfd7b881bcaf7104,0xbfd84ce89a9ee8c7,3 +np.float64,0x3feb916a40f722d4,0x3ff09c8aa851d7c4,3 +np.float64,0x3fdab5fbb5b56bf8,0x3fdb8de43961bbde,3 +np.float64,0x3fe4f35402e9e6a8,0x3fe6d75dc5082894,3 +np.float64,0x3fe2fdb2e5e5fb66,0x3fe454e32a5d2182,3 +np.float64,0x3fe8607195f0c0e4,0x3febb6a4c3bf6a5c,3 +np.float64,0x3fd543ca9aaa8794,0x3fd5ad49203ae572,3 +np.float64,0x3fe8e05ca1f1c0ba,0x3fec7eff123dcc58,3 +np.float64,0x3fe298b6ca65316e,0x3fe3d81d2927c4dd,3 +np.float64,0x3fcfecea733fd9d8,0x3fd0220f1d0faf78,3 +np.float64,0xbfe2e739f065ce74,0xbfe439004e73772a,3 +np.float64,0xbfd1ae6b82a35cd8,0xbfd1ea129a5ee756,3 +np.float64,0xbfeb7edff576fdc0,0xbff08a5a638b8a8b,3 +np.float64,0x3fe5b645ff6b6c8c,0x3fe7dcee1faefe3f,3 +np.float64,0xbfd478427ba8f084,0xbfd4d5fc7c239e60,3 +np.float64,0xbfe39904e3e7320a,0xbfe517972b30b1e5,3 +np.float64,0xbfd3b75b6ba76eb6,0xbfd40acf20a6e074,3 +np.float64,0x3fd596267aab2c4c,0x3fd604b01faeaf75,3 +np.float64,0x3fe134463762688c,0x3fe229fc36784a72,3 +np.float64,0x3fd25dadf7a4bb5c,0x3fd2a0b9e04ea060,3 +np.float64,0xbfc05d3e0b20ba7c,0xbfc068bd2bb9966f,3 +np.float64,0x3f8cf517b039ea00,0x3f8cf556ed74b163,3 +np.float64,0x3fda87361cb50e6c,0x3fdb5a75af897e7f,3 +np.float64,0x3fe53e1926ea7c32,0x3fe73acf01b8ff31,3 +np.float64,0x3fe2e94857e5d290,0x3fe43b8cc820f9c7,3 +np.float64,0x3fd81fe6acb03fcc,0x3fd8bc623c0068cf,3 +np.float64,0xbfddf662c3bbecc6,0xbfdf2e76dc90786e,3 +np.float64,0x3fece174fbf9c2ea,0x3ff2026a1a889580,3 +np.float64,0xbfdc83c5b8b9078c,0xbfdd8dcf6ee3b7da,3 +np.float64,0x3feaf5448f75ea8a,0x3ff0075b108bcd0d,3 +np.float64,0xbfebf32f7ef7e65f,0xbff0fed42aaa826a,3 +np.float64,0x3fe389e5e8e713cc,0x3fe5047ade055ccb,3 +np.float64,0x3f635cdcc026ba00,0x3f635cddeea082ce,3 +np.float64,0x3fae580f543cb020,0x3fae5c9d5108a796,3 +np.float64,0x3fec9fafce793f60,0x3ff1b77bec654f00,3 +np.float64,0x3fb19d226e233a40,0x3fb1a0b32531f7ee,3 +np.float64,0xbfdf9a71e7bf34e4,0xbfe086cef88626c7,3 +np.float64,0x8010000000000000,0x8010000000000000,3 +np.float64,0xbfef170ba2fe2e17,0xbff54ed4675f5b8a,3 +np.float64,0xbfcc6e2f8f38dc60,0xbfccab65fc34d183,3 +np.float64,0x3fee756c4bfcead8,0x3ff4258782c137e6,3 +np.float64,0xbfd461c218a8c384,0xbfd4be3e391f0ff4,3 +np.float64,0xbfe3b64686e76c8d,0xbfe53caa16d6c90f,3 +np.float64,0xbfc1c65d8d238cbc,0xbfc1d51e58f82403,3 +np.float64,0x3fe6e06c63edc0d8,0x3fe97cb832eeb6a2,3 +np.float64,0xbfc9fc20b933f840,0xbfca2ab004312d85,3 +np.float64,0xbfe29aa6df65354e,0xbfe3da7ecf3ba466,3 +np.float64,0x3fea4df7d1749bf0,0x3feee0d448bd4746,3 +np.float64,0xbfedec6161fbd8c3,0xbff3563e1d943aa2,3 +np.float64,0x3fdb6f0437b6de08,0x3fdc5a1888b1213d,3 +np.float64,0xbfe270cbd3e4e198,0xbfe3a72ac27a0b0c,3 +np.float64,0xbfdfff8068bfff00,0xbfe0c1088e3b8983,3 +np.float64,0xbfd28edbe6a51db8,0xbfd2d416c8ed363e,3 +np.float64,0xbfb4e35f9229c6c0,0xbfb4e9531d2a737f,3 +np.float64,0xbfee6727e97cce50,0xbff40e7717576e46,3 +np.float64,0xbfddb5fbddbb6bf8,0xbfdee5aad78f5361,3 +np.float64,0xbfdf9d3e9dbf3a7e,0xbfe0886b191f2957,3 +np.float64,0x3fa57e77042afce0,0x3fa5801518ea9342,3 +np.float64,0x3f95c4e4882b89c0,0x3f95c55003c8e714,3 +np.float64,0x3fd9b10f61b36220,0x3fda6fe5d635a8aa,3 +np.float64,0xbfe2973411652e68,0xbfe3d641fe9885fd,3 +np.float64,0xbfee87bd5a7d0f7b,0xbff443bea81b3fff,3 +np.float64,0x3f9ea064c83d40c0,0x3f9ea19025085b2f,3 +np.float64,0xbfe4b823dfe97048,0xbfe689623d30dc75,3 +np.float64,0xbfa06a326c20d460,0xbfa06aeacbcd3eb8,3 +np.float64,0x3fe1e5c4c1e3cb8a,0x3fe2fe44b822f20e,3 +np.float64,0x3f99dafaa833b600,0x3f99dbaec10a1a0a,3 +np.float64,0xbfed7cb3877af967,0xbff2bfe9e556aaf9,3 +np.float64,0x3fd604f2e2ac09e4,0x3fd67a89408ce6ba,3 +np.float64,0x3fec57b60f78af6c,0x3ff16881f46d60f7,3 +np.float64,0xbfea2e3a17745c74,0xbfeea95c7190fd42,3 +np.float64,0xbfd60a7c37ac14f8,0xbfd6806ed642de35,3 +np.float64,0xbfe544b9726a8973,0xbfe743ac399d81d7,3 +np.float64,0xbfd13520faa26a42,0xbfd16c02034a8fe0,3 +np.float64,0xbfea9ea59ff53d4b,0xbfef70538ee12e00,3 +np.float64,0x3fd66633f8accc68,0x3fd6e23c13ab0e9e,3 +np.float64,0xbfe4071bd3e80e38,0xbfe5a3c9ba897d81,3 +np.float64,0xbfbe1659fa3c2cb0,0xbfbe2831d4fed196,3 +np.float64,0xbfd3312777a6624e,0xbfd37df09b9baeba,3 +np.float64,0x3fd13997caa27330,0x3fd170a4900c8907,3 +np.float64,0xbfe7cbc235ef9784,0xbfead4c4d6cbf129,3 +np.float64,0xbfe1456571628acb,0xbfe23e4ec768c8e2,3 +np.float64,0xbfedf1a044fbe340,0xbff35da96773e176,3 +np.float64,0x3fce38b1553c7160,0x3fce8270709774f9,3 +np.float64,0xbfecb01761f9602f,0xbff1c9e9d382f1f8,3 +np.float64,0xbfe0a03560e1406b,0xbfe17b8d5a1ca662,3 +np.float64,0x3fe50f37cbea1e70,0x3fe6fc55e1ae7da6,3 +np.float64,0xbfe12d64a0625aca,0xbfe221d3a7834e43,3 +np.float64,0xbf6fb288403f6500,0xbf6fb28d6f389db6,3 +np.float64,0x3fda831765b50630,0x3fdb55eecae58ca9,3 +np.float64,0x3fe1a0fe4c6341fc,0x3fe2ab9564304425,3 +np.float64,0xbfef2678a77e4cf1,0xbff56ff42b2797bb,3 +np.float64,0xbfab269c1c364d40,0xbfab29df1cd48779,3 +np.float64,0x3fe8ec82a271d906,0x3fec92567d7a6675,3 +np.float64,0xbfc235115f246a24,0xbfc244ee567682ea,3 +np.float64,0x3feef5bf8d7deb80,0x3ff50ad4875ee9bd,3 +np.float64,0x3fe768b5486ed16a,0x3fea421356160e65,3 +np.float64,0xbfd4255684a84aae,0xbfd47e8baf7ec7f6,3 +np.float64,0x3fc7f67f2b2fed00,0x3fc81ae83cf92dd5,3 +np.float64,0x3fe9b1b19a736364,0x3fedd4b0e24ee741,3 +np.float64,0x3fb27eb9e624fd70,0x3fb282dacd89ce28,3 +np.float64,0xbfd490b710a9216e,0xbfd4efcdeb213458,3 +np.float64,0xbfd1347b2ca268f6,0xbfd16b55dece2d38,3 +np.float64,0x3fc6a5668d2d4ad0,0x3fc6c41452c0c087,3 +np.float64,0xbfca7b209f34f640,0xbfcaac710486f6bd,3 +np.float64,0x3fc23a1a47247438,0x3fc24a047fd4c27a,3 +np.float64,0x3fdb1413a8b62828,0x3fdbf595e2d994bc,3 +np.float64,0xbfea69b396f4d367,0xbfef11bdd2b0709a,3 +np.float64,0x3fd14c9958a29934,0x3fd1846161b10422,3 +np.float64,0xbfe205f44be40be8,0xbfe325283aa3c6a8,3 +np.float64,0x3fecd03c9ef9a07a,0x3ff1ee85aaf52a01,3 +np.float64,0x3fe34281d7e68504,0x3fe4aab63e6de816,3 +np.float64,0xbfe120e2376241c4,0xbfe213023ab03939,3 +np.float64,0xbfe951edc4f2a3dc,0xbfed3615e38576f8,3 +np.float64,0x3fe5a2286f6b4450,0x3fe7c196e0ec10ed,3 +np.float64,0xbfed7a3e1f7af47c,0xbff2bcc0793555d2,3 +np.float64,0x3fe050274960a04e,0x3fe11e2e256ea5cc,3 +np.float64,0xbfcfa71f653f4e40,0xbfcffc11483d6a06,3 +np.float64,0x3f6ead2e403d5a00,0x3f6ead32f314c052,3 +np.float64,0x3fe3a2a026674540,0x3fe523bfe085f6ec,3 +np.float64,0xbfe294a62e65294c,0xbfe3d31ebd0b4ca2,3 +np.float64,0xbfb4894d06291298,0xbfb48ef4b8e256b8,3 +np.float64,0xbfc0c042c1218084,0xbfc0cc98ac2767c4,3 +np.float64,0xbfc6a32cb52d4658,0xbfc6c1d1597ed06b,3 +np.float64,0xbfd30f7777a61eee,0xbfd35aa39fee34eb,3 +np.float64,0x3fe7fc2c2eeff858,0x3feb1d8a558b5537,3 +np.float64,0x7fefffffffffffff,0x7ff8000000000000,3 +np.float64,0xbfdadf917bb5bf22,0xbfdbbbae9a9f67a0,3 +np.float64,0xbfcf0395e13e072c,0xbfcf5366015f7362,3 +np.float64,0xbfe8644c9170c899,0xbfebbc98e74a227d,3 +np.float64,0x3fc3b2d8e52765b0,0x3fc3c6f7d44cffaa,3 +np.float64,0x3fc57407b92ae810,0x3fc58e12ccdd47a1,3 +np.float64,0x3fd56a560daad4ac,0x3fd5d62b8dfcc058,3 +np.float64,0x3fd595deefab2bbc,0x3fd6046420b2f79b,3 +np.float64,0xbfd5360f50aa6c1e,0xbfd59ebaacd815b8,3 +np.float64,0x3fdfb6aababf6d54,0x3fe0970b8aac9f61,3 +np.float64,0x3ff0000000000000,0x3ff921fb54442d18,3 +np.float64,0xbfeb3a8958f67513,0xbff04872e8278c79,3 +np.float64,0x3f9e1ea6683c3d40,0x3f9e1fc326186705,3 +np.float64,0x3fe6b6d5986d6dac,0x3fe94175bd60b19d,3 +np.float64,0xbfee4d90b77c9b21,0xbff3e60e9134edc2,3 +np.float64,0x3fd806ce0cb00d9c,0x3fd8a14c4855a8f5,3 +np.float64,0x3fd54acc75aa9598,0x3fd5b4b72fcbb5df,3 +np.float64,0xbfe59761f16b2ec4,0xbfe7b2fa5d0244ac,3 +np.float64,0xbfcd4fa3513a9f48,0xbfcd92d0814a5383,3 +np.float64,0xbfdc827523b904ea,0xbfdd8c577b53053c,3 +np.float64,0xbfd4bb7f34a976fe,0xbfd51d00d9a99360,3 +np.float64,0xbfe818bc87f03179,0xbfeb48d1ea0199c5,3 +np.float64,0xbfa8a2e15c3145c0,0xbfa8a5510ba0e45c,3 +np.float64,0xbfb6d15f422da2c0,0xbfb6d922689da015,3 +np.float64,0x3fcd04eaab3a09d8,0x3fcd46131746ef08,3 +np.float64,0x3fcfb5cfbb3f6ba0,0x3fd0059d308237f3,3 +np.float64,0x3fe8dcf609f1b9ec,0x3fec7997973010b6,3 +np.float64,0xbfdf1834d7be306a,0xbfe03c1d4e2b48f0,3 +np.float64,0x3fee82ae50fd055c,0x3ff43b545066fe1a,3 +np.float64,0xbfde039c08bc0738,0xbfdf3d6ed4d2ee5c,3 +np.float64,0x3fec07389bf80e72,0x3ff1137ed0acd161,3 +np.float64,0xbfef44c010fe8980,0xbff5b488ad22a4c5,3 +np.float64,0x3f76e722e02dce00,0x3f76e72ab2759d88,3 +np.float64,0xbfcaa9e6053553cc,0xbfcadc41125fca93,3 +np.float64,0x3fed6088147ac110,0x3ff29c06c4ef35fc,3 +np.float64,0x3fd32bd836a657b0,0x3fd3785fdb75909f,3 +np.float64,0xbfeedbb1d97db764,0xbff4d87f6c82a93c,3 +np.float64,0xbfe40f31d5e81e64,0xbfe5ae292cf258a2,3 +np.float64,0x7ff8000000000000,0x7ff8000000000000,3 +np.float64,0xbfeb2b25bc76564c,0xbff039d81388550c,3 +np.float64,0x3fec5008fa78a012,0x3ff1604195801da3,3 +np.float64,0x3fce2d4f293c5aa0,0x3fce76b99c2db4da,3 +np.float64,0xbfdc435412b886a8,0xbfdd45e7b7813f1e,3 +np.float64,0x3fdf2c9d06be593c,0x3fe047cb03c141b6,3 +np.float64,0x3fddefc61ebbdf8c,0x3fdf26fb8fad9fae,3 +np.float64,0x3fab50218436a040,0x3fab537395eaf3bb,3 +np.float64,0xbfd5b95a8fab72b6,0xbfd62a191a59343a,3 +np.float64,0x3fdbf803b4b7f008,0x3fdcf211578e98c3,3 +np.float64,0xbfec8c255979184b,0xbff1a1bee108ed30,3 +np.float64,0x3fe33cdaffe679b6,0x3fe4a3a318cd994f,3 +np.float64,0x3fd8cf585cb19eb0,0x3fd97a408bf3c38c,3 +np.float64,0x3fe919dde07233bc,0x3fecdb0ea13a2455,3 +np.float64,0xbfd5ba35e4ab746c,0xbfd62b024805542d,3 +np.float64,0x3fd2f933e7a5f268,0x3fd343527565e97c,3 +np.float64,0xbfe5b9f8ddeb73f2,0xbfe7e1f772c3e438,3 +np.float64,0x3fe843cd92f0879c,0x3feb8a92d68eae3e,3 +np.float64,0xbfd096b234a12d64,0xbfd0c7beca2c6605,3 +np.float64,0xbfef3363da7e66c8,0xbff58c98dde6c27c,3 +np.float64,0x3fd51b01ddaa3604,0x3fd582109d89ead1,3 +np.float64,0x3fea0f10ff741e22,0x3fee736c2d2a2067,3 +np.float64,0x3fc276e7b724edd0,0x3fc28774520bc6d4,3 +np.float64,0xbfef9abc9f7f3579,0xbff69d49762b1889,3 +np.float64,0x3fe1539ec0e2a73e,0x3fe24f370b7687d0,3 +np.float64,0x3fad72350c3ae460,0x3fad765e7766682a,3 +np.float64,0x3fa289a47c251340,0x3fa28aae12f41646,3 +np.float64,0xbfe5c488e5eb8912,0xbfe7f05d7e7dcddb,3 +np.float64,0xbfc22ef1d7245de4,0xbfc23ebeb990a1b8,3 +np.float64,0x3fe59a0b80eb3418,0x3fe7b695fdcba1de,3 +np.float64,0xbfe9cad619f395ac,0xbfedff0514d91e2c,3 +np.float64,0x3fc8bc74eb3178e8,0x3fc8e48cb22da666,3 +np.float64,0xbfc5389a3f2a7134,0xbfc551cd6febc544,3 +np.float64,0x3fce82feb33d0600,0x3fceceecce2467ef,3 +np.float64,0x3fda346791b468d0,0x3fdaff95154a4ca6,3 +np.float64,0x3fd04501fea08a04,0x3fd073397b32607e,3 +np.float64,0xbfb6be498a2d7c90,0xbfb6c5f93aeb0e57,3 +np.float64,0x3fe1f030dd63e062,0x3fe30ad8fb97cce0,3 +np.float64,0xbfee3fb36dfc7f67,0xbff3d0a5e380b86f,3 +np.float64,0xbfa876773c30ecf0,0xbfa878d9d3df6a3f,3 +np.float64,0x3fdb58296eb6b054,0x3fdc40ceffb17f82,3 +np.float64,0xbfea16b5d8742d6c,0xbfee809b99fd6adc,3 +np.float64,0xbfdc5062b6b8a0c6,0xbfdd547623275fdb,3 +np.float64,0x3fef6db242fedb64,0x3ff61ab4cdaef467,3 +np.float64,0xbfc9f778f933eef0,0xbfca25eef1088167,3 +np.float64,0xbfd22063eba440c8,0xbfd260c8766c69cf,3 +np.float64,0x3fdd2379f2ba46f4,0x3fde40b025cb1ffa,3 +np.float64,0xbfea967af2f52cf6,0xbfef61a178774636,3 +np.float64,0x3fe4f5b49fe9eb6a,0x3fe6da8311a5520e,3 +np.float64,0x3feccde17b799bc2,0x3ff1ebd0ea228b71,3 +np.float64,0x3fe1bb76506376ec,0x3fe2cb56fca01840,3 +np.float64,0xbfef94e583ff29cb,0xbff68aeab8ba75a2,3 +np.float64,0x3fed024a55fa0494,0x3ff228ea5d456e9d,3 +np.float64,0xbfe877b2a8f0ef65,0xbfebdaa1a4712459,3 +np.float64,0x3fef687a8d7ed0f6,0x3ff60cf5fef8d448,3 +np.float64,0xbfeeb2dc8afd65b9,0xbff48dda6a906cd6,3 +np.float64,0x3fdb2e28aeb65c50,0x3fdc12620655eb7a,3 +np.float64,0x3fedc1863afb830c,0x3ff31ae823315e83,3 +np.float64,0xbfe6b1bb546d6376,0xbfe93a38163e3a59,3 +np.float64,0x3fe479c78468f390,0x3fe637e5c0fc5730,3 +np.float64,0x3fbad1fade35a3f0,0x3fbade9a43ca05cf,3 +np.float64,0xbfe2d1c563e5a38b,0xbfe41e712785900c,3 +np.float64,0xbfc08c33ed211868,0xbfc09817a752d500,3 +np.float64,0xbfecce0935f99c12,0xbff1ebfe84524037,3 +np.float64,0x3fce4ef0e73c9de0,0x3fce995638a3dc48,3 +np.float64,0xbfd2fb2343a5f646,0xbfd345592517ca18,3 +np.float64,0x3fd848f7cdb091f0,0x3fd8e8bee5f7b49a,3 +np.float64,0x3fe532b7d2ea6570,0x3fe72b9ac747926a,3 +np.float64,0x3fd616aadcac2d54,0x3fd68d692c5cad42,3 +np.float64,0x3fd7720eb3aee41c,0x3fd801206a0e1e43,3 +np.float64,0x3fee835a35fd06b4,0x3ff43c7175eb7a54,3 +np.float64,0xbfe2e8f70b65d1ee,0xbfe43b2800a947a7,3 +np.float64,0xbfed38f45d7a71e9,0xbff26acd6bde7174,3 +np.float64,0xbfc0c62661218c4c,0xbfc0d28964d66120,3 +np.float64,0x3fe97940bef2f282,0x3fed76b986a74ee3,3 +np.float64,0x3fc96f7dc532def8,0x3fc99b20044c8fcf,3 +np.float64,0xbfd60201eeac0404,0xbfd677675efaaedc,3 +np.float64,0x3fe63c0867ec7810,0x3fe894f060200140,3 +np.float64,0xbfef6144b37ec289,0xbff5fa589a515ba8,3 +np.float64,0xbfde2da0c8bc5b42,0xbfdf6d0b59e3232a,3 +np.float64,0xbfd7401612ae802c,0xbfd7cb74ddd413b9,3 +np.float64,0x3fe41c012de83802,0x3fe5be9d87da3f82,3 +np.float64,0x3fdf501609bea02c,0x3fe05c1d96a2270b,3 +np.float64,0x3fcf9fa1233f3f40,0x3fcff45598e72f07,3 +np.float64,0x3fd4e3895ea9c714,0x3fd547580d8392a2,3 +np.float64,0x3fe1e8ff5fe3d1fe,0x3fe3022a0b86a2ab,3 +np.float64,0xbfe0aa55956154ab,0xbfe18768823da589,3 +np.float64,0x3fb2a0aa26254150,0x3fb2a4e1faff1c93,3 +np.float64,0x3fd3823417a70468,0x3fd3d2f808dbb167,3 +np.float64,0xbfaed323643da640,0xbfaed7e9bef69811,3 +np.float64,0x3fe661e8c4ecc3d2,0x3fe8c9c535f43c16,3 +np.float64,0xbfa429777c2852f0,0xbfa42acd38ba02a6,3 +np.float64,0x3fb5993ea22b3280,0x3fb59fd353e47397,3 +np.float64,0x3fee62d21efcc5a4,0x3ff40788f9278ade,3 +np.float64,0xbf813fb810227f80,0xbf813fc56d8f3c53,3 +np.float64,0x3fd56205deaac40c,0x3fd5cd59671ef193,3 +np.float64,0x3fd31a4de5a6349c,0x3fd365fe401b66e8,3 +np.float64,0xbfec7cc7a478f98f,0xbff190cf69703ca4,3 +np.float64,0xbf755881a02ab100,0xbf755887f52e7794,3 +np.float64,0x3fdd1c92e6ba3924,0x3fde38efb4e8605c,3 +np.float64,0x3fdf49da80be93b4,0x3fe0588af8dd4a34,3 +np.float64,0x3fe1fcdbf2e3f9b8,0x3fe31a27b9d273f2,3 +np.float64,0x3fe2a0f18be541e4,0x3fe3e23b159ce20f,3 +np.float64,0xbfed0f1561fa1e2b,0xbff23820fc0a54ca,3 +np.float64,0x3fe34a006c669400,0x3fe4b419b9ed2b83,3 +np.float64,0xbfd51be430aa37c8,0xbfd583005a4d62e7,3 +np.float64,0x3fe5ec4e336bd89c,0x3fe826caad6b0f65,3 +np.float64,0xbfdad71b1fb5ae36,0xbfdbb25bef8b53d8,3 +np.float64,0xbfe8eac2d871d586,0xbfec8f8cac7952f9,3 +np.float64,0xbfe1d5aef663ab5e,0xbfe2eae14b7ccdfd,3 +np.float64,0x3fec11d3157823a6,0x3ff11e8279506753,3 +np.float64,0xbfe67ff1166cffe2,0xbfe8f3e61c1dfd32,3 +np.float64,0xbfd101eecda203de,0xbfd136e0e9557022,3 +np.float64,0x3fde6c9e5cbcd93c,0x3fdfb48ee7efe134,3 +np.float64,0x3fec3ede9c787dbe,0x3ff14dead1e5cc1c,3 +np.float64,0x3fe7a022086f4044,0x3fea93ce2980b161,3 +np.float64,0xbfc3b2b1b7276564,0xbfc3c6d02d60bb21,3 +np.float64,0x7ff0000000000000,0x7ff8000000000000,3 +np.float64,0x3fe60b5647ec16ac,0x3fe8517ef0544b40,3 +np.float64,0xbfd20ab654a4156c,0xbfd24a2f1b8e4932,3 +np.float64,0xbfe4aa1e2f69543c,0xbfe677005cbd2646,3 +np.float64,0xbfc831cc0b306398,0xbfc8574910d0b86d,3 +np.float64,0xbfc3143495262868,0xbfc3267961b79198,3 +np.float64,0x3fc14d64c1229ac8,0x3fc15afea90a319d,3 +np.float64,0x3fc0a5a207214b48,0x3fc0b1bd2f15c1b0,3 +np.float64,0xbfc0b8351521706c,0xbfc0c4792672d6db,3 +np.float64,0xbfdc383600b8706c,0xbfdd398429e163bd,3 +np.float64,0x3fd9e17321b3c2e8,0x3fdaa4c4d140a622,3 +np.float64,0xbfd44f079ea89e10,0xbfd4aa7d6deff4ab,3 +np.float64,0xbfc3de52a927bca4,0xbfc3f2f8f65f4c3f,3 +np.float64,0x3fe7779d566eef3a,0x3fea57f8592dbaad,3 +np.float64,0xbfe309039e661207,0xbfe462f47f9a64e5,3 +np.float64,0x3fd8e06d08b1c0dc,0x3fd98cc946e440a6,3 +np.float64,0x3fdde66c9ebbccd8,0x3fdf1c68009a8dc1,3 +np.float64,0x3fd4369c6ba86d38,0x3fd490bf460a69e4,3 +np.float64,0xbfe132252fe2644a,0xbfe22775e109cc2e,3 +np.float64,0x3fee15483c7c2a90,0x3ff39111de89036f,3 +np.float64,0xbfc1d5ee8123abdc,0xbfc1e4d66c6871a5,3 +np.float64,0x3fc851c52b30a388,0x3fc877d93fb4ae1a,3 +np.float64,0x3fdaade707b55bd0,0x3fdb85001661fffe,3 +np.float64,0xbfe79fb7f96f3f70,0xbfea9330ec27ac10,3 +np.float64,0xbfe8b0f725f161ee,0xbfec3411c0e4517a,3 +np.float64,0xbfea79f5f374f3ec,0xbfef2e9dd9270488,3 +np.float64,0x3fe0b5fe5b616bfc,0x3fe19512a36a4534,3 +np.float64,0xbfad7c622c3af8c0,0xbfad808fea96a804,3 +np.float64,0xbfe3e24dbce7c49c,0xbfe574b4c1ea9818,3 +np.float64,0xbfe80b038af01607,0xbfeb33fec279576a,3 +np.float64,0xbfef69e2ea7ed3c6,0xbff610a5593a18bc,3 +np.float64,0x3fdcc0bb39b98178,0x3fddd1f8c9a46430,3 +np.float64,0xbfba39976a347330,0xbfba4563bb5369a4,3 +np.float64,0xbfebf9768ef7f2ed,0xbff10548ab725f74,3 +np.float64,0xbfec21c066f84381,0xbff12f2803ba052f,3 +np.float64,0xbfca216a6b3442d4,0xbfca50c5e1e5748e,3 +np.float64,0x3fd5e40da4abc81c,0x3fd65783f9a22946,3 +np.float64,0x3fc235ca17246b98,0x3fc245a8f453173f,3 +np.float64,0x3fecb5b867796b70,0x3ff1d046a0bfda69,3 +np.float64,0x3fcb457fef368b00,0x3fcb7b6daa8165a7,3 +np.float64,0xbfa5ed6f7c2bdae0,0xbfa5ef27244e2e42,3 +np.float64,0x3fecf618a1f9ec32,0x3ff21a86cc104542,3 +np.float64,0x3fe9d95413f3b2a8,0x3fee178dcafa11fc,3 +np.float64,0xbfe93a5357f274a7,0xbfed0f9a565da84a,3 +np.float64,0xbfeb9e45ff773c8c,0xbff0a93cab8e258d,3 +np.float64,0x3fcbd9d0bd37b3a0,0x3fcc134e87cae241,3 +np.float64,0x3fe55d4db76aba9c,0x3fe764a0e028475a,3 +np.float64,0xbfc8a6fc71314df8,0xbfc8ceaafbfc59a7,3 +np.float64,0x3fe0615fa660c2c0,0x3fe1323611c4cbc2,3 +np.float64,0x3fb965558632cab0,0x3fb9700b84de20ab,3 +np.float64,0x8000000000000000,0x8000000000000000,3 +np.float64,0x3fe76776c6eeceee,0x3fea40403e24a9f1,3 +np.float64,0x3fe3b7f672676fec,0x3fe53ece71a1a1b1,3 +np.float64,0xbfa9b82ba4337050,0xbfa9baf15394ca64,3 +np.float64,0xbfe31faf49663f5e,0xbfe47f31b1ca73dc,3 +np.float64,0xbfcc4c6beb3898d8,0xbfcc88c5f814b2c1,3 +np.float64,0x3fd481530aa902a8,0x3fd4df8df03bc155,3 +np.float64,0x3fd47593b8a8eb28,0x3fd4d327ab78a1a8,3 +np.float64,0x3fd70e6ccbae1cd8,0x3fd7962fe8b63d46,3 +np.float64,0x3fd25191f7a4a324,0x3fd2941623c88e02,3 +np.float64,0x3fd0603ef0a0c07c,0x3fd08f64e97588dc,3 +np.float64,0xbfc653bae52ca774,0xbfc6711e5e0d8ea9,3 +np.float64,0xbfd11db8fea23b72,0xbfd153b63c6e8812,3 +np.float64,0xbfea9bde25f537bc,0xbfef6b52268e139a,3 +np.float64,0x1,0x1,3 +np.float64,0xbfefd3806d7fa701,0xbff776dcef9583ca,3 +np.float64,0xbfe0fb8cfde1f71a,0xbfe1e6e2e774a8f8,3 +np.float64,0x3fea384534f4708a,0x3feebadaa389be0d,3 +np.float64,0x3feff761c97feec4,0x3ff866157b9d072d,3 +np.float64,0x3fe7131ccb6e263a,0x3fe9c58b4389f505,3 +np.float64,0x3fe9084f7872109e,0x3fecbed0355dbc8f,3 +np.float64,0x3f708e89e0211d00,0x3f708e8cd4946b9e,3 +np.float64,0xbfe39185f067230c,0xbfe50e1cd178244d,3 +np.float64,0x3fd67cc1a9acf984,0x3fd6fa514784b48c,3 +np.float64,0xbfecaef005f95de0,0xbff1c89c9c3ef94a,3 +np.float64,0xbfe12eec81e25dd9,0xbfe223a4285bba9a,3 +np.float64,0x3fbe7f9faa3cff40,0x3fbe92363525068d,3 +np.float64,0xbfe1950b2b632a16,0xbfe29d45fc1e4ce9,3 +np.float64,0x3fe45049e6e8a094,0x3fe6020de759e383,3 +np.float64,0x3fe4d10c8969a21a,0x3fe6aa1fe42cbeb9,3 +np.float64,0xbfe9d04658f3a08d,0xbfee08370a0dbf0c,3 +np.float64,0x3fe14fb314e29f66,0x3fe24a8d73663521,3 +np.float64,0xbfef4abfe4fe9580,0xbff5c2c1ff1250ca,3 +np.float64,0xbfe6162b366c2c56,0xbfe86073ac3c6243,3 +np.float64,0x3feffe781e7ffcf0,0x3ff8d2cbedd6a1b5,3 +np.float64,0xbff0000000000000,0xbff921fb54442d18,3 +np.float64,0x3fc1dc45ad23b888,0x3fc1eb3d9bddda58,3 +np.float64,0xbfe793f6fcef27ee,0xbfea81c93d65aa64,3 +np.float64,0x3fdef6d2bbbdeda4,0x3fe029079d42efb5,3 +np.float64,0xbfdf0ac479be1588,0xbfe0346dbc95963f,3 +np.float64,0xbfd33927d7a67250,0xbfd38653f90a5b73,3 +np.float64,0xbfe248b072e49161,0xbfe37631ef6572e1,3 +np.float64,0xbfc8ceb6af319d6c,0xbfc8f7288657f471,3 +np.float64,0x3fdd7277fcbae4f0,0x3fde99886e6766ef,3 +np.float64,0xbfe0d30c6561a619,0xbfe1b72f90bf53d6,3 +np.float64,0xbfcb0fe07d361fc0,0xbfcb448e2eae9542,3 +np.float64,0xbfe351f57fe6a3eb,0xbfe4be13eef250f2,3 +np.float64,0x3fe85ec02cf0bd80,0x3febb407e2e52e4c,3 +np.float64,0x3fc8bc59b53178b0,0x3fc8e470f65800ec,3 +np.float64,0xbfd278d447a4f1a8,0xbfd2bd133c9c0620,3 +np.float64,0x3feda5cfd87b4ba0,0x3ff2f5ab4324f43f,3 +np.float64,0xbfd2b32a36a56654,0xbfd2fa09c36afd34,3 +np.float64,0xbfed4a81cb7a9504,0xbff28077a4f4fff4,3 +np.float64,0x3fdf079bf9be0f38,0x3fe0329f7fb13f54,3 +np.float64,0x3fd14097f6a28130,0x3fd177e9834ec23f,3 +np.float64,0xbfaeab11843d5620,0xbfaeafc5531eb6b5,3 +np.float64,0xbfac3f8c14387f20,0xbfac433893d53360,3 +np.float64,0xbfc139d7ed2273b0,0xbfc14743adbbe660,3 +np.float64,0x3fe78cb02cef1960,0x3fea7707f76edba9,3 +np.float64,0x3fefe16b41ffc2d6,0x3ff7bff36a7aa7b8,3 +np.float64,0x3fec5260d378a4c2,0x3ff162c588b0da38,3 +np.float64,0x3fedb146f17b628e,0x3ff304f90d3a15d1,3 +np.float64,0x3fd1fd45f7a3fa8c,0x3fd23c2dc3929e20,3 +np.float64,0x3fe0898a5ee11314,0x3fe1610c63e726eb,3 +np.float64,0x3fe7719946eee332,0x3fea4f205eecb59f,3 +np.float64,0x3fe955218972aa44,0x3fed3b530c1f7651,3 +np.float64,0x3fe0ccbf4461997e,0x3fe1afc7b4587836,3 +np.float64,0xbfe9204314f24086,0xbfece5605780e346,3 +np.float64,0xbfe552017feaa403,0xbfe755773cbd74d5,3 +np.float64,0x3fd8ce4b32b19c98,0x3fd9791c8dd44eae,3 +np.float64,0x3fef89acd9ff135a,0x3ff668f78adf7ced,3 +np.float64,0x3fc9d713ad33ae28,0x3fca04da6c293bbd,3 +np.float64,0xbfe22d9c4de45b38,0xbfe3553effadcf92,3 +np.float64,0x3fa5cda38c2b9b40,0x3fa5cf53c5787482,3 +np.float64,0x3fa878ebdc30f1e0,0x3fa87b4f2bf1d4c3,3 +np.float64,0x3fe8030353700606,0x3feb27e196928789,3 +np.float64,0x3fb50607222a0c10,0x3fb50c188ce391e6,3 +np.float64,0x3fd9ba4ab4b37494,0x3fda79fa8bd40f45,3 +np.float64,0x3fb564598e2ac8b0,0x3fb56abe42d1ba13,3 +np.float64,0xbfd1177c83a22efa,0xbfd14d3d7ef30cc4,3 +np.float64,0xbfd952cec7b2a59e,0xbfda09215d17c0ac,3 +np.float64,0x3fe1d8066663b00c,0x3fe2edb35770b8dd,3 +np.float64,0xbfc89427a3312850,0xbfc8bb7a7c389497,3 +np.float64,0xbfe86ebfd3f0dd80,0xbfebccc2ba0f506c,3 +np.float64,0x3fc390578b2720b0,0x3fc3a40cb7f5f728,3 +np.float64,0xbfd122f9b8a245f4,0xbfd15929dc57a897,3 +np.float64,0x3f8d0636d03a0c80,0x3f8d06767de576df,3 +np.float64,0xbfe4b55d8b696abb,0xbfe685be537a9637,3 +np.float64,0xbfdfd51cf9bfaa3a,0xbfe0a894fcff0c76,3 +np.float64,0xbfd37c1f52a6f83e,0xbfd3cc9593c37aad,3 +np.float64,0x3fd0e8283ea1d050,0x3fd11c25c800785a,3 +np.float64,0x3fd3160784a62c10,0x3fd36183a6c2880c,3 +np.float64,0x3fd4c66e57a98cdc,0x3fd5288fe3394eff,3 +np.float64,0x3fee2f7e3afc5efc,0x3ff3b8063eb30cdc,3 +np.float64,0xbfe526773a6a4cee,0xbfe71b4364215b18,3 +np.float64,0x3fea01181e740230,0x3fee5b65eccfd130,3 +np.float64,0xbfe51c03f76a3808,0xbfe70d5919d37587,3 +np.float64,0x3fd97e1375b2fc28,0x3fda3845da40b22b,3 +np.float64,0x3fd5c14a14ab8294,0x3fd632890d07ed03,3 +np.float64,0xbfec9b474279368e,0xbff1b28f50584fe3,3 +np.float64,0x3fe0139ca860273a,0x3fe0d7fc377f001c,3 +np.float64,0x3fdb080c9db61018,0x3fdbe85056358fa0,3 +np.float64,0xbfdd72ceb1bae59e,0xbfde99ea171661eb,3 +np.float64,0xbfe64e934fec9d26,0xbfe8aec2ef24be63,3 +np.float64,0x3fd1036a93a206d4,0x3fd1386adabe01bd,3 +np.float64,0x3febc9d4a5f793aa,0x3ff0d4c069f1e67d,3 +np.float64,0xbfe547a16fea8f43,0xbfe747902fe6fb4d,3 +np.float64,0x3fc289b0f9251360,0x3fc29a709de6bdd9,3 +np.float64,0xbfe694494a6d2892,0xbfe9108f3dc133e2,3 +np.float64,0x3fd827dfe4b04fc0,0x3fd8c4fe40532b91,3 +np.float64,0xbfe8b89418f17128,0xbfec400c5a334b2e,3 +np.float64,0x3fed5605147aac0a,0x3ff28ed1f612814a,3 +np.float64,0xbfed36af31fa6d5e,0xbff26804e1f71af0,3 +np.float64,0x3fdbb01c02b76038,0x3fdca2381558bbf0,3 +np.float64,0x3fe2a951666552a2,0x3fe3ec88f780f9e6,3 +np.float64,0x3fe662defbecc5be,0x3fe8cb1dbfca98ab,3 +np.float64,0x3fd098b1b3a13164,0x3fd0c9d064e4eaf2,3 +np.float64,0x3fefa10edeff421e,0x3ff6b1c6187b18a8,3 +np.float64,0xbfec4feb7a789fd7,0xbff16021ef37a219,3 +np.float64,0x3fd8e415bbb1c82c,0x3fd990c1f8b786bd,3 +np.float64,0xbfead5a09275ab41,0xbfefd44fab5b4f6e,3 +np.float64,0xbfe8666c16f0ccd8,0xbfebbfe0c9f2a9ae,3 +np.float64,0x3fdc962132b92c44,0x3fdda2525a6f406c,3 +np.float64,0xbfe2037f03e406fe,0xbfe3222ec2a3449e,3 +np.float64,0xbfec82c27e790585,0xbff197626ea9df1e,3 +np.float64,0x3fd2b4e03ca569c0,0x3fd2fbd3c7fda23e,3 +np.float64,0xbfe9b0dee5f361be,0xbfedd34f6d3dfe8a,3 +np.float64,0x3feef45cd17de8ba,0x3ff508180687b591,3 +np.float64,0x3f82c39bf0258700,0x3f82c3ad24c3b3f1,3 +np.float64,0xbfca848cfd350918,0xbfcab612ce258546,3 +np.float64,0x3fd6442aaaac8854,0x3fd6bdea54016e48,3 +np.float64,0x3fe550799e6aa0f4,0x3fe75369c9ea5b1e,3 +np.float64,0xbfe0e9d5a361d3ac,0xbfe1d20011139d89,3 +np.float64,0x3fbfc9ff1e3f9400,0x3fbfdf0ea6885c80,3 +np.float64,0xbfa187e8b4230fd0,0xbfa188c95072092e,3 +np.float64,0x3fcd28c9533a5190,0x3fcd6ae879c21b47,3 +np.float64,0x3fc6227ec52c4500,0x3fc63f1fbb441d29,3 +np.float64,0x3fe9b7a2ed736f46,0x3feddeab49b2d176,3 +np.float64,0x3fd4aee93da95dd4,0x3fd50fb3b71e0339,3 +np.float64,0xbfe164dacf62c9b6,0xbfe263bb2f7dd5d9,3 +np.float64,0x3fec62e525f8c5ca,0x3ff17496416d9921,3 +np.float64,0x3fdd363ee0ba6c7c,0x3fde55c6a49a5f86,3 +np.float64,0x3fe65cbf75ecb97e,0x3fe8c28d31ff3ebd,3 +np.float64,0xbfe76d27ca6eda50,0xbfea4899e3661425,3 +np.float64,0xbfc305738d260ae8,0xbfc3178dcfc9d30f,3 +np.float64,0xbfd3aa2a54a75454,0xbfd3fcf1e1ce8328,3 +np.float64,0x3fd1609fc9a2c140,0x3fd1992efa539b9f,3 +np.float64,0xbfac1291bc382520,0xbfac162cc7334b4d,3 +np.float64,0xbfedb461ea7b68c4,0xbff309247850455d,3 +np.float64,0xbfe8d2adf8f1a55c,0xbfec6947be90ba92,3 +np.float64,0xbfd7128965ae2512,0xbfd79a9855bcfc5a,3 +np.float64,0x3fe8deb09471bd62,0x3fec7c56b3aee531,3 +np.float64,0xbfe5f4d329ebe9a6,0xbfe8327ea8189af8,3 +np.float64,0xbfd3b46ac9a768d6,0xbfd407b80b12ff17,3 +np.float64,0x3fec899d7cf9133a,0x3ff19ef26baca36f,3 +np.float64,0xbfec192fd5783260,0xbff126306e507fd0,3 +np.float64,0x3fe945bdaef28b7c,0x3fed222f787310bf,3 +np.float64,0xbfeff9635d7ff2c7,0xbff87d6773f318eb,3 +np.float64,0xbfd604b81cac0970,0xbfd67a4aa852559a,3 +np.float64,0x3fcd1cc9d53a3990,0x3fcd5e962e237c24,3 +np.float64,0xbfed77b0fffaef62,0xbff2b97a1c9b6483,3 +np.float64,0xbfc9c69325338d28,0xbfc9f401500402fb,3 +np.float64,0xbfdf97e246bf2fc4,0xbfe0855601ea9db3,3 +np.float64,0x3fc7e6304f2fcc60,0x3fc80a4e718504cd,3 +np.float64,0x3fec3b599e7876b4,0x3ff14a2d1b9c68e6,3 +np.float64,0xbfe98618e1f30c32,0xbfed8bfbb31c394a,3 +np.float64,0xbfe59b3c0feb3678,0xbfe7b832d6df81de,3 +np.float64,0xbfe54ce2fe6a99c6,0xbfe74e9a85be4116,3 +np.float64,0x3fc9db49cb33b690,0x3fca092737ef500a,3 +np.float64,0xbfb4a922ae295248,0xbfb4aee4e39078a9,3 +np.float64,0xbfd0e542e0a1ca86,0xbfd11925208d66af,3 +np.float64,0x3fd70543f2ae0a88,0x3fd78c5e9238a3ee,3 +np.float64,0x3fd67f7a7facfef4,0x3fd6fd3998df8545,3 +np.float64,0xbfe40b643d6816c8,0xbfe5a947e427f298,3 +np.float64,0xbfcd85f69b3b0bec,0xbfcdcaa24b75f1a3,3 +np.float64,0x3fec705fb4f8e0c0,0x3ff1833c82163ee2,3 +np.float64,0x3fb37650ea26eca0,0x3fb37b20c16fb717,3 +np.float64,0x3fe5ebfa55ebd7f4,0x3fe826578d716e70,3 +np.float64,0x3fe991dfe5f323c0,0x3fed9f8a4bf1f588,3 +np.float64,0xbfd658bd0aacb17a,0xbfd6d3dd06e54900,3 +np.float64,0xbfc24860252490c0,0xbfc258701a0b9290,3 +np.float64,0xbfefb8d763ff71af,0xbff705b6ea4a569d,3 +np.float64,0x3fb8fcb4ae31f970,0x3fb906e809e7899f,3 +np.float64,0x3fce6343cb3cc688,0x3fceae41d1629625,3 +np.float64,0xbfd43d5a11a87ab4,0xbfd497da25687e07,3 +np.float64,0xbfe9568851f2ad11,0xbfed3d9e5fe83a76,3 +np.float64,0x3fe1b66153e36cc2,0x3fe2c53c7e016271,3 +np.float64,0x3fef27452bfe4e8a,0x3ff571b3486ed416,3 +np.float64,0x3fca87c0a7350f80,0x3fcab958a7bb82d4,3 +np.float64,0xbfd8776a8fb0eed6,0xbfd91afaf2f50edf,3 +np.float64,0x3fe9522a76f2a454,0x3fed3679264e1525,3 +np.float64,0x3fea14ff2cf429fe,0x3fee7da6431cc316,3 +np.float64,0x3fe970618bf2e0c4,0x3fed68154d54dd97,3 +np.float64,0x3fd3410cfca68218,0x3fd38e9b21792240,3 +np.float64,0xbf6a8070c0350100,0xbf6a8073c7c34517,3 +np.float64,0xbfbe449de23c8938,0xbfbe56c8e5e4d98b,3 +np.float64,0x3fedbc92e27b7926,0x3ff314313216d8e6,3 +np.float64,0xbfe3be4706677c8e,0xbfe546d3ceb85aea,3 +np.float64,0x3fe30cd6d76619ae,0x3fe467b6f2664a8d,3 +np.float64,0x3fd7d69b21afad38,0x3fd86d54284d05ad,3 +np.float64,0xbfe501001fea0200,0xbfe6e978afcff4d9,3 +np.float64,0xbfe44ba3d8e89748,0xbfe5fc0a31cd1e3e,3 +np.float64,0x3fec52f7c078a5f0,0x3ff16367acb209b2,3 +np.float64,0xbfcb19efcb3633e0,0xbfcb4ed9235a7d47,3 +np.float64,0xbfab86796c370cf0,0xbfab89df7bf15710,3 +np.float64,0xbfb962feda32c600,0xbfb96db1e1679c98,3 +np.float64,0x3fe0dd14e861ba2a,0x3fe1c2fc72810567,3 +np.float64,0x3fe41bcc6de83798,0x3fe5be59b7f9003b,3 +np.float64,0x3fc82f4c4f305e98,0x3fc854bd9798939f,3 +np.float64,0xbfcd143a613a2874,0xbfcd55cbd1619d84,3 +np.float64,0xbfd52da61baa5b4c,0xbfd595d0b3543439,3 +np.float64,0xbfb71b4a8e2e3698,0xbfb7235a4ab8432f,3 +np.float64,0xbfec141a19782834,0xbff120e1e39fc856,3 +np.float64,0xbfdba9319db75264,0xbfdc9a8ca2578bb2,3 +np.float64,0xbfbce5d74639cbb0,0xbfbcf5a4878cfa51,3 +np.float64,0x3fde67f7b3bccff0,0x3fdfaf45a9f843ad,3 +np.float64,0xbfe12d87bc625b10,0xbfe221fd4476eb71,3 +np.float64,0x3fe35b8f6be6b71e,0x3fe4ca20f65179e1,3 +np.float64,0xbfdbada1d3b75b44,0xbfdc9f78b19f93d1,3 +np.float64,0xbfc60159c52c02b4,0xbfc61d79b879f598,3 +np.float64,0x3fd6b81c38ad7038,0x3fd739c27bfa16d8,3 +np.float64,0xbfd646a253ac8d44,0xbfd6c08c19612bbb,3 +np.float64,0xbfe6babef0ed757e,0xbfe94703d0bfa311,3 +np.float64,0xbfed5671f1faace4,0xbff28f5a3f3683d0,3 +np.float64,0x3fc01d1e85203a40,0x3fc02817ec0dfd38,3 +np.float64,0xbfe9188a61f23115,0xbfecd8eb5da84223,3 +np.float64,0x3fdca3bab9b94774,0x3fddb1868660c239,3 +np.float64,0xbfa255750c24aaf0,0xbfa25675f7b36343,3 +np.float64,0x3fb3602db626c060,0x3fb364ed2d5b2876,3 +np.float64,0xbfd30a14bda6142a,0xbfd354ff703b8862,3 +np.float64,0xbfe1cfe381639fc7,0xbfe2e3e720b968c8,3 +np.float64,0xbfd2af6a4fa55ed4,0xbfd2f61e190bcd1f,3 +np.float64,0xbfe93c50937278a1,0xbfed12d64bb10d73,3 +np.float64,0x3fddd8bc44bbb178,0x3fdf0ced7f9005cc,3 +np.float64,0x3fdb2bc73cb65790,0x3fdc0fc0e18e425e,3 +np.float64,0xbfd073f6aba0e7ee,0xbfd0a3cb5468a961,3 +np.float64,0x3fed4bad7b7a975a,0x3ff281ebeb75e414,3 +np.float64,0xbfdc75b50bb8eb6a,0xbfdd7e1a7631cb22,3 +np.float64,0x3fd458a90fa8b154,0x3fd4b4a5817248ce,3 +np.float64,0x3feead5db57d5abc,0x3ff484286fab55ff,3 +np.float64,0x3fb3894382271280,0x3fb38e217b4e7905,3 +np.float64,0xffefffffffffffff,0x7ff8000000000000,3 +np.float64,0xbfe428212ae85042,0xbfe5ce36f226bea8,3 +np.float64,0xbfc08b39f7211674,0xbfc0971b93ebc7ad,3 +np.float64,0xbfc2e7cf5525cfa0,0xbfc2f994eb72b623,3 +np.float64,0xbfdb0d85afb61b0c,0xbfdbee5a2de3c5db,3 +np.float64,0xfff0000000000000,0x7ff8000000000000,3 +np.float64,0xbfd0d36af7a1a6d6,0xbfd106a5f05ef6ff,3 +np.float64,0xbfc333d0912667a0,0xbfc3467162b7289a,3 +np.float64,0x3fcdababc53b5758,0x3fcdf16458c20fa8,3 +np.float64,0x3fd0821b38a10438,0x3fd0b26e3e0b9185,3 +np.float64,0x0,0x0,3 +np.float64,0x3feb7f70edf6fee2,0x3ff08ae81854bf20,3 +np.float64,0x3fe6e075716dc0ea,0x3fe97cc5254be6ff,3 +np.float64,0x3fea13b682f4276e,0x3fee7b6f18073b5b,3 diff --git a/numpy/core/tests/data/umath-validation-set-arcsinh.csv b/numpy/core/tests/data/umath-validation-set-arcsinh.csv new file mode 100644 index 000000000000..1da29c822bca --- /dev/null +++ b/numpy/core/tests/data/umath-validation-set-arcsinh.csv @@ -0,0 +1,1429 @@ +dtype,input,output,ulperrortol +np.float32,0xbf24142a,0xbf1a85ef,2 +np.float32,0x3e71cf91,0x3e6f9e37,2 +np.float32,0xe52a7,0xe52a7,2 +np.float32,0x3ef1e074,0x3ee9add9,2 +np.float32,0x806160ac,0x806160ac,2 +np.float32,0x7e2d59a2,0x42af4798,2 +np.float32,0xbf32cac9,0xbf26bf96,2 +np.float32,0x3f081701,0x3f026142,2 +np.float32,0x3f23cc88,0x3f1a499c,2 +np.float32,0xbf090d94,0xbf033ad0,2 +np.float32,0x803af2fc,0x803af2fc,2 +np.float32,0x807eb17e,0x807eb17e,2 +np.float32,0x5c0d8e,0x5c0d8e,2 +np.float32,0x3f7b79d2,0x3f5e6b1d,2 +np.float32,0x806feeae,0x806feeae,2 +np.float32,0x3e4b423a,0x3e49f274,2 +np.float32,0x3f49e5ac,0x3f394a41,2 +np.float32,0x3f18cd4e,0x3f10ef35,2 +np.float32,0xbed75734,0xbed17322,2 +np.float32,0x7f591151,0x42b28085,2 +np.float32,0xfefe9da6,0xc2b16f51,2 +np.float32,0xfeac90fc,0xc2b0a82a,2 +np.float32,0x805c198e,0x805c198e,2 +np.float32,0x7f66d6df,0x42b2a004,2 +np.float32,0x505438,0x505438,2 +np.float32,0xbf39a209,0xbf2c5255,2 +np.float32,0x7fa00000,0x7fe00000,2 +np.float32,0xc84cb,0xc84cb,2 +np.float32,0x7f07d6f5,0x42b19088,2 +np.float32,0x79d7e4,0x79d7e4,2 +np.float32,0xff32f6a0,0xc2b21db1,2 +np.float32,0x7c005c05,0x42a9222e,2 +np.float32,0x3ec449aa,0x3ebfc5ae,2 +np.float32,0x800ec323,0x800ec323,2 +np.float32,0xff1c904c,0xc2b1d93a,2 +np.float32,0x7f4eca52,0x42b267b0,2 +np.float32,0x3ee06540,0x3ed9c514,2 +np.float32,0x6aab4,0x6aab4,2 +np.float32,0x3e298d8c,0x3e28c99e,2 +np.float32,0xbf38d162,0xbf2ba94a,2 +np.float32,0x2d9083,0x2d9083,2 +np.float32,0x7eae5032,0x42b0ad52,2 +np.float32,0x3ead5b3c,0x3eaa3443,2 +np.float32,0x806fef66,0x806fef66,2 +np.float32,0x3f5b614e,0x3f46ca71,2 +np.float32,0xbf4c906a,0xbf3b60fc,2 +np.float32,0x8049453e,0x8049453e,2 +np.float32,0x3d305220,0x3d304432,2 +np.float32,0x2e1a89,0x2e1a89,2 +np.float32,0xbf4e74ec,0xbf3cdacf,2 +np.float32,0x807a827a,0x807a827a,2 +np.float32,0x80070745,0x80070745,2 +np.float32,0xbe1ba2fc,0xbe1b0b28,2 +np.float32,0xbe5131d0,0xbe4fc421,2 +np.float32,0x5bfd98,0x5bfd98,2 +np.float32,0xbd8e1a48,0xbd8dfd27,2 +np.float32,0x8006c160,0x8006c160,2 +np.float32,0x346490,0x346490,2 +np.float32,0xbdbdf060,0xbdbdaaf0,2 +np.float32,0x3ea9d0c4,0x3ea6d8c7,2 +np.float32,0xbf2aaa28,0xbf200916,2 +np.float32,0xbf160c26,0xbf0e9047,2 +np.float32,0x80081fd4,0x80081fd4,2 +np.float32,0x7db44283,0x42adf8b6,2 +np.float32,0xbf1983f8,0xbf118bf5,2 +np.float32,0x2c4a35,0x2c4a35,2 +np.float32,0x6165a7,0x6165a7,2 +np.float32,0xbe776b44,0xbe75129f,2 +np.float32,0xfe81841a,0xc2b0153b,2 +np.float32,0xbf7d1b2f,0xbf5f9461,2 +np.float32,0x80602d36,0x80602d36,2 +np.float32,0xfe8d5046,0xc2b041dd,2 +np.float32,0xfe5037bc,0xc2afa56d,2 +np.float32,0x4bbea6,0x4bbea6,2 +np.float32,0xfea039de,0xc2b0822d,2 +np.float32,0x7ea627a4,0x42b094c7,2 +np.float32,0x3f556198,0x3f423591,2 +np.float32,0xfedbae04,0xc2b123c1,2 +np.float32,0xbe30432c,0xbe2f6744,2 +np.float32,0x80202c77,0x80202c77,2 +np.float32,0xff335cc1,0xc2b21ed5,2 +np.float32,0x3e1e1ebe,0x3e1d7f95,2 +np.float32,0x8021c9c0,0x8021c9c0,2 +np.float32,0x7dc978,0x7dc978,2 +np.float32,0xff6cfabc,0xc2b2ad75,2 +np.float32,0x7f2bd542,0x42b208e0,2 +np.float32,0x53bf33,0x53bf33,2 +np.float32,0x804e04bb,0x804e04bb,2 +np.float32,0x3f30d2f9,0x3f2521ca,2 +np.float32,0x3dfde876,0x3dfd4316,2 +np.float32,0x46f8b1,0x46f8b1,2 +np.float32,0xbd5f9e20,0xbd5f81ba,2 +np.float32,0x807d6a22,0x807d6a22,2 +np.float32,0xff3881da,0xc2b22d50,2 +np.float32,0x1b1cb5,0x1b1cb5,2 +np.float32,0x3f75f2d0,0x3f5a7435,2 +np.float32,0xfee39c1a,0xc2b135e9,2 +np.float32,0x7f79f14a,0x42b2c8b9,2 +np.float32,0x8000e2d1,0x8000e2d1,2 +np.float32,0xab779,0xab779,2 +np.float32,0xbede6690,0xbed7f102,2 +np.float32,0x76e20d,0x76e20d,2 +np.float32,0x3ed714cb,0x3ed135e9,2 +np.float32,0xbeaa6f44,0xbea76f31,2 +np.float32,0x7f7dc8b1,0x42b2d089,2 +np.float32,0x108cb2,0x108cb2,2 +np.float32,0x7d37ba82,0x42ac9f94,2 +np.float32,0x3f31d068,0x3f25f221,2 +np.float32,0x8010a331,0x8010a331,2 +np.float32,0x3f2fdc7c,0x3f2456cd,2 +np.float32,0x7f7a9a67,0x42b2ca13,2 +np.float32,0x3f2acb31,0x3f202492,2 +np.float32,0x7f54fa94,0x42b276c9,2 +np.float32,0x3ebf8a70,0x3ebb553c,2 +np.float32,0x7f75b1a7,0x42b2bff2,2 +np.float32,0x7daebe07,0x42ade8cc,2 +np.float32,0xbd3a3ef0,0xbd3a2e86,2 +np.float32,0x8078ec9e,0x8078ec9e,2 +np.float32,0x3eda206a,0x3ed403ec,2 +np.float32,0x3f7248f2,0x3f57cd77,2 +np.float32,0x805d55ba,0x805d55ba,2 +np.float32,0xff30dc3e,0xc2b217a3,2 +np.float32,0xbe12b27c,0xbe123333,2 +np.float32,0xbf6ed9cf,0xbf554cd0,2 +np.float32,0xbed9eb5c,0xbed3d31c,2 +np.float32,0xbf1c9aea,0xbf14307b,2 +np.float32,0x3f540ac4,0x3f412de2,2 +np.float32,0x800333ac,0x800333ac,2 +np.float32,0x3f74cdb4,0x3f59a09a,2 +np.float32,0xbf41dc41,0xbf32ee6f,2 +np.float32,0xff2c7804,0xc2b20ac4,2 +np.float32,0x514493,0x514493,2 +np.float32,0xbddf1220,0xbddea1cf,2 +np.float32,0xfeaf74de,0xc2b0b0ab,2 +np.float32,0xfe5dfb30,0xc2afc633,2 +np.float32,0xbf4785c4,0xbf376bdb,2 +np.float32,0x80191cd3,0x80191cd3,2 +np.float32,0xfe44f708,0xc2af88fb,2 +np.float32,0x3d4cd8a0,0x3d4cc2ca,2 +np.float32,0x7f572eff,0x42b27c0f,2 +np.float32,0x8031bacb,0x8031bacb,2 +np.float32,0x7f2ea684,0x42b21133,2 +np.float32,0xbea1976a,0xbe9f05bb,2 +np.float32,0x3d677b41,0x3d675bc1,2 +np.float32,0x3f61bf24,0x3f4b9870,2 +np.float32,0x7ef55ddf,0x42b15c5f,2 +np.float32,0x3eabcb20,0x3ea8b91c,2 +np.float32,0xff73d9ec,0xc2b2bc18,2 +np.float32,0x77b9f5,0x77b9f5,2 +np.float32,0x4c6c6c,0x4c6c6c,2 +np.float32,0x7ed09c94,0x42b10949,2 +np.float32,0xdeeec,0xdeeec,2 +np.float32,0x7eac5858,0x42b0a782,2 +np.float32,0x7e190658,0x42af07bd,2 +np.float32,0xbe3c8980,0xbe3b7ce2,2 +np.float32,0x8059e86e,0x8059e86e,2 +np.float32,0xff201836,0xc2b1e4a5,2 +np.float32,0xbeac109c,0xbea8fafb,2 +np.float32,0x7edd1e2b,0x42b12718,2 +np.float32,0x639cd8,0x639cd8,2 +np.float32,0x3f5e4cae,0x3f490059,2 +np.float32,0x3d84c185,0x3d84a9c4,2 +np.float32,0xbe8c1130,0xbe8a605b,2 +np.float32,0x80000000,0x80000000,2 +np.float32,0x3f1da5e4,0x3f151404,2 +np.float32,0x7f75a873,0x42b2bfdf,2 +np.float32,0xbd873540,0xbd871c28,2 +np.float32,0xbe8e5e10,0xbe8c9808,2 +np.float32,0x7f004bf2,0x42b17347,2 +np.float32,0x800000,0x800000,2 +np.float32,0xbf6d6b79,0xbf544095,2 +np.float32,0x7ed7b563,0x42b11a6a,2 +np.float32,0x80693745,0x80693745,2 +np.float32,0x3ee0f608,0x3eda49a8,2 +np.float32,0xfe1285a4,0xc2aef181,2 +np.float32,0x72d946,0x72d946,2 +np.float32,0x6a0dca,0x6a0dca,2 +np.float32,0x3f5c9df6,0x3f47ba99,2 +np.float32,0xff002af6,0xc2b172c4,2 +np.float32,0x3f4ac98f,0x3f39fd0a,2 +np.float32,0x8066acf7,0x8066acf7,2 +np.float32,0xbcaa4e60,0xbcaa4b3c,2 +np.float32,0x80162813,0x80162813,2 +np.float32,0xff34b318,0xc2b222a2,2 +np.float32,0x7f1ce33c,0x42b1da49,2 +np.float32,0x3f0e55ab,0x3f07ddb0,2 +np.float32,0x7c75d996,0x42aa6eec,2 +np.float32,0xbf221bc6,0xbf18dc89,2 +np.float32,0x3f5a1a4c,0x3f45d1d4,2 +np.float32,0x7f2451b8,0x42b1f1fb,2 +np.float32,0x3ec55ca0,0x3ec0c655,2 +np.float32,0x3f752dc2,0x3f59e600,2 +np.float32,0xbe33f638,0xbe330c4d,2 +np.float32,0x3e2a9148,0x3e29c9d8,2 +np.float32,0x3f3362a1,0x3f273c01,2 +np.float32,0x5f83b3,0x5f83b3,2 +np.float32,0x3e362488,0x3e353216,2 +np.float32,0x140bcf,0x140bcf,2 +np.float32,0x7e3e96df,0x42af7822,2 +np.float32,0xbebc7082,0xbeb86ce6,2 +np.float32,0xbe92a92e,0xbe90b9d2,2 +np.float32,0xff3d8afc,0xc2b23b19,2 +np.float32,0x804125e3,0x804125e3,2 +np.float32,0x3f3675d1,0x3f29bedb,2 +np.float32,0xff70bb09,0xc2b2b57f,2 +np.float32,0x3f29681c,0x3f1efcd2,2 +np.float32,0xbdc70380,0xbdc6b3a8,2 +np.float32,0x54e0dd,0x54e0dd,2 +np.float32,0x3d545de0,0x3d54458c,2 +np.float32,0x7f800000,0x7f800000,2 +np.float32,0x8014a4c2,0x8014a4c2,2 +np.float32,0xbe93f58a,0xbe91f938,2 +np.float32,0x17de33,0x17de33,2 +np.float32,0xfefb679a,0xc2b168d2,2 +np.float32,0xbf23423e,0xbf19d511,2 +np.float32,0x7e893fa1,0x42b032ec,2 +np.float32,0x3f44fe2d,0x3f356bda,2 +np.float32,0xbebb2e78,0xbeb73e8f,2 +np.float32,0x3f5632e0,0x3f42d633,2 +np.float32,0x3ddd8698,0x3ddd1896,2 +np.float32,0x80164ea7,0x80164ea7,2 +np.float32,0x80087b37,0x80087b37,2 +np.float32,0xbf06ab1e,0xbf011f95,2 +np.float32,0x3db95524,0x3db9149f,2 +np.float32,0x7aa1fbb3,0x42a570a1,2 +np.float32,0xbd84fc48,0xbd84e467,2 +np.float32,0x3d65c6f5,0x3d65a826,2 +np.float32,0xfe987800,0xc2b068c4,2 +np.float32,0x7ec59532,0x42b0ed7a,2 +np.float32,0x3ea0232c,0x3e9da29a,2 +np.float32,0x80292a08,0x80292a08,2 +np.float32,0x734cfe,0x734cfe,2 +np.float32,0x3f3b6d63,0x3f2dc596,2 +np.float32,0x3f27bcc1,0x3f1d97e6,2 +np.float32,0xfe1da554,0xc2af16f9,2 +np.float32,0x7c91f5,0x7c91f5,2 +np.float32,0xfe4e78cc,0xc2afa11e,2 +np.float32,0x7e4b4e08,0x42af9933,2 +np.float32,0xfe0949ec,0xc2aed02e,2 +np.float32,0x7e2f057f,0x42af4c81,2 +np.float32,0xbf200ae0,0xbf171ce1,2 +np.float32,0x3ebcc244,0x3eb8b99e,2 +np.float32,0xbf68f58d,0xbf50f7aa,2 +np.float32,0x4420b1,0x4420b1,2 +np.float32,0x3f5b61bf,0x3f46cac7,2 +np.float32,0x3fec78,0x3fec78,2 +np.float32,0x7f4183c8,0x42b245b7,2 +np.float32,0xbf10587c,0xbf099ee2,2 +np.float32,0x0,0x0,2 +np.float32,0x7ec84dc3,0x42b0f47a,2 +np.float32,0x3f5fbd7b,0x3f4a166d,2 +np.float32,0xbd884eb8,0xbd883502,2 +np.float32,0xfe3f10a4,0xc2af7969,2 +np.float32,0xff3f4920,0xc2b23fc9,2 +np.float32,0x8013900f,0x8013900f,2 +np.float32,0x8003529d,0x8003529d,2 +np.float32,0xbf032384,0xbefbfb3c,2 +np.float32,0xff418c7c,0xc2b245ce,2 +np.float32,0xbec0aad0,0xbebc633b,2 +np.float32,0xfdbff178,0xc2ae18de,2 +np.float32,0x68ab15,0x68ab15,2 +np.float32,0xbdfc4a88,0xbdfba848,2 +np.float32,0xbf5adec6,0xbf466747,2 +np.float32,0x807d5dcc,0x807d5dcc,2 +np.float32,0x61d144,0x61d144,2 +np.float32,0x807e3a03,0x807e3a03,2 +np.float32,0x1872f2,0x1872f2,2 +np.float32,0x7f2a272c,0x42b203d8,2 +np.float32,0xfe7f8314,0xc2b00e3a,2 +np.float32,0xbe42aeac,0xbe418737,2 +np.float32,0x8024b614,0x8024b614,2 +np.float32,0xbe41b6b8,0xbe40939a,2 +np.float32,0xa765c,0xa765c,2 +np.float32,0x7ea74f4b,0x42b09853,2 +np.float32,0x7f7ef631,0x42b2d2e7,2 +np.float32,0x7eaef5e6,0x42b0af38,2 +np.float32,0xff733d85,0xc2b2bacf,2 +np.float32,0x537ac0,0x537ac0,2 +np.float32,0xbeca4790,0xbec55b1d,2 +np.float32,0x80117314,0x80117314,2 +np.float32,0xfe958536,0xc2b05ec5,2 +np.float32,0x8066ecc2,0x8066ecc2,2 +np.float32,0xbf56baf3,0xbf433e82,2 +np.float32,0x1f7fd7,0x1f7fd7,2 +np.float32,0x3e942104,0x3e9222fc,2 +np.float32,0xfeaffe82,0xc2b0b23c,2 +np.float32,0xfe0e02b0,0xc2aee17e,2 +np.float32,0xbf800000,0xbf61a1b3,2 +np.float32,0x800b7e49,0x800b7e49,2 +np.float32,0x6c514f,0x6c514f,2 +np.float32,0xff800000,0xff800000,2 +np.float32,0x7f7d9a45,0x42b2d02b,2 +np.float32,0x800c9c69,0x800c9c69,2 +np.float32,0x274b14,0x274b14,2 +np.float32,0xbf4b22b0,0xbf3a42e2,2 +np.float32,0x63e5ae,0x63e5ae,2 +np.float32,0xbe18facc,0xbe186a90,2 +np.float32,0x7e137351,0x42aef4bd,2 +np.float32,0x80518ffd,0x80518ffd,2 +np.float32,0xbf0a8ffc,0xbf048f0d,2 +np.float32,0x841d,0x841d,2 +np.float32,0x7edfdc9e,0x42b12d69,2 +np.float32,0xfd1092b0,0xc2ac24de,2 +np.float32,0x7e2c9bdf,0x42af4566,2 +np.float32,0x7f7fffff,0x42b2d4fc,2 +np.float32,0x3f4954a6,0x3f38d853,2 +np.float32,0xbe83efd2,0xbe8284c3,2 +np.float32,0x800e8e02,0x800e8e02,2 +np.float32,0x78ad39,0x78ad39,2 +np.float32,0x7eb0f967,0x42b0b514,2 +np.float32,0xbe39aa94,0xbe38a9ee,2 +np.float32,0x80194e7b,0x80194e7b,2 +np.float32,0x3cf3a340,0x3cf39a0f,2 +np.float32,0x3ed3117a,0x3ecd8173,2 +np.float32,0x7f530b11,0x42b2721c,2 +np.float32,0xff756ba2,0xc2b2bf60,2 +np.float32,0x15ea25,0x15ea25,2 +np.float32,0x803cbb64,0x803cbb64,2 +np.float32,0x3f34722d,0x3f281a2c,2 +np.float32,0x3ddd88e0,0x3ddd1adb,2 +np.float32,0x3f54244c,0x3f41418b,2 +np.float32,0x3e0adb98,0x3e0a6f8b,2 +np.float32,0x80800000,0x80800000,2 +np.float32,0x58902b,0x58902b,2 +np.float32,0xfe3b50b8,0xc2af6f43,2 +np.float32,0xfe0846d0,0xc2aecc64,2 +np.float32,0xbe0299d0,0xbe023fd4,2 +np.float32,0x18dde6,0x18dde6,2 +np.float32,0x8039fe8b,0x8039fe8b,2 +np.float32,0x8015d179,0x8015d179,2 +np.float32,0x3f551322,0x3f41f947,2 +np.float32,0x2ab387,0x2ab387,2 +np.float32,0xbf7e311e,0xbf6059d0,2 +np.float32,0xbdba58a8,0xbdba1713,2 +np.float32,0xbf1d008a,0xbf148724,2 +np.float32,0xbf6b9c97,0xbf52ec98,2 +np.float32,0x802acf04,0x802acf04,2 +np.float32,0x1,0x1,2 +np.float32,0xbe9e16d6,0xbe9bade3,2 +np.float32,0xbf048a14,0xbefe78c7,2 +np.float32,0x7e432ad3,0x42af8449,2 +np.float32,0xbdcc7fe0,0xbdcc2944,2 +np.float32,0x6dfc27,0x6dfc27,2 +np.float32,0xfef6eed8,0xc2b15fa1,2 +np.float32,0xbeeff6e8,0xbee7f2e4,2 +np.float32,0x7e3a6ca8,0x42af6cd2,2 +np.float32,0xff2c82e8,0xc2b20ae4,2 +np.float32,0x3e9f8d74,0x3e9d13b0,2 +np.float32,0x7ea36191,0x42b08c29,2 +np.float32,0x7f734bed,0x42b2baed,2 +np.float32,0x7f2df96d,0x42b20f37,2 +np.float32,0x5036fd,0x5036fd,2 +np.float32,0x806eab38,0x806eab38,2 +np.float32,0xbe9db90e,0xbe9b5446,2 +np.float32,0xfeef6fac,0xc2b14fd9,2 +np.float32,0xc2bf7,0xc2bf7,2 +np.float32,0xff53ec3d,0xc2b2743d,2 +np.float32,0x7e837637,0x42b01cde,2 +np.float32,0xbefb5934,0xbef23662,2 +np.float32,0x3f6cec80,0x3f53e371,2 +np.float32,0x3e86e7de,0x3e85643f,2 +np.float32,0x3f09cb42,0x3f03e1ef,2 +np.float32,0xbec3d236,0xbebf5620,2 +np.float32,0xfedef246,0xc2b12b50,2 +np.float32,0xbf08d6a8,0xbf030a62,2 +np.float32,0x8036cbf9,0x8036cbf9,2 +np.float32,0x3f74d3e3,0x3f59a512,2 +np.float32,0x6a600c,0x6a600c,2 +np.float32,0xfd1295b0,0xc2ac2bf1,2 +np.float32,0xbeb61142,0xbeb26efa,2 +np.float32,0x80216556,0x80216556,2 +np.float32,0xbf1fa0f6,0xbf16c30a,2 +np.float32,0x3e0af8e1,0x3e0a8c90,2 +np.float32,0x80434709,0x80434709,2 +np.float32,0x49efd9,0x49efd9,2 +np.float32,0x7f7cce6c,0x42b2ce8f,2 +np.float32,0x6e5450,0x6e5450,2 +np.float32,0x7f0fc115,0x42b1ad86,2 +np.float32,0x632db0,0x632db0,2 +np.float32,0x3f6f4c2a,0x3f55a064,2 +np.float32,0x7ec4f273,0x42b0ebd3,2 +np.float32,0x61ae1e,0x61ae1e,2 +np.float32,0x5f47c4,0x5f47c4,2 +np.float32,0xbf3c8f62,0xbf2eaf54,2 +np.float32,0xfca38900,0xc2ab0113,2 +np.float32,0x3ec89d52,0x3ec3ce78,2 +np.float32,0xbe0e3f70,0xbe0dcb53,2 +np.float32,0x805d3156,0x805d3156,2 +np.float32,0x3eee33f8,0x3ee65a4e,2 +np.float32,0xbeda7e9a,0xbed45a90,2 +np.float32,0x7e2fac7b,0x42af4e69,2 +np.float32,0x7efd0e28,0x42b16c2c,2 +np.float32,0x3f0c7b17,0x3f063e46,2 +np.float32,0xbf395bec,0xbf2c198f,2 +np.float32,0xfdf1c3f8,0xc2ae8f05,2 +np.float32,0xbe11f4e4,0xbe117783,2 +np.float32,0x7eddc901,0x42b128a3,2 +np.float32,0x3f4bad09,0x3f3aaf33,2 +np.float32,0xfefb5d76,0xc2b168bd,2 +np.float32,0x3ed3a4cf,0x3ece09a3,2 +np.float32,0x7ec582e4,0x42b0ed4a,2 +np.float32,0x3dc2268a,0x3dc1dc64,2 +np.float32,0x3ef9b17c,0x3ef0b9c9,2 +np.float32,0x2748ac,0x2748ac,2 +np.float32,0xfed6a602,0xc2b117e4,2 +np.float32,0xbefc9c36,0xbef35832,2 +np.float32,0x7e0476,0x7e0476,2 +np.float32,0x804be1a0,0x804be1a0,2 +np.float32,0xbefbc1c2,0xbef2943a,2 +np.float32,0xbd4698f0,0xbd46850a,2 +np.float32,0x688627,0x688627,2 +np.float32,0x3f7f7685,0x3f61406f,2 +np.float32,0x827fb,0x827fb,2 +np.float32,0x3f503264,0x3f3e34fd,2 +np.float32,0x7f5458d1,0x42b27543,2 +np.float32,0x800ac01f,0x800ac01f,2 +np.float32,0x6188dd,0x6188dd,2 +np.float32,0x806ac0ba,0x806ac0ba,2 +np.float32,0xbe14493c,0xbe13c5cc,2 +np.float32,0x3f77542c,0x3f5b72ae,2 +np.float32,0xfeaacab6,0xc2b0a2df,2 +np.float32,0x7f2893d5,0x42b1ff15,2 +np.float32,0x66b528,0x66b528,2 +np.float32,0xbf653e24,0xbf4e3573,2 +np.float32,0x801a2853,0x801a2853,2 +np.float32,0x3f3d8c98,0x3f2f7b04,2 +np.float32,0xfdffbad8,0xc2aeabc5,2 +np.float32,0x3dd50f,0x3dd50f,2 +np.float32,0x3f325a4c,0x3f266353,2 +np.float32,0xfcc48ec0,0xc2ab5f3f,2 +np.float32,0x3e6f5b9a,0x3e6d3ae5,2 +np.float32,0x3dbcd62b,0x3dbc91ee,2 +np.float32,0xbf7458d9,0xbf594c1c,2 +np.float32,0xff5adb24,0xc2b284b9,2 +np.float32,0x807b246d,0x807b246d,2 +np.float32,0x3f800000,0x3f61a1b3,2 +np.float32,0x231a28,0x231a28,2 +np.float32,0xbdc66258,0xbdc61341,2 +np.float32,0x3c84b4b4,0x3c84b338,2 +np.float32,0xbf215894,0xbf183783,2 +np.float32,0xff4ee298,0xc2b267ec,2 +np.float32,0x801ef52e,0x801ef52e,2 +np.float32,0x1040b0,0x1040b0,2 +np.float32,0xff545582,0xc2b2753b,2 +np.float32,0x3f3b9dda,0x3f2decaf,2 +np.float32,0x730f99,0x730f99,2 +np.float32,0xff7fffff,0xc2b2d4fc,2 +np.float32,0xff24cc5e,0xc2b1f379,2 +np.float32,0xbe9b456a,0xbe98fc0b,2 +np.float32,0x188fb,0x188fb,2 +np.float32,0x3f5c7ce2,0x3f47a18a,2 +np.float32,0x7fc00000,0x7fc00000,2 +np.float32,0x806ea4da,0x806ea4da,2 +np.float32,0xfe810570,0xc2b01345,2 +np.float32,0x8036af89,0x8036af89,2 +np.float32,0x8043cec6,0x8043cec6,2 +np.float32,0x80342bb3,0x80342bb3,2 +np.float32,0x1a2bd4,0x1a2bd4,2 +np.float32,0x3f6248c2,0x3f4bff9a,2 +np.float32,0x8024eb35,0x8024eb35,2 +np.float32,0x7ea55872,0x42b09247,2 +np.float32,0x806d6e56,0x806d6e56,2 +np.float32,0x25c21a,0x25c21a,2 +np.float32,0x3f4e95f3,0x3f3cf483,2 +np.float32,0x15ca38,0x15ca38,2 +np.float32,0x803f01b2,0x803f01b2,2 +np.float32,0xbe731634,0xbe70dc10,2 +np.float32,0x3e80cee4,0x3e7ef933,2 +np.float32,0x3ef6dda5,0x3eee2e7b,2 +np.float32,0x3f3dfdc2,0x3f2fd5ed,2 +np.float32,0xff0492a7,0xc2b18411,2 +np.float32,0xbf1d0adf,0xbf148ff3,2 +np.float32,0xfcf75460,0xc2abd4e3,2 +np.float32,0x3f46fca6,0x3f36ffa6,2 +np.float32,0xbe63b5c0,0xbe61dfb3,2 +np.float32,0xff019bec,0xc2b1787d,2 +np.float32,0x801f14a9,0x801f14a9,2 +np.float32,0x3f176cfa,0x3f0fc051,2 +np.float32,0x3f69d976,0x3f51a015,2 +np.float32,0x3f4917cb,0x3f38a87a,2 +np.float32,0x3b2a0bea,0x3b2a0bdd,2 +np.float32,0xbf41d857,0xbf32eb50,2 +np.float32,0xbf08841a,0xbf02c18f,2 +np.float32,0x7ec86f14,0x42b0f4d0,2 +np.float32,0xbf7d15d1,0xbf5f9090,2 +np.float32,0xbd080550,0xbd07feea,2 +np.float32,0xbf6f1bef,0xbf557d26,2 +np.float32,0xfebc282c,0xc2b0d473,2 +np.float32,0x3e68d2f5,0x3e66dd03,2 +np.float32,0x3f3ed8fe,0x3f3085d5,2 +np.float32,0xff2f78ae,0xc2b2139a,2 +np.float32,0xff647a70,0xc2b29ac1,2 +np.float32,0xfd0859a0,0xc2ac06e2,2 +np.float32,0x3ea578a8,0x3ea2b7e1,2 +np.float32,0x6c58c6,0x6c58c6,2 +np.float32,0xff23f26a,0xc2b1f0d2,2 +np.float32,0x800902a4,0x800902a4,2 +np.float32,0xfe8ba64e,0xc2b03bcd,2 +np.float32,0x3f091143,0x3f033e0f,2 +np.float32,0x8017c4bd,0x8017c4bd,2 +np.float32,0xbf708fd4,0xbf568c8c,2 +np.float32,0x3be1d8,0x3be1d8,2 +np.float32,0x80091f07,0x80091f07,2 +np.float32,0x68eabe,0x68eabe,2 +np.float32,0xfe9ab2c8,0xc2b07033,2 +np.float32,0x3eabe752,0x3ea8d3d7,2 +np.float32,0xbf7adcb2,0xbf5dfaf5,2 +np.float32,0x801ecc01,0x801ecc01,2 +np.float32,0xbf5570a9,0xbf424123,2 +np.float32,0x3e89eecd,0x3e88510e,2 +np.float32,0xfeb2feee,0xc2b0bae4,2 +np.float32,0xbeb25ec2,0xbeaef22b,2 +np.float32,0x201e49,0x201e49,2 +np.float32,0x800a35f6,0x800a35f6,2 +np.float32,0xbf02d449,0xbefb6e2a,2 +np.float32,0x3f062bea,0x3f00aef6,2 +np.float32,0x7f5219ff,0x42b26fd2,2 +np.float32,0xbd4561d0,0xbd454e47,2 +np.float32,0x3f6c4789,0x3f536a4b,2 +np.float32,0x7f58b06d,0x42b27fa1,2 +np.float32,0x7f132f39,0x42b1b999,2 +np.float32,0x3e05dcb4,0x3e057bd8,2 +np.float32,0x7f526045,0x42b2707d,2 +np.float32,0x3f6117d0,0x3f4b1adb,2 +np.float32,0xbf21f47d,0xbf18bb57,2 +np.float32,0x1a26d6,0x1a26d6,2 +np.float32,0x46b114,0x46b114,2 +np.float32,0x3eb24518,0x3eaed9ef,2 +np.float32,0xfe2139c8,0xc2af2278,2 +np.float32,0xbf7c36fb,0xbf5ef1f6,2 +np.float32,0x3f193834,0x3f114af7,2 +np.float32,0xff3ea650,0xc2b23e14,2 +np.float32,0xfeeb3bca,0xc2b146c7,2 +np.float32,0x7e8b8ca0,0x42b03b6f,2 +np.float32,0x3eed903d,0x3ee5c5d2,2 +np.float32,0xbdc73740,0xbdc6e72a,2 +np.float32,0x7e500307,0x42afa4ec,2 +np.float32,0xe003c,0xe003c,2 +np.float32,0x3e612bb4,0x3e5f64fd,2 +np.float32,0xfd81e248,0xc2ad50e6,2 +np.float32,0x766a4f,0x766a4f,2 +np.float32,0x3e8708c9,0x3e858414,2 +np.float32,0xbf206c58,0xbf176f7f,2 +np.float32,0x7e93aeb0,0x42b0586f,2 +np.float32,0xfd9d36b8,0xc2adb2ad,2 +np.float32,0xff1f4e0e,0xc2b1e21d,2 +np.float32,0x3f22bd5a,0x3f1964f8,2 +np.float32,0x7f6a517a,0x42b2a7ad,2 +np.float32,0xff6ca773,0xc2b2acc1,2 +np.float32,0x7f6bf453,0x42b2ab3d,2 +np.float32,0x3edfdd64,0x3ed9489f,2 +np.float32,0xbeafc5ba,0xbeac7daa,2 +np.float32,0x7d862039,0x42ad615b,2 +np.float32,0xbe9d2002,0xbe9ac1fc,2 +np.float32,0xbdcc54c0,0xbdcbfe5b,2 +np.float32,0xbf1bc0aa,0xbf13762a,2 +np.float32,0xbf4679ce,0xbf36984b,2 +np.float32,0x3ef45696,0x3eebe713,2 +np.float32,0xff6eb999,0xc2b2b137,2 +np.float32,0xbe4b2e4c,0xbe49dee8,2 +np.float32,0x3f498951,0x3f3901b7,2 +np.float32,0xbe9692f4,0xbe947be1,2 +np.float32,0xbf44ce26,0xbf3545c8,2 +np.float32,0x805787a8,0x805787a8,2 +np.float32,0xbf342650,0xbf27dc26,2 +np.float32,0x3edafbf0,0x3ed4cdd2,2 +np.float32,0x3f6fb858,0x3f55ef63,2 +np.float32,0xff227d0a,0xc2b1ec3f,2 +np.float32,0xfeb9a202,0xc2b0cd89,2 +np.float32,0x7f5b12c1,0x42b2853b,2 +np.float32,0x584578,0x584578,2 +np.float32,0x7ec0b76f,0x42b0e0b5,2 +np.float32,0x3f57f54b,0x3f442f10,2 +np.float32,0x7eef3620,0x42b14f5d,2 +np.float32,0x4525b5,0x4525b5,2 +np.float32,0x801bd407,0x801bd407,2 +np.float32,0xbed1f166,0xbecc7703,2 +np.float32,0x3f57e732,0x3f442449,2 +np.float32,0x80767cd5,0x80767cd5,2 +np.float32,0xbef1a7d2,0xbee97aa3,2 +np.float32,0x3dd5b1af,0x3dd54ee6,2 +np.float32,0x960c,0x960c,2 +np.float32,0x7c392d41,0x42a9ddd1,2 +np.float32,0x3f5c9a34,0x3f47b7c1,2 +np.float32,0x3f5cecee,0x3f47f667,2 +np.float32,0xbee482ce,0xbedd8899,2 +np.float32,0x8066ba7e,0x8066ba7e,2 +np.float32,0x7ed76127,0x42b119a2,2 +np.float32,0x805ca40b,0x805ca40b,2 +np.float32,0x7f5ed5d1,0x42b28df3,2 +np.float32,0xfe9e1b1e,0xc2b07b5b,2 +np.float32,0x3f0201a2,0x3ef9f6c4,2 +np.float32,0xbf2e6430,0xbf232039,2 +np.float32,0x80326b4d,0x80326b4d,2 +np.float32,0x3f11dc7c,0x3f0af06e,2 +np.float32,0xbe89c42e,0xbe8827e6,2 +np.float32,0x3f3c69f8,0x3f2e9133,2 +np.float32,0x806326a9,0x806326a9,2 +np.float32,0x3f1c5286,0x3f13f2b6,2 +np.float32,0xff5c0ead,0xc2b28786,2 +np.float32,0xff32b952,0xc2b21d01,2 +np.float32,0x7dd27c4e,0x42ae4815,2 +np.float32,0xbf7a6816,0xbf5da7a2,2 +np.float32,0xfeac72f8,0xc2b0a7d1,2 +np.float32,0x335ad7,0x335ad7,2 +np.float32,0xbe682da4,0xbe663bcc,2 +np.float32,0x3f2df244,0x3f22c208,2 +np.float32,0x80686e8e,0x80686e8e,2 +np.float32,0x7f50120f,0x42b26ad9,2 +np.float32,0x3dbc596a,0x3dbc15b3,2 +np.float32,0xbf4f2868,0xbf3d666d,2 +np.float32,0x80000001,0x80000001,2 +np.float32,0xff66c059,0xc2b29fd2,2 +np.float32,0xfe8bbcaa,0xc2b03c1f,2 +np.float32,0x3ece6a51,0x3ec93271,2 +np.float32,0x7f06cd26,0x42b18c9a,2 +np.float32,0x7e41e6dc,0x42af80f5,2 +np.float32,0x7d878334,0x42ad669f,2 +np.float32,0xfe8c5c4c,0xc2b03e67,2 +np.float32,0x337a05,0x337a05,2 +np.float32,0x3e63801d,0x3e61ab58,2 +np.float32,0x62c315,0x62c315,2 +np.float32,0x802aa888,0x802aa888,2 +np.float32,0x80038b43,0x80038b43,2 +np.float32,0xff5c1271,0xc2b2878f,2 +np.float32,0xff4184a5,0xc2b245b9,2 +np.float32,0x7ef58f4b,0x42b15cc6,2 +np.float32,0x7f42d8ac,0x42b2493a,2 +np.float32,0x806609f2,0x806609f2,2 +np.float32,0x801e763b,0x801e763b,2 +np.float32,0x7f2bc073,0x42b208a2,2 +np.float32,0x801d7d7f,0x801d7d7f,2 +np.float32,0x7d415dc1,0x42acb9c2,2 +np.float32,0xbf624ff9,0xbf4c0502,2 +np.float32,0xbf603afd,0xbf4a74e2,2 +np.float32,0x8007fe42,0x8007fe42,2 +np.float32,0x800456db,0x800456db,2 +np.float32,0x620871,0x620871,2 +np.float32,0x3e9c6c1e,0x3e9a15fa,2 +np.float32,0x4245d,0x4245d,2 +np.float32,0x8035bde9,0x8035bde9,2 +np.float32,0xbf597418,0xbf45533c,2 +np.float32,0x3c730f80,0x3c730d38,2 +np.float32,0x3f7cd8ed,0x3f5f6540,2 +np.float32,0x807e49c3,0x807e49c3,2 +np.float32,0x3d6584c0,0x3d65660c,2 +np.float32,0xff42a744,0xc2b248b8,2 +np.float32,0xfedc6f56,0xc2b12583,2 +np.float32,0x806263a4,0x806263a4,2 +np.float32,0x175a17,0x175a17,2 +np.float32,0x3f1e8537,0x3f15d208,2 +np.float32,0x4055b5,0x4055b5,2 +np.float32,0x438aa6,0x438aa6,2 +np.float32,0x8038507f,0x8038507f,2 +np.float32,0xbed75348,0xbed16f85,2 +np.float32,0x7f07b7d6,0x42b19012,2 +np.float32,0xfe8b9d30,0xc2b03bac,2 +np.float32,0x805c501c,0x805c501c,2 +np.float32,0x3ef22b1d,0x3ee9f159,2 +np.float32,0x802b6759,0x802b6759,2 +np.float32,0x45281a,0x45281a,2 +np.float32,0xbf7e9970,0xbf60a3cf,2 +np.float32,0xbf14d152,0xbf0d8062,2 +np.float32,0x3d9ff950,0x3d9fcfc8,2 +np.float32,0x7865d9,0x7865d9,2 +np.float32,0xbee67fa4,0xbedf58eb,2 +np.float32,0x7dc822d1,0x42ae2e44,2 +np.float32,0x3f3af0fe,0x3f2d612c,2 +np.float32,0xbefea106,0xbef5274e,2 +np.float32,0xbf758a3f,0xbf5a28c5,2 +np.float32,0xbf331bdd,0xbf270209,2 +np.float32,0x7f51c901,0x42b26f0d,2 +np.float32,0x3f67c33b,0x3f5014d8,2 +np.float32,0xbbc9d980,0xbbc9d92c,2 +np.float32,0xbc407540,0xbc40741e,2 +np.float32,0x7eed9a3c,0x42b14be9,2 +np.float32,0x1be0fe,0x1be0fe,2 +np.float32,0xbf6b4913,0xbf52af1f,2 +np.float32,0xbda8eba8,0xbda8bac6,2 +np.float32,0x8004bcea,0x8004bcea,2 +np.float32,0xff6f6afe,0xc2b2b2b3,2 +np.float32,0xbf205810,0xbf175e50,2 +np.float32,0x80651944,0x80651944,2 +np.float32,0xbec73016,0xbec27a3f,2 +np.float32,0x5701b9,0x5701b9,2 +np.float32,0xbf1062ce,0xbf09a7df,2 +np.float32,0x3e0306ae,0x3e02abd1,2 +np.float32,0x7bfc62,0x7bfc62,2 +np.float32,0xbf48dd3c,0xbf387a6b,2 +np.float32,0x8009573e,0x8009573e,2 +np.float32,0x660a2c,0x660a2c,2 +np.float32,0xff2280da,0xc2b1ec4b,2 +np.float32,0xbf7034fe,0xbf564a54,2 +np.float32,0xbeeb448e,0xbee3b045,2 +np.float32,0xff4e949c,0xc2b2672b,2 +np.float32,0xbf3c4486,0xbf2e7309,2 +np.float32,0x7eb086d8,0x42b0b3c8,2 +np.float32,0x7eac8aca,0x42b0a817,2 +np.float32,0xfd3d2d60,0xc2acae8b,2 +np.float32,0xbf363226,0xbf2987bd,2 +np.float32,0x7f02e524,0x42b17d8c,2 +np.float32,0x8049a148,0x8049a148,2 +np.float32,0x147202,0x147202,2 +np.float32,0x8031d3f6,0x8031d3f6,2 +np.float32,0xfe78bf68,0xc2b0007d,2 +np.float32,0x7ebd16d0,0x42b0d6fb,2 +np.float32,0xbdaed2e8,0xbdae9cbb,2 +np.float32,0x802833ae,0x802833ae,2 +np.float32,0x7f62adf6,0x42b296b5,2 +np.float32,0xff2841c0,0xc2b1fe1b,2 +np.float32,0xbeb2c47e,0xbeaf523b,2 +np.float32,0x7e42a36e,0x42af82e6,2 +np.float32,0x41ea29,0x41ea29,2 +np.float32,0xbcaaa800,0xbcaaa4d7,2 +np.float64,0x3fed71f27ebae3e5,0x3fea5c6095012ca6,2 +np.float64,0x224dc392449b9,0x224dc392449b9,2 +np.float64,0x3fdf897a7d3f12f5,0x3fde620339360992,2 +np.float64,0xbfe1f99a5123f334,0xbfe124a57cfaf556,2 +np.float64,0xbfd9725c3bb2e4b8,0xbfd8d1e3f75110c7,2 +np.float64,0x3fe38977546712ee,0x3fe27d9d37f4b91f,2 +np.float64,0xbfc36c29e526d854,0xbfc3594743ee45c4,2 +np.float64,0xbfe5cbec332b97d8,0xbfe4638802316849,2 +np.float64,0x2ff35efe5fe6d,0x2ff35efe5fe6d,2 +np.float64,0x7fd3f828e227f051,0x40862a7d4a40b1e0,2 +np.float64,0xffd06fc11620df82,0xc08628ee8f1bf6c8,2 +np.float64,0x3fe5321bf4aa6438,0x3fe3e3d9fa453199,2 +np.float64,0xffd07a323ca0f464,0xc08628f3a2930f8c,2 +np.float64,0x3fdf7abe7abef57c,0x3fde54cb193d49cb,2 +np.float64,0x40941f1881285,0x40941f1881285,2 +np.float64,0xffef18defc7e31bd,0xc0863393f2c9f061,2 +np.float64,0xbfe379f871e6f3f1,0xbfe270620cb68347,2 +np.float64,0xffec829848f90530,0xc08632e210edaa2b,2 +np.float64,0x80070c00574e1801,0x80070c00574e1801,2 +np.float64,0xffce7654b23ceca8,0xc086285291e89975,2 +np.float64,0x7fc9932daa33265a,0x408626ec6cc2b807,2 +np.float64,0x355ee98c6abde,0x355ee98c6abde,2 +np.float64,0x3fac54962c38a920,0x3fac50e40b6c19f2,2 +np.float64,0x800857984af0af31,0x800857984af0af31,2 +np.float64,0x7fea6a3d55f4d47a,0x40863245bf39f179,2 +np.float64,0x3fdb8fab33371f56,0x3fdac5ffc9e1c347,2 +np.float64,0x800a887a7bf510f5,0x800a887a7bf510f5,2 +np.float64,0xbfbdbda3c63b7b48,0xbfbdac9dd5a2d3e8,2 +np.float64,0xbfd4a2457b29448a,0xbfd44acb3b316d6d,2 +np.float64,0x7fd5329a502a6534,0x40862af789b528b5,2 +np.float64,0x3fd96a7bceb2d4f8,0x3fd8ca92104d6cd6,2 +np.float64,0x3fde6a0cd6bcd41a,0x3fdd5f4b85abf749,2 +np.float64,0xbfc7faaff32ff560,0xbfc7d7560b8c4a52,2 +np.float64,0x7fec381b2f787035,0x408632cd0e9c095c,2 +np.float64,0x1fc2eb543f85e,0x1fc2eb543f85e,2 +np.float64,0x7ac6000af58c1,0x7ac6000af58c1,2 +np.float64,0xffe060a87920c150,0xc0862e72c37d5a4e,2 +np.float64,0xbfb7d8c89e2fb190,0xbfb7cffd3c3f8e3a,2 +np.float64,0x3fd91033deb22068,0x3fd87695b067aa1e,2 +np.float64,0x3fec1aff01b835fe,0x3fe95d5cbd729af7,2 +np.float64,0x7fb97f69ec32fed3,0x4086215aaae5c697,2 +np.float64,0x7feaf1e4e5f5e3c9,0x4086326e6ca6a2bb,2 +np.float64,0x800537e44d0a6fc9,0x800537e44d0a6fc9,2 +np.float64,0x800b2a0d0d36541a,0x800b2a0d0d36541a,2 +np.float64,0x3fe2193846e43270,0x3fe140308550138e,2 +np.float64,0x5e2a0a32bc542,0x5e2a0a32bc542,2 +np.float64,0xffe5888b09eb1116,0xc08630a348783aa3,2 +np.float64,0xbfceb9b5033d736c,0xbfce701049c10435,2 +np.float64,0x7fe5d68589abad0a,0x408630c00ce63f23,2 +np.float64,0x8009b5457ff36a8b,0x8009b5457ff36a8b,2 +np.float64,0xbfb5518c2e2aa318,0xbfb54b42638ca718,2 +np.float64,0x3f9c58469838b080,0x3f9c575974fbcd7b,2 +np.float64,0x3fe8db4b4731b697,0x3fe6dc9231587966,2 +np.float64,0x8007d0f77f4fa1f0,0x8007d0f77f4fa1f0,2 +np.float64,0x7fe79eef542f3dde,0x40863160c673c67f,2 +np.float64,0xffbdc0b6163b8170,0xc0862296be4bf032,2 +np.float64,0x3fbb8d3312371a66,0x3fbb7fa76fb4cf8d,2 +np.float64,0xffd8a0eedbb141de,0xc0862c2ac6e512f0,2 +np.float64,0x7fee99d8d87d33b1,0x4086337301c4c8df,2 +np.float64,0xffe7479b552e8f36,0xc0863142fba0f0ec,2 +np.float64,0xffedf8ef4abbf1de,0xc08633488068fe69,2 +np.float64,0x895c4d9f12b8a,0x895c4d9f12b8a,2 +np.float64,0x29b4caf05369a,0x29b4caf05369a,2 +np.float64,0xbfefb90d657f721b,0xbfec01efa2425b35,2 +np.float64,0xde07c3bdbc0f9,0xde07c3bdbc0f9,2 +np.float64,0x7feae9fd02f5d3f9,0x4086326c1368ed5a,2 +np.float64,0x3feab792da756f26,0x3fe84f6e15338ed7,2 +np.float64,0xbfeff8ed72fff1db,0xbfec2f35da06daaf,2 +np.float64,0x8004b2c132896583,0x8004b2c132896583,2 +np.float64,0xbf9fcb00103f9600,0xbf9fc9b1751c569e,2 +np.float64,0x4182b72e83058,0x4182b72e83058,2 +np.float64,0x90820d812105,0x90820d812105,2 +np.float64,0xbfdec9a0ba3d9342,0xbfddb585df607ce1,2 +np.float64,0x7fdc0a69a03814d2,0x40862d347f201b63,2 +np.float64,0xbfef0708937e0e11,0xbfeb82d27f8ea97f,2 +np.float64,0xffda57e4ddb4afca,0xc0862cb49e2e0c4c,2 +np.float64,0xbfa30b9af4261730,0xbfa30a7b4a633060,2 +np.float64,0x7feb57fcc4b6aff9,0x4086328c83957a0b,2 +np.float64,0x7fe6759153eceb22,0x408630f980433963,2 +np.float64,0x7fdd3278c8ba64f1,0x40862d87445243e9,2 +np.float64,0xd3b8e6b9a771d,0xd3b8e6b9a771d,2 +np.float64,0x6267dc88c4cfc,0x6267dc88c4cfc,2 +np.float64,0x7fedd3cf00bba79d,0x4086333e91712ff5,2 +np.float64,0xffbe512ce03ca258,0xc08622bd39314cea,2 +np.float64,0xbfe71742ca6e2e86,0xbfe572ccbf2d010d,2 +np.float64,0x8002fb048c65f60a,0x8002fb048c65f60a,2 +np.float64,0x800d9d9ddf7b3b3c,0x800d9d9ddf7b3b3c,2 +np.float64,0xbfeaf6230df5ec46,0xbfe87f5d751ec3d5,2 +np.float64,0xbfe69973a42d32e8,0xbfe50c680f7002fe,2 +np.float64,0x3fe309cf87e613a0,0x3fe21048714ce1ac,2 +np.float64,0x800435d17a286ba4,0x800435d17a286ba4,2 +np.float64,0x7fefffffffffffff,0x408633ce8fb9f87e,2 +np.float64,0x3fe36ade1766d5bc,0x3fe26379fb285dde,2 +np.float64,0x3f98d8d94831b1c0,0x3f98d839885dc527,2 +np.float64,0xbfd08f7ae5211ef6,0xbfd0618ab5293e1e,2 +np.float64,0xbfcf630bd53ec618,0xbfcf14a0cd20704d,2 +np.float64,0xbfe58f0ca6eb1e1a,0xbfe4312225df8e28,2 +np.float64,0xffef4f6406be9ec7,0xc08633a1ed1d27e5,2 +np.float64,0x7fe10120b3e20240,0x40862ebfaf94e6e8,2 +np.float64,0xffe96c52fbb2d8a5,0xc08631f75d9a59a0,2 +np.float64,0xbfe448a333e89146,0xbfe31fee44c3ec43,2 +np.float64,0x80045ff4e788bfeb,0x80045ff4e788bfeb,2 +np.float64,0x7fefaa2f823f545e,0x408633b8fea29524,2 +np.float64,0xffea6b8bf234d717,0xc0863246248e5960,2 +np.float64,0xbfdb085d80b610bc,0xbfda498b15b43eec,2 +np.float64,0xbfd5e12da3abc25c,0xbfd57970e2b8aecc,2 +np.float64,0x3fcc84928a390925,0x3fcc497c417a89f3,2 +np.float64,0xbfdcb713bf396e28,0xbfdbd46c5e731fd9,2 +np.float64,0xffdf50c0453ea180,0xc0862e16b5562f25,2 +np.float64,0x800342c2f7268587,0x800342c2f7268587,2 +np.float64,0x7feb8b6d743716da,0x4086329b8248de2c,2 +np.float64,0x800a9b18b4953632,0x800a9b18b4953632,2 +np.float64,0xffedaf0d12fb5e19,0xc0863334af82de1a,2 +np.float64,0x800aebda4ab5d7b5,0x800aebda4ab5d7b5,2 +np.float64,0xbfa9f5848433eb10,0xbfa9f2ac7ac065d4,2 +np.float64,0x3fea375928f46eb2,0x3fe7ec9f10eeac7d,2 +np.float64,0x3fd6c213fead8428,0x3fd64dcc1eff5f1b,2 +np.float64,0xbfa0476f44208ee0,0xbfa046bb986007ac,2 +np.float64,0x6c8e18aed91c4,0x6c8e18aed91c4,2 +np.float64,0x8000000000000001,0x8000000000000001,2 +np.float64,0x7fea86b5ba350d6a,0x4086324e59f13027,2 +np.float64,0x2316c3b0462d9,0x2316c3b0462d9,2 +np.float64,0x3fec4e3281389c65,0x3fe983c5c9d65940,2 +np.float64,0x3fbb87c47f772,0x3fbb87c47f772,2 +np.float64,0x8004af00fdc95e03,0x8004af00fdc95e03,2 +np.float64,0xbfd316db9ba62db8,0xbfd2d12765b9d155,2 +np.float64,0x3fec1a7a99f834f6,0x3fe95cf941889b3d,2 +np.float64,0x3feff7e1477fefc3,0x3fec2e782392d4b9,2 +np.float64,0xbfc683ea042d07d4,0xbfc66698cfa5026e,2 +np.float64,0x3fdbc8aaa9b79154,0x3fdafa50e6fc3fff,2 +np.float64,0xfb3b630ff676d,0xfb3b630ff676d,2 +np.float64,0x7fe715ef8eae2bde,0x40863131d794b41f,2 +np.float64,0x7fefa06c11bf40d7,0x408633b686c7996a,2 +np.float64,0x80002a40f5205483,0x80002a40f5205483,2 +np.float64,0x7fe95f3c74b2be78,0x408631f33e37bf76,2 +np.float64,0x3fb2977b32252ef0,0x3fb2934eaf5a4be8,2 +np.float64,0x3fc0f3dbc821e7b8,0x3fc0e745288c84c3,2 +np.float64,0x3fda98da56b531b5,0x3fd9e2b19447dacc,2 +np.float64,0x3f95b9d5202b73aa,0x3f95b96a53282949,2 +np.float64,0x3fdc1ace7738359d,0x3fdb4597d31df7ff,2 +np.float64,0xffeac5bb2e358b76,0xc0863261452ab66c,2 +np.float64,0xbfefb1b78f7f636f,0xbfebfcb9be100ced,2 +np.float64,0xf5c9e191eb93c,0xf5c9e191eb93c,2 +np.float64,0x3fe83a977630752f,0x3fe65d0df90ff6ef,2 +np.float64,0x3fc317515d262ea0,0x3fc3056072b719f0,2 +np.float64,0x7fe2dcfab225b9f4,0x40862f94257c28a2,2 +np.float64,0xca2b115794562,0xca2b115794562,2 +np.float64,0x3fd495301aa92a60,0x3fd43e57108761d5,2 +np.float64,0x800ccc4293199885,0x800ccc4293199885,2 +np.float64,0xc8d3173d91a63,0xc8d3173d91a63,2 +np.float64,0xbf2541bb7e4a8,0xbf2541bb7e4a8,2 +np.float64,0xbfe9a330df334662,0xbfe779816573f5be,2 +np.float64,0xffd5e4c8252bc990,0xc0862b39b3ca5d72,2 +np.float64,0x3fe90f3a53721e75,0x3fe70585ae09531d,2 +np.float64,0xbfe2b5ddc7a56bbc,0xbfe1c7fa91a675ed,2 +np.float64,0xbf981a0360303400,0xbf9819719345073a,2 +np.float64,0x19174b0e322ea,0x19174b0e322ea,2 +np.float64,0xbfd2f71a1725ee34,0xbfd2b2b6f7cd10b1,2 +np.float64,0x80056e83236add07,0x80056e83236add07,2 +np.float64,0x7fe4bc41d9697883,0x40863055f20ce0cb,2 +np.float64,0xffe76e06c46edc0d,0xc086315024b25559,2 +np.float64,0x3fe3c4f0f96789e2,0x3fe2b04b584609bf,2 +np.float64,0x3fe6cfc533ed9f8a,0x3fe538b4d784d5ee,2 +np.float64,0x7fd234a640a4694c,0x408629bfead4f0b2,2 +np.float64,0x3fdbc49c9ab78939,0x3fdaf698a83d08e2,2 +np.float64,0x3fe4c5336ee98a66,0x3fe388c6ddb60e0a,2 +np.float64,0xf4b9497be9729,0xf4b9497be9729,2 +np.float64,0x3fb312be12262580,0x3fb30e3c847c1d16,2 +np.float64,0x3fe9554218f2aa84,0x3fe73c8b311c7a98,2 +np.float64,0xff899816a0333040,0xc08610bfb2cd8559,2 +np.float64,0x8006008ad52c0116,0x8006008ad52c0116,2 +np.float64,0x3fd7d47be4afa8f8,0x3fd74fa71ec17fd0,2 +np.float64,0x8010000000000000,0x8010000000000000,2 +np.float64,0xdf2a9943be553,0xdf2a9943be553,2 +np.float64,0xbfeb86bf1eb70d7e,0xbfe8ed797580ba5c,2 +np.float64,0x800e2c0c28bc5818,0x800e2c0c28bc5818,2 +np.float64,0xbfe2be65d4657ccc,0xbfe1cf578dec2323,2 +np.float64,0xbfedea3a5afbd475,0xbfeab490bf05e585,2 +np.float64,0xbfe04b1583a0962b,0xbfdf523dfd7be25c,2 +np.float64,0x75929bb4eb254,0x75929bb4eb254,2 +np.float64,0x3fd7b4968caf692d,0x3fd731c0938ff97c,2 +np.float64,0x60bd8fd2c17b3,0x60bd8fd2c17b3,2 +np.float64,0xbfdaf15e70b5e2bc,0xbfda345a95ce18fe,2 +np.float64,0x7fdd7c35c2baf86b,0x40862d9b5f40c6b2,2 +np.float64,0x7feeb4d2ab7d69a4,0x4086337a0c0dffaf,2 +np.float64,0xffe65b5a1decb6b4,0xc08630f024420efb,2 +np.float64,0x7feb272b30764e55,0x4086327e2e553aa2,2 +np.float64,0x3fd27513e8a4ea28,0x3fd235ea49670f6a,2 +np.float64,0x3fe6541a6aeca834,0x3fe4d3a5b69fd1b6,2 +np.float64,0xbfe0c6ca0f618d94,0xbfe017058259efdb,2 +np.float64,0x7fc1bf07b7237e0e,0x4086240000fa5a52,2 +np.float64,0x7fe96af9c0f2d5f3,0x408631f6f0f4faa2,2 +np.float64,0x3fe0728be7a0e518,0x3fdf9881a5869de9,2 +np.float64,0xffe8ea4441b1d488,0xc08631ce0685ae7e,2 +np.float64,0xffd0b973f02172e8,0xc08629121e7fdf85,2 +np.float64,0xffe37b907a26f720,0xc0862fd6529401a0,2 +np.float64,0x3fe0ee826461dd05,0x3fe03a2a424a1b40,2 +np.float64,0xbfe8073c92300e79,0xbfe6340cbd179ac1,2 +np.float64,0x800768383f8ed071,0x800768383f8ed071,2 +np.float64,0x8002e467c7c5c8d0,0x8002e467c7c5c8d0,2 +np.float64,0xbfd8d53ea5b1aa7e,0xbfd83fa7243289d7,2 +np.float64,0xffebefce2bb7df9c,0xc08632b874f4f8dc,2 +np.float64,0xffe3be9eb9277d3d,0xc0862ff1ac70ad0b,2 +np.float64,0xffe2f8a82e65f150,0xc0862f9fd9e77d86,2 +np.float64,0xbfa01d151c203a30,0xbfa01c66dc13a70a,2 +np.float64,0x800877062d30ee0d,0x800877062d30ee0d,2 +np.float64,0xaade16a755bc3,0xaade16a755bc3,2 +np.float64,0xbfeb1abc70363579,0xbfe89b52c3b003aa,2 +np.float64,0x80097d0b2ad2fa17,0x80097d0b2ad2fa17,2 +np.float64,0x8001499907429333,0x8001499907429333,2 +np.float64,0x3fe8db2aaf71b656,0x3fe6dc7873f1b235,2 +np.float64,0x5cfeadc4b9fd6,0x5cfeadc4b9fd6,2 +np.float64,0xff3f77d1fe7ef,0xff3f77d1fe7ef,2 +np.float64,0xffeecd56f9bd9aad,0xc08633806cb1163d,2 +np.float64,0xbf96f3ca582de7a0,0xbf96f34c6b8e1c85,2 +np.float64,0x7ed6b44afdad7,0x7ed6b44afdad7,2 +np.float64,0x80071808da4e3012,0x80071808da4e3012,2 +np.float64,0x3feb8aee2bf715dc,0x3fe8f0a55516615c,2 +np.float64,0x800038f62e2071ed,0x800038f62e2071ed,2 +np.float64,0x3fb13f9af2227f30,0x3fb13c456ced8e08,2 +np.float64,0xffd584d1812b09a4,0xc0862b165558ec0c,2 +np.float64,0x800b20c30fb64186,0x800b20c30fb64186,2 +np.float64,0x80024f9646e49f2d,0x80024f9646e49f2d,2 +np.float64,0xffefffffffffffff,0xc08633ce8fb9f87e,2 +np.float64,0x3fdddbcb5bbbb797,0x3fdcde981111f650,2 +np.float64,0xffed14077f3a280e,0xc086330a795ad634,2 +np.float64,0x800fec2da7ffd85b,0x800fec2da7ffd85b,2 +np.float64,0x3fe8205ffc7040c0,0x3fe6482318d217f9,2 +np.float64,0x3013e5226027d,0x3013e5226027d,2 +np.float64,0xffe4e5aad469cb55,0xc0863065dc2fb4e3,2 +np.float64,0x5cb0f7b2b9620,0x5cb0f7b2b9620,2 +np.float64,0xbfeb4537d2768a70,0xbfe8bbb2c1d3bff9,2 +np.float64,0xbfd859e297b0b3c6,0xbfd7cc807948bf9d,2 +np.float64,0x71f00b8ce3e02,0x71f00b8ce3e02,2 +np.float64,0xf5c1b875eb837,0xf5c1b875eb837,2 +np.float64,0xa0f35c8141e8,0xa0f35c8141e8,2 +np.float64,0xffe24860b42490c1,0xc0862f54222f616e,2 +np.float64,0xffcd9ae8583b35d0,0xc08628181e643a42,2 +np.float64,0x7fe9b710c7736e21,0x4086320ec033490f,2 +np.float64,0x3fd2b9ca1d257394,0x3fd277e631f0c0b3,2 +np.float64,0x23559bfc46ab4,0x23559bfc46ab4,2 +np.float64,0x8002adf75e455bef,0x8002adf75e455bef,2 +np.float64,0xbfefa4d75cbf49af,0xbfebf392e51d6a1a,2 +np.float64,0xffcfef263e3fde4c,0xc08628b336adb611,2 +np.float64,0x80061acaa8ec3596,0x80061acaa8ec3596,2 +np.float64,0x7fc1b33be0236677,0x408623faaddcc17e,2 +np.float64,0x7fe3a84083675080,0x40862fe8972e41e1,2 +np.float64,0xbfe756c1276ead82,0xbfe5a6318b061e1b,2 +np.float64,0xbfae4b71b43c96e0,0xbfae46ed0b6203a4,2 +np.float64,0x800421c6d0a8438e,0x800421c6d0a8438e,2 +np.float64,0x8009ad56fe335aae,0x8009ad56fe335aae,2 +np.float64,0xbfe71afc976e35f9,0xbfe575d21f3d7193,2 +np.float64,0x7fec0bbe4c38177c,0x408632c0710f1d8a,2 +np.float64,0x750e1daeea1c4,0x750e1daeea1c4,2 +np.float64,0x800501d4240a03a9,0x800501d4240a03a9,2 +np.float64,0x800794955cef292b,0x800794955cef292b,2 +np.float64,0x3fdf8a87f5bf1510,0x3fde62f4f00cfa19,2 +np.float64,0xbfebebdbc7f7d7b8,0xbfe939e51ba1340c,2 +np.float64,0xbfe3a16217a742c4,0xbfe292039dd08a71,2 +np.float64,0x3fed6cd04c3ad9a1,0x3fea58995973f74b,2 +np.float64,0xffcad8787335b0f0,0xc086274fbb35dd37,2 +np.float64,0x3fcb178e3d362f1c,0x3fcae4c9f3e6dddc,2 +np.float64,0xbfcadc669435b8cc,0xbfcaaae7cf075420,2 +np.float64,0x7fe0e3906321c720,0x40862eb1bacc5c43,2 +np.float64,0xff8ad5edb035abc0,0xc0861120b6404d0b,2 +np.float64,0x3fe175a21562eb44,0x3fe0b13120a46549,2 +np.float64,0xbfeb4c4a5f769895,0xbfe8c1147f1c9d8f,2 +np.float64,0x7fca22f4e63445e9,0x40862718e9b4094e,2 +np.float64,0x3fe4269d0c684d3a,0x3fe3032aa2015c53,2 +np.float64,0x3fef551c09beaa38,0x3febbabe03f49c83,2 +np.float64,0xffd843df9fb087c0,0xc0862c0c52d5e5d9,2 +np.float64,0x7fc497e2ca292fc5,0x40862530bbd9fcc7,2 +np.float64,0x3fee02919efc0523,0x3feac655588a4acd,2 +np.float64,0x7fed1e52c0fa3ca5,0x4086330d4ddd8a2c,2 +np.float64,0xba04d4ef7409b,0xba04d4ef7409b,2 +np.float64,0x3fee22d0937c45a2,0x3feaddd4ca66b447,2 +np.float64,0xffeb2558cf764ab1,0xc086327da4e84053,2 +np.float64,0xbfe103d987e207b3,0xbfe04d04818ad1ff,2 +np.float64,0x3f9fd7fed03faffe,0x3f9fd6ae9a45be84,2 +np.float64,0x800a53ec4c34a7d9,0x800a53ec4c34a7d9,2 +np.float64,0xbfe2feb17f65fd63,0xbfe206b9d33a78a2,2 +np.float64,0x989bdd613139,0x989bdd613139,2 +np.float64,0xbfdd0ad3fb3a15a8,0xbfdc20c32a530741,2 +np.float64,0xbfc4222163284444,0xbfc40d1c612784b5,2 +np.float64,0xc30cf5c78619f,0xc30cf5c78619f,2 +np.float64,0x3fe913bd6732277b,0x3fe70912f76bad71,2 +np.float64,0x98f175f531e2f,0x98f175f531e2f,2 +np.float64,0x3fed8c1f717b183f,0x3fea6f9fb3af3423,2 +np.float64,0x7fee46b085bc8d60,0x4086335d269eb7e9,2 +np.float64,0x8007480f564e901f,0x8007480f564e901f,2 +np.float64,0xc9b96e179372e,0xc9b96e179372e,2 +np.float64,0x3fe44deac4289bd6,0x3fe32463a74a69e7,2 +np.float64,0x80021d6c5c243ad9,0x80021d6c5c243ad9,2 +np.float64,0xbfebc805a6f7900b,0xbfe91edcf65a1c19,2 +np.float64,0x80044748adc88e92,0x80044748adc88e92,2 +np.float64,0x4007ee44800fe,0x4007ee44800fe,2 +np.float64,0xbfe24307a4648610,0xbfe1648ad5c47b6f,2 +np.float64,0xbfee6d3a93fcda75,0xbfeb13e1a3196e78,2 +np.float64,0x3fe49a287f293451,0x3fe364a11b9f0068,2 +np.float64,0x80052b37ceaa5670,0x80052b37ceaa5670,2 +np.float64,0xbfd42be893a857d2,0xbfd3da05dac7c286,2 +np.float64,0xffb4bbe4ac2977c8,0xc0861fb31bda6956,2 +np.float64,0xbfc732a4142e6548,0xbfc7129a4eafa399,2 +np.float64,0x7fd0696791a0d2ce,0x408628eb7756cb9c,2 +np.float64,0x3fe46c8f8d68d91f,0x3fe33e3df16187c1,2 +np.float64,0x3fe3a28f1ce7451e,0x3fe293043238d08c,2 +np.float64,0xffedc4eb723b89d6,0xc086333a92258c15,2 +np.float64,0x8000d15b4c41a2b7,0x8000d15b4c41a2b7,2 +np.float64,0xffeb73450236e689,0xc08632947b0148ab,2 +np.float64,0xffe68cf4722d19e8,0xc0863101d08d77bd,2 +np.float64,0x800c70eb4698e1d7,0x800c70eb4698e1d7,2 +np.float64,0xffa94387ff529,0xffa94387ff529,2 +np.float64,0x7fe3835d996706ba,0x40862fd985ff8e7d,2 +np.float64,0x3fe55e476feabc8e,0x3fe408a15594ec52,2 +np.float64,0xffc69672222d2ce4,0xc08625ee0c4c0f6a,2 +np.float64,0xbf9d900b883b2020,0xbf9d8efe811d36df,2 +np.float64,0xbfdb9b9755b7372e,0xbfdad0f2aa2cb110,2 +np.float64,0xffeade6073b5bcc0,0xc08632689f17a25d,2 +np.float64,0xffd1d6a6baa3ad4e,0xc086299630a93a7b,2 +np.float64,0x7fd05ba25620b744,0x408628e4be1ef845,2 +np.float64,0xbfc7d422d52fa844,0xbfc7b170a61531bf,2 +np.float64,0x3fd5196797aa32d0,0x3fd4bc0f0e7d8e1d,2 +np.float64,0x617594a4c2eb3,0x617594a4c2eb3,2 +np.float64,0x7fd779bc4caef378,0x40862bc89271b882,2 +np.float64,0xffd2fb262ba5f64c,0xc0862a15561e9524,2 +np.float64,0x72fd661ae5fad,0x72fd661ae5fad,2 +np.float64,0x3fecf441f339e884,0x3fe9ff880d584f64,2 +np.float64,0x7fc3a8968827512c,0x408624d198b05c61,2 +np.float64,0x3fe7a25c56ef44b9,0x3fe5e32509a7c32d,2 +np.float64,0x7fd117d514222fa9,0x4086293ec640d5f2,2 +np.float64,0x3fe37dfe5ee6fbfc,0x3fe273d1bcaa1ef0,2 +np.float64,0xbfed4cd19d7a99a3,0xbfea41064cba4c8b,2 +np.float64,0x8003ff12aaa7fe26,0x8003ff12aaa7fe26,2 +np.float64,0x3fcbc3d1193787a2,0x3fcb8d39e3e88264,2 +np.float64,0xe9ba1a91d3744,0xe9ba1a91d3744,2 +np.float64,0x8002ab71998556e4,0x8002ab71998556e4,2 +np.float64,0x800110057922200c,0x800110057922200c,2 +np.float64,0xbfe3b7af19a76f5e,0xbfe2a502fc0a2882,2 +np.float64,0x7fd9de9d5e33bd3a,0x40862c8f73cccabf,2 +np.float64,0xbfba0f0a86341e18,0xbfba0392f44c2771,2 +np.float64,0x8000000000000000,0x8000000000000000,2 +np.float64,0x7fe5d162e96ba2c5,0x408630be2b15e01b,2 +np.float64,0x800b7f0eac76fe1e,0x800b7f0eac76fe1e,2 +np.float64,0xff98bed150317da0,0xc086160633164f5f,2 +np.float64,0x3fef91fd70ff23fb,0x3febe629709d0ae7,2 +np.float64,0x7fe5bea7f16b7d4f,0x408630b749f445e9,2 +np.float64,0xbfe3dc428467b885,0xbfe2c41ea93fab07,2 +np.float64,0xbfeba1fbfcf743f8,0xbfe9021b52851bb9,2 +np.float64,0x7fd2fb2108a5f641,0x40862a1553f45830,2 +np.float64,0x7feb8199a4370332,0x40863298a7169dad,2 +np.float64,0x800f97ff8d7f2fff,0x800f97ff8d7f2fff,2 +np.float64,0x3fd5e20b6b2bc417,0x3fd57a42bd1c0993,2 +np.float64,0x8006b4072dad680f,0x8006b4072dad680f,2 +np.float64,0x605dccf2c0bba,0x605dccf2c0bba,2 +np.float64,0x3fc705ed142e0bda,0x3fc6e69971d86f73,2 +np.float64,0xffd2ba1aad257436,0xc08629f9bc918f8b,2 +np.float64,0x8002954e23c52a9d,0x8002954e23c52a9d,2 +np.float64,0xbfecc65da7798cbb,0xbfe9dd745be18562,2 +np.float64,0x7fc66110482cc220,0x408625db0db57ef8,2 +np.float64,0x3fcd09446d3a1289,0x3fcccaf2dd0a41ea,2 +np.float64,0x3febe7095437ce13,0x3fe93642d1e73b2a,2 +np.float64,0x8004773c7da8ee7a,0x8004773c7da8ee7a,2 +np.float64,0x8001833241230665,0x8001833241230665,2 +np.float64,0x3fe6a262db6d44c6,0x3fe513b3dab5adce,2 +np.float64,0xe6282cc1cc506,0xe6282cc1cc506,2 +np.float64,0x800b9d8553973b0b,0x800b9d8553973b0b,2 +np.float64,0x3fdfbe0c7b3f7c19,0x3fde912375d867a8,2 +np.float64,0x7fd5ac11ebab5823,0x40862b24dfc6d08e,2 +np.float64,0x800e4b7cb1fc96f9,0x800e4b7cb1fc96f9,2 +np.float64,0x3fe14706da628e0e,0x3fe0883aec2a917a,2 +np.float64,0x7fc963f97532c7f2,0x408626dd9b0cafe1,2 +np.float64,0xbfe9c250b5b384a2,0xbfe791c5eabcb05d,2 +np.float64,0x3fe8d16e6c71a2dd,0x3fe6d4c7a33a0bf4,2 +np.float64,0x3fe474ae4628e95d,0x3fe34515c93f4733,2 +np.float64,0x3fbf3257ee3e64b0,0x3fbf1eb530e126ea,2 +np.float64,0x8005f089b3abe114,0x8005f089b3abe114,2 +np.float64,0x3fece07bccf9c0f8,0x3fe9f0dc228124d5,2 +np.float64,0xbfc52521632a4a44,0xbfc50ccebdf59c2c,2 +np.float64,0x7fdf53beb13ea77c,0x40862e177918195e,2 +np.float64,0x8003d9f6ad07b3ee,0x8003d9f6ad07b3ee,2 +np.float64,0xffeacf96bbb59f2d,0xc086326436b38b1a,2 +np.float64,0xdccaea29b995e,0xdccaea29b995e,2 +np.float64,0x5948d21eb291b,0x5948d21eb291b,2 +np.float64,0x10000000000000,0x10000000000000,2 +np.float64,0x7fef6d2c543eda58,0x408633a98593cdf5,2 +np.float64,0x7feda454f47b48a9,0x40863331cb6dc9f7,2 +np.float64,0x3fdd377cecba6ef8,0x3fdc4968f74a9c83,2 +np.float64,0x800644096d4c8814,0x800644096d4c8814,2 +np.float64,0xbfe33ca15ae67942,0xbfe23be5de832bd8,2 +np.float64,0xffce9582bd3d2b04,0xc086285abdf9bf9d,2 +np.float64,0x3fe6621e86acc43d,0x3fe4df231bfa93e1,2 +np.float64,0xee7d19e9dcfa3,0xee7d19e9dcfa3,2 +np.float64,0x800be5997277cb33,0x800be5997277cb33,2 +np.float64,0x82069041040e,0x82069041040e,2 +np.float64,0x800d6efdc19addfc,0x800d6efdc19addfc,2 +np.float64,0x7fb27770ee24eee1,0x40861ec5ed91b839,2 +np.float64,0x3fd506064caa0c0d,0x3fd4a9a66353fefd,2 +np.float64,0xbfeca9b36bf95367,0xbfe9c81f03ba37b8,2 +np.float64,0xffeab1b7bab5636f,0xc086325b47f61f2b,2 +np.float64,0xffc99f5b2e333eb8,0xc08626f03b08b412,2 +np.float64,0x3fbf1a71bc3e34e3,0x3fbf06fbcaa5de58,2 +np.float64,0x3fe75015736ea02b,0x3fe5a0cd8d763d8d,2 +np.float64,0xffe6a7442fad4e88,0xc086310b20addba4,2 +np.float64,0x3fe5d62ff86bac60,0x3fe46c033195bf28,2 +np.float64,0x7fd0b1f0362163df,0x4086290e857dc1be,2 +np.float64,0xbe0353737c06b,0xbe0353737c06b,2 +np.float64,0x7fec912d8739225a,0x408632e627704635,2 +np.float64,0xded8ba2fbdb18,0xded8ba2fbdb18,2 +np.float64,0x7fec0b53fdf816a7,0x408632c052bc1bd2,2 +np.float64,0x7fe9640d12b2c819,0x408631f4c2ba54d8,2 +np.float64,0x800be714eeb7ce2a,0x800be714eeb7ce2a,2 +np.float64,0xbfcf444a793e8894,0xbfcef6c126b54853,2 +np.float64,0xffeb20cf1bf6419e,0xc086327c4e6ffe80,2 +np.float64,0xc07de22180fd,0xc07de22180fd,2 +np.float64,0xffed129d387a253a,0xc086330a15ad0adb,2 +np.float64,0x3fd9e94fedb3d2a0,0x3fd94049924706a8,2 +np.float64,0x7fe6ba488c2d7490,0x40863111d51e7861,2 +np.float64,0xbfebbdf25db77be5,0xbfe91740ad7ba521,2 +np.float64,0x7fbc6c3c4838d878,0x40862239160cb613,2 +np.float64,0xbfefa82ecebf505e,0xbfebf5f31957dffd,2 +np.float64,0x800bebeb7ad7d7d7,0x800bebeb7ad7d7d7,2 +np.float64,0x7fecccc6f8f9998d,0x408632f6c6da8aac,2 +np.float64,0xcbe4926197ca,0xcbe4926197ca,2 +np.float64,0x2c5d9fd858bb5,0x2c5d9fd858bb5,2 +np.float64,0xbfe9fb021073f604,0xbfe7bddc61f1151a,2 +np.float64,0xbfebb18572f7630b,0xbfe90ddc5002313f,2 +np.float64,0x13bb0d3227763,0x13bb0d3227763,2 +np.float64,0x3feefa5e5cbdf4bd,0x3feb79b9e8ce16bf,2 +np.float64,0x3fc97f086132fe10,0x3fc9549fc8e15ecb,2 +np.float64,0xffe70887c06e110f,0xc086312d30fd31cf,2 +np.float64,0xa00c113540182,0xa00c113540182,2 +np.float64,0x800950984772a131,0x800950984772a131,2 +np.float64,0x1,0x1,2 +np.float64,0x3fd83b4026b07680,0x3fd7afdc659d9a34,2 +np.float64,0xbfe32348fbe64692,0xbfe226292a706a1a,2 +np.float64,0x800b894dcc77129c,0x800b894dcc77129c,2 +np.float64,0xeb2ca419d6595,0xeb2ca419d6595,2 +np.float64,0xbff0000000000000,0xbfec34366179d427,2 +np.float64,0x3feb269e99f64d3d,0x3fe8a4634b927a21,2 +np.float64,0xbfe83149d7706294,0xbfe655a2b245254e,2 +np.float64,0xbfe6eef3ca6ddde8,0xbfe5521310e24d16,2 +np.float64,0x3fea89a4b7b51349,0x3fe82c1fc69edcec,2 +np.float64,0x800f2a8bf17e5518,0x800f2a8bf17e5518,2 +np.float64,0x800f71fac29ee3f6,0x800f71fac29ee3f6,2 +np.float64,0xe7cb31f1cf966,0xe7cb31f1cf966,2 +np.float64,0x3b0f8752761f2,0x3b0f8752761f2,2 +np.float64,0x3fea27dea3744fbd,0x3fe7e0a4705476b2,2 +np.float64,0xbfa97c019c32f800,0xbfa97950c1257b92,2 +np.float64,0xffeff13647ffe26c,0xc08633cadc7105ed,2 +np.float64,0x3feee162353dc2c4,0x3feb67c2da0fbce8,2 +np.float64,0x80088c0807911810,0x80088c0807911810,2 +np.float64,0x3fe936ab1db26d56,0x3fe72489bc69719d,2 +np.float64,0xa2f84bd545f0a,0xa2f84bd545f0a,2 +np.float64,0xbfed445ed27a88be,0xbfea3acac0aaf482,2 +np.float64,0x800faf3e69df5e7d,0x800faf3e69df5e7d,2 +np.float64,0x3fc145a330228b46,0x3fc13853f11b1c90,2 +np.float64,0xbfe25ec5abe4bd8c,0xbfe17c9e9b486f07,2 +np.float64,0x3fe119b160e23363,0x3fe0604b10178966,2 +np.float64,0x7fe0cbf2836197e4,0x40862ea6831e5f4a,2 +np.float64,0x3fe75dd3b4eebba8,0x3fe5abe80fd628fb,2 +np.float64,0x3f7c391000387220,0x3f7c39015d8f3a36,2 +np.float64,0x899d9cad133b4,0x899d9cad133b4,2 +np.float64,0x3fe5f0e34febe1c6,0x3fe4820cefe138fc,2 +np.float64,0x7fe060dfdba0c1bf,0x40862e72de8afcd0,2 +np.float64,0xbfae42f7103c85f0,0xbfae3e7630819c60,2 +np.float64,0x35f1f2c06be5,0x35f1f2c06be5,2 +np.float64,0xffc5194d362a329c,0xc086256266c8b7ad,2 +np.float64,0xbfda034f1b34069e,0xbfd95860a44c43ad,2 +np.float64,0x32bcebca6579e,0x32bcebca6579e,2 +np.float64,0xbfd1751ebca2ea3e,0xbfd13f79f45bf75c,2 +np.float64,0x3fee4fa1e5bc9f44,0x3feafe69e0d6c1c7,2 +np.float64,0x7f9c03cd5038079a,0x4086170459172900,2 +np.float64,0x7fc5fb6d6d2bf6da,0x408625b6651cfc73,2 +np.float64,0x7ff8000000000000,0x7ff8000000000000,2 +np.float64,0xffd1a8162ca3502c,0xc0862981333931ad,2 +np.float64,0x7fc415c198282b82,0x408624fd8c155d1b,2 +np.float64,0xffda37fbe7b46ff8,0xc0862caae7865c43,2 +np.float64,0xbfef4312257e8624,0xbfebadd89f3ee31c,2 +np.float64,0xbfec45e1fd788bc4,0xbfe97d8b14db6274,2 +np.float64,0xbfe6fdcfd26dfba0,0xbfe55e25b770d00a,2 +np.float64,0x7feb66d424f6cda7,0x40863290d9ff7ea2,2 +np.float64,0x8b08a29916115,0x8b08a29916115,2 +np.float64,0xffe12ca25c625944,0xc0862ed40d769f72,2 +np.float64,0x7ff4000000000000,0x7ffc000000000000,2 +np.float64,0x804925e100925,0x804925e100925,2 +np.float64,0xcebf3e019d9,0xcebf3e019d9,2 +np.float64,0xbfd5d75d4aabaeba,0xbfd57027671dedf7,2 +np.float64,0x800b829ecd37053e,0x800b829ecd37053e,2 +np.float64,0x800b1205daf6240c,0x800b1205daf6240c,2 +np.float64,0x3fdf7e9889befd31,0x3fde583fdff406c3,2 +np.float64,0x7ff0000000000000,0x7ff0000000000000,2 +np.float64,0x3fdc09760d3812ec,0x3fdb35b55c8090c6,2 +np.float64,0x800c4d99e4f89b34,0x800c4d99e4f89b34,2 +np.float64,0xffbaa6772e354cf0,0xc08621b535badb2f,2 +np.float64,0xbfc91188fd322310,0xbfc8e933b5d25ea7,2 +np.float64,0xffc1b947f4237290,0xc08623fd69164251,2 +np.float64,0x3fc6ab3b252d5678,0x3fc68d50bbac106d,2 +np.float64,0xffac8eb968391d70,0xc0861cb734833355,2 +np.float64,0xffe29a35c365346b,0xc0862f77a1aed6d8,2 +np.float64,0x3fde14b9543c2973,0x3fdd122697779015,2 +np.float64,0xbf10f5400021e000,0xbf10f53fffef1383,2 +np.float64,0xffe0831aa3e10635,0xc0862e838553d0ca,2 +np.float64,0x3fccbadbcf3975b8,0x3fcc7e768d0154ec,2 +np.float64,0x3fe092ef66e125df,0x3fdfd212a7116c9b,2 +np.float64,0xbfd727f039ae4fe0,0xbfd6adad040b2334,2 +np.float64,0xbfe4223b93a84477,0xbfe2ff7587364db4,2 +np.float64,0x3f4e5c3a003cb874,0x3f4e5c39b75c70f7,2 +np.float64,0x800e76b1a87ced63,0x800e76b1a87ced63,2 +np.float64,0x3fed2b7368fa56e7,0x3fea2863b9131b8c,2 +np.float64,0xffadb76ec43b6ee0,0xc0861d08ae79f20c,2 +np.float64,0x800b6a0cd1f6d41a,0x800b6a0cd1f6d41a,2 +np.float64,0xffee6aa943fcd552,0xc0863366a24250d5,2 +np.float64,0xbfe68cbc4e6d1978,0xbfe502040591aa5b,2 +np.float64,0xff859a38002b3480,0xc0860f64726235cc,2 +np.float64,0x3474d13e68e9b,0x3474d13e68e9b,2 +np.float64,0xffc11d49f6223a94,0xc08623b5c2df9712,2 +np.float64,0x800d82d019bb05a0,0x800d82d019bb05a0,2 +np.float64,0xbfe2af0192255e03,0xbfe1c20e38106388,2 +np.float64,0x3fe97d13c032fa28,0x3fe75bba11a65f86,2 +np.float64,0x7fcd457e133a8afb,0x40862800e80f5863,2 +np.float64,0x9d7254cf3ae4b,0x9d7254cf3ae4b,2 +np.float64,0x8003047675a608ee,0x8003047675a608ee,2 +np.float64,0x3fead6cd7d75ad9a,0x3fe8676138e5ff93,2 +np.float64,0x3fea6ee3b0f4ddc7,0x3fe817838a2bcbe3,2 +np.float64,0x3feed0edea7da1dc,0x3feb5bea3cb12fe2,2 +np.float64,0x88003fe510008,0x88003fe510008,2 +np.float64,0x3fe64cadc56c995c,0x3fe4cd8ead87fc79,2 +np.float64,0xaae30c5955c62,0xaae30c5955c62,2 +np.float64,0x7fc8c97cae3192f8,0x408626ac579f4fc5,2 +np.float64,0xbfc2bc0e8b25781c,0xbfc2ab188fdab7dc,2 +np.float64,0xc8f8e5e791f1d,0xc8f8e5e791f1d,2 +np.float64,0x3fecfaa5d6f9f54c,0x3fea0444dabe5a15,2 +np.float64,0xbfeb93740ff726e8,0xbfe8f71a9ab13baf,2 +np.float64,0xffd951236c32a246,0xc0862c633a4661eb,2 +np.float64,0x3fddbc5fcd3b78c0,0x3fdcc21c1a0a9246,2 +np.float64,0xbfd242443da48488,0xbfd20512d91f7924,2 +np.float64,0x2a3689b2546d2,0x2a3689b2546d2,2 +np.float64,0xffe24c67382498ce,0xc0862f55e4ea6283,2 +np.float64,0x800cbfce22197f9c,0x800cbfce22197f9c,2 +np.float64,0x8002269428044d29,0x8002269428044d29,2 +np.float64,0x7fd44babbd289756,0x40862a9e79b51c3b,2 +np.float64,0x3feea056a27d40ad,0x3feb38dcddb682f0,2 +np.float64,0xffeca8174b39502e,0xc08632ec8f88a5b2,2 +np.float64,0x7fbe0853a03c10a6,0x408622a9e8d53a9e,2 +np.float64,0xbfa9704b2432e090,0xbfa96d9dfc8c0cc2,2 +np.float64,0x800bda28fab7b452,0x800bda28fab7b452,2 +np.float64,0xbfb0ffa2f621ff48,0xbfb0fc71f405e82a,2 +np.float64,0xbfe66c04216cd808,0xbfe4e73ea3b58cf6,2 +np.float64,0x3fe336ea5d266dd5,0x3fe236ffcf078c62,2 +np.float64,0xbfe7729ae6aee536,0xbfe5bcad4b8ac62d,2 +np.float64,0x558cfc96ab1a0,0x558cfc96ab1a0,2 +np.float64,0xbfe7d792aaefaf26,0xbfe60de1b8f0279d,2 +np.float64,0xffd19ef6bda33dee,0xc086297d0ffee3c7,2 +np.float64,0x666b3ab4ccd68,0x666b3ab4ccd68,2 +np.float64,0xffa3d89e3c27b140,0xc08619cdeb2c1e49,2 +np.float64,0xbfb1728f7f62f,0xbfb1728f7f62f,2 +np.float64,0x3fc76319f32ec634,0x3fc74247bd005e20,2 +np.float64,0xbfbf1caee23e3960,0xbfbf0934c13d70e2,2 +np.float64,0x7fe79626f32f2c4d,0x4086315dcc68a5cb,2 +np.float64,0xffee78c4603cf188,0xc086336a572c05c2,2 +np.float64,0x3fce546eda3ca8de,0x3fce0d8d737fd31d,2 +np.float64,0xa223644d4446d,0xa223644d4446d,2 +np.float64,0x3fecea878b79d510,0x3fe9f850d50973f6,2 +np.float64,0x3fc20e0ea1241c1d,0x3fc1fedda87c5e75,2 +np.float64,0xffd1c5a99ca38b54,0xc086298e8e94cd47,2 +np.float64,0x7feb2c299d765852,0x4086327fa6db2808,2 +np.float64,0xcaf9d09595f3a,0xcaf9d09595f3a,2 +np.float64,0xbfe293bf21e5277e,0xbfe1aa7f6ac274ef,2 +np.float64,0xbfbaa3c8ce354790,0xbfba97891df19c01,2 +np.float64,0x3faf5784543eaf09,0x3faf5283acc7d71d,2 +np.float64,0x7fc014f8f62029f1,0x40862336531c662d,2 +np.float64,0xbfe0d9ac2d61b358,0xbfe027bce36699ca,2 +np.float64,0x8003e112ff27c227,0x8003e112ff27c227,2 +np.float64,0xffec0d4151381a82,0xc08632c0df718dd0,2 +np.float64,0x7fa2156fb0242ade,0x4086190f7587d708,2 +np.float64,0xd698358dad307,0xd698358dad307,2 +np.float64,0xbfed8d1b0efb1a36,0xbfea70588ef9ba18,2 +np.float64,0xbfd2cae6a92595ce,0xbfd28851e2185dee,2 +np.float64,0xffe7a36764ef46ce,0xc086316249c9287a,2 +np.float64,0xbfdb8ad8e5b715b2,0xbfdac19213c14315,2 +np.float64,0x3b5dba6076bc,0x3b5dba6076bc,2 +np.float64,0x800e6e8347bcdd07,0x800e6e8347bcdd07,2 +np.float64,0x800bea9f3fb7d53f,0x800bea9f3fb7d53f,2 +np.float64,0x7fb6d0e5fc2da1cb,0x4086207714c4ab85,2 +np.float64,0x0,0x0,2 +np.float64,0xbfe2aa1e1465543c,0xbfe1bdd550ef2966,2 +np.float64,0x7fd3f6a47fa7ed48,0x40862a7caea33055,2 +np.float64,0x800094e292c129c6,0x800094e292c129c6,2 +np.float64,0x800e1500ecbc2a02,0x800e1500ecbc2a02,2 +np.float64,0xbfd8ff6f97b1fee0,0xbfd866f84346ecdc,2 +np.float64,0x681457d0d028c,0x681457d0d028c,2 +np.float64,0x3feed0b5987da16b,0x3feb5bc1ab424984,2 +np.float64,0x3fdbcb34cdb79668,0x3fdafca540f32c06,2 +np.float64,0xbfdc9eacdcb93d5a,0xbfdbbe274aa8aeb0,2 +np.float64,0xffe6e35d526dc6ba,0xc08631203df38ed2,2 +np.float64,0x3fcac1cc65358398,0x3fca90de41889613,2 +np.float64,0xbfebf07a55b7e0f5,0xbfe93d6007db0c67,2 +np.float64,0xbfd7a7b1e7af4f64,0xbfd725a9081c22cb,2 +np.float64,0x800232bd7de4657c,0x800232bd7de4657c,2 +np.float64,0x7fb1dae43c23b5c7,0x40861e80f5c0a64e,2 +np.float64,0x8013ded70027c,0x8013ded70027c,2 +np.float64,0x7fc4373a59286e74,0x4086250ad60575d0,2 +np.float64,0xbfe9980fd6733020,0xbfe770d1352d0ed3,2 +np.float64,0x8008a66b8dd14cd7,0x8008a66b8dd14cd7,2 +np.float64,0xbfaebc67f83d78d0,0xbfaeb7b015848478,2 +np.float64,0xffd0c52762218a4e,0xc0862917b564afc6,2 +np.float64,0xbfd503860aaa070c,0xbfd4a74618441561,2 +np.float64,0x5bdacabcb7b5a,0x5bdacabcb7b5a,2 +np.float64,0xf3623cffe6c48,0xf3623cffe6c48,2 +np.float64,0x7fe16c6c7ea2d8d8,0x40862ef18d90201f,2 +np.float64,0x3ff0000000000000,0x3fec34366179d427,2 +np.float64,0x7fe19cbc84233978,0x40862f079dcbc169,2 +np.float64,0x3fcfd3d6933fa7ad,0x3fcf822187907f6b,2 +np.float64,0x8007d65d672facbc,0x8007d65d672facbc,2 +np.float64,0xffca6115aa34c22c,0xc086272bd7728750,2 +np.float64,0xbfe77ab1556ef562,0xbfe5c332fb55b66e,2 +np.float64,0x8001ed797c23daf4,0x8001ed797c23daf4,2 +np.float64,0x7fdd3d16cb3a7a2d,0x40862d8a2c869281,2 +np.float64,0x75f36beaebe6e,0x75f36beaebe6e,2 +np.float64,0xffda3c2798b47850,0xc0862cac2d3435df,2 +np.float64,0xbfa37cc3c426f980,0xbfa37b8f9d3ec4b7,2 +np.float64,0x80030ea8bd061d52,0x80030ea8bd061d52,2 +np.float64,0xffe41f7617683eec,0xc08630188a3e135e,2 +np.float64,0x800e40590dfc80b2,0x800e40590dfc80b2,2 +np.float64,0x3fea950d80f52a1c,0x3fe834e74481e66f,2 +np.float64,0xffec95e39a792bc6,0xc08632e779150084,2 +np.float64,0xbfd54310ecaa8622,0xbfd4e39c4d767002,2 +np.float64,0xffd40c9971a81932,0xc0862a85764eb2f4,2 +np.float64,0xb0a2230761445,0xb0a2230761445,2 +np.float64,0x80092973661252e7,0x80092973661252e7,2 +np.float64,0x7fb13b030a227605,0x40861e380aeb5549,2 +np.float64,0x3fbd5d8db23abb1b,0x3fbd4d2a0b94af36,2 +np.float64,0xbfd6cb8567ad970a,0xbfd656b19ab8fa61,2 +np.float64,0xbfe7c0fd346f81fa,0xbfe5fbc28807c794,2 +np.float64,0xffd586579eab0cb0,0xc0862b16e65c0754,2 +np.float64,0x8000e52da461ca5c,0x8000e52da461ca5c,2 +np.float64,0x3fc69d17112d3a2e,0x3fc67f63fe1fea1c,2 +np.float64,0x3fd36ba892a6d750,0x3fd3225be1fa87af,2 +np.float64,0x7fe2850598e50a0a,0x40862f6e7fcd6c1a,2 +np.float64,0x80074a4dacce949c,0x80074a4dacce949c,2 +np.float64,0x3fe25eea4d64bdd5,0x3fe17cbe5fefbd4e,2 +np.float64,0xbfe250c08be4a181,0xbfe17074c520e5de,2 +np.float64,0x8000f5665481eacd,0x8000f5665481eacd,2 +np.float64,0x7fdb3172f83662e5,0x40862cf5a46764f1,2 +np.float64,0x7fd8ed82d631db05,0x40862c4380658afa,2 +np.float64,0xffec5163feb8a2c7,0xc08632d4366aab06,2 +np.float64,0x800ff14ac6ffe296,0x800ff14ac6ffe296,2 +np.float64,0xbfc7cc7aea2f98f4,0xbfc7a9e9cb38f023,2 +np.float64,0xbfd50cdfc32a19c0,0xbfd4b0282b452fb2,2 +np.float64,0xbfec256d75b84adb,0xbfe965328c1860b2,2 +np.float64,0xffe860c4cdb0c189,0xc08631a164b7059a,2 +np.float64,0xbfe23de164247bc3,0xbfe16011bffa4651,2 +np.float64,0xcc96b39d992d7,0xcc96b39d992d7,2 +np.float64,0xbfec43acf938875a,0xbfe97be3a13b50c3,2 +np.float64,0xc4f587bb89eb1,0xc4f587bb89eb1,2 +np.float64,0xbfcd971d9a3b2e3c,0xbfcd5537ad15dab4,2 +np.float64,0xffcaf00d8035e01c,0xc0862756bf2cdf8f,2 +np.float64,0x8008c26f93f184e0,0x8008c26f93f184e0,2 +np.float64,0xfff0000000000000,0xfff0000000000000,2 +np.float64,0xbfd13552c3a26aa6,0xbfd101e5e252eb7b,2 +np.float64,0x7fe497235e292e46,0x4086304792fb423a,2 +np.float64,0x7fd6dc0192adb802,0x40862b921a5e935d,2 +np.float64,0xf16d49a1e2da9,0xf16d49a1e2da9,2 +np.float64,0xffef6b1b71bed636,0xc08633a8feed0178,2 +np.float64,0x7fe15ec62f62bd8b,0x40862eeb46b193dc,2 +np.float64,0x3fef4369ec7e86d4,0x3febae1768be52cc,2 +np.float64,0x4f84e8e89f09e,0x4f84e8e89f09e,2 +np.float64,0xbfe19e71ade33ce4,0xbfe0d4fad05e0ebc,2 +np.float64,0xbfe7e1df1defc3be,0xbfe616233e15b3d0,2 +np.float64,0x7fe9349afdb26935,0x408631e5c1c5c6cd,2 +np.float64,0xff90c35ac82186c0,0xc08612e896a06467,2 +np.float64,0xbfe88bf8807117f1,0xbfe69dc786464422,2 +np.float64,0x3feaf9ff6475f3fe,0x3fe8825132410d18,2 +np.float64,0x9ff487a33fe91,0x9ff487a33fe91,2 +np.float64,0x7fedb30159bb6602,0x40863335c0419322,2 +np.float64,0x800bddf6ed77bbee,0x800bddf6ed77bbee,2 +np.float64,0x3fd919df133233be,0x3fd87f963b9584ce,2 +np.float64,0x7fd64da3b52c9b46,0x40862b5fa9dd3b6d,2 +np.float64,0xbfce288db43c511c,0xbfcde2d953407ae8,2 +np.float64,0x3fe88bc72771178e,0x3fe69da05e9e9b4e,2 +np.float64,0x800feafe259fd5fc,0x800feafe259fd5fc,2 +np.float64,0x3febbbff4a7777ff,0x3fe915c78f6a280f,2 +np.float64,0xbfefbde4417f7bc9,0xbfec055f4fb2cd21,2 +np.float64,0xf13ca103e2794,0xf13ca103e2794,2 +np.float64,0x3fe6423884ec8471,0x3fe4c4f97eaa876a,2 +np.float64,0x800ca01c8cb94039,0x800ca01c8cb94039,2 +np.float64,0x3fbc5073f638a0e0,0x3fbc41c163ac0001,2 +np.float64,0xbfda0d83cfb41b08,0xbfd961d4cacc82cf,2 +np.float64,0x800f37b8f17e6f72,0x800f37b8f17e6f72,2 +np.float64,0x7fe0b08cd7216119,0x40862e996becb771,2 +np.float64,0xffd4222a40a84454,0xc0862a8e0c984917,2 +np.float64,0x7feb3df98ff67bf2,0x40863284e3a86ee6,2 +np.float64,0x8001d5d291e3aba6,0x8001d5d291e3aba6,2 +np.float64,0xbfd3c21629a7842c,0xbfd3750095a5894a,2 +np.float64,0xbfd069eb48a0d3d6,0xbfd03d2b1c2ae9db,2 +np.float64,0xffeb1be2973637c4,0xc086327ada954662,2 +np.float64,0x3fc659f97e2cb3f3,0x3fc63d497a451f10,2 +np.float64,0xbfeb624bc776c498,0xbfe8d1cf7c0626ca,2 +np.float64,0xffeedf26e23dbe4d,0xc08633850baab425,2 +np.float64,0xffe70da48a6e1b48,0xc086312ef75d5036,2 +np.float64,0x2b4f4830569ea,0x2b4f4830569ea,2 +np.float64,0xffe82e7fcfb05cff,0xc0863190d4771f75,2 +np.float64,0x3fcc2c1fd5385840,0x3fcbf3211ddc5123,2 +np.float64,0x7fe22ced5a6459da,0x40862f481629ee6a,2 +np.float64,0x7fe13d2895e27a50,0x40862edbbc411899,2 +np.float64,0x3fd54c4280aa9884,0x3fd4ec55a946c5d7,2 +np.float64,0xffd75b8e01aeb71c,0xc0862bbe42d76e5e,2 +np.float64,0x7f1d5376fe3ab,0x7f1d5376fe3ab,2 +np.float64,0x3fe6ec6c902dd8d9,0x3fe55004f35192bd,2 +np.float64,0x5634504aac68b,0x5634504aac68b,2 +np.float64,0x3feedb0d83bdb61b,0x3feb633467467ce6,2 +np.float64,0x3fddb1c0dcbb6380,0x3fdcb87a02daf1fa,2 +np.float64,0xbfa832da443065b0,0xbfa8308c70257209,2 +np.float64,0x87a9836b0f531,0x87a9836b0f531,2 diff --git a/numpy/core/tests/data/umath-validation-set-arctan.csv b/numpy/core/tests/data/umath-validation-set-arctan.csv new file mode 100644 index 000000000000..1e92073d375c --- /dev/null +++ b/numpy/core/tests/data/umath-validation-set-arctan.csv @@ -0,0 +1,1429 @@ +dtype,input,output,ulperrortol +np.float32,0x3f338252,0x3f1c8d9c,3 +np.float32,0x7e569df2,0x3fc90fdb,3 +np.float32,0xbf347e25,0xbf1d361f,3 +np.float32,0xbf0a654e,0xbefdbfd2,3 +np.float32,0x8070968e,0x8070968e,3 +np.float32,0x803cfb27,0x803cfb27,3 +np.float32,0x8024362e,0x8024362e,3 +np.float32,0xfd55dca0,0xbfc90fdb,3 +np.float32,0x592b82,0x592b82,3 +np.float32,0x802eb8e1,0x802eb8e1,3 +np.float32,0xbc5fef40,0xbc5febae,3 +np.float32,0x3f1f6ce8,0x3f0e967c,3 +np.float32,0x20bedc,0x20bedc,3 +np.float32,0xbf058860,0xbef629c7,3 +np.float32,0x311504,0x311504,3 +np.float32,0xbd23f560,0xbd23defa,3 +np.float32,0x800ff4e8,0x800ff4e8,3 +np.float32,0x355009,0x355009,3 +np.float32,0x3f7be42e,0x3f46fdb3,3 +np.float32,0xbf225f7c,0xbf10b364,3 +np.float32,0x8074fa9e,0x8074fa9e,3 +np.float32,0xbea4b418,0xbe9f59ce,3 +np.float32,0xbe909c14,0xbe8cf045,3 +np.float32,0x80026bee,0x80026bee,3 +np.float32,0x3d789c20,0x3d784e25,3 +np.float32,0x7f56a4ba,0x3fc90fdb,3 +np.float32,0xbf70d141,0xbf413db7,3 +np.float32,0xbf2c4886,0xbf17a505,3 +np.float32,0x7e2993bf,0x3fc90fdb,3 +np.float32,0xbe2c8a30,0xbe2aef28,3 +np.float32,0x803f82d9,0x803f82d9,3 +np.float32,0x3f062fbc,0x3ef730a1,3 +np.float32,0x3f349ee0,0x3f1d4bfa,3 +np.float32,0x3eccfb69,0x3ec2f9e8,3 +np.float32,0x7e8a85dd,0x3fc90fdb,3 +np.float32,0x25331,0x25331,3 +np.float32,0x464f19,0x464f19,3 +np.float32,0x8035c818,0x8035c818,3 +np.float32,0x802e5799,0x802e5799,3 +np.float32,0x64e1c0,0x64e1c0,3 +np.float32,0x701cc2,0x701cc2,3 +np.float32,0x265c57,0x265c57,3 +np.float32,0x807a053f,0x807a053f,3 +np.float32,0x3bd2c412,0x3bd2c354,3 +np.float32,0xff28f1c8,0xbfc90fdb,3 +np.float32,0x7f08f08b,0x3fc90fdb,3 +np.float32,0x800c50e4,0x800c50e4,3 +np.float32,0x369674,0x369674,3 +np.float32,0xbf5b7db3,0xbf3571bf,3 +np.float32,0x7edcf5e2,0x3fc90fdb,3 +np.float32,0x800e5d4b,0x800e5d4b,3 +np.float32,0x80722554,0x80722554,3 +np.float32,0x693f33,0x693f33,3 +np.float32,0x800844e4,0x800844e4,3 +np.float32,0xbf111b82,0xbf0402ec,3 +np.float32,0x7df9c9ac,0x3fc90fdb,3 +np.float32,0xbf6619a6,0xbf3b6f57,3 +np.float32,0x8002fafe,0x8002fafe,3 +np.float32,0xfe1e67f8,0xbfc90fdb,3 +np.float32,0x3f7f4bf8,0x3f48b5b7,3 +np.float32,0x7f017b20,0x3fc90fdb,3 +np.float32,0x2d9b07,0x2d9b07,3 +np.float32,0x803aa174,0x803aa174,3 +np.float32,0x7d530336,0x3fc90fdb,3 +np.float32,0x80662195,0x80662195,3 +np.float32,0xfd5ebcf0,0xbfc90fdb,3 +np.float32,0xbe7b8dcc,0xbe76ab59,3 +np.float32,0x7f2bacaf,0x3fc90fdb,3 +np.float32,0x3f194fc4,0x3f0a229e,3 +np.float32,0x7ee21cdf,0x3fc90fdb,3 +np.float32,0x3f5a17fc,0x3f34a307,3 +np.float32,0x7f100c58,0x3fc90fdb,3 +np.float32,0x7e9128f5,0x3fc90fdb,3 +np.float32,0xbf2107c6,0xbf0fbdb4,3 +np.float32,0xbd29c800,0xbd29af22,3 +np.float32,0xbf5af499,0xbf3522a6,3 +np.float32,0x801bde44,0x801bde44,3 +np.float32,0xfeb4761a,0xbfc90fdb,3 +np.float32,0x3d88aa1b,0x3d887650,3 +np.float32,0x7eba5e0b,0x3fc90fdb,3 +np.float32,0x803906bd,0x803906bd,3 +np.float32,0x80101512,0x80101512,3 +np.float32,0x7e898f83,0x3fc90fdb,3 +np.float32,0x806406d3,0x806406d3,3 +np.float32,0x7ed20fc0,0x3fc90fdb,3 +np.float32,0x20827d,0x20827d,3 +np.float32,0x3f361359,0x3f1e43fe,3 +np.float32,0xfe4ef8d8,0xbfc90fdb,3 +np.float32,0x805e7d2d,0x805e7d2d,3 +np.float32,0xbe4316b0,0xbe40c745,3 +np.float32,0xbf0a1c06,0xbefd4e5a,3 +np.float32,0x3e202860,0x3e1edee1,3 +np.float32,0xbeb32a2c,0xbeac5899,3 +np.float32,0xfe528838,0xbfc90fdb,3 +np.float32,0x2f73e2,0x2f73e2,3 +np.float32,0xbe16e010,0xbe15cc27,3 +np.float32,0x3f50d6c5,0x3f2f2d75,3 +np.float32,0xbe88a6a2,0xbe8589c7,3 +np.float32,0x3ee36060,0x3ed5fb36,3 +np.float32,0x6c978b,0x6c978b,3 +np.float32,0x7f1b735f,0x3fc90fdb,3 +np.float32,0x3dad8256,0x3dad1885,3 +np.float32,0x807f5094,0x807f5094,3 +np.float32,0x65c358,0x65c358,3 +np.float32,0xff315ce4,0xbfc90fdb,3 +np.float32,0x7411a6,0x7411a6,3 +np.float32,0x80757b04,0x80757b04,3 +np.float32,0x3eec73a6,0x3edd82f4,3 +np.float32,0xfe9f69e8,0xbfc90fdb,3 +np.float32,0x801f4fa8,0x801f4fa8,3 +np.float32,0xbf6f2fae,0xbf405f79,3 +np.float32,0xfea206b6,0xbfc90fdb,3 +np.float32,0x3f257301,0x3f12e1ee,3 +np.float32,0x7ea6a506,0x3fc90fdb,3 +np.float32,0x80800000,0x80800000,3 +np.float32,0xff735c2d,0xbfc90fdb,3 +np.float32,0x80197f95,0x80197f95,3 +np.float32,0x7f4a354f,0x3fc90fdb,3 +np.float32,0xff320c00,0xbfc90fdb,3 +np.float32,0x3f2659de,0x3f138484,3 +np.float32,0xbe5451bc,0xbe515a52,3 +np.float32,0x3f6e228c,0x3f3fcf7c,3 +np.float32,0x66855a,0x66855a,3 +np.float32,0x8034b3a3,0x8034b3a3,3 +np.float32,0xbe21a2fc,0xbe20505d,3 +np.float32,0x7f79e2dc,0x3fc90fdb,3 +np.float32,0xbe19a8e0,0xbe18858c,3 +np.float32,0x10802c,0x10802c,3 +np.float32,0xfeee579e,0xbfc90fdb,3 +np.float32,0x3f3292c8,0x3f1becc0,3 +np.float32,0xbf595a71,0xbf34350a,3 +np.float32,0xbf7c3373,0xbf4725f4,3 +np.float32,0xbdd30938,0xbdd24b36,3 +np.float32,0x153a17,0x153a17,3 +np.float32,0x807282a0,0x807282a0,3 +np.float32,0xfe817322,0xbfc90fdb,3 +np.float32,0x3f1b3628,0x3f0b8771,3 +np.float32,0x41be8f,0x41be8f,3 +np.float32,0x7f4a8343,0x3fc90fdb,3 +np.float32,0x3dc4ea2b,0x3dc44fae,3 +np.float32,0x802aac25,0x802aac25,3 +np.float32,0xbf20e1d7,0xbf0fa284,3 +np.float32,0xfd91a1b0,0xbfc90fdb,3 +np.float32,0x3f0d5476,0x3f012265,3 +np.float32,0x21c916,0x21c916,3 +np.float32,0x807df399,0x807df399,3 +np.float32,0x7e207b4c,0x3fc90fdb,3 +np.float32,0x8055f8ff,0x8055f8ff,3 +np.float32,0x7edf3b01,0x3fc90fdb,3 +np.float32,0x803a8df3,0x803a8df3,3 +np.float32,0x3ce3b002,0x3ce3a101,3 +np.float32,0x3f62dd54,0x3f39a248,3 +np.float32,0xff33ae10,0xbfc90fdb,3 +np.float32,0x7e3de69d,0x3fc90fdb,3 +np.float32,0x8024581e,0x8024581e,3 +np.float32,0xbf4ac99d,0xbf2b807a,3 +np.float32,0x3f157d19,0x3f074d8c,3 +np.float32,0xfed383f4,0xbfc90fdb,3 +np.float32,0xbf5a39fa,0xbf34b6b8,3 +np.float32,0x800d757d,0x800d757d,3 +np.float32,0x807d606b,0x807d606b,3 +np.float32,0x3e828f89,0x3e7fac2d,3 +np.float32,0x7a6604,0x7a6604,3 +np.float32,0x7dc7e72b,0x3fc90fdb,3 +np.float32,0x80144146,0x80144146,3 +np.float32,0x7c2eed69,0x3fc90fdb,3 +np.float32,0x3f5b4d8c,0x3f3555fc,3 +np.float32,0xfd8b7778,0xbfc90fdb,3 +np.float32,0xfc9d9140,0xbfc90fdb,3 +np.float32,0xbea265d4,0xbe9d4232,3 +np.float32,0xbe9344d0,0xbe8f65da,3 +np.float32,0x3f71f19a,0x3f41d65b,3 +np.float32,0x804a3f59,0x804a3f59,3 +np.float32,0x3e596290,0x3e563476,3 +np.float32,0x3e994ee4,0x3e94f546,3 +np.float32,0xbc103e00,0xbc103d0c,3 +np.float32,0xbf1cd896,0xbf0cb889,3 +np.float32,0x7f52b080,0x3fc90fdb,3 +np.float32,0xff584452,0xbfc90fdb,3 +np.float32,0x58b26b,0x58b26b,3 +np.float32,0x3f23cd4c,0x3f11b799,3 +np.float32,0x707d7,0x707d7,3 +np.float32,0xff732cff,0xbfc90fdb,3 +np.float32,0x3e41c2a6,0x3e3f7f0f,3 +np.float32,0xbf7058e9,0xbf40fdcf,3 +np.float32,0x7dca9857,0x3fc90fdb,3 +np.float32,0x7f0eb44b,0x3fc90fdb,3 +np.float32,0x8000405c,0x8000405c,3 +np.float32,0x4916ab,0x4916ab,3 +np.float32,0x4811a8,0x4811a8,3 +np.float32,0x3d69bf,0x3d69bf,3 +np.float32,0xfeadcf1e,0xbfc90fdb,3 +np.float32,0x3e08dbbf,0x3e080d58,3 +np.float32,0xff031f88,0xbfc90fdb,3 +np.float32,0xbe09cab8,0xbe08f818,3 +np.float32,0x21d7cd,0x21d7cd,3 +np.float32,0x3f23230d,0x3f113ea9,3 +np.float32,0x7e8a48d4,0x3fc90fdb,3 +np.float32,0x413869,0x413869,3 +np.float32,0x7e832990,0x3fc90fdb,3 +np.float32,0x800f5c09,0x800f5c09,3 +np.float32,0x7f5893b6,0x3fc90fdb,3 +np.float32,0x7f06b5b1,0x3fc90fdb,3 +np.float32,0xbe1cbee8,0xbe1b89d6,3 +np.float32,0xbf279f14,0xbf1468a8,3 +np.float32,0xfea86060,0xbfc90fdb,3 +np.float32,0x3e828174,0x3e7f91bb,3 +np.float32,0xff682c82,0xbfc90fdb,3 +np.float32,0x4e20f3,0x4e20f3,3 +np.float32,0x7f17d7e9,0x3fc90fdb,3 +np.float32,0x80671f92,0x80671f92,3 +np.float32,0x7f6dd100,0x3fc90fdb,3 +np.float32,0x3f219a4d,0x3f102695,3 +np.float32,0x803c9808,0x803c9808,3 +np.float32,0x3c432ada,0x3c43287d,3 +np.float32,0xbd3db450,0xbd3d91a2,3 +np.float32,0x3baac135,0x3baac0d0,3 +np.float32,0xff7fffe1,0xbfc90fdb,3 +np.float32,0xfe38a6f4,0xbfc90fdb,3 +np.float32,0x3dfb0a04,0x3df9cb04,3 +np.float32,0x800b05c2,0x800b05c2,3 +np.float32,0x644163,0x644163,3 +np.float32,0xff03a025,0xbfc90fdb,3 +np.float32,0x3f7d506c,0x3f47b641,3 +np.float32,0xff0e682a,0xbfc90fdb,3 +np.float32,0x3e09b7b0,0x3e08e567,3 +np.float32,0x7f72a216,0x3fc90fdb,3 +np.float32,0x7f800000,0x3fc90fdb,3 +np.float32,0x8050a281,0x8050a281,3 +np.float32,0x7edafa2f,0x3fc90fdb,3 +np.float32,0x3f4e0df6,0x3f2d7f2f,3 +np.float32,0xbf6728e0,0xbf3c050f,3 +np.float32,0x3e904ce4,0x3e8ca6eb,3 +np.float32,0x0,0x0,3 +np.float32,0xfd215070,0xbfc90fdb,3 +np.float32,0x7e406b15,0x3fc90fdb,3 +np.float32,0xbf2803c9,0xbf14af18,3 +np.float32,0x5950c8,0x5950c8,3 +np.float32,0xbeddcec8,0xbed14faa,3 +np.float32,0xbec6457e,0xbebd2aa5,3 +np.float32,0xbf42843c,0xbf2656db,3 +np.float32,0x3ee9cba8,0x3edb5163,3 +np.float32,0xbe30c954,0xbe2f0f90,3 +np.float32,0xbeee6b44,0xbedf216f,3 +np.float32,0xbe35d818,0xbe33f7cd,3 +np.float32,0xbe47c630,0xbe454bc6,3 +np.float32,0x801b146f,0x801b146f,3 +np.float32,0x7f6788da,0x3fc90fdb,3 +np.float32,0x3eaef088,0x3ea8927d,3 +np.float32,0x3eb5983e,0x3eae81fc,3 +np.float32,0x40b51d,0x40b51d,3 +np.float32,0xfebddd04,0xbfc90fdb,3 +np.float32,0x3e591aee,0x3e55efea,3 +np.float32,0xbe2b6b48,0xbe29d81f,3 +np.float32,0xff4a8826,0xbfc90fdb,3 +np.float32,0x3e791df0,0x3e745eac,3 +np.float32,0x7c8f681f,0x3fc90fdb,3 +np.float32,0xfe7a15c4,0xbfc90fdb,3 +np.float32,0x3c8963,0x3c8963,3 +np.float32,0x3f0afa0a,0x3efea5cc,3 +np.float32,0xbf0d2680,0xbf00ff29,3 +np.float32,0x3dc306b0,0x3dc27096,3 +np.float32,0x7f4cf105,0x3fc90fdb,3 +np.float32,0xbe196060,0xbe183ea4,3 +np.float32,0x5caf1c,0x5caf1c,3 +np.float32,0x801f2852,0x801f2852,3 +np.float32,0xbe01aa0c,0xbe00fa53,3 +np.float32,0x3f0cfd32,0x3f00df7a,3 +np.float32,0x7d82038e,0x3fc90fdb,3 +np.float32,0x7f7b927f,0x3fc90fdb,3 +np.float32,0xbe93b2e4,0xbe8fcb7f,3 +np.float32,0x1ffe8c,0x1ffe8c,3 +np.float32,0x3faaf6,0x3faaf6,3 +np.float32,0x3e32b1b8,0x3e30e9ab,3 +np.float32,0x802953c0,0x802953c0,3 +np.float32,0xfe5d9844,0xbfc90fdb,3 +np.float32,0x3e1a59d0,0x3e193292,3 +np.float32,0x801c6edc,0x801c6edc,3 +np.float32,0x1ecf41,0x1ecf41,3 +np.float32,0xfe56b09c,0xbfc90fdb,3 +np.float32,0x7e878351,0x3fc90fdb,3 +np.float32,0x3f401e2c,0x3f24cfcb,3 +np.float32,0xbf204a40,0xbf0f35bb,3 +np.float32,0x3e155a98,0x3e144ee1,3 +np.float32,0xbf34f929,0xbf1d8838,3 +np.float32,0x801bbf70,0x801bbf70,3 +np.float32,0x7e7c9730,0x3fc90fdb,3 +np.float32,0x7cc23432,0x3fc90fdb,3 +np.float32,0xbf351638,0xbf1d9b97,3 +np.float32,0x80152094,0x80152094,3 +np.float32,0x3f2d731c,0x3f187219,3 +np.float32,0x804ab0b7,0x804ab0b7,3 +np.float32,0x37d6db,0x37d6db,3 +np.float32,0xbf3ccc56,0xbf22acbf,3 +np.float32,0x3e546f8c,0x3e5176e7,3 +np.float32,0xbe90e87e,0xbe8d3707,3 +np.float32,0x48256c,0x48256c,3 +np.float32,0x7e2468d0,0x3fc90fdb,3 +np.float32,0x807af47e,0x807af47e,3 +np.float32,0x3ed4b221,0x3ec996f0,3 +np.float32,0x3d3b1956,0x3d3af811,3 +np.float32,0xbe69d93c,0xbe65e7f0,3 +np.float32,0xff03ff14,0xbfc90fdb,3 +np.float32,0x801e79dc,0x801e79dc,3 +np.float32,0x3f467c53,0x3f28d63d,3 +np.float32,0x3eab6baa,0x3ea56a1c,3 +np.float32,0xbf15519c,0xbf072d1c,3 +np.float32,0x7f0bd8e8,0x3fc90fdb,3 +np.float32,0xbe1e0d1c,0xbe1cd053,3 +np.float32,0x8016edab,0x8016edab,3 +np.float32,0x7ecaa09b,0x3fc90fdb,3 +np.float32,0x3f72e6d9,0x3f4257a8,3 +np.float32,0xbefe787e,0xbeec29a4,3 +np.float32,0xbee989e8,0xbedb1af9,3 +np.float32,0xbe662db0,0xbe626a45,3 +np.float32,0x495bf7,0x495bf7,3 +np.float32,0x26c379,0x26c379,3 +np.float32,0x7f54d41a,0x3fc90fdb,3 +np.float32,0x801e7dd9,0x801e7dd9,3 +np.float32,0x80000000,0x80000000,3 +np.float32,0xfa3d3000,0xbfc90fdb,3 +np.float32,0xfa3cb800,0xbfc90fdb,3 +np.float32,0x264894,0x264894,3 +np.float32,0xff6de011,0xbfc90fdb,3 +np.float32,0x7e9045b2,0x3fc90fdb,3 +np.float32,0x3f2253a8,0x3f10aaf4,3 +np.float32,0xbd462bf0,0xbd460469,3 +np.float32,0x7f1796af,0x3fc90fdb,3 +np.float32,0x3e718858,0x3e6d3279,3 +np.float32,0xff437d7e,0xbfc90fdb,3 +np.float32,0x805ae7cb,0x805ae7cb,3 +np.float32,0x807e32e9,0x807e32e9,3 +np.float32,0x3ee0bafc,0x3ed3c453,3 +np.float32,0xbf721dee,0xbf41edc3,3 +np.float32,0xfec9f792,0xbfc90fdb,3 +np.float32,0x7f050720,0x3fc90fdb,3 +np.float32,0x182261,0x182261,3 +np.float32,0x3e39e678,0x3e37e5be,3 +np.float32,0x7e096e4b,0x3fc90fdb,3 +np.float32,0x103715,0x103715,3 +np.float32,0x3f7e7741,0x3f484ae4,3 +np.float32,0x3e29aea5,0x3e28277c,3 +np.float32,0x58c183,0x58c183,3 +np.float32,0xff72fdb2,0xbfc90fdb,3 +np.float32,0xbd9a9420,0xbd9a493c,3 +np.float32,0x7f1e07e7,0x3fc90fdb,3 +np.float32,0xff79f522,0xbfc90fdb,3 +np.float32,0x7c7d0e96,0x3fc90fdb,3 +np.float32,0xbeba9e8e,0xbeb2f504,3 +np.float32,0xfd880a80,0xbfc90fdb,3 +np.float32,0xff7f2a33,0xbfc90fdb,3 +np.float32,0x3e861ae0,0x3e83289c,3 +np.float32,0x7f0161c1,0x3fc90fdb,3 +np.float32,0xfe844ff8,0xbfc90fdb,3 +np.float32,0xbebf4b98,0xbeb7128e,3 +np.float32,0x652bee,0x652bee,3 +np.float32,0xff188a4b,0xbfc90fdb,3 +np.float32,0xbf800000,0xbf490fdb,3 +np.float32,0x80418711,0x80418711,3 +np.float32,0xbeb712d4,0xbeafd1f6,3 +np.float32,0xbf7cee28,0xbf478491,3 +np.float32,0xfe66c59c,0xbfc90fdb,3 +np.float32,0x4166a2,0x4166a2,3 +np.float32,0x3dfa1a2c,0x3df8deb5,3 +np.float32,0xbdbfbcb8,0xbdbf2e0f,3 +np.float32,0xfe60ef70,0xbfc90fdb,3 +np.float32,0xfe009444,0xbfc90fdb,3 +np.float32,0xfeb27aa0,0xbfc90fdb,3 +np.float32,0xbe99f7bc,0xbe95902b,3 +np.float32,0x8043d28d,0x8043d28d,3 +np.float32,0xfe5328c4,0xbfc90fdb,3 +np.float32,0x8017b27e,0x8017b27e,3 +np.float32,0x3ef1d2cf,0x3ee1ebd7,3 +np.float32,0x805ddd90,0x805ddd90,3 +np.float32,0xbf424263,0xbf262d17,3 +np.float32,0xfc99dde0,0xbfc90fdb,3 +np.float32,0xbf7ec13b,0xbf487015,3 +np.float32,0xbef727ea,0xbee64377,3 +np.float32,0xff15ce95,0xbfc90fdb,3 +np.float32,0x1fbba4,0x1fbba4,3 +np.float32,0x3f3b2368,0x3f2198a9,3 +np.float32,0xfefda26e,0xbfc90fdb,3 +np.float32,0x801519ad,0x801519ad,3 +np.float32,0x80473fa2,0x80473fa2,3 +np.float32,0x7e7a8bc1,0x3fc90fdb,3 +np.float32,0x3e8a9289,0x3e87548a,3 +np.float32,0x3ed68987,0x3ecb2872,3 +np.float32,0x805bca66,0x805bca66,3 +np.float32,0x8079c4e3,0x8079c4e3,3 +np.float32,0x3a2510,0x3a2510,3 +np.float32,0x7eedc598,0x3fc90fdb,3 +np.float32,0x80681956,0x80681956,3 +np.float32,0xff64c778,0xbfc90fdb,3 +np.float32,0x806bbc46,0x806bbc46,3 +np.float32,0x433643,0x433643,3 +np.float32,0x705b92,0x705b92,3 +np.float32,0xff359392,0xbfc90fdb,3 +np.float32,0xbee78672,0xbed96fa7,3 +np.float32,0x3e21717b,0x3e202010,3 +np.float32,0xfea13c34,0xbfc90fdb,3 +np.float32,0x2c8895,0x2c8895,3 +np.float32,0x3ed33290,0x3ec84f7c,3 +np.float32,0x3e63031e,0x3e5f662e,3 +np.float32,0x7e30907b,0x3fc90fdb,3 +np.float32,0xbe293708,0xbe27b310,3 +np.float32,0x3ed93738,0x3ecd6ea3,3 +np.float32,0x9db7e,0x9db7e,3 +np.float32,0x3f7cd1b8,0x3f47762c,3 +np.float32,0x3eb5143c,0x3eae0cb0,3 +np.float32,0xbe69b234,0xbe65c2d7,3 +np.float32,0x3f6e74de,0x3f3ffb97,3 +np.float32,0x5d0559,0x5d0559,3 +np.float32,0x3e1e8c30,0x3e1d4c70,3 +np.float32,0xbf2d1878,0xbf1833ef,3 +np.float32,0xff2adf82,0xbfc90fdb,3 +np.float32,0x8012e2c1,0x8012e2c1,3 +np.float32,0x7f031be3,0x3fc90fdb,3 +np.float32,0x805ff94e,0x805ff94e,3 +np.float32,0x3e9d5b27,0x3e98aa31,3 +np.float32,0x3f56d5cf,0x3f32bc9e,3 +np.float32,0x3eaa0412,0x3ea4267f,3 +np.float32,0xbe899ea4,0xbe86712f,3 +np.float32,0x800f2f48,0x800f2f48,3 +np.float32,0x3f1c2269,0x3f0c33ea,3 +np.float32,0x3f4a5f64,0x3f2b3f28,3 +np.float32,0x80739318,0x80739318,3 +np.float32,0x806e9b47,0x806e9b47,3 +np.float32,0x3c8cd300,0x3c8ccf73,3 +np.float32,0x7f39a39d,0x3fc90fdb,3 +np.float32,0x3ec95d61,0x3ebfd9dc,3 +np.float32,0xff351ff8,0xbfc90fdb,3 +np.float32,0xff3a8f58,0xbfc90fdb,3 +np.float32,0x7f313ec0,0x3fc90fdb,3 +np.float32,0x803aed13,0x803aed13,3 +np.float32,0x7f771d9b,0x3fc90fdb,3 +np.float32,0x8045a6d6,0x8045a6d6,3 +np.float32,0xbc85f280,0xbc85ef72,3 +np.float32,0x7e9c68f5,0x3fc90fdb,3 +np.float32,0xbf0f9379,0xbf02d975,3 +np.float32,0x7e97bcb1,0x3fc90fdb,3 +np.float32,0x804a07d5,0x804a07d5,3 +np.float32,0x802e6117,0x802e6117,3 +np.float32,0x7ed5e388,0x3fc90fdb,3 +np.float32,0x80750455,0x80750455,3 +np.float32,0xff4a8325,0xbfc90fdb,3 +np.float32,0xbedb6866,0xbecf497c,3 +np.float32,0x52ea3b,0x52ea3b,3 +np.float32,0xff773172,0xbfc90fdb,3 +np.float32,0xbeaa8ff0,0xbea4a46e,3 +np.float32,0x7eef2058,0x3fc90fdb,3 +np.float32,0x3f712472,0x3f4169d3,3 +np.float32,0xff6c8608,0xbfc90fdb,3 +np.float32,0xbf6eaa41,0xbf40182a,3 +np.float32,0x3eb03c24,0x3ea9bb34,3 +np.float32,0xfe118cd4,0xbfc90fdb,3 +np.float32,0x3e5b03b0,0x3e57c378,3 +np.float32,0x7f34d92d,0x3fc90fdb,3 +np.float32,0x806c3418,0x806c3418,3 +np.float32,0x7f3074e3,0x3fc90fdb,3 +np.float32,0x8002df02,0x8002df02,3 +np.float32,0x3f6df63a,0x3f3fb7b7,3 +np.float32,0xfd2b4100,0xbfc90fdb,3 +np.float32,0x80363d5c,0x80363d5c,3 +np.float32,0xbeac1f98,0xbea60bd6,3 +np.float32,0xff7fffff,0xbfc90fdb,3 +np.float32,0x80045097,0x80045097,3 +np.float32,0xfe011100,0xbfc90fdb,3 +np.float32,0x80739ef5,0x80739ef5,3 +np.float32,0xff3976ed,0xbfc90fdb,3 +np.float32,0xbe18e3a0,0xbe17c49e,3 +np.float32,0xbe289294,0xbe2712f6,3 +np.float32,0x3f1d41e7,0x3f0d050e,3 +np.float32,0x39364a,0x39364a,3 +np.float32,0x8072b77e,0x8072b77e,3 +np.float32,0x3f7cfec0,0x3f478cf6,3 +np.float32,0x2f68f6,0x2f68f6,3 +np.float32,0xbf031fb8,0xbef25c84,3 +np.float32,0xbf0b842c,0xbeff7afc,3 +np.float32,0x3f081e7e,0x3efa3676,3 +np.float32,0x7f7fffff,0x3fc90fdb,3 +np.float32,0xff15da0e,0xbfc90fdb,3 +np.float32,0x3d2001b2,0x3d1fece1,3 +np.float32,0x7f76efef,0x3fc90fdb,3 +np.float32,0x3f2405dd,0x3f11dfb7,3 +np.float32,0xa0319,0xa0319,3 +np.float32,0x3e23d2bd,0x3e227255,3 +np.float32,0xbd4d4c50,0xbd4d205e,3 +np.float32,0x382344,0x382344,3 +np.float32,0x21bbf,0x21bbf,3 +np.float32,0xbf209e82,0xbf0f7239,3 +np.float32,0xff03bf9f,0xbfc90fdb,3 +np.float32,0x7b1789,0x7b1789,3 +np.float32,0xff314944,0xbfc90fdb,3 +np.float32,0x1a63eb,0x1a63eb,3 +np.float32,0x803dc983,0x803dc983,3 +np.float32,0x3f0ff558,0x3f0323dc,3 +np.float32,0x3f544f2c,0x3f313f58,3 +np.float32,0xff032948,0xbfc90fdb,3 +np.float32,0x7f4933cc,0x3fc90fdb,3 +np.float32,0x7f14c5ed,0x3fc90fdb,3 +np.float32,0x803aeebf,0x803aeebf,3 +np.float32,0xbf0d4c0f,0xbf011bf5,3 +np.float32,0xbeaf8de2,0xbea91f57,3 +np.float32,0xff3ae030,0xbfc90fdb,3 +np.float32,0xbb362d00,0xbb362ce1,3 +np.float32,0x3d1f79e0,0x3d1f6544,3 +np.float32,0x3f56e9d9,0x3f32c860,3 +np.float32,0x3f723e5e,0x3f41fee2,3 +np.float32,0x4c0179,0x4c0179,3 +np.float32,0xfee36132,0xbfc90fdb,3 +np.float32,0x619ae6,0x619ae6,3 +np.float32,0xfde5d670,0xbfc90fdb,3 +np.float32,0xff079ac5,0xbfc90fdb,3 +np.float32,0x3e974fbd,0x3e931fae,3 +np.float32,0x8020ae6b,0x8020ae6b,3 +np.float32,0x6b5af1,0x6b5af1,3 +np.float32,0xbeb57cd6,0xbeae69a3,3 +np.float32,0x806e7eb2,0x806e7eb2,3 +np.float32,0x7e666edb,0x3fc90fdb,3 +np.float32,0xbf458c18,0xbf283ff0,3 +np.float32,0x3e50518e,0x3e4d8399,3 +np.float32,0x3e9ce224,0x3e983b98,3 +np.float32,0x3e6bc067,0x3e67b6c6,3 +np.float32,0x13783d,0x13783d,3 +np.float32,0xff3d518c,0xbfc90fdb,3 +np.float32,0xfeba5968,0xbfc90fdb,3 +np.float32,0xbf0b9f76,0xbeffa50f,3 +np.float32,0xfe174900,0xbfc90fdb,3 +np.float32,0x3f38bb0a,0x3f200527,3 +np.float32,0x7e94a77d,0x3fc90fdb,3 +np.float32,0x29d776,0x29d776,3 +np.float32,0xbf4e058d,0xbf2d7a15,3 +np.float32,0xbd94abc8,0xbd946923,3 +np.float32,0xbee62db0,0xbed85124,3 +np.float32,0x800000,0x800000,3 +np.float32,0xbef1df7e,0xbee1f636,3 +np.float32,0xbcf3cd20,0xbcf3bab5,3 +np.float32,0x80007b05,0x80007b05,3 +np.float32,0x3d9b3f2e,0x3d9af351,3 +np.float32,0xbf714a68,0xbf417dee,3 +np.float32,0xbf2a2d37,0xbf163069,3 +np.float32,0x8055104f,0x8055104f,3 +np.float32,0x7f5c40d7,0x3fc90fdb,3 +np.float32,0x1,0x1,3 +np.float32,0xff35f3a6,0xbfc90fdb,3 +np.float32,0xd9c7c,0xd9c7c,3 +np.float32,0xbf440cfc,0xbf274f22,3 +np.float32,0x8050ac43,0x8050ac43,3 +np.float32,0x63ee16,0x63ee16,3 +np.float32,0x7d90419b,0x3fc90fdb,3 +np.float32,0xfee22198,0xbfc90fdb,3 +np.float32,0xc2ead,0xc2ead,3 +np.float32,0x7f5cd6a6,0x3fc90fdb,3 +np.float32,0x3f6fab7e,0x3f40a184,3 +np.float32,0x3ecf998c,0x3ec53a73,3 +np.float32,0x7e5271f0,0x3fc90fdb,3 +np.float32,0x67c016,0x67c016,3 +np.float32,0x2189c8,0x2189c8,3 +np.float32,0x27d892,0x27d892,3 +np.float32,0x3f0d02c4,0x3f00e3c0,3 +np.float32,0xbf69ebca,0xbf3d8862,3 +np.float32,0x3e60c0d6,0x3e5d3ebb,3 +np.float32,0x3f45206c,0x3f27fc66,3 +np.float32,0xbf6b47dc,0xbf3e4592,3 +np.float32,0xfe9be2e2,0xbfc90fdb,3 +np.float32,0x7fa00000,0x7fe00000,3 +np.float32,0xff271562,0xbfc90fdb,3 +np.float32,0x3e2e5270,0x3e2caaaf,3 +np.float32,0x80222934,0x80222934,3 +np.float32,0xbd01d220,0xbd01c701,3 +np.float32,0x223aa0,0x223aa0,3 +np.float32,0x3f4b5a7e,0x3f2bd967,3 +np.float32,0x3f217d85,0x3f101200,3 +np.float32,0xbf57663a,0xbf331144,3 +np.float32,0x3f219862,0x3f102536,3 +np.float32,0x28a28c,0x28a28c,3 +np.float32,0xbf3f55f4,0xbf244f86,3 +np.float32,0xbf3de287,0xbf236092,3 +np.float32,0xbf1c1ce2,0xbf0c2fe3,3 +np.float32,0x80000001,0x80000001,3 +np.float32,0x3db695d0,0x3db61a90,3 +np.float32,0x6c39bf,0x6c39bf,3 +np.float32,0x7e33a12f,0x3fc90fdb,3 +np.float32,0x67623a,0x67623a,3 +np.float32,0x3e45dc54,0x3e4373b6,3 +np.float32,0x7f62fa68,0x3fc90fdb,3 +np.float32,0x3f0e1d01,0x3f01bbe5,3 +np.float32,0x3f13dc69,0x3f0615f5,3 +np.float32,0x246703,0x246703,3 +np.float32,0xbf1055b5,0xbf036d07,3 +np.float32,0x7f46d3d0,0x3fc90fdb,3 +np.float32,0x3d2b8086,0x3d2b66e5,3 +np.float32,0xbf03be44,0xbef35776,3 +np.float32,0x3f800000,0x3f490fdb,3 +np.float32,0xbec8d226,0xbebf613d,3 +np.float32,0x3d8faf00,0x3d8f72d4,3 +np.float32,0x170c4e,0x170c4e,3 +np.float32,0xff14c0f0,0xbfc90fdb,3 +np.float32,0xff16245d,0xbfc90fdb,3 +np.float32,0x7f44ce6d,0x3fc90fdb,3 +np.float32,0xbe8175d8,0xbe7d9aeb,3 +np.float32,0x3df7a4a1,0x3df67254,3 +np.float32,0xfe2cc46c,0xbfc90fdb,3 +np.float32,0x3f284e63,0x3f14e335,3 +np.float32,0x7e46e5d6,0x3fc90fdb,3 +np.float32,0x397be4,0x397be4,3 +np.float32,0xbf2560bc,0xbf12d50b,3 +np.float32,0x3ed9b8c1,0x3ecddc60,3 +np.float32,0xfec18c5a,0xbfc90fdb,3 +np.float32,0x64894d,0x64894d,3 +np.float32,0x36a65d,0x36a65d,3 +np.float32,0x804ffcd7,0x804ffcd7,3 +np.float32,0x800f79e4,0x800f79e4,3 +np.float32,0x5d45ac,0x5d45ac,3 +np.float32,0x6cdda0,0x6cdda0,3 +np.float32,0xbf7f2077,0xbf489fe5,3 +np.float32,0xbf152f78,0xbf0713a1,3 +np.float32,0x807bf344,0x807bf344,3 +np.float32,0x3f775023,0x3f44a4d8,3 +np.float32,0xbf3edf67,0xbf240365,3 +np.float32,0x7eed729c,0x3fc90fdb,3 +np.float32,0x14cc29,0x14cc29,3 +np.float32,0x7edd7b6b,0x3fc90fdb,3 +np.float32,0xbf3c6e2c,0xbf226fb7,3 +np.float32,0x51b9ad,0x51b9ad,3 +np.float32,0x3f617ee8,0x3f38dd7c,3 +np.float32,0xff800000,0xbfc90fdb,3 +np.float32,0x7f440ea0,0x3fc90fdb,3 +np.float32,0x3e639893,0x3e5ff49e,3 +np.float32,0xbd791bb0,0xbd78cd3c,3 +np.float32,0x8059fcbc,0x8059fcbc,3 +np.float32,0xbf7d1214,0xbf4796bd,3 +np.float32,0x3ef368fa,0x3ee33788,3 +np.float32,0xbecec0f4,0xbec48055,3 +np.float32,0xbc83d940,0xbc83d656,3 +np.float32,0xbce01220,0xbce003d4,3 +np.float32,0x803192a5,0x803192a5,3 +np.float32,0xbe40e0c0,0xbe3ea4f0,3 +np.float32,0xfb692600,0xbfc90fdb,3 +np.float32,0x3f1bec65,0x3f0c0c88,3 +np.float32,0x7f042798,0x3fc90fdb,3 +np.float32,0xbe047374,0xbe03b83b,3 +np.float32,0x7f7c6630,0x3fc90fdb,3 +np.float32,0x7f58dae3,0x3fc90fdb,3 +np.float32,0x80691c92,0x80691c92,3 +np.float32,0x7dbe76,0x7dbe76,3 +np.float32,0xbf231384,0xbf11339d,3 +np.float32,0xbef4acf8,0xbee43f8b,3 +np.float32,0x3ee9f9d0,0x3edb7793,3 +np.float32,0x3f0064f6,0x3eee04a8,3 +np.float32,0x313732,0x313732,3 +np.float32,0xfd58cf80,0xbfc90fdb,3 +np.float32,0x3f7a2bc9,0x3f461d30,3 +np.float32,0x7f7681af,0x3fc90fdb,3 +np.float32,0x7f504211,0x3fc90fdb,3 +np.float32,0xfeae0c00,0xbfc90fdb,3 +np.float32,0xbee14396,0xbed436d1,3 +np.float32,0x7fc00000,0x7fc00000,3 +np.float32,0x693406,0x693406,3 +np.float32,0x3eb4a679,0x3eadab1b,3 +np.float32,0x550505,0x550505,3 +np.float32,0xfd493d10,0xbfc90fdb,3 +np.float32,0x3f4fc907,0x3f2e8b2c,3 +np.float32,0x80799aa4,0x80799aa4,3 +np.float32,0xff1ea89b,0xbfc90fdb,3 +np.float32,0xff424510,0xbfc90fdb,3 +np.float32,0x7f68d026,0x3fc90fdb,3 +np.float32,0xbea230ca,0xbe9d1200,3 +np.float32,0x7ea585da,0x3fc90fdb,3 +np.float32,0x3f3db211,0x3f23414c,3 +np.float32,0xfea4d964,0xbfc90fdb,3 +np.float32,0xbf17fe18,0xbf092984,3 +np.float32,0x7cc8a2,0x7cc8a2,3 +np.float32,0xff0330ba,0xbfc90fdb,3 +np.float32,0x3f769835,0x3f444592,3 +np.float32,0xeb0ac,0xeb0ac,3 +np.float32,0x7f7e45de,0x3fc90fdb,3 +np.float32,0xbdb510a8,0xbdb49873,3 +np.float32,0x3ebf900b,0x3eb74e9c,3 +np.float32,0xbf21bbce,0xbf103e89,3 +np.float32,0xbf3f4682,0xbf24459d,3 +np.float32,0x7eb6e9c8,0x3fc90fdb,3 +np.float32,0xbf42532d,0xbf2637be,3 +np.float32,0xbd3b2600,0xbd3b04b4,3 +np.float32,0x3f1fa9aa,0x3f0ec23e,3 +np.float32,0x7ed6a0f1,0x3fc90fdb,3 +np.float32,0xff4759a1,0xbfc90fdb,3 +np.float32,0x6d26e3,0x6d26e3,3 +np.float32,0xfe1108e0,0xbfc90fdb,3 +np.float32,0xfdf76900,0xbfc90fdb,3 +np.float32,0xfec66f22,0xbfc90fdb,3 +np.float32,0xbf3d097f,0xbf22d458,3 +np.float32,0x3d85be25,0x3d858d99,3 +np.float32,0x7f36739f,0x3fc90fdb,3 +np.float32,0x7bc0a304,0x3fc90fdb,3 +np.float32,0xff48dd90,0xbfc90fdb,3 +np.float32,0x48cab0,0x48cab0,3 +np.float32,0x3ed3943c,0x3ec8a2ef,3 +np.float32,0xbf61488e,0xbf38bede,3 +np.float32,0x3f543df5,0x3f313525,3 +np.float32,0x5cf2ca,0x5cf2ca,3 +np.float32,0x572686,0x572686,3 +np.float32,0x80369c7c,0x80369c7c,3 +np.float32,0xbd2c1d20,0xbd2c0338,3 +np.float32,0x3e255428,0x3e23ea0b,3 +np.float32,0xbeba9ee0,0xbeb2f54c,3 +np.float32,0x8015c165,0x8015c165,3 +np.float32,0x3d31f488,0x3d31d7e6,3 +np.float32,0x3f68591c,0x3f3cac43,3 +np.float32,0xf5ed5,0xf5ed5,3 +np.float32,0xbf3b1d34,0xbf21949e,3 +np.float32,0x1f0343,0x1f0343,3 +np.float32,0x3f0e52b5,0x3f01e4ef,3 +np.float32,0x7f57c596,0x3fc90fdb,3 +np.float64,0x7fd8e333ddb1c667,0x3ff921fb54442d18,3 +np.float64,0x800bcc9cdad7993a,0x800bcc9cdad7993a,3 +np.float64,0x3fcd6f81df3adf00,0x3fcceebbafc5d55e,3 +np.float64,0x3fed7338a57ae671,0x3fe7ce3e5811fc0a,3 +np.float64,0x7fe64994fcac9329,0x3ff921fb54442d18,3 +np.float64,0xfa5a6345f4b4d,0xfa5a6345f4b4d,3 +np.float64,0xe9dcd865d3b9b,0xe9dcd865d3b9b,3 +np.float64,0x7fea6cffabf4d9fe,0x3ff921fb54442d18,3 +np.float64,0xa9e1de6153c3c,0xa9e1de6153c3c,3 +np.float64,0xab6bdc5356d7c,0xab6bdc5356d7c,3 +np.float64,0x80062864a02c50ca,0x80062864a02c50ca,3 +np.float64,0xbfdac03aa7b58076,0xbfd9569f3230128d,3 +np.float64,0xbfe61b77752c36ef,0xbfe3588f51b8be8f,3 +np.float64,0x800bc854c8d790aa,0x800bc854c8d790aa,3 +np.float64,0x3feed1a2da3da346,0x3fe887f9b8ea031f,3 +np.float64,0x3fe910d3697221a7,0x3fe54365a53d840e,3 +np.float64,0x7fe7ab4944ef5692,0x3ff921fb54442d18,3 +np.float64,0x3fa462f1a028c5e3,0x3fa460303a6a4e69,3 +np.float64,0x800794f1a3af29e4,0x800794f1a3af29e4,3 +np.float64,0x3fee6fe7fafcdfd0,0x3fe854f863816d55,3 +np.float64,0x8000000000000000,0x8000000000000000,3 +np.float64,0x7f336472fe66d,0x7f336472fe66d,3 +np.float64,0xffb1623ac822c478,0xbff921fb54442d18,3 +np.float64,0x3fbacd68ce359ad2,0x3fbab480b3638846,3 +np.float64,0xffd5c02706ab804e,0xbff921fb54442d18,3 +np.float64,0xbfd4daf03d29b5e0,0xbfd42928f069c062,3 +np.float64,0x800c6e85dbd8dd0c,0x800c6e85dbd8dd0c,3 +np.float64,0x800e3599c5bc6b34,0x800e3599c5bc6b34,3 +np.float64,0x2c0d654c581ad,0x2c0d654c581ad,3 +np.float64,0xbfdd3eb13fba7d62,0xbfdb6e8143302de7,3 +np.float64,0x800b60cb8776c197,0x800b60cb8776c197,3 +np.float64,0x80089819ad113034,0x80089819ad113034,3 +np.float64,0x29fe721453fcf,0x29fe721453fcf,3 +np.float64,0x3fe8722f4df0e45f,0x3fe4e026d9eadb4d,3 +np.float64,0xffd1fbcd01a3f79a,0xbff921fb54442d18,3 +np.float64,0x7fc74e1e982e9c3c,0x3ff921fb54442d18,3 +np.float64,0x800c09d3d15813a8,0x800c09d3d15813a8,3 +np.float64,0xbfeee4578b3dc8af,0xbfe891ab3d6c3ce4,3 +np.float64,0xffdd01a6f33a034e,0xbff921fb54442d18,3 +np.float64,0x7fcc130480382608,0x3ff921fb54442d18,3 +np.float64,0xffcbb6bd1d376d7c,0xbff921fb54442d18,3 +np.float64,0xc068a53780d15,0xc068a53780d15,3 +np.float64,0xbfc974f15532e9e4,0xbfc92100b355f3e7,3 +np.float64,0x3fe6da79442db4f3,0x3fe3d87393b082e7,3 +np.float64,0xd9d9be4db3b38,0xd9d9be4db3b38,3 +np.float64,0x5ea50a20bd4a2,0x5ea50a20bd4a2,3 +np.float64,0xbfe5597f7d2ab2ff,0xbfe2d3ccc544b52b,3 +np.float64,0x80019364e4e326cb,0x80019364e4e326cb,3 +np.float64,0x3fed2902c3fa5206,0x3fe7a5e1df07e5c1,3 +np.float64,0xbfa7b72b5c2f6e50,0xbfa7b2d545b3cc1f,3 +np.float64,0xffdb60dd43b6c1ba,0xbff921fb54442d18,3 +np.float64,0x81a65d8b034cc,0x81a65d8b034cc,3 +np.float64,0x8000c30385818608,0x8000c30385818608,3 +np.float64,0x6022f5f4c045f,0x6022f5f4c045f,3 +np.float64,0x8007a2bb810f4578,0x8007a2bb810f4578,3 +np.float64,0x7fdc68893238d111,0x3ff921fb54442d18,3 +np.float64,0x7fd443454ea8868a,0x3ff921fb54442d18,3 +np.float64,0xffe6b04209ed6084,0xbff921fb54442d18,3 +np.float64,0x7fcd9733d13b2e67,0x3ff921fb54442d18,3 +np.float64,0xf5ee80a9ebdd0,0xf5ee80a9ebdd0,3 +np.float64,0x3fe3788e8de6f11e,0x3fe17dec7e6843a0,3 +np.float64,0x3fee36f62f7c6dec,0x3fe836f832515b43,3 +np.float64,0xf6cb49aded969,0xf6cb49aded969,3 +np.float64,0x3fd2b15ea4a562bc,0x3fd22fdc09920e67,3 +np.float64,0x7fccf6aef139ed5d,0x3ff921fb54442d18,3 +np.float64,0x3fd396b8ce272d72,0x3fd3026118857bd4,3 +np.float64,0x7fe53d3c80ea7a78,0x3ff921fb54442d18,3 +np.float64,0x3feae88fc4f5d120,0x3fe65fb04b18ef7a,3 +np.float64,0x3fedc643747b8c86,0x3fe7fafa6c20e25a,3 +np.float64,0xffdb2dc0df365b82,0xbff921fb54442d18,3 +np.float64,0xbfa2af3658255e70,0xbfa2ad17348f4253,3 +np.float64,0x3f8aa77b30354ef6,0x3f8aa71892336a69,3 +np.float64,0xbfdd1b1efbba363e,0xbfdb510dcd186820,3 +np.float64,0x800f50d99c5ea1b3,0x800f50d99c5ea1b3,3 +np.float64,0xff6ed602403dac00,0xbff921fb54442d18,3 +np.float64,0x800477d71aa8efaf,0x800477d71aa8efaf,3 +np.float64,0xbfe729a9e86e5354,0xbfe40ca78d9eefcf,3 +np.float64,0x3fd81ab2d4303566,0x3fd70d7e3937ea22,3 +np.float64,0xb617cbab6c2fa,0xb617cbab6c2fa,3 +np.float64,0x7fefffffffffffff,0x3ff921fb54442d18,3 +np.float64,0xffa40933ac281260,0xbff921fb54442d18,3 +np.float64,0xbfe1ede621e3dbcc,0xbfe057bb2b341ced,3 +np.float64,0xbfec700f03b8e01e,0xbfe73fb190bc722e,3 +np.float64,0x6e28af02dc517,0x6e28af02dc517,3 +np.float64,0x3fe37ad37ae6f5a7,0x3fe17f94674818a9,3 +np.float64,0x8000cbdeeae197bf,0x8000cbdeeae197bf,3 +np.float64,0x3fe8fd1f01f1fa3e,0x3fe5372bbec5d72c,3 +np.float64,0x3f8f9229103f2452,0x3f8f918531894256,3 +np.float64,0x800536858e0a6d0c,0x800536858e0a6d0c,3 +np.float64,0x7fe82bb4f9f05769,0x3ff921fb54442d18,3 +np.float64,0xffc1c2fb592385f8,0xbff921fb54442d18,3 +np.float64,0x7f924ddfc0249bbf,0x3ff921fb54442d18,3 +np.float64,0xffd5e125c52bc24c,0xbff921fb54442d18,3 +np.float64,0xbfef0d8738be1b0e,0xbfe8a6ef17b16c10,3 +np.float64,0x3fc9c8875233910f,0x3fc9715e708503cb,3 +np.float64,0xbfe2d926f4e5b24e,0xbfe108956e61cbb3,3 +np.float64,0x7fd61c496dac3892,0x3ff921fb54442d18,3 +np.float64,0x7fed545c6b7aa8b8,0x3ff921fb54442d18,3 +np.float64,0x8003746fea86e8e1,0x8003746fea86e8e1,3 +np.float64,0x3fdf515e75bea2bd,0x3fdd201a5585caa3,3 +np.float64,0xffda87c8ee350f92,0xbff921fb54442d18,3 +np.float64,0xffc675d8e22cebb0,0xbff921fb54442d18,3 +np.float64,0xffcdc173433b82e8,0xbff921fb54442d18,3 +np.float64,0xffed9df1517b3be2,0xbff921fb54442d18,3 +np.float64,0x3fd6a2eec72d45de,0x3fd5c1f1d7dcddcf,3 +np.float64,0xffec116a66f822d4,0xbff921fb54442d18,3 +np.float64,0x8007c2a2458f8545,0x8007c2a2458f8545,3 +np.float64,0x3fe4ee80d969dd02,0x3fe2895076094668,3 +np.float64,0x3fe3cae7116795ce,0x3fe1b9c07e0d03a7,3 +np.float64,0xbfd81bf8d8b037f2,0xbfd70e9bbbb4ca57,3 +np.float64,0x800c88ccd1f9119a,0x800c88ccd1f9119a,3 +np.float64,0xffdab2aee2b5655e,0xbff921fb54442d18,3 +np.float64,0x3fe743d227ee87a4,0x3fe41dcaef186d96,3 +np.float64,0x3fb060fd0220c1fa,0x3fb05b47f56ebbb4,3 +np.float64,0xbfd3f03772a7e06e,0xbfd3541522377291,3 +np.float64,0x190a5ae03216,0x190a5ae03216,3 +np.float64,0x3fe48c71916918e4,0x3fe24442f45b3183,3 +np.float64,0x800862470590c48e,0x800862470590c48e,3 +np.float64,0x7fd3ced89d279db0,0x3ff921fb54442d18,3 +np.float64,0x3feb3d9b4ab67b37,0x3fe69140cf2623f7,3 +np.float64,0xbc3f296b787e5,0xbc3f296b787e5,3 +np.float64,0xbfed6b905dfad721,0xbfe7ca1881a8c0fd,3 +np.float64,0xbfe621c2aaac4386,0xbfe35cd1969a82db,3 +np.float64,0x8009e7b17593cf63,0x8009e7b17593cf63,3 +np.float64,0x80045f580ca8beb1,0x80045f580ca8beb1,3 +np.float64,0xbfea2e177e745c2f,0xbfe5f13971633339,3 +np.float64,0x3fee655787fccab0,0x3fe84f6b98b6de26,3 +np.float64,0x3fc9cde92f339bd0,0x3fc9768a88b2c97c,3 +np.float64,0x3fc819c3b3303388,0x3fc7d25e1526e731,3 +np.float64,0x3fd3e848d2a7d090,0x3fd34cd9e6af558f,3 +np.float64,0x3fe19dacac633b5a,0x3fe01a6b4d27adc2,3 +np.float64,0x800b190da316321c,0x800b190da316321c,3 +np.float64,0xd5c69711ab8d3,0xd5c69711ab8d3,3 +np.float64,0xbfdc31bed7b8637e,0xbfda8ea3c1309d6d,3 +np.float64,0xbfd02ba007a05740,0xbfcfad86f0d756dc,3 +np.float64,0x3fe874473d70e88e,0x3fe4e1793cd82123,3 +np.float64,0xffb465585c28cab0,0xbff921fb54442d18,3 +np.float64,0xbfb5d8e13e2bb1c0,0xbfb5cb5c7807fc4d,3 +np.float64,0xffe80f933bf01f26,0xbff921fb54442d18,3 +np.float64,0x7feea783f5fd4f07,0x3ff921fb54442d18,3 +np.float64,0xbfae6665f43cccd0,0xbfae5d45b0a6f90a,3 +np.float64,0x800bd6ef5a77addf,0x800bd6ef5a77addf,3 +np.float64,0x800d145babda28b8,0x800d145babda28b8,3 +np.float64,0x39de155473bc3,0x39de155473bc3,3 +np.float64,0x3fefbd6bb1ff7ad8,0x3fe9008e73a3296e,3 +np.float64,0x3fc40bca3d281798,0x3fc3e2710e167007,3 +np.float64,0x3fcae0918335c120,0x3fca7e09e704a678,3 +np.float64,0x51287fbea2511,0x51287fbea2511,3 +np.float64,0x7fa6bc33a82d7866,0x3ff921fb54442d18,3 +np.float64,0xe72a2bebce546,0xe72a2bebce546,3 +np.float64,0x3fe1c8fd686391fa,0x3fe03b9622aeb4e3,3 +np.float64,0x3fe2a73ac3654e76,0x3fe0e36bc1ee4ac4,3 +np.float64,0x59895218b312b,0x59895218b312b,3 +np.float64,0xc6dc25c78db85,0xc6dc25c78db85,3 +np.float64,0xbfc06cfac520d9f4,0xbfc0561f85d2c907,3 +np.float64,0xbfea912dc4f5225c,0xbfe62c3b1c01c793,3 +np.float64,0x3fb78ce89a2f19d0,0x3fb77bfcb65a67d3,3 +np.float64,0xbfece5cdea39cb9c,0xbfe78103d24099e5,3 +np.float64,0x30d3054e61a61,0x30d3054e61a61,3 +np.float64,0xbfd3fe26fba7fc4e,0xbfd360c8447c4f7a,3 +np.float64,0x800956072a92ac0f,0x800956072a92ac0f,3 +np.float64,0x7fe639b3b6ec7366,0x3ff921fb54442d18,3 +np.float64,0x800ee30240bdc605,0x800ee30240bdc605,3 +np.float64,0x7fef6af0d2bed5e1,0x3ff921fb54442d18,3 +np.float64,0xffefce8725ff9d0d,0xbff921fb54442d18,3 +np.float64,0x3fe2e311da65c624,0x3fe10ff1623089dc,3 +np.float64,0xbfe7e5cbe56fcb98,0xbfe486c3daeda67c,3 +np.float64,0x80095bc14472b783,0x80095bc14472b783,3 +np.float64,0xffef0cb4553e1968,0xbff921fb54442d18,3 +np.float64,0xe3e60567c7cc1,0xe3e60567c7cc1,3 +np.float64,0xffde919f06bd233e,0xbff921fb54442d18,3 +np.float64,0x3fe3f9632e27f2c6,0x3fe1db49ebd21c4e,3 +np.float64,0x9dee9a233bdd4,0x9dee9a233bdd4,3 +np.float64,0xbfe3bb0602e7760c,0xbfe1ae41b6d4c488,3 +np.float64,0x3fc46945a128d288,0x3fc43da54c6c6a2a,3 +np.float64,0x7fdef149ac3de292,0x3ff921fb54442d18,3 +np.float64,0x800a96c76d752d8f,0x800a96c76d752d8f,3 +np.float64,0x3f971a32382e3464,0x3f9719316b9e9baf,3 +np.float64,0x7fe97bcf15b2f79d,0x3ff921fb54442d18,3 +np.float64,0x7fea894558f5128a,0x3ff921fb54442d18,3 +np.float64,0x3fc9e3be1933c780,0x3fc98b847c3923eb,3 +np.float64,0x3f7accac40359959,0x3f7acc9330741b64,3 +np.float64,0xa80c136950183,0xa80c136950183,3 +np.float64,0x3fe408732b2810e6,0x3fe1e61e7cbc8824,3 +np.float64,0xffa775bc042eeb80,0xbff921fb54442d18,3 +np.float64,0x3fbf04bd223e0980,0x3fbede37b8fc697e,3 +np.float64,0x7fd999b34c333366,0x3ff921fb54442d18,3 +np.float64,0xe72146dfce429,0xe72146dfce429,3 +np.float64,0x4f511ee49ea24,0x4f511ee49ea24,3 +np.float64,0xffb3e6e58827cdc8,0xbff921fb54442d18,3 +np.float64,0x3fd1f180cfa3e300,0x3fd17e85b2871de2,3 +np.float64,0x97c8e45b2f91d,0x97c8e45b2f91d,3 +np.float64,0xbfeeb20e88fd641d,0xbfe8778f878440bf,3 +np.float64,0xbfe1fc6dee23f8dc,0xbfe062c815a93cde,3 +np.float64,0xab4bf71f5697f,0xab4bf71f5697f,3 +np.float64,0xa9675a2952cec,0xa9675a2952cec,3 +np.float64,0xbfef3ea4a33e7d49,0xbfe8c02743ebc1b6,3 +np.float64,0x3fe22a2eafa4545d,0x3fe08577afca52a9,3 +np.float64,0x3fe8a08daaf1411c,0x3fe4fd5a34f05305,3 +np.float64,0xbfc6cda77b2d9b50,0xbfc6910bcfa0cf4f,3 +np.float64,0x3fec398394387307,0x3fe7211dd5276500,3 +np.float64,0x3fe36c95c626d92c,0x3fe1752e5aa2357b,3 +np.float64,0xffd8b9e7073173ce,0xbff921fb54442d18,3 +np.float64,0xffe19f043ae33e08,0xbff921fb54442d18,3 +np.float64,0x800e3640709c6c81,0x800e3640709c6c81,3 +np.float64,0x3fe7d6c20aafad84,0x3fe47d1a3307d9c8,3 +np.float64,0x80093fd63b727fad,0x80093fd63b727fad,3 +np.float64,0xffe1a671a4634ce3,0xbff921fb54442d18,3 +np.float64,0xbfe53a6b386a74d6,0xbfe2be41859cb10d,3 +np.float64,0xbfed149a097a2934,0xbfe79ab7e3e93c1c,3 +np.float64,0x7fc2769a5724ed34,0x3ff921fb54442d18,3 +np.float64,0xffd01e4e99a03c9e,0xbff921fb54442d18,3 +np.float64,0xa61f38434c3e7,0xa61f38434c3e7,3 +np.float64,0x800ad4ac5195a959,0x800ad4ac5195a959,3 +np.float64,0x7ff8000000000000,0x7ff8000000000000,3 +np.float64,0x80034a45b6c6948c,0x80034a45b6c6948c,3 +np.float64,0x6350b218c6a17,0x6350b218c6a17,3 +np.float64,0xfff0000000000000,0xbff921fb54442d18,3 +np.float64,0x3fe363e759e6c7cf,0x3fe16ed58d80f9ce,3 +np.float64,0xffe3b98e59e7731c,0xbff921fb54442d18,3 +np.float64,0x3fdbf7b40337ef68,0x3fda5df7ad3c80f9,3 +np.float64,0xbfe9cdf784739bef,0xbfe5b74f346ef93d,3 +np.float64,0xbfc321bea326437c,0xbfc2fdc0d4ff7561,3 +np.float64,0xbfe40f77d2a81ef0,0xbfe1eb28c4ae4dde,3 +np.float64,0x7fe071806960e300,0x3ff921fb54442d18,3 +np.float64,0x7fd269006ea4d200,0x3ff921fb54442d18,3 +np.float64,0x80017a56e0e2f4af,0x80017a56e0e2f4af,3 +np.float64,0x8004b4ea09a969d5,0x8004b4ea09a969d5,3 +np.float64,0xbfedbb01e63b7604,0xbfe7f4f0e84297df,3 +np.float64,0x3fe44454826888a9,0x3fe210ff6d005706,3 +np.float64,0xbfe0e77e6ea1cefd,0xbfdf1a977da33402,3 +np.float64,0xbfed6d4c8c3ada99,0xbfe7cb0932093f60,3 +np.float64,0x1d74cb9e3ae9a,0x1d74cb9e3ae9a,3 +np.float64,0x80082a785d1054f1,0x80082a785d1054f1,3 +np.float64,0x3fe58393266b0726,0x3fe2f0d8e91d4887,3 +np.float64,0xffe4028899680510,0xbff921fb54442d18,3 +np.float64,0x783a2e5af0746,0x783a2e5af0746,3 +np.float64,0x7fcdce88e73b9d11,0x3ff921fb54442d18,3 +np.float64,0x3fc58672a72b0ce5,0x3fc5535e090e56e2,3 +np.float64,0x800889c839b11391,0x800889c839b11391,3 +np.float64,0xffe5e05c466bc0b8,0xbff921fb54442d18,3 +np.float64,0xbfcbef6ebe37dedc,0xbfcb810752468f49,3 +np.float64,0xffe9408563b2810a,0xbff921fb54442d18,3 +np.float64,0xbfee4738367c8e70,0xbfe83f8e5dd7602f,3 +np.float64,0xbfe4aeb587295d6b,0xbfe25c7a0c76a454,3 +np.float64,0xffc9aea0a7335d40,0xbff921fb54442d18,3 +np.float64,0xe1e02199c3c04,0xe1e02199c3c04,3 +np.float64,0xbfbd9400783b2800,0xbfbd729345d1d14f,3 +np.float64,0x7a5418bcf4a84,0x7a5418bcf4a84,3 +np.float64,0x3fdc1c2fa5b83860,0x3fda7c935965ae72,3 +np.float64,0x80076a9f58ced53f,0x80076a9f58ced53f,3 +np.float64,0x3fedc4bf957b897f,0x3fe7fa2a83148f1c,3 +np.float64,0x800981b8a9d30372,0x800981b8a9d30372,3 +np.float64,0xffe1082311621046,0xbff921fb54442d18,3 +np.float64,0xe0091f89c0124,0xe0091f89c0124,3 +np.float64,0xbfce8d674f3d1ad0,0xbfcdfdbf2ddaa0ca,3 +np.float64,0x800516e72eaa2dcf,0x800516e72eaa2dcf,3 +np.float64,0xffe61ee64c6c3dcc,0xbff921fb54442d18,3 +np.float64,0x7fed2683cafa4d07,0x3ff921fb54442d18,3 +np.float64,0xffd4faf27729f5e4,0xbff921fb54442d18,3 +np.float64,0x7fe308fa842611f4,0x3ff921fb54442d18,3 +np.float64,0x3fc612a62b2c2550,0x3fc5db9ddbd4e159,3 +np.float64,0xbfe5b01e766b603d,0xbfe30f72a875e988,3 +np.float64,0x3fc2dd8b9a25bb17,0x3fc2bb06246b9f78,3 +np.float64,0x8170908102e12,0x8170908102e12,3 +np.float64,0x800c1c8a8a583915,0x800c1c8a8a583915,3 +np.float64,0xffe5d91e8b6bb23c,0xbff921fb54442d18,3 +np.float64,0xffd140adee22815c,0xbff921fb54442d18,3 +np.float64,0xbfe2f1f5f8e5e3ec,0xbfe11afa5d749952,3 +np.float64,0xbfed6d1d587ada3b,0xbfe7caef9ecf7651,3 +np.float64,0x3fe9b85e67f370bd,0x3fe5aa3474768982,3 +np.float64,0x7fdc8932edb91265,0x3ff921fb54442d18,3 +np.float64,0x7fd136bc54a26d78,0x3ff921fb54442d18,3 +np.float64,0x800a1ea12a343d43,0x800a1ea12a343d43,3 +np.float64,0x3fec6a5c1b78d4b8,0x3fe73c82235c3f8f,3 +np.float64,0x800fbf6a00df7ed4,0x800fbf6a00df7ed4,3 +np.float64,0xbfd0e6e0cda1cdc2,0xbfd0864bf8cad294,3 +np.float64,0x3fc716df482e2dbf,0x3fc6d7fbfd4a8470,3 +np.float64,0xbfe75990936eb321,0xbfe42bffec3fa0d7,3 +np.float64,0x3fd58e54a02b1ca9,0x3fd4cace1107a5cc,3 +np.float64,0xbfc9c04136338084,0xbfc9696ad2591d54,3 +np.float64,0xdd1f0147ba3e0,0xdd1f0147ba3e0,3 +np.float64,0x5c86a940b90e,0x5c86a940b90e,3 +np.float64,0xbfecae3b8e795c77,0xbfe7624d4988c612,3 +np.float64,0xffd0370595206e0c,0xbff921fb54442d18,3 +np.float64,0xbfdc26d443384da8,0xbfda857ecd33ba9f,3 +np.float64,0xbfd1c849d9a39094,0xbfd15849449cc378,3 +np.float64,0xffee04acdb3c0959,0xbff921fb54442d18,3 +np.float64,0xbfded1056dbda20a,0xbfdcb83b30e1528c,3 +np.float64,0x7fb7b826622f704c,0x3ff921fb54442d18,3 +np.float64,0xbfee4df8ae7c9bf1,0xbfe8431df9dfd05d,3 +np.float64,0x7fe7f3670e2fe6cd,0x3ff921fb54442d18,3 +np.float64,0x8008ac9ae0d15936,0x8008ac9ae0d15936,3 +np.float64,0x800dce9f3b3b9d3f,0x800dce9f3b3b9d3f,3 +np.float64,0x7fbb19db203633b5,0x3ff921fb54442d18,3 +np.float64,0x3fe56c7f302ad8fe,0x3fe2e0eec3ad45fd,3 +np.float64,0x7fe82c05c570580b,0x3ff921fb54442d18,3 +np.float64,0xc0552b7780aa6,0xc0552b7780aa6,3 +np.float64,0x39d40e3073a83,0x39d40e3073a83,3 +np.float64,0x3fd8db54d731b6aa,0x3fd7b589b3ee9b20,3 +np.float64,0xffcdd355233ba6ac,0xbff921fb54442d18,3 +np.float64,0x3fbe97b3a43d2f67,0x3fbe72bca9be0348,3 +np.float64,0xbff0000000000000,0xbfe921fb54442d18,3 +np.float64,0xbfb4f55e6229eac0,0xbfb4e96df18a75a7,3 +np.float64,0xbfc66399ba2cc734,0xbfc62a3298bd96fc,3 +np.float64,0x3fd00988bb201311,0x3fcf6d67a9374c38,3 +np.float64,0x7fe471867d28e30c,0x3ff921fb54442d18,3 +np.float64,0xbfe38e0e64271c1d,0xbfe18d9888b7523b,3 +np.float64,0x8009dc127573b825,0x8009dc127573b825,3 +np.float64,0x800047bde4608f7d,0x800047bde4608f7d,3 +np.float64,0xffeede42c77dbc85,0xbff921fb54442d18,3 +np.float64,0xd8cf6d13b19ee,0xd8cf6d13b19ee,3 +np.float64,0xbfd08fb302a11f66,0xbfd034b1f8235e23,3 +np.float64,0x7fdb404c0b368097,0x3ff921fb54442d18,3 +np.float64,0xbfd6ba0438ad7408,0xbfd5d673e3276ec1,3 +np.float64,0xffd9568027b2ad00,0xbff921fb54442d18,3 +np.float64,0xbfb313b73e262770,0xbfb30ab4acb4fa67,3 +np.float64,0xbfe2dc1a15e5b834,0xbfe10ac5f8f3acd3,3 +np.float64,0xbfee426bf4bc84d8,0xbfe83d061df91edd,3 +np.float64,0xd9142c2fb2286,0xd9142c2fb2286,3 +np.float64,0x7feb0d11dff61a23,0x3ff921fb54442d18,3 +np.float64,0x800fea5b509fd4b7,0x800fea5b509fd4b7,3 +np.float64,0x3fe1a8818da35103,0x3fe022ba1bdf366e,3 +np.float64,0x8010000000000000,0x8010000000000000,3 +np.float64,0xbfd8fc6de6b1f8dc,0xbfd7d24726ed8dcc,3 +np.float64,0xf4b3dc2de967c,0xf4b3dc2de967c,3 +np.float64,0x8af0409b15e08,0x8af0409b15e08,3 +np.float64,0x3fb21e6934243cd2,0x3fb216b065f8709a,3 +np.float64,0x3fc53069392a60d2,0x3fc4ffa931211fb9,3 +np.float64,0xffc955812c32ab04,0xbff921fb54442d18,3 +np.float64,0xbfe3de42b1a7bc86,0xbfe1c7bd1324de75,3 +np.float64,0x1dc149a03b82a,0x1dc149a03b82a,3 +np.float64,0x8001bc5a24a378b5,0x8001bc5a24a378b5,3 +np.float64,0x3da14c407b44,0x3da14c407b44,3 +np.float64,0x80025e8da924bd1c,0x80025e8da924bd1c,3 +np.float64,0xbfcb0141c9360284,0xbfca9d572ea5e1f3,3 +np.float64,0xc90036fd92007,0xc90036fd92007,3 +np.float64,0x138312c427063,0x138312c427063,3 +np.float64,0x800dda3a963bb475,0x800dda3a963bb475,3 +np.float64,0x3fe9339934f26732,0x3fe558e723291f78,3 +np.float64,0xbfea8357027506ae,0xbfe6240826faaf48,3 +np.float64,0x7fe04735cae08e6b,0x3ff921fb54442d18,3 +np.float64,0x3fe29aca3c653594,0x3fe0da214c8bc6a4,3 +np.float64,0x3fbe1f09a03c3e13,0x3fbdfbbefef0155b,3 +np.float64,0x816ee4ad02ddd,0x816ee4ad02ddd,3 +np.float64,0xffddd1b31d3ba366,0xbff921fb54442d18,3 +np.float64,0x3fe2e01e0625c03c,0x3fe10dc0bd6677c2,3 +np.float64,0x3fec6bcf1978d79e,0x3fe73d518cddeb7c,3 +np.float64,0x7fe01aaaf8603555,0x3ff921fb54442d18,3 +np.float64,0xdf300cc5be602,0xdf300cc5be602,3 +np.float64,0xbfe71c01a36e3804,0xbfe403af80ce47b8,3 +np.float64,0xffa5be00ac2b7c00,0xbff921fb54442d18,3 +np.float64,0xbfda9ba711b5374e,0xbfd93775e3ac6bda,3 +np.float64,0xbfe56d8a27eadb14,0xbfe2e1a7185e8e6d,3 +np.float64,0x800f1bc937be3792,0x800f1bc937be3792,3 +np.float64,0x800a61d93c74c3b3,0x800a61d93c74c3b3,3 +np.float64,0x7fe71a52fcae34a5,0x3ff921fb54442d18,3 +np.float64,0x7fb4aef256295de4,0x3ff921fb54442d18,3 +np.float64,0x3fe6c1e861ed83d1,0x3fe3c828f281a7ef,3 +np.float64,0x3fba128402342508,0x3fb9fb94cf141860,3 +np.float64,0x3fee55a7ecfcab50,0x3fe8472a9af893ee,3 +np.float64,0x3fe586f31b2b0de6,0x3fe2f32bce9e91bc,3 +np.float64,0xbfbb1d1442363a28,0xbfbb034c7729d5f2,3 +np.float64,0xc78b4d3f8f16a,0xc78b4d3f8f16a,3 +np.float64,0x7fdbc277d4b784ef,0x3ff921fb54442d18,3 +np.float64,0xbfa728ca2c2e5190,0xbfa724c04e73ccbd,3 +np.float64,0x7fefc7b2143f8f63,0x3ff921fb54442d18,3 +np.float64,0x3fd153a3dda2a748,0x3fd0ebccd33a4dca,3 +np.float64,0xbfe18a6eace314de,0xbfe00ba32ec89d30,3 +np.float64,0x7feef518537dea30,0x3ff921fb54442d18,3 +np.float64,0x8005f007cd4be010,0x8005f007cd4be010,3 +np.float64,0x7fd890b840b12170,0x3ff921fb54442d18,3 +np.float64,0x7feed0582ebda0af,0x3ff921fb54442d18,3 +np.float64,0x1013f53220280,0x1013f53220280,3 +np.float64,0xbfe77273986ee4e7,0xbfe43c375a8bf6de,3 +np.float64,0x7fe3ab8918675711,0x3ff921fb54442d18,3 +np.float64,0xbfc6ad515b2d5aa4,0xbfc671b2f7f86624,3 +np.float64,0x7fcd86231d3b0c45,0x3ff921fb54442d18,3 +np.float64,0xffe2523299a4a464,0xbff921fb54442d18,3 +np.float64,0x7fcadc5a1b35b8b3,0x3ff921fb54442d18,3 +np.float64,0x3fe5e020c4ebc042,0x3fe330418eec75bd,3 +np.float64,0x7fe332a9dc266553,0x3ff921fb54442d18,3 +np.float64,0xfa11dc21f425,0xfa11dc21f425,3 +np.float64,0xbec800177d900,0xbec800177d900,3 +np.float64,0x3fcadd057835ba0b,0x3fca7aa42face8bc,3 +np.float64,0xbfe6b9a206ad7344,0xbfe3c2a9719803de,3 +np.float64,0x3fbb4250b63684a0,0x3fbb281e9cefc519,3 +np.float64,0x7fef8787517f0f0e,0x3ff921fb54442d18,3 +np.float64,0x8001315c2d6262b9,0x8001315c2d6262b9,3 +np.float64,0xbfd94e3cf2b29c7a,0xbfd819257d36f56c,3 +np.float64,0xf1f325abe3e65,0xf1f325abe3e65,3 +np.float64,0x7fd6c07079ad80e0,0x3ff921fb54442d18,3 +np.float64,0x7fe328b075a65160,0x3ff921fb54442d18,3 +np.float64,0x7fe7998f812f331e,0x3ff921fb54442d18,3 +np.float64,0xffe026bb65604d76,0xbff921fb54442d18,3 +np.float64,0xffd6c06de8ad80dc,0xbff921fb54442d18,3 +np.float64,0x3fcd5a37bf3ab46f,0x3fccda82935d98ce,3 +np.float64,0xffc3e5a45227cb48,0xbff921fb54442d18,3 +np.float64,0x3febf7dd8177efbc,0x3fe6fc0bb999883e,3 +np.float64,0x7fd7047ea92e08fc,0x3ff921fb54442d18,3 +np.float64,0x35b3fc406b680,0x35b3fc406b680,3 +np.float64,0x7fd52e97632a5d2e,0x3ff921fb54442d18,3 +np.float64,0x3fd464d401a8c9a8,0x3fd3be2967fc97c3,3 +np.float64,0x800e815b2ebd02b6,0x800e815b2ebd02b6,3 +np.float64,0x3fca8428af350850,0x3fca257b466b8970,3 +np.float64,0x8007b7526f6f6ea6,0x8007b7526f6f6ea6,3 +np.float64,0x82f60a8f05ec2,0x82f60a8f05ec2,3 +np.float64,0x3fb71a5d0a2e34c0,0x3fb70a629ef8e2a2,3 +np.float64,0x7fc8570c7d30ae18,0x3ff921fb54442d18,3 +np.float64,0x7fe5528e77eaa51c,0x3ff921fb54442d18,3 +np.float64,0xffc20dbbf1241b78,0xbff921fb54442d18,3 +np.float64,0xeb13368fd6267,0xeb13368fd6267,3 +np.float64,0x7fe7d529056faa51,0x3ff921fb54442d18,3 +np.float64,0x3fecd02eabf9a05d,0x3fe77516f0ba1ac4,3 +np.float64,0x800fcba6a09f974d,0x800fcba6a09f974d,3 +np.float64,0x7fe7e8e015afd1bf,0x3ff921fb54442d18,3 +np.float64,0xbfd271a382a4e348,0xbfd1f513a191c595,3 +np.float64,0x9f1014013e21,0x9f1014013e21,3 +np.float64,0x3fc05da47f20bb49,0x3fc04708a13a3a47,3 +np.float64,0x3fe0f427dda1e850,0x3fdf2e60ba8678b9,3 +np.float64,0xbfecb29fa539653f,0xbfe764bc791c45dd,3 +np.float64,0x45881ec68b104,0x45881ec68b104,3 +np.float64,0x8000000000000001,0x8000000000000001,3 +np.float64,0x3fe9c67ee1338cfe,0x3fe5b2c7b3df6ce8,3 +np.float64,0x7fedb8fef6bb71fd,0x3ff921fb54442d18,3 +np.float64,0x3fe54f6aaaea9ed6,0x3fe2ccd1df2abaa9,3 +np.float64,0x7feff58a1bbfeb13,0x3ff921fb54442d18,3 +np.float64,0x7fe3b62827276c4f,0x3ff921fb54442d18,3 +np.float64,0x3fe5feb682ebfd6d,0x3fe345105bc6d980,3 +np.float64,0x3fe49f38d9693e72,0x3fe2518b2824757f,3 +np.float64,0x8006bfd27c6d7fa6,0x8006bfd27c6d7fa6,3 +np.float64,0x3fc13409e2226814,0x3fc119ce0c01a5a2,3 +np.float64,0x95f8c7212bf19,0x95f8c7212bf19,3 +np.float64,0x3fd9f0fa6133e1f5,0x3fd8a567515edecf,3 +np.float64,0x3fef95cbe5ff2b98,0x3fe8ec88c768ba0b,3 +np.float64,0x3fbed28bba3da510,0x3fbeacbf136e51c2,3 +np.float64,0xbfd3987aeca730f6,0xbfd303fca58e3e60,3 +np.float64,0xbfed0f90cbfa1f22,0xbfe797f59249410d,3 +np.float64,0xffe55d8cbf2abb19,0xbff921fb54442d18,3 +np.float64,0x3feb4d9fc6769b40,0x3fe69a88131a1f1f,3 +np.float64,0x80085569acd0aad4,0x80085569acd0aad4,3 +np.float64,0x20557a6e40ab0,0x20557a6e40ab0,3 +np.float64,0x3fead2fd5df5a5fb,0x3fe653091f33b27f,3 +np.float64,0x3fe7b9983eaf7330,0x3fe46a50c4b5235e,3 +np.float64,0xffdad237ffb5a470,0xbff921fb54442d18,3 +np.float64,0xbfe5cc39a4eb9874,0xbfe322ad3a903f93,3 +np.float64,0x800ad6eecb35adde,0x800ad6eecb35adde,3 +np.float64,0xffec620f6438c41e,0xbff921fb54442d18,3 +np.float64,0xbfe5ef29122bde52,0xbfe33a7dfcc255e2,3 +np.float64,0x3fd451e7d0a8a3d0,0x3fd3acfa4939af10,3 +np.float64,0x8003ea93c127d528,0x8003ea93c127d528,3 +np.float64,0x800b48d37c9691a7,0x800b48d37c9691a7,3 +np.float64,0x3fe7e202acafc405,0x3fe484558246069b,3 +np.float64,0x80070c9b686e1938,0x80070c9b686e1938,3 +np.float64,0xbfda90bbc6352178,0xbfd92e25fcd12288,3 +np.float64,0x800e1ffebb1c3ffe,0x800e1ffebb1c3ffe,3 +np.float64,0x3ff0000000000000,0x3fe921fb54442d18,3 +np.float64,0xffd8cfdd46319fba,0xbff921fb54442d18,3 +np.float64,0x7fd8cd4182319a82,0x3ff921fb54442d18,3 +np.float64,0x3fed8bb778bb176f,0x3fe7db7c77c4c694,3 +np.float64,0x3fc74a70302e94e0,0x3fc709e95d6defec,3 +np.float64,0x3fe87269d070e4d4,0x3fe4e04bcc4a2137,3 +np.float64,0x7fb48223f6290447,0x3ff921fb54442d18,3 +np.float64,0xffe8ec444b71d888,0xbff921fb54442d18,3 +np.float64,0x7fde17d280bc2fa4,0x3ff921fb54442d18,3 +np.float64,0x3fd1cbde01a397bc,0x3fd15b9bb7b3147b,3 +np.float64,0x800883a64451074d,0x800883a64451074d,3 +np.float64,0x7fe3160a3f262c13,0x3ff921fb54442d18,3 +np.float64,0xbfe051d4d9a0a3aa,0xbfde2ecf14dc75fb,3 +np.float64,0xbfd89de689b13bce,0xbfd780176d1a28a3,3 +np.float64,0x3fecde2bf779bc58,0x3fe77ccf10bdd8e2,3 +np.float64,0xffe75774dc6eaee9,0xbff921fb54442d18,3 +np.float64,0x7fe834414d706882,0x3ff921fb54442d18,3 +np.float64,0x1,0x1,3 +np.float64,0xbfea5e4e4a74bc9c,0xbfe60e0601711835,3 +np.float64,0xffec248d4cb8491a,0xbff921fb54442d18,3 +np.float64,0xffd9942c2c332858,0xbff921fb54442d18,3 +np.float64,0xa9db36a553b67,0xa9db36a553b67,3 +np.float64,0x7fec630718b8c60d,0x3ff921fb54442d18,3 +np.float64,0xbfd062188f20c432,0xbfd009ecd652be89,3 +np.float64,0x8001b84e3023709d,0x8001b84e3023709d,3 +np.float64,0xbfe9e26d7cb3c4db,0xbfe5c3b157ecf668,3 +np.float64,0xbfef66ddf33ecdbc,0xbfe8d4b1f6410a24,3 +np.float64,0x3fd8d7109431ae21,0x3fd7b1d4860719a2,3 +np.float64,0xffee0f53107c1ea5,0xbff921fb54442d18,3 +np.float64,0x80000b4fd60016a0,0x80000b4fd60016a0,3 +np.float64,0xbfd99ff6e5333fee,0xbfd85fb3cbdaa049,3 +np.float64,0xbfe9cfd268339fa5,0xbfe5b86ef021a1b1,3 +np.float64,0xe32eace1c65d6,0xe32eace1c65d6,3 +np.float64,0xffc81f6627303ecc,0xbff921fb54442d18,3 +np.float64,0x7fe98dadde331b5b,0x3ff921fb54442d18,3 +np.float64,0xbfbcebd11e39d7a0,0xbfbccc8ec47883c7,3 +np.float64,0x7fe164880f22c90f,0x3ff921fb54442d18,3 +np.float64,0x800467c0cae8cf82,0x800467c0cae8cf82,3 +np.float64,0x800071e4b140e3ca,0x800071e4b140e3ca,3 +np.float64,0xbfc87a7eae30f4fc,0xbfc82fbc55bb0f24,3 +np.float64,0xffb2e0e23225c1c8,0xbff921fb54442d18,3 +np.float64,0x20ef338041df,0x20ef338041df,3 +np.float64,0x7fe6de71ca6dbce3,0x3ff921fb54442d18,3 +np.float64,0x5d1fa026ba3f5,0x5d1fa026ba3f5,3 +np.float64,0xffd112a9ce222554,0xbff921fb54442d18,3 +np.float64,0x3fb351f66626a3ed,0x3fb3489ab578c452,3 +np.float64,0x7fef7b2bd3bef657,0x3ff921fb54442d18,3 +np.float64,0xffe144f5d4e289eb,0xbff921fb54442d18,3 +np.float64,0xffd63a6750ac74ce,0xbff921fb54442d18,3 +np.float64,0x7fd2d8bb25a5b175,0x3ff921fb54442d18,3 +np.float64,0x3fec5920a078b242,0x3fe732dcffcf6521,3 +np.float64,0x80009a8b7f813518,0x80009a8b7f813518,3 +np.float64,0x3fdea220893d4441,0x3fdc921edf6bf3d8,3 +np.float64,0x8006cee2208d9dc5,0x8006cee2208d9dc5,3 +np.float64,0xdd0b0081ba17,0xdd0b0081ba17,3 +np.float64,0x7ff4000000000000,0x7ffc000000000000,3 +np.float64,0xbfdac33955358672,0xbfd9592bce7daf1f,3 +np.float64,0x7fe8301d7170603a,0x3ff921fb54442d18,3 +np.float64,0xbfc1d34d8523a69c,0xbfc1b62449af9684,3 +np.float64,0x800c62239458c447,0x800c62239458c447,3 +np.float64,0xffd398c009a73180,0xbff921fb54442d18,3 +np.float64,0xbfe0c6d9ee218db4,0xbfdee777557f4401,3 +np.float64,0x3feccdd373799ba7,0x3fe773c9c2263f89,3 +np.float64,0xbfd21898bda43132,0xbfd1a2be8545fcc5,3 +np.float64,0x3fd77019b62ee033,0x3fd67793cabdf267,3 +np.float64,0x7fa609cad42c1395,0x3ff921fb54442d18,3 +np.float64,0x7fb4eaea5a29d5d4,0x3ff921fb54442d18,3 +np.float64,0x3fc570dc9a2ae1b9,0x3fc53e5f6218a799,3 +np.float64,0x800344ae8466895e,0x800344ae8466895e,3 +np.float64,0xbfc7c985252f930c,0xbfc784d60fa27bac,3 +np.float64,0xffaa2929fc345250,0xbff921fb54442d18,3 +np.float64,0xffe63e5ee9ac7cbe,0xbff921fb54442d18,3 +np.float64,0x73f0280ce7e06,0x73f0280ce7e06,3 +np.float64,0xffc525f8822a4bf0,0xbff921fb54442d18,3 +np.float64,0x7fd744d00aae899f,0x3ff921fb54442d18,3 +np.float64,0xbfe0fe590761fcb2,0xbfdf3e493e8b1f32,3 +np.float64,0xfae04ae7f5c0a,0xfae04ae7f5c0a,3 +np.float64,0xef821939df043,0xef821939df043,3 +np.float64,0x7fef6135843ec26a,0x3ff921fb54442d18,3 +np.float64,0xbfebf34dcbf7e69c,0xbfe6f97588a8f911,3 +np.float64,0xbfeec0b498fd8169,0xbfe87f2eceeead12,3 +np.float64,0x7fb67161b42ce2c2,0x3ff921fb54442d18,3 +np.float64,0x3fdcfd998639fb33,0x3fdb38934927c096,3 +np.float64,0xffda5960bc34b2c2,0xbff921fb54442d18,3 +np.float64,0xbfe11f8c71223f19,0xbfdf71fe770c96ab,3 +np.float64,0x3fe4ac1bab695838,0x3fe25aa4517b8322,3 +np.float64,0x3f730458a02608b1,0x3f73044fabb5e999,3 +np.float64,0x3fdb14ffcdb62a00,0x3fd99ea6c241a3ed,3 +np.float64,0xbfc93208cd326410,0xbfc8e09d78b6d4db,3 +np.float64,0x19e734dc33ce8,0x19e734dc33ce8,3 +np.float64,0x3fe5e98428abd308,0x3fe336a6a085eb55,3 +np.float64,0x7fec672a1378ce53,0x3ff921fb54442d18,3 +np.float64,0x800f8bd8d4ff17b2,0x800f8bd8d4ff17b2,3 +np.float64,0xbfe5a12e4e6b425c,0xbfe30533f99d5d06,3 +np.float64,0x75a34cb0eb46a,0x75a34cb0eb46a,3 +np.float64,0x7fe1d21d16a3a439,0x3ff921fb54442d18,3 +np.float64,0x7ff0000000000000,0x3ff921fb54442d18,3 +np.float64,0xffe0f50db261ea1b,0xbff921fb54442d18,3 +np.float64,0xbfd9dc22feb3b846,0xbfd8937ec965a501,3 +np.float64,0x8009d68e48d3ad1d,0x8009d68e48d3ad1d,3 +np.float64,0xbfe2eba620e5d74c,0xbfe1164d7d273c60,3 +np.float64,0x992efa09325e0,0x992efa09325e0,3 +np.float64,0x3fdab640ea356c82,0x3fd94e20cab88db2,3 +np.float64,0x69a6f04ad34df,0x69a6f04ad34df,3 +np.float64,0x3fe397df25272fbe,0x3fe194bd1a3a6192,3 +np.float64,0xebcce9fdd799d,0xebcce9fdd799d,3 +np.float64,0x3fbb49490c369292,0x3fbb2f02eccc497d,3 +np.float64,0xffd871f980b0e3f4,0xbff921fb54442d18,3 +np.float64,0x800348f6966691ee,0x800348f6966691ee,3 +np.float64,0xbfebc270a7f784e1,0xbfe6dda8d0d80f26,3 +np.float64,0xffd6d559b1adaab4,0xbff921fb54442d18,3 +np.float64,0x3fec3635c0b86c6c,0x3fe71f420256e43e,3 +np.float64,0x7fbc82ad7039055a,0x3ff921fb54442d18,3 +np.float64,0x7f873050602e60a0,0x3ff921fb54442d18,3 +np.float64,0x3fca44b8c3348970,0x3fc9e8a1a1a2d96e,3 +np.float64,0x3fe0fc308fe1f861,0x3fdf3aeb469ea225,3 +np.float64,0x7fefc27de8bf84fb,0x3ff921fb54442d18,3 +np.float64,0x8005f3f3916be7e8,0x8005f3f3916be7e8,3 +np.float64,0xbfd4278c7c284f18,0xbfd38678988873b6,3 +np.float64,0x435eafc486bd7,0x435eafc486bd7,3 +np.float64,0xbfd01f5199203ea4,0xbfcf96631f2108a3,3 +np.float64,0xffd5ee9185abdd24,0xbff921fb54442d18,3 +np.float64,0xffedb363257b66c5,0xbff921fb54442d18,3 +np.float64,0x800d68e6e11ad1ce,0x800d68e6e11ad1ce,3 +np.float64,0xbfcf687f8e3ed100,0xbfceccb771b0d39a,3 +np.float64,0x7feb3b9ef2f6773d,0x3ff921fb54442d18,3 +np.float64,0x3fe15ec5ca62bd8c,0x3fdfd3fab9d96f81,3 +np.float64,0x10000000000000,0x10000000000000,3 +np.float64,0xd2386f81a470e,0xd2386f81a470e,3 +np.float64,0xb9feed4573fde,0xb9feed4573fde,3 +np.float64,0x3fe7ed25c9efda4c,0x3fe48b7b72db4014,3 +np.float64,0xbfe01478726028f1,0xbfddcd1f5a2efc59,3 +np.float64,0x9946d02f328da,0x9946d02f328da,3 +np.float64,0xbfe3bb67f06776d0,0xbfe1ae88aa81c5a6,3 +np.float64,0xbfd3fd8a4c27fb14,0xbfd3603982e3b78d,3 +np.float64,0xffd5c3ab912b8758,0xbff921fb54442d18,3 +np.float64,0xffd5f502b12bea06,0xbff921fb54442d18,3 +np.float64,0xbfc64981ec2c9304,0xbfc610e0382b1fa6,3 +np.float64,0xffec42e3413885c6,0xbff921fb54442d18,3 +np.float64,0x80084eb4ed109d6a,0x80084eb4ed109d6a,3 +np.float64,0xbfd17cac9fa2f95a,0xbfd112020588a4b3,3 +np.float64,0xbfd06c1359a0d826,0xbfd0134a28aa9a66,3 +np.float64,0x7fdc3d7c03b87af7,0x3ff921fb54442d18,3 +np.float64,0x7bdf5aaaf7bec,0x7bdf5aaaf7bec,3 +np.float64,0xbfee3cd966fc79b3,0xbfe83a14bc07ac3b,3 +np.float64,0x7fec910da3f9221a,0x3ff921fb54442d18,3 +np.float64,0xffb4ea667029d4d0,0xbff921fb54442d18,3 +np.float64,0x800103d7cce207b0,0x800103d7cce207b0,3 +np.float64,0x7fbb229a6c364534,0x3ff921fb54442d18,3 +np.float64,0x0,0x0,3 +np.float64,0xffd8fccd0331f99a,0xbff921fb54442d18,3 +np.float64,0xbfd0784ae1a0f096,0xbfd01ebff62e39ad,3 +np.float64,0xbfed2ec9b3ba5d93,0xbfe7a9099410bc76,3 +np.float64,0x800690b8d16d2172,0x800690b8d16d2172,3 +np.float64,0x7fc061b26520c364,0x3ff921fb54442d18,3 +np.float64,0x8007ec47054fd88f,0x8007ec47054fd88f,3 +np.float64,0x775546b6eeaa9,0x775546b6eeaa9,3 +np.float64,0x8005e00fb56bc020,0x8005e00fb56bc020,3 +np.float64,0xbfe510f8d0ea21f2,0xbfe2a16862b5a37f,3 +np.float64,0xffd87a6bf3b0f4d8,0xbff921fb54442d18,3 +np.float64,0x800906e3d0520dc8,0x800906e3d0520dc8,3 +np.float64,0x2296f000452f,0x2296f000452f,3 +np.float64,0xbfe3189fa2e63140,0xbfe1378c0e005be4,3 +np.float64,0xb4d2447f69a49,0xb4d2447f69a49,3 +np.float64,0xffd056a24a20ad44,0xbff921fb54442d18,3 +np.float64,0xbfe3b23fe4e76480,0xbfe1a7e5840fcbeb,3 +np.float64,0x80018ee270831dc6,0x80018ee270831dc6,3 +np.float64,0x800df89f245bf13e,0x800df89f245bf13e,3 +np.float64,0x3fee1409d7bc2814,0x3fe824779d133232,3 +np.float64,0xbfef8d81667f1b03,0xbfe8e85523620368,3 +np.float64,0xffd8a6519b314ca4,0xbff921fb54442d18,3 +np.float64,0x7fc7bc86f32f790d,0x3ff921fb54442d18,3 +np.float64,0xffea6159e674c2b3,0xbff921fb54442d18,3 +np.float64,0x3fe153c3fba2a788,0x3fdfc2f74769d300,3 +np.float64,0xffc4261ef3284c3c,0xbff921fb54442d18,3 +np.float64,0x7fe8a8961ff1512b,0x3ff921fb54442d18,3 +np.float64,0xbfe3fb1fd167f640,0xbfe1dc89dcb7ecdf,3 +np.float64,0x3fd88577c2b10af0,0x3fd76acc09660704,3 +np.float64,0x3fe128ec27e251d8,0x3fdf808fc7ebcd8f,3 +np.float64,0xbfed6ca7c4fad950,0xbfe7caafe9a3e213,3 +np.float64,0xbf9a3912b8347220,0xbf9a379b3349352e,3 +np.float64,0xbfd724d7bcae49b0,0xbfd6351efa2a5fc5,3 +np.float64,0xbfed59700a7ab2e0,0xbfe7c043014c694c,3 +np.float64,0x8002ad435bc55a87,0x8002ad435bc55a87,3 +np.float64,0xffe46ed345a8dda6,0xbff921fb54442d18,3 +np.float64,0x7fd2f1d1d825e3a3,0x3ff921fb54442d18,3 +np.float64,0xbfea0265e23404cc,0xbfe5d6fb3fd30464,3 +np.float64,0xbfd17e049122fc0a,0xbfd113421078bbae,3 +np.float64,0xffea03b986b40772,0xbff921fb54442d18,3 +np.float64,0x800b55331a16aa67,0x800b55331a16aa67,3 +np.float64,0xbfc6fcafbf2df960,0xbfc6be9ecd0ebc1f,3 +np.float64,0xd6a36017ad46c,0xd6a36017ad46c,3 +np.float64,0xbfe9ba86dfb3750e,0xbfe5ab840cb0ef86,3 +np.float64,0x75c4a108eb895,0x75c4a108eb895,3 +np.float64,0x8008d6bc8051ad79,0x8008d6bc8051ad79,3 +np.float64,0xbfd3dc5984a7b8b4,0xbfd341f78e0528ec,3 +np.float64,0xffe1cbb01aa39760,0xbff921fb54442d18,3 +np.float64,0x3fc7e292f52fc526,0x3fc79d0ce9365767,3 +np.float64,0xbfcbeae2bd37d5c4,0xbfcb7cb034f82467,3 +np.float64,0x8000f0c62e21e18d,0x8000f0c62e21e18d,3 +np.float64,0xbfe23d8bc6247b18,0xbfe09418ee35c3c7,3 +np.float64,0x717394bae2e73,0x717394bae2e73,3 +np.float64,0xffa2ef1cc425de40,0xbff921fb54442d18,3 +np.float64,0x3fd938c229b27184,0x3fd806900735c99d,3 +np.float64,0x800bf3ec8a77e7d9,0x800bf3ec8a77e7d9,3 +np.float64,0xffeef41dd57de83b,0xbff921fb54442d18,3 +np.float64,0x8008df97e5b1bf30,0x8008df97e5b1bf30,3 +np.float64,0xffe9ab9d0db35739,0xbff921fb54442d18,3 +np.float64,0x99ff391333fe7,0x99ff391333fe7,3 +np.float64,0x3fb864b4a630c969,0x3fb851e883ea2cf9,3 +np.float64,0x22c1230a45825,0x22c1230a45825,3 +np.float64,0xff2336fbfe467,0xff2336fbfe467,3 +np.float64,0xbfd488f4cea911ea,0xbfd3def0490f5414,3 +np.float64,0x3fa379c78426f38f,0x3fa377607370800b,3 +np.float64,0xbfb0873302210e68,0xbfb08155b78dfd53,3 +np.float64,0xbfdf9ff7c2bf3ff0,0xbfdd5f658e357ad2,3 +np.float64,0x800978719192f0e4,0x800978719192f0e4,3 +np.float64,0xbfba8759ea350eb0,0xbfba6f325013b9e5,3 +np.float64,0xbfdd3e6b06ba7cd6,0xbfdb6e472b6091b0,3 +np.float64,0x7fe0c334a7a18668,0x3ff921fb54442d18,3 +np.float64,0xbfeb971feb772e40,0xbfe6c4e0f61404d1,3 +np.float64,0x3fe2a50968e54a13,0x3fe0e1c8b8d96e85,3 +np.float64,0x800fa9c5515f538b,0x800fa9c5515f538b,3 +np.float64,0x800f8532fbbf0a66,0x800f8532fbbf0a66,3 +np.float64,0x167d6f1e2cfaf,0x167d6f1e2cfaf,3 +np.float64,0xffee88e769fd11ce,0xbff921fb54442d18,3 +np.float64,0xbfeecc8529fd990a,0xbfe885520cdad8ea,3 +np.float64,0xffefffffffffffff,0xbff921fb54442d18,3 +np.float64,0xbfef6a566afed4ad,0xbfe8d6767b4c4235,3 +np.float64,0xffec12415af82482,0xbff921fb54442d18,3 +np.float64,0x3678a20a6cf15,0x3678a20a6cf15,3 +np.float64,0xffe468d54ee8d1aa,0xbff921fb54442d18,3 +np.float64,0x800ad6006795ac01,0x800ad6006795ac01,3 +np.float64,0x8001d5b61063ab6d,0x8001d5b61063ab6d,3 +np.float64,0x800dfcd1863bf9a3,0x800dfcd1863bf9a3,3 +np.float64,0xc9fbff6f93f80,0xc9fbff6f93f80,3 +np.float64,0xffe55c20f9eab842,0xbff921fb54442d18,3 +np.float64,0xbfcb596b6536b2d8,0xbfcaf1b339c5c615,3 +np.float64,0xbfe092689ea124d1,0xbfde94fa58946e51,3 +np.float64,0x3fe9ec733af3d8e6,0x3fe5c9bf5dee2623,3 +np.float64,0x3fe30f3d83261e7b,0x3fe1309fd6620e03,3 +np.float64,0xffd31d7f84263b00,0xbff921fb54442d18,3 +np.float64,0xbfe88d2d3e711a5a,0xbfe4f12b5a136178,3 +np.float64,0xffc81e4ce1303c98,0xbff921fb54442d18,3 +np.float64,0xffe5b96ebfab72dd,0xbff921fb54442d18,3 +np.float64,0x512f0502a25e1,0x512f0502a25e1,3 +np.float64,0x7fa3a376982746ec,0x3ff921fb54442d18,3 +np.float64,0x80005b5f2f60b6bf,0x80005b5f2f60b6bf,3 +np.float64,0xc337cc69866fa,0xc337cc69866fa,3 +np.float64,0x3fe7719c4caee339,0x3fe43bab42b19e64,3 +np.float64,0x7fde7ec1d93cfd83,0x3ff921fb54442d18,3 +np.float64,0x3fd2f38f3825e71e,0x3fd26cc7b1dd0acb,3 +np.float64,0x7fce298b993c5316,0x3ff921fb54442d18,3 +np.float64,0x56ae3b2cad5c8,0x56ae3b2cad5c8,3 +np.float64,0x3fe9299f2bf2533e,0x3fe552bddd999e72,3 +np.float64,0x7feff3a4823fe748,0x3ff921fb54442d18,3 +np.float64,0xbfd05c670aa0b8ce,0xbfd00494d78e9e97,3 +np.float64,0xffe745323eae8a64,0xbff921fb54442d18,3 diff --git a/numpy/core/tests/data/umath-validation-set-arctanh.csv b/numpy/core/tests/data/umath-validation-set-arctanh.csv new file mode 100644 index 000000000000..a655269d031f --- /dev/null +++ b/numpy/core/tests/data/umath-validation-set-arctanh.csv @@ -0,0 +1,1429 @@ +dtype,input,output,ulperrortol +np.float32,0x3ee82930,0x3efa60fd,2 +np.float32,0x3f0aa640,0x3f1b3e13,2 +np.float32,0x3ec1a21c,0x3ecbbf8d,2 +np.float32,0x3cdb1740,0x3cdb24a1,2 +np.float32,0xbf28b6f3,0xbf4a86ac,2 +np.float32,0xbe490dcc,0xbe4bb2eb,2 +np.float32,0x80000001,0x80000001,2 +np.float32,0xbf44f9dd,0xbf826ce1,2 +np.float32,0xbf1d66c4,0xbf37786b,2 +np.float32,0x3f0ad26a,0x3f1b7c9b,2 +np.float32,0x3f7b6c54,0x4016aab0,2 +np.float32,0xbf715bb8,0xbfe1a0bc,2 +np.float32,0xbee8a562,0xbefafd6a,2 +np.float32,0x3db94d00,0x3db9cf16,2 +np.float32,0x3ee2970c,0x3ef368b3,2 +np.float32,0x3f3f8614,0x3f77fdca,2 +np.float32,0xbf1fb5f0,0xbf3b3789,2 +np.float32,0x3f798dc0,0x400b96bb,2 +np.float32,0x3e975d64,0x3e9c0573,2 +np.float32,0xbe3f1908,0xbe415d1f,2 +np.float32,0x3f2cea38,0x3f52192e,2 +np.float32,0x3e82f1ac,0x3e85eaa1,2 +np.float32,0x3eab6b30,0x3eb24acd,2 +np.float32,0xbe9bb90c,0xbea0cf5f,2 +np.float32,0xbf43e847,0xbf81202f,2 +np.float32,0xbd232fa0,0xbd2345c0,2 +np.float32,0xbbabbc00,0xbbabbc67,2 +np.float32,0xbf0b2975,0xbf1bf808,2 +np.float32,0xbef5ab0a,0xbf05d305,2 +np.float32,0x3f2cad16,0x3f51a8e2,2 +np.float32,0xbef75940,0xbf06eb08,2 +np.float32,0xbf0c1216,0xbf1d4325,2 +np.float32,0x3e7bdc08,0x3e8090c2,2 +np.float32,0x3da14e10,0x3da1a3c5,2 +np.float32,0x3f627412,0x3fb2bf21,2 +np.float32,0xbd6d08c0,0xbd6d4ca0,2 +np.float32,0x3f3e2368,0x3f74df8b,2 +np.float32,0xbe0df104,0xbe0edc77,2 +np.float32,0x3e8a265c,0x3e8da833,2 +np.float32,0xbdccdbb0,0xbdcd8ba8,2 +np.float32,0x3eb080c4,0x3eb80a44,2 +np.float32,0x3e627800,0x3e6645fe,2 +np.float32,0xbd8be0b0,0xbd8c1886,2 +np.float32,0xbf3282ac,0xbf5cae8c,2 +np.float32,0xbe515910,0xbe545707,2 +np.float32,0xbf2e64ac,0xbf54d637,2 +np.float32,0x3e0fc230,0x3e10b6de,2 +np.float32,0x3eb13ca0,0x3eb8df94,2 +np.float32,0x3f07a3ca,0x3f170572,2 +np.float32,0x3f2c7026,0x3f513935,2 +np.float32,0x3f3c4ec8,0x3f70d67c,2 +np.float32,0xbee9cce8,0xbefc724f,2 +np.float32,0xbe53ca60,0xbe56e3f3,2 +np.float32,0x3dd9e9a0,0x3ddabd98,2 +np.float32,0x3f38b8d4,0x3f69319b,2 +np.float32,0xbe176dc4,0xbe188c1d,2 +np.float32,0xbf322f2e,0xbf5c0c51,2 +np.float32,0xbe9b8676,0xbea097a2,2 +np.float32,0xbca44280,0xbca44823,2 +np.float32,0xbe2b0248,0xbe2ca036,2 +np.float32,0x3d101e80,0x3d102dbd,2 +np.float32,0xbf4eb610,0xbf8f526d,2 +np.float32,0xbec32a50,0xbecd89d1,2 +np.float32,0x3d549100,0x3d54c1ee,2 +np.float32,0x3f78e55e,0x40087025,2 +np.float32,0x3e592798,0x3e5c802d,2 +np.float32,0x3de045d0,0x3de12cfb,2 +np.float32,0xbdad28e0,0xbdad92f7,2 +np.float32,0x3e9a69e0,0x3e9f5e59,2 +np.float32,0x3e809778,0x3e836716,2 +np.float32,0xbf3278d9,0xbf5c9b6d,2 +np.float32,0x3f39fa00,0x3f6bd4a5,2 +np.float32,0xbec8143c,0xbed34ffa,2 +np.float32,0x3ddb7f40,0x3ddc57e6,2 +np.float32,0x3f0e8342,0x3f20c634,2 +np.float32,0x3f353dda,0x3f6213a4,2 +np.float32,0xbe96b400,0xbe9b4bea,2 +np.float32,0x3e626580,0x3e66328a,2 +np.float32,0xbde091c8,0xbde179df,2 +np.float32,0x3eb47b5c,0x3ebc91ca,2 +np.float32,0xbf282182,0xbf497f2f,2 +np.float32,0x3ea9f64c,0x3eb0a748,2 +np.float32,0x3f28dd4e,0x3f4aca86,2 +np.float32,0xbf71de18,0xbfe3f587,2 +np.float32,0x7fa00000,0x7fe00000,2 +np.float32,0xbf6696a6,0xbfbcf11a,2 +np.float32,0xbc853ae0,0xbc853de2,2 +np.float32,0xbeced246,0xbedb51b8,2 +np.float32,0x3f3472a4,0x3f607e00,2 +np.float32,0xbee90124,0xbefb7117,2 +np.float32,0x3eb45b90,0x3ebc6d7c,2 +np.float32,0xbe53ead0,0xbe5705d6,2 +np.float32,0x3f630c80,0x3fb420e2,2 +np.float32,0xbf408cd0,0xbf7a56a2,2 +np.float32,0x3dda4ed0,0x3ddb23f1,2 +np.float32,0xbf37ae88,0xbf67096b,2 +np.float32,0xbdd48c28,0xbdd550c9,2 +np.float32,0xbf5745b0,0xbf9cb4a4,2 +np.float32,0xbf44e6fc,0xbf8255c1,2 +np.float32,0x3f5c8e6a,0x3fa65020,2 +np.float32,0xbea45fe8,0xbeaa6630,2 +np.float32,0x3f08bdee,0x3f188ef5,2 +np.float32,0x3ec77e74,0x3ed29f4b,2 +np.float32,0xbf1a1d3c,0xbf324029,2 +np.float32,0x3cad7340,0x3cad79e3,2 +np.float32,0xbf4fac2e,0xbf90b72a,2 +np.float32,0x3f58516e,0x3f9e8330,2 +np.float32,0x3f442008,0x3f816391,2 +np.float32,0xbf6e0c6c,0xbfd42854,2 +np.float32,0xbf266f7a,0xbf4689b2,2 +np.float32,0x3eb7e2f0,0x3ec077ba,2 +np.float32,0xbf320fd0,0xbf5bcf83,2 +np.float32,0xbf6a76b9,0xbfc80a11,2 +np.float32,0xbf2a91b4,0xbf4dd526,2 +np.float32,0x3f176e30,0x3f2e150e,2 +np.float32,0xbdcccad0,0xbdcd7a9c,2 +np.float32,0x3f60a8a4,0x3faebbf7,2 +np.float32,0x3d9706f0,0x3d974d40,2 +np.float32,0x3ef3cd34,0x3f049d58,2 +np.float32,0xbf73c615,0xbfed79fe,2 +np.float32,0x3df1b170,0x3df2d31b,2 +np.float32,0x3f632a46,0x3fb466c7,2 +np.float32,0xbf3ea18e,0xbf75f9ce,2 +np.float32,0xbf3ea05c,0xbf75f71f,2 +np.float32,0xbdd76750,0xbdd83403,2 +np.float32,0xbca830c0,0xbca836cd,2 +np.float32,0x3f1d4162,0x3f373c59,2 +np.float32,0x3c115700,0x3c1157fa,2 +np.float32,0x3dae8ab0,0x3daef758,2 +np.float32,0xbcad5020,0xbcad56bf,2 +np.float32,0x3ee299c4,0x3ef36c15,2 +np.float32,0xbf7f566c,0xc054c3bd,2 +np.float32,0x3f0cc698,0x3f1e4557,2 +np.float32,0xbe75c648,0xbe7aaa04,2 +np.float32,0x3ea29238,0x3ea86417,2 +np.float32,0x3f09d9c0,0x3f1a1d61,2 +np.float32,0x3f67275c,0x3fbe74b3,2 +np.float32,0x3e1a4e18,0x3e1b7d3a,2 +np.float32,0xbef6e3fc,0xbf069e98,2 +np.float32,0xbf6038ac,0xbfadc9fd,2 +np.float32,0xbe46bdd4,0xbe494b7f,2 +np.float32,0xbf4df1f4,0xbf8e3a98,2 +np.float32,0x3d094dc0,0x3d095aed,2 +np.float32,0x3f44c7d2,0x3f822fa3,2 +np.float32,0xbea30816,0xbea8e737,2 +np.float32,0xbe3c27c4,0xbe3e511b,2 +np.float32,0x3f3bb47c,0x3f6f8789,2 +np.float32,0xbe423760,0xbe4498c3,2 +np.float32,0x3ece1a74,0x3eda7634,2 +np.float32,0x3f14d1f6,0x3f2a1a89,2 +np.float32,0xbf4d9e8f,0xbf8dc4c1,2 +np.float32,0xbe92968e,0xbe96cd7f,2 +np.float32,0x3e99e6c0,0x3e9ece26,2 +np.float32,0xbf397361,0xbf6ab878,2 +np.float32,0xbf4fcea4,0xbf90e99f,2 +np.float32,0x3de37640,0x3de46779,2 +np.float32,0x3eb1b604,0x3eb9698c,2 +np.float32,0xbf52d0a2,0xbf957361,2 +np.float32,0xbe20435c,0xbe21975a,2 +np.float32,0x3f437a58,0x3f809bf1,2 +np.float32,0x3f27d1cc,0x3f48f335,2 +np.float32,0x3f7d4ff2,0x4027d1e2,2 +np.float32,0xbef732e4,0xbf06d205,2 +np.float32,0x3f4a0ae6,0x3f88e18e,2 +np.float32,0x3f800000,0x7f800000,2 +np.float32,0x3e3e56a0,0x3e4093ba,2 +np.float32,0xbed2fcfa,0xbee0517d,2 +np.float32,0xbe0e0114,0xbe0eecd7,2 +np.float32,0xbe808574,0xbe8353db,2 +np.float32,0x3f572e2a,0x3f9c8c86,2 +np.float32,0x80800000,0x80800000,2 +np.float32,0x3f3f3c82,0x3f775703,2 +np.float32,0xbf6e2482,0xbfd4818b,2 +np.float32,0xbf3943b0,0xbf6a5439,2 +np.float32,0x3f6e42ac,0x3fd4f1ea,2 +np.float32,0x3eb676c4,0x3ebed619,2 +np.float32,0xbe5e56c4,0xbe61ef6c,2 +np.float32,0x3eea200c,0x3efcdb65,2 +np.float32,0x3e3d2c78,0x3e3f5ef8,2 +np.float32,0xbdfd8fb0,0xbdfede71,2 +np.float32,0xbee69c8a,0xbef86e89,2 +np.float32,0x3e9efca0,0x3ea46a1c,2 +np.float32,0x3e4c2498,0x3e4ee9ee,2 +np.float32,0xbf3cc93c,0xbf71e21d,2 +np.float32,0x3ee0d77c,0x3ef13d2b,2 +np.float32,0xbefbcd2a,0xbf09d6a3,2 +np.float32,0x3f6dbe5c,0x3fd30a3e,2 +np.float32,0x3dae63e0,0x3daed03f,2 +np.float32,0xbd5001e0,0xbd502fb9,2 +np.float32,0x3f59632a,0x3fa067c8,2 +np.float32,0x3f0d355a,0x3f1ee452,2 +np.float32,0x3f2cbe5c,0x3f51c896,2 +np.float32,0x3c5e6e80,0x3c5e7200,2 +np.float32,0xbe8ac49c,0xbe8e52f0,2 +np.float32,0x3f54e576,0x3f98c0e6,2 +np.float32,0xbeaa0762,0xbeb0ba7c,2 +np.float32,0x3ec81e88,0x3ed35c21,2 +np.float32,0x3f5a6738,0x3fa23fb6,2 +np.float32,0xbf24a682,0xbf43784a,2 +np.float32,0x1,0x1,2 +np.float32,0x3ee6bc24,0x3ef89630,2 +np.float32,0x3f19444a,0x3f30ecf5,2 +np.float32,0x3ec1fc70,0x3ecc28fc,2 +np.float32,0xbf706e14,0xbfdd92fb,2 +np.float32,0x3eccb630,0x3ed8cd98,2 +np.float32,0xbcdf7aa0,0xbcdf88d3,2 +np.float32,0xbe450da8,0xbe478a8e,2 +np.float32,0x3ec9c210,0x3ed54c0b,2 +np.float32,0xbf3b86ca,0xbf6f24d1,2 +np.float32,0x3edcc7a0,0x3eec3a5c,2 +np.float32,0x3f075d5c,0x3f16a39a,2 +np.float32,0xbf5719ce,0xbf9c69de,2 +np.float32,0x3f62cb22,0x3fb3885a,2 +np.float32,0x3f639216,0x3fb55c93,2 +np.float32,0xbf473ee7,0xbf85413a,2 +np.float32,0xbf01b66c,0xbf0eea86,2 +np.float32,0x3e872d80,0x3e8a74f8,2 +np.float32,0xbf60957e,0xbfae925c,2 +np.float32,0xbf6847b2,0xbfc1929b,2 +np.float32,0x3f78bb94,0x4007b363,2 +np.float32,0xbf47efdb,0xbf8622db,2 +np.float32,0xbe1f2308,0xbe206fd6,2 +np.float32,0xbf414926,0xbf7c0a7e,2 +np.float32,0x3eecc268,0x3f00194d,2 +np.float32,0x3eb086d0,0x3eb81120,2 +np.float32,0xbef1af80,0xbf033ff5,2 +np.float32,0xbf454e56,0xbf82d4aa,2 +np.float32,0x3e622560,0x3e65ef20,2 +np.float32,0x3f50d2b2,0x3f926a83,2 +np.float32,0x3eb2c45c,0x3eba9d2c,2 +np.float32,0x3e42d1a0,0x3e4538c9,2 +np.float32,0xbf24cc5c,0xbf43b8e3,2 +np.float32,0x3e8c6464,0x3e90141a,2 +np.float32,0xbf3abff2,0xbf6d79c5,2 +np.float32,0xbec8f2e6,0xbed456fa,2 +np.float32,0xbf787b38,0xc00698b4,2 +np.float32,0xbf58d5cd,0xbf9f6c03,2 +np.float32,0x3df4ee20,0x3df61ba8,2 +np.float32,0xbf34581e,0xbf604951,2 +np.float32,0xbeba5cf4,0xbec35119,2 +np.float32,0xbf76c22d,0xbfffc51c,2 +np.float32,0x3ef63b2c,0x3f0630b4,2 +np.float32,0x3eeadb64,0x3efdc877,2 +np.float32,0x3dfd8c70,0x3dfedb24,2 +np.float32,0x3f441600,0x3f81576d,2 +np.float32,0x3f23a0d8,0x3f41bbf6,2 +np.float32,0x3cb84d40,0x3cb85536,2 +np.float32,0xbf25cb5c,0xbf456e38,2 +np.float32,0xbc108540,0xbc108636,2 +np.float32,0xbc5b9140,0xbc5b949e,2 +np.float32,0xbf62ff40,0xbfb401dd,2 +np.float32,0x3e8e0710,0x3e91d93e,2 +np.float32,0x3f1b6ae0,0x3f344dfd,2 +np.float32,0xbf4dbbbe,0xbf8dedea,2 +np.float32,0x3f1a5fb2,0x3f32a880,2 +np.float32,0xbe56bd00,0xbe59f8cb,2 +np.float32,0xbf490a5c,0xbf87902d,2 +np.float32,0xbf513072,0xbf92f717,2 +np.float32,0x3e73ee28,0x3e78b542,2 +np.float32,0x3f0a4c7a,0x3f1abf2c,2 +np.float32,0x3e10d5c8,0x3e11d00b,2 +np.float32,0xbf771aac,0xc001207e,2 +np.float32,0x3efe2f54,0x3f0b6a46,2 +np.float32,0xbea5f3ea,0xbeac291f,2 +np.float32,0xbf1a73e8,0xbf32c845,2 +np.float32,0x3ebcc82c,0x3ec61c4f,2 +np.float32,0xbf24f492,0xbf43fd9a,2 +np.float32,0x3ecbd908,0x3ed7c691,2 +np.float32,0x3f461c5e,0x3f83d3f0,2 +np.float32,0x3eed0524,0x3f0043c1,2 +np.float32,0x3d06e840,0x3d06f4bf,2 +np.float32,0x3eb6c974,0x3ebf34d7,2 +np.float32,0xbf1c85e1,0xbf36100f,2 +np.float32,0x3ed697d0,0x3ee4ad04,2 +np.float32,0x3eab0484,0x3eb1d733,2 +np.float32,0xbf3b02f2,0xbf6e0935,2 +np.float32,0xbeeab154,0xbefd9334,2 +np.float32,0xbf695372,0xbfc49881,2 +np.float32,0x3e8aaa7c,0x3e8e36be,2 +np.float32,0xbf208754,0xbf3c8f7b,2 +np.float32,0xbe0dbf28,0xbe0ea9a1,2 +np.float32,0x3ca780c0,0x3ca786ba,2 +np.float32,0xbeb320b4,0xbebb065e,2 +np.float32,0x3f13c698,0x3f288821,2 +np.float32,0xbe8cbbec,0xbe9072c4,2 +np.float32,0x3f1ed534,0x3f39c8df,2 +np.float32,0x3e1ca450,0x3e1de190,2 +np.float32,0x3f54be1c,0x3f988134,2 +np.float32,0x3f34e4ee,0x3f6161b4,2 +np.float32,0xbf7e6913,0xc038b246,2 +np.float32,0x3d3c3f20,0x3d3c6119,2 +np.float32,0x3ca9dc80,0x3ca9e2bc,2 +np.float32,0xbf577ea2,0xbf9d161a,2 +np.float32,0xbedb22c8,0xbeea3644,2 +np.float32,0x3f22a044,0x3f400bfa,2 +np.float32,0xbe214b8c,0xbe22a637,2 +np.float32,0x3e8cd300,0x3e908bbc,2 +np.float32,0xbec4d214,0xbecf7a58,2 +np.float32,0x3e9399a4,0x3e97e7e4,2 +np.float32,0xbee6a1a2,0xbef874ed,2 +np.float32,0xbf323742,0xbf5c1bfd,2 +np.float32,0x3f48b882,0x3f8725ac,2 +np.float32,0xbf4d4dba,0xbf8d532e,2 +np.float32,0xbf59640a,0xbfa0695a,2 +np.float32,0xbf2ad562,0xbf4e4f03,2 +np.float32,0x3e317d98,0x3e334d03,2 +np.float32,0xbf6a5b71,0xbfc7b5a2,2 +np.float32,0x3e87b434,0x3e8b05cf,2 +np.float32,0xbf1c344c,0xbf358dee,2 +np.float32,0x3e449428,0x3e470c65,2 +np.float32,0xbf2c0f2f,0xbf508808,2 +np.float32,0xbec5b5ac,0xbed0859c,2 +np.float32,0xbf4aa956,0xbf89b4b1,2 +np.float32,0x3f6dd374,0x3fd35717,2 +np.float32,0x3f45f76c,0x3f83a5ef,2 +np.float32,0xbed1fba8,0xbedf1bd5,2 +np.float32,0xbd26b2d0,0xbd26ca66,2 +np.float32,0xbe9817c2,0xbe9cd1c3,2 +np.float32,0x3e725988,0x3e770875,2 +np.float32,0xbf1a8ded,0xbf32f132,2 +np.float32,0xbe695860,0xbe6d83d3,2 +np.float32,0x3d8cecd0,0x3d8d25ea,2 +np.float32,0x3f574706,0x3f9cb6ec,2 +np.float32,0xbf5c5a1f,0xbfa5eaf3,2 +np.float32,0x3e7a7c88,0x3e7fab83,2 +np.float32,0xff800000,0xffc00000,2 +np.float32,0x3f66396a,0x3fbbfbb0,2 +np.float32,0x3ed6e588,0x3ee50b53,2 +np.float32,0xbb56d500,0xbb56d532,2 +np.float32,0x3ebd23fc,0x3ec6869a,2 +np.float32,0xbf70d490,0xbfdf4af5,2 +np.float32,0x3e514f88,0x3e544d15,2 +np.float32,0x3e660f98,0x3e6a0dac,2 +np.float32,0xbf034da1,0xbf1110bb,2 +np.float32,0xbf60d9be,0xbfaf2714,2 +np.float32,0x3df67b10,0x3df7ae64,2 +np.float32,0xbeeedc0a,0xbf017010,2 +np.float32,0xbe149224,0xbe15a072,2 +np.float32,0x3f455084,0x3f82d759,2 +np.float32,0x3f210f9e,0x3f3d7093,2 +np.float32,0xbeaea3e0,0xbeb5edd3,2 +np.float32,0x3e0724b0,0x3e07efad,2 +np.float32,0x3f09a784,0x3f19d6ac,2 +np.float32,0xbf044340,0xbf125ee8,2 +np.float32,0xbf71adc9,0xbfe315fe,2 +np.float32,0x3efd3870,0x3f0ac6a8,2 +np.float32,0xbf53c7a6,0xbf96f6df,2 +np.float32,0xbf3cf784,0xbf7247af,2 +np.float32,0x3e0ce9e0,0x3e0dd035,2 +np.float32,0xbd3051a0,0xbd306d89,2 +np.float32,0x3ecab804,0x3ed66f77,2 +np.float32,0x3e984350,0x3e9d0189,2 +np.float32,0x3edd1c00,0x3eeca20b,2 +np.float32,0xbe8e22a0,0xbe91f71b,2 +np.float32,0x3ebebc18,0x3ec85fd6,2 +np.float32,0xba275c00,0xba275c01,2 +np.float32,0x3f1d8190,0x3f37a385,2 +np.float32,0x3f17343e,0x3f2dbbfe,2 +np.float32,0x3caa8000,0x3caa864e,2 +np.float32,0x3e7a7308,0x3e7fa168,2 +np.float32,0x3f7359a6,0x3feb3e1a,2 +np.float32,0xbf7ad15a,0xc012a743,2 +np.float32,0xbf122efb,0xbf262812,2 +np.float32,0xbf03ba04,0xbf11a3fa,2 +np.float32,0x3ed7a90c,0x3ee5f8d4,2 +np.float32,0xbe23e318,0xbe254eed,2 +np.float32,0xbe2866f4,0xbe29f20a,2 +np.float32,0xbeaedff2,0xbeb631d0,2 +np.float32,0x0,0x0,2 +np.float32,0x3ef2a034,0x3f03dafd,2 +np.float32,0x3f35806c,0x3f62994e,2 +np.float32,0xbf655e19,0xbfb9c718,2 +np.float32,0x3f5d54ce,0x3fa7d4f4,2 +np.float32,0x3f33e64a,0x3f5f67e3,2 +np.float32,0x3ebf4010,0x3ec8f923,2 +np.float32,0xbe050dc8,0xbe05cf70,2 +np.float32,0x3f61693e,0x3fb063b0,2 +np.float32,0xbd94ac00,0xbd94ef12,2 +np.float32,0x3e9de008,0x3ea32f61,2 +np.float32,0xbe3d042c,0xbe3f3540,2 +np.float32,0x3e8fdfc0,0x3e93d9e4,2 +np.float32,0x3f28bc48,0x3f4a9019,2 +np.float32,0x3edea928,0x3eee8b09,2 +np.float32,0xbf05f673,0xbf14b362,2 +np.float32,0xbf360730,0xbf63a914,2 +np.float32,0xbe3fb454,0xbe41fe0a,2 +np.float32,0x3f6d99a8,0x3fd28552,2 +np.float32,0xbf3ae866,0xbf6dd052,2 +np.float32,0x3f5b1164,0x3fa37aec,2 +np.float32,0xbf64a451,0xbfb7f61b,2 +np.float32,0xbdd79bd0,0xbdd86919,2 +np.float32,0x3e89fc00,0x3e8d7a85,2 +np.float32,0x3f4bf690,0x3f8b77ea,2 +np.float32,0x3cbdf280,0x3cbdfb38,2 +np.float32,0x3f138f98,0x3f2835b4,2 +np.float32,0xbe33967c,0xbe3576bc,2 +np.float32,0xbf298164,0xbf4bedda,2 +np.float32,0x3e9955cc,0x3e9e2edb,2 +np.float32,0xbf79b383,0xc00c56c0,2 +np.float32,0x3ea0834c,0x3ea61aea,2 +np.float32,0xbf511184,0xbf92c89a,2 +np.float32,0x3f4d9fba,0x3f8dc666,2 +np.float32,0x3f3387c2,0x3f5ead80,2 +np.float32,0x3e3f7360,0x3e41babb,2 +np.float32,0xbf3cc4d6,0xbf71d879,2 +np.float32,0x3f2e4402,0x3f54994e,2 +np.float32,0x3e6a7118,0x3e6eabff,2 +np.float32,0xbf05d83e,0xbf1489cc,2 +np.float32,0xbdce4fd8,0xbdcf039a,2 +np.float32,0xbf03e2f4,0xbf11dbaf,2 +np.float32,0x3f1ea0a0,0x3f397375,2 +np.float32,0x3f7aff54,0x4013cb1b,2 +np.float32,0x3f5ef158,0x3fab1801,2 +np.float32,0xbe33bcc8,0xbe359e40,2 +np.float32,0xbf04dd0e,0xbf133111,2 +np.float32,0xbf14f887,0xbf2a54d1,2 +np.float32,0x3f75c37a,0x3ff9196e,2 +np.float32,0x3f35c3c8,0x3f6320f2,2 +np.float32,0x3f53bb94,0x3f96e3c3,2 +np.float32,0x3f4d473e,0x3f8d4a19,2 +np.float32,0xbdfe19e0,0xbdff6ac9,2 +np.float32,0xbf7f0cc4,0xc049342d,2 +np.float32,0xbdbfc778,0xbdc057bb,2 +np.float32,0xbf7575b7,0xbff73067,2 +np.float32,0xbe9df488,0xbea34609,2 +np.float32,0xbefbd3c6,0xbf09daff,2 +np.float32,0x3f19962c,0x3f316cbd,2 +np.float32,0x3f7acec6,0x40129732,2 +np.float32,0xbf5db7de,0xbfa89a21,2 +np.float32,0x3f62f444,0x3fb3e830,2 +np.float32,0xbf522adb,0xbf94737f,2 +np.float32,0xbef6ceb2,0xbf0690ba,2 +np.float32,0xbf57c41e,0xbf9d8db0,2 +np.float32,0x3eb3360c,0x3ebb1eb0,2 +np.float32,0x3f29327e,0x3f4b618e,2 +np.float32,0xbf08d099,0xbf18a916,2 +np.float32,0x3ea21014,0x3ea7d369,2 +np.float32,0x3f39e516,0x3f6ba861,2 +np.float32,0x3e7c4f28,0x3e80ce08,2 +np.float32,0xbec5a7f8,0xbed07582,2 +np.float32,0xbf0b1b46,0xbf1be3e7,2 +np.float32,0xbef0e0ec,0xbf02bb2e,2 +np.float32,0x3d835a30,0x3d838869,2 +np.float32,0x3f08aa40,0x3f18736e,2 +np.float32,0x3eb0e4c8,0x3eb87bcd,2 +np.float32,0x3eb3821c,0x3ebb7564,2 +np.float32,0xbe3a7320,0xbe3c8d5a,2 +np.float32,0x3e43f8c0,0x3e466b10,2 +np.float32,0x3e914288,0x3e955b69,2 +np.float32,0x3ec7d800,0x3ed308e7,2 +np.float32,0x3e603df8,0x3e63eef2,2 +np.float32,0x3f225cac,0x3f3f9ac6,2 +np.float32,0x3e3db8f0,0x3e3ff06b,2 +np.float32,0x3f358d78,0x3f62b38c,2 +np.float32,0xbed9bd64,0xbee88158,2 +np.float32,0x800000,0x800000,2 +np.float32,0x3f1adfce,0x3f337230,2 +np.float32,0xbefdc346,0xbf0b229d,2 +np.float32,0xbf091018,0xbf190208,2 +np.float32,0xbf800000,0xff800000,2 +np.float32,0x3f27c2c4,0x3f48d8db,2 +np.float32,0x3ef59c80,0x3f05c993,2 +np.float32,0x3e18a340,0x3e19c893,2 +np.float32,0x3f209610,0x3f3ca7c5,2 +np.float32,0x3f69cc22,0x3fc60087,2 +np.float32,0xbf66cf07,0xbfbd8721,2 +np.float32,0xbf768098,0xbffdfcc4,2 +np.float32,0x3df27a40,0x3df39ec4,2 +np.float32,0x3daf5bd0,0x3dafca02,2 +np.float32,0x3f53f2be,0x3f973b41,2 +np.float32,0xbf7edcbc,0xc0436ce3,2 +np.float32,0xbdf61db8,0xbdf74fae,2 +np.float32,0x3e2c9328,0x3e2e3cb2,2 +np.float32,0x3f1a4570,0x3f327f41,2 +np.float32,0xbf766306,0xbffd32f1,2 +np.float32,0xbf468b9d,0xbf845f0f,2 +np.float32,0x3e398970,0x3e3b9bb1,2 +np.float32,0xbbefa900,0xbbefaa18,2 +np.float32,0xbf54c989,0xbf9893ad,2 +np.float32,0x3f262cf6,0x3f46169d,2 +np.float32,0x3f638a8a,0x3fb54a98,2 +np.float32,0xbeb36c78,0xbebb5cb8,2 +np.float32,0xbeac4d42,0xbeb34993,2 +np.float32,0x3f1d1942,0x3f36fbf2,2 +np.float32,0xbf5d49ba,0xbfa7bf07,2 +np.float32,0xbf182b5c,0xbf2f38d0,2 +np.float32,0x3f41a742,0x3f7ce5ef,2 +np.float32,0x3f0b9a6c,0x3f1c9898,2 +np.float32,0x3e847494,0x3e8788f3,2 +np.float32,0xbde41608,0xbde50941,2 +np.float32,0x3f693944,0x3fc44b5a,2 +np.float32,0x3f0386b2,0x3f115e37,2 +np.float32,0x3f3a08b0,0x3f6bf3c1,2 +np.float32,0xbf78ee64,0xc0089977,2 +np.float32,0xbf013a11,0xbf0e436e,2 +np.float32,0x3f00668e,0x3f0d2836,2 +np.float32,0x3e6d9850,0x3e720081,2 +np.float32,0x3eacf578,0x3eb4075d,2 +np.float32,0x3f18aef8,0x3f3004b4,2 +np.float32,0x3de342f0,0x3de43385,2 +np.float32,0x3e56cee8,0x3e5a0b85,2 +np.float32,0xbf287912,0xbf4a1966,2 +np.float32,0x3e92c948,0x3e9704c2,2 +np.float32,0x3c07d080,0x3c07d14c,2 +np.float32,0xbe90f6a0,0xbe9508e0,2 +np.float32,0x3e8b4f28,0x3e8ee884,2 +np.float32,0xbf35b56c,0xbf6303ff,2 +np.float32,0xbef512b8,0xbf057027,2 +np.float32,0x3e36c630,0x3e38c0cd,2 +np.float32,0x3f0b3ca8,0x3f1c134a,2 +np.float32,0x3e4cd610,0x3e4fa2c5,2 +np.float32,0xbf5a8372,0xbfa273a3,2 +np.float32,0xbecaad3c,0xbed662ae,2 +np.float32,0xbec372d2,0xbecddeac,2 +np.float32,0x3f6fb2b2,0x3fda8a22,2 +np.float32,0x3f365f28,0x3f645b5a,2 +np.float32,0xbecd00fa,0xbed926a4,2 +np.float32,0xbebafa32,0xbec40672,2 +np.float32,0xbf235b73,0xbf4146c4,2 +np.float32,0x3f7a4658,0x400f6e2c,2 +np.float32,0x3f35e824,0x3f636a54,2 +np.float32,0x3cb87640,0x3cb87e3c,2 +np.float32,0xbf296288,0xbf4bb6ee,2 +np.float32,0x7f800000,0xffc00000,2 +np.float32,0xbf4de86e,0xbf8e2d1a,2 +np.float32,0xbf4ace12,0xbf89e5f3,2 +np.float32,0x3d65a300,0x3d65e0b5,2 +np.float32,0xbe10c534,0xbe11bf21,2 +np.float32,0xbeba3c1c,0xbec32b3e,2 +np.float32,0x3e87eaf8,0x3e8b40b8,2 +np.float32,0x3d5c3bc0,0x3d5c722d,2 +np.float32,0x3e8c14b8,0x3e8fbdf8,2 +np.float32,0xbf06c6f0,0xbf15d327,2 +np.float32,0xbe0f1e30,0xbe100f96,2 +np.float32,0xbee244b0,0xbef30251,2 +np.float32,0x3f2a21b0,0x3f4d0c1d,2 +np.float32,0xbf5f7f81,0xbfac408e,2 +np.float32,0xbe3dba2c,0xbe3ff1b2,2 +np.float32,0x3f3ffc22,0x3f790abf,2 +np.float32,0x3edc3dac,0x3eeb90fd,2 +np.float32,0x7f7fffff,0xffc00000,2 +np.float32,0x3ecfaaac,0x3edc5485,2 +np.float32,0x3f0affbe,0x3f1bbcd9,2 +np.float32,0x3f5f2264,0x3fab7dca,2 +np.float32,0x3f37394c,0x3f66186c,2 +np.float32,0xbe6b2f6c,0xbe6f74e3,2 +np.float32,0x3f284772,0x3f49c1f1,2 +np.float32,0xbdf27bc8,0xbdf3a051,2 +np.float32,0xbc8b14e0,0xbc8b184c,2 +np.float32,0x3f6a867c,0x3fc83b07,2 +np.float32,0x3f1ec876,0x3f39b429,2 +np.float32,0x3f6fd9a8,0x3fdb28d6,2 +np.float32,0xbf473cca,0xbf853e8c,2 +np.float32,0x3e23eff8,0x3e255c23,2 +np.float32,0x3ebefdfc,0x3ec8ac5d,2 +np.float32,0x3f6c8c22,0x3fced2b1,2 +np.float32,0x3f168388,0x3f2cad44,2 +np.float32,0xbece2410,0xbeda81ac,2 +np.float32,0x3f5532f0,0x3f993eea,2 +np.float32,0x3ef1938c,0x3f032dfa,2 +np.float32,0xbef05268,0xbf025fba,2 +np.float32,0x3f552e4a,0x3f993754,2 +np.float32,0x3e9ed068,0x3ea4392d,2 +np.float32,0xbe1a0c24,0xbe1b39be,2 +np.float32,0xbf2623aa,0xbf46068c,2 +np.float32,0xbe1cc300,0xbe1e00fc,2 +np.float32,0xbe9c0576,0xbea12397,2 +np.float32,0xbd827338,0xbd82a07e,2 +np.float32,0x3f0fc31a,0x3f229786,2 +np.float32,0x3e577810,0x3e5abc7d,2 +np.float32,0x3e0e1cb8,0x3e0f0906,2 +np.float32,0x3e84d344,0x3e87ee73,2 +np.float32,0xbf39c45e,0xbf6b6337,2 +np.float32,0x3edfb25c,0x3eefd273,2 +np.float32,0x3e016398,0x3e021596,2 +np.float32,0xbefeb1be,0xbf0bc0de,2 +np.float32,0x3f37e104,0x3f677196,2 +np.float32,0x3f545316,0x3f97d500,2 +np.float32,0xbefc165a,0xbf0a06ed,2 +np.float32,0xbf0923e6,0xbf191dcd,2 +np.float32,0xbf386508,0xbf68831f,2 +np.float32,0xbf3d4630,0xbf72f4e1,2 +np.float32,0x3f3dbe82,0x3f73ff13,2 +np.float32,0xbf703de4,0xbfdcc7e2,2 +np.float32,0xbf531482,0xbf95dd1a,2 +np.float32,0xbf0af1b6,0xbf1ba8f4,2 +np.float32,0xbec8fd9c,0xbed463a4,2 +np.float32,0xbe230320,0xbe24691a,2 +np.float32,0xbf7de541,0xc02faf38,2 +np.float32,0x3efd2360,0x3f0ab8b7,2 +np.float32,0x3db7f350,0x3db87291,2 +np.float32,0x3e74c510,0x3e799924,2 +np.float32,0x3da549c0,0x3da5a5fc,2 +np.float32,0x3e8a3bc4,0x3e8dbf4a,2 +np.float32,0xbf69f086,0xbfc66e84,2 +np.float32,0x3f323f8e,0x3f5c2c17,2 +np.float32,0x3ec0ae3c,0x3ecaa334,2 +np.float32,0xbebe8966,0xbec824fc,2 +np.float32,0x3f34691e,0x3f606b13,2 +np.float32,0x3f13790e,0x3f2813f5,2 +np.float32,0xbf61c027,0xbfb12618,2 +np.float32,0x3e90c690,0x3e94d4a1,2 +np.float32,0xbefce8f0,0xbf0a920e,2 +np.float32,0xbf5c0e8a,0xbfa559a7,2 +np.float32,0x3f374f60,0x3f6645b6,2 +np.float32,0x3f25f6fa,0x3f45b967,2 +np.float32,0x3f2421aa,0x3f42963a,2 +np.float32,0x3ebfa328,0x3ec96c57,2 +np.float32,0x3e3bef28,0x3e3e1685,2 +np.float32,0x3ea3fa3c,0x3ea9f4dd,2 +np.float32,0x3f362b8e,0x3f63f2b2,2 +np.float32,0xbedcef18,0xbeec6ada,2 +np.float32,0xbdd29c88,0xbdd35bd0,2 +np.float32,0x3f261aea,0x3f45f76f,2 +np.float32,0xbe62c470,0xbe66965e,2 +np.float32,0x7fc00000,0x7fc00000,2 +np.float32,0xbee991aa,0xbefc277b,2 +np.float32,0xbf571960,0xbf9c6923,2 +np.float32,0xbe6fb410,0xbe743b41,2 +np.float32,0x3eb1bed0,0x3eb9738d,2 +np.float32,0x80000000,0x80000000,2 +np.float32,0x3eddcbe4,0x3eed7a69,2 +np.float32,0xbf2a81ba,0xbf4db86d,2 +np.float32,0x3f74da54,0x3ff38737,2 +np.float32,0xbeb6bff4,0xbebf29f4,2 +np.float32,0x3f445752,0x3f81a698,2 +np.float32,0x3ed081b4,0x3edd5618,2 +np.float32,0xbee73802,0xbef931b4,2 +np.float32,0xbd13f2a0,0xbd14031c,2 +np.float32,0xbb4d1200,0xbb4d122c,2 +np.float32,0xbee8777a,0xbefac393,2 +np.float32,0x3f42047c,0x3f7dc06c,2 +np.float32,0xbd089270,0xbd089f67,2 +np.float32,0xbf628c16,0xbfb2f66b,2 +np.float32,0x3e72e098,0x3e77978d,2 +np.float32,0x3ed967cc,0x3ee818e4,2 +np.float32,0x3e284c80,0x3e29d6d9,2 +np.float32,0x3f74e8ba,0x3ff3dbef,2 +np.float32,0x3f013e86,0x3f0e4969,2 +np.float32,0xbf610d4f,0xbfaf983c,2 +np.float32,0xbf3c8d36,0xbf715eba,2 +np.float32,0xbedbc756,0xbeeaffdb,2 +np.float32,0x3e143ec8,0x3e154b4c,2 +np.float32,0xbe1c9808,0xbe1dd4fc,2 +np.float32,0xbe887a1e,0xbe8bdac5,2 +np.float32,0xbe85c4bc,0xbe88f17a,2 +np.float32,0x3f35967e,0x3f62c5b4,2 +np.float32,0x3ea2c4a4,0x3ea89c2d,2 +np.float32,0xbc8703c0,0xbc8706e1,2 +np.float32,0xbf13d52c,0xbf289dff,2 +np.float32,0xbf63bb56,0xbfb5bf29,2 +np.float32,0xbf61c5ef,0xbfb13319,2 +np.float32,0xbf128410,0xbf26a675,2 +np.float32,0x3f03fcf2,0x3f11ff13,2 +np.float32,0xbe49c924,0xbe4c75cd,2 +np.float32,0xbf211a9c,0xbf3d82c5,2 +np.float32,0x3f7e9d52,0x403d1b42,2 +np.float32,0x3edfefd4,0x3ef01e71,2 +np.float32,0x3ebc5bd8,0x3ec59efb,2 +np.float32,0x3d7b02e0,0x3d7b537f,2 +np.float32,0xbf1163ba,0xbf24fb43,2 +np.float32,0x3f5072f2,0x3f91dbf1,2 +np.float32,0xbee700ce,0xbef8ec60,2 +np.float32,0x3f534168,0x3f962359,2 +np.float32,0x3e6d6c40,0x3e71d1ef,2 +np.float32,0x3def9d70,0x3df0b7a8,2 +np.float32,0x3e89cf80,0x3e8d4a8a,2 +np.float32,0xbf687ca7,0xbfc2290f,2 +np.float32,0x3f35e134,0x3f635c51,2 +np.float32,0x3e59eef8,0x3e5d50fa,2 +np.float32,0xbf65c9e1,0xbfbada61,2 +np.float32,0xbf759292,0xbff7e43d,2 +np.float32,0x3f4635a0,0x3f83f372,2 +np.float32,0x3f29baaa,0x3f4c53f1,2 +np.float32,0x3f6b15a6,0x3fc9fe04,2 +np.float32,0x3edabc88,0x3ee9b922,2 +np.float32,0x3ef382e0,0x3f046d4d,2 +np.float32,0xbe351310,0xbe36ff7f,2 +np.float32,0xbf05c935,0xbf14751c,2 +np.float32,0xbf0e7c50,0xbf20bc24,2 +np.float32,0xbf69bc94,0xbfc5d1b8,2 +np.float32,0xbed41aca,0xbee1aa23,2 +np.float32,0x3f518c08,0x3f938162,2 +np.float32,0xbf3d7974,0xbf73661a,2 +np.float32,0x3f1951a6,0x3f3101c9,2 +np.float32,0xbeb3f436,0xbebbf787,2 +np.float32,0xbf77a190,0xc0031d43,2 +np.float32,0x3eb5b3cc,0x3ebdf6e7,2 +np.float32,0xbed534b4,0xbee2fed2,2 +np.float32,0xbe53e1b8,0xbe56fc56,2 +np.float32,0x3f679e20,0x3fbfb91c,2 +np.float32,0xff7fffff,0xffc00000,2 +np.float32,0xbf7b9bcb,0xc0180073,2 +np.float32,0xbf5635e8,0xbf9aea15,2 +np.float32,0xbe5a3318,0xbe5d9856,2 +np.float32,0xbe003284,0xbe00df9a,2 +np.float32,0x3eb119a4,0x3eb8b7d6,2 +np.float32,0xbf3bccf8,0xbf6fbc84,2 +np.float32,0x3f36f600,0x3f658ea8,2 +np.float32,0x3f1ea834,0x3f397fc2,2 +np.float32,0xbe7cfb54,0xbe8129b3,2 +np.float32,0xbe9b3746,0xbea0406a,2 +np.float32,0x3edc0f90,0x3eeb586c,2 +np.float32,0x3e1842e8,0x3e19660c,2 +np.float32,0xbd8f10b0,0xbd8f4c70,2 +np.float32,0xbf064aca,0xbf1527a2,2 +np.float32,0x3e632e58,0x3e6705be,2 +np.float32,0xbef28ba4,0xbf03cdbb,2 +np.float32,0x3f27b21e,0x3f48bbaf,2 +np.float32,0xbe6f30d4,0xbe73b06e,2 +np.float32,0x3f3e6cb0,0x3f75834b,2 +np.float32,0xbf264aa5,0xbf4649f0,2 +np.float32,0xbf690775,0xbfc3b978,2 +np.float32,0xbf3e4a38,0xbf753632,2 +np.float64,0x3fe12bbe8c62577e,0x3fe32de8e5f961b0,2 +np.float64,0x3fc9b8909b337120,0x3fca1366da00efff,2 +np.float64,0x3feaee4245f5dc84,0x3ff3a011ea0432f3,2 +np.float64,0xbfe892c000f12580,0xbff03e5adaed6f0c,2 +np.float64,0xbf9be8de4837d1c0,0xbf9beaa367756bd1,2 +np.float64,0x3fe632e58fec65cc,0x3feb5ccc5114ca38,2 +np.float64,0x3fe78a0ef7ef141e,0x3fee1b4521d8eb6c,2 +np.float64,0x3feec27a65fd84f4,0x3fff643c8318e81e,2 +np.float64,0x3fbed6efce3dade0,0x3fbefd76cff00111,2 +np.float64,0xbfe3a05fab6740c0,0xbfe6db078aeeb0ca,2 +np.float64,0x3fdca11a56b94234,0x3fdece9e6eacff1b,2 +np.float64,0x3fe0fb15aae1f62c,0x3fe2e9e095ec2089,2 +np.float64,0x3fede12abf7bc256,0x3ffafd0ff4142807,2 +np.float64,0x3feb919edcf7233e,0x3ff4c9aa0bc2432f,2 +np.float64,0x3fd39633b5a72c68,0x3fd43c2e6d5f441c,2 +np.float64,0x3fd9efcbfeb3df98,0x3fdb83f03e58f91c,2 +np.float64,0x3fe2867a36650cf4,0x3fe525858c8ce72e,2 +np.float64,0x3fdacbb8f3b59770,0x3fdc8cd431b6e3ff,2 +np.float64,0x3fcc120503382408,0x3fcc88a8fa43e1c6,2 +np.float64,0xbfd99ff4eab33fea,0xbfdb24a20ae3687d,2 +np.float64,0xbfe8caf0157195e0,0xbff083b8dd0941d3,2 +np.float64,0x3fddc9bf92bb9380,0x3fe022aac0f761d5,2 +np.float64,0x3fe2dbb66e65b76c,0x3fe5a6e7caf3f1f2,2 +np.float64,0x3fe95f5c4a72beb8,0x3ff1444697e96138,2 +np.float64,0xbfc6b163d92d62c8,0xbfc6ef6e006658a1,2 +np.float64,0x3fdf1b2616be364c,0x3fe0fcbd2848c9e8,2 +np.float64,0xbfdca1ccf7b9439a,0xbfdecf7dc0eaa663,2 +np.float64,0x3fe078d6a260f1ae,0x3fe236a7c66ef6c2,2 +np.float64,0x3fdf471bb9be8e38,0x3fe11990ec74e704,2 +np.float64,0xbfe417626be82ec5,0xbfe79c9aa5ed2e2f,2 +np.float64,0xbfeb9cf5677739eb,0xbff4dfc24c012c90,2 +np.float64,0x3f8d9142b03b2280,0x3f8d91c9559d4779,2 +np.float64,0x3fb052c67220a590,0x3fb05873c90d1cd6,2 +np.float64,0x3fd742e2c7ae85c4,0x3fd860128947d15d,2 +np.float64,0x3fec2e2a2bf85c54,0x3ff60eb554bb8d71,2 +np.float64,0xbfeb2b8bc8f65718,0xbff40b734679497a,2 +np.float64,0x3fe25f8e0d64bf1c,0x3fe4eb381d077803,2 +np.float64,0x3fe56426256ac84c,0x3fe9dafbe79370f0,2 +np.float64,0x3feecc1e5d7d983c,0x3fffa49bedc7aa25,2 +np.float64,0xbfc88ce94b3119d4,0xbfc8dbba0fdee2d2,2 +np.float64,0xbfabcf51ac379ea0,0xbfabd6552aa63da3,2 +np.float64,0xbfccc8b849399170,0xbfcd48d6ff057a4d,2 +np.float64,0x3fd2f831e8a5f064,0x3fd38e67b0dda905,2 +np.float64,0x3fcafdcd6135fb98,0x3fcb670ae2ef4d36,2 +np.float64,0x3feda6042efb4c08,0x3ffa219442ac4ea5,2 +np.float64,0x3fed382b157a7056,0x3ff8bc01bc6d10bc,2 +np.float64,0x3fed858a50fb0b14,0x3ff9b1c05cb6cc0f,2 +np.float64,0x3fcc3960653872c0,0x3fccb2045373a3d1,2 +np.float64,0xbfec5177e478a2f0,0xbff65eb4557d94eb,2 +np.float64,0x3feafe0d5e75fc1a,0x3ff3bb4a260a0dcb,2 +np.float64,0x3fe08bc87ee11790,0x3fe25078aac99d31,2 +np.float64,0xffefffffffffffff,0xfff8000000000000,2 +np.float64,0x3f79985ce0333100,0x3f799872b591d1cb,2 +np.float64,0xbfd4001cf9a8003a,0xbfd4b14b9035b94f,2 +np.float64,0x3fe54a17e6ea9430,0x3fe9ac0f18682343,2 +np.float64,0xbfb4e07fea29c100,0xbfb4ec6520dd0689,2 +np.float64,0xbfed2b6659fa56cd,0xbff895ed57dc1450,2 +np.float64,0xbfe81fc8b5f03f92,0xbfef6b95e72a7a7c,2 +np.float64,0xbfe6aced16ed59da,0xbfec4ce131ee3704,2 +np.float64,0xbfe599f30ceb33e6,0xbfea3d07c1cd78e2,2 +np.float64,0xbfe0ff278b61fe4f,0xbfe2ef8b5efa89ed,2 +np.float64,0xbfe3e9406467d281,0xbfe750e43e841736,2 +np.float64,0x3fcc6b52cf38d6a8,0x3fcce688f4fb2cf1,2 +np.float64,0xbfc890e8133121d0,0xbfc8dfdfee72d258,2 +np.float64,0x3fe46e81dbe8dd04,0x3fe82e09783811a8,2 +np.float64,0x3fd94455e5b288ac,0x3fdab7cef2de0b1f,2 +np.float64,0xbfe82151fff042a4,0xbfef6f254c9696ca,2 +np.float64,0x3fcee1ac1d3dc358,0x3fcf80a6ed07070a,2 +np.float64,0x3fcce8f90939d1f0,0x3fcd6ad18d34f8b5,2 +np.float64,0x3fd6afe56fad5fcc,0x3fd7b7567526b1fb,2 +np.float64,0x3fb1a77092234ee0,0x3fb1ae9fe0d176fc,2 +np.float64,0xbfeb758b0d76eb16,0xbff493d105652edc,2 +np.float64,0xbfb857c24e30af88,0xbfb86aa4da3be53f,2 +np.float64,0x3fe89064eff120ca,0x3ff03b7c5b3339a8,2 +np.float64,0xbfc1bd2fef237a60,0xbfc1da99893473ed,2 +np.float64,0xbfe5ad6e2eeb5adc,0xbfea60ed181b5c05,2 +np.float64,0x3fd5a66358ab4cc8,0x3fd6899e640aeb1f,2 +np.float64,0xbfe198e832e331d0,0xbfe3c8c9496d0de5,2 +np.float64,0xbfdaa5c0d7b54b82,0xbfdc5ed7d3c5ce49,2 +np.float64,0x3fcceccb6939d998,0x3fcd6ed88c2dd3a5,2 +np.float64,0xbfe44413eae88828,0xbfe7e6cd32b34046,2 +np.float64,0xbfc7cbeccf2f97d8,0xbfc8139a2626edae,2 +np.float64,0x3fbf31e4fa3e63d0,0x3fbf59c6e863255e,2 +np.float64,0x3fdf03fa05be07f4,0x3fe0ed953f7989ad,2 +np.float64,0x3fe7f4eaceefe9d6,0x3fef092ca7e2ac39,2 +np.float64,0xbfc084e9d92109d4,0xbfc09ca10fd6aaea,2 +np.float64,0xbf88cfbf70319f80,0xbf88d00effa6d897,2 +np.float64,0x7ff4000000000000,0x7ffc000000000000,2 +np.float64,0xbfa0176e9c202ee0,0xbfa018ca0a6ceef3,2 +np.float64,0xbfd88d0815b11a10,0xbfd9dfc6c6bcbe4e,2 +np.float64,0x3fe89f7730713eee,0x3ff04de52fb536f3,2 +np.float64,0xbfedc9707bfb92e1,0xbffaa25fcf9dd6da,2 +np.float64,0x3fe936d1a6726da4,0x3ff10e40c2d94bc9,2 +np.float64,0x3fdb64aec7b6c95c,0x3fdd473177317b3f,2 +np.float64,0xbfee4f9aaefc9f35,0xbffcdd212667003c,2 +np.float64,0x3fe3730067e6e600,0x3fe692b0a0babf5f,2 +np.float64,0xbfc257e58924afcc,0xbfc27871f8c218d7,2 +np.float64,0x3fe62db12dec5b62,0x3feb52c61b97d9f6,2 +np.float64,0xbfe3ff491367fe92,0xbfe774f1b3a96fd6,2 +np.float64,0x3fea43255274864a,0x3ff28b0c4b7b8d21,2 +np.float64,0xbfea37923c746f24,0xbff27962159f2072,2 +np.float64,0x3fcd0ac3c73a1588,0x3fcd8e6f8de41755,2 +np.float64,0xbfdccafde6b995fc,0xbfdf030fea8a0630,2 +np.float64,0x3fdba35268b746a4,0x3fdd94094f6f50c1,2 +np.float64,0x3fc68ea1d92d1d40,0x3fc6cb8d07cbb0e4,2 +np.float64,0xbfb88b1f6e311640,0xbfb89e7af4e58778,2 +np.float64,0xbfedc7cadffb8f96,0xbffa9c3766227956,2 +np.float64,0x3fe7928d3eef251a,0x3fee2dcf2ac7961b,2 +np.float64,0xbfeff42ede7fe85e,0xc00cef6b0f1e8323,2 +np.float64,0xbfebf07fa477e0ff,0xbff5893f99e15236,2 +np.float64,0x3fe3002ab9660056,0x3fe5defba550c583,2 +np.float64,0x3feb8f4307f71e86,0x3ff4c517ec8d6de9,2 +np.float64,0x3fd3c16f49a782e0,0x3fd46becaacf74da,2 +np.float64,0x3fc7613df12ec278,0x3fc7a52b2a3c3368,2 +np.float64,0xbfe33af560e675eb,0xbfe63a6528ff1587,2 +np.float64,0xbfde86495abd0c92,0xbfe09bd7ba05b461,2 +np.float64,0x3fe1e7fb4ee3cff6,0x3fe43b04311c0ab6,2 +np.float64,0xbfc528b6bd2a516c,0xbfc55ae0a0c184c8,2 +np.float64,0xbfd81025beb0204c,0xbfd94dd72d804613,2 +np.float64,0x10000000000000,0x10000000000000,2 +np.float64,0x3fc1151c47222a38,0x3fc12f5aad80a6bf,2 +np.float64,0x3feafa136775f426,0x3ff3b46854da0b3a,2 +np.float64,0x3fed2da0747a5b40,0x3ff89c85b658459e,2 +np.float64,0x3fda2a4b51b45498,0x3fdbca0d908ddbbd,2 +np.float64,0xbfd04cf518a099ea,0xbfd0aae0033b9e4c,2 +np.float64,0xbfb9065586320ca8,0xbfb91adb7e31f322,2 +np.float64,0xbfd830b428b06168,0xbfd973ca3c484d8d,2 +np.float64,0x3fc952f7ed32a5f0,0x3fc9a9994561fc1a,2 +np.float64,0xbfeb06c83c760d90,0xbff3ca77b326df20,2 +np.float64,0xbfeb1c98ac763931,0xbff3f0d0900f6149,2 +np.float64,0x3fdf061dbebe0c3c,0x3fe0eefb32b48d17,2 +np.float64,0xbf9acbaf28359760,0xbf9acd4024be9fec,2 +np.float64,0x3fec0adde2f815bc,0x3ff5c1628423794d,2 +np.float64,0xbfc4bc750d2978ec,0xbfc4eba43f590b94,2 +np.float64,0x3fdbe47878b7c8f0,0x3fdde44a2b500d73,2 +np.float64,0x3fe160d18162c1a4,0x3fe378cff08f18f0,2 +np.float64,0x3fc3b58dfd276b18,0x3fc3de01d3802de9,2 +np.float64,0x3fa860343430c060,0x3fa864ecd07ec962,2 +np.float64,0x3fcaebfb4b35d7f8,0x3fcb546512d1b4c7,2 +np.float64,0x3fe3fda558e7fb4a,0x3fe772412e5776de,2 +np.float64,0xbfe8169f2c702d3e,0xbfef5666c9a10f6d,2 +np.float64,0x3feda78e9efb4f1e,0x3ffa270712ded769,2 +np.float64,0xbfda483161b49062,0xbfdbedfbf2e850ba,2 +np.float64,0x3fd7407cf3ae80f8,0x3fd85d4f52622743,2 +np.float64,0xbfd63de4d4ac7bca,0xbfd73550a33e3c32,2 +np.float64,0xbfd9c30b90b38618,0xbfdb4e7695c856f3,2 +np.float64,0x3fcd70c00b3ae180,0x3fcdfa0969e0a119,2 +np.float64,0x3feb4f127f769e24,0x3ff44bf42514e0f4,2 +np.float64,0xbfec1db44af83b69,0xbff5ea54aed1f8e9,2 +np.float64,0x3fd68ff051ad1fe0,0x3fd792d0ed6d6122,2 +np.float64,0x3fe0a048a5614092,0x3fe26c80a826b2a2,2 +np.float64,0x3fd59f3742ab3e70,0x3fd6818563fcaf80,2 +np.float64,0x3fca26ecf9344dd8,0x3fca867ceb5d7ba8,2 +np.float64,0x3fdc1d547ab83aa8,0x3fde2a9cea866484,2 +np.float64,0xbfc78df6312f1bec,0xbfc7d3719b698a39,2 +np.float64,0x3fe754e72b6ea9ce,0x3feda89ea844a2e5,2 +np.float64,0x3fe740c1a4ee8184,0x3fed7dc56ec0c425,2 +np.float64,0x3fe77566a9eeeace,0x3fedee6f408df6de,2 +np.float64,0xbfbbf5bf8e37eb80,0xbfbc126a223781b4,2 +np.float64,0xbfe0acb297615965,0xbfe27d86681ca2b5,2 +np.float64,0xbfc20a0487241408,0xbfc228f5f7d52ce8,2 +np.float64,0xfff0000000000000,0xfff8000000000000,2 +np.float64,0x3fef98a4dbff314a,0x40043cfb60bd46fa,2 +np.float64,0x3fd059102ca0b220,0x3fd0b7d2be6d7822,2 +np.float64,0x3fe89f18a1f13e32,0x3ff04d714bbbf400,2 +np.float64,0x3fd45b6275a8b6c4,0x3fd516a44a276a4b,2 +np.float64,0xbfe04463e86088c8,0xbfe1ef9dfc9f9a53,2 +np.float64,0xbfe086e279610dc5,0xbfe249c9c1040a13,2 +np.float64,0x3f89c9b110339380,0x3f89ca0a641454b5,2 +np.float64,0xbfb5f5b4322beb68,0xbfb6038dc3fd1516,2 +np.float64,0x3fe6eae76f6dd5ce,0x3feccabae04d5c14,2 +np.float64,0x3fa9ef6c9c33dee0,0x3fa9f51c9a8c8a2f,2 +np.float64,0xbfe171b45f62e368,0xbfe390ccc4c01bf6,2 +np.float64,0x3fb2999442253330,0x3fb2a1fc006804b5,2 +np.float64,0x3fd124bf04a24980,0x3fd1927abb92472d,2 +np.float64,0xbfe6e05938edc0b2,0xbfecb519ba78114f,2 +np.float64,0x3fed466ee6fa8cde,0x3ff8e75405b50490,2 +np.float64,0xbfb999aa92333358,0xbfb9afa4f19f80a2,2 +np.float64,0xbfe98969ed7312d4,0xbff17d887b0303e7,2 +np.float64,0x3fe782843e6f0508,0x3fee0adbeebe3486,2 +np.float64,0xbfe232fcc26465fa,0xbfe4a90a68d46040,2 +np.float64,0x3fd190a90fa32154,0x3fd206f56ffcdca2,2 +np.float64,0xbfc4f8b75929f170,0xbfc5298b2d4e7740,2 +np.float64,0xbfba3a63d63474c8,0xbfba520835c2fdc2,2 +np.float64,0xbfb7708eea2ee120,0xbfb781695ec17846,2 +np.float64,0x3fed9fb7a5fb3f70,0x3ffa0b717bcd1609,2 +np.float64,0xbfc1b158cd2362b0,0xbfc1ce87345f3473,2 +np.float64,0x3f963478082c6900,0x3f96355c3000953b,2 +np.float64,0x3fc5050e532a0a20,0x3fc536397f38f616,2 +np.float64,0x3fe239f9eee473f4,0x3fe4b360da3b2faa,2 +np.float64,0xbfd66bd80eacd7b0,0xbfd769a29fd784c0,2 +np.float64,0x3fc57cdad52af9b8,0x3fc5b16b937f5f72,2 +np.float64,0xbfd3c36a0aa786d4,0xbfd46e1cd0b4eddc,2 +np.float64,0x3feff433487fe866,0x400cf0ea1def3161,2 +np.float64,0xbfed5577807aaaef,0xbff915e8f6bfdf22,2 +np.float64,0xbfca0dd3eb341ba8,0xbfca6c4d11836cb6,2 +np.float64,0x7ff8000000000000,0x7ff8000000000000,2 +np.float64,0xbf974deaa82e9be0,0xbf974ef26a3130d1,2 +np.float64,0xbfe7f425e1efe84c,0xbfef076cb00d649d,2 +np.float64,0xbfe4413605e8826c,0xbfe7e20448b8a4b1,2 +np.float64,0xbfdfad202cbf5a40,0xbfe15cd9eb2be707,2 +np.float64,0xbfe43261ee6864c4,0xbfe7c952c951fe33,2 +np.float64,0xbfec141225782824,0xbff5d54d33861d98,2 +np.float64,0x3fd0f47abaa1e8f4,0x3fd15e8691a7f1c2,2 +np.float64,0x3fd378f0baa6f1e0,0x3fd41bea4a599081,2 +np.float64,0xbfb52523462a4a48,0xbfb5317fa7f436e2,2 +np.float64,0x3fcb30797d3660f0,0x3fcb9c174ea401ff,2 +np.float64,0xbfd48480dea90902,0xbfd5446e02c8b329,2 +np.float64,0xbfee4ae3ab7c95c7,0xbffcc650340ba274,2 +np.float64,0xbfeab086d075610e,0xbff3387f4e83ae26,2 +np.float64,0x3fa17cddf422f9c0,0x3fa17e9bf1b25736,2 +np.float64,0xbfe3064536e60c8a,0xbfe5e86aa5244319,2 +np.float64,0x3feb2882c5765106,0x3ff40604c7d97d44,2 +np.float64,0xbfa6923ff42d2480,0xbfa695ff57b2fc3f,2 +np.float64,0xbfa8bdbdcc317b80,0xbfa8c2ada0d94aa7,2 +np.float64,0x3fe7f16b8e6fe2d8,0x3fef013948c391a6,2 +np.float64,0x3fe4e7169f69ce2e,0x3fe8fceef835050a,2 +np.float64,0x3fed877638fb0eec,0x3ff9b83694127959,2 +np.float64,0xbfe0cc9ecf61993e,0xbfe2a978234cbde5,2 +np.float64,0xbfe977e79672efcf,0xbff16589ea494a38,2 +np.float64,0xbfe240130ae48026,0xbfe4bc69113e0d7f,2 +np.float64,0x3feb1e9b70763d36,0x3ff3f4615938a491,2 +np.float64,0xbfdf197dfcbe32fc,0xbfe0fba78a0fc816,2 +np.float64,0xbfee0f8543fc1f0a,0xbffbb9d9a4ee5387,2 +np.float64,0x3fe88d2191f11a44,0x3ff037843b5b6313,2 +np.float64,0xbfd11bb850a23770,0xbfd188c1cef40007,2 +np.float64,0xbfa1b36e9c2366e0,0xbfa1b53d1d8a8bc4,2 +np.float64,0xbfea2d70d9f45ae2,0xbff26a0629e36b3e,2 +np.float64,0xbfd9188703b2310e,0xbfda83f9ddc18348,2 +np.float64,0xbfee194894fc3291,0xbffbe3c83b61e7cb,2 +np.float64,0xbfe093b4a9e1276a,0xbfe25b4ad6f8f83d,2 +np.float64,0x3fea031489f4062a,0x3ff22accc000082e,2 +np.float64,0xbfc6c0827b2d8104,0xbfc6ff0a94326381,2 +np.float64,0x3fef5cd340feb9a6,0x4002659c5a1b34af,2 +np.float64,0x8010000000000000,0x8010000000000000,2 +np.float64,0x3fd97cb533b2f96c,0x3fdafab28aaae8e3,2 +np.float64,0x3fe2123334642466,0x3fe478bd83a8ce02,2 +np.float64,0xbfd9a69637b34d2c,0xbfdb2c87c6b6fb8c,2 +np.float64,0x3fc58def7f2b1be0,0x3fc5c2ff724a9f61,2 +np.float64,0xbfedd5da1f7babb4,0xbffad15949b7fb22,2 +np.float64,0x3fe90e92a0721d26,0x3ff0d9b64323efb8,2 +np.float64,0x3fd34b9442a69728,0x3fd3e9f8fe80654e,2 +np.float64,0xbfc5f509ab2bea14,0xbfc62d2ad325c59f,2 +np.float64,0x3feb245634f648ac,0x3ff3fe91a46acbe1,2 +np.float64,0x3fd101e539a203cc,0x3fd16cf52ae6d203,2 +np.float64,0xbfc51e9ba72a3d38,0xbfc5507d00521ba3,2 +np.float64,0x3fe5fe1683ebfc2e,0x3feaf7dd8b1f92b0,2 +np.float64,0x3fc362e59126c5c8,0x3fc389601814170b,2 +np.float64,0x3fea34dbd77469b8,0x3ff27542eb721e7e,2 +np.float64,0xbfc13ed241227da4,0xbfc159d42c0a35a9,2 +np.float64,0xbfe6df118cedbe23,0xbfecb27bb5d3f784,2 +np.float64,0x3fd92895f6b2512c,0x3fda96f5f94b625e,2 +np.float64,0xbfe7ea3aa76fd476,0xbfeef0e93939086e,2 +np.float64,0xbfc855498330aa94,0xbfc8a1ff690c9533,2 +np.float64,0x3fd9f27b3ab3e4f8,0x3fdb8726979afc3b,2 +np.float64,0x3fc65d52232cbaa8,0x3fc698ac4367afba,2 +np.float64,0x3fd1271dd0a24e3c,0x3fd195087649d54e,2 +np.float64,0xbfe983445df30689,0xbff175158b773b90,2 +np.float64,0xbfe0d9b13261b362,0xbfe2bb8908fc9e6e,2 +np.float64,0x3fd7671f2aaece40,0x3fd889dccbf21629,2 +np.float64,0x3fe748aebfee915e,0x3fed8e970d94c17d,2 +np.float64,0x3fea756e4e74eadc,0x3ff2d947ef3a54f4,2 +np.float64,0x3fde22311cbc4464,0x3fe05b4ce9df1fdd,2 +np.float64,0x3fe2b55ec1e56abe,0x3fe56c6849e3985a,2 +np.float64,0x3fed7b47437af68e,0x3ff98f8e82de99a0,2 +np.float64,0x3fec8184b179030a,0x3ff6d03aaf0135ba,2 +np.float64,0x3fc9ea825533d508,0x3fca4776d7190e71,2 +np.float64,0xbfe8ddd58b71bbab,0xbff09b770ed7bc9a,2 +np.float64,0xbfed41741bfa82e8,0xbff8d81c2a9fc615,2 +np.float64,0x3fe0a73888e14e72,0x3fe27602ad9a3726,2 +np.float64,0xbfe9d0a565f3a14b,0xbff1e1897b628f66,2 +np.float64,0x3fda12b381b42568,0x3fdbadbec22fbd5a,2 +np.float64,0x3fef0081187e0102,0x4000949eff8313c2,2 +np.float64,0x3fef6942b67ed286,0x4002b7913eb1ee76,2 +np.float64,0x3fda10f882b421f0,0x3fdbababa2d6659d,2 +np.float64,0x3fe5828971eb0512,0x3fea122b5088315a,2 +np.float64,0x3fe9d4b53ff3a96a,0x3ff1e75c148bda01,2 +np.float64,0x3fe95d246bf2ba48,0x3ff1414a61a136ec,2 +np.float64,0x3f9e575eb83caec0,0x3f9e59a4f17179e3,2 +np.float64,0x3fdb0a20b5b61440,0x3fdcd8a56178a17f,2 +np.float64,0xbfdef425e3bde84c,0xbfe0e33eeacf3861,2 +np.float64,0x3fd6afcf6bad5fa0,0x3fd7b73d47288347,2 +np.float64,0x3fe89256367124ac,0x3ff03dd9f36ce40e,2 +np.float64,0x3fe7e560fcefcac2,0x3feee5ef8688b60b,2 +np.float64,0x3fedef55e1fbdeac,0x3ffb350ee1df986b,2 +np.float64,0xbfe44b926de89725,0xbfe7f3539910c41f,2 +np.float64,0x3fc58310f32b0620,0x3fc5b7cfdba15bd0,2 +np.float64,0x3f736d256026da00,0x3f736d2eebe91a90,2 +np.float64,0x3feb012d2076025a,0x3ff3c0b5d21a7259,2 +np.float64,0xbfe466a6c468cd4e,0xbfe820c9c197601f,2 +np.float64,0x3fe1aba8aa635752,0x3fe3e3b73920f64c,2 +np.float64,0x3fe5597c336ab2f8,0x3fe9c7bc4b765b15,2 +np.float64,0x3fe1004ac5e20096,0x3fe2f12116e99821,2 +np.float64,0x3fecbc67477978ce,0x3ff76377434dbdad,2 +np.float64,0x3fe0e64515e1cc8a,0x3fe2ccf5447c1579,2 +np.float64,0x3febcfa874f79f50,0x3ff54528f0822144,2 +np.float64,0x3fc36915ed26d228,0x3fc38fb5b28d3f72,2 +np.float64,0xbfe01213e5e02428,0xbfe1ac0e1e7418f1,2 +np.float64,0x3fcd97875b3b2f10,0x3fce22fe3fc98702,2 +np.float64,0xbfe30383c5e60708,0xbfe5e427e62cc957,2 +np.float64,0xbfde339bf9bc6738,0xbfe0667f337924f5,2 +np.float64,0xbfda7c1c49b4f838,0xbfdc2c8801ce654a,2 +np.float64,0x3fb6b3489e2d6690,0x3fb6c29650387b92,2 +np.float64,0xbfe1fd4d76e3fa9b,0xbfe45a1f60077678,2 +np.float64,0xbf67c5e0402f8c00,0xbf67c5e49fce115a,2 +np.float64,0xbfd4f9aa2da9f354,0xbfd5c759603d0b9b,2 +np.float64,0x3fe83c227bf07844,0x3fefada9f1bd7fa9,2 +np.float64,0xbf97f717982fee20,0xbf97f836701a8cd5,2 +np.float64,0x3fe9688a2472d114,0x3ff150aa575e7d51,2 +np.float64,0xbfc5a9779d2b52f0,0xbfc5df56509c48b1,2 +np.float64,0xbfe958d5f472b1ac,0xbff13b813f9bee20,2 +np.float64,0xbfd7b3b944af6772,0xbfd8e276c2b2920f,2 +np.float64,0x3fed10198e7a2034,0x3ff8469c817572f0,2 +np.float64,0xbfeeecc4517dd989,0xc000472b1f858be3,2 +np.float64,0xbfdbcce47eb799c8,0xbfddc734aa67812b,2 +np.float64,0xbfd013ee24a027dc,0xbfd06df3089384ca,2 +np.float64,0xbfd215f2bfa42be6,0xbfd29774ffe26a74,2 +np.float64,0x3fdfd0ae67bfa15c,0x3fe1746e3a963a9f,2 +np.float64,0xbfc84aa10b309544,0xbfc896f0d25b723a,2 +np.float64,0xbfcd0c627d3a18c4,0xbfcd9024c73747a9,2 +np.float64,0x3fd87df6dbb0fbec,0x3fd9ce1dde757f31,2 +np.float64,0xbfdad85e05b5b0bc,0xbfdc9c2addb6ce47,2 +np.float64,0xbfee4f8977fc9f13,0xbffcdccd68e514b3,2 +np.float64,0x3fa5c290542b8520,0x3fa5c5ebdf09ca70,2 +np.float64,0xbfd7e401d2afc804,0xbfd91a7e4eb5a026,2 +np.float64,0xbfe33ff73b667fee,0xbfe6423cc6eb07d7,2 +np.float64,0x3fdfb7d6c4bf6fac,0x3fe163f2e8175177,2 +np.float64,0xbfd515d69eaa2bae,0xbfd5e6eedd6a1598,2 +np.float64,0x3fb322232e264440,0x3fb32b49d91c3cbe,2 +np.float64,0xbfe20ac39e641587,0xbfe46dd4b3803f19,2 +np.float64,0x3fe282dc18e505b8,0x3fe520152120c297,2 +np.float64,0xbfc905a4cd320b48,0xbfc95929b74865fb,2 +np.float64,0x3fe0ae3b83615c78,0x3fe27fa1dafc825b,2 +np.float64,0xbfc1bfed0f237fdc,0xbfc1dd6466225cdf,2 +np.float64,0xbfeca4d47d7949a9,0xbff72761a34fb682,2 +np.float64,0xbfe8cf8c48f19f18,0xbff0897ebc003626,2 +np.float64,0xbfe1aaf0a36355e2,0xbfe3e2ae7b17a286,2 +np.float64,0x3fe2ca442e659488,0x3fe58c3a2fb4f14a,2 +np.float64,0xbfda3c2deeb4785c,0xbfdbdf89fe96a243,2 +np.float64,0xbfdc12bfecb82580,0xbfde1d81dea3c221,2 +np.float64,0xbfe2d6d877e5adb1,0xbfe59f73e22c1fc7,2 +np.float64,0x3fe5f930636bf260,0x3feaee96a462e4de,2 +np.float64,0x3fcf3c0ea53e7820,0x3fcfe0b0f92be7e9,2 +np.float64,0xbfa5bb90f42b7720,0xbfa5bee9424004cc,2 +np.float64,0xbfe2fb3a3265f674,0xbfe5d75b988bb279,2 +np.float64,0x3fcaec7aab35d8f8,0x3fcb54ea582fff6f,2 +np.float64,0xbfd8d3228db1a646,0xbfda322297747fbc,2 +np.float64,0x3fedd2e0ad7ba5c2,0x3ffac6002b65c424,2 +np.float64,0xbfd9edeca2b3dbda,0xbfdb81b2b7785e33,2 +np.float64,0xbfef5febb17ebfd7,0xc002796b15950960,2 +np.float64,0x3fde22f787bc45f0,0x3fe05bcc624b9ba2,2 +np.float64,0xbfc716a4ab2e2d48,0xbfc758073839dd44,2 +np.float64,0xbf9bed852837db00,0xbf9bef4b2a3f3bdc,2 +np.float64,0x3fef8f88507f1f10,0x4003e5e566444571,2 +np.float64,0xbfdc1bbed6b8377e,0xbfde28a64e174e60,2 +np.float64,0x3fe02d30eae05a62,0x3fe1d064ec027cd3,2 +np.float64,0x3fd9dbb500b3b76c,0x3fdb6bea40162279,2 +np.float64,0x3fe353ff1d66a7fe,0x3fe661b3358c925e,2 +np.float64,0x3fac3ebfb4387d80,0x3fac4618effff2b0,2 +np.float64,0x3fe63cf0ba6c79e2,0x3feb7030cff5f434,2 +np.float64,0x3fd0e915f8a1d22c,0x3fd152464597b510,2 +np.float64,0xbfd36987cda6d310,0xbfd40af049d7621e,2 +np.float64,0xbfdc5b4dc7b8b69c,0xbfde7790a35da2bc,2 +np.float64,0x3feee7ff4a7dcffe,0x40003545989e07c7,2 +np.float64,0xbfeb2c8308765906,0xbff40d2e6469249e,2 +np.float64,0x3fe535a894ea6b52,0x3fe98781648550d0,2 +np.float64,0xbfef168eb9fe2d1d,0xc000f274ed3cd312,2 +np.float64,0x3fc3e2d98927c5b0,0x3fc40c6991b8900c,2 +np.float64,0xbfcd8fe3e73b1fc8,0xbfce1aec7f9b7f7d,2 +np.float64,0xbfd55d8c3aaabb18,0xbfd6378132ee4892,2 +np.float64,0xbfe424a66168494d,0xbfe7b289d72c98b3,2 +np.float64,0x3fd81af13eb035e4,0x3fd95a6a9696ab45,2 +np.float64,0xbfe3016722e602ce,0xbfe5e0e46db228cd,2 +np.float64,0x3fe9a20beff34418,0x3ff19faca17fc468,2 +np.float64,0xbfe2124bc7e42498,0xbfe478e19927e723,2 +np.float64,0x3fd96f8622b2df0c,0x3fdaeb08da6b08ae,2 +np.float64,0x3fecd6796579acf2,0x3ff7a7d02159e181,2 +np.float64,0x3fe60015df6c002c,0x3feafba6f2682a61,2 +np.float64,0x3fc7181cf72e3038,0x3fc7598c2cc3c3b4,2 +np.float64,0xbfce6e2e0b3cdc5c,0xbfcf0621b3e37115,2 +np.float64,0xbfe52a829e6a5505,0xbfe973a785980af9,2 +np.float64,0x3fed4bbac37a9776,0x3ff8f7a0e68a2bbe,2 +np.float64,0x3fabdfaacc37bf60,0x3fabe6bab42bd246,2 +np.float64,0xbfcd9598cb3b2b30,0xbfce20f3c4c2c261,2 +np.float64,0x3fd717d859ae2fb0,0x3fd82e88eca09ab1,2 +np.float64,0x3fe28ccb18e51996,0x3fe52f071d2694fd,2 +np.float64,0xbfe43f064ae87e0c,0xbfe7de5eab36b5b9,2 +np.float64,0x7fefffffffffffff,0xfff8000000000000,2 +np.float64,0xbfb39b045a273608,0xbfb3a4dd3395fdd5,2 +np.float64,0xbfb3358bae266b18,0xbfb33ece5e95970a,2 +np.float64,0xbfeeafb6717d5f6d,0xbffeec3f9695b575,2 +np.float64,0xbfe7a321afef4644,0xbfee522dd80f41f4,2 +np.float64,0x3fe3a17e5be742fc,0x3fe6dcd32af51e92,2 +np.float64,0xbfc61694bd2c2d28,0xbfc64fbbd835f6e7,2 +np.float64,0xbfd795906faf2b20,0xbfd8bf89b370655c,2 +np.float64,0xbfe4b39b59e96736,0xbfe8a3c5c645b6e3,2 +np.float64,0x3fd310af3ba62160,0x3fd3a9442e825e1c,2 +np.float64,0xbfd45198a6a8a332,0xbfd50bc10311a0a3,2 +np.float64,0x3fd0017eaaa002fc,0x3fd05a472a837999,2 +np.float64,0xbfea974d98752e9b,0xbff30f67f1835183,2 +np.float64,0xbf978f60582f1ec0,0xbf979070e1c2b59d,2 +np.float64,0x3fe1c715d4e38e2c,0x3fe40b479e1241a2,2 +np.float64,0xbfccb965cd3972cc,0xbfcd38b40c4a352d,2 +np.float64,0xbfd9897048b312e0,0xbfdb09d55624c2a3,2 +np.float64,0x3fe7f5de4befebbc,0x3fef0b56be259f9c,2 +np.float64,0x3fcc6c6d4338d8d8,0x3fcce7b20ed68a78,2 +np.float64,0xbfe63884046c7108,0xbfeb67a3b945c3ee,2 +np.float64,0xbfce64e2ad3cc9c4,0xbfcefc47fae2e81f,2 +np.float64,0x3fefeb57b27fd6b0,0x400ab2eac6321cfb,2 +np.float64,0x3fe679627e6cf2c4,0x3febe6451b6ee0c4,2 +np.float64,0x3fc5f710172bee20,0x3fc62f40f85cb040,2 +np.float64,0x3fc34975e52692e8,0x3fc36f58588c7fa2,2 +np.float64,0x3fe8a3784cf146f0,0x3ff052ced9bb9406,2 +np.float64,0x3fd11a607ca234c0,0x3fd1874f876233fe,2 +np.float64,0x3fb2d653f625aca0,0x3fb2df0f4c9633f3,2 +np.float64,0x3fe555f39eeaabe8,0x3fe9c15ee962a28c,2 +np.float64,0xbfea297e3bf452fc,0xbff264107117f709,2 +np.float64,0x3fe1581cdde2b03a,0x3fe36c79acedf99c,2 +np.float64,0x3fd4567063a8ace0,0x3fd51123dbd9106f,2 +np.float64,0x3fa3883aec271080,0x3fa38aa86ec71218,2 +np.float64,0x3fe40e5d7de81cba,0x3fe78dbb9b568850,2 +np.float64,0xbfe9a2f7347345ee,0xbff1a0f4faa05041,2 +np.float64,0x3f9eef03a83dde00,0x3f9ef16caa0c1478,2 +np.float64,0xbfcb4641d1368c84,0xbfcbb2e7ff8c266d,2 +np.float64,0xbfa8403b2c308070,0xbfa844e148b735b7,2 +np.float64,0xbfe1875cd6e30eba,0xbfe3afadc08369f5,2 +np.float64,0xbfdd3c3d26ba787a,0xbfdf919b3e296766,2 +np.float64,0x3fcd6c4c853ad898,0x3fcdf55647b518b8,2 +np.float64,0xbfe360a173e6c143,0xbfe6759eb3a08cf2,2 +np.float64,0x3fe5a13147eb4262,0x3fea4a5a060f5adb,2 +np.float64,0x3feb3cdd7af679ba,0x3ff42aae0cf61234,2 +np.float64,0x3fe5205128ea40a2,0x3fe9618f3d0c54af,2 +np.float64,0x3fce35343f3c6a68,0x3fcec9c4e612b050,2 +np.float64,0xbfc345724d268ae4,0xbfc36b3ce6338e6a,2 +np.float64,0x3fedc4fc0e7b89f8,0x3ffa91c1d775c1f7,2 +np.float64,0x3fe41fbf21683f7e,0x3fe7aa6c174a0e65,2 +np.float64,0xbfc7a1a5d32f434c,0xbfc7e7d27a4c5241,2 +np.float64,0x3fd3e33eaca7c67c,0x3fd4915264441e2f,2 +np.float64,0x3feb3f02f6f67e06,0x3ff42e942249e596,2 +np.float64,0x3fdb75fcb0b6ebf8,0x3fdd5c63f98b6275,2 +np.float64,0x3fd6476603ac8ecc,0x3fd74020b164cf38,2 +np.float64,0x3fed535372faa6a6,0x3ff90f3791821841,2 +np.float64,0x3fe8648ead70c91e,0x3ff006a62befd7ed,2 +np.float64,0x3fd0f90760a1f210,0x3fd1636b39bb1525,2 +np.float64,0xbfca052443340a48,0xbfca633d6e777ae0,2 +np.float64,0xbfa6a5e3342d4bc0,0xbfa6a9ac6a488f5f,2 +np.float64,0x3fd5598038aab300,0x3fd632f35c0c3d52,2 +np.float64,0xbfdf66218fbecc44,0xbfe12df83b19f300,2 +np.float64,0x3fe78e15b56f1c2c,0x3fee240d12489cd1,2 +np.float64,0x3fe3d6a7b3e7ad50,0x3fe7329dcf7401e2,2 +np.float64,0xbfddb8e97bbb71d2,0xbfe017ed6d55a673,2 +np.float64,0xbfd57afd55aaf5fa,0xbfd658a9607c3370,2 +np.float64,0xbfdba4c9abb74994,0xbfdd95d69e5e8814,2 +np.float64,0xbfe71d8090ee3b01,0xbfed3390be6d2eef,2 +np.float64,0xbfc738ac0f2e7158,0xbfc77b3553b7c026,2 +np.float64,0x3f873656302e6c80,0x3f873697556ae011,2 +np.float64,0x3fe559491d6ab292,0x3fe9c7603b12c608,2 +np.float64,0xbfe262776864c4ef,0xbfe4ef905dda8599,2 +np.float64,0x3fe59d8917eb3b12,0x3fea439f44b7573f,2 +np.float64,0xbfd4b5afb5a96b60,0xbfd57b4e3df4dbc8,2 +np.float64,0x3fe81158447022b0,0x3fef4a3cea3eb6a9,2 +np.float64,0xbfeb023441f60468,0xbff3c27f0fc1a4dc,2 +np.float64,0x3fefb212eaff6426,0x40055fc6d949cf44,2 +np.float64,0xbfe1300ac1e26016,0xbfe333f297a1260e,2 +np.float64,0xbfeae0a2f575c146,0xbff388d58c380b8c,2 +np.float64,0xbfeddd8e55fbbb1d,0xbffaef045b2e21d9,2 +np.float64,0x3fec7c6c1d78f8d8,0x3ff6c3ebb019a8e5,2 +np.float64,0xbfe27e071f64fc0e,0xbfe518d2ff630f33,2 +np.float64,0x8000000000000001,0x8000000000000001,2 +np.float64,0x3fc5872abf2b0e58,0x3fc5bc083105db76,2 +np.float64,0x3fe65114baeca22a,0x3feb9745b82ef15a,2 +np.float64,0xbfc783abe52f0758,0xbfc7c8cb23f93e79,2 +np.float64,0x3fe4b7a5dd696f4c,0x3fe8aab9d492f0ca,2 +np.float64,0xbf91a8e8a82351e0,0xbf91a95b6ae806f1,2 +np.float64,0xbfee482eb77c905d,0xbffcb952830e715a,2 +np.float64,0x3fba0eee2a341de0,0x3fba261d495e3a1b,2 +np.float64,0xbfeb8876ae7710ed,0xbff4b7f7f4343506,2 +np.float64,0xbfe4d29e46e9a53c,0xbfe8d9547a601ba7,2 +np.float64,0xbfe12413b8e24828,0xbfe3232656541d10,2 +np.float64,0x3fc0bd8f61217b20,0x3fc0d63f937f0aa4,2 +np.float64,0xbfd3debafda7bd76,0xbfd48c534e5329e4,2 +np.float64,0x3fc0f92de921f258,0x3fc112eb7d47349b,2 +np.float64,0xbfe576b95f6aed72,0xbfe9fca859239b3c,2 +np.float64,0x3fd10e520da21ca4,0x3fd17a546e4152f7,2 +np.float64,0x3fcef917eb3df230,0x3fcf998677a8fa8f,2 +np.float64,0x3fdfcf863abf9f0c,0x3fe173a98af1cb13,2 +np.float64,0x3fc28c4b4f251898,0x3fc2adf43792e917,2 +np.float64,0x3fceb837ad3d7070,0x3fcf54a63b7d8c5c,2 +np.float64,0x3fc0140a05202818,0x3fc029e4f75330cb,2 +np.float64,0xbfd76c3362aed866,0xbfd88fb9e790b4e8,2 +np.float64,0xbfe475300868ea60,0xbfe8395334623e1f,2 +np.float64,0x3fea70b9b4f4e174,0x3ff2d1dad92173ba,2 +np.float64,0xbfe2edbd4965db7a,0xbfe5c29449a9365d,2 +np.float64,0xbfddf86f66bbf0de,0xbfe0408439cada9b,2 +np.float64,0xbfb443cdfa288798,0xbfb44eae796ad3ea,2 +np.float64,0xbf96a8a0482d5140,0xbf96a992b6ef073b,2 +np.float64,0xbfd279db2fa4f3b6,0xbfd3043db6acbd9e,2 +np.float64,0x3fe5d99088ebb322,0x3feab30be14e1605,2 +np.float64,0xbfe1a917abe35230,0xbfe3e0063d0f5f63,2 +np.float64,0x3fc77272f52ee4e8,0x3fc7b6f8ab6f4591,2 +np.float64,0x3fd6b62146ad6c44,0x3fd7be77eef8390a,2 +np.float64,0xbfe39fd9bc673fb4,0xbfe6da30dc4eadde,2 +np.float64,0x3fe35545c066aa8c,0x3fe663b5873e4d4b,2 +np.float64,0xbfcbbeffb3377e00,0xbfcc317edf7f6992,2 +np.float64,0xbfe28a58366514b0,0xbfe52b5734579ffa,2 +np.float64,0xbfbf0c87023e1910,0xbfbf33d970a0dfa5,2 +np.float64,0xbfd31144cba6228a,0xbfd3a9e84f9168f9,2 +np.float64,0xbfe5c044056b8088,0xbfea83d607c1a88a,2 +np.float64,0x3fdaabdf18b557c0,0x3fdc663ee8eddc83,2 +np.float64,0xbfeb883006f71060,0xbff4b76feff615be,2 +np.float64,0xbfebaef41d775de8,0xbff5034111440754,2 +np.float64,0x3fd9b6eb3bb36dd8,0x3fdb3fff5071dacf,2 +np.float64,0x3fe4e33c45e9c678,0x3fe8f637779ddedf,2 +np.float64,0x3fe52213a06a4428,0x3fe964adeff5c14e,2 +np.float64,0x3fe799254cef324a,0x3fee3c3ecfd3cdc5,2 +np.float64,0x3fd0533f35a0a680,0x3fd0b19a003469d3,2 +np.float64,0x3fec7ef5c7f8fdec,0x3ff6ca0abe055048,2 +np.float64,0xbfd1b5da82a36bb6,0xbfd22f357acbee79,2 +np.float64,0xbfd8f9c652b1f38c,0xbfda5faacbce9cf9,2 +np.float64,0x3fc8fc818b31f900,0x3fc94fa9a6aa53c8,2 +np.float64,0x3fcf42cc613e8598,0x3fcfe7dc128f33f2,2 +np.float64,0x3fd393a995a72754,0x3fd4396127b19305,2 +np.float64,0x3fec7b7df9f8f6fc,0x3ff6c1ae51753ef2,2 +np.float64,0x3fc07f175b20fe30,0x3fc096b55c11568c,2 +np.float64,0xbf979170082f22e0,0xbf979280d9555f44,2 +np.float64,0xbfb9d110c633a220,0xbfb9e79ba19b3c4a,2 +np.float64,0x3fedcd7d417b9afa,0x3ffab19734e86d58,2 +np.float64,0xbfec116f27f822de,0xbff5cf9425cb415b,2 +np.float64,0xbfec4fa0bef89f42,0xbff65a771982c920,2 +np.float64,0x3f94d4452829a880,0x3f94d501789ad11c,2 +np.float64,0xbfefe5ede27fcbdc,0xc009c440d3c2a4ce,2 +np.float64,0xbfe7e5f7b5efcbf0,0xbfeee74449aee1db,2 +np.float64,0xbfeb71dc8976e3b9,0xbff48cd84ea54ed2,2 +np.float64,0xbfe4cdb65f699b6c,0xbfe8d0d3bce901ef,2 +np.float64,0x3fb78ef1ee2f1de0,0x3fb7a00e7d183c48,2 +np.float64,0x3fb681864a2d0310,0x3fb6906fe64b4cd7,2 +np.float64,0xbfd2ad3b31a55a76,0xbfd33c57b5985399,2 +np.float64,0x3fdcdaaa95b9b554,0x3fdf16b99628db1e,2 +np.float64,0x3fa4780b7428f020,0x3fa47ad6ce9b8081,2 +np.float64,0x3fc546b0ad2a8d60,0x3fc579b361b3b18f,2 +np.float64,0x3feaf98dd6f5f31c,0x3ff3b38189c3539c,2 +np.float64,0x3feb0b2eca76165e,0x3ff3d22797083f9a,2 +np.float64,0xbfdc02ae3ab8055c,0xbfde099ecb5dbacf,2 +np.float64,0x3fd248bf17a49180,0x3fd2ceb77b346d1d,2 +np.float64,0x3fe349d666e693ac,0x3fe651b9933a8853,2 +np.float64,0xbfca526fc534a4e0,0xbfcab3e83f0d9b93,2 +np.float64,0x3fc156421722ac88,0x3fc171b38826563b,2 +np.float64,0xbfe4244569e8488b,0xbfe7b1e93e7d4f92,2 +np.float64,0x3fe010faabe021f6,0x3fe1aa961338886d,2 +np.float64,0xbfc52dacb72a5b58,0xbfc55ffa50eba380,2 +np.float64,0x8000000000000000,0x8000000000000000,2 +np.float64,0x3fea1d4865f43a90,0x3ff251b839eb4817,2 +np.float64,0xbfa0f65c8421ecc0,0xbfa0f7f37c91be01,2 +np.float64,0x3fcab29c0b356538,0x3fcb1863edbee184,2 +np.float64,0x3fe7949162ef2922,0x3fee323821958b88,2 +np.float64,0x3fdaf9288ab5f250,0x3fdcc400190a4839,2 +np.float64,0xbfe13ece6be27d9d,0xbfe348ba07553179,2 +np.float64,0x3f8a0c4fd0341880,0x3f8a0cabdf710185,2 +np.float64,0x3fdd0442a2ba0884,0x3fdf4b016c4da452,2 +np.float64,0xbfaf06d2343e0da0,0xbfaf1090b1600422,2 +np.float64,0xbfd3b65225a76ca4,0xbfd45fa49ae76cca,2 +np.float64,0x3fef5d75fefebaec,0x400269a5e7c11891,2 +np.float64,0xbfe048e35ce091c6,0xbfe1f5af45dd64f8,2 +np.float64,0xbfe27d4599e4fa8b,0xbfe517b07843d04c,2 +np.float64,0xbfe6f2a637ede54c,0xbfecdaa730462576,2 +np.float64,0x3fc63fbb752c7f78,0x3fc67a2854974109,2 +np.float64,0x3fedda6bfbfbb4d8,0x3ffae2e6131f3475,2 +np.float64,0x3fe7a6f5286f4dea,0x3fee5a9b1ef46016,2 +np.float64,0xbfd4ea8bcea9d518,0xbfd5b66ab7e5cf00,2 +np.float64,0x3fdc116568b822cc,0x3fde1bd4d0d9fd6c,2 +np.float64,0x3fdc45cb1bb88b98,0x3fde5cd1d2751032,2 +np.float64,0x3feabd932f757b26,0x3ff34e06e56a62a1,2 +np.float64,0xbfae5dbe0c3cbb80,0xbfae66e062ac0d65,2 +np.float64,0xbfdb385a00b670b4,0xbfdd10fedf3a58a7,2 +np.float64,0xbfebb14755f7628f,0xbff507e123a2b47c,2 +np.float64,0x3fe6de2fdfedbc60,0x3fecb0ae6e131da2,2 +np.float64,0xbfd86de640b0dbcc,0xbfd9bb4dbf0bf6af,2 +np.float64,0x3fe39e86d9e73d0e,0x3fe6d811c858d5d9,2 +np.float64,0x7ff0000000000000,0xfff8000000000000,2 +np.float64,0x3fa8101684302020,0x3fa814a12176e937,2 +np.float64,0x3fefdd5ad37fbab6,0x4008a08c0b76fbb5,2 +np.float64,0x3fe645c727ec8b8e,0x3feb814ebc470940,2 +np.float64,0x3fe3ba79dce774f4,0x3fe70500db564cb6,2 +np.float64,0xbfe0e5a254e1cb44,0xbfe2cc13940c6d9a,2 +np.float64,0x3fe2cac62465958c,0x3fe58d008c5e31f8,2 +np.float64,0xbfd3ffb531a7ff6a,0xbfd4b0d88cff2040,2 +np.float64,0x3fe0929104612522,0x3fe259bc42dce788,2 +np.float64,0x1,0x1,2 +np.float64,0xbfe7db77e6efb6f0,0xbfeecf93e8a61cb3,2 +np.float64,0xbfe37e9559e6fd2a,0xbfe6a514e29cb7aa,2 +np.float64,0xbfc53a843f2a7508,0xbfc56d2e9ad8b716,2 +np.float64,0xbfedb04485fb6089,0xbffa4615d4334ec3,2 +np.float64,0xbfc44349b1288694,0xbfc46f484b6f1cd6,2 +np.float64,0xbfe265188264ca31,0xbfe4f37d61cd9e17,2 +np.float64,0xbfd030351da0606a,0xbfd08c2537287ee1,2 +np.float64,0x3fd8fb131db1f628,0x3fda613363ca601e,2 +np.float64,0xbff0000000000000,0xfff0000000000000,2 +np.float64,0xbfe48d9a60691b35,0xbfe862c02d8fec1e,2 +np.float64,0x3fd185e050a30bc0,0x3fd1fb4c614ddb07,2 +np.float64,0xbfe4a5807e694b01,0xbfe88b8ff2d6caa7,2 +np.float64,0xbfc934d7ad3269b0,0xbfc98a405d25a666,2 +np.float64,0xbfea0e3c62741c79,0xbff23b4bd3a7b15d,2 +np.float64,0x3fe7244071ee4880,0x3fed41b27ba6bb22,2 +np.float64,0xbfd419f81ba833f0,0xbfd4cdf71b4533a3,2 +np.float64,0xbfe1e73a34e3ce74,0xbfe439eb15fa6baf,2 +np.float64,0x3fcdd9a63f3bb350,0x3fce68e1c401eff0,2 +np.float64,0x3fd1b5960ba36b2c,0x3fd22eeb566f1976,2 +np.float64,0x3fe9ad18e0735a32,0x3ff1af23c534260d,2 +np.float64,0xbfd537918aaa6f24,0xbfd60ccc8df0962b,2 +np.float64,0x3fcba3d3c73747a8,0x3fcc14fd5e5c49ad,2 +np.float64,0x3fd367e3c0a6cfc8,0x3fd40921b14e288e,2 +np.float64,0x3fe94303c6f28608,0x3ff11e62db2db6ac,2 +np.float64,0xbfcc5f77fd38bef0,0xbfccda110c087519,2 +np.float64,0xbfd63b74d7ac76ea,0xbfd7328af9f37402,2 +np.float64,0xbfe5321289ea6425,0xbfe9811ce96609ad,2 +np.float64,0xbfde910879bd2210,0xbfe0a2cd0ed1d368,2 +np.float64,0xbfcc9d9bad393b38,0xbfcd1b722a0b1371,2 +np.float64,0xbfe6dd39e16dba74,0xbfecaeb7c8c069f6,2 +np.float64,0xbfe98316eff3062e,0xbff174d7347d48bf,2 +np.float64,0xbfda88f8d1b511f2,0xbfdc3c0e75dad903,2 +np.float64,0x3fd400d8c2a801b0,0x3fd4b21bacff1f5d,2 +np.float64,0xbfe1ed335863da66,0xbfe4429e45e99779,2 +np.float64,0xbf3423a200284800,0xbf3423a20acb0342,2 +np.float64,0xbfe97bc59672f78b,0xbff16ad1adc44a33,2 +np.float64,0xbfeeca60d7fd94c2,0xbfff98d7f18f7728,2 +np.float64,0x3fd1eb13b2a3d628,0x3fd268e6ff4d56ce,2 +np.float64,0xbfa5594c242ab2a0,0xbfa55c77d6740a39,2 +np.float64,0x3fe72662006e4cc4,0x3fed462a9dedbfee,2 +np.float64,0x3fef4bb221fe9764,0x4001fe4f4cdfedb2,2 +np.float64,0xbfe938d417f271a8,0xbff110e78724ca2b,2 +np.float64,0xbfcc29ab2f385358,0xbfcca182140ef541,2 +np.float64,0x3fe18cd42c6319a8,0x3fe3b77e018165e7,2 +np.float64,0xbfec6c5cae78d8b9,0xbff69d8e01309b48,2 +np.float64,0xbfd5723da7aae47c,0xbfd64ecde17da471,2 +np.float64,0xbfe3096722e612ce,0xbfe5ed43634f37ff,2 +np.float64,0xbfdacaceb1b5959e,0xbfdc8bb826bbed39,2 +np.float64,0x3fc59a57cb2b34b0,0x3fc5cfc4a7c9bac8,2 +np.float64,0x3f84adce10295b80,0x3f84adfc1f1f6e97,2 +np.float64,0x3fdd5b28bbbab650,0x3fdfb8b906d77df4,2 +np.float64,0x3fdebf94c6bd7f28,0x3fe0c10188e1bc7c,2 +np.float64,0x3fdb30c612b6618c,0x3fdd07bf18597821,2 +np.float64,0x3fe7eeb3176fdd66,0x3feefb0be694b855,2 +np.float64,0x0,0x0,2 +np.float64,0xbfe10057e9e200b0,0xbfe2f13365e5b1c9,2 +np.float64,0xbfeb61a82376c350,0xbff46e665d3a60f5,2 +np.float64,0xbfe7f54aec6fea96,0xbfef0a0759f726dc,2 +np.float64,0xbfe4f6da3de9edb4,0xbfe9187d85bd1ab5,2 +np.float64,0xbfeb8be1b3f717c4,0xbff4be8efaab2e75,2 +np.float64,0x3fed40bc31fa8178,0x3ff8d5ec4a7f3e9b,2 +np.float64,0xbfe40f8711681f0e,0xbfe78fa5c62b191b,2 +np.float64,0x3fd1034d94a2069c,0x3fd16e78e9efb85b,2 +np.float64,0x3fc74db15b2e9b60,0x3fc790f26e894098,2 +np.float64,0x3fd912a88cb22550,0x3fda7d0ab3b21308,2 +np.float64,0x3fd8948a3bb12914,0x3fd9e8950c7874c8,2 +np.float64,0xbfa7ada5242f5b50,0xbfa7b1f8db50c104,2 +np.float64,0x3feeb2e1c27d65c4,0x3fff000b7d09c9b7,2 +np.float64,0x3fe9d46cbbf3a8da,0x3ff1e6f405265a6e,2 +np.float64,0xbfe2480b77e49017,0xbfe4c83b9b37bf0c,2 +np.float64,0x3fe950ea9372a1d6,0x3ff130e62468bf2c,2 +np.float64,0x3fefa7272a7f4e4e,0x4004d8c9bf31ab58,2 +np.float64,0xbfe7309209ee6124,0xbfed5b94acef917a,2 +np.float64,0x3fd05e8c64a0bd18,0x3fd0bdb11e0903c6,2 +np.float64,0x3fd9236043b246c0,0x3fda90ccbe4bab1e,2 +np.float64,0xbfdc3d6805b87ad0,0xbfde5266e17154c3,2 +np.float64,0x3fe5e6bad76bcd76,0x3feacbc306c63445,2 +np.float64,0x3ff0000000000000,0x7ff0000000000000,2 +np.float64,0xbfde3d7390bc7ae8,0xbfe06cd480bd0196,2 +np.float64,0xbfd3e2e3c0a7c5c8,0xbfd490edc0a45e26,2 +np.float64,0x3fe39871d76730e4,0x3fe6ce54d1719953,2 +np.float64,0x3fdff00ebcbfe01c,0x3fe1894b6655a6d0,2 +np.float64,0x3f91b7ad58236f40,0x3f91b8213bcb8b0b,2 +np.float64,0xbfd99f48f7b33e92,0xbfdb23d544f62591,2 +np.float64,0x3fae3512cc3c6a20,0x3fae3e10939fd7b5,2 +np.float64,0x3fcc4cf3db3899e8,0x3fccc698a15176d6,2 +np.float64,0xbfd0927e39a124fc,0xbfd0f5522e2bc030,2 +np.float64,0x3fcee859633dd0b0,0x3fcf87bdef7a1e82,2 +np.float64,0xbfe2a8b69565516d,0xbfe5593437b6659a,2 +np.float64,0x3fecf61e20f9ec3c,0x3ff7fda16b0209d4,2 +np.float64,0xbfbf37571e3e6eb0,0xbfbf5f4e1379a64c,2 +np.float64,0xbfd54e1b75aa9c36,0xbfd626223b68971a,2 +np.float64,0x3fe1035a56e206b4,0x3fe2f5651ca0f4b0,2 +np.float64,0x3fe4992989e93254,0x3fe876751afa70dc,2 +np.float64,0x3fc8c313d3318628,0x3fc913faf15d1562,2 +np.float64,0x3f99f6ba8833ed80,0x3f99f8274fb94828,2 +np.float64,0xbfd4a58af0a94b16,0xbfd56947c276e04f,2 +np.float64,0x3fc66f8c872cdf18,0x3fc6ab7a14372a73,2 +np.float64,0x3fc41eee0d283de0,0x3fc449ff1ff0e7a6,2 +np.float64,0x3fefd04d287fa09a,0x4007585010cfa9b0,2 +np.float64,0x3fce9e746f3d3ce8,0x3fcf39514bbe5070,2 +np.float64,0xbfe8056f72700adf,0xbfef2ee2c13e67ba,2 +np.float64,0x3fdd6b1ec0bad63c,0x3fdfccf2ba144fa8,2 +np.float64,0x3fd92ee432b25dc8,0x3fda9e6b96b2b142,2 +np.float64,0xbfc4d18f9529a320,0xbfc50150fb4de0cc,2 +np.float64,0xbfe09939a7613274,0xbfe262d703c317af,2 +np.float64,0xbfd130b132a26162,0xbfd19f5a00ae29c4,2 +np.float64,0x3fa06e21d420dc40,0x3fa06f93aba415fb,2 +np.float64,0x3fc5c48fbd2b8920,0x3fc5fb3bfad3bf55,2 +np.float64,0xbfdfa2bacbbf4576,0xbfe155f839825308,2 +np.float64,0x3fe3e1fa0f67c3f4,0x3fe745081dd4fd03,2 +np.float64,0x3fdae58289b5cb04,0x3fdcac1f6789130a,2 +np.float64,0xbf8ed3ba103da780,0xbf8ed452a9cc1442,2 +np.float64,0xbfec06b46f780d69,0xbff5b86f30d70908,2 +np.float64,0xbfe990c13b732182,0xbff187a90ae611f8,2 +np.float64,0xbfdd46c738ba8d8e,0xbfdf9eee0a113230,2 +np.float64,0x3fe08b83f3611708,0x3fe2501b1c77035c,2 +np.float64,0xbfd501b65baa036c,0xbfd5d05de3fceac8,2 +np.float64,0xbfcf4fa21f3e9f44,0xbfcff5829582c0b6,2 +np.float64,0xbfefbc0bfbff7818,0xc005eca1a2c56b38,2 +np.float64,0xbfe1ba6959e374d2,0xbfe3f8f88d128ce5,2 +np.float64,0xbfd4e74ee3a9ce9e,0xbfd5b2cabeb45e6c,2 +np.float64,0xbfe77c38eaeef872,0xbfedfd332d6f1c75,2 +np.float64,0x3fa9b5e4fc336bc0,0x3fa9bb6f6b80b4af,2 +np.float64,0xbfecba63917974c7,0xbff75e44df7f8e81,2 +np.float64,0x3fd6cf17b2ad9e30,0x3fd7db0b93b7f2b5,2 diff --git a/numpy/core/tests/data/umath-validation-set-cbrt.csv b/numpy/core/tests/data/umath-validation-set-cbrt.csv new file mode 100644 index 000000000000..ad141cb4f5a2 --- /dev/null +++ b/numpy/core/tests/data/umath-validation-set-cbrt.csv @@ -0,0 +1,1429 @@ +dtype,input,output,ulperrortol +np.float32,0x3ee7054c,0x3f4459ea,2 +np.float32,0x7d1e2489,0x54095925,2 +np.float32,0x7ee5edf5,0x549b992b,2 +np.float32,0x380607,0x2a425e72,2 +np.float32,0x34a8f3,0x2a3e6603,2 +np.float32,0x3eee2844,0x3f465a45,2 +np.float32,0x59e49c,0x2a638d0a,2 +np.float32,0xbf72c77a,0xbf7b83d4,2 +np.float32,0x7f2517b4,0x54af8bf0,2 +np.float32,0x80068a69,0xa9bdfe8b,2 +np.float32,0xbe8e3578,0xbf270775,2 +np.float32,0xbe4224dc,0xbf131119,2 +np.float32,0xbe0053b8,0xbf001be2,2 +np.float32,0x70e8d,0x29c2ddc5,2 +np.float32,0xff63f7b5,0xd4c37b7f,2 +np.float32,0x3f00bbed,0x3f4b9335,2 +np.float32,0x3f135f4e,0x3f54f5d4,2 +np.float32,0xbe13a488,0xbf063d13,2 +np.float32,0x3f14ec78,0x3f55b478,2 +np.float32,0x7ec35cfb,0x54935fbf,2 +np.float32,0x7d41c589,0x5412f904,2 +np.float32,0x3ef8a16e,0x3f4937f7,2 +np.float32,0x3f5d8464,0x3f73f279,2 +np.float32,0xbeec85ac,0xbf45e5cb,2 +np.float32,0x7f11f722,0x54a87cb1,2 +np.float32,0x8032c085,0xaa3c1219,2 +np.float32,0x80544bac,0xaa5eb9f2,2 +np.float32,0x3e944a10,0x3f296065,2 +np.float32,0xbf29fe50,0xbf5f5796,2 +np.float32,0x7e204d8d,0x545b03d5,2 +np.float32,0xfe1d0254,0xd4598127,2 +np.float32,0x80523129,0xaa5cdba9,2 +np.float32,0x806315fa,0xaa6b0eaf,2 +np.float32,0x3ed3d2a4,0x3f3ec117,2 +np.float32,0x7ee15007,0x549a8cc0,2 +np.float32,0x801ffb5e,0xaa213d4f,2 +np.float32,0x807f9f4a,0xaa7fbf76,2 +np.float32,0xbe45e854,0xbf1402d3,2 +np.float32,0x3d9e2e70,0x3eda0b64,2 +np.float32,0x51f404,0x2a5ca4d7,2 +np.float32,0xbe26a8b0,0xbf0bc54d,2 +np.float32,0x22c99a,0x2a25d2a7,2 +np.float32,0xbf71248b,0xbf7af2d5,2 +np.float32,0x7219fe,0x2a76608e,2 +np.float32,0x7f16fd7d,0x54aa6610,2 +np.float32,0x80716faa,0xaa75e5b9,2 +np.float32,0xbe24f9a4,0xbf0b4c65,2 +np.float32,0x800000,0x2a800000,2 +np.float32,0x80747456,0xaa780f27,2 +np.float32,0x68f9e8,0x2a6fa035,2 +np.float32,0x3f6a297e,0x3f7880d8,2 +np.float32,0x3f28b973,0x3f5ec8f6,2 +np.float32,0x7f58c577,0x54c03a70,2 +np.float32,0x804befcc,0xaa571b4f,2 +np.float32,0x3e2be027,0x3f0d36cf,2 +np.float32,0xfe7e80a4,0xd47f7ff7,2 +np.float32,0xfe9d444a,0xd489181b,2 +np.float32,0x3db3e790,0x3ee399d6,2 +np.float32,0xbf154c3e,0xbf55e23e,2 +np.float32,0x3d1096b7,0x3ea7f4aa,2 +np.float32,0x7fc00000,0x7fc00000,2 +np.float32,0x804e2521,0xaa592c06,2 +np.float32,0xbeda2f00,0xbf40a513,2 +np.float32,0x3f191788,0x3f57ae30,2 +np.float32,0x3ed24ade,0x3f3e4b34,2 +np.float32,0x807fadb4,0xaa7fc917,2 +np.float32,0xbe0a06dc,0xbf034234,2 +np.float32,0x3f250bba,0x3f5d276d,2 +np.float32,0x7e948b00,0x548682c8,2 +np.float32,0xfe65ecdc,0xd476fed2,2 +np.float32,0x6fdbdd,0x2a74c095,2 +np.float32,0x800112de,0xa9500fa6,2 +np.float32,0xfe63225c,0xd475fdee,2 +np.float32,0x7f3d9acd,0x54b7d648,2 +np.float32,0xfc46f480,0xd3bacf87,2 +np.float32,0xfe5deaac,0xd47417ff,2 +np.float32,0x60ce53,0x2a693d93,2 +np.float32,0x6a6e2f,0x2a70ba2c,2 +np.float32,0x7f43f0f1,0x54b9dcd0,2 +np.float32,0xbf6170c9,0xbf756104,2 +np.float32,0xbe5c9f74,0xbf197852,2 +np.float32,0xff1502b0,0xd4a9a693,2 +np.float32,0x8064f6af,0xaa6c886e,2 +np.float32,0xbf380564,0xbf6552e5,2 +np.float32,0xfeb9b7dc,0xd490e85f,2 +np.float32,0x7f34f941,0x54b5010d,2 +np.float32,0xbe9d4ca0,0xbf2cbd5f,2 +np.float32,0x3f6e43d2,0x3f79f240,2 +np.float32,0xbdad0530,0xbee0a8f2,2 +np.float32,0x3da18459,0x3edb9105,2 +np.float32,0xfd968340,0xd42a3808,2 +np.float32,0x3ea03e64,0x3f2dcf96,2 +np.float32,0x801d2f5b,0xaa1c6525,2 +np.float32,0xbf47d92d,0xbf6bb7e9,2 +np.float32,0x55a6b9,0x2a5fe9fb,2 +np.float32,0x77a7c2,0x2a7a4fb8,2 +np.float32,0xfebbc16e,0xd4916f88,2 +np.float32,0x3f5d3d6e,0x3f73d86a,2 +np.float32,0xfccd2b60,0xd3edcacb,2 +np.float32,0xbd026460,0xbea244b0,2 +np.float32,0x3e55bd,0x2a4968e4,2 +np.float32,0xbe7b5708,0xbf20490d,2 +np.float32,0xfe413cf4,0xd469171f,2 +np.float32,0x7710e3,0x2a79e657,2 +np.float32,0xfc932520,0xd3d4d9ca,2 +np.float32,0xbf764a1b,0xbf7cb8aa,2 +np.float32,0x6b1923,0x2a713aca,2 +np.float32,0xfe4dcd04,0xd46e092d,2 +np.float32,0xff3085ac,0xd4b381f8,2 +np.float32,0x3f72c438,0x3f7b82b4,2 +np.float32,0xbf6f0c6e,0xbf7a3852,2 +np.float32,0x801d2b1b,0xaa1c5d8d,2 +np.float32,0x3e9db91e,0x3f2ce50d,2 +np.float32,0x3f684f9d,0x3f77d8c5,2 +np.float32,0x7dc784,0x2a7e82cc,2 +np.float32,0x7d2c88e9,0x540d64f8,2 +np.float32,0x807fb708,0xaa7fcf51,2 +np.float32,0x8003c49a,0xa99e16e0,2 +np.float32,0x3ee4f5b8,0x3f43c3ff,2 +np.float32,0xfe992c5e,0xd487e4ec,2 +np.float32,0x4b4dfa,0x2a568216,2 +np.float32,0x3d374c80,0x3eb5c6a8,2 +np.float32,0xbd3a4700,0xbeb6c15c,2 +np.float32,0xbf13cb80,0xbf5529e5,2 +np.float32,0xbe7306d4,0xbf1e7f91,2 +np.float32,0xbf800000,0xbf800000,2 +np.float32,0xbea42efe,0xbf2f394e,2 +np.float32,0x3e1981d0,0x3f07fe2c,2 +np.float32,0x3f17ea1d,0x3f572047,2 +np.float32,0x7dc1e0,0x2a7e7efe,2 +np.float32,0x80169c08,0xaa0fa320,2 +np.float32,0x3f3e1972,0x3f67d248,2 +np.float32,0xfe5d3c88,0xd473d815,2 +np.float32,0xbf677448,0xbf778aac,2 +np.float32,0x7e799b7d,0x547dd9e4,2 +np.float32,0x3f00bb2c,0x3f4b92cf,2 +np.float32,0xbeb29f9c,0xbf343798,2 +np.float32,0xbd6b7830,0xbec59a86,2 +np.float32,0x807a524a,0xaa7c282a,2 +np.float32,0xbe0a7a04,0xbf0366ab,2 +np.float32,0x80237470,0xaa26e061,2 +np.float32,0x3ccbc0f6,0x3e95744f,2 +np.float32,0x3edec6bc,0x3f41fcb6,2 +np.float32,0x3f635198,0x3f760efa,2 +np.float32,0x800eca4f,0xa9f960d8,2 +np.float32,0x3f800000,0x3f800000,2 +np.float32,0xff4eeb9e,0xd4bd456a,2 +np.float32,0x56f4e,0x29b29e70,2 +np.float32,0xff5383a0,0xd4bea95c,2 +np.float32,0x3f4c3a77,0x3f6d6d94,2 +np.float32,0x3f6c324a,0x3f79388c,2 +np.float32,0xbebdc092,0xbf37e27c,2 +np.float32,0xff258956,0xd4afb42e,2 +np.float32,0xdc78c,0x29f39012,2 +np.float32,0xbf2db06a,0xbf60f2f5,2 +np.float32,0xbe3c5808,0xbf119660,2 +np.float32,0xbf1ba866,0xbf58e0f4,2 +np.float32,0x80377640,0xaa41b79d,2 +np.float32,0x4fdc4d,0x2a5abfea,2 +np.float32,0x7f5e7560,0x54c1e516,2 +np.float32,0xfeb4d3f2,0xd48f9fde,2 +np.float32,0x3f12a622,0x3f549c7d,2 +np.float32,0x7f737ed7,0x54c7d2dc,2 +np.float32,0xa0ddc,0x29db456d,2 +np.float32,0xfe006740,0xd44b6689,2 +np.float32,0x3f17dfd4,0x3f571b6c,2 +np.float32,0x67546e,0x2a6e5dd1,2 +np.float32,0xff0d0f11,0xd4a693e2,2 +np.float32,0xbd170090,0xbeaa6738,2 +np.float32,0x5274a0,0x2a5d1806,2 +np.float32,0x3e154fe0,0x3f06be1a,2 +np.float32,0x7ddb302e,0x5440f0a7,2 +np.float32,0x3f579d10,0x3f71c2af,2 +np.float32,0xff2bc5bb,0xd4b1e20c,2 +np.float32,0xfee8fa6a,0xd49c4872,2 +np.float32,0xbea551b0,0xbf2fa07b,2 +np.float32,0xfeabc75c,0xd48d3004,2 +np.float32,0x7f50a5a8,0x54bdcbd1,2 +np.float32,0x50354b,0x2a5b110d,2 +np.float32,0x7d139f13,0x54063b6b,2 +np.float32,0xbeee1b08,0xbf465699,2 +np.float32,0xfe5e1650,0xd47427fe,2 +np.float32,0x7f7fffff,0x54cb2ff5,2 +np.float32,0xbf52ede8,0xbf6fff35,2 +np.float32,0x804bba81,0xaa56e8f1,2 +np.float32,0x6609e2,0x2a6d5e94,2 +np.float32,0x692621,0x2a6fc1d6,2 +np.float32,0xbf288bb6,0xbf5eb4d3,2 +np.float32,0x804f28c4,0xaa5a1b82,2 +np.float32,0xbdaad2a8,0xbedfb46e,2 +np.float32,0x5e04f8,0x2a66fb13,2 +np.float32,0x804c10da,0xaa573a81,2 +np.float32,0xbe412764,0xbf12d0fd,2 +np.float32,0x801c35cc,0xaa1aa250,2 +np.float32,0x6364d4,0x2a6b4cf9,2 +np.float32,0xbf6d3cea,0xbf79962f,2 +np.float32,0x7e5a9935,0x5472defb,2 +np.float32,0xbe73a38c,0xbf1ea19c,2 +np.float32,0xbd35e950,0xbeb550f2,2 +np.float32,0x46cc16,0x2a5223d6,2 +np.float32,0x3f005288,0x3f4b5b97,2 +np.float32,0x8034e8b7,0xaa3eb2be,2 +np.float32,0xbea775fc,0xbf3061cf,2 +np.float32,0xea0e9,0x29f87751,2 +np.float32,0xbf38faaf,0xbf65b89d,2 +np.float32,0xbedf3184,0xbf421bb0,2 +np.float32,0xbe04250c,0xbf015def,2 +np.float32,0x7f56dae8,0x54bfa901,2 +np.float32,0xfebe3e04,0xd492132e,2 +np.float32,0x3e4dc326,0x3f15f19e,2 +np.float32,0x803da197,0xaa48a621,2 +np.float32,0x7eeb35aa,0x549cc7c6,2 +np.float32,0xfebb3eb6,0xd4914dc0,2 +np.float32,0xfed17478,0xd496d5e2,2 +np.float32,0x80243694,0xaa280ed2,2 +np.float32,0x8017e666,0xaa1251d3,2 +np.float32,0xbf07e942,0xbf4f4a3e,2 +np.float32,0xbf578fa6,0xbf71bdab,2 +np.float32,0x7ed8d80f,0x549896b6,2 +np.float32,0x3f2277ae,0x3f5bff11,2 +np.float32,0x7e6f195b,0x547a3cd4,2 +np.float32,0xbf441559,0xbf6a3a91,2 +np.float32,0x7f1fb427,0x54ad9d8d,2 +np.float32,0x71695f,0x2a75e12d,2 +np.float32,0xbd859588,0xbece19a1,2 +np.float32,0x7f5702fc,0x54bfb4eb,2 +np.float32,0x3f040008,0x3f4d4842,2 +np.float32,0x3de00ca5,0x3ef4df89,2 +np.float32,0x3eeabb03,0x3f45658c,2 +np.float32,0x3dfe5e65,0x3eff7480,2 +np.float32,0x1,0x26a14518,2 +np.float32,0x8065e400,0xaa6d4130,2 +np.float32,0xff50e1bb,0xd4bdde07,2 +np.float32,0xbe88635a,0xbf24b7e9,2 +np.float32,0x3f46bfab,0x3f6b4908,2 +np.float32,0xbd85c3c8,0xbece3168,2 +np.float32,0xbe633f64,0xbf1afdb1,2 +np.float32,0xff2c7706,0xd4b21f2a,2 +np.float32,0xbf02816c,0xbf4c812a,2 +np.float32,0x80653aeb,0xaa6cbdab,2 +np.float32,0x3eef1d10,0x3f469e24,2 +np.float32,0x3d9944bf,0x3ed7c36a,2 +np.float32,0x1b03d4,0x2a186b2b,2 +np.float32,0x3f251b7c,0x3f5d2e76,2 +np.float32,0x3edebab0,0x3f41f937,2 +np.float32,0xfefc2148,0xd4a073ff,2 +np.float32,0x7448ee,0x2a77f051,2 +np.float32,0x3bb8a400,0x3e3637ee,2 +np.float32,0x57df36,0x2a61d527,2 +np.float32,0xfd8b9098,0xd425fccb,2 +np.float32,0x7f67627e,0x54c4744d,2 +np.float32,0x801165d7,0xaa039fba,2 +np.float32,0x53aae5,0x2a5e2bfd,2 +np.float32,0x8014012b,0xaa09e4f1,2 +np.float32,0x3f7a2d53,0x3f7e0b4b,2 +np.float32,0x3f5fb700,0x3f74c052,2 +np.float32,0x7f192a06,0x54ab366c,2 +np.float32,0x3f569611,0x3f71603b,2 +np.float32,0x25e2dc,0x2a2a9b65,2 +np.float32,0x8036465e,0xaa405342,2 +np.float32,0x804118e1,0xaa4c5785,2 +np.float32,0xbef08d3e,0xbf4703e1,2 +np.float32,0x3447e2,0x2a3df0be,2 +np.float32,0xbf2a350b,0xbf5f6f8c,2 +np.float32,0xbec87e3e,0xbf3b4a73,2 +np.float32,0xbe99a4a8,0xbf2b6412,2 +np.float32,0x2ea2ae,0x2a36d77e,2 +np.float32,0xfcb69600,0xd3e4b9e3,2 +np.float32,0x717700,0x2a75eb06,2 +np.float32,0xbf4e81ce,0xbf6e4ecc,2 +np.float32,0xbe2021ac,0xbf09ebee,2 +np.float32,0xfef94eee,0xd49fda31,2 +np.float32,0x8563e,0x29ce0015,2 +np.float32,0x7f5d0ca5,0x54c17c0f,2 +np.float32,0x3f16459a,0x3f56590f,2 +np.float32,0xbe12f7bc,0xbf0608a0,2 +np.float32,0x3f10fd3d,0x3f53ce5f,2 +np.float32,0x3ca5e1b0,0x3e8b8d96,2 +np.float32,0xbe5288e0,0xbf17181f,2 +np.float32,0xbf7360f6,0xbf7bb8c9,2 +np.float32,0x7e989d33,0x5487ba88,2 +np.float32,0x3ea7b5dc,0x3f307839,2 +np.float32,0x7e8da0c9,0x548463f0,2 +np.float32,0xfeaf7888,0xd48e3122,2 +np.float32,0x7d90402d,0x5427d321,2 +np.float32,0x72e309,0x2a76f0ee,2 +np.float32,0xbe1faa34,0xbf09c998,2 +np.float32,0xbf2b1652,0xbf5fd1f4,2 +np.float32,0x8051eb0c,0xaa5c9cca,2 +np.float32,0x7edf02bf,0x549a058e,2 +np.float32,0x7fa00000,0x7fe00000,2 +np.float32,0x3f67f873,0x3f77b9c1,2 +np.float32,0x3f276b63,0x3f5e358c,2 +np.float32,0x7eeb4bf2,0x549cccb9,2 +np.float32,0x3bfa2c,0x2a46d675,2 +np.float32,0x3e133c50,0x3f061d75,2 +np.float32,0x3ca302c0,0x3e8abe4a,2 +np.float32,0x802e152e,0xaa361dd5,2 +np.float32,0x3f504810,0x3f6efd0a,2 +np.float32,0xbf43e0b5,0xbf6a2599,2 +np.float32,0x80800000,0xaa800000,2 +np.float32,0x3f1c0980,0x3f590e03,2 +np.float32,0xbf0084f6,0xbf4b7638,2 +np.float32,0xfee72d32,0xd49be10d,2 +np.float32,0x3f3c00ed,0x3f66f763,2 +np.float32,0x80511e81,0xaa5be492,2 +np.float32,0xfdd1b8a0,0xd43e1f0d,2 +np.float32,0x7d877474,0x54245785,2 +np.float32,0x7f110bfe,0x54a82207,2 +np.float32,0xff800000,0xff800000,2 +np.float32,0x6b6a2,0x29bfa706,2 +np.float32,0xbf5bdfd9,0xbf7357b7,2 +np.float32,0x8025bfa3,0xaa2a6676,2 +np.float32,0x3a3581,0x2a44dd3a,2 +np.float32,0x542c2a,0x2a5e9e2f,2 +np.float32,0xbe1d5650,0xbf091d57,2 +np.float32,0x3e97760d,0x3f2a935e,2 +np.float32,0x7f5dcde2,0x54c1b460,2 +np.float32,0x800bde1e,0xa9e7bbaf,2 +np.float32,0x3e6b9e61,0x3f1cdf07,2 +np.float32,0x7d46c003,0x54143884,2 +np.float32,0x80073fbb,0xa9c49e67,2 +np.float32,0x503c23,0x2a5b1748,2 +np.float32,0x7eb7b070,0x549060c8,2 +np.float32,0xe9d8f,0x29f86456,2 +np.float32,0xbeedd4f0,0xbf464320,2 +np.float32,0x3f40d5d6,0x3f68eda1,2 +np.float32,0xff201f28,0xd4adc44b,2 +np.float32,0xbdf61e98,0xbefca9c7,2 +np.float32,0x3e8a0dc9,0x3f2562e3,2 +np.float32,0xbc0c0c80,0xbe515f61,2 +np.float32,0x2b3c15,0x2a3248e3,2 +np.float32,0x42a7bb,0x2a4df592,2 +np.float32,0x7f337947,0x54b480af,2 +np.float32,0xfec21db4,0xd4930f4b,2 +np.float32,0x7f4fdbf3,0x54bd8e94,2 +np.float32,0x1e2253,0x2a1e1286,2 +np.float32,0x800c4c80,0xa9ea819e,2 +np.float32,0x7e96f5b7,0x54873c88,2 +np.float32,0x7ce4e131,0x53f69ed4,2 +np.float32,0xbead8372,0xbf327b63,2 +np.float32,0x3e15ca7e,0x3f06e2f3,2 +np.float32,0xbf63e17b,0xbf7642da,2 +np.float32,0xff5bdbdb,0xd4c122f9,2 +np.float32,0x3f44411e,0x3f6a4bfd,2 +np.float32,0xfd007da0,0xd40029d2,2 +np.float32,0xbe940168,0xbf2944b7,2 +np.float32,0x80000000,0x80000000,2 +np.float32,0x3d28e356,0x3eb0e1b8,2 +np.float32,0x3eb9fcd8,0x3f36a918,2 +np.float32,0x4f6410,0x2a5a51eb,2 +np.float32,0xbdf18e30,0xbefb1775,2 +np.float32,0x32edbd,0x2a3c49e3,2 +np.float32,0x801f70a5,0xaa2052da,2 +np.float32,0x8045a045,0xaa50f98c,2 +np.float32,0xbdd6cb00,0xbef17412,2 +np.float32,0x3f118f2c,0x3f541557,2 +np.float32,0xbe65c378,0xbf1b8f95,2 +np.float32,0xfd9a9060,0xd42bbb8b,2 +np.float32,0x3f04244f,0x3f4d5b0f,2 +np.float32,0xff05214b,0xd4a3656f,2 +np.float32,0xfe342cd0,0xd463b706,2 +np.float32,0x3f3409a8,0x3f63a836,2 +np.float32,0x80205db2,0xaa21e1e5,2 +np.float32,0xbf37c982,0xbf653a03,2 +np.float32,0x3f36ce8f,0x3f64d17e,2 +np.float32,0x36ffda,0x2a412d61,2 +np.float32,0xff569752,0xd4bf94e6,2 +np.float32,0x802fdb0f,0xaa386c3a,2 +np.float32,0x7ec55a87,0x5493df71,2 +np.float32,0x7f2234c7,0x54ae847e,2 +np.float32,0xbf02df76,0xbf4cb23d,2 +np.float32,0x3d68731a,0x3ec4c156,2 +np.float32,0x8146,0x2921cd8e,2 +np.float32,0x80119364,0xaa041235,2 +np.float32,0xfe6c1c00,0xd47930b5,2 +np.float32,0x8070da44,0xaa757996,2 +np.float32,0xfefbf50c,0xd4a06a9d,2 +np.float32,0xbf01b6a8,0xbf4c170a,2 +np.float32,0x110702,0x2a02aedb,2 +np.float32,0xbf063cd4,0xbf4e6f87,2 +np.float32,0x3f1ff178,0x3f5ad9dd,2 +np.float32,0xbf76dcd4,0xbf7cead0,2 +np.float32,0x80527281,0xaa5d1620,2 +np.float32,0xfea96df8,0xd48c8a7f,2 +np.float32,0x68db02,0x2a6f88b0,2 +np.float32,0x62d971,0x2a6adec7,2 +np.float32,0x3e816fe0,0x3f21df04,2 +np.float32,0x3f586379,0x3f720cc0,2 +np.float32,0x804a3718,0xaa5577ff,2 +np.float32,0x2e2506,0x2a3632b2,2 +np.float32,0x3f297d,0x2a4a4bf3,2 +np.float32,0xbe37aba8,0xbf105f88,2 +np.float32,0xbf18b264,0xbf577ea7,2 +np.float32,0x7f50d02d,0x54bdd8b5,2 +np.float32,0xfee296dc,0xd49ad757,2 +np.float32,0x7ec5137e,0x5493cdb1,2 +np.float32,0x3f4811f4,0x3f6bce3a,2 +np.float32,0xfdff32a0,0xd44af991,2 +np.float32,0x3f6ef140,0x3f7a2ed6,2 +np.float32,0x250838,0x2a2950b5,2 +np.float32,0x25c28e,0x2a2a6ada,2 +np.float32,0xbe875e50,0xbf244e90,2 +np.float32,0x3e3bdff8,0x3f11776a,2 +np.float32,0x3e9fe493,0x3f2daf17,2 +np.float32,0x804d8599,0xaa5897d9,2 +np.float32,0x3f0533da,0x3f4de759,2 +np.float32,0xbe63023c,0xbf1aefc8,2 +np.float32,0x80636e5e,0xaa6b547f,2 +np.float32,0xff112958,0xd4a82d5d,2 +np.float32,0x3e924112,0x3f28991f,2 +np.float32,0xbe996ffc,0xbf2b507a,2 +np.float32,0x802a7cda,0xaa314081,2 +np.float32,0x8022b524,0xaa25b21e,2 +np.float32,0x3f0808c8,0x3f4f5a43,2 +np.float32,0xbef0ec2a,0xbf471e0b,2 +np.float32,0xff4c2345,0xd4bc6b3c,2 +np.float32,0x25ccc8,0x2a2a7a3b,2 +np.float32,0x7f4467d6,0x54ba0260,2 +np.float32,0x7f506539,0x54bdb846,2 +np.float32,0x412ab4,0x2a4c6a2a,2 +np.float32,0x80672c4a,0xaa6e3ef0,2 +np.float32,0xbddfb7f8,0xbef4c0ac,2 +np.float32,0xbf250bb9,0xbf5d276c,2 +np.float32,0x807dca65,0xaa7e84bd,2 +np.float32,0xbf63b8e0,0xbf763438,2 +np.float32,0xbeed1b0c,0xbf460f6b,2 +np.float32,0x8021594f,0xaa238136,2 +np.float32,0xbebc74c8,0xbf377710,2 +np.float32,0x3e9f8e3b,0x3f2d8fce,2 +np.float32,0x7f50ca09,0x54bdd6d8,2 +np.float32,0x805797c1,0xaa6197df,2 +np.float32,0x3de198f9,0x3ef56f98,2 +np.float32,0xf154d,0x29fb0392,2 +np.float32,0xff7fffff,0xd4cb2ff5,2 +np.float32,0xfed22fa8,0xd49702c4,2 +np.float32,0xbf733736,0xbf7baa64,2 +np.float32,0xbf206a8a,0xbf5b1108,2 +np.float32,0xbca49680,0xbe8b3078,2 +np.float32,0xfecba794,0xd4956e1a,2 +np.float32,0x80126582,0xaa061886,2 +np.float32,0xfee5cc82,0xd49b919f,2 +np.float32,0xbf7ad6ae,0xbf7e4491,2 +np.float32,0x7ea88c81,0x548c4c0c,2 +np.float32,0xbf493a0d,0xbf6c4255,2 +np.float32,0xbf06dda0,0xbf4ec1d4,2 +np.float32,0xff3f6e84,0xd4b86cf6,2 +np.float32,0x3e4fe093,0x3f1674b0,2 +np.float32,0x8048ad60,0xaa53fbde,2 +np.float32,0x7ebb7112,0x54915ac5,2 +np.float32,0x5bd191,0x2a652a0d,2 +np.float32,0xfe3121d0,0xd4626cfb,2 +np.float32,0x7e4421c6,0x546a3f83,2 +np.float32,0x19975b,0x2a15b14f,2 +np.float32,0x801c8087,0xaa1b2a64,2 +np.float32,0xfdf6e950,0xd448c0f6,2 +np.float32,0x74e711,0x2a786083,2 +np.float32,0xbf2b2f2e,0xbf5fdccb,2 +np.float32,0x7ed19ece,0x5496e00b,2 +np.float32,0x7f6f8322,0x54c6ba63,2 +np.float32,0x3e90316d,0x3f27cd69,2 +np.float32,0x7ecb42ce,0x54955571,2 +np.float32,0x3f6d49be,0x3f799aaf,2 +np.float32,0x8053d327,0xaa5e4f9a,2 +np.float32,0x7ebd7361,0x5491df3e,2 +np.float32,0xfdb6eed0,0xd435a7aa,2 +np.float32,0x7f3e79f4,0x54b81e4b,2 +np.float32,0xfe83afa6,0xd4813794,2 +np.float32,0x37c443,0x2a421246,2 +np.float32,0xff075a10,0xd4a44cd8,2 +np.float32,0x3ebc5fe0,0x3f377047,2 +np.float32,0x739694,0x2a77714e,2 +np.float32,0xfe832946,0xd4810b91,2 +np.float32,0x7f2638e6,0x54aff235,2 +np.float32,0xfe87f7a6,0xd4829a3f,2 +np.float32,0x3f50f3f8,0x3f6f3eb8,2 +np.float32,0x3eafa3d0,0x3f333548,2 +np.float32,0xbec26ee6,0xbf39626f,2 +np.float32,0x7e6f924f,0x547a66ff,2 +np.float32,0x7f0baa46,0x54a606f8,2 +np.float32,0xbf6dfc49,0xbf79d939,2 +np.float32,0x7f005709,0x54a1699d,2 +np.float32,0x7ee3d7ef,0x549b2057,2 +np.float32,0x803709a4,0xaa4138d7,2 +np.float32,0x3f7bf49a,0x3f7ea509,2 +np.float32,0x509db7,0x2a5b6ff5,2 +np.float32,0x7eb1b0d4,0x548ec9ff,2 +np.float32,0x7eb996ec,0x5490dfce,2 +np.float32,0xbf1fcbaa,0xbf5ac89e,2 +np.float32,0x3e2c9a98,0x3f0d69cc,2 +np.float32,0x3ea77994,0x3f306312,2 +np.float32,0x3f3cbfe4,0x3f67457c,2 +np.float32,0x8422a,0x29cd5a30,2 +np.float32,0xbd974558,0xbed6d264,2 +np.float32,0xfecee77a,0xd496387f,2 +np.float32,0x3f51876b,0x3f6f76f1,2 +np.float32,0x3b1a25,0x2a45ddad,2 +np.float32,0xfe9912f0,0xd487dd67,2 +np.float32,0x3f3ab13d,0x3f666d99,2 +np.float32,0xbf35565a,0xbf64341b,2 +np.float32,0x7d4e84aa,0x54162091,2 +np.float32,0x4c2570,0x2a574dea,2 +np.float32,0x7e82dca6,0x5480f26b,2 +np.float32,0x7f5503e7,0x54bf1c8d,2 +np.float32,0xbeb85034,0xbf361c59,2 +np.float32,0x80460a69,0xaa516387,2 +np.float32,0x805fbbab,0xaa68602c,2 +np.float32,0x7d4b4c1b,0x541557b8,2 +np.float32,0xbefa9a0a,0xbf49bfbc,2 +np.float32,0x3dbd233f,0x3ee76e09,2 +np.float32,0x58b6df,0x2a628d50,2 +np.float32,0xfcdcc180,0xd3f3aad9,2 +np.float32,0x423a37,0x2a4d8487,2 +np.float32,0xbed8b32a,0xbf403507,2 +np.float32,0x3f68e85d,0x3f780f0b,2 +np.float32,0x7ee13c4b,0x549a883d,2 +np.float32,0xff2ed4c5,0xd4b2eec1,2 +np.float32,0xbf54dadc,0xbf70b99a,2 +np.float32,0x3f78b0af,0x3f7d8a32,2 +np.float32,0x3f377372,0x3f651635,2 +np.float32,0xfdaa6178,0xd43166bc,2 +np.float32,0x8060c337,0xaa6934a6,2 +np.float32,0x7ec752c2,0x54945cf6,2 +np.float32,0xbd01a760,0xbea1f624,2 +np.float32,0x6f6599,0x2a746a35,2 +np.float32,0x3f6315b0,0x3f75f95b,2 +np.float32,0x7f2baf32,0x54b1da44,2 +np.float32,0x3e400353,0x3f1286d8,2 +np.float32,0x40d3bf,0x2a4c0f15,2 +np.float32,0x7f733aca,0x54c7c03d,2 +np.float32,0x7e5c5407,0x5473828b,2 +np.float32,0x80191703,0xaa14b56a,2 +np.float32,0xbf4fc144,0xbf6ec970,2 +np.float32,0xbf1137a7,0xbf53eacd,2 +np.float32,0x80575410,0xaa615db3,2 +np.float32,0xbd0911d0,0xbea4fe07,2 +np.float32,0x3e98534a,0x3f2ae643,2 +np.float32,0x3f3b089a,0x3f669185,2 +np.float32,0x4fc752,0x2a5aacc1,2 +np.float32,0xbef44ddc,0xbf480b6e,2 +np.float32,0x80464217,0xaa519af4,2 +np.float32,0x80445fae,0xaa4fb6de,2 +np.float32,0x80771cf4,0xaa79eec8,2 +np.float32,0xfd9182e8,0xd4284fed,2 +np.float32,0xff0a5d16,0xd4a58288,2 +np.float32,0x3f33e169,0x3f63973e,2 +np.float32,0x8021a247,0xaa23f820,2 +np.float32,0xbf362522,0xbf648ab8,2 +np.float32,0x3f457cd7,0x3f6ac95e,2 +np.float32,0xbcadf400,0xbe8dc7e2,2 +np.float32,0x80237210,0xaa26dca7,2 +np.float32,0xbf1293c9,0xbf54939f,2 +np.float32,0xbc5e73c0,0xbe744a37,2 +np.float32,0x3c03f980,0x3e4d44df,2 +np.float32,0x7da46f,0x2a7e6b20,2 +np.float32,0x5d4570,0x2a665dd0,2 +np.float32,0x3e93fbac,0x3f294287,2 +np.float32,0x7e6808fd,0x5477bfa4,2 +np.float32,0xff5aa9a6,0xd4c0c925,2 +np.float32,0xbf5206ba,0xbf6fa767,2 +np.float32,0xbf6e513e,0xbf79f6f1,2 +np.float32,0x3ed01c0f,0x3f3da20f,2 +np.float32,0xff47d93d,0xd4bb1704,2 +np.float32,0x7f466cfd,0x54baa514,2 +np.float32,0x665e10,0x2a6d9fc8,2 +np.float32,0x804d0629,0xaa5820e8,2 +np.float32,0x7e0beaa0,0x54514e7e,2 +np.float32,0xbf7fcb6c,0xbf7fee78,2 +np.float32,0x3f6c5b03,0x3f7946dd,2 +np.float32,0x3e941504,0x3f294c30,2 +np.float32,0xbf2749ad,0xbf5e26a1,2 +np.float32,0xfec2a00a,0xd493302d,2 +np.float32,0x3f15a358,0x3f560bce,2 +np.float32,0x3f15c4e7,0x3f561bcd,2 +np.float32,0xfedc8692,0xd499728c,2 +np.float32,0x7e8f6902,0x5484f180,2 +np.float32,0x7f663d62,0x54c42136,2 +np.float32,0x8027ea62,0xaa2d99b4,2 +np.float32,0x3f3d093d,0x3f67636d,2 +np.float32,0x7f118c33,0x54a85382,2 +np.float32,0x803e866a,0xaa499d43,2 +np.float32,0x80053632,0xa9b02407,2 +np.float32,0xbf36dd66,0xbf64d7af,2 +np.float32,0xbf560358,0xbf71292b,2 +np.float32,0x139a8,0x29596bc0,2 +np.float32,0xbe04f75c,0xbf01a26c,2 +np.float32,0xfe1c3268,0xd45920fa,2 +np.float32,0x7ec77f72,0x5494680c,2 +np.float32,0xbedde724,0xbf41bbba,2 +np.float32,0x3e81dbe0,0x3f220bfd,2 +np.float32,0x800373ac,0xa99989d4,2 +np.float32,0x3f7f859a,0x3f7fd72d,2 +np.float32,0x3eb9dc7e,0x3f369e80,2 +np.float32,0xff5f8eb7,0xd4c236b1,2 +np.float32,0xff1c03cb,0xd4ac44ac,2 +np.float32,0x18cfe1,0x2a14285b,2 +np.float32,0x7f21b075,0x54ae54fd,2 +np.float32,0xff490bd8,0xd4bb7680,2 +np.float32,0xbf15dc22,0xbf5626de,2 +np.float32,0xfe1d5a10,0xd459a9a3,2 +np.float32,0x750544,0x2a7875e4,2 +np.float32,0x8023d5df,0xaa2778b3,2 +np.float32,0x3e42aa08,0x3f1332b2,2 +np.float32,0x3ecaa751,0x3f3bf60d,2 +np.float32,0x0,0x0,2 +np.float32,0x80416da6,0xaa4cb011,2 +np.float32,0x3f4ea9ae,0x3f6e5e22,2 +np.float32,0x2113f4,0x2a230f8e,2 +np.float32,0x3f35c2e6,0x3f64619a,2 +np.float32,0xbf50db8a,0xbf6f3564,2 +np.float32,0xff4d5cea,0xd4bccb8a,2 +np.float32,0x7ee54420,0x549b72d2,2 +np.float32,0x64ee68,0x2a6c81f7,2 +np.float32,0x5330da,0x2a5dbfc2,2 +np.float32,0x80047f88,0xa9a7b467,2 +np.float32,0xbda01078,0xbedae800,2 +np.float32,0xfe96d05a,0xd487315f,2 +np.float32,0x8003cc10,0xa99e7ef4,2 +np.float32,0x8007b4ac,0xa9c8aa3d,2 +np.float32,0x5d4bcf,0x2a66630e,2 +np.float32,0xfdd0c0b0,0xd43dd403,2 +np.float32,0xbf7a1d82,0xbf7e05f0,2 +np.float32,0x74ca33,0x2a784c0f,2 +np.float32,0x804f45e5,0xaa5a3640,2 +np.float32,0x7e6d16aa,0x547988c4,2 +np.float32,0x807d5762,0xaa7e3714,2 +np.float32,0xfecf93d0,0xd4966229,2 +np.float32,0xfecbd25c,0xd4957890,2 +np.float32,0xff7db31c,0xd4ca93b0,2 +np.float32,0x3dac9e18,0x3ee07c4a,2 +np.float32,0xbf4b2d28,0xbf6d0509,2 +np.float32,0xbd4f4c50,0xbebd62e0,2 +np.float32,0xbd2eac40,0xbeb2e0ee,2 +np.float32,0x3d01b69b,0x3ea1fc7b,2 +np.float32,0x7ec63902,0x549416ed,2 +np.float32,0xfcc47700,0xd3ea616d,2 +np.float32,0xbf5ddec2,0xbf7413a1,2 +np.float32,0xff6a6110,0xd4c54c52,2 +np.float32,0xfdfae2a0,0xd449d335,2 +np.float32,0x7e54868c,0x547099cd,2 +np.float32,0x802b5b88,0xaa327413,2 +np.float32,0x80440e72,0xaa4f647a,2 +np.float32,0x3e313c94,0x3f0eaad5,2 +np.float32,0x3ebb492a,0x3f3715a2,2 +np.float32,0xbef56286,0xbf4856d5,2 +np.float32,0x3f0154ba,0x3f4be3a0,2 +np.float32,0xff2df86c,0xd4b2a376,2 +np.float32,0x3ef6a850,0x3f48af57,2 +np.float32,0x3d8d33e1,0x3ed1f22d,2 +np.float32,0x4dd9b9,0x2a58e615,2 +np.float32,0x7f1caf83,0x54ac83c9,2 +np.float32,0xbf7286b3,0xbf7b6d73,2 +np.float32,0x80064f88,0xa9bbbd9f,2 +np.float32,0xbf1f55fa,0xbf5a92db,2 +np.float32,0x546a81,0x2a5ed516,2 +np.float32,0xbe912880,0xbf282d0a,2 +np.float32,0x5df587,0x2a66ee6e,2 +np.float32,0x801f706c,0xaa205279,2 +np.float32,0x58cb6d,0x2a629ece,2 +np.float32,0xfe754f8c,0xd47c62da,2 +np.float32,0xbefb6f4c,0xbf49f8e7,2 +np.float32,0x80000001,0xa6a14518,2 +np.float32,0xbf067837,0xbf4e8df4,2 +np.float32,0x3e8e715c,0x3f271ee4,2 +np.float32,0x8009de9b,0xa9d9ebc8,2 +np.float32,0xbf371ff1,0xbf64f36e,2 +np.float32,0x7f5ce661,0x54c170e4,2 +np.float32,0x3f3c47d1,0x3f671467,2 +np.float32,0xfea5e5a6,0xd48b8eb2,2 +np.float32,0xff62b17f,0xd4c31e15,2 +np.float32,0xff315932,0xd4b3c98f,2 +np.float32,0xbf1c3ca8,0xbf5925b9,2 +np.float32,0x7f800000,0x7f800000,2 +np.float32,0xfdf20868,0xd4476c3b,2 +np.float32,0x5b790e,0x2a64e052,2 +np.float32,0x3f5ddf4e,0x3f7413d4,2 +np.float32,0x7f1a3182,0x54ab9861,2 +np.float32,0x3f4b906e,0x3f6d2b9d,2 +np.float32,0x7ebac760,0x54912edb,2 +np.float32,0x7f626d3f,0x54c30a7e,2 +np.float32,0x3e27b058,0x3f0c0edc,2 +np.float32,0x8041e69c,0xaa4d2de8,2 +np.float32,0x3f42cee0,0x3f69b84a,2 +np.float32,0x7ec5fe83,0x5494085b,2 +np.float32,0x9d3e6,0x29d99cde,2 +np.float32,0x3edc50c0,0x3f41452d,2 +np.float32,0xbf2c463a,0xbf60562c,2 +np.float32,0x800bfa33,0xa9e871e8,2 +np.float32,0x7c9f2c,0x2a7dba4d,2 +np.float32,0x7f2ef9fd,0x54b2fb73,2 +np.float32,0x80741847,0xaa77cdb9,2 +np.float32,0x7e9c462a,0x5488ce1b,2 +np.float32,0x3ea47ec1,0x3f2f55a9,2 +np.float32,0x7f311c43,0x54b3b4f5,2 +np.float32,0x3d8f4c73,0x3ed2facd,2 +np.float32,0x806d7bd2,0xaa7301ef,2 +np.float32,0xbf633d24,0xbf760799,2 +np.float32,0xff4f9a3f,0xd4bd7a99,2 +np.float32,0x3f6021ca,0x3f74e73d,2 +np.float32,0x7e447015,0x546a5eac,2 +np.float32,0x6bff3c,0x2a71e711,2 +np.float32,0xe9c9f,0x29f85f06,2 +np.float32,0x8009fe14,0xa9dad277,2 +np.float32,0x807cf79c,0xaa7df644,2 +np.float32,0xff440e1b,0xd4b9e608,2 +np.float32,0xbddf9a50,0xbef4b5db,2 +np.float32,0x7f3b1c39,0x54b706fc,2 +np.float32,0x3c7471a0,0x3e7c16a7,2 +np.float32,0x8065b02b,0xaa6d18ee,2 +np.float32,0x7f63a3b2,0x54c36379,2 +np.float32,0xbe9c9d92,0xbf2c7d33,2 +np.float32,0x3d93aad3,0x3ed51a2e,2 +np.float32,0xbf41b040,0xbf694571,2 +np.float32,0x80396b9e,0xaa43f899,2 +np.float64,0x800fa025695f404b,0xaaa4000ff64bb00c,2 +np.float64,0xbfecc00198f98003,0xbfeee0b623fbd94b,2 +np.float64,0x7f9eeb60b03dd6c0,0x55291bf8554bb303,2 +np.float64,0x3fba74485634e890,0x3fde08710bdb148d,2 +np.float64,0xbfdd9a75193b34ea,0xbfe8bf711660a2f5,2 +np.float64,0xbfcf92e17a3f25c4,0xbfe4119eda6f3773,2 +np.float64,0xbfe359e2ba66b3c6,0xbfeb0f7ae97ea142,2 +np.float64,0x20791a5640f24,0x2a9441f13d262bed,2 +np.float64,0x3fe455fbfae8abf8,0x3feb830d63e1022c,2 +np.float64,0xbd112b7b7a226,0x2aa238c097ec269a,2 +np.float64,0x93349ba126694,0x2aa0c363cd74465a,2 +np.float64,0x20300cd440602,0x2a9432b4f4081209,2 +np.float64,0x3fdcfae677b9f5cc,0x3fe892a9ee56fe8d,2 +np.float64,0xbfefaae3f7bf55c8,0xbfefe388066132c4,2 +np.float64,0x1a7d6eb634faf,0x2a92ed9851d29ab5,2 +np.float64,0x7fd5308d39aa6119,0x553be444e30326c6,2 +np.float64,0xff811c7390223900,0xd5205cb404952fa7,2 +np.float64,0x80083d24aff07a4a,0xaaa0285cf764d898,2 +np.float64,0x800633810ccc6703,0xaa9d65341419586b,2 +np.float64,0x800ff456223fe8ac,0xaaa423bbcc24dff1,2 +np.float64,0x7fde5c99aebcb932,0x553f71be7d6d9daa,2 +np.float64,0x3fed961c4b3b2c39,0x3fef2ca146270cac,2 +np.float64,0x7fe744d30c6e89a5,0x554220a4cdc78e62,2 +np.float64,0x3fd8f527c7b1ea50,0x3fe76101085be1cb,2 +np.float64,0xbfc96a14b232d428,0xbfe2ab1a8962606c,2 +np.float64,0xffe85f540cf0bea7,0xd54268dff964519a,2 +np.float64,0x800e3be0fe7c77c2,0xaaa3634efd7f020b,2 +np.float64,0x3feb90d032f721a0,0x3fee72a4579e8b12,2 +np.float64,0xffe05674aaa0ace9,0xd5401c9e3fb4abcf,2 +np.float64,0x3fefc2e32c3f85c6,0x3fefeb940924bf42,2 +np.float64,0xbfecfd89e9f9fb14,0xbfeef6addf73ee49,2 +np.float64,0xf5862717eb0c5,0x2aa3e1428780382d,2 +np.float64,0xffc3003b32260078,0xd53558f92202dcdb,2 +np.float64,0x3feb4c152c36982a,0x3fee5940f7da0825,2 +np.float64,0x3fe7147b002e28f6,0x3fecb2948f46d1e3,2 +np.float64,0x7fe00ad9b4a015b2,0x5540039d15e1da54,2 +np.float64,0x8010000000000000,0xaaa428a2f98d728b,2 +np.float64,0xbfd3a41bfea74838,0xbfe595ab45b1be91,2 +np.float64,0x7fdbfd6e5537fadc,0x553e9a6e1107b8d0,2 +np.float64,0x800151d9d9a2a3b4,0xaa918cd8fb63f40f,2 +np.float64,0x7fe6828401ad0507,0x5541eda05dcd1fcf,2 +np.float64,0x3fdae1e7a1b5c3d0,0x3fe7f711e72ecc35,2 +np.float64,0x7fdf4936133e926b,0x553fc29c8d5edea3,2 +np.float64,0x80079de12d4f3bc3,0xaa9f7b06a9286da4,2 +np.float64,0x3fe1261cade24c39,0x3fe9fe09488e417a,2 +np.float64,0xbfc20dce21241b9c,0xbfe0a842fb207a28,2 +np.float64,0x3fe3285dfa2650bc,0x3feaf85215f59ef9,2 +np.float64,0x7fe42b93aea85726,0x554148c3c3bb35e3,2 +np.float64,0xffe6c74e7f6d8e9c,0xd541ffd13fa36dbd,2 +np.float64,0x3fe73ea139ee7d42,0x3fecc402242ab7d3,2 +np.float64,0xffbd4b46be3a9690,0xd53392de917c72e4,2 +np.float64,0x800caed8df395db2,0xaaa2a811a02e6be4,2 +np.float64,0x800aacdb6c9559b7,0xaaa19d6fbc8feebf,2 +np.float64,0x839fb4eb073f7,0x2aa0264b98327c12,2 +np.float64,0xffd0157ba9a02af8,0xd5397157a11c0d05,2 +np.float64,0x7fddc8ff173b91fd,0x553f3e7663fb2ac7,2 +np.float64,0x67b365facf66d,0x2a9dd4d838b0d853,2 +np.float64,0xffe12e7fc7225cff,0xd5406272a83a8e1b,2 +np.float64,0x7fea5b19a034b632,0x5542e567658b3e36,2 +np.float64,0x124989d824932,0x2a90ba8dc7a39532,2 +np.float64,0xffe12ef098225de0,0xd54062968450a078,2 +np.float64,0x3fea2f44a3f45e8a,0x3fedee3c461f4716,2 +np.float64,0x3fe6b033e66d6068,0x3fec88c8035e06b1,2 +np.float64,0x3fe928a2ccf25146,0x3fed88d4cde7a700,2 +np.float64,0x3feead27e97d5a50,0x3fef8d7537d82e60,2 +np.float64,0x8003ab80b6875702,0xaa98adfedd7715a9,2 +np.float64,0x45a405828b481,0x2a9a1fa99a4eff1e,2 +np.float64,0x8002ddebad85bbd8,0xaa96babfda4e0031,2 +np.float64,0x3fc278c32824f186,0x3fe0c8e7c979fbd5,2 +np.float64,0x2e10fffc5c221,0x2a96c30a766d06fa,2 +np.float64,0xffd6ba8c2ead7518,0xd53c8d1d92bc2788,2 +np.float64,0xbfeb5ec3a036bd87,0xbfee602bbf0a0d01,2 +np.float64,0x3fed5bd58f7ab7ab,0x3fef181bf591a4a7,2 +np.float64,0x7feb5274a5b6a4e8,0x55431fcf81876218,2 +np.float64,0xaf8fd6cf5f1fb,0x2aa1c6edbb1e2aaf,2 +np.float64,0x7fece718f179ce31,0x55437c74efb90933,2 +np.float64,0xbfa3c42d0c278860,0xbfd5a16407c77e73,2 +np.float64,0x800b5cff0576b9fe,0xaaa1fc4ecb0dec4f,2 +np.float64,0x800be89ae557d136,0xaaa244d115fc0963,2 +np.float64,0x800d2578f5ba4af2,0xaaa2e18a3a3fc134,2 +np.float64,0x80090ff93e321ff3,0xaaa0add578e3cc3c,2 +np.float64,0x28c5a240518c,0x2a81587cccd7e202,2 +np.float64,0x7fec066929780cd1,0x55434971435d1069,2 +np.float64,0x7fc84d4d15309a99,0x55372c204515694f,2 +np.float64,0xffe070a75de0e14e,0xd54025365046dad2,2 +np.float64,0x7fe5b27cc36b64f9,0x5541b5b822f0b6ca,2 +np.float64,0x3fdea35ac8bd46b6,0x3fe9086a0fb792c2,2 +np.float64,0xbfe79996f7af332e,0xbfece9571d37a5b3,2 +np.float64,0xffdfb47f943f6900,0xd53fe6c14c3366db,2 +np.float64,0xc015cf63802ba,0x2aa2517164d075f4,2 +np.float64,0x7feba98948375312,0x5543340b5b1f1181,2 +np.float64,0x8008678e6550cf1d,0xaaa043e7cea90da5,2 +np.float64,0x3fb11b92fa223726,0x3fd9f8b53be4d90b,2 +np.float64,0x7fc9b18cf0336319,0x55379b42da882047,2 +np.float64,0xbfe5043e736a087d,0xbfebd0c67db7a8e3,2 +np.float64,0x7fde88546a3d10a8,0x553f80cfe5bcf5fe,2 +np.float64,0x8006a6c82dcd4d91,0xaa9e171d182ba049,2 +np.float64,0xbfa0f707ac21ee10,0xbfd48e5d3faa1699,2 +np.float64,0xbfe7716bffaee2d8,0xbfecd8e6abfb8964,2 +np.float64,0x9511ccab2a23a,0x2aa0d56d748f0313,2 +np.float64,0x8003ddb9b847bb74,0xaa991ca06fd9d308,2 +np.float64,0x80030710fac60e23,0xaa9725845ac95fe8,2 +np.float64,0xffece5bbaeb9cb76,0xd5437c2670f894f4,2 +np.float64,0x3fd9be5c72b37cb9,0x3fe79f2e932a5708,2 +np.float64,0x1f050cca3e0a3,0x2a93f36499fe5228,2 +np.float64,0x3fd5422becaa8458,0x3fe6295d6150df58,2 +np.float64,0xffd72c050e2e580a,0xd53cbc52d73b495f,2 +np.float64,0xbfe66d5235ecdaa4,0xbfec6ca27e60bf23,2 +np.float64,0x17ac49a42f58a,0x2a923b5b757087a0,2 +np.float64,0xffd39edc40273db8,0xd53b2f7bb99b96bf,2 +np.float64,0x7fde6cf009bcd9df,0x553f77614eb30d75,2 +np.float64,0x80042b4c3fa85699,0xaa99c05fbdd057db,2 +np.float64,0xbfde5547f8bcaa90,0xbfe8f3147d67a940,2 +np.float64,0xbfdd02f9bf3a05f4,0xbfe894f2048aa3fe,2 +np.float64,0xbfa20ec82c241d90,0xbfd4fd02ee55aac7,2 +np.float64,0x8002f670f8c5ece3,0xaa96fad7e53dd479,2 +np.float64,0x80059f24d7eb3e4a,0xaa9c7312dae0d7bc,2 +np.float64,0x7fe6ae7423ad5ce7,0x5541f9430be53062,2 +np.float64,0xe135ea79c26be,0x2aa350d8f8c526e1,2 +np.float64,0x3fec188ce4f8311a,0x3feea44d21c23f68,2 +np.float64,0x800355688286aad2,0xaa97e6ca51eb8357,2 +np.float64,0xa2d6530b45acb,0x2aa15635bbd366e8,2 +np.float64,0x600e0150c01c1,0x2a9d1456ea6c239c,2 +np.float64,0x8009c30863338611,0xaaa118f94b188bcf,2 +np.float64,0x3fe7e4c0dfefc982,0x3fed07e8480b8c07,2 +np.float64,0xbfddac6407bb58c8,0xbfe8c46f63a50225,2 +np.float64,0xbc85e977790bd,0x2aa2344636ed713d,2 +np.float64,0xfff0000000000000,0xfff0000000000000,2 +np.float64,0xffcd1570303a2ae0,0xd5389a27d5148701,2 +np.float64,0xbf937334d026e660,0xbfd113762e4e29a7,2 +np.float64,0x3fdbfdaa9b37fb55,0x3fe84a425fdff7df,2 +np.float64,0xffc10800f5221000,0xd5349535ffe12030,2 +np.float64,0xaf40f3755e81f,0x2aa1c443af16cd27,2 +np.float64,0x800f7da34f7efb47,0xaaa3f14bf25fc89f,2 +np.float64,0xffe4a60125a94c02,0xd5416b764a294128,2 +np.float64,0xbf8e25aa903c4b40,0xbfcf5ebc275b4789,2 +np.float64,0x3fca681bbb34d038,0x3fe2e882bcaee320,2 +np.float64,0xbfd0f3c9c1a1e794,0xbfe48d0df7b47572,2 +np.float64,0xffeb99b49d373368,0xd5433060dc641910,2 +np.float64,0x3fe554fb916aa9f8,0x3febf437cf30bd67,2 +np.float64,0x80079518d0af2a32,0xaa9f6ee87044745a,2 +np.float64,0x5e01a8a0bc036,0x2a9cdf0badf222c3,2 +np.float64,0xbfea9831b3f53064,0xbfee1601ee953ab3,2 +np.float64,0xbfc369d1a826d3a4,0xbfe110b675c311e0,2 +np.float64,0xa82e640d505cd,0x2aa1863d4e523b9c,2 +np.float64,0x3fe506d70a2a0dae,0x3febd1eba3aa83fa,2 +np.float64,0xcbacba7197598,0x2aa2adeb9927f1f2,2 +np.float64,0xc112d6038225b,0x2aa25978f12038b0,2 +np.float64,0xffa7f5f44c2febf0,0xd52d0ede02d4e18b,2 +np.float64,0x8006f218e34de433,0xaa9e870cf373b4eb,2 +np.float64,0xffe6d9a5d06db34b,0xd54204a4adc608c7,2 +np.float64,0x7fe717210eae2e41,0x554214bf3e2b5228,2 +np.float64,0xbfdd4b45cdba968c,0xbfe8a94c7f225f8e,2 +np.float64,0x883356571066b,0x2aa055ab0b2a8833,2 +np.float64,0x3fe307fc02a60ff8,0x3feae9175053288f,2 +np.float64,0x3fefa985f77f530c,0x3fefe31289446615,2 +np.float64,0x8005698a98aad316,0xaa9c17814ff7d630,2 +np.float64,0x3fea77333c74ee66,0x3fee098ba70e10fd,2 +np.float64,0xbfd1d00b0023a016,0xbfe4e497fd1cbea1,2 +np.float64,0x80009b0c39813619,0xaa8b130a6909cc3f,2 +np.float64,0x3fdbeb896fb7d714,0x3fe84502ba5437f8,2 +np.float64,0x3fb6e7e3562dcfc7,0x3fdca00d35c389ad,2 +np.float64,0xb2d46ebf65a8e,0x2aa1e2fe158d0838,2 +np.float64,0xbfd5453266aa8a64,0xbfe62a6a74c8ef6e,2 +np.float64,0x7fe993aa07732753,0x5542b5438bf31cb7,2 +np.float64,0xbfda5a098cb4b414,0xbfe7ce6d4d606203,2 +np.float64,0xbfe40c3ce068187a,0xbfeb61a32c57a6d0,2 +np.float64,0x3fcf17671d3e2ed0,0x3fe3f753170ab686,2 +np.float64,0xbfe4f814b6e9f02a,0xbfebcb67c60b7b08,2 +np.float64,0x800efedf59fdfdbf,0xaaa3ba4ed44ad45a,2 +np.float64,0x800420b556e8416b,0xaa99aa7fb14edeab,2 +np.float64,0xbf6e4ae6403c9600,0xbfc3cb2b29923989,2 +np.float64,0x3fda5c760a34b8ec,0x3fe7cf2821c52391,2 +np.float64,0x7f898faac0331f55,0x5522b44a01408188,2 +np.float64,0x3fd55af4b7aab5e9,0x3fe631f6d19503b3,2 +np.float64,0xbfa30a255c261450,0xbfd55caf0826361d,2 +np.float64,0x7fdfb801343f7001,0x553fe7ee50b9199a,2 +np.float64,0x7fa89ee91c313dd1,0x552d528ca2a4d659,2 +np.float64,0xffea72921d34e524,0xd542eb01af2e470d,2 +np.float64,0x3feddf0f33fbbe1e,0x3fef462b67fc0a91,2 +np.float64,0x3fe36700b566ce01,0x3feb1596caa8eff7,2 +np.float64,0x7fe6284a25ac5093,0x5541d58be3956601,2 +np.float64,0xffda16f7c8b42df0,0xd53de4f722485205,2 +np.float64,0x7f9355b94026ab72,0x552578cdeb41d2ca,2 +np.float64,0xffd3a9b022275360,0xd53b347b02dcea21,2 +np.float64,0x3fcb7f4f4a36fe9f,0x3fe32a40e9f6c1aa,2 +np.float64,0x7fdb958836372b0f,0x553e746103f92111,2 +np.float64,0x3fd37761c0a6eec4,0x3fe5853c5654027e,2 +np.float64,0x3fe449f1a2e893e4,0x3feb7d9e4eacc356,2 +np.float64,0x80077dfbef0efbf9,0xaa9f4ed788d2fadd,2 +np.float64,0x4823aa7890476,0x2a9a6eb4b653bad5,2 +np.float64,0xbfede01a373bc034,0xbfef468895fbcd29,2 +np.float64,0xbfe2bac5f125758c,0xbfeac4811c4dd66f,2 +np.float64,0x3fec10373af8206e,0x3feea14529e0f178,2 +np.float64,0x3fe305e30ca60bc6,0x3feae81a2f9d0302,2 +np.float64,0xa9668c5f52cd2,0x2aa1910e3a8f2113,2 +np.float64,0xbfd98b1717b3162e,0xbfe78f75995335d2,2 +np.float64,0x800fa649c35f4c94,0xaaa402ae79026a8f,2 +np.float64,0xbfb07dacf620fb58,0xbfd9a7d33d93a30f,2 +np.float64,0x80015812f382b027,0xaa91a843e9c85c0e,2 +np.float64,0x3fc687d96c2d0fb3,0x3fe1ef0ac16319c5,2 +np.float64,0xbfecad2ecd795a5e,0xbfeed9f786697af0,2 +np.float64,0x1608c1242c119,0x2a91cd11e9b4ccd2,2 +np.float64,0x6df775e8dbeef,0x2a9e6ba8c71130eb,2 +np.float64,0xffe96e9332b2dd26,0xd542ac342d06299b,2 +np.float64,0x7fecb6a3b8396d46,0x5543718af8162472,2 +np.float64,0x800d379f893a6f3f,0xaaa2ea36bbcb9308,2 +np.float64,0x3f924cdb202499b6,0x3fd0bb90af8d1f79,2 +np.float64,0x0,0x0,2 +np.float64,0x7feaf3b365f5e766,0x5543099a160e2427,2 +np.float64,0x3fea169ed0742d3e,0x3fede4d526e404f8,2 +np.float64,0x7feaf5f2f775ebe5,0x55430a2196c5f35a,2 +np.float64,0xbfc80d4429301a88,0xbfe2541f2ddd3334,2 +np.float64,0xffc75203b32ea408,0xd536db2837068689,2 +np.float64,0xffed2850e63a50a1,0xd5438b1217b72b8a,2 +np.float64,0x7fc16b0e7f22d61c,0x5534bcd0bfddb6f0,2 +np.float64,0x7feee8ed09fdd1d9,0x5543ed5b3ca483ab,2 +np.float64,0x7fb6c7ee662d8fdc,0x5531fffb5d46dafb,2 +np.float64,0x3fd77cebf8aef9d8,0x3fe6e9242e2bd29d,2 +np.float64,0x3f81c33f70238680,0x3fca4c7f3c9848f7,2 +np.float64,0x3fd59fea92ab3fd5,0x3fe649c1558cadd5,2 +np.float64,0xffeba82d4bf7505a,0xd54333bad387f7bd,2 +np.float64,0xffd37630e1a6ec62,0xd53b1ca62818c670,2 +np.float64,0xffec2c1e70b8583c,0xd5435213dcd27c22,2 +np.float64,0x7fec206971f840d2,0x55434f6660a8ae41,2 +np.float64,0x3fed2964adba52c9,0x3fef0642fe72e894,2 +np.float64,0xffd08e30d6211c62,0xd539b060e0ae02da,2 +np.float64,0x3e5f976c7cbf4,0x2a992e6ff991a122,2 +np.float64,0xffe6eee761adddce,0xd5420a393c67182f,2 +np.float64,0xbfe8ec9a31f1d934,0xbfed714426f58147,2 +np.float64,0x7fefffffffffffff,0x554428a2f98d728b,2 +np.float64,0x3fb3ae8b2c275d16,0x3fdb36b81b18a546,2 +np.float64,0x800f73df4dfee7bf,0xaaa3ed1a3e2cf49c,2 +np.float64,0xffd0c8873b21910e,0xd539ce6a3eab5dfd,2 +np.float64,0x3facd6c49439ad80,0x3fd8886f46335df1,2 +np.float64,0x3935859c726b2,0x2a98775f6438dbb1,2 +np.float64,0x7feed879fbfdb0f3,0x5543e9d1ac239469,2 +np.float64,0xbfe84dd990f09bb3,0xbfed323af09543b1,2 +np.float64,0xbfe767cc5a6ecf98,0xbfecd4f39aedbacb,2 +np.float64,0xffd8bd91d5b17b24,0xd53d5eb3734a2609,2 +np.float64,0xbfe13edeb2a27dbe,0xbfea0a856f0b9656,2 +np.float64,0xd933dd53b267c,0x2aa3158784e428c9,2 +np.float64,0xbfef6fef987edfdf,0xbfefcfb1c160462b,2 +np.float64,0x8009eeda4893ddb5,0xaaa13268a41045b1,2 +np.float64,0xab48c7a156919,0x2aa1a1a9c124c87d,2 +np.float64,0xa997931d532f3,0x2aa192bfe5b7bbb4,2 +np.float64,0xffe39ce8b1e739d1,0xd5411fa1c5c2cbd8,2 +np.float64,0x7e7ac2f6fcf59,0x2a9fdf6f263a9e9f,2 +np.float64,0xbfee1e35a6fc3c6b,0xbfef5c25d32b4047,2 +np.float64,0xffe5589c626ab138,0xd5419d220cc9a6da,2 +np.float64,0x7fe12509bf224a12,0x55405f7036dc5932,2 +np.float64,0xa6f15ba94de2c,0x2aa17b3367b1fc1b,2 +np.float64,0x3fca8adbfa3515b8,0x3fe2f0ca775749e5,2 +np.float64,0xbfcb03aa21360754,0xbfe30d5b90ca41f7,2 +np.float64,0x3fefafb2da7f5f66,0x3fefe5251aead4e7,2 +np.float64,0xffd90a59d23214b4,0xd53d7cf63a644f0e,2 +np.float64,0x3fba499988349333,0x3fddf84154fab7e5,2 +np.float64,0x800a76a0bc54ed42,0xaaa17f68cf67f2fa,2 +np.float64,0x3fea33d15bb467a3,0x3fedeff7f445b2ff,2 +np.float64,0x8005d9b0726bb362,0xaa9cd48624afeca9,2 +np.float64,0x7febf42e9a77e85c,0x55434541d8073376,2 +np.float64,0xbfedfc4469bbf889,0xbfef505989f7ee7d,2 +np.float64,0x8001211f1422423f,0xaa90a9889d865349,2 +np.float64,0x800e852f7fdd0a5f,0xaaa3845f11917f8e,2 +np.float64,0xffefd613c87fac27,0xd5441fd17ec669b4,2 +np.float64,0x7fed2a74543a54e8,0x55438b8c637da8b8,2 +np.float64,0xb83d50ff707aa,0x2aa210b4fc11e4b2,2 +np.float64,0x10000000000000,0x2aa428a2f98d728b,2 +np.float64,0x474ad9208e97,0x2a84e5a31530368a,2 +np.float64,0xffd0c5498ea18a94,0xd539ccc0e5cb425e,2 +np.float64,0x8001a8e9c82351d4,0xaa92f1aee6ca5b7c,2 +np.float64,0xd28db1e5a51b6,0x2aa2e328c0788f4a,2 +np.float64,0x3bf734ac77ee7,0x2a98da65c014b761,2 +np.float64,0x3fe56e17c96adc30,0x3febff2b6b829b7a,2 +np.float64,0x7783113eef063,0x2a9f46c3f09eb42c,2 +np.float64,0x3fd69d4e42ad3a9d,0x3fe69f83a21679f4,2 +np.float64,0x3fd34f4841a69e90,0x3fe5766b3c771616,2 +np.float64,0x3febb49895b76931,0x3fee7fcb603416c9,2 +np.float64,0x7fe8d6cb55f1ad96,0x554286c3b3bf4313,2 +np.float64,0xbfe67c6ba36cf8d8,0xbfec730218f2e284,2 +np.float64,0xffef9d97723f3b2e,0xd54413e38b6c29be,2 +np.float64,0x12d8cd2a25b1b,0x2a90e5ccd37b8563,2 +np.float64,0x81fe019103fc0,0x2aa01524155e73c5,2 +np.float64,0x7fe95d546f72baa8,0x5542a7fabfd425ff,2 +np.float64,0x800e742f1f9ce85e,0xaaa37cbe09e1f874,2 +np.float64,0xffd96bd3a732d7a8,0xd53da3086071264a,2 +np.float64,0x4ef2691e9de4e,0x2a9b3d316047fd6d,2 +np.float64,0x1a91684c3522e,0x2a92f25913c213de,2 +np.float64,0x3d5151b87aa2b,0x2a9909dbd9a44a84,2 +np.float64,0x800d9049435b2093,0xaaa31424e32d94a2,2 +np.float64,0xffe5b25fcc2b64bf,0xd541b5b0416b40b5,2 +np.float64,0xffe0eb784c21d6f0,0xd5404d083c3d6bc6,2 +np.float64,0x8007ceefbf0f9de0,0xaa9fbe0d739368b4,2 +np.float64,0xb78529416f0b,0x2a8ca3b29b5b3f18,2 +np.float64,0x7fba61130034c225,0x5532e6d4ca0f2918,2 +np.float64,0x3fba8d67ae351acf,0x3fde11efd6239b09,2 +np.float64,0x3fe7f24c576fe498,0x3fed0d63947a854d,2 +np.float64,0x2bb58dec576b3,0x2a965de7fca12aff,2 +np.float64,0xbfe86ceec4f0d9de,0xbfed3ea7f1d084e2,2 +np.float64,0x7fd1a7f7bca34fee,0x553a3f01b67fad2a,2 +np.float64,0x3fd9a43acfb34874,0x3fe7972dc5d8dfd6,2 +np.float64,0x7fd9861acdb30c35,0x553dad3b1bbb3b4d,2 +np.float64,0xffecc0c388398186,0xd54373d3b903deec,2 +np.float64,0x3fa6f86e9c2df0e0,0x3fd6bdbe40fcf710,2 +np.float64,0x800ddd99815bbb33,0xaaa33820d2f889bb,2 +np.float64,0x7fe087089b610e10,0x55402c868348a6d3,2 +np.float64,0x3fdf43d249be87a5,0x3fe933d29fbf7c23,2 +np.float64,0x7fe4f734c7a9ee69,0x5541822e56c40725,2 +np.float64,0x3feb39a9d3b67354,0x3fee526bf1f69f0e,2 +np.float64,0x3fe61454a0ec28a9,0x3fec46d7c36f7566,2 +np.float64,0xbfeafaa0a375f541,0xbfee3af2e49d457a,2 +np.float64,0x3fda7378e1b4e6f0,0x3fe7d613a3f92c40,2 +np.float64,0xe3e31c5fc7c64,0x2aa3645c12e26171,2 +np.float64,0xbfe97a556df2f4ab,0xbfeda8aa84cf3544,2 +np.float64,0xff612f9c80225f00,0xd514a51e5a2a8a97,2 +np.float64,0x800c51c8a0f8a391,0xaaa279fe7d40b50b,2 +np.float64,0xffd6f9d2312df3a4,0xd53ca783a5f8d110,2 +np.float64,0xbfead48bd7f5a918,0xbfee2cb2f89c5e57,2 +np.float64,0x800f5949e89eb294,0xaaa3e1a67a10cfef,2 +np.float64,0x800faf292b7f5e52,0xaaa40675e0c96cfd,2 +np.float64,0xbfedc238453b8470,0xbfef3c179d2d0209,2 +np.float64,0x3feb0443c5760888,0x3fee3e8bf29089c2,2 +np.float64,0xb26f69e164ded,0x2aa1df9f3dd7d765,2 +np.float64,0x3fcacdc053359b80,0x3fe300a67765b667,2 +np.float64,0x3fe8b274647164e8,0x3fed5a4cd4da8155,2 +np.float64,0x291e6782523ce,0x2a95ea7ac1b13a68,2 +np.float64,0xbfc4fc094e29f814,0xbfe1838671fc8513,2 +np.float64,0x3fbf1301f23e2600,0x3fdfb03a6f13e597,2 +np.float64,0xffeb36554ab66caa,0xd543193d8181e4f9,2 +np.float64,0xbfd969a52db2d34a,0xbfe78528ae61f16d,2 +np.float64,0x800cccd04d3999a1,0xaaa2b6b7a2d2d2d6,2 +np.float64,0x808eb4cb011d7,0x2aa005effecb2b4a,2 +np.float64,0x7fe839b3f9b07367,0x55425f61e344cd6d,2 +np.float64,0xbfeb25b6ed764b6e,0xbfee4b0234fee365,2 +np.float64,0xffefffffffffffff,0xd54428a2f98d728b,2 +np.float64,0xbfe01305da60260c,0xbfe9700b784af7e9,2 +np.float64,0xffcbf36b0a37e6d8,0xd538474b1d74ffe1,2 +np.float64,0xffaeebe3e83dd7c0,0xd52fa2e8dabf7209,2 +np.float64,0xbfd9913bf0b32278,0xbfe7915907aab13c,2 +np.float64,0xbfe7d125d9efa24c,0xbfecfff563177706,2 +np.float64,0xbfee98d23cbd31a4,0xbfef867ae393e446,2 +np.float64,0x3fe30efb67e61df6,0x3feaec6344633d11,2 +np.float64,0x1,0x2990000000000000,2 +np.float64,0x7fd5524fd3aaa49f,0x553bf30d18ab877e,2 +np.float64,0xc98b403f93168,0x2aa29d2fadb13c07,2 +np.float64,0xffe57080046ae100,0xd541a3b1b687360e,2 +np.float64,0x7fe20bade5e4175b,0x5540a79b94294f40,2 +np.float64,0x3fe155400a22aa80,0x3fea15c45f5b5837,2 +np.float64,0x7fe428dc8f6851b8,0x554147fd2ce93cc1,2 +np.float64,0xffefb77eb67f6efc,0xd544195dcaff4980,2 +np.float64,0x3fe49e733b293ce6,0x3feba394b833452a,2 +np.float64,0x38e01e3e71c05,0x2a986b2c955bad21,2 +np.float64,0x7fe735eb376e6bd5,0x55421cc51290d92d,2 +np.float64,0xbfd81d8644b03b0c,0xbfe71ce6d6fbd51a,2 +np.float64,0x8009a32325134647,0xaaa10645d0e6b0d7,2 +np.float64,0x56031ab8ac064,0x2a9c074be40b1f80,2 +np.float64,0xff8989aa30331340,0xd522b2d319a0ac6e,2 +np.float64,0xbfd6c183082d8306,0xbfe6ab8ffb3a8293,2 +np.float64,0x7ff8000000000000,0x7ff8000000000000,2 +np.float64,0xbfe17b68b1e2f6d2,0xbfea28dac8e0c457,2 +np.float64,0x3fbb50e42236a1c8,0x3fde5b090d51e3bd,2 +np.float64,0xffc2bb7cbf2576f8,0xd5353f1b3571c17f,2 +np.float64,0xbfe7576bca6eaed8,0xbfecce388241f47c,2 +np.float64,0x3fe7b52b04ef6a56,0x3fecf495bef99e7e,2 +np.float64,0xffe5511af82aa236,0xd5419b11524e8350,2 +np.float64,0xbfe66d5edf2cdabe,0xbfec6ca7d7b5be8c,2 +np.float64,0xc84a0ba790942,0x2aa29346f16a2cb4,2 +np.float64,0x6db5e7a0db6be,0x2a9e659c0e8244a0,2 +np.float64,0x7fef8f7b647f1ef6,0x554410e67af75d27,2 +np.float64,0xbfe2b4ada7e5695c,0xbfeac1997ec5a064,2 +np.float64,0xbfe99372e03326e6,0xbfedb2662b287543,2 +np.float64,0x3fa45d352428ba6a,0x3fd5d8a895423abb,2 +np.float64,0x3fa029695c2052d3,0x3fd439f858998886,2 +np.float64,0xffe0a9bd3261537a,0xd54037d0cd8bfcda,2 +np.float64,0xbfef83e09a7f07c1,0xbfefd66a4070ce73,2 +np.float64,0x7fee3dcc31fc7b97,0x5543c8503869407e,2 +np.float64,0xffbd16f1603a2de0,0xd533872fa5be978b,2 +np.float64,0xbfe8173141b02e62,0xbfed1c478614c6f4,2 +np.float64,0xbfef57aa277eaf54,0xbfefc77fdab27771,2 +np.float64,0x7fe883a02f31073f,0x554271ff0e3208da,2 +np.float64,0xe3adb63bc75b7,0x2aa362d833d0e41c,2 +np.float64,0x8001c430bac38862,0xaa93575026d26510,2 +np.float64,0x12fb347225f67,0x2a90f00eb9edb3fe,2 +np.float64,0x3fe53f83cbaa7f08,0x3febead40de452c2,2 +np.float64,0xbfe7f67227efece4,0xbfed0f10e32ad220,2 +np.float64,0xb8c5b45d718b7,0x2aa2152912cda86d,2 +np.float64,0x3fd23bb734a4776e,0x3fe50e5d3008c095,2 +np.float64,0x8001fd558ee3faac,0xaa941faa1f7ed450,2 +np.float64,0xffe6bbeda9ed77db,0xd541fcd185a63afa,2 +np.float64,0x4361d79086c3c,0x2a99d692237c30b7,2 +np.float64,0xbfd012f004a025e0,0xbfe43093e290fd0d,2 +np.float64,0xffe1d8850423b10a,0xd54097cf79d8d01e,2 +np.float64,0x3fccf4df7939e9bf,0x3fe37f8cf8be6436,2 +np.float64,0x8000546bc6c0a8d8,0xaa861bb3588556f2,2 +np.float64,0xbfecb4d6ba7969ae,0xbfeedcb6239135fe,2 +np.float64,0xbfaeb425cc3d6850,0xbfd90cfc103bb896,2 +np.float64,0x800ec037ec7d8070,0xaaa39eae8bde9774,2 +np.float64,0xbfeeaf863dfd5f0c,0xbfef8e4514772a8a,2 +np.float64,0xffec67c6c4b8cf8d,0xd5435fad89f900cf,2 +np.float64,0x3fda4498da348932,0x3fe7c7f6b3f84048,2 +np.float64,0xbfd05fd3dea0bfa8,0xbfe4509265a9b65f,2 +np.float64,0x3fe42cc713a8598e,0x3feb706ba9cd533c,2 +np.float64,0xec22d4d7d845b,0x2aa39f8cccb9711c,2 +np.float64,0x7fda30606c3460c0,0x553deea865065196,2 +np.float64,0xbfd58cba8bab1976,0xbfe64327ce32d611,2 +np.float64,0xadd521c75baa4,0x2aa1b7efce201a98,2 +np.float64,0x7fed43c1027a8781,0x55439131832b6429,2 +np.float64,0x800bee278fb7dc4f,0xaaa247a71e776db4,2 +np.float64,0xbfe9be5dd2737cbc,0xbfedc2f9501755b0,2 +np.float64,0x8003f4854447e90b,0xaa994d9b5372b13b,2 +np.float64,0xbfe5d0f867eba1f1,0xbfec29f8dd8b33a4,2 +np.float64,0x3fd79102d5af2206,0x3fe6efaa7a1efddb,2 +np.float64,0xbfeae783c835cf08,0xbfee33cdb4a44e81,2 +np.float64,0x3fcf1713e83e2e28,0x3fe3f7414753ddfb,2 +np.float64,0xffe5ab3cff2b567a,0xd541b3bf0213274a,2 +np.float64,0x7fe0fc65d8a1f8cb,0x554052761ac96386,2 +np.float64,0x7e81292efd026,0x2a9fdff8c01ae86f,2 +np.float64,0x80091176039222ec,0xaaa0aebf0565dfa6,2 +np.float64,0x800d2bf5ab5a57ec,0xaaa2e4a4c31e7e29,2 +np.float64,0xffd1912ea923225e,0xd53a33b2856726ab,2 +np.float64,0x800869918ed0d323,0xaaa0453408e1295d,2 +np.float64,0xffba0898fa341130,0xd532d19b202a9646,2 +np.float64,0xbfe09fac29613f58,0xbfe9b9687b5811a1,2 +np.float64,0xbfbd4ae82e3a95d0,0xbfdf1220f6f0fdfa,2 +np.float64,0xffea11d27bb423a4,0xd542d3d3e1522474,2 +np.float64,0xbfe6b05705ad60ae,0xbfec88d6bcab2683,2 +np.float64,0x3fe624a3f2ec4948,0x3fec4dcc78ddf871,2 +np.float64,0x53483018a6907,0x2a9bba8f92006b69,2 +np.float64,0xbfec0a6eeb7814de,0xbfee9f2a741248d7,2 +np.float64,0x3fe8c8ce6371919d,0x3fed63250c643482,2 +np.float64,0xbfe26b0ef964d61e,0xbfea9e511db83437,2 +np.float64,0xffa0408784208110,0xd52987f62c369ae9,2 +np.float64,0xffc153abc322a758,0xd534b384b5c5fe63,2 +np.float64,0xbfbdce88a63b9d10,0xbfdf4065ef0b01d4,2 +np.float64,0xffed4a4136fa9482,0xd54392a450f8b0af,2 +np.float64,0x8007aa18748f5432,0xaa9f8bd2226d4299,2 +np.float64,0xbfdab4d3e8b569a8,0xbfe7e9a5402540e5,2 +np.float64,0x7fe68914f92d1229,0x5541ef5e78fa35de,2 +np.float64,0x800a538bb1b4a718,0xaaa16bc487711295,2 +np.float64,0xffe02edbc8605db7,0xd5400f8f713df890,2 +np.float64,0xffe8968053712d00,0xd54276b9cc7f460a,2 +np.float64,0x800a4ce211d499c5,0xaaa1680491deb40c,2 +np.float64,0x3f988080f8310102,0x3fd2713691e99329,2 +np.float64,0xf64e42a7ec9c9,0x2aa3e6a7af780878,2 +np.float64,0xff73cc7100279900,0xd51b4478c3409618,2 +np.float64,0x71e6722ce3ccf,0x2a9ec76ddf296ce0,2 +np.float64,0x8006ca16ab0d942e,0xaa9e4bfd862af570,2 +np.float64,0x8000000000000000,0x8000000000000000,2 +np.float64,0xbfed373e02ba6e7c,0xbfef0b2b7bb767b3,2 +np.float64,0xa6cb0f694d962,0x2aa179dd16b0242b,2 +np.float64,0x7fec14626cf828c4,0x55434ca55b7c85d5,2 +np.float64,0x3fcda404513b4808,0x3fe3a68e8d977752,2 +np.float64,0xbfeb94995f772933,0xbfee74091d288b81,2 +np.float64,0x3fce2299a13c4530,0x3fe3c2603f28d23b,2 +np.float64,0xffd07f4534a0fe8a,0xd539a8a6ebc5a603,2 +np.float64,0x7fdb1c651e3638c9,0x553e478a6385c86b,2 +np.float64,0x3fec758336f8eb06,0x3feec5f3b92c8b28,2 +np.float64,0x796fc87cf2dfa,0x2a9f7184a4ad8c49,2 +np.float64,0x3fef9ba866ff3750,0x3fefde6a446fc2cd,2 +np.float64,0x964d26c72c9a5,0x2aa0e143f1820179,2 +np.float64,0xbfef6af750bed5ef,0xbfefce04870a97bd,2 +np.float64,0x3fe2f3961aa5e72c,0x3feadf769321a3ff,2 +np.float64,0xbfd6b706e9ad6e0e,0xbfe6a8141c5c3b5d,2 +np.float64,0x7fe0ecc40a21d987,0x55404d72c2b46a82,2 +np.float64,0xbfe560d19deac1a3,0xbfebf962681a42a4,2 +np.float64,0xbfea37170ab46e2e,0xbfedf136ee9df02b,2 +np.float64,0xbfebf78947b7ef12,0xbfee9847ef160257,2 +np.float64,0x800551f8312aa3f1,0xaa9bee7d3aa5491b,2 +np.float64,0xffed2513897a4a26,0xd5438a58c4ae28ec,2 +np.float64,0x7fd962d75cb2c5ae,0x553d9f8a0c2016f3,2 +np.float64,0x3fefdd8512bfbb0a,0x3feff47d8da7424d,2 +np.float64,0xbfefa5b43bff4b68,0xbfefe1ca42867af0,2 +np.float64,0xbfc8a2853531450c,0xbfe279bb7b965729,2 +np.float64,0x800c8843bc391088,0xaaa2951344e7b29b,2 +np.float64,0x7fe22587bae44b0e,0x5540af8bb58cfe86,2 +np.float64,0xbfe159fae822b3f6,0xbfea182394eafd8d,2 +np.float64,0xbfe6fdfd50edfbfa,0xbfeca93f2a3597d0,2 +np.float64,0xbfe5cd5afaeb9ab6,0xbfec286a8ce0470f,2 +np.float64,0xbfc84bb97f309774,0xbfe263ef0f8f1f6e,2 +np.float64,0x7fd9c1e548b383ca,0x553dc4556874ecb9,2 +np.float64,0x7fda43d33bb487a5,0x553df60f61532fc0,2 +np.float64,0xbfe774bd25eee97a,0xbfecda42e8578c1f,2 +np.float64,0x800df1f5ab9be3ec,0xaaa34184712e69db,2 +np.float64,0xbff0000000000000,0xbff0000000000000,2 +np.float64,0x3fe14ec21b629d84,0x3fea128244215713,2 +np.float64,0x7fc1ce7843239cf0,0x5534e3fa8285b7b8,2 +np.float64,0xbfe922b204724564,0xbfed86818687d649,2 +np.float64,0x3fc58924fb2b1248,0x3fe1aa715ff6ebbf,2 +np.float64,0x8008b637e4d16c70,0xaaa0760b53abcf46,2 +np.float64,0xffbf55bd4c3eab78,0xd53404a23091a842,2 +np.float64,0x9f6b4a753ed6a,0x2aa136ef9fef9596,2 +np.float64,0xbfd11da7f8a23b50,0xbfe49deb493710d8,2 +np.float64,0x800a2f07fcd45e10,0xaaa157237c98b4f6,2 +np.float64,0x3fdd4defa4ba9bdf,0x3fe8aa0bcf895f4f,2 +np.float64,0x7fe9b0ab05f36155,0x5542bc5335414473,2 +np.float64,0x3fe89c97de313930,0x3fed51a1189b8982,2 +np.float64,0x3fdd45c8773a8b91,0x3fe8a7c2096fbf5a,2 +np.float64,0xbfeb6f64daf6deca,0xbfee665167ef43ad,2 +np.float64,0xffdf9da1c4bf3b44,0xd53fdf141944a983,2 +np.float64,0x3fde092ed0bc125c,0x3fe8de25bfbfc2db,2 +np.float64,0xbfcb21f96b3643f4,0xbfe3147904c258cf,2 +np.float64,0x800c9c934f993927,0xaaa29f17c43f021b,2 +np.float64,0x9b91814d37230,0x2aa11329e59bf6b0,2 +np.float64,0x3fe28a7e0b6514fc,0x3feaad6d23e2eadd,2 +np.float64,0xffecf38395f9e706,0xd5437f3ee1cd61e4,2 +np.float64,0x3fcade92a935bd25,0x3fe3049f4c1da1d0,2 +np.float64,0x800ab25d95d564bc,0xaaa1a076d7c66e04,2 +np.float64,0xffc0989e1e21313c,0xd53467f3b8158298,2 +np.float64,0x3fd81523eeb02a48,0x3fe71a38d2da8a82,2 +np.float64,0x7fe5b9dd402b73ba,0x5541b7b9b8631010,2 +np.float64,0x2c160d94582c3,0x2a966e51b503a3d1,2 +np.float64,0x2c416ffa5882f,0x2a9675aaef8b29c4,2 +np.float64,0x7fefe2ff01bfc5fd,0x55442289faf22b86,2 +np.float64,0xbfd469bf5d28d37e,0xbfe5dd239ffdc7eb,2 +np.float64,0xbfdd56f3eabaade8,0xbfe8ac93244ca17b,2 +np.float64,0xbfe057b89160af71,0xbfe9941557340bb3,2 +np.float64,0x800c50e140b8a1c3,0xaaa2798ace9097ee,2 +np.float64,0xbfda5a8984b4b514,0xbfe7ce93d65a56b0,2 +np.float64,0xbfcd6458323ac8b0,0xbfe39872514127bf,2 +np.float64,0x3fefb1f5ebff63ec,0x3fefe5e761b49b89,2 +np.float64,0x3fea3abc1df47578,0x3fedf29a1c997863,2 +np.float64,0x7fcb4a528e3694a4,0x553815f169667213,2 +np.float64,0x8c77da7b18efc,0x2aa080e52bdedb54,2 +np.float64,0x800e5dde4c5cbbbd,0xaaa372b16fd8b1ad,2 +np.float64,0x3fd2976038a52ec0,0x3fe5316b4f79fdbc,2 +np.float64,0x69413a0ed2828,0x2a9dfacd9cb44286,2 +np.float64,0xbfebbac0bdb77582,0xbfee820d9288b631,2 +np.float64,0x1a12aa7c34256,0x2a92d407e073bbfe,2 +np.float64,0xbfc41a27c3283450,0xbfe143c8665b0d3c,2 +np.float64,0xffe4faa41369f548,0xd54183230e0ce613,2 +np.float64,0xbfdeae81f23d5d04,0xbfe90b734bf35b68,2 +np.float64,0x3fc984ba58330975,0x3fe2b19e9052008e,2 +np.float64,0x7fe6e51b8d2dca36,0x554207a74ae2bb39,2 +np.float64,0x80081a58a81034b2,0xaaa0117d4aff11c8,2 +np.float64,0x7fde3fddfe3c7fbb,0x553f67d0082acc67,2 +np.float64,0x3fac7c999038f933,0x3fd86ec2f5dc3aa4,2 +np.float64,0x7fa26b4c4c24d698,0x552a9e6ea8545c18,2 +np.float64,0x3fdacd06e6b59a0e,0x3fe7f0dc0e8f9c6d,2 +np.float64,0x80064b62cbec96c6,0xaa9d8ac0506fdd05,2 +np.float64,0xb858116170b1,0x2a8caea703d9ccc8,2 +np.float64,0xbfe8d94ccef1b29a,0xbfed69a8782cbf3d,2 +np.float64,0x8005607d6a6ac0fc,0xaa9c07cf8620b037,2 +np.float64,0xbfe66a52daacd4a6,0xbfec6b5e403e6864,2 +np.float64,0x7fc398c2e0273185,0x5535918245894606,2 +np.float64,0x74b2d7dce965c,0x2a9f077020defdbc,2 +np.float64,0x7fe8f7a4d9b1ef49,0x55428eeae210e8eb,2 +np.float64,0x80027deddc84fbdc,0xaa95b11ff9089745,2 +np.float64,0xffeba2a94e774552,0xd5433273f6568902,2 +np.float64,0x80002f8259405f05,0xaa8240b68d7b9dc4,2 +np.float64,0xbfdf0d84883e1b0a,0xbfe92532c69c5802,2 +np.float64,0xbfcdfa7b6b3bf4f8,0xbfe3b997a84d0914,2 +np.float64,0x800c18b04e183161,0xaaa25d46d60b15c6,2 +np.float64,0xffeaf1e37c35e3c6,0xd543092cd929ac19,2 +np.float64,0xbfc5aa07752b5410,0xbfe1b36ab5ec741f,2 +np.float64,0x3fe5c491d1eb8924,0x3fec24a1c3f6a178,2 +np.float64,0xbfeb736937f6e6d2,0xbfee67cd296e6fa9,2 +np.float64,0xffec3d5718787aad,0xd5435602e1a2cc43,2 +np.float64,0x7fe71e1da86e3c3a,0x55421691ead882cb,2 +np.float64,0x3fdd6ed0c93adda2,0x3fe8b341d066c43c,2 +np.float64,0x7fbe3d7a203c7af3,0x5533c83e53283430,2 +np.float64,0x3fdc20cb56384197,0x3fe854676360aba9,2 +np.float64,0xb7a1ac636f436,0x2aa20b9d40d66e78,2 +np.float64,0x3fb1491bb8229237,0x3fda0fabad1738ee,2 +np.float64,0xbfdf9c0ce73f381a,0xbfe94b716dbe35ee,2 +np.float64,0xbfbd4f0ad23a9e18,0xbfdf1397329a2dce,2 +np.float64,0xbfe4e0caac69c196,0xbfebc119b8a181cd,2 +np.float64,0x5753641aaea6d,0x2a9c2ba3e92b0cd2,2 +np.float64,0x72bb814ae5771,0x2a9eda92fada66de,2 +np.float64,0x57ed8f5aafdb3,0x2a9c3c2e1d42e609,2 +np.float64,0xffec33359c38666a,0xd54353b2acd0daf1,2 +np.float64,0x3fa5fe6e8c2bfce0,0x3fd66a0b3bf2720a,2 +np.float64,0xffe2dc8d7ca5b91a,0xd540e6ebc097d601,2 +np.float64,0x7fd99d260eb33a4b,0x553db626c9c75f78,2 +np.float64,0xbfe2dd73e425bae8,0xbfead4fc4b93a727,2 +np.float64,0xdcd4a583b9a95,0x2aa33094c9a17ad7,2 +np.float64,0x7fb0af6422215ec7,0x553039a606e8e64f,2 +np.float64,0x7fdfab6227bf56c3,0x553fe3b26164aeda,2 +np.float64,0x1e4d265e3c9a6,0x2a93cba8a1a8ae6d,2 +np.float64,0xbfdc7d097238fa12,0xbfe86ee2f24fd473,2 +np.float64,0x7fe5d35d29eba6b9,0x5541bea5878bce2b,2 +np.float64,0xffcb886a903710d4,0xd53828281710aab5,2 +np.float64,0xffe058c7ffe0b190,0xd5401d61e9a7cbcf,2 +np.float64,0x3ff0000000000000,0x3ff0000000000000,2 +np.float64,0xffd5b1c1132b6382,0xd53c1c839c098340,2 +np.float64,0x3fe2e7956725cf2b,0x3fead9c907b9d041,2 +np.float64,0x800a8ee293951dc6,0xaaa18ce3f079f118,2 +np.float64,0x7febcd3085b79a60,0x55433c47e1f822ad,2 +np.float64,0x3feb0e14cd761c2a,0x3fee423542102546,2 +np.float64,0x3fb45e6d0628bcda,0x3fdb86db67d0c992,2 +np.float64,0x7fa836e740306dce,0x552d2907cb8118b2,2 +np.float64,0x3fd15ba25b22b745,0x3fe4b6b018409d78,2 +np.float64,0xbfb59980ce2b3300,0xbfdc1206274cb51d,2 +np.float64,0x3fdef1b87fbde371,0x3fe91dafc62124a1,2 +np.float64,0x7fed37a4337a6f47,0x55438e7e0b50ae37,2 +np.float64,0xffe6c87633ad90ec,0xd542001f216ab448,2 +np.float64,0x8008d2548ab1a4a9,0xaaa087ad272d8e17,2 +np.float64,0xbfd1d6744da3ace8,0xbfe4e71965adda74,2 +np.float64,0xbfb27f751224fee8,0xbfdaa82132775406,2 +np.float64,0x3fe2b336ae65666d,0x3feac0e6b13ec2d2,2 +np.float64,0xffc6bac2262d7584,0xd536a951a2eecb49,2 +np.float64,0x7fdb661321b6cc25,0x553e62dfd7fcd3f3,2 +np.float64,0xffe83567d5706acf,0xd5425e4bb5027568,2 +np.float64,0xbf7f0693e03e0d00,0xbfc9235314d53f82,2 +np.float64,0x3feb32b218766564,0x3fee4fd5847f3722,2 +np.float64,0x3fec25d33df84ba6,0x3feea91fcd4aebab,2 +np.float64,0x7fe17abecb22f57d,0x55407a8ba661207c,2 +np.float64,0xbfe5674b1eeace96,0xbfebfc351708dc70,2 +np.float64,0xbfe51a2d2f6a345a,0xbfebda702c9d302a,2 +np.float64,0x3fec05584af80ab0,0x3fee9d502a7bf54d,2 +np.float64,0xffda8871dcb510e4,0xd53e10105f0365b5,2 +np.float64,0xbfc279c31824f388,0xbfe0c9354d871484,2 +np.float64,0x1cbed61e397dc,0x2a937364712cd518,2 +np.float64,0x800787d198af0fa4,0xaa9f5c847affa1d2,2 +np.float64,0x80079f6d65af3edc,0xaa9f7d2863368bbd,2 +np.float64,0xb942f1e97285e,0x2aa2193e0c513b7f,2 +np.float64,0x7fe9078263320f04,0x554292d85dee2c18,2 +np.float64,0xbfe4de0761a9bc0f,0xbfebbfe04116b829,2 +np.float64,0xbfdbe6f3fc37cde8,0xbfe843aea59a0749,2 +np.float64,0xffcb6c0de136d81c,0xd5381fd9c525b813,2 +np.float64,0x9b6bda9336d7c,0x2aa111c924c35386,2 +np.float64,0x3fe17eece422fdda,0x3fea2a9bacd78607,2 +np.float64,0xd8011c49b0024,0x2aa30c87574fc0c6,2 +np.float64,0xbfc0a08b3f214118,0xbfe034d48f0d8dc0,2 +np.float64,0x3fd60adb1eac15b8,0x3fe66e42e4e7e6b5,2 +np.float64,0x80011d68ea023ad3,0xaa909733befbb962,2 +np.float64,0xffb35ac32426b588,0xd5310c4be1c37270,2 +np.float64,0x3fee8b56c9bd16ae,0x3fef81d8d15f6939,2 +np.float64,0x3fdc10a45e382149,0x3fe84fbe4cf11e68,2 +np.float64,0xbfc85dc45e30bb88,0xbfe2687b5518abde,2 +np.float64,0x3fd53b85212a770a,0x3fe6270d6d920d0f,2 +np.float64,0x800fc158927f82b1,0xaaa40e303239586f,2 +np.float64,0x11af5e98235ed,0x2a908b04a790083f,2 +np.float64,0xbfe2a097afe54130,0xbfeab80269eece99,2 +np.float64,0xbfd74ac588ae958c,0xbfe6d8ca3828d0b8,2 +np.float64,0xffea18ab2ef43156,0xd542d579ab31df1e,2 +np.float64,0xbfecda7058f9b4e1,0xbfeeea29c33b7913,2 +np.float64,0x3fc4ac56ed2958b0,0x3fe16d3e2bd7806d,2 +np.float64,0x3feccc898cb99913,0x3feee531f217dcfa,2 +np.float64,0xffeb3a64c5b674c9,0xd5431a30a41f0905,2 +np.float64,0x3fe5a7ee212b4fdc,0x3fec1844af9076fc,2 +np.float64,0x80080fdb52301fb7,0xaaa00a8b4274db67,2 +np.float64,0x800b3e7e47d67cfd,0xaaa1ec2876959852,2 +np.float64,0x80063fb8ee2c7f73,0xaa9d7875c9f20d6f,2 +np.float64,0x7fdacf80d0b59f01,0x553e2acede4c62a8,2 +np.float64,0x401e9b24803d4,0x2a996a0a75d0e093,2 +np.float64,0x3fe6c29505ed852a,0x3fec907a6d8c10af,2 +np.float64,0x8005c04ee2cb809f,0xaa9caa9813faef46,2 +np.float64,0xbfe1360f21e26c1e,0xbfea06155d6985b6,2 +np.float64,0xffc70606682e0c0c,0xd536c239b9d4be0a,2 +np.float64,0x800e639afefcc736,0xaaa37547d0229a26,2 +np.float64,0x3fe5589290aab125,0x3febf5c925c4e6db,2 +np.float64,0x8003b59330276b27,0xaa98c47e44524335,2 +np.float64,0x800d67ec22dacfd8,0xaaa301251b6a730a,2 +np.float64,0x7fdaeb5025b5d69f,0x553e35397dfe87eb,2 +np.float64,0x3fdae32a24b5c654,0x3fe7f771bc108f6c,2 +np.float64,0xffe6c1fc93ad83f8,0xd541fe6a6a716756,2 +np.float64,0xbfd7b9c1d32f7384,0xbfe6fcdae563d638,2 +np.float64,0x800e1bea06fc37d4,0xaaa354c0bf61449c,2 +np.float64,0xbfd78f097aaf1e12,0xbfe6ef068329bdf4,2 +np.float64,0x7fea6a400874d47f,0x5542e905978ad722,2 +np.float64,0x8008b4377cb1686f,0xaaa074c87eee29f9,2 +np.float64,0x8002f3fb8d45e7f8,0xaa96f47ac539b614,2 +np.float64,0xbfcf2b3fd13e5680,0xbfe3fb91c0cc66ad,2 +np.float64,0xffecca2f5279945e,0xd54375f361075927,2 +np.float64,0x7ff0000000000000,0x7ff0000000000000,2 +np.float64,0x7f84d5a5a029ab4a,0x552178d1d4e8640e,2 +np.float64,0x3fea8a4b64351497,0x3fee10c332440eb2,2 +np.float64,0x800fe01ac1dfc036,0xaaa41b34d91a4bee,2 +np.float64,0x3fc0b3d8872167b1,0x3fe03b178d354f8d,2 +np.float64,0x5ee8b0acbdd17,0x2a9cf69f2e317729,2 +np.float64,0x8006ef0407adde09,0xaa9e82888f3dd83e,2 +np.float64,0x7fdbb08a07b76113,0x553e7e4e35b938b9,2 +np.float64,0x49663f9c92cc9,0x2a9a95e0affe5108,2 +np.float64,0x7fd9b87e79b370fc,0x553dc0b5cff3dc7d,2 +np.float64,0xbfd86ae657b0d5cc,0xbfe73584d02bdd2b,2 +np.float64,0x3fd4d4a13729a942,0x3fe6030a962aaaf8,2 +np.float64,0x7fcc246bcb3848d7,0x5538557309449bba,2 +np.float64,0xbfdc86a7d5b90d50,0xbfe871a2983c2a29,2 +np.float64,0xd2a6e995a54dd,0x2aa2e3e9c0fdd6c0,2 +np.float64,0x3f92eb447825d680,0x3fd0eb4fd2ba16d2,2 +np.float64,0x800d4001697a8003,0xaaa2ee358661b75c,2 +np.float64,0x3fd3705fd1a6e0c0,0x3fe582a6f321d7d6,2 +np.float64,0xbfcfdf51533fbea4,0xbfe421c3bdd9f2a3,2 +np.float64,0x3fe268e87964d1d1,0x3fea9d47e08aad8a,2 +np.float64,0x24b8901e49713,0x2a951adeefe7b31b,2 +np.float64,0x3fedb35d687b66bb,0x3fef36e440850bf8,2 +np.float64,0x3fb7ab5cbe2f56c0,0x3fdcf097380721c6,2 +np.float64,0x3f8c4eaa10389d54,0x3fceb7ecb605b73b,2 +np.float64,0xbfed831ed6fb063e,0xbfef25f462a336f1,2 +np.float64,0x7fd8c52112318a41,0x553d61b0ee609f58,2 +np.float64,0xbfe71c4ff76e38a0,0xbfecb5d32e789771,2 +np.float64,0xbfe35fb7b166bf70,0xbfeb12328e75ee6b,2 +np.float64,0x458e1a3a8b1c4,0x2a9a1cebadc81342,2 +np.float64,0x8003c1b3ad478368,0xaa98df5ed060b28c,2 +np.float64,0x7ff4000000000000,0x7ffc000000000000,2 +np.float64,0x7fe17098c162e131,0x5540775a9a3a104f,2 +np.float64,0xbfd95cb71732b96e,0xbfe7812acf7ea511,2 +np.float64,0x8000000000000001,0xa990000000000000,2 +np.float64,0xbfde0e7d9ebc1cfc,0xbfe8df9ca9e49a5b,2 +np.float64,0xffef4f67143e9ecd,0xd5440348a6a2f231,2 +np.float64,0x7fe37d23c826fa47,0x5541165de17caa03,2 +np.float64,0xbfcc0e5f85381cc0,0xbfe34b44b0deefe9,2 +np.float64,0x3fe858f1c470b1e4,0x3fed36ab90557d89,2 +np.float64,0x800e857278fd0ae5,0xaaa3847d13220545,2 +np.float64,0x3febd31a66f7a635,0x3fee8af90e66b043,2 +np.float64,0x7fd3fde1b127fbc2,0x553b5b186a49b968,2 +np.float64,0x3fd3dabb8b27b577,0x3fe5a99b446bed26,2 +np.float64,0xffeb4500f1768a01,0xd5431cab828e254a,2 +np.float64,0xffccca8fc6399520,0xd53884f8b505e79e,2 +np.float64,0xffeee9406b7dd280,0xd543ed6d27a1a899,2 +np.float64,0xffecdde0f0f9bbc1,0xd5437a6258b14092,2 +np.float64,0xe6b54005cd6a8,0x2aa378c25938dfda,2 +np.float64,0x7fe610f1022c21e1,0x5541cf460b972925,2 +np.float64,0xbfe5a170ec6b42e2,0xbfec1576081e3232,2 diff --git a/numpy/core/tests/data/umath-validation-set-cos.csv b/numpy/core/tests/data/umath-validation-set-cos.csv new file mode 100644 index 000000000000..e315c28b81f2 --- /dev/null +++ b/numpy/core/tests/data/umath-validation-set-cos.csv @@ -0,0 +1,1375 @@ +dtype,input,output,ulperrortol +## +ve denormals ## +np.float32,0x004b4716,0x3f800000,2 +np.float32,0x007b2490,0x3f800000,2 +np.float32,0x007c99fa,0x3f800000,2 +np.float32,0x00734a0c,0x3f800000,2 +np.float32,0x0070de24,0x3f800000,2 +np.float32,0x007fffff,0x3f800000,2 +np.float32,0x00000001,0x3f800000,2 +## -ve denormals ## +np.float32,0x80495d65,0x3f800000,2 +np.float32,0x806894f6,0x3f800000,2 +np.float32,0x80555a76,0x3f800000,2 +np.float32,0x804e1fb8,0x3f800000,2 +np.float32,0x80687de9,0x3f800000,2 +np.float32,0x807fffff,0x3f800000,2 +np.float32,0x80000001,0x3f800000,2 +## +/-0.0f, +/-FLT_MIN +/-FLT_MAX ## +np.float32,0x00000000,0x3f800000,2 +np.float32,0x80000000,0x3f800000,2 +np.float32,0x00800000,0x3f800000,2 +np.float32,0x80800000,0x3f800000,2 +## 1.00f + 0x00000001 ## +np.float32,0x3f800000,0x3f0a5140,2 +np.float32,0x3f800001,0x3f0a513f,2 +np.float32,0x3f800002,0x3f0a513d,2 +np.float32,0xc090a8b0,0xbe4332ce,2 +np.float32,0x41ce3184,0x3f4d1de1,2 +np.float32,0xc1d85848,0xbeaa8980,2 +np.float32,0x402b8820,0xbf653aa3,2 +np.float32,0x42b4e454,0xbf4a338b,2 +np.float32,0x42a67a60,0x3c58202e,2 +np.float32,0x41d92388,0xbed987c7,2 +np.float32,0x422dd66c,0x3f5dcab3,2 +np.float32,0xc28f5be6,0xbf5688d8,2 +np.float32,0x41ab2674,0xbf53aa3b,2 +np.float32,0x3f490fdb,0x3f3504f3,2 +np.float32,0xbf490fdb,0x3f3504f3,2 +np.float32,0x3fc90fdb,0xb33bbd2e,2 +np.float32,0xbfc90fdb,0xb33bbd2e,2 +np.float32,0x40490fdb,0xbf800000,2 +np.float32,0xc0490fdb,0xbf800000,2 +np.float32,0x3fc90fdb,0xb33bbd2e,2 +np.float32,0xbfc90fdb,0xb33bbd2e,2 +np.float32,0x40490fdb,0xbf800000,2 +np.float32,0xc0490fdb,0xbf800000,2 +np.float32,0x40c90fdb,0x3f800000,2 +np.float32,0xc0c90fdb,0x3f800000,2 +np.float32,0x4016cbe4,0xbf3504f3,2 +np.float32,0xc016cbe4,0xbf3504f3,2 +np.float32,0x4096cbe4,0x324cde2e,2 +np.float32,0xc096cbe4,0x324cde2e,2 +np.float32,0x4116cbe4,0xbf800000,2 +np.float32,0xc116cbe4,0xbf800000,2 +np.float32,0x40490fdb,0xbf800000,2 +np.float32,0xc0490fdb,0xbf800000,2 +np.float32,0x40c90fdb,0x3f800000,2 +np.float32,0xc0c90fdb,0x3f800000,2 +np.float32,0x41490fdb,0x3f800000,2 +np.float32,0xc1490fdb,0x3f800000,2 +np.float32,0x407b53d2,0xbf3504f1,2 +np.float32,0xc07b53d2,0xbf3504f1,2 +np.float32,0x40fb53d2,0xb4b5563d,2 +np.float32,0xc0fb53d2,0xb4b5563d,2 +np.float32,0x417b53d2,0xbf800000,2 +np.float32,0xc17b53d2,0xbf800000,2 +np.float32,0x4096cbe4,0x324cde2e,2 +np.float32,0xc096cbe4,0x324cde2e,2 +np.float32,0x4116cbe4,0xbf800000,2 +np.float32,0xc116cbe4,0xbf800000,2 +np.float32,0x4196cbe4,0x3f800000,2 +np.float32,0xc196cbe4,0x3f800000,2 +np.float32,0x40afede0,0x3f3504f7,2 +np.float32,0xc0afede0,0x3f3504f7,2 +np.float32,0x412fede0,0x353222c4,2 +np.float32,0xc12fede0,0x353222c4,2 +np.float32,0x41afede0,0xbf800000,2 +np.float32,0xc1afede0,0xbf800000,2 +np.float32,0x40c90fdb,0x3f800000,2 +np.float32,0xc0c90fdb,0x3f800000,2 +np.float32,0x41490fdb,0x3f800000,2 +np.float32,0xc1490fdb,0x3f800000,2 +np.float32,0x41c90fdb,0x3f800000,2 +np.float32,0xc1c90fdb,0x3f800000,2 +np.float32,0x40e231d6,0x3f3504f3,2 +np.float32,0xc0e231d6,0x3f3504f3,2 +np.float32,0x416231d6,0xb319a6a2,2 +np.float32,0xc16231d6,0xb319a6a2,2 +np.float32,0x41e231d6,0xbf800000,2 +np.float32,0xc1e231d6,0xbf800000,2 +np.float32,0x40fb53d2,0xb4b5563d,2 +np.float32,0xc0fb53d2,0xb4b5563d,2 +np.float32,0x417b53d2,0xbf800000,2 +np.float32,0xc17b53d2,0xbf800000,2 +np.float32,0x41fb53d2,0x3f800000,2 +np.float32,0xc1fb53d2,0x3f800000,2 +np.float32,0x410a3ae7,0xbf3504fb,2 +np.float32,0xc10a3ae7,0xbf3504fb,2 +np.float32,0x418a3ae7,0x35b08908,2 +np.float32,0xc18a3ae7,0x35b08908,2 +np.float32,0x420a3ae7,0xbf800000,2 +np.float32,0xc20a3ae7,0xbf800000,2 +np.float32,0x4116cbe4,0xbf800000,2 +np.float32,0xc116cbe4,0xbf800000,2 +np.float32,0x4196cbe4,0x3f800000,2 +np.float32,0xc196cbe4,0x3f800000,2 +np.float32,0x4216cbe4,0x3f800000,2 +np.float32,0xc216cbe4,0x3f800000,2 +np.float32,0x41235ce2,0xbf3504ef,2 +np.float32,0xc1235ce2,0xbf3504ef,2 +np.float32,0x41a35ce2,0xb53889b6,2 +np.float32,0xc1a35ce2,0xb53889b6,2 +np.float32,0x42235ce2,0xbf800000,2 +np.float32,0xc2235ce2,0xbf800000,2 +np.float32,0x412fede0,0x353222c4,2 +np.float32,0xc12fede0,0x353222c4,2 +np.float32,0x41afede0,0xbf800000,2 +np.float32,0xc1afede0,0xbf800000,2 +np.float32,0x422fede0,0x3f800000,2 +np.float32,0xc22fede0,0x3f800000,2 +np.float32,0x413c7edd,0x3f3504f4,2 +np.float32,0xc13c7edd,0x3f3504f4,2 +np.float32,0x41bc7edd,0x33800add,2 +np.float32,0xc1bc7edd,0x33800add,2 +np.float32,0x423c7edd,0xbf800000,2 +np.float32,0xc23c7edd,0xbf800000,2 +np.float32,0x41490fdb,0x3f800000,2 +np.float32,0xc1490fdb,0x3f800000,2 +np.float32,0x41c90fdb,0x3f800000,2 +np.float32,0xc1c90fdb,0x3f800000,2 +np.float32,0x42490fdb,0x3f800000,2 +np.float32,0xc2490fdb,0x3f800000,2 +np.float32,0x4155a0d9,0x3f3504eb,2 +np.float32,0xc155a0d9,0x3f3504eb,2 +np.float32,0x41d5a0d9,0xb5b3bc81,2 +np.float32,0xc1d5a0d9,0xb5b3bc81,2 +np.float32,0x4255a0d9,0xbf800000,2 +np.float32,0xc255a0d9,0xbf800000,2 +np.float32,0x416231d6,0xb319a6a2,2 +np.float32,0xc16231d6,0xb319a6a2,2 +np.float32,0x41e231d6,0xbf800000,2 +np.float32,0xc1e231d6,0xbf800000,2 +np.float32,0x426231d6,0x3f800000,2 +np.float32,0xc26231d6,0x3f800000,2 +np.float32,0x416ec2d4,0xbf3504f7,2 +np.float32,0xc16ec2d4,0xbf3504f7,2 +np.float32,0x41eec2d4,0x353ef0a7,2 +np.float32,0xc1eec2d4,0x353ef0a7,2 +np.float32,0x426ec2d4,0xbf800000,2 +np.float32,0xc26ec2d4,0xbf800000,2 +np.float32,0x417b53d2,0xbf800000,2 +np.float32,0xc17b53d2,0xbf800000,2 +np.float32,0x41fb53d2,0x3f800000,2 +np.float32,0xc1fb53d2,0x3f800000,2 +np.float32,0x427b53d2,0x3f800000,2 +np.float32,0xc27b53d2,0x3f800000,2 +np.float32,0x4183f268,0xbf3504e7,2 +np.float32,0xc183f268,0xbf3504e7,2 +np.float32,0x4203f268,0xb6059a13,2 +np.float32,0xc203f268,0xb6059a13,2 +np.float32,0x4283f268,0xbf800000,2 +np.float32,0xc283f268,0xbf800000,2 +np.float32,0x418a3ae7,0x35b08908,2 +np.float32,0xc18a3ae7,0x35b08908,2 +np.float32,0x420a3ae7,0xbf800000,2 +np.float32,0xc20a3ae7,0xbf800000,2 +np.float32,0x428a3ae7,0x3f800000,2 +np.float32,0xc28a3ae7,0x3f800000,2 +np.float32,0x41908365,0x3f3504f0,2 +np.float32,0xc1908365,0x3f3504f0,2 +np.float32,0x42108365,0xb512200d,2 +np.float32,0xc2108365,0xb512200d,2 +np.float32,0x42908365,0xbf800000,2 +np.float32,0xc2908365,0xbf800000,2 +np.float32,0x4196cbe4,0x3f800000,2 +np.float32,0xc196cbe4,0x3f800000,2 +np.float32,0x4216cbe4,0x3f800000,2 +np.float32,0xc216cbe4,0x3f800000,2 +np.float32,0x4296cbe4,0x3f800000,2 +np.float32,0xc296cbe4,0x3f800000,2 +np.float32,0x419d1463,0x3f3504ef,2 +np.float32,0xc19d1463,0x3f3504ef,2 +np.float32,0x421d1463,0xb5455799,2 +np.float32,0xc21d1463,0xb5455799,2 +np.float32,0x429d1463,0xbf800000,2 +np.float32,0xc29d1463,0xbf800000,2 +np.float32,0x41a35ce2,0xb53889b6,2 +np.float32,0xc1a35ce2,0xb53889b6,2 +np.float32,0x42235ce2,0xbf800000,2 +np.float32,0xc2235ce2,0xbf800000,2 +np.float32,0x42a35ce2,0x3f800000,2 +np.float32,0xc2a35ce2,0x3f800000,2 +np.float32,0x41a9a561,0xbf3504ff,2 +np.float32,0xc1a9a561,0xbf3504ff,2 +np.float32,0x4229a561,0x360733d0,2 +np.float32,0xc229a561,0x360733d0,2 +np.float32,0x42a9a561,0xbf800000,2 +np.float32,0xc2a9a561,0xbf800000,2 +np.float32,0x41afede0,0xbf800000,2 +np.float32,0xc1afede0,0xbf800000,2 +np.float32,0x422fede0,0x3f800000,2 +np.float32,0xc22fede0,0x3f800000,2 +np.float32,0x42afede0,0x3f800000,2 +np.float32,0xc2afede0,0x3f800000,2 +np.float32,0x41b6365e,0xbf3504f6,2 +np.float32,0xc1b6365e,0xbf3504f6,2 +np.float32,0x4236365e,0x350bb91c,2 +np.float32,0xc236365e,0x350bb91c,2 +np.float32,0x42b6365e,0xbf800000,2 +np.float32,0xc2b6365e,0xbf800000,2 +np.float32,0x41bc7edd,0x33800add,2 +np.float32,0xc1bc7edd,0x33800add,2 +np.float32,0x423c7edd,0xbf800000,2 +np.float32,0xc23c7edd,0xbf800000,2 +np.float32,0x42bc7edd,0x3f800000,2 +np.float32,0xc2bc7edd,0x3f800000,2 +np.float32,0x41c2c75c,0x3f3504f8,2 +np.float32,0xc1c2c75c,0x3f3504f8,2 +np.float32,0x4242c75c,0x354bbe8a,2 +np.float32,0xc242c75c,0x354bbe8a,2 +np.float32,0x42c2c75c,0xbf800000,2 +np.float32,0xc2c2c75c,0xbf800000,2 +np.float32,0x41c90fdb,0x3f800000,2 +np.float32,0xc1c90fdb,0x3f800000,2 +np.float32,0x42490fdb,0x3f800000,2 +np.float32,0xc2490fdb,0x3f800000,2 +np.float32,0x42c90fdb,0x3f800000,2 +np.float32,0xc2c90fdb,0x3f800000,2 +np.float32,0x41cf585a,0x3f3504e7,2 +np.float32,0xc1cf585a,0x3f3504e7,2 +np.float32,0x424f585a,0xb608cd8c,2 +np.float32,0xc24f585a,0xb608cd8c,2 +np.float32,0x42cf585a,0xbf800000,2 +np.float32,0xc2cf585a,0xbf800000,2 +np.float32,0x41d5a0d9,0xb5b3bc81,2 +np.float32,0xc1d5a0d9,0xb5b3bc81,2 +np.float32,0x4255a0d9,0xbf800000,2 +np.float32,0xc255a0d9,0xbf800000,2 +np.float32,0x42d5a0d9,0x3f800000,2 +np.float32,0xc2d5a0d9,0x3f800000,2 +np.float32,0x41dbe958,0xbf350507,2 +np.float32,0xc1dbe958,0xbf350507,2 +np.float32,0x425be958,0x365eab75,2 +np.float32,0xc25be958,0x365eab75,2 +np.float32,0x42dbe958,0xbf800000,2 +np.float32,0xc2dbe958,0xbf800000,2 +np.float32,0x41e231d6,0xbf800000,2 +np.float32,0xc1e231d6,0xbf800000,2 +np.float32,0x426231d6,0x3f800000,2 +np.float32,0xc26231d6,0x3f800000,2 +np.float32,0x42e231d6,0x3f800000,2 +np.float32,0xc2e231d6,0x3f800000,2 +np.float32,0x41e87a55,0xbf3504ef,2 +np.float32,0xc1e87a55,0xbf3504ef,2 +np.float32,0x42687a55,0xb552257b,2 +np.float32,0xc2687a55,0xb552257b,2 +np.float32,0x42e87a55,0xbf800000,2 +np.float32,0xc2e87a55,0xbf800000,2 +np.float32,0x41eec2d4,0x353ef0a7,2 +np.float32,0xc1eec2d4,0x353ef0a7,2 +np.float32,0x426ec2d4,0xbf800000,2 +np.float32,0xc26ec2d4,0xbf800000,2 +np.float32,0x42eec2d4,0x3f800000,2 +np.float32,0xc2eec2d4,0x3f800000,2 +np.float32,0x41f50b53,0x3f3504ff,2 +np.float32,0xc1f50b53,0x3f3504ff,2 +np.float32,0x42750b53,0x360a6748,2 +np.float32,0xc2750b53,0x360a6748,2 +np.float32,0x42f50b53,0xbf800000,2 +np.float32,0xc2f50b53,0xbf800000,2 +np.float32,0x41fb53d2,0x3f800000,2 +np.float32,0xc1fb53d2,0x3f800000,2 +np.float32,0x427b53d2,0x3f800000,2 +np.float32,0xc27b53d2,0x3f800000,2 +np.float32,0x42fb53d2,0x3f800000,2 +np.float32,0xc2fb53d2,0x3f800000,2 +np.float32,0x4200ce28,0x3f3504f6,2 +np.float32,0xc200ce28,0x3f3504f6,2 +np.float32,0x4280ce28,0x34fdd672,2 +np.float32,0xc280ce28,0x34fdd672,2 +np.float32,0x4300ce28,0xbf800000,2 +np.float32,0xc300ce28,0xbf800000,2 +np.float32,0x4203f268,0xb6059a13,2 +np.float32,0xc203f268,0xb6059a13,2 +np.float32,0x4283f268,0xbf800000,2 +np.float32,0xc283f268,0xbf800000,2 +np.float32,0x4303f268,0x3f800000,2 +np.float32,0xc303f268,0x3f800000,2 +np.float32,0x420716a7,0xbf3504f8,2 +np.float32,0xc20716a7,0xbf3504f8,2 +np.float32,0x428716a7,0x35588c6d,2 +np.float32,0xc28716a7,0x35588c6d,2 +np.float32,0x430716a7,0xbf800000,2 +np.float32,0xc30716a7,0xbf800000,2 +np.float32,0x420a3ae7,0xbf800000,2 +np.float32,0xc20a3ae7,0xbf800000,2 +np.float32,0x428a3ae7,0x3f800000,2 +np.float32,0xc28a3ae7,0x3f800000,2 +np.float32,0x430a3ae7,0x3f800000,2 +np.float32,0xc30a3ae7,0x3f800000,2 +np.float32,0x420d5f26,0xbf3504e7,2 +np.float32,0xc20d5f26,0xbf3504e7,2 +np.float32,0x428d5f26,0xb60c0105,2 +np.float32,0xc28d5f26,0xb60c0105,2 +np.float32,0x430d5f26,0xbf800000,2 +np.float32,0xc30d5f26,0xbf800000,2 +np.float32,0x42108365,0xb512200d,2 +np.float32,0xc2108365,0xb512200d,2 +np.float32,0x42908365,0xbf800000,2 +np.float32,0xc2908365,0xbf800000,2 +np.float32,0x43108365,0x3f800000,2 +np.float32,0xc3108365,0x3f800000,2 +np.float32,0x4213a7a5,0x3f350507,2 +np.float32,0xc213a7a5,0x3f350507,2 +np.float32,0x4293a7a5,0x3661deee,2 +np.float32,0xc293a7a5,0x3661deee,2 +np.float32,0x4313a7a5,0xbf800000,2 +np.float32,0xc313a7a5,0xbf800000,2 +np.float32,0x4216cbe4,0x3f800000,2 +np.float32,0xc216cbe4,0x3f800000,2 +np.float32,0x4296cbe4,0x3f800000,2 +np.float32,0xc296cbe4,0x3f800000,2 +np.float32,0x4316cbe4,0x3f800000,2 +np.float32,0xc316cbe4,0x3f800000,2 +np.float32,0x4219f024,0x3f3504d8,2 +np.float32,0xc219f024,0x3f3504d8,2 +np.float32,0x4299f024,0xb69bde6c,2 +np.float32,0xc299f024,0xb69bde6c,2 +np.float32,0x4319f024,0xbf800000,2 +np.float32,0xc319f024,0xbf800000,2 +np.float32,0x421d1463,0xb5455799,2 +np.float32,0xc21d1463,0xb5455799,2 +np.float32,0x429d1463,0xbf800000,2 +np.float32,0xc29d1463,0xbf800000,2 +np.float32,0x431d1463,0x3f800000,2 +np.float32,0xc31d1463,0x3f800000,2 +np.float32,0x422038a3,0xbf350516,2 +np.float32,0xc22038a3,0xbf350516,2 +np.float32,0x42a038a3,0x36c6cd61,2 +np.float32,0xc2a038a3,0x36c6cd61,2 +np.float32,0x432038a3,0xbf800000,2 +np.float32,0xc32038a3,0xbf800000,2 +np.float32,0x42235ce2,0xbf800000,2 +np.float32,0xc2235ce2,0xbf800000,2 +np.float32,0x42a35ce2,0x3f800000,2 +np.float32,0xc2a35ce2,0x3f800000,2 +np.float32,0x43235ce2,0x3f800000,2 +np.float32,0xc3235ce2,0x3f800000,2 +np.float32,0x42268121,0xbf3504f6,2 +np.float32,0xc2268121,0xbf3504f6,2 +np.float32,0x42a68121,0x34e43aac,2 +np.float32,0xc2a68121,0x34e43aac,2 +np.float32,0x43268121,0xbf800000,2 +np.float32,0xc3268121,0xbf800000,2 +np.float32,0x4229a561,0x360733d0,2 +np.float32,0xc229a561,0x360733d0,2 +np.float32,0x42a9a561,0xbf800000,2 +np.float32,0xc2a9a561,0xbf800000,2 +np.float32,0x4329a561,0x3f800000,2 +np.float32,0xc329a561,0x3f800000,2 +np.float32,0x422cc9a0,0x3f3504f8,2 +np.float32,0xc22cc9a0,0x3f3504f8,2 +np.float32,0x42acc9a0,0x35655a50,2 +np.float32,0xc2acc9a0,0x35655a50,2 +np.float32,0x432cc9a0,0xbf800000,2 +np.float32,0xc32cc9a0,0xbf800000,2 +np.float32,0x422fede0,0x3f800000,2 +np.float32,0xc22fede0,0x3f800000,2 +np.float32,0x42afede0,0x3f800000,2 +np.float32,0xc2afede0,0x3f800000,2 +np.float32,0x432fede0,0x3f800000,2 +np.float32,0xc32fede0,0x3f800000,2 +np.float32,0x4233121f,0x3f3504e7,2 +np.float32,0xc233121f,0x3f3504e7,2 +np.float32,0x42b3121f,0xb60f347d,2 +np.float32,0xc2b3121f,0xb60f347d,2 +np.float32,0x4333121f,0xbf800000,2 +np.float32,0xc333121f,0xbf800000,2 +np.float32,0x4236365e,0x350bb91c,2 +np.float32,0xc236365e,0x350bb91c,2 +np.float32,0x42b6365e,0xbf800000,2 +np.float32,0xc2b6365e,0xbf800000,2 +np.float32,0x4336365e,0x3f800000,2 +np.float32,0xc336365e,0x3f800000,2 +np.float32,0x42395a9e,0xbf350507,2 +np.float32,0xc2395a9e,0xbf350507,2 +np.float32,0x42b95a9e,0x36651267,2 +np.float32,0xc2b95a9e,0x36651267,2 +np.float32,0x43395a9e,0xbf800000,2 +np.float32,0xc3395a9e,0xbf800000,2 +np.float32,0x423c7edd,0xbf800000,2 +np.float32,0xc23c7edd,0xbf800000,2 +np.float32,0x42bc7edd,0x3f800000,2 +np.float32,0xc2bc7edd,0x3f800000,2 +np.float32,0x433c7edd,0x3f800000,2 +np.float32,0xc33c7edd,0x3f800000,2 +np.float32,0x423fa31d,0xbf3504d7,2 +np.float32,0xc23fa31d,0xbf3504d7,2 +np.float32,0x42bfa31d,0xb69d7828,2 +np.float32,0xc2bfa31d,0xb69d7828,2 +np.float32,0x433fa31d,0xbf800000,2 +np.float32,0xc33fa31d,0xbf800000,2 +np.float32,0x4242c75c,0x354bbe8a,2 +np.float32,0xc242c75c,0x354bbe8a,2 +np.float32,0x42c2c75c,0xbf800000,2 +np.float32,0xc2c2c75c,0xbf800000,2 +np.float32,0x4342c75c,0x3f800000,2 +np.float32,0xc342c75c,0x3f800000,2 +np.float32,0x4245eb9c,0x3f350517,2 +np.float32,0xc245eb9c,0x3f350517,2 +np.float32,0x42c5eb9c,0x36c8671d,2 +np.float32,0xc2c5eb9c,0x36c8671d,2 +np.float32,0x4345eb9c,0xbf800000,2 +np.float32,0xc345eb9c,0xbf800000,2 +np.float32,0x42490fdb,0x3f800000,2 +np.float32,0xc2490fdb,0x3f800000,2 +np.float32,0x42c90fdb,0x3f800000,2 +np.float32,0xc2c90fdb,0x3f800000,2 +np.float32,0x43490fdb,0x3f800000,2 +np.float32,0xc3490fdb,0x3f800000,2 +np.float32,0x424c341a,0x3f3504f5,2 +np.float32,0xc24c341a,0x3f3504f5,2 +np.float32,0x42cc341a,0x34ca9ee6,2 +np.float32,0xc2cc341a,0x34ca9ee6,2 +np.float32,0x434c341a,0xbf800000,2 +np.float32,0xc34c341a,0xbf800000,2 +np.float32,0x424f585a,0xb608cd8c,2 +np.float32,0xc24f585a,0xb608cd8c,2 +np.float32,0x42cf585a,0xbf800000,2 +np.float32,0xc2cf585a,0xbf800000,2 +np.float32,0x434f585a,0x3f800000,2 +np.float32,0xc34f585a,0x3f800000,2 +np.float32,0x42527c99,0xbf3504f9,2 +np.float32,0xc2527c99,0xbf3504f9,2 +np.float32,0x42d27c99,0x35722833,2 +np.float32,0xc2d27c99,0x35722833,2 +np.float32,0x43527c99,0xbf800000,2 +np.float32,0xc3527c99,0xbf800000,2 +np.float32,0x4255a0d9,0xbf800000,2 +np.float32,0xc255a0d9,0xbf800000,2 +np.float32,0x42d5a0d9,0x3f800000,2 +np.float32,0xc2d5a0d9,0x3f800000,2 +np.float32,0x4355a0d9,0x3f800000,2 +np.float32,0xc355a0d9,0x3f800000,2 +np.float32,0x4258c518,0xbf3504e6,2 +np.float32,0xc258c518,0xbf3504e6,2 +np.float32,0x42d8c518,0xb61267f6,2 +np.float32,0xc2d8c518,0xb61267f6,2 +np.float32,0x4358c518,0xbf800000,2 +np.float32,0xc358c518,0xbf800000,2 +np.float32,0x425be958,0x365eab75,2 +np.float32,0xc25be958,0x365eab75,2 +np.float32,0x42dbe958,0xbf800000,2 +np.float32,0xc2dbe958,0xbf800000,2 +np.float32,0x435be958,0x3f800000,2 +np.float32,0xc35be958,0x3f800000,2 +np.float32,0x425f0d97,0x3f350508,2 +np.float32,0xc25f0d97,0x3f350508,2 +np.float32,0x42df0d97,0x366845e0,2 +np.float32,0xc2df0d97,0x366845e0,2 +np.float32,0x435f0d97,0xbf800000,2 +np.float32,0xc35f0d97,0xbf800000,2 +np.float32,0x426231d6,0x3f800000,2 +np.float32,0xc26231d6,0x3f800000,2 +np.float32,0x42e231d6,0x3f800000,2 +np.float32,0xc2e231d6,0x3f800000,2 +np.float32,0x436231d6,0x3f800000,2 +np.float32,0xc36231d6,0x3f800000,2 +np.float32,0x42655616,0x3f3504d7,2 +np.float32,0xc2655616,0x3f3504d7,2 +np.float32,0x42e55616,0xb69f11e5,2 +np.float32,0xc2e55616,0xb69f11e5,2 +np.float32,0x43655616,0xbf800000,2 +np.float32,0xc3655616,0xbf800000,2 +np.float32,0x42687a55,0xb552257b,2 +np.float32,0xc2687a55,0xb552257b,2 +np.float32,0x42e87a55,0xbf800000,2 +np.float32,0xc2e87a55,0xbf800000,2 +np.float32,0x43687a55,0x3f800000,2 +np.float32,0xc3687a55,0x3f800000,2 +np.float32,0x426b9e95,0xbf350517,2 +np.float32,0xc26b9e95,0xbf350517,2 +np.float32,0x42eb9e95,0x36ca00d9,2 +np.float32,0xc2eb9e95,0x36ca00d9,2 +np.float32,0x436b9e95,0xbf800000,2 +np.float32,0xc36b9e95,0xbf800000,2 +np.float32,0x426ec2d4,0xbf800000,2 +np.float32,0xc26ec2d4,0xbf800000,2 +np.float32,0x42eec2d4,0x3f800000,2 +np.float32,0xc2eec2d4,0x3f800000,2 +np.float32,0x436ec2d4,0x3f800000,2 +np.float32,0xc36ec2d4,0x3f800000,2 +np.float32,0x4271e713,0xbf3504f5,2 +np.float32,0xc271e713,0xbf3504f5,2 +np.float32,0x42f1e713,0x34b10321,2 +np.float32,0xc2f1e713,0x34b10321,2 +np.float32,0x4371e713,0xbf800000,2 +np.float32,0xc371e713,0xbf800000,2 +np.float32,0x42750b53,0x360a6748,2 +np.float32,0xc2750b53,0x360a6748,2 +np.float32,0x42f50b53,0xbf800000,2 +np.float32,0xc2f50b53,0xbf800000,2 +np.float32,0x43750b53,0x3f800000,2 +np.float32,0xc3750b53,0x3f800000,2 +np.float32,0x42782f92,0x3f3504f9,2 +np.float32,0xc2782f92,0x3f3504f9,2 +np.float32,0x42f82f92,0x357ef616,2 +np.float32,0xc2f82f92,0x357ef616,2 +np.float32,0x43782f92,0xbf800000,2 +np.float32,0xc3782f92,0xbf800000,2 +np.float32,0x427b53d2,0x3f800000,2 +np.float32,0xc27b53d2,0x3f800000,2 +np.float32,0x42fb53d2,0x3f800000,2 +np.float32,0xc2fb53d2,0x3f800000,2 +np.float32,0x437b53d2,0x3f800000,2 +np.float32,0xc37b53d2,0x3f800000,2 +np.float32,0x427e7811,0x3f3504e6,2 +np.float32,0xc27e7811,0x3f3504e6,2 +np.float32,0x42fe7811,0xb6159b6f,2 +np.float32,0xc2fe7811,0xb6159b6f,2 +np.float32,0x437e7811,0xbf800000,2 +np.float32,0xc37e7811,0xbf800000,2 +np.float32,0x4280ce28,0x34fdd672,2 +np.float32,0xc280ce28,0x34fdd672,2 +np.float32,0x4300ce28,0xbf800000,2 +np.float32,0xc300ce28,0xbf800000,2 +np.float32,0x4380ce28,0x3f800000,2 +np.float32,0xc380ce28,0x3f800000,2 +np.float32,0x42826048,0xbf350508,2 +np.float32,0xc2826048,0xbf350508,2 +np.float32,0x43026048,0x366b7958,2 +np.float32,0xc3026048,0x366b7958,2 +np.float32,0x43826048,0xbf800000,2 +np.float32,0xc3826048,0xbf800000,2 +np.float32,0x4283f268,0xbf800000,2 +np.float32,0xc283f268,0xbf800000,2 +np.float32,0x4303f268,0x3f800000,2 +np.float32,0xc303f268,0x3f800000,2 +np.float32,0x4383f268,0x3f800000,2 +np.float32,0xc383f268,0x3f800000,2 +np.float32,0x42858487,0xbf350504,2 +np.float32,0xc2858487,0xbf350504,2 +np.float32,0x43058487,0x363ea8be,2 +np.float32,0xc3058487,0x363ea8be,2 +np.float32,0x43858487,0xbf800000,2 +np.float32,0xc3858487,0xbf800000,2 +np.float32,0x428716a7,0x35588c6d,2 +np.float32,0xc28716a7,0x35588c6d,2 +np.float32,0x430716a7,0xbf800000,2 +np.float32,0xc30716a7,0xbf800000,2 +np.float32,0x438716a7,0x3f800000,2 +np.float32,0xc38716a7,0x3f800000,2 +np.float32,0x4288a8c7,0x3f350517,2 +np.float32,0xc288a8c7,0x3f350517,2 +np.float32,0x4308a8c7,0x36cb9a96,2 +np.float32,0xc308a8c7,0x36cb9a96,2 +np.float32,0x4388a8c7,0xbf800000,2 +np.float32,0xc388a8c7,0xbf800000,2 +np.float32,0x428a3ae7,0x3f800000,2 +np.float32,0xc28a3ae7,0x3f800000,2 +np.float32,0x430a3ae7,0x3f800000,2 +np.float32,0xc30a3ae7,0x3f800000,2 +np.float32,0x438a3ae7,0x3f800000,2 +np.float32,0xc38a3ae7,0x3f800000,2 +np.float32,0x428bcd06,0x3f3504f5,2 +np.float32,0xc28bcd06,0x3f3504f5,2 +np.float32,0x430bcd06,0x3497675b,2 +np.float32,0xc30bcd06,0x3497675b,2 +np.float32,0x438bcd06,0xbf800000,2 +np.float32,0xc38bcd06,0xbf800000,2 +np.float32,0x428d5f26,0xb60c0105,2 +np.float32,0xc28d5f26,0xb60c0105,2 +np.float32,0x430d5f26,0xbf800000,2 +np.float32,0xc30d5f26,0xbf800000,2 +np.float32,0x438d5f26,0x3f800000,2 +np.float32,0xc38d5f26,0x3f800000,2 +np.float32,0x428ef146,0xbf350526,2 +np.float32,0xc28ef146,0xbf350526,2 +np.float32,0x430ef146,0x3710bc40,2 +np.float32,0xc30ef146,0x3710bc40,2 +np.float32,0x438ef146,0xbf800000,2 +np.float32,0xc38ef146,0xbf800000,2 +np.float32,0x42908365,0xbf800000,2 +np.float32,0xc2908365,0xbf800000,2 +np.float32,0x43108365,0x3f800000,2 +np.float32,0xc3108365,0x3f800000,2 +np.float32,0x43908365,0x3f800000,2 +np.float32,0xc3908365,0x3f800000,2 +np.float32,0x42921585,0xbf3504e6,2 +np.float32,0xc2921585,0xbf3504e6,2 +np.float32,0x43121585,0xb618cee8,2 +np.float32,0xc3121585,0xb618cee8,2 +np.float32,0x43921585,0xbf800000,2 +np.float32,0xc3921585,0xbf800000,2 +np.float32,0x4293a7a5,0x3661deee,2 +np.float32,0xc293a7a5,0x3661deee,2 +np.float32,0x4313a7a5,0xbf800000,2 +np.float32,0xc313a7a5,0xbf800000,2 +np.float32,0x4393a7a5,0x3f800000,2 +np.float32,0xc393a7a5,0x3f800000,2 +np.float32,0x429539c5,0x3f350536,2 +np.float32,0xc29539c5,0x3f350536,2 +np.float32,0x431539c5,0x373bab34,2 +np.float32,0xc31539c5,0x373bab34,2 +np.float32,0x439539c5,0xbf800000,2 +np.float32,0xc39539c5,0xbf800000,2 +np.float32,0x4296cbe4,0x3f800000,2 +np.float32,0xc296cbe4,0x3f800000,2 +np.float32,0x4316cbe4,0x3f800000,2 +np.float32,0xc316cbe4,0x3f800000,2 +np.float32,0x4396cbe4,0x3f800000,2 +np.float32,0xc396cbe4,0x3f800000,2 +np.float32,0x42985e04,0x3f3504d7,2 +np.float32,0xc2985e04,0x3f3504d7,2 +np.float32,0x43185e04,0xb6a2455d,2 +np.float32,0xc3185e04,0xb6a2455d,2 +np.float32,0x43985e04,0xbf800000,2 +np.float32,0xc3985e04,0xbf800000,2 +np.float32,0x4299f024,0xb69bde6c,2 +np.float32,0xc299f024,0xb69bde6c,2 +np.float32,0x4319f024,0xbf800000,2 +np.float32,0xc319f024,0xbf800000,2 +np.float32,0x4399f024,0x3f800000,2 +np.float32,0xc399f024,0x3f800000,2 +np.float32,0x429b8243,0xbf3504ea,2 +np.float32,0xc29b8243,0xbf3504ea,2 +np.float32,0x431b8243,0xb5cb2eb8,2 +np.float32,0xc31b8243,0xb5cb2eb8,2 +np.float32,0x439b8243,0xbf800000,2 +np.float32,0xc39b8243,0xbf800000,2 +np.float32,0x435b2047,0x3f3504c1,2 +np.float32,0x42a038a2,0xb5e4ca7e,2 +np.float32,0x432038a2,0xbf800000,2 +np.float32,0x4345eb9b,0xbf800000,2 +np.float32,0x42c5eb9b,0xb5de638c,2 +np.float32,0x42eb9e94,0xb5d7fc9b,2 +np.float32,0x4350ea79,0x3631dadb,2 +np.float32,0x42dbe957,0xbf800000,2 +np.float32,0x425be957,0xb505522a,2 +np.float32,0x435be957,0x3f800000,2 +np.float32,0x46027eb2,0x3e7d94c9,2 +np.float32,0x4477baed,0xbe7f1824,2 +np.float32,0x454b8024,0x3e7f5268,2 +np.float32,0x455d2c09,0x3e7f40cb,2 +np.float32,0x4768d3de,0xba14b4af,2 +np.float32,0x46c1e7cd,0x3e7fb102,2 +np.float32,0x44a52949,0xbe7dc9d5,2 +np.float32,0x4454633a,0x3e7dbc7d,2 +np.float32,0x4689810b,0x3e7eb02b,2 +np.float32,0x473473cd,0xbe7eef6f,2 +np.float32,0x44a5193f,0x3e7e1b1f,2 +np.float32,0x46004b36,0x3e7dac59,2 +np.float32,0x467f604b,0x3d7ffd3a,2 +np.float32,0x45ea1805,0x3dffd2e0,2 +np.float32,0x457b6af3,0x3dff7831,2 +np.float32,0x44996159,0xbe7d85f4,2 +np.float32,0x47883553,0xbb80584e,2 +np.float32,0x44e19f0c,0xbdffcfe6,2 +np.float32,0x472b3bf6,0xbe7f7a82,2 +np.float32,0x4600bb4e,0x3a135e33,2 +np.float32,0x449f4556,0x3e7e42e5,2 +np.float32,0x474e9420,0x3dff77b2,2 +np.float32,0x45cbdb23,0x3dff7240,2 +np.float32,0x44222747,0x3dffb039,2 +np.float32,0x4772e419,0xbdff74b8,2 +np.float64,0x1,0x3ff0000000000000,4 +np.float64,0x8000000000000001,0x3ff0000000000000,4 +np.float64,0x10000000000000,0x3ff0000000000000,4 +np.float64,0x8010000000000000,0x3ff0000000000000,4 +np.float64,0x7fefffffffffffff,0xbfefffe62ecfab75,4 +np.float64,0xffefffffffffffff,0xbfefffe62ecfab75,4 +np.float64,0x7ff0000000000000,0xfff8000000000000,4 +np.float64,0xfff0000000000000,0xfff8000000000000,4 +np.float64,0x7ff8000000000000,0x7ff8000000000000,4 +np.float64,0x7ff4000000000000,0x7ffc000000000000,4 +np.float64,0xbfc28bd9dd2517b4,0x3fefaa28ba13a702,4 +np.float64,0x3fb673c62e2ce790,0x3fefe083847a717f,4 +np.float64,0xbfe3e1dac7e7c3b6,0x3fea0500ba099f3a,4 +np.float64,0xbfbe462caa3c8c58,0x3fefc6c8b9c1c87c,4 +np.float64,0xbfb9353576326a68,0x3fefd8513e50e6b1,4 +np.float64,0xbfc05e798520bcf4,0x3fefbd1ad81cf089,4 +np.float64,0xbfe3ca3be2e79478,0x3fea12b995ea6574,4 +np.float64,0xbfde875d46bd0eba,0x3fec6d888662a824,4 +np.float64,0x3fafc4e02c3f89c0,0x3feff03c34bffd69,4 +np.float64,0xbf98855848310ac0,0x3feffda6c1588bdb,4 +np.float64,0x3fe66c51186cd8a2,0x3fe875c61c630ecb,4 +np.float64,0xbfedff1c3b7bfe38,0x3fe2f0c8c9e8fa39,4 +np.float64,0x3fd6082267ac1044,0x3fee1f6023695050,4 +np.float64,0xbfe78449b06f0894,0x3fe7bda2b223850e,4 +np.float64,0x3feedb8e63fdb71c,0x3fe23d5dfd2dd33f,4 +np.float64,0xbfc0a9de3d2153bc,0x3fefbaadf5e5285e,4 +np.float64,0x3fc04c67432098d0,0x3fefbdae07b7de8d,4 +np.float64,0xbfeeef84c4fddf0a,0x3fe22cf37f309d88,4 +np.float64,0x3fc04bb025209760,0x3fefbdb3d7d34ecf,4 +np.float64,0x3fd6b84d48ad709c,0x3fee013403da6e2a,4 +np.float64,0x3fec1ae25d7835c4,0x3fe46e62195cf274,4 +np.float64,0xbfdc6fdf9bb8dfc0,0x3fece48dc78bbb2e,4 +np.float64,0x3fb4db2c9229b660,0x3fefe4d42f79bf49,4 +np.float64,0xbfc0ed698521dad4,0x3fefb8785ea658c9,4 +np.float64,0xbfee82772b7d04ee,0x3fe2864a80efe8e9,4 +np.float64,0x3fd575b664aaeb6c,0x3fee37c669a12879,4 +np.float64,0x3fe4afb1c5e95f64,0x3fe98b177194439c,4 +np.float64,0x3fd93962f9b272c4,0x3fed8bef61876294,4 +np.float64,0x3fd97ae025b2f5c0,0x3fed7f4cfbf4d300,4 +np.float64,0xbfd9afdb1bb35fb6,0x3fed74fdc44dabb1,4 +np.float64,0x3f8ae65e3035cc80,0x3fefff4b1a0ea62b,4 +np.float64,0xbfe7e58664efcb0d,0x3fe77c02a1cbb670,4 +np.float64,0x3fe5f68b37ebed16,0x3fe8c10f849a5d4d,4 +np.float64,0x3fd9137d61b226fc,0x3fed9330eb4815a1,4 +np.float64,0x3fc146d019228da0,0x3fefb57e2d4d52f8,4 +np.float64,0xbfda6036edb4c06e,0x3fed521b2b578679,4 +np.float64,0xbfe78ddfb0ef1bc0,0x3fe7b734319a77e4,4 +np.float64,0x3fe0877823610ef0,0x3febd33a993dd786,4 +np.float64,0x3fbc61af2e38c360,0x3fefcdb4f889756d,4 +np.float64,0x3fd4dcdca4a9b9b8,0x3fee50962ffea5ae,4 +np.float64,0xbfe03cb29f607965,0x3febf7dbf640a75a,4 +np.float64,0xbfc81de407303bc8,0x3fef6f066cef64bc,4 +np.float64,0x3fd8dea42db1bd48,0x3fed9d3e00dbe0b3,4 +np.float64,0x3feac75e94f58ebe,0x3fe56f1f47f97896,4 +np.float64,0x3fb3a1ea6e2743d0,0x3fefe7ec1247cdaa,4 +np.float64,0x3fd695c0f4ad2b80,0x3fee0730bd40883d,4 +np.float64,0xbfd2c631f5a58c64,0x3feea20cbd1105d7,4 +np.float64,0xbfe978a8e1f2f152,0x3fe663014d40ad7a,4 +np.float64,0x3fd8b6b76ab16d70,0x3feda4c879aacc19,4 +np.float64,0x3feaafd30e755fa6,0x3fe5809514c28453,4 +np.float64,0x3fe1e37dc263c6fc,0x3feb20f9ad1f3f5c,4 +np.float64,0x3fd0ec7c24a1d8f8,0x3feee34048f43b75,4 +np.float64,0xbfe3881cbf67103a,0x3fea38d7886e6f53,4 +np.float64,0xbfd7023957ae0472,0x3fedf4471c765a1c,4 +np.float64,0xbfebc51c4ef78a38,0x3fe4b01c424e297b,4 +np.float64,0xbfe20a93eae41528,0x3feb0c2aa321d2e0,4 +np.float64,0x3fef39be867e737e,0x3fe1efaba9164d27,4 +np.float64,0x3fe8ea9576f1d52a,0x3fe6c7a8826ce1be,4 +np.float64,0x3fea921d91f5243c,0x3fe5968c6cf78963,4 +np.float64,0x3fd7ee5d31afdcbc,0x3fedc9f19d43fe61,4 +np.float64,0xbfe3ed581767dab0,0x3fe9fe4ee2f2b1cd,4 +np.float64,0xbfc40923d5281248,0x3fef9bd8ee9f6e68,4 +np.float64,0x3fe411a834682350,0x3fe9e9103854f057,4 +np.float64,0xbfedf6ccdf7bed9a,0x3fe2f77ad6543246,4 +np.float64,0xbfe8788a44f0f114,0x3fe7172f3aa0c742,4 +np.float64,0xbfce728f173ce520,0x3fef1954083bea04,4 +np.float64,0xbfd64dd0acac9ba2,0x3fee138c3293c246,4 +np.float64,0xbfe00669f5600cd4,0x3fec121443945350,4 +np.float64,0xbfe7152ba2ee2a58,0x3fe8079465d09846,4 +np.float64,0x3fe8654d8f70ca9c,0x3fe7247c94f09596,4 +np.float64,0x3fea68045cf4d008,0x3fe5b58cfe81a243,4 +np.float64,0xbfcd4779073a8ef4,0x3fef2a9d78153fa5,4 +np.float64,0xbfdb4456e5b688ae,0x3fed23b11614203f,4 +np.float64,0x3fcb5d59cd36bab0,0x3fef45818216a515,4 +np.float64,0xbfd914ff5ab229fe,0x3fed92e73746fea8,4 +np.float64,0x3fe4d211db69a424,0x3fe97653f433d15f,4 +np.float64,0xbfdbbb9224b77724,0x3fed0adb593dde80,4 +np.float64,0x3fd424ceafa8499c,0x3fee6d9124795d33,4 +np.float64,0x3feb5968f976b2d2,0x3fe501d116efbf54,4 +np.float64,0x3fee7d92a2fcfb26,0x3fe28a479b6a9dcf,4 +np.float64,0x3fc308e9972611d0,0x3fefa595f4df0c89,4 +np.float64,0x3fda79cd77b4f39c,0x3fed4cf8e69ba1f8,4 +np.float64,0x3fcbcf42d5379e88,0x3fef3f6a6a77c187,4 +np.float64,0x3fe13a1da662743c,0x3feb79504faea888,4 +np.float64,0xbfee4435f07c886c,0x3fe2b8ea98d2fc29,4 +np.float64,0x3fd65d68ccacbad0,0x3fee10e1ac7ada89,4 +np.float64,0x3fef2f89bb7e5f14,0x3fe1f81e882cc3f4,4 +np.float64,0xbfef0a7769fe14ef,0x3fe216bf384fc646,4 +np.float64,0x3fc065277320ca50,0x3fefbce44835c193,4 +np.float64,0x3fe9c1a74d73834e,0x3fe62e9ee0c2f2bf,4 +np.float64,0x3fd9d96e5db3b2dc,0x3fed6cd88eb51f6a,4 +np.float64,0x3fe02bf1c56057e4,0x3febfffc24b5a7ba,4 +np.float64,0xbfd6814350ad0286,0x3fee0ab9ad318b84,4 +np.float64,0x3f9fcbec583f97c0,0x3feffc0d0f1d8e75,4 +np.float64,0x3fe23524e5e46a4a,0x3feaf55372949a06,4 +np.float64,0xbfbdc95f6a3b92c0,0x3fefc89c21d44995,4 +np.float64,0x3fe961bb9cf2c378,0x3fe6735d6e1cca58,4 +np.float64,0xbfe8f1c370f1e387,0x3fe6c29d1be8bee9,4 +np.float64,0x3fd880d43ab101a8,0x3fedaee3c7ccfc96,4 +np.float64,0xbfedb37005fb66e0,0x3fe32d91ef2e3bd3,4 +np.float64,0xfdce287bfb9c5,0x3ff0000000000000,4 +np.float64,0x9aa1b9e735437,0x3ff0000000000000,4 +np.float64,0x6beac6e0d7d59,0x3ff0000000000000,4 +np.float64,0x47457aae8e8b0,0x3ff0000000000000,4 +np.float64,0x35ff13b46bfe3,0x3ff0000000000000,4 +np.float64,0xb9c0c82b73819,0x3ff0000000000000,4 +np.float64,0x1a8dc21a351b9,0x3ff0000000000000,4 +np.float64,0x7e87ef6afd0ff,0x3ff0000000000000,4 +np.float64,0x620a6588c414d,0x3ff0000000000000,4 +np.float64,0x7f366000fe6e,0x3ff0000000000000,4 +np.float64,0x787e39f4f0fc8,0x3ff0000000000000,4 +np.float64,0xf5134f1fea26a,0x3ff0000000000000,4 +np.float64,0xbce700ef79ce0,0x3ff0000000000000,4 +np.float64,0x144d7cc8289b1,0x3ff0000000000000,4 +np.float64,0xb9fbc5b973f79,0x3ff0000000000000,4 +np.float64,0xc3d6292d87ac5,0x3ff0000000000000,4 +np.float64,0xc1084e618210a,0x3ff0000000000000,4 +np.float64,0xb6b9eca56d73e,0x3ff0000000000000,4 +np.float64,0xc7ac4b858f58a,0x3ff0000000000000,4 +np.float64,0x516d75d2a2daf,0x3ff0000000000000,4 +np.float64,0x9dc089d93b811,0x3ff0000000000000,4 +np.float64,0x7b5f2840f6be6,0x3ff0000000000000,4 +np.float64,0x121d3ce8243a9,0x3ff0000000000000,4 +np.float64,0xf0be0337e17c1,0x3ff0000000000000,4 +np.float64,0xff58a5cbfeb15,0x3ff0000000000000,4 +np.float64,0xdaf1d07fb5e3a,0x3ff0000000000000,4 +np.float64,0x61d95382c3b2b,0x3ff0000000000000,4 +np.float64,0xe4df943fc9bf3,0x3ff0000000000000,4 +np.float64,0xf72ac2bdee559,0x3ff0000000000000,4 +np.float64,0x12dafbf625b60,0x3ff0000000000000,4 +np.float64,0xee11d427dc23b,0x3ff0000000000000,4 +np.float64,0xf4f8eb37e9f1e,0x3ff0000000000000,4 +np.float64,0xad7cb5df5af97,0x3ff0000000000000,4 +np.float64,0x59fc9b06b3f94,0x3ff0000000000000,4 +np.float64,0x3c3e65e4787ce,0x3ff0000000000000,4 +np.float64,0xe37bc993c6f79,0x3ff0000000000000,4 +np.float64,0x13bd6330277ad,0x3ff0000000000000,4 +np.float64,0x56cc2800ad986,0x3ff0000000000000,4 +np.float64,0x6203b8fcc4078,0x3ff0000000000000,4 +np.float64,0x75c7c8b8eb8fa,0x3ff0000000000000,4 +np.float64,0x5ebf8e00bd7f2,0x3ff0000000000000,4 +np.float64,0xda81f2f1b503f,0x3ff0000000000000,4 +np.float64,0x6adb17d6d5b64,0x3ff0000000000000,4 +np.float64,0x1ba68eee374d3,0x3ff0000000000000,4 +np.float64,0xeecf6fbbdd9ee,0x3ff0000000000000,4 +np.float64,0x24d6dd8e49add,0x3ff0000000000000,4 +np.float64,0xdf7cb81bbef97,0x3ff0000000000000,4 +np.float64,0xafd7be1b5faf8,0x3ff0000000000000,4 +np.float64,0xdb90ca35b721a,0x3ff0000000000000,4 +np.float64,0xa72903a14e521,0x3ff0000000000000,4 +np.float64,0x14533ee028a7,0x3ff0000000000000,4 +np.float64,0x7951540cf2a2b,0x3ff0000000000000,4 +np.float64,0x22882be045106,0x3ff0000000000000,4 +np.float64,0x136270d626c4f,0x3ff0000000000000,4 +np.float64,0x6a0f5744d41ec,0x3ff0000000000000,4 +np.float64,0x21e0d1aa43c1b,0x3ff0000000000000,4 +np.float64,0xee544155dca88,0x3ff0000000000000,4 +np.float64,0xcbe8aac797d16,0x3ff0000000000000,4 +np.float64,0x6c065e80d80e,0x3ff0000000000000,4 +np.float64,0xe57f0411cafe1,0x3ff0000000000000,4 +np.float64,0xdec3a6bdbd875,0x3ff0000000000000,4 +np.float64,0xf4d23a0fe9a48,0x3ff0000000000000,4 +np.float64,0xda77ef47b4efe,0x3ff0000000000000,4 +np.float64,0x8c405c9b1880c,0x3ff0000000000000,4 +np.float64,0x4eced5149d9db,0x3ff0000000000000,4 +np.float64,0x16b6552c2d6cc,0x3ff0000000000000,4 +np.float64,0x6fbc262cdf785,0x3ff0000000000000,4 +np.float64,0x628c3844c5188,0x3ff0000000000000,4 +np.float64,0x6d827d2cdb050,0x3ff0000000000000,4 +np.float64,0xd1bfdf29a37fc,0x3ff0000000000000,4 +np.float64,0xd85400fdb0a80,0x3ff0000000000000,4 +np.float64,0xcc420b2d98842,0x3ff0000000000000,4 +np.float64,0xac41d21b5883b,0x3ff0000000000000,4 +np.float64,0x432f18d4865e4,0x3ff0000000000000,4 +np.float64,0xe7e89a1bcfd14,0x3ff0000000000000,4 +np.float64,0x9b1141d536228,0x3ff0000000000000,4 +np.float64,0x6805f662d00bf,0x3ff0000000000000,4 +np.float64,0xc76552358ecab,0x3ff0000000000000,4 +np.float64,0x4ae8ffee95d21,0x3ff0000000000000,4 +np.float64,0x4396c096872d9,0x3ff0000000000000,4 +np.float64,0x6e8e55d4dd1cb,0x3ff0000000000000,4 +np.float64,0x4c2e33dc985c7,0x3ff0000000000000,4 +np.float64,0xbce814a579d03,0x3ff0000000000000,4 +np.float64,0x911681b5222d0,0x3ff0000000000000,4 +np.float64,0x5f90a4b2bf215,0x3ff0000000000000,4 +np.float64,0x26f76be84deee,0x3ff0000000000000,4 +np.float64,0xb2f7536165eeb,0x3ff0000000000000,4 +np.float64,0x4de4e6089bc9d,0x3ff0000000000000,4 +np.float64,0xf2e016afe5c03,0x3ff0000000000000,4 +np.float64,0xb9b7b949736f7,0x3ff0000000000000,4 +np.float64,0x3363ea1866c7e,0x3ff0000000000000,4 +np.float64,0xd1a3bd6ba3478,0x3ff0000000000000,4 +np.float64,0xae89f3595d13f,0x3ff0000000000000,4 +np.float64,0xddbd9601bb7c,0x3ff0000000000000,4 +np.float64,0x5de41a06bbc84,0x3ff0000000000000,4 +np.float64,0xfd58c86dfab19,0x3ff0000000000000,4 +np.float64,0x24922e8c49247,0x3ff0000000000000,4 +np.float64,0xcda040339b408,0x3ff0000000000000,4 +np.float64,0x5fe500b2bfca1,0x3ff0000000000000,4 +np.float64,0x9214abb924296,0x3ff0000000000000,4 +np.float64,0x800609fe0a2c13fd,0x3ff0000000000000,4 +np.float64,0x800c7c6fe518f8e0,0x3ff0000000000000,4 +np.float64,0x800a1a9491b4352a,0x3ff0000000000000,4 +np.float64,0x800b45e0e8968bc2,0x3ff0000000000000,4 +np.float64,0x8008497e57d092fd,0x3ff0000000000000,4 +np.float64,0x800b9c0af0173816,0x3ff0000000000000,4 +np.float64,0x800194cccb43299a,0x3ff0000000000000,4 +np.float64,0x8001c91ef183923f,0x3ff0000000000000,4 +np.float64,0x800f25b5ccde4b6c,0x3ff0000000000000,4 +np.float64,0x800ce63ccc79cc7a,0x3ff0000000000000,4 +np.float64,0x800d8fb2e83b1f66,0x3ff0000000000000,4 +np.float64,0x80083cd06f7079a1,0x3ff0000000000000,4 +np.float64,0x800823598e9046b3,0x3ff0000000000000,4 +np.float64,0x8001c1319de38264,0x3ff0000000000000,4 +np.float64,0x800f2b68543e56d1,0x3ff0000000000000,4 +np.float64,0x80022a4f4364549f,0x3ff0000000000000,4 +np.float64,0x800f51badf7ea376,0x3ff0000000000000,4 +np.float64,0x8003fbf31e27f7e7,0x3ff0000000000000,4 +np.float64,0x800d4c00e2fa9802,0x3ff0000000000000,4 +np.float64,0x800023b974804774,0x3ff0000000000000,4 +np.float64,0x800860778990c0ef,0x3ff0000000000000,4 +np.float64,0x800a15c241542b85,0x3ff0000000000000,4 +np.float64,0x8003097d9dc612fc,0x3ff0000000000000,4 +np.float64,0x800d77d8541aefb1,0x3ff0000000000000,4 +np.float64,0x80093804ab52700a,0x3ff0000000000000,4 +np.float64,0x800d2b3bfd7a5678,0x3ff0000000000000,4 +np.float64,0x800da24bcd5b4498,0x3ff0000000000000,4 +np.float64,0x8006eee1c28dddc4,0x3ff0000000000000,4 +np.float64,0x80005137fa40a271,0x3ff0000000000000,4 +np.float64,0x8007a3fbc22f47f8,0x3ff0000000000000,4 +np.float64,0x800dcd97071b9b2e,0x3ff0000000000000,4 +np.float64,0x80065b36048cb66d,0x3ff0000000000000,4 +np.float64,0x8004206ba72840d8,0x3ff0000000000000,4 +np.float64,0x8007e82b98cfd058,0x3ff0000000000000,4 +np.float64,0x8001a116ed23422f,0x3ff0000000000000,4 +np.float64,0x800c69e9ff18d3d4,0x3ff0000000000000,4 +np.float64,0x8003843688e7086e,0x3ff0000000000000,4 +np.float64,0x800335e3b8866bc8,0x3ff0000000000000,4 +np.float64,0x800e3308f0bc6612,0x3ff0000000000000,4 +np.float64,0x8002a9ec55c553d9,0x3ff0000000000000,4 +np.float64,0x80001c2084e03842,0x3ff0000000000000,4 +np.float64,0x800bc2bbd8d78578,0x3ff0000000000000,4 +np.float64,0x800ae6bcc555cd7a,0x3ff0000000000000,4 +np.float64,0x80083f7a13907ef5,0x3ff0000000000000,4 +np.float64,0x800d83ed76db07db,0x3ff0000000000000,4 +np.float64,0x800a12251974244b,0x3ff0000000000000,4 +np.float64,0x800a69c95714d393,0x3ff0000000000000,4 +np.float64,0x800cd5a85639ab51,0x3ff0000000000000,4 +np.float64,0x800e0e1837bc1c31,0x3ff0000000000000,4 +np.float64,0x8007b5ca39ef6b95,0x3ff0000000000000,4 +np.float64,0x800cf961cad9f2c4,0x3ff0000000000000,4 +np.float64,0x80066e8fc14cdd20,0x3ff0000000000000,4 +np.float64,0x8001cb8c7b43971a,0x3ff0000000000000,4 +np.float64,0x800002df68a005c0,0x3ff0000000000000,4 +np.float64,0x8003e6681567ccd1,0x3ff0000000000000,4 +np.float64,0x800b039126b60723,0x3ff0000000000000,4 +np.float64,0x800d2e1b663a5c37,0x3ff0000000000000,4 +np.float64,0x800188b3e2a31169,0x3ff0000000000000,4 +np.float64,0x8001f272e943e4e7,0x3ff0000000000000,4 +np.float64,0x800d7f53607afea7,0x3ff0000000000000,4 +np.float64,0x80092cafa4f25960,0x3ff0000000000000,4 +np.float64,0x800fc009f07f8014,0x3ff0000000000000,4 +np.float64,0x8003da896507b514,0x3ff0000000000000,4 +np.float64,0x800d4d1b4c3a9a37,0x3ff0000000000000,4 +np.float64,0x8007a835894f506c,0x3ff0000000000000,4 +np.float64,0x80057ba0522af741,0x3ff0000000000000,4 +np.float64,0x8009b7054b336e0b,0x3ff0000000000000,4 +np.float64,0x800b2c6c125658d9,0x3ff0000000000000,4 +np.float64,0x8008b1840ad16308,0x3ff0000000000000,4 +np.float64,0x8007ea0e3befd41d,0x3ff0000000000000,4 +np.float64,0x800dd658683bacb1,0x3ff0000000000000,4 +np.float64,0x8008cda48fd19b49,0x3ff0000000000000,4 +np.float64,0x8003acca14c75995,0x3ff0000000000000,4 +np.float64,0x8008bd152d717a2b,0x3ff0000000000000,4 +np.float64,0x80010d1ea3621a3e,0x3ff0000000000000,4 +np.float64,0x800130b78b826170,0x3ff0000000000000,4 +np.float64,0x8002cf3a46e59e75,0x3ff0000000000000,4 +np.float64,0x800b76e7fa76edd0,0x3ff0000000000000,4 +np.float64,0x800e065fe1dc0cc0,0x3ff0000000000000,4 +np.float64,0x8000dd527ea1baa6,0x3ff0000000000000,4 +np.float64,0x80032cb234665965,0x3ff0000000000000,4 +np.float64,0x800affc1acb5ff84,0x3ff0000000000000,4 +np.float64,0x80074be23fee97c5,0x3ff0000000000000,4 +np.float64,0x8004f83eafc9f07e,0x3ff0000000000000,4 +np.float64,0x800b02a115560543,0x3ff0000000000000,4 +np.float64,0x800b324a55766495,0x3ff0000000000000,4 +np.float64,0x800ffbcfd69ff7a0,0x3ff0000000000000,4 +np.float64,0x800830bc7b906179,0x3ff0000000000000,4 +np.float64,0x800cbafe383975fd,0x3ff0000000000000,4 +np.float64,0x8001ee42bfe3dc86,0x3ff0000000000000,4 +np.float64,0x8005b00fdc0b6020,0x3ff0000000000000,4 +np.float64,0x8005e7addd0bcf5c,0x3ff0000000000000,4 +np.float64,0x8001ae4cb0635c9a,0x3ff0000000000000,4 +np.float64,0x80098a9941131533,0x3ff0000000000000,4 +np.float64,0x800334c929466993,0x3ff0000000000000,4 +np.float64,0x8009568239d2ad05,0x3ff0000000000000,4 +np.float64,0x800f0639935e0c73,0x3ff0000000000000,4 +np.float64,0x800cebce7499d79d,0x3ff0000000000000,4 +np.float64,0x800482ee4c2905dd,0x3ff0000000000000,4 +np.float64,0x8007b7bd9e2f6f7c,0x3ff0000000000000,4 +np.float64,0x3fe654469f2ca88d,0x3fe8853f6c01ffb3,4 +np.float64,0x3feb4d7297369ae5,0x3fe50ad5bb621408,4 +np.float64,0x3feef53ba43dea77,0x3fe2283f356f8658,4 +np.float64,0x3fddf564eabbeaca,0x3fec8ec0e0dead9c,4 +np.float64,0x3fd3a69078274d21,0x3fee80e05c320000,4 +np.float64,0x3fecdafe5d39b5fd,0x3fe3d91a5d440fd9,4 +np.float64,0x3fd93286bc32650d,0x3fed8d40696cd10e,4 +np.float64,0x3fc0d34eb821a69d,0x3fefb954023d4284,4 +np.float64,0x3fc7b4b9a02f6973,0x3fef73e8739787ce,4 +np.float64,0x3fe08c839a611907,0x3febd0bc6f5641cd,4 +np.float64,0x3fb3d1758627a2eb,0x3fefe776f6183f96,4 +np.float64,0x3fef93c9ff3f2794,0x3fe1a4d2f622627d,4 +np.float64,0x3fea8d0041351a01,0x3fe59a52a1c78c9e,4 +np.float64,0x3fe3e26a30e7c4d4,0x3fea04ad3e0bbf8d,4 +np.float64,0x3fe5a34c9f6b4699,0x3fe8f57c5ccd1eab,4 +np.float64,0x3fc21ef859243df1,0x3fefae0b68a3a2e7,4 +np.float64,0x3fed7dd585fafbab,0x3fe35860041e5b0d,4 +np.float64,0x3fe5abacf22b575a,0x3fe8f03d8b6ef0f2,4 +np.float64,0x3fe426451f284c8a,0x3fe9dcf21f13205b,4 +np.float64,0x3fc01f6456203ec9,0x3fefbf19e2a8e522,4 +np.float64,0x3fe1cf2772239e4f,0x3feb2bbd645c7697,4 +np.float64,0x3fd18c4ace231896,0x3feecdfdd086c110,4 +np.float64,0x3fe8387d5b7070fb,0x3fe74358f2ec4910,4 +np.float64,0x3fdce51c2239ca38,0x3feccb2ae5459632,4 +np.float64,0x3fe5b0f2e4eb61e6,0x3fe8ecef4dbe4277,4 +np.float64,0x3fe1ceeb08a39dd6,0x3feb2bdd4dcfb3df,4 +np.float64,0x3febc5899d778b13,0x3fe4afc8dd8ad228,4 +np.float64,0x3fe7a47fbe2f48ff,0x3fe7a7fd9b352ea5,4 +np.float64,0x3fe7f74e1fafee9c,0x3fe76feb2755b247,4 +np.float64,0x3fe2bfad04e57f5a,0x3feaa9b46adddaeb,4 +np.float64,0x3fd06a090320d412,0x3feef40c334f8fba,4 +np.float64,0x3fdc97297d392e53,0x3fecdc16a3e22fcb,4 +np.float64,0x3fdc1a3f3838347e,0x3fecf6db2769d404,4 +np.float64,0x3fcca90096395201,0x3fef338156fcd218,4 +np.float64,0x3fed464733fa8c8e,0x3fe38483f0465d91,4 +np.float64,0x3fe7e067d82fc0d0,0x3fe77f7c8c9de896,4 +np.float64,0x3fc014fa0b2029f4,0x3fefbf6d84c933f8,4 +np.float64,0x3fd3bf1524277e2a,0x3fee7d2997b74dec,4 +np.float64,0x3fec153b86782a77,0x3fe472bb5497bb2a,4 +np.float64,0x3fd3e4d9d5a7c9b4,0x3fee776842691902,4 +np.float64,0x3fea6c0e2c74d81c,0x3fe5b2954cb458d9,4 +np.float64,0x3fee8f6a373d1ed4,0x3fe27bb9e348125b,4 +np.float64,0x3fd30c6dd42618dc,0x3fee97d2cab2b0bc,4 +np.float64,0x3fe4f90e6d69f21d,0x3fe95ea3dd4007f2,4 +np.float64,0x3fe271d467e4e3a9,0x3fead470d6d4008b,4 +np.float64,0x3fef2983897e5307,0x3fe1fd1a4debe33b,4 +np.float64,0x3fe980cc83b30199,0x3fe65d2fb8a0eb46,4 +np.float64,0x3fdfdf53db3fbea8,0x3fec1cf95b2a1cc7,4 +np.float64,0x3fe4d5307ba9aa61,0x3fe974701b4156cb,4 +np.float64,0x3fdb4e2345b69c47,0x3fed21aa6c146512,4 +np.float64,0x3fe3f7830327ef06,0x3fe9f85f6c88c2a8,4 +np.float64,0x3fca915fb63522bf,0x3fef502b73a52ecf,4 +np.float64,0x3fe66d3709ecda6e,0x3fe87531d7372d7a,4 +np.float64,0x3fd86000bcb0c001,0x3fedb5018dd684ca,4 +np.float64,0x3fe516e5feea2dcc,0x3fe94c68b111404e,4 +np.float64,0x3fd83c53dd3078a8,0x3fedbb9e5dd9e165,4 +np.float64,0x3fedfeeb673bfdd7,0x3fe2f0f0253c5d5d,4 +np.float64,0x3fe0dc6f9c21b8df,0x3feba8e2452410c2,4 +np.float64,0x3fbe154d643c2a9b,0x3fefc780a9357457,4 +np.float64,0x3fe5f63986abec73,0x3fe8c1434951a40a,4 +np.float64,0x3fbce0e50839c1ca,0x3fefcbeeaa27de75,4 +np.float64,0x3fd7ef5c5c2fdeb9,0x3fedc9c3022495b3,4 +np.float64,0x3fc1073914220e72,0x3fefb79de80fc0fd,4 +np.float64,0x3fe1a93c3d235278,0x3feb3fb21f86ac67,4 +np.float64,0x3fe321ee53e643dd,0x3fea72e2999f1e22,4 +np.float64,0x3fa881578c3102af,0x3feff69e6e51e0d6,4 +np.float64,0x3fd313482a262690,0x3fee96d161199495,4 +np.float64,0x3fe7272cd6ae4e5a,0x3fe7fbacbd0d8f43,4 +np.float64,0x3fd6cf4015ad9e80,0x3fedfd3513d544b8,4 +np.float64,0x3fc67b7e6d2cf6fd,0x3fef81f5c16923a4,4 +np.float64,0x3fa1999c14233338,0x3feffb2913a14184,4 +np.float64,0x3fc74eb8dd2e9d72,0x3fef78909a138e3c,4 +np.float64,0x3fc0b9274921724f,0x3fefba2ebd5f3e1c,4 +np.float64,0x3fd53fa156aa7f43,0x3fee40a18e952e88,4 +np.float64,0x3feaccbca4b59979,0x3fe56b22b33eb713,4 +np.float64,0x3fe6a01e3a2d403c,0x3fe8543fbd820ecc,4 +np.float64,0x3fd392a869a72551,0x3fee83e0ffe0e8de,4 +np.float64,0x3fe44d8928689b12,0x3fe9c5bf3c8fffdb,4 +np.float64,0x3fca3f209f347e41,0x3fef5461b6fa0924,4 +np.float64,0x3fee9e84b07d3d09,0x3fe26f638f733549,4 +np.float64,0x3faf49acb03e9359,0x3feff0b583cd8c48,4 +np.float64,0x3fea874b2af50e96,0x3fe59e882fa6febf,4 +np.float64,0x3fc50b72772a16e5,0x3fef918777dc41be,4 +np.float64,0x3fe861d1d4f0c3a4,0x3fe726e44d9d42c2,4 +np.float64,0x3fcadd2e2535ba5c,0x3fef4c3e2b56da38,4 +np.float64,0x3fea59c29cb4b385,0x3fe5c0043e586439,4 +np.float64,0x3fc1ffef0d23ffde,0x3fefaf22be452d13,4 +np.float64,0x3fc2d8dbc125b1b8,0x3fefa75b646d8e4e,4 +np.float64,0x3fd66c6471acd8c9,0x3fee0e5038b895c0,4 +np.float64,0x3fd0854adfa10a96,0x3feef0945bcc5c99,4 +np.float64,0x3feaac7076f558e1,0x3fe58316c23a82ad,4 +np.float64,0x3fdda49db3bb493b,0x3feca0e347c0ad6f,4 +np.float64,0x3fe43a539de874a7,0x3fe9d11d722d4822,4 +np.float64,0x3feeee3ebbfddc7d,0x3fe22dffd251e9af,4 +np.float64,0x3f8ee2c5b03dc58b,0x3fefff11855a7b6c,4 +np.float64,0x3fcd7107c63ae210,0x3fef2840bb55ca52,4 +np.float64,0x3f8d950d203b2a1a,0x3fefff253a08e40e,4 +np.float64,0x3fd40a5e57a814bd,0x3fee71a633c761fc,4 +np.float64,0x3fee836ec83d06de,0x3fe28580975be2fd,4 +np.float64,0x3fd7bbe87f2f77d1,0x3fedd31f661890cc,4 +np.float64,0xbfe05bf138a0b7e2,0x3febe8a000d96e47,4 +np.float64,0xbf88bddd90317bc0,0x3fefff66f6e2ff26,4 +np.float64,0xbfdc9cbb12393976,0x3fecdae2982335db,4 +np.float64,0xbfd85b4eccb0b69e,0x3fedb5e0dd87f702,4 +np.float64,0xbfe5c326cb2b864e,0x3fe8e180f525fa12,4 +np.float64,0xbfe381a0e4a70342,0x3fea3c8e5e3ab78e,4 +np.float64,0xbfe58d892c2b1b12,0x3fe9031551617aed,4 +np.float64,0xbfd7f3a52cafe74a,0x3fedc8fa97edd080,4 +np.float64,0xbfef3417bc7e682f,0x3fe1f45989f6a009,4 +np.float64,0xbfddfb8208bbf704,0x3fec8d5fa9970773,4 +np.float64,0xbfdab69bcc356d38,0x3fed40b2f6c347c6,4 +np.float64,0xbfed3f7cf17a7efa,0x3fe389e4ff4d9235,4 +np.float64,0xbfe47675d9a8ecec,0x3fe9ad6829a69e94,4 +np.float64,0xbfd030e2902061c6,0x3feefb3f811e024f,4 +np.float64,0xbfc376ac7226ed58,0x3fefa1798712b37e,4 +np.float64,0xbfdb7e54a0b6fcaa,0x3fed17a974c4bc28,4 +np.float64,0xbfdb7d5d5736faba,0x3fed17dcf31a8d84,4 +np.float64,0xbf876bd6502ed7c0,0x3fefff76dce6232c,4 +np.float64,0xbfd211e6c02423ce,0x3feebba41f0a1764,4 +np.float64,0xbfb443e3962887c8,0x3fefe658953629d4,4 +np.float64,0xbfe81b09e9b03614,0x3fe757882e4fdbae,4 +np.float64,0xbfdcb905d2b9720c,0x3fecd4c22cfe84e5,4 +np.float64,0xbfe3b62d99276c5b,0x3fea1e5520b3098d,4 +np.float64,0xbfbf05b25c3e0b68,0x3fefc3ecc04bca8e,4 +np.float64,0xbfdedc885b3db910,0x3fec59e22feb49f3,4 +np.float64,0xbfe33aa282667545,0x3fea64f2d55ec471,4 +np.float64,0xbfec84745a3908e9,0x3fe41cb3214e7044,4 +np.float64,0xbfddefdff1bbdfc0,0x3fec8fff88d4d0ec,4 +np.float64,0xbfd26ae6aca4d5ce,0x3feeaf208c7fedf6,4 +np.float64,0xbfee010591fc020b,0x3fe2ef3e57211a5e,4 +np.float64,0xbfb8cfddca319fb8,0x3fefd98d8f7918ed,4 +np.float64,0xbfe991648f3322c9,0x3fe6514e54670bae,4 +np.float64,0xbfee63fd087cc7fa,0x3fe29f1bfa3297cc,4 +np.float64,0xbfe1685942a2d0b2,0x3feb617f5f839eee,4 +np.float64,0xbfc6fc2fd62df860,0x3fef7c4698fd58cf,4 +np.float64,0xbfe42723d3a84e48,0x3fe9dc6ef7243e90,4 +np.float64,0xbfc3a7e89d274fd0,0x3fef9f99e3314e77,4 +np.float64,0xbfeb4c9521f6992a,0x3fe50b7c919bc6d8,4 +np.float64,0xbf707b34e020f680,0x3fefffef05e30264,4 +np.float64,0xbfc078478e20f090,0x3fefbc479305d5aa,4 +np.float64,0xbfd494ac4ca92958,0x3fee5c11f1cd8269,4 +np.float64,0xbfdaf888a035f112,0x3fed3346ae600469,4 +np.float64,0xbfa5d8ed502bb1e0,0x3feff88b0f262609,4 +np.float64,0xbfeec0cbfffd8198,0x3fe253543b2371cb,4 +np.float64,0xbfe594b5986b296b,0x3fe8fe9b39fb3940,4 +np.float64,0xbfc8ece7c631d9d0,0x3fef652bd0611ac7,4 +np.float64,0xbfd8ffeca0b1ffda,0x3fed96ebdf9b65cb,4 +np.float64,0xbfba9b221e353648,0x3fefd3cc21e2f15c,4 +np.float64,0xbfca63a52c34c74c,0x3fef52848eb9ed3b,4 +np.float64,0xbfe588e9b06b11d4,0x3fe905f7403e8881,4 +np.float64,0xbfc76f82db2edf04,0x3fef77138fe9bbc2,4 +np.float64,0xbfeeb3f334bd67e6,0x3fe25ddadb1096d6,4 +np.float64,0xbfbf2b64ce3e56c8,0x3fefc35a9555f6df,4 +np.float64,0xbfe9920e4ff3241c,0x3fe650d4ab8f5c42,4 +np.float64,0xbfb4a54c02294a98,0x3fefe55fc85ae5e9,4 +np.float64,0xbfe353b0c766a762,0x3fea56c02d17e4b7,4 +np.float64,0xbfd99961a4b332c4,0x3fed795fcd00dbf9,4 +np.float64,0xbfef191ddabe323c,0x3fe20aa79524f636,4 +np.float64,0xbfb25d060224ba10,0x3fefeaeee5cc8c0b,4 +np.float64,0xbfe6022428ec0448,0x3fe8b9b46e776194,4 +np.float64,0xbfed1a236cba3447,0x3fe3a76bee0d9861,4 +np.float64,0xbfc59671e72b2ce4,0x3fef8bc4daef6f14,4 +np.float64,0xbfdf2711703e4e22,0x3fec4886a8c9ceb5,4 +np.float64,0xbfeb7e207536fc41,0x3fe4e610c783f168,4 +np.float64,0xbfe6cdf5bcad9bec,0x3fe8365f8a59bc81,4 +np.float64,0xbfe55294adaaa52a,0x3fe927b0af5ccd09,4 +np.float64,0xbfdf4a88913e9512,0x3fec4036df58ba74,4 +np.float64,0xbfebb7efe4376fe0,0x3fe4ba276006992d,4 +np.float64,0xbfe09f29cfa13e54,0x3febc77f4f9c95e7,4 +np.float64,0xbfdf8c75653f18ea,0x3fec30ac924e4f46,4 +np.float64,0xbfefd601c7ffac04,0x3fe16d6f21bcb9c1,4 +np.float64,0xbfeae97ff5f5d300,0x3fe555bb5b87efe9,4 +np.float64,0xbfed427f02fa84fe,0x3fe387830db093bc,4 +np.float64,0xbfa33909cc267210,0x3feffa3a1bcb50dd,4 +np.float64,0xbfe9aa4bf5f35498,0x3fe63f6e98f6aa0f,4 +np.float64,0xbfe2d7349b25ae69,0x3fea9caa7c331e7e,4 +np.float64,0xbfcdbb2a3a3b7654,0x3fef2401c9659e4b,4 +np.float64,0xbfc8a90919315214,0x3fef686fe7fc0513,4 +np.float64,0xbfe62a98df2c5532,0x3fe89ff22a02cc6b,4 +np.float64,0xbfdc0f67b3b81ed0,0x3fecf928b637798f,4 +np.float64,0xbfebb32bf6f76658,0x3fe4bdc893c09698,4 +np.float64,0xbfec067996380cf3,0x3fe47e132741db97,4 +np.float64,0xbfd9774e1d32ee9c,0x3fed7ffe1e87c434,4 +np.float64,0xbfef989890bf3131,0x3fe1a0d025c80cf4,4 +np.float64,0xbfe59887e62b3110,0x3fe8fc382a3d4197,4 +np.float64,0xbfdea0a11e3d4142,0x3fec67b987e236ec,4 +np.float64,0xbfe2ec495825d892,0x3fea90efb231602d,4 +np.float64,0xbfb329c5c2265388,0x3fefe90f1b8209c3,4 +np.float64,0xbfdcd2dcd339a5ba,0x3feccf24c60b1478,4 +np.float64,0xbfe537ea18aa6fd4,0x3fe938237e217fe0,4 +np.float64,0xbfe8675ce170ceba,0x3fe723105925ce3a,4 +np.float64,0xbfd70723acae0e48,0x3fedf369ac070e65,4 +np.float64,0xbfea9d8692b53b0d,0x3fe58e1ee42e3fdb,4 +np.float64,0xbfcfeb96653fd72c,0x3fef029770033bdc,4 +np.float64,0xbfcc06c92d380d94,0x3fef3c69797d9b0a,4 +np.float64,0xbfe16b7c4f62d6f8,0x3feb5fdf9f0a9a07,4 +np.float64,0xbfed4d7a473a9af4,0x3fe37ecee27b1eb7,4 +np.float64,0xbfe6a6f6942d4ded,0x3fe84fccdf762b19,4 +np.float64,0xbfda46d867348db0,0x3fed572d928fa657,4 +np.float64,0xbfdbd9482db7b290,0x3fed049b5f907b52,4 +np.float64,0x7fe992ceb933259c,0xbfeb15af92aad70e,4 +np.float64,0x7fe3069204a60d23,0xbfe5eeff454240e9,4 +np.float64,0x7fe729dbf32e53b7,0xbfefe0528a330e4c,4 +np.float64,0x7fec504fb638a09e,0x3fd288e95dbedf65,4 +np.float64,0x7fe1d30167a3a602,0xbfeffc41f946fd02,4 +np.float64,0x7fed7f8ffd3aff1f,0x3fefe68ec604a19d,4 +np.float64,0x7fd2f23635a5e46b,0x3fea63032efbb447,4 +np.float64,0x7fd4c86db1a990da,0x3fdf6b9f7888db5d,4 +np.float64,0x7fe7554db6eeaa9a,0x3fe1b41476861bb0,4 +np.float64,0x7fe34e823ba69d03,0x3fefc435532e6294,4 +np.float64,0x7fec5c82fef8b905,0x3fef8f0c6473034f,4 +np.float64,0x7feba221bff74442,0xbfea95b81eb19b47,4 +np.float64,0x7fe74808a5ae9010,0xbfd3aa322917c3e5,4 +np.float64,0x7fdf41b7e0be836f,0x3fd14283c7147282,4 +np.float64,0x7fec09892f381311,0x3fe5240376ae484b,4 +np.float64,0x7faaf80bf435f017,0x3fe20227fa811423,4 +np.float64,0x7f8422d8402845b0,0x3fe911714593b8a0,4 +np.float64,0x7fd23a7fada474fe,0x3feff9f40aa37e9c,4 +np.float64,0x7fef4a4806fe948f,0x3fec6eca89cb4a62,4 +np.float64,0x7fe1e71cf763ce39,0xbfea6ac63f9ba457,4 +np.float64,0x7fe3e555be27caaa,0xbfe75b305d0dbbfd,4 +np.float64,0x7fcb8bac96371758,0xbfe8b126077f9d4c,4 +np.float64,0x7fc98e2c84331c58,0x3fef9092eb0bc85a,4 +np.float64,0x7fe947cf2b728f9d,0xbfebfff2c5b7d198,4 +np.float64,0x7feee8058c3dd00a,0xbfef21ebaae2eb17,4 +np.float64,0x7fef61d8d5bec3b1,0xbfdf1a032fb1c864,4 +np.float64,0x7fcf714b6f3ee296,0x3fe6fc89a8084098,4 +np.float64,0x7fa9a8b44c335168,0xbfeb16c149cea943,4 +np.float64,0x7fd175c482a2eb88,0xbfef64d341e73f88,4 +np.float64,0x7feab8e6a87571cc,0x3feb10069c397464,4 +np.float64,0x7fe3ade72de75bcd,0x3fd1753e333d5790,4 +np.float64,0x7fb26d87d224db0f,0xbfe753d36b18f4ca,4 +np.float64,0x7fdb7ef159b6fde2,0x3fe5c0a6044d3607,4 +np.float64,0x7fd5af86422b5f0c,0x3fe77193c95f6484,4 +np.float64,0x7fee9e00b07d3c00,0x3fe864d494596845,4 +np.float64,0x7fef927a147f24f3,0xbfe673b14715693d,4 +np.float64,0x7fd0aea63c215d4b,0xbfeff435f119fce9,4 +np.float64,0x7fd02e3796a05c6e,0x3fe4f7e3706e9a3d,4 +np.float64,0x7fd3ed61da27dac3,0xbfefef2f057f168c,4 +np.float64,0x7fefaca0d4ff5941,0x3fd3e8ad205cd4ab,4 +np.float64,0x7feb659e06f6cb3b,0x3fd64d803203e027,4 +np.float64,0x7fc94ccfaf32999e,0x3fee04922209369a,4 +np.float64,0x7feb4ec294f69d84,0xbfd102763a056c89,4 +np.float64,0x7fe2ada6ac655b4c,0x3fef4f6792aa6093,4 +np.float64,0x7fe5f40fdc2be81f,0xbfb4a6327186eee8,4 +np.float64,0x7fe7584bc3eeb097,0xbfd685b8ff94651d,4 +np.float64,0x7fe45d276be8ba4e,0x3fee53b13f7e442f,4 +np.float64,0x7fe6449b3d6c8935,0xbfe7e08bafa75251,4 +np.float64,0x7f8d62e6b03ac5cc,0x3fe73d30762f38fd,4 +np.float64,0x7fe3a76f72a74ede,0xbfeb48a28bc60968,4 +np.float64,0x7fd057706920aee0,0x3fdece8fa06f626c,4 +np.float64,0x7fe45ae158e8b5c2,0x3fe7a70f47b4d349,4 +np.float64,0x7fea8a5a983514b4,0x3fefb053d5f9ddd7,4 +np.float64,0x7fdd1e86ab3a3d0c,0x3fe3cded1b93816b,4 +np.float64,0x7fdb456108b68ac1,0xbfe37574c0b9bf8f,4 +np.float64,0x7fe972602432e4bf,0x3fef9a26e65ec01c,4 +np.float64,0x7fdbe2385637c470,0x3fed541df57969e1,4 +np.float64,0x7fe57f03602afe06,0x3fbd90f595cbbd94,4 +np.float64,0x7feb0ceb68f619d6,0xbfeae9cb8ee5261f,4 +np.float64,0x7fe6abfe6c6d57fc,0xbfef40a6edaca26f,4 +np.float64,0x7fe037ea08606fd3,0xbfda817d75858597,4 +np.float64,0x7fdd75a52dbaeb49,0x3feef2a0d91d6aa1,4 +np.float64,0x7fe8f9af66b1f35e,0xbfedfceef2a3bfc9,4 +np.float64,0x7fedf762b53beec4,0x3fd8b4f21ef69ee3,4 +np.float64,0x7fe99295b7f3252a,0x3feffc24d970383e,4 +np.float64,0x7fe797b0172f2f5f,0x3fee089aa56f7ce8,4 +np.float64,0x7fed89dcc97b13b9,0xbfcfa2bb0c3ea41f,4 +np.float64,0x7fae9e8d5c3d3d1a,0xbfe512ffe16c6b08,4 +np.float64,0x7fefaecbe27f5d97,0x3fbfc718a5e972f1,4 +np.float64,0x7fce0236d93c046d,0xbfa9b7cd790db256,4 +np.float64,0x7fa9689aac32d134,0x3feced501946628a,4 +np.float64,0x7feb1469e93628d3,0x3fef2a988e7673ed,4 +np.float64,0x7fdba78344b74f06,0xbfe092e78965b30c,4 +np.float64,0x7fece54c3fb9ca97,0x3fd3cfd184bed2e6,4 +np.float64,0x7fdb84212b370841,0xbfe25ebf2db6ee55,4 +np.float64,0x7fbe3e8bf23c7d17,0x3fe2ee72df573345,4 +np.float64,0x7fe43d9803687b2f,0xbfed2eff6a9e66a0,4 +np.float64,0x7fb0f9c00a21f37f,0x3feff70f3276fdb7,4 +np.float64,0x7fea0c6cbbb418d8,0xbfefa612494798b2,4 +np.float64,0x7fe4b3239e296646,0xbfe74dd959af8cdc,4 +np.float64,0x7fe5c6a773eb8d4e,0xbfd06944048f8d2b,4 +np.float64,0x7fb1c1278223824e,0xbfeb533a34655bde,4 +np.float64,0x7fd21c09ee243813,0xbfe921ccbc9255c3,4 +np.float64,0x7fe051020c20a203,0x3fbd519d700c1f2f,4 +np.float64,0x7fe0c76845e18ed0,0x3fefb9595191a31b,4 +np.float64,0x7fe6b0b57b6d616a,0xbf8c59a8ba5fcd9a,4 +np.float64,0x7fd386c460270d88,0x3fe8ffea5d1a5c46,4 +np.float64,0x7feeb884713d7108,0x3fee9b2247ef6c0d,4 +np.float64,0x7fd85f71b6b0bee2,0xbfefc30ec3e28f07,4 +np.float64,0x7fc341366426826c,0x3fd4234d35386d3b,4 +np.float64,0x7fe56482dd6ac905,0x3fe7189de6a50668,4 +np.float64,0x7fec67a2e3f8cf45,0xbfef86d0b940f37f,4 +np.float64,0x7fe38b202fe7163f,0x3feb90b75caa2030,4 +np.float64,0x7fdcbc64883978c8,0x3fed4f758fbf64d4,4 +np.float64,0x7fea5f0598f4be0a,0x3fdd503a417b3d4d,4 +np.float64,0x7fda3b6bcf3476d7,0x3fea6e9af3f7f9f5,4 +np.float64,0x7fc7d7896c2faf12,0x3fda2bebc36a2363,4 +np.float64,0x7fe7e8e2626fd1c4,0xbfe7d5e390c4cc3f,4 +np.float64,0x7fde0f3d7abc1e7a,0xbfede7a0ecfa3606,4 +np.float64,0x7fc692b8f52d2571,0x3feff0cd7ab6f61b,4 +np.float64,0xff92d1fce825a400,0xbfc921c36fc014fa,4 +np.float64,0xffdec3af2fbd875e,0xbfed6a77e6a0364e,4 +np.float64,0xffef46e7d9be8dcf,0xbfed7d39476f7e27,4 +np.float64,0xffe2c2ce4525859c,0x3fe1757261316bc9,4 +np.float64,0xffe27c8b5864f916,0xbfefe017c0d43457,4 +np.float64,0xffe184d7442309ae,0x3fa1fb8c49dba596,4 +np.float64,0xffddf5f98d3bebf4,0x3fee4f8eaa5f847e,4 +np.float64,0xffee3ef354fc7de6,0xbfebfd60fa51b2ba,4 +np.float64,0xffdecb3e85bd967e,0x3fbfad2667a8b468,4 +np.float64,0xffe4ee900b29dd20,0xbfdc02dc626f91cd,4 +np.float64,0xffd3179f6da62f3e,0xbfe2cfe442511776,4 +np.float64,0xffe99ef7cef33def,0x3f50994542a7f303,4 +np.float64,0xffe2b66b1ae56cd6,0xbfefe3e066eb6329,4 +np.float64,0xff8f72aff03ee540,0x3fe9c46224cf5003,4 +np.float64,0xffd29beb85a537d8,0x3fefcb0b6166be71,4 +np.float64,0xffaef02d4c3de060,0xbfef5fb71028fc72,4 +np.float64,0xffd39a2a89273456,0x3fe6d4b183205dca,4 +np.float64,0xffef8a9392ff1526,0x3fedb99fbf402468,4 +np.float64,0xffb9b3f31e3367e8,0x3fee1005270fcf80,4 +np.float64,0xffed9d5c693b3ab8,0x3fd110f4b02365d5,4 +np.float64,0xffeaba45f9f5748b,0x3fe499e0a6f4afb2,4 +np.float64,0xffdba3f70d3747ee,0xbfca0c30493ae519,4 +np.float64,0xffa35b985426b730,0xbfdb625df56bcf45,4 +np.float64,0xffccbc9728397930,0x3fc53cbc59020704,4 +np.float64,0xffef73c942bee792,0xbfdc647a7a5e08be,4 +np.float64,0xffcb5acfb236b5a0,0x3feeb4ec038c39fc,4 +np.float64,0xffea116fe2b422df,0x3fefe03b6ae0b435,4 +np.float64,0xffe97de6e7b2fbcd,0xbfd2025698fab9eb,4 +np.float64,0xffdddba314bbb746,0x3fd31f0fdb8f93be,4 +np.float64,0xffd613a24a2c2744,0xbfebbb1efae884b3,4 +np.float64,0xffe3d938aa67b271,0xbfc2099cead3d3be,4 +np.float64,0xffdf08c2e33e1186,0xbfefd236839b900d,4 +np.float64,0xffea6ba8bd34d751,0x3fe8dfc032114719,4 +np.float64,0xffe3202083e64040,0x3fed513b81432a22,4 +np.float64,0xffb2397db62472f8,0xbfee7d7fe1c3f76c,4 +np.float64,0xffd9d0682ab3a0d0,0x3fe0bcf9e531ad79,4 +np.float64,0xffc293df202527c0,0xbfe58d0bdece5e64,4 +np.float64,0xffe1422c7da28458,0xbf81bd72595f2341,4 +np.float64,0xffd64e4ed4ac9c9e,0x3fa4334cc011c703,4 +np.float64,0xffe40a970ae8152e,0x3fead3d258b55b7d,4 +np.float64,0xffc8c2f2223185e4,0xbfef685f07c8b9fd,4 +np.float64,0xffe4b2f7216965ee,0x3fe3861d3d896a83,4 +np.float64,0xffdb531db3b6a63c,0x3fe18cb8332dd59d,4 +np.float64,0xffe8e727a3b1ce4e,0xbfe57b15abb677b9,4 +np.float64,0xffe530c1e12a6184,0xbfb973ea5535e48f,4 +np.float64,0xffe6f7849cedef08,0x3fd39a37ec5af4b6,4 +np.float64,0xffead62a78b5ac54,0x3fe69b3f6c7aa24b,4 +np.float64,0xffeefdd725fdfbad,0xbfc08a456111fdd5,4 +np.float64,0xffe682182fed0430,0x3fecc7c1292761d2,4 +np.float64,0xffee0ca8dcbc1951,0x3fef6cc361ef2c19,4 +np.float64,0xffec9b338f393666,0x3fefa9ab8e0471b5,4 +np.float64,0xffe13c5e29a278bc,0xbfef8da74ad83398,4 +np.float64,0xffd7bd48c62f7a92,0x3fe3468cd4ac9d34,4 +np.float64,0xffedd0ed14bba1d9,0xbfd563a83477077b,4 +np.float64,0xffe86b83f3f0d707,0x3fe9eb3c658e4b2d,4 +np.float64,0xffd6a4db4bad49b6,0xbfc7e11276166e17,4 +np.float64,0xffc29e8404253d08,0x3fd35971961c789f,4 +np.float64,0xffe27cf3d664f9e7,0xbfeca0f73c72f810,4 +np.float64,0xffc34152352682a4,0x3fef384e564c002c,4 +np.float64,0xffe395728ba72ae4,0x3f8fe18c2de86eba,4 +np.float64,0xffed86c4fbbb0d89,0x3fef709db881c672,4 +np.float64,0xffe8a98d37f1531a,0x3fd4879c8f73c3dc,4 +np.float64,0xffb8ce9fea319d40,0xbfb853c8fe46b08d,4 +np.float64,0xffe7f26db8efe4db,0xbfec1cfd3e5c2ac1,4 +np.float64,0xffd7935b77af26b6,0x3fb7368c89b2a460,4 +np.float64,0xffc5840ed02b081c,0x3fd92220b56631f3,4 +np.float64,0xffc36a873926d510,0x3fa84d61baf61811,4 +np.float64,0xffe06ea583e0dd4a,0x3feb647e348b9e39,4 +np.float64,0xffe6a33031ed4660,0xbfe096b851dc1a0a,4 +np.float64,0xffe001c938e00392,0x3fe4eece77623e7a,4 +np.float64,0xffc1e4f23b23c9e4,0xbfdb9bb1f83f6ac4,4 +np.float64,0xffecd3ecbab9a7d9,0x3fbafb1f800f177d,4 +np.float64,0xffc2d3016825a604,0xbfef650e8b0d6afb,4 +np.float64,0xffe222cb68e44596,0x3fde3690e44de5bd,4 +np.float64,0xffe5bb145e2b7628,0x3fedbb98e23c9dc1,4 +np.float64,0xffe9e5823b73cb04,0xbfee41661016c03c,4 +np.float64,0xffd234a00ba46940,0x3fda0312cda580c2,4 +np.float64,0xffe0913ed6e1227d,0xbfed508bb529bd23,4 +np.float64,0xffe8e3596171c6b2,0xbfdc33e1c1d0310e,4 +np.float64,0xffef9c6835ff38cf,0x3fea8ce6d27dfba3,4 +np.float64,0xffdd3bcf66ba779e,0x3fe50523d2b6470e,4 +np.float64,0xffe57e8cf06afd1a,0xbfee600933347247,4 +np.float64,0xffe0d8c65fa1b18c,0x3fe75091f93d5e4c,4 +np.float64,0xffea7c8c16b4f918,0x3fee681724795198,4 +np.float64,0xffe34f7a05269ef4,0xbfe3c3e179676f13,4 +np.float64,0xffd28894a6a5112a,0xbfe5d1027aee615d,4 +np.float64,0xffc73be6f22e77cc,0x3fe469bbc08b472a,4 +np.float64,0xffe7f71b066fee36,0x3fe7ed136c8fdfaa,4 +np.float64,0xffebc13e29f7827c,0x3fefcdc6e677d314,4 +np.float64,0xffd53e9c942a7d3a,0x3fea5a02c7341749,4 +np.float64,0xffd7191b23ae3236,0x3fea419b66023443,4 +np.float64,0xffe9480325b29006,0xbfefeaff5fa38cd5,4 +np.float64,0xffba46dc0e348db8,0xbfefa54f4de28eba,4 +np.float64,0xffdd4cc31eba9986,0x3fe60bb41fe1c4da,4 +np.float64,0xffe13a70dea274e1,0xbfaa9192f7bd6c9b,4 +np.float64,0xffde25127bbc4a24,0x3f7c75f45e29be7d,4 +np.float64,0xffe4076543a80eca,0x3fea5aad50d2f687,4 +np.float64,0xffe61512acec2a25,0xbfefffeb67401649,4 +np.float64,0xffef812ec1ff025d,0xbfe919c7c073c766,4 +np.float64,0xffd5552aeaaaaa56,0x3fc89d38ab047396,4 diff --git a/numpy/core/tests/data/umath-validation-set-cosh.csv b/numpy/core/tests/data/umath-validation-set-cosh.csv new file mode 100644 index 000000000000..c9e446c3b147 --- /dev/null +++ b/numpy/core/tests/data/umath-validation-set-cosh.csv @@ -0,0 +1,1429 @@ +dtype,input,output,ulperrortol +np.float32,0xfe0ac238,0x7f800000,3 +np.float32,0xbf553b86,0x3faf079b,3 +np.float32,0xff4457da,0x7f800000,3 +np.float32,0xff7253f3,0x7f800000,3 +np.float32,0x5a5802,0x3f800000,3 +np.float32,0x3db03413,0x3f80795b,3 +np.float32,0x7f6795c9,0x7f800000,3 +np.float32,0x805b9142,0x3f800000,3 +np.float32,0xfeea581a,0x7f800000,3 +np.float32,0x3f7e2dba,0x3fc472f6,3 +np.float32,0x3d9c4d74,0x3f805f7a,3 +np.float32,0x7f18c665,0x7f800000,3 +np.float32,0x7f003e23,0x7f800000,3 +np.float32,0x3d936fa0,0x3f8054f3,3 +np.float32,0x3f32034f,0x3fa0368e,3 +np.float32,0xff087604,0x7f800000,3 +np.float32,0x380a5,0x3f800000,3 +np.float32,0x3f59694e,0x3fb10077,3 +np.float32,0x3e63e648,0x3f832ee4,3 +np.float32,0x80712f42,0x3f800000,3 +np.float32,0x3e169908,0x3f816302,3 +np.float32,0x3f2d766e,0x3f9e8692,3 +np.float32,0x3d6412e0,0x3f8032d0,3 +np.float32,0xbde689e8,0x3f80cfd4,3 +np.float32,0x483e2e,0x3f800000,3 +np.float32,0xff1ba2d0,0x7f800000,3 +np.float32,0x80136bff,0x3f800000,3 +np.float32,0x3f72534c,0x3fbdc1d4,3 +np.float32,0x3e9eb381,0x3f8632c6,3 +np.float32,0x3e142892,0x3f815795,3 +np.float32,0x0,0x3f800000,3 +np.float32,0x2f2528,0x3f800000,3 +np.float32,0x7f38be13,0x7f800000,3 +np.float32,0xfeee6896,0x7f800000,3 +np.float32,0x7f09095d,0x7f800000,3 +np.float32,0xbe94d,0x3f800000,3 +np.float32,0xbedcf8d4,0x3f8c1b74,3 +np.float32,0xbf694c02,0x3fb8ef07,3 +np.float32,0x3e2261f8,0x3f819cde,3 +np.float32,0xbf01d3ce,0x3f90d0e0,3 +np.float32,0xbeb7b3a2,0x3f8853de,3 +np.float32,0x8046de7b,0x3f800000,3 +np.float32,0xbcb45ea0,0x3f8007f1,3 +np.float32,0x3eef14af,0x3f8e35dd,3 +np.float32,0xbf047316,0x3f91846e,3 +np.float32,0x801cef45,0x3f800000,3 +np.float32,0x3e9ad891,0x3f85e609,3 +np.float32,0xff20e9cf,0x7f800000,3 +np.float32,0x80068434,0x3f800000,3 +np.float32,0xbe253020,0x3f81ab49,3 +np.float32,0x3f13f4b8,0x3f95fac9,3 +np.float32,0x804accd1,0x3f800000,3 +np.float32,0x3dee3e10,0x3f80ddf7,3 +np.float32,0xbe6c4690,0x3f836c29,3 +np.float32,0xff30d431,0x7f800000,3 +np.float32,0xbec82416,0x3f89e791,3 +np.float32,0x3f30bbcb,0x3f9fbbcc,3 +np.float32,0x3f5620a2,0x3faf72b8,3 +np.float32,0x807a8130,0x3f800000,3 +np.float32,0x3e3cb02d,0x3f822de0,3 +np.float32,0xff4839ac,0x7f800000,3 +np.float32,0x800a3e9c,0x3f800000,3 +np.float32,0x3dffd65b,0x3f810002,3 +np.float32,0xbf2b1492,0x3f9da987,3 +np.float32,0xbf21602c,0x3f9a48fe,3 +np.float32,0x512531,0x3f800000,3 +np.float32,0x24b99a,0x3f800000,3 +np.float32,0xbf53e345,0x3fae67b1,3 +np.float32,0xff2126ec,0x7f800000,3 +np.float32,0x7e79b49d,0x7f800000,3 +np.float32,0x3ea3cf04,0x3f869b6f,3 +np.float32,0x7f270059,0x7f800000,3 +np.float32,0x3f625b2f,0x3fb561e1,3 +np.float32,0xbf59947e,0x3fb11519,3 +np.float32,0xfe0d1c64,0x7f800000,3 +np.float32,0xbf3f3eae,0x3fa568e2,3 +np.float32,0x7c04d1,0x3f800000,3 +np.float32,0x7e66bd,0x3f800000,3 +np.float32,0x8011880d,0x3f800000,3 +np.float32,0x3f302f07,0x3f9f8759,3 +np.float32,0x4e3375,0x3f800000,3 +np.float32,0xfe67a134,0x7f800000,3 +np.float32,0xff670249,0x7f800000,3 +np.float32,0x7e19f27d,0x7f800000,3 +np.float32,0xbf36ce12,0x3fa20b81,3 +np.float32,0xbe6bcfc4,0x3f8368b5,3 +np.float32,0x76fcba,0x3f800000,3 +np.float32,0x7f30abaf,0x7f800000,3 +np.float32,0x3f4c1f6d,0x3faae43c,3 +np.float32,0x7f61f44a,0x7f800000,3 +np.float32,0xbf4bb3c9,0x3faab4af,3 +np.float32,0xbda15ee0,0x3f8065c6,3 +np.float32,0xfbb4e800,0x7f800000,3 +np.float32,0x7fa00000,0x7fe00000,3 +np.float32,0x80568501,0x3f800000,3 +np.float32,0xfeb285e4,0x7f800000,3 +np.float32,0x804423a7,0x3f800000,3 +np.float32,0x7e6c0f21,0x7f800000,3 +np.float32,0x7f136b3c,0x7f800000,3 +np.float32,0x3f2d08e6,0x3f9e5e9c,3 +np.float32,0xbf6b454e,0x3fb9f7e6,3 +np.float32,0x3e6bceb0,0x3f8368ad,3 +np.float32,0xff1ad16a,0x7f800000,3 +np.float32,0x7cce1a04,0x7f800000,3 +np.float32,0xff7bcf95,0x7f800000,3 +np.float32,0x8049788d,0x3f800000,3 +np.float32,0x7ec45918,0x7f800000,3 +np.float32,0xff7fffff,0x7f800000,3 +np.float32,0x8039a1a0,0x3f800000,3 +np.float32,0x7e90cd72,0x7f800000,3 +np.float32,0xbf7dfd53,0x3fc456cc,3 +np.float32,0x3eeeb664,0x3f8e2a76,3 +np.float32,0x8055ef9b,0x3f800000,3 +np.float32,0x7ee06ddd,0x7f800000,3 +np.float32,0xba2cc000,0x3f800002,3 +np.float32,0x806da632,0x3f800000,3 +np.float32,0x7ecfaaf5,0x7f800000,3 +np.float32,0x3ddd12e6,0x3f80bf19,3 +np.float32,0xbf754394,0x3fbf60b1,3 +np.float32,0x6f3f19,0x3f800000,3 +np.float32,0x800a9af0,0x3f800000,3 +np.float32,0xfeef13ea,0x7f800000,3 +np.float32,0x7f74841f,0x7f800000,3 +np.float32,0xbeb9a2f0,0x3f888181,3 +np.float32,0x77cbb,0x3f800000,3 +np.float32,0xbf587f84,0x3fb0911b,3 +np.float32,0x210ba5,0x3f800000,3 +np.float32,0x3ee60a28,0x3f8d2367,3 +np.float32,0xbe3731ac,0x3f820dc7,3 +np.float32,0xbee8cfee,0x3f8d765e,3 +np.float32,0x7b2ef179,0x7f800000,3 +np.float32,0xfe81377c,0x7f800000,3 +np.float32,0x6ac98c,0x3f800000,3 +np.float32,0x3f51f144,0x3fad8288,3 +np.float32,0x80785750,0x3f800000,3 +np.float32,0x3f46615a,0x3fa864ff,3 +np.float32,0xbf35ac9e,0x3fa19b8e,3 +np.float32,0x7f0982ac,0x7f800000,3 +np.float32,0x1b2610,0x3f800000,3 +np.float32,0x3ed8bb25,0x3f8ba3df,3 +np.float32,0xbeb41bac,0x3f88006d,3 +np.float32,0xff48e89d,0x7f800000,3 +np.float32,0x3ed0ab8c,0x3f8ac755,3 +np.float32,0xbe64671c,0x3f833282,3 +np.float32,0x64bce4,0x3f800000,3 +np.float32,0x284f79,0x3f800000,3 +np.float32,0x7e09faa7,0x7f800000,3 +np.float32,0x4376c1,0x3f800000,3 +np.float32,0x805ca8c0,0x3f800000,3 +np.float32,0xff0859d5,0x7f800000,3 +np.float32,0xbed2f3b2,0x3f8b04dd,3 +np.float32,0x8045bd0c,0x3f800000,3 +np.float32,0x3f0e6216,0x3f94503f,3 +np.float32,0x3f41e3ae,0x3fa68035,3 +np.float32,0x80088ccc,0x3f800000,3 +np.float32,0x3f37fc19,0x3fa2812f,3 +np.float32,0x71c87d,0x3f800000,3 +np.float32,0x8024f4b2,0x3f800000,3 +np.float32,0xff78dd88,0x7f800000,3 +np.float32,0xbda66c90,0x3f806c40,3 +np.float32,0x7f33ef0d,0x7f800000,3 +np.float32,0x46a343,0x3f800000,3 +np.float32,0xff1dce38,0x7f800000,3 +np.float32,0x1b935d,0x3f800000,3 +np.float32,0x3ebec598,0x3f88fd0e,3 +np.float32,0xff115530,0x7f800000,3 +np.float32,0x803916aa,0x3f800000,3 +np.float32,0xff60a3e2,0x7f800000,3 +np.float32,0x3b8ddd48,0x3f80004f,3 +np.float32,0x3f761b6e,0x3fbfd8ea,3 +np.float32,0xbdf55b88,0x3f80eb70,3 +np.float32,0x37374,0x3f800000,3 +np.float32,0x3de150e0,0x3f80c682,3 +np.float32,0x3f343278,0x3fa10a83,3 +np.float32,0xbe9baefa,0x3f85f68b,3 +np.float32,0x3d8d43,0x3f800000,3 +np.float32,0x3e80994b,0x3f840f0c,3 +np.float32,0xbe573c6c,0x3f82d685,3 +np.float32,0x805b83b4,0x3f800000,3 +np.float32,0x683d88,0x3f800000,3 +np.float32,0x692465,0x3f800000,3 +np.float32,0xbdc345f8,0x3f809511,3 +np.float32,0x3f7c1c5a,0x3fc3406f,3 +np.float32,0xbf40bef3,0x3fa606df,3 +np.float32,0xff1e25b9,0x7f800000,3 +np.float32,0x3e4481e0,0x3f825d37,3 +np.float32,0x75d188,0x3f800000,3 +np.float32,0x3ea53cec,0x3f86b956,3 +np.float32,0xff105a54,0x7f800000,3 +np.float32,0x7f800000,0x7f800000,3 +np.float32,0x7f11f0b0,0x7f800000,3 +np.float32,0xbf58a57d,0x3fb0a328,3 +np.float32,0xbdd11e38,0x3f80aaf8,3 +np.float32,0xbea94adc,0x3f870fa0,3 +np.float32,0x3e9dd780,0x3f862180,3 +np.float32,0xff1786b9,0x7f800000,3 +np.float32,0xfec46aa2,0x7f800000,3 +np.float32,0x7f4300c1,0x7f800000,3 +np.float32,0x29ba2b,0x3f800000,3 +np.float32,0x3f4112e2,0x3fa62993,3 +np.float32,0xbe6c9224,0x3f836e5d,3 +np.float32,0x7f0e42a3,0x7f800000,3 +np.float32,0xff6390ad,0x7f800000,3 +np.float32,0x3f54e374,0x3faede94,3 +np.float32,0x7f2642a2,0x7f800000,3 +np.float32,0x7f46b2be,0x7f800000,3 +np.float32,0xfe59095c,0x7f800000,3 +np.float32,0x7146a0,0x3f800000,3 +np.float32,0x3f07763d,0x3f925786,3 +np.float32,0x3d172780,0x3f801651,3 +np.float32,0xff66f1c5,0x7f800000,3 +np.float32,0xff025349,0x7f800000,3 +np.float32,0x6ce99d,0x3f800000,3 +np.float32,0xbf7e4f50,0x3fc48685,3 +np.float32,0xbeff8ca2,0x3f904708,3 +np.float32,0x3e6c8,0x3f800000,3 +np.float32,0x7f7153dc,0x7f800000,3 +np.float32,0xbedcf612,0x3f8c1b26,3 +np.float32,0xbbc2f180,0x3f800094,3 +np.float32,0xbf397399,0x3fa314b8,3 +np.float32,0x6c6e35,0x3f800000,3 +np.float32,0x7f50a88b,0x7f800000,3 +np.float32,0xfe84093e,0x7f800000,3 +np.float32,0x3f737b9d,0x3fbe6478,3 +np.float32,0x7f6a5340,0x7f800000,3 +np.float32,0xbde83c20,0x3f80d2e7,3 +np.float32,0xff769ce9,0x7f800000,3 +np.float32,0xfdd33c30,0x7f800000,3 +np.float32,0xbc95cb60,0x3f80057a,3 +np.float32,0x8007a40d,0x3f800000,3 +np.float32,0x3f55d90c,0x3faf5132,3 +np.float32,0x80282082,0x3f800000,3 +np.float32,0xbf43b1f2,0x3fa7418c,3 +np.float32,0x3f1dc7cb,0x3f991731,3 +np.float32,0xbd4346a0,0x3f80253f,3 +np.float32,0xbf5aa82a,0x3fb19946,3 +np.float32,0x3f4b8c22,0x3faaa333,3 +np.float32,0x3d13468c,0x3f80152f,3 +np.float32,0x7db77097,0x7f800000,3 +np.float32,0x4a00df,0x3f800000,3 +np.float32,0xbedea5e0,0x3f8c4b64,3 +np.float32,0x80482543,0x3f800000,3 +np.float32,0xbef344fe,0x3f8eb8dd,3 +np.float32,0x7ebd4044,0x7f800000,3 +np.float32,0xbf512c0e,0x3fad287e,3 +np.float32,0x3db28cce,0x3f807c9c,3 +np.float32,0xbd0f5ae0,0x3f801412,3 +np.float32,0xfe7ed9ac,0x7f800000,3 +np.float32,0x3eb1aa82,0x3f87c8b4,3 +np.float32,0xfef1679e,0x7f800000,3 +np.float32,0xff3629f2,0x7f800000,3 +np.float32,0xff3562b4,0x7f800000,3 +np.float32,0x3dcafe1d,0x3f80a118,3 +np.float32,0xfedf242a,0x7f800000,3 +np.float32,0xbf43102a,0x3fa6fda4,3 +np.float32,0x8028834e,0x3f800000,3 +np.float32,0x805c8513,0x3f800000,3 +np.float32,0x3f59306a,0x3fb0e550,3 +np.float32,0x3eda2c9c,0x3f8bcc4a,3 +np.float32,0x80023524,0x3f800000,3 +np.float32,0x7ef72879,0x7f800000,3 +np.float32,0x661c8a,0x3f800000,3 +np.float32,0xfec3ba6c,0x7f800000,3 +np.float32,0x805aaca6,0x3f800000,3 +np.float32,0xff5c1f13,0x7f800000,3 +np.float32,0x3f6ab3f4,0x3fb9ab6b,3 +np.float32,0x3f014896,0x3f90ac20,3 +np.float32,0x3f030584,0x3f91222a,3 +np.float32,0xbf74853d,0x3fbef71d,3 +np.float32,0xbf534ee0,0x3fae2323,3 +np.float32,0x2c90c3,0x3f800000,3 +np.float32,0x7f62ad25,0x7f800000,3 +np.float32,0x1c8847,0x3f800000,3 +np.float32,0x7e2a8d43,0x7f800000,3 +np.float32,0x807a09cd,0x3f800000,3 +np.float32,0x413871,0x3f800000,3 +np.float32,0x80063692,0x3f800000,3 +np.float32,0x3edaf29b,0x3f8be211,3 +np.float32,0xbf64a7ab,0x3fb68b2d,3 +np.float32,0xfe56a720,0x7f800000,3 +np.float32,0xbf54a8d4,0x3faec350,3 +np.float32,0x3ecbaef7,0x3f8a4350,3 +np.float32,0x3f413714,0x3fa63890,3 +np.float32,0x7d3aa8,0x3f800000,3 +np.float32,0xbea9a13c,0x3f8716e7,3 +np.float32,0x7ef7553e,0x7f800000,3 +np.float32,0x8056f29f,0x3f800000,3 +np.float32,0xff1f7ffe,0x7f800000,3 +np.float32,0x3f41953b,0x3fa65f9c,3 +np.float32,0x3daa2f,0x3f800000,3 +np.float32,0xff0893e4,0x7f800000,3 +np.float32,0xbefc7ec6,0x3f8fe207,3 +np.float32,0xbb026800,0x3f800011,3 +np.float32,0x341e4f,0x3f800000,3 +np.float32,0x3e7b708a,0x3f83e0d1,3 +np.float32,0xa18cb,0x3f800000,3 +np.float32,0x7e290239,0x7f800000,3 +np.float32,0xbf4254f2,0x3fa6af62,3 +np.float32,0x80000000,0x3f800000,3 +np.float32,0x3f0a6c,0x3f800000,3 +np.float32,0xbec44d28,0x3f898609,3 +np.float32,0xf841f,0x3f800000,3 +np.float32,0x7f01a693,0x7f800000,3 +np.float32,0x8053340b,0x3f800000,3 +np.float32,0xfd4e7990,0x7f800000,3 +np.float32,0xbf782f1f,0x3fc10356,3 +np.float32,0xbe962118,0x3f858acc,3 +np.float32,0xfe8cd702,0x7f800000,3 +np.float32,0x7ecd986f,0x7f800000,3 +np.float32,0x3ebe775f,0x3f88f59b,3 +np.float32,0x8065524f,0x3f800000,3 +np.float32,0x3ede7fc4,0x3f8c471e,3 +np.float32,0x7f5e15ea,0x7f800000,3 +np.float32,0xbe871ada,0x3f847b78,3 +np.float32,0x3f21958b,0x3f9a5af7,3 +np.float32,0x3f64d480,0x3fb6a1fa,3 +np.float32,0xff18b0e9,0x7f800000,3 +np.float32,0xbf0840dd,0x3f928fd9,3 +np.float32,0x80104f5d,0x3f800000,3 +np.float32,0x643b94,0x3f800000,3 +np.float32,0xbc560a80,0x3f8002cc,3 +np.float32,0x3f5c75d6,0x3fb2786e,3 +np.float32,0x7f365fc9,0x7f800000,3 +np.float32,0x54e965,0x3f800000,3 +np.float32,0x6dcd4d,0x3f800000,3 +np.float32,0x3f2057a0,0x3f99f04d,3 +np.float32,0x272fa3,0x3f800000,3 +np.float32,0xff423dc9,0x7f800000,3 +np.float32,0x80273463,0x3f800000,3 +np.float32,0xfe21cc78,0x7f800000,3 +np.float32,0x7fc00000,0x7fc00000,3 +np.float32,0x802feb65,0x3f800000,3 +np.float32,0x3dc733d0,0x3f809b21,3 +np.float32,0x65d56b,0x3f800000,3 +np.float32,0x80351d8e,0x3f800000,3 +np.float32,0xbf244247,0x3f9b43dd,3 +np.float32,0x7f328e7e,0x7f800000,3 +np.float32,0x7f4d9712,0x7f800000,3 +np.float32,0x2c505d,0x3f800000,3 +np.float32,0xbf232ebe,0x3f9ae5a0,3 +np.float32,0x804a363a,0x3f800000,3 +np.float32,0x80417102,0x3f800000,3 +np.float32,0xbf48b170,0x3fa963d4,3 +np.float32,0x7ea3e3b6,0x7f800000,3 +np.float32,0xbf41415b,0x3fa63cd2,3 +np.float32,0xfe3af7c8,0x7f800000,3 +np.float32,0x7f478010,0x7f800000,3 +np.float32,0x80143113,0x3f800000,3 +np.float32,0x3f7626a7,0x3fbfdf2e,3 +np.float32,0xfea20b0a,0x7f800000,3 +np.float32,0x80144d64,0x3f800000,3 +np.float32,0x7db9ba47,0x7f800000,3 +np.float32,0x7f7fffff,0x7f800000,3 +np.float32,0xbe410834,0x3f8247ef,3 +np.float32,0x14a7af,0x3f800000,3 +np.float32,0x7eaebf9e,0x7f800000,3 +np.float32,0xff800000,0x7f800000,3 +np.float32,0x3f0a7d8e,0x3f9330fd,3 +np.float32,0x3ef780,0x3f800000,3 +np.float32,0x3f62253e,0x3fb546d1,3 +np.float32,0x3f4cbeac,0x3fab2acc,3 +np.float32,0x25db1,0x3f800000,3 +np.float32,0x65c54a,0x3f800000,3 +np.float32,0x800f0645,0x3f800000,3 +np.float32,0x3ed28c78,0x3f8af9f0,3 +np.float32,0x8040c6ce,0x3f800000,3 +np.float32,0x5e4e9a,0x3f800000,3 +np.float32,0xbd3fd2b0,0x3f8023f1,3 +np.float32,0xbf5d2d3f,0x3fb2d1b6,3 +np.float32,0x7ead999f,0x7f800000,3 +np.float32,0xbf30dc86,0x3f9fc805,3 +np.float32,0xff2b0a62,0x7f800000,3 +np.float32,0x3d5180e9,0x3f802adf,3 +np.float32,0x3f62716f,0x3fb56d0d,3 +np.float32,0x7e82ae9c,0x7f800000,3 +np.float32,0xfe2d4bdc,0x7f800000,3 +np.float32,0x805cc7d4,0x3f800000,3 +np.float32,0xfb50f700,0x7f800000,3 +np.float32,0xff57b684,0x7f800000,3 +np.float32,0x80344f01,0x3f800000,3 +np.float32,0x7f2af372,0x7f800000,3 +np.float32,0xfeab6204,0x7f800000,3 +np.float32,0x30b251,0x3f800000,3 +np.float32,0x3eed8cc4,0x3f8e0698,3 +np.float32,0x7eeb1c6a,0x7f800000,3 +np.float32,0x3f17ece6,0x3f9735b0,3 +np.float32,0x21e985,0x3f800000,3 +np.float32,0x3f3a7df3,0x3fa37e34,3 +np.float32,0x802a14a2,0x3f800000,3 +np.float32,0x807d4d5b,0x3f800000,3 +np.float32,0x7f6093ce,0x7f800000,3 +np.float32,0x3f800000,0x3fc583ab,3 +np.float32,0x3da2c26e,0x3f806789,3 +np.float32,0xfe05f278,0x7f800000,3 +np.float32,0x800000,0x3f800000,3 +np.float32,0xbee63342,0x3f8d282e,3 +np.float32,0xbf225586,0x3f9a9bd4,3 +np.float32,0xbed60e86,0x3f8b59ba,3 +np.float32,0xbec99484,0x3f8a0ca3,3 +np.float32,0x3e967c71,0x3f859199,3 +np.float32,0x7f26ab62,0x7f800000,3 +np.float32,0xca7f4,0x3f800000,3 +np.float32,0xbf543790,0x3fae8ebc,3 +np.float32,0x3e4c1ed9,0x3f828d2d,3 +np.float32,0xbdf37f88,0x3f80e7e1,3 +np.float32,0xff0cc44e,0x7f800000,3 +np.float32,0x5dea48,0x3f800000,3 +np.float32,0x31023c,0x3f800000,3 +np.float32,0x3ea10733,0x3f866208,3 +np.float32,0x3e11e6f2,0x3f814d2e,3 +np.float32,0x80641960,0x3f800000,3 +np.float32,0x3ef779a8,0x3f8f3edb,3 +np.float32,0x3f2a5062,0x3f9d632a,3 +np.float32,0x2b7d34,0x3f800000,3 +np.float32,0x3eeb95c5,0x3f8dca67,3 +np.float32,0x805c1357,0x3f800000,3 +np.float32,0x3db3a79d,0x3f807e29,3 +np.float32,0xfded1900,0x7f800000,3 +np.float32,0x45f362,0x3f800000,3 +np.float32,0x451f38,0x3f800000,3 +np.float32,0x801d3ae5,0x3f800000,3 +np.float32,0x458d45,0x3f800000,3 +np.float32,0xfda9d298,0x7f800000,3 +np.float32,0x467439,0x3f800000,3 +np.float32,0x7f66554a,0x7f800000,3 +np.float32,0xfef2375a,0x7f800000,3 +np.float32,0xbf33fc47,0x3fa0f5d7,3 +np.float32,0x3f75ba69,0x3fbfa2d0,3 +np.float32,0xfeb625b2,0x7f800000,3 +np.float32,0x8066b371,0x3f800000,3 +np.float32,0x3f5cb4e9,0x3fb29718,3 +np.float32,0x7f3b6a58,0x7f800000,3 +np.float32,0x7f6b35ea,0x7f800000,3 +np.float32,0xbf6ee555,0x3fbbe5be,3 +np.float32,0x3d836e21,0x3f804380,3 +np.float32,0xff43cd0c,0x7f800000,3 +np.float32,0xff55c1fa,0x7f800000,3 +np.float32,0xbf0dfccc,0x3f9432a6,3 +np.float32,0x3ed92121,0x3f8baf00,3 +np.float32,0x80068cc1,0x3f800000,3 +np.float32,0xff0103f9,0x7f800000,3 +np.float32,0x7e51b175,0x7f800000,3 +np.float32,0x8012f214,0x3f800000,3 +np.float32,0x62d298,0x3f800000,3 +np.float32,0xbf3e1525,0x3fa4ef8d,3 +np.float32,0x806b4882,0x3f800000,3 +np.float32,0xbf38c146,0x3fa2ce7c,3 +np.float32,0xbed59c30,0x3f8b4d70,3 +np.float32,0x3d1910c0,0x3f8016e2,3 +np.float32,0x7f33d55b,0x7f800000,3 +np.float32,0x7f5800e3,0x7f800000,3 +np.float32,0x5b2c5d,0x3f800000,3 +np.float32,0x807be750,0x3f800000,3 +np.float32,0x7eb297c1,0x7f800000,3 +np.float32,0x7dafee62,0x7f800000,3 +np.float32,0x7d9e23f0,0x7f800000,3 +np.float32,0x3e580537,0x3f82dbd8,3 +np.float32,0xbf800000,0x3fc583ab,3 +np.float32,0x7f40f880,0x7f800000,3 +np.float32,0x775ad3,0x3f800000,3 +np.float32,0xbedacd36,0x3f8bddf3,3 +np.float32,0x2138f6,0x3f800000,3 +np.float32,0x52c3b7,0x3f800000,3 +np.float32,0x8041cfdd,0x3f800000,3 +np.float32,0x7bf16791,0x7f800000,3 +np.float32,0xbe95869c,0x3f857f55,3 +np.float32,0xbf199796,0x3f97bcaf,3 +np.float32,0x3ef8da38,0x3f8f6b45,3 +np.float32,0x803f3648,0x3f800000,3 +np.float32,0x80026fd2,0x3f800000,3 +np.float32,0x7eb3ac26,0x7f800000,3 +np.float32,0x3e49921b,0x3f827ce8,3 +np.float32,0xbf689aed,0x3fb892de,3 +np.float32,0x3f253509,0x3f9b9779,3 +np.float32,0xff17894a,0x7f800000,3 +np.float32,0x3cd12639,0x3f800aae,3 +np.float32,0x1db14b,0x3f800000,3 +np.float32,0x39a0bf,0x3f800000,3 +np.float32,0xfdfe1d08,0x7f800000,3 +np.float32,0xff416cd2,0x7f800000,3 +np.float32,0x8070d818,0x3f800000,3 +np.float32,0x3e516e12,0x3f82afb8,3 +np.float32,0x80536651,0x3f800000,3 +np.float32,0xbf2903d2,0x3f9cecb7,3 +np.float32,0x3e896ae4,0x3f84a353,3 +np.float32,0xbd6ba2c0,0x3f80363d,3 +np.float32,0x80126d3e,0x3f800000,3 +np.float32,0xfd9d43d0,0x7f800000,3 +np.float32,0x7b56b6,0x3f800000,3 +np.float32,0xff04718e,0x7f800000,3 +np.float32,0x31440f,0x3f800000,3 +np.float32,0xbf7a1313,0x3fc215c9,3 +np.float32,0x7f43d6a0,0x7f800000,3 +np.float32,0x3f566503,0x3faf92cc,3 +np.float32,0xbf39eb0e,0x3fa343f1,3 +np.float32,0xbe35fd70,0x3f8206df,3 +np.float32,0x800c36ac,0x3f800000,3 +np.float32,0x60d061,0x3f800000,3 +np.float32,0x80453e12,0x3f800000,3 +np.float32,0xfe17c36c,0x7f800000,3 +np.float32,0x3d8c72,0x3f800000,3 +np.float32,0xfe8e9134,0x7f800000,3 +np.float32,0xff5d89de,0x7f800000,3 +np.float32,0x7f45020e,0x7f800000,3 +np.float32,0x3f28225e,0x3f9c9d01,3 +np.float32,0xbf3b6900,0x3fa3dbdd,3 +np.float32,0x80349023,0x3f800000,3 +np.float32,0xbf14d780,0x3f964042,3 +np.float32,0x3f56b5d2,0x3fafb8c3,3 +np.float32,0x800c639c,0x3f800000,3 +np.float32,0x7f7a19c8,0x7f800000,3 +np.float32,0xbf7a0815,0x3fc20f86,3 +np.float32,0xbec55926,0x3f89a06e,3 +np.float32,0x4b2cd2,0x3f800000,3 +np.float32,0xbf271eb2,0x3f9c41c8,3 +np.float32,0xff26e168,0x7f800000,3 +np.float32,0x800166b2,0x3f800000,3 +np.float32,0xbde97e38,0x3f80d532,3 +np.float32,0xbf1f93ec,0x3f99af1a,3 +np.float32,0x7f2896ed,0x7f800000,3 +np.float32,0x3da7d96d,0x3f806e1d,3 +np.float32,0x802b7237,0x3f800000,3 +np.float32,0xfdca6bc0,0x7f800000,3 +np.float32,0xbed2e300,0x3f8b0318,3 +np.float32,0x8079d9e8,0x3f800000,3 +np.float32,0x3f388c81,0x3fa2b9c2,3 +np.float32,0x3ed2607c,0x3f8af54a,3 +np.float32,0xff287de6,0x7f800000,3 +np.float32,0x3f55ed89,0x3faf5ac9,3 +np.float32,0x7f5b6af7,0x7f800000,3 +np.float32,0xbeb24730,0x3f87d698,3 +np.float32,0x1,0x3f800000,3 +np.float32,0x3f3a2350,0x3fa35a3b,3 +np.float32,0x8013b422,0x3f800000,3 +np.float32,0x3e9a6560,0x3f85dd35,3 +np.float32,0x80510631,0x3f800000,3 +np.float32,0xfeae39d6,0x7f800000,3 +np.float32,0x7eb437ad,0x7f800000,3 +np.float32,0x8047545b,0x3f800000,3 +np.float32,0x806a1c71,0x3f800000,3 +np.float32,0xbe5543f0,0x3f82c93b,3 +np.float32,0x40e8d,0x3f800000,3 +np.float32,0x63d18b,0x3f800000,3 +np.float32,0x1fa1ea,0x3f800000,3 +np.float32,0x801944e0,0x3f800000,3 +np.float32,0xbf4c7ac6,0x3fab0cae,3 +np.float32,0x7f2679d4,0x7f800000,3 +np.float32,0x3f0102fc,0x3f9099d0,3 +np.float32,0x7e44bdc1,0x7f800000,3 +np.float32,0xbf2072f6,0x3f99f970,3 +np.float32,0x5c7d38,0x3f800000,3 +np.float32,0x30a2e6,0x3f800000,3 +np.float32,0x805b9ca3,0x3f800000,3 +np.float32,0x7cc24ad5,0x7f800000,3 +np.float32,0x3f4f7920,0x3fac6357,3 +np.float32,0x111d62,0x3f800000,3 +np.float32,0xbf4de40a,0x3fabad77,3 +np.float32,0x805d0354,0x3f800000,3 +np.float32,0xbb3d2b00,0x3f800023,3 +np.float32,0x3ef229e7,0x3f8e960b,3 +np.float32,0x3f15754e,0x3f9670e0,3 +np.float32,0xbf689c6b,0x3fb893a5,3 +np.float32,0xbf3796c6,0x3fa2599b,3 +np.float32,0xbe95303c,0x3f8578f2,3 +np.float32,0xfee330de,0x7f800000,3 +np.float32,0xff0d9705,0x7f800000,3 +np.float32,0xbeb0ebd0,0x3f87b7dd,3 +np.float32,0xbf4d5a13,0x3fab6fe7,3 +np.float32,0x80142f5a,0x3f800000,3 +np.float32,0x7e01a87b,0x7f800000,3 +np.float32,0xbe45e5ec,0x3f8265d7,3 +np.float32,0x7f4ac255,0x7f800000,3 +np.float32,0x3ebf6a60,0x3f890ccb,3 +np.float32,0x7f771e16,0x7f800000,3 +np.float32,0x3f41834e,0x3fa6582b,3 +np.float32,0x3f7f6f98,0x3fc52ef0,3 +np.float32,0x7e4ad775,0x7f800000,3 +np.float32,0x3eb39991,0x3f87f4c4,3 +np.float32,0x1e3f4,0x3f800000,3 +np.float32,0x7e84ba19,0x7f800000,3 +np.float32,0x80640be4,0x3f800000,3 +np.float32,0x3f459fc8,0x3fa81272,3 +np.float32,0x3f554ed0,0x3faf109b,3 +np.float32,0x3c6617,0x3f800000,3 +np.float32,0x7f441158,0x7f800000,3 +np.float32,0x7f66e6d8,0x7f800000,3 +np.float32,0x7f565152,0x7f800000,3 +np.float32,0x7f16d550,0x7f800000,3 +np.float32,0xbd4f1950,0x3f8029e5,3 +np.float32,0xcf722,0x3f800000,3 +np.float32,0x3f37d6fd,0x3fa272ad,3 +np.float32,0xff7324ea,0x7f800000,3 +np.float32,0x804bc246,0x3f800000,3 +np.float32,0x7f099ef8,0x7f800000,3 +np.float32,0x5f838b,0x3f800000,3 +np.float32,0x80523534,0x3f800000,3 +np.float32,0x3f595e84,0x3fb0fb50,3 +np.float32,0xfdef8ac8,0x7f800000,3 +np.float32,0x3d9a07,0x3f800000,3 +np.float32,0x410f61,0x3f800000,3 +np.float32,0xbf715dbb,0x3fbd3bcb,3 +np.float32,0xbedd4734,0x3f8c242f,3 +np.float32,0x7e86739a,0x7f800000,3 +np.float32,0x3e81f144,0x3f8424fe,3 +np.float32,0x7f6342d1,0x7f800000,3 +np.float32,0xff6919a3,0x7f800000,3 +np.float32,0xff051878,0x7f800000,3 +np.float32,0x800ba28f,0x3f800000,3 +np.float32,0xfefab3d8,0x7f800000,3 +np.float32,0xff612a84,0x7f800000,3 +np.float32,0x800cd5ab,0x3f800000,3 +np.float32,0x802a07ae,0x3f800000,3 +np.float32,0xfef6ee3a,0x7f800000,3 +np.float32,0x8037e896,0x3f800000,3 +np.float32,0x3ef2d86f,0x3f8eab7d,3 +np.float32,0x3eafe53d,0x3f87a0cb,3 +np.float32,0xba591c00,0x3f800003,3 +np.float32,0x3e9ed028,0x3f863508,3 +np.float32,0x4a12a8,0x3f800000,3 +np.float32,0xbee55c84,0x3f8d0f45,3 +np.float32,0x8038a8d3,0x3f800000,3 +np.float32,0xff055243,0x7f800000,3 +np.float32,0xbf659067,0x3fb701ca,3 +np.float32,0xbee36a86,0x3f8cd5e0,3 +np.float32,0x7f1d74c1,0x7f800000,3 +np.float32,0xbf7657df,0x3fbffaad,3 +np.float32,0x7e37ee34,0x7f800000,3 +np.float32,0xff04bc74,0x7f800000,3 +np.float32,0x806d194e,0x3f800000,3 +np.float32,0x7f5596c3,0x7f800000,3 +np.float32,0xbe09d268,0x3f81293e,3 +np.float32,0x79ff75,0x3f800000,3 +np.float32,0xbf55479c,0x3faf0d3e,3 +np.float32,0xbe5428ec,0x3f82c1d4,3 +np.float32,0x3f624134,0x3fb554d7,3 +np.float32,0x2ccb8a,0x3f800000,3 +np.float32,0xfc082040,0x7f800000,3 +np.float32,0xff315467,0x7f800000,3 +np.float32,0x3e6ea2d2,0x3f837dd5,3 +np.float32,0x8020fdd1,0x3f800000,3 +np.float32,0x7f0416a1,0x7f800000,3 +np.float32,0x710a1b,0x3f800000,3 +np.float32,0x3dfcd050,0x3f80f9fc,3 +np.float32,0xfe995e96,0x7f800000,3 +np.float32,0x3f020d00,0x3f90e006,3 +np.float32,0x8064263e,0x3f800000,3 +np.float32,0xfcee4160,0x7f800000,3 +np.float32,0x801b3a18,0x3f800000,3 +np.float32,0x3f62c984,0x3fb59955,3 +np.float32,0x806e8355,0x3f800000,3 +np.float32,0x7e94f65d,0x7f800000,3 +np.float32,0x1173de,0x3f800000,3 +np.float32,0x3e3ff3b7,0x3f824166,3 +np.float32,0x803b4aea,0x3f800000,3 +np.float32,0x804c5bcc,0x3f800000,3 +np.float32,0x509fe5,0x3f800000,3 +np.float32,0xbf33b5ee,0x3fa0db0b,3 +np.float32,0x3f2ac15c,0x3f9d8ba4,3 +np.float32,0x7f2c54f8,0x7f800000,3 +np.float32,0x7f33d933,0x7f800000,3 +np.float32,0xbf09b2b4,0x3f92f795,3 +np.float32,0x805db8d6,0x3f800000,3 +np.float32,0x6d6e66,0x3f800000,3 +np.float32,0x3ddfea92,0x3f80c40c,3 +np.float32,0xfda719b8,0x7f800000,3 +np.float32,0x5d657f,0x3f800000,3 +np.float32,0xbf005ba3,0x3f906df6,3 +np.float32,0xbf45e606,0x3fa8305c,3 +np.float32,0x5e9fd1,0x3f800000,3 +np.float32,0x8079dc45,0x3f800000,3 +np.float32,0x7e9c40e3,0x7f800000,3 +np.float32,0x6bd5f6,0x3f800000,3 +np.float32,0xbea14a0e,0x3f866761,3 +np.float32,0x7e7323f3,0x7f800000,3 +np.float32,0x7f0c0a79,0x7f800000,3 +np.float32,0xbf7d7aeb,0x3fc40b0f,3 +np.float32,0x437588,0x3f800000,3 +np.float32,0xbf356376,0x3fa17f63,3 +np.float32,0x7f129921,0x7f800000,3 +np.float32,0x7f47a52e,0x7f800000,3 +np.float32,0xba8cb400,0x3f800005,3 +np.float32,0x802284e0,0x3f800000,3 +np.float32,0xbe820f56,0x3f8426ec,3 +np.float32,0x7f2ef6cf,0x7f800000,3 +np.float32,0xbf70a090,0x3fbcd501,3 +np.float32,0xbf173fea,0x3f96ff6d,3 +np.float32,0x3e19c489,0x3f817224,3 +np.float32,0x7f429b30,0x7f800000,3 +np.float32,0xbdae4118,0x3f8076af,3 +np.float32,0x3e70ad30,0x3f838d41,3 +np.float32,0x335fed,0x3f800000,3 +np.float32,0xff5359cf,0x7f800000,3 +np.float32,0xbf17e42b,0x3f9732f1,3 +np.float32,0xff3a950b,0x7f800000,3 +np.float32,0xbcca70c0,0x3f800a02,3 +np.float32,0x3f2cda62,0x3f9e4dad,3 +np.float32,0x3f50c185,0x3facf805,3 +np.float32,0x80000001,0x3f800000,3 +np.float32,0x807b86d2,0x3f800000,3 +np.float32,0x8010c2cf,0x3f800000,3 +np.float32,0x3f130fb8,0x3f95b519,3 +np.float32,0x807dc546,0x3f800000,3 +np.float32,0xbee20740,0x3f8cad3f,3 +np.float32,0x80800000,0x3f800000,3 +np.float32,0x3cbd90c0,0x3f8008c6,3 +np.float32,0x3e693488,0x3f835571,3 +np.float32,0xbe70cd44,0x3f838e35,3 +np.float32,0xbe348dc8,0x3f81feb1,3 +np.float32,0x3f31ea90,0x3fa02d3f,3 +np.float32,0xfcd7e180,0x7f800000,3 +np.float32,0xbe30a75c,0x3f81e8d0,3 +np.float32,0x3e552c5a,0x3f82c89d,3 +np.float32,0xff513f74,0x7f800000,3 +np.float32,0xbdb16248,0x3f807afd,3 +np.float64,0x7fbbf954e437f2a9,0x7ff0000000000000,2 +np.float64,0x581bbf0cb0379,0x3ff0000000000000,2 +np.float64,0x7ff8000000000000,0x7ff8000000000000,2 +np.float64,0xffb959a2a632b348,0x7ff0000000000000,2 +np.float64,0xbfdbd6baebb7ad76,0x3ff189a5ca25a6e1,2 +np.float64,0xbfd094ec9aa129da,0x3ff08a3f6b918065,2 +np.float64,0x3fe236753f646cea,0x3ff2a982660b8b43,2 +np.float64,0xbfe537fadfaa6ff6,0x3ff3a5f1c49c31bf,2 +np.float64,0xbfe31fa7dc663f50,0x3ff2f175374aef0e,2 +np.float64,0x3fc4b6569f296cb0,0x3ff035bde801bb53,2 +np.float64,0x800ce3c00f99c780,0x3ff0000000000000,2 +np.float64,0xbfebcde33e779bc6,0x3ff66de82cd30fc5,2 +np.float64,0x800dc09d3b7b813b,0x3ff0000000000000,2 +np.float64,0x80067d4c450cfa99,0x3ff0000000000000,2 +np.float64,0x1f6ade203ed7,0x3ff0000000000000,2 +np.float64,0xbfd4e311eca9c624,0x3ff0dc1383d6c3db,2 +np.float64,0x800649b3a54c9368,0x3ff0000000000000,2 +np.float64,0xcc14d1ab9829a,0x3ff0000000000000,2 +np.float64,0x3fc290c5bb25218b,0x3ff02b290f46dd6d,2 +np.float64,0x3fe78eb8376f1d70,0x3ff488f3bc259537,2 +np.float64,0xffc60f58e82c1eb0,0x7ff0000000000000,2 +np.float64,0x3fd35666ad26accd,0x3ff0bc6573da6bcd,2 +np.float64,0x7fc20257a62404ae,0x7ff0000000000000,2 +np.float64,0x80076d842e0edb09,0x3ff0000000000000,2 +np.float64,0x3fd8e44b08b1c898,0x3ff139b9a1f8428e,2 +np.float64,0x7fd6f6fc7a2dedf8,0x7ff0000000000000,2 +np.float64,0x3fa01b9f0820373e,0x3ff00206f8ad0f1b,2 +np.float64,0x69ed190ed3da4,0x3ff0000000000000,2 +np.float64,0xbfd997eb34b32fd6,0x3ff14be65a5db4a0,2 +np.float64,0x7feada2d0935b459,0x7ff0000000000000,2 +np.float64,0xbf80987120213100,0x3ff000226d29a9fc,2 +np.float64,0xbfef203e37fe407c,0x3ff82f51f04e8821,2 +np.float64,0xffe3dcf91fa7b9f2,0x7ff0000000000000,2 +np.float64,0x9a367283346cf,0x3ff0000000000000,2 +np.float64,0x800feb09f7bfd614,0x3ff0000000000000,2 +np.float64,0xbfe0319f9520633f,0x3ff217c5205c403f,2 +np.float64,0xbfa91eabd4323d50,0x3ff004ee4347f627,2 +np.float64,0x3fd19cbf7d23397f,0x3ff09c13e8e43571,2 +np.float64,0xffeb8945f0b7128b,0x7ff0000000000000,2 +np.float64,0x800a0eb4f2141d6a,0x3ff0000000000000,2 +np.float64,0xffe83e7312f07ce6,0x7ff0000000000000,2 +np.float64,0xffca53fee834a7fc,0x7ff0000000000000,2 +np.float64,0x800881cbf1710398,0x3ff0000000000000,2 +np.float64,0x80003e6abbe07cd6,0x3ff0000000000000,2 +np.float64,0xbfef6a998afed533,0x3ff859b7852d1b4d,2 +np.float64,0x3fd4eb7577a9d6eb,0x3ff0dcc601261aab,2 +np.float64,0xbfc9c12811338250,0x3ff05331268b05c8,2 +np.float64,0x7fddf84e5e3bf09c,0x7ff0000000000000,2 +np.float64,0xbfd4d6fbbc29adf8,0x3ff0db12db19d187,2 +np.float64,0x80077892bfaef126,0x3ff0000000000000,2 +np.float64,0xffae9d49543d3a90,0x7ff0000000000000,2 +np.float64,0xbfd8bef219317de4,0x3ff136034e5d2f1b,2 +np.float64,0xffe89c74ddb138e9,0x7ff0000000000000,2 +np.float64,0x8003b6bbb7e76d78,0x3ff0000000000000,2 +np.float64,0x315a4e8462b4b,0x3ff0000000000000,2 +np.float64,0x800ee616edddcc2e,0x3ff0000000000000,2 +np.float64,0xdfb27f97bf650,0x3ff0000000000000,2 +np.float64,0x8004723dc328e47c,0x3ff0000000000000,2 +np.float64,0xbfe529500daa52a0,0x3ff3a0b9b33fc84c,2 +np.float64,0xbfe4e46a7ce9c8d5,0x3ff3886ce0f92612,2 +np.float64,0xbf52003680240000,0x3ff00000a203d61a,2 +np.float64,0xffd3400458268008,0x7ff0000000000000,2 +np.float64,0x80076deb444edbd7,0x3ff0000000000000,2 +np.float64,0xa612f6c14c27,0x3ff0000000000000,2 +np.float64,0xbfd41c74c9a838ea,0x3ff0cbe61e16aecf,2 +np.float64,0x43f464a887e8d,0x3ff0000000000000,2 +np.float64,0x800976e748b2edcf,0x3ff0000000000000,2 +np.float64,0xffc79d6ba12f3ad8,0x7ff0000000000000,2 +np.float64,0xffd6dbcb022db796,0x7ff0000000000000,2 +np.float64,0xffd6a9672a2d52ce,0x7ff0000000000000,2 +np.float64,0x3fe95dcfa632bb9f,0x3ff54bbad2ee919e,2 +np.float64,0x3febadd2e1375ba6,0x3ff65e336c47c018,2 +np.float64,0x7fd47c37d828f86f,0x7ff0000000000000,2 +np.float64,0xbfd4ea59e0a9d4b4,0x3ff0dcae6af3e443,2 +np.float64,0x2c112afc58226,0x3ff0000000000000,2 +np.float64,0x8008122bced02458,0x3ff0000000000000,2 +np.float64,0x7fe7105ab3ee20b4,0x7ff0000000000000,2 +np.float64,0x80089634df312c6a,0x3ff0000000000000,2 +np.float64,0x68e9fbc8d1d40,0x3ff0000000000000,2 +np.float64,0xbfec1e1032f83c20,0x3ff69590b9f18ea8,2 +np.float64,0xbfedf181623be303,0x3ff787ef48935dc6,2 +np.float64,0xffe8600457f0c008,0x7ff0000000000000,2 +np.float64,0x7a841ec6f5084,0x3ff0000000000000,2 +np.float64,0x459a572e8b34c,0x3ff0000000000000,2 +np.float64,0x3fe8a232bef14465,0x3ff4fac1780f731e,2 +np.float64,0x3fcb37597d366eb3,0x3ff05cf08ab14ebd,2 +np.float64,0xbfb0261d00204c38,0x3ff00826fb86ca8a,2 +np.float64,0x3fc6e7a6dd2dcf4e,0x3ff041c1222ffa79,2 +np.float64,0xee65dd03dccbc,0x3ff0000000000000,2 +np.float64,0xffe26fdc23e4dfb8,0x7ff0000000000000,2 +np.float64,0x7fe8d6c8cab1ad91,0x7ff0000000000000,2 +np.float64,0xbfeb64bf2676c97e,0x3ff63abb8607828c,2 +np.float64,0x3fd28417b425082f,0x3ff0ac9eb22a732b,2 +np.float64,0xbfd26835b3a4d06c,0x3ff0aa94c48fb6d2,2 +np.float64,0xffec617a01b8c2f3,0x7ff0000000000000,2 +np.float64,0xe1bfff01c3800,0x3ff0000000000000,2 +np.float64,0x3fd4def913a9bdf4,0x3ff0dbbc7271046f,2 +np.float64,0x94f4c17129e98,0x3ff0000000000000,2 +np.float64,0x8009b2eaa33365d6,0x3ff0000000000000,2 +np.float64,0x3fd9633b41b2c678,0x3ff1468388bdfb65,2 +np.float64,0xffe0ae5c80e15cb8,0x7ff0000000000000,2 +np.float64,0x7fdfc35996bf86b2,0x7ff0000000000000,2 +np.float64,0x3fcfc5bdc23f8b7c,0x3ff07ed5caa4545c,2 +np.float64,0xd48b4907a9169,0x3ff0000000000000,2 +np.float64,0xbfe0a2cc52614598,0x3ff2361665895d95,2 +np.float64,0xbfe9068f90720d1f,0x3ff525b82491a1a5,2 +np.float64,0x4238b9208472,0x3ff0000000000000,2 +np.float64,0x800e6b2bf69cd658,0x3ff0000000000000,2 +np.float64,0x7fb638b6ae2c716c,0x7ff0000000000000,2 +np.float64,0x7fe267641764cec7,0x7ff0000000000000,2 +np.float64,0xffc0933d3521267c,0x7ff0000000000000,2 +np.float64,0x7fddfdfb533bfbf6,0x7ff0000000000000,2 +np.float64,0xced2a8e99da55,0x3ff0000000000000,2 +np.float64,0x2a80d5165501b,0x3ff0000000000000,2 +np.float64,0xbfeead2ab63d5a55,0x3ff7eeb5cbcfdcab,2 +np.float64,0x80097f6f92f2fee0,0x3ff0000000000000,2 +np.float64,0x3fee1f29b77c3e54,0x3ff7a0a58c13df62,2 +np.float64,0x3f9d06b8383a0d70,0x3ff001a54a2d8cf8,2 +np.float64,0xbfc8b41d3f31683c,0x3ff04c85379dd6b0,2 +np.float64,0xffd2a04c1e254098,0x7ff0000000000000,2 +np.float64,0xbfb71c01e02e3800,0x3ff010b34220e838,2 +np.float64,0xbfe69249ef6d2494,0x3ff425e48d1e938b,2 +np.float64,0xffefffffffffffff,0x7ff0000000000000,2 +np.float64,0x3feb1d52fbf63aa6,0x3ff618813ae922d7,2 +np.float64,0x7fb8d1a77e31a34e,0x7ff0000000000000,2 +np.float64,0xffc3cfc4ed279f88,0x7ff0000000000000,2 +np.float64,0x2164b9fc42c98,0x3ff0000000000000,2 +np.float64,0x3fbb868cee370d1a,0x3ff017b31b0d4d27,2 +np.float64,0x3fcd6dea583adbd5,0x3ff06cbd16bf44a0,2 +np.float64,0xbfecd041d479a084,0x3ff6efb25f61012d,2 +np.float64,0xbfb0552e6e20aa60,0x3ff00856ca83834a,2 +np.float64,0xe6293cbfcc528,0x3ff0000000000000,2 +np.float64,0x7fba58394034b072,0x7ff0000000000000,2 +np.float64,0x33bc96d467794,0x3ff0000000000000,2 +np.float64,0xffe90ea86bf21d50,0x7ff0000000000000,2 +np.float64,0xbfc626ea6d2c4dd4,0x3ff03d7e01ec3849,2 +np.float64,0x65b56fe4cb6af,0x3ff0000000000000,2 +np.float64,0x3fea409fb7f4813f,0x3ff5b171deab0ebd,2 +np.float64,0x3fe849c1df709384,0x3ff4d59063ff98c4,2 +np.float64,0x169073082d20f,0x3ff0000000000000,2 +np.float64,0xcc8b6add9916e,0x3ff0000000000000,2 +np.float64,0xbfef3d78d5fe7af2,0x3ff83fecc26abeea,2 +np.float64,0x3fe8c65a4a718cb4,0x3ff50a23bfeac7df,2 +np.float64,0x3fde9fa5c8bd3f4c,0x3ff1ddeb12b9d623,2 +np.float64,0xffe2af536da55ea6,0x7ff0000000000000,2 +np.float64,0x800186d0b0c30da2,0x3ff0000000000000,2 +np.float64,0x3fe9ba3c1d737478,0x3ff574ab2bf3a560,2 +np.float64,0xbfe1489c46a29138,0x3ff2641d36b30e21,2 +np.float64,0xbfe4b6b7c0e96d70,0x3ff37880ac8b0540,2 +np.float64,0x800e66ad82fccd5b,0x3ff0000000000000,2 +np.float64,0x7ff0000000000000,0x7ff0000000000000,2 +np.float64,0x7febb0fd477761fa,0x7ff0000000000000,2 +np.float64,0xbfdc433f2eb8867e,0x3ff195ec2a6cce27,2 +np.float64,0x3fe12c5a172258b4,0x3ff25c225b8a34bb,2 +np.float64,0xbfef6f116c3ede23,0x3ff85c47eaed49a0,2 +np.float64,0x800af6f60f35edec,0x3ff0000000000000,2 +np.float64,0xffe567999a2acf32,0x7ff0000000000000,2 +np.float64,0xbfc5ac5ae72b58b4,0x3ff03adb50ec04f3,2 +np.float64,0x3fea1b57e23436b0,0x3ff5a06f98541767,2 +np.float64,0x7fcc3e36fb387c6d,0x7ff0000000000000,2 +np.float64,0x8000c8dc698191ba,0x3ff0000000000000,2 +np.float64,0x3fee5085ed7ca10c,0x3ff7bb92f61245b8,2 +np.float64,0x7fbb9f803a373eff,0x7ff0000000000000,2 +np.float64,0xbfe1e5e806e3cbd0,0x3ff2918f2d773007,2 +np.float64,0x8008f8c3f3b1f188,0x3ff0000000000000,2 +np.float64,0x7fe53df515ea7be9,0x7ff0000000000000,2 +np.float64,0x7fdbb87fb3b770fe,0x7ff0000000000000,2 +np.float64,0x3fefcc0f50ff981f,0x3ff89210a6a04e6b,2 +np.float64,0x3fe33f87d0267f10,0x3ff2fb989ea4f2bc,2 +np.float64,0x1173992022e8,0x3ff0000000000000,2 +np.float64,0x3fef534632bea68c,0x3ff84c5ca9713ff9,2 +np.float64,0x3fc5991d552b3238,0x3ff03a72bfdb6e5f,2 +np.float64,0x3fdad90dc1b5b21c,0x3ff16db868180034,2 +np.float64,0xffe20b8078e41700,0x7ff0000000000000,2 +np.float64,0x7fdf409a82be8134,0x7ff0000000000000,2 +np.float64,0x3fccb7e691396fcd,0x3ff06786b6ccdbcb,2 +np.float64,0xffe416e0b7282dc1,0x7ff0000000000000,2 +np.float64,0xffe3a8a981275152,0x7ff0000000000000,2 +np.float64,0x3fd9c8bd31b3917c,0x3ff150ee6f5f692f,2 +np.float64,0xffeab6fef6356dfd,0x7ff0000000000000,2 +np.float64,0x3fe9c5e3faf38bc8,0x3ff579e18c9bd548,2 +np.float64,0x800b173e44762e7d,0x3ff0000000000000,2 +np.float64,0xffe2719db764e33b,0x7ff0000000000000,2 +np.float64,0x3fd1fcf31223f9e6,0x3ff0a2da7ad99856,2 +np.float64,0x80082c4afcd05896,0x3ff0000000000000,2 +np.float64,0xa56e5e4b4adcc,0x3ff0000000000000,2 +np.float64,0xffbbbddab2377bb8,0x7ff0000000000000,2 +np.float64,0x3b3927c076726,0x3ff0000000000000,2 +np.float64,0x3fec03fd58f807fb,0x3ff6889b8a774728,2 +np.float64,0xbfaa891fb4351240,0x3ff00580987bd914,2 +np.float64,0x7fb4800c4a290018,0x7ff0000000000000,2 +np.float64,0xffbb5d2b6036ba58,0x7ff0000000000000,2 +np.float64,0x7fd6608076acc100,0x7ff0000000000000,2 +np.float64,0x31267e4c624d1,0x3ff0000000000000,2 +np.float64,0x33272266664e5,0x3ff0000000000000,2 +np.float64,0x47bb37f28f768,0x3ff0000000000000,2 +np.float64,0x3fe134bb4ee26977,0x3ff25e7ea647a928,2 +np.float64,0xbfe2b5f42ba56be8,0x3ff2d05cbdc7344b,2 +np.float64,0xbfe0e013fd61c028,0x3ff246dfce572914,2 +np.float64,0x7fecedcda4f9db9a,0x7ff0000000000000,2 +np.float64,0x8001816c2da302d9,0x3ff0000000000000,2 +np.float64,0xffced8b65b3db16c,0x7ff0000000000000,2 +np.float64,0xffdc1d4a0b383a94,0x7ff0000000000000,2 +np.float64,0x7fe94e7339f29ce5,0x7ff0000000000000,2 +np.float64,0x33fb846667f71,0x3ff0000000000000,2 +np.float64,0x800a1380e9542702,0x3ff0000000000000,2 +np.float64,0x800b74eaa776e9d6,0x3ff0000000000000,2 +np.float64,0x5681784aad030,0x3ff0000000000000,2 +np.float64,0xbfee0eb7917c1d6f,0x3ff797b949f7f6b4,2 +np.float64,0xffe4ec5fd2a9d8bf,0x7ff0000000000000,2 +np.float64,0xbfcd7401dd3ae804,0x3ff06cea52c792c0,2 +np.float64,0x800587563beb0ead,0x3ff0000000000000,2 +np.float64,0x3fc15c6f3322b8de,0x3ff025bbd030166d,2 +np.float64,0x7feb6b4caf76d698,0x7ff0000000000000,2 +np.float64,0x7fe136ef82a26dde,0x7ff0000000000000,2 +np.float64,0xf592dac3eb25c,0x3ff0000000000000,2 +np.float64,0x7fd300baf6a60175,0x7ff0000000000000,2 +np.float64,0x7fc880de9e3101bc,0x7ff0000000000000,2 +np.float64,0x7fe7a1aa5caf4354,0x7ff0000000000000,2 +np.float64,0x2f9b8e0e5f373,0x3ff0000000000000,2 +np.float64,0xffcc9071993920e4,0x7ff0000000000000,2 +np.float64,0x8009e151b313c2a4,0x3ff0000000000000,2 +np.float64,0xbfd46e2d18a8dc5a,0x3ff0d27a7b37c1ae,2 +np.float64,0x3fe65c7961acb8f3,0x3ff4116946062a4c,2 +np.float64,0x7fd31b371626366d,0x7ff0000000000000,2 +np.float64,0x98dc924d31b93,0x3ff0000000000000,2 +np.float64,0x268bef364d17f,0x3ff0000000000000,2 +np.float64,0x7fd883ba56310774,0x7ff0000000000000,2 +np.float64,0x3fc53f01a32a7e03,0x3ff0388dea9cd63e,2 +np.float64,0xffe1ea8c0563d518,0x7ff0000000000000,2 +np.float64,0x3fd0bf0e63a17e1d,0x3ff08d0577f5ffa6,2 +np.float64,0x7fef42418f7e8482,0x7ff0000000000000,2 +np.float64,0x8000bccd38c1799b,0x3ff0000000000000,2 +np.float64,0xbfe6c48766ed890f,0x3ff43936fa4048c8,2 +np.float64,0xbfb2a38f3a254720,0x3ff00adc7f7b2822,2 +np.float64,0x3fd5262b2eaa4c56,0x3ff0e1af492c08f5,2 +np.float64,0x80065b4691ecb68e,0x3ff0000000000000,2 +np.float64,0xfb6b9e9ff6d74,0x3ff0000000000000,2 +np.float64,0x8006c71e6ecd8e3e,0x3ff0000000000000,2 +np.float64,0x3fd0a3e43ca147c8,0x3ff08b3ad7b42485,2 +np.float64,0xbfc82d8607305b0c,0x3ff04949d6733ef6,2 +np.float64,0xde048c61bc092,0x3ff0000000000000,2 +np.float64,0xffcf73e0fa3ee7c0,0x7ff0000000000000,2 +np.float64,0xbfe8639d7830c73b,0x3ff4e05f97948376,2 +np.float64,0x8010000000000000,0x3ff0000000000000,2 +np.float64,0x67f01a2acfe04,0x3ff0000000000000,2 +np.float64,0x3fe222e803e445d0,0x3ff2a3a75e5f29d8,2 +np.float64,0xffef84c6387f098b,0x7ff0000000000000,2 +np.float64,0x3fe5969c1e6b2d38,0x3ff3c80130462bb2,2 +np.float64,0x8009f56953d3ead3,0x3ff0000000000000,2 +np.float64,0x3fe05c9b6360b937,0x3ff2232e1cba5617,2 +np.float64,0x3fd8888d63b1111b,0x3ff130a5b788d52f,2 +np.float64,0xffe3a9e6f26753ce,0x7ff0000000000000,2 +np.float64,0x800e2aaa287c5554,0x3ff0000000000000,2 +np.float64,0x3fea8d6c82351ad9,0x3ff5d4d8cde9a11d,2 +np.float64,0x7feef700723dee00,0x7ff0000000000000,2 +np.float64,0x3fa5cb77242b96e0,0x3ff003b62b3e50f1,2 +np.float64,0x7fb68f0a862d1e14,0x7ff0000000000000,2 +np.float64,0x7fb97ee83432fdcf,0x7ff0000000000000,2 +np.float64,0x7fd74a78632e94f0,0x7ff0000000000000,2 +np.float64,0x7fcfe577713fcaee,0x7ff0000000000000,2 +np.float64,0xffe192ee5ea325dc,0x7ff0000000000000,2 +np.float64,0x477d6ae48efae,0x3ff0000000000000,2 +np.float64,0xffe34d5237669aa4,0x7ff0000000000000,2 +np.float64,0x7fe3ce8395a79d06,0x7ff0000000000000,2 +np.float64,0x80019c01ffa33805,0x3ff0000000000000,2 +np.float64,0x74b5b56ce96b7,0x3ff0000000000000,2 +np.float64,0x7fe05ecdeda0bd9b,0x7ff0000000000000,2 +np.float64,0xffe9693eb232d27d,0x7ff0000000000000,2 +np.float64,0xffd2be2c7da57c58,0x7ff0000000000000,2 +np.float64,0x800dbd5cbc1b7aba,0x3ff0000000000000,2 +np.float64,0xbfa36105d426c210,0x3ff002ef2e3a87f7,2 +np.float64,0x800b2d69fb765ad4,0x3ff0000000000000,2 +np.float64,0xbfdb81c9a9370394,0x3ff1802d409cbf7a,2 +np.float64,0x7fd481d014a9039f,0x7ff0000000000000,2 +np.float64,0xffe66c3c1fecd878,0x7ff0000000000000,2 +np.float64,0x3fc55865192ab0c8,0x3ff03915b51e8839,2 +np.float64,0xd6a78987ad4f1,0x3ff0000000000000,2 +np.float64,0x800c6cc80d58d990,0x3ff0000000000000,2 +np.float64,0x979435a12f29,0x3ff0000000000000,2 +np.float64,0xbfbd971e7a3b2e40,0x3ff01b647e45f5a6,2 +np.float64,0x80067565bfeceacc,0x3ff0000000000000,2 +np.float64,0x8001ad689ce35ad2,0x3ff0000000000000,2 +np.float64,0x7fa43253dc2864a7,0x7ff0000000000000,2 +np.float64,0xbfe3dda307e7bb46,0x3ff32ef99a2efe1d,2 +np.float64,0x3fe5d7b395ebaf68,0x3ff3dfd33cdc8ef4,2 +np.float64,0xd94cc9c3b2999,0x3ff0000000000000,2 +np.float64,0x3fee5a513fbcb4a2,0x3ff7c0f17b876ce5,2 +np.float64,0xffe27761fa64eec4,0x7ff0000000000000,2 +np.float64,0x3feb788119b6f102,0x3ff64446f67f4efa,2 +np.float64,0xbfed6e10dffadc22,0x3ff741d5ef610ca0,2 +np.float64,0x7fe73cf98b2e79f2,0x7ff0000000000000,2 +np.float64,0x7847d09af08fb,0x3ff0000000000000,2 +np.float64,0x29ded2da53bdb,0x3ff0000000000000,2 +np.float64,0xbfe51c1ec1aa383e,0x3ff39c0b7cf832e2,2 +np.float64,0xbfeafd5e65f5fabd,0x3ff609548a787f57,2 +np.float64,0x3fd872a26fb0e545,0x3ff12e7fbd95505c,2 +np.float64,0x7fed6b7c1b7ad6f7,0x7ff0000000000000,2 +np.float64,0xffe7ba9ec16f753d,0x7ff0000000000000,2 +np.float64,0x7f89b322f0336645,0x7ff0000000000000,2 +np.float64,0xbfad1677383a2cf0,0x3ff0069ca67e7baa,2 +np.float64,0x3fe0906d04a120da,0x3ff2311b04b7bfef,2 +np.float64,0xffe4b3c9d4296793,0x7ff0000000000000,2 +np.float64,0xbfe476bb0ce8ed76,0x3ff36277d2921a74,2 +np.float64,0x7fc35655cf26acab,0x7ff0000000000000,2 +np.float64,0x7fe9980f0373301d,0x7ff0000000000000,2 +np.float64,0x9e6e04cb3cdc1,0x3ff0000000000000,2 +np.float64,0x800b89e0afb713c2,0x3ff0000000000000,2 +np.float64,0x800bd951a3f7b2a4,0x3ff0000000000000,2 +np.float64,0x29644a9e52c8a,0x3ff0000000000000,2 +np.float64,0x3fe1be2843637c51,0x3ff285e90d8387e4,2 +np.float64,0x7fa233cce4246799,0x7ff0000000000000,2 +np.float64,0xbfcfb7bc2d3f6f78,0x3ff07e657de3e2ed,2 +np.float64,0xffd7c953e7af92a8,0x7ff0000000000000,2 +np.float64,0xbfc5bbaf772b7760,0x3ff03b2ee4febb1e,2 +np.float64,0x8007b7315a6f6e63,0x3ff0000000000000,2 +np.float64,0xbfe906d902320db2,0x3ff525d7e16acfe0,2 +np.float64,0x3fde33d8553c67b1,0x3ff1d09faa19aa53,2 +np.float64,0x61fe76a0c3fcf,0x3ff0000000000000,2 +np.float64,0xa75e355b4ebc7,0x3ff0000000000000,2 +np.float64,0x3fc9e6d86033cdb1,0x3ff05426299c7064,2 +np.float64,0x7fd83f489eb07e90,0x7ff0000000000000,2 +np.float64,0x8000000000000001,0x3ff0000000000000,2 +np.float64,0x80014434ae62886a,0x3ff0000000000000,2 +np.float64,0xbfe21af9686435f3,0x3ff2a149338bdefe,2 +np.float64,0x9354e6cd26a9d,0x3ff0000000000000,2 +np.float64,0xb42b95f768573,0x3ff0000000000000,2 +np.float64,0xbfecb4481bb96890,0x3ff6e15d269dd651,2 +np.float64,0x3f97842ae82f0840,0x3ff0011485156f28,2 +np.float64,0xffdef63d90bdec7c,0x7ff0000000000000,2 +np.float64,0x7fe511a8d36a2351,0x7ff0000000000000,2 +np.float64,0xbf8cb638a0396c80,0x3ff000670c318fb6,2 +np.float64,0x3fe467e1f668cfc4,0x3ff35d65f93ccac6,2 +np.float64,0xbfce7d88f03cfb10,0x3ff074c22475fe5b,2 +np.float64,0x6d0a4994da14a,0x3ff0000000000000,2 +np.float64,0xbfb3072580260e48,0x3ff00b51d3913e9f,2 +np.float64,0x8008fcde36b1f9bd,0x3ff0000000000000,2 +np.float64,0x3fd984df66b309c0,0x3ff149f29125eca4,2 +np.float64,0xffee2a10fe7c5421,0x7ff0000000000000,2 +np.float64,0x80039168ace722d2,0x3ff0000000000000,2 +np.float64,0xffda604379b4c086,0x7ff0000000000000,2 +np.float64,0xffdc6a405bb8d480,0x7ff0000000000000,2 +np.float64,0x3fe62888b26c5111,0x3ff3fdda754c4372,2 +np.float64,0x8008b452cb5168a6,0x3ff0000000000000,2 +np.float64,0x6165d540c2cbb,0x3ff0000000000000,2 +np.float64,0xbfee0c04d17c180a,0x3ff796431c64bcbe,2 +np.float64,0x800609b8448c1371,0x3ff0000000000000,2 +np.float64,0x800fc3fca59f87f9,0x3ff0000000000000,2 +np.float64,0x77f64848efeca,0x3ff0000000000000,2 +np.float64,0x8007cf522d8f9ea5,0x3ff0000000000000,2 +np.float64,0xbfe9fb0b93f3f617,0x3ff591cb0052e22c,2 +np.float64,0x7fd569d5f0aad3ab,0x7ff0000000000000,2 +np.float64,0x7fe5cf489d6b9e90,0x7ff0000000000000,2 +np.float64,0x7fd6e193e92dc327,0x7ff0000000000000,2 +np.float64,0xf78988a5ef131,0x3ff0000000000000,2 +np.float64,0x3fe8f97562b1f2eb,0x3ff5201080fbc12d,2 +np.float64,0x7febfd69d7b7fad3,0x7ff0000000000000,2 +np.float64,0xffc07b5c1720f6b8,0x7ff0000000000000,2 +np.float64,0xbfd966926832cd24,0x3ff146da9adf492e,2 +np.float64,0x7fef5bd9edfeb7b3,0x7ff0000000000000,2 +np.float64,0xbfd2afbc96255f7a,0x3ff0afd601febf44,2 +np.float64,0x7fdd4ea6293a9d4b,0x7ff0000000000000,2 +np.float64,0xbfe8a1e916b143d2,0x3ff4faa23c2793e5,2 +np.float64,0x800188fcd8c311fa,0x3ff0000000000000,2 +np.float64,0xbfe30803f1661008,0x3ff2e9fc729baaee,2 +np.float64,0x7fefffffffffffff,0x7ff0000000000000,2 +np.float64,0x3fd287bec3250f7e,0x3ff0ace34d3102f6,2 +np.float64,0x1f0ee9443e1de,0x3ff0000000000000,2 +np.float64,0xbfd92f73da325ee8,0x3ff14143e4fa2c5a,2 +np.float64,0x3fed7c9bdffaf938,0x3ff74984168734d3,2 +np.float64,0x8002c4d1696589a4,0x3ff0000000000000,2 +np.float64,0xfe03011bfc060,0x3ff0000000000000,2 +np.float64,0x7f7a391e6034723c,0x7ff0000000000000,2 +np.float64,0xffd6fd46f82dfa8e,0x7ff0000000000000,2 +np.float64,0xbfd7520a742ea414,0x3ff112f1ba5d4f91,2 +np.float64,0x8009389d8812713b,0x3ff0000000000000,2 +np.float64,0x7fefb846aaff708c,0x7ff0000000000000,2 +np.float64,0x3fd98a0983331413,0x3ff14a79efb8adbf,2 +np.float64,0xbfd897158db12e2c,0x3ff132137902cf3e,2 +np.float64,0xffc4048d5928091c,0x7ff0000000000000,2 +np.float64,0x80036ae46046d5ca,0x3ff0000000000000,2 +np.float64,0x7faba7ed3c374fd9,0x7ff0000000000000,2 +np.float64,0xbfec4265e1f884cc,0x3ff6a7b8602422c9,2 +np.float64,0xaa195e0b5432c,0x3ff0000000000000,2 +np.float64,0x3feac15d317582ba,0x3ff5ed115758145f,2 +np.float64,0x6c13a5bcd8275,0x3ff0000000000000,2 +np.float64,0xbfed20b8883a4171,0x3ff7194dbd0dc988,2 +np.float64,0x800cde65c899bccc,0x3ff0000000000000,2 +np.float64,0x7c72912af8e53,0x3ff0000000000000,2 +np.float64,0x3fe49d2bb4e93a57,0x3ff36fab3aba15d4,2 +np.float64,0xbfd598fa02ab31f4,0x3ff0eb72fc472025,2 +np.float64,0x8007a191712f4324,0x3ff0000000000000,2 +np.float64,0xbfdeb14872bd6290,0x3ff1e01ca83f35fd,2 +np.float64,0xbfe1da46b3e3b48e,0x3ff28e23ad2f5615,2 +np.float64,0x800a2f348e745e69,0x3ff0000000000000,2 +np.float64,0xbfee66928afccd25,0x3ff7c7ac7dbb3273,2 +np.float64,0xffd78a0a2b2f1414,0x7ff0000000000000,2 +np.float64,0x7fc5fa80b82bf500,0x7ff0000000000000,2 +np.float64,0x800e6d7260dcdae5,0x3ff0000000000000,2 +np.float64,0xbfd6cff2aaad9fe6,0x3ff106f78ee61642,2 +np.float64,0x7fe1041d1d220839,0x7ff0000000000000,2 +np.float64,0xbfdf75586cbeeab0,0x3ff1f8dbaa7e57f0,2 +np.float64,0xffdcaae410b955c8,0x7ff0000000000000,2 +np.float64,0x800fe5e0d1ffcbc2,0x3ff0000000000000,2 +np.float64,0x800d7999527af333,0x3ff0000000000000,2 +np.float64,0xbfe62c233bac5846,0x3ff3ff34220a204c,2 +np.float64,0x7fe99bbff8f3377f,0x7ff0000000000000,2 +np.float64,0x7feeaf471d3d5e8d,0x7ff0000000000000,2 +np.float64,0xd5904ff5ab20a,0x3ff0000000000000,2 +np.float64,0x3fd07aae3320f55c,0x3ff08888c227c968,2 +np.float64,0x7fea82b8dff50571,0x7ff0000000000000,2 +np.float64,0xffef2db9057e5b71,0x7ff0000000000000,2 +np.float64,0xbfe2077fef640f00,0x3ff29b7dd0d39d36,2 +np.float64,0xbfe09a4d7c61349b,0x3ff233c7e88881f4,2 +np.float64,0x3fda50c4cbb4a188,0x3ff15f28a71deee7,2 +np.float64,0x7fe7d9ee6b2fb3dc,0x7ff0000000000000,2 +np.float64,0x3febbf6faeb77edf,0x3ff666d13682ea93,2 +np.float64,0xc401a32988035,0x3ff0000000000000,2 +np.float64,0xbfeab30aa8f56615,0x3ff5e65dcc6603f8,2 +np.float64,0x92c8cea32591a,0x3ff0000000000000,2 +np.float64,0xbff0000000000000,0x3ff8b07551d9f550,2 +np.float64,0xbfbddfb4dc3bbf68,0x3ff01bebaec38faa,2 +np.float64,0xbfd8de3e2a31bc7c,0x3ff1391f4830d20b,2 +np.float64,0xffc83a8f8a307520,0x7ff0000000000000,2 +np.float64,0x3fee026ef53c04de,0x3ff7911337085827,2 +np.float64,0x7fbaf380b235e700,0x7ff0000000000000,2 +np.float64,0xffe5b89fa62b713f,0x7ff0000000000000,2 +np.float64,0xbfdc1ff54ab83fea,0x3ff191e8c0b60bb2,2 +np.float64,0x6ae3534cd5c6b,0x3ff0000000000000,2 +np.float64,0xbfea87e558750fcb,0x3ff5d24846013794,2 +np.float64,0xffe0f467bee1e8cf,0x7ff0000000000000,2 +np.float64,0x7fee3b0dc7bc761b,0x7ff0000000000000,2 +np.float64,0x3fed87521afb0ea4,0x3ff74f2f5cd36a5c,2 +np.float64,0x7b3c9882f6794,0x3ff0000000000000,2 +np.float64,0x7fdd1a62243a34c3,0x7ff0000000000000,2 +np.float64,0x800f1dc88d3e3b91,0x3ff0000000000000,2 +np.float64,0x7fc3213cfa264279,0x7ff0000000000000,2 +np.float64,0x3fe40e0f3d681c1e,0x3ff33f135e9d5ded,2 +np.float64,0x7febf14e51f7e29c,0x7ff0000000000000,2 +np.float64,0xffe96c630c72d8c5,0x7ff0000000000000,2 +np.float64,0x7fdd82fbe7bb05f7,0x7ff0000000000000,2 +np.float64,0xbf9a6a0b1034d420,0x3ff0015ce009f7d8,2 +np.float64,0xbfceb4f8153d69f0,0x3ff0766e3ecc77df,2 +np.float64,0x3fd9de31e633bc64,0x3ff15327b794a16e,2 +np.float64,0x3faa902a30352054,0x3ff00583848d1969,2 +np.float64,0x0,0x3ff0000000000000,2 +np.float64,0x3fbe3459c43c68b4,0x3ff01c8af6710ef6,2 +np.float64,0xbfa8df010031be00,0x3ff004d5632dc9f5,2 +np.float64,0x7fbcf6cf2a39ed9d,0x7ff0000000000000,2 +np.float64,0xffe4236202a846c4,0x7ff0000000000000,2 +np.float64,0x3fd35ed52e26bdaa,0x3ff0bd0b231f11f7,2 +np.float64,0x7fe7a2df532f45be,0x7ff0000000000000,2 +np.float64,0xffe32f8315665f06,0x7ff0000000000000,2 +np.float64,0x7fe1a69f03e34d3d,0x7ff0000000000000,2 +np.float64,0x7fa5542b742aa856,0x7ff0000000000000,2 +np.float64,0x3fe84e9f8ef09d3f,0x3ff4d79816359765,2 +np.float64,0x29076fe6520ef,0x3ff0000000000000,2 +np.float64,0xffd70894f7ae112a,0x7ff0000000000000,2 +np.float64,0x800188edcbe311dc,0x3ff0000000000000,2 +np.float64,0x3fe2c7acda258f5a,0x3ff2d5dad4617703,2 +np.float64,0x3f775d41a02ebb00,0x3ff000110f212445,2 +np.float64,0x7fe8a084d1714109,0x7ff0000000000000,2 +np.float64,0x3fe31562d8a62ac6,0x3ff2ee35055741cd,2 +np.float64,0xbfd195d4d1a32baa,0x3ff09b98a50c151b,2 +np.float64,0xffaae9ff0c35d400,0x7ff0000000000000,2 +np.float64,0xff819866502330c0,0x7ff0000000000000,2 +np.float64,0x7fddc64815bb8c8f,0x7ff0000000000000,2 +np.float64,0xbfd442b428288568,0x3ff0cef70aa73ae6,2 +np.float64,0x8002e7625aa5cec5,0x3ff0000000000000,2 +np.float64,0x7fe8d4f70e71a9ed,0x7ff0000000000000,2 +np.float64,0xbfc3bd015f277a04,0x3ff030cbf16f29d9,2 +np.float64,0x3fd315d5baa62bab,0x3ff0b77a551a5335,2 +np.float64,0x7fa638b4642c7168,0x7ff0000000000000,2 +np.float64,0x3fdea8b795bd516f,0x3ff1df0bb70cdb79,2 +np.float64,0xbfd78754762f0ea8,0x3ff117ee0f29abed,2 +np.float64,0x8009f6a37633ed47,0x3ff0000000000000,2 +np.float64,0x3fea1daf75343b5f,0x3ff5a1804789bf13,2 +np.float64,0x3fd044b6c0a0896e,0x3ff0850b7297d02f,2 +np.float64,0x8003547a9c86a8f6,0x3ff0000000000000,2 +np.float64,0x3fa6c2cd782d859b,0x3ff0040c4ac8f44a,2 +np.float64,0x3fe225baaae44b76,0x3ff2a47f5e1f5e85,2 +np.float64,0x8000000000000000,0x3ff0000000000000,2 +np.float64,0x3fcb53da8736a7b8,0x3ff05db45af470ac,2 +np.float64,0x80079f8f140f3f1f,0x3ff0000000000000,2 +np.float64,0xbfcd1d7e2b3a3afc,0x3ff06a6b6845d05f,2 +np.float64,0x96df93672dbf3,0x3ff0000000000000,2 +np.float64,0xdef86e43bdf0e,0x3ff0000000000000,2 +np.float64,0xbfec05a09db80b41,0x3ff6896b768eea08,2 +np.float64,0x7fe3ff91d267ff23,0x7ff0000000000000,2 +np.float64,0xffea3eaa07347d53,0x7ff0000000000000,2 +np.float64,0xbfebde1cc1f7bc3a,0x3ff675e34ac2afc2,2 +np.float64,0x629bcde8c537a,0x3ff0000000000000,2 +np.float64,0xbfdde4fcff3bc9fa,0x3ff1c7061d21f0fe,2 +np.float64,0x3fee60fd003cc1fa,0x3ff7c49af3878a51,2 +np.float64,0x3fe5c92ac32b9256,0x3ff3da7a7929588b,2 +np.float64,0xbfe249c78f64938f,0x3ff2af52a06f1a50,2 +np.float64,0xbfc6de9dbe2dbd3c,0x3ff0418d284ee29f,2 +np.float64,0xffc8ef094631de14,0x7ff0000000000000,2 +np.float64,0x3fdef05f423de0bf,0x3ff1e800caba8ab5,2 +np.float64,0xffc1090731221210,0x7ff0000000000000,2 +np.float64,0xbfedec9b5fbbd937,0x3ff7854b6792a24a,2 +np.float64,0xbfb873507630e6a0,0x3ff012b23b3b7a67,2 +np.float64,0xbfe3cd6692679acd,0x3ff3299d6936ec4b,2 +np.float64,0xbfb107c890220f90,0x3ff0091122162472,2 +np.float64,0xbfe4e6ee48e9cddc,0x3ff3894e5a5e70a6,2 +np.float64,0xffe6fa3413edf468,0x7ff0000000000000,2 +np.float64,0x3fe2faf79b65f5ef,0x3ff2e5e11fae8b54,2 +np.float64,0xbfdfeb8df9bfd71c,0x3ff208189691b15f,2 +np.float64,0x75d2d03ceba5b,0x3ff0000000000000,2 +np.float64,0x3feb48c182b69183,0x3ff62d4462eba6cb,2 +np.float64,0xffcda9f7ff3b53f0,0x7ff0000000000000,2 +np.float64,0x7fcafbdcbd35f7b8,0x7ff0000000000000,2 +np.float64,0xbfd1895523a312aa,0x3ff09aba642a78d9,2 +np.float64,0x3fe3129c3f662538,0x3ff2ed546bbfafcf,2 +np.float64,0x3fb444dee02889be,0x3ff00cd86273b964,2 +np.float64,0xbf73b32d7ee77,0x3ff0000000000000,2 +np.float64,0x3fae19904c3c3321,0x3ff00714865c498a,2 +np.float64,0x7fefbfaef5bf7f5d,0x7ff0000000000000,2 +np.float64,0x8000dc3816e1b871,0x3ff0000000000000,2 +np.float64,0x8003f957ba47f2b0,0x3ff0000000000000,2 +np.float64,0xbfe3563c7ea6ac79,0x3ff302dcebc92856,2 +np.float64,0xbfdc80fbae3901f8,0x3ff19cfe73e58092,2 +np.float64,0x8009223b04524476,0x3ff0000000000000,2 +np.float64,0x3fd95f431c32be86,0x3ff1461c21cb03f0,2 +np.float64,0x7ff4000000000000,0x7ffc000000000000,2 +np.float64,0xbfe7c12ed3ef825e,0x3ff49d59c265efcd,2 +np.float64,0x10000000000000,0x3ff0000000000000,2 +np.float64,0x7fc5e2632f2bc4c5,0x7ff0000000000000,2 +np.float64,0xffd8f6b4c7b1ed6a,0x7ff0000000000000,2 +np.float64,0x80034b93d4069728,0x3ff0000000000000,2 +np.float64,0xffdf5d4c1dbeba98,0x7ff0000000000000,2 +np.float64,0x800bc63d70178c7b,0x3ff0000000000000,2 +np.float64,0xbfeba31ea0f7463d,0x3ff658fa27073d2b,2 +np.float64,0xbfeebeede97d7ddc,0x3ff7f89a8e80dec4,2 +np.float64,0x7feb0f1f91361e3e,0x7ff0000000000000,2 +np.float64,0xffec3158d0b862b1,0x7ff0000000000000,2 +np.float64,0x3fde51cbfbbca398,0x3ff1d44c2ff15b3d,2 +np.float64,0xd58fb2b3ab1f7,0x3ff0000000000000,2 +np.float64,0x80028b9e32e5173d,0x3ff0000000000000,2 +np.float64,0x7fea77a56c74ef4a,0x7ff0000000000000,2 +np.float64,0x3fdaabbd4a35577b,0x3ff168d82edf2fe0,2 +np.float64,0xbfe69c39cc2d3874,0x3ff429b2f4cdb362,2 +np.float64,0x3b78f5d876f20,0x3ff0000000000000,2 +np.float64,0x7fa47d116428fa22,0x7ff0000000000000,2 +np.float64,0xbfe4118b0ce82316,0x3ff3403d989f780f,2 +np.float64,0x800482e793c905d0,0x3ff0000000000000,2 +np.float64,0xbfe48e5728e91cae,0x3ff36a9020bf9d20,2 +np.float64,0x7fe078ba8860f174,0x7ff0000000000000,2 +np.float64,0x3fd80843e5b01088,0x3ff1242f401e67da,2 +np.float64,0x3feb1f6965f63ed3,0x3ff6197fc590e143,2 +np.float64,0xffa41946d8283290,0x7ff0000000000000,2 +np.float64,0xffe30de129661bc2,0x7ff0000000000000,2 +np.float64,0x3fec9c8e1ab9391c,0x3ff6d542ea2f49b4,2 +np.float64,0x3fdc3e4490387c89,0x3ff1955ae18cac37,2 +np.float64,0xffef49d9c77e93b3,0x7ff0000000000000,2 +np.float64,0xfff0000000000000,0x7ff0000000000000,2 +np.float64,0x3fe0442455608849,0x3ff21cab90067d5c,2 +np.float64,0xbfed86aebd3b0d5e,0x3ff74ed8d4b75f50,2 +np.float64,0xffe4600d2b28c01a,0x7ff0000000000000,2 +np.float64,0x7fc1e8ccff23d199,0x7ff0000000000000,2 +np.float64,0x8008d49b0091a936,0x3ff0000000000000,2 +np.float64,0xbfe4139df028273c,0x3ff340ef3c86227c,2 +np.float64,0xbfe9ab4542b3568a,0x3ff56dfe32061247,2 +np.float64,0xbfd76dd365aedba6,0x3ff11589bab5fe71,2 +np.float64,0x3fd42cf829a859f0,0x3ff0cd3844bb0e11,2 +np.float64,0x7fd077cf2e20ef9d,0x7ff0000000000000,2 +np.float64,0x3fd7505760aea0b0,0x3ff112c937b3f088,2 +np.float64,0x1f93341a3f267,0x3ff0000000000000,2 +np.float64,0x7fe3c3c1b0678782,0x7ff0000000000000,2 +np.float64,0x800f85cec97f0b9e,0x3ff0000000000000,2 +np.float64,0xd93ab121b2756,0x3ff0000000000000,2 +np.float64,0xbfef8066fd7f00ce,0x3ff8663ed7d15189,2 +np.float64,0xffe31dd4af663ba9,0x7ff0000000000000,2 +np.float64,0xbfd7ff05a6affe0c,0x3ff1234c09bb686d,2 +np.float64,0xbfe718c31fee3186,0x3ff45a0c2d0ef7b0,2 +np.float64,0x800484bf33e9097f,0x3ff0000000000000,2 +np.float64,0xffd409dad02813b6,0x7ff0000000000000,2 +np.float64,0x3fe59679896b2cf4,0x3ff3c7f49e4fbbd3,2 +np.float64,0xbfd830c54d30618a,0x3ff1281729861390,2 +np.float64,0x1d4fc81c3a9fa,0x3ff0000000000000,2 +np.float64,0x3fd334e4272669c8,0x3ff0b9d5d82894f0,2 +np.float64,0xffc827e65c304fcc,0x7ff0000000000000,2 +np.float64,0xffe2d1814aa5a302,0x7ff0000000000000,2 +np.float64,0xffd7b5b8d32f6b72,0x7ff0000000000000,2 +np.float64,0xbfdbc9f077b793e0,0x3ff18836b9106ad0,2 +np.float64,0x7fc724c2082e4983,0x7ff0000000000000,2 +np.float64,0x3fa39ed72c273da0,0x3ff00302051ce17e,2 +np.float64,0xbfe3c4c209678984,0x3ff326c4fd16b5cd,2 +np.float64,0x7fe91f6d00f23ed9,0x7ff0000000000000,2 +np.float64,0x8004ee93fea9dd29,0x3ff0000000000000,2 +np.float64,0xbfe7c32d0eaf865a,0x3ff49e290ed2ca0e,2 +np.float64,0x800ea996b29d532d,0x3ff0000000000000,2 +np.float64,0x2df9ec1c5bf3e,0x3ff0000000000000,2 +np.float64,0xabb175df5762f,0x3ff0000000000000,2 +np.float64,0xffe3fc9c8e27f938,0x7ff0000000000000,2 +np.float64,0x7fb358a62826b14b,0x7ff0000000000000,2 +np.float64,0x800aedcccaf5db9a,0x3ff0000000000000,2 +np.float64,0xffca530c5234a618,0x7ff0000000000000,2 +np.float64,0x40f91e9681f24,0x3ff0000000000000,2 +np.float64,0x80098f4572f31e8b,0x3ff0000000000000,2 +np.float64,0xbfdc58c21fb8b184,0x3ff1986115f8fe92,2 +np.float64,0xbfebeafd40b7d5fa,0x3ff67c3cf34036e3,2 +np.float64,0x7fd108861a22110b,0x7ff0000000000000,2 +np.float64,0xff8e499ae03c9340,0x7ff0000000000000,2 +np.float64,0xbfd2f58caa25eb1a,0x3ff0b50b1bffafdf,2 +np.float64,0x3fa040c9bc208193,0x3ff002105e95aefa,2 +np.float64,0xbfd2ebc0a5a5d782,0x3ff0b44ed5a11584,2 +np.float64,0xffe237bc93a46f78,0x7ff0000000000000,2 +np.float64,0x3fd557c5eeaaaf8c,0x3ff0e5e0a575e1ba,2 +np.float64,0x7abb419ef5769,0x3ff0000000000000,2 +np.float64,0xffefa1fe353f43fb,0x7ff0000000000000,2 +np.float64,0x3fa6f80ba02df017,0x3ff0041f51fa0d76,2 +np.float64,0xbfdce79488b9cf2a,0x3ff1a8e32877beb4,2 +np.float64,0x2285f3e4450bf,0x3ff0000000000000,2 +np.float64,0x3bf7eb7277efe,0x3ff0000000000000,2 +np.float64,0xbfd5925fd3ab24c0,0x3ff0eae1c2ac2e78,2 +np.float64,0xbfed6325227ac64a,0x3ff73c14a2ad5bfe,2 +np.float64,0x8000429c02408539,0x3ff0000000000000,2 +np.float64,0xb67c21e76cf84,0x3ff0000000000000,2 +np.float64,0x3fec3d3462f87a69,0x3ff6a51e4c027eb7,2 +np.float64,0x3feae69cbcf5cd3a,0x3ff5fe9387314afd,2 +np.float64,0x7fd0c9a0ec219341,0x7ff0000000000000,2 +np.float64,0x8004adb7f6295b71,0x3ff0000000000000,2 +np.float64,0xffd61fe8bb2c3fd2,0x7ff0000000000000,2 +np.float64,0xffe7fb3834aff670,0x7ff0000000000000,2 +np.float64,0x7fd1eef163a3dde2,0x7ff0000000000000,2 +np.float64,0x2e84547a5d08b,0x3ff0000000000000,2 +np.float64,0x8002d8875ee5b10f,0x3ff0000000000000,2 +np.float64,0x3fe1d1c5f763a38c,0x3ff28ba524fb6de8,2 +np.float64,0x8001dea0bc43bd42,0x3ff0000000000000,2 +np.float64,0xfecfad91fd9f6,0x3ff0000000000000,2 +np.float64,0xffed7965fa3af2cb,0x7ff0000000000000,2 +np.float64,0xbfe6102ccc2c205a,0x3ff3f4c082506686,2 +np.float64,0x3feff75b777feeb6,0x3ff8ab6222578e0c,2 +np.float64,0x3fb8a97bd43152f8,0x3ff013057f0a9d89,2 +np.float64,0xffe234b5e964696c,0x7ff0000000000000,2 +np.float64,0x984d9137309b2,0x3ff0000000000000,2 +np.float64,0xbfe42e9230e85d24,0x3ff349fb7d1a7560,2 +np.float64,0xbfecc8b249f99165,0x3ff6ebd0fea0ea72,2 +np.float64,0x8000840910410813,0x3ff0000000000000,2 +np.float64,0xbfd81db9e7303b74,0x3ff126402d3539ec,2 +np.float64,0x800548eb7fea91d8,0x3ff0000000000000,2 +np.float64,0xbfe4679ad0e8cf36,0x3ff35d4db89296a3,2 +np.float64,0x3fd4c55b5a298ab7,0x3ff0d99da31081f9,2 +np.float64,0xbfa8f5b38c31eb60,0x3ff004de3a23b32d,2 +np.float64,0x80005d348e80ba6a,0x3ff0000000000000,2 +np.float64,0x800c348d6118691b,0x3ff0000000000000,2 +np.float64,0xffd6b88f84ad7120,0x7ff0000000000000,2 +np.float64,0x3fc1aaaa82235555,0x3ff027136afd08e0,2 +np.float64,0x7fca7d081b34fa0f,0x7ff0000000000000,2 +np.float64,0x1,0x3ff0000000000000,2 +np.float64,0xbfdc810d1139021a,0x3ff19d007408cfe3,2 +np.float64,0xbfe5dce05f2bb9c0,0x3ff3e1bb9234617b,2 +np.float64,0xffecfe2c32b9fc58,0x7ff0000000000000,2 +np.float64,0x95b2891b2b651,0x3ff0000000000000,2 +np.float64,0x8000b60c6c616c1a,0x3ff0000000000000,2 +np.float64,0x4944f0889289f,0x3ff0000000000000,2 +np.float64,0x3fe6e508696dca10,0x3ff445d1b94863e9,2 +np.float64,0xbfe63355d0ec66ac,0x3ff401e74f16d16f,2 +np.float64,0xbfe9b9595af372b3,0x3ff57445e1b4d670,2 +np.float64,0x800e16f7313c2dee,0x3ff0000000000000,2 +np.float64,0xffe898f5f0b131eb,0x7ff0000000000000,2 +np.float64,0x3fe91ac651f2358d,0x3ff52e787c21c004,2 +np.float64,0x7fbfaac6783f558c,0x7ff0000000000000,2 +np.float64,0xd8ef3dfbb1de8,0x3ff0000000000000,2 +np.float64,0xbfc58c13a52b1828,0x3ff03a2c19d65019,2 +np.float64,0xbfbde55e8a3bcac0,0x3ff01bf648a3e0a7,2 +np.float64,0xffc3034930260694,0x7ff0000000000000,2 +np.float64,0xea77a64dd4ef5,0x3ff0000000000000,2 +np.float64,0x800cfe7e7739fcfd,0x3ff0000000000000,2 +np.float64,0x4960f31a92c1f,0x3ff0000000000000,2 +np.float64,0x3fd9552c94b2aa58,0x3ff14515a29add09,2 +np.float64,0xffe8b3244c316648,0x7ff0000000000000,2 +np.float64,0x3fe8201e6a70403d,0x3ff4c444fa679cce,2 +np.float64,0xffe9ab7c20f356f8,0x7ff0000000000000,2 +np.float64,0x3fed8bba5f7b1774,0x3ff751853c4c95c5,2 +np.float64,0x8007639cb76ec73a,0x3ff0000000000000,2 +np.float64,0xbfe396db89672db7,0x3ff317bfd1d6fa8c,2 +np.float64,0xbfeb42f888f685f1,0x3ff62a7e0eee56b1,2 +np.float64,0x3fe894827c712904,0x3ff4f4f561d9ea13,2 +np.float64,0xb66b3caf6cd68,0x3ff0000000000000,2 +np.float64,0x800f8907fdbf1210,0x3ff0000000000000,2 +np.float64,0x7fe9b0cddb73619b,0x7ff0000000000000,2 +np.float64,0xbfda70c0e634e182,0x3ff1628c6fdffc53,2 +np.float64,0x3fe0b5f534a16bea,0x3ff23b4ed4c2b48e,2 +np.float64,0xbfe8eee93671ddd2,0x3ff51b85b3c50ae4,2 +np.float64,0xbfe8c22627f1844c,0x3ff50858787a3bfe,2 +np.float64,0x37bb83c86f771,0x3ff0000000000000,2 +np.float64,0xffb7827ffe2f0500,0x7ff0000000000000,2 +np.float64,0x64317940c864,0x3ff0000000000000,2 +np.float64,0x800430ecee6861db,0x3ff0000000000000,2 +np.float64,0x3fa4291fbc285240,0x3ff0032d0204f6dd,2 +np.float64,0xffec69f76af8d3ee,0x7ff0000000000000,2 +np.float64,0x3ff0000000000000,0x3ff8b07551d9f550,2 +np.float64,0x3fc4cf3c42299e79,0x3ff0363fb1d3c254,2 +np.float64,0x7fe0223a77e04474,0x7ff0000000000000,2 +np.float64,0x800a3d4fa4347aa0,0x3ff0000000000000,2 +np.float64,0x3fdd273f94ba4e7f,0x3ff1b05b686e6879,2 +np.float64,0x3feca79052f94f20,0x3ff6dadedfa283aa,2 +np.float64,0x5e7f6f80bcfef,0x3ff0000000000000,2 +np.float64,0xbfef035892fe06b1,0x3ff81efb39cbeba2,2 +np.float64,0x3fee6c08e07cd812,0x3ff7caad952860a1,2 +np.float64,0xffeda715877b4e2a,0x7ff0000000000000,2 +np.float64,0x800580286b0b0052,0x3ff0000000000000,2 +np.float64,0x800703a73fee074f,0x3ff0000000000000,2 +np.float64,0xbfccf96a6639f2d4,0x3ff0696330a60832,2 +np.float64,0x7feb408442368108,0x7ff0000000000000,2 +np.float64,0x3fedc87a46fb90f5,0x3ff771e3635649a9,2 +np.float64,0x3fd8297b773052f7,0x3ff12762bc0cea76,2 +np.float64,0x3fee41bb03fc8376,0x3ff7b37b2da48ab4,2 +np.float64,0xbfe2b05a226560b4,0x3ff2cea17ae7c528,2 +np.float64,0xbfd2e92cf2a5d25a,0x3ff0b41d605ced61,2 +np.float64,0x4817f03a902ff,0x3ff0000000000000,2 +np.float64,0x8c9d4f0d193aa,0x3ff0000000000000,2 diff --git a/numpy/core/tests/data/umath-validation-set-exp.csv b/numpy/core/tests/data/umath-validation-set-exp.csv new file mode 100644 index 000000000000..7c5ef3b334fb --- /dev/null +++ b/numpy/core/tests/data/umath-validation-set-exp.csv @@ -0,0 +1,412 @@ +dtype,input,output,ulperrortol +## +ve denormals ## +np.float32,0x004b4716,0x3f800000,3 +np.float32,0x007b2490,0x3f800000,3 +np.float32,0x007c99fa,0x3f800000,3 +np.float32,0x00734a0c,0x3f800000,3 +np.float32,0x0070de24,0x3f800000,3 +np.float32,0x00495d65,0x3f800000,3 +np.float32,0x006894f6,0x3f800000,3 +np.float32,0x00555a76,0x3f800000,3 +np.float32,0x004e1fb8,0x3f800000,3 +np.float32,0x00687de9,0x3f800000,3 +## -ve denormals ## +np.float32,0x805b59af,0x3f800000,3 +np.float32,0x807ed8ed,0x3f800000,3 +np.float32,0x807142ad,0x3f800000,3 +np.float32,0x80772002,0x3f800000,3 +np.float32,0x8062abcb,0x3f800000,3 +np.float32,0x8045e31c,0x3f800000,3 +np.float32,0x805f01c2,0x3f800000,3 +np.float32,0x80506432,0x3f800000,3 +np.float32,0x8060089d,0x3f800000,3 +np.float32,0x8071292f,0x3f800000,3 +## floats that output a denormal ## +np.float32,0xc2cf3fc1,0x00000001,3 +np.float32,0xc2c79726,0x00000021,3 +np.float32,0xc2cb295d,0x00000005,3 +np.float32,0xc2b49e6b,0x00068c4c,3 +np.float32,0xc2ca8116,0x00000008,3 +np.float32,0xc2c23f82,0x000001d7,3 +np.float32,0xc2cb69c0,0x00000005,3 +np.float32,0xc2cc1f4d,0x00000003,3 +np.float32,0xc2ae094e,0x00affc4c,3 +np.float32,0xc2c86c44,0x00000015,3 +## random floats between -87.0f and 88.0f ## +np.float32,0x4030d7e0,0x417d9a05,3 +np.float32,0x426f60e8,0x6aa1be2c,3 +np.float32,0x41a1b220,0x4e0efc11,3 +np.float32,0xc20cc722,0x26159da7,3 +np.float32,0x41c492bc,0x512ec79d,3 +np.float32,0x40980210,0x42e73a0e,3 +np.float32,0xbf1f7b80,0x3f094de3,3 +np.float32,0x42a678a4,0x7b87a383,3 +np.float32,0xc20f3cfd,0x25a1c304,3 +np.float32,0x423ff34c,0x6216467f,3 +np.float32,0x00000000,0x3f800000,3 +## floats that cause an overflow ## +np.float32,0x7f06d8c1,0x7f800000,3 +np.float32,0x7f451912,0x7f800000,3 +np.float32,0x7ecceac3,0x7f800000,3 +np.float32,0x7f643b45,0x7f800000,3 +np.float32,0x7e910ea0,0x7f800000,3 +np.float32,0x7eb4756b,0x7f800000,3 +np.float32,0x7f4ec708,0x7f800000,3 +np.float32,0x7f6b4551,0x7f800000,3 +np.float32,0x7d8edbda,0x7f800000,3 +np.float32,0x7f730718,0x7f800000,3 +np.float32,0x42b17217,0x7f7fff84,3 +np.float32,0x42b17218,0x7f800000,3 +np.float32,0x42b17219,0x7f800000,3 +np.float32,0xfef2b0bc,0x00000000,3 +np.float32,0xff69f83e,0x00000000,3 +np.float32,0xff4ecb12,0x00000000,3 +np.float32,0xfeac6d86,0x00000000,3 +np.float32,0xfde0cdb8,0x00000000,3 +np.float32,0xff26aef4,0x00000000,3 +np.float32,0xff6f9277,0x00000000,3 +np.float32,0xff7adfc4,0x00000000,3 +np.float32,0xff0ad40e,0x00000000,3 +np.float32,0xff6fd8f3,0x00000000,3 +np.float32,0xc2cff1b4,0x00000001,3 +np.float32,0xc2cff1b5,0x00000000,3 +np.float32,0xc2cff1b6,0x00000000,3 +np.float32,0x7f800000,0x7f800000,3 +np.float32,0xff800000,0x00000000,3 +np.float32,0x4292f27c,0x7480000a,3 +np.float32,0x42a920be,0x7c7fff94,3 +np.float32,0x41c214c9,0x50ffffd9,3 +np.float32,0x41abe686,0x4effffd9,3 +np.float32,0x4287db5a,0x707fffd3,3 +np.float32,0x41902cbb,0x4c800078,3 +np.float32,0x42609466,0x67ffffeb,3 +np.float32,0x41a65af5,0x4e7fffd1,3 +np.float32,0x417f13ff,0x4affffc9,3 +np.float32,0x426d0e6c,0x6a3504f2,3 +np.float32,0x41bc8934,0x507fff51,3 +np.float32,0x42a7bdde,0x7c0000d6,3 +np.float32,0x4120cf66,0x46b504f6,3 +np.float32,0x4244da8f,0x62ffff1a,3 +np.float32,0x41a0cf69,0x4e000034,3 +np.float32,0x41cd2bec,0x52000005,3 +np.float32,0x42893e41,0x7100009e,3 +np.float32,0x41b437e1,0x4fb50502,3 +np.float32,0x41d8430f,0x5300001d,3 +np.float32,0x4244da92,0x62ffffda,3 +np.float32,0x41a0cf63,0x4dffffa9,3 +np.float32,0x3eb17218,0x3fb504f3,3 +np.float32,0x428729e8,0x703504dc,3 +np.float32,0x41a0cf67,0x4e000014,3 +np.float32,0x4252b77d,0x65800011,3 +np.float32,0x41902cb9,0x4c800058,3 +np.float32,0x42a0cf67,0x79800052,3 +np.float32,0x4152b77b,0x48ffffe9,3 +np.float32,0x41265af3,0x46ffffc8,3 +np.float32,0x42187e0b,0x5affff9a,3 +np.float32,0xc0d2b77c,0x3ab504f6,3 +np.float32,0xc283b2ac,0x10000072,3 +np.float32,0xc1cff1b4,0x2cb504f5,3 +np.float32,0xc05dce9e,0x3d000000,3 +np.float32,0xc28ec9d2,0x0bfffea5,3 +np.float32,0xc23c893a,0x1d7fffde,3 +np.float32,0xc2a920c0,0x027fff6c,3 +np.float32,0xc1f9886f,0x2900002b,3 +np.float32,0xc2c42920,0x000000b5,3 +np.float32,0xc2893e41,0x0dfffec5,3 +np.float32,0xc2c4da93,0x00000080,3 +np.float32,0xc17f1401,0x3400000c,3 +np.float32,0xc1902cb6,0x327fffaf,3 +np.float32,0xc27c4e3b,0x11ffffc5,3 +np.float32,0xc268e5c5,0x157ffe9d,3 +np.float32,0xc2b4e953,0x0005a826,3 +np.float32,0xc287db5a,0x0e800016,3 +np.float32,0xc207db5a,0x2700000b,3 +np.float32,0xc2b2d4fe,0x000ffff1,3 +np.float32,0xc268e5c0,0x157fffdd,3 +np.float32,0xc22920bd,0x2100003b,3 +np.float32,0xc2902caf,0x0b80011e,3 +np.float32,0xc1902cba,0x327fff2f,3 +np.float32,0xc2ca6625,0x00000008,3 +np.float32,0xc280ece8,0x10fffeb5,3 +np.float32,0xc2918f94,0x0b0000ea,3 +np.float32,0xc29b43d5,0x077ffffc,3 +np.float32,0xc1e61ff7,0x2ab504f5,3 +np.float32,0xc2867878,0x0effff15,3 +np.float32,0xc2a2324a,0x04fffff4,3 +#float64 +## near zero ## +np.float64,0x8000000000000000,0x3ff0000000000000,1 +np.float64,0x8010000000000000,0x3ff0000000000000,1 +np.float64,0x8000000000000001,0x3ff0000000000000,1 +np.float64,0x8360000000000000,0x3ff0000000000000,1 +np.float64,0x9a70000000000000,0x3ff0000000000000,1 +np.float64,0xb9b0000000000000,0x3ff0000000000000,1 +np.float64,0xb810000000000000,0x3ff0000000000000,1 +np.float64,0xbc30000000000000,0x3ff0000000000000,1 +np.float64,0xb6a0000000000000,0x3ff0000000000000,1 +np.float64,0x0000000000000000,0x3ff0000000000000,1 +np.float64,0x0010000000000000,0x3ff0000000000000,1 +np.float64,0x0000000000000001,0x3ff0000000000000,1 +np.float64,0x0360000000000000,0x3ff0000000000000,1 +np.float64,0x1a70000000000000,0x3ff0000000000000,1 +np.float64,0x3c30000000000000,0x3ff0000000000000,1 +np.float64,0x36a0000000000000,0x3ff0000000000000,1 +np.float64,0x39b0000000000000,0x3ff0000000000000,1 +np.float64,0x3810000000000000,0x3ff0000000000000,1 +## underflow ## +np.float64,0xc0c6276800000000,0x0000000000000000,1 +np.float64,0xc0c62d918ce2421d,0x0000000000000000,1 +np.float64,0xc0c62d918ce2421e,0x0000000000000000,1 +np.float64,0xc0c62d91a0000000,0x0000000000000000,1 +np.float64,0xc0c62d9180000000,0x0000000000000000,1 +np.float64,0xc0c62dea45ee3e06,0x0000000000000000,1 +np.float64,0xc0c62dea45ee3e07,0x0000000000000000,1 +np.float64,0xc0c62dea40000000,0x0000000000000000,1 +np.float64,0xc0c62dea60000000,0x0000000000000000,1 +np.float64,0xc0875f1120000000,0x0000000000000000,1 +np.float64,0xc0875f113c30b1c8,0x0000000000000000,1 +np.float64,0xc0875f1140000000,0x0000000000000000,1 +np.float64,0xc093480000000000,0x0000000000000000,1 +np.float64,0xffefffffffffffff,0x0000000000000000,1 +np.float64,0xc7efffffe0000000,0x0000000000000000,1 +## overflow ## +np.float64,0x40862e52fefa39ef,0x7ff0000000000000,1 +np.float64,0x40872e42fefa39ef,0x7ff0000000000000,1 +## +/- INF, +/- NAN ## +np.float64,0x7ff0000000000000,0x7ff0000000000000,1 +np.float64,0xfff0000000000000,0x0000000000000000,1 +np.float64,0x7ff8000000000000,0x7ff8000000000000,1 +np.float64,0xfff8000000000000,0xfff8000000000000,1 +## output denormal ## +np.float64,0xc087438520000000,0x0000000000000001,1 +np.float64,0xc08743853f2f4461,0x0000000000000001,1 +np.float64,0xc08743853f2f4460,0x0000000000000001,1 +np.float64,0xc087438540000000,0x0000000000000001,1 +## between -745.13321910 and 709.78271289 ## +np.float64,0xbff760cd14774bd9,0x3fcdb14ced00ceb6,1 +np.float64,0xbff760cd20000000,0x3fcdb14cd7993879,1 +np.float64,0xbff760cd00000000,0x3fcdb14d12fbd264,1 +np.float64,0xc07f1cf360000000,0x130c1b369af14fda,1 +np.float64,0xbeb0000000000000,0x3feffffe00001000,1 +np.float64,0xbd70000000000000,0x3fefffffffffe000,1 +np.float64,0xc084fd46e5c84952,0x0360000000000139,1 +np.float64,0xc084fd46e5c84953,0x035ffffffffffe71,1 +np.float64,0xc084fd46e0000000,0x0360000b9096d32c,1 +np.float64,0xc084fd4700000000,0x035fff9721d12104,1 +np.float64,0xc086232bc0000000,0x0010003af5e64635,1 +np.float64,0xc086232bdd7abcd2,0x001000000000007c,1 +np.float64,0xc086232bdd7abcd3,0x000ffffffffffe7c,1 +np.float64,0xc086232be0000000,0x000ffffaf57a6fc9,1 +np.float64,0xc086233920000000,0x000fe590e3b45eb0,1 +np.float64,0xc086233938000000,0x000fe56133493c57,1 +np.float64,0xc086233940000000,0x000fe5514deffbbc,1 +np.float64,0xc086234c98000000,0x000fbf1024c32ccb,1 +np.float64,0xc086234ca0000000,0x000fbf0065bae78d,1 +np.float64,0xc086234c80000000,0x000fbf3f623a7724,1 +np.float64,0xc086234ec0000000,0x000fbad237c846f9,1 +np.float64,0xc086234ec8000000,0x000fbac27cfdec97,1 +np.float64,0xc086234ee0000000,0x000fba934cfd3dc2,1 +np.float64,0xc086234ef0000000,0x000fba73d7f618d9,1 +np.float64,0xc086234f00000000,0x000fba54632dddc0,1 +np.float64,0xc0862356e0000000,0x000faae0945b761a,1 +np.float64,0xc0862356f0000000,0x000faac13eb9a310,1 +np.float64,0xc086235700000000,0x000faaa1e9567b0a,1 +np.float64,0xc086236020000000,0x000f98cd75c11ed7,1 +np.float64,0xc086236ca0000000,0x000f8081b4d93f89,1 +np.float64,0xc086236cb0000000,0x000f8062b3f4d6c5,1 +np.float64,0xc086236cc0000000,0x000f8043b34e6f8c,1 +np.float64,0xc086238d98000000,0x000f41220d9b0d2c,1 +np.float64,0xc086238da0000000,0x000f4112cc80a01f,1 +np.float64,0xc086238d80000000,0x000f414fd145db5b,1 +np.float64,0xc08624fd00000000,0x000cbfce8ea1e6c4,1 +np.float64,0xc086256080000000,0x000c250747fcd46e,1 +np.float64,0xc08626c480000000,0x000a34f4bd975193,1 +np.float64,0xbf50000000000000,0x3feff800ffeaac00,1 +np.float64,0xbe10000000000000,0x3fefffffff800000,1 +np.float64,0xbcd0000000000000,0x3feffffffffffff8,1 +np.float64,0xc055d589e0000000,0x38100004bf94f63e,1 +np.float64,0xc055d58a00000000,0x380ffff97f292ce8,1 +np.float64,0xbfd962d900000000,0x3fe585a4b00110e1,1 +np.float64,0x3ff4bed280000000,0x400d411e7a58a303,1 +np.float64,0x3fff0b3620000000,0x401bd7737ffffcf3,1 +np.float64,0x3ff0000000000000,0x4005bf0a8b145769,1 +np.float64,0x3eb0000000000000,0x3ff0000100000800,1 +np.float64,0x3d70000000000000,0x3ff0000000001000,1 +np.float64,0x40862e42e0000000,0x7fefff841808287f,1 +np.float64,0x40862e42fefa39ef,0x7fefffffffffff2a,1 +np.float64,0x40862e0000000000,0x7feef85a11e73f2d,1 +np.float64,0x4000000000000000,0x401d8e64b8d4ddae,1 +np.float64,0x4009242920000000,0x40372a52c383a488,1 +np.float64,0x4049000000000000,0x44719103e4080b45,1 +np.float64,0x4008000000000000,0x403415e5bf6fb106,1 +np.float64,0x3f50000000000000,0x3ff00400800aab55,1 +np.float64,0x3e10000000000000,0x3ff0000000400000,1 +np.float64,0x3cd0000000000000,0x3ff0000000000004,1 +np.float64,0x40562e40a0000000,0x47effed088821c3f,1 +np.float64,0x40562e42e0000000,0x47effff082e6c7ff,1 +np.float64,0x40562e4300000000,0x47f00000417184b8,1 +np.float64,0x3fe8000000000000,0x4000ef9db467dcf8,1 +np.float64,0x402b12e8d4f33589,0x412718f68c71a6fe,1 +np.float64,0x402b12e8d4f3358a,0x412718f68c71a70a,1 +np.float64,0x402b12e8c0000000,0x412718f59a7f472e,1 +np.float64,0x402b12e8e0000000,0x412718f70c0eac62,1 +##use 1th entry +np.float64,0x40631659AE147CB4,0x4db3a95025a4890f,1 +np.float64,0xC061B87D2E85A4E2,0x332640c8e2de2c51,1 +np.float64,0x405A4A50BE243AF4,0x496a45e4b7f0339a,1 +np.float64,0xC0839898B98EC5C6,0x0764027828830df4,1 +#use 2th entry +np.float64,0xC072428C44B6537C,0x2596ade838b96f3e,1 +np.float64,0xC053057C5E1AE9BF,0x3912c8fad18fdadf,1 +np.float64,0x407E89C78328BAA3,0x6bfe35d5b9a1a194,1 +np.float64,0x4083501B6DD87112,0x77a855503a38924e,1 +#use 3th entry +np.float64,0x40832C6195F24540,0x7741e73c80e5eb2f,1 +np.float64,0xC083D4CD557C2EC9,0x06b61727c2d2508e,1 +np.float64,0x400C48F5F67C99BD,0x404128820f02b92e,1 +np.float64,0x4056E36D9B2DF26A,0x4830f52ff34a8242,1 +#use 4th entry +np.float64,0x4080FF700D8CBD06,0x70fa70df9bc30f20,1 +np.float64,0x406C276D39E53328,0x543eb8e20a8f4741,1 +np.float64,0xC070D6159BBD8716,0x27a4a0548c904a75,1 +np.float64,0xC052EBCF8ED61F83,0x391c0e92368d15e4,1 +#use 5th entry +np.float64,0xC061F892A8AC5FBE,0x32f807a89efd3869,1 +np.float64,0x4021D885D2DBA085,0x40bd4dc86d3e3270,1 +np.float64,0x40767AEEEE7D4FCF,0x605e22851ee2afb7,1 +np.float64,0xC0757C5D75D08C80,0x20f0751599b992a2,1 +#use 6th entry +np.float64,0x405ACF7A284C4CE3,0x499a4e0b7a27027c,1 +np.float64,0xC085A6C9E80D7AF5,0x0175914009d62ec2,1 +np.float64,0xC07E4C02F86F1DAE,0x1439269b29a9231e,1 +np.float64,0x4080D80F9691CC87,0x7088a6cdafb041de,1 +#use 7th entry +np.float64,0x407FDFD84FBA0AC1,0x6deb1ae6f9bc4767,1 +np.float64,0x40630C06A1A2213D,0x4dac7a9d51a838b7,1 +np.float64,0x40685FDB30BB8B4F,0x5183f5cc2cac9e79,1 +np.float64,0x408045A2208F77F4,0x6ee299e08e2aa2f0,1 +#use 8th entry +np.float64,0xC08104E391F5078B,0x0ed397b7cbfbd230,1 +np.float64,0xC031501CAEFAE395,0x3e6040fd1ea35085,1 +np.float64,0xC079229124F6247C,0x1babf4f923306b1e,1 +np.float64,0x407FB65F44600435,0x6db03beaf2512b8a,1 +#use 9th entry +np.float64,0xC07EDEE8E8E8A5AC,0x136536cec9cbef48,1 +np.float64,0x4072BB4086099A14,0x5af4d3c3008b56cc,1 +np.float64,0x4050442A2EC42CB4,0x45cd393bd8fad357,1 +np.float64,0xC06AC28FB3D419B4,0x2ca1b9d3437df85f,1 +#use 10th entry +np.float64,0x40567FC6F0A68076,0x480c977fd5f3122e,1 +np.float64,0x40620A2F7EDA59BB,0x4cf278e96f4ce4d7,1 +np.float64,0xC085044707CD557C,0x034aad6c968a045a,1 +np.float64,0xC07374EA5AC516AA,0x23dd6afdc03e83d5,1 +#use 11th entry +np.float64,0x4073CC95332619C1,0x5c804b1498bbaa54,1 +np.float64,0xC0799FEBBE257F31,0x1af6a954c43b87d2,1 +np.float64,0x408159F19EA424F6,0x7200858efcbfc84d,1 +np.float64,0x404A81F6F24C0792,0x44b664a07ce5bbfa,1 +#use 12th entry +np.float64,0x40295FF1EFB9A741,0x4113c0e74c52d7b0,1 +np.float64,0x4073975F4CC411DA,0x5c32be40b4fec2c1,1 +np.float64,0x406E9DE52E82A77E,0x56049c9a3f1ae089,1 +np.float64,0x40748C2F52560ED9,0x5d93bc14fd4cd23b,1 +#use 13th entry +np.float64,0x4062A553CDC4D04C,0x4d6266bfde301318,1 +np.float64,0xC079EC1D63598AB7,0x1a88cb184dab224c,1 +np.float64,0xC0725C1CB3167427,0x25725b46f8a081f6,1 +np.float64,0x407888771D9B45F9,0x6353b1ec6bd7ce80,1 +#use 14th entry +np.float64,0xC082CBA03AA89807,0x09b383723831ce56,1 +np.float64,0xC083A8961BB67DD7,0x0735b118d5275552,1 +np.float64,0xC076BC6ECA12E7E3,0x1f2222679eaef615,1 +np.float64,0xC072752503AA1A5B,0x254eb832242c77e1,1 +#use 15th entry +np.float64,0xC058800792125DEC,0x371882372a0b48d4,1 +np.float64,0x4082909FD863E81C,0x7580d5f386920142,1 +np.float64,0xC071616F8FB534F9,0x26dbe20ef64a412b,1 +np.float64,0x406D1AB571CAA747,0x54ee0d55cb38ac20,1 +#use 16th entry +np.float64,0x406956428B7DAD09,0x52358682c271237f,1 +np.float64,0xC07EFC2D9D17B621,0x133b3e77c27a4d45,1 +np.float64,0xC08469BAC5BA3CCA,0x050863e5f42cc52f,1 +np.float64,0x407189D9626386A5,0x593cb1c0b3b5c1d3,1 +#use 17th entry +np.float64,0x4077E652E3DEB8C6,0x6269a10dcbd3c752,1 +np.float64,0x407674C97DB06878,0x605485dcc2426ec2,1 +np.float64,0xC07CE9969CF4268D,0x16386cf8996669f2,1 +np.float64,0x40780EE32D5847C4,0x62a436bd1abe108d,1 +#use 18th entry +np.float64,0x4076C3AA5E1E8DA1,0x60c62f56a5e72e24,1 +np.float64,0xC0730AFC7239B9BE,0x24758ead095cec1e,1 +np.float64,0xC085CC2B9C420DDB,0x0109cdaa2e5694c1,1 +np.float64,0x406D0765CB6D7AA4,0x54e06f8dd91bd945,1 +#use 19th entry +np.float64,0xC082D011F3B495E7,0x09a6647661d279c2,1 +np.float64,0xC072826AF8F6AFBC,0x253acd3cd224507e,1 +np.float64,0x404EB9C4810CEA09,0x457933dbf07e8133,1 +np.float64,0x408284FBC97C58CE,0x755f6eb234aa4b98,1 +#use 20th entry +np.float64,0x40856008CF6EDC63,0x7d9c0b3c03f4f73c,1 +np.float64,0xC077CB2E9F013B17,0x1d9b3d3a166a55db,1 +np.float64,0xC0479CA3C20AD057,0x3bad40e081555b99,1 +np.float64,0x40844CD31107332A,0x7a821d70aea478e2,1 +#use 21th entry +np.float64,0xC07C8FCC0BFCC844,0x16ba1cc8c539d19b,1 +np.float64,0xC085C4E9A3ABA488,0x011ff675ba1a2217,1 +np.float64,0x4074D538B32966E5,0x5dfd9d78043c6ad9,1 +np.float64,0xC0630CA16902AD46,0x3231a446074cede6,1 +#use 22th entry +np.float64,0xC06C826733D7D0B7,0x2b5f1078314d41e1,1 +np.float64,0xC0520DF55B2B907F,0x396c13a6ce8e833e,1 +np.float64,0xC080712072B0F437,0x107eae02d11d98ea,1 +np.float64,0x40528A6150E19EFB,0x469fdabda02228c5,1 +#use 23th entry +np.float64,0xC07B1D74B6586451,0x18d1253883ae3b48,1 +np.float64,0x4045AFD7867DAEC0,0x43d7d634fc4c5d98,1 +np.float64,0xC07A08B91F9ED3E2,0x1a60973e6397fc37,1 +np.float64,0x407B3ECF0AE21C8C,0x673e03e9d98d7235,1 +#use 24th entry +np.float64,0xC078AEB6F30CEABF,0x1c530b93ab54a1b3,1 +np.float64,0x4084495006A41672,0x7a775b6dc7e63064,1 +np.float64,0x40830B1C0EBF95DD,0x76e1e6eed77cfb89,1 +np.float64,0x407D93E8F33D8470,0x6a9adbc9e1e4f1e5,1 +#use 25th entry +np.float64,0x4066B11A09EFD9E8,0x504dd528065c28a7,1 +np.float64,0x408545823723AEEB,0x7d504a9b1844f594,1 +np.float64,0xC068C711F2CA3362,0x2e104f3496ea118e,1 +np.float64,0x407F317FCC3CA873,0x6cf0732c9948ebf4,1 +#use 26th entry +np.float64,0x407AFB3EBA2ED50F,0x66dc28a129c868d5,1 +np.float64,0xC075377037708ADE,0x21531a329f3d793e,1 +np.float64,0xC07C30066A1F3246,0x174448baa16ded2b,1 +np.float64,0xC06689A75DE2ABD3,0x2fad70662fae230b,1 +#use 27th entry +np.float64,0x4081514E9FCCF1E0,0x71e673b9efd15f44,1 +np.float64,0xC0762C710AF68460,0x1ff1ed7d8947fe43,1 +np.float64,0xC0468102FF70D9C4,0x3be0c3a8ff3419a3,1 +np.float64,0xC07EA4CEEF02A83E,0x13b908f085102c61,1 +#use 28th entry +np.float64,0xC06290B04AE823C4,0x328a83da3c2e3351,1 +np.float64,0xC0770EB1D1C395FB,0x1eab281c1f1db5fe,1 +np.float64,0xC06F5D4D838A5BAE,0x29500ea32fb474ea,1 +np.float64,0x40723B3133B54C5D,0x5a3c82c7c3a2b848,1 +#use 29th entry +np.float64,0x4085E6454CE3B4AA,0x7f20319b9638d06a,1 +np.float64,0x408389F2A0585D4B,0x7850667c58aab3d0,1 +np.float64,0xC0382798F9C8AE69,0x3dc1c79fe8739d6d,1 +np.float64,0xC08299D827608418,0x0a4335f76cdbaeb5,1 +#use 30th entry +np.float64,0xC06F3DED43301BF1,0x2965670ae46750a8,1 +np.float64,0xC070CAF6BDD577D9,0x27b4aa4ffdd29981,1 +np.float64,0x4078529AD4B2D9F2,0x6305c12755d5e0a6,1 +np.float64,0xC055B14E75A31B96,0x381c2eda6d111e5d,1 +#use 31th entry +np.float64,0x407B13EE414FA931,0x6700772c7544564d,1 +np.float64,0x407EAFDE9DE3EC54,0x6c346a0e49724a3c,1 +np.float64,0xC08362F398B9530D,0x07ffeddbadf980cb,1 +np.float64,0x407E865CDD9EEB86,0x6bf866cac5e0d126,1 +#use 32th entry +np.float64,0x407FB62DBC794C86,0x6db009f708ac62cb,1 +np.float64,0xC063D0BAA68CDDDE,0x31a3b2a51ce50430,1 +np.float64,0xC05E7706A2231394,0x34f24bead6fab5c9,1 +np.float64,0x4083E3A06FDE444E,0x79527b7a386d1937,1 diff --git a/numpy/core/tests/data/umath-validation-set-exp2.csv b/numpy/core/tests/data/umath-validation-set-exp2.csv new file mode 100644 index 000000000000..e19e9ebd6285 --- /dev/null +++ b/numpy/core/tests/data/umath-validation-set-exp2.csv @@ -0,0 +1,1429 @@ +dtype,input,output,ulperrortol +np.float32,0xbdfe94b0,0x3f6adda6,2 +np.float32,0x3f20f8f8,0x3fc5ec69,2 +np.float32,0x7040b5,0x3f800000,2 +np.float32,0x30ec5,0x3f800000,2 +np.float32,0x3eb63070,0x3fa3ce29,2 +np.float32,0xff4dda3d,0x0,2 +np.float32,0x805b832f,0x3f800000,2 +np.float32,0x3e883fb7,0x3f99ed8c,2 +np.float32,0x3f14d71f,0x3fbf8708,2 +np.float32,0xff7b1e55,0x0,2 +np.float32,0xbf691ac6,0x3f082fa2,2 +np.float32,0x7ee3e6ab,0x7f800000,2 +np.float32,0xbec6e2b4,0x3f439248,2 +np.float32,0xbf5f5ec2,0x3f0bd2c0,2 +np.float32,0x8025cc2c,0x3f800000,2 +np.float32,0x7e0d7672,0x7f800000,2 +np.float32,0xff4bbc5c,0x0,2 +np.float32,0xbd94fb30,0x3f73696b,2 +np.float32,0x6cc079,0x3f800000,2 +np.float32,0x803cf080,0x3f800000,2 +np.float32,0x71d418,0x3f800000,2 +np.float32,0xbf24a442,0x3f23ec1e,2 +np.float32,0xbe6c9510,0x3f5a1e1d,2 +np.float32,0xbe8fb284,0x3f52be38,2 +np.float32,0x7ea64754,0x7f800000,2 +np.float32,0x7fc00000,0x7fc00000,2 +np.float32,0x80620cfd,0x3f800000,2 +np.float32,0x3f3e20e8,0x3fd62e72,2 +np.float32,0x3f384600,0x3fd2d00e,2 +np.float32,0xff362150,0x0,2 +np.float32,0xbf349fa8,0x3f1cfaef,2 +np.float32,0xbf776cf2,0x3f0301a6,2 +np.float32,0x8021fc60,0x3f800000,2 +np.float32,0xbdb75280,0x3f70995c,2 +np.float32,0x7e9363a6,0x7f800000,2 +np.float32,0x7e728422,0x7f800000,2 +np.float32,0xfe91edc2,0x0,2 +np.float32,0x3f5f438c,0x3fea491d,2 +np.float32,0x3f2afae9,0x3fcb5c1f,2 +np.float32,0xbef8e766,0x3f36c448,2 +np.float32,0xba522c00,0x3f7fdb97,2 +np.float32,0xff18ee8c,0x0,2 +np.float32,0xbee8c5f4,0x3f3acd44,2 +np.float32,0x3e790448,0x3f97802c,2 +np.float32,0x3e8c9541,0x3f9ad571,2 +np.float32,0xbf03fa9f,0x3f331460,2 +np.float32,0x801ee053,0x3f800000,2 +np.float32,0xbf773230,0x3f03167f,2 +np.float32,0x356fd9,0x3f800000,2 +np.float32,0x8009cd88,0x3f800000,2 +np.float32,0x7f2bac51,0x7f800000,2 +np.float32,0x4d9eeb,0x3f800000,2 +np.float32,0x3133,0x3f800000,2 +np.float32,0x7f4290e0,0x7f800000,2 +np.float32,0xbf5e6523,0x3f0c3161,2 +np.float32,0x3f19182e,0x3fc1bf10,2 +np.float32,0x7e1248bb,0x7f800000,2 +np.float32,0xff5f7aae,0x0,2 +np.float32,0x7e8557b5,0x7f800000,2 +np.float32,0x26fc7f,0x3f800000,2 +np.float32,0x80397d61,0x3f800000,2 +np.float32,0x3cb1825d,0x3f81efe0,2 +np.float32,0x3ed808d0,0x3fab7c45,2 +np.float32,0xbf6f668a,0x3f05e259,2 +np.float32,0x3e3c7802,0x3f916abd,2 +np.float32,0xbd5ac5a0,0x3f76b21b,2 +np.float32,0x805aa6c9,0x3f800000,2 +np.float32,0xbe4d6f68,0x3f5ec3e1,2 +np.float32,0x3f3108b2,0x3fceb87f,2 +np.float32,0x3ec385cc,0x3fa6c9fb,2 +np.float32,0xbe9fc1ce,0x3f4e35e8,2 +np.float32,0x43b68,0x3f800000,2 +np.float32,0x3ef0cdcc,0x3fb15557,2 +np.float32,0x3e3f729b,0x3f91b5e1,2 +np.float32,0x7f52a4df,0x7f800000,2 +np.float32,0xbf56da96,0x3f0f15b9,2 +np.float32,0xbf161d2b,0x3f2a7faf,2 +np.float32,0x3e8df763,0x3f9b1fbe,2 +np.float32,0xff4f0780,0x0,2 +np.float32,0x8048f594,0x3f800000,2 +np.float32,0x3e62bb1d,0x3f953b7e,2 +np.float32,0xfe58e764,0x0,2 +np.float32,0x3dd2c922,0x3f897718,2 +np.float32,0x7fa00000,0x7fe00000,2 +np.float32,0xff07b4b2,0x0,2 +np.float32,0x7f6231a0,0x7f800000,2 +np.float32,0xb8d1d,0x3f800000,2 +np.float32,0x3ee01d24,0x3fad5f16,2 +np.float32,0xbf43f59f,0x3f169869,2 +np.float32,0x801f5257,0x3f800000,2 +np.float32,0x803c15d8,0x3f800000,2 +np.float32,0x3f171a08,0x3fc0b42a,2 +np.float32,0x127aef,0x3f800000,2 +np.float32,0xfd1c6,0x3f800000,2 +np.float32,0x3f1ed13e,0x3fc4c59a,2 +np.float32,0x57fd4f,0x3f800000,2 +np.float32,0x6e8c61,0x3f800000,2 +np.float32,0x804019ab,0x3f800000,2 +np.float32,0x3ef4e5c6,0x3fb251a1,2 +np.float32,0x5044c3,0x3f800000,2 +np.float32,0x3f04460f,0x3fb7204b,2 +np.float32,0x7e326b47,0x7f800000,2 +np.float32,0x800a7e4c,0x3f800000,2 +np.float32,0xbf47ec82,0x3f14fccc,2 +np.float32,0xbedb1b3e,0x3f3e4a4d,2 +np.float32,0x3f741d86,0x3ff7e4b0,2 +np.float32,0xbe249d20,0x3f6501a6,2 +np.float32,0xbf2ea152,0x3f1f8c68,2 +np.float32,0x3ec6dbcc,0x3fa78b3f,2 +np.float32,0x7ebd9bb4,0x7f800000,2 +np.float32,0x3f61b574,0x3febd77a,2 +np.float32,0x3f3dfb2b,0x3fd61891,2 +np.float32,0x3c7d95,0x3f800000,2 +np.float32,0x8071e840,0x3f800000,2 +np.float32,0x15c6fe,0x3f800000,2 +np.float32,0xbf096601,0x3f307893,2 +np.float32,0x7f5c2ef9,0x7f800000,2 +np.float32,0xbe79f750,0x3f582689,2 +np.float32,0x1eb692,0x3f800000,2 +np.float32,0xbd8024f0,0x3f75226d,2 +np.float32,0xbf5a8be8,0x3f0da950,2 +np.float32,0xbf4d28f3,0x3f12e3e1,2 +np.float32,0x7f800000,0x7f800000,2 +np.float32,0xfea8a758,0x0,2 +np.float32,0x8075d2cf,0x3f800000,2 +np.float32,0xfd99af58,0x0,2 +np.float32,0x9e6a,0x3f800000,2 +np.float32,0x2fa19f,0x3f800000,2 +np.float32,0x3e9f4206,0x3f9ecc56,2 +np.float32,0xbee0b666,0x3f3cd9fc,2 +np.float32,0xbec558c4,0x3f43fab1,2 +np.float32,0x7e9a77df,0x7f800000,2 +np.float32,0xff3a9694,0x0,2 +np.float32,0x3f3b3708,0x3fd47f9a,2 +np.float32,0x807cd6d4,0x3f800000,2 +np.float32,0x804aa422,0x3f800000,2 +np.float32,0xfead7a70,0x0,2 +np.float32,0x3f08c610,0x3fb95efe,2 +np.float32,0xff390126,0x0,2 +np.float32,0x5d2d47,0x3f800000,2 +np.float32,0x8006849c,0x3f800000,2 +np.float32,0x654f6e,0x3f800000,2 +np.float32,0xff478a16,0x0,2 +np.float32,0x3f480b0c,0x3fdc024c,2 +np.float32,0xbc3b96c0,0x3f7df9f4,2 +np.float32,0xbcc96460,0x3f7bacb5,2 +np.float32,0x7f349f30,0x7f800000,2 +np.float32,0xbe08fa98,0x3f6954a1,2 +np.float32,0x4f3a13,0x3f800000,2 +np.float32,0x7f6a5ab4,0x7f800000,2 +np.float32,0x7eb85247,0x7f800000,2 +np.float32,0xbf287246,0x3f223e08,2 +np.float32,0x801584d0,0x3f800000,2 +np.float32,0x7ec25371,0x7f800000,2 +np.float32,0x3f002165,0x3fb51552,2 +np.float32,0x3e1108a8,0x3f8d3429,2 +np.float32,0x4f0f88,0x3f800000,2 +np.float32,0x7f67c1ce,0x7f800000,2 +np.float32,0xbf4348f8,0x3f16dedf,2 +np.float32,0xbe292b64,0x3f644d24,2 +np.float32,0xbf2bfa36,0x3f20b2d6,2 +np.float32,0xbf2a6e58,0x3f215f71,2 +np.float32,0x3e97d5d3,0x3f9d35df,2 +np.float32,0x31f597,0x3f800000,2 +np.float32,0x100544,0x3f800000,2 +np.float32,0x10a197,0x3f800000,2 +np.float32,0x3f44df50,0x3fda20d2,2 +np.float32,0x59916d,0x3f800000,2 +np.float32,0x707472,0x3f800000,2 +np.float32,0x8054194e,0x3f800000,2 +np.float32,0x80627b01,0x3f800000,2 +np.float32,0x7f4d5a5b,0x7f800000,2 +np.float32,0xbcecad00,0x3f7aeca5,2 +np.float32,0xff69c541,0x0,2 +np.float32,0xbe164e20,0x3f673c3a,2 +np.float32,0x3dd321de,0x3f897b39,2 +np.float32,0x3c9c4900,0x3f81b431,2 +np.float32,0x7f0efae3,0x7f800000,2 +np.float32,0xbf1b3ee6,0x3f282567,2 +np.float32,0x3ee858ac,0x3faf5083,2 +np.float32,0x3f0e6a39,0x3fbc3965,2 +np.float32,0x7f0c06d8,0x7f800000,2 +np.float32,0x801dd236,0x3f800000,2 +np.float32,0x564245,0x3f800000,2 +np.float32,0x7e99d3ad,0x7f800000,2 +np.float32,0xff3b0164,0x0,2 +np.float32,0x3f386f18,0x3fd2e785,2 +np.float32,0x7f603c39,0x7f800000,2 +np.float32,0x3cbd9b00,0x3f8211f0,2 +np.float32,0x2178e2,0x3f800000,2 +np.float32,0x5db226,0x3f800000,2 +np.float32,0xfec78d62,0x0,2 +np.float32,0x7f40bc1e,0x7f800000,2 +np.float32,0x80325064,0x3f800000,2 +np.float32,0x3f6068dc,0x3feb0377,2 +np.float32,0xfe8b95c6,0x0,2 +np.float32,0xbe496894,0x3f5f5f87,2 +np.float32,0xbf18722a,0x3f296cf4,2 +np.float32,0x332d0e,0x3f800000,2 +np.float32,0x3f6329dc,0x3fecc5c0,2 +np.float32,0x807d1802,0x3f800000,2 +np.float32,0x3e8afcee,0x3f9a7ff1,2 +np.float32,0x26a0a7,0x3f800000,2 +np.float32,0x7f13085d,0x7f800000,2 +np.float32,0x68d547,0x3f800000,2 +np.float32,0x7e9b04ae,0x7f800000,2 +np.float32,0x3f3ecdfe,0x3fd692ea,2 +np.float32,0x805256f4,0x3f800000,2 +np.float32,0x3f312dc8,0x3fcecd42,2 +np.float32,0x23ca15,0x3f800000,2 +np.float32,0x3f53c455,0x3fe31ad6,2 +np.float32,0xbf21186c,0x3f2580fd,2 +np.float32,0x803b9bb1,0x3f800000,2 +np.float32,0xff6ae1fc,0x0,2 +np.float32,0x2103cf,0x3f800000,2 +np.float32,0xbedcec6c,0x3f3dd29d,2 +np.float32,0x7f520afa,0x7f800000,2 +np.float32,0x7e8b44f2,0x7f800000,2 +np.float32,0xfef7f6ce,0x0,2 +np.float32,0xbd5e7c30,0x3f768a6f,2 +np.float32,0xfeb36848,0x0,2 +np.float32,0xff49effb,0x0,2 +np.float32,0xbec207c0,0x3f44dc74,2 +np.float32,0x3e91147f,0x3f9bc77f,2 +np.float32,0xfe784cd4,0x0,2 +np.float32,0xfd1a7250,0x0,2 +np.float32,0xff3b3f48,0x0,2 +np.float32,0x3f685db5,0x3ff0219f,2 +np.float32,0x3f370976,0x3fd21bae,2 +np.float32,0xfed4cc20,0x0,2 +np.float32,0xbf41e337,0x3f17714a,2 +np.float32,0xbf4e8638,0x3f12593a,2 +np.float32,0x3edaf0f1,0x3fac295e,2 +np.float32,0x803cbb4f,0x3f800000,2 +np.float32,0x7f492043,0x7f800000,2 +np.float32,0x2cabcf,0x3f800000,2 +np.float32,0x17f8ac,0x3f800000,2 +np.float32,0x3e846478,0x3f99205a,2 +np.float32,0x76948f,0x3f800000,2 +np.float32,0x1,0x3f800000,2 +np.float32,0x7ea6419e,0x7f800000,2 +np.float32,0xa5315,0x3f800000,2 +np.float32,0xff3a8e32,0x0,2 +np.float32,0xbe5714e8,0x3f5d50b7,2 +np.float32,0xfeadf960,0x0,2 +np.float32,0x3ebbd1a8,0x3fa50efc,2 +np.float32,0x7f31dce7,0x7f800000,2 +np.float32,0x80314999,0x3f800000,2 +np.float32,0x8017f41b,0x3f800000,2 +np.float32,0x7ed6d051,0x7f800000,2 +np.float32,0x7f525688,0x7f800000,2 +np.float32,0x7f7fffff,0x7f800000,2 +np.float32,0x3e8b0461,0x3f9a8180,2 +np.float32,0x3d9fe46e,0x3f871e1f,2 +np.float32,0x5e6d8f,0x3f800000,2 +np.float32,0xbf09ae55,0x3f305608,2 +np.float32,0xfe7028c4,0x0,2 +np.float32,0x7f3ade56,0x7f800000,2 +np.float32,0xff4c9ef9,0x0,2 +np.float32,0x7e3199cf,0x7f800000,2 +np.float32,0x8048652f,0x3f800000,2 +np.float32,0x805e1237,0x3f800000,2 +np.float32,0x189ed8,0x3f800000,2 +np.float32,0xbea7c094,0x3f4bfd98,2 +np.float32,0xbf2f109c,0x3f1f5c5c,2 +np.float32,0xbf0e7f4c,0x3f2e0d2c,2 +np.float32,0x8005981f,0x3f800000,2 +np.float32,0xbf762005,0x3f0377f3,2 +np.float32,0xbf0f60ab,0x3f2da317,2 +np.float32,0xbf4aa3e7,0x3f13e54e,2 +np.float32,0xbf348fd2,0x3f1d01aa,2 +np.float32,0x3e530b50,0x3f93a7fb,2 +np.float32,0xbf0b05a4,0x3f2fb26a,2 +np.float32,0x3eea416c,0x3fafc4aa,2 +np.float32,0x805ad04d,0x3f800000,2 +np.float32,0xbf6328d8,0x3f0a655e,2 +np.float32,0x3f7347b9,0x3ff75558,2 +np.float32,0xfda3ca68,0x0,2 +np.float32,0x80497d21,0x3f800000,2 +np.float32,0x3e740452,0x3f96fd22,2 +np.float32,0x3e528e57,0x3f939b7e,2 +np.float32,0x3e9e19fa,0x3f9e8cbd,2 +np.float32,0x8078060b,0x3f800000,2 +np.float32,0x3f3fea7a,0x3fd73872,2 +np.float32,0xfcfa30a0,0x0,2 +np.float32,0x7f4eb4bf,0x7f800000,2 +np.float32,0x3f712618,0x3ff5e900,2 +np.float32,0xbf668f0e,0x3f0920c6,2 +np.float32,0x3f3001e9,0x3fce259d,2 +np.float32,0xbe9b6fac,0x3f4f6b9c,2 +np.float32,0xbf61fcf3,0x3f0ad5ec,2 +np.float32,0xff08a55c,0x0,2 +np.float32,0x3e805014,0x3f984872,2 +np.float32,0x6ce04c,0x3f800000,2 +np.float32,0x7f7cbc07,0x7f800000,2 +np.float32,0x3c87dc,0x3f800000,2 +np.float32,0x3f2ee498,0x3fcd869a,2 +np.float32,0x4b1116,0x3f800000,2 +np.float32,0x3d382d06,0x3f840d5f,2 +np.float32,0xff7de21e,0x0,2 +np.float32,0x3f2f1d6d,0x3fcda63c,2 +np.float32,0xbf1c1618,0x3f27c38a,2 +np.float32,0xff4264b1,0x0,2 +np.float32,0x8026e5e7,0x3f800000,2 +np.float32,0xbe6fa180,0x3f59ab02,2 +np.float32,0xbe923c02,0x3f52053b,2 +np.float32,0xff3aa453,0x0,2 +np.float32,0x3f77a7ac,0x3ffa47d0,2 +np.float32,0xbed15f36,0x3f40d08a,2 +np.float32,0xa62d,0x3f800000,2 +np.float32,0xbf342038,0x3f1d3123,2 +np.float32,0x7f2f7f80,0x7f800000,2 +np.float32,0x7f2b6fc1,0x7f800000,2 +np.float32,0xff323540,0x0,2 +np.float32,0x3f1a2b6e,0x3fc24faa,2 +np.float32,0x800cc1d2,0x3f800000,2 +np.float32,0xff38fa01,0x0,2 +np.float32,0x80800000,0x3f800000,2 +np.float32,0xbf3d22e0,0x3f196745,2 +np.float32,0x7f40fd62,0x7f800000,2 +np.float32,0x7e1785c7,0x7f800000,2 +np.float32,0x807408c4,0x3f800000,2 +np.float32,0xbf300192,0x3f1ef485,2 +np.float32,0x351e3d,0x3f800000,2 +np.float32,0x7f5ab736,0x7f800000,2 +np.float32,0x2f1696,0x3f800000,2 +np.float32,0x806ac5d7,0x3f800000,2 +np.float32,0x42ec59,0x3f800000,2 +np.float32,0x7f79f52d,0x7f800000,2 +np.float32,0x44ad28,0x3f800000,2 +np.float32,0xbf49dc9c,0x3f143532,2 +np.float32,0x3f6c1f1f,0x3ff295e7,2 +np.float32,0x1589b3,0x3f800000,2 +np.float32,0x3f49b44e,0x3fdd0031,2 +np.float32,0x7f5942c9,0x7f800000,2 +np.float32,0x3f2dab28,0x3fccd877,2 +np.float32,0xff7fffff,0x0,2 +np.float32,0x80578eb2,0x3f800000,2 +np.float32,0x3f39ba67,0x3fd3a50b,2 +np.float32,0x8020340d,0x3f800000,2 +np.float32,0xbf6025b2,0x3f0b8783,2 +np.float32,0x8015ccfe,0x3f800000,2 +np.float32,0x3f6b9762,0x3ff23cd0,2 +np.float32,0xfeeb0c86,0x0,2 +np.float32,0x802779bc,0x3f800000,2 +np.float32,0xbf32bf64,0x3f1dc796,2 +np.float32,0xbf577eb6,0x3f0ed631,2 +np.float32,0x0,0x3f800000,2 +np.float32,0xfe99de6c,0x0,2 +np.float32,0x7a4e53,0x3f800000,2 +np.float32,0x1a15d3,0x3f800000,2 +np.float32,0x8035fe16,0x3f800000,2 +np.float32,0x3e845784,0x3f991dab,2 +np.float32,0x43d688,0x3f800000,2 +np.float32,0xbd447cc0,0x3f77a0b7,2 +np.float32,0x3f83fa,0x3f800000,2 +np.float32,0x3f141df2,0x3fbf2719,2 +np.float32,0x805c586a,0x3f800000,2 +np.float32,0x14c47e,0x3f800000,2 +np.float32,0x3d3bed00,0x3f8422d4,2 +np.float32,0x7f6f4ecd,0x7f800000,2 +np.float32,0x3f0a5e5a,0x3fba2c5c,2 +np.float32,0x523ecf,0x3f800000,2 +np.float32,0xbef4a6e8,0x3f37d262,2 +np.float32,0xff54eb58,0x0,2 +np.float32,0xff3fc875,0x0,2 +np.float32,0x8067c392,0x3f800000,2 +np.float32,0xfedae910,0x0,2 +np.float32,0x80595979,0x3f800000,2 +np.float32,0x3ee87d1d,0x3faf5929,2 +np.float32,0x7f5bad33,0x7f800000,2 +np.float32,0xbf45b868,0x3f15e109,2 +np.float32,0x3ef2277d,0x3fb1a868,2 +np.float32,0x3ca5a950,0x3f81ce8c,2 +np.float32,0x3e70f4e6,0x3f96ad25,2 +np.float32,0xfe3515bc,0x0,2 +np.float32,0xfe4af088,0x0,2 +np.float32,0xff3c78b2,0x0,2 +np.float32,0x7f50f51a,0x7f800000,2 +np.float32,0x3e3a232a,0x3f913009,2 +np.float32,0x7dfec6ff,0x7f800000,2 +np.float32,0x3e1bbaec,0x3f8e3ad6,2 +np.float32,0xbd658fa0,0x3f763ee7,2 +np.float32,0xfe958684,0x0,2 +np.float32,0x503670,0x3f800000,2 +np.float32,0x3f800000,0x40000000,2 +np.float32,0x1bbec6,0x3f800000,2 +np.float32,0xbea7bb7c,0x3f4bff00,2 +np.float32,0xff3a24a2,0x0,2 +np.float32,0xbf416240,0x3f17a635,2 +np.float32,0xbf800000,0x3f000000,2 +np.float32,0xff0c965c,0x0,2 +np.float32,0x80000000,0x3f800000,2 +np.float32,0xbec2c69a,0x3f44a99e,2 +np.float32,0x5b68d4,0x3f800000,2 +np.float32,0xb9a93000,0x3f7ff158,2 +np.float32,0x3d5a0dd8,0x3f84cfbc,2 +np.float32,0xbeaf7a28,0x3f49de4e,2 +np.float32,0x3ee83555,0x3faf4820,2 +np.float32,0xfd320330,0x0,2 +np.float32,0xe1af2,0x3f800000,2 +np.float32,0x7cf28caf,0x7f800000,2 +np.float32,0x80781009,0x3f800000,2 +np.float32,0xbf1e0baf,0x3f26e04d,2 +np.float32,0x7edb05b1,0x7f800000,2 +np.float32,0x3de004,0x3f800000,2 +np.float32,0xff436af6,0x0,2 +np.float32,0x802a9408,0x3f800000,2 +np.float32,0x7ed82205,0x7f800000,2 +np.float32,0x3e3f8212,0x3f91b767,2 +np.float32,0x16a2b2,0x3f800000,2 +np.float32,0xff1e5af3,0x0,2 +np.float32,0xbf1c860c,0x3f2790b7,2 +np.float32,0x3f3bc5da,0x3fd4d1d6,2 +np.float32,0x7f5f7085,0x7f800000,2 +np.float32,0x7f68e409,0x7f800000,2 +np.float32,0x7f4b3388,0x7f800000,2 +np.float32,0x7ecaf440,0x7f800000,2 +np.float32,0x80078785,0x3f800000,2 +np.float32,0x3ebd800d,0x3fa56f45,2 +np.float32,0xbe39a140,0x3f61c58e,2 +np.float32,0x803b587e,0x3f800000,2 +np.float32,0xbeaaa418,0x3f4b31c4,2 +np.float32,0xff7e2b9f,0x0,2 +np.float32,0xff5180a3,0x0,2 +np.float32,0xbf291394,0x3f21f73c,2 +np.float32,0x7f7b9698,0x7f800000,2 +np.float32,0x4218da,0x3f800000,2 +np.float32,0x7f135262,0x7f800000,2 +np.float32,0x804c10e8,0x3f800000,2 +np.float32,0xbf1c2a54,0x3f27ba5a,2 +np.float32,0x7f41fd32,0x7f800000,2 +np.float32,0x3e5cc464,0x3f94a195,2 +np.float32,0xff7a2fa7,0x0,2 +np.float32,0x3e05dc30,0x3f8c23c9,2 +np.float32,0x7f206d99,0x7f800000,2 +np.float32,0xbe9ae520,0x3f4f9287,2 +np.float32,0xfe4f4d58,0x0,2 +np.float32,0xbf44db42,0x3f163ae3,2 +np.float32,0x3f65ac48,0x3fee6300,2 +np.float32,0x3ebfaf36,0x3fa5ecb0,2 +np.float32,0x3f466719,0x3fdb08b0,2 +np.float32,0x80000001,0x3f800000,2 +np.float32,0xff4b3c7b,0x0,2 +np.float32,0x3df44374,0x3f8b0819,2 +np.float32,0xfea4b540,0x0,2 +np.float32,0x7f358e3d,0x7f800000,2 +np.float32,0x801f5e63,0x3f800000,2 +np.float32,0x804ae77e,0x3f800000,2 +np.float32,0xdbb5,0x3f800000,2 +np.float32,0x7f0a7e3b,0x7f800000,2 +np.float32,0xbe4152e4,0x3f609953,2 +np.float32,0x4b9579,0x3f800000,2 +np.float32,0x3ece0bd4,0x3fa92ea5,2 +np.float32,0x7e499d9a,0x7f800000,2 +np.float32,0x80637d8a,0x3f800000,2 +np.float32,0x3e50a425,0x3f936a8b,2 +np.float32,0xbf0e8cb0,0x3f2e06dd,2 +np.float32,0x802763e2,0x3f800000,2 +np.float32,0xff73041b,0x0,2 +np.float32,0xfea466da,0x0,2 +np.float32,0x80064c73,0x3f800000,2 +np.float32,0xbef29222,0x3f385728,2 +np.float32,0x8029c215,0x3f800000,2 +np.float32,0xbd3994e0,0x3f7815d1,2 +np.float32,0xbe6ac9e4,0x3f5a61f3,2 +np.float32,0x804b58b0,0x3f800000,2 +np.float32,0xbdb83be0,0x3f70865c,2 +np.float32,0x7ee18da2,0x7f800000,2 +np.float32,0xfd4ca010,0x0,2 +np.float32,0x807c668b,0x3f800000,2 +np.float32,0xbd40ed90,0x3f77c6e9,2 +np.float32,0x7efc6881,0x7f800000,2 +np.float32,0xfe633bfc,0x0,2 +np.float32,0x803ce363,0x3f800000,2 +np.float32,0x7ecba81e,0x7f800000,2 +np.float32,0xfdcb2378,0x0,2 +np.float32,0xbebc5524,0x3f4662b2,2 +np.float32,0xfaa30000,0x0,2 +np.float32,0x805d451b,0x3f800000,2 +np.float32,0xbee85600,0x3f3ae996,2 +np.float32,0xfefb0a54,0x0,2 +np.float32,0xbdfc6690,0x3f6b0a08,2 +np.float32,0x58a57,0x3f800000,2 +np.float32,0x3b41b7,0x3f800000,2 +np.float32,0x7c99812d,0x7f800000,2 +np.float32,0xbd3ae740,0x3f78079d,2 +np.float32,0xbf4a48a7,0x3f1409dd,2 +np.float32,0xfdeaad58,0x0,2 +np.float32,0xbe9aa65a,0x3f4fa42c,2 +np.float32,0x3f79d78c,0x3ffbc458,2 +np.float32,0x805e7389,0x3f800000,2 +np.float32,0x7ebb3612,0x7f800000,2 +np.float32,0x2e27dc,0x3f800000,2 +np.float32,0x80726dec,0x3f800000,2 +np.float32,0xfe8fb738,0x0,2 +np.float32,0xff1ff3bd,0x0,2 +np.float32,0x7f5264a2,0x7f800000,2 +np.float32,0x3f5a6893,0x3fe739ca,2 +np.float32,0xbec4029c,0x3f44558d,2 +np.float32,0xbef65cfa,0x3f37657e,2 +np.float32,0x63aba1,0x3f800000,2 +np.float32,0xfbb6e200,0x0,2 +np.float32,0xbf3466fc,0x3f1d1307,2 +np.float32,0x3f258844,0x3fc861d7,2 +np.float32,0xbf5f29a7,0x3f0be6dc,2 +np.float32,0x802b51cd,0x3f800000,2 +np.float32,0xbe9094dc,0x3f527dae,2 +np.float32,0xfec2e68c,0x0,2 +np.float32,0x807b38bd,0x3f800000,2 +np.float32,0xbf594662,0x3f0e2663,2 +np.float32,0x7cbcf747,0x7f800000,2 +np.float32,0xbe4b88f0,0x3f5f0d47,2 +np.float32,0x3c53c4,0x3f800000,2 +np.float32,0xbe883562,0x3f54e3f7,2 +np.float32,0xbf1efaf0,0x3f267456,2 +np.float32,0x3e22cd3e,0x3f8ee98b,2 +np.float32,0x80434875,0x3f800000,2 +np.float32,0xbf000b44,0x3f34ff6e,2 +np.float32,0x7f311c3a,0x7f800000,2 +np.float32,0x802f7f3f,0x3f800000,2 +np.float32,0x805155fe,0x3f800000,2 +np.float32,0x7f5d7485,0x7f800000,2 +np.float32,0x80119197,0x3f800000,2 +np.float32,0x3f445b8b,0x3fd9d30d,2 +np.float32,0xbf638eb3,0x3f0a3f38,2 +np.float32,0x402410,0x3f800000,2 +np.float32,0xbc578a40,0x3f7dad1d,2 +np.float32,0xbeecbf8a,0x3f39cc9e,2 +np.float32,0x7f2935a4,0x7f800000,2 +np.float32,0x3f570fea,0x3fe523e2,2 +np.float32,0xbf06bffa,0x3f31bdb6,2 +np.float32,0xbf2afdfd,0x3f2120ba,2 +np.float32,0x7f76f7ab,0x7f800000,2 +np.float32,0xfee2d1e8,0x0,2 +np.float32,0x800b026d,0x3f800000,2 +np.float32,0xff0eda75,0x0,2 +np.float32,0x3d4c,0x3f800000,2 +np.float32,0xbed538a2,0x3f3fcffb,2 +np.float32,0x3f73f4f9,0x3ff7c979,2 +np.float32,0x2aa9fc,0x3f800000,2 +np.float32,0x806a45b3,0x3f800000,2 +np.float32,0xff770d35,0x0,2 +np.float32,0x7e999be3,0x7f800000,2 +np.float32,0x80741128,0x3f800000,2 +np.float32,0xff6aac34,0x0,2 +np.float32,0x470f74,0x3f800000,2 +np.float32,0xff423b7b,0x0,2 +np.float32,0x17dfdd,0x3f800000,2 +np.float32,0x7f029e12,0x7f800000,2 +np.float32,0x803fcb9d,0x3f800000,2 +np.float32,0x3f3dc3,0x3f800000,2 +np.float32,0x7f3a27bc,0x7f800000,2 +np.float32,0x3e473108,0x3f9279ec,2 +np.float32,0x7f4add5d,0x7f800000,2 +np.float32,0xfd9736e0,0x0,2 +np.float32,0x805f1df2,0x3f800000,2 +np.float32,0x6c49c1,0x3f800000,2 +np.float32,0x7ec733c7,0x7f800000,2 +np.float32,0x804c1abf,0x3f800000,2 +np.float32,0x3de2e887,0x3f8a37a5,2 +np.float32,0x3f51630a,0x3fe1a561,2 +np.float32,0x3de686a8,0x3f8a62ff,2 +np.float32,0xbedb3538,0x3f3e439c,2 +np.float32,0xbf3aa892,0x3f1a6f9e,2 +np.float32,0x7ee5fb32,0x7f800000,2 +np.float32,0x7e916c9b,0x7f800000,2 +np.float32,0x3f033f1c,0x3fb69e19,2 +np.float32,0x25324b,0x3f800000,2 +np.float32,0x3f348d1d,0x3fd0b2e2,2 +np.float32,0x3f5797e8,0x3fe57851,2 +np.float32,0xbf69c316,0x3f07f1a0,2 +np.float32,0xbe8b7fb0,0x3f53f1bf,2 +np.float32,0xbdbbc190,0x3f703d00,2 +np.float32,0xff6c4fc0,0x0,2 +np.float32,0x7f29fcbe,0x7f800000,2 +np.float32,0x3f678d19,0x3fef9a23,2 +np.float32,0x73d140,0x3f800000,2 +np.float32,0x3e25bdd2,0x3f8f326b,2 +np.float32,0xbeb775ec,0x3f47b2c6,2 +np.float32,0xff451c4d,0x0,2 +np.float32,0x8072c466,0x3f800000,2 +np.float32,0x3f65e836,0x3fee89b2,2 +np.float32,0x52ca7a,0x3f800000,2 +np.float32,0x62cfed,0x3f800000,2 +np.float32,0xbf583dd0,0x3f0e8c5c,2 +np.float32,0xbf683842,0x3f088342,2 +np.float32,0x3f1a7828,0x3fc2780c,2 +np.float32,0x800ea979,0x3f800000,2 +np.float32,0xbeb9133c,0x3f474328,2 +np.float32,0x3ef09fc7,0x3fb14a4b,2 +np.float32,0x7ebbcb75,0x7f800000,2 +np.float32,0xff316c0e,0x0,2 +np.float32,0x805b84e3,0x3f800000,2 +np.float32,0x3d6a55e0,0x3f852d8a,2 +np.float32,0x3e755788,0x3f971fd1,2 +np.float32,0x3ee7aacb,0x3faf2743,2 +np.float32,0x7f714039,0x7f800000,2 +np.float32,0xff70bad8,0x0,2 +np.float32,0xbe0b74c8,0x3f68f08c,2 +np.float32,0xbf6cb170,0x3f06de86,2 +np.float32,0x7ec1fbff,0x7f800000,2 +np.float32,0x8014b1f6,0x3f800000,2 +np.float32,0xfe8b45fe,0x0,2 +np.float32,0x6e2220,0x3f800000,2 +np.float32,0x3ed1777d,0x3fa9f7ab,2 +np.float32,0xff48e467,0x0,2 +np.float32,0xff76c5aa,0x0,2 +np.float32,0x3e9bd330,0x3f9e0fd7,2 +np.float32,0x3f17de4f,0x3fc11aae,2 +np.float32,0x7eeaa2fd,0x7f800000,2 +np.float32,0xbf572746,0x3f0ef806,2 +np.float32,0x7e235554,0x7f800000,2 +np.float32,0xfe24fc1c,0x0,2 +np.float32,0x7daf71ad,0x7f800000,2 +np.float32,0x800d4a6b,0x3f800000,2 +np.float32,0xbf6fc31d,0x3f05c0ce,2 +np.float32,0x1c4d93,0x3f800000,2 +np.float32,0x7ee9200c,0x7f800000,2 +np.float32,0x3f54b4da,0x3fe3aeec,2 +np.float32,0x2b37b1,0x3f800000,2 +np.float32,0x3f7468bd,0x3ff81731,2 +np.float32,0x3f2850ea,0x3fc9e5f4,2 +np.float32,0xbe0d47ac,0x3f68a6f9,2 +np.float32,0x314877,0x3f800000,2 +np.float32,0x802700c3,0x3f800000,2 +np.float32,0x7e2c915f,0x7f800000,2 +np.float32,0x800d0059,0x3f800000,2 +np.float32,0x3f7f3c25,0x3fff7862,2 +np.float32,0xff735d31,0x0,2 +np.float32,0xff7e339e,0x0,2 +np.float32,0xbef96cf0,0x3f36a340,2 +np.float32,0x3db6ea21,0x3f882cb2,2 +np.float32,0x67cb3d,0x3f800000,2 +np.float32,0x801f349d,0x3f800000,2 +np.float32,0x3f1390ec,0x3fbede29,2 +np.float32,0x7f13644a,0x7f800000,2 +np.float32,0x804a369b,0x3f800000,2 +np.float32,0x80262666,0x3f800000,2 +np.float32,0x7e850fbc,0x7f800000,2 +np.float32,0x18b002,0x3f800000,2 +np.float32,0x8051f1ed,0x3f800000,2 +np.float32,0x3eba48f6,0x3fa4b753,2 +np.float32,0xbf3f4130,0x3f1886a9,2 +np.float32,0xbedac006,0x3f3e61cf,2 +np.float32,0xbf097c70,0x3f306ddc,2 +np.float32,0x4aba6d,0x3f800000,2 +np.float32,0x580078,0x3f800000,2 +np.float32,0x3f64d82e,0x3fedda40,2 +np.float32,0x7f781fd6,0x7f800000,2 +np.float32,0x6aff3d,0x3f800000,2 +np.float32,0xff25e074,0x0,2 +np.float32,0x7ea9ec89,0x7f800000,2 +np.float32,0xbf63b816,0x3f0a2fbb,2 +np.float32,0x133f07,0x3f800000,2 +np.float32,0xff800000,0x0,2 +np.float32,0x8013dde7,0x3f800000,2 +np.float32,0xff770b95,0x0,2 +np.float32,0x806154e8,0x3f800000,2 +np.float32,0x3f1e7bce,0x3fc4981a,2 +np.float32,0xff262c78,0x0,2 +np.float32,0x3f59a652,0x3fe6c04c,2 +np.float32,0x7f220166,0x7f800000,2 +np.float32,0x7eb24939,0x7f800000,2 +np.float32,0xbed58bb0,0x3f3fba6a,2 +np.float32,0x3c2ad000,0x3f80eda7,2 +np.float32,0x2adb2e,0x3f800000,2 +np.float32,0xfe8b213e,0x0,2 +np.float32,0xbf2e0c1e,0x3f1fccea,2 +np.float32,0x7e1716be,0x7f800000,2 +np.float32,0x80184e73,0x3f800000,2 +np.float32,0xbf254743,0x3f23a3d5,2 +np.float32,0x8063a722,0x3f800000,2 +np.float32,0xbe50adf0,0x3f5e46c7,2 +np.float32,0x3f614158,0x3feb8d60,2 +np.float32,0x8014bbc8,0x3f800000,2 +np.float32,0x283bc7,0x3f800000,2 +np.float32,0x3ffb5c,0x3f800000,2 +np.float32,0xfe8de6bc,0x0,2 +np.float32,0xbea6e086,0x3f4c3b82,2 +np.float32,0xfee64b92,0x0,2 +np.float32,0x506c1a,0x3f800000,2 +np.float32,0xff342af8,0x0,2 +np.float32,0x6b6f4c,0x3f800000,2 +np.float32,0xfeb42b1e,0x0,2 +np.float32,0x3e49384a,0x3f92ad71,2 +np.float32,0x152d08,0x3f800000,2 +np.float32,0x804c8f09,0x3f800000,2 +np.float32,0xff5e927d,0x0,2 +np.float32,0x6374da,0x3f800000,2 +np.float32,0x3f48f011,0x3fdc8ae4,2 +np.float32,0xbf446a30,0x3f1668e8,2 +np.float32,0x3ee77073,0x3faf196e,2 +np.float32,0xff4caa40,0x0,2 +np.float32,0x7efc9363,0x7f800000,2 +np.float32,0xbf706dcc,0x3f05830d,2 +np.float32,0xfe29c7e8,0x0,2 +np.float32,0x803cfe58,0x3f800000,2 +np.float32,0x3ec34c7c,0x3fa6bd0a,2 +np.float32,0x3eb85b62,0x3fa44968,2 +np.float32,0xfda1b9d8,0x0,2 +np.float32,0x802932cd,0x3f800000,2 +np.float32,0xbf5cde78,0x3f0cc5fa,2 +np.float32,0x3f31bf44,0x3fcf1ec8,2 +np.float32,0x803a0882,0x3f800000,2 +np.float32,0x800000,0x3f800000,2 +np.float32,0x3f54110e,0x3fe34a08,2 +np.float32,0x80645ea9,0x3f800000,2 +np.float32,0xbd8c1070,0x3f7425c3,2 +np.float32,0x801a006a,0x3f800000,2 +np.float32,0x7f5d161e,0x7f800000,2 +np.float32,0x805b5df3,0x3f800000,2 +np.float32,0xbf71a7c0,0x3f0511be,2 +np.float32,0xbe9a55c0,0x3f4fbad6,2 +np.float64,0xde7e2fd9bcfc6,0x3ff0000000000000,2 +np.float64,0xbfd8cd88eb319b12,0x3fe876349efbfa2b,2 +np.float64,0x3fe4fa13ace9f428,0x3ff933fbb117d196,2 +np.float64,0x475b3d048eb68,0x3ff0000000000000,2 +np.float64,0x7fef39ed07be73d9,0x7ff0000000000000,2 +np.float64,0x80026b84d904d70a,0x3ff0000000000000,2 +np.float64,0xebd60627d7ac1,0x3ff0000000000000,2 +np.float64,0xbfd7cbefdbaf97e0,0x3fe8bad30f6cf8e1,2 +np.float64,0x7fc17c605a22f8c0,0x7ff0000000000000,2 +np.float64,0x8cdac05119b58,0x3ff0000000000000,2 +np.float64,0x3fc45cd60a28b9ac,0x3ff1dd8028ec3f41,2 +np.float64,0x7fef4fce137e9f9b,0x7ff0000000000000,2 +np.float64,0xe5a2b819cb457,0x3ff0000000000000,2 +np.float64,0xe3bcfd4dc77a0,0x3ff0000000000000,2 +np.float64,0x68f0b670d1e17,0x3ff0000000000000,2 +np.float64,0xae69a6455cd35,0x3ff0000000000000,2 +np.float64,0xffe7007a0c6e00f4,0x0,2 +np.float64,0x59fc57a8b3f8c,0x3ff0000000000000,2 +np.float64,0xbfeee429c0bdc854,0x3fe0638fa62bed9f,2 +np.float64,0x80030bb6e206176f,0x3ff0000000000000,2 +np.float64,0x8006967a36ad2cf5,0x3ff0000000000000,2 +np.float64,0x3fe128176a22502f,0x3ff73393301e5dc8,2 +np.float64,0x218de20c431bd,0x3ff0000000000000,2 +np.float64,0x3fe7dbc48aafb789,0x3ffad38989b5955c,2 +np.float64,0xffda1ef411343de8,0x0,2 +np.float64,0xc6b392838d673,0x3ff0000000000000,2 +np.float64,0x7fe6d080c1ada101,0x7ff0000000000000,2 +np.float64,0xbfed36dd67fa6dbb,0x3fe0fec342c4ee89,2 +np.float64,0x3fee2bb6a3fc576e,0x3ffec1c149f1f092,2 +np.float64,0xbfd1f785eb23ef0c,0x3fea576eb01233cb,2 +np.float64,0x7fdad29a1f35a533,0x7ff0000000000000,2 +np.float64,0xffe8928c4fb12518,0x0,2 +np.float64,0x7fb123160022462b,0x7ff0000000000000,2 +np.float64,0x8007ab56cfaf56ae,0x3ff0000000000000,2 +np.float64,0x7fda342d6634685a,0x7ff0000000000000,2 +np.float64,0xbfe3b7e42c676fc8,0x3fe4e05cf8685b8a,2 +np.float64,0xffa708be7c2e1180,0x0,2 +np.float64,0xbfe8ffbece31ff7e,0x3fe29eb84077a34a,2 +np.float64,0xbf91002008220040,0x3fefa245058f05cb,2 +np.float64,0x8000281f0ee0503f,0x3ff0000000000000,2 +np.float64,0x8005617adc2ac2f6,0x3ff0000000000000,2 +np.float64,0x7fa84fec60309fd8,0x7ff0000000000000,2 +np.float64,0x8d00c0231a018,0x3ff0000000000000,2 +np.float64,0xbfdfe52ca63fca5a,0x3fe6a7324cc00d57,2 +np.float64,0x7fcc81073d39020d,0x7ff0000000000000,2 +np.float64,0x800134ff5a6269ff,0x3ff0000000000000,2 +np.float64,0xffc7fff98d2ffff4,0x0,2 +np.float64,0x8000925ce50124bb,0x3ff0000000000000,2 +np.float64,0xffe2530c66a4a618,0x0,2 +np.float64,0x7fc99070673320e0,0x7ff0000000000000,2 +np.float64,0xbfddd5c1f13bab84,0x3fe72a0c80f8df39,2 +np.float64,0x3fe1c220fee38442,0x3ff7817ec66aa55b,2 +np.float64,0x3fb9a1e1043343c2,0x3ff1265e575e6404,2 +np.float64,0xffef72e0833ee5c0,0x0,2 +np.float64,0x3fe710c0416e2181,0x3ffa5e93588aaa69,2 +np.float64,0xbfd8d23cbab1a47a,0x3fe874f5b9d99885,2 +np.float64,0x7fe9628ebd72c51c,0x7ff0000000000000,2 +np.float64,0xdd5fa611babf5,0x3ff0000000000000,2 +np.float64,0x8002bafac86575f6,0x3ff0000000000000,2 +np.float64,0x68acea44d159e,0x3ff0000000000000,2 +np.float64,0xffd776695eaeecd2,0x0,2 +np.float64,0x80059b59bb4b36b4,0x3ff0000000000000,2 +np.float64,0xbdcdd2af7b9bb,0x3ff0000000000000,2 +np.float64,0x8002b432ee856867,0x3ff0000000000000,2 +np.float64,0xcbc72f09978e6,0x3ff0000000000000,2 +np.float64,0xbfee8f4bf6fd1e98,0x3fe081cc0318b170,2 +np.float64,0xffc6e2892d2dc514,0x0,2 +np.float64,0x7feb682e4db6d05c,0x7ff0000000000000,2 +np.float64,0x8004b70a04296e15,0x3ff0000000000000,2 +np.float64,0x42408a4284812,0x3ff0000000000000,2 +np.float64,0xbfe9b8b197f37163,0x3fe254b4c003ce0a,2 +np.float64,0x3fcaadf5f5355bec,0x3ff27ca7876a8d20,2 +np.float64,0xfff0000000000000,0x0,2 +np.float64,0x7fea8376d33506ed,0x7ff0000000000000,2 +np.float64,0xffef73c2d63ee785,0x0,2 +np.float64,0xffe68b2bae2d1657,0x0,2 +np.float64,0x3fd8339cb2306739,0x3ff4cb774d616f90,2 +np.float64,0xbfc6d1db4d2da3b8,0x3fec47bb873a309c,2 +np.float64,0x7fe858016230b002,0x7ff0000000000000,2 +np.float64,0x7fe74cb99d2e9972,0x7ff0000000000000,2 +np.float64,0xffec2e96dc385d2d,0x0,2 +np.float64,0xb762a9876ec55,0x3ff0000000000000,2 +np.float64,0x3feca230c5794462,0x3ffdbfe62a572f52,2 +np.float64,0xbfb5ebad3a2bd758,0x3fee27eed86dcc39,2 +np.float64,0x471c705a8e38f,0x3ff0000000000000,2 +np.float64,0x7fc79bb5cf2f376b,0x7ff0000000000000,2 +np.float64,0xbfe53d6164ea7ac3,0x3fe4331b3beb73bd,2 +np.float64,0xbfe375a3f766eb48,0x3fe4fe67edb516e6,2 +np.float64,0x3fe1c7686ca38ed1,0x3ff7842f04770ba9,2 +np.float64,0x242e74dc485cf,0x3ff0000000000000,2 +np.float64,0x8009c06ab71380d6,0x3ff0000000000000,2 +np.float64,0x3fd08505efa10a0c,0x3ff3227b735b956d,2 +np.float64,0xffe3dfcecda7bf9d,0x0,2 +np.float64,0x8001f079bbc3e0f4,0x3ff0000000000000,2 +np.float64,0x3fddc706b6bb8e0c,0x3ff616d927987363,2 +np.float64,0xbfd151373ea2a26e,0x3fea870ba53ec126,2 +np.float64,0x7fe89533bfb12a66,0x7ff0000000000000,2 +np.float64,0xffed302cbc3a6059,0x0,2 +np.float64,0x3fd871cc28b0e398,0x3ff4d97d58c16ae2,2 +np.float64,0x7fbe9239683d2472,0x7ff0000000000000,2 +np.float64,0x848a445909149,0x3ff0000000000000,2 +np.float64,0x8007b104ce2f620a,0x3ff0000000000000,2 +np.float64,0x7fc2cd6259259ac4,0x7ff0000000000000,2 +np.float64,0xbfeadb640df5b6c8,0x3fe1e2b068de10af,2 +np.float64,0x800033b2f1a06767,0x3ff0000000000000,2 +np.float64,0x7fe54e5b7caa9cb6,0x7ff0000000000000,2 +np.float64,0x4f928f209f26,0x3ff0000000000000,2 +np.float64,0x8003c3dc6f2787ba,0x3ff0000000000000,2 +np.float64,0xbfd55a59daaab4b4,0x3fe9649d57b32b5d,2 +np.float64,0xffe3e2968d67c52c,0x0,2 +np.float64,0x80087434d550e86a,0x3ff0000000000000,2 +np.float64,0xffdde800083bd000,0x0,2 +np.float64,0xffe291f0542523e0,0x0,2 +np.float64,0xbfe1419bc3e28338,0x3fe6051d4f95a34a,2 +np.float64,0x3fd9d00ee1b3a01e,0x3ff5292bb8d5f753,2 +np.float64,0x3fdb720b60b6e417,0x3ff589d133625374,2 +np.float64,0xbfe3e21f0967c43e,0x3fe4cd4d02e3ef9a,2 +np.float64,0x7fd7e27f3dafc4fd,0x7ff0000000000000,2 +np.float64,0x3fd1cc2620a3984c,0x3ff366befbc38e3e,2 +np.float64,0x3fe78d05436f1a0b,0x3ffaa5ee4ea54b79,2 +np.float64,0x7e2acc84fc55a,0x3ff0000000000000,2 +np.float64,0x800ffb861c5ff70c,0x3ff0000000000000,2 +np.float64,0xffb2b0db1a2561b8,0x0,2 +np.float64,0xbfe80c2363701847,0x3fe301fdfe789576,2 +np.float64,0x7fe383c1c3e70783,0x7ff0000000000000,2 +np.float64,0xbfeefc02e6fdf806,0x3fe05b1a8528bf6c,2 +np.float64,0xbfe42c9268285925,0x3fe4abdc14793cb8,2 +np.float64,0x1,0x3ff0000000000000,2 +np.float64,0xa71c7ce94e390,0x3ff0000000000000,2 +np.float64,0x800ed4e6777da9cd,0x3ff0000000000000,2 +np.float64,0x3fde11b35d3c2367,0x3ff628bdc6dd1b78,2 +np.float64,0x3fef3964dbfe72ca,0x3fff777cae357608,2 +np.float64,0x3fefe369b7ffc6d4,0x3fffec357be508a3,2 +np.float64,0xbfdef1855f3de30a,0x3fe6e348c58e3fed,2 +np.float64,0x3fee0e2bc13c1c58,0x3ffeae1909c1b973,2 +np.float64,0xbfd31554ffa62aaa,0x3fea06628b2f048a,2 +np.float64,0x800dc56bcc7b8ad8,0x3ff0000000000000,2 +np.float64,0x7fbba01b8e374036,0x7ff0000000000000,2 +np.float64,0x7fd9737a92b2e6f4,0x7ff0000000000000,2 +np.float64,0x3feeae0fac3d5c1f,0x3fff1913705f1f07,2 +np.float64,0x3fdcc64fcdb98ca0,0x3ff5d9c3e5862972,2 +np.float64,0x3fdad9f83db5b3f0,0x3ff56674e81c1bd1,2 +np.float64,0x32b8797065710,0x3ff0000000000000,2 +np.float64,0x3fd20deae6241bd6,0x3ff37495bc057394,2 +np.float64,0x7fc899f0763133e0,0x7ff0000000000000,2 +np.float64,0x80045805fc08b00d,0x3ff0000000000000,2 +np.float64,0xbfcd8304cb3b0608,0x3feb4611f1eaa30c,2 +np.float64,0x3fd632a2fcac6544,0x3ff4592e1ea14fb0,2 +np.float64,0xffeeb066007d60cb,0x0,2 +np.float64,0x800bb12a42b76255,0x3ff0000000000000,2 +np.float64,0xbfe060fe1760c1fc,0x3fe6714640ab2574,2 +np.float64,0x80067ed737acfdaf,0x3ff0000000000000,2 +np.float64,0x3fd5ec3211abd864,0x3ff449adea82e73e,2 +np.float64,0x7fc4b2fdc22965fb,0x7ff0000000000000,2 +np.float64,0xff656afd002ad600,0x0,2 +np.float64,0xffeadefcdcb5bdf9,0x0,2 +np.float64,0x80052f18610a5e32,0x3ff0000000000000,2 +np.float64,0xbfd5b75c78ab6eb8,0x3fe94b15e0f39194,2 +np.float64,0xa4d3de2b49a7c,0x3ff0000000000000,2 +np.float64,0xbfe321c93de64392,0x3fe524ac7bbee401,2 +np.float64,0x3feb32f5def665ec,0x3ffcd6e4e5f9c271,2 +np.float64,0x7fe6b07e4ced60fc,0x7ff0000000000000,2 +np.float64,0x3fe013bb2de02776,0x3ff6aa4c32ab5ba4,2 +np.float64,0xbfeadd81d375bb04,0x3fe1e1de89b4aebf,2 +np.float64,0xffece7678079cece,0x0,2 +np.float64,0x3fe3d87b8467b0f8,0x3ff897cf22505e4d,2 +np.float64,0xffc4e3a05129c740,0x0,2 +np.float64,0xbfddee6b03bbdcd6,0x3fe723dd83ab49bd,2 +np.float64,0x3fcc4e2672389c4d,0x3ff2a680db769116,2 +np.float64,0x3fd8ed221ab1da44,0x3ff4f569aec8b850,2 +np.float64,0x80000a3538a0146b,0x3ff0000000000000,2 +np.float64,0x8004832eb109065e,0x3ff0000000000000,2 +np.float64,0xffdca83c60395078,0x0,2 +np.float64,0xffef551cda3eaa39,0x0,2 +np.float64,0x800fd95dd65fb2bc,0x3ff0000000000000,2 +np.float64,0x3ff0000000000000,0x4000000000000000,2 +np.float64,0xbfc06f5c4f20deb8,0x3fed466c17305ad8,2 +np.float64,0xbfeb01b5f476036c,0x3fe1d3de0f4211f4,2 +np.float64,0xbfdb2b9284365726,0x3fe7d7b02f790b05,2 +np.float64,0xff76ba83202d7500,0x0,2 +np.float64,0x3fd3f1c59ea7e38c,0x3ff3db96b3a0aaad,2 +np.float64,0x8b99ff6d17340,0x3ff0000000000000,2 +np.float64,0xbfeb383aa0f67075,0x3fe1bedcf2531c08,2 +np.float64,0x3fe321e35fa643c7,0x3ff83749a5d686ee,2 +np.float64,0xbfd863eb2130c7d6,0x3fe8923fcc39bac7,2 +np.float64,0x9e71dd333ce3c,0x3ff0000000000000,2 +np.float64,0x9542962b2a853,0x3ff0000000000000,2 +np.float64,0xba2c963b74593,0x3ff0000000000000,2 +np.float64,0x80019f4d0ca33e9b,0x3ff0000000000000,2 +np.float64,0xffde3e39a73c7c74,0x0,2 +np.float64,0x800258ae02c4b15d,0x3ff0000000000000,2 +np.float64,0xbfd99a535a3334a6,0x3fe8402f3a0662a5,2 +np.float64,0xe6c62143cd8c4,0x3ff0000000000000,2 +np.float64,0x7fbcc828f0399051,0x7ff0000000000000,2 +np.float64,0xbfe42e3596285c6b,0x3fe4ab2066d66071,2 +np.float64,0xffe2ee42d365dc85,0x0,2 +np.float64,0x3fe1f98abea3f315,0x3ff79dc68002a80b,2 +np.float64,0x7fd7225891ae44b0,0x7ff0000000000000,2 +np.float64,0x477177408ee30,0x3ff0000000000000,2 +np.float64,0xbfe16a7e2162d4fc,0x3fe5f1a5c745385d,2 +np.float64,0xbf98aaee283155e0,0x3fef785952e9c089,2 +np.float64,0x7fd7c14a8daf8294,0x7ff0000000000000,2 +np.float64,0xf7e7713defcee,0x3ff0000000000000,2 +np.float64,0x800769aa11aed355,0x3ff0000000000000,2 +np.float64,0xbfed30385e3a6071,0x3fe10135a3bd9ae6,2 +np.float64,0x3fe6dd7205edbae4,0x3ffa4155899efd70,2 +np.float64,0x800d705d26bae0ba,0x3ff0000000000000,2 +np.float64,0xa443ac1f48876,0x3ff0000000000000,2 +np.float64,0xbfec8cfec43919fe,0x3fe13dbf966e6633,2 +np.float64,0x7fd246efaa248dde,0x7ff0000000000000,2 +np.float64,0x800f2ad14afe55a3,0x3ff0000000000000,2 +np.float64,0x800487a894c90f52,0x3ff0000000000000,2 +np.float64,0x80014c4f19e2989f,0x3ff0000000000000,2 +np.float64,0x3fc11f265f223e4d,0x3ff18def05c971e5,2 +np.float64,0xffeb6d565776daac,0x0,2 +np.float64,0x7fd5ca5df8ab94bb,0x7ff0000000000000,2 +np.float64,0xbfe33de4fde67bca,0x3fe517d0e212cd1c,2 +np.float64,0xbfd1c738e5a38e72,0x3fea6539e9491693,2 +np.float64,0xbfec1d8c33b83b18,0x3fe16790fbca0c65,2 +np.float64,0xbfeecb464b7d968d,0x3fe06c67e2aefa55,2 +np.float64,0xbfd621dbf1ac43b8,0x3fe92dfa32d93846,2 +np.float64,0x80069a02860d3406,0x3ff0000000000000,2 +np.float64,0xbfe84f650e309eca,0x3fe2e661300f1975,2 +np.float64,0x7fc1d2cec523a59d,0x7ff0000000000000,2 +np.float64,0x3fd7706d79aee0db,0x3ff49fb033353dfe,2 +np.float64,0xffd94ba458329748,0x0,2 +np.float64,0x7fea98ba1a753173,0x7ff0000000000000,2 +np.float64,0xbfe756ba092ead74,0x3fe34d428d1857bc,2 +np.float64,0xffecfbd836b9f7b0,0x0,2 +np.float64,0x3fd211fbe5a423f8,0x3ff375711a3641e0,2 +np.float64,0x7fee24f7793c49ee,0x7ff0000000000000,2 +np.float64,0x7fe6a098886d4130,0x7ff0000000000000,2 +np.float64,0xbfd4ade909a95bd2,0x3fe99436524db1f4,2 +np.float64,0xbfeb704e6476e09d,0x3fe1a95be4a21bc6,2 +np.float64,0xffefc0f6627f81ec,0x0,2 +np.float64,0x7feff3f896ffe7f0,0x7ff0000000000000,2 +np.float64,0xa3f74edb47eea,0x3ff0000000000000,2 +np.float64,0xbfe0a551cf214aa4,0x3fe65027a7ff42e3,2 +np.float64,0x3fe164b23622c964,0x3ff7521c6225f51d,2 +np.float64,0x7fc258752324b0e9,0x7ff0000000000000,2 +np.float64,0x4739b3348e737,0x3ff0000000000000,2 +np.float64,0xb0392b1d60726,0x3ff0000000000000,2 +np.float64,0x7fe26f42e5e4de85,0x7ff0000000000000,2 +np.float64,0x8004601f87e8c040,0x3ff0000000000000,2 +np.float64,0xffe92ce37b3259c6,0x0,2 +np.float64,0x3fe620da3a6c41b4,0x3ff9d6ee3d005466,2 +np.float64,0x3fd850cfa2b0a1a0,0x3ff4d20bd249d411,2 +np.float64,0xffdcdfdfb5b9bfc0,0x0,2 +np.float64,0x800390297d672054,0x3ff0000000000000,2 +np.float64,0x3fde5864f6bcb0ca,0x3ff639bb9321f5ef,2 +np.float64,0x3fee484cec7c909a,0x3ffed4d2c6274219,2 +np.float64,0x7fe9b9a064b37340,0x7ff0000000000000,2 +np.float64,0xffe50028b8aa0051,0x0,2 +np.float64,0x3fe37774ade6eee9,0x3ff864558498a9a8,2 +np.float64,0x7fef83c724bf078d,0x7ff0000000000000,2 +np.float64,0xbfeb58450fb6b08a,0x3fe1b290556be73d,2 +np.float64,0x7fd7161475ae2c28,0x7ff0000000000000,2 +np.float64,0x3fece09621f9c12c,0x3ffde836a583bbdd,2 +np.float64,0x3fd045790ea08af2,0x3ff31554778fd4e2,2 +np.float64,0xbfe7c7dd6cef8fbb,0x3fe31e2eeda857fc,2 +np.float64,0xffe9632f5372c65e,0x0,2 +np.float64,0x800d4f3a703a9e75,0x3ff0000000000000,2 +np.float64,0xffea880e4df5101c,0x0,2 +np.float64,0xbfeb7edc4ff6fdb8,0x3fe1a3cb5dc33594,2 +np.float64,0xbfcaae4bab355c98,0x3febb1ee65e16b58,2 +np.float64,0xbfde598a19bcb314,0x3fe709145eafaaf8,2 +np.float64,0x3feefb6d78fdf6db,0x3fff4d5c8c68e39a,2 +np.float64,0x13efc75427dfa,0x3ff0000000000000,2 +np.float64,0xffe26f65c064decb,0x0,2 +np.float64,0xbfed5c1addfab836,0x3fe0f1133bd2189a,2 +np.float64,0x7fe7a7cf756f4f9e,0x7ff0000000000000,2 +np.float64,0xffc681702e2d02e0,0x0,2 +np.float64,0x8003d6ab5067ad57,0x3ff0000000000000,2 +np.float64,0xffa695f1342d2be0,0x0,2 +np.float64,0xbfcf8857db3f10b0,0x3feafa14da8c29a4,2 +np.float64,0xbfe8ca06be71940e,0x3fe2b46f6d2c64b4,2 +np.float64,0x3451c74468a3a,0x3ff0000000000000,2 +np.float64,0x3fde47d5f6bc8fac,0x3ff635bf8e024716,2 +np.float64,0xffda159d5db42b3a,0x0,2 +np.float64,0x7fef9fecaa3f3fd8,0x7ff0000000000000,2 +np.float64,0x3fd4e745e3a9ce8c,0x3ff410a9cb6fd8bf,2 +np.float64,0xffef57019b3eae02,0x0,2 +np.float64,0xbfe6604f4f6cc09e,0x3fe3b55de43c626d,2 +np.float64,0xffe066a424a0cd48,0x0,2 +np.float64,0x3fd547de85aa8fbc,0x3ff425b2a7a16675,2 +np.float64,0xffb3c69280278d28,0x0,2 +np.float64,0xffebe0b759f7c16e,0x0,2 +np.float64,0x3fefc84106ff9082,0x3fffd973687337d8,2 +np.float64,0x501c42a4a0389,0x3ff0000000000000,2 +np.float64,0x7feb45d13eb68ba1,0x7ff0000000000000,2 +np.float64,0xbfb16a8c2e22d518,0x3fee86a9c0f9291a,2 +np.float64,0x3be327b877c66,0x3ff0000000000000,2 +np.float64,0x7fe4a58220694b03,0x7ff0000000000000,2 +np.float64,0x3fe0286220a050c4,0x3ff6b472157ab8f2,2 +np.float64,0x3fc9381825327030,0x3ff2575fbea2bf5d,2 +np.float64,0xbfd1af7ee8a35efe,0x3fea6c032cf7e669,2 +np.float64,0xbfea9b0f39b5361e,0x3fe1fbae14b40b4d,2 +np.float64,0x39efe4aa73dfd,0x3ff0000000000000,2 +np.float64,0xffeb06fdc8360dfb,0x0,2 +np.float64,0xbfda481e72b4903c,0x3fe812b4b08d4884,2 +np.float64,0xbfd414ba5ba82974,0x3fe9bec9474bdfe6,2 +np.float64,0x7fe707177b6e0e2e,0x7ff0000000000000,2 +np.float64,0x8000000000000001,0x3ff0000000000000,2 +np.float64,0xbfede6a75bbbcd4f,0x3fe0be874cccd399,2 +np.float64,0x8006cdb577cd9b6c,0x3ff0000000000000,2 +np.float64,0x800051374f20a26f,0x3ff0000000000000,2 +np.float64,0x3fe5cba8c96b9752,0x3ff9a76b3adcc122,2 +np.float64,0xbfee3933487c7267,0x3fe0a0b190f9609a,2 +np.float64,0x3fd574b8d8aae970,0x3ff42f7e83de1af9,2 +np.float64,0xba5db72b74bb7,0x3ff0000000000000,2 +np.float64,0x3fa9bf512c337ea0,0x3ff0914a7f743a94,2 +np.float64,0xffe8cb736c3196e6,0x0,2 +np.float64,0x3761b2f06ec37,0x3ff0000000000000,2 +np.float64,0x8b4d4433169a9,0x3ff0000000000000,2 +np.float64,0x800f0245503e048b,0x3ff0000000000000,2 +np.float64,0x7fb20d54ac241aa8,0x7ff0000000000000,2 +np.float64,0x3fdf26666b3e4ccd,0x3ff66b8995142017,2 +np.float64,0xbfcbf2a83737e550,0x3feb8173a7b9d6b5,2 +np.float64,0x3fd31572a0a62ae5,0x3ff3ac6c94313dcd,2 +np.float64,0x7fb6c2807a2d8500,0x7ff0000000000000,2 +np.float64,0x800799758f2f32ec,0x3ff0000000000000,2 +np.float64,0xe72f1f6bce5e4,0x3ff0000000000000,2 +np.float64,0x3fe0e0f223a1c1e4,0x3ff70fed5b761673,2 +np.float64,0x3fe6d4f133eda9e2,0x3ffa3c8000c169eb,2 +np.float64,0xbfe1ccc3d8639988,0x3fe5c32148bedbda,2 +np.float64,0x3fea71c53574e38a,0x3ffc5f31201fe9be,2 +np.float64,0x9e0323eb3c065,0x3ff0000000000000,2 +np.float64,0x8005cc79a5cb98f4,0x3ff0000000000000,2 +np.float64,0x1dace1f83b59d,0x3ff0000000000000,2 +np.float64,0x10000000000000,0x3ff0000000000000,2 +np.float64,0xbfdef50830bdea10,0x3fe6e269fc17ebef,2 +np.float64,0x8010000000000000,0x3ff0000000000000,2 +np.float64,0xbfdfa82192bf5044,0x3fe6b6313ee0a095,2 +np.float64,0x3fd9398fe2b27320,0x3ff506ca2093c060,2 +np.float64,0x8002721fe664e441,0x3ff0000000000000,2 +np.float64,0x800c04166ad8082d,0x3ff0000000000000,2 +np.float64,0xffec3918b3387230,0x0,2 +np.float64,0x3fec62d5dfb8c5ac,0x3ffd972ea4a54b32,2 +np.float64,0x3fe7e42a0b6fc854,0x3ffad86b0443181d,2 +np.float64,0x3fc0aff5f3215fec,0x3ff1836058d4d210,2 +np.float64,0xbf82ff68a025fec0,0x3fefcb7f06862dce,2 +np.float64,0xae2e35195c5c7,0x3ff0000000000000,2 +np.float64,0x3fece3bddf79c77c,0x3ffdea41fb1ba8fa,2 +np.float64,0xbfa97b947832f730,0x3feeea34ebedbbd2,2 +np.float64,0xbfdfb1b1ce3f6364,0x3fe6b3d72871335c,2 +np.float64,0xbfe61a4f24ac349e,0x3fe3d356bf991b06,2 +np.float64,0x7fe23117a5e4622e,0x7ff0000000000000,2 +np.float64,0x800552a8cccaa552,0x3ff0000000000000,2 +np.float64,0x625b4d0ac4b6a,0x3ff0000000000000,2 +np.float64,0x3f86cf15702d9e00,0x3ff01fbe0381676d,2 +np.float64,0x800d7d1b685afa37,0x3ff0000000000000,2 +np.float64,0x3fe2cb6e40a596dd,0x3ff80a1a562f7fc9,2 +np.float64,0x3fe756eb8e2eadd7,0x3ffa86c638aad07d,2 +np.float64,0x800dc9a5513b934b,0x3ff0000000000000,2 +np.float64,0xbfbbdd118a37ba20,0x3fedacb4624f3cee,2 +np.float64,0x800de01f8efbc03f,0x3ff0000000000000,2 +np.float64,0x800da1a3fe9b4348,0x3ff0000000000000,2 +np.float64,0xbf87d8c7602fb180,0x3fefbe2614998ab6,2 +np.float64,0xbfdfff6141bffec2,0x3fe6a0c54d9f1bc8,2 +np.float64,0xee8fbba5dd1f8,0x3ff0000000000000,2 +np.float64,0x3fe79dc93e6f3b92,0x3ffaaf9d7d955b2c,2 +np.float64,0xffedd4b3d07ba967,0x0,2 +np.float64,0x800905dfc1720bc0,0x3ff0000000000000,2 +np.float64,0x3fd9e483b8b3c907,0x3ff52ddc6c950e7f,2 +np.float64,0xe34ffefdc6a00,0x3ff0000000000000,2 +np.float64,0x2168e62242d1e,0x3ff0000000000000,2 +np.float64,0x800349950e26932b,0x3ff0000000000000,2 +np.float64,0x7fc50da8532a1b50,0x7ff0000000000000,2 +np.float64,0xae1a4d115c34a,0x3ff0000000000000,2 +np.float64,0xa020f0b74041e,0x3ff0000000000000,2 +np.float64,0x3fd2aa2f77a5545f,0x3ff3959f09519a25,2 +np.float64,0x3fbfefc3223fdf86,0x3ff171f3df2d408b,2 +np.float64,0xbfea9fc340b53f86,0x3fe1f9d92b712654,2 +np.float64,0xffe9b920a5337240,0x0,2 +np.float64,0xbfe2eb0265e5d605,0x3fe53dd195782de3,2 +np.float64,0x7fb932c70e32658d,0x7ff0000000000000,2 +np.float64,0x3fda816bfcb502d8,0x3ff551f8d5c84c82,2 +np.float64,0x3fed68cbe9fad198,0x3ffe40f6692d5693,2 +np.float64,0x32df077665be2,0x3ff0000000000000,2 +np.float64,0x7fdc9c2f3539385d,0x7ff0000000000000,2 +np.float64,0x7fe71091a2ee2122,0x7ff0000000000000,2 +np.float64,0xbfe68106c46d020e,0x3fe3a76b56024c2c,2 +np.float64,0xffcf0572823e0ae4,0x0,2 +np.float64,0xbfeeab341fbd5668,0x3fe077d496941cda,2 +np.float64,0x7fe7ada0d2af5b41,0x7ff0000000000000,2 +np.float64,0xffacdef2a439bde0,0x0,2 +np.float64,0x3fe4200f3128401e,0x3ff8be0ddf30fd1e,2 +np.float64,0xffd9022a69320454,0x0,2 +np.float64,0xbfe8e06914f1c0d2,0x3fe2ab5fe7fffb5a,2 +np.float64,0x3fc4b976602972ed,0x3ff1e6786fa7a890,2 +np.float64,0xbfd784c105af0982,0x3fe8cdeb1cdbd57e,2 +np.float64,0x7feb20a20eb64143,0x7ff0000000000000,2 +np.float64,0xbfc87dd83630fbb0,0x3fec067c1e7e6983,2 +np.float64,0x7fe5400cbe6a8018,0x7ff0000000000000,2 +np.float64,0xbfb4a1f5e22943e8,0x3fee42e6c81559a9,2 +np.float64,0x3fe967c575f2cf8a,0x3ffbbd8bc0d5c50d,2 +np.float64,0xbfeb059cf4760b3a,0x3fe1d25c592c4dab,2 +np.float64,0xbfeef536d5bdea6e,0x3fe05d832c15c64a,2 +np.float64,0x3fa90b3f6432167f,0x3ff08d410dd732cc,2 +np.float64,0xbfeaff265e75fe4d,0x3fe1d4db3fb3208d,2 +np.float64,0x6d93d688db27b,0x3ff0000000000000,2 +np.float64,0x800ab9b4ea55736a,0x3ff0000000000000,2 +np.float64,0x3fd444b39d288967,0x3ff3ed749d48d444,2 +np.float64,0xbfd5f2c0d0abe582,0x3fe93ad6124d88e7,2 +np.float64,0x3fea8fd915f51fb2,0x3ffc71b32cb92d60,2 +np.float64,0xbfd23d6491a47aca,0x3fea43875709b0f0,2 +np.float64,0xffe76f75ce6edeeb,0x0,2 +np.float64,0x1f5670da3eacf,0x3ff0000000000000,2 +np.float64,0x8000d89c9621b13a,0x3ff0000000000000,2 +np.float64,0x3fedb51c52bb6a39,0x3ffe732279c228ff,2 +np.float64,0x7f99215ac83242b5,0x7ff0000000000000,2 +np.float64,0x742a6864e854e,0x3ff0000000000000,2 +np.float64,0xbfe02fb340205f66,0x3fe689495f9164e3,2 +np.float64,0x7fef4c12b0fe9824,0x7ff0000000000000,2 +np.float64,0x3fd40e17c2a81c30,0x3ff3e1aee8ed972f,2 +np.float64,0x7fdcd264e939a4c9,0x7ff0000000000000,2 +np.float64,0x3fdb675838b6ceb0,0x3ff587526241c550,2 +np.float64,0x3fdf1a4081be3480,0x3ff66896a18c2385,2 +np.float64,0xbfea5082b874a106,0x3fe218cf8f11be13,2 +np.float64,0xffe1a0ebf7e341d8,0x0,2 +np.float64,0x3fed0a2222ba1444,0x3ffe032ce928ae7d,2 +np.float64,0xffeae036da75c06d,0x0,2 +np.float64,0x5b05fc8ab60c0,0x3ff0000000000000,2 +np.float64,0x7fd8aae5f03155cb,0x7ff0000000000000,2 +np.float64,0xbfd0b4d9fda169b4,0x3feab41e58b6ccb7,2 +np.float64,0xffdcaffa57395ff4,0x0,2 +np.float64,0xbfcbf1455437e28c,0x3feb81a884182c5d,2 +np.float64,0x3f9d6700b83ace01,0x3ff0525657db35d4,2 +np.float64,0x4fd5b0b29fab7,0x3ff0000000000000,2 +np.float64,0x3fe9af2df5b35e5c,0x3ffbe895684df916,2 +np.float64,0x800dfd41f9dbfa84,0x3ff0000000000000,2 +np.float64,0xbf2a30457e546,0x3ff0000000000000,2 +np.float64,0x7fc6be37182d7c6d,0x7ff0000000000000,2 +np.float64,0x800e0f9788dc1f2f,0x3ff0000000000000,2 +np.float64,0x8006890c704d121a,0x3ff0000000000000,2 +np.float64,0xffecb1a7cbb9634f,0x0,2 +np.float64,0xffb35c330426b868,0x0,2 +np.float64,0x7fe8f2ba8a71e574,0x7ff0000000000000,2 +np.float64,0xf3ccff8fe79a0,0x3ff0000000000000,2 +np.float64,0x3fdf19a84e3e3351,0x3ff66871b17474c1,2 +np.float64,0x80049a662d0934cd,0x3ff0000000000000,2 +np.float64,0xdf5bb4bbbeb77,0x3ff0000000000000,2 +np.float64,0x8005eca030cbd941,0x3ff0000000000000,2 +np.float64,0xffe5f239586be472,0x0,2 +np.float64,0xbfc4526a0728a4d4,0x3fecaa52fbf5345e,2 +np.float64,0xbfe8f1ecda31e3da,0x3fe2a44c080848b3,2 +np.float64,0x3feebd32f4bd7a66,0x3fff234788938c3e,2 +np.float64,0xffd6ca04e9ad940a,0x0,2 +np.float64,0x7ff0000000000000,0x7ff0000000000000,2 +np.float64,0xbfd4c560a9a98ac2,0x3fe98db6d97442fc,2 +np.float64,0x8005723471cae46a,0x3ff0000000000000,2 +np.float64,0xbfeb278299764f05,0x3fe1c54b48f8ba4b,2 +np.float64,0x8007907b376f20f7,0x3ff0000000000000,2 +np.float64,0x7fe9c2fd01b385f9,0x7ff0000000000000,2 +np.float64,0x7fdaa37368b546e6,0x7ff0000000000000,2 +np.float64,0xbfe6d0f3786da1e7,0x3fe38582271cada7,2 +np.float64,0xbfea9b77823536ef,0x3fe1fb8575cd1b7d,2 +np.float64,0xbfe90ac38bf21587,0x3fe29a471b47a2e8,2 +np.float64,0xbfe9c51844738a30,0x3fe24fc8de03ea84,2 +np.float64,0x3fe45a9013a8b520,0x3ff8dd7c80f1cf75,2 +np.float64,0xbfe5780551eaf00a,0x3fe419832a6a4c56,2 +np.float64,0xffefffffffffffff,0x0,2 +np.float64,0x7fe3778c84a6ef18,0x7ff0000000000000,2 +np.float64,0xbfdc8a60413914c0,0x3fe77dc55b85028f,2 +np.float64,0xef47ae2fde8f6,0x3ff0000000000000,2 +np.float64,0x8001269fa4c24d40,0x3ff0000000000000,2 +np.float64,0x3fe9d2d39e73a5a7,0x3ffbfe2a66c4148e,2 +np.float64,0xffee61f528fcc3e9,0x0,2 +np.float64,0x3fe8a259ab7144b3,0x3ffb47e797a34bd2,2 +np.float64,0x3f906d610820dac0,0x3ff02dccda8e1a75,2 +np.float64,0x3fe70739f32e0e74,0x3ffa59232f4fcd07,2 +np.float64,0x3fe6b7f5e6ad6fec,0x3ffa2c0cc54f2c16,2 +np.float64,0x95a91a792b524,0x3ff0000000000000,2 +np.float64,0xbfedf6fcf57bedfa,0x3fe0b89bb40081cc,2 +np.float64,0xbfa4d2de9c29a5c0,0x3fef1c485678d657,2 +np.float64,0x3fe130470d22608e,0x3ff737b0be409a38,2 +np.float64,0x3fcf8035423f006b,0x3ff2f9d7c3c6a302,2 +np.float64,0xffe5995a3eab32b4,0x0,2 +np.float64,0xffca68c63034d18c,0x0,2 +np.float64,0xff9d53af903aa760,0x0,2 +np.float64,0x800563f1de6ac7e4,0x3ff0000000000000,2 +np.float64,0x7fce284fa63c509e,0x7ff0000000000000,2 +np.float64,0x7fb2a3959a25472a,0x7ff0000000000000,2 +np.float64,0x7fdbe2652f37c4c9,0x7ff0000000000000,2 +np.float64,0x800d705bbc1ae0b8,0x3ff0000000000000,2 +np.float64,0x7fd9bd2347b37a46,0x7ff0000000000000,2 +np.float64,0x3fcac3c0fb358782,0x3ff27ed62d6c8221,2 +np.float64,0x800110691ec220d3,0x3ff0000000000000,2 +np.float64,0x3fef79a8157ef350,0x3fffa368513eb909,2 +np.float64,0x7fe8bd2f0e317a5d,0x7ff0000000000000,2 +np.float64,0x7fd3040e60a6081c,0x7ff0000000000000,2 +np.float64,0xffea50723234a0e4,0x0,2 +np.float64,0xbfe6220054ac4400,0x3fe3d00961238a93,2 +np.float64,0x3f9eddd8c83dbbc0,0x3ff0567b0c73005a,2 +np.float64,0xbfa4a062c42940c0,0x3fef1e68badde324,2 +np.float64,0xbfd077ad4720ef5a,0x3feac5d577581d07,2 +np.float64,0x7fdfd4b025bfa95f,0x7ff0000000000000,2 +np.float64,0xd00d3cf3a01a8,0x3ff0000000000000,2 +np.float64,0x7fe3010427260207,0x7ff0000000000000,2 +np.float64,0x22ea196645d44,0x3ff0000000000000,2 +np.float64,0x7fd747e8cd2e8fd1,0x7ff0000000000000,2 +np.float64,0xd50665e7aa0cd,0x3ff0000000000000,2 +np.float64,0x7fe1da580ae3b4af,0x7ff0000000000000,2 +np.float64,0xffeb218ecfb6431d,0x0,2 +np.float64,0xbf887d0dd030fa00,0x3fefbc6252c8b354,2 +np.float64,0x3fcaa31067354621,0x3ff27b904c07e07f,2 +np.float64,0x7fe698cc4ded3198,0x7ff0000000000000,2 +np.float64,0x1c40191a38804,0x3ff0000000000000,2 +np.float64,0x80086fd20e30dfa4,0x3ff0000000000000,2 +np.float64,0x7fed34d5eaba69ab,0x7ff0000000000000,2 +np.float64,0xffd00b52622016a4,0x0,2 +np.float64,0x3f80abcdb021579b,0x3ff0172d27945851,2 +np.float64,0x3fe614cfd66c29a0,0x3ff9d031e1839191,2 +np.float64,0x80021d71c8843ae4,0x3ff0000000000000,2 +np.float64,0x800bc2adc657855c,0x3ff0000000000000,2 +np.float64,0x6b9fec1cd73fe,0x3ff0000000000000,2 +np.float64,0xffd9093b5f321276,0x0,2 +np.float64,0x800d3c6c77fa78d9,0x3ff0000000000000,2 +np.float64,0xffe80fc1cbf01f83,0x0,2 +np.float64,0xffbffbaf2a3ff760,0x0,2 +np.float64,0x3fea1ed29eb43da5,0x3ffc2c64ec0e17a3,2 +np.float64,0x7ff4000000000000,0x7ffc000000000000,2 +np.float64,0x3fd944a052328941,0x3ff5094f4c43ecca,2 +np.float64,0x800b1f9416163f29,0x3ff0000000000000,2 +np.float64,0x800f06bf33de0d7e,0x3ff0000000000000,2 +np.float64,0xbfdbf0d226b7e1a4,0x3fe7a4f73793d95b,2 +np.float64,0xffe7306c30ae60d8,0x0,2 +np.float64,0x7fe991accfb32359,0x7ff0000000000000,2 +np.float64,0x3fcc0040d2380082,0x3ff29ea47e4f07d4,2 +np.float64,0x7fefffffffffffff,0x7ff0000000000000,2 +np.float64,0x0,0x3ff0000000000000,2 +np.float64,0x3fe1423f7be2847e,0x3ff740bc1d3b20f8,2 +np.float64,0xbfeae3a3cab5c748,0x3fe1df7e936f8504,2 +np.float64,0x800b2da7d6165b50,0x3ff0000000000000,2 +np.float64,0x800b2404fcd6480a,0x3ff0000000000000,2 +np.float64,0x6fcbcf88df97b,0x3ff0000000000000,2 +np.float64,0xa248c0e14492,0x3ff0000000000000,2 +np.float64,0xffd255776824aaee,0x0,2 +np.float64,0x80057b3effeaf67f,0x3ff0000000000000,2 +np.float64,0x3feb0b07d7761610,0x3ffcbdfe1be5a594,2 +np.float64,0x924e1019249c2,0x3ff0000000000000,2 +np.float64,0x80074307e80e8611,0x3ff0000000000000,2 +np.float64,0xffb207fa46240ff8,0x0,2 +np.float64,0x95ac388d2b587,0x3ff0000000000000,2 +np.float64,0xbff0000000000000,0x3fe0000000000000,2 +np.float64,0x3fd38b6a492716d5,0x3ff3c59f62b5add5,2 +np.float64,0x7fe49362c3e926c5,0x7ff0000000000000,2 +np.float64,0x7fe842889db08510,0x7ff0000000000000,2 +np.float64,0xbfba6003e834c008,0x3fedcb620a2d9856,2 +np.float64,0xffe7e782bd6fcf05,0x0,2 +np.float64,0x7fd9b93d9433727a,0x7ff0000000000000,2 +np.float64,0x7fc8fcb61d31f96b,0x7ff0000000000000,2 +np.float64,0xbfef9be8db3f37d2,0x3fe022d603b81dc2,2 +np.float64,0x6f4fc766de9fa,0x3ff0000000000000,2 +np.float64,0xbfe93016f132602e,0x3fe28b42d782d949,2 +np.float64,0x3fe10e52b8e21ca5,0x3ff726a38b0bb895,2 +np.float64,0x3fbbba0ae6377416,0x3ff13f56084a9da3,2 +np.float64,0x3fe09e42ece13c86,0x3ff6eeb57e775e24,2 +np.float64,0x800942e39fb285c8,0x3ff0000000000000,2 +np.float64,0xffe5964370eb2c86,0x0,2 +np.float64,0x3fde479f32bc8f3e,0x3ff635b2619ba53a,2 +np.float64,0x3fe826e187f04dc3,0x3ffaff52b79c3a08,2 +np.float64,0x3febcbf1eab797e4,0x3ffd37152e5e2598,2 +np.float64,0x3fa0816a202102d4,0x3ff05c8e6a8b00d5,2 +np.float64,0xbd005ccb7a00c,0x3ff0000000000000,2 +np.float64,0x44c12fdc89827,0x3ff0000000000000,2 +np.float64,0xffc8fdffa431fc00,0x0,2 +np.float64,0xffeb4f5a87b69eb4,0x0,2 +np.float64,0xbfb07e7f8420fd00,0x3fee9a32924fe6a0,2 +np.float64,0xbfbd9d1bb63b3a38,0x3fed88ca81e5771c,2 +np.float64,0x8008682a74f0d055,0x3ff0000000000000,2 +np.float64,0x3fdeedbc7b3ddb79,0x3ff65dcb7c55f4dc,2 +np.float64,0x8009e889c613d114,0x3ff0000000000000,2 +np.float64,0x3faea831f43d5064,0x3ff0ad935e890e49,2 +np.float64,0xf0af1703e15e3,0x3ff0000000000000,2 +np.float64,0xffec06c4a5f80d88,0x0,2 +np.float64,0x53a1cc0ca743a,0x3ff0000000000000,2 +np.float64,0x7fd10c9eea22193d,0x7ff0000000000000,2 +np.float64,0xbfd48a6bf0a914d8,0x3fe99e0d109f2bac,2 +np.float64,0x3fd6dfe931adbfd4,0x3ff47f81c2dfc5d3,2 +np.float64,0x3fed20e86b7a41d0,0x3ffe11fecc7bc686,2 +np.float64,0xbfea586818b4b0d0,0x3fe215b7747d5cb8,2 +np.float64,0xbfd4ad3e20295a7c,0x3fe99465ab8c3275,2 +np.float64,0x3fd6619ee4acc33e,0x3ff4638b7b80c08a,2 +np.float64,0x3fdf6fcb63bedf97,0x3ff67d62fd3d560c,2 +np.float64,0x800a9191e7152324,0x3ff0000000000000,2 +np.float64,0x3fd2ff3c0da5fe78,0x3ff3a7b17e892a28,2 +np.float64,0x8003dbf1f327b7e5,0x3ff0000000000000,2 +np.float64,0xffea6b89a934d712,0x0,2 +np.float64,0x7fcfb879043f70f1,0x7ff0000000000000,2 +np.float64,0xea6a84dbd4d51,0x3ff0000000000000,2 +np.float64,0x800ec97a815d92f5,0x3ff0000000000000,2 +np.float64,0xffe304c3a8660987,0x0,2 +np.float64,0xbfefe24dd3ffc49c,0x3fe00a4e065be96d,2 +np.float64,0xffd3cc8c00a79918,0x0,2 +np.float64,0x95be8b7b2b7d2,0x3ff0000000000000,2 +np.float64,0x7fe20570cba40ae1,0x7ff0000000000000,2 +np.float64,0x7f97a06da02f40da,0x7ff0000000000000,2 +np.float64,0xffe702b9522e0572,0x0,2 +np.float64,0x3fada2d8543b45b1,0x3ff0a7adc4201e08,2 +np.float64,0x235e6acc46bce,0x3ff0000000000000,2 +np.float64,0x3fea6bc28ef4d786,0x3ffc5b7fc68fddac,2 +np.float64,0xffdbc9f505b793ea,0x0,2 +np.float64,0xffe98b137ff31626,0x0,2 +np.float64,0x800e26c6721c4d8d,0x3ff0000000000000,2 +np.float64,0x80080de445301bc9,0x3ff0000000000000,2 +np.float64,0x37e504a86fca1,0x3ff0000000000000,2 +np.float64,0x8002f5f60325ebed,0x3ff0000000000000,2 +np.float64,0x5c8772feb90ef,0x3ff0000000000000,2 +np.float64,0xbfe021abb4604358,0x3fe69023a51d22b8,2 +np.float64,0x3fde744f8fbce8a0,0x3ff64074dc84edd7,2 +np.float64,0xbfdd92899f3b2514,0x3fe73aefd9701858,2 +np.float64,0x7fc1ad5c51235ab8,0x7ff0000000000000,2 +np.float64,0xaae2f98955c5f,0x3ff0000000000000,2 +np.float64,0x7f9123d5782247aa,0x7ff0000000000000,2 +np.float64,0xbfe3f8e94b67f1d2,0x3fe4c30ab28e9cb7,2 +np.float64,0x7fdaba8b4cb57516,0x7ff0000000000000,2 +np.float64,0x7fefc85cfeff90b9,0x7ff0000000000000,2 +np.float64,0xffb83b4f523076a0,0x0,2 +np.float64,0xbfe888a68c71114d,0x3fe2ceff17c203d1,2 +np.float64,0x800de1dac4bbc3b6,0x3ff0000000000000,2 +np.float64,0xbfe4f27f09e9e4fe,0x3fe453f9af407eac,2 +np.float64,0xffe3d2713467a4e2,0x0,2 +np.float64,0xbfebaab840375570,0x3fe1931131b98842,2 +np.float64,0x93892a1b27126,0x3ff0000000000000,2 +np.float64,0x1e8e7f983d1d1,0x3ff0000000000000,2 +np.float64,0x3fecc950627992a0,0x3ffdd926f036add0,2 +np.float64,0xbfd41dfb1aa83bf6,0x3fe9bc34ece35b94,2 +np.float64,0x800aebfc6555d7f9,0x3ff0000000000000,2 +np.float64,0x7fe33ba52ca67749,0x7ff0000000000000,2 +np.float64,0xffe57c9b3feaf936,0x0,2 +np.float64,0x3fdd12464fba248c,0x3ff5ebc5598e6bd0,2 +np.float64,0xffe06d7f0fe0dafe,0x0,2 +np.float64,0x800e55b7fe9cab70,0x3ff0000000000000,2 +np.float64,0x3fd33803c8267008,0x3ff3b3cb78b2d642,2 +np.float64,0xe9cab8a1d3957,0x3ff0000000000000,2 +np.float64,0x3fb38ac166271580,0x3ff0de906947c0f0,2 +np.float64,0xbfd67aa552acf54a,0x3fe915cf64a389fd,2 +np.float64,0x1db96daa3b72f,0x3ff0000000000000,2 +np.float64,0xbfee9f08f4fd3e12,0x3fe07c2c615add3c,2 +np.float64,0xf14f6d65e29ee,0x3ff0000000000000,2 +np.float64,0x800bce089e179c12,0x3ff0000000000000,2 +np.float64,0xffc42dcc37285b98,0x0,2 +np.float64,0x7fd5f37063abe6e0,0x7ff0000000000000,2 +np.float64,0xbfd943c2cbb28786,0x3fe856f6452ec753,2 +np.float64,0x8ddfbc091bbf8,0x3ff0000000000000,2 +np.float64,0xbfe153491e22a692,0x3fe5fcb075dbbd5d,2 +np.float64,0xffe7933999ef2672,0x0,2 +np.float64,0x7ff8000000000000,0x7ff8000000000000,2 +np.float64,0x8000000000000000,0x3ff0000000000000,2 +np.float64,0xbfe9154580b22a8b,0x3fe2960bac3a8220,2 +np.float64,0x800dc6dda21b8dbb,0x3ff0000000000000,2 +np.float64,0xbfb26225a824c448,0x3fee7239a457df81,2 +np.float64,0xbfd7b68c83af6d1a,0x3fe8c08e351ab468,2 +np.float64,0xffde01f7213c03ee,0x0,2 +np.float64,0x3fe54cbe0faa997c,0x3ff9614527191d72,2 +np.float64,0xbfd6bec3732d7d86,0x3fe90354909493de,2 +np.float64,0xbfef3c85bd7e790b,0x3fe0444f8c489ca6,2 +np.float64,0x899501b7132a0,0x3ff0000000000000,2 +np.float64,0xbfe17a456462f48b,0x3fe5ea2719a9a84b,2 +np.float64,0xffe34003b8668007,0x0,2 +np.float64,0x7feff6a3633fed46,0x7ff0000000000000,2 +np.float64,0x3fba597ecc34b2fe,0x3ff12ee72e4de474,2 +np.float64,0x4084c7b68109a,0x3ff0000000000000,2 +np.float64,0x3fad23bf4c3a4780,0x3ff0a4d06193ff6d,2 +np.float64,0xffd0fe2707a1fc4e,0x0,2 +np.float64,0xb96cb43f72d97,0x3ff0000000000000,2 +np.float64,0x7fc4d684d829ad09,0x7ff0000000000000,2 +np.float64,0x7fdc349226b86923,0x7ff0000000000000,2 +np.float64,0x7fd82851cd3050a3,0x7ff0000000000000,2 +np.float64,0x800cde0041b9bc01,0x3ff0000000000000,2 +np.float64,0x4e8caa1e9d196,0x3ff0000000000000,2 +np.float64,0xbfed06a6d2fa0d4e,0x3fe1108c3682b05a,2 +np.float64,0xffe8908122312102,0x0,2 +np.float64,0xffe56ed6d9aaddad,0x0,2 +np.float64,0x3fedd6db00fbadb6,0x3ffe896c68c4b26e,2 +np.float64,0x3fde31f9b4bc63f4,0x3ff6307e08f8b6ba,2 +np.float64,0x6bb963c2d772d,0x3ff0000000000000,2 +np.float64,0x787b7142f0f6f,0x3ff0000000000000,2 +np.float64,0x3fe6e4147c6dc829,0x3ffa451bbdece240,2 +np.float64,0x8003857401470ae9,0x3ff0000000000000,2 +np.float64,0xbfeae82c3c75d058,0x3fe1ddbd66e65aab,2 +np.float64,0x7fe174707c62e8e0,0x7ff0000000000000,2 +np.float64,0x80008d2545e11a4b,0x3ff0000000000000,2 +np.float64,0xbfecc2dce17985ba,0x3fe129ad4325985a,2 +np.float64,0xbfe1fa1daf63f43c,0x3fe5adcb0731a44b,2 +np.float64,0x7fcf2530203e4a5f,0x7ff0000000000000,2 +np.float64,0xbfea5cefe874b9e0,0x3fe213f134b61f4a,2 +np.float64,0x800103729f2206e6,0x3ff0000000000000,2 +np.float64,0xbfe8442ff7708860,0x3fe2eaf850faa169,2 +np.float64,0x8006c78e19ed8f1d,0x3ff0000000000000,2 +np.float64,0x3fc259589c24b2b1,0x3ff1abe6a4d28816,2 +np.float64,0xffed02b7b5ba056e,0x0,2 +np.float64,0xbfce0aa4fe3c1548,0x3feb32115d92103e,2 +np.float64,0x7fec06e78bf80dce,0x7ff0000000000000,2 +np.float64,0xbfe0960bbc612c18,0x3fe6578ab29b70d4,2 +np.float64,0x3fee45841cbc8b08,0x3ffed2f6ca808ad3,2 +np.float64,0xbfeb0f8ebef61f1e,0x3fe1ce86003044cd,2 +np.float64,0x8002c357358586af,0x3ff0000000000000,2 +np.float64,0x3fe9aa10cc735422,0x3ffbe57e294ce68b,2 +np.float64,0x800256c0a544ad82,0x3ff0000000000000,2 +np.float64,0x4de6e1449bcdd,0x3ff0000000000000,2 +np.float64,0x65e9bc9ccbd38,0x3ff0000000000000,2 +np.float64,0xbfe53b0fa9aa7620,0x3fe4341f0aa29bbc,2 +np.float64,0xbfcdd94cd13bb298,0x3feb3956acd2e2dd,2 +np.float64,0x8004a49b65a94938,0x3ff0000000000000,2 +np.float64,0x800d3d05deba7a0c,0x3ff0000000000000,2 +np.float64,0x3fe4e05bce69c0b8,0x3ff925f55602a7e0,2 +np.float64,0xffe391e3256723c6,0x0,2 +np.float64,0xbfe92f0f37b25e1e,0x3fe28bacc76ae753,2 +np.float64,0x3f990238d8320472,0x3ff045edd36e2d62,2 +np.float64,0xffed8d15307b1a2a,0x0,2 +np.float64,0x3fee82e01afd05c0,0x3ffefc09e8b9c2b7,2 +np.float64,0xffb2d94b2225b298,0x0,2 diff --git a/numpy/core/tests/data/umath-validation-set-expm1.csv b/numpy/core/tests/data/umath-validation-set-expm1.csv new file mode 100644 index 000000000000..732ae8654ef8 --- /dev/null +++ b/numpy/core/tests/data/umath-validation-set-expm1.csv @@ -0,0 +1,1429 @@ +dtype,input,output,ulperrortol +np.float32,0x80606724,0x80606724,3 +np.float32,0xbf16790f,0xbee38e14,3 +np.float32,0xbf1778a1,0xbee4a97f,3 +np.float32,0x7d4fc610,0x7f800000,3 +np.float32,0xbec30a20,0xbea230d5,3 +np.float32,0x3eae8a36,0x3ecffac5,3 +np.float32,0xbf1f08f1,0xbeece93c,3 +np.float32,0x80374376,0x80374376,3 +np.float32,0x3f2e04ca,0x3f793115,3 +np.float32,0x7e2c7e36,0x7f800000,3 +np.float32,0xbf686cae,0xbf18bcf0,3 +np.float32,0xbf5518cd,0xbf10a3da,3 +np.float32,0x807e233c,0x807e233c,3 +np.float32,0x7f4edd54,0x7f800000,3 +np.float32,0x7ed70088,0x7f800000,3 +np.float32,0x801675da,0x801675da,3 +np.float32,0x806735d5,0x806735d5,3 +np.float32,0xfe635fec,0xbf800000,3 +np.float32,0xfed88a0a,0xbf800000,3 +np.float32,0xff52c052,0xbf800000,3 +np.float32,0x7fc00000,0x7fc00000,3 +np.float32,0xff4f65f9,0xbf800000,3 +np.float32,0xfe0f6c20,0xbf800000,3 +np.float32,0x80322b30,0x80322b30,3 +np.float32,0xfb757000,0xbf800000,3 +np.float32,0x3c81e0,0x3c81e0,3 +np.float32,0x79d56a,0x79d56a,3 +np.float32,0x8029d7af,0x8029d7af,3 +np.float32,0x8058a593,0x8058a593,3 +np.float32,0x3f3a13c7,0x3f88c75c,3 +np.float32,0x2a6b05,0x2a6b05,3 +np.float32,0xbd64c960,0xbd5e83ae,3 +np.float32,0x80471052,0x80471052,3 +np.float32,0xbe5dd950,0xbe47766c,3 +np.float32,0xfd8f88f0,0xbf800000,3 +np.float32,0x75a4b7,0x75a4b7,3 +np.float32,0x3f726f2e,0x3fc9fb7d,3 +np.float32,0x3ed6795c,0x3f053115,3 +np.float32,0x17d7f5,0x17d7f5,3 +np.float32,0xbf4cf19b,0xbf0d094f,3 +np.float32,0x3e0ec532,0x3e1933c6,3 +np.float32,0xff084016,0xbf800000,3 +np.float32,0x800829aa,0x800829aa,3 +np.float32,0x806d7302,0x806d7302,3 +np.float32,0x7f59d9da,0x7f800000,3 +np.float32,0x15f8b9,0x15f8b9,3 +np.float32,0x803befb3,0x803befb3,3 +np.float32,0x525043,0x525043,3 +np.float32,0x51a647,0x51a647,3 +np.float32,0xbf1cfce4,0xbeeab3d9,3 +np.float32,0x3f1f27a4,0x3f5cb1d2,3 +np.float32,0xbebc3a04,0xbe9d8142,3 +np.float32,0xbeea548c,0xbebc07e5,3 +np.float32,0x3f47401c,0x3f96c2a3,3 +np.float32,0x806b1ea3,0x806b1ea3,3 +np.float32,0x3ea56bb8,0x3ec3450c,3 +np.float32,0x3f7b4963,0x3fd597b5,3 +np.float32,0x7f051fa0,0x7f800000,3 +np.float32,0x1d411c,0x1d411c,3 +np.float32,0xff0b6a35,0xbf800000,3 +np.float32,0xbead63c0,0xbe9314f7,3 +np.float32,0x3738be,0x3738be,3 +np.float32,0x3f138cc8,0x3f479155,3 +np.float32,0x800a539f,0x800a539f,3 +np.float32,0x801b0ebd,0x801b0ebd,3 +np.float32,0x318fcd,0x318fcd,3 +np.float32,0x3ed67556,0x3f052e06,3 +np.float32,0x702886,0x702886,3 +np.float32,0x80000001,0x80000001,3 +np.float32,0x70a174,0x70a174,3 +np.float32,0x4f9c66,0x4f9c66,3 +np.float32,0x3e3e1927,0x3e50e351,3 +np.float32,0x7eac9a4d,0x7f800000,3 +np.float32,0x4b7407,0x4b7407,3 +np.float32,0x7f5bd2fd,0x7f800000,3 +np.float32,0x3eaafc58,0x3ecaffbd,3 +np.float32,0xbc989360,0xbc9729e2,3 +np.float32,0x3f470e5c,0x3f968c7b,3 +np.float32,0x4c5672,0x4c5672,3 +np.float32,0xff2b2ee2,0xbf800000,3 +np.float32,0xbf28a104,0xbef7079b,3 +np.float32,0x2c6175,0x2c6175,3 +np.float32,0x3d7e4fb0,0x3d832f9f,3 +np.float32,0x763276,0x763276,3 +np.float32,0x3cf364,0x3cf364,3 +np.float32,0xbf7ace75,0xbf1fe48c,3 +np.float32,0xff19e858,0xbf800000,3 +np.float32,0x80504c70,0x80504c70,3 +np.float32,0xff390210,0xbf800000,3 +np.float32,0x8046a743,0x8046a743,3 +np.float32,0x80000000,0x80000000,3 +np.float32,0x806c51da,0x806c51da,3 +np.float32,0x806ab38f,0x806ab38f,3 +np.float32,0x3f3de863,0x3f8cc538,3 +np.float32,0x7f6d45bb,0x7f800000,3 +np.float32,0xfd16ec60,0xbf800000,3 +np.float32,0x80513cba,0x80513cba,3 +np.float32,0xbf68996b,0xbf18cefa,3 +np.float32,0xfe039f2c,0xbf800000,3 +np.float32,0x3f013207,0x3f280c55,3 +np.float32,0x7ef4bc07,0x7f800000,3 +np.float32,0xbe8b65ac,0xbe741069,3 +np.float32,0xbf7a8186,0xbf1fc7a6,3 +np.float32,0x802532e5,0x802532e5,3 +np.float32,0x32c7df,0x32c7df,3 +np.float32,0x3ce4dceb,0x3ce81701,3 +np.float32,0xfe801118,0xbf800000,3 +np.float32,0x3d905f20,0x3d9594fb,3 +np.float32,0xbe11ed28,0xbe080168,3 +np.float32,0x59e773,0x59e773,3 +np.float32,0x3e9a2547,0x3eb3dd57,3 +np.float32,0x7ecb7c67,0x7f800000,3 +np.float32,0x7f69a67e,0x7f800000,3 +np.float32,0xff121e11,0xbf800000,3 +np.float32,0x3f7917cb,0x3fd2ad8c,3 +np.float32,0xbf1a7da8,0xbee7fc0c,3 +np.float32,0x3f077e66,0x3f329c40,3 +np.float32,0x3ce8e040,0x3cec37b3,3 +np.float32,0xbf3f0b8e,0xbf069f4d,3 +np.float32,0x3f52f194,0x3fa3c9d6,3 +np.float32,0xbf0e7422,0xbeda80f2,3 +np.float32,0xfd67e230,0xbf800000,3 +np.float32,0xff14d9a9,0xbf800000,3 +np.float32,0x3f3546e3,0x3f83dc2b,3 +np.float32,0x3e152e3a,0x3e20983d,3 +np.float32,0x4a89a3,0x4a89a3,3 +np.float32,0x63217,0x63217,3 +np.float32,0xbeb9e2a8,0xbe9be153,3 +np.float32,0x7e9fa049,0x7f800000,3 +np.float32,0x7f58110c,0x7f800000,3 +np.float32,0x3e88290c,0x3e9bfba9,3 +np.float32,0xbf2cb206,0xbefb3494,3 +np.float32,0xff5880c4,0xbf800000,3 +np.float32,0x7ecff3ac,0x7f800000,3 +np.float32,0x3f4b3de6,0x3f9b23fd,3 +np.float32,0xbebd2048,0xbe9e208c,3 +np.float32,0xff08f7a2,0xbf800000,3 +np.float32,0xff473330,0xbf800000,3 +np.float32,0x1,0x1,3 +np.float32,0xbf5dc239,0xbf14584b,3 +np.float32,0x458e3f,0x458e3f,3 +np.float32,0xbdb8a650,0xbdb091f8,3 +np.float32,0xff336ffc,0xbf800000,3 +np.float32,0x3c60bd00,0x3c624966,3 +np.float32,0xbe16a4f8,0xbe0c1664,3 +np.float32,0x3f214246,0x3f60a0f0,3 +np.float32,0x7fa00000,0x7fe00000,3 +np.float32,0x7e08737e,0x7f800000,3 +np.float32,0x3f70574c,0x3fc74b8e,3 +np.float32,0xbed5745c,0xbeae8c77,3 +np.float32,0x361752,0x361752,3 +np.float32,0x3eb276d6,0x3ed584ea,3 +np.float32,0x3f03fc1e,0x3f2cb1a5,3 +np.float32,0x3fafd1,0x3fafd1,3 +np.float32,0x7e50d74c,0x7f800000,3 +np.float32,0x3eeca5,0x3eeca5,3 +np.float32,0x5dc963,0x5dc963,3 +np.float32,0x7f0e63ae,0x7f800000,3 +np.float32,0x8021745f,0x8021745f,3 +np.float32,0xbf5881a9,0xbf121d07,3 +np.float32,0x7dadc7fd,0x7f800000,3 +np.float32,0xbf2c0798,0xbefa86bb,3 +np.float32,0x3e635f50,0x3e7e97a9,3 +np.float32,0xbf2053fa,0xbeee4c0e,3 +np.float32,0x3e8eee2b,0x3ea4dfcc,3 +np.float32,0xfc8a03c0,0xbf800000,3 +np.float32,0xfd9e4948,0xbf800000,3 +np.float32,0x801e817e,0x801e817e,3 +np.float32,0xbf603a27,0xbf1560c3,3 +np.float32,0x7f729809,0x7f800000,3 +np.float32,0x3f5a1864,0x3fac0e04,3 +np.float32,0x3e7648b8,0x3e8b3677,3 +np.float32,0x3edade24,0x3f088bc1,3 +np.float32,0x65e16e,0x65e16e,3 +np.float32,0x3f24aa50,0x3f671117,3 +np.float32,0x803cb1d0,0x803cb1d0,3 +np.float32,0xbe7b1858,0xbe5eadcc,3 +np.float32,0xbf19bb27,0xbee726fb,3 +np.float32,0xfd1f6e60,0xbf800000,3 +np.float32,0xfeb0de60,0xbf800000,3 +np.float32,0xff511a52,0xbf800000,3 +np.float32,0xff7757f7,0xbf800000,3 +np.float32,0x463ff5,0x463ff5,3 +np.float32,0x3f770d12,0x3fcffcc2,3 +np.float32,0xbf208562,0xbeee80dc,3 +np.float32,0x6df204,0x6df204,3 +np.float32,0xbf62d24f,0xbf1673fb,3 +np.float32,0x3dfcf210,0x3e069d5f,3 +np.float32,0xbef26002,0xbec114d7,3 +np.float32,0x7f800000,0x7f800000,3 +np.float32,0x7f30fb85,0x7f800000,3 +np.float32,0x7ee5dfef,0x7f800000,3 +np.float32,0x3f317829,0x3f800611,3 +np.float32,0x3f4b0bbd,0x3f9aec88,3 +np.float32,0x7edf708c,0x7f800000,3 +np.float32,0xff071260,0xbf800000,3 +np.float32,0x3e7b8c30,0x3e8e9198,3 +np.float32,0x3f33778b,0x3f82077f,3 +np.float32,0x3e8cd11d,0x3ea215fd,3 +np.float32,0x8004483d,0x8004483d,3 +np.float32,0x801633e3,0x801633e3,3 +np.float32,0x7e76eb15,0x7f800000,3 +np.float32,0x3c1571,0x3c1571,3 +np.float32,0x7de3de52,0x7f800000,3 +np.float32,0x804ae906,0x804ae906,3 +np.float32,0x7f3a2616,0x7f800000,3 +np.float32,0xff7fffff,0xbf800000,3 +np.float32,0xff5d17e4,0xbf800000,3 +np.float32,0xbeaa6704,0xbe90f252,3 +np.float32,0x7e6a43af,0x7f800000,3 +np.float32,0x2a0f35,0x2a0f35,3 +np.float32,0xfd8fece0,0xbf800000,3 +np.float32,0xfeef2e2a,0xbf800000,3 +np.float32,0xff800000,0xbf800000,3 +np.float32,0xbeefcc52,0xbebf78e4,3 +np.float32,0x3db6c490,0x3dbf2bd5,3 +np.float32,0x8290f,0x8290f,3 +np.float32,0xbeace648,0xbe92bb7f,3 +np.float32,0x801fea79,0x801fea79,3 +np.float32,0x3ea6c230,0x3ec51ebf,3 +np.float32,0x3e5f2ca3,0x3e795c8a,3 +np.float32,0x3eb6f634,0x3edbeb9f,3 +np.float32,0xff790b45,0xbf800000,3 +np.float32,0x3d82e240,0x3d872816,3 +np.float32,0x3f0d6a57,0x3f3cc7db,3 +np.float32,0x7f08531a,0x7f800000,3 +np.float32,0x702b6d,0x702b6d,3 +np.float32,0x7d3a3c38,0x7f800000,3 +np.float32,0x3d0a7fb3,0x3d0cddf3,3 +np.float32,0xff28084c,0xbf800000,3 +np.float32,0xfeee8804,0xbf800000,3 +np.float32,0x804094eb,0x804094eb,3 +np.float32,0x7acb39,0x7acb39,3 +np.float32,0x3f01c07a,0x3f28f88c,3 +np.float32,0x3e05c500,0x3e0ee674,3 +np.float32,0xbe6f7c38,0xbe558ac1,3 +np.float32,0x803b1f4b,0x803b1f4b,3 +np.float32,0xbf76561f,0xbf1e332b,3 +np.float32,0xff30d368,0xbf800000,3 +np.float32,0x7e2e1f38,0x7f800000,3 +np.float32,0x3ee085b8,0x3f0ce7c0,3 +np.float32,0x8064c4a7,0x8064c4a7,3 +np.float32,0xa7c1d,0xa7c1d,3 +np.float32,0x3f27498a,0x3f6c14bc,3 +np.float32,0x137ca,0x137ca,3 +np.float32,0x3d0a5c60,0x3d0cb969,3 +np.float32,0x80765f1f,0x80765f1f,3 +np.float32,0x80230a71,0x80230a71,3 +np.float32,0x3f321ed2,0x3f80acf4,3 +np.float32,0x7d61e7f4,0x7f800000,3 +np.float32,0xbf39f7f2,0xbf0430f7,3 +np.float32,0xbe2503f8,0xbe1867e8,3 +np.float32,0x29333d,0x29333d,3 +np.float32,0x7edc5a0e,0x7f800000,3 +np.float32,0xbe81a8a2,0xbe651663,3 +np.float32,0x7f76ab6d,0x7f800000,3 +np.float32,0x7f46111f,0x7f800000,3 +np.float32,0xff0fc888,0xbf800000,3 +np.float32,0x805ece89,0x805ece89,3 +np.float32,0xc390b,0xc390b,3 +np.float32,0xff64bdee,0xbf800000,3 +np.float32,0x3dd07e4e,0x3ddb79bd,3 +np.float32,0xfecc1f10,0xbf800000,3 +np.float32,0x803f5177,0x803f5177,3 +np.float32,0x802a24d2,0x802a24d2,3 +np.float32,0x7f27d0cc,0x7f800000,3 +np.float32,0x3ef57c98,0x3f1d7e88,3 +np.float32,0x7b848d,0x7b848d,3 +np.float32,0x7f7fffff,0x7f800000,3 +np.float32,0xfe889c46,0xbf800000,3 +np.float32,0xff2d6dc5,0xbf800000,3 +np.float32,0x3f53a186,0x3fa492a6,3 +np.float32,0xbf239c94,0xbef1c90c,3 +np.float32,0xff7c0f4e,0xbf800000,3 +np.float32,0x3e7c69a9,0x3e8f1f3a,3 +np.float32,0xbf47c9e9,0xbf0ab2a9,3 +np.float32,0xbc1eaf00,0xbc1deae9,3 +np.float32,0x3f4a6d39,0x3f9a3d8e,3 +np.float32,0x3f677930,0x3fbc26eb,3 +np.float32,0x3f45eea1,0x3f955418,3 +np.float32,0x7f61a1f8,0x7f800000,3 +np.float32,0xff58c7c6,0xbf800000,3 +np.float32,0x80239801,0x80239801,3 +np.float32,0xff56e616,0xbf800000,3 +np.float32,0xff62052c,0xbf800000,3 +np.float32,0x8009b615,0x8009b615,3 +np.float32,0x293d6b,0x293d6b,3 +np.float32,0xfe9e585c,0xbf800000,3 +np.float32,0x7f58ff4b,0x7f800000,3 +np.float32,0x10937c,0x10937c,3 +np.float32,0x7f5cc13f,0x7f800000,3 +np.float32,0x110c5d,0x110c5d,3 +np.float32,0x805e51fc,0x805e51fc,3 +np.float32,0xbedcf70a,0xbeb3766c,3 +np.float32,0x3f4d5e42,0x3f9d8091,3 +np.float32,0xff5925a0,0xbf800000,3 +np.float32,0x7e87cafa,0x7f800000,3 +np.float32,0xbf6474b2,0xbf171fee,3 +np.float32,0x4b39b2,0x4b39b2,3 +np.float32,0x8020cc28,0x8020cc28,3 +np.float32,0xff004ed8,0xbf800000,3 +np.float32,0xbf204cf5,0xbeee448d,3 +np.float32,0x3e30cf10,0x3e40fdb1,3 +np.float32,0x80202bee,0x80202bee,3 +np.float32,0xbf55a985,0xbf10e2bc,3 +np.float32,0xbe297dd8,0xbe1c351c,3 +np.float32,0x5780d9,0x5780d9,3 +np.float32,0x7ef729fa,0x7f800000,3 +np.float32,0x8039a3b5,0x8039a3b5,3 +np.float32,0x7cdd3f,0x7cdd3f,3 +np.float32,0x7ef0145a,0x7f800000,3 +np.float32,0x807ad7ae,0x807ad7ae,3 +np.float32,0x7f6c2643,0x7f800000,3 +np.float32,0xbec56124,0xbea3c929,3 +np.float32,0x512c3b,0x512c3b,3 +np.float32,0xbed3effe,0xbead8c1e,3 +np.float32,0x7f5e0a4d,0x7f800000,3 +np.float32,0x3f315316,0x3f7fc200,3 +np.float32,0x7eca5727,0x7f800000,3 +np.float32,0x7f4834f3,0x7f800000,3 +np.float32,0x8004af6d,0x8004af6d,3 +np.float32,0x3f223ca4,0x3f6277e3,3 +np.float32,0x7eea4fdd,0x7f800000,3 +np.float32,0x3e7143e8,0x3e880763,3 +np.float32,0xbf737008,0xbf1d160e,3 +np.float32,0xfc408b00,0xbf800000,3 +np.float32,0x803912ca,0x803912ca,3 +np.float32,0x7db31f4e,0x7f800000,3 +np.float32,0xff578b54,0xbf800000,3 +np.float32,0x3f068ec4,0x3f31062b,3 +np.float32,0x35f64f,0x35f64f,3 +np.float32,0x80437df4,0x80437df4,3 +np.float32,0x568059,0x568059,3 +np.float32,0x8005f8ba,0x8005f8ba,3 +np.float32,0x6824ad,0x6824ad,3 +np.float32,0xff3fdf30,0xbf800000,3 +np.float32,0xbf6f7682,0xbf1b89d6,3 +np.float32,0x3dcea8a0,0x3dd971f5,3 +np.float32,0x3ee32a62,0x3f0ef5a9,3 +np.float32,0xbf735bcd,0xbf1d0e3d,3 +np.float32,0x7e8c7c28,0x7f800000,3 +np.float32,0x3ed552bc,0x3f045161,3 +np.float32,0xfed90a8a,0xbf800000,3 +np.float32,0xbe454368,0xbe336d2a,3 +np.float32,0xbf171d26,0xbee4442d,3 +np.float32,0x80652bf9,0x80652bf9,3 +np.float32,0xbdbaaa20,0xbdb26914,3 +np.float32,0x3f56063d,0x3fa7522e,3 +np.float32,0x3d3d4fd3,0x3d41c13f,3 +np.float32,0x80456040,0x80456040,3 +np.float32,0x3dc15586,0x3dcac0ef,3 +np.float32,0x7f753060,0x7f800000,3 +np.float32,0x7f7d8039,0x7f800000,3 +np.float32,0xfdebf280,0xbf800000,3 +np.float32,0xbf1892c3,0xbee5e116,3 +np.float32,0xbf0f1468,0xbedb3878,3 +np.float32,0x40d85c,0x40d85c,3 +np.float32,0x3f93dd,0x3f93dd,3 +np.float32,0xbf5730fd,0xbf118c24,3 +np.float32,0xfe17aa44,0xbf800000,3 +np.float32,0x3dc0baf4,0x3dca1716,3 +np.float32,0xbf3433d8,0xbf015efb,3 +np.float32,0x1c59f5,0x1c59f5,3 +np.float32,0x802b1540,0x802b1540,3 +np.float32,0xbe47df6c,0xbe35936e,3 +np.float32,0xbe8e7070,0xbe78af32,3 +np.float32,0xfe7057f4,0xbf800000,3 +np.float32,0x80668b69,0x80668b69,3 +np.float32,0xbe677810,0xbe4f2c2d,3 +np.float32,0xbe7a2f1c,0xbe5df733,3 +np.float32,0xfeb79e3c,0xbf800000,3 +np.float32,0xbeb6e320,0xbe99c9e8,3 +np.float32,0xfea188f2,0xbf800000,3 +np.float32,0x7dcaeb15,0x7f800000,3 +np.float32,0x1be567,0x1be567,3 +np.float32,0xbf4041cc,0xbf07320d,3 +np.float32,0x3f721aa7,0x3fc98e9a,3 +np.float32,0x7f5aa835,0x7f800000,3 +np.float32,0x15180e,0x15180e,3 +np.float32,0x3f73d739,0x3fcbccdb,3 +np.float32,0xbeecd380,0xbebd9b36,3 +np.float32,0x3f2caec7,0x3f768fea,3 +np.float32,0xbeaf65f2,0xbe9482bb,3 +np.float32,0xfe6aa384,0xbf800000,3 +np.float32,0xbf4f2c0a,0xbf0e085e,3 +np.float32,0xbf2b5907,0xbef9d431,3 +np.float32,0x3e855e0d,0x3e985960,3 +np.float32,0x8056cc64,0x8056cc64,3 +np.float32,0xff746bb5,0xbf800000,3 +np.float32,0x3e0332f6,0x3e0bf986,3 +np.float32,0xff637720,0xbf800000,3 +np.float32,0xbf330676,0xbf00c990,3 +np.float32,0x3ec449a1,0x3eef3862,3 +np.float32,0x766541,0x766541,3 +np.float32,0xfe2edf6c,0xbf800000,3 +np.float32,0xbebb28ca,0xbe9cc3e2,3 +np.float32,0x3f16c930,0x3f4d5ce4,3 +np.float32,0x7f1a9a4a,0x7f800000,3 +np.float32,0x3e9ba1,0x3e9ba1,3 +np.float32,0xbf73d5f6,0xbf1d3d69,3 +np.float32,0xfdc8a8b0,0xbf800000,3 +np.float32,0x50f051,0x50f051,3 +np.float32,0xff0add02,0xbf800000,3 +np.float32,0x1e50bf,0x1e50bf,3 +np.float32,0x3f04d287,0x3f2e1948,3 +np.float32,0x7f1e50,0x7f1e50,3 +np.float32,0x2affb3,0x2affb3,3 +np.float32,0x80039f07,0x80039f07,3 +np.float32,0x804ba79e,0x804ba79e,3 +np.float32,0x7b5a8eed,0x7f800000,3 +np.float32,0x3e1a8b28,0x3e26d0a7,3 +np.float32,0x3ea95f29,0x3ec8bfa4,3 +np.float32,0x7e09fa55,0x7f800000,3 +np.float32,0x7eacb1b3,0x7f800000,3 +np.float32,0x3e8ad7c0,0x3e9f7dec,3 +np.float32,0x7e0e997c,0x7f800000,3 +np.float32,0x3f4422b4,0x3f936398,3 +np.float32,0x806bd222,0x806bd222,3 +np.float32,0x677ae6,0x677ae6,3 +np.float32,0x62cf68,0x62cf68,3 +np.float32,0x7e4e594e,0x7f800000,3 +np.float32,0x80445fd1,0x80445fd1,3 +np.float32,0xff3a0d04,0xbf800000,3 +np.float32,0x8052b256,0x8052b256,3 +np.float32,0x3cb34440,0x3cb53e11,3 +np.float32,0xbf0e3865,0xbeda3c6d,3 +np.float32,0x3f49f5df,0x3f99ba17,3 +np.float32,0xbed75a22,0xbeafcc09,3 +np.float32,0xbf7aec64,0xbf1fefc8,3 +np.float32,0x7f35a62d,0x7f800000,3 +np.float32,0xbf787b03,0xbf1f03fc,3 +np.float32,0x8006a62a,0x8006a62a,3 +np.float32,0x3f6419e7,0x3fb803c7,3 +np.float32,0x3ecea2e5,0x3efe8f01,3 +np.float32,0x80603577,0x80603577,3 +np.float32,0xff73198c,0xbf800000,3 +np.float32,0x7def110a,0x7f800000,3 +np.float32,0x544efd,0x544efd,3 +np.float32,0x3f052340,0x3f2ea0fc,3 +np.float32,0xff306666,0xbf800000,3 +np.float32,0xbf800000,0xbf21d2a7,3 +np.float32,0xbed3e150,0xbead826a,3 +np.float32,0x3f430c99,0x3f92390f,3 +np.float32,0xbf4bffa4,0xbf0c9c73,3 +np.float32,0xfd97a710,0xbf800000,3 +np.float32,0x3cadf0fe,0x3cafcd1a,3 +np.float32,0x807af7b4,0x807af7b4,3 +np.float32,0xbc508600,0xbc4f33bc,3 +np.float32,0x7f3e0ec7,0x7f800000,3 +np.float32,0xbe51334c,0xbe3d36f7,3 +np.float32,0xfe7b7fb4,0xbf800000,3 +np.float32,0xfed9c45e,0xbf800000,3 +np.float32,0x3da024eb,0x3da6926a,3 +np.float32,0x7eed9e76,0x7f800000,3 +np.float32,0xbf2b8f1f,0xbefa0b91,3 +np.float32,0x3f2b9286,0x3f746318,3 +np.float32,0xfe8af49c,0xbf800000,3 +np.float32,0x9c4f7,0x9c4f7,3 +np.float32,0x801d7543,0x801d7543,3 +np.float32,0xbf66474a,0xbf17de66,3 +np.float32,0xbf562155,0xbf1116b1,3 +np.float32,0x46a8de,0x46a8de,3 +np.float32,0x8053fe6b,0x8053fe6b,3 +np.float32,0xbf6ee842,0xbf1b51f3,3 +np.float32,0xbf6ad78e,0xbf19b565,3 +np.float32,0xbf012574,0xbecad7ff,3 +np.float32,0x748364,0x748364,3 +np.float32,0x8073f59b,0x8073f59b,3 +np.float32,0xff526825,0xbf800000,3 +np.float32,0xfeb02dc4,0xbf800000,3 +np.float32,0x8033eb1c,0x8033eb1c,3 +np.float32,0x3f3685ea,0x3f8520cc,3 +np.float32,0x7f657902,0x7f800000,3 +np.float32,0xbf75eac4,0xbf1e0a1f,3 +np.float32,0xfe67f384,0xbf800000,3 +np.float32,0x3f56d3cc,0x3fa83faf,3 +np.float32,0x44a4ce,0x44a4ce,3 +np.float32,0x1dc4b3,0x1dc4b3,3 +np.float32,0x4fb3b2,0x4fb3b2,3 +np.float32,0xbea904a4,0xbe8ff3ed,3 +np.float32,0x7e668f16,0x7f800000,3 +np.float32,0x7f538378,0x7f800000,3 +np.float32,0x80541709,0x80541709,3 +np.float32,0x80228040,0x80228040,3 +np.float32,0x7ef9694e,0x7f800000,3 +np.float32,0x3f5fca9b,0x3fb2ce54,3 +np.float32,0xbe9c43c2,0xbe86ab84,3 +np.float32,0xfecee000,0xbf800000,3 +np.float32,0x5a65c2,0x5a65c2,3 +np.float32,0x3f736572,0x3fcb3985,3 +np.float32,0xbf2a03f7,0xbef87600,3 +np.float32,0xfe96b488,0xbf800000,3 +np.float32,0xfedd8800,0xbf800000,3 +np.float32,0x80411804,0x80411804,3 +np.float32,0x7edcb0a6,0x7f800000,3 +np.float32,0x2bb882,0x2bb882,3 +np.float32,0x3f800000,0x3fdbf0a9,3 +np.float32,0x764b27,0x764b27,3 +np.float32,0x7e92035d,0x7f800000,3 +np.float32,0x3e80facb,0x3e92ae1d,3 +np.float32,0x8040b81a,0x8040b81a,3 +np.float32,0x7f487fe4,0x7f800000,3 +np.float32,0xbc641780,0xbc6282ed,3 +np.float32,0x804b0bb9,0x804b0bb9,3 +np.float32,0x7d0b7c39,0x7f800000,3 +np.float32,0xff072080,0xbf800000,3 +np.float32,0xbed7aff8,0xbeb00462,3 +np.float32,0x35e247,0x35e247,3 +np.float32,0xbf7edd19,0xbf216766,3 +np.float32,0x8004a539,0x8004a539,3 +np.float32,0xfdfc1790,0xbf800000,3 +np.float32,0x8037a841,0x8037a841,3 +np.float32,0xfed0a8a8,0xbf800000,3 +np.float32,0x7f1f1697,0x7f800000,3 +np.float32,0x3f2ccc6e,0x3f76ca23,3 +np.float32,0x35eada,0x35eada,3 +np.float32,0xff111f42,0xbf800000,3 +np.float32,0x3ee1ab7f,0x3f0dcbbe,3 +np.float32,0xbf6e89ee,0xbf1b2cd4,3 +np.float32,0x3f58611c,0x3faa0cdc,3 +np.float32,0x1ac6a6,0x1ac6a6,3 +np.float32,0xbf1286fa,0xbedf2312,3 +np.float32,0x7e451137,0x7f800000,3 +np.float32,0xbe92c326,0xbe7f3405,3 +np.float32,0x3f2fdd16,0x3f7cd87b,3 +np.float32,0xbe5c0ea0,0xbe4604c2,3 +np.float32,0xbdb29968,0xbdab0883,3 +np.float32,0x3964,0x3964,3 +np.float32,0x3f0dc236,0x3f3d60a0,3 +np.float32,0x7c3faf06,0x7f800000,3 +np.float32,0xbef41f7a,0xbec22b16,3 +np.float32,0x3f4c0289,0x3f9bfdcc,3 +np.float32,0x806084e9,0x806084e9,3 +np.float32,0x3ed1d8dd,0x3f01b0c1,3 +np.float32,0x806d8d8b,0x806d8d8b,3 +np.float32,0x3f052180,0x3f2e9e0a,3 +np.float32,0x803d85d5,0x803d85d5,3 +np.float32,0x3e0afd70,0x3e14dd48,3 +np.float32,0x2fbc63,0x2fbc63,3 +np.float32,0x2e436f,0x2e436f,3 +np.float32,0xbf7b19e6,0xbf2000da,3 +np.float32,0x3f34022e,0x3f829362,3 +np.float32,0x3d2b40e0,0x3d2ee246,3 +np.float32,0x3f5298b4,0x3fa3649b,3 +np.float32,0xbdb01328,0xbda8b7de,3 +np.float32,0x7f693c81,0x7f800000,3 +np.float32,0xbeb1abc0,0xbe961edc,3 +np.float32,0x801d9b5d,0x801d9b5d,3 +np.float32,0x80628668,0x80628668,3 +np.float32,0x800f57dd,0x800f57dd,3 +np.float32,0x8017c94f,0x8017c94f,3 +np.float32,0xbf16f5f4,0xbee418b8,3 +np.float32,0x3e686476,0x3e827022,3 +np.float32,0xbf256796,0xbef3abd9,3 +np.float32,0x7f1b4485,0x7f800000,3 +np.float32,0xbea0b3cc,0xbe89ed21,3 +np.float32,0xfee08b2e,0xbf800000,3 +np.float32,0x523cb4,0x523cb4,3 +np.float32,0x3daf2cb2,0x3db6e273,3 +np.float32,0xbd531c40,0xbd4dc323,3 +np.float32,0x80078fe5,0x80078fe5,3 +np.float32,0x80800000,0x80800000,3 +np.float32,0x3f232438,0x3f642d1a,3 +np.float32,0x3ec29446,0x3eecb7c0,3 +np.float32,0x3dbcd2a4,0x3dc5cd1d,3 +np.float32,0x7f045b0d,0x7f800000,3 +np.float32,0x7f22e6d1,0x7f800000,3 +np.float32,0xbf5d3430,0xbf141c80,3 +np.float32,0xbe03ec70,0xbdf78ee6,3 +np.float32,0x3e93ec9a,0x3eab822f,3 +np.float32,0x7f3b9262,0x7f800000,3 +np.float32,0x65ac6a,0x65ac6a,3 +np.float32,0x3db9a8,0x3db9a8,3 +np.float32,0xbf37ab59,0xbf031306,3 +np.float32,0x33c40e,0x33c40e,3 +np.float32,0x7f7a478f,0x7f800000,3 +np.float32,0xbe8532d0,0xbe6a906f,3 +np.float32,0x801c081d,0x801c081d,3 +np.float32,0xbe4212a0,0xbe30ca73,3 +np.float32,0xff0b603e,0xbf800000,3 +np.float32,0x4554dc,0x4554dc,3 +np.float32,0x3dd324be,0x3dde695e,3 +np.float32,0x3f224c44,0x3f629557,3 +np.float32,0x8003cd79,0x8003cd79,3 +np.float32,0xbf31351c,0xbeffc2fd,3 +np.float32,0x8034603a,0x8034603a,3 +np.float32,0xbf6fcb70,0xbf1bab24,3 +np.float32,0x804eb67e,0x804eb67e,3 +np.float32,0xff05c00e,0xbf800000,3 +np.float32,0x3eb5b36f,0x3eda1ec7,3 +np.float32,0x3f1ed7f9,0x3f5c1d90,3 +np.float32,0x3f052d8a,0x3f2eb24b,3 +np.float32,0x5ddf51,0x5ddf51,3 +np.float32,0x7e50c11c,0x7f800000,3 +np.float32,0xff74f55a,0xbf800000,3 +np.float32,0x4322d,0x4322d,3 +np.float32,0x3f16f8a9,0x3f4db27a,3 +np.float32,0x3f4f23d6,0x3f9f7c2c,3 +np.float32,0xbf706c1e,0xbf1bea0a,3 +np.float32,0x3f2cbd52,0x3f76ac77,3 +np.float32,0xf3043,0xf3043,3 +np.float32,0xfee79de0,0xbf800000,3 +np.float32,0x7e942f69,0x7f800000,3 +np.float32,0x180139,0x180139,3 +np.float32,0xff69c678,0xbf800000,3 +np.float32,0x3f46773f,0x3f95e840,3 +np.float32,0x804aae1c,0x804aae1c,3 +np.float32,0x3eb383b4,0x3ed7024c,3 +np.float32,0x8032624e,0x8032624e,3 +np.float32,0xbd0a0f80,0xbd07c27d,3 +np.float32,0xbf1c9b98,0xbeea4a61,3 +np.float32,0x7f370999,0x7f800000,3 +np.float32,0x801931f9,0x801931f9,3 +np.float32,0x3f6f45ce,0x3fc5eea0,3 +np.float32,0xff0ab4cc,0xbf800000,3 +np.float32,0x4c043d,0x4c043d,3 +np.float32,0x8002a599,0x8002a599,3 +np.float32,0xbc4a6080,0xbc4921d7,3 +np.float32,0x3f008d14,0x3f26fb72,3 +np.float32,0x7f48b3d9,0x7f800000,3 +np.float32,0x7cb2ec7e,0x7f800000,3 +np.float32,0xbf1338bd,0xbedfeb61,3 +np.float32,0x0,0x0,3 +np.float32,0xbf2f5b64,0xbefde71c,3 +np.float32,0xbe422974,0xbe30dd56,3 +np.float32,0x3f776be8,0x3fd07950,3 +np.float32,0xbf3e97a1,0xbf06684a,3 +np.float32,0x7d28cb26,0x7f800000,3 +np.float32,0x801618d2,0x801618d2,3 +np.float32,0x807e4f83,0x807e4f83,3 +np.float32,0x8006b07d,0x8006b07d,3 +np.float32,0xfea1c042,0xbf800000,3 +np.float32,0xff24ef74,0xbf800000,3 +np.float32,0xfef7ab16,0xbf800000,3 +np.float32,0x70b771,0x70b771,3 +np.float32,0x7daeb64e,0x7f800000,3 +np.float32,0xbe66e378,0xbe4eb59c,3 +np.float32,0xbead1534,0xbe92dcf7,3 +np.float32,0x7e6769b8,0x7f800000,3 +np.float32,0x7ecd0890,0x7f800000,3 +np.float32,0xbe7380d8,0xbe58b747,3 +np.float32,0x3efa6f2f,0x3f218265,3 +np.float32,0x3f59dada,0x3fabc5eb,3 +np.float32,0xff0f2d20,0xbf800000,3 +np.float32,0x8060210e,0x8060210e,3 +np.float32,0x3ef681e8,0x3f1e51c8,3 +np.float32,0x77a6dd,0x77a6dd,3 +np.float32,0xbebfdd0e,0xbea00399,3 +np.float32,0xfe889b72,0xbf800000,3 +np.float32,0x8049ed2c,0x8049ed2c,3 +np.float32,0x3b089dc4,0x3b08c23e,3 +np.float32,0xbf13c7c4,0xbee08c28,3 +np.float32,0x3efa13b9,0x3f2137d7,3 +np.float32,0x3e9385dc,0x3eaaf914,3 +np.float32,0x7e0e6a43,0x7f800000,3 +np.float32,0x7df6d63f,0x7f800000,3 +np.float32,0x3f3efead,0x3f8dea03,3 +np.float32,0xff52548c,0xbf800000,3 +np.float32,0x803ff9d8,0x803ff9d8,3 +np.float32,0x3c825823,0x3c836303,3 +np.float32,0xfc9e97a0,0xbf800000,3 +np.float32,0xfe644f48,0xbf800000,3 +np.float32,0x802f5017,0x802f5017,3 +np.float32,0x3d5753b9,0x3d5d1661,3 +np.float32,0x7f2a55d2,0x7f800000,3 +np.float32,0x7f4dabfe,0x7f800000,3 +np.float32,0x3f49492a,0x3f98fc47,3 +np.float32,0x3f4d1589,0x3f9d2f82,3 +np.float32,0xff016208,0xbf800000,3 +np.float32,0xbf571cb7,0xbf118365,3 +np.float32,0xbf1ef297,0xbeecd136,3 +np.float32,0x36266b,0x36266b,3 +np.float32,0xbed07b0e,0xbeab4129,3 +np.float32,0x7f553365,0x7f800000,3 +np.float32,0xfe9bb8c6,0xbf800000,3 +np.float32,0xbeb497d6,0xbe982e19,3 +np.float32,0xbf27af6c,0xbef60d16,3 +np.float32,0x55cf51,0x55cf51,3 +np.float32,0x3eab1db0,0x3ecb2e4f,3 +np.float32,0x3e777603,0x3e8bf62f,3 +np.float32,0x7f10e374,0x7f800000,3 +np.float32,0xbf1f6480,0xbeed4b8d,3 +np.float32,0x40479d,0x40479d,3 +np.float32,0x156259,0x156259,3 +np.float32,0x3d852e30,0x3d899b2d,3 +np.float32,0x80014ff3,0x80014ff3,3 +np.float32,0xbd812fa8,0xbd7a645c,3 +np.float32,0x800ab780,0x800ab780,3 +np.float32,0x3ea02ff4,0x3ebc13bd,3 +np.float32,0x7e858b8e,0x7f800000,3 +np.float32,0x75d63b,0x75d63b,3 +np.float32,0xbeb15c94,0xbe95e6e3,3 +np.float32,0x3da0cee0,0x3da74a39,3 +np.float32,0xff21c01c,0xbf800000,3 +np.float32,0x8049b5eb,0x8049b5eb,3 +np.float32,0x80177ab0,0x80177ab0,3 +np.float32,0xff137a50,0xbf800000,3 +np.float32,0x3f7febba,0x3fdbd51c,3 +np.float32,0x8041e4dd,0x8041e4dd,3 +np.float32,0x99b8c,0x99b8c,3 +np.float32,0x5621ba,0x5621ba,3 +np.float32,0x14b534,0x14b534,3 +np.float32,0xbe2eb3a8,0xbe209c95,3 +np.float32,0x7e510c28,0x7f800000,3 +np.float32,0x804ec2f2,0x804ec2f2,3 +np.float32,0x3f662406,0x3fba82b0,3 +np.float32,0x800000,0x800000,3 +np.float32,0x3f3120d6,0x3f7f5d96,3 +np.float32,0x7f179b8e,0x7f800000,3 +np.float32,0x7f65278e,0x7f800000,3 +np.float32,0xfeb50f52,0xbf800000,3 +np.float32,0x7f051bd1,0x7f800000,3 +np.float32,0x7ea0558d,0x7f800000,3 +np.float32,0xbd0a96c0,0xbd08453f,3 +np.float64,0xee82da5ddd05c,0xee82da5ddd05c,3 +np.float64,0x800c3a22d7f87446,0x800c3a22d7f87446,3 +np.float64,0xbfd34b20eaa69642,0xbfd0a825e7688d3e,3 +np.float64,0x3fd6a0f2492d41e5,0x3fdb253b906057b3,3 +np.float64,0xbfda13d8783427b0,0xbfd56b1d76684332,3 +np.float64,0xbfe50b5a99ea16b5,0xbfded7dd82c6f746,3 +np.float64,0x3f82468fc0248d20,0x3f825b7fa9378ee9,3 +np.float64,0x7ff0000000000000,0x7ff0000000000000,3 +np.float64,0x856e50290adca,0x856e50290adca,3 +np.float64,0x7fde55a5fa3cab4b,0x7ff0000000000000,3 +np.float64,0x7fcf2c8dd93e591b,0x7ff0000000000000,3 +np.float64,0x8001b3a0e3236743,0x8001b3a0e3236743,3 +np.float64,0x8000fdb14821fb63,0x8000fdb14821fb63,3 +np.float64,0xbfe3645e08e6c8bc,0xbfdd161362a5e9ef,3 +np.float64,0x7feb34d28b3669a4,0x7ff0000000000000,3 +np.float64,0x80099dd810933bb1,0x80099dd810933bb1,3 +np.float64,0xbfedbcc1097b7982,0xbfe35d86414d53dc,3 +np.float64,0x7fdc406fbdb880de,0x7ff0000000000000,3 +np.float64,0x800c4bf85ab897f1,0x800c4bf85ab897f1,3 +np.float64,0x3fd8f7b0e0b1ef60,0x3fde89b497ae20d8,3 +np.float64,0xffe4fced5c69f9da,0xbff0000000000000,3 +np.float64,0xbfe54d421fea9a84,0xbfdf1be0cbfbfcba,3 +np.float64,0x800af72f3535ee5f,0x800af72f3535ee5f,3 +np.float64,0x3fe24e6570e49ccb,0x3fe8b3a86d970411,3 +np.float64,0xbfdd7b22d0baf646,0xbfd79fac2e4f7558,3 +np.float64,0xbfe6a7654c6d4eca,0xbfe03c1f13f3b409,3 +np.float64,0x3fe2c3eb662587d7,0x3fe98566e625d4f5,3 +np.float64,0x3b1ef71e763e0,0x3b1ef71e763e0,3 +np.float64,0xffed03c6baba078d,0xbff0000000000000,3 +np.float64,0x3febac19d0b75834,0x3ff5fdacc9d51bcd,3 +np.float64,0x800635d6794c6bae,0x800635d6794c6bae,3 +np.float64,0xbfe8cafc827195f9,0xbfe1411438608ae1,3 +np.float64,0x7feeb616a83d6c2c,0x7ff0000000000000,3 +np.float64,0x3fd52d62a2aa5ac5,0x3fd91a07a7f18f44,3 +np.float64,0x80036996b8a6d32e,0x80036996b8a6d32e,3 +np.float64,0x2b1945965632a,0x2b1945965632a,3 +np.float64,0xbfecb5e8c9796bd2,0xbfe2f40fca276aa2,3 +np.float64,0x3fe8669ed4f0cd3e,0x3ff24c89fc9cdbff,3 +np.float64,0x71e9f65ee3d3f,0x71e9f65ee3d3f,3 +np.float64,0xbfd5ab262bab564c,0xbfd261ae108ef79e,3 +np.float64,0xbfe7091342ee1226,0xbfe06bf5622d75f6,3 +np.float64,0x49e888d093d12,0x49e888d093d12,3 +np.float64,0x2272f3dc44e5f,0x2272f3dc44e5f,3 +np.float64,0x7fe98736e0b30e6d,0x7ff0000000000000,3 +np.float64,0x30fa9cde61f54,0x30fa9cde61f54,3 +np.float64,0x7fdc163fc0382c7f,0x7ff0000000000000,3 +np.float64,0xffb40d04ee281a08,0xbff0000000000000,3 +np.float64,0xffe624617f2c48c2,0xbff0000000000000,3 +np.float64,0x3febb582bd376b05,0x3ff608da584d1716,3 +np.float64,0xfc30a5a5f8615,0xfc30a5a5f8615,3 +np.float64,0x3fef202efd7e405e,0x3ffa52009319b069,3 +np.float64,0x8004d0259829a04c,0x8004d0259829a04c,3 +np.float64,0x800622dc71ec45ba,0x800622dc71ec45ba,3 +np.float64,0xffefffffffffffff,0xbff0000000000000,3 +np.float64,0x800e89113c9d1223,0x800e89113c9d1223,3 +np.float64,0x7fba7fde3034ffbb,0x7ff0000000000000,3 +np.float64,0xbfeea31e807d463d,0xbfe3b7369b725915,3 +np.float64,0x3feb7c9589f6f92c,0x3ff5c56cf71b0dff,3 +np.float64,0x3fd52d3b59aa5a77,0x3fd919d0f683fd07,3 +np.float64,0x800de90a43fbd215,0x800de90a43fbd215,3 +np.float64,0x3fe7eb35a9efd66b,0x3ff1c940dbfc6ef9,3 +np.float64,0xbda0adcb7b416,0xbda0adcb7b416,3 +np.float64,0x7fc5753e3a2aea7b,0x7ff0000000000000,3 +np.float64,0xffdd101d103a203a,0xbff0000000000000,3 +np.float64,0x7fcb54f56836a9ea,0x7ff0000000000000,3 +np.float64,0xbfd61c8d6eac391a,0xbfd2b23bc0a2cef4,3 +np.float64,0x3feef55de37deabc,0x3ffa198639a0161d,3 +np.float64,0x7fe4ffbfaea9ff7e,0x7ff0000000000000,3 +np.float64,0x9d1071873a20e,0x9d1071873a20e,3 +np.float64,0x3fef1ecb863e3d97,0x3ffa502a81e09cfc,3 +np.float64,0xad2da12b5a5b4,0xad2da12b5a5b4,3 +np.float64,0xffe614b74c6c296e,0xbff0000000000000,3 +np.float64,0xffe60d3f286c1a7e,0xbff0000000000000,3 +np.float64,0x7fda7d91f4b4fb23,0x7ff0000000000000,3 +np.float64,0x800023f266a047e6,0x800023f266a047e6,3 +np.float64,0x7fdf5f9ad23ebf35,0x7ff0000000000000,3 +np.float64,0x3fa7459f002e8b3e,0x3fa7cf178dcf0af6,3 +np.float64,0x3fe9938d61f3271b,0x3ff39516a13caec3,3 +np.float64,0xbfd59314c3ab262a,0xbfd250830f73efd2,3 +np.float64,0xbfc7e193f72fc328,0xbfc5c924339dd7a8,3 +np.float64,0x7fec1965f17832cb,0x7ff0000000000000,3 +np.float64,0xbfd932908eb26522,0xbfd4d4312d272580,3 +np.float64,0xbfdf2d08e2be5a12,0xbfd8add1413b0b1b,3 +np.float64,0x7fdcf7cc74b9ef98,0x7ff0000000000000,3 +np.float64,0x7fc79300912f2600,0x7ff0000000000000,3 +np.float64,0xffd4bd8f23297b1e,0xbff0000000000000,3 +np.float64,0x41869ce0830e,0x41869ce0830e,3 +np.float64,0x3fe5dcec91ebb9da,0x3fef5e213598cbd4,3 +np.float64,0x800815d9c2902bb4,0x800815d9c2902bb4,3 +np.float64,0x800ba1a4b877434a,0x800ba1a4b877434a,3 +np.float64,0x80069d7bdc4d3af8,0x80069d7bdc4d3af8,3 +np.float64,0xcf00d4339e01b,0xcf00d4339e01b,3 +np.float64,0x80072b71bd4e56e4,0x80072b71bd4e56e4,3 +np.float64,0x80059ca6fbab394f,0x80059ca6fbab394f,3 +np.float64,0x3fe522fc092a45f8,0x3fedf212682bf894,3 +np.float64,0x7fe17f384ea2fe70,0x7ff0000000000000,3 +np.float64,0x0,0x0,3 +np.float64,0x3f72bb4c20257698,0x3f72c64766b52069,3 +np.float64,0x7fbc97c940392f92,0x7ff0000000000000,3 +np.float64,0xffc5904ebd2b209c,0xbff0000000000000,3 +np.float64,0xbfe34fb55b669f6a,0xbfdcff81dd30a49d,3 +np.float64,0x8007ccda006f99b5,0x8007ccda006f99b5,3 +np.float64,0x3fee50e4c8fca1ca,0x3ff9434c7750ad0f,3 +np.float64,0x7fee7b07c67cf60f,0x7ff0000000000000,3 +np.float64,0x3fdcce4a5a399c95,0x3fe230c83f28218a,3 +np.float64,0x7fee5187b37ca30e,0x7ff0000000000000,3 +np.float64,0x3fc48f6a97291ed8,0x3fc64db6200a9833,3 +np.float64,0xc7fec3498ffd9,0xc7fec3498ffd9,3 +np.float64,0x800769c59d2ed38c,0x800769c59d2ed38c,3 +np.float64,0xffe69ede782d3dbc,0xbff0000000000000,3 +np.float64,0x3fecd9770979b2ee,0x3ff76a1f2f0f08f2,3 +np.float64,0x5aa358a8b546c,0x5aa358a8b546c,3 +np.float64,0xbfe795a0506f2b40,0xbfe0afcc52c0166b,3 +np.float64,0xffd4ada1e8a95b44,0xbff0000000000000,3 +np.float64,0xffcac1dc213583b8,0xbff0000000000000,3 +np.float64,0xffe393c15fa72782,0xbff0000000000000,3 +np.float64,0xbfcd6a3c113ad478,0xbfca47a2157b9cdd,3 +np.float64,0xffedde20647bbc40,0xbff0000000000000,3 +np.float64,0x3fd0d011b1a1a024,0x3fd33a57945559f4,3 +np.float64,0x3fef27e29f7e4fc6,0x3ffa5c314e0e3d69,3 +np.float64,0xffe96ff71f72dfee,0xbff0000000000000,3 +np.float64,0xffe762414f2ec482,0xbff0000000000000,3 +np.float64,0x3fc2dcfd3d25b9fa,0x3fc452f41682a12e,3 +np.float64,0xbfbdb125b63b6248,0xbfbc08e6553296d4,3 +np.float64,0x7b915740f724,0x7b915740f724,3 +np.float64,0x60b502b2c16a1,0x60b502b2c16a1,3 +np.float64,0xbfeb38b0be367162,0xbfe254f6782cfc47,3 +np.float64,0x800dc39a3edb8735,0x800dc39a3edb8735,3 +np.float64,0x3fea4fb433349f68,0x3ff468b97cf699f5,3 +np.float64,0xbfd49967962932d0,0xbfd19ceb41ff4cd0,3 +np.float64,0xbfebf75cd377eeba,0xbfe2a576bdbccccc,3 +np.float64,0xbfb653d65c2ca7b0,0xbfb561ab8fcb3f26,3 +np.float64,0xffe3f34b8727e696,0xbff0000000000000,3 +np.float64,0x3fdd798064baf301,0x3fe2b7c130a6fc63,3 +np.float64,0x3febe027e6b7c050,0x3ff63bac1b22e12d,3 +np.float64,0x7fcaa371af3546e2,0x7ff0000000000000,3 +np.float64,0xbfe6ee980a2ddd30,0xbfe05f0bc5dc80d2,3 +np.float64,0xc559c33f8ab39,0xc559c33f8ab39,3 +np.float64,0x84542c2b08a86,0x84542c2b08a86,3 +np.float64,0xbfe5645e046ac8bc,0xbfdf3398dc3cc1bd,3 +np.float64,0x3fee8c48ae7d1892,0x3ff9902899480526,3 +np.float64,0x3fb706471c2e0c8e,0x3fb817787aace8db,3 +np.float64,0x7fefe78f91ffcf1e,0x7ff0000000000000,3 +np.float64,0xbfcf6d560b3edaac,0xbfcbddc72a2130df,3 +np.float64,0x7fd282bfd925057f,0x7ff0000000000000,3 +np.float64,0x3fb973dbee32e7b8,0x3fbac2c87cbd0215,3 +np.float64,0x3fd1ce38ff239c72,0x3fd4876de5164420,3 +np.float64,0x8008ac2e3c31585d,0x8008ac2e3c31585d,3 +np.float64,0x3fa05e06dc20bc00,0x3fa0a1b7de904dce,3 +np.float64,0x7fd925f215324be3,0x7ff0000000000000,3 +np.float64,0x3f949d95d0293b2c,0x3f94d31197d51874,3 +np.float64,0xffdded9e67bbdb3c,0xbff0000000000000,3 +np.float64,0x3fed390dcfba721c,0x3ff7e08c7a709240,3 +np.float64,0x7fe6e62300adcc45,0x7ff0000000000000,3 +np.float64,0xbfd779bc312ef378,0xbfd3a6cb64bb0181,3 +np.float64,0x3fe43e9877287d31,0x3fec3e100ef935fd,3 +np.float64,0x210b68e44216e,0x210b68e44216e,3 +np.float64,0x3fcdffc1e73bff84,0x3fd0e729d02ec539,3 +np.float64,0xcea10c0f9d422,0xcea10c0f9d422,3 +np.float64,0x7feb97a82d772f4f,0x7ff0000000000000,3 +np.float64,0x9b4b4d953696a,0x9b4b4d953696a,3 +np.float64,0x3fd1bd8e95237b1d,0x3fd4716dd34cf828,3 +np.float64,0x800fc273841f84e7,0x800fc273841f84e7,3 +np.float64,0xbfd2aef167255de2,0xbfd0340f30d82f18,3 +np.float64,0x800d021a551a0435,0x800d021a551a0435,3 +np.float64,0xffebf934a8b7f268,0xbff0000000000000,3 +np.float64,0x3fd819849fb03308,0x3fdd43bca0aac749,3 +np.float64,0x7ff8000000000000,0x7ff8000000000000,3 +np.float64,0x27c34b064f86a,0x27c34b064f86a,3 +np.float64,0x7fef4f5a373e9eb3,0x7ff0000000000000,3 +np.float64,0x7fd92fccce325f99,0x7ff0000000000000,3 +np.float64,0x800520869d6a410e,0x800520869d6a410e,3 +np.float64,0x3fccbcaddf397958,0x3fd01bf6b0c4d97f,3 +np.float64,0x80039ebfc4273d80,0x80039ebfc4273d80,3 +np.float64,0xbfed1f0b3c7a3e16,0xbfe31ea6e4c69141,3 +np.float64,0x7fee1bb7c4bc376f,0x7ff0000000000000,3 +np.float64,0xbfa8bee1d8317dc0,0xbfa8283b7dbf95a9,3 +np.float64,0x3fe797db606f2fb6,0x3ff171b1c2bc8fe5,3 +np.float64,0xbfee2ecfdbbc5da0,0xbfe38a3f0a43d14e,3 +np.float64,0x3fe815c7f1302b90,0x3ff1f65165c45d71,3 +np.float64,0xbfbb265c94364cb8,0xbfb9c27ec61a9a1d,3 +np.float64,0x3fcf1cab5d3e3957,0x3fd19c07444642f9,3 +np.float64,0xbfe6ae753f6d5cea,0xbfe03f99666dbe17,3 +np.float64,0xbfd18a2a73a31454,0xbfceaee204aca016,3 +np.float64,0x3fb8a1dffc3143c0,0x3fb9db38341ab1a3,3 +np.float64,0x7fd2a0376025406e,0x7ff0000000000000,3 +np.float64,0x7fe718c0e3ae3181,0x7ff0000000000000,3 +np.float64,0x3fb264d42424c9a8,0x3fb3121f071d4db4,3 +np.float64,0xd27190a7a4e32,0xd27190a7a4e32,3 +np.float64,0xbfe467668c68cecd,0xbfde2c4616738d5e,3 +np.float64,0x800ab9a2b9357346,0x800ab9a2b9357346,3 +np.float64,0x7fcbd108d537a211,0x7ff0000000000000,3 +np.float64,0x3fb79bba6e2f3770,0x3fb8bb2c140d3445,3 +np.float64,0xffefa7165e3f4e2c,0xbff0000000000000,3 +np.float64,0x7fb40185a428030a,0x7ff0000000000000,3 +np.float64,0xbfe9e3d58e73c7ab,0xbfe1c04d51c83d69,3 +np.float64,0x7fef5b97b17eb72e,0x7ff0000000000000,3 +np.float64,0x800a2957683452af,0x800a2957683452af,3 +np.float64,0x800f54f1925ea9e3,0x800f54f1925ea9e3,3 +np.float64,0xeffa4e77dff4a,0xeffa4e77dff4a,3 +np.float64,0xffbe501aa03ca038,0xbff0000000000000,3 +np.float64,0x8006c651bced8ca4,0x8006c651bced8ca4,3 +np.float64,0x3fe159faff22b3f6,0x3fe708f78efbdbed,3 +np.float64,0x800e7d59a31cfab3,0x800e7d59a31cfab3,3 +np.float64,0x3fe6ac2f272d585e,0x3ff07ee5305385c3,3 +np.float64,0x7fd014c054202980,0x7ff0000000000000,3 +np.float64,0xbfe4800b11e90016,0xbfde4648c6f29ce5,3 +np.float64,0xbfe6738470ece709,0xbfe0227b5b42b713,3 +np.float64,0x3fed052add3a0a56,0x3ff7a01819e65c6e,3 +np.float64,0xffe03106f120620e,0xbff0000000000000,3 +np.float64,0x7fe11df4d4e23be9,0x7ff0000000000000,3 +np.float64,0xbfcea25d7b3d44bc,0xbfcb3e808e7ce852,3 +np.float64,0xd0807b03a1010,0xd0807b03a1010,3 +np.float64,0x8004eda4fec9db4b,0x8004eda4fec9db4b,3 +np.float64,0x3fceb5c98d3d6b90,0x3fd15a894b15dd9f,3 +np.float64,0xbfee27228afc4e45,0xbfe38741702f3c0b,3 +np.float64,0xbfe606278c6c0c4f,0xbfdfd7cb6093652d,3 +np.float64,0xbfd66f59bc2cdeb4,0xbfd2ecb2297f6afc,3 +np.float64,0x4aee390095dc8,0x4aee390095dc8,3 +np.float64,0xbfe391355d67226a,0xbfdd46ddc0997014,3 +np.float64,0xffd27765e7a4eecc,0xbff0000000000000,3 +np.float64,0xbfe795e20a2f2bc4,0xbfe0afebc66c4dbd,3 +np.float64,0x7fc9a62e81334c5c,0x7ff0000000000000,3 +np.float64,0xffe4e57e52a9cafc,0xbff0000000000000,3 +np.float64,0x7fac326c8c3864d8,0x7ff0000000000000,3 +np.float64,0x3fe8675f6370cebf,0x3ff24d5863029c15,3 +np.float64,0x7fcf4745e73e8e8b,0x7ff0000000000000,3 +np.float64,0x7fcc9aec9f3935d8,0x7ff0000000000000,3 +np.float64,0x3fec2e8fcab85d20,0x3ff699ccd0b2fed6,3 +np.float64,0x3fd110a968222153,0x3fd38e81a88c2d13,3 +np.float64,0xffb3a68532274d08,0xbff0000000000000,3 +np.float64,0xf0e562bbe1cad,0xf0e562bbe1cad,3 +np.float64,0xbfe815b9e5f02b74,0xbfe0ec9f5023aebc,3 +np.float64,0xbf5151d88022a400,0xbf514f80c465feea,3 +np.float64,0x2547e3144a8fd,0x2547e3144a8fd,3 +np.float64,0x3fedcc0c28fb9818,0x3ff899612fbeb4c5,3 +np.float64,0x3fdc3d1c0f387a38,0x3fe1bf6e2d39bd75,3 +np.float64,0x7fe544dbe62a89b7,0x7ff0000000000000,3 +np.float64,0x8001500e48e2a01d,0x8001500e48e2a01d,3 +np.float64,0xbfed3b2b09fa7656,0xbfe329f3e7bada64,3 +np.float64,0xbfe76a943aeed528,0xbfe09b24e3aa3f79,3 +np.float64,0x3fe944330e328866,0x3ff33d472dee70c5,3 +np.float64,0x8004bbbd6cc9777c,0x8004bbbd6cc9777c,3 +np.float64,0xbfe28133fb650268,0xbfdc1ac230ac4ef5,3 +np.float64,0xc1370af7826e2,0xc1370af7826e2,3 +np.float64,0x7fcfa47f5f3f48fe,0x7ff0000000000000,3 +np.float64,0xbfa3002a04260050,0xbfa2a703a538b54e,3 +np.float64,0xffef44f3903e89e6,0xbff0000000000000,3 +np.float64,0xc32cce298659a,0xc32cce298659a,3 +np.float64,0x7b477cc2f68f0,0x7b477cc2f68f0,3 +np.float64,0x40a7f4ec814ff,0x40a7f4ec814ff,3 +np.float64,0xffee38edf67c71db,0xbff0000000000000,3 +np.float64,0x3fe23f6f1ce47ede,0x3fe8992b8bb03499,3 +np.float64,0x7fc8edfe7f31dbfc,0x7ff0000000000000,3 +np.float64,0x800bb8e6fb3771ce,0x800bb8e6fb3771ce,3 +np.float64,0xbfe11d364ee23a6c,0xbfda82a0c2ef9e46,3 +np.float64,0xbfeb993cb4b7327a,0xbfe27df565da85dc,3 +np.float64,0x10000000000000,0x10000000000000,3 +np.float64,0x3fc1f997d723f330,0x3fc34c5cff060af1,3 +np.float64,0x6e326fa0dc64f,0x6e326fa0dc64f,3 +np.float64,0x800fa30c2c5f4618,0x800fa30c2c5f4618,3 +np.float64,0x7fed16ad603a2d5a,0x7ff0000000000000,3 +np.float64,0x9411cf172823a,0x9411cf172823a,3 +np.float64,0xffece51d4cb9ca3a,0xbff0000000000000,3 +np.float64,0x3fdda3d1453b47a3,0x3fe2d954f7849890,3 +np.float64,0xffd58330172b0660,0xbff0000000000000,3 +np.float64,0xbfc6962ae52d2c54,0xbfc4b4bdf0069f17,3 +np.float64,0xbfb4010a8e280218,0xbfb33e1236f7efa0,3 +np.float64,0x7fd0444909208891,0x7ff0000000000000,3 +np.float64,0xbfe027a24de04f44,0xbfd95e9064101e7c,3 +np.float64,0xa6f3f3214de9,0xa6f3f3214de9,3 +np.float64,0xbfe112eb0fe225d6,0xbfda768f7cbdf346,3 +np.float64,0xbfe99e90d4b33d22,0xbfe1a153e45a382a,3 +np.float64,0xffecb34f8e79669e,0xbff0000000000000,3 +np.float64,0xbfdf32c9653e6592,0xbfd8b159caf5633d,3 +np.float64,0x3fe9519829b2a330,0x3ff34c0a8152e20f,3 +np.float64,0xffd08ec8a7a11d92,0xbff0000000000000,3 +np.float64,0xffd19b71b6a336e4,0xbff0000000000000,3 +np.float64,0x7feda6b9377b4d71,0x7ff0000000000000,3 +np.float64,0x800fda2956bfb453,0x800fda2956bfb453,3 +np.float64,0x3fe54f601bea9ec0,0x3fee483cb03cbde4,3 +np.float64,0xbfe2a8ad5ee5515a,0xbfdc46ee7a10bf0d,3 +np.float64,0xbfd336c8bd266d92,0xbfd09916d432274a,3 +np.float64,0xfff0000000000000,0xbff0000000000000,3 +np.float64,0x3fd9a811a9b35024,0x3fdf8fa68cc048e3,3 +np.float64,0x3fe078c68520f18d,0x3fe58aecc1f9649b,3 +np.float64,0xbfc6d5aa3a2dab54,0xbfc4e9ea84f3d73c,3 +np.float64,0xf9682007f2d04,0xf9682007f2d04,3 +np.float64,0x3fee54523dbca8a4,0x3ff947b826de81f4,3 +np.float64,0x80461e5d008c4,0x80461e5d008c4,3 +np.float64,0x3fdd6d12d5bada26,0x3fe2ade8dee2fa02,3 +np.float64,0x3fcd5f0dfd3abe18,0x3fd081d6cd25731d,3 +np.float64,0x7fa36475c826c8eb,0x7ff0000000000000,3 +np.float64,0xbfdf3ce052be79c0,0xbfd8b78baccfb908,3 +np.float64,0x7fcd890dd13b121b,0x7ff0000000000000,3 +np.float64,0x8000000000000001,0x8000000000000001,3 +np.float64,0x800ec0f4281d81e8,0x800ec0f4281d81e8,3 +np.float64,0xbfba960116352c00,0xbfb94085424496d9,3 +np.float64,0x3fdddedc9bbbbdb8,0x3fe30853fe4ef5ce,3 +np.float64,0x238092a847013,0x238092a847013,3 +np.float64,0xbfe38d4803271a90,0xbfdd429a955c46af,3 +np.float64,0xbfd4c9067329920c,0xbfd1bf6255ed91a4,3 +np.float64,0xbfbee213923dc428,0xbfbd17ce1bda6088,3 +np.float64,0xffd5a2d337ab45a6,0xbff0000000000000,3 +np.float64,0x7fe21bfcf82437f9,0x7ff0000000000000,3 +np.float64,0x3fe2a2714da544e3,0x3fe949594a74ea25,3 +np.float64,0x800e05cf8ebc0b9f,0x800e05cf8ebc0b9f,3 +np.float64,0x559a1526ab343,0x559a1526ab343,3 +np.float64,0xffe6a1b7906d436e,0xbff0000000000000,3 +np.float64,0xffef27d6253e4fab,0xbff0000000000000,3 +np.float64,0xbfe0f90ab0a1f216,0xbfda5828a1edde48,3 +np.float64,0x9675d2ab2cebb,0x9675d2ab2cebb,3 +np.float64,0xffee0f7eecfc1efd,0xbff0000000000000,3 +np.float64,0x2ec005625d801,0x2ec005625d801,3 +np.float64,0x7fde35ff14bc6bfd,0x7ff0000000000000,3 +np.float64,0xffe03f36d9e07e6d,0xbff0000000000000,3 +np.float64,0x7fe09ff7c4213fef,0x7ff0000000000000,3 +np.float64,0xffeac29dd1b5853b,0xbff0000000000000,3 +np.float64,0x3fb63120aa2c6241,0x3fb72ea3de98a853,3 +np.float64,0xffd079eb84a0f3d8,0xbff0000000000000,3 +np.float64,0xbfd3c2cc75a78598,0xbfd1005996880b3f,3 +np.float64,0x7fb80507ee300a0f,0x7ff0000000000000,3 +np.float64,0xffe8006105f000c1,0xbff0000000000000,3 +np.float64,0x8009138b0ab22716,0x8009138b0ab22716,3 +np.float64,0xbfd6dfb40b2dbf68,0xbfd33b8e4008e3b0,3 +np.float64,0xbfe7c2cf9bef859f,0xbfe0c55c807460df,3 +np.float64,0xbfe75fe4da6ebfca,0xbfe09600256d3b81,3 +np.float64,0xffd662fc73acc5f8,0xbff0000000000000,3 +np.float64,0x20b99dbc41735,0x20b99dbc41735,3 +np.float64,0x3fe10b38ade21671,0x3fe68229a9bbeefc,3 +np.float64,0x3743b99c6e878,0x3743b99c6e878,3 +np.float64,0xff9eb5ed903d6be0,0xbff0000000000000,3 +np.float64,0x3ff0000000000000,0x3ffb7e151628aed3,3 +np.float64,0xffb9e0569e33c0b0,0xbff0000000000000,3 +np.float64,0x7fd39c804fa73900,0x7ff0000000000000,3 +np.float64,0x3fe881ef67f103df,0x3ff269dd704b7129,3 +np.float64,0x1b6eb40236dd7,0x1b6eb40236dd7,3 +np.float64,0xbfe734ea432e69d4,0xbfe0813e6355d02f,3 +np.float64,0xffcf48f3743e91e8,0xbff0000000000000,3 +np.float64,0xffed10bcf6fa2179,0xbff0000000000000,3 +np.float64,0x3fef07723b7e0ee4,0x3ffa3156123f3c15,3 +np.float64,0xffe45c704aa8b8e0,0xbff0000000000000,3 +np.float64,0xb7b818d96f703,0xb7b818d96f703,3 +np.float64,0x42fcc04085f99,0x42fcc04085f99,3 +np.float64,0xbfda7ced01b4f9da,0xbfd5b0ce1e5524ae,3 +np.float64,0xbfe1e5963d63cb2c,0xbfdb6a87b6c09185,3 +np.float64,0x7fdfa18003bf42ff,0x7ff0000000000000,3 +np.float64,0xbfe3790a43e6f214,0xbfdd2c9a38b4f089,3 +np.float64,0xffe0ff5b9ae1feb6,0xbff0000000000000,3 +np.float64,0x80085a7d3110b4fb,0x80085a7d3110b4fb,3 +np.float64,0xffd6bfa6622d7f4c,0xbff0000000000000,3 +np.float64,0xbfef5ddc7cfebbb9,0xbfe3fe170521593e,3 +np.float64,0x3fc21773fa242ee8,0x3fc36ebda1f91a72,3 +np.float64,0x7fc04d98da209b31,0x7ff0000000000000,3 +np.float64,0xbfeba3b535b7476a,0xbfe282602e3c322e,3 +np.float64,0xffd41fb5c1a83f6c,0xbff0000000000000,3 +np.float64,0xf87d206df0fa4,0xf87d206df0fa4,3 +np.float64,0x800060946fc0c12a,0x800060946fc0c12a,3 +np.float64,0x3fe69d5f166d3abe,0x3ff06fdddcf4ca93,3 +np.float64,0x7fe9b5793b336af1,0x7ff0000000000000,3 +np.float64,0x7fe0dd4143e1ba82,0x7ff0000000000000,3 +np.float64,0xbfa8eaea3c31d5d0,0xbfa8522e397da3bd,3 +np.float64,0x119f0078233e1,0x119f0078233e1,3 +np.float64,0xbfd78a207aaf1440,0xbfd3b225bbf2ab4f,3 +np.float64,0xc66a6d4d8cd4e,0xc66a6d4d8cd4e,3 +np.float64,0xe7fc4b57cff8a,0xe7fc4b57cff8a,3 +np.float64,0x800883e8091107d0,0x800883e8091107d0,3 +np.float64,0x3fa6520c842ca419,0x3fa6d06e1041743a,3 +np.float64,0x3fa563182c2ac630,0x3fa5d70e27a84c97,3 +np.float64,0xe6a30b61cd462,0xe6a30b61cd462,3 +np.float64,0x3fee85dac37d0bb6,0x3ff987cfa41a9778,3 +np.float64,0x3fe8f621db71ec44,0x3ff2e7b768a2e9d0,3 +np.float64,0x800f231d861e463b,0x800f231d861e463b,3 +np.float64,0xbfe22eb07c645d61,0xbfdbbdbb853ab4c6,3 +np.float64,0x7fd2dda2dea5bb45,0x7ff0000000000000,3 +np.float64,0xbfd09b79a0a136f4,0xbfcd4147606ffd27,3 +np.float64,0xca039cc394074,0xca039cc394074,3 +np.float64,0x8000000000000000,0x8000000000000000,3 +np.float64,0xcb34575d9668b,0xcb34575d9668b,3 +np.float64,0x3fea62c1f3f4c584,0x3ff47e6dc67ec89f,3 +np.float64,0x7fe544c8606a8990,0x7ff0000000000000,3 +np.float64,0xffe0a980c4615301,0xbff0000000000000,3 +np.float64,0x3fdd67d5f8bacfac,0x3fe2a9c3421830f1,3 +np.float64,0xffe41d3dda283a7b,0xbff0000000000000,3 +np.float64,0xffeed59e5ffdab3c,0xbff0000000000000,3 +np.float64,0xffeeae8326fd5d05,0xbff0000000000000,3 +np.float64,0x800d70b4fa7ae16a,0x800d70b4fa7ae16a,3 +np.float64,0xffec932e6839265c,0xbff0000000000000,3 +np.float64,0xee30b185dc616,0xee30b185dc616,3 +np.float64,0x7fc3cf4397279e86,0x7ff0000000000000,3 +np.float64,0xbfeab34f1875669e,0xbfe21b868229de7d,3 +np.float64,0xf45f5f7de8bec,0xf45f5f7de8bec,3 +np.float64,0x3fad2c4b203a5896,0x3fae0528b568f3cf,3 +np.float64,0xbfe2479543e48f2a,0xbfdbd9e57cf64028,3 +np.float64,0x3fd41a1473283429,0x3fd79df2bc60debb,3 +np.float64,0x3febb5155ef76a2a,0x3ff608585afd698b,3 +np.float64,0xffe21f5303e43ea6,0xbff0000000000000,3 +np.float64,0x7fe9ef390833de71,0x7ff0000000000000,3 +np.float64,0xffe8ee873d71dd0e,0xbff0000000000000,3 +np.float64,0x7fd7cbc55e2f978a,0x7ff0000000000000,3 +np.float64,0x80081f9080d03f21,0x80081f9080d03f21,3 +np.float64,0x7fecbafc8b3975f8,0x7ff0000000000000,3 +np.float64,0x800b6c4b0b16d896,0x800b6c4b0b16d896,3 +np.float64,0xbfaa0fc2d4341f80,0xbfa968cdf32b98ad,3 +np.float64,0x3fec79fe4078f3fc,0x3ff6f5361a4a5d93,3 +np.float64,0xbfb14b79de2296f0,0xbfb0b93b75ecec11,3 +np.float64,0x800009d084c013a2,0x800009d084c013a2,3 +np.float64,0x4a4cdfe29499d,0x4a4cdfe29499d,3 +np.float64,0xbfe721c2d56e4386,0xbfe077f541987d76,3 +np.float64,0x3e5f539e7cbeb,0x3e5f539e7cbeb,3 +np.float64,0x3fd23f044c247e09,0x3fd51ceafcdd64aa,3 +np.float64,0x3fc70785b02e0f0b,0x3fc93b2a37eb342a,3 +np.float64,0xbfe7ab4ec7af569e,0xbfe0ba28eecbf6b0,3 +np.float64,0x800c1d4134583a83,0x800c1d4134583a83,3 +np.float64,0xffd9a73070334e60,0xbff0000000000000,3 +np.float64,0x68a4bf24d1499,0x68a4bf24d1499,3 +np.float64,0x7feba9d9507753b2,0x7ff0000000000000,3 +np.float64,0xbfe9d747db73ae90,0xbfe1bab53d932010,3 +np.float64,0x800a9a4aed953496,0x800a9a4aed953496,3 +np.float64,0xffcb89b0ad371360,0xbff0000000000000,3 +np.float64,0xbfc62388b82c4710,0xbfc4547be442a38c,3 +np.float64,0x800a006d187400db,0x800a006d187400db,3 +np.float64,0x3fcef2fbd33de5f8,0x3fd18177b2150148,3 +np.float64,0x8000b74e3da16e9d,0x8000b74e3da16e9d,3 +np.float64,0x25be536e4b7cb,0x25be536e4b7cb,3 +np.float64,0x3fa86e189430dc31,0x3fa905b4684c9f01,3 +np.float64,0xa7584b114eb0a,0xa7584b114eb0a,3 +np.float64,0x800331133c866227,0x800331133c866227,3 +np.float64,0x3fb52b48142a5690,0x3fb611a6f6e7c664,3 +np.float64,0x3fe825797cf04af2,0x3ff206fd60e98116,3 +np.float64,0x3fd0bec4e5217d8a,0x3fd323db3ffd59b2,3 +np.float64,0x907b43a120f7,0x907b43a120f7,3 +np.float64,0x3fed31eb1d3a63d6,0x3ff7d7a91c6930a4,3 +np.float64,0x7f97a13d782f427a,0x7ff0000000000000,3 +np.float64,0xffc7121a702e2434,0xbff0000000000000,3 +np.float64,0xbfe8bb4cbbf1769a,0xbfe139d7f46f1fb1,3 +np.float64,0xbfe3593cc5a6b27a,0xbfdd09ec91d6cd48,3 +np.float64,0x7fcff218ff9ff,0x7fcff218ff9ff,3 +np.float64,0x3fe73651d4ae6ca4,0x3ff10c5c1d21d127,3 +np.float64,0x80054e396eaa9c74,0x80054e396eaa9c74,3 +np.float64,0x3fe527d5f9aa4fac,0x3fedfb7743db9b53,3 +np.float64,0x7fec6f28c5f8de51,0x7ff0000000000000,3 +np.float64,0x3fcd2bbff53a5780,0x3fd061987416b49b,3 +np.float64,0xffd1f0046423e008,0xbff0000000000000,3 +np.float64,0x80034d97fac69b31,0x80034d97fac69b31,3 +np.float64,0x3faa803f14350080,0x3fab32e3f8073be4,3 +np.float64,0x3fcf8da0163f1b40,0x3fd1e42ba2354c8e,3 +np.float64,0x3fd573c2632ae785,0x3fd97c37609d18d7,3 +np.float64,0x7f922960482452c0,0x7ff0000000000000,3 +np.float64,0x800ebd0c5d3d7a19,0x800ebd0c5d3d7a19,3 +np.float64,0xbfee63b7807cc76f,0xbfe39ec7981035db,3 +np.float64,0xffdc023f8e380480,0xbff0000000000000,3 +np.float64,0x3fe3ffa02c67ff40,0x3febc7f8b900ceba,3 +np.float64,0x36c508b86d8a2,0x36c508b86d8a2,3 +np.float64,0x3fc9fbb0f133f760,0x3fcccee9f6ba801c,3 +np.float64,0x3fd75c1d5faeb83b,0x3fdc3150f9eff99e,3 +np.float64,0x3fe9a8d907b351b2,0x3ff3accc78a31df8,3 +np.float64,0x3fdd8fdcafbb1fb8,0x3fe2c97c97757994,3 +np.float64,0x3fb10c34ca22186a,0x3fb1a0cc42c76b86,3 +np.float64,0xbff0000000000000,0xbfe43a54e4e98864,3 +np.float64,0xffd046aefda08d5e,0xbff0000000000000,3 +np.float64,0x80067989758cf314,0x80067989758cf314,3 +np.float64,0x3fee9d77763d3aef,0x3ff9a67ff0841ba5,3 +np.float64,0xffe4d3cbf8e9a798,0xbff0000000000000,3 +np.float64,0x800f9cab273f3956,0x800f9cab273f3956,3 +np.float64,0x800a5c84f9f4b90a,0x800a5c84f9f4b90a,3 +np.float64,0x4fd377009fa8,0x4fd377009fa8,3 +np.float64,0xbfe7ba26af6f744e,0xbfe0c13ce45d6f95,3 +np.float64,0x609c8a86c1392,0x609c8a86c1392,3 +np.float64,0x7fe4d0296ea9a052,0x7ff0000000000000,3 +np.float64,0x59847bccb3090,0x59847bccb3090,3 +np.float64,0xbfdf944157bf2882,0xbfd8ed092bacad43,3 +np.float64,0xbfe7560a632eac15,0xbfe091405ec34973,3 +np.float64,0x3fea0699f4340d34,0x3ff415eb72089230,3 +np.float64,0x800a5533f374aa68,0x800a5533f374aa68,3 +np.float64,0xbf8e8cdb103d19c0,0xbf8e52cffcb83774,3 +np.float64,0x3fe87d9e52f0fb3d,0x3ff2653952344b81,3 +np.float64,0x7fca3950f73472a1,0x7ff0000000000000,3 +np.float64,0xffd5d1068aaba20e,0xbff0000000000000,3 +np.float64,0x3fd1a5f169a34be4,0x3fd4524b6ef17f91,3 +np.float64,0x3fdc4b95a8b8972c,0x3fe1caafd8652bf7,3 +np.float64,0x3fe333f65a6667ed,0x3fea502fb1f8a578,3 +np.float64,0xbfc117aaac222f54,0xbfc00018a4b84b6e,3 +np.float64,0x7fecf2efdf39e5df,0x7ff0000000000000,3 +np.float64,0x4e99d83e9d33c,0x4e99d83e9d33c,3 +np.float64,0x800d18937bda3127,0x800d18937bda3127,3 +np.float64,0x3fd6c67778ad8cef,0x3fdb5aba70a3ea9e,3 +np.float64,0x3fdbb71770b76e2f,0x3fe157ae8da20bc5,3 +np.float64,0xbfe9faf6ebf3f5ee,0xbfe1ca963d83f17f,3 +np.float64,0x80038850ac0710a2,0x80038850ac0710a2,3 +np.float64,0x8006beb72f8d7d6f,0x8006beb72f8d7d6f,3 +np.float64,0x3feead67bffd5acf,0x3ff9bb43e8b15e2f,3 +np.float64,0xbfd1174b89222e98,0xbfcdff9972799907,3 +np.float64,0x7fee2c077cfc580e,0x7ff0000000000000,3 +np.float64,0xbfbdbd904e3b7b20,0xbfbc13f4916ed466,3 +np.float64,0xffee47b8fe3c8f71,0xbff0000000000000,3 +np.float64,0xffd161884222c310,0xbff0000000000000,3 +np.float64,0xbfd42f27c4a85e50,0xbfd14fa8d67ba5ee,3 +np.float64,0x7fefffffffffffff,0x7ff0000000000000,3 +np.float64,0x8008151791b02a30,0x8008151791b02a30,3 +np.float64,0xbfba79029234f208,0xbfb926616cf41755,3 +np.float64,0x8004c486be29890e,0x8004c486be29890e,3 +np.float64,0x7fe5325a252a64b3,0x7ff0000000000000,3 +np.float64,0x5a880f04b5103,0x5a880f04b5103,3 +np.float64,0xbfe6f4b7702de96f,0xbfe06209002dd72c,3 +np.float64,0xbfdf8b3739bf166e,0xbfd8e783efe3c30f,3 +np.float64,0xbfe32571c8e64ae4,0xbfdcd128b9aa49a1,3 +np.float64,0xbfe97c98c172f932,0xbfe1920ac0fc040f,3 +np.float64,0x3fd0b513a2a16a28,0x3fd31744e3a1bf0a,3 +np.float64,0xffe3ab70832756e0,0xbff0000000000000,3 +np.float64,0x80030f055ce61e0b,0x80030f055ce61e0b,3 +np.float64,0xffd5f3b21b2be764,0xbff0000000000000,3 +np.float64,0x800c1f2d6c783e5b,0x800c1f2d6c783e5b,3 +np.float64,0x80075f4f148ebe9f,0x80075f4f148ebe9f,3 +np.float64,0xbfa5a046f42b4090,0xbfa52cfbf8992256,3 +np.float64,0xffd6702583ace04c,0xbff0000000000000,3 +np.float64,0x800dc0a5cf1b814c,0x800dc0a5cf1b814c,3 +np.float64,0x14f2203a29e45,0x14f2203a29e45,3 +np.float64,0x800421a40ee84349,0x800421a40ee84349,3 +np.float64,0xbfea7c279df4f84f,0xbfe2037fff3ed877,3 +np.float64,0xbfe9b41ddcf3683c,0xbfe1aafe18a44bf8,3 +np.float64,0xffe7b037022f606e,0xbff0000000000000,3 +np.float64,0x800bafb648775f6d,0x800bafb648775f6d,3 +np.float64,0x800b81681d5702d1,0x800b81681d5702d1,3 +np.float64,0x3fe29f8dc8653f1c,0x3fe9442da1c32c6b,3 +np.float64,0xffef9a05dc7f340b,0xbff0000000000000,3 +np.float64,0x800c8c65a65918cb,0x800c8c65a65918cb,3 +np.float64,0xffe99df0d5f33be1,0xbff0000000000000,3 +np.float64,0x9afeb22535fd7,0x9afeb22535fd7,3 +np.float64,0x7fc620dd822c41ba,0x7ff0000000000000,3 +np.float64,0x29c2cdf25385b,0x29c2cdf25385b,3 +np.float64,0x2d92284e5b246,0x2d92284e5b246,3 +np.float64,0xffc794aa942f2954,0xbff0000000000000,3 +np.float64,0xbfe7ed907eafdb21,0xbfe0d9a7b1442497,3 +np.float64,0xbfd4e0d4aea9c1aa,0xbfd1d09366dba2a7,3 +np.float64,0xa70412c34e083,0xa70412c34e083,3 +np.float64,0x41dc0ee083b9,0x41dc0ee083b9,3 +np.float64,0x8000ece20da1d9c5,0x8000ece20da1d9c5,3 +np.float64,0x3fdf3dae103e7b5c,0x3fe42314bf826bc5,3 +np.float64,0x3fe972533c72e4a6,0x3ff3703761e70f04,3 +np.float64,0xffba1d2b82343a58,0xbff0000000000000,3 +np.float64,0xe0086c83c010e,0xe0086c83c010e,3 +np.float64,0x3fe6fb0dde6df61c,0x3ff0cf5fae01aa08,3 +np.float64,0x3fcfaf057e3f5e0b,0x3fd1f98c1fd20139,3 +np.float64,0xbfdca19d9239433c,0xbfd7158745192ca9,3 +np.float64,0xffb17f394e22fe70,0xbff0000000000000,3 +np.float64,0x7fe40f05c7681e0b,0x7ff0000000000000,3 +np.float64,0x800b3c575d5678af,0x800b3c575d5678af,3 +np.float64,0x7fa4ab20ac295640,0x7ff0000000000000,3 +np.float64,0xbfd2fff4f6a5ffea,0xbfd07069bb50e1a6,3 +np.float64,0xbfef81b9147f0372,0xbfe40b845a749787,3 +np.float64,0x7fd7400e54ae801c,0x7ff0000000000000,3 +np.float64,0x3fd4401a17a88034,0x3fd7d20fb76a4f3d,3 +np.float64,0xbfd3e907fd27d210,0xbfd11c64b7577fc5,3 +np.float64,0x7fe34bed9ae697da,0x7ff0000000000000,3 +np.float64,0x80039119c0472234,0x80039119c0472234,3 +np.float64,0xbfe2e36ac565c6d6,0xbfdc88454ee997b3,3 +np.float64,0xbfec57204478ae40,0xbfe2cd3183de1d2d,3 +np.float64,0x7fed7e2a12fafc53,0x7ff0000000000000,3 +np.float64,0x7fd5c5fa7d2b8bf4,0x7ff0000000000000,3 +np.float64,0x3fdcf368d6b9e6d0,0x3fe24decce1ebd35,3 +np.float64,0xbfe0ebfcf2e1d7fa,0xbfda48c9247ae8cf,3 +np.float64,0xbfe10dbea2e21b7e,0xbfda707d68b59674,3 +np.float64,0xbfdf201b6ebe4036,0xbfd8a5df27742fdf,3 +np.float64,0xffe16555be62caab,0xbff0000000000000,3 +np.float64,0xffc23a5db22474bc,0xbff0000000000000,3 +np.float64,0xffe1cbb3f8a39768,0xbff0000000000000,3 +np.float64,0x8007b823be0f7048,0x8007b823be0f7048,3 +np.float64,0xbfa5d1f3042ba3e0,0xbfa55c97cd77bf6e,3 +np.float64,0xbfe316a074662d41,0xbfdcc0da4e7334d0,3 +np.float64,0xbfdfab2bf2bf5658,0xbfd8fb046b88b51f,3 +np.float64,0xfacc9dabf5994,0xfacc9dabf5994,3 +np.float64,0xffe7e420a4efc841,0xbff0000000000000,3 +np.float64,0x800bb986cd57730e,0x800bb986cd57730e,3 +np.float64,0xbfe314fa38e629f4,0xbfdcbf09302c3bf5,3 +np.float64,0x7fc56b17772ad62e,0x7ff0000000000000,3 +np.float64,0x8006a87d54ad50fb,0x8006a87d54ad50fb,3 +np.float64,0xbfe6633e4a6cc67c,0xbfe01a67c3b3ff32,3 +np.float64,0x3fe0ff56eb21feae,0x3fe66df01defb0fb,3 +np.float64,0xffc369cfc126d3a0,0xbff0000000000000,3 +np.float64,0x7fe8775d9a30eeba,0x7ff0000000000000,3 +np.float64,0x3fb53db13e2a7b60,0x3fb625a7279cdac3,3 +np.float64,0xffee76e7e6fcedcf,0xbff0000000000000,3 +np.float64,0xb45595b568ab3,0xb45595b568ab3,3 +np.float64,0xffa09a1d50213440,0xbff0000000000000,3 +np.float64,0x7d11dc16fa23c,0x7d11dc16fa23c,3 +np.float64,0x7fd4cc2928299851,0x7ff0000000000000,3 +np.float64,0x6a30e0ead461d,0x6a30e0ead461d,3 +np.float64,0x7fd3ee735a27dce6,0x7ff0000000000000,3 +np.float64,0x8008d7084b31ae11,0x8008d7084b31ae11,3 +np.float64,0x3fe469353fe8d26a,0x3fec8e7e2df38590,3 +np.float64,0x3fcecef2743d9de5,0x3fd16a888b715dfd,3 +np.float64,0x460130d68c027,0x460130d68c027,3 +np.float64,0xbfd76510c62eca22,0xbfd398766b741d6e,3 +np.float64,0x800ec88c2a5d9118,0x800ec88c2a5d9118,3 +np.float64,0x3fac969c6c392d40,0x3fad66ca6a1e583c,3 +np.float64,0x3fe5c616bf6b8c2e,0x3fef30f931e8dde5,3 +np.float64,0xb4cb6cd56996e,0xb4cb6cd56996e,3 +np.float64,0xffc3eacf8827d5a0,0xbff0000000000000,3 +np.float64,0x3fe1ceaf60e39d5f,0x3fe7d31e0a627cf9,3 +np.float64,0xffea69b42ff4d368,0xbff0000000000000,3 +np.float64,0x800ff8aef99ff15e,0x800ff8aef99ff15e,3 +np.float64,0x6c3953f0d872b,0x6c3953f0d872b,3 +np.float64,0x8007ca5a0d0f94b5,0x8007ca5a0d0f94b5,3 +np.float64,0x800993ce3ad3279d,0x800993ce3ad3279d,3 +np.float64,0x3fe5a4d1516b49a2,0x3feeef67b22ac65b,3 +np.float64,0x8003d7512a67aea3,0x8003d7512a67aea3,3 +np.float64,0x33864430670c9,0x33864430670c9,3 +np.float64,0xbfdbf477e3b7e8f0,0xbfd6a63f1b36f424,3 +np.float64,0x3fb5da92582bb525,0x3fb6d04ef1a1d31a,3 +np.float64,0xe38aae71c7156,0xe38aae71c7156,3 +np.float64,0x3fcaf5590a35eab2,0x3fce01ed6eb6188e,3 +np.float64,0x800deba9b05bd754,0x800deba9b05bd754,3 +np.float64,0x7fee0cde287c19bb,0x7ff0000000000000,3 +np.float64,0xbfe0c2ae70e1855d,0xbfda17fa64d84fcf,3 +np.float64,0x518618faa30c4,0x518618faa30c4,3 +np.float64,0xbfeb4c49b8769894,0xbfe25d52cd7e529f,3 +np.float64,0xbfeb3aa21b367544,0xbfe255cae1df4cfd,3 +np.float64,0xffd23f1c5d247e38,0xbff0000000000000,3 +np.float64,0xff9a75132034ea20,0xbff0000000000000,3 +np.float64,0xbfef9d96307f3b2c,0xbfe415e8b6ce0e50,3 +np.float64,0x8004046f2f0808df,0x8004046f2f0808df,3 +np.float64,0x3fe15871aea2b0e3,0x3fe706532ea5c770,3 +np.float64,0x7fd86b1576b0d62a,0x7ff0000000000000,3 +np.float64,0xbfc240a5c724814c,0xbfc102c7971ca455,3 +np.float64,0xffd8ea670bb1d4ce,0xbff0000000000000,3 +np.float64,0xbfeb1ddd1ff63bba,0xbfe2497c4e27bb8e,3 +np.float64,0x3fcd47e0a33a8fc1,0x3fd0734444150d83,3 +np.float64,0xe00b6a65c016e,0xe00b6a65c016e,3 +np.float64,0xbfc7d582142fab04,0xbfc5bf1fbe755a4c,3 +np.float64,0x8cc91ca11993,0x8cc91ca11993,3 +np.float64,0x7fdbc530e3b78a61,0x7ff0000000000000,3 +np.float64,0x7fee437522bc86e9,0x7ff0000000000000,3 +np.float64,0xffe9e09ae2b3c135,0xbff0000000000000,3 +np.float64,0x8002841cada5083a,0x8002841cada5083a,3 +np.float64,0x3fd6b485f8ad690c,0x3fdb412135932699,3 +np.float64,0x80070e8d0b0e1d1b,0x80070e8d0b0e1d1b,3 +np.float64,0x7fed5df165babbe2,0x7ff0000000000000,3 +np.float64,0x7ff4000000000000,0x7ffc000000000000,3 +np.float64,0x7fe99d08cd333a11,0x7ff0000000000000,3 +np.float64,0xdfff4201bfff,0xdfff4201bfff,3 +np.float64,0x800ccf7aaf999ef6,0x800ccf7aaf999ef6,3 +np.float64,0x3fddb05aad3b60b5,0x3fe2e34bdd1dd9d5,3 +np.float64,0xbfe5e1c60e6bc38c,0xbfdfb3275cc1675f,3 +np.float64,0x8004fe674269fccf,0x8004fe674269fccf,3 +np.float64,0x7fe9280363325006,0x7ff0000000000000,3 +np.float64,0xf605b9f1ec0b7,0xf605b9f1ec0b7,3 +np.float64,0x800c7c214018f843,0x800c7c214018f843,3 +np.float64,0x7fd97eb6b9b2fd6c,0x7ff0000000000000,3 +np.float64,0x7fd03f8fb6207f1e,0x7ff0000000000000,3 +np.float64,0x7fc526b64d2a4d6c,0x7ff0000000000000,3 +np.float64,0xbfef1a7c42fe34f9,0xbfe3e4b4399e0fcf,3 +np.float64,0xffdde10a2fbbc214,0xbff0000000000000,3 +np.float64,0xbfdd274f72ba4e9e,0xbfd76aa73788863c,3 +np.float64,0xbfecf7f77af9efef,0xbfe30ee2ae03fed1,3 +np.float64,0xffde709322bce126,0xbff0000000000000,3 +np.float64,0x268b5dac4d16d,0x268b5dac4d16d,3 +np.float64,0x8005c099606b8134,0x8005c099606b8134,3 +np.float64,0xffcf54c1593ea984,0xbff0000000000000,3 +np.float64,0xbfee9b8ebabd371d,0xbfe3b44f2663139d,3 +np.float64,0x3faf0330643e0661,0x3faff88fab74b447,3 +np.float64,0x7fe1c6011be38c01,0x7ff0000000000000,3 +np.float64,0xbfe9d58053b3ab01,0xbfe1b9ea12242485,3 +np.float64,0xbfe15a80fee2b502,0xbfdaca2aa7d1231a,3 +np.float64,0x7fe0d766d8a1aecd,0x7ff0000000000000,3 +np.float64,0x800f65e6a21ecbcd,0x800f65e6a21ecbcd,3 +np.float64,0x7fc85e45a530bc8a,0x7ff0000000000000,3 +np.float64,0x3fcc240e5438481d,0x3fcf7954fc080ac3,3 +np.float64,0xffddd49da2bba93c,0xbff0000000000000,3 +np.float64,0x1376f36c26edf,0x1376f36c26edf,3 +np.float64,0x3feffb7af17ff6f6,0x3ffb77f0ead2f881,3 +np.float64,0x3fd9354ea9b26a9d,0x3fdee4e4c8db8239,3 +np.float64,0xffdf7beed4bef7de,0xbff0000000000000,3 +np.float64,0xbfdef256ecbde4ae,0xbfd889b0e213a019,3 +np.float64,0x800d78bd1e7af17a,0x800d78bd1e7af17a,3 +np.float64,0xb66d66276cdad,0xb66d66276cdad,3 +np.float64,0x7fd8f51138b1ea21,0x7ff0000000000000,3 +np.float64,0xffe8c9c302b19385,0xbff0000000000000,3 +np.float64,0x8000be4cf5417c9b,0x8000be4cf5417c9b,3 +np.float64,0xbfe2293a25645274,0xbfdbb78a8c547c68,3 +np.float64,0xce8392c19d08,0xce8392c19d08,3 +np.float64,0xbfe075736b60eae7,0xbfd9bc0f6e34a283,3 +np.float64,0xbfe8d6fe6a71adfd,0xbfe1469ba80b4915,3 +np.float64,0xffe0c7993fa18f32,0xbff0000000000000,3 +np.float64,0x3fce5210fd3ca422,0x3fd11b40a1270a95,3 +np.float64,0x6c0534a8d80a7,0x6c0534a8d80a7,3 +np.float64,0x23c1823647831,0x23c1823647831,3 +np.float64,0x3fc901253732024a,0x3fcb9d264accb07c,3 +np.float64,0x3fe42b8997685714,0x3fec1a39e207b6e4,3 +np.float64,0x3fec4fd00fb89fa0,0x3ff6c1fdd0c262c8,3 +np.float64,0x8007b333caaf6668,0x8007b333caaf6668,3 +np.float64,0x800f9275141f24ea,0x800f9275141f24ea,3 +np.float64,0xffbba361a23746c0,0xbff0000000000000,3 +np.float64,0xbfee4effa9fc9dff,0xbfe396c11d0cd524,3 +np.float64,0x3e47e84c7c8fe,0x3e47e84c7c8fe,3 +np.float64,0x3fe80eb7b1301d6f,0x3ff1eed318a00153,3 +np.float64,0x7fd3f4c5b4a7e98a,0x7ff0000000000000,3 +np.float64,0x158abab02b158,0x158abab02b158,3 +np.float64,0x1,0x1,3 +np.float64,0x1f1797883e2f4,0x1f1797883e2f4,3 +np.float64,0x3feec055d03d80ac,0x3ff9d3fb0394de33,3 +np.float64,0x8010000000000000,0x8010000000000000,3 +np.float64,0xbfd070860ea0e10c,0xbfccfeec2828efef,3 +np.float64,0x80015c8b3e82b917,0x80015c8b3e82b917,3 +np.float64,0xffef9956d9ff32ad,0xbff0000000000000,3 +np.float64,0x7fe7f087dd2fe10f,0x7ff0000000000000,3 +np.float64,0x8002e7718665cee4,0x8002e7718665cee4,3 +np.float64,0x3fdfb9adb2bf735c,0x3fe4887a86214c1e,3 +np.float64,0xffc7747dfb2ee8fc,0xbff0000000000000,3 +np.float64,0x3fec309bb5386137,0x3ff69c44e1738547,3 +np.float64,0xffdbe2bf9ab7c580,0xbff0000000000000,3 +np.float64,0xbfe6a274daed44ea,0xbfe039aff2be9d48,3 +np.float64,0x7fd5a4e4efab49c9,0x7ff0000000000000,3 +np.float64,0xffbe6aaeb03cd560,0xbff0000000000000,3 diff --git a/numpy/core/tests/data/umath-validation-set-log.csv b/numpy/core/tests/data/umath-validation-set-log.csv new file mode 100644 index 000000000000..b8f6b08757d5 --- /dev/null +++ b/numpy/core/tests/data/umath-validation-set-log.csv @@ -0,0 +1,271 @@ +dtype,input,output,ulperrortol +## +ve denormals ## +np.float32,0x004b4716,0xc2afbc1b,4 +np.float32,0x007b2490,0xc2aec01e,4 +np.float32,0x007c99fa,0xc2aeba17,4 +np.float32,0x00734a0c,0xc2aee1dc,4 +np.float32,0x0070de24,0xc2aeecba,4 +np.float32,0x007fffff,0xc2aeac50,4 +np.float32,0x00000001,0xc2ce8ed0,4 +## -ve denormals ## +np.float32,0x80495d65,0xffc00000,4 +np.float32,0x806894f6,0xffc00000,4 +np.float32,0x80555a76,0xffc00000,4 +np.float32,0x804e1fb8,0xffc00000,4 +np.float32,0x80687de9,0xffc00000,4 +np.float32,0x807fffff,0xffc00000,4 +np.float32,0x80000001,0xffc00000,4 +## +/-0.0f, +/-FLT_MIN +/-FLT_MAX ## +np.float32,0x00000000,0xff800000,4 +np.float32,0x80000000,0xff800000,4 +np.float32,0x7f7fffff,0x42b17218,4 +np.float32,0x80800000,0xffc00000,4 +np.float32,0xff7fffff,0xffc00000,4 +## 1.00f + 0x00000001 ## +np.float32,0x3f800000,0x00000000,4 +np.float32,0x3f800001,0x33ffffff,4 +np.float32,0x3f800002,0x347ffffe,4 +np.float32,0x3f7fffff,0xb3800000,4 +np.float32,0x3f7ffffe,0xb4000000,4 +np.float32,0x3f7ffffd,0xb4400001,4 +np.float32,0x402df853,0x3f7ffffe,4 +np.float32,0x402df854,0x3f7fffff,4 +np.float32,0x402df855,0x3f800000,4 +np.float32,0x402df856,0x3f800001,4 +np.float32,0x3ebc5ab0,0xbf800001,4 +np.float32,0x3ebc5ab1,0xbf800000,4 +np.float32,0x3ebc5ab2,0xbf800000,4 +np.float32,0x3ebc5ab3,0xbf7ffffe,4 +np.float32,0x423ef575,0x407768ab,4 +np.float32,0x427b8c61,0x408485dd,4 +np.float32,0x4211e9ee,0x406630b0,4 +np.float32,0x424d5c41,0x407c0fed,4 +np.float32,0x42be722a,0x4091cc91,4 +np.float32,0x42b73d30,0x4090908b,4 +np.float32,0x427e48e2,0x4084de7f,4 +np.float32,0x428f759b,0x4088bba3,4 +np.float32,0x41629069,0x4029a0cc,4 +np.float32,0x4272c99d,0x40836379,4 +np.float32,0x4d1b7458,0x4197463d,4 +np.float32,0x4f10c594,0x41ace2b2,4 +np.float32,0x4ea397c2,0x41a85171,4 +np.float32,0x4fefa9d1,0x41b6769c,4 +np.float32,0x4ebac6ab,0x41a960dc,4 +np.float32,0x4f6efb42,0x41b0e535,4 +np.float32,0x4e9ab8e7,0x41a7df44,4 +np.float32,0x4e81b5d1,0x41a67625,4 +np.float32,0x5014d9f2,0x41b832bd,4 +np.float32,0x4f02175c,0x41ac07b8,4 +np.float32,0x7f034f89,0x42b01c47,4 +np.float32,0x7f56d00e,0x42b11849,4 +np.float32,0x7f1cd5f6,0x42b0773a,4 +np.float32,0x7e979174,0x42af02d7,4 +np.float32,0x7f23369f,0x42b08ba2,4 +np.float32,0x7f0637ae,0x42b0277d,4 +np.float32,0x7efcb6e8,0x42b00897,4 +np.float32,0x7f7907c8,0x42b163f6,4 +np.float32,0x7e95c4c2,0x42aefcba,4 +np.float32,0x7f4577b2,0x42b0ed2d,4 +np.float32,0x3f49c92e,0xbe73ae84,4 +np.float32,0x3f4a23d1,0xbe71e2f8,4 +np.float32,0x3f4abb67,0xbe6ee430,4 +np.float32,0x3f48169a,0xbe7c5532,4 +np.float32,0x3f47f5fa,0xbe7cfc37,4 +np.float32,0x3f488309,0xbe7a2ad8,4 +np.float32,0x3f479df4,0xbe7ebf5f,4 +np.float32,0x3f47cfff,0xbe7dbec9,4 +np.float32,0x3f496704,0xbe75a125,4 +np.float32,0x3f478ee8,0xbe7f0c92,4 +np.float32,0x3f4a763b,0xbe7041ce,4 +np.float32,0x3f47a108,0xbe7eaf94,4 +np.float32,0x3f48136c,0xbe7c6578,4 +np.float32,0x3f481c17,0xbe7c391c,4 +np.float32,0x3f47cd28,0xbe7dcd56,4 +np.float32,0x3f478be8,0xbe7f1bf7,4 +np.float32,0x3f4c1f8e,0xbe67e367,4 +np.float32,0x3f489b0c,0xbe79b03f,4 +np.float32,0x3f4934cf,0xbe76a08a,4 +np.float32,0x3f4954df,0xbe75fd6a,4 +np.float32,0x3f47a3f5,0xbe7ea093,4 +np.float32,0x3f4ba4fc,0xbe6a4b02,4 +np.float32,0x3f47a0e1,0xbe7eb05c,4 +np.float32,0x3f48c30a,0xbe78e42f,4 +np.float32,0x3f48cab8,0xbe78bd05,4 +np.float32,0x3f4b0569,0xbe6d6ea4,4 +np.float32,0x3f47de32,0xbe7d7607,4 +np.float32,0x3f477328,0xbe7f9b00,4 +np.float32,0x3f496dab,0xbe757f52,4 +np.float32,0x3f47662c,0xbe7fddac,4 +np.float32,0x3f48ddd8,0xbe785b80,4 +np.float32,0x3f481866,0xbe7c4bff,4 +np.float32,0x3f48b119,0xbe793fb6,4 +np.float32,0x3f48c7e8,0xbe78cb5c,4 +np.float32,0x3f4985f6,0xbe7503da,4 +np.float32,0x3f483fdf,0xbe7b8212,4 +np.float32,0x3f4b1c76,0xbe6cfa67,4 +np.float32,0x3f480b2e,0xbe7c8fa8,4 +np.float32,0x3f48745f,0xbe7a75bf,4 +np.float32,0x3f485bda,0xbe7af308,4 +np.float32,0x3f47a660,0xbe7e942c,4 +np.float32,0x3f47d4d5,0xbe7da600,4 +np.float32,0x3f4b0a26,0xbe6d56be,4 +np.float32,0x3f4a4883,0xbe712924,4 +np.float32,0x3f4769e7,0xbe7fca84,4 +np.float32,0x3f499702,0xbe74ad3f,4 +np.float32,0x3f494ab1,0xbe763131,4 +np.float32,0x3f476b69,0xbe7fc2c6,4 +np.float32,0x3f4884e8,0xbe7a214a,4 +np.float32,0x3f486945,0xbe7aae76,4 +#float64 +## +ve denormal ## +np.float64,0x0000000000000001,0xc0874385446d71c3,1 +np.float64,0x0001000000000000,0xc086395a2079b70c,1 +np.float64,0x000fffffffffffff,0xc086232bdd7abcd2,1 +np.float64,0x0007ad63e2168cb6,0xc086290bc0b2980f,1 +## -ve denormal ## +np.float64,0x8000000000000001,0xfff8000000000001,1 +np.float64,0x8001000000000000,0xfff8000000000001,1 +np.float64,0x800fffffffffffff,0xfff8000000000001,1 +np.float64,0x8007ad63e2168cb6,0xfff8000000000001,1 +## +/-0.0f, MAX, MIN## +np.float64,0x0000000000000000,0xfff0000000000000,1 +np.float64,0x8000000000000000,0xfff0000000000000,1 +np.float64,0x7fefffffffffffff,0x40862e42fefa39ef,1 +np.float64,0xffefffffffffffff,0xfff8000000000001,1 +## near 1.0f ## +np.float64,0x3ff0000000000000,0x0000000000000000,1 +np.float64,0x3fe8000000000000,0xbfd269621134db92,1 +np.float64,0x3ff0000000000001,0x3cafffffffffffff,1 +np.float64,0x3ff0000020000000,0x3e7fffffe000002b,1 +np.float64,0x3ff0000000000001,0x3cafffffffffffff,1 +np.float64,0x3fefffffe0000000,0xbe70000008000005,1 +np.float64,0x3fefffffffffffff,0xbca0000000000000,1 +## random numbers ## +np.float64,0x02500186f3d9da56,0xc0855b8abf135773,1 +np.float64,0x09200815a3951173,0xc082ff1ad7131bdc,1 +np.float64,0x0da029623b0243d4,0xc0816fc994695bb5,1 +np.float64,0x48703b8ac483a382,0x40579213a313490b,1 +np.float64,0x09207b74c87c9860,0xc082fee20ff349ef,1 +np.float64,0x62c077698e8df947,0x407821c996d110f0,1 +np.float64,0x2350b45e87c3cfb0,0xc073d6b16b51d072,1 +np.float64,0x3990a23f9ff2b623,0xc051aa60eadd8c61,1 +np.float64,0x0d011386a116c348,0xc081a6cc7ea3b8fb,1 +np.float64,0x1fe0f0303ebe273a,0xc0763870b78a81ca,1 +np.float64,0x0cd1260121d387da,0xc081b7668d61a9d1,1 +np.float64,0x1e6135a8f581d422,0xc077425ac10f08c2,1 +np.float64,0x622168db5fe52d30,0x4077b3c669b9fadb,1 +np.float64,0x69f188e1ec6d1718,0x407d1e2f18c63889,1 +np.float64,0x3aa1bf1d9c4dd1a3,0xc04d682e24bde479,1 +np.float64,0x6c81c4011ce4f683,0x407ee5190e8a8e6a,1 +np.float64,0x2191fa55aa5a5095,0xc0750c0c318b5e2d,1 +np.float64,0x32a1f602a32bf360,0xc06270caa493fc17,1 +np.float64,0x16023c90ba93249b,0xc07d0f88e0801638,1 +np.float64,0x1c525fe6d71fa9ff,0xc078af49c66a5d63,1 +np.float64,0x1a927675815d65b7,0xc079e5bdd7fe376e,1 +np.float64,0x41227b8fe70da028,0x402aa0c9f9a84c71,1 +np.float64,0x4962bb6e853fe87d,0x405a34aa04c83747,1 +np.float64,0x23d2cda00b26b5a4,0xc0737c13a06d00ea,1 +np.float64,0x2d13083fd62987fa,0xc06a25055aeb474e,1 +np.float64,0x10e31e4c9b4579a1,0xc0804e181929418e,1 +np.float64,0x26d3247d556a86a9,0xc0716774171da7e8,1 +np.float64,0x6603379398d0d4ac,0x407a64f51f8a887b,1 +np.float64,0x02d38af17d9442ba,0xc0852d955ac9dd68,1 +np.float64,0x6a2382b4818dd967,0x407d4129d688e5d4,1 +np.float64,0x2ee3c403c79b3934,0xc067a091fefaf8b6,1 +np.float64,0x6493a699acdbf1a4,0x4079663c8602bfc5,1 +np.float64,0x1c8413c4f0de3100,0xc0788c99697059b6,1 +np.float64,0x4573f1ed350d9622,0x404e9bd1e4c08920,1 +np.float64,0x2f34265c9200b69c,0xc067310cfea4e986,1 +np.float64,0x19b43e65fa22029b,0xc07a7f8877de22d6,1 +np.float64,0x0af48ab7925ed6bc,0xc0825c4fbc0e5ade,1 +np.float64,0x4fa49699cad82542,0x4065c76d2a318235,1 +np.float64,0x7204a15e56ade492,0x40815bb87484dffb,1 +np.float64,0x4734aa08a230982d,0x40542a4bf7a361a9,1 +np.float64,0x1ae4ed296c2fd749,0xc079ac4921f20abb,1 +np.float64,0x472514ea4370289c,0x4053ff372bd8f18f,1 +np.float64,0x53a54b3f73820430,0x406b5411fc5f2e33,1 +np.float64,0x64754de5a15684fa,0x407951592e99a5ab,1 +np.float64,0x69358e279868a7c3,0x407c9c671a882c31,1 +np.float64,0x284579ec61215945,0xc0706688e55f0927,1 +np.float64,0x68b5c58806447adc,0x407c43d6f4eff760,1 +np.float64,0x1945a83f98b0e65d,0xc07acc15eeb032cc,1 +np.float64,0x0fc5eb98a16578bf,0xc080b0d02eddca0e,1 +np.float64,0x6a75e208f5784250,0x407d7a7383bf8f05,1 +np.float64,0x0fe63a029c47645d,0xc080a59ca1e98866,1 +np.float64,0x37963ac53f065510,0xc057236281f7bdb6,1 +np.float64,0x135661bb07067ff7,0xc07ee924930c21e4,1 +np.float64,0x4b4699469d458422,0x405f73843756e887,1 +np.float64,0x1a66d73e4bf4881b,0xc07a039ba1c63adf,1 +np.float64,0x12a6b9b119a7da59,0xc07f62e49c6431f3,1 +np.float64,0x24c719aa8fd1bdb5,0xc072d26da4bf84d3,1 +np.float64,0x0fa6ff524ffef314,0xc080bb8514662e77,1 +np.float64,0x1db751d66fdd4a9a,0xc077b77cb50d7c92,1 +np.float64,0x4947374c516da82c,0x4059e9acfc7105bf,1 +np.float64,0x1b1771ab98f3afc8,0xc07989326b8e1f66,1 +np.float64,0x25e78805baac8070,0xc0720a818e6ef080,1 +np.float64,0x4bd7a148225d3687,0x406082d004ea3ee7,1 +np.float64,0x53d7d6b2bbbda00a,0x406b9a398967cbd5,1 +np.float64,0x6997fb9f4e1c685f,0x407ce0a703413eba,1 +np.float64,0x069802c2ff71b951,0xc083df39bf7acddc,1 +np.float64,0x4d683ac9890f66d8,0x4062ae21d8c2acf0,1 +np.float64,0x5a2825863ec14f4c,0x40722d718d549552,1 +np.float64,0x0398799a88f4db80,0xc084e93dab8e2158,1 +np.float64,0x5ed87a8b77e135a5,0x40756d7051777b33,1 +np.float64,0x5828cd6d79b9bede,0x4070cafb22fc6ca1,1 +np.float64,0x7b18ba2a5ec6f068,0x408481386b3ed6fe,1 +np.float64,0x4938fd60922198fe,0x4059c206b762ea7e,1 +np.float64,0x31b8f44fcdd1a46e,0xc063b2faa8b6434e,1 +np.float64,0x5729341c0d918464,0x407019cac0c4a7d7,1 +np.float64,0x13595e9228ee878e,0xc07ee7235a7d8088,1 +np.float64,0x17698b0dc9dd4135,0xc07c1627e3a5ad5f,1 +np.float64,0x63b977c283abb0cc,0x4078cf1ec6ed65be,1 +np.float64,0x7349cc0d4dc16943,0x4081cc697ce4cb53,1 +np.float64,0x4e49a80b732fb28d,0x4063e67e3c5cbe90,1 +np.float64,0x07ba14b848a8ae02,0xc0837ac032a094e0,1 +np.float64,0x3da9f17b691bfddc,0xc03929c25366acda,1 +np.float64,0x02ea39aa6c3ac007,0xc08525af6f21e1c4,1 +np.float64,0x3a6a42f04ed9563d,0xc04e98e825dca46b,1 +np.float64,0x1afa877cd7900be7,0xc0799d6648cb34a9,1 +np.float64,0x58ea986649e052c6,0x4071512e939ad790,1 +np.float64,0x691abbc04647f536,0x407c89aaae0fcb83,1 +np.float64,0x43aabc5063e6f284,0x4044b45d18106fd2,1 +np.float64,0x488b003c893e0bea,0x4057df012a2dafbe,1 +np.float64,0x77eb076ed67caee5,0x40836720de94769e,1 +np.float64,0x5c1b46974aba46f4,0x40738731ba256007,1 +np.float64,0x1a5b29ecb5d3c261,0xc07a0becc77040d6,1 +np.float64,0x5d8b6ccf868c6032,0x4074865c1865e2db,1 +np.float64,0x4cfb6690b4aaf5af,0x406216cd8c7e8ddb,1 +np.float64,0x76cbd8eb5c5fc39e,0x4083038dc66d682b,1 +np.float64,0x28bbd1fec5012814,0xc07014c2dd1b9711,1 +np.float64,0x33dc1b3a4fd6bf7a,0xc060bd0756e07d8a,1 +np.float64,0x52bbe89b37de99f3,0x406a10041aa7d343,1 +np.float64,0x07bc479d15eb2dd3,0xc0837a1a6e3a3b61,1 +np.float64,0x18fc5275711a901d,0xc07aff3e9d62bc93,1 +np.float64,0x114c9758e247dc71,0xc080299a7cf15b05,1 +np.float64,0x25ac8f6d60755148,0xc07233c4c0c511d4,1 +np.float64,0x260cae2bb9e9fd7e,0xc071f128c7e82eac,1 +np.float64,0x572ccdfe0241de82,0x40701bedc84bb504,1 +np.float64,0x0ddcef6c8d41f5ee,0xc0815a7e16d07084,1 +np.float64,0x6dad1d59c988af68,0x407fb4a0bc0142b1,1 +np.float64,0x025d200580d8b6d1,0xc08556c0bc32b1b2,1 +np.float64,0x7aad344b6aa74c18,0x40845bbc453f22be,1 +np.float64,0x5b5d9d6ad9d14429,0x4073036d2d21f382,1 +np.float64,0x49cd8d8dcdf19954,0x405b5c034f5c7353,1 +np.float64,0x63edb9483335c1e6,0x4078f2dd21378786,1 +np.float64,0x7b1dd64c9d2c26bd,0x408482b922017bc9,1 +np.float64,0x782e13e0b574be5f,0x40837e2a0090a5ad,1 +np.float64,0x592dfe18b9d6db2f,0x40717f777fbcb1ec,1 +np.float64,0x654e3232ac60d72c,0x4079e71a95a70446,1 +np.float64,0x7b8e42ad22091456,0x4084a9a6f1e61722,1 +np.float64,0x570e88dfd5860ae6,0x407006ae6c0d137a,1 +np.float64,0x294e98346cb98ef1,0xc06f5edaac12bd44,1 +np.float64,0x1adeaa4ab792e642,0xc079b1431d5e2633,1 +np.float64,0x7b6ead3377529ac8,0x40849eabc8c7683c,1 +np.float64,0x2b8eedae8a9b2928,0xc06c400054deef11,1 +np.float64,0x65defb45b2dcf660,0x407a4b53f181c05a,1 +np.float64,0x1baf582d475e7701,0xc07920bcad4a502c,1 +np.float64,0x461f39cf05a0f15a,0x405126368f984fa1,1 +np.float64,0x7e5f6f5dcfff005b,0x4085a37d610439b4,1 +np.float64,0x136f66e4d09bd662,0xc07ed8a2719f2511,1 +np.float64,0x65afd8983fb6ca1f,0x407a2a7f48bf7fc1,1 +np.float64,0x572fa7f95ed22319,0x40701d706cf82e6f,1 diff --git a/numpy/core/tests/data/umath-validation-set-log10.csv b/numpy/core/tests/data/umath-validation-set-log10.csv new file mode 100644 index 000000000000..7f5241a2e03d --- /dev/null +++ b/numpy/core/tests/data/umath-validation-set-log10.csv @@ -0,0 +1,1629 @@ +dtype,input,output,ulperrortol +np.float32,0x3f6fd5c8,0xbce80e8e,4 +np.float32,0x3ea4ab17,0xbefc3deb,4 +np.float32,0x3e87a133,0xbf13b0b7,4 +np.float32,0x3f0d9069,0xbe83bb19,4 +np.float32,0x3f7b9269,0xbbf84f47,4 +np.float32,0x3f7a9ffa,0xbc16fd97,4 +np.float32,0x7f535d34,0x4219cb66,4 +np.float32,0x3e79ad7c,0xbf1ce857,4 +np.float32,0x7e8bfd3b,0x4217dfe9,4 +np.float32,0x3f2d2ee9,0xbe2dcec6,4 +np.float32,0x572e04,0xc21862e4,4 +np.float32,0x7f36f8,0xc217bad5,4 +np.float32,0x3f7982fb,0xbc36aaed,4 +np.float32,0x45b019,0xc218c67c,4 +np.float32,0x3f521c46,0xbdafb3e3,4 +np.float32,0x80000001,0x7fc00000,4 +np.float32,0x3f336c81,0xbe1e107f,4 +np.float32,0x3eac92d7,0xbef1d0bb,4 +np.float32,0x47bdfc,0xc218b990,4 +np.float32,0x7f2d94c8,0x421973d1,4 +np.float32,0x7d53ff8d,0x4214fbb6,4 +np.float32,0x3f581e4e,0xbd96a079,4 +np.float32,0x7ddaf20d,0x42163e4e,4 +np.float32,0x3f341d3c,0xbe1c5b4c,4 +np.float32,0x7ef04ba9,0x4218d032,4 +np.float32,0x620ed2,0xc2182e99,4 +np.float32,0x507850,0xc2188682,4 +np.float32,0x7d08f9,0xc217c284,4 +np.float32,0x7f0cf2aa,0x42191734,4 +np.float32,0x3f109a17,0xbe7e04fe,4 +np.float32,0x7f426152,0x4219a625,4 +np.float32,0x7f32d5a3,0x42198113,4 +np.float32,0x2e14b2,0xc2197e6f,4 +np.float32,0x3a5acd,0xc219156a,4 +np.float32,0x50a565,0xc2188589,4 +np.float32,0x5b751c,0xc2184d97,4 +np.float32,0x7e4149f6,0x42173b22,4 +np.float32,0x3dc34bf9,0xbf82a42a,4 +np.float32,0x3d12bc28,0xbfb910d6,4 +np.float32,0x7ebd2584,0x421865c1,4 +np.float32,0x7f6b3375,0x4219faeb,4 +np.float32,0x7fa00000,0x7fe00000,4 +np.float32,0x3f35fe7d,0xbe17bd33,4 +np.float32,0x7db45c87,0x4215e818,4 +np.float32,0x3efff366,0xbe9a2b8d,4 +np.float32,0x3eb331d0,0xbee971a3,4 +np.float32,0x3f259d5f,0xbe41ae2e,4 +np.float32,0x3eab85ec,0xbef32c4a,4 +np.float32,0x7f194b8a,0x42193c8c,4 +np.float32,0x3f11a614,0xbe7acfc7,4 +np.float32,0x5b17,0xc221f16b,4 +np.float32,0x3f33dadc,0xbe1cff4d,4 +np.float32,0x3cda1506,0xbfc9920f,4 +np.float32,0x3f6856f1,0xbd2c8290,4 +np.float32,0x7f3357fb,0x42198257,4 +np.float32,0x7f56f329,0x4219d2e1,4 +np.float32,0x3ef84108,0xbea0f595,4 +np.float32,0x3f72340f,0xbcc51916,4 +np.float32,0x3daf28,0xc218fcbd,4 +np.float32,0x131035,0xc21b06f4,4 +np.float32,0x3f275c3b,0xbe3d0487,4 +np.float32,0x3ef06130,0xbea82069,4 +np.float32,0x3f57f3b0,0xbd974fef,4 +np.float32,0x7f6c4a78,0x4219fcfa,4 +np.float32,0x7e8421d0,0x4217c639,4 +np.float32,0x3f17a479,0xbe68e08e,4 +np.float32,0x7f03774e,0x4218f83b,4 +np.float32,0x441a33,0xc218d0b8,4 +np.float32,0x539158,0xc21875b6,4 +np.float32,0x3e8fcc75,0xbf0d3018,4 +np.float32,0x7ef74130,0x4218dce4,4 +np.float32,0x3ea6f4fa,0xbef92c38,4 +np.float32,0x7f3948ab,0x421990d5,4 +np.float32,0x7db6f8f5,0x4215ee7c,4 +np.float32,0x3ee44a2f,0xbeb399e5,4 +np.float32,0x156c59,0xc21ad30d,4 +np.float32,0x3f21ee53,0xbe4baf16,4 +np.float32,0x3f2c08f4,0xbe30c424,4 +np.float32,0x3f49885c,0xbdd4c6a9,4 +np.float32,0x3eae0b9c,0xbeefed54,4 +np.float32,0x1b5c1f,0xc21a6646,4 +np.float32,0x3e7330e2,0xbf1fd592,4 +np.float32,0x3ebbeb4c,0xbededf82,4 +np.float32,0x427154,0xc218dbb1,4 +np.float32,0x3f6b8b4b,0xbd142498,4 +np.float32,0x8e769,0xc21c5981,4 +np.float32,0x3e9db557,0xbf02ec1c,4 +np.float32,0x3f001bef,0xbe99f019,4 +np.float32,0x3e58b48c,0xbf2ca77a,4 +np.float32,0x3d46c16b,0xbfa8327c,4 +np.float32,0x7eeeb305,0x4218cd3b,4 +np.float32,0x3e3f163d,0xbf3aa446,4 +np.float32,0x3f66c872,0xbd3877d9,4 +np.float32,0x7f7162f8,0x421a0677,4 +np.float32,0x3edca3bc,0xbebb2e28,4 +np.float32,0x3dc1055b,0xbf834afa,4 +np.float32,0x12b16f,0xc21b0fad,4 +np.float32,0x3f733898,0xbcb62e16,4 +np.float32,0x3e617af8,0xbf283db0,4 +np.float32,0x7e86577a,0x4217cd99,4 +np.float32,0x3f0ba3c7,0xbe86c633,4 +np.float32,0x3f4cad25,0xbdc70247,4 +np.float32,0xb6cdf,0xc21bea9f,4 +np.float32,0x3f42971a,0xbdf3f49e,4 +np.float32,0x3e6ccad2,0xbf22cc78,4 +np.float32,0x7f2121b2,0x421952b8,4 +np.float32,0x3f6d3f55,0xbd075366,4 +np.float32,0x3f524f,0xc218f117,4 +np.float32,0x3e95b5d9,0xbf08b56a,4 +np.float32,0x7f6ae47d,0x4219fa56,4 +np.float32,0x267539,0xc219ceda,4 +np.float32,0x3ef72f6d,0xbea1eb2e,4 +np.float32,0x2100b2,0xc21a12e2,4 +np.float32,0x3d9777d1,0xbf90c4e7,4 +np.float32,0x44c6f5,0xc218cc56,4 +np.float32,0x7f2a613d,0x42196b8a,4 +np.float32,0x390a25,0xc2191f8d,4 +np.float32,0x3f1de5ad,0xbe56e703,4 +np.float32,0x2f59ce,0xc2197258,4 +np.float32,0x7f3b12a1,0x4219951b,4 +np.float32,0x3ecb66d4,0xbecd44ca,4 +np.float32,0x7e74ff,0xc217bd7d,4 +np.float32,0x7ed83f78,0x4218a14d,4 +np.float32,0x685994,0xc21812f1,4 +np.float32,0xbf800000,0x7fc00000,4 +np.float32,0x736f47,0xc217e60b,4 +np.float32,0x7f09c371,0x42190d0a,4 +np.float32,0x3f7ca51d,0xbbbbbce0,4 +np.float32,0x7f4b4d3b,0x4219ba1a,4 +np.float32,0x3f6c4471,0xbd0eb076,4 +np.float32,0xd944e,0xc21b9dcf,4 +np.float32,0x7cb06ffc,0x421375cd,4 +np.float32,0x586187,0xc2185cce,4 +np.float32,0x3f3cbf5b,0xbe078911,4 +np.float32,0x3f30b504,0xbe24d983,4 +np.float32,0x3f0a16ba,0xbe8941fd,4 +np.float32,0x5c43b0,0xc21849af,4 +np.float32,0x3dad74f6,0xbf893bd5,4 +np.float32,0x3c586958,0xbff087a6,4 +np.float32,0x3e8307a8,0xbf1786ba,4 +np.float32,0x7dcd1776,0x4216213d,4 +np.float32,0x3f44d107,0xbde9d662,4 +np.float32,0x3e2e6823,0xbf44cbec,4 +np.float32,0x3d87ea27,0xbf96caca,4 +np.float32,0x3e0c715b,0xbf5ce07e,4 +np.float32,0x7ec9cd5a,0x4218828e,4 +np.float32,0x3e26c0b4,0xbf49c93e,4 +np.float32,0x75b94e,0xc217dd50,4 +np.float32,0x3df7b9f5,0xbf6ad7f4,4 +np.float32,0x0,0xff800000,4 +np.float32,0x3f284795,0xbe3a94da,4 +np.float32,0x7ee49092,0x4218b9f0,4 +np.float32,0x7f4c20e0,0x4219bbe8,4 +np.float32,0x3efbbce8,0xbe9ddc4b,4 +np.float32,0x12274a,0xc21b1cb4,4 +np.float32,0x5fa1b1,0xc21839be,4 +np.float32,0x7f0b210e,0x4219116d,4 +np.float32,0x3f67092a,0xbd368545,4 +np.float32,0x3d572721,0xbfa3ca5b,4 +np.float32,0x3f7913ce,0xbc431028,4 +np.float32,0x3b0613,0xc2191059,4 +np.float32,0x3e1d16c0,0xbf506c6f,4 +np.float32,0xab130,0xc21c081d,4 +np.float32,0x3e23ac97,0xbf4bdb9d,4 +np.float32,0x7ef52368,0x4218d911,4 +np.float32,0x7f38e686,0x42198fe9,4 +np.float32,0x3f106a21,0xbe7e9897,4 +np.float32,0x3ecef8d5,0xbec96644,4 +np.float32,0x3ec37e02,0xbed61683,4 +np.float32,0x3efbd063,0xbe9dcb17,4 +np.float32,0x3f318fe3,0xbe22b402,4 +np.float32,0x7e5e5228,0x4217795d,4 +np.float32,0x72a046,0xc217e92c,4 +np.float32,0x7f6f970b,0x421a0324,4 +np.float32,0x3ed871b4,0xbebf72fb,4 +np.float32,0x7a2eaa,0xc217ccc8,4 +np.float32,0x3e819655,0xbf18c1d7,4 +np.float32,0x80800000,0x7fc00000,4 +np.float32,0x7eab0719,0x421838f9,4 +np.float32,0x7f0763cb,0x4219054f,4 +np.float32,0x3f191672,0xbe64a8af,4 +np.float32,0x7d4327,0xc217c1b6,4 +np.float32,0x3f724ba6,0xbcc3bea3,4 +np.float32,0x60fe06,0xc2183375,4 +np.float32,0x48cd59,0xc218b30b,4 +np.float32,0x3f7fec2b,0xb909d3f3,4 +np.float32,0x1c7bb9,0xc21a5460,4 +np.float32,0x24d8a8,0xc219e1e4,4 +np.float32,0x3e727c52,0xbf20283c,4 +np.float32,0x4bc460,0xc218a14a,4 +np.float32,0x63e313,0xc2182661,4 +np.float32,0x7f625581,0x4219e9d4,4 +np.float32,0x3eeb3e77,0xbeacedc0,4 +np.float32,0x7ef27a47,0x4218d437,4 +np.float32,0x27105a,0xc219c7e6,4 +np.float32,0x22a10b,0xc219fd7d,4 +np.float32,0x3f41e907,0xbdf711ab,4 +np.float32,0x7c1fbf95,0x4212155b,4 +np.float32,0x7e5acceb,0x42177244,4 +np.float32,0x3e0892fa,0xbf5ffb83,4 +np.float32,0x3ea0e51d,0xbf00b2c0,4 +np.float32,0x3e56fc29,0xbf2d8a51,4 +np.float32,0x7ee724ed,0x4218beed,4 +np.float32,0x7ebf142b,0x42186a46,4 +np.float32,0x7f6cf35c,0x4219fe37,4 +np.float32,0x3f11abf7,0xbe7abdcd,4 +np.float32,0x588d7a,0xc2185bf1,4 +np.float32,0x3f6e81d2,0xbcfbcf97,4 +np.float32,0x3f1b6be8,0xbe5dee2b,4 +np.float32,0x7f3815e0,0x42198df2,4 +np.float32,0x3f5bfc88,0xbd86d93d,4 +np.float32,0x3f3775d0,0xbe142bbc,4 +np.float32,0x78a958,0xc217d25a,4 +np.float32,0x2ff7c3,0xc2196c96,4 +np.float32,0x4b9c0,0xc21d733c,4 +np.float32,0x3ec025af,0xbed9ecf3,4 +np.float32,0x6443f0,0xc21824b3,4 +np.float32,0x3f754e28,0xbc97d299,4 +np.float32,0x3eaa91d3,0xbef4699d,4 +np.float32,0x3e5f2837,0xbf296478,4 +np.float32,0xe5676,0xc21b85a4,4 +np.float32,0x3f6859f2,0xbd2c6b90,4 +np.float32,0x3f68686b,0xbd2bfcc6,4 +np.float32,0x4b39b8,0xc218a47b,4 +np.float32,0x630ac4,0xc2182a28,4 +np.float32,0x160980,0xc21ac67d,4 +np.float32,0x3ed91c4d,0xbebec3fd,4 +np.float32,0x7ec27b0d,0x4218721f,4 +np.float32,0x3f3c0a5f,0xbe09344b,4 +np.float32,0x3dbff9c1,0xbf839841,4 +np.float32,0x7f0e8ea7,0x42191c40,4 +np.float32,0x3f36b162,0xbe1608e4,4 +np.float32,0x228bb3,0xc219fe90,4 +np.float32,0x2fdd30,0xc2196d8c,4 +np.float32,0x3e8fce8e,0xbf0d2e79,4 +np.float32,0x3f36acc7,0xbe16141a,4 +np.float32,0x7f44b51c,0x4219ab70,4 +np.float32,0x3ec3371c,0xbed66736,4 +np.float32,0x4388a2,0xc218d473,4 +np.float32,0x3f5aa6c3,0xbd8c4344,4 +np.float32,0x7f09fce4,0x42190dc3,4 +np.float32,0x7ed7854a,0x42189fce,4 +np.float32,0x7f4da83a,0x4219bf3a,4 +np.float32,0x3db8da28,0xbf85b25a,4 +np.float32,0x7f449686,0x4219ab2b,4 +np.float32,0x2eb25,0xc21e498c,4 +np.float32,0x3f2bcc08,0xbe3161bd,4 +np.float32,0x36c923,0xc219317b,4 +np.float32,0x3d52a866,0xbfa4f6d2,4 +np.float32,0x3f7d6688,0xbb913e4e,4 +np.float32,0x3f5a6ba4,0xbd8d33e3,4 +np.float32,0x719740,0xc217ed35,4 +np.float32,0x78a472,0xc217d26c,4 +np.float32,0x7ee33d0c,0x4218b759,4 +np.float32,0x7f668c1d,0x4219f208,4 +np.float32,0x3e29c600,0xbf47ca46,4 +np.float32,0x3f3cefc3,0xbe071712,4 +np.float32,0x3e224ebd,0xbf4cca41,4 +np.float32,0x7f1417be,0x42192d31,4 +np.float32,0x7f29d7d5,0x42196a23,4 +np.float32,0x3338ce,0xc2194f65,4 +np.float32,0x2a7897,0xc219a2b6,4 +np.float32,0x3d6bc3d8,0xbf9eb468,4 +np.float32,0x3f6bd7bf,0xbd11e392,4 +np.float32,0x7f6d26bf,0x4219fe98,4 +np.float32,0x3f52d378,0xbdacadb5,4 +np.float32,0x3efac453,0xbe9eb84a,4 +np.float32,0x3f692eb7,0xbd261184,4 +np.float32,0x3f6a0bb5,0xbd1f7ec1,4 +np.float32,0x3f037a49,0xbe942aa8,4 +np.float32,0x3f465bd4,0xbde2e530,4 +np.float32,0x7ef0f47b,0x4218d16a,4 +np.float32,0x637127,0xc218285e,4 +np.float32,0x3f41e511,0xbdf723d7,4 +np.float32,0x7f800000,0x7f800000,4 +np.float32,0x3f3342d5,0xbe1e77d5,4 +np.float32,0x7f57cfe6,0x4219d4a9,4 +np.float32,0x3e4358ed,0xbf3830a7,4 +np.float32,0x3ce25f15,0xbfc77f2b,4 +np.float32,0x7ed057e7,0x421890be,4 +np.float32,0x7ce154d9,0x4213e295,4 +np.float32,0x3ee91984,0xbeaef703,4 +np.float32,0x7e4e919c,0x421758af,4 +np.float32,0x6830e7,0xc218139e,4 +np.float32,0x3f12f08e,0xbe76e328,4 +np.float32,0x7f0a7a32,0x42190f56,4 +np.float32,0x7f38e,0xc21c8bd3,4 +np.float32,0x3e01def9,0xbf6593e3,4 +np.float32,0x3f5c8c6d,0xbd849432,4 +np.float32,0x3eed8747,0xbeaac7a3,4 +np.float32,0x3cadaa0e,0xbfd63b21,4 +np.float32,0x3f7532a9,0xbc996178,4 +np.float32,0x31f3ac,0xc2195a8f,4 +np.float32,0x3f0e0f97,0xbe82f3af,4 +np.float32,0x3f2a1f35,0xbe35bd3f,4 +np.float32,0x3f4547b2,0xbde7bebd,4 +np.float32,0x3f7988a6,0xbc36094c,4 +np.float32,0x74464c,0xc217e2d2,4 +np.float32,0x7f7518be,0x421a0d3f,4 +np.float32,0x7e97fa0a,0x42180473,4 +np.float32,0x584e3a,0xc2185d2f,4 +np.float32,0x3e7291f3,0xbf201e52,4 +np.float32,0xc0a05,0xc21bd359,4 +np.float32,0x3a3177,0xc21916a6,4 +np.float32,0x4f417f,0xc2188d45,4 +np.float32,0x263fce,0xc219d145,4 +np.float32,0x7e1d58,0xc217beb1,4 +np.float32,0x7f056af3,0x4218fec9,4 +np.float32,0x3f21c181,0xbe4c2a3f,4 +np.float32,0x7eca4956,0x4218839f,4 +np.float32,0x3e58afa8,0xbf2ca9fd,4 +np.float32,0x3f40d583,0xbdfc04ef,4 +np.float32,0x7f432fbb,0x4219a7fc,4 +np.float32,0x43aaa4,0xc218d393,4 +np.float32,0x7f2c9b62,0x42197150,4 +np.float32,0x5c3876,0xc21849e5,4 +np.float32,0x7f2034e8,0x42195029,4 +np.float32,0x7e5be772,0x42177481,4 +np.float32,0x80000000,0xff800000,4 +np.float32,0x3f5be03b,0xbd874bb0,4 +np.float32,0x3e32494f,0xbf4259be,4 +np.float32,0x3e1f4671,0xbf4ee30b,4 +np.float32,0x4606cc,0xc218c454,4 +np.float32,0x425cbc,0xc218dc3b,4 +np.float32,0x7dd9b8bf,0x42163bd0,4 +np.float32,0x3f0465d0,0xbe929db7,4 +np.float32,0x3f735077,0xbcb4d0fa,4 +np.float32,0x4d6a43,0xc21897b8,4 +np.float32,0x3e27d600,0xbf4910f5,4 +np.float32,0x3f06e0cc,0xbe8e7d24,4 +np.float32,0x3f3fd064,0xbe005e45,4 +np.float32,0x176f1,0xc21f7c2d,4 +np.float32,0x3eb64e6f,0xbee59d9c,4 +np.float32,0x7f0f075d,0x42191db8,4 +np.float32,0x3f718cbe,0xbcceb621,4 +np.float32,0x3ead7bda,0xbef0a54a,4 +np.float32,0x7f77c1a8,0x421a120c,4 +np.float32,0x3f6a79c5,0xbd1c3afd,4 +np.float32,0x3e992d1f,0xbf062a02,4 +np.float32,0x3e6f6335,0xbf219639,4 +np.float32,0x7f6d9a3e,0x4219ff70,4 +np.float32,0x557ed1,0xc2186b91,4 +np.float32,0x3f13a456,0xbe74c457,4 +np.float32,0x15c2dc,0xc21acc17,4 +np.float32,0x71f36f,0xc217ebcc,4 +np.float32,0x748dea,0xc217e1c1,4 +np.float32,0x7f0f32e0,0x42191e3f,4 +np.float32,0x5b1da8,0xc2184f41,4 +np.float32,0x3d865d3a,0xbf976e11,4 +np.float32,0x3f800000,0x0,4 +np.float32,0x7f67b56d,0x4219f444,4 +np.float32,0x6266a1,0xc2182d0c,4 +np.float32,0x3ec9c5e4,0xbecf0e6b,4 +np.float32,0x6a6a0e,0xc2180a3b,4 +np.float32,0x7e9db6fd,0x421814ef,4 +np.float32,0x3e7458f7,0xbf1f4e88,4 +np.float32,0x3ead8016,0xbef09fdc,4 +np.float32,0x3e263d1c,0xbf4a211e,4 +np.float32,0x7f6b3329,0x4219faeb,4 +np.float32,0x800000,0xc217b818,4 +np.float32,0x3f0654c7,0xbe8f6471,4 +np.float32,0x3f281b71,0xbe3b0990,4 +np.float32,0x7c4c8e,0xc217c524,4 +np.float32,0x7d113a87,0x4214537d,4 +np.float32,0x734b5f,0xc217e696,4 +np.float32,0x7f079d05,0x4219060b,4 +np.float32,0x3ee830b1,0xbeafd58b,4 +np.float32,0x3f1c3b8b,0xbe5b9d96,4 +np.float32,0x3f2bf0c6,0xbe3102aa,4 +np.float32,0x7ddffe22,0x42164871,4 +np.float32,0x3f1e58b4,0xbe55a37f,4 +np.float32,0x5f3edf,0xc2183b8a,4 +np.float32,0x7f1fb6ec,0x42194eca,4 +np.float32,0x3f78718e,0xbc55311e,4 +np.float32,0x3e574b7d,0xbf2d6152,4 +np.float32,0x7eab27c6,0x4218394e,4 +np.float32,0x7f34603c,0x421984e5,4 +np.float32,0x3f3a8b57,0xbe0cc1ca,4 +np.float32,0x3f744181,0xbca7134e,4 +np.float32,0x3f7e3bc4,0xbb45156b,4 +np.float32,0x93ab4,0xc21c498b,4 +np.float32,0x7ed5541e,0x42189b42,4 +np.float32,0x6bf8ec,0xc21803c4,4 +np.float32,0x757395,0xc217de58,4 +np.float32,0x7f177214,0x42193726,4 +np.float32,0x59935f,0xc21856d6,4 +np.float32,0x2cd9ba,0xc2198a78,4 +np.float32,0x3ef6fd5c,0xbea2183c,4 +np.float32,0x3ebb6c63,0xbedf75e0,4 +np.float32,0x7f43272c,0x4219a7e9,4 +np.float32,0x7f42e67d,0x4219a755,4 +np.float32,0x3f3f744f,0xbe0133f6,4 +np.float32,0x7f5fddaa,0x4219e4f4,4 +np.float32,0x3dc9874f,0xbf80e529,4 +np.float32,0x3f2efe64,0xbe292ec8,4 +np.float32,0x3e0406a6,0xbf63bf7c,4 +np.float32,0x3cdbb0aa,0xbfc92984,4 +np.float32,0x3e6597e7,0xbf263b30,4 +np.float32,0x3f0c1153,0xbe861807,4 +np.float32,0x7fce16,0xc217b8c6,4 +np.float32,0x3f5f4e5f,0xbd730dc6,4 +np.float32,0x3ed41ffa,0xbec3ee69,4 +np.float32,0x3f216c78,0xbe4d1446,4 +np.float32,0x3f123ed7,0xbe78fe4b,4 +np.float32,0x7f7e0ca9,0x421a1d34,4 +np.float32,0x7e318af4,0x42171558,4 +np.float32,0x7f1e1659,0x42194a3d,4 +np.float32,0x34d12a,0xc21941c2,4 +np.float32,0x3d9566ad,0xbf918870,4 +np.float32,0x3e799a47,0xbf1cf0e5,4 +np.float32,0x3e89dd6f,0xbf11df76,4 +np.float32,0x32f0d3,0xc21951d8,4 +np.float32,0x7e89d17e,0x4217d8f6,4 +np.float32,0x1f3b38,0xc21a2b6b,4 +np.float32,0x7ee9e060,0x4218c427,4 +np.float32,0x31a673,0xc2195d41,4 +np.float32,0x5180f1,0xc21880d5,4 +np.float32,0x3cd36f,0xc21902f8,4 +np.float32,0x3bb63004,0xc01050cb,4 +np.float32,0x3e8ee9d1,0xbf0ddfde,4 +np.float32,0x3d2a7da3,0xbfb0b970,4 +np.float32,0x3ea58107,0xbefb1dc3,4 +np.float32,0x7f6760b0,0x4219f3a2,4 +np.float32,0x7f7f9e08,0x421a1ff0,4 +np.float32,0x37e7f1,0xc219287b,4 +np.float32,0x3ef7eb53,0xbea14267,4 +np.float32,0x3e2eb581,0xbf449aa5,4 +np.float32,0x3da7671c,0xbf8b3568,4 +np.float32,0x7af36f7b,0x420f33ee,4 +np.float32,0x3eb3602c,0xbee93823,4 +np.float32,0x3f68bcff,0xbd2975de,4 +np.float32,0x3ea7cefb,0xbef80a9d,4 +np.float32,0x3f329689,0xbe202414,4 +np.float32,0x7f0c7c80,0x421915be,4 +np.float32,0x7f4739b8,0x4219b118,4 +np.float32,0x73af58,0xc217e515,4 +np.float32,0x7f13eb2a,0x42192cab,4 +np.float32,0x30f2d9,0xc2196395,4 +np.float32,0x7ea7066c,0x42182e71,4 +np.float32,0x669fec,0xc2181a5b,4 +np.float32,0x3f7d6876,0xbb90d1ef,4 +np.float32,0x3f08a4ef,0xbe8b9897,4 +np.float32,0x7f2a906c,0x42196c05,4 +np.float32,0x3ed3ca42,0xbec44856,4 +np.float32,0x9d27,0xc220fee2,4 +np.float32,0x3e4508a1,0xbf373c03,4 +np.float32,0x3e41f8de,0xbf38f9bb,4 +np.float32,0x3e912714,0xbf0c255b,4 +np.float32,0xff800000,0x7fc00000,4 +np.float32,0x7eefd13d,0x4218cf4f,4 +np.float32,0x3f491674,0xbdd6bded,4 +np.float32,0x3ef49512,0xbea445c9,4 +np.float32,0x3f045b79,0xbe92af15,4 +np.float32,0x3ef6c412,0xbea24bd5,4 +np.float32,0x3e6f3c28,0xbf21a85d,4 +np.float32,0x3ef71839,0xbea2000e,4 +np.float32,0x1,0xc23369f4,4 +np.float32,0x3e3fcfe4,0xbf3a3876,4 +np.float32,0x3e9d7a65,0xbf0315b2,4 +np.float32,0x20b7c4,0xc21a16bd,4 +np.float32,0x7f707b10,0x421a04cb,4 +np.float32,0x7fc00000,0x7fc00000,4 +np.float32,0x3f285ebd,0xbe3a57ac,4 +np.float32,0x74c9ea,0xc217e0dc,4 +np.float32,0x3f6501f2,0xbd4634ab,4 +np.float32,0x3f248959,0xbe4495cc,4 +np.float32,0x7e915ff0,0x4217f0b3,4 +np.float32,0x7edbb910,0x4218a864,4 +np.float32,0x3f7042dd,0xbce1bddb,4 +np.float32,0x6f08c9,0xc217f754,4 +np.float32,0x7f423993,0x4219a5ca,4 +np.float32,0x3f125704,0xbe78b4cd,4 +np.float32,0x7ef7f5ae,0x4218de28,4 +np.float32,0x3f2dd940,0xbe2c1a33,4 +np.float32,0x3f1ca78e,0xbe5a6a8b,4 +np.float32,0x244863,0xc219e8be,4 +np.float32,0x3f2614fe,0xbe406d6b,4 +np.float32,0x3e75e7a3,0xbf1e99b5,4 +np.float32,0x2bdd6e,0xc2199459,4 +np.float32,0x7e49e279,0x42174e7b,4 +np.float32,0x3e3bb09a,0xbf3ca2cd,4 +np.float32,0x649f06,0xc2182320,4 +np.float32,0x7f4a44e1,0x4219b7d6,4 +np.float32,0x400473,0xc218ec3a,4 +np.float32,0x3edb19ad,0xbebcbcad,4 +np.float32,0x3d8ee956,0xbf94006c,4 +np.float32,0x7e91c603,0x4217f1eb,4 +np.float32,0x221384,0xc21a04a6,4 +np.float32,0x7f7dd660,0x421a1cd5,4 +np.float32,0x7ef34609,0x4218d5ac,4 +np.float32,0x7f5ed529,0x4219e2e5,4 +np.float32,0x7f1bf685,0x42194438,4 +np.float32,0x3cdd094a,0xbfc8d294,4 +np.float32,0x7e87fc8e,0x4217d303,4 +np.float32,0x7f53d971,0x4219cc6b,4 +np.float32,0xabc8b,0xc21c0646,4 +np.float32,0x7f5011e6,0x4219c46a,4 +np.float32,0x7e460638,0x421745e5,4 +np.float32,0xa8126,0xc21c0ffd,4 +np.float32,0x3eec2a66,0xbeac0f2d,4 +np.float32,0x3f3a1213,0xbe0de340,4 +np.float32,0x7f5908db,0x4219d72c,4 +np.float32,0x7e0ad3c5,0x4216a7f3,4 +np.float32,0x3f2de40e,0xbe2bfe90,4 +np.float32,0x3d0463c5,0xbfbec8e4,4 +np.float32,0x7c7cde0b,0x4212e19a,4 +np.float32,0x74c24f,0xc217e0f9,4 +np.float32,0x3f14b4cb,0xbe71929b,4 +np.float32,0x3e94e192,0xbf09537f,4 +np.float32,0x3eebde71,0xbeac56bd,4 +np.float32,0x3f65e413,0xbd3f5b8a,4 +np.float32,0x7e109199,0x4216b9f9,4 +np.float32,0x3f22f5d0,0xbe48ddc0,4 +np.float32,0x3e22d3bc,0xbf4c6f4d,4 +np.float32,0x3f7a812f,0xbc1a680b,4 +np.float32,0x3f67f361,0xbd2f7d7c,4 +np.float32,0x3f1caa63,0xbe5a6281,4 +np.float32,0x3f306fde,0xbe2587ab,4 +np.float32,0x3e8df9d3,0xbf0e9b2f,4 +np.float32,0x3eaaccc4,0xbef41cd4,4 +np.float32,0x7f3f65ec,0x42199f45,4 +np.float32,0x3dc706e0,0xbf8196ec,4 +np.float32,0x3e14eaba,0xbf565cf6,4 +np.float32,0xcc60,0xc2208a09,4 +np.float32,0x358447,0xc2193be7,4 +np.float32,0x3dcecade,0xbf7eec70,4 +np.float32,0x3f20b4f8,0xbe4f0ef0,4 +np.float32,0x7e7c979f,0x4217b222,4 +np.float32,0x7f2387b9,0x4219594a,4 +np.float32,0x3f6f6e5c,0xbcee0e05,4 +np.float32,0x7f19ad81,0x42193da8,4 +np.float32,0x5635e1,0xc21867dd,4 +np.float32,0x4c5e97,0xc2189dc4,4 +np.float32,0x7f35f97f,0x421988d1,4 +np.float32,0x7f685224,0x4219f571,4 +np.float32,0x3eca0616,0xbecec7b8,4 +np.float32,0x3f436d0d,0xbdf024ca,4 +np.float32,0x12a97d,0xc21b106a,4 +np.float32,0x7f0fdc93,0x4219204d,4 +np.float32,0x3debfb42,0xbf703e65,4 +np.float32,0x3c6c54d2,0xbfeba291,4 +np.float32,0x7e5d7491,0x421777a1,4 +np.float32,0x3f4bd2f0,0xbdcab87d,4 +np.float32,0x3f7517f4,0xbc9ae510,4 +np.float32,0x3f71a59a,0xbccd480d,4 +np.float32,0x3f514653,0xbdb33f61,4 +np.float32,0x3f4e6ea4,0xbdbf694b,4 +np.float32,0x3eadadec,0xbef06526,4 +np.float32,0x3f3b41c1,0xbe0b0fbf,4 +np.float32,0xc35a,0xc2209e1e,4 +np.float32,0x384982,0xc2192575,4 +np.float32,0x3464c3,0xc2194556,4 +np.float32,0x7f5e20d9,0x4219e17d,4 +np.float32,0x3ea18b62,0xbf004016,4 +np.float32,0x63a02b,0xc218278c,4 +np.float32,0x7ef547ba,0x4218d953,4 +np.float32,0x3f2496fb,0xbe4470f4,4 +np.float32,0x7ea0c8c6,0x42181d81,4 +np.float32,0x3f42ba60,0xbdf35372,4 +np.float32,0x7e40d9,0xc217be34,4 +np.float32,0x3e95883b,0xbf08d750,4 +np.float32,0x3e0cddf3,0xbf5c8aa8,4 +np.float32,0x3f2305d5,0xbe48b20a,4 +np.float32,0x7f0d0941,0x4219177b,4 +np.float32,0x3f7b98d3,0xbbf6e477,4 +np.float32,0x3f687cdc,0xbd2b6057,4 +np.float32,0x3f42ce91,0xbdf2f73d,4 +np.float32,0x3ee00fc0,0xbeb7c217,4 +np.float32,0x7f3d483a,0x42199a53,4 +np.float32,0x3e1e08eb,0xbf4fc18d,4 +np.float32,0x7e202ff5,0x4216e798,4 +np.float32,0x582898,0xc2185ded,4 +np.float32,0x3e3552b1,0xbf40790c,4 +np.float32,0x3d3f7c87,0xbfaa44b6,4 +np.float32,0x669d8e,0xc2181a65,4 +np.float32,0x3f0e21b4,0xbe82d757,4 +np.float32,0x686f95,0xc2181293,4 +np.float32,0x3f48367f,0xbdda9ead,4 +np.float32,0x3dc27802,0xbf82e0a0,4 +np.float32,0x3f6ac40c,0xbd1a07d4,4 +np.float32,0x3bba6d,0xc2190b12,4 +np.float32,0x3ec7b6b0,0xbed15665,4 +np.float32,0x3f1f9ca4,0xbe521955,4 +np.float32,0x3ef2f147,0xbea5c4b8,4 +np.float32,0x7c65f769,0x4212b762,4 +np.float32,0x7e98e162,0x42180716,4 +np.float32,0x3f0f0c09,0xbe8169ea,4 +np.float32,0x3d67f03b,0xbf9f9d48,4 +np.float32,0x7f3751e4,0x42198c18,4 +np.float32,0x7f1fac61,0x42194ead,4 +np.float32,0x3e9b698b,0xbf048d89,4 +np.float32,0x7e66507b,0x42178913,4 +np.float32,0x7f5cb680,0x4219dea5,4 +np.float32,0x234700,0xc219f53e,4 +np.float32,0x3d9984ad,0xbf900591,4 +np.float32,0x3f33a3f2,0xbe1d872a,4 +np.float32,0x3eaf52b6,0xbeee4cf4,4 +np.float32,0x7f078930,0x421905ca,4 +np.float32,0x3f083b39,0xbe8c44df,4 +np.float32,0x3e3823f8,0xbf3ec231,4 +np.float32,0x3eef6f5d,0xbea9008c,4 +np.float32,0x6145e1,0xc218322c,4 +np.float32,0x16d9ae,0xc21ab65f,4 +np.float32,0x7e543376,0x421764a5,4 +np.float32,0x3ef77ccb,0xbea1a5a0,4 +np.float32,0x3f4a443f,0xbdd18af5,4 +np.float32,0x8f209,0xc21c5770,4 +np.float32,0x3ecac126,0xbecdfa33,4 +np.float32,0x3e8662f9,0xbf14b6c7,4 +np.float32,0x23759a,0xc219f2f4,4 +np.float32,0xf256d,0xc21b6d3f,4 +np.float32,0x3f579f93,0xbd98aaa2,4 +np.float32,0x3ed4cc8e,0xbec339cb,4 +np.float32,0x3ed25400,0xbec5d2a1,4 +np.float32,0x3ed6f8ba,0xbec0f795,4 +np.float32,0x7f36efd9,0x42198b2a,4 +np.float32,0x7f5169dd,0x4219c746,4 +np.float32,0x7de18a20,0x42164b80,4 +np.float32,0x3e8de526,0xbf0eab61,4 +np.float32,0x3de0cbcd,0xbf75a47e,4 +np.float32,0xe265f,0xc21b8b82,4 +np.float32,0x3df3cdbd,0xbf6c9e40,4 +np.float32,0x3f38a25a,0xbe115589,4 +np.float32,0x7f01f2c0,0x4218f311,4 +np.float32,0x3da7d5f4,0xbf8b10a5,4 +np.float32,0x4d4fe8,0xc2189850,4 +np.float32,0x3cc96d9d,0xbfcdfc8d,4 +np.float32,0x259a88,0xc219d8d7,4 +np.float32,0x7f1d5102,0x42194810,4 +np.float32,0x7e17ca91,0x4216cfa7,4 +np.float32,0x3f73d110,0xbcad7a8f,4 +np.float32,0x3f009383,0xbe9920ed,4 +np.float32,0x7e22af,0xc217be9f,4 +np.float32,0x3f7de2ce,0xbb6c0394,4 +np.float32,0x3edd0cd2,0xbebac45a,4 +np.float32,0x3ec9b5c1,0xbecf2035,4 +np.float32,0x3168c5,0xc2195f6b,4 +np.float32,0x3e935522,0xbf0a7d18,4 +np.float32,0x3e494077,0xbf34e120,4 +np.float32,0x3f52ed06,0xbdac41ec,4 +np.float32,0x3f73d51e,0xbcad3f65,4 +np.float32,0x3f03d453,0xbe939295,4 +np.float32,0x7ef4ee68,0x4218d8b1,4 +np.float32,0x3ed0e2,0xc218f4a7,4 +np.float32,0x4efab8,0xc2188ed3,4 +np.float32,0x3dbd5632,0xbf845d3b,4 +np.float32,0x7eecad4f,0x4218c972,4 +np.float32,0x9d636,0xc21c2d32,4 +np.float32,0x3e5f3b6b,0xbf295ae7,4 +np.float32,0x7f4932df,0x4219b57a,4 +np.float32,0x4b59b5,0xc218a3be,4 +np.float32,0x3e5de97f,0xbf2a03b4,4 +np.float32,0x3f1c479d,0xbe5b7b3c,4 +np.float32,0x3f42e7e4,0xbdf283a5,4 +np.float32,0x2445,0xc2238af2,4 +np.float32,0x7aa71b43,0x420e8c9e,4 +np.float32,0x3ede6e4e,0xbeb961e1,4 +np.float32,0x7f05dd3b,0x42190045,4 +np.float32,0x3ef5b55c,0xbea3404b,4 +np.float32,0x7f738624,0x421a0a62,4 +np.float32,0x3e7d50a1,0xbf1b4cb4,4 +np.float32,0x3f44cc4a,0xbde9ebcc,4 +np.float32,0x7e1a7b0b,0x4216d777,4 +np.float32,0x3f1d9868,0xbe57c0da,4 +np.float32,0x1ebee2,0xc21a3263,4 +np.float32,0x31685f,0xc2195f6e,4 +np.float32,0x368a8e,0xc2193379,4 +np.float32,0xa9847,0xc21c0c2e,4 +np.float32,0x3bd3b3,0xc2190a56,4 +np.float32,0x3961e4,0xc2191ce3,4 +np.float32,0x7e13a243,0x4216c34e,4 +np.float32,0x7f7b1790,0x421a17ff,4 +np.float32,0x3e55f020,0xbf2e1545,4 +np.float32,0x3f513861,0xbdb37aa8,4 +np.float32,0x3dd9e754,0xbf791ad2,4 +np.float32,0x5e8d86,0xc2183ec9,4 +np.float32,0x26b796,0xc219cbdd,4 +np.float32,0x429daa,0xc218da89,4 +np.float32,0x3f477caa,0xbdddd9ba,4 +np.float32,0x3f0e5114,0xbe828d45,4 +np.float32,0x3f54f362,0xbda3c286,4 +np.float32,0x6eac1c,0xc217f8c8,4 +np.float32,0x3f04c479,0xbe91fef5,4 +np.float32,0x3e993765,0xbf06228e,4 +np.float32,0x3eafd99f,0xbeeda21b,4 +np.float32,0x3f2a759e,0xbe34db96,4 +np.float32,0x3f05adfb,0xbe907937,4 +np.float32,0x3f6e2dfc,0xbd005980,4 +np.float32,0x3f2f2daa,0xbe28b6b5,4 +np.float32,0x15e746,0xc21ac931,4 +np.float32,0x7d34ca26,0x4214b4e5,4 +np.float32,0x7ebd175c,0x4218659f,4 +np.float32,0x7f1ed26b,0x42194c4c,4 +np.float32,0x2588b,0xc21eaab0,4 +np.float32,0x3f0065e3,0xbe996fe2,4 +np.float32,0x3f610376,0xbd658122,4 +np.float32,0x451995,0xc218ca41,4 +np.float32,0x70e083,0xc217f002,4 +np.float32,0x7e19821a,0x4216d4a8,4 +np.float32,0x3e7cd9a0,0xbf1b80fb,4 +np.float32,0x7f1a8f18,0x42194033,4 +np.float32,0x3f008fee,0xbe99271f,4 +np.float32,0xff7fffff,0x7fc00000,4 +np.float32,0x7f31d826,0x42197e9b,4 +np.float32,0x3f18cf12,0xbe657838,4 +np.float32,0x3e5c1bc7,0xbf2aebf9,4 +np.float32,0x3e3d3993,0xbf3bbaf8,4 +np.float32,0x68457a,0xc2181347,4 +np.float32,0x7ddf7561,0x42164761,4 +np.float32,0x7f47341b,0x4219b10c,4 +np.float32,0x4d3ecd,0xc21898b2,4 +np.float32,0x7f43dee8,0x4219a98b,4 +np.float32,0x3f0def7c,0xbe8325f5,4 +np.float32,0x3d5a551f,0xbfa2f994,4 +np.float32,0x7ed26602,0x4218951b,4 +np.float32,0x3ee7fa5b,0xbeb0099a,4 +np.float32,0x7ef74ea8,0x4218dcfc,4 +np.float32,0x6a3bb2,0xc2180afd,4 +np.float32,0x7f4c1e6e,0x4219bbe3,4 +np.float32,0x3e26f625,0xbf49a5a2,4 +np.float32,0xb8482,0xc21be70b,4 +np.float32,0x3f32f077,0xbe1f445b,4 +np.float32,0x7dd694b6,0x4216355a,4 +np.float32,0x7f3d62fd,0x42199a92,4 +np.float32,0x3f48e41a,0xbdd79cbf,4 +np.float32,0x338fc3,0xc2194c75,4 +np.float32,0x3e8355f0,0xbf174462,4 +np.float32,0x7f487e83,0x4219b3eb,4 +np.float32,0x2227f7,0xc21a039b,4 +np.float32,0x7e4383dd,0x4217403a,4 +np.float32,0x52d28b,0xc21879b2,4 +np.float32,0x12472c,0xc21b19a9,4 +np.float32,0x353530,0xc2193e7b,4 +np.float32,0x3f4e4728,0xbdc0137a,4 +np.float32,0x3bf169,0xc2190979,4 +np.float32,0x3eb3ee2e,0xbee8885f,4 +np.float32,0x3f03e3c0,0xbe937892,4 +np.float32,0x3c9f8408,0xbfdaf47f,4 +np.float32,0x40e792,0xc218e61b,4 +np.float32,0x5a6b29,0xc21852ab,4 +np.float32,0x7f268b83,0x4219616a,4 +np.float32,0x3ee25997,0xbeb57fa7,4 +np.float32,0x3f175324,0xbe69cf53,4 +np.float32,0x3f781d91,0xbc5e9827,4 +np.float32,0x7dba5210,0x4215f68c,4 +np.float32,0x7f1e66,0xc217bb2b,4 +np.float32,0x7f7fffff,0x421a209b,4 +np.float32,0x3f646202,0xbd4b10b8,4 +np.float32,0x575248,0xc218622b,4 +np.float32,0x7c67faa1,0x4212bb42,4 +np.float32,0x7f1683f2,0x42193469,4 +np.float32,0x1a3864,0xc21a7931,4 +np.float32,0x7f30ad75,0x42197bae,4 +np.float32,0x7f1c9d05,0x42194612,4 +np.float32,0x3e791795,0xbf1d2b2c,4 +np.float32,0x7e9ebc19,0x421817cd,4 +np.float32,0x4999b7,0xc218ae31,4 +np.float32,0x3d130e2c,0xbfb8f1cc,4 +np.float32,0x3f7e436f,0xbb41bb07,4 +np.float32,0x3ee00241,0xbeb7cf7d,4 +np.float32,0x7e496181,0x42174d5f,4 +np.float32,0x7efe58be,0x4218e978,4 +np.float32,0x3f5e5b0c,0xbd7aa43f,4 +np.float32,0x7ee4c6ab,0x4218ba59,4 +np.float32,0x3f6da8c6,0xbd043d7e,4 +np.float32,0x3e3e6e0f,0xbf3b064b,4 +np.float32,0x3f0143b3,0xbe97f10a,4 +np.float32,0x79170f,0xc217d0c6,4 +np.float32,0x517645,0xc218810f,4 +np.float32,0x3f1f9960,0xbe52226e,4 +np.float32,0x2a8df9,0xc219a1d6,4 +np.float32,0x2300a6,0xc219f8b8,4 +np.float32,0x3ee31355,0xbeb4c97a,4 +np.float32,0x3f20b05f,0xbe4f1ba9,4 +np.float32,0x3ee64249,0xbeb1b0ff,4 +np.float32,0x3a94b7,0xc21913b2,4 +np.float32,0x7ef7ef43,0x4218de1d,4 +np.float32,0x3f1abb5d,0xbe5fe872,4 +np.float32,0x7f65360b,0x4219ef72,4 +np.float32,0x3d315d,0xc219004c,4 +np.float32,0x3f26bbc4,0xbe3eafb9,4 +np.float32,0x3ee8c6e9,0xbeaf45de,4 +np.float32,0x7e5f1452,0x42177ae1,4 +np.float32,0x3f32e777,0xbe1f5aba,4 +np.float32,0x4d39a1,0xc21898d0,4 +np.float32,0x3e59ad15,0xbf2c2841,4 +np.float32,0x3f4be746,0xbdca5fc4,4 +np.float32,0x72e4fd,0xc217e821,4 +np.float32,0x1af0b8,0xc21a6d25,4 +np.float32,0x3f311147,0xbe23f18d,4 +np.float32,0x3f1ecebb,0xbe545880,4 +np.float32,0x7e90d293,0x4217ef02,4 +np.float32,0x3e3b366a,0xbf3ceb46,4 +np.float32,0x3f133239,0xbe761c96,4 +np.float32,0x7541ab,0xc217df15,4 +np.float32,0x3d8c8275,0xbf94f1a1,4 +np.float32,0x483b92,0xc218b689,4 +np.float32,0x3eb0dbed,0xbeec5c6b,4 +np.float32,0x3f00c676,0xbe98c8e2,4 +np.float32,0x3f445ac2,0xbdebed7c,4 +np.float32,0x3d2af4,0xc219007a,4 +np.float32,0x7f196ee1,0x42193cf2,4 +np.float32,0x290c94,0xc219b1db,4 +np.float32,0x3f5dbdc9,0xbd7f9019,4 +np.float32,0x3e80c62e,0xbf1974fc,4 +np.float32,0x3ec9ed2c,0xbecee326,4 +np.float32,0x7f469d60,0x4219afbb,4 +np.float32,0x3f698413,0xbd2386ce,4 +np.float32,0x42163f,0xc218de14,4 +np.float32,0x67a554,0xc21815f4,4 +np.float32,0x3f4bff74,0xbdc9f651,4 +np.float32,0x16a743,0xc21aba39,4 +np.float32,0x2eb8b0,0xc219784b,4 +np.float32,0x3eed9be1,0xbeaab45b,4 +np.float64,0x7fe0d76873e1aed0,0x40733f9d783bad7a,2 +np.float64,0x3fe22626bb244c4d,0xbfcf86a59864eea2,2 +np.float64,0x7f874113d02e8227,0x407324f54c4015b8,2 +np.float64,0x3fe40a46a9e8148d,0xbfca0411f533fcb9,2 +np.float64,0x3fd03932eea07266,0xbfe312bc9cf5649e,2 +np.float64,0x7fee5d2a1b3cba53,0x407343b5f56367a0,2 +np.float64,0x3feb7bda4a76f7b5,0xbfb0ea2c6edc784a,2 +np.float64,0x3fd6cd831a2d9b06,0xbfdcaf2e1a5faf51,2 +np.float64,0x98324e273064a,0xc0733e0e4c6d11c6,2 +np.float64,0x7fe1dd63b363bac6,0x4073400667c405c3,2 +np.float64,0x3fec5971f178b2e4,0xbfaaef32a7d94563,2 +np.float64,0x17abc07e2f579,0xc0734afca4da721e,2 +np.float64,0x3feec6ab5cfd8d57,0xbf9157f3545a8235,2 +np.float64,0x3fe3ae9622a75d2c,0xbfcb04b5ad254581,2 +np.float64,0x7fea73d854b4e7b0,0x407342c0a548f4c5,2 +np.float64,0x7fe29babf4653757,0x4073404eeb5fe714,2 +np.float64,0x7fd3a55d85a74aba,0x40733bde72e86c27,2 +np.float64,0x3fe83ce305f079c6,0xbfbee3511e85e0f1,2 +np.float64,0x3fd72087ea2e4110,0xbfdc4ab30802d7c2,2 +np.float64,0x7feb54ddab76a9ba,0x407342facb6f3ede,2 +np.float64,0xc57e34a18afd,0xc0734f82ec815baa,2 +np.float64,0x7a8cb97ef5198,0xc0733f8fb3777a67,2 +np.float64,0x7fe801032c300205,0x40734213dbe4eda9,2 +np.float64,0x3aefb1f475df7,0xc07344a5f08a0584,2 +np.float64,0x7fee85f1dd3d0be3,0x407343bf4441c2a7,2 +np.float64,0x3fdc7f1055b8fe21,0xbfd67d300630e893,2 +np.float64,0xe8ecddb3d1d9c,0xc0733b194f18f466,2 +np.float64,0x3fdf2b23c73e5648,0xbfd3ff6872c1f887,2 +np.float64,0x3fdba4aef2b7495e,0xbfd7557205e18b7b,2 +np.float64,0x3fe2ac34c6e5586a,0xbfcdf1dac69bfa08,2 +np.float64,0x3fc9852628330a4c,0xbfe66914f0fb9b0a,2 +np.float64,0x7fda211acf344235,0x40733dd9c2177aeb,2 +np.float64,0x3fe9420eb432841d,0xbfba4dd969a32575,2 +np.float64,0xb2f9d1ed65f3a,0xc0733cedfb6527ff,2 +np.float64,0x3fe9768a68f2ed15,0xbfb967c39c35c435,2 +np.float64,0x7fe8268462b04d08,0x4073421eaed32734,2 +np.float64,0x3fcf331f063e663e,0xbfe39e2f4b427ca9,2 +np.float64,0x7fd4eb9e2b29d73b,0x40733c4e4141418d,2 +np.float64,0x7fd2bba658a5774c,0x40733b89cd53d5b1,2 +np.float64,0x3fdfdf04913fbe09,0xbfd360c7fd9d251b,2 +np.float64,0x3fca5bfd0534b7fa,0xbfe5f5f844b2b20c,2 +np.float64,0x3feacd5032f59aa0,0xbfb3b5234ba8bf7b,2 +np.float64,0x7fe9241cec724839,0x4073426631362cec,2 +np.float64,0x3fe57aca20eaf594,0xbfc628e3ac2c6387,2 +np.float64,0x3fec6553ca38caa8,0xbfaa921368d3b222,2 +np.float64,0x3fe1e9676563d2cf,0xbfd020f866ba9b24,2 +np.float64,0x3fd5590667aab20d,0xbfde8458af5a4fd6,2 +np.float64,0x3fdf7528f43eea52,0xbfd3bdb438d6ba5e,2 +np.float64,0xb8dddc5571bbc,0xc0733cb4601e5bb2,2 +np.float64,0xe6d4e1fbcda9c,0xc0733b295ef4a4ba,2 +np.float64,0x3fe7019d962e033b,0xbfc257c0a6e8de16,2 +np.float64,0x3f94ef585029deb1,0xbffb07e5dfb0e936,2 +np.float64,0x7fc863b08030c760,0x4073388e28d7b354,2 +np.float64,0xf684443bed089,0xc0733ab46cfbff9a,2 +np.float64,0x7fe00e901d201d1f,0x40733f489c05a0f0,2 +np.float64,0x9e5c0a273cb82,0xc0733dc7af797e19,2 +np.float64,0x7fe49734f0692e69,0x4073410303680df0,2 +np.float64,0x7fb7b584442f6b08,0x4073338acff72502,2 +np.float64,0x3f99984c30333098,0xbff9a2642a6ed8cc,2 +np.float64,0x7fea2fcda8745f9a,0x407342aeae7f5e64,2 +np.float64,0xe580caadcb01a,0xc0733b33a3639217,2 +np.float64,0x1899ab3831336,0xc0734ab823729417,2 +np.float64,0x39bd4c76737aa,0xc07344ca6fac6d21,2 +np.float64,0xd755b2dbaeab7,0xc0733ba4fe19f2cc,2 +np.float64,0x3f952bebf82a57d8,0xbffaf3e7749c2512,2 +np.float64,0x3fe62ee5d72c5dcc,0xbfc45e3cb5baad08,2 +np.float64,0xb1264a7d624ca,0xc0733d003a1d0a66,2 +np.float64,0x3fc4bd1bcd297a38,0xbfe94b3058345c46,2 +np.float64,0x7fc5758bb32aeb16,0x407337aa7805497f,2 +np.float64,0x3fb0edcaf421db96,0xbff2dfb09c405294,2 +np.float64,0x3fd240fceaa481fa,0xbfe16f356bb36134,2 +np.float64,0x38c0c62a7181a,0xc07344e916d1e9b7,2 +np.float64,0x3fe98f2b3bf31e56,0xbfb8fc6eb622a820,2 +np.float64,0x3fe2bdf99c257bf3,0xbfcdbd0dbbae4d0b,2 +np.float64,0xce4b390d9c967,0xc0733bf14ada3134,2 +np.float64,0x3fd2ad607ba55ac1,0xbfe11da15167b37b,2 +np.float64,0x3fd8154f11b02a9e,0xbfdb2a6fabb9a026,2 +np.float64,0xf37849fde6f09,0xc0733aca8c64344c,2 +np.float64,0x3fcbae43b2375c87,0xbfe547f267c8e570,2 +np.float64,0x3fcd46fd7d3a8dfb,0xbfe48070f7232929,2 +np.float64,0x7fcdd245273ba489,0x407339f3d907b101,2 +np.float64,0x3fac75cd0838eb9a,0xbff4149d177b057b,2 +np.float64,0x7fe8ff3fd7f1fe7f,0x4073425bf968ba6f,2 +np.float64,0x7febadaa4df75b54,0x407343113a91f0e9,2 +np.float64,0x7fd5e4649c2bc8c8,0x40733c9f0620b065,2 +np.float64,0x903429812069,0xc07351b255e27887,2 +np.float64,0x3fe1d8c51c63b18a,0xbfd03ad448c1f1ee,2 +np.float64,0x3fe573ea646ae7d5,0xbfc63ab0bfd0e601,2 +np.float64,0x3f83b3f3c02767e8,0xc00022677e310649,2 +np.float64,0x7fd15d1582a2ba2a,0x40733b02c469c1d6,2 +np.float64,0x3fe63d3dabec7a7b,0xbfc43a56ee97b27e,2 +np.float64,0x7fe3a452fb2748a5,0x407340af1973c228,2 +np.float64,0x3fafac6b303f58d6,0xbff35651703ae9f2,2 +np.float64,0x513ddd24a27bc,0xc073426af96aaebb,2 +np.float64,0x3fef152246be2a45,0xbf89df79d7719282,2 +np.float64,0x3fe8c923e9f19248,0xbfbc67228e8db5f6,2 +np.float64,0x3fd6e2325fadc465,0xbfdc9602fb0b950f,2 +np.float64,0x3fe9616815f2c2d0,0xbfb9c4311a3b415b,2 +np.float64,0x2fe4e4005fc9d,0xc0734616fe294395,2 +np.float64,0x3fbceb02dc39d606,0xbfee4e68f1c7886f,2 +np.float64,0x7fe35e843d66bd07,0x407340963b066ad6,2 +np.float64,0x7fecd6c648f9ad8c,0x4073435a4c176e94,2 +np.float64,0x7fcbd72bf437ae57,0x4073397994b85665,2 +np.float64,0x3feff6443b3fec88,0xbf40eb380d5318ae,2 +np.float64,0x7fb9373cf6326e79,0x407333f869edef08,2 +np.float64,0x63790d9cc6f22,0xc0734102d4793cda,2 +np.float64,0x3f9de6efe83bcde0,0xbff88db6f0a6b56e,2 +np.float64,0xe00f2dc1c01f,0xc0734ea26ab84ff2,2 +np.float64,0xd7a9aa8baf536,0xc0733ba248fa33ab,2 +np.float64,0x3fee0089ea7c0114,0xbf9cab936ac31c4b,2 +np.float64,0x3fdec0d51cbd81aa,0xbfd45ed8878c5860,2 +np.float64,0x7fe91bf5e9f237eb,0x40734263f005081d,2 +np.float64,0x34ea7d1e69d50,0xc07345659dde7444,2 +np.float64,0x7fe67321a3ace642,0x4073419cc8130d95,2 +np.float64,0x9d1aeb2f3a35e,0xc0733dd5d506425c,2 +np.float64,0x7fbb01df003603bd,0x4073347282f1391d,2 +np.float64,0x42b945b285729,0xc07343c92d1bbef9,2 +np.float64,0x7fc92799b8324f32,0x407338c51e3f0733,2 +np.float64,0x3fe119c19b223383,0xbfd16ab707f65686,2 +np.float64,0x3fc9f9ac5333f359,0xbfe62a2f91ec0dff,2 +np.float64,0x3fd820d5a8b041ab,0xbfdb1d2586fe7b18,2 +np.float64,0x10000000000000,0xc0733a7146f72a42,2 +np.float64,0x3fe7e1543eafc2a8,0xbfc045362889592d,2 +np.float64,0xcbc0e1819783,0xc0734f4b68e05b1c,2 +np.float64,0xeb57e411d6afd,0xc0733b06efec001a,2 +np.float64,0xa9b74b47536ea,0xc0733d4c7bd06ddc,2 +np.float64,0x3fe56d4022eada80,0xbfc64bf8c7e3dd59,2 +np.float64,0x3fd445ca27288b94,0xbfdff40aecd0f882,2 +np.float64,0x3fe5af1cf5ab5e3a,0xbfc5a21d83699a04,2 +np.float64,0x7fed3431eb7a6863,0x40734370aa6131e1,2 +np.float64,0x3fd878dea1b0f1bd,0xbfdab8730dc00517,2 +np.float64,0x7ff8000000000000,0x7ff8000000000000,2 +np.float64,0x3feba9fcc1f753fa,0xbfb03027dcecbf65,2 +np.float64,0x7fca4feed6349fdd,0x4073391526327eb0,2 +np.float64,0x3fe7748ddbaee91c,0xbfc144b438218065,2 +np.float64,0x3fb5fbd94c2bf7b3,0xbff10ee6342c21a0,2 +np.float64,0x3feb603b97f6c077,0xbfb15a1f99d6d25e,2 +np.float64,0x3fe2e6fc8ce5cdf9,0xbfcd43edd7f3b4e6,2 +np.float64,0x7feb2b31f7765663,0x407342f02b306688,2 +np.float64,0x3fe290e2282521c4,0xbfce436deb8dbcf3,2 +np.float64,0x3fe3d5adf9e7ab5c,0xbfca96b8aa55d942,2 +np.float64,0x691899f2d2314,0xc07340a1026897c8,2 +np.float64,0x7fe468b008e8d15f,0x407340f33eadc628,2 +np.float64,0x3fb3a4c416274988,0xbff1d71da539a56e,2 +np.float64,0x3fe2442b29e48856,0xbfcf2b0037322661,2 +np.float64,0x3f376fbc7e6ef,0xc073442939a84643,2 +np.float64,0x3fe7c78d65ef8f1b,0xbfc08157cff411de,2 +np.float64,0xd4f27acba9e50,0xc0733bb8d38daa50,2 +np.float64,0x5198919ea3313,0xc07342633ba7cbea,2 +np.float64,0x7fd09f66f0a13ecd,0x40733ab5310b4385,2 +np.float64,0x3fdfe5531dbfcaa6,0xbfd35b487c7e739f,2 +np.float64,0x3fc4b0fecc2961fe,0xbfe95350c38c1640,2 +np.float64,0x7fd5ae21962b5c42,0x40733c8db78b7250,2 +np.float64,0x3fa4a8fcd42951fa,0xbff64e62fe602b72,2 +np.float64,0x7fc8e0e25831c1c4,0x407338b179b91223,2 +np.float64,0x7fdde1df6f3bc3be,0x40733ec87f9f027e,2 +np.float64,0x3fd8b9ad86b1735b,0xbfda6f385532c41b,2 +np.float64,0x3fd9f20ee933e41e,0xbfd91872fd858597,2 +np.float64,0x7feb35332df66a65,0x407342f2b9c715f0,2 +np.float64,0x7fe783dc7eaf07b8,0x407341ef41873706,2 +np.float64,0x7fceee929f3ddd24,0x40733a34e3c660fd,2 +np.float64,0x985b58d730b6b,0xc0733e0c6cfbb6f8,2 +np.float64,0x3fef4bb55cfe976b,0xbf83cb246c6f2a78,2 +np.float64,0x3fe218014f243003,0xbfcfb20ac683e1f6,2 +np.float64,0x7fe43b9fbea8773e,0x407340e3d5d5d29e,2 +np.float64,0x7fe148c74c62918e,0x40733fcba4367b8b,2 +np.float64,0x3feea4ad083d495a,0xbf93443917f3c991,2 +np.float64,0x8bcf6311179ed,0xc0733ea54d59dd31,2 +np.float64,0xf4b7a2dbe96f5,0xc0733ac175182401,2 +np.float64,0x543338baa8668,0xc073422b59165fe4,2 +np.float64,0x3fdb467317368ce6,0xbfd7b4d515929635,2 +np.float64,0x7fe3bbbc89e77778,0x407340b75cdf3de7,2 +np.float64,0x7fe693377aad266e,0x407341a6af60a0f1,2 +np.float64,0x3fc66210502cc421,0xbfe83bb940610a24,2 +np.float64,0x7fa75638982eac70,0x40732e9da476b816,2 +np.float64,0x3fe0d72a4761ae55,0xbfd1d7c82c479fab,2 +np.float64,0x97dec0dd2fbd8,0xc0733e121e072804,2 +np.float64,0x3fef33ec8c7e67d9,0xbf86701be6be8df1,2 +np.float64,0x7fcfca9b423f9536,0x40733a65a51efb94,2 +np.float64,0x9f2215633e443,0xc0733dbf043de9ed,2 +np.float64,0x2469373e48d28,0xc07347fe9e904b77,2 +np.float64,0x7fecc2e18cb985c2,0x407343557f58dfa2,2 +np.float64,0x3fde4acbfdbc9598,0xbfd4ca559e575e74,2 +np.float64,0x3fd6b11cf1ad623a,0xbfdcd1e17ef36114,2 +np.float64,0x3fc19ec494233d89,0xbfeb8ef228e8826a,2 +np.float64,0x4c89ee389913e,0xc07342d50c904f61,2 +np.float64,0x88c2046f11841,0xc0733ecc91369431,2 +np.float64,0x7fc88c13fd311827,0x40733899a125b392,2 +np.float64,0x3fcebd893a3d7b12,0xbfe3d2f35ab93765,2 +np.float64,0x3feb582a1476b054,0xbfb17ae8ec6a0465,2 +np.float64,0x7fd4369e5da86d3c,0x40733c1118b8cd67,2 +np.float64,0x3fda013fc1340280,0xbfd90831b85e98b2,2 +np.float64,0x7fed33d73fba67ad,0x4073437094ce1bd9,2 +np.float64,0x3fed3191053a6322,0xbfa468cc26a8f685,2 +np.float64,0x3fc04ed51c209daa,0xbfeca24a6f093bca,2 +np.float64,0x3fee4ac8763c9591,0xbf986458abbb90b5,2 +np.float64,0xa2d39dd145a74,0xc0733d9633651fbc,2 +np.float64,0x3fe7d9f86f2fb3f1,0xbfc0565a0b059f1c,2 +np.float64,0x3fe3250144e64a03,0xbfcc8eb2b9ae494b,2 +np.float64,0x7fe2b29507a56529,0x4073405774492075,2 +np.float64,0x7fdcdfcbe2b9bf97,0x40733e8b736b1bd8,2 +np.float64,0x3fc832730f3064e6,0xbfe7267ac9b2e7c3,2 +np.float64,0x3fc7e912e52fd226,0xbfe750dfc0aeae57,2 +np.float64,0x7fc960472f32c08d,0x407338d4b4cb3957,2 +np.float64,0x3fbdf182ea3be306,0xbfedd27150283ffb,2 +np.float64,0x3fd1e9359823d26b,0xbfe1b2ac7fd25f8d,2 +np.float64,0x7fbcf75f6039eebe,0x407334ef13eb16f8,2 +np.float64,0x3fe5a3c910eb4792,0xbfc5bf2f57c5d643,2 +np.float64,0x3fcf4f2a6e3e9e55,0xbfe391b6f065c4b8,2 +np.float64,0x3fee067873fc0cf1,0xbf9c53af0373fc0e,2 +np.float64,0xd3f08b85a7e12,0xc0733bc14357e686,2 +np.float64,0x7ff0000000000000,0x7ff0000000000000,2 +np.float64,0x3fc8635f6430c6bf,0xbfe70a7dc77749a7,2 +np.float64,0x3fe3ff5c52a7feb9,0xbfca22617c6636d5,2 +np.float64,0x3fbbae91fa375d24,0xbfeee9d4c300543f,2 +np.float64,0xe3f71b59c7ee4,0xc0733b3f99187375,2 +np.float64,0x7fca93d3be3527a6,0x40733926fd48ecd6,2 +np.float64,0x3fcd29f7223a53ee,0xbfe48e3edf32fe57,2 +np.float64,0x7fdc4ef6f8389ded,0x40733e68401cf2a6,2 +np.float64,0xe009bc81c014,0xc0734ea295ee3e5b,2 +np.float64,0x61f56c78c3eae,0xc073411e1dbd7c54,2 +np.float64,0x3fde131928bc2632,0xbfd4fda024f6927c,2 +np.float64,0x3fb21ee530243dca,0xbff266aaf0358129,2 +np.float64,0x7feaac82a4f55904,0x407342cf7809d9f9,2 +np.float64,0x3fe66ab177ecd563,0xbfc3c92d4d522819,2 +np.float64,0xfe9f9c2bfd3f4,0xc0733a7ade3a88a7,2 +np.float64,0x7fd0c5217c218a42,0x40733ac4e4c6dfa5,2 +np.float64,0x430f4ae6861ea,0xc07343c03d8a9442,2 +np.float64,0x494bff2a92981,0xc073432209d2fd16,2 +np.float64,0x3f8860e9d030c1d4,0xbffeca059ebf5e89,2 +np.float64,0x3fe43732dc286e66,0xbfc98800388bad2e,2 +np.float64,0x6443b60ec8877,0xc07340f4bab11827,2 +np.float64,0x3feda9be6d7b537d,0xbfa0dcb9a6914069,2 +np.float64,0x3fc5ceb6772b9d6d,0xbfe89868c881db70,2 +np.float64,0x3fbdf153023be2a6,0xbfedd2878c3b4949,2 +np.float64,0x7fe8f6b8e8f1ed71,0x407342599a30b273,2 +np.float64,0x3fea6fbdb8b4df7b,0xbfb53bf66f71ee96,2 +np.float64,0xc7ac3dbb8f588,0xc0733c2b525b7963,2 +np.float64,0x3fef3a91f77e7524,0xbf85b2bd3adbbe31,2 +np.float64,0x3f887cb97030f973,0xbffec21ccbb5d22a,2 +np.float64,0x8b2f1c9f165e4,0xc0733ead49300951,2 +np.float64,0x2c1cb32058397,0xc07346a951bd8d2b,2 +np.float64,0x3fe057edd620afdc,0xbfd2acf1881b7e99,2 +np.float64,0x7f82e9530025d2a5,0x4073238591dd52ce,2 +np.float64,0x3fe4e03dff69c07c,0xbfc7be96c5c006fc,2 +np.float64,0x52727b4aa4e50,0xc0734250c58ebbc1,2 +np.float64,0x3f99a62160334c43,0xbff99ea3ca09d8f9,2 +np.float64,0x3fd5314b4faa6297,0xbfdeb843daf01e03,2 +np.float64,0x3fefde89e13fbd14,0xbf5d1facb7a1e9de,2 +np.float64,0x7fb460f1a228c1e2,0x4073327d8cbc5f86,2 +np.float64,0xeb93efb3d727e,0xc0733b052a4990e4,2 +np.float64,0x3fe884baecf10976,0xbfbd9ba9cfe23713,2 +np.float64,0x7fefffffffffffff,0x40734413509f79ff,2 +np.float64,0x149dc7c6293ba,0xc0734bf26b1df025,2 +np.float64,0x64188f88c8313,0xc07340f7b8e6f4b5,2 +np.float64,0x3fdfac314abf5863,0xbfd38d3e9dba1b0e,2 +np.float64,0x3fd72052a42e40a5,0xbfdc4af30ee0b245,2 +np.float64,0x7fdd951f743b2a3e,0x40733eb68fafa838,2 +np.float64,0x65a2dd5acb45c,0xc07340dc8ed625e1,2 +np.float64,0x7fe89a79997134f2,0x4073423fbceb1cbe,2 +np.float64,0x3fe70a000d6e1400,0xbfc24381e09d02f7,2 +np.float64,0x3fe2cec160259d83,0xbfcd8b5e92354129,2 +np.float64,0x3feb9ef77a773def,0xbfb05c7b2ee6f388,2 +np.float64,0xe0d66689c1acd,0xc0733b582c779620,2 +np.float64,0x3fee86bd0ffd0d7a,0xbf94f7870502c325,2 +np.float64,0x186afc6230d60,0xc0734ac55fb66d5d,2 +np.float64,0xc0631f4b80c64,0xc0733c6d7149d373,2 +np.float64,0x3fdad1b87735a371,0xbfd82cca73ec663b,2 +np.float64,0x7fe7f6d313efeda5,0x40734210e84576ab,2 +np.float64,0x7fd7b7fce6af6ff9,0x40733d2d92ffdaaf,2 +np.float64,0x3fe6f35a28ade6b4,0xbfc27a4239b540c3,2 +np.float64,0x7fdb0b834eb61706,0x40733e17073a61f3,2 +np.float64,0x82f4661105e8d,0xc0733f19b34adeed,2 +np.float64,0x3fc77230112ee460,0xbfe796a7603c0d16,2 +np.float64,0x8000000000000000,0xfff0000000000000,2 +np.float64,0x7fb8317bc63062f7,0x407333aec761a739,2 +np.float64,0x7fd165609a22cac0,0x40733b061541ff15,2 +np.float64,0x3fed394768fa728f,0xbfa42e1596e1faf6,2 +np.float64,0x7febab693d7756d1,0x40734310a9ac828e,2 +np.float64,0x7fe809a69230134c,0x407342165b9acb69,2 +np.float64,0x3fc091d38f2123a7,0xbfec69a70fc23548,2 +np.float64,0x3fb2a8f5dc2551ec,0xbff2327f2641dd0d,2 +np.float64,0x7fc60b6fe02c16df,0x407337da5adc342c,2 +np.float64,0x3fefa53c3bbf4a78,0xbf73d1be15b73b00,2 +np.float64,0x7fee09c1717c1382,0x407343a2c479e1cb,2 +np.float64,0x8000000000000001,0x7ff8000000000000,2 +np.float64,0x3fede0b2733bc165,0xbf9e848ac2ecf604,2 +np.float64,0x3fee2ac331bc5586,0xbf9a3b699b721c9a,2 +np.float64,0x3fd4db12d829b626,0xbfdf2a413d1e453a,2 +np.float64,0x7fe605230dec0a45,0x4073417a67db06be,2 +np.float64,0x3fe378b2bf26f165,0xbfcb9dbb2b6d6832,2 +np.float64,0xc1d4c1ab83a98,0xc0733c60244cadbf,2 +np.float64,0x3feb15500e762aa0,0xbfb28c071d5efc22,2 +np.float64,0x3fe36225a626c44b,0xbfcbde4259e9047e,2 +np.float64,0x3fe7c586a72f8b0d,0xbfc08614b13ed4b2,2 +np.float64,0x7fb0f2d8cc21e5b1,0x40733135b2c7dd99,2 +np.float64,0x5957f3feb2aff,0xc07341c1df75638c,2 +np.float64,0x3fca4851bd3490a3,0xbfe6005ae5279485,2 +np.float64,0x824217d904843,0xc0733f232fd58f0f,2 +np.float64,0x4f9332269f267,0xc073428fd8e9cb32,2 +np.float64,0x3fea6f087374de11,0xbfb53ef0d03918b2,2 +np.float64,0x3fd9409ab4328135,0xbfd9d9231381e2b8,2 +np.float64,0x3fdba03b00374076,0xbfd759ec94a7ab5b,2 +np.float64,0x3fe0ce3766619c6f,0xbfd1e6912582ccf0,2 +np.float64,0x3fabd45ddc37a8bc,0xbff43c78d3188423,2 +np.float64,0x3fc3cadd592795bb,0xbfe9f1576c9b2c79,2 +np.float64,0x3fe10df049621be1,0xbfd17df2f2c28022,2 +np.float64,0x945b5d1328b6c,0xc0733e3bc06f1e75,2 +np.float64,0x7fc1c3742b2386e7,0x4073365a403d1051,2 +np.float64,0x7fdc957138b92ae1,0x40733e7977717586,2 +np.float64,0x7f943fa1a0287f42,0x407328d01de143f5,2 +np.float64,0x3fec9631c4392c64,0xbfa914b176d8f9d2,2 +np.float64,0x3fd8e7c008b1cf80,0xbfda3b9d9b6da8f4,2 +np.float64,0x7222f9fee4460,0xc073400e371516cc,2 +np.float64,0x3fe890e43eb121c8,0xbfbd64921462e823,2 +np.float64,0x3fcfd7fe2a3faffc,0xbfe3557e2f207800,2 +np.float64,0x3fed5dd1c1babba4,0xbfa318bb20db64e6,2 +np.float64,0x3fe6aa34c66d546a,0xbfc32c8a8991c11e,2 +np.float64,0x8ca79801196,0xc0736522bd5adf6a,2 +np.float64,0x3feb274079364e81,0xbfb2427b24b0ca20,2 +np.float64,0x7fe04927e4a0924f,0x40733f61c96f7f89,2 +np.float64,0x7c05f656f80bf,0xc0733f7a70555b4e,2 +np.float64,0x7fe97819eff2f033,0x4073427d4169b0f8,2 +np.float64,0x9def86e33bdf1,0xc0733dcc740b7175,2 +np.float64,0x7fedd1ef3f3ba3dd,0x40734395ceab8238,2 +np.float64,0x77bed86cef7dc,0xc0733fb8e0e9bf73,2 +np.float64,0x9274b41b24e97,0xc0733e52b16dff71,2 +np.float64,0x8010000000000000,0x7ff8000000000000,2 +np.float64,0x9c977855392ef,0xc0733ddba7d421d9,2 +np.float64,0xfb4560a3f68ac,0xc0733a9271e6a118,2 +np.float64,0xa67d9f394cfb4,0xc0733d6e9d58cc94,2 +np.float64,0x3fbfa766b03f4ecd,0xbfed0cccfecfc900,2 +np.float64,0x3fe177417522ee83,0xbfd0d45803bff01a,2 +np.float64,0x7fe85e077bb0bc0e,0x4073422e957a4aa3,2 +np.float64,0x7feeb0a6883d614c,0x407343c8f6568f7c,2 +np.float64,0xbab82edb75706,0xc0733ca2a2b20094,2 +np.float64,0xfadb44bdf5b69,0xc0733a9561b7ec04,2 +np.float64,0x3fefb9b82b3f7370,0xbf6ea776b2dcc3a9,2 +np.float64,0x7fe080ba8a610174,0x40733f795779b220,2 +np.float64,0x3f87faa1c02ff544,0xbffee76acafc92b7,2 +np.float64,0x7fed474108fa8e81,0x4073437531d4313e,2 +np.float64,0x3fdb7b229336f645,0xbfd77f583a4a067f,2 +np.float64,0x256dbf0c4adb9,0xc07347cd94e6fa81,2 +np.float64,0x3fd034ae25a0695c,0xbfe3169c15decdac,2 +np.float64,0x3a72177274e44,0xc07344b4cf7d68cd,2 +np.float64,0x7fa2522d5c24a45a,0x40732cef2f793470,2 +np.float64,0x3fb052bdde20a57c,0xbff3207fd413c848,2 +np.float64,0x3fdccfecbbb99fd9,0xbfd62ec04a1a687a,2 +np.float64,0x3fd403ac53280759,0xbfe027a31df2c8cc,2 +np.float64,0x3fab708e4036e11d,0xbff45591df4f2e8b,2 +np.float64,0x7fcfc001993f8002,0x40733a63539acf9d,2 +np.float64,0x3fd2b295dfa5652c,0xbfe119c1b476c536,2 +np.float64,0x7fe8061262b00c24,0x4073421552ae4538,2 +np.float64,0xffefffffffffffff,0x7ff8000000000000,2 +np.float64,0x7fed52093ffaa411,0x40734377c072a7e8,2 +np.float64,0xf3df902fe7bf2,0xc0733ac79a75ff7a,2 +np.float64,0x7fe13d382e227a6f,0x40733fc6fd0486bd,2 +np.float64,0x3621d5086c43b,0xc073453d31effbcd,2 +np.float64,0x3ff0000000000000,0x0,2 +np.float64,0x3fdaffea27b5ffd4,0xbfd7fd139dc1c2c5,2 +np.float64,0x7fea6536dc34ca6d,0x407342bccc564fdd,2 +np.float64,0x7fd478f00c28f1df,0x40733c27c0072fde,2 +np.float64,0x7fa72ef0502e5de0,0x40732e91e83db75c,2 +np.float64,0x7fd302970626052d,0x40733ba3ec6775f6,2 +np.float64,0x7fbb57ab0036af55,0x407334887348e613,2 +np.float64,0x3fda0ff722b41fee,0xbfd8f87b77930330,2 +np.float64,0x1e983ce23d309,0xc073493438f57e61,2 +np.float64,0x7fc90de97c321bd2,0x407338be01ffd4bd,2 +np.float64,0x7fe074b09c20e960,0x40733f7443f0dbe1,2 +np.float64,0x3fed5dec9fbabbd9,0xbfa317efb1fe8a95,2 +np.float64,0x7fdb877632b70eeb,0x40733e3697c88ba8,2 +np.float64,0x7fe4fb0067e9f600,0x40734124604b99e8,2 +np.float64,0x7fd447dc96288fb8,0x40733c1703ab2cce,2 +np.float64,0x3feb2d1e64f65a3d,0xbfb22a781df61c05,2 +np.float64,0xb6c8e6676d91d,0xc0733cc8859a0b91,2 +np.float64,0x3fdc3c2418387848,0xbfd6bec3a3c3cdb5,2 +np.float64,0x3fdecb9ccdbd973a,0xbfd4551c05721a8e,2 +np.float64,0x3feb1100e7762202,0xbfb29db911fe6768,2 +np.float64,0x3fe0444bc2a08898,0xbfd2ce69582e78c1,2 +np.float64,0x7fda403218b48063,0x40733de201d8340c,2 +np.float64,0x3fdc70421238e084,0xbfd68ba4bd48322b,2 +np.float64,0x3fe06e747c60dce9,0xbfd286bcac34a981,2 +np.float64,0x7fc1931d9623263a,0x407336473da54de4,2 +np.float64,0x229914da45323,0xc073485979ff141c,2 +np.float64,0x3fe142f92da285f2,0xbfd1280909992cb6,2 +np.float64,0xf1d02fa9e3a06,0xc0733ad6b19d71a0,2 +np.float64,0x3fb1fe9b0023fd36,0xbff27317d8252c16,2 +np.float64,0x3fa544b9242a8972,0xbff61ac38569bcfc,2 +np.float64,0x3feeb129d4fd6254,0xbf928f23ad20c1ee,2 +np.float64,0xa2510b7f44a22,0xc0733d9bc81ea0a1,2 +np.float64,0x3fca75694d34ead3,0xbfe5e8975b3646c2,2 +np.float64,0x7fece10621b9c20b,0x4073435cc3dd9a1b,2 +np.float64,0x7fe98a57d3b314af,0x4073428239b6a135,2 +np.float64,0x3fe259c62a64b38c,0xbfcee96682a0f355,2 +np.float64,0x3feaaa9b9d755537,0xbfb445779f3359af,2 +np.float64,0xdaadecfdb55be,0xc0733b899338432a,2 +np.float64,0x3fed00eae4fa01d6,0xbfa5dc8d77be5991,2 +np.float64,0x7fcc96c773392d8e,0x407339a8c5cd786e,2 +np.float64,0x3fef7b8b203ef716,0xbf7cff655ecb6424,2 +np.float64,0x7fd4008113a80101,0x40733bfe6552acb7,2 +np.float64,0x7fe99ff035b33fdf,0x407342881753ee2e,2 +np.float64,0x3ee031e87dc07,0xc0734432d736e492,2 +np.float64,0x3fddfe390f3bfc72,0xbfd510f1d9ec3e36,2 +np.float64,0x3fd9ddce74b3bb9d,0xbfd92e2d75a061bb,2 +np.float64,0x7fe5f742edebee85,0x40734176058e3a77,2 +np.float64,0x3fdb04185b360831,0xbfd7f8c63aa5e1c4,2 +np.float64,0xea2b0f43d4562,0xc0733b0fd77c8118,2 +np.float64,0x7fc3f4973527e92d,0x407337293bbb22c4,2 +np.float64,0x3fb9adfb38335bf6,0xbfeff4f3ea85821a,2 +np.float64,0x87fb98750ff73,0xc0733ed6ad83c269,2 +np.float64,0x3fe005721a200ae4,0xbfd33a9f1ebfb0ac,2 +np.float64,0xd9e04fe7b3c0a,0xc0733b901ee257f3,2 +np.float64,0x2c39102658723,0xc07346a4db63bf55,2 +np.float64,0x3f7dc28e003b851c,0xc0011c1d1233d948,2 +np.float64,0x3430fd3868620,0xc073457e24e0b70d,2 +np.float64,0xbff0000000000000,0x7ff8000000000000,2 +np.float64,0x3fd23e45e0247c8c,0xbfe17146bcf87b57,2 +np.float64,0x6599df3ecb33d,0xc07340dd2c41644c,2 +np.float64,0x3fdf074f31be0e9e,0xbfd41f6e9dbb68a5,2 +np.float64,0x7fdd6233f3bac467,0x40733eaa8f674b72,2 +np.float64,0x7fe03e8481607d08,0x40733f5d3df3b087,2 +np.float64,0x3fcc3b79f13876f4,0xbfe501bf3b379b77,2 +np.float64,0xe5d97ae3cbb30,0xc0733b30f47cbd12,2 +np.float64,0x8acbc4a115979,0xc0733eb240a4d2c6,2 +np.float64,0x3fedbdbc48bb7b79,0xbfa0470fd70c4359,2 +np.float64,0x3fde1611103c2c22,0xbfd4fae1fa8e7e5e,2 +np.float64,0x3fe09478bd2128f1,0xbfd246b7e85711dc,2 +np.float64,0x3fd6dfe8f3adbfd2,0xbfdc98ca2f32c1ad,2 +np.float64,0x72ccf274e599f,0xc0734003e5b0da63,2 +np.float64,0xe27c7265c4f8f,0xc0733b4b2d808566,2 +np.float64,0x7fee3161703c62c2,0x407343abe90f5649,2 +np.float64,0xf54fb5c1eaa0,0xc0734e01384fcf78,2 +np.float64,0xcde5924d9bcb3,0xc0733bf4b83c66c2,2 +np.float64,0x3fc46fdbe528dfb8,0xbfe97f55ef5e9683,2 +np.float64,0x7fe513528a2a26a4,0x4073412c69baceca,2 +np.float64,0x3fd29eca4aa53d95,0xbfe128801cd33ed0,2 +np.float64,0x7febb21718b7642d,0x4073431256def857,2 +np.float64,0x3fcab536c0356a6e,0xbfe5c73c59f41578,2 +np.float64,0x7fc7e9f0d82fd3e1,0x4073386b213e5dfe,2 +np.float64,0xb5b121276b624,0xc0733cd33083941c,2 +np.float64,0x7e0dd9bcfc1bc,0xc0733f5d8bf35050,2 +np.float64,0x3fd1c75106238ea2,0xbfe1cd11cccda0f4,2 +np.float64,0x9f060e673e0c2,0xc0733dc03da71909,2 +np.float64,0x7fd915a2f3322b45,0x40733d912af07189,2 +np.float64,0x3fd8cbae4431975d,0xbfda5b02ca661139,2 +np.float64,0x3fde8b411f3d1682,0xbfd48f6f710a53b6,2 +np.float64,0x3fc17a780622f4f0,0xbfebabb10c55255f,2 +np.float64,0x3fde5cbe5f3cb97d,0xbfd4b9e2e0101fb1,2 +np.float64,0x7fd859036530b206,0x40733d5c2252ff81,2 +np.float64,0xb0f5040f61ea1,0xc0733d02292f527b,2 +np.float64,0x3fde5c49ae3cb893,0xbfd4ba4db3ce2cf3,2 +np.float64,0x3fecc4518df988a3,0xbfa7af0bfc98bc65,2 +np.float64,0x3feffee03cbffdc0,0xbf0f3ede6ca7d695,2 +np.float64,0xbc5eac9b78bd6,0xc0733c92fb51c8ae,2 +np.float64,0x3fe2bb4ef765769e,0xbfcdc4f70a65dadc,2 +np.float64,0x5089443ca1129,0xc073427a7d0cde4a,2 +np.float64,0x3fd0d6e29121adc5,0xbfe28e28ece1db86,2 +np.float64,0xbe171e397c2e4,0xc0733c82cede5d02,2 +np.float64,0x4ede27be9dbc6,0xc073429fba1a4af1,2 +np.float64,0x3fe2aff3af655fe7,0xbfcde6b52a8ed3c1,2 +np.float64,0x7fd85ca295b0b944,0x40733d5d2adcccf1,2 +np.float64,0x24919bba49234,0xc07347f6ed704a6f,2 +np.float64,0x7fd74bc1eeae9783,0x40733d0d94a89011,2 +np.float64,0x3fc1cd12cb239a26,0xbfeb6a9c25c2a11d,2 +np.float64,0x3fdafbc0ac35f781,0xbfd8015ccf1f1b51,2 +np.float64,0x3fee01327c3c0265,0xbf9ca1d0d762dc18,2 +np.float64,0x3fe65bd7702cb7af,0xbfc3ee0de5c36b8d,2 +np.float64,0x7349c82ee693a,0xc0733ffc5b6eccf2,2 +np.float64,0x3fdc5906f738b20e,0xbfd6a26288eb5933,2 +np.float64,0x1,0xc07434e6420f4374,2 +np.float64,0x3fb966128a32cc25,0xbff00e0aa7273838,2 +np.float64,0x3fd501ff9a2a03ff,0xbfdef69133482121,2 +np.float64,0x194d4f3c329ab,0xc0734a861b44cfbe,2 +np.float64,0x3fec5d34f8f8ba6a,0xbfaad1b31510e70b,2 +np.float64,0x1635e4c22c6be,0xc0734b6dec650943,2 +np.float64,0x3fead2f8edb5a5f2,0xbfb39dac30a962cf,2 +np.float64,0x3f7dfa4ce03bf49a,0xc00115a112141aa7,2 +np.float64,0x3fef6827223ed04e,0xbf80a42c9edebfe9,2 +np.float64,0xe771f303cee3f,0xc0733b24a6269fe4,2 +np.float64,0x1160ccc622c1b,0xc0734d22604eacb9,2 +np.float64,0x3fc485cd08290b9a,0xbfe970723008c8c9,2 +np.float64,0x7fef99c518bf3389,0x407343fcf9ed202f,2 +np.float64,0x7fd8c1447a318288,0x40733d79a440b44d,2 +np.float64,0xaf219f955e434,0xc0733d149c13f440,2 +np.float64,0xcf45f6239e8bf,0xc0733be8ddda045d,2 +np.float64,0x7599394aeb328,0xc0733fd90fdbb0ea,2 +np.float64,0xc7f6390f8fec7,0xc0733c28bfbc66a3,2 +np.float64,0x3fd39ae96c2735d3,0xbfe0712274a8742b,2 +np.float64,0xa4d6c18f49ad8,0xc0733d805a0528f7,2 +np.float64,0x7fd9ea78d7b3d4f1,0x40733dcb2b74802a,2 +np.float64,0x3fecd251cb39a4a4,0xbfa742ed41d4ae57,2 +np.float64,0x7fed7a07cd7af40f,0x407343813476027e,2 +np.float64,0x3fd328ae7f26515d,0xbfe0c30b56a83c64,2 +np.float64,0x7fc937ff7a326ffe,0x407338c9a45b9140,2 +np.float64,0x3fcf1d31143e3a62,0xbfe3a7f760fbd6a8,2 +np.float64,0x7fb911dcbc3223b8,0x407333ee158cccc7,2 +np.float64,0x3fd352fc83a6a5f9,0xbfe0a47d2f74d283,2 +np.float64,0x7fd310753fa620e9,0x40733ba8fc4300dd,2 +np.float64,0x3febd64b4577ac97,0xbfaefd4a79f95c4b,2 +np.float64,0x6a6961a4d4d2d,0xc073408ae1687943,2 +np.float64,0x3fe4ba73d16974e8,0xbfc8239341b9e457,2 +np.float64,0x3fed8e7cac3b1cf9,0xbfa1a96a0cc5fcdc,2 +np.float64,0x7fd505ec04aa0bd7,0x40733c56f86e3531,2 +np.float64,0x3fdf166e9abe2cdd,0xbfd411e5f8569d70,2 +np.float64,0x7fe1bc6434e378c7,0x40733ff9861bdabb,2 +np.float64,0x3fd3b0b175a76163,0xbfe061ba5703f3c8,2 +np.float64,0x7fed75d7ffbaebaf,0x4073438037ba6f19,2 +np.float64,0x5a9e109cb53c3,0xc07341a8b04819c8,2 +np.float64,0x3fe14786b4e28f0d,0xbfd120b541bb880e,2 +np.float64,0x3fed4948573a9291,0xbfa3b471ff91614b,2 +np.float64,0x66aac5d8cd559,0xc07340ca9b18af46,2 +np.float64,0x3fdb48efd23691e0,0xbfd7b24c5694838b,2 +np.float64,0x7fe6da7d1eadb4f9,0x407341bc7d1fae43,2 +np.float64,0x7feb702cf336e059,0x40734301b96cc3c0,2 +np.float64,0x3fd1e60987a3cc13,0xbfe1b522cfcc3d0e,2 +np.float64,0x3feca57f50794aff,0xbfa89dc90625d39c,2 +np.float64,0x7fdc46dc56b88db8,0x40733e664294a0f9,2 +np.float64,0x8dc8fd811b920,0xc0733e8c5955df06,2 +np.float64,0xf01634abe02c7,0xc0733ae370a76d0c,2 +np.float64,0x3fc6f8d8ab2df1b1,0xbfe7df5093829464,2 +np.float64,0xda3d7597b47af,0xc0733b8d2702727a,2 +np.float64,0x7feefd53227dfaa5,0x407343da3d04db28,2 +np.float64,0x3fe2fbca3525f794,0xbfcd06e134417c08,2 +np.float64,0x7fd36d3ce226da79,0x40733bca7c322df1,2 +np.float64,0x7fec37e00b786fbf,0x4073433397b48a5b,2 +np.float64,0x3fbf133f163e267e,0xbfed4e72f1362a77,2 +np.float64,0x3fc11efbb9223df7,0xbfebf53002a561fe,2 +np.float64,0x3fc89c0e5431381d,0xbfe6ea562364bf81,2 +np.float64,0x3f9cd45da839a8bb,0xbff8ceb14669ee4b,2 +np.float64,0x23dc8fa647b93,0xc0734819aaa9b0ee,2 +np.float64,0x3fe829110d305222,0xbfbf3e60c45e2399,2 +np.float64,0x7fed8144e57b0289,0x40734382e917a02a,2 +np.float64,0x7fe033fbf7a067f7,0x40733f58bb00b20f,2 +np.float64,0xe3807f45c7010,0xc0733b43379415d1,2 +np.float64,0x3fd708fb342e11f6,0xbfdc670ef9793782,2 +np.float64,0x3fe88c924b311925,0xbfbd78210d9e7164,2 +np.float64,0x3fe0a2a7c7614550,0xbfd22efaf0472c4a,2 +np.float64,0x7fe3a37501a746e9,0x407340aecaeade41,2 +np.float64,0x3fd05077ec20a0f0,0xbfe2fedbf07a5302,2 +np.float64,0x7fd33bf61da677eb,0x40733bb8c58912aa,2 +np.float64,0x3feb29bdae76537b,0xbfb2384a8f61b5f9,2 +np.float64,0x3fec0fc14ff81f83,0xbfad3423e7ade174,2 +np.float64,0x3fd0f8b1a1a1f163,0xbfe2725dd4ccea8b,2 +np.float64,0x3fe382d26a6705a5,0xbfcb80dba4218bdf,2 +np.float64,0x3fa873f2cc30e7e6,0xbff522911cb34279,2 +np.float64,0x7fed7fd7377affad,0x4073438292f6829b,2 +np.float64,0x3feeacd8067d59b0,0xbf92cdbeda94b35e,2 +np.float64,0x7fe464d62228c9ab,0x407340f1eee19aa9,2 +np.float64,0xe997648bd32ed,0xc0733b143aa0fad3,2 +np.float64,0x7fea4869f13490d3,0x407342b5333b54f7,2 +np.float64,0x935b871926b71,0xc0733e47c6683319,2 +np.float64,0x28a9d0c05155,0xc0735a7e3532af83,2 +np.float64,0x79026548f204d,0xc0733fa6339ffa2f,2 +np.float64,0x3fdb1daaabb63b55,0xbfd7de839c240ace,2 +np.float64,0x3fc0db73b421b6e7,0xbfec2c6e36c4f416,2 +np.float64,0xb8b50ac1716b,0xc0734ff9fc60ebce,2 +np.float64,0x7fdf13e0c6be27c1,0x40733f0e44f69437,2 +np.float64,0x3fcd0cb97b3a1973,0xbfe49c34ff531273,2 +np.float64,0x3fcbac034b375807,0xbfe54913d73f180d,2 +np.float64,0x3fe091d2a2e123a5,0xbfd24b290a9218de,2 +np.float64,0xede43627dbc87,0xc0733af3c7c7f716,2 +np.float64,0x7fc037e7ed206fcf,0x407335b85fb0fedb,2 +np.float64,0x3fce7ae4c63cf5ca,0xbfe3f1350fe03f28,2 +np.float64,0x7fcdd862263bb0c3,0x407339f5458bb20e,2 +np.float64,0x4d7adf709af5d,0xc07342bf4edfadb2,2 +np.float64,0xdc6c03f3b8d81,0xc0733b7b74d6a635,2 +np.float64,0x3fe72ae0a4ee55c1,0xbfc1f4665608b21f,2 +np.float64,0xcd62f19d9ac5e,0xc0733bf92235e4d8,2 +np.float64,0xe3a7b8fdc74f7,0xc0733b4204f8e166,2 +np.float64,0x3fdafd35adb5fa6b,0xbfd7ffdca0753b36,2 +np.float64,0x3fa023e8702047d1,0xbff8059150ea1464,2 +np.float64,0x99ff336933fe7,0xc0733df961197517,2 +np.float64,0x7feeb365b9bd66ca,0x407343c995864091,2 +np.float64,0x7fe449b49f689368,0x407340e8aa3369e3,2 +np.float64,0x7faf5843043eb085,0x407330aa700136ca,2 +np.float64,0x3fd47b2922a8f652,0xbfdfab3de86f09ee,2 +np.float64,0x7fd9fc3248b3f864,0x40733dcfea6f9b3e,2 +np.float64,0xe20b0d8dc4162,0xc0733b4ea8fe7b3f,2 +np.float64,0x7feff8e0e23ff1c1,0x40734411c490ed70,2 +np.float64,0x7fa58382d02b0705,0x40732e0cf28e14fe,2 +np.float64,0xb8ad9a1b715b4,0xc0733cb630b8f2d4,2 +np.float64,0xe90abcf1d2158,0xc0733b186b04eeee,2 +np.float64,0x7fd6aa6f32ad54dd,0x40733cdccc636604,2 +np.float64,0x3fd8f84eedb1f09e,0xbfda292909a5298a,2 +np.float64,0x7fecd6b1d9f9ad63,0x4073435a472b05b5,2 +np.float64,0x3fd9f47604b3e8ec,0xbfd915e028cbf4a6,2 +np.float64,0x3fd20d9398241b27,0xbfe19691363dd508,2 +np.float64,0x3fe5ed09bbabda13,0xbfc5043dfc9c8081,2 +np.float64,0x7fbe5265363ca4c9,0x407335406f8e4fac,2 +np.float64,0xac2878af5850f,0xc0733d3311be9786,2 +np.float64,0xac2074555840f,0xc0733d3364970018,2 +np.float64,0x3fcd49b96b3a9373,0xbfe47f24c8181d9c,2 +np.float64,0x3fd10caca6a21959,0xbfe2620ae5594f9a,2 +np.float64,0xec5b87e9d8b71,0xc0733aff499e72ca,2 +np.float64,0x9d5e9fad3abd4,0xc0733dd2d70eeb4a,2 +np.float64,0x7fe3d3a24227a744,0x407340bfc2072fdb,2 +np.float64,0x3fc5f7a77c2bef4f,0xbfe87e69d502d784,2 +np.float64,0x33161a66662c4,0xc07345a436308244,2 +np.float64,0xa27acdc744f5a,0xc0733d99feb3d8ea,2 +np.float64,0x3fe2d9301565b260,0xbfcd6c914e204437,2 +np.float64,0x7fd5d111e12ba223,0x40733c98e14a6fd0,2 +np.float64,0x6c3387bed8672,0xc073406d3648171a,2 +np.float64,0x24d89fe849b15,0xc07347e97bec008c,2 +np.float64,0x3fefd763677faec7,0xbf61ae69caa9cad9,2 +np.float64,0x7fe0a4684ba148d0,0x40733f884d32c464,2 +np.float64,0x3fd5c3c939ab8792,0xbfddfaaefc1c7fca,2 +np.float64,0x3fec9b87a6b9370f,0xbfa8eb34efcc6b9b,2 +np.float64,0x3feb062431f60c48,0xbfb2ca6036698877,2 +np.float64,0x3fef97f6633f2fed,0xbf76bc742860a340,2 +np.float64,0x74477490e88ef,0xc0733fed220986bc,2 +np.float64,0x3fe4bea67ce97d4d,0xbfc818525292b0f6,2 +np.float64,0x3fc6add3a92d5ba7,0xbfe80cfdc9a90bda,2 +np.float64,0x847c9ce308f94,0xc0733f05026f5965,2 +np.float64,0x7fea53fd2eb4a7f9,0x407342b841fc4723,2 +np.float64,0x3fc55a16fc2ab42e,0xbfe8e3849130da34,2 +np.float64,0x3fbdf7d07c3befa1,0xbfedcf84b9c6c161,2 +np.float64,0x3fe5fb25aa6bf64b,0xbfc4e083ff96b116,2 +np.float64,0x61c776a8c38ef,0xc0734121611d84d7,2 +np.float64,0x3fec413164f88263,0xbfabadbd05131546,2 +np.float64,0x9bf06fe137e0e,0xc0733de315469ee0,2 +np.float64,0x2075eefc40ebf,0xc07348cae84de924,2 +np.float64,0x3fdd42e0143a85c0,0xbfd5c0b6f60b3cea,2 +np.float64,0xdbb1ab45b7636,0xc0733b8157329daf,2 +np.float64,0x3feac6d56bf58dab,0xbfb3d00771b28621,2 +np.float64,0x7fb2dc825025b904,0x407331f3e950751a,2 +np.float64,0x3fecea6efd79d4de,0xbfa689309cc0e3fe,2 +np.float64,0x3fd83abec7b0757e,0xbfdaff5c674a9c59,2 +np.float64,0x3fd396f7c0272df0,0xbfe073ee75c414ba,2 +np.float64,0x3fe10036c162006e,0xbfd1945a38342ae1,2 +np.float64,0x3fd5bbded52b77be,0xbfde04cca40d4156,2 +np.float64,0x3fe870945ab0e129,0xbfbdf72f0e6206fa,2 +np.float64,0x3fef72fddcbee5fc,0xbf7ee2dba88b1bad,2 +np.float64,0x4e111aa09c224,0xc07342b1e2b29643,2 +np.float64,0x3fd926d8b5b24db1,0xbfd9f58b78d6b061,2 +np.float64,0x3fc55679172aacf2,0xbfe8e5df687842e2,2 +np.float64,0x7f5f1749803e2e92,0x40731886e16cfc4d,2 +np.float64,0x7fea082b53b41056,0x407342a42227700e,2 +np.float64,0x3fece1d1d039c3a4,0xbfa6cb780988a469,2 +np.float64,0x3b2721d8764e5,0xc073449f6a5a4832,2 +np.float64,0x365cb7006cba,0xc0735879ba5f0b6e,2 +np.float64,0x7ff4000000000000,0x7ffc000000000000,2 +np.float64,0x7fe606ce92ac0d9c,0x4073417aeebe97e8,2 +np.float64,0x3fe237b544a46f6b,0xbfcf50f8f76d7df9,2 +np.float64,0x3fe7265e5eee4cbd,0xbfc1ff39089ec8d0,2 +np.float64,0x7fe2bb3c5ea57678,0x4073405aaad81cf2,2 +np.float64,0x3fd811df84b023bf,0xbfdb2e670ea8d8de,2 +np.float64,0x3f6a0efd00341dfa,0xc003fac1ae831241,2 +np.float64,0x3fd0d214afa1a429,0xbfe2922080a91c72,2 +np.float64,0x3feca6a350b94d47,0xbfa894eea3a96809,2 +np.float64,0x7fe23e5c76247cb8,0x4073402bbaaf71c7,2 +np.float64,0x3fe739a1fdae7344,0xbfc1d109f66efb5d,2 +np.float64,0x3fdf4b8e283e971c,0xbfd3e28f46169cc5,2 +np.float64,0x38f2535271e4b,0xc07344e3085219fa,2 +np.float64,0x7fd263a0f9a4c741,0x40733b68d945dae0,2 +np.float64,0x7fdd941863bb2830,0x40733eb651e3dca9,2 +np.float64,0xace7279159ce5,0xc0733d2b63b5947e,2 +np.float64,0x7fe34670b2268ce0,0x4073408d92770cb5,2 +np.float64,0x7fd11fa6dfa23f4d,0x40733aea02e76ea3,2 +np.float64,0x3fe6d9cbca6db398,0xbfc2b84b5c8c7eab,2 +np.float64,0x3fd69a0274ad3405,0xbfdcee3c7e52c463,2 +np.float64,0x3feb5af671f6b5ed,0xbfb16f88d739477f,2 +np.float64,0x3feea400163d4800,0xbf934e071c64fd0b,2 +np.float64,0x3fefd6bcf17fad7a,0xbf61f711c392b119,2 +np.float64,0x3fe148d43da291a8,0xbfd11e9cd3f91cd3,2 +np.float64,0x7fedf1308b7be260,0x4073439d135656da,2 +np.float64,0x3fe614c99c6c2993,0xbfc49fd1984dfd6d,2 +np.float64,0xd6e8d4e5add1b,0xc0733ba88256026e,2 +np.float64,0xfff0000000000000,0x7ff8000000000000,2 +np.float64,0x3fb530b5562a616b,0xbff1504bcc5c8f73,2 +np.float64,0xb7da68396fb4d,0xc0733cbe2790f52e,2 +np.float64,0x7fad78e26c3af1c4,0x4073303cdbfb0a15,2 +np.float64,0x7fee5698447cad30,0x407343b474573a8b,2 +np.float64,0x3fd488325c291065,0xbfdf999296d901e7,2 +np.float64,0x2669283a4cd26,0xc073479f823109a4,2 +np.float64,0x7fef3b090afe7611,0x407343e805a3b264,2 +np.float64,0x7fe8b96ae0f172d5,0x4073424874a342ab,2 +np.float64,0x7fef409f56fe813e,0x407343e943c3cd44,2 +np.float64,0x3fed28073dfa500e,0xbfa4b17e4cd31a3a,2 +np.float64,0x7f87ecc4802fd988,0x40732527e027b24b,2 +np.float64,0x3fdda24da0bb449b,0xbfd566a43ac035af,2 +np.float64,0x179fc9e62f3fa,0xc0734b0028c80fc1,2 +np.float64,0x3fef85b0927f0b61,0xbf7ac27565d5ab4f,2 +np.float64,0x5631501aac62b,0xc0734201be12c5d4,2 +np.float64,0x3fd782e424af05c8,0xbfdbd57544f8a7c3,2 +np.float64,0x3fe603a9a6ac0753,0xbfc4caff04dc3caf,2 +np.float64,0x7fbd5225163aa449,0x40733504b88f0a56,2 +np.float64,0x3fecd27506b9a4ea,0xbfa741dd70e6b08c,2 +np.float64,0x9c99603b3932c,0xc0733ddb922dc5db,2 +np.float64,0x3fbeb57f1a3d6afe,0xbfed789ff217aa08,2 +np.float64,0x3fef9c0f85bf381f,0xbf75d5c3d6cb281a,2 +np.float64,0x3fde4afb613c95f7,0xbfd4ca2a231c9005,2 +np.float64,0x396233d472c47,0xc07344d56ee70631,2 +np.float64,0x3fb31ea1c6263d44,0xbff207356152138d,2 +np.float64,0x3fe50bdf78aa17bf,0xbfc74ae0cbffb735,2 +np.float64,0xef74c701dee99,0xc0733ae81e4bb443,2 +np.float64,0x9a3e13a1347c3,0xc0733df68b60afc7,2 +np.float64,0x33ba4f886774b,0xc073458e03f0c13e,2 +np.float64,0x3fe8ba0e9931741d,0xbfbcaadf974e8f64,2 +np.float64,0x3fe090a4cd61214a,0xbfd24d236cf365d6,2 +np.float64,0x7fd87d992930fb31,0x40733d668b73b820,2 +np.float64,0x3fe6422b296c8456,0xbfc42e070b695d01,2 +np.float64,0x3febe9334677d267,0xbfae667864606cfe,2 +np.float64,0x771a3ce4ee348,0xc0733fc274d12c97,2 +np.float64,0x3fe0413542e0826b,0xbfd2d3b08fb5b8a6,2 +np.float64,0x3fd00870ea2010e2,0xbfe33cc04cbd42e0,2 +np.float64,0x3fe74fb817ae9f70,0xbfc19c45dbf919e1,2 +np.float64,0x40382fa08071,0xc07357514ced5577,2 +np.float64,0xa14968474292d,0xc0733da71a990f3a,2 +np.float64,0x5487c740a90fa,0xc0734224622d5801,2 +np.float64,0x3fed7d8d14fafb1a,0xbfa228f7ecc2ac03,2 +np.float64,0x3fe39bb485e73769,0xbfcb3a235a722960,2 +np.float64,0x3fd01090b2202121,0xbfe335b752589a22,2 +np.float64,0x3fd21a3e7da4347d,0xbfe18cd435a7c582,2 +np.float64,0x3fe7fa855a2ff50b,0xbfc00ab0665709fe,2 +np.float64,0x3fedc0d4577b81a9,0xbfa02fef3ff553fc,2 +np.float64,0x3fe99d4906333a92,0xbfb8bf18220e5e8e,2 +np.float64,0x3fd944ee3c3289dc,0xbfd9d46071675e73,2 +np.float64,0x3fe3ed8d52e7db1b,0xbfca53f8d4aef484,2 +np.float64,0x7fe748623a6e90c3,0x407341dd97c9dd79,2 +np.float64,0x3fea1b4b98343697,0xbfb6a1560a56927f,2 +np.float64,0xe1215715c242b,0xc0733b55dbf1f0a8,2 +np.float64,0x3fd0d5bccca1ab7a,0xbfe28f1b66d7a470,2 +np.float64,0x881a962710353,0xc0733ed51848a30d,2 +np.float64,0x3fcf022afe3e0456,0xbfe3b40eabf24501,2 +np.float64,0x3fdf1ac6bbbe358d,0xbfd40e03e888288d,2 +np.float64,0x3fa51a5eac2a34bd,0xbff628a7c34d51b3,2 +np.float64,0x3fdbaf408d375e81,0xbfd74ad39d97c92a,2 +np.float64,0x3fcd2418ea3a4832,0xbfe4910b009d8b11,2 +np.float64,0x3fc7b3062a2f660c,0xbfe7706dc47993e1,2 +np.float64,0x7fb8232218304643,0x407333aaa7041a9f,2 +np.float64,0x7fd5f186362be30b,0x40733ca32fdf9cc6,2 +np.float64,0x3fe57ef1d6aafde4,0xbfc61e23d00210c7,2 +np.float64,0x7c6830baf8d07,0xc0733f74f19e9dad,2 +np.float64,0xcacbfd5595980,0xc0733c0fb49edca7,2 +np.float64,0x3fdfdeac873fbd59,0xbfd36114c56bed03,2 +np.float64,0x3fd31f0889263e11,0xbfe0ca0cc1250169,2 +np.float64,0x3fe839fbe47073f8,0xbfbef0a2abc3d63f,2 +np.float64,0x3fc36af57e26d5eb,0xbfea3553f38770b7,2 +np.float64,0x3fe73dbc44ee7b79,0xbfc1c738f8fa6b3d,2 +np.float64,0x3fd3760e4da6ec1d,0xbfe08b5b609d11e5,2 +np.float64,0x3fee1cfa297c39f4,0xbf9b06d081bc9d5b,2 +np.float64,0xdfb01561bf61,0xc0734ea55e559888,2 +np.float64,0x687bd01cd0f7b,0xc07340ab67fe1816,2 +np.float64,0x3fefc88f4cbf911f,0xbf6828c359cf19dc,2 +np.float64,0x8ad34adb15a6a,0xc0733eb1e03811e5,2 +np.float64,0x3fe2b49c12e56938,0xbfcdd8dbdbc0ce59,2 +np.float64,0x6e05037adc0a1,0xc073404f91261635,2 +np.float64,0x3fe2fd737fe5fae7,0xbfcd020407ef4d78,2 +np.float64,0x3fd0f3c0dc21e782,0xbfe2766a1ab02eae,2 +np.float64,0x28564d9850acb,0xc073474875f87c5e,2 +np.float64,0x3fe4758015a8eb00,0xbfc8ddb45134a1bd,2 +np.float64,0x7fe7f19306efe325,0x4073420f626141a7,2 +np.float64,0x7fd27f34c0a4fe69,0x40733b733d2a5b50,2 +np.float64,0x92c2366325847,0xc0733e4f04f8195a,2 +np.float64,0x3fc21f8441243f09,0xbfeb2ad23bc1ab0b,2 +np.float64,0x3fc721d3e42e43a8,0xbfe7c69bb47b40c2,2 +np.float64,0x3fe2f11a1625e234,0xbfcd26363b9c36c3,2 +np.float64,0x3fdcb585acb96b0b,0xbfd648446237cb55,2 +np.float64,0x3fd4060bf2280c18,0xbfe025fd4c8a658b,2 +np.float64,0x7fb8ae2750315c4e,0x407333d23b025d08,2 +np.float64,0x3fe3a03119a74062,0xbfcb2d6c91b38552,2 +np.float64,0x7fdd2af92bba55f1,0x40733e9d737e16e6,2 +np.float64,0x3fe50b05862a160b,0xbfc74d20815fe36b,2 +np.float64,0x164409f82c882,0xc0734b6980e19c03,2 +np.float64,0x3fe4093712a8126e,0xbfca070367fda5e3,2 +np.float64,0xae3049935c609,0xc0733d1e3608797b,2 +np.float64,0x3fd71df4b4ae3be9,0xbfdc4dcb7637600d,2 +np.float64,0x7fca01e8023403cf,0x407339006c521c49,2 +np.float64,0x3fb0c5c43e218b88,0xbff2f03211c63f25,2 +np.float64,0x3fee757af83ceaf6,0xbf95f33a6e56b454,2 +np.float64,0x3f865f1f402cbe3f,0xbfff62d9c9072bd7,2 +np.float64,0x89864e95130ca,0xc0733ec29f1e32c6,2 +np.float64,0x3fe51482bcea2905,0xbfc73414ddc8f1b7,2 +np.float64,0x7fd802f8fa3005f1,0x40733d43684e460a,2 +np.float64,0x3fbeb86ca63d70d9,0xbfed774ccca9b8f5,2 +np.float64,0x3fb355dcc826abba,0xbff1f33f9339e7a3,2 +np.float64,0x3fe506c61eaa0d8c,0xbfc7585a3f7565a6,2 +np.float64,0x7fe393f25ba727e4,0x407340a94bcea73b,2 +np.float64,0xf66f532decdeb,0xc0733ab5041feb0f,2 +np.float64,0x3fe26e872be4dd0e,0xbfceaaab466f32e0,2 +np.float64,0x3fefd9e290bfb3c5,0xbf60977d24496295,2 +np.float64,0x7fe19c5f692338be,0x40733fecef53ad95,2 +np.float64,0x3fe80365ab3006cb,0xbfbfec4090ef76ec,2 +np.float64,0x3fe88ab39eb11567,0xbfbd8099388d054d,2 +np.float64,0x3fe68fb09fad1f61,0xbfc36db9de38c2c0,2 +np.float64,0x3fe9051883b20a31,0xbfbb5b75b8cb8f24,2 +np.float64,0x3fd4708683a8e10d,0xbfdfb9b085dd8a83,2 +np.float64,0x3fe00ac11a601582,0xbfd3316af3e43500,2 +np.float64,0xd16af30ba2d5f,0xc0733bd68e8252f9,2 +np.float64,0x3fb97d654632facb,0xbff007ac1257f575,2 +np.float64,0x7fd637c10fac6f81,0x40733cb949d76546,2 +np.float64,0x7fed2cab6dba5956,0x4073436edfc3764e,2 +np.float64,0x3fed04afbbba095f,0xbfa5bfaa5074b7f4,2 +np.float64,0x0,0xfff0000000000000,2 +np.float64,0x389a1dc671345,0xc07344edd4206338,2 +np.float64,0x3fbc9ba25a393745,0xbfee74c34f49b921,2 +np.float64,0x3feee749947dce93,0xbf8f032d9cf6b5ae,2 +np.float64,0xedc4cf89db89a,0xc0733af4b2a57920,2 +np.float64,0x3fe41629eba82c54,0xbfc9e321faf79e1c,2 +np.float64,0x3feb0bcbf7b61798,0xbfb2b31e5d952869,2 +np.float64,0xad60654b5ac0d,0xc0733d26860df676,2 +np.float64,0x3fe154e1ff22a9c4,0xbfd10b416e58c867,2 +np.float64,0x7fb20e9c8a241d38,0x407331a66453b8bc,2 +np.float64,0x7fcbbaaf7d37755e,0x4073397274f28008,2 +np.float64,0x187d0fbc30fa3,0xc0734ac03cc98cc9,2 +np.float64,0x7fd153afeaa2a75f,0x40733aff00b4311d,2 +np.float64,0x3fe05310a5e0a621,0xbfd2b5386aeecaac,2 +np.float64,0x7fea863b2b750c75,0x407342c57807f700,2 +np.float64,0x3fed5f0c633abe19,0xbfa30f6cfbc4bf94,2 +np.float64,0xf227c8b3e44f9,0xc0733ad42daaec9f,2 +np.float64,0x3fe956524772aca5,0xbfb9f4cabed7081d,2 +np.float64,0xefd11af7dfa24,0xc0733ae570ed2552,2 +np.float64,0x1690fff02d221,0xc0734b51a56c2980,2 +np.float64,0x7fd2e547a825ca8e,0x40733b992d6d9635,2 diff --git a/numpy/core/tests/data/umath-validation-set-log1p.csv b/numpy/core/tests/data/umath-validation-set-log1p.csv new file mode 100644 index 000000000000..6e4f88b3446c --- /dev/null +++ b/numpy/core/tests/data/umath-validation-set-log1p.csv @@ -0,0 +1,1429 @@ +dtype,input,output,ulperrortol +np.float32,0x3e10aca8,0x3e075347,2 +np.float32,0x3f776e66,0x3f2d2003,2 +np.float32,0xbf34e8ce,0xbf9cfd5c,2 +np.float32,0xbf0260ee,0xbf363f69,2 +np.float32,0x3ed285e8,0x3eb05870,2 +np.float32,0x262b88,0x262b88,2 +np.float32,0x3eeffd6c,0x3ec4cfdb,2 +np.float32,0x3ee86808,0x3ebf9f54,2 +np.float32,0x3f36eba8,0x3f0a0524,2 +np.float32,0xbf1c047a,0xbf70afc7,2 +np.float32,0x3ead2916,0x3e952902,2 +np.float32,0x61c9c9,0x61c9c9,2 +np.float32,0xff7fffff,0xffc00000,2 +np.float32,0x7f64ee52,0x42b138e0,2 +np.float32,0x7ed00b1e,0x42afa4ff,2 +np.float32,0x3db53340,0x3dada0b2,2 +np.float32,0x3e6b0a4a,0x3e5397a4,2 +np.float32,0x7ed5d64f,0x42afb310,2 +np.float32,0xbf12bc5f,0xbf59f5ee,2 +np.float32,0xbda12710,0xbda7d8b5,2 +np.float32,0xbe2e89d8,0xbe3f5a9f,2 +np.float32,0x3f5bee75,0x3f1ebea4,2 +np.float32,0x9317a,0x9317a,2 +np.float32,0x7ee00130,0x42afcad8,2 +np.float32,0x7ef0d16d,0x42afefe7,2 +np.float32,0xbec7463a,0xbefc6a44,2 +np.float32,0xbf760ecc,0xc04fe59c,2 +np.float32,0xbecacb3c,0xbf011ae3,2 +np.float32,0x3ead92be,0x3e9577f0,2 +np.float32,0xbf41510d,0xbfb41b3a,2 +np.float32,0x7f71d489,0x42b154f1,2 +np.float32,0x8023bcd5,0x8023bcd5,2 +np.float32,0x801d33d8,0x801d33d8,2 +np.float32,0x3f3f545d,0x3f0ee0d4,2 +np.float32,0xbf700682,0xc0318c25,2 +np.float32,0xbe54e990,0xbe6eb0a3,2 +np.float32,0x7f0289bf,0x42b01941,2 +np.float32,0xbd61ac90,0xbd682113,2 +np.float32,0xbf2ff310,0xbf94cd6f,2 +np.float32,0x7f10064a,0x42b04b98,2 +np.float32,0x804d0d6d,0x804d0d6d,2 +np.float32,0x80317b0a,0x80317b0a,2 +np.float32,0xbddfef18,0xbded2640,2 +np.float32,0x3f00c9ab,0x3ed0a5bd,2 +np.float32,0x7f04b905,0x42b021c1,2 +np.float32,0x7fc00000,0x7fc00000,2 +np.float32,0x6524c4,0x6524c4,2 +np.float32,0x3da08ae0,0x3d9a8f88,2 +np.float32,0x293ea9,0x293ea9,2 +np.float32,0x71499e,0x71499e,2 +np.float32,0xbf14f54d,0xbf5f38a5,2 +np.float32,0x806e60f5,0x806e60f5,2 +np.float32,0x3f5f34bb,0x3f207fff,2 +np.float32,0x80513427,0x80513427,2 +np.float32,0x7f379670,0x42b0c7dc,2 +np.float32,0x3efba888,0x3eccb20b,2 +np.float32,0x3eeadd1b,0x3ec14f4b,2 +np.float32,0x7ec5a27f,0x42af8ab8,2 +np.float32,0x3f2afe4e,0x3f02f7a2,2 +np.float32,0x5591c8,0x5591c8,2 +np.float32,0x3dbb7240,0x3db35bab,2 +np.float32,0x805b911b,0x805b911b,2 +np.float32,0x800000,0x800000,2 +np.float32,0x7e784c04,0x42ae9cab,2 +np.float32,0x7ebaae14,0x42af6d86,2 +np.float32,0xbec84f7a,0xbefe1d42,2 +np.float32,0x7cea8281,0x42aa56bf,2 +np.float32,0xbf542cf6,0xbfe1eb1b,2 +np.float32,0xbf6bfb13,0xc0231a5b,2 +np.float32,0x7d6eeaef,0x42abc32c,2 +np.float32,0xbf062f6b,0xbf3e2000,2 +np.float32,0x8073d8e9,0x8073d8e9,2 +np.float32,0xbea4db14,0xbec6f485,2 +np.float32,0x7d7e8d62,0x42abe3a0,2 +np.float32,0x7e8fc34e,0x42aee7c6,2 +np.float32,0x7dcbb0c3,0x42acd464,2 +np.float32,0x7e123c,0x7e123c,2 +np.float32,0x3d77af62,0x3d707c34,2 +np.float32,0x498cc8,0x498cc8,2 +np.float32,0x7f4e2206,0x42b1032a,2 +np.float32,0x3f734e0a,0x3f2b04a1,2 +np.float32,0x8053a9d0,0x8053a9d0,2 +np.float32,0xbe8a67e0,0xbea15be9,2 +np.float32,0xbf78e0ea,0xc065409e,2 +np.float32,0x352bdd,0x352bdd,2 +np.float32,0x3ee42be7,0x3ebcb38a,2 +np.float32,0x7f482d10,0x42b0f427,2 +np.float32,0xbf23155e,0xbf81b993,2 +np.float32,0x594920,0x594920,2 +np.float32,0x63f53f,0x63f53f,2 +np.float32,0x363592,0x363592,2 +np.float32,0x7dafbb78,0x42ac88cc,2 +np.float32,0x7f69516c,0x42b14298,2 +np.float32,0x3e1d5be2,0x3e126131,2 +np.float32,0x410c23,0x410c23,2 +np.float32,0x7ec9563c,0x42af9439,2 +np.float32,0xbedd3a0e,0xbf10d705,2 +np.float32,0x7f7c4f1f,0x42b16aa8,2 +np.float32,0xbe99b34e,0xbeb6c2d3,2 +np.float32,0x6cdc84,0x6cdc84,2 +np.float32,0x5b3bbe,0x5b3bbe,2 +np.float32,0x252178,0x252178,2 +np.float32,0x7d531865,0x42ab83c8,2 +np.float32,0xbf565b44,0xbfe873bf,2 +np.float32,0x5977ce,0x5977ce,2 +np.float32,0x588a58,0x588a58,2 +np.float32,0x3eae7054,0x3e961d51,2 +np.float32,0x725049,0x725049,2 +np.float32,0x7f2b9386,0x42b0a538,2 +np.float32,0xbe674714,0xbe831245,2 +np.float32,0x8044f0d8,0x8044f0d8,2 +np.float32,0x800a3c21,0x800a3c21,2 +np.float32,0x807b275b,0x807b275b,2 +np.float32,0xbf2463b6,0xbf83896e,2 +np.float32,0x801cca42,0x801cca42,2 +np.float32,0xbf28f2d0,0xbf8a121a,2 +np.float32,0x3f4168c2,0x3f1010ce,2 +np.float32,0x6f91a1,0x6f91a1,2 +np.float32,0xbf2b9eeb,0xbf8e0fc5,2 +np.float32,0xbea4c858,0xbec6d8e4,2 +np.float32,0xbf7abba0,0xc0788e88,2 +np.float32,0x802f18f7,0x802f18f7,2 +np.float32,0xbf7f6c75,0xc0c3145c,2 +np.float32,0xbe988210,0xbeb50f5e,2 +np.float32,0xbf219b7e,0xbf7f6a3b,2 +np.float32,0x7f800000,0x7f800000,2 +np.float32,0x7f7fffff,0x42b17218,2 +np.float32,0xbdca8d90,0xbdd5487e,2 +np.float32,0xbef683b0,0xbf2821b0,2 +np.float32,0x8043e648,0x8043e648,2 +np.float32,0xbf4319a4,0xbfb7cd1b,2 +np.float32,0x62c2b2,0x62c2b2,2 +np.float32,0xbf479ccd,0xbfc1a7b1,2 +np.float32,0x806c8a32,0x806c8a32,2 +np.float32,0x7f004447,0x42b01045,2 +np.float32,0x3f737d36,0x3f2b1ccf,2 +np.float32,0x3ee71f24,0x3ebebced,2 +np.float32,0x3ea0b6b4,0x3e8bc606,2 +np.float32,0x358fd7,0x358fd7,2 +np.float32,0xbe69780c,0xbe847d17,2 +np.float32,0x7f6bed18,0x42b14849,2 +np.float32,0xbf6a5113,0xc01dfe1d,2 +np.float32,0xbf255693,0xbf84de88,2 +np.float32,0x7f34acac,0x42b0bfac,2 +np.float32,0xbe8a3b6a,0xbea11efe,2 +np.float32,0x3f470d84,0x3f1342ab,2 +np.float32,0xbf2cbde3,0xbf8fc602,2 +np.float32,0x47c103,0x47c103,2 +np.float32,0xe3c94,0xe3c94,2 +np.float32,0xbec07afa,0xbef1693a,2 +np.float32,0x6a9cfe,0x6a9cfe,2 +np.float32,0xbe4339e0,0xbe5899da,2 +np.float32,0x7ea9bf1e,0x42af3cd6,2 +np.float32,0x3f6378b4,0x3f22c4c4,2 +np.float32,0xbd989ff0,0xbd9e9c77,2 +np.float32,0xbe6f2f50,0xbe88343d,2 +np.float32,0x3f7f2ac5,0x3f310764,2 +np.float32,0x3f256704,0x3eff2fb2,2 +np.float32,0x80786aca,0x80786aca,2 +np.float32,0x65d02f,0x65d02f,2 +np.float32,0x50d1c3,0x50d1c3,2 +np.float32,0x3f4a9d76,0x3f1541b4,2 +np.float32,0x802cf491,0x802cf491,2 +np.float32,0x3e935cec,0x3e81829b,2 +np.float32,0x3e2ad478,0x3e1dfd81,2 +np.float32,0xbf107cbd,0xbf54bef2,2 +np.float32,0xbf58c02e,0xbff007fe,2 +np.float32,0x80090808,0x80090808,2 +np.float32,0x805d1f66,0x805d1f66,2 +np.float32,0x6aec95,0x6aec95,2 +np.float32,0xbee3fc6e,0xbf16dc73,2 +np.float32,0x7f63314b,0x42b134f9,2 +np.float32,0x550443,0x550443,2 +np.float32,0xbefa8174,0xbf2c026e,2 +np.float32,0x3f7fb380,0x3f314bd5,2 +np.float32,0x80171f2c,0x80171f2c,2 +np.float32,0x3f2f56ae,0x3f058f2d,2 +np.float32,0x3eacaecb,0x3e94cd97,2 +np.float32,0xbe0c4f0c,0xbe16e69d,2 +np.float32,0x3f48e4cb,0x3f144b42,2 +np.float32,0x7f03efe2,0x42b01eb7,2 +np.float32,0xbf1019ac,0xbf53dbe9,2 +np.float32,0x3e958524,0x3e832eb5,2 +np.float32,0xbf1b23c6,0xbf6e72f2,2 +np.float32,0x12c554,0x12c554,2 +np.float32,0x7dee588c,0x42ad24d6,2 +np.float32,0xbe8c216c,0xbea3ba70,2 +np.float32,0x804553cb,0x804553cb,2 +np.float32,0xbe446324,0xbe5a0966,2 +np.float32,0xbef7150a,0xbf28adff,2 +np.float32,0xbf087282,0xbf42ec6e,2 +np.float32,0x3eeef15c,0x3ec41937,2 +np.float32,0x61bbd2,0x61bbd2,2 +np.float32,0x3e51b28d,0x3e3ec538,2 +np.float32,0x57e869,0x57e869,2 +np.float32,0x7e5e7711,0x42ae646c,2 +np.float32,0x8050b173,0x8050b173,2 +np.float32,0xbf63c90c,0xc00d2438,2 +np.float32,0xbeba774c,0xbee7dcf8,2 +np.float32,0x8016faac,0x8016faac,2 +np.float32,0xbe8b448c,0xbea28aaf,2 +np.float32,0x3e8cd448,0x3e78d29e,2 +np.float32,0x80484e02,0x80484e02,2 +np.float32,0x3f63ba68,0x3f22e78c,2 +np.float32,0x2e87bb,0x2e87bb,2 +np.float32,0x230496,0x230496,2 +np.float32,0x1327b2,0x1327b2,2 +np.float32,0xbf046c56,0xbf3a72d2,2 +np.float32,0x3ecefe60,0x3eadd69a,2 +np.float32,0x49c56e,0x49c56e,2 +np.float32,0x3df22d60,0x3de4e550,2 +np.float32,0x3f67c19d,0x3f250707,2 +np.float32,0x3f20eb9c,0x3ef9b624,2 +np.float32,0x3f05ca75,0x3ed742fa,2 +np.float32,0xbe8514f8,0xbe9a1d45,2 +np.float32,0x8070a003,0x8070a003,2 +np.float32,0x7e49650e,0x42ae317a,2 +np.float32,0x3de16ce9,0x3dd5dc3e,2 +np.float32,0xbf4ae952,0xbfc95f1f,2 +np.float32,0xbe44dd84,0xbe5aa0db,2 +np.float32,0x803c3bc0,0x803c3bc0,2 +np.float32,0x3eebb9e8,0x3ec1e692,2 +np.float32,0x80588275,0x80588275,2 +np.float32,0xbea1e69a,0xbec29d86,2 +np.float32,0x3f7b4bf8,0x3f2f154c,2 +np.float32,0x7eb47ecc,0x42af5c46,2 +np.float32,0x3d441e00,0x3d3f911a,2 +np.float32,0x7f54d40e,0x42b11388,2 +np.float32,0xbf47f17e,0xbfc26882,2 +np.float32,0x3ea7da57,0x3e912db4,2 +np.float32,0x3f59cc7b,0x3f1d984e,2 +np.float32,0x570e08,0x570e08,2 +np.float32,0x3e99560c,0x3e8620a2,2 +np.float32,0x3ecfbd14,0x3eae5e55,2 +np.float32,0x7e86be08,0x42aec698,2 +np.float32,0x3f10f28a,0x3ee5b5d3,2 +np.float32,0x7f228722,0x42b0897a,2 +np.float32,0x3f4b979b,0x3f15cd30,2 +np.float32,0xbf134283,0xbf5b30f9,2 +np.float32,0x3f2ae16a,0x3f02e64f,2 +np.float32,0x3e98e158,0x3e85c6cc,2 +np.float32,0x7ec39f27,0x42af857a,2 +np.float32,0x3effedb0,0x3ecf8cea,2 +np.float32,0xbd545620,0xbd5a09c1,2 +np.float32,0x503a28,0x503a28,2 +np.float32,0x3f712744,0x3f29e9a1,2 +np.float32,0x3edc6194,0x3eb748b1,2 +np.float32,0xbf4ec1e5,0xbfd2ff5f,2 +np.float32,0x3f46669e,0x3f12e4b5,2 +np.float32,0xabad3,0xabad3,2 +np.float32,0x80000000,0x80000000,2 +np.float32,0x803f2e6d,0x803f2e6d,2 +np.float32,0xbf431542,0xbfb7c3e6,2 +np.float32,0x3f6f2d53,0x3f28e496,2 +np.float32,0x546bd8,0x546bd8,2 +np.float32,0x25c80a,0x25c80a,2 +np.float32,0x3e50883c,0x3e3dcd7e,2 +np.float32,0xbf5fa2ba,0xc0045c14,2 +np.float32,0x80271c07,0x80271c07,2 +np.float32,0x8043755d,0x8043755d,2 +np.float32,0xbf3c5cea,0xbfaa5ee9,2 +np.float32,0x3f2fea38,0x3f05e6af,2 +np.float32,0x6da3dc,0x6da3dc,2 +np.float32,0xbf095945,0xbf44dc70,2 +np.float32,0xbe33d584,0xbe45c1f5,2 +np.float32,0x7eb41b2e,0x42af5b2b,2 +np.float32,0xbf0feb74,0xbf537242,2 +np.float32,0xbe96225a,0xbeb1b0b1,2 +np.float32,0x3f63b95f,0x3f22e700,2 +np.float32,0x0,0x0,2 +np.float32,0x3e20b0cc,0x3e154374,2 +np.float32,0xbf79880c,0xc06b6801,2 +np.float32,0xbea690b6,0xbec97b93,2 +np.float32,0xbf3e11ca,0xbfada449,2 +np.float32,0x7e7e6292,0x42aea912,2 +np.float32,0x3e793350,0x3e5f0b7b,2 +np.float32,0x802e7183,0x802e7183,2 +np.float32,0x3f1b3695,0x3ef2a788,2 +np.float32,0x801efa20,0x801efa20,2 +np.float32,0x3f1ec43a,0x3ef70f42,2 +np.float32,0xbf12c5ed,0xbf5a0c52,2 +np.float32,0x8005e99c,0x8005e99c,2 +np.float32,0xbf79f5e7,0xc06fcca5,2 +np.float32,0x3ecbaf50,0x3eab7a03,2 +np.float32,0x46b0fd,0x46b0fd,2 +np.float32,0x3edb9023,0x3eb6b631,2 +np.float32,0x7f24bc41,0x42b09063,2 +np.float32,0xbd8d9328,0xbd92b4c6,2 +np.float32,0x3f2c5d7f,0x3f03c9d9,2 +np.float32,0x807bebc9,0x807bebc9,2 +np.float32,0x7f797a99,0x42b164e2,2 +np.float32,0x756e3c,0x756e3c,2 +np.float32,0x80416f8a,0x80416f8a,2 +np.float32,0x3e0d512a,0x3e04611a,2 +np.float32,0x3f7be3e6,0x3f2f61ec,2 +np.float32,0x80075c41,0x80075c41,2 +np.float32,0xbe850294,0xbe9a046c,2 +np.float32,0x684679,0x684679,2 +np.float32,0x3eb393c4,0x3e99eed2,2 +np.float32,0x3f4177c6,0x3f10195b,2 +np.float32,0x3dd1f402,0x3dc7dfe5,2 +np.float32,0x3ef484d4,0x3ec7e2e1,2 +np.float32,0x53eb8f,0x53eb8f,2 +np.float32,0x7f072cb6,0x42b02b20,2 +np.float32,0xbf1b6b55,0xbf6f28d4,2 +np.float32,0xbd8a98d8,0xbd8f827d,2 +np.float32,0x3eafb418,0x3e970e96,2 +np.float32,0x6555af,0x6555af,2 +np.float32,0x7dd5118e,0x42aceb6f,2 +np.float32,0x800a13f7,0x800a13f7,2 +np.float32,0x331a9d,0x331a9d,2 +np.float32,0x8063773f,0x8063773f,2 +np.float32,0x3e95e068,0x3e837553,2 +np.float32,0x80654b32,0x80654b32,2 +np.float32,0x3dabe0e0,0x3da50bb3,2 +np.float32,0xbf6283c3,0xc00a5280,2 +np.float32,0x80751cc5,0x80751cc5,2 +np.float32,0x3f668eb6,0x3f2465c0,2 +np.float32,0x3e13c058,0x3e0a048c,2 +np.float32,0x77780c,0x77780c,2 +np.float32,0x3f7d6e48,0x3f302868,2 +np.float32,0x7e31f9e3,0x42adf22f,2 +np.float32,0x246c7b,0x246c7b,2 +np.float32,0xbe915bf0,0xbeaafa6c,2 +np.float32,0xbf800000,0xff800000,2 +np.float32,0x3f698f42,0x3f25f8e0,2 +np.float32,0x7e698885,0x42ae7d48,2 +np.float32,0x3f5bbd42,0x3f1ea42c,2 +np.float32,0x5b8444,0x5b8444,2 +np.float32,0xbf6065f6,0xc005e2c6,2 +np.float32,0xbeb95036,0xbee60dad,2 +np.float32,0xbf44f846,0xbfbbcade,2 +np.float32,0xc96e5,0xc96e5,2 +np.float32,0xbf213e90,0xbf7e6eae,2 +np.float32,0xbeb309cc,0xbedc4fe6,2 +np.float32,0xbe781cf4,0xbe8e0fe6,2 +np.float32,0x7f0cf0db,0x42b04083,2 +np.float32,0xbf7b6143,0xc08078f9,2 +np.float32,0x80526fc6,0x80526fc6,2 +np.float32,0x3f092bf3,0x3edbaeec,2 +np.float32,0x3ecdf154,0x3ead16df,2 +np.float32,0x2fe85b,0x2fe85b,2 +np.float32,0xbf5100a0,0xbfd8f871,2 +np.float32,0xbec09d40,0xbef1a028,2 +np.float32,0x5e6a85,0x5e6a85,2 +np.float32,0xbec0e2a0,0xbef20f6b,2 +np.float32,0x3f72e788,0x3f2ad00d,2 +np.float32,0x880a6,0x880a6,2 +np.float32,0x3d9e90bf,0x3d98b9fc,2 +np.float32,0x15cf25,0x15cf25,2 +np.float32,0x10171b,0x10171b,2 +np.float32,0x805cf1aa,0x805cf1aa,2 +np.float32,0x3f19bd36,0x3ef0d0d2,2 +np.float32,0x3ebe2bda,0x3ea1b774,2 +np.float32,0xbecd8192,0xbf035c49,2 +np.float32,0x3e2ce508,0x3e1fc21b,2 +np.float32,0x290f,0x290f,2 +np.float32,0x803b679f,0x803b679f,2 +np.float32,0x1,0x1,2 +np.float32,0x807a9c76,0x807a9c76,2 +np.float32,0xbf65fced,0xc01257f8,2 +np.float32,0x3f783414,0x3f2d8475,2 +np.float32,0x3f2d9d92,0x3f0488da,2 +np.float32,0xbddb5798,0xbde80018,2 +np.float32,0x3e91afb8,0x3e8034e7,2 +np.float32,0xbf1b775a,0xbf6f476d,2 +np.float32,0xbf73a32c,0xc041f3ba,2 +np.float32,0xbea39364,0xbec5121b,2 +np.float32,0x80375b94,0x80375b94,2 +np.float32,0x3f331252,0x3f07c3e9,2 +np.float32,0xbf285774,0xbf892e74,2 +np.float32,0x3e699bb8,0x3e526d55,2 +np.float32,0x3f08208a,0x3eda523a,2 +np.float32,0xbf42fb4a,0xbfb78d60,2 +np.float32,0x8029c894,0x8029c894,2 +np.float32,0x3e926c0c,0x3e80c76e,2 +np.float32,0x801e4715,0x801e4715,2 +np.float32,0x3e4b36d8,0x3e395ffd,2 +np.float32,0x8041556b,0x8041556b,2 +np.float32,0xbf2d99ba,0xbf9119bd,2 +np.float32,0x3ed83ea8,0x3eb46250,2 +np.float32,0xbe94a280,0xbeaf92b4,2 +np.float32,0x7f4c7a64,0x42b0ff0a,2 +np.float32,0x806d4022,0x806d4022,2 +np.float32,0xbed382f8,0xbf086d26,2 +np.float32,0x1846fe,0x1846fe,2 +np.float32,0xbe702558,0xbe88d4d8,2 +np.float32,0xbe650ee0,0xbe81a3cc,2 +np.float32,0x3ee9d088,0x3ec0970c,2 +np.float32,0x7f6d4498,0x42b14b30,2 +np.float32,0xbef9f9e6,0xbf2b7ddb,2 +np.float32,0xbf70c384,0xc0349370,2 +np.float32,0xbeff9e9e,0xbf3110c8,2 +np.float32,0xbef06372,0xbf224aa9,2 +np.float32,0xbf15a692,0xbf60e1fa,2 +np.float32,0x8058c117,0x8058c117,2 +np.float32,0xbd9f74b8,0xbda6017b,2 +np.float32,0x801bf130,0x801bf130,2 +np.float32,0x805da84c,0x805da84c,2 +np.float32,0xff800000,0xffc00000,2 +np.float32,0xbeb01de2,0xbed7d6d6,2 +np.float32,0x8077de08,0x8077de08,2 +np.float32,0x3e327668,0x3e2482c1,2 +np.float32,0xbe7add88,0xbe8fe1ab,2 +np.float32,0x805a3c2e,0x805a3c2e,2 +np.float32,0x80326a73,0x80326a73,2 +np.float32,0x800b8a34,0x800b8a34,2 +np.float32,0x8048c83a,0x8048c83a,2 +np.float32,0xbf3799d6,0xbfa1a975,2 +np.float32,0x807649c7,0x807649c7,2 +np.float32,0x3dfdbf90,0x3def3798,2 +np.float32,0xbf1b538a,0xbf6eec4c,2 +np.float32,0xbf1e5989,0xbf76baa0,2 +np.float32,0xc7a80,0xc7a80,2 +np.float32,0x8001be54,0x8001be54,2 +np.float32,0x3f435bbc,0x3f112c6d,2 +np.float32,0xbeabcff8,0xbed151d1,2 +np.float32,0x7de20c78,0x42ad09b7,2 +np.float32,0x3f0e6d2e,0x3ee27b1e,2 +np.float32,0xbf0cb352,0xbf4c3267,2 +np.float32,0x7f6ec06f,0x42b14e61,2 +np.float32,0x7f6fa8ef,0x42b15053,2 +np.float32,0xbf3d2a6a,0xbfabe623,2 +np.float32,0x7f077a4c,0x42b02c46,2 +np.float32,0xbf2a68dc,0xbf8c3cc4,2 +np.float32,0x802a5dbe,0x802a5dbe,2 +np.float32,0x807f631c,0x807f631c,2 +np.float32,0x3dc9b8,0x3dc9b8,2 +np.float32,0x3ebdc1b7,0x3ea16a0a,2 +np.float32,0x7ef29dab,0x42aff3b5,2 +np.float32,0x3e8ab1cc,0x3e757806,2 +np.float32,0x3f27e88e,0x3f011c6d,2 +np.float32,0x3cfd1455,0x3cf93fb5,2 +np.float32,0x7f7eebf5,0x42b16fef,2 +np.float32,0x3c9b2140,0x3c99ade9,2 +np.float32,0x7e928601,0x42aef183,2 +np.float32,0xbd7d2db0,0xbd82abae,2 +np.float32,0x3e6f0df3,0x3e56da20,2 +np.float32,0x7d36a2fc,0x42ab39a3,2 +np.float32,0xbf49d3a2,0xbfc6c859,2 +np.float32,0x7ee541d3,0x42afd6b6,2 +np.float32,0x80753dc0,0x80753dc0,2 +np.float32,0x3f4ce486,0x3f16865d,2 +np.float32,0x39e701,0x39e701,2 +np.float32,0x3f3d9ede,0x3f0de5fa,2 +np.float32,0x7fafb2,0x7fafb2,2 +np.float32,0x3e013fdc,0x3df37090,2 +np.float32,0x807b6a2c,0x807b6a2c,2 +np.float32,0xbe86800a,0xbe9c08c7,2 +np.float32,0x7f40f080,0x42b0e14d,2 +np.float32,0x7eef5afe,0x42afecc8,2 +np.float32,0x7ec30052,0x42af83da,2 +np.float32,0x3eacf768,0x3e9503e1,2 +np.float32,0x7f13ef0e,0x42b0594e,2 +np.float32,0x80419f4a,0x80419f4a,2 +np.float32,0xbf485932,0xbfc3562a,2 +np.float32,0xbe8a24d6,0xbea10011,2 +np.float32,0xbda791c0,0xbdaed2bc,2 +np.float32,0x3e9b5169,0x3e87a67d,2 +np.float32,0x807dd882,0x807dd882,2 +np.float32,0x7f40170e,0x42b0df0a,2 +np.float32,0x7f02f7f9,0x42b01af1,2 +np.float32,0x3ea38bf9,0x3e8decde,2 +np.float32,0x3e2e7ce8,0x3e211ed4,2 +np.float32,0x70a7a6,0x70a7a6,2 +np.float32,0x7d978592,0x42ac3ce7,2 +np.float32,0x804d12d0,0x804d12d0,2 +np.float32,0x80165dc8,0x80165dc8,2 +np.float32,0x80000001,0x80000001,2 +np.float32,0x3e325da0,0x3e246da6,2 +np.float32,0xbe063bb8,0xbe0fe281,2 +np.float32,0x160b8,0x160b8,2 +np.float32,0xbe5687a4,0xbe70bbef,2 +np.float32,0x7f11ab34,0x42b05168,2 +np.float32,0xc955c,0xc955c,2 +np.float32,0xbea0003a,0xbebfd826,2 +np.float32,0x3f7fbdd9,0x3f315102,2 +np.float32,0xbe61aefc,0xbe7ef121,2 +np.float32,0xbf1b9873,0xbf6f9bc3,2 +np.float32,0x3a6d14,0x3a6d14,2 +np.float32,0xbf1ad3b4,0xbf6da808,2 +np.float32,0x3ed2dd24,0x3eb0963d,2 +np.float32,0xbe81a4ca,0xbe957d52,2 +np.float32,0x7f1be3e9,0x42b07421,2 +np.float32,0x7f5ce943,0x42b1269e,2 +np.float32,0x7eebcbdf,0x42afe51d,2 +np.float32,0x807181b5,0x807181b5,2 +np.float32,0xbecb03ba,0xbf0149ad,2 +np.float32,0x42edb8,0x42edb8,2 +np.float32,0xbf3aeec8,0xbfa7b13f,2 +np.float32,0xbd0c4f00,0xbd0ec4a0,2 +np.float32,0x3e48d260,0x3e376070,2 +np.float32,0x1a9731,0x1a9731,2 +np.float32,0x7f323be4,0x42b0b8b5,2 +np.float32,0x1a327f,0x1a327f,2 +np.float32,0x17f1fc,0x17f1fc,2 +np.float32,0xbf2f4f9b,0xbf93c91a,2 +np.float32,0x3ede8934,0x3eb8c9c3,2 +np.float32,0xbf56aaac,0xbfe968bb,2 +np.float32,0x3e22cb5a,0x3e17148c,2 +np.float32,0x7d9def,0x7d9def,2 +np.float32,0x8045b963,0x8045b963,2 +np.float32,0x77404f,0x77404f,2 +np.float32,0x7e2c9efb,0x42ade28b,2 +np.float32,0x8058ad89,0x8058ad89,2 +np.float32,0x7f4139,0x7f4139,2 +np.float32,0x8020e12a,0x8020e12a,2 +np.float32,0x800c9daa,0x800c9daa,2 +np.float32,0x7f2c5ac5,0x42b0a789,2 +np.float32,0x3f04a47b,0x3ed5c043,2 +np.float32,0x804692d5,0x804692d5,2 +np.float32,0xbf6e7fa4,0xc02bb493,2 +np.float32,0x80330756,0x80330756,2 +np.float32,0x7f3e29ad,0x42b0d9e1,2 +np.float32,0xbebf689a,0xbeefb24d,2 +np.float32,0x3f29a86c,0x3f022a56,2 +np.float32,0x3e3bd1c0,0x3e2c72b3,2 +np.float32,0x3f78f2e8,0x3f2de546,2 +np.float32,0x3f3709be,0x3f0a16af,2 +np.float32,0x3e11f150,0x3e086f97,2 +np.float32,0xbf5867ad,0xbfeee8a0,2 +np.float32,0xbebfb328,0xbef0296c,2 +np.float32,0x2f7f15,0x2f7f15,2 +np.float32,0x805cfe84,0x805cfe84,2 +np.float32,0xbf504e01,0xbfd71589,2 +np.float32,0x3ee0903c,0x3eba330c,2 +np.float32,0xbd838990,0xbd87f399,2 +np.float32,0x3f14444e,0x3ee9ee7d,2 +np.float32,0x7e352583,0x42adfb3a,2 +np.float32,0x7e76f824,0x42ae99ec,2 +np.float32,0x3f772d00,0x3f2cfebf,2 +np.float32,0x801f7763,0x801f7763,2 +np.float32,0x3f760bf5,0x3f2c6b87,2 +np.float32,0xbf0bb696,0xbf4a03a5,2 +np.float32,0x3f175d2c,0x3eedd6d2,2 +np.float32,0xbf5723f8,0xbfeae288,2 +np.float32,0x24de0a,0x24de0a,2 +np.float32,0x3cd73f80,0x3cd47801,2 +np.float32,0x7f013305,0x42b013fa,2 +np.float32,0x3e3ad425,0x3e2b9c50,2 +np.float32,0x7d3d16,0x7d3d16,2 +np.float32,0x3ef49738,0x3ec7ef54,2 +np.float32,0x3f5b8612,0x3f1e8678,2 +np.float32,0x7f0eeb5c,0x42b047a7,2 +np.float32,0x7e9d7cb0,0x42af1675,2 +np.float32,0xbdd1cfb0,0xbddd5aa0,2 +np.float32,0xbf645dba,0xc00e78fe,2 +np.float32,0x3f511174,0x3f18d56c,2 +np.float32,0x3d91ad00,0x3d8cba62,2 +np.float32,0x805298da,0x805298da,2 +np.float32,0xbedb6af4,0xbf0f4090,2 +np.float32,0x3d23b1ba,0x3d208205,2 +np.float32,0xbea5783e,0xbec7dc87,2 +np.float32,0x79d191,0x79d191,2 +np.float32,0x3e894413,0x3e7337da,2 +np.float32,0x80800000,0x80800000,2 +np.float32,0xbf34a8d3,0xbf9c907b,2 +np.float32,0x3bae779a,0x3bae011f,2 +np.float32,0x8049284d,0x8049284d,2 +np.float32,0x3eb42cc4,0x3e9a600b,2 +np.float32,0x3da1e2d0,0x3d9bce5f,2 +np.float32,0x3f364b8a,0x3f09a7af,2 +np.float32,0x3d930b10,0x3d8e0118,2 +np.float32,0x8061f8d7,0x8061f8d7,2 +np.float32,0x3f473213,0x3f13573b,2 +np.float32,0x3f1e2a38,0x3ef65102,2 +np.float32,0x8068f7d9,0x8068f7d9,2 +np.float32,0x3f181ef8,0x3eeeca2c,2 +np.float32,0x3eeb6168,0x3ec1a9f5,2 +np.float32,0xc2db6,0xc2db6,2 +np.float32,0x3ef7b578,0x3eca0a69,2 +np.float32,0xbf5b5a84,0xbff8d075,2 +np.float32,0x7f479d5f,0x42b0f2b7,2 +np.float32,0x3e6f3c24,0x3e56ff92,2 +np.float32,0x3f45543a,0x3f1249f0,2 +np.float32,0xbea7c1fa,0xbecb40d2,2 +np.float32,0x7de082,0x7de082,2 +np.float32,0x383729,0x383729,2 +np.float32,0xbd91cb90,0xbd973eb3,2 +np.float32,0x7f320218,0x42b0b80f,2 +np.float32,0x5547f2,0x5547f2,2 +np.float32,0x291fe4,0x291fe4,2 +np.float32,0xbe078ba0,0xbe11655f,2 +np.float32,0x7e0c0658,0x42ad7764,2 +np.float32,0x7e129a2b,0x42ad8ee5,2 +np.float32,0x3f7c96d4,0x3f2fbc0c,2 +np.float32,0x3f800000,0x3f317218,2 +np.float32,0x7f131754,0x42b05662,2 +np.float32,0x15f833,0x15f833,2 +np.float32,0x80392ced,0x80392ced,2 +np.float32,0x3f7c141a,0x3f2f7a36,2 +np.float32,0xbf71c03f,0xc038dcfd,2 +np.float32,0xbe14fb2c,0xbe20fff3,2 +np.float32,0xbee0bac6,0xbf13f14c,2 +np.float32,0x801a32dd,0x801a32dd,2 +np.float32,0x8e12d,0x8e12d,2 +np.float32,0x3f48c606,0x3f143a04,2 +np.float32,0x7f418af5,0x42b0e2e6,2 +np.float32,0x3f1f2918,0x3ef78bb7,2 +np.float32,0x11141b,0x11141b,2 +np.float32,0x3e9fc9e8,0x3e8b11ad,2 +np.float32,0xbea5447a,0xbec79010,2 +np.float32,0xbe31d904,0xbe4359db,2 +np.float32,0x80184667,0x80184667,2 +np.float32,0xbf00503c,0xbf3212c2,2 +np.float32,0x3e0328cf,0x3df6d425,2 +np.float32,0x7ee8e1b7,0x42afdebe,2 +np.float32,0xbef95e24,0xbf2ae5db,2 +np.float32,0x7f3e4eed,0x42b0da45,2 +np.float32,0x3f43ee85,0x3f117fa0,2 +np.float32,0xbcfa2ac0,0xbcfe10fe,2 +np.float32,0x80162774,0x80162774,2 +np.float32,0x372e8b,0x372e8b,2 +np.float32,0x3f263802,0x3f0016b0,2 +np.float32,0x8008725f,0x8008725f,2 +np.float32,0x800beb40,0x800beb40,2 +np.float32,0xbe93308e,0xbead8a77,2 +np.float32,0x3d8a4240,0x3d85cab8,2 +np.float32,0x80179de0,0x80179de0,2 +np.float32,0x7f4a98f2,0x42b0fa4f,2 +np.float32,0x3f0d214e,0x3ee0cff1,2 +np.float32,0x80536c2c,0x80536c2c,2 +np.float32,0x7e7038ed,0x42ae8bbe,2 +np.float32,0x7f345af9,0x42b0bec4,2 +np.float32,0xbf243219,0xbf83442f,2 +np.float32,0x7e0d5555,0x42ad7c27,2 +np.float32,0x762e95,0x762e95,2 +np.float32,0x7ebf4548,0x42af79f6,2 +np.float32,0x8079639e,0x8079639e,2 +np.float32,0x3ef925c0,0x3ecb0260,2 +np.float32,0x3f708695,0x3f2996d6,2 +np.float32,0xfca9f,0xfca9f,2 +np.float32,0x8060dbf4,0x8060dbf4,2 +np.float32,0x4c8840,0x4c8840,2 +np.float32,0xbea922ee,0xbecd4ed5,2 +np.float32,0xbf4f28a9,0xbfd40b98,2 +np.float32,0xbe25ad48,0xbe34ba1b,2 +np.float32,0x3f2fb254,0x3f05c58c,2 +np.float32,0x3f73bcc2,0x3f2b3d5f,2 +np.float32,0xbf479a07,0xbfc1a165,2 +np.float32,0xbeb9a808,0xbee69763,2 +np.float32,0x7eb16a65,0x42af5376,2 +np.float32,0xbeb3e442,0xbedda042,2 +np.float32,0x3d8f439c,0x3d8a79ac,2 +np.float32,0x80347516,0x80347516,2 +np.float32,0x3e8a0c5d,0x3e74738c,2 +np.float32,0xbf0383a4,0xbf389289,2 +np.float32,0x806be8f5,0x806be8f5,2 +np.float32,0x8023f0c5,0x8023f0c5,2 +np.float32,0x2060e9,0x2060e9,2 +np.float32,0xbf759eba,0xc04d239f,2 +np.float32,0x3d84cc5a,0x3d80ab96,2 +np.float32,0xbf57746b,0xbfebdf87,2 +np.float32,0x3e418417,0x3e31401f,2 +np.float32,0xaecce,0xaecce,2 +np.float32,0x3cd1766f,0x3cced45c,2 +np.float32,0x53724a,0x53724a,2 +np.float32,0x3f773710,0x3f2d03de,2 +np.float32,0x8013d040,0x8013d040,2 +np.float32,0x4d0eb2,0x4d0eb2,2 +np.float32,0x8014364a,0x8014364a,2 +np.float32,0x7f3c56c9,0x42b0d4f2,2 +np.float32,0x3eee1e1c,0x3ec3891a,2 +np.float32,0xbdda3eb8,0xbde6c5a0,2 +np.float32,0x26ef4a,0x26ef4a,2 +np.float32,0x7ed3370c,0x42afacbf,2 +np.float32,0xbf06e31b,0xbf3f9ab7,2 +np.float32,0xbe3185f0,0xbe42f556,2 +np.float32,0x3dcf9abe,0x3dc5be41,2 +np.float32,0xbf3696d9,0xbf9fe2bd,2 +np.float32,0x3e68ee50,0x3e51e01a,2 +np.float32,0x3f3d4cc2,0x3f0db6ca,2 +np.float32,0x7fa00000,0x7fe00000,2 +np.float32,0xbf03070c,0xbf3792d0,2 +np.float32,0x3ea79e6c,0x3e910092,2 +np.float32,0xbf1a393a,0xbf6c2251,2 +np.float32,0x3f41eb0e,0x3f105afc,2 +np.float32,0x3ceadb2f,0x3ce78d79,2 +np.float32,0xbf5dc105,0xc000be2c,2 +np.float32,0x7ebb5a0e,0x42af6f5c,2 +np.float32,0xbf7c44eb,0xc0875058,2 +np.float32,0x6aaaf4,0x6aaaf4,2 +np.float32,0x807d8f23,0x807d8f23,2 +np.float32,0xbee6b142,0xbf194fef,2 +np.float32,0xbe83f256,0xbe989526,2 +np.float32,0x7d588e,0x7d588e,2 +np.float32,0x7cc80131,0x42aa0542,2 +np.float32,0x3e0ab198,0x3e02124f,2 +np.float32,0xbf6e64db,0xc02b52eb,2 +np.float32,0x3d238b56,0x3d205d1b,2 +np.float32,0xbeb408e2,0xbeddd8bc,2 +np.float32,0x3f78340d,0x3f2d8471,2 +np.float32,0x806162a3,0x806162a3,2 +np.float32,0x804e484f,0x804e484f,2 +np.float32,0xbeb8c576,0xbee53466,2 +np.float32,0x807aab15,0x807aab15,2 +np.float32,0x3f523e20,0x3f197ab8,2 +np.float32,0xbf009190,0xbf3295de,2 +np.float32,0x3df43da5,0x3de6bd82,2 +np.float32,0x7f639aea,0x42b135e6,2 +np.float32,0x3f1e638a,0x3ef697da,2 +np.float32,0xbf4884de,0xbfc3bac3,2 +np.float32,0xbe9336b6,0xbead931b,2 +np.float32,0x6daf7f,0x6daf7f,2 +np.float32,0xbf1fc152,0xbf7a70b1,2 +np.float32,0x3f103720,0x3ee4c649,2 +np.float32,0x3eeaa227,0x3ec126df,2 +np.float32,0x7f7ea945,0x42b16f69,2 +np.float32,0x3d3cd800,0x3d389ead,2 +np.float32,0x3f3d7268,0x3f0dcc6e,2 +np.float32,0xbf3c1b41,0xbfa9e2e3,2 +np.float32,0x3ecf3818,0x3eadffb2,2 +np.float32,0x3f1af312,0x3ef25372,2 +np.float32,0x48fae4,0x48fae4,2 +np.float64,0x7fedaa1ee4fb543d,0x40862da7ca7c308e,2 +np.float64,0x8007d2d810efa5b1,0x8007d2d810efa5b1,2 +np.float64,0x3fc385e069270bc0,0x3fc22b8884cf2c3b,2 +np.float64,0x68ed4130d1da9,0x68ed4130d1da9,2 +np.float64,0x8008e93e58d1d27d,0x8008e93e58d1d27d,2 +np.float64,0xbfd3d62852a7ac50,0xbfd7be3a7ad1af02,2 +np.float64,0xbfc1fa0ba923f418,0xbfc35f0f19447df7,2 +np.float64,0xbfe01b8cec20371a,0xbfe6658c7e6c8e50,2 +np.float64,0xbfeda81a147b5034,0xc004e9c94f2b91c1,2 +np.float64,0xbfe1c36a97e386d5,0xbfe9ead4d6beaa92,2 +np.float64,0x3fe50be51f2a17ca,0x3fe02c8067d9e5c5,2 +np.float64,0x3febed4d3337da9a,0x3fe413956466134f,2 +np.float64,0x80068ea59ced1d4c,0x80068ea59ced1d4c,2 +np.float64,0x3febe77d5877cefb,0x3fe4107ac088bc71,2 +np.float64,0x800ae77617d5ceed,0x800ae77617d5ceed,2 +np.float64,0x3fd0546b60a0a8d7,0x3fcd16c2e995ab23,2 +np.float64,0xbfe33e1476667c29,0xbfed6d7faec4db2f,2 +np.float64,0x3fe9d2fd51b3a5fb,0x3fe2eef834310219,2 +np.float64,0x8004249878284932,0x8004249878284932,2 +np.float64,0xbfd5b485c72b690c,0xbfda828ccc6a7a5c,2 +np.float64,0x7fcd6e6b6b3adcd6,0x408622807f04768e,2 +np.float64,0x3fd7f9c32caff386,0x3fd45d024514b8da,2 +np.float64,0x7f87eb9d702fd73a,0x40860aa99fcff27f,2 +np.float64,0xbfc5d1f6fb2ba3ec,0xbfc7ec367cb3fecc,2 +np.float64,0x8008316a44d062d5,0x8008316a44d062d5,2 +np.float64,0xbfd54e4358aa9c86,0xbfd9e889d2998a4a,2 +np.float64,0xda65facdb4cc0,0xda65facdb4cc0,2 +np.float64,0x3fc5b4f6f32b69f0,0x3fc40d13aa8e248b,2 +np.float64,0x3fd825a5d5b04b4c,0x3fd47ce73e04d3ff,2 +np.float64,0x7ac9d56ef593b,0x7ac9d56ef593b,2 +np.float64,0xbfd0a51977214a32,0xbfd34702071428be,2 +np.float64,0x3fd21f620b243ec4,0x3fcfea0c02193640,2 +np.float64,0x3fe6fb3f1b2df67e,0x3fe151ffb18c983b,2 +np.float64,0x700de022e01bd,0x700de022e01bd,2 +np.float64,0xbfbb76b81236ed70,0xbfbd0d31deea1ec7,2 +np.float64,0x3fecfc3856f9f870,0x3fe4a2fcadf221e0,2 +np.float64,0x3fede286517bc50c,0x3fe51af2fbd6ef63,2 +np.float64,0x7fdc8da96c391b52,0x408627ce09cfef2b,2 +np.float64,0x8000edfcfb81dbfb,0x8000edfcfb81dbfb,2 +np.float64,0x8009ebc42af3d789,0x8009ebc42af3d789,2 +np.float64,0x7fd658aaf8acb155,0x408625d80cd1ccc9,2 +np.float64,0x3feea584a37d4b09,0x3fe57f29a73729cd,2 +np.float64,0x4cfe494699fca,0x4cfe494699fca,2 +np.float64,0xbfe9d96460b3b2c9,0xbffa62ecfa026c77,2 +np.float64,0x7fdb3852c3b670a5,0x4086276c191dc9b1,2 +np.float64,0xbfe4d1fc9ee9a3f9,0xbff0d37ce37cf479,2 +np.float64,0xffefffffffffffff,0xfff8000000000000,2 +np.float64,0xbfd1c43d7fa3887a,0xbfd4cfbefb5f2c43,2 +np.float64,0x3fec4a8e0d78951c,0x3fe4453a82ca2570,2 +np.float64,0x7fafed74583fdae8,0x4086181017b8dac9,2 +np.float64,0x80076c4ebcced89e,0x80076c4ebcced89e,2 +np.float64,0x8001a9aa7b235356,0x8001a9aa7b235356,2 +np.float64,0x121260fe2424d,0x121260fe2424d,2 +np.float64,0x3fddd028e3bba052,0x3fd87998c4c43c5b,2 +np.float64,0x800ed1cf4a9da39f,0x800ed1cf4a9da39f,2 +np.float64,0xbfef2e63d7fe5cc8,0xc00d53480b16971b,2 +np.float64,0xbfedde3309fbbc66,0xc005ab55b7a7c127,2 +np.float64,0x3fda3e1e85b47c3d,0x3fd5fddafd8d6729,2 +np.float64,0x8007c6443c6f8c89,0x8007c6443c6f8c89,2 +np.float64,0xbfe101705f2202e0,0xbfe8420817665121,2 +np.float64,0x7fe0bff3c1e17fe7,0x4086291539c56d80,2 +np.float64,0x7fe6001dab6c003a,0x40862b43aa7cb060,2 +np.float64,0x7fbdecf7de3bd9ef,0x40861d170b1c51a5,2 +np.float64,0xbfc0fd508c21faa0,0xbfc23a5876e99fa3,2 +np.float64,0xbfcf6eb14f3edd64,0xbfd208cbf742c8ea,2 +np.float64,0x3f6d40ea403a81d5,0x3f6d33934ab8e799,2 +np.float64,0x7fc32600b6264c00,0x40861f10302357e0,2 +np.float64,0x3fd05870baa0b0e0,0x3fcd1d2af420fac7,2 +np.float64,0x80051d5120aa3aa3,0x80051d5120aa3aa3,2 +np.float64,0x3fdb783fcfb6f080,0x3fd6db229658c083,2 +np.float64,0x3fe0b61199e16c24,0x3fdae41e277be2eb,2 +np.float64,0x3daf62167b5ed,0x3daf62167b5ed,2 +np.float64,0xbfec3c53b6f878a7,0xc0011f0ce7a78a2a,2 +np.float64,0x800fc905161f920a,0x800fc905161f920a,2 +np.float64,0x3fdc7b9cc138f73a,0x3fd78f9c2360e661,2 +np.float64,0x7fe4079e97a80f3c,0x40862a83795f2443,2 +np.float64,0x8010000000000000,0x8010000000000000,2 +np.float64,0x7fe6da5345adb4a6,0x40862b9183c1e4b0,2 +np.float64,0xbfd0a76667214ecc,0xbfd34a1e0c1f6186,2 +np.float64,0x37fb0b906ff62,0x37fb0b906ff62,2 +np.float64,0x7fe170e59fa2e1ca,0x408629680a55e5c5,2 +np.float64,0x3fea900c77752019,0x3fe356eec75aa345,2 +np.float64,0x3fc575c63a2aeb8c,0x3fc3d701167d76b5,2 +np.float64,0x3fe8b45da87168bc,0x3fe24ecbb778fd44,2 +np.float64,0xbfcb990ab5373214,0xbfcf1596c076813c,2 +np.float64,0xf146fdfbe28e0,0xf146fdfbe28e0,2 +np.float64,0x8001fcd474c3f9aa,0x8001fcd474c3f9aa,2 +np.float64,0xbfe9b555eeb36aac,0xbffa0630c3bb485b,2 +np.float64,0x800f950be83f2a18,0x800f950be83f2a18,2 +np.float64,0x7feb0e03ab761c06,0x40862ceb30e36887,2 +np.float64,0x7fca51bd4a34a37a,0x4086219b9dfd35c9,2 +np.float64,0xbfdc27c34cb84f86,0xbfe28ccde8d6bc08,2 +np.float64,0x80009ce1714139c4,0x80009ce1714139c4,2 +np.float64,0x8005290fb1ea5220,0x8005290fb1ea5220,2 +np.float64,0xbfee81e6473d03cd,0xc00885972ca1699b,2 +np.float64,0x7fcfb11a373f6233,0x408623180b8f75d9,2 +np.float64,0xbfcb9c4bfd373898,0xbfcf19bd25881928,2 +np.float64,0x7feaec5885f5d8b0,0x40862ce136050e6c,2 +np.float64,0x8009e17a4a53c2f5,0x8009e17a4a53c2f5,2 +np.float64,0xbfe1cceb9e6399d7,0xbfea0038bd3def20,2 +np.float64,0x8009170bd7122e18,0x8009170bd7122e18,2 +np.float64,0xb2b6f7f1656df,0xb2b6f7f1656df,2 +np.float64,0x3fc75bfd1f2eb7f8,0x3fc574c858332265,2 +np.float64,0x3fa24c06ec249800,0x3fa1fa462ffcb8ec,2 +np.float64,0xaa9a4d2d5534a,0xaa9a4d2d5534a,2 +np.float64,0xbfd7b76208af6ec4,0xbfdda0c3200dcc9f,2 +np.float64,0x7f8cbab73039756d,0x40860c20cba57a94,2 +np.float64,0x3fdbcf9f48b79f3f,0x3fd71827a60e8b6d,2 +np.float64,0xbfdd60f71a3ac1ee,0xbfe3a94bc8cf134d,2 +np.float64,0xb9253589724a7,0xb9253589724a7,2 +np.float64,0xbfcf28e37e3e51c8,0xbfd1da9977b741e3,2 +np.float64,0x80011457f7e228b1,0x80011457f7e228b1,2 +np.float64,0x7fec33df737867be,0x40862d404a897122,2 +np.float64,0xae55f8f95cabf,0xae55f8f95cabf,2 +np.float64,0xbfc1ab9397235728,0xbfc303e5533d4a5f,2 +np.float64,0x7fef0f84b3be1f08,0x40862e05f9ba7118,2 +np.float64,0x7fdc94f328b929e5,0x408627d01449d825,2 +np.float64,0x3fee1b598c7c36b3,0x3fe53847be166834,2 +np.float64,0x3fee8326f37d064e,0x3fe56d96f3fbcf43,2 +np.float64,0x3fe7b18a83ef6316,0x3fe1bb6a6d48c675,2 +np.float64,0x3fe5db969c6bb72e,0x3fe0a8d7d151996c,2 +np.float64,0x3e3391d27c673,0x3e3391d27c673,2 +np.float64,0x3fe79a46d76f348e,0x3fe1ae09a96ea628,2 +np.float64,0x7ff4000000000000,0x7ffc000000000000,2 +np.float64,0x7fe57d6505aafac9,0x40862b13925547f1,2 +np.float64,0x3fc433371d28666e,0x3fc2c196a764c47b,2 +np.float64,0x8008dbf69cd1b7ee,0x8008dbf69cd1b7ee,2 +np.float64,0xbfe744f459ee89e8,0xbff4c847ad3ee152,2 +np.float64,0x80098aa245331545,0x80098aa245331545,2 +np.float64,0x6747112ece8e3,0x6747112ece8e3,2 +np.float64,0x5d342a40ba69,0x5d342a40ba69,2 +np.float64,0xf7a17739ef42f,0xf7a17739ef42f,2 +np.float64,0x3fe1b34a9d236695,0x3fdc2d7c4e2c347a,2 +np.float64,0x7fb53bf5ec2a77eb,0x40861a585ec8f7ff,2 +np.float64,0xbfe6256f1cec4ade,0xbff2d89a36be65ae,2 +np.float64,0xb783bc9b6f078,0xb783bc9b6f078,2 +np.float64,0xbfedf74a3bfbee94,0xc0060bb6f2bc11ef,2 +np.float64,0x3fda2a5eccb454be,0x3fd5efd7f18b8e81,2 +np.float64,0xbfb3838ab2270718,0xbfb44c337fbca3c3,2 +np.float64,0x3fb4ac6dc22958e0,0x3fb3e194ca01a502,2 +np.float64,0x76c11aaaed824,0x76c11aaaed824,2 +np.float64,0x80025bb1af04b764,0x80025bb1af04b764,2 +np.float64,0x3fdc02740ab804e8,0x3fd73b8cd6f95f19,2 +np.float64,0x3fe71856f5ee30ae,0x3fe162e9fafb4428,2 +np.float64,0x800236f332646de7,0x800236f332646de7,2 +np.float64,0x7fe13fd9d2e27fb3,0x408629516b42a317,2 +np.float64,0x7fdf6bbd34bed779,0x40862892069d805c,2 +np.float64,0x3fd4727beba8e4f8,0x3fd1be5b48d9e282,2 +np.float64,0x800e0fac9e5c1f59,0x800e0fac9e5c1f59,2 +np.float64,0xfb54423ff6a89,0xfb54423ff6a89,2 +np.float64,0x800fbf7ed47f7efe,0x800fbf7ed47f7efe,2 +np.float64,0x3fe9d41fa2f3a840,0x3fe2ef98dc1fd463,2 +np.float64,0x800d733e805ae67d,0x800d733e805ae67d,2 +np.float64,0x3feebe4c46fd7c98,0x3fe58bcf7f47264e,2 +np.float64,0x7fe1ab77b5e356ee,0x40862982bb3dce34,2 +np.float64,0xbfdddac05abbb580,0xbfe41aa45f72d5a2,2 +np.float64,0x3fe14219dee28434,0x3fdb9b137d1f1220,2 +np.float64,0x3fe25d3d5a24ba7b,0x3fdd06e1cf32d35a,2 +np.float64,0x8000fa4fbe81f4a0,0x8000fa4fbe81f4a0,2 +np.float64,0x3fe303e23e6607c4,0x3fddd94982efa9f1,2 +np.float64,0x3fe89cf5d83139ec,0x3fe24193a2e12f75,2 +np.float64,0x3fe9b36ef87366de,0x3fe2dd7cdc25a4a5,2 +np.float64,0xbfdb8b38f8371672,0xbfe2023ba7e002bb,2 +np.float64,0xafc354955f86b,0xafc354955f86b,2 +np.float64,0xbfe2f3d49e65e7a9,0xbfecb557a94123d3,2 +np.float64,0x800496617c092cc4,0x800496617c092cc4,2 +np.float64,0x32db0cfa65b62,0x32db0cfa65b62,2 +np.float64,0xbfd893bfa2b12780,0xbfdf02a8c1e545aa,2 +np.float64,0x7fd5ac927d2b5924,0x408625997e7c1f9b,2 +np.float64,0x3fde9defb8bd3be0,0x3fd9056190986349,2 +np.float64,0x80030cfeb54619fe,0x80030cfeb54619fe,2 +np.float64,0x3fcba85b273750b8,0x3fc90a5ca976594f,2 +np.float64,0x3fe98f6f5cf31edf,0x3fe2c97fcb4eca25,2 +np.float64,0x3fe33dbf90667b80,0x3fde21b83321b993,2 +np.float64,0x3fe4686636e8d0cc,0x3fdf928cdca751b3,2 +np.float64,0x80018ade6ce315be,0x80018ade6ce315be,2 +np.float64,0x7fa9af70c8335ee1,0x408616528cd5a906,2 +np.float64,0x3fbeb460aa3d68c0,0x3fbcff96b00a2193,2 +np.float64,0x7fa82c869830590c,0x408615d6598d9368,2 +np.float64,0xd08c0e6fa1182,0xd08c0e6fa1182,2 +np.float64,0x3fef4eb750fe9d6f,0x3fe5d522fd4e7f64,2 +np.float64,0xbfc586f5492b0dec,0xbfc791eaae92aad1,2 +np.float64,0x7fede64ac7bbcc95,0x40862db7f444fa7b,2 +np.float64,0x3fe540003d6a8000,0x3fe04bdfc2916a0b,2 +np.float64,0x8009417fe6f28300,0x8009417fe6f28300,2 +np.float64,0x3fe6959cf16d2b3a,0x3fe116a1ce01887b,2 +np.float64,0x3fb0a40036214800,0x3fb01f447778219a,2 +np.float64,0x3feff26e91ffe4dd,0x3fe627798fc859a7,2 +np.float64,0x7fed8e46cd7b1c8d,0x40862da044a1d102,2 +np.float64,0x7fec4eb774f89d6e,0x40862d47e43edb53,2 +np.float64,0x3fe800e5e07001cc,0x3fe1e8e2b9105fc2,2 +np.float64,0x800f4eb2f9be9d66,0x800f4eb2f9be9d66,2 +np.float64,0x800611659bcc22cc,0x800611659bcc22cc,2 +np.float64,0x3fd66e65d2acdccc,0x3fd33ad63a5e1000,2 +np.float64,0x800a9085b7f5210c,0x800a9085b7f5210c,2 +np.float64,0x7fdf933a3fbf2673,0x4086289c0e292f2b,2 +np.float64,0x1cd1ba7a39a38,0x1cd1ba7a39a38,2 +np.float64,0xbfefd0b10fffa162,0xc0149ded900ed851,2 +np.float64,0xbfe8c63485b18c69,0xbff7cf3078b1574f,2 +np.float64,0x3fecde56ca79bcae,0x3fe4934afbd7dda9,2 +np.float64,0x8006cd6888cd9ad2,0x8006cd6888cd9ad2,2 +np.float64,0x3fd7a391c2af4724,0x3fd41e2f74df2329,2 +np.float64,0x3fe6a8ad58ed515a,0x3fe121ccfb28e6f5,2 +np.float64,0x7fe18a80dd631501,0x40862973c09086b9,2 +np.float64,0xbf74fd6d8029fb00,0xbf750b3e368ebe6b,2 +np.float64,0x3fdd35e93dba6bd4,0x3fd810071faaffad,2 +np.float64,0x3feb0d8f57361b1f,0x3fe39b3abdef8b7a,2 +np.float64,0xbfd5ec7288abd8e6,0xbfdad764df0d2ca1,2 +np.float64,0x7fdc848272b90904,0x408627cb78f3fb9e,2 +np.float64,0x800ed3eda91da7db,0x800ed3eda91da7db,2 +np.float64,0x3fefac64857f58c9,0x3fe60459dbaad1ba,2 +np.float64,0x3fd1df7a5ba3bef4,0x3fcf864a39b926ff,2 +np.float64,0xfe26ca4bfc4da,0xfe26ca4bfc4da,2 +np.float64,0xbfd1099f8da21340,0xbfd3cf6e6efe934b,2 +np.float64,0xbfe15de9a7a2bbd4,0xbfe909cc895f8795,2 +np.float64,0x3fe89714ed712e2a,0x3fe23e40d31242a4,2 +np.float64,0x800387113e470e23,0x800387113e470e23,2 +np.float64,0x3fe4f80730e9f00e,0x3fe0208219314cf1,2 +np.float64,0x2f95a97c5f2b6,0x2f95a97c5f2b6,2 +np.float64,0x800ea7cdd87d4f9c,0x800ea7cdd87d4f9c,2 +np.float64,0xbf64b967c0297300,0xbf64c020a145b7a5,2 +np.float64,0xbfc5a91a342b5234,0xbfc7bafd77a61d81,2 +np.float64,0xbfe2226fe76444e0,0xbfeac33eb1d1b398,2 +np.float64,0x3fc6aaa8d42d5552,0x3fc4de79f5c68cd4,2 +np.float64,0x3fe54fd4c1ea9faa,0x3fe05561a9a5922b,2 +np.float64,0x80029c1f75653840,0x80029c1f75653840,2 +np.float64,0xbfcb4a84a2369508,0xbfceb1a23bac3995,2 +np.float64,0x80010abeff02157f,0x80010abeff02157f,2 +np.float64,0x7f92d12cf825a259,0x40860e49bde3a5b6,2 +np.float64,0x800933e7027267ce,0x800933e7027267ce,2 +np.float64,0x3fc022b12e204562,0x3fbe64acc53ed887,2 +np.float64,0xbfe35f938de6bf27,0xbfedc1f3e443c016,2 +np.float64,0x1f8d9bae3f1b4,0x1f8d9bae3f1b4,2 +np.float64,0x3fe552f22ceaa5e4,0x3fe057404072350f,2 +np.float64,0xbfa73753442e6ea0,0xbfa7c24a100190f1,2 +np.float64,0x7fb3e2982827c52f,0x408619d1efa676b6,2 +np.float64,0xbfd80cb7a5301970,0xbfde28e65f344f33,2 +np.float64,0xbfcde835973bd06c,0xbfd10806fba46c8f,2 +np.float64,0xbfd4e3c749a9c78e,0xbfd949aff65de39c,2 +np.float64,0x3fcb4b9d6f36973b,0x3fc8be02ad6dc0d3,2 +np.float64,0x1a63000034c7,0x1a63000034c7,2 +np.float64,0x7fdc9c751e3938e9,0x408627d22df71959,2 +np.float64,0x3fd74f3f712e9e7f,0x3fd3e07df0c37ec1,2 +np.float64,0xbfceab74d33d56e8,0xbfd187e99bf82903,2 +np.float64,0x7ff0000000000000,0x7ff0000000000000,2 +np.float64,0xbfb2cca466259948,0xbfb3868208e8de30,2 +np.float64,0x800204688b8408d2,0x800204688b8408d2,2 +np.float64,0x3e4547407c8aa,0x3e4547407c8aa,2 +np.float64,0xbfe4668846e8cd10,0xbff03c85189f3818,2 +np.float64,0x800dd350245ba6a0,0x800dd350245ba6a0,2 +np.float64,0xbfbc13c160382780,0xbfbdbd56ce996d16,2 +np.float64,0x7fe25a628a24b4c4,0x408629d06eb2d64d,2 +np.float64,0x3fd19dabbc233b57,0x3fcf1f3ed1d34c8c,2 +np.float64,0x547e20faa8fc5,0x547e20faa8fc5,2 +np.float64,0xbfe19392c6232726,0xbfe97ffe4f303335,2 +np.float64,0x3f87f9f6702ff400,0x3f87d64fb471bb04,2 +np.float64,0x9dfc52db3bf8b,0x9dfc52db3bf8b,2 +np.float64,0x800e1f5a9adc3eb5,0x800e1f5a9adc3eb5,2 +np.float64,0xbfddbd09c8bb7a14,0xbfe3fed7d7cffc70,2 +np.float64,0xbfeda71af87b4e36,0xc004e6631c514544,2 +np.float64,0xbfdbfcfe1bb7f9fc,0xbfe266b5d4a56265,2 +np.float64,0x3fe4ee78cd69dcf2,0x3fe01abba4e81fc9,2 +np.float64,0x800f13b820de2770,0x800f13b820de2770,2 +np.float64,0x3f861e09702c3c00,0x3f85ffae83b02c4f,2 +np.float64,0xbfc0972479212e48,0xbfc1c4bf70b30cbc,2 +np.float64,0x7fef057ef57e0afd,0x40862e036479f6a9,2 +np.float64,0x8bdbabe517b76,0x8bdbabe517b76,2 +np.float64,0xbfec495417f892a8,0xc0013ade88746d18,2 +np.float64,0x3fec680ab3f8d015,0x3fe454dd304b560d,2 +np.float64,0xbfae7ce60c3cf9d0,0xbfaf6eef15bbe56b,2 +np.float64,0x3fec314124786282,0x3fe437ca06294f5a,2 +np.float64,0x7fd5ed05b82bda0a,0x408625b125518e58,2 +np.float64,0x3feac9f02f3593e0,0x3fe3768104dd5cb7,2 +np.float64,0x0,0x0,2 +np.float64,0xbfddd2abd5bba558,0xbfe41312b8ea20de,2 +np.float64,0xbfedf9558c7bf2ab,0xc00613c53e0bb33a,2 +np.float64,0x3fef245ffefe48c0,0x3fe5bfb4dfe3b7a5,2 +np.float64,0x7fe178604922f0c0,0x4086296b77d5eaef,2 +np.float64,0x10000000000000,0x10000000000000,2 +np.float64,0x7fed026766ba04ce,0x40862d7a0dc45643,2 +np.float64,0xbfde27d8c3bc4fb2,0xbfe46336b6447697,2 +np.float64,0x3fe9485d9cb290bb,0x3fe2a1e4b6419423,2 +np.float64,0xbfe27b8a7464f715,0xbfeb9382f5b16f65,2 +np.float64,0x5c34d274b869b,0x5c34d274b869b,2 +np.float64,0xbfeee0b7453dc16f,0xc00acdb46459b6e6,2 +np.float64,0x7fe3dfb4d4e7bf69,0x40862a73785fdf12,2 +np.float64,0xb4635eef68c6c,0xb4635eef68c6c,2 +np.float64,0xbfe522a2c82a4546,0xbff148912a59a1d6,2 +np.float64,0x8009ba38a9737472,0x8009ba38a9737472,2 +np.float64,0xbfc056ff3820ae00,0xbfc17b2205fa180d,2 +np.float64,0x7fe1c8b8a0239170,0x4086298feeee6133,2 +np.float64,0x3fe2d2c6b9e5a58e,0x3fdd9b907471031b,2 +np.float64,0x3fa0a161bc2142c0,0x3fa05db36f6a073b,2 +np.float64,0x3fdef4268ebde84c,0x3fd93f980794d1e7,2 +np.float64,0x800ecd9fe2fd9b40,0x800ecd9fe2fd9b40,2 +np.float64,0xbfc9fbd45e33f7a8,0xbfcd0afc47c340f6,2 +np.float64,0x3fe8c3035b718606,0x3fe2570eb65551a1,2 +np.float64,0xbfe78c4ad2ef1896,0xbff54d25b3328742,2 +np.float64,0x8006f5dcf8adebbb,0x8006f5dcf8adebbb,2 +np.float64,0x800301dca2a603ba,0x800301dca2a603ba,2 +np.float64,0xad4289e55a851,0xad4289e55a851,2 +np.float64,0x80037764f9e6eecb,0x80037764f9e6eecb,2 +np.float64,0xbfe73575b26e6aec,0xbff4abfb5e985c62,2 +np.float64,0xbfc6cb91652d9724,0xbfc91a8001b33ec2,2 +np.float64,0xbfe3a918ffe75232,0xbfee7e6e4fd34c53,2 +np.float64,0x9bc84e2b3790a,0x9bc84e2b3790a,2 +np.float64,0x7fdeec303cbdd85f,0x408628714a49d996,2 +np.float64,0x3fe1d1dcb763a3ba,0x3fdc54ce060dc7f4,2 +np.float64,0x8008ae6432b15cc9,0x8008ae6432b15cc9,2 +np.float64,0x3fd8022fa2b00460,0x3fd46322bf02a609,2 +np.float64,0xbfc55b64472ab6c8,0xbfc75d9568f462e0,2 +np.float64,0xbfe8b165437162ca,0xbff7a15e2ead645f,2 +np.float64,0x7f759330feeb3,0x7f759330feeb3,2 +np.float64,0xbfd504f68eaa09ee,0xbfd97b06c01d7473,2 +np.float64,0x54702d5aa8e06,0x54702d5aa8e06,2 +np.float64,0xbfed1779337a2ef2,0xc0032f7109ef5a51,2 +np.float64,0xe248bd4dc4918,0xe248bd4dc4918,2 +np.float64,0xbfd8c59150318b22,0xbfdf53bca6ca8b1e,2 +np.float64,0xbfe3b9d942e773b2,0xbfeea9fcad277ba7,2 +np.float64,0x800934ec127269d9,0x800934ec127269d9,2 +np.float64,0xbfbb7f535a36fea8,0xbfbd16d61b6c52b8,2 +np.float64,0xccb185a199631,0xccb185a199631,2 +np.float64,0x3fe3dda76fe7bb4e,0x3fdee83bc6094301,2 +np.float64,0xbfe0c902f5e19206,0xbfe7ca7c0e888006,2 +np.float64,0xbfefeed08cbfdda1,0xc018aadc483c8724,2 +np.float64,0x7fd0c05c52a180b8,0x40862389daf64aac,2 +np.float64,0xbfd28e3323a51c66,0xbfd5e9ba278fb685,2 +np.float64,0xbef4103b7de82,0xbef4103b7de82,2 +np.float64,0x3fe7661fd12ecc40,0x3fe18ff7dfb696e2,2 +np.float64,0x3fddd5f2f0bbabe4,0x3fd87d8bb6719c3b,2 +np.float64,0x800b3914cfd6722a,0x800b3914cfd6722a,2 +np.float64,0xf3f09a97e7e14,0xf3f09a97e7e14,2 +np.float64,0x7f97092b502e1256,0x40860fe8054cf54e,2 +np.float64,0xbfdbec7917b7d8f2,0xbfe2580b4b792c79,2 +np.float64,0x7fe7ff215aaffe42,0x40862bf5887fa062,2 +np.float64,0x80080186e570030e,0x80080186e570030e,2 +np.float64,0xbfc27f05e624fe0c,0xbfc3fa214be4adc4,2 +np.float64,0x3fe4481be1689038,0x3fdf6b11e9c4ca72,2 +np.float64,0x3fd642cc9cac8598,0x3fd31a857fe70227,2 +np.float64,0xbef8782d7df0f,0xbef8782d7df0f,2 +np.float64,0x8003077dc2e60efc,0x8003077dc2e60efc,2 +np.float64,0x80083eb5a2507d6c,0x80083eb5a2507d6c,2 +np.float64,0x800e8d1eb77d1a3e,0x800e8d1eb77d1a3e,2 +np.float64,0xbfc7737cd22ee6f8,0xbfc9e7716f03f1fc,2 +np.float64,0xbfe9a2b4ddf3456a,0xbff9d71664a8fc78,2 +np.float64,0x7fe67c7d322cf8f9,0x40862b7066465194,2 +np.float64,0x3fec080ce2b8101a,0x3fe421dac225be46,2 +np.float64,0xbfe6d27beb6da4f8,0xbff3fbb1add521f7,2 +np.float64,0x3fdd4f96ceba9f2e,0x3fd821a638986dbe,2 +np.float64,0x3fbd89f1303b13e2,0x3fbbf49223a9d002,2 +np.float64,0xbfe94e2b9d329c57,0xbff907e549c534f5,2 +np.float64,0x3fe2f2cc51e5e599,0x3fddc3d6b4a834a1,2 +np.float64,0xfdcb5b49fb96c,0xfdcb5b49fb96c,2 +np.float64,0xbfea7108fa74e212,0xbffc01b392f4897b,2 +np.float64,0x3fd38baef7a7175c,0x3fd10e7fd3b958dd,2 +np.float64,0x3fa75bf9cc2eb800,0x3fa6d792ecdedb8e,2 +np.float64,0x7fd19fd20aa33fa3,0x408623f1e2cd04c3,2 +np.float64,0x3fd62c708dac58e0,0x3fd309ec7818d16e,2 +np.float64,0x3fdf489047be9120,0x3fd978640617c758,2 +np.float64,0x1,0x1,2 +np.float64,0xbfe21e7c3ea43cf8,0xbfeaba21320697d3,2 +np.float64,0xbfd3649047a6c920,0xbfd71a6f14223744,2 +np.float64,0xbfd68ca68c2d194e,0xbfdbcce6784e5d44,2 +np.float64,0x3fdb26b0ea364d62,0x3fd6a1f86f64ff74,2 +np.float64,0xbfd843821cb08704,0xbfde80e90805ab3f,2 +np.float64,0x3fd508a27aaa1144,0x3fd22fc203a7b9d8,2 +np.float64,0xbfdb951c7eb72a38,0xbfe20aeaec13699b,2 +np.float64,0x3fef556ba57eaad7,0x3fe5d8865cce0a6d,2 +np.float64,0x3fd0d224b3a1a448,0x3fcdde7be5d7e21e,2 +np.float64,0x8007ff272baffe4f,0x8007ff272baffe4f,2 +np.float64,0x3fe1c7bddf638f7c,0x3fdc47cc6cf2f5cd,2 +np.float64,0x7ff8000000000000,0x7ff8000000000000,2 +np.float64,0x2016d560402f,0x2016d560402f,2 +np.float64,0xbfcca10be9394218,0xbfd033f36b94fc54,2 +np.float64,0xbfdb833628b7066c,0xbfe1fb344b840c70,2 +np.float64,0x3fd8529cb3b0a539,0x3fd49d847fe77218,2 +np.float64,0xbfc0b0ebab2161d8,0xbfc1e260c60ffd1b,2 +np.float64,0xbfea8b9a79f51735,0xbffc4ee6be8a0fa2,2 +np.float64,0x7feca8fab7f951f4,0x40862d613e454646,2 +np.float64,0x7fd8c52d82318a5a,0x408626aaf37423a3,2 +np.float64,0xbfe364ad4526c95a,0xbfedcee39bc93ff5,2 +np.float64,0x800b78161256f02d,0x800b78161256f02d,2 +np.float64,0xbfd55f0153aabe02,0xbfda01a78f72d494,2 +np.float64,0x800315a5f0662b4d,0x800315a5f0662b4d,2 +np.float64,0x7fe4c0dca02981b8,0x40862acc27e4819f,2 +np.float64,0x8009825c703304b9,0x8009825c703304b9,2 +np.float64,0x3fe6e94e1cadd29c,0x3fe1478ccc634f49,2 +np.float64,0x7fe622d8586c45b0,0x40862b504177827e,2 +np.float64,0x3fe4458600688b0c,0x3fdf67e79a84b953,2 +np.float64,0xbfdd75d8a1baebb2,0xbfe3bc9e6ca1bbb5,2 +np.float64,0x3fde789c6bbcf138,0x3fd8ec1d435531b3,2 +np.float64,0x3fe7052b94ee0a58,0x3fe157c5c4418dc1,2 +np.float64,0x7fef31652abe62c9,0x40862e0eaeabcfc0,2 +np.float64,0x3fe279691ee4f2d2,0x3fdd2aa41eb43cd4,2 +np.float64,0xbfd533fa95aa67f6,0xbfd9c12f516d29d7,2 +np.float64,0x3fe6d057f96da0b0,0x3fe138fd96693a6a,2 +np.float64,0x800bad984f775b31,0x800bad984f775b31,2 +np.float64,0x7fdd6fdba4badfb6,0x4086280c73d8ef97,2 +np.float64,0x7fe9b5c0eef36b81,0x40862c82c6f57a53,2 +np.float64,0x8000bc02ece17807,0x8000bc02ece17807,2 +np.float64,0xbff0000000000000,0xfff0000000000000,2 +np.float64,0xbfed430be3fa8618,0xc003aaf338c75b3c,2 +np.float64,0x3fee17b759fc2f6f,0x3fe53668696bf48b,2 +np.float64,0x3f8d4cf9d03a9a00,0x3f8d17d2f532afdc,2 +np.float64,0x8005d6257b8bac4c,0x8005d6257b8bac4c,2 +np.float64,0xbfd17a6df9a2f4dc,0xbfd469e3848adc6e,2 +np.float64,0xb28a293965145,0xb28a293965145,2 +np.float64,0xbfe7d011e42fa024,0xbff5cf818998c8ec,2 +np.float64,0xbfe74f0f136e9e1e,0xbff4dad6ebb0443c,2 +np.float64,0x800f249fc9be4940,0x800f249fc9be4940,2 +np.float64,0x2542f8fe4a860,0x2542f8fe4a860,2 +np.float64,0xc48d40cd891a8,0xc48d40cd891a8,2 +np.float64,0x3fe4e64bc8e9cc98,0x3fe015c9eb3caa53,2 +np.float64,0x3fd33881eca67104,0x3fd0cea886be2457,2 +np.float64,0xbfd01748fba02e92,0xbfd28875959e6901,2 +np.float64,0x7fb7ab01f22f5603,0x40861b369927bf53,2 +np.float64,0xbfe340274ce6804e,0xbfed72b39f0ebb24,2 +np.float64,0x7fc16c0c3422d817,0x40861e4eaf1a286c,2 +np.float64,0x3fc26944a324d288,0x3fc133a77b356ac4,2 +np.float64,0xa149d7134293b,0xa149d7134293b,2 +np.float64,0x800837382d106e71,0x800837382d106e71,2 +np.float64,0x797d1740f2fa4,0x797d1740f2fa4,2 +np.float64,0xc3f15b7787e2c,0xc3f15b7787e2c,2 +np.float64,0x80cad1b90195a,0x80cad1b90195a,2 +np.float64,0x3fdd8f1142bb1e23,0x3fd84d21490d1ce6,2 +np.float64,0xbfbde6c9123bcd90,0xbfbfcc030a86836a,2 +np.float64,0x8007f77e032feefd,0x8007f77e032feefd,2 +np.float64,0x3fe74fed1c6e9fda,0x3fe18322cf19cb61,2 +np.float64,0xbfd8a40bbcb14818,0xbfdf1d23520ba74b,2 +np.float64,0xbfeb7a0e6076f41d,0xbfff4ddfb926efa5,2 +np.float64,0xbfcb8c5f663718c0,0xbfcf0570f702bda9,2 +np.float64,0xf668cd97ecd1a,0xf668cd97ecd1a,2 +np.float64,0xbfe92accf572559a,0xbff8b4393878ffdb,2 +np.float64,0xbfeaa955567552ab,0xbffca70c7d73eee5,2 +np.float64,0xbfe083a14f610742,0xbfe739d84bc35077,2 +np.float64,0x78290568f0521,0x78290568f0521,2 +np.float64,0x3fe94bae2372975c,0x3fe2a3beac5c9858,2 +np.float64,0x3fca4fbab9349f78,0x3fc7edbca2492acb,2 +np.float64,0x8000000000000000,0x8000000000000000,2 +np.float64,0x7fb9eb505433d6a0,0x40861bf0adedb74d,2 +np.float64,0x7fdc66f72a38cded,0x408627c32aeecf0f,2 +np.float64,0x2e8e6f445d1cf,0x2e8e6f445d1cf,2 +np.float64,0xbfec43195af88633,0xc0012d7e3f91b7e8,2 +np.float64,0x7fcdb971e93b72e3,0x40862294c9e3a7bc,2 +np.float64,0x800cabc461195789,0x800cabc461195789,2 +np.float64,0x2c79709c58f2f,0x2c79709c58f2f,2 +np.float64,0x8005d772d3cbaee6,0x8005d772d3cbaee6,2 +np.float64,0x3fe84d8c03709b18,0x3fe21490ce3673dd,2 +np.float64,0x7fe5578adc2aaf15,0x40862b056e8437d4,2 +np.float64,0xbf91298c58225320,0xbf914ec86c32d11f,2 +np.float64,0xc7ed2b6d8fda6,0xc7ed2b6d8fda6,2 +np.float64,0x2761404c4ec29,0x2761404c4ec29,2 +np.float64,0x3fbad3c48835a789,0x3fb9833c02385305,2 +np.float64,0x3fa46fee5428dfe0,0x3fa40a357fb24c23,2 +np.float64,0xbfe3900c6fe72019,0xbfee3dba29dd9d43,2 +np.float64,0x3fe7a9e41a6f53c8,0x3fe1b704dfb9884b,2 +np.float64,0xbfe74a7a1eee94f4,0xbff4d269cacb1f29,2 +np.float64,0xbfee609c72fcc139,0xc007da8499d34123,2 +np.float64,0x3fef2d5fc23e5ac0,0x3fe5c44414e59cb4,2 +np.float64,0xbfd7bdc0402f7b80,0xbfddaae1e7bb78fb,2 +np.float64,0xd71ee01dae3dc,0xd71ee01dae3dc,2 +np.float64,0x3fe98cbcdef3197a,0x3fe2c7ffe33c4541,2 +np.float64,0x8000f8dbb3a1f1b8,0x8000f8dbb3a1f1b8,2 +np.float64,0x3fe3e98ad567d316,0x3fdef6e58058313f,2 +np.float64,0x41ad0bfc835a2,0x41ad0bfc835a2,2 +np.float64,0x7fdcc2dc0d3985b7,0x408627dce39f77af,2 +np.float64,0xbfe47b980de8f730,0xbff059acdccd6e2b,2 +np.float64,0xbfef49b6577e936d,0xc00e714f46b2ccc1,2 +np.float64,0x3fac31816c386300,0x3fab71cb92b0db8f,2 +np.float64,0x3fe59097e76b2130,0x3fe07c299fd1127c,2 +np.float64,0xbfecf0df5cf9e1bf,0xc002c7ebdd65039c,2 +np.float64,0x3fd2b7d0b6a56fa1,0x3fd06b638990ae02,2 +np.float64,0xbfeb68deecf6d1be,0xbfff1187e042d3e4,2 +np.float64,0x3fd44a9771a8952f,0x3fd1a01867c5e302,2 +np.float64,0xf79a9dedef354,0xf79a9dedef354,2 +np.float64,0x800c25a170d84b43,0x800c25a170d84b43,2 +np.float64,0x3ff0000000000000,0x3fe62e42fefa39ef,2 +np.float64,0x3fbff4f7623fe9f0,0x3fbe1d3878f4c417,2 +np.float64,0xd284c845a5099,0xd284c845a5099,2 +np.float64,0xbfe3c7815f678f02,0xbfeecdab5ca2e651,2 +np.float64,0x3fc19c934e233927,0x3fc08036104b1f23,2 +np.float64,0x800b6096de16c12e,0x800b6096de16c12e,2 +np.float64,0xbfe962a67e32c54d,0xbff9392313a112a1,2 +np.float64,0x2b9d0116573a1,0x2b9d0116573a1,2 +np.float64,0x3fcab269ed3564d4,0x3fc83f7e1c3095b7,2 +np.float64,0x3fc8c78d86318f1b,0x3fc6a6cde5696f99,2 +np.float64,0xd5b1e9b5ab63d,0xd5b1e9b5ab63d,2 +np.float64,0xbfed802a47fb0054,0xc00465cad3b5b0ef,2 +np.float64,0xbfd73aaf08ae755e,0xbfdcdbd62b8af271,2 +np.float64,0xbfd4f13c0229e278,0xbfd95dacff79e570,2 +np.float64,0xbfe9622808f2c450,0xbff937f13c397e8d,2 +np.float64,0xbfeddfa62efbbf4c,0xc005b0c835eed829,2 +np.float64,0x3fd65663d4acacc8,0x3fd3290cd0e675dc,2 +np.float64,0x8005e890f1abd123,0x8005e890f1abd123,2 +np.float64,0xbfe924919fb24923,0xbff8a5a827a28756,2 +np.float64,0x3fe8cdf490719be9,0x3fe25d39535e8366,2 +np.float64,0x7fc229e6ff2453cd,0x40861ea40ef87a5a,2 +np.float64,0x3fe5cf53ceeb9ea8,0x3fe0a18e0b65f27e,2 +np.float64,0xa79cf6fb4f39f,0xa79cf6fb4f39f,2 +np.float64,0x7fddbb3c0f3b7677,0x40862820d5edf310,2 +np.float64,0x3e1011de7c203,0x3e1011de7c203,2 +np.float64,0x3fc0b59a83216b38,0x3fbf6916510ff411,2 +np.float64,0x8647f98d0c8ff,0x8647f98d0c8ff,2 +np.float64,0x8005dad33ecbb5a7,0x8005dad33ecbb5a7,2 +np.float64,0x8a80d0631501a,0x8a80d0631501a,2 +np.float64,0xbfe18f7d6ee31efb,0xbfe976f06713afc1,2 +np.float64,0xbfe06eaed560dd5e,0xbfe70eac696933e6,2 +np.float64,0xbfed8ef93c7b1df2,0xc00495bfa3195b53,2 +np.float64,0x3febe9c24677d385,0x3fe411b10db16c42,2 +np.float64,0x7fd5d80c1fabb017,0x408625a97a7787ba,2 +np.float64,0x3fca79b59334f368,0x3fc8108a521341dc,2 +np.float64,0xbfccf8db4339f1b8,0xbfd06c9a5424aadb,2 +np.float64,0xbfea5ac5a574b58b,0xbffbc21d1405d840,2 +np.float64,0x800ce2bf4b19c57f,0x800ce2bf4b19c57f,2 +np.float64,0xbfe8df896d31bf13,0xbff807ab38ac41ab,2 +np.float64,0x3feab83da9f5707c,0x3fe36cdd827c0eff,2 +np.float64,0x3fee717683bce2ed,0x3fe564879171719b,2 +np.float64,0x80025e5577c4bcac,0x80025e5577c4bcac,2 +np.float64,0x3fe3e5378e67ca70,0x3fdef1902c5d1efd,2 +np.float64,0x3fa014bb7c202980,0x3f9faacf9238d499,2 +np.float64,0x3fddbf5e16bb7ebc,0x3fd86e2311cb0f6d,2 +np.float64,0x3fd24e50e6a49ca0,0x3fd0198f04f82186,2 +np.float64,0x656b5214cad6b,0x656b5214cad6b,2 +np.float64,0x8b0a4bfd1614a,0x8b0a4bfd1614a,2 +np.float64,0xbfeeb6bd9e7d6d7b,0xc009b669285e319e,2 +np.float64,0x8000000000000001,0x8000000000000001,2 +np.float64,0xbfe719feceee33fe,0xbff47a4c8cbf0cca,2 +np.float64,0xbfd14fa8c8a29f52,0xbfd42f27b1aced39,2 +np.float64,0x7fec9dcb80f93b96,0x40862d5e1e70bbb9,2 +np.float64,0x7fecacb826f9596f,0x40862d6249746915,2 +np.float64,0x973459f52e68b,0x973459f52e68b,2 +np.float64,0x7f40a59e00214b3b,0x4085f194f45f82b1,2 +np.float64,0x7fc5dbaec32bb75d,0x4086201f3e7065d9,2 +np.float64,0x82d0801305a10,0x82d0801305a10,2 +np.float64,0x7fec81c0f4790381,0x40862d5643c0fc85,2 +np.float64,0xbfe2d81e9ee5b03d,0xbfec71a8e864ea40,2 +np.float64,0x6c545c9ad8a8c,0x6c545c9ad8a8c,2 +np.float64,0x3f9be95a5037d2b5,0x3f9b89b48ac8f5d8,2 +np.float64,0x8000cae9702195d4,0x8000cae9702195d4,2 +np.float64,0xbfd375f45126ebe8,0xbfd733677e54a80d,2 +np.float64,0x3fd29a5b81a534b7,0x3fd05494bf200278,2 +np.float64,0xfff0000000000000,0xfff8000000000000,2 +np.float64,0x7fca8fc195351f82,0x408621ae61aa6c13,2 +np.float64,0x1b28e2ae3651d,0x1b28e2ae3651d,2 +np.float64,0x3fe7fdbd14effb7a,0x3fe1e714884b46a8,2 +np.float64,0x3fdf1ce068be39c0,0x3fd95b054e0fad3d,2 +np.float64,0x3fe79f9a636f3f34,0x3fe1b11a40c00b3e,2 +np.float64,0x3fe60eb7036c1d6e,0x3fe0c72a02176874,2 +np.float64,0x229da17e453b5,0x229da17e453b5,2 +np.float64,0x3fc1a921b5235240,0x3fc08b3f35e47fb1,2 +np.float64,0xbb92d2af7725b,0xbb92d2af7725b,2 +np.float64,0x3fe4110cb1e8221a,0x3fdf2787de6c73f7,2 +np.float64,0xbfbc87771a390ef0,0xbfbe3f6e95622363,2 +np.float64,0xbfe74025dfee804c,0xbff4bf7b1895e697,2 +np.float64,0x964eb6592c9d7,0x964eb6592c9d7,2 +np.float64,0x3f951689b82a2d00,0x3f94dfb38d746fdf,2 +np.float64,0x800356271be6ac4f,0x800356271be6ac4f,2 +np.float64,0x7fefffffffffffff,0x40862e42fefa39ef,2 +np.float64,0xbfed5ce250fab9c5,0xc003f7ddfeb94345,2 +np.float64,0x3fec3d5dc1387abc,0x3fe43e39c02d86f4,2 +np.float64,0x3999897e73332,0x3999897e73332,2 +np.float64,0xbfdcb57744b96aee,0xbfe30c4b98f3d088,2 +np.float64,0x7f961fb0b82c3f60,0x40860f9549c3a380,2 +np.float64,0x67d6efcacfadf,0x67d6efcacfadf,2 +np.float64,0x8002c9498f859294,0x8002c9498f859294,2 +np.float64,0xbfa3033800260670,0xbfa35fe3bf43e188,2 +np.float64,0xbfeab2fc157565f8,0xbffcc413c486b4eb,2 +np.float64,0x3fe25e62f364bcc6,0x3fdd0856e19e3430,2 +np.float64,0x7fb2f42dda25e85b,0x4086196fb34a65fd,2 +np.float64,0x3fe0f1a5af61e34c,0x3fdb3235a1786efb,2 +np.float64,0x800a340ca1f4681a,0x800a340ca1f4681a,2 +np.float64,0x7c20b9def8418,0x7c20b9def8418,2 +np.float64,0xdf0842a1be109,0xdf0842a1be109,2 +np.float64,0x3fe9f22cc2f3e45a,0x3fe300359b842bf0,2 +np.float64,0x3fe389ed73e713da,0x3fde809780fe4432,2 +np.float64,0x9500fb932a020,0x9500fb932a020,2 +np.float64,0x3fd8a21ffdb14440,0x3fd4d70862345d86,2 +np.float64,0x800d99c15cbb3383,0x800d99c15cbb3383,2 +np.float64,0x3fd96c98c932d932,0x3fd568959c9b028f,2 +np.float64,0x7fc228483a24508f,0x40861ea358420976,2 +np.float64,0x7fc6737bef2ce6f7,0x408620560ffc6a98,2 +np.float64,0xbfb2c27cee2584f8,0xbfb37b8cc7774b5f,2 +np.float64,0xbfd18409f9230814,0xbfd4771d1a9a24fb,2 +np.float64,0x3fb53cb3f42a7968,0x3fb466f06f88044b,2 +np.float64,0x3fef61d0187ec3a0,0x3fe5dec8a9d13dd9,2 +np.float64,0x3fe59a6ffd2b34e0,0x3fe0820a99c6143d,2 +np.float64,0x3fce18aff43c3160,0x3fcb07c7b523f0d1,2 +np.float64,0xbfb1319a62226338,0xbfb1cc62f31b2b40,2 +np.float64,0xa00cce6d4019a,0xa00cce6d4019a,2 +np.float64,0x80068ae8e0ed15d3,0x80068ae8e0ed15d3,2 +np.float64,0x3fecef353239de6a,0x3fe49c280adc607b,2 +np.float64,0x3fdf1a7fb0be34ff,0x3fd9596bafe2d766,2 +np.float64,0x3feb5e12eeb6bc26,0x3fe3c6be3ede8d07,2 +np.float64,0x3fdeff5cd43dfeba,0x3fd947262ec96b05,2 +np.float64,0x3f995e75e832bd00,0x3f990f511f4c7f1c,2 +np.float64,0xbfeb5b3ed0b6b67e,0xbffee24fc0fc2881,2 +np.float64,0x7fb82aad0a305559,0x40861b614d901182,2 +np.float64,0xbfe5c3a4926b8749,0xbff23cd0ad144fe6,2 +np.float64,0x3fef47da373e8fb4,0x3fe5d1aaa4031993,2 +np.float64,0x7fc6a8c3872d5186,0x40862068f5ca84be,2 +np.float64,0x7fc0c2276221844e,0x40861dff2566d001,2 +np.float64,0x7fc9ce7d28339cf9,0x40862173541f84d1,2 +np.float64,0x3fce2c34933c5869,0x3fcb179428ad241d,2 +np.float64,0xbfcf864c293f0c98,0xbfd21872c4821cfc,2 +np.float64,0x3fc51fd1f82a3fa4,0x3fc38d4f1685c166,2 +np.float64,0xbfe2707b70a4e0f7,0xbfeb795fbd5bb444,2 +np.float64,0x46629b568cc54,0x46629b568cc54,2 +np.float64,0x7fe5f821f32bf043,0x40862b40c2cdea3f,2 +np.float64,0x3fedd2c9457ba592,0x3fe512ce92394526,2 +np.float64,0x7fe6dcb8ceadb971,0x40862b925a7dc05d,2 +np.float64,0x3fd1b983b4a37307,0x3fcf4ae2545cf64e,2 +np.float64,0xbfe1c93104639262,0xbfe9f7d28e4c0c82,2 +np.float64,0x995ebc2932bd8,0x995ebc2932bd8,2 +np.float64,0x800a4c3ee614987e,0x800a4c3ee614987e,2 +np.float64,0x3fbb58766e36b0f0,0x3fb9fb3b9810ec16,2 +np.float64,0xbfe36d636666dac7,0xbfede5080f69053c,2 +np.float64,0x3f4feee1003fddc2,0x3f4feae5f05443d1,2 +np.float64,0x3fed0b772ffa16ee,0x3fe4aafb924903c6,2 +np.float64,0x800bb3faef3767f6,0x800bb3faef3767f6,2 +np.float64,0x3fe285cda5e50b9c,0x3fdd3a58df06c427,2 +np.float64,0x7feb9d560bb73aab,0x40862d152362bb94,2 +np.float64,0x3fecd1f447f9a3e9,0x3fe48cc78288cb3f,2 +np.float64,0x3fca927b0c3524f6,0x3fc8250f49ba28df,2 +np.float64,0x7fcc19944e383328,0x40862221b02fcf43,2 +np.float64,0xbfd8ddf41db1bbe8,0xbfdf7b92073ff2fd,2 +np.float64,0x80006fe736e0dfcf,0x80006fe736e0dfcf,2 +np.float64,0x800bbeb66d577d6d,0x800bbeb66d577d6d,2 +np.float64,0xbfe4329353e86526,0xbfefeaf19ab92b42,2 +np.float64,0x2fad72805f5af,0x2fad72805f5af,2 +np.float64,0x3fe1b827aa637050,0x3fdc33bf46012c0d,2 +np.float64,0x3fc3f3f8e227e7f2,0x3fc28aeb86d65278,2 +np.float64,0x3fec018933780312,0x3fe41e619aa4285c,2 +np.float64,0xbfd92428e0b24852,0xbfdfeecb08d154df,2 +np.float64,0x2d7046845ae0a,0x2d7046845ae0a,2 +np.float64,0x7fde7fd2233cffa3,0x408628550f8a948f,2 +np.float64,0x8000a32cd241465a,0x8000a32cd241465a,2 +np.float64,0x8004267a45084cf5,0x8004267a45084cf5,2 +np.float64,0xbfe6b422556d6844,0xbff3c71f67661e6e,2 +np.float64,0x3fe3a37d922746fb,0x3fdea04e04d6195c,2 +np.float64,0xbfddcc54b53b98aa,0xbfe40d2389cdb848,2 +np.float64,0x3fe18b4b92a31697,0x3fdbf9e68cbf5794,2 +np.float64,0x7fc9c5b2ee338b65,0x408621709a17a47a,2 +np.float64,0x1ebd1ce03d7b,0x1ebd1ce03d7b,2 +np.float64,0x8008a6fc39d14df9,0x8008a6fc39d14df9,2 +np.float64,0x3fec11384c782270,0x3fe426bdaedd2965,2 +np.float64,0x3fefc28344ff8507,0x3fe60f75d34fc3d2,2 +np.float64,0xc35f379786be7,0xc35f379786be7,2 +np.float64,0x3feef51f4a7dea3e,0x3fe5a7b95d7786b5,2 +np.float64,0x3fec9b9f0379373e,0x3fe4702477abbb63,2 +np.float64,0x3fde94f8cdbd29f0,0x3fd8ff50f7df0a6f,2 +np.float64,0xbfed32d1cdfa65a4,0xc0037c1470f6f979,2 +np.float64,0x800d3ba44f5a7749,0x800d3ba44f5a7749,2 +np.float64,0x3fe3c56c8fe78ad9,0x3fdeca4eb9bb8918,2 +np.float64,0xbfe7c97242ef92e4,0xbff5c2950dfd6f69,2 +np.float64,0xbd9440057b288,0xbd9440057b288,2 +np.float64,0x7feb2fc111f65f81,0x40862cf524bd2001,2 +np.float64,0x800a431e2df4863d,0x800a431e2df4863d,2 +np.float64,0x80038a3b79e71478,0x80038a3b79e71478,2 +np.float64,0x80000c93d4601928,0x80000c93d4601928,2 +np.float64,0x7fe9fec022f3fd7f,0x40862c995db8ada0,2 +np.float64,0x3fead0129c35a025,0x3fe379d7a92c8f79,2 +np.float64,0x3fdd8cbaf7bb1974,0x3fd84b87ff0c26c7,2 +np.float64,0x3fe8fb7c60b1f6f9,0x3fe276d5339e7135,2 +np.float64,0x85a255e10b44b,0x85a255e10b44b,2 +np.float64,0xbfe507c23fea0f84,0xbff1212d2260022a,2 +np.float64,0x3fc5487c7b2a90f9,0x3fc3b03222d3d148,2 +np.float64,0x7fec0bdcb8f817b8,0x40862d34e8fd11e7,2 +np.float64,0xbfc5f34b4f2be698,0xbfc8146a899c7a0c,2 +np.float64,0xbfa2a49c14254940,0xbfa2fdab2eae3826,2 +np.float64,0x800ec52f15dd8a5e,0x800ec52f15dd8a5e,2 +np.float64,0xbfe3ba4b12a77496,0xbfeeab256b3e9422,2 +np.float64,0x80034d6c7ba69ada,0x80034d6c7ba69ada,2 +np.float64,0x7fd394d4202729a7,0x408624c98a216742,2 +np.float64,0xbfd4493a38289274,0xbfd865d67af2de91,2 +np.float64,0xe47d6203c8fad,0xe47d6203c8fad,2 +np.float64,0x98eb4e4b31d6a,0x98eb4e4b31d6a,2 +np.float64,0x4507fb128a100,0x4507fb128a100,2 +np.float64,0xbfc77032e42ee064,0xbfc9e36ab747a14d,2 +np.float64,0xa1f8a03b43f14,0xa1f8a03b43f14,2 +np.float64,0xbfc3d4da8527a9b4,0xbfc58c27af2476b0,2 +np.float64,0x3fc0eb7d6921d6fb,0x3fbfc858a077ed61,2 +np.float64,0x7fddb2e9403b65d2,0x4086281e98443709,2 +np.float64,0xbfa7ea62942fd4c0,0xbfa87dfd06b05d2a,2 +np.float64,0xbfe7d5c5426fab8a,0xbff5daa969c6d9e5,2 +np.float64,0x3fbf7cba0c3ef974,0x3fbdb23cd8fe875b,2 +np.float64,0x7fe92021eb324043,0x40862c53aee8b154,2 +np.float64,0x7fefbaa1827f7542,0x40862e3194737072,2 +np.float64,0x3fc6f82c402df059,0x3fc520432cbc533f,2 +np.float64,0x7fb37679a826ecf2,0x408619a5f857e27f,2 +np.float64,0x79ec1528f3d83,0x79ec1528f3d83,2 +np.float64,0x3fbefe1d0c3dfc3a,0x3fbd41650ba2c893,2 +np.float64,0x3fc3e5e11827cbc2,0x3fc27eb9b47c9c42,2 +np.float64,0x16aed1922d5db,0x16aed1922d5db,2 +np.float64,0x800124f7e58249f1,0x800124f7e58249f1,2 +np.float64,0x8004f7d12489efa3,0x8004f7d12489efa3,2 +np.float64,0x3fef80b8e27f0172,0x3fe5ee5fd43322c6,2 +np.float64,0xbfe7740c88eee819,0xbff51f823c8da14d,2 +np.float64,0xbfe6e1f1f6edc3e4,0xbff416bcb1302e7c,2 +np.float64,0x8001a2c4a7e3458a,0x8001a2c4a7e3458a,2 +np.float64,0x3fe861e155f0c3c2,0x3fe2201d3000c329,2 +np.float64,0x3fd00a101a201420,0x3fcca01087dbd728,2 +np.float64,0x7fdf0eb1133e1d61,0x4086287a327839b8,2 +np.float64,0x95e3ffdb2bc80,0x95e3ffdb2bc80,2 +np.float64,0x3fd87a1e8230f43d,0x3fd4ba1eb9be1270,2 +np.float64,0x3fedc4792afb88f2,0x3fe50b6529080f73,2 +np.float64,0x7fc9e81fa833d03e,0x4086217b428cc6ff,2 +np.float64,0xbfd21f1ba5a43e38,0xbfd54e048b988e09,2 +np.float64,0xbfbf52af5a3ea560,0xbfc0b4ab3b81fafc,2 +np.float64,0x7fe475f8e268ebf1,0x40862aaf14fee029,2 +np.float64,0x3fcf56899f3ead10,0x3fcc081de28ae9cf,2 +np.float64,0x917d407122fa8,0x917d407122fa8,2 +np.float64,0x22e23e3245c49,0x22e23e3245c49,2 +np.float64,0xbfeec2814f3d8503,0xc00a00ecca27b426,2 +np.float64,0xbfd97fee1c32ffdc,0xbfe04351dfe306ec,2 diff --git a/numpy/core/tests/data/umath-validation-set-log2.csv b/numpy/core/tests/data/umath-validation-set-log2.csv new file mode 100644 index 000000000000..179c6519d6d5 --- /dev/null +++ b/numpy/core/tests/data/umath-validation-set-log2.csv @@ -0,0 +1,1629 @@ +dtype,input,output,ulperrortol +np.float32,0x80000000,0xff800000,3 +np.float32,0x7f12870a,0x42fe63db,3 +np.float32,0x3ef29cf5,0xbf89eb12,3 +np.float32,0x3d6ba8fb,0xc083d26c,3 +np.float32,0x3d9907e8,0xc06f8230,3 +np.float32,0x4ee592,0xc2fd656e,3 +np.float32,0x58d8b1,0xc2fd0db3,3 +np.float32,0x7ba103,0xc2fc19aa,3 +np.float32,0x7f52e90e,0x42ff70e4,3 +np.float32,0x7fcb15,0xc2fc0132,3 +np.float32,0x7cb7129f,0x42f50855,3 +np.float32,0x9faba,0xc301ae59,3 +np.float32,0x7f300a,0xc2fc04b4,3 +np.float32,0x3f0bf047,0xbf5f10cb,3 +np.float32,0x2fb1fb,0xc2fed934,3 +np.float32,0x3eedb0d1,0xbf8db417,3 +np.float32,0x3d7a0b40,0xc0811638,3 +np.float32,0x2e0bac,0xc2fef334,3 +np.float32,0x6278c1,0xc2fcc1b9,3 +np.float32,0x7f61ab2e,0x42ffa2d9,3 +np.float32,0x8fe7c,0xc301d4be,3 +np.float32,0x3f25e6ee,0xbf203536,3 +np.float32,0x7efc78f0,0x42fdf5c0,3 +np.float32,0x6d7304,0xc2fc73a7,3 +np.float32,0x7f1a472a,0x42fe89ed,3 +np.float32,0x7dd029a6,0x42f96734,3 +np.float32,0x3e9b9327,0xbfdbf8f7,3 +np.float32,0x3f4eefc1,0xbe9d2942,3 +np.float32,0x7f5b9b64,0x42ff8ebc,3 +np.float32,0x3e458ee1,0xc017ed6e,3 +np.float32,0x3f7b766b,0xbcd35acf,3 +np.float32,0x3e616070,0xc00bc378,3 +np.float32,0x7f20e633,0x42fea8f8,3 +np.float32,0x3ee3b461,0xbf95a126,3 +np.float32,0x7e7722ba,0x42fbe5f8,3 +np.float32,0x3f0873d7,0xbf6861fa,3 +np.float32,0x7b4cb2,0xc2fc1ba3,3 +np.float32,0x3f0b6b02,0xbf60712e,3 +np.float32,0x9bff4,0xc301b6f2,3 +np.float32,0x3f07be25,0xbf6a4f0c,3 +np.float32,0x3ef10e57,0xbf8b1b75,3 +np.float32,0x46ad75,0xc2fdb6b1,3 +np.float32,0x3f7bc542,0xbcc4e3a9,3 +np.float32,0x3f6673d4,0xbe1b509c,3 +np.float32,0x7f19fe59,0x42fe8890,3 +np.float32,0x7f800000,0x7f800000,3 +np.float32,0x7f2fe696,0x42feead0,3 +np.float32,0x3dc9432d,0xc0563655,3 +np.float32,0x3ee47623,0xbf950446,3 +np.float32,0x3f1f8817,0xbf2eab51,3 +np.float32,0x7f220ec5,0x42feae44,3 +np.float32,0x2325e3,0xc2ffbab1,3 +np.float32,0x29dfc8,0xc2ff395a,3 +np.float32,0x7f524950,0x42ff6eb3,3 +np.float32,0x3e2234e0,0xc02a21c8,3 +np.float32,0x7f1c6f5a,0x42fe942f,3 +np.float32,0x3b6a61,0xc2fe36e7,3 +np.float32,0x3f1df90e,0xbf324ba9,3 +np.float32,0xb57f0,0xc3017f07,3 +np.float32,0x7d0eba,0xc2fc112e,3 +np.float32,0x403aa9,0xc2fdfd5c,3 +np.float32,0x3e74ecc7,0xc004155f,3 +np.float32,0x17509c,0xc30074f2,3 +np.float32,0x7f62196b,0x42ffa442,3 +np.float32,0x3ecef9a9,0xbfa7417a,3 +np.float32,0x7f14b158,0x42fe6eb1,3 +np.float32,0x3ede12be,0xbf9a40fe,3 +np.float32,0x42cfaa,0xc2fde03f,3 +np.float32,0x3f407b0f,0xbed2a6f5,3 +np.float32,0x7f7fffff,0x43000000,3 +np.float32,0x5467c6,0xc2fd3394,3 +np.float32,0x7ea6b80f,0x42fcc336,3 +np.float32,0x3f21e7b2,0xbf293704,3 +np.float32,0x3dc7e9eb,0xc056d542,3 +np.float32,0x7f3e6e67,0x42ff2571,3 +np.float32,0x3e3e809d,0xc01b4911,3 +np.float32,0x3f800000,0x0,3 +np.float32,0x3d8fd238,0xc0753d52,3 +np.float32,0x3f74aa65,0xbd85cd0e,3 +np.float32,0x7ec30305,0x42fd36ff,3 +np.float32,0x3e97bb93,0xbfe0971d,3 +np.float32,0x3e109d9c,0xc034bb1b,3 +np.float32,0x3f4a0b67,0xbeaed537,3 +np.float32,0x3f25a7aa,0xbf20c228,3 +np.float32,0x3ebc05eb,0xbfb8fd6b,3 +np.float32,0x3eebe749,0xbf8f18e5,3 +np.float32,0x3e9dc479,0xbfd96356,3 +np.float32,0x7f245200,0x42feb882,3 +np.float32,0x1573a8,0xc30093b5,3 +np.float32,0x3e66c4b9,0xc00994a6,3 +np.float32,0x3e73bffc,0xc0048709,3 +np.float32,0x3dfef8e5,0xc0405f16,3 +np.float32,0x403750,0xc2fdfd83,3 +np.float32,0x3ebedf17,0xbfb636a4,3 +np.float32,0x15cae6,0xc3008de2,3 +np.float32,0x3edf4d4e,0xbf993c24,3 +np.float32,0x3f7cc41e,0xbc963fb3,3 +np.float32,0x3e9e12a4,0xbfd907ee,3 +np.float32,0x7ded7b59,0x42f9c889,3 +np.float32,0x7f034878,0x42fe12b5,3 +np.float32,0x7ddce43f,0x42f9930b,3 +np.float32,0x3d82b257,0xc07e1333,3 +np.float32,0x3dae89c1,0xc0635dd4,3 +np.float32,0x6b1d00,0xc2fc8396,3 +np.float32,0x449a5a,0xc2fdccb3,3 +np.float32,0x4e89d2,0xc2fd68cb,3 +np.float32,0x7e1ae83f,0x42fa8cef,3 +np.float32,0x7e4bb22c,0x42fb572e,3 +np.float32,0x3de308ea,0xc04b1634,3 +np.float32,0x7f238c7a,0x42feb508,3 +np.float32,0x3f6c62a3,0xbdeb86f3,3 +np.float32,0x3e58cba6,0xc00f5908,3 +np.float32,0x7f7dd91f,0x42fff9c4,3 +np.float32,0x3d989376,0xc06fc88d,3 +np.float32,0x3dd013c5,0xc0532339,3 +np.float32,0x4b17e6,0xc2fd89ed,3 +np.float32,0x7f67f287,0x42ffb71e,3 +np.float32,0x3f69365e,0xbe09ba3c,3 +np.float32,0x3e4b8b21,0xc0152bf1,3 +np.float32,0x3a75b,0xc3032171,3 +np.float32,0x7f303676,0x42feec1f,3 +np.float32,0x7f6570e5,0x42ffaf18,3 +np.float32,0x3f5ed61e,0xbe4cf676,3 +np.float32,0x3e9b22f9,0xbfdc7e4f,3 +np.float32,0x2c095e,0xc2ff1428,3 +np.float32,0x3f1b17c1,0xbf391754,3 +np.float32,0x422dc6,0xc2fde746,3 +np.float32,0x3f677c8d,0xbe14b365,3 +np.float32,0x3ef85d0c,0xbf8597a9,3 +np.float32,0x3ecaaa6b,0xbfab2430,3 +np.float32,0x3f0607d1,0xbf6eff3d,3 +np.float32,0x3f011fdb,0xbf7cc50d,3 +np.float32,0x6ed7c1,0xc2fc6a4e,3 +np.float32,0x7ec2d1a2,0x42fd3644,3 +np.float32,0x3f75b7fe,0xbd7238a2,3 +np.float32,0x3ef2d146,0xbf89c344,3 +np.float32,0x7ec2cd27,0x42fd3633,3 +np.float32,0x7ee1e55a,0x42fda397,3 +np.float32,0x7f464d6a,0x42ff435c,3 +np.float32,0x7f469a93,0x42ff447b,3 +np.float32,0x7ece752f,0x42fd6121,3 +np.float32,0x2ed878,0xc2fee67b,3 +np.float32,0x75b23,0xc3021eff,3 +np.float32,0x3e0f4be4,0xc03593b8,3 +np.float32,0x2778e1,0xc2ff64fc,3 +np.float32,0x5fe2b7,0xc2fcd561,3 +np.float32,0x19b8a9,0xc30050ab,3 +np.float32,0x7df303e5,0x42f9d98d,3 +np.float32,0x608b8d,0xc2fcd051,3 +np.float32,0x588f46,0xc2fd1017,3 +np.float32,0x3eec6a11,0xbf8eb2a1,3 +np.float32,0x3f714121,0xbdaf4906,3 +np.float32,0x7f4f7b9e,0x42ff64c9,3 +np.float32,0x3c271606,0xc0d3b29c,3 +np.float32,0x3f002fe0,0xbf7f75f6,3 +np.float32,0x7efa4798,0x42fdef4f,3 +np.float32,0x3f61a865,0xbe3a601a,3 +np.float32,0x7e8087aa,0x42fc030d,3 +np.float32,0x3f70f0c7,0xbdb321ba,3 +np.float32,0x5db898,0xc2fce63f,3 +np.float32,0x7a965f,0xc2fc1fea,3 +np.float32,0x7f68b112,0x42ffb97c,3 +np.float32,0x7ef0ed3d,0x42fdd32d,3 +np.float32,0x7f3156a1,0x42fef0d3,3 +np.float32,0x3f1d405f,0xbf33fc6e,3 +np.float32,0x3e3494cf,0xc0203945,3 +np.float32,0x6018de,0xc2fcd3c1,3 +np.float32,0x623e49,0xc2fcc370,3 +np.float32,0x3ea29f0f,0xbfd3cad4,3 +np.float32,0xa514,0xc305a20c,3 +np.float32,0x3e1b2ab1,0xc02e3a8f,3 +np.float32,0x3f450b6f,0xbec1578f,3 +np.float32,0x7eb12908,0x42fcf015,3 +np.float32,0x3f10b720,0xbf52ab48,3 +np.float32,0x3e0a93,0xc2fe16f6,3 +np.float32,0x93845,0xc301cb96,3 +np.float32,0x7f4e9ce3,0x42ff61af,3 +np.float32,0x3f6d4296,0xbde09ceb,3 +np.float32,0x6ddede,0xc2fc70d0,3 +np.float32,0x3f4fb6fd,0xbe9a636d,3 +np.float32,0x3f6d08de,0xbde36c0b,3 +np.float32,0x3f56f057,0xbe8122ad,3 +np.float32,0x334e95,0xc2fea349,3 +np.float32,0x7efadbcd,0x42fdf104,3 +np.float32,0x3db02e88,0xc0628046,3 +np.float32,0x3f3309d1,0xbf041066,3 +np.float32,0x2d8722,0xc2fefb8f,3 +np.float32,0x7e926cac,0x42fc6356,3 +np.float32,0x3e3674ab,0xc01f452e,3 +np.float32,0x1b46ce,0xc3003afc,3 +np.float32,0x3f06a338,0xbf6d53fc,3 +np.float32,0x1b1ba7,0xc3003d46,3 +np.float32,0x319dfb,0xc2febc06,3 +np.float32,0x3e2f126a,0xc02315a5,3 +np.float32,0x3f40fe65,0xbed0af9e,3 +np.float32,0x3f1d842f,0xbf335d4b,3 +np.float32,0x3d044e4f,0xc09e78f8,3 +np.float32,0x7f272674,0x42fec51f,3 +np.float32,0x3cda6d8f,0xc0a753db,3 +np.float32,0x3eb92f12,0xbfbbccbb,3 +np.float32,0x7e4318f4,0x42fb3752,3 +np.float32,0x3c5890,0xc2fe2b6d,3 +np.float32,0x3d1993c9,0xc09796f8,3 +np.float32,0x7f18ef24,0x42fe8377,3 +np.float32,0x3e30c3a0,0xc0223244,3 +np.float32,0x3f27cd27,0xbf1c00ef,3 +np.float32,0x3f150957,0xbf47cd6c,3 +np.float32,0x7e7178a3,0x42fbd4d8,3 +np.float32,0x3f298db8,0xbf182ac3,3 +np.float32,0x7cb3be,0xc2fc1348,3 +np.float32,0x3ef64266,0xbf8729de,3 +np.float32,0x3eeb06ce,0xbf8fc8f2,3 +np.float32,0x3f406e36,0xbed2d845,3 +np.float32,0x7f1e1bd3,0x42fe9c0b,3 +np.float32,0x478dcc,0xc2fdad97,3 +np.float32,0x7f7937b5,0x42ffec2b,3 +np.float32,0x3f20f350,0xbf2b6624,3 +np.float32,0x7f13661a,0x42fe683c,3 +np.float32,0x208177,0xc2fff46b,3 +np.float32,0x263cfb,0xc2ff7c72,3 +np.float32,0x7f0bd28c,0x42fe4141,3 +np.float32,0x7230d8,0xc2fc5453,3 +np.float32,0x3f261bbf,0xbf1fbfb4,3 +np.float32,0x737b56,0xc2fc4c05,3 +np.float32,0x3ef88f33,0xbf857263,3 +np.float32,0x7e036464,0x42fa1352,3 +np.float32,0x4b5c4f,0xc2fd874d,3 +np.float32,0x3f77984d,0xbd454596,3 +np.float32,0x3f674202,0xbe162932,3 +np.float32,0x3e7157d9,0xc0057197,3 +np.float32,0x3f3f21da,0xbed7d861,3 +np.float32,0x7f1fb40f,0x42fea375,3 +np.float32,0x7ef0157f,0x42fdd096,3 +np.float32,0x3f71e88d,0xbda74962,3 +np.float32,0x3f174855,0xbf424728,3 +np.float32,0x3f3fdd2c,0xbed505d5,3 +np.float32,0x7b95d1,0xc2fc19ed,3 +np.float32,0x7f23f4e5,0x42feb6df,3 +np.float32,0x7d741925,0x42f7dcd6,3 +np.float32,0x60f81d,0xc2fccd14,3 +np.float32,0x3f17d267,0xbf40f6ae,3 +np.float32,0x3f036fc8,0xbf7636f8,3 +np.float32,0x167653,0xc30082b5,3 +np.float32,0x256d05,0xc2ff8c4f,3 +np.float32,0x3eccc63d,0xbfa93adb,3 +np.float32,0x7f6c91ea,0x42ffc5b2,3 +np.float32,0x2ee52a,0xc2fee5b3,3 +np.float32,0x3dc3579e,0xc058f80d,3 +np.float32,0x4c7170,0xc2fd7cc4,3 +np.float32,0x7f737f20,0x42ffdb03,3 +np.float32,0x3f2f9dbf,0xbf0b3119,3 +np.float32,0x3f4d0c54,0xbea3eec5,3 +np.float32,0x7e380862,0x42fb0c32,3 +np.float32,0x5d637f,0xc2fce8df,3 +np.float32,0x3f0aa623,0xbf627c27,3 +np.float32,0x3e4d5896,0xc0145b88,3 +np.float32,0x3f6cacdc,0xbde7e7ca,3 +np.float32,0x63a2c3,0xc2fcb90a,3 +np.float32,0x6c138c,0xc2fc7cfa,3 +np.float32,0x2063c,0xc303fb88,3 +np.float32,0x7e9e5a3e,0x42fc9d2f,3 +np.float32,0x56ec64,0xc2fd1ddd,3 +np.float32,0x7f1d6a35,0x42fe98cc,3 +np.float32,0x73dc96,0xc2fc4998,3 +np.float32,0x3e5d74e5,0xc00d6238,3 +np.float32,0x7f033cbb,0x42fe1273,3 +np.float32,0x3f5143fc,0xbe94e4e7,3 +np.float32,0x1d56d9,0xc3002010,3 +np.float32,0x2bf3e4,0xc2ff1591,3 +np.float32,0x3f2a6ef1,0xbf164170,3 +np.float32,0x3f33238b,0xbf03db58,3 +np.float32,0x22780e,0xc2ffc91a,3 +np.float32,0x7f00b873,0x42fe0425,3 +np.float32,0x3f7f6145,0xbb654706,3 +np.float32,0x7fc00000,0x7fc00000,3 +np.float32,0x63895a,0xc2fcb9c7,3 +np.float32,0x18a1b2,0xc30060a8,3 +np.float32,0x7e43c6a6,0x42fb39e3,3 +np.float32,0x78676e,0xc2fc2d30,3 +np.float32,0x3f16d839,0xbf435940,3 +np.float32,0x7eff78ba,0x42fdfe79,3 +np.float32,0x3f2e152c,0xbf0e6e54,3 +np.float32,0x3db20ced,0xc06186e1,3 +np.float32,0x3f0cd1d8,0xbf5cbf57,3 +np.float32,0x3fd7a8,0xc2fe01d2,3 +np.float32,0x3ebb075e,0xbfb9f816,3 +np.float32,0x7f94ef,0xc2fc026b,3 +np.float32,0x3d80ba0e,0xc07f7a2b,3 +np.float32,0x7f227e15,0x42feb03f,3 +np.float32,0x792264bf,0x42e6afcc,3 +np.float32,0x7f501576,0x42ff66ec,3 +np.float32,0x223629,0xc2ffcea3,3 +np.float32,0x40a79e,0xc2fdf87b,3 +np.float32,0x449483,0xc2fdccf2,3 +np.float32,0x3f4fa978,0xbe9a9382,3 +np.float32,0x7f148c53,0x42fe6df9,3 +np.float32,0x3ec98b3c,0xbfac2a98,3 +np.float32,0x3e4da320,0xc0143a0a,3 +np.float32,0x3d1d94bb,0xc09666d0,3 +np.float32,0x3c8e624e,0xc0bb155b,3 +np.float32,0x66a9af,0xc2fca2ef,3 +np.float32,0x3ec76ed7,0xbfae1c57,3 +np.float32,0x3f4b52f3,0xbeaa2b81,3 +np.float32,0x7e99bbb5,0x42fc8750,3 +np.float32,0x3f69a46b,0xbe0701be,3 +np.float32,0x3f775400,0xbd4ba495,3 +np.float32,0x131e56,0xc300be3c,3 +np.float32,0x3f30abb4,0xbf08fb10,3 +np.float32,0x7f7e528c,0x42fffb25,3 +np.float32,0x3eb89515,0xbfbc668a,3 +np.float32,0x7e9191b6,0x42fc5f02,3 +np.float32,0x7e80c7e9,0x42fc047e,3 +np.float32,0x3f77ef58,0xbd3d2995,3 +np.float32,0x7ddb1f8a,0x42f98d1b,3 +np.float32,0x7ebc6c4f,0x42fd1d9c,3 +np.float32,0x3f6638e0,0xbe1ccab8,3 +np.float32,0x7f4c45,0xc2fc0410,3 +np.float32,0x3e7d8aad,0xc000e414,3 +np.float32,0x3f4d148b,0xbea3d12e,3 +np.float32,0x3e98c45c,0xbfdf55f4,3 +np.float32,0x3d754c78,0xc081f8a9,3 +np.float32,0x17e4cf,0xc3006be3,3 +np.float32,0x7eb65814,0x42fd0563,3 +np.float32,0x3f65e0d8,0xbe1f0008,3 +np.float32,0x3e99541f,0xbfdea87e,3 +np.float32,0x3f3cb80e,0xbee13b27,3 +np.float32,0x3e99f0c0,0xbfddec3b,3 +np.float32,0x3f43903e,0xbec6ea66,3 +np.float32,0x7e211cd4,0x42faa9f2,3 +np.float32,0x824af,0xc301f971,3 +np.float32,0x3e16a56e,0xc030f56c,3 +np.float32,0x542b3b,0xc2fd35a6,3 +np.float32,0x3eeea2d1,0xbf8cf873,3 +np.float32,0x232e93,0xc2ffb9fa,3 +np.float32,0x3e8c52b9,0xbfef06aa,3 +np.float32,0x7f69c7e3,0x42ffbcef,3 +np.float32,0x3f573e43,0xbe801714,3 +np.float32,0x43b009,0xc2fdd69f,3 +np.float32,0x3ee571ab,0xbf943966,3 +np.float32,0x3ee3d5d8,0xbf958604,3 +np.float32,0x338b12,0xc2fe9fe4,3 +np.float32,0x29cb1f,0xc2ff3ac6,3 +np.float32,0x3f0892b4,0xbf680e7a,3 +np.float32,0x3e8c4f7f,0xbfef0ae9,3 +np.float32,0x7c9d3963,0x42f497e6,3 +np.float32,0x3f26ba84,0xbf1e5f59,3 +np.float32,0x3dd0acc0,0xc052df6f,3 +np.float32,0x3e43fbda,0xc018aa8c,3 +np.float32,0x3ec4fd0f,0xbfb0635d,3 +np.float32,0x3f52c8c6,0xbe8f8d85,3 +np.float32,0x3f5fdc5d,0xbe462fdb,3 +np.float32,0x3f461920,0xbebd6743,3 +np.float32,0x6161ff,0xc2fcc9ef,3 +np.float32,0x7f7ed306,0x42fffc9a,3 +np.float32,0x3d212263,0xc0955f46,3 +np.float32,0x3eca5826,0xbfab6f36,3 +np.float32,0x7d6317ac,0x42f7a77e,3 +np.float32,0x3eb02063,0xbfc50f60,3 +np.float32,0x7f71a6f8,0x42ffd565,3 +np.float32,0x1a3efe,0xc3004935,3 +np.float32,0x3dc599c9,0xc057e856,3 +np.float32,0x3f3e1301,0xbedbf205,3 +np.float32,0xf17d4,0xc301158d,3 +np.float32,0x3f615f84,0xbe3c3d85,3 +np.float32,0x3de63be1,0xc049cb77,3 +np.float32,0x3e8d2f51,0xbfede541,3 +np.float32,0x3a5cdd,0xc2fe441c,3 +np.float32,0x3f443ec0,0xbec4586a,3 +np.float32,0x3eacbd00,0xbfc8a5ad,3 +np.float32,0x3f600f6a,0xbe44df1b,3 +np.float32,0x5f77a6,0xc2fcd89c,3 +np.float32,0x476706,0xc2fdaf28,3 +np.float32,0x2f469,0xc3036fde,3 +np.float32,0x7dc4ba24,0x42f93d77,3 +np.float32,0x3e2d6080,0xc023fb9b,3 +np.float32,0x7e8d7135,0x42fc49c3,3 +np.float32,0x3f589065,0xbe77247b,3 +np.float32,0x3f59e210,0xbe6e2c05,3 +np.float32,0x7f51d388,0x42ff6d15,3 +np.float32,0x7d9a5fda,0x42f88a63,3 +np.float32,0x3e67d5bc,0xc00927ab,3 +np.float32,0x61d72c,0xc2fcc679,3 +np.float32,0x3ef3351d,0xbf897766,3 +np.float32,0x1,0xc3150000,3 +np.float32,0x7f653429,0x42ffae54,3 +np.float32,0x7e1ad3e5,0x42fa8c8e,3 +np.float32,0x3f4ca01d,0xbea57500,3 +np.float32,0x3f7606db,0xbd6ad13e,3 +np.float32,0x7ec4a27d,0x42fd3d1f,3 +np.float32,0x3efe4fd5,0xbf8138c7,3 +np.float32,0x77c2f1,0xc2fc3124,3 +np.float32,0x7e4d3251,0x42fb5c9a,3 +np.float32,0x3f543ac7,0xbe8a8154,3 +np.float32,0x7c3dbe29,0x42f322c4,3 +np.float32,0x408e01,0xc2fdf9a0,3 +np.float32,0x45069b,0xc2fdc829,3 +np.float32,0x3d7ecab7,0xc08037e8,3 +np.float32,0xf8c22,0xc3010a99,3 +np.float32,0x7f69af63,0x42ffbca2,3 +np.float32,0x7ec7d228,0x42fd48fe,3 +np.float32,0xff800000,0xffc00000,3 +np.float32,0xdd7c5,0xc301357c,3 +np.float32,0x143f38,0xc300a90e,3 +np.float32,0x7e65c176,0x42fbb01b,3 +np.float32,0x2c1a9e,0xc2ff1307,3 +np.float32,0x7f6e9224,0x42ffcbeb,3 +np.float32,0x3d32ab39,0xc0909a77,3 +np.float32,0x3e150b42,0xc031f22b,3 +np.float32,0x1f84b4,0xc300059a,3 +np.float32,0x3f71ce21,0xbda88c2a,3 +np.float32,0x2625c4,0xc2ff7e33,3 +np.float32,0x3dd0b293,0xc052dcdc,3 +np.float32,0x625c11,0xc2fcc290,3 +np.float32,0x3f610297,0xbe3e9f24,3 +np.float32,0x7ebdd5e5,0x42fd2320,3 +np.float32,0x3e883458,0xbff486ff,3 +np.float32,0x782313,0xc2fc2ed4,3 +np.float32,0x7f39c843,0x42ff132f,3 +np.float32,0x7f326aa7,0x42fef54d,3 +np.float32,0x4d2c71,0xc2fd75be,3 +np.float32,0x3f55747c,0xbe86409e,3 +np.float32,0x7f7f0867,0x42fffd34,3 +np.float32,0x321316,0xc2feb53f,3 +np.float32,0x3e1b37ed,0xc02e32b0,3 +np.float32,0x80edf,0xc301fd54,3 +np.float32,0x3f0b08ad,0xbf617607,3 +np.float32,0x7f3f4174,0x42ff28a2,3 +np.float32,0x3d79306d,0xc0813eb0,3 +np.float32,0x3f5f657a,0xbe49413d,3 +np.float32,0x3f56c63a,0xbe81b376,3 +np.float32,0x7f667123,0x42ffb24f,3 +np.float32,0x3f71021b,0xbdb24d43,3 +np.float32,0x7f434ab1,0x42ff380f,3 +np.float32,0x3dcae496,0xc055779c,3 +np.float32,0x3f5a7d88,0xbe6a0f5b,3 +np.float32,0x3cdf5c32,0xc0a64bf5,3 +np.float32,0x3e56222c,0xc0107d11,3 +np.float32,0x561a3a,0xc2fd24df,3 +np.float32,0x7ddd953c,0x42f9955a,3 +np.float32,0x7e35d839,0x42fb035c,3 +np.float32,0x3ec1816c,0xbfb3aeb2,3 +np.float32,0x7c87cfcd,0x42f42bc2,3 +np.float32,0xd9cd,0xc3053baf,3 +np.float32,0x3f388234,0xbef1e5b7,3 +np.float32,0x3edfcaca,0xbf98d47b,3 +np.float32,0x3ef28852,0xbf89fac8,3 +np.float32,0x7f7525df,0x42ffe001,3 +np.float32,0x7f6c33ef,0x42ffc48c,3 +np.float32,0x3ea4a881,0xbfd17e61,3 +np.float32,0x3f3e379f,0xbedb63c6,3 +np.float32,0x3f0524c1,0xbf717301,3 +np.float32,0x3db3e7f0,0xc06091d3,3 +np.float32,0x800000,0xc2fc0000,3 +np.float32,0x3f2f2897,0xbf0c27ce,3 +np.float32,0x7eb1776d,0x42fcf15c,3 +np.float32,0x3f039018,0xbf75dc37,3 +np.float32,0x3c4055,0xc2fe2c96,3 +np.float32,0x3f603653,0xbe43dea5,3 +np.float32,0x7f700d24,0x42ffd07c,3 +np.float32,0x3f4741a3,0xbeb918dc,3 +np.float32,0x3f5fe959,0xbe45da2d,3 +np.float32,0x3f3e4401,0xbedb33b1,3 +np.float32,0x7f0705ff,0x42fe2775,3 +np.float32,0x3ea85662,0xbfcd69b0,3 +np.float32,0x3f15f49f,0xbf458829,3 +np.float32,0x3f17c50e,0xbf411728,3 +np.float32,0x3e483f60,0xc016add2,3 +np.float32,0x3f1ab9e5,0xbf39f71b,3 +np.float32,0x3de0b6fb,0xc04c08fe,3 +np.float32,0x7e671225,0x42fbb452,3 +np.float32,0x80800000,0xffc00000,3 +np.float32,0xe2df3,0xc3012c9d,3 +np.float32,0x3ede1e3c,0xbf9a3770,3 +np.float32,0x3df2ffde,0xc044cfec,3 +np.float32,0x3eed8da5,0xbf8dcf6c,3 +np.float32,0x3ead15c3,0xbfc846e1,3 +np.float32,0x7ef3750a,0x42fddae4,3 +np.float32,0x7e6ab7c0,0x42fbbfe4,3 +np.float32,0x7ea4bbe5,0x42fcba5d,3 +np.float32,0x3f227706,0xbf27f0a1,3 +np.float32,0x3ef39bfd,0xbf89295a,3 +np.float32,0x3f289a20,0xbf1a3edd,3 +np.float32,0x7f225f82,0x42feafb4,3 +np.float32,0x768963,0xc2fc38bc,3 +np.float32,0x3f493c00,0xbeb1ccfc,3 +np.float32,0x3f4e7249,0xbe9ee9a7,3 +np.float32,0x1d0c3a,0xc30023c0,3 +np.float32,0x7f3c5f78,0x42ff1d6a,3 +np.float32,0xff7fffff,0xffc00000,3 +np.float32,0x3ee7896a,0xbf928c2a,3 +np.float32,0x3e788479,0xc002bd2e,3 +np.float32,0x3ee4df17,0xbf94af84,3 +np.float32,0x5e06d7,0xc2fce3d7,3 +np.float32,0x3d7b2776,0xc080e1dc,3 +np.float32,0x3e3d39d3,0xc01be7fd,3 +np.float32,0x7c81dece,0x42f40ab7,3 +np.float32,0x3f7d2085,0xbc856255,3 +np.float32,0x7f7f6627,0x42fffe44,3 +np.float32,0x7f5f2e94,0x42ff9aaa,3 +np.float32,0x7f5835f2,0x42ff8339,3 +np.float32,0x3f6a0e32,0xbe046580,3 +np.float32,0x7e16f586,0x42fa79dd,3 +np.float32,0x3f04a2f2,0xbf72dbc5,3 +np.float32,0x3f35e334,0xbefc7740,3 +np.float32,0x3f0d056e,0xbf5c3824,3 +np.float32,0x7ebeb95e,0x42fd2693,3 +np.float32,0x3c6192,0xc2fe2aff,3 +np.float32,0x3e892b4f,0xbff33958,3 +np.float32,0x3f61d694,0xbe3931df,3 +np.float32,0x29d183,0xc2ff3a56,3 +np.float32,0x7f0b0598,0x42fe3d04,3 +np.float32,0x7f743b28,0x42ffdd3d,3 +np.float32,0x3a2ed6,0xc2fe4663,3 +np.float32,0x3e27403a,0xc0274de8,3 +np.float32,0x3f58ee78,0xbe74a349,3 +np.float32,0x3eaa4b,0xc2fe0f92,3 +np.float32,0x3ecb613b,0xbfaa7de8,3 +np.float32,0x7f637d81,0x42ffa8c9,3 +np.float32,0x3f026e96,0xbf790c73,3 +np.float32,0x386cdf,0xc2fe5d0c,3 +np.float32,0x35abd1,0xc2fe8202,3 +np.float32,0x3eac3cd1,0xbfc92ee8,3 +np.float32,0x3f567869,0xbe82bf47,3 +np.float32,0x3f65c643,0xbe1faae6,3 +np.float32,0x7f5422b9,0x42ff752b,3 +np.float32,0x7c26e9,0xc2fc168c,3 +np.float32,0x7eff5cfd,0x42fdfe29,3 +np.float32,0x3f728e7f,0xbd9f6142,3 +np.float32,0x3f10fd43,0xbf51f874,3 +np.float32,0x7e7ada08,0x42fbf0fe,3 +np.float32,0x3e82a611,0xbffc37be,3 +np.float32,0xbf800000,0xffc00000,3 +np.float32,0x3dbe2e12,0xc05b711c,3 +np.float32,0x7e768fa9,0x42fbe440,3 +np.float32,0x5e44e8,0xc2fce1f0,3 +np.float32,0x7f25071a,0x42febbae,3 +np.float32,0x3f54db5e,0xbe885339,3 +np.float32,0x3f0f2c26,0xbf56a0b8,3 +np.float32,0x22f9a7,0xc2ffbe55,3 +np.float32,0x7ed63dcb,0x42fd7c77,3 +np.float32,0x7ea4fae2,0x42fcbb78,3 +np.float32,0x3f1d7766,0xbf337b47,3 +np.float32,0x7f16d59f,0x42fe7941,3 +np.float32,0x3f3a1bb6,0xbeeb855c,3 +np.float32,0x3ef57128,0xbf87c709,3 +np.float32,0xb24ff,0xc3018591,3 +np.float32,0x3ef99e27,0xbf84a983,3 +np.float32,0x3eac2ccf,0xbfc94013,3 +np.float32,0x3e9d3e1e,0xbfda00dc,3 +np.float32,0x718213,0xc2fc58c1,3 +np.float32,0x7edbf509,0x42fd8fea,3 +np.float32,0x70c7f1,0xc2fc5d80,3 +np.float32,0x3f7012f5,0xbdbdc6cd,3 +np.float32,0x12cba,0xc304c487,3 +np.float32,0x7f5d445d,0x42ff944c,3 +np.float32,0x7f3e30bd,0x42ff2481,3 +np.float32,0x63b110,0xc2fcb8a0,3 +np.float32,0x3f39f728,0xbeec1680,3 +np.float32,0x3f5bea58,0xbe6074b1,3 +np.float32,0x3f350749,0xbefff679,3 +np.float32,0x3e91ab2c,0xbfe81f3e,3 +np.float32,0x7ec53fe0,0x42fd3f6d,3 +np.float32,0x3f6cbbdc,0xbde72c8e,3 +np.float32,0x3f4df49f,0xbea0abcf,3 +np.float32,0x3e9c9638,0xbfdac674,3 +np.float32,0x7f3b82ec,0x42ff1a07,3 +np.float32,0x7f612a09,0x42ffa132,3 +np.float32,0x7ea26650,0x42fcafd3,3 +np.float32,0x3a615138,0xc122f26d,3 +np.float32,0x3f1108bd,0xbf51db39,3 +np.float32,0x6f80f6,0xc2fc65ea,3 +np.float32,0x3f7cb578,0xbc98ecb1,3 +np.float32,0x7f54d31a,0x42ff7790,3 +np.float32,0x196868,0xc3005532,3 +np.float32,0x3f01ee0a,0xbf7a7925,3 +np.float32,0x3e184013,0xc02ffb11,3 +np.float32,0xadde3,0xc3018ee3,3 +np.float32,0x252a91,0xc2ff9173,3 +np.float32,0x3f0382c2,0xbf7601a9,3 +np.float32,0x6d818c,0xc2fc7345,3 +np.float32,0x3bfbfd,0xc2fe2fdd,3 +np.float32,0x7f3cad19,0x42ff1e9a,3 +np.float32,0x4169a7,0xc2fdefdf,3 +np.float32,0x3f615d96,0xbe3c4a2b,3 +np.float32,0x3f036480,0xbf7656ac,3 +np.float32,0x7f5fbda3,0x42ff9c83,3 +np.float32,0x3d202d,0xc2fe21f1,3 +np.float32,0x3d0f5e5d,0xc09ac3e9,3 +np.float32,0x3f0fff6e,0xbf548142,3 +np.float32,0x7f11ed32,0x42fe60d2,3 +np.float32,0x3e6f856b,0xc00624b6,3 +np.float32,0x7f7c4dd7,0x42fff542,3 +np.float32,0x3e76fb86,0xc0034fa0,3 +np.float32,0x3e8a0d6e,0xbff209e7,3 +np.float32,0x3eacad19,0xbfc8b6ad,3 +np.float32,0xa7776,0xc3019cbe,3 +np.float32,0x3dc84d74,0xc056a754,3 +np.float32,0x3efb8052,0xbf834626,3 +np.float32,0x3f0e55fc,0xbf58cacc,3 +np.float32,0x7e0e71e3,0x42fa4efb,3 +np.float32,0x3ed5a800,0xbfa1639c,3 +np.float32,0x3f33335b,0xbf03babf,3 +np.float32,0x38cad7,0xc2fe5842,3 +np.float32,0x3bc21256,0xc0ecc927,3 +np.float32,0x3f09522d,0xbf660a19,3 +np.float32,0xcbd5d,0xc3015428,3 +np.float32,0x492752,0xc2fd9d42,3 +np.float32,0x3f2b9b32,0xbf13b904,3 +np.float32,0x6544ac,0xc2fcad09,3 +np.float32,0x52eb12,0xc2fd40b5,3 +np.float32,0x3f66a7c0,0xbe1a03e8,3 +np.float32,0x7ab289,0xc2fc1f41,3 +np.float32,0x62af5e,0xc2fcc020,3 +np.float32,0x7f73e9cf,0x42ffdc46,3 +np.float32,0x3e5eca,0xc2fe130e,3 +np.float32,0x3e3a10f4,0xc01d7602,3 +np.float32,0x3f04db46,0xbf723f0d,3 +np.float32,0x18fc4a,0xc3005b63,3 +np.float32,0x525bcb,0xc2fd45b6,3 +np.float32,0x3f6b9108,0xbdf5c769,3 +np.float32,0x3e992e8c,0xbfded5c5,3 +np.float32,0x7efea647,0x42fdfc18,3 +np.float32,0x7e8371db,0x42fc139e,3 +np.float32,0x3f397cfb,0xbeedfc69,3 +np.float32,0x7e46d233,0x42fb454a,3 +np.float32,0x7d5281ad,0x42f76f79,3 +np.float32,0x7f4c1878,0x42ff58a1,3 +np.float32,0x3e96ca5e,0xbfe1bd97,3 +np.float32,0x6a2743,0xc2fc8a3d,3 +np.float32,0x7f688781,0x42ffb8f8,3 +np.float32,0x7814b7,0xc2fc2f2d,3 +np.float32,0x3f2ffdc9,0xbf0a6756,3 +np.float32,0x3f766fa8,0xbd60fe24,3 +np.float32,0x4dc64e,0xc2fd7003,3 +np.float32,0x3a296f,0xc2fe46a8,3 +np.float32,0x3f2af942,0xbf15162e,3 +np.float32,0x7f702c32,0x42ffd0dc,3 +np.float32,0x7e61e318,0x42fba390,3 +np.float32,0x7f7d3bdb,0x42fff7fa,3 +np.float32,0x3ee87f3f,0xbf91c881,3 +np.float32,0x2bbc28,0xc2ff193c,3 +np.float32,0x3e01f918,0xc03e966e,3 +np.float32,0x7f0b39f4,0x42fe3e1a,3 +np.float32,0x3eaa4d64,0xbfcb4516,3 +np.float32,0x3e53901e,0xc0119a88,3 +np.float32,0x603cb,0xc3026957,3 +np.float32,0x7e81f926,0x42fc0b4d,3 +np.float32,0x5dab7c,0xc2fce6a6,3 +np.float32,0x3f46fefd,0xbeba1018,3 +np.float32,0x648448,0xc2fcb28a,3 +np.float32,0x3ec49470,0xbfb0c58b,3 +np.float32,0x3e8a5393,0xbff1ac2b,3 +np.float32,0x3f27ccfc,0xbf1c014e,3 +np.float32,0x3ed886e6,0xbf9eeca8,3 +np.float32,0x7cfbe06e,0x42f5f401,3 +np.float32,0x3f5aa7ba,0xbe68f229,3 +np.float32,0x9500d,0xc301c7e3,3 +np.float32,0x3f4861,0xc2fe0853,3 +np.float32,0x3e5ae104,0xc00e76f5,3 +np.float32,0x71253a,0xc2fc5b1e,3 +np.float32,0xcf7b8,0xc3014d9c,3 +np.float32,0x7f7edd2d,0x42fffcb7,3 +np.float32,0x3e9039ee,0xbfe9f5ab,3 +np.float32,0x2fd54e,0xc2fed712,3 +np.float32,0x3f600752,0xbe45147a,3 +np.float32,0x3f4da8f6,0xbea1bb5c,3 +np.float32,0x3f2d34a9,0xbf104bd9,3 +np.float32,0x3e1e66dd,0xc02c52d2,3 +np.float32,0x798276,0xc2fc2670,3 +np.float32,0xd55e2,0xc3014347,3 +np.float32,0x80000001,0xffc00000,3 +np.float32,0x3e7a5ead,0xc0020da6,3 +np.float32,0x7ec4c744,0x42fd3da9,3 +np.float32,0x597e00,0xc2fd085a,3 +np.float32,0x3dff6bf4,0xc0403575,3 +np.float32,0x5d6f1a,0xc2fce883,3 +np.float32,0x7e21faff,0x42faadea,3 +np.float32,0x3e570fea,0xc01016c6,3 +np.float32,0x28e6b6,0xc2ff4ab7,3 +np.float32,0x7e77062d,0x42fbe5a3,3 +np.float32,0x74cac4,0xc2fc43b0,3 +np.float32,0x3f707273,0xbdb93078,3 +np.float32,0x228e96,0xc2ffc737,3 +np.float32,0x686ac1,0xc2fc966b,3 +np.float32,0x3d76400d,0xc081cae8,3 +np.float32,0x3e9f502f,0xbfd7966b,3 +np.float32,0x3f6bc656,0xbdf32b1f,3 +np.float32,0x3edb828b,0xbf9c65d4,3 +np.float32,0x6c6e56,0xc2fc7a8e,3 +np.float32,0x3f04552e,0xbf73b48f,3 +np.float32,0x3f39cb69,0xbeecc457,3 +np.float32,0x7f681c44,0x42ffb7a3,3 +np.float32,0x7f5b44ee,0x42ff8d99,3 +np.float32,0x3e71430a,0xc005798d,3 +np.float32,0x3edcfde3,0xbf9b27c6,3 +np.float32,0x3f616a5a,0xbe3bf67f,3 +np.float32,0x3f523936,0xbe918548,3 +np.float32,0x3f39ce3a,0xbeecb925,3 +np.float32,0x3eac589a,0xbfc91120,3 +np.float32,0x7efc8d3d,0x42fdf5fc,3 +np.float32,0x5704b0,0xc2fd1d0f,3 +np.float32,0x7e7972e9,0x42fbecda,3 +np.float32,0x3eb0811c,0xbfc4aa13,3 +np.float32,0x7f1efcbb,0x42fea023,3 +np.float32,0x3e0b9e32,0xc037fa6b,3 +np.float32,0x7eef6a48,0x42fdce87,3 +np.float32,0x3cc0a373,0xc0ad20c0,3 +np.float32,0x3f2a75bb,0xbf1632ba,3 +np.float32,0x0,0xff800000,3 +np.float32,0x7ecdb6f4,0x42fd5e77,3 +np.float32,0x7f2e2dfd,0x42fee38d,3 +np.float32,0x3ee17f6e,0xbf976d8c,3 +np.float32,0x3f51e7ee,0xbe92a319,3 +np.float32,0x3f06942f,0xbf6d7d3c,3 +np.float32,0x3f7ba528,0xbccac6f1,3 +np.float32,0x3f413787,0xbecfd513,3 +np.float32,0x3e085e48,0xc03a2716,3 +np.float32,0x7e4c5e0e,0x42fb599c,3 +np.float32,0x306f76,0xc2fecdd4,3 +np.float32,0x7f5c2203,0x42ff9081,3 +np.float32,0x3d5355b4,0xc088da05,3 +np.float32,0x9a2a,0xc305bb4f,3 +np.float32,0x3db93a1f,0xc05de0db,3 +np.float32,0x4e50c6,0xc2fd6ae4,3 +np.float32,0x7ec4afed,0x42fd3d51,3 +np.float32,0x3a8f27,0xc2fe41a0,3 +np.float32,0x7f213caf,0x42feaa84,3 +np.float32,0x7e7b5f00,0x42fbf286,3 +np.float32,0x7e367194,0x42fb05ca,3 +np.float32,0x7f56e6de,0x42ff7ebd,3 +np.float32,0x3ed7383e,0xbfa00aef,3 +np.float32,0x7e844752,0x42fc184a,3 +np.float32,0x15157,0xc3049a19,3 +np.float32,0x3f78cd92,0xbd28824a,3 +np.float32,0x7ecddb16,0x42fd5ef9,3 +np.float32,0x3e479f16,0xc016f7d8,3 +np.float32,0x3f5cb418,0xbe5b2bd3,3 +np.float32,0x7c0934cb,0x42f2334e,3 +np.float32,0x3ebe5505,0xbfb6bc69,3 +np.float32,0x3eb1335a,0xbfc3eff5,3 +np.float32,0x3f2488a3,0xbf234444,3 +np.float32,0x642906,0xc2fcb52a,3 +np.float32,0x3da635fa,0xc067e15a,3 +np.float32,0x7e0d80db,0x42fa4a15,3 +np.float32,0x4f0b9d,0xc2fd640a,3 +np.float32,0x7e083806,0x42fa2df8,3 +np.float32,0x7f77f8c6,0x42ffe877,3 +np.float32,0x3e7bb46a,0xc0018ff5,3 +np.float32,0x3f06eb2e,0xbf6c8eca,3 +np.float32,0x7eae8f7c,0x42fce52a,3 +np.float32,0x3de481a0,0xc04a7d7f,3 +np.float32,0x3eed4311,0xbf8e096f,3 +np.float32,0x3f7b0300,0xbce8903d,3 +np.float32,0x3811b,0xc30330dd,3 +np.float32,0x3eb6f8e1,0xbfbe04bc,3 +np.float32,0x3ec35210,0xbfb1f55a,3 +np.float32,0x3d386916,0xc08f24a5,3 +np.float32,0x3f1fa197,0xbf2e704d,3 +np.float32,0x7f2020a5,0x42fea56a,3 +np.float32,0x7e1ea53f,0x42fa9e8c,3 +np.float32,0x3f148903,0xbf490bf9,3 +np.float32,0x3f2f56a0,0xbf0bc6c9,3 +np.float32,0x7da9fc,0xc2fc0d9b,3 +np.float32,0x3d802134,0xc07fe810,3 +np.float32,0x3f6cb927,0xbde74e57,3 +np.float32,0x7e05b125,0x42fa2023,3 +np.float32,0x3f3307f9,0xbf041433,3 +np.float32,0x5666bf,0xc2fd2250,3 +np.float32,0x3f51c93b,0xbe930f28,3 +np.float32,0x3eb5dcfe,0xbfbf241e,3 +np.float32,0xb2773,0xc301853f,3 +np.float32,0x7f4dee96,0x42ff5f3f,3 +np.float32,0x3e3f5c33,0xc01adee1,3 +np.float32,0x3f2ed29a,0xbf0cdd4a,3 +np.float32,0x3e3c01ef,0xc01c80ab,3 +np.float32,0x3ec2236e,0xbfb31458,3 +np.float32,0x7e841dc4,0x42fc1761,3 +np.float32,0x3df2cd8e,0xc044e30c,3 +np.float32,0x3f010901,0xbf7d0670,3 +np.float32,0x3c05ceaa,0xc0ddf39b,3 +np.float32,0x3f517226,0xbe944206,3 +np.float32,0x3f23c83d,0xbf24f522,3 +np.float32,0x7fc9da,0xc2fc0139,3 +np.float32,0x7f1bde53,0x42fe9181,3 +np.float32,0x3ea3786c,0xbfd2d4a5,3 +np.float32,0x3e83a71b,0xbffacdd2,3 +np.float32,0x3f6f0d4f,0xbdca61d5,3 +np.float32,0x7f5ab613,0x42ff8bb7,3 +np.float32,0x3ab1ec,0xc2fe3fea,3 +np.float32,0x4fbf58,0xc2fd5d82,3 +np.float32,0x3dea141b,0xc0484403,3 +np.float32,0x7d86ad3b,0x42f8258f,3 +np.float32,0x7f345315,0x42fefd29,3 +np.float32,0x3f3752fe,0xbef6a780,3 +np.float32,0x64830d,0xc2fcb293,3 +np.float32,0x3d9dc1eb,0xc06cb32a,3 +np.float32,0x3f2f935a,0xbf0b46f6,3 +np.float32,0xb90a4,0xc30177e3,3 +np.float32,0x4111dd,0xc2fdf3c1,3 +np.float32,0x3d4cd078,0xc08a4c68,3 +np.float32,0x3e95c3f1,0xbfe30011,3 +np.float32,0x3ec9f356,0xbfabcb4e,3 +np.float32,0x1b90d5,0xc3003717,3 +np.float32,0xee70f,0xc3011a3e,3 +np.float32,0x7fa00000,0x7fe00000,3 +np.float32,0x3f74cdb6,0xbd8422af,3 +np.float32,0x3d9b56fe,0xc06e2037,3 +np.float32,0x3f1853df,0xbf3fbc40,3 +np.float32,0x7d86a011,0x42f82547,3 +np.float32,0x3dff9629,0xc0402634,3 +np.float32,0x46f8c9,0xc2fdb39f,3 +np.float32,0x3e9b410b,0xbfdc5a87,3 +np.float32,0x3f5aed42,0xbe671cac,3 +np.float32,0x3b739886,0xc101257f,3 +np.float64,0x3fe2f58d6565eb1b,0xbfe82a641138e19a,2 +np.float64,0x3fee7f0642fcfe0d,0xbfb1c702f6974932,2 +np.float64,0x25b71f244b6e5,0xc090030d3b3c5d2b,2 +np.float64,0x8c9cc8e1193b,0xc0900b752a678fa8,2 +np.float64,0x3fd329b5d326536c,0xbffbd607f6db945c,2 +np.float64,0x3fb5109b3a2a2136,0xc00cd36bd15dfb18,2 +np.float64,0x3fd5393ae12a7276,0xbff97a7e4a157154,2 +np.float64,0x3fd374d1b926e9a3,0xbffb7c3e1a3a7ed3,2 +np.float64,0x3fe2c7f4e2658fea,0xbfe899f15ca78fcb,2 +np.float64,0x7fe3d6b81ee7ad6f,0x408ffa7b63d407ee,2 +np.float64,0x3fe086d097e10da1,0xbfee81456ce8dd03,2 +np.float64,0x7fd374a64ca6e94c,0x408ff241c7306d39,2 +np.float64,0x3fc0709a5b20e135,0xc007afdede31b29c,2 +np.float64,0x3fd4218f4b28431f,0xbffab2c696966e2d,2 +np.float64,0x143134c828628,0xc09006a8372c4d8a,2 +np.float64,0x3f8bd0aa0037a154,0xc018cf0e8b9c3107,2 +np.float64,0x7fe0ce905ee19d20,0x408ff8915e71bd67,2 +np.float64,0x3fda0f5f32b41ebe,0xbff4bd5e0869e820,2 +np.float64,0x7fe9ae63d0b35cc7,0x408ffd760ca4f292,2 +np.float64,0x3fe75abd9eeeb57b,0xbfdd1476fc8b3089,2 +np.float64,0x786c3110f0d87,0xc08ff8b44cedbeea,2 +np.float64,0x22c5fe80458d,0xc09013853591c2f2,2 +np.float64,0x3fdc250797384a0f,0xbff2f6a02c961f0b,2 +np.float64,0x3fa2b367b02566cf,0xc013199238485054,2 +np.float64,0x3fd26a910ca4d522,0xbffcc0e2089b1c0c,2 +np.float64,0x8068d3b300d1b,0xc08ff7f690210aac,2 +np.float64,0x3fe663bfa9ecc77f,0xbfe07cd95a43a5ce,2 +np.float64,0x3fd0ddb07321bb61,0xbffec886665e895e,2 +np.float64,0x3f91c730b0238e61,0xc0176452badc8d22,2 +np.float64,0x4dd10d309ba22,0xc08ffdbe738b1d8d,2 +np.float64,0x7fe322afa4a6455e,0x408ffa10c038f9de,2 +np.float64,0x7fdf7f7c42befef8,0x408ff7d147ddaad5,2 +np.float64,0x7fd673f386ace7e6,0x408ff3e920d00eef,2 +np.float64,0x3feaebfcadb5d7f9,0xbfcfe8ec27083478,2 +np.float64,0x3fdc6dc23738db84,0xbff2bb46794f07b8,2 +np.float64,0xcd8819599b103,0xc08ff288c5b2cf0f,2 +np.float64,0xfda00e77fb402,0xc08ff01b895d2236,2 +np.float64,0x840b02ff08161,0xc08ff7a41e41114c,2 +np.float64,0x3fbdce3a383b9c74,0xc008d1e61903a289,2 +np.float64,0x3fd24ed3c4a49da8,0xbffce3c12136b6d3,2 +np.float64,0x3fe8d0834131a107,0xbfd77b194e7051d4,2 +np.float64,0x3fdd0cb11aba1962,0xbff23b9dbd554455,2 +np.float64,0x1a32d97e3465c,0xc090052781a37271,2 +np.float64,0x3fdb09d2b1b613a5,0xbff3e396b862bd83,2 +np.float64,0x3fe04c848aa09909,0xbfef2540dd90103a,2 +np.float64,0x3fce0c48613c1891,0xc000b9f76877d744,2 +np.float64,0x3fc37109a226e213,0xc005c05d8b2b9a2f,2 +np.float64,0x81cf3837039e7,0xc08ff7d686517dff,2 +np.float64,0xd9342c29b2686,0xc08ff1e591c9a895,2 +np.float64,0x7fec731b0638e635,0x408ffea4884550a9,2 +np.float64,0x3fba0fc138341f82,0xc00a5e839b085f64,2 +np.float64,0x7fdda893b03b5126,0x408ff71f7c5a2797,2 +np.float64,0xd2a4bb03a5498,0xc08ff2402f7a907c,2 +np.float64,0x3fea61fb0d34c3f6,0xbfd1d293fbe76183,2 +np.float64,0x3fed5cf486fab9e9,0xbfbfc2e01a7ffff1,2 +np.float64,0x3fcbabc2bf375785,0xc001ad7750c9dbdf,2 +np.float64,0x3fdb5fff53b6bfff,0xbff39a7973a0c6a5,2 +np.float64,0x7feef05a00bde0b3,0x408fff9c5cbc8651,2 +np.float64,0xb1cf24f1639e5,0xc08ff434de10fffb,2 +np.float64,0x3fa583989c2b0731,0xc0124a8a3bbf18ce,2 +np.float64,0x7feae90bf9f5d217,0x408ffe002e7bbbea,2 +np.float64,0x3fe9ef41c4b3de84,0xbfd367878ae4528e,2 +np.float64,0x9be24ce337c4a,0xc08ff5b9b1c31cf9,2 +np.float64,0x3fe916894cb22d13,0xbfd677f915d58503,2 +np.float64,0x3fec1bab20f83756,0xbfc7f2777aabe8ee,2 +np.float64,0x3feaabf2873557e5,0xbfd0d11f28341233,2 +np.float64,0x3fd4d3c3b529a787,0xbff9e9e47acc8ca9,2 +np.float64,0x3fe4cfe96c699fd3,0xbfe3dc53fa739169,2 +np.float64,0xccfdb97399fb7,0xc08ff2908d893400,2 +np.float64,0x3fec7598be78eb31,0xbfc5a750f8f3441a,2 +np.float64,0x355be5fc6ab7e,0xc090010ca315b50b,2 +np.float64,0x3fba9f9074353f21,0xc00a1f80eaf5e581,2 +np.float64,0x7fdcaff189395fe2,0x408ff6bd1c5b90d9,2 +np.float64,0x3fd94d3b64b29a77,0xbff56be1b43d25f3,2 +np.float64,0x4e5f29949cbe6,0xc08ffda972da1d73,2 +np.float64,0x3fe654e2d9aca9c6,0xbfe09b88dcd8f15d,2 +np.float64,0x7fdc130190b82602,0x408ff67d496c1a27,2 +np.float64,0x3fbcd4701e39a8e0,0xc009343e36627e80,2 +np.float64,0x7fdaa4d38f3549a6,0x408ff5e2c6d8678f,2 +np.float64,0x3febe95e5237d2bd,0xbfc93e16d453fe3a,2 +np.float64,0x9ef5ca553deba,0xc08ff57ff4f7883d,2 +np.float64,0x7fe878e91170f1d1,0x408ffce795868fc8,2 +np.float64,0x3fe63dff466c7bff,0xbfe0caf2b79c9e5f,2 +np.float64,0x6561446ccac29,0xc08ffab0e383834c,2 +np.float64,0x30c6c2ae618d9,0xc09001914b30381b,2 +np.float64,0x7ff0000000000000,0x7ff0000000000000,2 +np.float64,0x3fe5c9daf1ab93b6,0xbfe1be81baf4dbdb,2 +np.float64,0x3fe0a03e24a1407c,0xbfee3a73c4c0e8f8,2 +np.float64,0xff2a2cf3fe546,0xc08ff009a7e6e782,2 +np.float64,0x7fcf0332213e0663,0x408fefa36235e210,2 +np.float64,0x3fb612affc2c2560,0xc00c494be9c8c33b,2 +np.float64,0x3fd2b259702564b3,0xbffc67967f077e75,2 +np.float64,0x7fcb63685d36c6d0,0x408fee343343f913,2 +np.float64,0x3fe369f1d5a6d3e4,0xbfe71251139939ad,2 +np.float64,0x3fdd17c618ba2f8c,0xbff232d11c986251,2 +np.float64,0x3f92cc8040259901,0xc01711d8e06b52ee,2 +np.float64,0x69a81dc2d3504,0xc08ffa36cdaf1141,2 +np.float64,0x3fea0fad99b41f5b,0xbfd2f4625a652645,2 +np.float64,0xd1cd5799a39ab,0xc08ff24c02b90d26,2 +np.float64,0x324e59ce649cc,0xc0900163ad091c76,2 +np.float64,0x3fc3d460a227a8c1,0xc00585f903dc7a7f,2 +np.float64,0xa7185ec74e30c,0xc08ff4ec7d65ccd9,2 +np.float64,0x3fa254eaac24a9d5,0xc01337053963321a,2 +np.float64,0x3feaeb112435d622,0xbfcfef3be17f81f6,2 +np.float64,0x60144c3ac028a,0xc08ffb4f8eb94595,2 +np.float64,0x7fa4d2ec6829a5d8,0x408fdb0a9670ab83,2 +np.float64,0x3fed1372f97a26e6,0xbfc1b1fe50d48a55,2 +np.float64,0x3fd5ade5972b5bcb,0xbff8fcf28f525031,2 +np.float64,0x7fe72e335bee5c66,0x408ffc4759236437,2 +np.float64,0x7fdfafab143f5f55,0x408ff7e2e22a8129,2 +np.float64,0x3fe90d0db9321a1b,0xbfd69ae5fe10eb9e,2 +np.float64,0x7fe20a59072414b1,0x408ff962a2492484,2 +np.float64,0x3fed853690bb0a6d,0xbfbdc9dc5f199d2b,2 +np.float64,0x3fd709d469ae13a9,0xbff795a218deb700,2 +np.float64,0x3fe21c35f5e4386c,0xbfea47d71789329b,2 +np.float64,0x9ea5ec053d4be,0xc08ff585c2f6b7a3,2 +np.float64,0x3fc0580f9e20b01f,0xc007c1268f49d037,2 +np.float64,0xd99127abb3225,0xc08ff1e0a1ff339d,2 +np.float64,0x3fdc8c9bbfb91937,0xbff2a2478354effb,2 +np.float64,0x3fe15fc6b162bf8d,0xbfec323ac358e008,2 +np.float64,0xffefffffffffffff,0x7ff8000000000000,2 +np.float64,0x3fee341afb3c6836,0xbfb556b6faee9a84,2 +np.float64,0x3fe4b64c56296c99,0xbfe4154835ad2afe,2 +np.float64,0x85de22810bbc5,0xc08ff77b914fe5b5,2 +np.float64,0x3fd22c72e3a458e6,0xbffd0f4269d20bb9,2 +np.float64,0xc090e5218123,0xc09009a4a65a8a8f,2 +np.float64,0x7fd9641692b2c82c,0x408ff5547782bdfc,2 +np.float64,0x3fd9b9cb28b37396,0xbff509a8fb59a9f1,2 +np.float64,0x3fcd2726f93a4e4e,0xc001135059a22117,2 +np.float64,0x3fa4b493d4296928,0xc0128323c7a55f4a,2 +np.float64,0x47455e788e8ac,0xc08ffec2101c1e82,2 +np.float64,0x3fe0d7e2e261afc6,0xbfeda0f1e2d0f4bd,2 +np.float64,0x3fe860fc5b70c1f9,0xbfd91dc42eaf72c2,2 +np.float64,0xa5d7805b4baf0,0xc08ff502bc819ff6,2 +np.float64,0xd83395b1b0673,0xc08ff1f33c3f94c2,2 +np.float64,0x3f865972e02cb2e6,0xc01a1243651565c8,2 +np.float64,0x52fc6952a5f8e,0xc08ffd006b158179,2 +np.float64,0x7fecac6c793958d8,0x408ffebbb1c09a70,2 +np.float64,0x7fe621ff606c43fe,0x408ffbbeb2b1473a,2 +np.float64,0x3fdb9f3f9db73e7f,0xbff365610c52bda7,2 +np.float64,0x7feab92992757252,0x408ffdeb92a04813,2 +np.float64,0xcc46c79f988d9,0xc08ff29adf03fb7c,2 +np.float64,0x3fe3156a03262ad4,0xbfe7dd0f598781c7,2 +np.float64,0x3fc00e3a61201c75,0xc007f5c121a87302,2 +np.float64,0x3fdce8e9f739d1d4,0xbff2581d41ef50ef,2 +np.float64,0x0,0xfff0000000000000,2 +np.float64,0x7d373ac4fa6e8,0xc08ff840fa8beaec,2 +np.float64,0x3fee41e0653c83c1,0xbfb4ae786f2a0d54,2 +np.float64,0x3ff0000000000000,0x0,2 +np.float64,0x7feca6fff9794dff,0x408ffeb982a70556,2 +np.float64,0x7fc532716d2a64e2,0x408feb3f0f6c095b,2 +np.float64,0x3fe4ec2954a9d853,0xbfe39dd44aa5a040,2 +np.float64,0x7fd3321d52a6643a,0x408ff21a0ab9cd85,2 +np.float64,0x7fd8f1b2dfb1e365,0x408ff52001fa7922,2 +np.float64,0x3fee5e58cabcbcb2,0xbfb3539734a24d8b,2 +np.float64,0x3feebf6e7dfd7edd,0xbfad7c648f025102,2 +np.float64,0x6008026ec0101,0xc08ffb5108b54a93,2 +np.float64,0x3fea06f5e2340dec,0xbfd3134a48283360,2 +np.float64,0x41cad13c8395b,0xc08fffae654b2426,2 +np.float64,0x7fedb5c9353b6b91,0x408fff249f1f32b6,2 +np.float64,0xe00c5af9c018c,0xc08ff189e68c655f,2 +np.float64,0x7feac398ddf58731,0x408ffdf01374de9f,2 +np.float64,0x3fed21127c7a4225,0xbfc15b8cf55628fa,2 +np.float64,0x3fd3446711a688ce,0xbffbb5f7252a9fa3,2 +np.float64,0x7fe75fa07a6ebf40,0x408ffc5fdb096018,2 +np.float64,0x3feeb1618cbd62c3,0xbfaece3bd0863070,2 +np.float64,0x7f5226e180244dc2,0x408fb174d506e52f,2 +np.float64,0x3fcd67deca3acfbe,0xc000f9cd7a490749,2 +np.float64,0xdc6f30efb8de6,0xc08ff1b9f2a22d2e,2 +np.float64,0x9c14931338293,0xc08ff5b5f975ec5d,2 +np.float64,0x7fe93e802df27cff,0x408ffd4354eba0e0,2 +np.float64,0x3feb92ae5077255d,0xbfcb7f2084e44dbb,2 +np.float64,0xd78dbfddaf1b8,0xc08ff1fc19fa5a13,2 +np.float64,0x7fe14c301fa2985f,0x408ff8e666cb6592,2 +np.float64,0xbda3d8b77b47b,0xc08ff37689f4b2e5,2 +np.float64,0x8a42953b14853,0xc08ff71c2db3b8cf,2 +np.float64,0x7fe4ca7e186994fb,0x408ffb05e94254a7,2 +np.float64,0x7fe92ffc5e325ff8,0x408ffd3cb0265b12,2 +np.float64,0x91b262912364d,0xc08ff681619be214,2 +np.float64,0x33fe2b0667fc6,0xc0900132f3fab55e,2 +np.float64,0x3fde10e9183c21d2,0xbff17060fb4416c7,2 +np.float64,0xb6b811cb6d702,0xc08ff3e46303b541,2 +np.float64,0x3fe4a7bda0a94f7b,0xbfe435c6481cd0e3,2 +np.float64,0x7fd9fe6057b3fcc0,0x408ff599c79a822c,2 +np.float64,0x3fef44bf917e897f,0xbfa11484e351a6e9,2 +np.float64,0x3fe57d701daafae0,0xbfe2618ab40fc01b,2 +np.float64,0x7fe52d2adbaa5a55,0x408ffb3c2fb1c99d,2 +np.float64,0xb432f66d6865f,0xc08ff40d6b4084fe,2 +np.float64,0xbff0000000000000,0x7ff8000000000000,2 +np.float64,0x7fecd2292bf9a451,0x408ffecad860de6f,2 +np.float64,0x3fddd2ae153ba55c,0xbff1a059adaca33e,2 +np.float64,0x3fee55d6e5bcabae,0xbfb3bb1c6179d820,2 +np.float64,0x7fc1d0085623a010,0x408fe93d16ada7a7,2 +np.float64,0x829b000105360,0xc08ff7c47629a68f,2 +np.float64,0x7fe1e0257523c04a,0x408ff94782cf0717,2 +np.float64,0x7fd652f9ad2ca5f2,0x408ff3d820ec892e,2 +np.float64,0x3fef2246203e448c,0xbfa444ab6209d8cd,2 +np.float64,0x3fec6c0ae178d816,0xbfc5e559ebd4e790,2 +np.float64,0x3fe6ddfee92dbbfe,0xbfdf06dd7d3fa7a8,2 +np.float64,0x3fb7fbcbea2ff798,0xc00b5404d859d148,2 +np.float64,0x7feb9a154d37342a,0x408ffe4b26c29e55,2 +np.float64,0x3fe4db717aa9b6e3,0xbfe3c2c6b3ef13bc,2 +np.float64,0x3fbae17dda35c2fc,0xc00a030f7f4b37e7,2 +np.float64,0x7fd632b9082c6571,0x408ff3c76826ef19,2 +np.float64,0x7fc4184a15283093,0x408feaa14adf00be,2 +np.float64,0x3fe052d19920a5a3,0xbfef136b5df81a3e,2 +np.float64,0x7fe38b872b67170d,0x408ffa4f51aafc86,2 +np.float64,0x3fef9842d03f3086,0xbf92d3d2a21d4be2,2 +np.float64,0x9cea662139d4d,0xc08ff5a634810daa,2 +np.float64,0x3fe35f0855e6be11,0xbfe72c4b564e62aa,2 +np.float64,0x3fecee3d3779dc7a,0xbfc29ee942f8729e,2 +np.float64,0x3fe7903fd72f2080,0xbfdc41db9b5f4048,2 +np.float64,0xb958889572b11,0xc08ff3ba366cf84b,2 +np.float64,0x3fcb3a67c53674d0,0xc001dd21081ad1ea,2 +np.float64,0xe3b1b53fc7637,0xc08ff15a3505e1ce,2 +np.float64,0xe5954ae9cb2aa,0xc08ff141cbbf0ae4,2 +np.float64,0x3fe394af74e7295f,0xbfe6ad1d13f206e8,2 +np.float64,0x7fe21dd704643bad,0x408ff96f13f80c1a,2 +np.float64,0x3fd23a7cf02474fa,0xbffcfd7454117a05,2 +np.float64,0x7fe257515e24aea2,0x408ff99378764d52,2 +np.float64,0x7fe4c5d0a6e98ba0,0x408ffb03503cf939,2 +np.float64,0x3fadc2c1603b8583,0xc0106b2c17550e3a,2 +np.float64,0x3fc0f7f02421efe0,0xc007525ac446864c,2 +np.float64,0x3feaf0b27275e165,0xbfcfc8a03eaa32ad,2 +np.float64,0x5ce7503cb9ceb,0xc08ffbb2de365fa8,2 +np.float64,0x2a0014f654003,0xc090026e41761a0d,2 +np.float64,0x7fe2c848a8e59090,0x408ff9d9b723ee89,2 +np.float64,0x7f66f54bc02dea97,0x408fbc2ae0ec5623,2 +np.float64,0xa35a890146b6,0xc0900a97b358ddbd,2 +np.float64,0x7fee267ded7c4cfb,0x408fff501560c9f5,2 +np.float64,0x3fe07c328520f865,0xbfee9ef7c3435b58,2 +np.float64,0x3fe67122cf6ce246,0xbfe06147001932ba,2 +np.float64,0x3fdacc8925359912,0xbff41824cece219e,2 +np.float64,0xffa3047fff461,0xc08ff00431ec9be3,2 +np.float64,0x3e1af43e7c35f,0xc090002c6573d29b,2 +np.float64,0x86fa94590df53,0xc08ff7632525ed92,2 +np.float64,0x7fec4c76227898eb,0x408ffe94d032c657,2 +np.float64,0x7fe2274ce1e44e99,0x408ff975194cfdff,2 +np.float64,0x7fe670e1b4ace1c2,0x408ffbe78cc451de,2 +np.float64,0x7fe853871db0a70d,0x408ffcd5e6a6ff47,2 +np.float64,0x3fcbf265db37e4cc,0xc0019026336e1176,2 +np.float64,0x3fef033cef3e067a,0xbfa726712eaae7f0,2 +np.float64,0x5d74973abae94,0xc08ffba15e6bb992,2 +np.float64,0x7fdd9c99b6bb3932,0x408ff71ad24a7ae0,2 +np.float64,0xbdc8e09b7b91c,0xc08ff3744939e9a3,2 +np.float64,0xdbfcff71b7fa0,0xc08ff1bfeecc9dfb,2 +np.float64,0xf9b38cf5f3672,0xc08ff0499af34a43,2 +np.float64,0x3fea820aa6b50415,0xbfd162a38e1927b1,2 +np.float64,0x3fe67f59a12cfeb3,0xbfe04412adca49dc,2 +np.float64,0x3feb301d9c76603b,0xbfce17e6edeb92d5,2 +np.float64,0x828ce00b0519c,0xc08ff7c5b5c57cde,2 +np.float64,0x4f935e229f26c,0xc08ffd7c67c1c54f,2 +np.float64,0x7fcd139e023a273b,0x408feee4f12ff11e,2 +np.float64,0x666a9944ccd54,0xc08ffa92d5e5cd64,2 +np.float64,0x3fe792f0fa6f25e2,0xbfdc374fda28f470,2 +np.float64,0xe996029bd32c1,0xc08ff10eb9b47a11,2 +np.float64,0x3fe7b0dd1eef61ba,0xbfdbc2676dc77db0,2 +np.float64,0x7fd3ec0127a7d801,0x408ff287bf47e27d,2 +np.float64,0x3fe793a8ea6f2752,0xbfdc347f7717e48d,2 +np.float64,0x7fdb89d15e3713a2,0x408ff64457a13ea2,2 +np.float64,0x3fe35b3cbbe6b679,0xbfe73557c8321b70,2 +np.float64,0x66573c94ccae8,0xc08ffa9504af7eb5,2 +np.float64,0x3fc620a2302c4144,0xc00442036b944a67,2 +np.float64,0x49b2fe0693660,0xc08ffe5f131c3c7e,2 +np.float64,0x7fda936cdfb526d9,0x408ff5db3ab3f701,2 +np.float64,0xc774ceef8ee9a,0xc08ff2e16d082fa1,2 +np.float64,0x4da9f8a09b55,0xc0900ee2206d0c88,2 +np.float64,0x3fe2ca5d5ae594bb,0xbfe89406611a5f1a,2 +np.float64,0x7fe0832497e10648,0x408ff85d1de6056e,2 +np.float64,0x3fe6a9e3222d53c6,0xbfdfda35a9bc2de1,2 +np.float64,0x3fed3d92c8ba7b26,0xbfc0a73620db8b98,2 +np.float64,0x3fdd2ec093ba5d81,0xbff2209cf78ce3f1,2 +np.float64,0x62fcb968c5f98,0xc08ffaf775a593c7,2 +np.float64,0xfcfb019ff9f60,0xc08ff0230e95bd16,2 +np.float64,0x3fd7a63e8f2f4c7d,0xbff6faf4fff7dbe0,2 +np.float64,0x3fef23b0ec3e4762,0xbfa4230cb176f917,2 +np.float64,0x340d1e6a681a5,0xc09001314b68a0a2,2 +np.float64,0x7fc0b85ba02170b6,0x408fe8821487b802,2 +np.float64,0x7fe9976e84f32edc,0x408ffd6bb6aaf467,2 +np.float64,0x329a0e9e65343,0xc090015b044e3270,2 +np.float64,0x3fea4928d3f49252,0xbfd2299b05546eab,2 +np.float64,0x3f188c70003118e0,0xc02ac3ce23bc5d5a,2 +np.float64,0x3fecce5020b99ca0,0xbfc36b23153d5f50,2 +np.float64,0x3fe203873e24070e,0xbfea86edb3690830,2 +np.float64,0x3fe02d9eaa205b3d,0xbfef7d18c54a76d2,2 +np.float64,0xef7537ebdeea7,0xc08ff0c55e9d89e7,2 +np.float64,0x3fedf7572efbeeae,0xbfb840af357cf07c,2 +np.float64,0xd1a97a61a354,0xc0900926fdfb96cc,2 +np.float64,0x7fe6a0daeced41b5,0x408ffc001edf1407,2 +np.float64,0x3fe5063625aa0c6c,0xbfe3647cfb949d62,2 +np.float64,0x7fe9b28d31736519,0x408ffd77eb4a922b,2 +np.float64,0x7feea90d033d5219,0x408fff81a4bbff62,2 +np.float64,0x3fe9494d17f2929a,0xbfd5bde02eb5287a,2 +np.float64,0x7feee17a8cbdc2f4,0x408fff96cf0dc16a,2 +np.float64,0xb2ad18ef655a3,0xc08ff4267eda8af8,2 +np.float64,0x3fad3b52683a76a5,0xc01085ab75b797ce,2 +np.float64,0x2300a65846016,0xc090037b81ce9500,2 +np.float64,0x3feb1041f9b62084,0xbfcef0c87d8b3249,2 +np.float64,0x3fdd887d3e3b10fa,0xbff1da0e1ede6db2,2 +np.float64,0x3fd3e410eb27c822,0xbffaf9b5fc9cc8cc,2 +np.float64,0x3fe0aa53e3e154a8,0xbfee1e7b5c486578,2 +np.float64,0x7fe33e389aa67c70,0x408ffa214fe50961,2 +np.float64,0x3fd27e3a43a4fc75,0xbffca84a79e8adeb,2 +np.float64,0x3fb309e0082613c0,0xc00dfe407b77a508,2 +np.float64,0x7feaf2ed8cf5e5da,0x408ffe046a9d1ba9,2 +np.float64,0x1e76167a3cec4,0xc0900448cd35ec67,2 +np.float64,0x3fe0a18e1721431c,0xbfee36cf1165a0d4,2 +np.float64,0x3fa73b78c02e76f2,0xc011d9069823b172,2 +np.float64,0x3fef6d48287eda90,0xbf9ab2d08722c101,2 +np.float64,0x8fdf0da31fbe2,0xc08ff6a6a2accaa1,2 +np.float64,0x3fc3638db826c71b,0xc005c86191688826,2 +np.float64,0xaa9c09c555381,0xc08ff4aefe1d9473,2 +np.float64,0x7fccb0f4523961e8,0x408feebd84773f23,2 +np.float64,0xede75dcfdbcec,0xc08ff0d89ba887d1,2 +np.float64,0x7f8a051520340a29,0x408fcd9cc17f0d95,2 +np.float64,0x3fef5ca2babeb945,0xbf9dc221f3618e6a,2 +np.float64,0x7fea0ff4bcf41fe8,0x408ffda193359f22,2 +np.float64,0x7fe05c53fd20b8a7,0x408ff841dc7123e8,2 +np.float64,0x3fc625664b2c4acd,0xc0043f8749b9a1d8,2 +np.float64,0x7fed58f98f7ab1f2,0x408fff00585f48c2,2 +np.float64,0x3fb3e5e51427cbca,0xc00d7bcb6528cafe,2 +np.float64,0x3fe728bd3d6e517a,0xbfdddafa72bd0f60,2 +np.float64,0x3fe3f005dd27e00c,0xbfe5d7b3ec93bca0,2 +np.float64,0x3fd74fbd1a2e9f7a,0xbff750001b63ce81,2 +np.float64,0x3fd3af6d85a75edb,0xbffb371d678d11b4,2 +np.float64,0x7fa690ad8c2d215a,0x408fdbf7db9c7640,2 +np.float64,0x3fbdfd38e23bfa72,0xc008bfc1c5c9b89e,2 +np.float64,0x3fe2374684a46e8d,0xbfea030c4595dfba,2 +np.float64,0x7fc0806c372100d7,0x408fe85b36fee334,2 +np.float64,0x3fef3ac47b7e7589,0xbfa2007195c5213f,2 +np.float64,0x3fb55473922aa8e7,0xc00cae7af8230e0c,2 +np.float64,0x7fe018dc152031b7,0x408ff811e0d712fa,2 +np.float64,0x3fe3b3fca56767f9,0xbfe6638ae2c99c62,2 +np.float64,0x7fac79818c38f302,0x408fdea720b39c3c,2 +np.float64,0x7fefffffffffffff,0x4090000000000000,2 +np.float64,0xd2b290cba5652,0xc08ff23f6d7152a6,2 +np.float64,0x7fc5848eb52b091c,0x408feb6b6f8b77d0,2 +np.float64,0xf399f62de733f,0xc08ff092ae319ad8,2 +np.float64,0x7fdec56c12bd8ad7,0x408ff78c4ddbc667,2 +np.float64,0x3fca640f1e34c81e,0xc0023969c5cbfa4c,2 +np.float64,0x3fd55225db2aa44c,0xbff95f7442a2189e,2 +np.float64,0x7fefa009a97f4012,0x408fffdd2f42ef9f,2 +np.float64,0x4a3b70609478,0xc0900f24e449bc3d,2 +np.float64,0x7fe3738b1ba6e715,0x408ffa411f2cb5e7,2 +np.float64,0x7fe5e53f0b6bca7d,0x408ffb9ed8d95cea,2 +np.float64,0x3fe274dd24a4e9ba,0xbfe967fb114b2a83,2 +np.float64,0x3fcbc58b8c378b17,0xc001a2bb1e158bcc,2 +np.float64,0x3fefc2c0043f8580,0xbf862c9b464dcf38,2 +np.float64,0xc2c4fafd858a0,0xc08ff327aecc409b,2 +np.float64,0x3fd8bc39a9b17873,0xbff5f1ad46e5a51c,2 +np.float64,0x3fdf341656be682d,0xbff094f41e7cb4c4,2 +np.float64,0x3fef8495c13f092c,0xbf966cf6313bae4c,2 +np.float64,0x3fe14e0f05229c1e,0xbfec6166f26b7161,2 +np.float64,0x3fed42d3b2ba85a7,0xbfc0860b773d35d8,2 +np.float64,0x7fd92bbac5b25775,0x408ff53abcb3fe0c,2 +np.float64,0xb1635b6f62c6c,0xc08ff43bdf47accf,2 +np.float64,0x4a3a2dbc94746,0xc08ffe49fabddb36,2 +np.float64,0x87d831290fb06,0xc08ff750419dc6fb,2 +np.float64,0x3fec4713f7f88e28,0xbfc6d6217c9f5cf9,2 +np.float64,0x7fed43ba2d3a8773,0x408ffef7fa2fc303,2 +np.float64,0x7fd1ec5b56a3d8b6,0x408ff14f62615f1e,2 +np.float64,0x3fee534b6c7ca697,0xbfb3da1951aa3e68,2 +np.float64,0x3febb564c2b76aca,0xbfca9737062e55e7,2 +np.float64,0x943e6b0f287ce,0xc08ff64e2d09335c,2 +np.float64,0xf177d957e2efb,0xc08ff0acab2999fa,2 +np.float64,0x7fb5b881a82b7102,0x408fe3872b4fde5e,2 +np.float64,0x3fdb2b4a97b65695,0xbff3c715c91359bc,2 +np.float64,0x3fac0a17e4381430,0xc010c330967309fb,2 +np.float64,0x7fd8057990b00af2,0x408ff4b0a287a348,2 +np.float64,0x1f9026a23f206,0xc09004144f3a19dd,2 +np.float64,0x3fdb2977243652ee,0xbff3c8a2fd05803d,2 +np.float64,0x3fe0f6e74b21edcf,0xbfed4c3bb956bae0,2 +np.float64,0xde9cc3bbbd399,0xc08ff19ce5c1e762,2 +np.float64,0x3fe72ce106ae59c2,0xbfddca7ab14ceba2,2 +np.float64,0x3fa8ee14e031dc2a,0xc01170d54ca88e86,2 +np.float64,0x3fe0b09bbb216137,0xbfee0d189a95b877,2 +np.float64,0x7fdfdcb157bfb962,0x408ff7f33cf2afea,2 +np.float64,0x3fef84d5f53f09ac,0xbf966134e2a154f4,2 +np.float64,0x3fea0e0b1bb41c16,0xbfd2fa2d36637d19,2 +np.float64,0x1ab76fd6356ef,0xc090050a9616ffbd,2 +np.float64,0x7fd0ccf79a2199ee,0x408ff09045af2dee,2 +np.float64,0x7fea929345f52526,0x408ffddadc322b07,2 +np.float64,0x3fe9ef629cf3dec5,0xbfd367129c166838,2 +np.float64,0x3feedf0ea2fdbe1d,0xbfaa862afca44c00,2 +np.float64,0x7fce725f723ce4be,0x408fef6cfd2769a8,2 +np.float64,0x7fe4313b3ca86275,0x408ffaaf9557ef8c,2 +np.float64,0xe2d46463c5a8d,0xc08ff165725c6b08,2 +np.float64,0x7fbacb4ace359695,0x408fe5f3647bd0d5,2 +np.float64,0x3fbafd009635fa01,0xc009f745a7a5c5d5,2 +np.float64,0x3fe3cea66ce79d4d,0xbfe6253b895e2838,2 +np.float64,0x7feaa71484354e28,0x408ffde3c0bad2a6,2 +np.float64,0x3fd755b8b42eab71,0xbff74a1444c6e654,2 +np.float64,0x3fc313e2172627c4,0xc005f830e77940c3,2 +np.float64,0x12d699a225ad4,0xc090070ec00f2338,2 +np.float64,0x3fa975fe8432ebfd,0xc01151b3da48b3f9,2 +np.float64,0x7fdce3103b39c61f,0x408ff6d19b3326fa,2 +np.float64,0x7fd341cbba268396,0x408ff2237490fdca,2 +np.float64,0x3fd8405885b080b1,0xbff6666d8802a7d5,2 +np.float64,0x3fe0f0cca3a1e199,0xbfed5cdb3e600791,2 +np.float64,0x7fbd56680c3aaccf,0x408fe6ff55bf378d,2 +np.float64,0x3f939c4f3027389e,0xc016d364dd6313fb,2 +np.float64,0x3fe9e87fac73d0ff,0xbfd37f9a2be4fe38,2 +np.float64,0x7fc93c6a883278d4,0x408fed4260e614f1,2 +np.float64,0x7fa88c0ff031181f,0x408fdcf09a46bd3a,2 +np.float64,0xd5487f99aa910,0xc08ff21b6390ab3b,2 +np.float64,0x3fe34acc96e69599,0xbfe75c9d290428fb,2 +np.float64,0x3fd17f5964a2feb3,0xbffdef50b524137b,2 +np.float64,0xe23dec0dc47be,0xc08ff16d1ce61dcb,2 +np.float64,0x3fec8bd64fb917ad,0xbfc5173941614b8f,2 +np.float64,0x3fc81d97d7303b30,0xc00343ccb791401d,2 +np.float64,0x7fe79ad18e2f35a2,0x408ffc7cf0ab0f2a,2 +np.float64,0x3f96306b402c60d7,0xc0161ce54754cac1,2 +np.float64,0xfb09fc97f6140,0xc08ff039d1d30123,2 +np.float64,0x3fec9c4afa793896,0xbfc4ace43ee46079,2 +np.float64,0x3f9262dac824c5b6,0xc01732a3a7eeb598,2 +np.float64,0x3fa5cd33f42b9a68,0xc01236ed4d315a3a,2 +np.float64,0x3fe7bb336caf7667,0xbfdb9a268a82e267,2 +np.float64,0xc6c338f98d867,0xc08ff2ebb8475bbc,2 +np.float64,0x3fd50714482a0e29,0xbff9b14a9f84f2c2,2 +np.float64,0xfff0000000000000,0x7ff8000000000000,2 +np.float64,0x3fde2cd0f93c59a2,0xbff15afe35a43a37,2 +np.float64,0xf1719cb9e2e34,0xc08ff0acf77b06d3,2 +np.float64,0xfd3caaf9fa796,0xc08ff020101771bd,2 +np.float64,0x7f750d63a02a1ac6,0x408fc32ad0caa362,2 +np.float64,0x7fcc50f4e238a1e9,0x408fee96a5622f1a,2 +np.float64,0x421d1da0843a4,0xc08fff9ffe62d869,2 +np.float64,0x3fd9e17023b3c2e0,0xbff4e631d687ee8e,2 +np.float64,0x3fe4999a09693334,0xbfe4556b3734c215,2 +np.float64,0xd619ef03ac33e,0xc08ff21013c85529,2 +np.float64,0x3fc4da522229b4a4,0xc004f150b2c573aa,2 +np.float64,0x3feb04b053b60961,0xbfcf3fc9e00ebc40,2 +np.float64,0x3fbedec5ea3dbd8c,0xc0086a33dc22fab5,2 +np.float64,0x7fec3b217ab87642,0x408ffe8dbc8ca041,2 +np.float64,0xdb257d33b64b0,0xc08ff1cb42d3c182,2 +np.float64,0x7fa2d92ec025b25d,0x408fd9e414d11cb0,2 +np.float64,0x3fa425c550284b8b,0xc012ab7cbf83be12,2 +np.float64,0x10b4869021692,0xc09007c0487d648a,2 +np.float64,0x7f97918c902f2318,0x408fd47867806574,2 +np.float64,0x3fe4f91238e9f224,0xbfe38160b4e99919,2 +np.float64,0x3fc2b1af6125635f,0xc00634343bc58461,2 +np.float64,0x3fc2a98071255301,0xc0063942bc8301be,2 +np.float64,0x3fe4cfc585299f8b,0xbfe3dca39f114f34,2 +np.float64,0x3fd1ea75b3a3d4eb,0xbffd63acd02c5406,2 +np.float64,0x3fd6bf48492d7e91,0xbff7e0cd249f80f9,2 +np.float64,0x76643d36ecc88,0xc08ff8e68f13b38c,2 +np.float64,0x7feeabab3e7d5755,0x408fff82a0fd4501,2 +np.float64,0x46c0d4a68d81b,0xc08ffed79abaddc9,2 +np.float64,0x3fd088d57ca111ab,0xbfff3dd0ed7128ea,2 +np.float64,0x3fed25887cba4b11,0xbfc13f47639bd645,2 +np.float64,0x7fd90984b4b21308,0x408ff52b022c7fb4,2 +np.float64,0x3fe6ef31daadde64,0xbfdec185760cbf21,2 +np.float64,0x3fe48dbe83291b7d,0xbfe47005b99920bd,2 +np.float64,0x3fdce8422f39d084,0xbff258a33a96cc8e,2 +np.float64,0xb8ecdef771d9c,0xc08ff3c0eca61b10,2 +np.float64,0x3fe9bbf9a03377f3,0xbfd41ecfdcc336b9,2 +np.float64,0x7fe2565339a4aca5,0x408ff992d8851eaf,2 +np.float64,0x3fe1693e3822d27c,0xbfec1919da2ca697,2 +np.float64,0x3fd3680488a6d009,0xbffb8b7330275947,2 +np.float64,0x7fbe4f3d2c3c9e79,0x408fe75fa3f4e600,2 +np.float64,0x7fd4cfef3ca99fdd,0x408ff308ee3ab50f,2 +np.float64,0x3fd9c9a51cb3934a,0xbff4fb7440055ce6,2 +np.float64,0x3fe08a9640a1152d,0xbfee76bd1bfbf5c2,2 +np.float64,0x3fef012c41fe0259,0xbfa757a2da7f9707,2 +np.float64,0x3fee653fe2fcca80,0xbfb2ffae0c95025c,2 +np.float64,0x7fd0776933a0eed1,0x408ff054e7b43d41,2 +np.float64,0x4c94e5c09929d,0xc08ffdedb7f49e5e,2 +np.float64,0xca3e3d17947c8,0xc08ff2b86dce2f7a,2 +np.float64,0x3fb528e1342a51c2,0xc00cc626c8e2d9ba,2 +np.float64,0xd774df81aee9c,0xc08ff1fd6f0a7548,2 +np.float64,0x3fc47a9b6128f537,0xc00526c577b80849,2 +np.float64,0x3fe29a6f6a6534df,0xbfe90a5f83644911,2 +np.float64,0x3fecda4f59f9b49f,0xbfc31e4a80c4cbb6,2 +np.float64,0x7fe51d44f5aa3a89,0x408ffb3382437426,2 +np.float64,0x3fd677fc412ceff9,0xbff82999086977e7,2 +np.float64,0x3fe2a3c7e7254790,0xbfe8f33415cdba9d,2 +np.float64,0x3fe6d8d1dc6db1a4,0xbfdf1bc61bc24dff,2 +np.float64,0x7febb32d8ef7665a,0x408ffe55a043ded1,2 +np.float64,0x60677860c0d0,0xc0900da2caa7d571,2 +np.float64,0x7390c2e0e7219,0xc08ff92df18bb5d2,2 +np.float64,0x3fca53711b34a6e2,0xc00240b07a9b529b,2 +np.float64,0x7fe7ce6dd8ef9cdb,0x408ffc961164ead9,2 +np.float64,0x7fc0c9de0d2193bb,0x408fe88e245767f6,2 +np.float64,0xc0ee217981dc4,0xc08ff343b77ea770,2 +np.float64,0x72bd4668e57a9,0xc08ff94323fd74fc,2 +np.float64,0x7fd6970e252d2e1b,0x408ff3fb1e2fead2,2 +np.float64,0x7fdcb61040396c20,0x408ff6bf926bc98f,2 +np.float64,0xda4faa25b49f6,0xc08ff1d68b3877f0,2 +np.float64,0x3feb344749f6688f,0xbfcdfba2d66c72c5,2 +np.float64,0x3fe2aa4284e55485,0xbfe8e32ae0683f57,2 +np.float64,0x3f8e8fcfd03d1fa0,0xc01843efb2129908,2 +np.float64,0x8000000000000000,0xfff0000000000000,2 +np.float64,0x3fd8e01155b1c023,0xbff5d0529dae9515,2 +np.float64,0x3fe8033f3370067e,0xbfda837c80b87e7c,2 +np.float64,0x7fc5bf831e2b7f05,0x408feb8ae3b039a0,2 +np.float64,0x3fd8dcdf5331b9bf,0xbff5d349e1ed422a,2 +np.float64,0x3fe58b4e302b169c,0xbfe243c9cbccde44,2 +np.float64,0x3fea8a2e47b5145d,0xbfd1464e37221894,2 +np.float64,0x75cd1e88eb9a4,0xc08ff8f553ef0475,2 +np.float64,0x7fcfc876e23f90ed,0x408fefebe6cc95e6,2 +np.float64,0x7f51aceb002359d5,0x408fb1263f9003fb,2 +np.float64,0x7fc2a1b877254370,0x408fe9c1ec52f8b9,2 +np.float64,0x7fd495810e292b01,0x408ff2e859414d31,2 +np.float64,0x7fd72048632e4090,0x408ff440690cebdb,2 +np.float64,0x7fd7aafaffaf6,0xc08ff803a390779f,2 +np.float64,0x7fe18067d4a300cf,0x408ff9090a02693f,2 +np.float64,0x3fdc1080f8b82102,0xbff3077bf44a89bd,2 +np.float64,0x3fc34a462f26948c,0xc005d777b3cdf139,2 +np.float64,0x3fe21e4a1fe43c94,0xbfea428acfbc6ea9,2 +np.float64,0x1f0d79083e1b0,0xc090042c65a7abf2,2 +np.float64,0x3fe8d0d15931a1a3,0xbfd779f6bbd4db78,2 +np.float64,0x3fe74578022e8af0,0xbfdd68b6c15e9f5e,2 +np.float64,0x50995dd0a132c,0xc08ffd56a5c8accf,2 +np.float64,0x3f9a6342b034c685,0xc0151ce1973c62bd,2 +np.float64,0x3f30856a00210ad4,0xc027e852f4d1fcbc,2 +np.float64,0x3febcf7646b79eed,0xbfc9e9cc9d12425c,2 +np.float64,0x8010000000000000,0x7ff8000000000000,2 +np.float64,0x3fdf520c02bea418,0xbff07ed5013f3062,2 +np.float64,0x3fe5433ecbea867e,0xbfe2df38968b6d14,2 +np.float64,0x3fb933a84e326751,0xc00ac1a144ad26c5,2 +np.float64,0x7b6d72c2f6daf,0xc08ff86b7a67f962,2 +np.float64,0xaef5dae75debc,0xc08ff46496bb2932,2 +np.float64,0x522d869aa45b1,0xc08ffd1d55281e98,2 +np.float64,0xa2462b05448c6,0xc08ff542fe0ac5fd,2 +np.float64,0x3fe2b71dd6e56e3c,0xbfe8c3690cf15415,2 +np.float64,0x3fe5778231aaef04,0xbfe26e495d09b783,2 +np.float64,0x3fe9b8d564f371ab,0xbfd42a161132970d,2 +np.float64,0x3f89ebc34033d787,0xc019373f90bfc7f1,2 +np.float64,0x3fe438ddc6e871bc,0xbfe53039341b0a93,2 +np.float64,0x873c75250e78f,0xc08ff75d8478dccd,2 +np.float64,0x807134cb00e27,0xc08ff7f5cf59c57a,2 +np.float64,0x3fac459878388b31,0xc010b6fe803bcdc2,2 +np.float64,0xca9dc7eb953b9,0xc08ff2b2fb480784,2 +np.float64,0x7feb38587bb670b0,0x408ffe21ff6d521e,2 +np.float64,0x7fd70e9b782e1d36,0x408ff437936b393a,2 +np.float64,0x3fa4037bbc2806f7,0xc012b55744c65ab2,2 +np.float64,0x3fd3d4637427a8c7,0xbffb0beebf4311ef,2 +np.float64,0x7fdabbda5db577b4,0x408ff5ecbc0d4428,2 +np.float64,0x7fda9be0a2b537c0,0x408ff5dee5d03d5a,2 +np.float64,0x7fe9c74396338e86,0x408ffd813506a18a,2 +np.float64,0x3fd058243e20b048,0xbfff822ffd8a7f21,2 +np.float64,0x3fe6aa6ca9ed54d9,0xbfdfd805629ff49e,2 +np.float64,0x3fd91431d5322864,0xbff5a025eea8c78b,2 +np.float64,0x7fe4d7f02329afdf,0x408ffb0d5d9b7878,2 +np.float64,0x3fe2954a12252a94,0xbfe917266e3e22d5,2 +np.float64,0x3fb25f7c8224bef9,0xc00e6764c81b3718,2 +np.float64,0x3fda4bddeeb497bc,0xbff4880638908c81,2 +np.float64,0x55dfd12eabbfb,0xc08ffc9b54ff4002,2 +np.float64,0x3fe8f399e031e734,0xbfd6f8e5c4dcd93f,2 +np.float64,0x3fd954a24832a945,0xbff56521f4707a06,2 +np.float64,0x3fdea911f2bd5224,0xbff0fcb2d0c2b2e2,2 +np.float64,0x3fe6b4ff8a2d69ff,0xbfdfacfc85cafeab,2 +np.float64,0x3fc7fa02042ff404,0xc00354e13b0767ad,2 +np.float64,0x3fe955088c72aa11,0xbfd593130f29949e,2 +np.float64,0xd7e74ec1afcea,0xc08ff1f74f61721c,2 +np.float64,0x3fe9d69c1ab3ad38,0xbfd3bf710a337e06,2 +np.float64,0x3fd85669a2b0acd3,0xbff65176143ccc1e,2 +np.float64,0x3fea99b285353365,0xbfd11062744783f2,2 +np.float64,0x3fe2c79f80a58f3f,0xbfe89ac33f990289,2 +np.float64,0x3f8332ba30266574,0xc01af2cb7b635783,2 +np.float64,0x30d0150061a1,0xc090119030f74c5d,2 +np.float64,0x3fdbf4cb06b7e996,0xbff31e5207aaa754,2 +np.float64,0x3fe6b56c216d6ad8,0xbfdfab42fb2941c5,2 +np.float64,0x7fc4dc239829b846,0x408feb0fb0e13fbe,2 +np.float64,0x3fd0ab85ef21570c,0xbfff0d95d6c7a35c,2 +np.float64,0x7fe13d75e5e27aeb,0x408ff8dc8efa476b,2 +np.float64,0x3fece3b832f9c770,0xbfc2e21b165d583f,2 +np.float64,0x3fe3a279c4e744f4,0xbfe68ca4fbb55dbf,2 +np.float64,0x3feb64659ef6c8cb,0xbfccb6204b6bf724,2 +np.float64,0x2279a6bc44f36,0xc0900391eeeb3e7c,2 +np.float64,0xb88046d571009,0xc08ff3c7b5b45300,2 +np.float64,0x7ff4000000000000,0x7ffc000000000000,2 +np.float64,0x3fe49af059a935e1,0xbfe4526c294f248f,2 +np.float64,0xa3e5508147cc,0xc0900a92ce5924b1,2 +np.float64,0x7fc56def3d2adbdd,0x408feb5f46c360e8,2 +np.float64,0x7fd99f3574333e6a,0x408ff56f3807987c,2 +np.float64,0x3fdc38d56fb871ab,0xbff2e667cad8f36a,2 +np.float64,0xd0b03507a1607,0xc08ff25bbcf8aa9d,2 +np.float64,0xc493f9078927f,0xc08ff30c5fa4e759,2 +np.float64,0x3fc86ddbcb30dbb8,0xc0031da1fcb56d75,2 +np.float64,0x7fe75dc395aebb86,0x408ffc5eef841491,2 +np.float64,0x1647618a2c8ed,0xc0900616ef9479c1,2 +np.float64,0xdf144763be289,0xc08ff196b527f3c9,2 +np.float64,0x3fe0b29da6a1653b,0xbfee078b5f4d7744,2 +np.float64,0x3feb055852b60ab1,0xbfcf3b4db5779a7a,2 +np.float64,0x3fe8bc1625f1782c,0xbfd7c739ade904bc,2 +np.float64,0x7fd19bfb8ea337f6,0x408ff11b2b55699c,2 +np.float64,0x3fed1d80d1ba3b02,0xbfc1722e8d3ce094,2 +np.float64,0x2d9c65925b38e,0xc09001f46bcd3bc5,2 +np.float64,0x7fed6f4d857ade9a,0x408fff091cf6a3b4,2 +np.float64,0x3fd070cd6ba0e19b,0xbfff5f7609ca29e8,2 +np.float64,0x7fea3508b8f46a10,0x408ffdb1f30bd6be,2 +np.float64,0x508b897ca1172,0xc08ffd58a0eb3583,2 +np.float64,0x7feba367b07746ce,0x408ffe4f0bf4bd4e,2 +np.float64,0x3fefebd5c4bfd7ac,0xbf6d20b4fcf21b69,2 +np.float64,0x3fd8ef07b8b1de0f,0xbff5c2745c0795a5,2 +np.float64,0x3fd38ed518271daa,0xbffb5d75f00f6900,2 +np.float64,0x6de0fecedbc20,0xc08ff9c307bbc647,2 +np.float64,0xafc0ffc35f820,0xc08ff45737e5d6b4,2 +np.float64,0x7fd282097ca50412,0x408ff1ae3b27bf3b,2 +np.float64,0x3fe2f2d50b65e5aa,0xbfe831042e6a1e99,2 +np.float64,0x3faa437bac3486f7,0xc01123d8d962205a,2 +np.float64,0x3feea54434fd4a88,0xbfaff202cc456647,2 +np.float64,0x3fc9e65b8633ccb7,0xc00270e77ffd19da,2 +np.float64,0x7fee15af61fc2b5e,0x408fff49a49154a3,2 +np.float64,0x7fefe670a73fcce0,0x408ffff6c44c1005,2 +np.float64,0x3fc0832d0f21065a,0xc007a2dc2f25384a,2 +np.float64,0x3fecfc96bcb9f92d,0xbfc24367c3912620,2 +np.float64,0x3feb705682b6e0ad,0xbfcc65b1bb16f9c5,2 +np.float64,0x3fe185c4f9630b8a,0xbfebcdb401af67a4,2 +np.float64,0x3fb0a5a9f6214b54,0xc00f8ada2566a047,2 +np.float64,0x7fe2908cdda52119,0x408ff9b744861fb1,2 +np.float64,0x7fee776e183ceedb,0x408fff6ee7c2f86e,2 +np.float64,0x3fce1d608f3c3ac1,0xc000b3685d006474,2 +np.float64,0x7fecf92aa339f254,0x408ffeda6c998267,2 +np.float64,0xce13cb519c27a,0xc08ff280f02882a9,2 +np.float64,0x1,0xc090c80000000000,2 +np.float64,0x3fe485a8afa90b51,0xbfe4823265d5a50a,2 +np.float64,0x3feea60908bd4c12,0xbfafdf7ad7fe203f,2 +np.float64,0x3fd2253033a44a60,0xbffd187d0ec8d5b9,2 +np.float64,0x435338fc86a68,0xc08fff6a591059dd,2 +np.float64,0x7fce8763a73d0ec6,0x408fef74f1e715ff,2 +np.float64,0x3fbe5ddb783cbbb7,0xc0089acc5afa794b,2 +np.float64,0x7fe4cf19ada99e32,0x408ffb0877ca302b,2 +np.float64,0x3fe94c9ea1b2993d,0xbfd5b1c2e867b911,2 +np.float64,0x3fe75541c72eaa84,0xbfdd2a27aa117699,2 +np.float64,0x8000000000000001,0x7ff8000000000000,2 +np.float64,0x7fdbec7f2c37d8fd,0x408ff66d69a7f818,2 +np.float64,0x8ef10d091de22,0xc08ff6b9ca5094f8,2 +np.float64,0x3fea69025b74d205,0xbfd1b9fe2c252c70,2 +np.float64,0x562376d0ac46f,0xc08ffc924111cd31,2 +np.float64,0x8e8097ab1d013,0xc08ff6c2e2706f67,2 +np.float64,0x3fca6803ed34d008,0xc00237aef808825b,2 +np.float64,0x7fe8fe9067b1fd20,0x408ffd25f459a7d1,2 +np.float64,0x3f918e8c7f233,0xc0900009fe011d54,2 +np.float64,0x3fdfe773833fcee7,0xbff011bc1af87bb9,2 +np.float64,0xefffef6fdfffe,0xc08ff0beb0f09eb0,2 +np.float64,0x7fe64610282c8c1f,0x408ffbd17209db18,2 +np.float64,0xe66be8c1ccd7d,0xc08ff13706c056e1,2 +np.float64,0x2837e570506fd,0xc09002ae4dae0c1a,2 +np.float64,0x3febe3a081f7c741,0xbfc964171f2a5a47,2 +np.float64,0x3fe21ed09a243da1,0xbfea41342d29c3ff,2 +np.float64,0x3fe1596c8162b2d9,0xbfec431eee30823a,2 +np.float64,0x8f2b9a131e574,0xc08ff6b51104ed4e,2 +np.float64,0x3fe88ed179711da3,0xbfd870d08a4a4b0c,2 +np.float64,0x34159bc2682b4,0xc09001305a885f94,2 +np.float64,0x1ed31e543da65,0xc0900437481577f8,2 +np.float64,0x3feafbe9de75f7d4,0xbfcf7bcdbacf1c61,2 +np.float64,0xfb16fb27f62e0,0xc08ff03938e682a2,2 +np.float64,0x3fe5cd5ba7eb9ab7,0xbfe1b7165771af3c,2 +np.float64,0x7fe72905e76e520b,0x408ffc44c4e7e80c,2 +np.float64,0x7fb7136e2e2e26db,0x408fe439fd383fb7,2 +np.float64,0x8fa585e11f4c,0xc0900b55a08a486b,2 +np.float64,0x7fed985ce47b30b9,0x408fff192b596821,2 +np.float64,0x3feaaf0869755e11,0xbfd0c671571b3764,2 +np.float64,0x3fa40fd4ec281faa,0xc012b1c8dc0b9e5f,2 +np.float64,0x7fda2a70993454e0,0x408ff5ad47b0c68a,2 +np.float64,0x3fe5f7e931abefd2,0xbfe15d52b3605abf,2 +np.float64,0x3fe9fc6d3533f8da,0xbfd338b06a790994,2 +np.float64,0x3fe060649420c0c9,0xbfeeed1756111891,2 +np.float64,0x3fce8435e33d086c,0xc0008c41cea9ed40,2 +np.float64,0x7ff8000000000000,0x7ff8000000000000,2 +np.float64,0x617820aec2f05,0xc08ffb251e9af0f0,2 +np.float64,0x7fcc4ab6ee38956d,0x408fee9419c8f77d,2 +np.float64,0x7fdefda2fc3dfb45,0x408ff7a15063bc05,2 +np.float64,0x7fe5138ccaaa2719,0x408ffb2e30f3a46e,2 +np.float64,0x3fe3817a836702f5,0xbfe6da7c2b25e35a,2 +np.float64,0x3fb8a7dafa314fb6,0xc00b025bc0784ebe,2 +np.float64,0x349dc420693d,0xc09011215825d2c8,2 +np.float64,0x6b0e504ad61cb,0xc08ffa0fee9c5cd6,2 +np.float64,0x273987644e732,0xc09002d34294ed79,2 +np.float64,0x3fc0bd8a6e217b15,0xc0077a5828b4d2f5,2 +np.float64,0x758b48c4eb16a,0xc08ff8fbc8fbe46a,2 +np.float64,0x3fc8a9a52631534a,0xc00301854ec0ef81,2 +np.float64,0x7fe79d29a76f3a52,0x408ffc7e1607a4c1,2 +np.float64,0x3fd7d3ebce2fa7d8,0xbff6ce8a94aebcda,2 +np.float64,0x7fd1cb68a52396d0,0x408ff13a17533b2b,2 +np.float64,0x7fda514a5d34a294,0x408ff5be5e081578,2 +np.float64,0x3fc40b4382281687,0xc0056632c8067228,2 +np.float64,0x7feff1208c3fe240,0x408ffffaa180fa0d,2 +np.float64,0x8f58739f1eb0f,0xc08ff6b17402689d,2 +np.float64,0x1fdbe9a23fb7e,0xc090040685b2d24f,2 +np.float64,0xcb1d0e87963a2,0xc08ff2abbd903b82,2 +np.float64,0x3fc45a6a1a28b4d4,0xc00538f86c4aeaee,2 +np.float64,0x3fe61885b1ac310b,0xbfe118fd2251d2ec,2 +np.float64,0x3fedf584c8fbeb0a,0xbfb8572433ff67a9,2 +np.float64,0x7fb0bddd1a217bb9,0x408fe085e0d621db,2 +np.float64,0x72d8d3e0e5b3,0xc0900ca02f68c7a1,2 +np.float64,0x5cca6ff6b994f,0xc08ffbb6751fda01,2 +np.float64,0x7fe3197839a632ef,0x408ffa0b2fccfb68,2 +np.float64,0x3fcce4d9c139c9b4,0xc0012dae05baa91b,2 +np.float64,0x3fe76d00f62eda02,0xbfdccc5f12799be1,2 +np.float64,0x3fc53c22f72a7846,0xc004bbaa9cbc7958,2 +np.float64,0x7fdda02f1ebb405d,0x408ff71c37c71659,2 +np.float64,0x3fe0844eaba1089d,0xbfee884722762583,2 +np.float64,0x3febb438dc776872,0xbfca9f05e1c691f1,2 +np.float64,0x3fdf4170cdbe82e2,0xbff08b1561c8d848,2 +np.float64,0x3fce1b8d6f3c371b,0xc000b41b69507671,2 +np.float64,0x8370e60706e1d,0xc08ff7b19ea0b4ca,2 +np.float64,0x7fa5bf92382b7f23,0x408fdb8aebb3df87,2 +np.float64,0x7fe4a59979a94b32,0x408ffaf15c1358cd,2 +np.float64,0x3faa66086034cc11,0xc0111c466b7835d6,2 +np.float64,0x7fb7a958262f52af,0x408fe48408b1e093,2 +np.float64,0x3fdaacc5f635598c,0xbff43390d06b5614,2 +np.float64,0x3fd2825b9e2504b7,0xbffca3234264f109,2 +np.float64,0x3fcede160a3dbc2c,0xc0006a759e29060c,2 +np.float64,0x7fd3b19603a7632b,0x408ff265b528371c,2 +np.float64,0x7fcf8a86ea3f150d,0x408fefd552e7f3b2,2 +np.float64,0xedbcc0f7db798,0xc08ff0daad12096b,2 +np.float64,0xf1e1683de3c2d,0xc08ff0a7a0a37e00,2 +np.float64,0xb6ebd9bf6dd7b,0xc08ff3e11e28378d,2 +np.float64,0x3fec8090d6f90122,0xbfc56031b72194cc,2 +np.float64,0x3fd3e10e37a7c21c,0xbffafd34a3ebc933,2 +np.float64,0x7fbb1c96aa36392c,0x408fe616347b3342,2 +np.float64,0x3fe2f3996f25e733,0xbfe82f25bc5d1bbd,2 +np.float64,0x7fe8709da870e13a,0x408ffce3ab6ce59a,2 +np.float64,0x7fea3233d1b46467,0x408ffdb0b3bbc6de,2 +np.float64,0x65fa4112cbf49,0xc08ffa9f85eb72b9,2 +np.float64,0x3fca2cae9f34595d,0xc00251bb275afb87,2 +np.float64,0x8135fd9f026c0,0xc08ff7e42e14dce7,2 +np.float64,0x7fe0a6f057e14de0,0x408ff876081a4bfe,2 +np.float64,0x10000000000000,0xc08ff00000000000,2 +np.float64,0x3fe1fd506263faa1,0xbfea96dd8c543b72,2 +np.float64,0xa5532c554aa66,0xc08ff50bf5bfc66d,2 +np.float64,0xc239d00b8473a,0xc08ff32ff0ea3f92,2 +np.float64,0x7fdb5314e336a629,0x408ff62d4ff60d82,2 +np.float64,0x3fe5f506e2abea0e,0xbfe16362a4682120,2 +np.float64,0x3fa20c60202418c0,0xc0134e08d82608b6,2 +np.float64,0x7fe03864b22070c8,0x408ff82866d65e9a,2 +np.float64,0x3fe72cf5656e59eb,0xbfddca298969effa,2 +np.float64,0x5c295386b852b,0xc08ffbca90b136c9,2 +np.float64,0x7fd71e5020ae3c9f,0x408ff43f6d58eb7c,2 +np.float64,0x3fd1905a842320b5,0xbffdd8ecd288159c,2 +np.float64,0x3fe6bddb256d7bb6,0xbfdf88fee1a820bb,2 +np.float64,0xe061b967c0c37,0xc08ff18581951561,2 +np.float64,0x3fe534f65cea69ed,0xbfe2fe45fe7d3040,2 +np.float64,0xdc7dae07b8fb6,0xc08ff1b93074ea76,2 +np.float64,0x3fd0425082a084a1,0xbfffa11838b21633,2 +np.float64,0xba723fc974e48,0xc08ff3a8b8d01c58,2 +np.float64,0x3fce42ffc73c8600,0xc000a5062678406e,2 +np.float64,0x3f2e6d3c7e5ce,0xc090001304cfd1c7,2 +np.float64,0x3fd4b2e5f7a965cc,0xbffa0e6e6bae0a68,2 +np.float64,0x3fe6db1d18edb63a,0xbfdf128158ee92d9,2 +np.float64,0x7fe4e5792f29caf1,0x408ffb14d9dbf133,2 +np.float64,0x3fc11cdf992239bf,0xc00739569619cd77,2 +np.float64,0x3fc05ea11220bd42,0xc007bc841b48a890,2 +np.float64,0x4bd592d497ab3,0xc08ffe0ab1c962e2,2 +np.float64,0x280068fc5000e,0xc09002b64955e865,2 +np.float64,0x7fe2f2637065e4c6,0x408ff9f379c1253a,2 +np.float64,0x3fefc38467ff8709,0xbf85e53e64b9a424,2 +np.float64,0x2d78ec5a5af1e,0xc09001f8ea8601e0,2 +np.float64,0x7feeef2b957dde56,0x408fff9bebe995f7,2 +np.float64,0x2639baf44c738,0xc09002f9618d623b,2 +np.float64,0x3fc562964d2ac52d,0xc004a6d76959ef78,2 +np.float64,0x3fe21b071fe4360e,0xbfea4adb2cd96ade,2 +np.float64,0x7fe56aa6802ad54c,0x408ffb5d81d1a898,2 +np.float64,0x4296b452852d7,0xc08fff8ad7fbcbe1,2 +np.float64,0x7fe3fac4ff27f589,0x408ffa9049eec479,2 +np.float64,0x7fe7a83e6caf507c,0x408ffc837f436604,2 +np.float64,0x3fc4ac5b872958b7,0xc0050add72381ac3,2 +np.float64,0x3fd6d697c02dad30,0xbff7c931a3eefb01,2 +np.float64,0x3f61e391c023c724,0xc021ad91e754f94b,2 +np.float64,0x10817f9c21031,0xc09007d20434d7bc,2 +np.float64,0x3fdb9c4c4cb73899,0xbff367d8615c5ece,2 +np.float64,0x3fe26ead6b64dd5b,0xbfe977771def5989,2 +np.float64,0x3fc43ea5c3287d4c,0xc00548c2163ae631,2 +np.float64,0x3fe05bd8bba0b7b1,0xbfeef9ea0db91abc,2 +np.float64,0x3feac78369358f07,0xbfd071e2b0aeab39,2 +np.float64,0x7fe254922ca4a923,0x408ff991bdd4e5d3,2 +np.float64,0x3fe5a2f5842b45eb,0xbfe21135c9a71666,2 +np.float64,0x3fd5daf98c2bb5f3,0xbff8cd24f7c07003,2 +np.float64,0x3fcb2a1384365427,0xc001e40f0d04299a,2 +np.float64,0x3fe073974360e72f,0xbfeeb7183a9930b7,2 +np.float64,0xcf3440819e688,0xc08ff270d3a71001,2 +np.float64,0x3fd35656cda6acae,0xbffba083fba4939d,2 +np.float64,0x7fe6c59b4ded8b36,0x408ffc12ce725425,2 +np.float64,0x3fba896f943512df,0xc00a291cb6947701,2 +np.float64,0x7fe54917e86a922f,0x408ffb4b5e0fb848,2 +np.float64,0x7fed2a3f51ba547e,0x408ffeede945a948,2 +np.float64,0x3fdc72bd5038e57b,0xbff2b73b7e93e209,2 +np.float64,0x7fefdb3f9f3fb67e,0x408ffff2b702a768,2 +np.float64,0x3fb0184430203088,0xc00fee8c1351763c,2 +np.float64,0x7d6c3668fad87,0xc08ff83c195f2cca,2 +np.float64,0x3fd5aa254aab544b,0xbff900f16365991b,2 +np.float64,0x3f963daab02c7b55,0xc0161974495b1b71,2 +np.float64,0x3fa7a9c5982f538b,0xc011bde0f6052a89,2 +np.float64,0xb3a5a74b674b5,0xc08ff4167bc97c81,2 +np.float64,0x7fad0c14503a1828,0x408fdee1f2d56cd7,2 +np.float64,0x43e0e9d887c1e,0xc08fff522837b13b,2 +np.float64,0x3fe513b20aea2764,0xbfe346ea994100e6,2 +np.float64,0x7fe4e10393e9c206,0x408ffb12630f6a06,2 +np.float64,0x68b286e2d1651,0xc08ffa51c0d795d4,2 +np.float64,0x7fe8de453331bc89,0x408ffd17012b75ac,2 +np.float64,0x1b3d77d4367b0,0xc09004edea60aa36,2 +np.float64,0x3fd351cbc326a398,0xbffba5f0f4d5fdba,2 +np.float64,0x3fd264951b24c92a,0xbffcc8636788b9bf,2 +np.float64,0xd2465761a48cb,0xc08ff2455c9c53e5,2 +np.float64,0x7fe46a0ef028d41d,0x408ffacfe32c6f5d,2 +np.float64,0x3fafd8ac4c3fb159,0xc010071bf33195d0,2 +np.float64,0x902aec5d2055e,0xc08ff6a08e28aabc,2 +np.float64,0x3fcea61bb03d4c37,0xc0007f76e509b657,2 +np.float64,0x7fe8d90f9571b21e,0x408ffd1495f952e7,2 +np.float64,0x7fa650c9442ca192,0x408fdbd6ff22fdd8,2 +np.float64,0x3fe8ecfdf171d9fc,0xbfd7115df40e8580,2 +np.float64,0x7fd4e6fe7f29cdfc,0x408ff315b0dae183,2 +np.float64,0x77df4c52efbea,0xc08ff8c1d5c1df33,2 +np.float64,0xe200b0cfc4016,0xc08ff1703cfb8e79,2 +np.float64,0x3fe230ea7e2461d5,0xbfea132d2385160e,2 +np.float64,0x7fd1f7ced723ef9d,0x408ff156bfbf92a4,2 +np.float64,0x3fea762818f4ec50,0xbfd18c12a88e5f79,2 +np.float64,0x7feea4ba7c7d4974,0x408fff8004164054,2 +np.float64,0x833ec605067d9,0xc08ff7b606383841,2 +np.float64,0x7fd0c2d7fea185af,0x408ff0894f3a0cf4,2 +np.float64,0x3fe1d7d61d23afac,0xbfeaf76fee875d3e,2 +np.float64,0x65adecb0cb5be,0xc08ffaa82cb09d68,2 diff --git a/numpy/core/tests/data/umath-validation-set-sin.csv b/numpy/core/tests/data/umath-validation-set-sin.csv new file mode 100644 index 000000000000..3b913ccd9eb6 --- /dev/null +++ b/numpy/core/tests/data/umath-validation-set-sin.csv @@ -0,0 +1,1370 @@ +dtype,input,output,ulperrortol +## +ve denormals ## +np.float32,0x004b4716,0x004b4716,2 +np.float32,0x007b2490,0x007b2490,2 +np.float32,0x007c99fa,0x007c99fa,2 +np.float32,0x00734a0c,0x00734a0c,2 +np.float32,0x0070de24,0x0070de24,2 +np.float32,0x007fffff,0x007fffff,2 +np.float32,0x00000001,0x00000001,2 +## -ve denormals ## +np.float32,0x80495d65,0x80495d65,2 +np.float32,0x806894f6,0x806894f6,2 +np.float32,0x80555a76,0x80555a76,2 +np.float32,0x804e1fb8,0x804e1fb8,2 +np.float32,0x80687de9,0x80687de9,2 +np.float32,0x807fffff,0x807fffff,2 +np.float32,0x80000001,0x80000001,2 +## +/-0.0f, +/-FLT_MIN +/-FLT_MAX ## +np.float32,0x00000000,0x00000000,2 +np.float32,0x80000000,0x80000000,2 +np.float32,0x00800000,0x00800000,2 +np.float32,0x80800000,0x80800000,2 +## 1.00f ## +np.float32,0x3f800000,0x3f576aa4,2 +np.float32,0x3f800001,0x3f576aa6,2 +np.float32,0x3f800002,0x3f576aa7,2 +np.float32,0xc090a8b0,0x3f7b4e48,2 +np.float32,0x41ce3184,0x3f192d43,2 +np.float32,0xc1d85848,0xbf7161cb,2 +np.float32,0x402b8820,0x3ee3f29f,2 +np.float32,0x42b4e454,0x3f1d0151,2 +np.float32,0x42a67a60,0x3f7ffa4c,2 +np.float32,0x41d92388,0x3f67beef,2 +np.float32,0x422dd66c,0xbeffb0c1,2 +np.float32,0xc28f5be6,0xbf0bae79,2 +np.float32,0x41ab2674,0x3f0ffe2b,2 +np.float32,0x3f490fdb,0x3f3504f3,2 +np.float32,0xbf490fdb,0xbf3504f3,2 +np.float32,0x3fc90fdb,0x3f800000,2 +np.float32,0xbfc90fdb,0xbf800000,2 +np.float32,0x40490fdb,0xb3bbbd2e,2 +np.float32,0xc0490fdb,0x33bbbd2e,2 +np.float32,0x3fc90fdb,0x3f800000,2 +np.float32,0xbfc90fdb,0xbf800000,2 +np.float32,0x40490fdb,0xb3bbbd2e,2 +np.float32,0xc0490fdb,0x33bbbd2e,2 +np.float32,0x40c90fdb,0x343bbd2e,2 +np.float32,0xc0c90fdb,0xb43bbd2e,2 +np.float32,0x4016cbe4,0x3f3504f3,2 +np.float32,0xc016cbe4,0xbf3504f3,2 +np.float32,0x4096cbe4,0xbf800000,2 +np.float32,0xc096cbe4,0x3f800000,2 +np.float32,0x4116cbe4,0xb2ccde2e,2 +np.float32,0xc116cbe4,0x32ccde2e,2 +np.float32,0x40490fdb,0xb3bbbd2e,2 +np.float32,0xc0490fdb,0x33bbbd2e,2 +np.float32,0x40c90fdb,0x343bbd2e,2 +np.float32,0xc0c90fdb,0xb43bbd2e,2 +np.float32,0x41490fdb,0x34bbbd2e,2 +np.float32,0xc1490fdb,0xb4bbbd2e,2 +np.float32,0x407b53d2,0xbf3504f5,2 +np.float32,0xc07b53d2,0x3f3504f5,2 +np.float32,0x40fb53d2,0x3f800000,2 +np.float32,0xc0fb53d2,0xbf800000,2 +np.float32,0x417b53d2,0xb535563d,2 +np.float32,0xc17b53d2,0x3535563d,2 +np.float32,0x4096cbe4,0xbf800000,2 +np.float32,0xc096cbe4,0x3f800000,2 +np.float32,0x4116cbe4,0xb2ccde2e,2 +np.float32,0xc116cbe4,0x32ccde2e,2 +np.float32,0x4196cbe4,0x334cde2e,2 +np.float32,0xc196cbe4,0xb34cde2e,2 +np.float32,0x40afede0,0xbf3504ef,2 +np.float32,0xc0afede0,0x3f3504ef,2 +np.float32,0x412fede0,0xbf800000,2 +np.float32,0xc12fede0,0x3f800000,2 +np.float32,0x41afede0,0xb5b222c4,2 +np.float32,0xc1afede0,0x35b222c4,2 +np.float32,0x40c90fdb,0x343bbd2e,2 +np.float32,0xc0c90fdb,0xb43bbd2e,2 +np.float32,0x41490fdb,0x34bbbd2e,2 +np.float32,0xc1490fdb,0xb4bbbd2e,2 +np.float32,0x41c90fdb,0x353bbd2e,2 +np.float32,0xc1c90fdb,0xb53bbd2e,2 +np.float32,0x40e231d6,0x3f3504f3,2 +np.float32,0xc0e231d6,0xbf3504f3,2 +np.float32,0x416231d6,0x3f800000,2 +np.float32,0xc16231d6,0xbf800000,2 +np.float32,0x41e231d6,0xb399a6a2,2 +np.float32,0xc1e231d6,0x3399a6a2,2 +np.float32,0x40fb53d2,0x3f800000,2 +np.float32,0xc0fb53d2,0xbf800000,2 +np.float32,0x417b53d2,0xb535563d,2 +np.float32,0xc17b53d2,0x3535563d,2 +np.float32,0x41fb53d2,0x35b5563d,2 +np.float32,0xc1fb53d2,0xb5b5563d,2 +np.float32,0x410a3ae7,0x3f3504eb,2 +np.float32,0xc10a3ae7,0xbf3504eb,2 +np.float32,0x418a3ae7,0xbf800000,2 +np.float32,0xc18a3ae7,0x3f800000,2 +np.float32,0x420a3ae7,0xb6308908,2 +np.float32,0xc20a3ae7,0x36308908,2 +np.float32,0x4116cbe4,0xb2ccde2e,2 +np.float32,0xc116cbe4,0x32ccde2e,2 +np.float32,0x4196cbe4,0x334cde2e,2 +np.float32,0xc196cbe4,0xb34cde2e,2 +np.float32,0x4216cbe4,0x33ccde2e,2 +np.float32,0xc216cbe4,0xb3ccde2e,2 +np.float32,0x41235ce2,0xbf3504f7,2 +np.float32,0xc1235ce2,0x3f3504f7,2 +np.float32,0x41a35ce2,0x3f800000,2 +np.float32,0xc1a35ce2,0xbf800000,2 +np.float32,0x42235ce2,0xb5b889b6,2 +np.float32,0xc2235ce2,0x35b889b6,2 +np.float32,0x412fede0,0xbf800000,2 +np.float32,0xc12fede0,0x3f800000,2 +np.float32,0x41afede0,0xb5b222c4,2 +np.float32,0xc1afede0,0x35b222c4,2 +np.float32,0x422fede0,0x363222c4,2 +np.float32,0xc22fede0,0xb63222c4,2 +np.float32,0x413c7edd,0xbf3504f3,2 +np.float32,0xc13c7edd,0x3f3504f3,2 +np.float32,0x41bc7edd,0xbf800000,2 +np.float32,0xc1bc7edd,0x3f800000,2 +np.float32,0x423c7edd,0xb4000add,2 +np.float32,0xc23c7edd,0x34000add,2 +np.float32,0x41490fdb,0x34bbbd2e,2 +np.float32,0xc1490fdb,0xb4bbbd2e,2 +np.float32,0x41c90fdb,0x353bbd2e,2 +np.float32,0xc1c90fdb,0xb53bbd2e,2 +np.float32,0x42490fdb,0x35bbbd2e,2 +np.float32,0xc2490fdb,0xb5bbbd2e,2 +np.float32,0x4155a0d9,0x3f3504fb,2 +np.float32,0xc155a0d9,0xbf3504fb,2 +np.float32,0x41d5a0d9,0x3f800000,2 +np.float32,0xc1d5a0d9,0xbf800000,2 +np.float32,0x4255a0d9,0xb633bc81,2 +np.float32,0xc255a0d9,0x3633bc81,2 +np.float32,0x416231d6,0x3f800000,2 +np.float32,0xc16231d6,0xbf800000,2 +np.float32,0x41e231d6,0xb399a6a2,2 +np.float32,0xc1e231d6,0x3399a6a2,2 +np.float32,0x426231d6,0x3419a6a2,2 +np.float32,0xc26231d6,0xb419a6a2,2 +np.float32,0x416ec2d4,0x3f3504ef,2 +np.float32,0xc16ec2d4,0xbf3504ef,2 +np.float32,0x41eec2d4,0xbf800000,2 +np.float32,0xc1eec2d4,0x3f800000,2 +np.float32,0x426ec2d4,0xb5bef0a7,2 +np.float32,0xc26ec2d4,0x35bef0a7,2 +np.float32,0x417b53d2,0xb535563d,2 +np.float32,0xc17b53d2,0x3535563d,2 +np.float32,0x41fb53d2,0x35b5563d,2 +np.float32,0xc1fb53d2,0xb5b5563d,2 +np.float32,0x427b53d2,0x3635563d,2 +np.float32,0xc27b53d2,0xb635563d,2 +np.float32,0x4183f268,0xbf3504ff,2 +np.float32,0xc183f268,0x3f3504ff,2 +np.float32,0x4203f268,0x3f800000,2 +np.float32,0xc203f268,0xbf800000,2 +np.float32,0x4283f268,0xb6859a13,2 +np.float32,0xc283f268,0x36859a13,2 +np.float32,0x418a3ae7,0xbf800000,2 +np.float32,0xc18a3ae7,0x3f800000,2 +np.float32,0x420a3ae7,0xb6308908,2 +np.float32,0xc20a3ae7,0x36308908,2 +np.float32,0x428a3ae7,0x36b08908,2 +np.float32,0xc28a3ae7,0xb6b08908,2 +np.float32,0x41908365,0xbf3504f6,2 +np.float32,0xc1908365,0x3f3504f6,2 +np.float32,0x42108365,0xbf800000,2 +np.float32,0xc2108365,0x3f800000,2 +np.float32,0x42908365,0x3592200d,2 +np.float32,0xc2908365,0xb592200d,2 +np.float32,0x4196cbe4,0x334cde2e,2 +np.float32,0xc196cbe4,0xb34cde2e,2 +np.float32,0x4216cbe4,0x33ccde2e,2 +np.float32,0xc216cbe4,0xb3ccde2e,2 +np.float32,0x4296cbe4,0x344cde2e,2 +np.float32,0xc296cbe4,0xb44cde2e,2 +np.float32,0x419d1463,0x3f3504f8,2 +np.float32,0xc19d1463,0xbf3504f8,2 +np.float32,0x421d1463,0x3f800000,2 +np.float32,0xc21d1463,0xbf800000,2 +np.float32,0x429d1463,0xb5c55799,2 +np.float32,0xc29d1463,0x35c55799,2 +np.float32,0x41a35ce2,0x3f800000,2 +np.float32,0xc1a35ce2,0xbf800000,2 +np.float32,0x42235ce2,0xb5b889b6,2 +np.float32,0xc2235ce2,0x35b889b6,2 +np.float32,0x42a35ce2,0x363889b6,2 +np.float32,0xc2a35ce2,0xb63889b6,2 +np.float32,0x41a9a561,0x3f3504e7,2 +np.float32,0xc1a9a561,0xbf3504e7,2 +np.float32,0x4229a561,0xbf800000,2 +np.float32,0xc229a561,0x3f800000,2 +np.float32,0x42a9a561,0xb68733d0,2 +np.float32,0xc2a9a561,0x368733d0,2 +np.float32,0x41afede0,0xb5b222c4,2 +np.float32,0xc1afede0,0x35b222c4,2 +np.float32,0x422fede0,0x363222c4,2 +np.float32,0xc22fede0,0xb63222c4,2 +np.float32,0x42afede0,0x36b222c4,2 +np.float32,0xc2afede0,0xb6b222c4,2 +np.float32,0x41b6365e,0xbf3504f0,2 +np.float32,0xc1b6365e,0x3f3504f0,2 +np.float32,0x4236365e,0x3f800000,2 +np.float32,0xc236365e,0xbf800000,2 +np.float32,0x42b6365e,0x358bb91c,2 +np.float32,0xc2b6365e,0xb58bb91c,2 +np.float32,0x41bc7edd,0xbf800000,2 +np.float32,0xc1bc7edd,0x3f800000,2 +np.float32,0x423c7edd,0xb4000add,2 +np.float32,0xc23c7edd,0x34000add,2 +np.float32,0x42bc7edd,0x34800add,2 +np.float32,0xc2bc7edd,0xb4800add,2 +np.float32,0x41c2c75c,0xbf3504ef,2 +np.float32,0xc1c2c75c,0x3f3504ef,2 +np.float32,0x4242c75c,0xbf800000,2 +np.float32,0xc242c75c,0x3f800000,2 +np.float32,0x42c2c75c,0xb5cbbe8a,2 +np.float32,0xc2c2c75c,0x35cbbe8a,2 +np.float32,0x41c90fdb,0x353bbd2e,2 +np.float32,0xc1c90fdb,0xb53bbd2e,2 +np.float32,0x42490fdb,0x35bbbd2e,2 +np.float32,0xc2490fdb,0xb5bbbd2e,2 +np.float32,0x42c90fdb,0x363bbd2e,2 +np.float32,0xc2c90fdb,0xb63bbd2e,2 +np.float32,0x41cf585a,0x3f3504ff,2 +np.float32,0xc1cf585a,0xbf3504ff,2 +np.float32,0x424f585a,0x3f800000,2 +np.float32,0xc24f585a,0xbf800000,2 +np.float32,0x42cf585a,0xb688cd8c,2 +np.float32,0xc2cf585a,0x3688cd8c,2 +np.float32,0x41d5a0d9,0x3f800000,2 +np.float32,0xc1d5a0d9,0xbf800000,2 +np.float32,0x4255a0d9,0xb633bc81,2 +np.float32,0xc255a0d9,0x3633bc81,2 +np.float32,0x42d5a0d9,0x36b3bc81,2 +np.float32,0xc2d5a0d9,0xb6b3bc81,2 +np.float32,0x41dbe958,0x3f3504e0,2 +np.float32,0xc1dbe958,0xbf3504e0,2 +np.float32,0x425be958,0xbf800000,2 +np.float32,0xc25be958,0x3f800000,2 +np.float32,0x42dbe958,0xb6deab75,2 +np.float32,0xc2dbe958,0x36deab75,2 +np.float32,0x41e231d6,0xb399a6a2,2 +np.float32,0xc1e231d6,0x3399a6a2,2 +np.float32,0x426231d6,0x3419a6a2,2 +np.float32,0xc26231d6,0xb419a6a2,2 +np.float32,0x42e231d6,0x3499a6a2,2 +np.float32,0xc2e231d6,0xb499a6a2,2 +np.float32,0x41e87a55,0xbf3504f8,2 +np.float32,0xc1e87a55,0x3f3504f8,2 +np.float32,0x42687a55,0x3f800000,2 +np.float32,0xc2687a55,0xbf800000,2 +np.float32,0x42e87a55,0xb5d2257b,2 +np.float32,0xc2e87a55,0x35d2257b,2 +np.float32,0x41eec2d4,0xbf800000,2 +np.float32,0xc1eec2d4,0x3f800000,2 +np.float32,0x426ec2d4,0xb5bef0a7,2 +np.float32,0xc26ec2d4,0x35bef0a7,2 +np.float32,0x42eec2d4,0x363ef0a7,2 +np.float32,0xc2eec2d4,0xb63ef0a7,2 +np.float32,0x41f50b53,0xbf3504e7,2 +np.float32,0xc1f50b53,0x3f3504e7,2 +np.float32,0x42750b53,0xbf800000,2 +np.float32,0xc2750b53,0x3f800000,2 +np.float32,0x42f50b53,0xb68a6748,2 +np.float32,0xc2f50b53,0x368a6748,2 +np.float32,0x41fb53d2,0x35b5563d,2 +np.float32,0xc1fb53d2,0xb5b5563d,2 +np.float32,0x427b53d2,0x3635563d,2 +np.float32,0xc27b53d2,0xb635563d,2 +np.float32,0x42fb53d2,0x36b5563d,2 +np.float32,0xc2fb53d2,0xb6b5563d,2 +np.float32,0x4200ce28,0x3f3504f0,2 +np.float32,0xc200ce28,0xbf3504f0,2 +np.float32,0x4280ce28,0x3f800000,2 +np.float32,0xc280ce28,0xbf800000,2 +np.float32,0x4300ce28,0x357dd672,2 +np.float32,0xc300ce28,0xb57dd672,2 +np.float32,0x4203f268,0x3f800000,2 +np.float32,0xc203f268,0xbf800000,2 +np.float32,0x4283f268,0xb6859a13,2 +np.float32,0xc283f268,0x36859a13,2 +np.float32,0x4303f268,0x37059a13,2 +np.float32,0xc303f268,0xb7059a13,2 +np.float32,0x420716a7,0x3f3504ee,2 +np.float32,0xc20716a7,0xbf3504ee,2 +np.float32,0x428716a7,0xbf800000,2 +np.float32,0xc28716a7,0x3f800000,2 +np.float32,0x430716a7,0xb5d88c6d,2 +np.float32,0xc30716a7,0x35d88c6d,2 +np.float32,0x420a3ae7,0xb6308908,2 +np.float32,0xc20a3ae7,0x36308908,2 +np.float32,0x428a3ae7,0x36b08908,2 +np.float32,0xc28a3ae7,0xb6b08908,2 +np.float32,0x430a3ae7,0x37308908,2 +np.float32,0xc30a3ae7,0xb7308908,2 +np.float32,0x420d5f26,0xbf350500,2 +np.float32,0xc20d5f26,0x3f350500,2 +np.float32,0x428d5f26,0x3f800000,2 +np.float32,0xc28d5f26,0xbf800000,2 +np.float32,0x430d5f26,0xb68c0105,2 +np.float32,0xc30d5f26,0x368c0105,2 +np.float32,0x42108365,0xbf800000,2 +np.float32,0xc2108365,0x3f800000,2 +np.float32,0x42908365,0x3592200d,2 +np.float32,0xc2908365,0xb592200d,2 +np.float32,0x43108365,0xb612200d,2 +np.float32,0xc3108365,0x3612200d,2 +np.float32,0x4213a7a5,0xbf3504df,2 +np.float32,0xc213a7a5,0x3f3504df,2 +np.float32,0x4293a7a5,0xbf800000,2 +np.float32,0xc293a7a5,0x3f800000,2 +np.float32,0x4313a7a5,0xb6e1deee,2 +np.float32,0xc313a7a5,0x36e1deee,2 +np.float32,0x4216cbe4,0x33ccde2e,2 +np.float32,0xc216cbe4,0xb3ccde2e,2 +np.float32,0x4296cbe4,0x344cde2e,2 +np.float32,0xc296cbe4,0xb44cde2e,2 +np.float32,0x4316cbe4,0x34ccde2e,2 +np.float32,0xc316cbe4,0xb4ccde2e,2 +np.float32,0x4219f024,0x3f35050f,2 +np.float32,0xc219f024,0xbf35050f,2 +np.float32,0x4299f024,0x3f800000,2 +np.float32,0xc299f024,0xbf800000,2 +np.float32,0x4319f024,0xb71bde6c,2 +np.float32,0xc319f024,0x371bde6c,2 +np.float32,0x421d1463,0x3f800000,2 +np.float32,0xc21d1463,0xbf800000,2 +np.float32,0x429d1463,0xb5c55799,2 +np.float32,0xc29d1463,0x35c55799,2 +np.float32,0x431d1463,0x36455799,2 +np.float32,0xc31d1463,0xb6455799,2 +np.float32,0x422038a3,0x3f3504d0,2 +np.float32,0xc22038a3,0xbf3504d0,2 +np.float32,0x42a038a3,0xbf800000,2 +np.float32,0xc2a038a3,0x3f800000,2 +np.float32,0x432038a3,0xb746cd61,2 +np.float32,0xc32038a3,0x3746cd61,2 +np.float32,0x42235ce2,0xb5b889b6,2 +np.float32,0xc2235ce2,0x35b889b6,2 +np.float32,0x42a35ce2,0x363889b6,2 +np.float32,0xc2a35ce2,0xb63889b6,2 +np.float32,0x43235ce2,0x36b889b6,2 +np.float32,0xc3235ce2,0xb6b889b6,2 +np.float32,0x42268121,0xbf3504f1,2 +np.float32,0xc2268121,0x3f3504f1,2 +np.float32,0x42a68121,0x3f800000,2 +np.float32,0xc2a68121,0xbf800000,2 +np.float32,0x43268121,0x35643aac,2 +np.float32,0xc3268121,0xb5643aac,2 +np.float32,0x4229a561,0xbf800000,2 +np.float32,0xc229a561,0x3f800000,2 +np.float32,0x42a9a561,0xb68733d0,2 +np.float32,0xc2a9a561,0x368733d0,2 +np.float32,0x4329a561,0x370733d0,2 +np.float32,0xc329a561,0xb70733d0,2 +np.float32,0x422cc9a0,0xbf3504ee,2 +np.float32,0xc22cc9a0,0x3f3504ee,2 +np.float32,0x42acc9a0,0xbf800000,2 +np.float32,0xc2acc9a0,0x3f800000,2 +np.float32,0x432cc9a0,0xb5e55a50,2 +np.float32,0xc32cc9a0,0x35e55a50,2 +np.float32,0x422fede0,0x363222c4,2 +np.float32,0xc22fede0,0xb63222c4,2 +np.float32,0x42afede0,0x36b222c4,2 +np.float32,0xc2afede0,0xb6b222c4,2 +np.float32,0x432fede0,0x373222c4,2 +np.float32,0xc32fede0,0xb73222c4,2 +np.float32,0x4233121f,0x3f350500,2 +np.float32,0xc233121f,0xbf350500,2 +np.float32,0x42b3121f,0x3f800000,2 +np.float32,0xc2b3121f,0xbf800000,2 +np.float32,0x4333121f,0xb68f347d,2 +np.float32,0xc333121f,0x368f347d,2 +np.float32,0x4236365e,0x3f800000,2 +np.float32,0xc236365e,0xbf800000,2 +np.float32,0x42b6365e,0x358bb91c,2 +np.float32,0xc2b6365e,0xb58bb91c,2 +np.float32,0x4336365e,0xb60bb91c,2 +np.float32,0xc336365e,0x360bb91c,2 +np.float32,0x42395a9e,0x3f3504df,2 +np.float32,0xc2395a9e,0xbf3504df,2 +np.float32,0x42b95a9e,0xbf800000,2 +np.float32,0xc2b95a9e,0x3f800000,2 +np.float32,0x43395a9e,0xb6e51267,2 +np.float32,0xc3395a9e,0x36e51267,2 +np.float32,0x423c7edd,0xb4000add,2 +np.float32,0xc23c7edd,0x34000add,2 +np.float32,0x42bc7edd,0x34800add,2 +np.float32,0xc2bc7edd,0xb4800add,2 +np.float32,0x433c7edd,0x35000add,2 +np.float32,0xc33c7edd,0xb5000add,2 +np.float32,0x423fa31d,0xbf35050f,2 +np.float32,0xc23fa31d,0x3f35050f,2 +np.float32,0x42bfa31d,0x3f800000,2 +np.float32,0xc2bfa31d,0xbf800000,2 +np.float32,0x433fa31d,0xb71d7828,2 +np.float32,0xc33fa31d,0x371d7828,2 +np.float32,0x4242c75c,0xbf800000,2 +np.float32,0xc242c75c,0x3f800000,2 +np.float32,0x42c2c75c,0xb5cbbe8a,2 +np.float32,0xc2c2c75c,0x35cbbe8a,2 +np.float32,0x4342c75c,0x364bbe8a,2 +np.float32,0xc342c75c,0xb64bbe8a,2 +np.float32,0x4245eb9c,0xbf3504d0,2 +np.float32,0xc245eb9c,0x3f3504d0,2 +np.float32,0x42c5eb9c,0xbf800000,2 +np.float32,0xc2c5eb9c,0x3f800000,2 +np.float32,0x4345eb9c,0xb748671d,2 +np.float32,0xc345eb9c,0x3748671d,2 +np.float32,0x42490fdb,0x35bbbd2e,2 +np.float32,0xc2490fdb,0xb5bbbd2e,2 +np.float32,0x42c90fdb,0x363bbd2e,2 +np.float32,0xc2c90fdb,0xb63bbd2e,2 +np.float32,0x43490fdb,0x36bbbd2e,2 +np.float32,0xc3490fdb,0xb6bbbd2e,2 +np.float32,0x424c341a,0x3f3504f1,2 +np.float32,0xc24c341a,0xbf3504f1,2 +np.float32,0x42cc341a,0x3f800000,2 +np.float32,0xc2cc341a,0xbf800000,2 +np.float32,0x434c341a,0x354a9ee6,2 +np.float32,0xc34c341a,0xb54a9ee6,2 +np.float32,0x424f585a,0x3f800000,2 +np.float32,0xc24f585a,0xbf800000,2 +np.float32,0x42cf585a,0xb688cd8c,2 +np.float32,0xc2cf585a,0x3688cd8c,2 +np.float32,0x434f585a,0x3708cd8c,2 +np.float32,0xc34f585a,0xb708cd8c,2 +np.float32,0x42527c99,0x3f3504ee,2 +np.float32,0xc2527c99,0xbf3504ee,2 +np.float32,0x42d27c99,0xbf800000,2 +np.float32,0xc2d27c99,0x3f800000,2 +np.float32,0x43527c99,0xb5f22833,2 +np.float32,0xc3527c99,0x35f22833,2 +np.float32,0x4255a0d9,0xb633bc81,2 +np.float32,0xc255a0d9,0x3633bc81,2 +np.float32,0x42d5a0d9,0x36b3bc81,2 +np.float32,0xc2d5a0d9,0xb6b3bc81,2 +np.float32,0x4355a0d9,0x3733bc81,2 +np.float32,0xc355a0d9,0xb733bc81,2 +np.float32,0x4258c518,0xbf350500,2 +np.float32,0xc258c518,0x3f350500,2 +np.float32,0x42d8c518,0x3f800000,2 +np.float32,0xc2d8c518,0xbf800000,2 +np.float32,0x4358c518,0xb69267f6,2 +np.float32,0xc358c518,0x369267f6,2 +np.float32,0x425be958,0xbf800000,2 +np.float32,0xc25be958,0x3f800000,2 +np.float32,0x42dbe958,0xb6deab75,2 +np.float32,0xc2dbe958,0x36deab75,2 +np.float32,0x435be958,0x375eab75,2 +np.float32,0xc35be958,0xb75eab75,2 +np.float32,0x425f0d97,0xbf3504df,2 +np.float32,0xc25f0d97,0x3f3504df,2 +np.float32,0x42df0d97,0xbf800000,2 +np.float32,0xc2df0d97,0x3f800000,2 +np.float32,0x435f0d97,0xb6e845e0,2 +np.float32,0xc35f0d97,0x36e845e0,2 +np.float32,0x426231d6,0x3419a6a2,2 +np.float32,0xc26231d6,0xb419a6a2,2 +np.float32,0x42e231d6,0x3499a6a2,2 +np.float32,0xc2e231d6,0xb499a6a2,2 +np.float32,0x436231d6,0x3519a6a2,2 +np.float32,0xc36231d6,0xb519a6a2,2 +np.float32,0x42655616,0x3f35050f,2 +np.float32,0xc2655616,0xbf35050f,2 +np.float32,0x42e55616,0x3f800000,2 +np.float32,0xc2e55616,0xbf800000,2 +np.float32,0x43655616,0xb71f11e5,2 +np.float32,0xc3655616,0x371f11e5,2 +np.float32,0x42687a55,0x3f800000,2 +np.float32,0xc2687a55,0xbf800000,2 +np.float32,0x42e87a55,0xb5d2257b,2 +np.float32,0xc2e87a55,0x35d2257b,2 +np.float32,0x43687a55,0x3652257b,2 +np.float32,0xc3687a55,0xb652257b,2 +np.float32,0x426b9e95,0x3f3504cf,2 +np.float32,0xc26b9e95,0xbf3504cf,2 +np.float32,0x42eb9e95,0xbf800000,2 +np.float32,0xc2eb9e95,0x3f800000,2 +np.float32,0x436b9e95,0xb74a00d9,2 +np.float32,0xc36b9e95,0x374a00d9,2 +np.float32,0x426ec2d4,0xb5bef0a7,2 +np.float32,0xc26ec2d4,0x35bef0a7,2 +np.float32,0x42eec2d4,0x363ef0a7,2 +np.float32,0xc2eec2d4,0xb63ef0a7,2 +np.float32,0x436ec2d4,0x36bef0a7,2 +np.float32,0xc36ec2d4,0xb6bef0a7,2 +np.float32,0x4271e713,0xbf3504f1,2 +np.float32,0xc271e713,0x3f3504f1,2 +np.float32,0x42f1e713,0x3f800000,2 +np.float32,0xc2f1e713,0xbf800000,2 +np.float32,0x4371e713,0x35310321,2 +np.float32,0xc371e713,0xb5310321,2 +np.float32,0x42750b53,0xbf800000,2 +np.float32,0xc2750b53,0x3f800000,2 +np.float32,0x42f50b53,0xb68a6748,2 +np.float32,0xc2f50b53,0x368a6748,2 +np.float32,0x43750b53,0x370a6748,2 +np.float32,0xc3750b53,0xb70a6748,2 +np.float32,0x42782f92,0xbf3504ee,2 +np.float32,0xc2782f92,0x3f3504ee,2 +np.float32,0x42f82f92,0xbf800000,2 +np.float32,0xc2f82f92,0x3f800000,2 +np.float32,0x43782f92,0xb5fef616,2 +np.float32,0xc3782f92,0x35fef616,2 +np.float32,0x427b53d2,0x3635563d,2 +np.float32,0xc27b53d2,0xb635563d,2 +np.float32,0x42fb53d2,0x36b5563d,2 +np.float32,0xc2fb53d2,0xb6b5563d,2 +np.float32,0x437b53d2,0x3735563d,2 +np.float32,0xc37b53d2,0xb735563d,2 +np.float32,0x427e7811,0x3f350500,2 +np.float32,0xc27e7811,0xbf350500,2 +np.float32,0x42fe7811,0x3f800000,2 +np.float32,0xc2fe7811,0xbf800000,2 +np.float32,0x437e7811,0xb6959b6f,2 +np.float32,0xc37e7811,0x36959b6f,2 +np.float32,0x4280ce28,0x3f800000,2 +np.float32,0xc280ce28,0xbf800000,2 +np.float32,0x4300ce28,0x357dd672,2 +np.float32,0xc300ce28,0xb57dd672,2 +np.float32,0x4380ce28,0xb5fdd672,2 +np.float32,0xc380ce28,0x35fdd672,2 +np.float32,0x42826048,0x3f3504de,2 +np.float32,0xc2826048,0xbf3504de,2 +np.float32,0x43026048,0xbf800000,2 +np.float32,0xc3026048,0x3f800000,2 +np.float32,0x43826048,0xb6eb7958,2 +np.float32,0xc3826048,0x36eb7958,2 +np.float32,0x4283f268,0xb6859a13,2 +np.float32,0xc283f268,0x36859a13,2 +np.float32,0x4303f268,0x37059a13,2 +np.float32,0xc303f268,0xb7059a13,2 +np.float32,0x4383f268,0x37859a13,2 +np.float32,0xc383f268,0xb7859a13,2 +np.float32,0x42858487,0xbf3504e2,2 +np.float32,0xc2858487,0x3f3504e2,2 +np.float32,0x43058487,0x3f800000,2 +np.float32,0xc3058487,0xbf800000,2 +np.float32,0x43858487,0x36bea8be,2 +np.float32,0xc3858487,0xb6bea8be,2 +np.float32,0x428716a7,0xbf800000,2 +np.float32,0xc28716a7,0x3f800000,2 +np.float32,0x430716a7,0xb5d88c6d,2 +np.float32,0xc30716a7,0x35d88c6d,2 +np.float32,0x438716a7,0x36588c6d,2 +np.float32,0xc38716a7,0xb6588c6d,2 +np.float32,0x4288a8c7,0xbf3504cf,2 +np.float32,0xc288a8c7,0x3f3504cf,2 +np.float32,0x4308a8c7,0xbf800000,2 +np.float32,0xc308a8c7,0x3f800000,2 +np.float32,0x4388a8c7,0xb74b9a96,2 +np.float32,0xc388a8c7,0x374b9a96,2 +np.float32,0x428a3ae7,0x36b08908,2 +np.float32,0xc28a3ae7,0xb6b08908,2 +np.float32,0x430a3ae7,0x37308908,2 +np.float32,0xc30a3ae7,0xb7308908,2 +np.float32,0x438a3ae7,0x37b08908,2 +np.float32,0xc38a3ae7,0xb7b08908,2 +np.float32,0x428bcd06,0x3f3504f2,2 +np.float32,0xc28bcd06,0xbf3504f2,2 +np.float32,0x430bcd06,0x3f800000,2 +np.float32,0xc30bcd06,0xbf800000,2 +np.float32,0x438bcd06,0x3517675b,2 +np.float32,0xc38bcd06,0xb517675b,2 +np.float32,0x428d5f26,0x3f800000,2 +np.float32,0xc28d5f26,0xbf800000,2 +np.float32,0x430d5f26,0xb68c0105,2 +np.float32,0xc30d5f26,0x368c0105,2 +np.float32,0x438d5f26,0x370c0105,2 +np.float32,0xc38d5f26,0xb70c0105,2 +np.float32,0x428ef146,0x3f3504c0,2 +np.float32,0xc28ef146,0xbf3504c0,2 +np.float32,0x430ef146,0xbf800000,2 +np.float32,0xc30ef146,0x3f800000,2 +np.float32,0x438ef146,0xb790bc40,2 +np.float32,0xc38ef146,0x3790bc40,2 +np.float32,0x42908365,0x3592200d,2 +np.float32,0xc2908365,0xb592200d,2 +np.float32,0x43108365,0xb612200d,2 +np.float32,0xc3108365,0x3612200d,2 +np.float32,0x43908365,0xb692200d,2 +np.float32,0xc3908365,0x3692200d,2 +np.float32,0x42921585,0xbf350501,2 +np.float32,0xc2921585,0x3f350501,2 +np.float32,0x43121585,0x3f800000,2 +np.float32,0xc3121585,0xbf800000,2 +np.float32,0x43921585,0xb698cee8,2 +np.float32,0xc3921585,0x3698cee8,2 +np.float32,0x4293a7a5,0xbf800000,2 +np.float32,0xc293a7a5,0x3f800000,2 +np.float32,0x4313a7a5,0xb6e1deee,2 +np.float32,0xc313a7a5,0x36e1deee,2 +np.float32,0x4393a7a5,0x3761deee,2 +np.float32,0xc393a7a5,0xb761deee,2 +np.float32,0x429539c5,0xbf3504b1,2 +np.float32,0xc29539c5,0x3f3504b1,2 +np.float32,0x431539c5,0xbf800000,2 +np.float32,0xc31539c5,0x3f800000,2 +np.float32,0x439539c5,0xb7bbab34,2 +np.float32,0xc39539c5,0x37bbab34,2 +np.float32,0x4296cbe4,0x344cde2e,2 +np.float32,0xc296cbe4,0xb44cde2e,2 +np.float32,0x4316cbe4,0x34ccde2e,2 +np.float32,0xc316cbe4,0xb4ccde2e,2 +np.float32,0x4396cbe4,0x354cde2e,2 +np.float32,0xc396cbe4,0xb54cde2e,2 +np.float32,0x42985e04,0x3f350510,2 +np.float32,0xc2985e04,0xbf350510,2 +np.float32,0x43185e04,0x3f800000,2 +np.float32,0xc3185e04,0xbf800000,2 +np.float32,0x43985e04,0xb722455d,2 +np.float32,0xc3985e04,0x3722455d,2 +np.float32,0x4299f024,0x3f800000,2 +np.float32,0xc299f024,0xbf800000,2 +np.float32,0x4319f024,0xb71bde6c,2 +np.float32,0xc319f024,0x371bde6c,2 +np.float32,0x4399f024,0x379bde6c,2 +np.float32,0xc399f024,0xb79bde6c,2 +np.float32,0x429b8243,0x3f3504fc,2 +np.float32,0xc29b8243,0xbf3504fc,2 +np.float32,0x431b8243,0xbf800000,2 +np.float32,0xc31b8243,0x3f800000,2 +np.float32,0x439b8243,0x364b2eb8,2 +np.float32,0xc39b8243,0xb64b2eb8,2 +np.float32,0x435b2047,0xbf350525,2 +np.float32,0x42a038a2,0xbf800000,2 +np.float32,0x432038a2,0x3664ca7e,2 +np.float32,0x4345eb9b,0x365e638c,2 +np.float32,0x42c5eb9b,0xbf800000,2 +np.float32,0x42eb9e94,0xbf800000,2 +np.float32,0x4350ea79,0x3f800000,2 +np.float32,0x42dbe957,0x3585522a,2 +np.float32,0x425be957,0xbf800000,2 +np.float32,0x435be957,0xb605522a,2 +np.float32,0x476362a2,0xbd7ff911,2 +np.float32,0x464c99a4,0x3e7f4d41,2 +np.float32,0x4471f73d,0x3e7fe1b0,2 +np.float32,0x445a6752,0x3e7ef367,2 +np.float32,0x474fa400,0x3e7f9fcd,2 +np.float32,0x45c1e72f,0xbe7fc7af,2 +np.float32,0x4558c91d,0x3e7e9f31,2 +np.float32,0x43784f94,0xbdff6654,2 +np.float32,0x466e8500,0xbe7ea0a3,2 +np.float32,0x468e1c25,0x3e7e22fb,2 +np.float32,0x44ea6cfc,0x3dff70c3,2 +np.float32,0x4605126c,0x3e7f89ef,2 +np.float32,0x4788b3c6,0xbb87d853,2 +np.float32,0x4531b042,0x3dffd163,2 +np.float32,0x43f1f71d,0x3dfff387,2 +np.float32,0x462c3fa5,0xbd7fe13d,2 +np.float32,0x441c5354,0xbdff76b4,2 +np.float32,0x44908b69,0x3e7dcf0d,2 +np.float32,0x478813ad,0xbe7e9d80,2 +np.float32,0x441c4351,0x3dff937b,2 +np.float64,0x1,0x1,4 +np.float64,0x8000000000000001,0x8000000000000001,4 +np.float64,0x10000000000000,0x10000000000000,4 +np.float64,0x8010000000000000,0x8010000000000000,4 +np.float64,0x7fefffffffffffff,0x3f7452fc98b34e97,4 +np.float64,0xffefffffffffffff,0xbf7452fc98b34e97,4 +np.float64,0x7ff0000000000000,0xfff8000000000000,4 +np.float64,0xfff0000000000000,0xfff8000000000000,4 +np.float64,0x7ff8000000000000,0x7ff8000000000000,4 +np.float64,0x7ff4000000000000,0x7ffc000000000000,4 +np.float64,0xbfda51b226b4a364,0xbfd9956328ff876c,4 +np.float64,0xbfb4a65aee294cb8,0xbfb4a09fd744f8a5,4 +np.float64,0xbfd73b914fae7722,0xbfd6b9cce55af379,4 +np.float64,0xbfd90c12b4b21826,0xbfd869a3867b51c2,4 +np.float64,0x3fe649bb3d6c9376,0x3fe48778d9b48a21,4 +np.float64,0xbfd5944532ab288a,0xbfd52c30e1951b42,4 +np.float64,0x3fb150c45222a190,0x3fb14d633eb8275d,4 +np.float64,0x3fe4a6ffa9e94e00,0x3fe33f8a95c33299,4 +np.float64,0x3fe8d2157171a42a,0x3fe667d904ac95a6,4 +np.float64,0xbfa889f52c3113f0,0xbfa8878d90a23fa5,4 +np.float64,0x3feb3234bef6646a,0x3fe809d541d9017a,4 +np.float64,0x3fc6de266f2dbc50,0x3fc6bf0ee80a0d86,4 +np.float64,0x3fe8455368f08aa6,0x3fe6028254338ed5,4 +np.float64,0xbfe5576079eaaec1,0xbfe3cb4a8f6bc3f5,4 +np.float64,0xbfe9f822ff73f046,0xbfe7360d7d5cb887,4 +np.float64,0xbfb1960e7e232c20,0xbfb1928438258602,4 +np.float64,0xbfca75938d34eb28,0xbfca4570979bf2fa,4 +np.float64,0x3fd767dd15aecfbc,0x3fd6e33039018bab,4 +np.float64,0xbfe987750ef30eea,0xbfe6e7ed30ce77f0,4 +np.float64,0xbfe87f95a1f0ff2b,0xbfe62ca7e928bb2a,4 +np.float64,0xbfd2465301a48ca6,0xbfd2070245775d76,4 +np.float64,0xbfb1306ed22260e0,0xbfb12d2088eaa4f9,4 +np.float64,0xbfd8089010b01120,0xbfd778f9db77f2f3,4 +np.float64,0x3fbf9cf4ee3f39f0,0x3fbf88674fde1ca2,4 +np.float64,0x3fe6d8468a6db08e,0x3fe4f403f38b7bec,4 +np.float64,0xbfd9e5deefb3cbbe,0xbfd932692c722351,4 +np.float64,0x3fd1584d55a2b09c,0x3fd122253eeecc2e,4 +np.float64,0x3fe857979cf0af30,0x3fe60fc12b5ba8db,4 +np.float64,0x3fe3644149e6c882,0x3fe239f47013cfe6,4 +np.float64,0xbfe22ea62be45d4c,0xbfe13834c17d56fe,4 +np.float64,0xbfe8d93e1df1b27c,0xbfe66cf4ee467fd2,4 +np.float64,0xbfe9c497c9f38930,0xbfe7127417da4204,4 +np.float64,0x3fd6791cecacf238,0x3fd6039ccb5a7fde,4 +np.float64,0xbfc1dc1b1523b838,0xbfc1cd48edd9ae19,4 +np.float64,0xbfc92a8491325508,0xbfc901176e0158a5,4 +np.float64,0x3fa8649b3430c940,0x3fa8623e82d9504f,4 +np.float64,0x3fe0bed6a1617dae,0x3fdffbb307fb1abe,4 +np.float64,0x3febdf7765f7beee,0x3fe87ad01a89b74a,4 +np.float64,0xbfd3a56d46a74ada,0xbfd356cf41bf83cd,4 +np.float64,0x3fd321d824a643b0,0x3fd2d93846a224b3,4 +np.float64,0xbfc6a49fb52d4940,0xbfc686704906e7d3,4 +np.float64,0xbfdd4103c9ba8208,0xbfdc3ef0c03615b4,4 +np.float64,0xbfe0b78a51e16f14,0xbfdfef0d9ffc38b5,4 +np.float64,0xbfdac7a908b58f52,0xbfda0158956ceecf,4 +np.float64,0xbfbfbf12f23f7e28,0xbfbfaa428989258c,4 +np.float64,0xbfd55f5aa2aabeb6,0xbfd4fa39de65f33a,4 +np.float64,0x3fe06969abe0d2d4,0x3fdf6744fafdd9cf,4 +np.float64,0x3fe56ab8be6ad572,0x3fe3da7a1986d543,4 +np.float64,0xbfeefbbec67df77e,0xbfea5d426132f4aa,4 +np.float64,0x3fe6e1f49cedc3ea,0x3fe4fb53f3d8e3d5,4 +np.float64,0x3feceb231c79d646,0x3fe923d3efa55414,4 +np.float64,0xbfd03dd08ea07ba2,0xbfd011549aa1998a,4 +np.float64,0xbfd688327aad1064,0xbfd611c61b56adbe,4 +np.float64,0xbfde3249d8bc6494,0xbfdd16a7237a39d5,4 +np.float64,0x3febd4b65677a96c,0x3fe873e1a401ef03,4 +np.float64,0xbfe46bd2b368d7a6,0xbfe31023c2467749,4 +np.float64,0x3fbf9f5cde3f3ec0,0x3fbf8aca8ec53c45,4 +np.float64,0x3fc20374032406e8,0x3fc1f43f1f2f4d5e,4 +np.float64,0xbfec143b16f82876,0xbfe89caa42582381,4 +np.float64,0xbfd14fa635a29f4c,0xbfd119ced11da669,4 +np.float64,0x3fe25236d4e4a46e,0x3fe156242d644b7a,4 +np.float64,0xbfe4ed793469daf2,0xbfe377a88928fd77,4 +np.float64,0xbfb363572626c6b0,0xbfb35e98d8fe87ae,4 +np.float64,0xbfb389d5aa2713a8,0xbfb384fae55565a7,4 +np.float64,0x3fca6e001934dc00,0x3fca3e0661eaca84,4 +np.float64,0x3fe748f3f76e91e8,0x3fe548ab2168aea6,4 +np.float64,0x3fef150efdfe2a1e,0x3fea6b92d74f60d3,4 +np.float64,0xbfd14b52b1a296a6,0xbfd115a387c0fa93,4 +np.float64,0x3fe3286b5ce650d6,0x3fe208a6469a7527,4 +np.float64,0xbfd57b4f4baaf69e,0xbfd514a12a9f7ab0,4 +np.float64,0xbfef14bd467e297b,0xbfea6b64bbfd42ce,4 +np.float64,0xbfe280bc90650179,0xbfe17d2c49955dba,4 +np.float64,0x3fca8759d7350eb0,0x3fca56d5c17bbc14,4 +np.float64,0xbfdf988f30bf311e,0xbfde53f96f69b05f,4 +np.float64,0x3f6b6eeb4036de00,0x3f6b6ee7e3f86f9a,4 +np.float64,0xbfed560be8faac18,0xbfe9656c5cf973d8,4 +np.float64,0x3fc6102c592c2058,0x3fc5f43efad5396d,4 +np.float64,0xbfdef64ed2bdec9e,0xbfddc4b7fbd45aea,4 +np.float64,0x3fe814acd570295a,0x3fe5df183d543bfe,4 +np.float64,0x3fca21313f344260,0x3fc9f2d47f64fbe2,4 +np.float64,0xbfe89932cc713266,0xbfe63f186a2f60ce,4 +np.float64,0x3fe4ffcff169ffa0,0x3fe386336115ee21,4 +np.float64,0x3fee6964087cd2c8,0x3fea093d31e2c2c5,4 +np.float64,0xbfbeea604e3dd4c0,0xbfbed72734852669,4 +np.float64,0xbfea1954fb7432aa,0xbfe74cdad8720032,4 +np.float64,0x3fea3e1a5ef47c34,0x3fe765ffba65a11d,4 +np.float64,0x3fcedb850b3db708,0x3fce8f39d92f00ba,4 +np.float64,0x3fd3b52d41a76a5c,0x3fd365d22b0003f9,4 +np.float64,0xbfa4108a0c282110,0xbfa40f397fcd844f,4 +np.float64,0x3fd7454c57ae8a98,0x3fd6c2e5542c6c83,4 +np.float64,0xbfeecd3c7a7d9a79,0xbfea42ca943a1695,4 +np.float64,0xbfdddda397bbbb48,0xbfdccb27283d4c4c,4 +np.float64,0x3fe6b52cf76d6a5a,0x3fe4d96ff32925ff,4 +np.float64,0xbfa39a75ec2734f0,0xbfa3993c0da84f87,4 +np.float64,0x3fdd3fe6fdba7fcc,0x3fdc3df12fe9e525,4 +np.float64,0xbfb57a98162af530,0xbfb5742525d5fbe2,4 +np.float64,0xbfd3e166cfa7c2ce,0xbfd38ff2891be9b0,4 +np.float64,0x3fdb6a04f9b6d408,0x3fda955e5018e9dc,4 +np.float64,0x3fe4ab03a4e95608,0x3fe342bfa76e1aa8,4 +np.float64,0xbfe6c8480b6d9090,0xbfe4e7eaa935b3f5,4 +np.float64,0xbdd6b5a17bae,0xbdd6b5a17bae,4 +np.float64,0xd6591979acb23,0xd6591979acb23,4 +np.float64,0x5adbed90b5b7e,0x5adbed90b5b7e,4 +np.float64,0xa664c5314cc99,0xa664c5314cc99,4 +np.float64,0x1727fb162e500,0x1727fb162e500,4 +np.float64,0xdb49a93db6935,0xdb49a93db6935,4 +np.float64,0xb10c958d62193,0xb10c958d62193,4 +np.float64,0xad38276f5a705,0xad38276f5a705,4 +np.float64,0x1d5d0b983aba2,0x1d5d0b983aba2,4 +np.float64,0x915f48e122be9,0x915f48e122be9,4 +np.float64,0x475958ae8eb2c,0x475958ae8eb2c,4 +np.float64,0x3af8406675f09,0x3af8406675f09,4 +np.float64,0x655e88a4cabd2,0x655e88a4cabd2,4 +np.float64,0x40fee8ce81fde,0x40fee8ce81fde,4 +np.float64,0xab83103f57062,0xab83103f57062,4 +np.float64,0x7cf934b8f9f27,0x7cf934b8f9f27,4 +np.float64,0x29f7524853eeb,0x29f7524853eeb,4 +np.float64,0x4a5e954894bd3,0x4a5e954894bd3,4 +np.float64,0x24638f3a48c73,0x24638f3a48c73,4 +np.float64,0xa4f32fc749e66,0xa4f32fc749e66,4 +np.float64,0xf8e92df7f1d26,0xf8e92df7f1d26,4 +np.float64,0x292e9d50525d4,0x292e9d50525d4,4 +np.float64,0xe937e897d26fd,0xe937e897d26fd,4 +np.float64,0xd3bde1d5a77bc,0xd3bde1d5a77bc,4 +np.float64,0xa447ffd548900,0xa447ffd548900,4 +np.float64,0xa3b7b691476f7,0xa3b7b691476f7,4 +np.float64,0x490095c892013,0x490095c892013,4 +np.float64,0xfc853235f90a7,0xfc853235f90a7,4 +np.float64,0x5a8bc082b5179,0x5a8bc082b5179,4 +np.float64,0x1baca45a37595,0x1baca45a37595,4 +np.float64,0x2164120842c83,0x2164120842c83,4 +np.float64,0x66692bdeccd26,0x66692bdeccd26,4 +np.float64,0xf205bdd3e40b8,0xf205bdd3e40b8,4 +np.float64,0x7c3fff98f8801,0x7c3fff98f8801,4 +np.float64,0xccdf10e199bf,0xccdf10e199bf,4 +np.float64,0x92db8e8125b8,0x92db8e8125b8,4 +np.float64,0x5789a8d6af136,0x5789a8d6af136,4 +np.float64,0xbdda869d7bb51,0xbdda869d7bb51,4 +np.float64,0xb665e0596ccbc,0xb665e0596ccbc,4 +np.float64,0x74e6b46ee9cd7,0x74e6b46ee9cd7,4 +np.float64,0x4f39cf7c9e73b,0x4f39cf7c9e73b,4 +np.float64,0xfdbf3907fb7e7,0xfdbf3907fb7e7,4 +np.float64,0xafdef4d55fbdf,0xafdef4d55fbdf,4 +np.float64,0xb49858236930b,0xb49858236930b,4 +np.float64,0x3ebe21d47d7c5,0x3ebe21d47d7c5,4 +np.float64,0x5b620512b6c41,0x5b620512b6c41,4 +np.float64,0x31918cda63232,0x31918cda63232,4 +np.float64,0x68b5741ed16af,0x68b5741ed16af,4 +np.float64,0xa5c09a5b4b814,0xa5c09a5b4b814,4 +np.float64,0x55f51c14abea4,0x55f51c14abea4,4 +np.float64,0xda8a3e41b515,0xda8a3e41b515,4 +np.float64,0x9ea9c8513d539,0x9ea9c8513d539,4 +np.float64,0x7f23b964fe478,0x7f23b964fe478,4 +np.float64,0xf6e08c7bedc12,0xf6e08c7bedc12,4 +np.float64,0x7267aa24e4cf6,0x7267aa24e4cf6,4 +np.float64,0x236bb93a46d78,0x236bb93a46d78,4 +np.float64,0x9a98430b35309,0x9a98430b35309,4 +np.float64,0xbb683fef76d08,0xbb683fef76d08,4 +np.float64,0x1ff0eb6e3fe1e,0x1ff0eb6e3fe1e,4 +np.float64,0xf524038fea481,0xf524038fea481,4 +np.float64,0xd714e449ae29d,0xd714e449ae29d,4 +np.float64,0x4154fd7682aa0,0x4154fd7682aa0,4 +np.float64,0x5b8d2f6cb71a7,0x5b8d2f6cb71a7,4 +np.float64,0xc91aa21d92355,0xc91aa21d92355,4 +np.float64,0xbd94fd117b2a0,0xbd94fd117b2a0,4 +np.float64,0x685b207ad0b65,0x685b207ad0b65,4 +np.float64,0xd2485b05a490c,0xd2485b05a490c,4 +np.float64,0x151ea5e62a3d6,0x151ea5e62a3d6,4 +np.float64,0x2635a7164c6b6,0x2635a7164c6b6,4 +np.float64,0x88ae3b5d115c8,0x88ae3b5d115c8,4 +np.float64,0x8a055a55140ac,0x8a055a55140ac,4 +np.float64,0x756f7694eadef,0x756f7694eadef,4 +np.float64,0x866d74630cdaf,0x866d74630cdaf,4 +np.float64,0x39e44f2873c8b,0x39e44f2873c8b,4 +np.float64,0x2a07ceb6540fb,0x2a07ceb6540fb,4 +np.float64,0xc52b96398a573,0xc52b96398a573,4 +np.float64,0x9546543b2a8cb,0x9546543b2a8cb,4 +np.float64,0x5b995b90b732c,0x5b995b90b732c,4 +np.float64,0x2de10a565bc22,0x2de10a565bc22,4 +np.float64,0x3b06ee94760df,0x3b06ee94760df,4 +np.float64,0xb18e77a5631cf,0xb18e77a5631cf,4 +np.float64,0x3b89ae3a77137,0x3b89ae3a77137,4 +np.float64,0xd9b0b6e5b3617,0xd9b0b6e5b3617,4 +np.float64,0x30b2310861647,0x30b2310861647,4 +np.float64,0x326a3ab464d48,0x326a3ab464d48,4 +np.float64,0x4c18610a9830d,0x4c18610a9830d,4 +np.float64,0x541dea42a83be,0x541dea42a83be,4 +np.float64,0xcd027dbf9a050,0xcd027dbf9a050,4 +np.float64,0x780a0f80f015,0x780a0f80f015,4 +np.float64,0x740ed5b2e81db,0x740ed5b2e81db,4 +np.float64,0xc226814d844d0,0xc226814d844d0,4 +np.float64,0xde958541bd2b1,0xde958541bd2b1,4 +np.float64,0xb563d3296ac7b,0xb563d3296ac7b,4 +np.float64,0x1db3b0b83b677,0x1db3b0b83b677,4 +np.float64,0xa7b0275d4f605,0xa7b0275d4f605,4 +np.float64,0x72f8d038e5f1b,0x72f8d038e5f1b,4 +np.float64,0x860ed1350c1da,0x860ed1350c1da,4 +np.float64,0x79f88262f3f11,0x79f88262f3f11,4 +np.float64,0x8817761f102ef,0x8817761f102ef,4 +np.float64,0xac44784b5888f,0xac44784b5888f,4 +np.float64,0x800fd594241fab28,0x800fd594241fab28,4 +np.float64,0x800ede32f8ddbc66,0x800ede32f8ddbc66,4 +np.float64,0x800de4c1121bc982,0x800de4c1121bc982,4 +np.float64,0x80076ebcddcedd7a,0x80076ebcddcedd7a,4 +np.float64,0x800b3fee06567fdc,0x800b3fee06567fdc,4 +np.float64,0x800b444426b68889,0x800b444426b68889,4 +np.float64,0x800b1c037a563807,0x800b1c037a563807,4 +np.float64,0x8001eb88c2a3d712,0x8001eb88c2a3d712,4 +np.float64,0x80058aae6dab155e,0x80058aae6dab155e,4 +np.float64,0x80083df2d4f07be6,0x80083df2d4f07be6,4 +np.float64,0x800e3b19d97c7634,0x800e3b19d97c7634,4 +np.float64,0x800a71c6f374e38e,0x800a71c6f374e38e,4 +np.float64,0x80048557f1490ab1,0x80048557f1490ab1,4 +np.float64,0x8000a00e6b01401e,0x8000a00e6b01401e,4 +np.float64,0x800766a3e2cecd49,0x800766a3e2cecd49,4 +np.float64,0x80015eb44602bd69,0x80015eb44602bd69,4 +np.float64,0x800bde885a77bd11,0x800bde885a77bd11,4 +np.float64,0x800224c53ea4498b,0x800224c53ea4498b,4 +np.float64,0x80048e8c6a291d1a,0x80048e8c6a291d1a,4 +np.float64,0x800b667e4af6ccfd,0x800b667e4af6ccfd,4 +np.float64,0x800ae3d7e395c7b0,0x800ae3d7e395c7b0,4 +np.float64,0x80086c245550d849,0x80086c245550d849,4 +np.float64,0x800d7d25f6fafa4c,0x800d7d25f6fafa4c,4 +np.float64,0x800f8d9ab0ff1b35,0x800f8d9ab0ff1b35,4 +np.float64,0x800690e949cd21d3,0x800690e949cd21d3,4 +np.float64,0x8003022381060448,0x8003022381060448,4 +np.float64,0x80085e0dad70bc1c,0x80085e0dad70bc1c,4 +np.float64,0x800e2ffc369c5ff9,0x800e2ffc369c5ff9,4 +np.float64,0x800b629b5af6c537,0x800b629b5af6c537,4 +np.float64,0x800fdc964b7fb92d,0x800fdc964b7fb92d,4 +np.float64,0x80036bb4b1c6d76a,0x80036bb4b1c6d76a,4 +np.float64,0x800b382f7f16705f,0x800b382f7f16705f,4 +np.float64,0x800ebac9445d7593,0x800ebac9445d7593,4 +np.float64,0x80015075c3e2a0ec,0x80015075c3e2a0ec,4 +np.float64,0x8002a6ec5ce54dd9,0x8002a6ec5ce54dd9,4 +np.float64,0x8009fab74a93f56f,0x8009fab74a93f56f,4 +np.float64,0x800c94b9ea992974,0x800c94b9ea992974,4 +np.float64,0x800dc2efd75b85e0,0x800dc2efd75b85e0,4 +np.float64,0x800be6400d57cc80,0x800be6400d57cc80,4 +np.float64,0x80021f6858443ed1,0x80021f6858443ed1,4 +np.float64,0x800600e2ac4c01c6,0x800600e2ac4c01c6,4 +np.float64,0x800a2159e6b442b4,0x800a2159e6b442b4,4 +np.float64,0x800c912f4bb9225f,0x800c912f4bb9225f,4 +np.float64,0x800a863a9db50c76,0x800a863a9db50c76,4 +np.float64,0x800ac16851d582d1,0x800ac16851d582d1,4 +np.float64,0x8003f7d32e87efa7,0x8003f7d32e87efa7,4 +np.float64,0x800be4eee3d7c9de,0x800be4eee3d7c9de,4 +np.float64,0x80069ff0ac4d3fe2,0x80069ff0ac4d3fe2,4 +np.float64,0x80061c986d4c3932,0x80061c986d4c3932,4 +np.float64,0x8000737b4de0e6f7,0x8000737b4de0e6f7,4 +np.float64,0x8002066ef7440cdf,0x8002066ef7440cdf,4 +np.float64,0x8001007050c200e1,0x8001007050c200e1,4 +np.float64,0x8008df9fa351bf40,0x8008df9fa351bf40,4 +np.float64,0x800f8394ee5f072a,0x800f8394ee5f072a,4 +np.float64,0x80008e0b01c11c17,0x80008e0b01c11c17,4 +np.float64,0x800f7088ed3ee112,0x800f7088ed3ee112,4 +np.float64,0x800285b86f650b72,0x800285b86f650b72,4 +np.float64,0x8008ec18af51d832,0x8008ec18af51d832,4 +np.float64,0x800da08523bb410a,0x800da08523bb410a,4 +np.float64,0x800de853ca7bd0a8,0x800de853ca7bd0a8,4 +np.float64,0x8008c8aefad1915e,0x8008c8aefad1915e,4 +np.float64,0x80010c39d5821874,0x80010c39d5821874,4 +np.float64,0x8009208349724107,0x8009208349724107,4 +np.float64,0x800783783f0f06f1,0x800783783f0f06f1,4 +np.float64,0x80025caf9984b960,0x80025caf9984b960,4 +np.float64,0x800bc76fa6778ee0,0x800bc76fa6778ee0,4 +np.float64,0x80017e2f89a2fc60,0x80017e2f89a2fc60,4 +np.float64,0x800ef169843de2d3,0x800ef169843de2d3,4 +np.float64,0x80098a5f7db314bf,0x80098a5f7db314bf,4 +np.float64,0x800d646f971ac8df,0x800d646f971ac8df,4 +np.float64,0x800110d1dc6221a4,0x800110d1dc6221a4,4 +np.float64,0x800f8b422a1f1684,0x800f8b422a1f1684,4 +np.float64,0x800785c97dcf0b94,0x800785c97dcf0b94,4 +np.float64,0x800da201283b4403,0x800da201283b4403,4 +np.float64,0x800a117cc7b422fa,0x800a117cc7b422fa,4 +np.float64,0x80024731cfa48e64,0x80024731cfa48e64,4 +np.float64,0x800199d456c333a9,0x800199d456c333a9,4 +np.float64,0x8005f66bab8becd8,0x8005f66bab8becd8,4 +np.float64,0x8008e7227c11ce45,0x8008e7227c11ce45,4 +np.float64,0x8007b66cc42f6cda,0x8007b66cc42f6cda,4 +np.float64,0x800669e6f98cd3cf,0x800669e6f98cd3cf,4 +np.float64,0x800aed917375db23,0x800aed917375db23,4 +np.float64,0x8008b6dd15116dbb,0x8008b6dd15116dbb,4 +np.float64,0x800f49869cfe930d,0x800f49869cfe930d,4 +np.float64,0x800a712661b4e24d,0x800a712661b4e24d,4 +np.float64,0x800944e816f289d1,0x800944e816f289d1,4 +np.float64,0x800eba0f8a1d741f,0x800eba0f8a1d741f,4 +np.float64,0x800cf6ded139edbe,0x800cf6ded139edbe,4 +np.float64,0x80023100c6246202,0x80023100c6246202,4 +np.float64,0x800c5a94add8b52a,0x800c5a94add8b52a,4 +np.float64,0x800adf329b95be66,0x800adf329b95be66,4 +np.float64,0x800af9afc115f360,0x800af9afc115f360,4 +np.float64,0x800d66ce837acd9d,0x800d66ce837acd9d,4 +np.float64,0x8003ffb5e507ff6d,0x8003ffb5e507ff6d,4 +np.float64,0x80027d280024fa51,0x80027d280024fa51,4 +np.float64,0x800fc37e1d1f86fc,0x800fc37e1d1f86fc,4 +np.float64,0x800fc7258b9f8e4b,0x800fc7258b9f8e4b,4 +np.float64,0x8003fb5789e7f6b0,0x8003fb5789e7f6b0,4 +np.float64,0x800eb4e7a13d69cf,0x800eb4e7a13d69cf,4 +np.float64,0x800951850952a30a,0x800951850952a30a,4 +np.float64,0x3fed4071be3a80e3,0x3fe95842074431df,4 +np.float64,0x3f8d2341203a4682,0x3f8d2300b453bd9f,4 +np.float64,0x3fdc8ce332b919c6,0x3fdb9cdf1440c28f,4 +np.float64,0x3fdc69bd84b8d37b,0x3fdb7d25c8166b7b,4 +np.float64,0x3fc4c22ad0298456,0x3fc4aae73e231b4f,4 +np.float64,0x3fea237809f446f0,0x3fe753cc6ca96193,4 +np.float64,0x3fd34cf6462699ed,0x3fd30268909bb47e,4 +np.float64,0x3fafce20643f9c41,0x3fafc8e41a240e35,4 +np.float64,0x3fdc6d416538da83,0x3fdb805262292863,4 +np.float64,0x3fe7d8362aefb06c,0x3fe5b2ce659db7fd,4 +np.float64,0x3fe290087de52011,0x3fe189f9a3eb123d,4 +np.float64,0x3fa62d2bf82c5a58,0x3fa62b65958ca2b8,4 +np.float64,0x3fafd134403fa269,0x3fafcbf670f8a6f3,4 +np.float64,0x3fa224e53c2449ca,0x3fa223ec5de1631b,4 +np.float64,0x3fb67e2c2c2cfc58,0x3fb676c445fb70a0,4 +np.float64,0x3fda358d01346b1a,0x3fd97b9441666eb2,4 +np.float64,0x3fdd30fc4bba61f9,0x3fdc308da423778d,4 +np.float64,0x3fc56e99c52add34,0x3fc5550004492621,4 +np.float64,0x3fe32d08de265a12,0x3fe20c761a73cec2,4 +np.float64,0x3fd46cf932a8d9f2,0x3fd414a7f3db03df,4 +np.float64,0x3fd94cfa2b3299f4,0x3fd8a5961b3e4bdd,4 +np.float64,0x3fed6ea3a6fadd47,0x3fe9745b2f6c9204,4 +np.float64,0x3fe4431d1768863a,0x3fe2ef61d0481de0,4 +np.float64,0x3fe1d8e00ea3b1c0,0x3fe0efab5050ee78,4 +np.float64,0x3fe56f37dcaade70,0x3fe3de00b0f392e0,4 +np.float64,0x3fde919a2dbd2334,0x3fdd6b6d2dcf2396,4 +np.float64,0x3fe251e3d4a4a3c8,0x3fe155de69605d60,4 +np.float64,0x3fe5e0ecc5abc1da,0x3fe436a5de5516cf,4 +np.float64,0x3fcd48780c3a90f0,0x3fcd073fa907ba9b,4 +np.float64,0x3fe4e8149229d029,0x3fe37360801d5b66,4 +np.float64,0x3fb9ef159633de2b,0x3fb9e3bc05a15d1d,4 +np.float64,0x3fc24a3f0424947e,0x3fc23a5432ca0e7c,4 +np.float64,0x3fe55ca196aab943,0x3fe3cf6b3143435a,4 +np.float64,0x3fe184544c2308a9,0x3fe0a7b49fa80aec,4 +np.float64,0x3fe2c76e83658edd,0x3fe1b8355c1ea771,4 +np.float64,0x3fea8d2c4ab51a59,0x3fe79ba85aabc099,4 +np.float64,0x3fd74f98abae9f31,0x3fd6cc85005d0593,4 +np.float64,0x3fec6de9a678dbd3,0x3fe8d59a1d23cdd1,4 +np.float64,0x3fec8a0e50f9141d,0x3fe8e7500f6f6a00,4 +np.float64,0x3fe9de6d08b3bcda,0x3fe7245319508767,4 +np.float64,0x3fe4461fd1688c40,0x3fe2f1cf0b93aba6,4 +np.float64,0x3fde342d9d3c685b,0x3fdd185609d5719d,4 +np.float64,0x3feb413fc8368280,0x3fe813c091d2519a,4 +np.float64,0x3fe64333156c8666,0x3fe48275b9a6a358,4 +np.float64,0x3fe03c65226078ca,0x3fdf18b26786be35,4 +np.float64,0x3fee11054dbc220b,0x3fe9d579a1cfa7ad,4 +np.float64,0x3fbaefccae35df99,0x3fbae314fef7c7ea,4 +np.float64,0x3feed4e3487da9c7,0x3fea4729241c8811,4 +np.float64,0x3fbb655df836cabc,0x3fbb57fcf9a097be,4 +np.float64,0x3fe68b0273ed1605,0x3fe4b96109afdf76,4 +np.float64,0x3fd216bfc3242d80,0x3fd1d957363f6a43,4 +np.float64,0x3fe01328d4a02652,0x3fded083bbf94aba,4 +np.float64,0x3fe3f9a61ae7f34c,0x3fe2b3f701b79028,4 +np.float64,0x3fed4e7cf8fa9cfa,0x3fe960d27084fb40,4 +np.float64,0x3faec08e343d811c,0x3faebbd2aa07ac1f,4 +np.float64,0x3fd2d1bbeea5a378,0x3fd28c9aefcf48ad,4 +np.float64,0x3fd92e941fb25d28,0x3fd889857f88410d,4 +np.float64,0x3fe43decb7e87bd9,0x3fe2eb32b4ee4667,4 +np.float64,0x3fef49cabcfe9395,0x3fea892f9a233f76,4 +np.float64,0x3fe3e96812e7d2d0,0x3fe2a6c6b45dd6ee,4 +np.float64,0x3fd24c0293a49805,0x3fd20c76d54473cb,4 +np.float64,0x3fb43d6b7e287ad7,0x3fb438060772795a,4 +np.float64,0x3fe87bf7d3f0f7f0,0x3fe62a0c47411c62,4 +np.float64,0x3fee82a2e07d0546,0x3fea17e27e752b7b,4 +np.float64,0x3fe40c01bbe81803,0x3fe2c2d9483f44d8,4 +np.float64,0x3fd686ccae2d0d99,0x3fd610763fb61097,4 +np.float64,0x3fe90fcf2af21f9e,0x3fe693c12df59ba9,4 +np.float64,0x3fefb3ce11ff679c,0x3feac3dd4787529d,4 +np.float64,0x3fcec53ff63d8a80,0x3fce79992af00c58,4 +np.float64,0x3fe599dd7bab33bb,0x3fe3ff5da7575d85,4 +np.float64,0x3fe9923b1a732476,0x3fe6ef71d13db456,4 +np.float64,0x3febf76fcef7eee0,0x3fe88a3952e11373,4 +np.float64,0x3fc2cfd128259fa2,0x3fc2be7fd47fd811,4 +np.float64,0x3fe4d37ae269a6f6,0x3fe36300d45e3745,4 +np.float64,0x3fe23aa2e4247546,0x3fe1424e172f756f,4 +np.float64,0x3fe4f0596ca9e0b3,0x3fe379f0c49de7ef,4 +np.float64,0x3fe2e4802fe5c900,0x3fe1d062a8812601,4 +np.float64,0x3fe5989c79eb3139,0x3fe3fe6308552dec,4 +np.float64,0x3fe3c53cb4e78a79,0x3fe28956e573aca4,4 +np.float64,0x3fe6512beeeca258,0x3fe48d2d5ece979f,4 +np.float64,0x3fd8473ddb308e7c,0x3fd7b33e38adc6ad,4 +np.float64,0x3fecd09c9679a139,0x3fe91361fa0c5bcb,4 +np.float64,0x3fc991530e3322a6,0x3fc965e2c514a9e9,4 +np.float64,0x3f6d4508403a8a11,0x3f6d45042b68acc5,4 +np.float64,0x3fea1f198f743e33,0x3fe750ce918d9330,4 +np.float64,0x3fd0a0bb4da14177,0x3fd07100f9c71e1c,4 +np.float64,0x3fd30c45ffa6188c,0x3fd2c499f9961f66,4 +np.float64,0x3fcad98e7c35b31d,0x3fcaa74293cbc52e,4 +np.float64,0x3fec8e4a5eb91c95,0x3fe8e9f898d118db,4 +np.float64,0x3fd19fdb79233fb7,0x3fd1670c00febd24,4 +np.float64,0x3fea9fcbb1f53f97,0x3fe7a836b29c4075,4 +np.float64,0x3fc6d12ea12da25d,0x3fc6b24bd2f89f59,4 +np.float64,0x3fd6af3658ad5e6d,0x3fd636613e08df3f,4 +np.float64,0x3fe31bc385a63787,0x3fe1fe3081621213,4 +np.float64,0x3fc0dbba2221b774,0x3fc0cf42c9313dba,4 +np.float64,0x3fef639ce87ec73a,0x3fea9795454f1036,4 +np.float64,0x3fee5f29dcbcbe54,0x3fea0349b288f355,4 +np.float64,0x3fed46bdb37a8d7b,0x3fe95c199f5aa569,4 +np.float64,0x3fef176afa3e2ed6,0x3fea6ce78b2aa3aa,4 +np.float64,0x3fc841e7683083cf,0x3fc81cccb84848cc,4 +np.float64,0xbfda3ec9a2347d94,0xbfd9840d180e9de3,4 +np.float64,0xbfcd5967ae3ab2d0,0xbfcd17be13142bb9,4 +np.float64,0xbfedf816573bf02d,0xbfe9c6bb06476c60,4 +np.float64,0xbfd0d6e10e21adc2,0xbfd0a54f99d2f3dc,4 +np.float64,0xbfe282df096505be,0xbfe17ef5e2e80760,4 +np.float64,0xbfd77ae6e62ef5ce,0xbfd6f4f6b603ad8a,4 +np.float64,0xbfe37b171aa6f62e,0xbfe24cb4b2d0ade4,4 +np.float64,0xbfef9e5ed9bf3cbe,0xbfeab817b41000bd,4 +np.float64,0xbfe624d6f96c49ae,0xbfe46b1e9c9aff86,4 +np.float64,0xbfefb5da65ff6bb5,0xbfeac4fc9c982772,4 +np.float64,0xbfd29a65d52534cc,0xbfd2579df8ff87b9,4 +np.float64,0xbfd40270172804e0,0xbfd3af6471104aef,4 +np.float64,0xbfb729ee7a2e53e0,0xbfb721d7dbd2705e,4 +np.float64,0xbfb746f1382e8de0,0xbfb73ebc1207f8e3,4 +np.float64,0xbfd3c7e606a78fcc,0xbfd377a8aa1b0dd9,4 +np.float64,0xbfd18c4880231892,0xbfd1543506584ad5,4 +np.float64,0xbfea988080753101,0xbfe7a34cba0d0fa1,4 +np.float64,0xbf877400e02ee800,0xbf8773df47fa7e35,4 +np.float64,0xbfb07e050820fc08,0xbfb07b198d4a52c9,4 +np.float64,0xbfee0a3621fc146c,0xbfe9d1745a05ba77,4 +np.float64,0xbfe78de246ef1bc4,0xbfe57bf2baab91c8,4 +np.float64,0xbfcdbfd3bd3b7fa8,0xbfcd7b728a955a06,4 +np.float64,0xbfe855ea79b0abd5,0xbfe60e8a4a17b921,4 +np.float64,0xbfd86c8e3530d91c,0xbfd7d5e36c918dc1,4 +np.float64,0xbfe4543169e8a863,0xbfe2fd23d42f552e,4 +np.float64,0xbfe41efbf1283df8,0xbfe2d235a2faed1a,4 +np.float64,0xbfd9a55464b34aa8,0xbfd8f7083f7281e5,4 +np.float64,0xbfe5f5078d6bea0f,0xbfe44637d910c270,4 +np.float64,0xbfe6d83e3dedb07c,0xbfe4f3fdadd10552,4 +np.float64,0xbfdb767e70b6ecfc,0xbfdaa0b6c17f3fb1,4 +np.float64,0xbfdfc91b663f9236,0xbfde7eb0dfbeaa26,4 +np.float64,0xbfbfbd18783f7a30,0xbfbfa84bf2fa1c8d,4 +np.float64,0xbfe51199242a2332,0xbfe39447dbe066ae,4 +np.float64,0xbfdbb94814b77290,0xbfdadd63bd796972,4 +np.float64,0xbfd8c6272cb18c4e,0xbfd828f2d9e8607e,4 +np.float64,0xbfce51e0b63ca3c0,0xbfce097ee908083a,4 +np.float64,0xbfe99a177d73342f,0xbfe6f4ec776a57ae,4 +np.float64,0xbfefde2ab0ffbc55,0xbfeadafdcbf54733,4 +np.float64,0xbfcccb5c1c3996b8,0xbfcc8d586a73d126,4 +np.float64,0xbfdf7ddcedbefbba,0xbfde3c749a906de7,4 +np.float64,0xbfef940516ff280a,0xbfeab26429e89f4b,4 +np.float64,0xbfe08009f1e10014,0xbfdf8eab352997eb,4 +np.float64,0xbfe9c02682b3804d,0xbfe70f5fd05f79ee,4 +np.float64,0xbfb3ca1732279430,0xbfb3c50bec5b453a,4 +np.float64,0xbfe368e81926d1d0,0xbfe23dc704d0887c,4 +np.float64,0xbfbd20cc2e3a4198,0xbfbd10b7e6d81c6c,4 +np.float64,0xbfd67ece4d2cfd9c,0xbfd608f527dcc5e7,4 +np.float64,0xbfdc02d1333805a2,0xbfdb20104454b79f,4 +np.float64,0xbfc007a626200f4c,0xbfbff9dc9dc70193,4 +np.float64,0xbfda9e4f8fb53ca0,0xbfd9db8af35dc630,4 +np.float64,0xbfd8173d77302e7a,0xbfd786a0cf3e2914,4 +np.float64,0xbfeb8fcbd0b71f98,0xbfe84734debc10fb,4 +np.float64,0xbfe4bf1cb7697e3a,0xbfe352c891113f29,4 +np.float64,0xbfc18624d5230c48,0xbfc178248e863b64,4 +np.float64,0xbfcf184bac3e3098,0xbfceca3b19be1ebe,4 +np.float64,0xbfd2269c42a44d38,0xbfd1e8920d72b694,4 +np.float64,0xbfe8808526b1010a,0xbfe62d5497292495,4 +np.float64,0xbfe498bd1da9317a,0xbfe334245eadea93,4 +np.float64,0xbfef0855aebe10ab,0xbfea6462f29aeaf9,4 +np.float64,0xbfdeb186c93d630e,0xbfdd87c37943c602,4 +np.float64,0xbfb29fe2ae253fc8,0xbfb29bae3c87efe4,4 +np.float64,0xbfddd9c6c3bbb38e,0xbfdcc7b400bf384b,4 +np.float64,0xbfe3506673e6a0cd,0xbfe2299f26295553,4 +np.float64,0xbfe765957a2ecb2b,0xbfe55e03cf22edab,4 +np.float64,0xbfecc9876c79930f,0xbfe90efaf15b6207,4 +np.float64,0xbfefb37a0a7f66f4,0xbfeac3af3898e7c2,4 +np.float64,0xbfeefa0da7bdf41b,0xbfea5c4cde53c1c3,4 +np.float64,0xbfe6639ee9ecc73e,0xbfe49b4e28a72482,4 +np.float64,0xbfef91a4bb7f2349,0xbfeab114ac9e25dd,4 +np.float64,0xbfc8b392bb316724,0xbfc88c657f4441a3,4 +np.float64,0xbfc88a358231146c,0xbfc863cb900970fe,4 +np.float64,0xbfef25a9d23e4b54,0xbfea74eda432aabe,4 +np.float64,0xbfe6aceea0ed59de,0xbfe4d32e54a3fd01,4 +np.float64,0xbfefe2b3e37fc568,0xbfeadd74f4605835,4 +np.float64,0xbfa9eecb8833dd90,0xbfa9ebf4f4cb2591,4 +np.float64,0xbfd42bad7428575a,0xbfd3d69de8e52d0a,4 +np.float64,0xbfbc366b4a386cd8,0xbfbc27ceee8f3019,4 +np.float64,0xbfd9bca7be337950,0xbfd90c80e6204e57,4 +np.float64,0xbfe8173f53f02e7f,0xbfe5e0f8d8ed329c,4 +np.float64,0xbfce22dbcb3c45b8,0xbfcddbc8159b63af,4 +np.float64,0xbfea2d7ba7345af7,0xbfe75aa62ad5b80a,4 +np.float64,0xbfc08b783e2116f0,0xbfc07faf8d501558,4 +np.float64,0xbfb8c4161c318830,0xbfb8ba33950748ec,4 +np.float64,0xbfddd930bcbbb262,0xbfdcc72dffdf51bb,4 +np.float64,0xbfd108ce8a22119e,0xbfd0d5801e7698bd,4 +np.float64,0xbfd5bd2b5dab7a56,0xbfd552c52c468c76,4 +np.float64,0xbfe7ffe67fefffcd,0xbfe5cfe96e35e6e5,4 +np.float64,0xbfa04ec6bc209d90,0xbfa04e120a2c25cc,4 +np.float64,0xbfef7752cc7eeea6,0xbfeaa28715addc4f,4 +np.float64,0xbfe7083c2eae1078,0xbfe5182bf8ddfc8e,4 +np.float64,0xbfe05dafd0a0bb60,0xbfdf52d397cfe5f6,4 +np.float64,0xbfacb4f2243969e0,0xbfacb118991ea235,4 +np.float64,0xbfc7d47e422fa8fc,0xbfc7b1504714a4fd,4 +np.float64,0xbfbd70b2243ae168,0xbfbd60182efb61de,4 +np.float64,0xbfe930e49cb261c9,0xbfe6ab272b3f9cfc,4 +np.float64,0xbfb5f537e62bea70,0xbfb5ee540dcdc635,4 +np.float64,0xbfbb0c8278361908,0xbfbaffa1f7642a87,4 +np.float64,0xbfe82af2447055e4,0xbfe5ef54ca8db9e8,4 +np.float64,0xbfe92245e6f2448c,0xbfe6a0d32168040b,4 +np.float64,0xbfb799a8522f3350,0xbfb7911a7ada3640,4 +np.float64,0x7faa8290c8350521,0x3fe5916f67209cd6,4 +np.float64,0x7f976597082ecb2d,0x3fcf94dce396bd37,4 +np.float64,0x7fede721237bce41,0x3fe3e7b1575b005f,4 +np.float64,0x7fd5f674d72bece9,0x3fe3210628eba199,4 +np.float64,0x7f9b0f1aa0361e34,0x3feffd34d15d1da7,4 +np.float64,0x7fec48346ab89068,0x3fe93dd84253d9a2,4 +np.float64,0x7f9cac76283958eb,0xbfec4cd999653868,4 +np.float64,0x7fed51ab6bbaa356,0x3fecc27fb5f37bca,4 +np.float64,0x7fded3c116bda781,0xbfda473efee47cf1,4 +np.float64,0x7fd19c48baa33890,0xbfe25700cbfc0326,4 +np.float64,0x7fe5c8f478ab91e8,0xbfee4ab6d84806be,4 +np.float64,0x7fe53c64e46a78c9,0x3fee19c3f227f4e1,4 +np.float64,0x7fc2ad1936255a31,0xbfe56db9b877f807,4 +np.float64,0x7fe2b071b52560e2,0xbfce3990a8d390a9,4 +np.float64,0x7fc93f3217327e63,0xbfd1f6d7ef838d2b,4 +np.float64,0x7fec26df08784dbd,0x3fd5397be41c93d9,4 +np.float64,0x7fcf4770183e8edf,0x3fe6354f5a785016,4 +np.float64,0x7fdc9fcc0bb93f97,0xbfeeeae952e8267d,4 +np.float64,0x7feb21f29c7643e4,0x3fec20122e33f1bf,4 +np.float64,0x7fd0b51273216a24,0x3fefb09f8daba00b,4 +np.float64,0x7fe747a9d76e8f53,0x3feb46a3232842a4,4 +np.float64,0x7fd58885972b110a,0xbfce5ea57c186221,4 +np.float64,0x7fca3ce85c3479d0,0x3fef93a24548e8ca,4 +np.float64,0x7fe1528a46a2a514,0xbfb54bb578d9da91,4 +np.float64,0x7fcc58b21b38b163,0x3feffb5b741ffc2d,4 +np.float64,0x7fdabcaaf5357955,0x3fecbf855db524d1,4 +np.float64,0x7fdd27c6933a4f8c,0xbfef2f41bb80144b,4 +np.float64,0x7fbda4e1be3b49c2,0x3fdb9b33f84f5381,4 +np.float64,0x7fe53363362a66c5,0x3fe4daff3a6a4ed0,4 +np.float64,0x7fe5719d62eae33a,0xbfef761d98f625d5,4 +np.float64,0x7f982ce5a83059ca,0x3fd0b27c3365f0a8,4 +np.float64,0x7fe6db8c42edb718,0x3fe786f4b1fe11a6,4 +np.float64,0x7fe62cca1b2c5993,0x3fd425b6c4c9714a,4 +np.float64,0x7feea88850bd5110,0xbfd7bbb432017175,4 +np.float64,0x7fad6c6ae43ad8d5,0x3fe82e49098bc6de,4 +np.float64,0x7fe70542f02e0a85,0x3fec3017960b4822,4 +np.float64,0x7feaf0bcbb35e178,0xbfc3aac74dd322d5,4 +np.float64,0x7fb5e152fe2bc2a5,0x3fd4b27a4720614c,4 +np.float64,0x7fe456ee5be8addc,0xbfe9e15ab5cff229,4 +np.float64,0x7fd4b53a8d296a74,0xbfefff450f503326,4 +np.float64,0x7fd7149d7a2e293a,0x3fef4ef0a9009096,4 +np.float64,0x7fd43fc5a8a87f8a,0x3fe0c929fee9dce7,4 +np.float64,0x7fef97022aff2e03,0x3fd4ea52a813da20,4 +np.float64,0x7fe035950ae06b29,0x3fef4e125394fb05,4 +np.float64,0x7fecd0548979a0a8,0x3fe89d226244037b,4 +np.float64,0x7fc79b3ac22f3675,0xbfee9c9cf78c8270,4 +np.float64,0x7fd8b8e8263171cf,0x3fe8e24437961db0,4 +np.float64,0x7fc288c23e251183,0xbfbaf8eca50986ca,4 +np.float64,0x7fe436b4b6686d68,0xbfecd661741931c4,4 +np.float64,0x7fcdf99abe3bf334,0x3feaa75c90830b92,4 +np.float64,0x7fd9f9739233f2e6,0xbfebbfcb301b0da5,4 +np.float64,0x7fd6fcbd1b2df979,0xbfccf2c77cb65f56,4 +np.float64,0x7fe242a97b248552,0xbfe5b0f13bcbabc8,4 +np.float64,0x7fe38bf3e06717e7,0x3fbc8fa9004d2668,4 +np.float64,0x7fecd0e8d479a1d1,0xbfe886a6b4f73a4a,4 +np.float64,0x7fe958d60232b1ab,0xbfeb7c4cf0cee2dd,4 +np.float64,0x7f9d492b583a9256,0xbfebe975d00221cb,4 +np.float64,0x7fd6c9983bad932f,0xbfefe817621a31f6,4 +np.float64,0x7fed0d7239fa1ae3,0x3feac7e1b6455b4b,4 +np.float64,0x7fe61dac90ec3b58,0x3fef845b9efe8421,4 +np.float64,0x7f9acd3010359a5f,0xbfe460d376200130,4 +np.float64,0x7fedced9673b9db2,0xbfeeaf23445e1944,4 +np.float64,0x7fd9f271a733e4e2,0xbfd41544535ecb78,4 +np.float64,0x7fe703339bee0666,0x3fef93334626b56c,4 +np.float64,0x7fec7761b7b8eec2,0xbfe6da9179e8e714,4 +np.float64,0x7fdd9fff043b3ffd,0xbfc0761dfb8d94f9,4 +np.float64,0x7fdc10ed17b821d9,0x3fe1481e2a26c77f,4 +np.float64,0x7fe7681e72aed03c,0x3fefff94a6d47c84,4 +np.float64,0x7fe18c29e1e31853,0x3fe86ebd2fd89456,4 +np.float64,0x7fb2fb273c25f64d,0xbfefc136f57e06de,4 +np.float64,0x7fac2bbb90385776,0x3fe25d8e3cdae7e3,4 +np.float64,0x7fed16789efa2cf0,0x3fe94555091fdfd9,4 +np.float64,0x7fd8fe8f7831fd1e,0xbfed58d520361902,4 +np.float64,0x7fa59bde3c2b37bb,0x3fef585391c077ff,4 +np.float64,0x7fda981b53353036,0x3fde02ca08737b5f,4 +np.float64,0x7fd29f388aa53e70,0xbfe04f5499246df2,4 +np.float64,0x7fcd0232513a0464,0xbfd9737f2f565829,4 +np.float64,0x7fe9a881bcf35102,0xbfe079cf285b35dd,4 +np.float64,0x7fdbe399a9b7c732,0x3fe965bc4220f340,4 +np.float64,0x7feb77414af6ee82,0xbfb7df2fcd491f55,4 +np.float64,0x7fa26e86c424dd0d,0xbfea474c3d65b9be,4 +np.float64,0x7feaee869e35dd0c,0xbfd7b333a888cd14,4 +np.float64,0x7fcbd67f6137acfe,0xbfe15a7a15dfcee6,4 +np.float64,0x7fe36991e766d323,0xbfeb288077c4ed9f,4 +np.float64,0x7fdcf4f4fcb9e9e9,0xbfea331ef7a75e7b,4 +np.float64,0x7fbe3445643c688a,0x3fedf21b94ae8e37,4 +np.float64,0x7fd984cfd2b3099f,0x3fc0d3ade71c395e,4 +np.float64,0x7fdec987b23d930e,0x3fe4af5e48f6c26e,4 +np.float64,0x7fde56a9953cad52,0x3fc8e7762cefb8b0,4 +np.float64,0x7fd39fb446273f68,0xbfe6c3443208f44d,4 +np.float64,0x7fc609c1a72c1382,0x3fe884e639571baa,4 +np.float64,0x7fe001be4b20037c,0xbfed0d90cbcb6010,4 +np.float64,0x7fce7ace283cf59b,0xbfd0303792e51f49,4 +np.float64,0x7fe27ba93da4f751,0x3fe548b5ce740d71,4 +np.float64,0x7fcc13c79b38278e,0xbfe2e14f5b64a1e9,4 +np.float64,0x7fc058550620b0a9,0x3fe44bb55ebd0590,4 +np.float64,0x7fa4ba8bf8297517,0x3fee59b39f9d08c4,4 +np.float64,0x7fe50d6872ea1ad0,0xbfea1eaa2d059e13,4 +np.float64,0x7feb7e33b476fc66,0xbfeff28a4424dd3e,4 +np.float64,0x7fe2d7d2a165afa4,0xbfdbaff0ba1ea460,4 +np.float64,0xffd126654b224cca,0xbfef0cd3031fb97c,4 +np.float64,0xffb5f884942bf108,0x3fe0de589bea2e4c,4 +np.float64,0xffe011b4bfe02369,0xbfe805a0edf1e1f2,4 +np.float64,0xffec13eae9b827d5,0x3fb5f30347d78447,4 +np.float64,0xffa6552ae82caa50,0x3fb1ecee60135f2f,4 +np.float64,0xffb62d38b02c5a70,0x3fbd35903148fd12,4 +np.float64,0xffe2c44ea425889d,0xbfd7616547f99a7d,4 +np.float64,0xffea24c61a74498c,0x3fef4a1b15ae9005,4 +np.float64,0xffd23a4ab2a47496,0x3fe933bfaa569ae9,4 +np.float64,0xffc34a073d269410,0xbfeec0f510bb7474,4 +np.float64,0xffeead84cfbd5b09,0x3feb2d635e5a78bd,4 +np.float64,0xffcfd8f3b43fb1e8,0xbfdd59625801771b,4 +np.float64,0xffd3c7f662a78fec,0x3f9cf3209edfbc4e,4 +np.float64,0xffe7b7e4f72f6fca,0xbfefdcff4925632c,4 +np.float64,0xffe48cab05e91956,0x3fe6b41217948423,4 +np.float64,0xffeb6980b336d301,0xbfca5de148f69324,4 +np.float64,0xffe3f15c4aa7e2b8,0xbfeb18efae892081,4 +np.float64,0xffcf290c713e5218,0x3fefe6f1a513ed26,4 +np.float64,0xffd80979b43012f4,0xbfde6c8df91af976,4 +np.float64,0xffc3181e0026303c,0x3fe7448f681def38,4 +np.float64,0xffedfa68f97bf4d1,0xbfeca6efb802d109,4 +np.float64,0xffca0931c0341264,0x3fe31b9f073b08cd,4 +np.float64,0xffe4c44934e98892,0x3feda393a2e8a0f7,4 +np.float64,0xffe65bb56f2cb76a,0xbfeffaf638a4b73e,4 +np.float64,0xffe406a332a80d46,0x3fe8151dadb853c1,4 +np.float64,0xffdb7eae9c36fd5e,0xbfeff89abf5ab16e,4 +np.float64,0xffe245a02da48b40,0x3fef1fb43e85f4b8,4 +np.float64,0xffe2bafa732575f4,0x3fcbab115c6fd86e,4 +np.float64,0xffe8b1eedb7163dd,0x3feff263df6f6b12,4 +np.float64,0xffe6c76c796d8ed8,0xbfe61a8668511293,4 +np.float64,0xffefe327d1ffc64f,0xbfd9b92887a84827,4 +np.float64,0xffa452180c28a430,0xbfa9b9e578a4e52f,4 +np.float64,0xffe9867d0bf30cf9,0xbfca577867588408,4 +np.float64,0xffdfe9b923bfd372,0x3fdab5c15f085c2d,4 +np.float64,0xffed590c6abab218,0xbfd7e7b6c5a120e6,4 +np.float64,0xffeaebcfbab5d79f,0x3fed58be8a9e2c3b,4 +np.float64,0xffe2ba83a8257507,0x3fe6c42a4ac1d4d9,4 +np.float64,0xffe01d5b0ee03ab6,0xbfe5dad6c9247db7,4 +np.float64,0xffe51095d52a212b,0x3fef822cebc32d8e,4 +np.float64,0xffebd7a901b7af51,0xbfe5e63f3e3b1185,4 +np.float64,0xffe4efdcde29dfb9,0xbfe811294dfa758f,4 +np.float64,0xffe3be1aa4a77c35,0x3fdd8dcfcd409bb1,4 +np.float64,0xffbe6f2f763cde60,0x3fd13766e43bd622,4 +np.float64,0xffeed3d80fbda7af,0x3fec10a23c1b7a4a,4 +np.float64,0xffd6ebff37add7fe,0xbfe6177411607c86,4 +np.float64,0xffe85a90f4b0b521,0x3fc09fdd66c8fde9,4 +np.float64,0xffea3d58c2b47ab1,0x3feb5bd4a04b3562,4 +np.float64,0xffef675be6beceb7,0x3fecd840683d1044,4 +np.float64,0xff726a088024d400,0x3feff2b4f47b5214,4 +np.float64,0xffc90856733210ac,0xbfe3c6ffbf6840a5,4 +np.float64,0xffc0b58d9a216b1c,0xbfe10314267d0611,4 +np.float64,0xffee1f3d0abc3e79,0xbfd12ea7efea9067,4 +np.float64,0xffd988c41a331188,0x3febe83802d8a32e,4 +np.float64,0xffe8f1ac9bb1e358,0xbfdbf5fa7e84f2f2,4 +np.float64,0xffe47af279e8f5e4,0x3fef11e339e5fa78,4 +np.float64,0xff9960a7f832c140,0xbfa150363f8ec5b2,4 +np.float64,0xffcac40fa7358820,0xbfec3d5847a3df1d,4 +np.float64,0xffcb024a9d360494,0xbfd060fa31fd6b6a,4 +np.float64,0xffe385ffb3270bff,0xbfee6859e8dcd9e8,4 +np.float64,0xffef62f2c53ec5e5,0x3fe0a71ffddfc718,4 +np.float64,0xffed87ff20fb0ffd,0xbfe661db7c4098e3,4 +np.float64,0xffe369278526d24e,0x3fd64d89a41822fc,4 +np.float64,0xff950288c02a0520,0x3fe1df91d1ad7d5c,4 +np.float64,0xffe70e7c2cee1cf8,0x3fc9fece08df2fd8,4 +np.float64,0xffbaf020b635e040,0xbfc68c43ff9911a7,4 +np.float64,0xffee0120b0fc0240,0x3f9f792e17b490b0,4 +np.float64,0xffe1fa4be7a3f498,0xbfef4b18ab4b319e,4 +np.float64,0xffe61887bf2c310f,0x3fe846714826cb32,4 +np.float64,0xffdc3cf77f3879ee,0x3fe033b948a36125,4 +np.float64,0xffcc2b86f238570c,0xbfefdcceac3f220f,4 +np.float64,0xffe1f030c0a3e061,0x3fef502a808c359a,4 +np.float64,0xffb872c4ee30e588,0x3fef66ed8d3e6175,4 +np.float64,0xffeac8fc617591f8,0xbfe5d8448602aac9,4 +np.float64,0xffe5be16afab7c2d,0x3fee75ccde3cd14d,4 +np.float64,0xffae230ad83c4610,0xbfe49bbe6074d459,4 +np.float64,0xffc8fbeff531f7e0,0x3f77201e0c927f97,4 +np.float64,0xffdc314f48b8629e,0x3fef810dfc5db118,4 +np.float64,0xffec1f8970783f12,0x3fe15567102e042a,4 +np.float64,0xffc6995f902d32c0,0xbfecd5d2eedf342c,4 +np.float64,0xffdc7af76b38f5ee,0xbfd6e754476ab320,4 +np.float64,0xffb30cf8682619f0,0x3fd5ac3dfc4048d0,4 +np.float64,0xffd3a77695a74eee,0xbfefb5d6889e36e9,4 +np.float64,0xffd8b971803172e4,0xbfeb7f62f0b6c70b,4 +np.float64,0xffde4c0234bc9804,0xbfed50ba9e16d5e0,4 +np.float64,0xffb62b3f342c5680,0xbfeabc0de4069b84,4 +np.float64,0xff9af5674035eac0,0xbfed6c198b6b1bd8,4 +np.float64,0xffdfe20cb43fc41a,0x3fb11f8238f66306,4 +np.float64,0xffd2ecd7a0a5d9b0,0xbfec17ef1a62b1e3,4 +np.float64,0xffce60f7863cc1f0,0x3fe6dbcad3e3a006,4 +np.float64,0xffbbb8306a377060,0xbfbfd0fbef485c4c,4 +np.float64,0xffd1b2bd2b23657a,0xbfda3e046d987b99,4 +np.float64,0xffc480f4092901e8,0xbfeeff0427f6897b,4 +np.float64,0xffe6e02d926dc05a,0xbfcd59552778890b,4 +np.float64,0xffd302e5b7a605cc,0xbfee7c08641366b0,4 +np.float64,0xffec2eb92f785d72,0xbfef5c9c7f771050,4 +np.float64,0xffea3e31a9747c62,0xbfc49cd54755faf0,4 +np.float64,0xffce0a4e333c149c,0x3feeb9a6d0db4aee,4 +np.float64,0xffdc520a2db8a414,0x3fefc7b72613dcd0,4 +np.float64,0xffe056b968a0ad72,0xbfe47a9fe1f827fb,4 +np.float64,0xffe5a10f4cab421e,0x3fec2b1f74b73dec,4 diff --git a/numpy/core/tests/data/umath-validation-set-sinh.csv b/numpy/core/tests/data/umath-validation-set-sinh.csv new file mode 100644 index 000000000000..5888c91c20db --- /dev/null +++ b/numpy/core/tests/data/umath-validation-set-sinh.csv @@ -0,0 +1,1429 @@ +dtype,input,output,ulperrortol +np.float32,0xfee27582,0xff800000,2 +np.float32,0xff19f092,0xff800000,2 +np.float32,0xbf393576,0xbf49cb31,2 +np.float32,0x8020fdea,0x8020fdea,2 +np.float32,0x455f4e,0x455f4e,2 +np.float32,0xff718c35,0xff800000,2 +np.float32,0x3f3215e3,0x3f40cce5,2 +np.float32,0x19e833,0x19e833,2 +np.float32,0xff2dcd49,0xff800000,2 +np.float32,0x7e8f6c95,0x7f800000,2 +np.float32,0xbf159dac,0xbf1e47a5,2 +np.float32,0x100d3d,0x100d3d,2 +np.float32,0xff673441,0xff800000,2 +np.float32,0x80275355,0x80275355,2 +np.float32,0x4812d0,0x4812d0,2 +np.float32,0x8072b956,0x8072b956,2 +np.float32,0xff3bb918,0xff800000,2 +np.float32,0x0,0x0,2 +np.float32,0xfe327798,0xff800000,2 +np.float32,0x41d4e2,0x41d4e2,2 +np.float32,0xfe34b1b8,0xff800000,2 +np.float32,0x80199f72,0x80199f72,2 +np.float32,0x807242ce,0x807242ce,2 +np.float32,0x3ef4202d,0x3efd7b48,2 +np.float32,0x763529,0x763529,2 +np.float32,0x4f6662,0x4f6662,2 +np.float32,0x3f18efe9,0x3f2232b5,2 +np.float32,0x80701846,0x80701846,2 +np.float32,0x3f599948,0x3f74c393,2 +np.float32,0x5a3d69,0x5a3d69,2 +np.float32,0xbf4a7e65,0xbf6047a3,2 +np.float32,0xff0d4c82,0xff800000,2 +np.float32,0x7a74db,0x7a74db,2 +np.float32,0x803388e6,0x803388e6,2 +np.float32,0x7f4430bb,0x7f800000,2 +np.float32,0x14c5b1,0x14c5b1,2 +np.float32,0xfa113400,0xff800000,2 +np.float32,0x7f4b3209,0x7f800000,2 +np.float32,0x8038d88c,0x8038d88c,2 +np.float32,0xbef2f9de,0xbefc330b,2 +np.float32,0xbe147b38,0xbe15008f,2 +np.float32,0x2b61e6,0x2b61e6,2 +np.float32,0x80000001,0x80000001,2 +np.float32,0x8060456c,0x8060456c,2 +np.float32,0x3f30fa82,0x3f3f6a99,2 +np.float32,0xfd1f0220,0xff800000,2 +np.float32,0xbf2b7555,0xbf389151,2 +np.float32,0xff100b7a,0xff800000,2 +np.float32,0x70d3cd,0x70d3cd,2 +np.float32,0x2a8d4a,0x2a8d4a,2 +np.float32,0xbf7b733f,0xbf92f05f,2 +np.float32,0x3f7106dc,0x3f8b1fc6,2 +np.float32,0x3f39da7a,0x3f4a9d79,2 +np.float32,0x3f5dd73f,0x3f7aaab5,2 +np.float32,0xbe8c8754,0xbe8e4cba,2 +np.float32,0xbf6c74c9,0xbf87c556,2 +np.float32,0x800efbbb,0x800efbbb,2 +np.float32,0xff054ab5,0xff800000,2 +np.float32,0x800b4b46,0x800b4b46,2 +np.float32,0xff77fd74,0xff800000,2 +np.float32,0x257d0,0x257d0,2 +np.float32,0x7caa0c,0x7caa0c,2 +np.float32,0x8025d24d,0x8025d24d,2 +np.float32,0x3d9f1b60,0x3d9f445c,2 +np.float32,0xbe3bf6e8,0xbe3d0595,2 +np.float32,0x54bb93,0x54bb93,2 +np.float32,0xbf3e6a45,0xbf507716,2 +np.float32,0x3f4bb26e,0x3f61e1cd,2 +np.float32,0x3f698edc,0x3f85aac5,2 +np.float32,0xff7bd0ef,0xff800000,2 +np.float32,0xbed07b68,0xbed64a8e,2 +np.float32,0xbf237c72,0xbf2ed3d2,2 +np.float32,0x27b0fa,0x27b0fa,2 +np.float32,0x3f7606d1,0x3f8ed7d6,2 +np.float32,0x790dc0,0x790dc0,2 +np.float32,0x7f68f3ac,0x7f800000,2 +np.float32,0xbed39288,0xbed9a52f,2 +np.float32,0x3f6f8266,0x3f8a0187,2 +np.float32,0x3fbdca,0x3fbdca,2 +np.float32,0xbf7c3e5d,0xbf938b2c,2 +np.float32,0x802321a8,0x802321a8,2 +np.float32,0x3eecab66,0x3ef53031,2 +np.float32,0x62b324,0x62b324,2 +np.float32,0x3f13afac,0x3f1c03fe,2 +np.float32,0xff315ad7,0xff800000,2 +np.float32,0xbf1fac0d,0xbf2a3a63,2 +np.float32,0xbf543984,0xbf6d61d6,2 +np.float32,0x71a212,0x71a212,2 +np.float32,0x114fbe,0x114fbe,2 +np.float32,0x3f5b6ff2,0x3f77505f,2 +np.float32,0xff6ff89e,0xff800000,2 +np.float32,0xff4527a1,0xff800000,2 +np.float32,0x22cb3,0x22cb3,2 +np.float32,0x7f53bb6b,0x7f800000,2 +np.float32,0xff3d2dea,0xff800000,2 +np.float32,0xfd21dac0,0xff800000,2 +np.float32,0xfc486140,0xff800000,2 +np.float32,0x7e2b693a,0x7f800000,2 +np.float32,0x8022a9fb,0x8022a9fb,2 +np.float32,0x80765de0,0x80765de0,2 +np.float32,0x13d299,0x13d299,2 +np.float32,0x7ee53713,0x7f800000,2 +np.float32,0xbde1c770,0xbde23c96,2 +np.float32,0xbd473fc0,0xbd4753de,2 +np.float32,0x3f1cb455,0x3f26acf3,2 +np.float32,0x683e49,0x683e49,2 +np.float32,0x3ed5a9fc,0x3edbeb79,2 +np.float32,0x3f4fe3f6,0x3f67814f,2 +np.float32,0x802a2bce,0x802a2bce,2 +np.float32,0x7e951b4c,0x7f800000,2 +np.float32,0xbe6eb260,0xbe70dd44,2 +np.float32,0xbe3daca8,0xbe3ec2cb,2 +np.float32,0xbe9c38b2,0xbe9ea822,2 +np.float32,0xff2e29dc,0xff800000,2 +np.float32,0x7f62c7cc,0x7f800000,2 +np.float32,0xbf6799a4,0xbf84416c,2 +np.float32,0xbe30a7f0,0xbe318898,2 +np.float32,0xc83d9,0xc83d9,2 +np.float32,0x3f05abf4,0x3f0bd447,2 +np.float32,0x7e9b018a,0x7f800000,2 +np.float32,0xbf0ed72e,0xbf165e5b,2 +np.float32,0x8011ac8c,0x8011ac8c,2 +np.float32,0xbeb7c706,0xbebbbfcb,2 +np.float32,0x803637f9,0x803637f9,2 +np.float32,0xfe787cc8,0xff800000,2 +np.float32,0x3f533d4b,0x3f6c0a50,2 +np.float32,0x3f5c0f1c,0x3f782dde,2 +np.float32,0x3f301f36,0x3f3e590d,2 +np.float32,0x2dc929,0x2dc929,2 +np.float32,0xff15018a,0xff800000,2 +np.float32,0x3f4d0c56,0x3f63afeb,2 +np.float32,0xbf7a2ae3,0xbf91f6e4,2 +np.float32,0xbe771b84,0xbe798346,2 +np.float32,0x80800000,0x80800000,2 +np.float32,0x7f5689ba,0x7f800000,2 +np.float32,0x3f1c3177,0x3f2610df,2 +np.float32,0x3f1b9664,0x3f255825,2 +np.float32,0x3f7e5066,0x3f9520d4,2 +np.float32,0xbf1935f8,0xbf2285ab,2 +np.float32,0x3f096cc7,0x3f101ef9,2 +np.float32,0x8030c180,0x8030c180,2 +np.float32,0x6627ed,0x6627ed,2 +np.float32,0x454595,0x454595,2 +np.float32,0x7de66a33,0x7f800000,2 +np.float32,0xbf800000,0xbf966cfe,2 +np.float32,0xbf35c0a8,0xbf456939,2 +np.float32,0x3f6a6266,0x3f8643e0,2 +np.float32,0x3f0cbcee,0x3f13ef6a,2 +np.float32,0x7efd1e58,0x7f800000,2 +np.float32,0xfe9a74c6,0xff800000,2 +np.float32,0x807ebe6c,0x807ebe6c,2 +np.float32,0x80656736,0x80656736,2 +np.float32,0x800e0608,0x800e0608,2 +np.float32,0xbf30e39a,0xbf3f4e00,2 +np.float32,0x802015fd,0x802015fd,2 +np.float32,0x3e3ce26d,0x3e3df519,2 +np.float32,0x7ec142ac,0x7f800000,2 +np.float32,0xbf68c9ce,0xbf851c78,2 +np.float32,0xfede8356,0xff800000,2 +np.float32,0xbf1507ce,0xbf1d978d,2 +np.float32,0x3e53914c,0x3e551374,2 +np.float32,0x7f3e1c14,0x7f800000,2 +np.float32,0x8070d2ba,0x8070d2ba,2 +np.float32,0xbf4eb793,0xbf65ecee,2 +np.float32,0x7365a6,0x7365a6,2 +np.float32,0x8045cba2,0x8045cba2,2 +np.float32,0x7e4af521,0x7f800000,2 +np.float32,0xbf228625,0xbf2da9e1,2 +np.float32,0x7ee0536c,0x7f800000,2 +np.float32,0x3e126607,0x3e12e5d5,2 +np.float32,0x80311d92,0x80311d92,2 +np.float32,0xbf386b8b,0xbf48ca54,2 +np.float32,0x7f800000,0x7f800000,2 +np.float32,0x8049ec7a,0x8049ec7a,2 +np.float32,0xbf1dfde4,0xbf2836be,2 +np.float32,0x7e719a8c,0x7f800000,2 +np.float32,0x3eb9c856,0x3ebde2e6,2 +np.float32,0xfe3efda8,0xff800000,2 +np.float32,0xbe89d60c,0xbe8b81d1,2 +np.float32,0x3eaad338,0x3eae0317,2 +np.float32,0x7f4e5217,0x7f800000,2 +np.float32,0x3e9d0f40,0x3e9f88ce,2 +np.float32,0xbe026708,0xbe02c155,2 +np.float32,0x5fc22f,0x5fc22f,2 +np.float32,0x1c4572,0x1c4572,2 +np.float32,0xbed89d96,0xbedf22c5,2 +np.float32,0xbf3debee,0xbf4fd441,2 +np.float32,0xbf465520,0xbf5ac6e5,2 +np.float32,0x3f797081,0x3f9169b3,2 +np.float32,0xbf250734,0xbf30b2aa,2 +np.float32,0x7f5068e9,0x7f800000,2 +np.float32,0x3f1b814e,0x3f253f0c,2 +np.float32,0xbf27c5d3,0xbf340b05,2 +np.float32,0x3f1b78ae,0x3f2534c8,2 +np.float32,0x8059b51a,0x8059b51a,2 +np.float32,0x8059f182,0x8059f182,2 +np.float32,0xbf1bb36e,0xbf257ab8,2 +np.float32,0x41ac35,0x41ac35,2 +np.float32,0x68f41f,0x68f41f,2 +np.float32,0xbea504dc,0xbea7e40f,2 +np.float32,0x1,0x1,2 +np.float32,0x3e96b5b0,0x3e98e542,2 +np.float32,0x7f7fffff,0x7f800000,2 +np.float32,0x3c557a80,0x3c557c0c,2 +np.float32,0x800ca3ec,0x800ca3ec,2 +np.float32,0x8077d4aa,0x8077d4aa,2 +np.float32,0x3f000af0,0x3f0572d6,2 +np.float32,0x3e0434dd,0x3e0492f8,2 +np.float32,0x7d1a710a,0x7f800000,2 +np.float32,0x3f70f996,0x3f8b15f8,2 +np.float32,0x8033391d,0x8033391d,2 +np.float32,0x11927c,0x11927c,2 +np.float32,0x7f7784be,0x7f800000,2 +np.float32,0x7acb22af,0x7f800000,2 +np.float32,0x7e8b153c,0x7f800000,2 +np.float32,0x66d402,0x66d402,2 +np.float32,0xfed6e7b0,0xff800000,2 +np.float32,0x7f6872d3,0x7f800000,2 +np.float32,0x1bd49c,0x1bd49c,2 +np.float32,0xfdc4f1b8,0xff800000,2 +np.float32,0xbed8a466,0xbedf2a33,2 +np.float32,0x7ee789,0x7ee789,2 +np.float32,0xbece94b4,0xbed43b52,2 +np.float32,0x3cf3f734,0x3cf4006f,2 +np.float32,0x7e44aa00,0x7f800000,2 +np.float32,0x7f19e99c,0x7f800000,2 +np.float32,0x806ff1bc,0x806ff1bc,2 +np.float32,0x80296934,0x80296934,2 +np.float32,0x7f463363,0x7f800000,2 +np.float32,0xbf212ac3,0xbf2c06bb,2 +np.float32,0x3dc63778,0x3dc686ba,2 +np.float32,0x7f1b4328,0x7f800000,2 +np.float32,0x6311f6,0x6311f6,2 +np.float32,0xbf6b6fb6,0xbf870751,2 +np.float32,0xbf2c44cf,0xbf399155,2 +np.float32,0x3e7a67bc,0x3e7ce887,2 +np.float32,0x7f57c5f7,0x7f800000,2 +np.float32,0x7f2bb4ff,0x7f800000,2 +np.float32,0xbe9d448e,0xbe9fc0a4,2 +np.float32,0xbf4840f0,0xbf5d4f6b,2 +np.float32,0x7f1e1176,0x7f800000,2 +np.float32,0xff76638e,0xff800000,2 +np.float32,0xff055555,0xff800000,2 +np.float32,0x3f32b82b,0x3f419834,2 +np.float32,0xff363aa8,0xff800000,2 +np.float32,0x7f737fd0,0x7f800000,2 +np.float32,0x3da5d798,0x3da60602,2 +np.float32,0x3f1cc126,0x3f26bc3e,2 +np.float32,0x7eb07541,0x7f800000,2 +np.float32,0x3f7b2ff2,0x3f92bd2a,2 +np.float32,0x474f7,0x474f7,2 +np.float32,0x7fc00000,0x7fc00000,2 +np.float32,0xff2b0a4e,0xff800000,2 +np.float32,0xfeb24f16,0xff800000,2 +np.float32,0x2cb9fc,0x2cb9fc,2 +np.float32,0x67189d,0x67189d,2 +np.float32,0x8033d854,0x8033d854,2 +np.float32,0xbe85e94c,0xbe87717a,2 +np.float32,0x80767c6c,0x80767c6c,2 +np.float32,0x7ea84d65,0x7f800000,2 +np.float32,0x3f024bc7,0x3f07fead,2 +np.float32,0xbdcb0100,0xbdcb5625,2 +np.float32,0x3f160a9e,0x3f1ec7c9,2 +np.float32,0xff1734c8,0xff800000,2 +np.float32,0x7f424d5e,0x7f800000,2 +np.float32,0xbf75b215,0xbf8e9862,2 +np.float32,0x3f262a42,0x3f3214c4,2 +np.float32,0xbf4cfb53,0xbf639927,2 +np.float32,0x3f4ac8b8,0x3f60aa7c,2 +np.float32,0x3e90e593,0x3e92d6b3,2 +np.float32,0xbf66bccf,0xbf83a2d8,2 +np.float32,0x7d3d851a,0x7f800000,2 +np.float32,0x7bac783c,0x7f800000,2 +np.float32,0x8001c626,0x8001c626,2 +np.float32,0xbdffd480,0xbe003f7b,2 +np.float32,0x7f6680bf,0x7f800000,2 +np.float32,0xbecf448e,0xbed4f9bb,2 +np.float32,0x584c7,0x584c7,2 +np.float32,0x3f3e8ea0,0x3f50a5fb,2 +np.float32,0xbf5a5f04,0xbf75d56e,2 +np.float32,0x8065ae47,0x8065ae47,2 +np.float32,0xbf48dce3,0xbf5e1dba,2 +np.float32,0xbe8dae2e,0xbe8f7ed8,2 +np.float32,0x3f7ca6ab,0x3f93dace,2 +np.float32,0x4c3e81,0x4c3e81,2 +np.float32,0x80000000,0x80000000,2 +np.float32,0x3ee1f7d9,0x3ee96033,2 +np.float32,0x80588c6f,0x80588c6f,2 +np.float32,0x5ba34e,0x5ba34e,2 +np.float32,0x80095d28,0x80095d28,2 +np.float32,0xbe7ba198,0xbe7e2bdd,2 +np.float32,0xbe0bdcb4,0xbe0c4c22,2 +np.float32,0x1776f7,0x1776f7,2 +np.float32,0x80328b2a,0x80328b2a,2 +np.float32,0x3e978d37,0x3e99c63e,2 +np.float32,0x7ed50906,0x7f800000,2 +np.float32,0x3f776a54,0x3f8fe2bd,2 +np.float32,0xbed624c4,0xbedc7120,2 +np.float32,0x7f0b6a31,0x7f800000,2 +np.float32,0x7eb13913,0x7f800000,2 +np.float32,0xbe733684,0xbe758190,2 +np.float32,0x80016474,0x80016474,2 +np.float32,0x7a51ee,0x7a51ee,2 +np.float32,0x3f6cb91e,0x3f87f729,2 +np.float32,0xbd99b050,0xbd99d540,2 +np.float32,0x7c6e3cba,0x7f800000,2 +np.float32,0xbf00179a,0xbf05811e,2 +np.float32,0x3e609b29,0x3e626954,2 +np.float32,0xff3fd71a,0xff800000,2 +np.float32,0x5d8c2,0x5d8c2,2 +np.float32,0x7ee93662,0x7f800000,2 +np.float32,0x4b0b31,0x4b0b31,2 +np.float32,0x3ec243b7,0x3ec6f594,2 +np.float32,0x804d60f1,0x804d60f1,2 +np.float32,0xbf0cb784,0xbf13e929,2 +np.float32,0x3f13b74d,0x3f1c0cee,2 +np.float32,0xfe37cb64,0xff800000,2 +np.float32,0x1a88,0x1a88,2 +np.float32,0x3e22a472,0x3e2353ba,2 +np.float32,0x7f07d6a0,0x7f800000,2 +np.float32,0x3f78f435,0x3f910bb5,2 +np.float32,0x555a4a,0x555a4a,2 +np.float32,0x3e306c1f,0x3e314be3,2 +np.float32,0x8005877c,0x8005877c,2 +np.float32,0x4df389,0x4df389,2 +np.float32,0x8069ffc7,0x8069ffc7,2 +np.float32,0x3f328f24,0x3f4164c6,2 +np.float32,0x53a31b,0x53a31b,2 +np.float32,0xbe4d6768,0xbe4ec8be,2 +np.float32,0x7fa00000,0x7fe00000,2 +np.float32,0x3f484c1b,0x3f5d5e2f,2 +np.float32,0x8038be05,0x8038be05,2 +np.float32,0x58ac0f,0x58ac0f,2 +np.float32,0x7ed7fb72,0x7f800000,2 +np.float32,0x5a22e1,0x5a22e1,2 +np.float32,0xbebb7394,0xbebfaad6,2 +np.float32,0xbda98160,0xbda9b2ef,2 +np.float32,0x7f3e5c42,0x7f800000,2 +np.float32,0xfed204ae,0xff800000,2 +np.float32,0xbf5ef782,0xbf7c3ec5,2 +np.float32,0xbef7a0a8,0xbf00b292,2 +np.float32,0xfee6e176,0xff800000,2 +np.float32,0xfe121140,0xff800000,2 +np.float32,0xfe9e13be,0xff800000,2 +np.float32,0xbf3c98b1,0xbf4e2003,2 +np.float32,0x77520d,0x77520d,2 +np.float32,0xf17b2,0xf17b2,2 +np.float32,0x724d2f,0x724d2f,2 +np.float32,0x7eb326f5,0x7f800000,2 +np.float32,0x3edd6bf2,0x3ee4636e,2 +np.float32,0x350f57,0x350f57,2 +np.float32,0xff7d4435,0xff800000,2 +np.float32,0x802b2b9d,0x802b2b9d,2 +np.float32,0xbf7fbeee,0xbf963acf,2 +np.float32,0x804f3100,0x804f3100,2 +np.float32,0x7c594a71,0x7f800000,2 +np.float32,0x3ef49340,0x3efdfbb6,2 +np.float32,0x2e0659,0x2e0659,2 +np.float32,0x8006d5fe,0x8006d5fe,2 +np.float32,0xfd2a00b0,0xff800000,2 +np.float32,0xbee1c016,0xbee922ed,2 +np.float32,0x3e3b7de8,0x3e3c8a8b,2 +np.float32,0x805e6bba,0x805e6bba,2 +np.float32,0x1a7da2,0x1a7da2,2 +np.float32,0x6caba4,0x6caba4,2 +np.float32,0x802f7eab,0x802f7eab,2 +np.float32,0xff68b16b,0xff800000,2 +np.float32,0x8064f5e5,0x8064f5e5,2 +np.float32,0x2e39b4,0x2e39b4,2 +np.float32,0x800000,0x800000,2 +np.float32,0xfd0334c0,0xff800000,2 +np.float32,0x3e952fc4,0x3e974e7e,2 +np.float32,0x80057d33,0x80057d33,2 +np.float32,0x3ed3ddc4,0x3ed9f6f1,2 +np.float32,0x3f74ce18,0x3f8dedf4,2 +np.float32,0xff6bb7c0,0xff800000,2 +np.float32,0xff43bc21,0xff800000,2 +np.float32,0x80207570,0x80207570,2 +np.float32,0x7e1dda75,0x7f800000,2 +np.float32,0x3efe335c,0x3f0462ff,2 +np.float32,0xbf252c0c,0xbf30df70,2 +np.float32,0x3ef4b8e3,0x3efe25ba,2 +np.float32,0x7c33938d,0x7f800000,2 +np.float32,0x3eb1593c,0x3eb4ea95,2 +np.float32,0xfe1d0068,0xff800000,2 +np.float32,0xbf10da9b,0xbf18b551,2 +np.float32,0xfeb65748,0xff800000,2 +np.float32,0xfe8c6014,0xff800000,2 +np.float32,0x3f0503e2,0x3f0b14e3,2 +np.float32,0xfe5e5248,0xff800000,2 +np.float32,0xbd10afa0,0xbd10b754,2 +np.float32,0xff64b609,0xff800000,2 +np.float32,0xbf674a96,0xbf84089c,2 +np.float32,0x7f5d200d,0x7f800000,2 +np.float32,0x3cf44900,0x3cf45245,2 +np.float32,0x8044445a,0x8044445a,2 +np.float32,0xff35b676,0xff800000,2 +np.float32,0x806452cd,0x806452cd,2 +np.float32,0xbf2930fb,0xbf35c7b4,2 +np.float32,0x7e500617,0x7f800000,2 +np.float32,0x543719,0x543719,2 +np.float32,0x3ed11068,0x3ed6ec1d,2 +np.float32,0xbd8db068,0xbd8dcd59,2 +np.float32,0x3ede62c8,0x3ee571d0,2 +np.float32,0xbf00a410,0xbf061f9c,2 +np.float32,0xbf44fa39,0xbf58ff5b,2 +np.float32,0x3f1c3114,0x3f261069,2 +np.float32,0xbdea6210,0xbdeae521,2 +np.float32,0x80059f6d,0x80059f6d,2 +np.float32,0xbdba15f8,0xbdba578c,2 +np.float32,0x6d8a61,0x6d8a61,2 +np.float32,0x6f5428,0x6f5428,2 +np.float32,0x18d0e,0x18d0e,2 +np.float32,0x50e131,0x50e131,2 +np.float32,0x3f2f52be,0x3f3d5a7e,2 +np.float32,0x7399d8,0x7399d8,2 +np.float32,0x106524,0x106524,2 +np.float32,0x7ebf1c53,0x7f800000,2 +np.float32,0x80276458,0x80276458,2 +np.float32,0x3ebbde67,0x3ec01ceb,2 +np.float32,0x80144d9d,0x80144d9d,2 +np.float32,0x8017ea6b,0x8017ea6b,2 +np.float32,0xff38f201,0xff800000,2 +np.float32,0x7f2daa82,0x7f800000,2 +np.float32,0x3f3cb7c7,0x3f4e47ed,2 +np.float32,0x7f08c779,0x7f800000,2 +np.float32,0xbecc907a,0xbed20cec,2 +np.float32,0x7d440002,0x7f800000,2 +np.float32,0xbd410d80,0xbd411fcd,2 +np.float32,0x3d63ae07,0x3d63cc0c,2 +np.float32,0x805a9c13,0x805a9c13,2 +np.float32,0x803bdcdc,0x803bdcdc,2 +np.float32,0xbe88b354,0xbe8a5497,2 +np.float32,0x3f4eaf43,0x3f65e1c2,2 +np.float32,0x3f15e5b8,0x3f1e9c60,2 +np.float32,0x3e8a870c,0x3e8c394e,2 +np.float32,0x7e113de9,0x7f800000,2 +np.float32,0x7ee5ba41,0x7f800000,2 +np.float32,0xbe73d178,0xbe7620eb,2 +np.float32,0xfe972e6a,0xff800000,2 +np.float32,0xbf65567d,0xbf82a25a,2 +np.float32,0x3f38247e,0x3f487010,2 +np.float32,0xbece1c62,0xbed3b918,2 +np.float32,0x442c8d,0x442c8d,2 +np.float32,0x2dc52,0x2dc52,2 +np.float32,0x802ed923,0x802ed923,2 +np.float32,0x788cf8,0x788cf8,2 +np.float32,0x8024888e,0x8024888e,2 +np.float32,0x3f789bde,0x3f90c8fc,2 +np.float32,0x3f5de620,0x3f7abf88,2 +np.float32,0x3f0ffc45,0x3f17b2a7,2 +np.float32,0xbf709678,0xbf8accd4,2 +np.float32,0x12181f,0x12181f,2 +np.float32,0xfe54bbe4,0xff800000,2 +np.float32,0x7f1daba0,0x7f800000,2 +np.float32,0xbf6226df,0xbf805e3c,2 +np.float32,0xbd120610,0xbd120dfb,2 +np.float32,0x7f75e951,0x7f800000,2 +np.float32,0x80068048,0x80068048,2 +np.float32,0x45f04a,0x45f04a,2 +np.float32,0xff4c4f58,0xff800000,2 +np.float32,0x311604,0x311604,2 +np.float32,0x805e809c,0x805e809c,2 +np.float32,0x3d1d62c0,0x3d1d6caa,2 +np.float32,0x7f14ccf9,0x7f800000,2 +np.float32,0xff10017c,0xff800000,2 +np.float32,0xbf43ec48,0xbf579df4,2 +np.float32,0xff64da57,0xff800000,2 +np.float32,0x7f0622c5,0x7f800000,2 +np.float32,0x7f5460cd,0x7f800000,2 +np.float32,0xff0ef1c6,0xff800000,2 +np.float32,0xbece1146,0xbed3ad13,2 +np.float32,0x3f4d457f,0x3f63fc70,2 +np.float32,0xbdc1da28,0xbdc2244b,2 +np.float32,0xbe46d3f4,0xbe481463,2 +np.float32,0xff36b3d6,0xff800000,2 +np.float32,0xbec2e76c,0xbec7a540,2 +np.float32,0x8078fb81,0x8078fb81,2 +np.float32,0x7ec819cb,0x7f800000,2 +np.float32,0x39c4d,0x39c4d,2 +np.float32,0xbe8cddc2,0xbe8ea670,2 +np.float32,0xbf36dffb,0xbf46d48b,2 +np.float32,0xbf2302a3,0xbf2e4065,2 +np.float32,0x3e7b34a2,0x3e7dbb9a,2 +np.float32,0x3e3d87e1,0x3e3e9d62,2 +np.float32,0x7f3c94b1,0x7f800000,2 +np.float32,0x80455a85,0x80455a85,2 +np.float32,0xfd875568,0xff800000,2 +np.float32,0xbf618103,0xbf7fd1c8,2 +np.float32,0xbe332e3c,0xbe3418ac,2 +np.float32,0x80736b79,0x80736b79,2 +np.float32,0x3f705d9a,0x3f8aa2e6,2 +np.float32,0xbf3a36d2,0xbf4b134b,2 +np.float32,0xfddc55c0,0xff800000,2 +np.float32,0x805606fd,0x805606fd,2 +np.float32,0x3f4f0bc4,0x3f665e25,2 +np.float32,0xfebe7494,0xff800000,2 +np.float32,0xff0c541b,0xff800000,2 +np.float32,0xff0b8e7f,0xff800000,2 +np.float32,0xbcc51640,0xbcc51b1e,2 +np.float32,0x7ec1c4d0,0x7f800000,2 +np.float32,0xfc5c8e00,0xff800000,2 +np.float32,0x7f48d682,0x7f800000,2 +np.float32,0x7d5c7d8d,0x7f800000,2 +np.float32,0x8052ed03,0x8052ed03,2 +np.float32,0x7d4db058,0x7f800000,2 +np.float32,0xff3a65ee,0xff800000,2 +np.float32,0x806eeb93,0x806eeb93,2 +np.float32,0x803f9733,0x803f9733,2 +np.float32,0xbf2d1388,0xbf3a90e3,2 +np.float32,0x68e260,0x68e260,2 +np.float32,0x3e47a69f,0x3e48eb0e,2 +np.float32,0x3f0c4623,0x3f136646,2 +np.float32,0x3f37a831,0x3f47d249,2 +np.float32,0xff153a0c,0xff800000,2 +np.float32,0x2e8086,0x2e8086,2 +np.float32,0xc3f5e,0xc3f5e,2 +np.float32,0x7f31dc14,0x7f800000,2 +np.float32,0xfee37d68,0xff800000,2 +np.float32,0x711d4,0x711d4,2 +np.float32,0x7ede2ce4,0x7f800000,2 +np.float32,0xbf5d76d0,0xbf7a23d0,2 +np.float32,0xbe2b9eb4,0xbe2c6cac,2 +np.float32,0x2b14d7,0x2b14d7,2 +np.float32,0x3ea1db72,0x3ea4910e,2 +np.float32,0x7f3f03f7,0x7f800000,2 +np.float32,0x92de5,0x92de5,2 +np.float32,0x80322e1b,0x80322e1b,2 +np.float32,0xbf5eb214,0xbf7bdd55,2 +np.float32,0xbf21bf87,0xbf2cba14,2 +np.float32,0xbf5d4b78,0xbf79e73a,2 +np.float32,0xbc302840,0xbc30291e,2 +np.float32,0xfee567c6,0xff800000,2 +np.float32,0x7f70ee14,0x7f800000,2 +np.float32,0x7e5c4b33,0x7f800000,2 +np.float32,0x3f1e7b64,0x3f28ccfd,2 +np.float32,0xbf6309f7,0xbf80ff3e,2 +np.float32,0x1c2fe3,0x1c2fe3,2 +np.float32,0x8e78d,0x8e78d,2 +np.float32,0x7f2fce73,0x7f800000,2 +np.float32,0x7f25f690,0x7f800000,2 +np.float32,0x8074cba5,0x8074cba5,2 +np.float32,0x16975f,0x16975f,2 +np.float32,0x8012cf5c,0x8012cf5c,2 +np.float32,0x7da72138,0x7f800000,2 +np.float32,0xbf563f35,0xbf7025be,2 +np.float32,0x3f69d3f5,0x3f85dcbe,2 +np.float32,0xbf15c148,0xbf1e7184,2 +np.float32,0xbe7a077c,0xbe7c8564,2 +np.float32,0x3ebb6ef1,0x3ebfa5e3,2 +np.float32,0xbe41fde4,0xbe43277b,2 +np.float32,0x7f10b479,0x7f800000,2 +np.float32,0x3e021ace,0x3e02747d,2 +np.float32,0x3e93d984,0x3e95e9be,2 +np.float32,0xfe17e924,0xff800000,2 +np.float32,0xfe21a7cc,0xff800000,2 +np.float32,0x8019b660,0x8019b660,2 +np.float32,0x7e954631,0x7f800000,2 +np.float32,0x7e7330d1,0x7f800000,2 +np.float32,0xbe007d98,0xbe00d3fb,2 +np.float32,0x3ef3870e,0x3efcd077,2 +np.float32,0x7f5bbde8,0x7f800000,2 +np.float32,0x14a5b3,0x14a5b3,2 +np.float32,0x3e84d23f,0x3e8650e8,2 +np.float32,0x80763017,0x80763017,2 +np.float32,0xfe871f36,0xff800000,2 +np.float32,0x7ed43150,0x7f800000,2 +np.float32,0x3cc44547,0x3cc44a16,2 +np.float32,0x3ef0c0fa,0x3ef9b97d,2 +np.float32,0xbede9944,0xbee5ad86,2 +np.float32,0xbf10f0b2,0xbf18cf0a,2 +np.float32,0x3ecdaa78,0x3ed33dd9,2 +np.float32,0x3f7cc058,0x3f93ee6b,2 +np.float32,0x2d952f,0x2d952f,2 +np.float32,0x3f2cf2de,0x3f3a687a,2 +np.float32,0x8029b33c,0x8029b33c,2 +np.float32,0xbf22c737,0xbf2df888,2 +np.float32,0xff53c84a,0xff800000,2 +np.float32,0x40a509,0x40a509,2 +np.float32,0x56abce,0x56abce,2 +np.float32,0xff7fffff,0xff800000,2 +np.float32,0xbf3e67f6,0xbf50741c,2 +np.float32,0xfde67580,0xff800000,2 +np.float32,0x3f103e9b,0x3f17ffc7,2 +np.float32,0x3f3f7232,0x3f51cbe2,2 +np.float32,0x803e6d78,0x803e6d78,2 +np.float32,0x3a61da,0x3a61da,2 +np.float32,0xbc04de80,0xbc04dedf,2 +np.float32,0x7f1e7c52,0x7f800000,2 +np.float32,0x8058ee88,0x8058ee88,2 +np.float32,0x806dd660,0x806dd660,2 +np.float32,0x7e4af9,0x7e4af9,2 +np.float32,0x80702d27,0x80702d27,2 +np.float32,0x802cdad1,0x802cdad1,2 +np.float32,0x3e9b5c23,0x3e9dc149,2 +np.float32,0x7f076e89,0x7f800000,2 +np.float32,0x7f129d68,0x7f800000,2 +np.float32,0x7f6f0b0a,0x7f800000,2 +np.float32,0x7eafafb5,0x7f800000,2 +np.float32,0xbf2ef2ca,0xbf3ce332,2 +np.float32,0xff34c000,0xff800000,2 +np.float32,0x7f559274,0x7f800000,2 +np.float32,0xfed08556,0xff800000,2 +np.float32,0xbf014621,0xbf06d6ad,2 +np.float32,0xff23086a,0xff800000,2 +np.float32,0x6cb33f,0x6cb33f,2 +np.float32,0xfe6e3ffc,0xff800000,2 +np.float32,0x3e6bbec0,0x3e6dd546,2 +np.float32,0x8036afa6,0x8036afa6,2 +np.float32,0xff800000,0xff800000,2 +np.float32,0x3e0ed05c,0x3e0f46ff,2 +np.float32,0x3ec9215c,0x3ece57e6,2 +np.float32,0xbf449fa4,0xbf5888aa,2 +np.float32,0xff2c6640,0xff800000,2 +np.float32,0x7f08f4a7,0x7f800000,2 +np.float32,0xbf4f63e5,0xbf66d4c1,2 +np.float32,0x3f800000,0x3f966cfe,2 +np.float32,0xfe86c7d2,0xff800000,2 +np.float32,0x3f63f969,0x3f81a970,2 +np.float32,0xbd7022d0,0xbd704609,2 +np.float32,0xbead906c,0xbeb0e853,2 +np.float32,0x7ef149ee,0x7f800000,2 +np.float32,0xff0b9ff7,0xff800000,2 +np.float32,0x3f38380d,0x3f4888e7,2 +np.float32,0x3ef3a3e2,0x3efcf09e,2 +np.float32,0xff616477,0xff800000,2 +np.float32,0x3f3f83e4,0x3f51e2c3,2 +np.float32,0xbf79963c,0xbf918642,2 +np.float32,0x801416f4,0x801416f4,2 +np.float32,0xff75ce6d,0xff800000,2 +np.float32,0xbdbf3588,0xbdbf7cad,2 +np.float32,0xbe6ea938,0xbe70d3dc,2 +np.float32,0x8066f977,0x8066f977,2 +np.float32,0x3f5b5362,0x3f7728aa,2 +np.float32,0xbf72052c,0xbf8bdbd8,2 +np.float32,0xbe21ed74,0xbe229a6f,2 +np.float32,0x8062d19c,0x8062d19c,2 +np.float32,0x3ed8d01f,0x3edf59e6,2 +np.float32,0x803ed42b,0x803ed42b,2 +np.float32,0xbe099a64,0xbe0a0481,2 +np.float32,0xbe173eb4,0xbe17cba2,2 +np.float32,0xbebdcf02,0xbec22faf,2 +np.float32,0x7e3ff29e,0x7f800000,2 +np.float32,0x367c92,0x367c92,2 +np.float32,0xbf5c9db8,0xbf78f4a4,2 +np.float32,0xff0b49ea,0xff800000,2 +np.float32,0x3f4f9bc4,0x3f672001,2 +np.float32,0x85d4a,0x85d4a,2 +np.float32,0x80643e33,0x80643e33,2 +np.float32,0x8013aabd,0x8013aabd,2 +np.float32,0xff6997c3,0xff800000,2 +np.float32,0x3f4dd43c,0x3f64bbb6,2 +np.float32,0xff13bbb9,0xff800000,2 +np.float32,0x3f34efa2,0x3f446187,2 +np.float32,0x3e4b2f10,0x3e4c850d,2 +np.float32,0xfef695c6,0xff800000,2 +np.float32,0x7f7e0057,0x7f800000,2 +np.float32,0x3f6e1b9c,0x3f88fa40,2 +np.float32,0x806e46cf,0x806e46cf,2 +np.float32,0x3f15a88a,0x3f1e546c,2 +np.float32,0xbd2de7d0,0xbd2df530,2 +np.float32,0xbf63cae0,0xbf818854,2 +np.float32,0xbdc3e1a0,0xbdc42e1e,2 +np.float32,0xbf11a038,0xbf199b98,2 +np.float32,0xbec13706,0xbec5d56b,2 +np.float32,0x3f1c5f54,0x3f26478d,2 +np.float32,0x3e9ea97e,0x3ea136b4,2 +np.float32,0xfeb5a508,0xff800000,2 +np.float32,0x7f4698f4,0x7f800000,2 +np.float32,0xff51ee2c,0xff800000,2 +np.float32,0xff5994df,0xff800000,2 +np.float32,0x4b9fb9,0x4b9fb9,2 +np.float32,0xfda10d98,0xff800000,2 +np.float32,0x525555,0x525555,2 +np.float32,0x7ed571ef,0x7f800000,2 +np.float32,0xbf600d18,0xbf7dc50c,2 +np.float32,0x3ec674ca,0x3ecb768b,2 +np.float32,0x3cb69115,0x3cb694f3,2 +np.float32,0x7eac75f2,0x7f800000,2 +np.float32,0x804d4d75,0x804d4d75,2 +np.float32,0xfed5292e,0xff800000,2 +np.float32,0x800ed06a,0x800ed06a,2 +np.float32,0xfec37584,0xff800000,2 +np.float32,0x3ef96ac7,0x3f01b326,2 +np.float32,0x42f743,0x42f743,2 +np.float32,0x3f56f442,0x3f711e39,2 +np.float32,0xbf7ea726,0xbf956375,2 +np.float32,0x806c7202,0x806c7202,2 +np.float32,0xbd8ee980,0xbd8f0733,2 +np.float32,0xbdf2e930,0xbdf37b18,2 +np.float32,0x3f103910,0x3f17f955,2 +np.float32,0xff123e8f,0xff800000,2 +np.float32,0x806e4b5d,0x806e4b5d,2 +np.float32,0xbf4f3bfc,0xbf669f07,2 +np.float32,0xbf070c16,0xbf0d6609,2 +np.float32,0xff00e0ba,0xff800000,2 +np.float32,0xff49d828,0xff800000,2 +np.float32,0x7e47f04a,0x7f800000,2 +np.float32,0x7e984dac,0x7f800000,2 +np.float32,0x3f77473c,0x3f8fc858,2 +np.float32,0x3f017439,0x3f070ac8,2 +np.float32,0x118417,0x118417,2 +np.float32,0xbcf7a2c0,0xbcf7ac68,2 +np.float32,0xfee46fee,0xff800000,2 +np.float32,0x3e42a648,0x3e43d2e9,2 +np.float32,0x80131916,0x80131916,2 +np.float32,0x806209d3,0x806209d3,2 +np.float32,0x807c1f12,0x807c1f12,2 +np.float32,0x2f3696,0x2f3696,2 +np.float32,0xff28722b,0xff800000,2 +np.float32,0x7f1416a1,0x7f800000,2 +np.float32,0x8054e7a1,0x8054e7a1,2 +np.float32,0xbddc39a0,0xbddca656,2 +np.float32,0x7dc60175,0x7f800000,2 +np.float64,0x7fd0ae584da15cb0,0x7ff0000000000000,2 +np.float64,0x7fd41d68e5283ad1,0x7ff0000000000000,2 +np.float64,0x7fe93073bb7260e6,0x7ff0000000000000,2 +np.float64,0x3fb4fd19d229fa34,0x3fb5031f57dbac0f,2 +np.float64,0x85609ce10ac2,0x85609ce10ac2,2 +np.float64,0xbfd7aa12ccaf5426,0xbfd8351003a320e2,2 +np.float64,0x8004487c9b4890fa,0x8004487c9b4890fa,2 +np.float64,0x7fe7584cfd2eb099,0x7ff0000000000000,2 +np.float64,0x800ea8edc6dd51dc,0x800ea8edc6dd51dc,2 +np.float64,0x3fe0924aa5a12495,0x3fe15276e271c6dc,2 +np.float64,0x3feb1abf6d36357f,0x3fee76b4d3d06964,2 +np.float64,0x3fa8c14534318280,0x3fa8c3bd5ce5923c,2 +np.float64,0x800b9f5915d73eb3,0x800b9f5915d73eb3,2 +np.float64,0xffc05aaa7820b554,0xfff0000000000000,2 +np.float64,0x800157eda8c2afdc,0x800157eda8c2afdc,2 +np.float64,0xffe8d90042b1b200,0xfff0000000000000,2 +np.float64,0x3feda02ea93b405d,0x3ff1057e61d08d59,2 +np.float64,0xffd03b7361a076e6,0xfff0000000000000,2 +np.float64,0x3fe1a8ecd7e351da,0x3fe291eda9080847,2 +np.float64,0xffc5bfdff82b7fc0,0xfff0000000000000,2 +np.float64,0xbfe6fb3d386df67a,0xbfe9022c05df0565,2 +np.float64,0x7fefffffffffffff,0x7ff0000000000000,2 +np.float64,0x7fa10c340c221867,0x7ff0000000000000,2 +np.float64,0x3fe55cbf1daab97e,0x3fe6fc1648258b75,2 +np.float64,0xbfddeb5f60bbd6be,0xbfdf056d4fb5825f,2 +np.float64,0xffddb1a8213b6350,0xfff0000000000000,2 +np.float64,0xbfb20545e4240a88,0xbfb2091579375176,2 +np.float64,0x3f735ded2026bbda,0x3f735df1dad4ee3a,2 +np.float64,0xbfd1eb91efa3d724,0xbfd227c044dead61,2 +np.float64,0xffd737c588ae6f8c,0xfff0000000000000,2 +np.float64,0x3fc46818ec28d032,0x3fc47e416c4237a6,2 +np.float64,0x0,0x0,2 +np.float64,0xffb632097a2c6410,0xfff0000000000000,2 +np.float64,0xbfcb5ae84b36b5d0,0xbfcb905613af55b8,2 +np.float64,0xbfe7b926402f724c,0xbfe9f4f0be6aacc3,2 +np.float64,0x80081840b3f03082,0x80081840b3f03082,2 +np.float64,0x3fe767a656eecf4d,0x3fe98c53b4779de7,2 +np.float64,0x8005834c088b0699,0x8005834c088b0699,2 +np.float64,0x80074e92658e9d26,0x80074e92658e9d26,2 +np.float64,0x80045d60c268bac2,0x80045d60c268bac2,2 +np.float64,0xffb9aecfe8335da0,0xfff0000000000000,2 +np.float64,0x7fcad3e1cd35a7c3,0x7ff0000000000000,2 +np.float64,0xbf881853d03030c0,0xbf8818783e28fc87,2 +np.float64,0xe18c6d23c318e,0xe18c6d23c318e,2 +np.float64,0x7fcb367b8f366cf6,0x7ff0000000000000,2 +np.float64,0x5c13436cb8269,0x5c13436cb8269,2 +np.float64,0xffe5399938aa7332,0xfff0000000000000,2 +np.float64,0xbfdc45dbc3b88bb8,0xbfdd33958222c27e,2 +np.float64,0xbfd714691bae28d2,0xbfd7954edbef810b,2 +np.float64,0xbfdf18b02b3e3160,0xbfe02ad13634c651,2 +np.float64,0x8003e6f276e7cde6,0x8003e6f276e7cde6,2 +np.float64,0x3febb6b412776d68,0x3fef4f753def31f9,2 +np.float64,0x7fe016a3b4a02d46,0x7ff0000000000000,2 +np.float64,0x3fdc899ac7b91336,0x3fdd7e1cee1cdfc8,2 +np.float64,0x800219271e24324f,0x800219271e24324f,2 +np.float64,0x1529d93e2a53c,0x1529d93e2a53c,2 +np.float64,0x800d5bc827fab790,0x800d5bc827fab790,2 +np.float64,0x3e1495107c293,0x3e1495107c293,2 +np.float64,0x3fe89da0f2b13b42,0x3feb1dc1f3015ad7,2 +np.float64,0x800ba8c17b975183,0x800ba8c17b975183,2 +np.float64,0x8002dacf0265b59f,0x8002dacf0265b59f,2 +np.float64,0xffe6d0a4cc2da149,0xfff0000000000000,2 +np.float64,0x3fdf23fe82be47fc,0x3fe03126d8e2b309,2 +np.float64,0xffe41b1f1c28363e,0xfff0000000000000,2 +np.float64,0xbfd635c634ac6b8c,0xbfd6a8966da6adaa,2 +np.float64,0x800755bc08eeab79,0x800755bc08eeab79,2 +np.float64,0x800ba4c47c374989,0x800ba4c47c374989,2 +np.float64,0x7fec9f7649793eec,0x7ff0000000000000,2 +np.float64,0x7fdbf45738b7e8ad,0x7ff0000000000000,2 +np.float64,0x3f5597f07eab4,0x3f5597f07eab4,2 +np.float64,0xbfbf4599183e8b30,0xbfbf5985d8c65097,2 +np.float64,0xbf5b200580364000,0xbf5b2006501b21ae,2 +np.float64,0x7f91868370230d06,0x7ff0000000000000,2 +np.float64,0x3838e2a67071d,0x3838e2a67071d,2 +np.float64,0xffefe3ff5d3fc7fe,0xfff0000000000000,2 +np.float64,0xffe66b26d06cd64d,0xfff0000000000000,2 +np.float64,0xbfd830a571b0614a,0xbfd8c526927c742c,2 +np.float64,0x7fe8442122f08841,0x7ff0000000000000,2 +np.float64,0x800efa8c637df519,0x800efa8c637df519,2 +np.float64,0xf0026835e004d,0xf0026835e004d,2 +np.float64,0xffb11beefe2237e0,0xfff0000000000000,2 +np.float64,0x3fef9bbb327f3776,0x3ff2809f10641c32,2 +np.float64,0x350595306a0b3,0x350595306a0b3,2 +np.float64,0xf7f6538befecb,0xf7f6538befecb,2 +np.float64,0xffe36379c4a6c6f3,0xfff0000000000000,2 +np.float64,0x28b1d82e5163c,0x28b1d82e5163c,2 +np.float64,0x70a3d804e147c,0x70a3d804e147c,2 +np.float64,0xffd96c1bc9b2d838,0xfff0000000000000,2 +np.float64,0xffce8e00893d1c00,0xfff0000000000000,2 +np.float64,0x800f2bdcb25e57b9,0x800f2bdcb25e57b9,2 +np.float64,0xbfe0d9c63361b38c,0xbfe1a3eb02192b76,2 +np.float64,0xbfdc7b8711b8f70e,0xbfdd6e9db3a01e51,2 +np.float64,0x99e22ec133c46,0x99e22ec133c46,2 +np.float64,0xffeaef6ddab5dedb,0xfff0000000000000,2 +np.float64,0x7fe89c22c0f13845,0x7ff0000000000000,2 +np.float64,0x8002d5207de5aa42,0x8002d5207de5aa42,2 +np.float64,0x3fd1b13353236267,0x3fd1eb1b9345dfca,2 +np.float64,0x800ccae0a41995c1,0x800ccae0a41995c1,2 +np.float64,0x3fdbdaba38b7b574,0x3fdcbdfcbca37ce6,2 +np.float64,0x5b06d12cb60db,0x5b06d12cb60db,2 +np.float64,0xffd52262752a44c4,0xfff0000000000000,2 +np.float64,0x5a17f050b42ff,0x5a17f050b42ff,2 +np.float64,0x3d24205e7a485,0x3d24205e7a485,2 +np.float64,0x7fbed4dec63da9bd,0x7ff0000000000000,2 +np.float64,0xbfe56e9776aadd2f,0xbfe71212863c284f,2 +np.float64,0x7fea0bc952341792,0x7ff0000000000000,2 +np.float64,0x800f692d139ed25a,0x800f692d139ed25a,2 +np.float64,0xffdb63feab36c7fe,0xfff0000000000000,2 +np.float64,0x3fe1c2297fe38452,0x3fe2af21293c9571,2 +np.float64,0x7fede384747bc708,0x7ff0000000000000,2 +np.float64,0x800440169288802e,0x800440169288802e,2 +np.float64,0xffe3241eeb26483e,0xfff0000000000000,2 +np.float64,0xffe28f3879651e70,0xfff0000000000000,2 +np.float64,0xa435cbc1486d,0xa435cbc1486d,2 +np.float64,0x7fe55e08db6abc11,0x7ff0000000000000,2 +np.float64,0x1405e624280be,0x1405e624280be,2 +np.float64,0x3fd861bdf0b0c37c,0x3fd8f9d2e33e45e5,2 +np.float64,0x3feeb67cdc3d6cfa,0x3ff1d337d81d1c14,2 +np.float64,0x3fd159a10e22b342,0x3fd1903be7c2ea0c,2 +np.float64,0x3fd84626bc308c4d,0x3fd8dc373645e65b,2 +np.float64,0xffd3da81d9a7b504,0xfff0000000000000,2 +np.float64,0xbfd4a768b8294ed2,0xbfd503aa7c240051,2 +np.float64,0x3fe3059f2a660b3e,0x3fe42983e0c6bb2e,2 +np.float64,0x3fe3b8353827706a,0x3fe4fdd635c7269b,2 +np.float64,0xbfe4af0399695e07,0xbfe6277d9002b46c,2 +np.float64,0xbfd7e18a92afc316,0xbfd87066b54c4fe6,2 +np.float64,0x800432bcab48657a,0x800432bcab48657a,2 +np.float64,0x80033d609d267ac2,0x80033d609d267ac2,2 +np.float64,0x7fef5f758e7ebeea,0x7ff0000000000000,2 +np.float64,0xbfed7833dbfaf068,0xbff0e85bf45a5ebc,2 +np.float64,0x3fe2283985a45073,0x3fe325b0a9099c74,2 +np.float64,0xe820b4b3d0417,0xe820b4b3d0417,2 +np.float64,0x8003ecb72aa7d96f,0x8003ecb72aa7d96f,2 +np.float64,0xbfeab2c755b5658f,0xbfede7c83e92a625,2 +np.float64,0xbfc7b287f72f6510,0xbfc7d53ef2ffe9dc,2 +np.float64,0xffd9a41d0f33483a,0xfff0000000000000,2 +np.float64,0x3fd3a5b6e3a74b6c,0x3fd3f516f39a4725,2 +np.float64,0x800bc72091578e42,0x800bc72091578e42,2 +np.float64,0x800ff405ce9fe80c,0x800ff405ce9fe80c,2 +np.float64,0x57918600af24,0x57918600af24,2 +np.float64,0x2a5be7fa54b7e,0x2a5be7fa54b7e,2 +np.float64,0xbfdca7886bb94f10,0xbfdd9f142b5b43e4,2 +np.float64,0xbfe216993ee42d32,0xbfe3112936590995,2 +np.float64,0xbfe06bd9cf20d7b4,0xbfe126cd353ab42f,2 +np.float64,0x8003e6c31827cd87,0x8003e6c31827cd87,2 +np.float64,0x8005f37d810be6fc,0x8005f37d810be6fc,2 +np.float64,0x800715b081ae2b62,0x800715b081ae2b62,2 +np.float64,0x3fef94c35bff2986,0x3ff27b4bed2f4051,2 +np.float64,0x6f5798e0deb0,0x6f5798e0deb0,2 +np.float64,0x3fcef1f05c3de3e1,0x3fcf3f557550598f,2 +np.float64,0xbf9a91c400352380,0xbf9a92876273b85c,2 +np.float64,0x3fc9143f7f322880,0x3fc93d678c05d26b,2 +np.float64,0x78ad847af15b1,0x78ad847af15b1,2 +np.float64,0x8000fdc088c1fb82,0x8000fdc088c1fb82,2 +np.float64,0x800200fd304401fb,0x800200fd304401fb,2 +np.float64,0x7fb8ab09dc315613,0x7ff0000000000000,2 +np.float64,0x3fe949771b7292ee,0x3fec00891c3fc5a2,2 +np.float64,0xbfc54cae0e2a995c,0xbfc565e0f3d0e3af,2 +np.float64,0xffd546161e2a8c2c,0xfff0000000000000,2 +np.float64,0x800fe1d1279fc3a2,0x800fe1d1279fc3a2,2 +np.float64,0x3fd9c45301b388a8,0x3fda77fa1f4c79bf,2 +np.float64,0x7fe10ff238221fe3,0x7ff0000000000000,2 +np.float64,0xbfbc2181ae384300,0xbfbc3002229155c4,2 +np.float64,0xbfe7bbfae4ef77f6,0xbfe9f895e91f468d,2 +np.float64,0x800d3d994f7a7b33,0x800d3d994f7a7b33,2 +np.float64,0xffe6e15a896dc2b4,0xfff0000000000000,2 +np.float64,0x800e6b6c8abcd6d9,0x800e6b6c8abcd6d9,2 +np.float64,0xbfd862c938b0c592,0xbfd8faf1cdcb09db,2 +np.float64,0xffe2411f8464823e,0xfff0000000000000,2 +np.float64,0xffd0b32efaa1665e,0xfff0000000000000,2 +np.float64,0x3ac4ace475896,0x3ac4ace475896,2 +np.float64,0xf9c3a7ebf3875,0xf9c3a7ebf3875,2 +np.float64,0xdb998ba5b7332,0xdb998ba5b7332,2 +np.float64,0xbfe438a14fe87142,0xbfe5981751e4c5cd,2 +np.float64,0xbfbcf48cbc39e918,0xbfbd045d60e65d3a,2 +np.float64,0x7fde499615bc932b,0x7ff0000000000000,2 +np.float64,0x800bba269057744e,0x800bba269057744e,2 +np.float64,0x3fc9bb1ba3337638,0x3fc9e78fdb6799c1,2 +np.float64,0xffd9f974fbb3f2ea,0xfff0000000000000,2 +np.float64,0x7fcf1ad1693e35a2,0x7ff0000000000000,2 +np.float64,0x7fe5dcedd32bb9db,0x7ff0000000000000,2 +np.float64,0xeb06500bd60ca,0xeb06500bd60ca,2 +np.float64,0x7fd73e7b592e7cf6,0x7ff0000000000000,2 +np.float64,0xbfe9d91ae873b236,0xbfecc08482849bcd,2 +np.float64,0xffc85338b730a670,0xfff0000000000000,2 +np.float64,0x7fbba41eee37483d,0x7ff0000000000000,2 +np.float64,0x3fed5624fb7aac4a,0x3ff0cf9f0de1fd54,2 +np.float64,0xffe566d80d6acdb0,0xfff0000000000000,2 +np.float64,0x3fd4477884a88ef1,0x3fd49ec7acdd25a0,2 +np.float64,0x3fcb98c5fd37318c,0x3fcbcfa20e2c2712,2 +np.float64,0xffdeba71d5bd74e4,0xfff0000000000000,2 +np.float64,0x8001edc59dc3db8c,0x8001edc59dc3db8c,2 +np.float64,0x3fe6b09e896d613e,0x3fe8a3bb541ec0e3,2 +np.float64,0x3fe8694b4970d296,0x3fead94d271d05cf,2 +np.float64,0xb52c27bf6a585,0xb52c27bf6a585,2 +np.float64,0x7fcb0a21d9361443,0x7ff0000000000000,2 +np.float64,0xbfd9efc68cb3df8e,0xbfdaa7058c0ccbd1,2 +np.float64,0x8007cd170fef9a2f,0x8007cd170fef9a2f,2 +np.float64,0x3fe83325e770664c,0x3fea92c55c9d567e,2 +np.float64,0x800bd0085537a011,0x800bd0085537a011,2 +np.float64,0xffe05b9e7820b73c,0xfff0000000000000,2 +np.float64,0x3fea4ce4347499c8,0x3fed5cea9fdc541b,2 +np.float64,0x7fe08aae1921155b,0x7ff0000000000000,2 +np.float64,0x3fe7a5e7deef4bd0,0x3fe9dc2e20cfb61c,2 +np.float64,0xbfe0ccc8e6e19992,0xbfe195175f32ee3f,2 +np.float64,0xbfe8649717f0c92e,0xbfead3298974dcf0,2 +np.float64,0x7fed6c5308bad8a5,0x7ff0000000000000,2 +np.float64,0xffdbd8c7af37b190,0xfff0000000000000,2 +np.float64,0xbfb2bc4d06257898,0xbfb2c09569912839,2 +np.float64,0x3fc62eca512c5d95,0x3fc64b4251bce8f9,2 +np.float64,0xbfcae2ddbd35c5bc,0xbfcb15971fc61312,2 +np.float64,0x18d26ce831a4f,0x18d26ce831a4f,2 +np.float64,0x7fe38b279267164e,0x7ff0000000000000,2 +np.float64,0x97e1d9ab2fc3b,0x97e1d9ab2fc3b,2 +np.float64,0xbfee8e4785fd1c8f,0xbff1b52d16807627,2 +np.float64,0xbfb189b4a6231368,0xbfb18d37e83860ee,2 +np.float64,0xffd435761ea86aec,0xfff0000000000000,2 +np.float64,0x3fe6c48ebced891e,0x3fe8bcea189c3867,2 +np.float64,0x7fdadd3678b5ba6c,0x7ff0000000000000,2 +np.float64,0x7fea8f15b7b51e2a,0x7ff0000000000000,2 +np.float64,0xbff0000000000000,0xbff2cd9fc44eb982,2 +np.float64,0x80004c071120980f,0x80004c071120980f,2 +np.float64,0x8005367adfea6cf6,0x8005367adfea6cf6,2 +np.float64,0x3fbdc9139a3b9220,0x3fbdda4aba667ce5,2 +np.float64,0x7fed5ee3ad7abdc6,0x7ff0000000000000,2 +np.float64,0x51563fb2a2ac9,0x51563fb2a2ac9,2 +np.float64,0xbfba7d26ce34fa50,0xbfba894229c50ea1,2 +np.float64,0x6c10db36d821c,0x6c10db36d821c,2 +np.float64,0xbfbdaec0d03b5d80,0xbfbdbfca6ede64f4,2 +np.float64,0x800a1cbe7414397d,0x800a1cbe7414397d,2 +np.float64,0x800ae6e7f2d5cdd0,0x800ae6e7f2d5cdd0,2 +np.float64,0x3fea63d3fef4c7a8,0x3fed7c1356688ddc,2 +np.float64,0xbfde1e3a88bc3c76,0xbfdf3dfb09cc2260,2 +np.float64,0xbfd082d75a2105ae,0xbfd0b1e28c84877b,2 +np.float64,0x7fea1e5e85f43cbc,0x7ff0000000000000,2 +np.float64,0xffe2237a1a6446f4,0xfff0000000000000,2 +np.float64,0x3fd1e2be8523c57d,0x3fd21e93dfd1bbc4,2 +np.float64,0x3fd1acd428a359a8,0x3fd1e6916a42bc3a,2 +np.float64,0x61a152f0c342b,0x61a152f0c342b,2 +np.float64,0xbfc61a6b902c34d8,0xbfc6369557690ba0,2 +np.float64,0x7fd1a84b1f235095,0x7ff0000000000000,2 +np.float64,0x1c5cc7e638b9a,0x1c5cc7e638b9a,2 +np.float64,0x8008039755f0072f,0x8008039755f0072f,2 +np.float64,0x80097532d6f2ea66,0x80097532d6f2ea66,2 +np.float64,0xbfc6d979a12db2f4,0xbfc6f89777c53f8f,2 +np.float64,0x8004293ab1085276,0x8004293ab1085276,2 +np.float64,0x3fc2af5c21255eb8,0x3fc2c05dc0652554,2 +np.float64,0xbfd9a5ab87b34b58,0xbfda56d1076abc98,2 +np.float64,0xbfebd360ba77a6c2,0xbfef779fd6595f9b,2 +np.float64,0xffd5313c43aa6278,0xfff0000000000000,2 +np.float64,0xbfe994a262b32945,0xbfec64b969852ed5,2 +np.float64,0x3fce01a52e3c034a,0x3fce48324eb29c31,2 +np.float64,0x56bd74b2ad7af,0x56bd74b2ad7af,2 +np.float64,0xb84093ff70813,0xb84093ff70813,2 +np.float64,0x7fe776df946eedbe,0x7ff0000000000000,2 +np.float64,0xbfe294ac2e652958,0xbfe3a480938afa26,2 +np.float64,0x7fe741b4d0ee8369,0x7ff0000000000000,2 +np.float64,0x800b7e8a1056fd15,0x800b7e8a1056fd15,2 +np.float64,0x7fd28f1269251e24,0x7ff0000000000000,2 +np.float64,0x8009d4492e73a893,0x8009d4492e73a893,2 +np.float64,0x3fe3f27fca67e500,0x3fe543aff825e244,2 +np.float64,0x3fd12447e5a24890,0x3fd158efe43c0452,2 +np.float64,0xbfd58df0f2ab1be2,0xbfd5f6d908e3ebce,2 +np.float64,0xffc0a8e4642151c8,0xfff0000000000000,2 +np.float64,0xbfedb197787b632f,0xbff112367ec9d3e7,2 +np.float64,0xffdde07a7f3bc0f4,0xfff0000000000000,2 +np.float64,0x3fe91f3e5b723e7d,0x3febc886a1d48364,2 +np.float64,0x3fe50415236a082a,0x3fe68f43a5468d8c,2 +np.float64,0xd9a0c875b3419,0xd9a0c875b3419,2 +np.float64,0xbfee04ccf4bc099a,0xbff14f4740a114cf,2 +np.float64,0xbfd2bcc6a125798e,0xbfd30198b1e7d7ed,2 +np.float64,0xbfeb3c16f8f6782e,0xbfeea4ce47d09f58,2 +np.float64,0xffd3ba19e4a77434,0xfff0000000000000,2 +np.float64,0x8010000000000000,0x8010000000000000,2 +np.float64,0x3fdef0a642bde14d,0x3fe0146677b3a488,2 +np.float64,0x3fdc3dd0a2b87ba0,0x3fdd2abe65651487,2 +np.float64,0x3fdbb1fd47b763fb,0x3fdc915a2fd19f4b,2 +np.float64,0x7fbaa375e63546eb,0x7ff0000000000000,2 +np.float64,0x433ef8ee867e0,0x433ef8ee867e0,2 +np.float64,0xf5345475ea68b,0xf5345475ea68b,2 +np.float64,0xa126419b424c8,0xa126419b424c8,2 +np.float64,0x3fe0057248200ae5,0x3fe0b2f488339709,2 +np.float64,0xffc5e3b82f2bc770,0xfff0000000000000,2 +np.float64,0xffb215c910242b90,0xfff0000000000000,2 +np.float64,0xbfeba4ae0837495c,0xbfef3642e4b54aac,2 +np.float64,0xffbb187ebe363100,0xfff0000000000000,2 +np.float64,0x3fe4c6a496a98d49,0x3fe64440cdf06aab,2 +np.float64,0x800767a28f6ecf46,0x800767a28f6ecf46,2 +np.float64,0x3fdbed63b1b7dac8,0x3fdcd27318c0b683,2 +np.float64,0x80006d8339e0db07,0x80006d8339e0db07,2 +np.float64,0x8000b504f0416a0b,0x8000b504f0416a0b,2 +np.float64,0xbfe88055bfb100ac,0xbfeaf767bd2767b9,2 +np.float64,0x3fefe503317fca06,0x3ff2b8d4057240c8,2 +np.float64,0x7fe307538b660ea6,0x7ff0000000000000,2 +np.float64,0x944963c12892d,0x944963c12892d,2 +np.float64,0xbfd2c20b38a58416,0xbfd30717900f8233,2 +np.float64,0x7feed04e3e3da09b,0x7ff0000000000000,2 +np.float64,0x3fe639619cac72c3,0x3fe80de7b8560a8d,2 +np.float64,0x3fde066c66bc0cd9,0x3fdf237fb759a652,2 +np.float64,0xbfc56b22b52ad644,0xbfc584c267a47ebd,2 +np.float64,0x3fc710d5b12e21ab,0x3fc730d817ba0d0c,2 +np.float64,0x3fee1dfc347c3bf8,0x3ff161d9c3e15f68,2 +np.float64,0x3fde400954bc8013,0x3fdf639e5cc9e7a9,2 +np.float64,0x56e701f8adce1,0x56e701f8adce1,2 +np.float64,0xbfe33bbc89e67779,0xbfe46996b39381fe,2 +np.float64,0x7fec89e2f87913c5,0x7ff0000000000000,2 +np.float64,0xbfdad58b40b5ab16,0xbfdba098cc0ad5d3,2 +np.float64,0x3fe99c76a13338ed,0x3fec6f31bae613e7,2 +np.float64,0x3fe4242a29a84854,0x3fe57f6b45e5c0ef,2 +np.float64,0xbfe79d3199ef3a63,0xbfe9d0fb96c846ba,2 +np.float64,0x7ff8000000000000,0x7ff8000000000000,2 +np.float64,0xbfeb35a6cf766b4e,0xbfee9be4e7e943f7,2 +np.float64,0x3e047f267c091,0x3e047f267c091,2 +np.float64,0x4bf1376a97e28,0x4bf1376a97e28,2 +np.float64,0x800ef419685de833,0x800ef419685de833,2 +np.float64,0x3fe0efa61a21df4c,0x3fe1bce98baf2f0f,2 +np.float64,0x3fcc13c4d738278a,0x3fcc4d8c778bcaf7,2 +np.float64,0x800f1d291afe3a52,0x800f1d291afe3a52,2 +np.float64,0x3fd3f10e6da7e21d,0x3fd444106761ea1d,2 +np.float64,0x800706d6d76e0dae,0x800706d6d76e0dae,2 +np.float64,0xffa1ffbc9023ff80,0xfff0000000000000,2 +np.float64,0xbfe098f26d6131e5,0xbfe15a08a5f3eac0,2 +np.float64,0x3fe984f9cc7309f4,0x3fec4fcdbdb1cb9b,2 +np.float64,0x7fd7c2f1eaaf85e3,0x7ff0000000000000,2 +np.float64,0x800a8adb64f515b7,0x800a8adb64f515b7,2 +np.float64,0x80060d3ffc8c1a81,0x80060d3ffc8c1a81,2 +np.float64,0xbfec37e4aef86fc9,0xbff0029a6a1d61e2,2 +np.float64,0x800b21bcfcf6437a,0x800b21bcfcf6437a,2 +np.float64,0xbfc08facc1211f58,0xbfc09b8380ea8032,2 +np.float64,0xffebb4b52577696a,0xfff0000000000000,2 +np.float64,0x800b08096df61013,0x800b08096df61013,2 +np.float64,0x8000000000000000,0x8000000000000000,2 +np.float64,0xffd2f0c9c8a5e194,0xfff0000000000000,2 +np.float64,0xffe78b2299af1644,0xfff0000000000000,2 +np.float64,0x7fd0444794a0888e,0x7ff0000000000000,2 +np.float64,0x307c47b460f8a,0x307c47b460f8a,2 +np.float64,0xffe6b4c851ad6990,0xfff0000000000000,2 +np.float64,0xffe1877224a30ee4,0xfff0000000000000,2 +np.float64,0x48d7b5c091af7,0x48d7b5c091af7,2 +np.float64,0xbfa1dc6b1c23b8d0,0xbfa1dd5889e1b7da,2 +np.float64,0x3fe5004737ea008e,0x3fe68a9c310b08c1,2 +np.float64,0x7fec5f0742b8be0e,0x7ff0000000000000,2 +np.float64,0x3fd0a86285a150c5,0x3fd0d8b238d557fa,2 +np.float64,0x7fed60380efac06f,0x7ff0000000000000,2 +np.float64,0xeeca74dfdd94f,0xeeca74dfdd94f,2 +np.float64,0x3fda05aaa8b40b54,0x3fdabebdbf405e84,2 +np.float64,0x800e530ceb1ca61a,0x800e530ceb1ca61a,2 +np.float64,0x800b3866379670cd,0x800b3866379670cd,2 +np.float64,0xffedb3e7fa3b67cf,0xfff0000000000000,2 +np.float64,0xffdfa4c0713f4980,0xfff0000000000000,2 +np.float64,0x7fe4679e0728cf3b,0x7ff0000000000000,2 +np.float64,0xffe978611ef2f0c2,0xfff0000000000000,2 +np.float64,0x7fc9f4601f33e8bf,0x7ff0000000000000,2 +np.float64,0x3fd4942de6a9285c,0x3fd4ef6e089357dd,2 +np.float64,0x3faafe064435fc00,0x3fab0139cd6564dc,2 +np.float64,0x800d145a519a28b5,0x800d145a519a28b5,2 +np.float64,0xbfd82636f2304c6e,0xbfd8b9f75ddd2f02,2 +np.float64,0xbfdf2e975e3e5d2e,0xbfe037174280788c,2 +np.float64,0x7fd7051d7c2e0a3a,0x7ff0000000000000,2 +np.float64,0x8007933d452f267b,0x8007933d452f267b,2 +np.float64,0xb2043beb64088,0xb2043beb64088,2 +np.float64,0x3febfd9708f7fb2e,0x3fefb2ef090f18d2,2 +np.float64,0xffd9bc6bc83378d8,0xfff0000000000000,2 +np.float64,0xc10f9fd3821f4,0xc10f9fd3821f4,2 +np.float64,0x3fe3c83413a79068,0x3fe510fa1dd8edf7,2 +np.float64,0x3fbe26ccda3c4da0,0x3fbe38a892279975,2 +np.float64,0x3fcc1873103830e6,0x3fcc5257a6ae168d,2 +np.float64,0xe7e000e9cfc00,0xe7e000e9cfc00,2 +np.float64,0xffda73852bb4e70a,0xfff0000000000000,2 +np.float64,0xbfe831be19f0637c,0xbfea90f1b34da3e5,2 +np.float64,0xbfeb568f3076ad1e,0xbfeec97eebfde862,2 +np.float64,0x510a6ad0a214e,0x510a6ad0a214e,2 +np.float64,0x3fe6ba7e35ed74fc,0x3fe8b032a9a28c6a,2 +np.float64,0xffeb5cdcff76b9b9,0xfff0000000000000,2 +np.float64,0x4f0a23e89e145,0x4f0a23e89e145,2 +np.float64,0x446ec20288dd9,0x446ec20288dd9,2 +np.float64,0x7fe2521b02e4a435,0x7ff0000000000000,2 +np.float64,0x8001cd2969e39a54,0x8001cd2969e39a54,2 +np.float64,0x3fdfe90600bfd20c,0x3fe09fdcca10001c,2 +np.float64,0x7fd660c5762cc18a,0x7ff0000000000000,2 +np.float64,0xbfb11b23aa223648,0xbfb11e661949b377,2 +np.float64,0x800e025285fc04a5,0x800e025285fc04a5,2 +np.float64,0xffb180bb18230178,0xfff0000000000000,2 +np.float64,0xaaf590df55eb2,0xaaf590df55eb2,2 +np.float64,0xbfe8637d9df0c6fb,0xbfead1ba429462ec,2 +np.float64,0x7fd2577866a4aef0,0x7ff0000000000000,2 +np.float64,0xbfcfb2ab5a3f6558,0xbfd002ee87f272b9,2 +np.float64,0x7fdd64ae2f3ac95b,0x7ff0000000000000,2 +np.float64,0xffd1a502c9234a06,0xfff0000000000000,2 +np.float64,0x7fc4be4b60297c96,0x7ff0000000000000,2 +np.float64,0xbfb46b712a28d6e0,0xbfb470fca9919172,2 +np.float64,0xffdef913033df226,0xfff0000000000000,2 +np.float64,0x3fd94a3545b2946b,0x3fd9f40431ce9f9c,2 +np.float64,0x7fef88a0b6ff1140,0x7ff0000000000000,2 +np.float64,0xbfbcc81876399030,0xbfbcd7a0ab6cb388,2 +np.float64,0x800a4acfdd9495a0,0x800a4acfdd9495a0,2 +np.float64,0xffe270b3d5e4e167,0xfff0000000000000,2 +np.float64,0xbfd23f601e247ec0,0xbfd27eeca50a49eb,2 +np.float64,0x7fec6e796a78dcf2,0x7ff0000000000000,2 +np.float64,0x3fb85e0c9630bc19,0x3fb867791ccd6c72,2 +np.float64,0x7fe49fc424a93f87,0x7ff0000000000000,2 +np.float64,0xbfe75a99fbaeb534,0xbfe97ba37663de4c,2 +np.float64,0xffe85011b630a023,0xfff0000000000000,2 +np.float64,0xffe5962e492b2c5c,0xfff0000000000000,2 +np.float64,0x6f36ed4cde6de,0x6f36ed4cde6de,2 +np.float64,0x3feb72170af6e42e,0x3feeefbe6f1a2084,2 +np.float64,0x80014d8d60629b1c,0x80014d8d60629b1c,2 +np.float64,0xbfe0eb40d321d682,0xbfe1b7e31f252bf1,2 +np.float64,0x31fe305663fc7,0x31fe305663fc7,2 +np.float64,0x3fd2cd6381a59ac7,0x3fd312edc9868a4d,2 +np.float64,0xffcf0720793e0e40,0xfff0000000000000,2 +np.float64,0xbfeef1ef133de3de,0xbff1ffd5e1a3b648,2 +np.float64,0xbfd01c787aa038f0,0xbfd0482be3158a01,2 +np.float64,0x3fda3607c5b46c10,0x3fdaf3301e217301,2 +np.float64,0xffda9a9911b53532,0xfff0000000000000,2 +np.float64,0x3fc0b37c392166f8,0x3fc0bfa076f3c43e,2 +np.float64,0xbfe06591c760cb24,0xbfe11fad179ea12c,2 +np.float64,0x8006e369c20dc6d4,0x8006e369c20dc6d4,2 +np.float64,0x3fdf2912a8be5224,0x3fe033ff74b92f4d,2 +np.float64,0xffc0feb07821fd60,0xfff0000000000000,2 +np.float64,0xa4b938c949727,0xa4b938c949727,2 +np.float64,0x8008fe676571fccf,0x8008fe676571fccf,2 +np.float64,0xbfdda68459bb4d08,0xbfdeb8faab34fcbc,2 +np.float64,0xbfda18b419343168,0xbfdad360ca52ec7c,2 +np.float64,0x3febcbae35b7975c,0x3fef6cd51c9ebc15,2 +np.float64,0x3fbec615f63d8c30,0x3fbed912ba729926,2 +np.float64,0x7f99a831c8335063,0x7ff0000000000000,2 +np.float64,0x3fe663e8826cc7d1,0x3fe84330bd9aada8,2 +np.float64,0x70a9f9e6e1540,0x70a9f9e6e1540,2 +np.float64,0x8a13a5db14275,0x8a13a5db14275,2 +np.float64,0x7fc4330a3b286613,0x7ff0000000000000,2 +np.float64,0xbfe580c6136b018c,0xbfe728806cc7a99a,2 +np.float64,0x8000000000000001,0x8000000000000001,2 +np.float64,0xffec079d5df80f3a,0xfff0000000000000,2 +np.float64,0x8e1173c31c22f,0x8e1173c31c22f,2 +np.float64,0x3fe088456d21108b,0x3fe14712ca414103,2 +np.float64,0x3fe1b76f73636edf,0x3fe2a2b658557112,2 +np.float64,0xbfd4a1dd162943ba,0xbfd4fdd45cae8fb8,2 +np.float64,0x7fd60b46c8ac168d,0x7ff0000000000000,2 +np.float64,0xffe36cc3b166d987,0xfff0000000000000,2 +np.float64,0x3fdc2ae0cfb855c0,0x3fdd15f026773151,2 +np.float64,0xbfc41aa203283544,0xbfc42fd1b145fdd5,2 +np.float64,0xffed90c55fbb218a,0xfff0000000000000,2 +np.float64,0x3fe67e3a9aecfc75,0x3fe86440db65b4f6,2 +np.float64,0x7fd12dbeaba25b7c,0x7ff0000000000000,2 +np.float64,0xbfe1267c0de24cf8,0xbfe1fbb611bdf1e9,2 +np.float64,0x22e5619645cad,0x22e5619645cad,2 +np.float64,0x7fe327c72ea64f8d,0x7ff0000000000000,2 +np.float64,0x7fd2c3f545a587ea,0x7ff0000000000000,2 +np.float64,0x7fc7b689372f6d11,0x7ff0000000000000,2 +np.float64,0xc5e140bd8bc28,0xc5e140bd8bc28,2 +np.float64,0x3fccb3627a3966c5,0x3fccf11b44fa4102,2 +np.float64,0xbfd2cf725c259ee4,0xbfd315138d0e5dca,2 +np.float64,0x10000000000000,0x10000000000000,2 +np.float64,0xbfd3dfa8b627bf52,0xbfd431d17b235477,2 +np.float64,0xbfb82124e6304248,0xbfb82a4b6d9c2663,2 +np.float64,0x3fdcd590d9b9ab22,0x3fddd1d548806347,2 +np.float64,0x7fdee0cd1b3dc199,0x7ff0000000000000,2 +np.float64,0x8004ebfc60a9d7fa,0x8004ebfc60a9d7fa,2 +np.float64,0x3fe8eb818b71d704,0x3feb842679806108,2 +np.float64,0xffdd5e8fe63abd20,0xfff0000000000000,2 +np.float64,0xbfe3efcbd9e7df98,0xbfe54071436645ee,2 +np.float64,0x3fd5102557aa204b,0x3fd57203d31a05b8,2 +np.float64,0x3fe6318af7ec6316,0x3fe8041a177cbf96,2 +np.float64,0x3fdf3cecdabe79da,0x3fe03f2084ffbc78,2 +np.float64,0x7fe0ab6673a156cc,0x7ff0000000000000,2 +np.float64,0x800037d5c6c06fac,0x800037d5c6c06fac,2 +np.float64,0xffce58b86a3cb170,0xfff0000000000000,2 +np.float64,0xbfe3455d6ce68abb,0xbfe475034cecb2b8,2 +np.float64,0x991b663d3236d,0x991b663d3236d,2 +np.float64,0x3fda82d37c3505a7,0x3fdb46973da05c12,2 +np.float64,0x3f9b736fa036e6df,0x3f9b74471c234411,2 +np.float64,0x8001c96525e392cb,0x8001c96525e392cb,2 +np.float64,0x7ff0000000000000,0x7ff0000000000000,2 +np.float64,0xbfaf59122c3eb220,0xbfaf5e15f8b272b0,2 +np.float64,0xbf9aa7d288354fa0,0xbf9aa897d2a40cb5,2 +np.float64,0x8004a43428694869,0x8004a43428694869,2 +np.float64,0x7feead476dbd5a8e,0x7ff0000000000000,2 +np.float64,0xffca150f81342a20,0xfff0000000000000,2 +np.float64,0x80047ec3bc88fd88,0x80047ec3bc88fd88,2 +np.float64,0xbfee3e5b123c7cb6,0xbff179c8b8334278,2 +np.float64,0x3fd172359f22e46b,0x3fd1a9ba6b1420a1,2 +np.float64,0x3fe8e5e242f1cbc5,0x3feb7cbcaefc4d5c,2 +np.float64,0x8007fb059a6ff60c,0x8007fb059a6ff60c,2 +np.float64,0xe3899e71c7134,0xe3899e71c7134,2 +np.float64,0x7fe3b98326a77305,0x7ff0000000000000,2 +np.float64,0x7fec4e206cb89c40,0x7ff0000000000000,2 +np.float64,0xbfa3b012c4276020,0xbfa3b150c13b3cc5,2 +np.float64,0xffefffffffffffff,0xfff0000000000000,2 +np.float64,0xffe28a5b9aa514b6,0xfff0000000000000,2 +np.float64,0xbfd76a6cc2aed4da,0xbfd7f10f4d04e7f6,2 +np.float64,0xbc2b1c0178564,0xbc2b1c0178564,2 +np.float64,0x6d9d444adb3a9,0x6d9d444adb3a9,2 +np.float64,0xbfdcadd368395ba6,0xbfdda6037b5c429c,2 +np.float64,0x3fe11891fde23124,0x3fe1ebc1c204b14b,2 +np.float64,0x3fdd66c3eebacd88,0x3fde72526b5304c4,2 +np.float64,0xbfe79d85612f3b0b,0xbfe9d1673bd1f6d6,2 +np.float64,0x3fed60abdabac158,0x3ff0d7426b3800a2,2 +np.float64,0xbfb0ffa54021ff48,0xbfb102d81073a9f0,2 +np.float64,0xd2452af5a48a6,0xd2452af5a48a6,2 +np.float64,0xf4b835c1e971,0xf4b835c1e971,2 +np.float64,0x7e269cdafc4d4,0x7e269cdafc4d4,2 +np.float64,0x800097a21d812f45,0x800097a21d812f45,2 +np.float64,0x3fdfcc85e8bf990c,0x3fe08fcf770fd456,2 +np.float64,0xd8d53155b1aa6,0xd8d53155b1aa6,2 +np.float64,0x7fb8ed658831daca,0x7ff0000000000000,2 +np.float64,0xbfec865415b90ca8,0xbff03a4584d719f9,2 +np.float64,0xffd8cda62a319b4c,0xfff0000000000000,2 +np.float64,0x273598d84e6b4,0x273598d84e6b4,2 +np.float64,0x7fd566b5c32acd6b,0x7ff0000000000000,2 +np.float64,0xff61d9d48023b400,0xfff0000000000000,2 +np.float64,0xbfec5c3bf4f8b878,0xbff01c594243337c,2 +np.float64,0x7fd1be0561a37c0a,0x7ff0000000000000,2 +np.float64,0xffeaee3271b5dc64,0xfff0000000000000,2 +np.float64,0x800c0e1931b81c33,0x800c0e1931b81c33,2 +np.float64,0xbfad1171583a22e0,0xbfad1570e5c466d2,2 +np.float64,0x7fd783b0fe2f0761,0x7ff0000000000000,2 +np.float64,0x7fc39903e6273207,0x7ff0000000000000,2 +np.float64,0xffe00003c5600007,0xfff0000000000000,2 +np.float64,0x35a7b9c06b50,0x35a7b9c06b50,2 +np.float64,0x7fee441a22bc8833,0x7ff0000000000000,2 +np.float64,0xff6e47fbc03c9000,0xfff0000000000000,2 +np.float64,0xbfd3c3c9c8a78794,0xbfd41499b1912534,2 +np.float64,0x82c9c87f05939,0x82c9c87f05939,2 +np.float64,0xbfedeb0fe4fbd620,0xbff13c573ce9d3d0,2 +np.float64,0x2b79298656f26,0x2b79298656f26,2 +np.float64,0xbf5ee44f003dc800,0xbf5ee4503353c0ba,2 +np.float64,0xbfe1dd264e63ba4c,0xbfe2ce68116c7bf6,2 +np.float64,0x3fece10b7579c217,0x3ff07b21b11799c6,2 +np.float64,0x3fba47143a348e28,0x3fba52e601adf24c,2 +np.float64,0xffe9816e7a7302dc,0xfff0000000000000,2 +np.float64,0x8009a8047fd35009,0x8009a8047fd35009,2 +np.float64,0x800ac28e4e95851d,0x800ac28e4e95851d,2 +np.float64,0x80093facf4f27f5a,0x80093facf4f27f5a,2 +np.float64,0x3ff0000000000000,0x3ff2cd9fc44eb982,2 +np.float64,0x3fe76a9857eed530,0x3fe99018a5895a4f,2 +np.float64,0xbfd13c59a3a278b4,0xbfd171e133df0b16,2 +np.float64,0x7feb43bc83368778,0x7ff0000000000000,2 +np.float64,0xbfe2970c5fa52e18,0xbfe3a74a434c6efe,2 +np.float64,0xffd091c380212388,0xfff0000000000000,2 +np.float64,0x3febb3b9d2f76774,0x3fef4b4af2bd8580,2 +np.float64,0x7fec66787ef8ccf0,0x7ff0000000000000,2 +np.float64,0xbf935e185826bc40,0xbf935e640557a354,2 +np.float64,0x979df1552f3be,0x979df1552f3be,2 +np.float64,0x7fc096ee73212ddc,0x7ff0000000000000,2 +np.float64,0xbfe9de88faf3bd12,0xbfecc7d1ae691d1b,2 +np.float64,0x7fdc733f06b8e67d,0x7ff0000000000000,2 +np.float64,0xffd71be1a0ae37c4,0xfff0000000000000,2 +np.float64,0xb50dabd36a1b6,0xb50dabd36a1b6,2 +np.float64,0x7fce3d94d63c7b29,0x7ff0000000000000,2 +np.float64,0x7fbaf95e4435f2bc,0x7ff0000000000000,2 +np.float64,0x81a32a6f03466,0x81a32a6f03466,2 +np.float64,0xa99b5b4d5336c,0xa99b5b4d5336c,2 +np.float64,0x7f97c1eeb82f83dc,0x7ff0000000000000,2 +np.float64,0x3fe761636d6ec2c6,0x3fe98451160d2ffb,2 +np.float64,0xbfe3224ef5e6449e,0xbfe44b73eeadac52,2 +np.float64,0x7fde6feb0dbcdfd5,0x7ff0000000000000,2 +np.float64,0xbfee87f9ca7d0ff4,0xbff1b079e9d7f706,2 +np.float64,0x3fe46f4c9828de99,0x3fe5da2ab9609ea5,2 +np.float64,0xffb92fe882325fd0,0xfff0000000000000,2 +np.float64,0x80054bc63cea978d,0x80054bc63cea978d,2 +np.float64,0x3d988bea7b312,0x3d988bea7b312,2 +np.float64,0x3fe6468e1d6c8d1c,0x3fe81e64d37d39a8,2 +np.float64,0x3fd68eefc22d1de0,0x3fd7074264faeead,2 +np.float64,0xffb218a074243140,0xfff0000000000000,2 +np.float64,0x3fdbcb3b6cb79678,0x3fdcad011de40b7d,2 +np.float64,0x7fe3c161772782c2,0x7ff0000000000000,2 +np.float64,0x25575c904aaec,0x25575c904aaec,2 +np.float64,0x800fa43a8f5f4875,0x800fa43a8f5f4875,2 +np.float64,0x3fe41fc9e1e83f94,0x3fe57a25dd1a37f1,2 +np.float64,0x3fd895f4a7b12be9,0x3fd931e7b721a08a,2 +np.float64,0xce31469f9c629,0xce31469f9c629,2 +np.float64,0xffea0f55ca341eab,0xfff0000000000000,2 +np.float64,0xffe831c9ba306393,0xfff0000000000000,2 +np.float64,0x7fe2056f03a40add,0x7ff0000000000000,2 +np.float64,0x7fd6b075e02d60eb,0x7ff0000000000000,2 +np.float64,0x3fdfbef4273f7de8,0x3fe0882c1f59efc0,2 +np.float64,0x8005b9e094ab73c2,0x8005b9e094ab73c2,2 +np.float64,0x3fea881ac6351036,0x3fedad7a319b887c,2 +np.float64,0xbfe2c61c7ee58c39,0xbfe3de9a99d8a9c6,2 +np.float64,0x30b0d3786161b,0x30b0d3786161b,2 +np.float64,0x3fa51d56a02a3aad,0x3fa51edee2d2ecef,2 +np.float64,0x79745732f2e8c,0x79745732f2e8c,2 +np.float64,0x800d55b4907aab69,0x800d55b4907aab69,2 +np.float64,0xbfbe8fcf0a3d1fa0,0xbfbea267fbb5bfdf,2 +np.float64,0xbfd04e2756a09c4e,0xbfd07b74d079f9a2,2 +np.float64,0x3fc65170552ca2e1,0x3fc66e6eb00c82ed,2 +np.float64,0xbfb0674b8020ce98,0xbfb06a2b4771b64c,2 +np.float64,0x2059975840b34,0x2059975840b34,2 +np.float64,0x33d1385467a28,0x33d1385467a28,2 +np.float64,0x3fea41b74ff4836f,0x3fed4dc1a09e53cc,2 +np.float64,0xbfe8e08c9d71c119,0xbfeb75b4c59a6bec,2 +np.float64,0x7fdbbf14d6377e29,0x7ff0000000000000,2 +np.float64,0x3fcd8b71513b16e0,0x3fcdcec80174f9ad,2 +np.float64,0x5c50bc94b8a18,0x5c50bc94b8a18,2 +np.float64,0x969a18f52d343,0x969a18f52d343,2 +np.float64,0x3fd7ae44462f5c89,0x3fd8398bc34e395c,2 +np.float64,0xffdd0f8617ba1f0c,0xfff0000000000000,2 +np.float64,0xfff0000000000000,0xfff0000000000000,2 +np.float64,0xbfe2f9badb65f376,0xbfe41b771320ece8,2 +np.float64,0x3fd140bc7fa29,0x3fd140bc7fa29,2 +np.float64,0xbfe14523b5628a48,0xbfe21ee850972043,2 +np.float64,0x3feedd0336bdba06,0x3ff1f01afc1f3a06,2 +np.float64,0x800de423ad7bc848,0x800de423ad7bc848,2 +np.float64,0x4cef857c99df1,0x4cef857c99df1,2 +np.float64,0xbfea55e0e374abc2,0xbfed691e41d648dd,2 +np.float64,0x3fe70d7a18ae1af4,0x3fe91955a34d8094,2 +np.float64,0xbfc62fc3032c5f88,0xbfc64c3ec25decb8,2 +np.float64,0x3fc915abb5322b58,0x3fc93edac5cc73fe,2 +np.float64,0x69aaff66d3561,0x69aaff66d3561,2 +np.float64,0x5c6a90f2b8d53,0x5c6a90f2b8d53,2 +np.float64,0x3fefe30dc1bfc61c,0x3ff2b752257bdacd,2 +np.float64,0x3fef15db15fe2bb6,0x3ff21aea05601396,2 +np.float64,0xbfe353e5ac66a7cc,0xbfe48644e6553d1a,2 +np.float64,0x3fe6d30cffada61a,0x3fe8cf3e4c61ddac,2 +np.float64,0x7fb7857eb62f0afc,0x7ff0000000000000,2 +np.float64,0xbfdd9b53d23b36a8,0xbfdeac91a7af1340,2 +np.float64,0x3fd1456357228ac7,0x3fd17b3f7d39b27a,2 +np.float64,0x3fb57d10ae2afa21,0x3fb5838702b806f4,2 +np.float64,0x800c59c96c98b393,0x800c59c96c98b393,2 +np.float64,0x7fc1f2413823e481,0x7ff0000000000000,2 +np.float64,0xbfa3983624273070,0xbfa3996fa26c419a,2 +np.float64,0x7fb28874ae2510e8,0x7ff0000000000000,2 +np.float64,0x3fe826d02a304da0,0x3fea82bec50bc0b6,2 +np.float64,0x8008d6f0d3d1ade2,0x8008d6f0d3d1ade2,2 +np.float64,0xffe7c970ca2f92e1,0xfff0000000000000,2 +np.float64,0x7fcf42bcaa3e8578,0x7ff0000000000000,2 +np.float64,0x7fda1ab517343569,0x7ff0000000000000,2 +np.float64,0xbfe7926a65ef24d5,0xbfe9c323dd890d5b,2 +np.float64,0xbfcaf6282d35ec50,0xbfcb294f36a0a33d,2 +np.float64,0x800ca49df8d9493c,0x800ca49df8d9493c,2 +np.float64,0xffea18d26af431a4,0xfff0000000000000,2 +np.float64,0x3fb72f276e2e5e50,0x3fb7374539fd1221,2 +np.float64,0xffa6b613842d6c20,0xfff0000000000000,2 +np.float64,0xbfeb3c7263f678e5,0xbfeea54cdb60b54c,2 +np.float64,0x3fc976d2ba32eda5,0x3fc9a1e83a058de4,2 +np.float64,0xbfe4acd4b0e959aa,0xbfe624d5d4f9b9a6,2 +np.float64,0x7fca410a0f348213,0x7ff0000000000000,2 +np.float64,0xbfde368f77bc6d1e,0xbfdf5910c8c8bcb0,2 +np.float64,0xbfed7412937ae825,0xbff0e55afc428453,2 +np.float64,0xffef6b7b607ed6f6,0xfff0000000000000,2 +np.float64,0xbfb936f17e326de0,0xbfb941629a53c694,2 +np.float64,0x800dbb0c469b7619,0x800dbb0c469b7619,2 +np.float64,0x800f68b0581ed161,0x800f68b0581ed161,2 +np.float64,0x3fe25b2aad64b656,0x3fe361266fa9c5eb,2 +np.float64,0xbfb87e445a30fc88,0xbfb887d676910c3f,2 +np.float64,0x6e6ba9b6dcd76,0x6e6ba9b6dcd76,2 +np.float64,0x3fad27ce583a4f9d,0x3fad2bd72782ffdb,2 +np.float64,0xbfec0bc5d638178c,0xbfefc6e8c8f9095f,2 +np.float64,0x7fcba4a296374944,0x7ff0000000000000,2 +np.float64,0x8004ca237cc99448,0x8004ca237cc99448,2 +np.float64,0xffe85b8c3270b718,0xfff0000000000000,2 +np.float64,0x7fe7ee3eddafdc7d,0x7ff0000000000000,2 +np.float64,0xffd275967ca4eb2c,0xfff0000000000000,2 +np.float64,0xbfa95bc3a032b780,0xbfa95e6b288ecf43,2 +np.float64,0x3fc9e3214b33c643,0x3fca10667e7e7ff4,2 +np.float64,0x8001b89c5d837139,0x8001b89c5d837139,2 +np.float64,0xbf8807dfc0300fc0,0xbf880803e3badfbd,2 +np.float64,0x800aca94b895952a,0x800aca94b895952a,2 +np.float64,0x7fd79534a02f2a68,0x7ff0000000000000,2 +np.float64,0x3fe1b81179e37023,0x3fe2a371d8cc26f0,2 +np.float64,0x800699539d6d32a8,0x800699539d6d32a8,2 +np.float64,0xffe51dfbb3aa3bf7,0xfff0000000000000,2 +np.float64,0xbfdfb775abbf6eec,0xbfe083f48be2f98f,2 +np.float64,0x3fe87979d7b0f2f4,0x3feaee701d959079,2 +np.float64,0x3fd8e4e6a731c9cd,0x3fd986d29f25f982,2 +np.float64,0x3fe3dadaaf67b5b6,0x3fe527520fb02920,2 +np.float64,0x8003c2262bc7844d,0x8003c2262bc7844d,2 +np.float64,0x800c930add392616,0x800c930add392616,2 +np.float64,0xffb7a152a22f42a8,0xfff0000000000000,2 +np.float64,0x80028fe03dc51fc1,0x80028fe03dc51fc1,2 +np.float64,0xffe32ae60c6655cc,0xfff0000000000000,2 +np.float64,0x3fea3527e4746a50,0x3fed3cbbf47f18eb,2 +np.float64,0x800a53059e14a60c,0x800a53059e14a60c,2 +np.float64,0xbfd79e3b202f3c76,0xbfd828672381207b,2 +np.float64,0xffeed7e2eb7dafc5,0xfff0000000000000,2 +np.float64,0x3fec51ed6778a3db,0x3ff01509e34df61d,2 +np.float64,0xbfd84bc577b0978a,0xbfd8e23ec55e42e8,2 +np.float64,0x2483aff849077,0x2483aff849077,2 +np.float64,0x6f57883adeaf2,0x6f57883adeaf2,2 +np.float64,0xffd3fd74d927faea,0xfff0000000000000,2 +np.float64,0x7fca49ec773493d8,0x7ff0000000000000,2 +np.float64,0x7fd08fe2e8211fc5,0x7ff0000000000000,2 +np.float64,0x800852086db0a411,0x800852086db0a411,2 +np.float64,0x3fe5b1f2c9eb63e6,0x3fe7654f511bafc6,2 +np.float64,0xbfe01e2a58e03c54,0xbfe0cedb68f021e6,2 +np.float64,0x800988421d331085,0x800988421d331085,2 +np.float64,0xffd5038b18aa0716,0xfff0000000000000,2 +np.float64,0x8002c9264c85924d,0x8002c9264c85924d,2 +np.float64,0x3fd21ca302243946,0x3fd25ac653a71aab,2 +np.float64,0xbfea60d6e6f4c1ae,0xbfed78031d9dfa2b,2 +np.float64,0xffef97b6263f2f6b,0xfff0000000000000,2 +np.float64,0xbfd524732faa48e6,0xbfd5876ecc415dcc,2 +np.float64,0x660387e8cc072,0x660387e8cc072,2 +np.float64,0x7fcfc108a33f8210,0x7ff0000000000000,2 +np.float64,0x7febe5b0f877cb61,0x7ff0000000000000,2 +np.float64,0xbfa55fdfac2abfc0,0xbfa56176991851a8,2 +np.float64,0x25250f4c4a4a3,0x25250f4c4a4a3,2 +np.float64,0xffe2f6a2f2a5ed46,0xfff0000000000000,2 +np.float64,0x7fa754fcc02ea9f9,0x7ff0000000000000,2 +np.float64,0x3febd19dea37a33c,0x3fef75279f75d3b8,2 +np.float64,0xc5ed55218bdab,0xc5ed55218bdab,2 +np.float64,0x3fe72ff6b3ee5fed,0x3fe945388b979882,2 +np.float64,0xbfe16b854e22d70a,0xbfe24b10fc0dff14,2 +np.float64,0xffb22cbe10245980,0xfff0000000000000,2 +np.float64,0xa54246b54a849,0xa54246b54a849,2 +np.float64,0x3fe7f4cda76fe99c,0x3fea41edc74888b6,2 +np.float64,0x1,0x1,2 +np.float64,0x800d84acce9b095a,0x800d84acce9b095a,2 +np.float64,0xb0eef04761dde,0xb0eef04761dde,2 +np.float64,0x7ff4000000000000,0x7ffc000000000000,2 +np.float64,0xffecaf1dbb795e3b,0xfff0000000000000,2 +np.float64,0x90dbab8d21b76,0x90dbab8d21b76,2 +np.float64,0x3fe79584a9ef2b09,0x3fe9c71fa9e40eb5,2 diff --git a/numpy/core/tests/data/umath-validation-set-tan.csv b/numpy/core/tests/data/umath-validation-set-tan.csv new file mode 100644 index 000000000000..083cdb2fbc51 --- /dev/null +++ b/numpy/core/tests/data/umath-validation-set-tan.csv @@ -0,0 +1,1429 @@ +dtype,input,output,ulperrortol +np.float32,0xfd97ece0,0xc11186e9,4 +np.float32,0x8013bb34,0x8013bb34,4 +np.float32,0x316389,0x316389,4 +np.float32,0x7f7fffff,0xbf1c9eca,4 +np.float32,0x3f7674bb,0x3fb7e450,4 +np.float32,0x80800000,0x80800000,4 +np.float32,0x7f5995e8,0xbf94106c,4 +np.float32,0x74527,0x74527,4 +np.float32,0x7f08caea,0xbeceddb6,4 +np.float32,0x2d49b2,0x2d49b2,4 +np.float32,0x3f74e5e4,0x3fb58695,4 +np.float32,0x3f3fcd51,0x3f6e1e81,4 +np.float32,0xbf4f3608,0xbf864d3d,4 +np.float32,0xbed974a0,0xbee78c70,4 +np.float32,0xff5f483c,0x3ecf3cb2,4 +np.float32,0x7f4532f4,0xc0b96f7b,4 +np.float32,0x3f0a4f7c,0x3f198cc0,4 +np.float32,0x210193,0x210193,4 +np.float32,0xfeebad7a,0xbf92eba8,4 +np.float32,0xfed29f74,0xc134cab6,4 +np.float32,0x803433a0,0x803433a0,4 +np.float32,0x64eb46,0x64eb46,4 +np.float32,0xbf54ef22,0xbf8c757b,4 +np.float32,0x3f3d5fdd,0x3f69a17b,4 +np.float32,0x80000001,0x80000001,4 +np.float32,0x800a837a,0x800a837a,4 +np.float32,0x6ff0be,0x6ff0be,4 +np.float32,0xfe8f1186,0x3f518820,4 +np.float32,0x804963e5,0x804963e5,4 +np.float32,0xfebaa59a,0x3fa1dbb0,4 +np.float32,0x637970,0x637970,4 +np.float32,0x3e722a6b,0x3e76c89a,4 +np.float32,0xff2b0478,0xbddccb5f,4 +np.float32,0xbf7bd85b,0xbfc06821,4 +np.float32,0x3ec33600,0x3ecd4126,4 +np.float32,0x3e0a43b9,0x3e0b1c69,4 +np.float32,0x7f7511b6,0xbe427083,4 +np.float32,0x3f28c114,0x3f465a73,4 +np.float32,0x3f179e1c,0x3f2c3e7c,4 +np.float32,0x7b2963,0x7b2963,4 +np.float32,0x3f423d06,0x3f72b442,4 +np.float32,0x3f5a24c6,0x3f925508,4 +np.float32,0xff18c834,0xbf79b5c8,4 +np.float32,0x3f401ece,0x3f6eb6ac,4 +np.float32,0x7b8a3013,0xbffab968,4 +np.float32,0x80091ff0,0x80091ff0,4 +np.float32,0x3f389c51,0x3f610b47,4 +np.float32,0x5ea174,0x5ea174,4 +np.float32,0x807a9eb2,0x807a9eb2,4 +np.float32,0x806ce61e,0x806ce61e,4 +np.float32,0xbe956acc,0xbe99cefc,4 +np.float32,0x7e60e247,0xbf5e64a5,4 +np.float32,0x7f398e24,0x404d12ed,4 +np.float32,0x3d9049f8,0x3d908735,4 +np.float32,0x7db17ffc,0xbf5b3d87,4 +np.float32,0xff453f78,0xc0239c9f,4 +np.float32,0x3f024aac,0x3f0ed802,4 +np.float32,0xbe781c30,0xbe7d1508,4 +np.float32,0x3f77962a,0x3fb9a28e,4 +np.float32,0xff7fffff,0x3f1c9eca,4 +np.float32,0x3f7152e3,0x3fb03f9d,4 +np.float32,0xff7cb167,0x3f9ce831,4 +np.float32,0x3e763e30,0x3e7b1a10,4 +np.float32,0xbf126527,0xbf24c253,4 +np.float32,0x803f6660,0x803f6660,4 +np.float32,0xbf79de38,0xbfbd38b1,4 +np.float32,0x8046c2f0,0x8046c2f0,4 +np.float32,0x6dc74e,0x6dc74e,4 +np.float32,0xbec9c45e,0xbed4e768,4 +np.float32,0x3f0eedb6,0x3f1fe610,4 +np.float32,0x7e031999,0xbcc13026,4 +np.float32,0x7efc2fd7,0x41e4b284,4 +np.float32,0xbeab7454,0xbeb22a1b,4 +np.float32,0x805ee67b,0x805ee67b,4 +np.float32,0x7f76e58e,0xc2436659,4 +np.float32,0xbe62b024,0xbe667718,4 +np.float32,0x3eea0808,0x3efbd182,4 +np.float32,0xbf7fd00c,0xbfc70719,4 +np.float32,0x7f27b640,0xbf0d97e0,4 +np.float32,0x3f1b58a4,0x3f31b6f4,4 +np.float32,0x252a9f,0x252a9f,4 +np.float32,0x7f65f95a,0xbead5de3,4 +np.float32,0xfc6ea780,0x42d15801,4 +np.float32,0x7eac4c52,0xc0682424,4 +np.float32,0xbe8a3f5a,0xbe8db54d,4 +np.float32,0xbf1644e2,0xbf2a4abd,4 +np.float32,0x3fc96a,0x3fc96a,4 +np.float32,0x7f38c0e4,0x3cc04af8,4 +np.float32,0x3f623d75,0x3f9c065d,4 +np.float32,0x3ee6a51a,0x3ef7a058,4 +np.float32,0x3dd11020,0x3dd1cacf,4 +np.float32,0xb6918,0xb6918,4 +np.float32,0xfdd7a540,0x3f22f081,4 +np.float32,0x80798563,0x80798563,4 +np.float32,0x3e9a8b7a,0x3e9f6a7e,4 +np.float32,0xbea515d4,0xbeab0df5,4 +np.float32,0xbea9b9f4,0xbeb03abe,4 +np.float32,0xbf11a5fa,0xbf23b478,4 +np.float32,0xfd6cadf0,0xbfa2a878,4 +np.float32,0xbf6edd07,0xbfacbb78,4 +np.float32,0xff5c5328,0x3e2d1552,4 +np.float32,0xbea2f788,0xbea8b3f5,4 +np.float32,0x802efaeb,0x802efaeb,4 +np.float32,0xff1c85e5,0x41f8560e,4 +np.float32,0x3f53b123,0x3f8b18e1,4 +np.float32,0xff798c4a,0x4092e66f,4 +np.float32,0x7f2e6fe7,0xbdcbd58f,4 +np.float32,0xfe8a8196,0x3fd7fc56,4 +np.float32,0x5e7ad4,0x5e7ad4,4 +np.float32,0xbf23a02d,0xbf3e4533,4 +np.float32,0x3f31c55c,0x3f5531bf,4 +np.float32,0x80331be3,0x80331be3,4 +np.float32,0x8056960a,0x8056960a,4 +np.float32,0xff1c06ae,0xbfd26992,4 +np.float32,0xbe0cc4b0,0xbe0da96c,4 +np.float32,0x7e925ad5,0xbf8dba54,4 +np.float32,0x2c8cec,0x2c8cec,4 +np.float32,0x8011951e,0x8011951e,4 +np.float32,0x3f2caf84,0x3f4cb89f,4 +np.float32,0xbd32c220,0xbd32df33,4 +np.float32,0xbec358d6,0xbecd6996,4 +np.float32,0x3f6e4930,0x3fabeb92,4 +np.float32,0xbf6a3afd,0xbfa65a3a,4 +np.float32,0x80067764,0x80067764,4 +np.float32,0x3d8df1,0x3d8df1,4 +np.float32,0x7ee51cf2,0x409e4061,4 +np.float32,0x435f5d,0x435f5d,4 +np.float32,0xbf5b17f7,0xbf936ebe,4 +np.float32,0x3ecaacb5,0x3ed5f81f,4 +np.float32,0x807b0aa5,0x807b0aa5,4 +np.float32,0x52b40b,0x52b40b,4 +np.float32,0x146a97,0x146a97,4 +np.float32,0x7f42b952,0xbfdcb413,4 +np.float32,0xbf1a1af2,0xbf2fe1bb,4 +np.float32,0x3f312034,0x3f541aa2,4 +np.float32,0x3f281d60,0x3f4554f9,4 +np.float32,0x50e451,0x50e451,4 +np.float32,0xbe45838c,0xbe480016,4 +np.float32,0xff7d0aeb,0x3eb0746e,4 +np.float32,0x7f32a489,0xbf96af6d,4 +np.float32,0xbf1b4e27,0xbf31a769,4 +np.float32,0x3f242936,0x3f3f1a44,4 +np.float32,0xbf7482ff,0xbfb4f201,4 +np.float32,0x4bda38,0x4bda38,4 +np.float32,0xbf022208,0xbf0ea2bb,4 +np.float32,0x7d08ca95,0xbe904602,4 +np.float32,0x7ed2f356,0xc02b55ad,4 +np.float32,0xbf131204,0xbf25b734,4 +np.float32,0xff3464b4,0x3fb23706,4 +np.float32,0x5a97cf,0x5a97cf,4 +np.float32,0xbe52db70,0xbe55e388,4 +np.float32,0x3f52934f,0x3f89e2aa,4 +np.float32,0xfeea866a,0x40a2b33f,4 +np.float32,0x80333925,0x80333925,4 +np.float32,0xfef5d13e,0xc00139ec,4 +np.float32,0x3f4750ab,0x3f7c87ad,4 +np.float32,0x3e41bfdd,0x3e44185a,4 +np.float32,0xbf5b0572,0xbf935935,4 +np.float32,0xbe93c9da,0xbe9808d8,4 +np.float32,0x7f501f33,0xc0f9973c,4 +np.float32,0x800af035,0x800af035,4 +np.float32,0x3f29faf8,0x3f4852a8,4 +np.float32,0xbe1e4c20,0xbe1f920c,4 +np.float32,0xbf7e8616,0xbfc4d79d,4 +np.float32,0x43ffbf,0x43ffbf,4 +np.float32,0x7f28e8a9,0xbfa1ac24,4 +np.float32,0xbf1f9f92,0xbf3820bc,4 +np.float32,0x3f07e004,0x3f1641c4,4 +np.float32,0x3ef7ea7f,0x3f06a64a,4 +np.float32,0x7e013101,0x3f6080e6,4 +np.float32,0x7f122a4f,0xbf0a796f,4 +np.float32,0xfe096960,0x3ed7273a,4 +np.float32,0x3f06abf1,0x3f14a4b2,4 +np.float32,0x3e50ded3,0x3e53d0f1,4 +np.float32,0x7f50b346,0x3eabb536,4 +np.float32,0xff5adb0f,0xbd441972,4 +np.float32,0xbecefe46,0xbedb0f66,4 +np.float32,0x7da70bd4,0xbec66273,4 +np.float32,0x169811,0x169811,4 +np.float32,0xbee4dfee,0xbef5721a,4 +np.float32,0x3efbeae3,0x3f0936e6,4 +np.float32,0x8031bd61,0x8031bd61,4 +np.float32,0x8048e443,0x8048e443,4 +np.float32,0xff209aa6,0xbeb364cb,4 +np.float32,0xff477499,0x3c1b0041,4 +np.float32,0x803fe929,0x803fe929,4 +np.float32,0x3f70158b,0x3fae7725,4 +np.float32,0x7f795723,0x3e8e850a,4 +np.float32,0x3cba99,0x3cba99,4 +np.float32,0x80588d2a,0x80588d2a,4 +np.float32,0x805d1f05,0x805d1f05,4 +np.float32,0xff4ac09a,0xbefe614d,4 +np.float32,0x804af084,0x804af084,4 +np.float32,0x7c64ae63,0xc1a8b563,4 +np.float32,0x8078d793,0x8078d793,4 +np.float32,0x7f3e2436,0xbf8bf9d3,4 +np.float32,0x7ccec1,0x7ccec1,4 +np.float32,0xbf6462c7,0xbf9eb830,4 +np.float32,0x3f1002ca,0x3f216843,4 +np.float32,0xfe878ca6,0x409e73a5,4 +np.float32,0x3bd841d9,0x3bd842a7,4 +np.float32,0x7d406f41,0xbd9dcfa3,4 +np.float32,0x7c6d6,0x7c6d6,4 +np.float32,0x3f4ef360,0x3f86074b,4 +np.float32,0x805f534a,0x805f534a,4 +np.float32,0x1,0x1,4 +np.float32,0x3f739ee2,0x3fb39db2,4 +np.float32,0x3d0c2352,0x3d0c3153,4 +np.float32,0xfe8a4f2c,0x3edd8add,4 +np.float32,0x3e52eaa0,0x3e55f362,4 +np.float32,0x7bde9758,0xbf5ba5cf,4 +np.float32,0xff422654,0xbf41e487,4 +np.float32,0x385e5b,0x385e5b,4 +np.float32,0x5751dd,0x5751dd,4 +np.float32,0xff6c671c,0xc03e2d6d,4 +np.float32,0x1458be,0x1458be,4 +np.float32,0x80153d4d,0x80153d4d,4 +np.float32,0x7efd2adb,0x3e25458f,4 +np.float32,0xbe161880,0xbe172e12,4 +np.float32,0x7ecea1aa,0x40a66d79,4 +np.float32,0xbf5b02a2,0xbf9355f0,4 +np.float32,0x15d9ab,0x15d9ab,4 +np.float32,0x2dc7c7,0x2dc7c7,4 +np.float32,0xfebbf81a,0x4193f6e6,4 +np.float32,0xfe8e3594,0xc00a6695,4 +np.float32,0x185aa8,0x185aa8,4 +np.float32,0x3daea156,0x3daf0e00,4 +np.float32,0x3e071688,0x3e07e08e,4 +np.float32,0x802db9e6,0x802db9e6,4 +np.float32,0x7f7be2c4,0x3f1363dd,4 +np.float32,0x7eba3f5e,0xc13eb497,4 +np.float32,0x3de04a00,0x3de130a9,4 +np.float32,0xbf1022bc,0xbf2194eb,4 +np.float32,0xbf5b547e,0xbf93b53b,4 +np.float32,0x3e867bd6,0x3e89aa10,4 +np.float32,0xbea5eb5c,0xbeabfb73,4 +np.float32,0x7f1efae9,0x3ffca038,4 +np.float32,0xff5d0344,0xbe55dbbb,4 +np.float32,0x805167e7,0x805167e7,4 +np.float32,0xbdb3a020,0xbdb41667,4 +np.float32,0xbedea6b4,0xbeedd5fd,4 +np.float32,0x8053b45c,0x8053b45c,4 +np.float32,0x7ed370e9,0x3d90eba5,4 +np.float32,0xbefcd7da,0xbf09cf91,4 +np.float32,0x78b9ac,0x78b9ac,4 +np.float32,0xbf2f6dc0,0xbf5141ef,4 +np.float32,0x802d3a7b,0x802d3a7b,4 +np.float32,0xfd45d120,0x3fec31cc,4 +np.float32,0xbf7e7020,0xbfc4b2af,4 +np.float32,0xf04da,0xf04da,4 +np.float32,0xbe9819d4,0xbe9cbd35,4 +np.float32,0x8075ab35,0x8075ab35,4 +np.float32,0xbf052fdc,0xbf12aa2c,4 +np.float32,0x3f1530d0,0x3f28bd9f,4 +np.float32,0x80791881,0x80791881,4 +np.float32,0x67f309,0x67f309,4 +np.float32,0x3f12f16a,0x3f2588f5,4 +np.float32,0x3ecdac47,0x3ed97ff8,4 +np.float32,0xbf297fb7,0xbf478c39,4 +np.float32,0x8069fa80,0x8069fa80,4 +np.float32,0x807f940e,0x807f940e,4 +np.float32,0xbf648dc8,0xbf9eeecb,4 +np.float32,0x3de873b0,0x3de9748d,4 +np.float32,0x3f1aa645,0x3f30af1f,4 +np.float32,0xff227a62,0x3d8283cc,4 +np.float32,0xbf37187d,0xbf5e5f4c,4 +np.float32,0x803b1b1f,0x803b1b1f,4 +np.float32,0x3f58142a,0x3f8ff8da,4 +np.float32,0x8004339e,0x8004339e,4 +np.float32,0xbf0f5654,0xbf2077a4,4 +np.float32,0x3f17e509,0x3f2ca598,4 +np.float32,0x3f800000,0x3fc75923,4 +np.float32,0xfdf79980,0x42f13047,4 +np.float32,0x7f111381,0x3f13c4c9,4 +np.float32,0xbea40c70,0xbea9e724,4 +np.float32,0x110520,0x110520,4 +np.float32,0x60490d,0x60490d,4 +np.float32,0x3f6703ec,0x3fa21951,4 +np.float32,0xbf098256,0xbf187652,4 +np.float32,0x658951,0x658951,4 +np.float32,0x3f53bf16,0x3f8b2818,4 +np.float32,0xff451811,0xc0026068,4 +np.float32,0x80777ee0,0x80777ee0,4 +np.float32,0x3e4fcc19,0x3e52b286,4 +np.float32,0x7f387ee0,0x3ce93eb6,4 +np.float32,0xff51181f,0xbfca3ee4,4 +np.float32,0xbf5655ae,0xbf8e0304,4 +np.float32,0xff2f1dcd,0x40025471,4 +np.float32,0x7f6e58e5,0xbe9930d5,4 +np.float32,0x7adf11,0x7adf11,4 +np.float32,0xbe9a2bc2,0xbe9f0185,4 +np.float32,0x8065d3a0,0x8065d3a0,4 +np.float32,0x3ed6e826,0x3ee47c45,4 +np.float32,0x80598ea0,0x80598ea0,4 +np.float32,0x7f10b90a,0x40437bd0,4 +np.float32,0x27b447,0x27b447,4 +np.float32,0x7ecd861c,0x3fce250f,4 +np.float32,0x0,0x0,4 +np.float32,0xbeba82d6,0xbec3394c,4 +np.float32,0xbf4958b0,0xbf8048ea,4 +np.float32,0x7c643e,0x7c643e,4 +np.float32,0x580770,0x580770,4 +np.float32,0x805bf54a,0x805bf54a,4 +np.float32,0x7f1f3cee,0xbe1a54d6,4 +np.float32,0xfefefdea,0x3fa84576,4 +np.float32,0x7f007b7a,0x3e8a6d25,4 +np.float32,0xbf177959,0xbf2c0919,4 +np.float32,0xbf30fda0,0xbf53e058,4 +np.float32,0x3f0576be,0x3f130861,4 +np.float32,0x3f49380e,0x3f80283a,4 +np.float32,0xebc56,0xebc56,4 +np.float32,0x654e3b,0x654e3b,4 +np.float32,0x14a4d8,0x14a4d8,4 +np.float32,0xff69b3cb,0xbf822a88,4 +np.float32,0xbe9b6c1c,0xbea06109,4 +np.float32,0xbefddd7e,0xbf0a787b,4 +np.float32,0x4c4ebb,0x4c4ebb,4 +np.float32,0x7d0a74,0x7d0a74,4 +np.float32,0xbebb5f80,0xbec43635,4 +np.float32,0x7ee79723,0xc1c7f3f3,4 +np.float32,0x7f2be4c7,0xbfa6c693,4 +np.float32,0x805bc7d5,0x805bc7d5,4 +np.float32,0x8042f12c,0x8042f12c,4 +np.float32,0x3ef91be8,0x3f07697b,4 +np.float32,0x3cf37ac0,0x3cf38d1c,4 +np.float32,0x800000,0x800000,4 +np.float32,0xbe1ebf4c,0xbe200806,4 +np.float32,0x7f380862,0xbeb512e8,4 +np.float32,0xbe320064,0xbe33d0fc,4 +np.float32,0xff300b0c,0xbfadb805,4 +np.float32,0x308a06,0x308a06,4 +np.float32,0xbf084f6e,0xbf16d7b6,4 +np.float32,0xff47cab6,0x3f892b65,4 +np.float32,0xbed99f4a,0xbee7bfd5,4 +np.float32,0xff7d74c0,0x3ee88c9a,4 +np.float32,0x3c3d23,0x3c3d23,4 +np.float32,0x8074bde8,0x8074bde8,4 +np.float32,0x80042164,0x80042164,4 +np.float32,0x3e97c92a,0x3e9c6500,4 +np.float32,0x3b80e0,0x3b80e0,4 +np.float32,0xbf16646a,0xbf2a783d,4 +np.float32,0x7f3b4cb1,0xc01339be,4 +np.float32,0xbf31f36e,0xbf557fd0,4 +np.float32,0x7f540618,0xbe5f6fc1,4 +np.float32,0x7eee47d0,0x40a27e94,4 +np.float32,0x7f12f389,0xbebed654,4 +np.float32,0x56cff5,0x56cff5,4 +np.float32,0x8056032b,0x8056032b,4 +np.float32,0x3ed34e40,0x3ee02e38,4 +np.float32,0x7d51a908,0xbf19a90e,4 +np.float32,0x80000000,0x80000000,4 +np.float32,0xfdf73fd0,0xbf0f8cad,4 +np.float32,0x7ee4fe6d,0xbf1ea7e4,4 +np.float32,0x1f15ba,0x1f15ba,4 +np.float32,0xd18c3,0xd18c3,4 +np.float32,0x80797705,0x80797705,4 +np.float32,0x7ef07091,0x3f2f3b9a,4 +np.float32,0x7f552f41,0x3faf608c,4 +np.float32,0x3f779977,0x3fb9a7ad,4 +np.float32,0xfe1a7a50,0xbdadc4d1,4 +np.float32,0xbf449cf0,0xbf7740db,4 +np.float32,0xbe44e620,0xbe475cad,4 +np.float32,0x3f63a098,0x3f9dc2b5,4 +np.float32,0xfed40a12,0x4164533a,4 +np.float32,0x7a2bbb,0x7a2bbb,4 +np.float32,0xff7f7b9e,0xbeee8740,4 +np.float32,0x7ee27f8b,0x4233f53b,4 +np.float32,0xbf044c06,0xbf117c28,4 +np.float32,0xbeffde54,0xbf0bc49f,4 +np.float32,0xfeaef2e8,0x3ff258fe,4 +np.float32,0x527451,0x527451,4 +np.float32,0xbcef8d00,0xbcef9e7c,4 +np.float32,0xbf0e20c0,0xbf1ec9b2,4 +np.float32,0x8024afda,0x8024afda,4 +np.float32,0x7ef6cb3e,0x422cad0b,4 +np.float32,0x3c120,0x3c120,4 +np.float32,0xbf125c8f,0xbf24b62c,4 +np.float32,0x7e770a93,0x402c9d86,4 +np.float32,0xbd30a4e0,0xbd30c0ee,4 +np.float32,0xbf4d3388,0xbf843530,4 +np.float32,0x3f529072,0x3f89df92,4 +np.float32,0xff0270b1,0xbf81be9a,4 +np.float32,0x5e07e7,0x5e07e7,4 +np.float32,0x7bec32,0x7bec32,4 +np.float32,0x7fc00000,0x7fc00000,4 +np.float32,0x3e3ba5e0,0x3e3dc6e9,4 +np.float32,0x3ecb62d4,0x3ed6ce2c,4 +np.float32,0x3eb3dde8,0x3ebba68f,4 +np.float32,0x8063f952,0x8063f952,4 +np.float32,0x7f204aeb,0x3e88614e,4 +np.float32,0xbeae1ddc,0xbeb5278e,4 +np.float32,0x6829e9,0x6829e9,4 +np.float32,0xbf361a99,0xbf5ca354,4 +np.float32,0xbf24fbe6,0xbf406326,4 +np.float32,0x3f329d41,0x3f56a061,4 +np.float32,0xfed6d666,0x3e8f71a5,4 +np.float32,0x337f92,0x337f92,4 +np.float32,0xbe1c4970,0xbe1d8305,4 +np.float32,0xbe6b7e18,0xbe6fbbde,4 +np.float32,0x3f2267b9,0x3f3c61da,4 +np.float32,0xbee1ee94,0xbef1d628,4 +np.float32,0x7ecffc1a,0x3f02987e,4 +np.float32,0xbe9b1306,0xbe9fff3b,4 +np.float32,0xbeffacae,0xbf0ba468,4 +np.float32,0x7f800000,0xffc00000,4 +np.float32,0xfefc9aa8,0xc19de2a3,4 +np.float32,0x7d7185bb,0xbf9090ec,4 +np.float32,0x7edfbafd,0x3fe9352f,4 +np.float32,0x4ef2ec,0x4ef2ec,4 +np.float32,0x7f4cab2e,0xbff4e5dd,4 +np.float32,0xff3b1788,0x3e3c22e9,4 +np.float32,0x4e15ee,0x4e15ee,4 +np.float32,0xbf5451e6,0xbf8bc8a7,4 +np.float32,0x3f7f6d2e,0x3fc65e8b,4 +np.float32,0xbf1d9184,0xbf35071b,4 +np.float32,0xbf3a81cf,0xbf646d9b,4 +np.float32,0xbe71acc4,0xbe7643ab,4 +np.float32,0x528b7d,0x528b7d,4 +np.float32,0x2cb1d0,0x2cb1d0,4 +np.float32,0x3f324bf8,0x3f56161a,4 +np.float32,0x80709a21,0x80709a21,4 +np.float32,0x4bc448,0x4bc448,4 +np.float32,0x3e8bd600,0x3e8f6b7a,4 +np.float32,0xbeb97d30,0xbec20dd6,4 +np.float32,0x2a5669,0x2a5669,4 +np.float32,0x805f2689,0x805f2689,4 +np.float32,0xfe569f50,0x3fc51952,4 +np.float32,0x1de44c,0x1de44c,4 +np.float32,0x3ec7036c,0x3ed1ae67,4 +np.float32,0x8052b8e5,0x8052b8e5,4 +np.float32,0xff740a6b,0x3f4981a8,4 +np.float32,0xfee9bb70,0xc05e23be,4 +np.float32,0xff4e12c9,0x4002b4ad,4 +np.float32,0x803de0c2,0x803de0c2,4 +np.float32,0xbf433a07,0xbf74966f,4 +np.float32,0x803e60ca,0x803e60ca,4 +np.float32,0xbf19ee98,0xbf2fa07a,4 +np.float32,0x92929,0x92929,4 +np.float32,0x7f709c27,0x4257ba2d,4 +np.float32,0x803167c6,0x803167c6,4 +np.float32,0xbf095ead,0xbf184607,4 +np.float32,0x617060,0x617060,4 +np.float32,0x2d85b3,0x2d85b3,4 +np.float32,0x53d20b,0x53d20b,4 +np.float32,0x3e046838,0x3e052666,4 +np.float32,0xbe7c5fdc,0xbe80ce4b,4 +np.float32,0x3d18d060,0x3d18e289,4 +np.float32,0x804dc031,0x804dc031,4 +np.float32,0x3f224166,0x3f3c26cd,4 +np.float32,0x7d683e3c,0xbea24f25,4 +np.float32,0xbf3a92aa,0xbf648be4,4 +np.float32,0x8072670b,0x8072670b,4 +np.float32,0xbe281aec,0xbe29a1bc,4 +np.float32,0x7f09d918,0xc0942490,4 +np.float32,0x7ca9fd07,0x4018b990,4 +np.float32,0x7d36ac5d,0x3cf57184,4 +np.float32,0x8039b62f,0x8039b62f,4 +np.float32,0x6cad7b,0x6cad7b,4 +np.float32,0x3c0fd9ab,0x3c0fda9d,4 +np.float32,0x80299883,0x80299883,4 +np.float32,0x3c2d0e3e,0x3c2d0fe4,4 +np.float32,0x8002cf62,0x8002cf62,4 +np.float32,0x801dde97,0x801dde97,4 +np.float32,0x80411856,0x80411856,4 +np.float32,0x6ebce8,0x6ebce8,4 +np.float32,0x7b7d1a,0x7b7d1a,4 +np.float32,0x8031d3de,0x8031d3de,4 +np.float32,0x8005c4ab,0x8005c4ab,4 +np.float32,0xbf7dd803,0xbfc3b3ef,4 +np.float32,0x8017ae60,0x8017ae60,4 +np.float32,0xfe9316ce,0xbfe0544a,4 +np.float32,0x3f136bfe,0x3f2636ff,4 +np.float32,0x3df87b80,0x3df9b57d,4 +np.float32,0xff44c356,0xbf11c7ad,4 +np.float32,0x4914ae,0x4914ae,4 +np.float32,0x80524c21,0x80524c21,4 +np.float32,0x805c7dc8,0x805c7dc8,4 +np.float32,0xfed3c0aa,0xbff0c0ab,4 +np.float32,0x7eb2bfbb,0xbf4600bc,4 +np.float32,0xfec8df84,0x3f5bd350,4 +np.float32,0x3e5431a4,0x3e5748c3,4 +np.float32,0xbee6a3a0,0xbef79e86,4 +np.float32,0xbf6cc9b2,0xbfa9d61a,4 +np.float32,0x3f132bd5,0x3f25dbd9,4 +np.float32,0x7e6d2e48,0x3f9d025b,4 +np.float32,0x3edf430c,0x3eee942d,4 +np.float32,0x3f0d1b8a,0x3f1d60e1,4 +np.float32,0xbdf2f688,0xbdf41bfb,4 +np.float32,0xbe47a284,0xbe4a33ff,4 +np.float32,0x3eaa9fbc,0x3eb13be7,4 +np.float32,0xfe98d45e,0x3eb84517,4 +np.float32,0x7efc23b3,0x3dcc1c99,4 +np.float32,0x3ca36242,0x3ca367ce,4 +np.float32,0x3f76a944,0x3fb834e3,4 +np.float32,0xbf45207c,0xbf783f9b,4 +np.float32,0x3e7c1220,0x3e80a4f8,4 +np.float32,0x3f018200,0x3f0dd14e,4 +np.float32,0x3f53cdde,0x3f8b3839,4 +np.float32,0xbdbacb58,0xbdbb5063,4 +np.float32,0x804af68d,0x804af68d,4 +np.float32,0x3e2c12fc,0x3e2db65b,4 +np.float32,0x3f039433,0x3f10895a,4 +np.float32,0x7ef5193d,0x3f4115f7,4 +np.float32,0x8030afbe,0x8030afbe,4 +np.float32,0x3f06fa2a,0x3f150d5d,4 +np.float32,0x3f124442,0x3f2493d2,4 +np.float32,0xbeb5b792,0xbebdc090,4 +np.float32,0xbedc90a4,0xbeeb4de9,4 +np.float32,0x3f3ff8,0x3f3ff8,4 +np.float32,0x3ee75bc5,0x3ef881e4,4 +np.float32,0xfe80e3de,0xbf5cd535,4 +np.float32,0xf52eb,0xf52eb,4 +np.float32,0x80660ee8,0x80660ee8,4 +np.float32,0x3e173a58,0x3e185648,4 +np.float32,0xfe49520c,0xbf728d7c,4 +np.float32,0xbecbb8ec,0xbed73373,4 +np.float32,0xbf027ae0,0xbf0f173e,4 +np.float32,0xbcab6740,0xbcab6da8,4 +np.float32,0xbf2a15e2,0xbf487e11,4 +np.float32,0x3b781b,0x3b781b,4 +np.float32,0x44f559,0x44f559,4 +np.float32,0xff6a0ca6,0xc174d7c3,4 +np.float32,0x6460ef,0x6460ef,4 +np.float32,0xfe58009c,0x3ee2bb30,4 +np.float32,0xfec3c038,0x3e30d617,4 +np.float32,0x7f0687c0,0xbf62c820,4 +np.float32,0xbf44655e,0xbf76d589,4 +np.float32,0xbf42968c,0xbf735e78,4 +np.float32,0x80385503,0x80385503,4 +np.float32,0xbea7e3a2,0xbeae2d59,4 +np.float32,0x3dd0b770,0x3dd17131,4 +np.float32,0xbf4bc185,0xbf82b907,4 +np.float32,0xfefd7d64,0xbee05650,4 +np.float32,0xfaac3c00,0xbff23bc9,4 +np.float32,0xbf562f0d,0xbf8dd7f4,4 +np.float32,0x7fa00000,0x7fe00000,4 +np.float32,0x3e01bdb8,0x3e027098,4 +np.float32,0x3e2868ab,0x3e29f19e,4 +np.float32,0xfec55f2e,0x3f39f304,4 +np.float32,0xed4e,0xed4e,4 +np.float32,0x3e2b7330,0x3e2d11fa,4 +np.float32,0x7f738542,0x40cbbe16,4 +np.float32,0x3f123521,0x3f247e71,4 +np.float32,0x73572c,0x73572c,4 +np.float32,0x804936c8,0x804936c8,4 +np.float32,0x803b80d8,0x803b80d8,4 +np.float32,0x7f566c57,0xbee2855a,4 +np.float32,0xff0e3bd8,0xbff0543f,4 +np.float32,0x7d2b2fe7,0xbf94ba4c,4 +np.float32,0xbf0da470,0xbf1e1dc2,4 +np.float32,0xbd276500,0xbd277ce0,4 +np.float32,0xfcd15dc0,0x403ccc2a,4 +np.float32,0x80071e59,0x80071e59,4 +np.float32,0xbe9b0c34,0xbe9ff7be,4 +np.float32,0x3f4f9069,0x3f86ac50,4 +np.float32,0x80042a95,0x80042a95,4 +np.float32,0x7de28e39,0x3bc9b7f4,4 +np.float32,0xbf641935,0xbf9e5af8,4 +np.float32,0x8034f068,0x8034f068,4 +np.float32,0xff33a3d2,0xbf408e75,4 +np.float32,0xbcc51540,0xbcc51efc,4 +np.float32,0xff6d1ddf,0x3ef58f0e,4 +np.float32,0xbf64dfc4,0xbf9f5725,4 +np.float32,0xff068a06,0x3eea8987,4 +np.float32,0xff01c0af,0x3f24cdfe,4 +np.float32,0x3f4def7e,0x3f84f802,4 +np.float32,0xbf1b4ae7,0xbf31a299,4 +np.float32,0x8077df2d,0x8077df2d,4 +np.float32,0x3f0155c5,0x3f0d9785,4 +np.float32,0x5a54b2,0x5a54b2,4 +np.float32,0x7f271f9e,0x3efb2ef3,4 +np.float32,0xbf0ff2ec,0xbf215217,4 +np.float32,0x7f500130,0xbf8a7fdd,4 +np.float32,0xfed9891c,0xbf65c872,4 +np.float32,0xfecbfaae,0x403bdbc2,4 +np.float32,0x3f3a5aba,0x3f642772,4 +np.float32,0x7ebc681e,0xbd8df059,4 +np.float32,0xfe05e400,0xbfe35d74,4 +np.float32,0xbf295ace,0xbf4750ea,4 +np.float32,0x7ea055b2,0x3f62d6be,4 +np.float32,0xbd00b520,0xbd00bff9,4 +np.float32,0xbf7677aa,0xbfb7e8cf,4 +np.float32,0x3e83f788,0x3e86f816,4 +np.float32,0x801f6710,0x801f6710,4 +np.float32,0x801133cc,0x801133cc,4 +np.float32,0x41da2a,0x41da2a,4 +np.float32,0xff1622fd,0x3f023650,4 +np.float32,0x806c7a72,0x806c7a72,4 +np.float32,0x3f10779c,0x3f220bb4,4 +np.float32,0xbf08cf94,0xbf17848d,4 +np.float32,0xbecb55b4,0xbed6bebd,4 +np.float32,0xbf0a1528,0xbf193d7b,4 +np.float32,0x806a16bd,0x806a16bd,4 +np.float32,0xc222a,0xc222a,4 +np.float32,0x3930de,0x3930de,4 +np.float32,0x3f5c3588,0x3f94bca2,4 +np.float32,0x1215ad,0x1215ad,4 +np.float32,0x3ed15030,0x3eddcf67,4 +np.float32,0x7da83b2e,0x3fce0d39,4 +np.float32,0x32b0a8,0x32b0a8,4 +np.float32,0x805aed6b,0x805aed6b,4 +np.float32,0x3ef8e02f,0x3f074346,4 +np.float32,0xbdeb6780,0xbdec7250,4 +np.float32,0x3f6e3cec,0x3fabda61,4 +np.float32,0xfefd467a,0x3ef7821a,4 +np.float32,0xfef090fe,0x3bb752a2,4 +np.float32,0x8019c538,0x8019c538,4 +np.float32,0x3e8cf284,0x3e909e81,4 +np.float32,0xbe6c6618,0xbe70b0a2,4 +np.float32,0x7f50a539,0x3f367be1,4 +np.float32,0x8019fe2f,0x8019fe2f,4 +np.float32,0x800c3f48,0x800c3f48,4 +np.float32,0xfd054cc0,0xc0f52802,4 +np.float32,0x3d0cca20,0x3d0cd853,4 +np.float32,0xbf4a7c44,0xbf816e74,4 +np.float32,0x3f46fc40,0x3f7be153,4 +np.float32,0x807c5849,0x807c5849,4 +np.float32,0xd7e41,0xd7e41,4 +np.float32,0x70589b,0x70589b,4 +np.float32,0x80357b95,0x80357b95,4 +np.float32,0x3de239f0,0x3de326a5,4 +np.float32,0x800b08e3,0x800b08e3,4 +np.float32,0x807ec946,0x807ec946,4 +np.float32,0x3e2e4b83,0x3e2fff76,4 +np.float32,0x3f198e0f,0x3f2f12a6,4 +np.float32,0xbecb1aca,0xbed67979,4 +np.float32,0x80134082,0x80134082,4 +np.float32,0x3f3a269f,0x3f63ca05,4 +np.float32,0x3f1381e4,0x3f265622,4 +np.float32,0xff293080,0xbf10be6f,4 +np.float32,0xff800000,0xffc00000,4 +np.float32,0x37d196,0x37d196,4 +np.float32,0x7e57eea7,0x3e7d8138,4 +np.float32,0x804b1dae,0x804b1dae,4 +np.float32,0x7d9508f9,0xc1075b35,4 +np.float32,0x3f7bf468,0x3fc095e0,4 +np.float32,0x55472c,0x55472c,4 +np.float32,0x3ecdcd86,0x3ed9a738,4 +np.float32,0x3ed9be0f,0x3ee7e4e9,4 +np.float32,0x3e7e0ddb,0x3e81b2fe,4 +np.float32,0x7ee6c1d3,0x3f850634,4 +np.float32,0x800f6fad,0x800f6fad,4 +np.float32,0xfefb3bd6,0xbff68ecc,4 +np.float32,0x8013d6e2,0x8013d6e2,4 +np.float32,0x3f3a2cb6,0x3f63d4ee,4 +np.float32,0xff383c84,0x3e7854bb,4 +np.float32,0x3f21946e,0x3f3b1cea,4 +np.float32,0xff322ea2,0x3fb22f31,4 +np.float32,0x8065a024,0x8065a024,4 +np.float32,0x7f395e30,0xbefe0de1,4 +np.float32,0x5b52db,0x5b52db,4 +np.float32,0x7f7caea7,0x3dac8ded,4 +np.float32,0xbf0431f8,0xbf1159b2,4 +np.float32,0x7f15b25b,0xc02a3833,4 +np.float32,0x80131abc,0x80131abc,4 +np.float32,0x7e829d81,0xbeb2e93d,4 +np.float32,0x3f2c64d7,0x3f4c3e4d,4 +np.float32,0x7f228d48,0xc1518c74,4 +np.float32,0xfc3c6f40,0xbf00d585,4 +np.float32,0x7f754f0f,0x3e2152f5,4 +np.float32,0xff65d32b,0xbe8bd56c,4 +np.float32,0xfea6b8c0,0x41608655,4 +np.float32,0x3f7d4b05,0x3fc2c96a,4 +np.float32,0x3f463230,0x3f7a54da,4 +np.float32,0x805117bb,0x805117bb,4 +np.float32,0xbf2ad4f7,0xbf49b30e,4 +np.float32,0x3eaa01ff,0x3eb08b56,4 +np.float32,0xff7a02bb,0x3f095f73,4 +np.float32,0x759176,0x759176,4 +np.float32,0x803c18d5,0x803c18d5,4 +np.float32,0xbe0722d8,0xbe07ed16,4 +np.float32,0x3f4b4a99,0x3f823fc6,4 +np.float32,0x3f7d0451,0x3fc25463,4 +np.float32,0xfee31e40,0xbfb41091,4 +np.float32,0xbf733d2c,0xbfb30cf1,4 +np.float32,0x7ed81015,0x417c380c,4 +np.float32,0x7daafc3e,0xbe2a37ed,4 +np.float32,0x3e44f82b,0x3e476f67,4 +np.float32,0x7c8d99,0x7c8d99,4 +np.float32,0x3f7aec5a,0x3fbee991,4 +np.float32,0xff09fd55,0x3e0709d3,4 +np.float32,0xff4ba4df,0x4173c01f,4 +np.float32,0x3f43d944,0x3f75c7bd,4 +np.float32,0xff6a9106,0x40a10eff,4 +np.float32,0x3bc8341c,0x3bc834bf,4 +np.float32,0x3eea82,0x3eea82,4 +np.float32,0xfea36a3c,0x435729b2,4 +np.float32,0x7dcc1fb0,0x3e330053,4 +np.float32,0x3f616ae6,0x3f9b01ae,4 +np.float32,0x8030963f,0x8030963f,4 +np.float32,0x10d1e2,0x10d1e2,4 +np.float32,0xfeb9a8a6,0x40e6daac,4 +np.float32,0xbe1aba00,0xbe1bea3a,4 +np.float32,0x3cb6b4ea,0x3cb6bcac,4 +np.float32,0x3d8b0b64,0x3d8b422f,4 +np.float32,0x7b6894,0x7b6894,4 +np.float32,0x3e89dcde,0x3e8d4b4b,4 +np.float32,0x3f12b952,0x3f253974,4 +np.float32,0x1c316c,0x1c316c,4 +np.float32,0x7e2da535,0x3f95fe6b,4 +np.float32,0x3ae9a494,0x3ae9a4a4,4 +np.float32,0xbc5f5500,0xbc5f588b,4 +np.float32,0x3e7850fc,0x3e7d4d0e,4 +np.float32,0xbf800000,0xbfc75923,4 +np.float32,0x3e652d69,0x3e691502,4 +np.float32,0xbf6bdd26,0xbfa89129,4 +np.float32,0x3f441cfc,0x3f764a02,4 +np.float32,0x7f5445ff,0xc0906191,4 +np.float32,0x807b2ee3,0x807b2ee3,4 +np.float32,0xbeb6cab8,0xbebef9c0,4 +np.float32,0xff737277,0xbf327011,4 +np.float32,0xfc832aa0,0x402fd52e,4 +np.float32,0xbf0c7538,0xbf1c7c0f,4 +np.float32,0x7e1301c7,0xbf0ee63e,4 +np.float64,0xbfe0ef7df7a1defc,0xbfe2b76a8d8aeb35,4 +np.float64,0x7fdd9c2eae3b385c,0xbfc00d6885485039,4 +np.float64,0xbfb484c710290990,0xbfb4900e0a527555,4 +np.float64,0x7fe73e5d6cee7cba,0x3fefbf70a56b60d3,4 +np.float64,0x800a110aa8d42216,0x800a110aa8d42216,4 +np.float64,0xffedd4f3f3bba9e7,0xbff076f8c4124919,4 +np.float64,0x800093407f812682,0x800093407f812682,4 +np.float64,0x800a23150e54462a,0x800a23150e54462a,4 +np.float64,0xbfb1076864220ed0,0xbfb10dd95a74b733,4 +np.float64,0x3fed1f8b37fa3f16,0x3ff496100985211f,4 +np.float64,0x3fdf762f84beec5f,0x3fe1223eb04a17e0,4 +np.float64,0x53fd4e0aa7faa,0x53fd4e0aa7faa,4 +np.float64,0x3fdbd283bdb7a507,0x3fddb7ec9856a546,4 +np.float64,0xbfe43f449d687e89,0xbfe77724a0d3072b,4 +np.float64,0x618b73bcc316f,0x618b73bcc316f,4 +np.float64,0x67759424ceeb3,0x67759424ceeb3,4 +np.float64,0xbfe4b6f7d9a96df0,0xbfe831371f3bd7a8,4 +np.float64,0x800a531b8b74a637,0x800a531b8b74a637,4 +np.float64,0xffeeffd5c37dffab,0x3fea140cbc2c3726,4 +np.float64,0x3fe648e2002c91c4,0x3feac1b8816f972a,4 +np.float64,0x800f16242a1e2c48,0x800f16242a1e2c48,4 +np.float64,0xffeeff8e1dbdff1b,0xc000b555f117dce7,4 +np.float64,0x3fdf1cf73fbe39f0,0x3fe0e9032401135b,4 +np.float64,0x7fe19c388b633870,0x3fd5271b69317d5b,4 +np.float64,0x918f226d231e5,0x918f226d231e5,4 +np.float64,0x4cc19ab499834,0x4cc19ab499834,4 +np.float64,0xbd3121d57a624,0xbd3121d57a624,4 +np.float64,0xbfd145d334a28ba6,0xbfd1b468866124d6,4 +np.float64,0x8bdbf41517b7f,0x8bdbf41517b7f,4 +np.float64,0x3fd1b8cb3ea37198,0x3fd2306b13396cae,4 +np.float64,0xbfd632a959ac6552,0xbfd7220fcfb5ef78,4 +np.float64,0x1cdaafc639b57,0x1cdaafc639b57,4 +np.float64,0x3febdcce1577b99c,0x3ff2fe076195a2bc,4 +np.float64,0x7fca6e945934dd28,0x3ff43040df7024e8,4 +np.float64,0x3fbe08e78e3c11cf,0x3fbe2c60e6b48f75,4 +np.float64,0x7fc1ed0d0523da19,0x3ff55f8dcad9440f,4 +np.float64,0xbfdc729b8cb8e538,0xbfde7b6e15dd60c4,4 +np.float64,0x3fd219404f243281,0x3fd298d7b3546531,4 +np.float64,0x3fe715c3f56e2b88,0x3fec255b5a59456e,4 +np.float64,0x7fe8b88e74b1711c,0x3ff60efd2c81d13d,4 +np.float64,0xa1d2b9fd43a57,0xa1d2b9fd43a57,4 +np.float64,0xffc1818223230304,0xbfb85c6c1e8018e7,4 +np.float64,0x3fde38ac8b3c7159,0x3fe0580c7e228576,4 +np.float64,0x8008faf7b491f5f0,0x8008faf7b491f5f0,4 +np.float64,0xffe7a1d751af43ae,0xbf7114cd7bbcd981,4 +np.float64,0xffec2db1b4b85b62,0xbff5cae759667f83,4 +np.float64,0x7fefce1ae27f9c35,0x3ff4b8b88f4876cf,4 +np.float64,0x7fd1ff56a523feac,0xbff342ce192f14dd,4 +np.float64,0x80026b3e3f84d67d,0x80026b3e3f84d67d,4 +np.float64,0xffedee5879bbdcb0,0xc02fae11508b2be0,4 +np.float64,0x8003c0dc822781ba,0x8003c0dc822781ba,4 +np.float64,0xffe38a79eca714f4,0xc008aa23b7a63980,4 +np.float64,0xbfda70411eb4e082,0xbfdc0d7e29c89010,4 +np.float64,0x800a5e34f574bc6a,0x800a5e34f574bc6a,4 +np.float64,0x3fc19fac6e233f59,0x3fc1bc66ac0d73d4,4 +np.float64,0x3a8a61ea7514d,0x3a8a61ea7514d,4 +np.float64,0x3fb57b536e2af6a0,0x3fb588451f72f44c,4 +np.float64,0x7fd68c6d082d18d9,0xc032ac926b665c9a,4 +np.float64,0xd5b87cfdab710,0xd5b87cfdab710,4 +np.float64,0xfe80b20bfd017,0xfe80b20bfd017,4 +np.float64,0x3fef8781e37f0f04,0x3ff8215fe2c1315a,4 +np.float64,0xffedddbb9c3bbb76,0x3fd959b82258a32a,4 +np.float64,0x3fc7d41f382fa83e,0x3fc81b94c3a091ba,4 +np.float64,0xffc3275dcf264ebc,0x3fb2b3d4985c6078,4 +np.float64,0x7fe34d2b7ba69a56,0x40001f3618e3c7c9,4 +np.float64,0x3fd64ae35fac95c7,0x3fd73d77e0b730f8,4 +np.float64,0x800e53bf6b3ca77f,0x800e53bf6b3ca77f,4 +np.float64,0xbfddf7c9083bef92,0xbfe02f392744d2d1,4 +np.float64,0x1c237cc038471,0x1c237cc038471,4 +np.float64,0x3fe4172beea82e58,0x3fe739b4bf16bc7e,4 +np.float64,0xfa950523f52a1,0xfa950523f52a1,4 +np.float64,0xffc839a2c5307344,0xbff70ff8a3c9247f,4 +np.float64,0x264f828c4c9f1,0x264f828c4c9f1,4 +np.float64,0x148a650a2914e,0x148a650a2914e,4 +np.float64,0x3fe8d255c0b1a4ac,0x3fef623c3ea8d6e3,4 +np.float64,0x800f4fbb28be9f76,0x800f4fbb28be9f76,4 +np.float64,0x7fdca57bcfb94af7,0x3ff51207563fb6cb,4 +np.float64,0x3fe4944107692882,0x3fe7fad593235364,4 +np.float64,0x800119b4f1a2336b,0x800119b4f1a2336b,4 +np.float64,0xbfe734075e6e680e,0xbfec5b35381069f2,4 +np.float64,0xffeb3c00db767801,0xbfbbd7d22df7b4b3,4 +np.float64,0xbfe95c658cb2b8cb,0xbff03ad5e0bc888a,4 +np.float64,0xffeefeb58fbdfd6a,0xbfd5c9264deb0e11,4 +np.float64,0x7fccc80fde39901f,0xc012c60f914f3ca2,4 +np.float64,0x3fe5da289c2bb451,0x3fea07ad00a0ca63,4 +np.float64,0x800e364b0a5c6c96,0x800e364b0a5c6c96,4 +np.float64,0x3fcf9ea7d23f3d50,0x3fd023b72e8c9dcf,4 +np.float64,0x800a475cfc948eba,0x800a475cfc948eba,4 +np.float64,0xffd4e0d757a9c1ae,0xbfa89d573352e011,4 +np.float64,0xbfd4dbec8229b7da,0xbfd5a165f12c7c40,4 +np.float64,0xffe307ab51260f56,0x3fe6b1639da58c3f,4 +np.float64,0xbfe6955a546d2ab4,0xbfeb44ae2183fee9,4 +np.float64,0xbfca1f18f5343e30,0xbfca7d804ccccdf4,4 +np.float64,0xe9f4dfebd3e9c,0xe9f4dfebd3e9c,4 +np.float64,0xfff0000000000000,0xfff8000000000000,4 +np.float64,0x8008e69c0fb1cd38,0x8008e69c0fb1cd38,4 +np.float64,0xbfead1ccf975a39a,0xbff1c84b3db8ca93,4 +np.float64,0x25a982424b531,0x25a982424b531,4 +np.float64,0x8010000000000000,0x8010000000000000,4 +np.float64,0x80056204ea0ac40b,0x80056204ea0ac40b,4 +np.float64,0x800d1442d07a2886,0x800d1442d07a2886,4 +np.float64,0xbfaef3dadc3de7b0,0xbfaefd85ae6205f0,4 +np.float64,0x7fe969ce4b32d39c,0xbff3c4364fc6778f,4 +np.float64,0x7fe418bac0a83175,0x402167d16b1efe0b,4 +np.float64,0x3fd7c82a25af9054,0x3fd8f0c701315672,4 +np.float64,0x80013782a7826f06,0x80013782a7826f06,4 +np.float64,0x7fc031c7ee20638f,0x400747ab705e6904,4 +np.float64,0x3fe8cf327ff19e65,0x3fef5c14f8aafa89,4 +np.float64,0xbfe331a416a66348,0xbfe5e2290a098dd4,4 +np.float64,0x800607b2116c0f65,0x800607b2116c0f65,4 +np.float64,0x7fb40448f0280891,0xbfd43d4f0ffa1d64,4 +np.float64,0x7fefffffffffffff,0xbf74530cfe729484,4 +np.float64,0x3fe39b5444a736a9,0x3fe67eaa0b6acf27,4 +np.float64,0x3fee4733c4fc8e68,0x3ff631eabeef9696,4 +np.float64,0xbfec840f3b79081e,0xbff3cc8563ab2e74,4 +np.float64,0xbfc8f6854c31ed0c,0xbfc948caacb3bba0,4 +np.float64,0xffbcf754a639eea8,0xbfc88d17cad3992b,4 +np.float64,0x8000bd3163417a64,0x8000bd3163417a64,4 +np.float64,0x3fe766d0eaeecda2,0x3fecb660882f7024,4 +np.float64,0xb6cc30156d986,0xb6cc30156d986,4 +np.float64,0xffc0161f9f202c40,0x3fe19bdefe5cf8b1,4 +np.float64,0xffe1e462caa3c8c5,0x3fe392c47feea17b,4 +np.float64,0x30a36a566146e,0x30a36a566146e,4 +np.float64,0x3fa996f580332deb,0x3fa99c6b4f2abebe,4 +np.float64,0x3fba71716e34e2e0,0x3fba899f35edba1d,4 +np.float64,0xbfe8f7e5e971efcc,0xbfefac431a0e3d55,4 +np.float64,0xf48f1803e91e3,0xf48f1803e91e3,4 +np.float64,0x7fe3edc0a127db80,0xc03d1a579a5d74a8,4 +np.float64,0xffeba82056375040,0x3fdfd701308700db,4 +np.float64,0xbfeb5a924cf6b524,0xbff2640de7cd107f,4 +np.float64,0xfa4cd1a9f499a,0xfa4cd1a9f499a,4 +np.float64,0x800de1be7b9bc37d,0x800de1be7b9bc37d,4 +np.float64,0xffd44e56ad289cae,0x3fdf4b8085db9b67,4 +np.float64,0xbfe4fb3aea69f676,0xbfe89d2cc46fcc50,4 +np.float64,0xbfe596495d6b2c92,0xbfe997a589a1f632,4 +np.float64,0x6f55a2b8deab5,0x6f55a2b8deab5,4 +np.float64,0x7fe72dc4712e5b88,0x4039c4586b28c2bc,4 +np.float64,0x89348bd712692,0x89348bd712692,4 +np.float64,0xffe062156120c42a,0x4005f0580973bc77,4 +np.float64,0xbfeabc714d7578e2,0xbff1b07e2fa57dc0,4 +np.float64,0x8003a56b3e874ad7,0x8003a56b3e874ad7,4 +np.float64,0x800eeadfb85dd5c0,0x800eeadfb85dd5c0,4 +np.float64,0x46d77a4c8daf0,0x46d77a4c8daf0,4 +np.float64,0x8000c06e7dc180de,0x8000c06e7dc180de,4 +np.float64,0x3fe428d211e851a4,0x3fe754b1c00a89bc,4 +np.float64,0xc5be11818b7c2,0xc5be11818b7c2,4 +np.float64,0x7fefc244893f8488,0x401133dc54f52de5,4 +np.float64,0x3fde30eee93c61de,0x3fe0532b827543a6,4 +np.float64,0xbfd447f48b288fea,0xbfd4fd0654f90718,4 +np.float64,0xbfde98dc7b3d31b8,0xbfe094df12f84a06,4 +np.float64,0x3fed2c1a1dfa5834,0x3ff4a6c4f3470a65,4 +np.float64,0xbfe992165073242d,0xbff071ab039c9177,4 +np.float64,0x3fd0145d1b2028ba,0x3fd06d3867b703dc,4 +np.float64,0x3fe179457362f28b,0x3fe3722f1d045fda,4 +np.float64,0x800e28964fbc512d,0x800e28964fbc512d,4 +np.float64,0x8004a5d785294bb0,0x8004a5d785294bb0,4 +np.float64,0xbfd652f2272ca5e4,0xbfd7469713125120,4 +np.float64,0x7fe61f49036c3e91,0xbf9b6ccdf2d87e70,4 +np.float64,0xffb7d47dd02fa8f8,0xc004449a82320b13,4 +np.float64,0x3feb82f996b705f3,0x3ff29336c738a4c5,4 +np.float64,0x3fbb7fceea36ffa0,0x3fbb9b02c8ad7f93,4 +np.float64,0x80004519fb208a35,0x80004519fb208a35,4 +np.float64,0xbfe0539114e0a722,0xbfe1e86dc5aa039c,4 +np.float64,0x0,0x0,4 +np.float64,0xbfe99d1125f33a22,0xbff07cf8ec04300f,4 +np.float64,0xffd4fbeecc29f7de,0x3ffab76775a8455f,4 +np.float64,0xbfbf1c618e3e38c0,0xbfbf43d2764a8333,4 +np.float64,0x800cae02a9d95c06,0x800cae02a9d95c06,4 +np.float64,0x3febc47d3bf788fa,0x3ff2e0d7cf8ef509,4 +np.float64,0x3fef838f767f071f,0x3ff81aeac309bca0,4 +np.float64,0xbfd5e70716abce0e,0xbfd6ccb033ef7a35,4 +np.float64,0x3f9116fa60222df5,0x3f9117625f008e0b,4 +np.float64,0xffe02b1e5f20563c,0xbfe6b2ec293520b7,4 +np.float64,0xbf9b5aec3036b5e0,0xbf9b5c96c4c7f951,4 +np.float64,0xfdb0169bfb603,0xfdb0169bfb603,4 +np.float64,0x7fcdd1d51c3ba3a9,0x401f0e12fa0b7570,4 +np.float64,0xbfd088103fa11020,0xbfd0e8c4a333ffb2,4 +np.float64,0x3fe22df82ee45bf0,0x3fe46d03a7c14de2,4 +np.float64,0xbfd57b0c28aaf618,0xbfd65349a6191de5,4 +np.float64,0x3fe0a42f50a1485f,0x3fe252e26775d9a4,4 +np.float64,0x800fab4e363f569c,0x800fab4e363f569c,4 +np.float64,0xffe9f0ed63f3e1da,0xbfe278c341b171d5,4 +np.float64,0x7fe26c244664d848,0xbfb325269dad1996,4 +np.float64,0xffe830410bf06081,0xc00181a39f606e96,4 +np.float64,0x800c548a0c78a914,0x800c548a0c78a914,4 +np.float64,0x800f94761ebf28ec,0x800f94761ebf28ec,4 +np.float64,0x3fe5984845eb3091,0x3fe99aeb653c666d,4 +np.float64,0x7fe93e5bf8f27cb7,0xc010d159fa27396a,4 +np.float64,0xffefffffffffffff,0x3f74530cfe729484,4 +np.float64,0x4c83f1269907f,0x4c83f1269907f,4 +np.float64,0x3fde0065a8bc00cc,0x3fe034a1cdf026d4,4 +np.float64,0x800743810d6e8703,0x800743810d6e8703,4 +np.float64,0x80040662d5280cc6,0x80040662d5280cc6,4 +np.float64,0x3fed20b2c5ba4166,0x3ff497988519d7aa,4 +np.float64,0xffe8fa15e5f1f42b,0x3fff82ca76d797b4,4 +np.float64,0xbb72e22f76e5d,0xbb72e22f76e5d,4 +np.float64,0x7fc18ffa7c231ff4,0xbff4b8b4c3315026,4 +np.float64,0xbfe8d1ac44f1a358,0xbfef60efc4f821e3,4 +np.float64,0x3fd38c1fe8271840,0x3fd42dc37ff7262b,4 +np.float64,0xe577bee5caef8,0xe577bee5caef8,4 +np.float64,0xbff0000000000000,0xbff8eb245cbee3a6,4 +np.float64,0xffcb3a9dd436753c,0x3fcd1a3aff1c3fc7,4 +np.float64,0x7fe44bf2172897e3,0x3ff60bfe82a379f4,4 +np.float64,0x8009203823924071,0x8009203823924071,4 +np.float64,0x7fef8e0abc7f1c14,0x3fe90e4962d47ce5,4 +np.float64,0xffda50004434a000,0x3fb50dee03e1418b,4 +np.float64,0x7fe2ff276ea5fe4e,0xc0355b7d2a0a8d9d,4 +np.float64,0x3fd0711ba5a0e238,0x3fd0d03823d2d259,4 +np.float64,0xe7625b03cec4c,0xe7625b03cec4c,4 +np.float64,0xbfd492c8d7a92592,0xbfd55006cde8d300,4 +np.float64,0x8001fee99f23fdd4,0x8001fee99f23fdd4,4 +np.float64,0x7ff4000000000000,0x7ffc000000000000,4 +np.float64,0xfa15df97f42bc,0xfa15df97f42bc,4 +np.float64,0xbfec3fdca9787fb9,0xbff377164b13c7a9,4 +np.float64,0xbcec10e579d82,0xbcec10e579d82,4 +np.float64,0xbfc3b4e2132769c4,0xbfc3dd1fcc7150a6,4 +np.float64,0x80045b149ee8b62a,0x80045b149ee8b62a,4 +np.float64,0xffe044554c2088aa,0xbff741436d558785,4 +np.float64,0xffcc65f09f38cbe0,0xc0172b4adc2d317d,4 +np.float64,0xf68b2d3bed166,0xf68b2d3bed166,4 +np.float64,0x7fc7f44c572fe898,0x3fec69f3b1eca790,4 +np.float64,0x3fac51f61438a3ec,0x3fac595d34156002,4 +np.float64,0xbfeaa9f256f553e5,0xbff19bfdf5984326,4 +np.float64,0x800e4742149c8e84,0x800e4742149c8e84,4 +np.float64,0xbfc493df132927c0,0xbfc4c1ba4268ead9,4 +np.float64,0xbfbf0c56383e18b0,0xbfbf3389fcf50c72,4 +np.float64,0xbf978a0e082f1420,0xbf978b1dd1da3d3c,4 +np.float64,0xbfe04375356086ea,0xbfe1d34c57314dd1,4 +np.float64,0x3feaeeb29b75dd65,0x3ff1e8b772374979,4 +np.float64,0xbfe15e42c3a2bc86,0xbfe34d45d56c5c15,4 +np.float64,0x3fe507429a6a0e85,0x3fe8b058176b3225,4 +np.float64,0x3feee2b26c3dc565,0x3ff71b73203de921,4 +np.float64,0xbfd496577aa92cae,0xbfd553fa7fe15a5f,4 +np.float64,0x7fe2c10953e58212,0x3fc8ead6a0d14bbf,4 +np.float64,0x800035b77aa06b70,0x800035b77aa06b70,4 +np.float64,0x2329201e46525,0x2329201e46525,4 +np.float64,0xbfe6225c9a6c44b9,0xbfea80861590fa02,4 +np.float64,0xbfd6925030ad24a0,0xbfd78e70b1c2215d,4 +np.float64,0xbfd82225c4b0444c,0xbfd958a60f845b39,4 +np.float64,0xbb03d8a17609,0xbb03d8a17609,4 +np.float64,0x7fc33967b12672ce,0x40001e00c9af4002,4 +np.float64,0xff9373c6d026e780,0xbff308654a459d3d,4 +np.float64,0x3feab1f9c5f563f4,0x3ff1a4e0fd2f093d,4 +np.float64,0xbf993ef768327de0,0xbf994046b64e308b,4 +np.float64,0xffb87382fc30e708,0xbfde0accb83c891b,4 +np.float64,0x800bb3a118176743,0x800bb3a118176743,4 +np.float64,0x800c810250d90205,0x800c810250d90205,4 +np.float64,0xbfd2c4eb9ba589d8,0xbfd3539508b4a4a8,4 +np.float64,0xbee1f5437dc3f,0xbee1f5437dc3f,4 +np.float64,0x3fc07aeab520f5d8,0x3fc0926272f9d8e2,4 +np.float64,0xbfe23747a3246e90,0xbfe47a20a6e98687,4 +np.float64,0x3fde1296debc252c,0x3fe0401143ff6b5c,4 +np.float64,0xbfcec8c2f73d9184,0xbfcf644e25ed3b74,4 +np.float64,0xff9314f2c82629e0,0x40559a0f9099dfd1,4 +np.float64,0xbfe27487afa4e910,0xbfe4d0e01200bde6,4 +np.float64,0xffb3d6637627acc8,0x3fe326d4b1e1834f,4 +np.float64,0xffe6f84d642df09a,0x3fc73fa9f57c3acb,4 +np.float64,0xffe67cf76fecf9ee,0xc01cf48c97937ef9,4 +np.float64,0x7fdc73fc12b8e7f7,0xbfcfcecde9331104,4 +np.float64,0xffdcf8789239f0f2,0x3fe345e3b8e28776,4 +np.float64,0x800a70af5314e15f,0x800a70af5314e15f,4 +np.float64,0xffc862300730c460,0x3fc4e9ea813beca7,4 +np.float64,0xbfcc6961bd38d2c4,0xbfcce33bfa6c6bd1,4 +np.float64,0xbfc9b76bbf336ed8,0xbfca117456ac37e5,4 +np.float64,0x7fb86e829430dd04,0x400a5bd7a18e302d,4 +np.float64,0x7fb9813ef833027d,0xbfe5a6494f143625,4 +np.float64,0x8005085e2c2a10bd,0x8005085e2c2a10bd,4 +np.float64,0xffe5af099d6b5e12,0x40369bbe31e03e06,4 +np.float64,0xffde03b1fd3c0764,0x3ff061120aa1f52a,4 +np.float64,0x7fa4eb6cdc29d6d9,0x3fe9defbe9010322,4 +np.float64,0x800803f4b11007ea,0x800803f4b11007ea,4 +np.float64,0x7febd50f6df7aa1e,0xbffcf540ccf220dd,4 +np.float64,0x7fed454f08fa8a9d,0xbffc2a8b81079403,4 +np.float64,0xbfed7e8c69bafd19,0xbff5161e51ba6634,4 +np.float64,0xffef92e78eff25ce,0xbffefeecddae0ad3,4 +np.float64,0x7fe5b9b413ab7367,0xbfc681ba29704176,4 +np.float64,0x29284e805252,0x29284e805252,4 +np.float64,0xffed3955bcfa72ab,0xbfc695acb5f468de,4 +np.float64,0x3fe464ee1ca8c9dc,0x3fe7b140ce50fdca,4 +np.float64,0xffe522ae4bea455c,0x3feb957c146e66ef,4 +np.float64,0x8000000000000000,0x8000000000000000,4 +np.float64,0x3fd0c353a2a186a8,0x3fd1283aaa43a411,4 +np.float64,0x3fdb30a749b6614f,0x3fdcf40df006ed10,4 +np.float64,0x800109213cc21243,0x800109213cc21243,4 +np.float64,0xbfe72aa0c5ee5542,0xbfec4a713f513bc5,4 +np.float64,0x800865344ad0ca69,0x800865344ad0ca69,4 +np.float64,0x7feb7df60eb6fbeb,0x3fb1df06a67aa22f,4 +np.float64,0x3fe83a5dd93074bc,0x3fee3d63cda72636,4 +np.float64,0xbfde70e548bce1ca,0xbfe07b8e19c9dac6,4 +np.float64,0xbfeea38d537d471b,0xbff6bb18c230c0be,4 +np.float64,0x3fefeebbc47fdd78,0x3ff8cdaa53b7c7b4,4 +np.float64,0x7fe6512e20eca25b,0xbff623cee44a22b5,4 +np.float64,0xf8fa5ca3f1f4c,0xf8fa5ca3f1f4c,4 +np.float64,0x7fd12d00ed225a01,0xbfe90d518ea61faf,4 +np.float64,0x80027db43504fb69,0x80027db43504fb69,4 +np.float64,0xffc10a01aa221404,0x3fcc2065b3d0157b,4 +np.float64,0xbfef8286e87f050e,0xbff8193a54449b59,4 +np.float64,0xbfc73178092e62f0,0xbfc7735072ba4593,4 +np.float64,0x3fc859d70630b3ae,0x3fc8a626522af1c0,4 +np.float64,0x3fe4654c4268ca99,0x3fe7b1d2913eda1a,4 +np.float64,0xbfce93cd843d279c,0xbfcf2c2ef16a0957,4 +np.float64,0xffbcaa16d4395430,0xbfd511ced032d784,4 +np.float64,0xbfe91f980e723f30,0xbfeffb39cf8c7746,4 +np.float64,0x800556fb6f0aadf8,0x800556fb6f0aadf8,4 +np.float64,0xffd009cde520139c,0x3fe4fa83b1e93d28,4 +np.float64,0x7febc0675e3780ce,0x3feb53930c004dae,4 +np.float64,0xbfe7f975bdeff2ec,0xbfedc36e6729b010,4 +np.float64,0x45aff57c8b5ff,0x45aff57c8b5ff,4 +np.float64,0xbfec7ebd0138fd7a,0xbff3c5cab680aae0,4 +np.float64,0x8009448003b28900,0x8009448003b28900,4 +np.float64,0x3fca4b992d349732,0x3fcaabebcc86aa9c,4 +np.float64,0x3fca069161340d20,0x3fca63ecc742ff3a,4 +np.float64,0x80063bc80bec7791,0x80063bc80bec7791,4 +np.float64,0xbfe1764bffe2ec98,0xbfe36e1cb30cec94,4 +np.float64,0xffd0dba72f21b74e,0x3fb1834964d57ef6,4 +np.float64,0xbfe31848fc263092,0xbfe5bd066445cbc3,4 +np.float64,0xbfd1fb227323f644,0xbfd278334e27f02d,4 +np.float64,0xffdc59069fb8b20e,0xbfdfc363f559ea2c,4 +np.float64,0x3fdea52a52bd4a55,0x3fe09cada4e5344c,4 +np.float64,0x3f715e55a022bd00,0x3f715e5c72a2809e,4 +np.float64,0x1d1ac6023a35a,0x1d1ac6023a35a,4 +np.float64,0x7feacc71627598e2,0x400486b82121da19,4 +np.float64,0xa0287fa340510,0xa0287fa340510,4 +np.float64,0xffe352c5abe6a58b,0xc002623346060543,4 +np.float64,0x7fed577a23baaef3,0x3fda19bc8fa3b21f,4 +np.float64,0x3fde8dd5263d1baa,0x3fe08de0fedf7029,4 +np.float64,0x3feddd3be2bbba78,0x3ff599b2f3e018cc,4 +np.float64,0xc7a009f58f401,0xc7a009f58f401,4 +np.float64,0xbfef03d5a4fe07ab,0xbff74ee08681f47b,4 +np.float64,0x7fe2cf60eea59ec1,0x3fe905fb44f8cc60,4 +np.float64,0xbfe498fcab6931fa,0xbfe8023a6ff8becf,4 +np.float64,0xbfef7142acfee285,0xbff7fd196133a595,4 +np.float64,0xd214ffdba42a0,0xd214ffdba42a0,4 +np.float64,0x8006de7d78cdbcfc,0x8006de7d78cdbcfc,4 +np.float64,0xb247d34f648fb,0xb247d34f648fb,4 +np.float64,0xbfdd5bece6bab7da,0xbfdf9ba63ca2c5b2,4 +np.float64,0x7fe874650af0e8c9,0x3fe74204e122c10f,4 +np.float64,0x800768c49baed18a,0x800768c49baed18a,4 +np.float64,0x3fb4c0a192298140,0x3fb4cc4c8aa43300,4 +np.float64,0xbfa740531c2e80a0,0xbfa7446b7c74ae8e,4 +np.float64,0x7fe10d6edf221add,0x3fedbcd2eae26657,4 +np.float64,0xbfe9175d0f722eba,0xbfefeaca7f32c6e3,4 +np.float64,0x953e11d32a7c2,0x953e11d32a7c2,4 +np.float64,0x80032df90c465bf3,0x80032df90c465bf3,4 +np.float64,0xffec5b799638b6f2,0xbfe95cd2c69be12c,4 +np.float64,0xffe0c3cfa9a1879f,0x3fe20b99b0c108ce,4 +np.float64,0x3fb610d8e22c21b2,0x3fb61ee0d6c16df8,4 +np.float64,0xffe16bb39962d766,0xc016d370381b6b42,4 +np.float64,0xbfdc72edb238e5dc,0xbfde7bd2de10717a,4 +np.float64,0xffed52dee3baa5bd,0xc01994c08899129a,4 +np.float64,0xffa92aab08325550,0xbff2b881ce363cbd,4 +np.float64,0x7fe028282de0504f,0xc0157ff96c69a9c7,4 +np.float64,0xbfdb2151bf3642a4,0xbfdce196fcc35857,4 +np.float64,0x3fcffbd13c3ff7a2,0x3fd0554b5f0371ac,4 +np.float64,0x800d206bff1a40d8,0x800d206bff1a40d8,4 +np.float64,0x458f818c8b1f1,0x458f818c8b1f1,4 +np.float64,0x800a7b56a234f6ae,0x800a7b56a234f6ae,4 +np.float64,0xffe3d86161e7b0c2,0xbff58d0dbde9f188,4 +np.float64,0xe8ed82e3d1db1,0xe8ed82e3d1db1,4 +np.float64,0x3fe234e0176469c0,0x3fe476bd36b96a75,4 +np.float64,0xbfc7cb9c132f9738,0xbfc812c46e185e0b,4 +np.float64,0xbfeba116c1f7422e,0xbff2b6b7563ad854,4 +np.float64,0x7fe7041de62e083b,0x3f5d2b42aca47274,4 +np.float64,0xbfcf60f4ff3ec1e8,0xbfd002eb83406436,4 +np.float64,0xbfc06067a520c0d0,0xbfc0776e5839ecda,4 +np.float64,0x4384965a87093,0x4384965a87093,4 +np.float64,0xd2ed9d01a5db4,0xd2ed9d01a5db4,4 +np.float64,0x3fbea88cb63d5119,0x3fbece49cc34a379,4 +np.float64,0x3fe7e982ebefd306,0x3feda5bd4c435d43,4 +np.float64,0xffdb60a3e036c148,0xbfcb7ed21e7a8f49,4 +np.float64,0x7fdba9231eb75245,0xbfd750cab1536398,4 +np.float64,0x800d593534dab26b,0x800d593534dab26b,4 +np.float64,0xffdf15fb683e2bf6,0x3fb3aaea23357f06,4 +np.float64,0xbfd6f8a2e5adf146,0xbfd802e509d67c67,4 +np.float64,0x3feeaa31513d5463,0x3ff6c52147dc053c,4 +np.float64,0xf2f6dfd3e5edc,0xf2f6dfd3e5edc,4 +np.float64,0x7fd58d8279ab1b04,0x403243f23d02af2a,4 +np.float64,0x8000000000000001,0x8000000000000001,4 +np.float64,0x3fdffb8e0ebff71c,0x3fe1786cb0a6b0f3,4 +np.float64,0xc999826b93331,0xc999826b93331,4 +np.float64,0xffc4966f19292ce0,0x3ff0836c75c56cc7,4 +np.float64,0x7fef95a4b2ff2b48,0xbfbbe2c27c78154f,4 +np.float64,0xb8f1307f71e26,0xb8f1307f71e26,4 +np.float64,0x3fe807bc7eb00f79,0x3fedde19f2d3c42d,4 +np.float64,0x5e4b6580bc98,0x5e4b6580bc98,4 +np.float64,0xffe19353576326a6,0xc0278c51fee07d36,4 +np.float64,0xbfb0ca6f3e2194e0,0xbfb0d09be673fa72,4 +np.float64,0x3fea724211b4e484,0x3ff15ee06f0a0a13,4 +np.float64,0xbfda21e1c4b443c4,0xbfdbb041f3c86832,4 +np.float64,0x8008082b24901057,0x8008082b24901057,4 +np.float64,0xbfd031aa4ea06354,0xbfd08c77729634bb,4 +np.float64,0xbfc407e153280fc4,0xbfc432275711df5f,4 +np.float64,0xbb4fa4b5769f5,0xbb4fa4b5769f5,4 +np.float64,0x7fed6d1daffada3a,0xc037a14bc7b41fab,4 +np.float64,0xffeee589943dcb12,0x3ff2abfe47037778,4 +np.float64,0x301379d260270,0x301379d260270,4 +np.float64,0xbfec2fefc2b85fe0,0xbff36362c0363e06,4 +np.float64,0xbfe0b1c82e216390,0xbfe264f503f7c22c,4 +np.float64,0xbfea2bce78f4579d,0xbff112d6f07935ea,4 +np.float64,0x18508ef230a13,0x18508ef230a13,4 +np.float64,0x800667a74d6ccf4f,0x800667a74d6ccf4f,4 +np.float64,0x79ce5c8cf39cc,0x79ce5c8cf39cc,4 +np.float64,0x3feda61c8efb4c39,0x3ff54c9ade076f54,4 +np.float64,0x3fe27e06b0e4fc0d,0x3fe4de665c1dc3ca,4 +np.float64,0xbfd15fea2722bfd4,0xbfd1d081c55813b0,4 +np.float64,0xbfe5222c4cea4458,0xbfe8db62deb7d2ad,4 +np.float64,0xbfe8a16c33b142d8,0xbfef02d5831592a8,4 +np.float64,0x3fdb60e7c4b6c1d0,0x3fdd2e4265c4c3b6,4 +np.float64,0x800076d62b60edad,0x800076d62b60edad,4 +np.float64,0xbfec8f1527791e2a,0xbff3da7ed3641e8d,4 +np.float64,0x2af03bfe55e08,0x2af03bfe55e08,4 +np.float64,0xa862ee0950c5e,0xa862ee0950c5e,4 +np.float64,0x7fea5a7c1eb4b4f7,0xbffa6f07d28ef211,4 +np.float64,0x90e118fb21c23,0x90e118fb21c23,4 +np.float64,0xbfead0721bf5a0e4,0xbff1c6c7a771a128,4 +np.float64,0x3f63f4a4c027e94a,0x3f63f4a75665da67,4 +np.float64,0x3fece0efa579c1e0,0x3ff443bec52f021e,4 +np.float64,0xbfdbe743b737ce88,0xbfddd129bff89c15,4 +np.float64,0x3fd48c9b8fa91938,0x3fd5492a630a8cb5,4 +np.float64,0x3ff0000000000000,0x3ff8eb245cbee3a6,4 +np.float64,0xbfd51ea33baa3d46,0xbfd5ebd5dc710204,4 +np.float64,0x3fcfbab0183f7560,0x3fd032a054580b00,4 +np.float64,0x8007abce13cf579d,0x8007abce13cf579d,4 +np.float64,0xbfef0f4723be1e8e,0xbff760c7008e8913,4 +np.float64,0x8006340f524c681f,0x8006340f524c681f,4 +np.float64,0x87b7d7010f71,0x87b7d7010f71,4 +np.float64,0x3fe9422da9b2845b,0x3ff02052e6148c45,4 +np.float64,0x7fddd259b93ba4b2,0xc000731aa33d84b6,4 +np.float64,0x3fe0156d12202ada,0x3fe1972ba309cb29,4 +np.float64,0x8004f1264b89e24d,0x8004f1264b89e24d,4 +np.float64,0x3fececdcacb9d9b9,0x3ff4534d5861f731,4 +np.float64,0x3fd1790ab822f215,0x3fd1eb97b1bb6fb4,4 +np.float64,0xffce5d11863cba24,0xbfcb4f38c17210da,4 +np.float64,0x800a30c32a546187,0x800a30c32a546187,4 +np.float64,0x3fa58cc61c2b198c,0x3fa59008add7233e,4 +np.float64,0xbfe0ac77d62158f0,0xbfe25de3dba0bc4a,4 +np.float64,0xeb8c5753d718b,0xeb8c5753d718b,4 +np.float64,0x3fee5438dafca872,0x3ff644fef7e7adb5,4 +np.float64,0x3faad1eb2c35a3e0,0x3faad83499f94057,4 +np.float64,0x3fe39152c46722a6,0x3fe66fba0b96ab6e,4 +np.float64,0xffd6fd17712dfa2e,0xc010d697d1ab8731,4 +np.float64,0x5214a888a4296,0x5214a888a4296,4 +np.float64,0x8000127a5da024f5,0x8000127a5da024f5,4 +np.float64,0x7feb3a366cb6746c,0x3fbe49bd8d5f213a,4 +np.float64,0xca479501948f3,0xca479501948f3,4 +np.float64,0x7fe7c799ce6f8f33,0xbfd796cd98dc620c,4 +np.float64,0xffe20bcf30a4179e,0xbff8ca5453fa088f,4 +np.float64,0x3fe624638a6c48c7,0x3fea83f123832c3c,4 +np.float64,0xbfe5f1377c6be26f,0xbfea2e143a2d522c,4 +np.float64,0x7fd193f9f8a327f3,0xbfb04ee2602574d4,4 +np.float64,0xbfe7419d2fee833a,0xbfec737f140d363d,4 +np.float64,0x1,0x1,4 +np.float64,0x7fe2ac246c655848,0x3fd14fee3237727a,4 +np.float64,0xa459b42948b37,0xa459b42948b37,4 +np.float64,0x3fb26155ae24c2ab,0x3fb2696fc446d4c6,4 +np.float64,0xbfdd7b332e3af666,0xbfdfc296c21f1aa8,4 +np.float64,0xbfe00dbda4a01b7c,0xbfe18d2b060f0506,4 +np.float64,0x8003bb22d3e77646,0x8003bb22d3e77646,4 +np.float64,0x3fee21b0a57c4361,0x3ff5fb6a21dc911c,4 +np.float64,0x80ca69270194d,0x80ca69270194d,4 +np.float64,0xbfd6d80350adb006,0xbfd7ddb501edbde0,4 +np.float64,0xd2f8b801a5f2,0xd2f8b801a5f2,4 +np.float64,0xbfe856b3f170ad68,0xbfee7334fdc49296,4 +np.float64,0x3fed5c1b20bab836,0x3ff4e73ee5d5c7f3,4 +np.float64,0xbfd58085a5ab010c,0xbfd6596ddc381ffa,4 +np.float64,0x3fe4f0134b29e027,0x3fe88b70602fbd21,4 +np.float64,0xffc9098fdc321320,0x4011c334a74a92cf,4 +np.float64,0x794749bef28ea,0x794749bef28ea,4 +np.float64,0xbfc86b547f30d6a8,0xbfc8b84a4fafe0af,4 +np.float64,0x7fe1356b9da26ad6,0x3fd270bca208d899,4 +np.float64,0x7fca0ef1aa341de2,0xbff851044c0734fa,4 +np.float64,0x80064cb8b62c9972,0x80064cb8b62c9972,4 +np.float64,0xffd3a09a83a74136,0x3ffb66dae0accdf5,4 +np.float64,0x800e301aa15c6035,0x800e301aa15c6035,4 +np.float64,0x800e51f323bca3e6,0x800e51f323bca3e6,4 +np.float64,0x7ff0000000000000,0xfff8000000000000,4 +np.float64,0x800c4278c87884f2,0x800c4278c87884f2,4 +np.float64,0xbfe8481649f0902c,0xbfee576772695096,4 +np.float64,0xffe2344e3fa4689c,0x3fb10442ec0888de,4 +np.float64,0xbfeada313d75b462,0xbff1d1aee3fab3a9,4 +np.float64,0x8009ddfb1333bbf7,0x8009ddfb1333bbf7,4 +np.float64,0x7fed3314c93a6629,0x3ff7a9b12dc1cd37,4 +np.float64,0x3fd55c26da2ab84e,0x3fd630a7b8aac78a,4 +np.float64,0x800cdb5203f9b6a4,0x800cdb5203f9b6a4,4 +np.float64,0xffd04a875da0950e,0x4009a13810ab121d,4 +np.float64,0x800f1acb527e3597,0x800f1acb527e3597,4 +np.float64,0xbf9519bf282a3380,0xbf951a82e9b955ff,4 +np.float64,0x3fcd7a42fa3af486,0x3fce028f3c51072d,4 +np.float64,0xbfdd3e21b73a7c44,0xbfdf769f2ff2480b,4 +np.float64,0xffd4361e2aa86c3c,0xbfc211ce8e9f792c,4 +np.float64,0x7fccf97f6939f2fe,0xbff8464bad830f06,4 +np.float64,0x800ce47fb939c900,0x800ce47fb939c900,4 +np.float64,0xffe9e51df173ca3b,0xbfceaf990d652c4e,4 +np.float64,0x3fe05bba5b20b775,0x3fe1f326e4455442,4 +np.float64,0x800a29b4b134536a,0x800a29b4b134536a,4 +np.float64,0xe6f794b7cdef3,0xe6f794b7cdef3,4 +np.float64,0xffb5b688ce2b6d10,0x3ff924bb97ae2f6d,4 +np.float64,0x7fa74105d82e820b,0x3fd49643aaa9eee4,4 +np.float64,0x80020d15f7a41a2d,0x80020d15f7a41a2d,4 +np.float64,0x3fd6a983d5ad5308,0x3fd7a8cc8835b5b8,4 +np.float64,0x7fcd9798f03b2f31,0x3fc534c2f7bf4721,4 +np.float64,0xffdd31873a3a630e,0xbfe3171fcdffb3f7,4 +np.float64,0x80075183234ea307,0x80075183234ea307,4 +np.float64,0x82f3132505e63,0x82f3132505e63,4 +np.float64,0x3febfd9cb837fb39,0x3ff325bbf812515d,4 +np.float64,0xbfb4630fda28c620,0xbfb46e1f802ec278,4 +np.float64,0x3feeed7c89fddafa,0x3ff72c20ce5a9ee4,4 +np.float64,0x7fd3dcb3c127b967,0x40123d27ec9ec31d,4 +np.float64,0xbfe923450c72468a,0xbff00149c5742725,4 +np.float64,0x7fdef7f91abdeff1,0xbfe02ceb21f7923d,4 +np.float64,0x7fdd70d28fbae1a4,0xbfefcc5c9d10cdfd,4 +np.float64,0x800ca445a8d9488c,0x800ca445a8d9488c,4 +np.float64,0x7fec2754e1f84ea9,0x40173f6c1c97f825,4 +np.float64,0x7fcbca31f7379463,0x401e26bd2667075b,4 +np.float64,0x8003fa1d0847f43b,0x8003fa1d0847f43b,4 +np.float64,0xffe95cf85932b9f0,0xc01308e60278aa11,4 +np.float64,0x8009c53948f38a73,0x8009c53948f38a73,4 +np.float64,0x3fdcca9226b99524,0x3fdee7a008f75d41,4 +np.float64,0xbfe9ee241f33dc48,0xbff0d16bfff6c8e9,4 +np.float64,0xbfb3365058266ca0,0xbfb33f9176ebb51d,4 +np.float64,0x7fa98e10f4331c21,0x3fdee04ffd31314e,4 +np.float64,0xbfe1a11aea634236,0xbfe3a8e3d84fda38,4 +np.float64,0xbfd8df051131be0a,0xbfda342805d1948b,4 +np.float64,0x3d49a2407a935,0x3d49a2407a935,4 +np.float64,0xfc51eefff8a3e,0xfc51eefff8a3e,4 +np.float64,0xda63950bb4c73,0xda63950bb4c73,4 +np.float64,0x80050f3d4fea1e7b,0x80050f3d4fea1e7b,4 +np.float64,0x3fcdbd6e453b7ae0,0x3fce497478c28e77,4 +np.float64,0x7ebd4932fd7aa,0x7ebd4932fd7aa,4 +np.float64,0x7fa3904eac27209c,0xc0015f3125efc151,4 +np.float64,0x7fc59f956b2b3f2a,0xc00c012e7a2c281f,4 +np.float64,0xbfd436d716a86dae,0xbfd4ea13533a942b,4 +np.float64,0x9347ae3d268f6,0x9347ae3d268f6,4 +np.float64,0xffd001764d2002ec,0xbffab3462e515623,4 +np.float64,0x3fe6f406662de80d,0x3febe9bac3954999,4 +np.float64,0x3f943ecaf8287d96,0x3f943f77dee5e77f,4 +np.float64,0x3fd6250efcac4a1c,0x3fd712afa947d56f,4 +np.float64,0xbfe849ff777093ff,0xbfee5b089d03391f,4 +np.float64,0xffd3b8ef8f2771e0,0x4000463ff7f29214,4 +np.float64,0xbfc3bae9252775d4,0xbfc3e34c133f1933,4 +np.float64,0xbfea93943df52728,0xbff18355e4fc341d,4 +np.float64,0x3fc4d922ad29b245,0x3fc508d66869ef29,4 +np.float64,0x4329694a8652e,0x4329694a8652e,4 +np.float64,0x8834f1a71069e,0x8834f1a71069e,4 +np.float64,0xe0e5be8dc1cb8,0xe0e5be8dc1cb8,4 +np.float64,0x7fef4d103afe9a1f,0xc0047b88b94554fe,4 +np.float64,0x3fe9b57af4f36af6,0x3ff0963831d51c3f,4 +np.float64,0x3fe081e2fa6103c6,0x3fe22572e41be655,4 +np.float64,0x3fd78cf7b42f19ef,0x3fd8acafa1ad776a,4 +np.float64,0x7fbffd58d43ffab1,0x3fb16092c7de6036,4 +np.float64,0xbfe1e8bfae23d180,0xbfe40c1c6277dd52,4 +np.float64,0x800a9f59fb153eb4,0x800a9f59fb153eb4,4 +np.float64,0xffebe14e33b7c29c,0x3fe0ec532f4deedd,4 +np.float64,0xffc36ca00426d940,0xc000806a712d6e83,4 +np.float64,0xbfcc2be82d3857d0,0xbfcca2a7d372ec64,4 +np.float64,0x800c03b908780772,0x800c03b908780772,4 +np.float64,0xf315a64be62b5,0xf315a64be62b5,4 +np.float64,0xbfe644043cec8808,0xbfeab974d3dc6d80,4 +np.float64,0x3fedb7de3cbb6fbc,0x3ff56549a5acd324,4 +np.float64,0xbfb1a875522350e8,0xbfb1afa41dee338d,4 +np.float64,0xffee8d4a407d1a94,0x3fead1749a636ff6,4 +np.float64,0x8004061c13080c39,0x8004061c13080c39,4 +np.float64,0x3fe650ae7feca15c,0x3feacefb8bc25f64,4 +np.float64,0x3fda8340e6b50682,0x3fdc24275cab1df8,4 +np.float64,0x8009084344321087,0x8009084344321087,4 +np.float64,0x7fdd19cb823a3396,0xbfd1d8fb35d89e3f,4 +np.float64,0xbfe893172571262e,0xbfeee716b592b93c,4 +np.float64,0x8ff5acc11fec,0x8ff5acc11fec,4 +np.float64,0xbfdca0c57cb9418a,0xbfdeb42465a1b59e,4 +np.float64,0xffd77bd2a3aef7a6,0x4012cd69e85b82d8,4 +np.float64,0xbfe6ea78982dd4f1,0xbfebd8ec61fb9e1f,4 +np.float64,0x7fe14b1d80a2963a,0xc02241642102cf71,4 +np.float64,0x3fe712bf286e257e,0x3fec20012329a7fb,4 +np.float64,0x7fcb6fa4d636df49,0x400b899d14a886b3,4 +np.float64,0x3fb82cb39a305960,0x3fb83f29c5f0822e,4 +np.float64,0x7fed694c8b3ad298,0xbfe2724373c69808,4 +np.float64,0xbfcd21229f3a4244,0xbfcda497fc3e1245,4 +np.float64,0x564d3770ac9a8,0x564d3770ac9a8,4 +np.float64,0xf4409e13e8814,0xf4409e13e8814,4 +np.float64,0x80068dca9a8d1b96,0x80068dca9a8d1b96,4 +np.float64,0xbfe13f82afe27f06,0xbfe3236ddded353f,4 +np.float64,0x80023f8114647f03,0x80023f8114647f03,4 +np.float64,0xeafba7dfd5f75,0xeafba7dfd5f75,4 +np.float64,0x3feca74ddeb94e9c,0x3ff3f95dcce5a227,4 +np.float64,0x10000000000000,0x10000000000000,4 +np.float64,0xbfebdb4141f7b682,0xbff2fc29823ac64a,4 +np.float64,0xbfcd75ee2f3aebdc,0xbfcdfdfd87cc6a29,4 +np.float64,0x7fc010cda420219a,0x3fae4ca2cf1f2657,4 +np.float64,0x1a90209e35205,0x1a90209e35205,4 +np.float64,0x8008057d01900afa,0x8008057d01900afa,4 +np.float64,0x3f9cb5f280396be5,0x3f9cb7dfb4e4be4e,4 +np.float64,0xffe1bbb60b63776c,0xc00011b1ffcb2561,4 +np.float64,0xffda883f6fb5107e,0x4044238ef4e2a198,4 +np.float64,0x3fc07c0b4a20f817,0x3fc09387de9eebcf,4 +np.float64,0x8003a9ebc0c753d8,0x8003a9ebc0c753d8,4 +np.float64,0x1d7fd5923affc,0x1d7fd5923affc,4 +np.float64,0xbfe9cd8cf9b39b1a,0xbff0af43e567ba4a,4 +np.float64,0x11285cb42250c,0x11285cb42250c,4 +np.float64,0xffe81ae1ccb035c3,0xbfe038be7eb563a6,4 +np.float64,0xbfe56473b1eac8e8,0xbfe94654d8ab9e75,4 +np.float64,0x3fee904619fd208c,0x3ff69e198152fe17,4 +np.float64,0xbfeeb9a2cbfd7346,0xbff6dc8d96da78cd,4 +np.float64,0x8006cdfa59ed9bf5,0x8006cdfa59ed9bf5,4 +np.float64,0x8008f2366d31e46d,0x8008f2366d31e46d,4 +np.float64,0x8008d5f91e31abf3,0x8008d5f91e31abf3,4 +np.float64,0x3fe85886f8b0b10e,0x3fee76af16f5a126,4 +np.float64,0x3fefb9b2b73f7365,0x3ff8745128fa3e3b,4 +np.float64,0x7fdf3e721f3e7ce3,0xbfb19381541ca2a8,4 +np.float64,0x3fd2768c41a4ed18,0x3fd2fe2f85a3f3a6,4 +np.float64,0xbfcabe3c6a357c78,0xbfcb239fb88bc260,4 +np.float64,0xffdffb6a3dbff6d4,0xbff7af4759fd557c,4 +np.float64,0x800817f75f302fef,0x800817f75f302fef,4 +np.float64,0xbfe6a1d1762d43a3,0xbfeb5a399a095ef3,4 +np.float64,0x7fd6f32f912de65e,0x40016dedc51aabd0,4 +np.float64,0x3fc6cb26652d964d,0x3fc7099f047d924a,4 +np.float64,0x3fe8b975d67172ec,0x3fef31946123c0e7,4 +np.float64,0xffe44a09d1e89413,0x3fdee9e5eac6e540,4 +np.float64,0xbfece76d4cb9cedb,0xbff44c34849d07ba,4 +np.float64,0x7feb76027036ec04,0x3fe08595a5e263ac,4 +np.float64,0xffe194f591a329ea,0x3fbe5bd626400a70,4 +np.float64,0xbfc170698122e0d4,0xbfc18c3de8b63565,4 +np.float64,0x3fc82b2c0f305658,0x3fc875c3b5fbcd08,4 +np.float64,0x3fd5015634aa02ac,0x3fd5cb1df07213c3,4 +np.float64,0x7fe640884b6c8110,0xbff66255a420abb5,4 +np.float64,0x5a245206b448b,0x5a245206b448b,4 +np.float64,0xffe9d9fa2f73b3f4,0xc0272b0dd34ab9bf,4 +np.float64,0x3fd990e8aab321d0,0x3fdb04cd3a29bcc3,4 +np.float64,0xde9dda8bbd3bc,0xde9dda8bbd3bc,4 +np.float64,0xbfe81b32b4703666,0xbfee029937fa9f5a,4 +np.float64,0xbfe68116886d022d,0xbfeb21c62081cb73,4 +np.float64,0x3fb8da191231b432,0x3fb8ee28c71507d3,4 +np.float64,0x3fb111395a222273,0x3fb117b57de3dea4,4 +np.float64,0xffbafadc6a35f5b8,0x3ffcc6d2370297b9,4 +np.float64,0x8002ca475b05948f,0x8002ca475b05948f,4 +np.float64,0xbfeafef57875fdeb,0xbff1fb1315676f24,4 +np.float64,0x7fcda427d73b484f,0xbff9f70212694d17,4 +np.float64,0xffe2517b3ba4a2f6,0xc029ca6707305bf4,4 +np.float64,0x7fc5ee156b2bdc2a,0xbff8384b59e9056e,4 +np.float64,0xbfec22af3278455e,0xbff3530fe25816b4,4 +np.float64,0x6b5a8c2cd6b52,0x6b5a8c2cd6b52,4 +np.float64,0xffdaf6c4b935ed8a,0x4002f00ce58affcf,4 +np.float64,0x800a41813c748303,0x800a41813c748303,4 +np.float64,0xbfd09a1269213424,0xbfd0fc0a0c5de8eb,4 +np.float64,0x7fa2cb74d42596e9,0x3fc3d40e000fa69d,4 +np.float64,0x7ff8000000000000,0x7ff8000000000000,4 +np.float64,0x3fbfbf8ed63f7f1e,0x3fbfe97bcad9f53a,4 +np.float64,0x7fe0ebba65a1d774,0x401b0f17b28618df,4 +np.float64,0x3fd02c3a25a05874,0x3fd086aa55b19c9c,4 +np.float64,0xec628f95d8c52,0xec628f95d8c52,4 +np.float64,0x3fd319329fa63264,0x3fd3afb04e0dec63,4 +np.float64,0x180e0ade301c2,0x180e0ade301c2,4 +np.float64,0xbfe8d78324f1af06,0xbfef6c66153064ee,4 +np.float64,0xffb89fa200313f48,0xbfeb96ff2d9358dc,4 +np.float64,0x7fe6abcf86ed579e,0xc0269f4de86365ec,4 +np.float64,0x7fdff8cd65bff19a,0xbfd0f7c6b9052c9a,4 +np.float64,0xbfd2e3a53d25c74a,0xbfd37520cda5f6b2,4 +np.float64,0x7fe844b096708960,0x3ff696a6182e5a7a,4 +np.float64,0x7fdce0c7a3b9c18e,0x3fd42875d69ed379,4 +np.float64,0xffba5a91cc34b520,0x4001b571e8991951,4 +np.float64,0xffe78fe4a6ef1fc9,0x3ff4507b31f5b3bc,4 +np.float64,0xbfd7047493ae08ea,0xbfd810618a53fffb,4 +np.float64,0xc6559def8cab4,0xc6559def8cab4,4 +np.float64,0x3fe75d67a76ebacf,0x3feca56817de65e4,4 +np.float64,0xffd24adbd6a495b8,0xc012c491addf2df5,4 +np.float64,0x7fed35e28dba6bc4,0x403a0fa555ff7ec6,4 +np.float64,0x80078c4afa0f1897,0x80078c4afa0f1897,4 +np.float64,0xa6ec39114dd87,0xa6ec39114dd87,4 +np.float64,0x7fb1bd33ba237a66,0x4010092bb6810fd4,4 +np.float64,0x800ecf215edd9e43,0x800ecf215edd9e43,4 +np.float64,0x3fb7c169242f82d2,0x3fb7d2ed30c462e6,4 +np.float64,0xbf71b46d60236900,0xbf71b4749a10c112,4 +np.float64,0x800d7851787af0a3,0x800d7851787af0a3,4 +np.float64,0x3fcb4a45e7369488,0x3fcbb61701a1bcec,4 +np.float64,0x3fd4e3682429c6d0,0x3fd5a9bcb916eb94,4 +np.float64,0x800497564c292ead,0x800497564c292ead,4 +np.float64,0xbfca3737a1346e70,0xbfca96a86ae5d687,4 +np.float64,0x19aa87e03356,0x19aa87e03356,4 +np.float64,0xffb2593fe624b280,0xc05fedb99b467ced,4 +np.float64,0xbfdd8748fbbb0e92,0xbfdfd1a7df17252c,4 +np.float64,0x8004c7afc7098f60,0x8004c7afc7098f60,4 +np.float64,0x7fde48b2bf3c9164,0xbfe36ef1158ed420,4 +np.float64,0xbfec8e0eb0f91c1d,0xbff3d9319705a602,4 +np.float64,0xffea1be204f437c3,0xc0144f67298c3e6f,4 +np.float64,0x7fdb906b593720d6,0xbfce99233396eda7,4 +np.float64,0x3fef0f114ffe1e22,0x3ff76072a258a51b,4 +np.float64,0x3fe3e284c8e7c50a,0x3fe6e9b05e17c999,4 +np.float64,0xbfbda9eef23b53e0,0xbfbdcc1abb443597,4 +np.float64,0x3feb6454d4f6c8aa,0x3ff26f65a85baba4,4 +np.float64,0x3fea317439f462e8,0x3ff118e2187ef33f,4 +np.float64,0x376ad0646ed5b,0x376ad0646ed5b,4 +np.float64,0x7fdd461a1c3a8c33,0x3f7ba20fb79e785f,4 +np.float64,0xebc520a3d78a4,0xebc520a3d78a4,4 +np.float64,0x3fca90fe53352200,0x3fcaf45c7fae234d,4 +np.float64,0xbfe80dd1de701ba4,0xbfede97e12cde9de,4 +np.float64,0x3fd242b00ea48560,0x3fd2c5cf9bf69a31,4 +np.float64,0x7fe46c057828d80a,0xbfe2f76837488f94,4 +np.float64,0x3fc162bea322c580,0x3fc17e517c958867,4 +np.float64,0xffebf0452ff7e08a,0x3ffc3fd95c257b54,4 +np.float64,0xffd88043c6310088,0x4008b05598d0d95f,4 +np.float64,0x800d8c49da5b1894,0x800d8c49da5b1894,4 +np.float64,0xbfed33b487ba6769,0xbff4b0ea941f8a6a,4 +np.float64,0x16b881e22d711,0x16b881e22d711,4 +np.float64,0x288bae0051177,0x288bae0051177,4 +np.float64,0xffc83a0fe8307420,0x4006eff03da17f86,4 +np.float64,0x3fc7868b252f0d18,0x3fc7cb4954290324,4 +np.float64,0xbfe195514b232aa2,0xbfe398aae6c8ed76,4 +np.float64,0x800c001ae7f80036,0x800c001ae7f80036,4 +np.float64,0x7feb82abe7370557,0xbff1e13fe6fad23c,4 +np.float64,0xffecf609cdf9ec13,0xc0112aa1805ae59e,4 +np.float64,0xffddd654f63bacaa,0x3fe46cce899f710d,4 +np.float64,0x3fe2163138642c62,0x3fe44b9c760acd4c,4 +np.float64,0x4e570dc09cae2,0x4e570dc09cae2,4 +np.float64,0x7fe9e8d091f3d1a0,0xc000fe20f8e9a4b5,4 +np.float64,0x7fe60042952c0084,0x3fd0aa740f394c2a,4 diff --git a/numpy/core/tests/data/umath-validation-set-tanh.csv b/numpy/core/tests/data/umath-validation-set-tanh.csv new file mode 100644 index 000000000000..9e3ddc60ffa6 --- /dev/null +++ b/numpy/core/tests/data/umath-validation-set-tanh.csv @@ -0,0 +1,1429 @@ +dtype,input,output,ulperrortol +np.float32,0xbe26ebb0,0xbe25752f,2 +np.float32,0xbe22ecc0,0xbe219054,2 +np.float32,0x8010a6b3,0x8010a6b3,2 +np.float32,0x3135da,0x3135da,2 +np.float32,0xbe982afc,0xbe93d727,2 +np.float32,0x16a51f,0x16a51f,2 +np.float32,0x491e56,0x491e56,2 +np.float32,0x4bf7ca,0x4bf7ca,2 +np.float32,0x3eebc21c,0x3edc65b2,2 +np.float32,0x80155c94,0x80155c94,2 +np.float32,0x3e14f626,0x3e13eb6a,2 +np.float32,0x801a238f,0x801a238f,2 +np.float32,0xbde33a80,0xbde24cf9,2 +np.float32,0xbef8439c,0xbee67a51,2 +np.float32,0x7f60d0a5,0x3f800000,2 +np.float32,0x190ee3,0x190ee3,2 +np.float32,0x80759113,0x80759113,2 +np.float32,0x800afa9f,0x800afa9f,2 +np.float32,0x7110cf,0x7110cf,2 +np.float32,0x3cf709f0,0x3cf6f6c6,2 +np.float32,0x3ef58da4,0x3ee44fa7,2 +np.float32,0xbf220ff2,0xbf0f662c,2 +np.float32,0xfd888078,0xbf800000,2 +np.float32,0xbe324734,0xbe307f9b,2 +np.float32,0x3eb5cb4f,0x3eae8560,2 +np.float32,0xbf7e7d02,0xbf425493,2 +np.float32,0x3ddcdcf0,0x3ddc02c2,2 +np.float32,0x8026d27a,0x8026d27a,2 +np.float32,0x3d4c0fb1,0x3d4be484,2 +np.float32,0xbf27d2c9,0xbf134d7c,2 +np.float32,0x8029ff80,0x8029ff80,2 +np.float32,0x7f046d2c,0x3f800000,2 +np.float32,0x13f94b,0x13f94b,2 +np.float32,0x7f4ff922,0x3f800000,2 +np.float32,0x3f4ea2ed,0x3f2b03e4,2 +np.float32,0x3e7211f0,0x3e6da8cf,2 +np.float32,0x7f39d0cf,0x3f800000,2 +np.float32,0xfee57fc6,0xbf800000,2 +np.float32,0xff6fb326,0xbf800000,2 +np.float32,0xff800000,0xbf800000,2 +np.float32,0x3f0437a4,0x3ef32fcd,2 +np.float32,0xff546d1e,0xbf800000,2 +np.float32,0x3eb5645b,0x3eae2a5c,2 +np.float32,0x3f08a6e5,0x3ef9ff8f,2 +np.float32,0x80800000,0x80800000,2 +np.float32,0x7f3413da,0x3f800000,2 +np.float32,0xfd760140,0xbf800000,2 +np.float32,0x7f3ad24a,0x3f800000,2 +np.float32,0xbf56e812,0xbf2f7f14,2 +np.float32,0xbece0338,0xbec3920a,2 +np.float32,0xbeede54a,0xbede22ae,2 +np.float32,0x7eaeb215,0x3f800000,2 +np.float32,0x3c213c00,0x3c213aab,2 +np.float32,0x7eaac217,0x3f800000,2 +np.float32,0xbf2f740e,0xbf1851a6,2 +np.float32,0x7f6ca5b8,0x3f800000,2 +np.float32,0xff42ce95,0xbf800000,2 +np.float32,0x802e4189,0x802e4189,2 +np.float32,0x80000001,0x80000001,2 +np.float32,0xbf31f298,0xbf19ebbe,2 +np.float32,0x3dcb0e6c,0x3dca64c1,2 +np.float32,0xbf29599c,0xbf145204,2 +np.float32,0x2e33f2,0x2e33f2,2 +np.float32,0x1c11e7,0x1c11e7,2 +np.float32,0x3f3b188d,0x3f1fa302,2 +np.float32,0x113300,0x113300,2 +np.float32,0x8054589e,0x8054589e,2 +np.float32,0x2a9e69,0x2a9e69,2 +np.float32,0xff513af7,0xbf800000,2 +np.float32,0x7f2e987a,0x3f800000,2 +np.float32,0x807cd426,0x807cd426,2 +np.float32,0x7f0dc4e4,0x3f800000,2 +np.float32,0x7e7c0d56,0x3f800000,2 +np.float32,0x5cb076,0x5cb076,2 +np.float32,0x80576426,0x80576426,2 +np.float32,0xff616222,0xbf800000,2 +np.float32,0xbf7accb5,0xbf40c005,2 +np.float32,0xfe4118c8,0xbf800000,2 +np.float32,0x804b9327,0x804b9327,2 +np.float32,0x3ed2b428,0x3ec79026,2 +np.float32,0x3f4a048f,0x3f286d41,2 +np.float32,0x800000,0x800000,2 +np.float32,0x7efceb9f,0x3f800000,2 +np.float32,0xbf5fe2d3,0xbf34246f,2 +np.float32,0x807e086a,0x807e086a,2 +np.float32,0x7ef5e856,0x3f800000,2 +np.float32,0xfc546f00,0xbf800000,2 +np.float32,0x3a65b890,0x3a65b88c,2 +np.float32,0x800cfa70,0x800cfa70,2 +np.float32,0x80672ea7,0x80672ea7,2 +np.float32,0x3f2bf3f2,0x3f160a12,2 +np.float32,0xbf0ab67e,0xbefd2004,2 +np.float32,0x3f2a0bb4,0x3f14c824,2 +np.float32,0xbeff5374,0xbeec12d7,2 +np.float32,0xbf221b58,0xbf0f6dff,2 +np.float32,0x7cc1f3,0x7cc1f3,2 +np.float32,0x7f234e3c,0x3f800000,2 +np.float32,0x3f60ff10,0x3f34b37d,2 +np.float32,0xbdd957f0,0xbdd887fe,2 +np.float32,0x801ce048,0x801ce048,2 +np.float32,0x7f3a8f76,0x3f800000,2 +np.float32,0xfdd13d08,0xbf800000,2 +np.float32,0x3e9af4a4,0x3e966445,2 +np.float32,0x1e55f3,0x1e55f3,2 +np.float32,0x327905,0x327905,2 +np.float32,0xbf03cf0b,0xbef28dad,2 +np.float32,0x3f0223d3,0x3eeff4f4,2 +np.float32,0xfdd96ff8,0xbf800000,2 +np.float32,0x428db8,0x428db8,2 +np.float32,0xbd74a200,0xbd7457a5,2 +np.float32,0x2a63a3,0x2a63a3,2 +np.float32,0x7e8aa9d7,0x3f800000,2 +np.float32,0x7f50b810,0x3f800000,2 +np.float32,0xbce5ec80,0xbce5dd0d,2 +np.float32,0x54711,0x54711,2 +np.float32,0x8074212a,0x8074212a,2 +np.float32,0xbf13d0ec,0xbf0551b5,2 +np.float32,0x80217f89,0x80217f89,2 +np.float32,0x3f300824,0x3f18b12f,2 +np.float32,0x7d252462,0x3f800000,2 +np.float32,0x807a154c,0x807a154c,2 +np.float32,0x8064d4b9,0x8064d4b9,2 +np.float32,0x804543b4,0x804543b4,2 +np.float32,0x4c269e,0x4c269e,2 +np.float32,0xff39823b,0xbf800000,2 +np.float32,0x3f5040b1,0x3f2be80b,2 +np.float32,0xbf7028c1,0xbf3bfee5,2 +np.float32,0x3e94eb78,0x3e90db93,2 +np.float32,0x3ccc1b40,0x3ccc1071,2 +np.float32,0xbe8796f0,0xbe8481a1,2 +np.float32,0xfc767bc0,0xbf800000,2 +np.float32,0xbdd81ed0,0xbdd75259,2 +np.float32,0xbed31bfc,0xbec7e82d,2 +np.float32,0xbf350a9e,0xbf1be1c6,2 +np.float32,0x33d41f,0x33d41f,2 +np.float32,0x3f73e076,0x3f3db0b5,2 +np.float32,0x3f800000,0x3f42f7d6,2 +np.float32,0xfee27c14,0xbf800000,2 +np.float32,0x7f6e4388,0x3f800000,2 +np.float32,0x4ea19b,0x4ea19b,2 +np.float32,0xff2d75f2,0xbf800000,2 +np.float32,0x7ee225ca,0x3f800000,2 +np.float32,0x3f31cb4b,0x3f19d2a4,2 +np.float32,0x80554a9d,0x80554a9d,2 +np.float32,0x3f4d57fa,0x3f2a4c03,2 +np.float32,0x3eac6a88,0x3ea62e72,2 +np.float32,0x773520,0x773520,2 +np.float32,0x8079c20a,0x8079c20a,2 +np.float32,0xfeb1eb94,0xbf800000,2 +np.float32,0xfe8d81c0,0xbf800000,2 +np.float32,0xfeed6902,0xbf800000,2 +np.float32,0x8066bb65,0x8066bb65,2 +np.float32,0x7f800000,0x3f800000,2 +np.float32,0x1,0x1,2 +np.float32,0x3f2c66a4,0x3f16554a,2 +np.float32,0x3cd231,0x3cd231,2 +np.float32,0x3e932a64,0x3e8f3e0c,2 +np.float32,0xbf3ab1c3,0xbf1f6420,2 +np.float32,0xbc902b20,0xbc902751,2 +np.float32,0x7dac0a5b,0x3f800000,2 +np.float32,0x3f2b7e06,0x3f15bc93,2 +np.float32,0x75de0,0x75de0,2 +np.float32,0x8020b7bc,0x8020b7bc,2 +np.float32,0x3f257cda,0x3f11bb6b,2 +np.float32,0x807480e5,0x807480e5,2 +np.float32,0xfe00d758,0xbf800000,2 +np.float32,0xbd9b54e0,0xbd9b08cd,2 +np.float32,0x4dfbe3,0x4dfbe3,2 +np.float32,0xff645788,0xbf800000,2 +np.float32,0xbe92c80a,0xbe8ee360,2 +np.float32,0x3eb9b400,0x3eb1f77c,2 +np.float32,0xff20b69c,0xbf800000,2 +np.float32,0x623c28,0x623c28,2 +np.float32,0xff235748,0xbf800000,2 +np.float32,0xbf3bbc56,0xbf2006f3,2 +np.float32,0x7e6f78b1,0x3f800000,2 +np.float32,0x7e1584e9,0x3f800000,2 +np.float32,0xff463423,0xbf800000,2 +np.float32,0x8002861e,0x8002861e,2 +np.float32,0xbf0491d8,0xbef3bb6a,2 +np.float32,0x7ea3bc17,0x3f800000,2 +np.float32,0xbedde7ea,0xbed0fb49,2 +np.float32,0xbf4bac48,0xbf295c8b,2 +np.float32,0xff28e276,0xbf800000,2 +np.float32,0x7e8f3bf5,0x3f800000,2 +np.float32,0xbf0a4a73,0xbefc7c9d,2 +np.float32,0x7ec5bd96,0x3f800000,2 +np.float32,0xbf4c22e8,0xbf299f2c,2 +np.float32,0x3e3970a0,0x3e377064,2 +np.float32,0x3ecb1118,0x3ec10c88,2 +np.float32,0xff548a7a,0xbf800000,2 +np.float32,0xfe8ec550,0xbf800000,2 +np.float32,0x3e158985,0x3e147bb2,2 +np.float32,0x7eb79ad7,0x3f800000,2 +np.float32,0xbe811384,0xbe7cd1ab,2 +np.float32,0xbdc4b9e8,0xbdc41f94,2 +np.float32,0xe0fd5,0xe0fd5,2 +np.float32,0x3f2485f2,0x3f11142b,2 +np.float32,0xfdd3c3d8,0xbf800000,2 +np.float32,0xfe8458e6,0xbf800000,2 +np.float32,0x3f06e398,0x3ef74dd8,2 +np.float32,0xff4752cf,0xbf800000,2 +np.float32,0x6998e3,0x6998e3,2 +np.float32,0x626751,0x626751,2 +np.float32,0x806631d6,0x806631d6,2 +np.float32,0xbf0c3cf4,0xbeff6c54,2 +np.float32,0x802860f8,0x802860f8,2 +np.float32,0xff2952cb,0xbf800000,2 +np.float32,0xff31d40b,0xbf800000,2 +np.float32,0x7c389473,0x3f800000,2 +np.float32,0x3dcd2f1b,0x3dcc8010,2 +np.float32,0x3d70c29f,0x3d707bbc,2 +np.float32,0x3f6bd386,0x3f39f979,2 +np.float32,0x1efec9,0x1efec9,2 +np.float32,0x3f675518,0x3f37d338,2 +np.float32,0x5fdbe3,0x5fdbe3,2 +np.float32,0x5d684e,0x5d684e,2 +np.float32,0xbedfe748,0xbed2a4c7,2 +np.float32,0x3f0cb07a,0x3f000cdc,2 +np.float32,0xbf77151e,0xbf3f1f5d,2 +np.float32,0x7f038ea0,0x3f800000,2 +np.float32,0x3ea91be9,0x3ea3376f,2 +np.float32,0xbdf20738,0xbdf0e861,2 +np.float32,0x807ea380,0x807ea380,2 +np.float32,0x2760ca,0x2760ca,2 +np.float32,0x7f20a544,0x3f800000,2 +np.float32,0x76ed83,0x76ed83,2 +np.float32,0x15a441,0x15a441,2 +np.float32,0x74c76d,0x74c76d,2 +np.float32,0xff3d5c2a,0xbf800000,2 +np.float32,0x7f6a76a6,0x3f800000,2 +np.float32,0x3eb87067,0x3eb0dabe,2 +np.float32,0xbf515cfa,0xbf2c83af,2 +np.float32,0xbdececc0,0xbdebdf9d,2 +np.float32,0x7f51b7c2,0x3f800000,2 +np.float32,0x3eb867ac,0x3eb0d30d,2 +np.float32,0xff50fd84,0xbf800000,2 +np.float32,0x806945e9,0x806945e9,2 +np.float32,0x298eed,0x298eed,2 +np.float32,0x441f53,0x441f53,2 +np.float32,0x8066d4b0,0x8066d4b0,2 +np.float32,0x3f6a479c,0x3f393dae,2 +np.float32,0xbf6ce2a7,0xbf3a7921,2 +np.float32,0x8064c3cf,0x8064c3cf,2 +np.float32,0xbf2d8146,0xbf170dfd,2 +np.float32,0x3b0e82,0x3b0e82,2 +np.float32,0xbea97574,0xbea387dc,2 +np.float32,0x67ad15,0x67ad15,2 +np.float32,0xbf68478f,0xbf38485a,2 +np.float32,0xff6f593b,0xbf800000,2 +np.float32,0xbeda26f2,0xbecdd806,2 +np.float32,0xbd216d50,0xbd2157ee,2 +np.float32,0x7a8544db,0x3f800000,2 +np.float32,0x801df20b,0x801df20b,2 +np.float32,0xbe14ba24,0xbe13b0a8,2 +np.float32,0xfdc6d8a8,0xbf800000,2 +np.float32,0x1d6b49,0x1d6b49,2 +np.float32,0x7f5ff1b8,0x3f800000,2 +np.float32,0x3f75e032,0x3f3e9625,2 +np.float32,0x7f2c5687,0x3f800000,2 +np.float32,0x3d95fb6c,0x3d95b6ee,2 +np.float32,0xbea515e4,0xbe9f97c8,2 +np.float32,0x7f2b2cd7,0x3f800000,2 +np.float32,0x3f076f7a,0x3ef8241e,2 +np.float32,0x5178ca,0x5178ca,2 +np.float32,0xbeb5976a,0xbeae5781,2 +np.float32,0x3e3c3563,0x3e3a1e13,2 +np.float32,0xbd208530,0xbd20702a,2 +np.float32,0x3eb03b04,0x3ea995ef,2 +np.float32,0x17fb9c,0x17fb9c,2 +np.float32,0xfca68e40,0xbf800000,2 +np.float32,0xbf5e7433,0xbf336a9f,2 +np.float32,0xff5b8d3d,0xbf800000,2 +np.float32,0x8003121d,0x8003121d,2 +np.float32,0xbe6dd344,0xbe69a3b0,2 +np.float32,0x67cc4,0x67cc4,2 +np.float32,0x9b01d,0x9b01d,2 +np.float32,0x127c13,0x127c13,2 +np.float32,0xfea5e3d6,0xbf800000,2 +np.float32,0xbdf5c610,0xbdf499c1,2 +np.float32,0x3aff4c00,0x3aff4beb,2 +np.float32,0x3b00afd0,0x3b00afc5,2 +np.float32,0x479618,0x479618,2 +np.float32,0x801cbd05,0x801cbd05,2 +np.float32,0x3ec9249f,0x3ebf6579,2 +np.float32,0x3535c4,0x3535c4,2 +np.float32,0xbeb4f662,0xbeadc915,2 +np.float32,0x8006fda6,0x8006fda6,2 +np.float32,0xbf4f3097,0xbf2b5239,2 +np.float32,0xbf3cb9a8,0xbf20a0e9,2 +np.float32,0x32ced0,0x32ced0,2 +np.float32,0x7ea34e76,0x3f800000,2 +np.float32,0x80063046,0x80063046,2 +np.float32,0x80727e8b,0x80727e8b,2 +np.float32,0xfd6b5780,0xbf800000,2 +np.float32,0x80109815,0x80109815,2 +np.float32,0xfdcc8a78,0xbf800000,2 +np.float32,0x81562,0x81562,2 +np.float32,0x803dfacc,0x803dfacc,2 +np.float32,0xbe204318,0xbe1ef75f,2 +np.float32,0xbf745d34,0xbf3de8e2,2 +np.float32,0xff13fdcc,0xbf800000,2 +np.float32,0x7f75ba8c,0x3f800000,2 +np.float32,0x806c04b4,0x806c04b4,2 +np.float32,0x3ec61ca6,0x3ebcc877,2 +np.float32,0xbeaea984,0xbea8301f,2 +np.float32,0xbf4dcd0e,0xbf2a8d34,2 +np.float32,0x802a01d3,0x802a01d3,2 +np.float32,0xbf747be5,0xbf3df6ad,2 +np.float32,0xbf75cbd2,0xbf3e8d0f,2 +np.float32,0x7db86576,0x3f800000,2 +np.float32,0xff49a2c3,0xbf800000,2 +np.float32,0xbedc5314,0xbecfa978,2 +np.float32,0x8078877b,0x8078877b,2 +np.float32,0xbead4824,0xbea6f499,2 +np.float32,0xbf3926e3,0xbf1e716c,2 +np.float32,0x807f4a1c,0x807f4a1c,2 +np.float32,0x7f2cd8fd,0x3f800000,2 +np.float32,0x806cfcca,0x806cfcca,2 +np.float32,0xff1aa048,0xbf800000,2 +np.float32,0x7eb9ea08,0x3f800000,2 +np.float32,0xbf1034bc,0xbf02ab3a,2 +np.float32,0xbd087830,0xbd086b44,2 +np.float32,0x7e071034,0x3f800000,2 +np.float32,0xbefcc9de,0xbeea122f,2 +np.float32,0x80796d7a,0x80796d7a,2 +np.float32,0x33ce46,0x33ce46,2 +np.float32,0x8074a783,0x8074a783,2 +np.float32,0xbe95a56a,0xbe918691,2 +np.float32,0xbf2ff3f4,0xbf18a42d,2 +np.float32,0x1633e9,0x1633e9,2 +np.float32,0x7f0f104b,0x3f800000,2 +np.float32,0xbf800000,0xbf42f7d6,2 +np.float32,0x3d2cd6,0x3d2cd6,2 +np.float32,0xfed43e16,0xbf800000,2 +np.float32,0x3ee6faec,0x3ed87d2c,2 +np.float32,0x3f2c32d0,0x3f163352,2 +np.float32,0xff4290c0,0xbf800000,2 +np.float32,0xbf66500e,0xbf37546a,2 +np.float32,0x7dfb8fe3,0x3f800000,2 +np.float32,0x3f20ba5d,0x3f0e7b16,2 +np.float32,0xff30c7ae,0xbf800000,2 +np.float32,0x1728a4,0x1728a4,2 +np.float32,0x340d82,0x340d82,2 +np.float32,0xff7870b7,0xbf800000,2 +np.float32,0xbeac6ac4,0xbea62ea7,2 +np.float32,0xbef936fc,0xbee73c36,2 +np.float32,0x3ec7e12c,0x3ebe4ef8,2 +np.float32,0x80673488,0x80673488,2 +np.float32,0xfdf14c90,0xbf800000,2 +np.float32,0x3f182568,0x3f08726e,2 +np.float32,0x7ed7dcd0,0x3f800000,2 +np.float32,0x3de4da34,0x3de3e790,2 +np.float32,0xff7fffff,0xbf800000,2 +np.float32,0x4ff90c,0x4ff90c,2 +np.float32,0x3efb0d1c,0x3ee8b1d6,2 +np.float32,0xbf66e952,0xbf379ef4,2 +np.float32,0xba9dc,0xba9dc,2 +np.float32,0xff67c766,0xbf800000,2 +np.float32,0x7f1ffc29,0x3f800000,2 +np.float32,0x3f51c906,0x3f2cbe99,2 +np.float32,0x3f2e5792,0x3f179968,2 +np.float32,0x3ecb9750,0x3ec17fa0,2 +np.float32,0x7f3fcefc,0x3f800000,2 +np.float32,0xbe4e30fc,0xbe4b72f9,2 +np.float32,0x7e9bc4ce,0x3f800000,2 +np.float32,0x7e70aa1f,0x3f800000,2 +np.float32,0x14c6e9,0x14c6e9,2 +np.float32,0xbcf327c0,0xbcf3157a,2 +np.float32,0xff1fd204,0xbf800000,2 +np.float32,0x7d934a03,0x3f800000,2 +np.float32,0x8028bf1e,0x8028bf1e,2 +np.float32,0x7f0800b7,0x3f800000,2 +np.float32,0xfe04825c,0xbf800000,2 +np.float32,0x807210ac,0x807210ac,2 +np.float32,0x3f7faf7c,0x3f42d5fd,2 +np.float32,0x3e04a543,0x3e03e899,2 +np.float32,0x3e98ea15,0x3e94863e,2 +np.float32,0x3d2a2e48,0x3d2a153b,2 +np.float32,0x7fa00000,0x7fe00000,2 +np.float32,0x20a488,0x20a488,2 +np.float32,0x3f6ba86a,0x3f39e51a,2 +np.float32,0x0,0x0,2 +np.float32,0x3e892ddd,0x3e85fcfe,2 +np.float32,0x3e2da627,0x3e2c00e0,2 +np.float32,0xff000a50,0xbf800000,2 +np.float32,0x3eb749f4,0x3eafd739,2 +np.float32,0x8024c0ae,0x8024c0ae,2 +np.float32,0xfc8f3b40,0xbf800000,2 +np.float32,0xbf685fc7,0xbf385405,2 +np.float32,0x3f1510e6,0x3f063a4f,2 +np.float32,0x3f68e8ad,0x3f3895d8,2 +np.float32,0x3dba8608,0x3dba0271,2 +np.float32,0xbf16ea10,0xbf079017,2 +np.float32,0xb3928,0xb3928,2 +np.float32,0xfe447c00,0xbf800000,2 +np.float32,0x3db9cd57,0x3db94b45,2 +np.float32,0x803b66b0,0x803b66b0,2 +np.float32,0x805b5e02,0x805b5e02,2 +np.float32,0x7ec93f61,0x3f800000,2 +np.float32,0x8005a126,0x8005a126,2 +np.float32,0x6d8888,0x6d8888,2 +np.float32,0x3e21b7de,0x3e206314,2 +np.float32,0xbec9c31e,0xbebfedc2,2 +np.float32,0xbea88aa8,0xbea2b4e5,2 +np.float32,0x3d8fc310,0x3d8f86bb,2 +np.float32,0xbf3cc68a,0xbf20a8b8,2 +np.float32,0x432690,0x432690,2 +np.float32,0xbe51d514,0xbe4ef1a3,2 +np.float32,0xbcda6d20,0xbcda5fe1,2 +np.float32,0xfe24e458,0xbf800000,2 +np.float32,0xfedc8c14,0xbf800000,2 +np.float32,0x7f7e9bd4,0x3f800000,2 +np.float32,0x3ebcc880,0x3eb4ab44,2 +np.float32,0xbe0aa490,0xbe09cd44,2 +np.float32,0x3dc9158c,0x3dc870c3,2 +np.float32,0x3e5c319e,0x3e58dc90,2 +np.float32,0x1d4527,0x1d4527,2 +np.float32,0x2dbf5,0x2dbf5,2 +np.float32,0xbf1f121f,0xbf0d5534,2 +np.float32,0x7e3e9ab5,0x3f800000,2 +np.float32,0x7f74b5c1,0x3f800000,2 +np.float32,0xbf6321ba,0xbf35c42b,2 +np.float32,0xbe5c7488,0xbe591c79,2 +np.float32,0x7e7b02cd,0x3f800000,2 +np.float32,0xfe7cbfa4,0xbf800000,2 +np.float32,0xbeace360,0xbea69a86,2 +np.float32,0x7e149b00,0x3f800000,2 +np.float32,0xbf61a700,0xbf35079a,2 +np.float32,0x7eb592a7,0x3f800000,2 +np.float32,0x3f2105e6,0x3f0eaf30,2 +np.float32,0xfd997a88,0xbf800000,2 +np.float32,0xff5d093b,0xbf800000,2 +np.float32,0x63aede,0x63aede,2 +np.float32,0x6907ee,0x6907ee,2 +np.float32,0xbf7578ee,0xbf3e680f,2 +np.float32,0xfea971e8,0xbf800000,2 +np.float32,0x3f21d0f5,0x3f0f3aed,2 +np.float32,0x3a50e2,0x3a50e2,2 +np.float32,0x7f0f5b1e,0x3f800000,2 +np.float32,0x805b9765,0x805b9765,2 +np.float32,0xbe764ab8,0xbe71a664,2 +np.float32,0x3eafac7f,0x3ea91701,2 +np.float32,0x807f4130,0x807f4130,2 +np.float32,0x7c5f31,0x7c5f31,2 +np.float32,0xbdbe0e30,0xbdbd8300,2 +np.float32,0x7ecfe4e0,0x3f800000,2 +np.float32,0xff7cb628,0xbf800000,2 +np.float32,0xff1842bc,0xbf800000,2 +np.float32,0xfd4163c0,0xbf800000,2 +np.float32,0x800e11f7,0x800e11f7,2 +np.float32,0x7f3adec8,0x3f800000,2 +np.float32,0x7f597514,0x3f800000,2 +np.float32,0xbe986e14,0xbe9414a4,2 +np.float32,0x800fa9d7,0x800fa9d7,2 +np.float32,0xff5b79c4,0xbf800000,2 +np.float32,0x80070565,0x80070565,2 +np.float32,0xbee5628e,0xbed72d60,2 +np.float32,0x3f438ef2,0x3f24b3ca,2 +np.float32,0xcda91,0xcda91,2 +np.float32,0x7e64151a,0x3f800000,2 +np.float32,0xbe95d584,0xbe91b2c7,2 +np.float32,0x8022c2a1,0x8022c2a1,2 +np.float32,0x7e7097bf,0x3f800000,2 +np.float32,0x80139035,0x80139035,2 +np.float32,0x804de2cb,0x804de2cb,2 +np.float32,0xfde5d178,0xbf800000,2 +np.float32,0x6d238,0x6d238,2 +np.float32,0x807abedc,0x807abedc,2 +np.float32,0x3f450a12,0x3f259129,2 +np.float32,0x3ef1c120,0x3ee141f2,2 +np.float32,0xfeb64dae,0xbf800000,2 +np.float32,0x8001732c,0x8001732c,2 +np.float32,0x3f76062e,0x3f3ea711,2 +np.float32,0x3eddd550,0x3ed0ebc8,2 +np.float32,0xff5ca1d4,0xbf800000,2 +np.float32,0xbf49dc5e,0xbf285673,2 +np.float32,0x7e9e5438,0x3f800000,2 +np.float32,0x7e83625e,0x3f800000,2 +np.float32,0x3f5dc41c,0x3f3310da,2 +np.float32,0x3f583efa,0x3f30342f,2 +np.float32,0xbe26bf88,0xbe254a2d,2 +np.float32,0xff1e0beb,0xbf800000,2 +np.float32,0xbe2244c8,0xbe20ec86,2 +np.float32,0xff0b1630,0xbf800000,2 +np.float32,0xff338dd6,0xbf800000,2 +np.float32,0x3eafc22c,0x3ea92a51,2 +np.float32,0x800ea07f,0x800ea07f,2 +np.float32,0x3f46f006,0x3f26aa7e,2 +np.float32,0x3e5f57cd,0x3e5bde16,2 +np.float32,0xbf1b2d8e,0xbf0a9a93,2 +np.float32,0xfeacdbe0,0xbf800000,2 +np.float32,0x7e5ea4bc,0x3f800000,2 +np.float32,0xbf51cbe2,0xbf2cc027,2 +np.float32,0x8073644c,0x8073644c,2 +np.float32,0xff2d6bfe,0xbf800000,2 +np.float32,0x3f65f0f6,0x3f37260a,2 +np.float32,0xff4b37a6,0xbf800000,2 +np.float32,0x712df7,0x712df7,2 +np.float32,0x7f71ef17,0x3f800000,2 +np.float32,0x8042245c,0x8042245c,2 +np.float32,0x3e5dde7b,0x3e5a760d,2 +np.float32,0x8069317d,0x8069317d,2 +np.float32,0x807932dd,0x807932dd,2 +np.float32,0x802f847e,0x802f847e,2 +np.float32,0x7e9300,0x7e9300,2 +np.float32,0x8040b4ab,0x8040b4ab,2 +np.float32,0xff76ef8e,0xbf800000,2 +np.float32,0x4aae3a,0x4aae3a,2 +np.float32,0x8058de73,0x8058de73,2 +np.float32,0x7e4d58c0,0x3f800000,2 +np.float32,0x3d811b30,0x3d80ef79,2 +np.float32,0x7ec952cc,0x3f800000,2 +np.float32,0xfe162b1c,0xbf800000,2 +np.float32,0x3f0f1187,0x3f01d367,2 +np.float32,0xbf2f3458,0xbf182878,2 +np.float32,0x5ceb14,0x5ceb14,2 +np.float32,0xbec29476,0xbeb9b939,2 +np.float32,0x3e71f943,0x3e6d9176,2 +np.float32,0x3ededefc,0x3ed1c909,2 +np.float32,0x805df6ac,0x805df6ac,2 +np.float32,0x3e5ae2c8,0x3e579ca8,2 +np.float32,0x3f6ad2c3,0x3f397fdf,2 +np.float32,0x7d5f94d3,0x3f800000,2 +np.float32,0xbeec7fe4,0xbedd0037,2 +np.float32,0x3f645304,0x3f365b0d,2 +np.float32,0xbf69a087,0xbf38edef,2 +np.float32,0x8025102e,0x8025102e,2 +np.float32,0x800db486,0x800db486,2 +np.float32,0x4df6c7,0x4df6c7,2 +np.float32,0x806d8cdd,0x806d8cdd,2 +np.float32,0x7f0c78cc,0x3f800000,2 +np.float32,0x7e1cf70b,0x3f800000,2 +np.float32,0x3e0ae570,0x3e0a0cf7,2 +np.float32,0x80176ef8,0x80176ef8,2 +np.float32,0x3f38b60c,0x3f1e2bbb,2 +np.float32,0x3d3071e0,0x3d3055f5,2 +np.float32,0x3ebfcfdd,0x3eb750a9,2 +np.float32,0xfe2cdec0,0xbf800000,2 +np.float32,0x7eeb2eed,0x3f800000,2 +np.float32,0x8026c904,0x8026c904,2 +np.float32,0xbec79bde,0xbebe133a,2 +np.float32,0xbf7dfab6,0xbf421d47,2 +np.float32,0x805b3cfd,0x805b3cfd,2 +np.float32,0xfdfcfb68,0xbf800000,2 +np.float32,0xbd537ec0,0xbd534eaf,2 +np.float32,0x52ce73,0x52ce73,2 +np.float32,0xfeac6ea6,0xbf800000,2 +np.float32,0x3f2c2990,0x3f162d41,2 +np.float32,0x3e3354e0,0x3e318539,2 +np.float32,0x802db22b,0x802db22b,2 +np.float32,0x7f0faa83,0x3f800000,2 +np.float32,0x7f10e161,0x3f800000,2 +np.float32,0x7f165c60,0x3f800000,2 +np.float32,0xbf5a756f,0xbf315c82,2 +np.float32,0x7f5a4b68,0x3f800000,2 +np.float32,0xbd77fbf0,0xbd77ae7c,2 +np.float32,0x65d83c,0x65d83c,2 +np.float32,0x3e5f28,0x3e5f28,2 +np.float32,0x8040ec92,0x8040ec92,2 +np.float32,0xbf2b41a6,0xbf1594d5,2 +np.float32,0x7f2f88f1,0x3f800000,2 +np.float32,0xfdb64ab8,0xbf800000,2 +np.float32,0xbf7a3ff1,0xbf4082f5,2 +np.float32,0x1948fc,0x1948fc,2 +np.float32,0x802c1039,0x802c1039,2 +np.float32,0x80119274,0x80119274,2 +np.float32,0x7e885d7b,0x3f800000,2 +np.float32,0xfaf6a,0xfaf6a,2 +np.float32,0x3eba28c4,0x3eb25e1d,2 +np.float32,0x3e4df370,0x3e4b37da,2 +np.float32,0xbf19eff6,0xbf09b97d,2 +np.float32,0xbeddd3c6,0xbed0ea7f,2 +np.float32,0xff6fc971,0xbf800000,2 +np.float32,0x7e93de29,0x3f800000,2 +np.float32,0x3eb12332,0x3eaa6485,2 +np.float32,0x3eb7c6e4,0x3eb04563,2 +np.float32,0x4a67ee,0x4a67ee,2 +np.float32,0xff1cafde,0xbf800000,2 +np.float32,0x3f5e2812,0x3f3343da,2 +np.float32,0x3f060e04,0x3ef605d4,2 +np.float32,0x3e9027d8,0x3e8c76a6,2 +np.float32,0xe2d33,0xe2d33,2 +np.float32,0xff4c94fc,0xbf800000,2 +np.float32,0xbf574908,0xbf2fb26b,2 +np.float32,0xbf786c08,0xbf3fb68e,2 +np.float32,0x8011ecab,0x8011ecab,2 +np.float32,0xbf061c6a,0xbef61bfa,2 +np.float32,0x7eea5f9d,0x3f800000,2 +np.float32,0x3ea2e19c,0x3e9d99a5,2 +np.float32,0x8071550c,0x8071550c,2 +np.float32,0x41c70b,0x41c70b,2 +np.float32,0x80291fc8,0x80291fc8,2 +np.float32,0x43b1ec,0x43b1ec,2 +np.float32,0x32f5a,0x32f5a,2 +np.float32,0xbe9310ec,0xbe8f2692,2 +np.float32,0x7f75f6bf,0x3f800000,2 +np.float32,0x3e6642a6,0x3e6274d2,2 +np.float32,0x3ecb88e0,0x3ec1733f,2 +np.float32,0x804011b6,0x804011b6,2 +np.float32,0x80629cca,0x80629cca,2 +np.float32,0x8016b914,0x8016b914,2 +np.float32,0xbdd05fc0,0xbdcfa870,2 +np.float32,0x807b824d,0x807b824d,2 +np.float32,0xfeec2576,0xbf800000,2 +np.float32,0xbf54bf22,0xbf2e584c,2 +np.float32,0xbf185eb0,0xbf089b6b,2 +np.float32,0xfbc09480,0xbf800000,2 +np.float32,0x3f413054,0x3f234e25,2 +np.float32,0x7e9e32b8,0x3f800000,2 +np.float32,0x266296,0x266296,2 +np.float32,0x460284,0x460284,2 +np.float32,0x3eb0b056,0x3ea9fe5a,2 +np.float32,0x1a7be5,0x1a7be5,2 +np.float32,0x7f099895,0x3f800000,2 +np.float32,0x3f3614f0,0x3f1c88ef,2 +np.float32,0x7e757dc2,0x3f800000,2 +np.float32,0x801fc91e,0x801fc91e,2 +np.float32,0x3f5ce37d,0x3f329ddb,2 +np.float32,0x3e664d70,0x3e627f15,2 +np.float32,0xbf38ed78,0xbf1e4dfa,2 +np.float32,0xbf5c563d,0xbf325543,2 +np.float32,0xbe91cc54,0xbe8dfb24,2 +np.float32,0x3d767fbe,0x3d7633ac,2 +np.float32,0xbf6aeb40,0xbf398b7f,2 +np.float32,0x7f40508b,0x3f800000,2 +np.float32,0x2650df,0x2650df,2 +np.float32,0xbe8cea3c,0xbe897628,2 +np.float32,0x80515af8,0x80515af8,2 +np.float32,0x7f423986,0x3f800000,2 +np.float32,0xbdf250e8,0xbdf1310c,2 +np.float32,0xfe89288a,0xbf800000,2 +np.float32,0x397b3b,0x397b3b,2 +np.float32,0x7e5e91b0,0x3f800000,2 +np.float32,0x6866e2,0x6866e2,2 +np.float32,0x7f4d8877,0x3f800000,2 +np.float32,0x3e6c4a21,0x3e682ee3,2 +np.float32,0xfc3d5980,0xbf800000,2 +np.float32,0x7eae2cd0,0x3f800000,2 +np.float32,0xbf241222,0xbf10c579,2 +np.float32,0xfebc02de,0xbf800000,2 +np.float32,0xff6e0645,0xbf800000,2 +np.float32,0x802030b6,0x802030b6,2 +np.float32,0x7ef9a441,0x3f800000,2 +np.float32,0x3fcf9f,0x3fcf9f,2 +np.float32,0xbf0ccf13,0xbf0023cc,2 +np.float32,0xfefee688,0xbf800000,2 +np.float32,0xbf6c8e0c,0xbf3a5160,2 +np.float32,0xfe749c28,0xbf800000,2 +np.float32,0x7f7fffff,0x3f800000,2 +np.float32,0x58c1a0,0x58c1a0,2 +np.float32,0x3f2de0a1,0x3f174c17,2 +np.float32,0xbf5f7138,0xbf33eb03,2 +np.float32,0x3da15270,0x3da0fd3c,2 +np.float32,0x3da66560,0x3da607e4,2 +np.float32,0xbf306f9a,0xbf18f3c6,2 +np.float32,0x3e81a4de,0x3e7de293,2 +np.float32,0xbebb5fb8,0xbeb36f1a,2 +np.float32,0x14bf64,0x14bf64,2 +np.float32,0xbeac46c6,0xbea60e73,2 +np.float32,0xbdcdf210,0xbdcd4111,2 +np.float32,0x3f7e3cd9,0x3f42395e,2 +np.float32,0xbc4be640,0xbc4be38e,2 +np.float32,0xff5f53b4,0xbf800000,2 +np.float32,0xbf1315ae,0xbf04c90b,2 +np.float32,0x80000000,0x80000000,2 +np.float32,0xbf6a4149,0xbf393aaa,2 +np.float32,0x3f66b8ee,0x3f378772,2 +np.float32,0xff29293e,0xbf800000,2 +np.float32,0xbcc989c0,0xbcc97f58,2 +np.float32,0xbd9a1b70,0xbd99d125,2 +np.float32,0xfef353cc,0xbf800000,2 +np.float32,0xbdc30cf0,0xbdc27683,2 +np.float32,0xfdfd6768,0xbf800000,2 +np.float32,0x7ebac44c,0x3f800000,2 +np.float32,0xff453cd6,0xbf800000,2 +np.float32,0x3ef07720,0x3ee03787,2 +np.float32,0x80219c14,0x80219c14,2 +np.float32,0x805553a8,0x805553a8,2 +np.float32,0x80703928,0x80703928,2 +np.float32,0xff16d3a7,0xbf800000,2 +np.float32,0x3f1472bc,0x3f05c77b,2 +np.float32,0x3eeea37a,0x3edebcf9,2 +np.float32,0x3db801e6,0x3db7838d,2 +np.float32,0x800870d2,0x800870d2,2 +np.float32,0xbea1172c,0xbe9bfa32,2 +np.float32,0x3f1f5e7c,0x3f0d8a42,2 +np.float32,0x123cdb,0x123cdb,2 +np.float32,0x7f6e6b06,0x3f800000,2 +np.float32,0x3ed80573,0x3ecc0def,2 +np.float32,0xfea31b82,0xbf800000,2 +np.float32,0x6744e0,0x6744e0,2 +np.float32,0x695e8b,0x695e8b,2 +np.float32,0xbee3888a,0xbed5a67d,2 +np.float32,0x7f64bc2a,0x3f800000,2 +np.float32,0x7f204244,0x3f800000,2 +np.float32,0x7f647102,0x3f800000,2 +np.float32,0x3dd8ebc0,0x3dd81d03,2 +np.float32,0x801e7ab1,0x801e7ab1,2 +np.float32,0x7d034b56,0x3f800000,2 +np.float32,0x7fc00000,0x7fc00000,2 +np.float32,0x80194193,0x80194193,2 +np.float32,0xfe31c8d4,0xbf800000,2 +np.float32,0x7fc0c4,0x7fc0c4,2 +np.float32,0xd95bf,0xd95bf,2 +np.float32,0x7e4f991d,0x3f800000,2 +np.float32,0x7fc563,0x7fc563,2 +np.float32,0xbe3fcccc,0xbe3d968a,2 +np.float32,0xfdaaa1c8,0xbf800000,2 +np.float32,0xbf48e449,0xbf27c949,2 +np.float32,0x3eb6c584,0x3eaf625e,2 +np.float32,0xbea35a74,0xbe9e0702,2 +np.float32,0x3eeab47a,0x3edb89d5,2 +np.float32,0xbed99556,0xbecd5de5,2 +np.float64,0xbfb94a81e0329500,0xbfb935867ba761fe,2 +np.float64,0xbfec132f1678265e,0xbfe6900eb097abc3,2 +np.float64,0x5685ea72ad0be,0x5685ea72ad0be,2 +np.float64,0xbfd74d3169ae9a62,0xbfd652e09b9daf32,2 +np.float64,0xbfe28df53d651bea,0xbfe0b8a7f50ab433,2 +np.float64,0x0,0x0,2 +np.float64,0xbfed912738bb224e,0xbfe749e3732831ae,2 +np.float64,0x7fcc6faed838df5d,0x3ff0000000000000,2 +np.float64,0xbfe95fe9a432bfd3,0xbfe51f6349919910,2 +np.float64,0xbfc4d5900b29ab20,0xbfc4a6f496179b8b,2 +np.float64,0xbfcd6025033ac04c,0xbfccded7b34b49b0,2 +np.float64,0xbfdfa655b43f4cac,0xbfdd4ca1e5bb9db8,2 +np.float64,0xe7ea5c7fcfd4c,0xe7ea5c7fcfd4c,2 +np.float64,0xffa5449ca42a8940,0xbff0000000000000,2 +np.float64,0xffe63294c1ac6529,0xbff0000000000000,2 +np.float64,0x7feb9cbae7f73975,0x3ff0000000000000,2 +np.float64,0x800eb07c3e3d60f9,0x800eb07c3e3d60f9,2 +np.float64,0x3fc95777e932aef0,0x3fc9040391e20c00,2 +np.float64,0x800736052dee6c0b,0x800736052dee6c0b,2 +np.float64,0x3fe9ae4afd335c96,0x3fe54b569bab45c7,2 +np.float64,0x7fee4c94217c9927,0x3ff0000000000000,2 +np.float64,0x80094b594bd296b3,0x80094b594bd296b3,2 +np.float64,0xffe5adbcee6b5b7a,0xbff0000000000000,2 +np.float64,0x3fecb8eab47971d5,0x3fe6e236be6f27e9,2 +np.float64,0x44956914892ae,0x44956914892ae,2 +np.float64,0xbfe3bd18ef677a32,0xbfe190bf1e07200c,2 +np.float64,0x800104e5b46209cc,0x800104e5b46209cc,2 +np.float64,0x8008fbcecf71f79e,0x8008fbcecf71f79e,2 +np.float64,0x800f0a46a0be148d,0x800f0a46a0be148d,2 +np.float64,0x7fe657a0702caf40,0x3ff0000000000000,2 +np.float64,0xffd3ff1a9027fe36,0xbff0000000000000,2 +np.float64,0x3fe78bc87bef1790,0x3fe40d2e63aaf029,2 +np.float64,0x7feeabdc4c7d57b8,0x3ff0000000000000,2 +np.float64,0xbfabd28d8437a520,0xbfabcb8ce03a0e56,2 +np.float64,0xbfddc3a133bb8742,0xbfdbc9fdb2594451,2 +np.float64,0x7fec911565b9222a,0x3ff0000000000000,2 +np.float64,0x71302604e2605,0x71302604e2605,2 +np.float64,0xee919d2bdd234,0xee919d2bdd234,2 +np.float64,0xbfc04fcff3209fa0,0xbfc0395a739a2ce4,2 +np.float64,0xffe4668a36e8cd14,0xbff0000000000000,2 +np.float64,0xbfeeafeebefd5fde,0xbfe7cd5f3d61a3ec,2 +np.float64,0x7fddb34219bb6683,0x3ff0000000000000,2 +np.float64,0xbfd2cac6cba5958e,0xbfd24520abb2ff36,2 +np.float64,0xbfb857e49630afc8,0xbfb8452d5064dec2,2 +np.float64,0x3fd2dbf90b25b7f2,0x3fd254eaf48484c2,2 +np.float64,0x800af65c94f5ecba,0x800af65c94f5ecba,2 +np.float64,0xa0eef4bf41ddf,0xa0eef4bf41ddf,2 +np.float64,0xffd8e0a4adb1c14a,0xbff0000000000000,2 +np.float64,0xffe858f6e870b1ed,0xbff0000000000000,2 +np.float64,0x3f94c2c308298580,0x3f94c208a4bb006d,2 +np.float64,0xffb45f0d7428be18,0xbff0000000000000,2 +np.float64,0x800ed4f43dbda9e9,0x800ed4f43dbda9e9,2 +np.float64,0x8002dd697e85bad4,0x8002dd697e85bad4,2 +np.float64,0x787ceab2f0f9e,0x787ceab2f0f9e,2 +np.float64,0xbfdff5fcc2bfebfa,0xbfdd8b736b128589,2 +np.float64,0x7fdb2b4294365684,0x3ff0000000000000,2 +np.float64,0xffe711e5e92e23cc,0xbff0000000000000,2 +np.float64,0x800b1c93f1163928,0x800b1c93f1163928,2 +np.float64,0x7fc524d2f22a49a5,0x3ff0000000000000,2 +np.float64,0x7fc88013b5310026,0x3ff0000000000000,2 +np.float64,0x3fe1a910c5e35222,0x3fe00fd779ebaa2a,2 +np.float64,0xbfb57ec9ca2afd90,0xbfb571e47ecb9335,2 +np.float64,0x7fd7594b20aeb295,0x3ff0000000000000,2 +np.float64,0x7fba4641ca348c83,0x3ff0000000000000,2 +np.float64,0xffe61393706c2726,0xbff0000000000000,2 +np.float64,0x7fd54f3c7baa9e78,0x3ff0000000000000,2 +np.float64,0xffe65ffb12ecbff6,0xbff0000000000000,2 +np.float64,0xbfba3b0376347608,0xbfba239cbbbd1b11,2 +np.float64,0x800200886d640112,0x800200886d640112,2 +np.float64,0xbfecf0ba4679e174,0xbfe6fd59de44a3ec,2 +np.float64,0xffe5c57e122b8afc,0xbff0000000000000,2 +np.float64,0x7fdaad0143355a02,0x3ff0000000000000,2 +np.float64,0x46ab32c08d567,0x46ab32c08d567,2 +np.float64,0x7ff8000000000000,0x7ff8000000000000,2 +np.float64,0xbfda7980fdb4f302,0xbfd90fa9c8066109,2 +np.float64,0x3fe237703c646ee0,0x3fe07969f8d8805a,2 +np.float64,0x8000e9fcfc21d3fb,0x8000e9fcfc21d3fb,2 +np.float64,0xbfdfe6e958bfcdd2,0xbfdd7f952fe87770,2 +np.float64,0xbd7baf217af8,0xbd7baf217af8,2 +np.float64,0xbfceba9e4b3d753c,0xbfce26e54359869a,2 +np.float64,0xb95a2caf72b46,0xb95a2caf72b46,2 +np.float64,0x3fb407e25a280fc5,0x3fb3fd71e457b628,2 +np.float64,0xa1da09d943b41,0xa1da09d943b41,2 +np.float64,0xbfe9c7271cf38e4e,0xbfe559296b471738,2 +np.float64,0x3fefae6170ff5cc3,0x3fe83c70ba82f0e1,2 +np.float64,0x7fe7375348ae6ea6,0x3ff0000000000000,2 +np.float64,0xffe18c9cc6e31939,0xbff0000000000000,2 +np.float64,0x800483d13a6907a3,0x800483d13a6907a3,2 +np.float64,0x7fe772a18caee542,0x3ff0000000000000,2 +np.float64,0xffefff64e7bffec9,0xbff0000000000000,2 +np.float64,0x7fcffc31113ff861,0x3ff0000000000000,2 +np.float64,0x3fd91e067e323c0d,0x3fd7e70bf365a7b3,2 +np.float64,0xb0a6673d614cd,0xb0a6673d614cd,2 +np.float64,0xffef9a297e3f3452,0xbff0000000000000,2 +np.float64,0xffe87cc15e70f982,0xbff0000000000000,2 +np.float64,0xffefd6ad8e7fad5a,0xbff0000000000000,2 +np.float64,0x7fe3aaa3a8a75546,0x3ff0000000000000,2 +np.float64,0xddab0341bb561,0xddab0341bb561,2 +np.float64,0x3fe996d6d7332dae,0x3fe53e3ed5be2922,2 +np.float64,0x3fdbe66a18b7ccd4,0x3fda41e6053c1512,2 +np.float64,0x8914775d1228f,0x8914775d1228f,2 +np.float64,0x3fe44621d4688c44,0x3fe1ef9c7225f8bd,2 +np.float64,0xffab29a2a4365340,0xbff0000000000000,2 +np.float64,0xffc8d4a0c431a940,0xbff0000000000000,2 +np.float64,0xbfd426e085284dc2,0xbfd382e2a9617b87,2 +np.float64,0xbfd3b0a525a7614a,0xbfd3176856faccf1,2 +np.float64,0x80036dedcb06dbdc,0x80036dedcb06dbdc,2 +np.float64,0x3feb13823b762704,0x3fe60ca3facdb696,2 +np.float64,0x3fd7246b7bae48d8,0x3fd62f08afded155,2 +np.float64,0x1,0x1,2 +np.float64,0x3fe8ade4b9715bc9,0x3fe4b97cc1387d27,2 +np.float64,0x3fdf2dbec53e5b7e,0x3fdcecfeee33de95,2 +np.float64,0x3fe4292bf9685258,0x3fe1dbb5a6704090,2 +np.float64,0xbfd21acbb8243598,0xbfd1a2ff42174cae,2 +np.float64,0xdd0d2d01ba1a6,0xdd0d2d01ba1a6,2 +np.float64,0x3fa3f3d2f427e7a0,0x3fa3f13d6f101555,2 +np.float64,0x7fdabf4aceb57e95,0x3ff0000000000000,2 +np.float64,0xd4d9e39ba9b3d,0xd4d9e39ba9b3d,2 +np.float64,0xffec773396f8ee66,0xbff0000000000000,2 +np.float64,0x3fa88cc79031198f,0x3fa887f7ade722ba,2 +np.float64,0xffe63a92066c7524,0xbff0000000000000,2 +np.float64,0xbfcf514e2e3ea29c,0xbfceb510e99aaa19,2 +np.float64,0x9d78c19d3af18,0x9d78c19d3af18,2 +np.float64,0x7fdd748bfbbae917,0x3ff0000000000000,2 +np.float64,0xffb3594c4626b298,0xbff0000000000000,2 +np.float64,0x80068ce5b32d19cc,0x80068ce5b32d19cc,2 +np.float64,0x3fec63d60e78c7ac,0x3fe6b85536e44217,2 +np.float64,0x80080bad4dd0175b,0x80080bad4dd0175b,2 +np.float64,0xbfec6807baf8d010,0xbfe6ba69740f9687,2 +np.float64,0x7fedbae0bbfb75c0,0x3ff0000000000000,2 +np.float64,0x8001cb7aa3c396f6,0x8001cb7aa3c396f6,2 +np.float64,0x7fe1f1f03563e3df,0x3ff0000000000000,2 +np.float64,0x7fd83d3978307a72,0x3ff0000000000000,2 +np.float64,0xbfc05ffe9d20bffc,0xbfc049464e3f0af2,2 +np.float64,0xfe6e053ffcdc1,0xfe6e053ffcdc1,2 +np.float64,0xbfd3bdf39d277be8,0xbfd32386edf12726,2 +np.float64,0x800f41b27bde8365,0x800f41b27bde8365,2 +np.float64,0xbfe2c98390e59307,0xbfe0e3c9260fe798,2 +np.float64,0xffdd6206bcbac40e,0xbff0000000000000,2 +np.float64,0x67f35ef4cfe6c,0x67f35ef4cfe6c,2 +np.float64,0x800337e02ae66fc1,0x800337e02ae66fc1,2 +np.float64,0x3fe0ff70afe1fee1,0x3fdf1f46434330df,2 +np.float64,0x3fd7e0a1df2fc144,0x3fd6d3f82c8031e4,2 +np.float64,0x8008da5cd1b1b4ba,0x8008da5cd1b1b4ba,2 +np.float64,0x80065ec9e4ccbd95,0x80065ec9e4ccbd95,2 +np.float64,0x3fe1d1e559a3a3cb,0x3fe02e4f146aa1ab,2 +np.float64,0x7feb7d2f0836fa5d,0x3ff0000000000000,2 +np.float64,0xbfcb33ce9736679c,0xbfcaccd431b205bb,2 +np.float64,0x800e6d0adf5cda16,0x800e6d0adf5cda16,2 +np.float64,0x7fe46f272ca8de4d,0x3ff0000000000000,2 +np.float64,0x4fdfc73e9fbfa,0x4fdfc73e9fbfa,2 +np.float64,0x800958a13112b143,0x800958a13112b143,2 +np.float64,0xbfea01f877f403f1,0xbfe579a541594247,2 +np.float64,0xeefaf599ddf5f,0xeefaf599ddf5f,2 +np.float64,0x80038766c5e70ece,0x80038766c5e70ece,2 +np.float64,0x7fd31bc28ba63784,0x3ff0000000000000,2 +np.float64,0xbfe4df77eee9bef0,0xbfe257abe7083b77,2 +np.float64,0x7fe6790c78acf218,0x3ff0000000000000,2 +np.float64,0xffe7c66884af8cd0,0xbff0000000000000,2 +np.float64,0x800115e36f422bc8,0x800115e36f422bc8,2 +np.float64,0x3fc601945d2c0329,0x3fc5cab917bb20bc,2 +np.float64,0x3fd6ac9546ad592b,0x3fd5c55437ec3508,2 +np.float64,0xa7bd59294f7ab,0xa7bd59294f7ab,2 +np.float64,0x8005c26c8b8b84da,0x8005c26c8b8b84da,2 +np.float64,0x8257501704aea,0x8257501704aea,2 +np.float64,0x5b12aae0b6256,0x5b12aae0b6256,2 +np.float64,0x800232fe02c465fd,0x800232fe02c465fd,2 +np.float64,0x800dae28f85b5c52,0x800dae28f85b5c52,2 +np.float64,0x3fdade1ac135bc36,0x3fd964a2000ace25,2 +np.float64,0x3fed72ca04fae594,0x3fe73b9170d809f9,2 +np.float64,0x7fc6397e2b2c72fb,0x3ff0000000000000,2 +np.float64,0x3fe1f5296d23ea53,0x3fe048802d17621e,2 +np.float64,0xffe05544b920aa89,0xbff0000000000000,2 +np.float64,0xbfdb2e1588365c2c,0xbfd9a7e4113c713e,2 +np.float64,0xbfed6a06fa3ad40e,0xbfe7376be60535f8,2 +np.float64,0xbfe31dcaf5e63b96,0xbfe120417c46cac1,2 +np.float64,0xbfb7ed67ae2fdad0,0xbfb7dba14af33b00,2 +np.float64,0xffd32bb7eb265770,0xbff0000000000000,2 +np.float64,0x80039877b04730f0,0x80039877b04730f0,2 +np.float64,0x3f832e5630265cac,0x3f832e316f47f218,2 +np.float64,0xffe7fa7f732ff4fe,0xbff0000000000000,2 +np.float64,0x9649b87f2c937,0x9649b87f2c937,2 +np.float64,0xffaee447183dc890,0xbff0000000000000,2 +np.float64,0x7fe4e02dd869c05b,0x3ff0000000000000,2 +np.float64,0x3fe1d35e7463a6bd,0x3fe02f67bd21e86e,2 +np.float64,0xffe57f40fe2afe82,0xbff0000000000000,2 +np.float64,0xbfea1362b93426c6,0xbfe5833421dba8fc,2 +np.float64,0xffe9c689fe338d13,0xbff0000000000000,2 +np.float64,0xffc592dd102b25bc,0xbff0000000000000,2 +np.float64,0x3fd283c7aba5078f,0x3fd203d61d1398c3,2 +np.float64,0x8001d6820243ad05,0x8001d6820243ad05,2 +np.float64,0x3fe0ad5991e15ab4,0x3fdea14ef0d47fbd,2 +np.float64,0x3fe3916f2ee722de,0x3fe1722684a9ffb1,2 +np.float64,0xffef9e54e03f3ca9,0xbff0000000000000,2 +np.float64,0x7fe864faebb0c9f5,0x3ff0000000000000,2 +np.float64,0xbfed3587c3fa6b10,0xbfe71e7112df8a68,2 +np.float64,0xbfdd9efc643b3df8,0xbfdbac3a16caf208,2 +np.float64,0xbfd5ac08feab5812,0xbfd4e14575a6e41b,2 +np.float64,0xffda90fae6b521f6,0xbff0000000000000,2 +np.float64,0x8001380ecf22701e,0x8001380ecf22701e,2 +np.float64,0x7fed266fa5fa4cde,0x3ff0000000000000,2 +np.float64,0xffec6c0ac3b8d815,0xbff0000000000000,2 +np.float64,0x3fe7de43c32fbc88,0x3fe43ef62821a5a6,2 +np.float64,0x800bf4ffc357ea00,0x800bf4ffc357ea00,2 +np.float64,0x3fe125c975624b93,0x3fdf59b2de3eff5d,2 +np.float64,0x8004714c1028e299,0x8004714c1028e299,2 +np.float64,0x3fef1bfbf5fe37f8,0x3fe7fd2ba1b63c8a,2 +np.float64,0x800cae15c3195c2c,0x800cae15c3195c2c,2 +np.float64,0x7fde708e083ce11b,0x3ff0000000000000,2 +np.float64,0x7fbcee5df639dcbb,0x3ff0000000000000,2 +np.float64,0x800b1467141628cf,0x800b1467141628cf,2 +np.float64,0x3fe525e0d36a4bc2,0x3fe286b6e59e30f5,2 +np.float64,0xffe987f8b8330ff1,0xbff0000000000000,2 +np.float64,0x7e0a8284fc151,0x7e0a8284fc151,2 +np.float64,0x8006f982442df305,0x8006f982442df305,2 +np.float64,0xbfd75a3cb62eb47a,0xbfd65e54cee981c9,2 +np.float64,0x258e91104b1d3,0x258e91104b1d3,2 +np.float64,0xbfecd0056779a00b,0xbfe6ed7ae97fff1b,2 +np.float64,0x7fc3a4f9122749f1,0x3ff0000000000000,2 +np.float64,0x6e2b1024dc563,0x6e2b1024dc563,2 +np.float64,0x800d575ad4daaeb6,0x800d575ad4daaeb6,2 +np.float64,0xbfceafb1073d5f64,0xbfce1c93023d8414,2 +np.float64,0xffe895cb5f312b96,0xbff0000000000000,2 +np.float64,0x7fe7811ed4ef023d,0x3ff0000000000000,2 +np.float64,0xbfd93f952f327f2a,0xbfd803e6b5576b99,2 +np.float64,0xffdd883a3fbb1074,0xbff0000000000000,2 +np.float64,0x7fee5624eefcac49,0x3ff0000000000000,2 +np.float64,0xbfe264bb2624c976,0xbfe09a9b7cc896e7,2 +np.float64,0xffef14b417be2967,0xbff0000000000000,2 +np.float64,0xbfecbd0d94397a1b,0xbfe6e43bef852d9f,2 +np.float64,0xbfe20d9e4ba41b3c,0xbfe05a98e05846d9,2 +np.float64,0x10000000000000,0x10000000000000,2 +np.float64,0x7fefde93f7bfbd27,0x3ff0000000000000,2 +np.float64,0x80076b9e232ed73d,0x80076b9e232ed73d,2 +np.float64,0xbfe80df52c701bea,0xbfe45b754b433792,2 +np.float64,0x7fe3b5a637676b4b,0x3ff0000000000000,2 +np.float64,0x2c81d14c5903b,0x2c81d14c5903b,2 +np.float64,0x80038945c767128c,0x80038945c767128c,2 +np.float64,0xffeebaf544bd75ea,0xbff0000000000000,2 +np.float64,0xffdb1867d2b630d0,0xbff0000000000000,2 +np.float64,0x3fe3376eaee66ede,0x3fe13285579763d8,2 +np.float64,0xffddf65ca43becba,0xbff0000000000000,2 +np.float64,0xffec8e3e04791c7b,0xbff0000000000000,2 +np.float64,0x80064f4bde2c9e98,0x80064f4bde2c9e98,2 +np.float64,0x7fe534a085ea6940,0x3ff0000000000000,2 +np.float64,0xbfcbabe31d3757c8,0xbfcb3f8e70adf7e7,2 +np.float64,0xbfe45ca11e28b942,0xbfe1ff04515ef809,2 +np.float64,0x65f4df02cbe9d,0x65f4df02cbe9d,2 +np.float64,0xb08b0cbb61162,0xb08b0cbb61162,2 +np.float64,0x3feae2e8b975c5d1,0x3fe5f302b5e8eda2,2 +np.float64,0x7fcf277ff93e4eff,0x3ff0000000000000,2 +np.float64,0x80010999c4821334,0x80010999c4821334,2 +np.float64,0xbfd7f65911afecb2,0xbfd6e6e9cd098f8b,2 +np.float64,0x800e0560ec3c0ac2,0x800e0560ec3c0ac2,2 +np.float64,0x7fec4152ba3882a4,0x3ff0000000000000,2 +np.float64,0xbfb5c77cd42b8ef8,0xbfb5ba1336084908,2 +np.float64,0x457ff1b68afff,0x457ff1b68afff,2 +np.float64,0x5323ec56a647e,0x5323ec56a647e,2 +np.float64,0xbfeed16cf8bda2da,0xbfe7dc49fc9ae549,2 +np.float64,0xffe8446106b088c1,0xbff0000000000000,2 +np.float64,0xffb93cd13c3279a0,0xbff0000000000000,2 +np.float64,0x7fe515c2aeea2b84,0x3ff0000000000000,2 +np.float64,0x80099df83f933bf1,0x80099df83f933bf1,2 +np.float64,0x7fb3a375562746ea,0x3ff0000000000000,2 +np.float64,0x7fcd7efa243afdf3,0x3ff0000000000000,2 +np.float64,0xffe40cddb12819bb,0xbff0000000000000,2 +np.float64,0x8008b68eecd16d1e,0x8008b68eecd16d1e,2 +np.float64,0x2aec688055d8e,0x2aec688055d8e,2 +np.float64,0xffe23750bc646ea1,0xbff0000000000000,2 +np.float64,0x5adacf60b5b7,0x5adacf60b5b7,2 +np.float64,0x7fefb29b1cbf6535,0x3ff0000000000000,2 +np.float64,0xbfeadbf90175b7f2,0xbfe5ef55e2194794,2 +np.float64,0xeaad2885d55a5,0xeaad2885d55a5,2 +np.float64,0xffd7939fba2f2740,0xbff0000000000000,2 +np.float64,0x3fd187ea3aa30fd4,0x3fd11af023472386,2 +np.float64,0xbf6eb579c03d6b00,0xbf6eb57052f47019,2 +np.float64,0x3fefb67b3bff6cf6,0x3fe83fe4499969ac,2 +np.float64,0xbfe5183aacea3076,0xbfe27da1aa0b61a0,2 +np.float64,0xbfb83e47a2307c90,0xbfb82bcb0e12db42,2 +np.float64,0x80088849b1b11094,0x80088849b1b11094,2 +np.float64,0x800ceeed7399dddb,0x800ceeed7399dddb,2 +np.float64,0x80097cd90892f9b2,0x80097cd90892f9b2,2 +np.float64,0x7ec73feefd8e9,0x7ec73feefd8e9,2 +np.float64,0x7fe3291de5a6523b,0x3ff0000000000000,2 +np.float64,0xbfd537086daa6e10,0xbfd4787af5f60653,2 +np.float64,0x800e8ed4455d1da9,0x800e8ed4455d1da9,2 +np.float64,0x800ef8d19cbdf1a3,0x800ef8d19cbdf1a3,2 +np.float64,0x800dc4fa3a5b89f5,0x800dc4fa3a5b89f5,2 +np.float64,0xaa8b85cd55171,0xaa8b85cd55171,2 +np.float64,0xffd67a5f40acf4be,0xbff0000000000000,2 +np.float64,0xbfb7496db22e92d8,0xbfb7390a48130861,2 +np.float64,0x3fd86a8e7ab0d51d,0x3fd74bfba0f72616,2 +np.float64,0xffb7f5b7fc2feb70,0xbff0000000000000,2 +np.float64,0xbfea0960a7f412c1,0xbfe57db6d0ff4191,2 +np.float64,0x375f4fc26ebeb,0x375f4fc26ebeb,2 +np.float64,0x800c537e70b8a6fd,0x800c537e70b8a6fd,2 +np.float64,0x800b3f4506d67e8a,0x800b3f4506d67e8a,2 +np.float64,0x7fe61f2d592c3e5a,0x3ff0000000000000,2 +np.float64,0xffefffffffffffff,0xbff0000000000000,2 +np.float64,0x8005d0bb84eba178,0x8005d0bb84eba178,2 +np.float64,0x800c78b0ec18f162,0x800c78b0ec18f162,2 +np.float64,0xbfc42cccfb285998,0xbfc4027392f66b0d,2 +np.float64,0x3fd8fdc73fb1fb8e,0x3fd7cb46f928153f,2 +np.float64,0x800c71754298e2eb,0x800c71754298e2eb,2 +np.float64,0x3fe4aa7a96a954f5,0x3fe233f5d3bc1352,2 +np.float64,0x7fd53841f6aa7083,0x3ff0000000000000,2 +np.float64,0x3fd0a887b8a15110,0x3fd04ac3b9c0d1ca,2 +np.float64,0x8007b8e164cf71c4,0x8007b8e164cf71c4,2 +np.float64,0xbfddc35c66bb86b8,0xbfdbc9c5dddfb014,2 +np.float64,0x6a3756fed46eb,0x6a3756fed46eb,2 +np.float64,0xffd3dcd05527b9a0,0xbff0000000000000,2 +np.float64,0xbfd7dc75632fb8ea,0xbfd6d0538b340a98,2 +np.float64,0x17501f822ea05,0x17501f822ea05,2 +np.float64,0xbfe1f98b99a3f317,0xbfe04bbf8f8b6cb3,2 +np.float64,0x66ea65d2cdd4d,0x66ea65d2cdd4d,2 +np.float64,0xbfd12241e2224484,0xbfd0bc62f46ea5e1,2 +np.float64,0x3fed6e6fb3fadcdf,0x3fe7398249097285,2 +np.float64,0x3fe0b5ebeba16bd8,0x3fdeae84b3000a47,2 +np.float64,0x66d1bce8cda38,0x66d1bce8cda38,2 +np.float64,0x3fdd728db3bae51b,0x3fdb880f28c52713,2 +np.float64,0xffb45dbe5228bb80,0xbff0000000000000,2 +np.float64,0x1ff8990c3ff14,0x1ff8990c3ff14,2 +np.float64,0x800a68e8f294d1d2,0x800a68e8f294d1d2,2 +np.float64,0xbfe4d08b84a9a117,0xbfe24da40bff6be7,2 +np.float64,0x3fe0177f0ee02efe,0x3fddb83c5971df51,2 +np.float64,0xffc56893692ad128,0xbff0000000000000,2 +np.float64,0x51b44f6aa368b,0x51b44f6aa368b,2 +np.float64,0x2258ff4e44b21,0x2258ff4e44b21,2 +np.float64,0x3fe913649e7226c9,0x3fe4f3f119530f53,2 +np.float64,0xffe3767df766ecfc,0xbff0000000000000,2 +np.float64,0xbfe62ae12fec55c2,0xbfe33108f1f22a94,2 +np.float64,0x7fb6a6308e2d4c60,0x3ff0000000000000,2 +np.float64,0xbfe00f2085e01e41,0xbfddab19b6fc77d1,2 +np.float64,0x3fb66447dc2cc890,0x3fb655b4f46844f0,2 +np.float64,0x3fd80238f6b00470,0x3fd6f143be1617d6,2 +np.float64,0xbfd05bfeb3a0b7fe,0xbfd0031ab3455e15,2 +np.float64,0xffc3a50351274a08,0xbff0000000000000,2 +np.float64,0xffd8f4241cb1e848,0xbff0000000000000,2 +np.float64,0xbfca72a88c34e550,0xbfca13ebe85f2aca,2 +np.float64,0x3fd47d683ba8fad0,0x3fd3d13f1176ed8c,2 +np.float64,0x3fb6418e642c831d,0x3fb6333ebe479ff2,2 +np.float64,0x800fde8e023fbd1c,0x800fde8e023fbd1c,2 +np.float64,0x8001fb01e323f605,0x8001fb01e323f605,2 +np.float64,0x3febb21ff9f76440,0x3fe65ed788d52fee,2 +np.float64,0x3fe47553ffe8eaa8,0x3fe20fe01f853603,2 +np.float64,0x7fca20b3f9344167,0x3ff0000000000000,2 +np.float64,0x3fe704f4ec6e09ea,0x3fe3ba7277201805,2 +np.float64,0xf864359df0c87,0xf864359df0c87,2 +np.float64,0x4d96b01c9b2d7,0x4d96b01c9b2d7,2 +np.float64,0x3fe8a09fe9f14140,0x3fe4b1c6a2d2e095,2 +np.float64,0xffc46c61b228d8c4,0xbff0000000000000,2 +np.float64,0x3fe680a837ed0150,0x3fe3679d6eeb6485,2 +np.float64,0xbfecedc20f39db84,0xbfe6fbe9ee978bf6,2 +np.float64,0x3fb2314eae24629d,0x3fb2297ba6d55d2d,2 +np.float64,0x3fe9f0b8e7b3e172,0x3fe57026eae36db3,2 +np.float64,0x80097a132ed2f427,0x80097a132ed2f427,2 +np.float64,0x800ae5a41955cb49,0x800ae5a41955cb49,2 +np.float64,0xbfd7527279aea4e4,0xbfd6577de356e1bd,2 +np.float64,0x3fe27d3e01e4fa7c,0x3fe0ac7dd96f9179,2 +np.float64,0x7fedd8cb01bbb195,0x3ff0000000000000,2 +np.float64,0x78f8695af1f0e,0x78f8695af1f0e,2 +np.float64,0x800d2d0e927a5a1d,0x800d2d0e927a5a1d,2 +np.float64,0xffe74b46fb2e968e,0xbff0000000000000,2 +np.float64,0xbfdd12d4c8ba25aa,0xbfdb39dae49e1c10,2 +np.float64,0xbfd6c14710ad828e,0xbfd5d79ef5a8d921,2 +np.float64,0x921f4e55243ea,0x921f4e55243ea,2 +np.float64,0x800b4e4c80969c99,0x800b4e4c80969c99,2 +np.float64,0x7fe08c6ab7e118d4,0x3ff0000000000000,2 +np.float64,0xbfed290014fa5200,0xbfe71871f7e859ed,2 +np.float64,0x8008c1d5c59183ac,0x8008c1d5c59183ac,2 +np.float64,0x3fd339e68c2673cd,0x3fd2aaff3f165a9d,2 +np.float64,0xbfdd20d8113a41b0,0xbfdb4553ea2cb2fb,2 +np.float64,0x3fe52a25deea544c,0x3fe2898d5bf4442c,2 +np.float64,0x498602d4930c1,0x498602d4930c1,2 +np.float64,0x3fd8c450113188a0,0x3fd799b0b2a6c43c,2 +np.float64,0xbfd72bc2f2ae5786,0xbfd6357e15ba7f70,2 +np.float64,0xbfd076188ea0ec32,0xbfd01b8fce44d1af,2 +np.float64,0x9aace1713559c,0x9aace1713559c,2 +np.float64,0x8008a730e8914e62,0x8008a730e8914e62,2 +np.float64,0x7fe9e9a3d833d347,0x3ff0000000000000,2 +np.float64,0x800d3a0d69da741b,0x800d3a0d69da741b,2 +np.float64,0xbfe3e28a29e7c514,0xbfe1aad7643a2d19,2 +np.float64,0x7fe9894c71331298,0x3ff0000000000000,2 +np.float64,0xbfe7c6acb5ef8d5a,0xbfe430c9e258ce62,2 +np.float64,0xffb5a520a62b4a40,0xbff0000000000000,2 +np.float64,0x7fc02109ae204212,0x3ff0000000000000,2 +np.float64,0xb5c58f196b8b2,0xb5c58f196b8b2,2 +np.float64,0x3feb4ee82e769dd0,0x3fe62bae9a39d8b1,2 +np.float64,0x3fec5c3cf278b87a,0x3fe6b49000f12441,2 +np.float64,0x81f64b8103eca,0x81f64b8103eca,2 +np.float64,0xbfeab00d73f5601b,0xbfe5d7f755ab73d9,2 +np.float64,0x3fd016bf28a02d7e,0x3fcf843ea23bcd3c,2 +np.float64,0xbfa1db617423b6c0,0xbfa1d9872ddeb5a8,2 +np.float64,0x3fe83c879d70790f,0x3fe4771502d8f012,2 +np.float64,0x6b267586d64cf,0x6b267586d64cf,2 +np.float64,0x3fc91b6d3f3236d8,0x3fc8ca3eb4da25a9,2 +np.float64,0x7fd4e3f8f3a9c7f1,0x3ff0000000000000,2 +np.float64,0x800a75899214eb14,0x800a75899214eb14,2 +np.float64,0x7fdb1f2e07b63e5b,0x3ff0000000000000,2 +np.float64,0xffe7805a11ef00b4,0xbff0000000000000,2 +np.float64,0x3fc8e1b88a31c371,0x3fc892af45330818,2 +np.float64,0xbfe809fe447013fc,0xbfe45918f07da4d9,2 +np.float64,0xbfeb9d7f2ab73afe,0xbfe65446bfddc792,2 +np.float64,0x3fb47f0a5c28fe15,0x3fb473db9113e880,2 +np.float64,0x800a17ae3cb42f5d,0x800a17ae3cb42f5d,2 +np.float64,0xf5540945eaa81,0xf5540945eaa81,2 +np.float64,0xbfe577fc26aaeff8,0xbfe2bcfbf2cf69ff,2 +np.float64,0xbfb99b3e06333680,0xbfb98577b88e0515,2 +np.float64,0x7fd9290391b25206,0x3ff0000000000000,2 +np.float64,0x7fe1aa62ffa354c5,0x3ff0000000000000,2 +np.float64,0x7b0189a0f604,0x7b0189a0f604,2 +np.float64,0x3f9000ed602001db,0x3f900097fe168105,2 +np.float64,0x3fd576128d2aec25,0x3fd4b1002c92286f,2 +np.float64,0xffecc98ece79931d,0xbff0000000000000,2 +np.float64,0x800a1736c7f42e6e,0x800a1736c7f42e6e,2 +np.float64,0xbfed947548bb28eb,0xbfe74b71479ae739,2 +np.float64,0xa45c032148b9,0xa45c032148b9,2 +np.float64,0xbfc13d011c227a04,0xbfc1228447de5e9f,2 +np.float64,0xffed8baa6ebb1754,0xbff0000000000000,2 +np.float64,0x800ea2de243d45bc,0x800ea2de243d45bc,2 +np.float64,0x8001396be52272d9,0x8001396be52272d9,2 +np.float64,0xd018d1cda031a,0xd018d1cda031a,2 +np.float64,0x7fe1fece1fe3fd9b,0x3ff0000000000000,2 +np.float64,0x8009ac484c135891,0x8009ac484c135891,2 +np.float64,0x3fc560ad132ac15a,0x3fc52e5a9479f08e,2 +np.float64,0x3fd6f80ebe2df01d,0x3fd607f70ce8e3f4,2 +np.float64,0xbfd3e69e82a7cd3e,0xbfd34887c2a40699,2 +np.float64,0x3fe232d9baa465b3,0x3fe0760a822ada0c,2 +np.float64,0x3fe769bbc6eed378,0x3fe3f872680f6631,2 +np.float64,0xffe63dbd952c7b7a,0xbff0000000000000,2 +np.float64,0x4e0c00da9c181,0x4e0c00da9c181,2 +np.float64,0xffeae4d89735c9b0,0xbff0000000000000,2 +np.float64,0x3fe030bcbb606179,0x3fdddfc66660bfce,2 +np.float64,0x7fe35ca40d66b947,0x3ff0000000000000,2 +np.float64,0xbfd45bd66628b7ac,0xbfd3b2e04bfe7866,2 +np.float64,0x3fd1f0be2323e17c,0x3fd17c1c340d7a48,2 +np.float64,0x3fd7123b6cae2478,0x3fd61f0675aa9ae1,2 +np.float64,0xbfe918a377723147,0xbfe4f6efe66f5714,2 +np.float64,0x7fc400356f28006a,0x3ff0000000000000,2 +np.float64,0x7fd2dead70a5bd5a,0x3ff0000000000000,2 +np.float64,0xffe9c28f81f3851e,0xbff0000000000000,2 +np.float64,0x3fd09b1ec7a1363e,0x3fd03e3894320140,2 +np.float64,0x7fe6e80c646dd018,0x3ff0000000000000,2 +np.float64,0x7fec3760a4786ec0,0x3ff0000000000000,2 +np.float64,0x309eb6ee613d8,0x309eb6ee613d8,2 +np.float64,0x800731cb0ece6397,0x800731cb0ece6397,2 +np.float64,0xbfdb0c553db618aa,0xbfd98b8a4680ee60,2 +np.float64,0x3fd603a52eac074c,0x3fd52f6b53de7455,2 +np.float64,0x9ecb821b3d971,0x9ecb821b3d971,2 +np.float64,0x3feb7d64dc36faca,0x3fe643c2754bb7f4,2 +np.float64,0xffeb94825ef72904,0xbff0000000000000,2 +np.float64,0x24267418484cf,0x24267418484cf,2 +np.float64,0xbfa6b2fbac2d65f0,0xbfa6af2dca5bfa6f,2 +np.float64,0x8010000000000000,0x8010000000000000,2 +np.float64,0xffe6873978ed0e72,0xbff0000000000000,2 +np.float64,0x800447934ba88f27,0x800447934ba88f27,2 +np.float64,0x3fef305f09fe60be,0x3fe806156b8ca47c,2 +np.float64,0xffd441c697a8838e,0xbff0000000000000,2 +np.float64,0xbfa7684f6c2ed0a0,0xbfa764238d34830c,2 +np.float64,0xffb2c976142592f0,0xbff0000000000000,2 +np.float64,0xbfcc9d1585393a2c,0xbfcc25756bcbca1f,2 +np.float64,0xbfd477bb1ba8ef76,0xbfd3cc1d2114e77e,2 +np.float64,0xbfed1559983a2ab3,0xbfe70f03afd994ee,2 +np.float64,0xbfeb51139036a227,0xbfe62ccf56bc7fff,2 +np.float64,0x7d802890fb006,0x7d802890fb006,2 +np.float64,0x800e00af777c015f,0x800e00af777c015f,2 +np.float64,0x800647ce128c8f9d,0x800647ce128c8f9d,2 +np.float64,0x800a26da91d44db6,0x800a26da91d44db6,2 +np.float64,0x3fdc727eddb8e4fe,0x3fdab5fd9db630b3,2 +np.float64,0x7fd06def2ba0dbdd,0x3ff0000000000000,2 +np.float64,0xffe23678c4a46cf1,0xbff0000000000000,2 +np.float64,0xbfe7198e42ee331c,0xbfe3c7326c9c7553,2 +np.float64,0xffae465f3c3c8cc0,0xbff0000000000000,2 +np.float64,0xff9aea7c5035d500,0xbff0000000000000,2 +np.float64,0xbfeae49c0f35c938,0xbfe5f3e9326cb08b,2 +np.float64,0x3f9a16f300342de6,0x3f9a1581212be50f,2 +np.float64,0x8d99e2c31b33d,0x8d99e2c31b33d,2 +np.float64,0xffd58af253ab15e4,0xbff0000000000000,2 +np.float64,0xbfd205cd25a40b9a,0xbfd18f97155f8b25,2 +np.float64,0xbfebe839bbf7d074,0xbfe67a6024e8fefe,2 +np.float64,0xbfe4fb3595a9f66b,0xbfe26a42f99819ea,2 +np.float64,0x800e867c739d0cf9,0x800e867c739d0cf9,2 +np.float64,0x8bc4274f17885,0x8bc4274f17885,2 +np.float64,0xaec8914b5d912,0xaec8914b5d912,2 +np.float64,0x7fd1d64473a3ac88,0x3ff0000000000000,2 +np.float64,0xbfe6d6f69cedaded,0xbfe39dd61bc7e23e,2 +np.float64,0x7fed05039d7a0a06,0x3ff0000000000000,2 +np.float64,0xbfc40eab0f281d58,0xbfc3e50d14b79265,2 +np.float64,0x45179aec8a2f4,0x45179aec8a2f4,2 +np.float64,0xbfe717e362ee2fc7,0xbfe3c62a95b07d13,2 +np.float64,0xbfe5b8df0d6b71be,0xbfe2e76c7ec5013d,2 +np.float64,0x5c67ba6eb8cf8,0x5c67ba6eb8cf8,2 +np.float64,0xbfda72ce4cb4e59c,0xbfd909fdc7ecfe20,2 +np.float64,0x7fdf59a1e2beb343,0x3ff0000000000000,2 +np.float64,0xc4f7897f89ef1,0xc4f7897f89ef1,2 +np.float64,0x8fcd0a351f9a2,0x8fcd0a351f9a2,2 +np.float64,0x3fb161761022c2ec,0x3fb15aa31c464de2,2 +np.float64,0x8008a985be71530c,0x8008a985be71530c,2 +np.float64,0x3fca4ddb5e349bb7,0x3fc9f0a3b60e49c6,2 +np.float64,0x7fcc10a2d9382145,0x3ff0000000000000,2 +np.float64,0x78902b3af1206,0x78902b3af1206,2 +np.float64,0x7fe1e2765f23c4ec,0x3ff0000000000000,2 +np.float64,0xc1d288cf83a51,0xc1d288cf83a51,2 +np.float64,0x7fe8af692bb15ed1,0x3ff0000000000000,2 +np.float64,0x80057d90fb8afb23,0x80057d90fb8afb23,2 +np.float64,0x3fdc136b8fb826d8,0x3fda6749582b2115,2 +np.float64,0x800ec8ea477d91d5,0x800ec8ea477d91d5,2 +np.float64,0x4c0f4796981ea,0x4c0f4796981ea,2 +np.float64,0xec34c4a5d8699,0xec34c4a5d8699,2 +np.float64,0x7fce343dfb3c687b,0x3ff0000000000000,2 +np.float64,0xbfc95a98a332b530,0xbfc90705b2cc2fec,2 +np.float64,0x800d118e1dba231c,0x800d118e1dba231c,2 +np.float64,0x3fd354f310a6a9e8,0x3fd2c3bb90054154,2 +np.float64,0xbfdac0d4fab581aa,0xbfd94bf37424928e,2 +np.float64,0x3fe7f5391fefea72,0x3fe44cb49d51985b,2 +np.float64,0xd4c3c329a9879,0xd4c3c329a9879,2 +np.float64,0x3fc53977692a72f0,0x3fc50835d85c9ed1,2 +np.float64,0xbfd6989538ad312a,0xbfd5b3a2c08511fe,2 +np.float64,0xbfe329f2906653e5,0xbfe128ec1525a1c0,2 +np.float64,0x7ff0000000000000,0x3ff0000000000000,2 +np.float64,0xbfea57c90974af92,0xbfe5a87b04aa3116,2 +np.float64,0x7fdfba94043f7527,0x3ff0000000000000,2 +np.float64,0x3feedabddafdb57c,0x3fe7e06c0661978d,2 +np.float64,0x4bd9f3b697b3f,0x4bd9f3b697b3f,2 +np.float64,0x3fdd15bbfc3a2b78,0x3fdb3c3b8d070f7e,2 +np.float64,0x3fbd89ccd23b13a0,0x3fbd686b825cff80,2 +np.float64,0x7ff4000000000000,0x7ffc000000000000,2 +np.float64,0x3f9baa8928375512,0x3f9ba8d01ddd5300,2 +np.float64,0x4a3ebdf2947d8,0x4a3ebdf2947d8,2 +np.float64,0x3fe698d5c06d31ac,0x3fe376dff48312c8,2 +np.float64,0xffd5323df12a647c,0xbff0000000000000,2 +np.float64,0xffea7f111174fe22,0xbff0000000000000,2 +np.float64,0x3feb4656a9b68cad,0x3fe627392eb2156f,2 +np.float64,0x7fc1260e9c224c1c,0x3ff0000000000000,2 +np.float64,0x80056e45e5eadc8d,0x80056e45e5eadc8d,2 +np.float64,0x7fd0958ef6a12b1d,0x3ff0000000000000,2 +np.float64,0x8001f85664e3f0ae,0x8001f85664e3f0ae,2 +np.float64,0x3fe553853beaa70a,0x3fe2a4f5e7c83558,2 +np.float64,0xbfeb33ce6276679d,0xbfe61d8ec9e5ff8c,2 +np.float64,0xbfd1b24e21a3649c,0xbfd14245df6065e9,2 +np.float64,0x3fe286fc40650df9,0x3fe0b395c8059429,2 +np.float64,0xffed378058fa6f00,0xbff0000000000000,2 +np.float64,0xbfd0c4a2d7a18946,0xbfd06509a434d6a0,2 +np.float64,0xbfea31d581f463ab,0xbfe593d976139f94,2 +np.float64,0xbfe0705c85e0e0b9,0xbfde42efa978eb0c,2 +np.float64,0xe4c4c339c9899,0xe4c4c339c9899,2 +np.float64,0x3fd68befa9ad17df,0x3fd5a870b3f1f83e,2 +np.float64,0x8000000000000001,0x8000000000000001,2 +np.float64,0x3fe294256965284b,0x3fe0bd271e22d86b,2 +np.float64,0x8005327a862a64f6,0x8005327a862a64f6,2 +np.float64,0xbfdb8155ce3702ac,0xbfd9ed9ef97920f8,2 +np.float64,0xbff0000000000000,0xbfe85efab514f394,2 +np.float64,0xffe66988f1ecd312,0xbff0000000000000,2 +np.float64,0x3fb178a85e22f150,0x3fb171b9fbf95f1d,2 +np.float64,0x7f829b900025371f,0x3ff0000000000000,2 +np.float64,0x8000000000000000,0x8000000000000000,2 +np.float64,0x8006cb77f60d96f1,0x8006cb77f60d96f1,2 +np.float64,0x3fe0c5d53aa18baa,0x3fdec7012ab92b42,2 +np.float64,0x77266426ee4cd,0x77266426ee4cd,2 +np.float64,0xbfec95f468392be9,0xbfe6d11428f60136,2 +np.float64,0x3fedbf532dfb7ea6,0x3fe75f8436dd1d58,2 +np.float64,0x8002fadd3f85f5bb,0x8002fadd3f85f5bb,2 +np.float64,0xbfefebaa8d3fd755,0xbfe8566c6aa90fba,2 +np.float64,0xffc7dd2b712fba58,0xbff0000000000000,2 +np.float64,0x7fe5d3a6e8aba74d,0x3ff0000000000000,2 +np.float64,0x2da061525b40d,0x2da061525b40d,2 +np.float64,0x7fcb9b9953373732,0x3ff0000000000000,2 +np.float64,0x2ca2f6fc59460,0x2ca2f6fc59460,2 +np.float64,0xffeb84b05af70960,0xbff0000000000000,2 +np.float64,0xffe551e86c6aa3d0,0xbff0000000000000,2 +np.float64,0xbfdb311311366226,0xbfd9aa6688faafb9,2 +np.float64,0xbfd4f3875629e70e,0xbfd43bcd73534c66,2 +np.float64,0x7fe95666f932accd,0x3ff0000000000000,2 +np.float64,0x3fc73dfb482e7bf7,0x3fc6fd70c20ebf60,2 +np.float64,0x800cd9e40939b3c8,0x800cd9e40939b3c8,2 +np.float64,0x3fb0c9fa422193f0,0x3fb0c3d38879a2ac,2 +np.float64,0xffd59a38372b3470,0xbff0000000000000,2 +np.float64,0x3fa8320ef4306420,0x3fa82d739e937d35,2 +np.float64,0x3fd517f16caa2fe4,0x3fd45c8de1e93b37,2 +np.float64,0xaed921655db24,0xaed921655db24,2 +np.float64,0x93478fb9268f2,0x93478fb9268f2,2 +np.float64,0x1615e28a2c2bd,0x1615e28a2c2bd,2 +np.float64,0xbfead23010f5a460,0xbfe5ea24d5d8f820,2 +np.float64,0x774a6070ee94d,0x774a6070ee94d,2 +np.float64,0x3fdf5874bd3eb0e9,0x3fdd0ef121dd915c,2 +np.float64,0x8004b25f53a964bf,0x8004b25f53a964bf,2 +np.float64,0xbfddacdd2ebb59ba,0xbfdbb78198fab36b,2 +np.float64,0x8008a3acf271475a,0x8008a3acf271475a,2 +np.float64,0xbfdb537c8736a6fa,0xbfd9c741038bb8f0,2 +np.float64,0xbfe56a133f6ad426,0xbfe2b3d5b8d259a1,2 +np.float64,0xffda1db531343b6a,0xbff0000000000000,2 +np.float64,0x3fcbe05f3a37c0be,0x3fcb71a54a64ddfb,2 +np.float64,0x7fe1ccaa7da39954,0x3ff0000000000000,2 +np.float64,0x3faeadd8343d5bb0,0x3faea475608860e6,2 +np.float64,0x3fe662ba1c2cc574,0x3fe354a6176e90df,2 +np.float64,0xffe4d49f4e69a93e,0xbff0000000000000,2 +np.float64,0xbfeadbc424f5b788,0xbfe5ef39dbe66343,2 +np.float64,0x99cf66f1339ed,0x99cf66f1339ed,2 +np.float64,0x33af77a2675f0,0x33af77a2675f0,2 +np.float64,0x7fec7b32ecf8f665,0x3ff0000000000000,2 +np.float64,0xffef3e44993e7c88,0xbff0000000000000,2 +np.float64,0xffe8f8ceac31f19c,0xbff0000000000000,2 +np.float64,0x7fe0d15b6da1a2b6,0x3ff0000000000000,2 +np.float64,0x4ba795c2974f3,0x4ba795c2974f3,2 +np.float64,0x3fe361aa37a6c354,0x3fe15079021d6b15,2 +np.float64,0xffe709714f6e12e2,0xbff0000000000000,2 +np.float64,0xffe7ea6a872fd4d4,0xbff0000000000000,2 +np.float64,0xffdb9441c8b72884,0xbff0000000000000,2 +np.float64,0xffd5e11ae9abc236,0xbff0000000000000,2 +np.float64,0xffe092a08b612540,0xbff0000000000000,2 +np.float64,0x3fe1f27e1ca3e4fc,0x3fe04685b5131207,2 +np.float64,0xbfe71ce1bdee39c4,0xbfe3c940809a7081,2 +np.float64,0xffe8c3aa68318754,0xbff0000000000000,2 +np.float64,0x800d4e2919da9c52,0x800d4e2919da9c52,2 +np.float64,0x7fe6c8bca76d9178,0x3ff0000000000000,2 +np.float64,0x7fced8751e3db0e9,0x3ff0000000000000,2 +np.float64,0xd61d0c8bac3a2,0xd61d0c8bac3a2,2 +np.float64,0x3fec57732938aee6,0x3fe6b22f15f38352,2 +np.float64,0xff9251cc7024a3a0,0xbff0000000000000,2 +np.float64,0xf4a68cb9e94d2,0xf4a68cb9e94d2,2 +np.float64,0x3feed76703bdaece,0x3fe7def0fc9a080c,2 +np.float64,0xbfe8971ff7712e40,0xbfe4ac3eb8ebff07,2 +np.float64,0x3fe4825f682904bf,0x3fe218c1952fe67d,2 +np.float64,0xbfd60f7698ac1eee,0xbfd539f0979b4b0c,2 +np.float64,0x3fcf0845993e1088,0x3fce7032f7180144,2 +np.float64,0x7fc83443f3306887,0x3ff0000000000000,2 +np.float64,0x3fe93123ae726247,0x3fe504e4fc437e89,2 +np.float64,0x3fbf9eb8363f3d70,0x3fbf75cdfa6828d5,2 +np.float64,0xbf8b45e5d0368bc0,0xbf8b457c29dfe1a9,2 +np.float64,0x8006c2853d0d850b,0x8006c2853d0d850b,2 +np.float64,0xffef26e25ffe4dc4,0xbff0000000000000,2 +np.float64,0x7fefffffffffffff,0x3ff0000000000000,2 +np.float64,0xbfde98f2c2bd31e6,0xbfdc761bfab1c4cb,2 +np.float64,0xffb725e6222e4bd0,0xbff0000000000000,2 +np.float64,0x800c63ead5d8c7d6,0x800c63ead5d8c7d6,2 +np.float64,0x3fea087e95f410fd,0x3fe57d3ab440706c,2 +np.float64,0xbfdf9f8a603f3f14,0xbfdd4742d77dfa57,2 +np.float64,0xfff0000000000000,0xbff0000000000000,2 +np.float64,0xbfcdc0841d3b8108,0xbfcd3a401debba9a,2 +np.float64,0x800f0c8f4f7e191f,0x800f0c8f4f7e191f,2 +np.float64,0x800ba6e75fd74dcf,0x800ba6e75fd74dcf,2 +np.float64,0x7fee4927e8bc924f,0x3ff0000000000000,2 +np.float64,0x3fadf141903be283,0x3fade8878d9d3551,2 +np.float64,0x3efb1a267df64,0x3efb1a267df64,2 +np.float64,0xffebf55f22b7eabe,0xbff0000000000000,2 +np.float64,0x7fbe8045663d008a,0x3ff0000000000000,2 +np.float64,0x3fefc0129f7f8026,0x3fe843f8b7d6cf38,2 +np.float64,0xbfe846b420f08d68,0xbfe47d1709e43937,2 +np.float64,0x7fe8e87043f1d0e0,0x3ff0000000000000,2 +np.float64,0x3fcfb718453f6e31,0x3fcf14ecee7b32b4,2 +np.float64,0x7fe4306b71a860d6,0x3ff0000000000000,2 +np.float64,0x7fee08459f7c108a,0x3ff0000000000000,2 +np.float64,0x3fed705165fae0a3,0x3fe73a66369c5700,2 +np.float64,0x7fd0e63f4da1cc7e,0x3ff0000000000000,2 +np.float64,0xffd1a40c2ea34818,0xbff0000000000000,2 +np.float64,0xbfa369795c26d2f0,0xbfa36718218d46b3,2 +np.float64,0xef70b9f5dee17,0xef70b9f5dee17,2 +np.float64,0x3fb50a0a6e2a1410,0x3fb4fdf27724560a,2 +np.float64,0x7fe30a0f6166141e,0x3ff0000000000000,2 +np.float64,0xbfd7b3ca7daf6794,0xbfd6accb81032b2d,2 +np.float64,0x3fc21dceb3243b9d,0x3fc1ff15d5d277a3,2 +np.float64,0x3fe483e445a907c9,0x3fe219ca0e269552,2 +np.float64,0x3fb2b1e2a22563c0,0x3fb2a96554900eaf,2 +np.float64,0x4b1ff6409641,0x4b1ff6409641,2 +np.float64,0xbfd92eabc9b25d58,0xbfd7f55d7776d64e,2 +np.float64,0x8003b8604c8770c1,0x8003b8604c8770c1,2 +np.float64,0x800d20a9df1a4154,0x800d20a9df1a4154,2 +np.float64,0xecf8a535d9f15,0xecf8a535d9f15,2 +np.float64,0x3fe92d15bab25a2b,0x3fe50296aa15ae85,2 +np.float64,0x800239c205a47385,0x800239c205a47385,2 +np.float64,0x3fc48664a9290cc8,0x3fc459d126320ef6,2 +np.float64,0x3fe7620625eec40c,0x3fe3f3bcbee3e8c6,2 +np.float64,0x3fd242ff4ca48600,0x3fd1c81ed7a971c8,2 +np.float64,0xbfe39bafcfa73760,0xbfe17959c7a279db,2 +np.float64,0x7fdcd2567239a4ac,0x3ff0000000000000,2 +np.float64,0x3fe5f2f292ebe5e6,0x3fe30d12f05e2752,2 +np.float64,0x7fda3819d1347033,0x3ff0000000000000,2 +np.float64,0xffca5b4d4334b69c,0xbff0000000000000,2 +np.float64,0xb8a2b7cd71457,0xb8a2b7cd71457,2 +np.float64,0x3fee689603fcd12c,0x3fe7ad4ace26d6dd,2 +np.float64,0x7fe26541a564ca82,0x3ff0000000000000,2 +np.float64,0x3fe6912ee66d225e,0x3fe3720d242c4d82,2 +np.float64,0xffe6580c75ecb018,0xbff0000000000000,2 +np.float64,0x7fe01a3370603466,0x3ff0000000000000,2 +np.float64,0xffe84e3f84b09c7e,0xbff0000000000000,2 +np.float64,0x3ff0000000000000,0x3fe85efab514f394,2 +np.float64,0x3fe214d4266429a8,0x3fe05fec03a3c247,2 +np.float64,0x3fd00aec5da015d8,0x3fcf6e070ad4ad62,2 +np.float64,0x800aac8631f5590d,0x800aac8631f5590d,2 +np.float64,0xbfe7c4f5f76f89ec,0xbfe42fc1c57b4a13,2 +np.float64,0xaf146c7d5e28e,0xaf146c7d5e28e,2 +np.float64,0xbfe57188b66ae312,0xbfe2b8be4615ef75,2 +np.float64,0xffef8cb8e1ff1971,0xbff0000000000000,2 +np.float64,0x8001daf8aa63b5f2,0x8001daf8aa63b5f2,2 +np.float64,0x3fdddcc339bbb986,0x3fdbde5f3783538b,2 +np.float64,0xdd8c92c3bb193,0xdd8c92c3bb193,2 +np.float64,0xbfe861a148f0c342,0xbfe48cf1d228a336,2 +np.float64,0xffe260a32e24c146,0xbff0000000000000,2 +np.float64,0x1f7474b43ee8f,0x1f7474b43ee8f,2 +np.float64,0x3fe81dbd89703b7c,0x3fe464d78df92b7b,2 +np.float64,0x7fed0101177a0201,0x3ff0000000000000,2 +np.float64,0x7fd8b419a8316832,0x3ff0000000000000,2 +np.float64,0x3fe93debccf27bd8,0x3fe50c27727917f0,2 +np.float64,0xe5ead05bcbd5a,0xe5ead05bcbd5a,2 +np.float64,0xbfebbbc4cff7778a,0xbfe663c4ca003bbf,2 +np.float64,0xbfea343eb474687e,0xbfe59529f73ea151,2 +np.float64,0x3fbe74a5963ce94b,0x3fbe50123ed05d8d,2 +np.float64,0x3fd31d3a5d263a75,0x3fd290c026cb38a5,2 +np.float64,0xbfd79908acaf3212,0xbfd695620e31c3c6,2 +np.float64,0xbfc26a350324d46c,0xbfc249f335f3e465,2 +np.float64,0xbfac38d5583871b0,0xbfac31866d12a45e,2 +np.float64,0x3fe40cea672819d5,0x3fe1c83754e72c92,2 +np.float64,0xbfa74770642e8ee0,0xbfa74355fcf67332,2 +np.float64,0x7fc60942d32c1285,0x3ff0000000000000,2 diff --git a/numpy/core/tests/examples/checks.pyx b/numpy/core/tests/examples/checks.pyx new file mode 100644 index 000000000000..151979db7043 --- /dev/null +++ b/numpy/core/tests/examples/checks.pyx @@ -0,0 +1,30 @@ +""" +Functions in this module give python-space wrappers for cython functions +exposed in numpy/__init__.pxd, so they can be tested in test_cython.py +""" +cimport numpy as cnp +cnp.import_array() + + +def is_td64(obj): + return cnp.is_timedelta64_object(obj) + + +def is_dt64(obj): + return cnp.is_datetime64_object(obj) + + +def get_dt64_value(obj): + return cnp.get_datetime64_value(obj) + + +def get_td64_value(obj): + return cnp.get_timedelta64_value(obj) + + +def get_dt64_unit(obj): + return cnp.get_datetime64_unit(obj) + + +def is_integer(obj): + return isinstance(obj, (cnp.integer, int)) diff --git a/numpy/core/tests/examples/setup.py b/numpy/core/tests/examples/setup.py new file mode 100644 index 000000000000..6e34aa7787ad --- /dev/null +++ b/numpy/core/tests/examples/setup.py @@ -0,0 +1,25 @@ +""" +Provide python-space access to the functions exposed in numpy/__init__.pxd +for testing. +""" + +import numpy as np +from distutils.core import setup +from Cython.Build import cythonize +from setuptools.extension import Extension +import os + +macros = [("NPY_NO_DEPRECATED_API", 0)] + +checks = Extension( + "checks", + sources=[os.path.join('.', "checks.pyx")], + include_dirs=[np.get_include()], + define_macros=macros, +) + +extensions = [checks] + +setup( + ext_modules=cythonize(extensions) +) diff --git a/numpy/core/tests/test__exceptions.py b/numpy/core/tests/test__exceptions.py new file mode 100644 index 000000000000..10b87e052b38 --- /dev/null +++ b/numpy/core/tests/test__exceptions.py @@ -0,0 +1,88 @@ +""" +Tests of the ._exceptions module. Primarily for exercising the __str__ methods. +""" + +import pickle + +import pytest +import numpy as np + +_ArrayMemoryError = np.core._exceptions._ArrayMemoryError +_UFuncNoLoopError = np.core._exceptions._UFuncNoLoopError + +class TestArrayMemoryError: + def test_pickling(self): + """ Test that _ArrayMemoryError can be pickled """ + error = _ArrayMemoryError((1023,), np.dtype(np.uint8)) + res = pickle.loads(pickle.dumps(error)) + assert res._total_size == error._total_size + + def test_str(self): + e = _ArrayMemoryError((1023,), np.dtype(np.uint8)) + str(e) # not crashing is enough + + # testing these properties is easier than testing the full string repr + def test__size_to_string(self): + """ Test e._size_to_string """ + f = _ArrayMemoryError._size_to_string + Ki = 1024 + assert f(0) == '0 bytes' + assert f(1) == '1 bytes' + assert f(1023) == '1023 bytes' + assert f(Ki) == '1.00 KiB' + assert f(Ki+1) == '1.00 KiB' + assert f(10*Ki) == '10.0 KiB' + assert f(int(999.4*Ki)) == '999. KiB' + assert f(int(1023.4*Ki)) == '1023. KiB' + assert f(int(1023.5*Ki)) == '1.00 MiB' + assert f(Ki*Ki) == '1.00 MiB' + + # 1023.9999 Mib should round to 1 GiB + assert f(int(Ki*Ki*Ki*0.9999)) == '1.00 GiB' + assert f(Ki*Ki*Ki*Ki*Ki*Ki) == '1.00 EiB' + # larger than sys.maxsize, adding larger prefixes isn't going to help + # anyway. + assert f(Ki*Ki*Ki*Ki*Ki*Ki*123456) == '123456. EiB' + + def test__total_size(self): + """ Test e._total_size """ + e = _ArrayMemoryError((1,), np.dtype(np.uint8)) + assert e._total_size == 1 + + e = _ArrayMemoryError((2, 4), np.dtype((np.uint64, 16))) + assert e._total_size == 1024 + + +class TestUFuncNoLoopError: + def test_pickling(self): + """ Test that _UFuncNoLoopError can be pickled """ + assert isinstance(pickle.dumps(_UFuncNoLoopError), bytes) + + +@pytest.mark.parametrize("args", [ + (2, 1, None), + (2, 1, "test_prefix"), + ("test message",), +]) +class TestAxisError: + def test_attr(self, args): + """Validate attribute types.""" + exc = np.AxisError(*args) + if len(args) == 1: + assert exc.axis is None + assert exc.ndim is None + else: + axis, ndim, *_ = args + assert exc.axis == axis + assert exc.ndim == ndim + + def test_pickling(self, args): + """Test that `AxisError` can be pickled.""" + exc = np.AxisError(*args) + exc2 = pickle.loads(pickle.dumps(exc)) + + assert type(exc) is type(exc2) + for name in ("axis", "ndim", "args"): + attr1 = getattr(exc, name) + attr2 = getattr(exc2, name) + assert attr1 == attr2, name diff --git a/numpy/core/tests/test_abc.py b/numpy/core/tests/test_abc.py index d9c61b0c6115..30e5748af867 100644 --- a/numpy/core/tests/test_abc.py +++ b/numpy/core/tests/test_abc.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - from numpy.testing import assert_ import numbers @@ -7,7 +5,7 @@ import numpy as np from numpy.core.numerictypes import sctypes -class TestABC(object): +class TestABC: def test_abstract(self): assert_(issubclass(np.number, numbers.Number)) diff --git a/numpy/core/tests/test_api.py b/numpy/core/tests/test_api.py index 9755e7b36dfe..d3c7211cd1b9 100644 --- a/numpy/core/tests/test_api.py +++ b/numpy/core/tests/test_api.py @@ -1,10 +1,11 @@ -from __future__ import division, absolute_import, print_function - import sys import numpy as np +from numpy.core._rational_tests import rational +import pytest from numpy.testing import ( - assert_, assert_equal, assert_array_equal, assert_raises, HAS_REFCOUNT + assert_, assert_equal, assert_array_equal, assert_raises, assert_warns, + HAS_REFCOUNT ) # Switch between new behaviour when NPY_RELAXED_STRIDES_CHECKING is set. @@ -39,57 +40,38 @@ def test_array_array(): assert_equal(old_refcount, sys.getrefcount(np.float64)) # test string - S2 = np.dtype((str, 2)) - S3 = np.dtype((str, 3)) - S5 = np.dtype((str, 5)) + S2 = np.dtype((bytes, 2)) + S3 = np.dtype((bytes, 3)) + S5 = np.dtype((bytes, 5)) + assert_equal(np.array(b"1.0", dtype=np.float64), + np.ones((), dtype=np.float64)) + assert_equal(np.array(b"1.0").dtype, S3) + assert_equal(np.array(b"1.0", dtype=bytes).dtype, S3) + assert_equal(np.array(b"1.0", dtype=S2), np.array(b"1.")) + assert_equal(np.array(b"1", dtype=S5), np.ones((), dtype=S5)) + + # test string + U2 = np.dtype((str, 2)) + U3 = np.dtype((str, 3)) + U5 = np.dtype((str, 5)) assert_equal(np.array("1.0", dtype=np.float64), np.ones((), dtype=np.float64)) - assert_equal(np.array("1.0").dtype, S3) - assert_equal(np.array("1.0", dtype=str).dtype, S3) - assert_equal(np.array("1.0", dtype=S2), np.array("1.")) - assert_equal(np.array("1", dtype=S5), np.ones((), dtype=S5)) - - # test unicode - _unicode = globals().get("unicode") - if _unicode: - U2 = np.dtype((_unicode, 2)) - U3 = np.dtype((_unicode, 3)) - U5 = np.dtype((_unicode, 5)) - assert_equal(np.array(_unicode("1.0"), dtype=np.float64), - np.ones((), dtype=np.float64)) - assert_equal(np.array(_unicode("1.0")).dtype, U3) - assert_equal(np.array(_unicode("1.0"), dtype=_unicode).dtype, U3) - assert_equal(np.array(_unicode("1.0"), dtype=U2), - np.array(_unicode("1."))) - assert_equal(np.array(_unicode("1"), dtype=U5), - np.ones((), dtype=U5)) + assert_equal(np.array("1.0").dtype, U3) + assert_equal(np.array("1.0", dtype=str).dtype, U3) + assert_equal(np.array("1.0", dtype=U2), np.array(str("1."))) + assert_equal(np.array("1", dtype=U5), np.ones((), dtype=U5)) builtins = getattr(__builtins__, '__dict__', __builtins__) assert_(hasattr(builtins, 'get')) - # test buffer - _buffer = builtins.get("buffer") - if _buffer and sys.version_info[:3] >= (2, 7, 5): - # This test fails for earlier versions of Python. - # Evidently a bug got fixed in 2.7.5. - dat = np.array(_buffer('1.0'), dtype=np.float64) - assert_equal(dat, [49.0, 46.0, 48.0]) - assert_(dat.dtype.type is np.float64) - - dat = np.array(_buffer(b'1.0')) - assert_equal(dat, [49, 46, 48]) - assert_(dat.dtype.type is np.uint8) - - # test memoryview, new version of buffer - _memoryview = builtins.get("memoryview") - if _memoryview: - dat = np.array(_memoryview(b'1.0'), dtype=np.float64) - assert_equal(dat, [49.0, 46.0, 48.0]) - assert_(dat.dtype.type is np.float64) - - dat = np.array(_memoryview(b'1.0')) - assert_equal(dat, [49, 46, 48]) - assert_(dat.dtype.type is np.uint8) + # test memoryview + dat = np.array(memoryview(b'1.0'), dtype=np.float64) + assert_equal(dat, [49.0, 46.0, 48.0]) + assert_(dat.dtype.type is np.float64) + + dat = np.array(memoryview(b'1.0')) + assert_equal(dat, [49, 46, 48]) + assert_(dat.dtype.type is np.uint8) # test array interface a = np.array(100.0, dtype=np.float64) @@ -160,6 +142,16 @@ def test_array_array(): assert_equal(np.array([(1.0,) * 10] * 10, dtype=np.float64), np.ones((10, 10), dtype=np.float64)) +@pytest.mark.parametrize("array", [True, False]) +def test_array_impossible_casts(array): + # All builtin types can forst cast as least theoretically + # but user dtypes cannot necessarily. + rt = rational(1, 2) + if array: + rt = np.array(rt) + with assert_raises(TypeError): + np.array(rt, dtype="M8") + def test_fastCopyAndTranspose(): # 0D array @@ -289,6 +281,91 @@ class MyNDArray(np.ndarray): a = np.array(1000, dtype='i4') assert_raises(TypeError, a.astype, 'U1', casting='safe') +@pytest.mark.parametrize("dt", ["S", "U"]) +def test_array_astype_to_string_discovery_empty(dt): + # See also gh-19085 + arr = np.array([""], dtype=object) + # Note, the itemsize is the `0 -> 1` logic, which should change. + # The important part the test is rather that it does not error. + assert arr.astype(dt).dtype.itemsize == np.dtype(f"{dt}1").itemsize + + # check the same thing for `np.can_cast` (since it accepts arrays) + assert np.can_cast(arr, dt, casting="unsafe") + assert not np.can_cast(arr, dt, casting="same_kind") + # as well as for the object as a descriptor: + assert np.can_cast("O", dt, casting="unsafe") + +@pytest.mark.parametrize("dt", ["d", "f", "S13", "U32"]) +def test_array_astype_to_void(dt): + dt = np.dtype(dt) + arr = np.array([], dtype=dt) + assert arr.astype("V").dtype.itemsize == dt.itemsize + +def test_object_array_astype_to_void(): + # This is different to `test_array_astype_to_void` as object arrays + # are inspected. The default void is "V8" (8 is the length of double) + arr = np.array([], dtype="O").astype("V") + assert arr.dtype == "V8" + +@pytest.mark.parametrize("t", + np.sctypes['uint'] + np.sctypes['int'] + np.sctypes['float'] +) +def test_array_astype_warning(t): + # test ComplexWarning when casting from complex to float or int + a = np.array(10, dtype=np.complex_) + assert_warns(np.ComplexWarning, a.astype, t) + +@pytest.mark.parametrize(["dtype", "out_dtype"], + [(np.bytes_, np.bool_), + (np.unicode_, np.bool_), + (np.dtype("S10,S9"), np.dtype("?,?"))]) +def test_string_to_boolean_cast(dtype, out_dtype): + """ + Currently, for `astype` strings are cast to booleans effectively by + calling `bool(int(string)`. This is not consistent (see gh-9875) and + will eventually be deprecated. + """ + arr = np.array(["10", "10\0\0\0", "0\0\0", "0"], dtype=dtype) + expected = np.array([True, True, False, False], dtype=out_dtype) + assert_array_equal(arr.astype(out_dtype), expected) + +@pytest.mark.parametrize(["dtype", "out_dtype"], + [(np.bytes_, np.bool_), + (np.unicode_, np.bool_), + (np.dtype("S10,S9"), np.dtype("?,?"))]) +def test_string_to_boolean_cast_errors(dtype, out_dtype): + """ + These currently error out, since cast to integers fails, but should not + error out in the future. + """ + for invalid in ["False", "True", "", "\0", "non-empty"]: + arr = np.array([invalid], dtype=dtype) + with assert_raises(ValueError): + arr.astype(out_dtype) + +@pytest.mark.parametrize("str_type", [str, bytes, np.str_, np.unicode_]) +@pytest.mark.parametrize("scalar_type", + [np.complex64, np.complex128, np.clongdouble]) +def test_string_to_complex_cast(str_type, scalar_type): + value = scalar_type(b"1+3j") + assert scalar_type(value) == 1+3j + assert np.array([value], dtype=object).astype(scalar_type)[()] == 1+3j + assert np.array(value).astype(scalar_type)[()] == 1+3j + arr = np.zeros(1, dtype=scalar_type) + arr[0] = value + assert arr[0] == 1+3j + +@pytest.mark.parametrize("dtype", np.typecodes["AllFloat"]) +def test_none_to_nan_cast(dtype): + # Note that at the time of writing this test, the scalar constructors + # reject None + arr = np.zeros(1, dtype=dtype) + arr[0] = None + assert np.isnan(arr)[0] + assert np.isnan(np.array(None, dtype=dtype))[()] + assert np.isnan(np.array([None], dtype=dtype))[0] + assert np.isnan(np.array(None).astype(dtype))[()] + def test_copyto_fromscalar(): a = np.arange(6, dtype='f4').reshape(2, 3) @@ -514,3 +591,38 @@ def test_broadcast_arrays(): result = np.broadcast_arrays(a, b) assert_equal(result[0], np.array([(1, 2, 3), (1, 2, 3), (1, 2, 3)], dtype='u4,u4,u4')) assert_equal(result[1], np.array([(1, 2, 3), (4, 5, 6), (7, 8, 9)], dtype='u4,u4,u4')) + +@pytest.mark.parametrize(["shape", "fill_value", "expected_output"], + [((2, 2), [5.0, 6.0], np.array([[5.0, 6.0], [5.0, 6.0]])), + ((3, 2), [1.0, 2.0], np.array([[1.0, 2.0], [1.0, 2.0], [1.0, 2.0]]))]) +def test_full_from_list(shape, fill_value, expected_output): + output = np.full(shape, fill_value) + assert_equal(output, expected_output) + +def test_astype_copyflag(): + # test the various copyflag options + arr = np.arange(10, dtype=np.intp) + + res_true = arr.astype(np.intp, copy=True) + assert not np.may_share_memory(arr, res_true) + res_always = arr.astype(np.intp, copy=np._CopyMode.ALWAYS) + assert not np.may_share_memory(arr, res_always) + + res_false = arr.astype(np.intp, copy=False) + # `res_false is arr` currently, but check `may_share_memory`. + assert np.may_share_memory(arr, res_false) + res_if_needed = arr.astype(np.intp, copy=np._CopyMode.IF_NEEDED) + # `res_if_needed is arr` currently, but check `may_share_memory`. + assert np.may_share_memory(arr, res_if_needed) + + res_never = arr.astype(np.intp, copy=np._CopyMode.NEVER) + assert np.may_share_memory(arr, res_never) + + # Simple tests for when a copy is necessary: + res_false = arr.astype(np.float64, copy=False) + assert_array_equal(res_false, arr) + res_if_needed = arr.astype(np.float64, + copy=np._CopyMode.IF_NEEDED) + assert_array_equal(res_if_needed, arr) + assert_raises(ValueError, arr.astype, np.float64, + copy=np._CopyMode.NEVER) diff --git a/numpy/core/tests/test_argparse.py b/numpy/core/tests/test_argparse.py new file mode 100644 index 000000000000..63a01dee404f --- /dev/null +++ b/numpy/core/tests/test_argparse.py @@ -0,0 +1,62 @@ +""" +Tests for the private NumPy argument parsing functionality. +They mainly exists to ensure good test coverage without having to try the +weirder cases on actual numpy functions but test them in one place. + +The test function is defined in C to be equivalent to (errors may not always +match exactly, and could be adjusted): + + def func(arg1, /, arg2, *, arg3): + i = integer(arg1) # reproducing the 'i' parsing in Python. + return None +""" + +import pytest + +import numpy as np +from numpy.core._multiarray_tests import argparse_example_function as func + + +def test_invalid_integers(): + with pytest.raises(TypeError, + match="integer argument expected, got float"): + func(1.) + with pytest.raises(OverflowError): + func(2**100) + + +def test_missing_arguments(): + with pytest.raises(TypeError, + match="missing required positional argument 0"): + func() + with pytest.raises(TypeError, + match="missing required positional argument 0"): + func(arg2=1, arg3=4) + with pytest.raises(TypeError, + match=r"missing required argument \'arg2\' \(pos 1\)"): + func(1, arg3=5) + + +def test_too_many_positional(): + # the second argument is positional but can be passed as keyword. + with pytest.raises(TypeError, + match="takes from 2 to 3 positional arguments but 4 were given"): + func(1, 2, 3, 4) + + +def test_multiple_values(): + with pytest.raises(TypeError, + match=r"given by name \('arg2'\) and position \(position 1\)"): + func(1, 2, arg2=3) + + +def test_string_fallbacks(): + # We can (currently?) use numpy strings to test the "slow" fallbacks + # that should normally not be taken due to string interning. + arg2 = np.unicode_("arg2") + missing_arg = np.unicode_("missing_arg") + func(1, **{arg2: 3}) + with pytest.raises(TypeError, + match="got an unexpected keyword argument 'missing_arg'"): + func(2, **{missing_arg: 3}) + diff --git a/numpy/core/tests/test_array_coercion.py b/numpy/core/tests/test_array_coercion.py new file mode 100644 index 000000000000..293f5a68f8e6 --- /dev/null +++ b/numpy/core/tests/test_array_coercion.py @@ -0,0 +1,748 @@ +""" +Tests for array coercion, mainly through testing `np.array` results directly. +Note that other such tests exist e.g. in `test_api.py` and many corner-cases +are tested (sometimes indirectly) elsewhere. +""" + +import pytest +from pytest import param + +from itertools import product + +import numpy as np +from numpy.core._rational_tests import rational +from numpy.core._multiarray_umath import _discover_array_parameters + +from numpy.testing import ( + assert_array_equal, assert_warns, IS_PYPY) + + +def arraylikes(): + """ + Generator for functions converting an array into various array-likes. + If full is True (default) includes array-likes not capable of handling + all dtypes + """ + # base array: + def ndarray(a): + return a + + yield param(ndarray, id="ndarray") + + # subclass: + class MyArr(np.ndarray): + pass + + def subclass(a): + return a.view(MyArr) + + yield subclass + + class _SequenceLike(): + # We are giving a warning that array-like's were also expected to be + # sequence-like in `np.array([array_like])`, this can be removed + # when the deprecation exired (started NumPy 1.20) + def __len__(self): + raise TypeError + + def __getitem__(self): + raise TypeError + + # Array-interface + class ArrayDunder(_SequenceLike): + def __init__(self, a): + self.a = a + + def __array__(self, dtype=None): + return self.a + + yield param(ArrayDunder, id="__array__") + + # memory-view + yield param(memoryview, id="memoryview") + + # Array-interface + class ArrayInterface(_SequenceLike): + def __init__(self, a): + self.a = a # need to hold on to keep interface valid + self.__array_interface__ = a.__array_interface__ + + yield param(ArrayInterface, id="__array_interface__") + + # Array-Struct + class ArrayStruct(_SequenceLike): + def __init__(self, a): + self.a = a # need to hold on to keep struct valid + self.__array_struct__ = a.__array_struct__ + + yield param(ArrayStruct, id="__array_struct__") + + +def scalar_instances(times=True, extended_precision=True, user_dtype=True): + # Hard-coded list of scalar instances. + # Floats: + yield param(np.sqrt(np.float16(5)), id="float16") + yield param(np.sqrt(np.float32(5)), id="float32") + yield param(np.sqrt(np.float64(5)), id="float64") + if extended_precision: + yield param(np.sqrt(np.longdouble(5)), id="longdouble") + + # Complex: + yield param(np.sqrt(np.complex64(2+3j)), id="complex64") + yield param(np.sqrt(np.complex128(2+3j)), id="complex128") + if extended_precision: + yield param(np.sqrt(np.longcomplex(2+3j)), id="clongdouble") + + # Bool: + # XFAIL: Bool should be added, but has some bad properties when it + # comes to strings, see also gh-9875 + # yield param(np.bool_(0), id="bool") + + # Integers: + yield param(np.int8(2), id="int8") + yield param(np.int16(2), id="int16") + yield param(np.int32(2), id="int32") + yield param(np.int64(2), id="int64") + + yield param(np.uint8(2), id="uint8") + yield param(np.uint16(2), id="uint16") + yield param(np.uint32(2), id="uint32") + yield param(np.uint64(2), id="uint64") + + # Rational: + if user_dtype: + yield param(rational(1, 2), id="rational") + + # Cannot create a structured void scalar directly: + structured = np.array([(1, 3)], "i,i")[0] + assert isinstance(structured, np.void) + assert structured.dtype == np.dtype("i,i") + yield param(structured, id="structured") + + if times: + # Datetimes and timedelta + yield param(np.timedelta64(2), id="timedelta64[generic]") + yield param(np.timedelta64(23, "s"), id="timedelta64[s]") + yield param(np.timedelta64("NaT", "s"), id="timedelta64[s](NaT)") + + yield param(np.datetime64("NaT"), id="datetime64[generic](NaT)") + yield param(np.datetime64("2020-06-07 12:43", "ms"), id="datetime64[ms]") + + # Strings and unstructured void: + yield param(np.bytes_(b"1234"), id="bytes") + yield param(np.unicode_("2345"), id="unicode") + yield param(np.void(b"4321"), id="unstructured_void") + + +def is_parametric_dtype(dtype): + """Returns True if the the dtype is a parametric legacy dtype (itemsize + is 0, or a datetime without units) + """ + if dtype.itemsize == 0: + return True + if issubclass(dtype.type, (np.datetime64, np.timedelta64)): + if dtype.name.endswith("64"): + # Generic time units + return True + return False + + +class TestStringDiscovery: + @pytest.mark.parametrize("obj", + [object(), 1.2, 10**43, None, "string"], + ids=["object", "1.2", "10**43", "None", "string"]) + def test_basic_stringlength(self, obj): + length = len(str(obj)) + expected = np.dtype(f"S{length}") + + assert np.array(obj, dtype="S").dtype == expected + assert np.array([obj], dtype="S").dtype == expected + + # A nested array is also discovered correctly + arr = np.array(obj, dtype="O") + assert np.array(arr, dtype="S").dtype == expected + # Check that .astype() behaves identical + assert arr.astype("S").dtype == expected + + @pytest.mark.parametrize("obj", + [object(), 1.2, 10**43, None, "string"], + ids=["object", "1.2", "10**43", "None", "string"]) + def test_nested_arrays_stringlength(self, obj): + length = len(str(obj)) + expected = np.dtype(f"S{length}") + arr = np.array(obj, dtype="O") + assert np.array([arr, arr], dtype="S").dtype == expected + + @pytest.mark.parametrize("arraylike", arraylikes()) + def test_unpack_first_level(self, arraylike): + # We unpack exactly one level of array likes + obj = np.array([None]) + obj[0] = np.array(1.2) + # the length of the included item, not of the float dtype + length = len(str(obj[0])) + expected = np.dtype(f"S{length}") + + obj = arraylike(obj) + # casting to string usually calls str(obj) + arr = np.array([obj], dtype="S") + assert arr.shape == (1, 1) + assert arr.dtype == expected + + +class TestScalarDiscovery: + def test_void_special_case(self): + # Void dtypes with structures discover tuples as elements + arr = np.array((1, 2, 3), dtype="i,i,i") + assert arr.shape == () + arr = np.array([(1, 2, 3)], dtype="i,i,i") + assert arr.shape == (1,) + + def test_char_special_case(self): + arr = np.array("string", dtype="c") + assert arr.shape == (6,) + assert arr.dtype.char == "c" + arr = np.array(["string"], dtype="c") + assert arr.shape == (1, 6) + assert arr.dtype.char == "c" + + def test_char_special_case_deep(self): + # Check that the character special case errors correctly if the + # array is too deep: + nested = ["string"] # 2 dimensions (due to string being sequence) + for i in range(np.MAXDIMS - 2): + nested = [nested] + + arr = np.array(nested, dtype='c') + assert arr.shape == (1,) * (np.MAXDIMS - 1) + (6,) + with pytest.raises(ValueError): + np.array([nested], dtype="c") + + def test_unknown_object(self): + arr = np.array(object()) + assert arr.shape == () + assert arr.dtype == np.dtype("O") + + @pytest.mark.parametrize("scalar", scalar_instances()) + def test_scalar(self, scalar): + arr = np.array(scalar) + assert arr.shape == () + assert arr.dtype == scalar.dtype + + arr = np.array([[scalar, scalar]]) + assert arr.shape == (1, 2) + assert arr.dtype == scalar.dtype + + # Additionally to string this test also runs into a corner case + # with datetime promotion (the difference is the promotion order). + @pytest.mark.filterwarnings("ignore:Promotion of numbers:FutureWarning") + def test_scalar_promotion(self): + for sc1, sc2 in product(scalar_instances(), scalar_instances()): + sc1, sc2 = sc1.values[0], sc2.values[0] + # test all combinations: + try: + arr = np.array([sc1, sc2]) + except (TypeError, ValueError): + # The promotion between two times can fail + # XFAIL (ValueError): Some object casts are currently undefined + continue + assert arr.shape == (2,) + try: + dt1, dt2 = sc1.dtype, sc2.dtype + expected_dtype = np.promote_types(dt1, dt2) + assert arr.dtype == expected_dtype + except TypeError as e: + # Will currently always go to object dtype + assert arr.dtype == np.dtype("O") + + @pytest.mark.parametrize("scalar", scalar_instances()) + def test_scalar_coercion(self, scalar): + # This tests various scalar coercion paths, mainly for the numerical + # types. It includes some paths not directly related to `np.array` + if isinstance(scalar, np.inexact): + # Ensure we have a full-precision number if available + scalar = type(scalar)((scalar * 2)**0.5) + + if type(scalar) is rational: + # Rational generally fails due to a missing cast. In the future + # object casts should automatically be defined based on `setitem`. + pytest.xfail("Rational to object cast is undefined currently.") + + # Use casting from object: + arr = np.array(scalar, dtype=object).astype(scalar.dtype) + + # Test various ways to create an array containing this scalar: + arr1 = np.array(scalar).reshape(1) + arr2 = np.array([scalar]) + arr3 = np.empty(1, dtype=scalar.dtype) + arr3[0] = scalar + arr4 = np.empty(1, dtype=scalar.dtype) + arr4[:] = [scalar] + # All of these methods should yield the same results + assert_array_equal(arr, arr1) + assert_array_equal(arr, arr2) + assert_array_equal(arr, arr3) + assert_array_equal(arr, arr4) + + @pytest.mark.xfail(IS_PYPY, reason="`int(np.complex128(3))` fails on PyPy") + @pytest.mark.filterwarnings("ignore::numpy.ComplexWarning") + @pytest.mark.parametrize("cast_to", scalar_instances()) + def test_scalar_coercion_same_as_cast_and_assignment(self, cast_to): + """ + Test that in most cases: + * `np.array(scalar, dtype=dtype)` + * `np.empty((), dtype=dtype)[()] = scalar` + * `np.array(scalar).astype(dtype)` + should behave the same. The only exceptions are paramteric dtypes + (mainly datetime/timedelta without unit) and void without fields. + """ + dtype = cast_to.dtype # use to parametrize only the target dtype + + for scalar in scalar_instances(times=False): + scalar = scalar.values[0] + + if dtype.type == np.void: + if scalar.dtype.fields is not None and dtype.fields is None: + # Here, coercion to "V6" works, but the cast fails. + # Since the types are identical, SETITEM takes care of + # this, but has different rules than the cast. + with pytest.raises(TypeError): + np.array(scalar).astype(dtype) + np.array(scalar, dtype=dtype) + np.array([scalar], dtype=dtype) + continue + + # The main test, we first try to use casting and if it succeeds + # continue below testing that things are the same, otherwise + # test that the alternative paths at least also fail. + try: + cast = np.array(scalar).astype(dtype) + except (TypeError, ValueError, RuntimeError): + # coercion should also raise (error type may change) + with pytest.raises(Exception): + np.array(scalar, dtype=dtype) + + if (isinstance(scalar, rational) and + np.issubdtype(dtype, np.signedinteger)): + return + + with pytest.raises(Exception): + np.array([scalar], dtype=dtype) + # assignment should also raise + res = np.zeros((), dtype=dtype) + with pytest.raises(Exception): + res[()] = scalar + + return + + # Non error path: + arr = np.array(scalar, dtype=dtype) + assert_array_equal(arr, cast) + # assignment behaves the same + ass = np.zeros((), dtype=dtype) + ass[()] = scalar + assert_array_equal(ass, cast) + + @pytest.mark.parametrize("pyscalar", [10, 10.32, 10.14j, 10**100]) + def test_pyscalar_subclasses(self, pyscalar): + """NumPy arrays are read/write which means that anything but invariant + behaviour is on thin ice. However, we currently are happy to discover + subclasses of Python float, int, complex the same as the base classes. + This should potentially be deprecated. + """ + class MyScalar(type(pyscalar)): + pass + + res = np.array(MyScalar(pyscalar)) + expected = np.array(pyscalar) + assert_array_equal(res, expected) + + @pytest.mark.parametrize("dtype_char", np.typecodes["All"]) + def test_default_dtype_instance(self, dtype_char): + if dtype_char in "SU": + dtype = np.dtype(dtype_char + "1") + elif dtype_char == "V": + # Legacy behaviour was to use V8. The reason was float64 being the + # default dtype and that having 8 bytes. + dtype = np.dtype("V8") + else: + dtype = np.dtype(dtype_char) + + discovered_dtype, _ = _discover_array_parameters([], type(dtype)) + + assert discovered_dtype == dtype + assert discovered_dtype.itemsize == dtype.itemsize + + @pytest.mark.parametrize("dtype", np.typecodes["Integer"]) + def test_scalar_to_int_coerce_does_not_cast(self, dtype): + """ + Signed integers are currently different in that they do not cast other + NumPy scalar, but instead use scalar.__int__(). The hardcoded + exception to this rule is `np.array(scalar, dtype=integer)`. + """ + dtype = np.dtype(dtype) + invalid_int = np.ulonglong(-1) + + float_nan = np.float64(np.nan) + + for scalar in [float_nan, invalid_int]: + # This is a special case using casting logic and thus not failing: + coerced = np.array(scalar, dtype=dtype) + cast = np.array(scalar).astype(dtype) + assert_array_equal(coerced, cast) + + # However these fail: + with pytest.raises((ValueError, OverflowError)): + np.array([scalar], dtype=dtype) + with pytest.raises((ValueError, OverflowError)): + cast[()] = scalar + + +class TestTimeScalars: + @pytest.mark.parametrize("dtype", [np.int64, np.float32]) + @pytest.mark.parametrize("scalar", + [param(np.timedelta64("NaT", "s"), id="timedelta64[s](NaT)"), + param(np.timedelta64(123, "s"), id="timedelta64[s]"), + param(np.datetime64("NaT", "generic"), id="datetime64[generic](NaT)"), + param(np.datetime64(1, "D"), id="datetime64[D]")],) + def test_coercion_basic(self, dtype, scalar): + # Note the `[scalar]` is there because np.array(scalar) uses stricter + # `scalar.__int__()` rules for backward compatibility right now. + arr = np.array(scalar, dtype=dtype) + cast = np.array(scalar).astype(dtype) + assert_array_equal(arr, cast) + + ass = np.ones((), dtype=dtype) + if issubclass(dtype, np.integer): + with pytest.raises(TypeError): + # raises, as would np.array([scalar], dtype=dtype), this is + # conversion from times, but behaviour of integers. + ass[()] = scalar + else: + ass[()] = scalar + assert_array_equal(ass, cast) + + @pytest.mark.parametrize("dtype", [np.int64, np.float32]) + @pytest.mark.parametrize("scalar", + [param(np.timedelta64(123, "ns"), id="timedelta64[ns]"), + param(np.timedelta64(12, "generic"), id="timedelta64[generic]")]) + def test_coercion_timedelta_convert_to_number(self, dtype, scalar): + # Only "ns" and "generic" timedeltas can be converted to numbers + # so these are slightly special. + arr = np.array(scalar, dtype=dtype) + cast = np.array(scalar).astype(dtype) + ass = np.ones((), dtype=dtype) + ass[()] = scalar # raises, as would np.array([scalar], dtype=dtype) + + assert_array_equal(arr, cast) + assert_array_equal(cast, cast) + + @pytest.mark.parametrize("dtype", ["S6", "U6"]) + @pytest.mark.parametrize(["val", "unit"], + [param(123, "s", id="[s]"), param(123, "D", id="[D]")]) + def test_coercion_assignment_datetime(self, val, unit, dtype): + # String from datetime64 assignment is currently special cased to + # never use casting. This is because casting will error in this + # case, and traditionally in most cases the behaviour is maintained + # like this. (`np.array(scalar, dtype="U6")` would have failed before) + # TODO: This discrepancy _should_ be resolved, either by relaxing the + # cast, or by deprecating the first part. + scalar = np.datetime64(val, unit) + dtype = np.dtype(dtype) + cut_string = dtype.type(str(scalar)[:6]) + + arr = np.array(scalar, dtype=dtype) + assert arr[()] == cut_string + ass = np.ones((), dtype=dtype) + ass[()] = scalar + assert ass[()] == cut_string + + with pytest.raises(RuntimeError): + # However, unlike the above assignment using `str(scalar)[:6]` + # due to being handled by the string DType and not be casting + # the explicit cast fails: + np.array(scalar).astype(dtype) + + + @pytest.mark.parametrize(["val", "unit"], + [param(123, "s", id="[s]"), param(123, "D", id="[D]")]) + def test_coercion_assignment_timedelta(self, val, unit): + scalar = np.timedelta64(val, unit) + + # Unlike datetime64, timedelta allows the unsafe cast: + np.array(scalar, dtype="S6") + cast = np.array(scalar).astype("S6") + ass = np.ones((), dtype="S6") + ass[()] = scalar + expected = scalar.astype("S")[:6] + assert cast[()] == expected + assert ass[()] == expected + +class TestNested: + def test_nested_simple(self): + initial = [1.2] + nested = initial + for i in range(np.MAXDIMS - 1): + nested = [nested] + + arr = np.array(nested, dtype="float64") + assert arr.shape == (1,) * np.MAXDIMS + with pytest.raises(ValueError): + np.array([nested], dtype="float64") + + # We discover object automatically at this time: + with assert_warns(np.VisibleDeprecationWarning): + arr = np.array([nested]) + assert arr.dtype == np.dtype("O") + assert arr.shape == (1,) * np.MAXDIMS + assert arr.item() is initial + + def test_pathological_self_containing(self): + # Test that this also works for two nested sequences + l = [] + l.append(l) + arr = np.array([l, l, l], dtype=object) + assert arr.shape == (3,) + (1,) * (np.MAXDIMS - 1) + + # Also check a ragged case: + arr = np.array([l, [None], l], dtype=object) + assert arr.shape == (3, 1) + + @pytest.mark.parametrize("arraylike", arraylikes()) + def test_nested_arraylikes(self, arraylike): + # We try storing an array like into an array, but the array-like + # will have too many dimensions. This means the shape discovery + # decides that the array-like must be treated as an object (a special + # case of ragged discovery). The result will be an array with one + # dimension less than the maximum dimensions, and the array being + # assigned to it (which does work for object or if `float(arraylike)` + # works). + initial = arraylike(np.ones((1, 1))) + + nested = initial + for i in range(np.MAXDIMS - 1): + nested = [nested] + + with pytest.warns(DeprecationWarning): + # It will refuse to assign the array into + np.array(nested, dtype="float64") + + # If this is object, we end up assigning a (1, 1) array into (1,) + # (due to running out of dimensions), this is currently supported but + # a special case which is not ideal. + arr = np.array(nested, dtype=object) + assert arr.shape == (1,) * np.MAXDIMS + assert arr.item() == np.array(initial).item() + + @pytest.mark.parametrize("arraylike", arraylikes()) + def test_uneven_depth_ragged(self, arraylike): + arr = np.arange(4).reshape((2, 2)) + arr = arraylike(arr) + + # Array is ragged in the second dimension already: + out = np.array([arr, [arr]], dtype=object) + assert out.shape == (2,) + assert out[0] is arr + assert type(out[1]) is list + + # Array is ragged in the third dimension: + with pytest.raises(ValueError): + # This is a broadcast error during assignment, because + # the array shape would be (2, 2, 2) but `arr[0, 0] = arr` fails. + np.array([arr, [arr, arr]], dtype=object) + + def test_empty_sequence(self): + arr = np.array([[], [1], [[1]]], dtype=object) + assert arr.shape == (3,) + + # The empty sequence stops further dimension discovery, so the + # result shape will be (0,) which leads to an error during: + with pytest.raises(ValueError): + np.array([[], np.empty((0, 1))], dtype=object) + + def test_array_of_different_depths(self): + # When multiple arrays (or array-likes) are included in a + # sequences and have different depth, we currently discover + # as many dimensions as they share. (see also gh-17224) + arr = np.zeros((3, 2)) + mismatch_first_dim = np.zeros((1, 2)) + mismatch_second_dim = np.zeros((3, 3)) + + dtype, shape = _discover_array_parameters( + [arr, mismatch_second_dim], dtype=np.dtype("O")) + assert shape == (2, 3) + + dtype, shape = _discover_array_parameters( + [arr, mismatch_first_dim], dtype=np.dtype("O")) + assert shape == (2,) + # The second case is currently supported because the arrays + # can be stored as objects: + res = np.asarray([arr, mismatch_first_dim], dtype=np.dtype("O")) + assert res[0] is arr + assert res[1] is mismatch_first_dim + + +class TestBadSequences: + # These are tests for bad objects passed into `np.array`, in general + # these have undefined behaviour. In the old code they partially worked + # when now they will fail. We could (and maybe should) create a copy + # of all sequences to be safe against bad-actors. + + def test_growing_list(self): + # List to coerce, `mylist` will append to it during coercion + obj = [] + class mylist(list): + def __len__(self): + obj.append([1, 2]) + return super().__len__() + + obj.append(mylist([1, 2])) + + with pytest.raises(RuntimeError): + np.array(obj) + + # Note: We do not test a shrinking list. These do very evil things + # and the only way to fix them would be to copy all sequences. + # (which may be a real option in the future). + + def test_mutated_list(self): + # List to coerce, `mylist` will mutate the first element + obj = [] + class mylist(list): + def __len__(self): + obj[0] = [2, 3] # replace with a different list. + return super().__len__() + + obj.append([2, 3]) + obj.append(mylist([1, 2])) + with pytest.raises(RuntimeError): + np.array(obj) + + def test_replace_0d_array(self): + # List to coerce, `mylist` will mutate the first element + obj = [] + class baditem: + def __len__(self): + obj[0][0] = 2 # replace with a different list. + raise ValueError("not actually a sequence!") + + def __getitem__(self): + pass + + # Runs into a corner case in the new code, the `array(2)` is cached + # so replacing it invalidates the cache. + obj.append([np.array(2), baditem()]) + with pytest.raises(RuntimeError): + np.array(obj) + + +class TestArrayLikes: + @pytest.mark.parametrize("arraylike", arraylikes()) + def test_0d_object_special_case(self, arraylike): + arr = np.array(0.) + obj = arraylike(arr) + # A single array-like is always converted: + res = np.array(obj, dtype=object) + assert_array_equal(arr, res) + + # But a single 0-D nested array-like never: + res = np.array([obj], dtype=object) + assert res[0] is obj + + def test_0d_generic_special_case(self): + class ArraySubclass(np.ndarray): + def __float__(self): + raise TypeError("e.g. quantities raise on this") + + arr = np.array(0.) + obj = arr.view(ArraySubclass) + res = np.array(obj) + # The subclass is simply cast: + assert_array_equal(arr, res) + + # If the 0-D array-like is included, __float__ is currently + # guaranteed to be used. We may want to change that, quantities + # and masked arrays half make use of this. + with pytest.raises(TypeError): + np.array([obj]) + + # The same holds for memoryview: + obj = memoryview(arr) + res = np.array(obj) + assert_array_equal(arr, res) + with pytest.raises(ValueError): + # The error type does not matter much here. + np.array([obj]) + + def test_arraylike_classes(self): + # The classes of array-likes should generally be acceptable to be + # stored inside a numpy (object) array. This tests all of the + # special attributes (since all are checked during coercion). + arr = np.array(np.int64) + assert arr[()] is np.int64 + arr = np.array([np.int64]) + assert arr[0] is np.int64 + + # This also works for properties/unbound methods: + class ArrayLike: + @property + def __array_interface__(self): + pass + + @property + def __array_struct__(self): + pass + + def __array__(self): + pass + + arr = np.array(ArrayLike) + assert arr[()] is ArrayLike + arr = np.array([ArrayLike]) + assert arr[0] is ArrayLike + + @pytest.mark.skipif( + np.dtype(np.intp).itemsize < 8, reason="Needs 64bit platform") + def test_too_large_array_error_paths(self): + """Test the error paths, including for memory leaks""" + arr = np.array(0, dtype="uint8") + # Guarantees that a contiguous copy won't work: + arr = np.broadcast_to(arr, 2**62) + + for i in range(5): + # repeat, to ensure caching cannot have an effect: + with pytest.raises(MemoryError): + np.array(arr) + with pytest.raises(MemoryError): + np.array([arr]) + + @pytest.mark.parametrize("attribute", + ["__array_interface__", "__array__", "__array_struct__"]) + @pytest.mark.parametrize("error", [RecursionError, MemoryError]) + def test_bad_array_like_attributes(self, attribute, error): + # RecursionError and MemoryError are considered fatal. All errors + # (except AttributeError) should probably be raised in the future, + # but shapely made use of it, so it will require a deprecation. + + class BadInterface: + def __getattr__(self, attr): + if attr == attribute: + raise error + super().__getattr__(attr) + + with pytest.raises(error): + np.array(BadInterface()) + + @pytest.mark.parametrize("error", [RecursionError, MemoryError]) + def test_bad_array_like_bad_length(self, error): + # RecursionError and MemoryError are considered "critical" in + # sequences. We could expand this more generally though. (NumPy 1.20) + class BadSequence: + def __len__(self): + raise error + def __getitem__(self): + # must have getitem to be a Sequence + return 1 + + with pytest.raises(error): + np.array(BadSequence()) + diff --git a/numpy/core/tests/test_arraymethod.py b/numpy/core/tests/test_arraymethod.py new file mode 100644 index 000000000000..49aa9f6dfcfa --- /dev/null +++ b/numpy/core/tests/test_arraymethod.py @@ -0,0 +1,94 @@ +""" +This file tests the generic aspects of ArrayMethod. At the time of writing +this is private API, but when added, public API may be added here. +""" + +import sys +import types +from typing import Any, Type + +import pytest + +import numpy as np +from numpy.core._multiarray_umath import _get_castingimpl as get_castingimpl + + +class TestResolveDescriptors: + # Test mainly error paths of the resolve_descriptors function, + # note that the `casting_unittests` tests exercise this non-error paths. + + # Casting implementations are the main/only current user: + method = get_castingimpl(type(np.dtype("d")), type(np.dtype("f"))) + + @pytest.mark.parametrize("args", [ + (True,), # Not a tuple. + ((None,)), # Too few elements + ((None, None, None),), # Too many + ((None, None),), # Input dtype is None, which is invalid. + ((np.dtype("d"), True),), # Output dtype is not a dtype + ((np.dtype("f"), None),), # Input dtype does not match method + ]) + def test_invalid_arguments(self, args): + with pytest.raises(TypeError): + self.method._resolve_descriptors(*args) + + +class TestSimpleStridedCall: + # Test mainly error paths of the resolve_descriptors function, + # note that the `casting_unittests` tests exercise this non-error paths. + + # Casting implementations are the main/only current user: + method = get_castingimpl(type(np.dtype("d")), type(np.dtype("f"))) + + @pytest.mark.parametrize(["args", "error"], [ + ((True,), TypeError), # Not a tuple + (((None,),), TypeError), # Too few elements + ((None, None), TypeError), # Inputs are not arrays. + (((None, None, None),), TypeError), # Too many + (((np.arange(3), np.arange(3)),), TypeError), # Incorrect dtypes + (((np.ones(3, dtype=">d"), np.ones(3, dtype="<f")),), + TypeError), # Does not support byte-swapping + (((np.ones((2, 2), dtype="d"), np.ones((2, 2), dtype="f")),), + ValueError), # not 1-D + (((np.ones(3, dtype="d"), np.ones(4, dtype="f")),), + ValueError), # different length + (((np.frombuffer(b"\0x00"*3*2, dtype="d"), + np.frombuffer(b"\0x00"*3, dtype="f")),), + ValueError), # output not writeable + ]) + def test_invalid_arguments(self, args, error): + # This is private API, which may be modified freely + with pytest.raises(error): + self.method._simple_strided_call(*args) + + +@pytest.mark.skipif(sys.version_info < (3, 9), reason="Requires python 3.9") +class TestClassGetItem: + @pytest.mark.parametrize( + "cls", [np.ndarray, np.recarray, np.chararray, np.matrix, np.memmap] + ) + def test_class_getitem(self, cls: Type[np.ndarray]) -> None: + """Test `ndarray.__class_getitem__`.""" + alias = cls[Any, Any] + assert isinstance(alias, types.GenericAlias) + assert alias.__origin__ is cls + + @pytest.mark.parametrize("arg_len", range(4)) + def test_subscript_tuple(self, arg_len: int) -> None: + arg_tup = (Any,) * arg_len + if arg_len == 2: + assert np.ndarray[arg_tup] + else: + with pytest.raises(TypeError): + np.ndarray[arg_tup] + + def test_subscript_scalar(self) -> None: + with pytest.raises(TypeError): + np.ndarray[Any] + + +@pytest.mark.skipif(sys.version_info >= (3, 9), reason="Requires python 3.8") +def test_class_getitem_38() -> None: + match = "Type subscription requires python >= 3.9" + with pytest.raises(TypeError, match=match): + np.ndarray[Any, Any] diff --git a/numpy/core/tests/test_arrayprint.py b/numpy/core/tests/test_arrayprint.py index 6214e325c44b..25826d8eda3c 100644 --- a/numpy/core/tests/test_arrayprint.py +++ b/numpy/core/tests/test_arrayprint.py @@ -1,17 +1,17 @@ -# -*- coding: utf-8 -*- -from __future__ import division, absolute_import, print_function - import sys import gc +from hypothesis import given +from hypothesis.extra import numpy as hynp import pytest import numpy as np from numpy.testing import ( assert_, assert_equal, assert_raises, assert_warns, HAS_REFCOUNT, + assert_raises_regex, ) import textwrap -class TestArrayRepr(object): +class TestArrayRepr: def test_nan_inf(self): x = np.array([np.nan, np.inf]) assert_equal(repr(x), 'array([nan, inf])') @@ -44,7 +44,7 @@ def __new__(cls, inp): return obj def __getitem__(self, ind): - ret = super(sub, self).__getitem__(ind) + ret = super().__getitem__(ind) return sub(ret) # test that object + subclass is OK: @@ -66,7 +66,7 @@ def __new__(cls, inp): return obj def __getitem__(self, ind): - ret = super(sub, self).__getitem__(ind) + ret = super().__getitem__(ind) return sub(ret) x = sub(1) @@ -89,6 +89,7 @@ def __getitem__(self, ind): assert_equal(repr(x), 'sub(sub(sub(..., dtype=object), dtype=object), dtype=object)') assert_equal(str(x), '...') + x[()] = 0 # resolve circular references for garbage collector # nested 0d-subclass-object x = sub(None) @@ -99,7 +100,7 @@ def __getitem__(self, ind): # gh-10663 class DuckCounter(np.ndarray): def __getitem__(self, item): - result = super(DuckCounter, self).__getitem__(item) + result = super().__getitem__(item) if not isinstance(result, DuckCounter): result = result[...].view(DuckCounter) return result @@ -123,11 +124,13 @@ def test_self_containing(self): arr0d[()] = arr0d assert_equal(repr(arr0d), 'array(array(..., dtype=object), dtype=object)') + arr0d[()] = 0 # resolve recursion for garbage collector arr1d = np.array([None, None]) arr1d[1] = arr1d assert_equal(repr(arr1d), 'array([None, array(..., dtype=object)], dtype=object)') + arr1d[1] = 0 # resolve recursion for garbage collector first = np.array(None) second = np.array(None) @@ -135,6 +138,7 @@ def test_self_containing(self): second[()] = first assert_equal(repr(first), 'array(array(array(..., dtype=object), dtype=object), dtype=object)') + first[()] = 0 # resolve circular references for garbage collector def test_containing_list(self): # printing square brackets directly would be ambiguuous @@ -155,7 +159,7 @@ def test_fieldless_structured(self): assert_equal(repr(arr_no_fields), 'array([(), (), (), ()], dtype=[])') -class TestComplexArray(object): +class TestComplexArray: def test_str(self): rvals = [0, 1, -1, np.inf, -np.inf, np.nan] cvals = [complex(rp, ip) for rp in rvals for ip in rvals] @@ -202,7 +206,7 @@ def test_str(self): for res, val in zip(actual, wanted): assert_equal(res, val) -class TestArray2String(object): +class TestArray2String: def test_basic(self): """Basic test of array2string.""" a = np.arange(3) @@ -210,6 +214,15 @@ def test_basic(self): assert_(np.array2string(a, max_line_width=4, legacy='1.13') == '[0 1\n 2]') assert_(np.array2string(a, max_line_width=4) == '[0\n 1\n 2]') + def test_unexpected_kwarg(self): + # ensure than an appropriate TypeError + # is raised when array2string receives + # an unexpected kwarg + + with assert_raises_regex(TypeError, 'nonsense'): + np.array2string(np.array([1, 2, 3]), + nonsense=None) + def test_format_function(self): """Test custom format function for each element in array.""" def _format_function(x): @@ -221,12 +234,8 @@ def _format_function(x): return 'O' x = np.arange(3) - if sys.version_info[0] >= 3: - x_hex = "[0x0 0x1 0x2]" - x_oct = "[0o0 0o1 0o2]" - else: - x_hex = "[0x0L 0x1L 0x2L]" - x_oct = "[0L 01L 02L]" + x_hex = "[0x0 0x1 0x2]" + x_oct = "[0o0 0o1 0o2]" assert_(np.array2string(x, formatter={'all':_format_function}) == "[. o O]") assert_(np.array2string(x, formatter={'int_kind':_format_function}) == @@ -248,11 +257,6 @@ def _format_function(x): assert_(np.array2string(s, formatter={'numpystr':lambda s: s*2}) == '[abcabc defdef]') - # check for backcompat that using FloatFormat works and emits warning - with assert_warns(DeprecationWarning): - fmt = np.core.arrayprint.FloatFormat(x, 9, 'maxprec', False) - assert_equal(np.array2string(x, formatter={'float_kind': fmt}), - '[0. 1. 2.]') def test_structure_format(self): dt = np.dtype([('name', np.str_, 16), ('grades', np.float64, (2,))]) @@ -390,6 +394,93 @@ def test_wide_element(self): "[ 'xxxxx']" ) + def test_multiline_repr(self): + class MultiLine: + def __repr__(self): + return "Line 1\nLine 2" + + a = np.array([[None, MultiLine()], [MultiLine(), None]]) + + assert_equal( + np.array2string(a), + '[[None Line 1\n' + ' Line 2]\n' + ' [Line 1\n' + ' Line 2 None]]' + ) + assert_equal( + np.array2string(a, max_line_width=5), + '[[None\n' + ' Line 1\n' + ' Line 2]\n' + ' [Line 1\n' + ' Line 2\n' + ' None]]' + ) + assert_equal( + repr(a), + 'array([[None, Line 1\n' + ' Line 2],\n' + ' [Line 1\n' + ' Line 2, None]], dtype=object)' + ) + + class MultiLineLong: + def __repr__(self): + return "Line 1\nLooooooooooongestLine2\nLongerLine 3" + + a = np.array([[None, MultiLineLong()], [MultiLineLong(), None]]) + assert_equal( + repr(a), + 'array([[None, Line 1\n' + ' LooooooooooongestLine2\n' + ' LongerLine 3 ],\n' + ' [Line 1\n' + ' LooooooooooongestLine2\n' + ' LongerLine 3 , None]], dtype=object)' + ) + assert_equal( + np.array_repr(a, 20), + 'array([[None,\n' + ' Line 1\n' + ' LooooooooooongestLine2\n' + ' LongerLine 3 ],\n' + ' [Line 1\n' + ' LooooooooooongestLine2\n' + ' LongerLine 3 ,\n' + ' None]],\n' + ' dtype=object)' + ) + + def test_nested_array_repr(self): + a = np.empty((2, 2), dtype=object) + a[0, 0] = np.eye(2) + a[0, 1] = np.eye(3) + a[1, 0] = None + a[1, 1] = np.ones((3, 1)) + assert_equal( + repr(a), + 'array([[array([[1., 0.],\n' + ' [0., 1.]]), array([[1., 0., 0.],\n' + ' [0., 1., 0.],\n' + ' [0., 0., 1.]])],\n' + ' [None, array([[1.],\n' + ' [1.],\n' + ' [1.]])]], dtype=object)' + ) + + @given(hynp.from_dtype(np.dtype("U"))) + def test_any_text(self, text): + # This test checks that, given any value that can be represented in an + # array of dtype("U") (i.e. unicode string), ... + a = np.array([text, text, text]) + # casting a list of them to an array does not e.g. truncate the value + assert_equal(a[0], text) + # and that np.array2string puts a newline in the expected location + expected_repr = "[{0!r} {0!r}\n {0!r}]".format(text) + result = np.array2string(a, max_line_width=len(repr(text)) * 2 + 3) + assert_equal(result, expected_repr) + @pytest.mark.skipif(not HAS_REFCOUNT, reason="Python lacks refcounts") def test_refcount(self): # make sure we do not hold references to the array due to a recursive @@ -404,7 +495,7 @@ def test_refcount(self): gc.enable() assert_(r1 == r2) -class TestPrintOptions(object): +class TestPrintOptions: """Test getting and setting global print options.""" def setup(self): @@ -458,16 +549,10 @@ def test_formatter_reset(self): assert_equal(repr(x), "array([0., 1., 2.])") def test_0d_arrays(self): - unicode = type(u'') + assert_equal(str(np.array(u'café', '<U4')), u'café') - assert_equal(unicode(np.array(u'café', '<U4')), u'café') - - if sys.version_info[0] >= 3: - assert_equal(repr(np.array('café', '<U4')), - "array('café', dtype='<U4')") - else: - assert_equal(repr(np.array(u'café', '<U4')), - "array(u'caf\\xe9', dtype='<U4')") + assert_equal(repr(np.array('café', '<U4')), + "array('café', dtype='<U4')") assert_equal(str(np.array('test', np.str_)), 'test') a = np.zeros(1, dtype=[('a', '<i4', (3,))]) @@ -673,6 +758,10 @@ def test_floatmode(self): assert_equal(repr(c), "array([1.00000000+1.00000000j, 1.12345679+1.12345679j])") + # test unique special case (gh-18609) + a = np.float64.fromhex('-1p-97') + assert_equal(np.float64(np.array2string(a, floatmode='unique')), a) + def test_legacy_mode_scalars(self): # in legacy mode, str of floats get truncated, and complex scalars # use * for non-finite imaginary part @@ -700,7 +789,7 @@ def test_dtype_linewidth_wrapping(self): array([10., 11., 12., 13., 14., 15., 16., 17., 18., 19., 20., 21., 22.], dtype=float32)""")) - styp = '<U4' if sys.version_info[0] >= 3 else '|S4' + styp = '<U4' assert_equal(repr(np.ones(3, dtype=styp)), "array(['1', '1', '1'], dtype='{}')".format(styp)) assert_equal(repr(np.ones(12, dtype=styp)), textwrap.dedent("""\ @@ -832,20 +921,23 @@ def test_edgeitems(self): [[ 0.]]]])""") ) + def test_bad_args(self): + assert_raises(ValueError, np.set_printoptions, threshold=float('nan')) + assert_raises(TypeError, np.set_printoptions, threshold='1') + assert_raises(TypeError, np.set_printoptions, threshold=b'1') + + assert_raises(TypeError, np.set_printoptions, precision='1') + assert_raises(TypeError, np.set_printoptions, precision=1.5) def test_unicode_object_array(): - import sys - if sys.version_info[0] >= 3: - expected = "array(['é'], dtype=object)" - else: - expected = "array([u'\\xe9'], dtype=object)" + expected = "array(['é'], dtype=object)" x = np.array([u'\xe9'], dtype=object) assert_equal(repr(x), expected) -class TestContextManager(object): +class TestContextManager: def test_ctx_mgr(self): - # test that context manager actuall works + # test that context manager actually works with np.printoptions(precision=2): s = str(np.array([2.0]) / 3) assert_equal(s, '[0.67]') diff --git a/numpy/core/tests/test_casting_unittests.py b/numpy/core/tests/test_casting_unittests.py new file mode 100644 index 000000000000..cb479209030b --- /dev/null +++ b/numpy/core/tests/test_casting_unittests.py @@ -0,0 +1,711 @@ +""" +The tests exercise the casting machinery in a more low-level manner. +The reason is mostly to test a new implementation of the casting machinery. + +Unlike most tests in NumPy, these are closer to unit-tests rather +than integration tests. +""" + +import pytest +import textwrap +import enum +import random + +import numpy as np +from numpy.lib.stride_tricks import as_strided + +from numpy.testing import assert_array_equal +from numpy.core._multiarray_umath import _get_castingimpl as get_castingimpl + + +# Simple skips object, parametric and long double (unsupported by struct) +simple_dtypes = "?bhilqBHILQefdFD" +if np.dtype("l").itemsize != np.dtype("q").itemsize: + # Remove l and L, the table was generated with 64bit linux in mind. + simple_dtypes = simple_dtypes.replace("l", "").replace("L", "") +simple_dtypes = [type(np.dtype(c)) for c in simple_dtypes] + + +def simple_dtype_instances(): + for dtype_class in simple_dtypes: + dt = dtype_class() + yield pytest.param(dt, id=str(dt)) + if dt.byteorder != "|": + dt = dt.newbyteorder() + yield pytest.param(dt, id=str(dt)) + + +def get_expected_stringlength(dtype): + """Returns the string length when casting the basic dtypes to strings. + """ + if dtype == np.bool_: + return 5 + if dtype.kind in "iu": + if dtype.itemsize == 1: + length = 3 + elif dtype.itemsize == 2: + length = 5 + elif dtype.itemsize == 4: + length = 10 + elif dtype.itemsize == 8: + length = 20 + else: + raise AssertionError(f"did not find expected length for {dtype}") + + if dtype.kind == "i": + length += 1 # adds one character for the sign + + return length + + # Note: Can't do dtype comparison for longdouble on windows + if dtype.char == "g": + return 48 + elif dtype.char == "G": + return 48 * 2 + elif dtype.kind == "f": + return 32 # also for half apparently. + elif dtype.kind == "c": + return 32 * 2 + + raise AssertionError(f"did not find expected length for {dtype}") + + +class Casting(enum.IntEnum): + no = 0 + equiv = 1 + safe = 2 + same_kind = 3 + unsafe = 4 + cast_is_view = 1 << 16 + + +def _get_cancast_table(): + table = textwrap.dedent(""" + X ? b h i l q B H I L Q e f d g F D G S U V O M m + ? # = = = = = = = = = = = = = = = = = = = = = . = + b . # = = = = . . . . . = = = = = = = = = = = . = + h . ~ # = = = . . . . . ~ = = = = = = = = = = . = + i . ~ ~ # = = . . . . . ~ ~ = = ~ = = = = = = . = + l . ~ ~ ~ # # . . . . . ~ ~ = = ~ = = = = = = . = + q . ~ ~ ~ # # . . . . . ~ ~ = = ~ = = = = = = . = + B . ~ = = = = # = = = = = = = = = = = = = = = . = + H . ~ ~ = = = ~ # = = = ~ = = = = = = = = = = . = + I . ~ ~ ~ = = ~ ~ # = = ~ ~ = = ~ = = = = = = . = + L . ~ ~ ~ ~ ~ ~ ~ ~ # # ~ ~ = = ~ = = = = = = . ~ + Q . ~ ~ ~ ~ ~ ~ ~ ~ # # ~ ~ = = ~ = = = = = = . ~ + e . . . . . . . . . . . # = = = = = = = = = = . . + f . . . . . . . . . . . ~ # = = = = = = = = = . . + d . . . . . . . . . . . ~ ~ # = ~ = = = = = = . . + g . . . . . . . . . . . ~ ~ ~ # ~ ~ = = = = = . . + F . . . . . . . . . . . . . . . # = = = = = = . . + D . . . . . . . . . . . . . . . ~ # = = = = = . . + G . . . . . . . . . . . . . . . ~ ~ # = = = = . . + S . . . . . . . . . . . . . . . . . . # = = = . . + U . . . . . . . . . . . . . . . . . . . # = = . . + V . . . . . . . . . . . . . . . . . . . . # = . . + O . . . . . . . . . . . . . . . . . . . . = # . . + M . . . . . . . . . . . . . . . . . . . . = = # . + m . . . . . . . . . . . . . . . . . . . . = = . # + """).strip().split("\n") + dtypes = [type(np.dtype(c)) for c in table[0][2::2]] + + convert_cast = {".": Casting.unsafe, "~": Casting.same_kind, + "=": Casting.safe, "#": Casting.equiv, + " ": -1} + + cancast = {} + for from_dt, row in zip(dtypes, table[1:]): + cancast[from_dt] = {} + for to_dt, c in zip(dtypes, row[2::2]): + cancast[from_dt][to_dt] = convert_cast[c] + + return cancast + +CAST_TABLE = _get_cancast_table() + + +class TestChanges: + """ + These test cases exercise some behaviour changes + """ + @pytest.mark.parametrize("string", ["S", "U"]) + @pytest.mark.parametrize("floating", ["e", "f", "d", "g"]) + def test_float_to_string(self, floating, string): + assert np.can_cast(floating, string) + # 100 is long enough to hold any formatted floating + assert np.can_cast(floating, f"{string}100") + + def test_to_void(self): + # But in general, we do consider these safe: + assert np.can_cast("d", "V") + assert np.can_cast("S20", "V") + + # Do not consider it a safe cast if the void is too smaller: + assert not np.can_cast("d", "V1") + assert not np.can_cast("S20", "V1") + assert not np.can_cast("U1", "V1") + # Structured to unstructured is just like any other: + assert np.can_cast("d,i", "V", casting="same_kind") + # Unstructured void to unstructured is actually no cast at all: + assert np.can_cast("V3", "V", casting="no") + assert np.can_cast("V0", "V", casting="no") + + +class TestCasting: + size = 1500 # Best larger than NPY_LOWLEVEL_BUFFER_BLOCKSIZE * itemsize + + def get_data(self, dtype1, dtype2): + if dtype2 is None or dtype1.itemsize >= dtype2.itemsize: + length = self.size // dtype1.itemsize + else: + length = self.size // dtype2.itemsize + + # Assume that the base array is well enough aligned for all inputs. + arr1 = np.empty(length, dtype=dtype1) + assert arr1.flags.c_contiguous + assert arr1.flags.aligned + + values = [random.randrange(-128, 128) for _ in range(length)] + + for i, value in enumerate(values): + # Use item assignment to ensure this is not using casting: + arr1[i] = value + + if dtype2 is None: + if dtype1.char == "?": + values = [bool(v) for v in values] + return arr1, values + + if dtype2.char == "?": + values = [bool(v) for v in values] + + arr2 = np.empty(length, dtype=dtype2) + assert arr2.flags.c_contiguous + assert arr2.flags.aligned + + for i, value in enumerate(values): + # Use item assignment to ensure this is not using casting: + arr2[i] = value + + return arr1, arr2, values + + def get_data_variation(self, arr1, arr2, aligned=True, contig=True): + """ + Returns a copy of arr1 that may be non-contiguous or unaligned, and a + matching array for arr2 (although not a copy). + """ + if contig: + stride1 = arr1.dtype.itemsize + stride2 = arr2.dtype.itemsize + elif aligned: + stride1 = 2 * arr1.dtype.itemsize + stride2 = 2 * arr2.dtype.itemsize + else: + stride1 = arr1.dtype.itemsize + 1 + stride2 = arr2.dtype.itemsize + 1 + + max_size1 = len(arr1) * 3 * arr1.dtype.itemsize + 1 + max_size2 = len(arr2) * 3 * arr2.dtype.itemsize + 1 + from_bytes = np.zeros(max_size1, dtype=np.uint8) + to_bytes = np.zeros(max_size2, dtype=np.uint8) + + # Sanity check that the above is large enough: + assert stride1 * len(arr1) <= from_bytes.nbytes + assert stride2 * len(arr2) <= to_bytes.nbytes + + if aligned: + new1 = as_strided(from_bytes[:-1].view(arr1.dtype), + arr1.shape, (stride1,)) + new2 = as_strided(to_bytes[:-1].view(arr2.dtype), + arr2.shape, (stride2,)) + else: + new1 = as_strided(from_bytes[1:].view(arr1.dtype), + arr1.shape, (stride1,)) + new2 = as_strided(to_bytes[1:].view(arr2.dtype), + arr2.shape, (stride2,)) + + new1[...] = arr1 + + if not contig: + # Ensure we did not overwrite bytes that should not be written: + offset = arr1.dtype.itemsize if aligned else 0 + buf = from_bytes[offset::stride1].tobytes() + assert buf.count(b"\0") == len(buf) + + if contig: + assert new1.flags.c_contiguous + assert new2.flags.c_contiguous + else: + assert not new1.flags.c_contiguous + assert not new2.flags.c_contiguous + + if aligned: + assert new1.flags.aligned + assert new2.flags.aligned + else: + assert not new1.flags.aligned or new1.dtype.alignment == 1 + assert not new2.flags.aligned or new2.dtype.alignment == 1 + + return new1, new2 + + @pytest.mark.parametrize("from_Dt", simple_dtypes) + def test_simple_cancast(self, from_Dt): + for to_Dt in simple_dtypes: + cast = get_castingimpl(from_Dt, to_Dt) + + for from_dt in [from_Dt(), from_Dt().newbyteorder()]: + default = cast._resolve_descriptors((from_dt, None))[1][1] + assert default == to_Dt() + del default + + for to_dt in [to_Dt(), to_Dt().newbyteorder()]: + casting, (from_res, to_res) = cast._resolve_descriptors( + (from_dt, to_dt)) + assert(type(from_res) == from_Dt) + assert(type(to_res) == to_Dt) + if casting & Casting.cast_is_view: + # If a view is acceptable, this is "no" casting + # and byte order must be matching. + assert casting == Casting.no | Casting.cast_is_view + # The above table lists this as "equivalent" + assert Casting.equiv == CAST_TABLE[from_Dt][to_Dt] + # Note that to_res may not be the same as from_dt + assert from_res.isnative == to_res.isnative + else: + if from_Dt == to_Dt: + # Note that to_res may not be the same as from_dt + assert from_res.isnative != to_res.isnative + assert casting == CAST_TABLE[from_Dt][to_Dt] + + if from_Dt is to_Dt: + assert(from_dt is from_res) + assert(to_dt is to_res) + + + @pytest.mark.filterwarnings("ignore::numpy.ComplexWarning") + @pytest.mark.parametrize("from_dt", simple_dtype_instances()) + def test_simple_direct_casts(self, from_dt): + """ + This test checks numeric direct casts for dtypes supported also by the + struct module (plus complex). It tries to be test a wide range of + inputs, but skips over possibly undefined behaviour (e.g. int rollover). + Longdouble and CLongdouble are tested, but only using double precision. + + If this test creates issues, it should possibly just be simplified + or even removed (checking whether unaligned/non-contiguous casts give + the same results is useful, though). + """ + for to_dt in simple_dtype_instances(): + to_dt = to_dt.values[0] + cast = get_castingimpl(type(from_dt), type(to_dt)) + + casting, (from_res, to_res) = cast._resolve_descriptors( + (from_dt, to_dt)) + + if from_res is not from_dt or to_res is not to_dt: + # Do not test this case, it is handled in multiple steps, + # each of which should is tested individually. + return + + safe = (casting & ~Casting.cast_is_view) <= Casting.safe + del from_res, to_res, casting + + arr1, arr2, values = self.get_data(from_dt, to_dt) + + cast._simple_strided_call((arr1, arr2)) + + # Check via python list + assert arr2.tolist() == values + + # Check that the same results are achieved for strided loops + arr1_o, arr2_o = self.get_data_variation(arr1, arr2, True, False) + cast._simple_strided_call((arr1_o, arr2_o)) + + assert_array_equal(arr2_o, arr2) + assert arr2_o.tobytes() == arr2.tobytes() + + # Check if alignment makes a difference, but only if supported + # and only if the alignment can be wrong + if ((from_dt.alignment == 1 and to_dt.alignment == 1) or + not cast._supports_unaligned): + return + + arr1_o, arr2_o = self.get_data_variation(arr1, arr2, False, True) + cast._simple_strided_call((arr1_o, arr2_o)) + + assert_array_equal(arr2_o, arr2) + assert arr2_o.tobytes() == arr2.tobytes() + + arr1_o, arr2_o = self.get_data_variation(arr1, arr2, False, False) + cast._simple_strided_call((arr1_o, arr2_o)) + + assert_array_equal(arr2_o, arr2) + assert arr2_o.tobytes() == arr2.tobytes() + + del arr1_o, arr2_o, cast + + @pytest.mark.parametrize("from_Dt", simple_dtypes) + def test_numeric_to_times(self, from_Dt): + # We currently only implement contiguous loops, so only need to + # test those. + from_dt = from_Dt() + + time_dtypes = [np.dtype("M8"), np.dtype("M8[ms]"), np.dtype("M8[4D]"), + np.dtype("m8"), np.dtype("m8[ms]"), np.dtype("m8[4D]")] + for time_dt in time_dtypes: + cast = get_castingimpl(type(from_dt), type(time_dt)) + + casting, (from_res, to_res) = cast._resolve_descriptors( + (from_dt, time_dt)) + + assert from_res is from_dt + assert to_res is time_dt + del from_res, to_res + + assert(casting & CAST_TABLE[from_Dt][type(time_dt)]) + + int64_dt = np.dtype(np.int64) + arr1, arr2, values = self.get_data(from_dt, int64_dt) + arr2 = arr2.view(time_dt) + arr2[...] = np.datetime64("NaT") + + if time_dt == np.dtype("M8"): + # This is a bit of a strange path, and could probably be removed + arr1[-1] = 0 # ensure at least one value is not NaT + + # The cast currently succeeds, but the values are invalid: + cast._simple_strided_call((arr1, arr2)) + with pytest.raises(ValueError): + str(arr2[-1]) # e.g. conversion to string fails + return + + cast._simple_strided_call((arr1, arr2)) + + assert [int(v) for v in arr2.tolist()] == values + + # Check that the same results are achieved for strided loops + arr1_o, arr2_o = self.get_data_variation(arr1, arr2, True, False) + cast._simple_strided_call((arr1_o, arr2_o)) + + assert_array_equal(arr2_o, arr2) + assert arr2_o.tobytes() == arr2.tobytes() + + @pytest.mark.parametrize( + ["from_dt", "to_dt", "expected_casting", "nom", "denom"], + [("M8[ns]", None, + Casting.no | Casting.cast_is_view, 1, 1), + (str(np.dtype("M8[ns]").newbyteorder()), None, Casting.equiv, 1, 1), + ("M8", "M8[ms]", Casting.safe | Casting.cast_is_view, 1, 1), + ("M8[ms]", "M8", Casting.unsafe, 1, 1), # should be invalid cast + ("M8[5ms]", "M8[5ms]", Casting.no | Casting.cast_is_view, 1, 1), + ("M8[ns]", "M8[ms]", Casting.same_kind, 1, 10**6), + ("M8[ms]", "M8[ns]", Casting.safe, 10**6, 1), + ("M8[ms]", "M8[7ms]", Casting.same_kind, 1, 7), + ("M8[4D]", "M8[1M]", Casting.same_kind, None, + # give full values based on NumPy 1.19.x + [-2**63, 0, -1, 1314, -1315, 564442610]), + ("m8[ns]", None, Casting.no | Casting.cast_is_view, 1, 1), + (str(np.dtype("m8[ns]").newbyteorder()), None, Casting.equiv, 1, 1), + ("m8", "m8[ms]", Casting.safe | Casting.cast_is_view, 1, 1), + ("m8[ms]", "m8", Casting.unsafe, 1, 1), # should be invalid cast + ("m8[5ms]", "m8[5ms]", Casting.no | Casting.cast_is_view, 1, 1), + ("m8[ns]", "m8[ms]", Casting.same_kind, 1, 10**6), + ("m8[ms]", "m8[ns]", Casting.safe, 10**6, 1), + ("m8[ms]", "m8[7ms]", Casting.same_kind, 1, 7), + ("m8[4D]", "m8[1M]", Casting.unsafe, None, + # give full values based on NumPy 1.19.x + [-2**63, 0, 0, 1314, -1315, 564442610])]) + def test_time_to_time(self, from_dt, to_dt, expected_casting, nom, denom): + from_dt = np.dtype(from_dt) + if to_dt is not None: + to_dt = np.dtype(to_dt) + + # Test a few values for casting (results generated with NumPy 1.19) + values = np.array([-2**63, 1, 2**63-1, 10000, -10000, 2**32]) + values = values.astype(np.dtype("int64").newbyteorder(from_dt.byteorder)) + assert values.dtype.byteorder == from_dt.byteorder + assert np.isnat(values.view(from_dt)[0]) + + DType = type(from_dt) + cast = get_castingimpl(DType, DType) + casting, (from_res, to_res) = cast._resolve_descriptors((from_dt, to_dt)) + assert from_res is from_dt + assert to_res is to_dt or to_dt is None + assert casting == expected_casting + + if nom is not None: + expected_out = (values * nom // denom).view(to_res) + expected_out[0] = "NaT" + else: + expected_out = np.empty_like(values) + expected_out[...] = denom + expected_out = expected_out.view(to_dt) + + orig_arr = values.view(from_dt) + orig_out = np.empty_like(expected_out) + + if casting == Casting.unsafe and (to_dt == "m8" or to_dt == "M8"): + # Casting from non-generic to generic units is an error and should + # probably be reported as an invalid cast earlier. + with pytest.raises(ValueError): + cast._simple_strided_call((orig_arr, orig_out)) + return + + for aligned in [True, True]: + for contig in [True, True]: + arr, out = self.get_data_variation( + orig_arr, orig_out, aligned, contig) + out[...] = 0 + cast._simple_strided_call((arr, out)) + assert_array_equal(out.view("int64"), expected_out.view("int64")) + + def string_with_modified_length(self, dtype, change_length): + fact = 1 if dtype.char == "S" else 4 + length = dtype.itemsize // fact + change_length + return np.dtype(f"{dtype.byteorder}{dtype.char}{length}") + + @pytest.mark.parametrize("other_DT", simple_dtypes) + @pytest.mark.parametrize("string_char", ["S", "U"]) + def test_string_cancast(self, other_DT, string_char): + fact = 1 if string_char == "S" else 4 + + string_DT = type(np.dtype(string_char)) + cast = get_castingimpl(other_DT, string_DT) + + other_dt = other_DT() + expected_length = get_expected_stringlength(other_dt) + string_dt = np.dtype(f"{string_char}{expected_length}") + + safety, (res_other_dt, res_dt) = cast._resolve_descriptors((other_dt, None)) + assert res_dt.itemsize == expected_length * fact + assert safety == Casting.safe # we consider to string casts "safe" + assert isinstance(res_dt, string_DT) + + # These casts currently implement changing the string length, so + # check the cast-safety for too long/fixed string lengths: + for change_length in [-1, 0, 1]: + if change_length >= 0: + expected_safety = Casting.safe + else: + expected_safety = Casting.same_kind + + to_dt = self.string_with_modified_length(string_dt, change_length) + safety, (_, res_dt) = cast._resolve_descriptors((other_dt, to_dt)) + assert res_dt is to_dt + assert safety == expected_safety + + # The opposite direction is always considered unsafe: + cast = get_castingimpl(string_DT, other_DT) + + safety, _ = cast._resolve_descriptors((string_dt, other_dt)) + assert safety == Casting.unsafe + + cast = get_castingimpl(string_DT, other_DT) + safety, (_, res_dt) = cast._resolve_descriptors((string_dt, None)) + assert safety == Casting.unsafe + assert other_dt is res_dt # returns the singleton for simple dtypes + + @pytest.mark.parametrize("string_char", ["S", "U"]) + @pytest.mark.parametrize("other_dt", simple_dtype_instances()) + def test_simple_string_casts_roundtrip(self, other_dt, string_char): + """ + Tests casts from and to string by checking the roundtripping property. + + The test also covers some string to string casts (but not all). + + If this test creates issues, it should possibly just be simplified + or even removed (checking whether unaligned/non-contiguous casts give + the same results is useful, though). + """ + string_DT = type(np.dtype(string_char)) + + cast = get_castingimpl(type(other_dt), string_DT) + cast_back = get_castingimpl(string_DT, type(other_dt)) + _, (res_other_dt, string_dt) = cast._resolve_descriptors((other_dt, None)) + + if res_other_dt is not other_dt: + # do not support non-native byteorder, skip test in that case + assert other_dt.byteorder != res_other_dt.byteorder + return + + orig_arr, values = self.get_data(other_dt, None) + str_arr = np.zeros(len(orig_arr), dtype=string_dt) + string_dt_short = self.string_with_modified_length(string_dt, -1) + str_arr_short = np.zeros(len(orig_arr), dtype=string_dt_short) + string_dt_long = self.string_with_modified_length(string_dt, 1) + str_arr_long = np.zeros(len(orig_arr), dtype=string_dt_long) + + assert not cast._supports_unaligned # if support is added, should test + assert not cast_back._supports_unaligned + + for contig in [True, False]: + other_arr, str_arr = self.get_data_variation( + orig_arr, str_arr, True, contig) + _, str_arr_short = self.get_data_variation( + orig_arr, str_arr_short.copy(), True, contig) + _, str_arr_long = self.get_data_variation( + orig_arr, str_arr_long, True, contig) + + cast._simple_strided_call((other_arr, str_arr)) + + cast._simple_strided_call((other_arr, str_arr_short)) + assert_array_equal(str_arr.astype(string_dt_short), str_arr_short) + + cast._simple_strided_call((other_arr, str_arr_long)) + assert_array_equal(str_arr, str_arr_long) + + if other_dt.kind == "b": + # Booleans do not roundtrip + continue + + other_arr[...] = 0 + cast_back._simple_strided_call((str_arr, other_arr)) + assert_array_equal(orig_arr, other_arr) + + other_arr[...] = 0 + cast_back._simple_strided_call((str_arr_long, other_arr)) + assert_array_equal(orig_arr, other_arr) + + @pytest.mark.parametrize("other_dt", ["S8", "<U8", ">U8"]) + @pytest.mark.parametrize("string_char", ["S", "U"]) + def test_string_to_string_cancast(self, other_dt, string_char): + other_dt = np.dtype(other_dt) + + fact = 1 if string_char == "S" else 4 + div = 1 if other_dt.char == "S" else 4 + + string_DT = type(np.dtype(string_char)) + cast = get_castingimpl(type(other_dt), string_DT) + + expected_length = other_dt.itemsize // div + string_dt = np.dtype(f"{string_char}{expected_length}") + + safety, (res_other_dt, res_dt) = cast._resolve_descriptors((other_dt, None)) + assert res_dt.itemsize == expected_length * fact + assert isinstance(res_dt, string_DT) + + if other_dt.char == string_char: + if other_dt.isnative: + expected_safety = Casting.no | Casting.cast_is_view + else: + expected_safety = Casting.equiv + elif string_char == "U": + expected_safety = Casting.safe + else: + expected_safety = Casting.unsafe + + assert expected_safety == safety + + for change_length in [-1, 0, 1]: + to_dt = self.string_with_modified_length(string_dt, change_length) + safety, (_, res_dt) = cast._resolve_descriptors((other_dt, to_dt)) + + assert res_dt is to_dt + if expected_safety == Casting.unsafe: + assert safety == expected_safety + elif change_length < 0: + assert safety == Casting.same_kind + elif change_length == 0: + assert safety == expected_safety + elif change_length > 0: + assert safety == Casting.safe + + @pytest.mark.parametrize("order1", [">", "<"]) + @pytest.mark.parametrize("order2", [">", "<"]) + def test_unicode_byteswapped_cast(self, order1, order2): + # Very specific tests (not using the castingimpl directly) + # that tests unicode bytedwaps including for unaligned array data. + dtype1 = np.dtype(f"{order1}U30") + dtype2 = np.dtype(f"{order2}U30") + data1 = np.empty(30 * 4 + 1, dtype=np.uint8)[1:].view(dtype1) + data2 = np.empty(30 * 4 + 1, dtype=np.uint8)[1:].view(dtype2) + if dtype1.alignment != 1: + # alignment should always be >1, but skip the check if not + assert not data1.flags.aligned + assert not data2.flags.aligned + + element = "this is a ünicode string‽" + data1[()] = element + # Test both `data1` and `data1.copy()` (which should be aligned) + for data in [data1, data1.copy()]: + data2[...] = data1 + assert data2[()] == element + assert data2.copy()[()] == element + + def test_void_to_string_special_case(self): + # Cover a small special case in void to string casting that could + # probably just as well be turned into an error (compare + # `test_object_to_parametric_internal_error` below). + assert np.array([], dtype="V5").astype("S").dtype.itemsize == 5 + assert np.array([], dtype="V5").astype("U").dtype.itemsize == 4 * 5 + + def test_object_to_parametric_internal_error(self): + # We reject casting from object to a parametric type, without + # figuring out the correct instance first. + object_dtype = type(np.dtype(object)) + other_dtype = type(np.dtype(str)) + cast = get_castingimpl(object_dtype, other_dtype) + with pytest.raises(TypeError, + match="casting from object to the parametric DType"): + cast._resolve_descriptors((np.dtype("O"), None)) + + @pytest.mark.parametrize("dtype", simple_dtype_instances()) + def test_object_and_simple_resolution(self, dtype): + # Simple test to exercise the cast when no instance is specified + object_dtype = type(np.dtype(object)) + cast = get_castingimpl(object_dtype, type(dtype)) + + safety, (_, res_dt) = cast._resolve_descriptors((np.dtype("O"), dtype)) + assert safety == Casting.unsafe + assert res_dt is dtype + + safety, (_, res_dt) = cast._resolve_descriptors((np.dtype("O"), None)) + assert safety == Casting.unsafe + assert res_dt == dtype.newbyteorder("=") + + @pytest.mark.parametrize("dtype", simple_dtype_instances()) + def test_simple_to_object_resolution(self, dtype): + # Simple test to exercise the cast when no instance is specified + object_dtype = type(np.dtype(object)) + cast = get_castingimpl(type(dtype), object_dtype) + + safety, (_, res_dt) = cast._resolve_descriptors((dtype, None)) + assert safety == Casting.safe + assert res_dt is np.dtype("O") + + @pytest.mark.parametrize("casting", ["no", "unsafe"]) + def test_void_and_structured_with_subarray(self, casting): + # test case corresponding to gh-19325 + dtype = np.dtype([("foo", "<f4", (3, 2))]) + expected = casting == "unsafe" + assert np.can_cast("V4", dtype, casting=casting) == expected + assert np.can_cast(dtype, "V4", casting=casting) == expected + + @pytest.mark.parametrize("dtype", np.typecodes["All"]) + def test_object_casts_NULL_None_equivalence(self, dtype): + # None to <other> casts may succeed or fail, but a NULL'ed array must + # behave the same as one filled with None's. + arr_normal = np.array([None] * 5) + arr_NULLs = np.empty_like([None] * 5) + # If the check fails (maybe it should) the test would lose its purpose: + assert arr_NULLs.tobytes() == b"\x00" * arr_NULLs.nbytes + + try: + expected = arr_normal.astype(dtype) + except TypeError: + with pytest.raises(TypeError): + arr_NULLs.astype(dtype), + else: + assert_array_equal(expected, arr_NULLs.astype(dtype)) + + @pytest.mark.parametrize("dtype", + np.typecodes["AllInteger"] + np.typecodes["AllFloat"]) + def test_nonstandard_bool_to_other(self, dtype): + # simple test for casting bool_ to numeric types, which should not + # expose the detail that NumPy bools can sometimes take values other + # than 0 and 1. See also gh-19514. + nonstandard_bools = np.array([0, 3, -7], dtype=np.int8).view(bool) + res = nonstandard_bools.astype(dtype) + expected = [0, 1, 1] + assert_array_equal(res, expected) + diff --git a/numpy/core/tests/test_conversion_utils.py b/numpy/core/tests/test_conversion_utils.py new file mode 100644 index 000000000000..d8849ee29b0b --- /dev/null +++ b/numpy/core/tests/test_conversion_utils.py @@ -0,0 +1,205 @@ +""" +Tests for numpy/core/src/multiarray/conversion_utils.c +""" +import re + +import pytest + +import numpy as np +import numpy.core._multiarray_tests as mt +from numpy.testing import assert_warns + + +class StringConverterTestCase: + allow_bytes = True + case_insensitive = True + exact_match = False + warn = True + + def _check_value_error(self, val): + pattern = r'\(got {}\)'.format(re.escape(repr(val))) + with pytest.raises(ValueError, match=pattern) as exc: + self.conv(val) + + def _check_conv_assert_warn(self, val, expected): + if self.warn: + with assert_warns(DeprecationWarning) as exc: + assert self.conv(val) == expected + else: + assert self.conv(val) == expected + + def _check(self, val, expected): + """Takes valid non-deprecated inputs for converters, + runs converters on inputs, checks correctness of outputs, + warnings and errors""" + assert self.conv(val) == expected + + if self.allow_bytes: + assert self.conv(val.encode('ascii')) == expected + else: + with pytest.raises(TypeError): + self.conv(val.encode('ascii')) + + if len(val) != 1: + if self.exact_match: + self._check_value_error(val[:1]) + self._check_value_error(val + '\0') + else: + self._check_conv_assert_warn(val[:1], expected) + + if self.case_insensitive: + if val != val.lower(): + self._check_conv_assert_warn(val.lower(), expected) + if val != val.upper(): + self._check_conv_assert_warn(val.upper(), expected) + else: + if val != val.lower(): + self._check_value_error(val.lower()) + if val != val.upper(): + self._check_value_error(val.upper()) + + def test_wrong_type(self): + # common cases which apply to all the below + with pytest.raises(TypeError): + self.conv({}) + with pytest.raises(TypeError): + self.conv([]) + + def test_wrong_value(self): + # nonsense strings + self._check_value_error('') + self._check_value_error('\N{greek small letter pi}') + + if self.allow_bytes: + self._check_value_error(b'') + # bytes which can't be converted to strings via utf8 + self._check_value_error(b"\xFF") + if self.exact_match: + self._check_value_error("there's no way this is supported") + + +class TestByteorderConverter(StringConverterTestCase): + """ Tests of PyArray_ByteorderConverter """ + conv = mt.run_byteorder_converter + warn = False + + def test_valid(self): + for s in ['big', '>']: + self._check(s, 'NPY_BIG') + for s in ['little', '<']: + self._check(s, 'NPY_LITTLE') + for s in ['native', '=']: + self._check(s, 'NPY_NATIVE') + for s in ['ignore', '|']: + self._check(s, 'NPY_IGNORE') + for s in ['swap']: + self._check(s, 'NPY_SWAP') + + +class TestSortkindConverter(StringConverterTestCase): + """ Tests of PyArray_SortkindConverter """ + conv = mt.run_sortkind_converter + warn = False + + def test_valid(self): + self._check('quicksort', 'NPY_QUICKSORT') + self._check('heapsort', 'NPY_HEAPSORT') + self._check('mergesort', 'NPY_STABLESORT') # alias + self._check('stable', 'NPY_STABLESORT') + + +class TestSelectkindConverter(StringConverterTestCase): + """ Tests of PyArray_SelectkindConverter """ + conv = mt.run_selectkind_converter + case_insensitive = False + exact_match = True + + def test_valid(self): + self._check('introselect', 'NPY_INTROSELECT') + + +class TestSearchsideConverter(StringConverterTestCase): + """ Tests of PyArray_SearchsideConverter """ + conv = mt.run_searchside_converter + def test_valid(self): + self._check('left', 'NPY_SEARCHLEFT') + self._check('right', 'NPY_SEARCHRIGHT') + + +class TestOrderConverter(StringConverterTestCase): + """ Tests of PyArray_OrderConverter """ + conv = mt.run_order_converter + warn = False + + def test_valid(self): + self._check('c', 'NPY_CORDER') + self._check('f', 'NPY_FORTRANORDER') + self._check('a', 'NPY_ANYORDER') + self._check('k', 'NPY_KEEPORDER') + + def test_flatten_invalid_order(self): + # invalid after gh-14596 + with pytest.raises(ValueError): + self.conv('Z') + for order in [False, True, 0, 8]: + with pytest.raises(TypeError): + self.conv(order) + + +class TestClipmodeConverter(StringConverterTestCase): + """ Tests of PyArray_ClipmodeConverter """ + conv = mt.run_clipmode_converter + def test_valid(self): + self._check('clip', 'NPY_CLIP') + self._check('wrap', 'NPY_WRAP') + self._check('raise', 'NPY_RAISE') + + # integer values allowed here + assert self.conv(np.CLIP) == 'NPY_CLIP' + assert self.conv(np.WRAP) == 'NPY_WRAP' + assert self.conv(np.RAISE) == 'NPY_RAISE' + + +class TestCastingConverter(StringConverterTestCase): + """ Tests of PyArray_CastingConverter """ + conv = mt.run_casting_converter + case_insensitive = False + exact_match = True + + def test_valid(self): + self._check("no", "NPY_NO_CASTING") + self._check("equiv", "NPY_EQUIV_CASTING") + self._check("safe", "NPY_SAFE_CASTING") + self._check("same_kind", "NPY_SAME_KIND_CASTING") + self._check("unsafe", "NPY_UNSAFE_CASTING") + + +class TestIntpConverter: + """ Tests of PyArray_IntpConverter """ + conv = mt.run_intp_converter + + def test_basic(self): + assert self.conv(1) == (1,) + assert self.conv((1, 2)) == (1, 2) + assert self.conv([1, 2]) == (1, 2) + assert self.conv(()) == () + + def test_none(self): + # once the warning expires, this will raise TypeError + with pytest.warns(DeprecationWarning): + assert self.conv(None) == () + + def test_float(self): + with pytest.raises(TypeError): + self.conv(1.0) + with pytest.raises(TypeError): + self.conv([1, 1.0]) + + def test_too_large(self): + with pytest.raises(ValueError): + self.conv(2**64) + + def test_too_many_dims(self): + assert self.conv([1]*32) == (1,)*32 + with pytest.raises(ValueError): + self.conv([1]*33) diff --git a/numpy/core/tests/test_cpu_dispatcher.py b/numpy/core/tests/test_cpu_dispatcher.py new file mode 100644 index 000000000000..2f7eac7e8e90 --- /dev/null +++ b/numpy/core/tests/test_cpu_dispatcher.py @@ -0,0 +1,42 @@ +from numpy.core._multiarray_umath import __cpu_features__, __cpu_baseline__, __cpu_dispatch__ +from numpy.core import _umath_tests +from numpy.testing import assert_equal + +def test_dispatcher(): + """ + Testing the utilities of the CPU dispatcher + """ + targets = ( + "SSE2", "SSE41", "AVX2", + "VSX", "VSX2", "VSX3", + "NEON", "ASIMD", "ASIMDHP" + ) + highest_sfx = "" # no suffix for the baseline + all_sfx = [] + for feature in reversed(targets): + # skip baseline features, by the default `CCompilerOpt` do not generate separated objects + # for the baseline, just one object combined all of them via 'baseline' option + # within the configuration statements. + if feature in __cpu_baseline__: + continue + # check compiler and running machine support + if feature not in __cpu_dispatch__ or not __cpu_features__[feature]: + continue + + if not highest_sfx: + highest_sfx = "_" + feature + all_sfx.append("func" + "_" + feature) + + test = _umath_tests.test_dispatch() + assert_equal(test["func"], "func" + highest_sfx) + assert_equal(test["var"], "var" + highest_sfx) + + if highest_sfx: + assert_equal(test["func_xb"], "func" + highest_sfx) + assert_equal(test["var_xb"], "var" + highest_sfx) + else: + assert_equal(test["func_xb"], "nobase") + assert_equal(test["var_xb"], "nobase") + + all_sfx.append("func") # add the baseline + assert_equal(test["all"], all_sfx) diff --git a/numpy/core/tests/test_cpu_features.py b/numpy/core/tests/test_cpu_features.py new file mode 100644 index 000000000000..2ccbff41ca63 --- /dev/null +++ b/numpy/core/tests/test_cpu_features.py @@ -0,0 +1,174 @@ +import sys, platform, re, pytest +from numpy.core._multiarray_umath import __cpu_features__ + +def assert_features_equal(actual, desired, fname): + __tracebackhide__ = True # Hide traceback for py.test + actual, desired = str(actual), str(desired) + if actual == desired: + return + detected = str(__cpu_features__).replace("'", "") + try: + with open("/proc/cpuinfo", "r") as fd: + cpuinfo = fd.read(2048) + except Exception as err: + cpuinfo = str(err) + + try: + import subprocess + auxv = subprocess.check_output(['/bin/true'], env=dict(LD_SHOW_AUXV="1")) + auxv = auxv.decode() + except Exception as err: + auxv = str(err) + + import textwrap + error_report = textwrap.indent( +""" +########################################### +### Extra debugging information +########################################### +------------------------------------------- +--- NumPy Detections +------------------------------------------- +%s +------------------------------------------- +--- SYS / CPUINFO +------------------------------------------- +%s.... +------------------------------------------- +--- SYS / AUXV +------------------------------------------- +%s +""" % (detected, cpuinfo, auxv), prefix='\r') + + raise AssertionError(( + "Failure Detection\n" + " NAME: '%s'\n" + " ACTUAL: %s\n" + " DESIRED: %s\n" + "%s" + ) % (fname, actual, desired, error_report)) + +class AbstractTest: + features = [] + features_groups = {} + features_map = {} + features_flags = set() + + def load_flags(self): + # a hook + pass + def test_features(self): + self.load_flags() + for gname, features in self.features_groups.items(): + test_features = [self.cpu_have(f) for f in features] + assert_features_equal(__cpu_features__.get(gname), all(test_features), gname) + + for feature_name in self.features: + cpu_have = self.cpu_have(feature_name) + npy_have = __cpu_features__.get(feature_name) + assert_features_equal(npy_have, cpu_have, feature_name) + + def cpu_have(self, feature_name): + map_names = self.features_map.get(feature_name, feature_name) + if isinstance(map_names, str): + return map_names in self.features_flags + for f in map_names: + if f in self.features_flags: + return True + return False + + def load_flags_cpuinfo(self, magic_key): + self.features_flags = self.get_cpuinfo_item(magic_key) + + def get_cpuinfo_item(self, magic_key): + values = set() + with open('/proc/cpuinfo') as fd: + for line in fd: + if not line.startswith(magic_key): + continue + flags_value = [s.strip() for s in line.split(':', 1)] + if len(flags_value) == 2: + values = values.union(flags_value[1].upper().split()) + return values + + def load_flags_auxv(self): + import subprocess + auxv = subprocess.check_output(['/bin/true'], env=dict(LD_SHOW_AUXV="1")) + for at in auxv.split(b'\n'): + if not at.startswith(b"AT_HWCAP"): + continue + hwcap_value = [s.strip() for s in at.split(b':', 1)] + if len(hwcap_value) == 2: + self.features_flags = self.features_flags.union( + hwcap_value[1].upper().decode().split() + ) + +is_linux = sys.platform.startswith('linux') +is_cygwin = sys.platform.startswith('cygwin') +machine = platform.machine() +is_x86 = re.match("^(amd64|x86|i386|i686)", machine, re.IGNORECASE) +@pytest.mark.skipif( + not (is_linux or is_cygwin) or not is_x86, reason="Only for Linux and x86" +) +class Test_X86_Features(AbstractTest): + features = [ + "MMX", "SSE", "SSE2", "SSE3", "SSSE3", "SSE41", "POPCNT", "SSE42", + "AVX", "F16C", "XOP", "FMA4", "FMA3", "AVX2", "AVX512F", "AVX512CD", + "AVX512ER", "AVX512PF", "AVX5124FMAPS", "AVX5124VNNIW", "AVX512VPOPCNTDQ", + "AVX512VL", "AVX512BW", "AVX512DQ", "AVX512VNNI", "AVX512IFMA", + "AVX512VBMI", "AVX512VBMI2", "AVX512BITALG", + ] + features_groups = dict( + AVX512_KNL = ["AVX512F", "AVX512CD", "AVX512ER", "AVX512PF"], + AVX512_KNM = ["AVX512F", "AVX512CD", "AVX512ER", "AVX512PF", "AVX5124FMAPS", + "AVX5124VNNIW", "AVX512VPOPCNTDQ"], + AVX512_SKX = ["AVX512F", "AVX512CD", "AVX512BW", "AVX512DQ", "AVX512VL"], + AVX512_CLX = ["AVX512F", "AVX512CD", "AVX512BW", "AVX512DQ", "AVX512VL", "AVX512VNNI"], + AVX512_CNL = ["AVX512F", "AVX512CD", "AVX512BW", "AVX512DQ", "AVX512VL", "AVX512IFMA", + "AVX512VBMI"], + AVX512_ICL = ["AVX512F", "AVX512CD", "AVX512BW", "AVX512DQ", "AVX512VL", "AVX512IFMA", + "AVX512VBMI", "AVX512VNNI", "AVX512VBMI2", "AVX512BITALG", "AVX512VPOPCNTDQ"], + ) + features_map = dict( + SSE3="PNI", SSE41="SSE4_1", SSE42="SSE4_2", FMA3="FMA", + AVX512VNNI="AVX512_VNNI", AVX512BITALG="AVX512_BITALG", AVX512VBMI2="AVX512_VBMI2", + AVX5124FMAPS="AVX512_4FMAPS", AVX5124VNNIW="AVX512_4VNNIW", AVX512VPOPCNTDQ="AVX512_VPOPCNTDQ", + ) + def load_flags(self): + self.load_flags_cpuinfo("flags") + +is_power = re.match("^(powerpc|ppc)64", machine, re.IGNORECASE) +@pytest.mark.skipif(not is_linux or not is_power, reason="Only for Linux and Power") +class Test_POWER_Features(AbstractTest): + features = ["VSX", "VSX2", "VSX3"] + features_map = dict(VSX2="ARCH_2_07", VSX3="ARCH_3_00") + + def load_flags(self): + self.load_flags_auxv() + +is_arm = re.match("^(arm|aarch64)", machine, re.IGNORECASE) +@pytest.mark.skipif(not is_linux or not is_arm, reason="Only for Linux and ARM") +class Test_ARM_Features(AbstractTest): + features = [ + "NEON", "ASIMD", "FPHP", "ASIMDHP", "ASIMDDP", "ASIMDFHM" + ] + features_groups = dict( + NEON_FP16 = ["NEON", "HALF"], + NEON_VFPV4 = ["NEON", "VFPV4"], + ) + def load_flags(self): + self.load_flags_cpuinfo("Features") + arch = self.get_cpuinfo_item("CPU architecture") + # in case of mounting virtual filesystem of aarch64 kernel + is_rootfs_v8 = int('0'+next(iter(arch))) > 7 if arch else 0 + if re.match("^(aarch64|AARCH64)", machine) or is_rootfs_v8: + self.features_map = dict( + NEON="ASIMD", HALF="ASIMD", VFPV4="ASIMD" + ) + else: + self.features_map = dict( + # ELF auxiliary vector and /proc/cpuinfo on Linux kernel(armv8 aarch32) + # doesn't provide information about ASIMD, so we assume that ASIMD is supported + # if the kernel reports any one of the following ARM8 features. + ASIMD=("AES", "SHA1", "SHA2", "PMULL", "CRC32") + ) diff --git a/numpy/core/tests/test_custom_dtypes.py b/numpy/core/tests/test_custom_dtypes.py new file mode 100644 index 000000000000..6bcc45d6b398 --- /dev/null +++ b/numpy/core/tests/test_custom_dtypes.py @@ -0,0 +1,201 @@ +import pytest + +import numpy as np +from numpy.testing import assert_array_equal +from numpy.core._multiarray_umath import ( + _discover_array_parameters as discover_array_params, _get_sfloat_dtype) + + +SF = _get_sfloat_dtype() + + +class TestSFloat: + def _get_array(self, scaling, aligned=True): + if not aligned: + a = np.empty(3*8 + 1, dtype=np.uint8)[1:] + a = a.view(np.float64) + a[:] = [1., 2., 3.] + else: + a = np.array([1., 2., 3.]) + + a *= 1./scaling # the casting code also uses the reciprocal. + return a.view(SF(scaling)) + + def test_sfloat_rescaled(self): + sf = SF(1.) + sf2 = sf.scaled_by(2.) + assert sf2.get_scaling() == 2. + sf6 = sf2.scaled_by(3.) + assert sf6.get_scaling() == 6. + + def test_class_discovery(self): + # This does not test much, since we always discover the scaling as 1. + # But most of NumPy (when writing) does not understand DType classes + dt, _ = discover_array_params([1., 2., 3.], dtype=SF) + assert dt == SF(1.) + + @pytest.mark.parametrize("scaling", [1., -1., 2.]) + def test_scaled_float_from_floats(self, scaling): + a = np.array([1., 2., 3.], dtype=SF(scaling)) + + assert a.dtype.get_scaling() == scaling + assert_array_equal(scaling * a.view(np.float64), [1., 2., 3.]) + + def test_repr(self): + # Check the repr, mainly to cover the code paths: + assert repr(SF(scaling=1.)) == "_ScaledFloatTestDType(scaling=1.0)" + + @pytest.mark.parametrize("scaling", [1., -1., 2.]) + def test_sfloat_from_float(self, scaling): + a = np.array([1., 2., 3.]).astype(dtype=SF(scaling)) + + assert a.dtype.get_scaling() == scaling + assert_array_equal(scaling * a.view(np.float64), [1., 2., 3.]) + + @pytest.mark.parametrize("aligned", [True, False]) + @pytest.mark.parametrize("scaling", [1., -1., 2.]) + def test_sfloat_getitem(self, aligned, scaling): + a = self._get_array(1., aligned) + assert a.tolist() == [1., 2., 3.] + + @pytest.mark.parametrize("aligned", [True, False]) + def test_sfloat_casts(self, aligned): + a = self._get_array(1., aligned) + + assert np.can_cast(a, SF(-1.), casting="equiv") + assert not np.can_cast(a, SF(-1.), casting="no") + na = a.astype(SF(-1.)) + assert_array_equal(-1 * na.view(np.float64), a.view(np.float64)) + + assert np.can_cast(a, SF(2.), casting="same_kind") + assert not np.can_cast(a, SF(2.), casting="safe") + a2 = a.astype(SF(2.)) + assert_array_equal(2 * a2.view(np.float64), a.view(np.float64)) + + @pytest.mark.parametrize("aligned", [True, False]) + def test_sfloat_cast_internal_errors(self, aligned): + a = self._get_array(2e300, aligned) + + with pytest.raises(TypeError, + match="error raised inside the core-loop: non-finite factor!"): + a.astype(SF(2e-300)) + + def test_sfloat_promotion(self): + assert np.result_type(SF(2.), SF(3.)) == SF(3.) + assert np.result_type(SF(3.), SF(2.)) == SF(3.) + # Float64 -> SF(1.) and then promotes normally, so both of this work: + assert np.result_type(SF(3.), np.float64) == SF(3.) + assert np.result_type(np.float64, SF(0.5)) == SF(1.) + + # Test an undefined promotion: + with pytest.raises(TypeError): + np.result_type(SF(1.), np.int64) + + def test_basic_multiply(self): + a = self._get_array(2.) + b = self._get_array(4.) + + res = a * b + # multiplies dtype scaling and content separately: + assert res.dtype.get_scaling() == 8. + expected_view = a.view(np.float64) * b.view(np.float64) + assert_array_equal(res.view(np.float64), expected_view) + + def test_possible_and_impossible_reduce(self): + # For reductions to work, the first and last operand must have the + # same dtype. For this parametric DType that is not necessarily true. + a = self._get_array(2.) + # Addition reductin works (as of writing requires to pass initial + # because setting a scaled-float from the default `0` fails). + res = np.add.reduce(a, initial=0.) + assert res == a.astype(np.float64).sum() + + # But each multiplication changes the factor, so a reduction is not + # possible (the relaxed version of the old refusal to handle any + # flexible dtype). + with pytest.raises(TypeError, + match="the resolved dtypes are not compatible"): + np.multiply.reduce(a) + + def test_basic_ufunc_at(self): + float_a = np.array([1., 2., 3.]) + b = self._get_array(2.) + + float_b = b.view(np.float64).copy() + np.multiply.at(float_b, [1, 1, 1], float_a) + np.multiply.at(b, [1, 1, 1], float_a) + + assert_array_equal(b.view(np.float64), float_b) + + def test_basic_multiply_promotion(self): + float_a = np.array([1., 2., 3.]) + b = self._get_array(2.) + + res1 = float_a * b + res2 = b * float_a + + # one factor is one, so we get the factor of b: + assert res1.dtype == res2.dtype == b.dtype + expected_view = float_a * b.view(np.float64) + assert_array_equal(res1.view(np.float64), expected_view) + assert_array_equal(res2.view(np.float64), expected_view) + + # Check that promotion works when `out` is used: + np.multiply(b, float_a, out=res2) + with pytest.raises(TypeError): + # The promoter accepts this (maybe it should not), but the SFloat + # result cannot be cast to integer: + np.multiply(b, float_a, out=np.arange(3)) + + def test_basic_addition(self): + a = self._get_array(2.) + b = self._get_array(4.) + + res = a + b + # addition uses the type promotion rules for the result: + assert res.dtype == np.result_type(a.dtype, b.dtype) + expected_view = (a.astype(res.dtype).view(np.float64) + + b.astype(res.dtype).view(np.float64)) + assert_array_equal(res.view(np.float64), expected_view) + + def test_addition_cast_safety(self): + """The addition method is special for the scaled float, because it + includes the "cast" between different factors, thus cast-safety + is influenced by the implementation. + """ + a = self._get_array(2.) + b = self._get_array(-2.) + c = self._get_array(3.) + + # sign change is "equiv": + np.add(a, b, casting="equiv") + with pytest.raises(TypeError): + np.add(a, b, casting="no") + + # Different factor is "same_kind" (default) so check that "safe" fails + with pytest.raises(TypeError): + np.add(a, c, casting="safe") + + # Check that casting the output fails also (done by the ufunc here) + with pytest.raises(TypeError): + np.add(a, a, out=c, casting="safe") + + @pytest.mark.parametrize("ufunc", + [np.logical_and, np.logical_or, np.logical_xor]) + def test_logical_ufuncs_casts_to_bool(self, ufunc): + a = self._get_array(2.) + a[0] = 0. # make sure first element is considered False. + + float_equiv = a.astype(float) + expected = ufunc(float_equiv, float_equiv) + res = ufunc(a, a) + assert_array_equal(res, expected) + + # also check that the same works for reductions: + expected = ufunc.reduce(float_equiv) + res = ufunc.reduce(a) + assert_array_equal(res, expected) + + # The output casting does not match the bool, bool -> bool loop: + with pytest.raises(TypeError): + ufunc(a, a, out=np.empty(a.shape, dtype=int), casting="equiv") diff --git a/numpy/core/tests/test_cython.py b/numpy/core/tests/test_cython.py new file mode 100644 index 000000000000..a1f09d0fef12 --- /dev/null +++ b/numpy/core/tests/test_cython.py @@ -0,0 +1,134 @@ +import os +import shutil +import subprocess +import sys +import pytest + +import numpy as np + +# This import is copied from random.tests.test_extending +try: + import cython + from Cython.Compiler.Version import version as cython_version +except ImportError: + cython = None +else: + from distutils.version import LooseVersion + + # Cython 0.29.21 is required for Python 3.9 and there are + # other fixes in the 0.29 series that are needed even for earlier + # Python versions. + # Note: keep in sync with the one in pyproject.toml + required_version = LooseVersion("0.29.21") + if LooseVersion(cython_version) < required_version: + # too old or wrong cython, skip the test + cython = None + +pytestmark = pytest.mark.skipif(cython is None, reason="requires cython") + + +@pytest.fixture +def install_temp(request, tmp_path): + # Based in part on test_cython from random.tests.test_extending + + here = os.path.dirname(__file__) + ext_dir = os.path.join(here, "examples") + + cytest = str(tmp_path / "cytest") + + shutil.copytree(ext_dir, cytest) + # build the examples and "install" them into a temporary directory + + install_log = str(tmp_path / "tmp_install_log.txt") + subprocess.check_call( + [ + sys.executable, + "setup.py", + "build", + "install", + "--prefix", str(tmp_path / "installdir"), + "--single-version-externally-managed", + "--record", + install_log, + ], + cwd=cytest, + ) + + # In order to import the built module, we need its path to sys.path + # so parse that out of the record + with open(install_log) as fid: + for line in fid: + if "checks" in line: + sys.path.append(os.path.dirname(line)) + break + else: + raise RuntimeError(f'could not parse "{install_log}"') + + +def test_is_timedelta64_object(install_temp): + import checks + + assert checks.is_td64(np.timedelta64(1234)) + assert checks.is_td64(np.timedelta64(1234, "ns")) + assert checks.is_td64(np.timedelta64("NaT", "ns")) + + assert not checks.is_td64(1) + assert not checks.is_td64(None) + assert not checks.is_td64("foo") + assert not checks.is_td64(np.datetime64("now", "s")) + + +def test_is_datetime64_object(install_temp): + import checks + + assert checks.is_dt64(np.datetime64(1234, "ns")) + assert checks.is_dt64(np.datetime64("NaT", "ns")) + + assert not checks.is_dt64(1) + assert not checks.is_dt64(None) + assert not checks.is_dt64("foo") + assert not checks.is_dt64(np.timedelta64(1234)) + + +def test_get_datetime64_value(install_temp): + import checks + + dt64 = np.datetime64("2016-01-01", "ns") + + result = checks.get_dt64_value(dt64) + expected = dt64.view("i8") + + assert result == expected + + +def test_get_timedelta64_value(install_temp): + import checks + + td64 = np.timedelta64(12345, "h") + + result = checks.get_td64_value(td64) + expected = td64.view("i8") + + assert result == expected + + +def test_get_datetime64_unit(install_temp): + import checks + + dt64 = np.datetime64("2016-01-01", "ns") + result = checks.get_dt64_unit(dt64) + expected = 10 + assert result == expected + + td64 = np.timedelta64(12345, "h") + result = checks.get_dt64_unit(td64) + expected = 5 + assert result == expected + + +def test_abstract_scalars(install_temp): + import checks + + assert checks.is_integer(1) + assert checks.is_integer(np.int8(1)) + assert checks.is_integer(np.uint64(1)) diff --git a/numpy/core/tests/test_datetime.py b/numpy/core/tests/test_datetime.py index 942554caebcc..5294c7b8d6d7 100644 --- a/numpy/core/tests/test_datetime.py +++ b/numpy/core/tests/test_datetime.py @@ -1,14 +1,13 @@ -from __future__ import division, absolute_import, print_function - -import pickle import numpy import numpy as np import datetime import pytest from numpy.testing import ( - assert_, assert_equal, assert_raises, assert_warns, suppress_warnings + assert_, assert_equal, assert_raises, assert_warns, suppress_warnings, + assert_raises_regex, assert_array_equal, ) +from numpy.compat import pickle # Use pytz to test out various time zones if available try: @@ -23,10 +22,11 @@ RecursionError = RuntimeError # python < 3.5 -class TestDateTime(object): +class TestDateTime: def test_datetime_dtype_creation(self): for unit in ['Y', 'M', 'W', 'D', 'h', 'm', 's', 'ms', 'us', + 'μs', # alias for us 'ns', 'ps', 'fs', 'as']: dt1 = np.dtype('M8[750%s]' % unit) assert_(dt1 == np.dtype('datetime64[750%s]' % unit)) @@ -63,6 +63,7 @@ def test_datetime_dtype_creation(self): assert_raises(TypeError, np.dtype, 'm7') assert_raises(TypeError, np.dtype, 'M16') assert_raises(TypeError, np.dtype, 'm16') + assert_raises(TypeError, np.dtype, 'M8[3000000000ps]') def test_datetime_casting_rules(self): # Cannot cast safely/same_kind between timedelta and datetime @@ -74,6 +75,15 @@ def test_datetime_casting_rules(self): # Can cast safely/same_kind from integer to timedelta assert_(np.can_cast('i8', 'm8', casting='same_kind')) assert_(np.can_cast('i8', 'm8', casting='safe')) + assert_(np.can_cast('i4', 'm8', casting='same_kind')) + assert_(np.can_cast('i4', 'm8', casting='safe')) + assert_(np.can_cast('u4', 'm8', casting='same_kind')) + assert_(np.can_cast('u4', 'm8', casting='safe')) + + # Cannot cast safely from unsigned integer of the same size, which + # could overflow + assert_(np.can_cast('u8', 'm8', casting='same_kind')) + assert_(not np.can_cast('u8', 'm8', casting='safe')) # Cannot cast safely/same_kind from float to timedelta assert_(not np.can_cast('f4', 'm8', casting='same_kind')) @@ -128,15 +138,92 @@ def test_datetime_casting_rules(self): assert_(not np.can_cast('M8[h]', 'M8', casting='same_kind')) assert_(not np.can_cast('M8[h]', 'M8', casting='safe')) + def test_datetime_prefix_conversions(self): + # regression tests related to gh-19631; + # test metric prefixes from seconds down to + # attoseconds for bidirectional conversions + smaller_units = ['M8[7000ms]', + 'M8[2000us]', + 'M8[1000ns]', + 'M8[5000ns]', + 'M8[2000ps]', + 'M8[9000fs]', + 'M8[1000as]', + 'M8[2000000ps]', + 'M8[1000000as]', + 'M8[2000000000ps]', + 'M8[1000000000as]'] + larger_units = ['M8[7s]', + 'M8[2ms]', + 'M8[us]', + 'M8[5us]', + 'M8[2ns]', + 'M8[9ps]', + 'M8[1fs]', + 'M8[2us]', + 'M8[1ps]', + 'M8[2ms]', + 'M8[1ns]'] + for larger_unit, smaller_unit in zip(larger_units, smaller_units): + assert np.can_cast(larger_unit, smaller_unit, casting='safe') + assert np.can_cast(smaller_unit, larger_unit, casting='safe') + + @pytest.mark.parametrize("unit", [ + "s", "ms", "us", "ns", "ps", "fs", "as"]) + def test_prohibit_negative_datetime(self, unit): + with assert_raises(TypeError): + np.array([1], dtype=f"M8[-1{unit}]") + def test_compare_generic_nat(self): # regression tests for gh-6452 - assert_equal(np.datetime64('NaT'), - np.datetime64('2000') + np.timedelta64('NaT')) - # nb. we may want to make NaT != NaT true in the future - with suppress_warnings() as sup: - sup.filter(FutureWarning, ".*NAT ==") - assert_(np.datetime64('NaT') == np.datetime64('NaT', 'us')) - assert_(np.datetime64('NaT', 'us') == np.datetime64('NaT')) + assert_(np.datetime64('NaT') != + np.datetime64('2000') + np.timedelta64('NaT')) + assert_(np.datetime64('NaT') != np.datetime64('NaT', 'us')) + assert_(np.datetime64('NaT', 'us') != np.datetime64('NaT')) + + @pytest.mark.parametrize("size", [ + 3, 21, 217, 1000]) + def test_datetime_nat_argsort_stability(self, size): + # NaT < NaT should be False internally for + # sort stability + expected = np.arange(size) + arr = np.tile(np.datetime64('NaT'), size) + assert_equal(np.argsort(arr, kind='mergesort'), expected) + + @pytest.mark.parametrize("size", [ + 3, 21, 217, 1000]) + def test_timedelta_nat_argsort_stability(self, size): + # NaT < NaT should be False internally for + # sort stability + expected = np.arange(size) + arr = np.tile(np.timedelta64('NaT'), size) + assert_equal(np.argsort(arr, kind='mergesort'), expected) + + @pytest.mark.parametrize("arr, expected", [ + # the example provided in gh-12629 + (['NaT', 1, 2, 3], + [1, 2, 3, 'NaT']), + # multiple NaTs + (['NaT', 9, 'NaT', -707], + [-707, 9, 'NaT', 'NaT']), + # this sort explores another code path for NaT + ([1, -2, 3, 'NaT'], + [-2, 1, 3, 'NaT']), + # 2-D array + ([[51, -220, 'NaT'], + [-17, 'NaT', -90]], + [[-220, 51, 'NaT'], + [-90, -17, 'NaT']]), + ]) + @pytest.mark.parametrize("dtype", [ + 'M8[ns]', 'M8[us]', + 'm8[ns]', 'm8[us]']) + def test_datetime_timedelta_sort_nat(self, arr, expected, dtype): + # fix for gh-12629 and gh-15063; NaT sorting to end of array + arr = np.array(arr, dtype=dtype) + expected = np.array(expected, dtype=dtype) + arr.sort() + assert_equal(arr, expected) def test_datetime_scalar_construction(self): # Construct with different units @@ -260,6 +347,21 @@ def test_datetime_array_find_type(self): arr = np.array([dt, dt]).astype('datetime64') assert_equal(arr.dtype, np.dtype('M8[us]')) + @pytest.mark.parametrize("unit", [ + # test all date / time units and use + # "generic" to select generic unit + ("Y"), ("M"), ("W"), ("D"), ("h"), ("m"), + ("s"), ("ms"), ("us"), ("ns"), ("ps"), + ("fs"), ("as"), ("generic") ]) + def test_timedelta_np_int_construction(self, unit): + # regression test for gh-7617 + if unit != "generic": + assert_equal(np.timedelta64(np.int64(123), unit), + np.timedelta64(123, unit)) + else: + assert_equal(np.timedelta64(np.int64(123)), + np.timedelta64(123)) + def test_timedelta_scalar_construction(self): # Construct with different units assert_equal(np.timedelta64(7, 'D'), @@ -355,6 +457,20 @@ def test_timedelta_object_array_conversion(self): actual = np.array(inputs, dtype='timedelta64[D]') assert_equal(expected, actual) + def test_timedelta_0_dim_object_array_conversion(self): + # Regression test for gh-11151 + test = np.array(datetime.timedelta(seconds=20)) + actual = test.astype(np.timedelta64) + # expected value from the array constructor workaround + # described in above issue + expected = np.array(datetime.timedelta(seconds=20), + np.timedelta64) + assert_equal(actual, expected) + + def test_timedelta_nat_format(self): + # gh-17552 + assert_equal('NaT', '{0}'.format(np.timedelta64('nat'))) + def test_timedelta_scalar_construction_units(self): # String construction detecting units assert_equal(np.datetime64('2010').dtype, @@ -460,6 +576,30 @@ def test_datetime_nat_casting(self): assert_equal(np.datetime64(a, '[Y]'), np.datetime64('NaT', '[Y]')) assert_equal(np.datetime64(a, '[W]'), np.datetime64('NaT', '[W]')) + # NaN -> NaT + nan = np.array([np.nan] * 8) + fnan = nan.astype('f') + lnan = nan.astype('g') + cnan = nan.astype('D') + cfnan = nan.astype('F') + clnan = nan.astype('G') + + nat = np.array([np.datetime64('NaT')] * 8) + assert_equal(nan.astype('M8[ns]'), nat) + assert_equal(fnan.astype('M8[ns]'), nat) + assert_equal(lnan.astype('M8[ns]'), nat) + assert_equal(cnan.astype('M8[ns]'), nat) + assert_equal(cfnan.astype('M8[ns]'), nat) + assert_equal(clnan.astype('M8[ns]'), nat) + + nat = np.array([np.timedelta64('NaT')] * 8) + assert_equal(nan.astype('timedelta64[ns]'), nat) + assert_equal(fnan.astype('timedelta64[ns]'), nat) + assert_equal(lnan.astype('timedelta64[ns]'), nat) + assert_equal(cnan.astype('timedelta64[ns]'), nat) + assert_equal(cfnan.astype('timedelta64[ns]'), nat) + assert_equal(clnan.astype('timedelta64[ns]'), nat) + def test_days_creation(self): assert_equal(np.array('1599', dtype='M8[D]').astype('i8'), (1600-1970)*365 - (1972-1600)/4 + 3 - 365) @@ -583,6 +723,63 @@ def test_datetime_string_conversion(self): str_b[...] = dt_a assert_equal(str_a, str_b) + @pytest.mark.parametrize("time_dtype", ["m8[D]", "M8[Y]"]) + def test_time_byteswapping(self, time_dtype): + times = np.array(["2017", "NaT"], dtype=time_dtype) + times_swapped = times.astype(times.dtype.newbyteorder()) + assert_array_equal(times, times_swapped) + + unswapped = times_swapped.view(np.int64).newbyteorder() + assert_array_equal(unswapped, times.view(np.int64)) + + @pytest.mark.parametrize(["time1", "time2"], + [("M8[s]", "M8[D]"), ("m8[s]", "m8[ns]")]) + def test_time_byteswapped_cast(self, time1, time2): + dtype1 = np.dtype(time1) + dtype2 = np.dtype(time2) + times = np.array(["2017", "NaT"], dtype=dtype1) + expected = times.astype(dtype2) + + # Test that every byte-swapping combination also returns the same + # results (previous tests check that this comparison works fine). + res = times.astype(dtype1.newbyteorder()).astype(dtype2) + assert_array_equal(res, expected) + res = times.astype(dtype2.newbyteorder()) + assert_array_equal(res, expected) + res = times.astype(dtype1.newbyteorder()).astype(dtype2.newbyteorder()) + assert_array_equal(res, expected) + + @pytest.mark.parametrize("time_dtype", ["m8[D]", "M8[Y]"]) + @pytest.mark.parametrize("str_dtype", ["U", "S"]) + def test_datetime_conversions_byteorders(self, str_dtype, time_dtype): + times = np.array(["2017", "NaT"], dtype=time_dtype) + # Unfortunately, timedelta does not roundtrip: + from_strings = np.array(["2017", "NaT"], dtype=str_dtype) + to_strings = times.astype(str_dtype) # assume this is correct + + # Check that conversion from times to string works if src is swapped: + times_swapped = times.astype(times.dtype.newbyteorder()) + res = times_swapped.astype(str_dtype) + assert_array_equal(res, to_strings) + # And also if both are swapped: + res = times_swapped.astype(to_strings.dtype.newbyteorder()) + assert_array_equal(res, to_strings) + # only destination is swapped: + res = times.astype(to_strings.dtype.newbyteorder()) + assert_array_equal(res, to_strings) + + # Check that conversion from string to times works if src is swapped: + from_strings_swapped = from_strings.astype( + from_strings.dtype.newbyteorder()) + res = from_strings_swapped.astype(time_dtype) + assert_array_equal(res, times) + # And if both are swapped: + res = from_strings_swapped.astype(times.dtype.newbyteorder()) + assert_array_equal(res, times) + # Only destination is swapped: + res = from_strings.astype(times.dtype.newbyteorder()) + assert_array_equal(res, times) + def test_datetime_array_str(self): a = np.array(['2011-03-16', '1920-01-01', '2013-05-19'], dtype='M') assert_equal(str(a), "['2011-03-16' '1920-01-01' '2013-05-19']") @@ -616,10 +813,17 @@ def test_timedelta_array_str(self): def test_pickle(self): # Check that pickle roundtripping works - dt = np.dtype('M8[7D]') - assert_equal(pickle.loads(pickle.dumps(dt)), dt) - dt = np.dtype('M8[W]') - assert_equal(pickle.loads(pickle.dumps(dt)), dt) + for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): + dt = np.dtype('M8[7D]') + assert_equal(pickle.loads(pickle.dumps(dt, protocol=proto)), dt) + dt = np.dtype('M8[W]') + assert_equal(pickle.loads(pickle.dumps(dt, protocol=proto)), dt) + scalar = np.datetime64('2016-01-01T00:00:00.000000000') + assert_equal(pickle.loads(pickle.dumps(scalar, protocol=proto)), + scalar) + delta = scalar - np.datetime64('2015-01-01T00:00:00.000000000') + assert_equal(pickle.loads(pickle.dumps(delta, protocol=proto)), + delta) # Check that loading pickles from 1.6 works pkl = b"cnumpy\ndtype\np0\n(S'M8'\np1\nI0\nI1\ntp2\nRp3\n" + \ @@ -670,6 +874,12 @@ def test_dtype_promotion(self): np.dtype('m8[Y]'), np.dtype('m8[D]')) assert_raises(TypeError, np.promote_types, np.dtype('m8[M]'), np.dtype('m8[W]')) + # timedelta and float cannot be safely cast with each other + assert_raises(TypeError, np.promote_types, "float32", "m8") + assert_raises(TypeError, np.promote_types, "m8", "float32") + assert_raises(TypeError, np.promote_types, "uint64", "m8") + assert_raises(TypeError, np.promote_types, "m8", "uint64") + # timedelta <op> timedelta may overflow with big unit ranges assert_raises(OverflowError, np.promote_types, np.dtype('m8[W]'), np.dtype('m8[fs]')) @@ -1051,6 +1261,133 @@ def check(a, b, res): check(np.timedelta64(0), f, nat) check(nat, f, nat) + @pytest.mark.parametrize("op1, op2, exp", [ + # m8 same units round down + (np.timedelta64(7, 's'), + np.timedelta64(4, 's'), + 1), + # m8 same units round down with negative + (np.timedelta64(7, 's'), + np.timedelta64(-4, 's'), + -2), + # m8 same units negative no round down + (np.timedelta64(8, 's'), + np.timedelta64(-4, 's'), + -2), + # m8 different units + (np.timedelta64(1, 'm'), + np.timedelta64(31, 's'), + 1), + # m8 generic units + (np.timedelta64(1890), + np.timedelta64(31), + 60), + # Y // M works + (np.timedelta64(2, 'Y'), + np.timedelta64('13', 'M'), + 1), + # handle 1D arrays + (np.array([1, 2, 3], dtype='m8'), + np.array([2], dtype='m8'), + np.array([0, 1, 1], dtype=np.int64)), + ]) + def test_timedelta_floor_divide(self, op1, op2, exp): + assert_equal(op1 // op2, exp) + + @pytest.mark.parametrize("op1, op2", [ + # div by 0 + (np.timedelta64(10, 'us'), + np.timedelta64(0, 'us')), + # div with NaT + (np.timedelta64('NaT'), + np.timedelta64(50, 'us')), + # special case for int64 min + # in integer floor division + (np.timedelta64(np.iinfo(np.int64).min), + np.timedelta64(-1)), + ]) + def test_timedelta_floor_div_warnings(self, op1, op2): + with assert_warns(RuntimeWarning): + actual = op1 // op2 + assert_equal(actual, 0) + assert_equal(actual.dtype, np.int64) + + @pytest.mark.parametrize("val1, val2", [ + # the smallest integer that can't be represented + # exactly in a double should be preserved if we avoid + # casting to double in floordiv operation + (9007199254740993, 1), + # stress the alternate floordiv code path where + # operand signs don't match and remainder isn't 0 + (9007199254740999, -2), + ]) + def test_timedelta_floor_div_precision(self, val1, val2): + op1 = np.timedelta64(val1) + op2 = np.timedelta64(val2) + actual = op1 // op2 + # Python reference integer floor + expected = val1 // val2 + assert_equal(actual, expected) + + @pytest.mark.parametrize("val1, val2", [ + # years and months sometimes can't be unambiguously + # divided for floor division operation + (np.timedelta64(7, 'Y'), + np.timedelta64(3, 's')), + (np.timedelta64(7, 'M'), + np.timedelta64(1, 'D')), + ]) + def test_timedelta_floor_div_error(self, val1, val2): + with assert_raises_regex(TypeError, "common metadata divisor"): + val1 // val2 + + @pytest.mark.parametrize("op1, op2", [ + # reuse the test cases from floordiv + (np.timedelta64(7, 's'), + np.timedelta64(4, 's')), + # m8 same units round down with negative + (np.timedelta64(7, 's'), + np.timedelta64(-4, 's')), + # m8 same units negative no round down + (np.timedelta64(8, 's'), + np.timedelta64(-4, 's')), + # m8 different units + (np.timedelta64(1, 'm'), + np.timedelta64(31, 's')), + # m8 generic units + (np.timedelta64(1890), + np.timedelta64(31)), + # Y // M works + (np.timedelta64(2, 'Y'), + np.timedelta64('13', 'M')), + # handle 1D arrays + (np.array([1, 2, 3], dtype='m8'), + np.array([2], dtype='m8')), + ]) + def test_timedelta_divmod(self, op1, op2): + expected = (op1 // op2, op1 % op2) + assert_equal(divmod(op1, op2), expected) + + @pytest.mark.parametrize("op1, op2", [ + # reuse cases from floordiv + # div by 0 + (np.timedelta64(10, 'us'), + np.timedelta64(0, 'us')), + # div with NaT + (np.timedelta64('NaT'), + np.timedelta64(50, 'us')), + # special case for int64 min + # in integer floor division + (np.timedelta64(np.iinfo(np.int64).min), + np.timedelta64(-1)), + ]) + def test_timedelta_divmod_warnings(self, op1, op2): + with assert_warns(RuntimeWarning): + expected = (op1 // op2, op1 % op2) + with assert_warns(RuntimeWarning): + actual = divmod(op1, op2) + assert_equal(actual, expected) + def test_datetime_divide(self): for dta, tda, tdb, tdc, tdd in \ [ @@ -1073,16 +1410,14 @@ def test_datetime_divide(self): assert_equal(tda / 0.5, tdc) assert_equal((tda / 0.5).dtype, np.dtype('m8[h]')) # m8 / m8 - assert_equal(tda / tdb, 6.0 / 9.0) - assert_equal(np.divide(tda, tdb), 6.0 / 9.0) - assert_equal(np.true_divide(tda, tdb), 6.0 / 9.0) - assert_equal(tdb / tda, 9.0 / 6.0) + assert_equal(tda / tdb, 6 / 9) + assert_equal(np.divide(tda, tdb), 6 / 9) + assert_equal(np.true_divide(tda, tdb), 6 / 9) + assert_equal(tdb / tda, 9 / 6) assert_equal((tda / tdb).dtype, np.dtype('f8')) - assert_equal(tda / tdd, 60.0) - assert_equal(tdd / tda, 1.0 / 60.0) + assert_equal(tda / tdd, 60) + assert_equal(tdd / tda, 1 / 60) - # m8 // m8 - assert_raises(TypeError, np.floor_divide, tda, tdb) # int / m8 assert_raises(TypeError, np.divide, 2, tdb) # float / m8 @@ -1140,47 +1475,23 @@ def test_datetime_compare_nat(self): td_nat = np.timedelta64('NaT', 'h') td_other = np.timedelta64(1, 'h') - with suppress_warnings() as sup: - # The assert warns contexts will again see the warning: - sup.filter(FutureWarning, ".*NAT") - - for op in [np.equal, np.less, np.less_equal, - np.greater, np.greater_equal]: - if op(dt_nat, dt_nat): - assert_warns(FutureWarning, op, dt_nat, dt_nat) - if op(dt_nat, dt_other): - assert_warns(FutureWarning, op, dt_nat, dt_other) - if op(dt_other, dt_nat): - assert_warns(FutureWarning, op, dt_other, dt_nat) - if op(td_nat, td_nat): - assert_warns(FutureWarning, op, td_nat, td_nat) - if op(td_nat, td_other): - assert_warns(FutureWarning, op, td_nat, td_other) - if op(td_other, td_nat): - assert_warns(FutureWarning, op, td_other, td_nat) - - assert_warns(FutureWarning, np.not_equal, dt_nat, dt_nat) - assert_warns(FutureWarning, np.not_equal, td_nat, td_nat) - - with suppress_warnings() as sup: - sup.record(FutureWarning) - assert_(np.not_equal(dt_nat, dt_other)) - assert_(np.not_equal(dt_other, dt_nat)) - assert_(np.not_equal(td_nat, td_other)) - assert_(np.not_equal(td_other, td_nat)) - assert_equal(len(sup.log), 0) - - def test_datetime_futurewarning_once_nat(self): - # Test that the futurewarning is only given once per inner loop - arr1 = np.array(['NaT', 'NaT', '2000-01-01'] * 2, dtype='M8[s]') - arr2 = np.array(['NaT', '2000-01-01', 'NaT'] * 2, dtype='M8[s]') - # All except less, because for less it can't be wrong (NaT is min) for op in [np.equal, np.less, np.less_equal, np.greater, np.greater_equal]: - with suppress_warnings() as sup: - rec = sup.record(FutureWarning, ".*NAT") - op(arr1, arr2) - assert_(len(rec) == 1, "failed for {}".format(op)) + assert_(not op(dt_nat, dt_nat)) + assert_(not op(dt_nat, dt_other)) + assert_(not op(dt_other, dt_nat)) + + assert_(not op(td_nat, td_nat)) + assert_(not op(td_nat, td_other)) + assert_(not op(td_other, td_nat)) + + assert_(np.not_equal(dt_nat, dt_nat)) + assert_(np.not_equal(dt_nat, dt_other)) + assert_(np.not_equal(dt_other, dt_nat)) + + assert_(np.not_equal(td_nat, td_nat)) + assert_(np.not_equal(td_nat, td_other)) + assert_(np.not_equal(td_other, td_nat)) def test_datetime_minmax(self): # The metadata of the result should become the GCD @@ -1202,10 +1513,14 @@ def test_datetime_minmax(self): # Interaction with NaT a = np.array('1999-03-12T13', dtype='M8[2m]') dtnat = np.array('NaT', dtype='M8[h]') - assert_equal(np.minimum(a, dtnat), a) - assert_equal(np.minimum(dtnat, a), a) - assert_equal(np.maximum(a, dtnat), a) - assert_equal(np.maximum(dtnat, a), a) + assert_equal(np.minimum(a, dtnat), dtnat) + assert_equal(np.minimum(dtnat, a), dtnat) + assert_equal(np.maximum(a, dtnat), dtnat) + assert_equal(np.maximum(dtnat, a), dtnat) + assert_equal(np.fmin(dtnat, a), a) + assert_equal(np.fmin(a, dtnat), a) + assert_equal(np.fmax(dtnat, a), a) + assert_equal(np.fmax(a, dtnat), a) # Also do timedelta a = np.array(3, dtype='m8[h]') @@ -1412,6 +1727,12 @@ def test_creation_overflow(self): assert_equal(x[0].astype(np.int64), 322689600000000000) + # gh-13062 + with pytest.raises(OverflowError): + np.datetime64(2**64, 'D') + with pytest.raises(OverflowError): + np.timedelta64(2**64, 'D') + def test_datetime_as_string(self): # Check all the units with default string conversion date = '1959-10-13' @@ -1431,8 +1752,9 @@ def test_datetime_as_string(self): '1959-10-13T12:34:56') assert_equal(np.datetime_as_string(np.datetime64(datetime, 'ms')), '1959-10-13T12:34:56.789') - assert_equal(np.datetime_as_string(np.datetime64(datetime, 'us')), - '1959-10-13T12:34:56.789012') + for us in ['us', 'μs', b'us']: # check non-ascii and bytes too + assert_equal(np.datetime_as_string(np.datetime64(datetime, us)), + '1959-10-13T12:34:56.789012') datetime = '1969-12-31T23:34:56.789012345678901234' @@ -1621,10 +1943,80 @@ def test_timedelta_arange(self): assert_raises(TypeError, np.arange, np.timedelta64(0, 'Y'), np.timedelta64(5, 'D')) + @pytest.mark.parametrize("val1, val2, expected", [ + # case from gh-12092 + (np.timedelta64(7, 's'), + np.timedelta64(3, 's'), + np.timedelta64(1, 's')), + # negative value cases + (np.timedelta64(3, 's'), + np.timedelta64(-2, 's'), + np.timedelta64(-1, 's')), + (np.timedelta64(-3, 's'), + np.timedelta64(2, 's'), + np.timedelta64(1, 's')), + # larger value cases + (np.timedelta64(17, 's'), + np.timedelta64(22, 's'), + np.timedelta64(17, 's')), + (np.timedelta64(22, 's'), + np.timedelta64(17, 's'), + np.timedelta64(5, 's')), + # different units + (np.timedelta64(1, 'm'), + np.timedelta64(57, 's'), + np.timedelta64(3, 's')), + (np.timedelta64(1, 'us'), + np.timedelta64(727, 'ns'), + np.timedelta64(273, 'ns')), + # NaT is propagated + (np.timedelta64('NaT'), + np.timedelta64(50, 'ns'), + np.timedelta64('NaT')), + # Y % M works + (np.timedelta64(2, 'Y'), + np.timedelta64(22, 'M'), + np.timedelta64(2, 'M')), + ]) + def test_timedelta_modulus(self, val1, val2, expected): + assert_equal(val1 % val2, expected) + + @pytest.mark.parametrize("val1, val2", [ + # years and months sometimes can't be unambiguously + # divided for modulus operation + (np.timedelta64(7, 'Y'), + np.timedelta64(3, 's')), + (np.timedelta64(7, 'M'), + np.timedelta64(1, 'D')), + ]) + def test_timedelta_modulus_error(self, val1, val2): + with assert_raises_regex(TypeError, "common metadata divisor"): + val1 % val2 + + def test_timedelta_modulus_div_by_zero(self): + with assert_warns(RuntimeWarning): + actual = np.timedelta64(10, 's') % np.timedelta64(0, 's') + assert_equal(actual, np.timedelta64('NaT')) + + @pytest.mark.parametrize("val1, val2", [ + # cases where one operand is not + # timedelta64 + (np.timedelta64(7, 'Y'), + 15,), + (7.5, + np.timedelta64(1, 'D')), + ]) + def test_timedelta_modulus_type_resolution(self, val1, val2): + # NOTE: some of the operations may be supported + # in the future + with assert_raises_regex(TypeError, + "'remainder' cannot use operands with types"): + val1 % val2 + def test_timedelta_arange_no_dtype(self): d = np.array(5, dtype="m8[D]") assert_equal(np.arange(d, d + 1), d) - assert_raises(ValueError, np.arange, d) + assert_equal(np.arange(d), np.arange(0, d)) def test_datetime_maximum_reduce(self): a = np.array(['2010-01-02', '1999-03-14', '1833-03'], dtype='M8[D]') @@ -1637,6 +2029,27 @@ def test_datetime_maximum_reduce(self): assert_equal(np.maximum.reduce(a), np.timedelta64(7, 's')) + def test_timedelta_correct_mean(self): + # test mainly because it worked only via a bug in that allowed: + # `timedelta.sum(dtype="f8")` to ignore the dtype request. + a = np.arange(1000, dtype="m8[s]") + assert_array_equal(a.mean(), a.sum() / len(a)) + + def test_datetime_no_subtract_reducelike(self): + # subtracting two datetime64 works, but we cannot reduce it, since + # the result of that subtraction will have a different dtype. + arr = np.array(["2021-12-02", "2019-05-12"], dtype="M8[ms]") + msg = r"the resolved dtypes are not compatible" + + with pytest.raises(TypeError, match=msg): + np.subtract.reduce(arr) + + with pytest.raises(TypeError, match=msg): + np.subtract.accumulate(arr) + + with pytest.raises(TypeError, match=msg): + np.subtract.reduceat(arr, [0]) + def test_datetime_busday_offset(self): # First Monday in June assert_equal( @@ -1698,7 +2111,6 @@ def test_datetime_busday_offset(self): assert_equal(np.busday_offset(np.datetime64('NaT'), 1, roll='preceding'), np.datetime64('NaT')) - def test_datetime_busdaycalendar(self): # Check that it removes NaT, duplicates, and weekends # and sorts the result. @@ -2002,6 +2414,34 @@ def test_isnat_error(self): continue assert_raises(TypeError, np.isnat, np.zeros(10, t)) + def test_isfinite_scalar(self): + assert_(not np.isfinite(np.datetime64('NaT', 'ms'))) + assert_(not np.isfinite(np.datetime64('NaT', 'ns'))) + assert_(np.isfinite(np.datetime64('2038-01-19T03:14:07'))) + + assert_(not np.isfinite(np.timedelta64('NaT', "ms"))) + assert_(np.isfinite(np.timedelta64(34, "ms"))) + + @pytest.mark.parametrize('unit', ['Y', 'M', 'W', 'D', 'h', 'm', 's', 'ms', + 'us', 'ns', 'ps', 'fs', 'as']) + @pytest.mark.parametrize('dstr', ['<datetime64[%s]', '>datetime64[%s]', + '<timedelta64[%s]', '>timedelta64[%s]']) + def test_isfinite_isinf_isnan_units(self, unit, dstr): + '''check isfinite, isinf, isnan for all units of <M, >M, <m, >m dtypes + ''' + arr_val = [123, -321, "NaT"] + arr = np.array(arr_val, dtype= dstr % unit) + pos = np.array([True, True, False]) + neg = np.array([False, False, True]) + false = np.array([False, False, False]) + assert_equal(np.isfinite(arr), pos) + assert_equal(np.isinf(arr), false) + assert_equal(np.isnan(arr), neg) + + def test_assert_equal(self): + assert_raises(AssertionError, assert_equal, + np.datetime64('nat'), np.timedelta64('nat')) + def test_corecursive_input(self): # construct a co-recursive list a, b = [], [] @@ -2010,13 +2450,79 @@ def test_corecursive_input(self): obj_arr = np.array([None]) obj_arr[0] = a - # gh-11154: This shouldn't cause a C stack overflow - assert_raises(RecursionError, obj_arr.astype, 'M8') - assert_raises(RecursionError, obj_arr.astype, 'm8') - - -class TestDateTimeData(object): + # At some point this caused a stack overflow (gh-11154). Now raises + # ValueError since the nested list cannot be converted to a datetime. + assert_raises(ValueError, obj_arr.astype, 'M8') + assert_raises(ValueError, obj_arr.astype, 'm8') + + @pytest.mark.parametrize("shape", [(), (1,)]) + def test_discovery_from_object_array(self, shape): + arr = np.array("2020-10-10", dtype=object).reshape(shape) + res = np.array("2020-10-10", dtype="M8").reshape(shape) + assert res.dtype == np.dtype("M8[D]") + assert_equal(arr.astype("M8"), res) + arr[...] = np.bytes_("2020-10-10") # try a numpy string type + assert_equal(arr.astype("M8"), res) + arr = arr.astype("S") + assert_equal(arr.astype("S").astype("M8"), res) + + @pytest.mark.parametrize("time_unit", [ + "Y", "M", "W", "D", "h", "m", "s", "ms", "us", "ns", "ps", "fs", "as", + # compound units + "10D", "2M", + ]) + def test_limit_symmetry(self, time_unit): + """ + Dates should have symmetric limits around the unix epoch at +/-np.int64 + """ + epoch = np.datetime64(0, time_unit) + latest = np.datetime64(np.iinfo(np.int64).max, time_unit) + earliest = np.datetime64(-np.iinfo(np.int64).max, time_unit) + + # above should not have overflowed + assert earliest < epoch < latest + + @pytest.mark.parametrize("time_unit", [ + "Y", "M", + pytest.param("W", marks=pytest.mark.xfail(reason="gh-13197")), + "D", "h", "m", + "s", "ms", "us", "ns", "ps", "fs", "as", + pytest.param("10D", marks=pytest.mark.xfail(reason="similar to gh-13197")), + ]) + @pytest.mark.parametrize("sign", [-1, 1]) + def test_limit_str_roundtrip(self, time_unit, sign): + """ + Limits should roundtrip when converted to strings. + + This tests the conversion to and from npy_datetimestruct. + """ + # TODO: add absolute (gold standard) time span limit strings + limit = np.datetime64(np.iinfo(np.int64).max * sign, time_unit) + + # Convert to string and back. Explicit unit needed since the day and + # week reprs are not distinguishable. + limit_via_str = np.datetime64(str(limit), time_unit) + assert limit_via_str == limit + + +class TestDateTimeData: def test_basic(self): a = np.array(['1980-03-23'], dtype=np.datetime64) assert_equal(np.datetime_data(a.dtype), ('D', 1)) + + def test_bytes(self): + # byte units are converted to unicode + dt = np.datetime64('2000', (b'ms', 5)) + assert np.datetime_data(dt.dtype) == ('ms', 5) + + dt = np.datetime64('2000', b'5ms') + assert np.datetime_data(dt.dtype) == ('ms', 5) + + def test_non_ascii(self): + # μs is normalized to μ + dt = np.datetime64('2000', ('μs', 5)) + assert np.datetime_data(dt.dtype) == ('us', 5) + + dt = np.datetime64('2000', '5μs') + assert np.datetime_data(dt.dtype) == ('us', 5) diff --git a/numpy/core/tests/test_defchararray.py b/numpy/core/tests/test_defchararray.py index 43f1b71c7873..59fc54722397 100644 --- a/numpy/core/tests/test_defchararray.py +++ b/numpy/core/tests/test_defchararray.py @@ -1,18 +1,15 @@ -from __future__ import division, absolute_import, print_function - -import sys import numpy as np from numpy.core.multiarray import _vec_string from numpy.testing import ( assert_, assert_equal, assert_array_equal, assert_raises, - suppress_warnings, + assert_raises_regex ) kw_unicode_true = {'unicode': True} # make 2to3 work properly kw_unicode_false = {'unicode': False} -class TestBasic(object): +class TestBasic: def test_from_object_array(self): A = np.array([['abc', 2], ['long ', '0123456789']], dtype='O') @@ -83,7 +80,7 @@ def test_from_unicode(self): assert_equal(A.itemsize, 4) assert_(issubclass(A.dtype.type, np.unicode_)) -class TestVecString(object): +class TestVecString: def test_non_existent_method(self): def fail(): @@ -122,19 +119,19 @@ def fail(): def test_invalid_result_type(self): def fail(): - _vec_string(['a'], np.integer, 'strip') + _vec_string(['a'], np.int_, 'strip') assert_raises(TypeError, fail) def test_broadcast_error(self): def fail(): - _vec_string([['abc', 'def']], np.integer, 'find', (['a', 'd', 'j'],)) + _vec_string([['abc', 'def']], np.int_, 'find', (['a', 'd', 'j'],)) assert_raises(ValueError, fail) -class TestWhitespace(object): +class TestWhitespace: def setup(self): self.A = np.array([['abc ', '123 '], ['789 ', 'xyz ']]).view(np.chararray) @@ -149,7 +146,7 @@ def test1(self): assert_(not np.any(self.A < self.B)) assert_(not np.any(self.A != self.B)) -class TestChar(object): +class TestChar: def setup(self): self.A = np.array('abc1', dtype='c').view(np.chararray) @@ -157,7 +154,7 @@ def test_it(self): assert_equal(self.A.shape, (4,)) assert_equal(self.A.upper()[:2].tobytes(), b'AB') -class TestComparisons(object): +class TestComparisons: def setup(self): self.A = np.array([['abc', '123'], ['789', 'xyz']]).view(np.chararray) @@ -182,6 +179,12 @@ def test_greater(self): def test_less(self): assert_array_equal((self.A < self.B), [[True, False], [False, False]]) + def test_type(self): + out1 = np.char.equal(self.A, self.B) + out2 = np.char.equal('a', 'a') + assert_(isinstance(out1, np.ndarray)) + assert_(isinstance(out2, np.ndarray)) + class TestComparisonsMixed1(TestComparisons): """Ticket #1276""" @@ -198,7 +201,7 @@ def setup(self): self.A = np.array([['abc', '123'], ['789', 'xyz']], np.unicode_).view(np.chararray) -class TestInformation(object): +class TestInformation: def setup(self): self.A = np.array([[' abc ', ''], ['12345', 'MixedCase'], @@ -304,7 +307,7 @@ def fail(): assert_raises(TypeError, fail) -class TestMethods(object): +class TestMethods: def setup(self): self.A = np.array([[' abc ', ''], ['12345', 'MixedCase'], @@ -343,15 +346,8 @@ def test_center(self): assert_array_equal(C, tgt) def test_decode(self): - if sys.version_info[0] >= 3: - A = np.char.array([b'\\u03a3']) - assert_(A.decode('unicode-escape')[0] == '\u03a3') - else: - with suppress_warnings() as sup: - if sys.py3kwarning: - sup.filter(DeprecationWarning, "'hex_codec'") - A = np.char.array(['736563726574206d657373616765']) - assert_(A.decode('hex_codec')[0] == 'secret message') + A = np.char.array([b'\\u03a3']) + assert_(A.decode('unicode-escape')[0] == '\u03a3') def test_encode(self): B = self.B.encode('unicode_escape') @@ -362,18 +358,12 @@ def test_expandtabs(self): assert_(T[2, 0] == b'123 345 \0') def test_join(self): - if sys.version_info[0] >= 3: - # NOTE: list(b'123') == [49, 50, 51] - # so that b','.join(b'123') results to an error on Py3 - A0 = self.A.decode('ascii') - else: - A0 = self.A + # NOTE: list(b'123') == [49, 50, 51] + # so that b','.join(b'123') results to an error on Py3 + A0 = self.A.decode('ascii') A = np.char.join([',', '#'], A0) - if sys.version_info[0] >= 3: - assert_(issubclass(A.dtype.type, np.unicode_)) - else: - assert_(issubclass(A.dtype.type, np.string_)) + assert_(issubclass(A.dtype.type, np.unicode_)) tgt = np.array([[' ,a,b,c, ', ''], ['1,2,3,4,5', 'M#i#x#e#d#C#a#s#e'], ['1,2,3, ,\t, ,3,4,5, ,\x00, ', 'U#P#P#E#R']]) @@ -444,15 +434,6 @@ def test_replace(self): assert_(issubclass(R.dtype.type, np.string_)) assert_array_equal(R, tgt) - if sys.version_info[0] < 3: - # NOTE: b'abc'.replace(b'a', 'b') is not allowed on Py3 - R = self.A.replace(b'a', u'\u03a3') - tgt = [[u' \u03a3bc ', ''], - ['12345', u'MixedC\u03a3se'], - ['123 \t 345 \x00', 'UPPER']] - assert_(issubclass(R.dtype.type, np.unicode_)) - assert_array_equal(R, tgt) - def test_rjust(self): assert_(issubclass(self.A.rjust(10).dtype.type, np.string_)) @@ -599,7 +580,7 @@ def fail(): [False, False], [True, False], [False, False]]) -class TestOperations(object): +class TestOperations: def setup(self): self.A = np.array([['abc', '123'], ['789', 'xyz']]).view(np.chararray) @@ -626,12 +607,9 @@ def test_mul(self): assert_array_equal(Ar, (self.A * r)) for ob in [object(), 'qrs']: - try: - A * ob - except ValueError: - pass - else: - self.fail("chararray can only be multiplied by integers") + with assert_raises_regex(ValueError, + 'Can only multiply by integers'): + A*ob def test_rmul(self): A = self.A @@ -641,12 +619,9 @@ def test_rmul(self): assert_array_equal(Ar, (r * self.A)) for ob in [object(), 'qrs']: - try: + with assert_raises_regex(ValueError, + 'Can only multiply by integers'): ob * A - except ValueError: - pass - else: - self.fail("chararray can only be multiplied by integers") def test_mod(self): """Ticket #856""" @@ -668,13 +643,9 @@ def test_rmod(self): assert_(("%r" % self.A) == repr(self.A)) for ob in [42, object()]: - try: + with assert_raises_regex( + TypeError, "unsupported operand type.* and 'chararray'"): ob % self.A - except TypeError: - pass - else: - self.fail("chararray __rmod__ should fail with " - "non-string objects") def test_slice(self): """Regression test for https://github.com/numpy/numpy/issues/5982""" diff --git a/numpy/core/tests/test_deprecations.py b/numpy/core/tests/test_deprecations.py index 60a7c72f7f41..94583a5ee04f 100644 --- a/numpy/core/tests/test_deprecations.py +++ b/numpy/core/tests/test_deprecations.py @@ -3,20 +3,21 @@ to document how deprecations should eventually be turned into errors. """ -from __future__ import division, absolute_import, print_function - import datetime -import sys import operator import warnings import pytest +import tempfile +import re +import sys import numpy as np from numpy.testing import ( - assert_raises, assert_warns, assert_no_warnings, assert_array_equal, - assert_ + assert_raises, assert_warns, assert_, assert_array_equal, SkipTest, KnownFailureException ) +from numpy.core._multiarray_tests import fromstring_null_term_c_api + try: import pytz _has_pytz = True @@ -24,7 +25,7 @@ _has_pytz = False -class _DeprecationTestCase(object): +class _DeprecationTestCase: # Just as warning: warnings uses re.match, so the start of this message # must match. message = '' @@ -36,7 +37,7 @@ def setup(self): # Do *not* ignore other DeprecationWarnings. Ignoring warnings # can give very confusing results because of - # http://bugs.python.org/issue4180 and it is probably simplest to + # https://bugs.python.org/issue4180 and it is probably simplest to # try to keep the tests cleanly giving only the right warning type. # (While checking them set to "error" those are ignored anyway) # We still have them show up, because otherwise they would be raised @@ -80,6 +81,8 @@ def assert_deprecated(self, function, num=1, ignore_others=False, kwargs : dict Keyword arguments for `function` """ + __tracebackhide__ = True # Hide traceback for py.test + # reset the log self.log[:] = [] @@ -102,7 +105,7 @@ def assert_deprecated(self, function, num=1, ignore_others=False, (self.warning_cls.__name__, warning.category)) if num is not None and num_found != num: msg = "%i warnings found but %i expected." % (len(self.log), num) - lst = [str(w.category) for w in self.log] + lst = [str(w) for w in self.log] raise AssertionError("\n".join([msg] + lst)) with warnings.catch_warnings(): @@ -134,7 +137,7 @@ class _VisibleDeprecationTestCase(_DeprecationTestCase): warning_cls = np.VisibleDeprecationWarning -class TestNonTupleNDIndexDeprecation(object): +class TestNonTupleNDIndexDeprecation: def test_basic(self): a = np.zeros((5, 5)) with warnings.catch_warnings(): @@ -150,16 +153,6 @@ def test_basic(self): a[[0, 1]] -class TestRankDeprecation(_DeprecationTestCase): - """Test that np.rank is deprecated. The function should simply be - removed. The VisibleDeprecationWarning may become unnecessary. - """ - - def test(self): - a = np.arange(10) - assert_warns(np.VisibleDeprecationWarning, np.rank, a) - - class TestComparisonDeprecations(_DeprecationTestCase): """This tests the deprecation, for non-element-wise comparison logic. This used to mean that when an error occurred during element-wise comparison @@ -179,7 +172,7 @@ def test_normal_types(self): # (warning is issued a couple of times here) self.assert_deprecated(op, args=(a, a[:-1]), num=None) - # Element comparison error (numpy array can't be compared). + # ragged array comparison returns True/False a = np.array([1, np.array([1,2,3])], dtype=object) b = np.array([1, np.array([1,2,3])], dtype=object) self.assert_deprecated(op, args=(a, b), num=None) @@ -190,13 +183,13 @@ def test_string(self): b = np.array(['a', 'b', 'c']) assert_raises(ValueError, lambda x, y: x == y, a, b) - # The empty list is not cast to string, as this is only to document - # that fact (it likely should be changed). This means that the - # following works (and returns False) due to dtype mismatch: - a == [] + # The empty list is not cast to string, and this used to pass due + # to dtype mismatch; now (2018-06-21) it correctly leads to a + # FutureWarning. + assert_warns(FutureWarning, lambda: a == []) def test_void_dtype_equality_failures(self): - class NotArray(object): + class NotArray: def __array__(self): raise TypeError @@ -236,15 +229,10 @@ def test_array_richcompare_legacy_weirdness(self): struct = np.zeros(2, dtype="i4,i4") for arg2 in [struct, "a"]: for f in [operator.lt, operator.le, operator.gt, operator.ge]: - if sys.version_info[0] >= 3: - # py3 - with warnings.catch_warnings() as l: - warnings.filterwarnings("always") - assert_raises(TypeError, f, arg1, arg2) - assert_(not l) - else: - # py2 - assert_warns(DeprecationWarning, f, arg1, arg2) + with warnings.catch_warnings() as l: + warnings.filterwarnings("always") + assert_raises(TypeError, f, arg1, arg2) + assert_(not l) class TestDatetime64Timezone(_DeprecationTestCase): @@ -282,36 +270,6 @@ def test_fortran_contiguous(self): self.assert_deprecated(np.ones((2,2)).T.view, args=(np.int8,)) -class TestInvalidOrderParameterInputForFlattenArrayDeprecation(_DeprecationTestCase): - """Invalid arguments to the ORDER parameter in array.flatten() should not be - allowed and should raise an error. However, in the interests of not breaking - code that may inadvertently pass invalid arguments to this parameter, a - DeprecationWarning will be issued instead for the time being to give developers - time to refactor relevant code. - """ - - def test_flatten_array_non_string_arg(self): - x = np.zeros((3, 5)) - self.message = ("Non-string object detected for " - "the array ordering. Please pass " - "in 'C', 'F', 'A', or 'K' instead") - self.assert_deprecated(x.flatten, args=(np.pi,)) - - def test_flatten_array_invalid_string_arg(self): - # Tests that a DeprecationWarning is raised - # when a string of length greater than one - # starting with "C", "F", "A", or "K" (case- - # and unicode-insensitive) is passed in for - # the ORDER parameter. Otherwise, a TypeError - # will be raised! - - x = np.zeros((3, 5)) - self.message = ("Non length-one string passed " - "in for the array ordering. Please " - "pass in 'C', 'F', 'A', or 'K' instead") - self.assert_deprecated(x.flatten, args=("FACK",)) - - class TestArrayDataAttributeAssignmentDeprecation(_DeprecationTestCase): """Assigning the 'data' attribute of an ndarray is unsafe as pointed out in gh-7093. Eventually, such assignment should NOT be allowed, but @@ -330,22 +288,6 @@ def test_data_attr_assignment(self): self.assert_deprecated(a.__setattr__, args=('data', b.data)) -class TestLinspaceInvalidNumParameter(_DeprecationTestCase): - """Argument to the num parameter in linspace that cannot be - safely interpreted as an integer is deprecated in 1.12.0. - - Argument to the num parameter in linspace that cannot be - safely interpreted as an integer should not be allowed. - In the interest of not breaking code that passes - an argument that could still be interpreted as an integer, a - DeprecationWarning will be issued for the time being to give - developers time to refactor relevant code. - """ - def test_float_arg(self): - # 2016-02-25, PR#7328 - self.assert_deprecated(np.linspace, args=(0, 10, 2.5)) - - class TestBinaryReprInsufficientWidthParameterForRepresentation(_DeprecationTestCase): """ If a 'width' parameter is passed into ``binary_repr`` that is insufficient to @@ -372,30 +314,24 @@ def test_insufficient_width_negative(self): self.assert_deprecated(np.binary_repr, args=args, kwargs=kwargs) -class TestNumericStyleTypecodes(_DeprecationTestCase): - """ - Deprecate the old numeric-style dtypes, which are especially - confusing for complex types, e.g. Complex32 -> complex64. When the - deprecation cycle is complete, the check for the strings should be - removed from PyArray_DescrConverter in descriptor.c, and the - deprecated keys should not be added as capitalized aliases in - _add_aliases in numerictypes.py. - """ - def test_all_dtypes(self): - deprecated_types = [ - 'Bool', 'Complex32', 'Complex64', 'Float16', 'Float32', 'Float64', - 'Int8', 'Int16', 'Int32', 'Int64', 'Object0', 'Timedelta64', - 'UInt8', 'UInt16', 'UInt32', 'UInt64', 'Void0' - ] - if sys.version_info[0] < 3: - deprecated_types.extend(['Unicode0', 'String0']) +class TestDTypeAttributeIsDTypeDeprecation(_DeprecationTestCase): + # Deprecated 2021-01-05, NumPy 1.21 + message = r".*`.dtype` attribute" - for dt in deprecated_types: - self.assert_deprecated(np.dtype, exceptions=(TypeError,), - args=(dt,)) + def test_deprecation_dtype_attribute_is_dtype(self): + class dt: + dtype = "f8" + class vdt(np.void): + dtype = "f,f" -class TestTestDeprecated(object): + self.assert_deprecated(lambda: np.dtype(dt)) + self.assert_deprecated(lambda: np.dtype(dt())) + self.assert_deprecated(lambda: np.dtype(vdt)) + self.assert_deprecated(lambda: np.dtype(vdt(1))) + + +class TestTestDeprecated: def test_assert_deprecated(self): test_case_instance = _DeprecationTestCase() test_case_instance.setup() @@ -410,28 +346,6 @@ def foo(): test_case_instance.teardown() -class TestClassicIntDivision(_DeprecationTestCase): - """ - See #7949. Deprecate the numeric-style dtypes with -3 flag in python 2 - if used for division - List of data types: http://docs.scipy.org/doc/numpy/user/basics.types.html - """ - def test_int_dtypes(self): - #scramble types and do some mix and match testing - deprecated_types = [ - 'bool_', 'int_', 'intc', 'uint8', 'int8', 'uint64', 'int32', 'uint16', - 'intp', 'int64', 'uint32', 'int16' - ] - if sys.version_info[0] < 3 and sys.py3kwarning: - import operator as op - dt2 = 'bool_' - for dt1 in deprecated_types: - a = np.array([1,2,3], dtype=dt1) - b = np.array([1,2,3], dtype=dt2) - self.assert_deprecated(op.div, args=(a,b)) - dt2 = dt1 - - class TestNonNumericConjugate(_DeprecationTestCase): """ Deprecate no-op behavior of ndarray.conjugate on non-numeric dtypes, @@ -453,6 +367,18 @@ def test_npy_char_deprecation(self): assert_(npy_char_deprecation() == 'S1') +class TestPyArray_AS1D(_DeprecationTestCase): + def test_npy_pyarrayas1d_deprecation(self): + from numpy.core._multiarray_tests import npy_pyarrayas1d_deprecation + assert_raises(NotImplementedError, npy_pyarrayas1d_deprecation) + + +class TestPyArray_AS2D(_DeprecationTestCase): + def test_npy_pyarrayas2d_deprecation(self): + from numpy.core._multiarray_tests import npy_pyarrayas2d_deprecation + assert_raises(NotImplementedError, npy_pyarrayas2d_deprecation) + + class Test_UPDATEIFCOPY(_DeprecationTestCase): """ v1.14 deprecates creating an array with the UPDATEIFCOPY flag, use @@ -500,7 +426,827 @@ def test_bincount_minlength(self): self.assert_deprecated(lambda: np.bincount([1, 2, 3], minlength=None)) +class TestAlen(_DeprecationTestCase): + # 2019-08-02, 1.18.0 + def test_alen(self): + self.assert_deprecated(lambda: np.alen(np.array([1, 2, 3]))) + + class TestGeneratorSum(_DeprecationTestCase): # 2018-02-25, 1.15.0 def test_generator_sum(self): self.assert_deprecated(np.sum, args=((i for i in range(5)),)) + + +class TestPositiveOnNonNumerical(_DeprecationTestCase): + # 2018-06-28, 1.16.0 + def test_positive_on_non_number(self): + self.assert_deprecated(operator.pos, args=(np.array('foo'),)) + + +class TestFromstring(_DeprecationTestCase): + # 2017-10-19, 1.14 + def test_fromstring(self): + self.assert_deprecated(np.fromstring, args=('\x00'*80,)) + + +class TestFromStringAndFileInvalidData(_DeprecationTestCase): + # 2019-06-08, 1.17.0 + # Tests should be moved to real tests when deprecation is done. + message = "string or file could not be read to its end" + + @pytest.mark.parametrize("invalid_str", [",invalid_data", "invalid_sep"]) + def test_deprecate_unparsable_data_file(self, invalid_str): + x = np.array([1.51, 2, 3.51, 4], dtype=float) + + with tempfile.TemporaryFile(mode="w") as f: + x.tofile(f, sep=',', format='%.2f') + f.write(invalid_str) + + f.seek(0) + self.assert_deprecated(lambda: np.fromfile(f, sep=",")) + f.seek(0) + self.assert_deprecated(lambda: np.fromfile(f, sep=",", count=5)) + # Should not raise: + with warnings.catch_warnings(): + warnings.simplefilter("error", DeprecationWarning) + f.seek(0) + res = np.fromfile(f, sep=",", count=4) + assert_array_equal(res, x) + + @pytest.mark.parametrize("invalid_str", [",invalid_data", "invalid_sep"]) + def test_deprecate_unparsable_string(self, invalid_str): + x = np.array([1.51, 2, 3.51, 4], dtype=float) + x_str = "1.51,2,3.51,4{}".format(invalid_str) + + self.assert_deprecated(lambda: np.fromstring(x_str, sep=",")) + self.assert_deprecated(lambda: np.fromstring(x_str, sep=",", count=5)) + + # The C-level API can use not fixed size, but 0 terminated strings, + # so test that as well: + bytestr = x_str.encode("ascii") + self.assert_deprecated(lambda: fromstring_null_term_c_api(bytestr)) + + with assert_warns(DeprecationWarning): + # this is slightly strange, in that fromstring leaves data + # potentially uninitialized (would be good to error when all is + # read, but count is larger then actual data maybe). + res = np.fromstring(x_str, sep=",", count=5) + assert_array_equal(res[:-1], x) + + with warnings.catch_warnings(): + warnings.simplefilter("error", DeprecationWarning) + + # Should not raise: + res = np.fromstring(x_str, sep=",", count=4) + assert_array_equal(res, x) + + +class Test_GetSet_NumericOps(_DeprecationTestCase): + # 2018-09-20, 1.16.0 + def test_get_numeric_ops(self): + from numpy.core._multiarray_tests import getset_numericops + self.assert_deprecated(getset_numericops, num=2) + + # empty kwargs prevents any state actually changing which would break + # other tests. + self.assert_deprecated(np.set_numeric_ops, kwargs={}) + assert_raises(ValueError, np.set_numeric_ops, add='abc') + + +class TestShape1Fields(_DeprecationTestCase): + warning_cls = FutureWarning + + # 2019-05-20, 1.17.0 + def test_shape_1_fields(self): + self.assert_deprecated(np.dtype, args=([('a', int, 1)],)) + + +class TestNonZero(_DeprecationTestCase): + # 2019-05-26, 1.17.0 + def test_zerod(self): + self.assert_deprecated(lambda: np.nonzero(np.array(0))) + self.assert_deprecated(lambda: np.nonzero(np.array(1))) + + +def test_deprecate_ragged_arrays(): + # 2019-11-29 1.19.0 + # + # NEP 34 deprecated automatic object dtype when creating ragged + # arrays. Also see the "ragged" tests in `test_multiarray` + # + # emits a VisibleDeprecationWarning + arg = [1, [2, 3]] + with assert_warns(np.VisibleDeprecationWarning): + np.array(arg) + + +class TestTooDeepDeprecation(_VisibleDeprecationTestCase): + # NumPy 1.20, 2020-05-08 + # This is a bit similar to the above ragged array deprecation case. + message = re.escape("Creating an ndarray from nested sequences exceeding") + + def test_deprecation(self): + nested = [1] + for i in range(np.MAXDIMS - 1): + nested = [nested] + self.assert_not_deprecated(np.array, args=(nested,)) + self.assert_not_deprecated(np.array, + args=(nested,), kwargs=dict(dtype=object)) + + self.assert_deprecated(np.array, args=([nested],)) + + +class TestToString(_DeprecationTestCase): + # 2020-03-06 1.19.0 + message = re.escape("tostring() is deprecated. Use tobytes() instead.") + + def test_tostring(self): + arr = np.array(list(b"test\xFF"), dtype=np.uint8) + self.assert_deprecated(arr.tostring) + + def test_tostring_matches_tobytes(self): + arr = np.array(list(b"test\xFF"), dtype=np.uint8) + b = arr.tobytes() + with assert_warns(DeprecationWarning): + s = arr.tostring() + assert s == b + + +class TestDTypeCoercion(_DeprecationTestCase): + # 2020-02-06 1.19.0 + message = "Converting .* to a dtype .*is deprecated" + deprecated_types = [ + # The builtin scalar super types: + np.generic, np.flexible, np.number, + np.inexact, np.floating, np.complexfloating, + np.integer, np.unsignedinteger, np.signedinteger, + # character is a deprecated S1 special case: + np.character, + ] + + def test_dtype_coercion(self): + for scalar_type in self.deprecated_types: + self.assert_deprecated(np.dtype, args=(scalar_type,)) + + def test_array_construction(self): + for scalar_type in self.deprecated_types: + self.assert_deprecated(np.array, args=([], scalar_type,)) + + def test_not_deprecated(self): + # All specific types are not deprecated: + for group in np.sctypes.values(): + for scalar_type in group: + self.assert_not_deprecated(np.dtype, args=(scalar_type,)) + + for scalar_type in [type, dict, list, tuple]: + # Typical python types are coerced to object currently: + self.assert_not_deprecated(np.dtype, args=(scalar_type,)) + + +class BuiltInRoundComplexDType(_DeprecationTestCase): + # 2020-03-31 1.19.0 + deprecated_types = [np.csingle, np.cdouble, np.clongdouble] + not_deprecated_types = [ + np.int8, np.int16, np.int32, np.int64, + np.uint8, np.uint16, np.uint32, np.uint64, + np.float16, np.float32, np.float64, + ] + + def test_deprecated(self): + for scalar_type in self.deprecated_types: + scalar = scalar_type(0) + self.assert_deprecated(round, args=(scalar,)) + self.assert_deprecated(round, args=(scalar, 0)) + self.assert_deprecated(round, args=(scalar,), kwargs={'ndigits': 0}) + + def test_not_deprecated(self): + for scalar_type in self.not_deprecated_types: + scalar = scalar_type(0) + self.assert_not_deprecated(round, args=(scalar,)) + self.assert_not_deprecated(round, args=(scalar, 0)) + self.assert_not_deprecated(round, args=(scalar,), kwargs={'ndigits': 0}) + + +class TestIncorrectAdvancedIndexWithEmptyResult(_DeprecationTestCase): + # 2020-05-27, NumPy 1.20.0 + message = "Out of bound index found. This was previously ignored.*" + + @pytest.mark.parametrize("index", [([3, 0],), ([0, 0], [3, 0])]) + def test_empty_subspace(self, index): + # Test for both a single and two/multiple advanced indices. These + # This will raise an IndexError in the future. + arr = np.ones((2, 2, 0)) + self.assert_deprecated(arr.__getitem__, args=(index,)) + self.assert_deprecated(arr.__setitem__, args=(index, 0.)) + + # for this array, the subspace is only empty after applying the slice + arr2 = np.ones((2, 2, 1)) + index2 = (slice(0, 0),) + index + self.assert_deprecated(arr2.__getitem__, args=(index2,)) + self.assert_deprecated(arr2.__setitem__, args=(index2, 0.)) + + def test_empty_index_broadcast_not_deprecated(self): + arr = np.ones((2, 2, 2)) + + index = ([[3], [2]], []) # broadcast to an empty result. + self.assert_not_deprecated(arr.__getitem__, args=(index,)) + self.assert_not_deprecated(arr.__setitem__, + args=(index, np.empty((2, 0, 2)))) + + +class TestNonExactMatchDeprecation(_DeprecationTestCase): + # 2020-04-22 + def test_non_exact_match(self): + arr = np.array([[3, 6, 6], [4, 5, 1]]) + # misspelt mode check + self.assert_deprecated(lambda: np.ravel_multi_index(arr, (7, 6), mode='Cilp')) + # using completely different word with first character as R + self.assert_deprecated(lambda: np.searchsorted(arr[0], 4, side='Random')) + + +class TestDeprecatedGlobals(_DeprecationTestCase): + # 2020-06-06 + @pytest.mark.skipif( + sys.version_info < (3, 7), + reason='module-level __getattr__ not supported') + def test_type_aliases(self): + # from builtins + self.assert_deprecated(lambda: np.bool(True)) + self.assert_deprecated(lambda: np.int(1)) + self.assert_deprecated(lambda: np.float(1)) + self.assert_deprecated(lambda: np.complex(1)) + self.assert_deprecated(lambda: np.object()) + self.assert_deprecated(lambda: np.str('abc')) + + # from np.compat + self.assert_deprecated(lambda: np.long(1)) + self.assert_deprecated(lambda: np.unicode('abc')) + + # from np.core.numerictypes + self.assert_deprecated(lambda: np.typeDict) + + +class TestMatrixInOuter(_DeprecationTestCase): + # 2020-05-13 NumPy 1.20.0 + message = (r"add.outer\(\) was passed a numpy matrix as " + r"(first|second) argument.") + + def test_deprecated(self): + arr = np.array([1, 2, 3]) + m = np.array([1, 2, 3]).view(np.matrix) + self.assert_deprecated(np.add.outer, args=(m, m), num=2) + self.assert_deprecated(np.add.outer, args=(arr, m)) + self.assert_deprecated(np.add.outer, args=(m, arr)) + self.assert_not_deprecated(np.add.outer, args=(arr, arr)) + + +class TestRaggedArray(_DeprecationTestCase): + # 2020-07-24, NumPy 1.20.0 + message = "setting an array element with a sequence" + + def test_deprecated(self): + arr = np.ones((1, 1)) + # Deprecated if the array is a leave node: + self.assert_deprecated(lambda: np.array([arr, 0], dtype=np.float64)) + self.assert_deprecated(lambda: np.array([0, arr], dtype=np.float64)) + # And when it is an assignment into a lower dimensional subarray: + self.assert_deprecated(lambda: np.array([arr, [0]], dtype=np.float64)) + self.assert_deprecated(lambda: np.array([[0], arr], dtype=np.float64)) + + +class FlatteningConcatenateUnsafeCast(_DeprecationTestCase): + # NumPy 1.20, 2020-09-03 + message = "concatenate with `axis=None` will use same-kind casting" + + def test_deprecated(self): + self.assert_deprecated(np.concatenate, + args=(([0.], [1.]),), + kwargs=dict(axis=None, out=np.empty(2, dtype=np.int64))) + + def test_not_deprecated(self): + self.assert_not_deprecated(np.concatenate, + args=(([0.], [1.]),), + kwargs={'axis': None, 'out': np.empty(2, dtype=np.int64), + 'casting': "unsafe"}) + + with assert_raises(TypeError): + # Tests should notice if the deprecation warning is given first... + np.concatenate(([0.], [1.]), out=np.empty(2, dtype=np.int64), + casting="same_kind") + + +class TestDeprecateSubarrayDTypeDuringArrayCoercion(_DeprecationTestCase): + warning_cls = FutureWarning + message = "(creating|casting) an array (with|to) a subarray dtype" + + def test_deprecated_array(self): + # Arrays are more complex, since they "broadcast" on success: + arr = np.array([1, 2]) + + self.assert_deprecated(lambda: arr.astype("(2)i,")) + with pytest.warns(FutureWarning): + res = arr.astype("(2)i,") + + assert_array_equal(res, [[1, 2], [1, 2]]) + + self.assert_deprecated(lambda: np.array(arr, dtype="(2)i,")) + with pytest.warns(FutureWarning): + res = np.array(arr, dtype="(2)i,") + + assert_array_equal(res, [[1, 2], [1, 2]]) + + with pytest.warns(FutureWarning): + res = np.array([[(1,), (2,)], arr], dtype="(2)i,") + + assert_array_equal(res, [[[1, 1], [2, 2]], [[1, 2], [1, 2]]]) + + def test_deprecated_and_error(self): + # These error paths do not give a warning, but will succeed in the + # future. + arr = np.arange(5 * 2).reshape(5, 2) + def check(): + with pytest.raises(ValueError): + arr.astype("(2,2)f") + + self.assert_deprecated(check) + + def check(): + with pytest.raises(ValueError): + np.array(arr, dtype="(2,2)f") + + self.assert_deprecated(check) + + +class TestFutureWarningArrayLikeNotIterable(_DeprecationTestCase): + # Deprecated 2020-12-09, NumPy 1.20 + warning_cls = FutureWarning + message = "The input object of type.*but not a sequence" + + @pytest.mark.parametrize("protocol", + ["__array__", "__array_interface__", "__array_struct__"]) + def test_deprecated(self, protocol): + """Test that these objects give a warning since they are not 0-D, + not coerced at the top level `np.array(obj)`, but nested, and do + *not* define the sequence protocol. + + NOTE: Tests for the versions including __len__ and __getitem__ exist + in `test_array_coercion.py` and they can be modified or amended + when this deprecation expired. + """ + blueprint = np.arange(10) + MyArr = type("MyArr", (), {protocol: getattr(blueprint, protocol)}) + self.assert_deprecated(lambda: np.array([MyArr()], dtype=object)) + + @pytest.mark.parametrize("protocol", + ["__array__", "__array_interface__", "__array_struct__"]) + def test_0d_not_deprecated(self, protocol): + # 0-D always worked (albeit it would use __float__ or similar for the + # conversion, which may not happen anymore) + blueprint = np.array(1.) + MyArr = type("MyArr", (), {protocol: getattr(blueprint, protocol)}) + myarr = MyArr() + + self.assert_not_deprecated(lambda: np.array([myarr], dtype=object)) + res = np.array([myarr], dtype=object) + expected = np.empty(1, dtype=object) + expected[0] = myarr + assert_array_equal(res, expected) + + @pytest.mark.parametrize("protocol", + ["__array__", "__array_interface__", "__array_struct__"]) + def test_unnested_not_deprecated(self, protocol): + blueprint = np.arange(10) + MyArr = type("MyArr", (), {protocol: getattr(blueprint, protocol)}) + myarr = MyArr() + + self.assert_not_deprecated(lambda: np.array(myarr)) + res = np.array(myarr) + assert_array_equal(res, blueprint) + + @pytest.mark.parametrize("protocol", + ["__array__", "__array_interface__", "__array_struct__"]) + def test_strange_dtype_handling(self, protocol): + """The old code would actually use the dtype from the array, but + then end up not using the array (for dimension discovery) + """ + blueprint = np.arange(10).astype("f4") + MyArr = type("MyArr", (), {protocol: getattr(blueprint, protocol), + "__float__": lambda _: 0.5}) + myarr = MyArr() + + # Make sure we warn (and capture the FutureWarning) + with pytest.warns(FutureWarning, match=self.message): + res = np.array([[myarr]]) + + assert res.shape == (1, 1) + assert res.dtype == "f4" + assert res[0, 0] == 0.5 + + @pytest.mark.parametrize("protocol", + ["__array__", "__array_interface__", "__array_struct__"]) + def test_assignment_not_deprecated(self, protocol): + # If the result is dtype=object we do not unpack a nested array or + # array-like, if it is nested at exactly the right depth. + # NOTE: We actually do still call __array__, etc. but ignore the result + # in the end. For `dtype=object` we could optimize that away. + blueprint = np.arange(10).astype("f4") + MyArr = type("MyArr", (), {protocol: getattr(blueprint, protocol), + "__float__": lambda _: 0.5}) + myarr = MyArr() + + res = np.empty(3, dtype=object) + def set(): + res[:] = [myarr, myarr, myarr] + self.assert_not_deprecated(set) + assert res[0] is myarr + assert res[1] is myarr + assert res[2] is myarr + + +class TestDeprecatedUnpickleObjectScalar(_DeprecationTestCase): + # Deprecated 2020-11-24, NumPy 1.20 + """ + Technically, it should be impossible to create numpy object scalars, + but there was an unpickle path that would in theory allow it. That + path is invalid and must lead to the warning. + """ + message = "Unpickling a scalar with object dtype is deprecated." + + def test_deprecated(self): + ctor = np.core.multiarray.scalar + self.assert_deprecated(lambda: ctor(np.dtype("O"), 1)) + +try: + with warnings.catch_warnings(): + warnings.simplefilter("always") + import nose # noqa: F401 +except ImportError: + HAVE_NOSE = False +else: + HAVE_NOSE = True + + +@pytest.mark.skipif(not HAVE_NOSE, reason="Needs nose") +class TestNoseDecoratorsDeprecated(_DeprecationTestCase): + class DidntSkipException(Exception): + pass + + def test_slow(self): + def _test_slow(): + @np.testing.dec.slow + def slow_func(x, y, z): + pass + + assert_(slow_func.slow) + self.assert_deprecated(_test_slow) + + def test_setastest(self): + def _test_setastest(): + @np.testing.dec.setastest() + def f_default(a): + pass + + @np.testing.dec.setastest(True) + def f_istest(a): + pass + + @np.testing.dec.setastest(False) + def f_isnottest(a): + pass + + assert_(f_default.__test__) + assert_(f_istest.__test__) + assert_(not f_isnottest.__test__) + self.assert_deprecated(_test_setastest, num=3) + + def test_skip_functions_hardcoded(self): + def _test_skip_functions_hardcoded(): + @np.testing.dec.skipif(True) + def f1(x): + raise self.DidntSkipException + + try: + f1('a') + except self.DidntSkipException: + raise Exception('Failed to skip') + except SkipTest().__class__: + pass + + @np.testing.dec.skipif(False) + def f2(x): + raise self.DidntSkipException + + try: + f2('a') + except self.DidntSkipException: + pass + except SkipTest().__class__: + raise Exception('Skipped when not expected to') + self.assert_deprecated(_test_skip_functions_hardcoded, num=2) + + def test_skip_functions_callable(self): + def _test_skip_functions_callable(): + def skip_tester(): + return skip_flag == 'skip me!' + + @np.testing.dec.skipif(skip_tester) + def f1(x): + raise self.DidntSkipException + + try: + skip_flag = 'skip me!' + f1('a') + except self.DidntSkipException: + raise Exception('Failed to skip') + except SkipTest().__class__: + pass + + @np.testing.dec.skipif(skip_tester) + def f2(x): + raise self.DidntSkipException + + try: + skip_flag = 'five is right out!' + f2('a') + except self.DidntSkipException: + pass + except SkipTest().__class__: + raise Exception('Skipped when not expected to') + self.assert_deprecated(_test_skip_functions_callable, num=2) + + def test_skip_generators_hardcoded(self): + def _test_skip_generators_hardcoded(): + @np.testing.dec.knownfailureif(True, "This test is known to fail") + def g1(x): + yield from range(x) + + try: + for j in g1(10): + pass + except KnownFailureException().__class__: + pass + else: + raise Exception('Failed to mark as known failure') + + @np.testing.dec.knownfailureif(False, "This test is NOT known to fail") + def g2(x): + yield from range(x) + raise self.DidntSkipException('FAIL') + + try: + for j in g2(10): + pass + except KnownFailureException().__class__: + raise Exception('Marked incorrectly as known failure') + except self.DidntSkipException: + pass + self.assert_deprecated(_test_skip_generators_hardcoded, num=2) + + def test_skip_generators_callable(self): + def _test_skip_generators_callable(): + def skip_tester(): + return skip_flag == 'skip me!' + + @np.testing.dec.knownfailureif(skip_tester, "This test is known to fail") + def g1(x): + yield from range(x) + + try: + skip_flag = 'skip me!' + for j in g1(10): + pass + except KnownFailureException().__class__: + pass + else: + raise Exception('Failed to mark as known failure') + + @np.testing.dec.knownfailureif(skip_tester, "This test is NOT known to fail") + def g2(x): + yield from range(x) + raise self.DidntSkipException('FAIL') + + try: + skip_flag = 'do not skip' + for j in g2(10): + pass + except KnownFailureException().__class__: + raise Exception('Marked incorrectly as known failure') + except self.DidntSkipException: + pass + self.assert_deprecated(_test_skip_generators_callable, num=2) + + def test_deprecated(self): + def _test_deprecated(): + @np.testing.dec.deprecated(True) + def non_deprecated_func(): + pass + + @np.testing.dec.deprecated() + def deprecated_func(): + import warnings + warnings.warn("TEST: deprecated func", DeprecationWarning, stacklevel=1) + + @np.testing.dec.deprecated() + def deprecated_func2(): + import warnings + warnings.warn("AHHHH", stacklevel=1) + raise ValueError + + @np.testing.dec.deprecated() + def deprecated_func3(): + import warnings + warnings.warn("AHHHH", stacklevel=1) + + # marked as deprecated, but does not raise DeprecationWarning + assert_raises(AssertionError, non_deprecated_func) + # should be silent + deprecated_func() + with warnings.catch_warnings(record=True): + warnings.simplefilter("always") # do not propagate unrelated warnings + # fails if deprecated decorator just disables test. See #1453. + assert_raises(ValueError, deprecated_func2) + # warning is not a DeprecationWarning + assert_raises(AssertionError, deprecated_func3) + self.assert_deprecated(_test_deprecated, num=4) + + def test_parametrize(self): + def _test_parametrize(): + # dec.parametrize assumes that it is being run by nose. Because + # we are running under pytest, we need to explicitly check the + # results. + @np.testing.dec.parametrize('base, power, expected', + [(1, 1, 1), + (2, 1, 2), + (2, 2, 4)]) + def check_parametrize(base, power, expected): + assert_(base**power == expected) + + count = 0 + for test in check_parametrize(): + test[0](*test[1:]) + count += 1 + assert_(count == 3) + self.assert_deprecated(_test_parametrize) + + +class TestSingleElementSignature(_DeprecationTestCase): + # Deprecated 2021-04-01, NumPy 1.21 + message = r"The use of a length 1" + + def test_deprecated(self): + self.assert_deprecated(lambda: np.add(1, 2, signature="d")) + self.assert_deprecated(lambda: np.add(1, 2, sig=(np.dtype("l"),))) + + +class TestComparisonBadDType(_DeprecationTestCase): + # Deprecated 2021-04-01, NumPy 1.21 + message = r"using `dtype=` in comparisons is only useful for" + + def test_deprecated(self): + self.assert_deprecated(lambda: np.equal(1, 1, dtype=np.int64)) + # Not an error only for the transition + self.assert_deprecated(lambda: np.equal(1, 1, sig=(None, None, "l"))) + + def test_not_deprecated(self): + np.equal(True, False, dtype=bool) + np.equal(3, 5, dtype=bool, casting="unsafe") + np.equal([None], [4], dtype=object) + +class TestComparisonBadObjectDType(_DeprecationTestCase): + # Deprecated 2021-04-01, NumPy 1.21 (different branch of the above one) + message = r"using `dtype=object` \(or equivalent signature\) will" + warning_cls = FutureWarning + + def test_deprecated(self): + self.assert_deprecated(lambda: np.equal(1, 1, dtype=object)) + self.assert_deprecated( + lambda: np.equal(1, 1, sig=(None, None, object))) + + +class TestSpecialAttributeLookupFailure(_DeprecationTestCase): + message = r"An exception was ignored while fetching the attribute" + + class WeirdArrayLike: + @property + def __array__(self): + raise RuntimeError("oops!") + + class WeirdArrayInterface: + @property + def __array_interface__(self): + raise RuntimeError("oops!") + + def test_deprecated(self): + self.assert_deprecated(lambda: np.array(self.WeirdArrayLike())) + self.assert_deprecated(lambda: np.array(self.WeirdArrayInterface())) + + +class TestCtypesGetter(_DeprecationTestCase): + # Deprecated 2021-05-18, Numpy 1.21.0 + warning_cls = DeprecationWarning + ctypes = np.array([1]).ctypes + + @pytest.mark.parametrize( + "name", ["get_data", "get_shape", "get_strides", "get_as_parameter"] + ) + def test_deprecated(self, name: str) -> None: + func = getattr(self.ctypes, name) + self.assert_deprecated(lambda: func()) + + @pytest.mark.parametrize( + "name", ["data", "shape", "strides", "_as_parameter_"] + ) + def test_not_deprecated(self, name: str) -> None: + self.assert_not_deprecated(lambda: getattr(self.ctypes, name)) + + +class TestUFuncForcedDTypeWarning(_DeprecationTestCase): + message = "The `dtype` and `signature` arguments to ufuncs only select the" + + def test_not_deprecated(self): + import pickle + # does not warn (test relies on bad pickling behaviour, simply remove + # it if the `assert int64 is not int64_2` should start failing. + int64 = np.dtype("int64") + int64_2 = pickle.loads(pickle.dumps(int64)) + assert int64 is not int64_2 + self.assert_not_deprecated(lambda: np.add(3, 4, dtype=int64_2)) + + def test_deprecation(self): + int64 = np.dtype("int64") + self.assert_deprecated(lambda: np.add(3, 5, dtype=int64.newbyteorder())) + self.assert_deprecated(lambda: np.add(3, 5, dtype="m8[ns]")) + + def test_behaviour(self): + int64 = np.dtype("int64") + arr = np.arange(10, dtype="m8[s]") + + with pytest.warns(DeprecationWarning, match=self.message): + np.add(3, 5, dtype=int64.newbyteorder()) + with pytest.warns(DeprecationWarning, match=self.message): + np.add(3, 5, dtype="m8[ns]") # previously used the "ns" + with pytest.warns(DeprecationWarning, match=self.message): + np.add(arr, arr, dtype="m8[ns]") # never preserved the "ns" + with pytest.warns(DeprecationWarning, match=self.message): + np.maximum(arr, arr, dtype="m8[ns]") # previously used the "ns" + with pytest.warns(DeprecationWarning, match=self.message): + np.maximum.reduce(arr, dtype="m8[ns]") # never preserved the "ns" + + +PARTITION_DICT = { + "partition method": np.arange(10).partition, + "argpartition method": np.arange(10).argpartition, + "partition function": lambda kth: np.partition(np.arange(10), kth), + "argpartition function": lambda kth: np.argpartition(np.arange(10), kth), +} + + +@pytest.mark.parametrize("func", PARTITION_DICT.values(), ids=PARTITION_DICT) +class TestPartitionBoolIndex(_DeprecationTestCase): + # Deprecated 2021-09-29, NumPy 1.22 + warning_cls = DeprecationWarning + message = "Passing booleans as partition index is deprecated" + + def test_deprecated(self, func): + self.assert_deprecated(lambda: func(True)) + self.assert_deprecated(lambda: func([False, True])) + + def test_not_deprecated(self, func): + self.assert_not_deprecated(lambda: func(1)) + self.assert_not_deprecated(lambda: func([0, 1])) + + +class TestMachAr(_DeprecationTestCase): + # Deprecated 2021-10-19, NumPy 1.22 + warning_cls = DeprecationWarning + + def test_deprecated(self): + self.assert_deprecated(lambda: np.MachAr) + + def test_deprecated_module(self): + self.assert_deprecated(lambda: getattr(np.core, "machar")) + + def test_deprecated_attr(self): + finfo = np.finfo(float) + self.assert_deprecated(lambda: getattr(finfo, "machar")) + + +class TestQuantileInterpolationDeprecation(_DeprecationTestCase): + # Deprecated 2021-11-08, NumPy 1.22 + @pytest.mark.parametrize("func", + [np.percentile, np.quantile, np.nanpercentile, np.nanquantile]) + def test_deprecated(self, func): + self.assert_deprecated( + lambda: func([0., 1.], 0., interpolation="linear")) + self.assert_deprecated( + lambda: func([0., 1.], 0., interpolation="nearest")) + + @pytest.mark.parametrize("func", + [np.percentile, np.quantile, np.nanpercentile, np.nanquantile]) + def test_both_passed(self, func): + with warnings.catch_warnings(): + # catch the DeprecationWarning so that it does not raise: + warnings.simplefilter("always", DeprecationWarning) + with pytest.raises(TypeError): + func([0., 1.], 0., interpolation="nearest", method="nearest") diff --git a/numpy/core/tests/test_dlpack.py b/numpy/core/tests/test_dlpack.py new file mode 100644 index 000000000000..f848b2008cf9 --- /dev/null +++ b/numpy/core/tests/test_dlpack.py @@ -0,0 +1,109 @@ +import sys +import pytest + +import numpy as np +from numpy.testing import assert_array_equal, IS_PYPY + + +class TestDLPack: + @pytest.mark.skipif(IS_PYPY, reason="PyPy can't get refcounts.") + def test_dunder_dlpack_refcount(self): + x = np.arange(5) + y = x.__dlpack__() + assert sys.getrefcount(x) == 3 + del y + assert sys.getrefcount(x) == 2 + + def test_dunder_dlpack_stream(self): + x = np.arange(5) + x.__dlpack__(stream=None) + + with pytest.raises(RuntimeError): + x.__dlpack__(stream=1) + + def test_strides_not_multiple_of_itemsize(self): + dt = np.dtype([('int', np.int32), ('char', np.int8)]) + y = np.zeros((5,), dtype=dt) + z = y['int'] + + with pytest.raises(RuntimeError): + np._from_dlpack(z) + + @pytest.mark.skipif(IS_PYPY, reason="PyPy can't get refcounts.") + def test_from_dlpack_refcount(self): + x = np.arange(5) + y = np._from_dlpack(x) + assert sys.getrefcount(x) == 3 + del y + assert sys.getrefcount(x) == 2 + + @pytest.mark.parametrize("dtype", [ + np.int8, np.int16, np.int32, np.int64, + np.uint8, np.uint16, np.uint32, np.uint64, + np.float16, np.float32, np.float64, + np.complex64, np.complex128 + ]) + def test_dtype_passthrough(self, dtype): + x = np.arange(5, dtype=dtype) + y = np._from_dlpack(x) + + assert y.dtype == x.dtype + assert_array_equal(x, y) + + def test_invalid_dtype(self): + x = np.asarray(np.datetime64('2021-05-27')) + + with pytest.raises(TypeError): + np._from_dlpack(x) + + def test_invalid_byte_swapping(self): + dt = np.dtype('=i8').newbyteorder() + x = np.arange(5, dtype=dt) + + with pytest.raises(TypeError): + np._from_dlpack(x) + + def test_non_contiguous(self): + x = np.arange(25).reshape((5, 5)) + + y1 = x[0] + assert_array_equal(y1, np._from_dlpack(y1)) + + y2 = x[:, 0] + assert_array_equal(y2, np._from_dlpack(y2)) + + y3 = x[1, :] + assert_array_equal(y3, np._from_dlpack(y3)) + + y4 = x[1] + assert_array_equal(y4, np._from_dlpack(y4)) + + y5 = np.diagonal(x).copy() + assert_array_equal(y5, np._from_dlpack(y5)) + + @pytest.mark.parametrize("ndim", range(33)) + def test_higher_dims(self, ndim): + shape = (1,) * ndim + x = np.zeros(shape, dtype=np.float64) + + assert shape == np._from_dlpack(x).shape + + def test_dlpack_device(self): + x = np.arange(5) + assert x.__dlpack_device__() == (1, 0) + assert np._from_dlpack(x).__dlpack_device__() == (1, 0) + + def dlpack_deleter_exception(self): + x = np.arange(5) + _ = x.__dlpack__() + raise RuntimeError + + def test_dlpack_destructor_exception(self): + with pytest.raises(RuntimeError): + self.dlpack_deleter_exception() + + def test_readonly(self): + x = np.arange(5) + x.flags.writeable = False + with pytest.raises(TypeError): + x.__dlpack__() diff --git a/numpy/core/tests/test_dtype.py b/numpy/core/tests/test_dtype.py index 27fbb10d59ec..e49604e4db7a 100644 --- a/numpy/core/tests/test_dtype.py +++ b/numpy/core/tests/test_dtype.py @@ -1,13 +1,20 @@ -from __future__ import division, absolute_import, print_function - -import pickle import sys import operator import pytest +import ctypes +import gc +import types +from typing import Any import numpy as np from numpy.core._rational_tests import rational -from numpy.testing import assert_, assert_equal, assert_raises +from numpy.core._multiarray_tests import create_custom_field_dtype +from numpy.testing import ( + assert_, assert_equal, assert_array_equal, assert_raises, HAS_REFCOUNT, + IS_PYSTON) +from numpy.compat import pickle +from itertools import permutations + def assert_dtype_equal(a, b): assert_equal(a, b) @@ -19,27 +26,27 @@ def assert_dtype_not_equal(a, b): assert_(hash(a) != hash(b), "two different types hash to the same value !") -class TestBuiltin(object): - def test_run(self): +class TestBuiltin: + @pytest.mark.parametrize('t', [int, float, complex, np.int32, str, object, + np.compat.unicode]) + def test_run(self, t): """Only test hash runs at all.""" - for t in [int, float, complex, np.int32, str, object, - np.unicode]: - dt = np.dtype(t) - hash(dt) + dt = np.dtype(t) + hash(dt) - def test_dtype(self): + @pytest.mark.parametrize('t', [int, float]) + def test_dtype(self, t): # Make sure equivalent byte order char hash the same (e.g. < and = on # little endian) - for t in [int, float]: - dt = np.dtype(t) - dt2 = dt.newbyteorder("<") - dt3 = dt.newbyteorder(">") - if dt == dt2: - assert_(dt.byteorder != dt2.byteorder, "bogus test") - assert_dtype_equal(dt, dt2) - else: - assert_(dt.byteorder != dt3.byteorder, "bogus test") - assert_dtype_equal(dt, dt3) + dt = np.dtype(t) + dt2 = dt.newbyteorder("<") + dt3 = dt.newbyteorder(">") + if dt == dt2: + assert_(dt.byteorder != dt2.byteorder, "bogus test") + assert_dtype_equal(dt, dt2) + else: + assert_(dt.byteorder != dt3.byteorder, "bogus test") + assert_dtype_equal(dt, dt3) def test_equivalent_dtype_hashing(self): # Make sure equivalent dtypes with different type num hash equal @@ -84,6 +91,66 @@ def test_invalid_types(self): assert_raises(TypeError, np.dtype, 'q8') assert_raises(TypeError, np.dtype, 'Q8') + def test_richcompare_invalid_dtype_equality(self): + # Make sure objects that cannot be converted to valid + # dtypes results in False/True when compared to valid dtypes. + # Here 7 cannot be converted to dtype. No exceptions should be raised + + assert not np.dtype(np.int32) == 7, "dtype richcompare failed for ==" + assert np.dtype(np.int32) != 7, "dtype richcompare failed for !=" + + @pytest.mark.parametrize( + 'operation', + [operator.le, operator.lt, operator.ge, operator.gt]) + def test_richcompare_invalid_dtype_comparison(self, operation): + # Make sure TypeError is raised for comparison operators + # for invalid dtypes. Here 7 is an invalid dtype. + + with pytest.raises(TypeError): + operation(np.dtype(np.int32), 7) + + @pytest.mark.parametrize("dtype", + ['Bool', 'Bytes0', 'Complex32', 'Complex64', + 'Datetime64', 'Float16', 'Float32', 'Float64', + 'Int8', 'Int16', 'Int32', 'Int64', + 'Object0', 'Str0', 'Timedelta64', + 'UInt8', 'UInt16', 'Uint32', 'UInt32', + 'Uint64', 'UInt64', 'Void0', + "Float128", "Complex128"]) + def test_numeric_style_types_are_invalid(self, dtype): + with assert_raises(TypeError): + np.dtype(dtype) + + @pytest.mark.parametrize( + 'value', + ['m8', 'M8', 'datetime64', 'timedelta64', + 'i4, (2,3)f8, f4', 'a3, 3u8, (3,4)a10', + '>f', '<f', '=f', '|f', + ]) + def test_dtype_bytes_str_equivalence(self, value): + bytes_value = value.encode('ascii') + from_bytes = np.dtype(bytes_value) + from_str = np.dtype(value) + assert_dtype_equal(from_bytes, from_str) + + def test_dtype_from_bytes(self): + # Empty bytes object + assert_raises(TypeError, np.dtype, b'') + # Byte order indicator, but no type + assert_raises(TypeError, np.dtype, b'|') + + # Single character with ordinal < NPY_NTYPES returns + # type by index into _builtin_descrs + assert_dtype_equal(np.dtype(bytes([0])), np.dtype('bool')) + assert_dtype_equal(np.dtype(bytes([17])), np.dtype(object)) + + # Single character where value is a valid type code + assert_dtype_equal(np.dtype(b'f'), np.dtype('float32')) + + # Bytes with non-ascii values raise errors + assert_raises(TypeError, np.dtype, b'\xff') + assert_raises(TypeError, np.dtype, b's\xff') + def test_bad_param(self): # Can't give a size that's too small assert_raises(ValueError, np.dtype, @@ -104,15 +171,18 @@ def test_bad_param(self): 'offsets':[0, 2]}, align=True) def test_field_order_equality(self): - x = np.dtype({'names': ['A', 'B'], - 'formats': ['i4', 'f4'], + x = np.dtype({'names': ['A', 'B'], + 'formats': ['i4', 'f4'], 'offsets': [0, 4]}) - y = np.dtype({'names': ['B', 'A'], - 'formats': ['f4', 'i4'], + y = np.dtype({'names': ['B', 'A'], + 'formats': ['f4', 'i4'], 'offsets': [4, 0]}) assert_equal(x == y, False) + # But it is currently an equivalent cast: + assert np.can_cast(x, y, casting="equiv") -class TestRecord(object): + +class TestRecord: def test_equivalent_record(self): """Test whether equivalent record dtypes hash the same.""" a = np.dtype([('yo', int)]) @@ -135,6 +205,18 @@ def test_different_titles(self): 'titles': ['RRed pixel', 'Blue pixel']}) assert_dtype_not_equal(a, b) + @pytest.mark.skipif(not HAS_REFCOUNT, reason="Python lacks refcounts") + def test_refcount_dictionary_setting(self): + names = ["name1"] + formats = ["f8"] + titles = ["t1"] + offsets = [0] + d = dict(names=names, formats=formats, titles=titles, offsets=offsets) + refcounts = {k: sys.getrefcount(i) for k, i in d.items()} + np.dtype(d) + refcounts_new = {k: sys.getrefcount(i) for k, i in d.items()} + assert refcounts == refcounts_new + def test_mutate(self): # Mutating a dtype should reset the cached hash value a = np.dtype([('yo', int)]) @@ -155,9 +237,9 @@ def test_not_lists(self): the dtype constructor. """ assert_raises(TypeError, np.dtype, - dict(names=set(['A', 'B']), formats=['f8', 'i4'])) + dict(names={'A', 'B'}, formats=['f8', 'i4'])) assert_raises(TypeError, np.dtype, - dict(names=['A', 'B'], formats=set(['f8', 'i4']))) + dict(names=['A', 'B'], formats={'f8', 'i4'})) def test_aligned_size(self): # Check that structured dtypes get padded to an aligned size @@ -212,7 +294,6 @@ def test_aligned_size(self): assert_equal(dt1.descr, [('a', '|i1'), ('', '|V3'), ('b', [('f0', '<i2'), ('', '|V2'), ('f1', '<f4')], (2,))]) - def test_union_struct(self): # Should be able to create union dtypes @@ -261,6 +342,24 @@ def test_union_struct(self): 'formats':['i1', 'O'], 'offsets':[np.dtype('intp').itemsize, 0]}) + @pytest.mark.parametrize(["obj", "dtype", "expected"], + [([], ("(2)f4,"), np.empty((0, 2), dtype="f4")), + (3, "(3)f4,", [3, 3, 3]), + (np.float64(2), "(2)f4,", [2, 2]), + ([((0, 1), (1, 2)), ((2,),)], '(2,2)f4', None), + (["1", "2"], "(2)i,", None)]) + def test_subarray_list(self, obj, dtype, expected): + dtype = np.dtype(dtype) + res = np.array(obj, dtype=dtype) + + if expected is None: + # iterate the 1-d list to fill the array + expected = np.empty(len(obj), dtype=dtype) + for i in range(len(expected)): + expected[i] = obj[i] + + assert_array_equal(res, expected) + def test_comma_datetime(self): dt = np.dtype('M8[D],datetime64[Y],i8') assert_equal(dt, np.dtype([('f0', 'M8[D]'), @@ -314,12 +413,93 @@ def test_fields_by_index(self): assert_raises(IndexError, lambda: dt[-3]) assert_raises(TypeError, operator.getitem, dt, 3.0) - assert_raises(TypeError, operator.getitem, dt, []) assert_equal(dt[1], dt[np.int8(1)]) + @pytest.mark.parametrize('align_flag',[False, True]) + def test_multifield_index(self, align_flag): + # indexing with a list produces subfields + # the align flag should be preserved + dt = np.dtype([ + (('title', 'col1'), '<U20'), ('A', '<f8'), ('B', '<f8') + ], align=align_flag) + + dt_sub = dt[['B', 'col1']] + assert_equal( + dt_sub, + np.dtype({ + 'names': ['B', 'col1'], + 'formats': ['<f8', '<U20'], + 'offsets': [88, 0], + 'titles': [None, 'title'], + 'itemsize': 96 + }) + ) + assert_equal(dt_sub.isalignedstruct, align_flag) + + dt_sub = dt[['B']] + assert_equal( + dt_sub, + np.dtype({ + 'names': ['B'], + 'formats': ['<f8'], + 'offsets': [88], + 'itemsize': 96 + }) + ) + assert_equal(dt_sub.isalignedstruct, align_flag) + + dt_sub = dt[[]] + assert_equal( + dt_sub, + np.dtype({ + 'names': [], + 'formats': [], + 'offsets': [], + 'itemsize': 96 + }) + ) + assert_equal(dt_sub.isalignedstruct, align_flag) + + assert_raises(TypeError, operator.getitem, dt, ()) + assert_raises(TypeError, operator.getitem, dt, [1, 2, 3]) + assert_raises(TypeError, operator.getitem, dt, ['col1', 2]) + assert_raises(KeyError, operator.getitem, dt, ['fake']) + assert_raises(KeyError, operator.getitem, dt, ['title']) + assert_raises(ValueError, operator.getitem, dt, ['col1', 'col1']) + + def test_partial_dict(self): + # 'names' is missing + assert_raises(ValueError, np.dtype, + {'formats': ['i4', 'i4'], 'f0': ('i4', 0), 'f1':('i4', 4)}) + + def test_fieldless_views(self): + a = np.zeros(2, dtype={'names':[], 'formats':[], 'offsets':[], + 'itemsize':8}) + assert_raises(ValueError, a.view, np.dtype([])) + + d = np.dtype((np.dtype([]), 10)) + assert_equal(d.shape, (10,)) + assert_equal(d.itemsize, 0) + assert_equal(d.base, np.dtype([])) + + arr = np.fromiter((() for i in range(10)), []) + assert_equal(arr.dtype, np.dtype([])) + assert_raises(ValueError, np.frombuffer, b'', dtype=[]) + assert_equal(np.frombuffer(b'', dtype=[], count=2), + np.empty(2, dtype=[])) + + assert_raises(ValueError, np.dtype, ([], 'f8')) + assert_raises(ValueError, np.zeros(1, dtype='i4').view, []) + + assert_equal(np.zeros(2, dtype=[]) == np.zeros(2, dtype=[]), + np.ones(2, dtype=bool)) + + assert_equal(np.zeros((1, 2), dtype=[]) == a, + np.ones((1, 2), dtype=bool)) -class TestSubarray(object): + +class TestSubarray: def test_single_subarray(self): a = np.dtype((int, (2))) b = np.dtype((int, (2,))) @@ -351,7 +531,10 @@ def test_nonequivalent_record(self): def test_shape_equal(self): """Test some data types that are equal""" assert_dtype_equal(np.dtype('f8'), np.dtype(('f8', tuple()))) - assert_dtype_equal(np.dtype('f8'), np.dtype(('f8', 1))) + # FutureWarning during deprecation period; after it is passed this + # should instead check that "(1)f8" == "1f8" == ("f8", 1). + with pytest.warns(FutureWarning): + assert_dtype_equal(np.dtype('f8'), np.dtype(('f8', 1))) assert_dtype_equal(np.dtype((int, 2)), np.dtype((int, (2,)))) assert_dtype_equal(np.dtype(('<f4', (3, 2))), np.dtype(('<f4', (3, 2)))) d = ([('a', 'f4', (1, 2)), ('b', 'f8', (3, 1))], (3, 2)) @@ -392,7 +575,7 @@ def test_shape_sequence(self): assert_(isinstance(dt['a'].shape, tuple)) # - class IntLike(object): + class IntLike: def __index__(self): return 3 @@ -440,12 +623,187 @@ def test_shape_invalid(self): def test_alignment(self): #Check that subarrays are aligned - t1 = np.dtype('1i4', align=True) + t1 = np.dtype('(1,)i4', align=True) t2 = np.dtype('2i4', align=True) assert_equal(t1.alignment, t2.alignment) - -class TestMonsterType(object): + def test_aligned_empty(self): + # Mainly regression test for gh-19696: construction failed completely + dt = np.dtype([], align=True) + assert dt == np.dtype([]) + dt = np.dtype({"names": [], "formats": [], "itemsize": 0}, align=True) + assert dt == np.dtype([]) + +def iter_struct_object_dtypes(): + """ + Iterates over a few complex dtypes and object pattern which + fill the array with a given object (defaults to a singleton). + + Yields + ------ + dtype : dtype + pattern : tuple + Structured tuple for use with `np.array`. + count : int + Number of objects stored in the dtype. + singleton : object + A singleton object. The returned pattern is constructed so that + all objects inside the datatype are set to the singleton. + """ + obj = object() + + dt = np.dtype([('b', 'O', (2, 3))]) + p = ([[obj] * 3] * 2,) + yield pytest.param(dt, p, 6, obj, id="<subarray>") + + dt = np.dtype([('a', 'i4'), ('b', 'O', (2, 3))]) + p = (0, [[obj] * 3] * 2) + yield pytest.param(dt, p, 6, obj, id="<subarray in field>") + + dt = np.dtype([('a', 'i4'), + ('b', [('ba', 'O'), ('bb', 'i1')], (2, 3))]) + p = (0, [[(obj, 0)] * 3] * 2) + yield pytest.param(dt, p, 6, obj, id="<structured subarray 1>") + + dt = np.dtype([('a', 'i4'), + ('b', [('ba', 'O'), ('bb', 'O')], (2, 3))]) + p = (0, [[(obj, obj)] * 3] * 2) + yield pytest.param(dt, p, 12, obj, id="<structured subarray 2>") + + +@pytest.mark.skipif(not HAS_REFCOUNT, reason="Python lacks refcounts") +class TestStructuredObjectRefcounting: + """These tests cover various uses of complicated structured types which + include objects and thus require reference counting. + """ + @pytest.mark.parametrize(['dt', 'pat', 'count', 'singleton'], + iter_struct_object_dtypes()) + @pytest.mark.parametrize(["creation_func", "creation_obj"], [ + pytest.param(np.empty, None, + # None is probably used for too many things + marks=pytest.mark.skip("unreliable due to python's behaviour")), + (np.ones, 1), + (np.zeros, 0)]) + def test_structured_object_create_delete(self, dt, pat, count, singleton, + creation_func, creation_obj): + """Structured object reference counting in creation and deletion""" + # The test assumes that 0, 1, and None are singletons. + gc.collect() + before = sys.getrefcount(creation_obj) + arr = creation_func(3, dt) + + now = sys.getrefcount(creation_obj) + assert now - before == count * 3 + del arr + now = sys.getrefcount(creation_obj) + assert now == before + + @pytest.mark.parametrize(['dt', 'pat', 'count', 'singleton'], + iter_struct_object_dtypes()) + def test_structured_object_item_setting(self, dt, pat, count, singleton): + """Structured object reference counting for simple item setting""" + one = 1 + + gc.collect() + before = sys.getrefcount(singleton) + arr = np.array([pat] * 3, dt) + assert sys.getrefcount(singleton) - before == count * 3 + # Fill with `1` and check that it was replaced correctly: + before2 = sys.getrefcount(one) + arr[...] = one + after2 = sys.getrefcount(one) + assert after2 - before2 == count * 3 + del arr + gc.collect() + assert sys.getrefcount(one) == before2 + assert sys.getrefcount(singleton) == before + + @pytest.mark.parametrize(['dt', 'pat', 'count', 'singleton'], + iter_struct_object_dtypes()) + @pytest.mark.parametrize( + ['shape', 'index', 'items_changed'], + [((3,), ([0, 2],), 2), + ((3, 2), ([0, 2], slice(None)), 4), + ((3, 2), ([0, 2], [1]), 2), + ((3,), ([True, False, True]), 2)]) + def test_structured_object_indexing(self, shape, index, items_changed, + dt, pat, count, singleton): + """Structured object reference counting for advanced indexing.""" + # Use two small negative values (should be singletons, but less likely + # to run into race-conditions). This failed in some threaded envs + # When using 0 and 1. If it fails again, should remove all explicit + # checks, and rely on `pytest-leaks` reference count checker only. + val0 = -4 + val1 = -5 + + arr = np.full(shape, val0, dt) + + gc.collect() + before_val0 = sys.getrefcount(val0) + before_val1 = sys.getrefcount(val1) + # Test item getting: + part = arr[index] + after_val0 = sys.getrefcount(val0) + assert after_val0 - before_val0 == count * items_changed + del part + # Test item setting: + arr[index] = val1 + gc.collect() + after_val0 = sys.getrefcount(val0) + after_val1 = sys.getrefcount(val1) + assert before_val0 - after_val0 == count * items_changed + assert after_val1 - before_val1 == count * items_changed + + @pytest.mark.parametrize(['dt', 'pat', 'count', 'singleton'], + iter_struct_object_dtypes()) + def test_structured_object_take_and_repeat(self, dt, pat, count, singleton): + """Structured object reference counting for specialized functions. + The older functions such as take and repeat use different code paths + then item setting (when writing this). + """ + indices = [0, 1] + + arr = np.array([pat] * 3, dt) + gc.collect() + before = sys.getrefcount(singleton) + res = arr.take(indices) + after = sys.getrefcount(singleton) + assert after - before == count * 2 + new = res.repeat(10) + gc.collect() + after_repeat = sys.getrefcount(singleton) + assert after_repeat - after == count * 2 * 10 + + +class TestStructuredDtypeSparseFields: + """Tests subarray fields which contain sparse dtypes so that + not all memory is used by the dtype work. Such dtype's should + leave the underlying memory unchanged. + """ + dtype = np.dtype([('a', {'names':['aa', 'ab'], 'formats':['f', 'f'], + 'offsets':[0, 4]}, (2, 3))]) + sparse_dtype = np.dtype([('a', {'names':['ab'], 'formats':['f'], + 'offsets':[4]}, (2, 3))]) + + def test_sparse_field_assignment(self): + arr = np.zeros(3, self.dtype) + sparse_arr = arr.view(self.sparse_dtype) + + sparse_arr[...] = np.finfo(np.float32).max + # dtype is reduced when accessing the field, so shape is (3, 2, 3): + assert_array_equal(arr["a"]["aa"], np.zeros((3, 2, 3))) + + def test_sparse_field_assignment_fancy(self): + # Fancy assignment goes to the copyswap function for complex types: + arr = np.zeros(3, self.dtype) + sparse_arr = arr.view(self.sparse_dtype) + + sparse_arr[[0, 1, 2]] = np.finfo(np.float32).max + # dtype is reduced when accessing the field, so shape is (3, 2, 3): + assert_array_equal(arr["a"]["aa"], np.zeros((3, 2, 3))) + + +class TestMonsterType: """Test deeply nested subtypes.""" def test1(self): @@ -463,7 +821,30 @@ def test1(self): ('yi', np.dtype((a, (3, 2))))]) assert_dtype_equal(c, d) -class TestMetadata(object): + @pytest.mark.skipif(IS_PYSTON, reason="Pyston disables recursion checking") + def test_list_recursion(self): + l = list() + l.append(('f', l)) + with pytest.raises(RecursionError): + np.dtype(l) + + @pytest.mark.skipif(IS_PYSTON, reason="Pyston disables recursion checking") + def test_tuple_recursion(self): + d = np.int32 + for i in range(100000): + d = (d, (1,)) + with pytest.raises(RecursionError): + np.dtype(d) + + @pytest.mark.skipif(IS_PYSTON, reason="Pyston disables recursion checking") + def test_dict_recursion(self): + d = dict(names=['self'], formats=[None], offsets=[0]) + d['formats'][0] = d + with pytest.raises(RecursionError): + np.dtype(d) + + +class TestMetadata: def test_no_metadata(self): d = np.dtype(int) assert_(d.metadata is None) @@ -485,7 +866,7 @@ def test_base_metadata_copied(self): d = np.dtype((np.void, np.dtype('i4,i4', metadata={'datum': 1}))) assert_(d.metadata == {'datum': 1}) -class TestString(object): +class TestString: def test_complex_dtype_str(self): dt = np.dtype([('top', [('tiles', ('>f4', (64, 64)), (1,)), ('rtile', '>f4', (64, 36))], (3,)), @@ -506,14 +887,24 @@ def test_complex_dtype_str(self): ('bright', '>f4', (8, 36))])], align=True) assert_equal(str(dt), - "{'names':['top','bottom'], " - "'formats':[([('tiles', ('>f4', (64, 64)), (1,)), " - "('rtile', '>f4', (64, 36))], (3,))," - "[('bleft', ('>f4', (8, 64)), (1,)), " - "('bright', '>f4', (8, 36))]], " - "'offsets':[0,76800], " - "'itemsize':80000, " - "'aligned':True}") + "{'names': ['top', 'bottom']," + " 'formats': [([('tiles', ('>f4', (64, 64)), (1,)), " + "('rtile', '>f4', (64, 36))], (3,)), " + "[('bleft', ('>f4', (8, 64)), (1,)), " + "('bright', '>f4', (8, 36))]]," + " 'offsets': [0, 76800]," + " 'itemsize': 80000," + " 'aligned': True}") + with np.printoptions(legacy='1.21'): + assert_equal(str(dt), + "{'names':['top','bottom'], " + "'formats':[([('tiles', ('>f4', (64, 64)), (1,)), " + "('rtile', '>f4', (64, 36))], (3,))," + "[('bleft', ('>f4', (8, 64)), (1,)), " + "('bright', '>f4', (8, 36))]], " + "'offsets':[0,76800], " + "'itemsize':80000, " + "'aligned':True}") assert_equal(np.dtype(eval(str(dt))), dt) dt = np.dtype({'names': ['r', 'g', 'b'], 'formats': ['u1', 'u1', 'u1'], @@ -530,28 +921,28 @@ def test_complex_dtype_str(self): 'titles': ['Color', 'Red pixel', 'Green pixel', 'Blue pixel']}) assert_equal(str(dt), - "{'names':['rgba','r','g','b']," - " 'formats':['<u4','u1','u1','u1']," - " 'offsets':[0,0,1,2]," - " 'titles':['Color','Red pixel'," - "'Green pixel','Blue pixel']," - " 'itemsize':4}") + "{'names': ['rgba', 'r', 'g', 'b']," + " 'formats': ['<u4', 'u1', 'u1', 'u1']," + " 'offsets': [0, 0, 1, 2]," + " 'titles': ['Color', 'Red pixel', " + "'Green pixel', 'Blue pixel']," + " 'itemsize': 4}") dt = np.dtype({'names': ['r', 'b'], 'formats': ['u1', 'u1'], 'offsets': [0, 2], 'titles': ['Red pixel', 'Blue pixel']}) assert_equal(str(dt), - "{'names':['r','b']," - " 'formats':['u1','u1']," - " 'offsets':[0,2]," - " 'titles':['Red pixel','Blue pixel']," - " 'itemsize':3}") + "{'names': ['r', 'b']," + " 'formats': ['u1', 'u1']," + " 'offsets': [0, 2]," + " 'titles': ['Red pixel', 'Blue pixel']," + " 'itemsize': 3}") dt = np.dtype([('a', '<m8[D]'), ('b', '<M8[us]')]) assert_equal(str(dt), "[('a', '<m8[D]'), ('b', '<M8[us]')]") - def test_complex_dtype_repr(self): + def test_repr_structured(self): dt = np.dtype([('top', [('tiles', ('>f4', (64, 64)), (1,)), ('rtile', '>f4', (64, 36))], (3,)), ('bottom', [('bleft', ('>f4', (8, 64)), (1,)), @@ -571,38 +962,40 @@ def test_complex_dtype_repr(self): "(('Green pixel', 'g'), 'u1'), " "(('Blue pixel', 'b'), 'u1')], align=True)") + def test_repr_structured_not_packed(self): dt = np.dtype({'names': ['rgba', 'r', 'g', 'b'], 'formats': ['<u4', 'u1', 'u1', 'u1'], 'offsets': [0, 0, 1, 2], 'titles': ['Color', 'Red pixel', 'Green pixel', 'Blue pixel']}, align=True) assert_equal(repr(dt), - "dtype({'names':['rgba','r','g','b']," - " 'formats':['<u4','u1','u1','u1']," - " 'offsets':[0,0,1,2]," - " 'titles':['Color','Red pixel'," - "'Green pixel','Blue pixel']," - " 'itemsize':4}, align=True)") + "dtype({'names': ['rgba', 'r', 'g', 'b']," + " 'formats': ['<u4', 'u1', 'u1', 'u1']," + " 'offsets': [0, 0, 1, 2]," + " 'titles': ['Color', 'Red pixel', " + "'Green pixel', 'Blue pixel']," + " 'itemsize': 4}, align=True)") dt = np.dtype({'names': ['r', 'b'], 'formats': ['u1', 'u1'], 'offsets': [0, 2], 'titles': ['Red pixel', 'Blue pixel'], 'itemsize': 4}) assert_equal(repr(dt), - "dtype({'names':['r','b'], " - "'formats':['u1','u1'], " - "'offsets':[0,2], " - "'titles':['Red pixel','Blue pixel'], " - "'itemsize':4})") + "dtype({'names': ['r', 'b'], " + "'formats': ['u1', 'u1'], " + "'offsets': [0, 2], " + "'titles': ['Red pixel', 'Blue pixel'], " + "'itemsize': 4})") + def test_repr_structured_datetime(self): dt = np.dtype([('a', '<M8[D]'), ('b', '<m8[us]')]) assert_equal(repr(dt), "dtype([('a', '<M8[D]'), ('b', '<m8[us]')])") - @pytest.mark.skipif(sys.version_info[0] >= 3, reason="Python 2 only") - def test_dtype_str_with_long_in_shape(self): - # Pull request #376, should not error - np.dtype('(1L,)i4') + def test_repr_str_subarray(self): + dt = np.dtype(('<i2', (1,))) + assert_equal(repr(dt), "dtype(('<i2', (1,)))") + assert_equal(str(dt), "('<i2', (1,))") def test_base_dtype_with_object_type(self): # Issue gh-2798, should not error. @@ -612,7 +1005,26 @@ def test_empty_string_to_object(self): # Pull request #4722 np.array(["", ""]).astype(object) -class TestDtypeAttributeDeletion(object): + def test_void_subclass_unsized(self): + dt = np.dtype(np.record) + assert_equal(repr(dt), "dtype('V')") + assert_equal(str(dt), '|V0') + assert_equal(dt.name, 'record') + + def test_void_subclass_sized(self): + dt = np.dtype((np.record, 2)) + assert_equal(repr(dt), "dtype('V2')") + assert_equal(str(dt), '|V2') + assert_equal(dt.name, 'record16') + + def test_void_subclass_fields(self): + dt = np.dtype((np.record, [('a', '<u2')])) + assert_equal(repr(dt), "dtype((numpy.record, [('a', '<u2')]))") + assert_equal(str(dt), "(numpy.record, [('a', '<u2')])") + assert_equal(dt.name, 'record16') + + +class TestDtypeAttributeDeletion: def test_dtype_non_writable_attributes_deletion(self): dt = np.dtype(np.double) @@ -630,7 +1042,7 @@ def test_dtype_writable_attributes_deletion(self): assert_raises(AttributeError, delattr, dt, s) -class TestDtypeAttributes(object): +class TestDtypeAttributes: def test_descr_has_trailing_void(self): # see gh-6359 dtype = np.dtype({ @@ -641,13 +1053,6 @@ def test_descr_has_trailing_void(self): new_dtype = np.dtype(dtype.descr) assert_equal(new_dtype.itemsize, 16) - def test_name_builtin(self): - for t in np.typeDict.values(): - name = t.__name__ - if name.endswith('_'): - name = name[:-1] - assert_equal(np.dtype(t).name, name) - def test_name_dtype_subclass(self): # Ticket #4357 class user_def_subcls(np.void): @@ -655,11 +1060,16 @@ class user_def_subcls(np.void): assert_equal(np.dtype(user_def_subcls).name, 'user_def_subcls') -class TestPickling(object): +class TestPickling: def check_pickling(self, dtype): for proto in range(pickle.HIGHEST_PROTOCOL + 1): - pickled = pickle.loads(pickle.dumps(dtype, proto)) + buf = pickle.dumps(dtype, proto) + # The dtype pickling itself pickles `np.dtype` if it is pickled + # as a singleton `dtype` should be stored in the buffer: + assert b"_DType_reconstruct" not in buf + assert b"dtype" in buf + pickled = pickle.loads(buf) assert_equal(pickled, dtype) assert_equal(pickled.descr, dtype.descr) if dtype.metadata is not None: @@ -670,43 +1080,157 @@ def check_pickling(self, dtype): assert_equal(x, y) assert_equal(x[0], y[0]) - def test_builtin(self): - for t in [int, float, complex, np.int32, str, object, - np.unicode, bool]: - self.check_pickling(np.dtype(t)) + @pytest.mark.parametrize('t', [int, float, complex, np.int32, str, object, + np.compat.unicode, bool]) + def test_builtin(self, t): + self.check_pickling(np.dtype(t)) def test_structured(self): dt = np.dtype(([('a', '>f4', (2, 1)), ('b', '<f8', (1, 3))], (2, 2))) self.check_pickling(dt) + + def test_structured_aligned(self): dt = np.dtype('i4, i1', align=True) self.check_pickling(dt) + + def test_structured_unaligned(self): dt = np.dtype('i4, i1', align=False) self.check_pickling(dt) + + def test_structured_padded(self): dt = np.dtype({ 'names': ['A', 'B'], 'formats': ['f4', 'f4'], 'offsets': [0, 8], 'itemsize': 16}) self.check_pickling(dt) + + def test_structured_titles(self): dt = np.dtype({'names': ['r', 'b'], 'formats': ['u1', 'u1'], 'titles': ['Red pixel', 'Blue pixel']}) self.check_pickling(dt) - def test_datetime(self): - for base in ['m8', 'M8']: - for unit in ['', 'Y', 'M', 'W', 'D', 'h', 'm', 's', 'ms', - 'us', 'ns', 'ps', 'fs', 'as']: - dt = np.dtype('%s[%s]' % (base, unit) if unit else base) - self.check_pickling(dt) - if unit: - dt = np.dtype('%s[7%s]' % (base, unit)) - self.check_pickling(dt) + @pytest.mark.parametrize('base', ['m8', 'M8']) + @pytest.mark.parametrize('unit', ['', 'Y', 'M', 'W', 'D', 'h', 'm', 's', + 'ms', 'us', 'ns', 'ps', 'fs', 'as']) + def test_datetime(self, base, unit): + dt = np.dtype('%s[%s]' % (base, unit) if unit else base) + self.check_pickling(dt) + if unit: + dt = np.dtype('%s[7%s]' % (base, unit)) + self.check_pickling(dt) def test_metadata(self): dt = np.dtype(int, metadata={'datum': 1}) self.check_pickling(dt) + @pytest.mark.parametrize("DType", + [type(np.dtype(t)) for t in np.typecodes['All']] + + [np.dtype(rational), np.dtype]) + def test_pickle_types(self, DType): + # Check that DTypes (the classes/types) roundtrip when pickling + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + roundtrip_DType = pickle.loads(pickle.dumps(DType, proto)) + assert roundtrip_DType is DType + + +class TestPromotion: + """Test cases related to more complex DType promotions. Further promotion + tests are defined in `test_numeric.py` + """ + @pytest.mark.parametrize(["other", "expected"], + [(2**16-1, np.complex64), + (2**32-1, np.complex128), + (np.float16(2), np.complex64), + (np.float32(2), np.complex64), + (np.longdouble(2), np.complex64), + # Base of the double value to sidestep any rounding issues: + (np.longdouble(np.nextafter(1.7e308, 0.)), np.complex128), + # Additionally use "nextafter" so the cast can't round down: + (np.longdouble(np.nextafter(1.7e308, np.inf)), np.clongdouble), + # repeat for complex scalars: + (np.complex64(2), np.complex64), + (np.clongdouble(2), np.complex64), + # Base of the double value to sidestep any rounding issues: + (np.clongdouble(np.nextafter(1.7e308, 0.) * 1j), np.complex128), + # Additionally use "nextafter" so the cast can't round down: + (np.clongdouble(np.nextafter(1.7e308, np.inf)), np.clongdouble), + ]) + def test_complex_other_value_based(self, other, expected): + # This would change if we modify the value based promotion + min_complex = np.dtype(np.complex64) + + res = np.result_type(other, min_complex) + assert res == expected + # Check the same for a simple ufunc call that uses the same logic: + res = np.minimum(other, np.ones(3, dtype=min_complex)).dtype + assert res == expected + + @pytest.mark.parametrize(["other", "expected"], + [(np.bool_, np.complex128), + (np.int64, np.complex128), + (np.float16, np.complex64), + (np.float32, np.complex64), + (np.float64, np.complex128), + (np.longdouble, np.clongdouble), + (np.complex64, np.complex64), + (np.complex128, np.complex128), + (np.clongdouble, np.clongdouble), + ]) + def test_complex_scalar_value_based(self, other, expected): + # This would change if we modify the value based promotion + complex_scalar = 1j + + res = np.result_type(other, complex_scalar) + assert res == expected + # Check the same for a simple ufunc call that uses the same logic: + res = np.minimum(np.ones(3, dtype=other), complex_scalar).dtype + assert res == expected + + def test_complex_pyscalar_promote_rational(self): + with pytest.raises(TypeError, + match=r".* do not have a common DType"): + np.result_type(1j, rational) + + with pytest.raises(TypeError, + match=r".* no common DType exists for the given inputs"): + np.result_type(1j, rational(1, 2)) + + @pytest.mark.parametrize(["other", "expected"], + [(1, rational), (1., np.float64)]) + def test_float_int_pyscalar_promote_rational(self, other, expected): + # Note that rationals are a bit akward as they promote with float64 + # or default ints, but not float16 or uint8/int8 (which looks + # inconsistent here) + with pytest.raises(TypeError, + match=r".* do not have a common DType"): + np.result_type(other, rational) + + assert np.result_type(other, rational(1, 2)) == expected + + @pytest.mark.parametrize(["dtypes", "expected"], [ + # These promotions are not associative/commutative: + ([np.uint16, np.int16, np.float16], np.float32), + ([np.uint16, np.int8, np.float16], np.float32), + ([np.uint8, np.int16, np.float16], np.float32), + # The following promotions are not ambiguous, but cover code + # paths of abstract promotion (no particular logic being tested) + ([1, 1, np.float64], np.float64), + ([1, 1., np.complex128], np.complex128), + ([1, 1j, np.float64], np.complex128), + ([1., 1., np.int64], np.float64), + ([1., 1j, np.float64], np.complex128), + ([1j, 1j, np.float64], np.complex128), + ([1, True, np.bool_], np.int_), + ]) + def test_permutations_do_not_influence_result(self, dtypes, expected): + # Tests that most permutations do not influence the result. In the + # above some uint and int combintations promote to a larger integer + # type, which would then promote to a larger than necessary float. + for perm in permutations(dtypes): + assert np.result_type(*perm) == expected + def test_rational_dtype(): # test for bug gh-5719 @@ -728,3 +1252,363 @@ def test_dtypes_are_true(): def test_invalid_dtype_string(): # test for gh-10440 assert_raises(TypeError, np.dtype, 'f8,i8,[f8,i8]') + assert_raises(TypeError, np.dtype, u'Fl\xfcgel') + + +def test_keyword_argument(): + # test for https://github.com/numpy/numpy/pull/16574#issuecomment-642660971 + assert np.dtype(dtype=np.float64) == np.dtype(np.float64) + + +class TestFromDTypeAttribute: + def test_simple(self): + class dt: + dtype = np.dtype("f8") + + assert np.dtype(dt) == np.float64 + assert np.dtype(dt()) == np.float64 + + @pytest.mark.skipif(IS_PYSTON, reason="Pyston disables recursion checking") + def test_recursion(self): + class dt: + pass + + dt.dtype = dt + with pytest.raises(RecursionError): + np.dtype(dt) + + dt_instance = dt() + dt_instance.dtype = dt + with pytest.raises(RecursionError): + np.dtype(dt_instance) + + def test_void_subtype(self): + class dt(np.void): + # This code path is fully untested before, so it is unclear + # what this should be useful for. Note that if np.void is used + # numpy will think we are deallocating a base type [1.17, 2019-02]. + dtype = np.dtype("f,f") + + np.dtype(dt) + np.dtype(dt(1)) + + @pytest.mark.skipif(IS_PYSTON, reason="Pyston disables recursion checking") + def test_void_subtype_recursion(self): + class vdt(np.void): + pass + + vdt.dtype = vdt + + with pytest.raises(RecursionError): + np.dtype(vdt) + + with pytest.raises(RecursionError): + np.dtype(vdt(1)) + + +class TestDTypeClasses: + @pytest.mark.parametrize("dtype", list(np.typecodes['All']) + [rational]) + def test_basic_dtypes_subclass_properties(self, dtype): + # Note: Except for the isinstance and type checks, these attributes + # are considered currently private and may change. + dtype = np.dtype(dtype) + assert isinstance(dtype, np.dtype) + assert type(dtype) is not np.dtype + assert type(dtype).__name__ == f"dtype[{dtype.type.__name__}]" + assert type(dtype).__module__ == "numpy" + assert not type(dtype)._abstract + + # the flexible dtypes and datetime/timedelta have additional parameters + # which are more than just storage information, these would need to be + # given when creating a dtype: + parametric = (np.void, np.str_, np.bytes_, np.datetime64, np.timedelta64) + if dtype.type not in parametric: + assert not type(dtype)._parametric + assert type(dtype)() is dtype + else: + assert type(dtype)._parametric + with assert_raises(TypeError): + type(dtype)() + + def test_dtype_superclass(self): + assert type(np.dtype) is not type + assert isinstance(np.dtype, type) + + assert type(np.dtype).__name__ == "_DTypeMeta" + assert type(np.dtype).__module__ == "numpy" + assert np.dtype._abstract + + +class TestFromCTypes: + + @staticmethod + def check(ctype, dtype): + dtype = np.dtype(dtype) + assert_equal(np.dtype(ctype), dtype) + assert_equal(np.dtype(ctype()), dtype) + + def test_array(self): + c8 = ctypes.c_uint8 + self.check( 3 * c8, (np.uint8, (3,))) + self.check( 1 * c8, (np.uint8, (1,))) + self.check( 0 * c8, (np.uint8, (0,))) + self.check(1 * (3 * c8), ((np.uint8, (3,)), (1,))) + self.check(3 * (1 * c8), ((np.uint8, (1,)), (3,))) + + def test_padded_structure(self): + class PaddedStruct(ctypes.Structure): + _fields_ = [ + ('a', ctypes.c_uint8), + ('b', ctypes.c_uint16) + ] + expected = np.dtype([ + ('a', np.uint8), + ('b', np.uint16) + ], align=True) + self.check(PaddedStruct, expected) + + def test_bit_fields(self): + class BitfieldStruct(ctypes.Structure): + _fields_ = [ + ('a', ctypes.c_uint8, 7), + ('b', ctypes.c_uint8, 1) + ] + assert_raises(TypeError, np.dtype, BitfieldStruct) + assert_raises(TypeError, np.dtype, BitfieldStruct()) + + def test_pointer(self): + p_uint8 = ctypes.POINTER(ctypes.c_uint8) + assert_raises(TypeError, np.dtype, p_uint8) + + def test_void_pointer(self): + self.check(ctypes.c_void_p, np.uintp) + + def test_union(self): + class Union(ctypes.Union): + _fields_ = [ + ('a', ctypes.c_uint8), + ('b', ctypes.c_uint16), + ] + expected = np.dtype(dict( + names=['a', 'b'], + formats=[np.uint8, np.uint16], + offsets=[0, 0], + itemsize=2 + )) + self.check(Union, expected) + + def test_union_with_struct_packed(self): + class Struct(ctypes.Structure): + _pack_ = 1 + _fields_ = [ + ('one', ctypes.c_uint8), + ('two', ctypes.c_uint32) + ] + + class Union(ctypes.Union): + _fields_ = [ + ('a', ctypes.c_uint8), + ('b', ctypes.c_uint16), + ('c', ctypes.c_uint32), + ('d', Struct), + ] + expected = np.dtype(dict( + names=['a', 'b', 'c', 'd'], + formats=['u1', np.uint16, np.uint32, [('one', 'u1'), ('two', np.uint32)]], + offsets=[0, 0, 0, 0], + itemsize=ctypes.sizeof(Union) + )) + self.check(Union, expected) + + def test_union_packed(self): + class Struct(ctypes.Structure): + _fields_ = [ + ('one', ctypes.c_uint8), + ('two', ctypes.c_uint32) + ] + _pack_ = 1 + class Union(ctypes.Union): + _pack_ = 1 + _fields_ = [ + ('a', ctypes.c_uint8), + ('b', ctypes.c_uint16), + ('c', ctypes.c_uint32), + ('d', Struct), + ] + expected = np.dtype(dict( + names=['a', 'b', 'c', 'd'], + formats=['u1', np.uint16, np.uint32, [('one', 'u1'), ('two', np.uint32)]], + offsets=[0, 0, 0, 0], + itemsize=ctypes.sizeof(Union) + )) + self.check(Union, expected) + + def test_packed_structure(self): + class PackedStructure(ctypes.Structure): + _pack_ = 1 + _fields_ = [ + ('a', ctypes.c_uint8), + ('b', ctypes.c_uint16) + ] + expected = np.dtype([ + ('a', np.uint8), + ('b', np.uint16) + ]) + self.check(PackedStructure, expected) + + def test_large_packed_structure(self): + class PackedStructure(ctypes.Structure): + _pack_ = 2 + _fields_ = [ + ('a', ctypes.c_uint8), + ('b', ctypes.c_uint16), + ('c', ctypes.c_uint8), + ('d', ctypes.c_uint16), + ('e', ctypes.c_uint32), + ('f', ctypes.c_uint32), + ('g', ctypes.c_uint8) + ] + expected = np.dtype(dict( + formats=[np.uint8, np.uint16, np.uint8, np.uint16, np.uint32, np.uint32, np.uint8 ], + offsets=[0, 2, 4, 6, 8, 12, 16], + names=['a', 'b', 'c', 'd', 'e', 'f', 'g'], + itemsize=18)) + self.check(PackedStructure, expected) + + def test_big_endian_structure_packed(self): + class BigEndStruct(ctypes.BigEndianStructure): + _fields_ = [ + ('one', ctypes.c_uint8), + ('two', ctypes.c_uint32) + ] + _pack_ = 1 + expected = np.dtype([('one', 'u1'), ('two', '>u4')]) + self.check(BigEndStruct, expected) + + def test_little_endian_structure_packed(self): + class LittleEndStruct(ctypes.LittleEndianStructure): + _fields_ = [ + ('one', ctypes.c_uint8), + ('two', ctypes.c_uint32) + ] + _pack_ = 1 + expected = np.dtype([('one', 'u1'), ('two', '<u4')]) + self.check(LittleEndStruct, expected) + + def test_little_endian_structure(self): + class PaddedStruct(ctypes.LittleEndianStructure): + _fields_ = [ + ('a', ctypes.c_uint8), + ('b', ctypes.c_uint16) + ] + expected = np.dtype([ + ('a', '<B'), + ('b', '<H') + ], align=True) + self.check(PaddedStruct, expected) + + def test_big_endian_structure(self): + class PaddedStruct(ctypes.BigEndianStructure): + _fields_ = [ + ('a', ctypes.c_uint8), + ('b', ctypes.c_uint16) + ] + expected = np.dtype([ + ('a', '>B'), + ('b', '>H') + ], align=True) + self.check(PaddedStruct, expected) + + def test_simple_endian_types(self): + self.check(ctypes.c_uint16.__ctype_le__, np.dtype('<u2')) + self.check(ctypes.c_uint16.__ctype_be__, np.dtype('>u2')) + self.check(ctypes.c_uint8.__ctype_le__, np.dtype('u1')) + self.check(ctypes.c_uint8.__ctype_be__, np.dtype('u1')) + + all_types = set(np.typecodes['All']) + all_pairs = permutations(all_types, 2) + + @pytest.mark.parametrize("pair", all_pairs) + def test_pairs(self, pair): + """ + Check that np.dtype('x,y') matches [np.dtype('x'), np.dtype('y')] + Example: np.dtype('d,I') -> dtype([('f0', '<f8'), ('f1', '<u4')]) + """ + # gh-5645: check that np.dtype('i,L') can be used + pair_type = np.dtype('{},{}'.format(*pair)) + expected = np.dtype([('f0', pair[0]), ('f1', pair[1])]) + assert_equal(pair_type, expected) + + +class TestUserDType: + @pytest.mark.leaks_references(reason="dynamically creates custom dtype.") + def test_custom_structured_dtype(self): + class mytype: + pass + + blueprint = np.dtype([("field", object)]) + dt = create_custom_field_dtype(blueprint, mytype, 0) + assert dt.type == mytype + # We cannot (currently) *create* this dtype with `np.dtype` because + # mytype does not inherit from `np.generic`. This seems like an + # unnecessary restriction, but one that has been around forever: + assert np.dtype(mytype) == np.dtype("O") + + def test_custom_structured_dtype_errors(self): + class mytype: + pass + + blueprint = np.dtype([("field", object)]) + + with pytest.raises(ValueError): + # Tests what happens if fields are unset during creation + # which is currently rejected due to the containing object + # (see PyArray_RegisterDataType). + create_custom_field_dtype(blueprint, mytype, 1) + + with pytest.raises(RuntimeError): + # Tests that a dtype must have its type field set up to np.dtype + # or in this case a builtin instance. + create_custom_field_dtype(blueprint, mytype, 2) + + +@pytest.mark.skipif(sys.version_info < (3, 9), reason="Requires python 3.9") +class TestClassGetItem: + def test_dtype(self) -> None: + alias = np.dtype[Any] + assert isinstance(alias, types.GenericAlias) + assert alias.__origin__ is np.dtype + + @pytest.mark.parametrize("code", np.typecodes["All"]) + def test_dtype_subclass(self, code: str) -> None: + cls = type(np.dtype(code)) + alias = cls[Any] + assert isinstance(alias, types.GenericAlias) + assert alias.__origin__ is cls + + @pytest.mark.parametrize("arg_len", range(4)) + def test_subscript_tuple(self, arg_len: int) -> None: + arg_tup = (Any,) * arg_len + if arg_len == 1: + assert np.dtype[arg_tup] + else: + with pytest.raises(TypeError): + np.dtype[arg_tup] + + def test_subscript_scalar(self) -> None: + assert np.dtype[Any] + + +def test_result_type_integers_and_unitless_timedelta64(): + # Regression test for gh-20077. The following call of `result_type` + # would cause a seg. fault. + td = np.timedelta64(4) + result = np.result_type(0, td) + assert_dtype_equal(result, td.dtype) + + +@pytest.mark.skipif(sys.version_info >= (3, 9), reason="Requires python 3.8") +def test_class_getitem_38() -> None: + match = "Type subscription requires python >= 3.9" + with pytest.raises(TypeError, match=match): + np.dtype[Any] diff --git a/numpy/core/tests/test_einsum.py b/numpy/core/tests/test_einsum.py index 647738831594..172311624c27 100644 --- a/numpy/core/tests/test_einsum.py +++ b/numpy/core/tests/test_einsum.py @@ -1,22 +1,20 @@ -from __future__ import division, absolute_import, print_function - import itertools +import pytest + import numpy as np from numpy.testing import ( assert_, assert_equal, assert_array_equal, assert_almost_equal, - assert_raises, suppress_warnings + assert_raises, suppress_warnings, assert_raises_regex, assert_allclose ) # Setup for optimize einsum chars = 'abcdefghij' sizes = np.array([2, 3, 4, 5, 4, 3, 2, 6, 5, 4, 3]) -global_size_dict = {} -for size, char in zip(sizes, chars): - global_size_dict[char] = size +global_size_dict = dict(zip(chars, sizes)) -class TestEinSum(object): +class TestEinsum: def test_einsum_errors(self): for do_opt in [True, False]: # Need enough arguments @@ -31,7 +29,7 @@ def test_einsum_errors(self): optimize=do_opt) # order parameter must be a valid order - assert_raises(TypeError, np.einsum, "", 0, order='W', + assert_raises(ValueError, np.einsum, "", 0, order='W', optimize=do_opt) # casting parameter must be a valid casting @@ -92,6 +90,15 @@ def test_einsum_errors(self): optimize=do_opt) assert_raises(ValueError, np.einsum, "i->i", [[0, 1], [0, 1]], out=np.arange(4).reshape(2, 2), optimize=do_opt) + with assert_raises_regex(ValueError, "'b'"): + # gh-11221 - 'c' erroneously appeared in the error message + a = np.ones((3, 3, 4, 5, 6)) + b = np.ones((3, 4, 5)) + np.einsum('aabcb,abc', a, b) + + # Check order kwarg, asanyarray allows 1d to pass through + assert_raises(ValueError, np.einsum, "i->i", np.arange(6).reshape(-1, 1), + optimize=do_opt, order='d') def test_einsum_views(self): # pass-through @@ -273,6 +280,13 @@ def check_einsum_sums(self, dtype, do_opt=False): assert_equal(np.einsum(a, [0, 0], optimize=do_opt), np.trace(a).astype(dtype)) + # gh-15961: should accept numpy int64 type in subscript list + np_array = np.asarray([0, 0]) + assert_equal(np.einsum(a, np_array, optimize=do_opt), + np.trace(a).astype(dtype)) + assert_equal(np.einsum(a, list(np_array), optimize=do_opt), + np.trace(a).astype(dtype)) + # multiply(a, b) assert_equal(np.einsum("..., ...", 3, 4), 12) # scalar case for n in range(1, 17): @@ -604,6 +618,10 @@ def test_einsum_misc(self): [[[1, 3], [3, 9], [5, 15], [7, 21]], [[8, 16], [16, 32], [24, 48], [32, 64]]]) + # Ensure explicitly setting out=None does not cause an error + # see issue gh-15776 and issue gh-15256 + assert_equal(np.einsum('i,j', [1], [2], out=None), [[2]]) + def test_subscript_range(self): # Issue #7741, make sure that all letters of Latin alphabet (both uppercase & lowercase) can be used # when creating a subscript from arrays @@ -614,7 +632,7 @@ def test_subscript_range(self): np.einsum(a, [0, 51], b, [51, 2], [0, 2], optimize=False) assert_raises(ValueError, lambda: np.einsum(a, [0, 52], b, [52, 2], [0, 2], optimize=False)) assert_raises(ValueError, lambda: np.einsum(a, [-1, 5], b, [5, 2], [-1, 2], optimize=False)) - + def test_einsum_broadcast(self): # Issue #2455 change in handling ellipsis # remove the 'middle broadcast' error @@ -697,6 +715,14 @@ def test_einsum_fixed_collapsingbug(self): y2 = x[idx[:, None], idx[:, None], idx, idx] assert_equal(y1, y2) + def test_einsum_failed_on_p9_and_s390x(self): + # Issues gh-14692 and gh-12689 + # Bug with signed vs unsigned char errored on power9 and s390x Linux + tensor = np.random.random_sample((10, 10, 10, 10)) + x = np.einsum('ijij->', tensor) + y = tensor.trace(axis1=0, axis2=2).trace() + assert_allclose(x, y) + def test_einsum_all_contig_non_contig_output(self): # Issue gh-5907, tests that the all contiguous special case # actually checks the contiguity of the output @@ -720,6 +746,52 @@ def test_einsum_all_contig_non_contig_output(self): np.einsum('ij,jk->ik', x, x, out=out) assert_array_equal(out.base, correct_base) + @pytest.mark.parametrize("dtype", + np.typecodes["AllFloat"] + np.typecodes["AllInteger"]) + def test_different_paths(self, dtype): + # Test originally added to cover broken float16 path: gh-20305 + # Likely most are covered elsewhere, at least partially. + dtype = np.dtype(dtype) + # Simple test, designed to excersize most specialized code paths, + # note the +0.5 for floats. This makes sure we use a float value + # where the results must be exact. + arr = (np.arange(7) + 0.5).astype(dtype) + scalar = np.array(2, dtype=dtype) + + # contig -> scalar: + res = np.einsum('i->', arr) + assert res == arr.sum() + # contig, contig -> contig: + res = np.einsum('i,i->i', arr, arr) + assert_array_equal(res, arr * arr) + # noncontig, noncontig -> contig: + res = np.einsum('i,i->i', arr.repeat(2)[::2], arr.repeat(2)[::2]) + assert_array_equal(res, arr * arr) + # contig + contig -> scalar + assert np.einsum('i,i->', arr, arr) == (arr * arr).sum() + # contig + scalar -> contig (with out) + out = np.ones(7, dtype=dtype) + res = np.einsum('i,->i', arr, dtype.type(2), out=out) + assert_array_equal(res, arr * dtype.type(2)) + # scalar + contig -> contig (with out) + res = np.einsum(',i->i', scalar, arr) + assert_array_equal(res, arr * dtype.type(2)) + # scalar + contig -> scalar + res = np.einsum(',i->', scalar, arr) + # Use einsum to compare to not have difference due to sum round-offs: + assert res == np.einsum('i->', scalar * arr) + # contig + scalar -> scalar + res = np.einsum('i,->', arr, scalar) + # Use einsum to compare to not have difference due to sum round-offs: + assert res == np.einsum('i->', scalar * arr) + # contig + contig + contig -> scalar + arr = np.array([0.5, 0.5, 0.25, 4.5, 3.], dtype=dtype) + res = np.einsum('i,i,i->', arr, arr, arr) + assert_array_equal(res, (arr * arr * arr).sum()) + # four arrays: + res = np.einsum('i,i,i,i->', arr, arr, arr, arr) + assert_array_equal(res, (arr * arr * arr * arr).sum()) + def test_small_boolean_arrays(self): # See gh-5946. # Use array of True embedded in False. @@ -730,19 +802,27 @@ def test_small_boolean_arrays(self): res = np.einsum('...ij,...jk->...ik', a, a, out=out) assert_equal(res, tgt) - def optimize_compare(self, string): + def test_out_is_res(self): + a = np.arange(9).reshape(3, 3) + res = np.einsum('...ij,...jk->...ik', a, a, out=a) + assert res is a + + def optimize_compare(self, subscripts, operands=None): # Tests all paths of the optimization function against # conventional einsum - operands = [string] - terms = string.split('->')[0].split(',') - for term in terms: - dims = [global_size_dict[x] for x in term] - operands.append(np.random.rand(*dims)) - - noopt = np.einsum(*operands, optimize=False) - opt = np.einsum(*operands, optimize='greedy') + if operands is None: + args = [subscripts] + terms = subscripts.split('->')[0].split(',') + for term in terms: + dims = [global_size_dict[x] for x in term] + args.append(np.random.rand(*dims)) + else: + args = [subscripts] + operands + + noopt = np.einsum(*args, optimize=False) + opt = np.einsum(*args, optimize='greedy') assert_almost_equal(opt, noopt) - opt = np.einsum(*operands, optimize='optimal') + opt = np.einsum(*args, optimize='optimal') assert_almost_equal(opt, noopt) def test_hadamard_like_products(self): @@ -828,8 +908,63 @@ def test_combined_views_mapping(self): b = np.einsum('bbcdc->d', a) assert_equal(b, [12]) + def test_broadcasting_dot_cases(self): + # Ensures broadcasting cases are not mistaken for GEMM + + a = np.random.rand(1, 5, 4) + b = np.random.rand(4, 6) + c = np.random.rand(5, 6) + d = np.random.rand(10) -class TestEinSumPath(object): + self.optimize_compare('ijk,kl,jl', operands=[a, b, c]) + self.optimize_compare('ijk,kl,jl,i->i', operands=[a, b, c, d]) + + e = np.random.rand(1, 1, 5, 4) + f = np.random.rand(7, 7) + self.optimize_compare('abjk,kl,jl', operands=[e, b, c]) + self.optimize_compare('abjk,kl,jl,ab->ab', operands=[e, b, c, f]) + + # Edge case found in gh-11308 + g = np.arange(64).reshape(2, 4, 8) + self.optimize_compare('obk,ijk->ioj', operands=[g, g]) + + def test_output_order(self): + # Ensure output order is respected for optimize cases, the below + # conraction should yield a reshaped tensor view + # gh-16415 + + a = np.ones((2, 3, 5), order='F') + b = np.ones((4, 3), order='F') + + for opt in [True, False]: + tmp = np.einsum('...ft,mf->...mt', a, b, order='a', optimize=opt) + assert_(tmp.flags.f_contiguous) + + tmp = np.einsum('...ft,mf->...mt', a, b, order='f', optimize=opt) + assert_(tmp.flags.f_contiguous) + + tmp = np.einsum('...ft,mf->...mt', a, b, order='c', optimize=opt) + assert_(tmp.flags.c_contiguous) + + tmp = np.einsum('...ft,mf->...mt', a, b, order='k', optimize=opt) + assert_(tmp.flags.c_contiguous is False) + assert_(tmp.flags.f_contiguous is False) + + tmp = np.einsum('...ft,mf->...mt', a, b, optimize=opt) + assert_(tmp.flags.c_contiguous is False) + assert_(tmp.flags.f_contiguous is False) + + c = np.ones((4, 3), order='C') + for opt in [True, False]: + tmp = np.einsum('...ft,mf->...mt', a, c, order='a', optimize=opt) + assert_(tmp.flags.c_contiguous) + + d = np.ones((2, 3, 5), order='C') + for opt in [True, False]: + tmp = np.einsum('...ft,mf->...mt', d, c, order='a', optimize=opt) + assert_(tmp.flags.c_contiguous) + +class TestEinsumPath: def build_operands(self, string, size_dict=global_size_dict): # Builds views based off initial operands @@ -875,7 +1010,7 @@ def test_long_paths(self): long_test1 = self.build_operands('acdf,jbje,gihb,hfac,gfac,gifabc,hfac') path, path_str = np.einsum_path(*long_test1, optimize='greedy') self.assert_path_equal(path, ['einsum_path', - (1, 4), (2, 4), (1, 4), (1, 3), (1, 2), (0, 1)]) + (3, 6), (3, 4), (2, 4), (2, 3), (0, 2), (0, 1)]) path, path_str = np.einsum_path(*long_test1, optimize='optimal') self.assert_path_equal(path, ['einsum_path', @@ -884,10 +1019,12 @@ def test_long_paths(self): # Long test 2 long_test2 = self.build_operands('chd,bde,agbc,hiad,bdi,cgh,agdb') path, path_str = np.einsum_path(*long_test2, optimize='greedy') + print(path) self.assert_path_equal(path, ['einsum_path', (3, 4), (0, 3), (3, 4), (1, 3), (1, 2), (0, 1)]) path, path_str = np.einsum_path(*long_test2, optimize='optimal') + print(path) self.assert_path_equal(path, ['einsum_path', (0, 5), (1, 4), (3, 4), (1, 3), (1, 2), (0, 1)]) @@ -921,7 +1058,7 @@ def test_edge_paths(self): # Edge test4 edge_test4 = self.build_operands('dcc,fce,ea,dbf->ab') path, path_str = np.einsum_path(*edge_test4, optimize='greedy') - self.assert_path_equal(path, ['einsum_path', (0, 3), (0, 2), (0, 1)]) + self.assert_path_equal(path, ['einsum_path', (1, 2), (0, 1), (0, 1)]) path, path_str = np.einsum_path(*edge_test4, optimize='optimal') self.assert_path_equal(path, ['einsum_path', (1, 2), (0, 2), (0, 1)]) @@ -935,16 +1072,15 @@ def test_edge_paths(self): path, path_str = np.einsum_path(*edge_test4, optimize='optimal') self.assert_path_equal(path, ['einsum_path', (0, 1), (0, 1, 2, 3, 4, 5)]) - def test_path_type_input(self): - # Test explicit path handeling + # Test explicit path handling path_test = self.build_operands('dcc,fce,ea,dbf->ab') path, path_str = np.einsum_path(*path_test, optimize=False) self.assert_path_equal(path, ['einsum_path', (0, 1, 2, 3)]) path, path_str = np.einsum_path(*path_test, optimize=True) - self.assert_path_equal(path, ['einsum_path', (0, 3), (0, 2), (0, 1)]) + self.assert_path_equal(path, ['einsum_path', (1, 2), (0, 1), (0, 1)]) exp_path = ['einsum_path', (0, 2), (0, 2), (0, 1)] path, path_str = np.einsum_path(*path_test, optimize=exp_path) diff --git a/numpy/core/tests/test_errstate.py b/numpy/core/tests/test_errstate.py index 4f611192177b..184a373002fa 100644 --- a/numpy/core/tests/test_errstate.py +++ b/numpy/core/tests/test_errstate.py @@ -1,14 +1,19 @@ -from __future__ import division, absolute_import, print_function - -import platform import pytest +import sysconfig import numpy as np -from numpy.testing import assert_ +from numpy.testing import assert_, assert_raises +# The floating point emulation on ARM EABI systems lacking a hardware FPU is +# known to be buggy. This is an attempt to identify these hosts. It may not +# catch all possible cases, but it catches the known cases of gh-413 and +# gh-15562. +hosttype = sysconfig.get_config_var('HOST_GNU_TYPE') +arm_softfloat = False if hosttype is None else hosttype.endswith('gnueabi') -class TestErrstate(object): - @pytest.mark.skipif(platform.machine() == "armv5tel", reason="See gh-413.") +class TestErrstate: + @pytest.mark.skipif(arm_softfloat, + reason='platform/cpu issue with FPU (gh-413,-15562)') def test_invalid(self): with np.errstate(all='raise', under='ignore'): a = -np.arange(3) @@ -16,13 +21,11 @@ def test_invalid(self): with np.errstate(invalid='ignore'): np.sqrt(a) # While this should fail! - try: + with assert_raises(FloatingPointError): np.sqrt(a) - except FloatingPointError: - pass - else: - self.fail("Did not raise an invalid error") + @pytest.mark.skipif(arm_softfloat, + reason='platform/cpu issue with FPU (gh-15562)') def test_divide(self): with np.errstate(all='raise', under='ignore'): a = -np.arange(3) @@ -30,12 +33,11 @@ def test_divide(self): with np.errstate(divide='ignore'): a // 0 # While this should fail! - try: + with assert_raises(FloatingPointError): a // 0 - except FloatingPointError: - pass - else: - self.fail("Did not raise divide by zero error") + # As should this, see gh-15562 + with assert_raises(FloatingPointError): + a // a def test_errcall(self): def foo(*args): @@ -47,3 +49,11 @@ def foo(*args): with np.errstate(call=None): assert_(np.geterrcall() is None, 'call is not None') assert_(np.geterrcall() is olderrcall, 'call is not olderrcall') + + def test_errstate_decorator(self): + @np.errstate(all='ignore') + def foo(): + a = -np.arange(3) + a // 0 + + foo() diff --git a/numpy/core/tests/test_extint128.py b/numpy/core/tests/test_extint128.py index 0e9c07fd5231..3b64915f36a3 100644 --- a/numpy/core/tests/test_extint128.py +++ b/numpy/core/tests/test_extint128.py @@ -1,6 +1,3 @@ -from __future__ import division, absolute_import, print_function - -import sys import itertools import contextlib import operator @@ -8,7 +5,6 @@ import numpy as np import numpy.core._multiarray_tests as mt -from numpy.compat import long from numpy.testing import assert_raises, assert_equal diff --git a/numpy/core/tests/test_function_base.py b/numpy/core/tests/test_function_base.py index d0ff1c15f31c..dad7a58835f9 100644 --- a/numpy/core/tests/test_function_base.py +++ b/numpy/core/tests/test_function_base.py @@ -1,12 +1,9 @@ -from __future__ import division, absolute_import, print_function - from numpy import ( logspace, linspace, geomspace, dtype, array, sctypes, arange, isnan, - ndarray, sqrt, nextafter + ndarray, sqrt, nextafter, stack, errstate ) from numpy.testing import ( assert_, assert_equal, assert_raises, assert_array_equal, assert_allclose, - suppress_warnings ) @@ -42,18 +39,32 @@ class PhysicalQuantity2(ndarray): __array_priority__ = 10 -class TestLogspace(object): +class TestLogspace: def test_basic(self): y = logspace(0, 6) assert_(len(y) == 50) y = logspace(0, 6, num=100) assert_(y[-1] == 10 ** 6) - y = logspace(0, 6, endpoint=0) + y = logspace(0, 6, endpoint=False) assert_(y[-1] < 10 ** 6) y = logspace(0, 6, num=7) assert_array_equal(y, [1, 10, 100, 1e3, 1e4, 1e5, 1e6]) + def test_start_stop_array(self): + start = array([0., 1.]) + stop = array([6., 7.]) + t1 = logspace(start, stop, 6) + t2 = stack([logspace(_start, _stop, 6) + for _start, _stop in zip(start, stop)], axis=1) + assert_equal(t1, t2) + t3 = logspace(start, stop[0], 6) + t4 = stack([logspace(_start, stop[0], 6) + for _start in start], axis=1) + assert_equal(t3, t4) + t5 = logspace(start, stop, 6, axis=-1) + assert_equal(t5, t2.T) + def test_dtype(self): y = logspace(0, 6, dtype='float32') assert_equal(y.dtype, dtype('float32')) @@ -78,7 +89,7 @@ def test_subclass(self): assert_equal(ls, logspace(1.0, 7.0, 1)) -class TestGeomspace(object): +class TestGeomspace: def test_basic(self): y = geomspace(1, 1e6) @@ -102,6 +113,40 @@ def test_basic(self): assert_array_equal(y, [-100, -10, -1]) assert_array_equal(y.imag, 0) + def test_boundaries_match_start_and_stop_exactly(self): + # make sure that the boundaries of the returned array exactly + # equal 'start' and 'stop' - this isn't obvious because + # np.exp(np.log(x)) isn't necessarily exactly equal to x + start = 0.3 + stop = 20.3 + + y = geomspace(start, stop, num=1) + assert_equal(y[0], start) + + y = geomspace(start, stop, num=1, endpoint=False) + assert_equal(y[0], start) + + y = geomspace(start, stop, num=3) + assert_equal(y[0], start) + assert_equal(y[-1], stop) + + y = geomspace(start, stop, num=3, endpoint=False) + assert_equal(y[0], start) + + def test_nan_interior(self): + with errstate(invalid='ignore'): + y = geomspace(-3, 3, num=4) + + assert_equal(y[0], -3.0) + assert_(isnan(y[1:-1]).all()) + assert_equal(y[3], 3.0) + + with errstate(invalid='ignore'): + y = geomspace(-3, 3, num=4, endpoint=False) + + assert_equal(y[0], -3.0) + assert_(isnan(y[1:]).all()) + def test_complex(self): # Purely imaginary y = geomspace(1j, 16j, num=5) @@ -156,7 +201,7 @@ def test_dtype(self): y = geomspace(1, 1e6, dtype=complex) assert_equal(y.dtype, dtype('complex')) - def test_array_scalar(self): + def test_start_stop_array_scalar(self): lim1 = array([120, 100], dtype="int8") lim2 = array([-120, -100], dtype="int8") lim3 = array([1200, 1000], dtype="uint16") @@ -172,6 +217,21 @@ def test_array_scalar(self): assert_allclose(t2, t5, rtol=1e-2) assert_allclose(t3, t6, rtol=1e-5) + def test_start_stop_array(self): + # Try to use all special cases. + start = array([1.e0, 32., 1j, -4j, 1+1j, -1]) + stop = array([1.e4, 2., 16j, -324j, 10000+10000j, 1]) + t1 = geomspace(start, stop, 5) + t2 = stack([geomspace(_start, _stop, 5) + for _start, _stop in zip(start, stop)], axis=1) + assert_equal(t1, t2) + t3 = geomspace(start, stop[0], 5) + t4 = stack([geomspace(_start, stop[0], 5) + for _start in start], axis=1) + assert_equal(t3, t4) + t5 = geomspace(start, stop, 5, axis=-1) + assert_equal(t5, t2.T) + def test_physical_quantities(self): a = PhysicalQuantity(1.0) b = PhysicalQuantity(5.0) @@ -193,24 +253,21 @@ def test_bounds(self): assert_raises(ValueError, geomspace, 0, 0) -class TestLinspace(object): +class TestLinspace: def test_basic(self): y = linspace(0, 10) assert_(len(y) == 50) y = linspace(2, 10, num=100) assert_(y[-1] == 10) - y = linspace(2, 10, endpoint=0) + y = linspace(2, 10, endpoint=False) assert_(y[-1] < 10) assert_raises(ValueError, linspace, 0, 10, num=-1) def test_corner(self): y = list(linspace(0, 1, 1)) assert_(y == [0.0], y) - with suppress_warnings() as sup: - sup.filter(DeprecationWarning, ".*safely interpreted as an integer") - y = list(linspace(0, 1, 2.5)) - assert_(y == [0.0, 1.0]) + assert_raises(TypeError, linspace, 0, 1, num=2.5) def test_type(self): t1 = linspace(0, 1, 0).dtype @@ -227,7 +284,7 @@ def test_dtype(self): y = linspace(0, 6, dtype='int32') assert_equal(y.dtype, dtype('int32')) - def test_array_scalar(self): + def test_start_stop_array_scalar(self): lim1 = array([-120, 100], dtype="int8") lim2 = array([120, -100], dtype="int8") lim3 = array([1200, 1000], dtype="uint16") @@ -241,6 +298,20 @@ def test_array_scalar(self): assert_equal(t2, t5) assert_equal(t3, t6) + def test_start_stop_array(self): + start = array([-120, 120], dtype="int8") + stop = array([100, -100], dtype="int8") + t1 = linspace(start, stop, 5) + t2 = stack([linspace(_start, _stop, 5) + for _start, _stop in zip(start, stop)], axis=1) + assert_equal(t1, t2) + t3 = linspace(start, stop[0], 5) + t4 = stack([linspace(_start, stop[0], 5) + for _start in start], axis=1) + assert_equal(t3, t4) + t5 = linspace(start, stop, 5, axis=-1) + assert_equal(t5, t2.T) + def test_complex(self): lim1 = linspace(1 + 2j, 3 + 4j, 5) t1 = array([1.0+2.j, 1.5+2.5j, 2.0+3j, 2.5+3.5j, 3.0+4j]) @@ -269,7 +340,7 @@ def test_array_interface(self): # Ensure that start/stop can be objects that implement # __array_interface__ and are convertible to numeric scalars - class Arrayish(object): + class Arrayish: """ A generic object that supports the __array_interface__ and hence can in principle be converted to a numeric scalar, but is not @@ -285,9 +356,7 @@ def __init__(self, data): @property def __array_interface__(self): - # Ideally should be `'shape': ()` but the current interface - # does not allow that - return {'shape': (1,), 'typestr': '<i4', 'data': self._data, + return {'shape': (), 'typestr': '<i4', 'data': self._data, 'version': 3} def __mul__(self, other): @@ -313,11 +382,28 @@ def test_equivalent_to_arange(self): arange(j+1, dtype=int)) def test_retstep(self): - y = linspace(0, 1, 2, retstep=True) - assert_(isinstance(y, tuple) and len(y) == 2) - for num in (0, 1): - for ept in (False, True): + for num in [0, 1, 2]: + for ept in [False, True]: y = linspace(0, 1, num, endpoint=ept, retstep=True) - assert_(isinstance(y, tuple) and len(y) == 2 and - len(y[0]) == num and isnan(y[1]), - 'num={0}, endpoint={1}'.format(num, ept)) + assert isinstance(y, tuple) and len(y) == 2 + if num == 2: + y0_expect = [0.0, 1.0] if ept else [0.0, 0.5] + assert_array_equal(y[0], y0_expect) + assert_equal(y[1], y0_expect[1]) + elif num == 1 and not ept: + assert_array_equal(y[0], [0.0]) + assert_equal(y[1], 1.0) + else: + assert_array_equal(y[0], [0.0][:num]) + assert isnan(y[1]) + + def test_object(self): + start = array(1, dtype='O') + stop = array(2, dtype='O') + y = linspace(start, stop, 3) + assert_array_equal(y, array([1., 1.5, 2.])) + + def test_round_negative(self): + y = linspace(-1, 3, num=8, dtype=int) + t = array([-1, -1, 0, 0, 1, 1, 2, 3], dtype=int) + assert_array_equal(y, t) diff --git a/numpy/core/tests/test_getlimits.py b/numpy/core/tests/test_getlimits.py index ca8093c624f5..c5148db2c715 100644 --- a/numpy/core/tests/test_getlimits.py +++ b/numpy/core/tests/test_getlimits.py @@ -1,63 +1,60 @@ """ Test functions for limits module. """ -from __future__ import division, absolute_import, print_function - +import warnings import numpy as np from numpy.core import finfo, iinfo from numpy import half, single, double, longdouble from numpy.testing import assert_equal, assert_, assert_raises -from numpy.core.getlimits import ( - _discovered_machar, _float16_ma, _float32_ma, _float64_ma, _float128_ma, - _float80_ma - ) +from numpy.core.getlimits import _discovered_machar, _float_ma ################################################## -class TestPythonFloat(object): +class TestPythonFloat: def test_singleton(self): ftype = finfo(float) ftype2 = finfo(float) assert_equal(id(ftype), id(ftype2)) -class TestHalf(object): +class TestHalf: def test_singleton(self): ftype = finfo(half) ftype2 = finfo(half) assert_equal(id(ftype), id(ftype2)) -class TestSingle(object): +class TestSingle: def test_singleton(self): ftype = finfo(single) ftype2 = finfo(single) assert_equal(id(ftype), id(ftype2)) -class TestDouble(object): +class TestDouble: def test_singleton(self): ftype = finfo(double) ftype2 = finfo(double) assert_equal(id(ftype), id(ftype2)) -class TestLongdouble(object): +class TestLongdouble: def test_singleton(self): ftype = finfo(longdouble) ftype2 = finfo(longdouble) assert_equal(id(ftype), id(ftype2)) -class TestFinfo(object): +class TestFinfo: def test_basic(self): dts = list(zip(['f2', 'f4', 'f8', 'c8', 'c16'], [np.float16, np.float32, np.float64, np.complex64, np.complex128])) for dt1, dt2 in dts: - for attr in ('bits', 'eps', 'epsneg', 'iexp', 'machar', 'machep', + for attr in ('bits', 'eps', 'epsneg', 'iexp', 'machep', 'max', 'maxexp', 'min', 'minexp', 'negep', 'nexp', - 'nmant', 'precision', 'resolution', 'tiny'): + 'nmant', 'precision', 'resolution', 'tiny', + 'smallest_normal', 'smallest_subnormal'): assert_equal(getattr(finfo(dt1), attr), getattr(finfo(dt2), attr), attr) assert_raises(ValueError, finfo, 'i4') -class TestIinfo(object): +class TestIinfo: def test_basic(self): dts = list(zip(['i1', 'i2', 'i4', 'i8', 'u1', 'u2', 'u4', 'u8'], @@ -74,7 +71,7 @@ def test_unsigned_max(self): for T in types: assert_equal(iinfo(T).max, T(-1)) -class TestRepr(object): +class TestRepr: def test_iinfo_repr(self): expected = "iinfo(min=-32768, max=32767, dtype=int16)" assert_equal(repr(np.iinfo(np.int16)), expected) @@ -101,9 +98,9 @@ def assert_ma_equal(discovered, ma_like): def test_known_types(): # Test we are correctly compiling parameters for known types - for ftype, ma_like in ((np.float16, _float16_ma), - (np.float32, _float32_ma), - (np.float64, _float64_ma)): + for ftype, ma_like in ((np.float16, _float_ma[16]), + (np.float32, _float_ma[32]), + (np.float64, _float_ma[64])): assert_ma_equal(_discovered_machar(ftype), ma_like) # Suppress warning for broken discovery of double double on PPC with np.errstate(all='ignore'): @@ -111,10 +108,32 @@ def test_known_types(): bytes = np.dtype(np.longdouble).itemsize if (ld_ma.it, ld_ma.maxexp) == (63, 16384) and bytes in (12, 16): # 80-bit extended precision - assert_ma_equal(ld_ma, _float80_ma) + assert_ma_equal(ld_ma, _float_ma[80]) elif (ld_ma.it, ld_ma.maxexp) == (112, 16384) and bytes == 16: # IEE 754 128-bit - assert_ma_equal(ld_ma, _float128_ma) + assert_ma_equal(ld_ma, _float_ma[128]) + + +def test_subnormal_warning(): + """Test that the subnormal is zero warning is not being raised.""" + with np.errstate(all='ignore'): + ld_ma = _discovered_machar(np.longdouble) + bytes = np.dtype(np.longdouble).itemsize + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter('always') + if (ld_ma.it, ld_ma.maxexp) == (63, 16384) and bytes in (12, 16): + # 80-bit extended precision + ld_ma.smallest_subnormal + assert len(w) == 0 + elif (ld_ma.it, ld_ma.maxexp) == (112, 16384) and bytes == 16: + # IEE 754 128-bit + ld_ma.smallest_subnormal + assert len(w) == 0 + else: + # Double double + ld_ma.smallest_subnormal + # This test may fail on some platforms + assert len(w) == 0 def test_plausible_finfo(): diff --git a/numpy/core/tests/test_half.py b/numpy/core/tests/test_half.py index b02f6cae2ed4..1b6fd21e14bb 100644 --- a/numpy/core/tests/test_half.py +++ b/numpy/core/tests/test_half.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - import platform import pytest @@ -18,7 +16,7 @@ def assert_raises_fpe(strmatch, callable, *args, **kwargs): assert_(False, "Did not raise floating point %s error" % strmatch) -class TestHalf(object): +class TestHalf: def setup(self): # An array of all possible float16 values self.all_f16 = np.arange(0x10000, dtype=uint16) @@ -69,6 +67,100 @@ def test_half_conversions(self): j = np.array(i_f16, dtype=int) assert_equal(i_int, j) + @pytest.mark.parametrize("string_dt", ["S", "U"]) + def test_half_conversion_to_string(self, string_dt): + # Currently uses S/U32 (which is sufficient for float32) + expected_dt = np.dtype(f"{string_dt}32") + assert np.promote_types(np.float16, string_dt) == expected_dt + assert np.promote_types(string_dt, np.float16) == expected_dt + + arr = np.ones(3, dtype=np.float16).astype(string_dt) + assert arr.dtype == expected_dt + + @pytest.mark.parametrize("string_dt", ["S", "U"]) + def test_half_conversion_from_string(self, string_dt): + string = np.array("3.1416", dtype=string_dt) + assert string.astype(np.float16) == np.array(3.1416, dtype=np.float16) + + @pytest.mark.parametrize("offset", [None, "up", "down"]) + @pytest.mark.parametrize("shift", [None, "up", "down"]) + @pytest.mark.parametrize("float_t", [np.float32, np.float64]) + def test_half_conversion_rounding(self, float_t, shift, offset): + # Assumes that round to even is used during casting. + max_pattern = np.float16(np.finfo(np.float16).max).view(np.uint16) + + # Test all (positive) finite numbers, denormals are most interesting + # however: + f16s_patterns = np.arange(0, max_pattern+1, dtype=np.uint16) + f16s_float = f16s_patterns.view(np.float16).astype(float_t) + + # Shift the values by half a bit up or a down (or do not shift), + if shift == "up": + f16s_float = 0.5 * (f16s_float[:-1] + f16s_float[1:])[1:] + elif shift == "down": + f16s_float = 0.5 * (f16s_float[:-1] + f16s_float[1:])[:-1] + else: + f16s_float = f16s_float[1:-1] + + # Increase the float by a minimal value: + if offset == "up": + f16s_float = np.nextafter(f16s_float, float_t(1e50)) + elif offset == "down": + f16s_float = np.nextafter(f16s_float, float_t(-1e50)) + + # Convert back to float16 and its bit pattern: + res_patterns = f16s_float.astype(np.float16).view(np.uint16) + + # The above calculations tries the original values, or the exact + # mid points between the float16 values. It then further offsets them + # by as little as possible. If no offset occurs, "round to even" + # logic will be necessary, an arbitrarily small offset should cause + # normal up/down rounding always. + + # Calculate the expected pattern: + cmp_patterns = f16s_patterns[1:-1].copy() + + if shift == "down" and offset != "up": + shift_pattern = -1 + elif shift == "up" and offset != "down": + shift_pattern = 1 + else: + # There cannot be a shift, either shift is None, so all rounding + # will go back to original, or shift is reduced by offset too much. + shift_pattern = 0 + + # If rounding occurs, is it normal rounding or round to even? + if offset is None: + # Round to even occurs, modify only non-even, cast to allow + (-1) + cmp_patterns[0::2].view(np.int16)[...] += shift_pattern + else: + cmp_patterns.view(np.int16)[...] += shift_pattern + + assert_equal(res_patterns, cmp_patterns) + + @pytest.mark.parametrize(["float_t", "uint_t", "bits"], + [(np.float32, np.uint32, 23), + (np.float64, np.uint64, 52)]) + def test_half_conversion_denormal_round_even(self, float_t, uint_t, bits): + # Test specifically that all bits are considered when deciding + # whether round to even should occur (i.e. no bits are lost at the + # end. Compare also gh-12721. The most bits can get lost for the + # smallest denormal: + smallest_value = np.uint16(1).view(np.float16).astype(float_t) + assert smallest_value == 2**-24 + + # Will be rounded to zero based on round to even rule: + rounded_to_zero = smallest_value / float_t(2) + assert rounded_to_zero.astype(np.float16) == 0 + + # The significand will be all 0 for the float_t, test that we do not + # lose the lower ones of these: + for i in range(bits): + # slightly increasing the value should make it round up: + larger_pattern = rounded_to_zero.view(uint_t) | uint_t(1 << i) + larger_value = larger_pattern.view(float_t) + assert larger_value.astype(np.float16) == smallest_value + def test_nans_infs(self): with np.errstate(all='ignore'): # Check some of the ufuncs @@ -252,6 +344,7 @@ def test_spacing_nextafter(self): # All non-negative finite #'s a = np.arange(0x7c00, dtype=uint16) hinf = np.array((np.inf,), dtype=float16) + hnan = np.array((np.nan,), dtype=float16) a_f16 = a.view(dtype=float16) assert_equal(np.spacing(a_f16[:-1]), a_f16[1:]-a_f16[:-1]) @@ -260,6 +353,21 @@ def test_spacing_nextafter(self): assert_equal(np.nextafter(a_f16[0], -hinf), -a_f16[1]) assert_equal(np.nextafter(a_f16[1:], -hinf), a_f16[:-1]) + assert_equal(np.nextafter(hinf, a_f16), a_f16[-1]) + assert_equal(np.nextafter(-hinf, a_f16), -a_f16[-1]) + + assert_equal(np.nextafter(hinf, hinf), hinf) + assert_equal(np.nextafter(hinf, -hinf), a_f16[-1]) + assert_equal(np.nextafter(-hinf, hinf), -a_f16[-1]) + assert_equal(np.nextafter(-hinf, -hinf), -hinf) + + assert_equal(np.nextafter(a_f16, hnan), hnan[0]) + assert_equal(np.nextafter(hnan, a_f16), hnan[0]) + + assert_equal(np.nextafter(hnan, hnan), hnan) + assert_equal(np.nextafter(hinf, hnan), hnan) + assert_equal(np.nextafter(hnan, hinf), hnan) + # switch to negatives a |= 0x8000 @@ -270,6 +378,12 @@ def test_spacing_nextafter(self): assert_equal(np.nextafter(a_f16[1:], hinf), a_f16[:-1]) assert_equal(np.nextafter(a_f16[:-1], -hinf), a_f16[1:]) + assert_equal(np.nextafter(hinf, a_f16), -a_f16[-1]) + assert_equal(np.nextafter(-hinf, a_f16), a_f16[-1]) + + assert_equal(np.nextafter(a_f16, hnan), hnan[0]) + assert_equal(np.nextafter(hnan, a_f16), hnan[0]) + def test_half_ufuncs(self): """Test the various ufuncs""" @@ -301,15 +415,19 @@ def test_half_ufuncs(self): assert_equal(np.copysign(b, a), [2, 5, 1, 4, 3]) assert_equal(np.maximum(a, b), [0, 5, 2, 4, 3]) + x = np.maximum(b, c) assert_(np.isnan(x[3])) x[3] = 0 assert_equal(x, [0, 5, 1, 0, 6]) + assert_equal(np.minimum(a, b), [-2, 1, 1, 4, 2]) + x = np.minimum(b, c) assert_(np.isnan(x[3])) x[3] = 0 assert_equal(x, [-2, -1, -np.inf, 0, 3]) + assert_equal(np.fmax(a, b), [0, 5, 2, 4, 3]) assert_equal(np.fmax(b, c), [0, 5, 1, 4, 6]) assert_equal(np.fmin(a, b), [-2, 1, 1, 4, 2]) @@ -406,9 +524,6 @@ def test_half_fpe(self): assert_raises_fpe('invalid', np.divide, float16(np.inf), float16(np.inf)) assert_raises_fpe('invalid', np.spacing, float16(np.inf)) assert_raises_fpe('invalid', np.spacing, float16(np.nan)) - assert_raises_fpe('invalid', np.nextafter, float16(np.inf), float16(0)) - assert_raises_fpe('invalid', np.nextafter, float16(-np.inf), float16(0)) - assert_raises_fpe('invalid', np.nextafter, float16(0), float16(np.nan)) # These should not raise float16(65472)+float16(32) @@ -417,6 +532,10 @@ def test_half_fpe(self): np.spacing(float16(-65504)) np.nextafter(float16(65504), float16(-np.inf)) np.nextafter(float16(-65504), float16(np.inf)) + np.nextafter(float16(np.inf), float16(0)) + np.nextafter(float16(-np.inf), float16(0)) + np.nextafter(float16(0), float16(np.nan)) + np.nextafter(float16(np.nan), float16(0)) float16(2**-14)/float16(2**10) float16(-2**-14)/float16(2**10) float16(2**-14+2**-23)/float16(2) diff --git a/numpy/core/tests/test_hashtable.py b/numpy/core/tests/test_hashtable.py new file mode 100644 index 000000000000..bace4c051e11 --- /dev/null +++ b/numpy/core/tests/test_hashtable.py @@ -0,0 +1,30 @@ +import pytest + +import random +from numpy.core._multiarray_tests import identityhash_tester + + +@pytest.mark.parametrize("key_length", [1, 3, 6]) +@pytest.mark.parametrize("length", [1, 16, 2000]) +def test_identity_hashtable(key_length, length): + # use a 30 object pool for everything (duplicates will happen) + pool = [object() for i in range(20)] + keys_vals = [] + for i in range(length): + keys = tuple(random.choices(pool, k=key_length)) + keys_vals.append((keys, random.choice(pool))) + + dictionary = dict(keys_vals) + + # add a random item at the end: + keys_vals.append(random.choice(keys_vals)) + # the expected one could be different with duplicates: + expected = dictionary[keys_vals[-1][0]] + + res = identityhash_tester(key_length, keys_vals, replace=True) + assert res is expected + + # check that ensuring one duplicate definitely raises: + keys_vals.insert(0, keys_vals[-2]) + with pytest.raises(RuntimeError): + identityhash_tester(key_length, keys_vals) diff --git a/numpy/core/tests/test_indexerrors.py b/numpy/core/tests/test_indexerrors.py index 63b43c473c8a..a0e9a8c55834 100644 --- a/numpy/core/tests/test_indexerrors.py +++ b/numpy/core/tests/test_indexerrors.py @@ -1,9 +1,10 @@ -from __future__ import division, absolute_import, print_function - import numpy as np -from numpy.testing import assert_raises +from numpy.testing import ( + assert_raises, assert_raises_regex, + ) + -class TestIndexErrors(object): +class TestIndexErrors: '''Tests to exercise indexerrors not covered by other tests.''' def test_arraytypes_fasttake(self): @@ -112,6 +113,15 @@ def assign(obj, ind, val): assert_raises(IndexError, lambda: a[(1, [0, 1])]) assert_raises(IndexError, lambda: assign(a, (1, [0, 1]), 1)) + def test_mapping_error_message(self): + a = np.zeros((3, 5)) + index = (1, 2, 3, 4, 5) + assert_raises_regex( + IndexError, + "too many indices for array: " + "array is 2-dimensional, but 5 were indexed", + lambda: a[index]) + def test_methods(self): "cases from methods.c" diff --git a/numpy/core/tests/test_indexing.py b/numpy/core/tests/test_indexing.py index 88f5deabcea0..1c22538567d3 100644 --- a/numpy/core/tests/test_indexing.py +++ b/numpy/core/tests/test_indexing.py @@ -1,21 +1,20 @@ -from __future__ import division, absolute_import, print_function - import sys import warnings import functools import operator + import pytest import numpy as np from numpy.core._multiarray_tests import array_indexing from itertools import product from numpy.testing import ( - assert_, assert_equal, assert_raises, assert_array_equal, assert_warns, - HAS_REFCOUNT, suppress_warnings, + assert_, assert_equal, assert_raises, assert_raises_regex, + assert_array_equal, assert_warns, HAS_REFCOUNT, ) -class TestIndexing(object): +class TestIndexing: def test_index_no_floats(self): a = np.array([[[5]]]) @@ -194,7 +193,6 @@ def test_boolean_shape_mismatch(self): assert_raises(IndexError, arr.__getitem__, (slice(None), index)) - def test_boolean_indexing_onedim(self): # Indexing a 2-dimensional array with # boolean array of length one @@ -250,6 +248,15 @@ def test_boolean_indexing_twodim(self): [4, 0, 6], [0, 8, 0]]) + def test_boolean_indexing_list(self): + # Regression test for #13715. It's a use-after-free bug which the + # test won't directly catch, but it will show up in valgrind. + a = np.array([1, 2, 3]) + b = [True, False, True] + # Two variants of the test because the first takes a fast path + assert_equal(a[b], [1, 3]) + assert_equal(a[None, b], [[1, 3]]) + def test_reverse_strides_and_subspace_bufferinit(self): # This tests that the strides are not reversed for simple and # subspace fancy indexing. @@ -329,6 +336,21 @@ def test_trivial_fancy_out_of_bounds(self): assert_raises(IndexError, a.__getitem__, ind) assert_raises(IndexError, a.__setitem__, ind, 0) + def test_trivial_fancy_not_possible(self): + # Test that the fast path for trivial assignment is not incorrectly + # used when the index is not contiguous or 1D, see also gh-11467. + a = np.arange(6) + idx = np.arange(6, dtype=np.intp).reshape(2, 1, 3)[:, :, 0] + assert_array_equal(a[idx], idx) + + # this case must not go into the fast path, note that idx is + # a non-contiuguous none 1D array here. + a[idx] = -1 + res = np.arange(6) + res[0] = -1 + res[3] = -1 + assert_array_equal(a, res) + def test_nonbaseclass_values(self): class SubClass(np.ndarray): def __array_finalize__(self, old): @@ -350,6 +372,20 @@ def __array_finalize__(self, old): a[...] = s assert_((a == 1).all()) + def test_array_like_values(self): + # Similar to the above test, but use a memoryview instead + a = np.zeros((5, 5)) + s = np.arange(25, dtype=np.float64).reshape(5, 5) + + a[[0, 1, 2, 3, 4], :] = memoryview(s) + assert_array_equal(a, s) + + a[:, [0, 1, 2, 3, 4]] = memoryview(s) + assert_array_equal(a, s) + + a[...] = memoryview(s) + assert_array_equal(a, s) + def test_subclass_writeable(self): d = np.rec.array([('NGC1001', 11), ('NGC1002', 1.), ('NGC1003', 1.)], dtype=[('target', 'S20'), ('V_mag', '>f4')]) @@ -374,14 +410,14 @@ def test_memory_order(self): def test_scalar_return_type(self): # Full scalar indices should return scalars and object # arrays should not call PyArray_Return on their items - class Zero(object): + class Zero: # The most basic valid indexing def __index__(self): return 0 z = Zero() - class ArrayLike(object): + class ArrayLike: # Simple array, should behave like the array def __array__(self): return np.array(0) @@ -461,7 +497,7 @@ def test_broken_sequence_not_nd_index(self): # on item getting, this should not be converted to an nd-index (tuple) # If this object happens to be a valid index otherwise, it should work # This object here is very dubious and probably bad though: - class SequenceLike(object): + class SequenceLike: def __index__(self): return 0 @@ -504,7 +540,55 @@ def test_indexing_array_negative_strides(self): arr[slices] = 10 assert_array_equal(arr, 10.) -class TestFieldIndexing(object): + def test_character_assignment(self): + # This is an example a function going through CopyObject which + # used to have an untested special path for scalars + # (the character special dtype case, should be deprecated probably) + arr = np.zeros((1, 5), dtype="c") + arr[0] = np.str_("asdfg") # must assign as a sequence + assert_array_equal(arr[0], np.array("asdfg", dtype="c")) + assert arr[0, 1] == b"s" # make sure not all were set to "a" for both + + @pytest.mark.parametrize("index", + [True, False, np.array([0])]) + @pytest.mark.parametrize("num", [32, 40]) + @pytest.mark.parametrize("original_ndim", [1, 32]) + def test_too_many_advanced_indices(self, index, num, original_ndim): + # These are limitations based on the number of arguments we can process. + # For `num=32` (and all boolean cases), the result is actually define; + # but the use of NpyIter (NPY_MAXARGS) limits it for technical reasons. + arr = np.ones((1,) * original_ndim) + with pytest.raises(IndexError): + arr[(index,) * num] + with pytest.raises(IndexError): + arr[(index,) * num] = 1. + + def test_structured_advanced_indexing(self): + # Test that copyswap(n) used by integer array indexing is threadsafe + # for structured datatypes, see gh-15387. This test can behave randomly. + from concurrent.futures import ThreadPoolExecutor + + # Create a deeply nested dtype to make a failure more likely: + dt = np.dtype([("", "f8")]) + dt = np.dtype([("", dt)] * 2) + dt = np.dtype([("", dt)] * 2) + # The array should be large enough to likely run into threading issues + arr = np.random.uniform(size=(6000, 8)).view(dt)[:, 0] + + rng = np.random.default_rng() + def func(arr): + indx = rng.integers(0, len(arr), size=6000, dtype=np.intp) + arr[indx] + + tpe = ThreadPoolExecutor(max_workers=8) + futures = [tpe.submit(func, arr) for _ in range(10)] + for f in futures: + f.result() + + assert arr.dtype is dt + + +class TestFieldIndexing: def test_scalar_return_type(self): # Field access on an array should return an array, even if it # is 0-d. @@ -513,7 +597,7 @@ def test_scalar_return_type(self): assert_(isinstance(a[['a']], np.ndarray)) -class TestBroadcastedAssignments(object): +class TestBroadcastedAssignments: def assign(self, a, ind, val): a[ind] = val return a @@ -549,6 +633,22 @@ def test_simple_broadcasting_errors(self): assert_raises(ValueError, assign, a, s_[:, [0]], np.zeros((5, 0))) assert_raises(ValueError, assign, a, s_[[0], :], np.zeros((2, 1))) + @pytest.mark.parametrize("index", [ + (..., [1, 2], slice(None)), + ([0, 1], ..., 0), + (..., [1, 2], [1, 2])]) + def test_broadcast_error_reports_correct_shape(self, index): + values = np.zeros((100, 100)) # will never broadcast below + + arr = np.zeros((3, 4, 5, 6, 7)) + # We currently report without any spaces (could be changed) + shape_str = str(arr[index].shape).replace(" ", "") + + with pytest.raises(ValueError) as e: + arr[index] = values + + assert str(e.value).endswith(shape_str) + def test_index_is_larger(self): # Simple case of fancy index broadcasting of the index. a = np.zeros((5, 5)) @@ -564,17 +664,48 @@ def test_broadcast_subspace(self): assert_((a[::-1] == v).all()) -class TestSubclasses(object): +class TestSubclasses: def test_basic(self): + # Test that indexing in various ways produces SubClass instances, + # and that the base is set up correctly: the original subclass + # instance for views, and a new ndarray for advanced/boolean indexing + # where a copy was made (latter a regression test for gh-11983). + class SubClass(np.ndarray): + pass + + a = np.arange(5) + s = a.view(SubClass) + s_slice = s[:3] + assert_(type(s_slice) is SubClass) + assert_(s_slice.base is s) + assert_array_equal(s_slice, a[:3]) + + s_fancy = s[[0, 1, 2]] + assert_(type(s_fancy) is SubClass) + assert_(s_fancy.base is not s) + assert_(type(s_fancy.base) is np.ndarray) + assert_array_equal(s_fancy, a[[0, 1, 2]]) + assert_array_equal(s_fancy.base, a[[0, 1, 2]]) + + s_bool = s[s > 0] + assert_(type(s_bool) is SubClass) + assert_(s_bool.base is not s) + assert_(type(s_bool.base) is np.ndarray) + assert_array_equal(s_bool, a[a > 0]) + assert_array_equal(s_bool.base, a[a > 0]) + + def test_fancy_on_read_only(self): + # Test that fancy indexing on read-only SubClass does not make a + # read-only copy (gh-14132) class SubClass(np.ndarray): pass - s = np.arange(5).view(SubClass) - assert_(isinstance(s[:3], SubClass)) - assert_(s[:3].base is s) + a = np.arange(5) + s = a.view(SubClass) + s.flags.writeable = False + s_fancy = s[[0, 1, 2]] + assert_(s_fancy.flags.writeable) - assert_(isinstance(s[[0, 1, 2]], SubClass)) - assert_(isinstance(s[s > 0], SubClass)) def test_finalize_gets_full_info(self): # Array finalize should be called on the filled array. @@ -596,56 +727,8 @@ def __array_finalize__(self, old): assert_array_equal(new_s.finalize_status, new_s) assert_array_equal(new_s.old, s) - @pytest.mark.skipif(not HAS_REFCOUNT, reason="Python lacks refcounts") - def test_slice_decref_getsetslice(self): - # See gh-10066, a temporary slice object should be discarted. - # This test is only really interesting on Python 2 since - # it goes through `__set/getslice__` here and can probably be - # removed. Use 0:7 to make sure it is never None:7. - class KeepIndexObject(np.ndarray): - def __getitem__(self, indx): - self.indx = indx - if indx == slice(0, 7): - raise ValueError - - def __setitem__(self, indx, val): - self.indx = indx - if indx == slice(0, 4): - raise ValueError - k = np.array([1]).view(KeepIndexObject) - k[0:5] - assert_equal(k.indx, slice(0, 5)) - assert_equal(sys.getrefcount(k.indx), 2) - try: - k[0:7] - raise AssertionError - except ValueError: - # The exception holds a reference to the slice so clear on Py2 - if hasattr(sys, 'exc_clear'): - with suppress_warnings() as sup: - sup.filter(DeprecationWarning) - sys.exc_clear() - assert_equal(k.indx, slice(0, 7)) - assert_equal(sys.getrefcount(k.indx), 2) - - k[0:3] = 6 - assert_equal(k.indx, slice(0, 3)) - assert_equal(sys.getrefcount(k.indx), 2) - try: - k[0:4] = 2 - raise AssertionError - except ValueError: - # The exception holds a reference to the slice so clear on Py2 - if hasattr(sys, 'exc_clear'): - with suppress_warnings() as sup: - sup.filter(DeprecationWarning) - sys.exc_clear() - assert_equal(k.indx, slice(0, 4)) - assert_equal(sys.getrefcount(k.indx), 2) - - -class TestFancyIndexingCast(object): +class TestFancyIndexingCast: def test_boolean_index_cast_assign(self): # Setup the boolean index and float arrays. shape = (8, 63) @@ -667,7 +750,7 @@ def test_boolean_index_cast_assign(self): zero_array.__setitem__, bool_index, np.array([1j])) assert_equal(zero_array[0, 1], 0) -class TestFancyIndexingEquivalence(object): +class TestFancyIndexingEquivalence: def test_object_assign(self): # Check that the field and object special case using copyto is active. # The right hand side cannot be converted to an array here. @@ -715,7 +798,7 @@ def test_cast_equivalence(self): assert_array_equal(a, b[0]) -class TestMultiIndexingAutomated(object): +class TestMultiIndexingAutomated: """ These tests use code to mimic the C-Code indexing for selection. @@ -833,7 +916,10 @@ def _get_multi_index(self, arr, indices): # is not safe. It rejects np.array([1., 2.]) but not # [1., 2.] as index (same for ie. np.take). # (Note the importance of empty lists if changing this here) - indx = np.array(indx, dtype=np.intp) + try: + indx = np.array(indx, dtype=np.intp) + except ValueError: + raise IndexError in_indices[i] = indx elif indx.dtype.kind != 'b' and indx.dtype.kind != 'i': raise IndexError('arrays used as indices must be of ' @@ -986,9 +1072,13 @@ def _get_multi_index(self, arr, indices): # Maybe never happens... raise ValueError arr = arr.take(mi.ravel(), axis=ax) - arr = arr.reshape((arr.shape[:ax] - + mi.shape - + arr.shape[ax+1:])) + try: + arr = arr.reshape((arr.shape[:ax] + + mi.shape + + arr.shape[ax+1:])) + except ValueError: + # too many dimensions, probably + raise IndexError ax += mi.ndim continue @@ -1014,8 +1104,8 @@ def _check_multi_index(self, arr, index): except Exception as e: if HAS_REFCOUNT: prev_refcount = sys.getrefcount(arr) - assert_raises(Exception, arr.__getitem__, index) - assert_raises(Exception, arr.__setitem__, index, 0) + assert_raises(type(e), arr.__getitem__, index) + assert_raises(type(e), arr.__setitem__, index, 0) if HAS_REFCOUNT: assert_equal(prev_refcount, sys.getrefcount(arr)) return @@ -1038,8 +1128,8 @@ def _check_single_index(self, arr, index): except Exception as e: if HAS_REFCOUNT: prev_refcount = sys.getrefcount(arr) - assert_raises(Exception, arr.__getitem__, index) - assert_raises(Exception, arr.__setitem__, index, 0) + assert_raises(type(e), arr.__getitem__, index) + assert_raises(type(e), arr.__setitem__, index, 0) if HAS_REFCOUNT: assert_equal(prev_refcount, sys.getrefcount(arr)) return @@ -1127,12 +1217,10 @@ def isskip(idx): def test_1d(self): a = np.arange(10) - with warnings.catch_warnings(): - warnings.filterwarnings('error', '', np.VisibleDeprecationWarning) - for index in self.complex_indices: - self._check_single_index(a, index) + for index in self.complex_indices: + self._check_single_index(a, index) -class TestFloatNonIntegerArgument(object): +class TestFloatNonIntegerArgument: """ These test that ``TypeError`` is raised when you try to use non-integers as arguments to for indexing and slicing e.g. ``a[0.0:5]`` @@ -1187,7 +1275,7 @@ def test_reduce_axis_float_index(self): assert_raises(TypeError, np.min, d, (.2, 1.2)) -class TestBooleanIndexing(object): +class TestBooleanIndexing: # Using a boolean as integer argument/indexing is an error. def test_bool_as_int_argument_errors(self): a = np.array([[[1]]]) @@ -1208,7 +1296,42 @@ def test_boolean_indexing_weirdness(self): assert_raises(IndexError, lambda: a[False, [0, 1], ...]) -class TestArrayToIndexDeprecation(object): + def test_boolean_indexing_fast_path(self): + # These used to either give the wrong error, or incorrectly give no + # error. + a = np.ones((3, 3)) + + # This used to incorrectly work (and give an array of shape (0,)) + idx1 = np.array([[False]*9]) + assert_raises_regex(IndexError, + "boolean index did not match indexed array along dimension 0; " + "dimension is 3 but corresponding boolean dimension is 1", + lambda: a[idx1]) + + # This used to incorrectly give a ValueError: operands could not be broadcast together + idx2 = np.array([[False]*8 + [True]]) + assert_raises_regex(IndexError, + "boolean index did not match indexed array along dimension 0; " + "dimension is 3 but corresponding boolean dimension is 1", + lambda: a[idx2]) + + # This is the same as it used to be. The above two should work like this. + idx3 = np.array([[False]*10]) + assert_raises_regex(IndexError, + "boolean index did not match indexed array along dimension 0; " + "dimension is 3 but corresponding boolean dimension is 1", + lambda: a[idx3]) + + # This used to give ValueError: non-broadcastable operand + a = np.ones((1, 1, 2)) + idx = np.array([[[True], [False]]]) + assert_raises_regex(IndexError, + "boolean index did not match indexed array along dimension 1; " + "dimension is 1 but corresponding boolean dimension is 2", + lambda: a[idx]) + + +class TestArrayToIndexDeprecation: """Creating an an index from array not 0-D is an error. """ @@ -1221,7 +1344,7 @@ def test_array_to_index_error(self): assert_raises(TypeError, np.take, a, [0], a) -class TestNonIntegerArrayLike(object): +class TestNonIntegerArrayLike: """Tests that array_likes only valid if can safely cast to integer. For instance, lists give IndexError when they cannot be safely cast to @@ -1238,7 +1361,7 @@ def test_basic(self): a.__getitem__([]) -class TestMultipleEllipsisError(object): +class TestMultipleEllipsisError: """An index can only have a single ellipsis. """ @@ -1249,7 +1372,7 @@ def test_basic(self): assert_raises(IndexError, a.__getitem__, ((Ellipsis,) * 3,)) -class TestCApiAccess(object): +class TestCApiAccess: def test_getitem(self): subscript = functools.partial(array_indexing, 0) diff --git a/numpy/core/tests/test_item_selection.py b/numpy/core/tests/test_item_selection.py index 3bc24fc95612..3c35245a3f43 100644 --- a/numpy/core/tests/test_item_selection.py +++ b/numpy/core/tests/test_item_selection.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - import sys import numpy as np @@ -8,7 +6,7 @@ ) -class TestTake(object): +class TestTake: def test_simple(self): a = [[1, 2], [3, 4]] a_str = [[b'1', b'2'], [b'3', b'4']] @@ -22,8 +20,9 @@ def test_simple(self): 'clip': {-1: 0, 4: 1}} # Currently all types but object, use the same function generation. # So it should not be necessary to test all. However test also a non - # refcounted struct on top of object. - types = int, object, np.dtype([('', 'i', 2)]) + # refcounted struct on top of object, which has a size that hits the + # default (non-specialized) path. + types = int, object, np.dtype([('', 'i2', 3)]) for t in types: # ta works, even if the array may be odd if buffer interface is used ta = np.array(a if np.issubdtype(t, np.number) else a_str, dtype=t) @@ -79,9 +78,9 @@ def test_empty_partition(self): assert_array_equal(a, a_original) def test_empty_argpartition(self): - # In reference to github issue #6530 - a = np.array([0, 2, 4, 6, 8, 10]) - a = a.argpartition(np.array([], dtype=np.int16)) + # In reference to github issue #6530 + a = np.array([0, 2, 4, 6, 8, 10]) + a = a.argpartition(np.array([], dtype=np.int16)) - b = np.array([0, 1, 2, 3, 4, 5]) - assert_array_equal(a, b) + b = np.array([0, 1, 2, 3, 4, 5]) + assert_array_equal(a, b) diff --git a/numpy/core/tests/test_longdouble.py b/numpy/core/tests/test_longdouble.py index cf50d5d5c6e4..1a54e62d8b3d 100644 --- a/numpy/core/tests/test_longdouble.py +++ b/numpy/core/tests/test_longdouble.py @@ -1,13 +1,14 @@ -from __future__ import division, absolute_import, print_function - +import warnings import pytest import numpy as np from numpy.testing import ( - assert_, assert_equal, assert_raises, assert_array_equal, temppath, + assert_, assert_equal, assert_raises, assert_warns, assert_array_equal, + temppath, ) from numpy.core.tests._locales import CommaDecimalPointLocale + LD_INFO = np.finfo(np.longdouble) longdouble_longer_than_double = (LD_INFO.eps < np.finfo(np.double).eps) @@ -37,22 +38,36 @@ def test_repr_roundtrip(): assert_equal(np.longdouble(repr(o)), o, "repr was %s" % repr(o)) -def test_unicode(): - np.longdouble(u"1.2") +@pytest.mark.skipif(string_to_longdouble_inaccurate, reason="Need strtold_l") +def test_repr_roundtrip_bytes(): + o = 1 + LD_INFO.eps + assert_equal(np.longdouble(repr(o).encode("ascii")), o) -def test_string(): - np.longdouble("1.2") +@pytest.mark.skipif(string_to_longdouble_inaccurate, reason="Need strtold_l") +@pytest.mark.parametrize("strtype", (np.str_, np.bytes_, str, bytes)) +def test_array_and_stringlike_roundtrip(strtype): + """ + Test that string representations of long-double roundtrip both + for array casting and scalar coercion, see also gh-15608. + """ + o = 1 + LD_INFO.eps + if strtype in (np.bytes_, bytes): + o_str = strtype(repr(o).encode("ascii")) + else: + o_str = strtype(repr(o)) -def test_bytes(): - np.longdouble(b"1.2") + # Test that `o` is correctly coerced from the string-like + assert o == np.longdouble(o_str) + # Test that arrays also roundtrip correctly: + o_strarr = np.asarray([o] * 3, dtype=strtype) + assert (o == o_strarr.astype(np.longdouble)).all() -@pytest.mark.skipif(string_to_longdouble_inaccurate, reason="Need strtold_l") -def test_repr_roundtrip_bytes(): - o = 1 + LD_INFO.eps - assert_equal(np.longdouble(repr(o).encode("ascii")), o) + # And array coercion and casting to string give the same as scalar repr: + assert (o_strarr == o_str).all() + assert (np.asarray([o] * 3).astype(strtype) == o_str).all() def test_bogus_string(): @@ -69,22 +84,57 @@ def test_fromstring(): err_msg="reading '%s'" % s) +def test_fromstring_complex(): + for ctype in ["complex", "cdouble", "cfloat"]: + # Check spacing between separator + assert_equal(np.fromstring("1, 2 , 3 ,4", sep=",", dtype=ctype), + np.array([1., 2., 3., 4.])) + # Real component not specified + assert_equal(np.fromstring("1j, -2j, 3j, 4e1j", sep=",", dtype=ctype), + np.array([1.j, -2.j, 3.j, 40.j])) + # Both components specified + assert_equal(np.fromstring("1+1j,2-2j, -3+3j, -4e1+4j", sep=",", dtype=ctype), + np.array([1. + 1.j, 2. - 2.j, - 3. + 3.j, - 40. + 4j])) + # Spaces at wrong places + with assert_warns(DeprecationWarning): + assert_equal(np.fromstring("1+2 j,3", dtype=ctype, sep=","), + np.array([1.])) + with assert_warns(DeprecationWarning): + assert_equal(np.fromstring("1+ 2j,3", dtype=ctype, sep=","), + np.array([1.])) + with assert_warns(DeprecationWarning): + assert_equal(np.fromstring("1 +2j,3", dtype=ctype, sep=","), + np.array([1.])) + with assert_warns(DeprecationWarning): + assert_equal(np.fromstring("1+j", dtype=ctype, sep=","), + np.array([1.])) + with assert_warns(DeprecationWarning): + assert_equal(np.fromstring("1+", dtype=ctype, sep=","), + np.array([1.])) + with assert_warns(DeprecationWarning): + assert_equal(np.fromstring("1j+1", dtype=ctype, sep=","), + np.array([1j])) + + def test_fromstring_bogus(): - assert_equal(np.fromstring("1. 2. 3. flop 4.", dtype=float, sep=" "), - np.array([1., 2., 3.])) + with assert_warns(DeprecationWarning): + assert_equal(np.fromstring("1. 2. 3. flop 4.", dtype=float, sep=" "), + np.array([1., 2., 3.])) def test_fromstring_empty(): - assert_equal(np.fromstring("xxxxx", sep="x"), - np.array([])) + with assert_warns(DeprecationWarning): + assert_equal(np.fromstring("xxxxx", sep="x"), + np.array([])) def test_fromstring_missing(): - assert_equal(np.fromstring("1xx3x4x5x6", sep="x"), - np.array([1])) + with assert_warns(DeprecationWarning): + assert_equal(np.fromstring("1xx3x4x5x6", sep="x"), + np.array([1])) -class TestFileBased(object): +class TestFileBased: ldbl = 1 + LD_INFO.eps tgt = np.array([ldbl]*5) @@ -94,9 +144,93 @@ def test_fromfile_bogus(self): with temppath() as path: with open(path, 'wt') as f: f.write("1. 2. 3. flop 4.\n") - res = np.fromfile(path, dtype=float, sep=" ") + + with assert_warns(DeprecationWarning): + res = np.fromfile(path, dtype=float, sep=" ") assert_equal(res, np.array([1., 2., 3.])) + def test_fromfile_complex(self): + for ctype in ["complex", "cdouble", "cfloat"]: + # Check spacing between separator and only real component specified + with temppath() as path: + with open(path, 'wt') as f: + f.write("1, 2 , 3 ,4\n") + + res = np.fromfile(path, dtype=ctype, sep=",") + assert_equal(res, np.array([1., 2., 3., 4.])) + + # Real component not specified + with temppath() as path: + with open(path, 'wt') as f: + f.write("1j, -2j, 3j, 4e1j\n") + + res = np.fromfile(path, dtype=ctype, sep=",") + assert_equal(res, np.array([1.j, -2.j, 3.j, 40.j])) + + # Both components specified + with temppath() as path: + with open(path, 'wt') as f: + f.write("1+1j,2-2j, -3+3j, -4e1+4j\n") + + res = np.fromfile(path, dtype=ctype, sep=",") + assert_equal(res, np.array([1. + 1.j, 2. - 2.j, - 3. + 3.j, - 40. + 4j])) + + # Spaces at wrong places + with temppath() as path: + with open(path, 'wt') as f: + f.write("1+2 j,3\n") + + with assert_warns(DeprecationWarning): + res = np.fromfile(path, dtype=ctype, sep=",") + assert_equal(res, np.array([1.])) + + # Spaces at wrong places + with temppath() as path: + with open(path, 'wt') as f: + f.write("1+ 2j,3\n") + + with assert_warns(DeprecationWarning): + res = np.fromfile(path, dtype=ctype, sep=",") + assert_equal(res, np.array([1.])) + + # Spaces at wrong places + with temppath() as path: + with open(path, 'wt') as f: + f.write("1 +2j,3\n") + + with assert_warns(DeprecationWarning): + res = np.fromfile(path, dtype=ctype, sep=",") + assert_equal(res, np.array([1.])) + + # Spaces at wrong places + with temppath() as path: + with open(path, 'wt') as f: + f.write("1+j\n") + + with assert_warns(DeprecationWarning): + res = np.fromfile(path, dtype=ctype, sep=",") + assert_equal(res, np.array([1.])) + + # Spaces at wrong places + with temppath() as path: + with open(path, 'wt') as f: + f.write("1+\n") + + with assert_warns(DeprecationWarning): + res = np.fromfile(path, dtype=ctype, sep=",") + assert_equal(res, np.array([1.])) + + # Spaces at wrong places + with temppath() as path: + with open(path, 'wt') as f: + f.write("1j+1\n") + + with assert_warns(DeprecationWarning): + res = np.fromfile(path, dtype=ctype, sep=",") + assert_equal(res, np.array([1.j])) + + + @pytest.mark.skipif(string_to_longdouble_inaccurate, reason="Need strtold_l") def test_fromfile(self): @@ -185,12 +319,14 @@ def test_fromstring_foreign_repr(self): assert_equal(a[0], f) def test_fromstring_best_effort_float(self): - assert_equal(np.fromstring("1,234", dtype=float, sep=" "), - np.array([1.])) + with assert_warns(DeprecationWarning): + assert_equal(np.fromstring("1,234", dtype=float, sep=" "), + np.array([1.])) def test_fromstring_best_effort(self): - assert_equal(np.fromstring("1,234", dtype=np.longdouble, sep=" "), - np.array([1.])) + with assert_warns(DeprecationWarning): + assert_equal(np.fromstring("1,234", dtype=np.longdouble, sep=" "), + np.array([1.])) def test_fromstring_foreign(self): s = "1.234" @@ -203,5 +339,32 @@ def test_fromstring_foreign_sep(self): assert_array_equal(a, b) def test_fromstring_foreign_value(self): - b = np.fromstring("1,234", dtype=np.longdouble, sep=" ") - assert_array_equal(b[0], 1) + with assert_warns(DeprecationWarning): + b = np.fromstring("1,234", dtype=np.longdouble, sep=" ") + assert_array_equal(b[0], 1) + + +@pytest.mark.parametrize("int_val", [ + # cases discussed in gh-10723 + # and gh-9968 + 2 ** 1024, 0]) +def test_longdouble_from_int(int_val): + # for issue gh-9968 + str_val = str(int_val) + # we'll expect a RuntimeWarning on platforms + # with np.longdouble equivalent to np.double + # for large integer input + with warnings.catch_warnings(record=True) as w: + warnings.filterwarnings('always', '', RuntimeWarning) + # can be inf==inf on some platforms + assert np.longdouble(int_val) == np.longdouble(str_val) + # we can't directly compare the int and + # max longdouble value on all platforms + if np.allclose(np.finfo(np.longdouble).max, + np.finfo(np.double).max) and w: + assert w[0].category is RuntimeWarning + +@pytest.mark.parametrize("bool_val", [ + True, False]) +def test_longdouble_from_bool(bool_val): + assert np.longdouble(bool_val) == np.longdouble(int(bool_val)) diff --git a/numpy/core/tests/test_machar.py b/numpy/core/tests/test_machar.py index ab8800c09d30..3a66ec51fd58 100644 --- a/numpy/core/tests/test_machar.py +++ b/numpy/core/tests/test_machar.py @@ -3,20 +3,18 @@ rid of both MachAr and this test at some point. """ -from __future__ import division, absolute_import, print_function - -from numpy.core.machar import MachAr +from numpy.core._machar import MachAr import numpy.core.numerictypes as ntypes from numpy import errstate, array -class TestMachAr(object): +class TestMachAr: def _run_machar_highprec(self): # Instantiate MachAr instance with high enough precision to cause # underflow try: hiprec = ntypes.float96 - MachAr(lambda v:array([v], hiprec)) + MachAr(lambda v: array(v, hiprec)) except AttributeError: # Fixme, this needs to raise a 'skip' exception. "Skipping test: no ntypes.float96 available on this platform." diff --git a/numpy/core/tests/test_mem_overlap.py b/numpy/core/tests/test_mem_overlap.py index f4ce6a84ae51..24bdf477f7c7 100644 --- a/numpy/core/tests/test_mem_overlap.py +++ b/numpy/core/tests/test_mem_overlap.py @@ -1,6 +1,3 @@ -from __future__ import division, absolute_import, print_function - -import sys import itertools import pytest @@ -8,14 +5,10 @@ from numpy.core._multiarray_tests import solve_diophantine, internal_overlap from numpy.core import _umath_tests from numpy.lib.stride_tricks import as_strided -from numpy.compat import long from numpy.testing import ( - assert_, assert_raises, assert_equal, assert_array_equal, assert_allclose + assert_, assert_raises, assert_equal, assert_array_equal ) -if sys.version_info[0] >= 3: - xrange = range - ndims = 2 size = 10 @@ -47,9 +40,7 @@ def _indices_for_axis(): res = [] for nelems in (0, 2, 3): ind = _indices_for_nelems(nelems) - - # no itertools.product available in Py2.4 - res.extend([(a, b) for a in ind for b in ind]) # all assignments of size "nelems" + res.extend(itertools.product(ind, ind)) # all assignments of size "nelems" return res @@ -58,18 +49,7 @@ def _indices(ndims): """Returns ((axis0_src, axis0_dst), (axis1_src, axis1_dst), ... ) index pairs.""" ind = _indices_for_axis() - - # no itertools.product available in Py2.4 - - res = [[]] - for i in range(ndims): - newres = [] - for elem in ind: - for others in res: - newres.append([elem] + others) - res = newres - - return res + return itertools.product(ind, repeat=ndims) def _check_assignment(srcidx, dstidx): @@ -140,11 +120,7 @@ def test_diophantine_fuzz(): # Check no solution exists (provided the problem is # small enough so that brute force checking doesn't # take too long) - try: - ranges = tuple(xrange(0, a*ub+1, a) for a, ub in zip(A, U)) - except OverflowError: - # xrange on 32-bit Python 2 may overflow - continue + ranges = tuple(range(0, a*ub+1, a) for a, ub in zip(A, U)) size = 1 for r in ranges: @@ -410,7 +386,6 @@ def test_shares_memory_api(): assert_equal(np.shares_memory(a, b), True) assert_equal(np.shares_memory(a, b, max_work=None), True) assert_raises(np.TooHardError, np.shares_memory, a, b, max_work=1) - assert_raises(np.TooHardError, np.shares_memory, a, b, max_work=long(1)) def test_may_share_memory_bad_max_work(): @@ -477,7 +452,7 @@ def check_internal_overlap(a, manual_expected=None): # Brute-force check m = set() - ranges = tuple(xrange(n) for n in a.shape) + ranges = tuple(range(n) for n in a.shape) for v in itertools.product(*ranges): offset = sum(s*w for s, w in zip(a.strides, v)) if offset in m: @@ -564,7 +539,7 @@ def test_internal_overlap_fuzz(): def test_non_ndarray_inputs(): # Regression check for gh-5604 - class MyArray(object): + class MyArray: def __init__(self, data): self.data = data @@ -572,7 +547,7 @@ def __init__(self, data): def __array_interface__(self): return self.data.__array_interface__ - class MyArray2(object): + class MyArray2: def __init__(self, data): self.data = data @@ -619,7 +594,7 @@ def assert_copy_equivalent(operation, args, out, **kwargs): assert_equal(got, expected) -class TestUFunc(object): +class TestUFunc: """ Test ufunc call memory overlap handling """ @@ -691,6 +666,11 @@ def check_unary_fuzz(self, operation, get_out_axis_size, dtype=np.int16, def test_unary_ufunc_call_fuzz(self): self.check_unary_fuzz(np.invert, None, np.int16) + @pytest.mark.slow + def test_unary_ufunc_call_complex_fuzz(self): + # Complex typically has a smaller alignment than itemsize + self.check_unary_fuzz(np.negative, None, np.complex128, count=500) + def test_binary_ufunc_accumulate_fuzz(self): def get_out_axis_size(a, b, axis): if axis is None: @@ -748,6 +728,7 @@ def check(ufunc, a, ind, out): a = np.arange(10000, dtype=np.int16) check(np.add, a, a[::-1], a) + @pytest.mark.slow def test_unary_gufunc_fuzz(self): shapes = [7, 13, 8, 21, 29, 32] gufunc = _umath_tests.euclidean_pdist @@ -816,7 +797,7 @@ def check(ufunc, a, ind, b=None): check(np.add, a, ind, a[25:75]) def test_unary_ufunc_1d_manual(self): - # Exercise branches in PyArray_EQUIVALENTLY_ITERABLE + # Exercise ufunc fast-paths (that avoid creation of an `np.nditer`) def check(a, b): a_orig = a.copy() diff --git a/numpy/core/tests/test_mem_policy.py b/numpy/core/tests/test_mem_policy.py new file mode 100644 index 000000000000..3dae36d5a56c --- /dev/null +++ b/numpy/core/tests/test_mem_policy.py @@ -0,0 +1,423 @@ +import asyncio +import gc +import os +import pytest +import numpy as np +import threading +import warnings +from numpy.testing import extbuild, assert_warns +import sys + + +@pytest.fixture +def get_module(tmp_path): + """ Add a memory policy that returns a false pointer 64 bytes into the + actual allocation, and fill the prefix with some text. Then check at each + memory manipulation that the prefix exists, to make sure all alloc/realloc/ + free/calloc go via the functions here. + """ + if sys.platform.startswith('cygwin'): + pytest.skip('link fails on cygwin') + functions = [ + ("get_default_policy", "METH_NOARGS", """ + Py_INCREF(PyDataMem_DefaultHandler); + return PyDataMem_DefaultHandler; + """), + ("set_secret_data_policy", "METH_NOARGS", """ + PyObject *secret_data = + PyCapsule_New(&secret_data_handler, "mem_handler", NULL); + if (secret_data == NULL) { + return NULL; + } + PyObject *old = PyDataMem_SetHandler(secret_data); + Py_DECREF(secret_data); + return old; + """), + ("set_old_policy", "METH_O", """ + PyObject *old; + if (args != NULL && PyCapsule_CheckExact(args)) { + old = PyDataMem_SetHandler(args); + } + else { + old = PyDataMem_SetHandler(NULL); + } + return old; + """), + ("get_array", "METH_NOARGS", """ + char *buf = (char *)malloc(20); + npy_intp dims[1]; + dims[0] = 20; + PyArray_Descr *descr = PyArray_DescrNewFromType(NPY_UINT8); + return PyArray_NewFromDescr(&PyArray_Type, descr, 1, dims, NULL, + buf, NPY_ARRAY_WRITEABLE, NULL); + """), + ("set_own", "METH_O", """ + if (!PyArray_Check(args)) { + PyErr_SetString(PyExc_ValueError, + "need an ndarray"); + return NULL; + } + PyArray_ENABLEFLAGS((PyArrayObject*)args, NPY_ARRAY_OWNDATA); + // Maybe try this too? + // PyArray_BASE(PyArrayObject *)args) = NULL; + Py_RETURN_NONE; + """), + ("get_array_with_base", "METH_NOARGS", """ + char *buf = (char *)malloc(20); + npy_intp dims[1]; + dims[0] = 20; + PyArray_Descr *descr = PyArray_DescrNewFromType(NPY_UINT8); + PyObject *arr = PyArray_NewFromDescr(&PyArray_Type, descr, 1, dims, + NULL, buf, + NPY_ARRAY_WRITEABLE, NULL); + if (arr == NULL) return NULL; + PyObject *obj = PyCapsule_New(buf, "buf capsule", + (PyCapsule_Destructor)&warn_on_free); + if (obj == NULL) { + Py_DECREF(arr); + return NULL; + } + if (PyArray_SetBaseObject((PyArrayObject *)arr, obj) < 0) { + Py_DECREF(arr); + Py_DECREF(obj); + return NULL; + } + return arr; + + """), + ] + prologue = ''' + #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION + #include <numpy/arrayobject.h> + /* + * This struct allows the dynamic configuration of the allocator funcs + * of the `secret_data_allocator`. It is provided here for + * demonstration purposes, as a valid `ctx` use-case scenario. + */ + typedef struct { + void *(*malloc)(size_t); + void *(*calloc)(size_t, size_t); + void *(*realloc)(void *, size_t); + void (*free)(void *); + } SecretDataAllocatorFuncs; + + NPY_NO_EXPORT void * + shift_alloc(void *ctx, size_t sz) { + SecretDataAllocatorFuncs *funcs = (SecretDataAllocatorFuncs *)ctx; + char *real = (char *)funcs->malloc(sz + 64); + if (real == NULL) { + return NULL; + } + snprintf(real, 64, "originally allocated %ld", (unsigned long)sz); + return (void *)(real + 64); + } + NPY_NO_EXPORT void * + shift_zero(void *ctx, size_t sz, size_t cnt) { + SecretDataAllocatorFuncs *funcs = (SecretDataAllocatorFuncs *)ctx; + char *real = (char *)funcs->calloc(sz + 64, cnt); + if (real == NULL) { + return NULL; + } + snprintf(real, 64, "originally allocated %ld via zero", + (unsigned long)sz); + return (void *)(real + 64); + } + NPY_NO_EXPORT void + shift_free(void *ctx, void * p, npy_uintp sz) { + SecretDataAllocatorFuncs *funcs = (SecretDataAllocatorFuncs *)ctx; + if (p == NULL) { + return ; + } + char *real = (char *)p - 64; + if (strncmp(real, "originally allocated", 20) != 0) { + fprintf(stdout, "uh-oh, unmatched shift_free, " + "no appropriate prefix\\n"); + /* Make C runtime crash by calling free on the wrong address */ + funcs->free((char *)p + 10); + /* funcs->free(real); */ + } + else { + npy_uintp i = (npy_uintp)atoi(real +20); + if (i != sz) { + fprintf(stderr, "uh-oh, unmatched shift_free" + "(ptr, %ld) but allocated %ld\\n", sz, i); + /* This happens in some places, only print */ + funcs->free(real); + } + else { + funcs->free(real); + } + } + } + NPY_NO_EXPORT void * + shift_realloc(void *ctx, void * p, npy_uintp sz) { + SecretDataAllocatorFuncs *funcs = (SecretDataAllocatorFuncs *)ctx; + if (p != NULL) { + char *real = (char *)p - 64; + if (strncmp(real, "originally allocated", 20) != 0) { + fprintf(stdout, "uh-oh, unmatched shift_realloc\\n"); + return realloc(p, sz); + } + return (void *)((char *)funcs->realloc(real, sz + 64) + 64); + } + else { + char *real = (char *)funcs->realloc(p, sz + 64); + if (real == NULL) { + return NULL; + } + snprintf(real, 64, "originally allocated " + "%ld via realloc", (unsigned long)sz); + return (void *)(real + 64); + } + } + /* As an example, we use the standard {m|c|re}alloc/free funcs. */ + static SecretDataAllocatorFuncs secret_data_handler_ctx = { + malloc, + calloc, + realloc, + free + }; + static PyDataMem_Handler secret_data_handler = { + "secret_data_allocator", + 1, + { + &secret_data_handler_ctx, /* ctx */ + shift_alloc, /* malloc */ + shift_zero, /* calloc */ + shift_realloc, /* realloc */ + shift_free /* free */ + } + }; + void warn_on_free(void *capsule) { + PyErr_WarnEx(PyExc_UserWarning, "in warn_on_free", 1); + void * obj = PyCapsule_GetPointer(capsule, + PyCapsule_GetName(capsule)); + free(obj); + }; + ''' + more_init = "import_array();" + try: + import mem_policy + return mem_policy + except ImportError: + pass + # if it does not exist, build and load it + return extbuild.build_and_import_extension('mem_policy', + functions, + prologue=prologue, + include_dirs=[np.get_include()], + build_dir=tmp_path, + more_init=more_init) + + +def test_set_policy(get_module): + + get_handler_name = np.core.multiarray.get_handler_name + get_handler_version = np.core.multiarray.get_handler_version + orig_policy_name = get_handler_name() + + a = np.arange(10).reshape((2, 5)) # a doesn't own its own data + assert get_handler_name(a) is None + assert get_handler_version(a) is None + assert get_handler_name(a.base) == orig_policy_name + assert get_handler_version(a.base) == 1 + + orig_policy = get_module.set_secret_data_policy() + + b = np.arange(10).reshape((2, 5)) # b doesn't own its own data + assert get_handler_name(b) is None + assert get_handler_version(b) is None + assert get_handler_name(b.base) == 'secret_data_allocator' + assert get_handler_version(b.base) == 1 + + if orig_policy_name == 'default_allocator': + get_module.set_old_policy(None) # tests PyDataMem_SetHandler(NULL) + assert get_handler_name() == 'default_allocator' + else: + get_module.set_old_policy(orig_policy) + assert get_handler_name() == orig_policy_name + + +def test_default_policy_singleton(get_module): + get_handler_name = np.core.multiarray.get_handler_name + + # set the policy to default + orig_policy = get_module.set_old_policy(None) + + assert get_handler_name() == 'default_allocator' + + # re-set the policy to default + def_policy_1 = get_module.set_old_policy(None) + + assert get_handler_name() == 'default_allocator' + + # set the policy to original + def_policy_2 = get_module.set_old_policy(orig_policy) + + # since default policy is a singleton, + # these should be the same object + assert def_policy_1 is def_policy_2 is get_module.get_default_policy() + + +def test_policy_propagation(get_module): + # The memory policy goes hand-in-hand with flags.owndata + + class MyArr(np.ndarray): + pass + + get_handler_name = np.core.multiarray.get_handler_name + orig_policy_name = get_handler_name() + a = np.arange(10).view(MyArr).reshape((2, 5)) + assert get_handler_name(a) is None + assert a.flags.owndata is False + + assert get_handler_name(a.base) is None + assert a.base.flags.owndata is False + + assert get_handler_name(a.base.base) == orig_policy_name + assert a.base.base.flags.owndata is True + + +async def concurrent_context1(get_module, orig_policy_name, event): + if orig_policy_name == 'default_allocator': + get_module.set_secret_data_policy() + assert np.core.multiarray.get_handler_name() == 'secret_data_allocator' + else: + get_module.set_old_policy(None) + assert np.core.multiarray.get_handler_name() == 'default_allocator' + event.set() + + +async def concurrent_context2(get_module, orig_policy_name, event): + await event.wait() + # the policy is not affected by changes in parallel contexts + assert np.core.multiarray.get_handler_name() == orig_policy_name + # change policy in the child context + if orig_policy_name == 'default_allocator': + get_module.set_secret_data_policy() + assert np.core.multiarray.get_handler_name() == 'secret_data_allocator' + else: + get_module.set_old_policy(None) + assert np.core.multiarray.get_handler_name() == 'default_allocator' + + +async def async_test_context_locality(get_module): + orig_policy_name = np.core.multiarray.get_handler_name() + + event = asyncio.Event() + # the child contexts inherit the parent policy + concurrent_task1 = asyncio.create_task( + concurrent_context1(get_module, orig_policy_name, event)) + concurrent_task2 = asyncio.create_task( + concurrent_context2(get_module, orig_policy_name, event)) + await concurrent_task1 + await concurrent_task2 + + # the parent context is not affected by child policy changes + assert np.core.multiarray.get_handler_name() == orig_policy_name + + +def test_context_locality(get_module): + if (sys.implementation.name == 'pypy' + and sys.pypy_version_info[:3] < (7, 3, 6)): + pytest.skip('no context-locality support in PyPy < 7.3.6') + asyncio.run(async_test_context_locality(get_module)) + + +def concurrent_thread1(get_module, event): + get_module.set_secret_data_policy() + assert np.core.multiarray.get_handler_name() == 'secret_data_allocator' + event.set() + + +def concurrent_thread2(get_module, event): + event.wait() + # the policy is not affected by changes in parallel threads + assert np.core.multiarray.get_handler_name() == 'default_allocator' + # change policy in the child thread + get_module.set_secret_data_policy() + + +def test_thread_locality(get_module): + orig_policy_name = np.core.multiarray.get_handler_name() + + event = threading.Event() + # the child threads do not inherit the parent policy + concurrent_task1 = threading.Thread(target=concurrent_thread1, + args=(get_module, event)) + concurrent_task2 = threading.Thread(target=concurrent_thread2, + args=(get_module, event)) + concurrent_task1.start() + concurrent_task2.start() + concurrent_task1.join() + concurrent_task2.join() + + # the parent thread is not affected by child policy changes + assert np.core.multiarray.get_handler_name() == orig_policy_name + + +@pytest.mark.slow +def test_new_policy(get_module): + a = np.arange(10) + orig_policy_name = np.core.multiarray.get_handler_name(a) + + orig_policy = get_module.set_secret_data_policy() + + b = np.arange(10) + assert np.core.multiarray.get_handler_name(b) == 'secret_data_allocator' + + # test array manipulation. This is slow + if orig_policy_name == 'default_allocator': + # when the np.core.test tests recurse into this test, the + # policy will be set so this "if" will be false, preventing + # infinite recursion + # + # if needed, debug this by + # - running tests with -- -s (to not capture stdout/stderr + # - setting extra_argv=['-vv'] here + assert np.core.test('full', verbose=2, extra_argv=['-vv']) + # also try the ma tests, the pickling test is quite tricky + assert np.ma.test('full', verbose=2, extra_argv=['-vv']) + + get_module.set_old_policy(orig_policy) + + c = np.arange(10) + assert np.core.multiarray.get_handler_name(c) == orig_policy_name + +@pytest.mark.xfail(sys.implementation.name == "pypy", + reason=("bad interaction between getenv and " + "os.environ inside pytest")) +@pytest.mark.parametrize("policy", ["0", "1", None]) +def test_switch_owner(get_module, policy): + a = get_module.get_array() + assert np.core.multiarray.get_handler_name(a) is None + get_module.set_own(a) + oldval = os.environ.get('NUMPY_WARN_IF_NO_MEM_POLICY', None) + if policy is None: + if 'NUMPY_WARN_IF_NO_MEM_POLICY' in os.environ: + os.environ.pop('NUMPY_WARN_IF_NO_MEM_POLICY') + else: + os.environ['NUMPY_WARN_IF_NO_MEM_POLICY'] = policy + try: + # The policy should be NULL, so we have to assume we can call + # "free". A warning is given if the policy == "1" + if policy == "1": + with assert_warns(RuntimeWarning) as w: + del a + gc.collect() + else: + del a + gc.collect() + + finally: + if oldval is None: + if 'NUMPY_WARN_IF_NO_MEM_POLICY' in os.environ: + os.environ.pop('NUMPY_WARN_IF_NO_MEM_POLICY') + else: + os.environ['NUMPY_WARN_IF_NO_MEM_POLICY'] = oldval + +def test_owner_is_base(get_module): + a = get_module.get_array_with_base() + with pytest.warns(UserWarning, match='warn_on_free'): + del a + gc.collect() diff --git a/numpy/core/tests/test_memmap.py b/numpy/core/tests/test_memmap.py index 59ca28324d43..e4f0a6b3f665 100644 --- a/numpy/core/tests/test_memmap.py +++ b/numpy/core/tests/test_memmap.py @@ -1,25 +1,22 @@ -from __future__ import division, absolute_import, print_function - import sys import os -import shutil import mmap import pytest -from tempfile import NamedTemporaryFile, TemporaryFile, mktemp, mkdtemp +from pathlib import Path +from tempfile import NamedTemporaryFile, TemporaryFile from numpy import ( memmap, sum, average, product, ndarray, isscalar, add, subtract, multiply) -from numpy.compat import Path from numpy import arange, allclose, asarray from numpy.testing import ( - assert_, assert_equal, assert_array_equal, suppress_warnings + assert_, assert_equal, assert_array_equal, suppress_warnings, IS_PYPY, + break_cycles ) -class TestMemmap(object): +class TestMemmap: def setup(self): self.tmpfp = NamedTemporaryFile(prefix='mmap') - self.tempdir = mkdtemp() self.shape = (3, 4) self.dtype = 'float32' self.data = arange(12, dtype=self.dtype) @@ -27,7 +24,10 @@ def setup(self): def teardown(self): self.tmpfp.close() - shutil.rmtree(self.tempdir) + self.data = None + if IS_PYPY: + break_cycles() + break_cycles() def test_roundtrip(self): # Write data to file @@ -43,8 +43,8 @@ def test_roundtrip(self): assert_array_equal(self.data, newfp) assert_equal(newfp.flags.writeable, False) - def test_open_with_filename(self): - tmpname = mktemp('', 'mmap', dir=self.tempdir) + def test_open_with_filename(self, tmp_path): + tmpname = tmp_path / 'mmap' fp = memmap(tmpname, dtype=self.dtype, mode='w+', shape=self.shape) fp[:] = self.data[:] @@ -64,11 +64,11 @@ def test_attributes(self): assert_equal(mode, fp.mode) del fp - def test_filename(self): - tmpname = mktemp('', 'mmap', dir=self.tempdir) + def test_filename(self, tmp_path): + tmpname = tmp_path / "mmap" fp = memmap(tmpname, dtype=self.dtype, mode='w+', shape=self.shape) - abspath = os.path.abspath(tmpname) + abspath = Path(os.path.abspath(tmpname)) fp[:] = self.data[:] assert_equal(abspath, fp.filename) b = fp[:1] @@ -76,12 +76,14 @@ def test_filename(self): del b del fp - @pytest.mark.skipif(Path is None, reason="No pathlib.Path") - def test_path(self): - tmpname = mktemp('', 'mmap', dir=self.tempdir) + def test_path(self, tmp_path): + tmpname = tmp_path / "mmap" fp = memmap(Path(tmpname), dtype=self.dtype, mode='w+', shape=self.shape) - abspath = os.path.realpath(os.path.abspath(tmpname)) + # os.path.realpath does not resolve symlinks on Windows + # see: https://bugs.python.org/issue9949 + # use Path.resolve, just as memmap class does internally + abspath = str(Path(tmpname).resolve()) fp[:] = self.data[:] assert_equal(abspath, str(fp.filename.resolve())) b = fp[:1] @@ -201,3 +203,13 @@ def test_no_shape(self): self.tmpfp.write(b'a'*16) mm = memmap(self.tmpfp, dtype='float64') assert_equal(mm.shape, (2,)) + + def test_empty_array(self): + # gh-12653 + with pytest.raises(ValueError, match='empty file'): + memmap(self.tmpfp, shape=(0,4), mode='w+') + + self.tmpfp.write(b'\0') + + # ok now the file is not empty + memmap(self.tmpfp, shape=(0,4), mode='w+') diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py index 37d73e42c3b7..4413cd0d0e69 100644 --- a/numpy/core/tests/test_multiarray.py +++ b/numpy/core/tests/test_multiarray.py @@ -1,14 +1,6 @@ -from __future__ import division, absolute_import, print_function - -try: - # Accessing collections abstract classes from collections - # has been deprecated since Python 3.3 - import collections.abc as collections_abc -except ImportError: - import collections as collections_abc +import collections.abc import tempfile import sys -import shutil import warnings import operator import io @@ -20,38 +12,36 @@ import weakref import pytest from contextlib import contextmanager -if sys.version_info[0] >= 3: - import builtins -else: - import __builtin__ as builtins + +from numpy.compat import pickle + +import pathlib +import builtins from decimal import Decimal import numpy as np -from numpy.compat import strchar, unicode import numpy.core._multiarray_tests as _multiarray_tests +from numpy.core._rational_tests import rational from numpy.testing import ( assert_, assert_raises, assert_warns, assert_equal, assert_almost_equal, assert_array_equal, assert_raises_regex, assert_array_almost_equal, - assert_allclose, IS_PYPY, HAS_REFCOUNT, assert_array_less, runstring, - SkipTest, temppath, suppress_warnings + assert_allclose, IS_PYPY, IS_PYSTON, HAS_REFCOUNT, assert_array_less, + runstring, temppath, suppress_warnings, break_cycles, ) +from numpy.testing._private.utils import _no_tracing from numpy.core.tests._locales import CommaDecimalPointLocale # Need to test an object that does not fully implement math interface from datetime import timedelta, datetime -if sys.version_info[:2] > (3, 2): - # In Python 3.3 the representation of empty shape, strides and sub-offsets - # is an empty tuple instead of None. - # http://docs.python.org/dev/whatsnew/3.3.html#api-changes - EMPTY = () -else: - EMPTY = None - - def _aligned_zeros(shape, dtype=float, order="C", align=None): - """Allocate a new ndarray with aligned memory.""" + """ + Allocate a new ndarray with aligned memory. + + The ndarray is guaranteed *not* aligned to twice the requested alignment. + Eg, if align=4, guarantees it is not aligned to 8. If align=None uses + dtype.alignment.""" dtype = np.dtype(dtype) if dtype == np.dtype(object): # Can't do this, fall back to standard allocation (which @@ -64,10 +54,15 @@ def _aligned_zeros(shape, dtype=float, order="C", align=None): if not hasattr(shape, '__len__'): shape = (shape,) size = functools.reduce(operator.mul, shape) * dtype.itemsize - buf = np.empty(size + align + 1, np.uint8) - offset = buf.__array_interface__['data'][0] % align + buf = np.empty(size + 2*align + 1, np.uint8) + + ptr = buf.__array_interface__['data'][0] + offset = ptr % align if offset != 0: offset = align - offset + if (ptr % (2*align)) == 0: + offset += align + # Note: slices producing 0-size arrays do not necessarily change # data pointer --- so we use and allocate size+1 buf = buf[offset:offset+size+1][:-1] @@ -76,7 +71,7 @@ def _aligned_zeros(shape, dtype=float, order="C", align=None): return data -class TestFlags(object): +class TestFlags: def setup(self): self.a = np.arange(10) @@ -89,6 +84,146 @@ def test_writeable(self): self.a[0] = 5 self.a[0] = 0 + def test_writeable_any_base(self): + # Ensure that any base being writeable is sufficient to change flag; + # this is especially interesting for arrays from an array interface. + arr = np.arange(10) + + class subclass(np.ndarray): + pass + + # Create subclass so base will not be collapsed, this is OK to change + view1 = arr.view(subclass) + view2 = view1[...] + arr.flags.writeable = False + view2.flags.writeable = False + view2.flags.writeable = True # Can be set to True again. + + arr = np.arange(10) + + class frominterface: + def __init__(self, arr): + self.arr = arr + self.__array_interface__ = arr.__array_interface__ + + view1 = np.asarray(frominterface) + view2 = view1[...] + view2.flags.writeable = False + view2.flags.writeable = True + + view1.flags.writeable = False + view2.flags.writeable = False + with assert_raises(ValueError): + # Must assume not writeable, since only base is not: + view2.flags.writeable = True + + def test_writeable_from_readonly(self): + # gh-9440 - make sure fromstring, from buffer on readonly buffers + # set writeable False + data = b'\x00' * 100 + vals = np.frombuffer(data, 'B') + assert_raises(ValueError, vals.setflags, write=True) + types = np.dtype( [('vals', 'u1'), ('res3', 'S4')] ) + values = np.core.records.fromstring(data, types) + vals = values['vals'] + assert_raises(ValueError, vals.setflags, write=True) + + def test_writeable_from_buffer(self): + data = bytearray(b'\x00' * 100) + vals = np.frombuffer(data, 'B') + assert_(vals.flags.writeable) + vals.setflags(write=False) + assert_(vals.flags.writeable is False) + vals.setflags(write=True) + assert_(vals.flags.writeable) + types = np.dtype( [('vals', 'u1'), ('res3', 'S4')] ) + values = np.core.records.fromstring(data, types) + vals = values['vals'] + assert_(vals.flags.writeable) + vals.setflags(write=False) + assert_(vals.flags.writeable is False) + vals.setflags(write=True) + assert_(vals.flags.writeable) + + @pytest.mark.skipif(IS_PYPY, reason="PyPy always copies") + def test_writeable_pickle(self): + import pickle + # Small arrays will be copied without setting base. + # See condition for using PyArray_SetBaseObject in + # array_setstate. + a = np.arange(1000) + for v in range(pickle.HIGHEST_PROTOCOL): + vals = pickle.loads(pickle.dumps(a, v)) + assert_(vals.flags.writeable) + assert_(isinstance(vals.base, bytes)) + + def test_writeable_from_c_data(self): + # Test that the writeable flag can be changed for an array wrapping + # low level C-data, but not owning its data. + # Also see that this is deprecated to change from python. + from numpy.core._multiarray_tests import get_c_wrapping_array + + arr_writeable = get_c_wrapping_array(True) + assert not arr_writeable.flags.owndata + assert arr_writeable.flags.writeable + view = arr_writeable[...] + + # Toggling the writeable flag works on the view: + view.flags.writeable = False + assert not view.flags.writeable + view.flags.writeable = True + assert view.flags.writeable + # Flag can be unset on the arr_writeable: + arr_writeable.flags.writeable = False + + arr_readonly = get_c_wrapping_array(False) + assert not arr_readonly.flags.owndata + assert not arr_readonly.flags.writeable + + for arr in [arr_writeable, arr_readonly]: + view = arr[...] + view.flags.writeable = False # make sure it is readonly + arr.flags.writeable = False + assert not arr.flags.writeable + + with assert_raises(ValueError): + view.flags.writeable = True + + with warnings.catch_warnings(): + warnings.simplefilter("error", DeprecationWarning) + with assert_raises(DeprecationWarning): + arr.flags.writeable = True + + with assert_warns(DeprecationWarning): + arr.flags.writeable = True + + def test_warnonwrite(self): + a = np.arange(10) + a.flags._warn_on_write = True + with warnings.catch_warnings(record=True) as w: + warnings.filterwarnings('always') + a[1] = 10 + a[2] = 10 + # only warn once + assert_(len(w) == 1) + + @pytest.mark.parametrize(["flag", "flag_value", "writeable"], + [("writeable", True, True), + # Delete _warn_on_write after deprecation and simplify + # the parameterization: + ("_warn_on_write", True, False), + ("writeable", False, False)]) + def test_readonly_flag_protocols(self, flag, flag_value, writeable): + a = np.arange(10) + setattr(a.flags, flag, flag_value) + + class MyArr(): + __array_struct__ = a.__array_struct__ + + assert memoryview(a).readonly is not writeable + assert a.__array_interface__['data'][1] is not writeable + assert np.asarray(MyArr()).flags.writeable is writeable + def test_otherflags(self): assert_equal(self.a.flags.carray, True) assert_equal(self.a.flags['C'], True) @@ -108,7 +243,6 @@ def test_otherflags(self): assert_equal(self.a.flags['X'], False) assert_equal(self.a.flags['WRITEBACKIFCOPY'], False) - def test_string_align(self): a = np.zeros(4, dtype=np.dtype('|S4')) assert_(a.flags.aligned) @@ -121,7 +255,7 @@ def test_void_align(self): assert_(a.flags.aligned) -class TestHash(object): +class TestHash: # see #3793 def test_int(self): for st, ut, s in [(np.int8, np.uint8, 8), @@ -143,7 +277,7 @@ def test_int(self): err_msg="%r: 2**%d - 1" % (ut, i)) -class TestAttributes(object): +class TestAttributes: def setup(self): self.one = np.arange(10) self.two = np.arange(20).reshape(4, 5) @@ -184,17 +318,8 @@ def test_int_subclassing(self): numpy_int = np.int_(0) - if sys.version_info[0] >= 3: - # On Py3k int_ should not inherit from int, because it's not - # fixed-width anymore - assert_equal(isinstance(numpy_int, int), False) - else: - # Otherwise, it should inherit from int... - assert_equal(isinstance(numpy_int, int), True) - - # ... and fast-path checks on C-API level should also work - from numpy.core._multiarray_tests import test_int_subclass - assert_equal(test_int_subclass(numpy_int), True) + # int_ doesn't inherit from Python int, because it's not fixed-width + assert_(not isinstance(numpy_int, int)) def test_stridesattr(self): x = self.one @@ -247,6 +372,11 @@ def set_strides(arr, strides): a.strides = 1 a[::2].strides = 2 + # test 0d + arr_0d = np.array(0) + arr_0d.strides = () + assert_raises(TypeError, set_strides, arr_0d, None) + def test_fill(self): for t in "?bhilqpBHILQPfdgFDGO": x = np.empty((3, 2, 1), t) @@ -276,7 +406,7 @@ def test_fill_struct_array(self): assert_array_equal(x['b'], [-2, -2]) -class TestArrayConstruction(object): +class TestArrayConstruction: def test_array(self): d = np.ones(6) r = np.array([d, d]) @@ -303,7 +433,7 @@ def test_array(self): assert_equal(r, np.ones((2, 6, 6))) d = np.ones((6, )) - r = np.array([[d, d + 1], d + 2]) + r = np.array([[d, d + 1], d + 2], dtype=object) assert_equal(len(r), 2) assert_equal(r[0], [d, d + 1]) assert_equal(r[1], d + 2) @@ -353,8 +483,35 @@ def test_array_cont(self): assert_(np.ascontiguousarray(d).flags.c_contiguous) assert_(np.asfortranarray(d).flags.f_contiguous) + @pytest.mark.parametrize("func", + [np.array, + np.asarray, + np.asanyarray, + np.ascontiguousarray, + np.asfortranarray]) + def test_bad_arguments_error(self, func): + with pytest.raises(TypeError): + func(3, dtype="bad dtype") + with pytest.raises(TypeError): + func() # missing arguments + with pytest.raises(TypeError): + func(1, 2, 3, 4, 5, 6, 7, 8) # too many arguments + + @pytest.mark.parametrize("func", + [np.array, + np.asarray, + np.asanyarray, + np.ascontiguousarray, + np.asfortranarray]) + def test_array_as_keyword(self, func): + # This should likely be made positional only, but do not change + # the name accidentally. + if func is np.array: + func(object=3) + else: + func(a=3) -class TestAssignment(object): +class TestAssignment: def test_assignment_broadcasting(self): a = np.arange(6).reshape(2, 3) @@ -420,7 +577,7 @@ def test_stringlike_empty_list(self): u = np.array([u'done']) b = np.array([b'done']) - class bad_sequence(object): + class bad_sequence: def __getitem__(self): pass def __len__(self): raise RuntimeError @@ -471,7 +628,7 @@ def test_cast_to_string(self): assert_equal(a[0], b"1.1234567890123457") -class TestDtypedescr(object): +class TestDtypedescr: def test_construction(self): d1 = np.dtype('i4') assert_equal(d1, np.dtype(np.int32)) @@ -493,7 +650,7 @@ def test_structured_non_void(self): "array([0, 0, 0, 0], dtype=(numpy.int32, [('a', '<i2'), ('b', '<i2')]))") -class TestZeroRank(object): +class TestZeroRank: def setup(self): self.d = np.array(0), np.array('x', object) @@ -570,6 +727,12 @@ def test_constructor(self): y[()] = 6 assert_equal(x[()], 6) + # strides and shape must be the same length + with pytest.raises(ValueError): + np.ndarray((2,), strides=()) + with pytest.raises(ValueError): + np.ndarray((), strides=(2,)) + def test_output(self): x = np.array(2) assert_raises(ValueError, np.add, x, [1], x) @@ -591,7 +754,7 @@ def test_real_imag(self): assert_equal(xi.flags.f_contiguous, True) -class TestScalarIndexing(object): +class TestScalarIndexing: def setup(self): self.d = np.array([0, 1])[0] @@ -687,9 +850,12 @@ def test_overlapping_assignment(self): assert_equal(a, [0, 1, 0, 1, 2]) -class TestCreation(object): +class TestCreation: + """ + Test the np.array constructor + """ def test_from_attribute(self): - class x(object): + class x: def __array__(self, dtype=None): pass @@ -705,7 +871,37 @@ def test_from_string(self): def test_void(self): arr = np.array([], dtype='V') - assert_equal(arr.dtype.kind, 'V') + assert arr.dtype == 'V8' # current default + # Same length scalars (those that go to the same void) work: + arr = np.array([b"1234", b"1234"], dtype="V") + assert arr.dtype == "V4" + + # Promoting different lengths will fail (pre 1.20 this worked) + # by going via S5 and casting to V5. + with pytest.raises(TypeError): + np.array([b"1234", b"12345"], dtype="V") + with pytest.raises(TypeError): + np.array([b"12345", b"1234"], dtype="V") + + # Check the same for the casting path: + arr = np.array([b"1234", b"1234"], dtype="O").astype("V") + assert arr.dtype == "V4" + with pytest.raises(TypeError): + np.array([b"1234", b"12345"], dtype="O").astype("V") + + @pytest.mark.parametrize("idx", + [pytest.param(Ellipsis, id="arr"), pytest.param((), id="scalar")]) + def test_structured_void_promotion(self, idx): + arr = np.array( + [np.array(1, dtype="i,i")[idx], np.array(2, dtype='i,i')[idx]], + dtype="V") + assert_array_equal(arr, np.array([(1, 1), (2, 2)], dtype="i,i")) + # The following fails to promote the two dtypes, resulting in an error + with pytest.raises(TypeError): + np.array( + [np.array(1, dtype="i,i")[idx], np.array(2, dtype='i,i,i')[idx]], + dtype="V") + def test_too_big_error(self): # 45341 is the smallest integer greater than sqrt(2**31 - 1). @@ -722,6 +918,13 @@ def test_too_big_error(self): assert_raises(ValueError, np.zeros, shape, dtype=np.int8) assert_raises(ValueError, np.ones, shape, dtype=np.int8) + @pytest.mark.skipif(np.dtype(np.intp).itemsize != 8, + reason="malloc may not fail on 32 bit systems") + def test_malloc_fails(self): + # This test is guaranteed to fail due to a too large allocation + with assert_raises(np.core._exceptions._ArrayMemoryError): + np.empty(np.iinfo(np.intp).max, dtype=np.uint8) + def test_zeros(self): types = np.typecodes['AllInteger'] + np.typecodes['AllFloat'] for dt in types: @@ -806,7 +1009,7 @@ def test_empty_unicode(self): d = np.empty(i, dtype='U') str(d) - def test_sequence_non_homogenous(self): + def test_sequence_non_homogeneous(self): assert_equal(np.array([4, 2**80]).dtype, object) assert_equal(np.array([4, 2**80, 4]).dtype, object) assert_equal(np.array([2**80, 4]).dtype, object) @@ -815,13 +1018,6 @@ def test_sequence_non_homogenous(self): assert_equal(np.array([[1j, 1j],[1, 1]]).dtype, complex) assert_equal(np.array([[1, 1, 1],[1, 1j, 1.], [1, 1, 1]]).dtype, complex) - @pytest.mark.skipif(sys.version_info[0] >= 3, reason="Not Python 2") - def test_sequence_long(self): - assert_equal(np.array([long(4), long(4)]).dtype, np.long) - assert_equal(np.array([long(4), 2**80]).dtype, object) - assert_equal(np.array([long(4), 2**80, long(4)]).dtype, object) - assert_equal(np.array([2**80, long(4)]).dtype, object) - def test_non_sequence_sequence(self): """Should not segfault. @@ -831,14 +1027,14 @@ def test_non_sequence_sequence(self): of an error in the Fail case. """ - class Fail(object): + class Fail: def __len__(self): return 1 def __getitem__(self, index): raise ValueError() - class Map(object): + class Map: def __len__(self): return 1 @@ -872,11 +1068,28 @@ def __getitem__(self, i): def __len__(self): return 42 - assert_raises(ValueError, np.array, C()) # segfault? + a = np.array(C()) # segfault? + assert_equal(len(a), 0) + + def test_false_len_iterable(self): + # Special case where a bad __getitem__ makes us fall back on __iter__: + class C: + def __getitem__(self, x): + raise Exception + def __iter__(self): + return iter(()) + def __len__(self): + return 2 + + a = np.empty(2) + with assert_raises(ValueError): + a[:] = C() # Segfault! + + np.array(C()) == list(C()) def test_failed_len_sequence(self): # gh-7393 - class A(object): + class A: def __init__(self, data): self._data = data def __getitem__(self, item): @@ -903,8 +1116,62 @@ def test_array_too_big(self): assert_raises(ValueError, np.ndarray, buffer=buf, strides=(0,), shape=(max_bytes//itemsize + 1,), dtype=dtype) - -class TestStructured(object): + def _ragged_creation(self, seq): + # without dtype=object, the ragged object should raise + with assert_warns(np.VisibleDeprecationWarning): + a = np.array(seq) + b = np.array(seq, dtype=object) + assert_equal(a, b) + return b + + def test_ragged_ndim_object(self): + # Lists of mismatching depths are treated as object arrays + a = self._ragged_creation([[1], 2, 3]) + assert_equal(a.shape, (3,)) + assert_equal(a.dtype, object) + + a = self._ragged_creation([1, [2], 3]) + assert_equal(a.shape, (3,)) + assert_equal(a.dtype, object) + + a = self._ragged_creation([1, 2, [3]]) + assert_equal(a.shape, (3,)) + assert_equal(a.dtype, object) + + def test_ragged_shape_object(self): + # The ragged dimension of a list is turned into an object array + a = self._ragged_creation([[1, 1], [2], [3]]) + assert_equal(a.shape, (3,)) + assert_equal(a.dtype, object) + + a = self._ragged_creation([[1], [2, 2], [3]]) + assert_equal(a.shape, (3,)) + assert_equal(a.dtype, object) + + a = self._ragged_creation([[1], [2], [3, 3]]) + assert a.shape == (3,) + assert a.dtype == object + + def test_array_of_ragged_array(self): + outer = np.array([None, None]) + outer[0] = outer[1] = np.array([1, 2, 3]) + assert np.array(outer).shape == (2,) + assert np.array([outer]).shape == (1, 2) + + outer_ragged = np.array([None, None]) + outer_ragged[0] = np.array([1, 2, 3]) + outer_ragged[1] = np.array([1, 2, 3, 4]) + # should both of these emit deprecation warnings? + assert np.array(outer_ragged).shape == (2,) + assert np.array([outer_ragged]).shape == (1, 2,) + + def test_deep_nonragged_object(self): + # None of these should raise, even though they are missing dtype=object + a = np.array([[[Decimal(1)]]]) + a = np.array([1, Decimal(1)]) + a = np.array([[1], [Decimal(1)]]) + +class TestStructured: def test_subarray_field_access(self): a = np.zeros((3, 5), dtype=[('a', ('i4', (2, 2)))]) a['a'] = np.arange(60).reshape(3, 5, 2, 2) @@ -1178,6 +1445,11 @@ def testassign(arr, v): a[['a', 'b']] = a[['b', 'a']] assert_equal(a[0].item(), (2,1)) + def test_scalar_assignment(self): + with assert_raises(ValueError): + arr = np.arange(25).reshape(5, 5) + arr.itemset(3) + def test_structuredscalar_indexing(self): # test gh-7262 x = np.empty(shape=1, dtype="(2)3S,(2)3U") @@ -1191,8 +1463,23 @@ def test_multiindex_titles(self): assert_raises(ValueError, lambda : a[['b','b']]) # field exists, but repeated a[['b','c']] # no exception - -class TestBool(object): + def test_structured_asarray_is_view(self): + # A scalar viewing an array preserves its view even when creating a + # new array. This test documents behaviour, it may not be the best + # desired behaviour. + arr = np.array([1], dtype="i,i") + scalar = arr[0] + assert not scalar.flags.owndata # view into the array + assert np.asarray(scalar).base is scalar + # But never when a dtype is passed in: + assert np.asarray(scalar, dtype=scalar.dtype).base is None + # A scalar which owns its data does not have this property. + # It is not easy to create one, one method is to use pickle: + scalar = pickle.loads(pickle.dumps(scalar)) + assert scalar.flags.owndata + assert np.asarray(scalar).base is None + +class TestBool: def test_test_interning(self): a0 = np.bool_(0) b0 = np.bool_(False) @@ -1282,7 +1569,7 @@ def test_cast_from_bytes(self): self._test_cast_from_flexible(np.bytes_) -class TestZeroSizeFlexible(object): +class TestZeroSizeFlexible: @staticmethod def _zeros(shape, dtype=str): dtype = np.dtype(dtype) @@ -1298,12 +1585,12 @@ def test_create(self): assert_equal(zs.itemsize, 0) zs = self._zeros(10, np.void) assert_equal(zs.itemsize, 0) - zs = self._zeros(10, unicode) + zs = self._zeros(10, str) assert_equal(zs.itemsize, 0) def _test_sort_partition(self, name, kinds, **kwargs): # Previously, these would all hang - for dt in [bytes, np.void, unicode]: + for dt in [bytes, np.void, str]: zs = self._zeros(10, dt) sort_method = getattr(zs, name) sort_func = getattr(np, name) @@ -1312,10 +1599,10 @@ def _test_sort_partition(self, name, kinds, **kwargs): sort_func(zs, kind=kind, **kwargs) def test_sort(self): - self._test_sort_partition('sort', kinds='qhm') + self._test_sort_partition('sort', kinds='qhs') def test_argsort(self): - self._test_sort_partition('argsort', kinds='qhm') + self._test_sort_partition('argsort', kinds='qhs') def test_partition(self): self._test_sort_partition('partition', kinds=['introselect'], kth=2) @@ -1325,13 +1612,13 @@ def test_argpartition(self): def test_resize(self): # previously an error - for dt in [bytes, np.void, unicode]: + for dt in [bytes, np.void, str]: zs = self._zeros(10, dt) zs.resize(25) zs.resize((10, 10)) def test_view(self): - for dt in [bytes, np.void, unicode]: + for dt in [bytes, np.void, str]: zs = self._zeros(10, dt) # viewing as itself should be allowed @@ -1340,17 +1627,80 @@ def test_view(self): # viewing as any non-empty type gives an empty result assert_equal(zs.view((dt, 1)).shape, (0,)) - def test_pickle(self): - import pickle - for dt in [bytes, np.void, unicode]: - zs = self._zeros(10, dt) - p = pickle.dumps(zs) - zs2 = pickle.loads(p) - - assert_equal(zs.dtype, zs2.dtype) + def test_dumps(self): + zs = self._zeros(10, int) + assert_equal(zs, pickle.loads(zs.dumps())) + def test_pickle(self): + for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): + for dt in [bytes, np.void, str]: + zs = self._zeros(10, dt) + p = pickle.dumps(zs, protocol=proto) + zs2 = pickle.loads(p) + + assert_equal(zs.dtype, zs2.dtype) + + @pytest.mark.skipif(pickle.HIGHEST_PROTOCOL < 5, + reason="requires pickle protocol 5") + def test_pickle_with_buffercallback(self): + array = np.arange(10) + buffers = [] + bytes_string = pickle.dumps(array, buffer_callback=buffers.append, + protocol=5) + array_from_buffer = pickle.loads(bytes_string, buffers=buffers) + # when using pickle protocol 5 with buffer callbacks, + # array_from_buffer is reconstructed from a buffer holding a view + # to the initial array's data, so modifying an element in array + # should modify it in array_from_buffer too. + array[0] = -1 + assert array_from_buffer[0] == -1, array_from_buffer[0] + + +class TestMethods: + + sort_kinds = ['quicksort', 'heapsort', 'stable'] + + def test_all_where(self): + a = np.array([[True, False, True], + [False, False, False], + [True, True, True]]) + wh_full = np.array([[True, False, True], + [False, False, False], + [True, False, True]]) + wh_lower = np.array([[False], + [False], + [True]]) + for _ax in [0, None]: + assert_equal(a.all(axis=_ax, where=wh_lower), + np.all(a[wh_lower[:,0],:], axis=_ax)) + assert_equal(np.all(a, axis=_ax, where=wh_lower), + a[wh_lower[:,0],:].all(axis=_ax)) + + assert_equal(a.all(where=wh_full), True) + assert_equal(np.all(a, where=wh_full), True) + assert_equal(a.all(where=False), True) + assert_equal(np.all(a, where=False), True) + + def test_any_where(self): + a = np.array([[True, False, True], + [False, False, False], + [True, True, True]]) + wh_full = np.array([[False, True, False], + [True, True, True], + [False, False, False]]) + wh_middle = np.array([[False], + [True], + [False]]) + for _ax in [0, None]: + assert_equal(a.any(axis=_ax, where=wh_middle), + np.any(a[wh_middle[:,0],:], axis=_ax)) + assert_equal(np.any(a, axis=_ax, where=wh_middle), + a[wh_middle[:,0],:].any(axis=_ax)) + assert_equal(a.any(where=wh_full), False) + assert_equal(np.any(a, where=wh_full), False) + assert_equal(a.any(where=False), False) + assert_equal(np.any(a, where=False), False) -class TestMethods(object): def test_compress(self): tgt = [[5, 6, 7, 8, 9]] arr = np.arange(10).reshape(2, 5) @@ -1386,6 +1736,20 @@ def test_choose(self): A = ind.choose((x, y2)) assert_equal(A, [[2, 2, 3], [2, 2, 3]]) + oned = np.ones(1) + # gh-12031, caused SEGFAULT + assert_raises(TypeError, oned.choose,np.void(0), [oned]) + + out = np.array(0) + ret = np.choose(np.array(1), [10, 20, 30], out=out) + assert out is ret + assert_equal(out[()], 20) + + # gh-6272 check overlap on out + x = np.arange(5) + y = np.choose([0,0,0], [x[:3], x[:3], x[:3]], out=x[1:4], mode='wrap') + assert_equal(y, np.array([0, 1, 2])) + def test_prod(self): ba = [1, 2, 10, 11, 6, 5, 4] ba2 = [[1, 2, 3, 4], [5, 6, 7, 9], [10, 3, 4, 5]] @@ -1457,7 +1821,7 @@ def check_round(arr, expected, *round_args): out = np.zeros_like(arr) res = arr.round(*round_args, out=out) assert_equal(out, expected) - assert_equal(out, res) + assert out is res check_round(np.array([1.2, 1.5]), [1, 2]) check_round(np.array(1.5), 2) @@ -1500,14 +1864,19 @@ def test_sort(self): b = np.sort(a) assert_equal(b, a[::-1], msg) - # all c scalar sorts use the same code with different types - # so it suffices to run a quick check with one type. The number - # of sorted items must be greater than ~50 to check the actual - # algorithm because quick and merge sort fall over to insertion - # sort for small arrays. - a = np.arange(101) + # all c scalar sorts use the same code with different types + # so it suffices to run a quick check with one type. The number + # of sorted items must be greater than ~50 to check the actual + # algorithm because quick and merge sort fall over to insertion + # sort for small arrays. + + @pytest.mark.parametrize('dtype', [np.uint8, np.uint16, np.uint32, np.uint64, + np.float16, np.float32, np.float64, + np.longdouble]) + def test_sort_unsigned(self, dtype): + a = np.arange(101, dtype=dtype) b = a[::-1].copy() - for kind in ['q', 'm', 'h']: + for kind in self.sort_kinds: msg = "scalar sort, kind=%s" % kind c = a.copy() c.sort(kind=kind) @@ -1516,22 +1885,39 @@ def test_sort(self): c.sort(kind=kind) assert_equal(c, a, msg) - # test complex sorts. These use the same code as the scalars - # but the compare function differs. - ai = a*1j + 1 - bi = b*1j + 1 - for kind in ['q', 'm', 'h']: - msg = "complex sort, real part == 1, kind=%s" % kind - c = ai.copy() + @pytest.mark.parametrize('dtype', + [np.int8, np.int16, np.int32, np.int64, np.float16, + np.float32, np.float64, np.longdouble]) + def test_sort_signed(self, dtype): + a = np.arange(-50, 51, dtype=dtype) + b = a[::-1].copy() + for kind in self.sort_kinds: + msg = "scalar sort, kind=%s" % (kind) + c = a.copy() c.sort(kind=kind) - assert_equal(c, ai, msg) - c = bi.copy() + assert_equal(c, a, msg) + c = b.copy() c.sort(kind=kind) - assert_equal(c, ai, msg) - ai = a + 1j - bi = b + 1j - for kind in ['q', 'm', 'h']: - msg = "complex sort, imag part == 1, kind=%s" % kind + assert_equal(c, a, msg) + + @pytest.mark.parametrize('dtype', [np.float32, np.float64, np.longdouble]) + @pytest.mark.parametrize('part', ['real', 'imag']) + def test_sort_complex(self, part, dtype): + # test complex sorts. These use the same code as the scalars + # but the compare function differs. + cdtype = { + np.single: np.csingle, + np.double: np.cdouble, + np.longdouble: np.clongdouble, + }[dtype] + a = np.arange(-50, 51, dtype=dtype) + b = a[::-1].copy() + ai = (a * (1+1j)).astype(cdtype) + bi = (b * (1+1j)).astype(cdtype) + setattr(ai, part, 1) + setattr(bi, part, 1) + for kind in self.sort_kinds: + msg = "complex sort, %s part == 1, kind=%s" % (part, kind) c = ai.copy() c.sort(kind=kind) assert_equal(c, ai, msg) @@ -1539,6 +1925,7 @@ def test_sort(self): c.sort(kind=kind) assert_equal(c, ai, msg) + def test_sort_complex_byte_swapping(self): # test sorting of complex arrays requiring byte-swapping, gh-5441 for endianness in '<>': for dt in np.typecodes['Complex']: @@ -1548,25 +1935,13 @@ def test_sort(self): msg = 'byte-swapped complex sort, dtype={0}'.format(dt) assert_equal(c, arr, msg) - # test string sorts. - s = 'aaaaaaaa' - a = np.array([s + chr(i) for i in range(101)]) - b = a[::-1].copy() - for kind in ['q', 'm', 'h']: - msg = "string sort, kind=%s" % kind - c = a.copy() - c.sort(kind=kind) - assert_equal(c, a, msg) - c = b.copy() - c.sort(kind=kind) - assert_equal(c, a, msg) - - # test unicode sorts. - s = 'aaaaaaaa' - a = np.array([s + chr(i) for i in range(101)], dtype=np.unicode) + @pytest.mark.parametrize('dtype', [np.bytes_, np.unicode_]) + def test_sort_string(self, dtype): + # np.array will perform the encoding to bytes for us in the bytes test + a = np.array(['aaaaaaaa' + chr(i) for i in range(101)], dtype=dtype) b = a[::-1].copy() - for kind in ['q', 'm', 'h']: - msg = "unicode sort, kind=%s" % kind + for kind in self.sort_kinds: + msg = "kind=%s" % kind c = a.copy() c.sort(kind=kind) assert_equal(c, a, msg) @@ -1574,12 +1949,13 @@ def test_sort(self): c.sort(kind=kind) assert_equal(c, a, msg) + def test_sort_object(self): # test object array sorts. a = np.empty((101,), dtype=object) a[:] = list(range(101)) b = a[::-1] for kind in ['q', 'h', 'm']: - msg = "object sort, kind=%s" % kind + msg = "kind=%s" % kind c = a.copy() c.sort(kind=kind) assert_equal(c, a, msg) @@ -1587,24 +1963,13 @@ def test_sort(self): c.sort(kind=kind) assert_equal(c, a, msg) + def test_sort_structured(self): # test record array sorts. dt = np.dtype([('f', float), ('i', int)]) a = np.array([(i, i) for i in range(101)], dtype=dt) b = a[::-1] for kind in ['q', 'h', 'm']: - msg = "object sort, kind=%s" % kind - c = a.copy() - c.sort(kind=kind) - assert_equal(c, a, msg) - c = b.copy() - c.sort(kind=kind) - assert_equal(c, a, msg) - - # test datetime64 sorts. - a = np.arange(0, 101, dtype='datetime64[D]') - b = a[::-1] - for kind in ['q', 'h', 'm']: - msg = "datetime64 sort, kind=%s" % kind + msg = "kind=%s" % kind c = a.copy() c.sort(kind=kind) assert_equal(c, a, msg) @@ -1612,11 +1977,13 @@ def test_sort(self): c.sort(kind=kind) assert_equal(c, a, msg) - # test timedelta64 sorts. - a = np.arange(0, 101, dtype='timedelta64[D]') + @pytest.mark.parametrize('dtype', ['datetime64[D]', 'timedelta64[D]']) + def test_sort_time(self, dtype): + # test datetime64 and timedelta64 sorts. + a = np.arange(0, 101, dtype=dtype) b = a[::-1] for kind in ['q', 'h', 'm']: - msg = "timedelta64 sort, kind=%s" % kind + msg = "kind=%s" % kind c = a.copy() c.sort(kind=kind) assert_equal(c, a, msg) @@ -1624,6 +1991,7 @@ def test_sort(self): c.sort(kind=kind) assert_equal(c, a, msg) + def test_sort_axis(self): # check axis handling. This should be the same for all type # specific sorts, so we only check it for one type and one kind a = np.array([[3, 2], [1, 0]]) @@ -1639,6 +2007,7 @@ def test_sort(self): d.sort() assert_equal(d, c, "test sort with default axis") + def test_sort_size_0(self): # check axis handling for multidimensional empty arrays a = np.array([]) a.shape = (3, 2, 1, 0) @@ -1648,16 +2017,19 @@ def test_sort(self): msg = 'test empty array sort with axis=None' assert_equal(np.sort(a, axis=None), a.ravel(), msg) + def test_sort_bad_ordering(self): # test generic class with bogus ordering, # should not segfault. - class Boom(object): + class Boom: def __lt__(self, other): return True - a = np.array([Boom()]*100, dtype=object) - for kind in ['q', 'm', 'h']: - msg = "bogus comparison object sort, kind=%s" % kind + a = np.array([Boom()] * 100, dtype=object) + for kind in self.sort_kinds: + msg = "kind=%s" % kind + c = a.copy() c.sort(kind=kind) + assert_equal(c, a, msg) def test_void_sort(self): # gh-8210 - previously segfaulted @@ -1675,16 +2047,16 @@ def test_void_sort(self): def test_sort_raises(self): #gh-9404 arr = np.array([0, datetime.now(), 1], dtype=object) - for kind in ['q', 'm', 'h']: + for kind in self.sort_kinds: assert_raises(TypeError, arr.sort, kind=kind) #gh-3879 - class Raiser(object): + class Raiser: def raises_anything(*args, **kwargs): raise TypeError("SOMETHING ERRORED") __eq__ = __ne__ = __lt__ = __gt__ = __ge__ = __le__ = raises_anything arr = np.array([[Raiser(), n] for n in range(10)]).reshape(-1) np.random.shuffle(arr) - for kind in ['q', 'm', 'h']: + for kind in self.sort_kinds: assert_raises(TypeError, arr.sort, kind=kind) def test_sort_degraded(self): @@ -1756,7 +2128,7 @@ def test_sort_order(self): strtype = '>i2' else: strtype = '<i2' - mydtype = [('name', strchar + '5'), ('col2', strtype)] + mydtype = [('name', 'U5'), ('col2', strtype)] r = np.array([('a', 1), ('b', 255), ('c', 3), ('d', 258)], dtype=mydtype) r.sort(order='col2') @@ -1770,24 +2142,26 @@ def test_argsort(self): # of sorted items must be greater than ~50 to check the actual # algorithm because quick and merge sort fall over to insertion # sort for small arrays. - a = np.arange(101) - b = a[::-1].copy() - for kind in ['q', 'm', 'h']: - msg = "scalar argsort, kind=%s" % kind - assert_equal(a.copy().argsort(kind=kind), a, msg) - assert_equal(b.copy().argsort(kind=kind), b, msg) + + for dtype in [np.int32, np.uint32, np.float32]: + a = np.arange(101, dtype=dtype) + b = a[::-1].copy() + for kind in self.sort_kinds: + msg = "scalar argsort, kind=%s, dtype=%s" % (kind, dtype) + assert_equal(a.copy().argsort(kind=kind), a, msg) + assert_equal(b.copy().argsort(kind=kind), b, msg) # test complex argsorts. These use the same code as the scalars # but the compare function differs. ai = a*1j + 1 bi = b*1j + 1 - for kind in ['q', 'm', 'h']: + for kind in self.sort_kinds: msg = "complex argsort, kind=%s" % kind assert_equal(ai.copy().argsort(kind=kind), a, msg) assert_equal(bi.copy().argsort(kind=kind), b, msg) ai = a + 1j bi = b + 1j - for kind in ['q', 'm', 'h']: + for kind in self.sort_kinds: msg = "complex argsort, kind=%s" % kind assert_equal(ai.copy().argsort(kind=kind), a, msg) assert_equal(bi.copy().argsort(kind=kind), b, msg) @@ -1806,18 +2180,18 @@ def test_argsort(self): b = a[::-1].copy() r = np.arange(101) rr = r[::-1] - for kind in ['q', 'm', 'h']: + for kind in self.sort_kinds: msg = "string argsort, kind=%s" % kind assert_equal(a.copy().argsort(kind=kind), r, msg) assert_equal(b.copy().argsort(kind=kind), rr, msg) # test unicode argsorts. s = 'aaaaaaaa' - a = np.array([s + chr(i) for i in range(101)], dtype=np.unicode) + a = np.array([s + chr(i) for i in range(101)], dtype=np.unicode_) b = a[::-1] r = np.arange(101) rr = r[::-1] - for kind in ['q', 'm', 'h']: + for kind in self.sort_kinds: msg = "unicode argsort, kind=%s" % kind assert_equal(a.copy().argsort(kind=kind), r, msg) assert_equal(b.copy().argsort(kind=kind), rr, msg) @@ -1828,7 +2202,7 @@ def test_argsort(self): b = a[::-1] r = np.arange(101) rr = r[::-1] - for kind in ['q', 'm', 'h']: + for kind in self.sort_kinds: msg = "object argsort, kind=%s" % kind assert_equal(a.copy().argsort(kind=kind), r, msg) assert_equal(b.copy().argsort(kind=kind), rr, msg) @@ -1839,7 +2213,7 @@ def test_argsort(self): b = a[::-1] r = np.arange(101) rr = r[::-1] - for kind in ['q', 'm', 'h']: + for kind in self.sort_kinds: msg = "structured array argsort, kind=%s" % kind assert_equal(a.copy().argsort(kind=kind), r, msg) assert_equal(b.copy().argsort(kind=kind), rr, msg) @@ -1896,7 +2270,7 @@ def test_argsort(self): a = np.array(['aaaaaaaaa' for i in range(100)]) assert_equal(a.argsort(kind='m'), r) # unicode - a = np.array(['aaaaaaaaa' for i in range(100)], dtype=np.unicode) + a = np.array(['aaaaaaaaa' for i in range(100)], dtype=np.unicode_) assert_equal(a.argsort(kind='m'), r) def test_sort_unicode_kind(self): @@ -1915,20 +2289,22 @@ def test_searchsorted(self): # check double a = np.array([0, 1, np.nan]) msg = "Test real searchsorted with nans, side='l'" - b = a.searchsorted(a, side='l') + b = a.searchsorted(a, side='left') assert_equal(b, np.arange(3), msg) msg = "Test real searchsorted with nans, side='r'" - b = a.searchsorted(a, side='r') + b = a.searchsorted(a, side='right') assert_equal(b, np.arange(1, 4), msg) + # check keyword arguments + a.searchsorted(v=1) # check double complex a = np.zeros(9, dtype=np.complex128) a.real += [0, 0, 1, 1, 0, 1, np.nan, np.nan, np.nan] a.imag += [0, 1, 0, 1, np.nan, np.nan, 0, 1, np.nan] msg = "Test complex searchsorted with nans, side='l'" - b = a.searchsorted(a, side='l') + b = a.searchsorted(a, side='left') assert_equal(b, np.arange(9), msg) msg = "Test complex searchsorted with nans, side='r'" - b = a.searchsorted(a, side='r') + b = a.searchsorted(a, side='right') assert_equal(b, np.arange(1, 10), msg) msg = "Test searchsorted with little endian, side='l'" a = np.array([0, 128], dtype='<i4') @@ -1941,21 +2317,21 @@ def test_searchsorted(self): # Check 0 elements a = np.ones(0) - b = a.searchsorted([0, 1, 2], 'l') + b = a.searchsorted([0, 1, 2], 'left') assert_equal(b, [0, 0, 0]) - b = a.searchsorted([0, 1, 2], 'r') + b = a.searchsorted([0, 1, 2], 'right') assert_equal(b, [0, 0, 0]) a = np.ones(1) # Check 1 element - b = a.searchsorted([0, 1, 2], 'l') + b = a.searchsorted([0, 1, 2], 'left') assert_equal(b, [0, 0, 1]) - b = a.searchsorted([0, 1, 2], 'r') + b = a.searchsorted([0, 1, 2], 'right') assert_equal(b, [0, 1, 1]) # Check all elements equal a = np.ones(2) - b = a.searchsorted([0, 1, 2], 'l') + b = a.searchsorted([0, 1, 2], 'left') assert_equal(b, [0, 0, 2]) - b = a.searchsorted([0, 1, 2], 'r') + b = a.searchsorted([0, 1, 2], 'right') assert_equal(b, [0, 2, 2]) # Test searching unaligned array @@ -1964,21 +2340,21 @@ def test_searchsorted(self): unaligned = aligned[1:].view(a.dtype) unaligned[:] = a # Test searching unaligned array - b = unaligned.searchsorted(a, 'l') + b = unaligned.searchsorted(a, 'left') assert_equal(b, a) - b = unaligned.searchsorted(a, 'r') + b = unaligned.searchsorted(a, 'right') assert_equal(b, a + 1) # Test searching for unaligned keys - b = a.searchsorted(unaligned, 'l') + b = a.searchsorted(unaligned, 'left') assert_equal(b, a) - b = a.searchsorted(unaligned, 'r') + b = a.searchsorted(unaligned, 'right') assert_equal(b, a + 1) # Test smart resetting of binsearch indices a = np.arange(5) - b = a.searchsorted([6, 5, 4], 'l') + b = a.searchsorted([6, 5, 4], 'left') assert_equal(b, [5, 5, 4]) - b = a.searchsorted([6, 5, 4], 'r') + b = a.searchsorted([6, 5, 4], 'right') assert_equal(b, [5, 5, 5]) # Test all type specific binary search functions @@ -1993,16 +2369,16 @@ def test_searchsorted(self): else: a = np.arange(0, 5, dtype=dt) out = np.arange(5) - b = a.searchsorted(a, 'l') + b = a.searchsorted(a, 'left') assert_equal(b, out) - b = a.searchsorted(a, 'r') + b = a.searchsorted(a, 'right') assert_equal(b, out + 1) # Test empty array, use a fresh array to get warnings in # valgrind if access happens. e = np.ndarray(shape=0, buffer=b'', dtype=dt) - b = e.searchsorted(a, 'l') + b = e.searchsorted(a, 'left') assert_array_equal(b, np.zeros(len(a), dtype=np.intp)) - b = a.searchsorted(e, 'l') + b = a.searchsorted(e, 'left') assert_array_equal(b, np.zeros(0, dtype=np.intp)) def test_searchsorted_unicode(self): @@ -2025,17 +2401,18 @@ def test_searchsorted_unicode(self): 'P:\\20x_dapi_cy3\\20x_dapi_cy3_20100197_1', 'P:\\20x_dapi_cy3\\20x_dapi_cy3_20100198_1', 'P:\\20x_dapi_cy3\\20x_dapi_cy3_20100199_1'], - dtype=np.unicode) + dtype=np.unicode_) ind = np.arange(len(a)) assert_equal([a.searchsorted(v, 'left') for v in a], ind) assert_equal([a.searchsorted(v, 'right') for v in a], ind + 1) assert_equal([a.searchsorted(a[i], 'left') for i in ind], ind) assert_equal([a.searchsorted(a[i], 'right') for i in ind], ind + 1) - def test_searchsorted_with_sorter(self): + def test_searchsorted_with_invalid_sorter(self): a = np.array([5, 2, 1, 3, 4]) s = np.argsort(a) - assert_raises(TypeError, np.searchsorted, a, 0, sorter=(1, (2, 3))) + assert_raises(TypeError, np.searchsorted, a, 0, + sorter=np.array((1, (2, 3)), dtype=object)) assert_raises(TypeError, np.searchsorted, a, 0, sorter=[1.1]) assert_raises(ValueError, np.searchsorted, a, 0, sorter=[1, 2, 3, 4]) assert_raises(ValueError, np.searchsorted, a, 0, sorter=[1, 2, 3, 4, 5, 6]) @@ -2045,6 +2422,7 @@ def test_searchsorted_with_sorter(self): assert_raises(ValueError, np.searchsorted, a, 0, sorter=[-1, 0, 1, 2, 3]) assert_raises(ValueError, np.searchsorted, a, 0, sorter=[4, 0, -1, 2, 3]) + def test_searchsorted_with_sorter(self): a = np.random.rand(300) s = a.argsort() b = np.sort(a) @@ -2055,9 +2433,9 @@ def test_searchsorted_with_sorter(self): s = a.argsort() k = [0, 1, 2, 3, 5] expected = [0, 20, 40, 60, 80] - assert_equal(a.searchsorted(k, side='l', sorter=s), expected) + assert_equal(a.searchsorted(k, side='left', sorter=s), expected) expected = [20, 40, 60, 80, 100] - assert_equal(a.searchsorted(k, side='r', sorter=s), expected) + assert_equal(a.searchsorted(k, side='right', sorter=s), expected) # Test searching unaligned array keys = np.arange(10) @@ -2068,15 +2446,15 @@ def test_searchsorted_with_sorter(self): unaligned = aligned[1:].view(a.dtype) # Test searching unaligned array unaligned[:] = a - b = unaligned.searchsorted(keys, 'l', s) + b = unaligned.searchsorted(keys, 'left', s) assert_equal(b, keys) - b = unaligned.searchsorted(keys, 'r', s) + b = unaligned.searchsorted(keys, 'right', s) assert_equal(b, keys + 1) # Test searching for unaligned keys unaligned[:] = keys - b = a.searchsorted(unaligned, 'l', s) + b = a.searchsorted(unaligned, 'left', s) assert_equal(b, keys) - b = a.searchsorted(unaligned, 'r', s) + b = a.searchsorted(unaligned, 'right', s) assert_equal(b, keys + 1) # Test all type specific indirect binary search functions @@ -2097,16 +2475,16 @@ def test_searchsorted_with_sorter(self): # from np.intp in all platforms, to check for #4698 s = np.array([4, 2, 3, 0, 1], dtype=np.int16) out = np.array([3, 4, 1, 2, 0], dtype=np.intp) - b = a.searchsorted(a, 'l', s) + b = a.searchsorted(a, 'left', s) assert_equal(b, out) - b = a.searchsorted(a, 'r', s) + b = a.searchsorted(a, 'right', s) assert_equal(b, out + 1) # Test empty array, use a fresh array to get warnings in # valgrind if access happens. e = np.ndarray(shape=0, buffer=b'', dtype=dt) - b = e.searchsorted(a, 'l', s[:0]) + b = e.searchsorted(a, 'left', s[:0]) assert_array_equal(b, np.zeros(len(a), dtype=np.intp)) - b = a.searchsorted(e, 'l', s) + b = a.searchsorted(e, 'left', s) assert_array_equal(b, np.zeros(0, dtype=np.intp)) # Test non-contiguous sorter array @@ -2116,9 +2494,9 @@ def test_searchsorted_with_sorter(self): srt[::2] = [4, 2, 3, 0, 1] s = srt[::2] out = np.array([3, 4, 1, 2, 0], dtype=np.intp) - b = a.searchsorted(a, 'l', s) + b = a.searchsorted(a, 'left', s) assert_equal(b, out) - b = a.searchsorted(a, 'r', s) + b = a.searchsorted(a, 'right', s) assert_equal(b, out + 1) def test_searchsorted_return_type(self): @@ -2128,32 +2506,24 @@ class A(np.ndarray): a = np.arange(5).view(A) b = np.arange(1, 3).view(A) s = np.arange(5).view(A) - assert_(not isinstance(a.searchsorted(b, 'l'), A)) - assert_(not isinstance(a.searchsorted(b, 'r'), A)) - assert_(not isinstance(a.searchsorted(b, 'l', s), A)) - assert_(not isinstance(a.searchsorted(b, 'r', s), A)) + assert_(not isinstance(a.searchsorted(b, 'left'), A)) + assert_(not isinstance(a.searchsorted(b, 'right'), A)) + assert_(not isinstance(a.searchsorted(b, 'left', s), A)) + assert_(not isinstance(a.searchsorted(b, 'right', s), A)) - def test_argpartition_out_of_range(self): + @pytest.mark.parametrize("dtype", np.typecodes["All"]) + def test_argpartition_out_of_range(self, dtype): # Test out of range values in kth raise an error, gh-5469 - d = np.arange(10) + d = np.arange(10).astype(dtype=dtype) assert_raises(ValueError, d.argpartition, 10) assert_raises(ValueError, d.argpartition, -11) - # Test also for generic type argpartition, which uses sorting - # and used to not bound check kth - d_obj = np.arange(10, dtype=object) - assert_raises(ValueError, d_obj.argpartition, 10) - assert_raises(ValueError, d_obj.argpartition, -11) - def test_partition_out_of_range(self): + @pytest.mark.parametrize("dtype", np.typecodes["All"]) + def test_partition_out_of_range(self, dtype): # Test out of range values in kth raise an error, gh-5469 - d = np.arange(10) + d = np.arange(10).astype(dtype=dtype) assert_raises(ValueError, d.partition, 10) assert_raises(ValueError, d.partition, -11) - # Test also for generic type partition, which uses sorting - # and used to not bound check kth - d_obj = np.arange(10, dtype=object) - assert_raises(ValueError, d_obj.partition, 10) - assert_raises(ValueError, d_obj.partition, -11) def test_argpartition_integer(self): # Test non-integer values in kth raise an error/ @@ -2173,26 +2543,30 @@ def test_partition_integer(self): d_obj = np.arange(10, dtype=object) assert_raises(TypeError, d_obj.partition, 9.) - def test_partition_empty_array(self): + @pytest.mark.parametrize("kth_dtype", np.typecodes["AllInteger"]) + def test_partition_empty_array(self, kth_dtype): # check axis handling for multidimensional empty arrays + kth = np.array(0, dtype=kth_dtype)[()] a = np.array([]) a.shape = (3, 2, 1, 0) for axis in range(-a.ndim, a.ndim): msg = 'test empty array partition with axis={0}'.format(axis) - assert_equal(np.partition(a, 0, axis=axis), a, msg) + assert_equal(np.partition(a, kth, axis=axis), a, msg) msg = 'test empty array partition with axis=None' - assert_equal(np.partition(a, 0, axis=None), a.ravel(), msg) + assert_equal(np.partition(a, kth, axis=None), a.ravel(), msg) - def test_argpartition_empty_array(self): + @pytest.mark.parametrize("kth_dtype", np.typecodes["AllInteger"]) + def test_argpartition_empty_array(self, kth_dtype): # check axis handling for multidimensional empty arrays + kth = np.array(0, dtype=kth_dtype)[()] a = np.array([]) a.shape = (3, 2, 1, 0) for axis in range(-a.ndim, a.ndim): msg = 'test empty array argpartition with axis={0}'.format(axis) - assert_equal(np.partition(a, 0, axis=axis), + assert_equal(np.partition(a, kth, axis=axis), np.zeros_like(a, dtype=np.intp), msg) msg = 'test empty array argpartition with axis=None' - assert_equal(np.partition(a, 0, axis=None), + assert_equal(np.partition(a, kth, axis=None), np.zeros_like(a.ravel(), dtype=np.intp), msg) def test_partition(self): @@ -2523,10 +2897,12 @@ def test_partition_fuzz(self): assert_array_equal(np.partition(d, kth)[kth], tgt, err_msg="data: %r\n kth: %r" % (d, kth)) - def test_argpartition_gh5524(self): + @pytest.mark.parametrize("kth_dtype", np.typecodes["AllInteger"]) + def test_argpartition_gh5524(self, kth_dtype): # A test for functionality of argpartition on lists. - d = [6,7,3,2,9,0] - p = np.argpartition(d,1) + kth = np.array(1, dtype=kth_dtype)[()] + d = [6, 7, 3, 2, 9, 0] + p = np.argpartition(d, kth) self.assert_partitioned(np.array(d)[p],[1]) def test_flatten(self): @@ -2543,7 +2919,9 @@ def test_flatten(self): assert_equal(x1.flatten('F'), y1f) assert_equal(x1.flatten('F'), x1.T.flatten()) - def test_dot(self): + + @pytest.mark.parametrize('func', (np.dot, np.matmul)) + def test_arr_mult(self, func): a = np.array([[1, 0], [0, 1]]) b = np.array([[0, 1], [1, 0]]) c = np.array([[9, 1], [1, -9]]) @@ -2567,49 +2945,49 @@ def test_dot(self): # gemm vs syrk optimizations for et in [np.float32, np.float64, np.complex64, np.complex128]: eaf = a.astype(et) - assert_equal(np.dot(eaf, eaf), eaf) - assert_equal(np.dot(eaf.T, eaf), eaf) - assert_equal(np.dot(eaf, eaf.T), eaf) - assert_equal(np.dot(eaf.T, eaf.T), eaf) - assert_equal(np.dot(eaf.T.copy(), eaf), eaf) - assert_equal(np.dot(eaf, eaf.T.copy()), eaf) - assert_equal(np.dot(eaf.T.copy(), eaf.T.copy()), eaf) + assert_equal(func(eaf, eaf), eaf) + assert_equal(func(eaf.T, eaf), eaf) + assert_equal(func(eaf, eaf.T), eaf) + assert_equal(func(eaf.T, eaf.T), eaf) + assert_equal(func(eaf.T.copy(), eaf), eaf) + assert_equal(func(eaf, eaf.T.copy()), eaf) + assert_equal(func(eaf.T.copy(), eaf.T.copy()), eaf) # syrk validations for et in [np.float32, np.float64, np.complex64, np.complex128]: eaf = a.astype(et) ebf = b.astype(et) - assert_equal(np.dot(ebf, ebf), eaf) - assert_equal(np.dot(ebf.T, ebf), eaf) - assert_equal(np.dot(ebf, ebf.T), eaf) - assert_equal(np.dot(ebf.T, ebf.T), eaf) + assert_equal(func(ebf, ebf), eaf) + assert_equal(func(ebf.T, ebf), eaf) + assert_equal(func(ebf, ebf.T), eaf) + assert_equal(func(ebf.T, ebf.T), eaf) # syrk - different shape, stride, and view validations for et in [np.float32, np.float64, np.complex64, np.complex128]: edf = d.astype(et) assert_equal( - np.dot(edf[::-1, :], edf.T), - np.dot(edf[::-1, :].copy(), edf.T.copy()) + func(edf[::-1, :], edf.T), + func(edf[::-1, :].copy(), edf.T.copy()) ) assert_equal( - np.dot(edf[:, ::-1], edf.T), - np.dot(edf[:, ::-1].copy(), edf.T.copy()) + func(edf[:, ::-1], edf.T), + func(edf[:, ::-1].copy(), edf.T.copy()) ) assert_equal( - np.dot(edf, edf[::-1, :].T), - np.dot(edf, edf[::-1, :].T.copy()) + func(edf, edf[::-1, :].T), + func(edf, edf[::-1, :].T.copy()) ) assert_equal( - np.dot(edf, edf[:, ::-1].T), - np.dot(edf, edf[:, ::-1].T.copy()) + func(edf, edf[:, ::-1].T), + func(edf, edf[:, ::-1].T.copy()) ) assert_equal( - np.dot(edf[:edf.shape[0] // 2, :], edf[::2, :].T), - np.dot(edf[:edf.shape[0] // 2, :].copy(), edf[::2, :].T.copy()) + func(edf[:edf.shape[0] // 2, :], edf[::2, :].T), + func(edf[:edf.shape[0] // 2, :].copy(), edf[::2, :].T.copy()) ) assert_equal( - np.dot(edf[::2, :], edf[:edf.shape[0] // 2, :].T), - np.dot(edf[::2, :].copy(), edf[:edf.shape[0] // 2, :].T.copy()) + func(edf[::2, :], edf[:edf.shape[0] // 2, :].T), + func(edf[::2, :].copy(), edf[:edf.shape[0] // 2, :].T.copy()) ) # syrk - different shape @@ -2617,9 +2995,43 @@ def test_dot(self): edf = d.astype(et) eddtf = ddt.astype(et) edtdf = dtd.astype(et) - assert_equal(np.dot(edf, edf.T), eddtf) - assert_equal(np.dot(edf.T, edf), edtdf) + assert_equal(func(edf, edf.T), eddtf) + assert_equal(func(edf.T, edf), edtdf) + + @pytest.mark.parametrize('func', (np.dot, np.matmul)) + @pytest.mark.parametrize('dtype', 'ifdFD') + def test_no_dgemv(self, func, dtype): + # check vector arg for contiguous before gemv + # gh-12156 + a = np.arange(8.0, dtype=dtype).reshape(2, 4) + b = np.broadcast_to(1., (4, 1)) + ret1 = func(a, b) + ret2 = func(a, b.copy()) + assert_equal(ret1, ret2) + + ret1 = func(b.T, a.T) + ret2 = func(b.T.copy(), a.T) + assert_equal(ret1, ret2) + + # check for unaligned data + dt = np.dtype(dtype) + a = np.zeros(8 * dt.itemsize // 2 + 1, dtype='int16')[1:].view(dtype) + a = a.reshape(2, 4) + b = a[0] + # make sure it is not aligned + assert_(a.__array_interface__['data'][0] % dt.itemsize != 0) + ret1 = func(a, b) + ret2 = func(a.copy(), b.copy()) + assert_equal(ret1, ret2) + + ret1 = func(b.T, a.T) + ret2 = func(b.T.copy(), a.T.copy()) + assert_equal(ret1, ret2) + def test_dot(self): + a = np.array([[1, 0], [0, 1]]) + b = np.array([[0, 1], [1, 0]]) + c = np.array([[9, 1], [1, -9]]) # function versus methods assert_equal(np.dot(a, b), a.dot(b)) assert_equal(np.dot(np.dot(a, b), c), a.dot(b).dot(c)) @@ -2675,6 +3087,29 @@ class Sub(np.ndarray): np.dot(a, b, out=out) np.matmul(a, b, out=out) + def test_dot_matmul_inner_array_casting_fails(self): + + class A: + def __array__(self, *args, **kwargs): + raise NotImplementedError + + # Don't override the error from calling __array__() + assert_raises(NotImplementedError, np.dot, A(), A()) + assert_raises(NotImplementedError, np.matmul, A(), A()) + assert_raises(NotImplementedError, np.inner, A(), A()) + + def test_matmul_out(self): + # overlapping memory + a = np.arange(18).reshape(2, 3, 3) + b = np.matmul(a, a) + c = np.matmul(a, a, out=a) + assert_(c is a) + assert_equal(c, b) + a = np.arange(18).reshape(2, 3, 3) + c = np.matmul(a, a, out=a[::-1, ...]) + assert_(c.base is a.base) + assert_equal(c, b) + def test_diagonal(self): a = np.arange(12).reshape((3, 4)) assert_equal(a.diagonal(), [0, 5, 10]) @@ -2698,10 +3133,7 @@ def test_diagonal(self): # Order of axis argument doesn't matter: assert_equal(b.diagonal(0, 2, 1), [[0, 3], [4, 7]]) - def test_diagonal_view_notwriteable(self): - # this test is only for 1.9, the diagonal view will be - # writeable in 1.10. a = np.eye(3).diagonal() assert_(not a.flags.writeable) assert_(not a.flags.owndata) @@ -2752,6 +3184,10 @@ def test_trace(self): assert_equal(b.trace(0, 1, 2), [3, 11]) assert_equal(b.trace(offset=1, axis1=0, axis2=2), [1, 3]) + out = np.array(1) + ret = a.trace(out=out) + assert ret is out + def test_trace_subclass(self): # The class would need to overwrite trace to ensure single-element # output also has the right subclass. @@ -2975,8 +3411,17 @@ def test_conjugate(self): assert_equal(ac, np.conjugate(a)) a = np.array([1-1j, 1, 2.0, 'f'], object) - assert_raises(AttributeError, lambda: a.conj()) - assert_raises(AttributeError, lambda: a.conjugate()) + assert_raises(TypeError, lambda: a.conj()) + assert_raises(TypeError, lambda: a.conjugate()) + + def test_conjugate_out(self): + # Minimal test for the out argument being passed on correctly + # NOTE: The ability to pass `out` is currently undocumented! + a = np.array([1-1j, 1+1j, 23+23.0j]) + out = np.empty_like(a) + res = a.conjugate(out) + assert res is out + assert_array_equal(out, a.conjugate()) def test__complex__(self): dtypes = ['i1', 'i2', 'i4', 'i8', @@ -3018,12 +3463,12 @@ def test__complex__should_not_work(self): e = np.array(['1+1j'], 'U') assert_raises(TypeError, complex, e) -class TestCequenceMethods(object): +class TestCequenceMethods: def test_array_contains(self): assert_(4.0 in np.arange(16.).reshape(4,4)) assert_(20.0 not in np.arange(16.).reshape(4,4)) -class TestBinop(object): +class TestBinop: def test_inplace(self): # test refcount 1 inplace conversion assert_array_almost_equal(np.array([0.5]) * np.array([1.0, 2.0]), @@ -3078,6 +3523,7 @@ def test_ufunc_binop_interaction(self): 'and': (np.bitwise_and, True, int), 'xor': (np.bitwise_xor, True, int), 'or': (np.bitwise_or, True, int), + 'matmul': (np.matmul, False, float), # 'ge': (np.less_equal, False), # 'gt': (np.less, False), # 'le': (np.greater_equal, False), @@ -3127,7 +3573,7 @@ def make_obj(base, array_priority=False, array_ufunc=False, if issubclass(MyType, np.ndarray): # Use this range to avoid special case weirdnesses around # divide-by-0, pow(x, 2), overflow due to pow(big, big), etc. - return np.arange(3, 5).view(MyType) + return np.arange(3, 7).reshape(2, 2).view(MyType) else: return MyType() @@ -3136,7 +3582,7 @@ def check(obj, binop_override_expected, ufunc_override_expected, for op, (ufunc, has_inplace, dtype) in ops.items(): err_msg = ('op: %s, ufunc: %s, has_inplace: %s, dtype: %s' % (op, ufunc, has_inplace, dtype)) - check_objs = [np.arange(3, 5, dtype=dtype)] + check_objs = [np.arange(3, 7, dtype=dtype).reshape(2, 2)] if check_scalar: check_objs.append(check_objs[0][0]) for arr in check_objs: @@ -3246,7 +3692,7 @@ def first_out_arg(result): def test_ufunc_override_normalize_signature(self): # gh-5674 - class SomeClass(object): + class SomeClass: def __array_ufunc__(self, ufunc, method, *inputs, **kw): return kw @@ -3264,7 +3710,7 @@ def test_array_ufunc_index(self): # Check that index is set appropriately, also if only an output # is passed on (latter is another regression tests for github bug 4753) # This also checks implicitly that 'out' is always a tuple. - class CheckIndex(object): + class CheckIndex: def __array_ufunc__(self, ufunc, method, *inputs, **kw): for i, a in enumerate(inputs): if a is self: @@ -3293,10 +3739,10 @@ def __array_ufunc__(self, ufunc, method, *inputs, **kw): assert_equal(np.modf(dummy, out=(None, a)), (1,)) assert_equal(np.modf(dummy, out=(dummy, a)), (1,)) assert_equal(np.modf(a, out=(dummy, a)), 0) - with warnings.catch_warnings(record=True) as w: - warnings.filterwarnings('always', '', DeprecationWarning) - assert_equal(np.modf(dummy, out=a), (0,)) - assert_(w[0].category is DeprecationWarning) + with assert_raises(TypeError): + # Out argument must be tuple, since there are multiple outputs + np.modf(dummy, out=a) + assert_raises(ValueError, np.modf, dummy, out=(a,)) # 2 inputs, 1 output @@ -3352,7 +3798,7 @@ def __array_ufunc__(self, ufunc, method, *inputs, **kw): def test_pow_array_object_dtype(self): # test pow on arrays of object dtype - class SomeClass(object): + class SomeClass: def __init__(self, num=None): self.num = num @@ -3383,7 +3829,17 @@ def pow_for(exp, arr): assert_equal(obj_arr ** -1, pow_for(-1, obj_arr)) assert_equal(obj_arr ** 2, pow_for(2, obj_arr)) -class TestTemporaryElide(object): + def test_pos_array_ufunc_override(self): + class A(np.ndarray): + def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): + return getattr(ufunc, method)(*[i.view(np.ndarray) for + i in inputs], **kwargs) + tst = np.array('foo').view(A) + with assert_raises(TypeError): + +tst + + +class TestTemporaryElide: # elision is only triggered on relatively large arrays def test_extension_incref_elide(self): @@ -3485,7 +3941,7 @@ def test_elide_updateifcopy(self): assert_equal(a, 1) -class TestCAPI(object): +class TestCAPI: def test_IsPythonScalar(self): from numpy.core._multiarray_tests import IsPythonScalar assert_(IsPythonScalar(b'foobar')) @@ -3495,35 +3951,102 @@ def test_IsPythonScalar(self): assert_(IsPythonScalar("a")) -class TestSubscripting(object): +class TestSubscripting: def test_test_zero_rank(self): x = np.array([1, 2, 3]) assert_(isinstance(x[0], np.int_)) - if sys.version_info[0] < 3: - assert_(isinstance(x[0], int)) assert_(type(x[0, ...]) is np.ndarray) -class TestPickling(object): +class TestPickling: + @pytest.mark.skipif(pickle.HIGHEST_PROTOCOL >= 5, + reason=('this tests the error messages when trying to' + 'protocol 5 although it is not available')) + def test_correct_protocol5_error_message(self): + array = np.arange(10) + + if sys.version_info[:2] in ((3, 6), (3, 7)): + # For the specific case of python3.6 and 3.7, raise a clear import + # error about the pickle5 backport when trying to use protocol=5 + # without the pickle5 package + with pytest.raises(ImportError): + array.__reduce_ex__(5) + + def test_record_array_with_object_dtype(self): + my_object = object() + + arr_with_object = np.array( + [(my_object, 1, 2.0)], + dtype=[('a', object), ('b', int), ('c', float)]) + arr_without_object = np.array( + [('xxx', 1, 2.0)], + dtype=[('a', str), ('b', int), ('c', float)]) + + for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): + depickled_arr_with_object = pickle.loads( + pickle.dumps(arr_with_object, protocol=proto)) + depickled_arr_without_object = pickle.loads( + pickle.dumps(arr_without_object, protocol=proto)) + + assert_equal(arr_with_object.dtype, + depickled_arr_with_object.dtype) + assert_equal(arr_without_object.dtype, + depickled_arr_without_object.dtype) + + @pytest.mark.skipif(pickle.HIGHEST_PROTOCOL < 5, + reason="requires pickle protocol 5") + def test_f_contiguous_array(self): + f_contiguous_array = np.array([[1, 2, 3], [4, 5, 6]], order='F') + buffers = [] + + # When using pickle protocol 5, Fortran-contiguous arrays can be + # serialized using out-of-band buffers + bytes_string = pickle.dumps(f_contiguous_array, protocol=5, + buffer_callback=buffers.append) + + assert len(buffers) > 0 + + depickled_f_contiguous_array = pickle.loads(bytes_string, + buffers=buffers) + + assert_equal(f_contiguous_array, depickled_f_contiguous_array) + + def test_non_contiguous_array(self): + non_contiguous_array = np.arange(12).reshape(3, 4)[:, :2] + assert not non_contiguous_array.flags.c_contiguous + assert not non_contiguous_array.flags.f_contiguous + + # make sure non-contiguous arrays can be pickled-depickled + # using any protocol + for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): + depickled_non_contiguous_array = pickle.loads( + pickle.dumps(non_contiguous_array, protocol=proto)) + + assert_equal(non_contiguous_array, depickled_non_contiguous_array) + def test_roundtrip(self): - import pickle - carray = np.array([[2, 9], [7, 0], [3, 8]]) - DATA = [ - carray, - np.transpose(carray), - np.array([('xxx', 1, 2.0)], dtype=[('a', (str, 3)), ('b', int), - ('c', float)]) - ] + for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): + carray = np.array([[2, 9], [7, 0], [3, 8]]) + DATA = [ + carray, + np.transpose(carray), + np.array([('xxx', 1, 2.0)], dtype=[('a', (str, 3)), ('b', int), + ('c', float)]) + ] - for a in DATA: - assert_equal(a, pickle.loads(a.dumps()), err_msg="%r" % a) + refs = [weakref.ref(a) for a in DATA] + for a in DATA: + assert_equal( + a, pickle.loads(pickle.dumps(a, protocol=proto)), + err_msg="%r" % a) + del a, DATA, carray + break_cycles() + # check for reference leaks (gh-12793) + for ref in refs: + assert ref() is None def _loads(self, obj): - import pickle - if sys.version_info[0] >= 3: - return pickle.loads(obj, encoding='latin1') - else: - return pickle.loads(obj) + return pickle.loads(obj, encoding='latin1') # version 0 pickles, using protocol=2 to pickle # version 0 doesn't have a version field @@ -3570,8 +4093,19 @@ def test_subarray_int_shape(self): p = self._loads(s) assert_equal(a, p) + def test_datetime64_byteorder(self): + original = np.array([['2015-02-24T00:00:00.000000000']], dtype='datetime64[ns]') + + original_byte_reversed = original.copy(order='K') + original_byte_reversed.dtype = original_byte_reversed.dtype.newbyteorder('S') + original_byte_reversed.byteswap(inplace=True) -class TestFancyIndexing(object): + new = pickle.loads(pickle.dumps(original_byte_reversed)) + + assert_equal(original.dtype, new.dtype) + + +class TestFancyIndexing: def test_list(self): x = np.ones((1, 1)) x[:, [0]] = 2.0 @@ -3625,7 +4159,7 @@ def test_assign_mask2(self): assert_array_equal(x, np.array([[1, 10, 3, 4], [5, 6, 7, 8]])) -class TestStringCompare(object): +class TestStringCompare: def test_string(self): g1 = np.array(["This", "is", "example"]) g2 = np.array(["This", "was", "example"]) @@ -3656,8 +4190,178 @@ def test_unicode(self): assert_array_equal(g1 < g2, [g1[i] < g2[i] for i in [0, 1, 2]]) assert_array_equal(g1 > g2, [g1[i] > g2[i] for i in [0, 1, 2]]) +class TestArgmaxArgminCommon: + + sizes = [(), (3,), (3, 2), (2, 3), + (3, 3), (2, 3, 4), (4, 3, 2), + (1, 2, 3, 4), (2, 3, 4, 1), + (3, 4, 1, 2), (4, 1, 2, 3)] -class TestArgmax(object): + @pytest.mark.parametrize("size, axis", itertools.chain(*[[(size, axis) + for axis in list(range(-len(size), len(size))) + [None]] + for size in sizes])) + @pytest.mark.parametrize('method', [np.argmax, np.argmin]) + def test_np_argmin_argmax_keepdims(self, size, axis, method): + + arr = np.random.normal(size=size) + + # contiguous arrays + if axis is None: + new_shape = [1 for _ in range(len(size))] + else: + new_shape = list(size) + new_shape[axis] = 1 + new_shape = tuple(new_shape) + + _res_orig = method(arr, axis=axis) + res_orig = _res_orig.reshape(new_shape) + res = method(arr, axis=axis, keepdims=True) + assert_equal(res, res_orig) + assert_(res.shape == new_shape) + outarray = np.empty(res.shape, dtype=res.dtype) + res1 = method(arr, axis=axis, out=outarray, + keepdims=True) + assert_(res1 is outarray) + assert_equal(res, outarray) + + if len(size) > 0: + wrong_shape = list(new_shape) + if axis is not None: + wrong_shape[axis] = 2 + else: + wrong_shape[0] = 2 + wrong_outarray = np.empty(wrong_shape, dtype=res.dtype) + with pytest.raises(ValueError): + method(arr.T, axis=axis, + out=wrong_outarray, keepdims=True) + + # non-contiguous arrays + if axis is None: + new_shape = [1 for _ in range(len(size))] + else: + new_shape = list(size)[::-1] + new_shape[axis] = 1 + new_shape = tuple(new_shape) + + _res_orig = method(arr.T, axis=axis) + res_orig = _res_orig.reshape(new_shape) + res = method(arr.T, axis=axis, keepdims=True) + assert_equal(res, res_orig) + assert_(res.shape == new_shape) + outarray = np.empty(new_shape[::-1], dtype=res.dtype) + outarray = outarray.T + res1 = method(arr.T, axis=axis, out=outarray, + keepdims=True) + assert_(res1 is outarray) + assert_equal(res, outarray) + + if len(size) > 0: + # one dimension lesser for non-zero sized + # array should raise an error + with pytest.raises(ValueError): + method(arr[0], axis=axis, + out=outarray, keepdims=True) + + if len(size) > 0: + wrong_shape = list(new_shape) + if axis is not None: + wrong_shape[axis] = 2 + else: + wrong_shape[0] = 2 + wrong_outarray = np.empty(wrong_shape, dtype=res.dtype) + with pytest.raises(ValueError): + method(arr.T, axis=axis, + out=wrong_outarray, keepdims=True) + + @pytest.mark.parametrize('method', ['max', 'min']) + def test_all(self, method): + a = np.random.normal(0, 1, (4, 5, 6, 7, 8)) + arg_method = getattr(a, 'arg' + method) + val_method = getattr(a, method) + for i in range(a.ndim): + a_maxmin = val_method(i) + aarg_maxmin = arg_method(i) + axes = list(range(a.ndim)) + axes.remove(i) + assert_(np.all(a_maxmin == aarg_maxmin.choose( + *a.transpose(i, *axes)))) + + @pytest.mark.parametrize('method', ['argmax', 'argmin']) + def test_output_shape(self, method): + # see also gh-616 + a = np.ones((10, 5)) + arg_method = getattr(a, method) + # Check some simple shape mismatches + out = np.ones(11, dtype=np.int_) + assert_raises(ValueError, arg_method, -1, out) + + out = np.ones((2, 5), dtype=np.int_) + assert_raises(ValueError, arg_method, -1, out) + + # these could be relaxed possibly (used to allow even the previous) + out = np.ones((1, 10), dtype=np.int_) + assert_raises(ValueError, arg_method, -1, out) + + out = np.ones(10, dtype=np.int_) + arg_method(-1, out=out) + assert_equal(out, arg_method(-1)) + + @pytest.mark.parametrize('ndim', [0, 1]) + @pytest.mark.parametrize('method', ['argmax', 'argmin']) + def test_ret_is_out(self, ndim, method): + a = np.ones((4,) + (3,)*ndim) + arg_method = getattr(a, method) + out = np.empty((3,)*ndim, dtype=np.intp) + ret = arg_method(axis=0, out=out) + assert ret is out + + @pytest.mark.parametrize('np_array, method, idx, val', + [(np.zeros, 'argmax', 5942, "as"), + (np.ones, 'argmin', 6001, "0")]) + def test_unicode(self, np_array, method, idx, val): + d = np_array(6031, dtype='<U9') + arg_method = getattr(d, method) + d[idx] = val + assert_equal(arg_method(), idx) + + @pytest.mark.parametrize('arr_method, np_method', + [('argmax', np.argmax), + ('argmin', np.argmin)]) + def test_np_vs_ndarray(self, arr_method, np_method): + # make sure both ndarray.argmax/argmin and + # numpy.argmax/argmin support out/axis args + a = np.random.normal(size=(2, 3)) + arg_method = getattr(a, arr_method) + + # check positional args + out1 = np.zeros(2, dtype=int) + out2 = np.zeros(2, dtype=int) + assert_equal(arg_method(1, out1), np_method(a, 1, out2)) + assert_equal(out1, out2) + + # check keyword args + out1 = np.zeros(3, dtype=int) + out2 = np.zeros(3, dtype=int) + assert_equal(arg_method(out=out1, axis=0), + np_method(a, out=out2, axis=0)) + assert_equal(out1, out2) + + @pytest.mark.leaks_references(reason="replaces None with NULL.") + @pytest.mark.parametrize('method, vals', + [('argmax', (10, 30)), + ('argmin', (30, 10))]) + def test_object_with_NULLs(self, method, vals): + # See gh-6032 + a = np.empty(4, dtype='O') + arg_method = getattr(a, method) + ctypes.memset(a.ctypes.data, 0, a.nbytes) + assert_equal(arg_method(), 0) + a[3] = vals[0] + assert_equal(arg_method(), 3) + a[1] = vals[1] + assert_equal(arg_method(), 1) + +class TestArgmax: nan_arr = [ ([0, 1, 2, 3, np.nan], 4), @@ -3696,17 +4400,17 @@ class TestArgmax(object): np.datetime64('2010-01-03T05:14:12'), np.datetime64('NaT'), np.datetime64('2015-09-23T10:10:13'), - np.datetime64('1932-10-10T03:50:30')], 4), + np.datetime64('1932-10-10T03:50:30')], 0), ([np.datetime64('2059-03-14T12:43:12'), np.datetime64('1996-09-21T14:43:15'), np.datetime64('NaT'), np.datetime64('2022-12-25T16:02:16'), np.datetime64('1963-10-04T03:14:12'), - np.datetime64('2013-05-08T18:15:23')], 0), + np.datetime64('2013-05-08T18:15:23')], 2), ([np.timedelta64(2, 's'), np.timedelta64(1, 's'), np.timedelta64('NaT', 's'), - np.timedelta64(3, 's')], 3), + np.timedelta64(3, 's')], 2), ([np.timedelta64('NaT', 's')] * 3, 0), ([timedelta(days=5, seconds=14), timedelta(days=2, seconds=35), @@ -3722,76 +4426,33 @@ class TestArgmax(object): ([True, False, True, False, False], 0), ] - def test_all(self): - a = np.random.normal(0, 1, (4, 5, 6, 7, 8)) - for i in range(a.ndim): - amax = a.max(i) - aargmax = a.argmax(i) - axes = list(range(a.ndim)) - axes.remove(i) - assert_(np.all(amax == aargmax.choose(*a.transpose(i,*axes)))) - - def test_combinations(self): - for arr, pos in self.nan_arr: - with suppress_warnings() as sup: - sup.filter(RuntimeWarning, - "invalid value encountered in reduce") - max_val = np.max(arr) - - assert_equal(np.argmax(arr), pos, err_msg="%r" % arr) - assert_equal(arr[np.argmax(arr)], max_val, err_msg="%r" % arr) - - def test_output_shape(self): - # see also gh-616 - a = np.ones((10, 5)) - # Check some simple shape mismatches - out = np.ones(11, dtype=np.int_) - assert_raises(ValueError, a.argmax, -1, out) - - out = np.ones((2, 5), dtype=np.int_) - assert_raises(ValueError, a.argmax, -1, out) - - # these could be relaxed possibly (used to allow even the previous) - out = np.ones((1, 10), dtype=np.int_) - assert_raises(ValueError, a.argmax, -1, out) + @pytest.mark.parametrize('data', nan_arr) + def test_combinations(self, data): + arr, pos = data + with suppress_warnings() as sup: + sup.filter(RuntimeWarning, + "invalid value encountered in reduce") + val = np.max(arr) - out = np.ones(10, dtype=np.int_) - a.argmax(-1, out=out) - assert_equal(out, a.argmax(-1)) + assert_equal(np.argmax(arr), pos, err_msg="%r" % arr) + assert_equal(arr[np.argmax(arr)], val, err_msg="%r" % arr) - def test_argmax_unicode(self): - d = np.zeros(6031, dtype='<U9') - d[5942] = "as" - assert_equal(d.argmax(), 5942) + def test_maximum_signed_integers(self): - def test_np_vs_ndarray(self): - # make sure both ndarray.argmax and numpy.argmax support out/axis args - a = np.random.normal(size=(2,3)) + a = np.array([1, 2**7 - 1, -2**7], dtype=np.int8) + assert_equal(np.argmax(a), 1) - # check positional args - out1 = np.zeros(2, dtype=int) - out2 = np.zeros(2, dtype=int) - assert_equal(a.argmax(1, out1), np.argmax(a, 1, out2)) - assert_equal(out1, out2) + a = np.array([1, 2**15 - 1, -2**15], dtype=np.int16) + assert_equal(np.argmax(a), 1) - # check keyword args - out1 = np.zeros(3, dtype=int) - out2 = np.zeros(3, dtype=int) - assert_equal(a.argmax(out=out1, axis=0), np.argmax(a, out=out2, axis=0)) - assert_equal(out1, out2) + a = np.array([1, 2**31 - 1, -2**31], dtype=np.int32) + assert_equal(np.argmax(a), 1) - def test_object_argmax_with_NULLs(self): - # See gh-6032 - a = np.empty(4, dtype='O') - ctypes.memset(a.ctypes.data, 0, a.nbytes) - assert_equal(a.argmax(), 0) - a[3] = 10 - assert_equal(a.argmax(), 3) - a[1] = 30 - assert_equal(a.argmax(), 1) + a = np.array([1, 2**63 - 1, -2**63], dtype=np.int64) + assert_equal(np.argmax(a), 1) -class TestArgmin(object): +class TestArgmin: nan_arr = [ ([0, 1, 2, 3, np.nan], 4), @@ -3830,17 +4491,17 @@ class TestArgmin(object): np.datetime64('2010-01-03T05:14:12'), np.datetime64('NaT'), np.datetime64('2015-09-23T10:10:13'), - np.datetime64('1932-10-10T03:50:30')], 5), + np.datetime64('1932-10-10T03:50:30')], 0), ([np.datetime64('2059-03-14T12:43:12'), np.datetime64('1996-09-21T14:43:15'), np.datetime64('NaT'), np.datetime64('2022-12-25T16:02:16'), np.datetime64('1963-10-04T03:14:12'), - np.datetime64('2013-05-08T18:15:23')], 4), + np.datetime64('2013-05-08T18:15:23')], 2), ([np.timedelta64(2, 's'), np.timedelta64(1, 's'), np.timedelta64('NaT', 's'), - np.timedelta64(3, 's')], 1), + np.timedelta64(3, 's')], 2), ([np.timedelta64('NaT', 's')] * 3, 0), ([timedelta(days=5, seconds=14), timedelta(days=2, seconds=35), @@ -3856,15 +4517,6 @@ class TestArgmin(object): ([False, True, False, True, True], 0), ] - def test_all(self): - a = np.random.normal(0, 1, (4, 5, 6, 7, 8)) - for i in range(a.ndim): - amin = a.min(i) - aargmin = a.argmin(i) - axes = list(range(a.ndim)) - axes.remove(i) - assert_(np.all(amin == aargmin.choose(*a.transpose(i,*axes)))) - def test_combinations(self): for arr, pos in self.nan_arr: with suppress_warnings() as sup: @@ -3877,69 +4529,20 @@ def test_combinations(self): def test_minimum_signed_integers(self): - a = np.array([1, -2**7, -2**7 + 1], dtype=np.int8) + a = np.array([1, -2**7, -2**7 + 1, 2**7 - 1], dtype=np.int8) assert_equal(np.argmin(a), 1) - a = np.array([1, -2**15, -2**15 + 1], dtype=np.int16) + a = np.array([1, -2**15, -2**15 + 1, 2**15 - 1], dtype=np.int16) assert_equal(np.argmin(a), 1) - a = np.array([1, -2**31, -2**31 + 1], dtype=np.int32) + a = np.array([1, -2**31, -2**31 + 1, 2**31 - 1], dtype=np.int32) assert_equal(np.argmin(a), 1) - a = np.array([1, -2**63, -2**63 + 1], dtype=np.int64) + a = np.array([1, -2**63, -2**63 + 1, 2**63 - 1], dtype=np.int64) assert_equal(np.argmin(a), 1) - def test_output_shape(self): - # see also gh-616 - a = np.ones((10, 5)) - # Check some simple shape mismatches - out = np.ones(11, dtype=np.int_) - assert_raises(ValueError, a.argmin, -1, out) - - out = np.ones((2, 5), dtype=np.int_) - assert_raises(ValueError, a.argmin, -1, out) - # these could be relaxed possibly (used to allow even the previous) - out = np.ones((1, 10), dtype=np.int_) - assert_raises(ValueError, a.argmin, -1, out) - - out = np.ones(10, dtype=np.int_) - a.argmin(-1, out=out) - assert_equal(out, a.argmin(-1)) - - def test_argmin_unicode(self): - d = np.ones(6031, dtype='<U9') - d[6001] = "0" - assert_equal(d.argmin(), 6001) - - def test_np_vs_ndarray(self): - # make sure both ndarray.argmin and numpy.argmin support out/axis args - a = np.random.normal(size=(2, 3)) - - # check positional args - out1 = np.zeros(2, dtype=int) - out2 = np.ones(2, dtype=int) - assert_equal(a.argmin(1, out1), np.argmin(a, 1, out2)) - assert_equal(out1, out2) - - # check keyword args - out1 = np.zeros(3, dtype=int) - out2 = np.ones(3, dtype=int) - assert_equal(a.argmin(out=out1, axis=0), np.argmin(a, out=out2, axis=0)) - assert_equal(out1, out2) - - def test_object_argmin_with_NULLs(self): - # See gh-6032 - a = np.empty(4, dtype='O') - ctypes.memset(a.ctypes.data, 0, a.nbytes) - assert_equal(a.argmin(), 0) - a[3] = 30 - assert_equal(a.argmin(), 3) - a[1] = 10 - assert_equal(a.argmin(), 1) - - -class TestMinMax(object): +class TestMinMax: def test_scalar(self): assert_raises(np.AxisError, np.amax, 1, 1) @@ -3955,28 +4558,24 @@ def test_axis(self): assert_equal(np.amax([[1, 2, 3]], axis=1), 3) def test_datetime(self): - # NaTs are ignored + # Do not ignore NaT for dtype in ('m8[s]', 'm8[Y]'): a = np.arange(10).astype(dtype) - a[3] = 'NaT' assert_equal(np.amin(a), a[0]) assert_equal(np.amax(a), a[9]) - a[0] = 'NaT' - assert_equal(np.amin(a), a[1]) - assert_equal(np.amax(a), a[9]) - a.fill('NaT') - assert_equal(np.amin(a), a[0]) - assert_equal(np.amax(a), a[0]) + a[3] = 'NaT' + assert_equal(np.amin(a), a[3]) + assert_equal(np.amax(a), a[3]) -class TestNewaxis(object): +class TestNewaxis: def test_basic(self): sk = np.array([0, -0.1, 0.1]) res = 250*sk[:, np.newaxis] assert_almost_equal(res.ravel(), 250*sk) -class TestClip(object): +class TestClip: def _check_range(self, x, cmin, cmax): assert_(np.all(x >= cmin)) assert_(np.all(x <= cmax)) @@ -4000,7 +4599,11 @@ def _clip_type(self, type_group, array_max, x = (np.random.random(1000) * array_max).astype(dtype) if inplace: - x.clip(clip_min, clip_max, x) + # The tests that call us pass clip_min and clip_max that + # might not fit in the destination dtype. They were written + # assuming the previous unsafe casting, which now must be + # passed explicitly to avoid a warning. + x.clip(clip_min, clip_max, x, casting='unsafe') else: x = x.clip(clip_min, clip_max) byteorder = '=' @@ -4019,7 +4622,7 @@ def test_basic(self): 'float', 1024, 0, 0, inplace=inplace) self._clip_type( - 'int', 1024, -120, 100.5, inplace=inplace) + 'int', 1024, -120, 100, inplace=inplace) self._clip_type( 'int', 1024, 0, 0, inplace=inplace) @@ -4050,7 +4653,7 @@ def test_nan(self): assert_array_equal(result, expected) -class TestCompress(object): +class TestCompress: def test_axis(self): tgt = [[5, 6, 7, 8, 9]] arr = np.arange(10).reshape(2, 5) @@ -4073,14 +4676,13 @@ def test_flatten(self): assert_equal(out, 1) -class TestPutmask(object): +class TestPutmask: def tst_basic(self, x, T, mask, val): np.putmask(x, mask, val) - assert_equal(x[mask], T(val)) - assert_equal(x.dtype, T) + assert_equal(x[mask], np.array(val, T)) def test_ip_types(self): - unchecked_types = [bytes, unicode, np.void, object] + unchecked_types = [bytes, str, np.void] x = np.random.random(1000)*100 mask = x < 40 @@ -4091,18 +4693,19 @@ def test_ip_types(self): if T not in unchecked_types: self.tst_basic(x.copy().astype(T), T, mask, val) + # Also test string of a length which uses an untypical length + dt = np.dtype("S3") + self.tst_basic(x.astype(dt), dt.type, mask, dt.type(val)[:3]) + def test_mask_size(self): assert_raises(ValueError, np.putmask, np.array([1, 2, 3]), [True], 5) - def tst_byteorder(self, dtype): + @pytest.mark.parametrize('dtype', ('>i4', '<i4')) + def test_byteorder(self, dtype): x = np.array([1, 2, 3], dtype) np.putmask(x, [True, False, True], -1) assert_array_equal(x, [-1, 2, -1]) - def test_ip_byteorder(self): - for dtype in ('>i4', '<i4'): - self.tst_byteorder(dtype) - def test_record_array(self): # Note mixed byteorder. rec = np.array([(-5, 2.0, 3.0), (5.0, 4.0, 3.0)], @@ -4116,14 +4719,31 @@ def test_record_array(self): assert_array_equal(rec['y'], [11, 4]) assert_array_equal(rec['z'], [3, 3]) + def test_overlaps(self): + # gh-6272 check overlap + x = np.array([True, False, True, False]) + np.putmask(x[1:4], [True, True, True], x[:3]) + assert_equal(x, np.array([True, True, False, True])) + + x = np.array([True, False, True, False]) + np.putmask(x[1:4], x[:3], [True, False, True]) + assert_equal(x, np.array([True, True, True, True])) + + def test_writeable(self): + a = np.arange(5) + a.flags.writeable = False + + with pytest.raises(ValueError): + np.putmask(a, a >= 2, 3) + -class TestTake(object): +class TestTake: def tst_basic(self, x): ind = list(range(x.shape[0])) assert_array_equal(x.take(ind, axis=0), x) def test_ip_types(self): - unchecked_types = [bytes, unicode, np.void, object] + unchecked_types = [bytes, str, np.void] x = np.random.random(24)*100 x.shape = 2, 3, 4 @@ -4132,6 +4752,9 @@ def test_ip_types(self): if T not in unchecked_types: self.tst_basic(x.copy().astype(T)) + # Also test string of a length which uses an untypical length + self.tst_basic(x.astype("S3")) + def test_raise(self): x = np.random.random(24)*100 x.shape = 2, 3, 4 @@ -4152,14 +4775,11 @@ def test_wrap(self): assert_array_equal(x.take([2], axis=0, mode='wrap')[0], x[0]) assert_array_equal(x.take([3], axis=0, mode='wrap')[0], x[1]) - def tst_byteorder(self, dtype): + @pytest.mark.parametrize('dtype', ('>i4', '<i4')) + def test_byteorder(self, dtype): x = np.array([1, 2, 3], dtype) assert_array_equal(x.take([0, 2, 1]), [1, 3, 2]) - def test_ip_byteorder(self): - for dtype in ('>i4', '<i4'): - self.tst_byteorder(dtype) - def test_record_array(self): # Note mixed byteorder. rec = np.array([(-5, 2.0, 3.0), (5.0, 4.0, 3.0)], @@ -4167,20 +4787,43 @@ def test_record_array(self): rec1 = rec.take([1]) assert_(rec1['x'] == 5.0 and rec1['y'] == 4.0) - -class TestLexsort(object): - def test_basic(self): - a = [1, 2, 1, 3, 1, 5] - b = [0, 4, 5, 6, 2, 3] + def test_out_overlap(self): + # gh-6272 check overlap on out + x = np.arange(5) + y = np.take(x, [1, 2, 3], out=x[2:5], mode='wrap') + assert_equal(y, np.array([1, 2, 3])) + + @pytest.mark.parametrize('shape', [(1, 2), (1,), ()]) + def test_ret_is_out(self, shape): + # 0d arrays should not be an exception to this rule + x = np.arange(5) + inds = np.zeros(shape, dtype=np.intp) + out = np.zeros(shape, dtype=x.dtype) + ret = np.take(x, inds, out=out) + assert ret is out + + +class TestLexsort: + @pytest.mark.parametrize('dtype',[ + np.uint8, np.uint16, np.uint32, np.uint64, + np.int8, np.int16, np.int32, np.int64, + np.float16, np.float32, np.float64 + ]) + def test_basic(self, dtype): + a = np.array([1, 2, 1, 3, 1, 5], dtype=dtype) + b = np.array([0, 4, 5, 6, 2, 3], dtype=dtype) idx = np.lexsort((b, a)) expected_idx = np.array([0, 4, 2, 1, 3, 5]) assert_array_equal(idx, expected_idx) + assert_array_equal(a[idx], np.sort(a)) - x = np.vstack((b, a)) - idx = np.lexsort(x) - assert_array_equal(idx, expected_idx) + def test_mixed(self): + a = np.array([1, 2, 1, 3, 1, 5]) + b = np.array([0, 4, 5, 6, 2, 3], dtype='datetime64[D]') - assert_array_equal(x[1][idx], np.sort(x[1])) + idx = np.lexsort((b, a)) + expected_idx = np.array([0, 4, 2, 1, 3, 5]) + assert_array_equal(idx, expected_idx) def test_datetime(self): a = np.array([0,0,0], dtype='datetime64[D]') @@ -4215,28 +4858,34 @@ def test_invalid_axis(self): # gh-7528 x = np.linspace(0., 1., 42*3).reshape(42, 3) assert_raises(np.AxisError, np.lexsort, x, axis=2) -class TestIO(object): +class TestIO: """Test tofile, fromfile, tobytes, and fromstring""" - def setup(self): + @pytest.fixture() + def x(self): shape = (2, 4, 3) rand = np.random.random - self.x = rand(shape) + rand(shape).astype(complex)*1j - self.x[0,:, 1] = [np.nan, np.inf, -np.inf, np.nan] - self.dtype = self.x.dtype - self.tempdir = tempfile.mkdtemp() - self.filename = tempfile.mktemp(dir=self.tempdir) + x = rand(shape) + rand(shape).astype(complex) * 1j + x[0, :, 1] = [np.nan, np.inf, -np.inf, np.nan] + return x - def teardown(self): - shutil.rmtree(self.tempdir) + @pytest.fixture(params=["string", "path_obj"]) + def tmp_filename(self, tmp_path, request): + # This fixture covers two cases: + # one where the filename is a string and + # another where it is a pathlib object + filename = tmp_path / "file" + if request.param == "string": + filename = str(filename) + yield filename def test_nofile(self): # this should probably be supported as a file # but for now test for proper errors b = io.BytesIO() - assert_raises(IOError, np.fromfile, b, np.uint8, 80) + assert_raises(OSError, np.fromfile, b, np.uint8, 80) d = np.ones(7) - assert_raises(IOError, lambda x: x.tofile(b), d) + assert_raises(OSError, lambda x: x.tofile(b), d) def test_bool_fromstring(self): v = np.array([True, False, True, False], dtype=np.bool_) @@ -4255,44 +4904,52 @@ def test_int64_fromstring(self): e = np.array([-25041670086757, 104783749223640], dtype=np.int64) assert_array_equal(d, e) - def test_empty_files_binary(self): - f = open(self.filename, 'w') - f.close() - y = np.fromfile(self.filename) + def test_fromstring_count0(self): + d = np.fromstring("1,2", sep=",", dtype=np.int64, count=0) + assert d.shape == (0,) + + def test_empty_files_text(self, tmp_filename): + with open(tmp_filename, 'w') as f: + pass + y = np.fromfile(tmp_filename) assert_(y.size == 0, "Array not empty") - def test_empty_files_text(self): - f = open(self.filename, 'w') - f.close() - y = np.fromfile(self.filename, sep=" ") + def test_empty_files_binary(self, tmp_filename): + with open(tmp_filename, 'wb') as f: + pass + y = np.fromfile(tmp_filename, sep=" ") assert_(y.size == 0, "Array not empty") - def test_roundtrip_file(self): - f = open(self.filename, 'wb') - self.x.tofile(f) - f.close() + def test_roundtrip_file(self, x, tmp_filename): + with open(tmp_filename, 'wb') as f: + x.tofile(f) # NB. doesn't work with flush+seek, due to use of C stdio - f = open(self.filename, 'rb') - y = np.fromfile(f, dtype=self.dtype) - f.close() - assert_array_equal(y, self.x.flat) - - def test_roundtrip_filename(self): - self.x.tofile(self.filename) - y = np.fromfile(self.filename, dtype=self.dtype) - assert_array_equal(y, self.x.flat) - - def test_roundtrip_binary_str(self): - s = self.x.tobytes() - y = np.frombuffer(s, dtype=self.dtype) - assert_array_equal(y, self.x.flat) - - s = self.x.tobytes('F') - y = np.frombuffer(s, dtype=self.dtype) - assert_array_equal(y, self.x.flatten('F')) - - def test_roundtrip_str(self): - x = self.x.real.ravel() + with open(tmp_filename, 'rb') as f: + y = np.fromfile(f, dtype=x.dtype) + assert_array_equal(y, x.flat) + + def test_roundtrip(self, x, tmp_filename): + x.tofile(tmp_filename) + y = np.fromfile(tmp_filename, dtype=x.dtype) + assert_array_equal(y, x.flat) + + def test_roundtrip_dump_pathlib(self, x, tmp_filename): + p = pathlib.Path(tmp_filename) + x.dump(p) + y = np.load(p, allow_pickle=True) + assert_array_equal(y, x) + + def test_roundtrip_binary_str(self, x): + s = x.tobytes() + y = np.frombuffer(s, dtype=x.dtype) + assert_array_equal(y, x.flat) + + s = x.tobytes('F') + y = np.frombuffer(s, dtype=x.dtype) + assert_array_equal(y, x.flatten('F')) + + def test_roundtrip_str(self, x): + x = x.real.ravel() s = "@".join(map(str, x)) y = np.fromstring(s, sep="@") # NB. str imbues less precision @@ -4300,82 +4957,79 @@ def test_roundtrip_str(self): assert_array_equal(x[nan_mask], y[nan_mask]) assert_array_almost_equal(x[~nan_mask], y[~nan_mask], decimal=5) - def test_roundtrip_repr(self): - x = self.x.real.ravel() + def test_roundtrip_repr(self, x): + x = x.real.ravel() s = "@".join(map(repr, x)) y = np.fromstring(s, sep="@") assert_array_equal(x, y) - def test_unseekable_fromfile(self): + def test_unseekable_fromfile(self, x, tmp_filename): # gh-6246 - self.x.tofile(self.filename) + x.tofile(tmp_filename) def fail(*args, **kwargs): - raise IOError('Can not tell or seek') + raise OSError('Can not tell or seek') - with io.open(self.filename, 'rb', buffering=0) as f: + with io.open(tmp_filename, 'rb', buffering=0) as f: f.seek = fail f.tell = fail - assert_raises(IOError, np.fromfile, f, dtype=self.dtype) + assert_raises(OSError, np.fromfile, f, dtype=x.dtype) - def test_io_open_unbuffered_fromfile(self): + def test_io_open_unbuffered_fromfile(self, x, tmp_filename): # gh-6632 - self.x.tofile(self.filename) - with io.open(self.filename, 'rb', buffering=0) as f: - y = np.fromfile(f, dtype=self.dtype) - assert_array_equal(y, self.x.flat) + x.tofile(tmp_filename) + with io.open(tmp_filename, 'rb', buffering=0) as f: + y = np.fromfile(f, dtype=x.dtype) + assert_array_equal(y, x.flat) - def test_largish_file(self): + def test_largish_file(self, tmp_filename): # check the fallocate path on files > 16MB d = np.zeros(4 * 1024 ** 2) - d.tofile(self.filename) - assert_equal(os.path.getsize(self.filename), d.nbytes) - assert_array_equal(d, np.fromfile(self.filename)) + d.tofile(tmp_filename) + assert_equal(os.path.getsize(tmp_filename), d.nbytes) + assert_array_equal(d, np.fromfile(tmp_filename)) # check offset - with open(self.filename, "r+b") as f: + with open(tmp_filename, "r+b") as f: f.seek(d.nbytes) d.tofile(f) - assert_equal(os.path.getsize(self.filename), d.nbytes * 2) + assert_equal(os.path.getsize(tmp_filename), d.nbytes * 2) # check append mode (gh-8329) - open(self.filename, "w").close() # delete file contents - with open(self.filename, "ab") as f: + open(tmp_filename, "w").close() # delete file contents + with open(tmp_filename, "ab") as f: d.tofile(f) - assert_array_equal(d, np.fromfile(self.filename)) - with open(self.filename, "ab") as f: + assert_array_equal(d, np.fromfile(tmp_filename)) + with open(tmp_filename, "ab") as f: d.tofile(f) - assert_equal(os.path.getsize(self.filename), d.nbytes * 2) - + assert_equal(os.path.getsize(tmp_filename), d.nbytes * 2) - def test_io_open_buffered_fromfile(self): + def test_io_open_buffered_fromfile(self, x, tmp_filename): # gh-6632 - self.x.tofile(self.filename) - with io.open(self.filename, 'rb', buffering=-1) as f: - y = np.fromfile(f, dtype=self.dtype) - assert_array_equal(y, self.x.flat) + x.tofile(tmp_filename) + with io.open(tmp_filename, 'rb', buffering=-1) as f: + y = np.fromfile(f, dtype=x.dtype) + assert_array_equal(y, x.flat) - def test_file_position_after_fromfile(self): + def test_file_position_after_fromfile(self, tmp_filename): # gh-4118 sizes = [io.DEFAULT_BUFFER_SIZE//8, io.DEFAULT_BUFFER_SIZE, io.DEFAULT_BUFFER_SIZE*8] for size in sizes: - f = open(self.filename, 'wb') - f.seek(size-1) - f.write(b'\0') - f.close() + with open(tmp_filename, 'wb') as f: + f.seek(size-1) + f.write(b'\0') for mode in ['rb', 'r+b']: err_msg = "%d %s" % (size, mode) - f = open(self.filename, mode) - f.read(2) - np.fromfile(f, dtype=np.float64, count=1) - pos = f.tell() - f.close() + with open(tmp_filename, mode) as f: + f.read(2) + np.fromfile(f, dtype=np.float64, count=1) + pos = f.tell() assert_equal(pos, 10, err_msg=err_msg) - def test_file_position_after_tofile(self): + def test_file_position_after_tofile(self, tmp_filename): # gh-4118 sizes = [io.DEFAULT_BUFFER_SIZE//8, io.DEFAULT_BUFFER_SIZE, @@ -4384,57 +5038,154 @@ def test_file_position_after_tofile(self): for size in sizes: err_msg = "%d" % (size,) - f = open(self.filename, 'wb') - f.seek(size-1) - f.write(b'\0') - f.seek(10) - f.write(b'12') - np.array([0], dtype=np.float64).tofile(f) - pos = f.tell() - f.close() + with open(tmp_filename, 'wb') as f: + f.seek(size-1) + f.write(b'\0') + f.seek(10) + f.write(b'12') + np.array([0], dtype=np.float64).tofile(f) + pos = f.tell() assert_equal(pos, 10 + 2 + 8, err_msg=err_msg) - f = open(self.filename, 'r+b') - f.read(2) - f.seek(0, 1) # seek between read&write required by ANSI C - np.array([0], dtype=np.float64).tofile(f) - pos = f.tell() - f.close() + with open(tmp_filename, 'r+b') as f: + f.read(2) + f.seek(0, 1) # seek between read&write required by ANSI C + np.array([0], dtype=np.float64).tofile(f) + pos = f.tell() assert_equal(pos, 10, err_msg=err_msg) - def _check_from(self, s, value, **kw): + def test_load_object_array_fromfile(self, tmp_filename): + # gh-12300 + with open(tmp_filename, 'w') as f: + # Ensure we have a file with consistent contents + pass + + with open(tmp_filename, 'rb') as f: + assert_raises_regex(ValueError, "Cannot read into object array", + np.fromfile, f, dtype=object) + + assert_raises_regex(ValueError, "Cannot read into object array", + np.fromfile, tmp_filename, dtype=object) + + def test_fromfile_offset(self, x, tmp_filename): + with open(tmp_filename, 'wb') as f: + x.tofile(f) + + with open(tmp_filename, 'rb') as f: + y = np.fromfile(f, dtype=x.dtype, offset=0) + assert_array_equal(y, x.flat) + + with open(tmp_filename, 'rb') as f: + count_items = len(x.flat) // 8 + offset_items = len(x.flat) // 4 + offset_bytes = x.dtype.itemsize * offset_items + y = np.fromfile( + f, dtype=x.dtype, count=count_items, offset=offset_bytes + ) + assert_array_equal( + y, x.flat[offset_items:offset_items+count_items] + ) + + # subsequent seeks should stack + offset_bytes = x.dtype.itemsize + z = np.fromfile(f, dtype=x.dtype, offset=offset_bytes) + assert_array_equal(z, x.flat[offset_items+count_items+1:]) + + with open(tmp_filename, 'wb') as f: + x.tofile(f, sep=",") + + with open(tmp_filename, 'rb') as f: + assert_raises_regex( + TypeError, + "'offset' argument only permitted for binary files", + np.fromfile, tmp_filename, dtype=x.dtype, + sep=",", offset=1) + + @pytest.mark.skipif(IS_PYPY, reason="bug in PyPy's PyNumber_AsSsize_t") + def test_fromfile_bad_dup(self, x, tmp_filename): + def dup_str(fd): + return 'abc' + + def dup_bigint(fd): + return 2**68 + + old_dup = os.dup + try: + with open(tmp_filename, 'wb') as f: + x.tofile(f) + for dup, exc in ((dup_str, TypeError), (dup_bigint, OSError)): + os.dup = dup + assert_raises(exc, np.fromfile, f) + finally: + os.dup = old_dup + + def _check_from(self, s, value, filename, **kw): if 'sep' not in kw: y = np.frombuffer(s, **kw) else: y = np.fromstring(s, **kw) assert_array_equal(y, value) - f = open(self.filename, 'wb') - f.write(s) - f.close() - y = np.fromfile(self.filename, **kw) + with open(filename, 'wb') as f: + f.write(s) + y = np.fromfile(filename, **kw) assert_array_equal(y, value) - def test_nan(self): + @pytest.fixture(params=["period", "comma"]) + def decimal_sep_localization(self, request): + """ + Including this fixture in a test will automatically + execute it with both types of decimal separator. + + So:: + + def test_decimal(decimal_sep_localization): + pass + + is equivalent to the following two tests:: + + def test_decimal_period_separator(): + pass + + def test_decimal_comma_separator(): + with CommaDecimalPointLocale(): + pass + """ + if request.param == "period": + yield + elif request.param == "comma": + with CommaDecimalPointLocale(): + yield + else: + assert False, request.param + + def test_nan(self, tmp_filename, decimal_sep_localization): self._check_from( b"nan +nan -nan NaN nan(foo) +NaN(BAR) -NAN(q_u_u_x_)", [np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan], + tmp_filename, sep=' ') - def test_inf(self): + def test_inf(self, tmp_filename, decimal_sep_localization): self._check_from( b"inf +inf -inf infinity -Infinity iNfInItY -inF", [np.inf, np.inf, -np.inf, np.inf, -np.inf, np.inf, -np.inf], + tmp_filename, sep=' ') - def test_numbers(self): - self._check_from(b"1.234 -1.234 .3 .3e55 -123133.1231e+133", - [1.234, -1.234, .3, .3e55, -123133.1231e+133], sep=' ') + def test_numbers(self, tmp_filename, decimal_sep_localization): + self._check_from( + b"1.234 -1.234 .3 .3e55 -123133.1231e+133", + [1.234, -1.234, .3, .3e55, -123133.1231e+133], + tmp_filename, + sep=' ') - def test_binary(self): - self._check_from(b'\x00\x00\x80?\x00\x00\x00@\x00\x00@@\x00\x00\x80@', - np.array([1, 2, 3, 4]), - dtype='<f4') + def test_binary(self, tmp_filename): + self._check_from( + b'\x00\x00\x80?\x00\x00\x00@\x00\x00@@\x00\x00\x80@', + np.array([1, 2, 3, 4]), + tmp_filename, + dtype='<f4') @pytest.mark.slow # takes > 1 minute on mechanical hard drive def test_big_binary(self): @@ -4461,97 +5212,139 @@ def test_big_binary(self): except (MemoryError, ValueError): pass - def test_string(self): - self._check_from(b'1,2,3,4', [1., 2., 3., 4.], sep=',') + def test_string(self, tmp_filename): + self._check_from(b'1,2,3,4', [1., 2., 3., 4.], tmp_filename, sep=',') - def test_counted_string(self): - self._check_from(b'1,2,3,4', [1., 2., 3., 4.], count=4, sep=',') - self._check_from(b'1,2,3,4', [1., 2., 3.], count=3, sep=',') - self._check_from(b'1,2,3,4', [1., 2., 3., 4.], count=-1, sep=',') + def test_counted_string(self, tmp_filename, decimal_sep_localization): + self._check_from( + b'1,2,3,4', [1., 2., 3., 4.], tmp_filename, count=4, sep=',') + self._check_from( + b'1,2,3,4', [1., 2., 3.], tmp_filename, count=3, sep=',') + self._check_from( + b'1,2,3,4', [1., 2., 3., 4.], tmp_filename, count=-1, sep=',') - def test_string_with_ws(self): - self._check_from(b'1 2 3 4 ', [1, 2, 3, 4], dtype=int, sep=' ') + def test_string_with_ws(self, tmp_filename): + self._check_from( + b'1 2 3 4 ', [1, 2, 3, 4], tmp_filename, dtype=int, sep=' ') - def test_counted_string_with_ws(self): - self._check_from(b'1 2 3 4 ', [1, 2, 3], count=3, dtype=int, - sep=' ') + def test_counted_string_with_ws(self, tmp_filename): + self._check_from( + b'1 2 3 4 ', [1, 2, 3], tmp_filename, count=3, dtype=int, + sep=' ') - def test_ascii(self): - self._check_from(b'1 , 2 , 3 , 4', [1., 2., 3., 4.], sep=',') - self._check_from(b'1,2,3,4', [1., 2., 3., 4.], dtype=float, sep=',') + def test_ascii(self, tmp_filename, decimal_sep_localization): + self._check_from( + b'1 , 2 , 3 , 4', [1., 2., 3., 4.], tmp_filename, sep=',') + self._check_from( + b'1,2,3,4', [1., 2., 3., 4.], tmp_filename, dtype=float, sep=',') - def test_malformed(self): - self._check_from(b'1.234 1,234', [1.234, 1.], sep=' ') + def test_malformed(self, tmp_filename, decimal_sep_localization): + with assert_warns(DeprecationWarning): + self._check_from( + b'1.234 1,234', [1.234, 1.], tmp_filename, sep=' ') - def test_long_sep(self): - self._check_from(b'1_x_3_x_4_x_5', [1, 3, 4, 5], sep='_x_') + def test_long_sep(self, tmp_filename): + self._check_from( + b'1_x_3_x_4_x_5', [1, 3, 4, 5], tmp_filename, sep='_x_') - def test_dtype(self): + def test_dtype(self, tmp_filename): v = np.array([1, 2, 3, 4], dtype=np.int_) - self._check_from(b'1,2,3,4', v, sep=',', dtype=np.int_) + self._check_from(b'1,2,3,4', v, tmp_filename, sep=',', dtype=np.int_) - def test_dtype_bool(self): + def test_dtype_bool(self, tmp_filename): # can't use _check_from because fromstring can't handle True/False v = np.array([True, False, True, False], dtype=np.bool_) s = b'1,0,-2.3,0' - f = open(self.filename, 'wb') - f.write(s) - f.close() - y = np.fromfile(self.filename, sep=',', dtype=np.bool_) + with open(tmp_filename, 'wb') as f: + f.write(s) + y = np.fromfile(tmp_filename, sep=',', dtype=np.bool_) assert_(y.dtype == '?') assert_array_equal(y, v) - def test_tofile_sep(self): + def test_tofile_sep(self, tmp_filename, decimal_sep_localization): x = np.array([1.51, 2, 3.51, 4], dtype=float) - f = open(self.filename, 'w') - x.tofile(f, sep=',') - f.close() - f = open(self.filename, 'r') - s = f.read() - f.close() + with open(tmp_filename, 'w') as f: + x.tofile(f, sep=',') + with open(tmp_filename, 'r') as f: + s = f.read() #assert_equal(s, '1.51,2.0,3.51,4.0') y = np.array([float(p) for p in s.split(',')]) assert_array_equal(x,y) - def test_tofile_format(self): + def test_tofile_format(self, tmp_filename, decimal_sep_localization): x = np.array([1.51, 2, 3.51, 4], dtype=float) - f = open(self.filename, 'w') - x.tofile(f, sep=',', format='%.2f') - f.close() - f = open(self.filename, 'r') - s = f.read() - f.close() + with open(tmp_filename, 'w') as f: + x.tofile(f, sep=',', format='%.2f') + with open(tmp_filename, 'r') as f: + s = f.read() assert_equal(s, '1.51,2.00,3.51,4.00') - def test_locale(self): - with CommaDecimalPointLocale(): - self.test_numbers() - self.test_nan() - self.test_inf() - self.test_counted_string() - self.test_ascii() - self.test_malformed() - self.test_tofile_sep() - self.test_tofile_format() - - -class TestFromBuffer(object): - def tst_basic(self, buffer, expected, kwargs): - assert_array_equal(np.frombuffer(buffer,**kwargs), expected) - - def test_ip_basic(self): - for byteorder in ['<', '>']: - for dtype in [float, int, complex]: - dt = np.dtype(dtype).newbyteorder(byteorder) - x = (np.random.random((4, 7))*5).astype(dt) - buf = x.tobytes() - self.tst_basic(buf, x.flat, {'dtype':dt}) + def test_tofile_cleanup(self, tmp_filename): + x = np.zeros((10), dtype=object) + with open(tmp_filename, 'wb') as f: + assert_raises(OSError, lambda: x.tofile(f, sep='')) + # Dup-ed file handle should be closed or remove will fail on Windows OS + os.remove(tmp_filename) + + # Also make sure that we close the Python handle + assert_raises(OSError, lambda: x.tofile(tmp_filename)) + os.remove(tmp_filename) + + def test_fromfile_subarray_binary(self, tmp_filename): + # Test subarray dtypes which are absorbed into the shape + x = np.arange(24, dtype="i4").reshape(2, 3, 4) + x.tofile(tmp_filename) + res = np.fromfile(tmp_filename, dtype="(3,4)i4") + assert_array_equal(x, res) + + x_str = x.tobytes() + with assert_warns(DeprecationWarning): + # binary fromstring is deprecated + res = np.fromstring(x_str, dtype="(3,4)i4") + assert_array_equal(x, res) + + def test_parsing_subarray_unsupported(self, tmp_filename): + # We currently do not support parsing subarray dtypes + data = "12,42,13," * 50 + with pytest.raises(ValueError): + expected = np.fromstring(data, dtype="(3,)i", sep=",") + + with open(tmp_filename, "w") as f: + f.write(data) + + with pytest.raises(ValueError): + np.fromfile(tmp_filename, dtype="(3,)i", sep=",") + + def test_read_shorter_than_count_subarray(self, tmp_filename): + # Test that requesting more values does not cause any problems + # in conjunction with subarray dimensions being absorbed into the + # array dimension. + expected = np.arange(511 * 10, dtype="i").reshape(-1, 10) + + binary = expected.tobytes() + with pytest.raises(ValueError): + with pytest.warns(DeprecationWarning): + np.fromstring(binary, dtype="(10,)i", count=10000) + + expected.tofile(tmp_filename) + res = np.fromfile(tmp_filename, dtype="(10,)i", count=10000) + assert_array_equal(res, expected) + + +class TestFromBuffer: + @pytest.mark.parametrize('byteorder', ['<', '>']) + @pytest.mark.parametrize('dtype', [float, int, complex]) + def test_basic(self, byteorder, dtype): + dt = np.dtype(dtype).newbyteorder(byteorder) + x = (np.random.random((4, 7)) * 5).astype(dt) + buf = x.tobytes() + assert_array_equal(np.frombuffer(buf, dtype=dt), x.flat) def test_empty(self): - self.tst_basic(b'', np.array([]), {}) + assert_array_equal(np.frombuffer(b''), np.array([])) -class TestFlat(object): +class TestFlat: def setup(self): a0 = np.arange(20.0) a = a0.reshape(4, 5) @@ -4606,8 +5399,37 @@ def test___array__(self): assert_(e.flags.writebackifcopy is False) assert_(f.flags.writebackifcopy is False) + @pytest.mark.skipif(not HAS_REFCOUNT, reason="Python lacks refcounts") + def test_refcount(self): + # includes regression test for reference count error gh-13165 + inds = [np.intp(0), np.array([True]*self.a.size), np.array([0]), None] + indtype = np.dtype(np.intp) + rc_indtype = sys.getrefcount(indtype) + for ind in inds: + rc_ind = sys.getrefcount(ind) + for _ in range(100): + try: + self.a.flat[ind] + except IndexError: + pass + assert_(abs(sys.getrefcount(ind) - rc_ind) < 50) + assert_(abs(sys.getrefcount(indtype) - rc_indtype) < 50) + + def test_index_getset(self): + it = np.arange(10).reshape(2, 1, 5).flat + with pytest.raises(AttributeError): + it.index = 10 + + for _ in it: + pass + # Check the value of `.index` is updated correctly (see also gh-19153) + # If the type was incorrect, this would show up on big-endian machines + assert it.index == it.base.size -class TestResize(object): + +class TestResize: + + @_no_tracing def test_basic(self): x = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) if IS_PYPY: @@ -4624,6 +5446,7 @@ def test_check_reference(self): assert_raises(ValueError, x.resize, (5, 1)) del y # avoid pyflakes unused variable warning. + @_no_tracing def test_int_shape(self): x = np.eye(3) if IS_PYPY: @@ -4657,6 +5480,7 @@ def test_invalid_arguments(self): assert_raises(TypeError, np.eye(3).resize, order=1) assert_raises(TypeError, np.eye(3).resize, refcheck='hi') + @_no_tracing def test_freeform_shape(self): x = np.eye(3) if IS_PYPY: @@ -4665,6 +5489,7 @@ def test_freeform_shape(self): x.resize(3, 2, 1) assert_(x.shape == (3, 2, 1)) + @_no_tracing def test_zeros_appended(self): x = np.eye(3) if IS_PYPY: @@ -4674,6 +5499,7 @@ def test_zeros_appended(self): assert_array_equal(x[0], np.eye(3)) assert_array_equal(x[1], np.zeros((3, 3))) + @_no_tracing def test_obj_obj(self): # check memory is initialized on resize, gh-4857 a = np.ones(10, dtype=[('k', object, 2)]) @@ -4693,181 +5519,103 @@ def test_empty_view(self): x_view.resize((0, 10)) x_view.resize((0, 100)) + def test_check_weakref(self): + x = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) + xref = weakref.ref(x) + assert_raises(ValueError, x.resize, (5, 1)) + del xref # avoid pyflakes unused variable warning. -class TestRecord(object): + +class TestRecord: def test_field_rename(self): dt = np.dtype([('f', float), ('i', int)]) dt.names = ['p', 'q'] assert_equal(dt.names, ['p', 'q']) def test_multiple_field_name_occurrence(self): - def test_assign(): - dtype = np.dtype([("A", "f8"), ("B", "f8"), ("A", "f8")]) + def test_dtype_init(): + np.dtype([("A", "f8"), ("B", "f8"), ("A", "f8")]) # Error raised when multiple fields have the same name - assert_raises(ValueError, test_assign) + assert_raises(ValueError, test_dtype_init) - if sys.version_info[0] >= 3: - def test_bytes_fields(self): - # Bytes are not allowed in field names and not recognized in titles - # on Py3 - assert_raises(TypeError, np.dtype, [(b'a', int)]) - assert_raises(TypeError, np.dtype, [(('b', b'a'), int)]) + def test_bytes_fields(self): + # Bytes are not allowed in field names and not recognized in titles + # on Py3 + assert_raises(TypeError, np.dtype, [(b'a', int)]) + assert_raises(TypeError, np.dtype, [(('b', b'a'), int)]) - dt = np.dtype([((b'a', 'b'), int)]) - assert_raises(TypeError, dt.__getitem__, b'a') + dt = np.dtype([((b'a', 'b'), int)]) + assert_raises(TypeError, dt.__getitem__, b'a') - x = np.array([(1,), (2,), (3,)], dtype=dt) - assert_raises(IndexError, x.__getitem__, b'a') + x = np.array([(1,), (2,), (3,)], dtype=dt) + assert_raises(IndexError, x.__getitem__, b'a') - y = x[0] - assert_raises(IndexError, y.__getitem__, b'a') + y = x[0] + assert_raises(IndexError, y.__getitem__, b'a') - def test_multiple_field_name_unicode(self): - def test_assign_unicode(): - dt = np.dtype([("\u20B9", "f8"), - ("B", "f8"), - ("\u20B9", "f8")]) + def test_multiple_field_name_unicode(self): + def test_dtype_unicode(): + np.dtype([("\u20B9", "f8"), ("B", "f8"), ("\u20B9", "f8")]) - # Error raised when multiple fields have the same name(unicode included) - assert_raises(ValueError, test_assign_unicode) + # Error raised when multiple fields have the same name(unicode included) + assert_raises(ValueError, test_dtype_unicode) - else: - def test_unicode_field_titles(self): - # Unicode field titles are added to field dict on Py2 - title = u'b' - dt = np.dtype([((title, 'a'), int)]) - dt[title] - dt['a'] - x = np.array([(1,), (2,), (3,)], dtype=dt) - x[title] - x['a'] - y = x[0] - y[title] - y['a'] - - def test_unicode_field_names(self): - # Unicode field names are converted to ascii on Python 2: - encodable_name = u'b' - assert_equal(np.dtype([(encodable_name, int)]).names[0], b'b') - assert_equal(np.dtype([(('a', encodable_name), int)]).names[0], b'b') - - # But raises UnicodeEncodeError if it can't be encoded: - nonencodable_name = u'\uc3bc' - assert_raises(UnicodeEncodeError, np.dtype, [(nonencodable_name, int)]) - assert_raises(UnicodeEncodeError, np.dtype, [(('a', nonencodable_name), int)]) + def test_fromarrays_unicode(self): + # A single name string provided to fromarrays() is allowed to be unicode + # on both Python 2 and 3: + x = np.core.records.fromarrays([[0], [1]], names=u'a,b', formats=u'i4,i4') + assert_equal(x['a'][0], 0) + assert_equal(x['b'][0], 1) + + def test_unicode_order(self): + # Test that we can sort with order as a unicode field name in both Python 2 and + # 3: + name = u'b' + x = np.array([1, 3, 2], dtype=[(name, int)]) + x.sort(order=name) + assert_equal(x[u'b'], np.array([1, 2, 3])) def test_field_names(self): # Test unicode and 8-bit / byte strings can be used a = np.zeros((1,), dtype=[('f1', 'i4'), ('f2', 'i4'), ('f3', [('sf1', 'i4')])]) - is_py3 = sys.version_info[0] >= 3 - if is_py3: - funcs = (str,) - # byte string indexing fails gracefully - assert_raises(IndexError, a.__setitem__, b'f1', 1) - assert_raises(IndexError, a.__getitem__, b'f1') - assert_raises(IndexError, a['f1'].__setitem__, b'sf1', 1) - assert_raises(IndexError, a['f1'].__getitem__, b'sf1') - else: - funcs = (str, unicode) - for func in funcs: - b = a.copy() - fn1 = func('f1') - b[fn1] = 1 - assert_equal(b[fn1], 1) - fnn = func('not at all') - assert_raises(ValueError, b.__setitem__, fnn, 1) - assert_raises(ValueError, b.__getitem__, fnn) - b[0][fn1] = 2 - assert_equal(b[fn1], 2) - # Subfield - assert_raises(ValueError, b[0].__setitem__, fnn, 1) - assert_raises(ValueError, b[0].__getitem__, fnn) - # Subfield - fn3 = func('f3') - sfn1 = func('sf1') - b[fn3][sfn1] = 1 - assert_equal(b[fn3][sfn1], 1) - assert_raises(ValueError, b[fn3].__setitem__, fnn, 1) - assert_raises(ValueError, b[fn3].__getitem__, fnn) - # multiple subfields - fn2 = func('f2') - b[fn2] = 3 - - # In 1.16 code below can be replaced by: - # assert_equal(b[['f1', 'f2']][0].tolist(), (2, 3)) - # assert_equal(b[['f2', 'f1']][0].tolist(), (3, 2)) - # assert_equal(b[['f1', 'f3']][0].tolist(), (2, (1,))) - with suppress_warnings() as sup: - sup.filter(FutureWarning, - ".* selecting multiple fields .*") - - assert_equal(b[['f1', 'f2']][0].tolist(), (2, 3)) - assert_equal(b[['f2', 'f1']][0].tolist(), (3, 2)) - assert_equal(b[['f1', 'f3']][0].tolist(), (2, (1,))) - # view of subfield view/copy - assert_equal(b[['f1', 'f2']][0].view(('i4', 2)).tolist(), - (2, 3)) - assert_equal(b[['f2', 'f1']][0].view(('i4', 2)).tolist(), - (3, 2)) - view_dtype = [('f1', 'i4'), ('f3', [('', 'i4')])] - assert_equal(b[['f1', 'f3']][0].view(view_dtype).tolist(), - (2, (1,))) + # byte string indexing fails gracefully + assert_raises(IndexError, a.__setitem__, b'f1', 1) + assert_raises(IndexError, a.__getitem__, b'f1') + assert_raises(IndexError, a['f1'].__setitem__, b'sf1', 1) + assert_raises(IndexError, a['f1'].__getitem__, b'sf1') + b = a.copy() + fn1 = str('f1') + b[fn1] = 1 + assert_equal(b[fn1], 1) + fnn = str('not at all') + assert_raises(ValueError, b.__setitem__, fnn, 1) + assert_raises(ValueError, b.__getitem__, fnn) + b[0][fn1] = 2 + assert_equal(b[fn1], 2) + # Subfield + assert_raises(ValueError, b[0].__setitem__, fnn, 1) + assert_raises(ValueError, b[0].__getitem__, fnn) + # Subfield + fn3 = str('f3') + sfn1 = str('sf1') + b[fn3][sfn1] = 1 + assert_equal(b[fn3][sfn1], 1) + assert_raises(ValueError, b[fn3].__setitem__, fnn, 1) + assert_raises(ValueError, b[fn3].__getitem__, fnn) + # multiple subfields + fn2 = str('f2') + b[fn2] = 3 + + assert_equal(b[['f1', 'f2']][0].tolist(), (2, 3)) + assert_equal(b[['f2', 'f1']][0].tolist(), (3, 2)) + assert_equal(b[['f1', 'f3']][0].tolist(), (2, (1,))) # non-ascii unicode field indexing is well behaved - if not is_py3: - raise SkipTest('non ascii unicode field indexing skipped; ' - 'raises segfault on python 2.x') - else: - assert_raises(ValueError, a.__setitem__, u'\u03e0', 1) - assert_raises(ValueError, a.__getitem__, u'\u03e0') - - # can be removed in 1.16 - def test_field_names_deprecation(self): - - def collect_warnings(f, *args, **kwargs): - with warnings.catch_warnings(record=True) as log: - warnings.simplefilter("always") - f(*args, **kwargs) - return [w.category for w in log] - - a = np.zeros((1,), dtype=[('f1', 'i4'), - ('f2', 'i4'), - ('f3', [('sf1', 'i4')])]) - a['f1'][0] = 1 - a['f2'][0] = 2 - a['f3'][0] = (3,) - b = np.zeros((1,), dtype=[('f1', 'i4'), - ('f2', 'i4'), - ('f3', [('sf1', 'i4')])]) - b['f1'][0] = 1 - b['f2'][0] = 2 - b['f3'][0] = (3,) - - # All the different functions raise a warning, but not an error - assert_equal(collect_warnings(a[['f1', 'f2']].__setitem__, 0, (10, 20)), - [FutureWarning]) - # For <=1.12 a is not modified, but it will be in 1.13 - assert_equal(a, b) - - # Views also warn - subset = a[['f1', 'f2']] - subset_view = subset.view() - assert_equal(collect_warnings(subset_view['f1'].__setitem__, 0, 10), - [FutureWarning]) - # But the write goes through: - assert_equal(subset['f1'][0], 10) - # Only one warning per multiple field indexing, though (even if there - # are multiple views involved): - assert_equal(collect_warnings(subset['f1'].__setitem__, 0, 10), []) - - # make sure views of a multi-field index warn too - c = np.zeros(3, dtype='i8,i8,i8') - assert_equal(collect_warnings(c[['f0', 'f2']].view, 'i8,i8'), - [FutureWarning]) - + assert_raises(ValueError, a.__setitem__, u'\u03e0', 1) + assert_raises(ValueError, a.__getitem__, u'\u03e0') def test_record_hash(self): a = np.array([(1, 2), (1, 2)], dtype='i1,i2') @@ -4892,7 +5640,17 @@ def test_empty_structure_creation(self): np.array([(), (), (), (), ()], dtype={'names': [], 'formats': [], 'offsets': [], 'itemsize': 12}) -class TestView(object): + def test_multifield_indexing_view(self): + a = np.ones(3, dtype=[('a', 'i4'), ('b', 'f4'), ('c', 'u4')]) + v = a[['a', 'c']] + assert_(v.base is a) + assert_(v.dtype == np.dtype({'names': ['a', 'c'], + 'formats': ['i4', 'u4'], + 'offsets': [0, 8]})) + v[:] = (4,5) + assert_equal(a[0].item(), (4, 1, 5)) + +class TestView: def test_basic(self): x = np.array([(1, 2, 3, 4), (5, 6, 7, 8)], dtype=[('r', np.int8), ('g', np.int8), @@ -4917,7 +5675,7 @@ def _std(a, **args): return a.std(**args) -class TestStats(object): +class TestStats: funcs = [_mean, _var, _std] @@ -5089,6 +5847,48 @@ def test_mean_float16(self): # of float32. assert_(_mean(np.ones(100000, dtype='float16')) == 1) + def test_mean_axis_error(self): + # Ensure that AxisError is raised instead of IndexError when axis is + # out of bounds, see gh-15817. + with assert_raises(np.core._exceptions.AxisError): + np.arange(10).mean(axis=2) + + def test_mean_where(self): + a = np.arange(16).reshape((4, 4)) + wh_full = np.array([[False, True, False, True], + [True, False, True, False], + [True, True, False, False], + [False, False, True, True]]) + wh_partial = np.array([[False], + [True], + [True], + [False]]) + _cases = [(1, True, [1.5, 5.5, 9.5, 13.5]), + (0, wh_full, [6., 5., 10., 9.]), + (1, wh_full, [2., 5., 8.5, 14.5]), + (0, wh_partial, [6., 7., 8., 9.])] + for _ax, _wh, _res in _cases: + assert_allclose(a.mean(axis=_ax, where=_wh), + np.array(_res)) + assert_allclose(np.mean(a, axis=_ax, where=_wh), + np.array(_res)) + + a3d = np.arange(16).reshape((2, 2, 4)) + _wh_partial = np.array([False, True, True, False]) + _res = [[1.5, 5.5], [9.5, 13.5]] + assert_allclose(a3d.mean(axis=2, where=_wh_partial), + np.array(_res)) + assert_allclose(np.mean(a3d, axis=2, where=_wh_partial), + np.array(_res)) + + with pytest.warns(RuntimeWarning) as w: + assert_allclose(a.mean(axis=1, where=wh_partial), + np.array([np.nan, 5.5, 9.5, np.nan])) + with pytest.warns(RuntimeWarning) as w: + assert_equal(a.mean(where=False), np.nan) + with pytest.warns(RuntimeWarning) as w: + assert_equal(np.mean(a, where=False), np.nan) + def test_var_values(self): for mat in [self.rmat, self.cmat, self.omat]: for axis in [0, 1, None]: @@ -5098,6 +5898,82 @@ def test_var_values(self): res = _var(mat, axis=axis) assert_almost_equal(res, tgt) + @pytest.mark.parametrize(('complex_dtype', 'ndec'), ( + ('complex64', 6), + ('complex128', 7), + ('clongdouble', 7), + )) + def test_var_complex_values(self, complex_dtype, ndec): + # Test fast-paths for every builtin complex type + for axis in [0, 1, None]: + mat = self.cmat.copy().astype(complex_dtype) + msqr = _mean(mat * mat.conj(), axis=axis) + mean = _mean(mat, axis=axis) + tgt = msqr - mean * mean.conjugate() + res = _var(mat, axis=axis) + assert_almost_equal(res, tgt, decimal=ndec) + + def test_var_dimensions(self): + # _var paths for complex number introduce additions on views that + # increase dimensions. Ensure this generalizes to higher dims + mat = np.stack([self.cmat]*3) + for axis in [0, 1, 2, -1, None]: + msqr = _mean(mat * mat.conj(), axis=axis) + mean = _mean(mat, axis=axis) + tgt = msqr - mean * mean.conjugate() + res = _var(mat, axis=axis) + assert_almost_equal(res, tgt) + + def test_var_complex_byteorder(self): + # Test that var fast-path does not cause failures for complex arrays + # with non-native byteorder + cmat = self.cmat.copy().astype('complex128') + cmat_swapped = cmat.astype(cmat.dtype.newbyteorder()) + assert_almost_equal(cmat.var(), cmat_swapped.var()) + + def test_var_axis_error(self): + # Ensure that AxisError is raised instead of IndexError when axis is + # out of bounds, see gh-15817. + with assert_raises(np.core._exceptions.AxisError): + np.arange(10).var(axis=2) + + def test_var_where(self): + a = np.arange(25).reshape((5, 5)) + wh_full = np.array([[False, True, False, True, True], + [True, False, True, True, False], + [True, True, False, False, True], + [False, True, True, False, True], + [True, False, True, True, False]]) + wh_partial = np.array([[False], + [True], + [True], + [False], + [True]]) + _cases = [(0, True, [50., 50., 50., 50., 50.]), + (1, True, [2., 2., 2., 2., 2.])] + for _ax, _wh, _res in _cases: + assert_allclose(a.var(axis=_ax, where=_wh), + np.array(_res)) + assert_allclose(np.var(a, axis=_ax, where=_wh), + np.array(_res)) + + a3d = np.arange(16).reshape((2, 2, 4)) + _wh_partial = np.array([False, True, True, False]) + _res = [[0.25, 0.25], [0.25, 0.25]] + assert_allclose(a3d.var(axis=2, where=_wh_partial), + np.array(_res)) + assert_allclose(np.var(a3d, axis=2, where=_wh_partial), + np.array(_res)) + + assert_allclose(np.var(a, axis=1, where=wh_full), + np.var(a[wh_full].reshape((5, 3)), axis=1)) + assert_allclose(np.var(a, axis=0, where=wh_partial), + np.var(a[wh_partial[:,0]], axis=0)) + with pytest.warns(RuntimeWarning) as w: + assert_equal(a.var(where=False), np.nan) + with pytest.warns(RuntimeWarning) as w: + assert_equal(np.var(a, where=False), np.nan) + def test_std_values(self): for mat in [self.rmat, self.cmat, self.omat]: for axis in [0, 1, None]: @@ -5105,6 +5981,50 @@ def test_std_values(self): res = _std(mat, axis=axis) assert_almost_equal(res, tgt) + def test_std_where(self): + a = np.arange(25).reshape((5,5))[::-1] + whf = np.array([[False, True, False, True, True], + [True, False, True, False, True], + [True, True, False, True, False], + [True, False, True, True, False], + [False, True, False, True, True]]) + whp = np.array([[False], + [False], + [True], + [True], + [False]]) + _cases = [ + (0, True, 7.07106781*np.ones((5))), + (1, True, 1.41421356*np.ones((5))), + (0, whf, + np.array([4.0824829 , 8.16496581, 5., 7.39509973, 8.49836586])), + (0, whp, 2.5*np.ones((5))) + ] + for _ax, _wh, _res in _cases: + assert_allclose(a.std(axis=_ax, where=_wh), _res) + assert_allclose(np.std(a, axis=_ax, where=_wh), _res) + + a3d = np.arange(16).reshape((2, 2, 4)) + _wh_partial = np.array([False, True, True, False]) + _res = [[0.5, 0.5], [0.5, 0.5]] + assert_allclose(a3d.std(axis=2, where=_wh_partial), + np.array(_res)) + assert_allclose(np.std(a3d, axis=2, where=_wh_partial), + np.array(_res)) + + assert_allclose(a.std(axis=1, where=whf), + np.std(a[whf].reshape((5,3)), axis=1)) + assert_allclose(np.std(a, axis=1, where=whf), + (a[whf].reshape((5,3))).std(axis=1)) + assert_allclose(a.std(axis=0, where=whp), + np.std(a[whp[:,0]], axis=0)) + assert_allclose(np.std(a, axis=0, where=whp), + (a[whp[:,0]]).std(axis=0)) + with pytest.warns(RuntimeWarning) as w: + assert_equal(a.std(where=False), np.nan) + with pytest.warns(RuntimeWarning) as w: + assert_equal(np.std(a, where=False), np.nan) + def test_subclass(self): class TestArray(np.ndarray): def __new__(cls, data, info): @@ -5124,7 +6044,8 @@ def __array_finalize__(self, obj): res = dat.var(1) assert_(res.info == dat.info) -class TestVdot(object): + +class TestVdot: def test_basic(self): dt_numeric = np.typecodes['AllFloat'] + np.typecodes['AllInteger'] dt_complex = np.typecodes['Complex'] @@ -5184,7 +6105,7 @@ def test_vdot_uncontiguous(self): np.vdot(a.flatten(), b.flatten())) -class TestDot(object): +class TestDot: def setup(self): np.random.seed(128) self.A = np.random.rand(4, 2) @@ -5286,7 +6207,7 @@ def test_all(self): assert_almost_equal(res, tgt, decimal=self.N) def test_vecobject(self): - class Vec(object): + class Vec: def __init__(self, sequence=None): if sequence is None: sequence = [] @@ -5415,7 +6336,7 @@ def assert_dot_close(A, X, desired): s = aligned_array((100, 100), 15, np.float32) np.dot(s, m) # this will always segfault if the bug is present - testdata = itertools.product((15,32), (10000,), (200,89), ('C','F')) + testdata = itertools.product((15, 32), (10000,), (200, 89), ('C', 'F')) for align, m, n, a_order in testdata: # Calculation in double precision A_d = np.random.rand(m, n) @@ -5455,13 +6376,14 @@ def assert_dot_close(A, X, desired): assert_dot_close(A_f_12, X_f_2, desired) -class MatmulCommon(object): + +class MatmulCommon: """Common tests for '@' operator and numpy.matmul. """ # Should work with these types. Will want to add # "O" at some point - types = "?bhilqBHILQefdgFDG" + types = "?bhilqBHILQefdgFDGO" def test_exceptions(self): dims = [ @@ -5512,16 +6434,40 @@ def test_result_types(self): assert_(res.dtype == dt) # vector vector returns scalars - res = self.matmul(v, v) - assert_(type(res) is np.dtype(dt).type) + if dt != "O": + res = self.matmul(v, v) + assert_(type(res) is np.dtype(dt).type) + + def test_scalar_output(self): + vec1 = np.array([2]) + vec2 = np.array([3, 4]).reshape(1, -1) + tgt = np.array([6, 8]) + for dt in self.types[1:]: + v1 = vec1.astype(dt) + v2 = vec2.astype(dt) + res = self.matmul(v1, v2) + assert_equal(res, tgt) + res = self.matmul(v2.T, v1) + assert_equal(res, tgt) + + # boolean type + vec = np.array([True, True], dtype='?').reshape(1, -1) + res = self.matmul(vec[:, 0], vec) + assert_equal(res, True) def test_vector_vector_values(self): - vec = np.array([1, 2]) - tgt = 5 + vec1 = np.array([1, 2]) + vec2 = np.array([3, 4]).reshape(-1, 1) + tgt1 = np.array([11]) + tgt2 = np.array([[3, 6], [4, 8]]) for dt in self.types[1:]: - v1 = vec.astype(dt) - res = self.matmul(v1, v1) - assert_equal(res, tgt) + v1 = vec1.astype(dt) + v2 = vec2.astype(dt) + res = self.matmul(v1, v2) + assert_equal(res, tgt1) + # no broadcast, we must make v1 into a 2d ndarray + res = self.matmul(v2, v1.reshape(1, -1)) + assert_equal(res, tgt2) # boolean type vec = np.array([True, True], dtype='?') @@ -5652,83 +6598,219 @@ class TestMatmul(MatmulCommon): matmul = np.matmul def test_out_arg(self): - a = np.ones((2, 2), dtype=float) - b = np.ones((2, 2), dtype=float) - tgt = np.full((2,2), 2, dtype=float) + a = np.ones((5, 2), dtype=float) + b = np.array([[1, 3], [5, 7]], dtype=float) + tgt = np.dot(a, b) # test as positional argument msg = "out positional argument" - out = np.zeros((2, 2), dtype=float) + out = np.zeros((5, 2), dtype=float) self.matmul(a, b, out) assert_array_equal(out, tgt, err_msg=msg) # test as keyword argument msg = "out keyword argument" - out = np.zeros((2, 2), dtype=float) + out = np.zeros((5, 2), dtype=float) self.matmul(a, b, out=out) assert_array_equal(out, tgt, err_msg=msg) # test out with not allowed type cast (safe casting) - # einsum and cblas raise different error types, so - # use Exception. - msg = "out argument with illegal cast" - out = np.zeros((2, 2), dtype=np.int32) - assert_raises(Exception, self.matmul, a, b, out=out) - - # skip following tests for now, cblas does not allow non-contiguous - # outputs and consistency with dot would require same type, - # dimensions, subtype, and c_contiguous. - - # test out with allowed type cast - # msg = "out argument with allowed cast" - # out = np.zeros((2, 2), dtype=np.complex128) - # self.matmul(a, b, out=out) - # assert_array_equal(out, tgt, err_msg=msg) + msg = "Cannot cast ufunc .* output" + out = np.zeros((5, 2), dtype=np.int32) + assert_raises_regex(TypeError, msg, self.matmul, a, b, out=out) + + # test out with type upcast to complex + out = np.zeros((5, 2), dtype=np.complex128) + c = self.matmul(a, b, out=out) + assert_(c is out) + with suppress_warnings() as sup: + sup.filter(np.ComplexWarning, '') + c = c.astype(tgt.dtype) + assert_array_equal(c, tgt) + + def test_empty_out(self): + # Check that the output cannot be broadcast, so that it cannot be + # size zero when the outer dimensions (iterator size) has size zero. + arr = np.ones((0, 1, 1)) + out = np.ones((1, 1, 1)) + assert self.matmul(arr, arr).shape == (0, 1, 1) + + with pytest.raises(ValueError, match=r"non-broadcastable"): + self.matmul(arr, arr, out=out) + + def test_out_contiguous(self): + a = np.ones((5, 2), dtype=float) + b = np.array([[1, 3], [5, 7]], dtype=float) + v = np.array([1, 3], dtype=float) + tgt = np.dot(a, b) + tgt_mv = np.dot(a, v) # test out non-contiguous - # msg = "out argument with non-contiguous layout" - # c = np.zeros((2, 2, 2), dtype=float) - # self.matmul(a, b, out=c[..., 0]) - # assert_array_equal(c, tgt, err_msg=msg) + out = np.ones((5, 2, 2), dtype=float) + c = self.matmul(a, b, out=out[..., 0]) + assert c.base is out + assert_array_equal(c, tgt) + c = self.matmul(a, v, out=out[:, 0, 0]) + assert_array_equal(c, tgt_mv) + c = self.matmul(v, a.T, out=out[:, 0, 0]) + assert_array_equal(c, tgt_mv) + + # test out contiguous in only last dim + out = np.ones((10, 2), dtype=float) + c = self.matmul(a, b, out=out[::2, :]) + assert_array_equal(c, tgt) + + # test transposes of out, args + out = np.ones((5, 2), dtype=float) + c = self.matmul(b.T, a.T, out=out.T) + assert_array_equal(out, tgt) + + m1 = np.arange(15.).reshape(5, 3) + m2 = np.arange(21.).reshape(3, 7) + m3 = np.arange(30.).reshape(5, 6)[:, ::2] # non-contiguous + vc = np.arange(10.) + vr = np.arange(6.) + m0 = np.zeros((3, 0)) + @pytest.mark.parametrize('args', ( + # matrix-matrix + (m1, m2), (m2.T, m1.T), (m2.T.copy(), m1.T), (m2.T, m1.T.copy()), + # matrix-matrix-transpose, contiguous and non + (m1, m1.T), (m1.T, m1), (m1, m3.T), (m3, m1.T), + (m3, m3.T), (m3.T, m3), + # matrix-matrix non-contiguous + (m3, m2), (m2.T, m3.T), (m2.T.copy(), m3.T), + # vector-matrix, matrix-vector, contiguous + (m1, vr[:3]), (vc[:5], m1), (m1.T, vc[:5]), (vr[:3], m1.T), + # vector-matrix, matrix-vector, vector non-contiguous + (m1, vr[::2]), (vc[::2], m1), (m1.T, vc[::2]), (vr[::2], m1.T), + # vector-matrix, matrix-vector, matrix non-contiguous + (m3, vr[:3]), (vc[:5], m3), (m3.T, vc[:5]), (vr[:3], m3.T), + # vector-matrix, matrix-vector, both non-contiguous + (m3, vr[::2]), (vc[::2], m3), (m3.T, vc[::2]), (vr[::2], m3.T), + # size == 0 + (m0, m0.T), (m0.T, m0), (m1, m0), (m0.T, m1.T), + )) + def test_dot_equivalent(self, args): + r1 = np.matmul(*args) + r2 = np.dot(*args) + assert_equal(r1, r2) + r3 = np.matmul(args[0].copy(), args[1].copy()) + assert_equal(r1, r3) -if sys.version_info[:2] >= (3, 5): - class TestMatmulOperator(MatmulCommon): - import operator - matmul = operator.matmul + def test_matmul_object(self): + import fractions - def test_array_priority_override(self): + f = np.vectorize(fractions.Fraction) + def random_ints(): + return np.random.randint(1, 1000, size=(10, 3, 3)) + M1 = f(random_ints(), random_ints()) + M2 = f(random_ints(), random_ints()) - class A(object): - __array_priority__ = 1000 + M3 = self.matmul(M1, M2) - def __matmul__(self, other): - return "A" + [N1, N2, N3] = [a.astype(float) for a in [M1, M2, M3]] - def __rmatmul__(self, other): - return "A" + assert_allclose(N3, self.matmul(N1, N2)) - a = A() - b = np.ones(2) - assert_equal(self.matmul(a, b), "A") - assert_equal(self.matmul(b, a), "A") + def test_matmul_object_type_scalar(self): + from fractions import Fraction as F + v = np.array([F(2,3), F(5,7)]) + res = self.matmul(v, v) + assert_(type(res) is F) + + def test_matmul_empty(self): + a = np.empty((3, 0), dtype=object) + b = np.empty((0, 3), dtype=object) + c = np.zeros((3, 3)) + assert_array_equal(np.matmul(a, b), c) + + def test_matmul_exception_multiply(self): + # test that matmul fails if `__mul__` is missing + class add_not_multiply(): + def __add__(self, other): + return self + a = np.full((3,3), add_not_multiply()) + with assert_raises(TypeError): + b = np.matmul(a, a) + + def test_matmul_exception_add(self): + # test that matmul fails if `__add__` is missing + class multiply_not_add(): + def __mul__(self, other): + return self + a = np.full((3,3), multiply_not_add()) + with assert_raises(TypeError): + b = np.matmul(a, a) + + def test_matmul_bool(self): + # gh-14439 + a = np.array([[1, 0],[1, 1]], dtype=bool) + assert np.max(a.view(np.uint8)) == 1 + b = np.matmul(a, a) + # matmul with boolean output should always be 0, 1 + assert np.max(b.view(np.uint8)) == 1 + + rg = np.random.default_rng(np.random.PCG64(43)) + d = rg.integers(2, size=4*5, dtype=np.int8) + d = d.reshape(4, 5) > 0 + out1 = np.matmul(d, d.reshape(5, 4)) + out2 = np.dot(d, d.reshape(5, 4)) + assert_equal(out1, out2) + + c = np.matmul(np.zeros((2, 0), dtype=bool), np.zeros(0, dtype=bool)) + assert not np.any(c) - def test_matmul_inplace(): - # It would be nice to support in-place matmul eventually, but for now - # we don't have a working implementation, so better just to error out - # and nudge people to writing "a = a @ b". - a = np.eye(3) - b = np.eye(3) - assert_raises(TypeError, a.__imatmul__, b) - import operator - assert_raises(TypeError, operator.imatmul, a, b) - # we avoid writing the token `exec` so as not to crash python 2's - # parser - exec_ = getattr(builtins, "exec") - assert_raises(TypeError, exec_, "a @= b", globals(), locals()) +class TestMatmulOperator(MatmulCommon): + import operator + matmul = operator.matmul -class TestInner(object): + def test_array_priority_override(self): + + class A: + __array_priority__ = 1000 + + def __matmul__(self, other): + return "A" + + def __rmatmul__(self, other): + return "A" + + a = A() + b = np.ones(2) + assert_equal(self.matmul(a, b), "A") + assert_equal(self.matmul(b, a), "A") + + def test_matmul_raises(self): + assert_raises(TypeError, self.matmul, np.int8(5), np.int8(5)) + assert_raises(TypeError, self.matmul, np.void(b'abc'), np.void(b'abc')) + assert_raises(TypeError, self.matmul, np.arange(10), np.void(b'abc')) + +def test_matmul_inplace(): + # It would be nice to support in-place matmul eventually, but for now + # we don't have a working implementation, so better just to error out + # and nudge people to writing "a = a @ b". + a = np.eye(3) + b = np.eye(3) + assert_raises(TypeError, a.__imatmul__, b) + import operator + assert_raises(TypeError, operator.imatmul, a, b) + assert_raises(TypeError, exec, "a @= b", globals(), locals()) + +def test_matmul_axes(): + a = np.arange(3*4*5).reshape(3, 4, 5) + c = np.matmul(a, a, axes=[(-2, -1), (-1, -2), (1, 2)]) + assert c.shape == (3, 4, 4) + d = np.matmul(a, a, axes=[(-2, -1), (-1, -2), (0, 1)]) + assert d.shape == (4, 4, 3) + e = np.swapaxes(d, 0, 2) + assert_array_equal(e, c) + f = np.matmul(a, np.arange(3), axes=[(1, 0), (0), (0)]) + assert f.shape == (4, 5) + + +class TestInner: def test_inner_type_mismatch(self): c = 1. @@ -5806,25 +6888,27 @@ def test_3d_tensor(self): assert_equal(np.inner(b, a).transpose(2,3,0,1), desired) -class TestAlen(object): +class TestAlen: def test_basic(self): - m = np.array([1, 2, 3]) - assert_equal(np.alen(m), 3) + with pytest.warns(DeprecationWarning): + m = np.array([1, 2, 3]) + assert_equal(np.alen(m), 3) - m = np.array([[1, 2, 3], [4, 5, 7]]) - assert_equal(np.alen(m), 2) + m = np.array([[1, 2, 3], [4, 5, 7]]) + assert_equal(np.alen(m), 2) - m = [1, 2, 3] - assert_equal(np.alen(m), 3) + m = [1, 2, 3] + assert_equal(np.alen(m), 3) - m = [[1, 2, 3], [4, 5, 7]] - assert_equal(np.alen(m), 2) + m = [[1, 2, 3], [4, 5, 7]] + assert_equal(np.alen(m), 2) def test_singleton(self): - assert_equal(np.alen(5), 1) + with pytest.warns(DeprecationWarning): + assert_equal(np.alen(5), 1) -class TestChoose(object): +class TestChoose: def setup(self): self.x = 2*np.ones((3,), dtype=int) self.y = 3*np.ones((3,), dtype=int) @@ -5844,8 +6928,17 @@ def test_broadcast2(self): A = np.choose(self.ind, (self.x, self.y2)) assert_equal(A, [[2, 2, 3], [2, 2, 3]]) + @pytest.mark.parametrize("ops", + [(1000, np.array([1], dtype=np.uint8)), + (-1, np.array([1], dtype=np.uint8)), + (1., np.float32(3)), + (1., np.array([3], dtype=np.float32))],) + def test_output_dtype(self, ops): + expected_dt = np.result_type(*ops) + assert(np.choose([0], ops).dtype == expected_dt) + -class TestRepeat(object): +class TestRepeat: def setup(self): self.m = np.array([1, 2, 3, 4, 5, 6]) self.m_rect = self.m.reshape((2, 3)) @@ -5886,9 +6979,10 @@ def test_broadcast2(self): NEIGH_MODE = {'zero': 0, 'one': 1, 'constant': 2, 'circular': 3, 'mirror': 4} -class TestNeighborhoodIter(object): +@pytest.mark.parametrize('dt', [float, Decimal], ids=['float', 'object']) +class TestNeighborhoodIter: # Simple, 2d tests - def _test_simple2d(self, dt): + def test_simple2d(self, dt): # Test zero and one padding for simple data type x = np.array([[0, 1], [2, 3]], dtype=dt) r = [np.array([[0, 0, 0], [0, 0, 1]], dtype=dt), @@ -5915,13 +7009,14 @@ def _test_simple2d(self, dt): x, [-1, 0, -1, 1], 4, NEIGH_MODE['constant']) assert_array_equal(l, r) - def test_simple2d(self): - self._test_simple2d(float) - - def test_simple2d_object(self): - self._test_simple2d(Decimal) + # Test with start in the middle + r = [np.array([[4, 0, 1], [4, 2, 3]], dtype=dt), + np.array([[0, 1, 4], [2, 3, 4]], dtype=dt)] + l = _multiarray_tests.test_neighborhood_iterator( + x, [-1, 0, -1, 1], 4, NEIGH_MODE['constant'], 2) + assert_array_equal(l, r) - def _test_mirror2d(self, dt): + def test_mirror2d(self, dt): x = np.array([[0, 1], [2, 3]], dtype=dt) r = [np.array([[0, 0, 1], [0, 0, 1]], dtype=dt), np.array([[0, 1, 1], [0, 1, 1]], dtype=dt), @@ -5931,14 +7026,8 @@ def _test_mirror2d(self, dt): x, [-1, 0, -1, 1], x[0], NEIGH_MODE['mirror']) assert_array_equal(l, r) - def test_mirror2d(self): - self._test_mirror2d(float) - - def test_mirror2d_object(self): - self._test_mirror2d(Decimal) - # Simple, 1d tests - def _test_simple(self, dt): + def test_simple(self, dt): # Test padding with constant values x = np.linspace(1, 5, 5).astype(dt) r = [[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 0]] @@ -5956,14 +7045,8 @@ def _test_simple(self, dt): x, [-1, 1], x[4], NEIGH_MODE['constant']) assert_array_equal(l, r) - def test_simple_float(self): - self._test_simple(float) - - def test_simple_object(self): - self._test_simple(Decimal) - # Test mirror modes - def _test_mirror(self, dt): + def test_mirror(self, dt): x = np.linspace(1, 5, 5).astype(dt) r = np.array([[2, 1, 1, 2, 3], [1, 1, 2, 3, 4], [1, 2, 3, 4, 5], [2, 3, 4, 5, 5], [3, 4, 5, 5, 4]], dtype=dt) @@ -5972,14 +7055,8 @@ def _test_mirror(self, dt): assert_([i.dtype == dt for i in l]) assert_array_equal(l, r) - def test_mirror(self): - self._test_mirror(float) - - def test_mirror_object(self): - self._test_mirror(Decimal) - # Circular mode - def _test_circular(self, dt): + def test_circular(self, dt): x = np.linspace(1, 5, 5).astype(dt) r = np.array([[4, 5, 1, 2, 3], [5, 1, 2, 3, 4], [1, 2, 3, 4, 5], [2, 3, 4, 5, 1], [3, 4, 5, 1, 2]], dtype=dt) @@ -5987,14 +7064,9 @@ def _test_circular(self, dt): x, [-2, 2], x[0], NEIGH_MODE['circular']) assert_array_equal(l, r) - def test_circular(self): - self._test_circular(float) - - def test_circular_object(self): - self._test_circular(Decimal) # Test stacking neighborhood iterators -class TestStackedNeighborhoodIter(object): +class TestStackedNeighborhoodIter: # Simple, 1d test: stacking 2 constant-padded neigh iterators def test_simple_const(self): dt = np.float64 @@ -6144,7 +7216,7 @@ def test_simple_strict_within(self): x, [1, 1], NEIGH_MODE['zero'], [-1, 2], NEIGH_MODE['circular']) assert_array_equal(l, r) -class TestWarnings(object): +class TestWarnings: def test_complex_warning(self): x = np.array([1, 2]) @@ -6156,7 +7228,7 @@ def test_complex_warning(self): assert_equal(x, [1, 2]) -class TestMinScalarType(object): +class TestMinScalarType: def test_usigned_shortshort(self): dt = np.min_scalar_type(2**8-1) @@ -6187,7 +7259,7 @@ def test_object(self): from numpy.core._internal import _dtype_from_pep3118 -class TestPEP3118Dtype(object): +class TestPEP3118Dtype: def _check(self, spec, wanted): dt = np.dtype(wanted) actual = _dtype_from_pep3118(spec) @@ -6294,7 +7366,7 @@ def test_unnamed_fields(self): self._check('i:f0:', [('f0', 'i')]) -class TestNewBufferProtocol(object): +class TestNewBufferProtocol: """ Test PEP3118 buffers """ def _check_roundtrip(self, obj): @@ -6403,7 +7475,7 @@ def test_roundtrip_half(self): self._check_roundtrip(x) def test_roundtrip_single_types(self): - for typ in np.typeDict.values(): + for typ in np.sctypeDict.values(): dtype = np.dtype(typ) if dtype.char in 'Mm': @@ -6429,6 +7501,14 @@ def test_roundtrip_scalar(self): # Issue #4015. self._check_roundtrip(0) + def test_invalid_buffer_format(self): + # datetime64 cannot be used fully in a buffer yet + # Should be fixed in the next Numpy major release + dt = np.dtype([('a', 'uint16'), ('b', 'M8[s]')]) + a = np.empty(3, dt) + assert_raises((ValueError, BufferError), memoryview, a) + assert_raises((ValueError, BufferError), memoryview, np.array((3), 'M8[D]')) + def test_export_simple_1d(self): x = np.array([1, 2, 3, 4, 5], dtype='i') y = memoryview(x) @@ -6436,7 +7516,7 @@ def test_export_simple_1d(self): assert_equal(y.shape, (5,)) assert_equal(y.ndim, 1) assert_equal(y.strides, (4,)) - assert_equal(y.suboffsets, EMPTY) + assert_equal(y.suboffsets, ()) assert_equal(y.itemsize, 4) def test_export_simple_nd(self): @@ -6446,7 +7526,7 @@ def test_export_simple_nd(self): assert_equal(y.shape, (2, 2)) assert_equal(y.ndim, 2) assert_equal(y.strides, (16, 8)) - assert_equal(y.suboffsets, EMPTY) + assert_equal(y.suboffsets, ()) assert_equal(y.itemsize, 8) def test_export_discontiguous(self): @@ -6456,7 +7536,7 @@ def test_export_discontiguous(self): assert_equal(y.shape, (3, 3)) assert_equal(y.ndim, 2) assert_equal(y.strides, (36, 4)) - assert_equal(y.suboffsets, EMPTY) + assert_equal(y.suboffsets, ()) assert_equal(y.itemsize, 4) def test_export_record(self): @@ -6489,7 +7569,7 @@ def test_export_record(self): y = memoryview(x) assert_equal(y.shape, (1,)) assert_equal(y.ndim, 1) - assert_equal(y.suboffsets, EMPTY) + assert_equal(y.suboffsets, ()) sz = sum([np.dtype(b).itemsize for a, b in dt]) if np.dtype('l').itemsize == 4: @@ -6505,10 +7585,10 @@ def test_export_subarray(self): x = np.array(([[1, 2], [3, 4]],), dtype=[('a', ('i', (2, 2)))]) y = memoryview(x) assert_equal(y.format, 'T{(2,2)i:a:}') - assert_equal(y.shape, EMPTY) + assert_equal(y.shape, ()) assert_equal(y.ndim, 0) - assert_equal(y.strides, EMPTY) - assert_equal(y.suboffsets, EMPTY) + assert_equal(y.strides, ()) + assert_equal(y.suboffsets, ()) assert_equal(y.itemsize, 16) def test_export_endian(self): @@ -6532,6 +7612,21 @@ def test_export_flags(self): _multiarray_tests.get_buffer_info, np.arange(5)[::2], ('SIMPLE',)) + @pytest.mark.parametrize(["obj", "error"], [ + pytest.param(np.array([1, 2], dtype=rational), ValueError, id="array"), + pytest.param(rational(1, 2), TypeError, id="scalar")]) + def test_export_and_pickle_user_dtype(self, obj, error): + # User dtypes should export successfully when FORMAT was not requested. + with pytest.raises(error): + _multiarray_tests.get_buffer_info(obj, ("STRIDED_RO", "FORMAT")) + + _multiarray_tests.get_buffer_info(obj, ("STRIDED_RO",)) + + # This is currently also necessary to implement pickling: + pickle_obj = pickle.dumps(obj) + res = pickle.loads(pickle_obj) + assert_array_equal(res, obj) + def test_padding(self): for j in range(8): x = np.array([(1,), (2,)], dtype={'f0': (int, j)}) @@ -6567,9 +7662,10 @@ def test_padded_struct_array(self): x3 = np.arange(dt3.itemsize, dtype=np.int8).view(dt3) self._check_roundtrip(x3) - def test_relaxed_strides(self): - # Test that relaxed strides are converted to non-relaxed - c = np.ones((1, 10, 10), dtype='i8') + @pytest.mark.valgrind_error(reason="leaks buffer info cache temporarily.") + def test_relaxed_strides(self, c=np.ones((1, 10, 10), dtype='i8')): + # Note: c defined as parameter so that it is persistent and leak + # checks will notice gh-16934 (buffer info cache leak). # Check for NPY_RELAXED_STRIDES_CHECKING: if np.ones((10, 1), order="C").flags.f_contiguous: @@ -6594,6 +7690,23 @@ def test_relaxed_strides(self): arr, ['C_CONTIGUOUS']) assert_(strides[-1] == 8) + @pytest.mark.valgrind_error(reason="leaks buffer info cache temporarily.") + @pytest.mark.skipif(not np.ones((10, 1), order="C").flags.f_contiguous, + reason="Test is unnecessary (but fails) without relaxed strides.") + def test_relaxed_strides_buffer_info_leak(self, arr=np.ones((1, 10))): + """Test that alternating export of C- and F-order buffers from + an array which is both C- and F-order when relaxed strides is + active works. + This test defines array in the signature to ensure leaking more + references every time the test is run (catching the leak with + pytest-leaks). + """ + for i in range(10): + _, s = _multiarray_tests.get_buffer_info(arr, ['F_CONTIGUOUS']) + assert s == (8, 8) + _, s = _multiarray_tests.get_buffer_info(arr, ['C_CONTIGUOUS']) + assert s == (80, 8) + def test_out_of_order_fields(self): dt = np.dtype(dict( formats=['<i4', '<i4'], @@ -6608,10 +7721,10 @@ def test_out_of_order_fields(self): memoryview(arr) def test_max_dims(self): - a = np.empty((1,) * 32) + a = np.ones((1,) * 32) self._check_roundtrip(a) - @pytest.mark.skipif(sys.version_info < (2, 7, 7), reason="See gh-11115") + @pytest.mark.slow def test_error_too_many_dims(self): def make_ctype(shape, scalar_type): t = scalar_type @@ -6628,6 +7741,13 @@ def make_ctype(shape, scalar_type): RuntimeError, "ndim", np.array, m) + # The above seems to create some deep cycles, clean them up for + # easier reference count debugging: + del c_u8_33d, m + for i in range(33): + if gc.collect() == 0: + break + def test_error_pointer_type(self): # gh-6741 m = memoryview(ctypes.pointer(ctypes.c_uint8())) @@ -6637,11 +7757,25 @@ def test_error_pointer_type(self): ValueError, "format string", np.array, m) + def test_error_message_unsupported(self): + # wchar has no corresponding numpy type - if this changes in future, we + # need a better way to construct an invalid memoryview format. + t = ctypes.c_wchar * 4 + with assert_raises(ValueError) as cm: + np.array(t()) + + exc = cm.exception + with assert_raises_regex( + NotImplementedError, + r"Unrepresentable .* 'u' \(UCS-2 strings\)" + ): + raise exc.__cause__ + def test_ctypes_integer_via_memoryview(self): # gh-11150, due to bpo-10746 for c_integer in {ctypes.c_int, ctypes.c_long, ctypes.c_longlong}: value = c_integer(42) - with warnings.catch_warnings(record=True) as w: + with warnings.catch_warnings(record=True): warnings.filterwarnings('always', r'.*\bctypes\b', RuntimeWarning) np.asarray(value) @@ -6651,7 +7785,7 @@ class foo(ctypes.Structure): _fields_ = [('a', ctypes.c_uint8), ('b', ctypes.c_uint32)] f = foo(a=1, b=2) - with warnings.catch_warnings(record=True) as w: + with warnings.catch_warnings(record=True): warnings.filterwarnings('always', r'.*\bctypes\b', RuntimeWarning) arr = np.asarray(f) @@ -6660,8 +7794,237 @@ class foo(ctypes.Structure): f.a = 3 assert_equal(arr['a'], 3) + @pytest.mark.parametrize("obj", [np.ones(3), np.ones(1, dtype="i,i")[()]]) + def test_error_if_stored_buffer_info_is_corrupted(self, obj): + """ + If a user extends a NumPy array before 1.20 and then runs it + on NumPy 1.20+. A C-subclassed array might in theory modify + the new buffer-info field. This checks that an error is raised + if this happens (for buffer export), an error is written on delete. + This is a sanity check to help users transition to safe code, it + may be deleted at any point. + """ + # corrupt buffer info: + _multiarray_tests.corrupt_or_fix_bufferinfo(obj) + name = type(obj) + with pytest.raises(RuntimeError, + match=f".*{name} appears to be C subclassed"): + memoryview(obj) + # Fix buffer info again before we delete (or we lose the memory) + _multiarray_tests.corrupt_or_fix_bufferinfo(obj) + + +class TestArrayCreationCopyArgument(object): + + class RaiseOnBool: + + def __bool__(self): + raise ValueError + + true_vals = [True, np._CopyMode.ALWAYS, np.True_] + false_vals = [False, np._CopyMode.IF_NEEDED, np.False_] + + def test_scalars(self): + # Test both numpy and python scalars + for dtype in np.typecodes["All"]: + arr = np.zeros((), dtype=dtype) + scalar = arr[()] + pyscalar = arr.item(0) + + # Test never-copy raises error: + assert_raises(ValueError, np.array, scalar, + copy=np._CopyMode.NEVER) + assert_raises(ValueError, np.array, pyscalar, + copy=np._CopyMode.NEVER) + assert_raises(ValueError, np.array, pyscalar, + copy=self.RaiseOnBool()) + assert_raises(ValueError, _multiarray_tests.npy_ensurenocopy, + [1]) + # Casting with a dtype (to unsigned integers) can be special: + with pytest.raises(ValueError): + np.array(pyscalar, dtype=np.int64, copy=np._CopyMode.NEVER) + + def test_compatible_cast(self): + + # Some types are compatible even though they are different, no + # copy is necessary for them. This is mostly true for some integers + def int_types(byteswap=False): + int_types = (np.typecodes["Integer"] + + np.typecodes["UnsignedInteger"]) + for int_type in int_types: + yield np.dtype(int_type) + if byteswap: + yield np.dtype(int_type).newbyteorder() + + for int1 in int_types(): + for int2 in int_types(True): + arr = np.arange(10, dtype=int1) + + for copy in self.true_vals: + res = np.array(arr, copy=copy, dtype=int2) + assert res is not arr and res.flags.owndata + assert_array_equal(res, arr) + + if int1 == int2: + # Casting is not necessary, base check is sufficient here + for copy in self.false_vals: + res = np.array(arr, copy=copy, dtype=int2) + assert res is arr or res.base is arr + + res = np.array(arr, + copy=np._CopyMode.NEVER, + dtype=int2) + assert res is arr or res.base is arr -class TestArrayAttributeDeletion(object): + else: + # Casting is necessary, assert copy works: + for copy in self.false_vals: + res = np.array(arr, copy=copy, dtype=int2) + assert res is not arr and res.flags.owndata + assert_array_equal(res, arr) + + assert_raises(ValueError, np.array, + arr, copy=np._CopyMode.NEVER, + dtype=int2) + assert_raises(ValueError, np.array, + arr, copy=None, + dtype=int2) + + def test_buffer_interface(self): + + # Buffer interface gives direct memory access (no copy) + arr = np.arange(10) + view = memoryview(arr) + + # Checking bases is a bit tricky since numpy creates another + # memoryview, so use may_share_memory. + for copy in self.true_vals: + res = np.array(view, copy=copy) + assert not np.may_share_memory(arr, res) + for copy in self.false_vals: + res = np.array(view, copy=copy) + assert np.may_share_memory(arr, res) + res = np.array(view, copy=np._CopyMode.NEVER) + assert np.may_share_memory(arr, res) + + def test_array_interfaces(self): + # Array interface gives direct memory access (much like a memoryview) + base_arr = np.arange(10) + + class ArrayLike: + __array_interface__ = base_arr.__array_interface__ + + arr = ArrayLike() + + for copy, val in [(True, None), (np._CopyMode.ALWAYS, None), + (False, arr), (np._CopyMode.IF_NEEDED, arr), + (np._CopyMode.NEVER, arr)]: + res = np.array(arr, copy=copy) + assert res.base is val + + def test___array__(self): + base_arr = np.arange(10) + + class ArrayLike: + def __array__(self): + # __array__ should return a copy, numpy cannot know this + # however. + return base_arr + + arr = ArrayLike() + + for copy in self.true_vals: + res = np.array(arr, copy=copy) + assert_array_equal(res, base_arr) + # An additional copy is currently forced by numpy in this case, + # you could argue, numpy does not trust the ArrayLike. This + # may be open for change: + assert res is not base_arr + + for copy in self.false_vals: + res = np.array(arr, copy=False) + assert_array_equal(res, base_arr) + assert res is base_arr # numpy trusts the ArrayLike + + with pytest.raises(ValueError): + np.array(arr, copy=np._CopyMode.NEVER) + + @pytest.mark.parametrize( + "arr", [np.ones(()), np.arange(81).reshape((9, 9))]) + @pytest.mark.parametrize("order1", ["C", "F", None]) + @pytest.mark.parametrize("order2", ["C", "F", "A", "K"]) + def test_order_mismatch(self, arr, order1, order2): + # The order is the main (python side) reason that can cause + # a never-copy to fail. + # Prepare C-order, F-order and non-contiguous arrays: + arr = arr.copy(order1) + if order1 == "C": + assert arr.flags.c_contiguous + elif order1 == "F": + assert arr.flags.f_contiguous + elif arr.ndim != 0: + # Make array non-contiguous + arr = arr[::2, ::2] + assert not arr.flags.forc + + # Whether a copy is necessary depends on the order of arr: + if order2 == "C": + no_copy_necessary = arr.flags.c_contiguous + elif order2 == "F": + no_copy_necessary = arr.flags.f_contiguous + else: + # Keeporder and Anyorder are OK with non-contiguous output. + # This is not consistent with the `astype` behaviour which + # enforces contiguity for "A". It is probably historic from when + # "K" did not exist. + no_copy_necessary = True + + # Test it for both the array and a memoryview + for view in [arr, memoryview(arr)]: + for copy in self.true_vals: + res = np.array(view, copy=copy, order=order2) + assert res is not arr and res.flags.owndata + assert_array_equal(arr, res) + + if no_copy_necessary: + for copy in self.false_vals: + res = np.array(view, copy=copy, order=order2) + # res.base.obj refers to the memoryview + if not IS_PYPY: + assert res is arr or res.base.obj is arr + + res = np.array(view, copy=np._CopyMode.NEVER, + order=order2) + if not IS_PYPY: + assert res is arr or res.base.obj is arr + else: + for copy in self.false_vals: + res = np.array(arr, copy=copy, order=order2) + assert_array_equal(arr, res) + assert_raises(ValueError, np.array, + view, copy=np._CopyMode.NEVER, + order=order2) + assert_raises(ValueError, np.array, + view, copy=None, + order=order2) + + def test_striding_not_ok(self): + arr = np.array([[1, 2, 4], [3, 4, 5]]) + assert_raises(ValueError, np.array, + arr.T, copy=np._CopyMode.NEVER, + order='C') + assert_raises(ValueError, np.array, + arr.T, copy=np._CopyMode.NEVER, + order='C', dtype=np.int64) + assert_raises(ValueError, np.array, + arr, copy=np._CopyMode.NEVER, + order='F') + assert_raises(ValueError, np.array, + arr, copy=np._CopyMode.NEVER, + order='F', dtype=np.int64) + + +class TestArrayAttributeDeletion: def test_multiarray_writable_attributes_deletion(self): # ticket #2046, should not seqfault, raise AttributeError @@ -6695,12 +8058,11 @@ def test_multiarray_flags_not_writable_attribute_deletion(self): assert_raises(AttributeError, delattr, a, s) -def test_array_interface(): - # Test scalar coercion within the array interface - class Foo(object): +class TestArrayInterface(): + class Foo: def __init__(self, value): self.value = value - self.iface = {'typestr': '=f8'} + self.iface = {'typestr': 'f8'} def __float__(self): return float(self.value) @@ -6709,23 +8071,40 @@ def __float__(self): def __array_interface__(self): return self.iface + f = Foo(0.5) - assert_equal(np.array(f), 0.5) - assert_equal(np.array([f]), [0.5]) - assert_equal(np.array([f, f]), [0.5, 0.5]) - assert_equal(np.array(f).dtype, np.dtype('=f8')) - # Test various shape definitions - f.iface['shape'] = () - assert_equal(np.array(f), 0.5) - f.iface['shape'] = None - assert_raises(TypeError, np.array, f) - f.iface['shape'] = (1, 1) - assert_equal(np.array(f), [[0.5]]) - f.iface['shape'] = (2,) - assert_raises(ValueError, np.array, f) - - # test scalar with no shape - class ArrayLike(object): + + @pytest.mark.parametrize('val, iface, expected', [ + (f, {}, 0.5), + ([f], {}, [0.5]), + ([f, f], {}, [0.5, 0.5]), + (f, {'shape': ()}, 0.5), + (f, {'shape': None}, TypeError), + (f, {'shape': (1, 1)}, [[0.5]]), + (f, {'shape': (2,)}, ValueError), + (f, {'strides': ()}, 0.5), + (f, {'strides': (2,)}, ValueError), + (f, {'strides': 16}, TypeError), + ]) + def test_scalar_interface(self, val, iface, expected): + # Test scalar coercion within the array interface + self.f.iface = {'typestr': 'f8'} + self.f.iface.update(iface) + if HAS_REFCOUNT: + pre_cnt = sys.getrefcount(np.dtype('f8')) + if isinstance(expected, type): + assert_raises(expected, np.array, val) + else: + result = np.array(val) + assert_equal(np.array(val), expected) + assert result.dtype == 'f8' + del result + if HAS_REFCOUNT: + post_cnt = sys.getrefcount(np.dtype('f8')) + assert_equal(pre_cnt, post_cnt) + +def test_interface_no_shape(): + class ArrayLike: array = np.array(1) __array_interface__ = array.__array_interface__ assert_equal(np.array(ArrayLike()), 1) @@ -6747,7 +8126,7 @@ def test_array_interface_empty_shape(): interface1 = dict(arr.__array_interface__) interface1['shape'] = () - class DummyArray1(object): + class DummyArray1: __array_interface__ = interface1 # NOTE: Because Py2 str/Py3 bytes supports the buffer interface, setting @@ -6757,7 +8136,7 @@ class DummyArray1(object): interface2 = dict(interface1) interface2['data'] = arr[0].tobytes() - class DummyArray2(object): + class DummyArray2: __array_interface__ = interface2 arr1 = np.asarray(DummyArray1()) @@ -6766,6 +8145,31 @@ class DummyArray2(object): assert_equal(arr1, arr2) assert_equal(arr1, arr3) +def test_array_interface_offset(): + arr = np.array([1, 2, 3], dtype='int32') + interface = dict(arr.__array_interface__) + interface['data'] = memoryview(arr) + interface['shape'] = (2,) + interface['offset'] = 4 + + + class DummyArray: + __array_interface__ = interface + + arr1 = np.asarray(DummyArray()) + assert_equal(arr1, arr[1:]) + +def test_array_interface_unicode_typestr(): + arr = np.array([1, 2, 3], dtype='int32') + interface = dict(arr.__array_interface__) + interface['typestr'] = '\N{check mark}' + + class DummyArray: + __array_interface__ = interface + + # should not be UnicodeEncodeError + with pytest.raises(TypeError): + np.asarray(DummyArray()) def test_flat_element_deletion(): it = np.ones(3).flat @@ -6783,7 +8187,7 @@ def test_scalar_element_deletion(): assert_raises(ValueError, a[0].__delitem__, 'x') -class TestMemEventHook(object): +class TestMemEventHook: def test_mem_seteventhook(self): # The actual tests are within the C code in # multiarray/_multiarray_tests.c.src @@ -6792,10 +8196,10 @@ def test_mem_seteventhook(self): # needs to be larger then limit of small memory cacher in ctors.c a = np.zeros(1000) del a - gc.collect() + break_cycles() _multiarray_tests.test_pydatamem_seteventhook_end() -class TestMapIter(object): +class TestMapIter: def test_mapiter(self): # The actual tests are within the C code in # multiarray/_multiarray_tests.c.src @@ -6817,7 +8221,7 @@ def test_mapiter(self): assert_equal(b, [100.1, 51., 6., 3., 4., 5.]) -class TestAsCArray(object): +class TestAsCArray: def test_1darray(self): array = np.arange(24, dtype=np.double) from_c = _multiarray_tests.test_as_c_array(array, 3) @@ -6834,7 +8238,7 @@ def test_3darray(self): assert_equal(array[1, 2, 3], from_c) -class TestConversion(object): +class TestConversion: def test_array_scalar_relational_operation(self): # All integer for dt1 in np.typecodes['AllInteger']: @@ -6882,13 +8286,14 @@ def test_to_bool_scalar(self): assert_equal(bool(np.array([[42]])), True) assert_raises(ValueError, bool, np.array([1, 2])) - class NotConvertible(object): + class NotConvertible: def __bool__(self): raise NotImplementedError - __nonzero__ = __bool__ # python 2 assert_raises(NotImplementedError, bool, np.array(NotConvertible())) assert_raises(NotImplementedError, bool, np.array([NotConvertible()])) + if IS_PYSTON: + pytest.skip("Pyston disables recursion checking") self_containing = np.array([None]) self_containing[0] = self_containing @@ -6897,13 +8302,14 @@ def __bool__(self): except NameError: Error = RuntimeError # python < 3.5 assert_raises(Error, bool, self_containing) # previously stack overflow + self_containing[0] = None # resolve circular reference def test_to_int_scalar(self): # gh-9972 means that these aren't always the same int_funcs = (int, lambda x: x.__int__()) for int_func in int_funcs: + assert_equal(int_func(np.array(0)), 0) assert_equal(int_func(np.array([1])), 1) - assert_equal(int_func(np.array([0])), 0) assert_equal(int_func(np.array([[42]])), 42) assert_raises(TypeError, int_func, np.array([1, 2])) @@ -6918,7 +8324,7 @@ def __trunc__(self): assert_equal(3, int_func(np.array(HasTrunc()))) assert_equal(3, int_func(np.array([HasTrunc()]))) - class NotConvertible(object): + class NotConvertible: def __int__(self): raise NotImplementedError assert_raises(NotImplementedError, @@ -6927,7 +8333,7 @@ def __int__(self): int_func, np.array([NotConvertible()])) -class TestWhere(object): +class TestWhere: def test_basic(self): dts = [bool, np.int16, np.int32, np.int64, np.double, np.complex128, np.longdouble, np.clongdouble] @@ -7094,7 +8500,7 @@ def test_largedim(self): if not IS_PYPY: # sys.getsizeof() is not valid on PyPy - class TestSizeOf(object): + class TestSizeOf: def test_empty_array(self): x = np.array([]) @@ -7127,6 +8533,7 @@ def test_reshape(self): d = np.ones(100) assert_(sys.getsizeof(d) < sys.getsizeof(d.reshape(100, 1, 1).copy())) + @_no_tracing def test_resize(self): d = np.ones(100) old = sys.getsizeof(d) @@ -7140,7 +8547,7 @@ def test_error(self): assert_raises(TypeError, d.__sizeof__, "a") -class TestHashing(object): +class TestHashing: def test_arrays_not_hashable(self): x = np.ones(3) @@ -7148,10 +8555,10 @@ def test_arrays_not_hashable(self): def test_collections_hashable(self): x = np.array([]) - assert_(not isinstance(x, collections_abc.Hashable)) + assert_(not isinstance(x, collections.abc.Hashable)) -class TestArrayPriority(object): +class TestArrayPriority: # This will go away when __array_priority__ is settled, meanwhile # it serves to check unintended changes. op = operator @@ -7161,11 +8568,6 @@ class TestArrayPriority(object): op.ge, op.lt, op.le, op.ne, op.eq ] - # See #7949. Don't use "/" operator With -3 switch, since python reports it - # as a DeprecationWarning - if sys.version_info[0] < 3 and not sys.py3kwarning: - binary_ops.append(op.div) - class Foo(np.ndarray): __array_priority__ = 100. @@ -7178,7 +8580,7 @@ class Bar(np.ndarray): def __new__(cls, *args, **kwargs): return np.array(*args, **kwargs).view(cls) - class Other(object): + class Other: __array_priority__ = 1000. def _all(self, other): @@ -7237,7 +8639,7 @@ def test_subclass_other(self): assert_(isinstance(f(b, a), self.Other), msg) -class TestBytestringArrayNonzero(object): +class TestBytestringArrayNonzero: def test_empty_bstring_array_is_falsey(self): assert_(not np.array([''], dtype=str)) @@ -7258,28 +8660,56 @@ def test_null_inside_bstring_array_is_truthy(self): assert_(a) -class TestUnicodeArrayNonzero(object): +class TestUnicodeEncoding: + """ + Tests for encoding related bugs, such as UCS2 vs UCS4, round-tripping + issues, etc + """ + def test_round_trip(self): + """ Tests that GETITEM, SETITEM, and PyArray_Scalar roundtrip """ + # gh-15363 + arr = np.zeros(shape=(), dtype="U1") + for i in range(1, sys.maxunicode + 1): + expected = chr(i) + arr[()] = expected + assert arr[()] == expected + assert arr.item() == expected + + def test_assign_scalar(self): + # gh-3258 + l = np.array(['aa', 'bb']) + l[:] = np.unicode_('cc') + assert_equal(l, ['cc', 'cc']) + + def test_fill_scalar(self): + # gh-7227 + l = np.array(['aa', 'bb']) + l.fill(np.unicode_('cc')) + assert_equal(l, ['cc', 'cc']) + + +class TestUnicodeArrayNonzero: def test_empty_ustring_array_is_falsey(self): - assert_(not np.array([''], dtype=np.unicode)) + assert_(not np.array([''], dtype=np.unicode_)) def test_whitespace_ustring_array_is_falsey(self): - a = np.array(['eggs'], dtype=np.unicode) + a = np.array(['eggs'], dtype=np.unicode_) a[0] = ' \0\0' assert_(not a) def test_all_null_ustring_array_is_falsey(self): - a = np.array(['eggs'], dtype=np.unicode) + a = np.array(['eggs'], dtype=np.unicode_) a[0] = '\0\0\0\0' assert_(not a) def test_null_inside_ustring_array_is_truthy(self): - a = np.array(['eggs'], dtype=np.unicode) + a = np.array(['eggs'], dtype=np.unicode_) a[0] = ' \0 \0' assert_(a) -class TestFormat(object): +class TestFormat: def test_0d(self): a = np.array(np.pi) @@ -7293,17 +8723,11 @@ def test_1d_no_format(self): def test_1d_format(self): # until gh-5543, ensure that the behaviour matches what it used to be a = np.array([np.pi]) - if sys.version_info[:2] >= (3, 4): - assert_raises(TypeError, '{:30}'.format, a) - else: - with suppress_warnings() as sup: - sup.filter(PendingDeprecationWarning) - res = '{:30}'.format(a) - dst = object.__format__(a, '30') - assert_equal(res, dst) + assert_raises(TypeError, '{:30}'.format, a) +from numpy.testing import IS_PYPY -class TestCTypes(object): +class TestCTypes: def test_ctypes_is_available(self): test_arr = np.array([[1, 2, 3], [4, 5, 6]]) @@ -7323,8 +8747,79 @@ def test_ctypes_is_not_available(self): finally: _internal.ctypes = ctypes + def _make_readonly(x): + x.flags.writeable = False + return x + + @pytest.mark.parametrize('arr', [ + np.array([1, 2, 3]), + np.array([['one', 'two'], ['three', 'four']]), + np.array((1, 2), dtype='i4,i4'), + np.zeros((2,), dtype= + np.dtype(dict( + formats=['<i4', '<i4'], + names=['a', 'b'], + offsets=[0, 2], + itemsize=6 + )) + ), + np.array([None], dtype=object), + np.array([]), + np.empty((0, 0)), + _make_readonly(np.array([1, 2, 3])), + ], ids=[ + '1d', + '2d', + 'structured', + 'overlapping', + 'object', + 'empty', + 'empty-2d', + 'readonly' + ]) + def test_ctypes_data_as_holds_reference(self, arr): + # gh-9647 + # create a copy to ensure that pytest does not mess with the refcounts + arr = arr.copy() + + arr_ref = weakref.ref(arr) + + ctypes_ptr = arr.ctypes.data_as(ctypes.c_void_p) + + # `ctypes_ptr` should hold onto `arr` + del arr + break_cycles() + assert_(arr_ref() is not None, "ctypes pointer did not hold onto a reference") + + # but when the `ctypes_ptr` object dies, so should `arr` + del ctypes_ptr + if IS_PYPY: + # Pypy does not recycle arr objects immediately. Trigger gc to + # release arr. Cpython uses refcounts. An explicit call to gc + # should not be needed here. + break_cycles() + assert_(arr_ref() is None, "unknowable whether ctypes pointer holds a reference") + + def test_ctypes_as_parameter_holds_reference(self): + arr = np.array([None]).copy() + + arr_ref = weakref.ref(arr) + + ctypes_ptr = arr.ctypes._as_parameter_ + + # `ctypes_ptr` should hold onto `arr` + del arr + break_cycles() + assert_(arr_ref() is not None, "ctypes pointer did not hold onto a reference") + + # but when the `ctypes_ptr` object dies, so should `arr` + del ctypes_ptr + if IS_PYPY: + break_cycles() + assert_(arr_ref() is None, "unknowable whether ctypes pointer holds a reference") -class TestWritebackIfCopy(object): + +class TestWritebackIfCopy: # all these tests use the WRITEBACKIFCOPY mechanism def test_argmax_with_out(self): mat = np.eye(5) @@ -7338,17 +8833,13 @@ def test_argmin_with_out(self): res = np.argmin(mat, 0, out=out) assert_equal(res, range(5)) - def test_clip_with_out(self): - mat = np.eye(5) - out = np.eye(5, dtype='i2') - res = np.clip(mat, a_min=-10, a_max=0, out=out) - assert_equal(np.sum(out), 0) - def test_insert_noncontiguous(self): a = np.arange(6).reshape(2,3).T # force non-c-contiguous # uses arr_insert np.place(a, a>2, [44, 55]) assert_equal(a, np.array([[0, 44], [1, 55], [2, 44]])) + # hit one of the failing paths + assert_raises(ValueError, np.place, a, a>20, []) def test_put_noncontiguous(self): a = np.arange(6).reshape(2,3).T # force non-c-contiguous @@ -7407,6 +8898,8 @@ def test_view_assign(self): arr_wb[...] = 100 assert_equal(arr, -100) + @pytest.mark.leaks_references( + reason="increments self in dealloc; ignore since deprecated path.") def test_dealloc_warning(self): with suppress_warnings() as sup: sup.record(RuntimeWarning) @@ -7439,7 +8932,7 @@ def test_view_discard_refcount(self): assert_equal(arr, orig) -class TestArange(object): +class TestArange: def test_infinite(self): assert_raises_regex( ValueError, "size exceeded", @@ -7460,8 +8953,24 @@ def test_zero_step(self): assert_raises(ZeroDivisionError, np.arange, 0, 0, 0) assert_raises(ZeroDivisionError, np.arange, 0.0, 0.0, 0.0) + def test_require_range(self): + assert_raises(TypeError, np.arange) + assert_raises(TypeError, np.arange, step=3) + assert_raises(TypeError, np.arange, dtype='int64') + assert_raises(TypeError, np.arange, start=4) + + def test_start_stop_kwarg(self): + keyword_stop = np.arange(stop=3) + keyword_zerotostop = np.arange(start=0, stop=3) + keyword_start_stop = np.arange(start=3, stop=9) -class TestArrayFinalize(object): + assert len(keyword_stop) == 3 + assert len(keyword_zerotostop) == 3 + assert len(keyword_start_stop) == 6 + assert_array_equal(keyword_stop, keyword_zerotostop) + + +class TestArrayFinalize: """ Tests __array_finalize__ """ def test_receives_base(self): @@ -7473,6 +8982,15 @@ def __array_finalize__(self, obj): a = np.array(1).view(SavesBase) assert_(a.saved_base is a.base) + def test_bad_finalize(self): + class BadAttributeArray(np.ndarray): + @property + def __array_finalize__(self): + raise RuntimeError("boohoo!") + + with pytest.raises(RuntimeError, match="boohoo!"): + np.arange(10).view(BadAttributeArray) + def test_lifetime_on_error(self): # gh-11237 class RaisesInFinalize(np.ndarray): @@ -7481,7 +8999,7 @@ def __array_finalize__(self, obj): raise Exception(self) # a plain object can't be weakref'd - class Dummy(object): pass + class Dummy: pass # get a weak reference to an object within an array obj_arr = np.array(Dummy()) @@ -7490,24 +9008,21 @@ class Dummy(object): pass # get an array that crashed in __array_finalize__ with assert_raises(Exception) as e: obj_arr.view(RaisesInFinalize) - if sys.version_info.major == 2: - # prevent an extra reference being kept - sys.exc_clear() obj_subarray = e.exception.args[0] del e assert_(isinstance(obj_subarray, RaisesInFinalize)) # reference should still be held by obj_arr - gc.collect() + break_cycles() assert_(obj_ref() is not None, "object should not already be dead") del obj_arr - gc.collect() + break_cycles() assert_(obj_ref() is not None, "obj_arr should not hold the last reference") del obj_subarray - gc.collect() + break_cycles() assert_(obj_ref() is None, "no references should remain") @@ -7521,7 +9036,7 @@ def test_equal_override(): # gh-9153: ndarray.__eq__ uses special logic for structured arrays, which # did not respect overrides with __array_priority__ or __array_ufunc__. # The PR fixed this for __array_priority__ and __array_ufunc__ = None. - class MyAlwaysEqual(object): + class MyAlwaysEqual: def __eq__(self, other): return "eq" @@ -7543,23 +9058,22 @@ class MyAlwaysEqualNew(MyAlwaysEqual): assert_equal(array != my_always_equal, 'ne') -def test_npymath_complex(): +@pytest.mark.parametrize( + ["fun", "npfun"], + [ + (_multiarray_tests.npy_cabs, np.absolute), + (_multiarray_tests.npy_carg, np.angle) + ] +) +@pytest.mark.parametrize("x", [1, np.inf, -np.inf, np.nan]) +@pytest.mark.parametrize("y", [1, np.inf, -np.inf, np.nan]) +@pytest.mark.parametrize("test_dtype", np.complexfloating.__subclasses__()) +def test_npymath_complex(fun, npfun, x, y, test_dtype): # Smoketest npymath functions - from numpy.core._multiarray_tests import ( - npy_cabs, npy_carg) - - funcs = {npy_cabs: np.absolute, - npy_carg: np.angle} - vals = (1, np.inf, -np.inf, np.nan) - types = (np.complex64, np.complex128, np.clongdouble) - - for fun, npfun in funcs.items(): - for x, y in itertools.product(vals, vals): - for t in types: - z = t(complex(x, y)) - got = fun(z) - expected = npfun(z) - assert_allclose(got, expected) + z = test_dtype(complex(x, y)) + got = fun(z) + expected = npfun(z) + assert_allclose(got, expected) def test_npymath_real(): @@ -7582,3 +9096,126 @@ def test_npymath_real(): got = fun(z) expected = npfun(z) assert_allclose(got, expected) + +def test_uintalignment_and_alignment(): + # alignment code needs to satisfy these requirements: + # 1. numpy structs match C struct layout + # 2. ufuncs/casting is safe wrt to aligned access + # 3. copy code is safe wrt to "uint alidned" access + # + # Complex types are the main problem, whose alignment may not be the same + # as their "uint alignment". + # + # This test might only fail on certain platforms, where uint64 alignment is + # not equal to complex64 alignment. The second 2 tests will only fail + # for DEBUG=1. + + d1 = np.dtype('u1,c8', align=True) + d2 = np.dtype('u4,c8', align=True) + d3 = np.dtype({'names': ['a', 'b'], 'formats': ['u1', d1]}, align=True) + + assert_equal(np.zeros(1, dtype=d1)['f1'].flags['ALIGNED'], True) + assert_equal(np.zeros(1, dtype=d2)['f1'].flags['ALIGNED'], True) + assert_equal(np.zeros(1, dtype='u1,c8')['f1'].flags['ALIGNED'], False) + + # check that C struct matches numpy struct size + s = _multiarray_tests.get_struct_alignments() + for d, (alignment, size) in zip([d1,d2,d3], s): + assert_equal(d.alignment, alignment) + assert_equal(d.itemsize, size) + + # check that ufuncs don't complain in debug mode + # (this is probably OK if the aligned flag is true above) + src = np.zeros((2,2), dtype=d1)['f1'] # 4-byte aligned, often + np.exp(src) # assert fails? + + # check that copy code doesn't complain in debug mode + dst = np.zeros((2,2), dtype='c8') + dst[:,1] = src[:,1] # assert in lowlevel_strided_loops fails? + +class TestAlignment: + # adapted from scipy._lib.tests.test__util.test__aligned_zeros + # Checks that unusual memory alignments don't trip up numpy. + # In particular, check RELAXED_STRIDES don't trip alignment assertions in + # NDEBUG mode for size-0 arrays (gh-12503) + + def check(self, shape, dtype, order, align): + err_msg = repr((shape, dtype, order, align)) + x = _aligned_zeros(shape, dtype, order, align=align) + if align is None: + align = np.dtype(dtype).alignment + assert_equal(x.__array_interface__['data'][0] % align, 0) + if hasattr(shape, '__len__'): + assert_equal(x.shape, shape, err_msg) + else: + assert_equal(x.shape, (shape,), err_msg) + assert_equal(x.dtype, dtype) + if order == "C": + assert_(x.flags.c_contiguous, err_msg) + elif order == "F": + if x.size > 0: + assert_(x.flags.f_contiguous, err_msg) + elif order is None: + assert_(x.flags.c_contiguous, err_msg) + else: + raise ValueError() + + def test_various_alignments(self): + for align in [1, 2, 3, 4, 8, 12, 16, 32, 64, None]: + for n in [0, 1, 3, 11]: + for order in ["C", "F", None]: + for dtype in list(np.typecodes["All"]) + ['i4,i4,i4']: + if dtype == 'O': + # object dtype can't be misaligned + continue + for shape in [n, (1, 2, 3, n)]: + self.check(shape, np.dtype(dtype), order, align) + + def test_strided_loop_alignments(self): + # particularly test that complex64 and float128 use right alignment + # code-paths, since these are particularly problematic. It is useful to + # turn on USE_DEBUG for this test, so lowlevel-loop asserts are run. + for align in [1, 2, 4, 8, 12, 16, None]: + xf64 = _aligned_zeros(3, np.float64) + + xc64 = _aligned_zeros(3, np.complex64, align=align) + xf128 = _aligned_zeros(3, np.longdouble, align=align) + + # test casting, both to and from misaligned + with suppress_warnings() as sup: + sup.filter(np.ComplexWarning, "Casting complex values") + xc64.astype('f8') + xf64.astype(np.complex64) + test = xc64 + xf64 + + xf128.astype('f8') + xf64.astype(np.longdouble) + test = xf128 + xf64 + + test = xf128 + xc64 + + # test copy, both to and from misaligned + # contig copy + xf64[:] = xf64.copy() + xc64[:] = xc64.copy() + xf128[:] = xf128.copy() + # strided copy + xf64[::2] = xf64[::2].copy() + xc64[::2] = xc64[::2].copy() + xf128[::2] = xf128[::2].copy() + +def test_getfield(): + a = np.arange(32, dtype='uint16') + if sys.byteorder == 'little': + i = 0 + j = 1 + else: + i = 1 + j = 0 + b = a.getfield('int8', i) + assert_equal(b, a) + b = a.getfield('int8', j) + assert_equal(b, 0) + pytest.raises(ValueError, a.getfield, 'uint8', -1) + pytest.raises(ValueError, a.getfield, 'uint8', 16) + pytest.raises(ValueError, a.getfield, 'uint64', 0) diff --git a/numpy/core/tests/test_nditer.py b/numpy/core/tests/test_nditer.py index a0096efdbc4b..ed775cac628d 100644 --- a/numpy/core/tests/test_nditer.py +++ b/numpy/core/tests/test_nditer.py @@ -1,15 +1,15 @@ -from __future__ import division, absolute_import, print_function - import sys -import warnings import pytest +import textwrap +import subprocess + import numpy as np import numpy.core._multiarray_tests as _multiarray_tests from numpy import array, arange, nditer, all from numpy.testing import ( - assert_, assert_equal, assert_array_equal, assert_raises, assert_warns, - HAS_REFCOUNT, suppress_warnings + assert_, assert_equal, assert_array_equal, assert_raises, + HAS_REFCOUNT, suppress_warnings, break_cycles ) @@ -185,6 +185,29 @@ def test_iter_c_or_f_order(): assert_equal([x for x in i], aview.swapaxes(0, 1).ravel(order='A')) +def test_nditer_multi_index_set(): + # Test the multi_index set + a = np.arange(6).reshape(2, 3) + it = np.nditer(a, flags=['multi_index']) + + # Removes the iteration on two first elements of a[0] + it.multi_index = (0, 2,) + + assert_equal([i for i in it], [2, 3, 4, 5]) + +@pytest.mark.skipif(not HAS_REFCOUNT, reason="Python lacks refcounts") +def test_nditer_multi_index_set_refcount(): + # Test if the reference count on index variable is decreased + + index = 0 + i = np.nditer(np.array([111, 222, 333, 444]), flags=['multi_index']) + + start_count = sys.getrefcount(index) + i.multi_index = (index,) + end_count = sys.getrefcount(index) + + assert_equal(start_count, end_count) + def test_iter_best_order_multi_index_1d(): # The multi-indices should be correct with any reordering @@ -1365,6 +1388,75 @@ def test_iter_copy(): j = i.copy() assert_equal([x[()] for x in j], a.ravel(order='F')) + +@pytest.mark.parametrize("dtype", np.typecodes["All"]) +@pytest.mark.parametrize("loop_dtype", np.typecodes["All"]) +@pytest.mark.filterwarnings("ignore::numpy.ComplexWarning") +def test_iter_copy_casts(dtype, loop_dtype): + # Ensure the dtype is never flexible: + if loop_dtype.lower() == "m": + loop_dtype = loop_dtype + "[ms]" + elif np.dtype(loop_dtype).itemsize == 0: + loop_dtype = loop_dtype + "50" + + # Make things a bit more interesting by requiring a byte-swap as well: + arr = np.ones(1000, dtype=np.dtype(dtype).newbyteorder()) + try: + expected = arr.astype(loop_dtype) + except Exception: + # Some casts are not possible, do not worry about them + return + + it = np.nditer((arr,), ["buffered", "external_loop", "refs_ok"], + op_dtypes=[loop_dtype], casting="unsafe") + + if np.issubdtype(np.dtype(loop_dtype), np.number): + # Casting to strings may be strange, but for simple dtypes do not rely + # on the cast being correct: + assert_array_equal(expected, np.ones(1000, dtype=loop_dtype)) + + it_copy = it.copy() + res = next(it) + del it + res_copy = next(it_copy) + del it_copy + + assert_array_equal(res, expected) + assert_array_equal(res_copy, expected) + + +def test_iter_copy_casts_structured(): + # Test a complicated structured dtype for casting, as it requires + # both multiple steps and a more complex casting setup. + # Includes a structured -> unstructured (any to object), and many other + # casts, which cause this to require all steps in the casting machinery + # one level down as well as the iterator copy (which uses NpyAuxData clone) + in_dtype = np.dtype([("a", np.dtype("i,")), + ("b", np.dtype(">i,<i,>d,S17,>d,(3)f,O,i1"))]) + out_dtype = np.dtype([("a", np.dtype("O")), + ("b", np.dtype(">i,>i,S17,>d,>U3,(3)d,i1,O"))]) + arr = np.ones(1000, dtype=in_dtype) + + it = np.nditer((arr,), ["buffered", "external_loop", "refs_ok"], + op_dtypes=[out_dtype], casting="unsafe") + it_copy = it.copy() + + res1 = next(it) + del it + res2 = next(it_copy) + del it_copy + + expected = arr["a"].astype(out_dtype["a"]) + assert_array_equal(res1["a"], expected) + assert_array_equal(res2["a"], expected) + + for field in in_dtype["b"].names: + # Note that the .base avoids the subarray field + expected = arr["b"][field].astype(out_dtype["b"][field].base) + assert_array_equal(res1["b"][field], expected) + assert_array_equal(res2["b"][field], expected) + + def test_iter_allocate_output_simple(): # Check that the iterator will properly allocate outputs @@ -1523,6 +1615,13 @@ def test_iter_allocate_output_errors(): [['readonly'], ['writeonly', 'allocate']], op_dtypes=[None, np.dtype('f4')], op_axes=[None, [0, 2, 1, 0]]) + # Not all axes may be specified if a reduction. If there is a hole + # in op_axes, this is an error. + a = arange(24, dtype='i4').reshape(2, 3, 4) + assert_raises(ValueError, nditer, [a, None], ["reduce_ok"], + [['readonly'], ['readwrite', 'allocate']], + op_dtypes=[None, np.dtype('f4')], + op_axes=[None, [0, np.newaxis, 2]]) def test_iter_remove_axis(): a = arange(24).reshape(2, 3, 4) @@ -1865,7 +1964,7 @@ def test_iter_buffered_cast_structured_type(): # make sure multi-field struct type -> simple doesn't work sdt = [('a', 'f4'), ('b', 'i8'), ('d', 'O')] a = np.array([(5.5, 7, 'test'), (8, 10, 11)], dtype=sdt) - assert_raises(ValueError, lambda: ( + assert_raises(TypeError, lambda: ( nditer(a, ['buffered', 'refs_ok'], ['readonly'], casting='unsafe', op_dtypes='i4'))) @@ -1882,16 +1981,58 @@ def test_iter_buffered_cast_structured_type(): [np.array((1, 2, 3), dtype=sdt2), np.array((4, 5, 6), dtype=sdt2)]) + +def test_iter_buffered_cast_structured_type_failure_with_cleanup(): # make sure struct type -> struct type with different # number of fields fails sdt1 = [('a', 'f4'), ('b', 'i8'), ('d', 'O')] sdt2 = [('b', 'O'), ('a', 'f8')] a = np.array([(1, 2, 3), (4, 5, 6)], dtype=sdt1) - assert_raises(ValueError, lambda : ( - nditer(a, ['buffered', 'refs_ok'], ['readwrite'], - casting='unsafe', - op_dtypes=sdt2))) + for intent in ["readwrite", "readonly", "writeonly"]: + # If the following assert fails, the place where the error is raised + # within nditer may change. That is fine, but it may make sense for + # a new (hard to design) test to replace it. The `simple_arr` is + # designed to require a multi-step cast (due to having fields). + assert np.can_cast(a.dtype, sdt2, casting="unsafe") + simple_arr = np.array([1, 2], dtype="i,i") # requires clean up + with pytest.raises(ValueError): + nditer((simple_arr, a), ['buffered', 'refs_ok'], [intent, intent], + casting='unsafe', op_dtypes=["f,f", sdt2]) + + +def test_buffered_cast_error_paths(): + with pytest.raises(ValueError): + # The input is cast into an `S3` buffer + np.nditer((np.array("a", dtype="S1"),), op_dtypes=["i"], + casting="unsafe", flags=["buffered"]) + + # The `M8[ns]` is cast into the `S3` output + it = np.nditer((np.array(1, dtype="i"),), op_dtypes=["S1"], + op_flags=["writeonly"], casting="unsafe", flags=["buffered"]) + with pytest.raises(ValueError): + with it: + buf = next(it) + buf[...] = "a" # cannot be converted to int. + +@pytest.mark.skipif(not HAS_REFCOUNT, reason="PyPy seems to not hit this.") +def test_buffered_cast_error_paths_unraisable(): + # The following gives an unraisable error. Pytest sometimes captures that + # (depending python and/or pytest version). So with Python>=3.8 this can + # probably be cleaned out in the future to check for + # pytest.PytestUnraisableExceptionWarning: + code = textwrap.dedent(""" + import numpy as np + + it = np.nditer((np.array(1, dtype="i"),), op_dtypes=["S1"], + op_flags=["writeonly"], casting="unsafe", flags=["buffered"]) + buf = next(it) + buf[...] = "a" + del buf, it # Flushing only happens during deallocate right now. + """) + res = subprocess.check_output([sys.executable, "-c", code], + stderr=subprocess.STDOUT, text=True) + assert "ValueError" in res def test_iter_buffered_cast_subarray(): @@ -2105,7 +2246,7 @@ def test_iter_buffering_string(): assert_equal(i[0], b'abc') assert_equal(i[0].dtype, np.dtype('S6')) - a = np.array(['abc', 'a', 'abcd'], dtype=np.unicode) + a = np.array(['abc', 'a', 'abcd'], dtype=np.unicode_) assert_equal(a.dtype, np.dtype('U4')) assert_raises(TypeError, nditer, a, ['buffered'], ['readonly'], op_dtypes='U2') @@ -2189,28 +2330,22 @@ def test_iter_no_broadcast(): [['readonly'], ['readonly'], ['readonly', 'no_broadcast']]) -class TestIterNested(object): +class TestIterNested: def test_basic(self): # Test nested iteration basic usage a = arange(12).reshape(2, 3, 2) i, j = np.nested_iters(a, [[0], [1, 2]]) - vals = [] - for x in i: - vals.append([y for y in j]) + vals = [list(j) for _ in i] assert_equal(vals, [[0, 1, 2, 3, 4, 5], [6, 7, 8, 9, 10, 11]]) i, j = np.nested_iters(a, [[0, 1], [2]]) - vals = [] - for x in i: - vals.append([y for y in j]) + vals = [list(j) for _ in i] assert_equal(vals, [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9], [10, 11]]) i, j = np.nested_iters(a, [[0, 2], [1]]) - vals = [] - for x in i: - vals.append([y for y in j]) + vals = [list(j) for _ in i] assert_equal(vals, [[0, 2, 4], [1, 3, 5], [6, 8, 10], [7, 9, 11]]) def test_reorder(self): @@ -2219,40 +2354,28 @@ def test_reorder(self): # In 'K' order (default), it gets reordered i, j = np.nested_iters(a, [[0], [2, 1]]) - vals = [] - for x in i: - vals.append([y for y in j]) + vals = [list(j) for _ in i] assert_equal(vals, [[0, 1, 2, 3, 4, 5], [6, 7, 8, 9, 10, 11]]) i, j = np.nested_iters(a, [[1, 0], [2]]) - vals = [] - for x in i: - vals.append([y for y in j]) + vals = [list(j) for _ in i] assert_equal(vals, [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9], [10, 11]]) i, j = np.nested_iters(a, [[2, 0], [1]]) - vals = [] - for x in i: - vals.append([y for y in j]) + vals = [list(j) for _ in i] assert_equal(vals, [[0, 2, 4], [1, 3, 5], [6, 8, 10], [7, 9, 11]]) # In 'C' order, it doesn't i, j = np.nested_iters(a, [[0], [2, 1]], order='C') - vals = [] - for x in i: - vals.append([y for y in j]) + vals = [list(j) for _ in i] assert_equal(vals, [[0, 2, 4, 1, 3, 5], [6, 8, 10, 7, 9, 11]]) i, j = np.nested_iters(a, [[1, 0], [2]], order='C') - vals = [] - for x in i: - vals.append([y for y in j]) + vals = [list(j) for _ in i] assert_equal(vals, [[0, 1], [6, 7], [2, 3], [8, 9], [4, 5], [10, 11]]) i, j = np.nested_iters(a, [[2, 0], [1]], order='C') - vals = [] - for x in i: - vals.append([y for y in j]) + vals = [list(j) for _ in i] assert_equal(vals, [[0, 2, 4], [6, 8, 10], [1, 3, 5], [7, 9, 11]]) def test_flip_axes(self): @@ -2261,40 +2384,28 @@ def test_flip_axes(self): # In 'K' order (default), the axes all get flipped i, j = np.nested_iters(a, [[0], [1, 2]]) - vals = [] - for x in i: - vals.append([y for y in j]) + vals = [list(j) for _ in i] assert_equal(vals, [[0, 1, 2, 3, 4, 5], [6, 7, 8, 9, 10, 11]]) i, j = np.nested_iters(a, [[0, 1], [2]]) - vals = [] - for x in i: - vals.append([y for y in j]) + vals = [list(j) for _ in i] assert_equal(vals, [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9], [10, 11]]) i, j = np.nested_iters(a, [[0, 2], [1]]) - vals = [] - for x in i: - vals.append([y for y in j]) + vals = [list(j) for _ in i] assert_equal(vals, [[0, 2, 4], [1, 3, 5], [6, 8, 10], [7, 9, 11]]) # In 'C' order, flipping axes is disabled i, j = np.nested_iters(a, [[0], [1, 2]], order='C') - vals = [] - for x in i: - vals.append([y for y in j]) + vals = [list(j) for _ in i] assert_equal(vals, [[11, 10, 9, 8, 7, 6], [5, 4, 3, 2, 1, 0]]) i, j = np.nested_iters(a, [[0, 1], [2]], order='C') - vals = [] - for x in i: - vals.append([y for y in j]) + vals = [list(j) for _ in i] assert_equal(vals, [[11, 10], [9, 8], [7, 6], [5, 4], [3, 2], [1, 0]]) i, j = np.nested_iters(a, [[0, 2], [1]], order='C') - vals = [] - for x in i: - vals.append([y for y in j]) + vals = [list(j) for _ in i] assert_equal(vals, [[11, 9, 7], [10, 8, 6], [5, 3, 1], [4, 2, 0]]) def test_broadcast(self): @@ -2303,15 +2414,11 @@ def test_broadcast(self): b = arange(3).reshape(1, 3) i, j = np.nested_iters([a, b], [[0], [1]]) - vals = [] - for x in i: - vals.append([y for y in j]) + vals = [list(j) for _ in i] assert_equal(vals, [[[0, 0], [0, 1], [0, 2]], [[1, 0], [1, 1], [1, 2]]]) i, j = np.nested_iters([a, b], [[1], [0]]) - vals = [] - for x in i: - vals.append([y for y in j]) + vals = [list(j) for _ in i] assert_equal(vals, [[[0, 0], [1, 0]], [[0, 1], [1, 1]], [[0, 2], [1, 2]]]) def test_dtype_copy(self): @@ -2323,13 +2430,11 @@ def test_dtype_copy(self): op_flags=['readonly', 'copy'], op_dtypes='f8') assert_equal(j[0].dtype, np.dtype('f8')) - vals = [] - for x in i: - vals.append([y for y in j]) + vals = [list(j) for _ in i] assert_equal(vals, [[0, 1, 2], [3, 4, 5]]) vals = None - # writebackifcopy - using conext manager + # writebackifcopy - using context manager a = arange(6, dtype='f4').reshape(2, 3) i, j = np.nested_iters(a, [[0], [1]], op_flags=['readwrite', 'updateifcopy'], @@ -2358,7 +2463,6 @@ def test_dtype_copy(self): j.close() assert_equal(a, [[1, 2, 3], [4, 5, 6]]) - def test_dtype_buffered(self): # Test nested iteration with buffering to change dtype @@ -2377,15 +2481,11 @@ def test_dtype_buffered(self): def test_0d(self): a = np.arange(12).reshape(2, 3, 2) i, j = np.nested_iters(a, [[], [1, 0, 2]]) - vals = [] - for x in i: - vals.append([y for y in j]) + vals = [list(j) for _ in i] assert_equal(vals, [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]]) i, j = np.nested_iters(a, [[1, 0, 2], []]) - vals = [] - for x in i: - vals.append([y for y in j]) + vals = [list(j) for _ in i] assert_equal(vals, [[0], [1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11]]) i, j, k = np.nested_iters(a, [[2, 0], [], [1]]) @@ -2557,10 +2657,8 @@ def test_iter_buffering_reduction_reuse_reduce_loops(): op_flags=[['readonly'], ['readwrite']], buffersize=5) - bufsizes = [] with it: - for x, y in it: - bufsizes.append(x.shape[0]) + bufsizes = [x.shape[0] for x, y in it] assert_equal(bufsizes, [5, 2, 5, 2]) assert_equal(sum(bufsizes), a.size) @@ -2630,9 +2728,30 @@ def test_iter_writemasked_badinput(): op_dtypes=['f4', None], casting='same_kind') -def test_iter_writemasked(): - a = np.zeros((3,), dtype='f8') - msk = np.array([True, True, False]) +def _is_buffered(iterator): + try: + iterator.itviews + except ValueError: + return True + return False + +@pytest.mark.parametrize("a", + [np.zeros((3,), dtype='f8'), + np.zeros((9876, 3*5), dtype='f8')[::2, :], + np.zeros((4, 312, 124, 3), dtype='f8')[::2, :, ::2, :], + # Also test with the last dimension strided (so it does not fit if + # there is repeated access) + np.zeros((9,), dtype='f8')[::3], + np.zeros((9876, 3*10), dtype='f8')[::2, ::5], + np.zeros((4, 312, 124, 3), dtype='f8')[::2, :, ::2, ::-1]]) +def test_iter_writemasked(a): + # Note, the slicing above is to ensure that nditer cannot combine multiple + # axes into one. The repetition is just to make things a bit more + # interesting. + shape = a.shape + reps = shape[-1] // 3 + msk = np.empty(shape, dtype=bool) + msk[...] = [True, True, False] * reps # When buffering is unused, 'writemasked' effectively does nothing. # It's up to the user of the iterator to obey the requested semantics. @@ -2643,18 +2762,31 @@ def test_iter_writemasked(): for x, m in it: x[...] = 1 # Because we violated the semantics, all the values became 1 - assert_equal(a, [1, 1, 1]) + assert_equal(a, np.broadcast_to([1, 1, 1] * reps, shape)) # Even if buffering is enabled, we still may be accessing the array # directly. it = np.nditer([a, msk], ['buffered'], [['readwrite', 'writemasked'], ['readonly', 'arraymask']]) + # @seberg: I honestly don't currently understand why a "buffered" iterator + # would end up not using a buffer for the small array here at least when + # "writemasked" is used, that seems confusing... Check by testing for + # actual memory overlap! + is_buffered = True with it: for x, m in it: x[...] = 2.5 - # Because we violated the semantics, all the values became 2.5 - assert_equal(a, [2.5, 2.5, 2.5]) + if np.may_share_memory(x, a): + is_buffered = False + + if not is_buffered: + # Because we violated the semantics, all the values became 2.5 + assert_equal(a, np.broadcast_to([2.5, 2.5, 2.5] * reps, shape)) + else: + # For large sizes, the iterator may be buffered: + assert_equal(a, np.broadcast_to([2.5, 2.5, 1] * reps, shape)) + a[...] = 2.5 # If buffering will definitely happening, for instance because of # a cast, only the items selected by the mask will be copied back from @@ -2669,7 +2801,38 @@ def test_iter_writemasked(): x[...] = 3 # Even though we violated the semantics, only the selected values # were copied back - assert_equal(a, [3, 3, 2.5]) + assert_equal(a, np.broadcast_to([3, 3, 2.5] * reps, shape)) + +def test_iter_writemasked_decref(): + # force casting (to make it interesting) by using a structured dtype. + arr = np.arange(10000).astype(">i,O") + original = arr.copy() + mask = np.random.randint(0, 2, size=10000).astype(bool) + + it = np.nditer([arr, mask], ['buffered', "refs_ok"], + [['readwrite', 'writemasked'], + ['readonly', 'arraymask']], + op_dtypes=["<i,O", "?"]) + singleton = object() + if HAS_REFCOUNT: + count = sys.getrefcount(singleton) + for buf, mask_buf in it: + buf[...] = (3, singleton) + + del buf, mask_buf, it # delete everything to ensure correct cleanup + + if HAS_REFCOUNT: + # The buffer would have included additional items, they must be + # cleared correctly: + assert sys.getrefcount(singleton) - count == np.count_nonzero(mask) + + assert_array_equal(arr[~mask], original[~mask]) + assert (arr[mask] == np.array((3, singleton), arr.dtype)).all() + del arr + + if HAS_REFCOUNT: + assert sys.getrefcount(singleton) == count + def test_iter_non_writable_attribute_deletion(): it = np.nditer(np.ones(2)) @@ -2707,6 +2870,14 @@ def test_iter_allocated_array_dtypes(): b[1] = a + 1 assert_equal(it.operands[1], [[0, 2], [2, 4], [19, 21]]) + # Check the same (less sensitive) thing when `op_axes` with -1 is given. + it = np.nditer(([[1, 3, 20]], None), op_dtypes=[None, ('i4', (2,))], + flags=["reduce_ok"], op_axes=[None, (-1, 0)]) + for a, b in it: + b[0] = a - 1 + b[1] = a + 1 + assert_equal(it.operands[1], [[0, 2], [2, 4], [19, 21]]) + # Make sure this works for scalars too it = np.nditer((10, 2, None), op_dtypes=[None, None, ('i4', (2, 2))]) for a, b, c in it: @@ -2734,7 +2905,15 @@ def test_0d_iter(): i = nditer(np.arange(5), ['multi_index'], [['readonly']], op_axes=[()]) assert_equal(i.ndim, 0) assert_equal(len(i), 1) - # note that itershape=(), still behaves like None due to the conversions + + i = nditer(np.arange(5), ['multi_index'], [['readonly']], + op_axes=[()], itershape=()) + assert_equal(i.ndim, 0) + assert_equal(len(i), 1) + + # passing an itershape alone is not enough, the op_axes are also needed + with assert_raises(ValueError): + nditer(np.arange(5), ['multi_index'], [['readonly']], itershape=()) # Test a more complex buffered casting case (same as another test above) sdt = [('a', 'f4'), ('b', 'i8'), ('c', 'c8', (2, 3)), ('d', 'O')] @@ -2747,6 +2926,46 @@ def test_0d_iter(): assert_equal(vals['c'], [[(0.5)]*3]*2) assert_equal(vals['d'], 0.5) +def test_object_iter_cleanup(): + # see gh-18450 + # object arrays can raise a python exception in ufunc inner loops using + # nditer, which should cause iteration to stop & cleanup. There were bugs + # in the nditer cleanup when decref'ing object arrays. + # This test would trigger valgrind "uninitialized read" before the bugfix. + assert_raises(TypeError, lambda: np.zeros((17000, 2), dtype='f4') * None) + + # this more explicit code also triggers the invalid access + arr = np.arange(np.BUFSIZE * 10).reshape(10, -1).astype(str) + oarr = arr.astype(object) + oarr[:, -1] = None + assert_raises(TypeError, lambda: np.add(oarr[:, ::-1], arr[:, ::-1])) + + # followup: this tests for a bug introduced in the first pass of gh-18450, + # caused by an incorrect fallthrough of the TypeError + class T: + def __bool__(self): + raise TypeError("Ambiguous") + assert_raises(TypeError, np.logical_or.reduce, + np.array([T(), T()], dtype='O')) + +def test_object_iter_cleanup_reduce(): + # Similar as above, but a complex reduction case that was previously + # missed (see gh-18810). + # The following array is special in that it cannot be flattened: + arr = np.array([[None, 1], [-1, -1], [None, 2], [-1, -1]])[::2] + with pytest.raises(TypeError): + np.sum(arr) + +@pytest.mark.parametrize("arr", [ + np.ones((8000, 4, 2), dtype=object)[:, ::2, :], + np.ones((8000, 4, 2), dtype=object, order="F")[:, ::2, :], + np.ones((8000, 4, 2), dtype=object)[:, ::2, :].copy("F")]) +def test_object_iter_cleanup_large_reduce(arr): + # More complicated calls are possible for large arrays: + out = np.ones(8000, dtype=np.intp) + # force casting with `dtype=object` + res = np.sum(arr, axis=(1, 2), dtype=object, out=out) + assert_array_equal(res, np.full(8000, 4, dtype=object)) def test_iter_too_large(): # The total size of the iterator must not exceed the maximum intp due @@ -2830,10 +3049,6 @@ def test_writebacks(): x[:] = 123 # x.data still valid assert_equal(au, 6) # but not connected to au - do_close = 1 - # test like above, only in C, and with an option to skip the NpyIter_Close - _multiarray_tests.test_nditer_writeback(3, do_close, au, op_dtypes=[np.dtype('f4')]) - assert_equal(au, 3) it = nditer(au, [], [['readwrite', 'updateifcopy']], casting='equiv', op_dtypes=[np.dtype('f4')]) @@ -2862,7 +3077,7 @@ def test_writebacks(): x[...] = 123 # make sure we cannot reenter the closed iterator enter = it.__enter__ - assert_raises(ValueError, enter) + assert_raises(RuntimeError, enter) def test_close_equivalent(): ''' using a context amanger and using nditer.close are equivalent @@ -2897,12 +3112,169 @@ def test_close_raises(): assert_raises(StopIteration, next, it) assert_raises(ValueError, getattr, it, 'operands') +def test_close_parameters(): + it = np.nditer(np.arange(3)) + assert_raises(TypeError, it.close, 1) + +@pytest.mark.skipif(not HAS_REFCOUNT, reason="Python lacks refcounts") def test_warn_noclose(): a = np.arange(6, dtype='f4') au = a.byteswap().newbyteorder() - do_close = 0 with suppress_warnings() as sup: sup.record(RuntimeWarning) - # test like above, only in C, and with an option to skip the NpyIter_Close - _multiarray_tests.test_nditer_writeback(3, do_close, au, op_dtypes=[np.dtype('f4')]) + it = np.nditer(au, [], [['readwrite', 'updateifcopy']], + casting='equiv', op_dtypes=[np.dtype('f4')]) + del it assert len(sup.log) == 1 + + +@pytest.mark.skipif(sys.version_info[:2] == (3, 9) and sys.platform == "win32", + reason="Errors with Python 3.9 on Windows") +@pytest.mark.skipif(not HAS_REFCOUNT, reason="Python lacks refcounts") +@pytest.mark.parametrize(["in_dtype", "buf_dtype"], + [("i", "O"), ("O", "i"), # most simple cases + ("i,O", "O,O"), # structured partially only copying O + ("O,i", "i,O"), # structured casting to and from O + ]) +@pytest.mark.parametrize("steps", [1, 2, 3]) +def test_partial_iteration_cleanup(in_dtype, buf_dtype, steps): + value = 123 # relies on python cache (leak-check will still find it) + arr = np.full(int(np.BUFSIZE * 2.5), value).astype(in_dtype) + count = sys.getrefcount(value) + + it = np.nditer(arr, op_dtypes=[np.dtype(buf_dtype)], + flags=["buffered", "external_loop", "refs_ok"], casting="unsafe") + for step in range(steps): + # The iteration finishes in 3 steps, the first two are partial + next(it) + + # Note that resetting does not free references + del it + break_cycles() + break_cycles() + assert count == sys.getrefcount(value) + + # Repeat the test with `iternext` + it = np.nditer(arr, op_dtypes=[np.dtype(buf_dtype)], + flags=["buffered", "external_loop", "refs_ok"], casting="unsafe") + for step in range(steps): + it.iternext() + + del it # should ensure cleanup + break_cycles() + break_cycles() + assert count == sys.getrefcount(value) + + +@pytest.mark.skipif(not HAS_REFCOUNT, reason="Python lacks refcounts") +@pytest.mark.parametrize(["in_dtype", "buf_dtype"], + [("O", "i"), # most simple cases + ("O,i", "i,O"), # structured casting to and from O + ]) +def test_partial_iteration_error(in_dtype, buf_dtype): + value = 123 # relies on python cache (leak-check will still find it) + arr = np.full(int(np.BUFSIZE * 2.5), value).astype(in_dtype) + if in_dtype == "O": + arr[int(np.BUFSIZE * 1.5)] = None + else: + arr[int(np.BUFSIZE * 1.5)]["f0"] = None + + count = sys.getrefcount(value) + + it = np.nditer(arr, op_dtypes=[np.dtype(buf_dtype)], + flags=["buffered", "external_loop", "refs_ok"], casting="unsafe") + with pytest.raises(TypeError): + # pytest.raises seems to have issues with the error originating + # in the for loop, so manually unravel: + next(it) + next(it) # raises TypeError + + # Repeat the test with `iternext` after resetting, the buffers should + # already be cleared from any references, so resetting is sufficient. + it.reset() + with pytest.raises(TypeError): + it.iternext() + it.iternext() + + assert count == sys.getrefcount(value) + + +def test_debug_print(capfd): + """ + Matches the expected output of a debug print with the actual output. + Note that the iterator dump should not be considered stable API, + this test is mainly to ensure the print does not crash. + + Currently uses a subprocess to avoid dealing with the C level `printf`s. + """ + # the expected output with all addresses and sizes stripped (they vary + # and/or are platform dependent). + expected = """ + ------ BEGIN ITERATOR DUMP ------ + | Iterator Address: + | ItFlags: BUFFER REDUCE REUSE_REDUCE_LOOPS + | NDim: 2 + | NOp: 2 + | IterSize: 50 + | IterStart: 0 + | IterEnd: 50 + | IterIndex: 0 + | Iterator SizeOf: + | BufferData SizeOf: + | AxisData SizeOf: + | + | Perm: 0 1 + | DTypes: + | DTypes: dtype('float64') dtype('int32') + | InitDataPtrs: + | BaseOffsets: 0 0 + | Operands: + | Operand DTypes: dtype('int64') dtype('float64') + | OpItFlags: + | Flags[0]: READ CAST ALIGNED + | Flags[1]: READ WRITE CAST ALIGNED REDUCE + | + | BufferData: + | BufferSize: 50 + | Size: 5 + | BufIterEnd: 5 + | REDUCE Pos: 0 + | REDUCE OuterSize: 10 + | REDUCE OuterDim: 1 + | Strides: 8 4 + | Ptrs: + | REDUCE Outer Strides: 40 0 + | REDUCE Outer Ptrs: + | ReadTransferFn: + | ReadTransferData: + | WriteTransferFn: + | WriteTransferData: + | Buffers: + | + | AxisData[0]: + | Shape: 5 + | Index: 0 + | Strides: 16 8 + | Ptrs: + | AxisData[1]: + | Shape: 10 + | Index: 0 + | Strides: 80 0 + | Ptrs: + ------- END ITERATOR DUMP ------- + """.strip().splitlines() + + arr1 = np.arange(100, dtype=np.int64).reshape(10, 10)[:, ::2] + arr2 = np.arange(5.) + it = np.nditer((arr1, arr2), op_dtypes=["d", "i4"], casting="unsafe", + flags=["reduce_ok", "buffered"], + op_flags=[["readonly"], ["readwrite"]]) + it.debug_print() + res = capfd.readouterr().out + res = res.strip().splitlines() + + assert len(res) == len(expected) + for res_line, expected_line in zip(res, expected): + # The actual output may have additional pointers listed that are + # stripped from the example output: + assert res_line.startswith(expected_line.strip()) diff --git a/numpy/core/tests/test_numeric.py b/numpy/core/tests/test_numeric.py index 53486dc5151a..ad94379115a8 100644 --- a/numpy/core/tests/test_numeric.py +++ b/numpy/core/tests/test_numeric.py @@ -1,10 +1,9 @@ -from __future__ import division, absolute_import, print_function - import sys import warnings import itertools import platform import pytest +import math from decimal import Decimal import numpy as np @@ -13,11 +12,15 @@ from numpy.testing import ( assert_, assert_equal, assert_raises, assert_raises_regex, assert_array_equal, assert_almost_equal, assert_array_almost_equal, - suppress_warnings, HAS_REFCOUNT + assert_warns, assert_array_max_ulp, HAS_REFCOUNT ) +from numpy.core._rational_tests import rational + +from hypothesis import given, strategies as st +from hypothesis.extra import numpy as hynp -class TestResize(object): +class TestResize: def test_copies(self): A = np.array([[1, 2], [3, 4]]) Ar1 = np.array([[1, 2, 3, 4], [1, 2, 3, 4]]) @@ -29,6 +32,17 @@ def test_copies(self): Ar3 = np.array([[1, 2, 3], [4, 1, 2], [3, 4, 1], [2, 3, 4]]) assert_equal(np.resize(A, (4, 3)), Ar3) + def test_repeats(self): + A = np.array([1, 2, 3]) + Ar1 = np.array([[1, 2, 3, 1], [2, 3, 1, 2]]) + assert_equal(np.resize(A, (2, 4)), Ar1) + + Ar2 = np.array([[1, 2], [3, 1], [2, 3], [1, 2]]) + assert_equal(np.resize(A, (4, 2)), Ar2) + + Ar3 = np.array([[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]]) + assert_equal(np.resize(A, (4, 3)), Ar3) + def test_zeroresize(self): A = np.array([[1, 2], [3, 4]]) Ar = np.resize(A, (0,)) @@ -43,13 +57,30 @@ def test_zeroresize(self): def test_reshape_from_zero(self): # See also gh-6740 - A = np.zeros(0, dtype=[('a', np.float32, 1)]) + A = np.zeros(0, dtype=[('a', np.float32)]) Ar = np.resize(A, (2, 1)) assert_array_equal(Ar, np.zeros((2, 1), Ar.dtype)) assert_equal(A.dtype, Ar.dtype) + def test_negative_resize(self): + A = np.arange(0, 10, dtype=np.float32) + new_shape = (-10, -1) + with pytest.raises(ValueError, match=r"negative"): + np.resize(A, new_shape=new_shape) -class TestNonarrayArgs(object): + def test_subclass(self): + class MyArray(np.ndarray): + __array_priority__ = 1. + + my_arr = np.array([1]).view(MyArray) + assert type(np.resize(my_arr, 5)) is MyArray + assert type(np.resize(my_arr, 0)) is MyArray + + my_arr = np.array([]).view(MyArray) + assert type(np.resize(my_arr, 5)) is MyArray + + +class TestNonarrayArgs: # check that non-array arguments to functions wrap them in arrays def test_choose(self): choices = [[0, 1, 2], @@ -138,6 +169,51 @@ def test_round(self): arr = [1.56, 72.54, 6.35, 3.25] tgt = [1.6, 72.5, 6.4, 3.2] assert_equal(np.around(arr, decimals=1), tgt) + s = np.float64(1.) + assert_(isinstance(s.round(), np.float64)) + assert_equal(s.round(), 1.) + + @pytest.mark.parametrize('dtype', [ + np.int8, np.int16, np.int32, np.int64, + np.uint8, np.uint16, np.uint32, np.uint64, + np.float16, np.float32, np.float64, + ]) + def test_dunder_round(self, dtype): + s = dtype(1) + assert_(isinstance(round(s), int)) + assert_(isinstance(round(s, None), int)) + assert_(isinstance(round(s, ndigits=None), int)) + assert_equal(round(s), 1) + assert_equal(round(s, None), 1) + assert_equal(round(s, ndigits=None), 1) + + @pytest.mark.parametrize('val, ndigits', [ + pytest.param(2**31 - 1, -1, + marks=pytest.mark.xfail(reason="Out of range of int32") + ), + (2**31 - 1, 1-math.ceil(math.log10(2**31 - 1))), + (2**31 - 1, -math.ceil(math.log10(2**31 - 1))) + ]) + def test_dunder_round_edgecases(self, val, ndigits): + assert_equal(round(val, ndigits), round(np.int32(val), ndigits)) + + def test_dunder_round_accuracy(self): + f = np.float64(5.1 * 10**73) + assert_(isinstance(round(f, -73), np.float64)) + assert_array_max_ulp(round(f, -73), 5.0 * 10**73) + assert_(isinstance(round(f, ndigits=-73), np.float64)) + assert_array_max_ulp(round(f, ndigits=-73), 5.0 * 10**73) + + i = np.int64(501) + assert_(isinstance(round(i, -2), np.int64)) + assert_array_max_ulp(round(i, -2), 500) + assert_(isinstance(round(i, ndigits=-2), np.int64)) + assert_array_max_ulp(round(i, ndigits=-2), 500) + + @pytest.mark.xfail(raises=AssertionError, reason="gh-15896") + def test_round_py_consistency(self): + f = 5.1 * 10**73 + assert_equal(round(np.float64(f), -73), round(f, -73)) def test_searchsorted(self): arr = [-8, -5, -1, 3, 6, 10] @@ -152,7 +228,15 @@ def test_size(self): def test_squeeze(self): A = [[[1, 1, 1], [2, 2, 2], [3, 3, 3]]] - assert_(np.squeeze(A).shape == (3, 3)) + assert_equal(np.squeeze(A).shape, (3, 3)) + assert_equal(np.squeeze(np.zeros((1, 3, 1))).shape, (3,)) + assert_equal(np.squeeze(np.zeros((1, 3, 1)), axis=0).shape, (3, 1)) + assert_equal(np.squeeze(np.zeros((1, 3, 1)), axis=-1).shape, (1, 3)) + assert_equal(np.squeeze(np.zeros((1, 3, 1)), axis=2).shape, (1, 3)) + assert_equal(np.squeeze([np.zeros((3, 1))]).shape, (3,)) + assert_equal(np.squeeze([np.zeros((3, 1))], axis=0).shape, (3, 1)) + assert_equal(np.squeeze([np.zeros((3, 1))], axis=2).shape, (1, 3)) + assert_equal(np.squeeze([np.zeros((3, 1))], axis=-1).shape, (1, 3)) def test_std(self): A = [[1, 2, 3], [4, 5, 6]] @@ -208,8 +292,12 @@ def test_var(self): assert_(np.isnan(np.var([]))) assert_(w[0].category is RuntimeWarning) + B = np.array([None, 0]) + B[0] = 1j + assert_almost_equal(np.var(B), 0.25) -class TestIsscalar(object): + +class TestIsscalar: def test_isscalar(self): assert_(np.isscalar(3.1)) assert_(np.isscalar(np.int16(12345))) @@ -225,7 +313,7 @@ def test_isscalar(self): assert_(np.isscalar(Number())) -class TestBoolScalar(object): +class TestBoolScalar: def test_logical(self): f = np.False_ t = np.True_ @@ -258,7 +346,7 @@ def test_bitwise_xor(self): assert_((f ^ f) is f) -class TestBoolArray(object): +class TestBoolArray: def setup(self): # offset for simd tests self.t = np.array([True] * 41, dtype=bool)[1::] @@ -345,7 +433,7 @@ def test_logical_and_or_xor(self): assert_array_equal(self.im ^ False, self.im) -class TestBoolCmp(object): +class TestBoolCmp: def setup(self): self.f = np.ones(256, dtype=np.float32) self.ef = np.ones(self.f.size, dtype=bool) @@ -445,7 +533,7 @@ def test_double(self): assert_array_equal(np.signbit(self.signd[i:]), self.ed[i:]) -class TestSeterr(object): +class TestSeterr: def test_default(self): err = np.geterr() assert_equal(err, @@ -471,12 +559,9 @@ def test_set(self): @pytest.mark.skipif(platform.machine() == "armv5tel", reason="See gh-413.") def test_divide_err(self): with np.errstate(divide='raise'): - try: + with assert_raises(FloatingPointError): np.array([1.]) / np.array([0.]) - except FloatingPointError: - pass - else: - self.fail() + np.seterr(divide='ignore') np.array([1.]) / np.array([0.]) @@ -529,7 +614,7 @@ def test_errobj_noerrmask(self): np.seterrobj(olderrobj) -class TestFloatExceptions(object): +class TestFloatExceptions: def assert_raises_fpe(self, fpeerr, flop, x, y): ftype = type(x) try: @@ -552,59 +637,65 @@ def assert_op_raises_fpe(self, fpeerr, flop, sc1, sc2): self.assert_raises_fpe(fpeerr, flop, sc1, sc2[()]) self.assert_raises_fpe(fpeerr, flop, sc1[()], sc2[()]) - def test_floating_exceptions(self): + # Test for all real and complex float types + @pytest.mark.parametrize("typecode", np.typecodes["AllFloat"]) + def test_floating_exceptions(self, typecode): # Test basic arithmetic function errors with np.errstate(all='raise'): - # Test for all real and complex float types - for typecode in np.typecodes['AllFloat']: - ftype = np.obj2sctype(typecode) - if np.dtype(ftype).kind == 'f': - # Get some extreme values for the type - fi = np.finfo(ftype) - ft_tiny = fi.tiny - ft_max = fi.max - ft_eps = fi.eps - underflow = 'underflow' - divbyzero = 'divide by zero' - else: - # 'c', complex, corresponding real dtype - rtype = type(ftype(0).real) - fi = np.finfo(rtype) - ft_tiny = ftype(fi.tiny) - ft_max = ftype(fi.max) - ft_eps = ftype(fi.eps) - # The complex types raise different exceptions - underflow = '' - divbyzero = '' - overflow = 'overflow' - invalid = 'invalid' - + ftype = np.obj2sctype(typecode) + if np.dtype(ftype).kind == 'f': + # Get some extreme values for the type + fi = np.finfo(ftype) + ft_tiny = fi._machar.tiny + ft_max = fi.max + ft_eps = fi.eps + underflow = 'underflow' + divbyzero = 'divide by zero' + else: + # 'c', complex, corresponding real dtype + rtype = type(ftype(0).real) + fi = np.finfo(rtype) + ft_tiny = ftype(fi._machar.tiny) + ft_max = ftype(fi.max) + ft_eps = ftype(fi.eps) + # The complex types raise different exceptions + underflow = '' + divbyzero = '' + overflow = 'overflow' + invalid = 'invalid' + + # The value of tiny for double double is NaN, so we need to + # pass the assert + if not np.isnan(ft_tiny): self.assert_raises_fpe(underflow, - lambda a, b: a/b, ft_tiny, ft_max) + lambda a, b: a/b, ft_tiny, ft_max) self.assert_raises_fpe(underflow, - lambda a, b: a*b, ft_tiny, ft_tiny) - self.assert_raises_fpe(overflow, - lambda a, b: a*b, ft_max, ftype(2)) - self.assert_raises_fpe(overflow, - lambda a, b: a/b, ft_max, ftype(0.5)) - self.assert_raises_fpe(overflow, - lambda a, b: a+b, ft_max, ft_max*ft_eps) - self.assert_raises_fpe(overflow, - lambda a, b: a-b, -ft_max, ft_max*ft_eps) - self.assert_raises_fpe(overflow, - np.power, ftype(2), ftype(2**fi.nexp)) - self.assert_raises_fpe(divbyzero, - lambda a, b: a/b, ftype(1), ftype(0)) - self.assert_raises_fpe(invalid, - lambda a, b: a/b, ftype(np.inf), ftype(np.inf)) - self.assert_raises_fpe(invalid, - lambda a, b: a/b, ftype(0), ftype(0)) - self.assert_raises_fpe(invalid, - lambda a, b: a-b, ftype(np.inf), ftype(np.inf)) - self.assert_raises_fpe(invalid, - lambda a, b: a+b, ftype(np.inf), ftype(-np.inf)) - self.assert_raises_fpe(invalid, - lambda a, b: a*b, ftype(0), ftype(np.inf)) + lambda a, b: a*b, ft_tiny, ft_tiny) + self.assert_raises_fpe(overflow, + lambda a, b: a*b, ft_max, ftype(2)) + self.assert_raises_fpe(overflow, + lambda a, b: a/b, ft_max, ftype(0.5)) + self.assert_raises_fpe(overflow, + lambda a, b: a+b, ft_max, ft_max*ft_eps) + self.assert_raises_fpe(overflow, + lambda a, b: a-b, -ft_max, ft_max*ft_eps) + self.assert_raises_fpe(overflow, + np.power, ftype(2), ftype(2**fi.nexp)) + self.assert_raises_fpe(divbyzero, + lambda a, b: a/b, ftype(1), ftype(0)) + self.assert_raises_fpe( + invalid, lambda a, b: a/b, ftype(np.inf), ftype(np.inf) + ) + self.assert_raises_fpe(invalid, + lambda a, b: a/b, ftype(0), ftype(0)) + self.assert_raises_fpe( + invalid, lambda a, b: a-b, ftype(np.inf), ftype(np.inf) + ) + self.assert_raises_fpe( + invalid, lambda a, b: a+b, ftype(np.inf), ftype(-np.inf) + ) + self.assert_raises_fpe(invalid, + lambda a, b: a*b, ftype(0), ftype(np.inf)) def test_warnings(self): # test warning code path @@ -625,7 +716,7 @@ def test_warnings(self): assert_("underflow" in str(w[-1].message)) -class TestTypes(object): +class TestTypes: def check_promotion_cases(self, promote_func): # tests that the scalars get coerced correctly. b = np.bool_(0) @@ -780,39 +871,168 @@ def test_promote_types_endian(self): assert_equal(np.promote_types('<m8', '<m8'), np.dtype('m8')) assert_equal(np.promote_types('>m8', '>m8'), np.dtype('m8')) - def test_promote_types_strings(self): - assert_equal(np.promote_types('bool', 'S'), np.dtype('S5')) - assert_equal(np.promote_types('b', 'S'), np.dtype('S4')) - assert_equal(np.promote_types('u1', 'S'), np.dtype('S3')) - assert_equal(np.promote_types('u2', 'S'), np.dtype('S5')) - assert_equal(np.promote_types('u4', 'S'), np.dtype('S10')) - assert_equal(np.promote_types('u8', 'S'), np.dtype('S20')) - assert_equal(np.promote_types('i1', 'S'), np.dtype('S4')) - assert_equal(np.promote_types('i2', 'S'), np.dtype('S6')) - assert_equal(np.promote_types('i4', 'S'), np.dtype('S11')) - assert_equal(np.promote_types('i8', 'S'), np.dtype('S21')) - assert_equal(np.promote_types('bool', 'U'), np.dtype('U5')) - assert_equal(np.promote_types('b', 'U'), np.dtype('U4')) - assert_equal(np.promote_types('u1', 'U'), np.dtype('U3')) - assert_equal(np.promote_types('u2', 'U'), np.dtype('U5')) - assert_equal(np.promote_types('u4', 'U'), np.dtype('U10')) - assert_equal(np.promote_types('u8', 'U'), np.dtype('U20')) - assert_equal(np.promote_types('i1', 'U'), np.dtype('U4')) - assert_equal(np.promote_types('i2', 'U'), np.dtype('U6')) - assert_equal(np.promote_types('i4', 'U'), np.dtype('U11')) - assert_equal(np.promote_types('i8', 'U'), np.dtype('U21')) - assert_equal(np.promote_types('bool', 'S1'), np.dtype('S5')) - assert_equal(np.promote_types('bool', 'S30'), np.dtype('S30')) - assert_equal(np.promote_types('b', 'S1'), np.dtype('S4')) - assert_equal(np.promote_types('b', 'S30'), np.dtype('S30')) - assert_equal(np.promote_types('u1', 'S1'), np.dtype('S3')) - assert_equal(np.promote_types('u1', 'S30'), np.dtype('S30')) - assert_equal(np.promote_types('u2', 'S1'), np.dtype('S5')) - assert_equal(np.promote_types('u2', 'S30'), np.dtype('S30')) - assert_equal(np.promote_types('u4', 'S1'), np.dtype('S10')) - assert_equal(np.promote_types('u4', 'S30'), np.dtype('S30')) - assert_equal(np.promote_types('u8', 'S1'), np.dtype('S20')) - assert_equal(np.promote_types('u8', 'S30'), np.dtype('S30')) + def test_can_cast_and_promote_usertypes(self): + # The rational type defines safe casting for signed integers, + # boolean. Rational itself *does* cast safely to double. + # (rational does not actually cast to all signed integers, e.g. + # int64 can be both long and longlong and it registers only the first) + valid_types = ["int8", "int16", "int32", "int64", "bool"] + invalid_types = "BHILQP" + "FDG" + "mM" + "f" + "V" + + rational_dt = np.dtype(rational) + for numpy_dtype in valid_types: + numpy_dtype = np.dtype(numpy_dtype) + assert np.can_cast(numpy_dtype, rational_dt) + assert np.promote_types(numpy_dtype, rational_dt) is rational_dt + + for numpy_dtype in invalid_types: + numpy_dtype = np.dtype(numpy_dtype) + assert not np.can_cast(numpy_dtype, rational_dt) + with pytest.raises(TypeError): + np.promote_types(numpy_dtype, rational_dt) + + double_dt = np.dtype("double") + assert np.can_cast(rational_dt, double_dt) + assert np.promote_types(double_dt, rational_dt) is double_dt + + @pytest.mark.parametrize("swap", ["", "swap"]) + @pytest.mark.parametrize("string_dtype", ["U", "S"]) + def test_promote_types_strings(self, swap, string_dtype): + if swap == "swap": + promote_types = lambda a, b: np.promote_types(b, a) + else: + promote_types = np.promote_types + + S = string_dtype + + # Promote numeric with unsized string: + assert_equal(promote_types('bool', S), np.dtype(S+'5')) + assert_equal(promote_types('b', S), np.dtype(S+'4')) + assert_equal(promote_types('u1', S), np.dtype(S+'3')) + assert_equal(promote_types('u2', S), np.dtype(S+'5')) + assert_equal(promote_types('u4', S), np.dtype(S+'10')) + assert_equal(promote_types('u8', S), np.dtype(S+'20')) + assert_equal(promote_types('i1', S), np.dtype(S+'4')) + assert_equal(promote_types('i2', S), np.dtype(S+'6')) + assert_equal(promote_types('i4', S), np.dtype(S+'11')) + assert_equal(promote_types('i8', S), np.dtype(S+'21')) + # Promote numeric with sized string: + assert_equal(promote_types('bool', S+'1'), np.dtype(S+'5')) + assert_equal(promote_types('bool', S+'30'), np.dtype(S+'30')) + assert_equal(promote_types('b', S+'1'), np.dtype(S+'4')) + assert_equal(promote_types('b', S+'30'), np.dtype(S+'30')) + assert_equal(promote_types('u1', S+'1'), np.dtype(S+'3')) + assert_equal(promote_types('u1', S+'30'), np.dtype(S+'30')) + assert_equal(promote_types('u2', S+'1'), np.dtype(S+'5')) + assert_equal(promote_types('u2', S+'30'), np.dtype(S+'30')) + assert_equal(promote_types('u4', S+'1'), np.dtype(S+'10')) + assert_equal(promote_types('u4', S+'30'), np.dtype(S+'30')) + assert_equal(promote_types('u8', S+'1'), np.dtype(S+'20')) + assert_equal(promote_types('u8', S+'30'), np.dtype(S+'30')) + # Promote with object: + assert_equal(promote_types('O', S+'30'), np.dtype('O')) + + @pytest.mark.parametrize("dtype", + list(np.typecodes["All"]) + + ["i,i", "S3", "S100", "U3", "U100", rational]) + def test_promote_identical_types_metadata(self, dtype): + # The same type passed in twice to promote types always + # preserves metadata + metadata = {1: 1} + dtype = np.dtype(dtype, metadata=metadata) + + res = np.promote_types(dtype, dtype) + assert res.metadata == dtype.metadata + + # byte-swapping preserves and makes the dtype native: + dtype = dtype.newbyteorder() + if dtype.isnative: + # The type does not have byte swapping + return + + res = np.promote_types(dtype, dtype) + if res.char in "?bhilqpBHILQPefdgFDGOmM" or dtype.type is rational: + # Metadata is lost for simple promotions (they create a new dtype) + assert res.metadata is None + else: + assert res.metadata == metadata + if dtype.kind != "V": + # the result is native (except for structured void) + assert res.isnative + + @pytest.mark.slow + @pytest.mark.filterwarnings('ignore:Promotion of numbers:FutureWarning') + @pytest.mark.parametrize(["dtype1", "dtype2"], + itertools.product( + list(np.typecodes["All"]) + + ["i,i", "S3", "S100", "U3", "U100", rational], + repeat=2)) + def test_promote_types_metadata(self, dtype1, dtype2): + """Metadata handling in promotion does not appear formalized + right now in NumPy. This test should thus be considered to + document behaviour, rather than test the correct definition of it. + + This test is very ugly, it was useful for rewriting part of the + promotion, but probably should eventually be replaced/deleted + (i.e. when metadata handling in promotion is better defined). + """ + metadata1 = {1: 1} + metadata2 = {2: 2} + dtype1 = np.dtype(dtype1, metadata=metadata1) + dtype2 = np.dtype(dtype2, metadata=metadata2) + + try: + res = np.promote_types(dtype1, dtype2) + except TypeError: + # Promotion failed, this test only checks metadata + return + + if res.char in "?bhilqpBHILQPefdgFDGOmM" or res.type is rational: + # All simple types lose metadata (due to using promotion table): + assert res.metadata is None + elif res == dtype1: + # If one result is the result, it is usually returned unchanged: + assert res is dtype1 + elif res == dtype2: + # dtype1 may have been cast to the same type/kind as dtype2. + # If the resulting dtype is identical we currently pick the cast + # version of dtype1, which lost the metadata: + if np.promote_types(dtype1, dtype2.kind) == dtype2: + res.metadata is None + else: + res.metadata == metadata2 + else: + assert res.metadata is None + + # Try again for byteswapped version + dtype1 = dtype1.newbyteorder() + assert dtype1.metadata == metadata1 + res_bs = np.promote_types(dtype1, dtype2) + if res_bs.names is not None: + # Structured promotion doesn't remove byteswap: + assert res_bs.newbyteorder() == res + else: + assert res_bs == res + assert res_bs.metadata == res.metadata + + @pytest.mark.parametrize(["dtype1", "dtype2"], + [[np.dtype("V6"), np.dtype("V10")], + [np.dtype([("name1", "i8")]), np.dtype([("name2", "i8")])], + [np.dtype("i8,i8"), np.dtype("i4,i4")], + ]) + def test_invalid_void_promotion(self, dtype1, dtype2): + # Mainly test structured void promotion, which currently allows + # byte-swapping, but nothing else: + with pytest.raises(TypeError): + np.promote_types(dtype1, dtype2) + + @pytest.mark.parametrize(["dtype1", "dtype2"], + [[np.dtype("V10"), np.dtype("V10")], + [np.dtype([("name1", "<i8")]), np.dtype([("name1", ">i8")])], + [np.dtype("i8,i8"), np.dtype("i8,>i8")], + ]) + def test_valid_void_promotion(self, dtype1, dtype2): + assert np.promote_types(dtype1, dtype2) is dtype1 def test_can_cast(self): assert_(np.can_cast(np.int32, np.int64)) @@ -891,6 +1111,41 @@ def test_can_cast(self): # Also test keyword arguments assert_(np.can_cast(from_=np.int32, to=np.int64)) + def test_can_cast_simple_to_structured(self): + # Non-structured can only be cast to structured in 'unsafe' mode. + assert_(not np.can_cast('i4', 'i4,i4')) + assert_(not np.can_cast('i4', 'i4,i2')) + assert_(np.can_cast('i4', 'i4,i4', casting='unsafe')) + assert_(np.can_cast('i4', 'i4,i2', casting='unsafe')) + # Even if there is just a single field which is OK. + assert_(not np.can_cast('i2', [('f1', 'i4')])) + assert_(not np.can_cast('i2', [('f1', 'i4')], casting='same_kind')) + assert_(np.can_cast('i2', [('f1', 'i4')], casting='unsafe')) + # It should be the same for recursive structured or subarrays. + assert_(not np.can_cast('i2', [('f1', 'i4,i4')])) + assert_(np.can_cast('i2', [('f1', 'i4,i4')], casting='unsafe')) + assert_(not np.can_cast('i2', [('f1', '(2,3)i4')])) + assert_(np.can_cast('i2', [('f1', '(2,3)i4')], casting='unsafe')) + + def test_can_cast_structured_to_simple(self): + # Need unsafe casting for structured to simple. + assert_(not np.can_cast([('f1', 'i4')], 'i4')) + assert_(np.can_cast([('f1', 'i4')], 'i4', casting='unsafe')) + assert_(np.can_cast([('f1', 'i4')], 'i2', casting='unsafe')) + # Since it is unclear what is being cast, multiple fields to + # single should not work even for unsafe casting. + assert_(not np.can_cast('i4,i4', 'i4', casting='unsafe')) + # But a single field inside a single field is OK. + assert_(not np.can_cast([('f1', [('x', 'i4')])], 'i4')) + assert_(np.can_cast([('f1', [('x', 'i4')])], 'i4', casting='unsafe')) + # And a subarray is fine too - it will just take the first element + # (arguably not very consistently; might also take the first field). + assert_(not np.can_cast([('f0', '(3,)i4')], 'i4')) + assert_(np.can_cast([('f0', '(3,)i4')], 'i4', casting='unsafe')) + # But a structured subarray with multiple fields should fail. + assert_(not np.can_cast([('f0', ('i4,i4'), (2,))], 'i4', + casting='unsafe')) + def test_can_cast_values(self): # gh-5917 for dt in np.sctypes['int'] + np.sctypes['uint']: @@ -911,10 +1166,9 @@ class NIterError(Exception): pass -class TestFromiter(object): +class TestFromiter: def makegen(self): - for x in range(24): - yield x**2 + return (x**2 for x in range(24)) def test_types(self): ai32 = np.fromiter(self.makegen(), np.int32) @@ -962,18 +1216,30 @@ def test_2592_edge(self): self.load_data(count, eindex), dtype=int, count=count) -class TestNonzero(object): +class TestNonzero: def test_nonzero_trivial(self): assert_equal(np.count_nonzero(np.array([])), 0) assert_equal(np.count_nonzero(np.array([], dtype='?')), 0) assert_equal(np.nonzero(np.array([])), ([],)) + assert_equal(np.count_nonzero(np.array([0])), 0) + assert_equal(np.count_nonzero(np.array([0], dtype='?')), 0) + assert_equal(np.nonzero(np.array([0])), ([],)) + + assert_equal(np.count_nonzero(np.array([1])), 1) + assert_equal(np.count_nonzero(np.array([1], dtype='?')), 1) + assert_equal(np.nonzero(np.array([1])), ([0],)) + + def test_nonzero_zerod(self): assert_equal(np.count_nonzero(np.array(0)), 0) assert_equal(np.count_nonzero(np.array(0, dtype='?')), 0) - assert_equal(np.nonzero(np.array(0)), ([],)) + with assert_warns(DeprecationWarning): + assert_equal(np.nonzero(np.array(0)), ([],)) + assert_equal(np.count_nonzero(np.array(1)), 1) assert_equal(np.count_nonzero(np.array(1, dtype='?')), 1) - assert_equal(np.nonzero(np.array(1)), ([0],)) + with assert_warns(DeprecationWarning): + assert_equal(np.nonzero(np.array(1)), ([0],)) def test_nonzero_onedim(self): x = np.array([1, 0, 2, -1, 0, 0, 8]) @@ -981,20 +1247,30 @@ def test_nonzero_onedim(self): assert_equal(np.count_nonzero(x), 4) assert_equal(np.nonzero(x), ([0, 2, 3, 6],)) - x = np.array([(1, 2), (0, 0), (1, 1), (-1, 3), (0, 7)], - dtype=[('a', 'i4'), ('b', 'i2')]) + # x = np.array([(1, 2), (0, 0), (1, 1), (-1, 3), (0, 7)], + # dtype=[('a', 'i4'), ('b', 'i2')]) + x = np.array([(1, 2, -5, -3), (0, 0, 2, 7), (1, 1, 0, 1), (-1, 3, 1, 0), (0, 7, 0, 4)], + dtype=[('a', 'i4'), ('b', 'i2'), ('c', 'i1'), ('d', 'i8')]) assert_equal(np.count_nonzero(x['a']), 3) assert_equal(np.count_nonzero(x['b']), 4) + assert_equal(np.count_nonzero(x['c']), 3) + assert_equal(np.count_nonzero(x['d']), 4) assert_equal(np.nonzero(x['a']), ([0, 2, 3],)) assert_equal(np.nonzero(x['b']), ([0, 2, 3, 4],)) def test_nonzero_twodim(self): x = np.array([[0, 1, 0], [2, 0, 3]]) - assert_equal(np.count_nonzero(x), 3) + assert_equal(np.count_nonzero(x.astype('i1')), 3) + assert_equal(np.count_nonzero(x.astype('i2')), 3) + assert_equal(np.count_nonzero(x.astype('i4')), 3) + assert_equal(np.count_nonzero(x.astype('i8')), 3) assert_equal(np.nonzero(x), ([0, 1, 1], [1, 0, 2])) x = np.eye(3) - assert_equal(np.count_nonzero(x), 3) + assert_equal(np.count_nonzero(x.astype('i1')), 3) + assert_equal(np.count_nonzero(x.astype('i2')), 3) + assert_equal(np.count_nonzero(x.astype('i4')), 3) + assert_equal(np.count_nonzero(x.astype('i8')), 3) assert_equal(np.nonzero(x), ([0, 1, 2], [0, 1, 2])) x = np.array([[(0, 1), (0, 0), (1, 11)], @@ -1146,6 +1422,17 @@ def test_countnonzero_axis_empty(self): a = np.array([[0, 0, 1], [1, 0, 1]]) assert_equal(np.count_nonzero(a, axis=()), a.astype(bool)) + def test_countnonzero_keepdims(self): + a = np.array([[0, 0, 1, 0], + [0, 3, 5, 0], + [7, 9, 2, 0]]) + assert_equal(np.count_nonzero(a, axis=0, keepdims=True), + [[1, 2, 3, 0]]) + assert_equal(np.count_nonzero(a, axis=1, keepdims=True), + [[1], [2], [3]]) + assert_equal(np.count_nonzero(a, keepdims=True), + [[6]]) + def test_array_method(self): # Tests that the array method # call to nonzero works @@ -1156,19 +1443,115 @@ def test_array_method(self): def test_nonzero_invalid_object(self): # gh-9295 - a = np.array([np.array([1, 2]), 3]) + a = np.array([np.array([1, 2]), 3], dtype=object) assert_raises(ValueError, np.nonzero, a) class BoolErrors: def __bool__(self): raise ValueError("Not allowed") - def __nonzero__(self): - raise ValueError("Not allowed") assert_raises(ValueError, np.nonzero, np.array([BoolErrors()])) + def test_nonzero_sideeffect_safety(self): + # gh-13631 + class FalseThenTrue: + _val = False + def __bool__(self): + try: + return self._val + finally: + self._val = True + + class TrueThenFalse: + _val = True + def __bool__(self): + try: + return self._val + finally: + self._val = False + + # result grows on the second pass + a = np.array([True, FalseThenTrue()]) + assert_raises(RuntimeError, np.nonzero, a) + + a = np.array([[True], [FalseThenTrue()]]) + assert_raises(RuntimeError, np.nonzero, a) + + # result shrinks on the second pass + a = np.array([False, TrueThenFalse()]) + assert_raises(RuntimeError, np.nonzero, a) + + a = np.array([[False], [TrueThenFalse()]]) + assert_raises(RuntimeError, np.nonzero, a) + + def test_nonzero_sideffects_structured_void(self): + # Checks that structured void does not mutate alignment flag of + # original array. + arr = np.zeros(5, dtype="i1,i8,i8") # `ones` may short-circuit + assert arr.flags.aligned # structs are considered "aligned" + assert not arr["f2"].flags.aligned + # make sure that nonzero/count_nonzero do not flip the flag: + np.nonzero(arr) + assert arr.flags.aligned + np.count_nonzero(arr) + assert arr.flags.aligned + + def test_nonzero_exception_safe(self): + # gh-13930 + + class ThrowsAfter: + def __init__(self, iters): + self.iters_left = iters -class TestIndex(object): + def __bool__(self): + if self.iters_left == 0: + raise ValueError("called `iters` times") + + self.iters_left -= 1 + return True + + """ + Test that a ValueError is raised instead of a SystemError + + If the __bool__ function is called after the error state is set, + Python (cpython) will raise a SystemError. + """ + + # assert that an exception in first pass is handled correctly + a = np.array([ThrowsAfter(5)]*10) + assert_raises(ValueError, np.nonzero, a) + + # raise exception in second pass for 1-dimensional loop + a = np.array([ThrowsAfter(15)]*10) + assert_raises(ValueError, np.nonzero, a) + + # raise exception in second pass for n-dimensional loop + a = np.array([[ThrowsAfter(15)]]*10) + assert_raises(ValueError, np.nonzero, a) + + def test_structured_threadsafety(self): + # Nonzero (and some other functions) should be threadsafe for + # structured datatypes, see gh-15387. This test can behave randomly. + from concurrent.futures import ThreadPoolExecutor + + # Create a deeply nested dtype to make a failure more likely: + dt = np.dtype([("", "f8")]) + dt = np.dtype([("", dt)]) + dt = np.dtype([("", dt)] * 2) + # The array should be large enough to likely run into threading issues + arr = np.random.uniform(size=(5000, 4)).view(dt)[:, 0] + def func(arr): + arr.nonzero() + + tpe = ThreadPoolExecutor(max_workers=8) + futures = [tpe.submit(func, arr) for _ in range(10)] + for f in futures: + f.result() + + assert arr.dtype is dt + + +class TestIndex: def test_boolean(self): a = rand(3, 5, 8) V = rand(5, 8) @@ -1185,7 +1568,7 @@ def test_boolean_edgecase(self): assert_equal(c.dtype, np.dtype('int32')) -class TestBinaryRepr(object): +class TestBinaryRepr: def test_zero(self): assert_equal(np.binary_repr(0), '0') @@ -1221,8 +1604,13 @@ def test_neg_width_boundaries(self): exp = '1' + (width - 1) * '0' assert_equal(np.binary_repr(num, width=width), exp) + def test_large_neg_int64(self): + # See gh-14289. + assert_equal(np.binary_repr(np.int64(-2**62), width=64), + '11' + '0'*62) + -class TestBaseRepr(object): +class TestBaseRepr: def test_base3(self): assert_equal(np.base_repr(3**5, 3), '100000') @@ -1244,7 +1632,7 @@ def test_base_range(self): np.base_repr(1, 37) -class TestArrayComparisons(object): +class TestArrayComparisons: def test_array_equal(self): res = np.array_equal(np.array([1, 2]), np.array([1, 2])) assert_(res) @@ -1266,6 +1654,36 @@ def test_array_equal(self): assert_(res) assert_(type(res) is bool) + def test_array_equal_equal_nan(self): + # Test array_equal with equal_nan kwarg + a1 = np.array([1, 2, np.nan]) + a2 = np.array([1, np.nan, 2]) + a3 = np.array([1, 2, np.inf]) + + # equal_nan=False by default + assert_(not np.array_equal(a1, a1)) + assert_(np.array_equal(a1, a1, equal_nan=True)) + assert_(not np.array_equal(a1, a2, equal_nan=True)) + # nan's not conflated with inf's + assert_(not np.array_equal(a1, a3, equal_nan=True)) + # 0-D arrays + a = np.array(np.nan) + assert_(not np.array_equal(a, a)) + assert_(np.array_equal(a, a, equal_nan=True)) + # Non-float dtype - equal_nan should have no effect + a = np.array([1, 2, 3], dtype=int) + assert_(np.array_equal(a, a)) + assert_(np.array_equal(a, a, equal_nan=True)) + # Multi-dimensional array + a = np.array([[0, 1], [np.nan, 1]]) + assert_(not np.array_equal(a, a)) + assert_(np.array_equal(a, a, equal_nan=True)) + # Complex values + a, b = [np.array([1 + 1j])]*2 + a.real, b.imag = np.nan, np.nan + assert_(not np.array_equal(a, b, equal_nan=False)) + assert_(np.array_equal(a, b, equal_nan=True)) + def test_none_compares_elementwise(self): a = np.array([None, 1, None], dtype=object) assert_equal(a == None, [True, False, True]) @@ -1275,7 +1693,6 @@ def test_none_compares_elementwise(self): assert_equal(a == None, [False, False, False]) assert_equal(a != None, [True, True, True]) - def test_array_equiv(self): res = np.array_equiv(np.array([1, 2]), np.array([1, 2])) assert_(res) @@ -1306,6 +1723,22 @@ def test_array_equiv(self): assert_(not res) assert_(type(res) is bool) + @pytest.mark.parametrize("dtype", ["V0", "V3", "V10"]) + def test_compare_unstructured_voids(self, dtype): + zeros = np.zeros(3, dtype=dtype) + + assert_array_equal(zeros, zeros) + assert not (zeros != zeros).any() + + if dtype == "V0": + # Can't test != of actually different data + return + + nonzeros = np.array([b"1", b"2", b"3"], dtype=dtype) + + assert not (zeros == nonzeros).any() + assert (zeros != nonzeros).all() + def assert_array_strict_equal(x, y): assert_array_equal(x, y) @@ -1324,16 +1757,22 @@ def assert_array_strict_equal(x, y): assert_(x.dtype.isnative == y.dtype.isnative) -class TestClip(object): +class TestClip: def setup(self): self.nr = 5 self.nc = 3 - def fastclip(self, a, m, M, out=None): + def fastclip(self, a, m, M, out=None, casting=None): if out is None: - return a.clip(m, M) + if casting is None: + return a.clip(m, M) + else: + return a.clip(m, M, casting=casting) else: - return a.clip(m, M, out) + if casting is None: + return a.clip(m, M, out) + else: + return a.clip(m, M, out, casting=casting) def clip(self, a, m, M, out=None): # use slow-clip @@ -1371,6 +1810,20 @@ def _generate_int32_data(self, n, m): return (10 * rand(n, m)).astype(np.int32) # Now the real test cases + + @pytest.mark.parametrize("dtype", '?bhilqpBHILQPefdgFDGO') + def test_ones_pathological(self, dtype): + # for preservation of behavior described in + # gh-12519; amin > amax behavior may still change + # in the future + arr = np.ones(10, dtype=dtype) + expected = np.zeros(10, dtype=dtype) + actual = np.clip(arr, 1, 0) + if dtype == 'O': + assert actual.tolist() == expected.tolist() + else: + assert_equal(actual, expected) + def test_simple_double(self): # Test native double input with scalar min/max. a = self._generate_data(self.nr, self.nc) @@ -1469,14 +1922,21 @@ def test_simple_out(self): self.clip(a, m, M, act) assert_array_strict_equal(ac, act) - def test_simple_int32_inout(self): + @pytest.mark.parametrize("casting", [None, "unsafe"]) + def test_simple_int32_inout(self, casting): # Test native int32 input with double min/max and int32 out. a = self._generate_int32_data(self.nr, self.nc) m = np.float64(0) M = np.float64(2) ac = np.zeros(a.shape, dtype=np.int32) act = ac.copy() - self.fastclip(a, m, M, ac) + if casting is None: + with assert_warns(DeprecationWarning): + # NumPy 1.17.0, 2018-02-24 - casting is unsafe + self.fastclip(a, m, M, ac, casting=casting) + else: + # explicitly passing "unsafe" will silence warning + self.fastclip(a, m, M, ac, casting=casting) self.clip(a, m, M, act) assert_array_strict_equal(ac, act) @@ -1498,7 +1958,9 @@ def test_simple_int64_inout(self): M = np.float64(1) ac = np.zeros(a.shape, dtype=np.int32) act = ac.copy() - self.fastclip(a, m, M, ac) + with assert_warns(DeprecationWarning): + # NumPy 1.17.0, 2018-02-24 - casting is unsafe + self.fastclip(a, m, M, ac) self.clip(a, m, M, act) assert_array_strict_equal(ac, act) @@ -1509,7 +1971,9 @@ def test_simple_int32_out(self): M = 2.0 ac = np.zeros(a.shape, dtype=np.int32) act = ac.copy() - self.fastclip(a, m, M, ac) + with assert_warns(DeprecationWarning): + # NumPy 1.17.0, 2018-02-24 - casting is unsafe + self.fastclip(a, m, M, ac) self.clip(a, m, M, act) assert_array_strict_equal(ac, act) @@ -1530,7 +1994,7 @@ def test_simple_inplace_02(self): m = -0.5 M = 0.6 self.fastclip(a, m, M, a) - self.clip(a, m, M, ac) + self.clip(ac, m, M, ac) assert_array_strict_equal(a, ac) def test_noncontig_inplace(self): @@ -1543,7 +2007,7 @@ def test_noncontig_inplace(self): m = -0.5 M = 0.6 self.fastclip(a, m, M, a) - self.clip(a, m, M, ac) + self.clip(ac, m, M, ac) assert_array_equal(a, ac) def test_type_cast_01(self): @@ -1685,7 +2149,9 @@ def test_clip_with_out_simple2(self): M = np.float64(2) ac = np.zeros(a.shape, dtype=np.int32) act = ac.copy() - self.fastclip(a, m, M, ac) + with assert_warns(DeprecationWarning): + # NumPy 1.17.0, 2018-02-24 - casting is unsafe + self.fastclip(a, m, M, ac) self.clip(a, m, M, act) assert_array_strict_equal(ac, act) @@ -1707,7 +2173,9 @@ def test_clip_with_out_array_int32(self): M = np.float64(1) ac = np.zeros(a.shape, dtype=np.int32) act = ac.copy() - self.fastclip(a, m, M, ac) + with assert_warns(DeprecationWarning): + # NumPy 1.17.0, 2018-02-24 - casting is unsafe + self.fastclip(a, m, M, ac) self.clip(a, m, M, act) assert_array_strict_equal(ac, act) @@ -1718,10 +2186,28 @@ def test_clip_with_out_array_outint32(self): M = 2.0 ac = np.zeros(a.shape, dtype=np.int32) act = ac.copy() - self.fastclip(a, m, M, ac) + with assert_warns(DeprecationWarning): + # NumPy 1.17.0, 2018-02-24 - casting is unsafe + self.fastclip(a, m, M, ac) self.clip(a, m, M, act) assert_array_strict_equal(ac, act) + def test_clip_with_out_transposed(self): + # Test that the out argument works when transposed + a = np.arange(16).reshape(4, 4) + out = np.empty_like(a).T + a.clip(4, 10, out=out) + expected = self.clip(a, 4, 10) + assert_array_equal(out, expected) + + def test_clip_with_out_memory_overlap(self): + # Test that the out argument works when it has memory overlap + a = np.arange(16).reshape(4, 4) + ac = a.copy() + a[:-1].clip(4, 10, out=a[1:]) + expected = self.clip(ac[:-1], 4, 10) + assert_array_equal(a[1:], expected) + def test_clip_inplace_array(self): # Test native double input with array min/max a = self._generate_data(self.nr, self.nc) @@ -1755,14 +2241,149 @@ def test_clip_func_takes_out(self): def test_clip_nan(self): d = np.arange(7.) - assert_equal(d.clip(min=np.nan), d) - assert_equal(d.clip(max=np.nan), d) - assert_equal(d.clip(min=np.nan, max=np.nan), d) - assert_equal(d.clip(min=-2, max=np.nan), d) - assert_equal(d.clip(min=np.nan, max=10), d) - - -class TestAllclose(object): + with assert_warns(DeprecationWarning): + assert_equal(d.clip(min=np.nan), d) + with assert_warns(DeprecationWarning): + assert_equal(d.clip(max=np.nan), d) + with assert_warns(DeprecationWarning): + assert_equal(d.clip(min=np.nan, max=np.nan), d) + with assert_warns(DeprecationWarning): + assert_equal(d.clip(min=-2, max=np.nan), d) + with assert_warns(DeprecationWarning): + assert_equal(d.clip(min=np.nan, max=10), d) + + def test_object_clip(self): + a = np.arange(10, dtype=object) + actual = np.clip(a, 1, 5) + expected = np.array([1, 1, 2, 3, 4, 5, 5, 5, 5, 5]) + assert actual.tolist() == expected.tolist() + + def test_clip_all_none(self): + a = np.arange(10, dtype=object) + with assert_raises_regex(ValueError, 'max or min'): + np.clip(a, None, None) + + def test_clip_invalid_casting(self): + a = np.arange(10, dtype=object) + with assert_raises_regex(ValueError, + 'casting must be one of'): + self.fastclip(a, 1, 8, casting="garbage") + + @pytest.mark.parametrize("amin, amax", [ + # two scalars + (1, 0), + # mix scalar and array + (1, np.zeros(10)), + # two arrays + (np.ones(10), np.zeros(10)), + ]) + def test_clip_value_min_max_flip(self, amin, amax): + a = np.arange(10, dtype=np.int64) + # requirement from ufunc_docstrings.py + expected = np.minimum(np.maximum(a, amin), amax) + actual = np.clip(a, amin, amax) + assert_equal(actual, expected) + + @pytest.mark.parametrize("arr, amin, amax, exp", [ + # for a bug in npy_ObjectClip, based on a + # case produced by hypothesis + (np.zeros(10, dtype=np.int64), + 0, + -2**64+1, + np.full(10, -2**64+1, dtype=object)), + # for bugs in NPY_TIMEDELTA_MAX, based on a case + # produced by hypothesis + (np.zeros(10, dtype='m8') - 1, + 0, + 0, + np.zeros(10, dtype='m8')), + ]) + def test_clip_problem_cases(self, arr, amin, amax, exp): + actual = np.clip(arr, amin, amax) + assert_equal(actual, exp) + + @pytest.mark.xfail(reason="no scalar nan propagation yet", + raises=AssertionError, + strict=True) + @pytest.mark.parametrize("arr, amin, amax", [ + # problematic scalar nan case from hypothesis + (np.zeros(10, dtype=np.int64), + np.array(np.nan), + np.zeros(10, dtype=np.int32)), + ]) + @pytest.mark.filterwarnings("ignore::DeprecationWarning") + def test_clip_scalar_nan_propagation(self, arr, amin, amax): + # enforcement of scalar nan propagation for comparisons + # called through clip() + expected = np.minimum(np.maximum(arr, amin), amax) + actual = np.clip(arr, amin, amax) + assert_equal(actual, expected) + + @pytest.mark.xfail(reason="propagation doesn't match spec") + @pytest.mark.parametrize("arr, amin, amax", [ + (np.array([1] * 10, dtype='m8'), + np.timedelta64('NaT'), + np.zeros(10, dtype=np.int32)), + ]) + @pytest.mark.filterwarnings("ignore::DeprecationWarning") + def test_NaT_propagation(self, arr, amin, amax): + # NOTE: the expected function spec doesn't + # propagate NaT, but clip() now does + expected = np.minimum(np.maximum(arr, amin), amax) + actual = np.clip(arr, amin, amax) + assert_equal(actual, expected) + + @given( + data=st.data(), + arr=hynp.arrays( + dtype=hynp.integer_dtypes() | hynp.floating_dtypes(), + shape=hynp.array_shapes() + ) + ) + def test_clip_property(self, data, arr): + """A property-based test using Hypothesis. + + This aims for maximum generality: it could in principle generate *any* + valid inputs to np.clip, and in practice generates much more varied + inputs than human testers come up with. + + Because many of the inputs have tricky dependencies - compatible dtypes + and mutually-broadcastable shapes - we use `st.data()` strategy draw + values *inside* the test function, from strategies we construct based + on previous values. An alternative would be to define a custom strategy + with `@st.composite`, but until we have duplicated code inline is fine. + + That accounts for most of the function; the actual test is just three + lines to calculate and compare actual vs expected results! + """ + numeric_dtypes = hynp.integer_dtypes() | hynp.floating_dtypes() + # Generate shapes for the bounds which can be broadcast with each other + # and with the base shape. Below, we might decide to use scalar bounds, + # but it's clearer to generate these shapes unconditionally in advance. + in_shapes, result_shape = data.draw( + hynp.mutually_broadcastable_shapes( + num_shapes=2, base_shape=arr.shape + ) + ) + # Scalar `nan` is deprecated due to the differing behaviour it shows. + s = numeric_dtypes.flatmap( + lambda x: hynp.from_dtype(x, allow_nan=False)) + amin = data.draw(s | hynp.arrays(dtype=numeric_dtypes, + shape=in_shapes[0], elements={"allow_nan": False})) + amax = data.draw(s | hynp.arrays(dtype=numeric_dtypes, + shape=in_shapes[1], elements={"allow_nan": False})) + + # Then calculate our result and expected result and check that they're + # equal! See gh-12519 and gh-19457 for discussion deciding on this + # property and the result_type argument. + result = np.clip(arr, amin, amax) + t = np.result_type(arr, amin, amax) + expected = np.minimum(amax, np.maximum(arr, amin, dtype=t), dtype=t) + assert result.dtype == t + assert_array_equal(result, expected) + + +class TestAllclose: rtol = 1e-5 atol = 1e-8 @@ -1847,7 +2468,7 @@ def __new__(cls, *args, **kwargs): assert_(type(np.allclose(a, a)) is bool) -class TestIsclose(object): +class TestIsclose: rtol = 1e-5 atol = 1e-8 @@ -1984,8 +2605,17 @@ def test_non_finite_scalar(self): assert_(np.isclose(0, np.inf) is np.False_) assert_(type(np.isclose(0, np.inf)) is np.bool_) + def test_timedelta(self): + # Allclose currently works for timedelta64 as long as `atol` is + # an integer or also a timedelta64 + a = np.array([[1, 2, 3, "NaT"]], dtype="m8[ns]") + assert np.isclose(a, a, atol=0, equal_nan=True).all() + assert np.isclose(a, a, atol=np.timedelta64(1, "ns"), equal_nan=True).all() + assert np.allclose(a, a, atol=0, equal_nan=True) + assert np.allclose(a, a, atol=np.timedelta64(1, "ns"), equal_nan=True) + -class TestStdVar(object): +class TestStdVar: def setup(self): self.A = np.array([1, -1, 1, -1]) self.real_var = 1 @@ -2000,15 +2630,15 @@ def test_scalars(self): def test_ddof1(self): assert_almost_equal(np.var(self.A, ddof=1), - self.real_var*len(self.A)/float(len(self.A)-1)) + self.real_var * len(self.A) / (len(self.A) - 1)) assert_almost_equal(np.std(self.A, ddof=1)**2, - self.real_var*len(self.A)/float(len(self.A)-1)) + self.real_var*len(self.A) / (len(self.A) - 1)) def test_ddof2(self): assert_almost_equal(np.var(self.A, ddof=2), - self.real_var*len(self.A)/float(len(self.A)-2)) + self.real_var * len(self.A) / (len(self.A) - 2)) assert_almost_equal(np.std(self.A, ddof=2)**2, - self.real_var*len(self.A)/float(len(self.A)-2)) + self.real_var * len(self.A) / (len(self.A) - 2)) def test_out_scalar(self): d = np.arange(10) @@ -2024,7 +2654,7 @@ def test_out_scalar(self): assert_array_equal(r, out) -class TestStdVarComplex(object): +class TestStdVarComplex: def test_basic(self): A = np.array([1, 1.j, -1, -1.j]) real_var = 1 @@ -2036,7 +2666,7 @@ def test_scalars(self): assert_equal(np.std(1j), 0) -class TestCreationFuncs(object): +class TestCreationFuncs: # Test ones, zeros, empty and full. def setup(self): @@ -2083,7 +2713,7 @@ def test_zeros(self): self.check_function(np.zeros) def test_ones(self): - self.check_function(np.zeros) + self.check_function(np.ones) def test_empty(self): self.check_function(np.empty) @@ -2107,7 +2737,7 @@ def test_for_reference_leak(self): assert_(sys.getrefcount(dim) == beg) -class TestLikeFuncs(object): +class TestLikeFuncs: '''Test ones_like, zeros_like, empty_like and full_like''' def setup(self): @@ -2134,6 +2764,7 @@ def setup(self): (np.arange(24).reshape(2, 3, 4).swapaxes(0, 1), None), (np.arange(24).reshape(4, 3, 2).swapaxes(0, 1), '?'), ] + self.shapes = [(), (5,), (5,6,), (5,6,7,)] def compare_array_value(self, dz, value, fill_value): if value is not None: @@ -2199,6 +2830,34 @@ def check_like_function(self, like_function, value, fill_value=False): assert_equal(dz.dtype, np.dtype(dtype)) self.compare_array_value(dz, value, fill_value) + # Test the 'shape' parameter + for s in self.shapes: + for o in 'CFA': + sz = like_function(d, dtype=dtype, shape=s, order=o, + **fill_kwarg) + assert_equal(sz.shape, s) + if dtype is None: + assert_equal(sz.dtype, d.dtype) + else: + assert_equal(sz.dtype, np.dtype(dtype)) + if o == 'C' or (o == 'A' and d.flags.c_contiguous): + assert_(sz.flags.c_contiguous) + elif o == 'F' or (o == 'A' and d.flags.f_contiguous): + assert_(sz.flags.f_contiguous) + self.compare_array_value(sz, value, fill_value) + + if (d.ndim != len(s)): + assert_equal(np.argsort(like_function(d, dtype=dtype, + shape=s, order='K', + **fill_kwarg).strides), + np.argsort(np.empty(s, dtype=dtype, + order='C').strides)) + else: + assert_equal(np.argsort(like_function(d, dtype=dtype, + shape=s, order='K', + **fill_kwarg).strides), + np.argsort(d.strides)) + # Test the 'subok' parameter class MyNDArray(np.ndarray): pass @@ -2227,16 +2886,31 @@ def test_filled_like(self): self.check_like_function(np.full_like, 123.456, True) self.check_like_function(np.full_like, np.inf, True) + @pytest.mark.parametrize('likefunc', [np.empty_like, np.full_like, + np.zeros_like, np.ones_like]) + @pytest.mark.parametrize('dtype', [str, bytes]) + def test_dtype_str_bytes(self, likefunc, dtype): + # Regression test for gh-19860 + a = np.arange(16).reshape(2, 8) + b = a[:, ::2] # Ensure b is not contiguous. + kwargs = {'fill_value': ''} if likefunc == np.full_like else {} + result = likefunc(b, dtype=dtype, **kwargs) + if dtype == str: + assert result.strides == (16, 4) + else: + # dtype is bytes + assert result.strides == (4, 1) -class TestCorrelate(object): + +class TestCorrelate: def _setup(self, dt): self.x = np.array([1, 2, 3, 4, 5], dtype=dt) self.xs = np.arange(1, 20)[::3] self.y = np.array([-1, -2, -3], dtype=dt) - self.z1 = np.array([ -3., -8., -14., -20., -26., -14., -5.], dtype=dt) + self.z1 = np.array([-3., -8., -14., -20., -26., -14., -5.], dtype=dt) self.z1_4 = np.array([-2., -5., -8., -11., -14., -5.], dtype=dt) - self.z1r = np.array([-15., -22., -22., -16., -10., -4., -1.], dtype=dt) - self.z2 = np.array([-5., -14., -26., -20., -14., -8., -3.], dtype=dt) + self.z1r = np.array([-15., -22., -22., -16., -10., -4., -1.], dtype=dt) + self.z2 = np.array([-5., -14., -26., -20., -14., -8., -3.], dtype=dt) self.z2r = np.array([-1., -4., -10., -16., -22., -22., -15.], dtype=dt) self.zs = np.array([-3., -14., -30., -48., -66., -84., -102., -54., -19.], dtype=dt) @@ -2278,8 +2952,29 @@ def test_complex(self): z = np.correlate(y, x, mode='full') assert_array_almost_equal(z, r_z) + def test_zero_size(self): + with pytest.raises(ValueError): + np.correlate(np.array([]), np.ones(1000), mode='full') + with pytest.raises(ValueError): + np.correlate(np.ones(1000), np.array([]), mode='full') -class TestConvolve(object): + def test_mode(self): + d = np.ones(100) + k = np.ones(3) + default_mode = np.correlate(d, k, mode='valid') + with assert_warns(DeprecationWarning): + valid_mode = np.correlate(d, k, mode='v') + assert_array_equal(valid_mode, default_mode) + # integer mode + with assert_raises(ValueError): + np.correlate(d, k, mode=-1) + assert_array_equal(np.correlate(d, k, mode=0), valid_mode) + # illegal arguments + with assert_raises(TypeError): + np.correlate(d, k, mode=None) + + +class TestConvolve: def test_object(self): d = [1.] * 100 k = [1.] * 3 @@ -2292,8 +2987,47 @@ def test_no_overwrite(self): assert_array_equal(d, np.ones(100)) assert_array_equal(k, np.ones(3)) + def test_mode(self): + d = np.ones(100) + k = np.ones(3) + default_mode = np.convolve(d, k, mode='full') + with assert_warns(DeprecationWarning): + full_mode = np.convolve(d, k, mode='f') + assert_array_equal(full_mode, default_mode) + # integer mode + with assert_raises(ValueError): + np.convolve(d, k, mode=-1) + assert_array_equal(np.convolve(d, k, mode=2), full_mode) + # illegal arguments + with assert_raises(TypeError): + np.convolve(d, k, mode=None) + + +class TestArgwhere: + + @pytest.mark.parametrize('nd', [0, 1, 2]) + def test_nd(self, nd): + # get an nd array with multiple elements in every dimension + x = np.empty((2,)*nd, bool) + + # none + x[...] = False + assert_equal(np.argwhere(x).shape, (0, nd)) + + # only one + x[...] = False + x.flat[0] = True + assert_equal(np.argwhere(x).shape, (1, nd)) + + # all but one + x[...] = True + x.flat[0] = False + assert_equal(np.argwhere(x).shape, (x.size - 1, nd)) + + # all + x[...] = True + assert_equal(np.argwhere(x).shape, (x.size, nd)) -class TestArgwhere(object): def test_2D(self): x = np.arange(6).reshape((2, 3)) assert_array_equal(np.argwhere(x > 1), @@ -2306,7 +3040,7 @@ def test_list(self): assert_equal(np.argwhere([4, 0, 2, 1, 3]), [[0], [2], [3], [4]]) -class TestStringFunction(object): +class TestStringFunction: def test_set_string_function(self): a = np.array([1]) @@ -2321,7 +3055,7 @@ def test_set_string_function(self): assert_equal(str(a), "[1]") -class TestRoll(object): +class TestRoll: def test_roll1d(self): x = np.arange(10) xr = np.roll(x, 2) @@ -2379,7 +3113,7 @@ def test_roll_empty(self): assert_equal(np.roll(x, 1), np.array([])) -class TestRollaxis(object): +class TestRollaxis: # expected shape indexed by (axis, start) for array of # shape (1, 2, 3, 4) @@ -2441,7 +3175,7 @@ def test_results(self): assert_(not res.flags['OWNDATA']) -class TestMoveaxis(object): +class TestMoveaxis: def test_move_to_end(self): x = np.random.randn(5, 6, 7) for source, expected in [(0, (6, 7, 5)), @@ -2515,7 +3249,7 @@ def test_array_likes(self): assert_(isinstance(result, np.ndarray)) -class TestCross(object): +class TestCross: def test_2x2(self): u = [1, 2] v = [3, 4] @@ -2604,7 +3338,48 @@ def test_outer_out_param(): assert_equal(np.outer(arr2, arr3, out2), out2) -class TestRequire(object): +class TestIndices: + + def test_simple(self): + [x, y] = np.indices((4, 3)) + assert_array_equal(x, np.array([[0, 0, 0], + [1, 1, 1], + [2, 2, 2], + [3, 3, 3]])) + assert_array_equal(y, np.array([[0, 1, 2], + [0, 1, 2], + [0, 1, 2], + [0, 1, 2]])) + + def test_single_input(self): + [x] = np.indices((4,)) + assert_array_equal(x, np.array([0, 1, 2, 3])) + + [x] = np.indices((4,), sparse=True) + assert_array_equal(x, np.array([0, 1, 2, 3])) + + def test_scalar_input(self): + assert_array_equal([], np.indices(())) + assert_array_equal([], np.indices((), sparse=True)) + assert_array_equal([[]], np.indices((0,))) + assert_array_equal([[]], np.indices((0,), sparse=True)) + + def test_sparse(self): + [x, y] = np.indices((4,3), sparse=True) + assert_array_equal(x, np.array([[0], [1], [2], [3]])) + assert_array_equal(y, np.array([[0, 1, 2]])) + + @pytest.mark.parametrize("dtype", [np.int32, np.int64, np.float32, np.float64]) + @pytest.mark.parametrize("dims", [(), (0,), (4, 3)]) + def test_return_type(self, dtype, dims): + inds = np.indices(dims, dtype=dtype) + assert_(inds.dtype == dtype) + + for arr in np.indices(dims, dtype=dtype, sparse=True): + assert_(arr.dtype == dtype) + + +class TestRequire: flag_names = ['C', 'C_CONTIGUOUS', 'CONTIGUOUS', 'F', 'F_CONTIGUOUS', 'FORTRAN', 'A', 'ALIGNED', @@ -2678,12 +3453,14 @@ class ArraySubclass(np.ndarray): self.set_and_check_flag(flag, None, a) -class TestBroadcast(object): +class TestBroadcast: def test_broadcast_in_args(self): # gh-5881 arrs = [np.empty((6, 7)), np.empty((5, 6, 1)), np.empty((7,)), np.empty((5, 1, 7))] mits = [np.broadcast(*arrs), + np.broadcast(np.broadcast(*arrs[:0]), np.broadcast(*arrs[0:])), + np.broadcast(np.broadcast(*arrs[:1]), np.broadcast(*arrs[1:])), np.broadcast(np.broadcast(*arrs[:2]), np.broadcast(*arrs[2:])), np.broadcast(arrs[0], np.broadcast(*arrs[1:-1]), arrs[-1])] for mit in mits: @@ -2708,14 +3485,32 @@ def test_number_of_arguments(self): arr = np.empty((5,)) for j in range(35): arrs = [arr] * j - if j < 1 or j > 32: + if j > 32: assert_raises(ValueError, np.broadcast, *arrs) else: mit = np.broadcast(*arrs) assert_equal(mit.numiter, j) + def test_broadcast_error_kwargs(self): + #gh-13455 + arrs = [np.empty((5, 6, 7))] + mit = np.broadcast(*arrs) + mit2 = np.broadcast(*arrs, **{}) + assert_equal(mit.shape, mit2.shape) + assert_equal(mit.ndim, mit2.ndim) + assert_equal(mit.nd, mit2.nd) + assert_equal(mit.numiter, mit2.numiter) + assert_(mit.iters[0].base is mit2.iters[0].base) + + assert_raises(ValueError, np.broadcast, 1, **{'x': 1}) -class TestKeepdims(object): + def test_shape_mismatch_error_message(self): + with pytest.raises(ValueError, match=r"arg 0 with shape \(1, 3\) and " + r"arg 2 with shape \(2,\)"): + np.broadcast([[1, 2, 3]], [[4], [5]], [6, 7]) + + +class TestKeepdims: class sub_array(np.ndarray): def sum(self, axis=None, dtype=None, out=None): @@ -2727,7 +3522,7 @@ def test_raise(self): assert_raises(TypeError, np.sum, x, keepdims=True) -class TestTensordot(object): +class TestTensordot: def test_zero_dimension(self): # Test resolution to issue #5663 @@ -2736,3 +3531,9 @@ def test_zero_dimension(self): td = np.tensordot(a, b, (1, 0)) assert_array_equal(td, np.dot(a, b)) assert_array_equal(td, np.einsum('ij,jk', a, b)) + + def test_zero_dimensional(self): + # gh-12130 + arr_0d = np.array(1) + ret = np.tensordot(arr_0d, arr_0d, ([], [])) # contracting no axes is well defined + assert_array_equal(ret, arr_0d) diff --git a/numpy/core/tests/test_numerictypes.py b/numpy/core/tests/test_numerictypes.py index cdf1b0490fec..9cb00342dd0c 100644 --- a/numpy/core/tests/test_numerictypes.py +++ b/numpy/core/tests/test_numerictypes.py @@ -1,10 +1,9 @@ -from __future__ import division, absolute_import, print_function - import sys import itertools +import pytest import numpy as np -from numpy.testing import assert_, assert_equal, assert_raises +from numpy.testing import assert_, assert_equal, assert_raises, IS_PYPY # This is the structure of the table used for plain objects: # @@ -86,10 +85,8 @@ def normalize_descr(descr): else: nitem = (item[0], dtype) out.append(nitem) - elif isinstance(item[1], list): - l = [] - for j in normalize_descr(item[1]): - l.append(j) + elif isinstance(dtype, list): + l = normalize_descr(dtype) out.append((item[0], l)) else: raise ValueError("Expected a str or list and got %s" % @@ -101,7 +98,7 @@ def normalize_descr(descr): # Creation tests ############################################################ -class CreateZeros(object): +class CreateZeros: """Check the creation of heterogeneous arrays zero-valued""" def test_zeros0D(self): @@ -144,7 +141,7 @@ class TestCreateZerosNested(CreateZeros): _descr = Ndescr -class CreateValues(object): +class CreateValues: """Check the creation of heterogeneous arrays with values""" def test_tuple(self): @@ -204,7 +201,7 @@ class TestCreateValuesNestedMultiple(CreateValues): # Reading tests ############################################################ -class ReadValuesPlain(object): +class ReadValuesPlain: """Check the reading of values in heterogeneous arrays (plain)""" def test_access_fields(self): @@ -236,7 +233,7 @@ class TestReadValuesPlainMultiple(ReadValuesPlain): multiple_rows = 1 _buffer = PbufferT -class ReadValuesNested(object): +class ReadValuesNested: """Check the reading of values in heterogeneous arrays (nested)""" def test_access_top_fields(self): @@ -309,10 +306,7 @@ def test_nested1_descriptor(self): h = np.array(self._buffer, dtype=self._descr) assert_(h.dtype['Info']['value'].name == 'complex128') assert_(h.dtype['Info']['y2'].name == 'float64') - if sys.version_info[0] >= 3: - assert_(h.dtype['info']['Name'].name == 'str256') - else: - assert_(h.dtype['info']['Name'].name == 'unicode256') + assert_(h.dtype['info']['Name'].name == 'str256') assert_(h.dtype['info']['Value'].name == 'complex128') def test_nested2_descriptor(self): @@ -334,14 +328,14 @@ class TestReadValuesNestedMultiple(ReadValuesNested): multiple_rows = True _buffer = NbufferT -class TestEmptyField(object): +class TestEmptyField: def test_assign(self): a = np.arange(10, dtype=np.float32) a.dtype = [("int", "<0i4"), ("float", "<2f4")] assert_(a['int'].shape == (5, 0)) assert_(a['float'].shape == (5, 2)) -class TestCommonType(object): +class TestCommonType: def test_scalar_loses1(self): res = np.find_common_type(['f4', 'f4', 'i2'], ['f8']) assert_(res == 'f4') @@ -362,7 +356,7 @@ def test_scalar_wins3(self): # doesn't go up to 'f16' on purpose res = np.find_common_type(['u8', 'i8', 'i8'], ['f8']) assert_(res == 'f8') -class TestMultipleFields(object): +class TestMultipleFields: def setup(self): self.ary = np.array([(1, 2, 3, 4), (5, 6, 7, 8)], dtype='i4,f4,i2,c8') @@ -377,7 +371,7 @@ def test_return(self): assert_(res == [(1, 3), (5, 7)]) -class TestIsSubDType(object): +class TestIsSubDType: # scalar types can be promoted into dtypes wrappers = [np.dtype, lambda x: x] @@ -406,3 +400,155 @@ def test_sibling_class(self): for w1, w2 in itertools.product(self.wrappers, repeat=2): assert_(not np.issubdtype(w1(np.float32), w2(np.float64))) assert_(not np.issubdtype(w1(np.float64), w2(np.float32))) + + def test_nondtype_nonscalartype(self): + # See gh-14619 and gh-9505 which introduced the deprecation to fix + # this. These tests are directly taken from gh-9505 + assert not np.issubdtype(np.float32, 'float64') + assert not np.issubdtype(np.float32, 'f8') + assert not np.issubdtype(np.int32, str) + assert not np.issubdtype(np.int32, 'int64') + assert not np.issubdtype(np.str_, 'void') + # for the following the correct spellings are + # np.integer, np.floating, or np.complexfloating respectively: + assert not np.issubdtype(np.int8, int) # np.int8 is never np.int_ + assert not np.issubdtype(np.float32, float) + assert not np.issubdtype(np.complex64, complex) + assert not np.issubdtype(np.float32, "float") + assert not np.issubdtype(np.float64, "f") + + # Test the same for the correct first datatype and abstract one + # in the case of int, float, complex: + assert np.issubdtype(np.float64, 'float64') + assert np.issubdtype(np.float64, 'f8') + assert np.issubdtype(np.str_, str) + assert np.issubdtype(np.int64, 'int64') + assert np.issubdtype(np.void, 'void') + assert np.issubdtype(np.int8, np.integer) + assert np.issubdtype(np.float32, np.floating) + assert np.issubdtype(np.complex64, np.complexfloating) + assert np.issubdtype(np.float64, "float") + assert np.issubdtype(np.float32, "f") + + +class TestSctypeDict: + def test_longdouble(self): + assert_(np.sctypeDict['f8'] is not np.longdouble) + assert_(np.sctypeDict['c16'] is not np.clongdouble) + + +class TestBitName: + def test_abstract(self): + assert_raises(ValueError, np.core.numerictypes.bitname, np.floating) + + +class TestMaximumSctype: + + # note that parametrizing with sctype['int'] and similar would skip types + # with the same size (gh-11923) + + @pytest.mark.parametrize('t', [np.byte, np.short, np.intc, np.int_, np.longlong]) + def test_int(self, t): + assert_equal(np.maximum_sctype(t), np.sctypes['int'][-1]) + + @pytest.mark.parametrize('t', [np.ubyte, np.ushort, np.uintc, np.uint, np.ulonglong]) + def test_uint(self, t): + assert_equal(np.maximum_sctype(t), np.sctypes['uint'][-1]) + + @pytest.mark.parametrize('t', [np.half, np.single, np.double, np.longdouble]) + def test_float(self, t): + assert_equal(np.maximum_sctype(t), np.sctypes['float'][-1]) + + @pytest.mark.parametrize('t', [np.csingle, np.cdouble, np.clongdouble]) + def test_complex(self, t): + assert_equal(np.maximum_sctype(t), np.sctypes['complex'][-1]) + + @pytest.mark.parametrize('t', [np.bool_, np.object_, np.unicode_, np.bytes_, np.void]) + def test_other(self, t): + assert_equal(np.maximum_sctype(t), t) + + +class Test_sctype2char: + # This function is old enough that we're really just documenting the quirks + # at this point. + + def test_scalar_type(self): + assert_equal(np.sctype2char(np.double), 'd') + assert_equal(np.sctype2char(np.int_), 'l') + assert_equal(np.sctype2char(np.unicode_), 'U') + assert_equal(np.sctype2char(np.bytes_), 'S') + + def test_other_type(self): + assert_equal(np.sctype2char(float), 'd') + assert_equal(np.sctype2char(list), 'O') + assert_equal(np.sctype2char(np.ndarray), 'O') + + def test_third_party_scalar_type(self): + from numpy.core._rational_tests import rational + assert_raises(KeyError, np.sctype2char, rational) + assert_raises(KeyError, np.sctype2char, rational(1)) + + def test_array_instance(self): + assert_equal(np.sctype2char(np.array([1.0, 2.0])), 'd') + + def test_abstract_type(self): + assert_raises(KeyError, np.sctype2char, np.floating) + + def test_non_type(self): + assert_raises(ValueError, np.sctype2char, 1) + +@pytest.mark.parametrize("rep, expected", [ + (np.int32, True), + (list, False), + (1.1, False), + (str, True), + (np.dtype(np.float64), True), + (np.dtype((np.int16, (3, 4))), True), + (np.dtype([('a', np.int8)]), True), + ]) +def test_issctype(rep, expected): + # ensure proper identification of scalar + # data-types by issctype() + actual = np.issctype(rep) + assert_equal(actual, expected) + + +@pytest.mark.skipif(sys.flags.optimize > 1, + reason="no docstrings present to inspect when PYTHONOPTIMIZE/Py_OptimizeFlag > 1") +@pytest.mark.xfail(IS_PYPY, + reason="PyPy cannot modify tp_doc after PyType_Ready") +class TestDocStrings: + def test_platform_dependent_aliases(self): + if np.int64 is np.int_: + assert_('int64' in np.int_.__doc__) + elif np.int64 is np.longlong: + assert_('int64' in np.longlong.__doc__) + + +class TestScalarTypeNames: + # gh-9799 + + numeric_types = [ + np.byte, np.short, np.intc, np.int_, np.longlong, + np.ubyte, np.ushort, np.uintc, np.uint, np.ulonglong, + np.half, np.single, np.double, np.longdouble, + np.csingle, np.cdouble, np.clongdouble, + ] + + def test_names_are_unique(self): + # none of the above may be aliases for each other + assert len(set(self.numeric_types)) == len(self.numeric_types) + + # names must be unique + names = [t.__name__ for t in self.numeric_types] + assert len(set(names)) == len(names) + + @pytest.mark.parametrize('t', numeric_types) + def test_names_reflect_attributes(self, t): + """ Test that names correspond to where the type is under ``np.`` """ + assert getattr(np, t.__name__) is t + + @pytest.mark.parametrize('t', numeric_types) + def test_names_are_undersood_by_dtype(self, t): + """ Test the dtype constructor maps names back to the type """ + assert np.dtype(t.__name__).type is t diff --git a/numpy/core/tests/test_overrides.py b/numpy/core/tests/test_overrides.py new file mode 100644 index 000000000000..9216a3f5fdfa --- /dev/null +++ b/numpy/core/tests/test_overrides.py @@ -0,0 +1,584 @@ +import inspect +import sys +import os +import tempfile +from io import StringIO +from unittest import mock + +import numpy as np +from numpy.testing import ( + assert_, assert_equal, assert_raises, assert_raises_regex) +from numpy.core.overrides import ( + _get_implementing_args, array_function_dispatch, + verify_matching_signatures, ARRAY_FUNCTION_ENABLED) +from numpy.compat import pickle +import pytest + + +requires_array_function = pytest.mark.skipif( + not ARRAY_FUNCTION_ENABLED, + reason="__array_function__ dispatch not enabled.") + + +def _return_not_implemented(self, *args, **kwargs): + return NotImplemented + + +# need to define this at the top level to test pickling +@array_function_dispatch(lambda array: (array,)) +def dispatched_one_arg(array): + """Docstring.""" + return 'original' + + +@array_function_dispatch(lambda array1, array2: (array1, array2)) +def dispatched_two_arg(array1, array2): + """Docstring.""" + return 'original' + + +class TestGetImplementingArgs: + + def test_ndarray(self): + array = np.array(1) + + args = _get_implementing_args([array]) + assert_equal(list(args), [array]) + + args = _get_implementing_args([array, array]) + assert_equal(list(args), [array]) + + args = _get_implementing_args([array, 1]) + assert_equal(list(args), [array]) + + args = _get_implementing_args([1, array]) + assert_equal(list(args), [array]) + + def test_ndarray_subclasses(self): + + class OverrideSub(np.ndarray): + __array_function__ = _return_not_implemented + + class NoOverrideSub(np.ndarray): + pass + + array = np.array(1).view(np.ndarray) + override_sub = np.array(1).view(OverrideSub) + no_override_sub = np.array(1).view(NoOverrideSub) + + args = _get_implementing_args([array, override_sub]) + assert_equal(list(args), [override_sub, array]) + + args = _get_implementing_args([array, no_override_sub]) + assert_equal(list(args), [no_override_sub, array]) + + args = _get_implementing_args( + [override_sub, no_override_sub]) + assert_equal(list(args), [override_sub, no_override_sub]) + + def test_ndarray_and_duck_array(self): + + class Other: + __array_function__ = _return_not_implemented + + array = np.array(1) + other = Other() + + args = _get_implementing_args([other, array]) + assert_equal(list(args), [other, array]) + + args = _get_implementing_args([array, other]) + assert_equal(list(args), [array, other]) + + def test_ndarray_subclass_and_duck_array(self): + + class OverrideSub(np.ndarray): + __array_function__ = _return_not_implemented + + class Other: + __array_function__ = _return_not_implemented + + array = np.array(1) + subarray = np.array(1).view(OverrideSub) + other = Other() + + assert_equal(_get_implementing_args([array, subarray, other]), + [subarray, array, other]) + assert_equal(_get_implementing_args([array, other, subarray]), + [subarray, array, other]) + + def test_many_duck_arrays(self): + + class A: + __array_function__ = _return_not_implemented + + class B(A): + __array_function__ = _return_not_implemented + + class C(A): + __array_function__ = _return_not_implemented + + class D: + __array_function__ = _return_not_implemented + + a = A() + b = B() + c = C() + d = D() + + assert_equal(_get_implementing_args([1]), []) + assert_equal(_get_implementing_args([a]), [a]) + assert_equal(_get_implementing_args([a, 1]), [a]) + assert_equal(_get_implementing_args([a, a, a]), [a]) + assert_equal(_get_implementing_args([a, d, a]), [a, d]) + assert_equal(_get_implementing_args([a, b]), [b, a]) + assert_equal(_get_implementing_args([b, a]), [b, a]) + assert_equal(_get_implementing_args([a, b, c]), [b, c, a]) + assert_equal(_get_implementing_args([a, c, b]), [c, b, a]) + + def test_too_many_duck_arrays(self): + namespace = dict(__array_function__=_return_not_implemented) + types = [type('A' + str(i), (object,), namespace) for i in range(33)] + relevant_args = [t() for t in types] + + actual = _get_implementing_args(relevant_args[:32]) + assert_equal(actual, relevant_args[:32]) + + with assert_raises_regex(TypeError, 'distinct argument types'): + _get_implementing_args(relevant_args) + + +class TestNDArrayArrayFunction: + + @requires_array_function + def test_method(self): + + class Other: + __array_function__ = _return_not_implemented + + class NoOverrideSub(np.ndarray): + pass + + class OverrideSub(np.ndarray): + __array_function__ = _return_not_implemented + + array = np.array([1]) + other = Other() + no_override_sub = array.view(NoOverrideSub) + override_sub = array.view(OverrideSub) + + result = array.__array_function__(func=dispatched_two_arg, + types=(np.ndarray,), + args=(array, 1.), kwargs={}) + assert_equal(result, 'original') + + result = array.__array_function__(func=dispatched_two_arg, + types=(np.ndarray, Other), + args=(array, other), kwargs={}) + assert_(result is NotImplemented) + + result = array.__array_function__(func=dispatched_two_arg, + types=(np.ndarray, NoOverrideSub), + args=(array, no_override_sub), + kwargs={}) + assert_equal(result, 'original') + + result = array.__array_function__(func=dispatched_two_arg, + types=(np.ndarray, OverrideSub), + args=(array, override_sub), + kwargs={}) + assert_equal(result, 'original') + + with assert_raises_regex(TypeError, 'no implementation found'): + np.concatenate((array, other)) + + expected = np.concatenate((array, array)) + result = np.concatenate((array, no_override_sub)) + assert_equal(result, expected.view(NoOverrideSub)) + result = np.concatenate((array, override_sub)) + assert_equal(result, expected.view(OverrideSub)) + + def test_no_wrapper(self): + # This shouldn't happen unless a user intentionally calls + # __array_function__ with invalid arguments, but check that we raise + # an appropriate error all the same. + array = np.array(1) + func = lambda x: x + with assert_raises_regex(AttributeError, '_implementation'): + array.__array_function__(func=func, types=(np.ndarray,), + args=(array,), kwargs={}) + + +@requires_array_function +class TestArrayFunctionDispatch: + + def test_pickle(self): + for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): + roundtripped = pickle.loads( + pickle.dumps(dispatched_one_arg, protocol=proto)) + assert_(roundtripped is dispatched_one_arg) + + def test_name_and_docstring(self): + assert_equal(dispatched_one_arg.__name__, 'dispatched_one_arg') + if sys.flags.optimize < 2: + assert_equal(dispatched_one_arg.__doc__, 'Docstring.') + + def test_interface(self): + + class MyArray: + def __array_function__(self, func, types, args, kwargs): + return (self, func, types, args, kwargs) + + original = MyArray() + (obj, func, types, args, kwargs) = dispatched_one_arg(original) + assert_(obj is original) + assert_(func is dispatched_one_arg) + assert_equal(set(types), {MyArray}) + # assert_equal uses the overloaded np.iscomplexobj() internally + assert_(args == (original,)) + assert_equal(kwargs, {}) + + def test_not_implemented(self): + + class MyArray: + def __array_function__(self, func, types, args, kwargs): + return NotImplemented + + array = MyArray() + with assert_raises_regex(TypeError, 'no implementation found'): + dispatched_one_arg(array) + + +@requires_array_function +class TestVerifyMatchingSignatures: + + def test_verify_matching_signatures(self): + + verify_matching_signatures(lambda x: 0, lambda x: 0) + verify_matching_signatures(lambda x=None: 0, lambda x=None: 0) + verify_matching_signatures(lambda x=1: 0, lambda x=None: 0) + + with assert_raises(RuntimeError): + verify_matching_signatures(lambda a: 0, lambda b: 0) + with assert_raises(RuntimeError): + verify_matching_signatures(lambda x: 0, lambda x=None: 0) + with assert_raises(RuntimeError): + verify_matching_signatures(lambda x=None: 0, lambda y=None: 0) + with assert_raises(RuntimeError): + verify_matching_signatures(lambda x=1: 0, lambda y=1: 0) + + def test_array_function_dispatch(self): + + with assert_raises(RuntimeError): + @array_function_dispatch(lambda x: (x,)) + def f(y): + pass + + # should not raise + @array_function_dispatch(lambda x: (x,), verify=False) + def f(y): + pass + + +def _new_duck_type_and_implements(): + """Create a duck array type and implements functions.""" + HANDLED_FUNCTIONS = {} + + class MyArray: + def __array_function__(self, func, types, args, kwargs): + if func not in HANDLED_FUNCTIONS: + return NotImplemented + if not all(issubclass(t, MyArray) for t in types): + return NotImplemented + return HANDLED_FUNCTIONS[func](*args, **kwargs) + + def implements(numpy_function): + """Register an __array_function__ implementations.""" + def decorator(func): + HANDLED_FUNCTIONS[numpy_function] = func + return func + return decorator + + return (MyArray, implements) + + +@requires_array_function +class TestArrayFunctionImplementation: + + def test_one_arg(self): + MyArray, implements = _new_duck_type_and_implements() + + @implements(dispatched_one_arg) + def _(array): + return 'myarray' + + assert_equal(dispatched_one_arg(1), 'original') + assert_equal(dispatched_one_arg(MyArray()), 'myarray') + + def test_optional_args(self): + MyArray, implements = _new_duck_type_and_implements() + + @array_function_dispatch(lambda array, option=None: (array,)) + def func_with_option(array, option='default'): + return option + + @implements(func_with_option) + def my_array_func_with_option(array, new_option='myarray'): + return new_option + + # we don't need to implement every option on __array_function__ + # implementations + assert_equal(func_with_option(1), 'default') + assert_equal(func_with_option(1, option='extra'), 'extra') + assert_equal(func_with_option(MyArray()), 'myarray') + with assert_raises(TypeError): + func_with_option(MyArray(), option='extra') + + # but new options on implementations can't be used + result = my_array_func_with_option(MyArray(), new_option='yes') + assert_equal(result, 'yes') + with assert_raises(TypeError): + func_with_option(MyArray(), new_option='no') + + def test_not_implemented(self): + MyArray, implements = _new_duck_type_and_implements() + + @array_function_dispatch(lambda array: (array,), module='my') + def func(array): + return array + + array = np.array(1) + assert_(func(array) is array) + assert_equal(func.__module__, 'my') + + with assert_raises_regex( + TypeError, "no implementation found for 'my.func'"): + func(MyArray()) + + +class TestNDArrayMethods: + + def test_repr(self): + # gh-12162: should still be defined even if __array_function__ doesn't + # implement np.array_repr() + + class MyArray(np.ndarray): + def __array_function__(*args, **kwargs): + return NotImplemented + + array = np.array(1).view(MyArray) + assert_equal(repr(array), 'MyArray(1)') + assert_equal(str(array), '1') + + +class TestNumPyFunctions: + + def test_set_module(self): + assert_equal(np.sum.__module__, 'numpy') + assert_equal(np.char.equal.__module__, 'numpy.char') + assert_equal(np.fft.fft.__module__, 'numpy.fft') + assert_equal(np.linalg.solve.__module__, 'numpy.linalg') + + def test_inspect_sum(self): + signature = inspect.signature(np.sum) + assert_('axis' in signature.parameters) + + @requires_array_function + def test_override_sum(self): + MyArray, implements = _new_duck_type_and_implements() + + @implements(np.sum) + def _(array): + return 'yes' + + assert_equal(np.sum(MyArray()), 'yes') + + @requires_array_function + def test_sum_on_mock_array(self): + + # We need a proxy for mocks because __array_function__ is only looked + # up in the class dict + class ArrayProxy: + def __init__(self, value): + self.value = value + def __array_function__(self, *args, **kwargs): + return self.value.__array_function__(*args, **kwargs) + def __array__(self, *args, **kwargs): + return self.value.__array__(*args, **kwargs) + + proxy = ArrayProxy(mock.Mock(spec=ArrayProxy)) + proxy.value.__array_function__.return_value = 1 + result = np.sum(proxy) + assert_equal(result, 1) + proxy.value.__array_function__.assert_called_once_with( + np.sum, (ArrayProxy,), (proxy,), {}) + proxy.value.__array__.assert_not_called() + + @requires_array_function + def test_sum_forwarding_implementation(self): + + class MyArray(np.ndarray): + + def sum(self, axis, out): + return 'summed' + + def __array_function__(self, func, types, args, kwargs): + return super().__array_function__(func, types, args, kwargs) + + # note: the internal implementation of np.sum() calls the .sum() method + array = np.array(1).view(MyArray) + assert_equal(np.sum(array), 'summed') + + +class TestArrayLike: + def setup(self): + class MyArray(): + def __init__(self, function=None): + self.function = function + + def __array_function__(self, func, types, args, kwargs): + try: + my_func = getattr(self, func.__name__) + except AttributeError: + return NotImplemented + return my_func(*args, **kwargs) + + self.MyArray = MyArray + + class MyNoArrayFunctionArray(): + def __init__(self, function=None): + self.function = function + + self.MyNoArrayFunctionArray = MyNoArrayFunctionArray + + def add_method(self, name, arr_class, enable_value_error=False): + def _definition(*args, **kwargs): + # Check that `like=` isn't propagated downstream + assert 'like' not in kwargs + + if enable_value_error and 'value_error' in kwargs: + raise ValueError + + return arr_class(getattr(arr_class, name)) + setattr(arr_class, name, _definition) + + def func_args(*args, **kwargs): + return args, kwargs + + @requires_array_function + def test_array_like_not_implemented(self): + self.add_method('array', self.MyArray) + + ref = self.MyArray.array() + + with assert_raises_regex(TypeError, 'no implementation found'): + array_like = np.asarray(1, like=ref) + + _array_tests = [ + ('array', *func_args((1,))), + ('asarray', *func_args((1,))), + ('asanyarray', *func_args((1,))), + ('ascontiguousarray', *func_args((2, 3))), + ('asfortranarray', *func_args((2, 3))), + ('require', *func_args((np.arange(6).reshape(2, 3),), + requirements=['A', 'F'])), + ('empty', *func_args((1,))), + ('full', *func_args((1,), 2)), + ('ones', *func_args((1,))), + ('zeros', *func_args((1,))), + ('arange', *func_args(3)), + ('frombuffer', *func_args(b'\x00' * 8, dtype=int)), + ('fromiter', *func_args(range(3), dtype=int)), + ('fromstring', *func_args('1,2', dtype=int, sep=',')), + ('loadtxt', *func_args(lambda: StringIO('0 1\n2 3'))), + ('genfromtxt', *func_args(lambda: StringIO(u'1,2.1'), + dtype=[('int', 'i8'), ('float', 'f8')], + delimiter=',')), + ] + + @pytest.mark.parametrize('function, args, kwargs', _array_tests) + @pytest.mark.parametrize('numpy_ref', [True, False]) + @requires_array_function + def test_array_like(self, function, args, kwargs, numpy_ref): + self.add_method('array', self.MyArray) + self.add_method(function, self.MyArray) + np_func = getattr(np, function) + my_func = getattr(self.MyArray, function) + + if numpy_ref is True: + ref = np.array(1) + else: + ref = self.MyArray.array() + + like_args = tuple(a() if callable(a) else a for a in args) + array_like = np_func(*like_args, **kwargs, like=ref) + + if numpy_ref is True: + assert type(array_like) is np.ndarray + + np_args = tuple(a() if callable(a) else a for a in args) + np_arr = np_func(*np_args, **kwargs) + + # Special-case np.empty to ensure values match + if function == "empty": + np_arr.fill(1) + array_like.fill(1) + + assert_equal(array_like, np_arr) + else: + assert type(array_like) is self.MyArray + assert array_like.function is my_func + + @pytest.mark.parametrize('function, args, kwargs', _array_tests) + @pytest.mark.parametrize('ref', [1, [1], "MyNoArrayFunctionArray"]) + @requires_array_function + def test_no_array_function_like(self, function, args, kwargs, ref): + self.add_method('array', self.MyNoArrayFunctionArray) + self.add_method(function, self.MyNoArrayFunctionArray) + np_func = getattr(np, function) + + # Instantiate ref if it's the MyNoArrayFunctionArray class + if ref == "MyNoArrayFunctionArray": + ref = self.MyNoArrayFunctionArray.array() + + like_args = tuple(a() if callable(a) else a for a in args) + + with assert_raises_regex(TypeError, + 'The `like` argument must be an array-like that implements'): + np_func(*like_args, **kwargs, like=ref) + + @pytest.mark.parametrize('numpy_ref', [True, False]) + def test_array_like_fromfile(self, numpy_ref): + self.add_method('array', self.MyArray) + self.add_method("fromfile", self.MyArray) + + if numpy_ref is True: + ref = np.array(1) + else: + ref = self.MyArray.array() + + data = np.random.random(5) + + with tempfile.TemporaryDirectory() as tmpdir: + fname = os.path.join(tmpdir, "testfile") + data.tofile(fname) + + array_like = np.fromfile(fname, like=ref) + if numpy_ref is True: + assert type(array_like) is np.ndarray + np_res = np.fromfile(fname, like=ref) + assert_equal(np_res, data) + assert_equal(array_like, np_res) + else: + assert type(array_like) is self.MyArray + assert array_like.function is self.MyArray.fromfile + + @requires_array_function + def test_exception_handling(self): + self.add_method('array', self.MyArray, enable_value_error=True) + + ref = self.MyArray.array() + + with assert_raises(TypeError): + # Raises the error about `value_error` being invalid first + np.array(1, value_error=True, like=ref) diff --git a/numpy/core/tests/test_print.py b/numpy/core/tests/test_print.py index 4332087489ed..89a8b48bfdee 100644 --- a/numpy/core/tests/test_print.py +++ b/numpy/core/tests/test_print.py @@ -1,21 +1,26 @@ -from __future__ import division, absolute_import, print_function - import sys +import pytest + import numpy as np -from numpy.testing import assert_, assert_equal, SkipTest +from numpy.testing import assert_, assert_equal from numpy.core.tests._locales import CommaDecimalPointLocale -if sys.version_info[0] >= 3: - from io import StringIO -else: - from StringIO import StringIO +from io import StringIO _REF = {np.inf: 'inf', -np.inf: '-inf', np.nan: 'nan'} -def check_float_type(tp): +@pytest.mark.parametrize('tp', [np.float32, np.double, np.longdouble]) +def test_float_types(tp): + """ Check formatting. + + This is only for the str function, and only for simple types. + The precision of np.float32 and np.longdouble aren't the same as the + python float precision. + + """ for x in [0, 1, -1, 1e20]: assert_equal(str(tp(x)), str(float(x)), err_msg='Failed str formatting for type %s' % tp) @@ -28,34 +33,30 @@ def check_float_type(tp): assert_equal(str(tp(1e16)), ref, err_msg='Failed str formatting for type %s' % tp) -def test_float_types(): - """ Check formatting. + +@pytest.mark.parametrize('tp', [np.float32, np.double, np.longdouble]) +def test_nan_inf_float(tp): + """ Check formatting of nan & inf. This is only for the str function, and only for simple types. The precision of np.float32 and np.longdouble aren't the same as the python float precision. """ - for t in [np.float32, np.double, np.longdouble]: - check_float_type(t) - -def check_nan_inf_float(tp): for x in [np.inf, -np.inf, np.nan]: assert_equal(str(tp(x)), _REF[x], err_msg='Failed str formatting for type %s' % tp) -def test_nan_inf_float(): - """ Check formatting of nan & inf. + +@pytest.mark.parametrize('tp', [np.complex64, np.cdouble, np.clongdouble]) +def test_complex_types(tp): + """Check formatting of complex types. This is only for the str function, and only for simple types. The precision of np.float32 and np.longdouble aren't the same as the python float precision. """ - for t in [np.float32, np.double, np.longdouble]: - check_nan_inf_float(t) - -def check_complex_type(tp): for x in [0, 1, -1, 1e20]: assert_equal(str(tp(x)), str(complex(x)), err_msg='Failed str formatting for type %s' % tp) @@ -72,18 +73,9 @@ def check_complex_type(tp): assert_equal(str(tp(1e16)), ref, err_msg='Failed str formatting for type %s' % tp) -def test_complex_types(): - """Check formatting of complex types. - - This is only for the str function, and only for simple types. - The precision of np.float32 and np.longdouble aren't the same as the - python float precision. - - """ - for t in [np.complex64, np.cdouble, np.clongdouble]: - check_complex_type(t) -def test_complex_inf_nan(): +@pytest.mark.parametrize('dtype', [np.complex64, np.cdouble, np.clongdouble]) +def test_complex_inf_nan(dtype): """Check inf/nan formatting of complex types.""" TESTS = { complex(np.inf, 0): "(inf+0j)", @@ -103,12 +95,9 @@ def test_complex_inf_nan(): complex(-np.nan, 1): "(nan+1j)", complex(1, -np.nan): "(1+nanj)", } - for tp in [np.complex64, np.cdouble, np.clongdouble]: - for c, s in TESTS.items(): - _check_complex_inf_nan(c, s, tp) + for c, s in TESTS.items(): + assert_equal(str(dtype(c)), s) -def _check_complex_inf_nan(c, s, dtype): - assert_equal(str(dtype(c)), s) # print tests def _test_redirected_print(x, tp, ref=None): @@ -129,7 +118,10 @@ def _test_redirected_print(x, tp, ref=None): assert_equal(file.getvalue(), file_tp.getvalue(), err_msg='print failed for type%s' % tp) -def check_float_type_print(tp): + +@pytest.mark.parametrize('tp', [np.float32, np.double, np.longdouble]) +def test_float_type_print(tp): + """Check formatting when using print """ for x in [0, 1, -1, 1e20]: _test_redirected_print(float(x), tp) @@ -142,7 +134,10 @@ def check_float_type_print(tp): ref = '1e+16' _test_redirected_print(float(1e16), tp, ref) -def check_complex_type_print(tp): + +@pytest.mark.parametrize('tp', [np.complex64, np.cdouble, np.clongdouble]) +def test_complex_type_print(tp): + """Check formatting when using print """ # We do not create complex with inf/nan directly because the feature is # missing in python < 2.6 for x in [0, 1, -1, 1e20]: @@ -158,15 +153,6 @@ def check_complex_type_print(tp): _test_redirected_print(complex(-np.inf, 1), tp, '(-inf+1j)') _test_redirected_print(complex(-np.nan, 1), tp, '(nan+1j)') -def test_float_type_print(): - """Check formatting when using print """ - for t in [np.float32, np.double, np.longdouble]: - check_float_type_print(t) - -def test_complex_type_print(): - """Check formatting when using print """ - for t in [np.complex64, np.cdouble, np.clongdouble]: - check_complex_type_print(t) def test_scalar_format(): """Test the str.format method with NumPy scalar types""" diff --git a/numpy/core/tests/test_protocols.py b/numpy/core/tests/test_protocols.py new file mode 100644 index 000000000000..55a2bcf72fad --- /dev/null +++ b/numpy/core/tests/test_protocols.py @@ -0,0 +1,44 @@ +import pytest +import warnings +import numpy as np + + +@pytest.mark.filterwarnings("error") +def test_getattr_warning(): + # issue gh-14735: make sure we clear only getattr errors, and let warnings + # through + class Wrapper: + def __init__(self, array): + self.array = array + + def __len__(self): + return len(self.array) + + def __getitem__(self, item): + return type(self)(self.array[item]) + + def __getattr__(self, name): + if name.startswith("__array_"): + warnings.warn("object got converted", UserWarning, stacklevel=1) + + return getattr(self.array, name) + + def __repr__(self): + return "<Wrapper({self.array})>".format(self=self) + + array = Wrapper(np.arange(10)) + with pytest.raises(UserWarning, match="object got converted"): + np.asarray(array) + + +def test_array_called(): + class Wrapper: + val = '0' * 100 + def __array__(self, result=None): + return np.array([self.val], dtype=object) + + + wrapped = Wrapper() + arr = np.array(wrapped, dtype=str) + assert arr.dtype == 'U100' + assert arr[0] == Wrapper.val diff --git a/numpy/core/tests/test_records.py b/numpy/core/tests/test_records.py index d7c7d16e3f74..4d4b4b5158de 100644 --- a/numpy/core/tests/test_records.py +++ b/numpy/core/tests/test_records.py @@ -1,37 +1,26 @@ -from __future__ import division, absolute_import, print_function - -import sys -try: - # Accessing collections abstract classes from collections - # has been deprecated since Python 3.3 - import collections.abc as collections_abc -except ImportError: - import collections as collections_abc -import pickle -import warnings +import collections.abc import textwrap +from io import BytesIO from os import path +from pathlib import Path import pytest import numpy as np from numpy.testing import ( assert_, assert_equal, assert_array_equal, assert_array_almost_equal, - assert_raises, assert_warns + assert_raises, temppath, ) +from numpy.compat import pickle -class TestFromrecords(object): +class TestFromrecords: def test_fromrecords(self): r = np.rec.fromrecords([[456, 'dbe', 1.2], [2, 'de', 1.3]], names='col1,col2,col3') assert_equal(r[0].item(), (456, 'dbe', 1.2)) assert_equal(r['col1'].dtype.kind, 'i') - if sys.version_info[0] >= 3: - assert_equal(r['col2'].dtype.kind, 'U') - assert_equal(r['col2'].dtype.itemsize, 12) - else: - assert_equal(r['col2'].dtype.kind, 'S') - assert_equal(r['col2'].dtype.itemsize, 3) + assert_equal(r['col2'].dtype.kind, 'U') + assert_equal(r['col2'].dtype.itemsize, 12) assert_equal(r['col3'].dtype.kind, 'f') def test_fromrecords_0len(self): @@ -91,8 +80,14 @@ def test_recarray_fromfile(self): r1 = np.rec.fromfile(fd, formats='f8,i4,a5', shape=3, byteorder='big') fd.seek(2880 * 2) r2 = np.rec.array(fd, formats='f8,i4,a5', shape=3, byteorder='big') + fd.seek(2880 * 2) + bytes_array = BytesIO() + bytes_array.write(fd.read()) + bytes_array.seek(0) + r3 = np.rec.fromfile(bytes_array, formats='f8,i4,a5', shape=3, byteorder='big') fd.close() assert_equal(r1, r2) + assert_equal(r2, r3) def test_recarray_from_obj(self): count = 10 @@ -258,7 +253,7 @@ def test_recarray_conflict_fields(self): assert_array_equal(ra['shape'], [['A', 'B', 'C']]) ra.field = 5 assert_array_equal(ra['field'], [[5, 5, 5]]) - assert_(isinstance(ra.field, collections_abc.Callable)) + assert_(isinstance(ra.field, collections.abc.Callable)) def test_fromrecords_with_explicit_dtype(self): a = np.rec.fromrecords([(1, 'a'), (2, 'bbb')], @@ -325,7 +320,23 @@ def test_zero_width_strings(self): assert_equal(rec['f1'], [b'', b'', b'']) -class TestRecord(object): +class TestPathUsage: + # Test that pathlib.Path can be used + def test_tofile_fromfile(self): + with temppath(suffix='.bin') as path: + path = Path(path) + np.random.seed(123) + a = np.random.rand(10).astype('f8,i4,a5') + a[5] = (0.5,10,'abcde') + with path.open("wb") as fd: + a.tofile(fd) + x = np.core.records.fromfile(path, + formats='f8,i4,a5', + shape=10) + assert_array_equal(x, a) + + +class TestRecord: def setup(self): self.data = np.rec.fromrecords([(1, 2, 3), (4, 5, 6)], dtype=[("col1", "<i4"), @@ -361,7 +372,6 @@ def test_nonwriteable_setfield(self): with assert_raises(ValueError): r.setfield([2,3], *r.dtype.fields['f']) - @pytest.mark.xfail(reason="See gh-10411, becomes real error in 1.16") def test_out_of_order_fields(self): # names in the same order, padding added to descr x = self.data[['col1', 'col2']] @@ -378,22 +388,52 @@ def test_out_of_order_fields(self): def test_pickle_1(self): # Issue #1529 a = np.array([(1, [])], dtype=[('a', np.int32), ('b', np.int32, 0)]) - assert_equal(a, pickle.loads(pickle.dumps(a))) - assert_equal(a[0], pickle.loads(pickle.dumps(a[0]))) + for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): + assert_equal(a, pickle.loads(pickle.dumps(a, protocol=proto))) + assert_equal(a[0], pickle.loads(pickle.dumps(a[0], + protocol=proto))) def test_pickle_2(self): a = self.data - assert_equal(a, pickle.loads(pickle.dumps(a))) - assert_equal(a[0], pickle.loads(pickle.dumps(a[0]))) + for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): + assert_equal(a, pickle.loads(pickle.dumps(a, protocol=proto))) + assert_equal(a[0], pickle.loads(pickle.dumps(a[0], + protocol=proto))) def test_pickle_3(self): # Issue #7140 a = self.data - pa = pickle.loads(pickle.dumps(a[0])) - assert_(pa.flags.c_contiguous) - assert_(pa.flags.f_contiguous) - assert_(pa.flags.writeable) - assert_(pa.flags.aligned) + for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): + pa = pickle.loads(pickle.dumps(a[0], protocol=proto)) + assert_(pa.flags.c_contiguous) + assert_(pa.flags.f_contiguous) + assert_(pa.flags.writeable) + assert_(pa.flags.aligned) + + def test_pickle_void(self): + # issue gh-13593 + dt = np.dtype([('obj', 'O'), ('int', 'i')]) + a = np.empty(1, dtype=dt) + data = (bytearray(b'eman'),) + a['obj'] = data + a['int'] = 42 + ctor, args = a[0].__reduce__() + # check the constructor is what we expect before interpreting the arguments + assert ctor is np.core.multiarray.scalar + dtype, obj = args + # make sure we did not pickle the address + assert not isinstance(obj, bytes) + + assert_raises(RuntimeError, ctor, dtype, 13) + + # Test roundtrip: + dump = pickle.dumps(a[0]) + unpickled = pickle.loads(dump) + assert a[0] == unpickled + + # Also check the similar (impossible) "object scalar" path: + with pytest.warns(DeprecationWarning): + assert ctor(np.dtype("O"), data) is data def test_objview_record(self): # https://github.com/numpy/numpy/issues/2599 @@ -414,7 +454,56 @@ def test_record_scalar_setitem(self): def test_missing_field(self): # https://github.com/numpy/numpy/issues/4806 arr = np.zeros((3,), dtype=[('x', int), ('y', int)]) - assert_raises(ValueError, lambda: arr[['nofield']]) + assert_raises(KeyError, lambda: arr[['nofield']]) + + def test_fromarrays_nested_structured_arrays(self): + arrays = [ + np.arange(10), + np.ones(10, dtype=[('a', '<u2'), ('b', '<f4')]), + ] + arr = np.rec.fromarrays(arrays) # ValueError? + + @pytest.mark.parametrize('nfields', [0, 1, 2]) + def test_assign_dtype_attribute(self, nfields): + dt = np.dtype([('a', np.uint8), ('b', np.uint8), ('c', np.uint8)][:nfields]) + data = np.zeros(3, dt).view(np.recarray) + + # the original and resulting dtypes differ on whether they are records + assert data.dtype.type == np.record + assert dt.type != np.record + + # ensure that the dtype remains a record even when assigned + data.dtype = dt + assert data.dtype.type == np.record + + @pytest.mark.parametrize('nfields', [0, 1, 2]) + def test_nested_fields_are_records(self, nfields): + """ Test that nested structured types are treated as records too """ + dt = np.dtype([('a', np.uint8), ('b', np.uint8), ('c', np.uint8)][:nfields]) + dt_outer = np.dtype([('inner', dt)]) + + data = np.zeros(3, dt_outer).view(np.recarray) + assert isinstance(data, np.recarray) + assert isinstance(data['inner'], np.recarray) + + data0 = data[0] + assert isinstance(data0, np.record) + assert isinstance(data0['inner'], np.record) + + def test_nested_dtype_padding(self): + """ test that trailing padding is preserved """ + # construct a dtype with padding at the end + dt = np.dtype([('a', np.uint8), ('b', np.uint8), ('c', np.uint8)]) + dt_padded_end = dt[['a', 'b']] + assert dt_padded_end.itemsize == dt.itemsize + + dt_outer = np.dtype([('inner', dt_padded_end)]) + + data = np.zeros(3, dt_outer).view(np.recarray) + assert_equal(data['inner'].dtype, dt_padded_end) + + data0 = data[0] + assert_equal(data0['inner'].dtype, dt_padded_end) def test_find_duplicate(): diff --git a/numpy/core/tests/test_regression.py b/numpy/core/tests/test_regression.py index f8f75d9ea35f..21cc8c1595f6 100644 --- a/numpy/core/tests/test_regression.py +++ b/numpy/core/tests/test_regression.py @@ -1,11 +1,6 @@ -from __future__ import division, absolute_import, print_function - import copy -import pickle import sys -import platform import gc -import warnings import tempfile import pytest from os import path @@ -16,16 +11,20 @@ from numpy.testing import ( assert_, assert_equal, IS_PYPY, assert_almost_equal, assert_array_equal, assert_array_almost_equal, assert_raises, - assert_warns, suppress_warnings, _assert_valid_refcount, HAS_REFCOUNT, + assert_raises_regex, assert_warns, suppress_warnings, + _assert_valid_refcount, HAS_REFCOUNT, IS_PYSTON ) -from numpy.compat import asbytes, asunicode, long +from numpy.testing._private.utils import _no_tracing, requires_memory +from numpy.compat import asbytes, asunicode, pickle try: RecursionError except NameError: RecursionError = RuntimeError # python < 3.5 -class TestRegression(object): + + +class TestRegression: def test_invalid_round(self): # Ticket #3 v = 4.7599999999999998 @@ -38,17 +37,12 @@ def test_mem_empty(self): def test_pickle_transposed(self): # Ticket #16 a = np.transpose(np.array([[2, 9], [7, 0], [3, 8]])) - f = BytesIO() - pickle.dump(a, f) - f.seek(0) - b = pickle.load(f) - f.close() - assert_array_equal(a, b) - - def test_typeNA(self): - # Ticket #31 - assert_equal(np.typeNA[np.int64], 'Int64') - assert_equal(np.typeNA[np.uint64], 'UInt64') + for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): + with BytesIO() as f: + pickle.dump(a, f, protocol=proto) + f.seek(0) + b = pickle.load(f) + assert_array_equal(a, b) def test_dtype_names(self): # Ticket #35 @@ -92,12 +86,12 @@ def test_negative_nd_indexing(self): def test_char_dump(self): # Ticket #50 - f = BytesIO() ca = np.char.array(np.arange(1000, 1010), itemsize=4) - ca.dump(f) - f.seek(0) - ca = np.load(f) - f.close() + for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): + with BytesIO() as f: + pickle.dump(ca, f, protocol=proto) + f.seek(0) + ca = np.load(f, allow_pickle=True) def test_noncontiguous_fill(self): # Ticket #58. @@ -356,12 +350,12 @@ def assign(a, b, c): def test_unpickle_dtype_with_object(self): # Implemented in r2840 dt = np.dtype([('x', int), ('y', np.object_), ('z', 'O')]) - f = BytesIO() - pickle.dump(dt, f) - f.seek(0) - dt_ = pickle.load(f) - f.close() - assert_equal(dt, dt_) + for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): + with BytesIO() as f: + pickle.dump(dt, f, protocol=proto) + f.seek(0) + dt_ = pickle.load(f) + assert_equal(dt, dt_) def test_mem_array_creation_invalid_specification(self): # Ticket #196 @@ -424,7 +418,7 @@ def test_lexsort(self): def test_lexsort_invalid_sequence(self): # Issue gh-4123 - class BuggySequence(object): + class BuggySequence: def __len__(self): return 4 @@ -433,6 +427,37 @@ def __getitem__(self, key): assert_raises(KeyError, np.lexsort, BuggySequence()) + def test_lexsort_zerolen_custom_strides(self): + # Ticket #14228 + xs = np.array([], dtype='i8') + assert xs.strides == (8,) + assert np.lexsort((xs,)).shape[0] == 0 # Works + + xs.strides = (16,) + assert np.lexsort((xs,)).shape[0] == 0 # Was: MemoryError + + def test_lexsort_zerolen_custom_strides_2d(self): + xs = np.array([], dtype='i8') + + xs.shape = (0, 2) + xs.strides = (16, 16) + assert np.lexsort((xs,), axis=0).shape[0] == 0 + + xs.shape = (2, 0) + xs.strides = (16, 16) + assert np.lexsort((xs,), axis=0).shape[0] == 2 + + def test_lexsort_invalid_axis(self): + assert_raises(np.AxisError, np.lexsort, (np.arange(1),), axis=2) + assert_raises(np.AxisError, np.lexsort, (np.array([]),), axis=1) + assert_raises(np.AxisError, np.lexsort, (np.array(1),), axis=10) + + def test_lexsort_zerolen_element(self): + dt = np.dtype([]) # a void dtype with no fields + xs = np.empty(4, dt) + + assert np.lexsort((xs,)).shape[0] == xs.shape[0] + def test_pickle_py2_bytes_encoding(self): # Check that arrays and scalars pickled on Py2 are # unpickleable on Py3 using encoding='bytes' @@ -459,19 +484,18 @@ def test_pickle_py2_bytes_encoding(self): b"bI00\nS'O\\x81\\xb7Z\\xaa:\\xabY'\np22\ntp23\nb."), ] - if sys.version_info[:2] >= (3, 4): - # encoding='bytes' was added in Py3.4 - for original, data in test_data: - result = pickle.loads(data, encoding='bytes') - assert_equal(result, original) + for original, data in test_data: + result = pickle.loads(data, encoding='bytes') + assert_equal(result, original) - if isinstance(result, np.ndarray) and result.dtype.names: - for name in result.dtype.names: - assert_(isinstance(name, str)) + if isinstance(result, np.ndarray) and result.dtype.names is not None: + for name in result.dtype.names: + assert_(isinstance(name, str)) def test_pickle_dtype(self): # Ticket #251 - pickle.dumps(float) + for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): + pickle.dumps(float, protocol=proto) def test_swap_real(self): # Ticket #265 @@ -481,8 +505,8 @@ def test_swap_real(self): assert_equal(np.arange(4, dtype='<c8').real.max(), 3.0) def test_object_array_from_list(self): - # Ticket #270 - assert_(np.array([1, 'A', None]).shape == (3,)) + # Ticket #270 (gh-868) + assert_(np.array([1, None, 'A']).shape == (3,)) def test_multiple_assign(self): # Ticket #273 @@ -815,8 +839,9 @@ def test_unicode_scalar(self): # Ticket #600 x = np.array(["DROND", "DROND1"], dtype="U6") el = x[1] - new = pickle.loads(pickle.dumps(el)) - assert_equal(new, el) + for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): + new = pickle.loads(pickle.dumps(el, protocol=proto)) + assert_equal(new, el) def test_arange_non_native_dtype(self): # Ticket #616 @@ -1009,7 +1034,7 @@ def test_object_array_refcounting(self): def test_mem_custom_float_to_array(self): # Ticket 702 - class MyFloat(object): + class MyFloat: def __float__(self): return 1.0 @@ -1018,7 +1043,7 @@ def __float__(self): def test_object_array_refcount_self_assign(self): # Ticket #711 - class VictimObject(object): + class VictimObject: deleted = False def __del__(self): @@ -1063,11 +1088,12 @@ def test_nonnative_endian_fill(self): def test_dot_alignment_sse2(self): # Test for ticket #551, changeset r5140 x = np.zeros((30, 40)) - y = pickle.loads(pickle.dumps(x)) - # y is now typically not aligned on a 8-byte boundary - z = np.ones((1, y.shape[0])) - # This shouldn't cause a segmentation fault: - np.dot(z, y) + for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): + y = pickle.loads(pickle.dumps(x, protocol=proto)) + # y is now typically not aligned on a 8-byte boundary + z = np.ones((1, y.shape[0])) + # This shouldn't cause a segmentation fault: + np.dot(z, y) def test_astype_copy(self): # Ticket #788, changeset r5155 @@ -1075,14 +1101,8 @@ def test_astype_copy(self): # The dtype is float64, but the isbuiltin attribute is 0. data_dir = path.join(path.dirname(__file__), 'data') filename = path.join(data_dir, "astype_copy.pkl") - if sys.version_info[0] >= 3: - f = open(filename, 'rb') + with open(filename, 'rb') as f: xp = pickle.load(f, encoding='latin1') - f.close() - else: - f = open(filename) - xp = pickle.load(f) - f.close() xpd = xp.astype(np.float64) assert_((xp.__array_interface__['data'][0] != xpd.__array_interface__['data'][0])) @@ -1199,10 +1219,7 @@ def test_unaligned_unicode_access(self): msg = 'unicode offset: %d chars' % i t = np.dtype([('a', 'S%d' % i), ('b', 'U2')]) x = np.array([(b'a', u'b')], dtype=t) - if sys.version_info[0] >= 3: - assert_equal(str(x), "[(b'a', 'b')]", err_msg=msg) - else: - assert_equal(str(x), "[('a', u'b')]", err_msg=msg) + assert_equal(str(x), "[(b'a', 'b')]", err_msg=msg) def test_sign_for_complex_nan(self): # Ticket 794. @@ -1277,10 +1294,14 @@ def test_void_scalar_constructor(self): assert_(test_record_void_scalar == test_record) - #Test pickle and unpickle of void and record scalars - assert_(pickle.loads(pickle.dumps(test_string)) == test_string) - assert_(pickle.loads(pickle.dumps(test_record)) == test_record) + # Test pickle and unpickle of void and record scalars + for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): + assert_(pickle.loads( + pickle.dumps(test_string, protocol=proto)) == test_string) + assert_(pickle.loads( + pickle.dumps(test_record, protocol=proto)) == test_record) + @_no_tracing def test_blasdot_uninitialized_memory(self): # Ticket #950 for m in [0, 1, 2]: @@ -1307,28 +1328,18 @@ def test_zeros(self): # Regression test for #1061. # Set a size which cannot fit into a 64 bits signed integer sz = 2 ** 64 - good = 'Maximum allowed dimension exceeded' - try: + with assert_raises_regex(ValueError, + 'Maximum allowed dimension exceeded'): np.empty(sz) - except ValueError as e: - if not str(e) == good: - self.fail("Got msg '%s', expected '%s'" % (e, good)) - except Exception as e: - self.fail("Got exception of type %s instead of ValueError" % type(e)) def test_huge_arange(self): # Regression test for #1062. # Set a size which cannot fit into a 64 bits signed integer sz = 2 ** 64 - good = 'Maximum allowed size exceeded' - try: + with assert_raises_regex(ValueError, + 'Maximum allowed size exceeded'): np.arange(sz) assert_(np.size == sz) - except ValueError as e: - if not str(e) == good: - self.fail("Got msg '%s', expected '%s'" % (e, good)) - except Exception as e: - self.fail("Got exception of type %s instead of ValueError" % type(e)) def test_fromiter_bytes(self): # Ticket #1058 @@ -1340,13 +1351,13 @@ def test_fromiter_bytes(self): def test_array_from_sequence_scalar_array(self): # Ticket #1078: segfaults when creating an array with a sequence of # 0d arrays. - a = np.array((np.ones(2), np.array(2))) + a = np.array((np.ones(2), np.array(2)), dtype=object) assert_equal(a.shape, (2,)) assert_equal(a.dtype, np.dtype(object)) assert_equal(a[0], np.ones(2)) assert_equal(a[1], np.array(2)) - a = np.array(((1,), np.array(1))) + a = np.array(((1,), np.array(1)), dtype=object) assert_equal(a.shape, (2,)) assert_equal(a.dtype, np.dtype(object)) assert_equal(a[0], (1,)) @@ -1354,7 +1365,7 @@ def test_array_from_sequence_scalar_array(self): def test_array_from_sequence_scalar_array2(self): # Ticket #1081: weird array with strange input... - t = np.array([np.array([]), np.array(0, object)]) + t = np.array([np.array([]), np.array(0, object)], dtype=object) assert_equal(t.shape, (2,)) assert_equal(t.dtype, np.dtype(object)) @@ -1397,6 +1408,13 @@ def test_unicode_to_string_cast(self): dtype='U') assert_raises(UnicodeEncodeError, np.array, a, 'S4') + def test_unicode_to_string_cast_error(self): + # gh-15790 + a = np.array([u'\x80'] * 129, dtype='U3') + assert_raises(UnicodeEncodeError, np.array, a, 'S') + b = a.reshape(3, 43)[:-1, :-1] + assert_raises(UnicodeEncodeError, np.array, b, 'S') + def test_mixed_string_unicode_array_creation(self): a = np.array(['1234', u'123']) assert_(a.itemsize == 16) @@ -1486,17 +1504,14 @@ def test_type(t): min //= -1 with np.errstate(divide="ignore"): - for t in (np.int8, np.int16, np.int32, np.int64, int, np.long): + for t in (np.int8, np.int16, np.int32, np.int64, int): test_type(t) def test_buffer_hashlib(self): - try: - from hashlib import md5 - except ImportError: - from md5 import new as md5 + from hashlib import sha256 x = np.array([1, 2, 3], dtype=np.dtype('<i4')) - assert_equal(md5(x).hexdigest(), '2a1dd1e1e59d0a384c26951e316cd7e6') + assert_equal(sha256(x).hexdigest(), '4636993d3e1da4e9d6b8f87b79e8f7c6d018580d52661950eabc3845c5897a4d') def test_0d_string_scalar(self): # Bug #1436; the following should succeed @@ -1514,10 +1529,11 @@ def test_fromiter_comparison(self): def test_fromstring_crash(self): # Ticket #1345: the following should not cause a crash - np.fromstring(b'aa, aa, 1.0', sep=',') + with assert_warns(DeprecationWarning): + np.fromstring(b'aa, aa, 1.0', sep=',') def test_ticket_1539(self): - dtypes = [x for x in np.typeDict.values() + dtypes = [x for x in np.sctypeDict.values() if (issubclass(x, np.number) and not issubclass(x, np.timedelta64))] a = np.array([], np.bool_) # not x[0] because it is unordered @@ -1734,7 +1750,7 @@ def __new__(cls, # it is designed to simulate an old API # expectation to guard against regression def squeeze(self): - return super(OldSqueeze, self).squeeze() + return super().squeeze() oldsqueeze = OldSqueeze(np.array([[1],[2],[3]])) @@ -1782,20 +1798,16 @@ def test_reduce_contiguous(self): assert_(a.flags.f_contiguous) assert_(b.flags.c_contiguous) + @pytest.mark.skipif(IS_PYSTON, reason="Pyston disables recursion checking") def test_object_array_self_reference(self): # Object arrays with references to themselves can cause problems a = np.array(0, dtype=object) a[()] = a assert_raises(RecursionError, int, a) - assert_raises(RecursionError, long, a) assert_raises(RecursionError, float, a) - if sys.version_info.major == 2: - # in python 3, this falls back on operator.index, which fails on - # on dtype=object - assert_raises(RecursionError, oct, a) - assert_raises(RecursionError, hex, a) a[()] = None + @pytest.mark.skipif(IS_PYSTON, reason="Pyston disables recursion checking") def test_object_array_circular_reference(self): # Test the same for a circular reference. a = np.array(0, dtype=object) @@ -1818,14 +1830,7 @@ def test_object_array_nested(self): b = np.array(0, dtype=object) a[()] = b assert_equal(int(a), int(0)) - assert_equal(long(a), long(0)) assert_equal(float(a), float(0)) - if sys.version_info.major == 2: - # in python 3, this falls back on operator.index, which fails on - # on dtype=object - assert_equal(oct(a), oct(0)) - assert_equal(hex(a), hex(0)) - def test_object_array_self_copy(self): # An object array being copied into itself DECREF'ed before INCREF'ing @@ -1929,9 +1934,9 @@ def test_pickle_string_overwrite(self): assert_equal(s[0], "\x01") def test_pickle_bytes_overwrite(self): - if sys.version_info[0] >= 3: + for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): data = np.array([1], dtype='b') - data = pickle.loads(pickle.dumps(data)) + data = pickle.loads(pickle.dumps(data, protocol=proto)) data[0] = 0xdd bytestring = "\x01 ".encode('ascii') assert_equal(bytestring[0:1], '\x01'.encode('ascii')) @@ -1945,12 +1950,11 @@ def test_pickle_py2_array_latin1_hack(self): b"tp2\nS'b'\np3\ntp4\nRp5\n(I1\n(I1\ntp6\ncnumpy\ndtype\np7\n(S'i1'\np8\n" b"I0\nI1\ntp9\nRp10\n(I3\nS'|'\np11\nNNNI-1\nI-1\nI0\ntp12\nbI00\nS'\\x81'\n" b"p13\ntp14\nb.") - if sys.version_info[0] >= 3: - # This should work: - result = pickle.loads(data, encoding='latin1') - assert_array_equal(result, np.array([129], dtype='b')) - # Should not segfault: - assert_raises(Exception, pickle.loads, data, encoding='koi8-r') + # This should work: + result = pickle.loads(data, encoding='latin1') + assert_array_equal(result, np.array([129], dtype='b')) + # Should not segfault: + assert_raises(Exception, pickle.loads, data, encoding='koi8-r') def test_pickle_py2_scalar_latin1_hack(self): # Check that scalar unpickling hack in Py3 that supports @@ -1977,25 +1981,24 @@ def test_pickle_py2_scalar_latin1_hack(self): b"tp8\nRp9\n."), 'different'), ] - if sys.version_info[0] >= 3: - for original, data, koi8r_validity in datas: - result = pickle.loads(data, encoding='latin1') - assert_equal(result, original) - - # Decoding under non-latin1 encoding (e.g.) KOI8-R can - # produce bad results, but should not segfault. - if koi8r_validity == 'different': - # Unicode code points happen to lie within latin1, - # but are different in koi8-r, resulting to silent - # bogus results - result = pickle.loads(data, encoding='koi8-r') - assert_(result != original) - elif koi8r_validity == 'invalid': - # Unicode code points outside latin1, so results - # to an encoding exception - assert_raises(ValueError, pickle.loads, data, encoding='koi8-r') - else: - raise ValueError(koi8r_validity) + for original, data, koi8r_validity in datas: + result = pickle.loads(data, encoding='latin1') + assert_equal(result, original) + + # Decoding under non-latin1 encoding (e.g.) KOI8-R can + # produce bad results, but should not segfault. + if koi8r_validity == 'different': + # Unicode code points happen to lie within latin1, + # but are different in koi8-r, resulting to silent + # bogus results + result = pickle.loads(data, encoding='koi8-r') + assert_(result != original) + elif koi8r_validity == 'invalid': + # Unicode code points outside latin1, so results + # to an encoding exception + assert_raises(ValueError, pickle.loads, data, encoding='koi8-r') + else: + raise ValueError(koi8r_validity) def test_structured_type_to_object(self): a_rec = np.array([(0, 1), (3, 2)], dtype='i4,i8') @@ -2029,6 +2032,7 @@ def test_assign_obj_listoflists(self): a[...] = [[1, 2]] assert_equal(a, [[1, 2], [1, 2]]) + @pytest.mark.slow_pypy def test_memoryleak(self): # Ticket #1917 - ensure that array data doesn't leak for i in range(1000): @@ -2050,28 +2054,25 @@ def test_search_sorted_invalid_arguments(self): def test_string_truncation(self): # Ticket #1990 - Data can be truncated in creation of an array from a - # mixed sequence of numeric values and strings + # mixed sequence of numeric values and strings (gh-2583) for val in [True, 1234, 123.4, complex(1, 234)]: - for tostr in [asunicode, asbytes]: - b = np.array([val, tostr('xx')]) + for tostr, dtype in [(asunicode, "U"), (asbytes, "S")]: + b = np.array([val, tostr('xx')], dtype=dtype) assert_equal(tostr(b[0]), tostr(val)) - b = np.array([tostr('xx'), val]) + b = np.array([tostr('xx'), val], dtype=dtype) assert_equal(tostr(b[1]), tostr(val)) # test also with longer strings - b = np.array([val, tostr('xxxxxxxxxx')]) + b = np.array([val, tostr('xxxxxxxxxx')], dtype=dtype) assert_equal(tostr(b[0]), tostr(val)) - b = np.array([tostr('xxxxxxxxxx'), val]) + b = np.array([tostr('xxxxxxxxxx'), val], dtype=dtype) assert_equal(tostr(b[1]), tostr(val)) def test_string_truncation_ucs2(self): # Ticket #2081. Python compiled with two byte unicode # can lead to truncation if itemsize is not properly # adjusted for NumPy's four byte unicode. - if sys.version_info[0] >= 3: - a = np.array(['abcd']) - else: - a = np.array([u'abcd']) + a = np.array(['abcd']) assert_equal(a.dtype.itemsize, 16) def test_unique_stable(self): @@ -2086,7 +2087,7 @@ def test_unicode_alloc_dealloc_match(self): # Ticket #1578, the mismatch only showed up when running # python-debug for python versions >= 2.7, and then as # a core dump and error message. - a = np.array(['abc'], dtype=np.unicode)[0] + a = np.array(['abc'], dtype=np.unicode_)[0] del a def test_refcount_error_in_clip(self): @@ -2105,7 +2106,8 @@ def test_searchsorted_wrong_dtype(self): assert_raises(TypeError, np.searchsorted, a, 1.2) # Ticket #2066, similar problem: dtype = np.format_parser(['i4', 'i4'], [], []) - a = np.recarray((2, ), dtype) + a = np.recarray((2,), dtype) + a[...] = [(1, 2), (3, 4)] assert_raises(TypeError, np.searchsorted, a, 1) def test_complex64_alignment(self): @@ -2205,7 +2207,7 @@ def test_richcompare_crash(self): import operator as op # dummy class where __array__ throws exception - class Foo(object): + class Foo: __array_priority__ = 1002 def __array__(self, *args, **kwargs): @@ -2214,12 +2216,7 @@ def __array__(self, *args, **kwargs): rhs = Foo() lhs = np.array(1) for f in [op.lt, op.le, op.gt, op.ge]: - if sys.version_info[0] >= 3: - assert_raises(TypeError, f, lhs, rhs) - elif not sys.py3kwarning: - # With -3 switch in python 2, DeprecationWarning is raised - # which we are not interested in - f(lhs, rhs) + assert_raises(TypeError, f, lhs, rhs) assert_(not op.eq(lhs, rhs)) assert_(op.ne(lhs, rhs)) @@ -2236,10 +2233,10 @@ def __eq__(self, other): def test_pickle_empty_string(self): # gh-3926 - - import pickle - test_string = np.string_('') - assert_equal(pickle.loads(pickle.dumps(test_string)), test_string) + for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): + test_string = np.string_('') + assert_equal(pickle.loads( + pickle.dumps(test_string, protocol=proto)), test_string) def test_frompyfunc_many_args(self): # gh-5672 @@ -2262,9 +2259,10 @@ def f(x): x[0], x[-1] = x[-1], x[0] uf = np.frompyfunc(f, 1, 0) - a = np.array([[1, 2, 3], [4, 5], [6, 7, 8, 9]]) + a = np.array([[1, 2, 3], [4, 5], [6, 7, 8, 9]], dtype=object) assert_equal(uf(a), ()) - assert_array_equal(a, [[3, 2, 1], [5, 4], [9, 7, 8, 6]]) + expected = np.array([[3, 2, 1], [5, 4], [9, 7, 8, 6]], dtype=object) + assert_array_equal(a, expected) @pytest.mark.skipif(not HAS_REFCOUNT, reason="Python lacks refcounts") def test_leak_in_structured_dtype_comparison(self): @@ -2332,10 +2330,14 @@ def test_invalid_structured_dtypes(self): # allowed as a special case due to existing use, see gh-2798 a = np.ones(1, dtype=('O', [('name', 'O')])) assert_equal(a[0], 1) + # In particular, the above union dtype (and union dtypes in general) + # should mainly behave like the main (object) dtype: + assert a[0] is a.item() + assert type(a[0]) is int def test_correct_hash_dict(self): # gh-8887 - __hash__ would be None despite tp_hash being set - all_types = set(np.typeDict.values()) - {np.void} + all_types = set(np.sctypeDict.values()) - {np.void} for t in all_types: val = t() @@ -2366,6 +2368,13 @@ def test_void_item_memview(self): del va assert_equal(x, b'\x00\x00\x00\x00') + def test_void_getitem(self): + # Test fix for gh-11668. + assert_(np.array([b'a'], 'V1').astype('O') == b'a') + assert_(np.array([b'ab'], 'V2').astype('O') == b'ab') + assert_(np.array([b'abc'], 'V3').astype('O') == b'abc') + assert_(np.array([b'abcd'], 'V4').astype('O') == b'abcd') + def test_structarray_title(self): # The following used to segfault on pypy, due to NPY_TITLE_KEY # not working properly and resulting to double-decref of the @@ -2375,3 +2384,181 @@ def test_structarray_title(self): structure = np.array([1], dtype=[(('x', 'X'), np.object_)]) structure[0]['x'] = np.array([2]) gc.collect() + + def test_dtype_scalar_squeeze(self): + # gh-11384 + values = { + 'S': b"a", + 'M': "2018-06-20", + } + for ch in np.typecodes['All']: + if ch in 'O': + continue + sctype = np.dtype(ch).type + scvalue = sctype(values.get(ch, 3)) + for axis in [None, ()]: + squeezed = scvalue.squeeze(axis=axis) + assert_equal(squeezed, scvalue) + assert_equal(type(squeezed), type(scvalue)) + + def test_field_access_by_title(self): + # gh-11507 + s = 'Some long field name' + if HAS_REFCOUNT: + base = sys.getrefcount(s) + t = np.dtype([((s, 'f1'), np.float64)]) + data = np.zeros(10, t) + for i in range(10): + str(data[['f1']]) + if HAS_REFCOUNT: + assert_(base <= sys.getrefcount(s)) + + @pytest.mark.parametrize('val', [ + # arrays and scalars + np.ones((10, 10), dtype='int32'), + np.uint64(10), + ]) + @pytest.mark.parametrize('protocol', + range(2, pickle.HIGHEST_PROTOCOL + 1) + ) + def test_pickle_module(self, protocol, val): + # gh-12837 + s = pickle.dumps(val, protocol) + assert b'_multiarray_umath' not in s + if protocol == 5 and len(val.shape) > 0: + # unpickling ndarray goes through _frombuffer for protocol 5 + assert b'numpy.core.numeric' in s + else: + assert b'numpy.core.multiarray' in s + + def test_object_casting_errors(self): + # gh-11993 update to ValueError (see gh-16909), since strings can in + # principle be converted to complex, but this string cannot. + arr = np.array(['AAAAA', 18465886.0, 18465886.0], dtype=object) + assert_raises(ValueError, arr.astype, 'c8') + + def test_eff1d_casting(self): + # gh-12711 + x = np.array([1, 2, 4, 7, 0], dtype=np.int16) + res = np.ediff1d(x, to_begin=-99, to_end=np.array([88, 99])) + assert_equal(res, [-99, 1, 2, 3, -7, 88, 99]) + + # The use of safe casting means, that 1<<20 is cast unsafely, an + # error may be better, but currently there is no mechanism for it. + res = np.ediff1d(x, to_begin=(1<<20), to_end=(1<<20)) + assert_equal(res, [0, 1, 2, 3, -7, 0]) + + def test_pickle_datetime64_array(self): + # gh-12745 (would fail with pickle5 installed) + d = np.datetime64('2015-07-04 12:59:59.50', 'ns') + arr = np.array([d]) + for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): + dumped = pickle.dumps(arr, protocol=proto) + assert_equal(pickle.loads(dumped), arr) + + def test_bad_array_interface(self): + class T: + __array_interface__ = {} + + with assert_raises(ValueError): + np.array([T()]) + + def test_2d__array__shape(self): + class T: + def __array__(self): + return np.ndarray(shape=(0,0)) + + # Make sure __array__ is used instead of Sequence methods. + def __iter__(self): + return iter([]) + + def __getitem__(self, idx): + raise AssertionError("__getitem__ was called") + + def __len__(self): + return 0 + + + t = T() + # gh-13659, would raise in broadcasting [x=t for x in result] + arr = np.array([t]) + assert arr.shape == (1, 0, 0) + + @pytest.mark.skipif(sys.maxsize < 2 ** 31 + 1, reason='overflows 32-bit python') + @pytest.mark.skipif(sys.platform == 'win32' and sys.version_info[:2] < (3, 8), + reason='overflows on windows, fixed in bpo-16865') + def test_to_ctypes(self): + #gh-14214 + arr = np.zeros((2 ** 31 + 1,), 'b') + assert arr.size * arr.itemsize > 2 ** 31 + c_arr = np.ctypeslib.as_ctypes(arr) + assert_equal(c_arr._length_, arr.size) + + def test_complex_conversion_error(self): + # gh-17068 + with pytest.raises(TypeError, match=r"Unable to convert dtype.*"): + complex(np.array("now", np.datetime64)) + + def test__array_interface__descr(self): + # gh-17068 + dt = np.dtype(dict(names=['a', 'b'], + offsets=[0, 0], + formats=[np.int64, np.int64])) + descr = np.array((1, 1), dtype=dt).__array_interface__['descr'] + assert descr == [('', '|V8')] # instead of [(b'', '|V8')] + + @pytest.mark.skipif(sys.maxsize < 2 ** 31 + 1, reason='overflows 32-bit python') + @requires_memory(free_bytes=9e9) + def test_dot_big_stride(self): + # gh-17111 + # blas stride = stride//itemsize > int32 max + int32_max = np.iinfo(np.int32).max + n = int32_max + 3 + a = np.empty([n], dtype=np.float32) + b = a[::n-1] + b[...] = 1 + assert b.strides[0] > int32_max * b.dtype.itemsize + assert np.dot(b, b) == 2.0 + + def test_frompyfunc_name(self): + # name conversion was failing for python 3 strings + # resulting in the default '?' name. Also test utf-8 + # encoding using non-ascii name. + def cassé(x): + return x + + f = np.frompyfunc(cassé, 1, 1) + assert str(f) == "<ufunc 'cassé (vectorized)'>" + + @pytest.mark.parametrize("operation", [ + 'add', 'subtract', 'multiply', 'floor_divide', + 'conjugate', 'fmod', 'square', 'reciprocal', + 'power', 'absolute', 'negative', 'positive', + 'greater', 'greater_equal', 'less', + 'less_equal', 'equal', 'not_equal', 'logical_and', + 'logical_not', 'logical_or', 'bitwise_and', 'bitwise_or', + 'bitwise_xor', 'invert', 'left_shift', 'right_shift', + 'gcd', 'lcm' + ] + ) + @pytest.mark.parametrize("order", [ + ('b->', 'B->'), + ('h->', 'H->'), + ('i->', 'I->'), + ('l->', 'L->'), + ('q->', 'Q->'), + ] + ) + def test_ufunc_order(self, operation, order): + # gh-18075 + # Ensure signed types before unsigned + def get_idx(string, str_lst): + for i, s in enumerate(str_lst): + if string in s: + return i + raise ValueError(f"{string} not in list") + types = getattr(np, operation).types + assert get_idx(order[0], types) < get_idx(order[1], types), ( + f"Unexpected types order of ufunc in {operation}" + f"for {order}. Possible fix: Use signed before unsigned" + "in generate_umath.py") diff --git a/numpy/core/tests/test_scalar_ctors.py b/numpy/core/tests/test_scalar_ctors.py index b21bc9dad07d..7e933537dbf3 100644 --- a/numpy/core/tests/test_scalar_ctors.py +++ b/numpy/core/tests/test_scalar_ctors.py @@ -1,18 +1,14 @@ """ Test the scalar constructors, which also do type-coercion """ -from __future__ import division, absolute_import, print_function - -import sys -import platform import pytest import numpy as np from numpy.testing import ( - assert_equal, assert_almost_equal, assert_raises, assert_warns, + assert_equal, assert_almost_equal, assert_warns, ) -class TestFromString(object): +class TestFromString: def test_floating(self): # Ticket #640, floats from string fsingle = np.single('1.234') @@ -42,24 +38,78 @@ def test_floating_overflow(self): flongdouble = assert_warns(RuntimeWarning, np.longdouble, '-1e10000') assert_equal(flongdouble, -np.inf) - @pytest.mark.skipif((sys.version_info[0] >= 3) - or (sys.platform == "win32" - and platform.architecture()[0] == "64bit"), - reason="numpy.intp('0xff', 16) not supported on Py3 " - "or 64 bit Windows") - def test_intp(self): - # Ticket #99 - i_width = np.int_(0).nbytes*2 - 1 - np.intp('0x' + 'f'*i_width, 16) - assert_raises(OverflowError, np.intp, '0x' + 'f'*(i_width+1), 16) - assert_raises(ValueError, np.intp, '0x1', 32) - assert_equal(255, np.intp('0xFF', 16)) +class TestExtraArgs: + def test_superclass(self): + # try both positional and keyword arguments + s = np.str_(b'\\x61', encoding='unicode-escape') + assert s == 'a' + s = np.str_(b'\\x61', 'unicode-escape') + assert s == 'a' + + # previously this would return '\\xx' + with pytest.raises(UnicodeDecodeError): + np.str_(b'\\xx', encoding='unicode-escape') + with pytest.raises(UnicodeDecodeError): + np.str_(b'\\xx', 'unicode-escape') + + # superclass fails, but numpy succeeds + assert np.bytes_(-2) == b'-2' + + def test_datetime(self): + dt = np.datetime64('2000-01', ('M', 2)) + assert np.datetime_data(dt) == ('M', 2) + + with pytest.raises(TypeError): + np.datetime64('2000', garbage=True) + + def test_bool(self): + with pytest.raises(TypeError): + np.bool_(False, garbage=True) + + def test_void(self): + with pytest.raises(TypeError): + np.void(b'test', garbage=True) -class TestFromInt(object): + +class TestFromInt: def test_intp(self): # Ticket #99 assert_equal(1024, np.intp(1024)) def test_uint64_from_negative(self): assert_equal(np.uint64(-2), np.uint64(18446744073709551614)) + + +int_types = [np.byte, np.short, np.intc, np.int_, np.longlong] +uint_types = [np.ubyte, np.ushort, np.uintc, np.uint, np.ulonglong] +float_types = [np.half, np.single, np.double, np.longdouble] +cfloat_types = [np.csingle, np.cdouble, np.clongdouble] + + +class TestArrayFromScalar: + """ gh-15467 """ + + def _do_test(self, t1, t2): + x = t1(2) + arr = np.array(x, dtype=t2) + # type should be preserved exactly + if t2 is None: + assert arr.dtype.type is t1 + else: + assert arr.dtype.type is t2 + + @pytest.mark.parametrize('t1', int_types + uint_types) + @pytest.mark.parametrize('t2', int_types + uint_types + [None]) + def test_integers(self, t1, t2): + return self._do_test(t1, t2) + + @pytest.mark.parametrize('t1', float_types) + @pytest.mark.parametrize('t2', float_types + [None]) + def test_reals(self, t1, t2): + return self._do_test(t1, t2) + + @pytest.mark.parametrize('t1', cfloat_types) + @pytest.mark.parametrize('t2', cfloat_types + [None]) + def test_complex(self, t1, t2): + return self._do_test(t1, t2) diff --git a/numpy/core/tests/test_scalar_methods.py b/numpy/core/tests/test_scalar_methods.py new file mode 100644 index 000000000000..eef4c1433910 --- /dev/null +++ b/numpy/core/tests/test_scalar_methods.py @@ -0,0 +1,203 @@ +""" +Test the scalar constructors, which also do type-coercion +""" +import sys +import fractions +import platform +import types +from typing import Any, Type + +import pytest +import numpy as np + +from numpy.testing import assert_equal, assert_raises + + +class TestAsIntegerRatio: + # derived in part from the cpython test "test_floatasratio" + + @pytest.mark.parametrize("ftype", [ + np.half, np.single, np.double, np.longdouble]) + @pytest.mark.parametrize("f, ratio", [ + (0.875, (7, 8)), + (-0.875, (-7, 8)), + (0.0, (0, 1)), + (11.5, (23, 2)), + ]) + def test_small(self, ftype, f, ratio): + assert_equal(ftype(f).as_integer_ratio(), ratio) + + @pytest.mark.parametrize("ftype", [ + np.half, np.single, np.double, np.longdouble]) + def test_simple_fractions(self, ftype): + R = fractions.Fraction + assert_equal(R(0, 1), + R(*ftype(0.0).as_integer_ratio())) + assert_equal(R(5, 2), + R(*ftype(2.5).as_integer_ratio())) + assert_equal(R(1, 2), + R(*ftype(0.5).as_integer_ratio())) + assert_equal(R(-2100, 1), + R(*ftype(-2100.0).as_integer_ratio())) + + @pytest.mark.parametrize("ftype", [ + np.half, np.single, np.double, np.longdouble]) + def test_errors(self, ftype): + assert_raises(OverflowError, ftype('inf').as_integer_ratio) + assert_raises(OverflowError, ftype('-inf').as_integer_ratio) + assert_raises(ValueError, ftype('nan').as_integer_ratio) + + def test_against_known_values(self): + R = fractions.Fraction + assert_equal(R(1075, 512), + R(*np.half(2.1).as_integer_ratio())) + assert_equal(R(-1075, 512), + R(*np.half(-2.1).as_integer_ratio())) + assert_equal(R(4404019, 2097152), + R(*np.single(2.1).as_integer_ratio())) + assert_equal(R(-4404019, 2097152), + R(*np.single(-2.1).as_integer_ratio())) + assert_equal(R(4728779608739021, 2251799813685248), + R(*np.double(2.1).as_integer_ratio())) + assert_equal(R(-4728779608739021, 2251799813685248), + R(*np.double(-2.1).as_integer_ratio())) + # longdouble is platform dependent + + @pytest.mark.parametrize("ftype, frac_vals, exp_vals", [ + # dtype test cases generated using hypothesis + # first five generated cases per dtype + (np.half, [0.0, 0.01154830649280303, 0.31082276347447274, + 0.527350517124794, 0.8308562335072596], + [0, 1, 0, -8, 12]), + (np.single, [0.0, 0.09248576989263226, 0.8160498218131407, + 0.17389442853722373, 0.7956044195067877], + [0, 12, 10, 17, -26]), + (np.double, [0.0, 0.031066908499895136, 0.5214135908877832, + 0.45780736035689296, 0.5906586745934036], + [0, -801, 51, 194, -653]), + pytest.param( + np.longdouble, + [0.0, 0.20492557202724854, 0.4277180662199366, 0.9888085019891495, + 0.9620175814461964], + [0, -7400, 14266, -7822, -8721], + marks=[ + pytest.mark.skipif( + np.finfo(np.double) == np.finfo(np.longdouble), + reason="long double is same as double"), + pytest.mark.skipif( + platform.machine().startswith("ppc"), + reason="IBM double double"), + ] + ) + ]) + def test_roundtrip(self, ftype, frac_vals, exp_vals): + for frac, exp in zip(frac_vals, exp_vals): + f = np.ldexp(ftype(frac), exp) + assert f.dtype == ftype + n, d = f.as_integer_ratio() + + try: + # workaround for gh-9968 + nf = np.longdouble(str(n)) + df = np.longdouble(str(d)) + except (OverflowError, RuntimeWarning): + # the values may not fit in any float type + pytest.skip("longdouble too small on this platform") + + assert_equal(nf / df, f, "{}/{}".format(n, d)) + + +class TestIsInteger: + @pytest.mark.parametrize("str_value", ["inf", "nan"]) + @pytest.mark.parametrize("code", np.typecodes["Float"]) + def test_special(self, code: str, str_value: str) -> None: + cls = np.dtype(code).type + value = cls(str_value) + assert not value.is_integer() + + @pytest.mark.parametrize( + "code", np.typecodes["Float"] + np.typecodes["AllInteger"] + ) + def test_true(self, code: str) -> None: + float_array = np.arange(-5, 5).astype(code) + for value in float_array: + assert value.is_integer() + + @pytest.mark.parametrize("code", np.typecodes["Float"]) + def test_false(self, code: str) -> None: + float_array = np.arange(-5, 5).astype(code) + float_array *= 1.1 + for value in float_array: + if value == 0: + continue + assert not value.is_integer() + + +@pytest.mark.skipif(sys.version_info < (3, 9), reason="Requires python 3.9") +class TestClassGetItem: + @pytest.mark.parametrize("cls", [ + np.number, + np.integer, + np.inexact, + np.unsignedinteger, + np.signedinteger, + np.floating, + ]) + def test_abc(self, cls: Type[np.number]) -> None: + alias = cls[Any] + assert isinstance(alias, types.GenericAlias) + assert alias.__origin__ is cls + + def test_abc_complexfloating(self) -> None: + alias = np.complexfloating[Any, Any] + assert isinstance(alias, types.GenericAlias) + assert alias.__origin__ is np.complexfloating + + @pytest.mark.parametrize("cls", [np.generic, np.flexible, np.character]) + def test_abc_non_numeric(self, cls: Type[np.generic]) -> None: + with pytest.raises(TypeError): + cls[Any] + + @pytest.mark.parametrize("code", np.typecodes["All"]) + def test_concrete(self, code: str) -> None: + cls = np.dtype(code).type + with pytest.raises(TypeError): + cls[Any] + + @pytest.mark.parametrize("arg_len", range(4)) + def test_subscript_tuple(self, arg_len: int) -> None: + arg_tup = (Any,) * arg_len + if arg_len == 1: + assert np.number[arg_tup] + else: + with pytest.raises(TypeError): + np.number[arg_tup] + + def test_subscript_scalar(self) -> None: + assert np.number[Any] + + +@pytest.mark.skipif(sys.version_info >= (3, 9), reason="Requires python 3.8") +@pytest.mark.parametrize("cls", [np.number, np.complexfloating, np.int64]) +def test_class_getitem_38(cls: Type[np.number]) -> None: + match = "Type subscription requires python >= 3.9" + with pytest.raises(TypeError, match=match): + cls[Any] + + +class TestBitCount: + # derived in part from the cpython test "test_bit_count" + + @pytest.mark.parametrize("itype", np.sctypes['int']+np.sctypes['uint']) + def test_small(self, itype): + for a in range(max(np.iinfo(itype).min, 0), 128): + msg = f"Smoke test for {itype}({a}).bit_count()" + assert itype(a).bit_count() == bin(a).count("1"), msg + + def test_bit_count(self): + for exp in [10, 17, 63]: + a = 2**exp + assert np.uint64(a).bit_count() == 1 + assert np.uint64(a - 1).bit_count() == exp + assert np.uint64(a ^ 63).bit_count() == 7 + assert np.uint64((a - 1) ^ 510).bit_count() == exp - 8 diff --git a/numpy/core/tests/test_scalarbuffer.py b/numpy/core/tests/test_scalarbuffer.py index 6d57a5014dcf..851cd3081aee 100644 --- a/numpy/core/tests/test_scalarbuffer.py +++ b/numpy/core/tests/test_scalarbuffer.py @@ -1,11 +1,12 @@ """ Test scalar buffer interface adheres to PEP 3118 """ -import sys import numpy as np +from numpy.core._rational_tests import rational +from numpy.core._multiarray_tests import get_buffer_info import pytest -from numpy.testing import assert_, assert_equal +from numpy.testing import assert_, assert_equal, assert_raises # PEP3118 format strings for native (standard alignment and byteorder) types scalars_and_codes = [ @@ -28,35 +29,44 @@ (np.cdouble, 'Zd'), (np.clongdouble, 'Zg'), ] +scalars_only, codes_only = zip(*scalars_and_codes) -@pytest.mark.skipif(sys.version_info.major < 3, - reason="Python 2 scalars lack a buffer interface") -class TestScalarPEP3118(object): - - def test_scalar_match_array(self): - for scalar, _ in scalars_and_codes: - x = scalar() - a = np.array([], dtype=np.dtype(scalar)) - mv_x = memoryview(x) - mv_a = memoryview(a) - assert_equal(mv_x.format, mv_a.format) - - def test_scalar_dim(self): - for scalar, _ in scalars_and_codes: - x = scalar() - mv_x = memoryview(x) - assert_equal(mv_x.itemsize, np.dtype(scalar).itemsize) - assert_equal(mv_x.ndim, 0) - assert_equal(mv_x.shape, ()) - assert_equal(mv_x.strides, ()) - assert_equal(mv_x.suboffsets, ()) - - def test_scalar_known_code(self): - for scalar, code in scalars_and_codes: - x = scalar() - mv_x = memoryview(x) - assert_equal(mv_x.format, code) +class TestScalarPEP3118: + + @pytest.mark.parametrize('scalar', scalars_only, ids=codes_only) + def test_scalar_match_array(self, scalar): + x = scalar() + a = np.array([], dtype=np.dtype(scalar)) + mv_x = memoryview(x) + mv_a = memoryview(a) + assert_equal(mv_x.format, mv_a.format) + + @pytest.mark.parametrize('scalar', scalars_only, ids=codes_only) + def test_scalar_dim(self, scalar): + x = scalar() + mv_x = memoryview(x) + assert_equal(mv_x.itemsize, np.dtype(scalar).itemsize) + assert_equal(mv_x.ndim, 0) + assert_equal(mv_x.shape, ()) + assert_equal(mv_x.strides, ()) + assert_equal(mv_x.suboffsets, ()) + + @pytest.mark.parametrize('scalar, code', scalars_and_codes, ids=codes_only) + def test_scalar_code_and_properties(self, scalar, code): + x = scalar() + expected = dict(strides=(), itemsize=x.dtype.itemsize, ndim=0, + shape=(), format=code, readonly=True) + + mv_x = memoryview(x) + print(mv_x.readonly, self._as_dict(mv_x)) + assert self._as_dict(mv_x) == expected + + @pytest.mark.parametrize('scalar', scalars_only, ids=codes_only) + def test_scalar_buffers_readonly(self, scalar): + x = scalar() + with pytest.raises(BufferError, match="scalar buffer is readonly"): + get_buffer_info(x, ["WRITABLE"]) def test_void_scalar_structured_data(self): dt = np.dtype([('name', np.unicode_, 16), ('grades', np.float64, (2,))]) @@ -64,7 +74,7 @@ def test_void_scalar_structured_data(self): assert_(isinstance(x, np.void)) mv_x = memoryview(x) expected_size = 16 * np.dtype((np.unicode_, 1)).itemsize - expected_size += 2 * np.dtype((np.float64, 1)).itemsize + expected_size += 2 * np.dtype(np.float64).itemsize assert_equal(mv_x.itemsize, expected_size) assert_equal(mv_x.ndim, 0) assert_equal(mv_x.shape, ()) @@ -77,3 +87,68 @@ def test_void_scalar_structured_data(self): mv_a = memoryview(a) assert_equal(mv_x.itemsize, mv_a.itemsize) assert_equal(mv_x.format, mv_a.format) + + # Check that we do not allow writeable buffer export (technically + # we could allow it sometimes here...) + with pytest.raises(BufferError, match="scalar buffer is readonly"): + get_buffer_info(x, ["WRITABLE"]) + + def _as_dict(self, m): + return dict(strides=m.strides, shape=m.shape, itemsize=m.itemsize, + ndim=m.ndim, format=m.format, readonly=m.readonly) + + def test_datetime_memoryview(self): + # gh-11656 + # Values verified with v1.13.3, shape is not () as in test_scalar_dim + + dt1 = np.datetime64('2016-01-01') + dt2 = np.datetime64('2017-01-01') + expected = dict(strides=(1,), itemsize=1, ndim=1, shape=(8,), + format='B', readonly=True) + v = memoryview(dt1) + assert self._as_dict(v) == expected + + v = memoryview(dt2 - dt1) + assert self._as_dict(v) == expected + + dt = np.dtype([('a', 'uint16'), ('b', 'M8[s]')]) + a = np.empty(1, dt) + # Fails to create a PEP 3118 valid buffer + assert_raises((ValueError, BufferError), memoryview, a[0]) + + # Check that we do not allow writeable buffer export + with pytest.raises(BufferError, match="scalar buffer is readonly"): + get_buffer_info(dt1, ["WRITABLE"]) + + @pytest.mark.parametrize('s', [ + pytest.param("\x32\x32", id="ascii"), + pytest.param("\uFE0F\uFE0F", id="basic multilingual"), + pytest.param("\U0001f4bb\U0001f4bb", id="non-BMP"), + ]) + def test_str_ucs4(self, s): + s = np.str_(s) # only our subclass implements the buffer protocol + + # all the same, characters always encode as ucs4 + expected = dict(strides=(), itemsize=8, ndim=0, shape=(), format='2w', + readonly=True) + + v = memoryview(s) + assert self._as_dict(v) == expected + + # integers of the paltform-appropriate endianness + code_points = np.frombuffer(v, dtype='i4') + + assert_equal(code_points, [ord(c) for c in s]) + + # Check that we do not allow writeable buffer export + with pytest.raises(BufferError, match="scalar buffer is readonly"): + get_buffer_info(s, ["WRITABLE"]) + + def test_user_scalar_fails_buffer(self): + r = rational(1) + with assert_raises(TypeError): + memoryview(r) + + # Check that we do not allow writeable buffer export + with pytest.raises(BufferError, match="scalar buffer is readonly"): + get_buffer_info(r, ["WRITABLE"]) \ No newline at end of file diff --git a/numpy/core/tests/test_scalarinherit.py b/numpy/core/tests/test_scalarinherit.py index 28436f6c74da..98d7f7cdef7f 100644 --- a/numpy/core/tests/test_scalarinherit.py +++ b/numpy/core/tests/test_scalarinherit.py @@ -1,14 +1,13 @@ -# -*- coding: utf-8 -*- """ Test printing of scalar types. """ -from __future__ import division, absolute_import, print_function +import pytest import numpy as np -from numpy.testing import assert_ +from numpy.testing import assert_, assert_raises -class A(object): +class A: pass class B(A, np.float64): pass @@ -23,7 +22,15 @@ class B0(np.float64, A): class C0(B0): pass -class TestInherit(object): +class HasNew: + def __new__(cls, *args, **kwargs): + return cls, args, kwargs + +class B1(np.float64, HasNew): + pass + + +class TestInherit: def test_init(self): x = B(1.0) assert_(str(x) == '1.0') @@ -38,8 +45,17 @@ def test_init2(self): y = C0(2.0) assert_(str(y) == '2.0') + def test_gh_15395(self): + # HasNew is the second base, so `np.float64` should have priority + x = B1(1.0) + assert_(str(x) == '1.0') + + # previously caused RecursionError!? + with pytest.raises(TypeError): + B1(1.0, 2.0) + -class TestCharacter(object): +class TestCharacter: def test_char_radd(self): # GH issue 9620, reached gentype_add and raise TypeError np_s = np.string_('abc') @@ -57,18 +73,26 @@ def test_char_radd(self): assert_(s + np_s == b'defabc') assert_(u + np_u == u'defabc') + class MyStr(str, np.generic): + # would segfault + pass + + with assert_raises(TypeError): + # Previously worked, but gave completely wrong result + ret = s + MyStr('abc') - class Mystr(str, np.generic): + class MyBytes(bytes, np.generic): # would segfault pass - ret = s + Mystr('abc') - assert_(type(ret) is type(s)) + ret = s + MyBytes(b'abc') + assert(type(ret) is type(s)) + assert ret == b"defabc" def test_char_repeat(self): np_s = np.string_('abc') np_u = np.unicode_('abc') - np_i = np.int(5) - res_np = np_s * np_i res_s = b'abc' * 5 - assert_(res_np == res_s) + res_u = u'abc' * 5 + assert_(np_s * 5 == res_s) + assert_(np_u * 5 == res_u) diff --git a/numpy/core/tests/test_scalarmath.py b/numpy/core/tests/test_scalarmath.py index ab2ef5ce6793..90078a2ea3ce 100644 --- a/numpy/core/tests/test_scalarmath.py +++ b/numpy/core/tests/test_scalarmath.py @@ -1,17 +1,18 @@ -from __future__ import division, absolute_import, print_function - +import contextlib import sys import warnings import itertools import operator import platform import pytest +from hypothesis import given, settings, Verbosity +from hypothesis.strategies import sampled_from import numpy as np from numpy.testing import ( - assert_, assert_equal, assert_raises, assert_almost_equal, assert_allclose, + assert_, assert_equal, assert_raises, assert_almost_equal, assert_array_equal, IS_PYPY, suppress_warnings, _gen_alignment_data, - assert_warns + assert_warns, ) types = [np.bool_, np.byte, np.ubyte, np.short, np.ushort, np.intc, np.uintc, @@ -25,7 +26,7 @@ # This compares scalarmath against ufuncs. -class TestTypes(object): +class TestTypes: def test_types(self): for atype in types: a = atype(1) @@ -64,7 +65,7 @@ def test_leak(self): np.add(1, 1) -class TestBaseMath(object): +class TestBaseMath: def test_blocked(self): # test alignments offsets for simd instructions # alignments for vz + 2 * (vs - 1) + 1 @@ -86,7 +87,7 @@ def test_blocked(self): assert_almost_equal(np.square(inp2), np.multiply(inp2, inp2), err_msg=msg) # skip true divide for ints - if dt != np.int32 or (sys.version_info.major < 3 and not sys.py3kwarning): + if dt != np.int32: assert_almost_equal(np.reciprocal(inp2), np.divide(1, inp2), err_msg=msg) @@ -110,7 +111,7 @@ def test_lower_align(self): np.add(d, np.ones_like(d)) -class TestPower(object): +class TestPower: def test_small_types(self): for t in [np.int8, np.int16, np.float16]: a = t(3) @@ -136,7 +137,7 @@ def test_integers_to_negative_integer_power(self): # 1 ** -1 possible special case base = [np.array(1, dt)[()] for dt in 'bhilqBHILQ'] for i1, i2 in itertools.product(base, exp): - if i1.dtype.name != 'uint64': + if i1.dtype != np.uint64: assert_raises(ValueError, operator.pow, i1, i2) else: res = operator.pow(i1, i2) @@ -146,7 +147,7 @@ def test_integers_to_negative_integer_power(self): # -1 ** -1 possible special case base = [np.array(-1, dt)[()] for dt in 'bhilq'] for i1, i2 in itertools.product(base, exp): - if i1.dtype.name != 'uint64': + if i1.dtype != np.uint64: assert_raises(ValueError, operator.pow, i1, i2) else: res = operator.pow(i1, i2) @@ -156,7 +157,7 @@ def test_integers_to_negative_integer_power(self): # 2 ** -1 perhaps generic base = [np.array(2, dt)[()] for dt in 'bhilqBHILQ'] for i1, i2 in itertools.product(base, exp): - if i1.dtype.name != 'uint64': + if i1.dtype != np.uint64: assert_raises(ValueError, operator.pow, i1, i2) else: res = operator.pow(i1, i2) @@ -184,7 +185,7 @@ def test_modular_power(self): a = 5 b = 4 c = 10 - expected = pow(a, b, c) + expected = pow(a, b, c) # noqa: F841 for t in (np.int32, np.float32, np.complex64): # note that 3-operand power only dispatches on the first argument assert_raises(TypeError, operator.pow, t(a), b, c) @@ -202,7 +203,7 @@ def _signs(dt): return (+1, -1) -class TestModulus(object): +class TestModulus: def test_modulus_basic(self): dt = np.typecodes['AllInteger'] + np.typecodes['Float'] @@ -278,6 +279,10 @@ def test_float_modulus_corner_cases(self): # Check nans, inf with suppress_warnings() as sup: sup.filter(RuntimeWarning, "invalid value encountered in remainder") + sup.filter(RuntimeWarning, "divide by zero encountered in remainder") + sup.filter(RuntimeWarning, "divide by zero encountered in floor_divide") + sup.filter(RuntimeWarning, "divide by zero encountered in divmod") + sup.filter(RuntimeWarning, "invalid value encountered in divmod") for dt in np.typecodes['Float']: fone = np.array(1.0, dtype=dt) fzer = np.array(0.0, dtype=dt) @@ -292,9 +297,22 @@ def test_float_modulus_corner_cases(self): assert_(np.isnan(rem), 'dt: %s' % dt) rem = operator.mod(finf, fone) assert_(np.isnan(rem), 'dt: %s' % dt) - - -class TestComplexDivision(object): + for op in [floordiv_and_mod, divmod]: + div, mod = op(fone, fzer) + assert_(np.isinf(div)) and assert_(np.isnan(mod)) + + def test_inplace_floordiv_handling(self): + # issue gh-12927 + # this only applies to in-place floordiv //=, because the output type + # promotes to float which does not fit + a = np.array([1, 2], np.int64) + b = np.array([1, 2], np.uint64) + with pytest.raises(TypeError, + match=r"Cannot cast ufunc 'floor_divide' output from"): + a //= b + + +class TestComplexDivision: def test_zero_division(self): with np.errstate(all="ignore"): for t in [np.complex64, np.complex128]: @@ -366,7 +384,7 @@ def test_branches(self): assert_equal(result.imag, ex[1]) -class TestConversion(object): +class TestConversion: def test_int_from_long(self): l = [1e6, 1e12, 1e18, -1e6, -1e12, -1e18] li = [10**6, 10**12, 10**18, -10**6, -10**12, -10**18] @@ -389,15 +407,15 @@ def test_iinfo_long_values(self): assert_(res == tgt) for code in np.typecodes['AllInteger']: - res = np.typeDict[code](np.iinfo(code).max) + res = np.dtype(code).type(np.iinfo(code).max) tgt = np.iinfo(code).max assert_(res == tgt) def test_int_raise_behaviour(self): def overflow_error_func(dtype): - np.typeDict[dtype](np.iinfo(dtype).max + 1) + dtype(np.iinfo(dtype).max + 1) - for code in 'lLqQ': + for code in [np.int_, np.uint, np.longlong, np.ulonglong]: assert_raises(OverflowError, overflow_error_func, code) def test_int_from_infinite_longdouble(self): @@ -422,7 +440,7 @@ def test_int_from_infinite_longdouble___int__(self): @pytest.mark.skipif(np.finfo(np.double) == np.finfo(np.longdouble), reason="long double is same as double") - @pytest.mark.skipif(platform.machine().startswith("ppc64"), + @pytest.mark.skipif(platform.machine().startswith("ppc"), reason="IBM double double") def test_int_from_huge_longdouble(self): # Produce a longdouble that would overflow a double, @@ -502,7 +520,7 @@ def test_scalar_comparison_to_none(self): assert_(np.equal(np.datetime64('NaT'), None)) -#class TestRepr(object): +#class TestRepr: # def test_repr(self): # for t in types: # val = t(1197346475.0137341) @@ -511,7 +529,7 @@ def test_scalar_comparison_to_none(self): # assert_equal( val, val2 ) -class TestRepr(object): +class TestRepr: def _test_type_repr(self, t): finfo = np.finfo(t) last_fraction_bit_idx = finfo.nexp + finfo.nmant @@ -519,7 +537,7 @@ def _test_type_repr(self, t): storage_bytes = np.dtype(t).itemsize*8 # could add some more types to the list below for which in ['small denorm', 'small norm']: - # Values from http://en.wikipedia.org/wiki/IEEE_754 + # Values from https://en.wikipedia.org/wiki/IEEE_754 constr = np.array([0x00]*storage_bytes, dtype=np.uint8) if which == 'small denorm': byte = last_fraction_bit_idx // 8 @@ -546,7 +564,7 @@ def test_float_repr(self): if not IS_PYPY: # sys.getsizeof() is not valid on PyPy - class TestSizeOf(object): + class TestSizeOf: def test_equal_nbytes(self): for type in types: @@ -558,17 +576,17 @@ def test_error(self): assert_raises(TypeError, d.__sizeof__, "a") -class TestMultiply(object): +class TestMultiply: def test_seq_repeat(self): # Test that basic sequences get repeated when multiplied with # numpy integers. And errors are raised when multiplied with others. # Some of this behaviour may be controversial and could be open for # change. accepted_types = set(np.typecodes["AllInteger"]) - deprecated_types = set('?') + deprecated_types = {'?'} forbidden_types = ( set(np.typecodes["All"]) - accepted_types - deprecated_types) - forbidden_types -= set('V') # can't default-construct void scalars + forbidden_types -= {'V'} # can't default-construct void scalars for seq_type in (list, tuple): seq = seq_type([1, 2, 3]) @@ -595,7 +613,7 @@ def test_no_seq_repeat_basic_array_like(self): # Test that an array-like which does not know how to be multiplied # does not attempt sequence repeat (raise TypeError). # See also gh-7428. - class ArrayLike(object): + class ArrayLike: def __init__(self, arr): self.arr = arr def __array__(self): @@ -609,7 +627,7 @@ def __array__(self): assert_array_equal(np.int_(3) * arr_like, np.full(3, 3)) -class TestNegative(object): +class TestNegative: def test_exceptions(self): a = np.ones((), dtype=np.bool_)[()] assert_raises(TypeError, operator.neg, a) @@ -623,7 +641,7 @@ def test_result(self): assert_equal(operator.neg(a) + a, 0) -class TestSubtract(object): +class TestSubtract: def test_exceptions(self): a = np.ones((), dtype=np.bool_)[()] assert_raises(TypeError, operator.sub, a, a) @@ -637,30 +655,180 @@ def test_result(self): assert_equal(operator.sub(a, a), 0) -class TestAbs(object): - def _test_abs_func(self, absfunc): - for tp in floating_types + complex_floating_types: - x = tp(-1.5) - assert_equal(absfunc(x), 1.5) - x = tp(0.0) - res = absfunc(x) - # assert_equal() checks zero signedness - assert_equal(res, 0.0) - x = tp(-0.0) - res = absfunc(x) - assert_equal(res, 0.0) - - x = tp(np.finfo(tp).max) - assert_equal(absfunc(x), x.real) - - x = tp(np.finfo(tp).tiny) - assert_equal(absfunc(x), x.real) +class TestAbs: + def _test_abs_func(self, absfunc, test_dtype): + x = test_dtype(-1.5) + assert_equal(absfunc(x), 1.5) + x = test_dtype(0.0) + res = absfunc(x) + # assert_equal() checks zero signedness + assert_equal(res, 0.0) + x = test_dtype(-0.0) + res = absfunc(x) + assert_equal(res, 0.0) - x = tp(np.finfo(tp).min) - assert_equal(absfunc(x), -x.real) + x = test_dtype(np.finfo(test_dtype).max) + assert_equal(absfunc(x), x.real) - def test_builtin_abs(self): - self._test_abs_func(abs) + with suppress_warnings() as sup: + sup.filter(UserWarning) + x = test_dtype(np.finfo(test_dtype).tiny) + assert_equal(absfunc(x), x.real) - def test_numpy_abs(self): - self._test_abs_func(np.abs) + x = test_dtype(np.finfo(test_dtype).min) + assert_equal(absfunc(x), -x.real) + + @pytest.mark.parametrize("dtype", floating_types + complex_floating_types) + def test_builtin_abs(self, dtype): + if sys.platform == "cygwin" and dtype == np.clongdouble: + pytest.xfail( + reason="absl is computed in double precision on cygwin" + ) + self._test_abs_func(abs, dtype) + + @pytest.mark.parametrize("dtype", floating_types + complex_floating_types) + def test_numpy_abs(self, dtype): + if sys.platform == "cygwin" and dtype == np.clongdouble: + pytest.xfail( + reason="absl is computed in double precision on cygwin" + ) + self._test_abs_func(np.abs, dtype) + +class TestBitShifts: + + @pytest.mark.parametrize('type_code', np.typecodes['AllInteger']) + @pytest.mark.parametrize('op', + [operator.rshift, operator.lshift], ids=['>>', '<<']) + def test_shift_all_bits(self, type_code, op): + """ Shifts where the shift amount is the width of the type or wider """ + # gh-2449 + dt = np.dtype(type_code) + nbits = dt.itemsize * 8 + for val in [5, -5]: + for shift in [nbits, nbits + 4]: + val_scl = dt.type(val) + shift_scl = dt.type(shift) + res_scl = op(val_scl, shift_scl) + if val_scl < 0 and op is operator.rshift: + # sign bit is preserved + assert_equal(res_scl, -1) + else: + assert_equal(res_scl, 0) + + # Result on scalars should be the same as on arrays + val_arr = np.array([val]*32, dtype=dt) + shift_arr = np.array([shift]*32, dtype=dt) + res_arr = op(val_arr, shift_arr) + assert_equal(res_arr, res_scl) + + +class TestHash: + @pytest.mark.parametrize("type_code", np.typecodes['AllInteger']) + def test_integer_hashes(self, type_code): + scalar = np.dtype(type_code).type + for i in range(128): + assert hash(i) == hash(scalar(i)) + + @pytest.mark.parametrize("type_code", np.typecodes['AllFloat']) + def test_float_and_complex_hashes(self, type_code): + scalar = np.dtype(type_code).type + for val in [np.pi, np.inf, 3, 6.]: + numpy_val = scalar(val) + # Cast back to Python, in case the NumPy scalar has less precision + if numpy_val.dtype.kind == 'c': + val = complex(numpy_val) + else: + val = float(numpy_val) + assert val == numpy_val + print(repr(numpy_val), repr(val)) + assert hash(val) == hash(numpy_val) + + if hash(float(np.nan)) != hash(float(np.nan)): + # If Python distinguises different NaNs we do so too (gh-18833) + assert hash(scalar(np.nan)) != hash(scalar(np.nan)) + + @pytest.mark.parametrize("type_code", np.typecodes['Complex']) + def test_complex_hashes(self, type_code): + # Test some complex valued hashes specifically: + scalar = np.dtype(type_code).type + for val in [np.pi+1j, np.inf-3j, 3j, 6.+1j]: + numpy_val = scalar(val) + assert hash(complex(numpy_val)) == hash(numpy_val) + + +@contextlib.contextmanager +def recursionlimit(n): + o = sys.getrecursionlimit() + try: + sys.setrecursionlimit(n) + yield + finally: + sys.setrecursionlimit(o) + + +objecty_things = [object(), None] +reasonable_operators_for_scalars = [ + operator.lt, operator.le, operator.eq, operator.ne, operator.ge, + operator.gt, operator.add, operator.floordiv, operator.mod, + operator.mul, operator.matmul, operator.pow, operator.sub, + operator.truediv, +] + + +@given(sampled_from(objecty_things), + sampled_from(reasonable_operators_for_scalars), + sampled_from(types)) +@settings(verbosity=Verbosity.verbose) +def test_operator_object_left(o, op, type_): + try: + with recursionlimit(200): + op(o, type_(1)) + except TypeError: + pass + + +@given(sampled_from(objecty_things), + sampled_from(reasonable_operators_for_scalars), + sampled_from(types)) +def test_operator_object_right(o, op, type_): + try: + with recursionlimit(200): + op(type_(1), o) + except TypeError: + pass + + +@given(sampled_from(reasonable_operators_for_scalars), + sampled_from(types), + sampled_from(types)) +def test_operator_scalars(op, type1, type2): + try: + op(type1(1), type2(1)) + except TypeError: + pass + + +@pytest.mark.parametrize("op", reasonable_operators_for_scalars) +def test_longdouble_inf_loop(op): + try: + op(np.longdouble(3), None) + except TypeError: + pass + try: + op(None, np.longdouble(3)) + except TypeError: + pass + + +@pytest.mark.parametrize("op", reasonable_operators_for_scalars) +def test_clongdouble_inf_loop(op): + if op in {operator.mod} and False: + pytest.xfail("The modulo operator is known to be broken") + try: + op(np.clongdouble(3), None) + except TypeError: + pass + try: + op(None, np.longdouble(3)) + except TypeError: + pass diff --git a/numpy/core/tests/test_scalarprint.py b/numpy/core/tests/test_scalarprint.py index 472ff691da17..ee21d4aa5e0d 100644 --- a/numpy/core/tests/test_scalarprint.py +++ b/numpy/core/tests/test_scalarprint.py @@ -1,18 +1,16 @@ -# -*- coding: utf-8 -*- """ Test printing of scalar types. """ -from __future__ import division, absolute_import, print_function - -import code, sys +import code import platform import pytest +import sys from tempfile import TemporaryFile import numpy as np -from numpy.testing import assert_, assert_equal, suppress_warnings, dec +from numpy.testing import assert_, assert_equal, assert_raises -class TestRealScalars(object): +class TestRealScalars: def test_str(self): svals = [0.0, -0.0, 1, -1, np.inf, -np.inf, np.nan] styps = [np.float16, np.float32, np.float64, np.longdouble] @@ -32,12 +30,12 @@ def test_str(self): def test_scalar_cutoffs(self): # test that both the str and repr of np.float64 behaves - # like python floats in python3. Note that in python2 - # the str has truncated digits, but we do not do this + # like python floats in python3. def check(v): - # we compare str to repr, to avoid python2 truncation behavior + assert_equal(str(np.float64(v)), str(v)) assert_equal(str(np.float64(v)), repr(v)) assert_equal(repr(np.float64(v)), repr(v)) + assert_equal(repr(np.float64(v)), str(v)) # check we use the same number of significant digits check(1.12345678901234567890) @@ -51,7 +49,7 @@ def check(v): def test_py2_float_print(self): # gh-10753 - # In python2, the python float type implements an obsolte method + # In python2, the python float type implements an obsolete method # tp_print, which overrides tp_repr and tp_str when using "print" to # output to a "real file" (ie, not a StringIO). Make sure we don't # inherit it. @@ -84,10 +82,7 @@ def userinput(): orig_stdout, orig_stderr = sys.stdout, sys.stderr sys.stdout, sys.stderr = fo, fe - # py2 code.interact sends irrelevant internal DeprecationWarnings - with suppress_warnings() as sup: - sup.filter(DeprecationWarning) - code.interact(local={'np': np}, readfunc=input_func, banner='') + code.interact(local={'np': np}, readfunc=input_func, banner='') sys.stdout, sys.stderr = orig_stdout, orig_stderr @@ -158,7 +153,8 @@ def test_dragon4(self): "0.00000000000000000000000000000000000000000000140129846432" "4817070923729583289916131280261941876515771757068283889791" "08268586060148663818836212158203125") - assert_equal(fpos64(0.5**(1022 + 52), unique=False, precision=1074), + + assert_equal(fpos64(5e-324, unique=False, precision=1074), "0.00000000000000000000000000000000000000000000000000000000" "0000000000000000000000000000000000000000000000000000000000" "0000000000000000000000000000000000000000000000000000000000" @@ -180,7 +176,8 @@ def test_dragon4(self): "87538682506419718265533447265625") # largest numbers - assert_equal(fpos32(np.finfo(np.float32).max, **preckwd(0)), + f32x = np.finfo(np.float32).max + assert_equal(fpos32(f32x, **preckwd(0)), "340282346638528859811704183484516925440.") assert_equal(fpos64(np.finfo(np.float64).max, **preckwd(0)), "1797693134862315708145274237317043567980705675258449965989" @@ -190,9 +187,65 @@ def test_dragon4(self): "6580855933212334827479782620414472316873817718091929988125" "0404026184124858368.") # Warning: In unique mode only the integer digits necessary for - # uniqueness are computed, the rest are 0. Should we change this? - assert_equal(fpos32(np.finfo(np.float32).max, precision=0), + # uniqueness are computed, the rest are 0. + assert_equal(fpos32(f32x), + "340282350000000000000000000000000000000.") + + # Further tests of zero-padding vs rounding in different combinations + # of unique, fractional, precision, min_digits + # precision can only reduce digits, not add them. + # min_digits can only extend digits, not reduce them. + assert_equal(fpos32(f32x, unique=True, fractional=True, precision=0), + "340282350000000000000000000000000000000.") + assert_equal(fpos32(f32x, unique=True, fractional=True, precision=4), "340282350000000000000000000000000000000.") + assert_equal(fpos32(f32x, unique=True, fractional=True, min_digits=0), + "340282346638528859811704183484516925440.") + assert_equal(fpos32(f32x, unique=True, fractional=True, min_digits=4), + "340282346638528859811704183484516925440.0000") + assert_equal(fpos32(f32x, unique=True, fractional=True, + min_digits=4, precision=4), + "340282346638528859811704183484516925440.0000") + assert_raises(ValueError, fpos32, f32x, unique=True, fractional=False, + precision=0) + assert_equal(fpos32(f32x, unique=True, fractional=False, precision=4), + "340300000000000000000000000000000000000.") + assert_equal(fpos32(f32x, unique=True, fractional=False, precision=20), + "340282350000000000000000000000000000000.") + assert_equal(fpos32(f32x, unique=True, fractional=False, min_digits=4), + "340282350000000000000000000000000000000.") + assert_equal(fpos32(f32x, unique=True, fractional=False, + min_digits=20), + "340282346638528859810000000000000000000.") + assert_equal(fpos32(f32x, unique=True, fractional=False, + min_digits=15), + "340282346638529000000000000000000000000.") + assert_equal(fpos32(f32x, unique=False, fractional=False, precision=4), + "340300000000000000000000000000000000000.") + # test that unique rounding is preserved when precision is supplied + # but no extra digits need to be printed (gh-18609) + a = np.float64.fromhex('-1p-97') + assert_equal(fsci64(a, unique=True), '-6.310887241768095e-30') + assert_equal(fsci64(a, unique=False, precision=15), + '-6.310887241768094e-30') + assert_equal(fsci64(a, unique=True, precision=15), + '-6.310887241768095e-30') + assert_equal(fsci64(a, unique=True, min_digits=15), + '-6.310887241768095e-30') + assert_equal(fsci64(a, unique=True, precision=15, min_digits=15), + '-6.310887241768095e-30') + # adds/remove digits in unique mode with unbiased rnding + assert_equal(fsci64(a, unique=True, precision=14), + '-6.31088724176809e-30') + assert_equal(fsci64(a, unique=True, min_digits=16), + '-6.3108872417680944e-30') + assert_equal(fsci64(a, unique=True, precision=16), + '-6.310887241768095e-30') + assert_equal(fsci64(a, unique=True, min_digits=14), + '-6.310887241768095e-30') + # test min_digits in unique mode with different rounding cases + assert_equal(fsci64('1e120', min_digits=3), '1.000e+120') + assert_equal(fsci64('1e100', min_digits=3), '1.000e+100') # test trailing zeros assert_equal(fpos32('1.0', unique=False, precision=3), "1.000") @@ -204,7 +257,9 @@ def test_dragon4(self): assert_equal(fsci32('1.5', unique=False, precision=3), "1.500e+00") assert_equal(fsci64('1.5', unique=False, precision=3), "1.500e+00") # gh-10713 - assert_equal(fpos64('324', unique=False, precision=5, fractional=False), "324.00") + assert_equal(fpos64('324', unique=False, precision=5, + fractional=False), "324.00") + def test_dragon4_interface(self): tps = [np.float16, np.float32, np.float64] diff --git a/numpy/core/tests/test_shape_base.py b/numpy/core/tests/test_shape_base.py index 72b3451a4f8c..679e3c036351 100644 --- a/numpy/core/tests/test_shape_base.py +++ b/numpy/core/tests/test_shape_base.py @@ -1,19 +1,18 @@ -from __future__ import division, absolute_import, print_function - -import warnings +import pytest import numpy as np from numpy.core import ( array, arange, atleast_1d, atleast_2d, atleast_3d, block, vstack, hstack, newaxis, concatenate, stack ) +from numpy.core.shape_base import (_block_dispatcher, _block_setup, + _block_concatenate, _block_slicing) from numpy.testing import ( assert_, assert_raises, assert_array_equal, assert_equal, - assert_raises_regex, assert_almost_equal + assert_raises_regex, assert_warns, IS_PYPY ) -from numpy.compat import long -class TestAtleast1d(object): +class TestAtleast1d: def test_0D_array(self): a = array(1) b = array(2) @@ -49,12 +48,11 @@ def test_r1array(self): """ assert_(atleast_1d(3).shape == (1,)) assert_(atleast_1d(3j).shape == (1,)) - assert_(atleast_1d(long(3)).shape == (1,)) assert_(atleast_1d(3.0).shape == (1,)) assert_(atleast_1d([[2, 3], [4, 5]]).shape == (2, 2)) -class TestAtleast2d(object): +class TestAtleast2d: def test_0D_array(self): a = array(1) b = array(2) @@ -93,7 +91,7 @@ def test_r2array(self): assert_(atleast_2d([[[3, 1], [4, 5]], [[3, 5], [1, 2]]]).shape == (2, 2, 2)) -class TestAtleast3d(object): +class TestAtleast3d: def test_0D_array(self): a = array(1) b = array(2) @@ -125,7 +123,7 @@ def test_3D_array(self): assert_array_equal(res, desired) -class TestHstack(object): +class TestHstack: def test_non_iterable(self): assert_raises(TypeError, hstack, 1) @@ -153,8 +151,14 @@ def test_2D_array(self): desired = array([[1, 1], [2, 2]]) assert_array_equal(res, desired) + def test_generator(self): + with assert_warns(FutureWarning): + hstack((np.arange(3) for _ in range(2))) + with assert_warns(FutureWarning): + hstack(map(lambda x: x, np.ones((3, 2)))) + -class TestVstack(object): +class TestVstack: def test_non_iterable(self): assert_raises(TypeError, vstack, 1) @@ -189,8 +193,18 @@ def test_2D_array2(self): desired = array([[1, 2], [1, 2]]) assert_array_equal(res, desired) + def test_generator(self): + with assert_warns(FutureWarning): + vstack((np.arange(3) for _ in range(2))) + + +class TestConcatenate: + def test_returns_copy(self): + a = np.eye(3) + b = np.concatenate([a]) + b[0, 0] = 2 + assert b[0, 0] != a[0, 0] -class TestConcatenate(object): def test_exceptions(self): # test axis must be in bounds for ndim in [1, 2, 3]: @@ -203,13 +217,27 @@ def test_exceptions(self): assert_raises(ValueError, concatenate, (0,)) assert_raises(ValueError, concatenate, (np.array(0),)) + # dimensionality must match + assert_raises_regex( + ValueError, + r"all the input arrays must have same number of dimensions, but " + r"the array at index 0 has 1 dimension\(s\) and the array at " + r"index 1 has 2 dimension\(s\)", + np.concatenate, (np.zeros(1), np.zeros((1, 1)))) + # test shapes must match except for concatenation axis a = np.ones((1, 2, 3)) b = np.ones((2, 2, 3)) axis = list(range(3)) for i in range(3): np.concatenate((a, b), axis=axis[0]) # OK - assert_raises(ValueError, np.concatenate, (a, b), axis=axis[1]) + assert_raises_regex( + ValueError, + "all the input array dimensions for the concatenation axis " + "must match exactly, but along dimension {}, the array at " + "index 0 has size 1 and the array at index 1 has size 2" + .format(i), + np.concatenate, (a, b), axis=axis[1]) assert_raises(ValueError, np.concatenate, (a, b), axis=axis[2]) a = np.moveaxis(a, -1, 0) b = np.moveaxis(b, -1, 0) @@ -228,7 +256,7 @@ def test_concatenate_axis_None(self): r = np.concatenate((a, b), axis=None) assert_equal(r.size, a.size + len(b)) assert_equal(r.dtype, a.dtype) - r = np.concatenate((a, b, c), axis=None) + r = np.concatenate((a, b, c), axis=None, dtype="U") d = array(['0.0', '1.0', '2.0', '3.0', '0', '1', '2', 'x']) assert_array_equal(r, d) @@ -292,6 +320,19 @@ def test_concatenate(self): assert_(out is rout) assert_equal(res, rout) + @pytest.mark.skipif(IS_PYPY, reason="PYPY handles sq_concat, nb_add differently than cpython") + def test_operator_concat(self): + import operator + a = array([1, 2]) + b = array([3, 4]) + n = [1,2] + res = array([1, 2, 3, 4]) + assert_raises(TypeError, operator.concat, a, b) + assert_raises(TypeError, operator.concat, a, n) + assert_raises(TypeError, operator.concat, n, a) + assert_raises(TypeError, operator.concat, a, 1) + assert_raises(TypeError, operator.concat, 1, a) + def test_bad_out_shape(self): a = array([1, 2]) b = array([3, 4]) @@ -301,19 +342,55 @@ def test_bad_out_shape(self): assert_raises(ValueError, concatenate, (a, b), out=np.empty((1,4))) concatenate((a, b), out=np.empty(4)) - def test_out_dtype(self): - out = np.empty(4, np.float32) - res = concatenate((array([1, 2]), array([3, 4])), out=out) - assert_(out is res) - - out = np.empty(4, np.complex64) - res = concatenate((array([0.1, 0.2]), array([0.3, 0.4])), out=out) - assert_(out is res) - - # invalid cast - out = np.empty(4, np.int32) - assert_raises(TypeError, concatenate, - (array([0.1, 0.2]), array([0.3, 0.4])), out=out) + @pytest.mark.parametrize("axis", [None, 0]) + @pytest.mark.parametrize("out_dtype", ["c8", "f4", "f8", ">f8", "i8", "S4"]) + @pytest.mark.parametrize("casting", + ['no', 'equiv', 'safe', 'same_kind', 'unsafe']) + def test_out_and_dtype(self, axis, out_dtype, casting): + # Compare usage of `out=out` with `dtype=out.dtype` + out = np.empty(4, dtype=out_dtype) + to_concat = (array([1.1, 2.2]), array([3.3, 4.4])) + + if not np.can_cast(to_concat[0], out_dtype, casting=casting): + with assert_raises(TypeError): + concatenate(to_concat, out=out, axis=axis, casting=casting) + with assert_raises(TypeError): + concatenate(to_concat, dtype=out.dtype, + axis=axis, casting=casting) + else: + res_out = concatenate(to_concat, out=out, + axis=axis, casting=casting) + res_dtype = concatenate(to_concat, dtype=out.dtype, + axis=axis, casting=casting) + assert res_out is out + assert_array_equal(out, res_dtype) + assert res_dtype.dtype == out_dtype + + with assert_raises(TypeError): + concatenate(to_concat, out=out, dtype=out_dtype, axis=axis) + + @pytest.mark.parametrize("axis", [None, 0]) + @pytest.mark.parametrize("string_dt", ["S", "U", "S0", "U0"]) + @pytest.mark.parametrize("arrs", + [([0.],), ([0.], [1]), ([0], ["string"], [1.])]) + def test_dtype_with_promotion(self, arrs, string_dt, axis): + # Note that U0 and S0 should be deprecated eventually and changed to + # actually give the empty string result (together with `np.array`) + res = np.concatenate(arrs, axis=axis, dtype=string_dt, casting="unsafe") + # The actual dtype should be identical to a cast (of a double array): + assert res.dtype == np.array(1.).astype(string_dt).dtype + + @pytest.mark.parametrize("axis", [None, 0]) + def test_string_dtype_does_not_inspect(self, axis): + with pytest.raises(TypeError): + np.concatenate(([None], [1]), dtype="S", axis=axis) + with pytest.raises(TypeError): + np.concatenate(([None], [1]), dtype="U", axis=axis) + + @pytest.mark.parametrize("axis", [None, 0]) + def test_subarray_error(self, axis): + with pytest.raises(TypeError, match=".*subarray dtype"): + np.concatenate(([1], [1]), dtype="(2,)i", axis=axis) def test_stack(): @@ -346,12 +423,16 @@ def test_stack(): arrays = [np.random.randn(3, 4) for _ in range(10)] axes = [0, 1, 2, -1, -2, -3] expected_shapes = [(10, 3, 4), (3, 10, 4), (3, 4, 10), - (3, 4, 10), (3, 10, 4), (10, 3, 4)] + (3, 4, 10), (3, 10, 4), (10, 3, 4)] for axis, expected_shape in zip(axes, expected_shapes): assert_equal(np.stack(arrays, axis).shape, expected_shape) # empty arrays assert_(stack([[], [], []]).shape == (3, 0)) assert_(stack([[], [], []], axis=1).shape == (0, 3)) + # out + out = np.zeros_like(r1) + np.stack((a, b), out=out) + assert_array_equal(out, r1) # edge cases assert_raises_regex(ValueError, 'need at least one array', stack, []) assert_raises_regex(ValueError, 'must have the same shape', @@ -364,10 +445,62 @@ def test_stack(): stack, [np.zeros((3, 3)), np.zeros(3)], axis=1) assert_raises_regex(ValueError, 'must have the same shape', stack, [np.arange(2), np.arange(3)]) - - -class TestBlock(object): - def test_block_simple_row_wise(self): + # generator is deprecated + with assert_warns(FutureWarning): + result = stack((x for x in range(3))) + assert_array_equal(result, np.array([0, 1, 2])) + + +class TestBlock: + @pytest.fixture(params=['block', 'force_concatenate', 'force_slicing']) + def block(self, request): + # blocking small arrays and large arrays go through different paths. + # the algorithm is triggered depending on the number of element + # copies required. + # We define a test fixture that forces most tests to go through + # both code paths. + # Ultimately, this should be removed if a single algorithm is found + # to be faster for both small and large arrays. + def _block_force_concatenate(arrays): + arrays, list_ndim, result_ndim, _ = _block_setup(arrays) + return _block_concatenate(arrays, list_ndim, result_ndim) + + def _block_force_slicing(arrays): + arrays, list_ndim, result_ndim, _ = _block_setup(arrays) + return _block_slicing(arrays, list_ndim, result_ndim) + + if request.param == 'force_concatenate': + return _block_force_concatenate + elif request.param == 'force_slicing': + return _block_force_slicing + elif request.param == 'block': + return block + else: + raise ValueError('Unknown blocking request. There is a typo in the tests.') + + def test_returns_copy(self, block): + a = np.eye(3) + b = block(a) + b[0, 0] = 2 + assert b[0, 0] != a[0, 0] + + def test_block_total_size_estimate(self, block): + _, _, _, total_size = _block_setup([1]) + assert total_size == 1 + + _, _, _, total_size = _block_setup([[1]]) + assert total_size == 1 + + _, _, _, total_size = _block_setup([[1, 1]]) + assert total_size == 2 + + _, _, _, total_size = _block_setup([[1], [1]]) + assert total_size == 2 + + _, _, _, total_size = _block_setup([[1, 2], [3, 4]]) + assert total_size == 4 + + def test_block_simple_row_wise(self, block): a_2d = np.ones((2, 2)) b_2d = 2 * a_2d desired = np.array([[1, 1, 2, 2], @@ -375,7 +508,7 @@ def test_block_simple_row_wise(self): result = block([a_2d, b_2d]) assert_equal(desired, result) - def test_block_simple_column_wise(self): + def test_block_simple_column_wise(self, block): a_2d = np.ones((2, 2)) b_2d = 2 * a_2d expected = np.array([[1, 1], @@ -385,7 +518,7 @@ def test_block_simple_column_wise(self): result = block([[a_2d], [b_2d]]) assert_equal(expected, result) - def test_block_with_1d_arrays_row_wise(self): + def test_block_with_1d_arrays_row_wise(self, block): # # # 1-D vectors are treated as row arrays a = np.array([1, 2, 3]) b = np.array([2, 3, 4]) @@ -393,7 +526,7 @@ def test_block_with_1d_arrays_row_wise(self): result = block([a, b]) assert_equal(expected, result) - def test_block_with_1d_arrays_multiple_rows(self): + def test_block_with_1d_arrays_multiple_rows(self, block): a = np.array([1, 2, 3]) b = np.array([2, 3, 4]) expected = np.array([[1, 2, 3, 2, 3, 4], @@ -401,7 +534,7 @@ def test_block_with_1d_arrays_multiple_rows(self): result = block([[a, b], [a, b]]) assert_equal(expected, result) - def test_block_with_1d_arrays_column_wise(self): + def test_block_with_1d_arrays_column_wise(self, block): # # # 1-D vectors are treated as row arrays a_1d = np.array([1, 2, 3]) b_1d = np.array([2, 3, 4]) @@ -410,7 +543,7 @@ def test_block_with_1d_arrays_column_wise(self): result = block([[a_1d], [b_1d]]) assert_equal(expected, result) - def test_block_mixed_1d_and_2d(self): + def test_block_mixed_1d_and_2d(self, block): a_2d = np.ones((2, 2)) b_1d = np.array([2, 2]) result = block([[a_2d], [b_1d]]) @@ -419,7 +552,7 @@ def test_block_mixed_1d_and_2d(self): [2, 2]]) assert_equal(expected, result) - def test_block_complicated(self): + def test_block_complicated(self, block): # a bit more complicated one_2d = np.array([[1, 1, 1]]) two_2d = np.array([[2, 2, 2]]) @@ -443,7 +576,7 @@ def test_block_complicated(self): [zero_2d]]) assert_equal(result, expected) - def test_nested(self): + def test_nested(self, block): one = np.array([1, 1, 1]) two = np.array([[2, 2, 2], [2, 2, 2], [2, 2, 2]]) three = np.array([3, 3, 3]) @@ -452,9 +585,9 @@ def test_nested(self): six = np.array([6, 6, 6, 6, 6]) zero = np.zeros((2, 6)) - result = np.block([ + result = block([ [ - np.block([ + block([ [one], [three], [four] @@ -473,7 +606,7 @@ def test_nested(self): assert_equal(result, expected) - def test_3d(self): + def test_3d(self, block): a000 = np.ones((2, 2, 2), int) * 1 a100 = np.ones((3, 2, 2), int) * 2 @@ -486,7 +619,7 @@ def test_3d(self): a111 = np.ones((3, 3, 3), int) * 8 - result = np.block([ + result = block([ [ [a000, a001], [a010, a011], @@ -528,55 +661,102 @@ def test_3d(self): assert_array_equal(result, expected) - def test_block_with_mismatched_shape(self): + def test_block_with_mismatched_shape(self, block): a = np.array([0, 0]) b = np.eye(2) - assert_raises(ValueError, np.block, [a, b]) - assert_raises(ValueError, np.block, [b, a]) + assert_raises(ValueError, block, [a, b]) + assert_raises(ValueError, block, [b, a]) - def test_no_lists(self): - assert_equal(np.block(1), np.array(1)) - assert_equal(np.block(np.eye(3)), np.eye(3)) + to_block = [[np.ones((2,3)), np.ones((2,2))], + [np.ones((2,2)), np.ones((2,2))]] + assert_raises(ValueError, block, to_block) + def test_no_lists(self, block): + assert_equal(block(1), np.array(1)) + assert_equal(block(np.eye(3)), np.eye(3)) - def test_invalid_nesting(self): + def test_invalid_nesting(self, block): msg = 'depths are mismatched' - assert_raises_regex(ValueError, msg, np.block, [1, [2]]) - assert_raises_regex(ValueError, msg, np.block, [1, []]) - assert_raises_regex(ValueError, msg, np.block, [[1], 2]) - assert_raises_regex(ValueError, msg, np.block, [[], 2]) - assert_raises_regex(ValueError, msg, np.block, [ + assert_raises_regex(ValueError, msg, block, [1, [2]]) + assert_raises_regex(ValueError, msg, block, [1, []]) + assert_raises_regex(ValueError, msg, block, [[1], 2]) + assert_raises_regex(ValueError, msg, block, [[], 2]) + assert_raises_regex(ValueError, msg, block, [ [[1], [2]], [[3, 4]], [5] # missing brackets ]) - def test_empty_lists(self): - assert_raises_regex(ValueError, 'empty', np.block, []) - assert_raises_regex(ValueError, 'empty', np.block, [[]]) - assert_raises_regex(ValueError, 'empty', np.block, [[1], []]) + def test_empty_lists(self, block): + assert_raises_regex(ValueError, 'empty', block, []) + assert_raises_regex(ValueError, 'empty', block, [[]]) + assert_raises_regex(ValueError, 'empty', block, [[1], []]) - def test_tuple(self): - assert_raises_regex(TypeError, 'tuple', np.block, ([1, 2], [3, 4])) - assert_raises_regex(TypeError, 'tuple', np.block, [(1, 2), (3, 4)]) + def test_tuple(self, block): + assert_raises_regex(TypeError, 'tuple', block, ([1, 2], [3, 4])) + assert_raises_regex(TypeError, 'tuple', block, [(1, 2), (3, 4)]) - def test_different_ndims(self): + def test_different_ndims(self, block): a = 1. b = 2 * np.ones((1, 2)) c = 3 * np.ones((1, 1, 3)) - result = np.block([a, b, c]) + result = block([a, b, c]) expected = np.array([[[1., 2., 2., 3., 3., 3.]]]) assert_equal(result, expected) - def test_different_ndims_depths(self): + def test_different_ndims_depths(self, block): a = 1. b = 2 * np.ones((1, 2)) c = 3 * np.ones((1, 2, 3)) - result = np.block([[a, b], [c]]) + result = block([[a, b], [c]]) expected = np.array([[[1., 2., 2.], [3., 3., 3.], [3., 3., 3.]]]) assert_equal(result, expected) + + def test_block_memory_order(self, block): + # 3D + arr_c = np.zeros((3,)*3, order='C') + arr_f = np.zeros((3,)*3, order='F') + + b_c = [[[arr_c, arr_c], + [arr_c, arr_c]], + [[arr_c, arr_c], + [arr_c, arr_c]]] + + b_f = [[[arr_f, arr_f], + [arr_f, arr_f]], + [[arr_f, arr_f], + [arr_f, arr_f]]] + + assert block(b_c).flags['C_CONTIGUOUS'] + assert block(b_f).flags['F_CONTIGUOUS'] + + arr_c = np.zeros((3, 3), order='C') + arr_f = np.zeros((3, 3), order='F') + # 2D + b_c = [[arr_c, arr_c], + [arr_c, arr_c]] + + b_f = [[arr_f, arr_f], + [arr_f, arr_f]] + + assert block(b_c).flags['C_CONTIGUOUS'] + assert block(b_f).flags['F_CONTIGUOUS'] + + +def test_block_dispatcher(): + class ArrayLike: + pass + a = ArrayLike() + b = ArrayLike() + c = ArrayLike() + assert_equal(list(_block_dispatcher(a)), [a]) + assert_equal(list(_block_dispatcher([a])), [a]) + assert_equal(list(_block_dispatcher([a, b])), [a, b]) + assert_equal(list(_block_dispatcher([[a], [b, [c]]])), [a, b, c]) + # don't recurse into non-lists + assert_equal(list(_block_dispatcher((a, b))), [(a, b)]) diff --git a/numpy/core/tests/test_simd.py b/numpy/core/tests/test_simd.py new file mode 100644 index 000000000000..12a67c44dde5 --- /dev/null +++ b/numpy/core/tests/test_simd.py @@ -0,0 +1,981 @@ +# NOTE: Please avoid the use of numpy.testing since NPYV intrinsics +# may be involved in their functionality. +import pytest, math, re +import itertools +from numpy.core._simd import targets +from numpy.core._multiarray_umath import __cpu_baseline__ + +class _Test_Utility: + # submodule of the desired SIMD extension, e.g. targets["AVX512F"] + npyv = None + # the current data type suffix e.g. 's8' + sfx = None + # target name can be 'baseline' or one or more of CPU features + target_name = None + + def __getattr__(self, attr): + """ + To call NPV intrinsics without the attribute 'npyv' and + auto suffixing intrinsics according to class attribute 'sfx' + """ + return getattr(self.npyv, attr + "_" + self.sfx) + + def _data(self, start=None, count=None, reverse=False): + """ + Create list of consecutive numbers according to number of vector's lanes. + """ + if start is None: + start = 1 + if count is None: + count = self.nlanes + rng = range(start, start + count) + if reverse: + rng = reversed(rng) + if self._is_fp(): + return [x / 1.0 for x in rng] + return list(rng) + + def _is_unsigned(self): + return self.sfx[0] == 'u' + + def _is_signed(self): + return self.sfx[0] == 's' + + def _is_fp(self): + return self.sfx[0] == 'f' + + def _scalar_size(self): + return int(self.sfx[1:]) + + def _int_clip(self, seq): + if self._is_fp(): + return seq + max_int = self._int_max() + min_int = self._int_min() + return [min(max(v, min_int), max_int) for v in seq] + + def _int_max(self): + if self._is_fp(): + return None + max_u = self._to_unsigned(self.setall(-1))[0] + if self._is_signed(): + return max_u // 2 + return max_u + + def _int_min(self): + if self._is_fp(): + return None + if self._is_unsigned(): + return 0 + return -(self._int_max() + 1) + + def _true_mask(self): + max_unsig = getattr(self.npyv, "setall_u" + self.sfx[1:])(-1) + return max_unsig[0] + + def _to_unsigned(self, vector): + if isinstance(vector, (list, tuple)): + return getattr(self.npyv, "load_u" + self.sfx[1:])(vector) + else: + sfx = vector.__name__.replace("npyv_", "") + if sfx[0] == "b": + cvt_intrin = "cvt_u{0}_b{0}" + else: + cvt_intrin = "reinterpret_u{0}_{1}" + return getattr(self.npyv, cvt_intrin.format(sfx[1:], sfx))(vector) + + def _pinfinity(self): + v = self.npyv.setall_u32(0x7f800000) + return self.npyv.reinterpret_f32_u32(v)[0] + + def _ninfinity(self): + v = self.npyv.setall_u32(0xff800000) + return self.npyv.reinterpret_f32_u32(v)[0] + + def _nan(self): + v = self.npyv.setall_u32(0x7fc00000) + return self.npyv.reinterpret_f32_u32(v)[0] + + def _cpu_features(self): + target = self.target_name + if target == "baseline": + target = __cpu_baseline__ + else: + target = target.split('__') # multi-target separator + return ' '.join(target) + +class _SIMD_BOOL(_Test_Utility): + """ + To test all boolean vector types at once + """ + def _data(self, start=None, count=None, reverse=False): + nlanes = getattr(self.npyv, "nlanes_u" + self.sfx[1:]) + true_mask = self._true_mask() + rng = range(nlanes) + if reverse: + rng = reversed(rng) + return [true_mask if x % 2 else 0 for x in rng] + + def _load_b(self, data): + len_str = self.sfx[1:] + load = getattr(self.npyv, "load_u" + len_str) + cvt = getattr(self.npyv, f"cvt_b{len_str}_u{len_str}") + return cvt(load(data)) + + def test_operators_logical(self): + """ + Logical operations for boolean types. + Test intrinsics: + npyv_xor_##SFX, npyv_and_##SFX, npyv_or_##SFX, npyv_not_##SFX + """ + data_a = self._data() + data_b = self._data(reverse=True) + vdata_a = self._load_b(data_a) + vdata_b = self._load_b(data_b) + + data_and = [a & b for a, b in zip(data_a, data_b)] + vand = getattr(self, "and")(vdata_a, vdata_b) + assert vand == data_and + + data_or = [a | b for a, b in zip(data_a, data_b)] + vor = getattr(self, "or")(vdata_a, vdata_b) + assert vor == data_or + + data_xor = [a ^ b for a, b in zip(data_a, data_b)] + vxor = getattr(self, "xor")(vdata_a, vdata_b) + assert vxor == data_xor + + vnot = getattr(self, "not")(vdata_a) + assert vnot == data_b + + def test_tobits(self): + data2bits = lambda data: sum([int(x != 0) << i for i, x in enumerate(data, 0)]) + for data in (self._data(), self._data(reverse=True)): + vdata = self._load_b(data) + data_bits = data2bits(data) + tobits = bin(self.tobits(vdata)) + assert tobits == bin(data_bits) + +class _SIMD_INT(_Test_Utility): + """ + To test all integer vector types at once + """ + def test_operators_shift(self): + if self.sfx in ("u8", "s8"): + return + + data_a = self._data(self._int_max() - self.nlanes) + data_b = self._data(self._int_min(), reverse=True) + vdata_a, vdata_b = self.load(data_a), self.load(data_b) + + for count in range(self._scalar_size()): + # load to cast + data_shl_a = self.load([a << count for a in data_a]) + # left shift + shl = self.shl(vdata_a, count) + assert shl == data_shl_a + # load to cast + data_shr_a = self.load([a >> count for a in data_a]) + # right shift + shr = self.shr(vdata_a, count) + assert shr == data_shr_a + + # shift by zero or max or out-range immediate constant is not applicable and illogical + for count in range(1, self._scalar_size()): + # load to cast + data_shl_a = self.load([a << count for a in data_a]) + # left shift by an immediate constant + shli = self.shli(vdata_a, count) + assert shli == data_shl_a + # load to cast + data_shr_a = self.load([a >> count for a in data_a]) + # right shift by an immediate constant + shri = self.shri(vdata_a, count) + assert shri == data_shr_a + + def test_arithmetic_subadd_saturated(self): + if self.sfx in ("u32", "s32", "u64", "s64"): + return + + data_a = self._data(self._int_max() - self.nlanes) + data_b = self._data(self._int_min(), reverse=True) + vdata_a, vdata_b = self.load(data_a), self.load(data_b) + + data_adds = self._int_clip([a + b for a, b in zip(data_a, data_b)]) + adds = self.adds(vdata_a, vdata_b) + assert adds == data_adds + + data_subs = self._int_clip([a - b for a, b in zip(data_a, data_b)]) + subs = self.subs(vdata_a, vdata_b) + assert subs == data_subs + + def test_math_max_min(self): + data_a = self._data() + data_b = self._data(self.nlanes) + vdata_a, vdata_b = self.load(data_a), self.load(data_b) + + data_max = [max(a, b) for a, b in zip(data_a, data_b)] + simd_max = self.max(vdata_a, vdata_b) + assert simd_max == data_max + + data_min = [min(a, b) for a, b in zip(data_a, data_b)] + simd_min = self.min(vdata_a, vdata_b) + assert simd_min == data_min + +class _SIMD_FP32(_Test_Utility): + """ + To only test single precision + """ + def test_conversions(self): + """ + Round to nearest even integer, assume CPU control register is set to rounding. + Test intrinsics: + npyv_round_s32_##SFX + """ + features = self._cpu_features() + if not self.npyv.simd_f64 and re.match(r".*(NEON|ASIMD)", features): + # very costly to emulate nearest even on Armv7 + # instead we round halves to up. e.g. 0.5 -> 1, -0.5 -> -1 + _round = lambda v: int(v + (0.5 if v >= 0 else -0.5)) + else: + _round = round + vdata_a = self.load(self._data()) + vdata_a = self.sub(vdata_a, self.setall(0.5)) + data_round = [_round(x) for x in vdata_a] + vround = self.round_s32(vdata_a) + assert vround == data_round + +class _SIMD_FP64(_Test_Utility): + """ + To only test double precision + """ + def test_conversions(self): + """ + Round to nearest even integer, assume CPU control register is set to rounding. + Test intrinsics: + npyv_round_s32_##SFX + """ + vdata_a = self.load(self._data()) + vdata_a = self.sub(vdata_a, self.setall(0.5)) + vdata_b = self.mul(vdata_a, self.setall(-1.5)) + data_round = [round(x) for x in list(vdata_a) + list(vdata_b)] + vround = self.round_s32(vdata_a, vdata_b) + assert vround == data_round + +class _SIMD_FP(_Test_Utility): + """ + To test all float vector types at once + """ + def test_arithmetic_fused(self): + vdata_a, vdata_b, vdata_c = [self.load(self._data())]*3 + vdata_cx2 = self.add(vdata_c, vdata_c) + # multiply and add, a*b + c + data_fma = self.load([a * b + c for a, b, c in zip(vdata_a, vdata_b, vdata_c)]) + fma = self.muladd(vdata_a, vdata_b, vdata_c) + assert fma == data_fma + # multiply and subtract, a*b - c + fms = self.mulsub(vdata_a, vdata_b, vdata_c) + data_fms = self.sub(data_fma, vdata_cx2) + assert fms == data_fms + # negate multiply and add, -(a*b) + c + nfma = self.nmuladd(vdata_a, vdata_b, vdata_c) + data_nfma = self.sub(vdata_cx2, data_fma) + assert nfma == data_nfma + # negate multiply and subtract, -(a*b) - c + nfms = self.nmulsub(vdata_a, vdata_b, vdata_c) + data_nfms = self.mul(data_fma, self.setall(-1)) + assert nfms == data_nfms + + def test_abs(self): + pinf, ninf, nan = self._pinfinity(), self._ninfinity(), self._nan() + data = self._data() + vdata = self.load(self._data()) + + abs_cases = ((-0, 0), (ninf, pinf), (pinf, pinf), (nan, nan)) + for case, desired in abs_cases: + data_abs = [desired]*self.nlanes + vabs = self.abs(self.setall(case)) + assert vabs == pytest.approx(data_abs, nan_ok=True) + + vabs = self.abs(self.mul(vdata, self.setall(-1))) + assert vabs == data + + def test_sqrt(self): + pinf, ninf, nan = self._pinfinity(), self._ninfinity(), self._nan() + data = self._data() + vdata = self.load(self._data()) + + sqrt_cases = ((-0.0, -0.0), (0.0, 0.0), (-1.0, nan), (ninf, nan), (pinf, pinf)) + for case, desired in sqrt_cases: + data_sqrt = [desired]*self.nlanes + sqrt = self.sqrt(self.setall(case)) + assert sqrt == pytest.approx(data_sqrt, nan_ok=True) + + data_sqrt = self.load([math.sqrt(x) for x in data]) # load to truncate precision + sqrt = self.sqrt(vdata) + assert sqrt == data_sqrt + + def test_square(self): + pinf, ninf, nan = self._pinfinity(), self._ninfinity(), self._nan() + data = self._data() + vdata = self.load(self._data()) + # square + square_cases = ((nan, nan), (pinf, pinf), (ninf, pinf)) + for case, desired in square_cases: + data_square = [desired]*self.nlanes + square = self.square(self.setall(case)) + assert square == pytest.approx(data_square, nan_ok=True) + + data_square = [x*x for x in data] + square = self.square(vdata) + assert square == data_square + + @pytest.mark.parametrize("intrin, func", [("self.ceil", math.ceil), + ("self.trunc", math.trunc)]) + def test_rounding(self, intrin, func): + """ + Test intrinsics: + npyv_ceil_##SFX + npyv_trunc_##SFX + """ + intrin_name = intrin + intrin = eval(intrin) + pinf, ninf, nan = self._pinfinity(), self._ninfinity(), self._nan() + # special cases + round_cases = ((nan, nan), (pinf, pinf), (ninf, ninf)) + for case, desired in round_cases: + data_round = [desired]*self.nlanes + _round = intrin(self.setall(case)) + assert _round == pytest.approx(data_round, nan_ok=True) + for x in range(0, 2**20, 256**2): + for w in (-1.05, -1.10, -1.15, 1.05, 1.10, 1.15): + data = [x*w+a for a in range(self.nlanes)] + vdata = self.load(data) + data_round = [func(x) for x in data] + _round = intrin(vdata) + assert _round == data_round + # signed zero + if "ceil" in intrin_name or "trunc" in intrin_name: + for w in (-0.25, -0.30, -0.45): + _round = self._to_unsigned(intrin(self.setall(w))) + data_round = self._to_unsigned(self.setall(-0.0)) + assert _round == data_round + + def test_max(self): + """ + Test intrinsics: + npyv_max_##SFX + npyv_maxp_##SFX + """ + data_a = self._data() + data_b = self._data(self.nlanes) + vdata_a, vdata_b = self.load(data_a), self.load(data_b) + data_max = [max(a, b) for a, b in zip(data_a, data_b)] + _max = self.max(vdata_a, vdata_b) + assert _max == data_max + maxp = self.maxp(vdata_a, vdata_b) + assert maxp == data_max + # test IEEE standards + pinf, ninf, nan = self._pinfinity(), self._ninfinity(), self._nan() + max_cases = ((nan, nan, nan), (nan, 10, 10), (10, nan, 10), + (pinf, pinf, pinf), (pinf, 10, pinf), (10, pinf, pinf), + (ninf, ninf, ninf), (ninf, 10, 10), (10, ninf, 10), + (10, 0, 10), (10, -10, 10)) + for case_operand1, case_operand2, desired in max_cases: + data_max = [desired]*self.nlanes + vdata_a = self.setall(case_operand1) + vdata_b = self.setall(case_operand2) + maxp = self.maxp(vdata_a, vdata_b) + assert maxp == pytest.approx(data_max, nan_ok=True) + if nan in (case_operand1, case_operand2, desired): + continue + _max = self.max(vdata_a, vdata_b) + assert _max == data_max + + def test_min(self): + """ + Test intrinsics: + npyv_min_##SFX + npyv_minp_##SFX + """ + data_a = self._data() + data_b = self._data(self.nlanes) + vdata_a, vdata_b = self.load(data_a), self.load(data_b) + data_min = [min(a, b) for a, b in zip(data_a, data_b)] + _min = self.min(vdata_a, vdata_b) + assert _min == data_min + minp = self.minp(vdata_a, vdata_b) + assert minp == data_min + # test IEEE standards + pinf, ninf, nan = self._pinfinity(), self._ninfinity(), self._nan() + min_cases = ((nan, nan, nan), (nan, 10, 10), (10, nan, 10), + (pinf, pinf, pinf), (pinf, 10, 10), (10, pinf, 10), + (ninf, ninf, ninf), (ninf, 10, ninf), (10, ninf, ninf), + (10, 0, 0), (10, -10, -10)) + for case_operand1, case_operand2, desired in min_cases: + data_min = [desired]*self.nlanes + vdata_a = self.setall(case_operand1) + vdata_b = self.setall(case_operand2) + minp = self.minp(vdata_a, vdata_b) + assert minp == pytest.approx(data_min, nan_ok=True) + if nan in (case_operand1, case_operand2, desired): + continue + _min = self.min(vdata_a, vdata_b) + assert _min == data_min + + def test_reciprocal(self): + pinf, ninf, nan = self._pinfinity(), self._ninfinity(), self._nan() + data = self._data() + vdata = self.load(self._data()) + + recip_cases = ((nan, nan), (pinf, 0.0), (ninf, -0.0), (0.0, pinf), (-0.0, ninf)) + for case, desired in recip_cases: + data_recip = [desired]*self.nlanes + recip = self.recip(self.setall(case)) + assert recip == pytest.approx(data_recip, nan_ok=True) + + data_recip = self.load([1/x for x in data]) # load to truncate precision + recip = self.recip(vdata) + assert recip == data_recip + + def test_special_cases(self): + """ + Compare Not NaN. Test intrinsics: + npyv_notnan_##SFX + """ + nnan = self.notnan(self.setall(self._nan())) + assert nnan == [0]*self.nlanes + +class _SIMD_ALL(_Test_Utility): + """ + To test all vector types at once + """ + def test_memory_load(self): + data = self._data() + # unaligned load + load_data = self.load(data) + assert load_data == data + # aligned load + loada_data = self.loada(data) + assert loada_data == data + # stream load + loads_data = self.loads(data) + assert loads_data == data + # load lower part + loadl = self.loadl(data) + loadl_half = list(loadl)[:self.nlanes//2] + data_half = data[:self.nlanes//2] + assert loadl_half == data_half + assert loadl != data # detect overflow + + def test_memory_store(self): + data = self._data() + vdata = self.load(data) + # unaligned store + store = [0] * self.nlanes + self.store(store, vdata) + assert store == data + # aligned store + store_a = [0] * self.nlanes + self.storea(store_a, vdata) + assert store_a == data + # stream store + store_s = [0] * self.nlanes + self.stores(store_s, vdata) + assert store_s == data + # store lower part + store_l = [0] * self.nlanes + self.storel(store_l, vdata) + assert store_l[:self.nlanes//2] == data[:self.nlanes//2] + assert store_l != vdata # detect overflow + # store higher part + store_h = [0] * self.nlanes + self.storeh(store_h, vdata) + assert store_h[:self.nlanes//2] == data[self.nlanes//2:] + assert store_h != vdata # detect overflow + + def test_memory_partial_load(self): + if self.sfx in ("u8", "s8", "u16", "s16"): + return + + data = self._data() + lanes = list(range(1, self.nlanes + 1)) + lanes += [self.nlanes**2, self.nlanes**4] # test out of range + for n in lanes: + load_till = self.load_till(data, n, 15) + data_till = data[:n] + [15] * (self.nlanes-n) + assert load_till == data_till + load_tillz = self.load_tillz(data, n) + data_tillz = data[:n] + [0] * (self.nlanes-n) + assert load_tillz == data_tillz + + def test_memory_partial_store(self): + if self.sfx in ("u8", "s8", "u16", "s16"): + return + + data = self._data() + data_rev = self._data(reverse=True) + vdata = self.load(data) + lanes = list(range(1, self.nlanes + 1)) + lanes += [self.nlanes**2, self.nlanes**4] + for n in lanes: + data_till = data_rev.copy() + data_till[:n] = data[:n] + store_till = self._data(reverse=True) + self.store_till(store_till, n, vdata) + assert store_till == data_till + + def test_memory_noncont_load(self): + if self.sfx in ("u8", "s8", "u16", "s16"): + return + + for stride in range(1, 64): + data = self._data(count=stride*self.nlanes) + data_stride = data[::stride] + loadn = self.loadn(data, stride) + assert loadn == data_stride + + for stride in range(-64, 0): + data = self._data(stride, -stride*self.nlanes) + data_stride = self.load(data[::stride]) # cast unsigned + loadn = self.loadn(data, stride) + assert loadn == data_stride + + def test_memory_noncont_partial_load(self): + if self.sfx in ("u8", "s8", "u16", "s16"): + return + + lanes = list(range(1, self.nlanes + 1)) + lanes += [self.nlanes**2, self.nlanes**4] + for stride in range(1, 64): + data = self._data(count=stride*self.nlanes) + data_stride = data[::stride] + for n in lanes: + data_stride_till = data_stride[:n] + [15] * (self.nlanes-n) + loadn_till = self.loadn_till(data, stride, n, 15) + assert loadn_till == data_stride_till + data_stride_tillz = data_stride[:n] + [0] * (self.nlanes-n) + loadn_tillz = self.loadn_tillz(data, stride, n) + assert loadn_tillz == data_stride_tillz + + for stride in range(-64, 0): + data = self._data(stride, -stride*self.nlanes) + data_stride = list(self.load(data[::stride])) # cast unsigned + for n in lanes: + data_stride_till = data_stride[:n] + [15] * (self.nlanes-n) + loadn_till = self.loadn_till(data, stride, n, 15) + assert loadn_till == data_stride_till + data_stride_tillz = data_stride[:n] + [0] * (self.nlanes-n) + loadn_tillz = self.loadn_tillz(data, stride, n) + assert loadn_tillz == data_stride_tillz + + def test_memory_noncont_store(self): + if self.sfx in ("u8", "s8", "u16", "s16"): + return + + vdata = self.load(self._data()) + for stride in range(1, 64): + data = [15] * stride * self.nlanes + data[::stride] = vdata + storen = [15] * stride * self.nlanes + storen += [127]*64 + self.storen(storen, stride, vdata) + assert storen[:-64] == data + assert storen[-64:] == [127]*64 # detect overflow + + for stride in range(-64, 0): + data = [15] * -stride * self.nlanes + data[::stride] = vdata + storen = [127]*64 + storen += [15] * -stride * self.nlanes + self.storen(storen, stride, vdata) + assert storen[64:] == data + assert storen[:64] == [127]*64 # detect overflow + + def test_memory_noncont_partial_store(self): + if self.sfx in ("u8", "s8", "u16", "s16"): + return + + data = self._data() + vdata = self.load(data) + lanes = list(range(1, self.nlanes + 1)) + lanes += [self.nlanes**2, self.nlanes**4] + for stride in range(1, 64): + for n in lanes: + data_till = [15] * stride * self.nlanes + data_till[::stride] = data[:n] + [15] * (self.nlanes-n) + storen_till = [15] * stride * self.nlanes + storen_till += [127]*64 + self.storen_till(storen_till, stride, n, vdata) + assert storen_till[:-64] == data_till + assert storen_till[-64:] == [127]*64 # detect overflow + + for stride in range(-64, 0): + for n in lanes: + data_till = [15] * -stride * self.nlanes + data_till[::stride] = data[:n] + [15] * (self.nlanes-n) + storen_till = [127]*64 + storen_till += [15] * -stride * self.nlanes + self.storen_till(storen_till, stride, n, vdata) + assert storen_till[64:] == data_till + assert storen_till[:64] == [127]*64 # detect overflow + + def test_misc(self): + broadcast_zero = self.zero() + assert broadcast_zero == [0] * self.nlanes + for i in range(1, 10): + broadcasti = self.setall(i) + assert broadcasti == [i] * self.nlanes + + data_a, data_b = self._data(), self._data(reverse=True) + vdata_a, vdata_b = self.load(data_a), self.load(data_b) + + # py level of npyv_set_* don't support ignoring the extra specified lanes or + # fill non-specified lanes with zero. + vset = self.set(*data_a) + assert vset == data_a + # py level of npyv_setf_* don't support ignoring the extra specified lanes or + # fill non-specified lanes with the specified scalar. + vsetf = self.setf(10, *data_a) + assert vsetf == data_a + + # We're testing the sanity of _simd's type-vector, + # reinterpret* intrinsics itself are tested via compiler + # during the build of _simd module + sfxes = ["u8", "s8", "u16", "s16", "u32", "s32", "u64", "s64", "f32"] + if self.npyv.simd_f64: + sfxes.append("f64") + for sfx in sfxes: + vec_name = getattr(self, "reinterpret_" + sfx)(vdata_a).__name__ + assert vec_name == "npyv_" + sfx + + # select & mask operations + select_a = self.select(self.cmpeq(self.zero(), self.zero()), vdata_a, vdata_b) + assert select_a == data_a + select_b = self.select(self.cmpneq(self.zero(), self.zero()), vdata_a, vdata_b) + assert select_b == data_b + + # cleanup intrinsic is only used with AVX for + # zeroing registers to avoid the AVX-SSE transition penalty, + # so nothing to test here + self.npyv.cleanup() + + def test_reorder(self): + data_a, data_b = self._data(), self._data(reverse=True) + vdata_a, vdata_b = self.load(data_a), self.load(data_b) + # lower half part + data_a_lo = data_a[:self.nlanes//2] + data_b_lo = data_b[:self.nlanes//2] + # higher half part + data_a_hi = data_a[self.nlanes//2:] + data_b_hi = data_b[self.nlanes//2:] + # combine two lower parts + combinel = self.combinel(vdata_a, vdata_b) + assert combinel == data_a_lo + data_b_lo + # combine two higher parts + combineh = self.combineh(vdata_a, vdata_b) + assert combineh == data_a_hi + data_b_hi + # combine x2 + combine = self.combine(vdata_a, vdata_b) + assert combine == (data_a_lo + data_b_lo, data_a_hi + data_b_hi) + # zip(interleave) + data_zipl = [v for p in zip(data_a_lo, data_b_lo) for v in p] + data_ziph = [v for p in zip(data_a_hi, data_b_hi) for v in p] + vzip = self.zip(vdata_a, vdata_b) + assert vzip == (data_zipl, data_ziph) + + def test_reorder_rev64(self): + # Reverse elements of each 64-bit lane + ssize = self._scalar_size() + if ssize == 64: + return + data_rev64 = [ + y for x in range(0, self.nlanes, 64//ssize) + for y in reversed(range(x, x + 64//ssize)) + ] + rev64 = self.rev64(self.load(range(self.nlanes))) + assert rev64 == data_rev64 + + def test_operators_comparison(self): + if self._is_fp(): + data_a = self._data() + else: + data_a = self._data(self._int_max() - self.nlanes) + data_b = self._data(self._int_min(), reverse=True) + vdata_a, vdata_b = self.load(data_a), self.load(data_b) + + mask_true = self._true_mask() + def to_bool(vector): + return [lane == mask_true for lane in vector] + # equal + data_eq = [a == b for a, b in zip(data_a, data_b)] + cmpeq = to_bool(self.cmpeq(vdata_a, vdata_b)) + assert cmpeq == data_eq + # not equal + data_neq = [a != b for a, b in zip(data_a, data_b)] + cmpneq = to_bool(self.cmpneq(vdata_a, vdata_b)) + assert cmpneq == data_neq + # greater than + data_gt = [a > b for a, b in zip(data_a, data_b)] + cmpgt = to_bool(self.cmpgt(vdata_a, vdata_b)) + assert cmpgt == data_gt + # greater than and equal + data_ge = [a >= b for a, b in zip(data_a, data_b)] + cmpge = to_bool(self.cmpge(vdata_a, vdata_b)) + assert cmpge == data_ge + # less than + data_lt = [a < b for a, b in zip(data_a, data_b)] + cmplt = to_bool(self.cmplt(vdata_a, vdata_b)) + assert cmplt == data_lt + # less than and equal + data_le = [a <= b for a, b in zip(data_a, data_b)] + cmple = to_bool(self.cmple(vdata_a, vdata_b)) + assert cmple == data_le + + def test_operators_logical(self): + if self._is_fp(): + data_a = self._data() + else: + data_a = self._data(self._int_max() - self.nlanes) + data_b = self._data(self._int_min(), reverse=True) + vdata_a, vdata_b = self.load(data_a), self.load(data_b) + + if self._is_fp(): + data_cast_a = self._to_unsigned(vdata_a) + data_cast_b = self._to_unsigned(vdata_b) + cast, cast_data = self._to_unsigned, self._to_unsigned + else: + data_cast_a, data_cast_b = data_a, data_b + cast, cast_data = lambda a: a, self.load + + data_xor = cast_data([a ^ b for a, b in zip(data_cast_a, data_cast_b)]) + vxor = cast(self.xor(vdata_a, vdata_b)) + assert vxor == data_xor + + data_or = cast_data([a | b for a, b in zip(data_cast_a, data_cast_b)]) + vor = cast(getattr(self, "or")(vdata_a, vdata_b)) + assert vor == data_or + + data_and = cast_data([a & b for a, b in zip(data_cast_a, data_cast_b)]) + vand = cast(getattr(self, "and")(vdata_a, vdata_b)) + assert vand == data_and + + data_not = cast_data([~a for a in data_cast_a]) + vnot = cast(getattr(self, "not")(vdata_a)) + assert vnot == data_not + + def test_conversion_boolean(self): + bsfx = "b" + self.sfx[1:] + to_boolean = getattr(self.npyv, "cvt_%s_%s" % (bsfx, self.sfx)) + from_boolean = getattr(self.npyv, "cvt_%s_%s" % (self.sfx, bsfx)) + + false_vb = to_boolean(self.setall(0)) + true_vb = self.cmpeq(self.setall(0), self.setall(0)) + assert false_vb != true_vb + + false_vsfx = from_boolean(false_vb) + true_vsfx = from_boolean(true_vb) + assert false_vsfx != true_vsfx + + def test_conversion_expand(self): + """ + Test expand intrinsics: + npyv_expand_u16_u8 + npyv_expand_u32_u16 + """ + if self.sfx not in ("u8", "u16"): + return + totype = self.sfx[0]+str(int(self.sfx[1:])*2) + expand = getattr(self.npyv, f"expand_{totype}_{self.sfx}") + # close enough from the edge to detect any deviation + data = self._data(self._int_max() - self.nlanes) + vdata = self.load(data) + edata = expand(vdata) + # lower half part + data_lo = data[:self.nlanes//2] + # higher half part + data_hi = data[self.nlanes//2:] + assert edata == (data_lo, data_hi) + + def test_arithmetic_subadd(self): + if self._is_fp(): + data_a = self._data() + else: + data_a = self._data(self._int_max() - self.nlanes) + data_b = self._data(self._int_min(), reverse=True) + vdata_a, vdata_b = self.load(data_a), self.load(data_b) + + # non-saturated + data_add = self.load([a + b for a, b in zip(data_a, data_b)]) # load to cast + add = self.add(vdata_a, vdata_b) + assert add == data_add + data_sub = self.load([a - b for a, b in zip(data_a, data_b)]) + sub = self.sub(vdata_a, vdata_b) + assert sub == data_sub + + def test_arithmetic_mul(self): + if self.sfx in ("u64", "s64"): + return + + if self._is_fp(): + data_a = self._data() + else: + data_a = self._data(self._int_max() - self.nlanes) + data_b = self._data(self._int_min(), reverse=True) + vdata_a, vdata_b = self.load(data_a), self.load(data_b) + + data_mul = self.load([a * b for a, b in zip(data_a, data_b)]) + mul = self.mul(vdata_a, vdata_b) + assert mul == data_mul + + def test_arithmetic_div(self): + if not self._is_fp(): + return + + data_a, data_b = self._data(), self._data(reverse=True) + vdata_a, vdata_b = self.load(data_a), self.load(data_b) + + # load to truncate f64 to precision of f32 + data_div = self.load([a / b for a, b in zip(data_a, data_b)]) + div = self.div(vdata_a, vdata_b) + assert div == data_div + + def test_arithmetic_intdiv(self): + """ + Test integer division intrinsics: + npyv_divisor_##sfx + npyv_divc_##sfx + """ + if self._is_fp(): + return + + int_min = self._int_min() + def trunc_div(a, d): + """ + Divide towards zero works with large integers > 2^53, + and wrap around overflow similar to what C does. + """ + if d == -1 and a == int_min: + return a + sign_a, sign_d = a < 0, d < 0 + if a == 0 or sign_a == sign_d: + return a // d + return (a + sign_d - sign_a) // d + 1 + + data = [1, -int_min] # to test overflow + data += range(0, 2**8, 2**5) + data += range(0, 2**8, 2**5-1) + bsize = self._scalar_size() + if bsize > 8: + data += range(2**8, 2**16, 2**13) + data += range(2**8, 2**16, 2**13-1) + if bsize > 16: + data += range(2**16, 2**32, 2**29) + data += range(2**16, 2**32, 2**29-1) + if bsize > 32: + data += range(2**32, 2**64, 2**61) + data += range(2**32, 2**64, 2**61-1) + # negate + data += [-x for x in data] + for dividend, divisor in itertools.product(data, data): + divisor = self.setall(divisor)[0] # cast + if divisor == 0: + continue + dividend = self.load(self._data(dividend)) + data_divc = [trunc_div(a, divisor) for a in dividend] + divisor_parms = self.divisor(divisor) + divc = self.divc(dividend, divisor_parms) + assert divc == data_divc + + def test_arithmetic_reduce_sum(self): + """ + Test reduce sum intrinsics: + npyv_sum_##sfx + """ + if self.sfx not in ("u32", "u64", "f32", "f64"): + return + # reduce sum + data = self._data() + vdata = self.load(data) + + data_sum = sum(data) + vsum = self.sum(vdata) + assert vsum == data_sum + + def test_arithmetic_reduce_sumup(self): + """ + Test extend reduce sum intrinsics: + npyv_sumup_##sfx + """ + if self.sfx not in ("u8", "u16"): + return + rdata = (0, self.nlanes, self._int_min(), self._int_max()-self.nlanes) + for r in rdata: + data = self._data(r) + vdata = self.load(data) + data_sum = sum(data) + vsum = self.sumup(vdata) + assert vsum == data_sum + + def test_mask_conditional(self): + """ + Conditional addition and subtraction for all supported data types. + Test intrinsics: + npyv_ifadd_##SFX, npyv_ifsub_##SFX + """ + vdata_a = self.load(self._data()) + vdata_b = self.load(self._data(reverse=True)) + true_mask = self.cmpeq(self.zero(), self.zero()) + false_mask = self.cmpneq(self.zero(), self.zero()) + + data_sub = self.sub(vdata_b, vdata_a) + ifsub = self.ifsub(true_mask, vdata_b, vdata_a, vdata_b) + assert ifsub == data_sub + ifsub = self.ifsub(false_mask, vdata_a, vdata_b, vdata_b) + assert ifsub == vdata_b + + data_add = self.add(vdata_b, vdata_a) + ifadd = self.ifadd(true_mask, vdata_b, vdata_a, vdata_b) + assert ifadd == data_add + ifadd = self.ifadd(false_mask, vdata_a, vdata_b, vdata_b) + assert ifadd == vdata_b + +bool_sfx = ("b8", "b16", "b32", "b64") +int_sfx = ("u8", "s8", "u16", "s16", "u32", "s32", "u64", "s64") +fp_sfx = ("f32", "f64") +all_sfx = int_sfx + fp_sfx +tests_registry = { + bool_sfx: _SIMD_BOOL, + int_sfx : _SIMD_INT, + fp_sfx : _SIMD_FP, + ("f32",): _SIMD_FP32, + ("f64",): _SIMD_FP64, + all_sfx : _SIMD_ALL +} +for target_name, npyv in targets.items(): + simd_width = npyv.simd if npyv else '' + pretty_name = target_name.split('__') # multi-target separator + if len(pretty_name) > 1: + # multi-target + pretty_name = f"({' '.join(pretty_name)})" + else: + pretty_name = pretty_name[0] + + skip = "" + skip_sfx = dict() + if not npyv: + skip = f"target '{pretty_name}' isn't supported by current machine" + elif not npyv.simd: + skip = f"target '{pretty_name}' isn't supported by NPYV" + elif not npyv.simd_f64: + skip_sfx["f64"] = f"target '{pretty_name}' doesn't support double-precision" + + for sfxes, cls in tests_registry.items(): + for sfx in sfxes: + skip_m = skip_sfx.get(sfx, skip) + inhr = (cls,) + attr = dict(npyv=targets[target_name], sfx=sfx, target_name=target_name) + tcls = type(f"Test{cls.__name__}_{simd_width}_{target_name}_{sfx}", inhr, attr) + if skip_m: + pytest.mark.skip(reason=skip_m)(tcls) + globals()[tcls.__name__] = tcls diff --git a/numpy/core/tests/test_simd_module.py b/numpy/core/tests/test_simd_module.py new file mode 100644 index 000000000000..3d710884ab09 --- /dev/null +++ b/numpy/core/tests/test_simd_module.py @@ -0,0 +1,97 @@ +import pytest +from numpy.core._simd import targets +""" +This testing unit only for checking the sanity of common functionality, +therefore all we need is just to take one submodule that represents any +of enabled SIMD extensions to run the test on it and the second submodule +required to run only one check related to the possibility of mixing +the data types among each submodule. +""" +npyvs = [npyv_mod for npyv_mod in targets.values() if npyv_mod and npyv_mod.simd] +npyv, npyv2 = (npyvs + [None, None])[:2] + +unsigned_sfx = ["u8", "u16", "u32", "u64"] +signed_sfx = ["s8", "s16", "s32", "s64"] +fp_sfx = ["f32"] +if npyv and npyv.simd_f64: + fp_sfx.append("f64") + +int_sfx = unsigned_sfx + signed_sfx +all_sfx = unsigned_sfx + int_sfx + +@pytest.mark.skipif(not npyv, reason="could not find any SIMD extension with NPYV support") +class Test_SIMD_MODULE: + + @pytest.mark.parametrize('sfx', all_sfx) + def test_num_lanes(self, sfx): + nlanes = getattr(npyv, "nlanes_" + sfx) + vector = getattr(npyv, "setall_" + sfx)(1) + assert len(vector) == nlanes + + @pytest.mark.parametrize('sfx', all_sfx) + def test_type_name(self, sfx): + vector = getattr(npyv, "setall_" + sfx)(1) + assert vector.__name__ == "npyv_" + sfx + + def test_raises(self): + a, b = [npyv.setall_u32(1)]*2 + for sfx in all_sfx: + vcb = lambda intrin: getattr(npyv, f"{intrin}_{sfx}") + pytest.raises(TypeError, vcb("add"), a) + pytest.raises(TypeError, vcb("add"), a, b, a) + pytest.raises(TypeError, vcb("setall")) + pytest.raises(TypeError, vcb("setall"), [1]) + pytest.raises(TypeError, vcb("load"), 1) + pytest.raises(ValueError, vcb("load"), [1]) + pytest.raises(ValueError, vcb("store"), [1], getattr(npyv, f"reinterpret_{sfx}_u32")(a)) + + @pytest.mark.skipif(not npyv2, reason=( + "could not find a second SIMD extension with NPYV support" + )) + def test_nomix(self): + # mix among submodules isn't allowed + a = npyv.setall_u32(1) + a2 = npyv2.setall_u32(1) + pytest.raises(TypeError, npyv.add_u32, a2, a2) + pytest.raises(TypeError, npyv2.add_u32, a, a) + + @pytest.mark.parametrize('sfx', unsigned_sfx) + def test_unsigned_overflow(self, sfx): + nlanes = getattr(npyv, "nlanes_" + sfx) + maxu = (1 << int(sfx[1:])) - 1 + maxu_72 = (1 << 72) - 1 + lane = getattr(npyv, "setall_" + sfx)(maxu_72)[0] + assert lane == maxu + lanes = getattr(npyv, "load_" + sfx)([maxu_72] * nlanes) + assert lanes == [maxu] * nlanes + lane = getattr(npyv, "setall_" + sfx)(-1)[0] + assert lane == maxu + lanes = getattr(npyv, "load_" + sfx)([-1] * nlanes) + assert lanes == [maxu] * nlanes + + @pytest.mark.parametrize('sfx', signed_sfx) + def test_signed_overflow(self, sfx): + nlanes = getattr(npyv, "nlanes_" + sfx) + maxs_72 = (1 << 71) - 1 + lane = getattr(npyv, "setall_" + sfx)(maxs_72)[0] + assert lane == -1 + lanes = getattr(npyv, "load_" + sfx)([maxs_72] * nlanes) + assert lanes == [-1] * nlanes + mins_72 = -1 << 71 + lane = getattr(npyv, "setall_" + sfx)(mins_72)[0] + assert lane == 0 + lanes = getattr(npyv, "load_" + sfx)([mins_72] * nlanes) + assert lanes == [0] * nlanes + + def test_truncate_f32(self): + f32 = npyv.setall_f32(0.1)[0] + assert f32 != 0.1 + assert round(f32, 1) == 0.1 + + def test_compare(self): + data_range = range(0, npyv.nlanes_u32) + vdata = npyv.load_u32(data_range) + assert vdata == list(data_range) + assert vdata == tuple(data_range) + for i in data_range: + assert vdata[i] == data_range[i] diff --git a/numpy/core/tests/test_ufunc.py b/numpy/core/tests/test_ufunc.py index ef9ced3541b1..76e4cdcfd94a 100644 --- a/numpy/core/tests/test_ufunc.py +++ b/numpy/core/tests/test_ufunc.py @@ -1,7 +1,8 @@ -from __future__ import division, absolute_import, print_function - import warnings import itertools +import sys + +import pytest import numpy as np import numpy.core._umath_tests as umt @@ -11,11 +12,17 @@ from numpy.testing import ( assert_, assert_equal, assert_raises, assert_array_equal, assert_almost_equal, assert_array_almost_equal, assert_no_warnings, - assert_allclose, + assert_allclose, HAS_REFCOUNT, suppress_warnings ) +from numpy.compat import pickle + + +UNARY_UFUNCS = [obj for obj in np.core.umath.__dict__.values() + if isinstance(obj, np.ufunc)] +UNARY_OBJECT_UFUNCS = [uf for uf in UNARY_UFUNCS if "O->O" in uf.types] -class TestUfuncKwargs(object): +class TestUfuncKwargs: def test_kwarg_exact(self): assert_raises(TypeError, np.add, 1, 2, castingx='safe') assert_raises(TypeError, np.add, 1, 2, dtypex=int) @@ -27,13 +34,13 @@ def test_kwarg_exact(self): assert_raises(TypeError, np.add, 1, 2, wherex=[True]) def test_sig_signature(self): - assert_raises(ValueError, np.add, 1, 2, sig='ii->i', + assert_raises(TypeError, np.add, 1, 2, sig='ii->i', signature='ii->i') def test_sig_dtype(self): - assert_raises(RuntimeError, np.add, 1, 2, sig='ii->i', + assert_raises(TypeError, np.add, 1, 2, sig='ii->i', dtype=int) - assert_raises(RuntimeError, np.add, 1, 2, signature='ii->i', + assert_raises(TypeError, np.add, 1, 2, signature='ii->i', dtype=int) def test_extobj_refcount(self): @@ -41,168 +48,173 @@ def test_extobj_refcount(self): assert_raises(TypeError, np.add, 1, 2, extobj=[4096], parrot=True) -class TestUfunc(object): +class TestUfuncGenericLoops: + """Test generic loops. + + The loops to be tested are: + + PyUFunc_ff_f_As_dd_d + PyUFunc_ff_f + PyUFunc_dd_d + PyUFunc_gg_g + PyUFunc_FF_F_As_DD_D + PyUFunc_DD_D + PyUFunc_FF_F + PyUFunc_GG_G + PyUFunc_OO_O + PyUFunc_OO_O_method + PyUFunc_f_f_As_d_d + PyUFunc_d_d + PyUFunc_f_f + PyUFunc_g_g + PyUFunc_F_F_As_D_D + PyUFunc_F_F + PyUFunc_D_D + PyUFunc_G_G + PyUFunc_O_O + PyUFunc_O_O_method + PyUFunc_On_Om + + Where: + + f -- float + d -- double + g -- long double + F -- complex float + D -- complex double + G -- complex long double + O -- python object + + It is difficult to assure that each of these loops is entered from the + Python level as the special cased loops are a moving target and the + corresponding types are architecture dependent. We probably need to + define C level testing ufuncs to get at them. For the time being, I've + just looked at the signatures registered in the build directory to find + relevant functions. + + """ + np_dtypes = [ + (np.single, np.single), (np.single, np.double), + (np.csingle, np.csingle), (np.csingle, np.cdouble), + (np.double, np.double), (np.longdouble, np.longdouble), + (np.cdouble, np.cdouble), (np.clongdouble, np.clongdouble)] + + @pytest.mark.parametrize('input_dtype,output_dtype', np_dtypes) + def test_unary_PyUFunc(self, input_dtype, output_dtype, f=np.exp, x=0, y=1): + xs = np.full(10, input_dtype(x), dtype=output_dtype) + ys = f(xs)[::2] + assert_allclose(ys, y) + assert_equal(ys.dtype, output_dtype) + + def f2(x, y): + return x**y + + @pytest.mark.parametrize('input_dtype,output_dtype', np_dtypes) + def test_binary_PyUFunc(self, input_dtype, output_dtype, f=f2, x=0, y=1): + xs = np.full(10, input_dtype(x), dtype=output_dtype) + ys = f(xs, xs)[::2] + assert_allclose(ys, y) + assert_equal(ys.dtype, output_dtype) + + # class to use in testing object method loops + class foo: + def conjugate(self): + return np.bool_(1) + + def logical_xor(self, obj): + return np.bool_(1) + + def test_unary_PyUFunc_O_O(self): + x = np.ones(10, dtype=object) + assert_(np.all(np.abs(x) == 1)) + + def test_unary_PyUFunc_O_O_method_simple(self, foo=foo): + x = np.full(10, foo(), dtype=object) + assert_(np.all(np.conjugate(x) == True)) + + def test_binary_PyUFunc_OO_O(self): + x = np.ones(10, dtype=object) + assert_(np.all(np.add(x, x) == 2)) + + def test_binary_PyUFunc_OO_O_method(self, foo=foo): + x = np.full(10, foo(), dtype=object) + assert_(np.all(np.logical_xor(x, x))) + + def test_binary_PyUFunc_On_Om_method(self, foo=foo): + x = np.full((10, 2, 3), foo(), dtype=object) + assert_(np.all(np.logical_xor(x, x))) + + def test_python_complex_conjugate(self): + # The conjugate ufunc should fall back to calling the method: + arr = np.array([1+2j, 3-4j], dtype="O") + assert isinstance(arr[0], complex) + res = np.conjugate(arr) + assert res.dtype == np.dtype("O") + assert_array_equal(res, np.array([1-2j, 3+4j], dtype="O")) + + @pytest.mark.parametrize("ufunc", UNARY_OBJECT_UFUNCS) + def test_unary_PyUFunc_O_O_method_full(self, ufunc): + """Compare the result of the object loop with non-object one""" + val = np.float64(np.pi/4) + + class MyFloat(np.float64): + def __getattr__(self, attr): + try: + return super().__getattr__(attr) + except AttributeError: + return lambda: getattr(np.core.umath, attr)(val) + + # Use 0-D arrays, to ensure the same element call + num_arr = np.array(val, dtype=np.float64) + obj_arr = np.array(MyFloat(val), dtype="O") + + with np.errstate(all="raise"): + try: + res_num = ufunc(num_arr) + except Exception as exc: + with assert_raises(type(exc)): + ufunc(obj_arr) + else: + res_obj = ufunc(obj_arr) + assert_array_almost_equal(res_num.astype("O"), res_obj) + + +def _pickleable_module_global(): + pass + + +class TestUfunc: def test_pickle(self): - import pickle - assert_(pickle.loads(pickle.dumps(np.sin)) is np.sin) + for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): + assert_(pickle.loads(pickle.dumps(np.sin, + protocol=proto)) is np.sin) - # Check that ufunc not defined in the top level numpy namespace such as - # numpy.core._rational_tests.test_add can also be pickled - res = pickle.loads(pickle.dumps(_rational_tests.test_add)) - assert_(res is _rational_tests.test_add) + # Check that ufunc not defined in the top level numpy namespace + # such as numpy.core._rational_tests.test_add can also be pickled + res = pickle.loads(pickle.dumps(_rational_tests.test_add, + protocol=proto)) + assert_(res is _rational_tests.test_add) def test_pickle_withstring(self): - import pickle astring = (b"cnumpy.core\n_ufunc_reconstruct\np0\n" b"(S'numpy.core.umath'\np1\nS'cos'\np2\ntp3\nRp4\n.") assert_(pickle.loads(astring) is np.cos) + def test_pickle_name_is_qualname(self): + # This tests that a simplification of our ufunc pickle code will + # lead to allowing qualnames as names. Future ufuncs should + # possible add a specific qualname, or a hook into pickling instead + # (dask+numba may benefit). + _pickleable_module_global.ufunc = umt._pickleable_module_global_ufunc + obj = pickle.loads(pickle.dumps(_pickleable_module_global.ufunc)) + assert obj is umt._pickleable_module_global_ufunc + def test_reduceat_shifting_sum(self): L = 6 x = np.arange(L) idx = np.array(list(zip(np.arange(L - 2), np.arange(L - 2) + 2))).ravel() assert_array_equal(np.add.reduceat(x, idx)[::2], [1, 3, 5, 7]) - def test_generic_loops(self): - """Test generic loops. - - The loops to be tested are: - - PyUFunc_ff_f_As_dd_d - PyUFunc_ff_f - PyUFunc_dd_d - PyUFunc_gg_g - PyUFunc_FF_F_As_DD_D - PyUFunc_DD_D - PyUFunc_FF_F - PyUFunc_GG_G - PyUFunc_OO_O - PyUFunc_OO_O_method - PyUFunc_f_f_As_d_d - PyUFunc_d_d - PyUFunc_f_f - PyUFunc_g_g - PyUFunc_F_F_As_D_D - PyUFunc_F_F - PyUFunc_D_D - PyUFunc_G_G - PyUFunc_O_O - PyUFunc_O_O_method - PyUFunc_On_Om - - Where: - - f -- float - d -- double - g -- long double - F -- complex float - D -- complex double - G -- complex long double - O -- python object - - It is difficult to assure that each of these loops is entered from the - Python level as the special cased loops are a moving target and the - corresponding types are architecture dependent. We probably need to - define C level testing ufuncs to get at them. For the time being, I've - just looked at the signatures registered in the build directory to find - relevant functions. - - Fixme, currently untested: - - PyUFunc_ff_f_As_dd_d - PyUFunc_FF_F_As_DD_D - PyUFunc_f_f_As_d_d - PyUFunc_F_F_As_D_D - PyUFunc_On_Om - - """ - fone = np.exp - ftwo = lambda x, y: x**y - fone_val = 1 - ftwo_val = 1 - # check unary PyUFunc_f_f. - msg = "PyUFunc_f_f" - x = np.zeros(10, dtype=np.single)[0::2] - assert_almost_equal(fone(x), fone_val, err_msg=msg) - # check unary PyUFunc_d_d. - msg = "PyUFunc_d_d" - x = np.zeros(10, dtype=np.double)[0::2] - assert_almost_equal(fone(x), fone_val, err_msg=msg) - # check unary PyUFunc_g_g. - msg = "PyUFunc_g_g" - x = np.zeros(10, dtype=np.longdouble)[0::2] - assert_almost_equal(fone(x), fone_val, err_msg=msg) - # check unary PyUFunc_F_F. - msg = "PyUFunc_F_F" - x = np.zeros(10, dtype=np.csingle)[0::2] - assert_almost_equal(fone(x), fone_val, err_msg=msg) - # check unary PyUFunc_D_D. - msg = "PyUFunc_D_D" - x = np.zeros(10, dtype=np.cdouble)[0::2] - assert_almost_equal(fone(x), fone_val, err_msg=msg) - # check unary PyUFunc_G_G. - msg = "PyUFunc_G_G" - x = np.zeros(10, dtype=np.clongdouble)[0::2] - assert_almost_equal(fone(x), fone_val, err_msg=msg) - - # check binary PyUFunc_ff_f. - msg = "PyUFunc_ff_f" - x = np.ones(10, dtype=np.single)[0::2] - assert_almost_equal(ftwo(x, x), ftwo_val, err_msg=msg) - # check binary PyUFunc_dd_d. - msg = "PyUFunc_dd_d" - x = np.ones(10, dtype=np.double)[0::2] - assert_almost_equal(ftwo(x, x), ftwo_val, err_msg=msg) - # check binary PyUFunc_gg_g. - msg = "PyUFunc_gg_g" - x = np.ones(10, dtype=np.longdouble)[0::2] - assert_almost_equal(ftwo(x, x), ftwo_val, err_msg=msg) - # check binary PyUFunc_FF_F. - msg = "PyUFunc_FF_F" - x = np.ones(10, dtype=np.csingle)[0::2] - assert_almost_equal(ftwo(x, x), ftwo_val, err_msg=msg) - # check binary PyUFunc_DD_D. - msg = "PyUFunc_DD_D" - x = np.ones(10, dtype=np.cdouble)[0::2] - assert_almost_equal(ftwo(x, x), ftwo_val, err_msg=msg) - # check binary PyUFunc_GG_G. - msg = "PyUFunc_GG_G" - x = np.ones(10, dtype=np.clongdouble)[0::2] - assert_almost_equal(ftwo(x, x), ftwo_val, err_msg=msg) - - # class to use in testing object method loops - class foo(object): - def conjugate(self): - return np.bool_(1) - - def logical_xor(self, obj): - return np.bool_(1) - - # check unary PyUFunc_O_O - msg = "PyUFunc_O_O" - x = np.ones(10, dtype=object)[0::2] - assert_(np.all(np.abs(x) == 1), msg) - # check unary PyUFunc_O_O_method - msg = "PyUFunc_O_O_method" - x = np.zeros(10, dtype=object)[0::2] - for i in range(len(x)): - x[i] = foo() - assert_(np.all(np.conjugate(x) == True), msg) - - # check binary PyUFunc_OO_O - msg = "PyUFunc_OO_O" - x = np.ones(10, dtype=object)[0::2] - assert_(np.all(np.add(x, x) == 2), msg) - # check binary PyUFunc_OO_O_method - msg = "PyUFunc_OO_O_method" - x = np.zeros(10, dtype=object)[0::2] - for i in range(len(x)): - x[i] = foo() - assert_(np.all(np.logical_xor(x, x)), msg) - - # check PyUFunc_On_Om - # fixme -- I don't know how to do this yet - def test_all_ufunc(self): """Try to check presence and results of all ufuncs. @@ -286,61 +298,130 @@ def test_all_ufunc(self): """ pass - def test_signature(self): + # from include/numpy/ufuncobject.h + size_inferred = 2 + can_ignore = 4 + def test_signature0(self): # the arguments to test_signature are: nin, nout, core_signature - # pass - enabled, num_dims, ixs = umt.test_signature(2, 1, "(i),(i)->()") + enabled, num_dims, ixs, flags, sizes = umt.test_signature( + 2, 1, "(i),(i)->()") assert_equal(enabled, 1) assert_equal(num_dims, (1, 1, 0)) assert_equal(ixs, (0, 0)) + assert_equal(flags, (self.size_inferred,)) + assert_equal(sizes, (-1,)) + def test_signature1(self): # empty core signature; treat as plain ufunc (with trivial core) - enabled, num_dims, ixs = umt.test_signature(2, 1, "(),()->()") + enabled, num_dims, ixs, flags, sizes = umt.test_signature( + 2, 1, "(),()->()") assert_equal(enabled, 0) assert_equal(num_dims, (0, 0, 0)) assert_equal(ixs, ()) + assert_equal(flags, ()) + assert_equal(sizes, ()) - # in the following calls, a ValueError should be raised because - # of error in core signature - # FIXME These should be using assert_raises + def test_signature2(self): + # more complicated names for variables + enabled, num_dims, ixs, flags, sizes = umt.test_signature( + 2, 1, "(i1,i2),(J_1)->(_kAB)") + assert_equal(enabled, 1) + assert_equal(num_dims, (2, 1, 1)) + assert_equal(ixs, (0, 1, 2, 3)) + assert_equal(flags, (self.size_inferred,)*4) + assert_equal(sizes, (-1, -1, -1, -1)) - # error: extra parenthesis - msg = "core_sig: extra parenthesis" - try: - ret = umt.test_signature(2, 1, "((i)),(i)->()") - assert_equal(ret, None, err_msg=msg) - except ValueError: - pass + def test_signature3(self): + enabled, num_dims, ixs, flags, sizes = umt.test_signature( + 2, 1, u"(i1, i12), (J_1)->(i12, i2)") + assert_equal(enabled, 1) + assert_equal(num_dims, (2, 1, 2)) + assert_equal(ixs, (0, 1, 2, 1, 3)) + assert_equal(flags, (self.size_inferred,)*4) + assert_equal(sizes, (-1, -1, -1, -1)) + + def test_signature4(self): + # matrix_multiply signature from _umath_tests + enabled, num_dims, ixs, flags, sizes = umt.test_signature( + 2, 1, "(n,k),(k,m)->(n,m)") + assert_equal(enabled, 1) + assert_equal(num_dims, (2, 2, 2)) + assert_equal(ixs, (0, 1, 1, 2, 0, 2)) + assert_equal(flags, (self.size_inferred,)*3) + assert_equal(sizes, (-1, -1, -1)) + + def test_signature5(self): + # matmul signature from _umath_tests + enabled, num_dims, ixs, flags, sizes = umt.test_signature( + 2, 1, "(n?,k),(k,m?)->(n?,m?)") + assert_equal(enabled, 1) + assert_equal(num_dims, (2, 2, 2)) + assert_equal(ixs, (0, 1, 1, 2, 0, 2)) + assert_equal(flags, (self.size_inferred | self.can_ignore, + self.size_inferred, + self.size_inferred | self.can_ignore)) + assert_equal(sizes, (-1, -1, -1)) + + def test_signature6(self): + enabled, num_dims, ixs, flags, sizes = umt.test_signature( + 1, 1, "(3)->()") + assert_equal(enabled, 1) + assert_equal(num_dims, (1, 0)) + assert_equal(ixs, (0,)) + assert_equal(flags, (0,)) + assert_equal(sizes, (3,)) + + def test_signature7(self): + enabled, num_dims, ixs, flags, sizes = umt.test_signature( + 3, 1, "(3),(03,3),(n)->(9)") + assert_equal(enabled, 1) + assert_equal(num_dims, (1, 2, 1, 1)) + assert_equal(ixs, (0, 0, 0, 1, 2)) + assert_equal(flags, (0, self.size_inferred, 0)) + assert_equal(sizes, (3, -1, 9)) + + def test_signature8(self): + enabled, num_dims, ixs, flags, sizes = umt.test_signature( + 3, 1, "(3?),(3?,3?),(n)->(9)") + assert_equal(enabled, 1) + assert_equal(num_dims, (1, 2, 1, 1)) + assert_equal(ixs, (0, 0, 0, 1, 2)) + assert_equal(flags, (self.can_ignore, self.size_inferred, 0)) + assert_equal(sizes, (3, -1, 9)) + + def test_signature9(self): + enabled, num_dims, ixs, flags, sizes = umt.test_signature( + 1, 1, "( 3) -> ( )") + assert_equal(enabled, 1) + assert_equal(num_dims, (1, 0)) + assert_equal(ixs, (0,)) + assert_equal(flags, (0,)) + assert_equal(sizes, (3,)) + + def test_signature10(self): + enabled, num_dims, ixs, flags, sizes = umt.test_signature( + 3, 1, "( 3? ) , (3? , 3?) ,(n )-> ( 9)") + assert_equal(enabled, 1) + assert_equal(num_dims, (1, 2, 1, 1)) + assert_equal(ixs, (0, 0, 0, 1, 2)) + assert_equal(flags, (self.can_ignore, self.size_inferred, 0)) + assert_equal(sizes, (3, -1, 9)) - # error: parenthesis matching - msg = "core_sig: parenthesis matching" - try: - ret = umt.test_signature(2, 1, "(i),)i(->()") - assert_equal(ret, None, err_msg=msg) - except ValueError: - pass + def test_signature_failure_extra_parenthesis(self): + with assert_raises(ValueError): + umt.test_signature(2, 1, "((i)),(i)->()") - # error: incomplete signature. letters outside of parenthesis are ignored - msg = "core_sig: incomplete signature" - try: - ret = umt.test_signature(2, 1, "(i),->()") - assert_equal(ret, None, err_msg=msg) - except ValueError: - pass + def test_signature_failure_mismatching_parenthesis(self): + with assert_raises(ValueError): + umt.test_signature(2, 1, "(i),)i(->()") - # error: incomplete signature. 2 output arguments are specified - msg = "core_sig: incomplete signature" - try: - ret = umt.test_signature(2, 2, "(i),(i)->()") - assert_equal(ret, None, err_msg=msg) - except ValueError: - pass + def test_signature_failure_signature_missing_input_arg(self): + with assert_raises(ValueError): + umt.test_signature(2, 1, "(i),->()") - # more complicated names for variables - enabled, num_dims, ixs = umt.test_signature(2, 1, "(i1,i2),(J_1)->(_kAB)") - assert_equal(enabled, 1) - assert_equal(num_dims, (2, 1, 1)) - assert_equal(ixs, (0, 1, 2, 3)) + def test_signature_failure_signature_missing_output_arg(self): + with assert_raises(ValueError): + umt.test_signature(2, 2, "(i),(i)->()") def test_get_signature(self): assert_equal(umt.inner1d.signature, "(i),(i)->()") @@ -348,9 +429,12 @@ def test_get_signature(self): def test_forced_sig(self): a = 0.5*np.arange(3, dtype='f8') assert_equal(np.add(a, 0.5), [0.5, 1, 1.5]) - assert_equal(np.add(a, 0.5, sig='i', casting='unsafe'), [0, 0, 1]) + with pytest.warns(DeprecationWarning): + assert_equal(np.add(a, 0.5, sig='i', casting='unsafe'), [0, 0, 1]) assert_equal(np.add(a, 0.5, sig='ii->i', casting='unsafe'), [0, 0, 1]) - assert_equal(np.add(a, 0.5, sig=('i4',), casting='unsafe'), [0, 0, 1]) + with pytest.warns(DeprecationWarning): + assert_equal(np.add(a, 0.5, sig=('i4',), casting='unsafe'), + [0, 0, 1]) assert_equal(np.add(a, 0.5, sig=('i4', 'i4', 'i4'), casting='unsafe'), [0, 0, 1]) @@ -358,18 +442,131 @@ def test_forced_sig(self): np.add(a, 0.5, out=b) assert_equal(b, [0.5, 1, 1.5]) b[:] = 0 - np.add(a, 0.5, sig='i', out=b, casting='unsafe') + with pytest.warns(DeprecationWarning): + np.add(a, 0.5, sig='i', out=b, casting='unsafe') assert_equal(b, [0, 0, 1]) b[:] = 0 np.add(a, 0.5, sig='ii->i', out=b, casting='unsafe') assert_equal(b, [0, 0, 1]) b[:] = 0 - np.add(a, 0.5, sig=('i4',), out=b, casting='unsafe') + with pytest.warns(DeprecationWarning): + np.add(a, 0.5, sig=('i4',), out=b, casting='unsafe') assert_equal(b, [0, 0, 1]) b[:] = 0 np.add(a, 0.5, sig=('i4', 'i4', 'i4'), out=b, casting='unsafe') assert_equal(b, [0, 0, 1]) + def test_signature_all_None(self): + # signature all None, is an acceptable alternative (since 1.21) + # to not providing a signature. + res1 = np.add([3], [4], sig=(None, None, None)) + res2 = np.add([3], [4]) + assert_array_equal(res1, res2) + res1 = np.maximum([3], [4], sig=(None, None, None)) + res2 = np.maximum([3], [4]) + assert_array_equal(res1, res2) + + with pytest.raises(TypeError): + # special case, that would be deprecated anyway, so errors: + np.add(3, 4, signature=(None,)) + + def test_signature_dtype_type(self): + # Since that will be the normal behaviour (past NumPy 1.21) + # we do support the types already: + float_dtype = type(np.dtype(np.float64)) + np.add(3, 4, signature=(float_dtype, float_dtype, None)) + + @pytest.mark.parametrize("casting", ["unsafe", "same_kind", "safe"]) + def test_partial_signature_mismatch(self, casting): + # If the second argument matches already, no need to specify it: + res = np.ldexp(np.float32(1.), np.int_(2), dtype="d") + assert res.dtype == "d" + res = np.ldexp(np.float32(1.), np.int_(2), signature=(None, None, "d")) + assert res.dtype == "d" + + # ldexp only has a loop for long input as second argument, overriding + # the output cannot help with that (no matter the casting) + with pytest.raises(TypeError): + np.ldexp(1., np.uint64(3), dtype="d") + with pytest.raises(TypeError): + np.ldexp(1., np.uint64(3), signature=(None, None, "d")) + + def test_use_output_signature_for_all_arguments(self): + # Test that providing only `dtype=` or `signature=(None, None, dtype)` + # is sufficient if falling back to a homogeneous signature works. + # In this case, the `intp, intp -> intp` loop is chosen. + res = np.power(1.5, 2.8, dtype=np.intp, casting="unsafe") + assert res == 1 # the cast happens first. + res = np.power(1.5, 2.8, signature=(None, None, np.intp), + casting="unsafe") + assert res == 1 + with pytest.raises(TypeError): + # the unsafe casting would normally cause errors though: + np.power(1.5, 2.8, dtype=np.intp) + + def test_signature_errors(self): + with pytest.raises(TypeError, + match="the signature object to ufunc must be a string or"): + np.add(3, 4, signature=123.) # neither a string nor a tuple + + with pytest.raises(ValueError): + # bad symbols that do not translate to dtypes + np.add(3, 4, signature="%^->#") + + with pytest.raises(ValueError): + np.add(3, 4, signature=b"ii-i") # incomplete and byte string + + with pytest.raises(ValueError): + np.add(3, 4, signature="ii>i") # incomplete string + + with pytest.raises(ValueError): + np.add(3, 4, signature=(None, "f8")) # bad length + + with pytest.raises(UnicodeDecodeError): + np.add(3, 4, signature=b"\xff\xff->i") + + def test_forced_dtype_times(self): + # Signatures only set the type numbers (not the actual loop dtypes) + # so using `M` in a signature/dtype should generally work: + a = np.array(['2010-01-02', '1999-03-14', '1833-03'], dtype='>M8[D]') + np.maximum(a, a, dtype="M") + np.maximum.reduce(a, dtype="M") + + arr = np.arange(10, dtype="m8[s]") + np.add(arr, arr, dtype="m") + np.maximum(arr, arr, dtype="m") + + @pytest.mark.parametrize("ufunc", [np.add, np.sqrt]) + def test_cast_safety(self, ufunc): + """Basic test for the safest casts, because ufuncs inner loops can + indicate a cast-safety as well (which is normally always "no"). + """ + def call_ufunc(arr, **kwargs): + return ufunc(*(arr,) * ufunc.nin, **kwargs) + + arr = np.array([1., 2., 3.], dtype=np.float32) + arr_bs = arr.astype(arr.dtype.newbyteorder()) + expected = call_ufunc(arr) + # Normally, a "no" cast: + res = call_ufunc(arr, casting="no") + assert_array_equal(expected, res) + # Byte-swapping is not allowed with "no" though: + with pytest.raises(TypeError): + call_ufunc(arr_bs, casting="no") + + # But is allowed with "equiv": + res = call_ufunc(arr_bs, casting="equiv") + assert_array_equal(expected, res) + + # Casting to float64 is safe, but not equiv: + with pytest.raises(TypeError): + call_ufunc(arr_bs, dtype=np.float64, casting="equiv") + + # but it is safe cast: + res = call_ufunc(arr_bs, dtype=np.float64, casting="safe") + expected = call_ufunc(arr.astype(np.float64)) # upcast + assert_array_equal(expected, res) + def test_true_divide(self): a = np.array(10) b = np.array(20) @@ -415,7 +612,13 @@ def test_true_divide(self): else: tgt = float(x)/float(y) rtol = max(np.finfo(dtout).resolution, 1e-15) - atol = max(np.finfo(dtout).tiny, 3e-308) + # The value of tiny for double double is NaN + with suppress_warnings() as sup: + sup.filter(UserWarning) + if not np.isnan(np.finfo(dtout).tiny): + atol = max(np.finfo(dtout).tiny, 3e-308) + else: + atol = 3e-308 # Some test values result in invalid for float16. with np.errstate(invalid='ignore'): res = np.true_divide(x, y, dtype=dtout) @@ -428,7 +631,13 @@ def test_true_divide(self): dtout = np.dtype(tcout) tgt = complex(x)/complex(y) rtol = max(np.finfo(dtout).resolution, 1e-15) - atol = max(np.finfo(dtout).tiny, 3e-308) + # The value of tiny for double double is NaN + with suppress_warnings() as sup: + sup.filter(UserWarning) + if not np.isnan(np.finfo(dtout).tiny): + atol = max(np.finfo(dtout).tiny, 3e-308) + else: + atol = 3e-308 res = np.true_divide(x, y, dtype=dtout) if not np.isfinite(res): continue @@ -519,6 +728,12 @@ def test_sum_initial(self): assert_equal(np.sum(np.ones((2, 3, 5), dtype=np.int64), axis=(0, 2), initial=2), [12, 12, 12]) + def test_sum_where(self): + # More extensive tests done in test_reduction_with_where. + assert_equal(np.sum([[1., 2.], [3., 4.]], where=[True, False]), 4.) + assert_equal(np.sum([[1., 2.], [3., 4.]], axis=0, initial=5., + where=[True, False]), [9., 5.]) + def test_inner1d(self): a = np.arange(6).reshape((2, 3)) assert_array_equal(umt.inner1d(a, a), np.sum(a*a, axis=-1)) @@ -546,6 +761,43 @@ def test_broadcast(self): b = np.arange(3).reshape((3, 1, 1)) assert_raises(ValueError, umt.inner1d, a, b) + # Writing to a broadcasted array with overlap should warn, gh-2705 + a = np.arange(2) + b = np.arange(4).reshape((2, 2)) + u, v = np.broadcast_arrays(a, b) + assert_equal(u.strides[0], 0) + x = u + v + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + u += v + assert_equal(len(w), 1) + assert_(x[0, 0] != u[0, 0]) + + # Output reduction should not be allowed. + # See gh-15139 + a = np.arange(6).reshape(3, 2) + b = np.ones(2) + out = np.empty(()) + assert_raises(ValueError, umt.inner1d, a, b, out) + out2 = np.empty(3) + c = umt.inner1d(a, b, out2) + assert_(c is out2) + + def test_out_broadcasts(self): + # For ufuncs and gufuncs (not for reductions), we currently allow + # the output to cause broadcasting of the input arrays. + # both along dimensions with shape 1 and dimensions which do not + # exist at all in the inputs. + arr = np.arange(3).reshape(1, 3) + out = np.empty((5, 4, 3)) + np.add(arr, arr, out=out) + assert (out == np.arange(3) * 2).all() + + # The same holds for gufuncs (gh-16484) + umt.inner1d(arr, arr, out=out) + # the result would be just a scalar `5`, but is broadcast fully: + assert (out == 5).all() + def test_type_cast(self): msg = "type cast" a = np.arange(6, dtype='short').reshape((2, 3)) @@ -866,8 +1118,94 @@ def test_innerwt_empty(self): w = np.array([], dtype='f8') assert_array_equal(umt.innerwt(a, b, w), np.sum(a*b*w, axis=-1)) + def test_cross1d(self): + """Test with fixed-sized signature.""" + a = np.eye(3) + assert_array_equal(umt.cross1d(a, a), np.zeros((3, 3))) + out = np.zeros((3, 3)) + result = umt.cross1d(a[0], a, out) + assert_(result is out) + assert_array_equal(result, np.vstack((np.zeros(3), a[2], -a[1]))) + assert_raises(ValueError, umt.cross1d, np.eye(4), np.eye(4)) + assert_raises(ValueError, umt.cross1d, a, np.arange(4.)) + # Wrong output core dimension. + assert_raises(ValueError, umt.cross1d, a, np.arange(3.), np.zeros((3, 4))) + # Wrong output broadcast dimension (see gh-15139). + assert_raises(ValueError, umt.cross1d, a, np.arange(3.), np.zeros(3)) + + def test_can_ignore_signature(self): + # Comparing the effects of ? in signature: + # matrix_multiply: (m,n),(n,p)->(m,p) # all must be there. + # matmul: (m?,n),(n,p?)->(m?,p?) # allow missing m, p. + mat = np.arange(12).reshape((2, 3, 2)) + single_vec = np.arange(2) + col_vec = single_vec[:, np.newaxis] + col_vec_array = np.arange(8).reshape((2, 2, 2, 1)) + 1 + # matrix @ single column vector with proper dimension + mm_col_vec = umt.matrix_multiply(mat, col_vec) + # matmul does the same thing + matmul_col_vec = umt.matmul(mat, col_vec) + assert_array_equal(matmul_col_vec, mm_col_vec) + # matrix @ vector without dimension making it a column vector. + # matrix multiply fails -> missing core dim. + assert_raises(ValueError, umt.matrix_multiply, mat, single_vec) + # matmul mimicker passes, and returns a vector. + matmul_col = umt.matmul(mat, single_vec) + assert_array_equal(matmul_col, mm_col_vec.squeeze()) + # Now with a column array: same as for column vector, + # broadcasting sensibly. + mm_col_vec = umt.matrix_multiply(mat, col_vec_array) + matmul_col_vec = umt.matmul(mat, col_vec_array) + assert_array_equal(matmul_col_vec, mm_col_vec) + # As above, but for row vector + single_vec = np.arange(3) + row_vec = single_vec[np.newaxis, :] + row_vec_array = np.arange(24).reshape((4, 2, 1, 1, 3)) + 1 + # row vector @ matrix + mm_row_vec = umt.matrix_multiply(row_vec, mat) + matmul_row_vec = umt.matmul(row_vec, mat) + assert_array_equal(matmul_row_vec, mm_row_vec) + # single row vector @ matrix + assert_raises(ValueError, umt.matrix_multiply, single_vec, mat) + matmul_row = umt.matmul(single_vec, mat) + assert_array_equal(matmul_row, mm_row_vec.squeeze()) + # row vector array @ matrix + mm_row_vec = umt.matrix_multiply(row_vec_array, mat) + matmul_row_vec = umt.matmul(row_vec_array, mat) + assert_array_equal(matmul_row_vec, mm_row_vec) + # Now for vector combinations + # row vector @ column vector + col_vec = row_vec.T + col_vec_array = row_vec_array.swapaxes(-2, -1) + mm_row_col_vec = umt.matrix_multiply(row_vec, col_vec) + matmul_row_col_vec = umt.matmul(row_vec, col_vec) + assert_array_equal(matmul_row_col_vec, mm_row_col_vec) + # single row vector @ single col vector + assert_raises(ValueError, umt.matrix_multiply, single_vec, single_vec) + matmul_row_col = umt.matmul(single_vec, single_vec) + assert_array_equal(matmul_row_col, mm_row_col_vec.squeeze()) + # row vector array @ matrix + mm_row_col_array = umt.matrix_multiply(row_vec_array, col_vec_array) + matmul_row_col_array = umt.matmul(row_vec_array, col_vec_array) + assert_array_equal(matmul_row_col_array, mm_row_col_array) + # Finally, check that things are *not* squeezed if one gives an + # output. + out = np.zeros_like(mm_row_col_array) + out = umt.matrix_multiply(row_vec_array, col_vec_array, out=out) + assert_array_equal(out, mm_row_col_array) + out[:] = 0 + out = umt.matmul(row_vec_array, col_vec_array, out=out) + assert_array_equal(out, mm_row_col_array) + # And check one cannot put missing dimensions back. + out = np.zeros_like(mm_row_col_vec) + assert_raises(ValueError, umt.matrix_multiply, single_vec, single_vec, + out) + # But fine for matmul, since it is just a broadcast. + out = umt.matmul(single_vec, single_vec, out) + assert_array_equal(out, mm_row_col_vec.squeeze()) + def test_matrix_multiply(self): - self.compare_matrix_multiply_results(np.long) + self.compare_matrix_multiply_results(np.int64) self.compare_matrix_multiply_results(np.double) def test_matrix_multiply_umath_empty(self): @@ -972,14 +1310,13 @@ def test_object_logical(self): assert_equal(np.logical_and.reduce(a), None) def test_object_comparison(self): - class HasComparisons(object): + class HasComparisons: def __eq__(self, other): return '==' arr0d = np.array(HasComparisons()) assert_equal(arr0d == arr0d, True) assert_equal(np.equal(arr0d, arr0d), True) # normal behavior is a cast - assert_equal(np.equal(arr0d, arr0d, dtype=object), '==') arr1d = np.array([HasComparisons()]) assert_equal(arr1d == arr1d, np.array([True])) @@ -1002,6 +1339,8 @@ def test_object_array_reduction(self): assert_equal(np.array([[1]], dtype=object).sum(), 1) assert_equal(np.array([[[1, 2]]], dtype=object).sum((0, 1)), [1, 2]) assert_equal(np.array([1], dtype=object).sum(initial=1), 2) + assert_equal(np.array([[1], [2, 3]], dtype=object) + .sum(initial=[0], where=[False, True]), [0, 2, 3]) def test_object_array_accumulate_inplace(self): # Checks that in-place accumulates work, see also gh-7402 @@ -1010,14 +1349,26 @@ def test_object_array_accumulate_inplace(self): # Twice reproduced also for tuples: np.add.accumulate(arr, out=arr) np.add.accumulate(arr, out=arr) - assert_array_equal(arr, np.array([[1]*i for i in [1, 3, 6, 10]])) + assert_array_equal(arr, + np.array([[1]*i for i in [1, 3, 6, 10]], dtype=object), + ) # And the same if the axis argument is used arr = np.ones((2, 4), dtype=object) arr[0, :] = [[2] for i in range(4)] np.add.accumulate(arr, out=arr, axis=-1) np.add.accumulate(arr, out=arr, axis=-1) - assert_array_equal(arr[0, :], np.array([[2]*i for i in [1, 3, 6, 10]])) + assert_array_equal(arr[0, :], + np.array([[2]*i for i in [1, 3, 6, 10]], dtype=object), + ) + + def test_object_array_accumulate_failure(self): + # Typical accumulation on object works as expected: + res = np.add.accumulate(np.array([1, 0, 2], dtype=object)) + assert_array_equal(res, np.array([1, 1, 3], dtype=object)) + # But errors are propagated from the inner-loop if they occur: + with pytest.raises(TypeError): + np.add.accumulate([1, None, 2]) def test_object_array_reduceat_inplace(self): # Checks that in-place reduceats work, see also gh-7465 @@ -1038,6 +1389,15 @@ def test_object_array_reduceat_inplace(self): np.add.reduceat(arr, np.arange(4), out=arr, axis=-1) assert_array_equal(arr, out) + def test_object_array_reduceat_failure(self): + # Reduceat works as expected when no invalid operation occurs (None is + # not involved in an operation here) + res = np.add.reduceat(np.array([1, None, 2], dtype=object), [1, 2]) + assert_array_equal(res, np.array([None, 2], dtype=object)) + # But errors when None would be involved in an operation: + with pytest.raises(TypeError): + np.add.reduceat([1, None, 2], [0, 2]) + def test_zerosize_reduction(self): # Test with default dtype and object dtype for a in [[], np.array([], dtype=object)]: @@ -1137,6 +1497,18 @@ def test_where_param_alloc(self): m = np.array([True], dtype=bool) assert_equal(np.sqrt(a, where=m), [1]) + def test_where_with_broadcasting(self): + # See gh-17198 + a = np.random.random((5000, 4)) + b = np.random.random((5000, 1)) + + where = a > 0.3 + out = np.full_like(a, 0) + np.less(a, b, where=where, out=out) + b_where = np.broadcast_to(b, a.shape)[where] + assert_array_equal((a[where] < b_where), out[where].astype(bool)) + assert not out[~where].any() # outside mask, out remains all 0 + def check_identityless_reduction(self, a): # np.minimum.reduce is an identityless reduction @@ -1236,6 +1608,44 @@ def test_initial_reduction(self): res = np.add.reduce(a, initial=5) assert_equal(res, 15) + @pytest.mark.parametrize('axis', (0, 1, None)) + @pytest.mark.parametrize('where', (np.array([False, True, True]), + np.array([[True], [False], [True]]), + np.array([[True, False, False], + [False, True, False], + [False, True, True]]))) + def test_reduction_with_where(self, axis, where): + a = np.arange(9.).reshape(3, 3) + a_copy = a.copy() + a_check = np.zeros_like(a) + np.positive(a, out=a_check, where=where) + + res = np.add.reduce(a, axis=axis, where=where) + check = a_check.sum(axis) + assert_equal(res, check) + # Check we do not overwrite elements of a internally. + assert_array_equal(a, a_copy) + + @pytest.mark.parametrize(('axis', 'where'), + ((0, np.array([True, False, True])), + (1, [True, True, False]), + (None, True))) + @pytest.mark.parametrize('initial', (-np.inf, 5.)) + def test_reduction_with_where_and_initial(self, axis, where, initial): + a = np.arange(9.).reshape(3, 3) + a_copy = a.copy() + a_check = np.full(a.shape, -np.inf) + np.positive(a, out=a_check, where=where) + + res = np.maximum.reduce(a, axis=axis, where=where, initial=initial) + check = a_check.max(axis, initial=initial) + assert_equal(res, check) + + def test_reduction_where_initial_needed(self): + a = np.arange(9.).reshape(3, 3) + m = [False, True, False] + assert_raises(ValueError, np.maximum.reduce, a, where=m) + def test_identityless_reduction_nonreorderable(self): a = np.array([[8.0, 2.0, 2.0], [1.0, 0.5, 0.25]]) @@ -1347,9 +1757,20 @@ def test_ufunc_custom_out(self): target = np.array([0, 2, 4], dtype=_rational_tests.rational) assert_equal(result, target) - # no output type should raise TypeError + # The new resolution means that we can (usually) find custom loops + # as long as they match exactly: + result = _rational_tests.test_add(a, b) + assert_equal(result, target) + + # This works even more generally, so long the default common-dtype + # promoter works out: + result = _rational_tests.test_add(a, b.astype(np.uint16), out=c) + assert_equal(result, target) + + # But, it can be fooled, e.g. (use scalars, which forces legacy + # type resolution to kick in, which then fails): with assert_raises(TypeError): - _rational_tests.test_add(a, b) + _rational_tests.test_add(a, np.uint16(2)) def test_operand_flags(self): a = np.arange(16, dtype='l').reshape(4, 4) @@ -1372,6 +1793,7 @@ def test_struct_ufunc(self): result = struct_ufunc.add_triplet(a, b) assert_equal(result, np.array([(2, 4, 6)], dtype='u8,u8,u8')) + assert_raises(RuntimeError, struct_ufunc.register_fail) def test_custom_ufunc(self): a = np.array( @@ -1401,7 +1823,7 @@ def test_custom_ufunc_forced_sig(self): def test_custom_array_like(self): - class MyThing(object): + class MyThing: __array_priority__ = 1000 rmul_count = 0 @@ -1589,16 +2011,19 @@ def test_reduce_arguments(self): assert_equal(f(d, 0, None, None, True), r.reshape((1,) + r.shape)) assert_equal(f(d, 0, None, None, False, 0), r) assert_equal(f(d, 0, None, None, False, initial=0), r) + assert_equal(f(d, 0, None, None, False, 0, True), r) + assert_equal(f(d, 0, None, None, False, 0, where=True), r) # multiple keywords assert_equal(f(d, axis=0, dtype=None, out=None, keepdims=False), r) assert_equal(f(d, 0, dtype=None, out=None, keepdims=False), r) assert_equal(f(d, 0, None, out=None, keepdims=False), r) - assert_equal(f(d, 0, None, out=None, keepdims=False, initial=0), r) + assert_equal(f(d, 0, None, out=None, keepdims=False, initial=0, + where=True), r) # too little assert_raises(TypeError, f) # too much - assert_raises(TypeError, f, d, 0, None, None, False, 0, 1) + assert_raises(TypeError, f, d, 0, None, None, False, 0, True, 1) # invalid axis assert_raises(TypeError, f, d, "invalid") assert_raises(TypeError, f, d, axis="invalid") @@ -1619,7 +2044,7 @@ def test_reduce_arguments(self): assert_raises(TypeError, f, d, 0, keepdims="invalid", dtype="invalid", out=None) - # invalid keyord + # invalid keyword assert_raises(TypeError, f, d, axis=0, dtype=None, invalid=0) assert_raises(TypeError, f, d, invalid=0) assert_raises(TypeError, f, d, 0, keepdims=True, invalid="invalid", @@ -1643,6 +2068,16 @@ def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): target = np.array([ True, False, False, False], dtype=bool) assert_equal(np.all(target == (mra == ra[0])), True) + def test_scalar_equal(self): + # Scalar comparisons should always work, without deprecation warnings. + # even when the ufunc fails. + a = np.array(0.) + b = np.array('a') + assert_(a != b) + assert_(b != a) + assert_(not (a == b)) + assert_(not (b == a)) + def test_NotImplemented_not_returned(self): # See gh-5964 and gh-2091. Some of these functions are not operator # related and were fixed for other reasons in the past. @@ -1651,18 +2086,110 @@ def test_NotImplemented_not_returned(self): np.true_divide, np.floor_divide, np.bitwise_and, np.bitwise_or, np.bitwise_xor, np.left_shift, np.right_shift, np.fmax, np.fmin, np.fmod, np.hypot, np.logaddexp, np.logaddexp2, - np.logical_and, np.logical_or, np.logical_xor, np.maximum, - np.minimum, np.mod - ] - - # These functions still return NotImplemented. Will be fixed in - # future. - # bad = [np.greater, np.greater_equal, np.less, np.less_equal, np.not_equal] + np.maximum, np.minimum, np.mod, + np.greater, np.greater_equal, np.less, np.less_equal, + np.equal, np.not_equal] a = np.array('1') b = 1 + c = np.array([1., 2.]) for f in binary_funcs: assert_raises(TypeError, f, a, b) + assert_raises(TypeError, f, c, a) + + @pytest.mark.parametrize("ufunc", + [np.logical_and, np.logical_or]) # logical_xor object loop is bad + @pytest.mark.parametrize("signature", + [(None, None, object), (object, None, None), + (None, object, None)]) + def test_logical_ufuncs_object_signatures(self, ufunc, signature): + a = np.array([True, None, False], dtype=object) + res = ufunc(a, a, signature=signature) + assert res.dtype == object + + @pytest.mark.parametrize("ufunc", + [np.logical_and, np.logical_or, np.logical_xor]) + @pytest.mark.parametrize("signature", + [(bool, None, object), (object, None, bool), + (None, object, bool)]) + def test_logical_ufuncs_mixed_object_signatures(self, ufunc, signature): + # Most mixed signatures fail (except those with bool out, e.g. `OO->?`) + a = np.array([True, None, False]) + with pytest.raises(TypeError): + ufunc(a, a, signature=signature) + + @pytest.mark.parametrize("ufunc", + [np.logical_and, np.logical_or, np.logical_xor]) + def test_logical_ufuncs_support_anything(self, ufunc): + # The logical ufuncs support even input that can't be promoted: + a = np.array('1') + c = np.array([1., 2.]) + assert_array_equal(ufunc(a, c), ufunc([True, True], True)) + assert ufunc.reduce(a) == True + # check that the output has no effect: + out = np.zeros(2, dtype=np.int32) + expected = ufunc([True, True], True).astype(out.dtype) + assert_array_equal(ufunc(a, c, out=out), expected) + out = np.zeros((), dtype=np.int32) + assert ufunc.reduce(a, out=out) == True + # Last check, test reduction when out and a match (the complexity here + # is that the "i,i->?" may seem right, but should not match. + a = np.array([3], dtype="i") + out = np.zeros((), dtype=a.dtype) + assert ufunc.reduce(a, out=out) == 1 + + @pytest.mark.parametrize("ufunc", + [np.logical_and, np.logical_or, np.logical_xor]) + def test_logical_ufuncs_out_cast_check(self, ufunc): + a = np.array('1') + c = np.array([1., 2.]) + out = a.copy() + with pytest.raises(TypeError): + # It would be safe, but not equiv casting: + ufunc(a, c, out=out, casting="equiv") + + def test_reducelike_out_promotes(self): + # Check that the out argument to reductions is considered for + # promotion. See also gh-20455. + # Note that these paths could prefer `initial=` in the future and + # do not up-cast to the default integer for add and prod + arr = np.ones(1000, dtype=np.uint8) + out = np.zeros((), dtype=np.uint16) + assert np.add.reduce(arr, out=out) == 1000 + arr[:10] = 2 + assert np.multiply.reduce(arr, out=out) == 2**10 + + # For legacy dtypes, the signature currently has to be forced if `out=` + # is passed. The two paths below should differ, without `dtype=` the + # expected result should be: `np.prod(arr.astype("f8")).astype("f4")`! + arr = np.full(5, 2**25-1, dtype=np.int64) + + # float32 and int64 promote to float64: + res = np.zeros((), dtype=np.float32) + # If `dtype=` is passed, the calculation is forced to float32: + single_res = np.zeros((), dtype=np.float32) + np.multiply.reduce(arr, out=single_res, dtype=np.float32) + assert single_res != res + + def test_reducelike_output_needs_identical_cast(self): + # Checks the case where the we have a simple byte-swap works, maily + # tests that this is not rejected directly. + # (interesting because we require descriptor identity in reducelikes). + arr = np.ones(20, dtype="f8") + out = np.empty((), dtype=arr.dtype.newbyteorder()) + expected = np.add.reduce(arr) + np.add.reduce(arr, out=out) + assert_array_equal(expected, out) + # Check reduceat: + out = np.empty(2, dtype=arr.dtype.newbyteorder()) + expected = np.add.reduceat(arr, [0, 1]) + np.add.reduceat(arr, [0, 1], out=out) + assert_array_equal(expected, out) + # And accumulate: + out = np.empty(arr.shape, dtype=arr.dtype.newbyteorder()) + expected = np.add.accumulate(arr) + np.add.accumulate(arr, out=out) + assert_array_equal(expected, out) def test_reduce_noncontig_output(self): # Check that reduction deals with non-contiguous output arrays @@ -1685,6 +2212,258 @@ def test_reduce_noncontig_output(self): assert_equal(y_base[1,:], y_base_copy[1,:]) assert_equal(y_base[3,:], y_base_copy[3,:]) + @pytest.mark.parametrize("with_cast", [True, False]) + def test_reduceat_and_accumulate_out_shape_mismatch(self, with_cast): + # Should raise an error mentioning "shape" or "size" + arr = np.arange(5) + out = np.arange(3) # definitely wrong shape + if with_cast: + # If a cast is necessary on the output, we can be sure to use + # the generic NpyIter (non-fast) path. + out = out.astype(np.float64) + + with pytest.raises(ValueError, match="(shape|size)"): + np.add.reduceat(arr, [0, 3], out=out) + + with pytest.raises(ValueError, match="(shape|size)"): + np.add.accumulate(arr, out=out) + + @pytest.mark.parametrize('out_shape', + [(), (1,), (3,), (1, 1), (1, 3), (4, 3)]) + @pytest.mark.parametrize('keepdims', [True, False]) + @pytest.mark.parametrize('f_reduce', [np.add.reduce, np.minimum.reduce]) + def test_reduce_wrong_dimension_output(self, f_reduce, keepdims, out_shape): + # Test that we're not incorrectly broadcasting dimensions. + # See gh-15144 (failed for np.add.reduce previously). + a = np.arange(12.).reshape(4, 3) + out = np.empty(out_shape, a.dtype) + + correct_out = f_reduce(a, axis=0, keepdims=keepdims) + if out_shape != correct_out.shape: + with assert_raises(ValueError): + f_reduce(a, axis=0, out=out, keepdims=keepdims) + else: + check = f_reduce(a, axis=0, out=out, keepdims=keepdims) + assert_(check is out) + assert_array_equal(check, correct_out) + + def test_reduce_output_does_not_broadcast_input(self): + # Test that the output shape cannot broadcast an input dimension + # (it never can add dimensions, but it might expand an existing one) + a = np.ones((1, 10)) + out_correct = (np.empty((1, 1))) + out_incorrect = np.empty((3, 1)) + np.add.reduce(a, axis=-1, out=out_correct, keepdims=True) + np.add.reduce(a, axis=-1, out=out_correct[:, 0], keepdims=False) + with assert_raises(ValueError): + np.add.reduce(a, axis=-1, out=out_incorrect, keepdims=True) + with assert_raises(ValueError): + np.add.reduce(a, axis=-1, out=out_incorrect[:, 0], keepdims=False) + + def test_reduce_output_subclass_ok(self): + class MyArr(np.ndarray): + pass + + out = np.empty(()) + np.add.reduce(np.ones(5), out=out) # no subclass, all fine + out = out.view(MyArr) + assert np.add.reduce(np.ones(5), out=out) is out + assert type(np.add.reduce(out)) is MyArr + def test_no_doc_string(self): # gh-9337 assert_('\n' not in umt.inner1d_no_doc.__doc__) + + def test_invalid_args(self): + # gh-7961 + exc = pytest.raises(TypeError, np.sqrt, None) + # minimally check the exception text + assert exc.match('loop of ufunc does not support') + + @pytest.mark.parametrize('nat', [np.datetime64('nat'), np.timedelta64('nat')]) + def test_nat_is_not_finite(self, nat): + try: + assert not np.isfinite(nat) + except TypeError: + pass # ok, just not implemented + + @pytest.mark.parametrize('nat', [np.datetime64('nat'), np.timedelta64('nat')]) + def test_nat_is_nan(self, nat): + try: + assert np.isnan(nat) + except TypeError: + pass # ok, just not implemented + + @pytest.mark.parametrize('nat', [np.datetime64('nat'), np.timedelta64('nat')]) + def test_nat_is_not_inf(self, nat): + try: + assert not np.isinf(nat) + except TypeError: + pass # ok, just not implemented + + +@pytest.mark.parametrize('ufunc', [getattr(np, x) for x in dir(np) + if isinstance(getattr(np, x), np.ufunc)]) +def test_ufunc_types(ufunc): + ''' + Check all ufuncs that the correct type is returned. Avoid + object and boolean types since many operations are not defined for + for them. + + Choose the shape so even dot and matmul will succeed + ''' + for typ in ufunc.types: + # types is a list of strings like ii->i + if 'O' in typ or '?' in typ: + continue + inp, out = typ.split('->') + args = [np.ones((3, 3), t) for t in inp] + with warnings.catch_warnings(record=True): + warnings.filterwarnings("always") + res = ufunc(*args) + if isinstance(res, tuple): + outs = tuple(out) + assert len(res) == len(outs) + for r, t in zip(res, outs): + assert r.dtype == np.dtype(t) + else: + assert res.dtype == np.dtype(out) + +@pytest.mark.parametrize('ufunc', [getattr(np, x) for x in dir(np) + if isinstance(getattr(np, x), np.ufunc)]) +def test_ufunc_noncontiguous(ufunc): + ''' + Check that contiguous and non-contiguous calls to ufuncs + have the same results for values in range(9) + ''' + for typ in ufunc.types: + # types is a list of strings like ii->i + if any(set('O?mM') & set(typ)): + # bool, object, datetime are too irregular for this simple test + continue + inp, out = typ.split('->') + args_c = [np.empty(6, t) for t in inp] + args_n = [np.empty(18, t)[::3] for t in inp] + for a in args_c: + a.flat = range(1,7) + for a in args_n: + a.flat = range(1,7) + with warnings.catch_warnings(record=True): + warnings.filterwarnings("always") + res_c = ufunc(*args_c) + res_n = ufunc(*args_n) + if len(out) == 1: + res_c = (res_c,) + res_n = (res_n,) + for c_ar, n_ar in zip(res_c, res_n): + dt = c_ar.dtype + if np.issubdtype(dt, np.floating): + # for floating point results allow a small fuss in comparisons + # since different algorithms (libm vs. intrinsics) can be used + # for different input strides + res_eps = np.finfo(dt).eps + tol = 2*res_eps + assert_allclose(res_c, res_n, atol=tol, rtol=tol) + else: + assert_equal(c_ar, n_ar) + + +@pytest.mark.parametrize('ufunc', [np.sign, np.equal]) +def test_ufunc_warn_with_nan(ufunc): + # issue gh-15127 + # test that calling certain ufuncs with a non-standard `nan` value does not + # emit a warning + # `b` holds a 64 bit signaling nan: the most significant bit of the + # significand is zero. + b = np.array([0x7ff0000000000001], 'i8').view('f8') + assert np.isnan(b) + if ufunc.nin == 1: + ufunc(b) + elif ufunc.nin == 2: + ufunc(b, b.copy()) + else: + raise ValueError('ufunc with more than 2 inputs') + + +@pytest.mark.skipif(not HAS_REFCOUNT, reason="Python lacks refcounts") +def test_ufunc_casterrors(): + # Tests that casting errors are correctly reported and buffers are + # cleared. + # The following array can be added to itself as an object array, but + # the result cannot be cast to an integer output: + value = 123 # relies on python cache (leak-check will still find it) + arr = np.array([value] * int(np.BUFSIZE * 1.5) + + ["string"] + + [value] * int(1.5 * np.BUFSIZE), dtype=object) + out = np.ones(len(arr), dtype=np.intp) + + count = sys.getrefcount(value) + with pytest.raises(ValueError): + # Output casting failure: + np.add(arr, arr, out=out, casting="unsafe") + + assert count == sys.getrefcount(value) + # output is unchanged after the error, this shows that the iteration + # was aborted (this is not necessarily defined behaviour) + assert out[-1] == 1 + + with pytest.raises(ValueError): + # Input casting failure: + np.add(arr, arr, out=out, dtype=np.intp, casting="unsafe") + + assert count == sys.getrefcount(value) + # output is unchanged after the error, this shows that the iteration + # was aborted (this is not necessarily defined behaviour) + assert out[-1] == 1 + + +def test_trivial_loop_invalid_cast(): + # This tests the fast-path "invalid cast", see gh-19904. + with pytest.raises(TypeError, + match="cast ufunc 'add' input 0"): + # the void dtype definitely cannot cast to double: + np.add(np.array(1, "i,i"), 3, signature="dd->d") + + +@pytest.mark.skipif(not HAS_REFCOUNT, reason="Python lacks refcounts") +@pytest.mark.parametrize("offset", + [0, np.BUFSIZE//2, int(1.5*np.BUFSIZE)]) +def test_reduce_casterrors(offset): + # Test reporting of casting errors in reductions, we test various + # offsets to where the casting error will occur, since these may occur + # at different places during the reduction procedure. For example + # the first item may be special. + value = 123 # relies on python cache (leak-check will still find it) + arr = np.array([value] * offset + + ["string"] + + [value] * int(1.5 * np.BUFSIZE), dtype=object) + out = np.array(-1, dtype=np.intp) + + count = sys.getrefcount(value) + with pytest.raises(ValueError, match="invalid literal"): + # This is an unsafe cast, but we currently always allow that. + # Note that the double loop is picked, but the cast fails. + np.add.reduce(arr, dtype=np.intp, out=out) + assert count == sys.getrefcount(value) + # If an error occurred during casting, the operation is done at most until + # the error occurs (the result of which would be `value * offset`) and -1 + # if the error happened immediately. + # This does not define behaviour, the output is invalid and thus undefined + assert out[()] < value * offset + + +@pytest.mark.parametrize("method", + [np.add.accumulate, np.add.reduce, + pytest.param(lambda x: np.add.reduceat(x, [0]), id="reduceat"), + pytest.param(lambda x: np.log.at(x, [2]), id="at")]) +def test_ufunc_methods_floaterrors(method): + # adding inf and -inf (or log(-inf) creates an invalid float and warns + arr = np.array([np.inf, 0, -np.inf]) + with np.errstate(all="warn"): + with pytest.warns(RuntimeWarning, match="invalid value"): + method(arr) + + arr = np.array([np.inf, 0, -np.inf]) + with np.errstate(all="raise"): + with pytest.raises(FloatingPointError): + method(arr) diff --git a/numpy/core/tests/test_umath.py b/numpy/core/tests/test_umath.py index 4772913be6b9..c0b26e75b2c8 100644 --- a/numpy/core/tests/test_umath.py +++ b/numpy/core/tests/test_umath.py @@ -1,11 +1,12 @@ -from __future__ import division, absolute_import, print_function - -import sys import platform import warnings import fnmatch import itertools import pytest +import sys +import os +from fractions import Fraction +from functools import reduce import numpy.core.umath as ncu from numpy.core import _umath_tests as ncu_tests @@ -13,10 +14,21 @@ from numpy.testing import ( assert_, assert_equal, assert_raises, assert_raises_regex, assert_array_equal, assert_almost_equal, assert_array_almost_equal, - assert_allclose, assert_no_warnings, suppress_warnings, - _gen_alignment_data, + assert_array_max_ulp, assert_allclose, assert_no_warnings, suppress_warnings, + _gen_alignment_data, assert_array_almost_equal_nulp ) +def get_glibc_version(): + try: + ver = os.confstr('CS_GNU_LIBC_VERSION').rsplit(' ')[1] + except Exception as inst: + ver = '0.0' + + return ver + + +glibcver = get_glibc_version() +glibc_older_than = lambda x: (glibcver != '0.0' and glibcver < x) def on_powerpc(): """ True if we are running on a Power PC platform.""" @@ -24,7 +36,20 @@ def on_powerpc(): platform.machine().startswith('ppc') -class _FilterInvalids(object): +def bad_arcsinh(): + """The blocklisted trig functions are not accurate on aarch64 for + complex256. Rather than dig through the actual problem skip the + test. This should be fixed when we can move past glibc2.17 + which is the version in manylinux2014 + """ + x = 1.78e-10 + v1 = np.arcsinh(np.float128(x)) + v2 = np.arcsinh(np.complex256(x)).real + # The eps for float128 is 1-e33, so this is way bigger + return abs((v1 / v2) - 1.0) > 1e-23 + + +class _FilterInvalids: def setup(self): self.olderr = np.seterr(invalid='ignore') @@ -32,7 +57,7 @@ def teardown(self): np.seterr(**self.olderr) -class TestConstants(object): +class TestConstants: def test_pi(self): assert_allclose(ncu.pi, 3.141592653589793, 1e-15) @@ -43,7 +68,7 @@ def test_euler_gamma(self): assert_allclose(ncu.euler_gamma, 0.5772156649015329, 1e-15) -class TestOut(object): +class TestOut: def test_out_subok(self): for subok in (True, False): a = np.array(0.5) @@ -76,15 +101,13 @@ def test_out_subok(self): assert_(r1 is o1) assert_(r2 is o2) - with warnings.catch_warnings(record=True) as w: - warnings.filterwarnings('always', '', DeprecationWarning) + with assert_raises(TypeError): + # Out argument must be tuple, since there are multiple outputs. r1, r2 = np.frexp(d, out=o1, subok=subok) - assert_(r1 is o1) - assert_(w[0].category is DeprecationWarning) - assert_raises(ValueError, np.add, a, 2, o, o, subok=subok) - assert_raises(ValueError, np.add, a, 2, o, out=o, subok=subok) - assert_raises(ValueError, np.add, a, 2, None, out=o, subok=subok) + assert_raises(TypeError, np.add, a, 2, o, o, subok=subok) + assert_raises(TypeError, np.add, a, 2, o, out=o, subok=subok) + assert_raises(TypeError, np.add, a, 2, None, out=o, subok=subok) assert_raises(ValueError, np.add, a, 2, out=(o, o), subok=subok) assert_raises(ValueError, np.add, a, 2, out=(), subok=subok) assert_raises(TypeError, np.add, a, 2, [], subok=subok) @@ -166,25 +189,20 @@ def __array_wrap__(self, arr, context): else: assert_(type(r1) == np.ndarray) - with warnings.catch_warnings(record=True) as w: - warnings.filterwarnings('always', '', DeprecationWarning) + with assert_raises(TypeError): + # Out argument must be tuple, since there are multiple outputs. r1, r2 = np.frexp(d, out=o1, subok=subok) - if subok: - assert_(isinstance(r2, ArrayWrap)) - else: - assert_(type(r2) == np.ndarray) - assert_(w[0].category is DeprecationWarning) -class TestComparisons(object): +class TestComparisons: def test_ignore_object_identity_in_equal(self): - # Check error raised when comparing identical objects whose comparison + # Check comparing identical objects whose comparison # is not a simple boolean, e.g., arrays that are compared elementwise. a = np.array([np.array([1, 2, 3]), None], dtype=object) assert_raises(ValueError, np.equal, a, a) # Check error raised when comparing identical non-comparable objects. - class FunkyType(object): + class FunkyType: def __eq__(self, other): raise TypeError("I won't compare") @@ -196,13 +214,13 @@ def __eq__(self, other): assert_equal(np.equal(a, a), [False]) def test_ignore_object_identity_in_not_equal(self): - # Check error raised when comparing identical objects whose comparison + # Check comparing identical objects whose comparison # is not a simple boolean, e.g., arrays that are compared elementwise. a = np.array([np.array([1, 2, 3]), None], dtype=object) assert_raises(ValueError, np.not_equal, a, a) # Check error raised when comparing identical non-comparable objects. - class FunkyType(object): + class FunkyType: def __ne__(self, other): raise TypeError("I won't compare") @@ -214,7 +232,7 @@ def __ne__(self, other): assert_equal(np.not_equal(a, a), [True]) -class TestAdd(object): +class TestAdd: def test_reduce_alignment(self): # gh-9876 # make sure arrays with weird strides work with the optimizations in @@ -225,7 +243,7 @@ def test_reduce_alignment(self): assert_equal(a['b'].sum(), 0) -class TestDivision(object): +class TestDivision: def test_division_int(self): # int division should follow Python x = np.array([5, 10, 90, 100, -5, -10, -90, -100, -120]) @@ -237,6 +255,148 @@ def test_division_int(self): assert_equal(x // 100, [0, 0, 0, 1, -1, -1, -1, -1, -2]) assert_equal(x % 100, [5, 10, 90, 0, 95, 90, 10, 0, 80]) + @pytest.mark.parametrize("dtype,ex_val", itertools.product( + np.sctypes['int'] + np.sctypes['uint'], ( + ( + # dividend + "np.arange(fo.max-lsize, fo.max, dtype=dtype)," + # divisors + "np.arange(lsize, dtype=dtype)," + # scalar divisors + "range(15)" + ), + ( + # dividend + "np.arange(fo.min, fo.min+lsize, dtype=dtype)," + # divisors + "np.arange(lsize//-2, lsize//2, dtype=dtype)," + # scalar divisors + "range(fo.min, fo.min + 15)" + ), ( + # dividend + "np.arange(fo.max-lsize, fo.max, dtype=dtype)," + # divisors + "np.arange(lsize, dtype=dtype)," + # scalar divisors + "[1,3,9,13,neg, fo.min+1, fo.min//2, fo.max//3, fo.max//4]" + ) + ) + )) + def test_division_int_boundary(self, dtype, ex_val): + fo = np.iinfo(dtype) + neg = -1 if fo.min < 0 else 1 + # Large enough to test SIMD loops and remaind elements + lsize = 512 + 7 + a, b, divisors = eval(ex_val) + a_lst, b_lst = a.tolist(), b.tolist() + + c_div = lambda n, d: ( + 0 if d == 0 or (n and n == fo.min and d == -1) else n//d + ) + with np.errstate(divide='ignore'): + ac = a.copy() + ac //= b + div_ab = a // b + div_lst = [c_div(x, y) for x, y in zip(a_lst, b_lst)] + + msg = "Integer arrays floor division check (//)" + assert all(div_ab == div_lst), msg + msg_eq = "Integer arrays floor division check (//=)" + assert all(ac == div_lst), msg_eq + + for divisor in divisors: + ac = a.copy() + with np.errstate(divide='ignore'): + div_a = a // divisor + ac //= divisor + div_lst = [c_div(i, divisor) for i in a_lst] + + assert all(div_a == div_lst), msg + assert all(ac == div_lst), msg_eq + + with np.errstate(divide='raise'): + if 0 in b or (fo.min and -1 in b and fo.min in a): + # Verify overflow case + with pytest.raises(FloatingPointError): + a // b + else: + a // b + if fo.min and fo.min in a: + with pytest.raises(FloatingPointError): + a // -1 + elif fo.min: + a // -1 + with pytest.raises(FloatingPointError): + a // 0 + with pytest.raises(FloatingPointError): + ac = a.copy() + ac //= 0 + + np.array([], dtype=dtype) // 0 + + @pytest.mark.parametrize("dtype,ex_val", itertools.product( + np.sctypes['int'] + np.sctypes['uint'], ( + "np.array([fo.max, 1, 2, 1, 1, 2, 3], dtype=dtype)", + "np.array([fo.min, 1, -2, 1, 1, 2, -3], dtype=dtype)", + "np.arange(fo.min, fo.min+(100*10), 10, dtype=dtype)", + "np.arange(fo.max-(100*7), fo.max, 7, dtype=dtype)", + ) + )) + def test_division_int_reduce(self, dtype, ex_val): + fo = np.iinfo(dtype) + a = eval(ex_val) + lst = a.tolist() + c_div = lambda n, d: ( + 0 if d == 0 or (n and n == fo.min and d == -1) else n//d + ) + + with np.errstate(divide='ignore'): + div_a = np.floor_divide.reduce(a) + div_lst = reduce(c_div, lst) + msg = "Reduce floor integer division check" + assert div_a == div_lst, msg + + with np.errstate(divide='raise'): + with pytest.raises(FloatingPointError): + np.floor_divide.reduce(np.arange(-100, 100, dtype=dtype)) + if fo.min: + with pytest.raises(FloatingPointError): + np.floor_divide.reduce( + np.array([fo.min, 1, -1], dtype=dtype) + ) + + @pytest.mark.parametrize( + "dividend,divisor,quotient", + [(np.timedelta64(2,'Y'), np.timedelta64(2,'M'), 12), + (np.timedelta64(2,'Y'), np.timedelta64(-2,'M'), -12), + (np.timedelta64(-2,'Y'), np.timedelta64(2,'M'), -12), + (np.timedelta64(-2,'Y'), np.timedelta64(-2,'M'), 12), + (np.timedelta64(2,'M'), np.timedelta64(-2,'Y'), -1), + (np.timedelta64(2,'Y'), np.timedelta64(0,'M'), 0), + (np.timedelta64(2,'Y'), 2, np.timedelta64(1,'Y')), + (np.timedelta64(2,'Y'), -2, np.timedelta64(-1,'Y')), + (np.timedelta64(-2,'Y'), 2, np.timedelta64(-1,'Y')), + (np.timedelta64(-2,'Y'), -2, np.timedelta64(1,'Y')), + (np.timedelta64(-2,'Y'), -2, np.timedelta64(1,'Y')), + (np.timedelta64(-2,'Y'), -3, np.timedelta64(0,'Y')), + (np.timedelta64(-2,'Y'), 0, np.timedelta64('Nat','Y')), + ]) + def test_division_int_timedelta(self, dividend, divisor, quotient): + # If either divisor is 0 or quotient is Nat, check for division by 0 + if divisor and (isinstance(quotient, int) or not np.isnat(quotient)): + msg = "Timedelta floor division check" + assert dividend // divisor == quotient, msg + + # Test for arrays as well + msg = "Timedelta arrays floor division check" + dividend_array = np.array([dividend]*5) + quotient_array = np.array([quotient]*5) + assert all(dividend_array // divisor == quotient_array), msg + else: + with np.errstate(divide='raise', invalid='raise'): + with pytest.raises(FloatingPointError): + dividend // divisor + def test_division_complex(self): # check that implementation is correct msg = "Complex division implementation check" @@ -263,17 +423,62 @@ def test_zero_division_complex(self): assert_(np.isnan(y)[0]) def test_floor_division_complex(self): - # check that implementation is correct - msg = "Complex floor division implementation check" + # check that floor division, divmod and remainder raises type errors x = np.array([.9 + 1j, -.1 + 1j, .9 + .5*1j, .9 + 2.*1j], dtype=np.complex128) - y = np.array([0., -1., 0., 0.], dtype=np.complex128) - assert_equal(np.floor_divide(x**2, x), y, err_msg=msg) - # check overflow, underflow - msg = "Complex floor division overflow/underflow check" - x = np.array([1.e+110, 1.e-110], dtype=np.complex128) - y = np.floor_divide(x**2, x) - assert_equal(y, [1.e+110, 0], err_msg=msg) - + with pytest.raises(TypeError): + x // 7 + with pytest.raises(TypeError): + np.divmod(x, 7) + with pytest.raises(TypeError): + np.remainder(x, 7) + + def test_floor_division_signed_zero(self): + # Check that the sign bit is correctly set when dividing positive and + # negative zero by one. + x = np.zeros(10) + assert_equal(np.signbit(x//1), 0) + assert_equal(np.signbit((-x)//1), 1) + + @pytest.mark.parametrize('dtype', np.typecodes['Float']) + def test_floor_division_errors(self, dtype): + fnan = np.array(np.nan, dtype=dtype) + fone = np.array(1.0, dtype=dtype) + fzer = np.array(0.0, dtype=dtype) + finf = np.array(np.inf, dtype=dtype) + # divide by zero error check + with np.errstate(divide='raise', invalid='ignore'): + assert_raises(FloatingPointError, np.floor_divide, fone, fzer) + with np.errstate(divide='ignore', invalid='raise'): + np.floor_divide(fone, fzer) + + # The following already contain a NaN and should not warn + with np.errstate(all='raise'): + np.floor_divide(fnan, fone) + np.floor_divide(fone, fnan) + np.floor_divide(fnan, fzer) + np.floor_divide(fzer, fnan) + + @pytest.mark.parametrize('dtype', np.typecodes['Float']) + def test_floor_division_corner_cases(self, dtype): + # test corner cases like 1.0//0.0 for errors and return vals + x = np.zeros(10, dtype=dtype) + y = np.ones(10, dtype=dtype) + fnan = np.array(np.nan, dtype=dtype) + fone = np.array(1.0, dtype=dtype) + fzer = np.array(0.0, dtype=dtype) + finf = np.array(np.inf, dtype=dtype) + with suppress_warnings() as sup: + sup.filter(RuntimeWarning, "invalid value encountered in floor_divide") + div = np.floor_divide(fnan, fone) + assert(np.isnan(div)), "dt: %s, div: %s" % (dt, div) + div = np.floor_divide(fone, fnan) + assert(np.isnan(div)), "dt: %s, div: %s" % (dt, div) + div = np.floor_divide(fnan, fzer) + assert(np.isnan(div)), "dt: %s, div: %s" % (dt, div) + # verify 1.0//0.0 computations return inf + with np.errstate(divide='ignore'): + z = np.floor_divide(y, x) + assert_(np.isinf(z).all()) def floor_divide_and_remainder(x, y): return (np.floor_divide(x, y), np.remainder(x, y)) @@ -286,7 +491,7 @@ def _signs(dt): return (+1, -1) -class TestRemainder(object): +class TestRemainder: def test_remainder_basic(self): dt = np.typecodes['AllInteger'] + np.typecodes['Float'] @@ -348,9 +553,102 @@ def test_float_remainder_roundoff(self): else: assert_(b > rem >= 0, msg) + @pytest.mark.xfail(sys.platform.startswith("darwin"), + reason="MacOS seems to not give the correct 'invalid' warning for " + "`fmod`. Hopefully, others always do.") + @pytest.mark.parametrize('dtype', np.typecodes['Float']) + def test_float_divmod_errors(self, dtype): + # Check valid errors raised for divmod and remainder + fzero = np.array(0.0, dtype=dtype) + fone = np.array(1.0, dtype=dtype) + finf = np.array(np.inf, dtype=dtype) + fnan = np.array(np.nan, dtype=dtype) + # since divmod is combination of both remainder and divide + # ops it will set both dividebyzero and invalid flags + with np.errstate(divide='raise', invalid='ignore'): + assert_raises(FloatingPointError, np.divmod, fone, fzero) + with np.errstate(divide='ignore', invalid='raise'): + assert_raises(FloatingPointError, np.divmod, fone, fzero) + with np.errstate(invalid='raise'): + assert_raises(FloatingPointError, np.divmod, fzero, fzero) + with np.errstate(invalid='raise'): + assert_raises(FloatingPointError, np.divmod, finf, finf) + with np.errstate(divide='ignore', invalid='raise'): + assert_raises(FloatingPointError, np.divmod, finf, fzero) + with np.errstate(divide='raise', invalid='ignore'): + # inf / 0 does not set any flags, only the modulo creates a NaN + np.divmod(finf, fzero) + + @pytest.mark.xfail(sys.platform.startswith("darwin"), + reason="MacOS seems to not give the correct 'invalid' warning for " + "`fmod`. Hopefully, others always do.") + @pytest.mark.parametrize('dtype', np.typecodes['Float']) + @pytest.mark.parametrize('fn', [np.fmod, np.remainder]) + def test_float_remainder_errors(self, dtype, fn): + fzero = np.array(0.0, dtype=dtype) + fone = np.array(1.0, dtype=dtype) + finf = np.array(np.inf, dtype=dtype) + fnan = np.array(np.nan, dtype=dtype) + + # The following already contain a NaN and should not warn. + with np.errstate(all='raise'): + with pytest.raises(FloatingPointError, + match="invalid value"): + fn(fone, fzero) + fn(fnan, fzero) + fn(fzero, fnan) + fn(fone, fnan) + fn(fnan, fone) + + def test_float_remainder_overflow(self): + a = np.finfo(np.float64).tiny + with np.errstate(over='ignore', invalid='ignore'): + div, mod = np.divmod(4, a) + np.isinf(div) + assert_(mod == 0) + with np.errstate(over='raise', invalid='ignore'): + assert_raises(FloatingPointError, np.divmod, 4, a) + with np.errstate(invalid='raise', over='ignore'): + assert_raises(FloatingPointError, np.divmod, 4, a) + + def test_float_divmod_corner_cases(self): + # check nan cases + for dt in np.typecodes['Float']: + fnan = np.array(np.nan, dtype=dt) + fone = np.array(1.0, dtype=dt) + fzer = np.array(0.0, dtype=dt) + finf = np.array(np.inf, dtype=dt) + with suppress_warnings() as sup: + sup.filter(RuntimeWarning, "invalid value encountered in divmod") + sup.filter(RuntimeWarning, "divide by zero encountered in divmod") + div, rem = np.divmod(fone, fzer) + assert(np.isinf(div)), 'dt: %s, div: %s' % (dt, rem) + assert(np.isnan(rem)), 'dt: %s, rem: %s' % (dt, rem) + div, rem = np.divmod(fzer, fzer) + assert(np.isnan(rem)), 'dt: %s, rem: %s' % (dt, rem) + assert_(np.isnan(div)), 'dt: %s, rem: %s' % (dt, rem) + div, rem = np.divmod(finf, finf) + assert(np.isnan(div)), 'dt: %s, rem: %s' % (dt, rem) + assert(np.isnan(rem)), 'dt: %s, rem: %s' % (dt, rem) + div, rem = np.divmod(finf, fzer) + assert(np.isinf(div)), 'dt: %s, rem: %s' % (dt, rem) + assert(np.isnan(rem)), 'dt: %s, rem: %s' % (dt, rem) + div, rem = np.divmod(fnan, fone) + assert(np.isnan(rem)), "dt: %s, rem: %s" % (dt, rem) + assert(np.isnan(div)), "dt: %s, rem: %s" % (dt, rem) + div, rem = np.divmod(fone, fnan) + assert(np.isnan(rem)), "dt: %s, rem: %s" % (dt, rem) + assert(np.isnan(div)), "dt: %s, rem: %s" % (dt, rem) + div, rem = np.divmod(fnan, fzer) + assert(np.isnan(rem)), "dt: %s, rem: %s" % (dt, rem) + assert(np.isnan(div)), "dt: %s, rem: %s" % (dt, rem) + def test_float_remainder_corner_cases(self): # Check remainder magnitude. for dt in np.typecodes['Float']: + fone = np.array(1.0, dtype=dt) + fzer = np.array(0.0, dtype=dt) + fnan = np.array(np.nan, dtype=dt) b = np.array(1.0, dtype=dt) a = np.nextafter(np.array(0.0, dtype=dt), -b) rem = np.remainder(a, b) @@ -361,6 +659,7 @@ def test_float_remainder_corner_cases(self): # Check nans, inf with suppress_warnings() as sup: sup.filter(RuntimeWarning, "invalid value encountered in remainder") + sup.filter(RuntimeWarning, "invalid value encountered in fmod") for dt in np.typecodes['Float']: fone = np.array(1.0, dtype=dt) fzer = np.array(0.0, dtype=dt) @@ -371,13 +670,33 @@ def test_float_remainder_corner_cases(self): # MSVC 2008 returns NaN here, so disable the check. #rem = np.remainder(fone, finf) #assert_(rem == fone, 'dt: %s, rem: %s' % (dt, rem)) + rem = np.remainder(finf, fone) + fmod = np.fmod(finf, fone) + assert_(np.isnan(fmod), 'dt: %s, fmod: %s' % (dt, fmod)) + assert_(np.isnan(rem), 'dt: %s, rem: %s' % (dt, rem)) + rem = np.remainder(finf, finf) + fmod = np.fmod(finf, fone) + assert_(np.isnan(rem), 'dt: %s, rem: %s' % (dt, rem)) + assert_(np.isnan(fmod), 'dt: %s, fmod: %s' % (dt, fmod)) + rem = np.remainder(finf, fzer) + fmod = np.fmod(finf, fzer) + assert_(np.isnan(rem), 'dt: %s, rem: %s' % (dt, rem)) + assert_(np.isnan(fmod), 'dt: %s, fmod: %s' % (dt, fmod)) rem = np.remainder(fone, fnan) + fmod = np.fmod(fone, fnan) assert_(np.isnan(rem), 'dt: %s, rem: %s' % (dt, rem)) - rem = np.remainder(finf, fone) + assert_(np.isnan(fmod), 'dt: %s, fmod: %s' % (dt, fmod)) + rem = np.remainder(fnan, fzer) + fmod = np.fmod(fnan, fzer) assert_(np.isnan(rem), 'dt: %s, rem: %s' % (dt, rem)) + assert_(np.isnan(fmod), 'dt: %s, fmod: %s' % (dt, rem)) + rem = np.remainder(fnan, fone) + fmod = np.fmod(fnan, fone) + assert_(np.isnan(rem), 'dt: %s, rem: %s' % (dt, rem)) + assert_(np.isnan(fmod), 'dt: %s, fmod: %s' % (dt, rem)) -class TestCbrt(object): +class TestCbrt: def test_cbrt_scalar(self): assert_almost_equal((np.cbrt(np.float32(-2.5)**3)), -2.5) @@ -390,7 +709,7 @@ def test_cbrt(self): assert_equal(np.cbrt(-np.inf), -np.inf) -class TestPower(object): +class TestPower: def test_power_float(self): x = np.array([1., 2., 3.]) assert_equal(x**0, [1., 1., 1.]) @@ -529,7 +848,7 @@ def test_integer_to_negative_power(self): assert_raises(ValueError, np.power, one, minusone) -class TestFloat_power(object): +class TestFloat_power: def test_type_conversion(self): arg_type = '?bhilBHILefdgFDG' res_type = 'ddddddddddddgDDG' @@ -540,21 +859,21 @@ def test_type_conversion(self): assert_(res.dtype.name == np.dtype(dtout).name, msg) -class TestLog2(object): - def test_log2_values(self): +class TestLog2: + @pytest.mark.parametrize('dt', ['f', 'd', 'g']) + def test_log2_values(self, dt): x = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024] y = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - for dt in ['f', 'd', 'g']: - xf = np.array(x, dtype=dt) - yf = np.array(y, dtype=dt) - assert_almost_equal(np.log2(xf), yf) + xf = np.array(x, dtype=dt) + yf = np.array(y, dtype=dt) + assert_almost_equal(np.log2(xf), yf) - def test_log2_ints(self): + @pytest.mark.parametrize("i", range(1, 65)) + def test_log2_ints(self, i): # a good log2 implementation should provide this, # might fail on OS with bad libm - for i in range(1, 65): - v = np.log2(2.**i) - assert_equal(v, float(i), err_msg='at exponent %d' % i) + v = np.log2(2.**i) + assert_equal(v, float(i), err_msg='at exponent %d' % i) def test_log2_special(self): assert_equal(np.log2(1.), 0.) @@ -571,7 +890,7 @@ def test_log2_special(self): assert_(w[2].category is RuntimeWarning) -class TestExp2(object): +class TestExp2: def test_exp2_values(self): x = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024] y = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] @@ -622,8 +941,14 @@ def test_nan(self): assert_(np.isnan(np.logaddexp2(0, np.nan))) assert_(np.isnan(np.logaddexp2(np.nan, np.nan))) + def test_reduce(self): + assert_equal(np.logaddexp2.identity, -np.inf) + assert_equal(np.logaddexp2.reduce([]), -np.inf) + assert_equal(np.logaddexp2.reduce([-np.inf]), -np.inf) + assert_equal(np.logaddexp2.reduce([-np.inf, 0]), 0) + -class TestLog(object): +class TestLog: def test_log_values(self): x = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024] y = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] @@ -633,8 +958,32 @@ def test_log_values(self): yf = np.array(y, dtype=dt)*log2_ assert_almost_equal(np.log(xf), yf) + # test aliasing(issue #17761) + x = np.array([2, 0.937500, 3, 0.947500, 1.054697]) + xf = np.log(x) + assert_almost_equal(np.log(x, out=x), xf) -class TestExp(object): + # test log() of max for dtype does not raise + for dt in ['f', 'd', 'g']: + with np.errstate(all='raise'): + x = np.finfo(dt).max + np.log(x) + + def test_log_strides(self): + np.random.seed(42) + strides = np.array([-4,-3,-2,-1,1,2,3,4]) + sizes = np.arange(2,100) + for ii in sizes: + x_f64 = np.float64(np.random.uniform(low=0.01, high=100.0,size=ii)) + x_special = x_f64.copy() + x_special[3:-1:4] = 1.0 + y_true = np.log(x_f64) + y_special = np.log(x_special) + for jj in strides: + assert_array_almost_equal_nulp(np.log(x_f64[::jj]), y_true[::jj], nulp=2) + assert_array_almost_equal_nulp(np.log(x_special[::jj]), y_special[::jj], nulp=2) + +class TestExp: def test_exp_values(self): x = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024] y = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] @@ -644,6 +993,439 @@ def test_exp_values(self): yf = np.array(y, dtype=dt)*log2_ assert_almost_equal(np.exp(yf), xf) + def test_exp_strides(self): + np.random.seed(42) + strides = np.array([-4,-3,-2,-1,1,2,3,4]) + sizes = np.arange(2,100) + for ii in sizes: + x_f64 = np.float64(np.random.uniform(low=0.01, high=709.1,size=ii)) + y_true = np.exp(x_f64) + for jj in strides: + assert_array_almost_equal_nulp(np.exp(x_f64[::jj]), y_true[::jj], nulp=2) + +class TestSpecialFloats: + def test_exp_values(self): + x = [np.nan, np.nan, np.inf, 0.] + y = [np.nan, -np.nan, np.inf, -np.inf] + for dt in ['f', 'd', 'g']: + xf = np.array(x, dtype=dt) + yf = np.array(y, dtype=dt) + assert_equal(np.exp(yf), xf) + + # See: https://github.com/numpy/numpy/issues/19192 + @pytest.mark.xfail( + glibc_older_than("2.17"), + reason="Older glibc versions may not raise appropriate FP exceptions" + ) + def test_exp_exceptions(self): + with np.errstate(over='raise'): + assert_raises(FloatingPointError, np.exp, np.float32(100.)) + assert_raises(FloatingPointError, np.exp, np.float32(1E19)) + assert_raises(FloatingPointError, np.exp, np.float64(800.)) + assert_raises(FloatingPointError, np.exp, np.float64(1E19)) + + with np.errstate(under='raise'): + assert_raises(FloatingPointError, np.exp, np.float32(-1000.)) + assert_raises(FloatingPointError, np.exp, np.float32(-1E19)) + assert_raises(FloatingPointError, np.exp, np.float64(-1000.)) + assert_raises(FloatingPointError, np.exp, np.float64(-1E19)) + + def test_log_values(self): + with np.errstate(all='ignore'): + x = [np.nan, np.nan, np.inf, np.nan, -np.inf, np.nan] + y = [np.nan, -np.nan, np.inf, -np.inf, 0.0, -1.0] + y1p = [np.nan, -np.nan, np.inf, -np.inf, -1.0, -2.0] + for dt in ['f', 'd', 'g']: + xf = np.array(x, dtype=dt) + yf = np.array(y, dtype=dt) + yf1p = np.array(y1p, dtype=dt) + assert_equal(np.log(yf), xf) + assert_equal(np.log2(yf), xf) + assert_equal(np.log10(yf), xf) + assert_equal(np.log1p(yf1p), xf) + + with np.errstate(divide='raise'): + for dt in ['f', 'd']: + assert_raises(FloatingPointError, np.log, + np.array(0.0, dtype=dt)) + assert_raises(FloatingPointError, np.log2, + np.array(0.0, dtype=dt)) + assert_raises(FloatingPointError, np.log10, + np.array(0.0, dtype=dt)) + assert_raises(FloatingPointError, np.log1p, + np.array(-1.0, dtype=dt)) + + with np.errstate(invalid='raise'): + for dt in ['f', 'd']: + assert_raises(FloatingPointError, np.log, + np.array(-np.inf, dtype=dt)) + assert_raises(FloatingPointError, np.log, + np.array(-1.0, dtype=dt)) + assert_raises(FloatingPointError, np.log2, + np.array(-np.inf, dtype=dt)) + assert_raises(FloatingPointError, np.log2, + np.array(-1.0, dtype=dt)) + assert_raises(FloatingPointError, np.log10, + np.array(-np.inf, dtype=dt)) + assert_raises(FloatingPointError, np.log10, + np.array(-1.0, dtype=dt)) + assert_raises(FloatingPointError, np.log1p, + np.array(-np.inf, dtype=dt)) + assert_raises(FloatingPointError, np.log1p, + np.array(-2.0, dtype=dt)) + + # See https://github.com/numpy/numpy/issues/18005 + with assert_no_warnings(): + a = np.array(1e9, dtype='float32') + np.log(a) + + def test_sincos_values(self): + with np.errstate(all='ignore'): + x = [np.nan, np.nan, np.nan, np.nan] + y = [np.nan, -np.nan, np.inf, -np.inf] + for dt in ['f', 'd', 'g']: + xf = np.array(x, dtype=dt) + yf = np.array(y, dtype=dt) + assert_equal(np.sin(yf), xf) + assert_equal(np.cos(yf), xf) + + with np.errstate(invalid='raise'): + assert_raises(FloatingPointError, np.sin, np.float32(-np.inf)) + assert_raises(FloatingPointError, np.sin, np.float32(np.inf)) + assert_raises(FloatingPointError, np.cos, np.float32(-np.inf)) + assert_raises(FloatingPointError, np.cos, np.float32(np.inf)) + + @pytest.mark.parametrize('dt', ['f', 'd', 'g']) + def test_sqrt_values(self, dt): + with np.errstate(all='ignore'): + x = [np.nan, np.nan, np.inf, np.nan, 0.] + y = [np.nan, -np.nan, np.inf, -np.inf, 0.] + xf = np.array(x, dtype=dt) + yf = np.array(y, dtype=dt) + assert_equal(np.sqrt(yf), xf) + + # with np.errstate(invalid='raise'): + # assert_raises( + # FloatingPointError, np.sqrt, np.array(-100., dtype=dt) + # ) + + def test_abs_values(self): + x = [np.nan, np.nan, np.inf, np.inf, 0., 0., 1.0, 1.0] + y = [np.nan, -np.nan, np.inf, -np.inf, 0., -0., -1.0, 1.0] + for dt in ['f', 'd', 'g']: + xf = np.array(x, dtype=dt) + yf = np.array(y, dtype=dt) + assert_equal(np.abs(yf), xf) + + def test_square_values(self): + x = [np.nan, np.nan, np.inf, np.inf] + y = [np.nan, -np.nan, np.inf, -np.inf] + with np.errstate(all='ignore'): + for dt in ['f', 'd', 'g']: + xf = np.array(x, dtype=dt) + yf = np.array(y, dtype=dt) + assert_equal(np.square(yf), xf) + + with np.errstate(over='raise'): + assert_raises(FloatingPointError, np.square, + np.array(1E32, dtype='f')) + assert_raises(FloatingPointError, np.square, + np.array(1E200, dtype='d')) + + def test_reciprocal_values(self): + with np.errstate(all='ignore'): + x = [np.nan, np.nan, 0.0, -0.0, np.inf, -np.inf] + y = [np.nan, -np.nan, np.inf, -np.inf, 0., -0.] + for dt in ['f', 'd', 'g']: + xf = np.array(x, dtype=dt) + yf = np.array(y, dtype=dt) + assert_equal(np.reciprocal(yf), xf) + + with np.errstate(divide='raise'): + for dt in ['f', 'd', 'g']: + assert_raises(FloatingPointError, np.reciprocal, + np.array(-0.0, dtype=dt)) + + def test_tan(self): + with np.errstate(all='ignore'): + in_ = [np.nan, -np.nan, 0.0, -0.0, np.inf, -np.inf] + out = [np.nan, np.nan, 0.0, -0.0, np.nan, np.nan] + for dt in ['f', 'd']: + in_arr = np.array(in_, dtype=dt) + out_arr = np.array(out, dtype=dt) + assert_equal(np.tan(in_arr), out_arr) + + with np.errstate(invalid='raise'): + for dt in ['f', 'd']: + assert_raises(FloatingPointError, np.tan, + np.array(np.inf, dtype=dt)) + assert_raises(FloatingPointError, np.tan, + np.array(-np.inf, dtype=dt)) + + def test_arcsincos(self): + with np.errstate(all='ignore'): + in_ = [np.nan, -np.nan, np.inf, -np.inf] + out = [np.nan, np.nan, np.nan, np.nan] + for dt in ['f', 'd']: + in_arr = np.array(in_, dtype=dt) + out_arr = np.array(out, dtype=dt) + assert_equal(np.arcsin(in_arr), out_arr) + assert_equal(np.arccos(in_arr), out_arr) + + for callable in [np.arcsin, np.arccos]: + for value in [np.inf, -np.inf, 2.0, -2.0]: + for dt in ['f', 'd']: + with np.errstate(invalid='raise'): + assert_raises(FloatingPointError, callable, + np.array(value, dtype=dt)) + + def test_arctan(self): + with np.errstate(all='ignore'): + in_ = [np.nan, -np.nan] + out = [np.nan, np.nan] + for dt in ['f', 'd']: + in_arr = np.array(in_, dtype=dt) + out_arr = np.array(out, dtype=dt) + assert_equal(np.arctan(in_arr), out_arr) + + def test_sinh(self): + in_ = [np.nan, -np.nan, np.inf, -np.inf] + out = [np.nan, np.nan, np.inf, -np.inf] + for dt in ['f', 'd']: + in_arr = np.array(in_, dtype=dt) + out_arr = np.array(out, dtype=dt) + assert_equal(np.sinh(in_arr), out_arr) + + with np.errstate(over='raise'): + assert_raises(FloatingPointError, np.sinh, + np.array(120.0, dtype='f')) + assert_raises(FloatingPointError, np.sinh, + np.array(1200.0, dtype='d')) + + def test_cosh(self): + in_ = [np.nan, -np.nan, np.inf, -np.inf] + out = [np.nan, np.nan, np.inf, np.inf] + for dt in ['f', 'd']: + in_arr = np.array(in_, dtype=dt) + out_arr = np.array(out, dtype=dt) + assert_equal(np.cosh(in_arr), out_arr) + + with np.errstate(over='raise'): + assert_raises(FloatingPointError, np.cosh, + np.array(120.0, dtype='f')) + assert_raises(FloatingPointError, np.cosh, + np.array(1200.0, dtype='d')) + + def test_tanh(self): + in_ = [np.nan, -np.nan, np.inf, -np.inf] + out = [np.nan, np.nan, 1.0, -1.0] + for dt in ['f', 'd']: + in_arr = np.array(in_, dtype=dt) + out_arr = np.array(out, dtype=dt) + assert_equal(np.tanh(in_arr), out_arr) + + def test_arcsinh(self): + in_ = [np.nan, -np.nan, np.inf, -np.inf] + out = [np.nan, np.nan, np.inf, -np.inf] + for dt in ['f', 'd']: + in_arr = np.array(in_, dtype=dt) + out_arr = np.array(out, dtype=dt) + assert_equal(np.arcsinh(in_arr), out_arr) + + def test_arccosh(self): + with np.errstate(all='ignore'): + in_ = [np.nan, -np.nan, np.inf, -np.inf, 1.0, 0.0] + out = [np.nan, np.nan, np.inf, np.nan, 0.0, np.nan] + for dt in ['f', 'd']: + in_arr = np.array(in_, dtype=dt) + out_arr = np.array(out, dtype=dt) + assert_equal(np.arccosh(in_arr), out_arr) + + for value in [0.0, -np.inf]: + with np.errstate(invalid='raise'): + for dt in ['f', 'd']: + assert_raises(FloatingPointError, np.arccosh, + np.array(value, dtype=dt)) + + def test_arctanh(self): + with np.errstate(all='ignore'): + in_ = [np.nan, -np.nan, np.inf, -np.inf, 1.0, -1.0, 2.0] + out = [np.nan, np.nan, np.nan, np.nan, np.inf, -np.inf, np.nan] + for dt in ['f', 'd']: + in_arr = np.array(in_, dtype=dt) + out_arr = np.array(out, dtype=dt) + assert_equal(np.arctanh(in_arr), out_arr) + + for value in [1.01, np.inf, -np.inf, 1.0, -1.0]: + with np.errstate(invalid='raise', divide='raise'): + for dt in ['f', 'd']: + assert_raises(FloatingPointError, np.arctanh, + np.array(value, dtype=dt)) + + def test_exp2(self): + with np.errstate(all='ignore'): + in_ = [np.nan, -np.nan, np.inf, -np.inf] + out = [np.nan, np.nan, np.inf, 0.0] + for dt in ['f', 'd']: + in_arr = np.array(in_, dtype=dt) + out_arr = np.array(out, dtype=dt) + assert_equal(np.exp2(in_arr), out_arr) + + for value in [2000.0, -2000.0]: + with np.errstate(over='raise', under='raise'): + for dt in ['f', 'd']: + assert_raises(FloatingPointError, np.exp2, + np.array(value, dtype=dt)) + + def test_expm1(self): + with np.errstate(all='ignore'): + in_ = [np.nan, -np.nan, np.inf, -np.inf] + out = [np.nan, np.nan, np.inf, -1.0] + for dt in ['f', 'd']: + in_arr = np.array(in_, dtype=dt) + out_arr = np.array(out, dtype=dt) + assert_equal(np.expm1(in_arr), out_arr) + + for value in [200.0, 2000.0]: + with np.errstate(over='raise'): + assert_raises(FloatingPointError, np.expm1, + np.array(value, dtype='f')) + +class TestFPClass: + @pytest.mark.parametrize("stride", [-4,-2,-1,1,2,4]) + def test_fpclass(self, stride): + arr_f64 = np.array([np.nan, -np.nan, np.inf, -np.inf, -1.0, 1.0, -0.0, 0.0, 2.2251e-308, -2.2251e-308], dtype='d') + arr_f32 = np.array([np.nan, -np.nan, np.inf, -np.inf, -1.0, 1.0, -0.0, 0.0, 1.4013e-045, -1.4013e-045], dtype='f') + nan = np.array([True, True, False, False, False, False, False, False, False, False]) + inf = np.array([False, False, True, True, False, False, False, False, False, False]) + sign = np.array([False, True, False, True, True, False, True, False, False, True]) + finite = np.array([False, False, False, False, True, True, True, True, True, True]) + assert_equal(np.isnan(arr_f32[::stride]), nan[::stride]) + assert_equal(np.isnan(arr_f64[::stride]), nan[::stride]) + assert_equal(np.isinf(arr_f32[::stride]), inf[::stride]) + assert_equal(np.isinf(arr_f64[::stride]), inf[::stride]) + assert_equal(np.signbit(arr_f32[::stride]), sign[::stride]) + assert_equal(np.signbit(arr_f64[::stride]), sign[::stride]) + assert_equal(np.isfinite(arr_f32[::stride]), finite[::stride]) + assert_equal(np.isfinite(arr_f64[::stride]), finite[::stride]) + +class TestLDExp: + @pytest.mark.parametrize("stride", [-4,-2,-1,1,2,4]) + @pytest.mark.parametrize("dtype", ['f', 'd']) + def test_ldexp(self, dtype, stride): + mant = np.array([0.125, 0.25, 0.5, 1., 1., 2., 4., 8.], dtype=dtype) + exp = np.array([3, 2, 1, 0, 0, -1, -2, -3], dtype='i') + out = np.zeros(8, dtype=dtype) + assert_equal(np.ldexp(mant[::stride], exp[::stride], out=out[::stride]), np.ones(8, dtype=dtype)[::stride]) + assert_equal(out[::stride], np.ones(8, dtype=dtype)[::stride]) + +class TestFRExp: + @pytest.mark.parametrize("stride", [-4,-2,-1,1,2,4]) + @pytest.mark.parametrize("dtype", ['f', 'd']) + @pytest.mark.skipif(not sys.platform.startswith('linux'), + reason="np.frexp gives different answers for NAN/INF on windows and linux") + def test_frexp(self, dtype, stride): + arr = np.array([np.nan, np.nan, np.inf, -np.inf, 0.0, -0.0, 1.0, -1.0], dtype=dtype) + mant_true = np.array([np.nan, np.nan, np.inf, -np.inf, 0.0, -0.0, 0.5, -0.5], dtype=dtype) + exp_true = np.array([0, 0, 0, 0, 0, 0, 1, 1], dtype='i') + out_mant = np.ones(8, dtype=dtype) + out_exp = 2*np.ones(8, dtype='i') + mant, exp = np.frexp(arr[::stride], out=(out_mant[::stride], out_exp[::stride])) + assert_equal(mant_true[::stride], mant) + assert_equal(exp_true[::stride], exp) + assert_equal(out_mant[::stride], mant_true[::stride]) + assert_equal(out_exp[::stride], exp_true[::stride]) + +# func : [maxulperror, low, high] +avx_ufuncs = {'sqrt' :[1, 0., 100.], + 'absolute' :[0, -100., 100.], + 'reciprocal' :[1, 1., 100.], + 'square' :[1, -100., 100.], + 'rint' :[0, -100., 100.], + 'floor' :[0, -100., 100.], + 'ceil' :[0, -100., 100.], + 'trunc' :[0, -100., 100.]} + +class TestAVXUfuncs: + def test_avx_based_ufunc(self): + strides = np.array([-4,-3,-2,-1,1,2,3,4]) + np.random.seed(42) + for func, prop in avx_ufuncs.items(): + maxulperr = prop[0] + minval = prop[1] + maxval = prop[2] + # various array sizes to ensure masking in AVX is tested + for size in range(1,32): + myfunc = getattr(np, func) + x_f32 = np.float32(np.random.uniform(low=minval, high=maxval, + size=size)) + x_f64 = np.float64(x_f32) + x_f128 = np.longdouble(x_f32) + y_true128 = myfunc(x_f128) + if maxulperr == 0: + assert_equal(myfunc(x_f32), np.float32(y_true128)) + assert_equal(myfunc(x_f64), np.float64(y_true128)) + else: + assert_array_max_ulp(myfunc(x_f32), np.float32(y_true128), + maxulp=maxulperr) + assert_array_max_ulp(myfunc(x_f64), np.float64(y_true128), + maxulp=maxulperr) + # various strides to test gather instruction + if size > 1: + y_true32 = myfunc(x_f32) + y_true64 = myfunc(x_f64) + for jj in strides: + assert_equal(myfunc(x_f64[::jj]), y_true64[::jj]) + assert_equal(myfunc(x_f32[::jj]), y_true32[::jj]) + +class TestAVXFloat32Transcendental: + def test_exp_float32(self): + np.random.seed(42) + x_f32 = np.float32(np.random.uniform(low=0.0,high=88.1,size=1000000)) + x_f64 = np.float64(x_f32) + assert_array_max_ulp(np.exp(x_f32), np.float32(np.exp(x_f64)), maxulp=3) + + def test_log_float32(self): + np.random.seed(42) + x_f32 = np.float32(np.random.uniform(low=0.0,high=1000,size=1000000)) + x_f64 = np.float64(x_f32) + assert_array_max_ulp(np.log(x_f32), np.float32(np.log(x_f64)), maxulp=4) + + def test_sincos_float32(self): + np.random.seed(42) + N = 1000000 + M = np.int_(N/20) + index = np.random.randint(low=0, high=N, size=M) + x_f32 = np.float32(np.random.uniform(low=-100.,high=100.,size=N)) + if not glibc_older_than("2.17"): + # test coverage for elements > 117435.992f for which glibc is used + # this is known to be problematic on old glibc, so skip it there + x_f32[index] = np.float32(10E+10*np.random.rand(M)) + x_f64 = np.float64(x_f32) + assert_array_max_ulp(np.sin(x_f32), np.float32(np.sin(x_f64)), maxulp=2) + assert_array_max_ulp(np.cos(x_f32), np.float32(np.cos(x_f64)), maxulp=2) + # test aliasing(issue #17761) + tx_f32 = x_f32.copy() + assert_array_max_ulp(np.sin(x_f32, out=x_f32), np.float32(np.sin(x_f64)), maxulp=2) + assert_array_max_ulp(np.cos(tx_f32, out=tx_f32), np.float32(np.cos(x_f64)), maxulp=2) + + def test_strided_float32(self): + np.random.seed(42) + strides = np.array([-4,-3,-2,-1,1,2,3,4]) + sizes = np.arange(2,100) + for ii in sizes: + x_f32 = np.float32(np.random.uniform(low=0.01,high=88.1,size=ii)) + x_f32_large = x_f32.copy() + x_f32_large[3:-1:4] = 120000.0 + exp_true = np.exp(x_f32) + log_true = np.log(x_f32) + sin_true = np.sin(x_f32_large) + cos_true = np.cos(x_f32_large) + for jj in strides: + assert_array_almost_equal_nulp(np.exp(x_f32[::jj]), exp_true[::jj], nulp=2) + assert_array_almost_equal_nulp(np.log(x_f32[::jj]), log_true[::jj], nulp=2) + assert_array_almost_equal_nulp(np.sin(x_f32_large[::jj]), sin_true[::jj], nulp=2) + assert_array_almost_equal_nulp(np.cos(x_f32_large[::jj]), cos_true[::jj], nulp=2) class TestLogAddExp(_FilterInvalids): def test_logaddexp_values(self): @@ -685,8 +1467,12 @@ def test_nan(self): assert_(np.isnan(np.logaddexp(0, np.nan))) assert_(np.isnan(np.logaddexp(np.nan, np.nan))) + def test_reduce(self): + assert_equal(np.logaddexp.identity, -np.inf) + assert_equal(np.logaddexp.reduce([]), -np.inf) -class TestLog1p(object): + +class TestLog1p: def test_log1p(self): assert_almost_equal(ncu.log1p(0.2), ncu.log(1.2)) assert_almost_equal(ncu.log1p(1e-6), ncu.log(1+1e-6)) @@ -700,7 +1486,7 @@ def test_special(self): assert_equal(ncu.log1p(-np.inf), np.nan) -class TestExpm1(object): +class TestExpm1: def test_expm1(self): assert_almost_equal(ncu.expm1(0.2), ncu.exp(0.2)-1) assert_almost_equal(ncu.expm1(1e-6), ncu.exp(1e-6)-1) @@ -712,8 +1498,14 @@ def test_special(self): assert_equal(ncu.expm1(np.inf), np.inf) assert_equal(ncu.expm1(-np.inf), -1.) + def test_complex(self): + x = np.asarray(1e-12) + assert_allclose(x, ncu.expm1(x)) + x = x.astype(np.complex128) + assert_allclose(x, ncu.expm1(x)) + -class TestHypot(object): +class TestHypot: def test_simple(self): assert_almost_equal(ncu.hypot(1, 1), ncu.sqrt(2)) assert_almost_equal(ncu.hypot(0, 0), 0) @@ -737,7 +1529,7 @@ def assert_hypot_isinf(x, y): "hypot(%s, %s) is %s, not inf" % (x, y, ncu.hypot(x, y))) -class TestHypotSpecialValues(object): +class TestHypotSpecialValues: def test_nan_outputs(self): assert_hypot_isnan(np.nan, np.nan) assert_hypot_isnan(np.nan, 1) @@ -774,7 +1566,7 @@ def assert_arctan2_isnzero(x, y): assert_((ncu.arctan2(x, y) == 0 and np.signbit(ncu.arctan2(x, y))), "arctan(%s, %s) is %s, not -0" % (x, y, ncu.arctan2(x, y))) -class TestArctan2SpecialValues(object): +class TestArctan2SpecialValues: def test_one_one(self): # atan2(1, 1) returns pi/4. assert_almost_equal(ncu.arctan2(1, 1), 0.25 * np.pi) @@ -843,7 +1635,7 @@ def test_nan_any(self): assert_arctan2_isnan(np.nan, np.nan) -class TestLdexp(object): +class TestLdexp: def _check_ldexp(self, tp): assert_almost_equal(ncu.ldexp(np.array(2., np.float32), np.array(3, tp)), 16.) @@ -928,6 +1720,19 @@ def test_object_array(self): arg2 = arg1 + 1 assert_equal(np.maximum(arg1, arg2), arg2) + def test_strided_array(self): + arr1 = np.array([-4.0, 1.0, 10.0, 0.0, np.nan, -np.nan, np.inf, -np.inf]) + arr2 = np.array([-2.0,-1.0, np.nan, 1.0, 0.0, np.nan, 1.0, -3.0]) + maxtrue = np.array([-2.0, 1.0, np.nan, 1.0, np.nan, np.nan, np.inf, -3.0]) + out = np.ones(8) + out_maxtrue = np.array([-2.0, 1.0, 1.0, 10.0, 1.0, 1.0, np.nan, 1.0]) + assert_equal(np.maximum(arr1,arr2), maxtrue) + assert_equal(np.maximum(arr1[::2],arr2[::2]), maxtrue[::2]) + assert_equal(np.maximum(arr1[:4:], arr2[::2]), np.array([-2.0, np.nan, 10.0, 1.0])) + assert_equal(np.maximum(arr1[::3], arr2[:3:]), np.array([-2.0, 0.0, np.nan])) + assert_equal(np.maximum(arr1[:6:2], arr2[::3], out=out[::3]), np.array([-2.0, 10., np.nan])) + assert_equal(out, out_maxtrue) + class TestMinimum(_FilterInvalids): def test_reduce(self): @@ -986,6 +1791,18 @@ def test_object_array(self): arg2 = arg1 + 1 assert_equal(np.minimum(arg1, arg2), arg1) + def test_strided_array(self): + arr1 = np.array([-4.0, 1.0, 10.0, 0.0, np.nan, -np.nan, np.inf, -np.inf]) + arr2 = np.array([-2.0,-1.0, np.nan, 1.0, 0.0, np.nan, 1.0, -3.0]) + mintrue = np.array([-4.0, -1.0, np.nan, 0.0, np.nan, np.nan, 1.0, -np.inf]) + out = np.ones(8) + out_mintrue = np.array([-4.0, 1.0, 1.0, 1.0, 1.0, 1.0, np.nan, 1.0]) + assert_equal(np.minimum(arr1,arr2), mintrue) + assert_equal(np.minimum(arr1[::2],arr2[::2]), mintrue[::2]) + assert_equal(np.minimum(arr1[:4:], arr2[::2]), np.array([-4.0, np.nan, 0.0, 0.0])) + assert_equal(np.minimum(arr1[::3], arr2[:3:]), np.array([-4.0, -1.0, np.nan])) + assert_equal(np.minimum(arr1[:6:2], arr2[::3], out=out[::3]), np.array([-4.0, 1.0, np.nan])) + assert_equal(out, out_mintrue) class TestFmax(_FilterInvalids): def test_reduce(self): @@ -1071,7 +1888,7 @@ def test_complex_nans(self): assert_equal(np.fmin(arg1, arg2), out) -class TestBool(object): +class TestBool: def test_exceptions(self): a = np.ones(1, dtype=np.bool_) assert_raises(TypeError, np.negative, a) @@ -1134,7 +1951,7 @@ def test_reduce(self): assert_equal(np.logical_xor.reduce(arr), arr.sum() % 2 == 1) -class TestBitwiseUFuncs(object): +class TestBitwiseUFuncs: bitwise_types = [np.dtype(c) for c in '?' + 'bBhHiIlLqQ' + 'O'] @@ -1173,7 +1990,6 @@ def test_types(self): assert_(np.bitwise_xor(zeros, zeros).dtype == dt, msg) assert_(np.bitwise_and(zeros, zeros).dtype == dt, msg) - def test_identity(self): assert_(np.bitwise_or.identity == 0, 'bitwise_or') assert_(np.bitwise_xor.identity == 0, 'bitwise_xor') @@ -1219,7 +2035,7 @@ def test_reduction(self): assert_(type(f.reduce(btype)) is bool, msg) -class TestInt(object): +class TestInt: def test_logical_not(self): x = np.ones(10, dtype=np.int16) o = np.ones(10 * 2, dtype=bool) @@ -1230,24 +2046,24 @@ def test_logical_not(self): assert_array_equal(o, tgt) -class TestFloatingPoint(object): +class TestFloatingPoint: def test_floating_point(self): assert_equal(ncu.FLOATING_POINT_SUPPORT, 1) -class TestDegrees(object): +class TestDegrees: def test_degrees(self): assert_almost_equal(ncu.degrees(np.pi), 180.0) assert_almost_equal(ncu.degrees(-0.5*np.pi), -90.0) -class TestRadians(object): +class TestRadians: def test_radians(self): assert_almost_equal(ncu.radians(180.0), np.pi) assert_almost_equal(ncu.radians(-90.0), -0.5*np.pi) -class TestHeavside(object): +class TestHeavside: def test_heaviside(self): x = np.array([[-30.0, -0.1, 0.0, 0.2], [7.5, np.nan, np.inf, -np.inf]]) expectedhalf = np.array([[0.0, 0.0, 0.5, 1.0], [1.0, np.nan, 1.0, 0.0]]) @@ -1269,7 +2085,7 @@ def test_heaviside(self): assert_equal(h, expected1.astype(np.float32)) -class TestSign(object): +class TestSign: def test_sign(self): a = np.array([np.inf, -np.inf, np.nan, 0.0, 3.0, -3.0]) out = np.zeros(a.shape) @@ -1295,11 +2111,12 @@ def test_sign_dtype_nan_object(self): # In reference to github issue #6229 def test_nan(): foo = np.array([np.nan]) + # FIXME: a not used a = np.sign(foo.astype(object)) assert_raises(TypeError, test_nan) -class TestMinMax(object): +class TestMinMax: def test_minmax_blocked(self): # simd tests on max/min, test all alignments, slow but important # for 2 * vz + 2 * (vs - 1) + 1 (unrolled once) @@ -1328,19 +2145,21 @@ def test_lower_align(self): assert_equal(d.max(), d[0]) assert_equal(d.min(), d[0]) - def test_reduce_warns(self): + def test_reduce_reorder(self): # gh 10370, 11029 Some compilers reorder the call to npy_getfloatstatus # and put it before the call to an intrisic function that causes - # invalid status to be set. Also make sure warnings are emitted + # invalid status to be set. Also make sure warnings are not emitted for n in (2, 4, 8, 16, 32): - with suppress_warnings() as sup: - sup.record(RuntimeWarning) - for r in np.diagflat([np.nan] * n): + for dt in (np.float32, np.float16, np.complex64): + for r in np.diagflat(np.array([np.nan] * n, dtype=dt)): assert_equal(np.min(r), np.nan) - assert_equal(len(sup.log), n) + def test_minimize_no_warns(self): + a = np.minimum(np.nan, 1) + assert_equal(a, np.nan) -class TestAbsoluteNegative(object): + +class TestAbsoluteNegative: def test_abs_neg_blocked(self): # simd tests on abs, test all alignments for vz + 2 * (vs - 1) + 1 for dt, sz in [(np.float32, 11), (np.float64, 5)]: @@ -1382,7 +2201,7 @@ def test_lower_align(self): np.abs(np.ones_like(d), out=d) -class TestPositive(object): +class TestPositive: def test_valid(self): valid_dtypes = [int, float, complex, object] for dtype in valid_dtypes: @@ -1401,10 +2220,10 @@ def test_invalid(self): np.positive(np.array(['bar'], dtype=object)) -class TestSpecialMethods(object): +class TestSpecialMethods: def test_wrap(self): - class with_wrap(object): + class with_wrap: def __array__(self): return np.zeros(1) @@ -1432,7 +2251,7 @@ class StoreArrayPrepareWrap(np.ndarray): _wrap_args = None _prepare_args = None def __new__(cls): - return np.empty(()).view(cls) + return np.zeros(()).view(cls) def __array_wrap__(self, obj, context): self._wrap_args = context[1] return obj @@ -1442,7 +2261,7 @@ def __array_prepare__(self, obj, context): @property def args(self): # We need to ensure these are fetched at the same time, before - # any other ufuncs are calld by the assertions + # any other ufuncs are called by the assertions return (self._prepare_args, self._wrap_args) def __repr__(self): return "a" # for short test output @@ -1475,6 +2294,10 @@ def do_test(f_call, f_expected): do_test(lambda a: np.add(0, 0, out=a), lambda a: (0, 0, a)) do_test(lambda a: np.add(0, 0, out=(a,)), lambda a: (0, 0, a)) + # Also check the where mask handling: + do_test(lambda a: np.add(a, 0, where=False), lambda a: (a, 0)) + do_test(lambda a: np.add(0, 0, a, where=False), lambda a: (0, 0, a)) + def test_wrap_with_iterable(self): # test fix for bug #1026: @@ -1508,7 +2331,7 @@ def __new__(cls): def test_old_wrap(self): - class with_wrap(object): + class with_wrap: def __array__(self): return np.zeros(1) @@ -1523,7 +2346,7 @@ def __array_wrap__(self, arr): def test_priority(self): - class A(object): + class A: def __array__(self): return np.zeros(1) @@ -1566,15 +2389,16 @@ class C(A): def test_failing_wrap(self): - class A(object): + class A: def __array__(self): - return np.zeros(1) + return np.zeros(2) def __array_wrap__(self, arr, context): raise RuntimeError a = A() assert_raises(RuntimeError, ncu.maximum, a, a) + assert_raises(RuntimeError, ncu.maximum.reduce, a) def test_failing_out_wrap(self): @@ -1590,7 +2414,6 @@ def __array_wrap__(self, obj): ok = np.empty(1).view(Ok) bad = np.empty(1).view(Bad) - # double-free (segfault) of "ok" if "bad" raises an exception for i in range(10): assert_raises(RuntimeError, ncu.frexp, 1, ok, bad) @@ -1598,7 +2421,7 @@ def __array_wrap__(self, obj): def test_none_wrap(self): # Tests that issue #8507 is resolved. Previously, this would segfault - class A(object): + class A: def __array__(self): return np.zeros(1) @@ -1610,7 +2433,7 @@ def __array_wrap__(self, arr, context=None): def test_default_prepare(self): - class with_wrap(object): + class with_wrap: __array_priority__ = 10 def __array__(self): @@ -1624,7 +2447,8 @@ def __array_wrap__(self, arr, context): assert_equal(x, np.zeros(1)) assert_equal(type(x), np.ndarray) - def test_prepare(self): + @pytest.mark.parametrize("use_where", [True, False]) + def test_prepare(self, use_where): class with_prepare(np.ndarray): __array_priority__ = 10 @@ -1634,11 +2458,15 @@ def __array_prepare__(self, arr, context): return np.array(arr).view(type=with_prepare) a = np.array(1).view(type=with_prepare) - x = np.add(a, a) + if use_where: + x = np.add(a, a, where=np.array(True)) + else: + x = np.add(a, a) assert_equal(x, np.array(2)) assert_equal(type(x), with_prepare) - def test_prepare_out(self): + @pytest.mark.parametrize("use_where", [True, False]) + def test_prepare_out(self, use_where): class with_prepare(np.ndarray): __array_priority__ = 10 @@ -1647,7 +2475,10 @@ def __array_prepare__(self, arr, context): return np.array(arr).view(type=with_prepare) a = np.array([1]).view(type=with_prepare) - x = np.add(a, a, a) + if use_where: + x = np.add(a, a, a, where=[True]) + else: + x = np.add(a, a, a) # Returned array is new, because of the strange # __array_prepare__ above assert_(not np.shares_memory(x, a)) @@ -1656,7 +2487,7 @@ def __array_prepare__(self, arr, context): def test_failing_prepare(self): - class A(object): + class A: def __array__(self): return np.zeros(1) @@ -1665,37 +2496,20 @@ def __array_prepare__(self, arr, context=None): a = A() assert_raises(RuntimeError, ncu.maximum, a, a) + assert_raises(RuntimeError, ncu.maximum, a, a, where=False) - def test_array_with_context(self): + def test_array_too_many_args(self): - class A(object): - def __array__(self, dtype=None, context=None): - func, args, i = context - self.func = func - self.args = args - self.i = i - return np.zeros(1) - - class B(object): - def __array__(self, dtype=None): - return np.zeros(1, dtype) - - class C(object): - def __array__(self): + class A: + def __array__(self, dtype, context): return np.zeros(1) a = A() - ncu.maximum(np.zeros(1), a) - assert_(a.func is ncu.maximum) - assert_equal(a.args[0], 0) - assert_(a.args[1] is a) - assert_(a.i == 1) - assert_equal(ncu.maximum(a, B()), 0) - assert_equal(ncu.maximum(a, C()), 0) + assert_raises_regex(TypeError, '2 required positional', np.sum, a) def test_ufunc_override(self): # check override works even with instance with high priority. - class A(object): + class A: def __array_ufunc__(self, func, method, *inputs, **kwargs): return self, func, method, inputs, kwargs @@ -1732,7 +2546,7 @@ def quatro_mul(a, b, c, d): three_mul_ufunc = np.frompyfunc(tres_mul, 3, 1) four_mul_ufunc = np.frompyfunc(quatro_mul, 4, 1) - class A(object): + class A: def __array_ufunc__(self, func, method, *inputs, **kwargs): return "A" @@ -1740,23 +2554,27 @@ class ASub(A): def __array_ufunc__(self, func, method, *inputs, **kwargs): return "ASub" - class B(object): + class B: def __array_ufunc__(self, func, method, *inputs, **kwargs): return "B" - class C(object): + class C: + def __init__(self): + self.count = 0 + def __array_ufunc__(self, func, method, *inputs, **kwargs): + self.count += 1 return NotImplemented class CSub(C): def __array_ufunc__(self, func, method, *inputs, **kwargs): + self.count += 1 return NotImplemented a = A() a_sub = ASub() b = B() c = C() - c_sub = CSub() # Standard res = np.multiply(a, a_sub) @@ -1767,11 +2585,27 @@ def __array_ufunc__(self, func, method, *inputs, **kwargs): # With 1 NotImplemented res = np.multiply(c, a) assert_equal(res, "A") + assert_equal(c.count, 1) + # Check our counter works, so we can trust tests below. + res = np.multiply(c, a) + assert_equal(c.count, 2) # Both NotImplemented. + c = C() + c_sub = CSub() assert_raises(TypeError, np.multiply, c, c_sub) + assert_equal(c.count, 1) + assert_equal(c_sub.count, 1) + c.count = c_sub.count = 0 assert_raises(TypeError, np.multiply, c_sub, c) + assert_equal(c.count, 1) + assert_equal(c_sub.count, 1) + c.count = 0 + assert_raises(TypeError, np.multiply, c, c) + assert_equal(c.count, 1) + c.count = 0 assert_raises(TypeError, np.multiply, 2, c) + assert_equal(c.count, 1) # Ternary testing. assert_equal(three_mul_ufunc(a, 1, 2), "A") @@ -1783,11 +2617,19 @@ def __array_ufunc__(self, func, method, *inputs, **kwargs): assert_equal(three_mul_ufunc(a, 2, b), "A") assert_equal(three_mul_ufunc(a, 2, a_sub), "ASub") assert_equal(three_mul_ufunc(a, a_sub, 3), "ASub") + c.count = 0 assert_equal(three_mul_ufunc(c, a_sub, 3), "ASub") + assert_equal(c.count, 1) + c.count = 0 assert_equal(three_mul_ufunc(1, a_sub, c), "ASub") + assert_equal(c.count, 0) + c.count = 0 assert_equal(three_mul_ufunc(a, b, c), "A") + assert_equal(c.count, 0) + c_sub.count = 0 assert_equal(three_mul_ufunc(a, b, c_sub), "A") + assert_equal(c_sub.count, 0) assert_equal(three_mul_ufunc(1, 2, b), "B") assert_raises(TypeError, three_mul_ufunc, 1, 2, c) @@ -1806,24 +2648,42 @@ def __array_ufunc__(self, func, method, *inputs, **kwargs): assert_equal(four_mul_ufunc(a_sub, 1, 2, a), "ASub") assert_equal(four_mul_ufunc(a, 1, 2, a_sub), "ASub") + c = C() + c_sub = CSub() assert_raises(TypeError, four_mul_ufunc, 1, 2, 3, c) + assert_equal(c.count, 1) + c.count = 0 assert_raises(TypeError, four_mul_ufunc, 1, 2, c_sub, c) - assert_raises(TypeError, four_mul_ufunc, 1, c, c_sub, c) + assert_equal(c_sub.count, 1) + assert_equal(c.count, 1) + c2 = C() + c.count = c_sub.count = 0 + assert_raises(TypeError, four_mul_ufunc, 1, c, c_sub, c2) + assert_equal(c_sub.count, 1) + assert_equal(c.count, 1) + assert_equal(c2.count, 0) + c.count = c2.count = c_sub.count = 0 + assert_raises(TypeError, four_mul_ufunc, c2, c, c_sub, c) + assert_equal(c_sub.count, 1) + assert_equal(c.count, 0) + assert_equal(c2.count, 1) def test_ufunc_override_methods(self): - class A(object): + class A: def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): return self, ufunc, method, inputs, kwargs # __call__ a = A() - res = np.multiply.__call__(1, a, foo='bar', answer=42) + with assert_raises(TypeError): + np.multiply.__call__(1, a, foo='bar', answer=42) + res = np.multiply.__call__(1, a, subok='bar', where=42) assert_equal(res[0], a) assert_equal(res[1], np.multiply) assert_equal(res[2], '__call__') assert_equal(res[3], (1, a)) - assert_equal(res[4], {'foo': 'bar', 'answer': 42}) + assert_equal(res[4], {'subok': 'bar', 'where': 42}) # __call__, wrong args assert_raises(TypeError, np.multiply, a) @@ -1844,7 +2704,8 @@ def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): # reduce, kwargs res = np.multiply.reduce(a, axis='axis0', dtype='dtype0', out='out0', - keepdims='keep0', initial='init0') + keepdims='keep0', initial='init0', + where='where0') assert_equal(res[0], a) assert_equal(res[1], np.multiply) assert_equal(res[2], 'reduce') @@ -1853,7 +2714,8 @@ def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): 'out': ('out0',), 'keepdims': 'keep0', 'axis': 'axis0', - 'initial': 'init0'}) + 'initial': 'init0', + 'where': 'where0'}) # reduce, output equal to None removed, but not other explicit ones, # even if they are at their default value. @@ -1863,14 +2725,18 @@ def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): assert_equal(res[4], {'axis': 0, 'keepdims': True}) res = np.multiply.reduce(a, None, out=(None,), dtype=None) assert_equal(res[4], {'axis': None, 'dtype': None}) - res = np.multiply.reduce(a, 0, None, None, False, 2) - assert_equal(res[4], {'axis': 0, 'dtype': None, 'keepdims': False, 'initial': 2}) - # np._NoValue ignored for initial. - res = np.multiply.reduce(a, 0, None, None, False, np._NoValue) - assert_equal(res[4], {'axis': 0, 'dtype': None, 'keepdims': False}) - # None kept for initial. - res = np.multiply.reduce(a, 0, None, None, False, None) - assert_equal(res[4], {'axis': 0, 'dtype': None, 'keepdims': False, 'initial': None}) + res = np.multiply.reduce(a, 0, None, None, False, 2, True) + assert_equal(res[4], {'axis': 0, 'dtype': None, 'keepdims': False, + 'initial': 2, 'where': True}) + # np._NoValue ignored for initial + res = np.multiply.reduce(a, 0, None, None, False, + np._NoValue, True) + assert_equal(res[4], {'axis': 0, 'dtype': None, 'keepdims': False, + 'where': True}) + # None kept for initial, True for where. + res = np.multiply.reduce(a, 0, None, None, False, None, True) + assert_equal(res[4], {'axis': 0, 'dtype': None, 'keepdims': False, + 'initial': None, 'where': True}) # reduce, wrong args assert_raises(ValueError, np.multiply.reduce, a, out=()) @@ -1975,11 +2841,11 @@ def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): def test_ufunc_override_out(self): - class A(object): + class A: def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): return kwargs - class B(object): + class B: def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): return kwargs @@ -2014,17 +2880,16 @@ def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): assert_(np.modf(a, None) == {}) assert_(np.modf(a, None, None) == {}) assert_(np.modf(a, out=(None, None)) == {}) - with warnings.catch_warnings(record=True) as w: - warnings.filterwarnings('always', '', DeprecationWarning) - assert_(np.modf(a, out=None) == {}) - assert_(w[0].category is DeprecationWarning) + with assert_raises(TypeError): + # Out argument must be tuple, since there are multiple outputs. + np.modf(a, out=None) # don't give positional and output argument, or too many arguments. # wrong number of arguments in the tuple is an error too. assert_raises(TypeError, np.multiply, a, b, 'one', out='two') assert_raises(TypeError, np.multiply, a, b, 'one', 'two') assert_raises(ValueError, np.multiply, a, b, out=('one', 'two')) - assert_raises(ValueError, np.multiply, a, out=()) + assert_raises(TypeError, np.multiply, a, out=()) assert_raises(TypeError, np.modf, a, 'one', out=('two', 'three')) assert_raises(TypeError, np.modf, a, 'one', 'two', 'three') assert_raises(ValueError, np.modf, a, out=('one', 'two', 'three')) @@ -2032,7 +2897,7 @@ def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): def test_ufunc_override_exception(self): - class A(object): + class A: def __array_ufunc__(self, *a, **kwargs): raise ValueError("oops") @@ -2043,7 +2908,7 @@ def __array_ufunc__(self, *a, **kwargs): def test_ufunc_override_not_implemented(self): - class A(object): + class A: def __array_ufunc__(self, *args, **kwargs): return NotImplemented @@ -2060,7 +2925,7 @@ def __array_ufunc__(self, *args, **kwargs): def test_ufunc_override_disabled(self): - class OptOut(object): + class OptOut: __array_ufunc__ = None opt_out = OptOut() @@ -2077,7 +2942,7 @@ class OptOut(object): # opt-outs still hold even when other arguments have pathological # __array_ufunc__ implementations - class GreedyArray(object): + class GreedyArray: def __array_ufunc__(self, *args, **kwargs): return self @@ -2091,7 +2956,7 @@ def __array_ufunc__(self, *args, **kwargs): def test_gufunc_override(self): # gufunc are just ufunc instances, but follow a different path, # so check __array_ufunc__ overrides them properly. - class A(object): + class A: def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): return self, ufunc, method, inputs, kwargs @@ -2119,10 +2984,10 @@ def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): assert_raises(ValueError, inner1d, a, a, out=()) def test_ufunc_override_with_super(self): - # NOTE: this class is given as an example in doc/subclassing.py; + # NOTE: this class is used in doc/source/user/basics.subclassing.rst # if you make any changes here, do update it there too. class A(np.ndarray): - def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): + def __array_ufunc__(self, ufunc, method, *inputs, out=None, **kwargs): args = [] in_no = [] for i, input_ in enumerate(inputs): @@ -2132,7 +2997,7 @@ def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): else: args.append(input_) - outputs = kwargs.pop('out', None) + outputs = out out_no = [] if outputs: out_args = [] @@ -2152,8 +3017,8 @@ def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): if out_no: info['outputs'] = out_no - results = super(A, self).__array_ufunc__(ufunc, method, - *args, **kwargs) + results = super().__array_ufunc__(ufunc, method, + *args, **kwargs) if results is NotImplemented: return NotImplemented @@ -2173,7 +3038,7 @@ def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): return results[0] if len(results) == 1 else results - class B(object): + class B: def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): if any(isinstance(input_, A) for input_ in inputs): return "A!" @@ -2292,14 +3157,14 @@ def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): assert_(a.info, {'inputs': [0, 2]}) -class TestChoose(object): +class TestChoose: def test_mixed(self): c = np.array([True, True]) a = np.array([True, True]) assert_equal(np.choose(c, (a, 1)), np.array([1, 1])) -class TestRationalFunctions(object): +class TestRationalFunctions: def test_lcm(self): self._test_lcm_inner(np.int16) self._test_lcm_inner(np.uint16) @@ -2398,12 +3263,43 @@ def test_builtin_long(self): assert_equal(np.gcd(2**100, 3**100), 1) -def is_longdouble_finfo_bogus(): - info = np.finfo(np.longcomplex) - return not np.isfinite(np.log10(info.tiny/info.eps)) +class TestRoundingFunctions: + + def test_object_direct(self): + """ test direct implementation of these magic methods """ + class C: + def __floor__(self): + return 1 + def __ceil__(self): + return 2 + def __trunc__(self): + return 3 + + arr = np.array([C(), C()]) + assert_equal(np.floor(arr), [1, 1]) + assert_equal(np.ceil(arr), [2, 2]) + assert_equal(np.trunc(arr), [3, 3]) + + def test_object_indirect(self): + """ test implementations via __float__ """ + class C: + def __float__(self): + return -2.5 + + arr = np.array([C(), C()]) + assert_equal(np.floor(arr), [-3, -3]) + assert_equal(np.ceil(arr), [-2, -2]) + with pytest.raises(TypeError): + np.trunc(arr) # consistent with math.trunc + def test_fraction(self): + f = Fraction(-4, 3) + assert_equal(np.floor(f), -2) + assert_equal(np.ceil(f), -1) + assert_equal(np.trunc(f), -1) -class TestComplexFunctions(object): + +class TestComplexFunctions: funcs = [np.arcsin, np.arccos, np.arctan, np.arcsinh, np.arccosh, np.arctanh, np.sin, np.cos, np.tan, np.exp, np.exp2, np.log, np.sqrt, np.log10, np.log2, @@ -2498,7 +3394,8 @@ def test_against_cmath(self): b = cfunc(p) assert_(abs(a - b) < atol, "%s %s: %s; cmath: %s" % (fname, p, a, b)) - def check_loss_of_precision(self, dtype): + @pytest.mark.parametrize('dtype', [np.complex64, np.complex_, np.longcomplex]) + def test_loss_of_precision(self, dtype): """Check loss of precision in complex arc* functions""" # Check against known-good functions @@ -2537,13 +3434,17 @@ def check(x, rtol): x_basic = np.logspace(-2.999, 0, 10, endpoint=False) if dtype is np.longcomplex: + if (platform.machine() == 'aarch64' and bad_arcsinh()): + pytest.skip("Trig functions of np.longcomplex values known " + "to be inaccurate on aarch64 for some compilation " + "configurations.") # It's not guaranteed that the system-provided arc functions # are accurate down to a few epsilons. (Eg. on Linux 64-bit) # So, give more leeway for long complex tests here: - check(x_series, 50*eps) + check(x_series, 50.0*eps) else: check(x_series, 2.1*eps) - check(x_basic, 2*eps/1e-3) + check(x_basic, 2.0*eps/1e-3) # Check a few points @@ -2583,17 +3484,8 @@ def check(func, z0, d=1): check(func, pts, 1j) check(func, pts, 1+1j) - def test_loss_of_precision(self): - for dtype in [np.complex64, np.complex_]: - self.check_loss_of_precision(dtype) - - @pytest.mark.skipif(is_longdouble_finfo_bogus(), - reason="Bogus long double finfo") - def test_loss_of_precision_longcomplex(self): - self.check_loss_of_precision(np.longcomplex) - -class TestAttributes(object): +class TestAttributes: def test_attributes(self): add = ncu.add assert_equal(add.__name__, 'add') @@ -2612,7 +3504,7 @@ def test_doc(self): "frexp(x[, out1, out2], / [, out=(None, None)], *, where=True")) -class TestSubclass(object): +class TestSubclass: def test_subclass_op(self): @@ -2625,6 +3517,32 @@ def __new__(subtype, shape): a = simple((3, 4)) assert_equal(a+a, a) + +class TestFrompyfunc: + + def test_identity(self): + def mul(a, b): + return a * b + + # with identity=value + mul_ufunc = np.frompyfunc(mul, nin=2, nout=1, identity=1) + assert_equal(mul_ufunc.reduce([2, 3, 4]), 24) + assert_equal(mul_ufunc.reduce(np.ones((2, 2)), axis=(0, 1)), 1) + assert_equal(mul_ufunc.reduce([]), 1) + + # with identity=None (reorderable) + mul_ufunc = np.frompyfunc(mul, nin=2, nout=1, identity=None) + assert_equal(mul_ufunc.reduce([2, 3, 4]), 24) + assert_equal(mul_ufunc.reduce(np.ones((2, 2)), axis=(0, 1)), 1) + assert_raises(ValueError, lambda: mul_ufunc.reduce([])) + + # with no identity (not reorderable) + mul_ufunc = np.frompyfunc(mul, nin=2, nout=1) + assert_equal(mul_ufunc.reduce([2, 3, 4]), 24) + assert_raises(ValueError, lambda: mul_ufunc.reduce(np.ones((2, 2)), axis=(0, 1))) + assert_raises(ValueError, lambda: mul_ufunc.reduce([])) + + def _check_branch_cut(f, x0, dx, re_sign=1, im_sign=-1, sig_zero_ok=False, dtype=complex): """ @@ -2723,8 +3641,14 @@ def test_nextafterl(): def test_nextafter_0(): for t, direction in itertools.product(np.sctypes['float'], (1, -1)): - tiny = np.finfo(t).tiny - assert_(0. < direction * np.nextafter(t(0), t(direction)) < tiny) + # The value of tiny for double double is NaN, so we need to pass the + # assert + with suppress_warnings() as sup: + sup.filter(UserWarning) + if not np.isnan(np.finfo(t).tiny): + tiny = np.finfo(t).tiny + assert_( + 0. < direction * np.nextafter(t(0), t(direction)) < tiny) assert_equal(np.nextafter(t(0), t(direction)) / t(2.1), direction * 0.0) def _test_spacing(t): @@ -2869,8 +3793,99 @@ def test_rint_big_int(): # Rint should not change the value assert_equal(val, np.rint(val)) +@pytest.mark.parametrize('ftype', [np.float32, np.float64]) +def test_memoverlap_accumulate(ftype): + # Reproduces bug https://github.com/numpy/numpy/issues/15597 + arr = np.array([0.61, 0.60, 0.77, 0.41, 0.19], dtype=ftype) + out_max = np.array([0.61, 0.61, 0.77, 0.77, 0.77], dtype=ftype) + out_min = np.array([0.61, 0.60, 0.60, 0.41, 0.19], dtype=ftype) + assert_equal(np.maximum.accumulate(arr), out_max) + assert_equal(np.minimum.accumulate(arr), out_min) def test_signaling_nan_exceptions(): with assert_no_warnings(): a = np.ndarray(shape=(), dtype='float32', buffer=b'\x00\xe0\xbf\xff') np.isnan(a) + +@pytest.mark.parametrize("arr", [ + np.arange(2), + np.matrix([0, 1]), + np.matrix([[0, 1], [2, 5]]), + ]) +def test_outer_subclass_preserve(arr): + # for gh-8661 + class foo(np.ndarray): pass + actual = np.multiply.outer(arr.view(foo), arr.view(foo)) + assert actual.__class__.__name__ == 'foo' + +def test_outer_bad_subclass(): + class BadArr1(np.ndarray): + def __array_finalize__(self, obj): + # The outer call reshapes to 3 dims, try to do a bad reshape. + if self.ndim == 3: + self.shape = self.shape + (1,) + + def __array_prepare__(self, obj, context=None): + return obj + + class BadArr2(np.ndarray): + def __array_finalize__(self, obj): + if isinstance(obj, BadArr2): + # outer inserts 1-sized dims. In that case disturb them. + if self.shape[-1] == 1: + self.shape = self.shape[::-1] + + def __array_prepare__(self, obj, context=None): + return obj + + for cls in [BadArr1, BadArr2]: + arr = np.ones((2, 3)).view(cls) + with assert_raises(TypeError) as a: + # The first array gets reshaped (not the second one) + np.add.outer(arr, [1, 2]) + + # This actually works, since we only see the reshaping error: + arr = np.ones((2, 3)).view(cls) + assert type(np.add.outer([1, 2], arr)) is cls + +def test_outer_exceeds_maxdims(): + deep = np.ones((1,) * 17) + with assert_raises(ValueError): + np.add.outer(deep, deep) + +def test_bad_legacy_ufunc_silent_errors(): + # legacy ufuncs can't report errors and NumPy can't check if the GIL + # is released. So NumPy has to check after the GIL is released just to + # cover all bases. `np.power` uses/used to use this. + arr = np.arange(3).astype(np.float64) + + with pytest.raises(RuntimeError, match=r"How unexpected :\)!"): + ncu_tests.always_error(arr, arr) + + with pytest.raises(RuntimeError, match=r"How unexpected :\)!"): + # not contiguous means the fast-path cannot be taken + non_contig = arr.repeat(20).reshape(-1, 6)[:, ::2] + ncu_tests.always_error(non_contig, arr) + + with pytest.raises(RuntimeError, match=r"How unexpected :\)!"): + ncu_tests.always_error.outer(arr, arr) + + with pytest.raises(RuntimeError, match=r"How unexpected :\)!"): + ncu_tests.always_error.reduce(arr) + + with pytest.raises(RuntimeError, match=r"How unexpected :\)!"): + ncu_tests.always_error.reduceat(arr, [0, 1]) + + with pytest.raises(RuntimeError, match=r"How unexpected :\)!"): + ncu_tests.always_error.accumulate(arr) + + with pytest.raises(RuntimeError, match=r"How unexpected :\)!"): + ncu_tests.always_error.at(arr, [0, 1, 2], arr) + + +@pytest.mark.parametrize('x1', [np.arange(3.0), [0.0, 1.0, 2.0]]) +def test_bad_legacy_gufunc_silent_errors(x1): + # Verify that an exception raised in a gufunc loop propagates correctly. + # The signature of always_error_gufunc is '(i),()->()'. + with pytest.raises(RuntimeError, match=r"How unexpected :\)!"): + ncu_tests.always_error_gufunc(x1, 0.0) diff --git a/numpy/core/tests/test_umath_accuracy.py b/numpy/core/tests/test_umath_accuracy.py new file mode 100644 index 000000000000..32e2dca66151 --- /dev/null +++ b/numpy/core/tests/test_umath_accuracy.py @@ -0,0 +1,58 @@ +import numpy as np +import os +from os import path +import sys +import pytest +from ctypes import c_longlong, c_double, c_float, c_int, cast, pointer, POINTER +from numpy.testing import assert_array_max_ulp +from numpy.core._multiarray_umath import __cpu_features__ + +IS_AVX = __cpu_features__.get('AVX512F', False) or \ + (__cpu_features__.get('FMA3', False) and __cpu_features__.get('AVX2', False)) +runtest = sys.platform.startswith('linux') and IS_AVX +platform_skip = pytest.mark.skipif(not runtest, + reason="avoid testing inconsistent platform " + "library implementations") + +# convert string to hex function taken from: +# https://stackoverflow.com/questions/1592158/convert-hex-to-float # +def convert(s, datatype="np.float32"): + i = int(s, 16) # convert from hex to a Python int + if (datatype == "np.float64"): + cp = pointer(c_longlong(i)) # make this into a c long long integer + fp = cast(cp, POINTER(c_double)) # cast the int pointer to a double pointer + else: + cp = pointer(c_int(i)) # make this into a c integer + fp = cast(cp, POINTER(c_float)) # cast the int pointer to a float pointer + + return fp.contents.value # dereference the pointer, get the float + +str_to_float = np.vectorize(convert) + +class TestAccuracy: + @platform_skip + def test_validate_transcendentals(self): + with np.errstate(all='ignore'): + data_dir = path.join(path.dirname(__file__), 'data') + files = os.listdir(data_dir) + files = list(filter(lambda f: f.endswith('.csv'), files)) + for filename in files: + filepath = path.join(data_dir, filename) + with open(filepath) as fid: + file_without_comments = (r for r in fid if not r[0] in ('$', '#')) + data = np.genfromtxt(file_without_comments, + dtype=('|S39','|S39','|S39',int), + names=('type','input','output','ulperr'), + delimiter=',', + skip_header=1) + npname = path.splitext(filename)[0].split('-')[3] + npfunc = getattr(np, npname) + for datatype in np.unique(data['type']): + data_subset = data[data['type'] == datatype] + inval = np.array(str_to_float(data_subset['input'].astype(str), data_subset['type'].astype(str)), dtype=eval(datatype)) + outval = np.array(str_to_float(data_subset['output'].astype(str), data_subset['type'].astype(str)), dtype=eval(datatype)) + perm = np.random.permutation(len(inval)) + inval = inval[perm] + outval = outval[perm] + maxulperr = data_subset['ulperr'].max() + assert_array_max_ulp(npfunc(inval), outval, maxulperr) diff --git a/numpy/core/tests/test_umath_complex.py b/numpy/core/tests/test_umath_complex.py index 785ae8c57d07..af5bbe59e698 100644 --- a/numpy/core/tests/test_umath_complex.py +++ b/numpy/core/tests/test_umath_complex.py @@ -1,13 +1,12 @@ -from __future__ import division, absolute_import, print_function - import sys import platform import pytest import numpy as np -import numpy.core.umath as ncu +# import the c-extension module directly since _arg is not exported via umath +import numpy.core._multiarray_umath as ncu from numpy.testing import ( - assert_raises, assert_equal, assert_array_equal, assert_almost_equal + assert_raises, assert_equal, assert_array_equal, assert_almost_equal, assert_array_max_ulp ) # TODO: branch cuts (use Pauli code) @@ -30,7 +29,7 @@ -class TestCexp(object): +class TestCexp: def test_simple(self): check = check_complex_value f = np.exp @@ -130,13 +129,12 @@ def test_special_values2(self): check(f, np.nan, 0, np.nan, 0) -class TestClog(object): +class TestClog: def test_simple(self): x = np.array([1+0j, 1+2j]) y_r = np.log(np.abs(x)) + 1j * np.angle(x) y = np.log(x) - for i in range(len(x)): - assert_almost_equal(y[i], y_r[i]) + assert_almost_equal(y, y_r) @platform_skip @pytest.mark.skipif(platform.machine() == "armv5tel", reason="See gh-413.") @@ -275,7 +273,7 @@ def test_special_values(self): assert_almost_equal(np.log(xa[i].conj()), ya[i].conj()) -class TestCsqrt(object): +class TestCsqrt: def test_simple(self): # sqrt(1) @@ -355,7 +353,7 @@ def _check_ninf_nan(dummy): # XXX: check for conj(csqrt(z)) == csqrt(conj(z)) (need to fix branch # cuts first) -class TestCpow(object): +class TestCpow: def setup(self): self.olderr = np.seterr(invalid='ignore') @@ -366,18 +364,24 @@ def test_simple(self): x = np.array([1+1j, 0+2j, 1+2j, np.inf, np.nan]) y_r = x ** 2 y = np.power(x, 2) - for i in range(len(x)): - assert_almost_equal(y[i], y_r[i]) + assert_almost_equal(y, y_r) def test_scalar(self): x = np.array([1, 1j, 2, 2.5+.37j, np.inf, np.nan]) y = np.array([1, 1j, -0.5+1.5j, -0.5+1.5j, 2, 3]) lx = list(range(len(x))) - # Compute the values for complex type in python - p_r = [complex(x[i]) ** complex(y[i]) for i in lx] - # Substitute a result allowed by C99 standard - p_r[4] = complex(np.inf, np.nan) - # Do the same with numpy complex scalars + + # Hardcode the expected `builtins.complex` values, + # as complex exponentiation is broken as of bpo-44698 + p_r = [ + 1+0j, + 0.20787957635076193+0j, + 0.35812203996480685+0.6097119028618724j, + 0.12659112128185032+0.48847676699581527j, + complex(np.inf, np.nan), + complex(np.nan, np.nan), + ] + n_r = [x[i] ** y[i] for i in lx] for i in lx: assert_almost_equal(n_r[i], p_r[i], err_msg='Loop %d\n' % i) @@ -386,16 +390,23 @@ def test_array(self): x = np.array([1, 1j, 2, 2.5+.37j, np.inf, np.nan]) y = np.array([1, 1j, -0.5+1.5j, -0.5+1.5j, 2, 3]) lx = list(range(len(x))) - # Compute the values for complex type in python - p_r = [complex(x[i]) ** complex(y[i]) for i in lx] - # Substitute a result allowed by C99 standard - p_r[4] = complex(np.inf, np.nan) - # Do the same with numpy arrays + + # Hardcode the expected `builtins.complex` values, + # as complex exponentiation is broken as of bpo-44698 + p_r = [ + 1+0j, + 0.20787957635076193+0j, + 0.35812203996480685+0.6097119028618724j, + 0.12659112128185032+0.48847676699581527j, + complex(np.inf, np.nan), + complex(np.nan, np.nan), + ] + n_r = x ** y for i in lx: assert_almost_equal(n_r[i], p_r[i], err_msg='Loop %d\n' % i) -class TestCabs(object): +class TestCabs: def setup(self): self.olderr = np.seterr(invalid='ignore') @@ -406,8 +417,7 @@ def test_simple(self): x = np.array([1+1j, 0+2j, 1+2j, np.inf, np.nan]) y_r = np.array([np.sqrt(2.), 2, np.sqrt(5), np.inf, np.nan]) y = np.abs(x) - for i in range(len(x)): - assert_almost_equal(y[i], y_r[i]) + assert_almost_equal(y, y_r) def test_fabs(self): # Test that np.abs(x +- 0j) == np.abs(x) (as mandated by C99 for cabs) @@ -453,11 +463,12 @@ def g(a, b): return np.abs(complex(a, b)) xa = np.array(x, dtype=complex) - for i in range(len(xa)): - ref = g(x[i], y[i]) - check_real_value(f, x[i], y[i], ref) + assert len(xa) == len(x) == len(y) + for xi, yi in zip(x, y): + ref = g(xi, yi) + check_real_value(f, xi, yi, ref) -class TestCarg(object): +class TestCarg: def test_simple(self): check_real_value(ncu._arg, 1, 0, 0, False) check_real_value(ncu._arg, 0, 1, 0.5*np.pi, False) @@ -541,3 +552,71 @@ def check_complex_value(f, x1, y1, x2, y2, exact=True): assert_equal(f(z1), z2) else: assert_almost_equal(f(z1), z2) + +class TestSpecialComplexAVX: + @pytest.mark.parametrize("stride", [-4,-2,-1,1,2,4]) + @pytest.mark.parametrize("astype", [np.complex64, np.complex128]) + def test_array(self, stride, astype): + arr = np.array([complex(np.nan , np.nan), + complex(np.nan , np.inf), + complex(np.inf , np.nan), + complex(np.inf , np.inf), + complex(0. , np.inf), + complex(np.inf , 0.), + complex(0. , 0.), + complex(0. , np.nan), + complex(np.nan , 0.)], dtype=astype) + abs_true = np.array([np.nan, np.inf, np.inf, np.inf, np.inf, np.inf, 0., np.nan, np.nan], dtype=arr.real.dtype) + sq_true = np.array([complex(np.nan, np.nan), + complex(np.nan, np.nan), + complex(np.nan, np.nan), + complex(np.nan, np.inf), + complex(-np.inf, np.nan), + complex(np.inf, np.nan), + complex(0., 0.), + complex(np.nan, np.nan), + complex(np.nan, np.nan)], dtype=astype) + assert_equal(np.abs(arr[::stride]), abs_true[::stride]) + with np.errstate(invalid='ignore'): + assert_equal(np.square(arr[::stride]), sq_true[::stride]) + +class TestComplexAbsoluteAVX: + @pytest.mark.parametrize("arraysize", [1,2,3,4,5,6,7,8,9,10,11,13,15,17,18,19]) + @pytest.mark.parametrize("stride", [-4,-3,-2,-1,1,2,3,4]) + @pytest.mark.parametrize("astype", [np.complex64, np.complex128]) + # test to ensure masking and strides work as intended in the AVX implementation + def test_array(self, arraysize, stride, astype): + arr = np.ones(arraysize, dtype=astype) + abs_true = np.ones(arraysize, dtype=arr.real.dtype) + assert_equal(np.abs(arr[::stride]), abs_true[::stride]) + +# Testcase taken as is from https://github.com/numpy/numpy/issues/16660 +class TestComplexAbsoluteMixedDTypes: + @pytest.mark.parametrize("stride", [-4,-3,-2,-1,1,2,3,4]) + @pytest.mark.parametrize("astype", [np.complex64, np.complex128]) + @pytest.mark.parametrize("func", ['abs', 'square', 'conjugate']) + + def test_array(self, stride, astype, func): + dtype = [('template_id', '<i8'), ('bank_chisq','<f4'), + ('bank_chisq_dof','<i8'), ('chisq', '<f4'), ('chisq_dof','<i8'), + ('cont_chisq', '<f4'), ('psd_var_val', '<f4'), ('sg_chisq','<f4'), + ('mycomplex', astype), ('time_index', '<i8')] + vec = np.array([ + (0, 0., 0, -31.666483, 200, 0., 0., 1. , 3.0+4.0j , 613090), + (1, 0., 0, 260.91525 , 42, 0., 0., 1. , 5.0+12.0j , 787315), + (1, 0., 0, 52.15155 , 42, 0., 0., 1. , 8.0+15.0j , 806641), + (1, 0., 0, 52.430195, 42, 0., 0., 1. , 7.0+24.0j , 1363540), + (2, 0., 0, 304.43646 , 58, 0., 0., 1. , 20.0+21.0j , 787323), + (3, 0., 0, 299.42108 , 52, 0., 0., 1. , 12.0+35.0j , 787332), + (4, 0., 0, 39.4836 , 28, 0., 0., 9.182192, 9.0+40.0j , 787304), + (4, 0., 0, 76.83787 , 28, 0., 0., 1. , 28.0+45.0j, 1321869), + (5, 0., 0, 143.26366 , 24, 0., 0., 10.996129, 11.0+60.0j , 787299)], dtype=dtype) + myfunc = getattr(np, func) + a = vec['mycomplex'] + g = myfunc(a[::stride]) + + b = vec['mycomplex'].copy() + h = myfunc(b[::stride]) + + assert_array_max_ulp(h.real, g.real, 1) + assert_array_max_ulp(h.imag, g.imag, 1) diff --git a/numpy/core/tests/test_unicode.py b/numpy/core/tests/test_unicode.py index 2ffd8801b7ea..8e0dd47cb077 100644 --- a/numpy/core/tests/test_unicode.py +++ b/numpy/core/tests/test_unicode.py @@ -1,45 +1,24 @@ -from __future__ import division, absolute_import, print_function - -import sys - import numpy as np -from numpy.compat import unicode from numpy.testing import assert_, assert_equal, assert_array_equal -# Guess the UCS length for this python interpreter -if sys.version_info[:2] >= (3, 3): - # Python 3.3 uses a flexible string representation - ucs4 = False - - def buffer_length(arr): - if isinstance(arr, unicode): - arr = str(arr) - if not arr: - charmax = 0 - else: - charmax = max([ord(c) for c in arr]) - if charmax < 256: - size = 1 - elif charmax < 65536: - size = 2 - else: - size = 4 - return size * len(arr) - v = memoryview(arr) - if v.shape is None: - return len(v) * v.itemsize +def buffer_length(arr): + if isinstance(arr, str): + if not arr: + charmax = 0 + else: + charmax = max([ord(c) for c in arr]) + if charmax < 256: + size = 1 + elif charmax < 65536: + size = 2 else: - return np.prod(v.shape) * v.itemsize -else: - if len(buffer(u'u')) == 4: - ucs4 = True + size = 4 + return size * len(arr) + v = memoryview(arr) + if v.shape is None: + return len(v) * v.itemsize else: - ucs4 = False - - def buffer_length(arr): - if isinstance(arr, np.ndarray): - return len(arr.data) - return len(buffer(arr)) + return np.prod(v.shape) * v.itemsize # In both cases below we need to make sure that the byte swapped value (as # UCS4) is still a valid unicode: @@ -54,12 +33,8 @@ def test_string_cast(): uni_arr1 = str_arr.astype('>U') uni_arr2 = str_arr.astype('<U') - if sys.version_info[0] < 3: - assert_array_equal(str_arr, uni_arr1) - assert_array_equal(str_arr, uni_arr2) - else: - assert_(str_arr != uni_arr1) - assert_(str_arr != uni_arr2) + assert_(str_arr != uni_arr1) + assert_(str_arr != uni_arr2) assert_array_equal(uni_arr1, uni_arr2) @@ -67,7 +42,7 @@ def test_string_cast(): # Creation tests ############################################################ -class CreateZeros(object): +class CreateZeros: """Check the creation of zero-valued arrays""" def content_check(self, ua, ua_scalar, nbytes): @@ -81,10 +56,7 @@ def content_check(self, ua, ua_scalar, nbytes): # Encode to ascii and double check assert_(ua_scalar.encode('ascii') == b'') # Check buffer lengths for scalars - if ucs4: - assert_(buffer_length(ua_scalar) == 0) - else: - assert_(buffer_length(ua_scalar) == 0) + assert_(buffer_length(ua_scalar) == 0) def test_zeros0D(self): # Check creation of 0-dimensional objects @@ -119,7 +91,7 @@ class TestCreateZeros_1009(CreateZeros): ulen = 1009 -class CreateValues(object): +class CreateValues: """Check the creation of unicode arrays with values""" def content_check(self, ua, ua_scalar, nbytes): @@ -134,17 +106,14 @@ def content_check(self, ua, ua_scalar, nbytes): assert_(ua_scalar.encode('utf-8') == (self.ucs_value*self.ulen).encode('utf-8')) # Check buffer lengths for scalars - if ucs4: - assert_(buffer_length(ua_scalar) == 4*self.ulen) + if self.ucs_value == ucs4_value: + # In UCS2, the \U0010FFFF will be represented using a + # surrogate *pair* + assert_(buffer_length(ua_scalar) == 2*2*self.ulen) else: - if self.ucs_value == ucs4_value: - # In UCS2, the \U0010FFFF will be represented using a - # surrogate *pair* - assert_(buffer_length(ua_scalar) == 2*2*self.ulen) - else: - # In UCS2, the \uFFFF will be represented using a - # regular 2-byte word - assert_(buffer_length(ua_scalar) == 2*self.ulen) + # In UCS2, the \uFFFF will be represented using a + # regular 2-byte word + assert_(buffer_length(ua_scalar) == 2*self.ulen) def test_values0D(self): # Check creation of 0-dimensional objects with values @@ -204,7 +173,7 @@ class TestCreateValues_1009_UCS4(CreateValues): # Assignment tests ############################################################ -class AssignValues(object): +class AssignValues: """Check the assignment of unicode arrays with values""" def content_check(self, ua, ua_scalar, nbytes): @@ -219,17 +188,14 @@ def content_check(self, ua, ua_scalar, nbytes): assert_(ua_scalar.encode('utf-8') == (self.ucs_value*self.ulen).encode('utf-8')) # Check buffer lengths for scalars - if ucs4: - assert_(buffer_length(ua_scalar) == 4*self.ulen) + if self.ucs_value == ucs4_value: + # In UCS2, the \U0010FFFF will be represented using a + # surrogate *pair* + assert_(buffer_length(ua_scalar) == 2*2*self.ulen) else: - if self.ucs_value == ucs4_value: - # In UCS2, the \U0010FFFF will be represented using a - # surrogate *pair* - assert_(buffer_length(ua_scalar) == 2*2*self.ulen) - else: - # In UCS2, the \uFFFF will be represented using a - # regular 2-byte word - assert_(buffer_length(ua_scalar) == 2*self.ulen) + # In UCS2, the \uFFFF will be represented using a + # regular 2-byte word + assert_(buffer_length(ua_scalar) == 2*self.ulen) def test_values0D(self): # Check assignment of 0-dimensional objects with values @@ -294,7 +260,7 @@ class TestAssignValues_1009_UCS4(AssignValues): # Byteorder tests ############################################################ -class ByteorderValues(object): +class ByteorderValues: """Check the byteorder of unicode arrays in round-trip conversions""" def test_values0D(self): diff --git a/numpy/core/umath.py b/numpy/core/umath.py new file mode 100644 index 000000000000..6a5474ffed14 --- /dev/null +++ b/numpy/core/umath.py @@ -0,0 +1,36 @@ +""" +Create the numpy.core.umath namespace for backward compatibility. In v1.16 +the multiarray and umath c-extension modules were merged into a single +_multiarray_umath extension module. So we replicate the old namespace +by importing from the extension module. + +""" + +from . import _multiarray_umath +from ._multiarray_umath import * # noqa: F403 +# These imports are needed for backward compatibility, +# do not change them. issue gh-11862 +# _ones_like is semi-public, on purpose not added to __all__ +from ._multiarray_umath import _UFUNC_API, _add_newdoc_ufunc, _ones_like + +__all__ = [ + '_UFUNC_API', 'ERR_CALL', 'ERR_DEFAULT', 'ERR_IGNORE', 'ERR_LOG', + 'ERR_PRINT', 'ERR_RAISE', 'ERR_WARN', 'FLOATING_POINT_SUPPORT', + 'FPE_DIVIDEBYZERO', 'FPE_INVALID', 'FPE_OVERFLOW', 'FPE_UNDERFLOW', 'NAN', + 'NINF', 'NZERO', 'PINF', 'PZERO', 'SHIFT_DIVIDEBYZERO', 'SHIFT_INVALID', + 'SHIFT_OVERFLOW', 'SHIFT_UNDERFLOW', 'UFUNC_BUFSIZE_DEFAULT', + 'UFUNC_PYVALS_NAME', '_add_newdoc_ufunc', 'absolute', 'add', + 'arccos', 'arccosh', 'arcsin', 'arcsinh', 'arctan', 'arctan2', 'arctanh', + 'bitwise_and', 'bitwise_or', 'bitwise_xor', 'cbrt', 'ceil', 'conj', + 'conjugate', 'copysign', 'cos', 'cosh', 'deg2rad', 'degrees', 'divide', + 'divmod', 'e', 'equal', 'euler_gamma', 'exp', 'exp2', 'expm1', 'fabs', + 'floor', 'floor_divide', 'float_power', 'fmax', 'fmin', 'fmod', 'frexp', + 'frompyfunc', 'gcd', 'geterrobj', 'greater', 'greater_equal', 'heaviside', + 'hypot', 'invert', 'isfinite', 'isinf', 'isnan', 'isnat', 'lcm', 'ldexp', + 'left_shift', 'less', 'less_equal', 'log', 'log10', 'log1p', 'log2', + 'logaddexp', 'logaddexp2', 'logical_and', 'logical_not', 'logical_or', + 'logical_xor', 'maximum', 'minimum', 'mod', 'modf', 'multiply', 'negative', + 'nextafter', 'not_equal', 'pi', 'positive', 'power', 'rad2deg', 'radians', + 'reciprocal', 'remainder', 'right_shift', 'rint', 'seterrobj', 'sign', + 'signbit', 'sin', 'sinh', 'spacing', 'sqrt', 'square', 'subtract', 'tan', + 'tanh', 'true_divide', 'trunc'] diff --git a/numpy/core/umath_tests.py b/numpy/core/umath_tests.py index 28e325b984cb..90ab17e6744a 100644 --- a/numpy/core/umath_tests.py +++ b/numpy/core/umath_tests.py @@ -2,8 +2,6 @@ Shim for _umath_tests to allow a deprecation period for the new name. """ -from __future__ import division, absolute_import, print_function - import warnings # 2018-04-04, numpy 1.15.0 diff --git a/numpy/ctypeslib.py b/numpy/ctypeslib.py index 9d71adbdb6f6..8d105a248f17 100644 --- a/numpy/ctypeslib.py +++ b/numpy/ctypeslib.py @@ -4,7 +4,7 @@ ============================ See Also ---------- +-------- load_library : Load a C library. ndpointer : Array restype/argtype with verification. as_ctypes : Create a ctypes array from an ndarray. @@ -12,7 +12,7 @@ References ---------- -.. [1] "SciPy Cookbook: ctypes", http://www.scipy.org/Cookbook/Ctypes +.. [1] "SciPy Cookbook: ctypes", https://scipy-cookbook.readthedocs.io/items/Ctypes.html Examples -------- @@ -49,13 +49,13 @@ >>> _lib.foo_func(out, len(out)) #doctest: +SKIP """ -from __future__ import division, absolute_import, print_function - -__all__ = ['load_library', 'ndpointer', 'test', 'ctypes_load_library', - 'c_intp', 'as_ctypes', 'as_array'] +__all__ = ['load_library', 'ndpointer', 'c_intp', 'as_ctypes', 'as_array', + 'as_ctypes_type'] -import sys, os -from numpy import integer, ndarray, dtype as _dtype, deprecate, array +import os +from numpy import ( + integer, ndarray, dtype as _dtype, asarray, frombuffer +) from numpy.core.multiarray import _flagdict, flagsobj try: @@ -75,7 +75,6 @@ def _dummy(*args, **kwds): """ raise ImportError("ctypes is not available.") - ctypes_load_library = _dummy load_library = _dummy as_ctypes = _dummy as_array = _dummy @@ -90,37 +89,46 @@ def _dummy(*args, **kwds): # Adapted from Albert Strasheim def load_library(libname, loader_path): """ - It is possible to load a library using - >>> lib = ctypes.cdll[<full_path_name>] + It is possible to load a library using + + >>> lib = ctypes.cdll[<full_path_name>] # doctest: +SKIP But there are cross-platform considerations, such as library file extensions, - plus the fact Windows will just load the first library it finds with that name. + plus the fact Windows will just load the first library it finds with that name. NumPy supplies the load_library function as a convenience. + .. versionchanged:: 1.20.0 + Allow libname and loader_path to take any + :term:`python:path-like object`. + Parameters ---------- - libname : str + libname : path-like Name of the library, which can have 'lib' as a prefix, but without an extension. - loader_path : str + loader_path : path-like Where the library can be found. Returns ------- ctypes.cdll[libpath] : library object - A ctypes library object + A ctypes library object Raises ------ OSError - If there is no library with the expected extension, or the + If there is no library with the expected extension, or the library is defective and cannot be loaded. """ if ctypes.__version__ < '1.0.1': import warnings - warnings.warn("All features of ctypes interface may not work " \ + warnings.warn("All features of ctypes interface may not work " "with ctypes < 1.0.1", stacklevel=2) + # Convert path-like objects into strings + libname = os.fsdecode(libname) + loader_path = os.fsdecode(loader_path) + ext = os.path.splitext(libname)[1] if not ext: # Try to load library with platform-specific name, otherwise @@ -154,8 +162,6 @@ def load_library(libname, loader_path): ## if no successful return in the libname_ext loop: raise OSError("no file with expected extension") - ctypes_load_library = deprecate(load_library, 'ctypes_load_library', - 'load_library') def _num_fromflags(flaglist): num = 0 @@ -175,24 +181,6 @@ def _flags_fromnum(num): class _ndptr(_ndptr_base): - - def _check_retval_(self): - """This method is called when this class is used as the .restype - attribute for a shared-library function. It constructs a numpy - array from a void pointer.""" - return array(self) - - @property - def __array_interface__(self): - return {'descr': self._dtype_.descr, - '__ref': self, - 'strides': None, - 'shape': self._shape_, - 'version': 3, - 'typestr': self._dtype_.descr[0][1], - 'data': (self.value, False), - } - @classmethod def from_param(cls, obj): if not isinstance(obj, ndarray): @@ -213,6 +201,34 @@ def from_param(cls, obj): return obj.ctypes +class _concrete_ndptr(_ndptr): + """ + Like _ndptr, but with `_shape_` and `_dtype_` specified. + + Notably, this means the pointer has enough information to reconstruct + the array, which is not generally true. + """ + def _check_retval_(self): + """ + This method is called when this class is used as the .restype + attribute for a shared-library function, to automatically wrap the + pointer into an array. + """ + return self.contents + + @property + def contents(self): + """ + Get an ndarray viewing the data pointed to by this pointer. + + This mirrors the `contents` attribute of a normal ctypes pointer + """ + full_dtype = _dtype((self._dtype_, self._shape_)) + full_ctype = ctypes.c_char * full_dtype.itemsize + buffer = ctypes.cast(self, ctypes.POINTER(full_ctype)).contents + return frombuffer(buffer, dtype=full_dtype).squeeze(axis=0) + + # Factory for an array-checking class with from_param defined for # use with ctypes argtypes mechanism _pointer_type_cache = {} @@ -269,8 +285,11 @@ def ndpointer(dtype=None, ndim=None, shape=None, flags=None): """ + # normalize dtype to an Optional[dtype] if dtype is not None: dtype = _dtype(dtype) + + # normalize flags to an Optional[int] num = None if flags is not None: if isinstance(flags, str): @@ -284,63 +303,208 @@ def ndpointer(dtype=None, ndim=None, shape=None, flags=None): if num is None: try: flags = [x.strip().upper() for x in flags] - except Exception: - raise TypeError("invalid flags specification") + except Exception as e: + raise TypeError("invalid flags specification") from e num = _num_fromflags(flags) + + # normalize shape to an Optional[tuple] + if shape is not None: + try: + shape = tuple(shape) + except TypeError: + # single integer -> 1-tuple + shape = (shape,) + + cache_key = (dtype, ndim, shape, num) + try: - return _pointer_type_cache[(dtype, ndim, shape, num)] + return _pointer_type_cache[cache_key] except KeyError: pass + + # produce a name for the new type if dtype is None: name = 'any' - elif dtype.names: + elif dtype.names is not None: name = str(id(dtype)) else: name = dtype.str if ndim is not None: name += "_%dd" % ndim if shape is not None: - try: - strshape = [str(x) for x in shape] - except TypeError: - strshape = [str(shape)] - shape = (shape,) - shape = tuple(shape) - name += "_"+"x".join(strshape) + name += "_"+"x".join(str(x) for x in shape) if flags is not None: name += "_"+"_".join(flags) + + if dtype is not None and shape is not None: + base = _concrete_ndptr else: - flags = [] - klass = type("ndpointer_%s"%name, (_ndptr,), + base = _ndptr + + klass = type("ndpointer_%s"%name, (base,), {"_dtype_": dtype, "_shape_" : shape, "_ndim_" : ndim, "_flags_" : num}) - _pointer_type_cache[(dtype, shape, ndim, num)] = klass + _pointer_type_cache[cache_key] = klass return klass -def _get_typecodes(): - """ Return a dictionary mapping __array_interface__ formats to ctypes types """ - ct = ctypes - simple_types = [ - ct.c_byte, ct.c_short, ct.c_int, ct.c_long, ct.c_longlong, - ct.c_ubyte, ct.c_ushort, ct.c_uint, ct.c_ulong, ct.c_ulonglong, - ct.c_float, ct.c_double, - ] +if ctypes is not None: + def _ctype_ndarray(element_type, shape): + """ Create an ndarray of the given element type and shape """ + for dim in shape[::-1]: + element_type = dim * element_type + # prevent the type name include np.ctypeslib + element_type.__module__ = None + return element_type - return {_dtype(ctype).str: ctype for ctype in simple_types} + def _get_scalar_type_map(): + """ + Return a dictionary mapping native endian scalar dtype to ctypes types + """ + ct = ctypes + simple_types = [ + ct.c_byte, ct.c_short, ct.c_int, ct.c_long, ct.c_longlong, + ct.c_ubyte, ct.c_ushort, ct.c_uint, ct.c_ulong, ct.c_ulonglong, + ct.c_float, ct.c_double, + ct.c_bool, + ] + return {_dtype(ctype): ctype for ctype in simple_types} -def _ctype_ndarray(element_type, shape): - """ Create an ndarray of the given element type and shape """ - for dim in shape[::-1]: - element_type = element_type * dim - return element_type + _scalar_type_map = _get_scalar_type_map() + + + def _ctype_from_dtype_scalar(dtype): + # swapping twice ensure that `=` is promoted to <, >, or | + dtype_with_endian = dtype.newbyteorder('S').newbyteorder('S') + dtype_native = dtype.newbyteorder('=') + try: + ctype = _scalar_type_map[dtype_native] + except KeyError as e: + raise NotImplementedError( + "Converting {!r} to a ctypes type".format(dtype) + ) from None + + if dtype_with_endian.byteorder == '>': + ctype = ctype.__ctype_be__ + elif dtype_with_endian.byteorder == '<': + ctype = ctype.__ctype_le__ + + return ctype + + + def _ctype_from_dtype_subarray(dtype): + element_dtype, shape = dtype.subdtype + ctype = _ctype_from_dtype(element_dtype) + return _ctype_ndarray(ctype, shape) + + + def _ctype_from_dtype_structured(dtype): + # extract offsets of each field + field_data = [] + for name in dtype.names: + field_dtype, offset = dtype.fields[name][:2] + field_data.append((offset, name, _ctype_from_dtype(field_dtype))) + + # ctypes doesn't care about field order + field_data = sorted(field_data, key=lambda f: f[0]) + + if len(field_data) > 1 and all(offset == 0 for offset, name, ctype in field_data): + # union, if multiple fields all at address 0 + size = 0 + _fields_ = [] + for offset, name, ctype in field_data: + _fields_.append((name, ctype)) + size = max(size, ctypes.sizeof(ctype)) + + # pad to the right size + if dtype.itemsize != size: + _fields_.append(('', ctypes.c_char * dtype.itemsize)) + + # we inserted manual padding, so always `_pack_` + return type('union', (ctypes.Union,), dict( + _fields_=_fields_, + _pack_=1, + __module__=None, + )) + else: + last_offset = 0 + _fields_ = [] + for offset, name, ctype in field_data: + padding = offset - last_offset + if padding < 0: + raise NotImplementedError("Overlapping fields") + if padding > 0: + _fields_.append(('', ctypes.c_char * padding)) + + _fields_.append((name, ctype)) + last_offset = offset + ctypes.sizeof(ctype) + + + padding = dtype.itemsize - last_offset + if padding > 0: + _fields_.append(('', ctypes.c_char * padding)) + + # we inserted manual padding, so always `_pack_` + return type('struct', (ctypes.Structure,), dict( + _fields_=_fields_, + _pack_=1, + __module__=None, + )) + + + def _ctype_from_dtype(dtype): + if dtype.fields is not None: + return _ctype_from_dtype_structured(dtype) + elif dtype.subdtype is not None: + return _ctype_from_dtype_subarray(dtype) + else: + return _ctype_from_dtype_scalar(dtype) + + + def as_ctypes_type(dtype): + r""" + Convert a dtype into a ctypes type. + + Parameters + ---------- + dtype : dtype + The dtype to convert + + Returns + ------- + ctype + A ctype scalar, union, array, or struct + + Raises + ------ + NotImplementedError + If the conversion is not possible + + Notes + ----- + This function does not losslessly round-trip in either direction. + + ``np.dtype(as_ctypes_type(dt))`` will: + + - insert padding fields + - reorder fields to be sorted by offset + - discard field titles + + ``as_ctypes_type(np.dtype(ctype))`` will: + + - discard the class names of `ctypes.Structure`\ s and + `ctypes.Union`\ s + - convert single-element `ctypes.Union`\ s into single-element + `ctypes.Structure`\ s + - insert padding fields + + """ + return _ctype_from_dtype(_dtype(dtype)) -if ctypes is not None: - _typecodes = _get_typecodes() def as_array(obj, shape=None): """ @@ -360,7 +524,8 @@ def as_array(obj, shape=None): p_arr_type = ctypes.POINTER(_ctype_ndarray(obj._type_, shape)) obj = ctypes.cast(obj, p_arr_type).contents - return array(obj, copy=False) + return asarray(obj) + def as_ctypes(obj): """Create and return a ctypes object from a numpy array. Actually @@ -373,7 +538,11 @@ def as_ctypes(obj): addr, readonly = ai["data"] if readonly: raise TypeError("readonly arrays unsupported") - tp = _ctype_ndarray(_typecodes[ai["typestr"]], ai["shape"]) - result = tp.from_address(addr) - result.__keep = ai + + # can't use `_dtype((ai["typestr"], ai["shape"]))` here, as it overflows + # dtype.itemsize (gh-14214) + ctype_scalar = as_ctypes_type(ai["typestr"]) + result_type = _ctype_ndarray(ctype_scalar, ai["shape"]) + result = result_type.from_address(addr) + result.__keep = obj return result diff --git a/numpy/ctypeslib.pyi b/numpy/ctypeslib.pyi new file mode 100644 index 000000000000..1c396d240173 --- /dev/null +++ b/numpy/ctypeslib.pyi @@ -0,0 +1,268 @@ +# NOTE: Numpy's mypy plugin is used for importing the correct +# platform-specific `ctypes._SimpleCData[int]` sub-type +from ctypes import c_int64 as _c_intp + +import os +import sys +import ctypes +from typing import ( + Literal as L, + Any, + List, + Union, + TypeVar, + Type, + Generic, + Optional, + overload, + Iterable, + ClassVar, + Tuple, + Sequence, + Dict, +) + +from numpy import ( + ndarray, + dtype, + generic, + bool_, + byte, + short, + intc, + int_, + longlong, + ubyte, + ushort, + uintc, + uint, + ulonglong, + single, + double, + float_, + longdouble, + void, +) +from numpy.core._internal import _ctypes +from numpy.core.multiarray import flagsobj +from numpy.typing import ( + # Arrays + ArrayLike, + NDArray, + _FiniteNestedSequence, + _SupportsArray, + + # Shapes + _ShapeLike, + + # DTypes + DTypeLike, + _SupportsDType, + _VoidDTypeLike, + _BoolCodes, + _UByteCodes, + _UShortCodes, + _UIntCCodes, + _UIntCodes, + _ULongLongCodes, + _ByteCodes, + _ShortCodes, + _IntCCodes, + _IntCodes, + _LongLongCodes, + _SingleCodes, + _DoubleCodes, + _LongDoubleCodes, +) + +# TODO: Add a proper `_Shape` bound once we've got variadic typevars +_DType = TypeVar("_DType", bound=dtype[Any]) +_DTypeOptional = TypeVar("_DTypeOptional", bound=Optional[dtype[Any]]) +_SCT = TypeVar("_SCT", bound=generic) + +_DTypeLike = Union[ + dtype[_SCT], + Type[_SCT], + _SupportsDType[dtype[_SCT]], +] +_ArrayLike = _FiniteNestedSequence[_SupportsArray[dtype[_SCT]]] + +_FlagsKind = L[ + 'C_CONTIGUOUS', 'CONTIGUOUS', 'C', + 'F_CONTIGUOUS', 'FORTRAN', 'F', + 'ALIGNED', 'A', + 'WRITEABLE', 'W', + 'OWNDATA', 'O', + 'UPDATEIFCOPY', 'U', + 'WRITEBACKIFCOPY', 'X', +] + +# TODO: Add a shape typevar once we have variadic typevars (PEP 646) +class _ndptr(ctypes.c_void_p, Generic[_DTypeOptional]): + # In practice these 4 classvars are defined in the dynamic class + # returned by `ndpointer` + _dtype_: ClassVar[_DTypeOptional] + _shape_: ClassVar[None] + _ndim_: ClassVar[None | int] + _flags_: ClassVar[None | List[_FlagsKind]] + + @overload + @classmethod + def from_param(cls: Type[_ndptr[None]], obj: ndarray[Any, Any]) -> _ctypes: ... + @overload + @classmethod + def from_param(cls: Type[_ndptr[_DType]], obj: ndarray[Any, _DType]) -> _ctypes: ... + +class _concrete_ndptr(_ndptr[_DType]): + _dtype_: ClassVar[_DType] + _shape_: ClassVar[Tuple[int, ...]] + @property + def contents(self) -> ndarray[Any, _DType]: ... + +def load_library( + libname: str | bytes | os.PathLike[str] | os.PathLike[bytes], + loader_path: str | bytes | os.PathLike[str] | os.PathLike[bytes], +) -> ctypes.CDLL: ... + +__all__: List[str] + +c_intp = _c_intp + +@overload +def ndpointer( + dtype: None = ..., + ndim: int = ..., + shape: None | _ShapeLike = ..., + flags: None | _FlagsKind | Iterable[_FlagsKind] | int | flagsobj = ..., +) -> Type[_ndptr[None]]: ... +@overload +def ndpointer( + dtype: _DTypeLike[_SCT], + ndim: int = ..., + *, + shape: _ShapeLike, + flags: None | _FlagsKind | Iterable[_FlagsKind] | int | flagsobj = ..., +) -> Type[_concrete_ndptr[dtype[_SCT]]]: ... +@overload +def ndpointer( + dtype: DTypeLike, + ndim: int = ..., + *, + shape: _ShapeLike, + flags: None | _FlagsKind | Iterable[_FlagsKind] | int | flagsobj = ..., +) -> Type[_concrete_ndptr[dtype[Any]]]: ... +@overload +def ndpointer( + dtype: _DTypeLike[_SCT], + ndim: int = ..., + shape: None = ..., + flags: None | _FlagsKind | Iterable[_FlagsKind] | int | flagsobj = ..., +) -> Type[_ndptr[dtype[_SCT]]]: ... +@overload +def ndpointer( + dtype: DTypeLike, + ndim: int = ..., + shape: None = ..., + flags: None | _FlagsKind | Iterable[_FlagsKind] | int | flagsobj = ..., +) -> Type[_ndptr[dtype[Any]]]: ... + +@overload +def as_ctypes_type(dtype: _BoolCodes | _DTypeLike[bool_] | Type[ctypes.c_bool]) -> Type[ctypes.c_bool]: ... +@overload +def as_ctypes_type(dtype: _ByteCodes | _DTypeLike[byte] | Type[ctypes.c_byte]) -> Type[ctypes.c_byte]: ... +@overload +def as_ctypes_type(dtype: _ShortCodes | _DTypeLike[short] | Type[ctypes.c_short]) -> Type[ctypes.c_short]: ... +@overload +def as_ctypes_type(dtype: _IntCCodes | _DTypeLike[intc] | Type[ctypes.c_int]) -> Type[ctypes.c_int]: ... +@overload +def as_ctypes_type(dtype: _IntCodes | _DTypeLike[int_] | Type[int | ctypes.c_long]) -> Type[ctypes.c_long]: ... +@overload +def as_ctypes_type(dtype: _LongLongCodes | _DTypeLike[longlong] | Type[ctypes.c_longlong]) -> Type[ctypes.c_longlong]: ... +@overload +def as_ctypes_type(dtype: _UByteCodes | _DTypeLike[ubyte] | Type[ctypes.c_ubyte]) -> Type[ctypes.c_ubyte]: ... +@overload +def as_ctypes_type(dtype: _UShortCodes | _DTypeLike[ushort] | Type[ctypes.c_ushort]) -> Type[ctypes.c_ushort]: ... +@overload +def as_ctypes_type(dtype: _UIntCCodes | _DTypeLike[uintc] | Type[ctypes.c_uint]) -> Type[ctypes.c_uint]: ... +@overload +def as_ctypes_type(dtype: _UIntCodes | _DTypeLike[uint] | Type[ctypes.c_ulong]) -> Type[ctypes.c_ulong]: ... +@overload +def as_ctypes_type(dtype: _ULongLongCodes | _DTypeLike[ulonglong] | Type[ctypes.c_ulonglong]) -> Type[ctypes.c_ulonglong]: ... +@overload +def as_ctypes_type(dtype: _SingleCodes | _DTypeLike[single] | Type[ctypes.c_float]) -> Type[ctypes.c_float]: ... +@overload +def as_ctypes_type(dtype: _DoubleCodes | _DTypeLike[double] | Type[float | ctypes.c_double]) -> Type[ctypes.c_double]: ... +@overload +def as_ctypes_type(dtype: _LongDoubleCodes | _DTypeLike[longdouble] | Type[ctypes.c_longdouble]) -> Type[ctypes.c_longdouble]: ... +@overload +def as_ctypes_type(dtype: _VoidDTypeLike) -> Type[Any]: ... # `ctypes.Union` or `ctypes.Structure` +@overload +def as_ctypes_type(dtype: str) -> Type[Any]: ... + +@overload +def as_array(obj: ctypes._PointerLike, shape: Sequence[int]) -> NDArray[Any]: ... +@overload +def as_array(obj: _ArrayLike[_SCT], shape: None | _ShapeLike = ...) -> NDArray[_SCT]: ... +@overload +def as_array(obj: object, shape: None | _ShapeLike = ...) -> NDArray[Any]: ... + +@overload +def as_ctypes(obj: bool_) -> ctypes.c_bool: ... +@overload +def as_ctypes(obj: byte) -> ctypes.c_byte: ... +@overload +def as_ctypes(obj: short) -> ctypes.c_short: ... +@overload +def as_ctypes(obj: intc) -> ctypes.c_int: ... +@overload +def as_ctypes(obj: int_) -> ctypes.c_long: ... +@overload +def as_ctypes(obj: longlong) -> ctypes.c_longlong: ... +@overload +def as_ctypes(obj: ubyte) -> ctypes.c_ubyte: ... +@overload +def as_ctypes(obj: ushort) -> ctypes.c_ushort: ... +@overload +def as_ctypes(obj: uintc) -> ctypes.c_uint: ... +@overload +def as_ctypes(obj: uint) -> ctypes.c_ulong: ... +@overload +def as_ctypes(obj: ulonglong) -> ctypes.c_ulonglong: ... +@overload +def as_ctypes(obj: single) -> ctypes.c_float: ... +@overload +def as_ctypes(obj: double) -> ctypes.c_double: ... +@overload +def as_ctypes(obj: longdouble) -> ctypes.c_longdouble: ... +@overload +def as_ctypes(obj: void) -> Any: ... # `ctypes.Union` or `ctypes.Structure` +@overload +def as_ctypes(obj: NDArray[bool_]) -> ctypes.Array[ctypes.c_bool]: ... +@overload +def as_ctypes(obj: NDArray[byte]) -> ctypes.Array[ctypes.c_byte]: ... +@overload +def as_ctypes(obj: NDArray[short]) -> ctypes.Array[ctypes.c_short]: ... +@overload +def as_ctypes(obj: NDArray[intc]) -> ctypes.Array[ctypes.c_int]: ... +@overload +def as_ctypes(obj: NDArray[int_]) -> ctypes.Array[ctypes.c_long]: ... +@overload +def as_ctypes(obj: NDArray[longlong]) -> ctypes.Array[ctypes.c_longlong]: ... +@overload +def as_ctypes(obj: NDArray[ubyte]) -> ctypes.Array[ctypes.c_ubyte]: ... +@overload +def as_ctypes(obj: NDArray[ushort]) -> ctypes.Array[ctypes.c_ushort]: ... +@overload +def as_ctypes(obj: NDArray[uintc]) -> ctypes.Array[ctypes.c_uint]: ... +@overload +def as_ctypes(obj: NDArray[uint]) -> ctypes.Array[ctypes.c_ulong]: ... +@overload +def as_ctypes(obj: NDArray[ulonglong]) -> ctypes.Array[ctypes.c_ulonglong]: ... +@overload +def as_ctypes(obj: NDArray[single]) -> ctypes.Array[ctypes.c_float]: ... +@overload +def as_ctypes(obj: NDArray[double]) -> ctypes.Array[ctypes.c_double]: ... +@overload +def as_ctypes(obj: NDArray[longdouble]) -> ctypes.Array[ctypes.c_longdouble]: ... +@overload +def as_ctypes(obj: NDArray[void]) -> ctypes.Array[Any]: ... # `ctypes.Union` or `ctypes.Structure` diff --git a/numpy/distutils/__init__.py b/numpy/distutils/__init__.py index b794bebd7cf7..79974d1c220a 100644 --- a/numpy/distutils/__init__.py +++ b/numpy/distutils/__init__.py @@ -1,14 +1,29 @@ -from __future__ import division, absolute_import, print_function +""" +An enhanced distutils, providing support for Fortran compilers, for BLAS, +LAPACK and other common libraries for numerical computing, and more. -import sys +Public submodules are:: + + misc_util + system_info + cpu_info + log + exec_command + +For details, please see the *Packaging* and *NumPy Distutils User Guide* +sections of the NumPy Reference Guide. + +For configuring the preference for and location of libraries like BLAS and +LAPACK, and for setting include paths and similar build options, please see +``site.cfg.example`` in the root of the NumPy repository or sdist. + +""" -from .__version__ import version as __version__ # Must import local ccompiler ASAP in order to get # customized CCompiler.spawn effective. from . import ccompiler from . import unixccompiler -from .info import __doc__ from .npy_pkg_config import * # If numpy is installed, add distutils.test() @@ -17,7 +32,7 @@ # Normally numpy is installed if the above import works, but an interrupted # in-place build could also have left a __config__.py. In that case the # next import may still fail, so keep it inside the try block. - from numpy.testing._private.pytesttester import PytestTester + from numpy._pytesttester import PytestTester test = PytestTester(__name__) del PytestTester except ImportError: @@ -30,7 +45,7 @@ def customized_fcompiler(plat=None, compiler=None): c.customize() return c -def customized_ccompiler(plat=None, compiler=None): - c = ccompiler.new_compiler(plat=plat, compiler=compiler) +def customized_ccompiler(plat=None, compiler=None, verbose=1): + c = ccompiler.new_compiler(plat=plat, compiler=compiler, verbose=verbose) c.customize('') return c diff --git a/numpy/distutils/__init__.pyi b/numpy/distutils/__init__.pyi new file mode 100644 index 000000000000..3938d68de14c --- /dev/null +++ b/numpy/distutils/__init__.pyi @@ -0,0 +1,4 @@ +from typing import Any + +# TODO: remove when the full numpy namespace is defined +def __getattr__(name: str) -> Any: ... diff --git a/numpy/distutils/__version__.py b/numpy/distutils/__version__.py deleted file mode 100644 index 969decbba20e..000000000000 --- a/numpy/distutils/__version__.py +++ /dev/null @@ -1,6 +0,0 @@ -from __future__ import division, absolute_import, print_function - -major = 0 -minor = 4 -micro = 0 -version = '%(major)d.%(minor)d.%(micro)d' % (locals()) diff --git a/numpy/distutils/_shell_utils.py b/numpy/distutils/_shell_utils.py new file mode 100644 index 000000000000..82abd5f4e0fe --- /dev/null +++ b/numpy/distutils/_shell_utils.py @@ -0,0 +1,91 @@ +""" +Helper functions for interacting with the shell, and consuming shell-style +parameters provided in config files. +""" +import os +import shlex +import subprocess +try: + from shlex import quote +except ImportError: + from pipes import quote + +__all__ = ['WindowsParser', 'PosixParser', 'NativeParser'] + + +class CommandLineParser: + """ + An object that knows how to split and join command-line arguments. + + It must be true that ``argv == split(join(argv))`` for all ``argv``. + The reverse neednt be true - `join(split(cmd))` may result in the addition + or removal of unnecessary escaping. + """ + @staticmethod + def join(argv): + """ Join a list of arguments into a command line string """ + raise NotImplementedError + + @staticmethod + def split(cmd): + """ Split a command line string into a list of arguments """ + raise NotImplementedError + + +class WindowsParser: + """ + The parsing behavior used by `subprocess.call("string")` on Windows, which + matches the Microsoft C/C++ runtime. + + Note that this is _not_ the behavior of cmd. + """ + @staticmethod + def join(argv): + # note that list2cmdline is specific to the windows syntax + return subprocess.list2cmdline(argv) + + @staticmethod + def split(cmd): + import ctypes # guarded import for systems without ctypes + try: + ctypes.windll + except AttributeError: + raise NotImplementedError + + # Windows has special parsing rules for the executable (no quotes), + # that we do not care about - insert a dummy element + if not cmd: + return [] + cmd = 'dummy ' + cmd + + CommandLineToArgvW = ctypes.windll.shell32.CommandLineToArgvW + CommandLineToArgvW.restype = ctypes.POINTER(ctypes.c_wchar_p) + CommandLineToArgvW.argtypes = (ctypes.c_wchar_p, ctypes.POINTER(ctypes.c_int)) + + nargs = ctypes.c_int() + lpargs = CommandLineToArgvW(cmd, ctypes.byref(nargs)) + args = [lpargs[i] for i in range(nargs.value)] + assert not ctypes.windll.kernel32.LocalFree(lpargs) + + # strip the element we inserted + assert args[0] == "dummy" + return args[1:] + + +class PosixParser: + """ + The parsing behavior used by `subprocess.call("string", shell=True)` on Posix. + """ + @staticmethod + def join(argv): + return ' '.join(quote(arg) for arg in argv) + + @staticmethod + def split(cmd): + return shlex.split(cmd, posix=True) + + +if os.name == 'nt': + NativeParser = WindowsParser +elif os.name == 'posix': + NativeParser = PosixParser diff --git a/numpy/distutils/armccompiler.py b/numpy/distutils/armccompiler.py new file mode 100644 index 000000000000..968504c7b4c1 --- /dev/null +++ b/numpy/distutils/armccompiler.py @@ -0,0 +1,28 @@ +from __future__ import division, absolute_import, print_function + +from distutils.unixccompiler import UnixCCompiler + +class ArmCCompiler(UnixCCompiler): + + """ + Arm compiler. + """ + + compiler_type = 'arm' + cc_exe = 'armclang' + cxx_exe = 'armclang++' + + def __init__(self, verbose=0, dry_run=0, force=0): + UnixCCompiler.__init__(self, verbose, dry_run, force) + cc_compiler = self.cc_exe + cxx_compiler = self.cxx_exe + self.set_executables(compiler=cc_compiler + + ' -O3 -fPIC', + compiler_so=cc_compiler + + ' -O3 -fPIC', + compiler_cxx=cxx_compiler + + ' -O3 -fPIC', + linker_exe=cc_compiler + + ' -lamath', + linker_so=cc_compiler + + ' -lamath -shared') diff --git a/numpy/distutils/ccompiler.py b/numpy/distutils/ccompiler.py index b03fb96b2851..16f00d8edf17 100644 --- a/numpy/distutils/ccompiler.py +++ b/numpy/distutils/ccompiler.py @@ -1,31 +1,34 @@ -from __future__ import division, absolute_import, print_function - import os import re import sys -import types import shlex import time +import subprocess from copy import copy from distutils import ccompiler -from distutils.ccompiler import * -from distutils.errors import DistutilsExecError, DistutilsModuleError, \ - DistutilsPlatformError, CompileError +from distutils.ccompiler import ( + compiler_class, gen_lib_options, get_default_compiler, new_compiler, + CCompiler +) +from distutils.errors import ( + DistutilsExecError, DistutilsModuleError, DistutilsPlatformError, + CompileError, UnknownFileError +) from distutils.sysconfig import customize_compiler from distutils.version import LooseVersion from numpy.distutils import log -from numpy.distutils.compat import get_exception -from numpy.distutils.exec_command import exec_command +from numpy.distutils.exec_command import ( + filepath_from_subprocess_output, forward_bytes_to_stdout +) from numpy.distutils.misc_util import cyg2win32, is_sequence, mingw32, \ - quote_args, get_num_build_jobs, \ - _commandline_dep_string + get_num_build_jobs, \ + _commandline_dep_string, \ + sanitize_cxx_flags # globals for parallel build management -try: - import threading -except ImportError: - import dummy_threading as threading +import threading + _job_semaphore = None _global_lock = threading.Lock() _processing_files = set() @@ -82,11 +85,8 @@ def _needs_build(obj, cc_args, extra_postargs, pp_opts): def replace_method(klass, method_name, func): - if sys.version_info[0] < 3: - m = types.MethodType(func, None, klass) - else: - # Py3k does not have unbound method anymore, MethodType does not work - m = lambda self, *args, **kw: func(self, *args, **kw) + # Py3k does not have unbound method anymore, MethodType does not work + m = lambda self, *args, **kw: func(self, *args, **kw) setattr(klass, method_name, m) @@ -109,7 +109,7 @@ class where more documentation can be found. # Using customized CCompiler.spawn. -def CCompiler_spawn(self, cmd, display=None): +def CCompiler_spawn(self, cmd, display=None, env=None): """ Execute a command in a sub-process. @@ -120,6 +120,7 @@ def CCompiler_spawn(self, cmd, display=None): display : str or sequence of str, optional The text to add to the log file kept by `numpy.distutils`. If not given, `display` is equal to `cmd`. + env: a dictionary for environment variables, optional Returns ------- @@ -131,25 +132,53 @@ def CCompiler_spawn(self, cmd, display=None): If the command failed, i.e. the exit status was not 0. """ + env = env if env is not None else dict(os.environ) if display is None: display = cmd if is_sequence(display): display = ' '.join(list(display)) log.info(display) - s, o = exec_command(cmd) - if s: - if is_sequence(cmd): - cmd = ' '.join(list(cmd)) - try: - print(o) - except UnicodeError: - # When installing through pip, `o` can contain non-ascii chars - pass - if re.search('Too many open files', o): - msg = '\nTry rerunning setup command until build succeeds.' + try: + if self.verbose: + subprocess.check_output(cmd, env=env) else: - msg = '' - raise DistutilsExecError('Command "%s" failed with exit status %d%s' % (cmd, s, msg)) + subprocess.check_output(cmd, stderr=subprocess.STDOUT, env=env) + except subprocess.CalledProcessError as exc: + o = exc.output + s = exc.returncode + except OSError as e: + # OSError doesn't have the same hooks for the exception + # output, but exec_command() historically would use an + # empty string for EnvironmentError (base class for + # OSError) + # o = b'' + # still that would make the end-user lost in translation! + o = f"\n\n{e}\n\n\n" + try: + o = o.encode(sys.stdout.encoding) + except AttributeError: + o = o.encode('utf8') + # status previously used by exec_command() for parent + # of OSError + s = 127 + else: + # use a convenience return here so that any kind of + # caught exception will execute the default code after the + # try / except block, which handles various exceptions + return None + + if is_sequence(cmd): + cmd = ' '.join(list(cmd)) + + if self.verbose: + forward_bytes_to_stdout(o) + + if re.search(b'Too many open files', o): + msg = '\nTry rerunning setup command until build succeeds.' + else: + msg = '' + raise DistutilsExecError('Command "%s" failed with exit status %d%s' % + (cmd, s, msg)) replace_method(CCompiler, 'spawn', CCompiler_spawn) @@ -238,9 +267,6 @@ def CCompiler_compile(self, sources, output_dir=None, macros=None, If compilation fails. """ - # This method is effective only with Python >=2.3 distutils. - # Any changes here should be applied also to fcompiler.compile - # method to support pre Python 2.3 distutils. global _job_semaphore jobs = get_num_build_jobs() @@ -253,12 +279,8 @@ def CCompiler_compile(self, sources, output_dir=None, macros=None, if not sources: return [] - # FIXME:RELATIVE_IMPORT - if sys.version_info[0] < 3: - from .fcompiler import FCompiler, is_f_file, has_f90_header - else: - from numpy.distutils.fcompiler import (FCompiler, is_f_file, - has_f90_header) + from numpy.distutils.fcompiler import (FCompiler, is_f_file, + has_f90_header) if isinstance(self, FCompiler): display = [] for fc in ['f77', 'f90', 'fix']: @@ -368,6 +390,13 @@ def CCompiler_customize_cmd(self, cmd, ignore=()): """ log.info('customize %s using %s' % (self.__class__.__name__, cmd.__class__.__name__)) + + if hasattr(self, 'compiler') and 'clang' in self.compiler[0]: + # clang defaults to a non-strict floating error point model. + # Since NumPy and most Python libs give warnings for these, override: + self.compiler.append('-ftrapping-math') + self.compiler_so.append('-ftrapping-math') + def allow(attr): return getattr(cmd, attr, None) is not None and attr not in ignore @@ -404,10 +433,8 @@ def _compiler_to_string(compiler): v = getattr(compiler, key) mx = max(mx, len(key)) props.append((key, repr(v))) - lines = [] - format = '%-' + repr(mx+1) + 's = %s' - for prop in props: - lines.append(format % prop) + fmt = '%-' + repr(mx+1) + 's = %s' + lines = [fmt % prop for prop in props] return '\n'.join(lines) def CCompiler_show_customization(self): @@ -427,14 +454,6 @@ def CCompiler_show_customization(self): Printing is only done if the distutils log threshold is < 2. """ - if 0: - for attrname in ['include_dirs', 'define', 'undef', - 'libraries', 'library_dirs', - 'rpath', 'link_objects']: - attr = getattr(self, attrname, None) - if not attr: - continue - log.info("compiler '%s' is set to %s" % (attrname, attr)) try: self.get_version() except Exception: @@ -620,7 +639,21 @@ def matcher(version_string): version = m.group('version') return version - status, output = exec_command(version_cmd, use_tee=0) + try: + output = subprocess.check_output(version_cmd, stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as exc: + output = exc.output + status = exc.returncode + except OSError: + # match the historical returns for a parent + # exception class caught by exec_command() + status = 127 + output = b'' + else: + # output isn't actually a filepath but we do this + # for now to match previous distutils behavior + output = filepath_from_subprocess_output(output) + status = 0 version = None if status in ok_status: @@ -650,7 +683,9 @@ def CCompiler_cxx_compiler(self): return self cxx = copy(self) - cxx.compiler_so = [cxx.compiler_cxx[0]] + cxx.compiler_so[1:] + cxx.compiler_cxx = cxx.compiler_cxx + cxx.compiler_so = [cxx.compiler_cxx[0]] + \ + sanitize_cxx_flags(cxx.compiler_so[1:]) if sys.platform.startswith('aix') and 'ld_so_aix' in cxx.linker_so[0]: # AIX needs the ld_so_aix script included with Python cxx.linker_so = [cxx.linker_so[0], cxx.compiler_cxx[0]] \ @@ -673,6 +708,9 @@ def CCompiler_cxx_compiler(self): "Intel C Compiler for 64-bit applications on Windows") compiler_class['pathcc'] = ('pathccompiler', 'PathScaleCCompiler', "PathScale Compiler for SiCortex-based applications") +compiler_class['arm'] = ('armccompiler', 'ArmCCompiler', + "Arm C Compiler") + ccompiler._default_compilers += (('linux.*', 'intel'), ('linux.*', 'intele'), ('linux.*', 'intelem'), @@ -695,10 +733,12 @@ def CCompiler_cxx_compiler(self): _distutils_new_compiler = new_compiler def new_compiler (plat=None, compiler=None, - verbose=0, + verbose=None, dry_run=0, force=0): # Try first C compilers from numpy.distutils. + if verbose is None: + verbose = log.get_threshold() <= log.INFO if plat is None: plat = os.name try: @@ -713,15 +753,15 @@ def new_compiler (plat=None, module_name = "numpy.distutils." + module_name try: __import__ (module_name) - except ImportError: - msg = str(get_exception()) + except ImportError as e: + msg = str(e) log.info('%s in numpy.distutils; trying from distutils', str(msg)) module_name = module_name[6:] try: __import__(module_name) - except ImportError: - msg = str(get_exception()) + except ImportError as e: + msg = str(e) raise DistutilsModuleError("can't compile C/C++ code: unable to load module '%s'" % \ module_name) try: @@ -731,6 +771,7 @@ def new_compiler (plat=None, raise DistutilsModuleError(("can't compile C/C++ code: unable to find class '%s' " + "in module '%s'") % (class_name, module_name)) compiler = klass(None, dry_run, force) + compiler.verbose = verbose log.debug('new_compiler returns %s' % (klass)) return compiler @@ -738,8 +779,13 @@ def new_compiler (plat=None, _distutils_gen_lib_options = gen_lib_options def gen_lib_options(compiler, library_dirs, runtime_library_dirs, libraries): - library_dirs = quote_args(library_dirs) - runtime_library_dirs = quote_args(runtime_library_dirs) + # the version of this function provided by CPython allows the following + # to return lists, which are unpacked automatically: + # - compiler.runtime_library_dir_option + # our version extends the behavior to: + # - compiler.library_dir_option + # - compiler.library_option + # - compiler.find_library_file r = _distutils_gen_lib_options(compiler, library_dirs, runtime_library_dirs, libraries) lib_opts = [] @@ -759,68 +805,3 @@ def gen_lib_options(compiler, library_dirs, runtime_library_dirs, libraries): if _m is not None: setattr(_m, 'gen_lib_options', gen_lib_options) -_distutils_gen_preprocess_options = gen_preprocess_options -def gen_preprocess_options (macros, include_dirs): - include_dirs = quote_args(include_dirs) - return _distutils_gen_preprocess_options(macros, include_dirs) -ccompiler.gen_preprocess_options = gen_preprocess_options - -##Fix distutils.util.split_quoted: -# NOTE: I removed this fix in revision 4481 (see ticket #619), but it appears -# that removing this fix causes f2py problems on Windows XP (see ticket #723). -# Specifically, on WinXP when gfortran is installed in a directory path, which -# contains spaces, then f2py is unable to find it. -import string -_wordchars_re = re.compile(r'[^\\\'\"%s ]*' % string.whitespace) -_squote_re = re.compile(r"'(?:[^'\\]|\\.)*'") -_dquote_re = re.compile(r'"(?:[^"\\]|\\.)*"') -_has_white_re = re.compile(r'\s') -def split_quoted(s): - s = s.strip() - words = [] - pos = 0 - - while s: - m = _wordchars_re.match(s, pos) - end = m.end() - if end == len(s): - words.append(s[:end]) - break - - if s[end] in string.whitespace: # unescaped, unquoted whitespace: now - words.append(s[:end]) # we definitely have a word delimiter - s = s[end:].lstrip() - pos = 0 - - elif s[end] == '\\': # preserve whatever is being escaped; - # will become part of the current word - s = s[:end] + s[end+1:] - pos = end+1 - - else: - if s[end] == "'": # slurp singly-quoted string - m = _squote_re.match(s, end) - elif s[end] == '"': # slurp doubly-quoted string - m = _dquote_re.match(s, end) - else: - raise RuntimeError("this can't happen (bad char '%c')" % s[end]) - - if m is None: - raise ValueError("bad string (mismatched %s quotes?)" % s[end]) - - (beg, end) = m.span() - if _has_white_re.search(s[beg+1:end-1]): - s = s[:beg] + s[beg+1:end-1] + s[end:] - pos = m.end() - 2 - else: - # Keeping quotes when a quoted word does not contain - # white-space. XXX: send a patch to distutils - pos = m.end() - - if pos >= len(s): - words.append(s) - break - - return words -ccompiler.split_quoted = split_quoted -##Fix distutils.util.split_quoted: diff --git a/numpy/distutils/ccompiler_opt.py b/numpy/distutils/ccompiler_opt.py new file mode 100644 index 000000000000..b38e47c13a94 --- /dev/null +++ b/numpy/distutils/ccompiler_opt.py @@ -0,0 +1,2580 @@ +"""Provides the `CCompilerOpt` class, used for handling the CPU/hardware +optimization, starting from parsing the command arguments, to managing the +relation between the CPU baseline and dispatch-able features, +also generating the required C headers and ending with compiling +the sources with proper compiler's flags. + +`CCompilerOpt` doesn't provide runtime detection for the CPU features, +instead only focuses on the compiler side, but it creates abstract C headers +that can be used later for the final runtime dispatching process.""" + +import atexit +import inspect +import os +import pprint +import re +import subprocess +import textwrap + + +class _Config: + """An abstract class holds all configurable attributes of `CCompilerOpt`, + these class attributes can be used to change the default behavior + of `CCompilerOpt` in order to fit other requirements. + + Attributes + ---------- + conf_nocache : bool + Set True to disable memory and file cache. + Default is False. + + conf_noopt : bool + Set True to forces the optimization to be disabled, + in this case `CCompilerOpt` tends to generate all + expected headers in order to 'not' break the build. + Default is False. + + conf_cache_factors : list + Add extra factors to the primary caching factors. The caching factors + are utilized to determine if there are changes had happened that + requires to discard the cache and re-updating it. The primary factors + are the arguments of `CCompilerOpt` and `CCompiler`'s properties(type, flags, etc). + Default is list of two items, containing the time of last modification + of `ccompiler_opt` and value of attribute "conf_noopt" + + conf_tmp_path : str, + The path of temporary directory. Default is auto-created + temporary directory via ``tempfile.mkdtemp()``. + + conf_check_path : str + The path of testing files. Each added CPU feature must have a + **C** source file contains at least one intrinsic or instruction that + related to this feature, so it can be tested against the compiler. + Default is ``./distutils/checks``. + + conf_target_groups : dict + Extra tokens that can be reached from dispatch-able sources through + the special mark ``@targets``. Default is an empty dictionary. + + **Notes**: + - case-insensitive for tokens and group names + - sign '#' must stick in the begin of group name and only within ``@targets`` + + **Example**: + .. code-block:: console + + $ "@targets #avx_group other_tokens" > group_inside.c + + >>> CCompilerOpt.conf_target_groups["avx_group"] = \\ + "$werror $maxopt avx2 avx512f avx512_skx" + >>> cco = CCompilerOpt(cc_instance) + >>> cco.try_dispatch(["group_inside.c"]) + + conf_c_prefix : str + The prefix of public C definitions. Default is ``"NPY_"``. + + conf_c_prefix_ : str + The prefix of internal C definitions. Default is ``"NPY__"``. + + conf_cc_flags : dict + Nested dictionaries defining several compiler flags + that linked to some major functions, the main key + represent the compiler name and sub-keys represent + flags names. Default is already covers all supported + **C** compilers. + + Sub-keys explained as follows: + + "native": str or None + used by argument option `native`, to detect the current + machine support via the compiler. + "werror": str or None + utilized to treat warning as errors during testing CPU features + against the compiler and also for target's policy `$werror` + via dispatch-able sources. + "maxopt": str or None + utilized for target's policy '$maxopt' and the value should + contains the maximum acceptable optimization by the compiler. + e.g. in gcc `'-O3'` + + **Notes**: + * case-sensitive for compiler names and flags + * use space to separate multiple flags + * any flag will tested against the compiler and it will skipped + if it's not applicable. + + conf_min_features : dict + A dictionary defines the used CPU features for + argument option `'min'`, the key represent the CPU architecture + name e.g. `'x86'`. Default values provide the best effort + on wide range of users platforms. + + **Note**: case-sensitive for architecture names. + + conf_features : dict + Nested dictionaries used for identifying the CPU features. + the primary key is represented as a feature name or group name + that gathers several features. Default values covers all + supported features but without the major options like "flags", + these undefined options handle it by method `conf_features_partial()`. + Default value is covers almost all CPU features for *X86*, *IBM/Power64* + and *ARM 7/8*. + + Sub-keys explained as follows: + + "implies" : str or list, optional, + List of CPU feature names to be implied by it, + the feature name must be defined within `conf_features`. + Default is None. + + "flags": str or list, optional + List of compiler flags. Default is None. + + "detect": str or list, optional + List of CPU feature names that required to be detected + in runtime. By default, its the feature name or features + in "group" if its specified. + + "implies_detect": bool, optional + If True, all "detect" of implied features will be combined. + Default is True. see `feature_detect()`. + + "group": str or list, optional + Same as "implies" but doesn't require the feature name to be + defined within `conf_features`. + + "interest": int, required + a key for sorting CPU features + + "headers": str or list, optional + intrinsics C header file + + "disable": str, optional + force disable feature, the string value should contains the + reason of disabling. + + "autovec": bool or None, optional + True or False to declare that CPU feature can be auto-vectorized + by the compiler. + By default(None), treated as True if the feature contains at + least one applicable flag. see `feature_can_autovec()` + + "extra_checks": str or list, optional + Extra test case names for the CPU feature that need to be tested + against the compiler. + + Each test case must have a C file named ``extra_xxxx.c``, where + ``xxxx`` is the case name in lower case, under 'conf_check_path'. + It should contain at least one intrinsic or function related to the test case. + + If the compiler able to successfully compile the C file then `CCompilerOpt` + will add a C ``#define`` for it into the main dispatch header, e.g. + ```#define {conf_c_prefix}_XXXX`` where ``XXXX`` is the case name in upper case. + + **NOTES**: + * space can be used as separator with options that supports "str or list" + * case-sensitive for all values and feature name must be in upper-case. + * if flags aren't applicable, its will skipped rather than disable the + CPU feature + * the CPU feature will disabled if the compiler fail to compile + the test file + """ + conf_nocache = False + conf_noopt = False + conf_cache_factors = None + conf_tmp_path = None + conf_check_path = os.path.join( + os.path.dirname(os.path.realpath(__file__)), "checks" + ) + conf_target_groups = {} + conf_c_prefix = 'NPY_' + conf_c_prefix_ = 'NPY__' + conf_cc_flags = dict( + gcc = dict( + # native should always fail on arm and ppc64, + # native usually works only with x86 + native = '-march=native', + opt = '-O3', + werror = '-Werror', + ), + clang = dict( + native = '-march=native', + opt = "-O3", + # One of the following flags needs to be applicable for Clang to + # guarantee the sanity of the testing process, however in certain + # cases `-Werror` gets skipped during the availability test due to + # "unused arguments" warnings. + # see https://github.com/numpy/numpy/issues/19624 + werror = '-Werror=switch -Werror', + ), + icc = dict( + native = '-xHost', + opt = '-O3', + werror = '-Werror', + ), + iccw = dict( + native = '/QxHost', + opt = '/O3', + werror = '/Werror', + ), + msvc = dict( + native = None, + opt = '/O2', + werror = '/WX', + ) + ) + conf_min_features = dict( + x86 = "SSE SSE2", + x64 = "SSE SSE2 SSE3", + ppc64 = '', # play it safe + ppc64le = "VSX VSX2", + armhf = '', # play it safe + aarch64 = "NEON NEON_FP16 NEON_VFPV4 ASIMD" + ) + conf_features = dict( + # X86 + SSE = dict( + interest=1, headers="xmmintrin.h", + # enabling SSE without SSE2 is useless also + # it's non-optional for x86_64 + implies="SSE2" + ), + SSE2 = dict(interest=2, implies="SSE", headers="emmintrin.h"), + SSE3 = dict(interest=3, implies="SSE2", headers="pmmintrin.h"), + SSSE3 = dict(interest=4, implies="SSE3", headers="tmmintrin.h"), + SSE41 = dict(interest=5, implies="SSSE3", headers="smmintrin.h"), + POPCNT = dict(interest=6, implies="SSE41", headers="popcntintrin.h"), + SSE42 = dict(interest=7, implies="POPCNT"), + AVX = dict( + interest=8, implies="SSE42", headers="immintrin.h", + implies_detect=False + ), + XOP = dict(interest=9, implies="AVX", headers="x86intrin.h"), + FMA4 = dict(interest=10, implies="AVX", headers="x86intrin.h"), + F16C = dict(interest=11, implies="AVX"), + FMA3 = dict(interest=12, implies="F16C"), + AVX2 = dict(interest=13, implies="F16C"), + AVX512F = dict( + interest=20, implies="FMA3 AVX2", implies_detect=False, + extra_checks="AVX512F_REDUCE" + ), + AVX512CD = dict(interest=21, implies="AVX512F"), + AVX512_KNL = dict( + interest=40, implies="AVX512CD", group="AVX512ER AVX512PF", + detect="AVX512_KNL", implies_detect=False + ), + AVX512_KNM = dict( + interest=41, implies="AVX512_KNL", + group="AVX5124FMAPS AVX5124VNNIW AVX512VPOPCNTDQ", + detect="AVX512_KNM", implies_detect=False + ), + AVX512_SKX = dict( + interest=42, implies="AVX512CD", group="AVX512VL AVX512BW AVX512DQ", + detect="AVX512_SKX", implies_detect=False, + extra_checks="AVX512BW_MASK AVX512DQ_MASK" + ), + AVX512_CLX = dict( + interest=43, implies="AVX512_SKX", group="AVX512VNNI", + detect="AVX512_CLX" + ), + AVX512_CNL = dict( + interest=44, implies="AVX512_SKX", group="AVX512IFMA AVX512VBMI", + detect="AVX512_CNL", implies_detect=False + ), + AVX512_ICL = dict( + interest=45, implies="AVX512_CLX AVX512_CNL", + group="AVX512VBMI2 AVX512BITALG AVX512VPOPCNTDQ", + detect="AVX512_ICL", implies_detect=False + ), + # IBM/Power + ## Power7/ISA 2.06 + VSX = dict(interest=1, headers="altivec.h", extra_checks="VSX_ASM"), + ## Power8/ISA 2.07 + VSX2 = dict(interest=2, implies="VSX", implies_detect=False), + ## Power9/ISA 3.00 + VSX3 = dict(interest=3, implies="VSX2", implies_detect=False), + # ARM + NEON = dict(interest=1, headers="arm_neon.h"), + NEON_FP16 = dict(interest=2, implies="NEON"), + ## FMA + NEON_VFPV4 = dict(interest=3, implies="NEON_FP16"), + ## Advanced SIMD + ASIMD = dict(interest=4, implies="NEON_FP16 NEON_VFPV4", implies_detect=False), + ## ARMv8.2 half-precision & vector arithm + ASIMDHP = dict(interest=5, implies="ASIMD"), + ## ARMv8.2 dot product + ASIMDDP = dict(interest=6, implies="ASIMD"), + ## ARMv8.2 Single & half-precision Multiply + ASIMDFHM = dict(interest=7, implies="ASIMDHP"), + ) + def conf_features_partial(self): + """Return a dictionary of supported CPU features by the platform, + and accumulate the rest of undefined options in `conf_features`, + the returned dict has same rules and notes in + class attribute `conf_features`, also its override + any options that been set in 'conf_features'. + """ + if self.cc_noopt: + # optimization is disabled + return {} + + on_x86 = self.cc_on_x86 or self.cc_on_x64 + is_unix = self.cc_is_gcc or self.cc_is_clang + + if on_x86 and is_unix: return dict( + SSE = dict(flags="-msse"), + SSE2 = dict(flags="-msse2"), + SSE3 = dict(flags="-msse3"), + SSSE3 = dict(flags="-mssse3"), + SSE41 = dict(flags="-msse4.1"), + POPCNT = dict(flags="-mpopcnt"), + SSE42 = dict(flags="-msse4.2"), + AVX = dict(flags="-mavx"), + F16C = dict(flags="-mf16c"), + XOP = dict(flags="-mxop"), + FMA4 = dict(flags="-mfma4"), + FMA3 = dict(flags="-mfma"), + AVX2 = dict(flags="-mavx2"), + AVX512F = dict(flags="-mavx512f"), + AVX512CD = dict(flags="-mavx512cd"), + AVX512_KNL = dict(flags="-mavx512er -mavx512pf"), + AVX512_KNM = dict( + flags="-mavx5124fmaps -mavx5124vnniw -mavx512vpopcntdq" + ), + AVX512_SKX = dict(flags="-mavx512vl -mavx512bw -mavx512dq"), + AVX512_CLX = dict(flags="-mavx512vnni"), + AVX512_CNL = dict(flags="-mavx512ifma -mavx512vbmi"), + AVX512_ICL = dict( + flags="-mavx512vbmi2 -mavx512bitalg -mavx512vpopcntdq" + ) + ) + if on_x86 and self.cc_is_icc: return dict( + SSE = dict(flags="-msse"), + SSE2 = dict(flags="-msse2"), + SSE3 = dict(flags="-msse3"), + SSSE3 = dict(flags="-mssse3"), + SSE41 = dict(flags="-msse4.1"), + POPCNT = {}, + SSE42 = dict(flags="-msse4.2"), + AVX = dict(flags="-mavx"), + F16C = {}, + XOP = dict(disable="Intel Compiler doesn't support it"), + FMA4 = dict(disable="Intel Compiler doesn't support it"), + # Intel Compiler doesn't support AVX2 or FMA3 independently + FMA3 = dict( + implies="F16C AVX2", flags="-march=core-avx2" + ), + AVX2 = dict(implies="FMA3", flags="-march=core-avx2"), + # Intel Compiler doesn't support AVX512F or AVX512CD independently + AVX512F = dict( + implies="AVX2 AVX512CD", flags="-march=common-avx512" + ), + AVX512CD = dict( + implies="AVX2 AVX512F", flags="-march=common-avx512" + ), + AVX512_KNL = dict(flags="-xKNL"), + AVX512_KNM = dict(flags="-xKNM"), + AVX512_SKX = dict(flags="-xSKYLAKE-AVX512"), + AVX512_CLX = dict(flags="-xCASCADELAKE"), + AVX512_CNL = dict(flags="-xCANNONLAKE"), + AVX512_ICL = dict(flags="-xICELAKE-CLIENT"), + ) + if on_x86 and self.cc_is_iccw: return dict( + SSE = dict(flags="/arch:SSE"), + SSE2 = dict(flags="/arch:SSE2"), + SSE3 = dict(flags="/arch:SSE3"), + SSSE3 = dict(flags="/arch:SSSE3"), + SSE41 = dict(flags="/arch:SSE4.1"), + POPCNT = {}, + SSE42 = dict(flags="/arch:SSE4.2"), + AVX = dict(flags="/arch:AVX"), + F16C = {}, + XOP = dict(disable="Intel Compiler doesn't support it"), + FMA4 = dict(disable="Intel Compiler doesn't support it"), + # Intel Compiler doesn't support FMA3 or AVX2 independently + FMA3 = dict( + implies="F16C AVX2", flags="/arch:CORE-AVX2" + ), + AVX2 = dict( + implies="FMA3", flags="/arch:CORE-AVX2" + ), + # Intel Compiler doesn't support AVX512F or AVX512CD independently + AVX512F = dict( + implies="AVX2 AVX512CD", flags="/Qx:COMMON-AVX512" + ), + AVX512CD = dict( + implies="AVX2 AVX512F", flags="/Qx:COMMON-AVX512" + ), + AVX512_KNL = dict(flags="/Qx:KNL"), + AVX512_KNM = dict(flags="/Qx:KNM"), + AVX512_SKX = dict(flags="/Qx:SKYLAKE-AVX512"), + AVX512_CLX = dict(flags="/Qx:CASCADELAKE"), + AVX512_CNL = dict(flags="/Qx:CANNONLAKE"), + AVX512_ICL = dict(flags="/Qx:ICELAKE-CLIENT") + ) + if on_x86 and self.cc_is_msvc: return dict( + SSE = dict(flags="/arch:SSE") if self.cc_on_x86 else {}, + SSE2 = dict(flags="/arch:SSE2") if self.cc_on_x86 else {}, + SSE3 = {}, + SSSE3 = {}, + SSE41 = {}, + POPCNT = dict(headers="nmmintrin.h"), + SSE42 = {}, + AVX = dict(flags="/arch:AVX"), + F16C = {}, + XOP = dict(headers="ammintrin.h"), + FMA4 = dict(headers="ammintrin.h"), + # MSVC doesn't support FMA3 or AVX2 independently + FMA3 = dict( + implies="F16C AVX2", flags="/arch:AVX2" + ), + AVX2 = dict( + implies="F16C FMA3", flags="/arch:AVX2" + ), + # MSVC doesn't support AVX512F or AVX512CD independently, + # always generate instructions belong to (VL/VW/DQ) + AVX512F = dict( + implies="AVX2 AVX512CD AVX512_SKX", flags="/arch:AVX512" + ), + AVX512CD = dict( + implies="AVX512F AVX512_SKX", flags="/arch:AVX512" + ), + AVX512_KNL = dict( + disable="MSVC compiler doesn't support it" + ), + AVX512_KNM = dict( + disable="MSVC compiler doesn't support it" + ), + AVX512_SKX = dict(flags="/arch:AVX512"), + AVX512_CLX = {}, + AVX512_CNL = {}, + AVX512_ICL = {} + ) + + on_power = self.cc_on_ppc64le or self.cc_on_ppc64 + if on_power: + partial = dict( + VSX = dict( + implies=("VSX2" if self.cc_on_ppc64le else ""), + flags="-mvsx" + ), + VSX2 = dict( + flags="-mcpu=power8", implies_detect=False + ), + VSX3 = dict( + flags="-mcpu=power9 -mtune=power9", implies_detect=False + ) + ) + if self.cc_is_clang: + partial["VSX"]["flags"] = "-maltivec -mvsx" + partial["VSX2"]["flags"] = "-mpower8-vector" + partial["VSX3"]["flags"] = "-mpower9-vector" + + return partial + + if self.cc_on_aarch64 and is_unix: return dict( + NEON = dict( + implies="NEON_FP16 NEON_VFPV4 ASIMD", autovec=True + ), + NEON_FP16 = dict( + implies="NEON NEON_VFPV4 ASIMD", autovec=True + ), + NEON_VFPV4 = dict( + implies="NEON NEON_FP16 ASIMD", autovec=True + ), + ASIMD = dict( + implies="NEON NEON_FP16 NEON_VFPV4", autovec=True + ), + ASIMDHP = dict( + flags="-march=armv8.2-a+fp16" + ), + ASIMDDP = dict( + flags="-march=armv8.2-a+dotprod" + ), + ASIMDFHM = dict( + flags="-march=armv8.2-a+fp16fml" + ), + ) + if self.cc_on_armhf and is_unix: return dict( + NEON = dict( + flags="-mfpu=neon" + ), + NEON_FP16 = dict( + flags="-mfpu=neon-fp16 -mfp16-format=ieee" + ), + NEON_VFPV4 = dict( + flags="-mfpu=neon-vfpv4", + ), + ASIMD = dict( + flags="-mfpu=neon-fp-armv8 -march=armv8-a+simd", + ), + ASIMDHP = dict( + flags="-march=armv8.2-a+fp16" + ), + ASIMDDP = dict( + flags="-march=armv8.2-a+dotprod", + ), + ASIMDFHM = dict( + flags="-march=armv8.2-a+fp16fml" + ) + ) + # TODO: ARM MSVC + return {} + + def __init__(self): + if self.conf_tmp_path is None: + import shutil + import tempfile + tmp = tempfile.mkdtemp() + def rm_temp(): + try: + shutil.rmtree(tmp) + except OSError: + pass + atexit.register(rm_temp) + self.conf_tmp_path = tmp + + if self.conf_cache_factors is None: + self.conf_cache_factors = [ + os.path.getmtime(__file__), + self.conf_nocache + ] + +class _Distutils: + """A helper class that provides a collection of fundamental methods + implemented in a top of Python and NumPy Distutils. + + The idea behind this class is to gather all methods that it may + need to override in case of reuse 'CCompilerOpt' in environment + different than of what NumPy has. + + Parameters + ---------- + ccompiler : `CCompiler` + The generate instance that returned from `distutils.ccompiler.new_compiler()`. + """ + def __init__(self, ccompiler): + self._ccompiler = ccompiler + + def dist_compile(self, sources, flags, ccompiler=None, **kwargs): + """Wrap CCompiler.compile()""" + assert(isinstance(sources, list)) + assert(isinstance(flags, list)) + flags = kwargs.pop("extra_postargs", []) + flags + if not ccompiler: + ccompiler = self._ccompiler + + return ccompiler.compile(sources, extra_postargs=flags, **kwargs) + + def dist_test(self, source, flags, macros=[]): + """Return True if 'CCompiler.compile()' able to compile + a source file with certain flags. + """ + assert(isinstance(source, str)) + from distutils.errors import CompileError + cc = self._ccompiler; + bk_spawn = getattr(cc, 'spawn', None) + if bk_spawn: + cc_type = getattr(self._ccompiler, "compiler_type", "") + if cc_type in ("msvc",): + setattr(cc, 'spawn', self._dist_test_spawn_paths) + else: + setattr(cc, 'spawn', self._dist_test_spawn) + test = False + try: + self.dist_compile( + [source], flags, macros=macros, output_dir=self.conf_tmp_path + ) + test = True + except CompileError as e: + self.dist_log(str(e), stderr=True) + if bk_spawn: + setattr(cc, 'spawn', bk_spawn) + return test + + def dist_info(self): + """ + Return a tuple containing info about (platform, compiler, extra_args), + required by the abstract class '_CCompiler' for discovering the + platform environment. This is also used as a cache factor in order + to detect any changes happening from outside. + """ + if hasattr(self, "_dist_info"): + return self._dist_info + + cc_type = getattr(self._ccompiler, "compiler_type", '') + if cc_type in ("intelem", "intelemw"): + platform = "x86_64" + elif cc_type in ("intel", "intelw", "intele"): + platform = "x86" + else: + from distutils.util import get_platform + platform = get_platform() + + cc_info = getattr(self._ccompiler, "compiler", getattr(self._ccompiler, "compiler_so", '')) + if not cc_type or cc_type == "unix": + if hasattr(cc_info, "__iter__"): + compiler = cc_info[0] + else: + compiler = str(cc_info) + else: + compiler = cc_type + + if hasattr(cc_info, "__iter__") and len(cc_info) > 1: + extra_args = ' '.join(cc_info[1:]) + else: + extra_args = os.environ.get("CFLAGS", "") + extra_args += os.environ.get("CPPFLAGS", "") + + self._dist_info = (platform, compiler, extra_args) + return self._dist_info + + @staticmethod + def dist_error(*args): + """Raise a compiler error""" + from distutils.errors import CompileError + raise CompileError(_Distutils._dist_str(*args)) + + @staticmethod + def dist_fatal(*args): + """Raise a distutils error""" + from distutils.errors import DistutilsError + raise DistutilsError(_Distutils._dist_str(*args)) + + @staticmethod + def dist_log(*args, stderr=False): + """Print a console message""" + from numpy.distutils import log + out = _Distutils._dist_str(*args) + if stderr: + log.warn(out) + else: + log.info(out) + + @staticmethod + def dist_load_module(name, path): + """Load a module from file, required by the abstract class '_Cache'.""" + from .misc_util import exec_mod_from_location + try: + return exec_mod_from_location(name, path) + except Exception as e: + _Distutils.dist_log(e, stderr=True) + return None + + @staticmethod + def _dist_str(*args): + """Return a string to print by log and errors.""" + def to_str(arg): + if not isinstance(arg, str) and hasattr(arg, '__iter__'): + ret = [] + for a in arg: + ret.append(to_str(a)) + return '('+ ' '.join(ret) + ')' + return str(arg) + + stack = inspect.stack()[2] + start = "CCompilerOpt.%s[%d] : " % (stack.function, stack.lineno) + out = ' '.join([ + to_str(a) + for a in (*args,) + ]) + return start + out + + def _dist_test_spawn_paths(self, cmd, display=None): + """ + Fix msvc SDK ENV path same as distutils do + without it we get c1: fatal error C1356: unable to find mspdbcore.dll + """ + if not hasattr(self._ccompiler, "_paths"): + self._dist_test_spawn(cmd) + return + old_path = os.getenv("path") + try: + os.environ["path"] = self._ccompiler._paths + self._dist_test_spawn(cmd) + finally: + os.environ["path"] = old_path + + _dist_warn_regex = re.compile( + # intel and msvc compilers don't raise + # fatal errors when flags are wrong or unsupported + ".*(" + "warning D9002|" # msvc, it should be work with any language. + "invalid argument for option" # intel + ").*" + ) + @staticmethod + def _dist_test_spawn(cmd, display=None): + try: + o = subprocess.check_output(cmd, stderr=subprocess.STDOUT, + universal_newlines=True) + if o and re.match(_Distutils._dist_warn_regex, o): + _Distutils.dist_error( + "Flags in command", cmd ,"aren't supported by the compiler" + ", output -> \n%s" % o + ) + except subprocess.CalledProcessError as exc: + o = exc.output + s = exc.returncode + except OSError as e: + o = e + s = 127 + else: + return None + _Distutils.dist_error( + "Command", cmd, "failed with exit status %d output -> \n%s" % ( + s, o + )) + +_share_cache = {} +class _Cache: + """An abstract class handles caching functionality, provides two + levels of caching, in-memory by share instances attributes among + each other and by store attributes into files. + + **Note**: + any attributes that start with ``_`` or ``conf_`` will be ignored. + + Parameters + ---------- + cache_path: str or None + The path of cache file, if None then cache in file will disabled. + + *factors: + The caching factors that need to utilize next to `conf_cache_factors`. + + Attributes + ---------- + cache_private: set + Hold the attributes that need be skipped from "in-memory cache". + + cache_infile: bool + Utilized during initializing this class, to determine if the cache was able + to loaded from the specified cache path in 'cache_path'. + """ + + # skip attributes from cache + _cache_ignore = re.compile("^(_|conf_)") + + def __init__(self, cache_path=None, *factors): + self.cache_me = {} + self.cache_private = set() + self.cache_infile = False + self._cache_path = None + + if self.conf_nocache: + self.dist_log("cache is disabled by `Config`") + return + + self._cache_hash = self.cache_hash(*factors, *self.conf_cache_factors) + self._cache_path = cache_path + if cache_path: + if os.path.exists(cache_path): + self.dist_log("load cache from file ->", cache_path) + cache_mod = self.dist_load_module("cache", cache_path) + if not cache_mod: + self.dist_log( + "unable to load the cache file as a module", + stderr=True + ) + elif not hasattr(cache_mod, "hash") or \ + not hasattr(cache_mod, "data"): + self.dist_log("invalid cache file", stderr=True) + elif self._cache_hash == cache_mod.hash: + self.dist_log("hit the file cache") + for attr, val in cache_mod.data.items(): + setattr(self, attr, val) + self.cache_infile = True + else: + self.dist_log("miss the file cache") + + if not self.cache_infile: + other_cache = _share_cache.get(self._cache_hash) + if other_cache: + self.dist_log("hit the memory cache") + for attr, val in other_cache.__dict__.items(): + if attr in other_cache.cache_private or \ + re.match(self._cache_ignore, attr): + continue + setattr(self, attr, val) + + _share_cache[self._cache_hash] = self + atexit.register(self.cache_flush) + + def __del__(self): + for h, o in _share_cache.items(): + if o == self: + _share_cache.pop(h) + break + + def cache_flush(self): + """ + Force update the cache. + """ + if not self._cache_path: + return + # TODO: don't write if the cache doesn't change + self.dist_log("write cache to path ->", self._cache_path) + cdict = self.__dict__.copy() + for attr in self.__dict__.keys(): + if re.match(self._cache_ignore, attr): + cdict.pop(attr) + + d = os.path.dirname(self._cache_path) + if not os.path.exists(d): + os.makedirs(d) + + repr_dict = pprint.pformat(cdict, compact=True) + with open(self._cache_path, "w") as f: + f.write(textwrap.dedent("""\ + # AUTOGENERATED DON'T EDIT + # Please make changes to the code generator \ + (distutils/ccompiler_opt.py) + hash = {} + data = \\ + """).format(self._cache_hash)) + f.write(repr_dict) + + def cache_hash(self, *factors): + # is there a built-in non-crypto hash? + # sdbm + chash = 0 + for f in factors: + for char in str(f): + chash = ord(char) + (chash << 6) + (chash << 16) - chash + chash &= 0xFFFFFFFF + return chash + + @staticmethod + def me(cb): + """ + A static method that can be treated as a decorator to + dynamically cache certain methods. + """ + def cache_wrap_me(self, *args, **kwargs): + # good for normal args + cache_key = str(( + cb.__name__, *args, *kwargs.keys(), *kwargs.values() + )) + if cache_key in self.cache_me: + return self.cache_me[cache_key] + ccb = cb(self, *args, **kwargs) + self.cache_me[cache_key] = ccb + return ccb + return cache_wrap_me + +class _CCompiler: + """A helper class for `CCompilerOpt` containing all utilities that + related to the fundamental compiler's functions. + + Attributes + ---------- + cc_on_x86 : bool + True when the target architecture is 32-bit x86 + cc_on_x64 : bool + True when the target architecture is 64-bit x86 + cc_on_ppc64 : bool + True when the target architecture is 64-bit big-endian PowerPC + cc_on_armhf : bool + True when the target architecture is 32-bit ARMv7+ + cc_on_aarch64 : bool + True when the target architecture is 64-bit Armv8-a+ + cc_on_noarch : bool + True when the target architecture is unknown or not supported + cc_is_gcc : bool + True if the compiler is GNU or + if the compiler is unknown + cc_is_clang : bool + True if the compiler is Clang + cc_is_icc : bool + True if the compiler is Intel compiler (unix like) + cc_is_iccw : bool + True if the compiler is Intel compiler (msvc like) + cc_is_nocc : bool + True if the compiler isn't supported directly, + Note: that cause a fail-back to gcc + cc_has_debug : bool + True if the compiler has debug flags + cc_has_native : bool + True if the compiler has native flags + cc_noopt : bool + True if the compiler has definition 'DISABLE_OPT*', + or 'cc_on_noarch' is True + cc_march : str + The target architecture name, or "unknown" if + the architecture isn't supported + cc_name : str + The compiler name, or "unknown" if the compiler isn't supported + cc_flags : dict + Dictionary containing the initialized flags of `_Config.conf_cc_flags` + """ + def __init__(self): + if hasattr(self, "cc_is_cached"): + return + # attr regex + detect_arch = ( + ("cc_on_x64", ".*(x|x86_|amd)64.*"), + ("cc_on_x86", ".*(win32|x86|i386|i686).*"), + ("cc_on_ppc64le", ".*(powerpc|ppc)64(el|le).*"), + ("cc_on_ppc64", ".*(powerpc|ppc)64.*"), + ("cc_on_aarch64", ".*(aarch64|arm64).*"), + ("cc_on_armhf", ".*arm.*"), + # undefined platform + ("cc_on_noarch", ""), + ) + detect_compiler = ( + ("cc_is_gcc", r".*(gcc|gnu\-g).*"), + ("cc_is_clang", ".*clang.*"), + ("cc_is_iccw", ".*(intelw|intelemw|iccw).*"), # intel msvc like + ("cc_is_icc", ".*(intel|icc).*"), # intel unix like + ("cc_is_msvc", ".*msvc.*"), + # undefined compiler will be treat it as gcc + ("cc_is_nocc", ""), + ) + detect_args = ( + ("cc_has_debug", ".*(O0|Od|ggdb|coverage|debug:full).*"), + ("cc_has_native", ".*(-march=native|-xHost|/QxHost).*"), + # in case if the class run with -DNPY_DISABLE_OPTIMIZATION + ("cc_noopt", ".*DISABLE_OPT.*"), + ) + + dist_info = self.dist_info() + platform, compiler_info, extra_args = dist_info + # set False to all attrs + for section in (detect_arch, detect_compiler, detect_args): + for attr, rgex in section: + setattr(self, attr, False) + + for detect, searchin in ((detect_arch, platform), (detect_compiler, compiler_info)): + for attr, rgex in detect: + if rgex and not re.match(rgex, searchin, re.IGNORECASE): + continue + setattr(self, attr, True) + break + + for attr, rgex in detect_args: + if rgex and not re.match(rgex, extra_args, re.IGNORECASE): + continue + setattr(self, attr, True) + + if self.cc_on_noarch: + self.dist_log( + "unable to detect CPU architecture which lead to disable the optimization. " + f"check dist_info:<<\n{dist_info}\n>>", + stderr=True + ) + self.cc_noopt = True + + if self.conf_noopt: + self.dist_log("Optimization is disabled by the Config", stderr=True) + self.cc_noopt = True + + if self.cc_is_nocc: + """ + mingw can be treated as a gcc, and also xlc even if it based on clang, + but still has the same gcc optimization flags. + """ + self.dist_log( + "unable to detect compiler type which leads to treating it as GCC. " + "this is a normal behavior if you're using gcc-like compiler such as MinGW or IBM/XLC." + f"check dist_info:<<\n{dist_info}\n>>", + stderr=True + ) + self.cc_is_gcc = True + + self.cc_march = "unknown" + for arch in ("x86", "x64", "ppc64", "ppc64le", "armhf", "aarch64"): + if getattr(self, "cc_on_" + arch): + self.cc_march = arch + break + + self.cc_name = "unknown" + for name in ("gcc", "clang", "iccw", "icc", "msvc"): + if getattr(self, "cc_is_" + name): + self.cc_name = name + break + + self.cc_flags = {} + compiler_flags = self.conf_cc_flags.get(self.cc_name) + if compiler_flags is None: + self.dist_fatal( + "undefined flag for compiler '%s', " + "leave an empty dict instead" % self.cc_name + ) + for name, flags in compiler_flags.items(): + self.cc_flags[name] = nflags = [] + if flags: + assert(isinstance(flags, str)) + flags = flags.split() + for f in flags: + if self.cc_test_flags([f]): + nflags.append(f) + + self.cc_is_cached = True + + @_Cache.me + def cc_test_flags(self, flags): + """ + Returns True if the compiler supports 'flags'. + """ + assert(isinstance(flags, list)) + self.dist_log("testing flags", flags) + test_path = os.path.join(self.conf_check_path, "test_flags.c") + test = self.dist_test(test_path, flags) + if not test: + self.dist_log("testing failed", stderr=True) + return test + + def cc_normalize_flags(self, flags): + """ + Remove the conflicts that caused due gathering implied features flags. + + Parameters + ---------- + 'flags' list, compiler flags + flags should be sorted from the lowest to the highest interest. + + Returns + ------- + list, filtered from any conflicts. + + Examples + -------- + >>> self.cc_normalize_flags(['-march=armv8.2-a+fp16', '-march=armv8.2-a+dotprod']) + ['armv8.2-a+fp16+dotprod'] + + >>> self.cc_normalize_flags( + ['-msse', '-msse2', '-msse3', '-mssse3', '-msse4.1', '-msse4.2', '-mavx', '-march=core-avx2'] + ) + ['-march=core-avx2'] + """ + assert(isinstance(flags, list)) + if self.cc_is_gcc or self.cc_is_clang or self.cc_is_icc: + return self._cc_normalize_unix(flags) + + if self.cc_is_msvc or self.cc_is_iccw: + return self._cc_normalize_win(flags) + return flags + + _cc_normalize_unix_mrgx = re.compile( + # 1- to check the highest of + r"^(-mcpu=|-march=|-x[A-Z0-9\-])" + ) + _cc_normalize_unix_frgx = re.compile( + # 2- to remove any flags starts with + # -march, -mcpu, -x(INTEL) and '-m' without '=' + r"^(?!(-mcpu=|-march=|-x[A-Z0-9\-]))(?!-m[a-z0-9\-\.]*.$)" + ) + _cc_normalize_unix_krgx = re.compile( + # 3- keep only the highest of + r"^(-mfpu|-mtune)" + ) + _cc_normalize_arch_ver = re.compile( + r"[0-9.]" + ) + def _cc_normalize_unix(self, flags): + def ver_flags(f): + # arch ver subflag + # -march=armv8.2-a+fp16fml + tokens = f.split('+') + ver = float('0' + ''.join( + re.findall(self._cc_normalize_arch_ver, tokens[0]) + )) + return ver, tokens[0], tokens[1:] + + if len(flags) <= 1: + return flags + # get the highest matched flag + for i, cur_flag in enumerate(reversed(flags)): + if not re.match(self._cc_normalize_unix_mrgx, cur_flag): + continue + lower_flags = flags[:-(i+1)] + upper_flags = flags[-i:] + filterd = list(filter( + self._cc_normalize_unix_frgx.search, lower_flags + )) + # gather subflags + ver, arch, subflags = ver_flags(cur_flag) + if ver > 0 and len(subflags) > 0: + for xflag in lower_flags: + xver, _, xsubflags = ver_flags(xflag) + if ver == xver: + subflags = xsubflags + subflags + cur_flag = arch + '+' + '+'.join(subflags) + + flags = filterd + [cur_flag] + if i > 0: + flags += upper_flags + break + + # to remove overridable flags + final_flags = [] + matched = set() + for f in reversed(flags): + match = re.match(self._cc_normalize_unix_krgx, f) + if not match: + pass + elif match[0] in matched: + continue + else: + matched.add(match[0]) + final_flags.insert(0, f) + return final_flags + + _cc_normalize_win_frgx = re.compile( + r"^(?!(/arch\:|/Qx\:))" + ) + _cc_normalize_win_mrgx = re.compile( + r"^(/arch|/Qx:)" + ) + def _cc_normalize_win(self, flags): + for i, f in enumerate(reversed(flags)): + if not re.match(self._cc_normalize_win_mrgx, f): + continue + i += 1 + return list(filter( + self._cc_normalize_win_frgx.search, flags[:-i] + )) + flags[-i:] + return flags + +class _Feature: + """A helper class for `CCompilerOpt` that managing CPU features. + + Attributes + ---------- + feature_supported : dict + Dictionary containing all CPU features that supported + by the platform, according to the specified values in attribute + `_Config.conf_features` and `_Config.conf_features_partial()` + + feature_min : set + The minimum support of CPU features, according to + the specified values in attribute `_Config.conf_min_features`. + """ + def __init__(self): + if hasattr(self, "feature_is_cached"): + return + self.feature_supported = pfeatures = self.conf_features_partial() + for feature_name in list(pfeatures.keys()): + feature = pfeatures[feature_name] + cfeature = self.conf_features[feature_name] + feature.update({ + k:v for k,v in cfeature.items() if k not in feature + }) + disabled = feature.get("disable") + if disabled is not None: + pfeatures.pop(feature_name) + self.dist_log( + "feature '%s' is disabled," % feature_name, + disabled, stderr=True + ) + continue + # list is used internally for these options + for option in ( + "implies", "group", "detect", "headers", "flags", "extra_checks" + ) : + oval = feature.get(option) + if isinstance(oval, str): + feature[option] = oval.split() + + self.feature_min = set() + min_f = self.conf_min_features.get(self.cc_march, "") + for F in min_f.upper().split(): + if F in self.feature_supported: + self.feature_min.add(F) + + self.feature_is_cached = True + + def feature_names(self, names=None, force_flags=None, macros=[]): + """ + Returns a set of CPU feature names that supported by platform and the **C** compiler. + + Parameters + ---------- + names: sequence or None, optional + Specify certain CPU features to test it against the **C** compiler. + if None(default), it will test all current supported features. + **Note**: feature names must be in upper-case. + + force_flags: list or None, optional + If None(default), default compiler flags for every CPU feature will + be used during the test. + + macros : list of tuples, optional + A list of C macro definitions. + """ + assert( + names is None or ( + not isinstance(names, str) and + hasattr(names, "__iter__") + ) + ) + assert(force_flags is None or isinstance(force_flags, list)) + if names is None: + names = self.feature_supported.keys() + supported_names = set() + for f in names: + if self.feature_is_supported( + f, force_flags=force_flags, macros=macros + ): + supported_names.add(f) + return supported_names + + def feature_is_exist(self, name): + """ + Returns True if a certain feature is exist and covered within + `_Config.conf_features`. + + Parameters + ---------- + 'name': str + feature name in uppercase. + """ + assert(name.isupper()) + return name in self.conf_features + + def feature_sorted(self, names, reverse=False): + """ + Sort a list of CPU features ordered by the lowest interest. + + Parameters + ---------- + 'names': sequence + sequence of supported feature names in uppercase. + 'reverse': bool, optional + If true, the sorted features is reversed. (highest interest) + + Returns + ------- + list, sorted CPU features + """ + def sort_cb(k): + if isinstance(k, str): + return self.feature_supported[k]["interest"] + # multiple features + rank = max([self.feature_supported[f]["interest"] for f in k]) + # FIXME: that's not a safe way to increase the rank for + # multi targets + rank += len(k) -1 + return rank + return sorted(names, reverse=reverse, key=sort_cb) + + def feature_implies(self, names, keep_origins=False): + """ + Return a set of CPU features that implied by 'names' + + Parameters + ---------- + names: str or sequence of str + CPU feature name(s) in uppercase. + + keep_origins: bool + if False(default) then the returned set will not contain any + features from 'names'. This case happens only when two features + imply each other. + + Examples + -------- + >>> self.feature_implies("SSE3") + {'SSE', 'SSE2'} + >>> self.feature_implies("SSE2") + {'SSE'} + >>> self.feature_implies("SSE2", keep_origins=True) + # 'SSE2' found here since 'SSE' and 'SSE2' imply each other + {'SSE', 'SSE2'} + """ + def get_implies(name, _caller=set()): + implies = set() + d = self.feature_supported[name] + for i in d.get("implies", []): + implies.add(i) + if i in _caller: + # infinity recursive guard since + # features can imply each other + continue + _caller.add(name) + implies = implies.union(get_implies(i, _caller)) + return implies + + if isinstance(names, str): + implies = get_implies(names) + names = [names] + else: + assert(hasattr(names, "__iter__")) + implies = set() + for n in names: + implies = implies.union(get_implies(n)) + if not keep_origins: + implies.difference_update(names) + return implies + + def feature_implies_c(self, names): + """same as feature_implies() but combining 'names'""" + if isinstance(names, str): + names = set((names,)) + else: + names = set(names) + return names.union(self.feature_implies(names)) + + def feature_ahead(self, names): + """ + Return list of features in 'names' after remove any + implied features and keep the origins. + + Parameters + ---------- + 'names': sequence + sequence of CPU feature names in uppercase. + + Returns + ------- + list of CPU features sorted as-is 'names' + + Examples + -------- + >>> self.feature_ahead(["SSE2", "SSE3", "SSE41"]) + ["SSE41"] + # assume AVX2 and FMA3 implies each other and AVX2 + # is the highest interest + >>> self.feature_ahead(["SSE2", "SSE3", "SSE41", "AVX2", "FMA3"]) + ["AVX2"] + # assume AVX2 and FMA3 don't implies each other + >>> self.feature_ahead(["SSE2", "SSE3", "SSE41", "AVX2", "FMA3"]) + ["AVX2", "FMA3"] + """ + assert( + not isinstance(names, str) + and hasattr(names, '__iter__') + ) + implies = self.feature_implies(names, keep_origins=True) + ahead = [n for n in names if n not in implies] + if len(ahead) == 0: + # return the highest interested feature + # if all features imply each other + ahead = self.feature_sorted(names, reverse=True)[:1] + return ahead + + def feature_untied(self, names): + """ + same as 'feature_ahead()' but if both features implied each other + and keep the highest interest. + + Parameters + ---------- + 'names': sequence + sequence of CPU feature names in uppercase. + + Returns + ------- + list of CPU features sorted as-is 'names' + + Examples + -------- + >>> self.feature_untied(["SSE2", "SSE3", "SSE41"]) + ["SSE2", "SSE3", "SSE41"] + # assume AVX2 and FMA3 implies each other + >>> self.feature_untied(["SSE2", "SSE3", "SSE41", "FMA3", "AVX2"]) + ["SSE2", "SSE3", "SSE41", "AVX2"] + """ + assert( + not isinstance(names, str) + and hasattr(names, '__iter__') + ) + final = [] + for n in names: + implies = self.feature_implies(n) + tied = [ + nn for nn in final + if nn in implies and n in self.feature_implies(nn) + ] + if tied: + tied = self.feature_sorted(tied + [n]) + if n not in tied[1:]: + continue + final.remove(tied[:1][0]) + final.append(n) + return final + + def feature_get_til(self, names, keyisfalse): + """ + same as `feature_implies_c()` but stop collecting implied + features when feature's option that provided through + parameter 'keyisfalse' is False, also sorting the returned + features. + """ + def til(tnames): + # sort from highest to lowest interest then cut if "key" is False + tnames = self.feature_implies_c(tnames) + tnames = self.feature_sorted(tnames, reverse=True) + for i, n in enumerate(tnames): + if not self.feature_supported[n].get(keyisfalse, True): + tnames = tnames[:i+1] + break + return tnames + + if isinstance(names, str) or len(names) <= 1: + names = til(names) + # normalize the sort + names.reverse() + return names + + names = self.feature_ahead(names) + names = {t for n in names for t in til(n)} + return self.feature_sorted(names) + + def feature_detect(self, names): + """ + Return a list of CPU features that required to be detected + sorted from the lowest to highest interest. + """ + names = self.feature_get_til(names, "implies_detect") + detect = [] + for n in names: + d = self.feature_supported[n] + detect += d.get("detect", d.get("group", [n])) + return detect + + @_Cache.me + def feature_flags(self, names): + """ + Return a list of CPU features flags sorted from the lowest + to highest interest. + """ + names = self.feature_sorted(self.feature_implies_c(names)) + flags = [] + for n in names: + d = self.feature_supported[n] + f = d.get("flags", []) + if not f or not self.cc_test_flags(f): + continue + flags += f + return self.cc_normalize_flags(flags) + + @_Cache.me + def feature_test(self, name, force_flags=None, macros=[]): + """ + Test a certain CPU feature against the compiler through its own + check file. + + Parameters + ---------- + name: str + Supported CPU feature name. + + force_flags: list or None, optional + If None(default), the returned flags from `feature_flags()` + will be used. + + macros : list of tuples, optional + A list of C macro definitions. + """ + if force_flags is None: + force_flags = self.feature_flags(name) + + self.dist_log( + "testing feature '%s' with flags (%s)" % ( + name, ' '.join(force_flags) + )) + # Each CPU feature must have C source code contains at + # least one intrinsic or instruction related to this feature. + test_path = os.path.join( + self.conf_check_path, "cpu_%s.c" % name.lower() + ) + if not os.path.exists(test_path): + self.dist_fatal("feature test file is not exist", test_path) + + test = self.dist_test( + test_path, force_flags + self.cc_flags["werror"], macros=macros + ) + if not test: + self.dist_log("testing failed", stderr=True) + return test + + @_Cache.me + def feature_is_supported(self, name, force_flags=None, macros=[]): + """ + Check if a certain CPU feature is supported by the platform and compiler. + + Parameters + ---------- + name: str + CPU feature name in uppercase. + + force_flags: list or None, optional + If None(default), default compiler flags for every CPU feature will + be used during test. + + macros : list of tuples, optional + A list of C macro definitions. + """ + assert(name.isupper()) + assert(force_flags is None or isinstance(force_flags, list)) + + supported = name in self.feature_supported + if supported: + for impl in self.feature_implies(name): + if not self.feature_test(impl, force_flags, macros=macros): + return False + if not self.feature_test(name, force_flags, macros=macros): + return False + return supported + + @_Cache.me + def feature_can_autovec(self, name): + """ + check if the feature can be auto-vectorized by the compiler + """ + assert(isinstance(name, str)) + d = self.feature_supported[name] + can = d.get("autovec", None) + if can is None: + valid_flags = [ + self.cc_test_flags([f]) for f in d.get("flags", []) + ] + can = valid_flags and any(valid_flags) + return can + + @_Cache.me + def feature_extra_checks(self, name): + """ + Return a list of supported extra checks after testing them against + the compiler. + + Parameters + ---------- + names: str + CPU feature name in uppercase. + """ + assert isinstance(name, str) + d = self.feature_supported[name] + extra_checks = d.get("extra_checks", []) + if not extra_checks: + return [] + + self.dist_log("Testing extra checks for feature '%s'" % name, extra_checks) + flags = self.feature_flags(name) + available = [] + not_available = [] + for chk in extra_checks: + test_path = os.path.join( + self.conf_check_path, "extra_%s.c" % chk.lower() + ) + if not os.path.exists(test_path): + self.dist_fatal("extra check file does not exist", test_path) + + is_supported = self.dist_test(test_path, flags + self.cc_flags["werror"]) + if is_supported: + available.append(chk) + else: + not_available.append(chk) + + if not_available: + self.dist_log("testing failed for checks", not_available, stderr=True) + return available + + + def feature_c_preprocessor(self, feature_name, tabs=0): + """ + Generate C preprocessor definitions and include headers of a CPU feature. + + Parameters + ---------- + 'feature_name': str + CPU feature name in uppercase. + 'tabs': int + if > 0, align the generated strings to the right depend on number of tabs. + + Returns + ------- + str, generated C preprocessor + + Examples + -------- + >>> self.feature_c_preprocessor("SSE3") + /** SSE3 **/ + #define NPY_HAVE_SSE3 1 + #include <pmmintrin.h> + """ + assert(feature_name.isupper()) + feature = self.feature_supported.get(feature_name) + assert(feature is not None) + + prepr = [ + "/** %s **/" % feature_name, + "#define %sHAVE_%s 1" % (self.conf_c_prefix, feature_name) + ] + prepr += [ + "#include <%s>" % h for h in feature.get("headers", []) + ] + + extra_defs = feature.get("group", []) + extra_defs += self.feature_extra_checks(feature_name) + for edef in extra_defs: + # Guard extra definitions in case of duplicate with + # another feature + prepr += [ + "#ifndef %sHAVE_%s" % (self.conf_c_prefix, edef), + "\t#define %sHAVE_%s 1" % (self.conf_c_prefix, edef), + "#endif", + ] + + if tabs > 0: + prepr = [('\t'*tabs) + l for l in prepr] + return '\n'.join(prepr) + +class _Parse: + """A helper class that parsing main arguments of `CCompilerOpt`, + also parsing configuration statements in dispatch-able sources. + + Parameters + ---------- + cpu_baseline: str or None + minimal set of required CPU features or special options. + + cpu_dispatch: str or None + dispatched set of additional CPU features or special options. + + Special options can be: + - **MIN**: Enables the minimum CPU features that utilized via `_Config.conf_min_features` + - **MAX**: Enables all supported CPU features by the Compiler and platform. + - **NATIVE**: Enables all CPU features that supported by the current machine. + - **NONE**: Enables nothing + - **Operand +/-**: remove or add features, useful with options **MAX**, **MIN** and **NATIVE**. + NOTE: operand + is only added for nominal reason. + + NOTES: + - Case-insensitive among all CPU features and special options. + - Comma or space can be used as a separator. + - If the CPU feature is not supported by the user platform or compiler, + it will be skipped rather than raising a fatal error. + - Any specified CPU features to 'cpu_dispatch' will be skipped if its part of CPU baseline features + - 'cpu_baseline' force enables implied features. + + Attributes + ---------- + parse_baseline_names : list + Final CPU baseline's feature names(sorted from low to high) + parse_baseline_flags : list + Compiler flags of baseline features + parse_dispatch_names : list + Final CPU dispatch-able feature names(sorted from low to high) + parse_target_groups : dict + Dictionary containing initialized target groups that configured + through class attribute `conf_target_groups`. + + The key is represent the group name and value is a tuple + contains three items : + - bool, True if group has the 'baseline' option. + - list, list of CPU features. + - list, list of extra compiler flags. + + """ + def __init__(self, cpu_baseline, cpu_dispatch): + self._parse_policies = dict( + # POLICY NAME, (HAVE, NOT HAVE, [DEB]) + KEEP_BASELINE = ( + None, self._parse_policy_not_keepbase, + [] + ), + KEEP_SORT = ( + self._parse_policy_keepsort, + self._parse_policy_not_keepsort, + [] + ), + MAXOPT = ( + self._parse_policy_maxopt, None, + [] + ), + WERROR = ( + self._parse_policy_werror, None, + [] + ), + AUTOVEC = ( + self._parse_policy_autovec, None, + ["MAXOPT"] + ) + ) + if hasattr(self, "parse_is_cached"): + return + + self.parse_baseline_names = [] + self.parse_baseline_flags = [] + self.parse_dispatch_names = [] + self.parse_target_groups = {} + + if self.cc_noopt: + # skip parsing baseline and dispatch args and keep parsing target groups + cpu_baseline = cpu_dispatch = None + + self.dist_log("check requested baseline") + if cpu_baseline is not None: + cpu_baseline = self._parse_arg_features("cpu_baseline", cpu_baseline) + baseline_names = self.feature_names(cpu_baseline) + self.parse_baseline_flags = self.feature_flags(baseline_names) + self.parse_baseline_names = self.feature_sorted( + self.feature_implies_c(baseline_names) + ) + + self.dist_log("check requested dispatch-able features") + if cpu_dispatch is not None: + cpu_dispatch_ = self._parse_arg_features("cpu_dispatch", cpu_dispatch) + cpu_dispatch = { + f for f in cpu_dispatch_ + if f not in self.parse_baseline_names + } + conflict_baseline = cpu_dispatch_.difference(cpu_dispatch) + self.parse_dispatch_names = self.feature_sorted( + self.feature_names(cpu_dispatch) + ) + if len(conflict_baseline) > 0: + self.dist_log( + "skip features", conflict_baseline, "since its part of baseline" + ) + + self.dist_log("initialize targets groups") + for group_name, tokens in self.conf_target_groups.items(): + self.dist_log("parse target group", group_name) + GROUP_NAME = group_name.upper() + if not tokens or not tokens.strip(): + # allow empty groups, useful in case if there's a need + # to disable certain group since '_parse_target_tokens()' + # requires at least one valid target + self.parse_target_groups[GROUP_NAME] = ( + False, [], [] + ) + continue + has_baseline, features, extra_flags = \ + self._parse_target_tokens(tokens) + self.parse_target_groups[GROUP_NAME] = ( + has_baseline, features, extra_flags + ) + + self.parse_is_cached = True + + def parse_targets(self, source): + """ + Fetch and parse configuration statements that required for + defining the targeted CPU features, statements should be declared + in the top of source in between **C** comment and start + with a special mark **@targets**. + + Configuration statements are sort of keywords representing + CPU features names, group of statements and policies, combined + together to determine the required optimization. + + Parameters + ---------- + source: str + the path of **C** source file. + + Returns + ------- + - bool, True if group has the 'baseline' option + - list, list of CPU features + - list, list of extra compiler flags + """ + self.dist_log("looking for '@targets' inside -> ", source) + # get lines between /*@targets and */ + with open(source) as fd: + tokens = "" + max_to_reach = 1000 # good enough, isn't? + start_with = "@targets" + start_pos = -1 + end_with = "*/" + end_pos = -1 + for current_line, line in enumerate(fd): + if current_line == max_to_reach: + self.dist_fatal("reached the max of lines") + break + if start_pos == -1: + start_pos = line.find(start_with) + if start_pos == -1: + continue + start_pos += len(start_with) + tokens += line + end_pos = line.find(end_with) + if end_pos != -1: + end_pos += len(tokens) - len(line) + break + + if start_pos == -1: + self.dist_fatal("expected to find '%s' within a C comment" % start_with) + if end_pos == -1: + self.dist_fatal("expected to end with '%s'" % end_with) + + tokens = tokens[start_pos:end_pos] + return self._parse_target_tokens(tokens) + + _parse_regex_arg = re.compile(r'\s|,|([+-])') + def _parse_arg_features(self, arg_name, req_features): + if not isinstance(req_features, str): + self.dist_fatal("expected a string in '%s'" % arg_name) + + final_features = set() + # space and comma can be used as a separator + tokens = list(filter(None, re.split(self._parse_regex_arg, req_features))) + append = True # append is the default + for tok in tokens: + if tok[0] in ("#", "$"): + self.dist_fatal( + arg_name, "target groups and policies " + "aren't allowed from arguments, " + "only from dispatch-able sources" + ) + if tok == '+': + append = True + continue + if tok == '-': + append = False + continue + + TOK = tok.upper() # we use upper-case internally + features_to = set() + if TOK == "NONE": + pass + elif TOK == "NATIVE": + native = self.cc_flags["native"] + if not native: + self.dist_fatal(arg_name, + "native option isn't supported by the compiler" + ) + features_to = self.feature_names( + force_flags=native, macros=[("DETECT_FEATURES", 1)] + ) + elif TOK == "MAX": + features_to = self.feature_supported.keys() + elif TOK == "MIN": + features_to = self.feature_min + else: + if TOK in self.feature_supported: + features_to.add(TOK) + else: + if not self.feature_is_exist(TOK): + self.dist_fatal(arg_name, + ", '%s' isn't a known feature or option" % tok + ) + if append: + final_features = final_features.union(features_to) + else: + final_features = final_features.difference(features_to) + + append = True # back to default + + return final_features + + _parse_regex_target = re.compile(r'\s|[*,/]|([()])') + def _parse_target_tokens(self, tokens): + assert(isinstance(tokens, str)) + final_targets = [] # to keep it sorted as specified + extra_flags = [] + has_baseline = False + + skipped = set() + policies = set() + multi_target = None + + tokens = list(filter(None, re.split(self._parse_regex_target, tokens))) + if not tokens: + self.dist_fatal("expected one token at least") + + for tok in tokens: + TOK = tok.upper() + ch = tok[0] + if ch in ('+', '-'): + self.dist_fatal( + "+/- are 'not' allowed from target's groups or @targets, " + "only from cpu_baseline and cpu_dispatch parms" + ) + elif ch == '$': + if multi_target is not None: + self.dist_fatal( + "policies aren't allowed inside multi-target '()'" + ", only CPU features" + ) + policies.add(self._parse_token_policy(TOK)) + elif ch == '#': + if multi_target is not None: + self.dist_fatal( + "target groups aren't allowed inside multi-target '()'" + ", only CPU features" + ) + has_baseline, final_targets, extra_flags = \ + self._parse_token_group(TOK, has_baseline, final_targets, extra_flags) + elif ch == '(': + if multi_target is not None: + self.dist_fatal("unclosed multi-target, missing ')'") + multi_target = set() + elif ch == ')': + if multi_target is None: + self.dist_fatal("multi-target opener '(' wasn't found") + targets = self._parse_multi_target(multi_target) + if targets is None: + skipped.add(tuple(multi_target)) + else: + if len(targets) == 1: + targets = targets[0] + if targets and targets not in final_targets: + final_targets.append(targets) + multi_target = None # back to default + else: + if TOK == "BASELINE": + if multi_target is not None: + self.dist_fatal("baseline isn't allowed inside multi-target '()'") + has_baseline = True + continue + + if multi_target is not None: + multi_target.add(TOK) + continue + + if not self.feature_is_exist(TOK): + self.dist_fatal("invalid target name '%s'" % TOK) + + is_enabled = ( + TOK in self.parse_baseline_names or + TOK in self.parse_dispatch_names + ) + if is_enabled: + if TOK not in final_targets: + final_targets.append(TOK) + continue + + skipped.add(TOK) + + if multi_target is not None: + self.dist_fatal("unclosed multi-target, missing ')'") + if skipped: + self.dist_log( + "skip targets", skipped, + "not part of baseline or dispatch-able features" + ) + + final_targets = self.feature_untied(final_targets) + + # add polices dependencies + for p in list(policies): + _, _, deps = self._parse_policies[p] + for d in deps: + if d in policies: + continue + self.dist_log( + "policy '%s' force enables '%s'" % ( + p, d + )) + policies.add(d) + + # release policies filtrations + for p, (have, nhave, _) in self._parse_policies.items(): + func = None + if p in policies: + func = have + self.dist_log("policy '%s' is ON" % p) + else: + func = nhave + if not func: + continue + has_baseline, final_targets, extra_flags = func( + has_baseline, final_targets, extra_flags + ) + + return has_baseline, final_targets, extra_flags + + def _parse_token_policy(self, token): + """validate policy token""" + if len(token) <= 1 or token[-1:] == token[0]: + self.dist_fatal("'$' must stuck in the begin of policy name") + token = token[1:] + if token not in self._parse_policies: + self.dist_fatal( + "'%s' is an invalid policy name, available policies are" % token, + self._parse_policies.keys() + ) + return token + + def _parse_token_group(self, token, has_baseline, final_targets, extra_flags): + """validate group token""" + if len(token) <= 1 or token[-1:] == token[0]: + self.dist_fatal("'#' must stuck in the begin of group name") + + token = token[1:] + ghas_baseline, gtargets, gextra_flags = self.parse_target_groups.get( + token, (False, None, []) + ) + if gtargets is None: + self.dist_fatal( + "'%s' is an invalid target group name, " % token + \ + "available target groups are", + self.parse_target_groups.keys() + ) + if ghas_baseline: + has_baseline = True + # always keep sorting as specified + final_targets += [f for f in gtargets if f not in final_targets] + extra_flags += [f for f in gextra_flags if f not in extra_flags] + return has_baseline, final_targets, extra_flags + + def _parse_multi_target(self, targets): + """validate multi targets that defined between parentheses()""" + # remove any implied features and keep the origins + if not targets: + self.dist_fatal("empty multi-target '()'") + if not all([ + self.feature_is_exist(tar) for tar in targets + ]) : + self.dist_fatal("invalid target name in multi-target", targets) + if not all([ + ( + tar in self.parse_baseline_names or + tar in self.parse_dispatch_names + ) + for tar in targets + ]) : + return None + targets = self.feature_ahead(targets) + if not targets: + return None + # force sort multi targets, so it can be comparable + targets = self.feature_sorted(targets) + targets = tuple(targets) # hashable + return targets + + def _parse_policy_not_keepbase(self, has_baseline, final_targets, extra_flags): + """skip all baseline features""" + skipped = [] + for tar in final_targets[:]: + is_base = False + if isinstance(tar, str): + is_base = tar in self.parse_baseline_names + else: + # multi targets + is_base = all([ + f in self.parse_baseline_names + for f in tar + ]) + if is_base: + skipped.append(tar) + final_targets.remove(tar) + + if skipped: + self.dist_log("skip baseline features", skipped) + + return has_baseline, final_targets, extra_flags + + def _parse_policy_keepsort(self, has_baseline, final_targets, extra_flags): + """leave a notice that $keep_sort is on""" + self.dist_log( + "policy 'keep_sort' is on, dispatch-able targets", final_targets, "\n" + "are 'not' sorted depend on the highest interest but" + "as specified in the dispatch-able source or the extra group" + ) + return has_baseline, final_targets, extra_flags + + def _parse_policy_not_keepsort(self, has_baseline, final_targets, extra_flags): + """sorted depend on the highest interest""" + final_targets = self.feature_sorted(final_targets, reverse=True) + return has_baseline, final_targets, extra_flags + + def _parse_policy_maxopt(self, has_baseline, final_targets, extra_flags): + """append the compiler optimization flags""" + if self.cc_has_debug: + self.dist_log("debug mode is detected, policy 'maxopt' is skipped.") + elif self.cc_noopt: + self.dist_log("optimization is disabled, policy 'maxopt' is skipped.") + else: + flags = self.cc_flags["opt"] + if not flags: + self.dist_log( + "current compiler doesn't support optimization flags, " + "policy 'maxopt' is skipped", stderr=True + ) + else: + extra_flags += flags + return has_baseline, final_targets, extra_flags + + def _parse_policy_werror(self, has_baseline, final_targets, extra_flags): + """force warnings to treated as errors""" + flags = self.cc_flags["werror"] + if not flags: + self.dist_log( + "current compiler doesn't support werror flags, " + "warnings will 'not' treated as errors", stderr=True + ) + else: + self.dist_log("compiler warnings are treated as errors") + extra_flags += flags + return has_baseline, final_targets, extra_flags + + def _parse_policy_autovec(self, has_baseline, final_targets, extra_flags): + """skip features that has no auto-vectorized support by compiler""" + skipped = [] + for tar in final_targets[:]: + if isinstance(tar, str): + can = self.feature_can_autovec(tar) + else: # multiple target + can = all([ + self.feature_can_autovec(t) + for t in tar + ]) + if not can: + final_targets.remove(tar) + skipped.append(tar) + + if skipped: + self.dist_log("skip non auto-vectorized features", skipped) + + return has_baseline, final_targets, extra_flags + +class CCompilerOpt(_Config, _Distutils, _Cache, _CCompiler, _Feature, _Parse): + """ + A helper class for `CCompiler` aims to provide extra build options + to effectively control of compiler optimizations that are directly + related to CPU features. + """ + def __init__(self, ccompiler, cpu_baseline="min", cpu_dispatch="max", cache_path=None): + _Config.__init__(self) + _Distutils.__init__(self, ccompiler) + _Cache.__init__(self, cache_path, self.dist_info(), cpu_baseline, cpu_dispatch) + _CCompiler.__init__(self) + _Feature.__init__(self) + if not self.cc_noopt and self.cc_has_native: + self.dist_log( + "native flag is specified through environment variables. " + "force cpu-baseline='native'" + ) + cpu_baseline = "native" + _Parse.__init__(self, cpu_baseline, cpu_dispatch) + # keep the requested features untouched, need it later for report + # and trace purposes + self._requested_baseline = cpu_baseline + self._requested_dispatch = cpu_dispatch + # key is the dispatch-able source and value is a tuple + # contains two items (has_baseline[boolean], dispatched-features[list]) + self.sources_status = getattr(self, "sources_status", {}) + # every instance should has a separate one + self.cache_private.add("sources_status") + # set it at the end to make sure the cache writing was done after init + # this class + self.hit_cache = hasattr(self, "hit_cache") + + def is_cached(self): + """ + Returns True if the class loaded from the cache file + """ + return self.cache_infile and self.hit_cache + + def cpu_baseline_flags(self): + """ + Returns a list of final CPU baseline compiler flags + """ + return self.parse_baseline_flags + + def cpu_baseline_names(self): + """ + return a list of final CPU baseline feature names + """ + return self.parse_baseline_names + + def cpu_dispatch_names(self): + """ + return a list of final CPU dispatch feature names + """ + return self.parse_dispatch_names + + def try_dispatch(self, sources, src_dir=None, ccompiler=None, **kwargs): + """ + Compile one or more dispatch-able sources and generates object files, + also generates abstract C config headers and macros that + used later for the final runtime dispatching process. + + The mechanism behind it is to takes each source file that specified + in 'sources' and branching it into several files depend on + special configuration statements that must be declared in the + top of each source which contains targeted CPU features, + then it compiles every branched source with the proper compiler flags. + + Parameters + ---------- + sources : list + Must be a list of dispatch-able sources file paths, + and configuration statements must be declared inside + each file. + + src_dir : str + Path of parent directory for the generated headers and wrapped sources. + If None(default) the files will generated in-place. + + ccompiler: CCompiler + Distutils `CCompiler` instance to be used for compilation. + If None (default), the provided instance during the initialization + will be used instead. + + **kwargs : any + Arguments to pass on to the `CCompiler.compile()` + + Returns + ------- + list : generated object files + + Raises + ------ + CompileError + Raises by `CCompiler.compile()` on compiling failure. + DistutilsError + Some errors during checking the sanity of configuration statements. + + See Also + -------- + parse_targets : + Parsing the configuration statements of dispatch-able sources. + """ + to_compile = {} + baseline_flags = self.cpu_baseline_flags() + include_dirs = kwargs.setdefault("include_dirs", []) + + for src in sources: + output_dir = os.path.dirname(src) + if src_dir: + if not output_dir.startswith(src_dir): + output_dir = os.path.join(src_dir, output_dir) + if output_dir not in include_dirs: + # To allow including the generated config header(*.dispatch.h) + # by the dispatch-able sources + include_dirs.append(output_dir) + + has_baseline, targets, extra_flags = self.parse_targets(src) + nochange = self._generate_config(output_dir, src, targets, has_baseline) + for tar in targets: + tar_src = self._wrap_target(output_dir, src, tar, nochange=nochange) + flags = tuple(extra_flags + self.feature_flags(tar)) + to_compile.setdefault(flags, []).append(tar_src) + + if has_baseline: + flags = tuple(extra_flags + baseline_flags) + to_compile.setdefault(flags, []).append(src) + + self.sources_status[src] = (has_baseline, targets) + + # For these reasons, the sources are compiled in a separate loop: + # - Gathering all sources with the same flags to benefit from + # the parallel compiling as much as possible. + # - To generate all config headers of the dispatchable sources, + # before the compilation in case if there are dependency relationships + # among them. + objects = [] + for flags, srcs in to_compile.items(): + objects += self.dist_compile( + srcs, list(flags), ccompiler=ccompiler, **kwargs + ) + return objects + + def generate_dispatch_header(self, header_path): + """ + Generate the dispatch header which contains the #definitions and headers + for platform-specific instruction-sets for the enabled CPU baseline and + dispatch-able features. + + Its highly recommended to take a look at the generated header + also the generated source files via `try_dispatch()` + in order to get the full picture. + """ + self.dist_log("generate CPU dispatch header: (%s)" % header_path) + + baseline_names = self.cpu_baseline_names() + dispatch_names = self.cpu_dispatch_names() + baseline_len = len(baseline_names) + dispatch_len = len(dispatch_names) + + header_dir = os.path.dirname(header_path) + if not os.path.exists(header_dir): + self.dist_log( + f"dispatch header dir {header_dir} does not exist, creating it", + stderr=True + ) + os.makedirs(header_dir) + + with open(header_path, 'w') as f: + baseline_calls = ' \\\n'.join([ + ( + "\t%sWITH_CPU_EXPAND_(MACRO_TO_CALL(%s, __VA_ARGS__))" + ) % (self.conf_c_prefix, f) + for f in baseline_names + ]) + dispatch_calls = ' \\\n'.join([ + ( + "\t%sWITH_CPU_EXPAND_(MACRO_TO_CALL(%s, __VA_ARGS__))" + ) % (self.conf_c_prefix, f) + for f in dispatch_names + ]) + f.write(textwrap.dedent("""\ + /* + * AUTOGENERATED DON'T EDIT + * Please make changes to the code generator (distutils/ccompiler_opt.py) + */ + #define {pfx}WITH_CPU_BASELINE "{baseline_str}" + #define {pfx}WITH_CPU_DISPATCH "{dispatch_str}" + #define {pfx}WITH_CPU_BASELINE_N {baseline_len} + #define {pfx}WITH_CPU_DISPATCH_N {dispatch_len} + #define {pfx}WITH_CPU_EXPAND_(X) X + #define {pfx}WITH_CPU_BASELINE_CALL(MACRO_TO_CALL, ...) \\ + {baseline_calls} + #define {pfx}WITH_CPU_DISPATCH_CALL(MACRO_TO_CALL, ...) \\ + {dispatch_calls} + """).format( + pfx=self.conf_c_prefix, baseline_str=" ".join(baseline_names), + dispatch_str=" ".join(dispatch_names), baseline_len=baseline_len, + dispatch_len=dispatch_len, baseline_calls=baseline_calls, + dispatch_calls=dispatch_calls + )) + baseline_pre = '' + for name in baseline_names: + baseline_pre += self.feature_c_preprocessor(name, tabs=1) + '\n' + + dispatch_pre = '' + for name in dispatch_names: + dispatch_pre += textwrap.dedent("""\ + #ifdef {pfx}CPU_TARGET_{name} + {pre} + #endif /*{pfx}CPU_TARGET_{name}*/ + """).format( + pfx=self.conf_c_prefix_, name=name, pre=self.feature_c_preprocessor( + name, tabs=1 + )) + + f.write(textwrap.dedent("""\ + /******* baseline features *******/ + {baseline_pre} + /******* dispatch features *******/ + {dispatch_pre} + """).format( + pfx=self.conf_c_prefix_, baseline_pre=baseline_pre, + dispatch_pre=dispatch_pre + )) + + def report(self, full=False): + report = [] + platform_rows = [] + baseline_rows = [] + dispatch_rows = [] + report.append(("Platform", platform_rows)) + report.append(("", "")) + report.append(("CPU baseline", baseline_rows)) + report.append(("", "")) + report.append(("CPU dispatch", dispatch_rows)) + + ########## platform ########## + platform_rows.append(("Architecture", ( + "unsupported" if self.cc_on_noarch else self.cc_march) + )) + platform_rows.append(("Compiler", ( + "unix-like" if self.cc_is_nocc else self.cc_name) + )) + ########## baseline ########## + if self.cc_noopt: + baseline_rows.append(("Requested", "optimization disabled")) + else: + baseline_rows.append(("Requested", repr(self._requested_baseline))) + + baseline_names = self.cpu_baseline_names() + baseline_rows.append(( + "Enabled", (' '.join(baseline_names) if baseline_names else "none") + )) + baseline_flags = self.cpu_baseline_flags() + baseline_rows.append(( + "Flags", (' '.join(baseline_flags) if baseline_flags else "none") + )) + extra_checks = [] + for name in baseline_names: + extra_checks += self.feature_extra_checks(name) + baseline_rows.append(( + "Extra checks", (' '.join(extra_checks) if extra_checks else "none") + )) + + ########## dispatch ########## + if self.cc_noopt: + baseline_rows.append(("Requested", "optimization disabled")) + else: + dispatch_rows.append(("Requested", repr(self._requested_dispatch))) + + dispatch_names = self.cpu_dispatch_names() + dispatch_rows.append(( + "Enabled", (' '.join(dispatch_names) if dispatch_names else "none") + )) + ########## Generated ########## + # TODO: + # - collect object names from 'try_dispatch()' + # then get size of each object and printed + # - give more details about the features that not + # generated due compiler support + # - find a better output's design. + # + target_sources = {} + for source, (_, targets) in self.sources_status.items(): + for tar in targets: + target_sources.setdefault(tar, []).append(source) + + if not full or not target_sources: + generated = "" + for tar in self.feature_sorted(target_sources): + sources = target_sources[tar] + name = tar if isinstance(tar, str) else '(%s)' % ' '.join(tar) + generated += name + "[%d] " % len(sources) + dispatch_rows.append(("Generated", generated[:-1] if generated else "none")) + else: + dispatch_rows.append(("Generated", '')) + for tar in self.feature_sorted(target_sources): + sources = target_sources[tar] + pretty_name = tar if isinstance(tar, str) else '(%s)' % ' '.join(tar) + flags = ' '.join(self.feature_flags(tar)) + implies = ' '.join(self.feature_sorted(self.feature_implies(tar))) + detect = ' '.join(self.feature_detect(tar)) + extra_checks = [] + for name in ((tar,) if isinstance(tar, str) else tar): + extra_checks += self.feature_extra_checks(name) + extra_checks = (' '.join(extra_checks) if extra_checks else "none") + + dispatch_rows.append(('', '')) + dispatch_rows.append((pretty_name, implies)) + dispatch_rows.append(("Flags", flags)) + dispatch_rows.append(("Extra checks", extra_checks)) + dispatch_rows.append(("Detect", detect)) + for src in sources: + dispatch_rows.append(("", src)) + + ############################### + # TODO: add support for 'markdown' format + text = [] + secs_len = [len(secs) for secs, _ in report] + cols_len = [len(col) for _, rows in report for col, _ in rows] + tab = ' ' * 2 + pad = max(max(secs_len), max(cols_len)) + for sec, rows in report: + if not sec: + text.append("") # empty line + continue + sec += ' ' * (pad - len(sec)) + text.append(sec + tab + ': ') + for col, val in rows: + col += ' ' * (pad - len(col)) + text.append(tab + col + ': ' + val) + + return '\n'.join(text) + + def _wrap_target(self, output_dir, dispatch_src, target, nochange=False): + assert(isinstance(target, (str, tuple))) + if isinstance(target, str): + ext_name = target_name = target + else: + # multi-target + ext_name = '.'.join(target) + target_name = '__'.join(target) + + wrap_path = os.path.join(output_dir, os.path.basename(dispatch_src)) + wrap_path = "{0}.{2}{1}".format(*os.path.splitext(wrap_path), ext_name.lower()) + if nochange and os.path.exists(wrap_path): + return wrap_path + + self.dist_log("wrap dispatch-able target -> ", wrap_path) + # sorting for readability + features = self.feature_sorted(self.feature_implies_c(target)) + target_join = "#define %sCPU_TARGET_" % self.conf_c_prefix_ + target_defs = [target_join + f for f in features] + target_defs = '\n'.join(target_defs) + + with open(wrap_path, "w") as fd: + fd.write(textwrap.dedent("""\ + /** + * AUTOGENERATED DON'T EDIT + * Please make changes to the code generator \ + (distutils/ccompiler_opt.py) + */ + #define {pfx}CPU_TARGET_MODE + #define {pfx}CPU_TARGET_CURRENT {target_name} + {target_defs} + #include "{path}" + """).format( + pfx=self.conf_c_prefix_, target_name=target_name, + path=os.path.abspath(dispatch_src), target_defs=target_defs + )) + return wrap_path + + def _generate_config(self, output_dir, dispatch_src, targets, has_baseline=False): + config_path = os.path.basename(dispatch_src) + config_path = os.path.splitext(config_path)[0] + '.h' + config_path = os.path.join(output_dir, config_path) + # check if targets didn't change to avoid recompiling + cache_hash = self.cache_hash(targets, has_baseline) + try: + with open(config_path) as f: + last_hash = f.readline().split("cache_hash:") + if len(last_hash) == 2 and int(last_hash[1]) == cache_hash: + return True + except OSError: + pass + + self.dist_log("generate dispatched config -> ", config_path) + dispatch_calls = [] + for tar in targets: + if isinstance(tar, str): + target_name = tar + else: # multi target + target_name = '__'.join([t for t in tar]) + req_detect = self.feature_detect(tar) + req_detect = '&&'.join([ + "CHK(%s)" % f for f in req_detect + ]) + dispatch_calls.append( + "\t%sCPU_DISPATCH_EXPAND_(CB((%s), %s, __VA_ARGS__))" % ( + self.conf_c_prefix_, req_detect, target_name + )) + dispatch_calls = ' \\\n'.join(dispatch_calls) + + if has_baseline: + baseline_calls = ( + "\t%sCPU_DISPATCH_EXPAND_(CB(__VA_ARGS__))" + ) % self.conf_c_prefix_ + else: + baseline_calls = '' + + with open(config_path, "w") as fd: + fd.write(textwrap.dedent("""\ + // cache_hash:{cache_hash} + /** + * AUTOGENERATED DON'T EDIT + * Please make changes to the code generator (distutils/ccompiler_opt.py) + */ + #ifndef {pfx}CPU_DISPATCH_EXPAND_ + #define {pfx}CPU_DISPATCH_EXPAND_(X) X + #endif + #undef {pfx}CPU_DISPATCH_BASELINE_CALL + #undef {pfx}CPU_DISPATCH_CALL + #define {pfx}CPU_DISPATCH_BASELINE_CALL(CB, ...) \\ + {baseline_calls} + #define {pfx}CPU_DISPATCH_CALL(CHK, CB, ...) \\ + {dispatch_calls} + """).format( + pfx=self.conf_c_prefix_, baseline_calls=baseline_calls, + dispatch_calls=dispatch_calls, cache_hash=cache_hash + )) + return False + +def new_ccompiler_opt(compiler, dispatch_hpath, **kwargs): + """ + Create a new instance of 'CCompilerOpt' and generate the dispatch header + which contains the #definitions and headers of platform-specific instruction-sets for + the enabled CPU baseline and dispatch-able features. + + Parameters + ---------- + compiler : CCompiler instance + dispatch_hpath : str + path of the dispatch header + + **kwargs: passed as-is to `CCompilerOpt(...)` + Returns + ------- + new instance of CCompilerOpt + """ + opt = CCompilerOpt(compiler, **kwargs) + if not os.path.exists(dispatch_hpath) or not opt.is_cached(): + opt.generate_dispatch_header(dispatch_hpath) + return opt diff --git a/numpy/distutils/checks/cpu_asimd.c b/numpy/distutils/checks/cpu_asimd.c new file mode 100644 index 000000000000..8df556b6c303 --- /dev/null +++ b/numpy/distutils/checks/cpu_asimd.c @@ -0,0 +1,25 @@ +#ifdef _MSC_VER + #include <Intrin.h> +#endif +#include <arm_neon.h> + +int main(void) +{ + float32x4_t v1 = vdupq_n_f32(1.0f), v2 = vdupq_n_f32(2.0f); + /* MAXMIN */ + int ret = (int)vgetq_lane_f32(vmaxnmq_f32(v1, v2), 0); + ret += (int)vgetq_lane_f32(vminnmq_f32(v1, v2), 0); + /* ROUNDING */ + ret += (int)vgetq_lane_f32(vrndq_f32(v1), 0); +#ifdef __aarch64__ + { + float64x2_t vd1 = vdupq_n_f64(1.0), vd2 = vdupq_n_f64(2.0); + /* MAXMIN */ + ret += (int)vgetq_lane_f64(vmaxnmq_f64(vd1, vd2), 0); + ret += (int)vgetq_lane_f64(vminnmq_f64(vd1, vd2), 0); + /* ROUNDING */ + ret += (int)vgetq_lane_f64(vrndq_f64(vd1), 0); + } +#endif + return ret; +} diff --git a/numpy/distutils/checks/cpu_asimddp.c b/numpy/distutils/checks/cpu_asimddp.c new file mode 100644 index 000000000000..0158d13543ad --- /dev/null +++ b/numpy/distutils/checks/cpu_asimddp.c @@ -0,0 +1,15 @@ +#ifdef _MSC_VER + #include <Intrin.h> +#endif +#include <arm_neon.h> + +int main(void) +{ + uint8x16_t v1 = vdupq_n_u8((unsigned char)1), v2 = vdupq_n_u8((unsigned char)2); + uint32x4_t va = vdupq_n_u32(3); + int ret = (int)vgetq_lane_u32(vdotq_u32(va, v1, v2), 0); +#ifdef __aarch64__ + ret += (int)vgetq_lane_u32(vdotq_laneq_u32(va, v1, v2, 0), 0); +#endif + return ret; +} diff --git a/numpy/distutils/checks/cpu_asimdfhm.c b/numpy/distutils/checks/cpu_asimdfhm.c new file mode 100644 index 000000000000..bb437aa40352 --- /dev/null +++ b/numpy/distutils/checks/cpu_asimdfhm.c @@ -0,0 +1,17 @@ +#ifdef _MSC_VER + #include <Intrin.h> +#endif +#include <arm_neon.h> + +int main(void) +{ + float16x8_t vhp = vdupq_n_f16((float16_t)1); + float16x4_t vlhp = vdup_n_f16((float16_t)1); + float32x4_t vf = vdupq_n_f32(1.0f); + float32x2_t vlf = vdup_n_f32(1.0f); + + int ret = (int)vget_lane_f32(vfmlal_low_u32(vlf, vlhp, vlhp), 0); + ret += (int)vgetq_lane_f32(vfmlslq_high_u32(vf, vhp, vhp), 0); + + return ret; +} diff --git a/numpy/distutils/checks/cpu_asimdhp.c b/numpy/distutils/checks/cpu_asimdhp.c new file mode 100644 index 000000000000..80b94000f04e --- /dev/null +++ b/numpy/distutils/checks/cpu_asimdhp.c @@ -0,0 +1,14 @@ +#ifdef _MSC_VER + #include <Intrin.h> +#endif +#include <arm_neon.h> + +int main(void) +{ + float16x8_t vhp = vdupq_n_f16((float16_t)-1); + float16x4_t vlhp = vdup_n_f16((float16_t)-1); + + int ret = (int)vgetq_lane_f16(vabdq_f16(vhp, vhp), 0); + ret += (int)vget_lane_f16(vabd_f16(vlhp, vlhp), 0); + return ret; +} diff --git a/numpy/distutils/checks/cpu_avx.c b/numpy/distutils/checks/cpu_avx.c new file mode 100644 index 000000000000..26ae18466740 --- /dev/null +++ b/numpy/distutils/checks/cpu_avx.c @@ -0,0 +1,20 @@ +#if defined(DETECT_FEATURES) && defined(__INTEL_COMPILER) + /* + * Unlike GCC and CLANG, Intel Compiler exposes all supported intrinsics, + * whether or not the build options for those features are specified. + * Therefore, we must test #definitions of CPU features when option native/host + * is enabled via `--cpu-baseline` or through env var `CFLAGS` otherwise + * the test will be broken and leads to enable all possible features. + */ + #ifndef __AVX__ + #error "HOST/ARCH doesn't support AVX" + #endif +#endif + +#include <immintrin.h> + +int main(int argc, char **argv) +{ + __m256 a = _mm256_add_ps(_mm256_loadu_ps((const float*)argv[argc-1]), _mm256_loadu_ps((const float*)argv[1])); + return (int)_mm_cvtss_f32(_mm256_castps256_ps128(a)); +} diff --git a/numpy/distutils/checks/cpu_avx2.c b/numpy/distutils/checks/cpu_avx2.c new file mode 100644 index 000000000000..ddde868f1b58 --- /dev/null +++ b/numpy/distutils/checks/cpu_avx2.c @@ -0,0 +1,20 @@ +#if defined(DETECT_FEATURES) && defined(__INTEL_COMPILER) + /* + * Unlike GCC and CLANG, Intel Compiler exposes all supported intrinsics, + * whether or not the build options for those features are specified. + * Therefore, we must test #definitions of CPU features when option native/host + * is enabled via `--cpu-baseline` or through env var `CFLAGS` otherwise + * the test will be broken and leads to enable all possible features. + */ + #ifndef __AVX2__ + #error "HOST/ARCH doesn't support AVX2" + #endif +#endif + +#include <immintrin.h> + +int main(int argc, char **argv) +{ + __m256i a = _mm256_abs_epi16(_mm256_loadu_si256((const __m256i*)argv[argc-1])); + return _mm_cvtsi128_si32(_mm256_castsi256_si128(a)); +} diff --git a/numpy/distutils/checks/cpu_avx512_clx.c b/numpy/distutils/checks/cpu_avx512_clx.c new file mode 100644 index 000000000000..81edcd067005 --- /dev/null +++ b/numpy/distutils/checks/cpu_avx512_clx.c @@ -0,0 +1,22 @@ +#if defined(DETECT_FEATURES) && defined(__INTEL_COMPILER) + /* + * Unlike GCC and CLANG, Intel Compiler exposes all supported intrinsics, + * whether or not the build options for those features are specified. + * Therefore, we must test #definitions of CPU features when option native/host + * is enabled via `--cpu-baseline` or through env var `CFLAGS` otherwise + * the test will be broken and leads to enable all possible features. + */ + #ifndef __AVX512VNNI__ + #error "HOST/ARCH doesn't support CascadeLake AVX512 features" + #endif +#endif + +#include <immintrin.h> + +int main(int argc, char **argv) +{ + /* VNNI */ + __m512i a = _mm512_loadu_si512((const __m512i*)argv[argc-1]); + a = _mm512_dpbusd_epi32(a, _mm512_setzero_si512(), a); + return _mm_cvtsi128_si32(_mm512_castsi512_si128(a)); +} diff --git a/numpy/distutils/checks/cpu_avx512_cnl.c b/numpy/distutils/checks/cpu_avx512_cnl.c new file mode 100644 index 000000000000..5799f122b511 --- /dev/null +++ b/numpy/distutils/checks/cpu_avx512_cnl.c @@ -0,0 +1,24 @@ +#if defined(DETECT_FEATURES) && defined(__INTEL_COMPILER) + /* + * Unlike GCC and CLANG, Intel Compiler exposes all supported intrinsics, + * whether or not the build options for those features are specified. + * Therefore, we must test #definitions of CPU features when option native/host + * is enabled via `--cpu-baseline` or through env var `CFLAGS` otherwise + * the test will be broken and leads to enable all possible features. + */ + #if !defined(__AVX512VBMI__) || !defined(__AVX512IFMA__) + #error "HOST/ARCH doesn't support CannonLake AVX512 features" + #endif +#endif + +#include <immintrin.h> + +int main(int argc, char **argv) +{ + __m512i a = _mm512_loadu_si512((const __m512i*)argv[argc-1]); + /* IFMA */ + a = _mm512_madd52hi_epu64(a, a, _mm512_setzero_si512()); + /* VMBI */ + a = _mm512_permutex2var_epi8(a, _mm512_setzero_si512(), a); + return _mm_cvtsi128_si32(_mm512_castsi512_si128(a)); +} diff --git a/numpy/distutils/checks/cpu_avx512_icl.c b/numpy/distutils/checks/cpu_avx512_icl.c new file mode 100644 index 000000000000..3cf44d73164b --- /dev/null +++ b/numpy/distutils/checks/cpu_avx512_icl.c @@ -0,0 +1,26 @@ +#if defined(DETECT_FEATURES) && defined(__INTEL_COMPILER) + /* + * Unlike GCC and CLANG, Intel Compiler exposes all supported intrinsics, + * whether or not the build options for those features are specified. + * Therefore, we must test #definitions of CPU features when option native/host + * is enabled via `--cpu-baseline` or through env var `CFLAGS` otherwise + * the test will be broken and leads to enable all possible features. + */ + #if !defined(__AVX512VPOPCNTDQ__) || !defined(__AVX512BITALG__) || !defined(__AVX512VPOPCNTDQ__) + #error "HOST/ARCH doesn't support IceLake AVX512 features" + #endif +#endif + +#include <immintrin.h> + +int main(int argc, char **argv) +{ + __m512i a = _mm512_loadu_si512((const __m512i*)argv[argc-1]); + /* VBMI2 */ + a = _mm512_shrdv_epi64(a, a, _mm512_setzero_si512()); + /* BITLAG */ + a = _mm512_popcnt_epi8(a); + /* VPOPCNTDQ */ + a = _mm512_popcnt_epi64(a); + return _mm_cvtsi128_si32(_mm512_castsi512_si128(a)); +} diff --git a/numpy/distutils/checks/cpu_avx512_knl.c b/numpy/distutils/checks/cpu_avx512_knl.c new file mode 100644 index 000000000000..b3f4f6976514 --- /dev/null +++ b/numpy/distutils/checks/cpu_avx512_knl.c @@ -0,0 +1,25 @@ +#if defined(DETECT_FEATURES) && defined(__INTEL_COMPILER) + /* + * Unlike GCC and CLANG, Intel Compiler exposes all supported intrinsics, + * whether or not the build options for those features are specified. + * Therefore, we must test #definitions of CPU features when option native/host + * is enabled via `--cpu-baseline` or through env var `CFLAGS` otherwise + * the test will be broken and leads to enable all possible features. + */ + #if !defined(__AVX512ER__) || !defined(__AVX512PF__) + #error "HOST/ARCH doesn't support Knights Landing AVX512 features" + #endif +#endif + +#include <immintrin.h> + +int main(int argc, char **argv) +{ + int base[128]; + __m512d ad = _mm512_loadu_pd((const __m512d*)argv[argc-1]); + /* ER */ + __m512i a = _mm512_castpd_si512(_mm512_exp2a23_pd(ad)); + /* PF */ + _mm512_mask_prefetch_i64scatter_pd(base, _mm512_cmpeq_epi64_mask(a, a), a, 1, _MM_HINT_T1); + return base[0]; +} diff --git a/numpy/distutils/checks/cpu_avx512_knm.c b/numpy/distutils/checks/cpu_avx512_knm.c new file mode 100644 index 000000000000..2c426462bd34 --- /dev/null +++ b/numpy/distutils/checks/cpu_avx512_knm.c @@ -0,0 +1,30 @@ +#if defined(DETECT_FEATURES) && defined(__INTEL_COMPILER) + /* + * Unlike GCC and CLANG, Intel Compiler exposes all supported intrinsics, + * whether or not the build options for those features are specified. + * Therefore, we must test #definitions of CPU features when option native/host + * is enabled via `--cpu-baseline` or through env var `CFLAGS` otherwise + * the test will be broken and leads to enable all possible features. + */ + #if !defined(__AVX5124FMAPS__) || !defined(__AVX5124VNNIW__) || !defined(__AVX512VPOPCNTDQ__) + #error "HOST/ARCH doesn't support Knights Mill AVX512 features" + #endif +#endif + +#include <immintrin.h> + +int main(int argc, char **argv) +{ + __m512i a = _mm512_loadu_si512((const __m512i*)argv[argc-1]); + __m512 b = _mm512_loadu_ps((const __m512*)argv[argc-2]); + + /* 4FMAPS */ + b = _mm512_4fmadd_ps(b, b, b, b, b, NULL); + /* 4VNNIW */ + a = _mm512_4dpwssd_epi32(a, a, a, a, a, NULL); + /* VPOPCNTDQ */ + a = _mm512_popcnt_epi64(a); + + a = _mm512_add_epi32(a, _mm512_castps_si512(b)); + return _mm_cvtsi128_si32(_mm512_castsi512_si128(a)); +} diff --git a/numpy/distutils/checks/cpu_avx512_skx.c b/numpy/distutils/checks/cpu_avx512_skx.c new file mode 100644 index 000000000000..8840efb7e5ee --- /dev/null +++ b/numpy/distutils/checks/cpu_avx512_skx.c @@ -0,0 +1,26 @@ +#if defined(DETECT_FEATURES) && defined(__INTEL_COMPILER) + /* + * Unlike GCC and CLANG, Intel Compiler exposes all supported intrinsics, + * whether or not the build options for those features are specified. + * Therefore, we must test #definitions of CPU features when option native/host + * is enabled via `--cpu-baseline` or through env var `CFLAGS` otherwise + * the test will be broken and leads to enable all possible features. + */ + #if !defined(__AVX512VL__) || !defined(__AVX512BW__) || !defined(__AVX512DQ__) + #error "HOST/ARCH doesn't support SkyLake AVX512 features" + #endif +#endif + +#include <immintrin.h> + +int main(int argc, char **argv) +{ + __m512i aa = _mm512_abs_epi32(_mm512_loadu_si512((const __m512i*)argv[argc-1])); + /* VL */ + __m256i a = _mm256_abs_epi64(_mm512_extracti64x4_epi64(aa, 1)); + /* DQ */ + __m512i b = _mm512_broadcast_i32x8(a); + /* BW */ + b = _mm512_abs_epi16(b); + return _mm_cvtsi128_si32(_mm512_castsi512_si128(b)); +} diff --git a/numpy/distutils/checks/cpu_avx512cd.c b/numpy/distutils/checks/cpu_avx512cd.c new file mode 100644 index 000000000000..5e29c79e34a7 --- /dev/null +++ b/numpy/distutils/checks/cpu_avx512cd.c @@ -0,0 +1,20 @@ +#if defined(DETECT_FEATURES) && defined(__INTEL_COMPILER) + /* + * Unlike GCC and CLANG, Intel Compiler exposes all supported intrinsics, + * whether or not the build options for those features are specified. + * Therefore, we must test #definitions of CPU features when option native/host + * is enabled via `--cpu-baseline` or through env var `CFLAGS` otherwise + * the test will be broken and leads to enable all possible features. + */ + #ifndef __AVX512CD__ + #error "HOST/ARCH doesn't support AVX512CD" + #endif +#endif + +#include <immintrin.h> + +int main(int argc, char **argv) +{ + __m512i a = _mm512_lzcnt_epi32(_mm512_loadu_si512((const __m512i*)argv[argc-1])); + return _mm_cvtsi128_si32(_mm512_castsi512_si128(a)); +} diff --git a/numpy/distutils/checks/cpu_avx512f.c b/numpy/distutils/checks/cpu_avx512f.c new file mode 100644 index 000000000000..d0eb7b1ad5c6 --- /dev/null +++ b/numpy/distutils/checks/cpu_avx512f.c @@ -0,0 +1,20 @@ +#if defined(DETECT_FEATURES) && defined(__INTEL_COMPILER) + /* + * Unlike GCC and CLANG, Intel Compiler exposes all supported intrinsics, + * whether or not the build options for those features are specified. + * Therefore, we must test #definitions of CPU features when option native/host + * is enabled via `--cpu-baseline` or through env var `CFLAGS` otherwise + * the test will be broken and leads to enable all possible features. + */ + #ifndef __AVX512F__ + #error "HOST/ARCH doesn't support AVX512F" + #endif +#endif + +#include <immintrin.h> + +int main(int argc, char **argv) +{ + __m512i a = _mm512_abs_epi32(_mm512_loadu_si512((const __m512i*)argv[argc-1])); + return _mm_cvtsi128_si32(_mm512_castsi512_si128(a)); +} diff --git a/numpy/distutils/checks/cpu_f16c.c b/numpy/distutils/checks/cpu_f16c.c new file mode 100644 index 000000000000..fdf36cec580c --- /dev/null +++ b/numpy/distutils/checks/cpu_f16c.c @@ -0,0 +1,22 @@ +#if defined(DETECT_FEATURES) && defined(__INTEL_COMPILER) + /* + * Unlike GCC and CLANG, Intel Compiler exposes all supported intrinsics, + * whether or not the build options for those features are specified. + * Therefore, we must test #definitions of CPU features when option native/host + * is enabled via `--cpu-baseline` or through env var `CFLAGS` otherwise + * the test will be broken and leads to enable all possible features. + */ + #ifndef __F16C__ + #error "HOST/ARCH doesn't support F16C" + #endif +#endif + +#include <emmintrin.h> +#include <immintrin.h> + +int main(int argc, char **argv) +{ + __m128 a = _mm_cvtph_ps(_mm_loadu_si128((const __m128i*)argv[argc-1])); + __m256 a8 = _mm256_cvtph_ps(_mm_loadu_si128((const __m128i*)argv[argc-2])); + return (int)(_mm_cvtss_f32(a) + _mm_cvtss_f32(_mm256_castps256_ps128(a8))); +} diff --git a/numpy/distutils/checks/cpu_fma3.c b/numpy/distutils/checks/cpu_fma3.c new file mode 100644 index 000000000000..bfeef22b5f0e --- /dev/null +++ b/numpy/distutils/checks/cpu_fma3.c @@ -0,0 +1,22 @@ +#if defined(DETECT_FEATURES) && defined(__INTEL_COMPILER) + /* + * Unlike GCC and CLANG, Intel Compiler exposes all supported intrinsics, + * whether or not the build options for those features are specified. + * Therefore, we must test #definitions of CPU features when option native/host + * is enabled via `--cpu-baseline` or through env var `CFLAGS` otherwise + * the test will be broken and leads to enable all possible features. + */ + #if !defined(__FMA__) && !defined(__AVX2__) + #error "HOST/ARCH doesn't support FMA3" + #endif +#endif + +#include <xmmintrin.h> +#include <immintrin.h> + +int main(int argc, char **argv) +{ + __m256 a = _mm256_loadu_ps((const float*)argv[argc-1]); + a = _mm256_fmadd_ps(a, a, a); + return (int)_mm_cvtss_f32(_mm256_castps256_ps128(a)); +} diff --git a/numpy/distutils/checks/cpu_fma4.c b/numpy/distutils/checks/cpu_fma4.c new file mode 100644 index 000000000000..0ff17a483385 --- /dev/null +++ b/numpy/distutils/checks/cpu_fma4.c @@ -0,0 +1,13 @@ +#include <immintrin.h> +#ifdef _MSC_VER + #include <ammintrin.h> +#else + #include <x86intrin.h> +#endif + +int main(int argc, char **argv) +{ + __m256 a = _mm256_loadu_ps((const float*)argv[argc-1]); + a = _mm256_macc_ps(a, a, a); + return (int)_mm_cvtss_f32(_mm256_castps256_ps128(a)); +} diff --git a/numpy/distutils/checks/cpu_neon.c b/numpy/distutils/checks/cpu_neon.c new file mode 100644 index 000000000000..4eab1f384a72 --- /dev/null +++ b/numpy/distutils/checks/cpu_neon.c @@ -0,0 +1,15 @@ +#ifdef _MSC_VER + #include <Intrin.h> +#endif +#include <arm_neon.h> + +int main(void) +{ + float32x4_t v1 = vdupq_n_f32(1.0f), v2 = vdupq_n_f32(2.0f); + int ret = (int)vgetq_lane_f32(vmulq_f32(v1, v2), 0); +#ifdef __aarch64__ + float64x2_t vd1 = vdupq_n_f64(1.0), vd2 = vdupq_n_f64(2.0); + ret += (int)vgetq_lane_f64(vmulq_f64(vd1, vd2), 0); +#endif + return ret; +} diff --git a/numpy/distutils/checks/cpu_neon_fp16.c b/numpy/distutils/checks/cpu_neon_fp16.c new file mode 100644 index 000000000000..745d2e793c4b --- /dev/null +++ b/numpy/distutils/checks/cpu_neon_fp16.c @@ -0,0 +1,11 @@ +#ifdef _MSC_VER + #include <Intrin.h> +#endif +#include <arm_neon.h> + +int main(void) +{ + short z4[] = {0, 0, 0, 0, 0, 0, 0, 0}; + float32x4_t v_z4 = vcvt_f32_f16((float16x4_t)vld1_s16((const short*)z4)); + return (int)vgetq_lane_f32(v_z4, 0); +} diff --git a/numpy/distutils/checks/cpu_neon_vfpv4.c b/numpy/distutils/checks/cpu_neon_vfpv4.c new file mode 100644 index 000000000000..45f7b5d69da4 --- /dev/null +++ b/numpy/distutils/checks/cpu_neon_vfpv4.c @@ -0,0 +1,19 @@ +#ifdef _MSC_VER + #include <Intrin.h> +#endif +#include <arm_neon.h> + +int main(void) +{ + float32x4_t v1 = vdupq_n_f32(1.0f); + float32x4_t v2 = vdupq_n_f32(2.0f); + float32x4_t v3 = vdupq_n_f32(3.0f); + int ret = (int)vgetq_lane_f32(vfmaq_f32(v1, v2, v3), 0); +#ifdef __aarch64__ + float64x2_t vd1 = vdupq_n_f64(1.0); + float64x2_t vd2 = vdupq_n_f64(2.0); + float64x2_t vd3 = vdupq_n_f64(3.0); + ret += (int)vgetq_lane_f64(vfmaq_f64(vd1, vd2, vd3), 0); +#endif + return ret; +} diff --git a/numpy/distutils/checks/cpu_popcnt.c b/numpy/distutils/checks/cpu_popcnt.c new file mode 100644 index 000000000000..813c461f05b3 --- /dev/null +++ b/numpy/distutils/checks/cpu_popcnt.c @@ -0,0 +1,32 @@ +#if defined(DETECT_FEATURES) && defined(__INTEL_COMPILER) + /* + * Unlike GCC and CLANG, Intel Compiler exposes all supported intrinsics, + * whether or not the build options for those features are specified. + * Therefore, we must test #definitions of CPU features when option native/host + * is enabled via `--cpu-baseline` or through env vr `CFLAGS` otherwise + * the test will be broken and leads to enable all possible features. + */ + #if !defined(__SSE4_2__) && !defined(__POPCNT__) + #error "HOST/ARCH doesn't support POPCNT" + #endif +#endif + +#ifdef _MSC_VER + #include <nmmintrin.h> +#else + #include <popcntintrin.h> +#endif + +int main(int argc, char **argv) +{ + // To make sure popcnt instructions are generated + // and been tested against the assembler + unsigned long long a = *((unsigned long long*)argv[argc-1]); + unsigned int b = *((unsigned int*)argv[argc-2]); + +#if defined(_M_X64) || defined(__x86_64__) + a = _mm_popcnt_u64(a); +#endif + b = _mm_popcnt_u32(b); + return (int)a + b; +} diff --git a/numpy/distutils/checks/cpu_sse.c b/numpy/distutils/checks/cpu_sse.c new file mode 100644 index 000000000000..602b74e7bc43 --- /dev/null +++ b/numpy/distutils/checks/cpu_sse.c @@ -0,0 +1,20 @@ +#if defined(DETECT_FEATURES) && defined(__INTEL_COMPILER) + /* + * Unlike GCC and CLANG, Intel Compiler exposes all supported intrinsics, + * whether or not the build options for those features are specified. + * Therefore, we must test #definitions of CPU features when option native/host + * is enabled via `--cpu-baseline` or through env var `CFLAGS` otherwise + * the test will be broken and leads to enable all possible features. + */ + #ifndef __SSE__ + #error "HOST/ARCH doesn't support SSE" + #endif +#endif + +#include <xmmintrin.h> + +int main(void) +{ + __m128 a = _mm_add_ps(_mm_setzero_ps(), _mm_setzero_ps()); + return (int)_mm_cvtss_f32(a); +} diff --git a/numpy/distutils/checks/cpu_sse2.c b/numpy/distutils/checks/cpu_sse2.c new file mode 100644 index 000000000000..33826a9ed1a5 --- /dev/null +++ b/numpy/distutils/checks/cpu_sse2.c @@ -0,0 +1,20 @@ +#if defined(DETECT_FEATURES) && defined(__INTEL_COMPILER) + /* + * Unlike GCC and CLANG, Intel Compiler exposes all supported intrinsics, + * whether or not the build options for those features are specified. + * Therefore, we must test #definitions of CPU features when option native/host + * is enabled via `--cpu-baseline` or through env var `CFLAGS` otherwise + * the test will be broken and leads to enable all possible features. + */ + #ifndef __SSE2__ + #error "HOST/ARCH doesn't support SSE2" + #endif +#endif + +#include <emmintrin.h> + +int main(void) +{ + __m128i a = _mm_add_epi16(_mm_setzero_si128(), _mm_setzero_si128()); + return _mm_cvtsi128_si32(a); +} diff --git a/numpy/distutils/checks/cpu_sse3.c b/numpy/distutils/checks/cpu_sse3.c new file mode 100644 index 000000000000..d47c20f74be1 --- /dev/null +++ b/numpy/distutils/checks/cpu_sse3.c @@ -0,0 +1,20 @@ +#if defined(DETECT_FEATURES) && defined(__INTEL_COMPILER) + /* + * Unlike GCC and CLANG, Intel Compiler exposes all supported intrinsics, + * whether or not the build options for those features are specified. + * Therefore, we must test #definitions of CPU features when option native/host + * is enabled via `--cpu-baseline` or through env var `CFLAGS` otherwise + * the test will be broken and leads to enable all possible features. + */ + #ifndef __SSE3__ + #error "HOST/ARCH doesn't support SSE3" + #endif +#endif + +#include <pmmintrin.h> + +int main(void) +{ + __m128 a = _mm_hadd_ps(_mm_setzero_ps(), _mm_setzero_ps()); + return (int)_mm_cvtss_f32(a); +} diff --git a/numpy/distutils/checks/cpu_sse41.c b/numpy/distutils/checks/cpu_sse41.c new file mode 100644 index 000000000000..7c80238a3bc1 --- /dev/null +++ b/numpy/distutils/checks/cpu_sse41.c @@ -0,0 +1,20 @@ +#if defined(DETECT_FEATURES) && defined(__INTEL_COMPILER) + /* + * Unlike GCC and CLANG, Intel Compiler exposes all supported intrinsics, + * whether or not the build options for those features are specified. + * Therefore, we must test #definitions of CPU features when option native/host + * is enabled via `--cpu-baseline` or through env var `CFLAGS` otherwise + * the test will be broken and leads to enable all possible features. + */ + #ifndef __SSE4_1__ + #error "HOST/ARCH doesn't support SSE41" + #endif +#endif + +#include <smmintrin.h> + +int main(void) +{ + __m128 a = _mm_floor_ps(_mm_setzero_ps()); + return (int)_mm_cvtss_f32(a); +} diff --git a/numpy/distutils/checks/cpu_sse42.c b/numpy/distutils/checks/cpu_sse42.c new file mode 100644 index 000000000000..f60e18f3c4f1 --- /dev/null +++ b/numpy/distutils/checks/cpu_sse42.c @@ -0,0 +1,20 @@ +#if defined(DETECT_FEATURES) && defined(__INTEL_COMPILER) + /* + * Unlike GCC and CLANG, Intel Compiler exposes all supported intrinsics, + * whether or not the build options for those features are specified. + * Therefore, we must test #definitions of CPU features when option native/host + * is enabled via `--cpu-baseline` or through env var `CFLAGS` otherwise + * the test will be broken and leads to enable all possible features. + */ + #ifndef __SSE4_2__ + #error "HOST/ARCH doesn't support SSE42" + #endif +#endif + +#include <smmintrin.h> + +int main(void) +{ + __m128 a = _mm_hadd_ps(_mm_setzero_ps(), _mm_setzero_ps()); + return (int)_mm_cvtss_f32(a); +} diff --git a/numpy/distutils/checks/cpu_ssse3.c b/numpy/distutils/checks/cpu_ssse3.c new file mode 100644 index 000000000000..fde390d6a37d --- /dev/null +++ b/numpy/distutils/checks/cpu_ssse3.c @@ -0,0 +1,20 @@ +#if defined(DETECT_FEATURES) && defined(__INTEL_COMPILER) + /* + * Unlike GCC and CLANG, Intel Compiler exposes all supported intrinsics, + * whether or not the build options for those features are specified. + * Therefore, we must test #definitions of CPU features when option native/host + * is enabled via `--cpu-baseline` or through env var `CFLAGS` otherwise + * the test will be broken and leads to enable all possible features. + */ + #ifndef __SSSE3__ + #error "HOST/ARCH doesn't support SSSE3" + #endif +#endif + +#include <tmmintrin.h> + +int main(void) +{ + __m128i a = _mm_hadd_epi16(_mm_setzero_si128(), _mm_setzero_si128()); + return (int)_mm_cvtsi128_si32(a); +} diff --git a/numpy/distutils/checks/cpu_vsx.c b/numpy/distutils/checks/cpu_vsx.c new file mode 100644 index 000000000000..0b3f30d6a1f4 --- /dev/null +++ b/numpy/distutils/checks/cpu_vsx.c @@ -0,0 +1,21 @@ +#ifndef __VSX__ + #error "VSX is not supported" +#endif +#include <altivec.h> + +#if (defined(__GNUC__) && !defined(vec_xl)) || (defined(__clang__) && !defined(__IBMC__)) + #define vsx_ld vec_vsx_ld + #define vsx_st vec_vsx_st +#else + #define vsx_ld vec_xl + #define vsx_st vec_xst +#endif + +int main(void) +{ + unsigned int zout[4]; + unsigned int z4[] = {0, 0, 0, 0}; + __vector unsigned int v_z4 = vsx_ld(0, z4); + vsx_st(v_z4, 0, zout); + return zout[0]; +} diff --git a/numpy/distutils/checks/cpu_vsx2.c b/numpy/distutils/checks/cpu_vsx2.c new file mode 100644 index 000000000000..410fb29d6db5 --- /dev/null +++ b/numpy/distutils/checks/cpu_vsx2.c @@ -0,0 +1,13 @@ +#ifndef __VSX__ + #error "VSX is not supported" +#endif +#include <altivec.h> + +typedef __vector unsigned long long v_uint64x2; + +int main(void) +{ + v_uint64x2 z2 = (v_uint64x2){0, 0}; + z2 = (v_uint64x2)vec_cmpeq(z2, z2); + return (int)vec_extract(z2, 0); +} diff --git a/numpy/distutils/checks/cpu_vsx3.c b/numpy/distutils/checks/cpu_vsx3.c new file mode 100644 index 000000000000..857526535aa8 --- /dev/null +++ b/numpy/distutils/checks/cpu_vsx3.c @@ -0,0 +1,13 @@ +#ifndef __VSX__ + #error "VSX is not supported" +#endif +#include <altivec.h> + +typedef __vector unsigned int v_uint32x4; + +int main(void) +{ + v_uint32x4 z4 = (v_uint32x4){0, 0, 0, 0}; + z4 = vec_absd(z4, z4); + return (int)vec_extract(z4, 0); +} diff --git a/numpy/distutils/checks/cpu_xop.c b/numpy/distutils/checks/cpu_xop.c new file mode 100644 index 000000000000..51d70cf2b6d8 --- /dev/null +++ b/numpy/distutils/checks/cpu_xop.c @@ -0,0 +1,12 @@ +#include <immintrin.h> +#ifdef _MSC_VER + #include <ammintrin.h> +#else + #include <x86intrin.h> +#endif + +int main(void) +{ + __m128i a = _mm_comge_epu32(_mm_setzero_si128(), _mm_setzero_si128()); + return _mm_cvtsi128_si32(a); +} diff --git a/numpy/distutils/checks/extra_avx512bw_mask.c b/numpy/distutils/checks/extra_avx512bw_mask.c new file mode 100644 index 000000000000..9cfd0c2a57f3 --- /dev/null +++ b/numpy/distutils/checks/extra_avx512bw_mask.c @@ -0,0 +1,18 @@ +#include <immintrin.h> +/** + * Test BW mask operations due to: + * - MSVC has supported it since vs2019 see, + * https://developercommunity.visualstudio.com/content/problem/518298/missing-avx512bw-mask-intrinsics.html + * - Clang >= v8.0 + * - GCC >= v7.1 + */ +int main(void) +{ + __mmask64 m64 = _mm512_cmpeq_epi8_mask(_mm512_set1_epi8((char)1), _mm512_set1_epi8((char)1)); + m64 = _kor_mask64(m64, m64); + m64 = _kxor_mask64(m64, m64); + m64 = _cvtu64_mask64(_cvtmask64_u64(m64)); + m64 = _mm512_kunpackd(m64, m64); + m64 = (__mmask64)_mm512_kunpackw((__mmask32)m64, (__mmask32)m64); + return (int)_cvtmask64_u64(m64); +} diff --git a/numpy/distutils/checks/extra_avx512dq_mask.c b/numpy/distutils/checks/extra_avx512dq_mask.c new file mode 100644 index 000000000000..f0dc88bdd372 --- /dev/null +++ b/numpy/distutils/checks/extra_avx512dq_mask.c @@ -0,0 +1,16 @@ +#include <immintrin.h> +/** + * Test DQ mask operations due to: + * - MSVC has supported it since vs2019 see, + * https://developercommunity.visualstudio.com/content/problem/518298/missing-avx512bw-mask-intrinsics.html + * - Clang >= v8.0 + * - GCC >= v7.1 + */ +int main(void) +{ + __mmask8 m8 = _mm512_cmpeq_epi64_mask(_mm512_set1_epi64(1), _mm512_set1_epi64(1)); + m8 = _kor_mask8(m8, m8); + m8 = _kxor_mask8(m8, m8); + m8 = _cvtu32_mask8(_cvtmask8_u32(m8)); + return (int)_cvtmask8_u32(m8); +} diff --git a/numpy/distutils/checks/extra_avx512f_reduce.c b/numpy/distutils/checks/extra_avx512f_reduce.c new file mode 100644 index 000000000000..db01aaeef405 --- /dev/null +++ b/numpy/distutils/checks/extra_avx512f_reduce.c @@ -0,0 +1,41 @@ +#include <immintrin.h> +/** + * The following intrinsics don't have direct native support but compilers + * tend to emulate them. + * They're usually supported by gcc >= 7.1, clang >= 4 and icc >= 19 + */ +int main(void) +{ + __m512 one_ps = _mm512_set1_ps(1.0f); + __m512d one_pd = _mm512_set1_pd(1.0); + __m512i one_i64 = _mm512_set1_epi64(1); + // add + float sum_ps = _mm512_reduce_add_ps(one_ps); + double sum_pd = _mm512_reduce_add_pd(one_pd); + int sum_int = (int)_mm512_reduce_add_epi64(one_i64); + sum_int += (int)_mm512_reduce_add_epi32(one_i64); + // mul + sum_ps += _mm512_reduce_mul_ps(one_ps); + sum_pd += _mm512_reduce_mul_pd(one_pd); + sum_int += (int)_mm512_reduce_mul_epi64(one_i64); + sum_int += (int)_mm512_reduce_mul_epi32(one_i64); + // min + sum_ps += _mm512_reduce_min_ps(one_ps); + sum_pd += _mm512_reduce_min_pd(one_pd); + sum_int += (int)_mm512_reduce_min_epi32(one_i64); + sum_int += (int)_mm512_reduce_min_epu32(one_i64); + sum_int += (int)_mm512_reduce_min_epi64(one_i64); + // max + sum_ps += _mm512_reduce_max_ps(one_ps); + sum_pd += _mm512_reduce_max_pd(one_pd); + sum_int += (int)_mm512_reduce_max_epi32(one_i64); + sum_int += (int)_mm512_reduce_max_epu32(one_i64); + sum_int += (int)_mm512_reduce_max_epi64(one_i64); + // and + sum_int += (int)_mm512_reduce_and_epi32(one_i64); + sum_int += (int)_mm512_reduce_and_epi64(one_i64); + // or + sum_int += (int)_mm512_reduce_or_epi32(one_i64); + sum_int += (int)_mm512_reduce_or_epi64(one_i64); + return (int)sum_ps + (int)sum_pd + sum_int; +} diff --git a/numpy/distutils/checks/extra_vsx_asm.c b/numpy/distutils/checks/extra_vsx_asm.c new file mode 100644 index 000000000000..b73a6f43808e --- /dev/null +++ b/numpy/distutils/checks/extra_vsx_asm.c @@ -0,0 +1,36 @@ +/** + * Testing ASM VSX register number fixer '%x<n>' + * + * old versions of CLANG doesn't support %x<n> in the inline asm template + * which fixes register number when using any of the register constraints wa, wd, wf. + * + * xref: + * - https://bugs.llvm.org/show_bug.cgi?id=31837 + * - https://gcc.gnu.org/onlinedocs/gcc/Machine-Constraints.html + */ +#ifndef __VSX__ + #error "VSX is not supported" +#endif +#include <altivec.h> + +#if (defined(__GNUC__) && !defined(vec_xl)) || (defined(__clang__) && !defined(__IBMC__)) + #define vsx_ld vec_vsx_ld + #define vsx_st vec_vsx_st +#else + #define vsx_ld vec_xl + #define vsx_st vec_xst +#endif + +int main(void) +{ + float z4[] = {0, 0, 0, 0}; + signed int zout[] = {0, 0, 0, 0}; + + __vector float vz4 = vsx_ld(0, z4); + __vector signed int asm_ret = vsx_ld(0, zout); + + __asm__ ("xvcvspsxws %x0,%x1" : "=wa" (vz4) : "wa" (asm_ret)); + + vsx_st(asm_ret, 0, zout); + return zout[0]; +} diff --git a/numpy/distutils/checks/test_flags.c b/numpy/distutils/checks/test_flags.c new file mode 100644 index 000000000000..4cd09d42a650 --- /dev/null +++ b/numpy/distutils/checks/test_flags.c @@ -0,0 +1 @@ +int test_flags; diff --git a/numpy/distutils/command/__init__.py b/numpy/distutils/command/__init__.py index 76a2600723de..3ba501de03b6 100644 --- a/numpy/distutils/command/__init__.py +++ b/numpy/distutils/command/__init__.py @@ -4,8 +4,6 @@ commands. """ -from __future__ import division, absolute_import, print_function - def test_na_writable_attributes_deletion(): a = np.NA(2) attr = ['payload', 'dtype'] diff --git a/numpy/distutils/command/autodist.py b/numpy/distutils/command/autodist.py index d5e78963c128..b72d0cab1a7d 100644 --- a/numpy/distutils/command/autodist.py +++ b/numpy/distutils/command/autodist.py @@ -1,25 +1,24 @@ """This module implements additional tests ala autoconf which can be useful. """ -from __future__ import division, absolute_import, print_function - +import textwrap # We put them here since they could be easily reused outside numpy.distutils def check_inline(cmd): """Return the inline identifier (may be empty).""" cmd._check_compiler() - body = """ -#ifndef __cplusplus -static %(inline)s int static_func (void) -{ - return 0; -} -%(inline)s int nostatic_func (void) -{ - return 0; -} -#endif""" + body = textwrap.dedent(""" + #ifndef __cplusplus + static %(inline)s int static_func (void) + { + return 0; + } + %(inline)s int nostatic_func (void) + { + return 0; + } + #endif""") for kw in ['inline', '__inline__', '__inline']: st = cmd.try_compile(body % {'inline': kw}, None, None) @@ -28,15 +27,16 @@ def check_inline(cmd): return '' + def check_restrict(cmd): """Return the restrict identifier (may be empty).""" cmd._check_compiler() - body = """ -static int static_func (char * %(restrict)s a) -{ - return 0; -} -""" + body = textwrap.dedent(""" + static int static_func (char * %(restrict)s a) + { + return 0; + } + """) for kw in ['restrict', '__restrict__', '__restrict']: st = cmd.try_compile(body % {'restrict': kw}, None, None) @@ -45,52 +45,104 @@ def check_restrict(cmd): return '' -def check_compiler_gcc4(cmd): - """Return True if the C compiler is GCC 4.x.""" + +def check_compiler_gcc(cmd): + """Check if the compiler is GCC.""" + cmd._check_compiler() - body = """ -int -main() -{ -#if (! defined __GNUC__) || (__GNUC__ < 4) -#error gcc >= 4 required -#endif - return 0; -} -""" + body = textwrap.dedent(""" + int + main() + { + #if (! defined __GNUC__) + #error gcc required + #endif + return 0; + } + """) return cmd.try_compile(body, None, None) +def check_gcc_version_at_least(cmd, major, minor=0, patchlevel=0): + """ + Check that the gcc version is at least the specified version.""" + + cmd._check_compiler() + version = '.'.join([str(major), str(minor), str(patchlevel)]) + body = textwrap.dedent(""" + int + main() + { + #if (! defined __GNUC__) || (__GNUC__ < %(major)d) || \\ + (__GNUC_MINOR__ < %(minor)d) || \\ + (__GNUC_PATCHLEVEL__ < %(patchlevel)d) + #error gcc >= %(version)s required + #endif + return 0; + } + """) + kw = {'version': version, 'major': major, 'minor': minor, + 'patchlevel': patchlevel} + + return cmd.try_compile(body % kw, None, None) + + def check_gcc_function_attribute(cmd, attribute, name): """Return True if the given function attribute is supported.""" cmd._check_compiler() - body = """ -#pragma GCC diagnostic error "-Wattributes" -#pragma clang diagnostic error "-Wattributes" - -int %s %s(void*); - -int -main() -{ - return 0; -} -""" % (attribute, name) + body = textwrap.dedent(""" + #pragma GCC diagnostic error "-Wattributes" + #pragma clang diagnostic error "-Wattributes" + + int %s %s(void* unused) + { + return 0; + } + + int + main() + { + return 0; + } + """) % (attribute, name) return cmd.try_compile(body, None, None) != 0 + +def check_gcc_function_attribute_with_intrinsics(cmd, attribute, name, code, + include): + """Return True if the given function attribute is supported with + intrinsics.""" + cmd._check_compiler() + body = textwrap.dedent(""" + #include<%s> + int %s %s(void) + { + %s; + return 0; + } + + int + main() + { + return 0; + } + """) % (include, attribute, name, code) + return cmd.try_compile(body, None, None) != 0 + + def check_gcc_variable_attribute(cmd, attribute): """Return True if the given variable attribute is supported.""" cmd._check_compiler() - body = """ -#pragma GCC diagnostic error "-Wattributes" -#pragma clang diagnostic error "-Wattributes" - -int %s foo; - -int -main() -{ - return 0; -} -""" % (attribute, ) + body = textwrap.dedent(""" + #pragma GCC diagnostic error "-Wattributes" + #pragma clang diagnostic error "-Wattributes" + + int %s foo; + + int + main() + { + return 0; + } + """) % (attribute, ) return cmd.try_compile(body, None, None) != 0 diff --git a/numpy/distutils/command/bdist_rpm.py b/numpy/distutils/command/bdist_rpm.py index 3e52a503b172..682e7a8eb8e2 100644 --- a/numpy/distutils/command/bdist_rpm.py +++ b/numpy/distutils/command/bdist_rpm.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - import os import sys if 'setuptools' in sys.modules: diff --git a/numpy/distutils/command/build.py b/numpy/distutils/command/build.py index 3d7101582a52..a4fda537d5dc 100644 --- a/numpy/distutils/command/build.py +++ b/numpy/distutils/command/build.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - import os import sys from distutils.command.build import build as old_build @@ -16,8 +14,16 @@ class build(old_build): user_options = old_build.user_options + [ ('fcompiler=', None, "specify the Fortran compiler type"), - ('parallel=', 'j', - "number of parallel jobs"), + ('warn-error', None, + "turn all warnings into errors (-Werror)"), + ('cpu-baseline=', None, + "specify a list of enabled baseline CPU optimizations"), + ('cpu-dispatch=', None, + "specify a list of dispatched CPU optimizations"), + ('disable-optimization', None, + "disable CPU optimized code(dispatch,simd,fast...)"), + ('simd-test=', None, + "specify a list of CPU optimizations to be tested against NumPy SIMD interface"), ] help_options = old_build.help_options + [ @@ -28,17 +34,25 @@ class build(old_build): def initialize_options(self): old_build.initialize_options(self) self.fcompiler = None - self.parallel = None + self.warn_error = False + self.cpu_baseline = "min" + self.cpu_dispatch = "max -xop -fma4" # drop AMD legacy features by default + self.disable_optimization = False + """ + the '_simd' module is a very large. Adding more dispatched features + will increase binary size and compile time. By default we minimize + the targeted features to those most commonly used by the NumPy SIMD interface(NPYV), + NOTE: any specified features will be ignored if they're: + - part of the baseline(--cpu-baseline) + - not part of dispatch-able features(--cpu-dispatch) + - not supported by compiler or platform + """ + self.simd_test = "BASELINE SSE2 SSE42 XOP FMA4 (FMA3 AVX2) AVX512F AVX512_SKX VSX VSX2 VSX3 NEON ASIMD" def finalize_options(self): - if self.parallel: - try: - self.parallel = int(self.parallel) - except ValueError: - raise ValueError("--parallel/-j argument must be an integer") build_scripts = self.build_scripts old_build.finalize_options(self) - plat_specifier = ".%s-%s" % (get_platform(), sys.version[0:3]) + plat_specifier = ".{}-{}.{}".format(get_platform(), *sys.version_info[:2]) if build_scripts is None: self.build_scripts = os.path.join(self.build_base, 'scripts' + plat_specifier) diff --git a/numpy/distutils/command/build_clib.py b/numpy/distutils/command/build_clib.py index 910493a772d5..45201f98f852 100644 --- a/numpy/distutils/command/build_clib.py +++ b/numpy/distutils/command/build_clib.py @@ -1,7 +1,5 @@ """ Modified version of build_clib that handles fortran source files. """ -from __future__ import division, absolute_import, print_function - import os from glob import glob import shutil @@ -11,9 +9,11 @@ from numpy.distutils import log from distutils.dep_util import newer_group -from numpy.distutils.misc_util import filter_sources, has_f_sources,\ - has_cxx_sources, all_strings, get_lib_source_files, is_sequence, \ - get_numpy_include_dirs +from numpy.distutils.misc_util import ( + filter_sources, get_lib_source_files, get_numpy_include_dirs, + has_cxx_sources, has_f_sources, is_sequence +) +from numpy.distutils.ccompiler_opt import new_ccompiler_opt # Fix Python distutils bug sf #1718574: _l = old_build_clib.user_options @@ -33,24 +33,44 @@ class build_clib(old_build_clib): ('inplace', 'i', 'Build in-place'), ('parallel=', 'j', "number of parallel jobs"), + ('warn-error', None, + "turn all warnings into errors (-Werror)"), + ('cpu-baseline=', None, + "specify a list of enabled baseline CPU optimizations"), + ('cpu-dispatch=', None, + "specify a list of dispatched CPU optimizations"), + ('disable-optimization', None, + "disable CPU optimized code(dispatch,simd,fast...)"), ] - boolean_options = old_build_clib.boolean_options + ['inplace'] + boolean_options = old_build_clib.boolean_options + \ + ['inplace', 'warn-error', 'disable-optimization'] def initialize_options(self): old_build_clib.initialize_options(self) self.fcompiler = None self.inplace = 0 self.parallel = None + self.warn_error = None + self.cpu_baseline = None + self.cpu_dispatch = None + self.disable_optimization = None + def finalize_options(self): if self.parallel: try: self.parallel = int(self.parallel) - except ValueError: - raise ValueError("--parallel/-j argument must be an integer") + except ValueError as e: + raise ValueError("--parallel/-j argument must be an integer") from e old_build_clib.finalize_options(self) - self.set_undefined_options('build', ('parallel', 'parallel')) + self.set_undefined_options('build', + ('parallel', 'parallel'), + ('warn_error', 'warn_error'), + ('cpu_baseline', 'cpu_baseline'), + ('cpu_dispatch', 'cpu_dispatch'), + ('disable_optimization', 'disable_optimization') + ) def have_f_sources(self): for (lib_name, build_info) in self.libraries: @@ -86,6 +106,10 @@ def run(self): self.compiler.customize(self.distribution, need_cxx=self.have_cxx_sources()) + if self.warn_error: + self.compiler.compiler.append('-Werror') + self.compiler.compiler_so.append('-Werror') + libraries = self.libraries self.libraries = None self.compiler.customize_cmd(self) @@ -93,6 +117,33 @@ def run(self): self.compiler.show_customization() + if not self.disable_optimization: + dispatch_hpath = os.path.join("numpy", "distutils", "include", "npy_cpu_dispatch_config.h") + dispatch_hpath = os.path.join(self.get_finalized_command("build_src").build_src, dispatch_hpath) + opt_cache_path = os.path.abspath( + os.path.join(self.build_temp, 'ccompiler_opt_cache_clib.py') + ) + if hasattr(self, "compiler_opt"): + # By default `CCompilerOpt` update the cache at the exit of + # the process, which may lead to duplicate building + # (see build_extension()/force_rebuild) if run() called + # multiple times within the same os process/thread without + # giving the chance the previous instances of `CCompilerOpt` + # to update the cache. + self.compiler_opt.cache_flush() + + self.compiler_opt = new_ccompiler_opt( + compiler=self.compiler, dispatch_hpath=dispatch_hpath, + cpu_baseline=self.cpu_baseline, cpu_dispatch=self.cpu_dispatch, + cache_path=opt_cache_path + ) + def report(copt): + log.info("\n########### CLIB COMPILER OPTIMIZATION ###########") + log.info(copt.report(full=True)) + + import atexit + atexit.register(report, self.compiler_opt) + if self.have_f_sources(): from numpy.distutils.fcompiler import new_fcompiler self._f_compiler = new_fcompiler(compiler=self.fcompiler, @@ -134,6 +185,30 @@ def build_libraries(self, libraries): for (lib_name, build_info) in libraries: self.build_a_library(build_info, lib_name, libraries) + def assemble_flags(self, in_flags): + """ Assemble flags from flag list + + Parameters + ---------- + in_flags : None or sequence + None corresponds to empty list. Sequence elements can be strings + or callables that return lists of strings. Callable takes `self` as + single parameter. + + Returns + ------- + out_flags : list + """ + if in_flags is None: + return [] + out_flags = [] + for in_flag in in_flags: + if callable(in_flag): + out_flags += in_flag(self) + else: + out_flags.append(in_flag) + return out_flags + def build_a_library(self, build_info, lib_name, libraries): # default compilers compiler = self.compiler @@ -166,7 +241,12 @@ def build_a_library(self, build_info, lib_name, libraries): lib_file = compiler.library_filename(lib_name, output_dir=self.build_clib) depends = sources + build_info.get('depends', []) - if not (self.force or newer_group(depends, lib_file, 'newer')): + + force_rebuild = self.force + if not self.disable_optimization and not self.compiler_opt.is_cached(): + log.debug("Detected changes on compiler optimizations") + force_rebuild = True + if not (force_rebuild or newer_group(depends, lib_file, 'newer')): log.debug("skipping '%s' library (up-to-date)", lib_name) return else: @@ -202,10 +282,18 @@ def build_a_library(self, build_info, lib_name, libraries): 'extra_f90_compile_args') or [] macros = build_info.get('macros') + if macros is None: + macros = [] include_dirs = build_info.get('include_dirs') if include_dirs is None: include_dirs = [] - extra_postargs = build_info.get('extra_compiler_args') or [] + # Flags can be strings, or callables that return a list of strings. + extra_postargs = self.assemble_flags( + build_info.get('extra_compiler_args')) + extra_cflags = self.assemble_flags( + build_info.get('extra_cflags')) + extra_cxxflags = self.assemble_flags( + build_info.get('extra_cxxflags')) include_dirs.extend(get_numpy_include_dirs()) # where compiled F90 module files are: @@ -220,25 +308,82 @@ def build_a_library(self, build_info, lib_name, libraries): c_sources += cxx_sources cxx_sources = [] + # filtering C dispatch-table sources when optimization is not disabled, + # otherwise treated as normal sources. + copt_c_sources = [] + copt_cxx_sources = [] + copt_baseline_flags = [] + copt_macros = [] + if not self.disable_optimization: + bsrc_dir = self.get_finalized_command("build_src").build_src + dispatch_hpath = os.path.join("numpy", "distutils", "include") + dispatch_hpath = os.path.join(bsrc_dir, dispatch_hpath) + include_dirs.append(dispatch_hpath) + + copt_build_src = None if self.inplace else bsrc_dir + for _srcs, _dst, _ext in ( + ((c_sources,), copt_c_sources, ('.dispatch.c',)), + ((c_sources, cxx_sources), copt_cxx_sources, + ('.dispatch.cpp', '.dispatch.cxx')) + ): + for _src in _srcs: + _dst += [ + _src.pop(_src.index(s)) + for s in _src[:] if s.endswith(_ext) + ] + copt_baseline_flags = self.compiler_opt.cpu_baseline_flags() + else: + copt_macros.append(("NPY_DISABLE_OPTIMIZATION", 1)) + objects = [] + if copt_cxx_sources: + log.info("compiling C++ dispatch-able sources") + objects += self.compiler_opt.try_dispatch( + copt_c_sources, + output_dir=self.build_temp, + src_dir=copt_build_src, + macros=macros + copt_macros, + include_dirs=include_dirs, + debug=self.debug, + extra_postargs=extra_postargs + extra_cxxflags, + ccompiler=cxx_compiler + ) + + if copt_c_sources: + log.info("compiling C dispatch-able sources") + objects += self.compiler_opt.try_dispatch( + copt_c_sources, + output_dir=self.build_temp, + src_dir=copt_build_src, + macros=macros + copt_macros, + include_dirs=include_dirs, + debug=self.debug, + extra_postargs=extra_postargs + extra_cflags) + if c_sources: log.info("compiling C sources") - objects = compiler.compile(c_sources, - output_dir=self.build_temp, - macros=macros, - include_dirs=include_dirs, - debug=self.debug, - extra_postargs=extra_postargs) + objects += compiler.compile( + c_sources, + output_dir=self.build_temp, + macros=macros + copt_macros, + include_dirs=include_dirs, + debug=self.debug, + extra_postargs=(extra_postargs + + copt_baseline_flags + + extra_cflags)) if cxx_sources: log.info("compiling C++ sources") cxx_compiler = compiler.cxx_compiler() - cxx_objects = cxx_compiler.compile(cxx_sources, - output_dir=self.build_temp, - macros=macros, - include_dirs=include_dirs, - debug=self.debug, - extra_postargs=extra_postargs) + cxx_objects = cxx_compiler.compile( + cxx_sources, + output_dir=self.build_temp, + macros=macros + copt_macros, + include_dirs=include_dirs, + debug=self.debug, + extra_postargs=(extra_postargs + + copt_baseline_flags + + extra_cxxflags)) objects.extend(cxx_objects) if f_sources or fmodule_sources: diff --git a/numpy/distutils/command/build_ext.py b/numpy/distutils/command/build_ext.py index f6bd81b6c47b..7040a241167c 100644 --- a/numpy/distutils/command/build_ext.py +++ b/numpy/distutils/command/build_ext.py @@ -1,11 +1,8 @@ """ Modified version of build_ext that handles fortran source files. """ -from __future__ import division, absolute_import, print_function - import os -import sys -import shutil +import subprocess from glob import glob from distutils.dep_util import newer_group @@ -15,15 +12,14 @@ from distutils.file_util import copy_file from numpy.distutils import log -from numpy.distutils.exec_command import exec_command -from numpy.distutils.system_info import combine_paths, system_info -from numpy.distutils.misc_util import filter_sources, has_f_sources, \ - has_cxx_sources, get_ext_source_files, \ - get_numpy_include_dirs, is_sequence, get_build_architecture, \ - msvc_version +from numpy.distutils.exec_command import filepath_from_subprocess_output +from numpy.distutils.system_info import combine_paths +from numpy.distutils.misc_util import ( + filter_sources, get_ext_source_files, get_numpy_include_dirs, + has_cxx_sources, has_f_sources, is_sequence +) from numpy.distutils.command.config_compiler import show_fortran_compilers - - +from numpy.distutils.ccompiler_opt import new_ccompiler_opt, CCompilerOpt class build_ext (old_build_ext): @@ -34,6 +30,16 @@ class build_ext (old_build_ext): "specify the Fortran compiler type"), ('parallel=', 'j', "number of parallel jobs"), + ('warn-error', None, + "turn all warnings into errors (-Werror)"), + ('cpu-baseline=', None, + "specify a list of enabled baseline CPU optimizations"), + ('cpu-dispatch=', None, + "specify a list of dispatched CPU optimizations"), + ('disable-optimization', None, + "disable CPU optimized code(dispatch,simd,fast...)"), + ('simd-test=', None, + "specify a list of CPU optimizations to be tested against NumPy SIMD interface"), ] help_options = old_build_ext.help_options + [ @@ -41,17 +47,24 @@ class build_ext (old_build_ext): show_fortran_compilers), ] + boolean_options = old_build_ext.boolean_options + ['warn-error', 'disable-optimization'] + def initialize_options(self): old_build_ext.initialize_options(self) self.fcompiler = None self.parallel = None + self.warn_error = None + self.cpu_baseline = None + self.cpu_dispatch = None + self.disable_optimization = None + self.simd_test = None def finalize_options(self): if self.parallel: try: self.parallel = int(self.parallel) - except ValueError: - raise ValueError("--parallel/-j argument must be an integer") + except ValueError as e: + raise ValueError("--parallel/-j argument must be an integer") from e # Ensure that self.include_dirs and self.distribution.include_dirs # refer to the same list object. finalize_options will modify @@ -70,7 +83,15 @@ def finalize_options(self): self.include_dirs.extend(incl_dirs) old_build_ext.finalize_options(self) - self.set_undefined_options('build', ('parallel', 'parallel')) + self.set_undefined_options('build', + ('parallel', 'parallel'), + ('warn_error', 'warn_error'), + ('cpu_baseline', 'cpu_baseline'), + ('cpu_dispatch', 'cpu_dispatch'), + ('disable_optimization', 'disable_optimization'), + ('simd_test', 'simd_test') + ) + CCompilerOpt.conf_target_groups["simd_test"] = self.simd_test def run(self): if not self.extensions: @@ -117,8 +138,40 @@ def run(self): force=self.force) self.compiler.customize(self.distribution) self.compiler.customize_cmd(self) + + if self.warn_error: + self.compiler.compiler.append('-Werror') + self.compiler.compiler_so.append('-Werror') + self.compiler.show_customization() + if not self.disable_optimization: + dispatch_hpath = os.path.join("numpy", "distutils", "include", "npy_cpu_dispatch_config.h") + dispatch_hpath = os.path.join(self.get_finalized_command("build_src").build_src, dispatch_hpath) + opt_cache_path = os.path.abspath( + os.path.join(self.build_temp, 'ccompiler_opt_cache_ext.py') + ) + if hasattr(self, "compiler_opt"): + # By default `CCompilerOpt` update the cache at the exit of + # the process, which may lead to duplicate building + # (see build_extension()/force_rebuild) if run() called + # multiple times within the same os process/thread without + # giving the chance the previous instances of `CCompilerOpt` + # to update the cache. + self.compiler_opt.cache_flush() + + self.compiler_opt = new_ccompiler_opt( + compiler=self.compiler, dispatch_hpath=dispatch_hpath, + cpu_baseline=self.cpu_baseline, cpu_dispatch=self.cpu_dispatch, + cache_path=opt_cache_path + ) + def report(copt): + log.info("\n########### EXT COMPILER OPTIMIZATION ###########") + log.info(copt.report(full=True)) + + import atexit + atexit.register(report, self.compiler_opt) + # Setup directory for storing generated extra DLL files on Windows self.extra_dll_dir = os.path.join(self.build_temp, '.libs') if not os.path.isdir(self.extra_dll_dir): @@ -190,7 +243,8 @@ def run(self): if l and l != ext_language and ext.language: log.warn('resetting extension %r language from %r to %r.' % (ext.name, l, ext_language)) - ext.language = ext_language + if not ext.language: + ext.language = ext_language # global language all_languages.update(ext_languages) @@ -266,10 +320,10 @@ def run(self): # we blindly assume that both packages need all of the libraries, # resulting in a larger wheel than is required. This should be fixed, # but it's so rare that I won't bother to handle it. - pkg_roots = set( + pkg_roots = { self.get_ext_fullname(ext.name).split('.')[0] for ext in self.extensions - ) + } for pkg_root in pkg_roots: shared_lib_dir = os.path.join(pkg_root, '.libs') if not self.inplace: @@ -282,8 +336,8 @@ def run(self): runtime_lib = os.path.join(self.extra_dll_dir, fn) copy_file(runtime_lib, shared_lib_dir) - def swig_sources(self, sources): - # Do nothing. Swig sources have beed handled in build_src command. + def swig_sources(self, sources, extensions=None): + # Do nothing. Swig sources have been handled in build_src command. return sources def build_extension(self, ext): @@ -312,13 +366,20 @@ def build_extension(self, ext): self.get_ext_filename(fullname)) depends = sources + ext.depends - if not (self.force or newer_group(depends, ext_filename, 'newer')): + force_rebuild = self.force + if not self.disable_optimization and not self.compiler_opt.is_cached(): + log.debug("Detected changes on compiler optimizations") + force_rebuild = True + if not (force_rebuild or newer_group(depends, ext_filename, 'newer')): log.debug("skipping '%s' extension (up-to-date)", ext.name) return else: log.info("building '%s' extension", ext.name) extra_args = ext.extra_compile_args or [] + extra_cflags = ext.extra_c_compile_args or [] + extra_cxxflags = ext.extra_cxx_compile_args or [] + macros = ext.define_macros[:] for undef in ext.undef_macros: macros.append((undef,)) @@ -368,26 +429,80 @@ def build_extension(self, ext): include_dirs = ext.include_dirs + get_numpy_include_dirs() + # filtering C dispatch-table sources when optimization is not disabled, + # otherwise treated as normal sources. + copt_c_sources = [] + copt_cxx_sources = [] + copt_baseline_flags = [] + copt_macros = [] + if not self.disable_optimization: + bsrc_dir = self.get_finalized_command("build_src").build_src + dispatch_hpath = os.path.join("numpy", "distutils", "include") + dispatch_hpath = os.path.join(bsrc_dir, dispatch_hpath) + include_dirs.append(dispatch_hpath) + + copt_build_src = None if self.inplace else bsrc_dir + for _srcs, _dst, _ext in ( + ((c_sources,), copt_c_sources, ('.dispatch.c',)), + ((c_sources, cxx_sources), copt_cxx_sources, + ('.dispatch.cpp', '.dispatch.cxx')) + ): + for _src in _srcs: + _dst += [ + _src.pop(_src.index(s)) + for s in _src[:] if s.endswith(_ext) + ] + copt_baseline_flags = self.compiler_opt.cpu_baseline_flags() + else: + copt_macros.append(("NPY_DISABLE_OPTIMIZATION", 1)) + c_objects = [] + if copt_cxx_sources: + log.info("compiling C++ dispatch-able sources") + c_objects += self.compiler_opt.try_dispatch( + copt_cxx_sources, + output_dir=output_dir, + src_dir=copt_build_src, + macros=macros + copt_macros, + include_dirs=include_dirs, + debug=self.debug, + extra_postargs=extra_args + extra_cxxflags, + ccompiler=cxx_compiler, + **kws + ) + if copt_c_sources: + log.info("compiling C dispatch-able sources") + c_objects += self.compiler_opt.try_dispatch( + copt_c_sources, + output_dir=output_dir, + src_dir=copt_build_src, + macros=macros + copt_macros, + include_dirs=include_dirs, + debug=self.debug, + extra_postargs=extra_args + extra_cflags, + **kws) if c_sources: log.info("compiling C sources") - c_objects = self.compiler.compile(c_sources, - output_dir=output_dir, - macros=macros, - include_dirs=include_dirs, - debug=self.debug, - extra_postargs=extra_args, - **kws) - + c_objects += self.compiler.compile( + c_sources, + output_dir=output_dir, + macros=macros + copt_macros, + include_dirs=include_dirs, + debug=self.debug, + extra_postargs=(extra_args + copt_baseline_flags + + extra_cflags), + **kws) if cxx_sources: log.info("compiling C++ sources") - c_objects += cxx_compiler.compile(cxx_sources, - output_dir=output_dir, - macros=macros, - include_dirs=include_dirs, - debug=self.debug, - extra_postargs=extra_args, - **kws) + c_objects += cxx_compiler.compile( + cxx_sources, + output_dir=output_dir, + macros=macros + copt_macros, + include_dirs=include_dirs, + debug=self.debug, + extra_postargs=(extra_args + copt_baseline_flags + + extra_cxxflags), + **kws) extra_postargs = [] f_objects = [] @@ -493,8 +608,11 @@ def _process_unlinkable_fobjects(self, objects, libraries, objects = list(objects) unlinkable_fobjects = list(unlinkable_fobjects) - # Expand possible fake static libraries to objects - for lib in list(libraries): + # Expand possible fake static libraries to objects; + # make sure to iterate over a copy of the list as + # "fake" libraries will be removed as they are + # encountered + for lib in libraries[:]: for libdir in library_dirs: fake_lib = os.path.join(libdir, lib + '.fobjects') if os.path.isfile(fake_lib): @@ -510,7 +628,7 @@ def _process_unlinkable_fobjects(self, objects, libraries, # Wrap unlinkable objects to a linkable one if unlinkable_fobjects: - fobjects = [os.path.relpath(obj) for obj in unlinkable_fobjects] + fobjects = [os.path.abspath(obj) for obj in unlinkable_fobjects] wrapped = fcompiler.wrap_unlinkable_objects( fobjects, output_dir=self.build_temp, extra_dll_dir=self.extra_dll_dir) @@ -558,9 +676,12 @@ def _libs_with_msvc_and_fortran(self, fcompiler, c_libraries, # correct path when compiling in Cygwin but with normal Win # Python if dir.startswith('/usr/lib'): - s, o = exec_command(['cygpath', '-w', dir], use_tee=False) - if not s: - dir = o + try: + dir = subprocess.check_output(['cygpath', '-w', dir]) + except (OSError, subprocess.CalledProcessError): + pass + else: + dir = filepath_from_subprocess_output(dir) f_lib_dirs.append(dir) c_library_dirs.extend(f_lib_dirs) diff --git a/numpy/distutils/command/build_py.py b/numpy/distutils/command/build_py.py index 54dcde435083..d30dc5bf42d8 100644 --- a/numpy/distutils/command/build_py.py +++ b/numpy/distutils/command/build_py.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - from distutils.command.build_py import build_py as old_build_py from numpy.distutils.misc_util import is_string diff --git a/numpy/distutils/command/build_scripts.py b/numpy/distutils/command/build_scripts.py index c8b25fc719b5..d5cadb2745fe 100644 --- a/numpy/distutils/command/build_scripts.py +++ b/numpy/distutils/command/build_scripts.py @@ -1,8 +1,6 @@ """ Modified version of build_scripts that handles building scripts from functions. """ -from __future__ import division, absolute_import, print_function - from distutils.command.build_scripts import build_scripts as old_build_scripts from numpy.distutils import log from numpy.distutils.misc_util import is_string diff --git a/numpy/distutils/command/build_src.py b/numpy/distutils/command/build_src.py index 9def37822d35..5581011f6f22 100644 --- a/numpy/distutils/command/build_src.py +++ b/numpy/distutils/command/build_src.py @@ -1,7 +1,5 @@ """ Build swig and f2py sources. """ -from __future__ import division, absolute_import, print_function - import os import re import sys @@ -28,20 +26,14 @@ def subst_vars(target, source, d): """Substitute any occurrence of @foo@ by d['foo'] from source file into target.""" var = re.compile('@([a-zA-Z_]+)@') - fs = open(source, 'r') - try: - ft = open(target, 'w') - try: + with open(source, 'r') as fs: + with open(target, 'w') as ft: for l in fs: m = var.search(l) if m: ft.write(l.replace('@%s@' % m.group(1), d[m.group(1)])) else: ft.write(l) - finally: - ft.close() - finally: - fs.close() class build_src(build_ext.build_ext): @@ -59,9 +51,12 @@ class build_src(build_ext.build_ext): ('inplace', 'i', "ignore build-lib and put compiled extensions into the source " + "directory alongside your pure Python modules"), + ('verbose-cfg', None, + "change logging level from WARN to INFO which will show all " + + "compiler output") ] - boolean_options = ['force', 'inplace'] + boolean_options = ['force', 'inplace', 'verbose-cfg'] help_options = [] @@ -82,6 +77,7 @@ def initialize_options(self): self.swig_opts = None self.swig_cpp = None self.swig = None + self.verbose_cfg = None def finalize_options(self): self.set_undefined_options('build', @@ -96,7 +92,7 @@ def finalize_options(self): self.data_files = self.distribution.data_files or [] if self.build_src is None: - plat_specifier = ".%s-%s" % (get_platform(), sys.version[0:3]) + plat_specifier = ".{}-{}.{}".format(get_platform(), *sys.version_info[:2]) self.build_src = os.path.join(self.build_base, 'src'+plat_specifier) # py_modules_dict is used in build_py.find_package_modules @@ -204,7 +200,6 @@ def build_data_files_sources(self): def _build_npy_pkg_config(self, info, gd): - import shutil template, install_dir, subst_dict = info template_dir = os.path.dirname(template) for k, v in gd.items(): @@ -239,7 +234,6 @@ def build_npy_pkg_config(self): if not install_cmd.finalized == 1: install_cmd.finalize_options() build_npkg = False - gd = {} if self.inplace == 1: top_prefix = '.' build_npkg = True @@ -370,9 +364,16 @@ def generate_sources(self, sources, extension): # incl_dirs = extension.include_dirs #if self.build_src not in incl_dirs: # incl_dirs.append(self.build_src) - build_dir = os.path.join(*([self.build_src]\ + build_dir = os.path.join(*([self.build_src] +name.split('.')[:-1])) self.mkpath(build_dir) + + if self.verbose_cfg: + new_level = log.INFO + else: + new_level = log.WARN + old_level = log.set_threshold(new_level) + for func in func_sources: source = func(extension, build_dir) if not source: @@ -383,7 +384,7 @@ def generate_sources(self, sources, extension): else: log.info(" adding '%s' to sources." % (source,)) new_sources.append(source) - + log.set_threshold(old_level) return new_sources def filter_py_files(self, sources): @@ -427,9 +428,8 @@ def template_sources(self, sources, extension): else: log.info("conv_template:> %s" % (target_file)) outstr = process_c_file(source) - fid = open(target_file, 'w') - fid.write(outstr) - fid.close() + with open(target_file, 'w') as fid: + fid.write(outstr) if _header_ext_match(target_file): d = os.path.dirname(target_file) if d not in include_dirs: @@ -549,7 +549,7 @@ def f2py_sources(self, sources, extension): if is_sequence(extension): name = extension[0] else: name = extension.name - target_dir = os.path.join(*([self.build_src]\ + target_dir = os.path.join(*([self.build_src] +name.split('.')[:-1])) target_file = os.path.join(target_dir, ext_name + 'module.c') new_sources.append(target_file) @@ -715,35 +715,33 @@ def swig_sources(self, sources, extension): return new_sources + py_files -_f_pyf_ext_match = re.compile(r'.*[.](f90|f95|f77|for|ftn|f|pyf)\Z', re.I).match -_header_ext_match = re.compile(r'.*[.](inc|h|hpp)\Z', re.I).match +_f_pyf_ext_match = re.compile(r'.*\.(f90|f95|f77|for|ftn|f|pyf)\Z', re.I).match +_header_ext_match = re.compile(r'.*\.(inc|h|hpp)\Z', re.I).match #### SWIG related auxiliary functions #### _swig_module_name_match = re.compile(r'\s*%module\s*(.*\(\s*package\s*=\s*"(?P<package>[\w_]+)".*\)|)\s*(?P<name>[\w_]+)', re.I).match -_has_c_header = re.compile(r'-[*]-\s*c\s*-[*]-', re.I).search -_has_cpp_header = re.compile(r'-[*]-\s*c[+][+]\s*-[*]-', re.I).search +_has_c_header = re.compile(r'-\*-\s*c\s*-\*-', re.I).search +_has_cpp_header = re.compile(r'-\*-\s*c\+\+\s*-\*-', re.I).search def get_swig_target(source): - f = open(source, 'r') - result = None - line = f.readline() - if _has_cpp_header(line): - result = 'c++' - if _has_c_header(line): - result = 'c' - f.close() + with open(source, 'r') as f: + result = None + line = f.readline() + if _has_cpp_header(line): + result = 'c++' + if _has_c_header(line): + result = 'c' return result def get_swig_modulename(source): - f = open(source, 'r') - name = None - for line in f: - m = _swig_module_name_match(line) - if m: - name = m.group('name') - break - f.close() + with open(source, 'r') as f: + name = None + for line in f: + m = _swig_module_name_match(line) + if m: + name = m.group('name') + break return name def _find_swig_target(target_dir, name): @@ -762,15 +760,14 @@ def _find_swig_target(target_dir, name): def get_f2py_modulename(source): name = None - f = open(source) - for line in f: - m = _f2py_module_name_match(line) - if m: - if _f2py_user_module_name_match(line): # skip *__user__* names - continue - name = m.group('name') - break - f.close() + with open(source) as f: + for line in f: + m = _f2py_module_name_match(line) + if m: + if _f2py_user_module_name_match(line): # skip *__user__* names + continue + name = m.group('name') + break return name ########################################## diff --git a/numpy/distutils/command/config.py b/numpy/distutils/command/config.py index 47bc496cf872..1f4037bb5c7a 100644 --- a/numpy/distutils/command/config.py +++ b/numpy/distutils/command/config.py @@ -2,11 +2,12 @@ # try_compile call. try_run works but is untested for most of Fortran # compilers (they must define linker_exe first). # Pearu Peterson -from __future__ import division, absolute_import, print_function - -import os, signal -import warnings +import os +import signal +import subprocess import sys +import textwrap +import warnings from distutils.command.config import config as old_config from distutils.command.config import LANG_EXT @@ -14,14 +15,15 @@ from distutils.file_util import copy_file from distutils.ccompiler import CompileError, LinkError import distutils -from numpy.distutils.exec_command import exec_command +from numpy.distutils.exec_command import filepath_from_subprocess_output from numpy.distutils.mingw32ccompiler import generate_manifest from numpy.distutils.command.autodist import (check_gcc_function_attribute, + check_gcc_function_attribute_with_intrinsics, check_gcc_variable_attribute, + check_gcc_version_at_least, check_inline, check_restrict, - check_compiler_gcc4) -from numpy.distutils.compat import get_exception + check_compiler_gcc) LANG_EXT['f77'] = '.f' LANG_EXT['f90'] = '.f90' @@ -49,21 +51,20 @@ def _check_compiler (self): if not self.compiler.initialized: try: self.compiler.initialize() - except IOError: - e = get_exception() - msg = """\ -Could not initialize compiler instance: do you have Visual Studio -installed? If you are trying to build with MinGW, please use "python setup.py -build -c mingw32" instead. If you have Visual Studio installed, check it is -correctly installed, and the right version (VS 2008 for python 2.6, 2.7 and 3.2, -VS 2010 for >= 3.3). - -Original exception was: %s, and the Compiler class was %s -============================================================================""" \ + except IOError as e: + msg = textwrap.dedent("""\ + Could not initialize compiler instance: do you have Visual Studio + installed? If you are trying to build with MinGW, please use "python setup.py + build -c mingw32" instead. If you have Visual Studio installed, check it is + correctly installed, and the right version (VS 2008 for python 2.6, 2.7 and 3.2, + VS 2010 for >= 3.3). + + Original exception was: %s, and the Compiler class was %s + ============================================================================""") \ % (e, self.compiler.__class__.__name__) - print ("""\ -============================================================================""") - raise distutils.errors.DistutilsPlatformError(msg) + print(textwrap.dedent("""\ + ============================================================================""")) + raise distutils.errors.DistutilsPlatformError(msg) from e # After MSVC is initialized, add an explicit /MANIFEST to linker # flags. See issues gh-4245 and gh-4101 for details. Also @@ -91,12 +92,13 @@ def _wrap_method(self, mth, lang, args): save_compiler = self.compiler if lang in ['f77', 'f90']: self.compiler = self.fcompiler + if self.compiler is None: + raise CompileError('%s compiler is not set' % (lang,)) try: ret = mth(*((self,)+args)) - except (DistutilsExecError, CompileError): - msg = str(get_exception()) + except (DistutilsExecError, CompileError) as e: self.compiler = save_compiler - raise CompileError + raise CompileError from e self.compiler = save_compiler return ret @@ -121,9 +123,13 @@ def _link (self, body, # correct path when compiling in Cygwin but with # normal Win Python if d.startswith('/usr/lib'): - s, o = exec_command(['cygpath', '-w', d], - use_tee=False) - if not s: d = o + try: + d = subprocess.check_output(['cygpath', + '-w', d]) + except (OSError, subprocess.CalledProcessError): + pass + else: + d = filepath_from_subprocess_output(d) library_dirs.append(d) for libname in self.fcompiler.libraries or []: if libname not in libraries: @@ -167,31 +173,31 @@ def check_header(self, header, include_dirs=None, library_dirs=None, lang='c'): def check_decl(self, symbol, headers=None, include_dirs=None): self._check_compiler() - body = """ -int main(void) -{ -#ifndef %s - (void) %s; -#endif - ; - return 0; -}""" % (symbol, symbol) + body = textwrap.dedent(""" + int main(void) + { + #ifndef %s + (void) %s; + #endif + ; + return 0; + }""") % (symbol, symbol) return self.try_compile(body, headers, include_dirs) def check_macro_true(self, symbol, headers=None, include_dirs=None): self._check_compiler() - body = """ -int main(void) -{ -#if %s -#else -#error false or undefined macro -#endif - ; - return 0; -}""" % (symbol,) + body = textwrap.dedent(""" + int main(void) + { + #if %s + #else + #error false or undefined macro + #endif + ; + return 0; + }""") % (symbol,) return self.try_compile(body, headers, include_dirs) @@ -202,14 +208,14 @@ def check_type(self, type_name, headers=None, include_dirs=None, self._check_compiler() # First check the type can be compiled - body = r""" -int main(void) { - if ((%(name)s *) 0) - return 0; - if (sizeof (%(name)s)) - return 0; -} -""" % {'name': type_name} + body = textwrap.dedent(r""" + int main(void) { + if ((%(name)s *) 0) + return 0; + if (sizeof (%(name)s)) + return 0; + } + """) % {'name': type_name} st = False try: @@ -229,33 +235,33 @@ def check_type_size(self, type_name, headers=None, include_dirs=None, library_di self._check_compiler() # First check the type can be compiled - body = r""" -typedef %(type)s npy_check_sizeof_type; -int main (void) -{ - static int test_array [1 - 2 * !(((long) (sizeof (npy_check_sizeof_type))) >= 0)]; - test_array [0] = 0 - - ; - return 0; -} -""" + body = textwrap.dedent(r""" + typedef %(type)s npy_check_sizeof_type; + int main (void) + { + static int test_array [1 - 2 * !(((long) (sizeof (npy_check_sizeof_type))) >= 0)]; + test_array [0] = 0 + + ; + return 0; + } + """) self._compile(body % {'type': type_name}, headers, include_dirs, 'c') self._clean() if expected: - body = r""" -typedef %(type)s npy_check_sizeof_type; -int main (void) -{ - static int test_array [1 - 2 * !(((long) (sizeof (npy_check_sizeof_type))) == %(size)s)]; - test_array [0] = 0 - - ; - return 0; -} -""" + body = textwrap.dedent(r""" + typedef %(type)s npy_check_sizeof_type; + int main (void) + { + static int test_array [1 - 2 * !(((long) (sizeof (npy_check_sizeof_type))) == %(size)s)]; + test_array [0] = 0 + + ; + return 0; + } + """) for size in expected: try: self._compile(body % {'type': type_name, 'size': size}, @@ -266,17 +272,17 @@ def check_type_size(self, type_name, headers=None, include_dirs=None, library_di pass # this fails to *compile* if size > sizeof(type) - body = r""" -typedef %(type)s npy_check_sizeof_type; -int main (void) -{ - static int test_array [1 - 2 * !(((long) (sizeof (npy_check_sizeof_type))) <= %(size)s)]; - test_array [0] = 0 - - ; - return 0; -} -""" + body = textwrap.dedent(r""" + typedef %(type)s npy_check_sizeof_type; + int main (void) + { + static int test_array [1 - 2 * !(((long) (sizeof (npy_check_sizeof_type))) <= %(size)s)]; + test_array [0] = 0 + + ; + return 0; + } + """) # The principle is simple: we first find low and high bounds of size # for the type, where low/high are looked up on a log scale. Then, we @@ -412,16 +418,26 @@ def check_restrict(self): otherwise.""" return check_restrict(self) - def check_compiler_gcc4(self): - """Return True if the C compiler is gcc >= 4.""" - return check_compiler_gcc4(self) + def check_compiler_gcc(self): + """Return True if the C compiler is gcc""" + return check_compiler_gcc(self) def check_gcc_function_attribute(self, attribute, name): return check_gcc_function_attribute(self, attribute, name) + def check_gcc_function_attribute_with_intrinsics(self, attribute, name, + code, include): + return check_gcc_function_attribute_with_intrinsics(self, attribute, + name, code, include) + def check_gcc_variable_attribute(self, attribute): return check_gcc_variable_attribute(self, attribute) + def check_gcc_version_at_least(self, major, minor=0, patchlevel=0): + """Return True if the GCC version is greater than or equal to the + specified version.""" + return check_gcc_version_at_least(self, major, minor, patchlevel) + def get_output(self, body, headers=None, include_dirs=None, libraries=None, library_dirs=None, lang="c", use_tee=None): @@ -430,13 +446,12 @@ def get_output(self, body, headers=None, include_dirs=None, of the program and its output. """ # 2008-11-16, RemoveMe - warnings.warn("\n+++++++++++++++++++++++++++++++++++++++++++++++++\n" \ - "Usage of get_output is deprecated: please do not \n" \ - "use it anymore, and avoid configuration checks \n" \ - "involving running executable on the target machine.\n" \ + warnings.warn("\n+++++++++++++++++++++++++++++++++++++++++++++++++\n" + "Usage of get_output is deprecated: please do not \n" + "use it anymore, and avoid configuration checks \n" + "involving running executable on the target machine.\n" "+++++++++++++++++++++++++++++++++++++++++++++++++\n", DeprecationWarning, stacklevel=2) - from distutils.ccompiler import CompileError, LinkError self._check_compiler() exitcode, output = 255, '' try: @@ -450,8 +465,24 @@ def get_output(self, body, headers=None, include_dirs=None, grabber.restore() raise exe = os.path.join('.', exe) - exitstatus, output = exec_command(exe, execute_in='.', - use_tee=use_tee) + try: + # specify cwd arg for consistency with + # historic usage pattern of exec_command() + # also, note that exe appears to be a string, + # which exec_command() handled, but we now + # use a list for check_output() -- this assumes + # that exe is always a single command + output = subprocess.check_output([exe], cwd='.') + except subprocess.CalledProcessError as exc: + exitstatus = exc.returncode + output = '' + except OSError: + # preserve the EnvironmentError exit status + # used historically in exec_command() + exitstatus = 127 + output = '' + else: + output = filepath_from_subprocess_output(output) if hasattr(os, 'WEXITSTATUS'): exitcode = os.WEXITSTATUS(exitstatus) if os.WIFSIGNALED(exitstatus): @@ -468,7 +499,7 @@ def get_output(self, body, headers=None, include_dirs=None, self._clean() return exitcode, output -class GrabStdout(object): +class GrabStdout: def __init__(self): self.sys_stdout = sys.stdout diff --git a/numpy/distutils/command/config_compiler.py b/numpy/distutils/command/config_compiler.py index 5e638feccce0..44265bfcce89 100644 --- a/numpy/distutils/command/config_compiler.py +++ b/numpy/distutils/command/config_compiler.py @@ -1,13 +1,14 @@ -from __future__ import division, absolute_import, print_function - from distutils.core import Command from numpy.distutils import log #XXX: Linker flags -def show_fortran_compilers(_cache=[]): - # Using cache to prevent infinite recursion - if _cache: return +def show_fortran_compilers(_cache=None): + # Using cache to prevent infinite recursion. + if _cache: + return + elif _cache is None: + _cache = [] _cache.append(1) from numpy.distutils.fcompiler import show_fcompilers import distutils.core diff --git a/numpy/distutils/command/develop.py b/numpy/distutils/command/develop.py index 1410ab2a00fd..af24baf2e7e1 100644 --- a/numpy/distutils/command/develop.py +++ b/numpy/distutils/command/develop.py @@ -3,8 +3,6 @@ files with filenames. """ -from __future__ import division, absolute_import, print_function - from setuptools.command.develop import develop as old_develop class develop(old_develop): diff --git a/numpy/distutils/command/egg_info.py b/numpy/distutils/command/egg_info.py index 18673ece7d19..14c62b4d1b90 100644 --- a/numpy/distutils/command/egg_info.py +++ b/numpy/distutils/command/egg_info.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - import sys from setuptools.command.egg_info import egg_info as _egg_info diff --git a/numpy/distutils/command/install.py b/numpy/distutils/command/install.py index a1dd47755c64..2eff2d145047 100644 --- a/numpy/distutils/command/install.py +++ b/numpy/distutils/command/install.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - import sys if 'setuptools' in sys.modules: import setuptools.command.install as old_install_mod @@ -64,16 +62,15 @@ def run(self): # bdist_rpm fails when INSTALLED_FILES contains # paths with spaces. Such paths must be enclosed # with double-quotes. - f = open(self.record, 'r') - lines = [] - need_rewrite = False - for l in f: - l = l.rstrip() - if ' ' in l: - need_rewrite = True - l = '"%s"' % (l) - lines.append(l) - f.close() + with open(self.record, 'r') as f: + lines = [] + need_rewrite = False + for l in f: + l = l.rstrip() + if ' ' in l: + need_rewrite = True + l = '"%s"' % (l) + lines.append(l) if need_rewrite: self.execute(write_file, (self.record, lines), diff --git a/numpy/distutils/command/install_clib.py b/numpy/distutils/command/install_clib.py index 662aa00bda9b..aa2e5594c3c2 100644 --- a/numpy/distutils/command/install_clib.py +++ b/numpy/distutils/command/install_clib.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - import os from distutils.core import Command from distutils.ccompiler import new_compiler @@ -19,6 +17,9 @@ def finalize_options(self): def run (self): build_clib_cmd = get_cmd("build_clib") + if not build_clib_cmd.build_clib: + # can happen if the user specified `--skip-build` + build_clib_cmd.finalize_options() build_dir = build_clib_cmd.build_clib # We need the compiler to get the library name -> filename association diff --git a/numpy/distutils/command/install_data.py b/numpy/distutils/command/install_data.py index 996cf7e4017a..0a2e68ae192a 100644 --- a/numpy/distutils/command/install_data.py +++ b/numpy/distutils/command/install_data.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - import sys have_setuptools = ('setuptools' in sys.modules) diff --git a/numpy/distutils/command/install_headers.py b/numpy/distutils/command/install_headers.py index f3f58aa2876f..bb4ad563b2a5 100644 --- a/numpy/distutils/command/install_headers.py +++ b/numpy/distutils/command/install_headers.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - import os from distutils.command.install_headers import install_headers as old_install_headers diff --git a/numpy/distutils/command/sdist.py b/numpy/distutils/command/sdist.py index bfaab1c8ffa1..e34193883dea 100644 --- a/numpy/distutils/command/sdist.py +++ b/numpy/distutils/command/sdist.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - import sys if 'setuptools' in sys.modules: from setuptools.command.sdist import sdist as old_sdist diff --git a/numpy/distutils/compat.py b/numpy/distutils/compat.py deleted file mode 100644 index 9a81cd392fc4..000000000000 --- a/numpy/distutils/compat.py +++ /dev/null @@ -1,10 +0,0 @@ -"""Small modules to cope with python 2 vs 3 incompatibilities inside -numpy.distutils - -""" -from __future__ import division, absolute_import, print_function - -import sys - -def get_exception(): - return sys.exc_info()[1] diff --git a/numpy/distutils/conv_template.py b/numpy/distutils/conv_template.py index 4a87462360a6..c8933d1d4286 100644 --- a/numpy/distutils/conv_template.py +++ b/numpy/distutils/conv_template.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """ takes templated file .xxx.src and produces .xxx file where .xxx is .i or .c or .h, using the following template rules @@ -78,8 +78,6 @@ 3, 3, jim """ -from __future__ import division, absolute_import, print_function - __all__ = ['process_str', 'process_file'] @@ -87,8 +85,6 @@ import sys import re -from numpy.distutils.compat import get_exception - # names for replacement that are already global. global_names = {} @@ -141,7 +137,7 @@ def paren_repl(obj): numrep = obj.group(2) return ','.join([torep]*int(numrep)) -parenrep = re.compile(r"[(]([^)]*)[)]\*(\d+)") +parenrep = re.compile(r"\(([^)]*)\)\*(\d+)") plainrep = re.compile(r"([^*]+)\*(\d+)") def parse_values(astr): # replaces all occurrences of '(a,b,c)*4' in astr @@ -206,14 +202,12 @@ def parse_loop_header(loophead) : dlist = [] if nsub is None : raise ValueError("No substitution variables found") - for i in range(nsub) : - tmp = {} - for name, vals in names : - tmp[name] = vals[i] + for i in range(nsub): + tmp = {name: vals[i] for name, vals in names} dlist.append(tmp) return dlist -replace_re = re.compile(r"@([\w]+)@") +replace_re = re.compile(r"@(\w+)@") def parse_string(astr, env, level, line) : lineno = "#line %d\n" % line @@ -224,7 +218,7 @@ def replace(match): val = env[name] except KeyError: msg = 'line %d: no definition of key "%s"'%(line, name) - raise ValueError(msg) + raise ValueError(msg) from None return val code = [lineno] @@ -242,8 +236,7 @@ def replace(match): code.append(replace_re.sub(replace, pref)) try : envlist = parse_loop_header(head) - except ValueError: - e = get_exception() + except ValueError as e: msg = "line %d: %s" % (newline, e) raise ValueError(msg) for newenv in envlist : @@ -269,22 +262,20 @@ def process_str(astr): def resolve_includes(source): d = os.path.dirname(source) - fid = open(source) - lines = [] - for line in fid: - m = include_src_re.match(line) - if m: - fn = m.group('name') - if not os.path.isabs(fn): - fn = os.path.join(d, fn) - if os.path.isfile(fn): - print('Including file', fn) - lines.extend(resolve_includes(fn)) + with open(source) as fid: + lines = [] + for line in fid: + m = include_src_re.match(line) + if m: + fn = m.group('name') + if not os.path.isabs(fn): + fn = os.path.join(d, fn) + if os.path.isfile(fn): + lines.extend(resolve_includes(fn)) + else: + lines.append(line) else: lines.append(line) - else: - lines.append(line) - fid.close() return lines def process_file(source): @@ -292,9 +283,8 @@ def process_file(source): sourcefile = os.path.normcase(source).replace("\\", "\\\\") try: code = process_str(''.join(lines)) - except ValueError: - e = get_exception() - raise ValueError('In "%s" loop at %s' % (sourcefile, e)) + except ValueError as e: + raise ValueError('In "%s" loop at %s' % (sourcefile, e)) from None return '#line 1 "%s"\n%s' % (sourcefile, code) @@ -330,9 +320,9 @@ def main(): allstr = fid.read() try: writestr = process_str(allstr) - except ValueError: - e = get_exception() - raise ValueError("In %s loop at %s" % (file, e)) + except ValueError as e: + raise ValueError("In %s loop at %s" % (file, e)) from None + outfile.write(writestr) if __name__ == "__main__": diff --git a/numpy/distutils/core.py b/numpy/distutils/core.py index d9e125368909..c4a14e59901f 100644 --- a/numpy/distutils/core.py +++ b/numpy/distutils/core.py @@ -1,7 +1,5 @@ -from __future__ import division, absolute_import, print_function - import sys -from distutils.core import * +from distutils.core import Distribution if 'setuptools' in sys.modules: have_setuptools = True @@ -21,13 +19,13 @@ import distutils.core import distutils.dist -from numpy.distutils.extension import Extension +from numpy.distutils.extension import Extension # noqa: F401 from numpy.distutils.numpy_distribution import NumpyDistribution from numpy.distutils.command import config, config_compiler, \ build, build_py, build_ext, build_clib, build_src, build_scripts, \ sdist, install_data, install_headers, install, bdist_rpm, \ install_clib -from numpy.distutils.misc_util import get_data_files, is_sequence, is_string +from numpy.distutils.misc_util import is_sequence, is_string numpy_cmdclass = {'build': build.build, 'build_src': build_src.build_src, @@ -71,12 +69,14 @@ def _dict_append(d, **kws): else: raise TypeError(repr(type(dv))) -def _command_line_ok(_cache=[]): +def _command_line_ok(_cache=None): """ Return True if command line does not contain any help or display requests. """ if _cache: return _cache[0] + elif _cache is None: + _cache = [] ok = True display_opts = ['--'+n for n in Distribution.display_option_names] for o in Distribution.display_options: diff --git a/numpy/distutils/cpuinfo.py b/numpy/distutils/cpuinfo.py index 5802993475f2..77620210981d 100644 --- a/numpy/distutils/cpuinfo.py +++ b/numpy/distutils/cpuinfo.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """ cpuinfo @@ -12,28 +12,22 @@ Pearu Peterson """ -from __future__ import division, absolute_import, print_function - __all__ = ['cpu'] -import sys, re, types import os - -if sys.version_info[0] >= 3: - from subprocess import getstatusoutput -else: - from commands import getstatusoutput - -import warnings import platform +import re +import sys +import types +import warnings + +from subprocess import getstatusoutput -from numpy.distutils.compat import get_exception def getoutput(cmd, successful_status=(0,), stacklevel=1): try: status, output = getstatusoutput(cmd) - except EnvironmentError: - e = get_exception() + except OSError as e: warnings.warn(str(e), UserWarning, stacklevel=stacklevel) return False, "" if os.WIFEXITED(status) and os.WEXITSTATUS(status) in successful_status: @@ -67,7 +61,7 @@ def key_value_from_command(cmd, sep, successful_status=(0,), d[l[0]] = l[1] return d -class CPUInfoBase(object): +class CPUInfoBase: """Holds CPU information and provides methods for requiring the availability of various CPU features. """ @@ -115,8 +109,7 @@ def __init__(self): info[0]['uname_m'] = output.strip() try: fo = open('/proc/cpuinfo') - except EnvironmentError: - e = get_exception() + except OSError as e: warnings.warn(str(e), UserWarning, stacklevel=2) else: for line in fo: @@ -242,16 +235,16 @@ def _is_Prescott(self): return self.is_PentiumIV() and self.has_sse3() def _is_Nocona(self): - return self.is_Intel() \ - and (self.info[0]['cpu family'] == '6' \ - or self.info[0]['cpu family'] == '15' ) \ - and (self.has_sse3() and not self.has_ssse3())\ - and re.match(r'.*?\blm\b', self.info[0]['flags']) is not None + return (self.is_Intel() + and (self.info[0]['cpu family'] == '6' + or self.info[0]['cpu family'] == '15') + and (self.has_sse3() and not self.has_ssse3()) + and re.match(r'.*?\blm\b', self.info[0]['flags']) is not None) def _is_Core2(self): - return self.is_64bit() and self.is_Intel() and \ - re.match(r'.*?Core\(TM\)2\b', \ - self.info[0]['model name']) is not None + return (self.is_64bit() and self.is_Intel() and + re.match(r'.*?Core\(TM\)2\b', + self.info[0]['model name']) is not None) def _is_Itanium(self): return re.match(r'.*?Itanium\b', @@ -490,10 +483,7 @@ def __init__(self): info = [] try: #XXX: Bad style to use so long `try:...except:...`. Fix it! - if sys.version_info[0] >= 3: - import winreg - else: - import _winreg as winreg + import winreg prgx = re.compile(r"family\s+(?P<FML>\d+)\s+model\s+(?P<MDL>\d+)" r"\s+stepping\s+(?P<STP>\d+)", re.IGNORECASE) @@ -523,8 +513,8 @@ def __init__(self): info[-1]["Family"]=int(srch.group("FML")) info[-1]["Model"]=int(srch.group("MDL")) info[-1]["Stepping"]=int(srch.group("STP")) - except Exception: - print(sys.exc_info()[1], '(ignoring)') + except Exception as e: + print(e, '(ignoring)') self.__class__.info = info def _not_impl(self): pass @@ -632,13 +622,13 @@ def _has_mmx(self): def _has_sse(self): if self.is_Intel(): - return (self.info[0]['Family']==6 and \ - self.info[0]['Model'] in [7, 8, 9, 10, 11]) \ - or self.info[0]['Family']==15 + return ((self.info[0]['Family']==6 and + self.info[0]['Model'] in [7, 8, 9, 10, 11]) + or self.info[0]['Family']==15) elif self.is_AMD(): - return (self.info[0]['Family']==6 and \ - self.info[0]['Model'] in [6, 7, 8, 10]) \ - or self.info[0]['Family']==15 + return ((self.info[0]['Family']==6 and + self.info[0]['Model'] in [6, 7, 8, 10]) + or self.info[0]['Family']==15) else: return False diff --git a/numpy/distutils/exec_command.py b/numpy/distutils/exec_command.py index 8118e2fc3ee3..79998cf5d478 100644 --- a/numpy/distutils/exec_command.py +++ b/numpy/distutils/exec_command.py @@ -49,19 +49,57 @@ because the messages are lost at some point. """ -from __future__ import division, absolute_import, print_function - __all__ = ['exec_command', 'find_executable'] import os import sys import subprocess import locale +import warnings from numpy.distutils.misc_util import is_sequence, make_temp_file from numpy.distutils import log +def filepath_from_subprocess_output(output): + """ + Convert `bytes` in the encoding used by a subprocess into a filesystem-appropriate `str`. + + Inherited from `exec_command`, and possibly incorrect. + """ + mylocale = locale.getpreferredencoding(False) + if mylocale is None: + mylocale = 'ascii' + output = output.decode(mylocale, errors='replace') + output = output.replace('\r\n', '\n') + # Another historical oddity + if output[-1:] == '\n': + output = output[:-1] + return output + + +def forward_bytes_to_stdout(val): + """ + Forward bytes from a subprocess call to the console, without attempting to + decode them. + + The assumption is that the subprocess call already returned bytes in + a suitable encoding. + """ + if hasattr(sys.stdout, 'buffer'): + # use the underlying binary output if there is one + sys.stdout.buffer.write(val) + elif hasattr(sys.stdout, 'encoding'): + # round-trip the encoding if necessary + sys.stdout.write(val.decode(sys.stdout.encoding)) + else: + # make a best-guess at the encoding + sys.stdout.write(val.decode('utf8', errors='replace')) + + def temp_file_name(): + # 2019-01-30, 1.17 + warnings.warn('temp_file_name is deprecated since NumPy v1.17, use ' + 'tempfile.mkstemp instead', DeprecationWarning, stacklevel=1) fo, name = make_temp_file() fo.close() return name @@ -128,9 +166,7 @@ def find_executable(exe, path=None, _cache={}): def _preserve_environment( names ): log.debug('_preserve_environment(%r)' % (names)) - env = {} - for name in names: - env[name] = os.environ.get(name) + env = {name: os.environ.get(name) for name in names} return env def _update_environment( **env ): @@ -138,24 +174,14 @@ def _update_environment( **env ): for name, value in env.items(): os.environ[name] = value or '' -def _supports_fileno(stream): - """ - Returns True if 'stream' supports the file descriptor and allows fileno(). - """ - if hasattr(stream, 'fileno'): - try: - stream.fileno() - return True - except IOError: - return False - else: - return False - def exec_command(command, execute_in='', use_shell=None, use_tee=None, _with_python = 1, **env ): """ Return (status,output) of executed command. + .. deprecated:: 1.17 + Use subprocess.Popen instead + Parameters ---------- command : str @@ -179,7 +205,10 @@ def exec_command(command, execute_in='', use_shell=None, use_tee=None, Wild cards will not work for non-posix systems or when use_shell=0. """ - log.debug('exec_command(%r,%s)' % (command,\ + # 2019-01-30, 1.17 + warnings.warn('exec_command is deprecated since NumPy v1.17, use ' + 'subprocess.Popen instead', DeprecationWarning, stacklevel=1) + log.debug('exec_command(%r,%s)' % (command, ','.join(['%s=%r'%kv for kv in env.items()]))) if use_tee is None: @@ -255,24 +284,20 @@ def _exec_command(command, use_shell=None, use_tee = None, **env): stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=False) - except EnvironmentError: + except OSError: # Return 127, as os.spawn*() and /bin/sh do return 127, '' text, err = proc.communicate() - text = text.decode(locale.getpreferredencoding(False), - errors='replace') - + mylocale = locale.getpreferredencoding(False) + if mylocale is None: + mylocale = 'ascii' + text = text.decode(mylocale, errors='replace') text = text.replace('\r\n', '\n') # Another historical oddity if text[-1:] == '\n': text = text[:-1] - # stdio uses bytes in python 2, so to avoid issues, we simply - # remove all non-ascii characters - if sys.version_info < (3, 0): - text = text.encode('ascii', errors='replace') - if use_tee and text: print(text) return proc.returncode, text diff --git a/numpy/distutils/extension.py b/numpy/distutils/extension.py index 935f3eec9254..3ede013e0f3c 100644 --- a/numpy/distutils/extension.py +++ b/numpy/distutils/extension.py @@ -6,21 +6,31 @@ Overridden to support f2py. """ -from __future__ import division, absolute_import, print_function - -import sys import re from distutils.extension import Extension as old_Extension -if sys.version_info[0] >= 3: - basestring = str +cxx_ext_re = re.compile(r'.*\.(cpp|cxx|cc)\Z', re.I).match +fortran_pyf_ext_re = re.compile(r'.*\.(f90|f95|f77|for|ftn|f|pyf)\Z', re.I).match -cxx_ext_re = re.compile(r'.*[.](cpp|cxx|cc)\Z', re.I).match -fortran_pyf_ext_re = re.compile(r'.*[.](f90|f95|f77|for|ftn|f|pyf)\Z', re.I).match class Extension(old_Extension): - def __init__ ( + """ + Parameters + ---------- + name : str + Extension name. + sources : list of str + List of source file locations relative to the top directory of + the package. + extra_compile_args : list of str + Extra command line arguments to pass to the compiler. + extra_f77_compile_args : list of str + Extra command line arguments to pass to the fortran77 compiler. + extra_f90_compile_args : list of str + Extra command line arguments to pass to the fortran90 compiler. + """ + def __init__( self, name, sources, include_dirs=None, define_macros=None, @@ -37,6 +47,8 @@ def __init__ ( language=None, f2py_options=None, module_dirs=None, + extra_c_compile_args=None, + extra_cxx_compile_args=None, extra_f77_compile_args=None, extra_f90_compile_args=None,): @@ -60,7 +72,7 @@ def __init__ ( self.swig_opts = swig_opts or [] # swig_opts is assumed to be a list. Here we handle the case where it # is specified as a string instead. - if isinstance(self.swig_opts, basestring): + if isinstance(self.swig_opts, str): import warnings msg = "swig_opts is specified as a string instead of a list" warnings.warn(msg, SyntaxWarning, stacklevel=2) @@ -73,6 +85,8 @@ def __init__ ( # numpy_distutils features self.f2py_options = f2py_options or [] self.module_dirs = module_dirs or [] + self.extra_c_compile_args = extra_c_compile_args or [] + self.extra_cxx_compile_args = extra_cxx_compile_args or [] self.extra_f77_compile_args = extra_f77_compile_args or [] self.extra_f90_compile_args = extra_f90_compile_args or [] diff --git a/numpy/distutils/fcompiler/__init__.py b/numpy/distutils/fcompiler/__init__.py index c926e7378290..d8dcfa8994e1 100644 --- a/numpy/distutils/fcompiler/__init__.py +++ b/numpy/distutils/fcompiler/__init__.py @@ -13,17 +13,12 @@ But note that FCompiler.executables is actually a dictionary of commands. """ -from __future__ import division, absolute_import, print_function - __all__ = ['FCompiler', 'new_fcompiler', 'show_fcompilers', 'dummy_fortran_file'] import os import sys import re -import types - -from numpy.compat import open_latin1 from distutils.sysconfig import get_python_lib from distutils.fancy_getopt import FancyGetopt @@ -35,9 +30,10 @@ from numpy.distutils import log from numpy.distutils.misc_util import is_string, all_strings, is_sequence, \ make_temp_file, get_shared_lib_extension -from numpy.distutils.environment import EnvironmentConfig from numpy.distutils.exec_command import find_executable -from numpy.distutils.compat import get_exception +from numpy.distutils import _shell_utils + +from .environment import EnvironmentConfig __metaclass__ = type @@ -91,7 +87,7 @@ class FCompiler(CCompiler): # These are the environment variables and distutils keys used. # Each configuration description is - # (<hook name>, <environment variable>, <key in distutils.cfg>, <convert>) + # (<hook name>, <environment variable>, <key in distutils.cfg>, <convert>, <append>) # The hook names are handled by the self._environment_hook method. # - names starting with 'self.' call methods in this class # - names starting with 'exe.' return the key in the executables dict @@ -101,43 +97,43 @@ class FCompiler(CCompiler): distutils_vars = EnvironmentConfig( distutils_section='config_fc', - noopt = (None, None, 'noopt', str2bool), - noarch = (None, None, 'noarch', str2bool), - debug = (None, None, 'debug', str2bool), - verbose = (None, None, 'verbose', str2bool), + noopt = (None, None, 'noopt', str2bool, False), + noarch = (None, None, 'noarch', str2bool, False), + debug = (None, None, 'debug', str2bool, False), + verbose = (None, None, 'verbose', str2bool, False), ) command_vars = EnvironmentConfig( distutils_section='config_fc', - compiler_f77 = ('exe.compiler_f77', 'F77', 'f77exec', None), - compiler_f90 = ('exe.compiler_f90', 'F90', 'f90exec', None), - compiler_fix = ('exe.compiler_fix', 'F90', 'f90exec', None), - version_cmd = ('exe.version_cmd', None, None, None), - linker_so = ('exe.linker_so', 'LDSHARED', 'ldshared', None), - linker_exe = ('exe.linker_exe', 'LD', 'ld', None), - archiver = (None, 'AR', 'ar', None), - ranlib = (None, 'RANLIB', 'ranlib', None), + compiler_f77 = ('exe.compiler_f77', 'F77', 'f77exec', None, False), + compiler_f90 = ('exe.compiler_f90', 'F90', 'f90exec', None, False), + compiler_fix = ('exe.compiler_fix', 'F90', 'f90exec', None, False), + version_cmd = ('exe.version_cmd', None, None, None, False), + linker_so = ('exe.linker_so', 'LDSHARED', 'ldshared', None, False), + linker_exe = ('exe.linker_exe', 'LD', 'ld', None, False), + archiver = (None, 'AR', 'ar', None, False), + ranlib = (None, 'RANLIB', 'ranlib', None, False), ) flag_vars = EnvironmentConfig( distutils_section='config_fc', - f77 = ('flags.f77', 'F77FLAGS', 'f77flags', flaglist), - f90 = ('flags.f90', 'F90FLAGS', 'f90flags', flaglist), - free = ('flags.free', 'FREEFLAGS', 'freeflags', flaglist), - fix = ('flags.fix', None, None, flaglist), - opt = ('flags.opt', 'FOPT', 'opt', flaglist), - opt_f77 = ('flags.opt_f77', None, None, flaglist), - opt_f90 = ('flags.opt_f90', None, None, flaglist), - arch = ('flags.arch', 'FARCH', 'arch', flaglist), - arch_f77 = ('flags.arch_f77', None, None, flaglist), - arch_f90 = ('flags.arch_f90', None, None, flaglist), - debug = ('flags.debug', 'FDEBUG', 'fdebug', flaglist), - debug_f77 = ('flags.debug_f77', None, None, flaglist), - debug_f90 = ('flags.debug_f90', None, None, flaglist), - flags = ('self.get_flags', 'FFLAGS', 'fflags', flaglist), - linker_so = ('flags.linker_so', 'LDFLAGS', 'ldflags', flaglist), - linker_exe = ('flags.linker_exe', 'LDFLAGS', 'ldflags', flaglist), - ar = ('flags.ar', 'ARFLAGS', 'arflags', flaglist), + f77 = ('flags.f77', 'F77FLAGS', 'f77flags', flaglist, True), + f90 = ('flags.f90', 'F90FLAGS', 'f90flags', flaglist, True), + free = ('flags.free', 'FREEFLAGS', 'freeflags', flaglist, True), + fix = ('flags.fix', None, None, flaglist, False), + opt = ('flags.opt', 'FOPT', 'opt', flaglist, True), + opt_f77 = ('flags.opt_f77', None, None, flaglist, False), + opt_f90 = ('flags.opt_f90', None, None, flaglist, False), + arch = ('flags.arch', 'FARCH', 'arch', flaglist, False), + arch_f77 = ('flags.arch_f77', None, None, flaglist, False), + arch_f90 = ('flags.arch_f90', None, None, flaglist, False), + debug = ('flags.debug', 'FDEBUG', 'fdebug', flaglist, True), + debug_f77 = ('flags.debug_f77', None, None, flaglist, False), + debug_f90 = ('flags.debug_f90', None, None, flaglist, False), + flags = ('self.get_flags', 'FFLAGS', 'fflags', flaglist, True), + linker_so = ('flags.linker_so', 'LDFLAGS', 'ldflags', flaglist, True), + linker_exe = ('flags.linker_exe', 'LDFLAGS', 'ldflags', flaglist, True), + ar = ('flags.ar', 'ARFLAGS', 'arflags', flaglist, True), ) language_map = {'.f': 'f77', @@ -360,7 +356,7 @@ def set_exe(exe_key, f77=None, f90=None): set_exe('archiver') set_exe('ranlib') - def update_executables(elf): + def update_executables(self): """Called at the beginning of customisation. Subclasses should override this if they need to set up the executables dictionary. @@ -473,13 +469,23 @@ def customize(self, dist = None): fixflags = [] if f77: + f77 = _shell_utils.NativeParser.split(f77) f77flags = self.flag_vars.f77 if f90: + f90 = _shell_utils.NativeParser.split(f90) f90flags = self.flag_vars.f90 freeflags = self.flag_vars.free # XXX Assuming that free format is default for f90 compiler. fix = self.command_vars.compiler_fix + # NOTE: this and similar examples are probably just + # excluding --coverage flag when F90 = gfortran --coverage + # instead of putting that flag somewhere more appropriate + # this and similar examples where a Fortran compiler + # environment variable has been customized by CI or a user + # should perhaps eventually be more thoroughly tested and more + # robustly handled if fix: + fix = _shell_utils.NativeParser.split(fix) fixflags = self.flag_vars.fix + f90flags oflags, aflags, dflags = [], [], [] @@ -505,11 +511,11 @@ def get_flags(tag, flags): fflags = self.flag_vars.flags + dflags + oflags + aflags if f77: - self.set_commands(compiler_f77=[f77]+f77flags+fflags) + self.set_commands(compiler_f77=f77+f77flags+fflags) if f90: - self.set_commands(compiler_f90=[f90]+freeflags+f90flags+fflags) + self.set_commands(compiler_f90=f90+freeflags+f90flags+fflags) if fix: - self.set_commands(compiler_fix=[fix]+fixflags+fflags) + self.set_commands(compiler_fix=fix+fixflags+fflags) #XXX: Do we need LDSHARED->SOSHARED, LDFLAGS->SOFLAGS @@ -602,9 +608,9 @@ def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): src) try: self.spawn(command, display=display) - except DistutilsExecError: - msg = str(get_exception()) - raise CompileError(msg) + except DistutilsExecError as e: + msg = str(e) + raise CompileError(msg) from None def module_options(self, module_dirs, module_build_dir): options = [] @@ -670,9 +676,9 @@ def link(self, target_desc, objects, command = linker + ld_args try: self.spawn(command) - except DistutilsExecError: - msg = str(get_exception()) - raise LinkError(msg) + except DistutilsExecError as e: + msg = str(e) + raise LinkError(msg) from None else: log.debug("skipping %s (up-to-date)", output_filename) @@ -737,9 +743,11 @@ def wrap_unlinkable_objects(self, objects, output_dir, extra_dll_dir): ('win32', ('gnu', 'intelv', 'absoft', 'compaqv', 'intelev', 'gnu95', 'g95', 'intelvem', 'intelem', 'flang')), ('cygwin.*', ('gnu', 'intelv', 'absoft', 'compaqv', 'intelev', 'gnu95', 'g95')), - ('linux.*', ('gnu95', 'intel', 'lahey', 'pg', 'absoft', 'nag', 'vast', 'compaq', - 'intele', 'intelem', 'gnu', 'g95', 'pathf95', 'nagfor')), - ('darwin.*', ('gnu95', 'nag', 'absoft', 'ibm', 'intel', 'gnu', 'g95', 'pg')), + ('linux.*', ('arm', 'gnu95', 'intel', 'lahey', 'pg', 'nv', 'absoft', 'nag', + 'vast', 'compaq', 'intele', 'intelem', 'gnu', 'g95', + 'pathf95', 'nagfor', 'fujitsu')), + ('darwin.*', ('gnu95', 'nag', 'nagfor', 'absoft', 'ibm', 'intel', 'gnu', + 'g95', 'pg')), ('sunos.*', ('sun', 'gnu', 'gnu95', 'g95')), ('irix.*', ('mips', 'gnu', 'gnu95',)), ('aix.*', ('ibm', 'gnu', 'gnu95',)), @@ -919,8 +927,7 @@ def show_fcompilers(dist=None): c = new_fcompiler(compiler=compiler, verbose=dist.verbose) c.customize(dist) v = c.get_version() - except (DistutilsModuleError, CompilerNotFound): - e = get_exception() + except (DistutilsModuleError, CompilerNotFound) as e: log.debug("show_fcompilers: %s not found" % (compiler,)) log.debug(repr(e)) @@ -957,10 +964,10 @@ def dummy_fortran_file(): return name[:-2] -is_f_file = re.compile(r'.*[.](for|ftn|f77|f)\Z', re.I).match -_has_f_header = re.compile(r'-[*]-\s*fortran\s*-[*]-', re.I).search -_has_f90_header = re.compile(r'-[*]-\s*f90\s*-[*]-', re.I).search -_has_fix_header = re.compile(r'-[*]-\s*fix\s*-[*]-', re.I).search +is_f_file = re.compile(r'.*\.(for|ftn|f77|f)\Z', re.I).match +_has_f_header = re.compile(r'-\*-\s*fortran\s*-\*-', re.I).search +_has_f90_header = re.compile(r'-\*-\s*f90\s*-\*-', re.I).search +_has_fix_header = re.compile(r'-\*-\s*fix\s*-\*-', re.I).search _free_f90_start = re.compile(r'[^c*!]\s*[^\s\d\t]', re.I).match def is_free_format(file): @@ -968,29 +975,27 @@ def is_free_format(file): # f90 allows both fixed and free format, assuming fixed unless # signs of free format are detected. result = 0 - f = open_latin1(file, 'r') - line = f.readline() - n = 10000 # the number of non-comment lines to scan for hints - if _has_f_header(line): - n = 0 - elif _has_f90_header(line): - n = 0 - result = 1 - while n>0 and line: - line = line.rstrip() - if line and line[0]!='!': - n -= 1 - if (line[0]!='\t' and _free_f90_start(line[:5])) or line[-1:]=='&': - result = 1 - break + with open(file, encoding='latin1') as f: line = f.readline() - f.close() + n = 10000 # the number of non-comment lines to scan for hints + if _has_f_header(line) or _has_fix_header(line): + n = 0 + elif _has_f90_header(line): + n = 0 + result = 1 + while n>0 and line: + line = line.rstrip() + if line and line[0]!='!': + n -= 1 + if (line[0]!='\t' and _free_f90_start(line[:5])) or line[-1:]=='&': + result = 1 + break + line = f.readline() return result def has_f90_header(src): - f = open_latin1(src, 'r') - line = f.readline() - f.close() + with open(src, encoding='latin1') as f: + line = f.readline() return _has_f90_header(line) or _has_fix_header(line) _f77flags_re = re.compile(r'(c|)f77flags\s*\(\s*(?P<fcname>\w+)\s*\)\s*=\s*(?P<fflags>.*)', re.I) @@ -1001,17 +1006,16 @@ def get_f77flags(src): Return a dictionary {<fcompiler type>:<f77 flags>}. """ flags = {} - f = open_latin1(src, 'r') - i = 0 - for line in f: - i += 1 - if i>20: break - m = _f77flags_re.match(line) - if not m: continue - fcname = m.group('fcname').strip() - fflags = m.group('fflags').strip() - flags[fcname] = split_quoted(fflags) - f.close() + with open(src, encoding='latin1') as f: + i = 0 + for line in f: + i += 1 + if i>20: break + m = _f77flags_re.match(line) + if not m: continue + fcname = m.group('fcname').strip() + fflags = m.group('fflags').strip() + flags[fcname] = split_quoted(fflags) return flags # TODO: implement get_f90flags and use it in _compile similarly to get_f77flags diff --git a/numpy/distutils/fcompiler/absoft.py b/numpy/distutils/fcompiler/absoft.py index 2c3edfe02392..efe3a4cb55e9 100644 --- a/numpy/distutils/fcompiler/absoft.py +++ b/numpy/distutils/fcompiler/absoft.py @@ -5,8 +5,6 @@ # Notes: # - when using -g77 then use -DUNDERSCORE_G77 to compile f2py # generated extension modules (works for f2py v2.45.241_1936 and up) -from __future__ import division, absolute_import, print_function - import os from numpy.distutils.cpuinfo import cpu @@ -66,7 +64,7 @@ def get_flags_linker_so(self): def library_dir_option(self, dir): if os.name=='nt': - return ['-link', '/PATH:"%s"' % (dir)] + return ['-link', '/PATH:%s' % (dir)] return "-L" + dir def library_option(self, lib): diff --git a/numpy/distutils/fcompiler/arm.py b/numpy/distutils/fcompiler/arm.py new file mode 100644 index 000000000000..bc491d947b29 --- /dev/null +++ b/numpy/distutils/fcompiler/arm.py @@ -0,0 +1,73 @@ +from __future__ import division, absolute_import, print_function + +import sys + +from numpy.distutils.fcompiler import FCompiler, dummy_fortran_file +from sys import platform +from os.path import join, dirname, normpath + +compilers = ['ArmFlangCompiler'] + +import functools + +class ArmFlangCompiler(FCompiler): + compiler_type = 'arm' + description = 'Arm Compiler' + version_pattern = r'\s*Arm.*version (?P<version>[\d.-]+).*' + + ar_exe = 'lib.exe' + possible_executables = ['armflang'] + + executables = { + 'version_cmd': ["", "--version"], + 'compiler_f77': ["armflang", "-fPIC"], + 'compiler_fix': ["armflang", "-fPIC", "-ffixed-form"], + 'compiler_f90': ["armflang", "-fPIC"], + 'linker_so': ["armflang", "-fPIC", "-shared"], + 'archiver': ["ar", "-cr"], + 'ranlib': None + } + + pic_flags = ["-fPIC", "-DPIC"] + c_compiler = 'arm' + module_dir_switch = '-module ' # Don't remove ending space! + + def get_libraries(self): + opt = FCompiler.get_libraries(self) + opt.extend(['flang', 'flangrti', 'ompstub']) + return opt + + @functools.lru_cache(maxsize=128) + def get_library_dirs(self): + """List of compiler library directories.""" + opt = FCompiler.get_library_dirs(self) + flang_dir = dirname(self.executables['compiler_f77'][0]) + opt.append(normpath(join(flang_dir, '..', 'lib'))) + + return opt + + def get_flags(self): + return [] + + def get_flags_free(self): + return [] + + def get_flags_debug(self): + return ['-g'] + + def get_flags_opt(self): + return ['-O3'] + + def get_flags_arch(self): + return [] + + def runtime_library_dir_option(self, dir): + return '-Wl,-rpath=%s' % dir + + +if __name__ == '__main__': + from distutils import log + log.set_verbosity(2) + from numpy.distutils import customized_fcompiler + print(customized_fcompiler(compiler='armflang').get_version()) + diff --git a/numpy/distutils/fcompiler/compaq.py b/numpy/distutils/fcompiler/compaq.py index 07d50270697c..01314c136acf 100644 --- a/numpy/distutils/fcompiler/compaq.py +++ b/numpy/distutils/fcompiler/compaq.py @@ -1,12 +1,9 @@ #http://www.compaq.com/fortran/docs/ -from __future__ import division, absolute_import, print_function - import os import sys from numpy.distutils.fcompiler import FCompiler -from numpy.distutils.compat import get_exception from distutils.errors import DistutilsPlatformError compilers = ['CompaqFCompiler'] @@ -82,22 +79,19 @@ class CompaqVisualFCompiler(FCompiler): ar_exe = m.lib except DistutilsPlatformError: pass - except AttributeError: - msg = get_exception() - if '_MSVCCompiler__root' in str(msg): - print('Ignoring "%s" (I think it is msvccompiler.py bug)' % (msg)) + except AttributeError as e: + if '_MSVCCompiler__root' in str(e): + print('Ignoring "%s" (I think it is msvccompiler.py bug)' % (e)) else: raise - except IOError: - e = get_exception() + except OSError as e: if not "vcvarsall.bat" in str(e): - print("Unexpected IOError in", __file__) - raise e - except ValueError: - e = get_exception() - if not "path']" in str(e): + print("Unexpected OSError in", __file__) + raise + except ValueError as e: + if not "'path'" in str(e): print("Unexpected ValueError in", __file__) - raise e + raise executables = { 'version_cmd' : ['<F90>', "/what"], diff --git a/numpy/distutils/environment.py b/numpy/distutils/fcompiler/environment.py similarity index 63% rename from numpy/distutils/environment.py rename to numpy/distutils/fcompiler/environment.py index 3798e16f5da7..ecd4d9989279 100644 --- a/numpy/distutils/environment.py +++ b/numpy/distutils/fcompiler/environment.py @@ -1,11 +1,9 @@ -from __future__ import division, absolute_import, print_function - import os from distutils.dist import Distribution __metaclass__ = type -class EnvironmentConfig(object): +class EnvironmentConfig: def __init__(self, distutils_section='ALL', **kw): self._distutils_section = distutils_section self._conf_keys = kw @@ -14,7 +12,7 @@ def __init__(self, distutils_section='ALL', **kw): def dump_variable(self, name): conf_desc = self._conf_keys[name] - hook, envvar, confvar, convert = conf_desc + hook, envvar, confvar, convert, append = conf_desc if not convert: convert = lambda x : x print('%s.%s:' % (self._distutils_section, name)) @@ -35,7 +33,10 @@ def __getattr__(self, name): try: conf_desc = self._conf_keys[name] except KeyError: - raise AttributeError(name) + raise AttributeError( + f"'EnvironmentConfig' object has no attribute '{name}'" + ) from None + return self._get_var(name, conf_desc) def get(self, name, default=None): @@ -49,16 +50,31 @@ def get(self, name, default=None): return var def _get_var(self, name, conf_desc): - hook, envvar, confvar, convert = conf_desc + hook, envvar, confvar, convert, append = conf_desc + if convert is None: + convert = lambda x: x var = self._hook_handler(name, hook) if envvar is not None: - var = os.environ.get(envvar, var) + envvar_contents = os.environ.get(envvar) + if envvar_contents is not None: + envvar_contents = convert(envvar_contents) + if var and append: + if os.environ.get('NPY_DISTUTILS_APPEND_FLAGS', '1') == '1': + var.extend(envvar_contents) + else: + # NPY_DISTUTILS_APPEND_FLAGS was explicitly set to 0 + # to keep old (overwrite flags rather than append to + # them) behavior + var = envvar_contents + else: + var = envvar_contents if confvar is not None and self._conf: - var = self._conf.get(confvar, (None, var))[1] - if convert is not None: - var = convert(var) + if confvar in self._conf: + source, confvar_contents = self._conf[confvar] + var = convert(confvar_contents) return var + def clone(self, hook_handler): ec = self.__class__(distutils_section=self._distutils_section, **self._conf_keys) diff --git a/numpy/distutils/fcompiler/fujitsu.py b/numpy/distutils/fcompiler/fujitsu.py new file mode 100644 index 000000000000..ddce67456d18 --- /dev/null +++ b/numpy/distutils/fcompiler/fujitsu.py @@ -0,0 +1,46 @@ +""" +fujitsu + +Supports Fujitsu compiler function. +This compiler is developed by Fujitsu and is used in A64FX on Fugaku. +""" +from numpy.distutils.fcompiler import FCompiler + +compilers = ['FujitsuFCompiler'] + +class FujitsuFCompiler(FCompiler): + compiler_type = 'fujitsu' + description = 'Fujitsu Fortran Compiler' + + possible_executables = ['frt'] + version_pattern = r'frt \(FRT\) (?P<version>[a-z\d.]+)' + # $ frt --version + # frt (FRT) x.x.x yyyymmdd + + executables = { + 'version_cmd' : ["<F77>", "--version"], + 'compiler_f77' : ["frt", "-Fixed"], + 'compiler_fix' : ["frt", "-Fixed"], + 'compiler_f90' : ["frt"], + 'linker_so' : ["frt", "-shared"], + 'archiver' : ["ar", "-cr"], + 'ranlib' : ["ranlib"] + } + pic_flags = ['-KPIC'] + module_dir_switch = '-M' + module_include_switch = '-I' + + def get_flags_opt(self): + return ['-O3'] + def get_flags_debug(self): + return ['-g'] + def runtime_library_dir_option(self, dir): + return f'-Wl,-rpath={dir}' + def get_libraries(self): + return ['fj90f', 'fj90i', 'fjsrcinfo'] + +if __name__ == '__main__': + from distutils import log + from numpy.distutils import customized_fcompiler + log.set_verbosity(2) + print(customized_fcompiler('fujitsu').get_version()) diff --git a/numpy/distutils/fcompiler/g95.py b/numpy/distutils/fcompiler/g95.py index e7c659b332c2..e109a972a872 100644 --- a/numpy/distutils/fcompiler/g95.py +++ b/numpy/distutils/fcompiler/g95.py @@ -1,6 +1,4 @@ # http://g95.sourceforge.net/ -from __future__ import division, absolute_import, print_function - from numpy.distutils.fcompiler import FCompiler compilers = ['G95FCompiler'] diff --git a/numpy/distutils/fcompiler/gnu.py b/numpy/distutils/fcompiler/gnu.py index 0ebbe79dc293..39178071d511 100644 --- a/numpy/distutils/fcompiler/gnu.py +++ b/numpy/distutils/fcompiler/gnu.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - import re import os import sys @@ -8,12 +6,11 @@ import tempfile import hashlib import base64 +import subprocess from subprocess import Popen, PIPE, STDOUT -from copy import copy +from numpy.distutils.exec_command import filepath_from_subprocess_output from numpy.distutils.fcompiler import FCompiler -from numpy.distutils.exec_command import exec_command -from numpy.distutils.compat import get_exception -from numpy.distutils.system_info import system_info +from distutils.version import LooseVersion compilers = ['GnuFCompiler', 'Gnu95FCompiler'] @@ -26,13 +23,6 @@ def is_win64(): return sys.platform == "win32" and platform.architecture()[0] == "64bit" -if is_win64(): - #_EXTRAFLAGS = ["-fno-leading-underscore"] - _EXTRAFLAGS = [] -else: - _EXTRAFLAGS = [] - - class GnuFCompiler(FCompiler): compiler_type = 'gnu' compiler_aliases = ('g77', ) @@ -42,7 +32,8 @@ def gnu_version_match(self, version_string): """Handle the different versions of GNU fortran compilers""" # Strip warning(s) that may be emitted by gfortran while version_string.startswith('gfortran: warning'): - version_string = version_string[version_string.find('\n') + 1:] + version_string =\ + version_string[version_string.find('\n') + 1:].strip() # Gfortran versions from after 2010 will output a simple string # (usually "x.y", "x.y.z" or "x.y.z-q") for ``-dumpversion``; older @@ -122,30 +113,21 @@ def get_flags_linker_so(self): # If MACOSX_DEPLOYMENT_TARGET is set, we simply trust the value # and leave it alone. But, distutils will complain if the # environment's value is different from the one in the Python - # Makefile used to build Python. We let disutils handle this + # Makefile used to build Python. We let distutils handle this # error checking. if not target: # If MACOSX_DEPLOYMENT_TARGET is not set in the environment, - # we try to get it first from the Python Makefile and then we - # fall back to setting it to 10.3 to maximize the set of - # versions we can work with. This is a reasonable default + # we try to get it first from sysconfig and then + # fall back to setting it to 10.9 This is a reasonable default # even when using the official Python dist and those derived # from it. - import distutils.sysconfig as sc - g = {} - try: - get_makefile_filename = sc.get_makefile_filename - except AttributeError: - pass # i.e. PyPy - else: - filename = get_makefile_filename() - sc.parse_makefile(filename, g) - target = g.get('MACOSX_DEPLOYMENT_TARGET', '10.3') - os.environ['MACOSX_DEPLOYMENT_TARGET'] = target - if target == '10.3': - s = 'Env. variable MACOSX_DEPLOYMENT_TARGET set to 10.3' + import sysconfig + target = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') + if not target: + target = '10.9' + s = f'Env. variable MACOSX_DEPLOYMENT_TARGET set to {target}' warnings.warn(s, stacklevel=2) - + os.environ['MACOSX_DEPLOYMENT_TARGET'] = str(target) opt.extend(['-undefined', 'dynamic_lookup', '-bundle']) else: opt.append("-shared") @@ -160,9 +142,13 @@ def get_flags_linker_so(self): return opt def get_libgcc_dir(self): - status, output = exec_command( - self.compiler_f77 + ['-print-libgcc-file-name'], use_tee=0) - if not status: + try: + output = subprocess.check_output(self.compiler_f77 + + ['-print-libgcc-file-name']) + except (OSError, subprocess.CalledProcessError): + pass + else: + output = filepath_from_subprocess_output(output) return os.path.dirname(output) return None @@ -177,9 +163,13 @@ def get_libgfortran_dir(self): libgfortran_dir = None if libgfortran_name: find_lib_arg = ['-print-file-name={0}'.format(libgfortran_name)] - status, output = exec_command( - self.compiler_f77 + find_lib_arg, use_tee=0) - if not status: + try: + output = subprocess.check_output( + self.compiler_f77 + find_lib_arg) + except (OSError, subprocess.CalledProcessError): + pass + else: + output = filepath_from_subprocess_output(output) libgfortran_dir = os.path.dirname(output) return libgfortran_dir @@ -242,7 +232,7 @@ def get_flags_opt(self): def _c_arch_flags(self): """ Return detected arch flags from CFLAGS """ - from distutils import sysconfig + import sysconfig try: cflags = sysconfig.get_config_vars()['CFLAGS'] except KeyError: @@ -257,8 +247,20 @@ def get_flags_arch(self): return [] def runtime_library_dir_option(self, dir): - sep = ',' if sys.platform == 'darwin' else '=' - return '-Wl,-rpath%s"%s"' % (sep, dir) + if sys.platform == 'win32' or sys.platform == 'cygwin': + # Linux/Solaris/Unix support RPATH, Windows does not + raise NotImplementedError + + # TODO: could use -Xlinker here, if it's supported + assert "," not in dir + + if sys.platform == 'darwin': + return f'-Wl,-rpath,{dir}' + elif sys.platform[:3] == 'aix': + # AIX RPATH is called LIBPATH + return f'-Wl,-blibpath:{dir}' + else: + return f'-Wl,-rpath={dir}' class Gnu95FCompiler(GnuFCompiler): @@ -271,7 +273,7 @@ def version_match(self, version_string): if not v or v[0] != 'gfortran': return None v = v[1] - if v >= '4.': + if LooseVersion(v) >= "4": # gcc-4 series releases do not support -mno-cygwin option pass else: @@ -289,11 +291,11 @@ def version_match(self, version_string): executables = { 'version_cmd' : ["<F90>", "-dumpversion"], 'compiler_f77' : [None, "-Wall", "-g", "-ffixed-form", - "-fno-second-underscore"] + _EXTRAFLAGS, + "-fno-second-underscore"], 'compiler_f90' : [None, "-Wall", "-g", - "-fno-second-underscore"] + _EXTRAFLAGS, + "-fno-second-underscore"], 'compiler_fix' : [None, "-Wall", "-g","-ffixed-form", - "-fno-second-underscore"] + _EXTRAFLAGS, + "-fno-second-underscore"], 'linker_so' : ["<F90>", "-Wall", "-g"], 'archiver' : ["ar", "-cr"], 'ranlib' : ["ranlib"], @@ -303,6 +305,12 @@ def version_match(self, version_string): module_dir_switch = '-J' module_include_switch = '-I' + if sys.platform[:3] == 'aix': + executables['linker_so'].append('-lpthread') + if platform.architecture()[0][:2] == '64': + for key in ['compiler_f77', 'compiler_f90','compiler_fix','linker_so', 'linker_exe']: + executables[key].append('-maix64') + g2c = 'gfortran' def _universal_flags(self, cmd): @@ -373,8 +381,12 @@ def get_libraries(self): return opt def get_target(self): - status, output = exec_command(self.compiler_f77 + ['-v'], use_tee=0) - if not status: + try: + output = subprocess.check_output(self.compiler_f77 + ['-v']) + except (OSError, subprocess.CalledProcessError): + pass + else: + output = filepath_from_subprocess_output(output) m = TARGET_R.search(output) if m: return m.group(1) @@ -390,8 +402,7 @@ def _hash_files(self, filenames): break h.update(block) text = base64.b32encode(h.digest()) - if sys.version_info[0] >= 3: - text = text.decode('ascii') + text = text.decode('ascii') return text.rstrip('=') def _link_wrapper_lib(self, objects, output_dir, extra_dll_dir, @@ -524,7 +535,6 @@ def _can_target(cmd, arch): os.remove(output) finally: os.remove(filename) - return False if __name__ == '__main__': @@ -535,5 +545,5 @@ def _can_target(cmd, arch): print(customized_fcompiler('gnu').get_version()) try: print(customized_fcompiler('g95').get_version()) - except Exception: - print(get_exception()) + except Exception as e: + print(e) diff --git a/numpy/distutils/fcompiler/hpux.py b/numpy/distutils/fcompiler/hpux.py index 51bad548a124..09e6483bf5ad 100644 --- a/numpy/distutils/fcompiler/hpux.py +++ b/numpy/distutils/fcompiler/hpux.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - from numpy.distutils.fcompiler import FCompiler compilers = ['HPUXFCompiler'] diff --git a/numpy/distutils/fcompiler/ibm.py b/numpy/distutils/fcompiler/ibm.py index d0c2202d4d5f..eff24401a1c3 100644 --- a/numpy/distutils/fcompiler/ibm.py +++ b/numpy/distutils/fcompiler/ibm.py @@ -1,11 +1,10 @@ -from __future__ import division, absolute_import, print_function - import os import re import sys +import subprocess from numpy.distutils.fcompiler import FCompiler -from numpy.distutils.exec_command import exec_command, find_executable +from numpy.distutils.exec_command import find_executable from numpy.distutils.misc_util import make_temp_file from distutils import log @@ -35,9 +34,13 @@ def get_version(self,*args,**kwds): lslpp = find_executable('lslpp') xlf = find_executable('xlf') if os.path.exists(xlf) and os.path.exists(lslpp): - s, o = exec_command(lslpp + ' -Lc xlfcmp') - m = re.search(r'xlfcmp:(?P<version>\d+([.]\d+)+)', o) - if m: version = m.group('version') + try: + o = subprocess.check_output([lslpp, '-Lc', 'xlfcmp']) + except (OSError, subprocess.CalledProcessError): + pass + else: + m = re.search(r'xlfcmp:(?P<version>\d+([.]\d+)+)', o) + if m: version = m.group('version') xlf_dir = '/etc/opt/ibmcmp/xlf' if version is None and os.path.isdir(xlf_dir): @@ -73,15 +76,14 @@ def get_flags_linker_so(self): xlf_cfg = '/etc/opt/ibmcmp/xlf/%s/xlf.cfg' % version fo, new_cfg = make_temp_file(suffix='_xlf.cfg') log.info('Creating '+new_cfg) - fi = open(xlf_cfg, 'r') - crt1_match = re.compile(r'\s*crt\s*[=]\s*(?P<path>.*)/crt1.o').match - for line in fi: - m = crt1_match(line) - if m: - fo.write('crt = %s/bundle1.o\n' % (m.group('path'))) - else: - fo.write(line) - fi.close() + with open(xlf_cfg, 'r') as fi: + crt1_match = re.compile(r'\s*crt\s*=\s*(?P<path>.*)/crt1.o').match + for line in fi: + m = crt1_match(line) + if m: + fo.write('crt = %s/bundle1.o\n' % (m.group('path'))) + else: + fo.write(line) fo.close() opt.append('-F'+new_cfg) return opt diff --git a/numpy/distutils/fcompiler/intel.py b/numpy/distutils/fcompiler/intel.py index 217eac8fbea3..f97c5b3483e1 100644 --- a/numpy/distutils/fcompiler/intel.py +++ b/numpy/distutils/fcompiler/intel.py @@ -1,6 +1,4 @@ # http://developer.intel.com/software/products/compilers/flin/ -from __future__ import division, absolute_import, print_function - import sys from numpy.distutils.ccompiler import simple_version_match @@ -23,7 +21,10 @@ def update_executables(self): f + '.f', '-o', f + '.o'] def runtime_library_dir_option(self, dir): - return '-Wl,-rpath="%s"' % dir + # TODO: could use -Xlinker here, if it's supported + assert "," not in dir + + return '-Wl,-rpath=%s' % dir class IntelFCompiler(BaseIntelFCompiler): @@ -58,7 +59,8 @@ def get_flags(self): def get_flags_opt(self): # Scipy test failures with -O2 v = self.get_version() mpopt = 'openmp' if v and v < '15' else 'qopenmp' - return ['-fp-model strict -O1 -{}'.format(mpopt)] + return ['-fp-model', 'strict', '-O1', + '-assume', 'minus0', '-{}'.format(mpopt)] def get_flags_arch(self): return [] @@ -118,17 +120,6 @@ class IntelEM64TFCompiler(IntelFCompiler): 'ranlib' : ["ranlib"] } - def get_flags(self): - return ['-fPIC'] - - def get_flags_opt(self): # Scipy test failures with -O2 - v = self.get_version() - mpopt = 'openmp' if v and v < '15' else 'qopenmp' - return ['-fp-model strict -O1 -{}'.format(mpopt)] - - def get_flags_arch(self): - return [''] - # Is there no difference in the version string between the above compilers # and the Visual compilers? @@ -173,7 +164,7 @@ def get_flags_debug(self): return ['/4Yb', '/d2'] def get_flags_opt(self): - return ['/O1'] # Scipy test failures with /O2 + return ['/O1', '/assume:minus0'] # Scipy test failures with /O2 def get_flags_arch(self): return ["/arch:IA32", "/QaxSSE3"] @@ -209,7 +200,7 @@ class IntelEM64VisualFCompiler(IntelVisualFCompiler): version_match = simple_version_match(start=r'Intel\(R\).*?64,') def get_flags_arch(self): - return [''] + return [] if __name__ == '__main__': diff --git a/numpy/distutils/fcompiler/lahey.py b/numpy/distutils/fcompiler/lahey.py index 1beb662f4e93..e925838268b8 100644 --- a/numpy/distutils/fcompiler/lahey.py +++ b/numpy/distutils/fcompiler/lahey.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - import os from numpy.distutils.fcompiler import FCompiler diff --git a/numpy/distutils/fcompiler/mips.py b/numpy/distutils/fcompiler/mips.py index da337b24a3ff..a0973804571b 100644 --- a/numpy/distutils/fcompiler/mips.py +++ b/numpy/distutils/fcompiler/mips.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - from numpy.distutils.cpuinfo import cpu from numpy.distutils.fcompiler import FCompiler diff --git a/numpy/distutils/fcompiler/nag.py b/numpy/distutils/fcompiler/nag.py index cb71d548cd2d..939201f44e02 100644 --- a/numpy/distutils/fcompiler/nag.py +++ b/numpy/distutils/fcompiler/nag.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - import sys import re from numpy.distutils.fcompiler import FCompiler @@ -21,7 +19,7 @@ def get_flags_linker_so(self): def get_flags_opt(self): return ['-O4'] def get_flags_arch(self): - return [''] + return [] class NAGFCompiler(BaseNAGFCompiler): @@ -66,6 +64,11 @@ class NAGFORCompiler(BaseNAGFCompiler): 'ranlib' : ["ranlib"] } + def get_flags_linker_so(self): + if sys.platform == 'darwin': + return ['-unsharedrts', + '-Wl,-bundle,-flat_namespace,-undefined,suppress'] + return BaseNAGFCompiler.get_flags_linker_so(self) def get_flags_debug(self): version = self.get_version() if version and version > '6.1': diff --git a/numpy/distutils/fcompiler/none.py b/numpy/distutils/fcompiler/none.py index bdeea1560e91..ef411fffc7cb 100644 --- a/numpy/distutils/fcompiler/none.py +++ b/numpy/distutils/fcompiler/none.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - from numpy.distutils.fcompiler import FCompiler from numpy.distutils import customized_fcompiler diff --git a/numpy/distutils/fcompiler/nv.py b/numpy/distutils/fcompiler/nv.py new file mode 100644 index 000000000000..212f34806fc4 --- /dev/null +++ b/numpy/distutils/fcompiler/nv.py @@ -0,0 +1,53 @@ +from numpy.distutils.fcompiler import FCompiler + +compilers = ['NVHPCFCompiler'] + +class NVHPCFCompiler(FCompiler): + """ NVIDIA High Performance Computing (HPC) SDK Fortran Compiler + + https://developer.nvidia.com/hpc-sdk + + Since august 2020 the NVIDIA HPC SDK includes the compilers formerly known as The Portland Group compilers, + https://www.pgroup.com/index.htm. + See also `numpy.distutils.fcompiler.pg`. + """ + + compiler_type = 'nv' + description = 'NVIDIA HPC SDK' + version_pattern = r'\s*(nvfortran|(pg(f77|f90|fortran)) \(aka nvfortran\)) (?P<version>[\d.-]+).*' + + executables = { + 'version_cmd': ["<F90>", "-V"], + 'compiler_f77': ["nvfortran"], + 'compiler_fix': ["nvfortran", "-Mfixed"], + 'compiler_f90': ["nvfortran"], + 'linker_so': ["<F90>"], + 'archiver': ["ar", "-cr"], + 'ranlib': ["ranlib"] + } + pic_flags = ['-fpic'] + + module_dir_switch = '-module ' + module_include_switch = '-I' + + def get_flags(self): + opt = ['-Minform=inform', '-Mnosecond_underscore'] + return self.pic_flags + opt + + def get_flags_opt(self): + return ['-fast'] + + def get_flags_debug(self): + return ['-g'] + + def get_flags_linker_so(self): + return ["-shared", '-fpic'] + + def runtime_library_dir_option(self, dir): + return '-R%s' % dir + +if __name__ == '__main__': + from distutils import log + log.set_verbosity(2) + from numpy.distutils import customized_fcompiler + print(customized_fcompiler(compiler='nv').get_version()) diff --git a/numpy/distutils/fcompiler/pathf95.py b/numpy/distutils/fcompiler/pathf95.py index 5de86f63aee6..0768cb12e87a 100644 --- a/numpy/distutils/fcompiler/pathf95.py +++ b/numpy/distutils/fcompiler/pathf95.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - from numpy.distutils.fcompiler import FCompiler compilers = ['PathScaleFCompiler'] diff --git a/numpy/distutils/fcompiler/pg.py b/numpy/distutils/fcompiler/pg.py index e6c816baaf61..72442c4fec61 100644 --- a/numpy/distutils/fcompiler/pg.py +++ b/numpy/distutils/fcompiler/pg.py @@ -1,10 +1,7 @@ # http://www.pgroup.com -from __future__ import division, absolute_import, print_function - import sys -import os -from numpy.distutils.fcompiler import FCompiler, dummy_fortran_file +from numpy.distutils.fcompiler import FCompiler from sys import platform from os.path import join, dirname, normpath @@ -34,7 +31,7 @@ class PGroupFCompiler(FCompiler): 'compiler_f77': ["pgfortran"], 'compiler_fix': ["pgfortran", "-Mfixed"], 'compiler_f90': ["pgfortran"], - 'linker_so': ["pgfortran", "-shared", "-fpic"], + 'linker_so': ["<F90>"], 'archiver': ["ar", "-cr"], 'ranlib': ["ranlib"] } @@ -57,78 +54,68 @@ def get_flags_debug(self): def get_flags_linker_so(self): return ["-dynamic", '-undefined', 'dynamic_lookup'] - def runtime_library_dir_option(self, dir): - return '-R"%s"' % dir - - -if sys.version_info >= (3, 5): - import subprocess - import shlex - import functools + else: + def get_flags_linker_so(self): + return ["-shared", '-fpic'] - class PGroupFlangCompiler(FCompiler): - compiler_type = 'flang' - description = 'Portland Group Fortran LLVM Compiler' - version_pattern = r'\s*(flang|clang) version (?P<version>[\d.-]+).*' + def runtime_library_dir_option(self, dir): + return '-R%s' % dir - ar_exe = 'lib.exe' - possible_executables = ['flang'] - executables = { - 'version_cmd': ["<F77>", "--version"], - 'compiler_f77': ["flang"], - 'compiler_fix': ["flang"], - 'compiler_f90': ["flang"], - 'linker_so': [None], - 'archiver': [ar_exe, "/verbose", "/OUT:"], - 'ranlib': None - } +import functools - library_switch = '/OUT:' # No space after /OUT:! - module_dir_switch = '-module ' # Don't remove ending space! +class PGroupFlangCompiler(FCompiler): + compiler_type = 'flang' + description = 'Portland Group Fortran LLVM Compiler' + version_pattern = r'\s*(flang|clang) version (?P<version>[\d.-]+).*' - def get_libraries(self): - opt = FCompiler.get_libraries(self) - opt.extend(['flang', 'flangrti', 'ompstub']) - return opt + ar_exe = 'lib.exe' + possible_executables = ['flang'] - @functools.lru_cache(maxsize=128) - def get_library_dirs(self): - """List of compiler library directories.""" - opt = FCompiler.get_library_dirs(self) - flang_dir = dirname(self.executables['compiler_f77'][0]) - opt.append(normpath(join(flang_dir, '..', 'lib'))) + executables = { + 'version_cmd': ["<F77>", "--version"], + 'compiler_f77': ["flang"], + 'compiler_fix': ["flang"], + 'compiler_f90': ["flang"], + 'linker_so': [None], + 'archiver': [ar_exe, "/verbose", "/OUT:"], + 'ranlib': None + } - return opt + library_switch = '/OUT:' # No space after /OUT:! + module_dir_switch = '-module ' # Don't remove ending space! - def get_flags(self): - return [] + def get_libraries(self): + opt = FCompiler.get_libraries(self) + opt.extend(['flang', 'flangrti', 'ompstub']) + return opt - def get_flags_free(self): - return [] + @functools.lru_cache(maxsize=128) + def get_library_dirs(self): + """List of compiler library directories.""" + opt = FCompiler.get_library_dirs(self) + flang_dir = dirname(self.executables['compiler_f77'][0]) + opt.append(normpath(join(flang_dir, '..', 'lib'))) - def get_flags_debug(self): - return ['-g'] + return opt - def get_flags_opt(self): - return ['-O3'] + def get_flags(self): + return [] - def get_flags_arch(self): - return [] + def get_flags_free(self): + return [] - def runtime_library_dir_option(self, dir): - raise NotImplementedError + def get_flags_debug(self): + return ['-g'] -else: - from numpy.distutils.fcompiler import CompilerNotFound + def get_flags_opt(self): + return ['-O3'] - # No point in supporting on older Pythons because not ABI compatible - class PGroupFlangCompiler(FCompiler): - compiler_type = 'flang' - description = 'Portland Group Fortran LLVM Compiler' + def get_flags_arch(self): + return [] - def get_version(self): - raise CompilerNotFound('Flang unsupported on Python < 3.5') + def runtime_library_dir_option(self, dir): + raise NotImplementedError if __name__ == '__main__': diff --git a/numpy/distutils/fcompiler/sun.py b/numpy/distutils/fcompiler/sun.py index d477d33087b0..d039f0b25705 100644 --- a/numpy/distutils/fcompiler/sun.py +++ b/numpy/distutils/fcompiler/sun.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - from numpy.distutils.ccompiler import simple_version_match from numpy.distutils.fcompiler import FCompiler @@ -44,7 +42,7 @@ def get_libraries(self): return opt def runtime_library_dir_option(self, dir): - return '-R"%s"' % dir + return '-R%s' % dir if __name__ == '__main__': from distutils import log diff --git a/numpy/distutils/fcompiler/vast.py b/numpy/distutils/fcompiler/vast.py index adc1591bde91..92a1647ba437 100644 --- a/numpy/distutils/fcompiler/vast.py +++ b/numpy/distutils/fcompiler/vast.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - import os from numpy.distutils.fcompiler.gnu import GnuFCompiler diff --git a/numpy/distutils/from_template.py b/numpy/distutils/from_template.py index 65c60c498c39..90d1f4c384c7 100644 --- a/numpy/distutils/from_template.py +++ b/numpy/distutils/from_template.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """ process_file(filename) @@ -45,8 +45,6 @@ <ctypereal=float,double,\\0,\\1> """ -from __future__ import division, absolute_import, print_function - __all__ = ['process_str', 'process_file'] import os @@ -208,26 +206,24 @@ def process_str(allstr): return writestr -include_src_re = re.compile(r"(\n|\A)\s*include\s*['\"](?P<name>[\w\d./\\]+[.]src)['\"]", re.I) +include_src_re = re.compile(r"(\n|\A)\s*include\s*['\"](?P<name>[\w\d./\\]+\.src)['\"]", re.I) def resolve_includes(source): d = os.path.dirname(source) - fid = open(source) - lines = [] - for line in fid: - m = include_src_re.match(line) - if m: - fn = m.group('name') - if not os.path.isabs(fn): - fn = os.path.join(d, fn) - if os.path.isfile(fn): - print('Including file', fn) - lines.extend(resolve_includes(fn)) + with open(source) as fid: + lines = [] + for line in fid: + m = include_src_re.match(line) + if m: + fn = m.group('name') + if not os.path.isabs(fn): + fn = os.path.join(d, fn) + if os.path.isfile(fn): + lines.extend(resolve_includes(fn)) + else: + lines.append(line) else: lines.append(line) - else: - lines.append(line) - fid.close() return lines def process_file(source): @@ -260,5 +256,6 @@ def main(): writestr = process_str(allstr) outfile.write(writestr) + if __name__ == "__main__": main() diff --git a/numpy/distutils/info.py b/numpy/distutils/info.py deleted file mode 100644 index 2f5310665cef..000000000000 --- a/numpy/distutils/info.py +++ /dev/null @@ -1,6 +0,0 @@ -""" -Enhanced distutils with Fortran compilers support and more. -""" -from __future__ import division, absolute_import, print_function - -postpone_import = True diff --git a/numpy/distutils/intelccompiler.py b/numpy/distutils/intelccompiler.py index 3386775ee56a..0fa1c11dd676 100644 --- a/numpy/distutils/intelccompiler.py +++ b/numpy/distutils/intelccompiler.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - import platform from distutils.unixccompiler import UnixCCompiler @@ -60,7 +58,7 @@ def __init__(self, verbose=0, dry_run=0, force=0): v = self.get_version() mpopt = 'openmp' if v and v < '15' else 'qopenmp' - self.cc_exe = ('icc -m64 -fPIC -fp-model strict -O3 ' + self.cc_exe = ('icc -std=c99 -m64 -fPIC -fp-model strict -O3 ' '-fomit-frame-pointer -{}').format(mpopt) compiler = self.cc_exe diff --git a/numpy/distutils/lib2def.py b/numpy/distutils/lib2def.py index 0a5364566437..820ed71f5808 100644 --- a/numpy/distutils/lib2def.py +++ b/numpy/distutils/lib2def.py @@ -1,8 +1,5 @@ -from __future__ import division, absolute_import, print_function - import re import sys -import os import subprocess __doc__ = """This module generates a DEF file from the symbols in @@ -25,7 +22,7 @@ py_ver = "%d%d" % tuple(sys.version_info[:2]) -DEFAULT_NM = 'nm -Cs' +DEFAULT_NM = ['nm', '-Cs'] DEF_HEADER = """LIBRARY python%s.dll ;CODE PRELOAD MOVEABLE DISCARDABLE @@ -62,13 +59,16 @@ def parse_cmd(): deffile = None return libfile, deffile -def getnm(nm_cmd = ['nm', '-Cs', 'python%s.lib' % py_ver]): +def getnm(nm_cmd=['nm', '-Cs', 'python%s.lib' % py_ver], shell=True): """Returns the output of nm_cmd via a pipe. -nm_output = getnam(nm_cmd = 'nm -Cs py_lib')""" - f = subprocess.Popen(nm_cmd, shell=True, stdout=subprocess.PIPE, universal_newlines=True) - nm_output = f.stdout.read() - f.stdout.close() +nm_output = getnm(nm_cmd = 'nm -Cs py_lib')""" + p = subprocess.Popen(nm_cmd, shell=shell, stdout=subprocess.PIPE, + stderr=subprocess.PIPE, universal_newlines=True) + nm_output, nm_err = p.communicate() + if p.returncode != 0: + raise RuntimeError('failed to run "%s": "%s"' % ( + ' '.join(nm_cmd), nm_err)) return nm_output def parse_nm(nm_output): @@ -110,7 +110,7 @@ def output_def(dlist, flist, header, file = sys.stdout): deffile = sys.stdout else: deffile = open(deffile, 'w') - nm_cmd = [str(DEFAULT_NM), str(libfile)] - nm_output = getnm(nm_cmd) + nm_cmd = DEFAULT_NM + [str(libfile)] + nm_output = getnm(nm_cmd, shell=False) dlist, flist = parse_nm(nm_output) output_def(dlist, flist, DEF_HEADER, deffile) diff --git a/numpy/distutils/line_endings.py b/numpy/distutils/line_endings.py index 5ecb104ffdf5..686e5ebd937f 100644 --- a/numpy/distutils/line_endings.py +++ b/numpy/distutils/line_endings.py @@ -1,9 +1,10 @@ """ Functions for converting from DOS to UNIX line endings """ -from __future__ import division, absolute_import, print_function +import os +import re +import sys -import sys, re, os def dos2unix(file): "Replace CRLF with LF in argument files. Print names of changed files." @@ -11,7 +12,8 @@ def dos2unix(file): print(file, "Directory!") return - data = open(file, "rb").read() + with open(file, "rb") as fp: + data = fp.read() if '\0' in data: print(file, "Binary!") return @@ -19,9 +21,8 @@ def dos2unix(file): newdata = re.sub("\r\n", "\n", data) if newdata != data: print('dos2unix:', file) - f = open(file, "wb") - f.write(newdata) - f.close() + with open(file, "wb") as f: + f.write(newdata) return file else: print(file, 'ok') @@ -45,7 +46,8 @@ def unix2dos(file): print(file, "Directory!") return - data = open(file, "rb").read() + with open(file, "rb") as fp: + data = fp.read() if '\0' in data: print(file, "Binary!") return @@ -53,9 +55,8 @@ def unix2dos(file): newdata = re.sub("\n", "\r\n", newdata) if newdata != data: print('unix2dos:', file) - f = open(file, "wb") - f.write(newdata) - f.close() + with open(file, "wb") as f: + f.write(newdata) return file else: print(file, 'ok') diff --git a/numpy/distutils/log.py b/numpy/distutils/log.py index 37f9fe5dd0ef..3347f56d6fe9 100644 --- a/numpy/distutils/log.py +++ b/numpy/distutils/log.py @@ -1,17 +1,11 @@ -# Colored log, requires Python 2.3 or up. -from __future__ import division, absolute_import, print_function - +# Colored log import sys -from distutils.log import * +from distutils.log import * # noqa: F403 from distutils.log import Log as old_Log from distutils.log import _global_log -if sys.version_info[0] < 3: - from .misc_util import (red_text, default_text, cyan_text, green_text, - is_sequence, is_string) -else: - from numpy.distutils.misc_util import (red_text, default_text, cyan_text, - green_text, is_sequence, is_string) +from numpy.distutils.misc_util import (red_text, default_text, cyan_text, + green_text, is_sequence, is_string) def _fix_args(args,flag=1): @@ -67,6 +61,8 @@ def set_threshold(level, force=False): ' %s to %s' % (prev_level, level)) return prev_level +def get_threshold(): + return _global_log.threshold def set_verbosity(v, force=False): prev_level = _global_log.threshold @@ -91,3 +87,25 @@ def set_verbosity(v, force=False): # don't use INFO,.. flags in set_verbosity, these flags are for set_threshold. set_verbosity(0, force=True) + + +_error = error +_warn = warn +_info = info +_debug = debug + + +def error(msg, *a, **kw): + _error(f"ERROR: {msg}", *a, **kw) + + +def warn(msg, *a, **kw): + _warn(f"WARN: {msg}", *a, **kw) + + +def info(msg, *a, **kw): + _info(f"INFO: {msg}", *a, **kw) + + +def debug(msg, *a, **kw): + _debug(f"DEBUG: {msg}", *a, **kw) diff --git a/numpy/distutils/mingw32ccompiler.py b/numpy/distutils/mingw32ccompiler.py index e7fa7bc0d0d5..fbe3655c965c 100644 --- a/numpy/distutils/mingw32ccompiler.py +++ b/numpy/distutils/mingw32ccompiler.py @@ -7,20 +7,16 @@ # 3. Force windows to use g77 """ -from __future__ import division, absolute_import, print_function - import os +import platform import sys import subprocess import re +import textwrap # Overwrite certain distutils.ccompiler functions: -import numpy.distutils.ccompiler - -if sys.version_info[0] < 3: - from . import log -else: - from numpy.distutils import log +import numpy.distutils.ccompiler # noqa: F401 +from numpy.distutils import log # NT stuff # 1. Make sure libpython<version>.a exists for gcc. If not, build it. # 2. Force windows to use gcc (we're struggling with MSVC and g77 support) @@ -28,12 +24,9 @@ # 3. Force windows to use g77 import distutils.cygwinccompiler -from distutils.version import StrictVersion -from numpy.distutils.ccompiler import gen_preprocess_options, gen_lib_options from distutils.unixccompiler import UnixCCompiler from distutils.msvccompiler import get_build_version as get_build_msvc_version -from distutils.errors import (DistutilsExecError, CompileError, - UnknownFileError) +from distutils.errors import UnknownFileError from numpy.distutils.misc_util import (msvc_runtime_library, msvc_runtime_version, msvc_runtime_major, @@ -68,36 +61,6 @@ def __init__ (self, distutils.cygwinccompiler.CygwinCCompiler.__init__ (self, verbose, dry_run, force) - # we need to support 3.2 which doesn't match the standard - # get_versions methods regex - if self.gcc_version is None: - import re - p = subprocess.Popen(['gcc', '-dumpversion'], shell=True, - stdout=subprocess.PIPE) - out_string = p.stdout.read() - p.stdout.close() - result = re.search(r'(\d+\.\d+)', out_string) - if result: - self.gcc_version = StrictVersion(result.group(1)) - - # A real mingw32 doesn't need to specify a different entry point, - # but cygwin 2.91.57 in no-cygwin-mode needs it. - if self.gcc_version <= "2.91.57": - entry_point = '--entry _DllMain@12' - else: - entry_point = '' - - if self.linker_dll == 'dllwrap': - # Commented out '--driver-name g++' part that fixes weird - # g++.exe: g++: No such file or directory - # error (mingw 1.0 in Enthon24 tree, gcc-3.4.5). - # If the --driver-name part is required for some environment - # then make the inclusion of this part specific to that - # environment. - self.linker = 'dllwrap' # --driver-name g++' - elif self.linker_dll == 'gcc': - self.linker = 'g++' - # **changes: eric jones 4/11/01 # 1. Check for import library on Windows. Build if it doesn't exist. @@ -120,42 +83,18 @@ def __init__ (self, # kind of bad consequences, like using Py_ModuleInit4 instead of # Py_ModuleInit4_64, etc... So we add it here if get_build_architecture() == 'AMD64': - if self.gcc_version < "4.0": - self.set_executables( - compiler='gcc -g -DDEBUG -DMS_WIN64 -mno-cygwin -O0 -Wall', - compiler_so='gcc -g -DDEBUG -DMS_WIN64 -mno-cygwin -O0' - ' -Wall -Wstrict-prototypes', - linker_exe='gcc -g -mno-cygwin', - linker_so='gcc -g -mno-cygwin -shared') - else: - # gcc-4 series releases do not support -mno-cygwin option - self.set_executables( - compiler='gcc -g -DDEBUG -DMS_WIN64 -O0 -Wall', - compiler_so='gcc -g -DDEBUG -DMS_WIN64 -O0 -Wall -Wstrict-prototypes', - linker_exe='gcc -g', - linker_so='gcc -g -shared') + self.set_executables( + compiler='gcc -g -DDEBUG -DMS_WIN64 -O0 -Wall', + compiler_so='gcc -g -DDEBUG -DMS_WIN64 -O0 -Wall ' + '-Wstrict-prototypes', + linker_exe='gcc -g', + linker_so='gcc -g -shared') else: - if self.gcc_version <= "3.0.0": - self.set_executables( - compiler='gcc -mno-cygwin -O2 -w', - compiler_so='gcc -mno-cygwin -mdll -O2 -w' - ' -Wstrict-prototypes', - linker_exe='g++ -mno-cygwin', - linker_so='%s -mno-cygwin -mdll -static %s' % - (self.linker, entry_point)) - elif self.gcc_version < "4.0": - self.set_executables( - compiler='gcc -mno-cygwin -O2 -Wall', - compiler_so='gcc -mno-cygwin -O2 -Wall' - ' -Wstrict-prototypes', - linker_exe='g++ -mno-cygwin', - linker_so='g++ -mno-cygwin -shared') - else: - # gcc-4 series releases do not support -mno-cygwin option - self.set_executables(compiler='gcc -O2 -Wall', - compiler_so='gcc -O2 -Wall -Wstrict-prototypes', - linker_exe='g++ ', - linker_so='g++ -shared') + self.set_executables( + compiler='gcc -O2 -Wall', + compiler_so='gcc -O2 -Wall -Wstrict-prototypes', + linker_exe='g++ ', + linker_so='g++ -shared') # added for python2.3 support # we can't pass it through set_executables because pre 2.2 would fail self.compiler_cxx = ['g++'] @@ -205,10 +144,7 @@ def link(self, extra_postargs, build_temp, target_lang) - if self.gcc_version < "3.0.0": - func = distutils.cygwinccompiler.CygwinCCompiler.link - else: - func = UnixCCompiler.link + func = UnixCCompiler.link func(*args[:func.__code__.co_argcount]) return @@ -273,21 +209,24 @@ def find_python_dll(): # search in the file system for possible candidates major_version, minor_version = tuple(sys.version_info[:2]) - patterns = ['python%d%d.dll'] - - for pat in patterns: - dllname = pat % (major_version, minor_version) - print("Looking for %s" % dllname) - for folder in lib_dirs: - dll = os.path.join(folder, dllname) - if os.path.exists(dll): - return dll - + implementation = platform.python_implementation() + if implementation == 'CPython': + dllname = f'python{major_version}{minor_version}.dll' + elif implementation == 'PyPy': + dllname = f'libpypy{major_version}-c.dll' + else: + dllname = 'Unknown platform {implementation}' + print("Looking for %s" % dllname) + for folder in lib_dirs: + dll = os.path.join(folder, dllname) + if os.path.exists(dll): + return dll + raise ValueError("%s not found in %s" % (dllname, lib_dirs)) def dump_table(dll): - st = subprocess.Popen(["objdump.exe", "-p", dll], stdout=subprocess.PIPE) - return st.stdout.readlines() + st = subprocess.check_output(["objdump.exe", "-p", dll]) + return st.split(b'\n') def generate_def(dll, dfile): """Given a dll file location, get all its exported symbols and dump them @@ -312,15 +251,14 @@ def generate_def(dll, dfile): if len(syms) == 0: log.warn('No symbols found in %s' % dll) - d = open(dfile, 'w') - d.write('LIBRARY %s\n' % os.path.basename(dll)) - d.write(';CODE PRELOAD MOVEABLE DISCARDABLE\n') - d.write(';DATA PRELOAD SINGLE\n') - d.write('\nEXPORTS\n') - for s in syms: - #d.write('@%d %s\n' % (s[0], s[1])) - d.write('%s\n' % s[1]) - d.close() + with open(dfile, 'w') as d: + d.write('LIBRARY %s\n' % os.path.basename(dll)) + d.write(';CODE PRELOAD MOVEABLE DISCARDABLE\n') + d.write(';DATA PRELOAD SINGLE\n') + d.write('\nEXPORTS\n') + for s in syms: + #d.write('@%d %s\n' % (s[0], s[1])) + d.write('%s\n' % s[1]) def find_dll(dll_name): @@ -473,7 +411,7 @@ def _build_import_library_amd64(): # generate import library from this symbol list cmd = ['dlltool', '-d', def_file, '-l', out_file] - subprocess.Popen(cmd) + subprocess.check_call(cmd) def _build_import_library_x86(): """ Build the import libraries for Mingw32-gcc on Windows @@ -507,16 +445,19 @@ def _build_import_library_x86(): def_name = "python%d%d.def" % tuple(sys.version_info[:2]) def_file = os.path.join(sys.prefix, 'libs', def_name) - nm_cmd = '%s %s' % (lib2def.DEFAULT_NM, lib_file) - nm_output = lib2def.getnm(nm_cmd) + nm_output = lib2def.getnm( + lib2def.DEFAULT_NM + [lib_file], shell=False) dlist, flist = lib2def.parse_nm(nm_output) - lib2def.output_def(dlist, flist, lib2def.DEF_HEADER, open(def_file, 'w')) + with open(def_file, 'w') as fid: + lib2def.output_def(dlist, flist, lib2def.DEF_HEADER, fid) dll_name = find_python_dll () - args = (dll_name, def_file, out_file) - cmd = 'dlltool --dllname "%s" --def "%s" --output-lib "%s"' % args - status = os.system(cmd) - # for now, fail silently + + cmd = ["dlltool", + "--dllname", dll_name, + "--def", def_file, + "--output-lib", out_file] + status = subprocess.check_output(cmd) if status: log.warn('Failed to build import library for gcc. Linking will fail.') return @@ -549,10 +490,12 @@ def _build_import_library_x86(): # Value from msvcrt.CRT_ASSEMBLY_VERSION under Python 3.3.0 # on Windows XP: _MSVCRVER_TO_FULLVER['100'] = "10.0.30319.460" - if hasattr(msvcrt, "CRT_ASSEMBLY_VERSION"): - major, minor, rest = msvcrt.CRT_ASSEMBLY_VERSION.split(".", 2) - _MSVCRVER_TO_FULLVER[major + minor] = msvcrt.CRT_ASSEMBLY_VERSION - del major, minor, rest + crt_ver = getattr(msvcrt, 'CRT_ASSEMBLY_VERSION', None) + if crt_ver is not None: # Available at least back to Python 3.3 + maj, min = re.match(r'(\d+)\.(\d)', crt_ver).groups() + _MSVCRVER_TO_FULLVER[maj + min] = crt_ver + del maj, min + del crt_ver except ImportError: # If we are here, means python was not built with MSVC. Not sure what # to do in that case: manifest building will fail, but it should not be @@ -566,28 +509,28 @@ def msvc_manifest_xml(maj, min): fullver = _MSVCRVER_TO_FULLVER[str(maj * 10 + min)] except KeyError: raise ValueError("Version %d,%d of MSVCRT not supported yet" % - (maj, min)) + (maj, min)) from None # Don't be fooled, it looks like an XML, but it is not. In particular, it # should not have any space before starting, and its size should be # divisible by 4, most likely for alignment constraints when the xml is # embedded in the binary... # This template was copied directly from the python 2.6 binary (using # strings.exe from mingw on python.exe). - template = """\ -<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> - <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> - <security> - <requestedPrivileges> - <requestedExecutionLevel level="asInvoker" uiAccess="false"></requestedExecutionLevel> - </requestedPrivileges> - </security> - </trustInfo> - <dependency> - <dependentAssembly> - <assemblyIdentity type="win32" name="Microsoft.VC%(maj)d%(min)d.CRT" version="%(fullver)s" processorArchitecture="*" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity> - </dependentAssembly> - </dependency> -</assembly>""" + template = textwrap.dedent("""\ + <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> + <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> + <security> + <requestedPrivileges> + <requestedExecutionLevel level="asInvoker" uiAccess="false"></requestedExecutionLevel> + </requestedPrivileges> + </security> + </trustInfo> + <dependency> + <dependentAssembly> + <assemblyIdentity type="win32" name="Microsoft.VC%(maj)d%(min)d.CRT" version="%(fullver)s" processorArchitecture="*" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity> + </dependentAssembly> + </dependency> + </assembly>""") return template % {'fullver': fullver, 'maj': maj, 'min': min} @@ -647,11 +590,9 @@ def generate_manifest(config): if msver is not None: if msver >= 8: check_embedded_msvcr_match_linked(msver) - ma = int(msver) - mi = int((msver - ma) * 10) + ma_str, mi_str = str(msver).split('.') # Write the manifest file - manxml = msvc_manifest_xml(ma, mi) - man = open(manifest_name(config), "w") - config.temp_files.append(manifest_name(config)) - man.write(manxml) - man.close() + manxml = msvc_manifest_xml(int(ma_str), int(mi_str)) + with open(manifest_name(config), "w") as man: + config.temp_files.append(manifest_name(config)) + man.write(manxml) diff --git a/numpy/distutils/misc_util.py b/numpy/distutils/misc_util.py index 41f0b1f6154c..513be75db2c5 100644 --- a/numpy/distutils/misc_util.py +++ b/numpy/distutils/misc_util.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - import os import re import sys @@ -10,14 +8,13 @@ import subprocess import shutil import multiprocessing +import textwrap +import importlib.util +from threading import local as tlocal +from functools import reduce import distutils from distutils.errors import DistutilsError -from distutils.msvccompiler import get_build_architecture -try: - from threading import local as tlocal -except ImportError: - from dummy_threading import local as tlocal # stores temporary directory of each thread to only create one per thread _tdata = tlocal() @@ -34,10 +31,6 @@ def clean_up_temporary_directory(): atexit.register(clean_up_temporary_directory) -from numpy.distutils.compat import get_exception -from numpy.compat import basestring -from numpy.compat import npy_load_module - __all__ = ['Configuration', 'get_numpy_include_dirs', 'default_config_dict', 'dict_append', 'appendpath', 'generate_config_py', 'get_cmd', 'allpath', 'get_mathlibs', @@ -48,10 +41,11 @@ def clean_up_temporary_directory(): 'get_script_files', 'get_lib_source_files', 'get_data_files', 'dot_join', 'get_frame', 'minrelpath', 'njoin', 'is_sequence', 'is_string', 'as_list', 'gpaths', 'get_language', - 'quote_args', 'get_build_architecture', 'get_info', 'get_pkg_info', - 'get_num_build_jobs'] + 'get_build_architecture', 'get_info', 'get_pkg_info', + 'get_num_build_jobs', 'sanitize_cxx_flags', + 'exec_mod_from_location'] -class InstallableLib(object): +class InstallableLib: """ Container to hold information on an installable library. @@ -84,7 +78,9 @@ def get_num_build_jobs(): Get number of parallel build jobs set by the --parallel command line argument of setup.py If the command did not receive a setting the environment variable - NPY_NUM_BUILD_JOBS checked and if that is unset it returns 1. + NPY_NUM_BUILD_JOBS is checked. If that is unset, return the number of + processors on the system, with a maximum of 8 (to prevent + overloading the system if there a lot of CPUs). Returns ------- @@ -97,6 +93,7 @@ def get_num_build_jobs(): cpu_count = len(os.sched_getaffinity(0)) except AttributeError: cpu_count = multiprocessing.cpu_count() + cpu_count = min(cpu_count, 8) envjobs = int(os.environ.get("NPY_NUM_BUILD_JOBS", cpu_count)) dist = get_distribution() # may be None during configuration @@ -113,6 +110,13 @@ def get_num_build_jobs(): return max(x for x in cmdattr if x is not None) def quote_args(args): + """Quote list of arguments. + + .. deprecated:: 1.22. + """ + import warnings + warnings.warn('"quote_args" is deprecated.', + DeprecationWarning, stacklevel=2) # don't used _nt_quote_args as it does not check if # args items already have quotes or not. args = list(args) @@ -124,8 +128,8 @@ def quote_args(args): def allpath(name): "Convert a /-separated pathname to one using the OS's path separator." - splitted = name.split('/') - return os.path.join(*splitted) + split = name.split('/') + return os.path.join(*split) def rel_path(path, parent_path): """Return path relative to parent_path.""" @@ -165,7 +169,6 @@ def get_path_from_frame(frame, parent_path=None): # we're probably running setup.py as execfile("setup.py") # (likely we're building an egg) d = os.path.abspath('.') - # hmm, should we use sys.argv[0] like in __builtin__ case? if parent_path is not None: d = rel_path(d, parent_path) @@ -216,15 +219,14 @@ def get_mathlibs(path=None): raise DistutilsError('_numpyconfig.h not found in numpy include ' 'dirs %r' % (dirs,)) - fid = open(config_file) - mathlibs = [] - s = '#define MATHLIB' - for line in fid: - if line.startswith(s): - value = line[len(s):].strip() - if value: - mathlibs.extend(value.split(',')) - fid.close() + with open(config_file) as fid: + mathlibs = [] + s = '#define MATHLIB' + for line in fid: + if line.startswith(s): + value = line[len(s):].strip() + if value: + mathlibs.extend(value.split(',')) return mathlibs def minrelpath(path): @@ -257,7 +259,7 @@ def minrelpath(path): return os.sep.join(l) def sorted_glob(fileglob): - """sorts output of python glob for http://bugs.python.org/issue30461 + """sorts output of python glob for https://bugs.python.org/issue30461 to allow extensions to have reproducible build results""" return sorted(glob.glob(fileglob)) @@ -317,7 +319,7 @@ def make_temp_file(suffix='', prefix='', text=True): return fo, name # Hooks for colored terminal output. -# See also http://www.livinglogic.de/Python/ansistyle +# See also https://web.archive.org/web/20100314204946/http://www.livinglogic.de/Python/ansistyle def terminal_has_colors(): if sys.platform=='cygwin' and 'USE_COLOR' not in os.environ: # Avoid importing curses that causes illegal operation @@ -381,10 +383,42 @@ def blue_text(s): ######################### -def cyg2win32(path): - if sys.platform=='cygwin' and path.startswith('/cygdrive'): - path = path[10] + ':' + os.path.normcase(path[11:]) - return path +def cyg2win32(path: str) -> str: + """Convert a path from Cygwin-native to Windows-native. + + Uses the cygpath utility (part of the Base install) to do the + actual conversion. Falls back to returning the original path if + this fails. + + Handles the default ``/cygdrive`` mount prefix as well as the + ``/proc/cygdrive`` portable prefix, custom cygdrive prefixes such + as ``/`` or ``/mnt``, and absolute paths such as ``/usr/src/`` or + ``/home/username`` + + Parameters + ---------- + path : str + The path to convert + + Returns + ------- + converted_path : str + The converted path + + Notes + ----- + Documentation for cygpath utility: + https://cygwin.com/cygwin-ug-net/cygpath.html + Documentation for the C function it wraps: + https://cygwin.com/cygwin-api/func-cygwin-conv-path.html + + """ + if sys.platform != "cygwin": + return path + return subprocess.check_output( + ["/usr/bin/cygpath", "--windows", path], universal_newlines=True + ) + def mingw32(): """Return true when using mingw32 environment. @@ -430,9 +464,9 @@ def msvc_runtime_major(): ######################### #XXX need support for .C that is also C++ -cxx_ext_match = re.compile(r'.*[.](cpp|cxx|cc)\Z', re.I).match -fortran_ext_match = re.compile(r'.*[.](f90|f95|f77|for|ftn|f)\Z', re.I).match -f90_ext_match = re.compile(r'.*[.](f90|f95)\Z', re.I).match +cxx_ext_match = re.compile(r'.*\.(cpp|cxx|cc)\Z', re.I).match +fortran_ext_match = re.compile(r'.*\.(f90|f95|f77|for|ftn|f)\Z', re.I).match +f90_ext_match = re.compile(r'.*\.(f90|f95)\Z', re.I).match f90_module_name_match = re.compile(r'\s*module\s*(?P<name>[\w_]+)', re.I).match def _get_f90_modules(source): """Return a list of Fortran f90 module names that @@ -441,18 +475,17 @@ def _get_f90_modules(source): if not f90_ext_match(source): return [] modules = [] - f = open(source, 'r') - for line in f: - m = f90_module_name_match(line) - if m: - name = m.group('name') - modules.append(name) - # break # XXX can we assume that there is one module per file? - f.close() + with open(source, 'r') as f: + for line in f: + m = f90_module_name_match(line) + if m: + name = m.group('name') + modules.append(name) + # break # XXX can we assume that there is one module per file? return modules def is_string(s): - return isinstance(s, basestring) + return isinstance(s, str) def all_strings(lst): """Return True if all items in lst are string objects. """ @@ -471,7 +504,7 @@ def is_sequence(seq): return True def is_glob_pattern(s): - return is_string(s) and ('*' in s or '?' is s) + return is_string(s) and ('*' in s or '?' in s) def as_list(seq): if is_sequence(seq): @@ -727,7 +760,7 @@ def get_frame(level=0): ###################### -class Configuration(object): +class Configuration: _list_keys = ['packages', 'ext_modules', 'data_files', 'include_dirs', 'libraries', 'headers', 'scripts', 'py_modules', @@ -858,7 +891,7 @@ def info(self, message): print(message) def warn(self, message): - sys.stderr.write('Warning: %s' % (message,)) + sys.stderr.write('Warning: %s\n' % (message,)) def set_options(self, **options): """ @@ -911,9 +944,8 @@ def _get_configuration_from_setup_py(self, setup_py, try: setup_name = os.path.splitext(os.path.basename(setup_py))[0] n = dot_join(self.name, subpackage_name, setup_name) - setup_module = npy_load_module('_'.join(n.split('.')), - setup_py, - ('.py', 'U', 1)) + setup_module = exec_mod_from_location( + '_'.join(n.split('.')), setup_py) if not hasattr(setup_module, 'configuration'): if not self.options['assume_default_configuration']: self.warn('Assuming default configuration '\ @@ -925,18 +957,8 @@ def _get_configuration_from_setup_py(self, setup_py, else: pn = dot_join(*([parent_name] + subpackage_name.split('.')[:-1])) args = (pn,) - def fix_args_py2(args): - if setup_module.configuration.__code__.co_argcount > 1: - args = args + (self.top_path,) - return args - def fix_args_py3(args): - if setup_module.configuration.__code__.co_argcount > 1: - args = args + (self.top_path,) - return args - if sys.version_info[0] < 3: - args = fix_args_py2(args) - else: - args = fix_args_py3(args) + if setup_module.configuration.__code__.co_argcount > 1: + args = args + (self.top_path,) config = setup_module.configuration(*args) if config.name!=dot_join(parent_name, subpackage_name): self.warn('Subpackage %r configuration returned as %r' % \ @@ -1564,7 +1586,6 @@ def _add_library(self, name, sources, install_dir, build_info): """Common implementation for add_library and add_installed_library. Do not use directly""" build_info = copy.copy(build_info) - name = name #+ '__OF__' + self.name build_info['sources'] = sources # Sometimes, depends is not set up to an empty list by default, and if @@ -1687,10 +1708,44 @@ def add_npy_pkg_config(self, template, install_dir, subst_dict=None): and will be installed as foo.ini in the 'lib' subpath. + When cross-compiling with numpy distutils, it might be necessary to + use modified npy-pkg-config files. Using the default/generated files + will link with the host libraries (i.e. libnpymath.a). For + cross-compilation you of-course need to link with target libraries, + while using the host Python installation. + + You can copy out the numpy/core/lib/npy-pkg-config directory, add a + pkgdir value to the .ini files and set NPY_PKG_CONFIG_PATH environment + variable to point to the directory with the modified npy-pkg-config + files. + + Example npymath.ini modified for cross-compilation:: + + [meta] + Name=npymath + Description=Portable, core math library implementing C99 standard + Version=0.1 + + [variables] + pkgname=numpy.core + pkgdir=/build/arm-linux-gnueabi/sysroot/usr/lib/python3.7/site-packages/numpy/core + prefix=${pkgdir} + libdir=${prefix}/lib + includedir=${prefix}/include + + [default] + Libs=-L${libdir} -lnpymath + Cflags=-I${includedir} + Requires=mlib + + [msvc] + Libs=/LIBPATH:${libdir} npymath.lib + Cflags=/INCLUDE:${includedir} + Requires=mlib + """ if subst_dict is None: subst_dict = {} - basename = os.path.splitext(template)[0] template = os.path.join(self.package_path, template) if self.name in self.installed_pkg_config: @@ -1833,81 +1888,68 @@ def append_to(self, extlib): def _get_svn_revision(self, path): """Return path's SVN revision number. """ - revision = None - m = None - cwd = os.getcwd() try: - os.chdir(path or '.') - p = subprocess.Popen(['svnversion'], shell=True, - stdout=subprocess.PIPE, stderr=None, - close_fds=True) - sout = p.stdout - m = re.match(r'(?P<revision>\d+)', sout.read()) - except Exception: + output = subprocess.check_output(['svnversion'], cwd=path) + except (subprocess.CalledProcessError, OSError): pass - os.chdir(cwd) - if m: - revision = int(m.group('revision')) - return revision + else: + m = re.match(rb'(?P<revision>\d+)', output) + if m: + return int(m.group('revision')) + if sys.platform=='win32' and os.environ.get('SVN_ASP_DOT_NET_HACK', None): entries = njoin(path, '_svn', 'entries') else: entries = njoin(path, '.svn', 'entries') if os.path.isfile(entries): - f = open(entries) - fstr = f.read() - f.close() + with open(entries) as f: + fstr = f.read() if fstr[:5] == '<?xml': # pre 1.4 m = re.search(r'revision="(?P<revision>\d+)"', fstr) if m: - revision = int(m.group('revision')) + return int(m.group('revision')) else: # non-xml entries file --- check to be sure that m = re.search(r'dir[\n\r]+(?P<revision>\d+)', fstr) if m: - revision = int(m.group('revision')) - return revision + return int(m.group('revision')) + return None def _get_hg_revision(self, path): """Return path's Mercurial revision number. """ - revision = None - m = None - cwd = os.getcwd() try: - os.chdir(path or '.') - p = subprocess.Popen(['hg identify --num'], shell=True, - stdout=subprocess.PIPE, stderr=None, - close_fds=True) - sout = p.stdout - m = re.match(r'(?P<revision>\d+)', sout.read()) - except Exception: + output = subprocess.check_output( + ['hg', 'identify', '--num'], cwd=path) + except (subprocess.CalledProcessError, OSError): pass - os.chdir(cwd) - if m: - revision = int(m.group('revision')) - return revision + else: + m = re.match(rb'(?P<revision>\d+)', output) + if m: + return int(m.group('revision')) + branch_fn = njoin(path, '.hg', 'branch') branch_cache_fn = njoin(path, '.hg', 'branch.cache') if os.path.isfile(branch_fn): branch0 = None - f = open(branch_fn) - revision0 = f.read().strip() - f.close() + with open(branch_fn) as f: + revision0 = f.read().strip() branch_map = {} - for line in file(branch_cache_fn, 'r'): - branch1, revision1 = line.split()[:2] - if revision1==revision0: - branch0 = branch1 - try: - revision1 = int(revision1) - except ValueError: - continue - branch_map[branch1] = revision1 + with open(branch_cache_fn, 'r') as f: + for line in f: + branch1, revision1 = line.split()[:2] + if revision1==revision0: + branch0 = branch1 + try: + revision1 = int(revision1) + except ValueError: + continue + branch_map[branch1] = revision1 - revision = branch_map.get(branch0) - return revision + return branch_map.get(branch0) + + return None def get_version(self, version_file=None, version_variable=None): @@ -1949,11 +1991,10 @@ def get_version(self, version_file=None, version_variable=None): name = os.path.splitext(os.path.basename(fn))[0] n = dot_join(self.name, name) try: - version_module = npy_load_module('_'.join(n.split('.')), - fn, info) - except ImportError: - msg = get_exception() - self.warn(str(msg)) + version_module = exec_mod_from_location( + '_'.join(n.split('.')), fn) + except ImportError as e: + self.warn(str(e)) version_module = None if version_module is None: continue @@ -1962,6 +2003,13 @@ def get_version(self, version_file=None, version_variable=None): version = getattr(version_module, a, None) if version is not None: break + + # Try if versioneer module + try: + version = version_module.get_versions()['version'] + except AttributeError: + pass + if version is not None: break @@ -2005,11 +2053,9 @@ def generate_svn_version_py(): if not os.path.isfile(target): version = str(revision) self.info('Creating %s (version=%r)' % (target, version)) - f = open(target, 'w') - f.write('version = %r\n' % (version)) - f.close() + with open(target, 'w') as f: + f.write('version = %r\n' % (version)) - import atexit def rm_file(f=target,p=self.info): if delete: try: os.remove(f); p('removed '+f) @@ -2047,11 +2093,9 @@ def generate_hg_version_py(): if not os.path.isfile(target): version = str(revision) self.info('Creating %s (version=%r)' % (target, version)) - f = open(target, 'w') - f.write('version = %r\n' % (version)) - f.close() + with open(target, 'w') as f: + f.write('version = %r\n' % (version)) - import atexit def rm_file(f=target,p=self.info): if delete: try: os.remove(f); p('removed '+f) @@ -2110,10 +2154,22 @@ def get_numpy_include_dirs(): return include_dirs def get_npy_pkg_dir(): - """Return the path where to find the npy-pkg-config directory.""" - # XXX: import here for bootstrapping reasons - import numpy - d = os.path.join(os.path.dirname(numpy.__file__), + """Return the path where to find the npy-pkg-config directory. + + If the NPY_PKG_CONFIG_PATH environment variable is set, the value of that + is returned. Otherwise, a path inside the location of the numpy module is + returned. + + The NPY_PKG_CONFIG_PATH can be useful when cross-compiling, maintaining + customized npy-pkg-config .ini files for the cross-compilation + environment, and using them when cross-compiling. + + """ + d = os.environ.get('NPY_PKG_CONFIG_PATH') + if d is not None: + return d + spec = importlib.util.find_spec('numpy') + d = os.path.join(os.path.dirname(spec.origin), 'core', 'lib', 'npy-pkg-config') return d @@ -2216,17 +2272,13 @@ def get_info(pkgname, dirs=None): return info def is_bootstrapping(): - if sys.version_info[0] >= 3: - import builtins - else: - import __builtin__ as builtins + import builtins try: builtins.__NUMPY_SETUP__ return True except AttributeError: return False - __NUMPY_SETUP__ = False ######################### @@ -2287,56 +2339,129 @@ def generate_config_py(target): from numpy.distutils.system_info import system_info from distutils.dir_util import mkpath mkpath(os.path.dirname(target)) - f = open(target, 'w') - f.write('# This file is generated by numpy\'s %s\n' % (os.path.basename(sys.argv[0]))) - f.write('# It contains system_info results at the time of building this package.\n') - f.write('__all__ = ["get_info","show"]\n\n') + with open(target, 'w') as f: + f.write('# This file is generated by numpy\'s %s\n' % (os.path.basename(sys.argv[0]))) + f.write('# It contains system_info results at the time of building this package.\n') + f.write('__all__ = ["get_info","show"]\n\n') - # For gfortran+msvc combination, extra shared libraries may exist - f.write(""" + # For gfortran+msvc combination, extra shared libraries may exist + f.write(textwrap.dedent(""" + import os + import sys -import os -import sys + extra_dll_dir = os.path.join(os.path.dirname(__file__), '.libs') -extra_dll_dir = os.path.join(os.path.dirname(__file__), '.libs') + if sys.platform == 'win32' and os.path.isdir(extra_dll_dir): + if sys.version_info >= (3, 8): + os.add_dll_directory(extra_dll_dir) + else: + os.environ.setdefault('PATH', '') + os.environ['PATH'] += os.pathsep + extra_dll_dir + + """)) + + for k, i in system_info.saved_results.items(): + f.write('%s=%r\n' % (k, i)) + f.write(textwrap.dedent(r''' + def get_info(name): + g = globals() + return g.get(name, g.get(name + "_info", {})) + + def show(): + """ + Show libraries in the system on which NumPy was built. + + Print information about various resources (libraries, library + directories, include directories, etc.) in the system on which + NumPy was built. + + See Also + -------- + get_include : Returns the directory containing NumPy C + header files. + + Notes + ----- + 1. Classes specifying the information to be printed are defined + in the `numpy.distutils.system_info` module. + + Information may include: + + * ``language``: language used to write the libraries (mostly + C or f77) + * ``libraries``: names of libraries found in the system + * ``library_dirs``: directories containing the libraries + * ``include_dirs``: directories containing library header files + * ``src_dirs``: directories containing library source files + * ``define_macros``: preprocessor macros used by + ``distutils.setup`` + * ``baseline``: minimum CPU features required + * ``found``: dispatched features supported in the system + * ``not found``: dispatched features that are not supported + in the system + + 2. NumPy BLAS/LAPACK Installation Notes + + Installing a numpy wheel (``pip install numpy`` or force it + via ``pip install numpy --only-binary :numpy: numpy``) includes + an OpenBLAS implementation of the BLAS and LAPACK linear algebra + APIs. In this case, ``library_dirs`` reports the original build + time configuration as compiled with gcc/gfortran; at run time + the OpenBLAS library is in + ``site-packages/numpy.libs/`` (linux), or + ``site-packages/numpy/.dylibs/`` (macOS), or + ``site-packages/numpy/.libs/`` (windows). + + Installing numpy from source + (``pip install numpy --no-binary numpy``) searches for BLAS and + LAPACK dynamic link libraries at build time as influenced by + environment variables NPY_BLAS_LIBS, NPY_CBLAS_LIBS, and + NPY_LAPACK_LIBS; or NPY_BLAS_ORDER and NPY_LAPACK_ORDER; + or the optional file ``~/.numpy-site.cfg``. + NumPy remembers those locations and expects to load the same + libraries at run-time. + In NumPy 1.21+ on macOS, 'accelerate' (Apple's Accelerate BLAS + library) is in the default build-time search order after + 'openblas'. + + Examples + -------- + >>> import numpy as np + >>> np.show_config() + blas_opt_info: + language = c + define_macros = [('HAVE_CBLAS', None)] + libraries = ['openblas', 'openblas'] + library_dirs = ['/usr/local/lib'] + """ + from numpy.core._multiarray_umath import ( + __cpu_features__, __cpu_baseline__, __cpu_dispatch__ + ) + for name,info_dict in globals().items(): + if name[0] == "_" or type(info_dict) is not type({}): continue + print(name + ":") + if not info_dict: + print(" NOT AVAILABLE") + for k,v in info_dict.items(): + v = str(v) + if k == "sources" and len(v) > 200: + v = v[:60] + " ...\n... " + v[-60:] + print(" %s = %s" % (k,v)) + + features_found, features_not_found = [], [] + for feature in __cpu_dispatch__: + if __cpu_features__[feature]: + features_found.append(feature) + else: + features_not_found.append(feature) + + print("Supported SIMD extensions in this NumPy install:") + print(" baseline = %s" % (','.join(__cpu_baseline__))) + print(" found = %s" % (','.join(features_found))) + print(" not found = %s" % (','.join(features_not_found))) + + ''')) -if os.path.isdir(extra_dll_dir) and sys.platform == 'win32': - try: - from ctypes import windll, c_wchar_p - _AddDllDirectory = windll.kernel32.AddDllDirectory - _AddDllDirectory.argtypes = [c_wchar_p] - # Needed to initialize AddDllDirectory modifications - windll.kernel32.SetDefaultDllDirectories(0x1000) - except AttributeError: - def _AddDllDirectory(dll_directory): - os.environ.setdefault('PATH', '') - os.environ['PATH'] += os.pathsep + dll_directory - - _AddDllDirectory(extra_dll_dir) - -""") - - for k, i in system_info.saved_results.items(): - f.write('%s=%r\n' % (k, i)) - f.write(r''' -def get_info(name): - g = globals() - return g.get(name, g.get(name + "_info", {})) - -def show(): - for name,info_dict in globals().items(): - if name[0] == "_" or type(info_dict) is not type({}): continue - print(name + ":") - if not info_dict: - print(" NOT AVAILABLE") - for k,v in info_dict.items(): - v = str(v) - if k == "sources" and len(v) > 200: - v = v[:60] + " ...\n... " + v[-60:] - print(" %s = %s" % (k,v)) - ''') - - f.close() return target def msvc_version(compiler): @@ -2346,3 +2471,32 @@ def msvc_version(compiler): raise ValueError("Compiler instance is not msvc (%s)"\ % compiler.compiler_type) return compiler._MSVCCompiler__version + +def get_build_architecture(): + # Importing distutils.msvccompiler triggers a warning on non-Windows + # systems, so delay the import to here. + from distutils.msvccompiler import get_build_architecture + return get_build_architecture() + + +_cxx_ignore_flags = {'-Werror=implicit-function-declaration', '-std=c99'} + + +def sanitize_cxx_flags(cxxflags): + ''' + Some flags are valid for C but not C++. Prune them. + ''' + return [flag for flag in cxxflags if flag not in _cxx_ignore_flags] + + +def exec_mod_from_location(modname, modfile): + ''' + Use importlib machinery to import a module `modname` from the file + `modfile`. Depending on the `spec.loader`, the module may not be + registered in sys.modules. + ''' + spec = importlib.util.spec_from_file_location(modname, modfile) + foo = importlib.util.module_from_spec(spec) + spec.loader.exec_module(foo) + return foo + diff --git a/numpy/distutils/msvc9compiler.py b/numpy/distutils/msvc9compiler.py index e9cc334a5ec4..68239495d6c7 100644 --- a/numpy/distutils/msvc9compiler.py +++ b/numpy/distutils/msvc9compiler.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - import os from distutils.msvc9compiler import MSVCCompiler as _MSVCCompiler diff --git a/numpy/distutils/msvccompiler.py b/numpy/distutils/msvccompiler.py index 0cb4bf9794be..681a254b87ee 100644 --- a/numpy/distutils/msvccompiler.py +++ b/numpy/distutils/msvccompiler.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - import os from distutils.msvccompiler import MSVCCompiler as _MSVCCompiler diff --git a/numpy/distutils/npy_pkg_config.py b/numpy/distutils/npy_pkg_config.py index 6fe517659ff3..f6e3ad3974ca 100644 --- a/numpy/distutils/npy_pkg_config.py +++ b/numpy/distutils/npy_pkg_config.py @@ -1,20 +1,15 @@ -from __future__ import division, absolute_import, print_function - import sys import re import os -if sys.version_info[0] < 3: - from ConfigParser import RawConfigParser, NoOptionError -else: - from configparser import RawConfigParser, NoOptionError +from configparser import RawConfigParser __all__ = ['FormatError', 'PkgNotFound', 'LibraryInfo', 'VariableSet', 'read_config', 'parse_flags'] _VAR = re.compile(r'\$\{([a-zA-Z0-9_-]+)\}') -class FormatError(IOError): +class FormatError(OSError): """ Exception thrown when there is a problem parsing a configuration file. @@ -25,7 +20,7 @@ def __init__(self, msg): def __str__(self): return self.msg -class PkgNotFound(IOError): +class PkgNotFound(OSError): """Exception raised when a package can not be located.""" def __init__(self, msg): self.msg = msg @@ -78,7 +73,7 @@ def parse_flags(line): def _escape_backslash(val): return val.replace('\\', '\\\\') -class LibraryInfo(object): +class LibraryInfo: """ Object containing build information about a library. @@ -150,7 +145,7 @@ def __str__(self): return "\n".join(m) -class VariableSet(object): +class VariableSet: """ Container object for the variables defined in a config file. @@ -222,9 +217,7 @@ def parse_meta(config): if not config.has_section('meta'): raise FormatError("No meta section found !") - d = {} - for name, value in config.items('meta'): - d[name] = value + d = dict(config.items('meta')) for k in ['name', 'description', 'version']: if not k in d: @@ -382,7 +375,6 @@ def read_config(pkgname, dirs=None): # pkg-config simple emulator - useful for debugging, and maybe later to query # the system if __name__ == '__main__': - import sys from optparse import OptionParser import glob @@ -414,7 +406,6 @@ def read_config(pkgname, dirs=None): print("%s\t%s - %s" % (info.name, info.name, info.description)) pkg_name = args[1] - import os d = os.environ.get('NPY_PKG_CONFIG_PATH') if d: info = read_config(pkg_name, ['numpy/core/lib/npy-pkg-config', '.', d]) @@ -429,7 +420,7 @@ def read_config(pkgname, dirs=None): if options.define_variable: m = re.search(r'([\S]+)=([\S]+)', options.define_variable) if not m: - raise ValueError("--define-variable option should be of " \ + raise ValueError("--define-variable option should be of " "the form --define-variable=foo=bar") else: name = m.group(1) diff --git a/numpy/distutils/numpy_distribution.py b/numpy/distutils/numpy_distribution.py index 6ae19d16b18f..ea8182659cb1 100644 --- a/numpy/distutils/numpy_distribution.py +++ b/numpy/distutils/numpy_distribution.py @@ -1,6 +1,4 @@ # XXX: Handle setuptools ? -from __future__ import division, absolute_import, print_function - from distutils.core import Distribution # This class is used because we add new files (sconscripts, and so on) with the diff --git a/numpy/distutils/pathccompiler.py b/numpy/distutils/pathccompiler.py index fc9872db34da..48051810ee21 100644 --- a/numpy/distutils/pathccompiler.py +++ b/numpy/distutils/pathccompiler.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - from distutils.unixccompiler import UnixCCompiler class PathScaleCCompiler(UnixCCompiler): diff --git a/numpy/distutils/setup.py b/numpy/distutils/setup.py index 82a53bd08dbe..522756fc9db3 100644 --- a/numpy/distutils/setup.py +++ b/numpy/distutils/setup.py @@ -1,14 +1,14 @@ -#!/usr/bin/env python -from __future__ import division, print_function - +#!/usr/bin/env python3 def configuration(parent_package='',top_path=None): from numpy.distutils.misc_util import Configuration config = Configuration('distutils', parent_package, top_path) config.add_subpackage('command') config.add_subpackage('fcompiler') - config.add_data_dir('tests') + config.add_subpackage('tests') config.add_data_files('site.cfg') config.add_data_files('mingw/gfortran_vs2003_hack.c') + config.add_data_dir('checks') + config.add_data_files('*.pyi') config.make_config_py() return config diff --git a/numpy/distutils/system_info.py b/numpy/distutils/system_info.py index 65d7de3168bb..d5a1687da322 100644 --- a/numpy/distutils/system_info.py +++ b/numpy/distutils/system_info.py @@ -1,50 +1,8 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """ This file defines a set of system_info classes for getting information about various resources (libraries, library directories, -include directories, etc.) in the system. Currently, the following -classes are available: - - atlas_info - atlas_threads_info - atlas_blas_info - atlas_blas_threads_info - lapack_atlas_info - lapack_atlas_threads_info - atlas_3_10_info - atlas_3_10_threads_info - atlas_3_10_blas_info, - atlas_3_10_blas_threads_info, - lapack_atlas_3_10_info - lapack_atlas_3_10_threads_info - blas_info - lapack_info - openblas_info - blis_info - blas_opt_info # usage recommended - lapack_opt_info # usage recommended - fftw_info,dfftw_info,sfftw_info - fftw_threads_info,dfftw_threads_info,sfftw_threads_info - djbfft_info - x11_info - lapack_src_info - blas_src_info - numpy_info - numarray_info - numpy_info - boost_python_info - agg2_info - wx_info - gdk_pixbuf_xlib_2_info - gdk_pixbuf_2_info - gdk_x11_2_info - gtkp_x11_2_info - gtkp_2_info - xft_info - freetype2_info - umfpack_info - -Usage: +include directories, etc.) in the system. Usage: info_dict = get_info(<name>) where <name> is a string 'atlas','x11','fftw','lapack','blas', 'lapack_src', 'blas_src', etc. For a complete list of allowed names, @@ -72,19 +30,107 @@ The first one found is used to get system configuration options The format is that used by ConfigParser (i.e., Windows .INI style). The -section ALL has options that are the default for each section. The -available sections are fftw, atlas, and x11. Appropriate defaults are -used if nothing is specified. +section ALL is not intended for general use. + +Appropriate defaults are used if nothing is specified. The order of finding the locations of resources is the following: 1. environment variable 2. section in site.cfg - 3. ALL section in site.cfg + 3. DEFAULT section in site.cfg + 4. System default search paths (see ``default_*`` variables below). Only the first complete match is returned. +Currently, the following classes are available, along with their section names: + + Numeric_info:Numeric + _numpy_info:Numeric + _pkg_config_info:None + accelerate_info:accelerate + agg2_info:agg2 + amd_info:amd + atlas_3_10_blas_info:atlas + atlas_3_10_blas_threads_info:atlas + atlas_3_10_info:atlas + atlas_3_10_threads_info:atlas + atlas_blas_info:atlas + atlas_blas_threads_info:atlas + atlas_info:atlas + atlas_threads_info:atlas + blas64__opt_info:ALL # usage recommended (general ILP64 BLAS, 64_ symbol suffix) + blas_ilp64_opt_info:ALL # usage recommended (general ILP64 BLAS) + blas_ilp64_plain_opt_info:ALL # usage recommended (general ILP64 BLAS, no symbol suffix) + blas_info:blas + blas_mkl_info:mkl + blas_opt_info:ALL # usage recommended + blas_src_info:blas_src + blis_info:blis + boost_python_info:boost_python + dfftw_info:fftw + dfftw_threads_info:fftw + djbfft_info:djbfft + f2py_info:ALL + fft_opt_info:ALL + fftw2_info:fftw + fftw3_info:fftw3 + fftw_info:fftw + fftw_threads_info:fftw + flame_info:flame + freetype2_info:freetype2 + gdk_2_info:gdk_2 + gdk_info:gdk + gdk_pixbuf_2_info:gdk_pixbuf_2 + gdk_pixbuf_xlib_2_info:gdk_pixbuf_xlib_2 + gdk_x11_2_info:gdk_x11_2 + gtkp_2_info:gtkp_2 + gtkp_x11_2_info:gtkp_x11_2 + lapack64__opt_info:ALL # usage recommended (general ILP64 LAPACK, 64_ symbol suffix) + lapack_atlas_3_10_info:atlas + lapack_atlas_3_10_threads_info:atlas + lapack_atlas_info:atlas + lapack_atlas_threads_info:atlas + lapack_ilp64_opt_info:ALL # usage recommended (general ILP64 LAPACK) + lapack_ilp64_plain_opt_info:ALL # usage recommended (general ILP64 LAPACK, no symbol suffix) + lapack_info:lapack + lapack_mkl_info:mkl + lapack_opt_info:ALL # usage recommended + lapack_src_info:lapack_src + mkl_info:mkl + numarray_info:numarray + numerix_info:numerix + numpy_info:numpy + openblas64__info:openblas64_ + openblas64__lapack_info:openblas64_ + openblas_clapack_info:openblas + openblas_ilp64_info:openblas_ilp64 + openblas_ilp64_lapack_info:openblas_ilp64 + openblas_info:openblas + openblas_lapack_info:openblas + sfftw_info:fftw + sfftw_threads_info:fftw + system_info:ALL + umfpack_info:umfpack + wx_info:wx + x11_info:x11 + xft_info:xft + +Note that blas_opt_info and lapack_opt_info honor the NPY_BLAS_ORDER +and NPY_LAPACK_ORDER environment variables to determine the order in which +specific BLAS and LAPACK libraries are searched for. + +This search (or autodetection) can be bypassed by defining the environment +variables NPY_BLAS_LIBS and NPY_LAPACK_LIBS, which should then contain the +exact linker flags to use (language will be set to F77). Building against +Netlib BLAS/LAPACK or stub files, in order to be able to switch BLAS and LAPACK +implementations at runtime. If using this to build NumPy itself, it is +recommended to also define NPY_CBLAS_LIBS (assuming your BLAS library has a +CBLAS interface) to enable CBLAS usage for matrix multiplication (unoptimized +otherwise). + Example: ---------- -[ALL] +[DEFAULT] +# default section library_dirs = /usr/lib:/usr/local/lib:/opt/lib include_dirs = /usr/include:/usr/local/include:/opt/include src_dirs = /usr/local/src:/opt/src @@ -92,20 +138,20 @@ search_static_first = 0 [fftw] -fftw_libs = rfftw, fftw -fftw_opt_libs = rfftw_threaded, fftw_threaded -# if the above aren't found, look for {s,d}fftw_libs and {s,d}fftw_opt_libs +libraries = rfftw, fftw [atlas] library_dirs = /usr/lib/3dnow:/usr/lib/3dnow/atlas # for overriding the names of the atlas libraries -atlas_libs = lapack, f77blas, cblas, atlas +libraries = lapack, f77blas, cblas, atlas [x11] library_dirs = /usr/X11R6/lib include_dirs = /usr/X11R6/include ---------- +Note that the ``libraries`` key is the default setting for libraries. + Authors: Pearu Peterson <pearu@cens.ioc.ee>, February 2002 David M. Cooke <cookedm@physics.mcmaster.ca>, April 2002 @@ -119,22 +165,18 @@ NO WARRANTY IS EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. """ -from __future__ import division, absolute_import, print_function - import sys import os import re import copy import warnings -import atexit +import subprocess +import textwrap + from glob import glob from functools import reduce -if sys.version_info[0] < 3: - from ConfigParser import NoOptionError - from ConfigParser import RawConfigParser as ConfigParser -else: - from configparser import NoOptionError - from configparser import RawConfigParser as ConfigParser +from configparser import NoOptionError +from configparser import RawConfigParser as ConfigParser # It seems that some people are importing ConfigParser from here so is # good to keep its class name. Use of RawConfigParser is needed in # order to be able to load path names with percent in them, like @@ -142,21 +184,23 @@ from distutils.errors import DistutilsError from distutils.dist import Distribution -import distutils.sysconfig -from distutils import log +import sysconfig +from numpy.distutils import log from distutils.util import get_platform from numpy.distutils.exec_command import ( - find_executable, exec_command, get_pythonexe) + find_executable, filepath_from_subprocess_output, + ) from numpy.distutils.misc_util import (is_sequence, is_string, get_shared_lib_extension) from numpy.distutils.command.config import config as cmd_config -from numpy.distutils.compat import get_exception -from numpy.distutils import customized_ccompiler +from numpy.distutils import customized_ccompiler as _customized_ccompiler +from numpy.distutils import _shell_utils import distutils.ccompiler import tempfile import shutil +__all__ = ['system_info'] # Determine number of bits import platform @@ -164,6 +208,26 @@ platform_bits = _bits[platform.architecture()[0]] +global_compiler = None + +def customized_ccompiler(): + global global_compiler + if not global_compiler: + global_compiler = _customized_ccompiler() + return global_compiler + + +def _c_string_literal(s): + """ + Convert a python string into a literal suitable for inclusion into C code + """ + # only these three characters are forbidden in C strings + s = s.replace('\\', r'\\') + s = s.replace('"', r'\"') + s = s.replace('\n', r'\n') + return '"{}"'.format(s) + + def libpaths(paths, bits): """Return a list of library paths valid on 32 or 64 bit systems. @@ -205,7 +269,7 @@ def libpaths(paths, bits): if sys.platform == 'win32': default_lib_dirs = ['C:\\', - os.path.join(distutils.sysconfig.EXEC_PREFIX, + os.path.join(sysconfig.get_config_var('exec_prefix'), 'libs')] default_runtime_dirs = [] default_include_dirs = [] @@ -234,39 +298,36 @@ def add_system_root(library_root): default_include_dirs.extend( os.path.join(library_root, d) for d in _include_dirs) - if sys.version_info >= (3, 3): - # VCpkg is the de-facto package manager on windows for C/C++ - # libraries. If it is on the PATH, then we append its paths here. - # We also don't re-implement shutil.which for Python 2.7 because - # vcpkg doesn't support MSVC 2008. - vcpkg = shutil.which('vcpkg') - if vcpkg: - vcpkg_dir = os.path.dirname(vcpkg) - if platform.architecture() == '32bit': - specifier = 'x86' - else: - specifier = 'x64' - - vcpkg_installed = os.path.join(vcpkg_dir, 'installed') - for vcpkg_root in [ - os.path.join(vcpkg_installed, specifier + '-windows'), - os.path.join(vcpkg_installed, specifier + '-windows-static'), - ]: - add_system_root(vcpkg_root) - - # Conda is another popular package manager that provides libraries - conda = shutil.which('conda') - if conda: - conda_dir = os.path.dirname(conda) - add_system_root(os.path.join(conda_dir, '..', 'Library')) - add_system_root(os.path.join(conda_dir, 'Library')) + # VCpkg is the de-facto package manager on windows for C/C++ + # libraries. If it is on the PATH, then we append its paths here. + vcpkg = shutil.which('vcpkg') + if vcpkg: + vcpkg_dir = os.path.dirname(vcpkg) + if platform.architecture()[0] == '32bit': + specifier = 'x86' + else: + specifier = 'x64' + + vcpkg_installed = os.path.join(vcpkg_dir, 'installed') + for vcpkg_root in [ + os.path.join(vcpkg_installed, specifier + '-windows'), + os.path.join(vcpkg_installed, specifier + '-windows-static'), + ]: + add_system_root(vcpkg_root) + + # Conda is another popular package manager that provides libraries + conda = shutil.which('conda') + if conda: + conda_dir = os.path.dirname(conda) + add_system_root(os.path.join(conda_dir, '..', 'Library')) + add_system_root(os.path.join(conda_dir, 'Library')) else: default_lib_dirs = libpaths(['/usr/local/lib', '/opt/lib', '/usr/lib', '/opt/local/lib', '/sw/lib'], platform_bits) default_runtime_dirs = [] default_include_dirs = ['/usr/local/include', - '/opt/include', '/usr/include', + '/opt/include', # path of umfpack under macports '/opt/local/include/ufsparse', '/opt/local/include', '/sw/include', @@ -275,8 +336,7 @@ def add_system_root(library_root): default_x11_lib_dirs = libpaths(['/usr/X11R6/lib', '/usr/X11/lib', '/usr/lib'], platform_bits) - default_x11_include_dirs = ['/usr/X11R6/include', '/usr/X11/include', - '/usr/include'] + default_x11_include_dirs = ['/usr/X11R6/include', '/usr/X11/include'] if os.path.exists('/usr/lib/X11'): globbed_x11_dir = glob('/usr/lib/*/libX11.so') @@ -286,27 +346,21 @@ def add_system_root(library_root): default_x11_include_dirs.extend(['/usr/lib/X11/include', '/usr/include/X11']) - import subprocess as sp - tmp = None - try: - # Explicitly open/close file to avoid ResourceWarning when - # tests are run in debug mode Python 3. - tmp = open(os.devnull, 'w') - p = sp.Popen(["gcc", "-print-multiarch"], stdout=sp.PIPE, - stderr=tmp) - except (OSError, DistutilsError): - # OSError if gcc is not installed, or SandboxViolation (DistutilsError - # subclass) if an old setuptools bug is triggered (see gh-3160). - pass - else: - triplet = str(p.communicate()[0].decode().strip()) - if p.returncode == 0: - # gcc supports the "-print-multiarch" option - default_x11_lib_dirs += [os.path.join("/usr/lib/", triplet)] - default_lib_dirs += [os.path.join("/usr/lib/", triplet)] - finally: - if tmp is not None: - tmp.close() + with open(os.devnull, 'w') as tmp: + try: + p = subprocess.Popen(["gcc", "-print-multiarch"], stdout=subprocess.PIPE, + stderr=tmp) + except (OSError, DistutilsError): + # OSError if gcc is not installed, or SandboxViolation (DistutilsError + # subclass) if an old setuptools bug is triggered (see gh-3160). + pass + else: + triplet = str(p.communicate()[0].decode().strip()) + if p.returncode == 0: + # gcc supports the "-print-multiarch" option + default_x11_lib_dirs += [os.path.join("/usr/lib/", triplet)] + default_lib_dirs += [os.path.join("/usr/lib/", triplet)] + if os.path.join(sys.prefix, 'lib') not in default_lib_dirs: default_lib_dirs.insert(0, os.path.join(sys.prefix, 'lib')) @@ -333,11 +387,10 @@ def get_standard_file(fname): f = __file__ except NameError: f = sys.argv[0] - else: - sysfile = os.path.join(os.path.split(os.path.abspath(f))[0], - fname) - if os.path.isfile(sysfile): - filenames.append(sysfile) + sysfile = os.path.join(os.path.split(os.path.abspath(f))[0], + fname) + if os.path.isfile(sysfile): + filenames.append(sysfile) # Home directory # And look for the user config file @@ -357,6 +410,90 @@ def get_standard_file(fname): return filenames +def _parse_env_order(base_order, env): + """ Parse an environment variable `env` by splitting with "," and only returning elements from `base_order` + + This method will sequence the environment variable and check for their + individual elements in `base_order`. + + The items in the environment variable may be negated via '^item' or '!itema,itemb'. + It must start with ^/! to negate all options. + + Raises + ------ + ValueError: for mixed negated and non-negated orders or multiple negated orders + + Parameters + ---------- + base_order : list of str + the base list of orders + env : str + the environment variable to be parsed, if none is found, `base_order` is returned + + Returns + ------- + allow_order : list of str + allowed orders in lower-case + unknown_order : list of str + for values not overlapping with `base_order` + """ + order_str = os.environ.get(env, None) + + # ensure all base-orders are lower-case (for easier comparison) + base_order = [order.lower() for order in base_order] + if order_str is None: + return base_order, [] + + neg = order_str.startswith('^') or order_str.startswith('!') + # Check format + order_str_l = list(order_str) + sum_neg = order_str_l.count('^') + order_str_l.count('!') + if neg: + if sum_neg > 1: + raise ValueError(f"Environment variable '{env}' may only contain a single (prefixed) negation: {order_str}") + # remove prefix + order_str = order_str[1:] + elif sum_neg > 0: + raise ValueError(f"Environment variable '{env}' may not mix negated an non-negated items: {order_str}") + + # Split and lower case + orders = order_str.lower().split(',') + + # to inform callee about non-overlapping elements + unknown_order = [] + + # if negated, we have to remove from the order + if neg: + allow_order = base_order.copy() + + for order in orders: + if not order: + continue + + if order not in base_order: + unknown_order.append(order) + continue + + if order in allow_order: + allow_order.remove(order) + + else: + allow_order = [] + + for order in orders: + if not order: + continue + + if order not in base_order: + unknown_order.append(order) + continue + + if order not in allow_order: + allow_order.append(order) + + return allow_order, unknown_order + + def get_info(name, notfound_action=0): """ notfound_action: @@ -364,7 +501,11 @@ def get_info(name, notfound_action=0): 1 - display warning message 2 - raise error """ - cl = {'atlas': atlas_info, # use lapack_opt or blas_opt instead + cl = {'armpl': armpl_info, + 'blas_armpl': blas_armpl_info, + 'lapack_armpl': lapack_armpl_info, + 'fftw3_armpl': fftw3_armpl_info, + 'atlas': atlas_info, # use lapack_opt or blas_opt instead 'atlas_threads': atlas_threads_info, # ditto 'atlas_blas': atlas_blas_info, 'atlas_blas_threads': atlas_blas_threads_info, @@ -376,6 +517,7 @@ def get_info(name, notfound_action=0): 'atlas_3_10_blas_threads': atlas_3_10_blas_threads_info, 'lapack_atlas_3_10': lapack_atlas_3_10_info, # use lapack_opt instead 'lapack_atlas_3_10_threads': lapack_atlas_3_10_threads_info, # ditto + 'flame': flame_info, # use lapack_opt instead 'mkl': mkl_info, # openblas which may or may not have embedded lapack 'openblas': openblas_info, # use blas_opt instead @@ -386,6 +528,10 @@ def get_info(name, notfound_action=0): 'lapack_mkl': lapack_mkl_info, # use lapack_opt instead 'blas_mkl': blas_mkl_info, # use blas_opt instead 'accelerate': accelerate_info, # use blas_opt instead + 'openblas64_': openblas64__info, + 'openblas64__lapack': openblas64__lapack_info, + 'openblas_ilp64': openblas_ilp64_info, + 'openblas_ilp64_lapack': openblas_ilp64_lapack_info, 'x11': x11_info, 'fft_opt': fft_opt_info, 'fftw': fftw_info, @@ -408,7 +554,13 @@ def get_info(name, notfound_action=0): 'numarray': numarray_info, 'numerix': numerix_info, 'lapack_opt': lapack_opt_info, + 'lapack_ilp64_opt': lapack_ilp64_opt_info, + 'lapack_ilp64_plain_opt': lapack_ilp64_plain_opt_info, + 'lapack64__opt': lapack64__opt_info, 'blas_opt': blas_opt_info, + 'blas_ilp64_opt': blas_ilp64_opt_info, + 'blas_ilp64_plain_opt': blas_ilp64_plain_opt_info, + 'blas64__opt': blas64__opt_info, 'boost_python': boost_python_info, 'agg2': agg2_info, 'wx': wx_info, @@ -437,14 +589,27 @@ class NotFoundError(DistutilsError): """Some third-party program or library is not found.""" +class AliasedOptionError(DistutilsError): + """ + Aliases entries in config files should not be existing. + In section '{section}' we found multiple appearances of options {options}.""" + + class AtlasNotFoundError(NotFoundError): """ - Atlas (http://math-atlas.sourceforge.net/) libraries not found. + Atlas (http://github.com/math-atlas/math-atlas) libraries not found. Directories to search for the libraries can be specified in the numpy/distutils/site.cfg file (section [atlas]) or by setting the ATLAS environment variable.""" +class FlameNotFoundError(NotFoundError): + """ + FLAME (http://www.cs.utexas.edu/~flame/web/) libraries not found. + Directories to search for the libraries can be specified in the + numpy/distutils/site.cfg file (section [flame]).""" + + class LapackNotFoundError(NotFoundError): """ Lapack (http://www.netlib.org/lapack/) libraries not found. @@ -461,6 +626,20 @@ class LapackSrcNotFoundError(LapackNotFoundError): the LAPACK_SRC environment variable.""" +class LapackILP64NotFoundError(NotFoundError): + """ + 64-bit Lapack libraries not found. + Known libraries in numpy/distutils/site.cfg file are: + openblas64_, openblas_ilp64 + """ + +class BlasOptNotFoundError(NotFoundError): + """ + Optimized (vendor) Blas libraries are not found. + Falls back to netlib Blas library which has worse performance. + A better performance should be easily gained by switching + Blas library.""" + class BlasNotFoundError(NotFoundError): """ Blas (http://www.netlib.org/blas/) libraries not found. @@ -468,6 +647,12 @@ class BlasNotFoundError(NotFoundError): numpy/distutils/site.cfg file (section [blas]) or by setting the BLAS environment variable.""" +class BlasILP64NotFoundError(NotFoundError): + """ + 64-bit Blas libraries not found. + Known libraries in numpy/distutils/site.cfg file are: + openblas64_, openblas_ilp64 + """ class BlasSrcNotFoundError(BlasNotFoundError): """ @@ -487,7 +672,7 @@ class FFTWNotFoundError(NotFoundError): class DJBFFTNotFoundError(NotFoundError): """ - DJBFFT (http://cr.yp.to/djbfft.html) libraries not found. + DJBFFT (https://cr.yp.to/djbfft.html) libraries not found. Directories to search for the libraries can be specified in the numpy/distutils/site.cfg file (section [djbfft]) or by setting the DJBFFT environment variable.""" @@ -495,7 +680,7 @@ class DJBFFTNotFoundError(NotFoundError): class NumericNotFoundError(NotFoundError): """ - Numeric (http://www.numpy.org/) module not found. + Numeric (https://www.numpy.org/) module not found. Get it from above location, install it, and retry setup.py.""" @@ -505,21 +690,24 @@ class X11NotFoundError(NotFoundError): class UmfpackNotFoundError(NotFoundError): """ - UMFPACK sparse solver (http://www.cise.ufl.edu/research/sparse/umfpack/) + UMFPACK sparse solver (https://www.cise.ufl.edu/research/sparse/umfpack/) not found. Directories to search for the libraries can be specified in the numpy/distutils/site.cfg file (section [umfpack]) or by setting the UMFPACK environment variable.""" -class system_info(object): +class system_info: """ get_info() is the only public method. Don't use others. """ - section = 'ALL' dir_env_var = None - search_static_first = 0 # XXX: disabled by default, may disappear in - # future unless it is proved to be useful. - verbosity = 1 + # XXX: search_static_first is disabled by default, may disappear in + # future unless it is proved to be useful. + search_static_first = 0 + # The base-class section name is a random word "ALL" and is not really + # intended for general use. It cannot be None nor can it be DEFAULT as + # these break the ConfigParser. See gh-15338 + section = 'ALL' saved_results = {} notfounderror = NotFoundError @@ -527,7 +715,6 @@ class system_info(object): def __init__(self, default_lib_dirs=default_lib_dirs, default_include_dirs=default_include_dirs, - verbosity=1, ): self.__class__.info = {} self.local_prefixes = [] @@ -595,6 +782,38 @@ def set_info(self, **info): dict_append(info, **extra_info) self.saved_results[self.__class__.__name__] = info + def get_option_single(self, *options): + """ Ensure that only one of `options` are found in the section + + Parameters + ---------- + *options : list of str + a list of options to be found in the section (``self.section``) + + Returns + ------- + str : + the option that is uniquely found in the section + + Raises + ------ + AliasedOptionError : + in case more than one of the options are found + """ + found = [self.cp.has_option(self.section, opt) for opt in options] + if sum(found) == 1: + return options[found.index(True)] + elif sum(found) == 0: + # nothing is found anyways + return options[0] + + # Else we have more than 1 key found + if AliasedOptionError.__doc__ is None: + raise AliasedOptionError() + raise AliasedOptionError(AliasedOptionError.__doc__.format( + section=self.section, options='[{}]'.format(', '.join(options)))) + + def has_info(self): return self.__class__.__name__ in self.saved_results @@ -608,13 +827,14 @@ def calc_extra_info(self): for key in ['extra_compile_args', 'extra_link_args']: # Get values opt = self.cp.get(self.section, key) + opt = _shell_utils.NativeParser.split(opt) if opt: - tmp = {key : [opt]} + tmp = {key: opt} dict_append(info, **tmp) return info def get_info(self, notfound_action=0): - """ Return a dictonary with items that are compatible + """ Return a dictionary with items that are compatible with numpy.distutils.setup keyword arguments. """ flag = 0 @@ -639,7 +859,7 @@ def get_info(self, notfound_action=0): log.info(' FOUND:') res = self.saved_results.get(self.__class__.__name__) - if self.verbosity > 0 and flag: + if log.get_threshold() <= log.INFO and flag: for k, v in res.items(): v = str(v) if k in ['sources', 'libraries'] and len(v) > 270: @@ -849,7 +1069,7 @@ def combine_paths(self, *args): """Return a list of existing paths composed by all combinations of items from the arguments. """ - return combine_paths(*args, **{'verbosity': self.verbosity}) + return combine_paths(*args) class fft_opt_info(system_info): @@ -884,7 +1104,9 @@ def calc_ver_info(self, ver_param): """Returns True on successful version detection, else False""" lib_dirs = self.get_lib_dirs() incl_dirs = self.get_include_dirs() - libs = self.get_libs(self.section + '_libs', ver_param['libs']) + + opt = self.get_option_single(self.section + '_libs', 'libraries') + libs = self.get_libs(opt, ver_param['libs']) info = self.check_libs(lib_dirs, libs) if info is not None: flag = 0 @@ -893,7 +1115,6 @@ def calc_ver_info(self, ver_param): == len(ver_param['includes']): dict_append(info, include_dirs=[d]) flag = 1 - incl_dirs = [d] break if flag: dict_append(info, define_macros=ver_param['macros']) @@ -935,6 +1156,16 @@ class fftw3_info(fftw_info): 'macros':[('SCIPY_FFTW3_H', None)]}, ] + +class fftw3_armpl_info(fftw_info): + section = 'fftw3' + dir_env_var = 'ARMPL_DIR' + notfounderror = FFTWNotFoundError + ver_info = [{'name': 'fftw3', + 'libs': ['armpl_lp64_mp'], + 'includes': ['fftw3.h'], + 'macros': [('SCIPY_FFTW3_H', None)]}] + class dfftw_info(fftw_info): section = 'fftw' @@ -1045,9 +1276,9 @@ def get_mkl_rootdir(self): for d in paths: dirs = glob(os.path.join(d, 'mkl', '*')) dirs += glob(os.path.join(d, 'mkl*')) - for d in dirs: - if os.path.isdir(os.path.join(d, 'lib')): - return d + for sub_dir in dirs: + if os.path.isdir(os.path.join(sub_dir, 'lib')): + return sub_dir return None def __init__(self): @@ -1070,7 +1301,8 @@ def __init__(self): def calc_info(self): lib_dirs = self.get_lib_dirs() incl_dirs = self.get_include_dirs() - mkl_libs = self.get_libs('mkl_libs', self._lib_mkl) + opt = self.get_option_single('mkl_libs', 'libraries') + mkl_libs = self.get_libs(opt, self._lib_mkl) info = self.check_libs2(lib_dirs, mkl_libs) if info is None: return @@ -1093,6 +1325,31 @@ class blas_mkl_info(mkl_info): pass +class armpl_info(system_info): + section = 'armpl' + dir_env_var = 'ARMPL_DIR' + _lib_armpl = ['armpl_lp64_mp'] + + def calc_info(self): + lib_dirs = self.get_lib_dirs() + incl_dirs = self.get_include_dirs() + armpl_libs = self.get_libs('armpl_libs', self._lib_armpl) + info = self.check_libs2(lib_dirs, armpl_libs) + if info is None: + return + dict_append(info, + define_macros=[('SCIPY_MKL_H', None), + ('HAVE_CBLAS', None)], + include_dirs=incl_dirs) + self.set_info(**info) + +class lapack_armpl_info(armpl_info): + pass + +class blas_armpl_info(armpl_info): + pass + + class atlas_info(system_info): section = 'atlas' dir_env_var = 'ATLAS' @@ -1117,15 +1374,14 @@ def get_paths(self, section, key): def calc_info(self): lib_dirs = self.get_lib_dirs() info = {} - atlas_libs = self.get_libs('atlas_libs', - self._lib_names + self._lib_atlas) + opt = self.get_option_single('atlas_libs', 'libraries') + atlas_libs = self.get_libs(opt, self._lib_names + self._lib_atlas) lapack_libs = self.get_libs('lapack_libs', self._lib_lapack) atlas = None lapack = None atlas_1 = None for d in lib_dirs: atlas = self.check_libs2(d, atlas_libs, []) - lapack_atlas = self.check_libs2(d, ['lapack_atlas'], []) if atlas is not None: lib_dirs2 = [d] + self.combine_paths(d, ['atlas*', 'ATLAS*']) lapack = self.check_libs2(lib_dirs2, lapack_libs, []) @@ -1157,11 +1413,11 @@ def calc_info(self): else: dict_append(info, **atlas) dict_append(info, define_macros=[('ATLAS_WITHOUT_LAPACK', None)]) - message = """ -********************************************************************* - Could not find lapack library within the ATLAS installation. -********************************************************************* -""" + message = textwrap.dedent(""" + ********************************************************************* + Could not find lapack library within the ATLAS installation. + ********************************************************************* + """) warnings.warn(message, stacklevel=2) self.set_info(**info) return @@ -1184,15 +1440,15 @@ def calc_info(self): if lapack_lib is not None: sz = os.stat(lapack_lib)[6] if sz <= 4000 * 1024: - message = """ -********************************************************************* - Lapack library (from ATLAS) is probably incomplete: - size of %s is %sk (expected >4000k) - - Follow the instructions in the KNOWN PROBLEMS section of the file - numpy/INSTALL.txt. -********************************************************************* -""" % (lapack_lib, sz / 1024) + message = textwrap.dedent(""" + ********************************************************************* + Lapack library (from ATLAS) is probably incomplete: + size of %s is %sk (expected >4000k) + + Follow the instructions in the KNOWN PROBLEMS section of the file + numpy/INSTALL.txt. + ********************************************************************* + """) % (lapack_lib, sz / 1024) warnings.warn(message, stacklevel=2) else: info['language'] = 'f77' @@ -1209,8 +1465,8 @@ class atlas_blas_info(atlas_info): def calc_info(self): lib_dirs = self.get_lib_dirs() info = {} - atlas_libs = self.get_libs('atlas_libs', - self._lib_names + self._lib_atlas) + opt = self.get_option_single('atlas_libs', 'libraries') + atlas_libs = self.get_libs(opt, self._lib_names + self._lib_atlas) atlas = self.check_libs2(lib_dirs, atlas_libs, []) if atlas is None: return @@ -1262,8 +1518,8 @@ class atlas_3_10_blas_info(atlas_3_10_info): def calc_info(self): lib_dirs = self.get_lib_dirs() info = {} - atlas_libs = self.get_libs('atlas_libs', - self._lib_names) + opt = self.get_option_single('atlas_lib', 'libraries') + atlas_libs = self.get_libs(opt, self._lib_names) atlas = self.check_libs2(lib_dirs, atlas_libs, []) if atlas is None: return @@ -1314,7 +1570,8 @@ class lapack_info(system_info): def calc_info(self): lib_dirs = self.get_lib_dirs() - lapack_libs = self.get_libs('lapack_libs', self._lib_names) + opt = self.get_option_single('lapack_libs', 'libraries') + lapack_libs = self.get_libs(opt, self._lib_names) info = self.check_libs(lib_dirs, lapack_libs, []) if info is None: return @@ -1323,6 +1580,9 @@ def calc_info(self): class lapack_src_info(system_info): + # LAPACK_SRC is deprecated, please do not use this! + # Build or install a BLAS library via your package manager or from + # source separately. section = 'lapack_src' dir_env_var = 'LAPACK_SRC' notfounderror = LapackSrcNotFoundError @@ -1462,23 +1722,23 @@ def get_atlas_version(**config): try: s, o = c.get_output(atlas_version_c_text, libraries=libraries, library_dirs=library_dirs, - use_tee=(system_info.verbosity > 0)) + ) if s and re.search(r'undefined reference to `_gfortran', o, re.M): s, o = c.get_output(atlas_version_c_text, libraries=libraries + ['gfortran'], library_dirs=library_dirs, - use_tee=(system_info.verbosity > 0)) + ) if not s: - warnings.warn(""" -***************************************************** -Linkage with ATLAS requires gfortran. Use + warnings.warn(textwrap.dedent(""" + ***************************************************** + Linkage with ATLAS requires gfortran. Use - python setup.py config_fc --fcompiler=gnu95 ... + python setup.py config_fc --fcompiler=gnu95 ... -when building extension libraries that use ATLAS. -Make sure that -lgfortran is used for C++ extensions. -***************************************************** -""", stacklevel=2) + when building extension libraries that use ATLAS. + Make sure that -lgfortran is used for C++ extensions. + ***************************************************** + """), stacklevel=2) dict_append(info, language='f90', define_macros=[('ATLAS_REQUIRES_GFORTRAN', None)]) except Exception: # failed to get version from file -- maybe on Windows @@ -1496,7 +1756,7 @@ def get_atlas_version(**config): atlas_version = os.environ.get('ATLAS_VERSION', None) if atlas_version: dict_append(info, define_macros=[( - 'ATLAS_INFO', '"\\"%s\\""' % atlas_version) + 'ATLAS_INFO', _c_string_literal(atlas_version)) ]) else: dict_append(info, define_macros=[('NO_ATLAS_INFO', -1)]) @@ -1513,152 +1773,346 @@ def get_atlas_version(**config): log.info('Status: %d', s) log.info('Output: %s', o) - if atlas_version == '3.2.1_pre3.3.6': + elif atlas_version == '3.2.1_pre3.3.6': dict_append(info, define_macros=[('NO_ATLAS_INFO', -2)]) else: dict_append(info, define_macros=[( - 'ATLAS_INFO', '"\\"%s\\""' % atlas_version) + 'ATLAS_INFO', _c_string_literal(atlas_version)) ]) result = _cached_atlas_version[key] = atlas_version, info return result class lapack_opt_info(system_info): - notfounderror = LapackNotFoundError - def calc_info(self): - - lapack_mkl_info = get_info('lapack_mkl') - if lapack_mkl_info: - self.set_info(**lapack_mkl_info) - return + # List of all known LAPACK libraries, in the default order + lapack_order = ['armpl', 'mkl', 'openblas', 'flame', + 'accelerate', 'atlas', 'lapack'] + order_env_var_name = 'NPY_LAPACK_ORDER' + + def _calc_info_armpl(self): + info = get_info('lapack_armpl') + if info: + self.set_info(**info) + return True + return False - openblas_info = get_info('openblas_lapack') - if openblas_info: - self.set_info(**openblas_info) - return + def _calc_info_mkl(self): + info = get_info('lapack_mkl') + if info: + self.set_info(**info) + return True + return False - openblas_info = get_info('openblas_clapack') - if openblas_info: - self.set_info(**openblas_info) - return + def _calc_info_openblas(self): + info = get_info('openblas_lapack') + if info: + self.set_info(**info) + return True + info = get_info('openblas_clapack') + if info: + self.set_info(**info) + return True + return False - atlas_info = get_info('atlas_3_10_threads') - if not atlas_info: - atlas_info = get_info('atlas_3_10') - if not atlas_info: - atlas_info = get_info('atlas_threads') - if not atlas_info: - atlas_info = get_info('atlas') - - accelerate_info = get_info('accelerate') - if accelerate_info and not atlas_info: - self.set_info(**accelerate_info) - return + def _calc_info_flame(self): + info = get_info('flame') + if info: + self.set_info(**info) + return True + return False - need_lapack = 0 - need_blas = 0 - info = {} - if atlas_info: - l = atlas_info.get('define_macros', []) + def _calc_info_atlas(self): + info = get_info('atlas_3_10_threads') + if not info: + info = get_info('atlas_3_10') + if not info: + info = get_info('atlas_threads') + if not info: + info = get_info('atlas') + if info: + # Figure out if ATLAS has lapack... + # If not we need the lapack library, but not BLAS! + l = info.get('define_macros', []) if ('ATLAS_WITH_LAPACK_ATLAS', None) in l \ - or ('ATLAS_WITHOUT_LAPACK', None) in l: - need_lapack = 1 - info = atlas_info + or ('ATLAS_WITHOUT_LAPACK', None) in l: + # Get LAPACK (with possible warnings) + # If not found we don't accept anything + # since we can't use ATLAS with LAPACK! + lapack_info = self._get_info_lapack() + if not lapack_info: + return False + dict_append(info, **lapack_info) + self.set_info(**info) + return True + return False - else: - warnings.warn(AtlasNotFoundError.__doc__, stacklevel=2) - need_blas = 1 - need_lapack = 1 - dict_append(info, define_macros=[('NO_ATLAS_INFO', 1)]) + def _calc_info_accelerate(self): + info = get_info('accelerate') + if info: + self.set_info(**info) + return True + return False - if need_lapack: - lapack_info = get_info('lapack') - #lapack_info = {} ## uncomment for testing - if lapack_info: - dict_append(info, **lapack_info) - else: - warnings.warn(LapackNotFoundError.__doc__, stacklevel=2) - lapack_src_info = get_info('lapack_src') - if not lapack_src_info: - warnings.warn(LapackSrcNotFoundError.__doc__, stacklevel=2) - return - dict_append(info, libraries=[('flapack_src', lapack_src_info)]) - - if need_blas: - blas_info = get_info('blas') - if blas_info: - dict_append(info, **blas_info) - else: - warnings.warn(BlasNotFoundError.__doc__, stacklevel=2) - blas_src_info = get_info('blas_src') - if not blas_src_info: - warnings.warn(BlasSrcNotFoundError.__doc__, stacklevel=2) - return - dict_append(info, libraries=[('fblas_src', blas_src_info)]) + def _get_info_blas(self): + # Default to get the optimized BLAS implementation + info = get_info('blas_opt') + if not info: + warnings.warn(BlasNotFoundError.__doc__ or '', stacklevel=3) + info_src = get_info('blas_src') + if not info_src: + warnings.warn(BlasSrcNotFoundError.__doc__ or '', stacklevel=3) + return {} + dict_append(info, libraries=[('fblas_src', info_src)]) + return info + def _get_info_lapack(self): + info = get_info('lapack') + if not info: + warnings.warn(LapackNotFoundError.__doc__ or '', stacklevel=3) + info_src = get_info('lapack_src') + if not info_src: + warnings.warn(LapackSrcNotFoundError.__doc__ or '', stacklevel=3) + return {} + dict_append(info, libraries=[('flapack_src', info_src)]) + return info + + def _calc_info_lapack(self): + info = self._get_info_lapack() + if info: + info_blas = self._get_info_blas() + dict_append(info, **info_blas) + dict_append(info, define_macros=[('NO_ATLAS_INFO', 1)]) + self.set_info(**info) + return True + return False + + def _calc_info_from_envvar(self): + info = {} + info['language'] = 'f77' + info['libraries'] = [] + info['include_dirs'] = [] + info['define_macros'] = [] + info['extra_link_args'] = os.environ['NPY_LAPACK_LIBS'].split() self.set_info(**info) - return + return True + def _calc_info(self, name): + return getattr(self, '_calc_info_{}'.format(name))() -class blas_opt_info(system_info): + def calc_info(self): + lapack_order, unknown_order = _parse_env_order(self.lapack_order, self.order_env_var_name) + if len(unknown_order) > 0: + raise ValueError("lapack_opt_info user defined " + "LAPACK order has unacceptable " + "values: {}".format(unknown_order)) + + if 'NPY_LAPACK_LIBS' in os.environ: + # Bypass autodetection, set language to F77 and use env var linker + # flags directly + self._calc_info_from_envvar() + return + + for lapack in lapack_order: + if self._calc_info(lapack): + return + + if 'lapack' not in lapack_order: + # Since the user may request *not* to use any library, we still need + # to raise warnings to signal missing packages! + warnings.warn(LapackNotFoundError.__doc__ or '', stacklevel=2) + warnings.warn(LapackSrcNotFoundError.__doc__ or '', stacklevel=2) + + +class _ilp64_opt_info_mixin: + symbol_suffix = None + symbol_prefix = None + + def _check_info(self, info): + macros = dict(info.get('define_macros', [])) + prefix = macros.get('BLAS_SYMBOL_PREFIX', '') + suffix = macros.get('BLAS_SYMBOL_SUFFIX', '') + + if self.symbol_prefix not in (None, prefix): + return False + + if self.symbol_suffix not in (None, suffix): + return False + + return bool(info) + + +class lapack_ilp64_opt_info(lapack_opt_info, _ilp64_opt_info_mixin): + notfounderror = LapackILP64NotFoundError + lapack_order = ['openblas64_', 'openblas_ilp64'] + order_env_var_name = 'NPY_LAPACK_ILP64_ORDER' + + def _calc_info(self, name): + info = get_info(name + '_lapack') + if self._check_info(info): + self.set_info(**info) + return True + return False + + +class lapack_ilp64_plain_opt_info(lapack_ilp64_opt_info): + # Same as lapack_ilp64_opt_info, but fix symbol names + symbol_prefix = '' + symbol_suffix = '' + + +class lapack64__opt_info(lapack_ilp64_opt_info): + symbol_prefix = '' + symbol_suffix = '64_' + +class blas_opt_info(system_info): notfounderror = BlasNotFoundError + # List of all known BLAS libraries, in the default order + + blas_order = ['armpl', 'mkl', 'blis', 'openblas', + 'accelerate', 'atlas', 'blas'] + order_env_var_name = 'NPY_BLAS_ORDER' + + def _calc_info_armpl(self): + info = get_info('blas_armpl') + if info: + self.set_info(**info) + return True + return False - def calc_info(self): + def _calc_info_mkl(self): + info = get_info('blas_mkl') + if info: + self.set_info(**info) + return True + return False - blas_mkl_info = get_info('blas_mkl') - if blas_mkl_info: - self.set_info(**blas_mkl_info) - return + def _calc_info_blis(self): + info = get_info('blis') + if info: + self.set_info(**info) + return True + return False - blis_info = get_info('blis') - if blis_info: - self.set_info(**blis_info) - return + def _calc_info_openblas(self): + info = get_info('openblas') + if info: + self.set_info(**info) + return True + return False - openblas_info = get_info('openblas') - if openblas_info: - self.set_info(**openblas_info) - return + def _calc_info_atlas(self): + info = get_info('atlas_3_10_blas_threads') + if not info: + info = get_info('atlas_3_10_blas') + if not info: + info = get_info('atlas_blas_threads') + if not info: + info = get_info('atlas_blas') + if info: + self.set_info(**info) + return True + return False - atlas_info = get_info('atlas_3_10_blas_threads') - if not atlas_info: - atlas_info = get_info('atlas_3_10_blas') - if not atlas_info: - atlas_info = get_info('atlas_blas_threads') - if not atlas_info: - atlas_info = get_info('atlas_blas') - - accelerate_info = get_info('accelerate') - if accelerate_info and not atlas_info: - self.set_info(**accelerate_info) - return + def _calc_info_accelerate(self): + info = get_info('accelerate') + if info: + self.set_info(**info) + return True + return False - need_blas = 0 + def _calc_info_blas(self): + # Warn about a non-optimized BLAS library + warnings.warn(BlasOptNotFoundError.__doc__ or '', stacklevel=3) info = {} - if atlas_info: - info = atlas_info + dict_append(info, define_macros=[('NO_ATLAS_INFO', 1)]) + + blas = get_info('blas') + if blas: + dict_append(info, **blas) else: - warnings.warn(AtlasNotFoundError.__doc__, stacklevel=2) - need_blas = 1 - dict_append(info, define_macros=[('NO_ATLAS_INFO', 1)]) + # Not even BLAS was found! + warnings.warn(BlasNotFoundError.__doc__ or '', stacklevel=3) - if need_blas: - blas_info = get_info('blas') - if blas_info: - dict_append(info, **blas_info) - else: - warnings.warn(BlasNotFoundError.__doc__, stacklevel=2) - blas_src_info = get_info('blas_src') - if not blas_src_info: - warnings.warn(BlasSrcNotFoundError.__doc__, stacklevel=2) - return - dict_append(info, libraries=[('fblas_src', blas_src_info)]) + blas_src = get_info('blas_src') + if not blas_src: + warnings.warn(BlasSrcNotFoundError.__doc__ or '', stacklevel=3) + return False + dict_append(info, libraries=[('fblas_src', blas_src)]) self.set_info(**info) - return + return True + + def _calc_info_from_envvar(self): + info = {} + info['language'] = 'f77' + info['libraries'] = [] + info['include_dirs'] = [] + info['define_macros'] = [] + info['extra_link_args'] = os.environ['NPY_BLAS_LIBS'].split() + if 'NPY_CBLAS_LIBS' in os.environ: + info['define_macros'].append(('HAVE_CBLAS', None)) + info['extra_link_args'].extend( + os.environ['NPY_CBLAS_LIBS'].split()) + self.set_info(**info) + return True + + def _calc_info(self, name): + return getattr(self, '_calc_info_{}'.format(name))() + + def calc_info(self): + blas_order, unknown_order = _parse_env_order(self.blas_order, self.order_env_var_name) + if len(unknown_order) > 0: + raise ValueError("blas_opt_info user defined BLAS order has unacceptable values: {}".format(unknown_order)) + + if 'NPY_BLAS_LIBS' in os.environ: + # Bypass autodetection, set language to F77 and use env var linker + # flags directly + self._calc_info_from_envvar() + return + + for blas in blas_order: + if self._calc_info(blas): + return + + if 'blas' not in blas_order: + # Since the user may request *not* to use any library, we still need + # to raise warnings to signal missing packages! + warnings.warn(BlasNotFoundError.__doc__ or '', stacklevel=2) + warnings.warn(BlasSrcNotFoundError.__doc__ or '', stacklevel=2) + + +class blas_ilp64_opt_info(blas_opt_info, _ilp64_opt_info_mixin): + notfounderror = BlasILP64NotFoundError + blas_order = ['openblas64_', 'openblas_ilp64'] + order_env_var_name = 'NPY_BLAS_ILP64_ORDER' + + def _calc_info(self, name): + info = get_info(name) + if self._check_info(info): + self.set_info(**info) + return True + return False + + +class blas_ilp64_plain_opt_info(blas_ilp64_opt_info): + symbol_prefix = '' + symbol_suffix = '' + + +class blas64__opt_info(blas_ilp64_opt_info): + symbol_prefix = '' + symbol_suffix = '64_' + + +class cblas_info(system_info): + section = 'cblas' + dir_env_var = 'CBLAS' + # No default as it's used only in blas_info + _lib_names = [] + notfounderror = BlasNotFoundError class blas_info(system_info): @@ -1669,39 +2123,71 @@ class blas_info(system_info): def calc_info(self): lib_dirs = self.get_lib_dirs() - blas_libs = self.get_libs('blas_libs', self._lib_names) + opt = self.get_option_single('blas_libs', 'libraries') + blas_libs = self.get_libs(opt, self._lib_names) info = self.check_libs(lib_dirs, blas_libs, []) if info is None: return else: info['include_dirs'] = self.get_include_dirs() if platform.system() == 'Windows': - # The check for windows is needed because has_cblas uses the + # The check for windows is needed because get_cblas_libs uses the # same compiler that was used to compile Python and msvc is # often not installed when mingw is being used. This rough # treatment is not desirable, but windows is tricky. info['language'] = 'f77' # XXX: is it generally true? + # If cblas is given as an option, use those + cblas_info_obj = cblas_info() + cblas_opt = cblas_info_obj.get_option_single('cblas_libs', 'libraries') + cblas_libs = cblas_info_obj.get_libs(cblas_opt, None) + if cblas_libs: + info['libraries'] = cblas_libs + blas_libs + info['define_macros'] = [('HAVE_CBLAS', None)] else: - lib = self.has_cblas(info) + lib = self.get_cblas_libs(info) if lib is not None: info['language'] = 'c' - info['libraries'] = [lib] + info['libraries'] = lib info['define_macros'] = [('HAVE_CBLAS', None)] self.set_info(**info) - def has_cblas(self, info): + def get_cblas_libs(self, info): + """ Check whether we can link with CBLAS interface + + This method will search through several combinations of libraries + to check whether CBLAS is present: + + 1. Libraries in ``info['libraries']``, as is + 2. As 1. but also explicitly adding ``'cblas'`` as a library + 3. As 1. but also explicitly adding ``'blas'`` as a library + 4. Check only library ``'cblas'`` + 5. Check only library ``'blas'`` + + Parameters + ---------- + info : dict + system information dictionary for compilation and linking + + Returns + ------- + libraries : list of str or None + a list of libraries that enables the use of CBLAS interface. + Returns None if not found or a compilation error occurs. + + Since 1.17 returns a list. + """ # primitive cblas check by looking for the header and trying to link # cblas or blas - res = False c = customized_ccompiler() tmpdir = tempfile.mkdtemp() - s = """#include <cblas.h> - int main(int argc, const char *argv[]) - { - double a[4] = {1,2,3,4}; - double b[4] = {5,6,7,8}; - return cblas_ddot(4, a, 1, b, 1) > 10; - }""" + s = textwrap.dedent("""\ + #include <cblas.h> + int main(int argc, const char *argv[]) + { + double a[4] = {1,2,3,4}; + double b[4] = {5,6,7,8}; + return cblas_ddot(4, a, 1, b, 1) > 10; + }""") src = os.path.join(tmpdir, 'source.c') try: with open(src, 'wt') as f: @@ -1711,46 +2197,55 @@ def has_cblas(self, info): # check we can compile (find headers) obj = c.compile([src], output_dir=tmpdir, include_dirs=self.get_include_dirs()) + except (distutils.ccompiler.CompileError, distutils.ccompiler.LinkError): + return None - # check we can link (find library) - # some systems have separate cblas and blas libs. First - # check for cblas lib, and if not present check for blas lib. + # check we can link (find library) + # some systems have separate cblas and blas libs. + for libs in [info['libraries'], ['cblas'] + info['libraries'], + ['blas'] + info['libraries'], ['cblas'], ['blas']]: try: c.link_executable(obj, os.path.join(tmpdir, "a.out"), - libraries=["cblas"], + libraries=libs, library_dirs=info['library_dirs'], extra_postargs=info.get('extra_link_args', [])) - res = "cblas" + return libs except distutils.ccompiler.LinkError: - c.link_executable(obj, os.path.join(tmpdir, "a.out"), - libraries=["blas"], - library_dirs=info['library_dirs'], - extra_postargs=info.get('extra_link_args', [])) - res = "blas" - except distutils.ccompiler.CompileError: - res = None + pass finally: shutil.rmtree(tmpdir) - return res + return None class openblas_info(blas_info): section = 'openblas' dir_env_var = 'OPENBLAS' _lib_names = ['openblas'] + _require_symbols = [] notfounderror = BlasNotFoundError - def check_embedded_lapack(self, info): - return True + @property + def symbol_prefix(self): + try: + return self.cp.get(self.section, 'symbol_prefix') + except NoOptionError: + return '' - def calc_info(self): + @property + def symbol_suffix(self): + try: + return self.cp.get(self.section, 'symbol_suffix') + except NoOptionError: + return '' + + def _calc_info(self): c = customized_ccompiler() lib_dirs = self.get_lib_dirs() - openblas_libs = self.get_libs('libraries', self._lib_names) - if openblas_libs == self._lib_names: # backward compat with 1.8.0 - openblas_libs = self.get_libs('openblas_libs', self._lib_names) + # Prefer to use libraries over openblas_libs + opt = self.get_option_single('openblas_libs', 'libraries') + openblas_libs = self.get_libs(opt, self._lib_names) info = self.check_libs(lib_dirs, openblas_libs, []) @@ -1761,23 +2256,33 @@ def calc_info(self): # Try gfortran-compatible library files info = self.check_msvc_gfortran_libs(lib_dirs, openblas_libs) # Skip lapack check, we'd need build_ext to do it - assume_lapack = True + skip_symbol_check = True elif info: - assume_lapack = False + skip_symbol_check = False info['language'] = 'c' if info is None: - return + return None # Add extra info for OpenBLAS extra_info = self.calc_extra_info() dict_append(info, **extra_info) - if not (assume_lapack or self.check_embedded_lapack(info)): - return + if not (skip_symbol_check or self.check_symbols(info)): + return None info['define_macros'] = [('HAVE_CBLAS', None)] - self.set_info(**info) + if self.symbol_prefix: + info['define_macros'] += [('BLAS_SYMBOL_PREFIX', self.symbol_prefix)] + if self.symbol_suffix: + info['define_macros'] += [('BLAS_SYMBOL_SUFFIX', self.symbol_suffix)] + + return info + + def calc_info(self): + info = self._calc_info() + if info is not None: + self.set_info(**info) def check_msvc_gfortran_libs(self, library_dirs, libraries): # First, find the full path to each library directory @@ -1793,16 +2298,17 @@ def check_msvc_gfortran_libs(self, library_dirs, libraries): return None # Generate numpy.distutils virtual static library file - tmpdir = os.path.join(os.getcwd(), 'build', 'openblas') + basename = self.__class__.__name__ + tmpdir = os.path.join(os.getcwd(), 'build', basename) if not os.path.isdir(tmpdir): os.makedirs(tmpdir) info = {'library_dirs': [tmpdir], - 'libraries': ['openblas'], + 'libraries': [basename], 'language': 'f77'} - fake_lib_file = os.path.join(tmpdir, 'openblas.fobjects') - fake_clib_file = os.path.join(tmpdir, 'openblas.cobjects') + fake_lib_file = os.path.join(tmpdir, basename + '.fobjects') + fake_clib_file = os.path.join(tmpdir, basename + '.cobjects') with open(fake_lib_file, 'w') as f: f.write("\n".join(library_paths)) with open(fake_clib_file, 'w') as f: @@ -1810,23 +2316,27 @@ def check_msvc_gfortran_libs(self, library_dirs, libraries): return info -class openblas_lapack_info(openblas_info): - section = 'openblas' - dir_env_var = 'OPENBLAS' - _lib_names = ['openblas'] - notfounderror = BlasNotFoundError - - def check_embedded_lapack(self, info): + def check_symbols(self, info): res = False c = customized_ccompiler() tmpdir = tempfile.mkdtemp() - s = """void zungqr_(); - int main(int argc, const char *argv[]) - { - zungqr_(); - return 0; - }""" + + prototypes = "\n".join("void %s%s%s();" % (self.symbol_prefix, + symbol_name, + self.symbol_suffix) + for symbol_name in self._require_symbols) + calls = "\n".join("%s%s%s();" % (self.symbol_prefix, + symbol_name, + self.symbol_suffix) + for symbol_name in self._require_symbols) + s = textwrap.dedent("""\ + %(prototypes)s + int main(int argc, const char *argv[]) + { + %(calls)s + return 0; + }""") % dict(prototypes=prototypes, calls=calls) src = os.path.join(tmpdir, 'source.c') out = os.path.join(tmpdir, 'a.out') # Add the additional "extra" arguments @@ -1834,8 +2344,6 @@ def check_embedded_lapack(self, info): extra_args = info['extra_link_args'] except Exception: extra_args = [] - if sys.version_info < (3, 5) and sys.version_info > (3, 0) and c.compiler_type == "msvc": - extra_args.append("/MANIFEST") try: with open(src, 'wt') as f: f.write(s) @@ -1851,9 +2359,49 @@ def check_embedded_lapack(self, info): shutil.rmtree(tmpdir) return res +class openblas_lapack_info(openblas_info): + section = 'openblas' + dir_env_var = 'OPENBLAS' + _lib_names = ['openblas'] + _require_symbols = ['zungqr_'] + notfounderror = BlasNotFoundError + class openblas_clapack_info(openblas_lapack_info): _lib_names = ['openblas', 'lapack'] +class openblas_ilp64_info(openblas_info): + section = 'openblas_ilp64' + dir_env_var = 'OPENBLAS_ILP64' + _lib_names = ['openblas64'] + _require_symbols = ['dgemm_', 'cblas_dgemm'] + notfounderror = BlasILP64NotFoundError + + def _calc_info(self): + info = super()._calc_info() + if info is not None: + info['define_macros'] += [('HAVE_BLAS_ILP64', None)] + return info + +class openblas_ilp64_lapack_info(openblas_ilp64_info): + _require_symbols = ['dgemm_', 'cblas_dgemm', 'zungqr_', 'LAPACKE_zungqr'] + + def _calc_info(self): + info = super()._calc_info() + if info: + info['define_macros'] += [('HAVE_LAPACKE', None)] + return info + +class openblas64__info(openblas_ilp64_info): + # ILP64 Openblas, with default symbol suffix + section = 'openblas64_' + dir_env_var = 'OPENBLAS64_' + _lib_names = ['openblas64_'] + symbol_suffix = '64_' + symbol_prefix = '' + +class openblas64__lapack_info(openblas_ilp64_lapack_info, openblas64__info): + pass + class blis_info(blas_info): section = 'blis' dir_env_var = 'BLIS' @@ -1862,10 +2410,8 @@ class blis_info(blas_info): def calc_info(self): lib_dirs = self.get_lib_dirs() - blis_libs = self.get_libs('libraries', self._lib_names) - if blis_libs == self._lib_names: - blis_libs = self.get_libs('blis_libs', self._lib_names) - + opt = self.get_option_single('blis_libs', 'libraries') + blis_libs = self.get_libs(opt, self._lib_names) info = self.check_libs2(lib_dirs, blis_libs, []) if info is None: return @@ -1878,8 +2424,90 @@ def calc_info(self): include_dirs=incl_dirs) self.set_info(**info) + +class flame_info(system_info): + """ Usage of libflame for LAPACK operations + + This requires libflame to be compiled with lapack wrappers: + + ./configure --enable-lapack2flame ... + + Be aware that libflame 5.1.0 has some missing names in the shared library, so + if you have problems, try the static flame library. + """ + section = 'flame' + _lib_names = ['flame'] + notfounderror = FlameNotFoundError + + def check_embedded_lapack(self, info): + """ libflame does not necessarily have a wrapper for fortran LAPACK, we need to check """ + c = customized_ccompiler() + + tmpdir = tempfile.mkdtemp() + s = textwrap.dedent("""\ + void zungqr_(); + int main(int argc, const char *argv[]) + { + zungqr_(); + return 0; + }""") + src = os.path.join(tmpdir, 'source.c') + out = os.path.join(tmpdir, 'a.out') + # Add the additional "extra" arguments + extra_args = info.get('extra_link_args', []) + try: + with open(src, 'wt') as f: + f.write(s) + obj = c.compile([src], output_dir=tmpdir) + try: + c.link_executable(obj, out, libraries=info['libraries'], + library_dirs=info['library_dirs'], + extra_postargs=extra_args) + return True + except distutils.ccompiler.LinkError: + return False + finally: + shutil.rmtree(tmpdir) + + def calc_info(self): + lib_dirs = self.get_lib_dirs() + flame_libs = self.get_libs('libraries', self._lib_names) + + info = self.check_libs2(lib_dirs, flame_libs, []) + if info is None: + return + + # Add the extra flag args to info + extra_info = self.calc_extra_info() + dict_append(info, **extra_info) + + if self.check_embedded_lapack(info): + # check if the user has supplied all information required + self.set_info(**info) + else: + # Try and get the BLAS lib to see if we can get it to work + blas_info = get_info('blas_opt') + if not blas_info: + # since we already failed once, this ain't going to work either + return + + # Now we need to merge the two dictionaries + for key in blas_info: + if isinstance(blas_info[key], list): + info[key] = info.get(key, []) + blas_info[key] + elif isinstance(blas_info[key], tuple): + info[key] = info.get(key, ()) + blas_info[key] + else: + info[key] = info.get(key, '') + blas_info[key] + + # Now check again + if self.check_embedded_lapack(info): + self.set_info(**info) + + class accelerate_info(system_info): section = 'accelerate' + _lib_names = ['accelerate', 'veclib'] notfounderror = BlasNotFoundError def calc_info(self): @@ -1888,7 +2516,7 @@ def calc_info(self): if libraries: libraries = [libraries] else: - libraries = self.get_libs('libraries', ['accelerate', 'veclib']) + libraries = self.get_libs('libraries', self._lib_names) libraries = [lib.strip().lower() for lib in libraries] if (sys.platform == 'darwin' and @@ -1907,8 +2535,6 @@ def calc_info(self): 'accelerate' in libraries): if intel: args.extend(['-msse3']) - else: - args.extend(['-faltivec']) args.extend([ '-I/System/Library/Frameworks/vecLib.framework/Headers']) link_args.extend(['-Wl,-framework', '-Wl,Accelerate']) @@ -1917,8 +2543,6 @@ def calc_info(self): 'veclib' in libraries): if intel: args.extend(['-msse3']) - else: - args.extend(['-faltivec']) args.extend([ '-I/System/Library/Frameworks/vecLib.framework/Headers']) link_args.extend(['-Wl,-framework', '-Wl,vecLib']) @@ -1932,6 +2556,9 @@ def calc_info(self): return class blas_src_info(system_info): + # BLAS_SRC is deprecated, please do not use this! + # Build or install a BLAS library via your package manager or from + # source separately. section = 'blas_src' dir_env_var = 'BLAS_SRC' notfounderror = BlasSrcNotFoundError @@ -1986,6 +2613,7 @@ def calc_info(self): class x11_info(system_info): section = 'x11' notfounderror = X11NotFoundError + _lib_names = ['X11'] def __init__(self): system_info.__init__(self, @@ -1997,7 +2625,8 @@ def calc_info(self): return lib_dirs = self.get_lib_dirs() include_dirs = self.get_include_dirs() - x11_libs = self.get_libs('x11_libs', ['X11']) + opt = self.get_option_single('x11_libs', 'libraries') + x11_libs = self.get_libs(opt, self._lib_names) info = self.check_libs(lib_dirs, x11_libs, []) if info is None: return @@ -2033,13 +2662,12 @@ def __init__(self): except AttributeError: pass - include_dirs.append(distutils.sysconfig.get_python_inc( - prefix=os.sep.join(prefix))) + include_dirs.append(sysconfig.get_path('include')) except ImportError: pass - py_incl_dir = distutils.sysconfig.get_python_inc() + py_incl_dir = sysconfig.get_path('include') include_dirs.append(py_incl_dir) - py_pincl_dir = distutils.sysconfig.get_python_inc(plat_specific=True) + py_pincl_dir = sysconfig.get_path('platinclude') if py_pincl_dir not in include_dirs: include_dirs.append(py_pincl_dir) for d in default_include_dirs: @@ -2062,7 +2690,7 @@ def calc_info(self): if vrs is None: continue macros = [(self.modulename.upper() + '_VERSION', - '"\\"%s\\""' % (vrs)), + _c_string_literal(vrs)), (self.modulename.upper(), None)] break dict_append(info, define_macros=macros) @@ -2107,20 +2735,20 @@ def calc_info(self): if which[0] is None: which = "numpy", "defaulted" try: - import numpy + import numpy # noqa: F401 which = "numpy", "defaulted" - except ImportError: - msg1 = str(get_exception()) + except ImportError as e: + msg1 = str(e) try: - import Numeric + import Numeric # noqa: F401 which = "numeric", "defaulted" - except ImportError: - msg2 = str(get_exception()) + except ImportError as e: + msg2 = str(e) try: - import numarray + import numarray # noqa: F401 which = "numarray", "defaulted" - except ImportError: - msg3 = str(get_exception()) + except ImportError as e: + msg3 = str(e) log.info(msg1) log.info(msg2) log.info(msg3) @@ -2166,8 +2794,8 @@ def calc_info(self): break if not src_dir: return - py_incl_dirs = [distutils.sysconfig.get_python_inc()] - py_pincl_dir = distutils.sysconfig.get_python_inc(plat_specific=True) + py_incl_dirs = [sysconfig.get_path('include')] + py_pincl_dir = sysconfig.get_path('platinclude') if py_pincl_dir not in py_incl_dirs: py_incl_dirs.append(py_pincl_dir) srcs_dir = os.path.join(src_dir, 'libs', 'python', 'src') @@ -2243,8 +2871,12 @@ def get_config_exe(self): def get_config_output(self, config_exe, option): cmd = config_exe + ' ' + self.append_config_exe + ' ' + option - s, o = exec_command(cmd, use_tee=0) - if not s: + try: + o = subprocess.check_output(cmd) + except (OSError, subprocess.CalledProcessError): + pass + else: + o = filepath_from_subprocess_output(o) return o def calc_info(self): @@ -2263,7 +2895,7 @@ def calc_info(self): version = self.get_config_output(config_exe, self.version_flag) if version: macros.append((self.__class__.__name__.split('.')[-1].upper(), - '"\\"%s\\""' % (version))) + _c_string_literal(version))) if self.version_macro_name: macros.append((self.version_macro_name + '_%s' % (version.replace('.', '_')), None)) @@ -2384,7 +3016,8 @@ class amd_info(system_info): def calc_info(self): lib_dirs = self.get_lib_dirs() - amd_libs = self.get_libs('amd_libs', self._lib_names) + opt = self.get_option_single('amd_libs', 'libraries') + amd_libs = self.get_libs(opt, self._lib_names) info = self.check_libs(lib_dirs, amd_libs, []) if info is None: return @@ -2415,7 +3048,8 @@ class umfpack_info(system_info): def calc_info(self): lib_dirs = self.get_lib_dirs() - umfpack_libs = self.get_libs('umfpack_libs', self._lib_names) + opt = self.get_option_single('umfpack_libs', 'libraries') + umfpack_libs = self.get_libs(opt, self._lib_names) info = self.check_libs(lib_dirs, umfpack_libs, []) if info is None: return @@ -2433,7 +3067,6 @@ def calc_info(self): define_macros=[('SCIPY_UMFPACK_H', None)], swig_opts=['-I' + inc_dir]) - amd = get_info('amd') dict_append(info, **get_info('amd')) self.set_info(**info) @@ -2529,7 +3162,9 @@ def show_all(argv=None): del show_only[show_only.index(name)] conf = c() conf.verbosity = 2 - r = conf.get_info() + # we don't need the result, but we want + # the side effect of printing diagnostics + conf.get_info() if show_only: log.info('Info classes not defined: %s', ','.join(show_only)) diff --git a/numpy/distutils/tests/test_build_ext.py b/numpy/distutils/tests/test_build_ext.py new file mode 100644 index 000000000000..c007159f520e --- /dev/null +++ b/numpy/distutils/tests/test_build_ext.py @@ -0,0 +1,72 @@ +'''Tests for numpy.distutils.build_ext.''' + +import os +import subprocess +import sys +from textwrap import indent, dedent +import pytest + +@pytest.mark.slow +def test_multi_fortran_libs_link(tmp_path): + ''' + Ensures multiple "fake" static libraries are correctly linked. + see gh-18295 + ''' + + # We need to make sure we actually have an f77 compiler. + # This is nontrivial, so we'll borrow the utilities + # from f2py tests: + from numpy.f2py.tests.util import has_f77_compiler + if not has_f77_compiler(): + pytest.skip('No F77 compiler found') + + # make some dummy sources + with open(tmp_path / '_dummy1.f', 'w') as fid: + fid.write(indent(dedent('''\ + FUNCTION dummy_one() + RETURN + END FUNCTION'''), prefix=' '*6)) + with open(tmp_path / '_dummy2.f', 'w') as fid: + fid.write(indent(dedent('''\ + FUNCTION dummy_two() + RETURN + END FUNCTION'''), prefix=' '*6)) + with open(tmp_path / '_dummy.c', 'w') as fid: + # doesn't need to load - just needs to exist + fid.write('int PyInit_dummyext;') + + # make a setup file + with open(tmp_path / 'setup.py', 'w') as fid: + srctree = os.path.join(os.path.dirname(__file__), '..', '..', '..') + fid.write(dedent(f'''\ + def configuration(parent_package="", top_path=None): + from numpy.distutils.misc_util import Configuration + config = Configuration("", parent_package, top_path) + config.add_library("dummy1", sources=["_dummy1.f"]) + config.add_library("dummy2", sources=["_dummy2.f"]) + config.add_extension("dummyext", sources=["_dummy.c"], libraries=["dummy1", "dummy2"]) + return config + + + if __name__ == "__main__": + import sys + sys.path.insert(0, r"{srctree}") + from numpy.distutils.core import setup + setup(**configuration(top_path="").todict())''')) + + # build the test extensino and "install" into a temporary directory + build_dir = tmp_path + subprocess.check_call([sys.executable, 'setup.py', 'build', 'install', + '--prefix', str(tmp_path / 'installdir'), + '--record', str(tmp_path / 'tmp_install_log.txt'), + ], + cwd=str(build_dir), + ) + # get the path to the so + so = None + with open(tmp_path /'tmp_install_log.txt') as fid: + for line in fid: + if 'dummyext' in line: + so = line.strip() + break + assert so is not None diff --git a/numpy/distutils/tests/test_ccompiler_opt.py b/numpy/distutils/tests/test_ccompiler_opt.py new file mode 100644 index 000000000000..1b27ab07c393 --- /dev/null +++ b/numpy/distutils/tests/test_ccompiler_opt.py @@ -0,0 +1,789 @@ +import re, textwrap, os +from os import sys, path +from distutils.errors import DistutilsError + +is_standalone = __name__ == '__main__' and __package__ is None +if is_standalone: + import unittest, contextlib, tempfile, shutil + sys.path.append(path.abspath(path.join(path.dirname(__file__), ".."))) + from ccompiler_opt import CCompilerOpt + + # from numpy/testing/_private/utils.py + @contextlib.contextmanager + def tempdir(*args, **kwargs): + tmpdir = tempfile.mkdtemp(*args, **kwargs) + try: + yield tmpdir + finally: + shutil.rmtree(tmpdir) + + def assert_(expr, msg=''): + if not expr: + raise AssertionError(msg) +else: + from numpy.distutils.ccompiler_opt import CCompilerOpt + from numpy.testing import assert_, tempdir + +# architectures and compilers to test +arch_compilers = dict( + x86 = ("gcc", "clang", "icc", "iccw", "msvc"), + x64 = ("gcc", "clang", "icc", "iccw", "msvc"), + ppc64 = ("gcc", "clang"), + ppc64le = ("gcc", "clang"), + armhf = ("gcc", "clang"), + aarch64 = ("gcc", "clang"), + noarch = ("gcc",) +) + +class FakeCCompilerOpt(CCompilerOpt): + fake_info = "" + def __init__(self, trap_files="", trap_flags="", *args, **kwargs): + self.fake_trap_files = trap_files + self.fake_trap_flags = trap_flags + CCompilerOpt.__init__(self, None, **kwargs) + + def __repr__(self): + return textwrap.dedent("""\ + <<<< + march : {} + compiler : {} + ---------------- + {} + >>>> + """).format(self.cc_march, self.cc_name, self.report()) + + def dist_compile(self, sources, flags, **kwargs): + assert(isinstance(sources, list)) + assert(isinstance(flags, list)) + if self.fake_trap_files: + for src in sources: + if re.match(self.fake_trap_files, src): + self.dist_error("source is trapped by a fake interface") + if self.fake_trap_flags: + for f in flags: + if re.match(self.fake_trap_flags, f): + self.dist_error("flag is trapped by a fake interface") + # fake objects + return zip(sources, [' '.join(flags)] * len(sources)) + + def dist_info(self): + return FakeCCompilerOpt.fake_info + + @staticmethod + def dist_log(*args, stderr=False): + pass + +class _Test_CCompilerOpt: + arch = None # x86_64 + cc = None # gcc + + def setup(self): + FakeCCompilerOpt.conf_nocache = True + self._opt = None + + def nopt(self, *args, **kwargs): + FakeCCompilerOpt.fake_info = (self.arch, self.cc, "") + return FakeCCompilerOpt(*args, **kwargs) + + def opt(self): + if not self._opt: + self._opt = self.nopt() + return self._opt + + def march(self): + return self.opt().cc_march + + def cc_name(self): + return self.opt().cc_name + + def get_targets(self, targets, groups, **kwargs): + FakeCCompilerOpt.conf_target_groups = groups + opt = self.nopt( + cpu_baseline=kwargs.get("baseline", "min"), + cpu_dispatch=kwargs.get("dispatch", "max"), + trap_files=kwargs.get("trap_files", ""), + trap_flags=kwargs.get("trap_flags", "") + ) + with tempdir() as tmpdir: + file = os.path.join(tmpdir, "test_targets.c") + with open(file, 'w') as f: + f.write(targets) + gtargets = [] + gflags = {} + fake_objects = opt.try_dispatch([file]) + for source, flags in fake_objects: + gtar = path.basename(source).split('.')[1:-1] + glen = len(gtar) + if glen == 0: + gtar = "baseline" + elif glen == 1: + gtar = gtar[0].upper() + else: + # converting multi-target into parentheses str format to be equivalent + # to the configuration statements syntax. + gtar = ('('+' '.join(gtar)+')').upper() + gtargets.append(gtar) + gflags[gtar] = flags + + has_baseline, targets = opt.sources_status[file] + targets = targets + ["baseline"] if has_baseline else targets + # convert tuple that represent multi-target into parentheses str format + targets = [ + '('+' '.join(tar)+')' if isinstance(tar, tuple) else tar + for tar in targets + ] + if len(targets) != len(gtargets) or not all(t in gtargets for t in targets): + raise AssertionError( + "'sources_status' returns different targets than the compiled targets\n" + "%s != %s" % (targets, gtargets) + ) + # return targets from 'sources_status' since the order is matters + return targets, gflags + + def arg_regex(self, **kwargs): + map2origin = dict( + x64 = "x86", + ppc64le = "ppc64", + aarch64 = "armhf", + clang = "gcc", + ) + march = self.march(); cc_name = self.cc_name() + map_march = map2origin.get(march, march) + map_cc = map2origin.get(cc_name, cc_name) + for key in ( + march, cc_name, map_march, map_cc, + march + '_' + cc_name, + map_march + '_' + cc_name, + march + '_' + map_cc, + map_march + '_' + map_cc, + ) : + regex = kwargs.pop(key, None) + if regex is not None: + break + if regex: + if isinstance(regex, dict): + for k, v in regex.items(): + if v[-1:] not in ')}$?\\.+*': + regex[k] = v + '$' + else: + assert(isinstance(regex, str)) + if regex[-1:] not in ')}$?\\.+*': + regex += '$' + return regex + + def expect(self, dispatch, baseline="", **kwargs): + match = self.arg_regex(**kwargs) + if match is None: + return + opt = self.nopt( + cpu_baseline=baseline, cpu_dispatch=dispatch, + trap_files=kwargs.get("trap_files", ""), + trap_flags=kwargs.get("trap_flags", "") + ) + features = ' '.join(opt.cpu_dispatch_names()) + if not match: + if len(features) != 0: + raise AssertionError( + 'expected empty features, not "%s"' % features + ) + return + if not re.match(match, features, re.IGNORECASE): + raise AssertionError( + 'dispatch features "%s" not match "%s"' % (features, match) + ) + + def expect_baseline(self, baseline, dispatch="", **kwargs): + match = self.arg_regex(**kwargs) + if match is None: + return + opt = self.nopt( + cpu_baseline=baseline, cpu_dispatch=dispatch, + trap_files=kwargs.get("trap_files", ""), + trap_flags=kwargs.get("trap_flags", "") + ) + features = ' '.join(opt.cpu_baseline_names()) + if not match: + if len(features) != 0: + raise AssertionError( + 'expected empty features, not "%s"' % features + ) + return + if not re.match(match, features, re.IGNORECASE): + raise AssertionError( + 'baseline features "%s" not match "%s"' % (features, match) + ) + + def expect_flags(self, baseline, dispatch="", **kwargs): + match = self.arg_regex(**kwargs) + if match is None: + return + opt = self.nopt( + cpu_baseline=baseline, cpu_dispatch=dispatch, + trap_files=kwargs.get("trap_files", ""), + trap_flags=kwargs.get("trap_flags", "") + ) + flags = ' '.join(opt.cpu_baseline_flags()) + if not match: + if len(flags) != 0: + raise AssertionError( + 'expected empty flags not "%s"' % flags + ) + return + if not re.match(match, flags): + raise AssertionError( + 'flags "%s" not match "%s"' % (flags, match) + ) + + def expect_targets(self, targets, groups={}, **kwargs): + match = self.arg_regex(**kwargs) + if match is None: + return + targets, _ = self.get_targets(targets=targets, groups=groups, **kwargs) + targets = ' '.join(targets) + if not match: + if len(targets) != 0: + raise AssertionError( + 'expected empty targets, not "%s"' % targets + ) + return + if not re.match(match, targets, re.IGNORECASE): + raise AssertionError( + 'targets "%s" not match "%s"' % (targets, match) + ) + + def expect_target_flags(self, targets, groups={}, **kwargs): + match_dict = self.arg_regex(**kwargs) + if match_dict is None: + return + assert(isinstance(match_dict, dict)) + _, tar_flags = self.get_targets(targets=targets, groups=groups) + + for match_tar, match_flags in match_dict.items(): + if match_tar not in tar_flags: + raise AssertionError( + 'expected to find target "%s"' % match_tar + ) + flags = tar_flags[match_tar] + if not match_flags: + if len(flags) != 0: + raise AssertionError( + 'expected to find empty flags in target "%s"' % match_tar + ) + if not re.match(match_flags, flags): + raise AssertionError( + '"%s" flags "%s" not match "%s"' % (match_tar, flags, match_flags) + ) + + def test_interface(self): + wrong_arch = "ppc64" if self.arch != "ppc64" else "x86" + wrong_cc = "clang" if self.cc != "clang" else "icc" + opt = self.opt() + assert_(getattr(opt, "cc_on_" + self.arch)) + assert_(not getattr(opt, "cc_on_" + wrong_arch)) + assert_(getattr(opt, "cc_is_" + self.cc)) + assert_(not getattr(opt, "cc_is_" + wrong_cc)) + + def test_args_empty(self): + for baseline, dispatch in ( + ("", "none"), + (None, ""), + ("none +none", "none - none"), + ("none -max", "min - max"), + ("+vsx2 -VSX2", "vsx avx2 avx512f -max"), + ("max -vsx - avx + avx512f neon -MAX ", + "min -min + max -max -vsx + avx2 -avx2 +NONE") + ) : + opt = self.nopt(cpu_baseline=baseline, cpu_dispatch=dispatch) + assert(len(opt.cpu_baseline_names()) == 0) + assert(len(opt.cpu_dispatch_names()) == 0) + + def test_args_validation(self): + if self.march() == "unknown": + return + # check sanity of argument's validation + for baseline, dispatch in ( + ("unkown_feature - max +min", "unknown max min"), # unknowing features + ("#avx2", "$vsx") # groups and polices aren't acceptable + ) : + try: + self.nopt(cpu_baseline=baseline, cpu_dispatch=dispatch) + raise AssertionError("excepted an exception for invalid arguments") + except DistutilsError: + pass + + def test_skip(self): + # only takes what platform supports and skip the others + # without casing exceptions + self.expect( + "sse vsx neon", + x86="sse", ppc64="vsx", armhf="neon", unknown="" + ) + self.expect( + "sse41 avx avx2 vsx2 vsx3 neon_vfpv4 asimd", + x86 = "sse41 avx avx2", + ppc64 = "vsx2 vsx3", + armhf = "neon_vfpv4 asimd", + unknown = "" + ) + # any features in cpu_dispatch must be ignored if it's part of baseline + self.expect( + "sse neon vsx", baseline="sse neon vsx", + x86="", ppc64="", armhf="" + ) + self.expect( + "avx2 vsx3 asimdhp", baseline="avx2 vsx3 asimdhp", + x86="", ppc64="", armhf="" + ) + + def test_implies(self): + # baseline combining implied features, so we count + # on it instead of testing 'feature_implies()'' directly + self.expect_baseline( + "fma3 avx2 asimd vsx3", + # .* between two spaces can validate features in between + x86 = "sse .* sse41 .* fma3.*avx2", + ppc64 = "vsx vsx2 vsx3", + armhf = "neon neon_fp16 neon_vfpv4 asimd" + ) + """ + special cases + """ + # in icc and msvc, FMA3 and AVX2 can't be separated + # both need to implies each other, same for avx512f & cd + for f0, f1 in ( + ("fma3", "avx2"), + ("avx512f", "avx512cd"), + ): + diff = ".* sse42 .* %s .*%s$" % (f0, f1) + self.expect_baseline(f0, + x86_gcc=".* sse42 .* %s$" % f0, + x86_icc=diff, x86_iccw=diff + ) + self.expect_baseline(f1, + x86_gcc=".* avx .* %s$" % f1, + x86_icc=diff, x86_iccw=diff + ) + # in msvc, following features can't be separated too + for f in (("fma3", "avx2"), ("avx512f", "avx512cd", "avx512_skx")): + for ff in f: + self.expect_baseline(ff, + x86_msvc=".*%s" % ' '.join(f) + ) + + # in ppc64le VSX and VSX2 can't be separated + self.expect_baseline("vsx", ppc64le="vsx vsx2") + # in aarch64 following features can't be separated + for f in ("neon", "neon_fp16", "neon_vfpv4", "asimd"): + self.expect_baseline(f, aarch64="neon neon_fp16 neon_vfpv4 asimd") + + def test_args_options(self): + # max & native + for o in ("max", "native"): + if o == "native" and self.cc_name() == "msvc": + continue + self.expect(o, + trap_files=".*cpu_(sse|vsx|neon).c", + x86="", ppc64="", armhf="" + ) + self.expect(o, + trap_files=".*cpu_(sse3|vsx2|neon_vfpv4).c", + x86="sse sse2", ppc64="vsx", armhf="neon neon_fp16", + aarch64="", ppc64le="" + ) + self.expect(o, + trap_files=".*cpu_(popcnt|vsx3).c", + x86="sse .* sse41", ppc64="vsx vsx2", + armhf="neon neon_fp16 .* asimd .*" + ) + self.expect(o, + x86_gcc=".* xop fma4 .* avx512f .* avx512_knl avx512_knm avx512_skx .*", + # in icc, xop and fam4 aren't supported + x86_icc=".* avx512f .* avx512_knl avx512_knm avx512_skx .*", + x86_iccw=".* avx512f .* avx512_knl avx512_knm avx512_skx .*", + # in msvc, avx512_knl avx512_knm aren't supported + x86_msvc=".* xop fma4 .* avx512f .* avx512_skx .*", + armhf=".* asimd asimdhp asimddp .*", + ppc64="vsx vsx2 vsx3.*" + ) + # min + self.expect("min", + x86="sse sse2", x64="sse sse2 sse3", + armhf="", aarch64="neon neon_fp16 .* asimd", + ppc64="", ppc64le="vsx vsx2" + ) + self.expect( + "min", trap_files=".*cpu_(sse2|vsx2).c", + x86="", ppc64le="" + ) + # an exception must triggered if native flag isn't supported + # when option "native" is activated through the args + try: + self.expect("native", + trap_flags=".*(-march=native|-xHost|/QxHost).*", + x86=".*", ppc64=".*", armhf=".*" + ) + if self.march() != "unknown": + raise AssertionError( + "excepted an exception for %s" % self.march() + ) + except DistutilsError: + if self.march() == "unknown": + raise AssertionError("excepted no exceptions") + + def test_flags(self): + self.expect_flags( + "sse sse2 vsx vsx2 neon neon_fp16", + x86_gcc="-msse -msse2", x86_icc="-msse -msse2", + x86_iccw="/arch:SSE2", + x86_msvc="/arch:SSE2" if self.march() == "x86" else "", + ppc64_gcc= "-mcpu=power8", + ppc64_clang="-maltivec -mvsx -mpower8-vector", + armhf_gcc="-mfpu=neon-fp16 -mfp16-format=ieee", + aarch64="" + ) + # testing normalize -march + self.expect_flags( + "asimd", + aarch64="", + armhf_gcc=r"-mfp16-format=ieee -mfpu=neon-fp-armv8 -march=armv8-a\+simd" + ) + self.expect_flags( + "asimdhp", + aarch64_gcc=r"-march=armv8.2-a\+fp16", + armhf_gcc=r"-mfp16-format=ieee -mfpu=neon-fp-armv8 -march=armv8.2-a\+fp16" + ) + self.expect_flags( + "asimddp", aarch64_gcc=r"-march=armv8.2-a\+dotprod" + ) + self.expect_flags( + # asimdfhm implies asimdhp + "asimdfhm", aarch64_gcc=r"-march=armv8.2-a\+fp16\+fp16fml" + ) + self.expect_flags( + "asimddp asimdhp asimdfhm", + aarch64_gcc=r"-march=armv8.2-a\+dotprod\+fp16\+fp16fml" + ) + + def test_targets_exceptions(self): + for targets in ( + "bla bla", "/*@targets", + "/*@targets */", + "/*@targets unknown */", + "/*@targets $unknown_policy avx2 */", + "/*@targets #unknown_group avx2 */", + "/*@targets $ */", + "/*@targets # vsx */", + "/*@targets #$ vsx */", + "/*@targets vsx avx2 ) */", + "/*@targets vsx avx2 (avx2 */", + "/*@targets vsx avx2 () */", + "/*@targets vsx avx2 ($autovec) */", # no features + "/*@targets vsx avx2 (xxx) */", + "/*@targets vsx avx2 (baseline) */", + ) : + try: + self.expect_targets( + targets, + x86="", armhf="", ppc64="" + ) + if self.march() != "unknown": + raise AssertionError( + "excepted an exception for %s" % self.march() + ) + except DistutilsError: + if self.march() == "unknown": + raise AssertionError("excepted no exceptions") + + def test_targets_syntax(self): + for targets in ( + "/*@targets $keep_baseline sse vsx neon*/", + "/*@targets,$keep_baseline,sse,vsx,neon*/", + "/*@targets*$keep_baseline*sse*vsx*neon*/", + """ + /* + ** @targets + ** $keep_baseline, sse vsx,neon + */ + """, + """ + /* + ************@targets************* + ** $keep_baseline, sse vsx, neon + ********************************* + */ + """, + """ + /* + /////////////@targets///////////////// + //$keep_baseline//sse//vsx//neon + ///////////////////////////////////// + */ + """, + """ + /* + @targets + $keep_baseline + SSE VSX NEON*/ + """ + ) : + self.expect_targets(targets, + x86="sse", ppc64="vsx", armhf="neon", unknown="" + ) + + def test_targets(self): + # test skipping baseline features + self.expect_targets( + """ + /*@targets + sse sse2 sse41 avx avx2 avx512f + vsx vsx2 vsx3 + neon neon_fp16 asimdhp asimddp + */ + """, + baseline="avx vsx2 asimd", + x86="avx512f avx2", armhf="asimddp asimdhp", ppc64="vsx3" + ) + # test skipping non-dispatch features + self.expect_targets( + """ + /*@targets + sse41 avx avx2 avx512f + vsx2 vsx3 + asimd asimdhp asimddp + */ + """, + baseline="", dispatch="sse41 avx2 vsx2 asimd asimddp", + x86="avx2 sse41", armhf="asimddp asimd", ppc64="vsx2" + ) + # test skipping features that not supported + self.expect_targets( + """ + /*@targets + sse2 sse41 avx2 avx512f + vsx2 vsx3 + neon asimdhp asimddp + */ + """, + baseline="", + trap_files=".*(avx2|avx512f|vsx3|asimddp).c", + x86="sse41 sse2", ppc64="vsx2", armhf="asimdhp neon" + ) + # test skipping features that implies each other + self.expect_targets( + """ + /*@targets + sse sse2 avx fma3 avx2 avx512f avx512cd + vsx vsx2 vsx3 + neon neon_vfpv4 neon_fp16 neon_fp16 asimd asimdhp + asimddp asimdfhm + */ + """, + baseline="", + x86_gcc="avx512cd avx512f avx2 fma3 avx sse2", + x86_msvc="avx512cd avx2 avx sse2", + x86_icc="avx512cd avx2 avx sse2", + x86_iccw="avx512cd avx2 avx sse2", + ppc64="vsx3 vsx2 vsx", + ppc64le="vsx3 vsx2", + armhf="asimdfhm asimddp asimdhp asimd neon_vfpv4 neon_fp16 neon", + aarch64="asimdfhm asimddp asimdhp asimd" + ) + + def test_targets_policies(self): + # 'keep_baseline', generate objects for baseline features + self.expect_targets( + """ + /*@targets + $keep_baseline + sse2 sse42 avx2 avx512f + vsx2 vsx3 + neon neon_vfpv4 asimd asimddp + */ + """, + baseline="sse41 avx2 vsx2 asimd vsx3", + x86="avx512f avx2 sse42 sse2", + ppc64="vsx3 vsx2", + armhf="asimddp asimd neon_vfpv4 neon", + # neon, neon_vfpv4, asimd implies each other + aarch64="asimddp asimd" + ) + # 'keep_sort', leave the sort as-is + self.expect_targets( + """ + /*@targets + $keep_baseline $keep_sort + avx512f sse42 avx2 sse2 + vsx2 vsx3 + asimd neon neon_vfpv4 asimddp + */ + """, + x86="avx512f sse42 avx2 sse2", + ppc64="vsx2 vsx3", + armhf="asimd neon neon_vfpv4 asimddp", + # neon, neon_vfpv4, asimd implies each other + aarch64="asimd asimddp" + ) + # 'autovec', skipping features that can't be + # vectorized by the compiler + self.expect_targets( + """ + /*@targets + $keep_baseline $keep_sort $autovec + avx512f avx2 sse42 sse41 sse2 + vsx3 vsx2 + asimddp asimd neon_vfpv4 neon + */ + """, + x86_gcc="avx512f avx2 sse42 sse41 sse2", + x86_icc="avx512f avx2 sse42 sse41 sse2", + x86_iccw="avx512f avx2 sse42 sse41 sse2", + x86_msvc="avx512f avx2 sse2" + if self.march() == 'x86' else "avx512f avx2", + ppc64="vsx3 vsx2", + armhf="asimddp asimd neon_vfpv4 neon", + # neon, neon_vfpv4, asimd implies each other + aarch64="asimddp asimd" + ) + for policy in ("$maxopt", "$autovec"): + # 'maxopt' and autovec set the max acceptable optimization flags + self.expect_target_flags( + "/*@targets baseline %s */" % policy, + gcc={"baseline":".*-O3.*"}, icc={"baseline":".*-O3.*"}, + iccw={"baseline":".*/O3.*"}, msvc={"baseline":".*/O2.*"}, + unknown={"baseline":".*"} + ) + + # 'werror', force compilers to treat warnings as errors + self.expect_target_flags( + "/*@targets baseline $werror */", + gcc={"baseline":".*-Werror.*"}, icc={"baseline":".*-Werror.*"}, + iccw={"baseline":".*/Werror.*"}, msvc={"baseline":".*/WX.*"}, + unknown={"baseline":".*"} + ) + + def test_targets_groups(self): + self.expect_targets( + """ + /*@targets $keep_baseline baseline #test_group */ + """, + groups=dict( + test_group=(""" + $keep_baseline + asimddp sse2 vsx2 avx2 vsx3 + avx512f asimdhp + """) + ), + x86="avx512f avx2 sse2 baseline", + ppc64="vsx3 vsx2 baseline", + armhf="asimddp asimdhp baseline" + ) + # test skip duplicating and sorting + self.expect_targets( + """ + /*@targets + * sse42 avx avx512f + * #test_group_1 + * vsx2 + * #test_group_2 + * asimddp asimdfhm + */ + """, + groups=dict( + test_group_1=(""" + VSX2 vsx3 asimd avx2 SSE41 + """), + test_group_2=(""" + vsx2 vsx3 asImd aVx2 sse41 + """) + ), + x86="avx512f avx2 avx sse42 sse41", + ppc64="vsx3 vsx2", + # vsx2 part of the default baseline of ppc64le, option ("min") + ppc64le="vsx3", + armhf="asimdfhm asimddp asimd", + # asimd part of the default baseline of aarch64, option ("min") + aarch64="asimdfhm asimddp" + ) + + def test_targets_multi(self): + self.expect_targets( + """ + /*@targets + (avx512_clx avx512_cnl) (asimdhp asimddp) + */ + """, + x86=r"\(avx512_clx avx512_cnl\)", + armhf=r"\(asimdhp asimddp\)", + ) + # test skipping implied features and auto-sort + self.expect_targets( + """ + /*@targets + f16c (sse41 avx sse42) (sse3 avx2 avx512f) + vsx2 (vsx vsx3 vsx2) + (neon neon_vfpv4 asimd asimdhp asimddp) + */ + """, + x86="avx512f f16c avx", + ppc64="vsx3 vsx2", + ppc64le="vsx3", # vsx2 part of baseline + armhf=r"\(asimdhp asimddp\)", + ) + # test skipping implied features and keep sort + self.expect_targets( + """ + /*@targets $keep_sort + (sse41 avx sse42) (sse3 avx2 avx512f) + (vsx vsx3 vsx2) + (asimddp neon neon_vfpv4 asimd asimdhp) + */ + """, + x86="avx avx512f", + ppc64="vsx3", + armhf=r"\(asimdhp asimddp\)", + ) + # test compiler variety and avoiding duplicating + self.expect_targets( + """ + /*@targets $keep_sort + fma3 avx2 (fma3 avx2) (avx2 fma3) avx2 fma3 + */ + """, + x86_gcc=r"fma3 avx2 \(fma3 avx2\)", + x86_icc="avx2", x86_iccw="avx2", + x86_msvc="avx2" + ) + +def new_test(arch, cc): + if is_standalone: return textwrap.dedent("""\ + class TestCCompilerOpt_{class_name}(_Test_CCompilerOpt, unittest.TestCase): + arch = '{arch}' + cc = '{cc}' + def __init__(self, methodName="runTest"): + unittest.TestCase.__init__(self, methodName) + self.setup() + """).format( + class_name=arch + '_' + cc, arch=arch, cc=cc + ) + return textwrap.dedent("""\ + class TestCCompilerOpt_{class_name}(_Test_CCompilerOpt): + arch = '{arch}' + cc = '{cc}' + """).format( + class_name=arch + '_' + cc, arch=arch, cc=cc + ) +""" +if 1 and is_standalone: + FakeCCompilerOpt.fake_info = "x86_icc" + cco = FakeCCompilerOpt(None, cpu_baseline="avx2") + print(' '.join(cco.cpu_baseline_names())) + print(cco.cpu_baseline_flags()) + unittest.main() + sys.exit() +""" +for arch, compilers in arch_compilers.items(): + for cc in compilers: + exec(new_test(arch, cc)) + +if is_standalone: + unittest.main() diff --git a/numpy/distutils/tests/test_ccompiler_opt_conf.py b/numpy/distutils/tests/test_ccompiler_opt_conf.py new file mode 100644 index 000000000000..09c1fad40c54 --- /dev/null +++ b/numpy/distutils/tests/test_ccompiler_opt_conf.py @@ -0,0 +1,176 @@ +import unittest +from os import sys, path + +is_standalone = __name__ == '__main__' and __package__ is None +if is_standalone: + sys.path.append(path.abspath(path.join(path.dirname(__file__), ".."))) + from ccompiler_opt import CCompilerOpt +else: + from numpy.distutils.ccompiler_opt import CCompilerOpt + +arch_compilers = dict( + x86 = ("gcc", "clang", "icc", "iccw", "msvc"), + x64 = ("gcc", "clang", "icc", "iccw", "msvc"), + ppc64 = ("gcc", "clang"), + ppc64le = ("gcc", "clang"), + armhf = ("gcc", "clang"), + aarch64 = ("gcc", "clang"), + narch = ("gcc",) +) + +class FakeCCompilerOpt(CCompilerOpt): + fake_info = ("arch", "compiler", "extra_args") + def __init__(self, *args, **kwargs): + CCompilerOpt.__init__(self, None, **kwargs) + def dist_compile(self, sources, flags, **kwargs): + return sources + def dist_info(self): + return FakeCCompilerOpt.fake_info + @staticmethod + def dist_log(*args, stderr=False): + pass + +class _TestConfFeatures(FakeCCompilerOpt): + """A hook to check the sanity of configured features +- before it called by the abstract class '_Feature' + """ + + def conf_features_partial(self): + conf_all = self.conf_features + for feature_name, feature in conf_all.items(): + self.test_feature( + "attribute conf_features", + conf_all, feature_name, feature + ) + + conf_partial = FakeCCompilerOpt.conf_features_partial(self) + for feature_name, feature in conf_partial.items(): + self.test_feature( + "conf_features_partial()", + conf_partial, feature_name, feature + ) + return conf_partial + + def test_feature(self, log, search_in, feature_name, feature_dict): + error_msg = ( + "during validate '{}' within feature '{}', " + "march '{}' and compiler '{}'\n>> " + ).format(log, feature_name, self.cc_march, self.cc_name) + + if not feature_name.isupper(): + raise AssertionError(error_msg + "feature name must be in uppercase") + + for option, val in feature_dict.items(): + self.test_option_types(error_msg, option, val) + self.test_duplicates(error_msg, option, val) + + self.test_implies(error_msg, search_in, feature_name, feature_dict) + self.test_group(error_msg, search_in, feature_name, feature_dict) + self.test_extra_checks(error_msg, search_in, feature_name, feature_dict) + + def test_option_types(self, error_msg, option, val): + for tp, available in ( + ((str, list), ( + "implies", "headers", "flags", "group", "detect", "extra_checks" + )), + ((str,), ("disable",)), + ((int,), ("interest",)), + ((bool,), ("implies_detect",)), + ((bool, type(None)), ("autovec",)), + ) : + found_it = option in available + if not found_it: + continue + if not isinstance(val, tp): + error_tp = [t.__name__ for t in (*tp,)] + error_tp = ' or '.join(error_tp) + raise AssertionError(error_msg + + "expected '%s' type for option '%s' not '%s'" % ( + error_tp, option, type(val).__name__ + )) + break + + if not found_it: + raise AssertionError(error_msg + "invalid option name '%s'" % option) + + def test_duplicates(self, error_msg, option, val): + if option not in ( + "implies", "headers", "flags", "group", "detect", "extra_checks" + ) : return + + if isinstance(val, str): + val = val.split() + + if len(val) != len(set(val)): + raise AssertionError(error_msg + "duplicated values in option '%s'" % option) + + def test_implies(self, error_msg, search_in, feature_name, feature_dict): + if feature_dict.get("disabled") is not None: + return + implies = feature_dict.get("implies", "") + if not implies: + return + if isinstance(implies, str): + implies = implies.split() + + if feature_name in implies: + raise AssertionError(error_msg + "feature implies itself") + + for impl in implies: + impl_dict = search_in.get(impl) + if impl_dict is not None: + if "disable" in impl_dict: + raise AssertionError(error_msg + "implies disabled feature '%s'" % impl) + continue + raise AssertionError(error_msg + "implies non-exist feature '%s'" % impl) + + def test_group(self, error_msg, search_in, feature_name, feature_dict): + if feature_dict.get("disabled") is not None: + return + group = feature_dict.get("group", "") + if not group: + return + if isinstance(group, str): + group = group.split() + + for f in group: + impl_dict = search_in.get(f) + if not impl_dict or "disable" in impl_dict: + continue + raise AssertionError(error_msg + + "in option 'group', '%s' already exists as a feature name" % f + ) + + def test_extra_checks(self, error_msg, search_in, feature_name, feature_dict): + if feature_dict.get("disabled") is not None: + return + extra_checks = feature_dict.get("extra_checks", "") + if not extra_checks: + return + if isinstance(extra_checks, str): + extra_checks = extra_checks.split() + + for f in extra_checks: + impl_dict = search_in.get(f) + if not impl_dict or "disable" in impl_dict: + continue + raise AssertionError(error_msg + + "in option 'extra_checks', extra test case '%s' already exists as a feature name" % f + ) + +class TestConfFeatures(unittest.TestCase): + def __init__(self, methodName="runTest"): + unittest.TestCase.__init__(self, methodName) + self.setup() + + def setup(self): + FakeCCompilerOpt.conf_nocache = True + + def test_features(self): + for arch, compilers in arch_compilers.items(): + for cc in compilers: + FakeCCompilerOpt.fake_info = (arch, cc, "") + _TestConfFeatures() + +if is_standalone: + unittest.main() diff --git a/numpy/distutils/tests/test_exec_command.py b/numpy/distutils/tests/test_exec_command.py index 8bd26500748b..d6eb7d1c3f27 100644 --- a/numpy/distutils/tests/test_exec_command.py +++ b/numpy/distutils/tests/test_exec_command.py @@ -1,21 +1,16 @@ -from __future__ import division, absolute_import, print_function - import os import sys from tempfile import TemporaryFile from numpy.distutils import exec_command from numpy.distutils.exec_command import get_pythonexe -from numpy.testing import tempdir, assert_ +from numpy.testing import tempdir, assert_, assert_warns # In python 3 stdout, stderr are text (unicode compliant) devices, so to # emulate them import StringIO from the io module. -if sys.version_info[0] >= 3: - from io import StringIO -else: - from StringIO import StringIO +from io import StringIO -class redirect_stdout(object): +class redirect_stdout: """Context manager to redirect stdout for exec_command test.""" def __init__(self, stdout=None): self._stdout = stdout or sys.stdout @@ -30,7 +25,7 @@ def __exit__(self, exc_type, exc_value, traceback): # note: closing sys.stdout won't close it. self._stdout.close() -class redirect_stderr(object): +class redirect_stderr: """Context manager to redirect stderr for exec_command test.""" def __init__(self, stderr=None): self._stderr = stderr or sys.stderr @@ -45,7 +40,7 @@ def __exit__(self, exc_type, exc_value, traceback): # note: closing sys.stderr won't close it. self._stderr.close() -class emulate_nonposix(object): +class emulate_nonposix: """Context manager to emulate os.name != 'posix' """ def __init__(self, osname='non-posix'): self._new_name = osname @@ -71,30 +66,34 @@ def test_exec_command_stdout(): # Test posix version: with redirect_stdout(StringIO()): with redirect_stderr(TemporaryFile()): - exec_command.exec_command("cd '.'") + with assert_warns(DeprecationWarning): + exec_command.exec_command("cd '.'") if os.name == 'posix': # Test general (non-posix) version: with emulate_nonposix(): with redirect_stdout(StringIO()): with redirect_stderr(TemporaryFile()): - exec_command.exec_command("cd '.'") + with assert_warns(DeprecationWarning): + exec_command.exec_command("cd '.'") def test_exec_command_stderr(): # Test posix version: with redirect_stdout(TemporaryFile(mode='w+')): with redirect_stderr(StringIO()): - exec_command.exec_command("cd '.'") + with assert_warns(DeprecationWarning): + exec_command.exec_command("cd '.'") if os.name == 'posix': # Test general (non-posix) version: with emulate_nonposix(): with redirect_stdout(TemporaryFile()): with redirect_stderr(StringIO()): - exec_command.exec_command("cd '.'") + with assert_warns(DeprecationWarning): + exec_command.exec_command("cd '.'") -class TestExecCommand(object): +class TestExecCommand: def setup(self): self.pyexe = get_pythonexe() @@ -187,9 +186,8 @@ def check_execute_in(self, **kws): with tempdir() as tmpdir: fn = "file" tmpfile = os.path.join(tmpdir, fn) - f = open(tmpfile, 'w') - f.write('Hello') - f.close() + with open(tmpfile, 'w') as f: + f.write('Hello') s, o = exec_command.exec_command( '"%s" -c "f = open(\'%s\', \'r\'); f.close()"' % @@ -205,11 +203,12 @@ def check_execute_in(self, **kws): def test_basic(self): with redirect_stdout(StringIO()): with redirect_stderr(StringIO()): - if os.name == "posix": - self.check_posix(use_tee=0) - self.check_posix(use_tee=1) - elif os.name == "nt": - self.check_nt(use_tee=0) - self.check_nt(use_tee=1) - self.check_execute_in(use_tee=0) - self.check_execute_in(use_tee=1) + with assert_warns(DeprecationWarning): + if os.name == "posix": + self.check_posix(use_tee=0) + self.check_posix(use_tee=1) + elif os.name == "nt": + self.check_nt(use_tee=0) + self.check_nt(use_tee=1) + self.check_execute_in(use_tee=0) + self.check_execute_in(use_tee=1) diff --git a/numpy/distutils/tests/test_fcompiler.py b/numpy/distutils/tests/test_fcompiler.py new file mode 100644 index 000000000000..dd97f1e72afc --- /dev/null +++ b/numpy/distutils/tests/test_fcompiler.py @@ -0,0 +1,43 @@ +from numpy.testing import assert_ +import numpy.distutils.fcompiler + +customizable_flags = [ + ('f77', 'F77FLAGS'), + ('f90', 'F90FLAGS'), + ('free', 'FREEFLAGS'), + ('arch', 'FARCH'), + ('debug', 'FDEBUG'), + ('flags', 'FFLAGS'), + ('linker_so', 'LDFLAGS'), +] + + +def test_fcompiler_flags(monkeypatch): + monkeypatch.setenv('NPY_DISTUTILS_APPEND_FLAGS', '0') + fc = numpy.distutils.fcompiler.new_fcompiler(compiler='none') + flag_vars = fc.flag_vars.clone(lambda *args, **kwargs: None) + + for opt, envvar in customizable_flags: + new_flag = '-dummy-{}-flag'.format(opt) + prev_flags = getattr(flag_vars, opt) + + monkeypatch.setenv(envvar, new_flag) + new_flags = getattr(flag_vars, opt) + + monkeypatch.delenv(envvar) + assert_(new_flags == [new_flag]) + + monkeypatch.setenv('NPY_DISTUTILS_APPEND_FLAGS', '1') + + for opt, envvar in customizable_flags: + new_flag = '-dummy-{}-flag'.format(opt) + prev_flags = getattr(flag_vars, opt) + monkeypatch.setenv(envvar, new_flag) + new_flags = getattr(flag_vars, opt) + + monkeypatch.delenv(envvar) + if prev_flags is None: + assert_(new_flags == [new_flag]) + else: + assert_(new_flags == prev_flags + [new_flag]) + diff --git a/numpy/distutils/tests/test_fcompiler_gnu.py b/numpy/distutils/tests/test_fcompiler_gnu.py index 49208aaced34..0817ae58c214 100644 --- a/numpy/distutils/tests/test_fcompiler_gnu.py +++ b/numpy/distutils/tests/test_fcompiler_gnu.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - from numpy.testing import assert_ import numpy.distutils.fcompiler @@ -30,7 +28,7 @@ ('GNU Fortran (crosstool-NG 8a21ab48) 7.2.0', '7.2.0') ] -class TestG77Versions(object): +class TestG77Versions: def test_g77_version(self): fc = numpy.distutils.fcompiler.new_fcompiler(compiler='gnu') for vs, version in g77_version_strings: @@ -43,7 +41,7 @@ def test_not_g77(self): v = fc.version_match(vs) assert_(v is None, (vs, v)) -class TestGFortranVersions(object): +class TestGFortranVersions: def test_gfortran_version(self): fc = numpy.distutils.fcompiler.new_fcompiler(compiler='gnu95') for vs, version in gfortran_version_strings: diff --git a/numpy/distutils/tests/test_fcompiler_intel.py b/numpy/distutils/tests/test_fcompiler_intel.py index 5e014bada34f..45c9cdac1910 100644 --- a/numpy/distutils/tests/test_fcompiler_intel.py +++ b/numpy/distutils/tests/test_fcompiler_intel.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - import numpy.distutils.fcompiler from numpy.testing import assert_ @@ -16,7 +14,7 @@ "running on Intel(R) 64, Version 11.1", '11.1') ] -class TestIntelFCompilerVersions(object): +class TestIntelFCompilerVersions: def test_32bit_version(self): fc = numpy.distutils.fcompiler.new_fcompiler(compiler='intel') for vs, version in intel_32bit_version_strings: @@ -24,7 +22,7 @@ def test_32bit_version(self): assert_(v == version) -class TestIntelEM64TFCompilerVersions(object): +class TestIntelEM64TFCompilerVersions: def test_64bit_version(self): fc = numpy.distutils.fcompiler.new_fcompiler(compiler='intelem') for vs, version in intel_64bit_version_strings: diff --git a/numpy/distutils/tests/test_fcompiler_nagfor.py b/numpy/distutils/tests/test_fcompiler_nagfor.py index 1c936056a89b..2e04f5266dc1 100644 --- a/numpy/distutils/tests/test_fcompiler_nagfor.py +++ b/numpy/distutils/tests/test_fcompiler_nagfor.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - from numpy.testing import assert_ import numpy.distutils.fcompiler @@ -16,7 +14,7 @@ '431,435,437,446,459-460,463,472,494,496,503,508,' '511,517,529,555,557,565)', '5.1')] -class TestNagFCompilerVersions(object): +class TestNagFCompilerVersions: def test_version_match(self): for comp, vs, version in nag_version_strings: fc = numpy.distutils.fcompiler.new_fcompiler(compiler=comp) diff --git a/numpy/distutils/tests/test_log.py b/numpy/distutils/tests/test_log.py new file mode 100644 index 000000000000..36f49f592c39 --- /dev/null +++ b/numpy/distutils/tests/test_log.py @@ -0,0 +1,32 @@ +import io +import re +from contextlib import redirect_stdout + +import pytest + +from numpy.distutils import log + + +def setup_module(): + log.set_verbosity(2, force=True) # i.e. DEBUG + + +def teardown_module(): + log.set_verbosity(0, force=True) # the default + + +r_ansi = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])") + + +@pytest.mark.parametrize("func_name", ["error", "warn", "info", "debug"]) +def test_log_prefix(func_name): + func = getattr(log, func_name) + msg = f"{func_name} message" + f = io.StringIO() + with redirect_stdout(f): + func(msg) + out = f.getvalue() + assert out # sanity check + clean_out = r_ansi.sub("", out) + line = next(line for line in clean_out.splitlines()) + assert line == f"{func_name.upper()}: {msg}" diff --git a/numpy/distutils/tests/test_mingw32ccompiler.py b/numpy/distutils/tests/test_mingw32ccompiler.py new file mode 100644 index 000000000000..ebedacb32448 --- /dev/null +++ b/numpy/distutils/tests/test_mingw32ccompiler.py @@ -0,0 +1,42 @@ +import shutil +import subprocess +import sys +import pytest + +from numpy.distutils import mingw32ccompiler + + +@pytest.mark.skipif(sys.platform != 'win32', reason='win32 only test') +def test_build_import(): + '''Test the mingw32ccompiler.build_import_library, which builds a + `python.a` from the MSVC `python.lib` + ''' + + # make sure `nm.exe` exists and supports the current python version. This + # can get mixed up when the PATH has a 64-bit nm but the python is 32-bit + try: + out = subprocess.check_output(['nm.exe', '--help']) + except FileNotFoundError: + pytest.skip("'nm.exe' not on path, is mingw installed?") + supported = out[out.find(b'supported targets:'):] + if sys.maxsize < 2**32: + if b'pe-i386' not in supported: + raise ValueError("'nm.exe' found but it does not support 32-bit " + "dlls when using 32-bit python. Supported " + "formats: '%s'" % supported) + elif b'pe-x86-64' not in supported: + raise ValueError("'nm.exe' found but it does not support 64-bit " + "dlls when using 64-bit python. Supported " + "formats: '%s'" % supported) + # Hide the import library to force a build + has_import_lib, fullpath = mingw32ccompiler._check_for_import_lib() + if has_import_lib: + shutil.move(fullpath, fullpath + '.bak') + + try: + # Whew, now we can actually test the function + mingw32ccompiler.build_import_library() + + finally: + if has_import_lib: + shutil.move(fullpath + '.bak', fullpath) diff --git a/numpy/distutils/tests/test_misc_util.py b/numpy/distutils/tests/test_misc_util.py index 2a3294ddfb95..605c80483b77 100644 --- a/numpy/distutils/tests/test_misc_util.py +++ b/numpy/distutils/tests/test_misc_util.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - from os.path import join, sep, dirname from numpy.distutils.misc_util import ( @@ -11,7 +9,7 @@ ajoin = lambda *paths: join(*((sep,)+paths)) -class TestAppendpath(object): +class TestAppendpath: def test_1(self): assert_equal(appendpath('prefix', 'name'), join('prefix', 'name')) @@ -35,7 +33,7 @@ def test_3(self): assert_equal(appendpath('/prefix/sub/sub2', '/prefix/sub/sup/name'), ajoin('prefix', 'sub', 'sub2', 'sup', 'name')) -class TestMinrelpath(object): +class TestMinrelpath: def test_1(self): n = lambda path: path.replace('/', sep) @@ -49,7 +47,7 @@ def test_1(self): assert_equal(minrelpath(n('.././..')), n('../..')) assert_equal(minrelpath(n('aa/bb/.././../dd')), n('dd')) -class TestGpaths(object): +class TestGpaths: def test_gpaths(self): local_path = minrelpath(join(dirname(__file__), '..')) @@ -58,7 +56,7 @@ def test_gpaths(self): f = gpaths('system_info.py', local_path) assert_(join(local_path, 'system_info.py') == f[0], repr(f)) -class TestSharedExtension(object): +class TestSharedExtension: def test_get_shared_lib_extension(self): import sys @@ -79,3 +77,6 @@ def test_installed_npymath_ini(): # Regression test for gh-7707. If npymath.ini wasn't installed, then this # will give an error. info = get_info('npymath') + + assert isinstance(info, dict) + assert "define_macros" in info diff --git a/numpy/distutils/tests/test_npy_pkg_config.py b/numpy/distutils/tests/test_npy_pkg_config.py index 537e16e90d2a..b287ebe2e832 100644 --- a/numpy/distutils/tests/test_npy_pkg_config.py +++ b/numpy/distutils/tests/test_npy_pkg_config.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - import os from numpy.distutils.npy_pkg_config import read_config, parse_flags @@ -36,7 +34,7 @@ simple_variable_d = {'cflags': '-I/foo/bar/include', 'libflags': '-L/foo/bar/lib', 'version': '0.1', 'name': 'foo'} -class TestLibraryInfo(object): +class TestLibraryInfo: def test_simple(self): with temppath('foo.ini') as path: with open(path, 'w') as f: @@ -63,7 +61,7 @@ def test_simple_variable(self): out.vars['prefix'] = '/Users/david' assert_(out.cflags() == '-I/Users/david/include') -class TestParseFlags(object): +class TestParseFlags: def test_simple_cflags(self): d = parse_flags("-I/usr/include") assert_(d['include_dirs'] == ['/usr/include']) diff --git a/numpy/distutils/tests/test_shell_utils.py b/numpy/distutils/tests/test_shell_utils.py new file mode 100644 index 000000000000..32bd283e56b6 --- /dev/null +++ b/numpy/distutils/tests/test_shell_utils.py @@ -0,0 +1,76 @@ +import pytest +import subprocess +import json +import sys + +from numpy.distutils import _shell_utils + +argv_cases = [ + [r'exe'], + [r'path/exe'], + [r'path\exe'], + [r'\\server\path\exe'], + [r'path to/exe'], + [r'path to\exe'], + + [r'exe', '--flag'], + [r'path/exe', '--flag'], + [r'path\exe', '--flag'], + [r'path to/exe', '--flag'], + [r'path to\exe', '--flag'], + + # flags containing literal quotes in their name + [r'path to/exe', '--flag-"quoted"'], + [r'path to\exe', '--flag-"quoted"'], + [r'path to/exe', '"--flag-quoted"'], + [r'path to\exe', '"--flag-quoted"'], +] + + +@pytest.fixture(params=[ + _shell_utils.WindowsParser, + _shell_utils.PosixParser +]) +def Parser(request): + return request.param + + +@pytest.fixture +def runner(Parser): + if Parser != _shell_utils.NativeParser: + pytest.skip('Unable to run with non-native parser') + + if Parser == _shell_utils.WindowsParser: + return lambda cmd: subprocess.check_output(cmd) + elif Parser == _shell_utils.PosixParser: + # posix has no non-shell string parsing + return lambda cmd: subprocess.check_output(cmd, shell=True) + else: + raise NotImplementedError + + +@pytest.mark.parametrize('argv', argv_cases) +def test_join_matches_subprocess(Parser, runner, argv): + """ + Test that join produces strings understood by subprocess + """ + # invoke python to return its arguments as json + cmd = [ + sys.executable, '-c', + 'import json, sys; print(json.dumps(sys.argv[1:]))' + ] + joined = Parser.join(cmd + argv) + json_out = runner(joined).decode() + assert json.loads(json_out) == argv + + +@pytest.mark.parametrize('argv', argv_cases) +def test_roundtrip(Parser, argv): + """ + Test that split is the inverse operation of join + """ + try: + joined = Parser.join(argv) + assert argv == Parser.split(joined) + except NotImplementedError: + pytest.skip("Not implemented") diff --git a/numpy/distutils/tests/test_system_info.py b/numpy/distutils/tests/test_system_info.py index 4aec13c82770..8c26271af782 100644 --- a/numpy/distutils/tests/test_system_info.py +++ b/numpy/distutils/tests/test_system_info.py @@ -1,5 +1,3 @@ -from __future__ import division, print_function - import os import shutil import pytest @@ -7,10 +5,12 @@ from subprocess import Popen, PIPE from distutils.errors import DistutilsError +from numpy.testing import assert_, assert_equal, assert_raises from numpy.distutils import ccompiler, customized_ccompiler -from numpy.testing import assert_, assert_equal -from numpy.distutils.system_info import system_info, ConfigParser +from numpy.distutils.system_info import system_info, ConfigParser, mkl_info +from numpy.distutils.system_info import AliasedOptionError from numpy.distutils.system_info import default_lib_dirs, default_include_dirs +from numpy.distutils import _shell_utils def get_class(name, notfound_action=1): @@ -21,7 +21,8 @@ def get_class(name, notfound_action=1): 2 - raise error """ cl = {'temp1': Temp1Info, - 'temp2': Temp2Info + 'temp2': Temp2Info, + 'duplicate_options': DuplicateOptionInfo, }.get(name.lower(), _system_info) return cl() @@ -29,7 +30,7 @@ def get_class(name, notfound_action=1): [ALL] library_dirs = {dir1:s}{pathsep:s}{dir2:s} libraries = {lib1:s},{lib2:s} -extra_compile_args = -I/fake/directory +extra_compile_args = -I/fake/directory -I"/path with/spaces" -Os runtime_library_dirs = {dir1:s} [temp1] @@ -40,8 +41,12 @@ def get_class(name, notfound_action=1): [temp2] library_dirs = {dir2:s} libraries = {lib2:s} -extra_link_args = -Wl,-rpath={lib2:s} +extra_link_args = -Wl,-rpath={lib2_escaped:s} rpath = {dir2:s} + +[duplicate_options] +mylib_libs = {lib1:s} +libraries = {lib2:s} """ site_cfg = simple_site @@ -118,8 +123,12 @@ class Temp2Info(_system_info): """For testing purposes""" section = 'temp2' +class DuplicateOptionInfo(_system_info): + """For testing purposes""" + section = 'duplicate_options' + -class TestSystemInfoReading(object): +class TestSystemInfoReading: def setup(self): """ Create the libraries """ @@ -137,7 +146,8 @@ def setup(self): 'lib1': self._lib1, 'dir2': self._dir2, 'lib2': self._lib2, - 'pathsep': os.pathsep + 'pathsep': os.pathsep, + 'lib2_escaped': _shell_utils.NativeParser.join([self._lib2]) }) # Write site.cfg fd, self._sitecfg = mkstemp() @@ -158,6 +168,9 @@ def site_and_parse(c, site_cfg): self.c_default = site_and_parse(get_class('default'), self._sitecfg) self.c_temp1 = site_and_parse(get_class('temp1'), self._sitecfg) self.c_temp2 = site_and_parse(get_class('temp2'), self._sitecfg) + self.c_dup_options = site_and_parse(get_class('duplicate_options'), + self._sitecfg) + def teardown(self): # Do each removal separately @@ -181,7 +194,7 @@ def test_all(self): assert_equal(tsi.get_libraries(), [self._lib1, self._lib2]) assert_equal(tsi.get_runtime_lib_dirs(), [self._dir1]) extra = tsi.calc_extra_info() - assert_equal(extra['extra_compile_args'], ['-I/fake/directory']) + assert_equal(extra['extra_compile_args'], ['-I/fake/directory', '-I/path with/spaces', '-Os']) def test_temp1(self): # Read in all information in the temp1 block @@ -200,6 +213,13 @@ def test_temp2(self): extra = tsi.calc_extra_info() assert_equal(extra['extra_link_args'], ['-Wl,-rpath=' + self._lib2]) + def test_duplicate_options(self): + # Ensure that duplicates are raising an AliasedOptionError + tsi = self.c_dup_options + assert_raises(AliasedOptionError, tsi.get_option_single, "mylib_libs", "libraries") + assert_equal(tsi.get_libs("mylib_libs", [self._lib1]), [self._lib1]) + assert_equal(tsi.get_libs("libraries", [self._lib2]), [self._lib2]) + @pytest.mark.skipif(not HAVE_COMPILER, reason="Missing compiler") def test_compile1(self): # Compile source and link the first source @@ -233,3 +253,72 @@ def test_compile2(self): assert_(os.path.isfile(self._src2.replace('.c', '.o'))) finally: os.chdir(previousDir) + + HAS_MKL = "mkl_rt" in mkl_info().calc_libraries_info().get("libraries", []) + + @pytest.mark.xfail(HAS_MKL, reason=("`[DEFAULT]` override doesn't work if " + "numpy is built with MKL support")) + def test_overrides(self): + previousDir = os.getcwd() + cfg = os.path.join(self._dir1, 'site.cfg') + shutil.copy(self._sitecfg, cfg) + try: + os.chdir(self._dir1) + # Check that the '[ALL]' section does not override + # missing values from other sections + info = mkl_info() + lib_dirs = info.cp['ALL']['library_dirs'].split(os.pathsep) + assert info.get_lib_dirs() != lib_dirs + + # But if we copy the values to a '[mkl]' section the value + # is correct + with open(cfg, 'r') as fid: + mkl = fid.read().replace('[ALL]', '[mkl]', 1) + with open(cfg, 'w') as fid: + fid.write(mkl) + info = mkl_info() + assert info.get_lib_dirs() == lib_dirs + + # Also, the values will be taken from a section named '[DEFAULT]' + with open(cfg, 'r') as fid: + dflt = fid.read().replace('[mkl]', '[DEFAULT]', 1) + with open(cfg, 'w') as fid: + fid.write(dflt) + info = mkl_info() + assert info.get_lib_dirs() == lib_dirs + finally: + os.chdir(previousDir) + + +def test_distutils_parse_env_order(monkeypatch): + from numpy.distutils.system_info import _parse_env_order + env = 'NPY_TESTS_DISTUTILS_PARSE_ENV_ORDER' + + base_order = list('abcdef') + + monkeypatch.setenv(env, 'b,i,e,f') + order, unknown = _parse_env_order(base_order, env) + assert len(order) == 3 + assert order == list('bef') + assert len(unknown) == 1 + + # For when LAPACK/BLAS optimization is disabled + monkeypatch.setenv(env, '') + order, unknown = _parse_env_order(base_order, env) + assert len(order) == 0 + assert len(unknown) == 0 + + for prefix in '^!': + monkeypatch.setenv(env, f'{prefix}b,i,e') + order, unknown = _parse_env_order(base_order, env) + assert len(order) == 4 + assert order == list('acdf') + assert len(unknown) == 1 + + with pytest.raises(ValueError): + monkeypatch.setenv(env, 'b,^e,i') + _parse_env_order(base_order, env) + + with pytest.raises(ValueError): + monkeypatch.setenv(env, '!b,^e,i') + _parse_env_order(base_order, env) diff --git a/numpy/distutils/unixccompiler.py b/numpy/distutils/unixccompiler.py index 11b2cce529af..4884960fdf22 100644 --- a/numpy/distutils/unixccompiler.py +++ b/numpy/distutils/unixccompiler.py @@ -2,20 +2,16 @@ unixccompiler - can handle very long argument lists for ar. """ -from __future__ import division, absolute_import, print_function - import os +import sys +import subprocess +import shlex -from distutils.errors import DistutilsExecError, CompileError -from distutils.unixccompiler import * +from distutils.errors import CompileError, DistutilsExecError, LibError +from distutils.unixccompiler import UnixCCompiler from numpy.distutils.ccompiler import replace_method -from numpy.distutils.compat import get_exception from numpy.distutils.misc_util import _commandline_dep_string - -if sys.version_info[0] < 3: - from . import log -else: - from numpy.distutils import log +from numpy.distutils import log # Note that UnixCCompiler._compile appeared in Python 2.3 def UnixCCompiler__compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): @@ -33,16 +29,17 @@ def UnixCCompiler__compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts self.compiler_so = ccomp # ensure OPT environment variable is read if 'OPT' in os.environ: - from distutils.sysconfig import get_config_vars - opt = " ".join(os.environ['OPT'].split()) - gcv_opt = " ".join(get_config_vars('OPT')[0].split()) - ccomp_s = " ".join(self.compiler_so) + # XXX who uses this? + from sysconfig import get_config_vars + opt = shlex.join(shlex.split(os.environ['OPT'])) + gcv_opt = shlex.join(shlex.split(get_config_vars('OPT')[0])) + ccomp_s = shlex.join(self.compiler_so) if opt not in ccomp_s: ccomp_s = ccomp_s.replace(gcv_opt, opt) - self.compiler_so = ccomp_s.split() - llink_s = " ".join(self.linker_so) + self.compiler_so = shlex.split(ccomp_s) + llink_s = shlex.join(self.linker_so) if opt not in llink_s: - self.linker_so = llink_s.split() + opt.split() + self.linker_so = self.linker_so + shlex.split(opt) display = '%s: %s' % (os.path.basename(self.compiler_so[0]), src) @@ -56,12 +53,17 @@ def UnixCCompiler__compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts try: self.spawn(self.compiler_so + cc_args + [src, '-o', obj] + deps + extra_postargs, display = display) - except DistutilsExecError: - msg = str(get_exception()) - raise CompileError(msg) + except DistutilsExecError as e: + msg = str(e) + raise CompileError(msg) from None # add commandline flags to dependency file if deps: + # After running the compiler, the file created will be in EBCDIC + # but will not be tagged as such. This tags it so the file does not + # have multiple different encodings being written to it + if sys.platform == 'zos': + subprocess.check_output(['chtag', '-tc', 'IBM1047', obj + '.d']) with open(obj + '.d', 'a') as f: f.write(_commandline_dep_string(cc_args, extra_postargs, pp_opts)) @@ -104,7 +106,7 @@ def UnixCCompiler_create_static_lib(self, objects, output_libname, # and recreate. # Also, ar on OS X doesn't handle updating universal archives os.unlink(output_filename) - except (IOError, OSError): + except OSError: pass self.mkpath(os.path.dirname(output_filename)) tmp_objects = objects + self.objects @@ -128,9 +130,9 @@ def UnixCCompiler_create_static_lib(self, objects, output_libname, try: self.spawn(self.ranlib + [output_filename], display = display) - except DistutilsExecError: - msg = str(get_exception()) - raise LibError(msg) + except DistutilsExecError as e: + msg = str(e) + raise LibError(msg) from None else: log.debug("skipping %s (up-to-date)", output_filename) return diff --git a/numpy/doc/__init__.py b/numpy/doc/__init__.py index b6f1fa71c54a..8a944fecd865 100644 --- a/numpy/doc/__init__.py +++ b/numpy/doc/__init__.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - import os ref_dir = os.path.join(os.path.dirname(__file__)) diff --git a/numpy/doc/basics.py b/numpy/doc/basics.py deleted file mode 100644 index 4d3ab046ecd7..000000000000 --- a/numpy/doc/basics.py +++ /dev/null @@ -1,185 +0,0 @@ -""" -============ -Array basics -============ - -Array types and conversions between types -========================================= - -NumPy supports a much greater variety of numerical types than Python does. -This section shows which are available, and how to modify an array's data-type. - -============ ========================================================== -Data type Description -============ ========================================================== -``bool_`` Boolean (True or False) stored as a byte -``int_`` Default integer type (same as C ``long``; normally either - ``int64`` or ``int32``) -intc Identical to C ``int`` (normally ``int32`` or ``int64``) -intp Integer used for indexing (same as C ``ssize_t``; normally - either ``int32`` or ``int64``) -int8 Byte (-128 to 127) -int16 Integer (-32768 to 32767) -int32 Integer (-2147483648 to 2147483647) -int64 Integer (-9223372036854775808 to 9223372036854775807) -uint8 Unsigned integer (0 to 255) -uint16 Unsigned integer (0 to 65535) -uint32 Unsigned integer (0 to 4294967295) -uint64 Unsigned integer (0 to 18446744073709551615) -``float_`` Shorthand for ``float64``. -float16 Half precision float: sign bit, 5 bits exponent, - 10 bits mantissa -float32 Single precision float: sign bit, 8 bits exponent, - 23 bits mantissa -float64 Double precision float: sign bit, 11 bits exponent, - 52 bits mantissa -``complex_`` Shorthand for ``complex128``. -complex64 Complex number, represented by two 32-bit floats (real - and imaginary components) -complex128 Complex number, represented by two 64-bit floats (real - and imaginary components) -============ ========================================================== - -Additionally to ``intc`` the platform dependent C integer types ``short``, -``long``, ``longlong`` and their unsigned versions are defined. - -NumPy numerical types are instances of ``dtype`` (data-type) objects, each -having unique characteristics. Once you have imported NumPy using - - :: - - >>> import numpy as np - -the dtypes are available as ``np.bool_``, ``np.float32``, etc. - -Advanced types, not listed in the table above, are explored in -section :ref:`structured_arrays`. - -There are 5 basic numerical types representing booleans (bool), integers (int), -unsigned integers (uint) floating point (float) and complex. Those with numbers -in their name indicate the bitsize of the type (i.e. how many bits are needed -to represent a single value in memory). Some types, such as ``int`` and -``intp``, have differing bitsizes, dependent on the platforms (e.g. 32-bit -vs. 64-bit machines). This should be taken into account when interfacing -with low-level code (such as C or Fortran) where the raw memory is addressed. - -Data-types can be used as functions to convert python numbers to array scalars -(see the array scalar section for an explanation), python sequences of numbers -to arrays of that type, or as arguments to the dtype keyword that many numpy -functions or methods accept. Some examples:: - - >>> import numpy as np - >>> x = np.float32(1.0) - >>> x - 1.0 - >>> y = np.int_([1,2,4]) - >>> y - array([1, 2, 4]) - >>> z = np.arange(3, dtype=np.uint8) - >>> z - array([0, 1, 2], dtype=uint8) - -Array types can also be referred to by character codes, mostly to retain -backward compatibility with older packages such as Numeric. Some -documentation may still refer to these, for example:: - - >>> np.array([1, 2, 3], dtype='f') - array([ 1., 2., 3.], dtype=float32) - -We recommend using dtype objects instead. - -To convert the type of an array, use the .astype() method (preferred) or -the type itself as a function. For example: :: - - >>> z.astype(float) #doctest: +NORMALIZE_WHITESPACE - array([ 0., 1., 2.]) - >>> np.int8(z) - array([0, 1, 2], dtype=int8) - -Note that, above, we use the *Python* float object as a dtype. NumPy knows -that ``int`` refers to ``np.int_``, ``bool`` means ``np.bool_``, -that ``float`` is ``np.float_`` and ``complex`` is ``np.complex_``. -The other data-types do not have Python equivalents. - -To determine the type of an array, look at the dtype attribute:: - - >>> z.dtype - dtype('uint8') - -dtype objects also contain information about the type, such as its bit-width -and its byte-order. The data type can also be used indirectly to query -properties of the type, such as whether it is an integer:: - - >>> d = np.dtype(int) - >>> d - dtype('int32') - - >>> np.issubdtype(d, np.integer) - True - - >>> np.issubdtype(d, np.floating) - False - - -Array Scalars -============= - -NumPy generally returns elements of arrays as array scalars (a scalar -with an associated dtype). Array scalars differ from Python scalars, but -for the most part they can be used interchangeably (the primary -exception is for versions of Python older than v2.x, where integer array -scalars cannot act as indices for lists and tuples). There are some -exceptions, such as when code requires very specific attributes of a scalar -or when it checks specifically whether a value is a Python scalar. Generally, -problems are easily fixed by explicitly converting array scalars -to Python scalars, using the corresponding Python type function -(e.g., ``int``, ``float``, ``complex``, ``str``, ``unicode``). - -The primary advantage of using array scalars is that -they preserve the array type (Python may not have a matching scalar type -available, e.g. ``int16``). Therefore, the use of array scalars ensures -identical behaviour between arrays and scalars, irrespective of whether the -value is inside an array or not. NumPy scalars also have many of the same -methods arrays do. - -Extended Precision -================== - -Python's floating-point numbers are usually 64-bit floating-point numbers, -nearly equivalent to ``np.float64``. In some unusual situations it may be -useful to use floating-point numbers with more precision. Whether this -is possible in numpy depends on the hardware and on the development -environment: specifically, x86 machines provide hardware floating-point -with 80-bit precision, and while most C compilers provide this as their -``long double`` type, MSVC (standard for Windows builds) makes -``long double`` identical to ``double`` (64 bits). NumPy makes the -compiler's ``long double`` available as ``np.longdouble`` (and -``np.clongdouble`` for the complex numbers). You can find out what your -numpy provides with ``np.finfo(np.longdouble)``. - -NumPy does not provide a dtype with more precision than C -``long double``\\s; in particular, the 128-bit IEEE quad precision -data type (FORTRAN's ``REAL*16``\\) is not available. - -For efficient memory alignment, ``np.longdouble`` is usually stored -padded with zero bits, either to 96 or 128 bits. Which is more efficient -depends on hardware and development environment; typically on 32-bit -systems they are padded to 96 bits, while on 64-bit systems they are -typically padded to 128 bits. ``np.longdouble`` is padded to the system -default; ``np.float96`` and ``np.float128`` are provided for users who -want specific padding. In spite of the names, ``np.float96`` and -``np.float128`` provide only as much precision as ``np.longdouble``, -that is, 80 bits on most x86 machines and 64 bits in standard -Windows builds. - -Be warned that even if ``np.longdouble`` offers more precision than -python ``float``, it is easy to lose that extra precision, since -python often forces values to pass through ``float``. For example, -the ``%`` formatting operator requires its arguments to be converted -to standard python types, and it is therefore impossible to preserve -extended precision even if many decimal places are requested. It can -be useful to test your code with the value -``1 + np.finfo(np.longdouble).eps``. - -""" -from __future__ import division, absolute_import, print_function diff --git a/numpy/doc/broadcasting.py b/numpy/doc/broadcasting.py deleted file mode 100644 index 717914cda28c..000000000000 --- a/numpy/doc/broadcasting.py +++ /dev/null @@ -1,178 +0,0 @@ -""" -======================== -Broadcasting over arrays -======================== - -The term broadcasting describes how numpy treats arrays with different -shapes during arithmetic operations. Subject to certain constraints, -the smaller array is "broadcast" across the larger array so that they -have compatible shapes. Broadcasting provides a means of vectorizing -array operations so that looping occurs in C instead of Python. It does -this without making needless copies of data and usually leads to -efficient algorithm implementations. There are, however, cases where -broadcasting is a bad idea because it leads to inefficient use of memory -that slows computation. - -NumPy operations are usually done on pairs of arrays on an -element-by-element basis. In the simplest case, the two arrays must -have exactly the same shape, as in the following example: - - >>> a = np.array([1.0, 2.0, 3.0]) - >>> b = np.array([2.0, 2.0, 2.0]) - >>> a * b - array([ 2., 4., 6.]) - -NumPy's broadcasting rule relaxes this constraint when the arrays' -shapes meet certain constraints. The simplest broadcasting example occurs -when an array and a scalar value are combined in an operation: - ->>> a = np.array([1.0, 2.0, 3.0]) ->>> b = 2.0 ->>> a * b -array([ 2., 4., 6.]) - -The result is equivalent to the previous example where ``b`` was an array. -We can think of the scalar ``b`` being *stretched* during the arithmetic -operation into an array with the same shape as ``a``. The new elements in -``b`` are simply copies of the original scalar. The stretching analogy is -only conceptual. NumPy is smart enough to use the original scalar value -without actually making copies, so that broadcasting operations are as -memory and computationally efficient as possible. - -The code in the second example is more efficient than that in the first -because broadcasting moves less memory around during the multiplication -(``b`` is a scalar rather than an array). - -General Broadcasting Rules -========================== -When operating on two arrays, NumPy compares their shapes element-wise. -It starts with the trailing dimensions, and works its way forward. Two -dimensions are compatible when - -1) they are equal, or -2) one of them is 1 - -If these conditions are not met, a -``ValueError: frames are not aligned`` exception is thrown, indicating that -the arrays have incompatible shapes. The size of the resulting array -is the maximum size along each dimension of the input arrays. - -Arrays do not need to have the same *number* of dimensions. For example, -if you have a ``256x256x3`` array of RGB values, and you want to scale -each color in the image by a different value, you can multiply the image -by a one-dimensional array with 3 values. Lining up the sizes of the -trailing axes of these arrays according to the broadcast rules, shows that -they are compatible:: - - Image (3d array): 256 x 256 x 3 - Scale (1d array): 3 - Result (3d array): 256 x 256 x 3 - -When either of the dimensions compared is one, the other is -used. In other words, dimensions with size 1 are stretched or "copied" -to match the other. - -In the following example, both the ``A`` and ``B`` arrays have axes with -length one that are expanded to a larger size during the broadcast -operation:: - - A (4d array): 8 x 1 x 6 x 1 - B (3d array): 7 x 1 x 5 - Result (4d array): 8 x 7 x 6 x 5 - -Here are some more examples:: - - A (2d array): 5 x 4 - B (1d array): 1 - Result (2d array): 5 x 4 - - A (2d array): 5 x 4 - B (1d array): 4 - Result (2d array): 5 x 4 - - A (3d array): 15 x 3 x 5 - B (3d array): 15 x 1 x 5 - Result (3d array): 15 x 3 x 5 - - A (3d array): 15 x 3 x 5 - B (2d array): 3 x 5 - Result (3d array): 15 x 3 x 5 - - A (3d array): 15 x 3 x 5 - B (2d array): 3 x 1 - Result (3d array): 15 x 3 x 5 - -Here are examples of shapes that do not broadcast:: - - A (1d array): 3 - B (1d array): 4 # trailing dimensions do not match - - A (2d array): 2 x 1 - B (3d array): 8 x 4 x 3 # second from last dimensions mismatched - -An example of broadcasting in practice:: - - >>> x = np.arange(4) - >>> xx = x.reshape(4,1) - >>> y = np.ones(5) - >>> z = np.ones((3,4)) - - >>> x.shape - (4,) - - >>> y.shape - (5,) - - >>> x + y - <type 'exceptions.ValueError'>: shape mismatch: objects cannot be broadcast to a single shape - - >>> xx.shape - (4, 1) - - >>> y.shape - (5,) - - >>> (xx + y).shape - (4, 5) - - >>> xx + y - array([[ 1., 1., 1., 1., 1.], - [ 2., 2., 2., 2., 2.], - [ 3., 3., 3., 3., 3.], - [ 4., 4., 4., 4., 4.]]) - - >>> x.shape - (4,) - - >>> z.shape - (3, 4) - - >>> (x + z).shape - (3, 4) - - >>> x + z - array([[ 1., 2., 3., 4.], - [ 1., 2., 3., 4.], - [ 1., 2., 3., 4.]]) - -Broadcasting provides a convenient way of taking the outer product (or -any other outer operation) of two arrays. The following example shows an -outer addition operation of two 1-d arrays:: - - >>> a = np.array([0.0, 10.0, 20.0, 30.0]) - >>> b = np.array([1.0, 2.0, 3.0]) - >>> a[:, np.newaxis] + b - array([[ 1., 2., 3.], - [ 11., 12., 13.], - [ 21., 22., 23.], - [ 31., 32., 33.]]) - -Here the ``newaxis`` index operator inserts a new axis into ``a``, -making it a two-dimensional ``4x1`` array. Combining the ``4x1`` array -with ``b``, which has shape ``(3,)``, yields a ``4x3`` array. - -See `this article <http://wiki.scipy.org/EricsBroadcastingDoc>`_ -for illustrations of broadcasting concepts. - -""" -from __future__ import division, absolute_import, print_function diff --git a/numpy/doc/byteswapping.py b/numpy/doc/byteswapping.py deleted file mode 100644 index f9491ed432bc..000000000000 --- a/numpy/doc/byteswapping.py +++ /dev/null @@ -1,156 +0,0 @@ -""" - -============================= - Byteswapping and byte order -============================= - -Introduction to byte ordering and ndarrays -========================================== - -The ``ndarray`` is an object that provide a python array interface to data -in memory. - -It often happens that the memory that you want to view with an array is -not of the same byte ordering as the computer on which you are running -Python. - -For example, I might be working on a computer with a little-endian CPU - -such as an Intel Pentium, but I have loaded some data from a file -written by a computer that is big-endian. Let's say I have loaded 4 -bytes from a file written by a Sun (big-endian) computer. I know that -these 4 bytes represent two 16-bit integers. On a big-endian machine, a -two-byte integer is stored with the Most Significant Byte (MSB) first, -and then the Least Significant Byte (LSB). Thus the bytes are, in memory order: - -#. MSB integer 1 -#. LSB integer 1 -#. MSB integer 2 -#. LSB integer 2 - -Let's say the two integers were in fact 1 and 770. Because 770 = 256 * -3 + 2, the 4 bytes in memory would contain respectively: 0, 1, 3, 2. -The bytes I have loaded from the file would have these contents: - ->>> big_end_str = chr(0) + chr(1) + chr(3) + chr(2) ->>> big_end_str -'\\x00\\x01\\x03\\x02' - -We might want to use an ``ndarray`` to access these integers. In that -case, we can create an array around this memory, and tell numpy that -there are two integers, and that they are 16 bit and big-endian: - ->>> import numpy as np ->>> big_end_arr = np.ndarray(shape=(2,),dtype='>i2', buffer=big_end_str) ->>> big_end_arr[0] -1 ->>> big_end_arr[1] -770 - -Note the array ``dtype`` above of ``>i2``. The ``>`` means 'big-endian' -(``<`` is little-endian) and ``i2`` means 'signed 2-byte integer'. For -example, if our data represented a single unsigned 4-byte little-endian -integer, the dtype string would be ``<u4``. - -In fact, why don't we try that? - ->>> little_end_u4 = np.ndarray(shape=(1,),dtype='<u4', buffer=big_end_str) ->>> little_end_u4[0] == 1 * 256**1 + 3 * 256**2 + 2 * 256**3 -True - -Returning to our ``big_end_arr`` - in this case our underlying data is -big-endian (data endianness) and we've set the dtype to match (the dtype -is also big-endian). However, sometimes you need to flip these around. - -.. warning:: - - Scalars currently do not include byte order information, so extracting - a scalar from an array will return an integer in native byte order. - Hence: - - >>> big_end_arr[0].dtype.byteorder == little_end_u4[0].dtype.byteorder - True - -Changing byte ordering -====================== - -As you can imagine from the introduction, there are two ways you can -affect the relationship between the byte ordering of the array and the -underlying memory it is looking at: - -* Change the byte-ordering information in the array dtype so that it - interprets the underlying data as being in a different byte order. - This is the role of ``arr.newbyteorder()`` -* Change the byte-ordering of the underlying data, leaving the dtype - interpretation as it was. This is what ``arr.byteswap()`` does. - -The common situations in which you need to change byte ordering are: - -#. Your data and dtype endianness don't match, and you want to change - the dtype so that it matches the data. -#. Your data and dtype endianness don't match, and you want to swap the - data so that they match the dtype -#. Your data and dtype endianness match, but you want the data swapped - and the dtype to reflect this - -Data and dtype endianness don't match, change dtype to match data ------------------------------------------------------------------ - -We make something where they don't match: - ->>> wrong_end_dtype_arr = np.ndarray(shape=(2,),dtype='<i2', buffer=big_end_str) ->>> wrong_end_dtype_arr[0] -256 - -The obvious fix for this situation is to change the dtype so it gives -the correct endianness: - ->>> fixed_end_dtype_arr = wrong_end_dtype_arr.newbyteorder() ->>> fixed_end_dtype_arr[0] -1 - -Note the array has not changed in memory: - ->>> fixed_end_dtype_arr.tobytes() == big_end_str -True - -Data and type endianness don't match, change data to match dtype ----------------------------------------------------------------- - -You might want to do this if you need the data in memory to be a certain -ordering. For example you might be writing the memory out to a file -that needs a certain byte ordering. - ->>> fixed_end_mem_arr = wrong_end_dtype_arr.byteswap() ->>> fixed_end_mem_arr[0] -1 - -Now the array *has* changed in memory: - ->>> fixed_end_mem_arr.tobytes() == big_end_str -False - -Data and dtype endianness match, swap data and dtype ----------------------------------------------------- - -You may have a correctly specified array dtype, but you need the array -to have the opposite byte order in memory, and you want the dtype to -match so the array values make sense. In this case you just do both of -the previous operations: - ->>> swapped_end_arr = big_end_arr.byteswap().newbyteorder() ->>> swapped_end_arr[0] -1 ->>> swapped_end_arr.tobytes() == big_end_str -False - -An easier way of casting the data to a specific dtype and byte ordering -can be achieved with the ndarray astype method: - ->>> swapped_end_arr = big_end_arr.astype('<i2') ->>> swapped_end_arr[0] -1 ->>> swapped_end_arr.tobytes() == big_end_str -False - -""" -from __future__ import division, absolute_import, print_function diff --git a/numpy/doc/constants.py b/numpy/doc/constants.py index 21c7a3c671b9..4db5c639047f 100644 --- a/numpy/doc/constants.py +++ b/numpy/doc/constants.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ ========= Constants @@ -13,62 +12,59 @@ # # Note: the docstring is autogenerated. # -from __future__ import division, absolute_import, print_function - -import textwrap, re +import re +import textwrap # Maintain same format as in numpy.add_newdocs constants = [] def add_newdoc(module, name, doc): constants.append((name, doc)) -add_newdoc('numpy', 'Inf', +add_newdoc('numpy', 'pi', """ - IEEE 754 floating point representation of (positive) infinity. - - Use `inf` because `Inf`, `Infinity`, `PINF` and `infty` are aliases for - `inf`. For more details, see `inf`. + ``pi = 3.1415926535897932384626433...`` - See Also - -------- - inf + References + ---------- + https://en.wikipedia.org/wiki/Pi """) -add_newdoc('numpy', 'Infinity', +add_newdoc('numpy', 'e', """ - IEEE 754 floating point representation of (positive) infinity. + Euler's constant, base of natural logarithms, Napier's constant. - Use `inf` because `Inf`, `Infinity`, `PINF` and `infty` are aliases for - `inf`. For more details, see `inf`. + ``e = 2.71828182845904523536028747135266249775724709369995...`` See Also -------- - inf + exp : Exponential function + log : Natural logarithm + + References + ---------- + https://en.wikipedia.org/wiki/E_%28mathematical_constant%29 """) -add_newdoc('numpy', 'NAN', +add_newdoc('numpy', 'euler_gamma', """ - IEEE 754 floating point representation of Not a Number (NaN). - - `NaN` and `NAN` are equivalent definitions of `nan`. Please use - `nan` instead of `NAN`. + ``γ = 0.5772156649015328606065120900824024310421...`` - See Also - -------- - nan + References + ---------- + https://en.wikipedia.org/wiki/Euler-Mascheroni_constant """) -add_newdoc('numpy', 'NINF', +add_newdoc('numpy', 'inf', """ - IEEE 754 floating point representation of negative infinity. + IEEE 754 floating point representation of (positive) infinity. Returns ------- y : float - A floating point representation of negative infinity. + A floating point representation of positive infinity. See Also -------- @@ -90,12 +86,92 @@ def add_newdoc(module, name, doc): Also that positive infinity is not equivalent to negative infinity. But infinity is equivalent to positive infinity. + `Inf`, `Infinity`, `PINF` and `infty` are aliases for `inf`. + Examples -------- - >>> np.NINF - -inf - >>> np.log(0) - -inf + >>> np.inf + inf + >>> np.array([1]) / 0. + array([ Inf]) + + """) + +add_newdoc('numpy', 'nan', + """ + IEEE 754 floating point representation of Not a Number (NaN). + + Returns + ------- + y : A floating point representation of Not a Number. + + See Also + -------- + isnan : Shows which elements are Not a Number. + + isfinite : Shows which elements are finite (not one of + Not a Number, positive infinity and negative infinity) + + Notes + ----- + NumPy uses the IEEE Standard for Binary Floating-Point for Arithmetic + (IEEE 754). This means that Not a Number is not equivalent to infinity. + + `NaN` and `NAN` are aliases of `nan`. + + Examples + -------- + >>> np.nan + nan + >>> np.log(-1) + nan + >>> np.log([-1, 1, 2]) + array([ NaN, 0. , 0.69314718]) + + """) + +add_newdoc('numpy', 'newaxis', + """ + A convenient alias for None, useful for indexing arrays. + + Examples + -------- + >>> newaxis is None + True + >>> x = np.arange(3) + >>> x + array([0, 1, 2]) + >>> x[:, newaxis] + array([[0], + [1], + [2]]) + >>> x[:, newaxis, newaxis] + array([[[0]], + [[1]], + [[2]]]) + >>> x[:, newaxis] * x + array([[0, 0, 0], + [0, 1, 2], + [0, 2, 4]]) + + Outer product, same as ``outer(x, y)``: + + >>> y = np.arange(3, 6) + >>> x[:, newaxis] * y + array([[ 0, 0, 0], + [ 3, 4, 5], + [ 6, 8, 10]]) + + ``x[newaxis, :]`` is equivalent to ``x[newaxis]`` and ``x[None]``: + + >>> x[newaxis, :].shape + (1, 3) + >>> x[newaxis].shape + (1, 3) + >>> x[None].shape + (1, 3) + >>> x[:, newaxis].shape + (3, 1) """) @@ -144,32 +220,6 @@ def add_newdoc(module, name, doc): """) -add_newdoc('numpy', 'NaN', - """ - IEEE 754 floating point representation of Not a Number (NaN). - - `NaN` and `NAN` are equivalent definitions of `nan`. Please use - `nan` instead of `NaN`. - - See Also - -------- - nan - - """) - -add_newdoc('numpy', 'PINF', - """ - IEEE 754 floating point representation of (positive) infinity. - - Use `inf` because `Inf`, `Infinity`, `PINF` and `infty` are aliases for - `inf`. For more details, see `inf`. - - See Also - -------- - inf - - """) - add_newdoc('numpy', 'PZERO', """ IEEE 754 floating point representation of positive zero. @@ -215,31 +265,40 @@ def add_newdoc(module, name, doc): """) -add_newdoc('numpy', 'e', +add_newdoc('numpy', 'NAN', """ - Euler's constant, base of natural logarithms, Napier's constant. + IEEE 754 floating point representation of Not a Number (NaN). - ``e = 2.71828182845904523536028747135266249775724709369995...`` + `NaN` and `NAN` are equivalent definitions of `nan`. Please use + `nan` instead of `NAN`. See Also -------- - exp : Exponential function - log : Natural logarithm + nan - References - ---------- - https://en.wikipedia.org/wiki/E_%28mathematical_constant%29 + """) + +add_newdoc('numpy', 'NaN', + """ + IEEE 754 floating point representation of Not a Number (NaN). + + `NaN` and `NAN` are equivalent definitions of `nan`. Please use + `nan` instead of `NaN`. + + See Also + -------- + nan """) -add_newdoc('numpy', 'inf', +add_newdoc('numpy', 'NINF', """ - IEEE 754 floating point representation of (positive) infinity. + IEEE 754 floating point representation of negative infinity. Returns ------- y : float - A floating point representation of positive infinity. + A floating point representation of negative infinity. See Also -------- @@ -261,18 +320,16 @@ def add_newdoc(module, name, doc): Also that positive infinity is not equivalent to negative infinity. But infinity is equivalent to positive infinity. - `Inf`, `Infinity`, `PINF` and `infty` are aliases for `inf`. - Examples -------- - >>> np.inf - inf - >>> np.array([1]) / 0. - array([ Inf]) + >>> np.NINF + -inf + >>> np.log(0) + -inf """) -add_newdoc('numpy', 'infty', +add_newdoc('numpy', 'PINF', """ IEEE 754 floating point representation of (positive) infinity. @@ -285,108 +342,46 @@ def add_newdoc(module, name, doc): """) -add_newdoc('numpy', 'nan', +add_newdoc('numpy', 'infty', """ - IEEE 754 floating point representation of Not a Number (NaN). + IEEE 754 floating point representation of (positive) infinity. - Returns - ------- - y : A floating point representation of Not a Number. + Use `inf` because `Inf`, `Infinity`, `PINF` and `infty` are aliases for + `inf`. For more details, see `inf`. See Also -------- - isnan : Shows which elements are Not a Number. - - isfinite : Shows which elements are finite (not one of - Not a Number, positive infinity and negative infinity) - - Notes - ----- - NumPy uses the IEEE Standard for Binary Floating-Point for Arithmetic - (IEEE 754). This means that Not a Number is not equivalent to infinity. - - `NaN` and `NAN` are aliases of `nan`. - - Examples - -------- - >>> np.nan - nan - >>> np.log(-1) - nan - >>> np.log([-1, 1, 2]) - array([ NaN, 0. , 0.69314718]) + inf """) -add_newdoc('numpy', 'newaxis', +add_newdoc('numpy', 'Inf', """ - A convenient alias for None, useful for indexing arrays. + IEEE 754 floating point representation of (positive) infinity. - See Also - -------- - `numpy.doc.indexing` + Use `inf` because `Inf`, `Infinity`, `PINF` and `infty` are aliases for + `inf`. For more details, see `inf`. - Examples + See Also -------- - >>> newaxis is None - True - >>> x = np.arange(3) - >>> x - array([0, 1, 2]) - >>> x[:, newaxis] - array([[0], - [1], - [2]]) - >>> x[:, newaxis, newaxis] - array([[[0]], - [[1]], - [[2]]]) - >>> x[:, newaxis] * x - array([[0, 0, 0], - [0, 1, 2], - [0, 2, 4]]) - - Outer product, same as ``outer(x, y)``: - - >>> y = np.arange(3, 6) - >>> x[:, newaxis] * y - array([[ 0, 0, 0], - [ 3, 4, 5], - [ 6, 8, 10]]) - - ``x[newaxis, :]`` is equivalent to ``x[newaxis]`` and ``x[None]``: - - >>> x[newaxis, :].shape - (1, 3) - >>> x[newaxis].shape - (1, 3) - >>> x[None].shape - (1, 3) - >>> x[:, newaxis].shape - (3, 1) + inf """) -add_newdoc('numpy', 'pi', +add_newdoc('numpy', 'Infinity', """ - ``pi = 3.1415926535897932384626433...`` - - References - ---------- - https://en.wikipedia.org/wiki/Pi - - """) + IEEE 754 floating point representation of (positive) infinity. -add_newdoc('numpy', 'euler_gamma', - """ - ``γ = 0.5772156649015328606065120900824024310421...`` + Use `inf` because `Inf`, `Infinity`, `PINF` and `infty` are aliases for + `inf`. For more details, see `inf`. - References - ---------- - https://en.wikipedia.org/wiki/Euler-Mascheroni_constant + See Also + -------- + inf """) + if __doc__: constants_str = [] constants.sort() diff --git a/numpy/doc/creation.py b/numpy/doc/creation.py deleted file mode 100644 index 9ebe938be000..000000000000 --- a/numpy/doc/creation.py +++ /dev/null @@ -1,144 +0,0 @@ -""" -============== -Array Creation -============== - -Introduction -============ - -There are 5 general mechanisms for creating arrays: - -1) Conversion from other Python structures (e.g., lists, tuples) -2) Intrinsic numpy array creation objects (e.g., arange, ones, zeros, - etc.) -3) Reading arrays from disk, either from standard or custom formats -4) Creating arrays from raw bytes through the use of strings or buffers -5) Use of special library functions (e.g., random) - -This section will not cover means of replicating, joining, or otherwise -expanding or mutating existing arrays. Nor will it cover creating object -arrays or structured arrays. Both of those are covered in their own sections. - -Converting Python array_like Objects to NumPy Arrays -==================================================== - -In general, numerical data arranged in an array-like structure in Python can -be converted to arrays through the use of the array() function. The most -obvious examples are lists and tuples. See the documentation for array() for -details for its use. Some objects may support the array-protocol and allow -conversion to arrays this way. A simple way to find out if the object can be -converted to a numpy array using array() is simply to try it interactively and -see if it works! (The Python Way). - -Examples: :: - - >>> x = np.array([2,3,1,0]) - >>> x = np.array([2, 3, 1, 0]) - >>> x = np.array([[1,2.0],[0,0],(1+1j,3.)]) # note mix of tuple and lists, - and types - >>> x = np.array([[ 1.+0.j, 2.+0.j], [ 0.+0.j, 0.+0.j], [ 1.+1.j, 3.+0.j]]) - -Intrinsic NumPy Array Creation -============================== - -NumPy has built-in functions for creating arrays from scratch: - -zeros(shape) will create an array filled with 0 values with the specified -shape. The default dtype is float64. :: - - >>> np.zeros((2, 3)) - array([[ 0., 0., 0.], [ 0., 0., 0.]]) - -ones(shape) will create an array filled with 1 values. It is identical to -zeros in all other respects. - -arange() will create arrays with regularly incrementing values. Check the -docstring for complete information on the various ways it can be used. A few -examples will be given here: :: - - >>> np.arange(10) - array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) - >>> np.arange(2, 10, dtype=float) - array([ 2., 3., 4., 5., 6., 7., 8., 9.]) - >>> np.arange(2, 3, 0.1) - array([ 2. , 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9]) - -Note that there are some subtleties regarding the last usage that the user -should be aware of that are described in the arange docstring. - -linspace() will create arrays with a specified number of elements, and -spaced equally between the specified beginning and end values. For -example: :: - - >>> np.linspace(1., 4., 6) - array([ 1. , 1.6, 2.2, 2.8, 3.4, 4. ]) - -The advantage of this creation function is that one can guarantee the -number of elements and the starting and end point, which arange() -generally will not do for arbitrary start, stop, and step values. - -indices() will create a set of arrays (stacked as a one-higher dimensioned -array), one per dimension with each representing variation in that dimension. -An example illustrates much better than a verbal description: :: - - >>> np.indices((3,3)) - array([[[0, 0, 0], [1, 1, 1], [2, 2, 2]], [[0, 1, 2], [0, 1, 2], [0, 1, 2]]]) - -This is particularly useful for evaluating functions of multiple dimensions on -a regular grid. - -Reading Arrays From Disk -======================== - -This is presumably the most common case of large array creation. The details, -of course, depend greatly on the format of data on disk and so this section -can only give general pointers on how to handle various formats. - -Standard Binary Formats ------------------------ - -Various fields have standard formats for array data. The following lists the -ones with known python libraries to read them and return numpy arrays (there -may be others for which it is possible to read and convert to numpy arrays so -check the last section as well) -:: - - HDF5: h5py - FITS: Astropy - -Examples of formats that cannot be read directly but for which it is not hard to -convert are those formats supported by libraries like PIL (able to read and -write many image formats such as jpg, png, etc). - -Common ASCII Formats ------------------------- - -Comma Separated Value files (CSV) are widely used (and an export and import -option for programs like Excel). There are a number of ways of reading these -files in Python. There are CSV functions in Python and functions in pylab -(part of matplotlib). - -More generic ascii files can be read using the io package in scipy. - -Custom Binary Formats ---------------------- - -There are a variety of approaches one can use. If the file has a relatively -simple format then one can write a simple I/O library and use the numpy -fromfile() function and .tofile() method to read and write numpy arrays -directly (mind your byteorder though!) If a good C or C++ library exists that -read the data, one can wrap that library with a variety of techniques though -that certainly is much more work and requires significantly more advanced -knowledge to interface with C or C++. - -Use of Special Libraries ------------------------- - -There are libraries that can be used to generate arrays for special purposes -and it isn't possible to enumerate all of them. The most common uses are use -of the many array generation functions in random that can generate arrays of -random values, and some utility functions to generate special matrices (e.g. -diagonal). - -""" -from __future__ import division, absolute_import, print_function diff --git a/numpy/doc/glossary.py b/numpy/doc/glossary.py deleted file mode 100644 index 0e1df495bfee..000000000000 --- a/numpy/doc/glossary.py +++ /dev/null @@ -1,450 +0,0 @@ -""" -======== -Glossary -======== - -.. glossary:: - - along an axis - Axes are defined for arrays with more than one dimension. A - 2-dimensional array has two corresponding axes: the first running - vertically downwards across rows (axis 0), and the second running - horizontally across columns (axis 1). - - Many operations can take place along one of these axes. For example, - we can sum each row of an array, in which case we operate along - columns, or axis 1:: - - >>> x = np.arange(12).reshape((3,4)) - - >>> x - array([[ 0, 1, 2, 3], - [ 4, 5, 6, 7], - [ 8, 9, 10, 11]]) - - >>> x.sum(axis=1) - array([ 6, 22, 38]) - - array - A homogeneous container of numerical elements. Each element in the - array occupies a fixed amount of memory (hence homogeneous), and - can be a numerical element of a single type (such as float, int - or complex) or a combination (such as ``(float, int, float)``). Each - array has an associated data-type (or ``dtype``), which describes - the numerical type of its elements:: - - >>> x = np.array([1, 2, 3], float) - - >>> x - array([ 1., 2., 3.]) - - >>> x.dtype # floating point number, 64 bits of memory per element - dtype('float64') - - - # More complicated data type: each array element is a combination of - # and integer and a floating point number - >>> np.array([(1, 2.0), (3, 4.0)], dtype=[('x', int), ('y', float)]) - array([(1, 2.0), (3, 4.0)], - dtype=[('x', '<i4'), ('y', '<f8')]) - - Fast element-wise operations, called a :term:`ufunc`, operate on arrays. - - array_like - Any sequence that can be interpreted as an ndarray. This includes - nested lists, tuples, scalars and existing arrays. - - attribute - A property of an object that can be accessed using ``obj.attribute``, - e.g., ``shape`` is an attribute of an array:: - - >>> x = np.array([1, 2, 3]) - >>> x.shape - (3,) - - big-endian - When storing a multi-byte value in memory as a sequence of bytes, the - sequence addresses/sends/stores the most significant byte first (lowest - address) and the least significant byte last (highest address). Common in - micro-processors and used for transmission of data over network protocols. - - BLAS - `Basic Linear Algebra Subprograms <http://en.wikipedia.org/wiki/BLAS>`_ - - broadcast - NumPy can do operations on arrays whose shapes are mismatched:: - - >>> x = np.array([1, 2]) - >>> y = np.array([[3], [4]]) - - >>> x - array([1, 2]) - - >>> y - array([[3], - [4]]) - - >>> x + y - array([[4, 5], - [5, 6]]) - - See `numpy.doc.broadcasting` for more information. - - C order - See `row-major` - - column-major - A way to represent items in a N-dimensional array in the 1-dimensional - computer memory. In column-major order, the leftmost index "varies the - fastest": for example the array:: - - [[1, 2, 3], - [4, 5, 6]] - - is represented in the column-major order as:: - - [1, 4, 2, 5, 3, 6] - - Column-major order is also known as the Fortran order, as the Fortran - programming language uses it. - - decorator - An operator that transforms a function. For example, a ``log`` - decorator may be defined to print debugging information upon - function execution:: - - >>> def log(f): - ... def new_logging_func(*args, **kwargs): - ... print("Logging call with parameters:", args, kwargs) - ... return f(*args, **kwargs) - ... - ... return new_logging_func - - Now, when we define a function, we can "decorate" it using ``log``:: - - >>> @log - ... def add(a, b): - ... return a + b - - Calling ``add`` then yields: - - >>> add(1, 2) - Logging call with parameters: (1, 2) {} - 3 - - dictionary - Resembling a language dictionary, which provides a mapping between - words and descriptions thereof, a Python dictionary is a mapping - between two objects:: - - >>> x = {1: 'one', 'two': [1, 2]} - - Here, `x` is a dictionary mapping keys to values, in this case - the integer 1 to the string "one", and the string "two" to - the list ``[1, 2]``. The values may be accessed using their - corresponding keys:: - - >>> x[1] - 'one' - - >>> x['two'] - [1, 2] - - Note that dictionaries are not stored in any specific order. Also, - most mutable (see *immutable* below) objects, such as lists, may not - be used as keys. - - For more information on dictionaries, read the - `Python tutorial <http://docs.python.org/tut>`_. - - field - In a :term:`structured data type`, each sub-type is called a `field`. - The `field` has a name (a string), a type (any valid :term:`dtype`, and - an optional `title`. See :ref:`arrays.dtypes` - - Fortran order - See `column-major` - - flattened - Collapsed to a one-dimensional array. See `numpy.ndarray.flatten` - for details. - - homogenous - Describes a block of memory comprised of blocks, each block comprised of - items and of the same size, and blocks are interpreted in exactly the - same way. In the simplest case each block contains a single item, for - instance int32 or float64. - - immutable - An object that cannot be modified after execution is called - immutable. Two common examples are strings and tuples. - - instance - A class definition gives the blueprint for constructing an object:: - - >>> class House(object): - ... wall_colour = 'white' - - Yet, we have to *build* a house before it exists:: - - >>> h = House() # build a house - - Now, ``h`` is called a ``House`` instance. An instance is therefore - a specific realisation of a class. - - iterable - A sequence that allows "walking" (iterating) over items, typically - using a loop such as:: - - >>> x = [1, 2, 3] - >>> [item**2 for item in x] - [1, 4, 9] - - It is often used in combination with ``enumerate``:: - >>> keys = ['a','b','c'] - >>> for n, k in enumerate(keys): - ... print("Key %d: %s" % (n, k)) - ... - Key 0: a - Key 1: b - Key 2: c - - list - A Python container that can hold any number of objects or items. - The items do not have to be of the same type, and can even be - lists themselves:: - - >>> x = [2, 2.0, "two", [2, 2.0]] - - The list `x` contains 4 items, each which can be accessed individually:: - - >>> x[2] # the string 'two' - 'two' - - >>> x[3] # a list, containing an integer 2 and a float 2.0 - [2, 2.0] - - It is also possible to select more than one item at a time, - using *slicing*:: - - >>> x[0:2] # or, equivalently, x[:2] - [2, 2.0] - - In code, arrays are often conveniently expressed as nested lists:: - - - >>> np.array([[1, 2], [3, 4]]) - array([[1, 2], - [3, 4]]) - - For more information, read the section on lists in the `Python - tutorial <http://docs.python.org/tut>`_. For a mapping - type (key-value), see *dictionary*. - - little-endian - When storing a multi-byte value in memory as a sequence of bytes, the - sequence addresses/sends/stores the least significant byte first (lowest - address) and the most significant byte last (highest address). Common in - x86 processors. - - mask - A boolean array, used to select only certain elements for an operation:: - - >>> x = np.arange(5) - >>> x - array([0, 1, 2, 3, 4]) - - >>> mask = (x > 2) - >>> mask - array([False, False, False, True, True]) - - >>> x[mask] = -1 - >>> x - array([ 0, 1, 2, -1, -1]) - - masked array - Array that suppressed values indicated by a mask:: - - >>> x = np.ma.masked_array([np.nan, 2, np.nan], [True, False, True]) - >>> x - masked_array(data = [-- 2.0 --], - mask = [ True False True], - fill_value = 1e+20) - <BLANKLINE> - - >>> x + [1, 2, 3] - masked_array(data = [-- 4.0 --], - mask = [ True False True], - fill_value = 1e+20) - <BLANKLINE> - - - Masked arrays are often used when operating on arrays containing - missing or invalid entries. - - matrix - A 2-dimensional ndarray that preserves its two-dimensional nature - throughout operations. It has certain special operations, such as ``*`` - (matrix multiplication) and ``**`` (matrix power), defined:: - - >>> x = np.mat([[1, 2], [3, 4]]) - >>> x - matrix([[1, 2], - [3, 4]]) - - >>> x**2 - matrix([[ 7, 10], - [15, 22]]) - - method - A function associated with an object. For example, each ndarray has a - method called ``repeat``:: - - >>> x = np.array([1, 2, 3]) - >>> x.repeat(2) - array([1, 1, 2, 2, 3, 3]) - - ndarray - See *array*. - - record array - An :term:`ndarray` with :term:`structured data type` which has been - subclassed as ``np.recarray`` and whose dtype is of type ``np.record``, - making the fields of its data type to be accessible by attribute. - - reference - If ``a`` is a reference to ``b``, then ``(a is b) == True``. Therefore, - ``a`` and ``b`` are different names for the same Python object. - - row-major - A way to represent items in a N-dimensional array in the 1-dimensional - computer memory. In row-major order, the rightmost index "varies - the fastest": for example the array:: - - [[1, 2, 3], - [4, 5, 6]] - - is represented in the row-major order as:: - - [1, 2, 3, 4, 5, 6] - - Row-major order is also known as the C order, as the C programming - language uses it. New NumPy arrays are by default in row-major order. - - self - Often seen in method signatures, ``self`` refers to the instance - of the associated class. For example: - - >>> class Paintbrush(object): - ... color = 'blue' - ... - ... def paint(self): - ... print("Painting the city %s!" % self.color) - ... - >>> p = Paintbrush() - >>> p.color = 'red' - >>> p.paint() # self refers to 'p' - Painting the city red! - - slice - Used to select only certain elements from a sequence:: - - >>> x = range(5) - >>> x - [0, 1, 2, 3, 4] - - >>> x[1:3] # slice from 1 to 3 (excluding 3 itself) - [1, 2] - - >>> x[1:5:2] # slice from 1 to 5, but skipping every second element - [1, 3] - - >>> x[::-1] # slice a sequence in reverse - [4, 3, 2, 1, 0] - - Arrays may have more than one dimension, each which can be sliced - individually:: - - >>> x = np.array([[1, 2], [3, 4]]) - >>> x - array([[1, 2], - [3, 4]]) - - >>> x[:, 1] - array([2, 4]) - - structure - See :term:`structured data type` - - structured data type - A data type composed of other datatypes - - tuple - A sequence that may contain a variable number of types of any - kind. A tuple is immutable, i.e., once constructed it cannot be - changed. Similar to a list, it can be indexed and sliced:: - - >>> x = (1, 'one', [1, 2]) - >>> x - (1, 'one', [1, 2]) - - >>> x[0] - 1 - - >>> x[:2] - (1, 'one') - - A useful concept is "tuple unpacking", which allows variables to - be assigned to the contents of a tuple:: - - >>> x, y = (1, 2) - >>> x, y = 1, 2 - - This is often used when a function returns multiple values: - - >>> def return_many(): - ... return 1, 'alpha', None - - >>> a, b, c = return_many() - >>> a, b, c - (1, 'alpha', None) - - >>> a - 1 - >>> b - 'alpha' - - ufunc - Universal function. A fast element-wise array operation. Examples include - ``add``, ``sin`` and ``logical_or``. - - view - An array that does not own its data, but refers to another array's - data instead. For example, we may create a view that only shows - every second element of another array:: - - >>> x = np.arange(5) - >>> x - array([0, 1, 2, 3, 4]) - - >>> y = x[::2] - >>> y - array([0, 2, 4]) - - >>> x[0] = 3 # changing x changes y as well, since y is a view on x - >>> y - array([3, 2, 4]) - - wrapper - Python is a high-level (highly abstracted, or English-like) language. - This abstraction comes at a price in execution speed, and sometimes - it becomes necessary to use lower level languages to do fast - computations. A wrapper is code that provides a bridge between - high and the low level languages, allowing, e.g., Python to execute - code written in C or Fortran. - - Examples include ctypes, SWIG and Cython (which wraps C and C++) - and f2py (which wraps Fortran). - -""" -from __future__ import division, absolute_import, print_function diff --git a/numpy/doc/indexing.py b/numpy/doc/indexing.py deleted file mode 100644 index 5f5033117ef5..000000000000 --- a/numpy/doc/indexing.py +++ /dev/null @@ -1,439 +0,0 @@ -"""============== -Array indexing -============== - -Array indexing refers to any use of the square brackets ([]) to index -array values. There are many options to indexing, which give numpy -indexing great power, but with power comes some complexity and the -potential for confusion. This section is just an overview of the -various options and issues related to indexing. Aside from single -element indexing, the details on most of these options are to be -found in related sections. - -Assignment vs referencing -========================= - -Most of the following examples show the use of indexing when -referencing data in an array. The examples work just as well -when assigning to an array. See the section at the end for -specific examples and explanations on how assignments work. - -Single element indexing -======================= - -Single element indexing for a 1-D array is what one expects. It work -exactly like that for other standard Python sequences. It is 0-based, -and accepts negative indices for indexing from the end of the array. :: - - >>> x = np.arange(10) - >>> x[2] - 2 - >>> x[-2] - 8 - -Unlike lists and tuples, numpy arrays support multidimensional indexing -for multidimensional arrays. That means that it is not necessary to -separate each dimension's index into its own set of square brackets. :: - - >>> x.shape = (2,5) # now x is 2-dimensional - >>> x[1,3] - 8 - >>> x[1,-1] - 9 - -Note that if one indexes a multidimensional array with fewer indices -than dimensions, one gets a subdimensional array. For example: :: - - >>> x[0] - array([0, 1, 2, 3, 4]) - -That is, each index specified selects the array corresponding to the -rest of the dimensions selected. In the above example, choosing 0 -means that the remaining dimension of length 5 is being left unspecified, -and that what is returned is an array of that dimensionality and size. -It must be noted that the returned array is not a copy of the original, -but points to the same values in memory as does the original array. -In this case, the 1-D array at the first position (0) is returned. -So using a single index on the returned array, results in a single -element being returned. That is: :: - - >>> x[0][2] - 2 - -So note that ``x[0,2] = x[0][2]`` though the second case is more -inefficient as a new temporary array is created after the first index -that is subsequently indexed by 2. - -Note to those used to IDL or Fortran memory order as it relates to -indexing. NumPy uses C-order indexing. That means that the last -index usually represents the most rapidly changing memory location, -unlike Fortran or IDL, where the first index represents the most -rapidly changing location in memory. This difference represents a -great potential for confusion. - -Other indexing options -====================== - -It is possible to slice and stride arrays to extract arrays of the -same number of dimensions, but of different sizes than the original. -The slicing and striding works exactly the same way it does for lists -and tuples except that they can be applied to multiple dimensions as -well. A few examples illustrates best: :: - - >>> x = np.arange(10) - >>> x[2:5] - array([2, 3, 4]) - >>> x[:-7] - array([0, 1, 2]) - >>> x[1:7:2] - array([1, 3, 5]) - >>> y = np.arange(35).reshape(5,7) - >>> y[1:5:2,::3] - array([[ 7, 10, 13], - [21, 24, 27]]) - -Note that slices of arrays do not copy the internal array data but -also produce new views of the original data. - -It is possible to index arrays with other arrays for the purposes of -selecting lists of values out of arrays into new arrays. There are -two different ways of accomplishing this. One uses one or more arrays -of index values. The other involves giving a boolean array of the proper -shape to indicate the values to be selected. Index arrays are a very -powerful tool that allow one to avoid looping over individual elements in -arrays and thus greatly improve performance. - -It is possible to use special features to effectively increase the -number of dimensions in an array through indexing so the resulting -array aquires the shape needed for use in an expression or with a -specific function. - -Index arrays -============ - -NumPy arrays may be indexed with other arrays (or any other sequence- -like object that can be converted to an array, such as lists, with the -exception of tuples; see the end of this document for why this is). The -use of index arrays ranges from simple, straightforward cases to -complex, hard-to-understand cases. For all cases of index arrays, what -is returned is a copy of the original data, not a view as one gets for -slices. - -Index arrays must be of integer type. Each value in the array indicates -which value in the array to use in place of the index. To illustrate: :: - - >>> x = np.arange(10,1,-1) - >>> x - array([10, 9, 8, 7, 6, 5, 4, 3, 2]) - >>> x[np.array([3, 3, 1, 8])] - array([7, 7, 9, 2]) - - -The index array consisting of the values 3, 3, 1 and 8 correspondingly -create an array of length 4 (same as the index array) where each index -is replaced by the value the index array has in the array being indexed. - -Negative values are permitted and work as they do with single indices -or slices: :: - - >>> x[np.array([3,3,-3,8])] - array([7, 7, 4, 2]) - -It is an error to have index values out of bounds: :: - - >>> x[np.array([3, 3, 20, 8])] - <type 'exceptions.IndexError'>: index 20 out of bounds 0<=index<9 - -Generally speaking, what is returned when index arrays are used is -an array with the same shape as the index array, but with the type -and values of the array being indexed. As an example, we can use a -multidimensional index array instead: :: - - >>> x[np.array([[1,1],[2,3]])] - array([[9, 9], - [8, 7]]) - -Indexing Multi-dimensional arrays -================================= - -Things become more complex when multidimensional arrays are indexed, -particularly with multidimensional index arrays. These tend to be -more unusual uses, but they are permitted, and they are useful for some -problems. We'll start with the simplest multidimensional case (using -the array y from the previous examples): :: - - >>> y[np.array([0,2,4]), np.array([0,1,2])] - array([ 0, 15, 30]) - -In this case, if the index arrays have a matching shape, and there is -an index array for each dimension of the array being indexed, the -resultant array has the same shape as the index arrays, and the values -correspond to the index set for each position in the index arrays. In -this example, the first index value is 0 for both index arrays, and -thus the first value of the resultant array is y[0,0]. The next value -is y[2,1], and the last is y[4,2]. - -If the index arrays do not have the same shape, there is an attempt to -broadcast them to the same shape. If they cannot be broadcast to the -same shape, an exception is raised: :: - - >>> y[np.array([0,2,4]), np.array([0,1])] - <type 'exceptions.ValueError'>: shape mismatch: objects cannot be - broadcast to a single shape - -The broadcasting mechanism permits index arrays to be combined with -scalars for other indices. The effect is that the scalar value is used -for all the corresponding values of the index arrays: :: - - >>> y[np.array([0,2,4]), 1] - array([ 1, 15, 29]) - -Jumping to the next level of complexity, it is possible to only -partially index an array with index arrays. It takes a bit of thought -to understand what happens in such cases. For example if we just use -one index array with y: :: - - >>> y[np.array([0,2,4])] - array([[ 0, 1, 2, 3, 4, 5, 6], - [14, 15, 16, 17, 18, 19, 20], - [28, 29, 30, 31, 32, 33, 34]]) - -What results is the construction of a new array where each value of -the index array selects one row from the array being indexed and the -resultant array has the resulting shape (number of index elements, -size of row). - -An example of where this may be useful is for a color lookup table -where we want to map the values of an image into RGB triples for -display. The lookup table could have a shape (nlookup, 3). Indexing -such an array with an image with shape (ny, nx) with dtype=np.uint8 -(or any integer type so long as values are with the bounds of the -lookup table) will result in an array of shape (ny, nx, 3) where a -triple of RGB values is associated with each pixel location. - -In general, the shape of the resultant array will be the concatenation -of the shape of the index array (or the shape that all the index arrays -were broadcast to) with the shape of any unused dimensions (those not -indexed) in the array being indexed. - -Boolean or "mask" index arrays -============================== - -Boolean arrays used as indices are treated in a different manner -entirely than index arrays. Boolean arrays must be of the same shape -as the initial dimensions of the array being indexed. In the -most straightforward case, the boolean array has the same shape: :: - - >>> b = y>20 - >>> y[b] - array([21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34]) - -Unlike in the case of integer index arrays, in the boolean case, the -result is a 1-D array containing all the elements in the indexed array -corresponding to all the true elements in the boolean array. The -elements in the indexed array are always iterated and returned in -:term:`row-major` (C-style) order. The result is also identical to -``y[np.nonzero(b)]``. As with index arrays, what is returned is a copy -of the data, not a view as one gets with slices. - -The result will be multidimensional if y has more dimensions than b. -For example: :: - - >>> b[:,5] # use a 1-D boolean whose first dim agrees with the first dim of y - array([False, False, False, True, True]) - >>> y[b[:,5]] - array([[21, 22, 23, 24, 25, 26, 27], - [28, 29, 30, 31, 32, 33, 34]]) - -Here the 4th and 5th rows are selected from the indexed array and -combined to make a 2-D array. - -In general, when the boolean array has fewer dimensions than the array -being indexed, this is equivalent to y[b, ...], which means -y is indexed by b followed by as many : as are needed to fill -out the rank of y. -Thus the shape of the result is one dimension containing the number -of True elements of the boolean array, followed by the remaining -dimensions of the array being indexed. - -For example, using a 2-D boolean array of shape (2,3) -with four True elements to select rows from a 3-D array of shape -(2,3,5) results in a 2-D result of shape (4,5): :: - - >>> x = np.arange(30).reshape(2,3,5) - >>> x - array([[[ 0, 1, 2, 3, 4], - [ 5, 6, 7, 8, 9], - [10, 11, 12, 13, 14]], - [[15, 16, 17, 18, 19], - [20, 21, 22, 23, 24], - [25, 26, 27, 28, 29]]]) - >>> b = np.array([[True, True, False], [False, True, True]]) - >>> x[b] - array([[ 0, 1, 2, 3, 4], - [ 5, 6, 7, 8, 9], - [20, 21, 22, 23, 24], - [25, 26, 27, 28, 29]]) - -For further details, consult the numpy reference documentation on array indexing. - -Combining index arrays with slices -================================== - -Index arrays may be combined with slices. For example: :: - - >>> y[np.array([0,2,4]),1:3] - array([[ 1, 2], - [15, 16], - [29, 30]]) - -In effect, the slice is converted to an index array -np.array([[1,2]]) (shape (1,2)) that is broadcast with the index array -to produce a resultant array of shape (3,2). - -Likewise, slicing can be combined with broadcasted boolean indices: :: - - >>> y[b[:,5],1:3] - array([[22, 23], - [29, 30]]) - -Structural indexing tools -========================= - -To facilitate easy matching of array shapes with expressions and in -assignments, the np.newaxis object can be used within array indices -to add new dimensions with a size of 1. For example: :: - - >>> y.shape - (5, 7) - >>> y[:,np.newaxis,:].shape - (5, 1, 7) - -Note that there are no new elements in the array, just that the -dimensionality is increased. This can be handy to combine two -arrays in a way that otherwise would require explicitly reshaping -operations. For example: :: - - >>> x = np.arange(5) - >>> x[:,np.newaxis] + x[np.newaxis,:] - array([[0, 1, 2, 3, 4], - [1, 2, 3, 4, 5], - [2, 3, 4, 5, 6], - [3, 4, 5, 6, 7], - [4, 5, 6, 7, 8]]) - -The ellipsis syntax maybe used to indicate selecting in full any -remaining unspecified dimensions. For example: :: - - >>> z = np.arange(81).reshape(3,3,3,3) - >>> z[1,...,2] - array([[29, 32, 35], - [38, 41, 44], - [47, 50, 53]]) - -This is equivalent to: :: - - >>> z[1,:,:,2] - array([[29, 32, 35], - [38, 41, 44], - [47, 50, 53]]) - -Assigning values to indexed arrays -================================== - -As mentioned, one can select a subset of an array to assign to using -a single index, slices, and index and mask arrays. The value being -assigned to the indexed array must be shape consistent (the same shape -or broadcastable to the shape the index produces). For example, it is -permitted to assign a constant to a slice: :: - - >>> x = np.arange(10) - >>> x[2:7] = 1 - -or an array of the right size: :: - - >>> x[2:7] = np.arange(5) - -Note that assignments may result in changes if assigning -higher types to lower types (like floats to ints) or even -exceptions (assigning complex to floats or ints): :: - - >>> x[1] = 1.2 - >>> x[1] - 1 - >>> x[1] = 1.2j - <type 'exceptions.TypeError'>: can't convert complex to long; use - long(abs(z)) - - -Unlike some of the references (such as array and mask indices) -assignments are always made to the original data in the array -(indeed, nothing else would make sense!). Note though, that some -actions may not work as one may naively expect. This particular -example is often surprising to people: :: - - >>> x = np.arange(0, 50, 10) - >>> x - array([ 0, 10, 20, 30, 40]) - >>> x[np.array([1, 1, 3, 1])] += 1 - >>> x - array([ 0, 11, 20, 31, 40]) - -Where people expect that the 1st location will be incremented by 3. -In fact, it will only be incremented by 1. The reason is because -a new array is extracted from the original (as a temporary) containing -the values at 1, 1, 3, 1, then the value 1 is added to the temporary, -and then the temporary is assigned back to the original array. Thus -the value of the array at x[1]+1 is assigned to x[1] three times, -rather than being incremented 3 times. - -Dealing with variable numbers of indices within programs -======================================================== - -The index syntax is very powerful but limiting when dealing with -a variable number of indices. For example, if you want to write -a function that can handle arguments with various numbers of -dimensions without having to write special case code for each -number of possible dimensions, how can that be done? If one -supplies to the index a tuple, the tuple will be interpreted -as a list of indices. For example (using the previous definition -for the array z): :: - - >>> indices = (1,1,1,1) - >>> z[indices] - 40 - -So one can use code to construct tuples of any number of indices -and then use these within an index. - -Slices can be specified within programs by using the slice() function -in Python. For example: :: - - >>> indices = (1,1,1,slice(0,2)) # same as [1,1,1,0:2] - >>> z[indices] - array([39, 40]) - -Likewise, ellipsis can be specified by code by using the Ellipsis -object: :: - - >>> indices = (1, Ellipsis, 1) # same as [1,...,1] - >>> z[indices] - array([[28, 31, 34], - [37, 40, 43], - [46, 49, 52]]) - -For this reason it is possible to use the output from the np.nonzero() -function directly as an index since it always returns a tuple of index -arrays. - -Because the special treatment of tuples, they are not automatically -converted to an array as a list would be. As an example: :: - - >>> z[[1,1,1,1]] # produces a large array - array([[[[27, 28, 29], - [30, 31, 32], ... - >>> z[(1,1,1,1)] # returns a single value - 40 - -""" -from __future__ import division, absolute_import, print_function diff --git a/numpy/doc/internals.py b/numpy/doc/internals.py deleted file mode 100644 index a14fee7c2fff..000000000000 --- a/numpy/doc/internals.py +++ /dev/null @@ -1,163 +0,0 @@ -""" -=============== -Array Internals -=============== - -Internal organization of numpy arrays -===================================== - -It helps to understand a bit about how numpy arrays are handled under the covers to help understand numpy better. This section will not go into great detail. Those wishing to understand the full details are referred to Travis Oliphant's book "Guide to NumPy". - -NumPy arrays consist of two major components, the raw array data (from now on, -referred to as the data buffer), and the information about the raw array data. -The data buffer is typically what people think of as arrays in C or Fortran, -a contiguous (and fixed) block of memory containing fixed sized data items. -NumPy also contains a significant set of data that describes how to interpret -the data in the data buffer. This extra information contains (among other things): - - 1) The basic data element's size in bytes - 2) The start of the data within the data buffer (an offset relative to the - beginning of the data buffer). - 3) The number of dimensions and the size of each dimension - 4) The separation between elements for each dimension (the 'stride'). This - does not have to be a multiple of the element size - 5) The byte order of the data (which may not be the native byte order) - 6) Whether the buffer is read-only - 7) Information (via the dtype object) about the interpretation of the basic - data element. The basic data element may be as simple as a int or a float, - or it may be a compound object (e.g., struct-like), a fixed character field, - or Python object pointers. - 8) Whether the array is to interpreted as C-order or Fortran-order. - -This arrangement allow for very flexible use of arrays. One thing that it allows -is simple changes of the metadata to change the interpretation of the array buffer. -Changing the byteorder of the array is a simple change involving no rearrangement -of the data. The shape of the array can be changed very easily without changing -anything in the data buffer or any data copying at all - -Among other things that are made possible is one can create a new array metadata -object that uses the same data buffer -to create a new view of that data buffer that has a different interpretation -of the buffer (e.g., different shape, offset, byte order, strides, etc) but -shares the same data bytes. Many operations in numpy do just this such as -slices. Other operations, such as transpose, don't move data elements -around in the array, but rather change the information about the shape and strides so that the indexing of the array changes, but the data in the doesn't move. - -Typically these new versions of the array metadata but the same data buffer are -new 'views' into the data buffer. There is a different ndarray object, but it -uses the same data buffer. This is why it is necessary to force copies through -use of the .copy() method if one really wants to make a new and independent -copy of the data buffer. - -New views into arrays mean the object reference counts for the data buffer -increase. Simply doing away with the original array object will not remove the -data buffer if other views of it still exist. - -Multidimensional Array Indexing Order Issues -============================================ - -What is the right way to index -multi-dimensional arrays? Before you jump to conclusions about the one and -true way to index multi-dimensional arrays, it pays to understand why this is -a confusing issue. This section will try to explain in detail how numpy -indexing works and why we adopt the convention we do for images, and when it -may be appropriate to adopt other conventions. - -The first thing to understand is -that there are two conflicting conventions for indexing 2-dimensional arrays. -Matrix notation uses the first index to indicate which row is being selected and -the second index to indicate which column is selected. This is opposite the -geometrically oriented-convention for images where people generally think the -first index represents x position (i.e., column) and the second represents y -position (i.e., row). This alone is the source of much confusion; -matrix-oriented users and image-oriented users expect two different things with -regard to indexing. - -The second issue to understand is how indices correspond -to the order the array is stored in memory. In Fortran the first index is the -most rapidly varying index when moving through the elements of a two -dimensional array as it is stored in memory. If you adopt the matrix -convention for indexing, then this means the matrix is stored one column at a -time (since the first index moves to the next row as it changes). Thus Fortran -is considered a Column-major language. C has just the opposite convention. In -C, the last index changes most rapidly as one moves through the array as -stored in memory. Thus C is a Row-major language. The matrix is stored by -rows. Note that in both cases it presumes that the matrix convention for -indexing is being used, i.e., for both Fortran and C, the first index is the -row. Note this convention implies that the indexing convention is invariant -and that the data order changes to keep that so. - -But that's not the only way -to look at it. Suppose one has large two-dimensional arrays (images or -matrices) stored in data files. Suppose the data are stored by rows rather than -by columns. If we are to preserve our index convention (whether matrix or -image) that means that depending on the language we use, we may be forced to -reorder the data if it is read into memory to preserve our indexing -convention. For example if we read row-ordered data into memory without -reordering, it will match the matrix indexing convention for C, but not for -Fortran. Conversely, it will match the image indexing convention for Fortran, -but not for C. For C, if one is using data stored in row order, and one wants -to preserve the image index convention, the data must be reordered when -reading into memory. - -In the end, which you do for Fortran or C depends on -which is more important, not reordering data or preserving the indexing -convention. For large images, reordering data is potentially expensive, and -often the indexing convention is inverted to avoid that. - -The situation with -numpy makes this issue yet more complicated. The internal machinery of numpy -arrays is flexible enough to accept any ordering of indices. One can simply -reorder indices by manipulating the internal stride information for arrays -without reordering the data at all. NumPy will know how to map the new index -order to the data without moving the data. - -So if this is true, why not choose -the index order that matches what you most expect? In particular, why not define -row-ordered images to use the image convention? (This is sometimes referred -to as the Fortran convention vs the C convention, thus the 'C' and 'FORTRAN' -order options for array ordering in numpy.) The drawback of doing this is -potential performance penalties. It's common to access the data sequentially, -either implicitly in array operations or explicitly by looping over rows of an -image. When that is done, then the data will be accessed in non-optimal order. -As the first index is incremented, what is actually happening is that elements -spaced far apart in memory are being sequentially accessed, with usually poor -memory access speeds. For example, for a two dimensional image 'im' defined so -that im[0, 10] represents the value at x=0, y=10. To be consistent with usual -Python behavior then im[0] would represent a column at x=0. Yet that data -would be spread over the whole array since the data are stored in row order. -Despite the flexibility of numpy's indexing, it can't really paper over the fact -basic operations are rendered inefficient because of data order or that getting -contiguous subarrays is still awkward (e.g., im[:,0] for the first row, vs -im[0]), thus one can't use an idiom such as for row in im; for col in im does -work, but doesn't yield contiguous column data. - -As it turns out, numpy is -smart enough when dealing with ufuncs to determine which index is the most -rapidly varying one in memory and uses that for the innermost loop. Thus for -ufuncs there is no large intrinsic advantage to either approach in most cases. -On the other hand, use of .flat with an FORTRAN ordered array will lead to -non-optimal memory access as adjacent elements in the flattened array (iterator, -actually) are not contiguous in memory. - -Indeed, the fact is that Python -indexing on lists and other sequences naturally leads to an outside-to inside -ordering (the first index gets the largest grouping, the next the next largest, -and the last gets the smallest element). Since image data are normally stored -by rows, this corresponds to position within rows being the last item indexed. - -If you do want to use Fortran ordering realize that -there are two approaches to consider: 1) accept that the first index is just not -the most rapidly changing in memory and have all your I/O routines reorder -your data when going from memory to disk or visa versa, or use numpy's -mechanism for mapping the first index to the most rapidly varying data. We -recommend the former if possible. The disadvantage of the latter is that many -of numpy's functions will yield arrays without Fortran ordering unless you are -careful to use the 'order' keyword. Doing this would be highly inconvenient. - -Otherwise we recommend simply learning to reverse the usual order of indices -when accessing elements of an array. Granted, it goes against the grain, but -it is more in line with Python semantics and the natural order of the data. - -""" -from __future__ import division, absolute_import, print_function diff --git a/numpy/doc/misc.py b/numpy/doc/misc.py deleted file mode 100644 index 24369871c96e..000000000000 --- a/numpy/doc/misc.py +++ /dev/null @@ -1,227 +0,0 @@ -""" -============= -Miscellaneous -============= - -IEEE 754 Floating Point Special Values --------------------------------------- - -Special values defined in numpy: nan, inf, - -NaNs can be used as a poor-man's mask (if you don't care what the -original value was) - -Note: cannot use equality to test NaNs. E.g.: :: - - >>> myarr = np.array([1., 0., np.nan, 3.]) - >>> np.nonzero(myarr == np.nan) - (array([], dtype=int64),) - >>> np.nan == np.nan # is always False! Use special numpy functions instead. - False - >>> myarr[myarr == np.nan] = 0. # doesn't work - >>> myarr - array([ 1., 0., NaN, 3.]) - >>> myarr[np.isnan(myarr)] = 0. # use this instead find - >>> myarr - array([ 1., 0., 0., 3.]) - -Other related special value functions: :: - - isinf(): True if value is inf - isfinite(): True if not nan or inf - nan_to_num(): Map nan to 0, inf to max float, -inf to min float - -The following corresponds to the usual functions except that nans are excluded -from the results: :: - - nansum() - nanmax() - nanmin() - nanargmax() - nanargmin() - - >>> x = np.arange(10.) - >>> x[3] = np.nan - >>> x.sum() - nan - >>> np.nansum(x) - 42.0 - -How numpy handles numerical exceptions --------------------------------------- - -The default is to ``'warn'`` for ``invalid``, ``divide``, and ``overflow`` -and ``'ignore'`` for ``underflow``. But this can be changed, and it can be -set individually for different kinds of exceptions. The different behaviors -are: - - - 'ignore' : Take no action when the exception occurs. - - 'warn' : Print a `RuntimeWarning` (via the Python `warnings` module). - - 'raise' : Raise a `FloatingPointError`. - - 'call' : Call a function specified using the `seterrcall` function. - - 'print' : Print a warning directly to ``stdout``. - - 'log' : Record error in a Log object specified by `seterrcall`. - -These behaviors can be set for all kinds of errors or specific ones: - - - all : apply to all numeric exceptions - - invalid : when NaNs are generated - - divide : divide by zero (for integers as well!) - - overflow : floating point overflows - - underflow : floating point underflows - -Note that integer divide-by-zero is handled by the same machinery. -These behaviors are set on a per-thread basis. - -Examples --------- - -:: - - >>> oldsettings = np.seterr(all='warn') - >>> np.zeros(5,dtype=np.float32)/0. - invalid value encountered in divide - >>> j = np.seterr(under='ignore') - >>> np.array([1.e-100])**10 - >>> j = np.seterr(invalid='raise') - >>> np.sqrt(np.array([-1.])) - FloatingPointError: invalid value encountered in sqrt - >>> def errorhandler(errstr, errflag): - ... print("saw stupid error!") - >>> np.seterrcall(errorhandler) - <function err_handler at 0x...> - >>> j = np.seterr(all='call') - >>> np.zeros(5, dtype=np.int32)/0 - FloatingPointError: invalid value encountered in divide - saw stupid error! - >>> j = np.seterr(**oldsettings) # restore previous - ... # error-handling settings - -Interfacing to C ----------------- -Only a survey of the choices. Little detail on how each works. - -1) Bare metal, wrap your own C-code manually. - - - Plusses: - - - Efficient - - No dependencies on other tools - - - Minuses: - - - Lots of learning overhead: - - - need to learn basics of Python C API - - need to learn basics of numpy C API - - need to learn how to handle reference counting and love it. - - - Reference counting often difficult to get right. - - - getting it wrong leads to memory leaks, and worse, segfaults - - - API will change for Python 3.0! - -2) Cython - - - Plusses: - - - avoid learning C API's - - no dealing with reference counting - - can code in pseudo python and generate C code - - can also interface to existing C code - - should shield you from changes to Python C api - - has become the de-facto standard within the scientific Python community - - fast indexing support for arrays - - - Minuses: - - - Can write code in non-standard form which may become obsolete - - Not as flexible as manual wrapping - -3) ctypes - - - Plusses: - - - part of Python standard library - - good for interfacing to existing sharable libraries, particularly - Windows DLLs - - avoids API/reference counting issues - - good numpy support: arrays have all these in their ctypes - attribute: :: - - a.ctypes.data a.ctypes.get_strides - a.ctypes.data_as a.ctypes.shape - a.ctypes.get_as_parameter a.ctypes.shape_as - a.ctypes.get_data a.ctypes.strides - a.ctypes.get_shape a.ctypes.strides_as - - - Minuses: - - - can't use for writing code to be turned into C extensions, only a wrapper - tool. - -4) SWIG (automatic wrapper generator) - - - Plusses: - - - around a long time - - multiple scripting language support - - C++ support - - Good for wrapping large (many functions) existing C libraries - - - Minuses: - - - generates lots of code between Python and the C code - - can cause performance problems that are nearly impossible to optimize - out - - interface files can be hard to write - - doesn't necessarily avoid reference counting issues or needing to know - API's - -5) scipy.weave - - - Plusses: - - - can turn many numpy expressions into C code - - dynamic compiling and loading of generated C code - - can embed pure C code in Python module and have weave extract, generate - interfaces and compile, etc. - - - Minuses: - - - Future very uncertain: it's the only part of Scipy not ported to Python 3 - and is effectively deprecated in favor of Cython. - -6) Psyco - - - Plusses: - - - Turns pure python into efficient machine code through jit-like - optimizations - - very fast when it optimizes well - - - Minuses: - - - Only on intel (windows?) - - Doesn't do much for numpy? - -Interfacing to Fortran: ------------------------ -The clear choice to wrap Fortran code is -`f2py <http://docs.scipy.org/doc/numpy/f2py/>`_. - -Pyfort is an older alternative, but not supported any longer. -Fwrap is a newer project that looked promising but isn't being developed any -longer. - -Interfacing to C++: -------------------- - 1) Cython - 2) CXX - 3) Boost.python - 4) SWIG - 5) SIP (used mainly in PyQT) - -""" -from __future__ import division, absolute_import, print_function diff --git a/numpy/doc/structured_arrays.py b/numpy/doc/structured_arrays.py deleted file mode 100644 index ab97c5df6141..000000000000 --- a/numpy/doc/structured_arrays.py +++ /dev/null @@ -1,605 +0,0 @@ -""" -================= -Structured Arrays -================= - -Introduction -============ - -Structured arrays are ndarrays whose datatype is a composition of simpler -datatypes organized as a sequence of named :term:`fields <field>`. For example, -:: - - >>> x = np.array([('Rex', 9, 81.0), ('Fido', 3, 27.0)], - ... dtype=[('name', 'U10'), ('age', 'i4'), ('weight', 'f4')]) - >>> x - array([('Rex', 9, 81.0), ('Fido', 3, 27.0)], - dtype=[('name', 'S10'), ('age', '<i4'), ('weight', '<f4')]) - -Here ``x`` is a one-dimensional array of length two whose datatype is a -structure with three fields: 1. A string of length 10 or less named 'name', 2. -a 32-bit integer named 'age', and 3. a 32-bit float named 'weight'. - -If you index ``x`` at position 1 you get a structure:: - - >>> x[1] - ('Fido', 3, 27.0) - -You can access and modify individual fields of a structured array by indexing -with the field name:: - - >>> x['age'] - array([9, 3], dtype=int32) - >>> x['age'] = 5 - >>> x - array([('Rex', 5, 81.0), ('Fido', 5, 27.0)], - dtype=[('name', 'S10'), ('age', '<i4'), ('weight', '<f4')]) - -Structured arrays are designed for low-level manipulation of structured data, -for example, for interpreting binary blobs. Structured datatypes are -designed to mimic 'structs' in the C language, making them also useful for -interfacing with C code. For these purposes, numpy supports specialized -features such as subarrays and nested datatypes, and allows manual control over -the memory layout of the structure. - -For simple manipulation of tabular data other pydata projects, such as pandas, -xarray, or DataArray, provide higher-level interfaces that may be more -suitable. These projects may also give better performance for tabular data -analysis because the C-struct-like memory layout of structured arrays can lead -to poor cache behavior. - -.. _defining-structured-types: - -Structured Datatypes -==================== - -To use structured arrays one first needs to define a structured datatype. - -A structured datatype can be thought of as a sequence of bytes of a certain -length (the structure's :term:`itemsize`) which is interpreted as a collection -of fields. Each field has a name, a datatype, and a byte offset within the -structure. The datatype of a field may be any numpy datatype including other -structured datatypes, and it may also be a :term:`sub-array` which behaves like -an ndarray of a specified shape. The offsets of the fields are arbitrary, and -fields may even overlap. These offsets are usually determined automatically by -numpy, but can also be specified. - -Structured Datatype Creation ----------------------------- - -Structured datatypes may be created using the function :func:`numpy.dtype`. -There are 4 alternative forms of specification which vary in flexibility and -conciseness. These are further documented in the -:ref:`Data Type Objects <arrays.dtypes.constructing>` reference page, and in -summary they are: - -1. A list of tuples, one tuple per field - - Each tuple has the form ``(fieldname, datatype, shape)`` where shape is - optional. ``fieldname`` is a string (or tuple if titles are used, see - :ref:`Field Titles <titles>` below), ``datatype`` may be any object - convertible to a datatype, and ``shape`` is a tuple of integers specifying - subarray shape. - - >>> np.dtype([('x', 'f4'), ('y', np.float32), ('z', 'f4', (2,2))]) - dtype=[('x', '<f4'), ('y', '<f4'), ('z', '<f4', (2, 2))]) - - If ``fieldname`` is the empty string ``''``, the field will be given a - default name of the form ``f#``, where ``#`` is the integer index of the - field, counting from 0 from the left:: - - >>> np.dtype([('x', 'f4'),('', 'i4'),('z', 'i8')]) - dtype([('x', '<f4'), ('f1', '<i4'), ('z', '<i8')]) - - The byte offsets of the fields within the structure and the total - structure itemsize are determined automatically. - -2. A string of comma-separated dtype specifications - - In this shorthand notation any of the :ref:`string dtype specifications - <arrays.dtypes.constructing>` may be used in a string and separated by - commas. The itemsize and byte offsets of the fields are determined - automatically, and the field names are given the default names ``f0``, - ``f1``, etc. :: - - >>> np.dtype('i8,f4,S3') - dtype([('f0', '<i8'), ('f1', '<f4'), ('f2', 'S3')]) - >>> np.dtype('3int8, float32, (2,3)float64') - dtype([('f0', 'i1', 3), ('f1', '<f4'), ('f2', '<f8', (2, 3))]) - -3. A dictionary of field parameter arrays - - This is the most flexible form of specification since it allows control - over the byte-offsets of the fields and the itemsize of the structure. - - The dictionary has two required keys, 'names' and 'formats', and four - optional keys, 'offsets', 'itemsize', 'aligned' and 'titles'. The values - for 'names' and 'formats' should respectively be a list of field names and - a list of dtype specifications, of the same length. The optional 'offsets' - value should be a list of integer byte-offsets, one for each field within - the structure. If 'offsets' is not given the offsets are determined - automatically. The optional 'itemsize' value should be an integer - describing the total size in bytes of the dtype, which must be large - enough to contain all the fields. - :: - - >>> np.dtype({'names': ['col1', 'col2'], 'formats': ['i4','f4']}) - dtype([('col1', '<i4'), ('col2', '<f4')]) - >>> np.dtype({'names': ['col1', 'col2'], - ... 'formats': ['i4','f4'], - ... 'offsets': [0, 4], - ... 'itemsize': 12}) - dtype({'names':['col1','col2'], 'formats':['<i4','<f4'], 'offsets':[0,4], 'itemsize':12}) - - Offsets may be chosen such that the fields overlap, though this will mean - that assigning to one field may clobber any overlapping field's data. As - an exception, fields of :class:`numpy.object` type cannot overlap with - other fields, because of the risk of clobbering the internal object - pointer and then dereferencing it. - - The optional 'aligned' value can be set to ``True`` to make the automatic - offset computation use aligned offsets (see :ref:`offsets-and-alignment`), - as if the 'align' keyword argument of :func:`numpy.dtype` had been set to - True. - - The optional 'titles' value should be a list of titles of the same length - as 'names', see :ref:`Field Titles <titles>` below. - -4. A dictionary of field names - - The use of this form of specification is discouraged, but documented here - because older numpy code may use it. The keys of the dictionary are the - field names and the values are tuples specifying type and offset:: - - >>> np.dtype=({'col1': ('i1',0), 'col2': ('f4',1)}) - dtype([(('col1'), 'i1'), (('col2'), '>f4')]) - - This form is discouraged because Python dictionaries do not preserve order - in Python versions before Python 3.6, and the order of the fields in a - structured dtype has meaning. :ref:`Field Titles <titles>` may be - specified by using a 3-tuple, see below. - -Manipulating and Displaying Structured Datatypes ------------------------------------------------- - -The list of field names of a structured datatype can be found in the ``names`` -attribute of the dtype object:: - - >>> d = np.dtype([('x', 'i8'), ('y', 'f4')]) - >>> d.names - ('x', 'y') - -The field names may be modified by assigning to the ``names`` attribute using a -sequence of strings of the same length. - -The dtype object also has a dictionary-like attribute, ``fields``, whose keys -are the field names (and :ref:`Field Titles <titles>`, see below) and whose -values are tuples containing the dtype and byte offset of each field. :: - - >>> d.fields - mappingproxy({'x': (dtype('int64'), 0), 'y': (dtype('float32'), 8)}) - -Both the ``names`` and ``fields`` attributes will equal ``None`` for -unstructured arrays. - -The string representation of a structured datatype is shown in the "list of -tuples" form if possible, otherwise numpy falls back to using the more general -dictionary form. - -.. _offsets-and-alignment: - -Automatic Byte Offsets and Alignment ------------------------------------- - -Numpy uses one of two methods to automatically determine the field byte offsets -and the overall itemsize of a structured datatype, depending on whether -``align=True`` was specified as a keyword argument to :func:`numpy.dtype`. - -By default (``align=False``), numpy will pack the fields together such that -each field starts at the byte offset the previous field ended, and the fields -are contiguous in memory. :: - - >>> def print_offsets(d): - ... print("offsets:", [d.fields[name][1] for name in d.names]) - ... print("itemsize:", d.itemsize) - >>> print_offsets(np.dtype('u1,u1,i4,u1,i8,u2')) - offsets: [0, 1, 2, 6, 7, 15] - itemsize: 17 - -If ``align=True`` is set, numpy will pad the structure in the same way many C -compilers would pad a C-struct. Aligned structures can give a performance -improvement in some cases, at the cost of increased datatype size. Padding -bytes are inserted between fields such that each field's byte offset will be a -multiple of that field's alignment, which is usually equal to the field's size -in bytes for simple datatypes, see :c:member:`PyArray_Descr.alignment`. The -structure will also have trailing padding added so that its itemsize is a -multiple of the largest field's alignment. :: - - >>> print_offsets(np.dtype('u1,u1,i4,u1,i8,u2', align=True)) - offsets: [0, 1, 4, 8, 16, 24] - itemsize: 32 - -Note that although almost all modern C compilers pad in this way by default, -padding in C structs is C-implementation-dependent so this memory layout is not -guaranteed to exactly match that of a corresponding struct in a C program. Some -work may be needed, either on the numpy side or the C side, to obtain exact -correspondence. - -If offsets were specified using the optional ``offsets`` key in the -dictionary-based dtype specification, setting ``align=True`` will check that -each field's offset is a multiple of its size and that the itemsize is a -multiple of the largest field size, and raise an exception if not. - -If the offsets of the fields and itemsize of a structured array satisfy the -alignment conditions, the array will have the ``ALIGNED`` :ref:`flag -<numpy.ndarray.flags>` set. - -A convenience function :func:`numpy.lib.recfunctions.repack_fields` converts an -aligned dtype or array to a packed one and vice versa. It takes either a dtype -or structured ndarray as an argument, and returns a copy with fields re-packed, -with or without padding bytes. - -.. _titles: - -Field Titles ------------- - -In addition to field names, fields may also have an associated :term:`title`, -an alternate name, which is sometimes used as an additional description or -alias for the field. The title may be used to index an array, just like a -field name. - -To add titles when using the list-of-tuples form of dtype specification, the -field name may be specified as a tuple of two strings instead of a single -string, which will be the field's title and field name respectively. For -example:: - - >>> np.dtype([(('my title', 'name'), 'f4')]) - -When using the first form of dictionary-based specification, the titles may be -supplied as an extra ``'titles'`` key as described above. When using the second -(discouraged) dictionary-based specification, the title can be supplied by -providing a 3-element tuple ``(datatype, offset, title)`` instead of the usual -2-element tuple:: - - >>> np.dtype({'name': ('i4', 0, 'my title')}) - -The ``dtype.fields`` dictionary will contain :term:`titles` as keys, if any -titles are used. This means effectively that a field with a title will be -represented twice in the fields dictionary. The tuple values for these fields -will also have a third element, the field title. Because of this, and because -the ``names`` attribute preserves the field order while the ``fields`` -attribute may not, it is recommended to iterate through the fields of a dtype -using the ``names`` attribute of the dtype, which will not list titles, as -in:: - - >>> for name in d.names: - ... print(d.fields[name][:2]) - -Union types ------------ - -Structured datatypes are implemented in numpy to have base type -:class:`numpy.void` by default, but it is possible to interpret other numpy -types as structured types using the ``(base_dtype, dtype)`` form of dtype -specification described in -:ref:`Data Type Objects <arrays.dtypes.constructing>`. Here, ``base_dtype`` is -the desired underlying dtype, and fields and flags will be copied from -``dtype``. This dtype is similar to a 'union' in C. - -Indexing and Assignment to Structured arrays -============================================ - -Assigning data to a Structured Array ------------------------------------- - -There are a number of ways to assign values to a structured array: Using python -tuples, using scalar values, or using other structured arrays. - -Assignment from Python Native Types (Tuples) -```````````````````````````````````````````` - -The simplest way to assign values to a structured array is using python tuples. -Each assigned value should be a tuple of length equal to the number of fields -in the array, and not a list or array as these will trigger numpy's -broadcasting rules. The tuple's elements are assigned to the successive fields -of the array, from left to right:: - - >>> x = np.array([(1,2,3),(4,5,6)], dtype='i8,f4,f8') - >>> x[1] = (7,8,9) - >>> x - array([(1, 2., 3.), (7, 8., 9.)], - dtype=[('f0', '<i8'), ('f1', '<f4'), ('f2', '<f8')]) - -Assignment from Scalars -``````````````````````` - -A scalar assigned to a structured element will be assigned to all fields. This -happens when a scalar is assigned to a structured array, or when an -unstructured array is assigned to a structured array:: - - >>> x = np.zeros(2, dtype='i8,f4,?,S1') - >>> x[:] = 3 - >>> x - array([(3, 3.0, True, b'3'), (3, 3.0, True, b'3')], - dtype=[('f0', '<i8'), ('f1', '<f4'), ('f2', '?'), ('f3', 'S1')]) - >>> x[:] = np.arange(2) - >>> x - array([(0, 0.0, False, b'0'), (1, 1.0, True, b'1')], - dtype=[('f0', '<i8'), ('f1', '<f4'), ('f2', '?'), ('f3', 'S1')]) - -Structured arrays can also be assigned to unstructured arrays, but only if the -structured datatype has just a single field:: - - >>> twofield = np.zeros(2, dtype=[('A', 'i4'), ('B', 'i4')]) - >>> onefield = np.zeros(2, dtype=[('A', 'i4')]) - >>> nostruct = np.zeros(2, dtype='i4') - >>> nostruct[:] = twofield - ValueError: Can't cast from structure to non-structure, except if the structure only has a single field. - >>> nostruct[:] = onefield - >>> nostruct - array([0, 0], dtype=int32) - -Assignment from other Structured Arrays -``````````````````````````````````````` - -Assignment between two structured arrays occurs as if the source elements had -been converted to tuples and then assigned to the destination elements. That -is, the first field of the source array is assigned to the first field of the -destination array, and the second field likewise, and so on, regardless of -field names. Structured arrays with a different number of fields cannot be -assigned to each other. Bytes of the destination structure which are not -included in any of the fields are unaffected. :: - - >>> a = np.zeros(3, dtype=[('a', 'i8'), ('b', 'f4'), ('c', 'S3')]) - >>> b = np.ones(3, dtype=[('x', 'f4'), ('y', 'S3'), ('z', 'O')]) - >>> b[:] = a - >>> b - array([(0.0, b'0.0', b''), (0.0, b'0.0', b''), (0.0, b'0.0', b'')], - dtype=[('x', '<f4'), ('y', 'S3'), ('z', 'O')]) - - -Assignment involving subarrays -`````````````````````````````` - -When assigning to fields which are subarrays, the assigned value will first be -broadcast to the shape of the subarray. - -Indexing Structured Arrays --------------------------- - -Accessing Individual Fields -``````````````````````````` - -Individual fields of a structured array may be accessed and modified by indexing -the array with the field name. :: - - >>> x = np.array([(1,2),(3,4)], dtype=[('foo', 'i8'), ('bar', 'f4')]) - >>> x['foo'] - array([1, 3]) - >>> x['foo'] = 10 - >>> x - array([(10, 2.), (10, 4.)], - dtype=[('foo', '<i8'), ('bar', '<f4')]) - -The resulting array is a view into the original array. It shares the same -memory locations and writing to the view will modify the original array. :: - - >>> y = x['bar'] - >>> y[:] = 10 - >>> x - array([(10, 5.), (10, 5.)], - dtype=[('foo', '<i8'), ('bar', '<f4')]) - -This view has the same dtype and itemsize as the indexed field, so it is -typically a non-structured array, except in the case of nested structures. - - >>> y.dtype, y.shape, y.strides - (dtype('float32'), (2,), (12,)) - -Accessing Multiple Fields -``````````````````````````` - -One can index and assign to a structured array with a multi-field index, where -the index is a list of field names. - -.. warning:: - The behavior of multi-field indexes will change from Numpy 1.15 to Numpy - 1.16. - -In Numpy 1.16, the result of indexing with a multi-field index will be a view -into the original array, as follows:: - - >>> a = np.zeros(3, dtype=[('a', 'i4'), ('b', 'i4'), ('c', 'f4')]) - >>> a[['a', 'c']] - array([(0, 0.), (0, 0.), (0, 0.)], - dtype={'names':['a','c'], 'formats':['<i4','<f4'], 'offsets':[0,8], 'itemsize':12}) - -Assignment to the view modifies the original array. The view's fields will be -in the order they were indexed. Note that unlike for single-field indexing, the -view's dtype has the same itemsize as the original array, and has fields at the -same offsets as in the original array, and unindexed fields are merely missing. - -In Numpy 1.15, indexing an array with a multi-field index returns a copy of -the result above for 1.16, but with fields packed together in memory as if -passed through :func:`numpy.lib.recfunctions.repack_fields`. This is the -behavior since Numpy 1.7. - -.. warning:: - The new behavior in Numpy 1.16 leads to extra "padding" bytes at the - location of unindexed fields. You will need to update any code which depends - on the data having a "packed" layout. For instance code such as:: - - >>> a[['a','c']].view('i8') # will fail in Numpy 1.16 - ValueError: When changing to a smaller dtype, its size must be a divisor of the size of original dtype - - will need to be changed. This code has raised a ``FutureWarning`` since - Numpy 1.12. - - The following is a recommended fix, which will behave identically in Numpy - 1.15 and Numpy 1.16:: - - >>> from numpy.lib.recfunctions import repack_fields - >>> repack_fields(a[['a','c']]).view('i8') # supported 1.15 and 1.16 - array([0, 0, 0]) - -Assigning to an array with a multi-field index will behave the same in Numpy -1.15 and Numpy 1.16. In both versions the assignment will modify the original -array:: - - >>> a[['a', 'c']] = (2, 3) - >>> a - array([(2, 0, 3.0), (2, 0, 3.0), (2, 0, 3.0)], - dtype=[('a', '<i8'), ('b', '<i4'), ('c', '<f8')]) - -This obeys the structured array assignment rules described above. For example, -this means that one can swap the values of two fields using appropriate -multi-field indexes:: - - >>> a[['a', 'c']] = a[['c', 'a']] - -Indexing with an Integer to get a Structured Scalar -``````````````````````````````````````````````````` - -Indexing a single element of a structured array (with an integer index) returns -a structured scalar:: - - >>> x = np.array([(1, 2., 3.)], dtype='i,f,f') - >>> scalar = x[0] - >>> scalar - (1, 2., 3.) - >>> type(scalar) - numpy.void - -Unlike other numpy scalars, structured scalars are mutable and act like views -into the original array, such that modifying the scalar will modify the -original array. Structured scalars also support access and assignment by field -name:: - - >>> x = np.array([(1,2),(3,4)], dtype=[('foo', 'i8'), ('bar', 'f4')]) - >>> s = x[0] - >>> s['bar'] = 100 - >>> x - array([(1, 100.), (3, 4.)], - dtype=[('foo', '<i8'), ('bar', '<f4')]) - -Similarly to tuples, structured scalars can also be indexed with an integer:: - - >>> scalar = np.array([(1, 2., 3.)], dtype='i,f,f')[0] - >>> scalar[0] - 1 - >>> scalar[1] = 4 - -Thus, tuples might be thought of as the native Python equivalent to numpy's -structured types, much like native python integers are the equivalent to -numpy's integer types. Structured scalars may be converted to a tuple by -calling :func:`ndarray.item`:: - - >>> scalar.item(), type(scalar.item()) - ((1, 2.0, 3.0), tuple) - -Viewing Structured Arrays Containing Objects --------------------------------------------- - -In order to prevent clobbering object pointers in fields of -:class:`numpy.object` type, numpy currently does not allow views of structured -arrays containing objects. - -Structure Comparison --------------------- - -If the dtypes of two void structured arrays are equal, testing the equality of -the arrays will result in a boolean array with the dimensions of the original -arrays, with elements set to ``True`` where all fields of the corresponding -structures are equal. Structured dtypes are equal if the field names, -dtypes and titles are the same, ignoring endianness, and the fields are in -the same order:: - - >>> a = np.zeros(2, dtype=[('a', 'i4'), ('b', 'i4')]) - >>> b = np.ones(2, dtype=[('a', 'i4'), ('b', 'i4')]) - >>> a == b - array([False, False]) - -Currently, if the dtypes of two void structured arrays are not equivalent the -comparison fails, returning the scalar value ``False``. This behavior is -deprecated as of numpy 1.10 and will raise an error or perform elementwise -comparison in the future. - -The ``<`` and ``>`` operators always return ``False`` when comparing void -structured arrays, and arithmetic and bitwise operations are not supported. - -Record Arrays -============= - -As an optional convenience numpy provides an ndarray subclass, -:class:`numpy.recarray`, and associated helper functions in the -:mod:`numpy.rec` submodule, that allows access to fields of structured arrays -by attribute instead of only by index. Record arrays also use a special -datatype, :class:`numpy.record`, that allows field access by attribute on the -structured scalars obtained from the array. - -The simplest way to create a record array is with :func:`numpy.rec.array`:: - - >>> recordarr = np.rec.array([(1,2.,'Hello'),(2,3.,"World")], - ... dtype=[('foo', 'i4'),('bar', 'f4'), ('baz', 'S10')]) - >>> recordarr.bar - array([ 2., 3.], dtype=float32) - >>> recordarr[1:2] - rec.array([(2, 3.0, 'World')], - dtype=[('foo', '<i4'), ('bar', '<f4'), ('baz', 'S10')]) - >>> recordarr[1:2].foo - array([2], dtype=int32) - >>> recordarr.foo[1:2] - array([2], dtype=int32) - >>> recordarr[1].baz - 'World' - -:func:`numpy.rec.array` can convert a wide variety of arguments into record -arrays, including structured arrays:: - - >>> arr = array([(1,2.,'Hello'),(2,3.,"World")], - ... dtype=[('foo', 'i4'), ('bar', 'f4'), ('baz', 'S10')]) - >>> recordarr = np.rec.array(arr) - -The :mod:`numpy.rec` module provides a number of other convenience functions for -creating record arrays, see :ref:`record array creation routines -<routines.array-creation.rec>`. - -A record array representation of a structured array can be obtained using the -appropriate :ref:`view`:: - - >>> arr = np.array([(1,2.,'Hello'),(2,3.,"World")], - ... dtype=[('foo', 'i4'),('bar', 'f4'), ('baz', 'a10')]) - >>> recordarr = arr.view(dtype=dtype((np.record, arr.dtype)), - ... type=np.recarray) - -For convenience, viewing an ndarray as type :class:`np.recarray` will -automatically convert to :class:`np.record` datatype, so the dtype can be left -out of the view:: - - >>> recordarr = arr.view(np.recarray) - >>> recordarr.dtype - dtype((numpy.record, [('foo', '<i4'), ('bar', '<f4'), ('baz', 'S10')])) - -To get back to a plain ndarray both the dtype and type must be reset. The -following view does so, taking into account the unusual case that the -recordarr was not a structured type:: - - >>> arr2 = recordarr.view(recordarr.dtype.fields or recordarr.dtype, np.ndarray) - -Record array fields accessed by index or by attribute are returned as a record -array if the field has a structured type but as a plain ndarray otherwise. :: - - >>> recordarr = np.rec.array([('Hello', (1,2)),("World", (3,4))], - ... dtype=[('foo', 'S6'),('bar', [('A', int), ('B', int)])]) - >>> type(recordarr.foo) - <type 'numpy.ndarray'> - >>> type(recordarr.bar) - <class 'numpy.core.records.recarray'> - -Note that if a field has the same name as an ndarray attribute, the ndarray -attribute takes precedence. Such fields will be inaccessible by attribute but -will still be accessible by index. - -""" -from __future__ import division, absolute_import, print_function diff --git a/numpy/doc/subclassing.py b/numpy/doc/subclassing.py deleted file mode 100644 index 3be3d94b3a61..000000000000 --- a/numpy/doc/subclassing.py +++ /dev/null @@ -1,752 +0,0 @@ -"""============================= -Subclassing ndarray in python -============================= - -Introduction ------------- - -Subclassing ndarray is relatively simple, but it has some complications -compared to other Python objects. On this page we explain the machinery -that allows you to subclass ndarray, and the implications for -implementing a subclass. - -ndarrays and object creation -============================ - -Subclassing ndarray is complicated by the fact that new instances of -ndarray classes can come about in three different ways. These are: - -#. Explicit constructor call - as in ``MySubClass(params)``. This is - the usual route to Python instance creation. -#. View casting - casting an existing ndarray as a given subclass -#. New from template - creating a new instance from a template - instance. Examples include returning slices from a subclassed array, - creating return types from ufuncs, and copying arrays. See - :ref:`new-from-template` for more details - -The last two are characteristics of ndarrays - in order to support -things like array slicing. The complications of subclassing ndarray are -due to the mechanisms numpy has to support these latter two routes of -instance creation. - -.. _view-casting: - -View casting ------------- - -*View casting* is the standard ndarray mechanism by which you take an -ndarray of any subclass, and return a view of the array as another -(specified) subclass: - ->>> import numpy as np ->>> # create a completely useless ndarray subclass ->>> class C(np.ndarray): pass ->>> # create a standard ndarray ->>> arr = np.zeros((3,)) ->>> # take a view of it, as our useless subclass ->>> c_arr = arr.view(C) ->>> type(c_arr) -<class 'C'> - -.. _new-from-template: - -Creating new from template --------------------------- - -New instances of an ndarray subclass can also come about by a very -similar mechanism to :ref:`view-casting`, when numpy finds it needs to -create a new instance from a template instance. The most obvious place -this has to happen is when you are taking slices of subclassed arrays. -For example: - ->>> v = c_arr[1:] ->>> type(v) # the view is of type 'C' -<class 'C'> ->>> v is c_arr # but it's a new instance -False - -The slice is a *view* onto the original ``c_arr`` data. So, when we -take a view from the ndarray, we return a new ndarray, of the same -class, that points to the data in the original. - -There are other points in the use of ndarrays where we need such views, -such as copying arrays (``c_arr.copy()``), creating ufunc output arrays -(see also :ref:`array-wrap`), and reducing methods (like -``c_arr.mean()``. - -Relationship of view casting and new-from-template --------------------------------------------------- - -These paths both use the same machinery. We make the distinction here, -because they result in different input to your methods. Specifically, -:ref:`view-casting` means you have created a new instance of your array -type from any potential subclass of ndarray. :ref:`new-from-template` -means you have created a new instance of your class from a pre-existing -instance, allowing you - for example - to copy across attributes that -are particular to your subclass. - -Implications for subclassing ----------------------------- - -If we subclass ndarray, we need to deal not only with explicit -construction of our array type, but also :ref:`view-casting` or -:ref:`new-from-template`. NumPy has the machinery to do this, and this -machinery that makes subclassing slightly non-standard. - -There are two aspects to the machinery that ndarray uses to support -views and new-from-template in subclasses. - -The first is the use of the ``ndarray.__new__`` method for the main work -of object initialization, rather then the more usual ``__init__`` -method. The second is the use of the ``__array_finalize__`` method to -allow subclasses to clean up after the creation of views and new -instances from templates. - -A brief Python primer on ``__new__`` and ``__init__`` -===================================================== - -``__new__`` is a standard Python method, and, if present, is called -before ``__init__`` when we create a class instance. See the `python -__new__ documentation -<http://docs.python.org/reference/datamodel.html#object.__new__>`_ for more detail. - -For example, consider the following Python code: - -.. testcode:: - - class C(object): - def __new__(cls, *args): - print('Cls in __new__:', cls) - print('Args in __new__:', args) - return object.__new__(cls, *args) - - def __init__(self, *args): - print('type(self) in __init__:', type(self)) - print('Args in __init__:', args) - -meaning that we get: - ->>> c = C('hello') -Cls in __new__: <class 'C'> -Args in __new__: ('hello',) -type(self) in __init__: <class 'C'> -Args in __init__: ('hello',) - -When we call ``C('hello')``, the ``__new__`` method gets its own class -as first argument, and the passed argument, which is the string -``'hello'``. After python calls ``__new__``, it usually (see below) -calls our ``__init__`` method, with the output of ``__new__`` as the -first argument (now a class instance), and the passed arguments -following. - -As you can see, the object can be initialized in the ``__new__`` -method or the ``__init__`` method, or both, and in fact ndarray does -not have an ``__init__`` method, because all the initialization is -done in the ``__new__`` method. - -Why use ``__new__`` rather than just the usual ``__init__``? Because -in some cases, as for ndarray, we want to be able to return an object -of some other class. Consider the following: - -.. testcode:: - - class D(C): - def __new__(cls, *args): - print('D cls is:', cls) - print('D args in __new__:', args) - return C.__new__(C, *args) - - def __init__(self, *args): - # we never get here - print('In D __init__') - -meaning that: - ->>> obj = D('hello') -D cls is: <class 'D'> -D args in __new__: ('hello',) -Cls in __new__: <class 'C'> -Args in __new__: ('hello',) ->>> type(obj) -<class 'C'> - -The definition of ``C`` is the same as before, but for ``D``, the -``__new__`` method returns an instance of class ``C`` rather than -``D``. Note that the ``__init__`` method of ``D`` does not get -called. In general, when the ``__new__`` method returns an object of -class other than the class in which it is defined, the ``__init__`` -method of that class is not called. - -This is how subclasses of the ndarray class are able to return views -that preserve the class type. When taking a view, the standard -ndarray machinery creates the new ndarray object with something -like:: - - obj = ndarray.__new__(subtype, shape, ... - -where ``subdtype`` is the subclass. Thus the returned view is of the -same class as the subclass, rather than being of class ``ndarray``. - -That solves the problem of returning views of the same type, but now -we have a new problem. The machinery of ndarray can set the class -this way, in its standard methods for taking views, but the ndarray -``__new__`` method knows nothing of what we have done in our own -``__new__`` method in order to set attributes, and so on. (Aside - -why not call ``obj = subdtype.__new__(...`` then? Because we may not -have a ``__new__`` method with the same call signature). - -The role of ``__array_finalize__`` -================================== - -``__array_finalize__`` is the mechanism that numpy provides to allow -subclasses to handle the various ways that new instances get created. - -Remember that subclass instances can come about in these three ways: - -#. explicit constructor call (``obj = MySubClass(params)``). This will - call the usual sequence of ``MySubClass.__new__`` then (if it exists) - ``MySubClass.__init__``. -#. :ref:`view-casting` -#. :ref:`new-from-template` - -Our ``MySubClass.__new__`` method only gets called in the case of the -explicit constructor call, so we can't rely on ``MySubClass.__new__`` or -``MySubClass.__init__`` to deal with the view casting and -new-from-template. It turns out that ``MySubClass.__array_finalize__`` -*does* get called for all three methods of object creation, so this is -where our object creation housekeeping usually goes. - -* For the explicit constructor call, our subclass will need to create a - new ndarray instance of its own class. In practice this means that - we, the authors of the code, will need to make a call to - ``ndarray.__new__(MySubClass,...)``, a class-hierarchy prepared call to - ``super(MySubClass, cls).__new__(cls, ...)``, or do view casting of an - existing array (see below) -* For view casting and new-from-template, the equivalent of - ``ndarray.__new__(MySubClass,...`` is called, at the C level. - -The arguments that ``__array_finalize__`` receives differ for the three -methods of instance creation above. - -The following code allows us to look at the call sequences and arguments: - -.. testcode:: - - import numpy as np - - class C(np.ndarray): - def __new__(cls, *args, **kwargs): - print('In __new__ with class %s' % cls) - return super(C, cls).__new__(cls, *args, **kwargs) - - def __init__(self, *args, **kwargs): - # in practice you probably will not need or want an __init__ - # method for your subclass - print('In __init__ with class %s' % self.__class__) - - def __array_finalize__(self, obj): - print('In array_finalize:') - print(' self type is %s' % type(self)) - print(' obj type is %s' % type(obj)) - - -Now: - ->>> # Explicit constructor ->>> c = C((10,)) -In __new__ with class <class 'C'> -In array_finalize: - self type is <class 'C'> - obj type is <type 'NoneType'> -In __init__ with class <class 'C'> ->>> # View casting ->>> a = np.arange(10) ->>> cast_a = a.view(C) -In array_finalize: - self type is <class 'C'> - obj type is <type 'numpy.ndarray'> ->>> # Slicing (example of new-from-template) ->>> cv = c[:1] -In array_finalize: - self type is <class 'C'> - obj type is <class 'C'> - -The signature of ``__array_finalize__`` is:: - - def __array_finalize__(self, obj): - -One sees that the ``super`` call, which goes to -``ndarray.__new__``, passes ``__array_finalize__`` the new object, of our -own class (``self``) as well as the object from which the view has been -taken (``obj``). As you can see from the output above, the ``self`` is -always a newly created instance of our subclass, and the type of ``obj`` -differs for the three instance creation methods: - -* When called from the explicit constructor, ``obj`` is ``None`` -* When called from view casting, ``obj`` can be an instance of any - subclass of ndarray, including our own. -* When called in new-from-template, ``obj`` is another instance of our - own subclass, that we might use to update the new ``self`` instance. - -Because ``__array_finalize__`` is the only method that always sees new -instances being created, it is the sensible place to fill in instance -defaults for new object attributes, among other tasks. - -This may be clearer with an example. - -Simple example - adding an extra attribute to ndarray ------------------------------------------------------ - -.. testcode:: - - import numpy as np - - class InfoArray(np.ndarray): - - def __new__(subtype, shape, dtype=float, buffer=None, offset=0, - strides=None, order=None, info=None): - # Create the ndarray instance of our type, given the usual - # ndarray input arguments. This will call the standard - # ndarray constructor, but return an object of our type. - # It also triggers a call to InfoArray.__array_finalize__ - obj = super(InfoArray, subtype).__new__(subtype, shape, dtype, - buffer, offset, strides, - order) - # set the new 'info' attribute to the value passed - obj.info = info - # Finally, we must return the newly created object: - return obj - - def __array_finalize__(self, obj): - # ``self`` is a new object resulting from - # ndarray.__new__(InfoArray, ...), therefore it only has - # attributes that the ndarray.__new__ constructor gave it - - # i.e. those of a standard ndarray. - # - # We could have got to the ndarray.__new__ call in 3 ways: - # From an explicit constructor - e.g. InfoArray(): - # obj is None - # (we're in the middle of the InfoArray.__new__ - # constructor, and self.info will be set when we return to - # InfoArray.__new__) - if obj is None: return - # From view casting - e.g arr.view(InfoArray): - # obj is arr - # (type(obj) can be InfoArray) - # From new-from-template - e.g infoarr[:3] - # type(obj) is InfoArray - # - # Note that it is here, rather than in the __new__ method, - # that we set the default value for 'info', because this - # method sees all creation of default objects - with the - # InfoArray.__new__ constructor, but also with - # arr.view(InfoArray). - self.info = getattr(obj, 'info', None) - # We do not need to return anything - - -Using the object looks like this: - - >>> obj = InfoArray(shape=(3,)) # explicit constructor - >>> type(obj) - <class 'InfoArray'> - >>> obj.info is None - True - >>> obj = InfoArray(shape=(3,), info='information') - >>> obj.info - 'information' - >>> v = obj[1:] # new-from-template - here - slicing - >>> type(v) - <class 'InfoArray'> - >>> v.info - 'information' - >>> arr = np.arange(10) - >>> cast_arr = arr.view(InfoArray) # view casting - >>> type(cast_arr) - <class 'InfoArray'> - >>> cast_arr.info is None - True - -This class isn't very useful, because it has the same constructor as the -bare ndarray object, including passing in buffers and shapes and so on. -We would probably prefer the constructor to be able to take an already -formed ndarray from the usual numpy calls to ``np.array`` and return an -object. - -Slightly more realistic example - attribute added to existing array -------------------------------------------------------------------- - -Here is a class that takes a standard ndarray that already exists, casts -as our type, and adds an extra attribute. - -.. testcode:: - - import numpy as np - - class RealisticInfoArray(np.ndarray): - - def __new__(cls, input_array, info=None): - # Input array is an already formed ndarray instance - # We first cast to be our class type - obj = np.asarray(input_array).view(cls) - # add the new attribute to the created instance - obj.info = info - # Finally, we must return the newly created object: - return obj - - def __array_finalize__(self, obj): - # see InfoArray.__array_finalize__ for comments - if obj is None: return - self.info = getattr(obj, 'info', None) - - -So: - - >>> arr = np.arange(5) - >>> obj = RealisticInfoArray(arr, info='information') - >>> type(obj) - <class 'RealisticInfoArray'> - >>> obj.info - 'information' - >>> v = obj[1:] - >>> type(v) - <class 'RealisticInfoArray'> - >>> v.info - 'information' - -.. _array-ufunc: - -``__array_ufunc__`` for ufuncs ------------------------------- - - .. versionadded:: 1.13 - -A subclass can override what happens when executing numpy ufuncs on it by -overriding the default ``ndarray.__array_ufunc__`` method. This method is -executed *instead* of the ufunc and should return either the result of the -operation, or :obj:`NotImplemented` if the operation requested is not -implemented. - -The signature of ``__array_ufunc__`` is:: - - def __array_ufunc__(ufunc, method, *inputs, **kwargs): - - - *ufunc* is the ufunc object that was called. - - *method* is a string indicating how the Ufunc was called, either - ``"__call__"`` to indicate it was called directly, or one of its - :ref:`methods<ufuncs.methods>`: ``"reduce"``, ``"accumulate"``, - ``"reduceat"``, ``"outer"``, or ``"at"``. - - *inputs* is a tuple of the input arguments to the ``ufunc`` - - *kwargs* contains any optional or keyword arguments passed to the - function. This includes any ``out`` arguments, which are always - contained in a tuple. - -A typical implementation would convert any inputs or outputs that are -instances of one's own class, pass everything on to a superclass using -``super()``, and finally return the results after possible -back-conversion. An example, taken from the test case -``test_ufunc_override_with_super`` in ``core/tests/test_umath.py``, is the -following. - -.. testcode:: - - input numpy as np - - class A(np.ndarray): - def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): - args = [] - in_no = [] - for i, input_ in enumerate(inputs): - if isinstance(input_, A): - in_no.append(i) - args.append(input_.view(np.ndarray)) - else: - args.append(input_) - - outputs = kwargs.pop('out', None) - out_no = [] - if outputs: - out_args = [] - for j, output in enumerate(outputs): - if isinstance(output, A): - out_no.append(j) - out_args.append(output.view(np.ndarray)) - else: - out_args.append(output) - kwargs['out'] = tuple(out_args) - else: - outputs = (None,) * ufunc.nout - - info = {} - if in_no: - info['inputs'] = in_no - if out_no: - info['outputs'] = out_no - - results = super(A, self).__array_ufunc__(ufunc, method, - *args, **kwargs) - if results is NotImplemented: - return NotImplemented - - if method == 'at': - if isinstance(inputs[0], A): - inputs[0].info = info - return - - if ufunc.nout == 1: - results = (results,) - - results = tuple((np.asarray(result).view(A) - if output is None else output) - for result, output in zip(results, outputs)) - if results and isinstance(results[0], A): - results[0].info = info - - return results[0] if len(results) == 1 else results - -So, this class does not actually do anything interesting: it just -converts any instances of its own to regular ndarray (otherwise, we'd -get infinite recursion!), and adds an ``info`` dictionary that tells -which inputs and outputs it converted. Hence, e.g., - ->>> a = np.arange(5.).view(A) ->>> b = np.sin(a) ->>> b.info -{'inputs': [0]} ->>> b = np.sin(np.arange(5.), out=(a,)) ->>> b.info -{'outputs': [0]} ->>> a = np.arange(5.).view(A) ->>> b = np.ones(1).view(A) ->>> c = a + b ->>> c.info -{'inputs': [0, 1]} ->>> a += b ->>> a.info -{'inputs': [0, 1], 'outputs': [0]} - -Note that another approach would be to to use ``getattr(ufunc, -methods)(*inputs, **kwargs)`` instead of the ``super`` call. For this example, -the result would be identical, but there is a difference if another operand -also defines ``__array_ufunc__``. E.g., lets assume that we evalulate -``np.add(a, b)``, where ``b`` is an instance of another class ``B`` that has -an override. If you use ``super`` as in the example, -``ndarray.__array_ufunc__`` will notice that ``b`` has an override, which -means it cannot evaluate the result itself. Thus, it will return -`NotImplemented` and so will our class ``A``. Then, control will be passed -over to ``b``, which either knows how to deal with us and produces a result, -or does not and returns `NotImplemented`, raising a ``TypeError``. - -If instead, we replace our ``super`` call with ``getattr(ufunc, method)``, we -effectively do ``np.add(a.view(np.ndarray), b)``. Again, ``B.__array_ufunc__`` -will be called, but now it sees an ``ndarray`` as the other argument. Likely, -it will know how to handle this, and return a new instance of the ``B`` class -to us. Our example class is not set up to handle this, but it might well be -the best approach if, e.g., one were to re-implement ``MaskedArray`` using -``__array_ufunc__``. - -As a final note: if the ``super`` route is suited to a given class, an -advantage of using it is that it helps in constructing class hierarchies. -E.g., suppose that our other class ``B`` also used the ``super`` in its -``__array_ufunc__`` implementation, and we created a class ``C`` that depended -on both, i.e., ``class C(A, B)`` (with, for simplicity, not another -``__array_ufunc__`` override). Then any ufunc on an instance of ``C`` would -pass on to ``A.__array_ufunc__``, the ``super`` call in ``A`` would go to -``B.__array_ufunc__``, and the ``super`` call in ``B`` would go to -``ndarray.__array_ufunc__``, thus allowing ``A`` and ``B`` to collaborate. - -.. _array-wrap: - -``__array_wrap__`` for ufuncs and other functions -------------------------------------------------- - -Prior to numpy 1.13, the behaviour of ufuncs could only be tuned using -``__array_wrap__`` and ``__array_prepare__``. These two allowed one to -change the output type of a ufunc, but, in contrast to -``__array_ufunc__``, did not allow one to make any changes to the inputs. -It is hoped to eventually deprecate these, but ``__array_wrap__`` is also -used by other numpy functions and methods, such as ``squeeze``, so at the -present time is still needed for full functionality. - -Conceptually, ``__array_wrap__`` "wraps up the action" in the sense of -allowing a subclass to set the type of the return value and update -attributes and metadata. Let's show how this works with an example. First -we return to the simpler example subclass, but with a different name and -some print statements: - -.. testcode:: - - import numpy as np - - class MySubClass(np.ndarray): - - def __new__(cls, input_array, info=None): - obj = np.asarray(input_array).view(cls) - obj.info = info - return obj - - def __array_finalize__(self, obj): - print('In __array_finalize__:') - print(' self is %s' % repr(self)) - print(' obj is %s' % repr(obj)) - if obj is None: return - self.info = getattr(obj, 'info', None) - - def __array_wrap__(self, out_arr, context=None): - print('In __array_wrap__:') - print(' self is %s' % repr(self)) - print(' arr is %s' % repr(out_arr)) - # then just call the parent - return super(MySubClass, self).__array_wrap__(self, out_arr, context) - -We run a ufunc on an instance of our new array: - ->>> obj = MySubClass(np.arange(5), info='spam') -In __array_finalize__: - self is MySubClass([0, 1, 2, 3, 4]) - obj is array([0, 1, 2, 3, 4]) ->>> arr2 = np.arange(5)+1 ->>> ret = np.add(arr2, obj) -In __array_wrap__: - self is MySubClass([0, 1, 2, 3, 4]) - arr is array([1, 3, 5, 7, 9]) -In __array_finalize__: - self is MySubClass([1, 3, 5, 7, 9]) - obj is MySubClass([0, 1, 2, 3, 4]) ->>> ret -MySubClass([1, 3, 5, 7, 9]) ->>> ret.info -'spam' - -Note that the ufunc (``np.add``) has called the ``__array_wrap__`` method -with arguments ``self`` as ``obj``, and ``out_arr`` as the (ndarray) result -of the addition. In turn, the default ``__array_wrap__`` -(``ndarray.__array_wrap__``) has cast the result to class ``MySubClass``, -and called ``__array_finalize__`` - hence the copying of the ``info`` -attribute. This has all happened at the C level. - -But, we could do anything we wanted: - -.. testcode:: - - class SillySubClass(np.ndarray): - - def __array_wrap__(self, arr, context=None): - return 'I lost your data' - ->>> arr1 = np.arange(5) ->>> obj = arr1.view(SillySubClass) ->>> arr2 = np.arange(5) ->>> ret = np.multiply(obj, arr2) ->>> ret -'I lost your data' - -So, by defining a specific ``__array_wrap__`` method for our subclass, -we can tweak the output from ufuncs. The ``__array_wrap__`` method -requires ``self``, then an argument - which is the result of the ufunc - -and an optional parameter *context*. This parameter is returned by -ufuncs as a 3-element tuple: (name of the ufunc, arguments of the ufunc, -domain of the ufunc), but is not set by other numpy functions. Though, -as seen above, it is possible to do otherwise, ``__array_wrap__`` should -return an instance of its containing class. See the masked array -subclass for an implementation. - -In addition to ``__array_wrap__``, which is called on the way out of the -ufunc, there is also an ``__array_prepare__`` method which is called on -the way into the ufunc, after the output arrays are created but before any -computation has been performed. The default implementation does nothing -but pass through the array. ``__array_prepare__`` should not attempt to -access the array data or resize the array, it is intended for setting the -output array type, updating attributes and metadata, and performing any -checks based on the input that may be desired before computation begins. -Like ``__array_wrap__``, ``__array_prepare__`` must return an ndarray or -subclass thereof or raise an error. - -Extra gotchas - custom ``__del__`` methods and ndarray.base ------------------------------------------------------------ - -One of the problems that ndarray solves is keeping track of memory -ownership of ndarrays and their views. Consider the case where we have -created an ndarray, ``arr`` and have taken a slice with ``v = arr[1:]``. -The two objects are looking at the same memory. NumPy keeps track of -where the data came from for a particular array or view, with the -``base`` attribute: - ->>> # A normal ndarray, that owns its own data ->>> arr = np.zeros((4,)) ->>> # In this case, base is None ->>> arr.base is None -True ->>> # We take a view ->>> v1 = arr[1:] ->>> # base now points to the array that it derived from ->>> v1.base is arr -True ->>> # Take a view of a view ->>> v2 = v1[1:] ->>> # base points to the view it derived from ->>> v2.base is v1 -True - -In general, if the array owns its own memory, as for ``arr`` in this -case, then ``arr.base`` will be None - there are some exceptions to this -- see the numpy book for more details. - -The ``base`` attribute is useful in being able to tell whether we have -a view or the original array. This in turn can be useful if we need -to know whether or not to do some specific cleanup when the subclassed -array is deleted. For example, we may only want to do the cleanup if -the original array is deleted, but not the views. For an example of -how this can work, have a look at the ``memmap`` class in -``numpy.core``. - -Subclassing and Downstream Compatibility ----------------------------------------- - -When sub-classing ``ndarray`` or creating duck-types that mimic the ``ndarray`` -interface, it is your responsibility to decide how aligned your APIs will be -with those of numpy. For convenience, many numpy functions that have a corresponding -``ndarray`` method (e.g., ``sum``, ``mean``, ``take``, ``reshape``) work by checking -if the first argument to a function has a method of the same name. If it exists, the -method is called instead of coercing the arguments to a numpy array. - -For example, if you want your sub-class or duck-type to be compatible with -numpy's ``sum`` function, the method signature for this object's ``sum`` method -should be the following: - -.. testcode:: - - def sum(self, axis=None, dtype=None, out=None, keepdims=False): - ... - -This is the exact same method signature for ``np.sum``, so now if a user calls -``np.sum`` on this object, numpy will call the object's own ``sum`` method and -pass in these arguments enumerated above in the signature, and no errors will -be raised because the signatures are completely compatible with each other. - -If, however, you decide to deviate from this signature and do something like this: - -.. testcode:: - - def sum(self, axis=None, dtype=None): - ... - -This object is no longer compatible with ``np.sum`` because if you call ``np.sum``, -it will pass in unexpected arguments ``out`` and ``keepdims``, causing a TypeError -to be raised. - -If you wish to maintain compatibility with numpy and its subsequent versions (which -might add new keyword arguments) but do not want to surface all of numpy's arguments, -your function's signature should accept ``**kwargs``. For example: - -.. testcode:: - - def sum(self, axis=None, dtype=None, **unused_kwargs): - ... - -This object is now compatible with ``np.sum`` again because any extraneous arguments -(i.e. keywords that are not ``axis`` or ``dtype``) will be hidden away in the -``**unused_kwargs`` parameter. - -""" -from __future__ import division, absolute_import, print_function diff --git a/numpy/doc/ufuncs.py b/numpy/doc/ufuncs.py index a112e559cb05..eecc15083d53 100644 --- a/numpy/doc/ufuncs.py +++ b/numpy/doc/ufuncs.py @@ -13,9 +13,9 @@ >>> np.array([0,2,3,4]) + np.array([1,1,-1,2]) array([1, 3, 2, 6]) -The unfunc module lists all the available ufuncs in numpy. Documentation on +The ufunc module lists all the available ufuncs in numpy. Documentation on the specific ufuncs may be found in those modules. This documentation is -intended to address the more general aspects of unfuncs common to most of +intended to address the more general aspects of ufuncs common to most of them. All of the ufuncs that make use of Python operators (e.g., +, -, etc.) have equivalent functions defined (e.g. add() for +) @@ -135,4 +135,3 @@ a convenient way to apply these operators. """ -from __future__ import division, absolute_import, print_function diff --git a/numpy/dual.py b/numpy/dual.py index 8b91da262037..eb7e61aac085 100644 --- a/numpy/dual.py +++ b/numpy/dual.py @@ -1,16 +1,28 @@ """ -Aliases for functions which may be accelerated by Scipy. +.. deprecated:: 1.20 -Scipy_ can be built to use accelerated or otherwise improved libraries +*This module is deprecated. Instead of importing functions from* +``numpy.dual``, *the functions should be imported directly from NumPy +or SciPy*. + +Aliases for functions which may be accelerated by SciPy. + +SciPy_ can be built to use accelerated or otherwise improved libraries for FFTs, linear algebra, and special functions. This module allows developers to transparently support these accelerated functions when -scipy is available but still support users who have only installed +SciPy is available but still support users who have only installed NumPy. -.. _Scipy : http://www.scipy.org +.. _SciPy : https://www.scipy.org """ -from __future__ import division, absolute_import, print_function +import warnings + + +warnings.warn('The module numpy.dual is deprecated. Instead of using dual, ' + 'use the functions directly from numpy or scipy.', + category=DeprecationWarning, + stacklevel=2) # This module should be used for functions both in numpy and scipy if # you want to use the numpy version if available but the scipy version @@ -51,14 +63,14 @@ def register_func(name, func): if name not in __all__: - raise ValueError("%s not a dual function." % name) + raise ValueError("{} not a dual function.".format(name)) f = sys._getframe(0).f_globals _restore_dict[name] = f[name] f[name] = func def restore_func(name): if name not in __all__: - raise ValueError("%s not a dual function." % name) + raise ValueError("{} not a dual function.".format(name)) try: val = _restore_dict[name] except KeyError: diff --git a/numpy/f2py/__init__.py b/numpy/f2py/__init__.py index 5075c682d68f..f147f1b970a3 100644 --- a/numpy/f2py/__init__.py +++ b/numpy/f2py/__init__.py @@ -1,15 +1,14 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """Fortran to Python Interface Generator. """ -from __future__ import division, absolute_import, print_function - -__all__ = ['run_main', 'compile', 'f2py_testing'] +__all__ = ['run_main', 'compile', 'get_include'] import sys +import subprocess +import os from . import f2py2e -from . import f2py_testing from . import diagnose run_main = f2py2e.run_main @@ -21,19 +20,28 @@ def compile(source, extra_args='', verbose=True, source_fn=None, - extension='.f' + extension='.f', + full_output=False ): """ - Build extension module from processing source with f2py. + Build extension module from a Fortran 77 source string with f2py. Parameters ---------- - source : str + source : str or bytes Fortran source of module / subroutine to compile + + .. versionchanged:: 1.16.0 + Accept str as well as bytes + modulename : str, optional The name of the compiled python module - extra_args : str, optional + extra_args : str or list, optional Additional parameters passed to f2py + + .. versionchanged:: 1.16.0 + A list of args may also be provided. + verbose : bool, optional Print f2py output to screen source_fn : str, optional @@ -47,28 +55,146 @@ def compile(source, .. versionadded:: 1.11.0 + full_output : bool, optional + If True, return a `subprocess.CompletedProcess` containing + the stdout and stderr of the compile process, instead of just + the status code. + + .. versionadded:: 1.20.0 + + + Returns + ------- + result : int or `subprocess.CompletedProcess` + 0 on success, or a `subprocess.CompletedProcess` if + ``full_output=True`` + + Examples + -------- + .. literalinclude:: ../../source/f2py/code/results/compile_session.dat + :language: python + """ - from numpy.distutils.exec_command import exec_command import tempfile + import shlex + if source_fn is None: - f = tempfile.NamedTemporaryFile(suffix=extension) + f, fname = tempfile.mkstemp(suffix=extension) + # f is a file descriptor so need to close it + # carefully -- not with .close() directly + os.close(f) else: - f = open(source_fn, 'w') + fname = source_fn + if not isinstance(source, str): + source = str(source, 'utf-8') try: - f.write(source) - f.flush() - - args = ' -c -m {} {} {}'.format(modulename, f.name, extra_args) - c = '{} -c "import numpy.f2py as f2py2e;f2py2e.main()" {}' - c = c.format(sys.executable, args) - status, output = exec_command(c) - if verbose: - print(output) + with open(fname, 'w') as f: + f.write(source) + + args = ['-c', '-m', modulename, f.name] + + if isinstance(extra_args, str): + is_posix = (os.name == 'posix') + extra_args = shlex.split(extra_args, posix=is_posix) + + args.extend(extra_args) + + c = [sys.executable, + '-c', + 'import numpy.f2py as f2py2e;f2py2e.main()'] + args + try: + cp = subprocess.run(c, stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + except OSError: + # preserve historic status code used by exec_command() + cp = subprocess.CompletedProcess(c, 127, stdout=b'', stderr=b'') + else: + if verbose: + print(cp.stdout.decode()) finally: - f.close() - return status + if source_fn is None: + os.remove(fname) + + if full_output: + return cp + else: + return cp.returncode + + +def get_include(): + """ + Return the directory that contains the fortranobject.c and .h files. + + .. note:: + + This function is not needed when building an extension with + `numpy.distutils` directly from ``.f`` and/or ``.pyf`` files + in one go. + + Python extension modules built with f2py-generated code need to use + ``fortranobject.c`` as a source file, and include the ``fortranobject.h`` + header. This function can be used to obtain the directory containing + both of these files. + + Returns + ------- + include_path : str + Absolute path to the directory containing ``fortranobject.c`` and + ``fortranobject.h``. + + Notes + ----- + .. versionadded:: 1.22.0 + + Unless the build system you are using has specific support for f2py, + building a Python extension using a ``.pyf`` signature file is a two-step + process. For a module ``mymod``: + + - Step 1: run ``python -m numpy.f2py mymod.pyf --quiet``. This + generates ``_mymodmodule.c`` and (if needed) + ``_fblas-f2pywrappers.f`` files next to ``mymod.pyf``. + - Step 2: build your Python extension module. This requires the + following source files: + + - ``_mymodmodule.c`` + - ``_mymod-f2pywrappers.f`` (if it was generated in step 1) + - ``fortranobject.c`` + + See Also + -------- + numpy.get_include : function that returns the numpy include directory + + """ + return os.path.join(os.path.dirname(__file__), 'src') + + +if sys.version_info[:2] >= (3, 7): + # module level getattr is only supported in 3.7 onwards + # https://www.python.org/dev/peps/pep-0562/ + def __getattr__(attr): + + # Avoid importing things that aren't needed for building + # which might import the main numpy module + if attr == "f2py_testing": + import numpy.f2py.f2py_testing as f2py_testing + return f2py_testing + + elif attr == "test": + from numpy._pytesttester import PytestTester + test = PytestTester(__name__) + return test + + else: + raise AttributeError("module {!r} has no attribute " + "{!r}".format(__name__, attr)) + + def __dir__(): + return list(globals().keys() | {"f2py_testing", "test"}) + +else: + from . import f2py_testing -from numpy.testing._private.pytesttester import PytestTester -test = PytestTester(__name__) -del PytestTester + from numpy._pytesttester import PytestTester + test = PytestTester(__name__) + del PytestTester diff --git a/numpy/f2py/__init__.pyi b/numpy/f2py/__init__.pyi new file mode 100644 index 000000000000..e52e12bbd156 --- /dev/null +++ b/numpy/f2py/__init__.pyi @@ -0,0 +1,42 @@ +import os +import subprocess +from typing import Literal as L, Any, List, Iterable, Dict, overload, TypedDict + +from numpy._pytesttester import PytestTester + +class _F2PyDictBase(TypedDict): + csrc: List[str] + h: List[str] + +class _F2PyDict(_F2PyDictBase, total=False): + fsrc: List[str] + ltx: List[str] + +__all__: List[str] +__path__: List[str] +test: PytestTester + +def run_main(comline_list: Iterable[str]) -> Dict[str, _F2PyDict]: ... + +@overload +def compile( # type: ignore[misc] + source: str | bytes, + modulename: str = ..., + extra_args: str | List[str] = ..., + verbose: bool = ..., + source_fn: None | str | bytes | os.PathLike[Any] = ..., + extension: L[".f", ".f90"] = ..., + full_output: L[False] = ..., +) -> int: ... +@overload +def compile( + source: str | bytes, + modulename: str = ..., + extra_args: str | List[str] = ..., + verbose: bool = ..., + source_fn: None | str | bytes | os.PathLike[Any] = ..., + extension: L[".f", ".f90"] = ..., + full_output: L[True] = ..., +) -> subprocess.CompletedProcess[bytes]: ... + +def get_include() -> str: ... diff --git a/numpy/f2py/__main__.py b/numpy/f2py/__main__.py index cb8f261c1b9e..936a753a2796 100644 --- a/numpy/f2py/__main__.py +++ b/numpy/f2py/__main__.py @@ -1,27 +1,5 @@ -# See http://cens.ioc.ee/projects/f2py2e/ -from __future__ import division, print_function +# See: +# https://web.archive.org/web/20140822061353/http://cens.ioc.ee/projects/f2py2e +from numpy.f2py.f2py2e import main -import os -import sys -for mode in ["g3-numpy", "2e-numeric", "2e-numarray", "2e-numpy"]: - try: - i = sys.argv.index("--" + mode) - del sys.argv[i] - break - except ValueError: - pass -os.environ["NO_SCIPY_IMPORT"] = "f2py" -if mode == "g3-numpy": - sys.stderr.write("G3 f2py support is not implemented, yet.\\n") - sys.exit(1) -elif mode == "2e-numeric": - from f2py2e import main -elif mode == "2e-numarray": - sys.argv.append("-DNUMARRAY") - from f2py2e import main -elif mode == "2e-numpy": - from numpy.f2py import main -else: - sys.stderr.write("Unknown mode: " + repr(mode) + "\\n") - sys.exit(1) main() diff --git a/numpy/f2py/__version__.py b/numpy/f2py/__version__.py index 49a2199bf38b..e20d7c1dbb38 100644 --- a/numpy/f2py/__version__.py +++ b/numpy/f2py/__version__.py @@ -1,10 +1 @@ -from __future__ import division, absolute_import, print_function - -major = 2 - -try: - from __svn_version__ import version - version_info = (major, version) - version = '%s_%s' % version_info -except (ImportError, ValueError): - version = str(major) +from numpy.version import version diff --git a/numpy/f2py/auxfuncs.py b/numpy/f2py/auxfuncs.py index 404bdbd2d62a..c8f2067c9351 100644 --- a/numpy/f2py/auxfuncs.py +++ b/numpy/f2py/auxfuncs.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """ Auxiliary functions for f2py2e. @@ -14,8 +14,6 @@ Pearu Peterson """ -from __future__ import division, absolute_import, print_function - import pprint import sys import types @@ -259,6 +257,7 @@ def ismodule(rout): def isfunction(rout): return 'block' in rout and 'function' == rout['block'] + def isfunction_wrap(rout): if isintent_c(rout): return 0 @@ -286,6 +285,10 @@ def hasassumedshape(rout): return False +def requiresf90wrapper(rout): + return ismoduleroutine(rout) or hasassumedshape(rout) + + def isroutine(rout): return isfunction(rout) or issubroutine(rout) @@ -344,9 +347,9 @@ def iscomplexfunction_warn(rout): ************************************************************** Warning: code with a function returning complex value may not work correctly with your Fortran compiler. - Run the following test before using it in your applications: - $(f2py install dir)/test-site/{b/runme_scalar,e/runme} - When using GNU gcc/g77 compilers, codes should work correctly. + When using GNU gcc/g77 compilers, codes should work + correctly for callbacks with: + f2py -c -DF2PY_CB_RETURNCOMPLEX **************************************************************\n""") return 1 return 0 @@ -552,7 +555,7 @@ class F2PYError(Exception): pass -class throw_error(object): +class throw_error: def __init__(self, mess): self.mess = mess diff --git a/numpy/f2py/capi_maps.py b/numpy/f2py/capi_maps.py index 8e63d3cff67c..581f946e5a21 100644 --- a/numpy/f2py/capi_maps.py +++ b/numpy/f2py/capi_maps.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """ Copyright 1999,2000 Pearu Peterson all rights reserved, @@ -11,21 +11,16 @@ Pearu Peterson """ -from __future__ import division, absolute_import, print_function - -__version__ = "$Revision: 1.60 $"[10:-1] - from . import __version__ f2py_version = __version__.version import copy import re import os -import sys from .crackfortran import markoutercomma from . import cb_rules -# The eviroment provided by auxfuncs.py is needed for some calls to eval. +# The environment provided by auxfuncs.py is needed for some calls to eval. # As the needed functions cannot be determined by static inspection of the # code, it is safest to use import * pending a major refactoring of f2py. from .auxfuncs import * @@ -53,7 +48,7 @@ 'unsigned_char': 'int', # forced casting 'short': 'int', # forced casting 'unsigned_short': 'int', # forced casting - 'int': 'int', # (forced casting) + 'int': 'int', # forced casting 'long': 'int', 'long_long': 'long', 'unsigned': 'int', # forced casting @@ -79,7 +74,7 @@ 'complex_long_double': 'NPY_CDOUBLE', # forced casting 'string': 'NPY_STRING'} -# These new maps aren't used anyhere yet, but should be by default +# These new maps aren't used anywhere yet, but should be by default # unless building numeric or numarray extensions. if using_newcore: c2capi_map = {'double': 'NPY_DOUBLE', @@ -100,8 +95,8 @@ 'complex_double': 'NPY_CDOUBLE', 'complex_long_double': 'NPY_CDOUBLE', 'string':'NPY_STRING' - } + c2pycode_map = {'double': 'd', 'float': 'f', 'long_double': 'd', # forced casting @@ -119,6 +114,7 @@ 'complex_long_double': 'D', # forced casting 'string': 'c' } + if using_newcore: c2pycode_map = {'double': 'd', 'float': 'f', @@ -138,6 +134,7 @@ 'complex_double': 'D', 'complex_long_double': 'G', 'string': 'S'} + c2buildvalue_map = {'double': 'd', 'float': 'f', 'char': 'b', @@ -149,15 +146,7 @@ 'complex_float': 'N', 'complex_double': 'N', 'complex_long_double': 'N', - 'string': 'z'} - -if sys.version_info[0] >= 3: - # Bytes, not Unicode strings - c2buildvalue_map['string'] = 'y' - -if using_newcore: - # c2buildvalue_map=??? - pass + 'string': 'y'} f2cmap_all = {'real': {'': 'float', '4': 'float', '8': 'double', '12': 'long_double', '16': 'long_double'}, @@ -179,25 +168,37 @@ 'character': {'': 'string'} } -if os.path.isfile('.f2py_f2cmap'): +f2cmap_default = copy.deepcopy(f2cmap_all) + + +def load_f2cmap_file(f2cmap_file): + global f2cmap_all + + f2cmap_all = copy.deepcopy(f2cmap_default) + + if f2cmap_file is None: + # Default value + f2cmap_file = '.f2py_f2cmap' + if not os.path.isfile(f2cmap_file): + return + # User defined additions to f2cmap_all. - # .f2py_f2cmap must contain a dictionary of dictionaries, only. For + # f2cmap_file must contain a dictionary of dictionaries, only. For # example, {'real':{'low':'float'}} means that Fortran 'real(low)' is - # interpreted as C 'float'. This feature is useful for F90/95 users if - # they use PARAMETERSs in type specifications. + # interpreted as C 'float'. This feature is useful for F90/95 users if + # they use PARAMETERS in type specifications. try: - outmess('Reading .f2py_f2cmap ...\n') - f = open('.f2py_f2cmap', 'r') - d = eval(f.read(), {}, {}) - f.close() - for k, d1 in list(d.items()): - for k1 in list(d1.keys()): + outmess('Reading f2cmap from {!r} ...\n'.format(f2cmap_file)) + with open(f2cmap_file, 'r') as f: + d = eval(f.read(), {}, {}) + for k, d1 in d.items(): + for k1 in d1.keys(): d1[k1.lower()] = d1[k1] d[k.lower()] = d[k] - for k in list(d.keys()): + for k in d.keys(): if k not in f2cmap_all: f2cmap_all[k] = {} - for k1 in list(d[k].keys()): + for k1 in d[k].keys(): if d[k][k1] in c2py_map: if k1 in f2cmap_all[k]: outmess( @@ -208,10 +209,10 @@ else: errmess("\tIgnoring map {'%s':{'%s':'%s'}}: '%s' must be in %s\n" % ( k, k1, d[k][k1], d[k][k1], list(c2py_map.keys()))) - outmess('Successfully applied user defined changes from .f2py_f2cmap\n') + outmess('Successfully applied user defined f2cmap changes\n') except Exception as msg: errmess( - 'Failed to apply user defined changes from .f2py_f2cmap: %s. Skipping.\n' % (msg)) + 'Failed to apply user defined f2cmap changes: %s. Skipping.\n' % (msg)) cformat_map = {'double': '%g', 'float': '%g', @@ -276,11 +277,9 @@ def getctype(var): errmess('getctype: "%s(kind=%s)" is mapped to C "%s" (to override define dict(%s = dict(%s="<C typespec>")) in %s/.f2py_f2cmap file).\n' % (typespec, var['kindselector']['kind'], ctype, typespec, var['kindselector']['kind'], os.getcwd())) - else: if not isexternal(var): - errmess( - 'getctype: No C-type found in "%s", assuming void.\n' % var) + errmess('getctype: No C-type found in "%s", assuming void.\n' % var) return ctype @@ -304,7 +303,7 @@ def getstrlength(var): len = a['*'] elif 'len' in a: len = a['len'] - if re.match(r'\(\s*([*]|[:])\s*\)', len) or re.match(r'([*]|[:])', len): + if re.match(r'\(\s*(\*|:)\s*\)', len) or re.match(r'(\*|:)', len): if isintent_hide(var): errmess('getstrlength:intent(hide): expected a string with defined length but got: %s\n' % ( repr(var))) @@ -313,7 +312,6 @@ def getstrlength(var): def getarrdims(a, var, verbose=0): - global depargs ret = {} if isstring(var) and not isarray(var): ret['dims'] = getstrlength(var) @@ -444,7 +442,7 @@ def getpydocsign(a, var): sigout = sig else: errmess( - 'getpydocsign: Could not resolve docsignature for "%s".\\n' % a) + 'getpydocsign: Could not resolve docsignature for "%s".\n' % a) return sig, sigout @@ -509,7 +507,6 @@ def sign2map(a, var): varrfromat intent """ - global lcb_map, cb_map out_a = a if isintent_out(var): for k in var['intent']: @@ -522,7 +519,7 @@ def sign2map(a, var): if f(var): intent_flags.append('F2PY_%s' % s) if intent_flags: - # XXX: Evaluate intent_flags here. + # TODO: Evaluate intent_flags here. ret['intent'] = '|'.join(intent_flags) else: ret['intent'] = 'F2PY_INTENT_IN' @@ -718,10 +715,7 @@ def modsign2map(m): def cb_sign2map(a, var, index=None): ret = {'varname': a} - if index is None or 1: # disable 7712 patch - ret['varname_i'] = ret['varname'] - else: - ret['varname_i'] = ret['varname'] + '_' + str(index) + ret['varname_i'] = ret['varname'] ret['ctype'] = getctype(var) if ret['ctype'] in c2capi_map: ret['atype'] = c2capi_map[ret['ctype']] diff --git a/numpy/f2py/cb_rules.py b/numpy/f2py/cb_rules.py index 183d7c2f920b..4848233d43a5 100644 --- a/numpy/f2py/cb_rules.py +++ b/numpy/f2py/cb_rules.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """ Build call-back mechanism for f2py2e. @@ -13,8 +13,6 @@ Pearu Peterson """ -from __future__ import division, absolute_import, print_function - from . import __version__ from .auxfuncs import ( applyrules, debugcapi, dictappend, errmess, getargs, hasnote, isarray, @@ -35,123 +33,165 @@ 'cbtypedefs': 'typedef #rctype#(*#name#_typedef)(#optargs_td##args_td##strarglens_td##noargs#);', 'body': """ #begintitle# -PyObject *#name#_capi = NULL;/*was Py_None*/ -PyTupleObject *#name#_args_capi = NULL; -int #name#_nofargs = 0; -jmp_buf #name#_jmpbuf; +typedef struct { + PyObject *capi; + PyTupleObject *args_capi; + int nofargs; + jmp_buf jmpbuf; +} #name#_t; + +#if defined(F2PY_THREAD_LOCAL_DECL) && !defined(F2PY_USE_PYTHON_TLS) + +static F2PY_THREAD_LOCAL_DECL #name#_t *_active_#name# = NULL; + +static #name#_t *swap_active_#name#(#name#_t *ptr) { + #name#_t *prev = _active_#name#; + _active_#name# = ptr; + return prev; +} + +static #name#_t *get_active_#name#(void) { + return _active_#name#; +} + +#else + +static #name#_t *swap_active_#name#(#name#_t *ptr) { + char *key = "__f2py_cb_#name#"; + return (#name#_t *)F2PySwapThreadLocalCallbackPtr(key, ptr); +} + +static #name#_t *get_active_#name#(void) { + char *key = "__f2py_cb_#name#"; + return (#name#_t *)F2PyGetThreadLocalCallbackPtr(key); +} + +#endif + /*typedef #rctype#(*#name#_typedef)(#optargs_td##args_td##strarglens_td##noargs#);*/ #static# #rctype# #callbackname# (#optargs##args##strarglens##noargs#) { -\tPyTupleObject *capi_arglist = #name#_args_capi; -\tPyObject *capi_return = NULL; -\tPyObject *capi_tmp = NULL; -\tPyObject *capi_arglist_list = NULL; -\tint capi_j,capi_i = 0; -\tint capi_longjmp_ok = 1; + #name#_t cb_local = { NULL, NULL, 0 }; + #name#_t *cb = NULL; + PyTupleObject *capi_arglist = NULL; + PyObject *capi_return = NULL; + PyObject *capi_tmp = NULL; + PyObject *capi_arglist_list = NULL; + int capi_j,capi_i = 0; + int capi_longjmp_ok = 1; #decl# #ifdef F2PY_REPORT_ATEXIT f2py_cb_start_clock(); #endif -\tCFUNCSMESS(\"cb:Call-back function #name# (maxnofargs=#maxnofargs#(-#nofoptargs#))\\n\"); -\tCFUNCSMESSPY(\"cb:#name#_capi=\",#name#_capi); -\tif (#name#_capi==NULL) { -\t\tcapi_longjmp_ok = 0; -\t\t#name#_capi = PyObject_GetAttrString(#modulename#_module,\"#argname#\"); -\t} -\tif (#name#_capi==NULL) { -\t\tPyErr_SetString(#modulename#_error,\"cb: Callback #argname# not defined (as an argument or module #modulename# attribute).\\n\"); -\t\tgoto capi_fail; -\t} -\tif (F2PyCapsule_Check(#name#_capi)) { -\t#name#_typedef #name#_cptr; -\t#name#_cptr = F2PyCapsule_AsVoidPtr(#name#_capi); -\t#returncptr#(*#name#_cptr)(#optargs_nm##args_nm##strarglens_nm#); -\t#return# -\t} -\tif (capi_arglist==NULL) { -\t\tcapi_longjmp_ok = 0; -\t\tcapi_tmp = PyObject_GetAttrString(#modulename#_module,\"#argname#_extra_args\"); -\t\tif (capi_tmp) { -\t\t\tcapi_arglist = (PyTupleObject *)PySequence_Tuple(capi_tmp); -\t\t\tif (capi_arglist==NULL) { -\t\t\t\tPyErr_SetString(#modulename#_error,\"Failed to convert #modulename#.#argname#_extra_args to tuple.\\n\"); -\t\t\t\tgoto capi_fail; -\t\t\t} -\t\t} else { -\t\t\tPyErr_Clear(); -\t\t\tcapi_arglist = (PyTupleObject *)Py_BuildValue(\"()\"); -\t\t} -\t} -\tif (capi_arglist == NULL) { -\t\tPyErr_SetString(#modulename#_error,\"Callback #argname# argument list is not set.\\n\"); -\t\tgoto capi_fail; -\t} + cb = get_active_#name#(); + if (cb == NULL) { + capi_longjmp_ok = 0; + cb = &cb_local; + } + capi_arglist = cb->args_capi; + CFUNCSMESS(\"cb:Call-back function #name# (maxnofargs=#maxnofargs#(-#nofoptargs#))\\n\"); + CFUNCSMESSPY(\"cb:#name#_capi=\",cb->capi); + if (cb->capi==NULL) { + capi_longjmp_ok = 0; + cb->capi = PyObject_GetAttrString(#modulename#_module,\"#argname#\"); + CFUNCSMESSPY(\"cb:#name#_capi=\",cb->capi); + } + if (cb->capi==NULL) { + PyErr_SetString(#modulename#_error,\"cb: Callback #argname# not defined (as an argument or module #modulename# attribute).\\n\"); + goto capi_fail; + } + if (F2PyCapsule_Check(cb->capi)) { + #name#_typedef #name#_cptr; + #name#_cptr = F2PyCapsule_AsVoidPtr(cb->capi); + #returncptr#(*#name#_cptr)(#optargs_nm##args_nm##strarglens_nm#); + #return# + } + if (capi_arglist==NULL) { + capi_longjmp_ok = 0; + capi_tmp = PyObject_GetAttrString(#modulename#_module,\"#argname#_extra_args\"); + if (capi_tmp) { + capi_arglist = (PyTupleObject *)PySequence_Tuple(capi_tmp); + Py_DECREF(capi_tmp); + if (capi_arglist==NULL) { + PyErr_SetString(#modulename#_error,\"Failed to convert #modulename#.#argname#_extra_args to tuple.\\n\"); + goto capi_fail; + } + } else { + PyErr_Clear(); + capi_arglist = (PyTupleObject *)Py_BuildValue(\"()\"); + } + } + if (capi_arglist == NULL) { + PyErr_SetString(#modulename#_error,\"Callback #argname# argument list is not set.\\n\"); + goto capi_fail; + } #setdims# #ifdef PYPY_VERSION #define CAPI_ARGLIST_SETITEM(idx, value) PyList_SetItem((PyObject *)capi_arglist_list, idx, value) -\tcapi_arglist_list = PySequence_List(capi_arglist); -\tif (capi_arglist_list == NULL) goto capi_fail; + capi_arglist_list = PySequence_List(capi_arglist); + if (capi_arglist_list == NULL) goto capi_fail; #else #define CAPI_ARGLIST_SETITEM(idx, value) PyTuple_SetItem((PyObject *)capi_arglist, idx, value) #endif #pyobjfrom# #undef CAPI_ARGLIST_SETITEM #ifdef PYPY_VERSION -\tCFUNCSMESSPY(\"cb:capi_arglist=\",capi_arglist_list); + CFUNCSMESSPY(\"cb:capi_arglist=\",capi_arglist_list); #else -\tCFUNCSMESSPY(\"cb:capi_arglist=\",capi_arglist); + CFUNCSMESSPY(\"cb:capi_arglist=\",capi_arglist); #endif -\tCFUNCSMESS(\"cb:Call-back calling Python function #argname#.\\n\"); + CFUNCSMESS(\"cb:Call-back calling Python function #argname#.\\n\"); #ifdef F2PY_REPORT_ATEXIT f2py_cb_start_call_clock(); #endif #ifdef PYPY_VERSION -\tcapi_return = PyObject_CallObject(#name#_capi,(PyObject *)capi_arglist_list); -\tPy_DECREF(capi_arglist_list); -\tcapi_arglist_list = NULL; + capi_return = PyObject_CallObject(cb->capi,(PyObject *)capi_arglist_list); + Py_DECREF(capi_arglist_list); + capi_arglist_list = NULL; #else -\tcapi_return = PyObject_CallObject(#name#_capi,(PyObject *)capi_arglist); + capi_return = PyObject_CallObject(cb->capi,(PyObject *)capi_arglist); #endif #ifdef F2PY_REPORT_ATEXIT f2py_cb_stop_call_clock(); #endif -\tCFUNCSMESSPY(\"cb:capi_return=\",capi_return); -\tif (capi_return == NULL) { -\t\tfprintf(stderr,\"capi_return is NULL\\n\"); -\t\tgoto capi_fail; -\t} -\tif (capi_return == Py_None) { -\t\tPy_DECREF(capi_return); -\t\tcapi_return = Py_BuildValue(\"()\"); -\t} -\telse if (!PyTuple_Check(capi_return)) { -\t\tcapi_return = Py_BuildValue(\"(N)\",capi_return); -\t} -\tcapi_j = PyTuple_Size(capi_return); -\tcapi_i = 0; + CFUNCSMESSPY(\"cb:capi_return=\",capi_return); + if (capi_return == NULL) { + fprintf(stderr,\"capi_return is NULL\\n\"); + goto capi_fail; + } + if (capi_return == Py_None) { + Py_DECREF(capi_return); + capi_return = Py_BuildValue(\"()\"); + } + else if (!PyTuple_Check(capi_return)) { + capi_return = Py_BuildValue(\"(N)\",capi_return); + } + capi_j = PyTuple_Size(capi_return); + capi_i = 0; #frompyobj# -\tCFUNCSMESS(\"cb:#name#:successful\\n\"); -\tPy_DECREF(capi_return); + CFUNCSMESS(\"cb:#name#:successful\\n\"); + Py_DECREF(capi_return); #ifdef F2PY_REPORT_ATEXIT f2py_cb_stop_clock(); #endif -\tgoto capi_return_pt; + goto capi_return_pt; capi_fail: -\tfprintf(stderr,\"Call-back #name# failed.\\n\"); -\tPy_XDECREF(capi_return); -\tPy_XDECREF(capi_arglist_list); -\tif (capi_longjmp_ok) -\t\tlongjmp(#name#_jmpbuf,-1); + fprintf(stderr,\"Call-back #name# failed.\\n\"); + Py_XDECREF(capi_return); + Py_XDECREF(capi_arglist_list); + if (capi_longjmp_ok) { + longjmp(cb->jmpbuf,-1); + } capi_return_pt: -\t; + ; #return# } #endtitle# """, - 'need': ['setjmp.h', 'CFUNCSMESS'], + 'need': ['setjmp.h', 'CFUNCSMESS', 'F2PY_THREAD_LOCAL_DECL'], 'maxnofargs': '#maxnofargs#', 'nofoptargs': '#nofoptargs#', 'docstr': """\ -\tdef #argname#(#docsignature#): return #docreturn#\\n\\ + def #argname#(#docsignature#): return #docreturn#\\n\\ #docstrsigns#""", 'latexdocstr': """ {{}\\verb@def #argname#(#latexdocsignature#): return #docreturn#@{}} @@ -179,10 +219,10 @@ 'noargs': '', 'setdims': '/*setdims*/', 'docstrsigns': '', 'latexdocstrsigns': '', - 'docstrreq': '\tRequired arguments:', - 'docstropt': '\tOptional arguments:', - 'docstrout': '\tReturn objects:', - 'docstrcbs': '\tCall-back functions:', + 'docstrreq': ' Required arguments:', + 'docstropt': ' Optional arguments:', + 'docstrout': ' Return objects:', + 'docstrcbs': ' Call-back functions:', 'docreturn': '', 'docsign': '', 'docsignopt': '', 'latexdocstrreq': '\\noindent Required arguments:', 'latexdocstropt': '\\noindent Optional arguments:', @@ -190,26 +230,26 @@ 'latexdocstrcbs': '\\noindent Call-back functions:', 'routnote': {hasnote: '--- #note#', l_not(hasnote): ''}, }, { # Function - 'decl': '\t#ctype# return_value;', - 'frompyobj': [{debugcapi: '\tCFUNCSMESS("cb:Getting return_value->");'}, - '\tif (capi_j>capi_i)\n\t\tGETSCALARFROMPYTUPLE(capi_return,capi_i++,&return_value,#ctype#,"#ctype#_from_pyobj failed in converting return_value of call-back function #name# to C #ctype#\\n");', + 'decl': ' #ctype# return_value;', + 'frompyobj': [{debugcapi: ' CFUNCSMESS("cb:Getting return_value->");'}, + ' if (capi_j>capi_i)\n GETSCALARFROMPYTUPLE(capi_return,capi_i++,&return_value,#ctype#,"#ctype#_from_pyobj failed in converting return_value of call-back function #name# to C #ctype#\\n");', {debugcapi: - '\tfprintf(stderr,"#showvalueformat#.\\n",return_value);'} + ' fprintf(stderr,"#showvalueformat#.\\n",return_value);'} ], 'need': ['#ctype#_from_pyobj', {debugcapi: 'CFUNCSMESS'}, 'GETSCALARFROMPYTUPLE'], - 'return': '\treturn return_value;', + 'return': ' return return_value;', '_check': l_and(isfunction, l_not(isstringfunction), l_not(iscomplexfunction)) }, { # String function - 'pyobjfrom': {debugcapi: '\tfprintf(stderr,"debug-capi:cb:#name#:%d:\\n",return_value_len);'}, + 'pyobjfrom': {debugcapi: ' fprintf(stderr,"debug-capi:cb:#name#:%d:\\n",return_value_len);'}, 'args': '#ctype# return_value,int return_value_len', 'args_nm': 'return_value,&return_value_len', 'args_td': '#ctype# ,int', - 'frompyobj': [{debugcapi: '\tCFUNCSMESS("cb:Getting return_value->\\"");'}, - """\tif (capi_j>capi_i) -\t\tGETSTRFROMPYTUPLE(capi_return,capi_i++,return_value,return_value_len);""", + 'frompyobj': [{debugcapi: ' CFUNCSMESS("cb:Getting return_value->\\"");'}, + """ if (capi_j>capi_i) + GETSTRFROMPYTUPLE(capi_return,capi_i++,return_value,return_value_len);""", {debugcapi: - '\tfprintf(stderr,"#showvalueformat#\\".\\n",return_value);'} + ' fprintf(stderr,"#showvalueformat#\\".\\n",return_value);'} ], 'need': ['#ctype#_from_pyobj', {debugcapi: 'CFUNCSMESS'}, 'string.h', 'GETSTRFROMPYTUPLE'], @@ -234,39 +274,39 @@ """, 'decl': """ #ifdef F2PY_CB_RETURNCOMPLEX -\t#ctype# return_value; + #ctype# return_value; #endif """, - 'frompyobj': [{debugcapi: '\tCFUNCSMESS("cb:Getting return_value->");'}, + 'frompyobj': [{debugcapi: ' CFUNCSMESS("cb:Getting return_value->");'}, """\ -\tif (capi_j>capi_i) + if (capi_j>capi_i) #ifdef F2PY_CB_RETURNCOMPLEX -\t\tGETSCALARFROMPYTUPLE(capi_return,capi_i++,&return_value,#ctype#,\"#ctype#_from_pyobj failed in converting return_value of call-back function #name# to C #ctype#\\n\"); + GETSCALARFROMPYTUPLE(capi_return,capi_i++,&return_value,#ctype#,\"#ctype#_from_pyobj failed in converting return_value of call-back function #name# to C #ctype#\\n\"); #else -\t\tGETSCALARFROMPYTUPLE(capi_return,capi_i++,return_value,#ctype#,\"#ctype#_from_pyobj failed in converting return_value of call-back function #name# to C #ctype#\\n\"); + GETSCALARFROMPYTUPLE(capi_return,capi_i++,return_value,#ctype#,\"#ctype#_from_pyobj failed in converting return_value of call-back function #name# to C #ctype#\\n\"); #endif """, {debugcapi: """ #ifdef F2PY_CB_RETURNCOMPLEX -\tfprintf(stderr,\"#showvalueformat#.\\n\",(return_value).r,(return_value).i); + fprintf(stderr,\"#showvalueformat#.\\n\",(return_value).r,(return_value).i); #else -\tfprintf(stderr,\"#showvalueformat#.\\n\",(*return_value).r,(*return_value).i); + fprintf(stderr,\"#showvalueformat#.\\n\",(*return_value).r,(*return_value).i); #endif """} ], 'return': """ #ifdef F2PY_CB_RETURNCOMPLEX -\treturn return_value; + return return_value; #else -\treturn; + return; #endif """, 'need': ['#ctype#_from_pyobj', {debugcapi: 'CFUNCSMESS'}, 'string.h', 'GETSCALARFROMPYTUPLE', '#ctype#'], '_check': iscomplexfunction }, - {'docstrout': '\t\t#pydocsignout#', + {'docstrout': ' #pydocsignout#', 'latexdocstrout': ['\\item[]{{}\\verb@#pydocsignout#@{}}', {hasnote: '--- #note#'}], 'docreturn': '#rname#,', @@ -276,9 +316,9 @@ cb_arg_rules = [ { # Doc - 'docstropt': {l_and(isoptional, isintent_nothide): '\t\t#pydocsign#'}, - 'docstrreq': {l_and(isrequired, isintent_nothide): '\t\t#pydocsign#'}, - 'docstrout': {isintent_out: '\t\t#pydocsignout#'}, + 'docstropt': {l_and(isoptional, isintent_nothide): ' #pydocsign#'}, + 'docstrreq': {l_and(isrequired, isintent_nothide): ' #pydocsign#'}, + 'docstrout': {isintent_out: ' #pydocsignout#'}, 'latexdocstropt': {l_and(isoptional, isintent_nothide): ['\\item[]{{}\\verb@#pydocsign#@{}}', {hasnote: '--- #note#'}]}, 'latexdocstrreq': {l_and(isrequired, isintent_nothide): ['\\item[]{{}\\verb@#pydocsign#@{}}', @@ -309,6 +349,7 @@ isarray: '#ctype# *', isstring: '#ctype#' }, + 'need': {l_or(isscalar, isarray, isstring): '#ctype#'}, # untested with multiple args 'strarglens': {isstring: ',int #varname_i#_cb_len'}, 'strarglens_td': {isstring: ',int'}, # untested with multiple args @@ -316,61 +357,61 @@ 'strarglens_nm': {isstring: ',#varname_i#_cb_len'}, }, { # Scalars - 'decl': {l_not(isintent_c): '\t#ctype# #varname_i#=(*#varname_i#_cb_capi);'}, + 'decl': {l_not(isintent_c): ' #ctype# #varname_i#=(*#varname_i#_cb_capi);'}, 'error': {l_and(isintent_c, isintent_out, throw_error('intent(c,out) is forbidden for callback scalar arguments')): ''}, - 'frompyobj': [{debugcapi: '\tCFUNCSMESS("cb:Getting #varname#->");'}, + 'frompyobj': [{debugcapi: ' CFUNCSMESS("cb:Getting #varname#->");'}, {isintent_out: - '\tif (capi_j>capi_i)\n\t\tGETSCALARFROMPYTUPLE(capi_return,capi_i++,#varname_i#_cb_capi,#ctype#,"#ctype#_from_pyobj failed in converting argument #varname# of call-back function #name# to C #ctype#\\n");'}, + ' if (capi_j>capi_i)\n GETSCALARFROMPYTUPLE(capi_return,capi_i++,#varname_i#_cb_capi,#ctype#,"#ctype#_from_pyobj failed in converting argument #varname# of call-back function #name# to C #ctype#\\n");'}, {l_and(debugcapi, l_and(l_not(iscomplex), isintent_c)): - '\tfprintf(stderr,"#showvalueformat#.\\n",#varname_i#);'}, + ' fprintf(stderr,"#showvalueformat#.\\n",#varname_i#);'}, {l_and(debugcapi, l_and(l_not(iscomplex), l_not( isintent_c))): - '\tfprintf(stderr,"#showvalueformat#.\\n",*#varname_i#_cb_capi);'}, + ' fprintf(stderr,"#showvalueformat#.\\n",*#varname_i#_cb_capi);'}, {l_and(debugcapi, l_and(iscomplex, isintent_c)): - '\tfprintf(stderr,"#showvalueformat#.\\n",(#varname_i#).r,(#varname_i#).i);'}, + ' fprintf(stderr,"#showvalueformat#.\\n",(#varname_i#).r,(#varname_i#).i);'}, {l_and(debugcapi, l_and(iscomplex, l_not( isintent_c))): - '\tfprintf(stderr,"#showvalueformat#.\\n",(*#varname_i#_cb_capi).r,(*#varname_i#_cb_capi).i);'}, + ' fprintf(stderr,"#showvalueformat#.\\n",(*#varname_i#_cb_capi).r,(*#varname_i#_cb_capi).i);'}, ], 'need': [{isintent_out: ['#ctype#_from_pyobj', 'GETSCALARFROMPYTUPLE']}, {debugcapi: 'CFUNCSMESS'}], '_check': isscalar }, { 'pyobjfrom': [{isintent_in: """\ -\tif (#name#_nofargs>capi_i) -\t\tif (CAPI_ARGLIST_SETITEM(capi_i++,pyobj_from_#ctype#1(#varname_i#))) -\t\t\tgoto capi_fail;"""}, + if (cb->nofargs>capi_i) + if (CAPI_ARGLIST_SETITEM(capi_i++,pyobj_from_#ctype#1(#varname_i#))) + goto capi_fail;"""}, {isintent_inout: """\ -\tif (#name#_nofargs>capi_i) -\t\tif (CAPI_ARGLIST_SETITEM(capi_i++,pyarr_from_p_#ctype#1(#varname_i#_cb_capi))) -\t\t\tgoto capi_fail;"""}], + if (cb->nofargs>capi_i) + if (CAPI_ARGLIST_SETITEM(capi_i++,pyarr_from_p_#ctype#1(#varname_i#_cb_capi))) + goto capi_fail;"""}], 'need': [{isintent_in: 'pyobj_from_#ctype#1'}, {isintent_inout: 'pyarr_from_p_#ctype#1'}, {iscomplex: '#ctype#'}], '_check': l_and(isscalar, isintent_nothide), '_optional': '' }, { # String - 'frompyobj': [{debugcapi: '\tCFUNCSMESS("cb:Getting #varname#->\\"");'}, - """\tif (capi_j>capi_i) -\t\tGETSTRFROMPYTUPLE(capi_return,capi_i++,#varname_i#,#varname_i#_cb_len);""", + 'frompyobj': [{debugcapi: ' CFUNCSMESS("cb:Getting #varname#->\\"");'}, + """ if (capi_j>capi_i) + GETSTRFROMPYTUPLE(capi_return,capi_i++,#varname_i#,#varname_i#_cb_len);""", {debugcapi: - '\tfprintf(stderr,"#showvalueformat#\\":%d:.\\n",#varname_i#,#varname_i#_cb_len);'}, + ' fprintf(stderr,"#showvalueformat#\\":%d:.\\n",#varname_i#,#varname_i#_cb_len);'}, ], 'need': ['#ctype#', 'GETSTRFROMPYTUPLE', {debugcapi: 'CFUNCSMESS'}, 'string.h'], '_check': l_and(isstring, isintent_out) }, { - 'pyobjfrom': [{debugcapi: '\tfprintf(stderr,"debug-capi:cb:#varname#=\\"#showvalueformat#\\":%d:\\n",#varname_i#,#varname_i#_cb_len);'}, + 'pyobjfrom': [{debugcapi: ' fprintf(stderr,"debug-capi:cb:#varname#=\\"#showvalueformat#\\":%d:\\n",#varname_i#,#varname_i#_cb_len);'}, {isintent_in: """\ -\tif (#name#_nofargs>capi_i) -\t\tif (CAPI_ARGLIST_SETITEM(capi_i++,pyobj_from_#ctype#1size(#varname_i#,#varname_i#_cb_len))) -\t\t\tgoto capi_fail;"""}, + if (cb->nofargs>capi_i) + if (CAPI_ARGLIST_SETITEM(capi_i++,pyobj_from_#ctype#1size(#varname_i#,#varname_i#_cb_len))) + goto capi_fail;"""}, {isintent_inout: """\ -\tif (#name#_nofargs>capi_i) { -\t\tint #varname_i#_cb_dims[] = {#varname_i#_cb_len}; -\t\tif (CAPI_ARGLIST_SETITEM(capi_i++,pyarr_from_p_#ctype#1(#varname_i#,#varname_i#_cb_dims))) -\t\t\tgoto capi_fail; -\t}"""}], + if (cb->nofargs>capi_i) { + int #varname_i#_cb_dims[] = {#varname_i#_cb_len}; + if (CAPI_ARGLIST_SETITEM(capi_i++,pyarr_from_p_#ctype#1(#varname_i#,#varname_i#_cb_dims))) + goto capi_fail; + }"""}], 'need': [{isintent_in: 'pyobj_from_#ctype#1size'}, {isintent_inout: 'pyarr_from_p_#ctype#1'}], '_check': l_and(isstring, isintent_nothide), @@ -378,52 +419,52 @@ }, # Array ... { - 'decl': '\tnpy_intp #varname_i#_Dims[#rank#] = {#rank*[-1]#};', - 'setdims': '\t#cbsetdims#;', + 'decl': ' npy_intp #varname_i#_Dims[#rank#] = {#rank*[-1]#};', + 'setdims': ' #cbsetdims#;', '_check': isarray, '_depend': '' }, { - 'pyobjfrom': [{debugcapi: '\tfprintf(stderr,"debug-capi:cb:#varname#\\n");'}, + 'pyobjfrom': [{debugcapi: ' fprintf(stderr,"debug-capi:cb:#varname#\\n");'}, {isintent_c: """\ -\tif (#name#_nofargs>capi_i) { -\t\tint itemsize_ = #atype# == NPY_STRING ? 1 : 0; -\t\t/*XXX: Hmm, what will destroy this array??? */ -\t\tPyArrayObject *tmp_arr = (PyArrayObject *)PyArray_New(&PyArray_Type,#rank#,#varname_i#_Dims,#atype#,NULL,(char*)#varname_i#,itemsize_,NPY_ARRAY_CARRAY,NULL); + if (cb->nofargs>capi_i) { + int itemsize_ = #atype# == NPY_STRING ? 1 : 0; + /*XXX: Hmm, what will destroy this array??? */ + PyArrayObject *tmp_arr = (PyArrayObject *)PyArray_New(&PyArray_Type,#rank#,#varname_i#_Dims,#atype#,NULL,(char*)#varname_i#,itemsize_,NPY_ARRAY_CARRAY,NULL); """, l_not(isintent_c): """\ -\tif (#name#_nofargs>capi_i) { -\t\tint itemsize_ = #atype# == NPY_STRING ? 1 : 0; -\t\t/*XXX: Hmm, what will destroy this array??? */ -\t\tPyArrayObject *tmp_arr = (PyArrayObject *)PyArray_New(&PyArray_Type,#rank#,#varname_i#_Dims,#atype#,NULL,(char*)#varname_i#,itemsize_,NPY_ARRAY_FARRAY,NULL); + if (cb->nofargs>capi_i) { + int itemsize_ = #atype# == NPY_STRING ? 1 : 0; + /*XXX: Hmm, what will destroy this array??? */ + PyArrayObject *tmp_arr = (PyArrayObject *)PyArray_New(&PyArray_Type,#rank#,#varname_i#_Dims,#atype#,NULL,(char*)#varname_i#,itemsize_,NPY_ARRAY_FARRAY,NULL); """, }, """ -\t\tif (tmp_arr==NULL) -\t\t\tgoto capi_fail; -\t\tif (CAPI_ARGLIST_SETITEM(capi_i++,(PyObject *)tmp_arr)) -\t\t\tgoto capi_fail; + if (tmp_arr==NULL) + goto capi_fail; + if (CAPI_ARGLIST_SETITEM(capi_i++,(PyObject *)tmp_arr)) + goto capi_fail; }"""], '_check': l_and(isarray, isintent_nothide, l_or(isintent_in, isintent_inout)), '_optional': '', }, { - 'frompyobj': [{debugcapi: '\tCFUNCSMESS("cb:Getting #varname#->");'}, - """\tif (capi_j>capi_i) { -\t\tPyArrayObject *rv_cb_arr = NULL; -\t\tif ((capi_tmp = PyTuple_GetItem(capi_return,capi_i++))==NULL) goto capi_fail; -\t\trv_cb_arr = array_from_pyobj(#atype#,#varname_i#_Dims,#rank#,F2PY_INTENT_IN""", + 'frompyobj': [{debugcapi: ' CFUNCSMESS("cb:Getting #varname#->");'}, + """ if (capi_j>capi_i) { + PyArrayObject *rv_cb_arr = NULL; + if ((capi_tmp = PyTuple_GetItem(capi_return,capi_i++))==NULL) goto capi_fail; + rv_cb_arr = array_from_pyobj(#atype#,#varname_i#_Dims,#rank#,F2PY_INTENT_IN""", {isintent_c: '|F2PY_INTENT_C'}, """,capi_tmp); -\t\tif (rv_cb_arr == NULL) { -\t\t\tfprintf(stderr,\"rv_cb_arr is NULL\\n\"); -\t\t\tgoto capi_fail; -\t\t} -\t\tMEMCOPY(#varname_i#,PyArray_DATA(rv_cb_arr),PyArray_NBYTES(rv_cb_arr)); -\t\tif (capi_tmp != (PyObject *)rv_cb_arr) { -\t\t\tPy_DECREF(rv_cb_arr); -\t\t} -\t}""", - {debugcapi: '\tfprintf(stderr,"<-.\\n");'}, + if (rv_cb_arr == NULL) { + fprintf(stderr,\"rv_cb_arr is NULL\\n\"); + goto capi_fail; + } + MEMCOPY(#varname_i#,PyArray_DATA(rv_cb_arr),PyArray_NBYTES(rv_cb_arr)); + if (capi_tmp != (PyObject *)rv_cb_arr) { + Py_DECREF(rv_cb_arr); + } + }""", + {debugcapi: ' fprintf(stderr,"<-.\\n");'}, ], 'need': ['MEMCOPY', {iscomplexarray: '#ctype#'}], '_check': l_and(isarray, isintent_out) @@ -438,7 +479,6 @@ def buildcallbacks(m): - global cb_map cb_map[m['name']] = [] for bi in m['body']: if bi['block'] == 'interface': @@ -450,10 +490,9 @@ def buildcallbacks(m): def buildcallback(rout, um): - global cb_map from . import capi_maps - outmess('\tConstructing call-back function "cb_%s_in_%s"\n' % + outmess(' Constructing call-back function "cb_%s_in_%s"\n' % (rout['name'], um)) args, depargs = getargs(rout) capi_maps.depargs = depargs @@ -573,6 +612,6 @@ def buildcallback(rout, um): 'latexdocstr': ar['latexdocstr'], 'argname': rd['argname'] } - outmess('\t %s\n' % (ar['docstrshort'])) + outmess(' %s\n' % (ar['docstrshort'])) return ################## Build call-back function ############# diff --git a/numpy/f2py/cfuncs.py b/numpy/f2py/cfuncs.py index d59b6301c164..528c4adeee77 100644 --- a/numpy/f2py/cfuncs.py +++ b/numpy/f2py/cfuncs.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """ C declarations, CPP macros, and C functions for f2py2e. @@ -14,8 +14,6 @@ Pearu Peterson """ -from __future__ import division, absolute_import, print_function - import sys import copy @@ -53,7 +51,7 @@ includes0['string.h'] = '#include <string.h>' includes0['setjmp.h'] = '#include <setjmp.h>' -includes['Python.h'] = '#include "Python.h"' +includes['Python.h'] = '#include <Python.h>' needs['arrayobject.h'] = ['Python.h'] includes['arrayobject.h'] = '''#define PY_ARRAY_UNIQUE_SYMBOL PyArray_API #include "arrayobject.h"''' @@ -288,11 +286,11 @@ """ cppmacros[ - 'pyobj_from_char1'] = '#define pyobj_from_char1(v) (PyInt_FromLong(v))' + 'pyobj_from_char1'] = '#define pyobj_from_char1(v) (PyLong_FromLong(v))' cppmacros[ - 'pyobj_from_short1'] = '#define pyobj_from_short1(v) (PyInt_FromLong(v))' + 'pyobj_from_short1'] = '#define pyobj_from_short1(v) (PyLong_FromLong(v))' needs['pyobj_from_int1'] = ['signed_char'] -cppmacros['pyobj_from_int1'] = '#define pyobj_from_int1(v) (PyInt_FromLong(v))' +cppmacros['pyobj_from_int1'] = '#define pyobj_from_int1(v) (PyLong_FromLong(v))' cppmacros[ 'pyobj_from_long1'] = '#define pyobj_from_long1(v) (PyLong_FromLong(v))' needs['pyobj_from_long_long1'] = ['long_long'] @@ -322,10 +320,10 @@ 'pyobj_from_complex_float1'] = '#define pyobj_from_complex_float1(v) (PyComplex_FromDoubles(v.r,v.i))' needs['pyobj_from_string1'] = ['string'] cppmacros[ - 'pyobj_from_string1'] = '#define pyobj_from_string1(v) (PyString_FromString((char *)v))' + 'pyobj_from_string1'] = '#define pyobj_from_string1(v) (PyUnicode_FromString((char *)v))' needs['pyobj_from_string1size'] = ['string'] cppmacros[ - 'pyobj_from_string1size'] = '#define pyobj_from_string1size(v,len) (PyUString_FromStringAndSize((char *)v, len))' + 'pyobj_from_string1size'] = '#define pyobj_from_string1size(v,len) (PyUnicode_FromStringAndSize((char *)v, len))' needs['TRYPYARRAYTEMPLATE'] = ['PRINTPYOBJERR'] cppmacros['TRYPYARRAYTEMPLATE'] = """\ /* New SciPy */ @@ -340,16 +338,16 @@ if (!(arr=(PyArrayObject *)obj)) {fprintf(stderr,\"TRYPYARRAYTEMPLATE:\");PRINTPYOBJERR(obj);return 0;}\\ if (PyArray_DESCR(arr)->type==typecode) {*(ctype *)(PyArray_DATA(arr))=*v; return 1;}\\ switch (PyArray_TYPE(arr)) {\\ - case NPY_DOUBLE: *(double *)(PyArray_DATA(arr))=*v; break;\\ - case NPY_INT: *(int *)(PyArray_DATA(arr))=*v; break;\\ - case NPY_LONG: *(long *)(PyArray_DATA(arr))=*v; break;\\ - case NPY_FLOAT: *(float *)(PyArray_DATA(arr))=*v; break;\\ - case NPY_CDOUBLE: *(double *)(PyArray_DATA(arr))=*v; break;\\ - case NPY_CFLOAT: *(float *)(PyArray_DATA(arr))=*v; break;\\ + case NPY_DOUBLE: *(npy_double *)(PyArray_DATA(arr))=*v; break;\\ + case NPY_INT: *(npy_int *)(PyArray_DATA(arr))=*v; break;\\ + case NPY_LONG: *(npy_long *)(PyArray_DATA(arr))=*v; break;\\ + case NPY_FLOAT: *(npy_float *)(PyArray_DATA(arr))=*v; break;\\ + case NPY_CDOUBLE: *(npy_double *)(PyArray_DATA(arr))=*v; break;\\ + case NPY_CFLOAT: *(npy_float *)(PyArray_DATA(arr))=*v; break;\\ case NPY_BOOL: *(npy_bool *)(PyArray_DATA(arr))=(*v!=0); break;\\ - case NPY_UBYTE: *(unsigned char *)(PyArray_DATA(arr))=*v; break;\\ - case NPY_BYTE: *(signed char *)(PyArray_DATA(arr))=*v; break;\\ - case NPY_SHORT: *(short *)(PyArray_DATA(arr))=*v; break;\\ + case NPY_UBYTE: *(npy_ubyte *)(PyArray_DATA(arr))=*v; break;\\ + case NPY_BYTE: *(npy_byte *)(PyArray_DATA(arr))=*v; break;\\ + case NPY_SHORT: *(npy_short *)(PyArray_DATA(arr))=*v; break;\\ case NPY_USHORT: *(npy_ushort *)(PyArray_DATA(arr))=*v; break;\\ case NPY_UINT: *(npy_uint *)(PyArray_DATA(arr))=*v; break;\\ case NPY_ULONG: *(npy_ulong *)(PyArray_DATA(arr))=*v; break;\\ @@ -377,15 +375,19 @@ return 1;\\ }\\ switch (PyArray_TYPE(arr)) {\\ - case NPY_CDOUBLE: *(double *)(PyArray_DATA(arr))=(*v).r;*(double *)(PyArray_DATA(arr)+sizeof(double))=(*v).i;break;\\ - case NPY_CFLOAT: *(float *)(PyArray_DATA(arr))=(*v).r;*(float *)(PyArray_DATA(arr)+sizeof(float))=(*v).i;break;\\ - case NPY_DOUBLE: *(double *)(PyArray_DATA(arr))=(*v).r; break;\\ - case NPY_LONG: *(long *)(PyArray_DATA(arr))=(*v).r; break;\\ - case NPY_FLOAT: *(float *)(PyArray_DATA(arr))=(*v).r; break;\\ - case NPY_INT: *(int *)(PyArray_DATA(arr))=(*v).r; break;\\ - case NPY_SHORT: *(short *)(PyArray_DATA(arr))=(*v).r; break;\\ - case NPY_UBYTE: *(unsigned char *)(PyArray_DATA(arr))=(*v).r; break;\\ - case NPY_BYTE: *(signed char *)(PyArray_DATA(arr))=(*v).r; break;\\ + case NPY_CDOUBLE: *(npy_double *)(PyArray_DATA(arr))=(*v).r;\\ + *(npy_double *)(PyArray_DATA(arr)+sizeof(npy_double))=(*v).i;\\ + break;\\ + case NPY_CFLOAT: *(npy_float *)(PyArray_DATA(arr))=(*v).r;\\ + *(npy_float *)(PyArray_DATA(arr)+sizeof(npy_float))=(*v).i;\\ + break;\\ + case NPY_DOUBLE: *(npy_double *)(PyArray_DATA(arr))=(*v).r; break;\\ + case NPY_LONG: *(npy_long *)(PyArray_DATA(arr))=(*v).r; break;\\ + case NPY_FLOAT: *(npy_float *)(PyArray_DATA(arr))=(*v).r; break;\\ + case NPY_INT: *(npy_int *)(PyArray_DATA(arr))=(*v).r; break;\\ + case NPY_SHORT: *(npy_short *)(PyArray_DATA(arr))=(*v).r; break;\\ + case NPY_UBYTE: *(npy_ubyte *)(PyArray_DATA(arr))=(*v).r; break;\\ + case NPY_BYTE: *(npy_byte *)(PyArray_DATA(arr))=(*v).r; break;\\ case NPY_BOOL: *(npy_bool *)(PyArray_DATA(arr))=((*v).r!=0 && (*v).i!=0); break;\\ case NPY_USHORT: *(npy_ushort *)(PyArray_DATA(arr))=(*v).r; break;\\ case NPY_UINT: *(npy_uint *)(PyArray_DATA(arr))=(*v).r; break;\\ @@ -393,7 +395,9 @@ case NPY_LONGLONG: *(npy_longlong *)(PyArray_DATA(arr))=(*v).r; break;\\ case NPY_ULONGLONG: *(npy_ulonglong *)(PyArray_DATA(arr))=(*v).r; break;\\ case NPY_LONGDOUBLE: *(npy_longdouble *)(PyArray_DATA(arr))=(*v).r; break;\\ - case NPY_CLONGDOUBLE: *(npy_longdouble *)(PyArray_DATA(arr))=(*v).r;*(npy_longdouble *)(PyArray_DATA(arr)+sizeof(npy_longdouble))=(*v).i;break;\\ + case NPY_CLONGDOUBLE: *(npy_longdouble *)(PyArray_DATA(arr))=(*v).r;\\ + *(npy_longdouble *)(PyArray_DATA(arr)+sizeof(npy_longdouble))=(*v).i;\\ + break;\\ case NPY_OBJECT: PyArray_SETITEM(arr, PyArray_DATA(arr), pyobj_from_complex_ ## ctype ## 1((*v))); break;\\ default: return -2;\\ };\\ @@ -438,9 +442,9 @@ PyObject *rv_cb_str = PyTuple_GetItem((tuple),(index));\\ if (rv_cb_str == NULL)\\ goto capi_fail;\\ - if (PyString_Check(rv_cb_str)) {\\ + if (PyBytes_Check(rv_cb_str)) {\\ str[len-1]='\\0';\\ - STRINGCOPYN((str),PyString_AS_STRING((PyStringObject*)rv_cb_str),(len));\\ + STRINGCOPYN((str),PyBytes_AS_STRING((PyBytesObject*)rv_cb_str),(len));\\ } else {\\ PRINTPYOBJERR(rv_cb_str);\\ PyErr_SetString(#modulename#_error,\"string object expected\");\\ @@ -471,7 +475,7 @@ """ cppmacros['STRINGMALLOC'] = """\ #define STRINGMALLOC(str,len)\\ - if ((str = (string)malloc(sizeof(char)*(len+1))) == NULL) {\\ + if ((str = (string)malloc(len+1)) == NULL) {\\ PyErr_SetString(PyExc_MemoryError, \"out of memory\");\\ goto capi_fail;\\ } else {\\ @@ -481,20 +485,41 @@ cppmacros['STRINGFREE'] = """\ #define STRINGFREE(str) do {if (!(str == NULL)) free(str);} while (0) """ +needs['STRINGPADN'] = ['string.h'] +cppmacros['STRINGPADN'] = """\ +/* +STRINGPADN replaces null values with padding values from the right. + +`to` must have size of at least N bytes. + +If the `to[N-1]` has null value, then replace it and all the +preceding, nulls with the given padding. + +STRINGPADN(to, N, PADDING, NULLVALUE) is an inverse operation. +*/ +#define STRINGPADN(to, N, NULLVALUE, PADDING) \\ + do { \\ + int _m = (N); \\ + char *_to = (to); \\ + for (_m -= 1; _m >= 0 && _to[_m] == NULLVALUE; _m--) { \\ + _to[_m] = PADDING; \\ + } \\ + } while (0) +""" needs['STRINGCOPYN'] = ['string.h', 'FAILNULL'] cppmacros['STRINGCOPYN'] = """\ -#define STRINGCOPYN(to,from,buf_size) \\ +/* +STRINGCOPYN copies N bytes. + +`to` and `from` buffers must have sizes of at least N bytes. +*/ +#define STRINGCOPYN(to,from,N) \\ do { \\ - int _m = (buf_size); \\ + int _m = (N); \\ char *_to = (to); \\ char *_from = (from); \\ FAILNULL(_to); FAILNULL(_from); \\ - (void)strncpy(_to, _from, sizeof(char)*_m); \\ - _to[_m-1] = '\\0'; \\ - /* Padding with spaces instead of nulls */ \\ - for (_m -= 2; _m >= 0 && _to[_m] == '\\0'; _m--) { \\ - _to[_m] = ' '; \\ - } \\ + (void)strncpy(_to, _from, _m); \\ } while (0) """ needs['STRINGCOPY'] = ['string.h', 'FAILNULL'] @@ -542,7 +567,29 @@ 'ARRSIZE'] = '#define ARRSIZE(dims,rank) (_PyArray_multiply_list(dims,rank))' cppmacros['OLDPYNUM'] = """\ #ifdef OLDPYNUM -#error You need to install Numeric Python version 13 or higher. Get it from http:/sourceforge.net/project/?group_id=1369 +#error You need to install NumPy version 0.13 or higher. See https://scipy.org/install.html +#endif +""" +cppmacros["F2PY_THREAD_LOCAL_DECL"] = """\ +#ifndef F2PY_THREAD_LOCAL_DECL +#if defined(_MSC_VER) \\ + || defined(_WIN32) || defined(_WIN64) \\ + || defined(__MINGW32__) || defined(__MINGW64__) +#define F2PY_THREAD_LOCAL_DECL __declspec(thread) +#elif defined(__STDC_VERSION__) \\ + && (__STDC_VERSION__ >= 201112L) \\ + && !defined(__STDC_NO_THREADS__) \\ + && (!defined(__GLIBC__) || __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 12)) +/* __STDC_NO_THREADS__ was first defined in a maintenance release of glibc 2.12, + see https://lists.gnu.org/archive/html/commit-hurd/2012-07/msg00180.html, + so `!defined(__STDC_NO_THREADS__)` may give false positive for the existence + of `threads.h` when using an older release of glibc 2.12 */ +#include <threads.h> +#define F2PY_THREAD_LOCAL_DECL thread_local +#elif defined(__GNUC__) \\ + && (__GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ >= 4))) +#define F2PY_THREAD_LOCAL_DECL __thread +#endif #endif """ ################# C functions ############### @@ -603,270 +650,386 @@ }""" needs['try_pyarr_from_string'] = ['STRINGCOPYN', 'PRINTPYOBJERR', 'string'] cfuncs['try_pyarr_from_string'] = """\ -static int try_pyarr_from_string(PyObject *obj,const string str) { - PyArrayObject *arr = NULL; - if (PyArray_Check(obj) && (!((arr = (PyArrayObject *)obj) == NULL))) - { STRINGCOPYN(PyArray_DATA(arr),str,PyArray_NBYTES(arr)); } - return 1; +/* + try_pyarr_from_string copies str[:len(obj)] to the data of an `ndarray`. + + If obj is an `ndarray`, it is assumed to be contiguous. + + If the specified len==-1, str must be null-terminated. +*/ +static int try_pyarr_from_string(PyObject *obj, + const string str, const int len) { +#ifdef DEBUGCFUNCS +fprintf(stderr, "try_pyarr_from_string(str='%s', len=%d, obj=%p)\\n", + (char*)str,len, obj); +#endif + if (PyArray_Check(obj)) { + PyArrayObject *arr = (PyArrayObject *)obj; + assert(ISCONTIGUOUS(arr)); + string buf = PyArray_DATA(arr); + npy_intp n = len; + if (n == -1) { + /* Assuming null-terminated str. */ + n = strlen(str); + } + if (n > PyArray_NBYTES(arr)) { + n = PyArray_NBYTES(arr); + } + STRINGCOPYN(buf, str, n); + return 1; + } capi_fail: PRINTPYOBJERR(obj); - PyErr_SetString(#modulename#_error,\"try_pyarr_from_string failed\"); + PyErr_SetString(#modulename#_error, \"try_pyarr_from_string failed\"); return 0; } """ needs['string_from_pyobj'] = ['string', 'STRINGMALLOC', 'STRINGCOPYN'] cfuncs['string_from_pyobj'] = """\ -static int string_from_pyobj(string *str,int *len,const string inistr,PyObject *obj,const char *errmess) { - PyArrayObject *arr = NULL; +/* + Create a new string buffer `str` of at most length `len` from a + Python string-like object `obj`. + + The string buffer has given size (len) or the size of inistr when len==-1. + + The string buffer is padded with blanks: in Fortran, trailing blanks + are insignificant contrary to C nulls. + */ +static int +string_from_pyobj(string *str, int *len, const string inistr, PyObject *obj, + const char *errmess) +{ PyObject *tmp = NULL; + string buf = NULL; + npy_intp n = -1; #ifdef DEBUGCFUNCS -fprintf(stderr,\"string_from_pyobj(str='%s',len=%d,inistr='%s',obj=%p)\\n\",(char*)str,*len,(char *)inistr,obj); +fprintf(stderr,\"string_from_pyobj(str='%s',len=%d,inistr='%s',obj=%p)\\n\", + (char*)str, *len, (char *)inistr, obj); #endif if (obj == Py_None) { - if (*len == -1) - *len = strlen(inistr); /* Will this cause problems? */ - STRINGMALLOC(*str,*len); - STRINGCOPYN(*str,inistr,*len+1); - return 1; + n = strlen(inistr); + buf = inistr; } - if (PyArray_Check(obj)) { - if ((arr = (PyArrayObject *)obj) == NULL) - goto capi_fail; + else if (PyArray_Check(obj)) { + PyArrayObject *arr = (PyArrayObject *)obj; if (!ISCONTIGUOUS(arr)) { - PyErr_SetString(PyExc_ValueError,\"array object is non-contiguous.\"); + PyErr_SetString(PyExc_ValueError, + \"array object is non-contiguous.\"); goto capi_fail; } - if (*len == -1) - *len = (PyArray_ITEMSIZE(arr))*PyArray_SIZE(arr); - STRINGMALLOC(*str,*len); - STRINGCOPYN(*str,PyArray_DATA(arr),*len+1); - return 1; - } - if (PyString_Check(obj)) { - tmp = obj; - Py_INCREF(tmp); - } -#if PY_VERSION_HEX >= 0x03000000 - else if (PyUnicode_Check(obj)) { - tmp = PyUnicode_AsASCIIString(obj); + n = PyArray_NBYTES(arr); + buf = PyArray_DATA(arr); + n = strnlen(buf, n); } else { - PyObject *tmp2; - tmp2 = PyObject_Str(obj); - if (tmp2) { - tmp = PyUnicode_AsASCIIString(tmp2); - Py_DECREF(tmp2); + if (PyBytes_Check(obj)) { + tmp = obj; + Py_INCREF(tmp); + } + else if (PyUnicode_Check(obj)) { + tmp = PyUnicode_AsASCIIString(obj); } else { - tmp = NULL; + PyObject *tmp2; + tmp2 = PyObject_Str(obj); + if (tmp2) { + tmp = PyUnicode_AsASCIIString(tmp2); + Py_DECREF(tmp2); + } + else { + tmp = NULL; + } } + if (tmp == NULL) goto capi_fail; + n = PyBytes_GET_SIZE(tmp); + buf = PyBytes_AS_STRING(tmp); } -#else - else { - tmp = PyObject_Str(obj); + if (*len == -1) { + /* TODO: change the type of `len` so that we can remove this */ + if (n > NPY_MAX_INT) { + PyErr_SetString(PyExc_OverflowError, + "object too large for a 32-bit int"); + goto capi_fail; + } + *len = n; } -#endif - if (tmp == NULL) goto capi_fail; - if (*len == -1) - *len = PyString_GET_SIZE(tmp); - STRINGMALLOC(*str,*len); - STRINGCOPYN(*str,PyString_AS_STRING(tmp),*len+1); - Py_DECREF(tmp); + else if (*len < n) { + /* discard the last (len-n) bytes of input buf */ + n = *len; + } + if (n < 0 || *len < 0 || buf == NULL) { + goto capi_fail; + } + STRINGMALLOC(*str, *len); // *str is allocated with size (*len + 1) + if (n < *len) { + /* + Pad fixed-width string with nulls. The caller will replace + nulls with blanks when the corresponding argument is not + intent(c). + */ + memset(*str + n, '\\0', *len - n); + } + STRINGCOPYN(*str, buf, n); + Py_XDECREF(tmp); return 1; capi_fail: Py_XDECREF(tmp); { PyObject* err = PyErr_Occurred(); - if (err==NULL) err = #modulename#_error; - PyErr_SetString(err,errmess); + if (err == NULL) { + err = #modulename#_error; + } + PyErr_SetString(err, errmess); } return 0; } """ + needs['char_from_pyobj'] = ['int_from_pyobj'] cfuncs['char_from_pyobj'] = """\ -static int char_from_pyobj(char* v,PyObject *obj,const char *errmess) { - int i=0; - if (int_from_pyobj(&i,obj,errmess)) { +static int +char_from_pyobj(char* v, PyObject *obj, const char *errmess) { + int i = 0; + if (int_from_pyobj(&i, obj, errmess)) { *v = (char)i; return 1; } return 0; } """ + + needs['signed_char_from_pyobj'] = ['int_from_pyobj', 'signed_char'] cfuncs['signed_char_from_pyobj'] = """\ -static int signed_char_from_pyobj(signed_char* v,PyObject *obj,const char *errmess) { - int i=0; - if (int_from_pyobj(&i,obj,errmess)) { +static int +signed_char_from_pyobj(signed_char* v, PyObject *obj, const char *errmess) { + int i = 0; + if (int_from_pyobj(&i, obj, errmess)) { *v = (signed_char)i; return 1; } return 0; } """ + + needs['short_from_pyobj'] = ['int_from_pyobj'] cfuncs['short_from_pyobj'] = """\ -static int short_from_pyobj(short* v,PyObject *obj,const char *errmess) { - int i=0; - if (int_from_pyobj(&i,obj,errmess)) { +static int +short_from_pyobj(short* v, PyObject *obj, const char *errmess) { + int i = 0; + if (int_from_pyobj(&i, obj, errmess)) { *v = (short)i; return 1; } return 0; } """ + + cfuncs['int_from_pyobj'] = """\ -static int int_from_pyobj(int* v,PyObject *obj,const char *errmess) { +static int +int_from_pyobj(int* v, PyObject *obj, const char *errmess) +{ PyObject* tmp = NULL; - if (PyInt_Check(obj)) { - *v = (int)PyInt_AS_LONG(obj); - return 1; + + if (PyLong_Check(obj)) { + *v = Npy__PyLong_AsInt(obj); + return !(*v == -1 && PyErr_Occurred()); } - tmp = PyNumber_Int(obj); + + tmp = PyNumber_Long(obj); if (tmp) { - *v = PyInt_AS_LONG(tmp); + *v = Npy__PyLong_AsInt(tmp); Py_DECREF(tmp); - return 1; + return !(*v == -1 && PyErr_Occurred()); } - if (PyComplex_Check(obj)) + + if (PyComplex_Check(obj)) { + PyErr_Clear(); tmp = PyObject_GetAttrString(obj,\"real\"); - else if (PyString_Check(obj) || PyUnicode_Check(obj)) + } + else if (PyBytes_Check(obj) || PyUnicode_Check(obj)) { /*pass*/; - else if (PySequence_Check(obj)) - tmp = PySequence_GetItem(obj,0); - if (tmp) { + } + else if (PySequence_Check(obj)) { PyErr_Clear(); - if (int_from_pyobj(v,tmp,errmess)) {Py_DECREF(tmp); return 1;} + tmp = PySequence_GetItem(obj, 0); + } + + if (tmp) { + if (int_from_pyobj(v, tmp, errmess)) { + Py_DECREF(tmp); + return 1; + } Py_DECREF(tmp); } + { PyObject* err = PyErr_Occurred(); - if (err==NULL) err = #modulename#_error; - PyErr_SetString(err,errmess); + if (err == NULL) { + err = #modulename#_error; + } + PyErr_SetString(err, errmess); } return 0; } """ + + cfuncs['long_from_pyobj'] = """\ -static int long_from_pyobj(long* v,PyObject *obj,const char *errmess) { +static int +long_from_pyobj(long* v, PyObject *obj, const char *errmess) { PyObject* tmp = NULL; - if (PyInt_Check(obj)) { - *v = PyInt_AS_LONG(obj); - return 1; + + if (PyLong_Check(obj)) { + *v = PyLong_AsLong(obj); + return !(*v == -1 && PyErr_Occurred()); } - tmp = PyNumber_Int(obj); + + tmp = PyNumber_Long(obj); if (tmp) { - *v = PyInt_AS_LONG(tmp); + *v = PyLong_AsLong(tmp); Py_DECREF(tmp); - return 1; + return !(*v == -1 && PyErr_Occurred()); } - if (PyComplex_Check(obj)) + + if (PyComplex_Check(obj)) { + PyErr_Clear(); tmp = PyObject_GetAttrString(obj,\"real\"); - else if (PyString_Check(obj) || PyUnicode_Check(obj)) + } + else if (PyBytes_Check(obj) || PyUnicode_Check(obj)) { /*pass*/; - else if (PySequence_Check(obj)) - tmp = PySequence_GetItem(obj,0); - if (tmp) { + } + else if (PySequence_Check(obj)) { PyErr_Clear(); - if (long_from_pyobj(v,tmp,errmess)) {Py_DECREF(tmp); return 1;} + tmp = PySequence_GetItem(obj, 0); + } + + if (tmp) { + if (long_from_pyobj(v, tmp, errmess)) { + Py_DECREF(tmp); + return 1; + } Py_DECREF(tmp); } { PyObject* err = PyErr_Occurred(); - if (err==NULL) err = #modulename#_error; - PyErr_SetString(err,errmess); + if (err == NULL) { + err = #modulename#_error; + } + PyErr_SetString(err, errmess); } return 0; } """ + + needs['long_long_from_pyobj'] = ['long_long'] cfuncs['long_long_from_pyobj'] = """\ -static int long_long_from_pyobj(long_long* v,PyObject *obj,const char *errmess) { +static int +long_long_from_pyobj(long_long* v, PyObject *obj, const char *errmess) +{ PyObject* tmp = NULL; + if (PyLong_Check(obj)) { *v = PyLong_AsLongLong(obj); - return (!PyErr_Occurred()); - } - if (PyInt_Check(obj)) { - *v = (long_long)PyInt_AS_LONG(obj); - return 1; + return !(*v == -1 && PyErr_Occurred()); } + tmp = PyNumber_Long(obj); if (tmp) { *v = PyLong_AsLongLong(tmp); Py_DECREF(tmp); - return (!PyErr_Occurred()); + return !(*v == -1 && PyErr_Occurred()); } - if (PyComplex_Check(obj)) + + if (PyComplex_Check(obj)) { + PyErr_Clear(); tmp = PyObject_GetAttrString(obj,\"real\"); - else if (PyString_Check(obj) || PyUnicode_Check(obj)) + } + else if (PyBytes_Check(obj) || PyUnicode_Check(obj)) { /*pass*/; - else if (PySequence_Check(obj)) - tmp = PySequence_GetItem(obj,0); - if (tmp) { + } + else if (PySequence_Check(obj)) { PyErr_Clear(); - if (long_long_from_pyobj(v,tmp,errmess)) {Py_DECREF(tmp); return 1;} + tmp = PySequence_GetItem(obj, 0); + } + + if (tmp) { + if (long_long_from_pyobj(v, tmp, errmess)) { + Py_DECREF(tmp); + return 1; + } Py_DECREF(tmp); } { PyObject* err = PyErr_Occurred(); - if (err==NULL) err = #modulename#_error; + if (err == NULL) { + err = #modulename#_error; + } PyErr_SetString(err,errmess); } return 0; } """ + + needs['long_double_from_pyobj'] = ['double_from_pyobj', 'long_double'] cfuncs['long_double_from_pyobj'] = """\ -static int long_double_from_pyobj(long_double* v,PyObject *obj,const char *errmess) { +static int +long_double_from_pyobj(long_double* v, PyObject *obj, const char *errmess) +{ double d=0; if (PyArray_CheckScalar(obj)){ if PyArray_IsScalar(obj, LongDouble) { PyArray_ScalarAsCtype(obj, v); return 1; } - else if (PyArray_Check(obj) && PyArray_TYPE(obj)==NPY_LONGDOUBLE) { + else if (PyArray_Check(obj) && PyArray_TYPE(obj) == NPY_LONGDOUBLE) { (*v) = *((npy_longdouble *)PyArray_DATA(obj)); return 1; } } - if (double_from_pyobj(&d,obj,errmess)) { + if (double_from_pyobj(&d, obj, errmess)) { *v = (long_double)d; return 1; } return 0; } """ + + cfuncs['double_from_pyobj'] = """\ -static int double_from_pyobj(double* v,PyObject *obj,const char *errmess) { +static int +double_from_pyobj(double* v, PyObject *obj, const char *errmess) +{ PyObject* tmp = NULL; if (PyFloat_Check(obj)) { -#ifdef __sgi *v = PyFloat_AsDouble(obj); -#else - *v = PyFloat_AS_DOUBLE(obj); -#endif - return 1; + return !(*v == -1.0 && PyErr_Occurred()); } + tmp = PyNumber_Float(obj); if (tmp) { -#ifdef __sgi *v = PyFloat_AsDouble(tmp); -#else - *v = PyFloat_AS_DOUBLE(tmp); -#endif Py_DECREF(tmp); - return 1; + return !(*v == -1.0 && PyErr_Occurred()); } - if (PyComplex_Check(obj)) + + if (PyComplex_Check(obj)) { + PyErr_Clear(); tmp = PyObject_GetAttrString(obj,\"real\"); - else if (PyString_Check(obj) || PyUnicode_Check(obj)) + } + else if (PyBytes_Check(obj) || PyUnicode_Check(obj)) { /*pass*/; - else if (PySequence_Check(obj)) - tmp = PySequence_GetItem(obj,0); - if (tmp) { + } + else if (PySequence_Check(obj)) { PyErr_Clear(); + tmp = PySequence_GetItem(obj, 0); + } + + if (tmp) { if (double_from_pyobj(v,tmp,errmess)) {Py_DECREF(tmp); return 1;} Py_DECREF(tmp); } @@ -878,9 +1041,13 @@ return 0; } """ + + needs['float_from_pyobj'] = ['double_from_pyobj'] cfuncs['float_from_pyobj'] = """\ -static int float_from_pyobj(float* v,PyObject *obj,const char *errmess) { +static int +float_from_pyobj(float* v, PyObject *obj, const char *errmess) +{ double d=0.0; if (double_from_pyobj(&d,obj,errmess)) { *v = (float)d; @@ -889,11 +1056,15 @@ return 0; } """ + + needs['complex_long_double_from_pyobj'] = ['complex_long_double', 'long_double', 'complex_double_from_pyobj'] cfuncs['complex_long_double_from_pyobj'] = """\ -static int complex_long_double_from_pyobj(complex_long_double* v,PyObject *obj,const char *errmess) { - complex_double cd={0.0,0.0}; +static int +complex_long_double_from_pyobj(complex_long_double* v, PyObject *obj, const char *errmess) +{ + complex_double cd = {0.0,0.0}; if (PyArray_CheckScalar(obj)){ if PyArray_IsScalar(obj, CLongDouble) { PyArray_ScalarAsCtype(obj, v); @@ -913,13 +1084,17 @@ return 0; } """ + + needs['complex_double_from_pyobj'] = ['complex_double'] cfuncs['complex_double_from_pyobj'] = """\ -static int complex_double_from_pyobj(complex_double* v,PyObject *obj,const char *errmess) { +static int +complex_double_from_pyobj(complex_double* v, PyObject *obj, const char *errmess) { Py_complex c; if (PyComplex_Check(obj)) { - c=PyComplex_AsCComplex(obj); - (*v).r=c.real, (*v).i=c.imag; + c = PyComplex_AsCComplex(obj); + (*v).r = c.real; + (*v).i = c.imag; return 1; } if (PyArray_IsScalar(obj, ComplexFloating)) { @@ -948,30 +1123,25 @@ else { arr = PyArray_FromScalar(obj, PyArray_DescrFromType(NPY_CDOUBLE)); } - if (arr==NULL) return 0; + if (arr == NULL) { + return 0; + } (*v).r = ((npy_cdouble *)PyArray_DATA(arr))->real; (*v).i = ((npy_cdouble *)PyArray_DATA(arr))->imag; + Py_DECREF(arr); return 1; } /* Python does not provide PyNumber_Complex function :-( */ - (*v).i=0.0; + (*v).i = 0.0; if (PyFloat_Check(obj)) { -#ifdef __sgi (*v).r = PyFloat_AsDouble(obj); -#else - (*v).r = PyFloat_AS_DOUBLE(obj); -#endif - return 1; - } - if (PyInt_Check(obj)) { - (*v).r = (double)PyInt_AS_LONG(obj); - return 1; + return !((*v).r == -1.0 && PyErr_Occurred()); } if (PyLong_Check(obj)) { (*v).r = PyLong_AsDouble(obj); - return (!PyErr_Occurred()); + return !((*v).r == -1.0 && PyErr_Occurred()); } - if (PySequence_Check(obj) && !(PyString_Check(obj) || PyUnicode_Check(obj))) { + if (PySequence_Check(obj) && !(PyBytes_Check(obj) || PyUnicode_Check(obj))) { PyObject *tmp = PySequence_GetItem(obj,0); if (tmp) { if (complex_double_from_pyobj(v,tmp,errmess)) { @@ -990,10 +1160,14 @@ return 0; } """ + + needs['complex_float_from_pyobj'] = [ 'complex_float', 'complex_double_from_pyobj'] cfuncs['complex_float_from_pyobj'] = """\ -static int complex_float_from_pyobj(complex_float* v,PyObject *obj,const char *errmess) { +static int +complex_float_from_pyobj(complex_float* v,PyObject *obj,const char *errmess) +{ complex_double cd={0.0,0.0}; if (complex_double_from_pyobj(&cd,obj,errmess)) { (*v).r = (float)cd.r; @@ -1003,6 +1177,8 @@ return 0; } """ + + needs['try_pyarr_from_char'] = ['pyobj_from_char1', 'TRYPYARRAYTEMPLATE'] cfuncs[ 'try_pyarr_from_char'] = 'static int try_pyarr_from_char(PyObject* obj,char* v) {\n TRYPYARRAYTEMPLATE(char,\'c\');\n}\n' @@ -1040,17 +1216,25 @@ cfuncs[ 'try_pyarr_from_complex_double'] = 'static int try_pyarr_from_complex_double(PyObject* obj,complex_double* v) {\n TRYCOMPLEXPYARRAYTEMPLATE(double,\'D\');\n}\n' + needs['create_cb_arglist'] = ['CFUNCSMESS', 'PRINTPYOBJERR', 'MINMAX'] +# create the list of arguments to be used when calling back to python cfuncs['create_cb_arglist'] = """\ -static int create_cb_arglist(PyObject* fun,PyTupleObject* xa,const int maxnofargs,const int nofoptargs,int *nofargs,PyTupleObject **args,const char *errmess) { +static int +create_cb_arglist(PyObject* fun, PyTupleObject* xa , const int maxnofargs, + const int nofoptargs, int *nofargs, PyTupleObject **args, + const char *errmess) +{ PyObject *tmp = NULL; PyObject *tmp_fun = NULL; - int tot,opt,ext,siz,i,di=0; + Py_ssize_t tot, opt, ext, siz, i, di = 0; CFUNCSMESS(\"create_cb_arglist\\n\"); tot=opt=ext=siz=0; /* Get the total number of arguments */ - if (PyFunction_Check(fun)) + if (PyFunction_Check(fun)) { tmp_fun = fun; + Py_INCREF(tmp_fun); + } else { di = 1; if (PyObject_HasAttrString(fun,\"im_func\")) { @@ -1062,7 +1246,12 @@ tmp_fun = PyObject_GetAttrString(tmp,\"im_func\"); else { tmp_fun = fun; /* built-in function */ + Py_INCREF(tmp_fun); tot = maxnofargs; + if (PyCFunction_Check(fun)) { + /* In case the function has a co_argcount (like on PyPy) */ + di = 0; + } if (xa != NULL) tot += PyTuple_Size((PyObject *)xa); } @@ -1073,6 +1262,7 @@ if (xa != NULL) tot += PyTuple_Size((PyObject *)xa); tmp_fun = fun; + Py_INCREF(tmp_fun); } else if (F2PyCapsule_Check(fun)) { tot = maxnofargs; @@ -1083,30 +1273,32 @@ goto capi_fail; } tmp_fun = fun; + Py_INCREF(tmp_fun); } } -if (tmp_fun==NULL) { -fprintf(stderr,\"Call-back argument must be function|instance|instance.__call__|f2py-function but got %s.\\n\",(fun==NULL?\"NULL\":Py_TYPE(fun)->tp_name)); -goto capi_fail; -} -#if PY_VERSION_HEX >= 0x03000000 + + if (tmp_fun == NULL) { + fprintf(stderr, + \"Call-back argument must be function|instance|instance.__call__|f2py-function \" + \"but got %s.\\n\", + ((fun == NULL) ? \"NULL\" : Py_TYPE(fun)->tp_name)); + goto capi_fail; + } + if (PyObject_HasAttrString(tmp_fun,\"__code__\")) { - if (PyObject_HasAttrString(tmp = PyObject_GetAttrString(tmp_fun,\"__code__\"),\"co_argcount\")) -#else - if (PyObject_HasAttrString(tmp_fun,\"func_code\")) { - if (PyObject_HasAttrString(tmp = PyObject_GetAttrString(tmp_fun,\"func_code\"),\"co_argcount\")) -#endif - tot = PyInt_AsLong(PyObject_GetAttrString(tmp,\"co_argcount\")) - di; - Py_XDECREF(tmp); + if (PyObject_HasAttrString(tmp = PyObject_GetAttrString(tmp_fun,\"__code__\"),\"co_argcount\")) { + PyObject *tmp_argcount = PyObject_GetAttrString(tmp,\"co_argcount\"); + Py_DECREF(tmp); + if (tmp_argcount == NULL) { + goto capi_fail; + } + tot = PyLong_AsSsize_t(tmp_argcount) - di; + Py_DECREF(tmp_argcount); + } } /* Get the number of optional arguments */ -#if PY_VERSION_HEX >= 0x03000000 if (PyObject_HasAttrString(tmp_fun,\"__defaults__\")) { if (PyTuple_Check(tmp = PyObject_GetAttrString(tmp_fun,\"__defaults__\"))) -#else - if (PyObject_HasAttrString(tmp_fun,\"func_defaults\")) { - if (PyTuple_Check(tmp = PyObject_GetAttrString(tmp_fun,\"func_defaults\"))) -#endif opt = PyTuple_Size(tmp); Py_XDECREF(tmp); } @@ -1116,13 +1308,23 @@ /* Calculate the size of call-backs argument list */ siz = MIN(maxnofargs+ext,tot); *nofargs = MAX(0,siz-ext); + #ifdef DEBUGCFUNCS - fprintf(stderr,\"debug-capi:create_cb_arglist:maxnofargs(-nofoptargs),tot,opt,ext,siz,nofargs=%d(-%d),%d,%d,%d,%d,%d\\n\",maxnofargs,nofoptargs,tot,opt,ext,siz,*nofargs); + fprintf(stderr, + \"debug-capi:create_cb_arglist:maxnofargs(-nofoptargs),\" + \"tot,opt,ext,siz,nofargs = %d(-%d), %zd, %zd, %zd, %zd, %d\\n\", + maxnofargs, nofoptargs, tot, opt, ext, siz, *nofargs); #endif - if (siz<tot-opt) { - fprintf(stderr,\"create_cb_arglist: Failed to build argument list (siz) with enough arguments (tot-opt) required by user-supplied function (siz,tot,opt=%d,%d,%d).\\n\",siz,tot,opt); + + if (siz < tot-opt) { + fprintf(stderr, + \"create_cb_arglist: Failed to build argument list \" + \"(siz) with enough arguments (tot-opt) required by \" + \"user-supplied function (siz,tot,opt=%zd, %zd, %zd).\\n\", + siz, tot, opt); goto capi_fail; } + /* Initialize argument list */ *args = (PyTupleObject *)PyTuple_New(siz); for (i=0;i<*nofargs;i++) { @@ -1136,10 +1338,13 @@ PyTuple_SET_ITEM(*args,i,tmp); } CFUNCSMESS(\"create_cb_arglist-end\\n\"); + Py_DECREF(tmp_fun); return 1; + capi_fail: - if ((PyErr_Occurred())==NULL) - PyErr_SetString(#modulename#_error,errmess); + if (PyErr_Occurred() == NULL) + PyErr_SetString(#modulename#_error, errmess); + Py_XDECREF(tmp_fun); return 0; } """ @@ -1161,7 +1366,7 @@ def buildcfuncs(): ############ Auxiliary functions for sorting needs ################### def append_needs(need, flag=1): - global outneeds, needs + # This function modifies the contents of the global `outneeds` dict. if isinstance(need, list): for n in need: append_needs(n, flag) @@ -1228,7 +1433,7 @@ def append_needs(need, flag=1): def get_needs(): - global outneeds, needs + # This function modifies the contents of the global `outneeds` dict. res = {} for n in outneeds.keys(): out = [] diff --git a/numpy/f2py/common_rules.py b/numpy/f2py/common_rules.py index 1940d421183f..937d8bc723bd 100644 --- a/numpy/f2py/common_rules.py +++ b/numpy/f2py/common_rules.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """ Build common block mechanism for f2py2e. @@ -13,10 +13,6 @@ Pearu Peterson """ -from __future__ import division, absolute_import, print_function - -__version__ = "$Revision: 1.19 $"[10:-1] - from . import __version__ f2py_version = __version__.version @@ -31,11 +27,9 @@ def findcommonblocks(block, top=1): ret = [] if hascommon(block): - for n in block['common'].keys(): - vars = {} - for v in block['common'][n]: - vars[v] = block['vars'][v] - ret.append((n, block['common'][n], vars)) + for key, value in block['common'].items(): + vars_ = {v: block['vars'][v] for v in value} + ret.append((key, value, vars_)) elif hasbody(block): for b in block['body']: ret = ret + findcommonblocks(b, 0) @@ -126,8 +120,9 @@ def dadd(line, s=doc): cadd('\t%s(f2pyinit%s,F2PYINIT%s)(f2py_setup_%s);' % (F_FUNC, lower_name, name.upper(), name)) cadd('}\n') - iadd('\tF2PyDict_SetItemString(d, \"%s\", PyFortranObject_New(f2py_%s_def,f2py_init_%s));' % ( - name, name, name)) + iadd('\ttmp = PyFortranObject_New(f2py_%s_def,f2py_init_%s);' % (name, name)) + iadd('\tF2PyDict_SetItemString(d, \"%s\", tmp);' % name) + iadd('\tPy_DECREF(tmp);') tname = name.replace('_', '\\_') dadd('\\subsection{Common block \\texttt{%s}}\n' % (tname)) dadd('\\begin{description}') diff --git a/numpy/f2py/crackfortran.py b/numpy/f2py/crackfortran.py index 19ce8c14582c..b02eb68b7fb3 100755 --- a/numpy/f2py/crackfortran.py +++ b/numpy/f2py/crackfortran.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """ crackfortran --- read fortran (77,90) code and extract declaration information. @@ -33,7 +33,7 @@ Note: pythonmodule is introduced to represent Python module Usage: - `postlist=crackfortran(files,funcs)` + `postlist=crackfortran(files)` `postlist` contains declaration information read from the list of files `files`. `crack2fortran(postlist)` returns a fortran code to be saved to pyf-file @@ -43,7 +43,8 @@ 'implicit','externals','interfaced','common','sortvars', 'commonvars','note']} B['block'] = 'interface' | 'function' | 'subroutine' | 'module' | - 'program' | 'block data' | 'type' | 'pythonmodule' + 'program' | 'block data' | 'type' | 'pythonmodule' | + 'abstract interface' B['body'] --- list containing `subblocks' with the same structure as `blocks' B['parent_block'] --- dictionary of a parent block: C['body'][<index>]['parent_block'] is C @@ -138,8 +139,6 @@ The above may be solved by creating appropriate preprocessor program, for example. """ -from __future__ import division, absolute_import, print_function - import sys import string import fileinput @@ -150,11 +149,11 @@ from . import __version__ -# The eviroment provided by auxfuncs.py is needed for some calls to eval. +# The environment provided by auxfuncs.py is needed for some calls to eval. # As the needed functions cannot be determined by static inspection of the # code, it is safest to use import * pending a major refactoring of f2py. from .auxfuncs import * - +from . import symbolic f2py_version = __version__.version @@ -246,7 +245,6 @@ def outmess(line, flag=1): defaultimplicitrules[c] = {'typespec': 'real'} for c in "ijklmn": defaultimplicitrules[c] = {'typespec': 'integer'} -del c badnames = {} invbadnames = {} for n in ['int', 'double', 'float', 'char', 'short', 'long', 'void', 'case', 'while', @@ -296,10 +294,10 @@ def getextension(name): return '' return name[i + 1:] -is_f_file = re.compile(r'.*[.](for|ftn|f77|f)\Z', re.I).match -_has_f_header = re.compile(r'-[*]-\s*fortran\s*-[*]-', re.I).search -_has_f90_header = re.compile(r'-[*]-\s*f90\s*-[*]-', re.I).search -_has_fix_header = re.compile(r'-[*]-\s*fix\s*-[*]-', re.I).search +is_f_file = re.compile(r'.*\.(for|ftn|f77|f)\Z', re.I).match +_has_f_header = re.compile(r'-\*-\s*fortran\s*-\*-', re.I).search +_has_f90_header = re.compile(r'-\*-\s*f90\s*-\*-', re.I).search +_has_fix_header = re.compile(r'-\*-\s*fix\s*-\*-', re.I).search _free_f90_start = re.compile(r'[^c*]\s*[^\s\d\t]', re.I).match @@ -343,7 +341,9 @@ def readfortrancode(ffile, dowithline=show, istop=1): if ffile == []: return localdolowercase = dolowercase - cont = 0 + # cont: set to True when the content of the last line read + # indicates statement continuation + cont = False finalline = '' ll = '' includeline = re.compile( @@ -394,14 +394,26 @@ def readfortrancode(ffile, dowithline=show, istop=1): if rl[:5].lower() == '!f2py': # f2py directive l, _ = split_by_unquoted(l + 4 * ' ' + rl[5:], '!') if l.strip() == '': # Skip empty line - cont = 0 + if sourcecodeform == 'free': + # In free form, a statement continues in the next line + # that is not a comment line [3.3.2.4^1], lines with + # blanks are comment lines [3.3.2.3^1]. Hence, the + # line continuation flag must retain its state. + pass + else: + # In fixed form, statement continuation is determined + # by a non-blank character at the 6-th position. Empty + # line indicates a start of a new statement + # [3.3.3.3^1]. Hence, the line continuation flag must + # be reset. + cont = False continue if sourcecodeform == 'fix': if l[0] in ['*', 'c', '!', 'C', '#']: if l[1:5].lower() == 'f2py': # f2py directive l = ' ' + l[5:] else: # Skip comment line - cont = 0 + cont = False continue elif strictf77: if len(l) > 72: @@ -555,14 +567,15 @@ def readfortrancode(ffile, dowithline=show, istop=1): beginpattern77 = re.compile( beforethisafter % ('', groupbegins77, groupbegins77, '.*'), re.I), 'begin' groupbegins90 = groupbegins77 + \ - r'|module(?!\s*procedure)|python\s*module|interface|type(?!\s*\()' + r'|module(?!\s*procedure)|python\s*module|(abstract|)\s*interface|' + \ + r'type(?!\s*\()' beginpattern90 = re.compile( beforethisafter % ('', groupbegins90, groupbegins90, '.*'), re.I), 'begin' -groupends = r'end|endprogram|endblockdata|endmodule|endpythonmodule|endinterface' +groupends = (r'end|endprogram|endblockdata|endmodule|endpythonmodule|' + r'endinterface|endsubroutine|endfunction') endpattern = re.compile( beforethisafter % ('', groupends, groupends, r'[\w\s]*'), re.I), 'end' -# endifs='end\s*(if|do|where|select|while|forall)' -endifs = r'(end\s*(if|do|where|select|while|forall))|(module\s*procedure)' +endifs = r'(end\s*(if|do|where|select|while|forall|associate|block|critical|enum|team))|(module\s*procedure)' endifpattern = re.compile( beforethisafter % (r'[\w]*?', endifs, endifs, r'[\w\s]*'), re.I), 'endif' # @@ -580,8 +593,8 @@ def readfortrancode(ffile, dowithline=show, istop=1): beforethisafter % ('', 'public', 'public', '.*'), re.I), 'public' privatepattern = re.compile( beforethisafter % ('', 'private', 'private', '.*'), re.I), 'private' -intrisicpattern = re.compile( - beforethisafter % ('', 'intrisic', 'intrisic', '.*'), re.I), 'intrisic' +intrinsicpattern = re.compile( + beforethisafter % ('', 'intrinsic', 'intrinsic', '.*'), re.I), 'intrinsic' intentpattern = re.compile(beforethisafter % ( '', 'intent|depend|note|check', 'intent|depend|note|check', r'\s*\(.*?\).*'), re.I), 'intent' parameterpattern = re.compile( @@ -637,7 +650,7 @@ def _simplifyargs(argsline): a.append(n) return ','.join(a) -crackline_re_1 = re.compile(r'\s*(?P<result>\b[a-z]+[\w]*\b)\s*[=].*', re.I) +crackline_re_1 = re.compile(r'\s*(?P<result>\b[a-z]+\w*\b)\s*=.*', re.I) def crackline(line, reset=0): @@ -706,7 +719,7 @@ def crackline(line, reset=0): for pat in [dimensionpattern, externalpattern, intentpattern, optionalpattern, requiredpattern, parameterpattern, datapattern, publicpattern, privatepattern, - intrisicpattern, + intrinsicpattern, endifpattern, endpattern, formatpattern, beginpattern, functionpattern, subroutinepattern, @@ -861,15 +874,16 @@ def appenddecl(decl, decl2, force=1): decl[k] = decl2[k] elif k == 'note': pass - elif k in ['intent', 'check', 'dimension', 'optional', 'required']: + elif k in ['intent', 'check', 'dimension', 'optional', + 'required', 'depend']: errmess('appenddecl: "%s" not implemented.\n' % k) else: - raise Exception('appenddecl: Unknown variable definition key:' + + raise Exception('appenddecl: Unknown variable definition key: ' + str(k)) return decl selectpattern = re.compile( - r'\s*(?P<this>(@\(@.*?@\)@|[*][\d*]+|[*]\s*@\(@.*?@\)@|))(?P<after>.*)\Z', re.I) + r'\s*(?P<this>(@\(@.*?@\)@|\*[\d*]+|\*\s*@\(@.*?@\)@|))(?P<after>.*)\Z', re.I) nameargspattern = re.compile( r'\s*(?P<name>\b[\w$]+\b)\s*(@\(@\s*(?P<args>[\w\s,]*)\s*@\)@|)\s*((result(\s*@\(@\s*(?P<result>\b[\w$]+\b)\s*@\)@|))|(bind\s*@\(@\s*(?P<bind>.*)\s*@\)@))*\s*\Z', re.I) callnameargspattern = re.compile( @@ -929,15 +943,17 @@ def analyzeline(m, case, line): block = block.lower() if re.match(r'block\s*data', block, re.I): block = 'block data' - if re.match(r'python\s*module', block, re.I): + elif re.match(r'python\s*module', block, re.I): block = 'python module' + elif re.match(r'abstract\s*interface', block, re.I): + block = 'abstract interface' name, args, result, bind = _resolvenameargspattern(m.group('after')) if name is None: if block == 'block data': name = '_BLOCK_DATA_' else: name = '' - if block not in ['interface', 'block data']: + if block not in ['interface', 'block data', 'abstract interface']: outmess('analyzeline: No name/args pattern found for line.\n') previous_context = (block, name, groupcounter) @@ -971,7 +987,7 @@ def analyzeline(m, case, line): if f77modulename and neededmodule == -1 and groupcounter <= 1: neededmodule = groupcounter + 2 needmodule = 1 - if block != 'interface': + if block not in ['interface', 'abstract interface']: needinterface = 1 # Create new block(s) groupcounter = groupcounter + 1 @@ -1011,7 +1027,7 @@ def analyzeline(m, case, line): groupname[groupcounter] = block groupcache[groupcounter]['block'] = block if not name: - name = 'unknown_' + block + name = 'unknown_' + block.replace(' ', '_') groupcache[groupcounter]['prefix'] = m.group('before') groupcache[groupcounter]['name'] = rmbadname1(name) groupcache[groupcounter]['result'] = result @@ -1098,7 +1114,7 @@ def analyzeline(m, case, line): last_name = updatevars(typespec, selector, attr, edecl) if last_name is not None: previous_context = ('variable', last_name, groupcounter) - elif case in ['dimension', 'intent', 'optional', 'required', 'external', 'public', 'private', 'intrisic']: + elif case in ['dimension', 'intent', 'optional', 'required', 'external', 'public', 'private', 'intrinsic']: edecl = groupcache[groupcounter]['vars'] ll = m.group('after').strip() i = ll.find('::') @@ -1154,11 +1170,11 @@ def analyzeline(m, case, line): groupcache[groupcounter]['args'].append(k) else: errmess( - 'analyzeline: intent(callback) %s is ignored' % (k)) + 'analyzeline: intent(callback) %s is ignored\n' % (k)) else: errmess('analyzeline: intent(callback) %s is already' - ' in argument list' % (k)) - if case in ['optional', 'required', 'public', 'external', 'private', 'intrisic']: + ' in argument list\n' % (k)) + if case in ['optional', 'required', 'public', 'external', 'private', 'intrinsic']: ap = case if 'attrspec' in edecl[k]: edecl[k]['attrspec'].append(ap) @@ -1390,7 +1406,7 @@ def analyzeline(m, case, line): previous_context = ('common', bn, groupcounter) elif case == 'use': m1 = re.match( - r'\A\s*(?P<name>\b[\w]+\b)\s*((,(\s*\bonly\b\s*:|(?P<notonly>))\s*(?P<list>.*))|)\s*\Z', m.group('after'), re.I) + r'\A\s*(?P<name>\b\w+\b)\s*((,(\s*\bonly\b\s*:|(?P<notonly>))\s*(?P<list>.*))|)\s*\Z', m.group('after'), re.I) if m1: mm = m1.groupdict() if 'use' not in groupcache[groupcounter]: @@ -1407,7 +1423,7 @@ def analyzeline(m, case, line): for l in ll: if '=' in l: m2 = re.match( - r'\A\s*(?P<local>\b[\w]+\b)\s*=\s*>\s*(?P<use>\b[\w]+\b)\s*\Z', l, re.I) + r'\A\s*(?P<local>\b\w+\b)\s*=\s*>\s*(?P<use>\b\w+\b)\s*\Z', l, re.I) if m2: rl[m2.group('local').strip()] = m2.group( 'use').strip() @@ -1483,15 +1499,15 @@ def cracktypespec0(typespec, ll): ll = ll[i + 2:] return typespec, selector, attr, ll ##### -namepattern = re.compile(r'\s*(?P<name>\b[\w]+\b)\s*(?P<after>.*)\s*\Z', re.I) +namepattern = re.compile(r'\s*(?P<name>\b\w+\b)\s*(?P<after>.*)\s*\Z', re.I) kindselector = re.compile( - r'\s*(\(\s*(kind\s*=)?\s*(?P<kind>.*)\s*\)|[*]\s*(?P<kind2>.*?))\s*\Z', re.I) + r'\s*(\(\s*(kind\s*=)?\s*(?P<kind>.*)\s*\)|\*\s*(?P<kind2>.*?))\s*\Z', re.I) charselector = re.compile( - r'\s*(\((?P<lenkind>.*)\)|[*]\s*(?P<charlen>.*))\s*\Z', re.I) + r'\s*(\((?P<lenkind>.*)\)|\*\s*(?P<charlen>.*))\s*\Z', re.I) lenkindpattern = re.compile( r'\s*(kind\s*=\s*(?P<kind>.*?)\s*(@,@\s*len\s*=\s*(?P<len>.*)|)|(len\s*=\s*|)(?P<len2>.*?)\s*(@,@\s*(kind\s*=\s*|)(?P<kind2>.*)|))\s*\Z', re.I) lenarraypattern = re.compile( - r'\s*(@\(@\s*(?!/)\s*(?P<array>.*?)\s*@\)@\s*[*]\s*(?P<len>.*?)|([*]\s*(?P<len2>.*?)|)\s*(@\(@\s*(?!/)\s*(?P<array2>.*?)\s*@\)@|))\s*(=\s*(?P<init>.*?)|(@\(@|)/\s*(?P<init2>.*?)\s*/(@\)@|)|)\s*\Z', re.I) + r'\s*(@\(@\s*(?!/)\s*(?P<array>.*?)\s*@\)@\s*\*\s*(?P<len>.*?)|(\*\s*(?P<len2>.*?)|)\s*(@\(@\s*(?!/)\s*(?P<array2>.*?)\s*@\)@|))\s*(=\s*(?P<init>.*?)|(@\(@|)/\s*(?P<init2>.*?)\s*/(@\)@|)|)\s*\Z', re.I) def removespaces(expr): @@ -1510,27 +1526,40 @@ def removespaces(expr): def markinnerspaces(line): - l = '' - f = 0 - cc = '\'' - cb = '' + """ + The function replace all spaces in the input variable line which are + surrounded with quotation marks, with the triplet "@_@". + + For instance, for the input "a 'b c'" the function returns "a 'b@_@c'" + + Parameters + ---------- + line : str + + Returns + ------- + str + + """ + fragment = '' + inside = False + current_quote = None + escaped = '' for c in line: - if cb == '\\' and c in ['\\', '\'', '"']: - l = l + c - cb = c + if escaped == '\\' and c in ['\\', '\'', '"']: + fragment += c + escaped = c continue - if f == 0 and c in ['\'', '"']: - cc = c - if c == cc: - f = f + 1 - elif c == cc: - f = f - 1 - elif c == ' ' and f == 1: - l = l + '@_@' + if not inside and c in ['\'', '"']: + current_quote = c + if c == current_quote: + inside = not inside + elif c == ' ' and inside: + fragment += '@_@' continue - l = l + c - cb = c - return l + fragment += c + escaped = c # reset to non-backslash + return fragment def updatevars(typespec, selector, attrspec, entitydecl): @@ -1612,6 +1641,10 @@ def updatevars(typespec, selector, attrspec, entitydecl): edecl['charselector'] = copy.copy(charselect) edecl['typename'] = typename edecl['attrspec'] = copy.copy(attrspec) + if 'external' in (edecl.get('attrspec') or []) and e in groupcache[groupcounter]['args']: + if 'externals' not in groupcache[groupcounter]: + groupcache[groupcounter]['externals'] = [] + groupcache[groupcounter]['externals'].append(e) if m.group('after'): m1 = lenarraypattern.match(markouterparen(m.group('after'))) if m1: @@ -1750,10 +1783,12 @@ def setattrspec(decl, attr, force=0): decl['attrspec'].append(attr) elif attr == 'automatic' and 'static' not in decl['attrspec']: decl['attrspec'].append(attr) - elif attr == 'public' and 'private' not in decl['attrspec']: - decl['attrspec'].append(attr) - elif attr == 'private' and 'public' not in decl['attrspec']: - decl['attrspec'].append(attr) + elif attr == 'public': + if 'private' not in decl['attrspec']: + decl['attrspec'].append(attr) + elif attr == 'private': + if 'public' not in decl['attrspec']: + decl['attrspec'].append(attr) else: decl['attrspec'].append(attr) return decl @@ -1833,11 +1868,11 @@ def get_useparameters(block, param_map=None): continue # XXX: apply mapping if mapping: - errmess('get_useparameters: mapping for %s not impl.' % (mapping)) + errmess('get_useparameters: mapping for %s not impl.\n' % (mapping)) for k, v in list(params.items()): if k in param_map: outmess('get_useparameters: overriding parameter %s with' - ' value from module %s' % (repr(k), repr(usename))) + ' value from module %s\n' % (repr(k), repr(usename))) param_map[k] = v return param_map @@ -1849,10 +1884,8 @@ def postcrack2(block, tab='', param_map=None): if not f90modulevars: return block if isinstance(block, list): - ret = [] - for g in block: - g = postcrack2(g, tab=tab + '\t', param_map=param_map) - ret.append(g) + ret = [postcrack2(g, tab=tab + '\t', param_map=param_map) + for g in block] return ret setmesstext(block) outmess('%sBlock: %s\n' % (tab, block['name']), 0) @@ -1870,10 +1903,8 @@ def postcrack2(block, tab='', param_map=None): val = kind['kind'] if val in param_map: kind['kind'] = param_map[val] - new_body = [] - for b in block['body']: - b = postcrack2(b, tab=tab + '\t', param_map=param_map) - new_body.append(b) + new_body = [postcrack2(b, tab=tab + '\t', param_map=param_map) + for b in block['body']] block['body'] = new_body return block @@ -2074,7 +2105,7 @@ def analyzebody(block, args, tab=''): else: as_ = args b = postcrack(b, as_, tab=tab + '\t') - if b['block'] == 'interface' and not b['body']: + if b['block'] in ['interface', 'abstract interface'] and not b['body']: if 'f2pyenhancements' not in b: continue if b['block'].replace(' ', '') == 'pythonmodule': @@ -2106,8 +2137,9 @@ def buildimplicitrules(block): def myeval(e, g=None, l=None): + """ Like `eval` but returns only integers and floats """ r = eval(e, g, l) - if type(r) in [type(0), type(0.0)]: + if type(r) in [int, float]: return r raise ValueError('r=%r' % (r)) @@ -2115,6 +2147,26 @@ def myeval(e, g=None, l=None): def getlincoef(e, xset): # e = a*x+b ; x in xset + """ + Obtain ``a`` and ``b`` when ``e == "a*x+b"``, where ``x`` is a symbol in + xset. + + >>> getlincoef('2*x + 1', {'x'}) + (2, 1, 'x') + >>> getlincoef('3*x + x*2 + 2 + 1', {'x'}) + (5, 3, 'x') + >>> getlincoef('0', {'x'}) + (0, 0, None) + >>> getlincoef('0*x', {'x'}) + (0, 0, 'x') + >>> getlincoef('x*x', {'x'}) + (None, None, None) + + This can be tricked by sufficiently complex expressions + + >>> getlincoef('(x - 0.5)*(x - 1.5)*(x - 1)*x + 2*x + 3', {'x'}) + (2.0, 3.0, 'x') + """ try: c = int(myeval(e, {}, {})) return 0, c, None @@ -2165,157 +2217,6 @@ def getlincoef(e, xset): # e = a*x+b ; x in xset break return None, None, None -_varname_match = re.compile(r'\A[a-z]\w*\Z').match - - -def getarrlen(dl, args, star='*'): - edl = [] - try: - edl.append(myeval(dl[0], {}, {})) - except Exception: - edl.append(dl[0]) - try: - edl.append(myeval(dl[1], {}, {})) - except Exception: - edl.append(dl[1]) - if isinstance(edl[0], int): - p1 = 1 - edl[0] - if p1 == 0: - d = str(dl[1]) - elif p1 < 0: - d = '%s-%s' % (dl[1], -p1) - else: - d = '%s+%s' % (dl[1], p1) - elif isinstance(edl[1], int): - p1 = 1 + edl[1] - if p1 == 0: - d = '-(%s)' % (dl[0]) - else: - d = '%s-(%s)' % (p1, dl[0]) - else: - d = '%s-(%s)+1' % (dl[1], dl[0]) - try: - return repr(myeval(d, {}, {})), None, None - except Exception: - pass - d1, d2 = getlincoef(dl[0], args), getlincoef(dl[1], args) - if None not in [d1[0], d2[0]]: - if (d1[0], d2[0]) == (0, 0): - return repr(d2[1] - d1[1] + 1), None, None - b = d2[1] - d1[1] + 1 - d1 = (d1[0], 0, d1[2]) - d2 = (d2[0], b, d2[2]) - if d1[0] == 0 and d2[2] in args: - if b < 0: - return '%s * %s - %s' % (d2[0], d2[2], -b), d2[2], '+%s)/(%s)' % (-b, d2[0]) - elif b: - return '%s * %s + %s' % (d2[0], d2[2], b), d2[2], '-%s)/(%s)' % (b, d2[0]) - else: - return '%s * %s' % (d2[0], d2[2]), d2[2], ')/(%s)' % (d2[0]) - if d2[0] == 0 and d1[2] in args: - - if b < 0: - return '%s * %s - %s' % (-d1[0], d1[2], -b), d1[2], '+%s)/(%s)' % (-b, -d1[0]) - elif b: - return '%s * %s + %s' % (-d1[0], d1[2], b), d1[2], '-%s)/(%s)' % (b, -d1[0]) - else: - return '%s * %s' % (-d1[0], d1[2]), d1[2], ')/(%s)' % (-d1[0]) - if d1[2] == d2[2] and d1[2] in args: - a = d2[0] - d1[0] - if not a: - return repr(b), None, None - if b < 0: - return '%s * %s - %s' % (a, d1[2], -b), d2[2], '+%s)/(%s)' % (-b, a) - elif b: - return '%s * %s + %s' % (a, d1[2], b), d2[2], '-%s)/(%s)' % (b, a) - else: - return '%s * %s' % (a, d1[2]), d2[2], ')/(%s)' % (a) - if d1[0] == d2[0] == 1: - c = str(d1[2]) - if c not in args: - if _varname_match(c): - outmess('\tgetarrlen:variable "%s" undefined\n' % (c)) - c = '(%s)' % c - if b == 0: - d = '%s-%s' % (d2[2], c) - elif b < 0: - d = '%s-%s-%s' % (d2[2], c, -b) - else: - d = '%s-%s+%s' % (d2[2], c, b) - elif d1[0] == 0: - c2 = str(d2[2]) - if c2 not in args: - if _varname_match(c2): - outmess('\tgetarrlen:variable "%s" undefined\n' % (c2)) - c2 = '(%s)' % c2 - if d2[0] == 1: - pass - elif d2[0] == -1: - c2 = '-%s' % c2 - else: - c2 = '%s*%s' % (d2[0], c2) - - if b == 0: - d = c2 - elif b < 0: - d = '%s-%s' % (c2, -b) - else: - d = '%s+%s' % (c2, b) - elif d2[0] == 0: - c1 = str(d1[2]) - if c1 not in args: - if _varname_match(c1): - outmess('\tgetarrlen:variable "%s" undefined\n' % (c1)) - c1 = '(%s)' % c1 - if d1[0] == 1: - c1 = '-%s' % c1 - elif d1[0] == -1: - c1 = '+%s' % c1 - elif d1[0] < 0: - c1 = '+%s*%s' % (-d1[0], c1) - else: - c1 = '-%s*%s' % (d1[0], c1) - - if b == 0: - d = c1 - elif b < 0: - d = '%s-%s' % (c1, -b) - else: - d = '%s+%s' % (c1, b) - else: - c1 = str(d1[2]) - if c1 not in args: - if _varname_match(c1): - outmess('\tgetarrlen:variable "%s" undefined\n' % (c1)) - c1 = '(%s)' % c1 - if d1[0] == 1: - c1 = '-%s' % c1 - elif d1[0] == -1: - c1 = '+%s' % c1 - elif d1[0] < 0: - c1 = '+%s*%s' % (-d1[0], c1) - else: - c1 = '-%s*%s' % (d1[0], c1) - - c2 = str(d2[2]) - if c2 not in args: - if _varname_match(c2): - outmess('\tgetarrlen:variable "%s" undefined\n' % (c2)) - c2 = '(%s)' % c2 - if d2[0] == 1: - pass - elif d2[0] == -1: - c2 = '-%s' % c2 - else: - c2 = '%s*%s' % (d2[0], c2) - - if b == 0: - d = '%s%s' % (c2, c1) - elif b < 0: - d = '%s%s-%s' % (c2, c1, -b) - else: - d = '%s%s+%s' % (c2, c1, b) - return d, None, None word_pattern = re.compile(r'\b[a-z][\w$]*\b', re.I) @@ -2326,7 +2227,9 @@ def _get_depend_dict(name, vars, deps): if '=' in vars[name] and not isstring(vars[name]): for word in word_pattern.findall(vars[name]['=']): - if word not in words and word in vars: + # The word_pattern may return values that are not + # only variables, they can be string content for instance + if word not in words and word in vars and word != name: words.append(word) for word in words[:]: for w in deps.get(word, []) \ @@ -2403,7 +2306,7 @@ def _selected_real_kind_func(p, r=0, radix=0): if p < 16: return 8 machine = platform.machine().lower() - if machine.startswith('power') or machine.startswith('ppc64'): + if machine.startswith(('aarch64', 'power', 'ppc', 'riscv', 's390x', 'sparc')): if p <= 20: return 16 else: @@ -2481,10 +2384,8 @@ def get_parameters(vars, global_params={}): v = ''.join(tt) elif iscomplex(vars[n]): - # FIXME complex numbers may also have exponents - if v[0] == '(' and v[-1] == ')': - # FIXME, unused l looks like potential bug - l = markoutercomma(v[1:-1]).split('@,@') + outmess(f'get_parameters[TODO]: ' + f'implement evaluation of complex expression {v}\n') try: params[n] = eval(v, g_params, params) @@ -2515,8 +2416,9 @@ def _eval_scalar(value, params): if _is_kind_number(value): value = value.split('_')[0] try: - value = str(eval(value, {}, params)) - except (NameError, SyntaxError): + value = eval(value, {}, params) + value = (repr if isinstance(value, str) else str)(value) + except (NameError, SyntaxError, TypeError): return value except Exception as msg: errmess('"%s" in evaluating %r ' @@ -2556,7 +2458,7 @@ def analyzevars(block): params = get_parameters(vars, get_useparameters(block)) dep_matches = {} - name_match = re.compile(r'\w[\w\d_$]*').match + name_match = re.compile(r'[A-Za-z][\w$]*').match for v in list(vars.keys()): m = name_match(v) if m: @@ -2602,7 +2504,7 @@ def analyzevars(block): pass vars[n]['kindselector']['kind'] = l - savelindims = {} + dimension_exprs = {} if 'attrspec' in vars[n]: attr = vars[n]['attrspec'] attr.reverse() @@ -2655,18 +2557,18 @@ def analyzevars(block): if dim and 'dimension' not in vars[n]: vars[n]['dimension'] = [] for d in rmbadname([x.strip() for x in markoutercomma(dim).split('@,@')]): - star = '*' - if d == ':': - star = ':' + star = ':' if d == ':' else '*' + # Evaluate `d` with respect to params if d in params: d = str(params[d]) - for p in list(params.keys()): + for p in params: re_1 = re.compile(r'(?P<before>.*?)\b' + p + r'\b(?P<after>.*)', re.I) m = re_1.match(d) while m: d = m.group('before') + \ str(params[p]) + m.group('after') m = re_1.match(d) + if d == star: dl = [star] else: @@ -2674,22 +2576,46 @@ def analyzevars(block): if len(dl) == 2 and '*' in dl: # e.g. dimension(5:*) dl = ['*'] d = '*' - if len(dl) == 1 and not dl[0] == star: + if len(dl) == 1 and dl[0] != star: dl = ['1', dl[0]] if len(dl) == 2: - d, v, di = getarrlen(dl, list(block['vars'].keys())) - if d[:4] == '1 * ': - d = d[4:] - if di and di[-4:] == '/(1)': - di = di[:-4] - if v: - savelindims[d] = v, di + d1, d2 = map(symbolic.Expr.parse, dl) + dsize = d2 - d1 + 1 + d = dsize.tostring(language=symbolic.Language.C) + # find variables v that define d as a linear + # function, `d == a * v + b`, and store + # coefficients a and b for further analysis. + solver_and_deps = {} + for v in block['vars']: + s = symbolic.as_symbol(v) + if dsize.contains(s): + try: + a, b = dsize.linear_solve(s) + solve_v = lambda s: (s - b) / a + all_symbols = set(a.symbols()) + all_symbols.update(b.symbols()) + except RuntimeError as msg: + # d is not a linear function of v, + # however, if v can be determined + # from d using other means, + # implement the corresponding + # solve_v function here. + solve_v = None + all_symbols = set(dsize.symbols()) + v_deps = set( + s.data for s in all_symbols + if s.data in vars) + solver_and_deps[v] = solve_v, list(v_deps) + # Note that dsize may contain symbols that are + # not defined in block['vars']. Here we assume + # these correspond to Fortran/C intrinsic + # functions or that are defined by other + # means. We'll let the compiler validate the + # definiteness of such symbols. + dimension_exprs[d] = solver_and_deps vars[n]['dimension'].append(d) + if 'dimension' in vars[n]: - if isintent_c(vars[n]): - shape_macro = 'shape' - else: - shape_macro = 'shape' # 'fshape' if isstringarray(vars[n]): if 'charselector' in vars[n]: d = vars[n]['charselector'] @@ -2707,70 +2633,88 @@ def analyzevars(block): vars[n]['intent'].append('c') else: errmess( - "analyzevars: charselector=%r unhandled." % (d)) + "analyzevars: charselector=%r unhandled.\n" % (d)) + if 'check' not in vars[n] and 'args' in block and n in block['args']: - flag = 'depend' not in vars[n] - if flag: - vars[n]['depend'] = [] - vars[n]['check'] = [] - if 'dimension' in vars[n]: - #/----< no check - i = -1 - ni = len(vars[n]['dimension']) - for d in vars[n]['dimension']: - ddeps = [] # dependencies of 'd' - ad = '' - pd = '' - if d not in vars: - if d in savelindims: - pd, ad = '(', savelindims[d][1] - d = savelindims[d][0] - else: - for r in block['args']: - if r not in vars: - continue - if re.match(r'.*?\b' + r + r'\b', d, re.I): - ddeps.append(r) - if d in vars: - if 'attrspec' in vars[d]: - for aa in vars[d]['attrspec']: - if aa[:6] == 'depend': - ddeps += aa[6:].strip()[1:-1].split(',') - if 'depend' in vars[d]: - ddeps = ddeps + vars[d]['depend'] - i = i + 1 - if d in vars and ('depend' not in vars[d]) \ - and ('=' not in vars[d]) and (d not in vars[n]['depend']) \ - and l_or(isintent_in, isintent_inout, isintent_inplace)(vars[n]): - vars[d]['depend'] = [n] - if ni > 1: - vars[d]['='] = '%s%s(%s,%s)%s' % ( - pd, shape_macro, n, i, ad) - else: - vars[d]['='] = '%slen(%s)%s' % (pd, n, ad) - # /---< no check - if 1 and 'check' not in vars[d]: - if ni > 1: - vars[d]['check'] = ['%s%s(%s,%i)%s==%s' - % (pd, shape_macro, n, i, ad, d)] - else: - vars[d]['check'] = [ - '%slen(%s)%s>=%s' % (pd, n, ad, d)] - if 'attrspec' not in vars[d]: - vars[d]['attrspec'] = ['optional'] - if ('optional' not in vars[d]['attrspec']) and\ - ('required' not in vars[d]['attrspec']): - vars[d]['attrspec'].append('optional') - elif d not in ['*', ':']: - #/----< no check - if flag: - if d in vars: - if n not in ddeps: - vars[n]['depend'].append(d) + # n is an argument that has no checks defined. Here we + # generate some consistency checks for n, and when n is an + # array, generate checks for its dimensions and construct + # initialization expressions. + n_deps = vars[n].get('depend', []) + n_checks = [] + n_is_input = l_or(isintent_in, isintent_inout, + isintent_inplace)(vars[n]) + if 'dimension' in vars[n]: # n is array + for i, d in enumerate(vars[n]['dimension']): + coeffs_and_deps = dimension_exprs.get(d) + if coeffs_and_deps is None: + # d is `:` or `*` or a constant expression + pass + elif n_is_input: + # n is an input array argument and its shape + # may define variables used in dimension + # specifications. + for v, (solver, deps) in coeffs_and_deps.items(): + if ((v in n_deps + or '=' in vars[v] + or 'depend' in vars[v])): + # Skip a variable that + # - n depends on + # - has user-defined initialization expression + # - has user-defined dependecies + continue + if solver is not None: + # v can be solved from d, hence, we + # make it an optional argument with + # initialization expression: + is_required = False + init = solver(symbolic.as_symbol( + f'shape({n}, {i})')) + init = init.tostring( + language=symbolic.Language.C) + vars[v]['='] = init + # n needs to be initialized before v. So, + # making v dependent on n and on any + # variables in solver or d. + vars[v]['depend'] = [n] + deps + if 'check' not in vars[v]: + # add check only when no + # user-specified checks exist + vars[v]['check'] = [ + f'shape({n}, {i}) == {d}'] else: - vars[n]['depend'] = vars[n]['depend'] + ddeps + # d is a non-linear function on v, + # hence, v must be a required input + # argument that n will depend on + is_required = True + if 'intent' not in vars[v]: + vars[v]['intent'] = [] + if 'in' not in vars[v]['intent']: + vars[v]['intent'].append('in') + # v needs to be initialized before n + n_deps.append(v) + n_checks.append( + f'shape({n}, {i}) == {d}') + v_attr = vars[v].get('attrspec', []) + if not ('optional' in v_attr + or 'required' in v_attr): + v_attr.append( + 'required' if is_required else 'optional') + if v_attr: + vars[v]['attrspec'] = v_attr + if coeffs_and_deps is not None: + # extend v dependencies with ones specified in attrspec + for v, (solver, deps) in coeffs_and_deps.items(): + v_deps = vars[v].get('depend', []) + for aa in vars[v].get('attrspec', []): + if aa.startswith('depend'): + aa = ''.join(aa.split()) + v_deps.extend(aa[7:-1].split(',')) + if v_deps: + vars[v]['depend'] = list(set(v_deps)) + if n not in v_deps: + n_deps.append(v) elif isstring(vars[n]): - length = '1' if 'charselector' in vars[n]: if '*' in vars[n]['charselector']: length = _eval_length(vars[n]['charselector']['*'], @@ -2781,11 +2725,11 @@ def analyzevars(block): params) del vars[n]['charselector']['len'] vars[n]['charselector']['*'] = length + if n_checks: + vars[n]['check'] = n_checks + if n_deps: + vars[n]['depend'] = list(set(n_deps)) - if not vars[n]['check']: - del vars[n]['check'] - if flag and not vars[n]['depend']: - del vars[n]['depend'] if '=' in vars[n]: if 'attrspec' not in vars[n]: vars[n]['attrspec'] = [] @@ -2811,8 +2755,6 @@ def analyzevars(block): vars[n] = appenddecl(vars[n], vars[block['result']]) if 'prefix' in block: pr = block['prefix'] - ispure = 0 - isrec = 1 pr1 = pr.replace('pure', '') ispure = (not pr == pr1) pr = pr1.replace('recursive', '') @@ -2915,7 +2857,7 @@ def expr2name(a, block, args=[]): def analyzeargs(block): setmesstext(block) - implicitrules, attrrules = buildimplicitrules(block) + implicitrules, _ = buildimplicitrules(block) if 'args' not in block: block['args'] = [] args = [] @@ -2939,10 +2881,10 @@ def analyzeargs(block): block['vars'][block['result']] = {} return block -determineexprtype_re_1 = re.compile(r'\A\(.+?[,].+?\)\Z', re.I) -determineexprtype_re_2 = re.compile(r'\A[+-]?\d+(_(?P<name>[\w]+)|)\Z', re.I) +determineexprtype_re_1 = re.compile(r'\A\(.+?,.+?\)\Z', re.I) +determineexprtype_re_2 = re.compile(r'\A[+-]?\d+(_(?P<name>\w+)|)\Z', re.I) determineexprtype_re_3 = re.compile( - r'\A[+-]?[\d.]+[\d+\-de.]*(_(?P<name>[\w]+)|)\Z', re.I) + r'\A[+-]?[\d.]+[-\d+de.]*(_(?P<name>\w+)|)\Z', re.I) determineexprtype_re_4 = re.compile(r'\A\(.*\)\Z', re.I) determineexprtype_re_5 = re.compile(r'\A(?P<name>\w+)\s*\(.*?\)\s*\Z', re.I) @@ -3060,7 +3002,7 @@ def crack2fortrangen(block, tab='\n', as_interface=False): result = ' result (%s)' % block['result'] if block['result'] not in argsl: argsl.append(block['result']) - body = crack2fortrangen(block['body'], tab + tabchar) + body = crack2fortrangen(block['body'], tab + tabchar, as_interface=as_interface) vars = vars2fortran( block, block['vars'], argsl, tab + tabchar, as_interface=as_interface) mess = '' @@ -3118,11 +3060,12 @@ def true_intent_list(var): ret = [] for intent in lst: try: - c = eval('isintent_%s(var)' % intent) - except NameError: - c = 0 - if c: - ret.append(intent) + f = globals()['isintent_%s' % intent] + except KeyError: + pass + else: + if f(var): + ret.append(intent) return ret @@ -3177,8 +3120,13 @@ def vars2fortran(block, vars, args, tab='', as_interface=False): show(vars) outmess('vars2fortran: No definition for argument "%s".\n' % a) continue - if a == block['name'] and not block['block'] == 'function': - continue + if a == block['name']: + if block['block'] != 'function' or block.get('result'): + # 1) skip declaring a variable that name matches with + # subroutine name + # 2) skip declaring function when its type is + # declared via `result` construction + continue if 'typespec' not in vars[a]: if 'attrspec' in vars[a] and 'external' in vars[a]['attrspec']: if a in args: @@ -3211,10 +3159,8 @@ def vars2fortran(block, vars, args, tab='', as_interface=False): vardef = '%s(kind=%s)' % (vardef, selector['kind']) c = ' ' if 'attrspec' in vars[a]: - attr = [] - for l in vars[a]['attrspec']: - if l not in ['external']: - attr.append(l) + attr = [l for l in vars[a]['attrspec'] + if l not in ['external']] if attr: vardef = '%s, %s' % (vardef, ','.join(attr)) c = ',' @@ -3272,7 +3218,8 @@ def crack2fortran(block): """ footer = """ ! This file was auto-generated with f2py (version:%s). -! See http://cens.ioc.ee/projects/f2py2e/ +! See: +! https://web.archive.org/web/20140822061353/http://cens.ioc.ee/projects/f2py2e """ % (f2py_version) return header + pyf + footer @@ -3329,8 +3276,8 @@ def crack2fortran(block): try: open(l).close() files.append(l) - except IOError as detail: - errmess('IOError: %s\n' % str(detail)) + except OSError as detail: + errmess(f'OSError: {detail!s}\n') else: funcs.append(l) if not strictf77 and f77modulename and not skipemptyends: @@ -3341,7 +3288,7 @@ def crack2fortran(block): and also be sure that the files do not contain programs without program statement). """, 0) - postlist = crackfortran(files, funcs) + postlist = crackfortran(files) if pyffilename: outmess('Writing fortran code to file %s\n' % repr(pyffilename), 0) pyf = crack2fortran(postlist) diff --git a/numpy/f2py/diagnose.py b/numpy/f2py/diagnose.py index 0241fed12ffe..21ee399f035f 100644 --- a/numpy/f2py/diagnose.py +++ b/numpy/f2py/diagnose.py @@ -1,6 +1,4 @@ -#!/usr/bin/env python -from __future__ import division, absolute_import, print_function - +#!/usr/bin/env python3 import os import sys import tempfile diff --git a/numpy/f2py/f2py2e.py b/numpy/f2py/f2py2e.py index 254f99966464..4d79c304ae91 100755 --- a/numpy/f2py/f2py2e.py +++ b/numpy/f2py/f2py2e.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """ f2py2e - Fortran to Python C/API generator. 2nd Edition. @@ -14,8 +14,6 @@ Pearu Peterson """ -from __future__ import division, absolute_import, print_function - import sys import os import pprint @@ -28,20 +26,17 @@ from . import cfuncs from . import f90mod_rules from . import __version__ +from . import capi_maps f2py_version = __version__.version +numpy_version = __version__.version errmess = sys.stderr.write # outmess=sys.stdout.write show = pprint.pprint outmess = auxfuncs.outmess -try: - from numpy import __version__ as numpy_version -except ImportError: - numpy_version = 'N/A' - -__usage__ = """\ -Usage: +__usage__ =\ +f"""Usage: 1) To construct extension module sources: @@ -98,8 +93,8 @@ --[no-]latex-doc Create (or not) <modulename>module.tex. Default is --no-latex-doc. --short-latex Create 'incomplete' LaTeX document (without commands - \\documentclass, \\tableofcontents, and \\begin{document}, - \\end{document}). + \\documentclass, \\tableofcontents, and \\begin{{document}}, + \\end{{document}}). --[no-]rest-doc Create (or not) <modulename>module.rst. Default is --no-rest-doc. @@ -118,6 +113,9 @@ --link-<resource> switch below. [..] is optional list of resources names. E.g. try 'f2py --help-link lapack_opt'. + --f2cmap <filename> Load Fortran-to-Python KIND specification from the given + file. Default: .f2py_f2cmap in current directory. + --quiet Run quietly. --verbose Run with extra verbosity. -v Print f2py version ID and exit. @@ -165,17 +163,17 @@ array. Integer <int> sets the threshold for array sizes when a message should be shown. -Version: %s -numpy Version: %s -Requires: Python 2.3 or higher. +Version: {f2py_version} +numpy Version: {numpy_version} +Requires: Python 3.5 or higher. License: NumPy license (see LICENSE.txt in the NumPy source code) Copyright 1999 - 2011 Pearu Peterson all rights reserved. -http://cens.ioc.ee/projects/f2py2e/""" % (f2py_version, numpy_version) +https://web.archive.org/web/20140822061353/http://cens.ioc.ee/projects/f2py2e""" def scaninputline(inputline): files, skipfuncs, onlyfuncs, debug = [], [], [], [] - f, f2, f3, f5, f6, f7, f8, f9 = 1, 0, 0, 0, 0, 0, 0, 0 + f, f2, f3, f5, f6, f7, f8, f9, f10 = 1, 0, 0, 0, 0, 0, 0, 0, 0 verbose = 1 dolc = -1 dolatexdoc = 0 @@ -226,6 +224,8 @@ def scaninputline(inputline): f8 = 1 elif l == '--f2py-wrapper-output': f9 = 1 + elif l == '--f2cmap': + f10 = 1 elif l == '--overwrite-signature': options['h-overwrite'] = 1 elif l == '-h': @@ -267,13 +267,16 @@ def scaninputline(inputline): elif f9: f9 = 0 options["f2py_wrapper_output"] = l + elif f10: + f10 = 0 + options["f2cmap_file"] = l elif f == 1: try: - open(l).close() + with open(l): + pass files.append(l) - except IOError as detail: - errmess('IOError: %s. Skipping file "%s".\n' % - (str(detail), l)) + except OSError as detail: + errmess(f'OSError: {detail!s}. Skipping file "{l!s}".\n') elif f == -1: skipfuncs.append(l) elif f == 0: @@ -283,7 +286,7 @@ def scaninputline(inputline): sys.exit() if not os.path.isdir(buildpath): if not verbose: - outmess('Creating build directory %s' % (buildpath)) + outmess('Creating build directory %s\n' % (buildpath)) os.mkdir(buildpath) if signsfile: signsfile = os.path.join(buildpath, signsfile) @@ -311,6 +314,7 @@ def scaninputline(inputline): options['wrapfuncs'] = wrapfuncs options['buildpath'] = buildpath options['include_paths'] = include_paths + options.setdefault('f2cmap_file', None) return files, options @@ -333,9 +337,8 @@ def callcrackfortran(files, options): if options['signsfile'][-6:] == 'stdout': sys.stdout.write(pyf) else: - f = open(options['signsfile'], 'w') - f.write(pyf) - f.close() + with open(options['signsfile'], 'w') as f: + f.write(pyf) if options["coutput"] is None: for mod in postlist: mod["coutput"] = "%smodule.c" % mod["name"] @@ -355,33 +358,34 @@ def buildmodules(lst): cfuncs.buildcfuncs() outmess('Building modules...\n') modules, mnames, isusedby = [], [], {} - for i in range(len(lst)): - if '__user__' in lst[i]['name']: - cb_rules.buildcallbacks(lst[i]) + for item in lst: + if '__user__' in item['name']: + cb_rules.buildcallbacks(item) else: - if 'use' in lst[i]: - for u in lst[i]['use'].keys(): + if 'use' in item: + for u in item['use'].keys(): if u not in isusedby: isusedby[u] = [] - isusedby[u].append(lst[i]['name']) - modules.append(lst[i]) - mnames.append(lst[i]['name']) + isusedby[u].append(item['name']) + modules.append(item) + mnames.append(item['name']) ret = {} - for i in range(len(mnames)): - if mnames[i] in isusedby: + for module, name in zip(modules, mnames): + if name in isusedby: outmess('\tSkipping module "%s" which is used by %s.\n' % ( - mnames[i], ','.join(['"%s"' % s for s in isusedby[mnames[i]]]))) + name, ','.join('"%s"' % s for s in isusedby[name]))) else: um = [] - if 'use' in modules[i]: - for u in modules[i]['use'].keys(): + if 'use' in module: + for u in module['use'].keys(): if u in isusedby and u in mnames: um.append(modules[mnames.index(u)]) else: outmess( - '\tModule "%s" uses nonexisting "%s" which will be ignored.\n' % (mnames[i], u)) - ret[mnames[i]] = {} - dict_append(ret[mnames[i]], rules.buildmodule(modules[i], um)) + f'\tModule "{name}" uses nonexisting "{u}" ' + 'which will be ignored.\n') + ret[name] = {} + dict_append(ret[name], rules.buildmodule(module, um)) return ret @@ -396,8 +400,25 @@ def dict_append(d_out, d_in): def run_main(comline_list): - """Run f2py as if string.join(comline_list,' ') is used as a command line. - In case of using -h flag, return None. + """ + Equivalent to running:: + + f2py <args> + + where ``<args>=string.join(<list>,' ')``, but in Python. Unless + ``-h`` is used, this function returns a dictionary containing + information on generated modules and their dependencies on source + files. For example, the command ``f2py -m scalar scalar.f`` can be + executed from Python as follows + + You cannot build extension modules with this function, that is, + using ``-c`` is not allowed. Use ``compile`` command instead + + Examples + -------- + .. literalinclude:: ../../source/f2py/code/results/run_main_session.dat + :language: python + """ crackfortran.reset_global_f2py_vars() f2pydir = os.path.dirname(os.path.abspath(cfuncs.__file__)) @@ -405,20 +426,23 @@ def run_main(comline_list): fobjcsrc = os.path.join(f2pydir, 'src', 'fortranobject.c') files, options = scaninputline(comline_list) auxfuncs.options = options + capi_maps.load_f2cmap_file(options['f2cmap_file']) postlist = callcrackfortran(files, options) isusedby = {} - for i in range(len(postlist)): - if 'use' in postlist[i]: - for u in postlist[i]['use'].keys(): + for plist in postlist: + if 'use' in plist: + for u in plist['use'].keys(): if u not in isusedby: isusedby[u] = [] - isusedby[u].append(postlist[i]['name']) - for i in range(len(postlist)): - if postlist[i]['block'] == 'python module' and '__user__' in postlist[i]['name']: - if postlist[i]['name'] in isusedby: + isusedby[u].append(plist['name']) + for plist in postlist: + if plist['block'] == 'python module' and '__user__' in plist['name']: + if plist['name'] in isusedby: # if not quiet: - outmess('Skipping Makefile build for module "%s" which is used by %s\n' % ( - postlist[i]['name'], ','.join(['"%s"' % s for s in isusedby[postlist[i]['name']]]))) + outmess( + f'Skipping Makefile build for module "{plist["name"]}" ' + 'which is used by {}\n'.format( + ','.join(f'"{s}"' for s in isusedby[plist['name']]))) if 'signsfile' in options: if options['verbose'] > 1: outmess( @@ -426,8 +450,8 @@ def run_main(comline_list): outmess('%s %s\n' % (os.path.basename(sys.argv[0]), options['signsfile'])) return - for i in range(len(postlist)): - if postlist[i]['block'] != 'python module': + for plist in postlist: + if plist['block'] != 'python module': if 'python module' not in options: errmess( 'Tip: If your original code is Fortran source then you must use -m option.\n') @@ -489,14 +513,14 @@ def run_compile(): remove_build_dir = 1 build_dir = tempfile.mkdtemp() - _reg1 = re.compile(r'[-][-]link[-]') + _reg1 = re.compile(r'--link-') sysinfo_flags = [_m for _m in sys.argv[1:] if _reg1.match(_m)] sys.argv = [_m for _m in sys.argv if _m not in sysinfo_flags] if sysinfo_flags: sysinfo_flags = [f[7:] for f in sysinfo_flags] _reg2 = re.compile( - r'[-][-]((no[-]|)(wrap[-]functions|lower)|debug[-]capi|quiet)|[-]include') + r'--((no-|)(wrap-functions|lower)|debug-capi|quiet)|-include') f2py_flags = [_m for _m in sys.argv[1:] if _reg2.match(_m)] sys.argv = [_m for _m in sys.argv if _m not in f2py_flags] f2py_flags2 = [] @@ -514,40 +538,39 @@ def run_compile(): sys.argv = [_m for _m in sys.argv if _m not in f2py_flags2] _reg3 = re.compile( - r'[-][-]((f(90)?compiler([-]exec|)|compiler)=|help[-]compiler)') + r'--((f(90)?compiler(-exec|)|compiler)=|help-compiler)') flib_flags = [_m for _m in sys.argv[1:] if _reg3.match(_m)] sys.argv = [_m for _m in sys.argv if _m not in flib_flags] _reg4 = re.compile( - r'[-][-]((f(77|90)(flags|exec)|opt|arch)=|(debug|noopt|noarch|help[-]fcompiler))') + r'--((f(77|90)(flags|exec)|opt|arch)=|(debug|noopt|noarch|help-fcompiler))') fc_flags = [_m for _m in sys.argv[1:] if _reg4.match(_m)] sys.argv = [_m for _m in sys.argv if _m not in fc_flags] - if 1: - del_list = [] - for s in flib_flags: - v = '--fcompiler=' - if s[:len(v)] == v: - from numpy.distutils import fcompiler - fcompiler.load_all_fcompiler_classes() - allowed_keys = list(fcompiler.fcompiler_class.keys()) - nv = ov = s[len(v):].lower() - if ov not in allowed_keys: - vmap = {} # XXX - try: - nv = vmap[ov] - except KeyError: - if ov not in vmap.values(): - print('Unknown vendor: "%s"' % (s[len(v):])) - nv = ov - i = flib_flags.index(s) - flib_flags[i] = '--fcompiler=' + nv - continue - for s in del_list: + del_list = [] + for s in flib_flags: + v = '--fcompiler=' + if s[:len(v)] == v: + from numpy.distutils import fcompiler + fcompiler.load_all_fcompiler_classes() + allowed_keys = list(fcompiler.fcompiler_class.keys()) + nv = ov = s[len(v):].lower() + if ov not in allowed_keys: + vmap = {} # XXX + try: + nv = vmap[ov] + except KeyError: + if ov not in vmap.values(): + print('Unknown vendor: "%s"' % (s[len(v):])) + nv = ov i = flib_flags.index(s) - del flib_flags[i] - assert len(flib_flags) <= 2, repr(flib_flags) - - _reg5 = re.compile(r'[-][-](verbose)') + flib_flags[i] = '--fcompiler=' + nv + continue + for s in del_list: + i = flib_flags.index(s) + del flib_flags[i] + assert len(flib_flags) <= 2, repr(flib_flags) + + _reg5 = re.compile(r'--(verbose)') setup_flags = [_m for _m in sys.argv[1:] if _reg5.match(_m)] sys.argv = [_m for _m in sys.argv if _m not in setup_flags] @@ -557,7 +580,7 @@ def run_compile(): modulename = 'untitled' sources = sys.argv[1:] - for optname in ['--include_paths', '--include-paths']: + for optname in ['--include_paths', '--include-paths', '--f2cmap']: if optname in sys.argv: i = sys.argv.index(optname) f2py_flags.extend(sys.argv[i:i + 2]) @@ -578,7 +601,7 @@ def run_compile(): if modulename: break - extra_objects, sources = filter_files('', '[.](o|a|so)', sources) + extra_objects, sources = filter_files('', '[.](o|a|so|dylib)', sources) include_dirs, sources = filter_files('-I', '', sources, remove_prefix=1) library_dirs, sources = filter_files('-L', '', sources, remove_prefix=1) libraries, sources = filter_files('-l', '', sources, remove_prefix=1) @@ -624,7 +647,9 @@ def run_compile(): sys.argv.extend(['build', '--build-temp', build_dir, '--build-base', build_dir, - '--build-platlib', '.']) + '--build-platlib', '.', + # disable CCompilerOpt + '--disable-optimization']) if fc_flags: sys.argv.extend(['config_fc'] + fc_flags) if flib_flags: @@ -644,13 +669,25 @@ def main(): from numpy.distutils.system_info import show_all show_all() return + + # Probably outdated options that were not working before 1.16 + if '--g3-numpy' in sys.argv[1:]: + sys.stderr.write("G3 f2py support is not implemented, yet.\\n") + sys.exit(1) + elif '--2e-numeric' in sys.argv[1:]: + sys.argv.remove('--2e-numeric') + elif '--2e-numarray' in sys.argv[1:]: + # Note that this errors becaust the -DNUMARRAY argument is + # not recognized. Just here for back compatibility and the + # error message. + sys.argv.append("-DNUMARRAY") + sys.argv.remove('--2e-numarray') + elif '--2e-numpy' in sys.argv[1:]: + sys.argv.remove('--2e-numpy') + else: + pass + if '-c' in sys.argv[1:]: run_compile() else: run_main(sys.argv[1:]) - -# if __name__ == "__main__": -# main() - - -# EOF diff --git a/numpy/f2py/f2py_testing.py b/numpy/f2py/f2py_testing.py index f5d5fa63d03f..1f109e67a5e2 100644 --- a/numpy/f2py/f2py_testing.py +++ b/numpy/f2py/f2py_testing.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - import sys import re diff --git a/numpy/f2py/f90mod_rules.py b/numpy/f2py/f90mod_rules.py index 85eae8047928..3e1c9674f8e2 100644 --- a/numpy/f2py/f90mod_rules.py +++ b/numpy/f2py/f90mod_rules.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """ Build F90 module support for f2py2e. @@ -13,8 +13,6 @@ Pearu Peterson """ -from __future__ import division, absolute_import, print_function - __version__ = "$Revision: 1.27 $"[10:-1] f2py_version = 'See `f2py -v`' @@ -25,7 +23,7 @@ from . import func2subr from .crackfortran import undo_rmbadname, undo_rmbadname1 -# The eviroment provided by auxfuncs.py is needed for some calls to eval. +# The environment provided by auxfuncs.py is needed for some calls to eval. # As the needed functions cannot be determined by static inspection of the # code, it is safest to use import * pending a major refactoring of f2py. from .auxfuncs import * @@ -87,7 +85,6 @@ def findf90modules(m): def buildhooks(pymod): - global fgetdims1, fgetdims2 from . import rules ret = {'f90modhooks': [], 'initf90modhooks': [], 'body': [], 'need': ['F_FUNC', 'arrayobject.h'], @@ -180,7 +177,7 @@ def iadd(line, s=ihooks): (m['name'], undo_rmbadname1(n))) fadd('integer flag\n') fhooks[0] = fhooks[0] + fgetdims1 - dms = eval('range(1,%s+1)' % (dm['rank'])) + dms = range(1, int(dm['rank']) + 1) fadd(' allocate(d(%s))\n' % (','.join(['s(%s)' % i for i in dms]))) fhooks[0] = fhooks[0] + use_fgetdims2 @@ -195,7 +192,8 @@ def iadd(line, s=ihooks): if hasbody(m): for b in m['body']: if not isroutine(b): - print('Skipping', b['block'], b['name']) + outmess("f90mod_rules.buildhooks:" + f" skipping {b['block']} {b['name']}\n") continue modobjs.append('%s()' % (b['name'])) b['modulename'] = m['name'] diff --git a/numpy/f2py/func2subr.py b/numpy/f2py/func2subr.py index 6010d5a231af..21d4c009cc26 100644 --- a/numpy/f2py/func2subr.py +++ b/numpy/f2py/func2subr.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """ Rules for building C/API module with f2py2e. @@ -13,8 +13,6 @@ Pearu Peterson """ -from __future__ import division, absolute_import, print_function - __version__ = "$Revision: 1.16 $"[10:-1] f2py_version = 'See `f2py -v`' @@ -132,7 +130,7 @@ def add(line, ret=ret): l = l + ', ' + fortranname if need_interface: for line in rout['saved_interface'].split('\n'): - if line.lstrip().startswith('use '): + if line.lstrip().startswith('use ') and '__user__' not in line: add(line) args = args[1:] @@ -224,7 +222,7 @@ def add(line, ret=ret): if need_interface: for line in rout['saved_interface'].split('\n'): - if line.lstrip().startswith('use '): + if line.lstrip().startswith('use ') and '__user__' not in line: add(line) dumped_args = [] @@ -249,7 +247,10 @@ def add(line, ret=ret): pass else: add('interface') - add(rout['saved_interface'].lstrip()) + for line in rout['saved_interface'].split('\n'): + if line.lstrip().startswith('use ') and '__user__' in line: + continue + add(line) add('end interface') sargs = ', '.join([a for a in args if a not in extra_args]) diff --git a/numpy/f2py/info.py b/numpy/f2py/info.py deleted file mode 100644 index c895c5de28d0..000000000000 --- a/numpy/f2py/info.py +++ /dev/null @@ -1,6 +0,0 @@ -"""Fortran to Python Interface Generator. - -""" -from __future__ import division, absolute_import, print_function - -postpone_import = True diff --git a/numpy/f2py/rules.py b/numpy/f2py/rules.py old mode 100644 new mode 100755 index 36e2222eac7b..78810a0a74a9 --- a/numpy/f2py/rules.py +++ b/numpy/f2py/rules.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """ Rules for building C/API module with f2py2e. @@ -50,17 +50,15 @@ Pearu Peterson """ -from __future__ import division, absolute_import, print_function - -__version__ = "$Revision: 1.129 $"[10:-1] - -from . import __version__ -f2py_version = __version__.version - import os import time import copy +# __version__.version is now the same as the NumPy version +from . import __version__ +f2py_version = __version__.version +numpy_version = __version__.version + from .auxfuncs import ( applyrules, debugcapi, dictappend, errmess, gentitle, getargs2, hascallstatement, hasexternals, hasinitvalue, hasnote, hasresultnote, @@ -75,7 +73,7 @@ issubroutine, issubroutine_wrap, isthreadsafe, isunsigned, isunsigned_char, isunsigned_chararray, isunsigned_long_long, isunsigned_long_longarray, isunsigned_short, isunsigned_shortarray, - l_and, l_not, l_or, outmess, replace, stripcomma, + l_and, l_not, l_or, outmess, replace, stripcomma, requiresf90wrapper ) from . import capi_maps @@ -122,6 +120,10 @@ extern \"C\" { #endif +#ifndef PY_SSIZE_T_CLEAN +#define PY_SSIZE_T_CLEAN +#endif /* PY_SSIZE_T_CLEAN */ + """ + gentitle("See f2py2e/cfuncs.py: includes") + """ #includes# #includes0# @@ -172,71 +174,67 @@ static FortranDataDef f2py_routine_defs[] = { #routine_defs# -\t{NULL} + {NULL} }; static PyMethodDef f2py_module_methods[] = { #pymethoddef# -\t{NULL,NULL} + {NULL,NULL} }; -#if PY_VERSION_HEX >= 0x03000000 static struct PyModuleDef moduledef = { -\tPyModuleDef_HEAD_INIT, -\t"#modulename#", -\tNULL, -\t-1, -\tf2py_module_methods, -\tNULL, -\tNULL, -\tNULL, -\tNULL + PyModuleDef_HEAD_INIT, + "#modulename#", + NULL, + -1, + f2py_module_methods, + NULL, + NULL, + NULL, + NULL }; -#endif -#if PY_VERSION_HEX >= 0x03000000 -#define RETVAL m PyMODINIT_FUNC PyInit_#modulename#(void) { -#else -#define RETVAL -PyMODINIT_FUNC init#modulename#(void) { -#endif -\tint i; -\tPyObject *m,*d, *s; -#if PY_VERSION_HEX >= 0x03000000 -\tm = #modulename#_module = PyModule_Create(&moduledef); -#else -\tm = #modulename#_module = Py_InitModule(\"#modulename#\", f2py_module_methods); -#endif -\tPy_TYPE(&PyFortran_Type) = &PyType_Type; -\timport_array(); -\tif (PyErr_Occurred()) -\t\t{PyErr_SetString(PyExc_ImportError, \"can't initialize module #modulename# (failed to import numpy)\"); return RETVAL;} -\td = PyModule_GetDict(m); -\ts = PyString_FromString(\"$R""" + """evision: $\"); -\tPyDict_SetItemString(d, \"__version__\", s); -#if PY_VERSION_HEX >= 0x03000000 -\ts = PyUnicode_FromString( -#else -\ts = PyString_FromString( -#endif -\t\t\"This module '#modulename#' is auto-generated with f2py (version:#f2py_version#).\\nFunctions:\\n\"\n#docs#\".\"); -\tPyDict_SetItemString(d, \"__doc__\", s); -\t#modulename#_error = PyErr_NewException (\"#modulename#.error\", NULL, NULL); -\tPy_DECREF(s); -\tfor(i=0;f2py_routine_defs[i].name!=NULL;i++) -\t\tPyDict_SetItemString(d, f2py_routine_defs[i].name,PyFortranObject_NewAsAttr(&f2py_routine_defs[i])); + int i; + PyObject *m,*d, *s, *tmp; + m = #modulename#_module = PyModule_Create(&moduledef); + Py_SET_TYPE(&PyFortran_Type, &PyType_Type); + import_array(); + if (PyErr_Occurred()) + {PyErr_SetString(PyExc_ImportError, \"can't initialize module #modulename# (failed to import numpy)\"); return m;} + d = PyModule_GetDict(m); + s = PyUnicode_FromString(\"#f2py_version#\"); + PyDict_SetItemString(d, \"__version__\", s); + Py_DECREF(s); + s = PyUnicode_FromString( + \"This module '#modulename#' is auto-generated with f2py (version:#f2py_version#).\\nFunctions:\\n\"\n#docs#\".\"); + PyDict_SetItemString(d, \"__doc__\", s); + Py_DECREF(s); + s = PyUnicode_FromString(\"""" + numpy_version + """\"); + PyDict_SetItemString(d, \"__f2py_numpy_version__\", s); + Py_DECREF(s); + #modulename#_error = PyErr_NewException (\"#modulename#.error\", NULL, NULL); + /* + * Store the error object inside the dict, so that it could get deallocated. + * (in practice, this is a module, so it likely will not and cannot.) + */ + PyDict_SetItemString(d, \"_#modulename#_error\", #modulename#_error); + Py_DECREF(#modulename#_error); + for(i=0;f2py_routine_defs[i].name!=NULL;i++) { + tmp = PyFortranObject_NewAsAttr(&f2py_routine_defs[i]); + PyDict_SetItemString(d, f2py_routine_defs[i].name, tmp); + Py_DECREF(tmp); + } #initf2pywraphooks# #initf90modhooks# #initcommonhooks# #interface_usercode# #ifdef F2PY_REPORT_ATEXIT -\tif (! PyErr_Occurred()) -\t\ton_exit(f2py_report_on_exit,(void*)\"#modulename#\"); + if (! PyErr_Occurred()) + on_exit(f2py_report_on_exit,(void*)\"#modulename#\"); #endif - -\treturn RETVAL; + return m; } #ifdef __cplusplus } @@ -275,18 +273,18 @@ PyObject *capi_args, PyObject *capi_keywds, #functype# (*f2py_func)(#callprotoargument#)) { -\tPyObject * volatile capi_buildvalue = NULL; -\tvolatile int f2py_success = 1; + PyObject * volatile capi_buildvalue = NULL; + volatile int f2py_success = 1; #decl# -\tstatic char *capi_kwlist[] = {#kwlist##kwlistopt##kwlistxa#NULL}; + static char *capi_kwlist[] = {#kwlist##kwlistopt##kwlistxa#NULL}; #usercode# #routdebugenter# #ifdef F2PY_REPORT_ATEXIT f2py_start_clock(); #endif -\tif (!PyArg_ParseTupleAndKeywords(capi_args,capi_keywds,\\ -\t\t\"#argformat##keyformat##xaformat#:#pyname#\",\\ -\t\tcapi_kwlist#args_capi##keys_capi##keys_xa#))\n\t\treturn NULL; + if (!PyArg_ParseTupleAndKeywords(capi_args,capi_keywds,\\ + \"#argformat#|#keyformat##xaformat#:#pyname#\",\\ + capi_kwlist#args_capi##keys_capi##keys_xa#))\n return NULL; #frompyobj# /*end of frompyobj*/ #ifdef F2PY_REPORT_ATEXIT @@ -299,27 +297,27 @@ f2py_stop_call_clock(); #endif /*end of callfortranroutine*/ -\t\tif (f2py_success) { + if (f2py_success) { #pyobjfrom# /*end of pyobjfrom*/ -\t\tCFUNCSMESS(\"Building return value.\\n\"); -\t\tcapi_buildvalue = Py_BuildValue(\"#returnformat#\"#return#); + CFUNCSMESS(\"Building return value.\\n\"); + capi_buildvalue = Py_BuildValue(\"#returnformat#\"#return#); /*closepyobjfrom*/ #closepyobjfrom# -\t\t} /*if (f2py_success) after callfortranroutine*/ + } /*if (f2py_success) after callfortranroutine*/ /*cleanupfrompyobj*/ #cleanupfrompyobj# -\tif (capi_buildvalue == NULL) { + if (capi_buildvalue == NULL) { #routdebugfailure# -\t} else { + } else { #routdebugleave# -\t} -\tCFUNCSMESS(\"Freeing memory.\\n\"); + } + CFUNCSMESS(\"Freeing memory.\\n\"); #freemem# #ifdef F2PY_REPORT_ATEXIT f2py_stop_clock(); #endif -\treturn capi_buildvalue; + return capi_buildvalue; } #endtitle# """, @@ -328,7 +326,7 @@ 'externroutines': '#declfortranroutine#', 'doc': '#docreturn##name#(#docsignature#)', 'docshort': '#docreturn##name#(#docsignatureshort#)', - 'docs': '"\t#docreturn##name#(#docsignature#)\\n"\n', + 'docs': '" #docreturn##name#(#docsignature#)\\n"\n', 'need': ['arrayobject.h', 'CFUNCSMESS', 'MINMAX'], 'cppmacros': {debugcapi: '#define DEBUGCFUNCS'}, 'latexdoc': ['\\subsection{Wrapper function \\texttt{#texname#}}\n', @@ -402,25 +400,25 @@ ismoduleroutine: '', isdummyroutine: '' }, - 'routine_def': {l_not(l_or(ismoduleroutine, isintent_c, isdummyroutine)): '\t{\"#name#\",-1,{{-1}},0,(char *)#F_FUNC#(#fortranname#,#FORTRANNAME#),(f2py_init_func)#apiname#,doc_#apiname#},', - l_and(l_not(ismoduleroutine), isintent_c, l_not(isdummyroutine)): '\t{\"#name#\",-1,{{-1}},0,(char *)#fortranname#,(f2py_init_func)#apiname#,doc_#apiname#},', - l_and(l_not(ismoduleroutine), isdummyroutine): '\t{\"#name#\",-1,{{-1}},0,NULL,(f2py_init_func)#apiname#,doc_#apiname#},', + 'routine_def': {l_not(l_or(ismoduleroutine, isintent_c, isdummyroutine)): ' {\"#name#\",-1,{{-1}},0,(char *)#F_FUNC#(#fortranname#,#FORTRANNAME#),(f2py_init_func)#apiname#,doc_#apiname#},', + l_and(l_not(ismoduleroutine), isintent_c, l_not(isdummyroutine)): ' {\"#name#\",-1,{{-1}},0,(char *)#fortranname#,(f2py_init_func)#apiname#,doc_#apiname#},', + l_and(l_not(ismoduleroutine), isdummyroutine): ' {\"#name#\",-1,{{-1}},0,NULL,(f2py_init_func)#apiname#,doc_#apiname#},', }, 'need': {l_and(l_not(l_or(ismoduleroutine, isintent_c)), l_not(isdummyroutine)): 'F_FUNC'}, 'callfortranroutine': [ {debugcapi: [ - """\tfprintf(stderr,\"debug-capi:Fortran subroutine `#fortranname#(#callfortran#)\'\\n\");"""]}, + """ fprintf(stderr,\"debug-capi:Fortran subroutine `#fortranname#(#callfortran#)\'\\n\");"""]}, {hasexternals: """\ -\t\tif (#setjmpbuf#) { -\t\t\tf2py_success = 0; -\t\t} else {"""}, - {isthreadsafe: '\t\t\tPy_BEGIN_ALLOW_THREADS'}, - {hascallstatement: '''\t\t\t\t#callstatement#; -\t\t\t\t/*(*f2py_func)(#callfortran#);*/'''}, + if (#setjmpbuf#) { + f2py_success = 0; + } else {"""}, + {isthreadsafe: ' Py_BEGIN_ALLOW_THREADS'}, + {hascallstatement: ''' #callstatement#; + /*(*f2py_func)(#callfortran#);*/'''}, {l_not(l_or(hascallstatement, isdummyroutine)) - : '\t\t\t\t(*f2py_func)(#callfortran#);'}, - {isthreadsafe: '\t\t\tPy_END_ALLOW_THREADS'}, - {hasexternals: """\t\t}"""} + : ' (*f2py_func)(#callfortran#);'}, + {isthreadsafe: ' Py_END_ALLOW_THREADS'}, + {hasexternals: """ }"""} ], '_check': l_and(issubroutine, l_not(issubroutine_wrap)), }, { # Wrapped function @@ -429,36 +427,36 @@ isdummyroutine: '', }, - 'routine_def': {l_not(l_or(ismoduleroutine, isdummyroutine)): '\t{\"#name#\",-1,{{-1}},0,(char *)#F_WRAPPEDFUNC#(#name_lower#,#NAME#),(f2py_init_func)#apiname#,doc_#apiname#},', - isdummyroutine: '\t{\"#name#\",-1,{{-1}},0,NULL,(f2py_init_func)#apiname#,doc_#apiname#},', + 'routine_def': {l_not(l_or(ismoduleroutine, isdummyroutine)): ' {\"#name#\",-1,{{-1}},0,(char *)#F_WRAPPEDFUNC#(#name_lower#,#NAME#),(f2py_init_func)#apiname#,doc_#apiname#},', + isdummyroutine: ' {\"#name#\",-1,{{-1}},0,NULL,(f2py_init_func)#apiname#,doc_#apiname#},', }, 'initf2pywraphook': {l_not(l_or(ismoduleroutine, isdummyroutine)): ''' { extern #ctype# #F_FUNC#(#name_lower#,#NAME#)(void); PyObject* o = PyDict_GetItemString(d,"#name#"); - PyObject_SetAttrString(o,"_cpointer", F2PyCapsule_FromVoidPtr((void*)#F_FUNC#(#name_lower#,#NAME#),NULL)); -#if PY_VERSION_HEX >= 0x03000000 - PyObject_SetAttrString(o,"__name__", PyUnicode_FromString("#name#")); -#else - PyObject_SetAttrString(o,"__name__", PyString_FromString("#name#")); -#endif + tmp = F2PyCapsule_FromVoidPtr((void*)#F_FUNC#(#name_lower#,#NAME#),NULL); + PyObject_SetAttrString(o,"_cpointer", tmp); + Py_DECREF(tmp); + s = PyUnicode_FromString("#name#"); + PyObject_SetAttrString(o,"__name__", s); + Py_DECREF(s); } '''}, 'need': {l_not(l_or(ismoduleroutine, isdummyroutine)): ['F_WRAPPEDFUNC', 'F_FUNC']}, 'callfortranroutine': [ {debugcapi: [ - """\tfprintf(stderr,\"debug-capi:Fortran subroutine `f2pywrap#name_lower#(#callfortran#)\'\\n\");"""]}, + """ fprintf(stderr,\"debug-capi:Fortran subroutine `f2pywrap#name_lower#(#callfortran#)\'\\n\");"""]}, {hasexternals: """\ -\tif (#setjmpbuf#) { -\t\tf2py_success = 0; -\t} else {"""}, - {isthreadsafe: '\tPy_BEGIN_ALLOW_THREADS'}, + if (#setjmpbuf#) { + f2py_success = 0; + } else {"""}, + {isthreadsafe: ' Py_BEGIN_ALLOW_THREADS'}, {l_not(l_or(hascallstatement, isdummyroutine)) - : '\t(*f2py_func)(#callfortran#);'}, + : ' (*f2py_func)(#callfortran#);'}, {hascallstatement: - '\t#callstatement#;\n\t/*(*f2py_func)(#callfortran#);*/'}, - {isthreadsafe: '\tPy_END_ALLOW_THREADS'}, - {hasexternals: '\t}'} + ' #callstatement#;\n /*(*f2py_func)(#callfortran#);*/'}, + {isthreadsafe: ' Py_END_ALLOW_THREADS'}, + {hasexternals: ' }'} ], '_check': isfunction_wrap, }, { # Wrapped subroutine @@ -467,36 +465,36 @@ isdummyroutine: '', }, - 'routine_def': {l_not(l_or(ismoduleroutine, isdummyroutine)): '\t{\"#name#\",-1,{{-1}},0,(char *)#F_WRAPPEDFUNC#(#name_lower#,#NAME#),(f2py_init_func)#apiname#,doc_#apiname#},', - isdummyroutine: '\t{\"#name#\",-1,{{-1}},0,NULL,(f2py_init_func)#apiname#,doc_#apiname#},', + 'routine_def': {l_not(l_or(ismoduleroutine, isdummyroutine)): ' {\"#name#\",-1,{{-1}},0,(char *)#F_WRAPPEDFUNC#(#name_lower#,#NAME#),(f2py_init_func)#apiname#,doc_#apiname#},', + isdummyroutine: ' {\"#name#\",-1,{{-1}},0,NULL,(f2py_init_func)#apiname#,doc_#apiname#},', }, 'initf2pywraphook': {l_not(l_or(ismoduleroutine, isdummyroutine)): ''' { extern void #F_FUNC#(#name_lower#,#NAME#)(void); PyObject* o = PyDict_GetItemString(d,"#name#"); - PyObject_SetAttrString(o,"_cpointer", F2PyCapsule_FromVoidPtr((void*)#F_FUNC#(#name_lower#,#NAME#),NULL)); -#if PY_VERSION_HEX >= 0x03000000 - PyObject_SetAttrString(o,"__name__", PyUnicode_FromString("#name#")); -#else - PyObject_SetAttrString(o,"__name__", PyString_FromString("#name#")); -#endif + tmp = F2PyCapsule_FromVoidPtr((void*)#F_FUNC#(#name_lower#,#NAME#),NULL); + PyObject_SetAttrString(o,"_cpointer", tmp); + Py_DECREF(tmp); + s = PyUnicode_FromString("#name#"); + PyObject_SetAttrString(o,"__name__", s); + Py_DECREF(s); } '''}, 'need': {l_not(l_or(ismoduleroutine, isdummyroutine)): ['F_WRAPPEDFUNC', 'F_FUNC']}, 'callfortranroutine': [ {debugcapi: [ - """\tfprintf(stderr,\"debug-capi:Fortran subroutine `f2pywrap#name_lower#(#callfortran#)\'\\n\");"""]}, + """ fprintf(stderr,\"debug-capi:Fortran subroutine `f2pywrap#name_lower#(#callfortran#)\'\\n\");"""]}, {hasexternals: """\ -\tif (#setjmpbuf#) { -\t\tf2py_success = 0; -\t} else {"""}, - {isthreadsafe: '\tPy_BEGIN_ALLOW_THREADS'}, + if (#setjmpbuf#) { + f2py_success = 0; + } else {"""}, + {isthreadsafe: ' Py_BEGIN_ALLOW_THREADS'}, {l_not(l_or(hascallstatement, isdummyroutine)) - : '\t(*f2py_func)(#callfortran#);'}, + : ' (*f2py_func)(#callfortran#);'}, {hascallstatement: - '\t#callstatement#;\n\t/*(*f2py_func)(#callfortran#);*/'}, - {isthreadsafe: '\tPy_END_ALLOW_THREADS'}, - {hasexternals: '\t}'} + ' #callstatement#;\n /*(*f2py_func)(#callfortran#);*/'}, + {isthreadsafe: ' Py_END_ALLOW_THREADS'}, + {hasexternals: ' }'} ], '_check': issubroutine_wrap, }, { # Function @@ -507,13 +505,13 @@ {hasresultnote: '--- #resultnote#'}], 'callfortranroutine': [{l_and(debugcapi, isstringfunction): """\ #ifdef USESCOMPAQFORTRAN -\tfprintf(stderr,\"debug-capi:Fortran function #ctype# #fortranname#(#callcompaqfortran#)\\n\"); + fprintf(stderr,\"debug-capi:Fortran function #ctype# #fortranname#(#callcompaqfortran#)\\n\"); #else -\tfprintf(stderr,\"debug-capi:Fortran function #ctype# #fortranname#(#callfortran#)\\n\"); + fprintf(stderr,\"debug-capi:Fortran function #ctype# #fortranname#(#callfortran#)\\n\"); #endif """}, {l_and(debugcapi, l_not(isstringfunction)): """\ -\tfprintf(stderr,\"debug-capi:Fortran function #ctype# #fortranname#(#callfortran#)\\n\"); + fprintf(stderr,\"debug-capi:Fortran function #ctype# #fortranname#(#callfortran#)\\n\"); """} ], '_check': l_and(isfunction, l_not(isfunction_wrap)) @@ -522,32 +520,32 @@ l_and(l_not(ismoduleroutine), isintent_c, l_not(isdummyroutine)): 'extern #ctype# #fortranname#(#callprotoargument#);', isdummyroutine: '' }, - 'routine_def': {l_and(l_not(l_or(ismoduleroutine, isintent_c)), l_not(isdummyroutine)): '\t{\"#name#\",-1,{{-1}},0,(char *)#F_FUNC#(#fortranname#,#FORTRANNAME#),(f2py_init_func)#apiname#,doc_#apiname#},', - l_and(l_not(ismoduleroutine), isintent_c, l_not(isdummyroutine)): '\t{\"#name#\",-1,{{-1}},0,(char *)#fortranname#,(f2py_init_func)#apiname#,doc_#apiname#},', - isdummyroutine: '\t{\"#name#\",-1,{{-1}},0,NULL,(f2py_init_func)#apiname#,doc_#apiname#},', + 'routine_def': {l_and(l_not(l_or(ismoduleroutine, isintent_c)), l_not(isdummyroutine)): ' {\"#name#\",-1,{{-1}},0,(char *)#F_FUNC#(#fortranname#,#FORTRANNAME#),(f2py_init_func)#apiname#,doc_#apiname#},', + l_and(l_not(ismoduleroutine), isintent_c, l_not(isdummyroutine)): ' {\"#name#\",-1,{{-1}},0,(char *)#fortranname#,(f2py_init_func)#apiname#,doc_#apiname#},', + isdummyroutine: ' {\"#name#\",-1,{{-1}},0,NULL,(f2py_init_func)#apiname#,doc_#apiname#},', }, - 'decl': [{iscomplexfunction_warn: '\t#ctype# #name#_return_value={0,0};', - l_not(iscomplexfunction): '\t#ctype# #name#_return_value=0;'}, + 'decl': [{iscomplexfunction_warn: ' #ctype# #name#_return_value={0,0};', + l_not(iscomplexfunction): ' #ctype# #name#_return_value=0;'}, {iscomplexfunction: - '\tPyObject *#name#_return_value_capi = Py_None;'} + ' PyObject *#name#_return_value_capi = Py_None;'} ], 'callfortranroutine': [ {hasexternals: """\ -\tif (#setjmpbuf#) { -\t\tf2py_success = 0; -\t} else {"""}, - {isthreadsafe: '\tPy_BEGIN_ALLOW_THREADS'}, - {hascallstatement: '''\t#callstatement#; -/*\t#name#_return_value = (*f2py_func)(#callfortran#);*/ + if (#setjmpbuf#) { + f2py_success = 0; + } else {"""}, + {isthreadsafe: ' Py_BEGIN_ALLOW_THREADS'}, + {hascallstatement: ''' #callstatement#; +/* #name#_return_value = (*f2py_func)(#callfortran#);*/ '''}, {l_not(l_or(hascallstatement, isdummyroutine)) - : '\t#name#_return_value = (*f2py_func)(#callfortran#);'}, - {isthreadsafe: '\tPy_END_ALLOW_THREADS'}, - {hasexternals: '\t}'}, + : ' #name#_return_value = (*f2py_func)(#callfortran#);'}, + {isthreadsafe: ' Py_END_ALLOW_THREADS'}, + {hasexternals: ' }'}, {l_and(debugcapi, iscomplexfunction) - : '\tfprintf(stderr,"#routdebugshowvalue#\\n",#name#_return_value.r,#name#_return_value.i);'}, - {l_and(debugcapi, l_not(iscomplexfunction)): '\tfprintf(stderr,"#routdebugshowvalue#\\n",#name#_return_value);'}], - 'pyobjfrom': {iscomplexfunction: '\t#name#_return_value_capi = pyobj_from_#ctype#1(#name#_return_value);'}, + : ' fprintf(stderr,"#routdebugshowvalue#\\n",#name#_return_value.r,#name#_return_value.i);'}, + {l_and(debugcapi, l_not(iscomplexfunction)): ' fprintf(stderr,"#routdebugshowvalue#\\n",#name#_return_value);'}], + 'pyobjfrom': {iscomplexfunction: ' #name#_return_value_capi = pyobj_from_#ctype#1(#name#_return_value);'}, 'need': [{l_not(isdummyroutine): 'F_FUNC'}, {iscomplexfunction: 'pyobj_from_#ctype#1'}, {islong_longfunction: 'long_long'}, @@ -559,49 +557,50 @@ }, { # String function # in use for --no-wrap 'declfortranroutine': 'extern void #F_FUNC#(#fortranname#,#FORTRANNAME#)(#callprotoargument#);', 'routine_def': {l_not(l_or(ismoduleroutine, isintent_c)): - '\t{\"#name#\",-1,{{-1}},0,(char *)#F_FUNC#(#fortranname#,#FORTRANNAME#),(f2py_init_func)#apiname#,doc_#apiname#},', + ' {\"#name#\",-1,{{-1}},0,(char *)#F_FUNC#(#fortranname#,#FORTRANNAME#),(f2py_init_func)#apiname#,doc_#apiname#},', l_and(l_not(ismoduleroutine), isintent_c): - '\t{\"#name#\",-1,{{-1}},0,(char *)#fortranname#,(f2py_init_func)#apiname#,doc_#apiname#},' + ' {\"#name#\",-1,{{-1}},0,(char *)#fortranname#,(f2py_init_func)#apiname#,doc_#apiname#},' }, - 'decl': ['\t#ctype# #name#_return_value = NULL;', - '\tint #name#_return_value_len = 0;'], + 'decl': [' #ctype# #name#_return_value = NULL;', + ' int #name#_return_value_len = 0;'], 'callfortran':'#name#_return_value,#name#_return_value_len,', - 'callfortranroutine':['\t#name#_return_value_len = #rlength#;', - '\tif ((#name#_return_value = (string)malloc(sizeof(char)*(#name#_return_value_len+1))) == NULL) {', - '\t\tPyErr_SetString(PyExc_MemoryError, \"out of memory\");', - '\t\tf2py_success = 0;', - '\t} else {', - "\t\t(#name#_return_value)[#name#_return_value_len] = '\\0';", - '\t}', - '\tif (f2py_success) {', + 'callfortranroutine':[' #name#_return_value_len = #rlength#;', + ' if ((#name#_return_value = (string)malloc(' + + '#name#_return_value_len+1) == NULL) {', + ' PyErr_SetString(PyExc_MemoryError, \"out of memory\");', + ' f2py_success = 0;', + ' } else {', + " (#name#_return_value)[#name#_return_value_len] = '\\0';", + ' }', + ' if (f2py_success) {', {hasexternals: """\ -\t\tif (#setjmpbuf#) { -\t\t\tf2py_success = 0; -\t\t} else {"""}, - {isthreadsafe: '\t\tPy_BEGIN_ALLOW_THREADS'}, + if (#setjmpbuf#) { + f2py_success = 0; + } else {"""}, + {isthreadsafe: ' Py_BEGIN_ALLOW_THREADS'}, """\ #ifdef USESCOMPAQFORTRAN -\t\t(*f2py_func)(#callcompaqfortran#); + (*f2py_func)(#callcompaqfortran#); #else -\t\t(*f2py_func)(#callfortran#); + (*f2py_func)(#callfortran#); #endif """, - {isthreadsafe: '\t\tPy_END_ALLOW_THREADS'}, - {hasexternals: '\t\t}'}, + {isthreadsafe: ' Py_END_ALLOW_THREADS'}, + {hasexternals: ' }'}, {debugcapi: - '\t\tfprintf(stderr,"#routdebugshowvalue#\\n",#name#_return_value_len,#name#_return_value);'}, - '\t} /* if (f2py_success) after (string)malloc */', + ' fprintf(stderr,"#routdebugshowvalue#\\n",#name#_return_value_len,#name#_return_value);'}, + ' } /* if (f2py_success) after (string)malloc */', ], 'returnformat': '#rformat#', 'return': ',#name#_return_value', - 'freemem': '\tSTRINGFREE(#name#_return_value);', + 'freemem': ' STRINGFREE(#name#_return_value);', 'need': ['F_FUNC', '#ctype#', 'STRINGFREE'], '_check':l_and(isstringfunction, l_not(isfunction_wrap)) # ???obsolete }, { # Debugging - 'routdebugenter': '\tfprintf(stderr,"debug-capi:Python C/API function #modulename#.#name#(#docsignature#)\\n");', - 'routdebugleave': '\tfprintf(stderr,"debug-capi:Python C/API function #modulename#.#name#: successful.\\n");', - 'routdebugfailure': '\tfprintf(stderr,"debug-capi:Python C/API function #modulename#.#name#: failure.\\n");', + 'routdebugenter': ' fprintf(stderr,"debug-capi:Python C/API function #modulename#.#name#(#docsignature#)\\n");', + 'routdebugleave': ' fprintf(stderr,"debug-capi:Python C/API function #modulename#.#name#: successful.\\n");', + 'routdebugfailure': ' fprintf(stderr,"debug-capi:Python C/API function #modulename#.#name#: failure.\\n");', '_check': debugcapi } ] @@ -626,16 +625,16 @@ 'separatorsfor': sepdict }, { # Common - 'frompyobj': ['\t/* Processing auxiliary variable #varname# */', - {debugcapi: '\tfprintf(stderr,"#vardebuginfo#\\n");'}, ], - 'cleanupfrompyobj': '\t/* End of cleaning variable #varname# */', + 'frompyobj': [' /* Processing auxiliary variable #varname# */', + {debugcapi: ' fprintf(stderr,"#vardebuginfo#\\n");'}, ], + 'cleanupfrompyobj': ' /* End of cleaning variable #varname# */', 'need': typedef_need_dict, }, # Scalars (not complex) { # Common - 'decl': '\t#ctype# #varname# = 0;', + 'decl': ' #ctype# #varname# = 0;', 'need': {hasinitvalue: 'math.h'}, - 'frompyobj': {hasinitvalue: '\t#varname# = #init#;'}, + 'frompyobj': {hasinitvalue: ' #varname# = #init#;'}, '_check': l_and(isscalar, l_not(iscomplex)), }, { @@ -647,23 +646,23 @@ }, # Complex scalars { # Common - 'decl': '\t#ctype# #varname#;', - 'frompyobj': {hasinitvalue: '\t#varname#.r = #init.r#, #varname#.i = #init.i#;'}, + 'decl': ' #ctype# #varname#;', + 'frompyobj': {hasinitvalue: ' #varname#.r = #init.r#, #varname#.i = #init.i#;'}, '_check': iscomplex }, # String { # Common - 'decl': ['\t#ctype# #varname# = NULL;', - '\tint slen(#varname#);', + 'decl': [' #ctype# #varname# = NULL;', + ' int slen(#varname#);', ], 'need':['len..'], '_check':isstring }, # Array { # Common - 'decl': ['\t#ctype# *#varname# = NULL;', - '\tnpy_intp #varname#_Dims[#rank#] = {#rank*[-1]#};', - '\tconst int #varname#_Rank = #rank#;', + 'decl': [' #ctype# *#varname# = NULL;', + ' npy_intp #varname#_Dims[#rank#] = {#rank*[-1]#};', + ' const int #varname#_Rank = #rank#;', ], 'need':['len..', {hasinitvalue: 'forcomb'}, {hasinitvalue: 'CFUNCSMESS'}], '_check': isarray @@ -712,9 +711,9 @@ 'separatorsfor': sepdict }, { # Common - 'frompyobj': ['\t/* Processing variable #varname# */', - {debugcapi: '\tfprintf(stderr,"#vardebuginfo#\\n");'}, ], - 'cleanupfrompyobj': '\t/* End of cleaning variable #varname# */', + 'frompyobj': [' /* Processing variable #varname# */', + {debugcapi: ' fprintf(stderr,"#vardebuginfo#\\n");'}, ], + 'cleanupfrompyobj': ' /* End of cleaning variable #varname# */', '_depend': '', 'need': typedef_need_dict, }, @@ -758,43 +757,45 @@ 'docstrcbs': '#cbdocstr#', 'latexdocstrcbs': '\\item[] #cblatexdocstr#', 'latexdocstropt': {isintent_nothide: '\\item[]{{}\\verb@#varname#_extra_args := () input tuple@{}} --- Extra arguments for call-back function {{}\\verb@#varname#@{}}.'}, - 'decl': ['\tPyObject *#varname#_capi = Py_None;', - '\tPyTupleObject *#varname#_xa_capi = NULL;', - '\tPyTupleObject *#varname#_args_capi = NULL;', - '\tint #varname#_nofargs_capi = 0;', + 'decl': [' #cbname#_t #varname#_cb = { Py_None, NULL, 0 };', + ' #cbname#_t *#varname#_cb_ptr = &#varname#_cb;', + ' PyTupleObject *#varname#_xa_capi = NULL;', {l_not(isintent_callback): - '\t#cbname#_typedef #varname#_cptr;'} + ' #cbname#_typedef #varname#_cptr;'} ], 'kwlistxa': {isintent_nothide: '"#varname#_extra_args",'}, 'argformat': {isrequired: 'O'}, 'keyformat': {isoptional: 'O'}, 'xaformat': {isintent_nothide: 'O!'}, - 'args_capi': {isrequired: ',&#varname#_capi'}, - 'keys_capi': {isoptional: ',&#varname#_capi'}, + 'args_capi': {isrequired: ',&#varname#_cb.capi'}, + 'keys_capi': {isoptional: ',&#varname#_cb.capi'}, 'keys_xa': ',&PyTuple_Type,&#varname#_xa_capi', - 'setjmpbuf': '(setjmp(#cbname#_jmpbuf))', + 'setjmpbuf': '(setjmp(#varname#_cb.jmpbuf))', 'callfortran': {l_not(isintent_callback): '#varname#_cptr,'}, 'need': ['#cbname#', 'setjmp.h'], '_check':isexternal }, { 'frompyobj': [{l_not(isintent_callback): """\ -if(F2PyCapsule_Check(#varname#_capi)) { - #varname#_cptr = F2PyCapsule_AsVoidPtr(#varname#_capi); +if(F2PyCapsule_Check(#varname#_cb.capi)) { + #varname#_cptr = F2PyCapsule_AsVoidPtr(#varname#_cb.capi); } else { #varname#_cptr = #cbname#; } """}, {isintent_callback: """\ -if (#varname#_capi==Py_None) { - #varname#_capi = PyObject_GetAttrString(#modulename#_module,\"#varname#\"); - if (#varname#_capi) { +if (#varname#_cb.capi==Py_None) { + #varname#_cb.capi = PyObject_GetAttrString(#modulename#_module,\"#varname#\"); + if (#varname#_cb.capi) { if (#varname#_xa_capi==NULL) { if (PyObject_HasAttrString(#modulename#_module,\"#varname#_extra_args\")) { PyObject* capi_tmp = PyObject_GetAttrString(#modulename#_module,\"#varname#_extra_args\"); - if (capi_tmp) + if (capi_tmp) { #varname#_xa_capi = (PyTupleObject *)PySequence_Tuple(capi_tmp); - else + Py_DECREF(capi_tmp); + } + else { #varname#_xa_capi = (PyTupleObject *)Py_BuildValue(\"()\"); + } if (#varname#_xa_capi==NULL) { PyErr_SetString(#modulename#_error,\"Failed to convert #modulename#.#varname#_extra_args to tuple.\\n\"); return NULL; @@ -802,43 +803,37 @@ } } } - if (#varname#_capi==NULL) { + if (#varname#_cb.capi==NULL) { PyErr_SetString(#modulename#_error,\"Callback #varname# not defined (as an argument or module #modulename# attribute).\\n\"); return NULL; } } """}, """\ -\t#varname#_nofargs_capi = #cbname#_nofargs; -\tif (create_cb_arglist(#varname#_capi,#varname#_xa_capi,#maxnofargs#,#nofoptargs#,&#cbname#_nofargs,&#varname#_args_capi,\"failed in processing argument list for call-back #varname#.\")) { -\t\tjmp_buf #varname#_jmpbuf;""", + if (create_cb_arglist(#varname#_cb.capi,#varname#_xa_capi,#maxnofargs#,#nofoptargs#,&#varname#_cb.nofargs,&#varname#_cb.args_capi,\"failed in processing argument list for call-back #varname#.\")) { +""", {debugcapi: ["""\ -\t\tfprintf(stderr,\"debug-capi:Assuming %d arguments; at most #maxnofargs#(-#nofoptargs#) is expected.\\n\",#cbname#_nofargs); -\t\tCFUNCSMESSPY(\"for #varname#=\",#cbname#_capi);""", - {l_not(isintent_callback): """\t\tfprintf(stderr,\"#vardebugshowvalue# (call-back in C).\\n\",#cbname#);"""}]}, + fprintf(stderr,\"debug-capi:Assuming %d arguments; at most #maxnofargs#(-#nofoptargs#) is expected.\\n\",#varname#_cb.nofargs); + CFUNCSMESSPY(\"for #varname#=\",#varname#_cb.capi);""", + {l_not(isintent_callback): """ fprintf(stderr,\"#vardebugshowvalue# (call-back in C).\\n\",#cbname#);"""}]}, """\ -\t\tCFUNCSMESS(\"Saving jmpbuf for `#varname#`.\\n\"); -\t\tSWAP(#varname#_capi,#cbname#_capi,PyObject); -\t\tSWAP(#varname#_args_capi,#cbname#_args_capi,PyTupleObject); -\t\tmemcpy(&#varname#_jmpbuf,&#cbname#_jmpbuf,sizeof(jmp_buf));""", + CFUNCSMESS(\"Saving callback variables for `#varname#`.\\n\"); + #varname#_cb_ptr = swap_active_#cbname#(#varname#_cb_ptr);""", ], 'cleanupfrompyobj': """\ -\t\tCFUNCSMESS(\"Restoring jmpbuf for `#varname#`.\\n\"); -\t\t#cbname#_capi = #varname#_capi; -\t\tPy_DECREF(#cbname#_args_capi); -\t\t#cbname#_args_capi = #varname#_args_capi; -\t\t#cbname#_nofargs = #varname#_nofargs_capi; -\t\tmemcpy(&#cbname#_jmpbuf,&#varname#_jmpbuf,sizeof(jmp_buf)); -\t}""", + CFUNCSMESS(\"Restoring callback variables for `#varname#`.\\n\"); + #varname#_cb_ptr = swap_active_#cbname#(#varname#_cb_ptr); + Py_DECREF(#varname#_cb.args_capi); + }""", 'need': ['SWAP', 'create_cb_arglist'], '_check':isexternal, '_depend':'' }, # Scalars (not complex) { # Common - 'decl': '\t#ctype# #varname# = 0;', - 'pyobjfrom': {debugcapi: '\tfprintf(stderr,"#vardebugshowvalue#\\n",#varname#);'}, + 'decl': ' #ctype# #varname# = 0;', + 'pyobjfrom': {debugcapi: ' fprintf(stderr,"#vardebugshowvalue#\\n",#varname#);'}, 'callfortran': {isintent_c: '#varname#,', l_not(isintent_c): '&#varname#,'}, 'return': {isintent_out: ',#varname#'}, '_check': l_and(isscalar, l_not(iscomplex)) @@ -846,15 +841,15 @@ 'need': {hasinitvalue: 'math.h'}, '_check': l_and(isscalar, l_not(iscomplex)), }, { # Not hidden - 'decl': '\tPyObject *#varname#_capi = Py_None;', + 'decl': ' PyObject *#varname#_capi = Py_None;', 'argformat': {isrequired: 'O'}, 'keyformat': {isoptional: 'O'}, 'args_capi': {isrequired: ',&#varname#_capi'}, 'keys_capi': {isoptional: ',&#varname#_capi'}, 'pyobjfrom': {isintent_inout: """\ -\tf2py_success = try_pyarr_from_#ctype#(#varname#_capi,&#varname#); -\tif (f2py_success) {"""}, - 'closepyobjfrom': {isintent_inout: "\t} /*if (f2py_success) of #varname# pyobjfrom*/"}, + f2py_success = try_pyarr_from_#ctype#(#varname#_capi,&#varname#); + if (f2py_success) {"""}, + 'closepyobjfrom': {isintent_inout: " } /*if (f2py_success) of #varname# pyobjfrom*/"}, 'need': {isintent_inout: 'try_pyarr_from_#ctype#'}, '_check': l_and(isscalar, l_not(iscomplex), isintent_nothide) }, { @@ -874,97 +869,113 @@ # ... # from_pyobj(varname) # - {hasinitvalue: '\tif (#varname#_capi == Py_None) #varname# = #init#; else', + {hasinitvalue: ' if (#varname#_capi == Py_None) #varname# = #init#; else', '_depend': ''}, - {l_and(isoptional, l_not(hasinitvalue)): '\tif (#varname#_capi != Py_None)', + {l_and(isoptional, l_not(hasinitvalue)): ' if (#varname#_capi != Py_None)', '_depend': ''}, {l_not(islogical): '''\ -\t\tf2py_success = #ctype#_from_pyobj(&#varname#,#varname#_capi,"#pyname#() #nth# (#varname#) can\'t be converted to #ctype#"); -\tif (f2py_success) {'''}, + f2py_success = #ctype#_from_pyobj(&#varname#,#varname#_capi,"#pyname#() #nth# (#varname#) can\'t be converted to #ctype#"); + if (f2py_success) {'''}, {islogical: '''\ -\t\t#varname# = (#ctype#)PyObject_IsTrue(#varname#_capi); -\t\tf2py_success = 1; -\tif (f2py_success) {'''}, + #varname# = (#ctype#)PyObject_IsTrue(#varname#_capi); + f2py_success = 1; + if (f2py_success) {'''}, ], - 'cleanupfrompyobj': '\t} /*if (f2py_success) of #varname#*/', + 'cleanupfrompyobj': ' } /*if (f2py_success) of #varname#*/', 'need': {l_not(islogical): '#ctype#_from_pyobj'}, '_check': l_and(isscalar, l_not(iscomplex), isintent_nothide), '_depend': '' }, { # Hidden - 'frompyobj': {hasinitvalue: '\t#varname# = #init#;'}, + 'frompyobj': {hasinitvalue: ' #varname# = #init#;'}, 'need': typedef_need_dict, '_check': l_and(isscalar, l_not(iscomplex), isintent_hide), '_depend': '' }, { # Common - 'frompyobj': {debugcapi: '\tfprintf(stderr,"#vardebugshowvalue#\\n",#varname#);'}, + 'frompyobj': {debugcapi: ' fprintf(stderr,"#vardebugshowvalue#\\n",#varname#);'}, '_check': l_and(isscalar, l_not(iscomplex)), '_depend': '' }, # Complex scalars { # Common - 'decl': '\t#ctype# #varname#;', + 'decl': ' #ctype# #varname#;', 'callfortran': {isintent_c: '#varname#,', l_not(isintent_c): '&#varname#,'}, - 'pyobjfrom': {debugcapi: '\tfprintf(stderr,"#vardebugshowvalue#\\n",#varname#.r,#varname#.i);'}, + 'pyobjfrom': {debugcapi: ' fprintf(stderr,"#vardebugshowvalue#\\n",#varname#.r,#varname#.i);'}, 'return': {isintent_out: ',#varname#_capi'}, '_check': iscomplex }, { # Not hidden - 'decl': '\tPyObject *#varname#_capi = Py_None;', + 'decl': ' PyObject *#varname#_capi = Py_None;', 'argformat': {isrequired: 'O'}, 'keyformat': {isoptional: 'O'}, 'args_capi': {isrequired: ',&#varname#_capi'}, 'keys_capi': {isoptional: ',&#varname#_capi'}, 'need': {isintent_inout: 'try_pyarr_from_#ctype#'}, 'pyobjfrom': {isintent_inout: """\ -\t\tf2py_success = try_pyarr_from_#ctype#(#varname#_capi,&#varname#); -\t\tif (f2py_success) {"""}, - 'closepyobjfrom': {isintent_inout: "\t\t} /*if (f2py_success) of #varname# pyobjfrom*/"}, + f2py_success = try_pyarr_from_#ctype#(#varname#_capi,&#varname#); + if (f2py_success) {"""}, + 'closepyobjfrom': {isintent_inout: " } /*if (f2py_success) of #varname# pyobjfrom*/"}, '_check': l_and(iscomplex, isintent_nothide) }, { - 'frompyobj': [{hasinitvalue: '\tif (#varname#_capi==Py_None) {#varname#.r = #init.r#, #varname#.i = #init.i#;} else'}, + 'frompyobj': [{hasinitvalue: ' if (#varname#_capi==Py_None) {#varname#.r = #init.r#, #varname#.i = #init.i#;} else'}, {l_and(isoptional, l_not(hasinitvalue)) - : '\tif (#varname#_capi != Py_None)'}, - '\t\tf2py_success = #ctype#_from_pyobj(&#varname#,#varname#_capi,"#pyname#() #nth# (#varname#) can\'t be converted to #ctype#");' - '\n\tif (f2py_success) {'], - 'cleanupfrompyobj': '\t} /*if (f2py_success) of #varname# frompyobj*/', + : ' if (#varname#_capi != Py_None)'}, + ' f2py_success = #ctype#_from_pyobj(&#varname#,#varname#_capi,"#pyname#() #nth# (#varname#) can\'t be converted to #ctype#");' + '\n if (f2py_success) {'], + 'cleanupfrompyobj': ' } /*if (f2py_success) of #varname# frompyobj*/', 'need': ['#ctype#_from_pyobj'], '_check': l_and(iscomplex, isintent_nothide), '_depend': '' }, { # Hidden - 'decl': {isintent_out: '\tPyObject *#varname#_capi = Py_None;'}, + 'decl': {isintent_out: ' PyObject *#varname#_capi = Py_None;'}, '_check': l_and(iscomplex, isintent_hide) }, { - 'frompyobj': {hasinitvalue: '\t#varname#.r = #init.r#, #varname#.i = #init.i#;'}, + 'frompyobj': {hasinitvalue: ' #varname#.r = #init.r#, #varname#.i = #init.i#;'}, '_check': l_and(iscomplex, isintent_hide), '_depend': '' }, { # Common - 'pyobjfrom': {isintent_out: '\t#varname#_capi = pyobj_from_#ctype#1(#varname#);'}, + 'pyobjfrom': {isintent_out: ' #varname#_capi = pyobj_from_#ctype#1(#varname#);'}, 'need': ['pyobj_from_#ctype#1'], '_check': iscomplex }, { - 'frompyobj': {debugcapi: '\tfprintf(stderr,"#vardebugshowvalue#\\n",#varname#.r,#varname#.i);'}, + 'frompyobj': {debugcapi: ' fprintf(stderr,"#vardebugshowvalue#\\n",#varname#.r,#varname#.i);'}, '_check': iscomplex, '_depend': '' }, # String { # Common - 'decl': ['\t#ctype# #varname# = NULL;', - '\tint slen(#varname#);', - '\tPyObject *#varname#_capi = Py_None;'], + 'decl': [' #ctype# #varname# = NULL;', + ' int slen(#varname#);', + ' PyObject *#varname#_capi = Py_None;'], 'callfortran':'#varname#,', 'callfortranappend':'slen(#varname#),', - 'pyobjfrom':{debugcapi: '\tfprintf(stderr,"#vardebugshowvalue#\\n",slen(#varname#),#varname#);'}, + 'pyobjfrom':[ + {debugcapi: + ' fprintf(stderr,' + '"#vardebugshowvalue#\\n",slen(#varname#),#varname#);'}, + # The trailing null value for Fortran is blank. + {l_and(isintent_out, l_not(isintent_c)): + " STRINGPADN(#varname#, slen(#varname#), ' ', '\\0');"}, + ], 'return': {isintent_out: ',#varname#'}, - 'need': ['len..'], # 'STRINGFREE'], + 'need': ['len..', + {l_and(isintent_out, l_not(isintent_c)): 'STRINGPADN'}], '_check':isstring }, { # Common - 'frompyobj': """\ -\tslen(#varname#) = #length#; -\tf2py_success = #ctype#_from_pyobj(&#varname#,&slen(#varname#),#init#,#varname#_capi,\"#ctype#_from_pyobj failed in converting #nth# `#varname#\' of #pyname# to C #ctype#\"); -\tif (f2py_success) {""", + 'frompyobj': [ + """\ + slen(#varname#) = #length#; + f2py_success = #ctype#_from_pyobj(&#varname#,&slen(#varname#),#init#,""" +"""#varname#_capi,\"#ctype#_from_pyobj failed in converting #nth#""" +"""`#varname#\' of #pyname# to C #ctype#\"); + if (f2py_success) {""", + # The trailing null value for Fortran is blank. + {l_not(isintent_c): + " STRINGPADN(#varname#, slen(#varname#), '\\0', ' ');"}, + ], 'cleanupfrompyobj': """\ -\t\tSTRINGFREE(#varname#); -\t} /*if (f2py_success) of #varname#*/""", - 'need': ['#ctype#_from_pyobj', 'len..', 'STRINGFREE'], + STRINGFREE(#varname#); + } /*if (f2py_success) of #varname#*/""", + 'need': ['#ctype#_from_pyobj', 'len..', 'STRINGFREE', + {l_not(isintent_c): 'STRINGPADN'}], '_check':isstring, '_depend':'' }, { # Not hidden @@ -972,33 +983,38 @@ 'keyformat': {isoptional: 'O'}, 'args_capi': {isrequired: ',&#varname#_capi'}, 'keys_capi': {isoptional: ',&#varname#_capi'}, - 'pyobjfrom': {isintent_inout: '''\ -\tf2py_success = try_pyarr_from_#ctype#(#varname#_capi,#varname#); -\tif (f2py_success) {'''}, - 'closepyobjfrom': {isintent_inout: '\t} /*if (f2py_success) of #varname# pyobjfrom*/'}, - 'need': {isintent_inout: 'try_pyarr_from_#ctype#'}, + 'pyobjfrom': [ + {l_and(isintent_inout, l_not(isintent_c)): + " STRINGPADN(#varname#, slen(#varname#), ' ', '\\0');"}, + {isintent_inout: '''\ + f2py_success = try_pyarr_from_#ctype#(#varname#_capi, #varname#, + slen(#varname#)); + if (f2py_success) {'''}], + 'closepyobjfrom': {isintent_inout: ' } /*if (f2py_success) of #varname# pyobjfrom*/'}, + 'need': {isintent_inout: 'try_pyarr_from_#ctype#', + l_and(isintent_inout, l_not(isintent_c)): 'STRINGPADN'}, '_check': l_and(isstring, isintent_nothide) }, { # Hidden '_check': l_and(isstring, isintent_hide) }, { - 'frompyobj': {debugcapi: '\tfprintf(stderr,"#vardebugshowvalue#\\n",slen(#varname#),#varname#);'}, + 'frompyobj': {debugcapi: ' fprintf(stderr,"#vardebugshowvalue#\\n",slen(#varname#),#varname#);'}, '_check': isstring, '_depend': '' }, # Array { # Common - 'decl': ['\t#ctype# *#varname# = NULL;', - '\tnpy_intp #varname#_Dims[#rank#] = {#rank*[-1]#};', - '\tconst int #varname#_Rank = #rank#;', - '\tPyArrayObject *capi_#varname#_tmp = NULL;', - '\tint capi_#varname#_intent = 0;', + 'decl': [' #ctype# *#varname# = NULL;', + ' npy_intp #varname#_Dims[#rank#] = {#rank*[-1]#};', + ' const int #varname#_Rank = #rank#;', + ' PyArrayObject *capi_#varname#_tmp = NULL;', + ' int capi_#varname#_intent = 0;', ], 'callfortran':'#varname#,', 'return':{isintent_out: ',capi_#varname#_tmp'}, 'need': 'len..', '_check': isarray }, { # intent(overwrite) array - 'decl': '\tint capi_overwrite_#varname# = 1;', + 'decl': ' int capi_overwrite_#varname# = 1;', 'kwlistxa': '"overwrite_#varname#",', 'xaformat': 'i', 'keys_xa': ',&capi_overwrite_#varname#', @@ -1007,12 +1023,12 @@ 'docstropt': 'overwrite_#varname# : input int, optional\\n Default: 1', '_check': l_and(isarray, isintent_overwrite), }, { - 'frompyobj': '\tcapi_#varname#_intent |= (capi_overwrite_#varname#?0:F2PY_INTENT_COPY);', + 'frompyobj': ' capi_#varname#_intent |= (capi_overwrite_#varname#?0:F2PY_INTENT_COPY);', '_check': l_and(isarray, isintent_overwrite), '_depend': '', }, { # intent(copy) array - 'decl': '\tint capi_overwrite_#varname# = 0;', + 'decl': ' int capi_overwrite_#varname# = 0;', 'kwlistxa': '"overwrite_#varname#",', 'xaformat': 'i', 'keys_xa': ',&capi_overwrite_#varname#', @@ -1021,7 +1037,7 @@ 'docstropt': 'overwrite_#varname# : input int, optional\\n Default: 0', '_check': l_and(isarray, isintent_copy), }, { - 'frompyobj': '\tcapi_#varname#_intent |= (capi_overwrite_#varname#?0:F2PY_INTENT_COPY);', + 'frompyobj': ' capi_#varname#_intent |= (capi_overwrite_#varname#?0:F2PY_INTENT_COPY);', '_check': l_and(isarray, isintent_copy), '_depend': '', }, { @@ -1029,53 +1045,57 @@ '_check': isarray, '_depend': '' }, { # Not hidden - 'decl': '\tPyObject *#varname#_capi = Py_None;', + 'decl': ' PyObject *#varname#_capi = Py_None;', 'argformat': {isrequired: 'O'}, 'keyformat': {isoptional: 'O'}, 'args_capi': {isrequired: ',&#varname#_capi'}, 'keys_capi': {isoptional: ',&#varname#_capi'}, '_check': l_and(isarray, isintent_nothide) }, { - 'frompyobj': ['\t#setdims#;', - '\tcapi_#varname#_intent |= #intent#;', + 'frompyobj': [' #setdims#;', + ' capi_#varname#_intent |= #intent#;', {isintent_hide: - '\tcapi_#varname#_tmp = array_from_pyobj(#atype#,#varname#_Dims,#varname#_Rank,capi_#varname#_intent,Py_None);'}, + ' capi_#varname#_tmp = array_from_pyobj(#atype#,#varname#_Dims,#varname#_Rank,capi_#varname#_intent,Py_None);'}, {isintent_nothide: - '\tcapi_#varname#_tmp = array_from_pyobj(#atype#,#varname#_Dims,#varname#_Rank,capi_#varname#_intent,#varname#_capi);'}, + ' capi_#varname#_tmp = array_from_pyobj(#atype#,#varname#_Dims,#varname#_Rank,capi_#varname#_intent,#varname#_capi);'}, """\ -\tif (capi_#varname#_tmp == NULL) { -\t\tif (!PyErr_Occurred()) -\t\t\tPyErr_SetString(#modulename#_error,\"failed in converting #nth# `#varname#\' of #pyname# to C/Fortran array\" ); -\t} else { -\t\t#varname# = (#ctype# *)(PyArray_DATA(capi_#varname#_tmp)); + if (capi_#varname#_tmp == NULL) { + PyObject *exc, *val, *tb; + PyErr_Fetch(&exc, &val, &tb); + PyErr_SetString(exc ? exc : #modulename#_error,\"failed in converting #nth# `#varname#\' of #pyname# to C/Fortran array\" ); + npy_PyErr_ChainExceptionsCause(exc, val, tb); + } else { + #varname# = (#ctype# *)(PyArray_DATA(capi_#varname#_tmp)); """, {hasinitvalue: [ {isintent_nothide: - '\tif (#varname#_capi == Py_None) {'}, - {isintent_hide: '\t{'}, - {iscomplexarray: '\t\t#ctype# capi_c;'}, + ' if (#varname#_capi == Py_None) {'}, + {isintent_hide: ' {'}, + {iscomplexarray: ' #ctype# capi_c;'}, """\ -\t\tint *_i,capi_i=0; -\t\tCFUNCSMESS(\"#name#: Initializing #varname#=#init#\\n\"); -\t\tif (initforcomb(PyArray_DIMS(capi_#varname#_tmp),PyArray_NDIM(capi_#varname#_tmp),1)) { -\t\t\twhile ((_i = nextforcomb())) -\t\t\t\t#varname#[capi_i++] = #init#; /* fortran way */ -\t\t} else { -\t\t\tif (!PyErr_Occurred()) -\t\t\t\tPyErr_SetString(#modulename#_error,\"Initialization of #nth# #varname# failed (initforcomb).\"); -\t\t\tf2py_success = 0; -\t\t} -\t} -\tif (f2py_success) {"""]}, + int *_i,capi_i=0; + CFUNCSMESS(\"#name#: Initializing #varname#=#init#\\n\"); + if (initforcomb(PyArray_DIMS(capi_#varname#_tmp),PyArray_NDIM(capi_#varname#_tmp),1)) { + while ((_i = nextforcomb())) + #varname#[capi_i++] = #init#; /* fortran way */ + } else { + PyObject *exc, *val, *tb; + PyErr_Fetch(&exc, &val, &tb); + PyErr_SetString(exc ? exc : #modulename#_error,\"Initialization of #nth# #varname# failed (initforcomb).\"); + npy_PyErr_ChainExceptionsCause(exc, val, tb); + f2py_success = 0; + } + } + if (f2py_success) {"""]}, ], 'cleanupfrompyobj': [ # note that this list will be reversed - '\t} /*if (capi_#varname#_tmp == NULL) ... else of #varname#*/', + ' } /*if (capi_#varname#_tmp == NULL) ... else of #varname#*/', {l_not(l_or(isintent_out, isintent_hide)): """\ -\tif((PyObject *)capi_#varname#_tmp!=#varname#_capi) { -\t\tPy_XDECREF(capi_#varname#_tmp); }"""}, + if((PyObject *)capi_#varname#_tmp!=#varname#_capi) { + Py_XDECREF(capi_#varname#_tmp); }"""}, {l_and(isintent_hide, l_not(isintent_out)) - : """\t\tPy_XDECREF(capi_#varname#_tmp);"""}, - {hasinitvalue: '\t} /*if (f2py_success) of #varname# init*/'}, + : """ Py_XDECREF(capi_#varname#_tmp);"""}, + {hasinitvalue: ' } /*if (f2py_success) of #varname# init*/'}, ], '_check': isarray, '_depend': '' @@ -1123,30 +1143,30 @@ check_rules = [ { - 'frompyobj': {debugcapi: '\tfprintf(stderr,\"debug-capi:Checking `#check#\'\\n\");'}, + 'frompyobj': {debugcapi: ' fprintf(stderr,\"debug-capi:Checking `#check#\'\\n\");'}, 'need': 'len..' }, { - 'frompyobj': '\tCHECKSCALAR(#check#,\"#check#\",\"#nth# #varname#\",\"#varshowvalue#\",#varname#) {', - 'cleanupfrompyobj': '\t} /*CHECKSCALAR(#check#)*/', + 'frompyobj': ' CHECKSCALAR(#check#,\"#check#\",\"#nth# #varname#\",\"#varshowvalue#\",#varname#) {', + 'cleanupfrompyobj': ' } /*CHECKSCALAR(#check#)*/', 'need': 'CHECKSCALAR', '_check': l_and(isscalar, l_not(iscomplex)), '_break': '' }, { - 'frompyobj': '\tCHECKSTRING(#check#,\"#check#\",\"#nth# #varname#\",\"#varshowvalue#\",#varname#) {', - 'cleanupfrompyobj': '\t} /*CHECKSTRING(#check#)*/', + 'frompyobj': ' CHECKSTRING(#check#,\"#check#\",\"#nth# #varname#\",\"#varshowvalue#\",#varname#) {', + 'cleanupfrompyobj': ' } /*CHECKSTRING(#check#)*/', 'need': 'CHECKSTRING', '_check': isstring, '_break': '' }, { 'need': 'CHECKARRAY', - 'frompyobj': '\tCHECKARRAY(#check#,\"#check#\",\"#nth# #varname#\") {', - 'cleanupfrompyobj': '\t} /*CHECKARRAY(#check#)*/', + 'frompyobj': ' CHECKARRAY(#check#,\"#check#\",\"#nth# #varname#\") {', + 'cleanupfrompyobj': ' } /*CHECKARRAY(#check#)*/', '_check': isarray, '_break': '' }, { 'need': 'CHECKGENERIC', - 'frompyobj': '\tCHECKGENERIC(#check#,\"#check#\",\"#nth# #varname#\") {', - 'cleanupfrompyobj': '\t} /*CHECKGENERIC(#check#)*/', + 'frompyobj': ' CHECKGENERIC(#check#,\"#check#\",\"#nth# #varname#\") {', + 'cleanupfrompyobj': ' } /*CHECKGENERIC(#check#)*/', } ] @@ -1159,8 +1179,7 @@ def buildmodule(m, um): """ Return """ - global f2py_version, options - outmess('\tBuilding module "%s"...\n' % (m['name'])) + outmess(' Building module "%s"...\n' % (m['name'])) ret = {} mod_rules = defmod_rules[:] vrd = capi_maps.modsign2map(m) @@ -1170,7 +1189,7 @@ def buildmodule(m, um): for n in m['interfaced']: nb = None for bi in m['body']: - if not bi['block'] == 'interface': + if bi['block'] not in ['interface', 'abstract interface']: errmess('buildmodule: Expected interface block. Skipping.\n') continue for b in bi['body']: @@ -1191,9 +1210,12 @@ def buildmodule(m, um): nb1['args'] = a nb_list.append(nb1) for nb in nb_list: + # requiresf90wrapper must be called before buildapi as it + # rewrites assumed shape arrays as automatic arrays. + isf90 = requiresf90wrapper(nb) api, wrap = buildapi(nb) if wrap: - if ismoduleroutine(nb): + if isf90: funcwrappers2.append(wrap) else: funcwrappers.append(wrap) @@ -1257,83 +1279,84 @@ def buildmodule(m, um): fn = os.path.join(options['buildpath'], vrd['coutput']) ret['csrc'] = fn - f = open(fn, 'w') - f.write(ar['modulebody'].replace('\t', 2 * ' ')) - f.close() - outmess('\tWrote C/API module "%s" to file "%s"\n' % (m['name'], fn)) + with open(fn, 'w') as f: + f.write(ar['modulebody'].replace('\t', 2 * ' ')) + outmess(' Wrote C/API module "%s" to file "%s"\n' % (m['name'], fn)) if options['dorestdoc']: fn = os.path.join( options['buildpath'], vrd['modulename'] + 'module.rest') - f = open(fn, 'w') - f.write('.. -*- rest -*-\n') - f.write('\n'.join(ar['restdoc'])) - f.close() - outmess('\tReST Documentation is saved to file "%s/%smodule.rest"\n' % + with open(fn, 'w') as f: + f.write('.. -*- rest -*-\n') + f.write('\n'.join(ar['restdoc'])) + outmess(' ReST Documentation is saved to file "%s/%smodule.rest"\n' % (options['buildpath'], vrd['modulename'])) if options['dolatexdoc']: fn = os.path.join( options['buildpath'], vrd['modulename'] + 'module.tex') ret['ltx'] = fn - f = open(fn, 'w') - f.write( - '%% This file is auto-generated with f2py (version:%s)\n' % (f2py_version)) - if 'shortlatex' not in options: + with open(fn, 'w') as f: f.write( - '\\documentclass{article}\n\\usepackage{a4wide}\n\\begin{document}\n\\tableofcontents\n\n') - f.write('\n'.join(ar['latexdoc'])) - if 'shortlatex' not in options: - f.write('\\end{document}') - f.close() - outmess('\tDocumentation is saved to file "%s/%smodule.tex"\n' % + '%% This file is auto-generated with f2py (version:%s)\n' % (f2py_version)) + if 'shortlatex' not in options: + f.write( + '\\documentclass{article}\n\\usepackage{a4wide}\n\\begin{document}\n\\tableofcontents\n\n') + f.write('\n'.join(ar['latexdoc'])) + if 'shortlatex' not in options: + f.write('\\end{document}') + outmess(' Documentation is saved to file "%s/%smodule.tex"\n' % (options['buildpath'], vrd['modulename'])) if funcwrappers: wn = os.path.join(options['buildpath'], vrd['f2py_wrapper_output']) ret['fsrc'] = wn - f = open(wn, 'w') - f.write('C -*- fortran -*-\n') - f.write( - 'C This file is autogenerated with f2py (version:%s)\n' % (f2py_version)) - f.write( - 'C It contains Fortran 77 wrappers to fortran functions.\n') - lines = [] - for l in ('\n\n'.join(funcwrappers) + '\n').split('\n'): - if l and l[0] == ' ': - while len(l) >= 66: - lines.append(l[:66] + '\n &') - l = l[66:] - lines.append(l + '\n') - else: - lines.append(l + '\n') - lines = ''.join(lines).replace('\n &\n', '\n') - f.write(lines) - f.close() - outmess('\tFortran 77 wrappers are saved to "%s"\n' % (wn)) + with open(wn, 'w') as f: + f.write('C -*- fortran -*-\n') + f.write( + 'C This file is autogenerated with f2py (version:%s)\n' % (f2py_version)) + f.write( + 'C It contains Fortran 77 wrappers to fortran functions.\n') + lines = [] + for l in ('\n\n'.join(funcwrappers) + '\n').split('\n'): + if 0 <= l.find('!') < 66: + # don't split comment lines + lines.append(l + '\n') + elif l and l[0] == ' ': + while len(l) >= 66: + lines.append(l[:66] + '\n &') + l = l[66:] + lines.append(l + '\n') + else: + lines.append(l + '\n') + lines = ''.join(lines).replace('\n &\n', '\n') + f.write(lines) + outmess(' Fortran 77 wrappers are saved to "%s"\n' % (wn)) if funcwrappers2: wn = os.path.join( options['buildpath'], '%s-f2pywrappers2.f90' % (vrd['modulename'])) ret['fsrc'] = wn - f = open(wn, 'w') - f.write('! -*- f90 -*-\n') - f.write( - '! This file is autogenerated with f2py (version:%s)\n' % (f2py_version)) - f.write( - '! It contains Fortran 90 wrappers to fortran functions.\n') - lines = [] - for l in ('\n\n'.join(funcwrappers2) + '\n').split('\n'): - if len(l) > 72 and l[0] == ' ': - lines.append(l[:72] + '&\n &') - l = l[72:] - while len(l) > 66: - lines.append(l[:66] + '&\n &') - l = l[66:] - lines.append(l + '\n') - else: - lines.append(l + '\n') - lines = ''.join(lines).replace('\n &\n', '\n') - f.write(lines) - f.close() - outmess('\tFortran 90 wrappers are saved to "%s"\n' % (wn)) + with open(wn, 'w') as f: + f.write('! -*- f90 -*-\n') + f.write( + '! This file is autogenerated with f2py (version:%s)\n' % (f2py_version)) + f.write( + '! It contains Fortran 90 wrappers to fortran functions.\n') + lines = [] + for l in ('\n\n'.join(funcwrappers2) + '\n').split('\n'): + if 0 <= l.find('!') < 72: + # don't split comment lines + lines.append(l + '\n') + elif len(l) > 72 and l[0] == ' ': + lines.append(l[:72] + '&\n &') + l = l[72:] + while len(l) > 66: + lines.append(l[:66] + '&\n &') + l = l[66:] + lines.append(l + '\n') + else: + lines.append(l + '\n') + lines = ''.join(lines).replace('\n &\n', '\n') + f.write(lines) + outmess(' Fortran 90 wrappers are saved to "%s"\n' % (wn)) return ret ################## Build C/API function ############# @@ -1349,10 +1372,10 @@ def buildapi(rout): var = rout['vars'] if ismoduleroutine(rout): - outmess('\t\t\tConstructing wrapper function "%s.%s"...\n' % + outmess(' Constructing wrapper function "%s.%s"...\n' % (rout['modulename'], rout['name'])) else: - outmess('\t\tConstructing wrapper function "%s"...\n' % (rout['name'])) + outmess(' Constructing wrapper function "%s"...\n' % (rout['name'])) # Routine vrd = capi_maps.routsign2map(rout) rd = dictappend({}, vrd) @@ -1452,21 +1475,11 @@ def buildapi(rout): ['\\begin{description}'] + rd[k][1:] +\ ['\\end{description}'] - # Workaround for Python 2.6, 2.6.1 bug: http://bugs.python.org/issue4720 - if rd['keyformat'] or rd['xaformat']: - argformat = rd['argformat'] - if isinstance(argformat, list): - argformat.append('|') - else: - assert isinstance(argformat, str), repr( - (argformat, type(argformat))) - rd['argformat'] += '|' - ar = applyrules(routine_rules, rd) if ismoduleroutine(rout): - outmess('\t\t\t %s\n' % (ar['docshort'])) + outmess(' %s\n' % (ar['docshort'])) else: - outmess('\t\t %s\n' % (ar['docshort'])) + outmess(' %s\n' % (ar['docshort'])) return ar, wrap diff --git a/numpy/f2py/setup.py b/numpy/f2py/setup.py index 73cb3b8bfe4d..499609f96600 100644 --- a/numpy/f2py/setup.py +++ b/numpy/f2py/setup.py @@ -1,9 +1,9 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """ setup.py for installing F2PY Usage: - python setup.py install + pip install . Copyright 2001-2005 Pearu Peterson all rights reserved, Pearu Peterson <pearu@cens.ioc.ee> @@ -16,75 +16,29 @@ Pearu Peterson """ -from __future__ import division, print_function - -__version__ = "$Id: setup.py,v 1.32 2005/01/30 17:22:14 pearu Exp $" - -import os -import sys -from distutils.dep_util import newer -from numpy.distutils import log from numpy.distutils.core import setup from numpy.distutils.misc_util import Configuration -from __version__ import version - -def _get_f2py_shebang(): - """ Return shebang line for f2py script - - If we are building a binary distribution format, then the shebang line - should be ``#!python`` rather than ``#!`` followed by the contents of - ``sys.executable``. - """ - if set(('bdist_wheel', 'bdist_egg', 'bdist_wininst', - 'bdist_rpm')).intersection(sys.argv): - return '#!python' - return '#!' + sys.executable +from __version__ import version def configuration(parent_package='', top_path=None): config = Configuration('f2py', parent_package, top_path) - - config.add_data_dir('tests') - - config.add_data_files('src/fortranobject.c', - 'src/fortranobject.h', - ) - - config.make_svn_version_py() - - def generate_f2py_py(build_dir): - f2py_exe = 'f2py' + os.path.basename(sys.executable)[6:] - if f2py_exe[-4:] == '.exe': - f2py_exe = f2py_exe[:-4] + '.py' - if 'bdist_wininst' in sys.argv and f2py_exe[-3:] != '.py': - f2py_exe = f2py_exe + '.py' - target = os.path.join(build_dir, f2py_exe) - if newer(__file__, target): - log.info('Creating %s', target) - f = open(target, 'w') - f.write(_get_f2py_shebang() + '\n') - mainloc = os.path.join(os.path.dirname(__file__), "__main__.py") - with open(mainloc) as mf: - f.write(mf.read()) - f.close() - return target - - config.add_scripts(generate_f2py_py) - - log.info('F2PY Version %s', config.get_version()) - + config.add_subpackage('tests') + config.add_data_dir('tests/src') + config.add_data_files( + 'src/fortranobject.c', + 'src/fortranobject.h') + config.add_data_files('*.pyi') return config + if __name__ == "__main__": config = configuration(top_path='') - print('F2PY Version', version) config = config.todict() - config['download_url'] = "http://cens.ioc.ee/projects/f2py2e/2.x"\ - "/F2PY-2-latest.tar.gz" config['classifiers'] = [ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', @@ -112,6 +66,6 @@ def generate_f2py_py(build_dir): wrapping Fortran 77/90/95 subroutines, accessing common blocks from Python, and calling Python functions from Fortran (call-backs). Interfacing subroutines/data from Fortran 90/95 modules is supported.""", - url="http://cens.ioc.ee/projects/f2py2e/", + url="https://numpy.org/doc/stable/f2py/", keywords=['Fortran', 'f2py'], **config) diff --git a/numpy/f2py/src/fortranobject.c b/numpy/f2py/src/fortranobject.c index 78b06f0662e3..c963781704cf 100644 --- a/numpy/f2py/src/fortranobject.c +++ b/numpy/f2py/src/fortranobject.c @@ -19,7 +19,7 @@ extern "C" { int F2PyDict_SetItemString(PyObject *dict, char *name, PyObject *obj) { - if (obj==NULL) { + if (obj == NULL) { fprintf(stderr, "Error loading %s\n", name); if (PyErr_Occurred()) { PyErr_Print(); @@ -30,57 +30,152 @@ F2PyDict_SetItemString(PyObject *dict, char *name, PyObject *obj) return PyDict_SetItemString(dict, name, obj); } +/* + * Python-only fallback for thread-local callback pointers + */ +void * +F2PySwapThreadLocalCallbackPtr(char *key, void *ptr) +{ + PyObject *local_dict, *value; + void *prev; + + local_dict = PyThreadState_GetDict(); + if (local_dict == NULL) { + Py_FatalError( + "F2PySwapThreadLocalCallbackPtr: PyThreadState_GetDict " + "failed"); + } + + value = PyDict_GetItemString(local_dict, key); + if (value != NULL) { + prev = PyLong_AsVoidPtr(value); + if (PyErr_Occurred()) { + Py_FatalError( + "F2PySwapThreadLocalCallbackPtr: PyLong_AsVoidPtr failed"); + } + } + else { + prev = NULL; + } + + value = PyLong_FromVoidPtr((void *)ptr); + if (value == NULL) { + Py_FatalError( + "F2PySwapThreadLocalCallbackPtr: PyLong_FromVoidPtr failed"); + } + + if (PyDict_SetItemString(local_dict, key, value) != 0) { + Py_FatalError( + "F2PySwapThreadLocalCallbackPtr: PyDict_SetItemString failed"); + } + + Py_DECREF(value); + + return prev; +} + +void * +F2PyGetThreadLocalCallbackPtr(char *key) +{ + PyObject *local_dict, *value; + void *prev; + + local_dict = PyThreadState_GetDict(); + if (local_dict == NULL) { + Py_FatalError( + "F2PyGetThreadLocalCallbackPtr: PyThreadState_GetDict failed"); + } + + value = PyDict_GetItemString(local_dict, key); + if (value != NULL) { + prev = PyLong_AsVoidPtr(value); + if (PyErr_Occurred()) { + Py_FatalError( + "F2PyGetThreadLocalCallbackPtr: PyLong_AsVoidPtr failed"); + } + } + else { + prev = NULL; + } + + return prev; +} + /************************* FortranObject *******************************/ -typedef PyObject *(*fortranfunc)(PyObject *,PyObject *,PyObject *,void *); +typedef PyObject *(*fortranfunc)(PyObject *, PyObject *, PyObject *, void *); PyObject * -PyFortranObject_New(FortranDataDef* defs, f2py_void_func init) { +PyFortranObject_New(FortranDataDef *defs, f2py_void_func init) +{ int i; PyFortranObject *fp = NULL; PyObject *v = NULL; - if (init!=NULL) /* Initialize F90 module objects */ + if (init != NULL) { /* Initialize F90 module objects */ (*(init))(); - if ((fp = PyObject_New(PyFortranObject, &PyFortran_Type))==NULL) return NULL; - if ((fp->dict = PyDict_New())==NULL) return NULL; + } + fp = PyObject_New(PyFortranObject, &PyFortran_Type); + if (fp == NULL) { + return NULL; + } + if ((fp->dict = PyDict_New()) == NULL) { + Py_DECREF(fp); + return NULL; + } fp->len = 0; - while (defs[fp->len].name != NULL) fp->len++; - if (fp->len == 0) goto fail; + while (defs[fp->len].name != NULL) { + fp->len++; + } + if (fp->len == 0) { + goto fail; + } fp->defs = defs; - for (i=0;i<fp->len;i++) - if (fp->defs[i].rank == -1) { /* Is Fortran routine */ + for (i = 0; i < fp->len; i++) { + if (fp->defs[i].rank == -1) { /* Is Fortran routine */ v = PyFortranObject_NewAsAttr(&(fp->defs[i])); - if (v==NULL) return NULL; - PyDict_SetItemString(fp->dict,fp->defs[i].name,v); - } else - if ((fp->defs[i].data)!=NULL) { /* Is Fortran variable or array (not allocatable) */ - if (fp->defs[i].type == NPY_STRING) { - int n = fp->defs[i].rank-1; - v = PyArray_New(&PyArray_Type, n, fp->defs[i].dims.d, - NPY_STRING, NULL, fp->defs[i].data, fp->defs[i].dims.d[n], - NPY_ARRAY_FARRAY, NULL); - } - else { - v = PyArray_New(&PyArray_Type, fp->defs[i].rank, fp->defs[i].dims.d, - fp->defs[i].type, NULL, fp->defs[i].data, 0, NPY_ARRAY_FARRAY, - NULL); - } - if (v==NULL) return NULL; - PyDict_SetItemString(fp->dict,fp->defs[i].name,v); + if (v == NULL) { + goto fail; + } + PyDict_SetItemString(fp->dict, fp->defs[i].name, v); + Py_XDECREF(v); + } + else if ((fp->defs[i].data) != + NULL) { /* Is Fortran variable or array (not allocatable) */ + if (fp->defs[i].type == NPY_STRING) { + npy_intp n = fp->defs[i].rank - 1; + v = PyArray_New(&PyArray_Type, n, fp->defs[i].dims.d, + NPY_STRING, NULL, fp->defs[i].data, + fp->defs[i].dims.d[n], NPY_ARRAY_FARRAY, NULL); + } + else { + v = PyArray_New(&PyArray_Type, fp->defs[i].rank, + fp->defs[i].dims.d, fp->defs[i].type, NULL, + fp->defs[i].data, 0, NPY_ARRAY_FARRAY, NULL); + } + if (v == NULL) { + goto fail; } - Py_XDECREF(v); + PyDict_SetItemString(fp->dict, fp->defs[i].name, v); + Py_XDECREF(v); + } + } return (PyObject *)fp; - fail: - Py_XDECREF(v); +fail: + Py_XDECREF(fp); return NULL; } PyObject * -PyFortranObject_NewAsAttr(FortranDataDef* defs) { /* used for calling F90 module routines */ +PyFortranObject_NewAsAttr(FortranDataDef *defs) +{ /* used for calling F90 module routines */ PyFortranObject *fp = NULL; fp = PyObject_New(PyFortranObject, &PyFortran_Type); - if (fp == NULL) return NULL; - if ((fp->dict = PyDict_New())==NULL) return NULL; + if (fp == NULL) + return NULL; + if ((fp->dict = PyDict_New()) == NULL) { + PyObject_Del(fp); + return NULL; + } fp->len = 1; fp->defs = defs; return (PyObject *)fp; @@ -89,26 +184,19 @@ PyFortranObject_NewAsAttr(FortranDataDef* defs) { /* used for calling F90 module /* Fortran methods */ static void -fortran_dealloc(PyFortranObject *fp) { +fortran_dealloc(PyFortranObject *fp) +{ Py_XDECREF(fp->dict); - PyMem_Del(fp); + PyObject_Del(fp); } - -#if PY_VERSION_HEX >= 0x03000000 -#else -static PyMethodDef fortran_methods[] = { - {NULL, NULL} /* sentinel */ -}; -#endif - - /* Returns number of bytes consumed from buf, or -1 on error. */ static Py_ssize_t format_def(char *buf, Py_ssize_t size, FortranDataDef def) { char *p = buf; - int i, n; + int i; + npy_intp n; n = PyOS_snprintf(p, size, "array(%" NPY_INTP_FMT, def.dims.d[0]); if (n < 0 || n >= size) { @@ -135,10 +223,12 @@ format_def(char *buf, Py_ssize_t size, FortranDataDef def) if (def.data == NULL) { static const char notalloc[] = ", not allocated"; - if (size < sizeof(notalloc)) { + if ((size_t)size < sizeof(notalloc)) { return -1; } memcpy(p, notalloc, sizeof(notalloc)); + p += sizeof(notalloc); + size -= sizeof(notalloc); } return p - buf; @@ -181,7 +271,7 @@ fortran_doc(FortranDataDef def) } else { PyArray_Descr *d = PyArray_DescrFromType(def.type); - n = PyOS_snprintf(p, size, "'%c'-", d->type); + n = PyOS_snprintf(p, size, "%s : '%c'-", def.name, d->type); Py_DECREF(d); if (n < 0 || n >= size) { goto fail; @@ -190,7 +280,7 @@ fortran_doc(FortranDataDef def) size -= n; if (def.data == NULL) { - n = format_def(p, size, def) == -1; + n = format_def(p, size, def); if (n < 0) { goto fail; } @@ -222,26 +312,25 @@ fortran_doc(FortranDataDef def) size--; /* p now points one beyond the last character of the string in buf */ -#if PY_VERSION_HEX >= 0x03000000 s = PyUnicode_FromStringAndSize(buf, p - buf); -#else - s = PyString_FromStringAndSize(buf, p - buf); -#endif PyMem_Free(buf); return s; - fail: - fprintf(stderr, "fortranobject.c: fortran_doc: len(p)=%zd>%zd=size:" - " too long docstring required, increase size\n", +fail: + fprintf(stderr, + "fortranobject.c: fortran_doc: len(p)=%zd>%zd=size:" + " too long docstring required, increase size\n", p - buf, origsize); PyMem_Free(buf); return NULL; } static FortranDataDef *save_def; /* save pointer of an allocatable array */ -static void set_data(char *d,npy_intp *f) { /* callback from Fortran */ - if (*f) /* In fortran f=allocated(d) */ +static void +set_data(char *d, npy_intp *f) +{ /* callback from Fortran */ + if (*f) /* In fortran f=allocated(d) */ save_def->data = d; else save_def->data = NULL; @@ -249,126 +338,141 @@ static void set_data(char *d,npy_intp *f) { /* callback from Fortran */ } static PyObject * -fortran_getattr(PyFortranObject *fp, char *name) { - int i,j,k,flag; +fortran_getattr(PyFortranObject *fp, char *name) +{ + int i, j, k, flag; if (fp->dict != NULL) { - PyObject *v = PyDict_GetItemString(fp->dict, name); - if (v != NULL) { + PyObject *v = _PyDict_GetItemStringWithError(fp->dict, name); + if (v == NULL && PyErr_Occurred()) { + return NULL; + } + else if (v != NULL) { Py_INCREF(v); return v; } } - for (i=0,j=1;i<fp->len && (j=strcmp(name,fp->defs[i].name));i++); - if (j==0) - if (fp->defs[i].rank!=-1) { /* F90 allocatable array */ - if (fp->defs[i].func==NULL) return NULL; - for(k=0;k<fp->defs[i].rank;++k) - fp->defs[i].dims.d[k]=-1; + for (i = 0, j = 1; i < fp->len && (j = strcmp(name, fp->defs[i].name)); + i++) + ; + if (j == 0) + if (fp->defs[i].rank != -1) { /* F90 allocatable array */ + if (fp->defs[i].func == NULL) + return NULL; + for (k = 0; k < fp->defs[i].rank; ++k) fp->defs[i].dims.d[k] = -1; save_def = &fp->defs[i]; - (*(fp->defs[i].func))(&fp->defs[i].rank,fp->defs[i].dims.d,set_data,&flag); - if (flag==2) + (*(fp->defs[i].func))(&fp->defs[i].rank, fp->defs[i].dims.d, + set_data, &flag); + if (flag == 2) k = fp->defs[i].rank + 1; else k = fp->defs[i].rank; - if (fp->defs[i].data !=NULL) { /* array is allocated */ - PyObject *v = PyArray_New(&PyArray_Type, k, fp->defs[i].dims.d, - fp->defs[i].type, NULL, fp->defs[i].data, 0, NPY_ARRAY_FARRAY, - NULL); - if (v==NULL) return NULL; + if (fp->defs[i].data != NULL) { /* array is allocated */ + PyObject *v = PyArray_New( + &PyArray_Type, k, fp->defs[i].dims.d, fp->defs[i].type, + NULL, fp->defs[i].data, 0, NPY_ARRAY_FARRAY, NULL); + if (v == NULL) + return NULL; /* Py_INCREF(v); */ return v; - } else { /* array is not allocated */ + } + else { /* array is not allocated */ Py_RETURN_NONE; } } - if (strcmp(name,"__dict__")==0) { + if (strcmp(name, "__dict__") == 0) { Py_INCREF(fp->dict); return fp->dict; } - if (strcmp(name,"__doc__")==0) { -#if PY_VERSION_HEX >= 0x03000000 + if (strcmp(name, "__doc__") == 0) { PyObject *s = PyUnicode_FromString(""), *s2, *s3; - for (i=0;i<fp->len;i++) { + for (i = 0; i < fp->len; i++) { s2 = fortran_doc(fp->defs[i]); s3 = PyUnicode_Concat(s, s2); Py_DECREF(s2); Py_DECREF(s); s = s3; } -#else - PyObject *s = PyString_FromString(""); - for (i=0;i<fp->len;i++) - PyString_ConcatAndDel(&s,fortran_doc(fp->defs[i])); -#endif if (PyDict_SetItemString(fp->dict, name, s)) return NULL; return s; } - if ((strcmp(name,"_cpointer")==0) && (fp->len==1)) { - PyObject *cobj = F2PyCapsule_FromVoidPtr((void *)(fp->defs[0].data),NULL); + if ((strcmp(name, "_cpointer") == 0) && (fp->len == 1)) { + PyObject *cobj = + F2PyCapsule_FromVoidPtr((void *)(fp->defs[0].data), NULL); if (PyDict_SetItemString(fp->dict, name, cobj)) return NULL; return cobj; } -#if PY_VERSION_HEX >= 0x03000000 - if (1) { - PyObject *str, *ret; - str = PyUnicode_FromString(name); - ret = PyObject_GenericGetAttr((PyObject *)fp, str); - Py_DECREF(str); - return ret; - } -#else - return Py_FindMethod(fortran_methods, (PyObject *)fp, name); -#endif + PyObject *str, *ret; + str = PyUnicode_FromString(name); + ret = PyObject_GenericGetAttr((PyObject *)fp, str); + Py_DECREF(str); + return ret; } static int -fortran_setattr(PyFortranObject *fp, char *name, PyObject *v) { - int i,j,flag; +fortran_setattr(PyFortranObject *fp, char *name, PyObject *v) +{ + int i, j, flag; PyArrayObject *arr = NULL; - for (i=0,j=1;i<fp->len && (j=strcmp(name,fp->defs[i].name));i++); - if (j==0) { - if (fp->defs[i].rank==-1) { - PyErr_SetString(PyExc_AttributeError,"over-writing fortran routine"); + for (i = 0, j = 1; i < fp->len && (j = strcmp(name, fp->defs[i].name)); + i++) + ; + if (j == 0) { + if (fp->defs[i].rank == -1) { + PyErr_SetString(PyExc_AttributeError, + "over-writing fortran routine"); return -1; } - if (fp->defs[i].func!=NULL) { /* is allocatable array */ + if (fp->defs[i].func != NULL) { /* is allocatable array */ npy_intp dims[F2PY_MAX_DIMS]; int k; save_def = &fp->defs[i]; - if (v!=Py_None) { /* set new value (reallocate if needed -- - see f2py generated code for more - details ) */ - for(k=0;k<fp->defs[i].rank;k++) dims[k]=-1; - if ((arr = array_from_pyobj(fp->defs[i].type,dims,fp->defs[i].rank,F2PY_INTENT_IN,v))==NULL) + if (v != Py_None) { /* set new value (reallocate if needed -- + see f2py generated code for more + details ) */ + for (k = 0; k < fp->defs[i].rank; k++) dims[k] = -1; + if ((arr = array_from_pyobj(fp->defs[i].type, dims, + fp->defs[i].rank, F2PY_INTENT_IN, + v)) == NULL) return -1; - (*(fp->defs[i].func))(&fp->defs[i].rank,PyArray_DIMS(arr),set_data,&flag); - } else { /* deallocate */ - for(k=0;k<fp->defs[i].rank;k++) dims[k]=0; - (*(fp->defs[i].func))(&fp->defs[i].rank,dims,set_data,&flag); - for(k=0;k<fp->defs[i].rank;k++) dims[k]=-1; + (*(fp->defs[i].func))(&fp->defs[i].rank, PyArray_DIMS(arr), + set_data, &flag); } - memcpy(fp->defs[i].dims.d,dims,fp->defs[i].rank*sizeof(npy_intp)); - } else { /* not allocatable array */ - if ((arr = array_from_pyobj(fp->defs[i].type,fp->defs[i].dims.d,fp->defs[i].rank,F2PY_INTENT_IN,v))==NULL) + else { /* deallocate */ + for (k = 0; k < fp->defs[i].rank; k++) dims[k] = 0; + (*(fp->defs[i].func))(&fp->defs[i].rank, dims, set_data, + &flag); + for (k = 0; k < fp->defs[i].rank; k++) dims[k] = -1; + } + memcpy(fp->defs[i].dims.d, dims, + fp->defs[i].rank * sizeof(npy_intp)); + } + else { /* not allocatable array */ + if ((arr = array_from_pyobj(fp->defs[i].type, fp->defs[i].dims.d, + fp->defs[i].rank, F2PY_INTENT_IN, + v)) == NULL) return -1; } - if (fp->defs[i].data!=NULL) { /* copy Python object to Fortran array */ - npy_intp s = PyArray_MultiplyList(fp->defs[i].dims.d,PyArray_NDIM(arr)); - if (s==-1) - s = PyArray_MultiplyList(PyArray_DIMS(arr),PyArray_NDIM(arr)); - if (s<0 || - (memcpy(fp->defs[i].data,PyArray_DATA(arr),s*PyArray_ITEMSIZE(arr)))==NULL) { - if ((PyObject*)arr!=v) { + if (fp->defs[i].data != + NULL) { /* copy Python object to Fortran array */ + npy_intp s = PyArray_MultiplyList(fp->defs[i].dims.d, + PyArray_NDIM(arr)); + if (s == -1) + s = PyArray_MultiplyList(PyArray_DIMS(arr), PyArray_NDIM(arr)); + if (s < 0 || (memcpy(fp->defs[i].data, PyArray_DATA(arr), + s * PyArray_ITEMSIZE(arr))) == NULL) { + if ((PyObject *)arr != v) { Py_DECREF(arr); } return -1; } - if ((PyObject*)arr!=v) { + if ((PyObject *)arr != v) { Py_DECREF(arr); } - } else return (fp->defs[i].func==NULL?-1:0); + } + else + return (fp->defs[i].func == NULL ? -1 : 0); return 0; /* successful */ } if (fp->dict == NULL) { @@ -379,30 +483,33 @@ fortran_setattr(PyFortranObject *fp, char *name, PyObject *v) { if (v == NULL) { int rv = PyDict_DelItemString(fp->dict, name); if (rv < 0) - PyErr_SetString(PyExc_AttributeError,"delete non-existing fortran attribute"); + PyErr_SetString(PyExc_AttributeError, + "delete non-existing fortran attribute"); return rv; } else return PyDict_SetItemString(fp->dict, name, v); } -static PyObject* -fortran_call(PyFortranObject *fp, PyObject *arg, PyObject *kw) { +static PyObject * +fortran_call(PyFortranObject *fp, PyObject *arg, PyObject *kw) +{ int i = 0; /* printf("fortran call name=%s,func=%p,data=%p,%p\n",fp->defs[i].name, fp->defs[i].func,fp->defs[i].data,&fp->defs[i].data); */ - if (fp->defs[i].rank==-1) {/* is Fortran routine */ - if (fp->defs[i].func==NULL) { + if (fp->defs[i].rank == -1) { /* is Fortran routine */ + if (fp->defs[i].func == NULL) { PyErr_Format(PyExc_RuntimeError, "no function to call"); return NULL; } - else if (fp->defs[i].data==NULL) + else if (fp->defs[i].data == NULL) /* dummy routine */ - return (*((fortranfunc)(fp->defs[i].func)))((PyObject *)fp,arg,kw,NULL); + return (*((fortranfunc)(fp->defs[i].func)))((PyObject *)fp, arg, + kw, NULL); else - return (*((fortranfunc)(fp->defs[i].func)))((PyObject *)fp,arg,kw, - (void *)fp->defs[i].data); + return (*((fortranfunc)(fp->defs[i].func)))( + (PyObject *)fp, arg, kw, (void *)fp->defs[i].data); } PyErr_Format(PyExc_TypeError, "this fortran object is not callable"); return NULL; @@ -414,48 +521,24 @@ fortran_repr(PyFortranObject *fp) PyObject *name = NULL, *repr = NULL; name = PyObject_GetAttrString((PyObject *)fp, "__name__"); PyErr_Clear(); -#if PY_VERSION_HEX >= 0x03000000 if (name != NULL && PyUnicode_Check(name)) { repr = PyUnicode_FromFormat("<fortran %U>", name); } else { repr = PyUnicode_FromString("<fortran object>"); } -#else - if (name != NULL && PyString_Check(name)) { - repr = PyString_FromFormat("<fortran %s>", PyString_AsString(name)); - } - else { - repr = PyString_FromString("<fortran object>"); - } -#endif Py_XDECREF(name); return repr; } - PyTypeObject PyFortran_Type = { -#if PY_VERSION_HEX >= 0x03000000 - PyVarObject_HEAD_INIT(NULL, 0) -#else - PyObject_HEAD_INIT(0) - 0, /*ob_size*/ -#endif - "fortran", /*tp_name*/ - sizeof(PyFortranObject), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - /* methods */ - (destructor)fortran_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - (getattrfunc)fortran_getattr, /*tp_getattr*/ - (setattrfunc)fortran_setattr, /*tp_setattr*/ - 0, /*tp_compare/tp_reserved*/ - (reprfunc)fortran_repr, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash*/ - (ternaryfunc)fortran_call, /*tp_call*/ + PyVarObject_HEAD_INIT(NULL, 0).tp_name = "fortran", + .tp_basicsize = sizeof(PyFortranObject), + .tp_dealloc = (destructor)fortran_dealloc, + .tp_getattr = (getattrfunc)fortran_getattr, + .tp_setattr = (setattrfunc)fortran_setattr, + .tp_repr = (reprfunc)fortran_repr, + .tp_call = (ternaryfunc)fortran_call, }; /************************* f2py_report_atexit *******************************/ @@ -476,99 +559,123 @@ static struct timeb cb_stop_time; static struct timeb cb_start_call_time; static struct timeb cb_stop_call_time; -extern void f2py_start_clock(void) { ftime(&start_time); } -extern -void f2py_start_call_clock(void) { +extern void +f2py_start_clock(void) +{ + ftime(&start_time); +} +extern void +f2py_start_call_clock(void) +{ f2py_stop_clock(); ftime(&start_call_time); } -extern -void f2py_stop_clock(void) { +extern void +f2py_stop_clock(void) +{ ftime(&stop_time); - passed_time += 1000*(stop_time.time - start_time.time); + passed_time += 1000 * (stop_time.time - start_time.time); passed_time += stop_time.millitm - start_time.millitm; } -extern -void f2py_stop_call_clock(void) { +extern void +f2py_stop_call_clock(void) +{ ftime(&stop_call_time); - passed_call_time += 1000*(stop_call_time.time - start_call_time.time); + passed_call_time += 1000 * (stop_call_time.time - start_call_time.time); passed_call_time += stop_call_time.millitm - start_call_time.millitm; passed_counter += 1; f2py_start_clock(); } -extern void f2py_cb_start_clock(void) { ftime(&cb_start_time); } -extern -void f2py_cb_start_call_clock(void) { +extern void +f2py_cb_start_clock(void) +{ + ftime(&cb_start_time); +} +extern void +f2py_cb_start_call_clock(void) +{ f2py_cb_stop_clock(); ftime(&cb_start_call_time); } -extern -void f2py_cb_stop_clock(void) { +extern void +f2py_cb_stop_clock(void) +{ ftime(&cb_stop_time); - cb_passed_time += 1000*(cb_stop_time.time - cb_start_time.time); + cb_passed_time += 1000 * (cb_stop_time.time - cb_start_time.time); cb_passed_time += cb_stop_time.millitm - cb_start_time.millitm; } -extern -void f2py_cb_stop_call_clock(void) { +extern void +f2py_cb_stop_call_clock(void) +{ ftime(&cb_stop_call_time); - cb_passed_call_time += 1000*(cb_stop_call_time.time - cb_start_call_time.time); - cb_passed_call_time += cb_stop_call_time.millitm - cb_start_call_time.millitm; + cb_passed_call_time += + 1000 * (cb_stop_call_time.time - cb_start_call_time.time); + cb_passed_call_time += + cb_stop_call_time.millitm - cb_start_call_time.millitm; cb_passed_counter += 1; f2py_cb_start_clock(); } static int f2py_report_on_exit_been_here = 0; -extern -void f2py_report_on_exit(int exit_flag,void *name) { +extern void +f2py_report_on_exit(int exit_flag, void *name) +{ if (f2py_report_on_exit_been_here) { - fprintf(stderr," %s\n",(char*)name); + fprintf(stderr, " %s\n", (char *)name); return; } f2py_report_on_exit_been_here = 1; - fprintf(stderr," /-----------------------\\\n"); - fprintf(stderr," < F2PY performance report >\n"); - fprintf(stderr," \\-----------------------/\n"); - fprintf(stderr,"Overall time spent in ...\n"); - fprintf(stderr,"(a) wrapped (Fortran/C) functions : %8d msec\n", + fprintf(stderr, " /-----------------------\\\n"); + fprintf(stderr, " < F2PY performance report >\n"); + fprintf(stderr, " \\-----------------------/\n"); + fprintf(stderr, "Overall time spent in ...\n"); + fprintf(stderr, "(a) wrapped (Fortran/C) functions : %8d msec\n", passed_call_time); - fprintf(stderr,"(b) f2py interface, %6d calls : %8d msec\n", - passed_counter,passed_time); - fprintf(stderr,"(c) call-back (Python) functions : %8d msec\n", + fprintf(stderr, "(b) f2py interface, %6d calls : %8d msec\n", + passed_counter, passed_time); + fprintf(stderr, "(c) call-back (Python) functions : %8d msec\n", cb_passed_call_time); - fprintf(stderr,"(d) f2py call-back interface, %6d calls : %8d msec\n", - cb_passed_counter,cb_passed_time); - - fprintf(stderr,"(e) wrapped (Fortran/C) functions (actual) : %8d msec\n\n", - passed_call_time-cb_passed_call_time-cb_passed_time); - fprintf(stderr,"Use -DF2PY_REPORT_ATEXIT_DISABLE to disable this message.\n"); - fprintf(stderr,"Exit status: %d\n",exit_flag); - fprintf(stderr,"Modules : %s\n",(char*)name); + fprintf(stderr, "(d) f2py call-back interface, %6d calls : %8d msec\n", + cb_passed_counter, cb_passed_time); + + fprintf(stderr, + "(e) wrapped (Fortran/C) functions (actual) : %8d msec\n\n", + passed_call_time - cb_passed_call_time - cb_passed_time); + fprintf(stderr, + "Use -DF2PY_REPORT_ATEXIT_DISABLE to disable this message.\n"); + fprintf(stderr, "Exit status: %d\n", exit_flag); + fprintf(stderr, "Modules : %s\n", (char *)name); } #endif /********************** report on array copy ****************************/ #ifdef F2PY_REPORT_ON_ARRAY_COPY -static void f2py_report_on_array_copy(PyArrayObject* arr) { +static void +f2py_report_on_array_copy(PyArrayObject *arr) +{ const npy_intp arr_size = PyArray_Size((PyObject *)arr); - if (arr_size>F2PY_REPORT_ON_ARRAY_COPY) { - fprintf(stderr,"copied an array: size=%ld, elsize=%"NPY_INTP_FMT"\n", + if (arr_size > F2PY_REPORT_ON_ARRAY_COPY) { + fprintf(stderr, + "copied an array: size=%ld, elsize=%" NPY_INTP_FMT "\n", arr_size, (npy_intp)PyArray_ITEMSIZE(arr)); } } -static void f2py_report_on_array_copy_fromany(void) { - fprintf(stderr,"created an array from object\n"); +static void +f2py_report_on_array_copy_fromany(void) +{ + fprintf(stderr, "created an array from object\n"); } -#define F2PY_REPORT_ON_ARRAY_COPY_FROMARR f2py_report_on_array_copy((PyArrayObject *)arr) +#define F2PY_REPORT_ON_ARRAY_COPY_FROMARR \ + f2py_report_on_array_copy((PyArrayObject *)arr) #define F2PY_REPORT_ON_ARRAY_COPY_FROMANY f2py_report_on_array_copy_fromany() #else #define F2PY_REPORT_ON_ARRAY_COPY_FROMARR #define F2PY_REPORT_ON_ARRAY_COPY_FROMANY #endif - /************************* array_from_obj *******************************/ /* @@ -590,72 +697,81 @@ static void f2py_report_on_array_copy_fromany(void) { * $Id: fortranobject.c,v 1.52 2005/07/11 07:44:20 pearu Exp $ */ -static int check_and_fix_dimensions(const PyArrayObject* arr, - const int rank, - npy_intp *dims); +static int +check_and_fix_dimensions(const PyArrayObject *arr, const int rank, + npy_intp *dims); static int -count_negative_dimensions(const int rank, - const npy_intp *dims) { - int i=0,r=0; - while (i<rank) { - if (dims[i] < 0) ++r; - ++i; +find_first_negative_dimension(const int rank, const npy_intp *dims) +{ + for (int i = 0; i < rank; ++i) { + if (dims[i] < 0) { + return i; + } } - return r; + return -1; } #ifdef DEBUG_COPY_ND_ARRAY -void dump_dims(int rank, npy_intp* dims) { +void +dump_dims(int rank, npy_intp const *dims) +{ int i; printf("["); - for(i=0;i<rank;++i) { + for (i = 0; i < rank; ++i) { printf("%3" NPY_INTP_FMT, dims[i]); } printf("]\n"); } -void dump_attrs(const PyArrayObject* obj) { - const PyArrayObject_fields *arr = (const PyArrayObject_fields*) obj; +void +dump_attrs(const PyArrayObject *obj) +{ + const PyArrayObject_fields *arr = (const PyArrayObject_fields *)obj; int rank = PyArray_NDIM(arr); npy_intp size = PyArray_Size((PyObject *)arr); - printf("\trank = %d, flags = %d, size = %" NPY_INTP_FMT "\n", - rank,arr->flags,size); + printf("\trank = %d, flags = %d, size = %" NPY_INTP_FMT "\n", rank, + arr->flags, size); printf("\tstrides = "); - dump_dims(rank,arr->strides); + dump_dims(rank, arr->strides); printf("\tdimensions = "); - dump_dims(rank,arr->dimensions); + dump_dims(rank, arr->dimensions); } #endif -#define SWAPTYPE(a,b,t) {t c; c = (a); (a) = (b); (b) = c; } - -static int swap_arrays(PyArrayObject* obj1, PyArrayObject* obj2) { - PyArrayObject_fields *arr1 = (PyArrayObject_fields*) obj1, - *arr2 = (PyArrayObject_fields*) obj2; - SWAPTYPE(arr1->data,arr2->data,char*); - SWAPTYPE(arr1->nd,arr2->nd,int); - SWAPTYPE(arr1->dimensions,arr2->dimensions,npy_intp*); - SWAPTYPE(arr1->strides,arr2->strides,npy_intp*); - SWAPTYPE(arr1->base,arr2->base,PyObject*); - SWAPTYPE(arr1->descr,arr2->descr,PyArray_Descr*); - SWAPTYPE(arr1->flags,arr2->flags,int); +#define SWAPTYPE(a, b, t) \ + { \ + t c; \ + c = (a); \ + (a) = (b); \ + (b) = c; \ + } + +static int +swap_arrays(PyArrayObject *obj1, PyArrayObject *obj2) +{ + PyArrayObject_fields *arr1 = (PyArrayObject_fields *)obj1, + *arr2 = (PyArrayObject_fields *)obj2; + SWAPTYPE(arr1->data, arr2->data, char *); + SWAPTYPE(arr1->nd, arr2->nd, int); + SWAPTYPE(arr1->dimensions, arr2->dimensions, npy_intp *); + SWAPTYPE(arr1->strides, arr2->strides, npy_intp *); + SWAPTYPE(arr1->base, arr2->base, PyObject *); + SWAPTYPE(arr1->descr, arr2->descr, PyArray_Descr *); + SWAPTYPE(arr1->flags, arr2->flags, int); /* SWAPTYPE(arr1->weakreflist,arr2->weakreflist,PyObject*); */ return 0; } -#define ARRAY_ISCOMPATIBLE(arr,type_num) \ - ( (PyArray_ISINTEGER(arr) && PyTypeNum_ISINTEGER(type_num)) \ - ||(PyArray_ISFLOAT(arr) && PyTypeNum_ISFLOAT(type_num)) \ - ||(PyArray_ISCOMPLEX(arr) && PyTypeNum_ISCOMPLEX(type_num)) \ - ||(PyArray_ISBOOL(arr) && PyTypeNum_ISBOOL(type_num)) \ - ) - -extern -PyArrayObject* array_from_pyobj(const int type_num, - npy_intp *dims, - const int rank, - const int intent, - PyObject *obj) { +#define ARRAY_ISCOMPATIBLE(arr, type_num) \ + ((PyArray_ISINTEGER(arr) && PyTypeNum_ISINTEGER(type_num)) || \ + (PyArray_ISFLOAT(arr) && PyTypeNum_ISFLOAT(type_num)) || \ + (PyArray_ISCOMPLEX(arr) && PyTypeNum_ISCOMPLEX(type_num)) || \ + (PyArray_ISBOOL(arr) && PyTypeNum_ISBOOL(type_num))) + +extern PyArrayObject * +array_from_pyobj(const int type_num, npy_intp *dims, const int rank, + const int intent, PyObject *obj) +{ /* * Note about reference counting * ----------------------------- @@ -674,27 +790,23 @@ PyArrayObject* array_from_pyobj(const int type_num, char typechar; int elsize; - if ((intent & F2PY_INTENT_HIDE) - || ((intent & F2PY_INTENT_CACHE) && (obj==Py_None)) - || ((intent & F2PY_OPTIONAL) && (obj==Py_None)) - ) { + if ((intent & F2PY_INTENT_HIDE) || + ((intent & F2PY_INTENT_CACHE) && (obj == Py_None)) || + ((intent & F2PY_OPTIONAL) && (obj == Py_None))) { /* intent(cache), optional, intent(hide) */ - if (count_negative_dimensions(rank,dims) > 0) { - int i; - strcpy(mess, "failed to create intent(cache|hide)|optional array" - "-- must have defined dimensions but got ("); - for(i=0;i<rank;++i) - sprintf(mess+strlen(mess),"%" NPY_INTP_FMT ",",dims[i]); - strcat(mess, ")"); - PyErr_SetString(PyExc_ValueError,mess); + int i = find_first_negative_dimension(rank, dims); + if (i >= 0) { + PyErr_Format(PyExc_ValueError, + "failed to create intent(cache|hide)|optional array" + " -- must have defined dimensions, but dims[%d] = %" + NPY_INTP_FMT, i, dims[i]); return NULL; } - arr = (PyArrayObject *) - PyArray_New(&PyArray_Type, rank, dims, type_num, - NULL,NULL,1, - !(intent&F2PY_INTENT_C), - NULL); - if (arr==NULL) return NULL; + arr = (PyArrayObject *)PyArray_New(&PyArray_Type, rank, dims, type_num, + NULL, NULL, 1, + !(intent & F2PY_INTENT_C), NULL); + if (arr == NULL) + return NULL; if (!(intent & F2PY_INTENT_CACHE)) PyArray_FILLWBYTE(arr, 0); return arr; @@ -718,8 +830,7 @@ PyArrayObject* array_from_pyobj(const int type_num, if (intent & F2PY_INTENT_CACHE) { /* intent(cache) */ - if (PyArray_ISONESEGMENT(arr) - && PyArray_ITEMSIZE(arr)>=elsize) { + if (PyArray_ISONESEGMENT(arr) && PyArray_ITEMSIZE(arr) >= elsize) { if (check_and_fix_dimensions(arr, rank, dims)) { return NULL; } @@ -730,17 +841,17 @@ PyArrayObject* array_from_pyobj(const int type_num, strcpy(mess, "failed to initialize intent(cache) array"); if (!PyArray_ISONESEGMENT(arr)) strcat(mess, " -- input must be in one segment"); - if (PyArray_ITEMSIZE(arr)<elsize) - sprintf(mess+strlen(mess), - " -- expected at least elsize=%d but got %" NPY_INTP_FMT, - elsize, - (npy_intp)PyArray_ITEMSIZE(arr) - ); - PyErr_SetString(PyExc_ValueError,mess); + if (PyArray_ITEMSIZE(arr) < elsize) + sprintf(mess + strlen(mess), + " -- expected at least elsize=%d but got " + "%" NPY_INTP_FMT, + elsize, (npy_intp)PyArray_ITEMSIZE(arr)); + PyErr_SetString(PyExc_ValueError, mess); return NULL; } - /* here we have always intent(in) or intent(inout) or intent(inplace) */ + /* here we have always intent(in) or intent(inout) or intent(inplace) + */ if (check_and_fix_dimensions(arr, rank, dims)) { return NULL; @@ -752,12 +863,12 @@ PyArrayObject* array_from_pyobj(const int type_num, for (i=1;i<=16;i++) printf("i=%d isaligned=%d\n", i, ARRAY_ISALIGNED(arr, i)); */ - if ((! (intent & F2PY_INTENT_COPY)) - && PyArray_ITEMSIZE(arr)==elsize - && ARRAY_ISCOMPATIBLE(arr,type_num) - && F2PY_CHECK_ALIGNMENT(arr, intent) - ) { - if ((intent & F2PY_INTENT_C)?PyArray_ISCARRAY(arr):PyArray_ISFARRAY(arr)) { + if ((!(intent & F2PY_INTENT_COPY)) && + PyArray_ITEMSIZE(arr) == elsize && + ARRAY_ISCOMPATIBLE(arr, type_num) && + F2PY_CHECK_ALIGNMENT(arr, intent)) { + if ((intent & F2PY_INTENT_C) ? PyArray_ISCARRAY_RO(arr) + : PyArray_ISFARRAY_RO(arr)) { if ((intent & F2PY_INTENT_OUT)) { Py_INCREF(arr); } @@ -765,38 +876,37 @@ PyArrayObject* array_from_pyobj(const int type_num, return arr; } } - if (intent & F2PY_INTENT_INOUT) { strcpy(mess, "failed to initialize intent(inout) array"); + /* Must use PyArray_IS*ARRAY because intent(inout) requires + * writable input */ if ((intent & F2PY_INTENT_C) && !PyArray_ISCARRAY(arr)) strcat(mess, " -- input not contiguous"); if (!(intent & F2PY_INTENT_C) && !PyArray_ISFARRAY(arr)) strcat(mess, " -- input not fortran contiguous"); - if (PyArray_ITEMSIZE(arr)!=elsize) - sprintf(mess+strlen(mess), + if (PyArray_ITEMSIZE(arr) != elsize) + sprintf(mess + strlen(mess), " -- expected elsize=%d but got %" NPY_INTP_FMT, - elsize, - (npy_intp)PyArray_ITEMSIZE(arr) - ); - if (!(ARRAY_ISCOMPATIBLE(arr,type_num))) - sprintf(mess+strlen(mess)," -- input '%c' not compatible to '%c'", - PyArray_DESCR(arr)->type,typechar); + elsize, (npy_intp)PyArray_ITEMSIZE(arr)); + if (!(ARRAY_ISCOMPATIBLE(arr, type_num))) + sprintf(mess + strlen(mess), + " -- input '%c' not compatible to '%c'", + PyArray_DESCR(arr)->type, typechar); if (!(F2PY_CHECK_ALIGNMENT(arr, intent))) - sprintf(mess+strlen(mess)," -- input not %d-aligned", F2PY_GET_ALIGNMENT(intent)); - PyErr_SetString(PyExc_ValueError,mess); + sprintf(mess + strlen(mess), " -- input not %d-aligned", + F2PY_GET_ALIGNMENT(intent)); + PyErr_SetString(PyExc_ValueError, mess); return NULL; } /* here we have always intent(in) or intent(inplace) */ { - PyArrayObject * retarr; - retarr = (PyArrayObject *) \ - PyArray_New(&PyArray_Type, PyArray_NDIM(arr), PyArray_DIMS(arr), type_num, - NULL,NULL,1, - !(intent&F2PY_INTENT_C), - NULL); - if (retarr==NULL) + PyArrayObject *retarr; + retarr = (PyArrayObject *)PyArray_New( + &PyArray_Type, PyArray_NDIM(arr), PyArray_DIMS(arr), + type_num, NULL, NULL, 1, !(intent & F2PY_INTENT_C), NULL); + if (retarr == NULL) return NULL; F2PY_REPORT_ON_ARRAY_COPY_FROMARR; if (PyArray_CopyInto(retarr, arr)) { @@ -804,29 +914,30 @@ PyArrayObject* array_from_pyobj(const int type_num, return NULL; } if (intent & F2PY_INTENT_INPLACE) { - if (swap_arrays(arr,retarr)) + if (swap_arrays(arr, retarr)) return NULL; /* XXX: set exception */ Py_XDECREF(retarr); if (intent & F2PY_INTENT_OUT) Py_INCREF(arr); - } else { + } + else { arr = retarr; } } return arr; } - if ((intent & F2PY_INTENT_INOUT) || - (intent & F2PY_INTENT_INPLACE) || - (intent & F2PY_INTENT_CACHE)) { - PyErr_SetString(PyExc_TypeError, - "failed to initialize intent(inout|inplace|cache) " - "array, input not an array"); + if ((intent & F2PY_INTENT_INOUT) || (intent & F2PY_INTENT_INPLACE) || + (intent & F2PY_INTENT_CACHE)) { + PyErr_Format(PyExc_TypeError, + "failed to initialize intent(inout|inplace|cache) " + "array, input '%s' object is not an array", + Py_TYPE(obj)->tp_name); return NULL; } { - PyArray_Descr * descr = PyArray_DescrFromType(type_num); + PyArray_Descr *descr = PyArray_DescrFromType(type_num); /* compatibility with NPY_CHAR */ if (type_num == NPY_STRING) { PyArray_DESCR_REPLACE(descr); @@ -837,26 +948,28 @@ PyArrayObject* array_from_pyobj(const int type_num, descr->type = NPY_CHARLTR; } F2PY_REPORT_ON_ARRAY_COPY_FROMANY; - arr = (PyArrayObject *) \ - PyArray_FromAny(obj, descr, 0,0, - ((intent & F2PY_INTENT_C)?NPY_ARRAY_CARRAY:NPY_ARRAY_FARRAY) \ - | NPY_ARRAY_FORCECAST, NULL); - if (arr==NULL) + arr = (PyArrayObject *)PyArray_FromAny( + obj, descr, 0, 0, + ((intent & F2PY_INTENT_C) ? NPY_ARRAY_CARRAY + : NPY_ARRAY_FARRAY) | + NPY_ARRAY_FORCECAST, + NULL); + if (arr == NULL) return NULL; if (check_and_fix_dimensions(arr, rank, dims)) { return NULL; } return arr; } - } /*****************************************/ /* Helper functions for array_from_pyobj */ /*****************************************/ -static -int check_and_fix_dimensions(const PyArrayObject* arr, const int rank, npy_intp *dims) +static int +check_and_fix_dimensions(const PyArrayObject *arr, const int rank, + npy_intp *dims) { /* * This function fills in blanks (that are -1's) in dims list using @@ -865,13 +978,15 @@ int check_and_fix_dimensions(const PyArrayObject* arr, const int rank, npy_intp * * Returns 0 if the function is successful. * - * If an error condition is detected, an exception is set and 1 is returned. + * If an error condition is detected, an exception is set and 1 is + * returned. */ - const npy_intp arr_size = (PyArray_NDIM(arr))?PyArray_Size((PyObject *)arr):1; + const npy_intp arr_size = + (PyArray_NDIM(arr)) ? PyArray_Size((PyObject *)arr) : 1; #ifdef DEBUG_COPY_ND_ARRAY dump_attrs(arr); printf("check_and_fix_dimensions:init: dims="); - dump_dims(rank,dims); + dump_dims(rank, dims); #endif if (rank > PyArray_NDIM(arr)) { /* [1,2] -> [[1],[2]]; 1 -> [[1]] */ npy_intp new_size = 1; @@ -879,35 +994,39 @@ int check_and_fix_dimensions(const PyArrayObject* arr, const int rank, npy_intp int i; npy_intp d; /* Fill dims where -1 or 0; check dimensions; calc new_size; */ - for(i=0;i<PyArray_NDIM(arr);++i) { - d = PyArray_DIM(arr,i); + for (i = 0; i < PyArray_NDIM(arr); ++i) { + d = PyArray_DIM(arr, i); if (dims[i] >= 0) { - if (d>1 && dims[i]!=d) { - PyErr_Format(PyExc_ValueError, - "%d-th dimension must be fixed to %" - NPY_INTP_FMT " but got %" NPY_INTP_FMT "\n", - i, dims[i], d); + if (d > 1 && dims[i] != d) { + PyErr_Format( + PyExc_ValueError, + "%d-th dimension must be fixed to %" NPY_INTP_FMT + " but got %" NPY_INTP_FMT "\n", + i, dims[i], d); return 1; } - if (!dims[i]) dims[i] = 1; - } else { + if (!dims[i]) + dims[i] = 1; + } + else { dims[i] = d ? d : 1; } new_size *= dims[i]; } - for(i=PyArray_NDIM(arr);i<rank;++i) - if (dims[i]>1) { + for (i = PyArray_NDIM(arr); i < rank; ++i) + if (dims[i] > 1) { PyErr_Format(PyExc_ValueError, "%d-th dimension must be %" NPY_INTP_FMT " but got 0 (not defined).\n", i, dims[i]); return 1; - } else if (free_axe<0) + } + else if (free_axe < 0) free_axe = i; else dims[i] = 1; - if (free_axe>=0) { - dims[free_axe] = arr_size/new_size; + if (free_axe >= 0) { + dims[free_axe] = arr_size / new_size; new_size *= dims[free_axe]; } if (new_size != arr_size) { @@ -918,22 +1037,27 @@ int check_and_fix_dimensions(const PyArrayObject* arr, const int rank, npy_intp new_size, arr_size); return 1; } - } else if (rank==PyArray_NDIM(arr)) { + } + else if (rank == PyArray_NDIM(arr)) { npy_intp new_size = 1; int i; npy_intp d; - for (i=0; i<rank; ++i) { - d = PyArray_DIM(arr,i); - if (dims[i]>=0) { - if (d > 1 && d!=dims[i]) { - PyErr_Format(PyExc_ValueError, - "%d-th dimension must be fixed to %" - NPY_INTP_FMT " but got %" NPY_INTP_FMT "\n", - i, dims[i], d); + for (i = 0; i < rank; ++i) { + d = PyArray_DIM(arr, i); + if (dims[i] >= 0) { + if (d > 1 && d != dims[i]) { + PyErr_Format( + PyExc_ValueError, + "%d-th dimension must be fixed to %" NPY_INTP_FMT + " but got %" NPY_INTP_FMT "\n", + i, dims[i], d); return 1; } - if (!dims[i]) dims[i] = 1; - } else dims[i] = d; + if (!dims[i]) + dims[i] = 1; + } + else + dims[i] = d; new_size *= dims[i]; } if (new_size != arr_size) { @@ -943,15 +1067,17 @@ int check_and_fix_dimensions(const PyArrayObject* arr, const int rank, npy_intp new_size, arr_size); return 1; } - } else { /* [[1,2]] -> [[1],[2]] */ - int i,j; + } + else { /* [[1,2]] -> [[1],[2]] */ + int i, j; npy_intp d; int effrank; npy_intp size; - for (i=0,effrank=0;i<PyArray_NDIM(arr);++i) - if (PyArray_DIM(arr,i)>1) ++effrank; - if (dims[rank-1]>=0) - if (effrank>rank) { + for (i = 0, effrank = 0; i < PyArray_NDIM(arr); ++i) + if (PyArray_DIM(arr, i) > 1) + ++effrank; + if (dims[rank - 1] >= 0) + if (effrank > rank) { PyErr_Format(PyExc_ValueError, "too many axes: %d (effrank=%d), " "expected rank=%d\n", @@ -959,31 +1085,38 @@ int check_and_fix_dimensions(const PyArrayObject* arr, const int rank, npy_intp return 1; } - for (i=0,j=0;i<rank;++i) { - while (j<PyArray_NDIM(arr) && PyArray_DIM(arr,j)<2) ++j; - if (j>=PyArray_NDIM(arr)) d = 1; - else d = PyArray_DIM(arr,j++); - if (dims[i]>=0) { - if (d>1 && d!=dims[i]) { - PyErr_Format(PyExc_ValueError, - "%d-th dimension must be fixed to %" - NPY_INTP_FMT " but got %" NPY_INTP_FMT - " (real index=%d)\n", - i, dims[i], d, j-1); + for (i = 0, j = 0; i < rank; ++i) { + while (j < PyArray_NDIM(arr) && PyArray_DIM(arr, j) < 2) ++j; + if (j >= PyArray_NDIM(arr)) + d = 1; + else + d = PyArray_DIM(arr, j++); + if (dims[i] >= 0) { + if (d > 1 && d != dims[i]) { + PyErr_Format( + PyExc_ValueError, + "%d-th dimension must be fixed to %" NPY_INTP_FMT + " but got %" NPY_INTP_FMT " (real index=%d)\n", + i, dims[i], d, j - 1); return 1; } - if (!dims[i]) dims[i] = 1; - } else + if (!dims[i]) + dims[i] = 1; + } + else dims[i] = d; } - for (i=rank;i<PyArray_NDIM(arr);++i) { /* [[1,2],[3,4]] -> [1,2,3,4] */ - while (j<PyArray_NDIM(arr) && PyArray_DIM(arr,j)<2) ++j; - if (j>=PyArray_NDIM(arr)) d = 1; - else d = PyArray_DIM(arr,j++); - dims[rank-1] *= d; + for (i = rank; i < PyArray_NDIM(arr); + ++i) { /* [[1,2],[3,4]] -> [1,2,3,4] */ + while (j < PyArray_NDIM(arr) && PyArray_DIM(arr, j) < 2) ++j; + if (j >= PyArray_NDIM(arr)) + d = 1; + else + d = PyArray_DIM(arr, j++); + dims[rank - 1] *= d; } - for (i=0,size=1;i<rank;++i) size *= dims[i]; + for (i = 0, size = 1; i < rank; ++i) size *= dims[i]; if (size != arr_size) { char msg[200]; int len; @@ -994,15 +1127,15 @@ int check_and_fix_dimensions(const PyArrayObject* arr, const int rank, npy_intp size, arr_size, rank, effrank, PyArray_NDIM(arr)); for (i = 0; i < rank; ++i) { len = strlen(msg); - snprintf(msg + len, sizeof(msg) - len, - " %" NPY_INTP_FMT, dims[i]); + snprintf(msg + len, sizeof(msg) - len, " %" NPY_INTP_FMT, + dims[i]); } len = strlen(msg); snprintf(msg + len, sizeof(msg) - len, " ], arr.dims=["); for (i = 0; i < PyArray_NDIM(arr); ++i) { len = strlen(msg); - snprintf(msg + len, sizeof(msg) - len, - " %" NPY_INTP_FMT, PyArray_DIM(arr, i)); + snprintf(msg + len, sizeof(msg) - len, " %" NPY_INTP_FMT, + PyArray_DIM(arr, i)); } len = strlen(msg); snprintf(msg + len, sizeof(msg) - len, " ]\n"); @@ -1012,7 +1145,7 @@ int check_and_fix_dimensions(const PyArrayObject* arr, const int rank, npy_intp } #ifdef DEBUG_COPY_ND_ARRAY printf("check_and_fix_dimensions:end: dims="); - dump_dims(rank,dims); + dump_dims(rank, dims); #endif return 0; } @@ -1021,8 +1154,8 @@ int check_and_fix_dimensions(const PyArrayObject* arr, const int rank, npy_intp /************************* copy_ND_array *******************************/ -extern -int copy_ND_array(const PyArrayObject *arr, PyArrayObject *out) +extern int +copy_ND_array(const PyArrayObject *arr, PyArrayObject *out) { F2PY_REPORT_ON_ARRAY_COPY_FROMARR; return PyArray_CopyInto(out, (PyArrayObject *)arr); @@ -1032,8 +1165,6 @@ int copy_ND_array(const PyArrayObject *arr, PyArrayObject *out) /* Compatibility functions for Python >= 3.0 */ /*********************************************/ -#if PY_VERSION_HEX >= 0x03000000 - PyObject * F2PyCapsule_FromVoidPtr(void *ptr, void (*dtor)(PyObject *)) { @@ -1060,29 +1191,6 @@ F2PyCapsule_Check(PyObject *ptr) return PyCapsule_CheckExact(ptr); } -#else - -PyObject * -F2PyCapsule_FromVoidPtr(void *ptr, void (*dtor)(void *)) -{ - return PyCObject_FromVoidPtr(ptr, dtor); -} - -void * -F2PyCapsule_AsVoidPtr(PyObject *ptr) -{ - return PyCObject_AsVoidPtr(ptr); -} - -int -F2PyCapsule_Check(PyObject *ptr) -{ - return PyCObject_Check(ptr); -} - -#endif - - #ifdef __cplusplus } #endif diff --git a/numpy/f2py/src/fortranobject.h b/numpy/f2py/src/fortranobject.h index 5d0dcf676337..a1e9fdbdfbac 100644 --- a/numpy/f2py/src/fortranobject.h +++ b/numpy/f2py/src/fortranobject.h @@ -4,50 +4,28 @@ extern "C" { #endif -#include "Python.h" +#include <Python.h> #ifdef FORTRANOBJECT_C #define NO_IMPORT_ARRAY #endif #define PY_ARRAY_UNIQUE_SYMBOL _npy_f2py_ARRAY_API #include "numpy/arrayobject.h" - -/* - * Python 3 support macros - */ -#if PY_VERSION_HEX >= 0x03000000 -#define PyString_Check PyBytes_Check -#define PyString_GET_SIZE PyBytes_GET_SIZE -#define PyString_AS_STRING PyBytes_AS_STRING -#define PyString_FromString PyBytes_FromString -#define PyUString_FromStringAndSize PyUnicode_FromStringAndSize -#define PyString_ConcatAndDel PyBytes_ConcatAndDel -#define PyString_AsString PyBytes_AsString - -#define PyInt_Check PyLong_Check -#define PyInt_FromLong PyLong_FromLong -#define PyInt_AS_LONG PyLong_AsLong -#define PyInt_AsLong PyLong_AsLong - -#define PyNumber_Int PyNumber_Long - -#else - -#define PyUString_FromStringAndSize PyString_FromStringAndSize -#endif - +#include "numpy/npy_3kcompat.h" #ifdef F2PY_REPORT_ATEXIT #include <sys/timeb.h> - extern void f2py_start_clock(void); - extern void f2py_stop_clock(void); - extern void f2py_start_call_clock(void); - extern void f2py_stop_call_clock(void); - extern void f2py_cb_start_clock(void); - extern void f2py_cb_stop_clock(void); - extern void f2py_cb_start_call_clock(void); - extern void f2py_cb_stop_call_clock(void); - extern void f2py_report_on_exit(int,void*); +// clang-format off +extern void f2py_start_clock(void); +extern void f2py_stop_clock(void); +extern void f2py_start_call_clock(void); +extern void f2py_stop_call_clock(void); +extern void f2py_cb_start_clock(void); +extern void f2py_cb_stop_clock(void); +extern void f2py_cb_start_call_clock(void); +extern void f2py_cb_stop_call_clock(void); +extern void f2py_report_on_exit(int, void *); +// clang-format on #endif #ifdef DMALLOC @@ -67,57 +45,60 @@ Author: Pearu Peterson <pearu@cens.ioc.ee> #define F2PY_MAX_DIMS 40 -typedef void (*f2py_set_data_func)(char*,npy_intp*); +typedef void (*f2py_set_data_func)(char *, npy_intp *); typedef void (*f2py_void_func)(void); -typedef void (*f2py_init_func)(int*,npy_intp*,f2py_set_data_func,int*); +typedef void (*f2py_init_func)(int *, npy_intp *, f2py_set_data_func, int *); - /*typedef void* (*f2py_c_func)(void*,...);*/ +/*typedef void* (*f2py_c_func)(void*,...);*/ typedef void *(*f2pycfunc)(void); typedef struct { - char *name; /* attribute (array||routine) name */ - int rank; /* array rank, 0 for scalar, max is F2PY_MAX_DIMS, - || rank=-1 for Fortran routine */ - struct {npy_intp d[F2PY_MAX_DIMS];} dims; /* dimensions of the array, || not used */ - int type; /* PyArray_<type> || not used */ - char *data; /* pointer to array || Fortran routine */ - f2py_init_func func; /* initialization function for - allocatable arrays: - func(&rank,dims,set_ptr_func,name,len(name)) - || C/API wrapper for Fortran routine */ - char *doc; /* documentation string; only recommended - for routines. */ + char *name; /* attribute (array||routine) name */ + int rank; /* array rank, 0 for scalar, max is F2PY_MAX_DIMS, + || rank=-1 for Fortran routine */ + struct { + npy_intp d[F2PY_MAX_DIMS]; + } dims; /* dimensions of the array, || not used */ + int type; /* PyArray_<type> || not used */ + char *data; /* pointer to array || Fortran routine */ + f2py_init_func func; /* initialization function for + allocatable arrays: + func(&rank,dims,set_ptr_func,name,len(name)) + || C/API wrapper for Fortran routine */ + char *doc; /* documentation string; only recommended + for routines. */ } FortranDataDef; typedef struct { - PyObject_HEAD - int len; /* Number of attributes */ - FortranDataDef *defs; /* An array of FortranDataDef's */ - PyObject *dict; /* Fortran object attribute dictionary */ + PyObject_HEAD + int len; /* Number of attributes */ + FortranDataDef *defs; /* An array of FortranDataDef's */ + PyObject *dict; /* Fortran object attribute dictionary */ } PyFortranObject; #define PyFortran_Check(op) (Py_TYPE(op) == &PyFortran_Type) -#define PyFortran_Check1(op) (0==strcmp(Py_TYPE(op)->tp_name,"fortran")) - - extern PyTypeObject PyFortran_Type; - extern int F2PyDict_SetItemString(PyObject* dict, char *name, PyObject *obj); - extern PyObject * PyFortranObject_New(FortranDataDef* defs, f2py_void_func init); - extern PyObject * PyFortranObject_NewAsAttr(FortranDataDef* defs); - -#if PY_VERSION_HEX >= 0x03000000 - -PyObject * F2PyCapsule_FromVoidPtr(void *ptr, void (*dtor)(PyObject *)); -void * F2PyCapsule_AsVoidPtr(PyObject *obj); -int F2PyCapsule_Check(PyObject *ptr); - -#else - -PyObject * F2PyCapsule_FromVoidPtr(void *ptr, void (*dtor)(void *)); -void * F2PyCapsule_AsVoidPtr(PyObject *ptr); -int F2PyCapsule_Check(PyObject *ptr); - -#endif +#define PyFortran_Check1(op) (0 == strcmp(Py_TYPE(op)->tp_name, "fortran")) + +extern PyTypeObject PyFortran_Type; +extern int +F2PyDict_SetItemString(PyObject *dict, char *name, PyObject *obj); +extern PyObject * +PyFortranObject_New(FortranDataDef *defs, f2py_void_func init); +extern PyObject * +PyFortranObject_NewAsAttr(FortranDataDef *defs); + +PyObject * +F2PyCapsule_FromVoidPtr(void *ptr, void (*dtor)(PyObject *)); +void * +F2PyCapsule_AsVoidPtr(PyObject *obj); +int +F2PyCapsule_Check(PyObject *ptr); + +extern void * +F2PySwapThreadLocalCallbackPtr(char *key, void *ptr); +extern void * +F2PyGetThreadLocalCallbackPtr(char *key); #define ISCONTIGUOUS(m) (PyArray_FLAGS(m) & NPY_ARRAY_C_CONTIGUOUS) #define F2PY_INTENT_IN 1 @@ -139,23 +120,23 @@ int F2PyCapsule_Check(PyObject *ptr); #define F2PY_ALIGN16(intent) (intent & F2PY_INTENT_ALIGNED16) #define F2PY_GET_ALIGNMENT(intent) \ - (F2PY_ALIGN4(intent) ? 4 : \ - (F2PY_ALIGN8(intent) ? 8 : \ - (F2PY_ALIGN16(intent) ? 16 : 1) )) -#define F2PY_CHECK_ALIGNMENT(arr, intent) ARRAY_ISALIGNED(arr, F2PY_GET_ALIGNMENT(intent)) - - extern PyArrayObject* array_from_pyobj(const int type_num, - npy_intp *dims, - const int rank, - const int intent, - PyObject *obj); - extern int copy_ND_array(const PyArrayObject *in, PyArrayObject *out); + (F2PY_ALIGN4(intent) \ + ? 4 \ + : (F2PY_ALIGN8(intent) ? 8 : (F2PY_ALIGN16(intent) ? 16 : 1))) +#define F2PY_CHECK_ALIGNMENT(arr, intent) \ + ARRAY_ISALIGNED(arr, F2PY_GET_ALIGNMENT(intent)) + +extern PyArrayObject * +array_from_pyobj(const int type_num, npy_intp *dims, const int rank, + const int intent, PyObject *obj); +extern int +copy_ND_array(const PyArrayObject *in, PyArrayObject *out); #ifdef DEBUG_COPY_ND_ARRAY - extern void dump_attrs(const PyArrayObject* arr); +extern void +dump_attrs(const PyArrayObject *arr); #endif - #ifdef __cplusplus } #endif diff --git a/numpy/f2py/src/test/Makefile b/numpy/f2py/src/test/Makefile deleted file mode 100644 index 0f8869f726f1..000000000000 --- a/numpy/f2py/src/test/Makefile +++ /dev/null @@ -1,96 +0,0 @@ -# -*- makefile -*- -# File: Makefile-foo -# Usage: -# make -f Makefile-foo [MODE=opt|debug] -# Notes: -# 1) You must use GNU make; try `gmake ..' if `make' fails. -# 2) This file is auto-generated with f2py (version 2.264). -# f2py is a Fortran to Python Interface Generator (FPIG), Second Edition, -# written by Pearu Peterson <pearu@ioc.ee>. -# See http://cens.ioc.ee/projects/f2py2e/ -# Generation date: Wed Sep 13 16:22:55 2000 -# $Revision: 1.2 $ -# $Date: 2000/09/17 16:10:27 $ - -# Recommendation notes produced by f2py2e/buildmakefile.py: -# *** - -PYINC = -I/numeric/include/python1.5/Numeric -I/numeric/include/python1.5 -INCLUDES = -I.. -LIBS = -L$(shell gcc -v 2>&1 | grep specs | sed -e 's/Reading specs from //g' | sed -e 's/\/specs//g') -lg2c -LIBS=-L$$ABSOFT/lib -lfio -lf77math -lf90math -LIBS=-L/numeric/bin -lvast90 -L/usr/lib/gcc-lib/i586-mandrake-linux/2.95.2 -lg2c - -# Wrapper generator: -F2PY = /home/pearu/bin/f2py-cvs - -# Fortran compiler: Absoft f95 -FC = f95 -FC = f90 -FOPT = -FDEBUG = -FFLAGS = -B108 -YCFRL=1 -YCOM_NAMES=LCS -YCOM_PFX -YCOM_SFX=_ -YEXT_PFX -YEXT_NAMES=LCS -FFLAGS = -# C compiler: cc ('gcc 2.x.x' 2.95.2) -CC = cc -COPT = -CDEBUG = -CFLAGS = -fpic - -# Linker: ld ('GNU ld' 2.9.5) -LD = ld -LDFLAGS = -shared -s -SO = .so - -ifeq '$(MODE)' 'debug' -FFLAGS += $(FDEBUG) -CFLAGS += $(CDEBUG) -endif -ifeq '$(MODE)' 'opt' -FFLAGS += $(FOPT) -CFLAGS += $(COPT) -endif -FFLAGS += $(INCLUDES) -CFLAGS += $(PYINC) $(INCLUDES) - -SRCC = ../fortranobject.c -SRCF = mod.f90 bar.f foo90.f90 wrap.f -SRCS = $(SRCC) $(SRCF) -OBJC = $(filter %.o,$(SRCC:.c=.o) $(SRCC:.cc=.o) $(SRCC:.C=.o)) -OBJF = $(filter %.o,$(SRCF:.f90=.o) $(SRCF:.f=.o) $(SRCF:.F=.o) $(SRCF:.for=.o)) -OBJS = $(OBJC) $(OBJF) - -INSTALLNAME = f2py2e-apps -INSTALLDIRECTORY = /numeric/lib/python1.5/site-packages/$(INSTALLNAME) -INSTALLDIR = install -d -c -INSTALLEXEC = install -m 755 -c - -all: foo - -foo: foomodule$(SO) -foomodule$(SO) : foomodule.o $(OBJS) - $(LD) $(LDFLAGS) -o $@ $< $(OBJS) $(LIBS) - -foomodule.o: foomodule.c - - -$(OBJS) : $(SRCS) -%.o : %.f ; $(FC) -c $(FFLAGS) $< -%.o : %.f90 ; $(FC) -c $(FFLAGS) $< - -test: foomodule$(SO) - python -c 'import foo;print foo.__doc__' -install: foomodule$(SO) - $(INSTALLDIR) $(INSTALLDIRECTORY) - $(INSTALLEXEC) foomodule$(SO) $(INSTALLDIRECTORY) - cd $(INSTALLDIRECTORY) && echo "$(INSTALLNAME)" > ../$(INSTALLNAME).pth - -.PHONY: clean distclean debug test install foo -debug: - echo "OBJS=$(OBJS)" - echo "SRCS=$(SRCS)" -clean: - $(RM) *.o *.mod core foomodule.{dvi,log} $(OBJS) -distclean: clean - $(RM) *.so *.sl foomodule.{tex,so} - $(RM) .f2py_get_compiler_* diff --git a/numpy/f2py/src/test/bar.f b/numpy/f2py/src/test/bar.f deleted file mode 100644 index 5354ceaf986b..000000000000 --- a/numpy/f2py/src/test/bar.f +++ /dev/null @@ -1,11 +0,0 @@ - subroutine bar() - integer a - real*8 b,c(3) - common /foodata/ a,b,c - a = 4 - b = 6.7 - c(2) = 3.0 - write(*,*) "bar:a=",a - write(*,*) "bar:b=",b - write(*,*) "bar:c=",c - end diff --git a/numpy/f2py/src/test/foo.f b/numpy/f2py/src/test/foo.f deleted file mode 100644 index 5354ceaf986b..000000000000 --- a/numpy/f2py/src/test/foo.f +++ /dev/null @@ -1,11 +0,0 @@ - subroutine bar() - integer a - real*8 b,c(3) - common /foodata/ a,b,c - a = 4 - b = 6.7 - c(2) = 3.0 - write(*,*) "bar:a=",a - write(*,*) "bar:b=",b - write(*,*) "bar:c=",c - end diff --git a/numpy/f2py/src/test/foo90.f90 b/numpy/f2py/src/test/foo90.f90 deleted file mode 100644 index dbca7e95ba88..000000000000 --- a/numpy/f2py/src/test/foo90.f90 +++ /dev/null @@ -1,13 +0,0 @@ -subroutine foo() - integer a - real*8 b,c(3) - common /foodata/ a,b,c - print*, " F: in foo" - a = 5 - b = 6.3 - c(2) = 9.1 -end subroutine foo - - - - diff --git a/numpy/f2py/src/test/foomodule.c b/numpy/f2py/src/test/foomodule.c deleted file mode 100644 index d7ecc251949d..000000000000 --- a/numpy/f2py/src/test/foomodule.c +++ /dev/null @@ -1,144 +0,0 @@ -/* File: foomodule.c - * Example of FortranObject usage. See also wrap.f foo.f foo90.f90. - * Author: Pearu Peterson <pearu@ioc.ee>. - * http://cens.ioc.ee/projects/f2py2e/ - * $Revision: 1.2 $ - * $Date: 2000/09/17 16:10:27 $ - */ -#ifdef __cplusplus -extern "C" { -#endif - -#include "Python.h" -#include "fortranobject.h" - -static PyObject *foo_error; - -#if defined(NO_APPEND_FORTRAN) -#if defined(UPPERCASE_FORTRAN) -#define F_FUNC(f,F) F -#else -#define F_FUNC(f,F) f -#endif -#else -#if defined(UPPERCASE_FORTRAN) -#define F_FUNC(f,F) F##_ -#else -#define F_FUNC(f,F) f##_ -#endif -#endif - -/************* foo_bar *************/ -static char doc_foo_bar[] = "\ -Function signature:\n\ - bar()\n\ -"; -static PyObject *foo_bar(PyObject *capi_self, PyObject *capi_args, - PyObject *capi_keywds, void (*f2py_func)()) { - PyObject *capi_buildvalue = NULL; - static char *capi_kwlist[] = {NULL}; - if (!PyArg_ParseTupleAndKeywords(capi_args,capi_keywds,\ - "|:foo.bar",\ - capi_kwlist)) - goto capi_fail; - (*f2py_func)(); - capi_buildvalue = Py_BuildValue(""); - capi_fail: - return capi_buildvalue; -} -/************ mod_init **************/ -static PyObject *mod_init(PyObject *capi_self, PyObject *capi_args, - PyObject *capi_keywds, void (*f2py_func)()) { - PyObject *capi_buildvalue = NULL; - static char *capi_kwlist[] = {NULL}; - if (!PyArg_ParseTupleAndKeywords(capi_args,capi_keywds,\ - "|:mod.init",\ - capi_kwlist)) - goto capi_fail; - (*f2py_func)(); - capi_buildvalue = Py_BuildValue(""); - capi_fail: - return capi_buildvalue; -} - -/* F90 module */ -static FortranDataDef f2py_mod_def[] = { - {"a",0, {}, NPY_INT}, - {"b",0, {}, NPY_DOUBLE}, - {"c",1, {3}, NPY_DOUBLE}, - {"d",1, {-1}, NPY_DOUBLE}, - {"init",-1,{},0,NULL,(void *)mod_init}, - {NULL} -}; -static void f2py_setup_mod(char *a,char *b,char *c,void (*d)(),char *init) { - f2py_mod_def[0].data = a; - f2py_mod_def[1].data = b; - f2py_mod_def[2].data = c; - f2py_mod_def[3].func = d; - f2py_mod_def[4].data = init; -} -extern void F_FUNC(f2pyinitmod,F2PYINITMOD)(); - static void f2py_init_mod() { - F_FUNC(f2pyinitmod,F2PYINITMOD)(f2py_setup_mod); - } - -/* COMMON block */ -static FortranDataDef f2py_foodata_def[] = { - {"a",0, {}, NPY_INT}, - {"b",0, {}, NPY_DOUBLE}, - {"c",1, {3}, NPY_DOUBLE}, - {NULL} -}; -static void f2py_setup_foodata(char *a,char *b,char *c) { - f2py_foodata_def[0].data = a; - f2py_foodata_def[1].data = b; - f2py_foodata_def[2].data = c; -} -extern void F_FUNC(f2pyinitfoodata,F2PYINITFOODATA)(); - static void f2py_init_foodata() { - F_FUNC(f2pyinitfoodata,F2PYINITFOODATA)(f2py_setup_foodata); - } - -/* Fortran routines (needs no initialization/setup function) */ -extern void F_FUNC(bar,BAR)(); - extern void F_FUNC(foo,FOO)(); - static FortranDataDef f2py_routines_def[] = { - {"bar",-1, {}, 0, (char *)F_FUNC(bar,BAR),(void *)foo_bar,doc_foo_bar}, - {"foo",-1, {}, 0, (char *)F_FUNC(foo,FOO),(void *)foo_bar,doc_foo_bar}, - {NULL} - }; - -static PyMethodDef foo_module_methods[] = { - /*eof method*/ - {NULL,NULL} -}; - -void initfoo() { - int i; - PyObject *m, *d, *s; - PyTypeObject *t; - PyObject *f; - import_array(); - - m = Py_InitModule("foo", foo_module_methods); - - d = PyModule_GetDict(m); - s = PyString_FromString("This module 'foo' demonstrates the usage of fortranobject."); - PyDict_SetItemString(d, "__doc__", s); - - /* Fortran objects: */ - PyDict_SetItemString(d, "mod", PyFortranObject_New(f2py_mod_def,f2py_init_mod)); - PyDict_SetItemString(d, "foodata", PyFortranObject_New(f2py_foodata_def,f2py_init_foodata)); - for(i=0;f2py_routines_def[i].name!=NULL;i++) - PyDict_SetItemString(d, f2py_routines_def[i].name, - PyFortranObject_NewAsAttr(&f2py_routines_def[i])); - - Py_DECREF(s); - - if (PyErr_Occurred()) - Py_FatalError("can't initialize module foo"); -} - -#ifdef __cplusplus -} -#endif diff --git a/numpy/f2py/src/test/wrap.f b/numpy/f2py/src/test/wrap.f deleted file mode 100644 index 9414eb9f6f8e..000000000000 --- a/numpy/f2py/src/test/wrap.f +++ /dev/null @@ -1,70 +0,0 @@ - subroutine f2py_mod_get_dims(f2py_r,f2py_s,f2py_set,f2py_n) - use mod - external f2py_set - logical f2py_ns - integer f2py_s(*),f2py_r,f2py_i,f2py_j - character*(*) f2py_n - if ("d".eq.f2py_n) then - f2py_ns = .FALSE. - if (allocated(d)) then - do f2py_i=1,f2py_r - if ((size(d,f2py_r-f2py_i+1).ne.f2py_s(f2py_i)).and. - c (f2py_s(f2py_i).ge.0)) then - f2py_ns = .TRUE. - end if - end do - if (f2py_ns) then - deallocate(d) - end if - end if - if (.not.allocated(d)) then - allocate(d(f2py_s(1))) - end if - if (allocated(d)) then - do f2py_i=1,f2py_r - f2py_s(f2py_i) = size(d,f2py_r-f2py_i+1) - end do - call f2py_set(d) - end if - end if - end subroutine f2py_mod_get_dims - subroutine f2py_mod_get_dims_d(r,s,set_data) - use mod, only: d => d - external set_data - logical ns - integer s(*),r,i,j - ns = .FALSE. - if (allocated(d)) then - do i=1,r - if ((size(d,r-i+1).ne.s(i)).and.(s(i).ge.0)) then - ns = .TRUE. - end if - end do - if (ns) then - deallocate(d) - end if - end if - if (.not.allocated(d).and.(s(1).ge.1)) then - allocate(d(s(1))) - end if - if (allocated(d)) then - do i=1,r - s(i) = size(d,r-i+1) - end do - end if - call set_data(d,allocated(d)) - end subroutine f2py_mod_get_dims_d - - subroutine f2pyinitmod(setupfunc) - use mod - external setupfunc,f2py_mod_get_dims_d,init - call setupfunc(a,b,c,f2py_mod_get_dims_d,init) - end subroutine f2pyinitmod - - subroutine f2pyinitfoodata(setupfunc) - external setupfunc - integer a - real*8 b,c(3) - common /foodata/ a,b,c - call setupfunc(a,b,c) - end subroutine f2pyinitfoodata diff --git a/numpy/f2py/symbolic.py b/numpy/f2py/symbolic.py new file mode 100644 index 000000000000..1b7b354583a3 --- /dev/null +++ b/numpy/f2py/symbolic.py @@ -0,0 +1,1510 @@ +"""Fortran/C symbolic expressions + +References: +- J3/21-007: Draft Fortran 202x. https://j3-fortran.org/doc/year/21/21-007.pdf +""" + +# To analyze Fortran expressions to solve dimensions specifications, +# for instances, we implement a minimal symbolic engine for parsing +# expressions into a tree of expression instances. As a first +# instance, we care only about arithmetic expressions involving +# integers and operations like addition (+), subtraction (-), +# multiplication (*), division (Fortran / is Python //, Fortran // is +# concatenate), and exponentiation (**). In addition, .pyf files may +# contain C expressions that support here is implemented as well. +# +# TODO: support logical constants (Op.BOOLEAN) +# TODO: support logical operators (.AND., ...) +# TODO: support defined operators (.MYOP., ...) +# +__all__ = ['Expr'] + + +import re +import warnings +from enum import Enum +from math import gcd + + +class Language(Enum): + """ + Used as Expr.tostring language argument. + """ + Python = 0 + Fortran = 1 + C = 2 + + +class Op(Enum): + """ + Used as Expr op attribute. + """ + INTEGER = 10 + REAL = 12 + COMPLEX = 15 + STRING = 20 + ARRAY = 30 + SYMBOL = 40 + TERNARY = 100 + APPLY = 200 + INDEXING = 210 + CONCAT = 220 + RELATIONAL = 300 + TERMS = 1000 + FACTORS = 2000 + REF = 3000 + DEREF = 3001 + + +class RelOp(Enum): + """ + Used in Op.RELATIONAL expression to specify the function part. + """ + EQ = 1 + NE = 2 + LT = 3 + LE = 4 + GT = 5 + GE = 6 + + @classmethod + def fromstring(cls, s, language=Language.C): + if language is Language.Fortran: + return {'.eq.': RelOp.EQ, '.ne.': RelOp.NE, + '.lt.': RelOp.LT, '.le.': RelOp.LE, + '.gt.': RelOp.GT, '.ge.': RelOp.GE}[s.lower()] + return {'==': RelOp.EQ, '!=': RelOp.NE, '<': RelOp.LT, + '<=': RelOp.LE, '>': RelOp.GT, '>=': RelOp.GE}[s] + + def tostring(self, language=Language.C): + if language is Language.Fortran: + return {RelOp.EQ: '.eq.', RelOp.NE: '.ne.', + RelOp.LT: '.lt.', RelOp.LE: '.le.', + RelOp.GT: '.gt.', RelOp.GE: '.ge.'}[self] + return {RelOp.EQ: '==', RelOp.NE: '!=', + RelOp.LT: '<', RelOp.LE: '<=', + RelOp.GT: '>', RelOp.GE: '>='}[self] + + +class ArithOp(Enum): + """ + Used in Op.APPLY expression to specify the function part. + """ + POS = 1 + NEG = 2 + ADD = 3 + SUB = 4 + MUL = 5 + DIV = 6 + POW = 7 + + +class OpError(Exception): + pass + + +class Precedence(Enum): + """ + Used as Expr.tostring precedence argument. + """ + ATOM = 0 + POWER = 1 + UNARY = 2 + PRODUCT = 3 + SUM = 4 + LT = 6 + EQ = 7 + LAND = 11 + LOR = 12 + TERNARY = 13 + ASSIGN = 14 + TUPLE = 15 + NONE = 100 + + +integer_types = (int,) +number_types = (int, float) + + +def _pairs_add(d, k, v): + # Internal utility method for updating terms and factors data. + c = d.get(k) + if c is None: + d[k] = v + else: + c = c + v + if c: + d[k] = c + else: + del d[k] + + +class ExprWarning(UserWarning): + pass + + +def ewarn(message): + warnings.warn(message, ExprWarning, stacklevel=2) + + +class Expr: + """Represents a Fortran expression as a op-data pair. + + Expr instances are hashable and sortable. + """ + + @staticmethod + def parse(s, language=Language.C): + """Parse a Fortran expression to a Expr. + """ + return fromstring(s, language=language) + + def __init__(self, op, data): + assert isinstance(op, Op) + + # sanity checks + if op is Op.INTEGER: + # data is a 2-tuple of numeric object and a kind value + # (default is 4) + assert isinstance(data, tuple) and len(data) == 2 + assert isinstance(data[0], int) + assert isinstance(data[1], (int, str)), data + elif op is Op.REAL: + # data is a 2-tuple of numeric object and a kind value + # (default is 4) + assert isinstance(data, tuple) and len(data) == 2 + assert isinstance(data[0], float) + assert isinstance(data[1], (int, str)), data + elif op is Op.COMPLEX: + # data is a 2-tuple of constant expressions + assert isinstance(data, tuple) and len(data) == 2 + elif op is Op.STRING: + # data is a 2-tuple of quoted string and a kind value + # (default is 1) + assert isinstance(data, tuple) and len(data) == 2 + assert (isinstance(data[0], str) + and data[0][::len(data[0])-1] in ('""', "''", '@@')) + assert isinstance(data[1], (int, str)), data + elif op is Op.SYMBOL: + # data is any hashable object + assert hash(data) is not None + elif op in (Op.ARRAY, Op.CONCAT): + # data is a tuple of expressions + assert isinstance(data, tuple) + assert all(isinstance(item, Expr) for item in data), data + elif op in (Op.TERMS, Op.FACTORS): + # data is {<term|base>:<coeff|exponent>} where dict values + # are nonzero Python integers + assert isinstance(data, dict) + elif op is Op.APPLY: + # data is (<function>, <operands>, <kwoperands>) where + # operands are Expr instances + assert isinstance(data, tuple) and len(data) == 3 + # function is any hashable object + assert hash(data[0]) is not None + assert isinstance(data[1], tuple) + assert isinstance(data[2], dict) + elif op is Op.INDEXING: + # data is (<object>, <indices>) + assert isinstance(data, tuple) and len(data) == 2 + # function is any hashable object + assert hash(data[0]) is not None + elif op is Op.TERNARY: + # data is (<cond>, <expr1>, <expr2>) + assert isinstance(data, tuple) and len(data) == 3 + elif op in (Op.REF, Op.DEREF): + # data is Expr instance + assert isinstance(data, Expr) + elif op is Op.RELATIONAL: + # data is (<relop>, <left>, <right>) + assert isinstance(data, tuple) and len(data) == 3 + else: + raise NotImplementedError( + f'unknown op or missing sanity check: {op}') + + self.op = op + self.data = data + + def __eq__(self, other): + return (isinstance(other, Expr) + and self.op is other.op + and self.data == other.data) + + def __hash__(self): + if self.op in (Op.TERMS, Op.FACTORS): + data = tuple(sorted(self.data.items())) + elif self.op is Op.APPLY: + data = self.data[:2] + tuple(sorted(self.data[2].items())) + else: + data = self.data + return hash((self.op, data)) + + def __lt__(self, other): + if isinstance(other, Expr): + if self.op is not other.op: + return self.op.value < other.op.value + if self.op in (Op.TERMS, Op.FACTORS): + return (tuple(sorted(self.data.items())) + < tuple(sorted(other.data.items()))) + if self.op is Op.APPLY: + if self.data[:2] != other.data[:2]: + return self.data[:2] < other.data[:2] + return tuple(sorted(self.data[2].items())) < tuple( + sorted(other.data[2].items())) + return self.data < other.data + return NotImplemented + + def __le__(self, other): return self == other or self < other + + def __gt__(self, other): return not (self <= other) + + def __ge__(self, other): return not (self < other) + + def __repr__(self): + return f'{type(self).__name__}({self.op}, {self.data!r})' + + def __str__(self): + return self.tostring() + + def tostring(self, parent_precedence=Precedence.NONE, + language=Language.Fortran): + """Return a string representation of Expr. + """ + if self.op in (Op.INTEGER, Op.REAL): + precedence = (Precedence.SUM if self.data[0] < 0 + else Precedence.ATOM) + r = str(self.data[0]) + (f'_{self.data[1]}' + if self.data[1] != 4 else '') + elif self.op is Op.COMPLEX: + r = ', '.join(item.tostring(Precedence.TUPLE, language=language) + for item in self.data) + r = '(' + r + ')' + precedence = Precedence.ATOM + elif self.op is Op.SYMBOL: + precedence = Precedence.ATOM + r = str(self.data) + elif self.op is Op.STRING: + r = self.data[0] + if self.data[1] != 1: + r = self.data[1] + '_' + r + precedence = Precedence.ATOM + elif self.op is Op.ARRAY: + r = ', '.join(item.tostring(Precedence.TUPLE, language=language) + for item in self.data) + r = '[' + r + ']' + precedence = Precedence.ATOM + elif self.op is Op.TERMS: + terms = [] + for term, coeff in sorted(self.data.items()): + if coeff < 0: + op = ' - ' + coeff = -coeff + else: + op = ' + ' + if coeff == 1: + term = term.tostring(Precedence.SUM, language=language) + else: + if term == as_number(1): + term = str(coeff) + else: + term = f'{coeff} * ' + term.tostring( + Precedence.PRODUCT, language=language) + if terms: + terms.append(op) + elif op == ' - ': + terms.append('-') + terms.append(term) + r = ''.join(terms) or '0' + precedence = Precedence.SUM if terms else Precedence.ATOM + elif self.op is Op.FACTORS: + factors = [] + tail = [] + for base, exp in sorted(self.data.items()): + op = ' * ' + if exp == 1: + factor = base.tostring(Precedence.PRODUCT, + language=language) + elif language is Language.C: + if exp in range(2, 10): + factor = base.tostring(Precedence.PRODUCT, + language=language) + factor = ' * '.join([factor] * exp) + elif exp in range(-10, 0): + factor = base.tostring(Precedence.PRODUCT, + language=language) + tail += [factor] * -exp + continue + else: + factor = base.tostring(Precedence.TUPLE, + language=language) + factor = f'pow({factor}, {exp})' + else: + factor = base.tostring(Precedence.POWER, + language=language) + f' ** {exp}' + if factors: + factors.append(op) + factors.append(factor) + if tail: + if not factors: + factors += ['1'] + factors += ['/', '(', ' * '.join(tail), ')'] + r = ''.join(factors) or '1' + precedence = Precedence.PRODUCT if factors else Precedence.ATOM + elif self.op is Op.APPLY: + name, args, kwargs = self.data + if name is ArithOp.DIV and language is Language.C: + numer, denom = [arg.tostring(Precedence.PRODUCT, + language=language) + for arg in args] + r = f'{numer} / {denom}' + precedence = Precedence.PRODUCT + else: + args = [arg.tostring(Precedence.TUPLE, language=language) + for arg in args] + args += [k + '=' + v.tostring(Precedence.NONE) + for k, v in kwargs.items()] + r = f'{name}({", ".join(args)})' + precedence = Precedence.ATOM + elif self.op is Op.INDEXING: + name = self.data[0] + args = [arg.tostring(Precedence.TUPLE, language=language) + for arg in self.data[1:]] + r = f'{name}[{", ".join(args)}]' + precedence = Precedence.ATOM + elif self.op is Op.CONCAT: + args = [arg.tostring(Precedence.PRODUCT, language=language) + for arg in self.data] + r = " // ".join(args) + precedence = Precedence.PRODUCT + elif self.op is Op.TERNARY: + cond, expr1, expr2 = [a.tostring(Precedence.TUPLE, + language=language) + for a in self.data] + if language is Language.C: + r = f'({cond} ? {expr1} : {expr2})' + elif language is Language.Python: + r = f'({expr1} if {cond} else {expr2})' + elif language is Language.Fortran: + r = f'merge({expr1}, {expr2}, {cond})' + else: + raise NotImplementedError( + f'tostring for {self.op} and {language}') + precedence = Precedence.ATOM + elif self.op is Op.REF: + r = '&' + self.data.tostring(Precedence.UNARY, language=language) + precedence = Precedence.UNARY + elif self.op is Op.DEREF: + r = '*' + self.data.tostring(Precedence.UNARY, language=language) + precedence = Precedence.UNARY + elif self.op is Op.RELATIONAL: + rop, left, right = self.data + precedence = (Precedence.EQ if rop in (RelOp.EQ, RelOp.NE) + else Precedence.LT) + left = left.tostring(precedence, language=language) + right = right.tostring(precedence, language=language) + rop = rop.tostring(language=language) + r = f'{left} {rop} {right}' + else: + raise NotImplementedError(f'tostring for op {self.op}') + if parent_precedence.value < precedence.value: + # If parent precedence is higher than operand precedence, + # operand will be enclosed in parenthesis. + return '(' + r + ')' + return r + + def __pos__(self): + return self + + def __neg__(self): + return self * -1 + + def __add__(self, other): + other = as_expr(other) + if isinstance(other, Expr): + if self.op is other.op: + if self.op in (Op.INTEGER, Op.REAL): + return as_number( + self.data[0] + other.data[0], + max(self.data[1], other.data[1])) + if self.op is Op.COMPLEX: + r1, i1 = self.data + r2, i2 = other.data + return as_complex(r1 + r2, i1 + i2) + if self.op is Op.TERMS: + r = Expr(self.op, dict(self.data)) + for k, v in other.data.items(): + _pairs_add(r.data, k, v) + return normalize(r) + if self.op is Op.COMPLEX and other.op in (Op.INTEGER, Op.REAL): + return self + as_complex(other) + elif self.op in (Op.INTEGER, Op.REAL) and other.op is Op.COMPLEX: + return as_complex(self) + other + elif self.op is Op.REAL and other.op is Op.INTEGER: + return self + as_real(other, kind=self.data[1]) + elif self.op is Op.INTEGER and other.op is Op.REAL: + return as_real(self, kind=other.data[1]) + other + return as_terms(self) + as_terms(other) + return NotImplemented + + def __radd__(self, other): + if isinstance(other, number_types): + return as_number(other) + self + return NotImplemented + + def __sub__(self, other): + return self + (-other) + + def __rsub__(self, other): + if isinstance(other, number_types): + return as_number(other) - self + return NotImplemented + + def __mul__(self, other): + other = as_expr(other) + if isinstance(other, Expr): + if self.op is other.op: + if self.op in (Op.INTEGER, Op.REAL): + return as_number(self.data[0] * other.data[0], + max(self.data[1], other.data[1])) + elif self.op is Op.COMPLEX: + r1, i1 = self.data + r2, i2 = other.data + return as_complex(r1 * r2 - i1 * i2, r1 * i2 + r2 * i1) + + if self.op is Op.FACTORS: + r = Expr(self.op, dict(self.data)) + for k, v in other.data.items(): + _pairs_add(r.data, k, v) + return normalize(r) + elif self.op is Op.TERMS: + r = Expr(self.op, {}) + for t1, c1 in self.data.items(): + for t2, c2 in other.data.items(): + _pairs_add(r.data, t1 * t2, c1 * c2) + return normalize(r) + + if self.op is Op.COMPLEX and other.op in (Op.INTEGER, Op.REAL): + return self * as_complex(other) + elif other.op is Op.COMPLEX and self.op in (Op.INTEGER, Op.REAL): + return as_complex(self) * other + elif self.op is Op.REAL and other.op is Op.INTEGER: + return self * as_real(other, kind=self.data[1]) + elif self.op is Op.INTEGER and other.op is Op.REAL: + return as_real(self, kind=other.data[1]) * other + + if self.op is Op.TERMS: + return self * as_terms(other) + elif other.op is Op.TERMS: + return as_terms(self) * other + + return as_factors(self) * as_factors(other) + return NotImplemented + + def __rmul__(self, other): + if isinstance(other, number_types): + return as_number(other) * self + return NotImplemented + + def __pow__(self, other): + other = as_expr(other) + if isinstance(other, Expr): + if other.op is Op.INTEGER: + exponent = other.data[0] + # TODO: other kind not used + if exponent == 0: + return as_number(1) + if exponent == 1: + return self + if exponent > 0: + if self.op is Op.FACTORS: + r = Expr(self.op, {}) + for k, v in self.data.items(): + r.data[k] = v * exponent + return normalize(r) + return self * (self ** (exponent - 1)) + elif exponent != -1: + return (self ** (-exponent)) ** -1 + return Expr(Op.FACTORS, {self: exponent}) + return as_apply(ArithOp.POW, self, other) + return NotImplemented + + def __truediv__(self, other): + other = as_expr(other) + if isinstance(other, Expr): + # Fortran / is different from Python /: + # - `/` is a truncate operation for integer operands + return normalize(as_apply(ArithOp.DIV, self, other)) + return NotImplemented + + def __rtruediv__(self, other): + other = as_expr(other) + if isinstance(other, Expr): + return other / self + return NotImplemented + + def __floordiv__(self, other): + other = as_expr(other) + if isinstance(other, Expr): + # Fortran // is different from Python //: + # - `//` is a concatenate operation for string operands + return normalize(Expr(Op.CONCAT, (self, other))) + return NotImplemented + + def __rfloordiv__(self, other): + other = as_expr(other) + if isinstance(other, Expr): + return other // self + return NotImplemented + + def __call__(self, *args, **kwargs): + # In Fortran, parenthesis () are use for both function call as + # well as indexing operations. + # + # TODO: implement a method for deciding when __call__ should + # return an INDEXING expression. + return as_apply(self, *map(as_expr, args), + **dict((k, as_expr(v)) for k, v in kwargs.items())) + + def __getitem__(self, index): + # Provided to support C indexing operations that .pyf files + # may contain. + index = as_expr(index) + if not isinstance(index, tuple): + index = index, + if len(index) > 1: + ewarn(f'C-index should be a single expression but got `{index}`') + return Expr(Op.INDEXING, (self,) + index) + + def substitute(self, symbols_map): + """Recursively substitute symbols with values in symbols map. + + Symbols map is a dictionary of symbol-expression pairs. + """ + if self.op is Op.SYMBOL: + value = symbols_map.get(self) + if value is None: + return self + m = re.match(r'\A(@__f2py_PARENTHESIS_(\w+)_\d+@)\Z', self.data) + if m: + # complement to fromstring method + items, paren = m.groups() + if paren in ['ROUNDDIV', 'SQUARE']: + return as_array(value) + assert paren == 'ROUND', (paren, value) + return value + if self.op in (Op.INTEGER, Op.REAL, Op.STRING): + return self + if self.op in (Op.ARRAY, Op.COMPLEX): + return Expr(self.op, tuple(item.substitute(symbols_map) + for item in self.data)) + if self.op is Op.CONCAT: + return normalize(Expr(self.op, tuple(item.substitute(symbols_map) + for item in self.data))) + if self.op is Op.TERMS: + r = None + for term, coeff in self.data.items(): + if r is None: + r = term.substitute(symbols_map) * coeff + else: + r += term.substitute(symbols_map) * coeff + if r is None: + ewarn('substitute: empty TERMS expression interpreted as' + ' int-literal 0') + return as_number(0) + return r + if self.op is Op.FACTORS: + r = None + for base, exponent in self.data.items(): + if r is None: + r = base.substitute(symbols_map) ** exponent + else: + r *= base.substitute(symbols_map) ** exponent + if r is None: + ewarn('substitute: empty FACTORS expression interpreted' + ' as int-literal 1') + return as_number(1) + return r + if self.op is Op.APPLY: + target, args, kwargs = self.data + if isinstance(target, Expr): + target = target.substitute(symbols_map) + args = tuple(a.substitute(symbols_map) for a in args) + kwargs = dict((k, v.substitute(symbols_map)) + for k, v in kwargs.items()) + return normalize(Expr(self.op, (target, args, kwargs))) + if self.op is Op.INDEXING: + func = self.data[0] + if isinstance(func, Expr): + func = func.substitute(symbols_map) + args = tuple(a.substitute(symbols_map) for a in self.data[1:]) + return normalize(Expr(self.op, (func,) + args)) + if self.op is Op.TERNARY: + operands = tuple(a.substitute(symbols_map) for a in self.data) + return normalize(Expr(self.op, operands)) + if self.op in (Op.REF, Op.DEREF): + return normalize(Expr(self.op, self.data.substitute(symbols_map))) + if self.op is Op.RELATIONAL: + rop, left, right = self.data + left = left.substitute(symbols_map) + right = right.substitute(symbols_map) + return normalize(Expr(self.op, (rop, left, right))) + raise NotImplementedError(f'substitute method for {self.op}: {self!r}') + + def traverse(self, visit, *args, **kwargs): + """Traverse expression tree with visit function. + + The visit function is applied to an expression with given args + and kwargs. + + Traverse call returns an expression returned by visit when not + None, otherwise return a new normalized expression with + traverse-visit sub-expressions. + """ + result = visit(self, *args, **kwargs) + if result is not None: + return result + + if self.op in (Op.INTEGER, Op.REAL, Op.STRING, Op.SYMBOL): + return self + elif self.op in (Op.COMPLEX, Op.ARRAY, Op.CONCAT, Op.TERNARY): + return normalize(Expr(self.op, tuple( + item.traverse(visit, *args, **kwargs) + for item in self.data))) + elif self.op in (Op.TERMS, Op.FACTORS): + data = {} + for k, v in self.data.items(): + k = k.traverse(visit, *args, **kwargs) + v = (v.traverse(visit, *args, **kwargs) + if isinstance(v, Expr) else v) + if k in data: + v = data[k] + v + data[k] = v + return normalize(Expr(self.op, data)) + elif self.op is Op.APPLY: + obj = self.data[0] + func = (obj.traverse(visit, *args, **kwargs) + if isinstance(obj, Expr) else obj) + operands = tuple(operand.traverse(visit, *args, **kwargs) + for operand in self.data[1]) + kwoperands = dict((k, v.traverse(visit, *args, **kwargs)) + for k, v in self.data[2].items()) + return normalize(Expr(self.op, (func, operands, kwoperands))) + elif self.op is Op.INDEXING: + obj = self.data[0] + obj = (obj.traverse(visit, *args, **kwargs) + if isinstance(obj, Expr) else obj) + indices = tuple(index.traverse(visit, *args, **kwargs) + for index in self.data[1:]) + return normalize(Expr(self.op, (obj,) + indices)) + elif self.op in (Op.REF, Op.DEREF): + return normalize(Expr(self.op, + self.data.traverse(visit, *args, **kwargs))) + elif self.op is Op.RELATIONAL: + rop, left, right = self.data + left = left.traverse(visit, *args, **kwargs) + right = right.traverse(visit, *args, **kwargs) + return normalize(Expr(self.op, (rop, left, right))) + raise NotImplementedError(f'traverse method for {self.op}') + + def contains(self, other): + """Check if self contains other. + """ + found = [] + + def visit(expr, found=found): + if found: + return expr + elif expr == other: + found.append(1) + return expr + + self.traverse(visit) + + return len(found) != 0 + + def symbols(self): + """Return a set of symbols contained in self. + """ + found = set() + + def visit(expr, found=found): + if expr.op is Op.SYMBOL: + found.add(expr) + + self.traverse(visit) + + return found + + def polynomial_atoms(self): + """Return a set of expressions used as atoms in polynomial self. + """ + found = set() + + def visit(expr, found=found): + if expr.op is Op.FACTORS: + for b in expr.data: + b.traverse(visit) + return expr + if expr.op in (Op.TERMS, Op.COMPLEX): + return + if expr.op is Op.APPLY and isinstance(expr.data[0], ArithOp): + if expr.data[0] is ArithOp.POW: + expr.data[1][0].traverse(visit) + return expr + return + if expr.op in (Op.INTEGER, Op.REAL): + return expr + + found.add(expr) + + if expr.op in (Op.INDEXING, Op.APPLY): + return expr + + self.traverse(visit) + + return found + + def linear_solve(self, symbol): + """Return a, b such that a * symbol + b == self. + + If self is not linear with respect to symbol, raise RuntimeError. + """ + b = self.substitute({symbol: as_number(0)}) + ax = self - b + a = ax.substitute({symbol: as_number(1)}) + + zero, _ = as_numer_denom(a * symbol - ax) + + if zero != as_number(0): + raise RuntimeError(f'not a {symbol}-linear equation:' + f' {a} * {symbol} + {b} == {self}') + return a, b + + +def normalize(obj): + """Normalize Expr and apply basic evaluation methods. + """ + if not isinstance(obj, Expr): + return obj + + if obj.op is Op.TERMS: + d = {} + for t, c in obj.data.items(): + if c == 0: + continue + if t.op is Op.COMPLEX and c != 1: + t = t * c + c = 1 + if t.op is Op.TERMS: + for t1, c1 in t.data.items(): + _pairs_add(d, t1, c1 * c) + else: + _pairs_add(d, t, c) + if len(d) == 0: + # TODO: deterimine correct kind + return as_number(0) + elif len(d) == 1: + (t, c), = d.items() + if c == 1: + return t + return Expr(Op.TERMS, d) + + if obj.op is Op.FACTORS: + coeff = 1 + d = {} + for b, e in obj.data.items(): + if e == 0: + continue + if b.op is Op.TERMS and isinstance(e, integer_types) and e > 1: + # expand integer powers of sums + b = b * (b ** (e - 1)) + e = 1 + + if b.op in (Op.INTEGER, Op.REAL): + if e == 1: + coeff *= b.data[0] + elif e > 0: + coeff *= b.data[0] ** e + else: + _pairs_add(d, b, e) + elif b.op is Op.FACTORS: + if e > 0 and isinstance(e, integer_types): + for b1, e1 in b.data.items(): + _pairs_add(d, b1, e1 * e) + else: + _pairs_add(d, b, e) + else: + _pairs_add(d, b, e) + if len(d) == 0 or coeff == 0: + # TODO: deterimine correct kind + assert isinstance(coeff, number_types) + return as_number(coeff) + elif len(d) == 1: + (b, e), = d.items() + if e == 1: + t = b + else: + t = Expr(Op.FACTORS, d) + if coeff == 1: + return t + return Expr(Op.TERMS, {t: coeff}) + elif coeff == 1: + return Expr(Op.FACTORS, d) + else: + return Expr(Op.TERMS, {Expr(Op.FACTORS, d): coeff}) + + if obj.op is Op.APPLY and obj.data[0] is ArithOp.DIV: + dividend, divisor = obj.data[1] + t1, c1 = as_term_coeff(dividend) + t2, c2 = as_term_coeff(divisor) + if isinstance(c1, integer_types) and isinstance(c2, integer_types): + g = gcd(c1, c2) + c1, c2 = c1//g, c2//g + else: + c1, c2 = c1/c2, 1 + + if t1.op is Op.APPLY and t1.data[0] is ArithOp.DIV: + numer = t1.data[1][0] * c1 + denom = t1.data[1][1] * t2 * c2 + return as_apply(ArithOp.DIV, numer, denom) + + if t2.op is Op.APPLY and t2.data[0] is ArithOp.DIV: + numer = t2.data[1][1] * t1 * c1 + denom = t2.data[1][0] * c2 + return as_apply(ArithOp.DIV, numer, denom) + + d = dict(as_factors(t1).data) + for b, e in as_factors(t2).data.items(): + _pairs_add(d, b, -e) + numer, denom = {}, {} + for b, e in d.items(): + if e > 0: + numer[b] = e + else: + denom[b] = -e + numer = normalize(Expr(Op.FACTORS, numer)) * c1 + denom = normalize(Expr(Op.FACTORS, denom)) * c2 + + if denom.op in (Op.INTEGER, Op.REAL) and denom.data[0] == 1: + # TODO: denom kind not used + return numer + return as_apply(ArithOp.DIV, numer, denom) + + if obj.op is Op.CONCAT: + lst = [obj.data[0]] + for s in obj.data[1:]: + last = lst[-1] + if ( + last.op is Op.STRING + and s.op is Op.STRING + and last.data[0][0] in '"\'' + and s.data[0][0] == last.data[0][-1] + ): + new_last = as_string(last.data[0][:-1] + s.data[0][1:], + max(last.data[1], s.data[1])) + lst[-1] = new_last + else: + lst.append(s) + if len(lst) == 1: + return lst[0] + return Expr(Op.CONCAT, tuple(lst)) + + if obj.op is Op.TERNARY: + cond, expr1, expr2 = map(normalize, obj.data) + if cond.op is Op.INTEGER: + return expr1 if cond.data[0] else expr2 + return Expr(Op.TERNARY, (cond, expr1, expr2)) + + return obj + + +def as_expr(obj): + """Convert non-Expr objects to Expr objects. + """ + if isinstance(obj, complex): + return as_complex(obj.real, obj.imag) + if isinstance(obj, number_types): + return as_number(obj) + if isinstance(obj, str): + # STRING expression holds string with boundary quotes, hence + # applying repr: + return as_string(repr(obj)) + if isinstance(obj, tuple): + return tuple(map(as_expr, obj)) + return obj + + +def as_symbol(obj): + """Return object as SYMBOL expression (variable or unparsed expression). + """ + return Expr(Op.SYMBOL, obj) + + +def as_number(obj, kind=4): + """Return object as INTEGER or REAL constant. + """ + if isinstance(obj, int): + return Expr(Op.INTEGER, (obj, kind)) + if isinstance(obj, float): + return Expr(Op.REAL, (obj, kind)) + if isinstance(obj, Expr): + if obj.op in (Op.INTEGER, Op.REAL): + return obj + raise OpError(f'cannot convert {obj} to INTEGER or REAL constant') + + +def as_integer(obj, kind=4): + """Return object as INTEGER constant. + """ + if isinstance(obj, int): + return Expr(Op.INTEGER, (obj, kind)) + if isinstance(obj, Expr): + if obj.op is Op.INTEGER: + return obj + raise OpError(f'cannot convert {obj} to INTEGER constant') + + +def as_real(obj, kind=4): + """Return object as REAL constant. + """ + if isinstance(obj, int): + return Expr(Op.REAL, (float(obj), kind)) + if isinstance(obj, float): + return Expr(Op.REAL, (obj, kind)) + if isinstance(obj, Expr): + if obj.op is Op.REAL: + return obj + elif obj.op is Op.INTEGER: + return Expr(Op.REAL, (float(obj.data[0]), kind)) + raise OpError(f'cannot convert {obj} to REAL constant') + + +def as_string(obj, kind=1): + """Return object as STRING expression (string literal constant). + """ + return Expr(Op.STRING, (obj, kind)) + + +def as_array(obj): + """Return object as ARRAY expression (array constant). + """ + if isinstance(obj, Expr): + obj = obj, + return Expr(Op.ARRAY, obj) + + +def as_complex(real, imag=0): + """Return object as COMPLEX expression (complex literal constant). + """ + return Expr(Op.COMPLEX, (as_expr(real), as_expr(imag))) + + +def as_apply(func, *args, **kwargs): + """Return object as APPLY expression (function call, constructor, etc.) + """ + return Expr(Op.APPLY, + (func, tuple(map(as_expr, args)), + dict((k, as_expr(v)) for k, v in kwargs.items()))) + + +def as_ternary(cond, expr1, expr2): + """Return object as TERNARY expression (cond?expr1:expr2). + """ + return Expr(Op.TERNARY, (cond, expr1, expr2)) + + +def as_ref(expr): + """Return object as referencing expression. + """ + return Expr(Op.REF, expr) + + +def as_deref(expr): + """Return object as dereferencing expression. + """ + return Expr(Op.DEREF, expr) + + +def as_eq(left, right): + return Expr(Op.RELATIONAL, (RelOp.EQ, left, right)) + + +def as_ne(left, right): + return Expr(Op.RELATIONAL, (RelOp.NE, left, right)) + + +def as_lt(left, right): + return Expr(Op.RELATIONAL, (RelOp.LT, left, right)) + + +def as_le(left, right): + return Expr(Op.RELATIONAL, (RelOp.LE, left, right)) + + +def as_gt(left, right): + return Expr(Op.RELATIONAL, (RelOp.GT, left, right)) + + +def as_ge(left, right): + return Expr(Op.RELATIONAL, (RelOp.GE, left, right)) + + +def as_terms(obj): + """Return expression as TERMS expression. + """ + if isinstance(obj, Expr): + obj = normalize(obj) + if obj.op is Op.TERMS: + return obj + if obj.op is Op.INTEGER: + return Expr(Op.TERMS, {as_integer(1, obj.data[1]): obj.data[0]}) + if obj.op is Op.REAL: + return Expr(Op.TERMS, {as_real(1, obj.data[1]): obj.data[0]}) + return Expr(Op.TERMS, {obj: 1}) + raise OpError(f'cannot convert {type(obj)} to terms Expr') + + +def as_factors(obj): + """Return expression as FACTORS expression. + """ + if isinstance(obj, Expr): + obj = normalize(obj) + if obj.op is Op.FACTORS: + return obj + if obj.op is Op.TERMS: + if len(obj.data) == 1: + (term, coeff), = obj.data.items() + if coeff == 1: + return Expr(Op.FACTORS, {term: 1}) + return Expr(Op.FACTORS, {term: 1, Expr.number(coeff): 1}) + if ((obj.op is Op.APPLY + and obj.data[0] is ArithOp.DIV + and not obj.data[2])): + return Expr(Op.FACTORS, {obj.data[1][0]: 1, obj.data[1][1]: -1}) + return Expr(Op.FACTORS, {obj: 1}) + raise OpError(f'cannot convert {type(obj)} to terms Expr') + + +def as_term_coeff(obj): + """Return expression as term-coefficient pair. + """ + if isinstance(obj, Expr): + obj = normalize(obj) + if obj.op is Op.INTEGER: + return as_integer(1, obj.data[1]), obj.data[0] + if obj.op is Op.REAL: + return as_real(1, obj.data[1]), obj.data[0] + if obj.op is Op.TERMS: + if len(obj.data) == 1: + (term, coeff), = obj.data.items() + return term, coeff + # TODO: find common divisor of coefficients + if obj.op is Op.APPLY and obj.data[0] is ArithOp.DIV: + t, c = as_term_coeff(obj.data[1][0]) + return as_apply(ArithOp.DIV, t, obj.data[1][1]), c + return obj, 1 + raise OpError(f'cannot convert {type(obj)} to term and coeff') + + +def as_numer_denom(obj): + """Return expression as numer-denom pair. + """ + if isinstance(obj, Expr): + obj = normalize(obj) + if obj.op in (Op.INTEGER, Op.REAL, Op.COMPLEX, Op.SYMBOL, + Op.INDEXING, Op.TERNARY): + return obj, as_number(1) + elif obj.op is Op.APPLY: + if obj.data[0] is ArithOp.DIV and not obj.data[2]: + numers, denoms = map(as_numer_denom, obj.data[1]) + return numers[0] * denoms[1], numers[1] * denoms[0] + return obj, as_number(1) + elif obj.op is Op.TERMS: + numers, denoms = [], [] + for term, coeff in obj.data.items(): + n, d = as_numer_denom(term) + n = n * coeff + numers.append(n) + denoms.append(d) + numer, denom = as_number(0), as_number(1) + for i in range(len(numers)): + n = numers[i] + for j in range(len(numers)): + if i != j: + n *= denoms[j] + numer += n + denom *= denoms[i] + if denom.op in (Op.INTEGER, Op.REAL) and denom.data[0] < 0: + numer, denom = -numer, -denom + return numer, denom + elif obj.op is Op.FACTORS: + numer, denom = as_number(1), as_number(1) + for b, e in obj.data.items(): + bnumer, bdenom = as_numer_denom(b) + if e > 0: + numer *= bnumer ** e + denom *= bdenom ** e + elif e < 0: + numer *= bdenom ** (-e) + denom *= bnumer ** (-e) + return numer, denom + raise OpError(f'cannot convert {type(obj)} to numer and denom') + + +def _counter(): + # Used internally to generate unique dummy symbols + counter = 0 + while True: + counter += 1 + yield counter + + +COUNTER = _counter() + + +def eliminate_quotes(s): + """Replace quoted substrings of input string. + + Return a new string and a mapping of replacements. + """ + d = {} + + def repl(m): + kind, value = m.groups()[:2] + if kind: + # remove trailing underscore + kind = kind[:-1] + p = {"'": "SINGLE", '"': "DOUBLE"}[value[0]] + k = f'{kind}@__f2py_QUOTES_{p}_{COUNTER.__next__()}@' + d[k] = value + return k + + new_s = re.sub(r'({kind}_|)({single_quoted}|{double_quoted})'.format( + kind=r'\w[\w\d_]*', + single_quoted=r"('([^'\\]|(\\.))*')", + double_quoted=r'("([^"\\]|(\\.))*")'), + repl, s) + + assert '"' not in new_s + assert "'" not in new_s + + return new_s, d + + +def insert_quotes(s, d): + """Inverse of eliminate_quotes. + """ + for k, v in d.items(): + kind = k[:k.find('@')] + if kind: + kind += '_' + s = s.replace(k, kind + v) + return s + + +def replace_parenthesis(s): + """Replace substrings of input that are enclosed in parenthesis. + + Return a new string and a mapping of replacements. + """ + # Find a parenthesis pair that appears first. + + # Fortran deliminator are `(`, `)`, `[`, `]`, `(/', '/)`, `/`. + # We don't handle `/` deliminator because it is not a part of an + # expression. + left, right = None, None + mn_i = len(s) + for left_, right_ in (('(/', '/)'), + '()', + '{}', # to support C literal structs + '[]'): + i = s.find(left_) + if i == -1: + continue + if i < mn_i: + mn_i = i + left, right = left_, right_ + + if left is None: + return s, {} + + i = mn_i + j = s.find(right, i) + + while s.count(left, i + 1, j) != s.count(right, i + 1, j): + j = s.find(right, j + 1) + if j == -1: + raise ValueError(f'Mismatch of {left+right} parenthesis in {s!r}') + + p = {'(': 'ROUND', '[': 'SQUARE', '{': 'CURLY', '(/': 'ROUNDDIV'}[left] + + k = f'@__f2py_PARENTHESIS_{p}_{COUNTER.__next__()}@' + v = s[i+len(left):j] + r, d = replace_parenthesis(s[j+len(right):]) + d[k] = v + return s[:i] + k + r, d + + +def _get_parenthesis_kind(s): + assert s.startswith('@__f2py_PARENTHESIS_'), s + return s.split('_')[4] + + +def unreplace_parenthesis(s, d): + """Inverse of replace_parenthesis. + """ + for k, v in d.items(): + p = _get_parenthesis_kind(k) + left = dict(ROUND='(', SQUARE='[', CURLY='{', ROUNDDIV='(/')[p] + right = dict(ROUND=')', SQUARE=']', CURLY='}', ROUNDDIV='/)')[p] + s = s.replace(k, left + v + right) + return s + + +def fromstring(s, language=Language.C): + """Create an expression from a string. + + This is a "lazy" parser, that is, only arithmetic operations are + resolved, non-arithmetic operations are treated as symbols. + """ + r = _FromStringWorker(language=language).parse(s) + if isinstance(r, Expr): + return r + raise ValueError(f'failed to parse `{s}` to Expr instance: got `{r}`') + + +class _Pair: + # Internal class to represent a pair of expressions + + def __init__(self, left, right): + self.left = left + self.right = right + + def substitute(self, symbols_map): + left, right = self.left, self.right + if isinstance(left, Expr): + left = left.substitute(symbols_map) + if isinstance(right, Expr): + right = right.substitute(symbols_map) + return _Pair(left, right) + + def __repr__(self): + return f'{type(self).__name__}({self.left}, {self.right})' + + +class _FromStringWorker: + + def __init__(self, language=Language.C): + self.original = None + self.quotes_map = None + self.language = language + + def finalize_string(self, s): + return insert_quotes(s, self.quotes_map) + + def parse(self, inp): + self.original = inp + unquoted, self.quotes_map = eliminate_quotes(inp) + return self.process(unquoted) + + def process(self, s, context='expr'): + """Parse string within the given context. + + The context may define the result in case of ambiguous + expressions. For instance, consider expressions `f(x, y)` and + `(x, y) + (a, b)` where `f` is a function and pair `(x, y)` + denotes complex number. Specifying context as "args" or + "expr", the subexpression `(x, y)` will be parse to an + argument list or to a complex number, respectively. + """ + if isinstance(s, (list, tuple)): + return type(s)(self.process(s_, context) for s_ in s) + + assert isinstance(s, str), (type(s), s) + + # replace subexpressions in parenthesis with f2py @-names + r, raw_symbols_map = replace_parenthesis(s) + r = r.strip() + + def restore(r): + # restores subexpressions marked with f2py @-names + if isinstance(r, (list, tuple)): + return type(r)(map(restore, r)) + return unreplace_parenthesis(r, raw_symbols_map) + + # comma-separated tuple + if ',' in r: + operands = restore(r.split(',')) + if context == 'args': + return tuple(self.process(operands)) + if context == 'expr': + if len(operands) == 2: + # complex number literal + return as_complex(*self.process(operands)) + raise NotImplementedError( + f'parsing comma-separated list (context={context}): {r}') + + # ternary operation + m = re.match(r'\A([^?]+)[?]([^:]+)[:](.+)\Z', r) + if m: + assert context == 'expr', context + oper, expr1, expr2 = restore(m.groups()) + oper = self.process(oper) + expr1 = self.process(expr1) + expr2 = self.process(expr2) + return as_ternary(oper, expr1, expr2) + + # relational expression + if self.language is Language.Fortran: + m = re.match( + r'\A(.+)\s*[.](eq|ne|lt|le|gt|ge)[.]\s*(.+)\Z', r, re.I) + else: + m = re.match( + r'\A(.+)\s*([=][=]|[!][=]|[<][=]|[<]|[>][=]|[>])\s*(.+)\Z', r) + if m: + left, rop, right = m.groups() + if self.language is Language.Fortran: + rop = '.' + rop + '.' + left, right = self.process(restore((left, right))) + rop = RelOp.fromstring(rop, language=self.language) + return Expr(Op.RELATIONAL, (rop, left, right)) + + # keyword argument + m = re.match(r'\A(\w[\w\d_]*)\s*[=](.*)\Z', r) + if m: + keyname, value = m.groups() + value = restore(value) + return _Pair(keyname, self.process(value)) + + # addition/subtraction operations + operands = re.split(r'((?<!\d[edED])[+-])', r) + if len(operands) > 1: + result = self.process(restore(operands[0] or '0')) + for op, operand in zip(operands[1::2], operands[2::2]): + operand = self.process(restore(operand)) + op = op.strip() + if op == '+': + result += operand + else: + assert op == '-' + result -= operand + return result + + # string concatenate operation + if self.language is Language.Fortran and '//' in r: + operands = restore(r.split('//')) + return Expr(Op.CONCAT, + tuple(self.process(operands))) + + # multiplication/division operations + operands = re.split(r'(?<=[@\w\d_])\s*([*]|/)', + (r if self.language is Language.C + else r.replace('**', '@__f2py_DOUBLE_STAR@'))) + if len(operands) > 1: + operands = restore(operands) + if self.language is not Language.C: + operands = [operand.replace('@__f2py_DOUBLE_STAR@', '**') + for operand in operands] + # Expression is an arithmetic product + result = self.process(operands[0]) + for op, operand in zip(operands[1::2], operands[2::2]): + operand = self.process(operand) + op = op.strip() + if op == '*': + result *= operand + else: + assert op == '/' + result /= operand + return result + + # referencing/dereferencing + if r.startswith('*') or r.startswith('&'): + op = {'*': Op.DEREF, '&': Op.REF}[r[0]] + operand = self.process(restore(r[1:])) + return Expr(op, operand) + + # exponentiation operations + if self.language is not Language.C and '**' in r: + operands = list(reversed(restore(r.split('**')))) + result = self.process(operands[0]) + for operand in operands[1:]: + operand = self.process(operand) + result = operand ** result + return result + + # int-literal-constant + m = re.match(r'\A({digit_string})({kind}|)\Z'.format( + digit_string=r'\d+', + kind=r'_(\d+|\w[\w\d_]*)'), r) + if m: + value, _, kind = m.groups() + if kind and kind.isdigit(): + kind = int(kind) + return as_integer(int(value), kind or 4) + + # real-literal-constant + m = re.match(r'\A({significant}({exponent}|)|\d+{exponent})({kind}|)\Z' + .format( + significant=r'[.]\d+|\d+[.]\d*', + exponent=r'[edED][+-]?\d+', + kind=r'_(\d+|\w[\w\d_]*)'), r) + if m: + value, _, _, kind = m.groups() + if kind and kind.isdigit(): + kind = int(kind) + value = value.lower() + if 'd' in value: + return as_real(float(value.replace('d', 'e')), kind or 8) + return as_real(float(value), kind or 4) + + # string-literal-constant with kind parameter specification + if r in self.quotes_map: + kind = r[:r.find('@')] + return as_string(self.quotes_map[r], kind or 1) + + # array constructor or literal complex constant or + # parenthesized expression + if r in raw_symbols_map: + paren = _get_parenthesis_kind(r) + items = self.process(restore(raw_symbols_map[r]), + 'expr' if paren == 'ROUND' else 'args') + if paren == 'ROUND': + if isinstance(items, Expr): + return items + if paren in ['ROUNDDIV', 'SQUARE']: + # Expression is a array constructor + if isinstance(items, Expr): + items = (items,) + return as_array(items) + + # function call/indexing + m = re.match(r'\A(.+)\s*(@__f2py_PARENTHESIS_(ROUND|SQUARE)_\d+@)\Z', + r) + if m: + target, args, paren = m.groups() + target = self.process(restore(target)) + args = self.process(restore(args)[1:-1], 'args') + if not isinstance(args, tuple): + args = args, + if paren == 'ROUND': + kwargs = dict((a.left, a.right) for a in args + if isinstance(a, _Pair)) + args = tuple(a for a in args if not isinstance(a, _Pair)) + # Warning: this could also be Fortran indexing operation.. + return as_apply(target, *args, **kwargs) + else: + # Expression is a C/Python indexing operation + # (e.g. used in .pyf files) + assert paren == 'SQUARE' + return target[args] + + # Fortran standard conforming identifier + m = re.match(r'\A\w[\w\d_]*\Z', r) + if m: + return as_symbol(r) + + # fall-back to symbol + r = self.finalize_string(restore(r)) + ewarn( + f'fromstring: treating {r!r} as symbol (original={self.original})') + return as_symbol(r) diff --git a/numpy/f2py/tests/src/array_from_pyobj/wrapmodule.c b/numpy/f2py/tests/src/array_from_pyobj/wrapmodule.c index 7f46303b014b..ea47e05558b7 100644 --- a/numpy/f2py/tests/src/array_from_pyobj/wrapmodule.c +++ b/numpy/f2py/tests/src/array_from_pyobj/wrapmodule.c @@ -1,20 +1,17 @@ -/* File: wrapmodule.c - * This file is auto-generated with f2py (version:2_1330). - * Hand edited by Pearu. - * f2py is a Fortran to Python Interface Generator (FPIG), Second Edition, - * written by Pearu Peterson <pearu@cens.ioc.ee>. - * See http://cens.ioc.ee/projects/f2py2e/ - * Generation date: Fri Oct 21 22:41:12 2005 - * $Revision:$ - * $Date:$ - * Do not edit this file directly unless you know what you are doing!!! +/* + * This file was auto-generated with f2py (version:2_1330) and hand edited by + * Pearu for testing purposes. Do not edit this file unless you know what you + * are doing!!! */ + #ifdef __cplusplus extern "C" { #endif /*********************** See f2py2e/cfuncs.py: includes ***********************/ -#include "Python.h" + +#define PY_SSIZE_T_CLEAN +#include <Python.h> #include "fortranobject.h" #include <math.h> @@ -49,9 +46,18 @@ static PyObject *f2py_rout_wrap_call(PyObject *capi_self, return NULL; rank = PySequence_Length(dims_capi); dims = malloc(rank*sizeof(npy_intp)); - for (i=0;i<rank;++i) - dims[i] = (npy_intp)PyInt_AsLong(PySequence_GetItem(dims_capi,i)); - + for (i=0;i<rank;++i) { + PyObject *tmp; + tmp = PySequence_GetItem(dims_capi, i); + if (tmp == NULL) { + goto fail; + } + dims[i] = (npy_intp)PyLong_AsLong(tmp); + Py_DECREF(tmp); + if (dims[i] == -1 && PyErr_Occurred()) { + goto fail; + } + } capi_arr_tmp = array_from_pyobj(type_num,dims,rank,intent|F2PY_INTENT_OUT,arr_capi); if (capi_arr_tmp == NULL) { free(dims); @@ -60,6 +66,10 @@ static PyObject *f2py_rout_wrap_call(PyObject *capi_self, capi_buildvalue = Py_BuildValue("N",capi_arr_tmp); free(dims); return capi_buildvalue; + +fail: + free(dims); + return NULL; } static char doc_f2py_rout_wrap_attrs[] = "\ @@ -85,7 +95,7 @@ static PyObject *f2py_rout_wrap_attrs(PyObject *capi_self, PyObject *strides = NULL; char s[100]; int i; - memset(s,0,100*sizeof(char)); + memset(s,0,100); if (!PyArg_ParseTuple(capi_args,"O!|:wrap.attrs", &PyArray_Type,&arr_capi)) return NULL; @@ -94,10 +104,10 @@ static PyObject *f2py_rout_wrap_attrs(PyObject *capi_self, dimensions = PyTuple_New(PyArray_NDIM(arr)); strides = PyTuple_New(PyArray_NDIM(arr)); for (i=0;i<PyArray_NDIM(arr);++i) { - PyTuple_SetItem(dimensions,i,PyInt_FromLong(PyArray_DIM(arr,i))); - PyTuple_SetItem(strides,i,PyInt_FromLong(PyArray_STRIDE(arr,i))); + PyTuple_SetItem(dimensions,i,PyLong_FromLong(PyArray_DIM(arr,i))); + PyTuple_SetItem(strides,i,PyLong_FromLong(PyArray_STRIDE(arr,i))); } - return Py_BuildValue("siOOO(cciii)ii",s,PyArray_NDIM(arr), + return Py_BuildValue("siNNO(cciii)ii",s,PyArray_NDIM(arr), dimensions,strides, (PyArray_BASE(arr)==NULL?Py_None:PyArray_BASE(arr)), PyArray_DESCR(arr)->kind, @@ -116,7 +126,6 @@ static PyMethodDef f2py_module_methods[] = { {NULL,NULL} }; -#if PY_VERSION_HEX >= 0x03000000 static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "test_array_from_pyobj_ext", @@ -128,87 +137,84 @@ static struct PyModuleDef moduledef = { NULL, NULL }; -#endif -#if PY_VERSION_HEX >= 0x03000000 -#define RETVAL m PyMODINIT_FUNC PyInit_test_array_from_pyobj_ext(void) { -#else -#define RETVAL -PyMODINIT_FUNC inittest_array_from_pyobj_ext(void) { -#endif PyObject *m,*d, *s; -#if PY_VERSION_HEX >= 0x03000000 m = wrap_module = PyModule_Create(&moduledef); -#else - m = wrap_module = Py_InitModule("test_array_from_pyobj_ext", f2py_module_methods); -#endif - Py_TYPE(&PyFortran_Type) = &PyType_Type; + Py_SET_TYPE(&PyFortran_Type, &PyType_Type); import_array(); if (PyErr_Occurred()) Py_FatalError("can't initialize module wrap (failed to import numpy)"); d = PyModule_GetDict(m); - s = PyString_FromString("This module 'wrap' is auto-generated with f2py (version:2_1330).\nFunctions:\n" -" arr = call(type_num,dims,intent,obj)\n" -"."); + s = PyUnicode_FromString("This module 'wrap' is auto-generated with f2py (version:2_1330).\nFunctions:\n" + " arr = call(type_num,dims,intent,obj)\n" + "."); PyDict_SetItemString(d, "__doc__", s); wrap_error = PyErr_NewException ("wrap.error", NULL, NULL); Py_DECREF(s); - PyDict_SetItemString(d, "F2PY_INTENT_IN", PyInt_FromLong(F2PY_INTENT_IN)); - PyDict_SetItemString(d, "F2PY_INTENT_INOUT", PyInt_FromLong(F2PY_INTENT_INOUT)); - PyDict_SetItemString(d, "F2PY_INTENT_OUT", PyInt_FromLong(F2PY_INTENT_OUT)); - PyDict_SetItemString(d, "F2PY_INTENT_HIDE", PyInt_FromLong(F2PY_INTENT_HIDE)); - PyDict_SetItemString(d, "F2PY_INTENT_CACHE", PyInt_FromLong(F2PY_INTENT_CACHE)); - PyDict_SetItemString(d, "F2PY_INTENT_COPY", PyInt_FromLong(F2PY_INTENT_COPY)); - PyDict_SetItemString(d, "F2PY_INTENT_C", PyInt_FromLong(F2PY_INTENT_C)); - PyDict_SetItemString(d, "F2PY_OPTIONAL", PyInt_FromLong(F2PY_OPTIONAL)); - PyDict_SetItemString(d, "F2PY_INTENT_INPLACE", PyInt_FromLong(F2PY_INTENT_INPLACE)); - PyDict_SetItemString(d, "NPY_BOOL", PyInt_FromLong(NPY_BOOL)); - PyDict_SetItemString(d, "NPY_BYTE", PyInt_FromLong(NPY_BYTE)); - PyDict_SetItemString(d, "NPY_UBYTE", PyInt_FromLong(NPY_UBYTE)); - PyDict_SetItemString(d, "NPY_SHORT", PyInt_FromLong(NPY_SHORT)); - PyDict_SetItemString(d, "NPY_USHORT", PyInt_FromLong(NPY_USHORT)); - PyDict_SetItemString(d, "NPY_INT", PyInt_FromLong(NPY_INT)); - PyDict_SetItemString(d, "NPY_UINT", PyInt_FromLong(NPY_UINT)); - PyDict_SetItemString(d, "NPY_INTP", PyInt_FromLong(NPY_INTP)); - PyDict_SetItemString(d, "NPY_UINTP", PyInt_FromLong(NPY_UINTP)); - PyDict_SetItemString(d, "NPY_LONG", PyInt_FromLong(NPY_LONG)); - PyDict_SetItemString(d, "NPY_ULONG", PyInt_FromLong(NPY_ULONG)); - PyDict_SetItemString(d, "NPY_LONGLONG", PyInt_FromLong(NPY_LONGLONG)); - PyDict_SetItemString(d, "NPY_ULONGLONG", PyInt_FromLong(NPY_ULONGLONG)); - PyDict_SetItemString(d, "NPY_FLOAT", PyInt_FromLong(NPY_FLOAT)); - PyDict_SetItemString(d, "NPY_DOUBLE", PyInt_FromLong(NPY_DOUBLE)); - PyDict_SetItemString(d, "NPY_LONGDOUBLE", PyInt_FromLong(NPY_LONGDOUBLE)); - PyDict_SetItemString(d, "NPY_CFLOAT", PyInt_FromLong(NPY_CFLOAT)); - PyDict_SetItemString(d, "NPY_CDOUBLE", PyInt_FromLong(NPY_CDOUBLE)); - PyDict_SetItemString(d, "NPY_CLONGDOUBLE", PyInt_FromLong(NPY_CLONGDOUBLE)); - PyDict_SetItemString(d, "NPY_OBJECT", PyInt_FromLong(NPY_OBJECT)); - PyDict_SetItemString(d, "NPY_STRING", PyInt_FromLong(NPY_STRING)); - PyDict_SetItemString(d, "NPY_UNICODE", PyInt_FromLong(NPY_UNICODE)); - PyDict_SetItemString(d, "NPY_VOID", PyInt_FromLong(NPY_VOID)); - PyDict_SetItemString(d, "NPY_NTYPES", PyInt_FromLong(NPY_NTYPES)); - PyDict_SetItemString(d, "NPY_NOTYPE", PyInt_FromLong(NPY_NOTYPE)); - PyDict_SetItemString(d, "NPY_USERDEF", PyInt_FromLong(NPY_USERDEF)); - - PyDict_SetItemString(d, "CONTIGUOUS", PyInt_FromLong(NPY_ARRAY_C_CONTIGUOUS)); - PyDict_SetItemString(d, "FORTRAN", PyInt_FromLong(NPY_ARRAY_F_CONTIGUOUS)); - PyDict_SetItemString(d, "OWNDATA", PyInt_FromLong(NPY_ARRAY_OWNDATA)); - PyDict_SetItemString(d, "FORCECAST", PyInt_FromLong(NPY_ARRAY_FORCECAST)); - PyDict_SetItemString(d, "ENSURECOPY", PyInt_FromLong(NPY_ARRAY_ENSURECOPY)); - PyDict_SetItemString(d, "ENSUREARRAY", PyInt_FromLong(NPY_ARRAY_ENSUREARRAY)); - PyDict_SetItemString(d, "ALIGNED", PyInt_FromLong(NPY_ARRAY_ALIGNED)); - PyDict_SetItemString(d, "WRITEABLE", PyInt_FromLong(NPY_ARRAY_WRITEABLE)); - PyDict_SetItemString(d, "UPDATEIFCOPY", PyInt_FromLong(NPY_ARRAY_UPDATEIFCOPY)); - PyDict_SetItemString(d, "WRITEBACKIFCOPY", PyInt_FromLong(NPY_ARRAY_WRITEBACKIFCOPY)); - - PyDict_SetItemString(d, "BEHAVED", PyInt_FromLong(NPY_ARRAY_BEHAVED)); - PyDict_SetItemString(d, "BEHAVED_NS", PyInt_FromLong(NPY_ARRAY_BEHAVED_NS)); - PyDict_SetItemString(d, "CARRAY", PyInt_FromLong(NPY_ARRAY_CARRAY)); - PyDict_SetItemString(d, "FARRAY", PyInt_FromLong(NPY_ARRAY_FARRAY)); - PyDict_SetItemString(d, "CARRAY_RO", PyInt_FromLong(NPY_ARRAY_CARRAY_RO)); - PyDict_SetItemString(d, "FARRAY_RO", PyInt_FromLong(NPY_ARRAY_FARRAY_RO)); - PyDict_SetItemString(d, "DEFAULT", PyInt_FromLong(NPY_ARRAY_DEFAULT)); - PyDict_SetItemString(d, "UPDATE_ALL", PyInt_FromLong(NPY_ARRAY_UPDATE_ALL)); + +#define ADDCONST(NAME, CONST) \ + s = PyLong_FromLong(CONST); \ + PyDict_SetItemString(d, NAME, s); \ + Py_DECREF(s) + + ADDCONST("F2PY_INTENT_IN", F2PY_INTENT_IN); + ADDCONST("F2PY_INTENT_INOUT", F2PY_INTENT_INOUT); + ADDCONST("F2PY_INTENT_OUT", F2PY_INTENT_OUT); + ADDCONST("F2PY_INTENT_HIDE", F2PY_INTENT_HIDE); + ADDCONST("F2PY_INTENT_CACHE", F2PY_INTENT_CACHE); + ADDCONST("F2PY_INTENT_COPY", F2PY_INTENT_COPY); + ADDCONST("F2PY_INTENT_C", F2PY_INTENT_C); + ADDCONST("F2PY_OPTIONAL", F2PY_OPTIONAL); + ADDCONST("F2PY_INTENT_INPLACE", F2PY_INTENT_INPLACE); + ADDCONST("NPY_BOOL", NPY_BOOL); + ADDCONST("NPY_BYTE", NPY_BYTE); + ADDCONST("NPY_UBYTE", NPY_UBYTE); + ADDCONST("NPY_SHORT", NPY_SHORT); + ADDCONST("NPY_USHORT", NPY_USHORT); + ADDCONST("NPY_INT", NPY_INT); + ADDCONST("NPY_UINT", NPY_UINT); + ADDCONST("NPY_INTP", NPY_INTP); + ADDCONST("NPY_UINTP", NPY_UINTP); + ADDCONST("NPY_LONG", NPY_LONG); + ADDCONST("NPY_ULONG", NPY_ULONG); + ADDCONST("NPY_LONGLONG", NPY_LONGLONG); + ADDCONST("NPY_ULONGLONG", NPY_ULONGLONG); + ADDCONST("NPY_FLOAT", NPY_FLOAT); + ADDCONST("NPY_DOUBLE", NPY_DOUBLE); + ADDCONST("NPY_LONGDOUBLE", NPY_LONGDOUBLE); + ADDCONST("NPY_CFLOAT", NPY_CFLOAT); + ADDCONST("NPY_CDOUBLE", NPY_CDOUBLE); + ADDCONST("NPY_CLONGDOUBLE", NPY_CLONGDOUBLE); + ADDCONST("NPY_OBJECT", NPY_OBJECT); + ADDCONST("NPY_STRING", NPY_STRING); + ADDCONST("NPY_UNICODE", NPY_UNICODE); + ADDCONST("NPY_VOID", NPY_VOID); + ADDCONST("NPY_NTYPES", NPY_NTYPES); + ADDCONST("NPY_NOTYPE", NPY_NOTYPE); + ADDCONST("NPY_USERDEF", NPY_USERDEF); + + ADDCONST("CONTIGUOUS", NPY_ARRAY_C_CONTIGUOUS); + ADDCONST("FORTRAN", NPY_ARRAY_F_CONTIGUOUS); + ADDCONST("OWNDATA", NPY_ARRAY_OWNDATA); + ADDCONST("FORCECAST", NPY_ARRAY_FORCECAST); + ADDCONST("ENSURECOPY", NPY_ARRAY_ENSURECOPY); + ADDCONST("ENSUREARRAY", NPY_ARRAY_ENSUREARRAY); + ADDCONST("ALIGNED", NPY_ARRAY_ALIGNED); + ADDCONST("WRITEABLE", NPY_ARRAY_WRITEABLE); + ADDCONST("UPDATEIFCOPY", NPY_ARRAY_UPDATEIFCOPY); + ADDCONST("WRITEBACKIFCOPY", NPY_ARRAY_WRITEBACKIFCOPY); + + ADDCONST("BEHAVED", NPY_ARRAY_BEHAVED); + ADDCONST("BEHAVED_NS", NPY_ARRAY_BEHAVED_NS); + ADDCONST("CARRAY", NPY_ARRAY_CARRAY); + ADDCONST("FARRAY", NPY_ARRAY_FARRAY); + ADDCONST("CARRAY_RO", NPY_ARRAY_CARRAY_RO); + ADDCONST("FARRAY_RO", NPY_ARRAY_FARRAY_RO); + ADDCONST("DEFAULT", NPY_ARRAY_DEFAULT); + ADDCONST("UPDATE_ALL", NPY_ARRAY_UPDATE_ALL); + +#undef ADDCONST( if (PyErr_Occurred()) Py_FatalError("can't initialize module wrap"); @@ -217,7 +223,7 @@ PyMODINIT_FUNC inittest_array_from_pyobj_ext(void) { on_exit(f2py_report_on_exit,(void*)"array_from_pyobj.wrap.call"); #endif - return RETVAL; + return m; } #ifdef __cplusplus } diff --git a/numpy/f2py/tests/src/module_data/mod.mod b/numpy/f2py/tests/src/module_data/mod.mod new file mode 100644 index 000000000000..8670a97e911c Binary files /dev/null and b/numpy/f2py/tests/src/module_data/mod.mod differ diff --git a/numpy/f2py/tests/src/module_data/module_data_docstring.f90 b/numpy/f2py/tests/src/module_data/module_data_docstring.f90 new file mode 100644 index 000000000000..4505e0cbc31e --- /dev/null +++ b/numpy/f2py/tests/src/module_data/module_data_docstring.f90 @@ -0,0 +1,12 @@ +module mod + integer :: i + integer :: x(4) + real, dimension(2,3) :: a + real, allocatable, dimension(:,:) :: b +contains + subroutine foo + integer :: k + k = 1 + a(1,2) = a(1,2)+3 + end subroutine foo +end module mod diff --git a/numpy/f2py/tests/test_abstract_interface.py b/numpy/f2py/tests/test_abstract_interface.py new file mode 100644 index 000000000000..936c1f7bc9ae --- /dev/null +++ b/numpy/f2py/tests/test_abstract_interface.py @@ -0,0 +1,66 @@ +import textwrap +from . import util +from numpy.f2py import crackfortran + + +class TestAbstractInterface(util.F2PyTest): + suffix = '.f90' + + skip = ['add1', 'add2'] + + code = textwrap.dedent(""" + module ops_module + + abstract interface + subroutine op(x, y, z) + integer, intent(in) :: x, y + integer, intent(out) :: z + end subroutine + end interface + + contains + + subroutine foo(x, y, r1, r2) + integer, intent(in) :: x, y + integer, intent(out) :: r1, r2 + procedure (op) add1, add2 + procedure (op), pointer::p + p=>add1 + call p(x, y, r1) + p=>add2 + call p(x, y, r2) + end subroutine + end module + + subroutine add1(x, y, z) + integer, intent(in) :: x, y + integer, intent(out) :: z + z = x + y + end subroutine + + subroutine add2(x, y, z) + integer, intent(in) :: x, y + integer, intent(out) :: z + z = x + 2 * y + end subroutine + """) + + def test_abstract_interface(self): + assert self.module.ops_module.foo(3, 5) == (8, 13) + + def test_parse_abstract_interface(self, tmp_path): + # Test gh18403 + f_path = tmp_path / "gh18403_mod.f90" + with f_path.open('w') as ff: + ff.write(textwrap.dedent("""\ + module test + abstract interface + subroutine foo() + end subroutine + end interface + end module test + """)) + mod = crackfortran.crackfortran([str(f_path)]) + assert len(mod) == 1 + assert len(mod[0]['body']) == 1 + assert mod[0]['body'][0]['block'] == 'abstract interface' diff --git a/numpy/f2py/tests/test_array_from_pyobj.py b/numpy/f2py/tests/test_array_from_pyobj.py index 8b021491f09a..649fd1c4863b 100644 --- a/numpy/f2py/tests/test_array_from_pyobj.py +++ b/numpy/f2py/tests/test_array_from_pyobj.py @@ -1,14 +1,12 @@ -from __future__ import division, absolute_import, print_function - -import unittest import os import sys import copy +import platform +import pytest + +import numpy as np -from numpy import ( - array, alltrue, ndarray, zeros, dtype, intp, clongdouble - ) -from numpy.testing import assert_, assert_equal, SkipTest +from numpy.testing import assert_, assert_equal from numpy.core.multiarray import typeinfo from . import util @@ -24,7 +22,7 @@ def setup_module(): # Check compiler availability first if not util.has_c_compiler(): - raise SkipTest("No C compiler available") + pytest.skip("No C compiler available") if wrap is None: config_code = """ @@ -57,7 +55,7 @@ def flags2names(flags): return info -class Intent(object): +class Intent: def __init__(self, intent_list=[]): self.intent_list = intent_list[:] @@ -121,8 +119,11 @@ def is_intent_exact(self, *names): # 16 byte long double types this means the inout intent cannot be satisfied # and several tests fail as the alignment flag can be randomly true or fals # when numpy gains an aligned allocator the tests could be enabled again -if ((intp().dtype.itemsize != 4 or clongdouble().dtype.alignment <= 8) and - sys.platform != 'win32'): +# +# Furthermore, on macOS ARM64, LONGDOUBLE is an alias for DOUBLE. +if ((np.intp().dtype.itemsize != 4 or np.clongdouble().dtype.alignment <= 8) and + sys.platform != 'win32' and + (platform.system(), platform.processor()) != ('Darwin', 'arm')): _type_names.extend(['LONGDOUBLE', 'CDOUBLE', 'CLONGDOUBLE']) _cast_dict['LONGDOUBLE'] = _cast_dict['LONG'] + \ ['ULONG', 'FLOAT', 'DOUBLE', 'LONGDOUBLE'] @@ -131,11 +132,11 @@ def is_intent_exact(self, *names): _cast_dict['CDOUBLE'] = _cast_dict['DOUBLE'] + ['CFLOAT', 'CDOUBLE'] -class Type(object): +class Type: _type_cache = {} def __new__(cls, name): - if isinstance(name, dtype): + if isinstance(name, np.dtype): dtype0 = name name = None for n, i in typeinfo.items(): @@ -155,7 +156,8 @@ def _init(self, name): info = typeinfo[self.NAME] self.type_num = getattr(wrap, 'NPY_' + self.NAME) assert_equal(self.type_num, info.num) - self.dtype = info.type + self.dtype = np.dtype(info.type) + self.type = info.type self.elsize = info.bits / 8 self.dtypechar = info.char @@ -192,7 +194,7 @@ def larger_types(self): return types -class Array(object): +class Array: def __init__(self, typ, dims, intent, obj): self.type = typ @@ -204,7 +206,7 @@ def __init__(self, typ, dims, intent, obj): # arr.dtypechar may be different from typ.dtypechar self.arr = wrap.call(typ.type_num, dims, intent.flags, obj) - assert_(isinstance(self.arr, ndarray), repr(type(self.arr))) + assert_(isinstance(self.arr, np.ndarray), repr(type(self.arr))) self.arr_attr = wrap.array_attrs(self.arr) @@ -227,13 +229,15 @@ def __init__(self, typ, dims, intent, obj): return if intent.is_intent('cache'): - assert_(isinstance(obj, ndarray), repr(type(obj))) - self.pyarr = array(obj).reshape(*dims).copy() + assert_(isinstance(obj, np.ndarray), repr(type(obj))) + self.pyarr = np.array(obj).reshape(*dims).copy() else: - self.pyarr = array(array(obj, dtype=typ.dtypechar).reshape(*dims), - order=self.intent.is_intent('c') and 'C' or 'F') + self.pyarr = np.array( + np.array(obj, dtype=typ.dtypechar).reshape(*dims), + order=self.intent.is_intent('c') and 'C' or 'F') assert_(self.pyarr.dtype == typ, repr((self.pyarr.dtype, typ))) + self.pyarr.setflags(write=self.arr.flags['WRITEABLE']) assert_(self.pyarr.flags['OWNDATA'], (obj, intent)) self.pyarr_attr = wrap.array_attrs(self.pyarr) @@ -268,7 +272,7 @@ def __init__(self, typ, dims, intent, obj): repr((self.arr_attr[5][3], self.type.elsize))) assert_(self.arr_equal(self.pyarr, self.arr)) - if isinstance(self.obj, ndarray): + if isinstance(self.obj, np.ndarray): if typ.elsize == Type(obj.dtype).elsize: if not intent.is_intent('copy') and self.arr_attr[1] <= 1: assert_(self.has_shared_memory()) @@ -276,8 +280,7 @@ def __init__(self, typ, dims, intent, obj): def arr_equal(self, arr1, arr2): if arr1.shape != arr2.shape: return False - s = arr1 == arr2 - return alltrue(s.flatten()) + return (arr1 == arr2).all() def __str__(self): return str(self.arr) @@ -287,13 +290,13 @@ def has_shared_memory(self): """ if self.obj is self.arr: return True - if not isinstance(self.obj, ndarray): + if not isinstance(self.obj, np.ndarray): return False obj_attr = wrap.array_attrs(self.obj) return obj_attr[0] == self.arr_attr[0] -class TestIntent(object): +class TestIntent: def test_in_out(self): assert_equal(str(intent.in_.out), 'intent(in,out)') @@ -304,17 +307,23 @@ def test_in_out(self): assert_(not intent.in_.is_intent('c')) -class _test_shared_memory(object): +class TestSharedMemory: num2seq = [1, 2] num23seq = [[1, 2, 3], [4, 5, 6]] + @pytest.fixture(autouse=True, scope='class', params=_type_names) + def setup_type(self, request): + request.cls.type = Type(request.param) + request.cls.array = lambda self, dims, intent, obj: \ + Array(Type(request.param), dims, intent, obj) + def test_in_from_2seq(self): a = self.array([2], intent.in_, self.num2seq) assert_(not a.has_shared_memory()) def test_in_from_2casttype(self): for t in self.type.cast_types(): - obj = array(self.num2seq, dtype=t.dtype) + obj = np.array(self.num2seq, dtype=t.dtype) a = self.array([len(self.num2seq)], intent.in_, obj) if t.elsize == self.type.elsize: assert_( @@ -322,8 +331,20 @@ def test_in_from_2casttype(self): else: assert_(not a.has_shared_memory(), repr(t.dtype)) + @pytest.mark.parametrize('write', ['w', 'ro']) + @pytest.mark.parametrize('order', ['C', 'F']) + @pytest.mark.parametrize('inp', ['2seq', '23seq']) + def test_in_nocopy(self, write, order, inp): + """Test if intent(in) array can be passed without copies + """ + seq = getattr(self, 'num' + inp) + obj = np.array(seq, dtype=self.type.dtype, order=order) + obj.setflags(write=(write == 'w')) + a = self.array(obj.shape, ((order=='C' and intent.in_.c) or intent.in_), obj) + assert a.has_shared_memory() + def test_inout_2seq(self): - obj = array(self.num2seq, dtype=self.type.dtype) + obj = np.array(self.num2seq, dtype=self.type.dtype) a = self.array([len(self.num2seq)], intent.inout, obj) assert_(a.has_shared_memory()) @@ -337,12 +358,12 @@ def test_inout_2seq(self): raise SystemError('intent(inout) should have failed on sequence') def test_f_inout_23seq(self): - obj = array(self.num23seq, dtype=self.type.dtype, order='F') + obj = np.array(self.num23seq, dtype=self.type.dtype, order='F') shape = (len(self.num23seq), len(self.num23seq[0])) a = self.array(shape, intent.in_.inout, obj) assert_(a.has_shared_memory()) - obj = array(self.num23seq, dtype=self.type.dtype, order='C') + obj = np.array(self.num23seq, dtype=self.type.dtype, order='C') shape = (len(self.num23seq), len(self.num23seq[0])) try: a = self.array(shape, intent.in_.inout, obj) @@ -355,14 +376,14 @@ def test_f_inout_23seq(self): 'intent(inout) should have failed on improper array') def test_c_inout_23seq(self): - obj = array(self.num23seq, dtype=self.type.dtype) + obj = np.array(self.num23seq, dtype=self.type.dtype) shape = (len(self.num23seq), len(self.num23seq[0])) a = self.array(shape, intent.in_.c.inout, obj) assert_(a.has_shared_memory()) def test_in_copy_from_2casttype(self): for t in self.type.cast_types(): - obj = array(self.num2seq, dtype=t.dtype) + obj = np.array(self.num2seq, dtype=t.dtype) a = self.array([len(self.num2seq)], intent.in_.copy, obj) assert_(not a.has_shared_memory(), repr(t.dtype)) @@ -373,14 +394,14 @@ def test_c_in_from_23seq(self): def test_in_from_23casttype(self): for t in self.type.cast_types(): - obj = array(self.num23seq, dtype=t.dtype) + obj = np.array(self.num23seq, dtype=t.dtype) a = self.array([len(self.num23seq), len(self.num23seq[0])], intent.in_, obj) assert_(not a.has_shared_memory(), repr(t.dtype)) def test_f_in_from_23casttype(self): for t in self.type.cast_types(): - obj = array(self.num23seq, dtype=t.dtype, order='F') + obj = np.array(self.num23seq, dtype=t.dtype, order='F') a = self.array([len(self.num23seq), len(self.num23seq[0])], intent.in_, obj) if t.elsize == self.type.elsize: @@ -390,7 +411,7 @@ def test_f_in_from_23casttype(self): def test_c_in_from_23casttype(self): for t in self.type.cast_types(): - obj = array(self.num23seq, dtype=t.dtype) + obj = np.array(self.num23seq, dtype=t.dtype) a = self.array([len(self.num23seq), len(self.num23seq[0])], intent.in_.c, obj) if t.elsize == self.type.elsize: @@ -400,14 +421,14 @@ def test_c_in_from_23casttype(self): def test_f_copy_in_from_23casttype(self): for t in self.type.cast_types(): - obj = array(self.num23seq, dtype=t.dtype, order='F') + obj = np.array(self.num23seq, dtype=t.dtype, order='F') a = self.array([len(self.num23seq), len(self.num23seq[0])], intent.in_.copy, obj) assert_(not a.has_shared_memory(), repr(t.dtype)) def test_c_copy_in_from_23casttype(self): for t in self.type.cast_types(): - obj = array(self.num23seq, dtype=t.dtype) + obj = np.array(self.num23seq, dtype=t.dtype) a = self.array([len(self.num23seq), len(self.num23seq[0])], intent.in_.c.copy, obj) assert_(not a.has_shared_memory(), repr(t.dtype)) @@ -416,7 +437,7 @@ def test_in_cache_from_2casttype(self): for t in self.type.all_types(): if t.elsize != self.type.elsize: continue - obj = array(self.num2seq, dtype=t.dtype) + obj = np.array(self.num2seq, dtype=t.dtype) shape = (len(self.num2seq),) a = self.array(shape, intent.in_.c.cache, obj) assert_(a.has_shared_memory(), repr(t.dtype)) @@ -424,7 +445,7 @@ def test_in_cache_from_2casttype(self): a = self.array(shape, intent.in_.cache, obj) assert_(a.has_shared_memory(), repr(t.dtype)) - obj = array(self.num2seq, dtype=t.dtype, order='F') + obj = np.array(self.num2seq, dtype=t.dtype, order='F') a = self.array(shape, intent.in_.c.cache, obj) assert_(a.has_shared_memory(), repr(t.dtype)) @@ -445,7 +466,7 @@ def test_in_cache_from_2casttype_failure(self): for t in self.type.all_types(): if t.elsize >= self.type.elsize: continue - obj = array(self.num2seq, dtype=t.dtype) + obj = np.array(self.num2seq, dtype=t.dtype) shape = (len(self.num2seq),) try: self.array(shape, intent.in_.cache, obj) # Should succeed @@ -481,18 +502,18 @@ def test_hidden(self): shape = (2,) a = self.array(shape, intent.hide, None) assert_(a.arr.shape == shape) - assert_(a.arr_equal(a.arr, zeros(shape, dtype=self.type.dtype))) + assert_(a.arr_equal(a.arr, np.zeros(shape, dtype=self.type.dtype))) shape = (2, 3) a = self.array(shape, intent.hide, None) assert_(a.arr.shape == shape) - assert_(a.arr_equal(a.arr, zeros(shape, dtype=self.type.dtype))) + assert_(a.arr_equal(a.arr, np.zeros(shape, dtype=self.type.dtype))) assert_(a.arr.flags['FORTRAN'] and not a.arr.flags['CONTIGUOUS']) shape = (2, 3) a = self.array(shape, intent.c.hide, None) assert_(a.arr.shape == shape) - assert_(a.arr_equal(a.arr, zeros(shape, dtype=self.type.dtype))) + assert_(a.arr_equal(a.arr, np.zeros(shape, dtype=self.type.dtype))) assert_(not a.arr.flags['FORTRAN'] and a.arr.flags['CONTIGUOUS']) shape = (-1, 3) @@ -510,18 +531,18 @@ def test_optional_none(self): shape = (2,) a = self.array(shape, intent.optional, None) assert_(a.arr.shape == shape) - assert_(a.arr_equal(a.arr, zeros(shape, dtype=self.type.dtype))) + assert_(a.arr_equal(a.arr, np.zeros(shape, dtype=self.type.dtype))) shape = (2, 3) a = self.array(shape, intent.optional, None) assert_(a.arr.shape == shape) - assert_(a.arr_equal(a.arr, zeros(shape, dtype=self.type.dtype))) + assert_(a.arr_equal(a.arr, np.zeros(shape, dtype=self.type.dtype))) assert_(a.arr.flags['FORTRAN'] and not a.arr.flags['CONTIGUOUS']) shape = (2, 3) a = self.array(shape, intent.c.optional, None) assert_(a.arr.shape == shape) - assert_(a.arr_equal(a.arr, zeros(shape, dtype=self.type.dtype))) + assert_(a.arr_equal(a.arr, np.zeros(shape, dtype=self.type.dtype))) assert_(not a.arr.flags['FORTRAN'] and a.arr.flags['CONTIGUOUS']) def test_optional_from_2seq(self): @@ -543,14 +564,14 @@ def test_optional_from_23seq(self): assert_(not a.has_shared_memory()) def test_inplace(self): - obj = array(self.num23seq, dtype=self.type.dtype) + obj = np.array(self.num23seq, dtype=self.type.dtype) assert_(not obj.flags['FORTRAN'] and obj.flags['CONTIGUOUS']) shape = obj.shape a = self.array(shape, intent.inplace, obj) assert_(obj[1][2] == a.arr[1][2], repr((obj, a.arr))) a.arr[1][2] = 54 assert_(obj[1][2] == a.arr[1][2] == - array(54, dtype=self.type.dtype), repr((obj, a.arr))) + np.array(54, dtype=self.type.dtype), repr((obj, a.arr))) assert_(a.arr is obj) assert_(obj.flags['FORTRAN']) # obj attributes are changed inplace! assert_(not obj.flags['CONTIGUOUS']) @@ -559,26 +580,17 @@ def test_inplace_from_casttype(self): for t in self.type.cast_types(): if t is self.type: continue - obj = array(self.num23seq, dtype=t.dtype) - assert_(obj.dtype.type == t.dtype) - assert_(obj.dtype.type is not self.type.dtype) + obj = np.array(self.num23seq, dtype=t.dtype) + assert_(obj.dtype.type == t.type) + assert_(obj.dtype.type is not self.type.type) assert_(not obj.flags['FORTRAN'] and obj.flags['CONTIGUOUS']) shape = obj.shape a = self.array(shape, intent.inplace, obj) assert_(obj[1][2] == a.arr[1][2], repr((obj, a.arr))) a.arr[1][2] = 54 assert_(obj[1][2] == a.arr[1][2] == - array(54, dtype=self.type.dtype), repr((obj, a.arr))) + np.array(54, dtype=self.type.dtype), repr((obj, a.arr))) assert_(a.arr is obj) assert_(obj.flags['FORTRAN']) # obj attributes changed inplace! assert_(not obj.flags['CONTIGUOUS']) - assert_(obj.dtype.type is self.type.dtype) # obj changed inplace! - - -for t in _type_names: - exec('''\ -class TestGen_%s(_test_shared_memory): - def setup(self): - self.type = Type(%r) - array = lambda self,dims,intent,obj: Array(Type(%r),dims,intent,obj) -''' % (t, t, t)) + assert_(obj.dtype.type is self.type.type) # obj changed inplace! diff --git a/numpy/f2py/tests/test_assumed_shape.py b/numpy/f2py/tests/test_assumed_shape.py index 460afd68db6e..79e3ad138426 100644 --- a/numpy/f2py/tests/test_assumed_shape.py +++ b/numpy/f2py/tests/test_assumed_shape.py @@ -1,7 +1,6 @@ -from __future__ import division, absolute_import, print_function - import os import pytest +import tempfile from numpy.testing import assert_ from . import util @@ -16,6 +15,7 @@ class TestAssumedShapeSumExample(util.F2PyTest): _path('src', 'assumed_shape', 'foo_use.f90'), _path('src', 'assumed_shape', 'precision.f90'), _path('src', 'assumed_shape', 'foo_mod.f90'), + _path('src', 'assumed_shape', '.f2py_f2cmap'), ] @pytest.mark.slow @@ -31,3 +31,23 @@ def test_all(self): assert_(r == 3, repr(r)) r = self.module.mod.fsum([1, 2]) assert_(r == 3, repr(r)) + + +class TestF2cmapOption(TestAssumedShapeSumExample): + def setup(self): + # Use a custom file name for .f2py_f2cmap + self.sources = list(self.sources) + f2cmap_src = self.sources.pop(-1) + + self.f2cmap_file = tempfile.NamedTemporaryFile(delete=False) + with open(f2cmap_src, 'rb') as f: + self.f2cmap_file.write(f.read()) + self.f2cmap_file.close() + + self.sources.append(self.f2cmap_file.name) + self.options = ["--f2cmap", self.f2cmap_file.name] + + super().setup() + + def teardown(self): + os.unlink(self.f2cmap_file.name) diff --git a/numpy/f2py/tests/test_block_docstring.py b/numpy/f2py/tests/test_block_docstring.py index b2981aa82980..7d725165b2fb 100644 --- a/numpy/f2py/tests/test_block_docstring.py +++ b/numpy/f2py/tests/test_block_docstring.py @@ -1,11 +1,8 @@ -from __future__ import division, absolute_import, print_function - -import textwrap import sys import pytest from . import util -from numpy.testing import assert_equal +from numpy.testing import assert_equal, IS_PYPY class TestBlockDocString(util.F2PyTest): code = """ @@ -19,6 +16,8 @@ class TestBlockDocString(util.F2PyTest): @pytest.mark.skipif(sys.platform=='win32', reason='Fails with MinGW64 Gfortran (Issue #9673)') + @pytest.mark.xfail(IS_PYPY, + reason="PyPy cannot modify tp_doc after PyType_Ready") def test_block_docstring(self): - expected = "'i'-array(2,3)\n" + expected = "bar : 'i'-array(2,3)\n" assert_equal(self.module.block.__doc__, expected) diff --git a/numpy/f2py/tests/test_callback.py b/numpy/f2py/tests/test_callback.py index 4e74947b02e2..5d2aab94df9a 100644 --- a/numpy/f2py/tests/test_callback.py +++ b/numpy/f2py/tests/test_callback.py @@ -1,12 +1,13 @@ -from __future__ import division, absolute_import, print_function - import math import textwrap import sys import pytest +import threading +import traceback +import time import numpy as np -from numpy.testing import assert_, assert_equal +from numpy.testing import assert_, assert_equal, IS_PYPY from . import util @@ -59,16 +60,31 @@ class TestF77Callback(util.F2PyTest): a = callback(cu, lencu) end + + subroutine hidden_callback(a, r) + external global_f +cf2py intent(callback, hide) global_f + integer a, r, global_f +cf2py intent(out) r + r = global_f(a) + end + + subroutine hidden_callback2(a, r) + external global_f + integer a, r, global_f +cf2py intent(out) r + r = global_f(a) + end """ - @pytest.mark.slow - def test_all(self): - for name in "t,t2".split(","): - self.check_function(name) + @pytest.mark.parametrize('name', 't,t2'.split(',')) + def test_all(self, name): + self.check_function(name) - @pytest.mark.slow + @pytest.mark.xfail(IS_PYPY, + reason="PyPy cannot modify tp_doc after PyType_Ready") def test_docstring(self): - expected = """ + expected = textwrap.dedent("""\ a = t(fun,[fun_extra_args]) Wrapper for ``t``. @@ -90,11 +106,11 @@ def test_docstring(self): ----- Call-back functions:: - def fun(): return a - Return objects: - a : int - """ - assert_equal(self.module.t.__doc__, textwrap.dedent(expected).lstrip()) + def fun(): return a + Return objects: + a : int + """) + assert_equal(self.module.t.__doc__, expected) def check_function(self, name): t = getattr(self.module, name) @@ -118,7 +134,7 @@ def check_function(self, name): r = t(self.module.func0._cpointer) assert_(r == 11, repr(r)) - class A(object): + class A: def __call__(self): return 7 @@ -163,3 +179,147 @@ def callback(cu, lencu): f = getattr(self.module, 'string_callback_array') res = f(callback, cu, len(cu)) assert_(res == 0, repr(res)) + + def test_threadsafety(self): + # Segfaults if the callback handling is not threadsafe + + errors = [] + + def cb(): + # Sleep here to make it more likely for another thread + # to call their callback at the same time. + time.sleep(1e-3) + + # Check reentrancy + r = self.module.t(lambda: 123) + assert_(r == 123) + + return 42 + + def runner(name): + try: + for j in range(50): + r = self.module.t(cb) + assert_(r == 42) + self.check_function(name) + except Exception: + errors.append(traceback.format_exc()) + + threads = [threading.Thread(target=runner, args=(arg,)) + for arg in ("t", "t2") for n in range(20)] + + for t in threads: + t.start() + + for t in threads: + t.join() + + errors = "\n\n".join(errors) + if errors: + raise AssertionError(errors) + + def test_hidden_callback(self): + try: + self.module.hidden_callback(2) + except Exception as msg: + assert_(str(msg).startswith('Callback global_f not defined')) + + try: + self.module.hidden_callback2(2) + except Exception as msg: + assert_(str(msg).startswith('cb: Callback global_f not defined')) + + self.module.global_f = lambda x: x + 1 + r = self.module.hidden_callback(2) + assert_(r == 3) + + self.module.global_f = lambda x: x + 2 + r = self.module.hidden_callback(2) + assert_(r == 4) + + del self.module.global_f + try: + self.module.hidden_callback(2) + except Exception as msg: + assert_(str(msg).startswith('Callback global_f not defined')) + + self.module.global_f = lambda x=0: x + 3 + r = self.module.hidden_callback(2) + assert_(r == 5) + + # reproducer of gh18341 + r = self.module.hidden_callback2(2) + assert_(r == 3) + + +class TestF77CallbackPythonTLS(TestF77Callback): + """ + Callback tests using Python thread-local storage instead of + compiler-provided + """ + options = ["-DF2PY_USE_PYTHON_TLS"] + + +class TestF90Callback(util.F2PyTest): + + suffix = '.f90' + + code = textwrap.dedent( + """ + function gh17797(f, y) result(r) + external f + integer(8) :: r, f + integer(8), dimension(:) :: y + r = f(0) + r = r + sum(y) + end function gh17797 + """) + + def test_gh17797(self): + + def incr(x): + return x + 123 + + y = np.array([1, 2, 3], dtype=np.int64) + r = self.module.gh17797(incr, y) + assert r == 123 + 1 + 2 + 3 + + +class TestGH18335(util.F2PyTest): + """The reproduction of the reported issue requires specific input that + extensions may break the issue conditions, so the reproducer is + implemented as a separate test class. Do not extend this test with + other tests! + """ + + suffix = '.f90' + + code = textwrap.dedent( + """ + ! When gh18335_workaround is defined as an extension, + ! the issue cannot be reproduced. + !subroutine gh18335_workaround(f, y) + ! implicit none + ! external f + ! integer(kind=1) :: y(1) + ! call f(y) + !end subroutine gh18335_workaround + + function gh18335(f) result (r) + implicit none + external f + integer(kind=1) :: y(1), r + y(1) = 123 + call f(y) + r = y(1) + end function gh18335 + """) + + def test_gh18335(self): + + def foo(x): + x[0] += 1 + + y = np.array([1, 2, 3], dtype=np.int8) + r = self.module.gh18335(foo) + assert r == 123 + 1 diff --git a/numpy/f2py/tests/test_common.py b/numpy/f2py/tests/test_common.py index dcb01b0ec736..e4bf35504761 100644 --- a/numpy/f2py/tests/test_common.py +++ b/numpy/f2py/tests/test_common.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - import os import sys import pytest diff --git a/numpy/f2py/tests/test_compile_function.py b/numpy/f2py/tests/test_compile_function.py new file mode 100644 index 000000000000..f76fd644807c --- /dev/null +++ b/numpy/f2py/tests/test_compile_function.py @@ -0,0 +1,125 @@ +"""See https://github.com/numpy/numpy/pull/11937. + +""" +import sys +import os +import uuid +from importlib import import_module +import pytest + +import numpy.f2py + +from numpy.testing import assert_equal +from . import util + + +def setup_module(): + if not util.has_c_compiler(): + pytest.skip("Needs C compiler") + if not util.has_f77_compiler(): + pytest.skip('Needs FORTRAN 77 compiler') + + +# extra_args can be a list (since gh-11937) or string. +# also test absence of extra_args +@pytest.mark.parametrize( + "extra_args", [['--noopt', '--debug'], '--noopt --debug', ''] + ) +@pytest.mark.leaks_references(reason="Imported module seems never deleted.") +def test_f2py_init_compile(extra_args): + # flush through the f2py __init__ compile() function code path as a + # crude test for input handling following migration from + # exec_command() to subprocess.check_output() in gh-11937 + + # the Fortran 77 syntax requires 6 spaces before any commands, but + # more space may be added/ + fsource = """ + integer function foo() + foo = 10 + 5 + return + end + """ + # use various helper functions in util.py to enable robust build / + # compile and reimport cycle in test suite + moddir = util.get_module_dir() + modname = util.get_temp_module_name() + + cwd = os.getcwd() + target = os.path.join(moddir, str(uuid.uuid4()) + '.f') + # try running compile() with and without a source_fn provided so + # that the code path where a temporary file for writing Fortran + # source is created is also explored + for source_fn in [target, None]: + # mimic the path changing behavior used by build_module() in + # util.py, but don't actually use build_module() because it has + # its own invocation of subprocess that circumvents the + # f2py.compile code block under test + try: + os.chdir(moddir) + ret_val = numpy.f2py.compile( + fsource, + modulename=modname, + extra_args=extra_args, + source_fn=source_fn + ) + finally: + os.chdir(cwd) + + # check for compile success return value + assert_equal(ret_val, 0) + + # we are not currently able to import the Python-Fortran + # interface module on Windows / Appveyor, even though we do get + # successful compilation on that platform with Python 3.x + if sys.platform != 'win32': + # check for sensible result of Fortran function; that means + # we can import the module name in Python and retrieve the + # result of the sum operation + return_check = import_module(modname) + calc_result = return_check.foo() + assert_equal(calc_result, 15) + # Removal from sys.modules, is not as such necessary. Even with + # removal, the module (dict) stays alive. + del sys.modules[modname] + + +def test_f2py_init_compile_failure(): + # verify an appropriate integer status value returned by + # f2py.compile() when invalid Fortran is provided + ret_val = numpy.f2py.compile(b"invalid") + assert_equal(ret_val, 1) + + +def test_f2py_init_compile_bad_cmd(): + # verify that usage of invalid command in f2py.compile() returns + # status value of 127 for historic consistency with exec_command() + # error handling + + # patch the sys Python exe path temporarily to induce an OSError + # downstream NOTE: how bad of an idea is this patching? + try: + temp = sys.executable + sys.executable = 'does not exist' + + # the OSError should take precedence over invalid Fortran + ret_val = numpy.f2py.compile(b"invalid") + assert_equal(ret_val, 127) + finally: + sys.executable = temp + + +@pytest.mark.parametrize('fsource', + ['program test_f2py\nend program test_f2py', + b'program test_f2py\nend program test_f2py',]) +def test_compile_from_strings(tmpdir, fsource): + # Make sure we can compile str and bytes gh-12796 + cwd = os.getcwd() + try: + os.chdir(str(tmpdir)) + ret_val = numpy.f2py.compile( + fsource, + modulename='test_compile_from_strings', + extension='.f90') + assert_equal(ret_val, 0) + finally: + os.chdir(cwd) diff --git a/numpy/f2py/tests/test_crackfortran.py b/numpy/f2py/tests/test_crackfortran.py new file mode 100644 index 000000000000..039e085b48f8 --- /dev/null +++ b/numpy/f2py/tests/test_crackfortran.py @@ -0,0 +1,283 @@ +import pytest +import numpy as np +from numpy.testing import assert_array_equal, assert_equal +from numpy.f2py.crackfortran import markinnerspaces +from . import util +from numpy.f2py import crackfortran +import textwrap + + +class TestNoSpace(util.F2PyTest): + # issue gh-15035: add handling for endsubroutine, endfunction with no space + # between "end" and the block name + code = """ + subroutine subb(k) + real(8), intent(inout) :: k(:) + k=k+1 + endsubroutine + + subroutine subc(w,k) + real(8), intent(in) :: w(:) + real(8), intent(out) :: k(size(w)) + k=w+1 + endsubroutine + + function t0(value) + character value + character t0 + t0 = value + endfunction + """ + + def test_module(self): + k = np.array([1, 2, 3], dtype=np.float64) + w = np.array([1, 2, 3], dtype=np.float64) + self.module.subb(k) + assert_array_equal(k, w + 1) + self.module.subc([w, k]) + assert_array_equal(k, w + 1) + assert self.module.t0(23) == b'2' + + +class TestPublicPrivate(): + + def test_defaultPrivate(self, tmp_path): + f_path = tmp_path / "mod.f90" + with f_path.open('w') as ff: + ff.write(textwrap.dedent("""\ + module foo + private + integer :: a + public :: setA + integer :: b + contains + subroutine setA(v) + integer, intent(in) :: v + a = v + end subroutine setA + end module foo + """)) + mod = crackfortran.crackfortran([str(f_path)]) + assert len(mod) == 1 + mod = mod[0] + assert 'private' in mod['vars']['a']['attrspec'] + assert 'public' not in mod['vars']['a']['attrspec'] + assert 'private' in mod['vars']['b']['attrspec'] + assert 'public' not in mod['vars']['b']['attrspec'] + assert 'private' not in mod['vars']['seta']['attrspec'] + assert 'public' in mod['vars']['seta']['attrspec'] + + def test_defaultPublic(self, tmp_path): + f_path = tmp_path / "mod.f90" + with f_path.open('w') as ff: + ff.write(textwrap.dedent("""\ + module foo + public + integer, private :: a + public :: setA + contains + subroutine setA(v) + integer, intent(in) :: v + a = v + end subroutine setA + end module foo + """)) + mod = crackfortran.crackfortran([str(f_path)]) + assert len(mod) == 1 + mod = mod[0] + assert 'private' in mod['vars']['a']['attrspec'] + assert 'public' not in mod['vars']['a']['attrspec'] + assert 'private' not in mod['vars']['seta']['attrspec'] + assert 'public' in mod['vars']['seta']['attrspec'] + + +class TestExternal(util.F2PyTest): + # issue gh-17859: add external attribute support + code = """ + integer(8) function external_as_statement(fcn) + implicit none + external fcn + integer(8) :: fcn + external_as_statement = fcn(0) + end + + integer(8) function external_as_attribute(fcn) + implicit none + integer(8), external :: fcn + external_as_attribute = fcn(0) + end + """ + + def test_external_as_statement(self): + def incr(x): + return x + 123 + r = self.module.external_as_statement(incr) + assert r == 123 + + def test_external_as_attribute(self): + def incr(x): + return x + 123 + r = self.module.external_as_attribute(incr) + assert r == 123 + + +class TestCrackFortran(util.F2PyTest): + + suffix = '.f90' + + code = textwrap.dedent(""" + subroutine gh2848( & + ! first 2 parameters + par1, par2,& + ! last 2 parameters + par3, par4) + + integer, intent(in) :: par1, par2 + integer, intent(out) :: par3, par4 + + par3 = par1 + par4 = par2 + + end subroutine gh2848 + """) + + def test_gh2848(self): + r = self.module.gh2848(1, 2) + assert r == (1, 2) + + +class TestMarkinnerspaces(): + # issue #14118: markinnerspaces does not handle multiple quotations + + def test_do_not_touch_normal_spaces(self): + test_list = ["a ", " a", "a b c", "'abcdefghij'"] + for i in test_list: + assert_equal(markinnerspaces(i), i) + + def test_one_relevant_space(self): + assert_equal(markinnerspaces("a 'b c' \\\' \\\'"), "a 'b@_@c' \\' \\'") + assert_equal(markinnerspaces(r'a "b c" \" \"'), r'a "b@_@c" \" \"') + + def test_ignore_inner_quotes(self): + assert_equal(markinnerspaces('a \'b c" " d\' e'), + "a 'b@_@c\"@_@\"@_@d' e") + assert_equal(markinnerspaces('a "b c\' \' d" e'), + "a \"b@_@c'@_@'@_@d\" e") + + def test_multiple_relevant_spaces(self): + assert_equal(markinnerspaces("a 'b c' 'd e'"), "a 'b@_@c' 'd@_@e'") + assert_equal(markinnerspaces(r'a "b c" "d e"'), r'a "b@_@c" "d@_@e"') + + +class TestDimSpec(util.F2PyTest): + """This test suite tests various expressions that are used as dimension + specifications. + + There exists two usage cases where analyzing dimensions + specifications are important. + + In the first case, the size of output arrays must be defined based + on the inputs to a Fortran function. Because Fortran supports + arbitrary bases for indexing, for instance, `arr(lower:upper)`, + f2py has to evaluate an expression `upper - lower + 1` where + `lower` and `upper` are arbitrary expressions of input parameters. + The evaluation is performed in C, so f2py has to translate Fortran + expressions to valid C expressions (an alternative approach is + that a developer specifies the corresponding C expressions in a + .pyf file). + + In the second case, when user provides an input array with a given + size but some hidden parameters used in dimensions specifications + need to be determined based on the input array size. This is a + harder problem because f2py has to solve the inverse problem: find + a parameter `p` such that `upper(p) - lower(p) + 1` equals to the + size of input array. In the case when this equation cannot be + solved (e.g. because the input array size is wrong), raise an + error before calling the Fortran function (that otherwise would + likely crash Python process when the size of input arrays is + wrong). f2py currently supports this case only when the equation + is linear with respect to unknown parameter. + + """ + + suffix = '.f90' + + code_template = textwrap.dedent(""" + function get_arr_size_{count}(a, n) result (length) + integer, intent(in) :: n + integer, dimension({dimspec}), intent(out) :: a + integer length + length = size(a) + end function + + subroutine get_inv_arr_size_{count}(a, n) + integer :: n + ! the value of n is computed in f2py wrapper + !f2py intent(out) n + integer, dimension({dimspec}), intent(in) :: a + if (a({first}).gt.0) then + print*, "a=", a + endif + end subroutine + """) + + linear_dimspecs = ['n', '2*n', '2:n', 'n/2', '5 - n/2', '3*n:20', + 'n*(n+1):n*(n+5)'] + nonlinear_dimspecs = ['2*n:3*n*n+2*n'] + all_dimspecs = linear_dimspecs + nonlinear_dimspecs + + code = '' + for count, dimspec in enumerate(all_dimspecs): + code += code_template.format( + count=count, dimspec=dimspec, + first=dimspec.split(':')[0] if ':' in dimspec else '1') + + @pytest.mark.parametrize('dimspec', all_dimspecs) + def test_array_size(self, dimspec): + + count = self.all_dimspecs.index(dimspec) + get_arr_size = getattr(self.module, f'get_arr_size_{count}') + + for n in [1, 2, 3, 4, 5]: + sz, a = get_arr_size(n) + assert len(a) == sz + + @pytest.mark.parametrize('dimspec', all_dimspecs) + def test_inv_array_size(self, dimspec): + + count = self.all_dimspecs.index(dimspec) + get_arr_size = getattr(self.module, f'get_arr_size_{count}') + get_inv_arr_size = getattr(self.module, f'get_inv_arr_size_{count}') + + for n in [1, 2, 3, 4, 5]: + sz, a = get_arr_size(n) + if dimspec in self.nonlinear_dimspecs: + # one must specify n as input, the call we'll ensure + # that a and n are compatible: + n1 = get_inv_arr_size(a, n) + else: + # in case of linear dependence, n can be determined + # from the shape of a: + n1 = get_inv_arr_size(a) + # n1 may be different from n (for instance, when `a` size + # is a function of some `n` fraction) but it must produce + # the same sized array + sz1, _ = get_arr_size(n1) + assert sz == sz1, (n, n1, sz, sz1) + + +class TestModuleDeclaration(): + def test_dependencies(self, tmp_path): + f_path = tmp_path / "mod.f90" + with f_path.open('w') as ff: + ff.write(textwrap.dedent("""\ + module foo + type bar + character(len = 4) :: text + end type bar + type(bar), parameter :: abar = bar('abar') + end module foo + """)) + mod = crackfortran.crackfortran([str(f_path)]) + assert len(mod) == 1 + assert mod[0]['vars']['abar']['='] == "bar('abar')" diff --git a/numpy/f2py/tests/test_kind.py b/numpy/f2py/tests/test_kind.py index 1f7762a805f2..a7e2b28ed37c 100644 --- a/numpy/f2py/tests/test_kind.py +++ b/numpy/f2py/tests/test_kind.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - import os import pytest diff --git a/numpy/f2py/tests/test_mixed.py b/numpy/f2py/tests/test_mixed.py index 28268ecc024b..04266ca5b190 100644 --- a/numpy/f2py/tests/test_mixed.py +++ b/numpy/f2py/tests/test_mixed.py @@ -1,10 +1,8 @@ -from __future__ import division, absolute_import, print_function - import os import textwrap import pytest -from numpy.testing import assert_, assert_equal +from numpy.testing import assert_, assert_equal, IS_PYPY from . import util @@ -17,15 +15,15 @@ class TestMixed(util.F2PyTest): _path('src', 'mixed', 'foo_fixed.f90'), _path('src', 'mixed', 'foo_free.f90')] - @pytest.mark.slow def test_all(self): assert_(self.module.bar11() == 11) assert_(self.module.foo_fixed.bar12() == 12) assert_(self.module.foo_free.bar13() == 13) - @pytest.mark.slow + @pytest.mark.xfail(IS_PYPY, + reason="PyPy cannot modify tp_doc after PyType_Ready") def test_docstring(self): - expected = """ + expected = textwrap.dedent("""\ a = bar11() Wrapper for ``bar11``. @@ -33,6 +31,5 @@ def test_docstring(self): Returns ------- a : int - """ - assert_equal(self.module.bar11.__doc__, - textwrap.dedent(expected).lstrip()) + """) + assert_equal(self.module.bar11.__doc__, expected) diff --git a/numpy/f2py/tests/test_module_doc.py b/numpy/f2py/tests/test_module_doc.py new file mode 100644 index 000000000000..4b9555cee1fc --- /dev/null +++ b/numpy/f2py/tests/test_module_doc.py @@ -0,0 +1,30 @@ +import os +import sys +import pytest +import textwrap + +from . import util +from numpy.testing import assert_equal, IS_PYPY + + +def _path(*a): + return os.path.join(*((os.path.dirname(__file__),) + a)) + + +class TestModuleDocString(util.F2PyTest): + sources = [_path('src', 'module_data', 'module_data_docstring.f90')] + + @pytest.mark.skipif(sys.platform=='win32', + reason='Fails with MinGW64 Gfortran (Issue #9673)') + @pytest.mark.xfail(IS_PYPY, + reason="PyPy cannot modify tp_doc after PyType_Ready") + def test_module_docstring(self): + assert_equal(self.module.mod.__doc__, + textwrap.dedent('''\ + i : 'i'-scalar + x : 'i'-array(4) + a : 'f'-array(2,3) + b : 'f'-array(-1,-1), not allocated\x00 + foo()\n + Wrapper for ``foo``.\n\n''') + ) diff --git a/numpy/f2py/tests/test_parameter.py b/numpy/f2py/tests/test_parameter.py index a0bbd9460a22..b6182716987b 100644 --- a/numpy/f2py/tests/test_parameter.py +++ b/numpy/f2py/tests/test_parameter.py @@ -1,7 +1,4 @@ -from __future__ import division, absolute_import, print_function - import os -import math import pytest import numpy as np diff --git a/numpy/f2py/tests/test_quoted_character.py b/numpy/f2py/tests/test_quoted_character.py index 09fc7328fcf5..20c77666c59a 100644 --- a/numpy/f2py/tests/test_quoted_character.py +++ b/numpy/f2py/tests/test_quoted_character.py @@ -1,11 +1,13 @@ -from __future__ import division, absolute_import, print_function +"""See https://github.com/numpy/numpy/pull/10676. +""" import sys import pytest from numpy.testing import assert_equal from . import util + class TestQuotedCharacter(util.F2PyTest): code = """ SUBROUTINE FOO(OUT1, OUT2, OUT3, OUT4, OUT5, OUT6) diff --git a/numpy/f2py/tests/test_regression.py b/numpy/f2py/tests/test_regression.py index d695de61b75c..b91499e4adb3 100644 --- a/numpy/f2py/tests/test_regression.py +++ b/numpy/f2py/tests/test_regression.py @@ -1,11 +1,8 @@ -from __future__ import division, absolute_import, print_function - import os -import math import pytest import numpy as np -from numpy.testing import assert_raises, assert_equal +from numpy.testing import assert_, assert_raises, assert_equal, assert_string_equal from . import util @@ -28,3 +25,31 @@ def test_inout(self): x = np.arange(3, dtype=np.float32) self.module.foo(x) assert_equal(x, [3, 1, 2]) + + +class TestNumpyVersionAttribute(util.F2PyTest): + # Check that th attribute __f2py_numpy_version__ is present + # in the compiled module and that has the value np.__version__. + sources = [_path('src', 'regression', 'inout.f90')] + + @pytest.mark.slow + def test_numpy_version_attribute(self): + + # Check that self.module has an attribute named "__f2py_numpy_version__" + assert_(hasattr(self.module, "__f2py_numpy_version__"), + msg="Fortran module does not have __f2py_numpy_version__") + + # Check that the attribute __f2py_numpy_version__ is a string + assert_(isinstance(self.module.__f2py_numpy_version__, str), + msg="__f2py_numpy_version__ is not a string") + + # Check that __f2py_numpy_version__ has the value numpy.__version__ + assert_string_equal(np.__version__, self.module.__f2py_numpy_version__) + + +def test_include_path(): + incdir = np.f2py.get_include() + fnames_in_dir = os.listdir(incdir) + for fname in ('fortranobject.c', 'fortranobject.h'): + assert fname in fnames_in_dir + diff --git a/numpy/f2py/tests/test_return_character.py b/numpy/f2py/tests/test_return_character.py index 4a94c5b98245..2c999ed0b071 100644 --- a/numpy/f2py/tests/test_return_character.py +++ b/numpy/f2py/tests/test_return_character.py @@ -1,16 +1,15 @@ -from __future__ import division, absolute_import, print_function - import pytest from numpy import array from numpy.testing import assert_ from . import util +import platform +IS_S390X = platform.machine() == 's390x' class TestReturnCharacter(util.F2PyTest): - def check_function(self, t): - tname = t.__doc__.split()[0] + def check_function(self, t, tname): if tname in ['t0', 't1', 's0', 's1']: assert_(t(23) == b'2') r = t('ab') @@ -22,11 +21,11 @@ def check_function(self, t): #assert_(_raises(ValueError, t, array([77,87]))) #assert_(_raises(ValueError, t, array(77))) elif tname in ['ts', 'ss']: - assert_(t(23) == b'23 ', repr(t(23))) + assert_(t(23) == b'23', repr(t(23))) assert_(t('123456789abcdef') == b'123456789a') elif tname in ['t5', 's5']: - assert_(t(23) == b'23 ', repr(t(23))) - assert_(t('ab') == b'ab ', repr(t('ab'))) + assert_(t(23) == b'23', repr(t(23))) + assert_(t('ab') == b'ab', repr(t('ab'))) assert_(t('123456789abcdef') == b'12345') else: raise NotImplementedError @@ -81,10 +80,10 @@ class TestF77ReturnCharacter(TestReturnCharacter): end """ - @pytest.mark.slow - def test_all(self): - for name in "t0,t1,t5,s0,s1,s5,ss".split(","): - self.check_function(getattr(self.module, name)) + @pytest.mark.xfail(IS_S390X, reason="callback returns ' '") + @pytest.mark.parametrize('name', 't0,t1,t5,s0,s1,s5,ss'.split(',')) + def test_all(self, name): + self.check_function(getattr(self.module, name), name) class TestF90ReturnCharacter(TestReturnCharacter): @@ -140,7 +139,7 @@ class TestF90ReturnCharacter(TestReturnCharacter): end module f90_return_char """ - @pytest.mark.slow - def test_all(self): - for name in "t0,t1,t5,ts,s0,s1,s5,ss".split(","): - self.check_function(getattr(self.module.f90_return_char, name)) + @pytest.mark.xfail(IS_S390X, reason="callback returns ' '") + @pytest.mark.parametrize('name', 't0,t1,t5,ts,s0,s1,s5,ss'.split(',')) + def test_all(self, name): + self.check_function(getattr(self.module.f90_return_char, name), name) diff --git a/numpy/f2py/tests/test_return_complex.py b/numpy/f2py/tests/test_return_complex.py index 152cfc960a9f..3d2e2b94f27a 100644 --- a/numpy/f2py/tests/test_return_complex.py +++ b/numpy/f2py/tests/test_return_complex.py @@ -1,24 +1,20 @@ -from __future__ import division, absolute_import, print_function - import pytest from numpy import array -from numpy.compat import long from numpy.testing import assert_, assert_raises from . import util class TestReturnComplex(util.F2PyTest): - def check_function(self, t): - tname = t.__doc__.split()[0] + def check_function(self, t, tname): if tname in ['t0', 't8', 's0', 's8']: err = 1e-5 else: err = 0.0 assert_(abs(t(234j) - 234.0j) <= err) assert_(abs(t(234.6) - 234.6) <= err) - assert_(abs(t(long(234)) - 234.0) <= err) + assert_(abs(t(234) - 234.0) <= err) assert_(abs(t(234.6 + 3j) - (234.6 + 3j)) <= err) #assert_( abs(t('234')-234.)<=err) #assert_( abs(t('234.6')-234.6)<=err) @@ -104,10 +100,9 @@ class TestF77ReturnComplex(TestReturnComplex): end """ - @pytest.mark.slow - def test_all(self): - for name in "t0,t8,t16,td,s0,s8,s16,sd".split(","): - self.check_function(getattr(self.module, name)) + @pytest.mark.parametrize('name', 't0,t8,t16,td,s0,s8,s16,sd'.split(',')) + def test_all(self, name): + self.check_function(getattr(self.module, name), name) class TestF90ReturnComplex(TestReturnComplex): @@ -163,7 +158,6 @@ class TestF90ReturnComplex(TestReturnComplex): end module f90_return_complex """ - @pytest.mark.slow - def test_all(self): - for name in "t0,t8,t16,td,s0,s8,s16,sd".split(","): - self.check_function(getattr(self.module.f90_return_complex, name)) + @pytest.mark.parametrize('name', 't0,t8,t16,td,s0,s8,s16,sd'.split(',')) + def test_all(self, name): + self.check_function(getattr(self.module.f90_return_complex, name), name) diff --git a/numpy/f2py/tests/test_return_integer.py b/numpy/f2py/tests/test_return_integer.py index 7a4b07c4f2b0..0a8121dc14b8 100644 --- a/numpy/f2py/tests/test_return_integer.py +++ b/numpy/f2py/tests/test_return_integer.py @@ -1,19 +1,15 @@ -from __future__ import division, absolute_import, print_function - import pytest from numpy import array -from numpy.compat import long from numpy.testing import assert_, assert_raises from . import util class TestReturnInteger(util.F2PyTest): - def check_function(self, t): + def check_function(self, t, tname): assert_(t(123) == 123, repr(t(123))) assert_(t(123.6) == 123) - assert_(t(long(123)) == 123) assert_(t('123') == 123) assert_(t(-123) == -123) assert_(t([123]) == 123) @@ -38,7 +34,7 @@ def check_function(self, t): assert_raises(Exception, t, t) assert_raises(Exception, t, {}) - if t.__doc__.split()[0] in ['t8', 's8']: + if tname in ['t8', 's8']: assert_raises(OverflowError, t, 100000000000000000000000) assert_raises(OverflowError, t, 10000000011111111111111.23) @@ -103,10 +99,10 @@ class TestF77ReturnInteger(TestReturnInteger): end """ - @pytest.mark.slow - def test_all(self): - for name in "t0,t1,t2,t4,t8,s0,s1,s2,s4,s8".split(","): - self.check_function(getattr(self.module, name)) + @pytest.mark.parametrize('name', + 't0,t1,t2,t4,t8,s0,s1,s2,s4,s8'.split(',')) + def test_all(self, name): + self.check_function(getattr(self.module, name), name) class TestF90ReturnInteger(TestReturnInteger): @@ -173,7 +169,7 @@ class TestF90ReturnInteger(TestReturnInteger): end module f90_return_integer """ - @pytest.mark.slow - def test_all(self): - for name in "t0,t1,t2,t4,t8,s0,s1,s2,s4,s8".split(","): - self.check_function(getattr(self.module.f90_return_integer, name)) + @pytest.mark.parametrize('name', + 't0,t1,t2,t4,t8,s0,s1,s2,s4,s8'.split(',')) + def test_all(self, name): + self.check_function(getattr(self.module.f90_return_integer, name), name) diff --git a/numpy/f2py/tests/test_return_logical.py b/numpy/f2py/tests/test_return_logical.py index 403f4e205aa8..9db939c7e066 100644 --- a/numpy/f2py/tests/test_return_logical.py +++ b/numpy/f2py/tests/test_return_logical.py @@ -1,9 +1,6 @@ -from __future__ import division, absolute_import, print_function - import pytest from numpy import array -from numpy.compat import long from numpy.testing import assert_, assert_raises from . import util @@ -20,7 +17,6 @@ def check_function(self, t): assert_(t(1j) == 1) assert_(t(234) == 1) assert_(t(234.6) == 1) - assert_(t(long(234)) == 1) assert_(t(234.6 + 3j) == 1) assert_(t('234') == 1) assert_(t('aaa') == 1) @@ -113,9 +109,9 @@ class TestF77ReturnLogical(TestReturnLogical): """ @pytest.mark.slow - def test_all(self): - for name in "t0,t1,t2,t4,s0,s1,s2,s4".split(","): - self.check_function(getattr(self.module, name)) + @pytest.mark.parametrize('name', 't0,t1,t2,t4,s0,s1,s2,s4'.split(',')) + def test_all(self, name): + self.check_function(getattr(self.module, name)) class TestF90ReturnLogical(TestReturnLogical): @@ -183,6 +179,7 @@ class TestF90ReturnLogical(TestReturnLogical): """ @pytest.mark.slow - def test_all(self): - for name in "t0,t1,t2,t4,t8,s0,s1,s2,s4,s8".split(","): - self.check_function(getattr(self.module.f90_return_logical, name)) + @pytest.mark.parametrize('name', + 't0,t1,t2,t4,t8,s0,s1,s2,s4,s8'.split(',')) + def test_all(self, name): + self.check_function(getattr(self.module.f90_return_logical, name)) diff --git a/numpy/f2py/tests/test_return_real.py b/numpy/f2py/tests/test_return_real.py index fcb13e1e0cd9..8e5022a8ec97 100644 --- a/numpy/f2py/tests/test_return_real.py +++ b/numpy/f2py/tests/test_return_real.py @@ -1,23 +1,20 @@ -from __future__ import division, absolute_import, print_function - +import platform import pytest from numpy import array -from numpy.compat import long from numpy.testing import assert_, assert_raises from . import util class TestReturnReal(util.F2PyTest): - def check_function(self, t): - if t.__doc__.split()[0] in ['t0', 't4', 's0', 's4']: + def check_function(self, t, tname): + if tname in ['t0', 't4', 's0', 's4']: err = 1e-5 else: err = 0.0 assert_(abs(t(234) - 234.0) <= err) assert_(abs(t(234.6) - 234.6) <= err) - assert_(abs(t(long(234)) - 234.0) <= err) assert_(abs(t('234') - 234) <= err) assert_(abs(t('234.6') - 234.6) <= err) assert_(abs(t(-234) + 234) <= err) @@ -33,7 +30,7 @@ def check_function(self, t): assert_(abs(t(array([234], 'B')) - 234.) <= err) assert_(abs(t(array([234], 'f')) - 234.) <= err) assert_(abs(t(array([234], 'd')) - 234.) <= err) - if t.__doc__.split()[0] in ['t0', 't4', 's0', 's4']: + if tname in ['t0', 't4', 's0', 's4']: assert_(t(1e200) == t(1e300)) # inf #assert_raises(ValueError, t, array([234], 'S1')) @@ -52,6 +49,11 @@ def check_function(self, t): pass + +@pytest.mark.skipif( + platform.system() == 'Darwin', + reason="Prone to error when run with numpy/f2py/tests on mac os, " + "but not when run in isolation") class TestCReturnReal(TestReturnReal): suffix = ".pyf" module_name = "c_ext_return_real" @@ -84,10 +86,9 @@ class TestCReturnReal(TestReturnReal): end python module c_ext_return_real """ - @pytest.mark.slow - def test_all(self): - for name in "t4,t8,s4,s8".split(","): - self.check_function(getattr(self.module, name)) + @pytest.mark.parametrize('name', 't4,t8,s4,s8'.split(',')) + def test_all(self, name): + self.check_function(getattr(self.module, name), name) class TestF77ReturnReal(TestReturnReal): @@ -139,10 +140,9 @@ class TestF77ReturnReal(TestReturnReal): end """ - @pytest.mark.slow - def test_all(self): - for name in "t0,t4,t8,td,s0,s4,s8,sd".split(","): - self.check_function(getattr(self.module, name)) + @pytest.mark.parametrize('name', 't0,t4,t8,td,s0,s4,s8,sd'.split(',')) + def test_all(self, name): + self.check_function(getattr(self.module, name), name) class TestF90ReturnReal(TestReturnReal): @@ -198,7 +198,6 @@ class TestF90ReturnReal(TestReturnReal): end module f90_return_real """ - @pytest.mark.slow - def test_all(self): - for name in "t0,t4,t8,td,s0,s4,s8,sd".split(","): - self.check_function(getattr(self.module.f90_return_real, name)) + @pytest.mark.parametrize('name', 't0,t4,t8,td,s0,s4,s8,sd'.split(',')) + def test_all(self, name): + self.check_function(getattr(self.module.f90_return_real, name), name) diff --git a/numpy/f2py/tests/test_semicolon_split.py b/numpy/f2py/tests/test_semicolon_split.py index 2b0f32727ca0..d8b4bf222122 100644 --- a/numpy/f2py/tests/test_semicolon_split.py +++ b/numpy/f2py/tests/test_semicolon_split.py @@ -1,8 +1,13 @@ -from __future__ import division, absolute_import, print_function +import platform +import pytest from . import util from numpy.testing import assert_equal +@pytest.mark.skipif( + platform.system() == 'Darwin', + reason="Prone to error when run with numpy/f2py/tests on mac os, " + "but not when run in isolation") class TestMultiline(util.F2PyTest): suffix = ".pyf" module_name = "multiline" @@ -26,6 +31,11 @@ class TestMultiline(util.F2PyTest): def test_multiline(self): assert_equal(self.module.foo(), 42) + +@pytest.mark.skipif( + platform.system() == 'Darwin', + reason="Prone to error when run with numpy/f2py/tests on mac os, " + "but not when run in isolation") class TestCallstatement(util.F2PyTest): suffix = ".pyf" module_name = "callstatement" diff --git a/numpy/f2py/tests/test_size.py b/numpy/f2py/tests/test_size.py index e2af6180489b..b609fa77f711 100644 --- a/numpy/f2py/tests/test_size.py +++ b/numpy/f2py/tests/test_size.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - import os import pytest diff --git a/numpy/f2py/tests/test_string.py b/numpy/f2py/tests/test_string.py index 0493c99cf1dd..7b27f8786ed6 100644 --- a/numpy/f2py/tests/test_string.py +++ b/numpy/f2py/tests/test_string.py @@ -1,8 +1,6 @@ -from __future__ import division, absolute_import, print_function - import os import pytest - +import textwrap from numpy.testing import assert_array_equal import numpy as np from . import util @@ -11,14 +9,158 @@ def _path(*a): return os.path.join(*((os.path.dirname(__file__),) + a)) + class TestString(util.F2PyTest): sources = [_path('src', 'string', 'char.f90')] @pytest.mark.slow def test_char(self): strings = np.array(['ab', 'cd', 'ef'], dtype='c').T - inp, out = self.module.char_test.change_strings(strings, strings.shape[1]) + inp, out = self.module.char_test.change_strings(strings, + strings.shape[1]) assert_array_equal(inp, strings) expected = strings.copy() expected[1, :] = 'AAA' assert_array_equal(out, expected) + + +class TestDocStringArguments(util.F2PyTest): + suffix = '.f' + + code = """ +C FILE: STRING.F + SUBROUTINE FOO(A,B,C,D) + CHARACTER*5 A, B + CHARACTER*(*) C,D +Cf2py intent(in) a,c +Cf2py intent(inout) b,d + PRINT*, "A=",A + PRINT*, "B=",B + PRINT*, "C=",C + PRINT*, "D=",D + PRINT*, "CHANGE A,B,C,D" + A(1:1) = 'A' + B(1:1) = 'B' + C(1:1) = 'C' + D(1:1) = 'D' + PRINT*, "A=",A + PRINT*, "B=",B + PRINT*, "C=",C + PRINT*, "D=",D + END +C END OF FILE STRING.F + """ + + def test_example(self): + a = np.array(b'123\0\0') + b = np.array(b'123\0\0') + c = np.array(b'123') + d = np.array(b'123') + + self.module.foo(a, b, c, d) + + assert a.tobytes() == b'123\0\0' + assert b.tobytes() == b'B23\0\0', (b.tobytes(),) + assert c.tobytes() == b'123' + assert d.tobytes() == b'D23' + + +class TestFixedString(util.F2PyTest): + suffix = '.f90' + + code = textwrap.dedent(""" + function sint(s) result(i) + implicit none + character(len=*) :: s + integer :: j, i + i = 0 + do j=len(s), 1, -1 + if (.not.((i.eq.0).and.(s(j:j).eq.' '))) then + i = i + ichar(s(j:j)) * 10 ** (j - 1) + endif + end do + return + end function sint + + function test_in_bytes4(a) result (i) + implicit none + integer :: sint + character(len=4) :: a + integer :: i + i = sint(a) + a(1:1) = 'A' + return + end function test_in_bytes4 + + function test_inout_bytes4(a) result (i) + implicit none + integer :: sint + character(len=4), intent(inout) :: a + integer :: i + if (a(1:1).ne.' ') then + a(1:1) = 'E' + endif + i = sint(a) + return + end function test_inout_bytes4 + """) + + @staticmethod + def _sint(s, start=0, end=None): + """Return the content of a string buffer as integer value. + + For example: + _sint('1234') -> 4321 + _sint('123A') -> 17321 + """ + if isinstance(s, np.ndarray): + s = s.tobytes() + elif isinstance(s, str): + s = s.encode() + assert isinstance(s, bytes) + if end is None: + end = len(s) + i = 0 + for j in range(start, min(end, len(s))): + i += s[j] * 10 ** j + return i + + def _get_input(self, intent='in'): + if intent in ['in']: + yield '' + yield '1' + yield '1234' + yield '12345' + yield b'' + yield b'\0' + yield b'1' + yield b'\01' + yield b'1\0' + yield b'1234' + yield b'12345' + yield np.ndarray((), np.bytes_, buffer=b'') # array(b'', dtype='|S0') + yield np.array(b'') # array(b'', dtype='|S1') + yield np.array(b'\0') + yield np.array(b'1') + yield np.array(b'1\0') + yield np.array(b'\01') + yield np.array(b'1234') + yield np.array(b'123\0') + yield np.array(b'12345') + + def test_intent_in(self): + for s in self._get_input(): + r = self.module.test_in_bytes4(s) + # also checks that s is not changed inplace + expected = self._sint(s, end=4) + assert r == expected, (s) + + def test_intent_inout(self): + for s in self._get_input(intent='inout'): + rest = self._sint(s, start=4) + r = self.module.test_inout_bytes4(s) + expected = self._sint(s, end=4) + assert r == expected + + # check that the rest of input string is preserved + assert rest == self._sint(s, start=4) diff --git a/numpy/f2py/tests/test_symbolic.py b/numpy/f2py/tests/test_symbolic.py new file mode 100644 index 000000000000..52cabac530d5 --- /dev/null +++ b/numpy/f2py/tests/test_symbolic.py @@ -0,0 +1,462 @@ +from numpy.testing import assert_raises +from numpy.f2py.symbolic import ( + Expr, Op, ArithOp, Language, + as_symbol, as_number, as_string, as_array, as_complex, + as_terms, as_factors, eliminate_quotes, insert_quotes, + fromstring, as_expr, as_apply, + as_numer_denom, as_ternary, as_ref, as_deref, + normalize, as_eq, as_ne, as_lt, as_gt, as_le, as_ge + ) +from . import util + + +class TestSymbolic(util.F2PyTest): + + def test_eliminate_quotes(self): + def worker(s): + r, d = eliminate_quotes(s) + s1 = insert_quotes(r, d) + assert s1 == s + + for kind in ['', 'mykind_']: + worker(kind + '"1234" // "ABCD"') + worker(kind + '"1234" // ' + kind + '"ABCD"') + worker(kind + '"1234" // \'ABCD\'') + worker(kind + '"1234" // ' + kind + '\'ABCD\'') + worker(kind + '"1\\"2\'AB\'34"') + worker('a = ' + kind + "'1\\'2\"AB\"34'") + + def test_sanity(self): + x = as_symbol('x') + y = as_symbol('y') + z = as_symbol('z') + + assert x.op == Op.SYMBOL + assert repr(x) == "Expr(Op.SYMBOL, 'x')" + assert x == x + assert x != y + assert hash(x) is not None + + n = as_number(123) + m = as_number(456) + assert n.op == Op.INTEGER + assert repr(n) == "Expr(Op.INTEGER, (123, 4))" + assert n == n + assert n != m + assert hash(n) is not None + + fn = as_number(12.3) + fm = as_number(45.6) + assert fn.op == Op.REAL + assert repr(fn) == "Expr(Op.REAL, (12.3, 4))" + assert fn == fn + assert fn != fm + assert hash(fn) is not None + + c = as_complex(1, 2) + c2 = as_complex(3, 4) + assert c.op == Op.COMPLEX + assert repr(c) == ("Expr(Op.COMPLEX, (Expr(Op.INTEGER, (1, 4))," + " Expr(Op.INTEGER, (2, 4))))") + assert c == c + assert c != c2 + assert hash(c) is not None + + s = as_string("'123'") + s2 = as_string('"ABC"') + assert s.op == Op.STRING + assert repr(s) == "Expr(Op.STRING, (\"'123'\", 1))", repr(s) + assert s == s + assert s != s2 + + a = as_array((n, m)) + b = as_array((n,)) + assert a.op == Op.ARRAY + assert repr(a) == ("Expr(Op.ARRAY, (Expr(Op.INTEGER, (123, 4))," + " Expr(Op.INTEGER, (456, 4))))") + assert a == a + assert a != b + + t = as_terms(x) + u = as_terms(y) + assert t.op == Op.TERMS + assert repr(t) == "Expr(Op.TERMS, {Expr(Op.SYMBOL, 'x'): 1})" + assert t == t + assert t != u + assert hash(t) is not None + + v = as_factors(x) + w = as_factors(y) + assert v.op == Op.FACTORS + assert repr(v) == "Expr(Op.FACTORS, {Expr(Op.SYMBOL, 'x'): 1})" + assert v == v + assert w != v + assert hash(v) is not None + + t = as_ternary(x, y, z) + u = as_ternary(x, z, y) + assert t.op == Op.TERNARY + assert t == t + assert t != u + assert hash(t) is not None + + e = as_eq(x, y) + f = as_lt(x, y) + assert e.op == Op.RELATIONAL + assert e == e + assert e != f + assert hash(e) is not None + + def test_tostring_fortran(self): + x = as_symbol('x') + y = as_symbol('y') + z = as_symbol('z') + n = as_number(123) + m = as_number(456) + a = as_array((n, m)) + c = as_complex(n, m) + + assert str(x) == 'x' + assert str(n) == '123' + assert str(a) == '[123, 456]' + assert str(c) == '(123, 456)' + + assert str(Expr(Op.TERMS, {x: 1})) == 'x' + assert str(Expr(Op.TERMS, {x: 2})) == '2 * x' + assert str(Expr(Op.TERMS, {x: -1})) == '-x' + assert str(Expr(Op.TERMS, {x: -2})) == '-2 * x' + assert str(Expr(Op.TERMS, {x: 1, y: 1})) == 'x + y' + assert str(Expr(Op.TERMS, {x: -1, y: -1})) == '-x - y' + assert str(Expr(Op.TERMS, {x: 2, y: 3})) == '2 * x + 3 * y' + assert str(Expr(Op.TERMS, {x: -2, y: 3})) == '-2 * x + 3 * y' + assert str(Expr(Op.TERMS, {x: 2, y: -3})) == '2 * x - 3 * y' + + assert str(Expr(Op.FACTORS, {x: 1})) == 'x' + assert str(Expr(Op.FACTORS, {x: 2})) == 'x ** 2' + assert str(Expr(Op.FACTORS, {x: -1})) == 'x ** -1' + assert str(Expr(Op.FACTORS, {x: -2})) == 'x ** -2' + assert str(Expr(Op.FACTORS, {x: 1, y: 1})) == 'x * y' + assert str(Expr(Op.FACTORS, {x: 2, y: 3})) == 'x ** 2 * y ** 3' + + v = Expr(Op.FACTORS, {x: 2, Expr(Op.TERMS, {x: 1, y: 1}): 3}) + assert str(v) == 'x ** 2 * (x + y) ** 3', str(v) + v = Expr(Op.FACTORS, {x: 2, Expr(Op.FACTORS, {x: 1, y: 1}): 3}) + assert str(v) == 'x ** 2 * (x * y) ** 3', str(v) + + assert str(Expr(Op.APPLY, ('f', (), {}))) == 'f()' + assert str(Expr(Op.APPLY, ('f', (x,), {}))) == 'f(x)' + assert str(Expr(Op.APPLY, ('f', (x, y), {}))) == 'f(x, y)' + assert str(Expr(Op.INDEXING, ('f', x))) == 'f[x]' + + assert str(as_ternary(x, y, z)) == 'merge(y, z, x)' + assert str(as_eq(x, y)) == 'x .eq. y' + assert str(as_ne(x, y)) == 'x .ne. y' + assert str(as_lt(x, y)) == 'x .lt. y' + assert str(as_le(x, y)) == 'x .le. y' + assert str(as_gt(x, y)) == 'x .gt. y' + assert str(as_ge(x, y)) == 'x .ge. y' + + def test_tostring_c(self): + language = Language.C + x = as_symbol('x') + y = as_symbol('y') + z = as_symbol('z') + n = as_number(123) + + assert Expr(Op.FACTORS, {x: 2}).tostring(language=language) == 'x * x' + assert Expr(Op.FACTORS, {x + y: 2}).tostring( + language=language) == '(x + y) * (x + y)' + assert Expr(Op.FACTORS, {x: 12}).tostring( + language=language) == 'pow(x, 12)' + + assert as_apply(ArithOp.DIV, x, y).tostring( + language=language) == 'x / y' + assert as_apply(ArithOp.DIV, x, x + y).tostring( + language=language) == 'x / (x + y)' + assert as_apply(ArithOp.DIV, x - y, x + y).tostring( + language=language) == '(x - y) / (x + y)' + assert (x + (x - y) / (x + y) + n).tostring( + language=language) == '123 + x + (x - y) / (x + y)' + + assert as_ternary(x, y, z).tostring(language=language) == '(x ? y : z)' + assert as_eq(x, y).tostring(language=language) == 'x == y' + assert as_ne(x, y).tostring(language=language) == 'x != y' + assert as_lt(x, y).tostring(language=language) == 'x < y' + assert as_le(x, y).tostring(language=language) == 'x <= y' + assert as_gt(x, y).tostring(language=language) == 'x > y' + assert as_ge(x, y).tostring(language=language) == 'x >= y' + + def test_operations(self): + x = as_symbol('x') + y = as_symbol('y') + z = as_symbol('z') + + assert x + x == Expr(Op.TERMS, {x: 2}) + assert x - x == Expr(Op.INTEGER, (0, 4)) + assert x + y == Expr(Op.TERMS, {x: 1, y: 1}) + assert x - y == Expr(Op.TERMS, {x: 1, y: -1}) + assert x * x == Expr(Op.FACTORS, {x: 2}) + assert x * y == Expr(Op.FACTORS, {x: 1, y: 1}) + + assert +x == x + assert -x == Expr(Op.TERMS, {x: -1}), repr(-x) + assert 2 * x == Expr(Op.TERMS, {x: 2}) + assert 2 + x == Expr(Op.TERMS, {x: 1, as_number(1): 2}) + assert 2 * x + 3 * y == Expr(Op.TERMS, {x: 2, y: 3}) + assert (x + y) * 2 == Expr(Op.TERMS, {x: 2, y: 2}) + + assert x ** 2 == Expr(Op.FACTORS, {x: 2}) + assert (x + y) ** 2 == Expr(Op.TERMS, + {Expr(Op.FACTORS, {x: 2}): 1, + Expr(Op.FACTORS, {y: 2}): 1, + Expr(Op.FACTORS, {x: 1, y: 1}): 2}) + assert (x + y) * x == x ** 2 + x * y + assert (x + y) ** 2 == x ** 2 + 2 * x * y + y ** 2 + assert (x + y) ** 2 + (x - y) ** 2 == 2 * x ** 2 + 2 * y ** 2 + assert (x + y) * z == x * z + y * z + assert z * (x + y) == x * z + y * z + + assert (x / 2) == as_apply(ArithOp.DIV, x, as_number(2)) + assert (2 * x / 2) == x + assert (3 * x / 2) == as_apply(ArithOp.DIV, 3*x, as_number(2)) + assert (4 * x / 2) == 2 * x + assert (5 * x / 2) == as_apply(ArithOp.DIV, 5*x, as_number(2)) + assert (6 * x / 2) == 3 * x + assert ((3*5) * x / 6) == as_apply(ArithOp.DIV, 5*x, as_number(2)) + assert (30*x**2*y**4 / (24*x**3*y**3)) == as_apply(ArithOp.DIV, + 5*y, 4*x) + assert ((15 * x / 6) / 5) == as_apply( + ArithOp.DIV, x, as_number(2)), ((15 * x / 6) / 5) + assert (x / (5 / x)) == as_apply(ArithOp.DIV, x**2, as_number(5)) + + assert (x / 2.0) == Expr(Op.TERMS, {x: 0.5}) + + s = as_string('"ABC"') + t = as_string('"123"') + + assert s // t == Expr(Op.STRING, ('"ABC123"', 1)) + assert s // x == Expr(Op.CONCAT, (s, x)) + assert x // s == Expr(Op.CONCAT, (x, s)) + + c = as_complex(1., 2.) + assert -c == as_complex(-1., -2.) + assert c + c == as_expr((1+2j)*2) + assert c * c == as_expr((1+2j)**2) + + def test_substitute(self): + x = as_symbol('x') + y = as_symbol('y') + z = as_symbol('z') + a = as_array((x, y)) + + assert x.substitute({x: y}) == y + assert (x + y).substitute({x: z}) == y + z + assert (x * y).substitute({x: z}) == y * z + assert (x ** 4).substitute({x: z}) == z ** 4 + assert (x / y).substitute({x: z}) == z / y + assert x.substitute({x: y + z}) == y + z + assert a.substitute({x: y + z}) == as_array((y + z, y)) + + assert as_ternary(x, y, z).substitute( + {x: y + z}) == as_ternary(y + z, y, z) + assert as_eq(x, y).substitute( + {x: y + z}) == as_eq(y + z, y) + + def test_fromstring(self): + + x = as_symbol('x') + y = as_symbol('y') + z = as_symbol('z') + f = as_symbol('f') + s = as_string('"ABC"') + t = as_string('"123"') + a = as_array((x, y)) + + assert fromstring('x') == x + assert fromstring('+ x') == x + assert fromstring('- x') == -x + assert fromstring('x + y') == x + y + assert fromstring('x + 1') == x + 1 + assert fromstring('x * y') == x * y + assert fromstring('x * 2') == x * 2 + assert fromstring('x / y') == x / y + assert fromstring('x ** 2', + language=Language.Python) == x ** 2 + assert fromstring('x ** 2 ** 3', + language=Language.Python) == x ** 2 ** 3 + assert fromstring('(x + y) * z') == (x + y) * z + + assert fromstring('f(x)') == f(x) + assert fromstring('f(x,y)') == f(x, y) + assert fromstring('f[x]') == f[x] + assert fromstring('f[x][y]') == f[x][y] + + assert fromstring('"ABC"') == s + assert normalize(fromstring('"ABC" // "123" ', + language=Language.Fortran)) == s // t + assert fromstring('f("ABC")') == f(s) + assert fromstring('MYSTRKIND_"ABC"') == as_string('"ABC"', 'MYSTRKIND') + + assert fromstring('(/x, y/)') == a, fromstring('(/x, y/)') + assert fromstring('f((/x, y/))') == f(a) + assert fromstring('(/(x+y)*z/)') == as_array(((x+y)*z,)) + + assert fromstring('123') == as_number(123) + assert fromstring('123_2') == as_number(123, 2) + assert fromstring('123_myintkind') == as_number(123, 'myintkind') + + assert fromstring('123.0') == as_number(123.0, 4) + assert fromstring('123.0_4') == as_number(123.0, 4) + assert fromstring('123.0_8') == as_number(123.0, 8) + assert fromstring('123.0e0') == as_number(123.0, 4) + assert fromstring('123.0d0') == as_number(123.0, 8) + assert fromstring('123d0') == as_number(123.0, 8) + assert fromstring('123e-0') == as_number(123.0, 4) + assert fromstring('123d+0') == as_number(123.0, 8) + assert fromstring('123.0_myrealkind') == as_number(123.0, 'myrealkind') + assert fromstring('3E4') == as_number(30000.0, 4) + + assert fromstring('(1, 2)') == as_complex(1, 2) + assert fromstring('(1e2, PI)') == as_complex( + as_number(100.0), as_symbol('PI')) + + assert fromstring('[1, 2]') == as_array((as_number(1), as_number(2))) + + assert fromstring('POINT(x, y=1)') == as_apply( + as_symbol('POINT'), x, y=as_number(1)) + assert (fromstring('PERSON(name="John", age=50, shape=(/34, 23/))') + == as_apply(as_symbol('PERSON'), + name=as_string('"John"'), + age=as_number(50), + shape=as_array((as_number(34), as_number(23))))) + + assert fromstring('x?y:z') == as_ternary(x, y, z) + + assert fromstring('*x') == as_deref(x) + assert fromstring('**x') == as_deref(as_deref(x)) + assert fromstring('&x') == as_ref(x) + assert fromstring('(*x) * (*y)') == as_deref(x) * as_deref(y) + assert fromstring('(*x) * *y') == as_deref(x) * as_deref(y) + assert fromstring('*x * *y') == as_deref(x) * as_deref(y) + assert fromstring('*x**y') == as_deref(x) * as_deref(y) + + assert fromstring('x == y') == as_eq(x, y) + assert fromstring('x != y') == as_ne(x, y) + assert fromstring('x < y') == as_lt(x, y) + assert fromstring('x > y') == as_gt(x, y) + assert fromstring('x <= y') == as_le(x, y) + assert fromstring('x >= y') == as_ge(x, y) + + assert fromstring('x .eq. y', language=Language.Fortran) == as_eq(x, y) + assert fromstring('x .ne. y', language=Language.Fortran) == as_ne(x, y) + assert fromstring('x .lt. y', language=Language.Fortran) == as_lt(x, y) + assert fromstring('x .gt. y', language=Language.Fortran) == as_gt(x, y) + assert fromstring('x .le. y', language=Language.Fortran) == as_le(x, y) + assert fromstring('x .ge. y', language=Language.Fortran) == as_ge(x, y) + + def test_traverse(self): + x = as_symbol('x') + y = as_symbol('y') + z = as_symbol('z') + f = as_symbol('f') + + # Use traverse to substitute a symbol + def replace_visit(s, r=z): + if s == x: + return r + + assert x.traverse(replace_visit) == z + assert y.traverse(replace_visit) == y + assert z.traverse(replace_visit) == z + assert (f(y)).traverse(replace_visit) == f(y) + assert (f(x)).traverse(replace_visit) == f(z) + assert (f[y]).traverse(replace_visit) == f[y] + assert (f[z]).traverse(replace_visit) == f[z] + assert (x + y + z).traverse(replace_visit) == (2 * z + y) + assert (x + f(y, x - z)).traverse( + replace_visit) == (z + f(y, as_number(0))) + assert as_eq(x, y).traverse(replace_visit) == as_eq(z, y) + + # Use traverse to collect symbols, method 1 + function_symbols = set() + symbols = set() + + def collect_symbols(s): + if s.op is Op.APPLY: + oper = s.data[0] + function_symbols.add(oper) + if oper in symbols: + symbols.remove(oper) + elif s.op is Op.SYMBOL and s not in function_symbols: + symbols.add(s) + + (x + f(y, x - z)).traverse(collect_symbols) + assert function_symbols == {f} + assert symbols == {x, y, z} + + # Use traverse to collect symbols, method 2 + def collect_symbols2(expr, symbols): + if expr.op is Op.SYMBOL: + symbols.add(expr) + + symbols = set() + (x + f(y, x - z)).traverse(collect_symbols2, symbols) + assert symbols == {x, y, z, f} + + # Use traverse to partially collect symbols + def collect_symbols3(expr, symbols): + if expr.op is Op.APPLY: + # skip traversing function calls + return expr + if expr.op is Op.SYMBOL: + symbols.add(expr) + + symbols = set() + (x + f(y, x - z)).traverse(collect_symbols3, symbols) + assert symbols == {x} + + def test_linear_solve(self): + x = as_symbol('x') + y = as_symbol('y') + z = as_symbol('z') + + assert x.linear_solve(x) == (as_number(1), as_number(0)) + assert (x+1).linear_solve(x) == (as_number(1), as_number(1)) + assert (2*x).linear_solve(x) == (as_number(2), as_number(0)) + assert (2*x+3).linear_solve(x) == (as_number(2), as_number(3)) + assert as_number(3).linear_solve(x) == (as_number(0), as_number(3)) + assert y.linear_solve(x) == (as_number(0), y) + assert (y*z).linear_solve(x) == (as_number(0), y * z) + + assert (x+y).linear_solve(x) == (as_number(1), y) + assert (z*x+y).linear_solve(x) == (z, y) + assert ((z+y)*x+y).linear_solve(x) == (z + y, y) + assert (z*y*x+y).linear_solve(x) == (z * y, y) + + assert_raises(RuntimeError, lambda: (x*x).linear_solve(x)) + + def test_as_numer_denom(self): + x = as_symbol('x') + y = as_symbol('y') + n = as_number(123) + + assert as_numer_denom(x) == (x, as_number(1)) + assert as_numer_denom(x / n) == (x, n) + assert as_numer_denom(n / x) == (n, x) + assert as_numer_denom(x / y) == (x, y) + assert as_numer_denom(x * y) == (x * y, as_number(1)) + assert as_numer_denom(n + x / y) == (x + n * y, y) + assert as_numer_denom(n + x / (y - x / n)) == (y * n ** 2, y * n - x) + + def test_polynomial_atoms(self): + x = as_symbol('x') + y = as_symbol('y') + n = as_number(123) + + assert x.polynomial_atoms() == {x} + assert n.polynomial_atoms() == set() + assert (y[x]).polynomial_atoms() == {y[x]} + assert (y(x)).polynomial_atoms() == {y(x)} + assert (y(x) + x).polynomial_atoms() == {y(x), x} + assert (y(x) * x[y]).polynomial_atoms() == {y(x), x[y]} + assert (y(x) ** x).polynomial_atoms() == {y(x)} diff --git a/numpy/f2py/tests/util.py b/numpy/f2py/tests/util.py index 466fd4970365..1a6805e751cd 100644 --- a/numpy/f2py/tests/util.py +++ b/numpy/f2py/tests/util.py @@ -5,8 +5,6 @@ - detecting if compilers are present """ -from __future__ import division, absolute_import, print_function - import os import sys import subprocess @@ -15,24 +13,18 @@ import atexit import textwrap import re -import random import pytest -import numpy.f2py from numpy.compat import asbytes, asstr -from numpy.testing import SkipTest, temppath +from numpy.testing import temppath from importlib import import_module -try: - from hashlib import md5 -except ImportError: - from md5 import new as md5 - # # Maintaining a temporary module directory # _module_dir = None +_module_num = 5403 def _cleanup(): @@ -44,7 +36,7 @@ def _cleanup(): pass try: shutil.rmtree(_module_dir) - except (IOError, OSError): + except OSError: pass _module_dir = None @@ -61,13 +53,14 @@ def get_module_dir(): def get_temp_module_name(): # Assume single-threaded, and the module dir usable only by this thread + global _module_num d = get_module_dir() - for j in range(5403, 9999999): - name = "_test_ext_module_%d" % j - fn = os.path.join(d, name) - if name not in sys.modules and not os.path.isfile(fn + '.py'): - return name - raise RuntimeError("Failed to create a temporary module name") + name = "_test_ext_module_%d" % _module_num + _module_num += 1 + if name in sys.modules: + # this should not be possible, but check anyway + raise RuntimeError("Temporary module name already in use.") + return name def _memoize(func): @@ -107,6 +100,7 @@ def build_module(source_files, options=[], skip=[], only=[], module_name=None): # Copy files dst_sources = [] + f2py_sources = [] for fn in source_files: if not os.path.isfile(fn): raise RuntimeError("%s is not a file" % fn) @@ -114,16 +108,14 @@ def build_module(source_files, options=[], skip=[], only=[], module_name=None): shutil.copyfile(fn, dst) dst_sources.append(dst) - fn = os.path.join(os.path.dirname(fn), '.f2py_f2cmap') - if os.path.isfile(fn): - dst = os.path.join(d, os.path.basename(fn)) - if not os.path.isfile(dst): - shutil.copyfile(fn, dst) + base, ext = os.path.splitext(dst) + if ext in ('.f90', '.f', '.c', '.pyf'): + f2py_sources.append(dst) # Prepare options if module_name is None: module_name = get_temp_module_name() - f2py_opts = ['-c', '-m', module_name] + options + dst_sources + f2py_opts = ['-c', '-m', module_name] + options + f2py_sources if skip: f2py_opts += ['skip:'] + skip if only: @@ -182,37 +174,43 @@ def _get_compiler_status(): # XXX: this is really ugly. But I don't know how to invoke Distutils # in a safer way... - code = """ -import os -import sys -sys.path = %(syspath)s - -def configuration(parent_name='',top_path=None): - global config - from numpy.distutils.misc_util import Configuration - config = Configuration('', parent_name, top_path) - return config - -from numpy.distutils.core import setup -setup(configuration=configuration) - -config_cmd = config.get_config_cmd() -have_c = config_cmd.try_compile('void foo() {}') -print('COMPILERS:%%d,%%d,%%d' %% (have_c, - config.have_f77c(), - config.have_f90c())) -sys.exit(99) -""" + code = textwrap.dedent("""\ + import os + import sys + sys.path = %(syspath)s + + def configuration(parent_name='',top_path=None): + global config + from numpy.distutils.misc_util import Configuration + config = Configuration('', parent_name, top_path) + return config + + from numpy.distutils.core import setup + setup(configuration=configuration) + + config_cmd = config.get_config_cmd() + have_c = config_cmd.try_compile('void foo() {}') + print('COMPILERS:%%d,%%d,%%d' %% (have_c, + config.have_f77c(), + config.have_f90c())) + sys.exit(99) + """) code = code % dict(syspath=repr(sys.path)) - with temppath(suffix='.py') as script: + tmpdir = tempfile.mkdtemp() + try: + script = os.path.join(tmpdir, 'setup.py') + with open(script, 'w') as f: f.write(code) - cmd = [sys.executable, script, 'config'] + cmd = [sys.executable, 'setup.py', 'config'] p = subprocess.Popen(cmd, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) + stderr=subprocess.STDOUT, + cwd=tmpdir) out, err = p.communicate() + finally: + shutil.rmtree(tmpdir) m = re.search(br'COMPILERS:(\d+),(\d+),(\d+)', out) if m: @@ -244,9 +242,6 @@ def build_module_distutils(source_files, config_code, module_name, **kw): Build a module via distutils and import it. """ - from numpy.distutils.misc_util import Configuration - from numpy.distutils.core import setup - d = get_module_dir() # Copy files @@ -261,27 +256,26 @@ def build_module_distutils(source_files, config_code, module_name, **kw): # Build script config_code = textwrap.dedent(config_code).replace("\n", "\n ") - code = """\ -import os -import sys -sys.path = %(syspath)s + code = textwrap.dedent("""\ + import os + import sys + sys.path = %(syspath)s -def configuration(parent_name='',top_path=None): - from numpy.distutils.misc_util import Configuration - config = Configuration('', parent_name, top_path) - %(config_code)s - return config + def configuration(parent_name='',top_path=None): + from numpy.distutils.misc_util import Configuration + config = Configuration('', parent_name, top_path) + %(config_code)s + return config -if __name__ == "__main__": - from numpy.distutils.core import setup - setup(configuration=configuration) -""" % dict(config_code=config_code, syspath=repr(sys.path)) + if __name__ == "__main__": + from numpy.distutils.core import setup + setup(configuration=configuration) + """) % dict(config_code=config_code, syspath=repr(sys.path)) script = os.path.join(d, get_temp_module_name() + '.py') dst_sources.append(script) - f = open(script, 'wb') - f.write(asbytes(code)) - f.close() + with open(script, 'wb') as f: + f.write(asbytes(code)) # Build cwd = os.getcwd() @@ -310,7 +304,7 @@ def configuration(parent_name='',top_path=None): # -class F2PyTest(object): +class F2PyTest: code = None sources = None options = [] @@ -322,14 +316,14 @@ class F2PyTest(object): def setup(self): if sys.platform == 'win32': - raise SkipTest('Fails with MinGW64 Gfortran (Issue #9673)') + pytest.skip('Fails with MinGW64 Gfortran (Issue #9673)') if self.module is not None: return # Check compiler availability first if not has_c_compiler(): - raise SkipTest("No C compiler available") + pytest.skip("No C compiler available") codes = [] if self.sources: @@ -345,9 +339,9 @@ def setup(self): elif fn.endswith('.f90'): needs_f90 = True if needs_f77 and not has_f77_compiler(): - raise SkipTest("No Fortran 77 compiler available") + pytest.skip("No Fortran 77 compiler available") if needs_f90 and not has_f90_compiler(): - raise SkipTest("No Fortran 90 compiler available") + pytest.skip("No Fortran 90 compiler available") # Build the module if self.code is not None: diff --git a/numpy/f2py/use_rules.py b/numpy/f2py/use_rules.py index 6f44f16345bd..f1b71e83c252 100644 --- a/numpy/f2py/use_rules.py +++ b/numpy/f2py/use_rules.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """ Build 'use others module data' mechanism for f2py2e. @@ -15,8 +15,6 @@ Pearu Peterson """ -from __future__ import division, absolute_import, print_function - __version__ = "$Revision: 1.3 $"[10:-1] f2py_version = 'See `f2py -v`' diff --git a/numpy/fft/README.md b/numpy/fft/README.md new file mode 100644 index 000000000000..f79188139ad9 --- /dev/null +++ b/numpy/fft/README.md @@ -0,0 +1,48 @@ +PocketFFT +--------- + +This is a heavily modified implementation of FFTPack [1,2], with the following +advantages: + +- strictly C99 compliant +- more accurate twiddle factor computation +- very fast plan generation +- worst case complexity for transform sizes with large prime factors is + `N*log(N)`, because Bluestein's algorithm [3] is used for these cases. + + +Some code details +----------------- + +Twiddle factor computation: + +- making use of symmetries to reduce number of sin/cos evaluations +- all angles are reduced to the range `[0; pi/4]` for higher accuracy +- an adapted implementation of `sincospi()` is used, which actually computes + `sin(x)` and `(cos(x)-1)`. +- if `n` sin/cos pairs are required, the adjusted `sincospi()` is only called + `2*sqrt(n)` times; the remaining values are obtained by evaluating the + angle addition theorems in a numerically accurate way. + +Parallel invocation: + +- Plans only contain read-only data; all temporary arrays are allocated and + deallocated during an individual FFT execution. This means that a single plan + can be used in several threads at the same time. + +Efficient codelets are available for the factors: + +- 2, 3, 4, 5, 7, 11 for complex-valued FFTs +- 2, 3, 4, 5 for real-valued FFTs + +Larger prime factors are handled by somewhat less efficient, generic routines. + +For lengths with very large prime factors, Bluestein's algorithm is used, and +instead of an FFT of length `n`, a convolution of length `n2 >= 2*n-1` +is performed, where `n2` is chosen to be highly composite. + + +[1] Swarztrauber, P. 1982, Vectorizing the Fast Fourier Transforms + (New York: Academic Press), 51 +[2] https://www.netlib.org/fftpack/ +[3] https://en.wikipedia.org/wiki/Chirp_Z-transform diff --git a/numpy/fft/__init__.py b/numpy/fft/__init__.py index bbb6ec8c71f7..fd5e47580a54 100644 --- a/numpy/fft/__init__.py +++ b/numpy/fft/__init__.py @@ -1,11 +1,212 @@ -from __future__ import division, absolute_import, print_function +""" +Discrete Fourier Transform (:mod:`numpy.fft`) +============================================= -# To get sub-modules -from .info import __doc__ +.. currentmodule:: numpy.fft -from .fftpack import * +The SciPy module `scipy.fft` is a more comprehensive superset +of ``numpy.fft``, which includes only a basic set of routines. + +Standard FFTs +------------- + +.. autosummary:: + :toctree: generated/ + + fft Discrete Fourier transform. + ifft Inverse discrete Fourier transform. + fft2 Discrete Fourier transform in two dimensions. + ifft2 Inverse discrete Fourier transform in two dimensions. + fftn Discrete Fourier transform in N-dimensions. + ifftn Inverse discrete Fourier transform in N dimensions. + +Real FFTs +--------- + +.. autosummary:: + :toctree: generated/ + + rfft Real discrete Fourier transform. + irfft Inverse real discrete Fourier transform. + rfft2 Real discrete Fourier transform in two dimensions. + irfft2 Inverse real discrete Fourier transform in two dimensions. + rfftn Real discrete Fourier transform in N dimensions. + irfftn Inverse real discrete Fourier transform in N dimensions. + +Hermitian FFTs +-------------- + +.. autosummary:: + :toctree: generated/ + + hfft Hermitian discrete Fourier transform. + ihfft Inverse Hermitian discrete Fourier transform. + +Helper routines +--------------- + +.. autosummary:: + :toctree: generated/ + + fftfreq Discrete Fourier Transform sample frequencies. + rfftfreq DFT sample frequencies (for usage with rfft, irfft). + fftshift Shift zero-frequency component to center of spectrum. + ifftshift Inverse of fftshift. + + +Background information +---------------------- + +Fourier analysis is fundamentally a method for expressing a function as a +sum of periodic components, and for recovering the function from those +components. When both the function and its Fourier transform are +replaced with discretized counterparts, it is called the discrete Fourier +transform (DFT). The DFT has become a mainstay of numerical computing in +part because of a very fast algorithm for computing it, called the Fast +Fourier Transform (FFT), which was known to Gauss (1805) and was brought +to light in its current form by Cooley and Tukey [CT]_. Press et al. [NR]_ +provide an accessible introduction to Fourier analysis and its +applications. + +Because the discrete Fourier transform separates its input into +components that contribute at discrete frequencies, it has a great number +of applications in digital signal processing, e.g., for filtering, and in +this context the discretized input to the transform is customarily +referred to as a *signal*, which exists in the *time domain*. The output +is called a *spectrum* or *transform* and exists in the *frequency +domain*. + +Implementation details +---------------------- + +There are many ways to define the DFT, varying in the sign of the +exponent, normalization, etc. In this implementation, the DFT is defined +as + +.. math:: + A_k = \\sum_{m=0}^{n-1} a_m \\exp\\left\\{-2\\pi i{mk \\over n}\\right\\} + \\qquad k = 0,\\ldots,n-1. + +The DFT is in general defined for complex inputs and outputs, and a +single-frequency component at linear frequency :math:`f` is +represented by a complex exponential +:math:`a_m = \\exp\\{2\\pi i\\,f m\\Delta t\\}`, where :math:`\\Delta t` +is the sampling interval. + +The values in the result follow so-called "standard" order: If ``A = +fft(a, n)``, then ``A[0]`` contains the zero-frequency term (the sum of +the signal), which is always purely real for real inputs. Then ``A[1:n/2]`` +contains the positive-frequency terms, and ``A[n/2+1:]`` contains the +negative-frequency terms, in order of decreasingly negative frequency. +For an even number of input points, ``A[n/2]`` represents both positive and +negative Nyquist frequency, and is also purely real for real input. For +an odd number of input points, ``A[(n-1)/2]`` contains the largest positive +frequency, while ``A[(n+1)/2]`` contains the largest negative frequency. +The routine ``np.fft.fftfreq(n)`` returns an array giving the frequencies +of corresponding elements in the output. The routine +``np.fft.fftshift(A)`` shifts transforms and their frequencies to put the +zero-frequency components in the middle, and ``np.fft.ifftshift(A)`` undoes +that shift. + +When the input `a` is a time-domain signal and ``A = fft(a)``, ``np.abs(A)`` +is its amplitude spectrum and ``np.abs(A)**2`` is its power spectrum. +The phase spectrum is obtained by ``np.angle(A)``. + +The inverse DFT is defined as + +.. math:: + a_m = \\frac{1}{n}\\sum_{k=0}^{n-1}A_k\\exp\\left\\{2\\pi i{mk\\over n}\\right\\} + \\qquad m = 0,\\ldots,n-1. + +It differs from the forward transform by the sign of the exponential +argument and the default normalization by :math:`1/n`. + +Type Promotion +-------------- + +`numpy.fft` promotes ``float32`` and ``complex64`` arrays to ``float64`` and +``complex128`` arrays respectively. For an FFT implementation that does not +promote input arrays, see `scipy.fftpack`. + +Normalization +------------- + +The argument ``norm`` indicates which direction of the pair of direct/inverse +transforms is scaled and with what normalization factor. +The default normalization (``"backward"``) has the direct (forward) transforms +unscaled and the inverse (backward) transforms scaled by :math:`1/n`. It is +possible to obtain unitary transforms by setting the keyword argument ``norm`` +to ``"ortho"`` so that both direct and inverse transforms are scaled by +:math:`1/\\sqrt{n}`. Finally, setting the keyword argument ``norm`` to +``"forward"`` has the direct transforms scaled by :math:`1/n` and the inverse +transforms unscaled (i.e. exactly opposite to the default ``"backward"``). +`None` is an alias of the default option ``"backward"`` for backward +compatibility. + +Real and Hermitian transforms +----------------------------- + +When the input is purely real, its transform is Hermitian, i.e., the +component at frequency :math:`f_k` is the complex conjugate of the +component at frequency :math:`-f_k`, which means that for real +inputs there is no information in the negative frequency components that +is not already available from the positive frequency components. +The family of `rfft` functions is +designed to operate on real inputs, and exploits this symmetry by +computing only the positive frequency components, up to and including the +Nyquist frequency. Thus, ``n`` input points produce ``n/2+1`` complex +output points. The inverses of this family assumes the same symmetry of +its input, and for an output of ``n`` points uses ``n/2+1`` input points. + +Correspondingly, when the spectrum is purely real, the signal is +Hermitian. The `hfft` family of functions exploits this symmetry by +using ``n/2+1`` complex points in the input (time) domain for ``n`` real +points in the frequency domain. + +In higher dimensions, FFTs are used, e.g., for image analysis and +filtering. The computational efficiency of the FFT means that it can +also be a faster way to compute large convolutions, using the property +that a convolution in the time domain is equivalent to a point-by-point +multiplication in the frequency domain. + +Higher dimensions +----------------- + +In two dimensions, the DFT is defined as + +.. math:: + A_{kl} = \\sum_{m=0}^{M-1} \\sum_{n=0}^{N-1} + a_{mn}\\exp\\left\\{-2\\pi i \\left({mk\\over M}+{nl\\over N}\\right)\\right\\} + \\qquad k = 0, \\ldots, M-1;\\quad l = 0, \\ldots, N-1, + +which extends in the obvious way to higher dimensions, and the inverses +in higher dimensions also extend in the same way. + +References +---------- + +.. [CT] Cooley, James W., and John W. Tukey, 1965, "An algorithm for the + machine calculation of complex Fourier series," *Math. Comput.* + 19: 297-301. + +.. [NR] Press, W., Teukolsky, S., Vetterline, W.T., and Flannery, B.P., + 2007, *Numerical Recipes: The Art of Scientific Computing*, ch. + 12-13. Cambridge Univ. Press, Cambridge, UK. + +Examples +-------- + +For examples, see the various functions. + +""" + +from . import _pocketfft, helper +from ._pocketfft import * from .helper import * -from numpy.testing._private.pytesttester import PytestTester +__all__ = _pocketfft.__all__.copy() +__all__ += helper.__all__ + +from numpy._pytesttester import PytestTester test = PytestTester(__name__) del PytestTester diff --git a/numpy/fft/__init__.pyi b/numpy/fft/__init__.pyi new file mode 100644 index 000000000000..510e576d3806 --- /dev/null +++ b/numpy/fft/__init__.pyi @@ -0,0 +1,31 @@ +from typing import Any, List + +from numpy._pytesttester import PytestTester + +from numpy.fft._pocketfft import ( + fft as fft, + ifft as ifft, + rfft as rfft, + irfft as irfft, + hfft as hfft, + ihfft as ihfft, + rfftn as rfftn, + irfftn as irfftn, + rfft2 as rfft2, + irfft2 as irfft2, + fft2 as fft2, + ifft2 as ifft2, + fftn as fftn, + ifftn as ifftn, +) + +from numpy.fft.helper import ( + fftshift as fftshift, + ifftshift as ifftshift, + fftfreq as fftfreq, + rfftfreq as rfftfreq, +) + +__all__: List[str] +__path__: List[str] +test: PytestTester diff --git a/numpy/fft/_pocketfft.c b/numpy/fft/_pocketfft.c new file mode 100644 index 000000000000..1eb2eba18c46 --- /dev/null +++ b/numpy/fft/_pocketfft.c @@ -0,0 +1,2385 @@ +/* + * This file is part of pocketfft. + * Licensed under a 3-clause BSD style license - see LICENSE.md + */ + +/* + * Main implementation file. + * + * Copyright (C) 2004-2018 Max-Planck-Society + * \author Martin Reinecke + */ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION + +#define PY_SSIZE_T_CLEAN +#include <Python.h> + +#include "numpy/arrayobject.h" + +#include "npy_config.h" + +#include <math.h> +#include <string.h> +#include <stdlib.h> + +#define restrict NPY_RESTRICT + +#define RALLOC(type,num) \ + ((type *)malloc((num)*sizeof(type))) +#define DEALLOC(ptr) \ + do { free(ptr); (ptr)=NULL; } while(0) + +#define SWAP(a,b,type) \ + do { type tmp_=(a); (a)=(b); (b)=tmp_; } while(0) + +#ifdef __GNUC__ +#define NOINLINE __attribute__((noinline)) +#define WARN_UNUSED_RESULT __attribute__ ((warn_unused_result)) +#else +#define NOINLINE +#define WARN_UNUSED_RESULT +#endif + +struct cfft_plan_i; +typedef struct cfft_plan_i * cfft_plan; +struct rfft_plan_i; +typedef struct rfft_plan_i * rfft_plan; + +// adapted from https://stackoverflow.com/questions/42792939/ +// CAUTION: this function only works for arguments in the range [-0.25; 0.25]! +static void my_sincosm1pi (double a, double *restrict res) + { + double s = a * a; + /* Approximate cos(pi*x)-1 for x in [-0.25,0.25] */ + double r = -1.0369917389758117e-4; + r = fma (r, s, 1.9294935641298806e-3); + r = fma (r, s, -2.5806887942825395e-2); + r = fma (r, s, 2.3533063028328211e-1); + r = fma (r, s, -1.3352627688538006e+0); + r = fma (r, s, 4.0587121264167623e+0); + r = fma (r, s, -4.9348022005446790e+0); + double c = r*s; + /* Approximate sin(pi*x) for x in [-0.25,0.25] */ + r = 4.6151442520157035e-4; + r = fma (r, s, -7.3700183130883555e-3); + r = fma (r, s, 8.2145868949323936e-2); + r = fma (r, s, -5.9926452893214921e-1); + r = fma (r, s, 2.5501640398732688e+0); + r = fma (r, s, -5.1677127800499516e+0); + s = s * a; + r = r * s; + s = fma (a, 3.1415926535897931e+0, r); + res[0] = c; + res[1] = s; + } + +NOINLINE static void calc_first_octant(size_t den, double * restrict res) + { + size_t n = (den+4)>>3; + if (n==0) return; + res[0]=1.; res[1]=0.; + if (n==1) return; + size_t l1=(size_t)sqrt(n); + for (size_t i=1; i<l1; ++i) + my_sincosm1pi((2.*i)/den,&res[2*i]); + size_t start=l1; + while(start<n) + { + double cs[2]; + my_sincosm1pi((2.*start)/den,cs); + res[2*start] = cs[0]+1.; + res[2*start+1] = cs[1]; + size_t end = l1; + if (start+end>n) end = n-start; + for (size_t i=1; i<end; ++i) + { + double csx[2]={res[2*i], res[2*i+1]}; + res[2*(start+i)] = ((cs[0]*csx[0] - cs[1]*csx[1] + cs[0]) + csx[0]) + 1.; + res[2*(start+i)+1] = (cs[0]*csx[1] + cs[1]*csx[0]) + cs[1] + csx[1]; + } + start += l1; + } + for (size_t i=1; i<l1; ++i) + res[2*i] += 1.; + } + +NOINLINE static void calc_first_quadrant(size_t n, double * restrict res) + { + double * restrict p = res+n; + calc_first_octant(n<<1, p); + size_t ndone=(n+2)>>2; + size_t i=0, idx1=0, idx2=2*ndone-2; + for (; i+1<ndone; i+=2, idx1+=2, idx2-=2) + { + res[idx1] = p[2*i]; + res[idx1+1] = p[2*i+1]; + res[idx2] = p[2*i+3]; + res[idx2+1] = p[2*i+2]; + } + if (i!=ndone) + { + res[idx1 ] = p[2*i]; + res[idx1+1] = p[2*i+1]; + } + } + +NOINLINE static void calc_first_half(size_t n, double * restrict res) + { + int ndone=(n+1)>>1; + double * p = res+n-1; + calc_first_octant(n<<2, p); + int i4=0, in=n, i=0; + for (; i4<=in-i4; ++i, i4+=4) // octant 0 + { + res[2*i] = p[2*i4]; res[2*i+1] = p[2*i4+1]; + } + for (; i4-in <= 0; ++i, i4+=4) // octant 1 + { + int xm = in-i4; + res[2*i] = p[2*xm+1]; res[2*i+1] = p[2*xm]; + } + for (; i4<=3*in-i4; ++i, i4+=4) // octant 2 + { + int xm = i4-in; + res[2*i] = -p[2*xm+1]; res[2*i+1] = p[2*xm]; + } + for (; i<ndone; ++i, i4+=4) // octant 3 + { + int xm = 2*in-i4; + res[2*i] = -p[2*xm]; res[2*i+1] = p[2*xm+1]; + } + } + +NOINLINE static void fill_first_quadrant(size_t n, double * restrict res) + { + const double hsqt2 = 0.707106781186547524400844362104849; + size_t quart = n>>2; + if ((n&7)==0) + res[quart] = res[quart+1] = hsqt2; + for (size_t i=2, j=2*quart-2; i<quart; i+=2, j-=2) + { + res[j ] = res[i+1]; + res[j+1] = res[i ]; + } + } + +NOINLINE static void fill_first_half(size_t n, double * restrict res) + { + size_t half = n>>1; + if ((n&3)==0) + for (size_t i=0; i<half; i+=2) + { + res[i+half] = -res[i+1]; + res[i+half+1] = res[i ]; + } + else + for (size_t i=2, j=2*half-2; i<half; i+=2, j-=2) + { + res[j ] = -res[i ]; + res[j+1] = res[i+1]; + } + } + +NOINLINE static void fill_second_half(size_t n, double * restrict res) + { + if ((n&1)==0) + for (size_t i=0; i<n; ++i) + res[i+n] = -res[i]; + else + for (size_t i=2, j=2*n-2; i<n; i+=2, j-=2) + { + res[j ] = res[i ]; + res[j+1] = -res[i+1]; + } + } + +NOINLINE static void sincos_2pibyn_half(size_t n, double * restrict res) + { + if ((n&3)==0) + { + calc_first_octant(n, res); + fill_first_quadrant(n, res); + fill_first_half(n, res); + } + else if ((n&1)==0) + { + calc_first_quadrant(n, res); + fill_first_half(n, res); + } + else + calc_first_half(n, res); + } + +NOINLINE static void sincos_2pibyn(size_t n, double * restrict res) + { + sincos_2pibyn_half(n, res); + fill_second_half(n, res); + } + +NOINLINE static size_t largest_prime_factor (size_t n) + { + size_t res=1; + size_t tmp; + while (((tmp=(n>>1))<<1)==n) + { res=2; n=tmp; } + + size_t limit=(size_t)sqrt(n+0.01); + for (size_t x=3; x<=limit; x+=2) + while (((tmp=(n/x))*x)==n) + { + res=x; + n=tmp; + limit=(size_t)sqrt(n+0.01); + } + if (n>1) res=n; + + return res; + } + +NOINLINE static double cost_guess (size_t n) + { + const double lfp=1.1; // penalty for non-hardcoded larger factors + size_t ni=n; + double result=0.; + size_t tmp; + while (((tmp=(n>>1))<<1)==n) + { result+=2; n=tmp; } + + size_t limit=(size_t)sqrt(n+0.01); + for (size_t x=3; x<=limit; x+=2) + while ((tmp=(n/x))*x==n) + { + result+= (x<=5) ? x : lfp*x; // penalize larger prime factors + n=tmp; + limit=(size_t)sqrt(n+0.01); + } + if (n>1) result+=(n<=5) ? n : lfp*n; + + return result*ni; + } + +/* returns the smallest composite of 2, 3, 5, 7 and 11 which is >= n */ +NOINLINE static size_t good_size(size_t n) + { + if (n<=6) return n; + + size_t bestfac=2*n; + for (size_t f2=1; f2<bestfac; f2*=2) + for (size_t f23=f2; f23<bestfac; f23*=3) + for (size_t f235=f23; f235<bestfac; f235*=5) + for (size_t f2357=f235; f2357<bestfac; f2357*=7) + for (size_t f235711=f2357; f235711<bestfac; f235711*=11) + if (f235711>=n) bestfac=f235711; + return bestfac; + } + +typedef struct cmplx { + double r,i; +} cmplx; + +#define NFCT 25 +typedef struct cfftp_fctdata + { + size_t fct; + cmplx *tw, *tws; + } cfftp_fctdata; + +typedef struct cfftp_plan_i + { + size_t length, nfct; + cmplx *mem; + cfftp_fctdata fct[NFCT]; + } cfftp_plan_i; +typedef struct cfftp_plan_i * cfftp_plan; + +#define PMC(a,b,c,d) { a.r=c.r+d.r; a.i=c.i+d.i; b.r=c.r-d.r; b.i=c.i-d.i; } +#define ADDC(a,b,c) { a.r=b.r+c.r; a.i=b.i+c.i; } +#define SCALEC(a,b) { a.r*=b; a.i*=b; } +#define ROT90(a) { double tmp_=a.r; a.r=-a.i; a.i=tmp_; } +#define ROTM90(a) { double tmp_=-a.r; a.r=a.i; a.i=tmp_; } +#define CH(a,b,c) ch[(a)+ido*((b)+l1*(c))] +#define CC(a,b,c) cc[(a)+ido*((b)+cdim*(c))] +#define WA(x,i) wa[(i)-1+(x)*(ido-1)] +/* a = b*c */ +#define A_EQ_B_MUL_C(a,b,c) { a.r=b.r*c.r-b.i*c.i; a.i=b.r*c.i+b.i*c.r; } +/* a = conj(b)*c*/ +#define A_EQ_CB_MUL_C(a,b,c) { a.r=b.r*c.r+b.i*c.i; a.i=b.r*c.i-b.i*c.r; } + +#define PMSIGNC(a,b,c,d) { a.r=c.r+sign*d.r; a.i=c.i+sign*d.i; b.r=c.r-sign*d.r; b.i=c.i-sign*d.i; } +/* a = b*c */ +#define MULPMSIGNC(a,b,c) { a.r=b.r*c.r-sign*b.i*c.i; a.i=b.r*c.i+sign*b.i*c.r; } +/* a *= b */ +#define MULPMSIGNCEQ(a,b) { double xtmp=a.r; a.r=b.r*a.r-sign*b.i*a.i; a.i=b.r*a.i+sign*b.i*xtmp; } + +NOINLINE static void pass2b (size_t ido, size_t l1, const cmplx * restrict cc, + cmplx * restrict ch, const cmplx * restrict wa) + { + const size_t cdim=2; + + if (ido==1) + for (size_t k=0; k<l1; ++k) + PMC (CH(0,k,0),CH(0,k,1),CC(0,0,k),CC(0,1,k)) + else + for (size_t k=0; k<l1; ++k) + { + PMC (CH(0,k,0),CH(0,k,1),CC(0,0,k),CC(0,1,k)) + for (size_t i=1; i<ido; ++i) + { + cmplx t; + PMC (CH(i,k,0),t,CC(i,0,k),CC(i,1,k)) + A_EQ_B_MUL_C (CH(i,k,1),WA(0,i),t) + } + } + } + +NOINLINE static void pass2f (size_t ido, size_t l1, const cmplx * restrict cc, + cmplx * restrict ch, const cmplx * restrict wa) + { + const size_t cdim=2; + + if (ido==1) + for (size_t k=0; k<l1; ++k) + PMC (CH(0,k,0),CH(0,k,1),CC(0,0,k),CC(0,1,k)) + else + for (size_t k=0; k<l1; ++k) + { + PMC (CH(0,k,0),CH(0,k,1),CC(0,0,k),CC(0,1,k)) + for (size_t i=1; i<ido; ++i) + { + cmplx t; + PMC (CH(i,k,0),t,CC(i,0,k),CC(i,1,k)) + A_EQ_CB_MUL_C (CH(i,k,1),WA(0,i),t) + } + } + } + +#define PREP3(idx) \ + cmplx t0 = CC(idx,0,k), t1, t2; \ + PMC (t1,t2,CC(idx,1,k),CC(idx,2,k)) \ + CH(idx,k,0).r=t0.r+t1.r; \ + CH(idx,k,0).i=t0.i+t1.i; +#define PARTSTEP3a(u1,u2,twr,twi) \ + { \ + cmplx ca,cb; \ + ca.r=t0.r+twr*t1.r; \ + ca.i=t0.i+twr*t1.i; \ + cb.i=twi*t2.r; \ + cb.r=-(twi*t2.i); \ + PMC(CH(0,k,u1),CH(0,k,u2),ca,cb) \ + } + +#define PARTSTEP3b(u1,u2,twr,twi) \ + { \ + cmplx ca,cb,da,db; \ + ca.r=t0.r+twr*t1.r; \ + ca.i=t0.i+twr*t1.i; \ + cb.i=twi*t2.r; \ + cb.r=-(twi*t2.i); \ + PMC(da,db,ca,cb) \ + A_EQ_B_MUL_C (CH(i,k,u1),WA(u1-1,i),da) \ + A_EQ_B_MUL_C (CH(i,k,u2),WA(u2-1,i),db) \ + } +NOINLINE static void pass3b (size_t ido, size_t l1, const cmplx * restrict cc, + cmplx * restrict ch, const cmplx * restrict wa) + { + const size_t cdim=3; + const double tw1r=-0.5, tw1i= 0.86602540378443864676; + + if (ido==1) + for (size_t k=0; k<l1; ++k) + { + PREP3(0) + PARTSTEP3a(1,2,tw1r,tw1i) + } + else + for (size_t k=0; k<l1; ++k) + { + { + PREP3(0) + PARTSTEP3a(1,2,tw1r,tw1i) + } + for (size_t i=1; i<ido; ++i) + { + PREP3(i) + PARTSTEP3b(1,2,tw1r,tw1i) + } + } + } +#define PARTSTEP3f(u1,u2,twr,twi) \ + { \ + cmplx ca,cb,da,db; \ + ca.r=t0.r+twr*t1.r; \ + ca.i=t0.i+twr*t1.i; \ + cb.i=twi*t2.r; \ + cb.r=-(twi*t2.i); \ + PMC(da,db,ca,cb) \ + A_EQ_CB_MUL_C (CH(i,k,u1),WA(u1-1,i),da) \ + A_EQ_CB_MUL_C (CH(i,k,u2),WA(u2-1,i),db) \ + } +NOINLINE static void pass3f (size_t ido, size_t l1, const cmplx * restrict cc, + cmplx * restrict ch, const cmplx * restrict wa) + { + const size_t cdim=3; + const double tw1r=-0.5, tw1i= -0.86602540378443864676; + + if (ido==1) + for (size_t k=0; k<l1; ++k) + { + PREP3(0) + PARTSTEP3a(1,2,tw1r,tw1i) + } + else + for (size_t k=0; k<l1; ++k) + { + { + PREP3(0) + PARTSTEP3a(1,2,tw1r,tw1i) + } + for (size_t i=1; i<ido; ++i) + { + PREP3(i) + PARTSTEP3f(1,2,tw1r,tw1i) + } + } + } + +NOINLINE static void pass4b (size_t ido, size_t l1, const cmplx * restrict cc, + cmplx * restrict ch, const cmplx * restrict wa) + { + const size_t cdim=4; + + if (ido==1) + for (size_t k=0; k<l1; ++k) + { + cmplx t1, t2, t3, t4; + PMC(t2,t1,CC(0,0,k),CC(0,2,k)) + PMC(t3,t4,CC(0,1,k),CC(0,3,k)) + ROT90(t4) + PMC(CH(0,k,0),CH(0,k,2),t2,t3) + PMC(CH(0,k,1),CH(0,k,3),t1,t4) + } + else + for (size_t k=0; k<l1; ++k) + { + { + cmplx t1, t2, t3, t4; + PMC(t2,t1,CC(0,0,k),CC(0,2,k)) + PMC(t3,t4,CC(0,1,k),CC(0,3,k)) + ROT90(t4) + PMC(CH(0,k,0),CH(0,k,2),t2,t3) + PMC(CH(0,k,1),CH(0,k,3),t1,t4) + } + for (size_t i=1; i<ido; ++i) + { + cmplx c2, c3, c4, t1, t2, t3, t4; + cmplx cc0=CC(i,0,k), cc1=CC(i,1,k),cc2=CC(i,2,k),cc3=CC(i,3,k); + PMC(t2,t1,cc0,cc2) + PMC(t3,t4,cc1,cc3) + ROT90(t4) + cmplx wa0=WA(0,i), wa1=WA(1,i),wa2=WA(2,i); + PMC(CH(i,k,0),c3,t2,t3) + PMC(c2,c4,t1,t4) + A_EQ_B_MUL_C (CH(i,k,1),wa0,c2) + A_EQ_B_MUL_C (CH(i,k,2),wa1,c3) + A_EQ_B_MUL_C (CH(i,k,3),wa2,c4) + } + } + } +NOINLINE static void pass4f (size_t ido, size_t l1, const cmplx * restrict cc, + cmplx * restrict ch, const cmplx * restrict wa) + { + const size_t cdim=4; + + if (ido==1) + for (size_t k=0; k<l1; ++k) + { + cmplx t1, t2, t3, t4; + PMC(t2,t1,CC(0,0,k),CC(0,2,k)) + PMC(t3,t4,CC(0,1,k),CC(0,3,k)) + ROTM90(t4) + PMC(CH(0,k,0),CH(0,k,2),t2,t3) + PMC(CH(0,k,1),CH(0,k,3),t1,t4) + } + else + for (size_t k=0; k<l1; ++k) + { + { + cmplx t1, t2, t3, t4; + PMC(t2,t1,CC(0,0,k),CC(0,2,k)) + PMC(t3,t4,CC(0,1,k),CC(0,3,k)) + ROTM90(t4) + PMC(CH(0,k,0),CH(0,k,2),t2,t3) + PMC (CH(0,k,1),CH(0,k,3),t1,t4) + } + for (size_t i=1; i<ido; ++i) + { + cmplx c2, c3, c4, t1, t2, t3, t4; + cmplx cc0=CC(i,0,k), cc1=CC(i,1,k),cc2=CC(i,2,k),cc3=CC(i,3,k); + PMC(t2,t1,cc0,cc2) + PMC(t3,t4,cc1,cc3) + ROTM90(t4) + cmplx wa0=WA(0,i), wa1=WA(1,i),wa2=WA(2,i); + PMC(CH(i,k,0),c3,t2,t3) + PMC(c2,c4,t1,t4) + A_EQ_CB_MUL_C (CH(i,k,1),wa0,c2) + A_EQ_CB_MUL_C (CH(i,k,2),wa1,c3) + A_EQ_CB_MUL_C (CH(i,k,3),wa2,c4) + } + } + } + +#define PREP5(idx) \ + cmplx t0 = CC(idx,0,k), t1, t2, t3, t4; \ + PMC (t1,t4,CC(idx,1,k),CC(idx,4,k)) \ + PMC (t2,t3,CC(idx,2,k),CC(idx,3,k)) \ + CH(idx,k,0).r=t0.r+t1.r+t2.r; \ + CH(idx,k,0).i=t0.i+t1.i+t2.i; + +#define PARTSTEP5a(u1,u2,twar,twbr,twai,twbi) \ + { \ + cmplx ca,cb; \ + ca.r=t0.r+twar*t1.r+twbr*t2.r; \ + ca.i=t0.i+twar*t1.i+twbr*t2.i; \ + cb.i=twai*t4.r twbi*t3.r; \ + cb.r=-(twai*t4.i twbi*t3.i); \ + PMC(CH(0,k,u1),CH(0,k,u2),ca,cb) \ + } + +#define PARTSTEP5b(u1,u2,twar,twbr,twai,twbi) \ + { \ + cmplx ca,cb,da,db; \ + ca.r=t0.r+twar*t1.r+twbr*t2.r; \ + ca.i=t0.i+twar*t1.i+twbr*t2.i; \ + cb.i=twai*t4.r twbi*t3.r; \ + cb.r=-(twai*t4.i twbi*t3.i); \ + PMC(da,db,ca,cb) \ + A_EQ_B_MUL_C (CH(i,k,u1),WA(u1-1,i),da) \ + A_EQ_B_MUL_C (CH(i,k,u2),WA(u2-1,i),db) \ + } +NOINLINE static void pass5b (size_t ido, size_t l1, const cmplx * restrict cc, + cmplx * restrict ch, const cmplx * restrict wa) + { + const size_t cdim=5; + const double tw1r= 0.3090169943749474241, + tw1i= 0.95105651629515357212, + tw2r= -0.8090169943749474241, + tw2i= 0.58778525229247312917; + + if (ido==1) + for (size_t k=0; k<l1; ++k) + { + PREP5(0) + PARTSTEP5a(1,4,tw1r,tw2r,+tw1i,+tw2i) + PARTSTEP5a(2,3,tw2r,tw1r,+tw2i,-tw1i) + } + else + for (size_t k=0; k<l1; ++k) + { + { + PREP5(0) + PARTSTEP5a(1,4,tw1r,tw2r,+tw1i,+tw2i) + PARTSTEP5a(2,3,tw2r,tw1r,+tw2i,-tw1i) + } + for (size_t i=1; i<ido; ++i) + { + PREP5(i) + PARTSTEP5b(1,4,tw1r,tw2r,+tw1i,+tw2i) + PARTSTEP5b(2,3,tw2r,tw1r,+tw2i,-tw1i) + } + } + } +#define PARTSTEP5f(u1,u2,twar,twbr,twai,twbi) \ + { \ + cmplx ca,cb,da,db; \ + ca.r=t0.r+twar*t1.r+twbr*t2.r; \ + ca.i=t0.i+twar*t1.i+twbr*t2.i; \ + cb.i=twai*t4.r twbi*t3.r; \ + cb.r=-(twai*t4.i twbi*t3.i); \ + PMC(da,db,ca,cb) \ + A_EQ_CB_MUL_C (CH(i,k,u1),WA(u1-1,i),da) \ + A_EQ_CB_MUL_C (CH(i,k,u2),WA(u2-1,i),db) \ + } +NOINLINE static void pass5f (size_t ido, size_t l1, const cmplx * restrict cc, + cmplx * restrict ch, const cmplx * restrict wa) + { + const size_t cdim=5; + const double tw1r= 0.3090169943749474241, + tw1i= -0.95105651629515357212, + tw2r= -0.8090169943749474241, + tw2i= -0.58778525229247312917; + + if (ido==1) + for (size_t k=0; k<l1; ++k) + { + PREP5(0) + PARTSTEP5a(1,4,tw1r,tw2r,+tw1i,+tw2i) + PARTSTEP5a(2,3,tw2r,tw1r,+tw2i,-tw1i) + } + else + for (size_t k=0; k<l1; ++k) + { + { + PREP5(0) + PARTSTEP5a(1,4,tw1r,tw2r,+tw1i,+tw2i) + PARTSTEP5a(2,3,tw2r,tw1r,+tw2i,-tw1i) + } + for (size_t i=1; i<ido; ++i) + { + PREP5(i) + PARTSTEP5f(1,4,tw1r,tw2r,+tw1i,+tw2i) + PARTSTEP5f(2,3,tw2r,tw1r,+tw2i,-tw1i) + } + } + } + +#define PREP7(idx) \ + cmplx t1 = CC(idx,0,k), t2, t3, t4, t5, t6, t7; \ + PMC (t2,t7,CC(idx,1,k),CC(idx,6,k)) \ + PMC (t3,t6,CC(idx,2,k),CC(idx,5,k)) \ + PMC (t4,t5,CC(idx,3,k),CC(idx,4,k)) \ + CH(idx,k,0).r=t1.r+t2.r+t3.r+t4.r; \ + CH(idx,k,0).i=t1.i+t2.i+t3.i+t4.i; + +#define PARTSTEP7a0(u1,u2,x1,x2,x3,y1,y2,y3,out1,out2) \ + { \ + cmplx ca,cb; \ + ca.r=t1.r+x1*t2.r+x2*t3.r+x3*t4.r; \ + ca.i=t1.i+x1*t2.i+x2*t3.i+x3*t4.i; \ + cb.i=y1*t7.r y2*t6.r y3*t5.r; \ + cb.r=-(y1*t7.i y2*t6.i y3*t5.i); \ + PMC(out1,out2,ca,cb) \ + } +#define PARTSTEP7a(u1,u2,x1,x2,x3,y1,y2,y3) \ + PARTSTEP7a0(u1,u2,x1,x2,x3,y1,y2,y3,CH(0,k,u1),CH(0,k,u2)) +#define PARTSTEP7(u1,u2,x1,x2,x3,y1,y2,y3) \ + { \ + cmplx da,db; \ + PARTSTEP7a0(u1,u2,x1,x2,x3,y1,y2,y3,da,db) \ + MULPMSIGNC (CH(i,k,u1),WA(u1-1,i),da) \ + MULPMSIGNC (CH(i,k,u2),WA(u2-1,i),db) \ + } + +NOINLINE static void pass7(size_t ido, size_t l1, const cmplx * restrict cc, + cmplx * restrict ch, const cmplx * restrict wa, const int sign) + { + const size_t cdim=7; + const double tw1r= 0.623489801858733530525, + tw1i= sign * 0.7818314824680298087084, + tw2r= -0.222520933956314404289, + tw2i= sign * 0.9749279121818236070181, + tw3r= -0.9009688679024191262361, + tw3i= sign * 0.4338837391175581204758; + + if (ido==1) + for (size_t k=0; k<l1; ++k) + { + PREP7(0) + PARTSTEP7a(1,6,tw1r,tw2r,tw3r,+tw1i,+tw2i,+tw3i) + PARTSTEP7a(2,5,tw2r,tw3r,tw1r,+tw2i,-tw3i,-tw1i) + PARTSTEP7a(3,4,tw3r,tw1r,tw2r,+tw3i,-tw1i,+tw2i) + } + else + for (size_t k=0; k<l1; ++k) + { + { + PREP7(0) + PARTSTEP7a(1,6,tw1r,tw2r,tw3r,+tw1i,+tw2i,+tw3i) + PARTSTEP7a(2,5,tw2r,tw3r,tw1r,+tw2i,-tw3i,-tw1i) + PARTSTEP7a(3,4,tw3r,tw1r,tw2r,+tw3i,-tw1i,+tw2i) + } + for (size_t i=1; i<ido; ++i) + { + PREP7(i) + PARTSTEP7(1,6,tw1r,tw2r,tw3r,+tw1i,+tw2i,+tw3i) + PARTSTEP7(2,5,tw2r,tw3r,tw1r,+tw2i,-tw3i,-tw1i) + PARTSTEP7(3,4,tw3r,tw1r,tw2r,+tw3i,-tw1i,+tw2i) + } + } + } + +#define PREP11(idx) \ + cmplx t1 = CC(idx,0,k), t2, t3, t4, t5, t6, t7, t8, t9, t10, t11; \ + PMC (t2,t11,CC(idx,1,k),CC(idx,10,k)) \ + PMC (t3,t10,CC(idx,2,k),CC(idx, 9,k)) \ + PMC (t4,t9 ,CC(idx,3,k),CC(idx, 8,k)) \ + PMC (t5,t8 ,CC(idx,4,k),CC(idx, 7,k)) \ + PMC (t6,t7 ,CC(idx,5,k),CC(idx, 6,k)) \ + CH(idx,k,0).r=t1.r+t2.r+t3.r+t4.r+t5.r+t6.r; \ + CH(idx,k,0).i=t1.i+t2.i+t3.i+t4.i+t5.i+t6.i; + +#define PARTSTEP11a0(u1,u2,x1,x2,x3,x4,x5,y1,y2,y3,y4,y5,out1,out2) \ + { \ + cmplx ca,cb; \ + ca.r=t1.r+x1*t2.r+x2*t3.r+x3*t4.r+x4*t5.r+x5*t6.r; \ + ca.i=t1.i+x1*t2.i+x2*t3.i+x3*t4.i+x4*t5.i+x5*t6.i; \ + cb.i=y1*t11.r y2*t10.r y3*t9.r y4*t8.r y5*t7.r; \ + cb.r=-(y1*t11.i y2*t10.i y3*t9.i y4*t8.i y5*t7.i ); \ + PMC(out1,out2,ca,cb) \ + } +#define PARTSTEP11a(u1,u2,x1,x2,x3,x4,x5,y1,y2,y3,y4,y5) \ + PARTSTEP11a0(u1,u2,x1,x2,x3,x4,x5,y1,y2,y3,y4,y5,CH(0,k,u1),CH(0,k,u2)) +#define PARTSTEP11(u1,u2,x1,x2,x3,x4,x5,y1,y2,y3,y4,y5) \ + { \ + cmplx da,db; \ + PARTSTEP11a0(u1,u2,x1,x2,x3,x4,x5,y1,y2,y3,y4,y5,da,db) \ + MULPMSIGNC (CH(i,k,u1),WA(u1-1,i),da) \ + MULPMSIGNC (CH(i,k,u2),WA(u2-1,i),db) \ + } + +NOINLINE static void pass11 (size_t ido, size_t l1, const cmplx * restrict cc, + cmplx * restrict ch, const cmplx * restrict wa, const int sign) + { + const size_t cdim=11; + const double tw1r = 0.8412535328311811688618, + tw1i = sign * 0.5406408174555975821076, + tw2r = 0.4154150130018864255293, + tw2i = sign * 0.9096319953545183714117, + tw3r = -0.1423148382732851404438, + tw3i = sign * 0.9898214418809327323761, + tw4r = -0.6548607339452850640569, + tw4i = sign * 0.755749574354258283774, + tw5r = -0.9594929736144973898904, + tw5i = sign * 0.2817325568414296977114; + + if (ido==1) + for (size_t k=0; k<l1; ++k) + { + PREP11(0) + PARTSTEP11a(1,10,tw1r,tw2r,tw3r,tw4r,tw5r,+tw1i,+tw2i,+tw3i,+tw4i,+tw5i) + PARTSTEP11a(2, 9,tw2r,tw4r,tw5r,tw3r,tw1r,+tw2i,+tw4i,-tw5i,-tw3i,-tw1i) + PARTSTEP11a(3, 8,tw3r,tw5r,tw2r,tw1r,tw4r,+tw3i,-tw5i,-tw2i,+tw1i,+tw4i) + PARTSTEP11a(4, 7,tw4r,tw3r,tw1r,tw5r,tw2r,+tw4i,-tw3i,+tw1i,+tw5i,-tw2i) + PARTSTEP11a(5, 6,tw5r,tw1r,tw4r,tw2r,tw3r,+tw5i,-tw1i,+tw4i,-tw2i,+tw3i) + } + else + for (size_t k=0; k<l1; ++k) + { + { + PREP11(0) + PARTSTEP11a(1,10,tw1r,tw2r,tw3r,tw4r,tw5r,+tw1i,+tw2i,+tw3i,+tw4i,+tw5i) + PARTSTEP11a(2, 9,tw2r,tw4r,tw5r,tw3r,tw1r,+tw2i,+tw4i,-tw5i,-tw3i,-tw1i) + PARTSTEP11a(3, 8,tw3r,tw5r,tw2r,tw1r,tw4r,+tw3i,-tw5i,-tw2i,+tw1i,+tw4i) + PARTSTEP11a(4, 7,tw4r,tw3r,tw1r,tw5r,tw2r,+tw4i,-tw3i,+tw1i,+tw5i,-tw2i) + PARTSTEP11a(5, 6,tw5r,tw1r,tw4r,tw2r,tw3r,+tw5i,-tw1i,+tw4i,-tw2i,+tw3i) + } + for (size_t i=1; i<ido; ++i) + { + PREP11(i) + PARTSTEP11(1,10,tw1r,tw2r,tw3r,tw4r,tw5r,+tw1i,+tw2i,+tw3i,+tw4i,+tw5i) + PARTSTEP11(2, 9,tw2r,tw4r,tw5r,tw3r,tw1r,+tw2i,+tw4i,-tw5i,-tw3i,-tw1i) + PARTSTEP11(3, 8,tw3r,tw5r,tw2r,tw1r,tw4r,+tw3i,-tw5i,-tw2i,+tw1i,+tw4i) + PARTSTEP11(4, 7,tw4r,tw3r,tw1r,tw5r,tw2r,+tw4i,-tw3i,+tw1i,+tw5i,-tw2i) + PARTSTEP11(5, 6,tw5r,tw1r,tw4r,tw2r,tw3r,+tw5i,-tw1i,+tw4i,-tw2i,+tw3i) + } + } + } + +#define CX(a,b,c) cc[(a)+ido*((b)+l1*(c))] +#define CX2(a,b) cc[(a)+idl1*(b)] +#define CH2(a,b) ch[(a)+idl1*(b)] + +NOINLINE static int passg (size_t ido, size_t ip, size_t l1, + cmplx * restrict cc, cmplx * restrict ch, const cmplx * restrict wa, + const cmplx * restrict csarr, const int sign) + { + const size_t cdim=ip; + size_t ipph = (ip+1)/2; + size_t idl1 = ido*l1; + + cmplx * restrict wal=RALLOC(cmplx,ip); + if (!wal) return -1; + wal[0]=(cmplx){1.,0.}; + for (size_t i=1; i<ip; ++i) + wal[i]=(cmplx){csarr[i].r,sign*csarr[i].i}; + + for (size_t k=0; k<l1; ++k) + for (size_t i=0; i<ido; ++i) + CH(i,k,0) = CC(i,0,k); + for (size_t j=1, jc=ip-1; j<ipph; ++j, --jc) + for (size_t k=0; k<l1; ++k) + for (size_t i=0; i<ido; ++i) + PMC(CH(i,k,j),CH(i,k,jc),CC(i,j,k),CC(i,jc,k)) + for (size_t k=0; k<l1; ++k) + for (size_t i=0; i<ido; ++i) + { + cmplx tmp = CH(i,k,0); + for (size_t j=1; j<ipph; ++j) + ADDC(tmp,tmp,CH(i,k,j)) + CX(i,k,0) = tmp; + } + for (size_t l=1, lc=ip-1; l<ipph; ++l, --lc) + { + // j=0 + for (size_t ik=0; ik<idl1; ++ik) + { + CX2(ik,l).r = CH2(ik,0).r+wal[l].r*CH2(ik,1).r+wal[2*l].r*CH2(ik,2).r; + CX2(ik,l).i = CH2(ik,0).i+wal[l].r*CH2(ik,1).i+wal[2*l].r*CH2(ik,2).i; + CX2(ik,lc).r=-wal[l].i*CH2(ik,ip-1).i-wal[2*l].i*CH2(ik,ip-2).i; + CX2(ik,lc).i=wal[l].i*CH2(ik,ip-1).r+wal[2*l].i*CH2(ik,ip-2).r; + } + + size_t iwal=2*l; + size_t j=3, jc=ip-3; + for (; j<ipph-1; j+=2, jc-=2) + { + iwal+=l; if (iwal>ip) iwal-=ip; + cmplx xwal=wal[iwal]; + iwal+=l; if (iwal>ip) iwal-=ip; + cmplx xwal2=wal[iwal]; + for (size_t ik=0; ik<idl1; ++ik) + { + CX2(ik,l).r += CH2(ik,j).r*xwal.r+CH2(ik,j+1).r*xwal2.r; + CX2(ik,l).i += CH2(ik,j).i*xwal.r+CH2(ik,j+1).i*xwal2.r; + CX2(ik,lc).r -= CH2(ik,jc).i*xwal.i+CH2(ik,jc-1).i*xwal2.i; + CX2(ik,lc).i += CH2(ik,jc).r*xwal.i+CH2(ik,jc-1).r*xwal2.i; + } + } + for (; j<ipph; ++j, --jc) + { + iwal+=l; if (iwal>ip) iwal-=ip; + cmplx xwal=wal[iwal]; + for (size_t ik=0; ik<idl1; ++ik) + { + CX2(ik,l).r += CH2(ik,j).r*xwal.r; + CX2(ik,l).i += CH2(ik,j).i*xwal.r; + CX2(ik,lc).r -= CH2(ik,jc).i*xwal.i; + CX2(ik,lc).i += CH2(ik,jc).r*xwal.i; + } + } + } + DEALLOC(wal); + + // shuffling and twiddling + if (ido==1) + for (size_t j=1, jc=ip-1; j<ipph; ++j, --jc) + for (size_t ik=0; ik<idl1; ++ik) + { + cmplx t1=CX2(ik,j), t2=CX2(ik,jc); + PMC(CX2(ik,j),CX2(ik,jc),t1,t2) + } + else + { + for (size_t j=1, jc=ip-1; j<ipph; ++j,--jc) + for (size_t k=0; k<l1; ++k) + { + cmplx t1=CX(0,k,j), t2=CX(0,k,jc); + PMC(CX(0,k,j),CX(0,k,jc),t1,t2) + for (size_t i=1; i<ido; ++i) + { + cmplx x1, x2; + PMC(x1,x2,CX(i,k,j),CX(i,k,jc)) + size_t idij=(j-1)*(ido-1)+i-1; + MULPMSIGNC (CX(i,k,j),wa[idij],x1) + idij=(jc-1)*(ido-1)+i-1; + MULPMSIGNC (CX(i,k,jc),wa[idij],x2) + } + } + } + return 0; + } + +#undef CH2 +#undef CX2 +#undef CX + +NOINLINE WARN_UNUSED_RESULT static int pass_all(cfftp_plan plan, cmplx c[], double fct, + const int sign) + { + if (plan->length==1) return 0; + size_t len=plan->length; + size_t l1=1, nf=plan->nfct; + cmplx *ch = RALLOC(cmplx, len); + if (!ch) return -1; + cmplx *p1=c, *p2=ch; + + for(size_t k1=0; k1<nf; k1++) + { + size_t ip=plan->fct[k1].fct; + size_t l2=ip*l1; + size_t ido = len/l2; + if (ip==4) + sign>0 ? pass4b (ido, l1, p1, p2, plan->fct[k1].tw) + : pass4f (ido, l1, p1, p2, plan->fct[k1].tw); + else if(ip==2) + sign>0 ? pass2b (ido, l1, p1, p2, plan->fct[k1].tw) + : pass2f (ido, l1, p1, p2, plan->fct[k1].tw); + else if(ip==3) + sign>0 ? pass3b (ido, l1, p1, p2, plan->fct[k1].tw) + : pass3f (ido, l1, p1, p2, plan->fct[k1].tw); + else if(ip==5) + sign>0 ? pass5b (ido, l1, p1, p2, plan->fct[k1].tw) + : pass5f (ido, l1, p1, p2, plan->fct[k1].tw); + else if(ip==7) pass7 (ido, l1, p1, p2, plan->fct[k1].tw, sign); + else if(ip==11) pass11(ido, l1, p1, p2, plan->fct[k1].tw, sign); + else + { + if (passg(ido, ip, l1, p1, p2, plan->fct[k1].tw, plan->fct[k1].tws, sign)) + { DEALLOC(ch); return -1; } + SWAP(p1,p2,cmplx *); + } + SWAP(p1,p2,cmplx *); + l1=l2; + } + if (p1!=c) + { + if (fct!=1.) + for (size_t i=0; i<len; ++i) + { + c[i].r = ch[i].r*fct; + c[i].i = ch[i].i*fct; + } + else + memcpy (c,p1,len*sizeof(cmplx)); + } + else + if (fct!=1.) + for (size_t i=0; i<len; ++i) + { + c[i].r *= fct; + c[i].i *= fct; + } + DEALLOC(ch); + return 0; + } + +#undef PMSIGNC +#undef A_EQ_B_MUL_C +#undef A_EQ_CB_MUL_C +#undef MULPMSIGNC +#undef MULPMSIGNCEQ + +#undef WA +#undef CC +#undef CH +#undef ROT90 +#undef SCALEC +#undef ADDC +#undef PMC + +NOINLINE WARN_UNUSED_RESULT +static int cfftp_forward(cfftp_plan plan, double c[], double fct) + { return pass_all(plan,(cmplx *)c, fct, -1); } + +NOINLINE WARN_UNUSED_RESULT +static int cfftp_backward(cfftp_plan plan, double c[], double fct) + { return pass_all(plan,(cmplx *)c, fct, 1); } + +NOINLINE WARN_UNUSED_RESULT +static int cfftp_factorize (cfftp_plan plan) + { + size_t length=plan->length; + size_t nfct=0; + while ((length%4)==0) + { if (nfct>=NFCT) return -1; plan->fct[nfct++].fct=4; length>>=2; } + if ((length%2)==0) + { + length>>=1; + // factor 2 should be at the front of the factor list + if (nfct>=NFCT) return -1; + plan->fct[nfct++].fct=2; + SWAP(plan->fct[0].fct, plan->fct[nfct-1].fct,size_t); + } + size_t maxl=(size_t)(sqrt((double)length))+1; + for (size_t divisor=3; (length>1)&&(divisor<maxl); divisor+=2) + if ((length%divisor)==0) + { + while ((length%divisor)==0) + { + if (nfct>=NFCT) return -1; + plan->fct[nfct++].fct=divisor; + length/=divisor; + } + maxl=(size_t)(sqrt((double)length))+1; + } + if (length>1) plan->fct[nfct++].fct=length; + plan->nfct=nfct; + return 0; + } + +NOINLINE static size_t cfftp_twsize (cfftp_plan plan) + { + size_t twsize=0, l1=1; + for (size_t k=0; k<plan->nfct; ++k) + { + size_t ip=plan->fct[k].fct, ido= plan->length/(l1*ip); + twsize+=(ip-1)*(ido-1); + if (ip>11) + twsize+=ip; + l1*=ip; + } + return twsize; + } + +NOINLINE WARN_UNUSED_RESULT static int cfftp_comp_twiddle (cfftp_plan plan) + { + size_t length=plan->length; + double *twid = RALLOC(double, 2*length); + if (!twid) return -1; + sincos_2pibyn(length, twid); + size_t l1=1; + size_t memofs=0; + for (size_t k=0; k<plan->nfct; ++k) + { + size_t ip=plan->fct[k].fct, ido= length/(l1*ip); + plan->fct[k].tw=plan->mem+memofs; + memofs+=(ip-1)*(ido-1); + for (size_t j=1; j<ip; ++j) + for (size_t i=1; i<ido; ++i) + { + plan->fct[k].tw[(j-1)*(ido-1)+i-1].r = twid[2*j*l1*i]; + plan->fct[k].tw[(j-1)*(ido-1)+i-1].i = twid[2*j*l1*i+1]; + } + if (ip>11) + { + plan->fct[k].tws=plan->mem+memofs; + memofs+=ip; + for (size_t j=0; j<ip; ++j) + { + plan->fct[k].tws[j].r = twid[2*j*l1*ido]; + plan->fct[k].tws[j].i = twid[2*j*l1*ido+1]; + } + } + l1*=ip; + } + DEALLOC(twid); + return 0; + } + +static cfftp_plan make_cfftp_plan (size_t length) + { + if (length==0) return NULL; + cfftp_plan plan = RALLOC(cfftp_plan_i,1); + if (!plan) return NULL; + plan->length=length; + plan->nfct=0; + for (size_t i=0; i<NFCT; ++i) + plan->fct[i]=(cfftp_fctdata){0,0,0}; + plan->mem=0; + if (length==1) return plan; + if (cfftp_factorize(plan)!=0) { DEALLOC(plan); return NULL; } + size_t tws=cfftp_twsize(plan); + plan->mem=RALLOC(cmplx,tws); + if (!plan->mem) { DEALLOC(plan); return NULL; } + if (cfftp_comp_twiddle(plan)!=0) + { DEALLOC(plan->mem); DEALLOC(plan); return NULL; } + return plan; + } + +static void destroy_cfftp_plan (cfftp_plan plan) + { + DEALLOC(plan->mem); + DEALLOC(plan); + } + +typedef struct rfftp_fctdata + { + size_t fct; + double *tw, *tws; + } rfftp_fctdata; + +typedef struct rfftp_plan_i + { + size_t length, nfct; + double *mem; + rfftp_fctdata fct[NFCT]; + } rfftp_plan_i; +typedef struct rfftp_plan_i * rfftp_plan; + +#define WA(x,i) wa[(i)+(x)*(ido-1)] +#define PM(a,b,c,d) { a=c+d; b=c-d; } +/* (a+ib) = conj(c+id) * (e+if) */ +#define MULPM(a,b,c,d,e,f) { a=c*e+d*f; b=c*f-d*e; } + +#define CC(a,b,c) cc[(a)+ido*((b)+l1*(c))] +#define CH(a,b,c) ch[(a)+ido*((b)+cdim*(c))] + +NOINLINE static void radf2 (size_t ido, size_t l1, const double * restrict cc, + double * restrict ch, const double * restrict wa) + { + const size_t cdim=2; + + for (size_t k=0; k<l1; k++) + PM (CH(0,0,k),CH(ido-1,1,k),CC(0,k,0),CC(0,k,1)) + if ((ido&1)==0) + for (size_t k=0; k<l1; k++) + { + CH( 0,1,k) = -CC(ido-1,k,1); + CH(ido-1,0,k) = CC(ido-1,k,0); + } + if (ido<=2) return; + for (size_t k=0; k<l1; k++) + for (size_t i=2; i<ido; i+=2) + { + size_t ic=ido-i; + double tr2, ti2; + MULPM (tr2,ti2,WA(0,i-2),WA(0,i-1),CC(i-1,k,1),CC(i,k,1)) + PM (CH(i-1,0,k),CH(ic-1,1,k),CC(i-1,k,0),tr2) + PM (CH(i ,0,k),CH(ic ,1,k),ti2,CC(i ,k,0)) + } + } + +NOINLINE static void radf3(size_t ido, size_t l1, const double * restrict cc, + double * restrict ch, const double * restrict wa) + { + const size_t cdim=3; + static const double taur=-0.5, taui=0.86602540378443864676; + + for (size_t k=0; k<l1; k++) + { + double cr2=CC(0,k,1)+CC(0,k,2); + CH(0,0,k) = CC(0,k,0)+cr2; + CH(0,2,k) = taui*(CC(0,k,2)-CC(0,k,1)); + CH(ido-1,1,k) = CC(0,k,0)+taur*cr2; + } + if (ido==1) return; + for (size_t k=0; k<l1; k++) + for (size_t i=2; i<ido; i+=2) + { + size_t ic=ido-i; + double di2, di3, dr2, dr3; + MULPM (dr2,di2,WA(0,i-2),WA(0,i-1),CC(i-1,k,1),CC(i,k,1)) // d2=conj(WA0)*CC1 + MULPM (dr3,di3,WA(1,i-2),WA(1,i-1),CC(i-1,k,2),CC(i,k,2)) // d3=conj(WA1)*CC2 + double cr2=dr2+dr3; // c add + double ci2=di2+di3; + CH(i-1,0,k) = CC(i-1,k,0)+cr2; // c add + CH(i ,0,k) = CC(i ,k,0)+ci2; + double tr2 = CC(i-1,k,0)+taur*cr2; // c add + double ti2 = CC(i ,k,0)+taur*ci2; + double tr3 = taui*(di2-di3); // t3 = taui*i*(d3-d2)? + double ti3 = taui*(dr3-dr2); + PM(CH(i-1,2,k),CH(ic-1,1,k),tr2,tr3) // PM(i) = t2+t3 + PM(CH(i ,2,k),CH(ic ,1,k),ti3,ti2) // PM(ic) = conj(t2-t3) + } + } + +NOINLINE static void radf4(size_t ido, size_t l1, const double * restrict cc, + double * restrict ch, const double * restrict wa) + { + const size_t cdim=4; + static const double hsqt2=0.70710678118654752440; + + for (size_t k=0; k<l1; k++) + { + double tr1,tr2; + PM (tr1,CH(0,2,k),CC(0,k,3),CC(0,k,1)) + PM (tr2,CH(ido-1,1,k),CC(0,k,0),CC(0,k,2)) + PM (CH(0,0,k),CH(ido-1,3,k),tr2,tr1) + } + if ((ido&1)==0) + for (size_t k=0; k<l1; k++) + { + double ti1=-hsqt2*(CC(ido-1,k,1)+CC(ido-1,k,3)); + double tr1= hsqt2*(CC(ido-1,k,1)-CC(ido-1,k,3)); + PM (CH(ido-1,0,k),CH(ido-1,2,k),CC(ido-1,k,0),tr1) + PM (CH( 0,3,k),CH( 0,1,k),ti1,CC(ido-1,k,2)) + } + if (ido<=2) return; + for (size_t k=0; k<l1; k++) + for (size_t i=2; i<ido; i+=2) + { + size_t ic=ido-i; + double ci2, ci3, ci4, cr2, cr3, cr4, ti1, ti2, ti3, ti4, tr1, tr2, tr3, tr4; + MULPM(cr2,ci2,WA(0,i-2),WA(0,i-1),CC(i-1,k,1),CC(i,k,1)) + MULPM(cr3,ci3,WA(1,i-2),WA(1,i-1),CC(i-1,k,2),CC(i,k,2)) + MULPM(cr4,ci4,WA(2,i-2),WA(2,i-1),CC(i-1,k,3),CC(i,k,3)) + PM(tr1,tr4,cr4,cr2) + PM(ti1,ti4,ci2,ci4) + PM(tr2,tr3,CC(i-1,k,0),cr3) + PM(ti2,ti3,CC(i ,k,0),ci3) + PM(CH(i-1,0,k),CH(ic-1,3,k),tr2,tr1) + PM(CH(i ,0,k),CH(ic ,3,k),ti1,ti2) + PM(CH(i-1,2,k),CH(ic-1,1,k),tr3,ti4) + PM(CH(i ,2,k),CH(ic ,1,k),tr4,ti3) + } + } + +NOINLINE static void radf5(size_t ido, size_t l1, const double * restrict cc, + double * restrict ch, const double * restrict wa) + { + const size_t cdim=5; + static const double tr11= 0.3090169943749474241, ti11=0.95105651629515357212, + tr12=-0.8090169943749474241, ti12=0.58778525229247312917; + + for (size_t k=0; k<l1; k++) + { + double cr2, cr3, ci4, ci5; + PM (cr2,ci5,CC(0,k,4),CC(0,k,1)) + PM (cr3,ci4,CC(0,k,3),CC(0,k,2)) + CH(0,0,k)=CC(0,k,0)+cr2+cr3; + CH(ido-1,1,k)=CC(0,k,0)+tr11*cr2+tr12*cr3; + CH(0,2,k)=ti11*ci5+ti12*ci4; + CH(ido-1,3,k)=CC(0,k,0)+tr12*cr2+tr11*cr3; + CH(0,4,k)=ti12*ci5-ti11*ci4; + } + if (ido==1) return; + for (size_t k=0; k<l1;++k) + for (size_t i=2; i<ido; i+=2) + { + double ci2, di2, ci4, ci5, di3, di4, di5, ci3, cr2, cr3, dr2, dr3, + dr4, dr5, cr5, cr4, ti2, ti3, ti5, ti4, tr2, tr3, tr4, tr5; + size_t ic=ido-i; + MULPM (dr2,di2,WA(0,i-2),WA(0,i-1),CC(i-1,k,1),CC(i,k,1)) + MULPM (dr3,di3,WA(1,i-2),WA(1,i-1),CC(i-1,k,2),CC(i,k,2)) + MULPM (dr4,di4,WA(2,i-2),WA(2,i-1),CC(i-1,k,3),CC(i,k,3)) + MULPM (dr5,di5,WA(3,i-2),WA(3,i-1),CC(i-1,k,4),CC(i,k,4)) + PM(cr2,ci5,dr5,dr2) + PM(ci2,cr5,di2,di5) + PM(cr3,ci4,dr4,dr3) + PM(ci3,cr4,di3,di4) + CH(i-1,0,k)=CC(i-1,k,0)+cr2+cr3; + CH(i ,0,k)=CC(i ,k,0)+ci2+ci3; + tr2=CC(i-1,k,0)+tr11*cr2+tr12*cr3; + ti2=CC(i ,k,0)+tr11*ci2+tr12*ci3; + tr3=CC(i-1,k,0)+tr12*cr2+tr11*cr3; + ti3=CC(i ,k,0)+tr12*ci2+tr11*ci3; + MULPM(tr5,tr4,cr5,cr4,ti11,ti12) + MULPM(ti5,ti4,ci5,ci4,ti11,ti12) + PM(CH(i-1,2,k),CH(ic-1,1,k),tr2,tr5) + PM(CH(i ,2,k),CH(ic ,1,k),ti5,ti2) + PM(CH(i-1,4,k),CH(ic-1,3,k),tr3,tr4) + PM(CH(i ,4,k),CH(ic ,3,k),ti4,ti3) + } + } + +#undef CC +#undef CH +#define C1(a,b,c) cc[(a)+ido*((b)+l1*(c))] +#define C2(a,b) cc[(a)+idl1*(b)] +#define CH2(a,b) ch[(a)+idl1*(b)] +#define CC(a,b,c) cc[(a)+ido*((b)+cdim*(c))] +#define CH(a,b,c) ch[(a)+ido*((b)+l1*(c))] +NOINLINE static void radfg(size_t ido, size_t ip, size_t l1, + double * restrict cc, double * restrict ch, const double * restrict wa, + const double * restrict csarr) + { + const size_t cdim=ip; + size_t ipph=(ip+1)/2; + size_t idl1 = ido*l1; + + if (ido>1) + { + for (size_t j=1, jc=ip-1; j<ipph; ++j,--jc) // 114 + { + size_t is=(j-1)*(ido-1), + is2=(jc-1)*(ido-1); + for (size_t k=0; k<l1; ++k) // 113 + { + size_t idij=is; + size_t idij2=is2; + for (size_t i=1; i<=ido-2; i+=2) // 112 + { + double t1=C1(i,k,j ), t2=C1(i+1,k,j ), + t3=C1(i,k,jc), t4=C1(i+1,k,jc); + double x1=wa[idij]*t1 + wa[idij+1]*t2, + x2=wa[idij]*t2 - wa[idij+1]*t1, + x3=wa[idij2]*t3 + wa[idij2+1]*t4, + x4=wa[idij2]*t4 - wa[idij2+1]*t3; + C1(i ,k,j ) = x1+x3; + C1(i ,k,jc) = x2-x4; + C1(i+1,k,j ) = x2+x4; + C1(i+1,k,jc) = x3-x1; + idij+=2; + idij2+=2; + } + } + } + } + + for (size_t j=1, jc=ip-1; j<ipph; ++j,--jc) // 123 + for (size_t k=0; k<l1; ++k) // 122 + { + double t1=C1(0,k,j), t2=C1(0,k,jc); + C1(0,k,j ) = t1+t2; + C1(0,k,jc) = t2-t1; + } + +//everything in C +//memset(ch,0,ip*l1*ido*sizeof(double)); + + for (size_t l=1,lc=ip-1; l<ipph; ++l,--lc) // 127 + { + for (size_t ik=0; ik<idl1; ++ik) // 124 + { + CH2(ik,l ) = C2(ik,0)+csarr[2*l]*C2(ik,1)+csarr[4*l]*C2(ik,2); + CH2(ik,lc) = csarr[2*l+1]*C2(ik,ip-1)+csarr[4*l+1]*C2(ik,ip-2); + } + size_t iang = 2*l; + size_t j=3, jc=ip-3; + for (; j<ipph-3; j+=4,jc-=4) // 126 + { + iang+=l; if (iang>=ip) iang-=ip; + double ar1=csarr[2*iang], ai1=csarr[2*iang+1]; + iang+=l; if (iang>=ip) iang-=ip; + double ar2=csarr[2*iang], ai2=csarr[2*iang+1]; + iang+=l; if (iang>=ip) iang-=ip; + double ar3=csarr[2*iang], ai3=csarr[2*iang+1]; + iang+=l; if (iang>=ip) iang-=ip; + double ar4=csarr[2*iang], ai4=csarr[2*iang+1]; + for (size_t ik=0; ik<idl1; ++ik) // 125 + { + CH2(ik,l ) += ar1*C2(ik,j )+ar2*C2(ik,j +1) + +ar3*C2(ik,j +2)+ar4*C2(ik,j +3); + CH2(ik,lc) += ai1*C2(ik,jc)+ai2*C2(ik,jc-1) + +ai3*C2(ik,jc-2)+ai4*C2(ik,jc-3); + } + } + for (; j<ipph-1; j+=2,jc-=2) // 126 + { + iang+=l; if (iang>=ip) iang-=ip; + double ar1=csarr[2*iang], ai1=csarr[2*iang+1]; + iang+=l; if (iang>=ip) iang-=ip; + double ar2=csarr[2*iang], ai2=csarr[2*iang+1]; + for (size_t ik=0; ik<idl1; ++ik) // 125 + { + CH2(ik,l ) += ar1*C2(ik,j )+ar2*C2(ik,j +1); + CH2(ik,lc) += ai1*C2(ik,jc)+ai2*C2(ik,jc-1); + } + } + for (; j<ipph; ++j,--jc) // 126 + { + iang+=l; if (iang>=ip) iang-=ip; + double ar=csarr[2*iang], ai=csarr[2*iang+1]; + for (size_t ik=0; ik<idl1; ++ik) // 125 + { + CH2(ik,l ) += ar*C2(ik,j ); + CH2(ik,lc) += ai*C2(ik,jc); + } + } + } + for (size_t ik=0; ik<idl1; ++ik) // 101 + CH2(ik,0) = C2(ik,0); + for (size_t j=1; j<ipph; ++j) // 129 + for (size_t ik=0; ik<idl1; ++ik) // 128 + CH2(ik,0) += C2(ik,j); + +// everything in CH at this point! +//memset(cc,0,ip*l1*ido*sizeof(double)); + + for (size_t k=0; k<l1; ++k) // 131 + for (size_t i=0; i<ido; ++i) // 130 + CC(i,0,k) = CH(i,k,0); + + for (size_t j=1, jc=ip-1; j<ipph; ++j,--jc) // 137 + { + size_t j2=2*j-1; + for (size_t k=0; k<l1; ++k) // 136 + { + CC(ido-1,j2,k) = CH(0,k,j); + CC(0,j2+1,k) = CH(0,k,jc); + } + } + + if (ido==1) return; + + for (size_t j=1, jc=ip-1; j<ipph; ++j,--jc) // 140 + { + size_t j2=2*j-1; + for(size_t k=0; k<l1; ++k) // 139 + for(size_t i=1, ic=ido-i-2; i<=ido-2; i+=2, ic-=2) // 138 + { + CC(i ,j2+1,k) = CH(i ,k,j )+CH(i ,k,jc); + CC(ic ,j2 ,k) = CH(i ,k,j )-CH(i ,k,jc); + CC(i+1 ,j2+1,k) = CH(i+1,k,j )+CH(i+1,k,jc); + CC(ic+1,j2 ,k) = CH(i+1,k,jc)-CH(i+1,k,j ); + } + } + } +#undef C1 +#undef C2 +#undef CH2 + +#undef CH +#undef CC +#define CH(a,b,c) ch[(a)+ido*((b)+l1*(c))] +#define CC(a,b,c) cc[(a)+ido*((b)+cdim*(c))] + +NOINLINE static void radb2(size_t ido, size_t l1, const double * restrict cc, + double * restrict ch, const double * restrict wa) + { + const size_t cdim=2; + + for (size_t k=0; k<l1; k++) + PM (CH(0,k,0),CH(0,k,1),CC(0,0,k),CC(ido-1,1,k)) + if ((ido&1)==0) + for (size_t k=0; k<l1; k++) + { + CH(ido-1,k,0) = 2.*CC(ido-1,0,k); + CH(ido-1,k,1) =-2.*CC(0 ,1,k); + } + if (ido<=2) return; + for (size_t k=0; k<l1;++k) + for (size_t i=2; i<ido; i+=2) + { + size_t ic=ido-i; + double ti2, tr2; + PM (CH(i-1,k,0),tr2,CC(i-1,0,k),CC(ic-1,1,k)) + PM (ti2,CH(i ,k,0),CC(i ,0,k),CC(ic ,1,k)) + MULPM (CH(i,k,1),CH(i-1,k,1),WA(0,i-2),WA(0,i-1),ti2,tr2) + } + } + +NOINLINE static void radb3(size_t ido, size_t l1, const double * restrict cc, + double * restrict ch, const double * restrict wa) + { + const size_t cdim=3; + static const double taur=-0.5, taui=0.86602540378443864676; + + for (size_t k=0; k<l1; k++) + { + double tr2=2.*CC(ido-1,1,k); + double cr2=CC(0,0,k)+taur*tr2; + CH(0,k,0)=CC(0,0,k)+tr2; + double ci3=2.*taui*CC(0,2,k); + PM (CH(0,k,2),CH(0,k,1),cr2,ci3); + } + if (ido==1) return; + for (size_t k=0; k<l1; k++) + for (size_t i=2; i<ido; i+=2) + { + size_t ic=ido-i; + double tr2=CC(i-1,2,k)+CC(ic-1,1,k); // t2=CC(I) + conj(CC(ic)) + double ti2=CC(i ,2,k)-CC(ic ,1,k); + double cr2=CC(i-1,0,k)+taur*tr2; // c2=CC +taur*t2 + double ci2=CC(i ,0,k)+taur*ti2; + CH(i-1,k,0)=CC(i-1,0,k)+tr2; // CH=CC+t2 + CH(i ,k,0)=CC(i ,0,k)+ti2; + double cr3=taui*(CC(i-1,2,k)-CC(ic-1,1,k));// c3=taui*(CC(i)-conj(CC(ic))) + double ci3=taui*(CC(i ,2,k)+CC(ic ,1,k)); + double di2, di3, dr2, dr3; + PM(dr3,dr2,cr2,ci3) // d2= (cr2-ci3, ci2+cr3) = c2+i*c3 + PM(di2,di3,ci2,cr3) // d3= (cr2+ci3, ci2-cr3) = c2-i*c3 + MULPM(CH(i,k,1),CH(i-1,k,1),WA(0,i-2),WA(0,i-1),di2,dr2) // ch = WA*d2 + MULPM(CH(i,k,2),CH(i-1,k,2),WA(1,i-2),WA(1,i-1),di3,dr3) + } + } + +NOINLINE static void radb4(size_t ido, size_t l1, const double * restrict cc, + double * restrict ch, const double * restrict wa) + { + const size_t cdim=4; + static const double sqrt2=1.41421356237309504880; + + for (size_t k=0; k<l1; k++) + { + double tr1, tr2; + PM (tr2,tr1,CC(0,0,k),CC(ido-1,3,k)) + double tr3=2.*CC(ido-1,1,k); + double tr4=2.*CC(0,2,k); + PM (CH(0,k,0),CH(0,k,2),tr2,tr3) + PM (CH(0,k,3),CH(0,k,1),tr1,tr4) + } + if ((ido&1)==0) + for (size_t k=0; k<l1; k++) + { + double tr1,tr2,ti1,ti2; + PM (ti1,ti2,CC(0 ,3,k),CC(0 ,1,k)) + PM (tr2,tr1,CC(ido-1,0,k),CC(ido-1,2,k)) + CH(ido-1,k,0)=tr2+tr2; + CH(ido-1,k,1)=sqrt2*(tr1-ti1); + CH(ido-1,k,2)=ti2+ti2; + CH(ido-1,k,3)=-sqrt2*(tr1+ti1); + } + if (ido<=2) return; + for (size_t k=0; k<l1;++k) + for (size_t i=2; i<ido; i+=2) + { + double ci2, ci3, ci4, cr2, cr3, cr4, ti1, ti2, ti3, ti4, tr1, tr2, tr3, tr4; + size_t ic=ido-i; + PM (tr2,tr1,CC(i-1,0,k),CC(ic-1,3,k)) + PM (ti1,ti2,CC(i ,0,k),CC(ic ,3,k)) + PM (tr4,ti3,CC(i ,2,k),CC(ic ,1,k)) + PM (tr3,ti4,CC(i-1,2,k),CC(ic-1,1,k)) + PM (CH(i-1,k,0),cr3,tr2,tr3) + PM (CH(i ,k,0),ci3,ti2,ti3) + PM (cr4,cr2,tr1,tr4) + PM (ci2,ci4,ti1,ti4) + MULPM (CH(i,k,1),CH(i-1,k,1),WA(0,i-2),WA(0,i-1),ci2,cr2) + MULPM (CH(i,k,2),CH(i-1,k,2),WA(1,i-2),WA(1,i-1),ci3,cr3) + MULPM (CH(i,k,3),CH(i-1,k,3),WA(2,i-2),WA(2,i-1),ci4,cr4) + } + } + +NOINLINE static void radb5(size_t ido, size_t l1, const double * restrict cc, + double * restrict ch, const double * restrict wa) + { + const size_t cdim=5; + static const double tr11= 0.3090169943749474241, ti11=0.95105651629515357212, + tr12=-0.8090169943749474241, ti12=0.58778525229247312917; + + for (size_t k=0; k<l1; k++) + { + double ti5=CC(0,2,k)+CC(0,2,k); + double ti4=CC(0,4,k)+CC(0,4,k); + double tr2=CC(ido-1,1,k)+CC(ido-1,1,k); + double tr3=CC(ido-1,3,k)+CC(ido-1,3,k); + CH(0,k,0)=CC(0,0,k)+tr2+tr3; + double cr2=CC(0,0,k)+tr11*tr2+tr12*tr3; + double cr3=CC(0,0,k)+tr12*tr2+tr11*tr3; + double ci4, ci5; + MULPM(ci5,ci4,ti5,ti4,ti11,ti12) + PM(CH(0,k,4),CH(0,k,1),cr2,ci5) + PM(CH(0,k,3),CH(0,k,2),cr3,ci4) + } + if (ido==1) return; + for (size_t k=0; k<l1;++k) + for (size_t i=2; i<ido; i+=2) + { + size_t ic=ido-i; + double tr2, tr3, tr4, tr5, ti2, ti3, ti4, ti5; + PM(tr2,tr5,CC(i-1,2,k),CC(ic-1,1,k)) + PM(ti5,ti2,CC(i ,2,k),CC(ic ,1,k)) + PM(tr3,tr4,CC(i-1,4,k),CC(ic-1,3,k)) + PM(ti4,ti3,CC(i ,4,k),CC(ic ,3,k)) + CH(i-1,k,0)=CC(i-1,0,k)+tr2+tr3; + CH(i ,k,0)=CC(i ,0,k)+ti2+ti3; + double cr2=CC(i-1,0,k)+tr11*tr2+tr12*tr3; + double ci2=CC(i ,0,k)+tr11*ti2+tr12*ti3; + double cr3=CC(i-1,0,k)+tr12*tr2+tr11*tr3; + double ci3=CC(i ,0,k)+tr12*ti2+tr11*ti3; + double ci4, ci5, cr5, cr4; + MULPM(cr5,cr4,tr5,tr4,ti11,ti12) + MULPM(ci5,ci4,ti5,ti4,ti11,ti12) + double dr2, dr3, dr4, dr5, di2, di3, di4, di5; + PM(dr4,dr3,cr3,ci4) + PM(di3,di4,ci3,cr4) + PM(dr5,dr2,cr2,ci5) + PM(di2,di5,ci2,cr5) + MULPM(CH(i,k,1),CH(i-1,k,1),WA(0,i-2),WA(0,i-1),di2,dr2) + MULPM(CH(i,k,2),CH(i-1,k,2),WA(1,i-2),WA(1,i-1),di3,dr3) + MULPM(CH(i,k,3),CH(i-1,k,3),WA(2,i-2),WA(2,i-1),di4,dr4) + MULPM(CH(i,k,4),CH(i-1,k,4),WA(3,i-2),WA(3,i-1),di5,dr5) + } + } + +#undef CC +#undef CH +#define CC(a,b,c) cc[(a)+ido*((b)+cdim*(c))] +#define CH(a,b,c) ch[(a)+ido*((b)+l1*(c))] +#define C1(a,b,c) cc[(a)+ido*((b)+l1*(c))] +#define C2(a,b) cc[(a)+idl1*(b)] +#define CH2(a,b) ch[(a)+idl1*(b)] + +NOINLINE static void radbg(size_t ido, size_t ip, size_t l1, + double * restrict cc, double * restrict ch, const double * restrict wa, + const double * restrict csarr) + { + const size_t cdim=ip; + size_t ipph=(ip+1)/ 2; + size_t idl1 = ido*l1; + + for (size_t k=0; k<l1; ++k) // 102 + for (size_t i=0; i<ido; ++i) // 101 + CH(i,k,0) = CC(i,0,k); + for (size_t j=1, jc=ip-1; j<ipph; ++j, --jc) // 108 + { + size_t j2=2*j-1; + for (size_t k=0; k<l1; ++k) + { + CH(0,k,j ) = 2*CC(ido-1,j2,k); + CH(0,k,jc) = 2*CC(0,j2+1,k); + } + } + + if (ido!=1) + { + for (size_t j=1, jc=ip-1; j<ipph; ++j,--jc) // 111 + { + size_t j2=2*j-1; + for (size_t k=0; k<l1; ++k) + for (size_t i=1, ic=ido-i-2; i<=ido-2; i+=2, ic-=2) // 109 + { + CH(i ,k,j ) = CC(i ,j2+1,k)+CC(ic ,j2,k); + CH(i ,k,jc) = CC(i ,j2+1,k)-CC(ic ,j2,k); + CH(i+1,k,j ) = CC(i+1,j2+1,k)-CC(ic+1,j2,k); + CH(i+1,k,jc) = CC(i+1,j2+1,k)+CC(ic+1,j2,k); + } + } + } + for (size_t l=1,lc=ip-1; l<ipph; ++l,--lc) + { + for (size_t ik=0; ik<idl1; ++ik) + { + C2(ik,l ) = CH2(ik,0)+csarr[2*l]*CH2(ik,1)+csarr[4*l]*CH2(ik,2); + C2(ik,lc) = csarr[2*l+1]*CH2(ik,ip-1)+csarr[4*l+1]*CH2(ik,ip-2); + } + size_t iang=2*l; + size_t j=3,jc=ip-3; + for(; j<ipph-3; j+=4,jc-=4) + { + iang+=l; if(iang>ip) iang-=ip; + double ar1=csarr[2*iang], ai1=csarr[2*iang+1]; + iang+=l; if(iang>ip) iang-=ip; + double ar2=csarr[2*iang], ai2=csarr[2*iang+1]; + iang+=l; if(iang>ip) iang-=ip; + double ar3=csarr[2*iang], ai3=csarr[2*iang+1]; + iang+=l; if(iang>ip) iang-=ip; + double ar4=csarr[2*iang], ai4=csarr[2*iang+1]; + for (size_t ik=0; ik<idl1; ++ik) + { + C2(ik,l ) += ar1*CH2(ik,j )+ar2*CH2(ik,j +1) + +ar3*CH2(ik,j +2)+ar4*CH2(ik,j +3); + C2(ik,lc) += ai1*CH2(ik,jc)+ai2*CH2(ik,jc-1) + +ai3*CH2(ik,jc-2)+ai4*CH2(ik,jc-3); + } + } + for(; j<ipph-1; j+=2,jc-=2) + { + iang+=l; if(iang>ip) iang-=ip; + double ar1=csarr[2*iang], ai1=csarr[2*iang+1]; + iang+=l; if(iang>ip) iang-=ip; + double ar2=csarr[2*iang], ai2=csarr[2*iang+1]; + for (size_t ik=0; ik<idl1; ++ik) + { + C2(ik,l ) += ar1*CH2(ik,j )+ar2*CH2(ik,j +1); + C2(ik,lc) += ai1*CH2(ik,jc)+ai2*CH2(ik,jc-1); + } + } + for(; j<ipph; ++j,--jc) + { + iang+=l; if(iang>ip) iang-=ip; + double war=csarr[2*iang], wai=csarr[2*iang+1]; + for (size_t ik=0; ik<idl1; ++ik) + { + C2(ik,l ) += war*CH2(ik,j ); + C2(ik,lc) += wai*CH2(ik,jc); + } + } + } + for (size_t j=1; j<ipph; ++j) + for (size_t ik=0; ik<idl1; ++ik) + CH2(ik,0) += CH2(ik,j); + for (size_t j=1, jc=ip-1; j<ipph; ++j,--jc) // 124 + for (size_t k=0; k<l1; ++k) + { + CH(0,k,j ) = C1(0,k,j)-C1(0,k,jc); + CH(0,k,jc) = C1(0,k,j)+C1(0,k,jc); + } + + if (ido==1) return; + + for (size_t j=1, jc=ip-1; j<ipph; ++j, --jc) // 127 + for (size_t k=0; k<l1; ++k) + for (size_t i=1; i<=ido-2; i+=2) + { + CH(i ,k,j ) = C1(i ,k,j)-C1(i+1,k,jc); + CH(i ,k,jc) = C1(i ,k,j)+C1(i+1,k,jc); + CH(i+1,k,j ) = C1(i+1,k,j)+C1(i ,k,jc); + CH(i+1,k,jc) = C1(i+1,k,j)-C1(i ,k,jc); + } + +// All in CH + + for (size_t j=1; j<ip; ++j) + { + size_t is = (j-1)*(ido-1); + for (size_t k=0; k<l1; ++k) + { + size_t idij = is; + for (size_t i=1; i<=ido-2; i+=2) + { + double t1=CH(i,k,j), t2=CH(i+1,k,j); + CH(i ,k,j) = wa[idij]*t1-wa[idij+1]*t2; + CH(i+1,k,j) = wa[idij]*t2+wa[idij+1]*t1; + idij+=2; + } + } + } + } +#undef C1 +#undef C2 +#undef CH2 + +#undef CC +#undef CH +#undef PM +#undef MULPM +#undef WA + +static void copy_and_norm(double *c, double *p1, size_t n, double fct) + { + if (p1!=c) + { + if (fct!=1.) + for (size_t i=0; i<n; ++i) + c[i] = fct*p1[i]; + else + memcpy (c,p1,n*sizeof(double)); + } + else + if (fct!=1.) + for (size_t i=0; i<n; ++i) + c[i] *= fct; + } + +WARN_UNUSED_RESULT +static int rfftp_forward(rfftp_plan plan, double c[], double fct) + { + if (plan->length==1) return 0; + size_t n=plan->length; + size_t l1=n, nf=plan->nfct; + double *ch = RALLOC(double, n); + if (!ch) return -1; + double *p1=c, *p2=ch; + + for(size_t k1=0; k1<nf;++k1) + { + size_t k=nf-k1-1; + size_t ip=plan->fct[k].fct; + size_t ido=n / l1; + l1 /= ip; + if(ip==4) + radf4(ido, l1, p1, p2, plan->fct[k].tw); + else if(ip==2) + radf2(ido, l1, p1, p2, plan->fct[k].tw); + else if(ip==3) + radf3(ido, l1, p1, p2, plan->fct[k].tw); + else if(ip==5) + radf5(ido, l1, p1, p2, plan->fct[k].tw); + else + { + radfg(ido, ip, l1, p1, p2, plan->fct[k].tw, plan->fct[k].tws); + SWAP (p1,p2,double *); + } + SWAP (p1,p2,double *); + } + copy_and_norm(c,p1,n,fct); + DEALLOC(ch); + return 0; + } + +WARN_UNUSED_RESULT +static int rfftp_backward(rfftp_plan plan, double c[], double fct) + { + if (plan->length==1) return 0; + size_t n=plan->length; + size_t l1=1, nf=plan->nfct; + double *ch = RALLOC(double, n); + if (!ch) return -1; + double *p1=c, *p2=ch; + + for(size_t k=0; k<nf; k++) + { + size_t ip = plan->fct[k].fct, + ido= n/(ip*l1); + if(ip==4) + radb4(ido, l1, p1, p2, plan->fct[k].tw); + else if(ip==2) + radb2(ido, l1, p1, p2, plan->fct[k].tw); + else if(ip==3) + radb3(ido, l1, p1, p2, plan->fct[k].tw); + else if(ip==5) + radb5(ido, l1, p1, p2, plan->fct[k].tw); + else + radbg(ido, ip, l1, p1, p2, plan->fct[k].tw, plan->fct[k].tws); + SWAP (p1,p2,double *); + l1*=ip; + } + copy_and_norm(c,p1,n,fct); + DEALLOC(ch); + return 0; + } + +WARN_UNUSED_RESULT +static int rfftp_factorize (rfftp_plan plan) + { + size_t length=plan->length; + size_t nfct=0; + while ((length%4)==0) + { if (nfct>=NFCT) return -1; plan->fct[nfct++].fct=4; length>>=2; } + if ((length%2)==0) + { + length>>=1; + // factor 2 should be at the front of the factor list + if (nfct>=NFCT) return -1; + plan->fct[nfct++].fct=2; + SWAP(plan->fct[0].fct, plan->fct[nfct-1].fct,size_t); + } + size_t maxl=(size_t)(sqrt((double)length))+1; + for (size_t divisor=3; (length>1)&&(divisor<maxl); divisor+=2) + if ((length%divisor)==0) + { + while ((length%divisor)==0) + { + if (nfct>=NFCT) return -1; + plan->fct[nfct++].fct=divisor; + length/=divisor; + } + maxl=(size_t)(sqrt((double)length))+1; + } + if (length>1) plan->fct[nfct++].fct=length; + plan->nfct=nfct; + return 0; + } + +static size_t rfftp_twsize(rfftp_plan plan) + { + size_t twsize=0, l1=1; + for (size_t k=0; k<plan->nfct; ++k) + { + size_t ip=plan->fct[k].fct, ido= plan->length/(l1*ip); + twsize+=(ip-1)*(ido-1); + if (ip>5) twsize+=2*ip; + l1*=ip; + } + return twsize; + return 0; + } + +WARN_UNUSED_RESULT NOINLINE static int rfftp_comp_twiddle (rfftp_plan plan) + { + size_t length=plan->length; + double *twid = RALLOC(double, 2*length); + if (!twid) return -1; + sincos_2pibyn_half(length, twid); + size_t l1=1; + double *ptr=plan->mem; + for (size_t k=0; k<plan->nfct; ++k) + { + size_t ip=plan->fct[k].fct, ido=length/(l1*ip); + if (k<plan->nfct-1) // last factor doesn't need twiddles + { + plan->fct[k].tw=ptr; ptr+=(ip-1)*(ido-1); + for (size_t j=1; j<ip; ++j) + for (size_t i=1; i<=(ido-1)/2; ++i) + { + plan->fct[k].tw[(j-1)*(ido-1)+2*i-2] = twid[2*j*l1*i]; + plan->fct[k].tw[(j-1)*(ido-1)+2*i-1] = twid[2*j*l1*i+1]; + } + } + if (ip>5) // special factors required by *g functions + { + plan->fct[k].tws=ptr; ptr+=2*ip; + plan->fct[k].tws[0] = 1.; + plan->fct[k].tws[1] = 0.; + for (size_t i=1; i<=(ip>>1); ++i) + { + plan->fct[k].tws[2*i ] = twid[2*i*(length/ip)]; + plan->fct[k].tws[2*i+1] = twid[2*i*(length/ip)+1]; + plan->fct[k].tws[2*(ip-i) ] = twid[2*i*(length/ip)]; + plan->fct[k].tws[2*(ip-i)+1] = -twid[2*i*(length/ip)+1]; + } + } + l1*=ip; + } + DEALLOC(twid); + return 0; + } + +NOINLINE static rfftp_plan make_rfftp_plan (size_t length) + { + if (length==0) return NULL; + rfftp_plan plan = RALLOC(rfftp_plan_i,1); + if (!plan) return NULL; + plan->length=length; + plan->nfct=0; + plan->mem=NULL; + for (size_t i=0; i<NFCT; ++i) + plan->fct[i]=(rfftp_fctdata){0,0,0}; + if (length==1) return plan; + if (rfftp_factorize(plan)!=0) { DEALLOC(plan); return NULL; } + size_t tws=rfftp_twsize(plan); + plan->mem=RALLOC(double,tws); + if (!plan->mem) { DEALLOC(plan); return NULL; } + if (rfftp_comp_twiddle(plan)!=0) + { DEALLOC(plan->mem); DEALLOC(plan); return NULL; } + return plan; + } + +NOINLINE static void destroy_rfftp_plan (rfftp_plan plan) + { + DEALLOC(plan->mem); + DEALLOC(plan); + } + +typedef struct fftblue_plan_i + { + size_t n, n2; + cfftp_plan plan; + double *mem; + double *bk, *bkf; + } fftblue_plan_i; +typedef struct fftblue_plan_i * fftblue_plan; + +NOINLINE static fftblue_plan make_fftblue_plan (size_t length) + { + fftblue_plan plan = RALLOC(fftblue_plan_i,1); + if (!plan) return NULL; + plan->n = length; + plan->n2 = good_size(plan->n*2-1); + plan->mem = RALLOC(double, 2*plan->n+2*plan->n2); + if (!plan->mem) { DEALLOC(plan); return NULL; } + plan->bk = plan->mem; + plan->bkf = plan->bk+2*plan->n; + +/* initialize b_k */ + double *tmp = RALLOC(double,4*plan->n); + if (!tmp) { DEALLOC(plan->mem); DEALLOC(plan); return NULL; } + sincos_2pibyn(2*plan->n,tmp); + plan->bk[0] = 1; + plan->bk[1] = 0; + + size_t coeff=0; + for (size_t m=1; m<plan->n; ++m) + { + coeff+=2*m-1; + if (coeff>=2*plan->n) coeff-=2*plan->n; + plan->bk[2*m ] = tmp[2*coeff ]; + plan->bk[2*m+1] = tmp[2*coeff+1]; + } + + /* initialize the zero-padded, Fourier transformed b_k. Add normalisation. */ + double xn2 = 1./plan->n2; + plan->bkf[0] = plan->bk[0]*xn2; + plan->bkf[1] = plan->bk[1]*xn2; + for (size_t m=2; m<2*plan->n; m+=2) + { + plan->bkf[m] = plan->bkf[2*plan->n2-m] = plan->bk[m] *xn2; + plan->bkf[m+1] = plan->bkf[2*plan->n2-m+1] = plan->bk[m+1] *xn2; + } + for (size_t m=2*plan->n;m<=(2*plan->n2-2*plan->n+1);++m) + plan->bkf[m]=0.; + plan->plan=make_cfftp_plan(plan->n2); + if (!plan->plan) + { DEALLOC(tmp); DEALLOC(plan->mem); DEALLOC(plan); return NULL; } + if (cfftp_forward(plan->plan,plan->bkf,1.)!=0) + { DEALLOC(tmp); DEALLOC(plan->mem); DEALLOC(plan); return NULL; } + DEALLOC(tmp); + + return plan; + } + +NOINLINE static void destroy_fftblue_plan (fftblue_plan plan) + { + DEALLOC(plan->mem); + destroy_cfftp_plan(plan->plan); + DEALLOC(plan); + } + +NOINLINE WARN_UNUSED_RESULT +static int fftblue_fft(fftblue_plan plan, double c[], int isign, double fct) + { + size_t n=plan->n; + size_t n2=plan->n2; + double *bk = plan->bk; + double *bkf = plan->bkf; + double *akf = RALLOC(double, 2*n2); + if (!akf) return -1; + +/* initialize a_k and FFT it */ + if (isign>0) + for (size_t m=0; m<2*n; m+=2) + { + akf[m] = c[m]*bk[m] - c[m+1]*bk[m+1]; + akf[m+1] = c[m]*bk[m+1] + c[m+1]*bk[m]; + } + else + for (size_t m=0; m<2*n; m+=2) + { + akf[m] = c[m]*bk[m] + c[m+1]*bk[m+1]; + akf[m+1] =-c[m]*bk[m+1] + c[m+1]*bk[m]; + } + for (size_t m=2*n; m<2*n2; ++m) + akf[m]=0; + + if (cfftp_forward (plan->plan,akf,fct)!=0) + { DEALLOC(akf); return -1; } + +/* do the convolution */ + if (isign>0) + for (size_t m=0; m<2*n2; m+=2) + { + double im = -akf[m]*bkf[m+1] + akf[m+1]*bkf[m]; + akf[m ] = akf[m]*bkf[m] + akf[m+1]*bkf[m+1]; + akf[m+1] = im; + } + else + for (size_t m=0; m<2*n2; m+=2) + { + double im = akf[m]*bkf[m+1] + akf[m+1]*bkf[m]; + akf[m ] = akf[m]*bkf[m] - akf[m+1]*bkf[m+1]; + akf[m+1] = im; + } + +/* inverse FFT */ + if (cfftp_backward (plan->plan,akf,1.)!=0) + { DEALLOC(akf); return -1; } + +/* multiply by b_k */ + if (isign>0) + for (size_t m=0; m<2*n; m+=2) + { + c[m] = bk[m] *akf[m] - bk[m+1]*akf[m+1]; + c[m+1] = bk[m+1]*akf[m] + bk[m] *akf[m+1]; + } + else + for (size_t m=0; m<2*n; m+=2) + { + c[m] = bk[m] *akf[m] + bk[m+1]*akf[m+1]; + c[m+1] =-bk[m+1]*akf[m] + bk[m] *akf[m+1]; + } + DEALLOC(akf); + return 0; + } + +WARN_UNUSED_RESULT +static int cfftblue_backward(fftblue_plan plan, double c[], double fct) + { return fftblue_fft(plan,c,1,fct); } + +WARN_UNUSED_RESULT +static int cfftblue_forward(fftblue_plan plan, double c[], double fct) + { return fftblue_fft(plan,c,-1,fct); } + +WARN_UNUSED_RESULT +static int rfftblue_backward(fftblue_plan plan, double c[], double fct) + { + size_t n=plan->n; + double *tmp = RALLOC(double,2*n); + if (!tmp) return -1; + tmp[0]=c[0]; + tmp[1]=0.; + memcpy (tmp+2,c+1, (n-1)*sizeof(double)); + if ((n&1)==0) tmp[n+1]=0.; + for (size_t m=2; m<n; m+=2) + { + tmp[2*n-m]=tmp[m]; + tmp[2*n-m+1]=-tmp[m+1]; + } + if (fftblue_fft(plan,tmp,1,fct)!=0) + { DEALLOC(tmp); return -1; } + for (size_t m=0; m<n; ++m) + c[m] = tmp[2*m]; + DEALLOC(tmp); + return 0; + } + +WARN_UNUSED_RESULT +static int rfftblue_forward(fftblue_plan plan, double c[], double fct) + { + size_t n=plan->n; + double *tmp = RALLOC(double,2*n); + if (!tmp) return -1; + for (size_t m=0; m<n; ++m) + { + tmp[2*m] = c[m]; + tmp[2*m+1] = 0.; + } + if (fftblue_fft(plan,tmp,-1,fct)!=0) + { DEALLOC(tmp); return -1; } + c[0] = tmp[0]; + memcpy (c+1, tmp+2, (n-1)*sizeof(double)); + DEALLOC(tmp); + return 0; + } + +typedef struct cfft_plan_i + { + cfftp_plan packplan; + fftblue_plan blueplan; + } cfft_plan_i; + +static cfft_plan make_cfft_plan (size_t length) + { + if (length==0) return NULL; + cfft_plan plan = RALLOC(cfft_plan_i,1); + if (!plan) return NULL; + plan->blueplan=0; + plan->packplan=0; + if ((length<50) || (largest_prime_factor(length)<=sqrt(length))) + { + plan->packplan=make_cfftp_plan(length); + if (!plan->packplan) { DEALLOC(plan); return NULL; } + return plan; + } + double comp1 = cost_guess(length); + double comp2 = 2*cost_guess(good_size(2*length-1)); + comp2*=1.5; /* fudge factor that appears to give good overall performance */ + if (comp2<comp1) // use Bluestein + { + plan->blueplan=make_fftblue_plan(length); + if (!plan->blueplan) { DEALLOC(plan); return NULL; } + } + else + { + plan->packplan=make_cfftp_plan(length); + if (!plan->packplan) { DEALLOC(plan); return NULL; } + } + return plan; + } + +static void destroy_cfft_plan (cfft_plan plan) + { + if (plan->blueplan) + destroy_fftblue_plan(plan->blueplan); + if (plan->packplan) + destroy_cfftp_plan(plan->packplan); + DEALLOC(plan); + } + +WARN_UNUSED_RESULT static int cfft_backward(cfft_plan plan, double c[], double fct) + { + if (plan->packplan) + return cfftp_backward(plan->packplan,c,fct); + // if (plan->blueplan) + return cfftblue_backward(plan->blueplan,c,fct); + } + +WARN_UNUSED_RESULT static int cfft_forward(cfft_plan plan, double c[], double fct) + { + if (plan->packplan) + return cfftp_forward(plan->packplan,c,fct); + // if (plan->blueplan) + return cfftblue_forward(plan->blueplan,c,fct); + } + +typedef struct rfft_plan_i + { + rfftp_plan packplan; + fftblue_plan blueplan; + } rfft_plan_i; + +static rfft_plan make_rfft_plan (size_t length) + { + if (length==0) return NULL; + rfft_plan plan = RALLOC(rfft_plan_i,1); + if (!plan) return NULL; + plan->blueplan=0; + plan->packplan=0; + if ((length<50) || (largest_prime_factor(length)<=sqrt(length))) + { + plan->packplan=make_rfftp_plan(length); + if (!plan->packplan) { DEALLOC(plan); return NULL; } + return plan; + } + double comp1 = 0.5*cost_guess(length); + double comp2 = 2*cost_guess(good_size(2*length-1)); + comp2*=1.5; /* fudge factor that appears to give good overall performance */ + if (comp2<comp1) // use Bluestein + { + plan->blueplan=make_fftblue_plan(length); + if (!plan->blueplan) { DEALLOC(plan); return NULL; } + } + else + { + plan->packplan=make_rfftp_plan(length); + if (!plan->packplan) { DEALLOC(plan); return NULL; } + } + return plan; + } + +static void destroy_rfft_plan (rfft_plan plan) + { + if (plan->blueplan) + destroy_fftblue_plan(plan->blueplan); + if (plan->packplan) + destroy_rfftp_plan(plan->packplan); + DEALLOC(plan); + } + +WARN_UNUSED_RESULT static int rfft_backward(rfft_plan plan, double c[], double fct) + { + if (plan->packplan) + return rfftp_backward(plan->packplan,c,fct); + else // if (plan->blueplan) + return rfftblue_backward(plan->blueplan,c,fct); + } + +WARN_UNUSED_RESULT static int rfft_forward(rfft_plan plan, double c[], double fct) + { + if (plan->packplan) + return rfftp_forward(plan->packplan,c,fct); + else // if (plan->blueplan) + return rfftblue_forward(plan->blueplan,c,fct); + } + +static PyObject * +execute_complex(PyObject *a1, int is_forward, double fct) +{ + PyArrayObject *data = (PyArrayObject *)PyArray_FromAny(a1, + PyArray_DescrFromType(NPY_CDOUBLE), 1, 0, + NPY_ARRAY_ENSURECOPY | NPY_ARRAY_DEFAULT | + NPY_ARRAY_ENSUREARRAY | NPY_ARRAY_FORCECAST, + NULL); + if (!data) return NULL; + + int npts = PyArray_DIM(data, PyArray_NDIM(data) - 1); + cfft_plan plan=NULL; + + int nrepeats = PyArray_SIZE(data)/npts; + double *dptr = (double *)PyArray_DATA(data); + int fail=0; + Py_BEGIN_ALLOW_THREADS; + plan = make_cfft_plan(npts); + if (!plan) fail=1; + if (!fail) + for (int i = 0; i < nrepeats; i++) { + int res = is_forward ? + cfft_forward(plan, dptr, fct) : cfft_backward(plan, dptr, fct); + if (res!=0) { fail=1; break; } + dptr += npts*2; + } + if (plan) destroy_cfft_plan(plan); + Py_END_ALLOW_THREADS; + if (fail) { + Py_XDECREF(data); + return PyErr_NoMemory(); + } + return (PyObject *)data; +} + +static PyObject * +execute_real_forward(PyObject *a1, double fct) +{ + rfft_plan plan=NULL; + int fail = 0; + PyArrayObject *data = (PyArrayObject *)PyArray_FromAny(a1, + PyArray_DescrFromType(NPY_DOUBLE), 1, 0, + NPY_ARRAY_DEFAULT | NPY_ARRAY_ENSUREARRAY | NPY_ARRAY_FORCECAST, + NULL); + if (!data) return NULL; + + int ndim = PyArray_NDIM(data); + const npy_intp *odim = PyArray_DIMS(data); + int npts = odim[ndim - 1]; + npy_intp *tdim=(npy_intp *)malloc(ndim*sizeof(npy_intp)); + if (!tdim) + { Py_XDECREF(data); return NULL; } + for (int d=0; d<ndim-1; ++d) + tdim[d] = odim[d]; + tdim[ndim-1] = npts/2 + 1; + PyArrayObject *ret = (PyArrayObject *)PyArray_Empty(ndim, + tdim, PyArray_DescrFromType(NPY_CDOUBLE), 0); + free(tdim); + if (!ret) fail=1; + if (!fail) { + int rstep = PyArray_DIM(ret, PyArray_NDIM(ret) - 1)*2; + + int nrepeats = PyArray_SIZE(data)/npts; + double *rptr = (double *)PyArray_DATA(ret), + *dptr = (double *)PyArray_DATA(data); + + Py_BEGIN_ALLOW_THREADS; + plan = make_rfft_plan(npts); + if (!plan) fail=1; + if (!fail) + for (int i = 0; i < nrepeats; i++) { + rptr[rstep-1] = 0.0; + memcpy((char *)(rptr+1), dptr, npts*sizeof(double)); + if (rfft_forward(plan, rptr+1, fct)!=0) {fail=1; break;} + rptr[0] = rptr[1]; + rptr[1] = 0.0; + rptr += rstep; + dptr += npts; + } + if (plan) destroy_rfft_plan(plan); + Py_END_ALLOW_THREADS; + } + if (fail) { + Py_XDECREF(data); + Py_XDECREF(ret); + return PyErr_NoMemory(); + } + Py_DECREF(data); + return (PyObject *)ret; +} +static PyObject * +execute_real_backward(PyObject *a1, double fct) +{ + rfft_plan plan=NULL; + PyArrayObject *data = (PyArrayObject *)PyArray_FromAny(a1, + PyArray_DescrFromType(NPY_CDOUBLE), 1, 0, + NPY_ARRAY_DEFAULT | NPY_ARRAY_ENSUREARRAY | NPY_ARRAY_FORCECAST, + NULL); + if (!data) return NULL; + int npts = PyArray_DIM(data, PyArray_NDIM(data) - 1); + PyArrayObject *ret = (PyArrayObject *)PyArray_Empty(PyArray_NDIM(data), + PyArray_DIMS(data), PyArray_DescrFromType(NPY_DOUBLE), 0); + int fail = 0; + if (!ret) fail=1; + if (!fail) { + int nrepeats = PyArray_SIZE(ret)/npts; + double *rptr = (double *)PyArray_DATA(ret), + *dptr = (double *)PyArray_DATA(data); + + Py_BEGIN_ALLOW_THREADS; + plan = make_rfft_plan(npts); + if (!plan) fail=1; + if (!fail) { + for (int i = 0; i < nrepeats; i++) { + memcpy((char *)(rptr + 1), (dptr + 2), (npts - 1)*sizeof(double)); + rptr[0] = dptr[0]; + if (rfft_backward(plan, rptr, fct)!=0) {fail=1; break;} + rptr += npts; + dptr += npts*2; + } + } + if (plan) destroy_rfft_plan(plan); + Py_END_ALLOW_THREADS; + } + if (fail) { + Py_XDECREF(data); + Py_XDECREF(ret); + return PyErr_NoMemory(); + } + Py_DECREF(data); + return (PyObject *)ret; +} + +static PyObject * +execute_real(PyObject *a1, int is_forward, double fct) +{ + return is_forward ? execute_real_forward(a1, fct) + : execute_real_backward(a1, fct); +} + +static const char execute__doc__[] = ""; + +static PyObject * +execute(PyObject *NPY_UNUSED(self), PyObject *args) +{ + PyObject *a1; + int is_real, is_forward; + double fct; + + if(!PyArg_ParseTuple(args, "Oiid:execute", &a1, &is_real, &is_forward, &fct)) { + return NULL; + } + + return is_real ? execute_real(a1, is_forward, fct) + : execute_complex(a1, is_forward, fct); +} + +/* List of methods defined in the module */ + +static struct PyMethodDef methods[] = { + {"execute", execute, 1, execute__doc__}, + {NULL, NULL, 0, NULL} /* sentinel */ +}; + +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "_pocketfft_internal", + NULL, + -1, + methods, + NULL, + NULL, + NULL, + NULL +}; + +/* Initialization function for the module */ +PyMODINIT_FUNC PyInit__pocketfft_internal(void) +{ + PyObject *m; + m = PyModule_Create(&moduledef); + if (m == NULL) { + return NULL; + } + + /* Import the array object */ + import_array(); + + /* XXXX Add constants here */ + + return m; +} diff --git a/numpy/fft/fftpack.py b/numpy/fft/_pocketfft.py similarity index 69% rename from numpy/fft/fftpack.py rename to numpy/fft/_pocketfft.py index e17e1cb34662..ad69f7c837bb 100644 --- a/numpy/fft/fftpack.py +++ b/numpy/fft/_pocketfft.py @@ -3,20 +3,20 @@ Routines in this module: -fft(a, n=None, axis=-1) -ifft(a, n=None, axis=-1) -rfft(a, n=None, axis=-1) -irfft(a, n=None, axis=-1) -hfft(a, n=None, axis=-1) -ihfft(a, n=None, axis=-1) -fftn(a, s=None, axes=None) -ifftn(a, s=None, axes=None) -rfftn(a, s=None, axes=None) -irfftn(a, s=None, axes=None) -fft2(a, s=None, axes=(-2,-1)) -ifft2(a, s=None, axes=(-2, -1)) -rfft2(a, s=None, axes=(-2,-1)) -irfft2(a, s=None, axes=(-2, -1)) +fft(a, n=None, axis=-1, norm="backward") +ifft(a, n=None, axis=-1, norm="backward") +rfft(a, n=None, axis=-1, norm="backward") +irfft(a, n=None, axis=-1, norm="backward") +hfft(a, n=None, axis=-1, norm="backward") +ihfft(a, n=None, axis=-1, norm="backward") +fftn(a, s=None, axes=None, norm="backward") +ifftn(a, s=None, axes=None, norm="backward") +rfftn(a, s=None, axes=None, norm="backward") +irfftn(a, s=None, axes=None, norm="backward") +fft2(a, s=None, axes=(-2,-1), norm="backward") +ifft2(a, s=None, axes=(-2, -1), norm="backward") +rfft2(a, s=None, axes=(-2,-1), norm="backward") +irfft2(a, s=None, axes=(-2, -1), norm="backward") i = inverse transform r = transform of purely real data @@ -26,79 +26,100 @@ (Note: 2D routines are just nD routines with different default behavior.) -The underlying code for these functions is an f2c-translated and modified -version of the FFTPACK routines. - """ -from __future__ import division, absolute_import, print_function - __all__ = ['fft', 'ifft', 'rfft', 'irfft', 'hfft', 'ihfft', 'rfftn', 'irfftn', 'rfft2', 'irfft2', 'fft2', 'ifft2', 'fftn', 'ifftn'] -from numpy.core import (array, asarray, zeros, swapaxes, shape, conjugate, - take, sqrt) -from . import fftpack_lite as fftpack -from .helper import _FFTCache +import functools -_fft_cache = _FFTCache(max_size_in_mb=100, max_item_count=32) -_real_fft_cache = _FFTCache(max_size_in_mb=100, max_item_count=32) +from numpy.core import asarray, zeros, swapaxes, conjugate, take, sqrt +from . import _pocketfft_internal as pfi +from numpy.core.multiarray import normalize_axis_index +from numpy.core import overrides -def _raw_fft(a, n=None, axis=-1, init_function=fftpack.cffti, - work_function=fftpack.cfftf, fft_cache=_fft_cache): - a = asarray(a) +array_function_dispatch = functools.partial( + overrides.array_function_dispatch, module='numpy.fft') + +# `inv_norm` is a float by which the result of the transform needs to be +# divided. This replaces the original, more intuitive 'fct` parameter to avoid +# divisions by zero (or alternatively additional checks) in the case of +# zero-length axes during its computation. +def _raw_fft(a, n, axis, is_real, is_forward, inv_norm): + axis = normalize_axis_index(axis, a.ndim) if n is None: n = a.shape[axis] - if n < 1: - raise ValueError("Invalid number of FFT data points (%d) specified." - % n) - - # We have to ensure that only a single thread can access a wsave array - # at any given time. Thus we remove it from the cache and insert it - # again after it has been used. Multiple threads might create multiple - # copies of the wsave array. This is intentional and a limitation of - # the current C code. - wsave = fft_cache.pop_twiddle_factors(n) - if wsave is None: - wsave = init_function(n) + fct = 1/inv_norm if a.shape[axis] != n: s = list(a.shape) + index = [slice(None)]*len(s) if s[axis] > n: - index = [slice(None)]*len(s) index[axis] = slice(0, n) a = a[tuple(index)] else: - index = [slice(None)]*len(s) index[axis] = slice(0, s[axis]) s[axis] = n z = zeros(s, a.dtype.char) z[tuple(index)] = a a = z - if axis != -1: + if axis == a.ndim-1: + r = pfi.execute(a, is_real, is_forward, fct) + else: a = swapaxes(a, axis, -1) - r = work_function(a, wsave) - if axis != -1: + r = pfi.execute(a, is_real, is_forward, fct) r = swapaxes(r, axis, -1) + return r - # As soon as we put wsave back into the cache, another thread could pick it - # up and start using it, so we must not do this until after we're - # completely done using it ourselves. - fft_cache.put_twiddle_factors(n, wsave) - return r +def _get_forward_norm(n, norm): + if n < 1: + raise ValueError(f"Invalid number of FFT data points ({n}) specified.") + if norm is None or norm == "backward": + return 1 + elif norm == "ortho": + return sqrt(n) + elif norm == "forward": + return n + raise ValueError(f'Invalid norm value {norm}; should be "backward",' + '"ortho" or "forward".') -def _unitary(norm): - if norm not in (None, "ortho"): - raise ValueError("Invalid norm value %s, should be None or \"ortho\"." - % norm) - return norm is not None +def _get_backward_norm(n, norm): + if n < 1: + raise ValueError(f"Invalid number of FFT data points ({n}) specified.") + + if norm is None or norm == "backward": + return n + elif norm == "ortho": + return sqrt(n) + elif norm == "forward": + return 1 + raise ValueError(f'Invalid norm value {norm}; should be "backward", ' + '"ortho" or "forward".') + +_SWAP_DIRECTION_MAP = {"backward": "forward", None: "forward", + "ortho": "ortho", "forward": "backward"} + + +def _swap_direction(norm): + try: + return _SWAP_DIRECTION_MAP[norm] + except KeyError: + raise ValueError(f'Invalid norm value {norm}; should be "backward", ' + '"ortho" or "forward".') from None + + +def _fft_dispatcher(a, n=None, axis=None, norm=None): + return (a,) + + +@array_function_dispatch(_fft_dispatcher) def fft(a, n=None, axis=-1, norm=None): """ Compute the one-dimensional discrete Fourier Transform. @@ -119,10 +140,16 @@ def fft(a, n=None, axis=-1, norm=None): axis : int, optional Axis over which to compute the FFT. If not given, the last axis is used. - norm : {None, "ortho"}, optional + norm : {"backward", "ortho", "forward"}, optional .. versionadded:: 1.10.0 - Normalization mode (see `numpy.fft`). Default is None. + Normalization mode (see `numpy.fft`). Default is "backward". + Indicates which direction of the forward/backward pair of transforms + is scaled and with what normalization factor. + + .. versionadded:: 1.20.0 + + The "backward", "forward" values were added. Returns ------- @@ -133,7 +160,7 @@ def fft(a, n=None, axis=-1, norm=None): Raises ------ IndexError - if `axes` is larger than the last axis of `a`. + If `axis` is not a valid axis of `a`. See Also -------- @@ -163,14 +190,10 @@ def fft(a, n=None, axis=-1, norm=None): Examples -------- >>> np.fft.fft(np.exp(2j * np.pi * np.arange(8) / 8)) - array([ -3.44505240e-16 +1.14383329e-17j, - 8.00000000e+00 -5.71092652e-15j, - 2.33482938e-16 +1.22460635e-16j, - 1.64863782e-15 +1.77635684e-15j, - 9.95839695e-17 +2.33482938e-16j, - 0.00000000e+00 +1.66837030e-15j, - 1.14383329e-17 +1.22460635e-16j, - -1.64863782e-15 +1.77635684e-15j]) + array([-2.33486982e-16+1.14423775e-17j, 8.00000000e+00-1.25557246e-15j, + 2.33486982e-16+2.33486982e-16j, 0.00000000e+00+1.22464680e-16j, + -1.14423775e-17+2.33486982e-16j, 0.00000000e+00+5.20784380e-16j, + 1.14423775e-17+1.14423775e-17j, 0.00000000e+00+1.22464680e-16j]) In this example, real input has an FFT which is Hermitian, i.e., symmetric in the real part and anti-symmetric in the imaginary part, as described in @@ -185,16 +208,15 @@ def fft(a, n=None, axis=-1, norm=None): >>> plt.show() """ - - a = asarray(a).astype(complex, copy=False) + a = asarray(a) if n is None: n = a.shape[axis] - output = _raw_fft(a, n, axis, fftpack.cffti, fftpack.cfftf, _fft_cache) - if _unitary(norm): - output *= 1 / sqrt(n) + inv_norm = _get_forward_norm(n, norm) + output = _raw_fft(a, n, axis, False, True, inv_norm) return output +@array_function_dispatch(_fft_dispatcher) def ifft(a, n=None, axis=-1, norm=None): """ Compute the one-dimensional inverse discrete Fourier Transform. @@ -230,10 +252,16 @@ def ifft(a, n=None, axis=-1, norm=None): axis : int, optional Axis over which to compute the inverse DFT. If not given, the last axis is used. - norm : {None, "ortho"}, optional + norm : {"backward", "ortho", "forward"}, optional .. versionadded:: 1.10.0 - Normalization mode (see `numpy.fft`). Default is None. + Normalization mode (see `numpy.fft`). Default is "backward". + Indicates which direction of the forward/backward pair of transforms + is scaled and with what normalization factor. + + .. versionadded:: 1.20.0 + + The "backward", "forward" values were added. Returns ------- @@ -244,7 +272,7 @@ def ifft(a, n=None, axis=-1, norm=None): Raises ------ IndexError - If `axes` is larger than the last axis of `a`. + If `axis` is not a valid axis of `a`. See Also -------- @@ -263,7 +291,7 @@ def ifft(a, n=None, axis=-1, norm=None): Examples -------- >>> np.fft.ifft([0, 4, 0, 0]) - array([ 1.+0.j, 0.+1.j, -1.+0.j, 0.-1.j]) + array([ 1.+0.j, 0.+1.j, -1.+0.j, 0.-1.j]) # may vary Create and plot a band-limited signal with random phases: @@ -272,22 +300,24 @@ def ifft(a, n=None, axis=-1, norm=None): >>> n = np.zeros((400,), dtype=complex) >>> n[40:60] = np.exp(1j*np.random.uniform(0, 2*np.pi, (20,))) >>> s = np.fft.ifft(n) - >>> plt.plot(t, s.real, 'b-', t, s.imag, 'r--') - ... - >>> plt.legend(('real', 'imaginary')) - ... + >>> plt.plot(t, s.real, label='real') + [<matplotlib.lines.Line2D object at ...>] + >>> plt.plot(t, s.imag, '--', label='imaginary') + [<matplotlib.lines.Line2D object at ...>] + >>> plt.legend() + <matplotlib.legend.Legend object at ...> >>> plt.show() """ - # The copy may be required for multithreading. - a = array(a, copy=True, dtype=complex) + a = asarray(a) if n is None: n = a.shape[axis] - unitary = _unitary(norm) - output = _raw_fft(a, n, axis, fftpack.cffti, fftpack.cfftb, _fft_cache) - return output * (1 / (sqrt(n) if unitary else n)) + inv_norm = _get_backward_norm(n, norm) + output = _raw_fft(a, n, axis, False, False, inv_norm) + return output +@array_function_dispatch(_fft_dispatcher) def rfft(a, n=None, axis=-1, norm=None): """ Compute the one-dimensional discrete Fourier Transform for real input. @@ -308,10 +338,16 @@ def rfft(a, n=None, axis=-1, norm=None): axis : int, optional Axis over which to compute the FFT. If not given, the last axis is used. - norm : {None, "ortho"}, optional + norm : {"backward", "ortho", "forward"}, optional .. versionadded:: 1.10.0 - Normalization mode (see `numpy.fft`). Default is None. + Normalization mode (see `numpy.fft`). Default is "backward". + Indicates which direction of the forward/backward pair of transforms + is scaled and with what normalization factor. + + .. versionadded:: 1.20.0 + + The "backward", "forward" values were added. Returns ------- @@ -324,7 +360,7 @@ def rfft(a, n=None, axis=-1, norm=None): Raises ------ IndexError - If `axis` is larger than the last axis of `a`. + If `axis` is not a valid axis of `a`. See Also -------- @@ -357,29 +393,27 @@ def rfft(a, n=None, axis=-1, norm=None): Examples -------- >>> np.fft.fft([0, 1, 0, 0]) - array([ 1.+0.j, 0.-1.j, -1.+0.j, 0.+1.j]) + array([ 1.+0.j, 0.-1.j, -1.+0.j, 0.+1.j]) # may vary >>> np.fft.rfft([0, 1, 0, 0]) - array([ 1.+0.j, 0.-1.j, -1.+0.j]) + array([ 1.+0.j, 0.-1.j, -1.+0.j]) # may vary Notice how the final element of the `fft` output is the complex conjugate of the second element, for real input. For `rfft`, this symmetry is exploited to compute only the non-negative frequency terms. """ - # The copy may be required for multithreading. - a = array(a, copy=True, dtype=float) - output = _raw_fft(a, n, axis, fftpack.rffti, fftpack.rfftf, - _real_fft_cache) - if _unitary(norm): - if n is None: - n = a.shape[axis] - output *= 1 / sqrt(n) + a = asarray(a) + if n is None: + n = a.shape[axis] + inv_norm = _get_forward_norm(n, norm) + output = _raw_fft(a, n, axis, True, True, inv_norm) return output +@array_function_dispatch(_fft_dispatcher) def irfft(a, n=None, axis=-1, norm=None): """ - Compute the inverse of the n-point DFT for real input. + Computes the inverse of `rfft`. This function computes the inverse of the one-dimensional *n*-point discrete Fourier Transform of real input computed by `rfft`. @@ -400,15 +434,22 @@ def irfft(a, n=None, axis=-1, norm=None): Length of the transformed axis of the output. For `n` output points, ``n//2+1`` input points are necessary. If the input is longer than this, it is cropped. If it is shorter than this, - it is padded with zeros. If `n` is not given, it is determined from - the length of the input along the axis specified by `axis`. + it is padded with zeros. If `n` is not given, it is taken to be + ``2*(m-1)`` where ``m`` is the length of the input along the axis + specified by `axis`. axis : int, optional Axis over which to compute the inverse FFT. If not given, the last axis is used. - norm : {None, "ortho"}, optional + norm : {"backward", "ortho", "forward"}, optional .. versionadded:: 1.10.0 - Normalization mode (see `numpy.fft`). Default is None. + Normalization mode (see `numpy.fft`). Default is "backward". + Indicates which direction of the forward/backward pair of transforms + is scaled and with what normalization factor. + + .. versionadded:: 1.20.0 + + The "backward", "forward" values were added. Returns ------- @@ -422,7 +463,7 @@ def irfft(a, n=None, axis=-1, norm=None): Raises ------ IndexError - If `axis` is larger than the last axis of `a`. + If `axis` is not a valid axis of `a`. See Also -------- @@ -444,12 +485,20 @@ def irfft(a, n=None, axis=-1, norm=None): thus resample a series to `m` points via Fourier interpolation by: ``a_resamp = irfft(rfft(a), m)``. + The correct interpretation of the hermitian input depends on the length of + the original data, as given by `n`. This is because each input shape could + correspond to either an odd or even length signal. By default, `irfft` + assumes an even output length which puts the last entry at the Nyquist + frequency; aliasing with its symmetric counterpart. By Hermitian symmetry, + the value is thus treated as purely real. To avoid losing information, the + correct length of the real input **must** be given. + Examples -------- >>> np.fft.ifft([1, -1j, -1, 1j]) - array([ 0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j]) + array([0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j]) # may vary >>> np.fft.irfft([1, -1j, -1]) - array([ 0., 1., 0., 0.]) + array([0., 1., 0., 0.]) Notice how the last term in the input to the ordinary `ifft` is the complex conjugate of the second term, and the output has zero imaginary @@ -457,16 +506,15 @@ def irfft(a, n=None, axis=-1, norm=None): specified, and the output array is purely real. """ - # The copy may be required for multithreading. - a = array(a, copy=True, dtype=complex) + a = asarray(a) if n is None: n = (a.shape[axis] - 1) * 2 - unitary = _unitary(norm) - output = _raw_fft(a, n, axis, fftpack.rffti, fftpack.rfftb, - _real_fft_cache) - return output * (1 / (sqrt(n) if unitary else n)) + inv_norm = _get_backward_norm(n, norm) + output = _raw_fft(a, n, axis, True, False, inv_norm) + return output +@array_function_dispatch(_fft_dispatcher) def hfft(a, n=None, axis=-1, norm=None): """ Compute the FFT of a signal that has Hermitian symmetry, i.e., a real @@ -480,16 +528,23 @@ def hfft(a, n=None, axis=-1, norm=None): Length of the transformed axis of the output. For `n` output points, ``n//2 + 1`` input points are necessary. If the input is longer than this, it is cropped. If it is shorter than this, it is - padded with zeros. If `n` is not given, it is determined from the - length of the input along the axis specified by `axis`. + padded with zeros. If `n` is not given, it is taken to be ``2*(m-1)`` + where ``m`` is the length of the input along the axis specified by + `axis`. axis : int, optional Axis over which to compute the FFT. If not given, the last axis is used. - norm : {None, "ortho"}, optional - Normalization mode (see `numpy.fft`). Default is None. - + norm : {"backward", "ortho", "forward"}, optional .. versionadded:: 1.10.0 + Normalization mode (see `numpy.fft`). Default is "backward". + Indicates which direction of the forward/backward pair of transforms + is scaled and with what normalization factor. + + .. versionadded:: 1.20.0 + + The "backward", "forward" values were added. + Returns ------- out : ndarray @@ -503,7 +558,7 @@ def hfft(a, n=None, axis=-1, norm=None): Raises ------ IndexError - If `axis` is larger than the last axis of `a`. + If `axis` is not a valid axis of `a`. See also -------- @@ -517,23 +572,31 @@ def hfft(a, n=None, axis=-1, norm=None): domain and is real in the frequency domain. So here it's `hfft` for which you must supply the length of the result if it is to be odd. - * even: ``ihfft(hfft(a, 2*len(a) - 2) == a``, within roundoff error, - * odd: ``ihfft(hfft(a, 2*len(a) - 1) == a``, within roundoff error. + * even: ``ihfft(hfft(a, 2*len(a) - 2)) == a``, within roundoff error, + * odd: ``ihfft(hfft(a, 2*len(a) - 1)) == a``, within roundoff error. + + The correct interpretation of the hermitian input depends on the length of + the original data, as given by `n`. This is because each input shape could + correspond to either an odd or even length signal. By default, `hfft` + assumes an even output length which puts the last entry at the Nyquist + frequency; aliasing with its symmetric counterpart. By Hermitian symmetry, + the value is thus treated as purely real. To avoid losing information, the + shape of the full signal **must** be given. Examples -------- >>> signal = np.array([1, 2, 3, 4, 3, 2]) >>> np.fft.fft(signal) - array([ 15.+0.j, -4.+0.j, 0.+0.j, -1.-0.j, 0.+0.j, -4.+0.j]) + array([15.+0.j, -4.+0.j, 0.+0.j, -1.-0.j, 0.+0.j, -4.+0.j]) # may vary >>> np.fft.hfft(signal[:4]) # Input first half of signal - array([ 15., -4., 0., -1., 0., -4.]) + array([15., -4., 0., -1., 0., -4.]) >>> np.fft.hfft(signal, 6) # Input entire signal and truncate - array([ 15., -4., 0., -1., 0., -4.]) + array([15., -4., 0., -1., 0., -4.]) >>> signal = np.array([[1, 1.j], [-1.j, 2]]) >>> np.conj(signal.T) - signal # check Hermitian symmetry - array([[ 0.-0.j, 0.+0.j], + array([[ 0.-0.j, -0.+0.j], # may vary [ 0.+0.j, 0.-0.j]]) >>> freq_spectrum = np.fft.hfft(signal) >>> freq_spectrum @@ -541,14 +604,15 @@ def hfft(a, n=None, axis=-1, norm=None): [ 2., -2.]]) """ - # The copy may be required for multithreading. - a = array(a, copy=True, dtype=complex) + a = asarray(a) if n is None: n = (a.shape[axis] - 1) * 2 - unitary = _unitary(norm) - return irfft(conjugate(a), n, axis) * (sqrt(n) if unitary else n) + new_norm = _swap_direction(norm) + output = irfft(conjugate(a), n, axis, norm=new_norm) + return output +@array_function_dispatch(_fft_dispatcher) def ihfft(a, n=None, axis=-1, norm=None): """ Compute the inverse FFT of a signal that has Hermitian symmetry. @@ -566,11 +630,17 @@ def ihfft(a, n=None, axis=-1, norm=None): axis : int, optional Axis over which to compute the inverse FFT. If not given, the last axis is used. - norm : {None, "ortho"}, optional - Normalization mode (see `numpy.fft`). Default is None. - + norm : {"backward", "ortho", "forward"}, optional .. versionadded:: 1.10.0 + Normalization mode (see `numpy.fft`). Default is "backward". + Indicates which direction of the forward/backward pair of transforms + is scaled and with what normalization factor. + + .. versionadded:: 1.20.0 + + The "backward", "forward" values were added. + Returns ------- out : complex ndarray @@ -589,25 +659,24 @@ def ihfft(a, n=None, axis=-1, norm=None): domain and is real in the frequency domain. So here it's `hfft` for which you must supply the length of the result if it is to be odd: - * even: ``ihfft(hfft(a, 2*len(a) - 2) == a``, within roundoff error, - * odd: ``ihfft(hfft(a, 2*len(a) - 1) == a``, within roundoff error. + * even: ``ihfft(hfft(a, 2*len(a) - 2)) == a``, within roundoff error, + * odd: ``ihfft(hfft(a, 2*len(a) - 1)) == a``, within roundoff error. Examples -------- >>> spectrum = np.array([ 15, -4, 0, -1, 0, -4]) >>> np.fft.ifft(spectrum) - array([ 1.+0.j, 2.-0.j, 3.+0.j, 4.+0.j, 3.+0.j, 2.-0.j]) + array([1.+0.j, 2.+0.j, 3.+0.j, 4.+0.j, 3.+0.j, 2.+0.j]) # may vary >>> np.fft.ihfft(spectrum) - array([ 1.-0.j, 2.-0.j, 3.-0.j, 4.-0.j]) + array([ 1.-0.j, 2.-0.j, 3.-0.j, 4.-0.j]) # may vary """ - # The copy may be required for multithreading. - a = array(a, copy=True, dtype=float) + a = asarray(a) if n is None: n = a.shape[axis] - unitary = _unitary(norm) - output = conjugate(rfft(a, n, axis)) - return output * (1 / (sqrt(n) if unitary else n)) + new_norm = _swap_direction(norm) + output = conjugate(rfft(a, n, axis, norm=new_norm)) + return output def _cook_nd_args(a, s=None, axes=None, invreal=0): @@ -639,6 +708,11 @@ def _raw_fftnd(a, s=None, axes=None, function=fft, norm=None): return a +def _fftn_dispatcher(a, s=None, axes=None, norm=None): + return (a,) + + +@array_function_dispatch(_fftn_dispatcher) def fftn(a, s=None, axes=None, norm=None): """ Compute the N-dimensional discrete Fourier Transform. @@ -664,10 +738,16 @@ def fftn(a, s=None, axes=None, norm=None): axes are used, or all axes if `s` is also not specified. Repeated indices in `axes` means that the transform over that axis is performed multiple times. - norm : {None, "ortho"}, optional + norm : {"backward", "ortho", "forward"}, optional .. versionadded:: 1.10.0 - Normalization mode (see `numpy.fft`). Default is None. + Normalization mode (see `numpy.fft`). Default is "backward". + Indicates which direction of the forward/backward pair of transforms + is scaled and with what normalization factor. + + .. versionadded:: 1.20.0 + + The "backward", "forward" values were added. Returns ------- @@ -707,17 +787,17 @@ def fftn(a, s=None, axes=None, norm=None): -------- >>> a = np.mgrid[:3, :3, :3][0] >>> np.fft.fftn(a, axes=(1, 2)) - array([[[ 0.+0.j, 0.+0.j, 0.+0.j], - [ 0.+0.j, 0.+0.j, 0.+0.j], - [ 0.+0.j, 0.+0.j, 0.+0.j]], - [[ 9.+0.j, 0.+0.j, 0.+0.j], - [ 0.+0.j, 0.+0.j, 0.+0.j], - [ 0.+0.j, 0.+0.j, 0.+0.j]], - [[ 18.+0.j, 0.+0.j, 0.+0.j], - [ 0.+0.j, 0.+0.j, 0.+0.j], - [ 0.+0.j, 0.+0.j, 0.+0.j]]]) + array([[[ 0.+0.j, 0.+0.j, 0.+0.j], # may vary + [ 0.+0.j, 0.+0.j, 0.+0.j], + [ 0.+0.j, 0.+0.j, 0.+0.j]], + [[ 9.+0.j, 0.+0.j, 0.+0.j], + [ 0.+0.j, 0.+0.j, 0.+0.j], + [ 0.+0.j, 0.+0.j, 0.+0.j]], + [[18.+0.j, 0.+0.j, 0.+0.j], + [ 0.+0.j, 0.+0.j, 0.+0.j], + [ 0.+0.j, 0.+0.j, 0.+0.j]]]) >>> np.fft.fftn(a, (2, 2), axes=(0, 1)) - array([[[ 2.+0.j, 2.+0.j, 2.+0.j], + array([[[ 2.+0.j, 2.+0.j, 2.+0.j], # may vary [ 0.+0.j, 0.+0.j, 0.+0.j]], [[-2.+0.j, -2.+0.j, -2.+0.j], [ 0.+0.j, 0.+0.j, 0.+0.j]]]) @@ -732,10 +812,10 @@ def fftn(a, s=None, axes=None, norm=None): >>> plt.show() """ - return _raw_fftnd(a, s, axes, fft, norm) +@array_function_dispatch(_fftn_dispatcher) def ifftn(a, s=None, axes=None, norm=None): """ Compute the N-dimensional inverse discrete Fourier Transform. @@ -770,10 +850,16 @@ def ifftn(a, s=None, axes=None, norm=None): axes are used, or all axes if `s` is also not specified. Repeated indices in `axes` means that the inverse transform over that axis is performed multiple times. - norm : {None, "ortho"}, optional + norm : {"backward", "ortho", "forward"}, optional .. versionadded:: 1.10.0 - Normalization mode (see `numpy.fft`). Default is None. + Normalization mode (see `numpy.fft`). Default is "backward". + Indicates which direction of the forward/backward pair of transforms + is scaled and with what normalization factor. + + .. versionadded:: 1.20.0 + + The "backward", "forward" values were added. Returns ------- @@ -812,10 +898,10 @@ def ifftn(a, s=None, axes=None, norm=None): -------- >>> a = np.eye(4) >>> np.fft.ifftn(np.fft.fftn(a, axes=(0,)), axes=(1,)) - array([[ 1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j], - [ 0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j], - [ 0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j], - [ 0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j]]) + array([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j], # may vary + [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j], + [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j], + [0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j]]) Create and plot an image with band-limited frequency content: @@ -829,13 +915,13 @@ def ifftn(a, s=None, axes=None, norm=None): >>> plt.show() """ - return _raw_fftnd(a, s, axes, ifft, norm) +@array_function_dispatch(_fftn_dispatcher) def fft2(a, s=None, axes=(-2, -1), norm=None): """ - Compute the 2-dimensional discrete Fourier Transform + Compute the 2-dimensional discrete Fourier Transform. This function computes the *n*-dimensional discrete Fourier Transform over any axes in an *M*-dimensional array by means of the @@ -859,10 +945,16 @@ def fft2(a, s=None, axes=(-2, -1), norm=None): axes are used. A repeated index in `axes` means the transform over that axis is performed multiple times. A one-element sequence means that a one-dimensional FFT is performed. - norm : {None, "ortho"}, optional + norm : {"backward", "ortho", "forward"}, optional .. versionadded:: 1.10.0 - Normalization mode (see `numpy.fft`). Default is None. + Normalization mode (see `numpy.fft`). Default is "backward". + Indicates which direction of the forward/backward pair of transforms + is scaled and with what normalization factor. + + .. versionadded:: 1.20.0 + + The "backward", "forward" values were added. Returns ------- @@ -907,22 +999,22 @@ def fft2(a, s=None, axes=(-2, -1), norm=None): -------- >>> a = np.mgrid[:5, :5][0] >>> np.fft.fft2(a) - array([[ 50.0 +0.j , 0.0 +0.j , 0.0 +0.j , - 0.0 +0.j , 0.0 +0.j ], - [-12.5+17.20477401j, 0.0 +0.j , 0.0 +0.j , - 0.0 +0.j , 0.0 +0.j ], - [-12.5 +4.0614962j , 0.0 +0.j , 0.0 +0.j , - 0.0 +0.j , 0.0 +0.j ], - [-12.5 -4.0614962j , 0.0 +0.j , 0.0 +0.j , - 0.0 +0.j , 0.0 +0.j ], - [-12.5-17.20477401j, 0.0 +0.j , 0.0 +0.j , - 0.0 +0.j , 0.0 +0.j ]]) + array([[ 50. +0.j , 0. +0.j , 0. +0.j , # may vary + 0. +0.j , 0. +0.j ], + [-12.5+17.20477401j, 0. +0.j , 0. +0.j , + 0. +0.j , 0. +0.j ], + [-12.5 +4.0614962j , 0. +0.j , 0. +0.j , + 0. +0.j , 0. +0.j ], + [-12.5 -4.0614962j , 0. +0.j , 0. +0.j , + 0. +0.j , 0. +0.j ], + [-12.5-17.20477401j, 0. +0.j , 0. +0.j , + 0. +0.j , 0. +0.j ]]) """ - return _raw_fftnd(a, s, axes, fft, norm) +@array_function_dispatch(_fftn_dispatcher) def ifft2(a, s=None, axes=(-2, -1), norm=None): """ Compute the 2-dimensional inverse discrete Fourier Transform. @@ -956,10 +1048,16 @@ def ifft2(a, s=None, axes=(-2, -1), norm=None): axes are used. A repeated index in `axes` means the transform over that axis is performed multiple times. A one-element sequence means that a one-dimensional FFT is performed. - norm : {None, "ortho"}, optional + norm : {"backward", "ortho", "forward"}, optional .. versionadded:: 1.10.0 - Normalization mode (see `numpy.fft`). Default is None. + Normalization mode (see `numpy.fft`). Default is "backward". + Indicates which direction of the forward/backward pair of transforms + is scaled and with what normalization factor. + + .. versionadded:: 1.20.0 + + The "backward", "forward" values were added. Returns ------- @@ -1000,16 +1098,16 @@ def ifft2(a, s=None, axes=(-2, -1), norm=None): -------- >>> a = 4 * np.eye(4) >>> np.fft.ifft2(a) - array([[ 1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j], - [ 0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j], - [ 0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j], - [ 0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j]]) + array([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j], # may vary + [0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j], + [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j], + [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j]]) """ - return _raw_fftnd(a, s, axes, ifft, norm) +@array_function_dispatch(_fftn_dispatcher) def rfftn(a, s=None, axes=None, norm=None): """ Compute the N-dimensional discrete Fourier Transform for real input. @@ -1036,10 +1134,16 @@ def rfftn(a, s=None, axes=None, norm=None): axes : sequence of ints, optional Axes over which to compute the FFT. If not given, the last ``len(s)`` axes are used, or all axes if `s` is also not specified. - norm : {None, "ortho"}, optional + norm : {"backward", "ortho", "forward"}, optional .. versionadded:: 1.10.0 - Normalization mode (see `numpy.fft`). Default is None. + Normalization mode (see `numpy.fft`). Default is "backward". + Indicates which direction of the forward/backward pair of transforms + is scaled and with what normalization factor. + + .. versionadded:: 1.20.0 + + The "backward", "forward" values were added. Returns ------- @@ -1081,20 +1185,19 @@ def rfftn(a, s=None, axes=None, norm=None): -------- >>> a = np.ones((2, 2, 2)) >>> np.fft.rfftn(a) - array([[[ 8.+0.j, 0.+0.j], - [ 0.+0.j, 0.+0.j]], - [[ 0.+0.j, 0.+0.j], - [ 0.+0.j, 0.+0.j]]]) + array([[[8.+0.j, 0.+0.j], # may vary + [0.+0.j, 0.+0.j]], + [[0.+0.j, 0.+0.j], + [0.+0.j, 0.+0.j]]]) >>> np.fft.rfftn(a, axes=(2, 0)) - array([[[ 4.+0.j, 0.+0.j], - [ 4.+0.j, 0.+0.j]], - [[ 0.+0.j, 0.+0.j], - [ 0.+0.j, 0.+0.j]]]) + array([[[4.+0.j, 0.+0.j], # may vary + [4.+0.j, 0.+0.j]], + [[0.+0.j, 0.+0.j], + [0.+0.j, 0.+0.j]]]) """ - # The copy may be required for multithreading. - a = array(a, copy=True, dtype=float) + a = asarray(a) s, axes = _cook_nd_args(a, s, axes) a = rfft(a, s[-1], axes[-1], norm) for ii in range(len(axes)-1): @@ -1102,6 +1205,7 @@ def rfftn(a, s=None, axes=None, norm=None): return a +@array_function_dispatch(_fftn_dispatcher) def rfft2(a, s=None, axes=(-2, -1), norm=None): """ Compute the 2-dimensional FFT of a real array. @@ -1114,10 +1218,16 @@ def rfft2(a, s=None, axes=(-2, -1), norm=None): Shape of the FFT. axes : sequence of ints, optional Axes over which to compute the FFT. - norm : {None, "ortho"}, optional + norm : {"backward", "ortho", "forward"}, optional .. versionadded:: 1.10.0 - Normalization mode (see `numpy.fft`). Default is None. + Normalization mode (see `numpy.fft`). Default is "backward". + Indicates which direction of the forward/backward pair of transforms + is scaled and with what normalization factor. + + .. versionadded:: 1.20.0 + + The "backward", "forward" values were added. Returns ------- @@ -1134,14 +1244,23 @@ def rfft2(a, s=None, axes=(-2, -1), norm=None): This is really just `rfftn` with different default behavior. For more details see `rfftn`. + Examples + -------- + >>> a = np.mgrid[:5, :5][0] + >>> np.fft.rfft2(a) + array([[ 50. +0.j , 0. +0.j , 0. +0.j ], + [-12.5+17.20477401j, 0. +0.j , 0. +0.j ], + [-12.5 +4.0614962j , 0. +0.j , 0. +0.j ], + [-12.5 -4.0614962j , 0. +0.j , 0. +0.j ], + [-12.5-17.20477401j, 0. +0.j , 0. +0.j ]]) """ - return rfftn(a, s, axes, norm) +@array_function_dispatch(_fftn_dispatcher) def irfftn(a, s=None, axes=None, norm=None): """ - Compute the inverse of the N-dimensional FFT of real input. + Computes the inverse of `rfftn`. This function computes the inverse of the N-dimensional discrete Fourier Transform for real input over any number of axes in an @@ -1165,17 +1284,24 @@ def irfftn(a, s=None, axes=None, norm=None): where ``s[-1]//2+1`` points of the input are used. Along any axis, if the shape indicated by `s` is smaller than that of the input, the input is cropped. If it is larger, the input is padded - with zeros. If `s` is not given, the shape of the input along the - axes specified by `axes` is used. + with zeros. If `s` is not given, the shape of the input along the axes + specified by axes is used. Except for the last axis which is taken to + be ``2*(m-1)`` where ``m`` is the length of the input along that axis. axes : sequence of ints, optional Axes over which to compute the inverse FFT. If not given, the last `len(s)` axes are used, or all axes if `s` is also not specified. Repeated indices in `axes` means that the inverse transform over that axis is performed multiple times. - norm : {None, "ortho"}, optional + norm : {"backward", "ortho", "forward"}, optional .. versionadded:: 1.10.0 - Normalization mode (see `numpy.fft`). Default is None. + Normalization mode (see `numpy.fft`). Default is "backward". + Indicates which direction of the forward/backward pair of transforms + is scaled and with what normalization factor. + + .. versionadded:: 1.20.0 + + The "backward", "forward" values were added. Returns ------- @@ -1211,21 +1337,29 @@ def irfftn(a, s=None, axes=None, norm=None): See `rfft` for definitions and conventions used for real input. + The correct interpretation of the hermitian input depends on the shape of + the original data, as given by `s`. This is because each input shape could + correspond to either an odd or even length signal. By default, `irfftn` + assumes an even output length which puts the last entry at the Nyquist + frequency; aliasing with its symmetric counterpart. When performing the + final complex to real transform, the last value is thus treated as purely + real. To avoid losing information, the correct shape of the real input + **must** be given. + Examples -------- >>> a = np.zeros((3, 2, 2)) >>> a[0, 0, 0] = 3 * 2 * 2 >>> np.fft.irfftn(a) - array([[[ 1., 1.], - [ 1., 1.]], - [[ 1., 1.], - [ 1., 1.]], - [[ 1., 1.], - [ 1., 1.]]]) + array([[[1., 1.], + [1., 1.]], + [[1., 1.], + [1., 1.]], + [[1., 1.], + [1., 1.]]]) """ - # The copy may be required for multithreading. - a = array(a, copy=True, dtype=complex) + a = asarray(a) s, axes = _cook_nd_args(a, s, axes, invreal=1) for ii in range(len(axes)-1): a = ifft(a, s[ii], axes[ii], norm) @@ -1233,23 +1367,30 @@ def irfftn(a, s=None, axes=None, norm=None): return a +@array_function_dispatch(_fftn_dispatcher) def irfft2(a, s=None, axes=(-2, -1), norm=None): """ - Compute the 2-dimensional inverse FFT of a real array. + Computes the inverse of `rfft2`. Parameters ---------- a : array_like The input array s : sequence of ints, optional - Shape of the inverse FFT. + Shape of the real output to the inverse FFT. axes : sequence of ints, optional The axes over which to compute the inverse fft. Default is the last two axes. - norm : {None, "ortho"}, optional + norm : {"backward", "ortho", "forward"}, optional .. versionadded:: 1.10.0 - Normalization mode (see `numpy.fft`). Default is None. + Normalization mode (see `numpy.fft`). Default is "backward". + Indicates which direction of the forward/backward pair of transforms + is scaled and with what normalization factor. + + .. versionadded:: 1.20.0 + + The "backward", "forward" values were added. Returns ------- @@ -1258,6 +1399,10 @@ def irfft2(a, s=None, axes=(-2, -1), norm=None): See Also -------- + rfft2 : The forward two-dimensional FFT of real input, + of which `irfft2` is the inverse. + rfft : The one-dimensional FFT for real input. + irfft : The inverse of the one-dimensional FFT of real input. irfftn : Compute the inverse of the N-dimensional FFT of real input. Notes @@ -1265,6 +1410,15 @@ def irfft2(a, s=None, axes=(-2, -1), norm=None): This is really `irfftn` with different defaults. For more details see `irfftn`. + Examples + -------- + >>> a = np.mgrid[:5, :5][0] + >>> A = np.fft.rfft2(a) + >>> np.fft.irfft2(A, s=a.shape) + array([[0., 0., 0., 0., 0.], + [1., 1., 1., 1., 1.], + [2., 2., 2., 2., 2.], + [3., 3., 3., 3., 3.], + [4., 4., 4., 4., 4.]]) """ - return irfftn(a, s, axes, norm) diff --git a/numpy/fft/_pocketfft.pyi b/numpy/fft/_pocketfft.pyi new file mode 100644 index 000000000000..86cf6a60d84e --- /dev/null +++ b/numpy/fft/_pocketfft.pyi @@ -0,0 +1,111 @@ +from typing import ( + Literal as L, + List, + Sequence, +) + +from numpy import complex128, float64 +from numpy.typing import ArrayLike, NDArray, _ArrayLikeNumber_co + +_NormKind = L[None, "backward", "ortho", "forward"] + +__all__: List[str] + +def fft( + a: ArrayLike, + n: None | int = ..., + axis: int = ..., + norm: _NormKind = ..., +) -> NDArray[complex128]: ... + +def ifft( + a: ArrayLike, + n: None | int = ..., + axis: int = ..., + norm: _NormKind = ..., +) -> NDArray[complex128]: ... + +def rfft( + a: ArrayLike, + n: None | int = ..., + axis: int = ..., + norm: _NormKind = ..., +) -> NDArray[complex128]: ... + +def irfft( + a: ArrayLike, + n: None | int = ..., + axis: int = ..., + norm: _NormKind = ..., +) -> NDArray[float64]: ... + +# Input array must be compatible with `np.conjugate` +def hfft( + a: _ArrayLikeNumber_co, + n: None | int = ..., + axis: int = ..., + norm: _NormKind = ..., +) -> NDArray[float64]: ... + +def ihfft( + a: ArrayLike, + n: None | int = ..., + axis: int = ..., + norm: _NormKind = ..., +) -> NDArray[complex128]: ... + +def fftn( + a: ArrayLike, + s: None | Sequence[int] = ..., + axes: None | Sequence[int] = ..., + norm: _NormKind = ..., +) -> NDArray[complex128]: ... + +def ifftn( + a: ArrayLike, + s: None | Sequence[int] = ..., + axes: None | Sequence[int] = ..., + norm: _NormKind = ..., +) -> NDArray[complex128]: ... + +def rfftn( + a: ArrayLike, + s: None | Sequence[int] = ..., + axes: None | Sequence[int] = ..., + norm: _NormKind = ..., +) -> NDArray[complex128]: ... + +def irfftn( + a: ArrayLike, + s: None | Sequence[int] = ..., + axes: None | Sequence[int] = ..., + norm: _NormKind = ..., +) -> NDArray[float64]: ... + +def fft2( + a: ArrayLike, + s: None | Sequence[int] = ..., + axes: None | Sequence[int] = ..., + norm: _NormKind = ..., +) -> NDArray[complex128]: ... + +def ifft2( + a: ArrayLike, + s: None | Sequence[int] = ..., + axes: None | Sequence[int] = ..., + norm: _NormKind = ..., +) -> NDArray[complex128]: ... + +def rfft2( + a: ArrayLike, + s: None | Sequence[int] = ..., + axes: None | Sequence[int] = ..., + norm: _NormKind = ..., +) -> NDArray[complex128]: ... + +def irfft2( + a: ArrayLike, + s: None | Sequence[int] = ..., + axes: None | Sequence[int] = ..., + norm: _NormKind = ..., +) -> NDArray[float64]: ... diff --git a/numpy/fft/fftpack.c b/numpy/fft/fftpack.c deleted file mode 100644 index 07fa2bf4ce0c..000000000000 --- a/numpy/fft/fftpack.c +++ /dev/null @@ -1,1536 +0,0 @@ -/* - * fftpack.c : A set of FFT routines in C. - * Algorithmically based on Fortran-77 FFTPACK by Paul N. Swarztrauber (Version 4, 1985). -*/ -#define NPY_NO_DEPRECATED_API NPY_API_VERSION - -#include <Python.h> -#include <math.h> -#include <stdio.h> -#include <numpy/ndarraytypes.h> - -#define DOUBLE -#ifdef DOUBLE -#define Treal double -#else -#define Treal float -#endif - -#define ref(u,a) u[a] - -/* Macros for accurate calculation of the twiddle factors. */ -#define TWOPI 6.283185307179586476925286766559005768391 -#define cos2pi(m, n) cos((TWOPI * (m)) / (n)) -#define sin2pi(m, n) sin((TWOPI * (m)) / (n)) - -#define MAXFAC 13 /* maximum number of factors in factorization of n */ -#define NSPECIAL 4 /* number of factors for which we have special-case routines */ - -#ifdef __cplusplus -extern "C" { -#endif - -static void sincos2pi(int m, int n, Treal* si, Treal* co) -/* Calculates sin(2pi * m/n) and cos(2pi * m/n). It is more accurate - * than the naive calculation as the fraction m/n is reduced to [0, 1/8) first. - * Due to the symmetry of sin(x) and cos(x) the values for all x can be - * determined from the function values of the reduced argument in the first - * octant. - */ - { - int n8, m8, octant; - n8 = 8 * n; - m8 = (8 * m) % n8; - octant = m8 / n; - m8 = m8 % n; - switch(octant) { - case 0: - *co = cos2pi(m8, n8); - *si = sin2pi(m8, n8); - break; - case 1: - *co = sin2pi(n-m8, n8); - *si = cos2pi(n-m8, n8); - break; - case 2: - *co = -sin2pi(m8, n8); - *si = cos2pi(m8, n8); - break; - case 3: - *co = -cos2pi(n-m8, n8); - *si = sin2pi(n-m8, n8); - break; - case 4: - *co = -cos2pi(m8, n8); - *si = -sin2pi(m8, n8); - break; - case 5: - *co = -sin2pi(n-m8, n8); - *si = -cos2pi(n-m8, n8); - break; - case 6: - *co = sin2pi(m8, n8); - *si = -cos2pi(m8, n8); - break; - case 7: - *co = cos2pi(n-m8, n8); - *si = -sin2pi(n-m8, n8); - break; - } - } - -/* ---------------------------------------------------------------------- - passf2, passf3, passf4, passf5, passf. Complex FFT passes fwd and bwd. ------------------------------------------------------------------------ */ - -static void passf2(int ido, int l1, const Treal cc[], Treal ch[], const Treal wa1[], int isign) - /* isign==+1 for backward transform */ - { - int i, k, ah, ac; - Treal ti2, tr2; - if (ido <= 2) { - for (k=0; k<l1; k++) { - ah = k*ido; - ac = 2*k*ido; - ch[ah] = ref(cc,ac) + ref(cc,ac + ido); - ch[ah + ido*l1] = ref(cc,ac) - ref(cc,ac + ido); - ch[ah+1] = ref(cc,ac+1) + ref(cc,ac + ido + 1); - ch[ah + ido*l1 + 1] = ref(cc,ac+1) - ref(cc,ac + ido + 1); - } - } else { - for (k=0; k<l1; k++) { - for (i=0; i<ido-1; i+=2) { - ah = i + k*ido; - ac = i + 2*k*ido; - ch[ah] = ref(cc,ac) + ref(cc,ac + ido); - tr2 = ref(cc,ac) - ref(cc,ac + ido); - ch[ah+1] = ref(cc,ac+1) + ref(cc,ac + 1 + ido); - ti2 = ref(cc,ac+1) - ref(cc,ac + 1 + ido); - ch[ah+l1*ido+1] = wa1[i]*ti2 + isign*wa1[i+1]*tr2; - ch[ah+l1*ido] = wa1[i]*tr2 - isign*wa1[i+1]*ti2; - } - } - } - } /* passf2 */ - - -static void passf3(int ido, int l1, const Treal cc[], Treal ch[], - const Treal wa1[], const Treal wa2[], int isign) - /* isign==+1 for backward transform */ - { - static const Treal taur = -0.5; - static const Treal taui = 0.86602540378443864676; - int i, k, ac, ah; - Treal ci2, ci3, di2, di3, cr2, cr3, dr2, dr3, ti2, tr2; - if (ido == 2) { - for (k=1; k<=l1; k++) { - ac = (3*k - 2)*ido; - tr2 = ref(cc,ac) + ref(cc,ac + ido); - cr2 = ref(cc,ac - ido) + taur*tr2; - ah = (k - 1)*ido; - ch[ah] = ref(cc,ac - ido) + tr2; - - ti2 = ref(cc,ac + 1) + ref(cc,ac + ido + 1); - ci2 = ref(cc,ac - ido + 1) + taur*ti2; - ch[ah + 1] = ref(cc,ac - ido + 1) + ti2; - - cr3 = isign*taui*(ref(cc,ac) - ref(cc,ac + ido)); - ci3 = isign*taui*(ref(cc,ac + 1) - ref(cc,ac + ido + 1)); - ch[ah + l1*ido] = cr2 - ci3; - ch[ah + 2*l1*ido] = cr2 + ci3; - ch[ah + l1*ido + 1] = ci2 + cr3; - ch[ah + 2*l1*ido + 1] = ci2 - cr3; - } - } else { - for (k=1; k<=l1; k++) { - for (i=0; i<ido-1; i+=2) { - ac = i + (3*k - 2)*ido; - tr2 = ref(cc,ac) + ref(cc,ac + ido); - cr2 = ref(cc,ac - ido) + taur*tr2; - ah = i + (k-1)*ido; - ch[ah] = ref(cc,ac - ido) + tr2; - ti2 = ref(cc,ac + 1) + ref(cc,ac + ido + 1); - ci2 = ref(cc,ac - ido + 1) + taur*ti2; - ch[ah + 1] = ref(cc,ac - ido + 1) + ti2; - cr3 = isign*taui*(ref(cc,ac) - ref(cc,ac + ido)); - ci3 = isign*taui*(ref(cc,ac + 1) - ref(cc,ac + ido + 1)); - dr2 = cr2 - ci3; - dr3 = cr2 + ci3; - di2 = ci2 + cr3; - di3 = ci2 - cr3; - ch[ah + l1*ido + 1] = wa1[i]*di2 + isign*wa1[i+1]*dr2; - ch[ah + l1*ido] = wa1[i]*dr2 - isign*wa1[i+1]*di2; - ch[ah + 2*l1*ido + 1] = wa2[i]*di3 + isign*wa2[i+1]*dr3; - ch[ah + 2*l1*ido] = wa2[i]*dr3 - isign*wa2[i+1]*di3; - } - } - } - } /* passf3 */ - - -static void passf4(int ido, int l1, const Treal cc[], Treal ch[], - const Treal wa1[], const Treal wa2[], const Treal wa3[], int isign) - /* isign == -1 for forward transform and +1 for backward transform */ - { - int i, k, ac, ah; - Treal ci2, ci3, ci4, cr2, cr3, cr4, ti1, ti2, ti3, ti4, tr1, tr2, tr3, tr4; - if (ido == 2) { - for (k=0; k<l1; k++) { - ac = 4*k*ido + 1; - ti1 = ref(cc,ac) - ref(cc,ac + 2*ido); - ti2 = ref(cc,ac) + ref(cc,ac + 2*ido); - tr4 = ref(cc,ac + 3*ido) - ref(cc,ac + ido); - ti3 = ref(cc,ac + ido) + ref(cc,ac + 3*ido); - tr1 = ref(cc,ac - 1) - ref(cc,ac + 2*ido - 1); - tr2 = ref(cc,ac - 1) + ref(cc,ac + 2*ido - 1); - ti4 = ref(cc,ac + ido - 1) - ref(cc,ac + 3*ido - 1); - tr3 = ref(cc,ac + ido - 1) + ref(cc,ac + 3*ido - 1); - ah = k*ido; - ch[ah] = tr2 + tr3; - ch[ah + 2*l1*ido] = tr2 - tr3; - ch[ah + 1] = ti2 + ti3; - ch[ah + 2*l1*ido + 1] = ti2 - ti3; - ch[ah + l1*ido] = tr1 + isign*tr4; - ch[ah + 3*l1*ido] = tr1 - isign*tr4; - ch[ah + l1*ido + 1] = ti1 + isign*ti4; - ch[ah + 3*l1*ido + 1] = ti1 - isign*ti4; - } - } else { - for (k=0; k<l1; k++) { - for (i=0; i<ido-1; i+=2) { - ac = i + 1 + 4*k*ido; - ti1 = ref(cc,ac) - ref(cc,ac + 2*ido); - ti2 = ref(cc,ac) + ref(cc,ac + 2*ido); - ti3 = ref(cc,ac + ido) + ref(cc,ac + 3*ido); - tr4 = ref(cc,ac + 3*ido) - ref(cc,ac + ido); - tr1 = ref(cc,ac - 1) - ref(cc,ac + 2*ido - 1); - tr2 = ref(cc,ac - 1) + ref(cc,ac + 2*ido - 1); - ti4 = ref(cc,ac + ido - 1) - ref(cc,ac + 3*ido - 1); - tr3 = ref(cc,ac + ido - 1) + ref(cc,ac + 3*ido - 1); - ah = i + k*ido; - ch[ah] = tr2 + tr3; - cr3 = tr2 - tr3; - ch[ah + 1] = ti2 + ti3; - ci3 = ti2 - ti3; - cr2 = tr1 + isign*tr4; - cr4 = tr1 - isign*tr4; - ci2 = ti1 + isign*ti4; - ci4 = ti1 - isign*ti4; - ch[ah + l1*ido] = wa1[i]*cr2 - isign*wa1[i + 1]*ci2; - ch[ah + l1*ido + 1] = wa1[i]*ci2 + isign*wa1[i + 1]*cr2; - ch[ah + 2*l1*ido] = wa2[i]*cr3 - isign*wa2[i + 1]*ci3; - ch[ah + 2*l1*ido + 1] = wa2[i]*ci3 + isign*wa2[i + 1]*cr3; - ch[ah + 3*l1*ido] = wa3[i]*cr4 -isign*wa3[i + 1]*ci4; - ch[ah + 3*l1*ido + 1] = wa3[i]*ci4 + isign*wa3[i + 1]*cr4; - } - } - } - } /* passf4 */ - - -static void passf5(int ido, int l1, const Treal cc[], Treal ch[], - const Treal wa1[], const Treal wa2[], const Treal wa3[], const Treal wa4[], int isign) - /* isign == -1 for forward transform and +1 for backward transform */ - { - static const Treal tr11 = 0.3090169943749474241; - static const Treal ti11 = 0.95105651629515357212; - static const Treal tr12 = -0.8090169943749474241; - static const Treal ti12 = 0.58778525229247312917; - int i, k, ac, ah; - Treal ci2, ci3, ci4, ci5, di3, di4, di5, di2, cr2, cr3, cr5, cr4, ti2, ti3, - ti4, ti5, dr3, dr4, dr5, dr2, tr2, tr3, tr4, tr5; - if (ido == 2) { - for (k = 1; k <= l1; ++k) { - ac = (5*k - 4)*ido + 1; - ti5 = ref(cc,ac) - ref(cc,ac + 3*ido); - ti2 = ref(cc,ac) + ref(cc,ac + 3*ido); - ti4 = ref(cc,ac + ido) - ref(cc,ac + 2*ido); - ti3 = ref(cc,ac + ido) + ref(cc,ac + 2*ido); - tr5 = ref(cc,ac - 1) - ref(cc,ac + 3*ido - 1); - tr2 = ref(cc,ac - 1) + ref(cc,ac + 3*ido - 1); - tr4 = ref(cc,ac + ido - 1) - ref(cc,ac + 2*ido - 1); - tr3 = ref(cc,ac + ido - 1) + ref(cc,ac + 2*ido - 1); - ah = (k - 1)*ido; - ch[ah] = ref(cc,ac - ido - 1) + tr2 + tr3; - ch[ah + 1] = ref(cc,ac - ido) + ti2 + ti3; - cr2 = ref(cc,ac - ido - 1) + tr11*tr2 + tr12*tr3; - ci2 = ref(cc,ac - ido) + tr11*ti2 + tr12*ti3; - cr3 = ref(cc,ac - ido - 1) + tr12*tr2 + tr11*tr3; - ci3 = ref(cc,ac - ido) + tr12*ti2 + tr11*ti3; - cr5 = isign*(ti11*tr5 + ti12*tr4); - ci5 = isign*(ti11*ti5 + ti12*ti4); - cr4 = isign*(ti12*tr5 - ti11*tr4); - ci4 = isign*(ti12*ti5 - ti11*ti4); - ch[ah + l1*ido] = cr2 - ci5; - ch[ah + 4*l1*ido] = cr2 + ci5; - ch[ah + l1*ido + 1] = ci2 + cr5; - ch[ah + 2*l1*ido + 1] = ci3 + cr4; - ch[ah + 2*l1*ido] = cr3 - ci4; - ch[ah + 3*l1*ido] = cr3 + ci4; - ch[ah + 3*l1*ido + 1] = ci3 - cr4; - ch[ah + 4*l1*ido + 1] = ci2 - cr5; - } - } else { - for (k=1; k<=l1; k++) { - for (i=0; i<ido-1; i+=2) { - ac = i + 1 + (k*5 - 4)*ido; - ti5 = ref(cc,ac) - ref(cc,ac + 3*ido); - ti2 = ref(cc,ac) + ref(cc,ac + 3*ido); - ti4 = ref(cc,ac + ido) - ref(cc,ac + 2*ido); - ti3 = ref(cc,ac + ido) + ref(cc,ac + 2*ido); - tr5 = ref(cc,ac - 1) - ref(cc,ac + 3*ido - 1); - tr2 = ref(cc,ac - 1) + ref(cc,ac + 3*ido - 1); - tr4 = ref(cc,ac + ido - 1) - ref(cc,ac + 2*ido - 1); - tr3 = ref(cc,ac + ido - 1) + ref(cc,ac + 2*ido - 1); - ah = i + (k - 1)*ido; - ch[ah] = ref(cc,ac - ido - 1) + tr2 + tr3; - ch[ah + 1] = ref(cc,ac - ido) + ti2 + ti3; - cr2 = ref(cc,ac - ido - 1) + tr11*tr2 + tr12*tr3; - - ci2 = ref(cc,ac - ido) + tr11*ti2 + tr12*ti3; - cr3 = ref(cc,ac - ido - 1) + tr12*tr2 + tr11*tr3; - - ci3 = ref(cc,ac - ido) + tr12*ti2 + tr11*ti3; - cr5 = isign*(ti11*tr5 + ti12*tr4); - ci5 = isign*(ti11*ti5 + ti12*ti4); - cr4 = isign*(ti12*tr5 - ti11*tr4); - ci4 = isign*(ti12*ti5 - ti11*ti4); - dr3 = cr3 - ci4; - dr4 = cr3 + ci4; - di3 = ci3 + cr4; - di4 = ci3 - cr4; - dr5 = cr2 + ci5; - dr2 = cr2 - ci5; - di5 = ci2 - cr5; - di2 = ci2 + cr5; - ch[ah + l1*ido] = wa1[i]*dr2 - isign*wa1[i+1]*di2; - ch[ah + l1*ido + 1] = wa1[i]*di2 + isign*wa1[i+1]*dr2; - ch[ah + 2*l1*ido] = wa2[i]*dr3 - isign*wa2[i+1]*di3; - ch[ah + 2*l1*ido + 1] = wa2[i]*di3 + isign*wa2[i+1]*dr3; - ch[ah + 3*l1*ido] = wa3[i]*dr4 - isign*wa3[i+1]*di4; - ch[ah + 3*l1*ido + 1] = wa3[i]*di4 + isign*wa3[i+1]*dr4; - ch[ah + 4*l1*ido] = wa4[i]*dr5 - isign*wa4[i+1]*di5; - ch[ah + 4*l1*ido + 1] = wa4[i]*di5 + isign*wa4[i+1]*dr5; - } - } - } - } /* passf5 */ - - -static void passf(int *nac, int ido, int ip, int l1, int idl1, - Treal cc[], Treal ch[], - const Treal wa[], int isign) - /* isign is -1 for forward transform and +1 for backward transform */ - { - int idij, idlj, idot, ipph, i, j, k, l, jc, lc, ik, idj, idl, inc,idp; - Treal wai, war; - - idot = ido / 2; - /* nt = ip*idl1;*/ - ipph = (ip + 1) / 2; - idp = ip*ido; - if (ido >= l1) { - for (j=1; j<ipph; j++) { - jc = ip - j; - for (k=0; k<l1; k++) { - for (i=0; i<ido; i++) { - ch[i + (k + j*l1)*ido] = - ref(cc,i + (j + k*ip)*ido) + ref(cc,i + (jc + k*ip)*ido); - ch[i + (k + jc*l1)*ido] = - ref(cc,i + (j + k*ip)*ido) - ref(cc,i + (jc + k*ip)*ido); - } - } - } - for (k=0; k<l1; k++) - for (i=0; i<ido; i++) - ch[i + k*ido] = ref(cc,i + k*ip*ido); - } else { - for (j=1; j<ipph; j++) { - jc = ip - j; - for (i=0; i<ido; i++) { - for (k=0; k<l1; k++) { - ch[i + (k + j*l1)*ido] = ref(cc,i + (j + k*ip)*ido) + ref(cc,i + (jc + k* - ip)*ido); - ch[i + (k + jc*l1)*ido] = ref(cc,i + (j + k*ip)*ido) - ref(cc,i + (jc + k* - ip)*ido); - } - } - } - for (i=0; i<ido; i++) - for (k=0; k<l1; k++) - ch[i + k*ido] = ref(cc,i + k*ip*ido); - } - - idl = 2 - ido; - inc = 0; - for (l=1; l<ipph; l++) { - lc = ip - l; - idl += ido; - for (ik=0; ik<idl1; ik++) { - cc[ik + l*idl1] = ch[ik] + wa[idl - 2]*ch[ik + idl1]; - cc[ik + lc*idl1] = isign*wa[idl-1]*ch[ik + (ip-1)*idl1]; - } - idlj = idl; - inc += ido; - for (j=2; j<ipph; j++) { - jc = ip - j; - idlj += inc; - if (idlj > idp) idlj -= idp; - war = wa[idlj - 2]; - wai = wa[idlj-1]; - for (ik=0; ik<idl1; ik++) { - cc[ik + l*idl1] += war*ch[ik + j*idl1]; - cc[ik + lc*idl1] += isign*wai*ch[ik + jc*idl1]; - } - } - } - for (j=1; j<ipph; j++) - for (ik=0; ik<idl1; ik++) - ch[ik] += ch[ik + j*idl1]; - for (j=1; j<ipph; j++) { - jc = ip - j; - for (ik=1; ik<idl1; ik+=2) { - ch[ik - 1 + j*idl1] = cc[ik - 1 + j*idl1] - cc[ik + jc*idl1]; - ch[ik - 1 + jc*idl1] = cc[ik - 1 + j*idl1] + cc[ik + jc*idl1]; - ch[ik + j*idl1] = cc[ik + j*idl1] + cc[ik - 1 + jc*idl1]; - ch[ik + jc*idl1] = cc[ik + j*idl1] - cc[ik - 1 + jc*idl1]; - } - } - *nac = 1; - if (ido == 2) return; - *nac = 0; - for (ik=0; ik<idl1; ik++) - cc[ik] = ch[ik]; - for (j=1; j<ip; j++) { - for (k=0; k<l1; k++) { - cc[(k + j*l1)*ido + 0] = ch[(k + j*l1)*ido + 0]; - cc[(k + j*l1)*ido + 1] = ch[(k + j*l1)*ido + 1]; - } - } - if (idot <= l1) { - idij = 0; - for (j=1; j<ip; j++) { - idij += 2; - for (i=3; i<ido; i+=2) { - idij += 2; - for (k=0; k<l1; k++) { - cc[i - 1 + (k + j*l1)*ido] = - wa[idij - 2]*ch[i - 1 + (k + j*l1)*ido] - - isign*wa[idij-1]*ch[i + (k + j*l1)*ido]; - cc[i + (k + j*l1)*ido] = - wa[idij - 2]*ch[i + (k + j*l1)*ido] + - isign*wa[idij-1]*ch[i - 1 + (k + j*l1)*ido]; - } - } - } - } else { - idj = 2 - ido; - for (j=1; j<ip; j++) { - idj += ido; - for (k = 0; k < l1; k++) { - idij = idj; - for (i=3; i<ido; i+=2) { - idij += 2; - cc[i - 1 + (k + j*l1)*ido] = - wa[idij - 2]*ch[i - 1 + (k + j*l1)*ido] - - isign*wa[idij-1]*ch[i + (k + j*l1)*ido]; - cc[i + (k + j*l1)*ido] = - wa[idij - 2]*ch[i + (k + j*l1)*ido] + - isign*wa[idij-1]*ch[i - 1 + (k + j*l1)*ido]; - } - } - } - } - } /* passf */ - - - /* ---------------------------------------------------------------------- -radf2,radb2, radf3,radb3, radf4,radb4, radf5,radb5, radfg,radbg. -Treal FFT passes fwd and bwd. ----------------------------------------------------------------------- */ - -static void radf2(int ido, int l1, const Treal cc[], Treal ch[], const Treal wa1[]) - { - int i, k, ic; - Treal ti2, tr2; - for (k=0; k<l1; k++) { - ch[2*k*ido] = - ref(cc,k*ido) + ref(cc,(k + l1)*ido); - ch[(2*k+1)*ido + ido-1] = - ref(cc,k*ido) - ref(cc,(k + l1)*ido); - } - if (ido < 2) return; - if (ido != 2) { - for (k=0; k<l1; k++) { - for (i=2; i<ido; i+=2) { - ic = ido - i; - tr2 = wa1[i - 2]*ref(cc, i-1 + (k + l1)*ido) + wa1[i - 1]*ref(cc, i + (k + l1)*ido); - ti2 = wa1[i - 2]*ref(cc, i + (k + l1)*ido) - wa1[i - 1]*ref(cc, i-1 + (k + l1)*ido); - ch[i + 2*k*ido] = ref(cc,i + k*ido) + ti2; - ch[ic + (2*k+1)*ido] = ti2 - ref(cc,i + k*ido); - ch[i - 1 + 2*k*ido] = ref(cc,i - 1 + k*ido) + tr2; - ch[ic - 1 + (2*k+1)*ido] = ref(cc,i - 1 + k*ido) - tr2; - } - } - if (ido % 2 == 1) return; - } - for (k=0; k<l1; k++) { - ch[(2*k+1)*ido] = -ref(cc,ido-1 + (k + l1)*ido); - ch[ido-1 + 2*k*ido] = ref(cc,ido-1 + k*ido); - } - } /* radf2 */ - - -static void radb2(int ido, int l1, const Treal cc[], Treal ch[], const Treal wa1[]) - { - int i, k, ic; - Treal ti2, tr2; - for (k=0; k<l1; k++) { - ch[k*ido] = - ref(cc,2*k*ido) + ref(cc,ido-1 + (2*k+1)*ido); - ch[(k + l1)*ido] = - ref(cc,2*k*ido) - ref(cc,ido-1 + (2*k+1)*ido); - } - if (ido < 2) return; - if (ido != 2) { - for (k = 0; k < l1; ++k) { - for (i = 2; i < ido; i += 2) { - ic = ido - i; - ch[i-1 + k*ido] = - ref(cc,i-1 + 2*k*ido) + ref(cc,ic-1 + (2*k+1)*ido); - tr2 = ref(cc,i-1 + 2*k*ido) - ref(cc,ic-1 + (2*k+1)*ido); - ch[i + k*ido] = - ref(cc,i + 2*k*ido) - ref(cc,ic + (2*k+1)*ido); - ti2 = ref(cc,i + (2*k)*ido) + ref(cc,ic + (2*k+1)*ido); - ch[i-1 + (k + l1)*ido] = - wa1[i - 2]*tr2 - wa1[i - 1]*ti2; - ch[i + (k + l1)*ido] = - wa1[i - 2]*ti2 + wa1[i - 1]*tr2; - } - } - if (ido % 2 == 1) return; - } - for (k = 0; k < l1; k++) { - ch[ido-1 + k*ido] = 2*ref(cc,ido-1 + 2*k*ido); - ch[ido-1 + (k + l1)*ido] = -2*ref(cc,(2*k+1)*ido); - } - } /* radb2 */ - - -static void radf3(int ido, int l1, const Treal cc[], Treal ch[], - const Treal wa1[], const Treal wa2[]) - { - static const Treal taur = -0.5; - static const Treal taui = 0.86602540378443864676; - int i, k, ic; - Treal ci2, di2, di3, cr2, dr2, dr3, ti2, ti3, tr2, tr3; - for (k=0; k<l1; k++) { - cr2 = ref(cc,(k + l1)*ido) + ref(cc,(k + 2*l1)*ido); - ch[3*k*ido] = ref(cc,k*ido) + cr2; - ch[(3*k+2)*ido] = taui*(ref(cc,(k + l1*2)*ido) - ref(cc,(k + l1)*ido)); - ch[ido-1 + (3*k + 1)*ido] = ref(cc,k*ido) + taur*cr2; - } - if (ido == 1) return; - for (k=0; k<l1; k++) { - for (i=2; i<ido; i+=2) { - ic = ido - i; - dr2 = wa1[i - 2]*ref(cc,i - 1 + (k + l1)*ido) + - wa1[i - 1]*ref(cc,i + (k + l1)*ido); - di2 = wa1[i - 2]*ref(cc,i + (k + l1)*ido) - wa1[i - 1]*ref(cc,i - 1 + (k + l1)*ido); - dr3 = wa2[i - 2]*ref(cc,i - 1 + (k + l1*2)*ido) + wa2[i - 1]*ref(cc,i + (k + l1*2)*ido); - di3 = wa2[i - 2]*ref(cc,i + (k + l1*2)*ido) - wa2[i - 1]*ref(cc,i - 1 + (k + l1*2)*ido); - cr2 = dr2 + dr3; - ci2 = di2 + di3; - ch[i - 1 + 3*k*ido] = ref(cc,i - 1 + k*ido) + cr2; - ch[i + 3*k*ido] = ref(cc,i + k*ido) + ci2; - tr2 = ref(cc,i - 1 + k*ido) + taur*cr2; - ti2 = ref(cc,i + k*ido) + taur*ci2; - tr3 = taui*(di2 - di3); - ti3 = taui*(dr3 - dr2); - ch[i - 1 + (3*k + 2)*ido] = tr2 + tr3; - ch[ic - 1 + (3*k + 1)*ido] = tr2 - tr3; - ch[i + (3*k + 2)*ido] = ti2 + ti3; - ch[ic + (3*k + 1)*ido] = ti3 - ti2; - } - } - } /* radf3 */ - - -static void radb3(int ido, int l1, const Treal cc[], Treal ch[], - const Treal wa1[], const Treal wa2[]) - { - static const Treal taur = -0.5; - static const Treal taui = 0.86602540378443864676; - int i, k, ic; - Treal ci2, ci3, di2, di3, cr2, cr3, dr2, dr3, ti2, tr2; - for (k=0; k<l1; k++) { - tr2 = 2*ref(cc,ido-1 + (3*k + 1)*ido); - cr2 = ref(cc,3*k*ido) + taur*tr2; - ch[k*ido] = ref(cc,3*k*ido) + tr2; - ci3 = 2*taui*ref(cc,(3*k + 2)*ido); - ch[(k + l1)*ido] = cr2 - ci3; - ch[(k + 2*l1)*ido] = cr2 + ci3; - } - if (ido == 1) return; - for (k=0; k<l1; k++) { - for (i=2; i<ido; i+=2) { - ic = ido - i; - tr2 = ref(cc,i - 1 + (3*k + 2)*ido) + ref(cc,ic - 1 + (3*k + 1)*ido); - cr2 = ref(cc,i - 1 + 3*k*ido) + taur*tr2; - ch[i - 1 + k*ido] = ref(cc,i - 1 + 3*k*ido) + tr2; - ti2 = ref(cc,i + (3*k + 2)*ido) - ref(cc,ic + (3*k + 1)*ido); - ci2 = ref(cc,i + 3*k*ido) + taur*ti2; - ch[i + k*ido] = ref(cc,i + 3*k*ido) + ti2; - cr3 = taui*(ref(cc,i - 1 + (3*k + 2)*ido) - ref(cc,ic - 1 + (3*k + 1)*ido)); - ci3 = taui*(ref(cc,i + (3*k + 2)*ido) + ref(cc,ic + (3*k + 1)*ido)); - dr2 = cr2 - ci3; - dr3 = cr2 + ci3; - di2 = ci2 + cr3; - di3 = ci2 - cr3; - ch[i - 1 + (k + l1)*ido] = wa1[i - 2]*dr2 - wa1[i - 1]*di2; - ch[i + (k + l1)*ido] = wa1[i - 2]*di2 + wa1[i - 1]*dr2; - ch[i - 1 + (k + 2*l1)*ido] = wa2[i - 2]*dr3 - wa2[i - 1]*di3; - ch[i + (k + 2*l1)*ido] = wa2[i - 2]*di3 + wa2[i - 1]*dr3; - } - } - } /* radb3 */ - - -static void radf4(int ido, int l1, const Treal cc[], Treal ch[], - const Treal wa1[], const Treal wa2[], const Treal wa3[]) - { - static const Treal hsqt2 = 0.70710678118654752440; - int i, k, ic; - Treal ci2, ci3, ci4, cr2, cr3, cr4, ti1, ti2, ti3, ti4, tr1, tr2, tr3, tr4; - for (k=0; k<l1; k++) { - tr1 = ref(cc,(k + l1)*ido) + ref(cc,(k + 3*l1)*ido); - tr2 = ref(cc,k*ido) + ref(cc,(k + 2*l1)*ido); - ch[4*k*ido] = tr1 + tr2; - ch[ido-1 + (4*k + 3)*ido] = tr2 - tr1; - ch[ido-1 + (4*k + 1)*ido] = ref(cc,k*ido) - ref(cc,(k + 2*l1)*ido); - ch[(4*k + 2)*ido] = ref(cc,(k + 3*l1)*ido) - ref(cc,(k + l1)*ido); - } - if (ido < 2) return; - if (ido != 2) { - for (k=0; k<l1; k++) { - for (i=2; i<ido; i += 2) { - ic = ido - i; - cr2 = wa1[i - 2]*ref(cc,i - 1 + (k + l1)*ido) + wa1[i - 1]*ref(cc,i + (k + l1)*ido); - ci2 = wa1[i - 2]*ref(cc,i + (k + l1)*ido) - wa1[i - 1]*ref(cc,i - 1 + (k + l1)*ido); - cr3 = wa2[i - 2]*ref(cc,i - 1 + (k + 2*l1)*ido) + wa2[i - 1]*ref(cc,i + (k + 2*l1)* - ido); - ci3 = wa2[i - 2]*ref(cc,i + (k + 2*l1)*ido) - wa2[i - 1]*ref(cc,i - 1 + (k + 2*l1)* - ido); - cr4 = wa3[i - 2]*ref(cc,i - 1 + (k + 3*l1)*ido) + wa3[i - 1]*ref(cc,i + (k + 3*l1)* - ido); - ci4 = wa3[i - 2]*ref(cc,i + (k + 3*l1)*ido) - wa3[i - 1]*ref(cc,i - 1 + (k + 3*l1)* - ido); - tr1 = cr2 + cr4; - tr4 = cr4 - cr2; - ti1 = ci2 + ci4; - ti4 = ci2 - ci4; - ti2 = ref(cc,i + k*ido) + ci3; - ti3 = ref(cc,i + k*ido) - ci3; - tr2 = ref(cc,i - 1 + k*ido) + cr3; - tr3 = ref(cc,i - 1 + k*ido) - cr3; - ch[i - 1 + 4*k*ido] = tr1 + tr2; - ch[ic - 1 + (4*k + 3)*ido] = tr2 - tr1; - ch[i + 4*k*ido] = ti1 + ti2; - ch[ic + (4*k + 3)*ido] = ti1 - ti2; - ch[i - 1 + (4*k + 2)*ido] = ti4 + tr3; - ch[ic - 1 + (4*k + 1)*ido] = tr3 - ti4; - ch[i + (4*k + 2)*ido] = tr4 + ti3; - ch[ic + (4*k + 1)*ido] = tr4 - ti3; - } - } - if (ido % 2 == 1) return; - } - for (k=0; k<l1; k++) { - ti1 = -hsqt2*(ref(cc,ido-1 + (k + l1)*ido) + ref(cc,ido-1 + (k + 3*l1)*ido)); - tr1 = hsqt2*(ref(cc,ido-1 + (k + l1)*ido) - ref(cc,ido-1 + (k + 3*l1)*ido)); - ch[ido-1 + 4*k*ido] = tr1 + ref(cc,ido-1 + k*ido); - ch[ido-1 + (4*k + 2)*ido] = ref(cc,ido-1 + k*ido) - tr1; - ch[(4*k + 1)*ido] = ti1 - ref(cc,ido-1 + (k + 2*l1)*ido); - ch[(4*k + 3)*ido] = ti1 + ref(cc,ido-1 + (k + 2*l1)*ido); - } - } /* radf4 */ - - -static void radb4(int ido, int l1, const Treal cc[], Treal ch[], - const Treal wa1[], const Treal wa2[], const Treal wa3[]) - { - static const Treal sqrt2 = 1.41421356237309504880; - int i, k, ic; - Treal ci2, ci3, ci4, cr2, cr3, cr4, ti1, ti2, ti3, ti4, tr1, tr2, tr3, tr4; - for (k = 0; k < l1; k++) { - tr1 = ref(cc,4*k*ido) - ref(cc,ido-1 + (4*k + 3)*ido); - tr2 = ref(cc,4*k*ido) + ref(cc,ido-1 + (4*k + 3)*ido); - tr3 = ref(cc,ido-1 + (4*k + 1)*ido) + ref(cc,ido-1 + (4*k + 1)*ido); - tr4 = ref(cc,(4*k + 2)*ido) + ref(cc,(4*k + 2)*ido); - ch[k*ido] = tr2 + tr3; - ch[(k + l1)*ido] = tr1 - tr4; - ch[(k + 2*l1)*ido] = tr2 - tr3; - ch[(k + 3*l1)*ido] = tr1 + tr4; - } - if (ido < 2) return; - if (ido != 2) { - for (k = 0; k < l1; ++k) { - for (i = 2; i < ido; i += 2) { - ic = ido - i; - ti1 = ref(cc,i + 4*k*ido) + ref(cc,ic + (4*k + 3)*ido); - ti2 = ref(cc,i + 4*k*ido) - ref(cc,ic + (4*k + 3)*ido); - ti3 = ref(cc,i + (4*k + 2)*ido) - ref(cc,ic + (4*k + 1)*ido); - tr4 = ref(cc,i + (4*k + 2)*ido) + ref(cc,ic + (4*k + 1)*ido); - tr1 = ref(cc,i - 1 + 4*k*ido) - ref(cc,ic - 1 + (4*k + 3)*ido); - tr2 = ref(cc,i - 1 + 4*k*ido) + ref(cc,ic - 1 + (4*k + 3)*ido); - ti4 = ref(cc,i - 1 + (4*k + 2)*ido) - ref(cc,ic - 1 + (4*k + 1)*ido); - tr3 = ref(cc,i - 1 + (4*k + 2)*ido) + ref(cc,ic - 1 + (4*k + 1)*ido); - ch[i - 1 + k*ido] = tr2 + tr3; - cr3 = tr2 - tr3; - ch[i + k*ido] = ti2 + ti3; - ci3 = ti2 - ti3; - cr2 = tr1 - tr4; - cr4 = tr1 + tr4; - ci2 = ti1 + ti4; - ci4 = ti1 - ti4; - ch[i - 1 + (k + l1)*ido] = wa1[i - 2]*cr2 - wa1[i - 1]*ci2; - ch[i + (k + l1)*ido] = wa1[i - 2]*ci2 + wa1[i - 1]*cr2; - ch[i - 1 + (k + 2*l1)*ido] = wa2[i - 2]*cr3 - wa2[i - 1]*ci3; - ch[i + (k + 2*l1)*ido] = wa2[i - 2]*ci3 + wa2[i - 1]*cr3; - ch[i - 1 + (k + 3*l1)*ido] = wa3[i - 2]*cr4 - wa3[i - 1]*ci4; - ch[i + (k + 3*l1)*ido] = wa3[i - 2]*ci4 + wa3[i - 1]*cr4; - } - } - if (ido % 2 == 1) return; - } - for (k = 0; k < l1; k++) { - ti1 = ref(cc,(4*k + 1)*ido) + ref(cc,(4*k + 3)*ido); - ti2 = ref(cc,(4*k + 3)*ido) - ref(cc,(4*k + 1)*ido); - tr1 = ref(cc,ido-1 + 4*k*ido) - ref(cc,ido-1 + (4*k + 2)*ido); - tr2 = ref(cc,ido-1 + 4*k*ido) + ref(cc,ido-1 + (4*k + 2)*ido); - ch[ido-1 + k*ido] = tr2 + tr2; - ch[ido-1 + (k + l1)*ido] = sqrt2*(tr1 - ti1); - ch[ido-1 + (k + 2*l1)*ido] = ti2 + ti2; - ch[ido-1 + (k + 3*l1)*ido] = -sqrt2*(tr1 + ti1); - } - } /* radb4 */ - - -static void radf5(int ido, int l1, const Treal cc[], Treal ch[], - const Treal wa1[], const Treal wa2[], const Treal wa3[], const Treal wa4[]) - { - static const Treal tr11 = 0.3090169943749474241; - static const Treal ti11 = 0.95105651629515357212; - static const Treal tr12 = -0.8090169943749474241; - static const Treal ti12 = 0.58778525229247312917; - int i, k, ic; - Treal ci2, di2, ci4, ci5, di3, di4, di5, ci3, cr2, cr3, dr2, dr3, dr4, dr5, - cr5, cr4, ti2, ti3, ti5, ti4, tr2, tr3, tr4, tr5; - for (k = 0; k < l1; k++) { - cr2 = ref(cc,(k + 4*l1)*ido) + ref(cc,(k + l1)*ido); - ci5 = ref(cc,(k + 4*l1)*ido) - ref(cc,(k + l1)*ido); - cr3 = ref(cc,(k + 3*l1)*ido) + ref(cc,(k + 2*l1)*ido); - ci4 = ref(cc,(k + 3*l1)*ido) - ref(cc,(k + 2*l1)*ido); - ch[5*k*ido] = ref(cc,k*ido) + cr2 + cr3; - ch[ido-1 + (5*k + 1)*ido] = ref(cc,k*ido) + tr11*cr2 + tr12*cr3; - ch[(5*k + 2)*ido] = ti11*ci5 + ti12*ci4; - ch[ido-1 + (5*k + 3)*ido] = ref(cc,k*ido) + tr12*cr2 + tr11*cr3; - ch[(5*k + 4)*ido] = ti12*ci5 - ti11*ci4; - } - if (ido == 1) return; - for (k = 0; k < l1; ++k) { - for (i = 2; i < ido; i += 2) { - ic = ido - i; - dr2 = wa1[i - 2]*ref(cc,i - 1 + (k + l1)*ido) + wa1[i - 1]*ref(cc,i + (k + l1)*ido); - di2 = wa1[i - 2]*ref(cc,i + (k + l1)*ido) - wa1[i - 1]*ref(cc,i - 1 + (k + l1)*ido); - dr3 = wa2[i - 2]*ref(cc,i - 1 + (k + 2*l1)*ido) + wa2[i - 1]*ref(cc,i + (k + 2*l1)*ido); - di3 = wa2[i - 2]*ref(cc,i + (k + 2*l1)*ido) - wa2[i - 1]*ref(cc,i - 1 + (k + 2*l1)*ido); - dr4 = wa3[i - 2]*ref(cc,i - 1 + (k + 3*l1)*ido) + wa3[i - 1]*ref(cc,i + (k + 3*l1)*ido); - di4 = wa3[i - 2]*ref(cc,i + (k + 3*l1)*ido) - wa3[i - 1]*ref(cc,i - 1 + (k + 3*l1)*ido); - dr5 = wa4[i - 2]*ref(cc,i - 1 + (k + 4*l1)*ido) + wa4[i - 1]*ref(cc,i + (k + 4*l1)*ido); - di5 = wa4[i - 2]*ref(cc,i + (k + 4*l1)*ido) - wa4[i - 1]*ref(cc,i - 1 + (k + 4*l1)*ido); - cr2 = dr2 + dr5; - ci5 = dr5 - dr2; - cr5 = di2 - di5; - ci2 = di2 + di5; - cr3 = dr3 + dr4; - ci4 = dr4 - dr3; - cr4 = di3 - di4; - ci3 = di3 + di4; - ch[i - 1 + 5*k*ido] = ref(cc,i - 1 + k*ido) + cr2 + cr3; - ch[i + 5*k*ido] = ref(cc,i + k*ido) + ci2 + ci3; - tr2 = ref(cc,i - 1 + k*ido) + tr11*cr2 + tr12*cr3; - ti2 = ref(cc,i + k*ido) + tr11*ci2 + tr12*ci3; - tr3 = ref(cc,i - 1 + k*ido) + tr12*cr2 + tr11*cr3; - ti3 = ref(cc,i + k*ido) + tr12*ci2 + tr11*ci3; - tr5 = ti11*cr5 + ti12*cr4; - ti5 = ti11*ci5 + ti12*ci4; - tr4 = ti12*cr5 - ti11*cr4; - ti4 = ti12*ci5 - ti11*ci4; - ch[i - 1 + (5*k + 2)*ido] = tr2 + tr5; - ch[ic - 1 + (5*k + 1)*ido] = tr2 - tr5; - ch[i + (5*k + 2)*ido] = ti2 + ti5; - ch[ic + (5*k + 1)*ido] = ti5 - ti2; - ch[i - 1 + (5*k + 4)*ido] = tr3 + tr4; - ch[ic - 1 + (5*k + 3)*ido] = tr3 - tr4; - ch[i + (5*k + 4)*ido] = ti3 + ti4; - ch[ic + (5*k + 3)*ido] = ti4 - ti3; - } - } - } /* radf5 */ - - -static void radb5(int ido, int l1, const Treal cc[], Treal ch[], - const Treal wa1[], const Treal wa2[], const Treal wa3[], const Treal wa4[]) - { - static const Treal tr11 = 0.3090169943749474241; - static const Treal ti11 = 0.95105651629515357212; - static const Treal tr12 = -0.8090169943749474241; - static const Treal ti12 = 0.58778525229247312917; - int i, k, ic; - Treal ci2, ci3, ci4, ci5, di3, di4, di5, di2, cr2, cr3, cr5, cr4, ti2, ti3, - ti4, ti5, dr3, dr4, dr5, dr2, tr2, tr3, tr4, tr5; - for (k = 0; k < l1; k++) { - ti5 = 2*ref(cc,(5*k + 2)*ido); - ti4 = 2*ref(cc,(5*k + 4)*ido); - tr2 = 2*ref(cc,ido-1 + (5*k + 1)*ido); - tr3 = 2*ref(cc,ido-1 + (5*k + 3)*ido); - ch[k*ido] = ref(cc,5*k*ido) + tr2 + tr3; - cr2 = ref(cc,5*k*ido) + tr11*tr2 + tr12*tr3; - cr3 = ref(cc,5*k*ido) + tr12*tr2 + tr11*tr3; - ci5 = ti11*ti5 + ti12*ti4; - ci4 = ti12*ti5 - ti11*ti4; - ch[(k + l1)*ido] = cr2 - ci5; - ch[(k + 2*l1)*ido] = cr3 - ci4; - ch[(k + 3*l1)*ido] = cr3 + ci4; - ch[(k + 4*l1)*ido] = cr2 + ci5; - } - if (ido == 1) return; - for (k = 0; k < l1; ++k) { - for (i = 2; i < ido; i += 2) { - ic = ido - i; - ti5 = ref(cc,i + (5*k + 2)*ido) + ref(cc,ic + (5*k + 1)*ido); - ti2 = ref(cc,i + (5*k + 2)*ido) - ref(cc,ic + (5*k + 1)*ido); - ti4 = ref(cc,i + (5*k + 4)*ido) + ref(cc,ic + (5*k + 3)*ido); - ti3 = ref(cc,i + (5*k + 4)*ido) - ref(cc,ic + (5*k + 3)*ido); - tr5 = ref(cc,i - 1 + (5*k + 2)*ido) - ref(cc,ic - 1 + (5*k + 1)*ido); - tr2 = ref(cc,i - 1 + (5*k + 2)*ido) + ref(cc,ic - 1 + (5*k + 1)*ido); - tr4 = ref(cc,i - 1 + (5*k + 4)*ido) - ref(cc,ic - 1 + (5*k + 3)*ido); - tr3 = ref(cc,i - 1 + (5*k + 4)*ido) + ref(cc,ic - 1 + (5*k + 3)*ido); - ch[i - 1 + k*ido] = ref(cc,i - 1 + 5*k*ido) + tr2 + tr3; - ch[i + k*ido] = ref(cc,i + 5*k*ido) + ti2 + ti3; - cr2 = ref(cc,i - 1 + 5*k*ido) + tr11*tr2 + tr12*tr3; - - ci2 = ref(cc,i + 5*k*ido) + tr11*ti2 + tr12*ti3; - cr3 = ref(cc,i - 1 + 5*k*ido) + tr12*tr2 + tr11*tr3; - - ci3 = ref(cc,i + 5*k*ido) + tr12*ti2 + tr11*ti3; - cr5 = ti11*tr5 + ti12*tr4; - ci5 = ti11*ti5 + ti12*ti4; - cr4 = ti12*tr5 - ti11*tr4; - ci4 = ti12*ti5 - ti11*ti4; - dr3 = cr3 - ci4; - dr4 = cr3 + ci4; - di3 = ci3 + cr4; - di4 = ci3 - cr4; - dr5 = cr2 + ci5; - dr2 = cr2 - ci5; - di5 = ci2 - cr5; - di2 = ci2 + cr5; - ch[i - 1 + (k + l1)*ido] = wa1[i - 2]*dr2 - wa1[i - 1]*di2; - ch[i + (k + l1)*ido] = wa1[i - 2]*di2 + wa1[i - 1]*dr2; - ch[i - 1 + (k + 2*l1)*ido] = wa2[i - 2]*dr3 - wa2[i - 1]*di3; - ch[i + (k + 2*l1)*ido] = wa2[i - 2]*di3 + wa2[i - 1]*dr3; - ch[i - 1 + (k + 3*l1)*ido] = wa3[i - 2]*dr4 - wa3[i - 1]*di4; - ch[i + (k + 3*l1)*ido] = wa3[i - 2]*di4 + wa3[i - 1]*dr4; - ch[i - 1 + (k + 4*l1)*ido] = wa4[i - 2]*dr5 - wa4[i - 1]*di5; - ch[i + (k + 4*l1)*ido] = wa4[i - 2]*di5 + wa4[i - 1]*dr5; - } - } - } /* radb5 */ - - -static void radfg(int ido, int ip, int l1, int idl1, - Treal cc[], Treal ch[], const Treal wa[]) - { - int idij, ipph, i, j, k, l, j2, ic, jc, lc, ik, is, nbd; - Treal dc2, ai1, ai2, ar1, ar2, ds2, dcp, dsp, ar1h, ar2h; - sincos2pi(1, ip, &dsp, &dcp); - ipph = (ip + 1) / 2; - nbd = (ido - 1) / 2; - if (ido != 1) { - for (ik=0; ik<idl1; ik++) ch[ik] = cc[ik]; - for (j=1; j<ip; j++) - for (k=0; k<l1; k++) - ch[(k + j*l1)*ido] = cc[(k + j*l1)*ido]; - if (nbd <= l1) { - is = -ido; - for (j=1; j<ip; j++) { - is += ido; - idij = is-1; - for (i=2; i<ido; i+=2) { - idij += 2; - for (k=0; k<l1; k++) { - ch[i - 1 + (k + j*l1)*ido] = - wa[idij - 1]*cc[i - 1 + (k + j*l1)*ido] + wa[idij]*cc[i + (k + j*l1)*ido]; - ch[i + (k + j*l1)*ido] = - wa[idij - 1]*cc[i + (k + j*l1)*ido] - wa[idij]*cc[i - 1 + (k + j*l1)*ido]; - } - } - } - } else { - is = -ido; - for (j=1; j<ip; j++) { - is += ido; - for (k=0; k<l1; k++) { - idij = is-1; - for (i=2; i<ido; i+=2) { - idij += 2; - ch[i - 1 + (k + j*l1)*ido] = - wa[idij - 1]*cc[i - 1 + (k + j*l1)*ido] + wa[idij]*cc[i + (k + j*l1)*ido]; - ch[i + (k + j*l1)*ido] = - wa[idij - 1]*cc[i + (k + j*l1)*ido] - wa[idij]*cc[i - 1 + (k + j*l1)*ido]; - } - } - } - } - if (nbd >= l1) { - for (j=1; j<ipph; j++) { - jc = ip - j; - for (k=0; k<l1; k++) { - for (i=2; i<ido; i+=2) { - cc[i - 1 + (k + j*l1)*ido] = ch[i - 1 + (k + j*l1)*ido] + ch[i - 1 + (k + jc*l1)*ido]; - cc[i - 1 + (k + jc*l1)*ido] = ch[i + (k + j*l1)*ido] - ch[i + (k + jc*l1)*ido]; - cc[i + (k + j*l1)*ido] = ch[i + (k + j*l1)*ido] + ch[i + (k + jc*l1)*ido]; - cc[i + (k + jc*l1)*ido] = ch[i - 1 + (k + jc*l1)*ido] - ch[i - 1 + (k + j*l1)*ido]; - } - } - } - } else { - for (j=1; j<ipph; j++) { - jc = ip - j; - for (i=2; i<ido; i+=2) { - for (k=0; k<l1; k++) { - cc[i - 1 + (k + j*l1)*ido] = - ch[i - 1 + (k + j*l1)*ido] + ch[i - 1 + (k + jc*l1)*ido]; - cc[i - 1 + (k + jc*l1)*ido] = ch[i + (k + j*l1)*ido] - ch[i + (k + jc*l1)*ido]; - cc[i + (k + j*l1)*ido] = ch[i + (k + j*l1)*ido] + ch[i + (k + jc*l1)*ido]; - cc[i + (k + jc*l1)*ido] = ch[i - 1 + (k + jc*l1)*ido] - ch[i - 1 + (k + j*l1)*ido]; - } - } - } - } - } else { /* now ido == 1 */ - for (ik=0; ik<idl1; ik++) cc[ik] = ch[ik]; - } - for (j=1; j<ipph; j++) { - jc = ip - j; - for (k=0; k<l1; k++) { - cc[(k + j*l1)*ido] = ch[(k + j*l1)*ido] + ch[(k + jc*l1)*ido]; - cc[(k + jc*l1)*ido] = ch[(k + jc*l1)*ido] - ch[(k + j*l1)*ido]; - } - } - - ar1 = 1; - ai1 = 0; - for (l=1; l<ipph; l++) { - lc = ip - l; - ar1h = dcp*ar1 - dsp*ai1; - ai1 = dcp*ai1 + dsp*ar1; - ar1 = ar1h; - for (ik=0; ik<idl1; ik++) { - ch[ik + l*idl1] = cc[ik] + ar1*cc[ik + idl1]; - ch[ik + lc*idl1] = ai1*cc[ik + (ip-1)*idl1]; - } - dc2 = ar1; - ds2 = ai1; - ar2 = ar1; - ai2 = ai1; - for (j=2; j<ipph; j++) { - jc = ip - j; - ar2h = dc2*ar2 - ds2*ai2; - ai2 = dc2*ai2 + ds2*ar2; - ar2 = ar2h; - for (ik=0; ik<idl1; ik++) { - ch[ik + l*idl1] += ar2*cc[ik + j*idl1]; - ch[ik + lc*idl1] += ai2*cc[ik + jc*idl1]; - } - } - } - - for (j=1; j<ipph; j++) - for (ik=0; ik<idl1; ik++) - ch[ik] += cc[ik + j*idl1]; - - if (ido >= l1) { - for (k=0; k<l1; k++) { - for (i=0; i<ido; i++) { - ref(cc,i + k*ip*ido) = ch[i + k*ido]; - } - } - } else { - for (i=0; i<ido; i++) { - for (k=0; k<l1; k++) { - ref(cc,i + k*ip*ido) = ch[i + k*ido]; - } - } - } - for (j=1; j<ipph; j++) { - jc = ip - j; - j2 = 2*j; - for (k=0; k<l1; k++) { - ref(cc,ido-1 + (j2 - 1 + k*ip)*ido) = - ch[(k + j*l1)*ido]; - ref(cc,(j2 + k*ip)*ido) = - ch[(k + jc*l1)*ido]; - } - } - if (ido == 1) return; - if (nbd >= l1) { - for (j=1; j<ipph; j++) { - jc = ip - j; - j2 = 2*j; - for (k=0; k<l1; k++) { - for (i=2; i<ido; i+=2) { - ic = ido - i; - ref(cc,i - 1 + (j2 + k*ip)*ido) = ch[i - 1 + (k + j*l1)*ido] + ch[i - 1 + (k + jc*l1)*ido]; - ref(cc,ic - 1 + (j2 - 1 + k*ip)*ido) = ch[i - 1 + (k + j*l1)*ido] - ch[i - 1 + (k + jc*l1)*ido]; - ref(cc,i + (j2 + k*ip)*ido) = ch[i + (k + j*l1)*ido] + ch[i + (k + jc*l1)*ido]; - ref(cc,ic + (j2 - 1 + k*ip)*ido) = ch[i + (k + jc*l1)*ido] - ch[i + (k + j*l1)*ido]; - } - } - } - } else { - for (j=1; j<ipph; j++) { - jc = ip - j; - j2 = 2*j; - for (i=2; i<ido; i+=2) { - ic = ido - i; - for (k=0; k<l1; k++) { - ref(cc,i - 1 + (j2 + k*ip)*ido) = ch[i - 1 + (k + j*l1)*ido] + ch[i - 1 + (k + jc*l1)*ido]; - ref(cc,ic - 1 + (j2 - 1 + k*ip)*ido) = ch[i - 1 + (k + j*l1)*ido] - ch[i - 1 + (k + jc*l1)*ido]; - ref(cc,i + (j2 + k*ip)*ido) = ch[i + (k + j*l1)*ido] + ch[i + (k + jc*l1)*ido]; - ref(cc,ic + (j2 - 1 + k*ip)*ido) = ch[i + (k + jc*l1)*ido] - ch[i + (k + j*l1)*ido]; - } - } - } - } - } /* radfg */ - - -static void radbg(int ido, int ip, int l1, int idl1, - Treal cc[], Treal ch[], const Treal wa[]) - { - int idij, ipph, i, j, k, l, j2, ic, jc, lc, ik, is; - Treal dc2, ai1, ai2, ar1, ar2, ds2; - int nbd; - Treal dcp, dsp, ar1h, ar2h; - sincos2pi(1, ip, &dsp, &dcp); - nbd = (ido - 1) / 2; - ipph = (ip + 1) / 2; - if (ido >= l1) { - for (k=0; k<l1; k++) { - for (i=0; i<ido; i++) { - ch[i + k*ido] = ref(cc,i + k*ip*ido); - } - } - } else { - for (i=0; i<ido; i++) { - for (k=0; k<l1; k++) { - ch[i + k*ido] = ref(cc,i + k*ip*ido); - } - } - } - for (j=1; j<ipph; j++) { - jc = ip - j; - j2 = 2*j; - for (k=0; k<l1; k++) { - ch[(k + j*l1)*ido] = ref(cc,ido-1 + (j2 - 1 + k*ip)*ido) + ref(cc,ido-1 + (j2 - 1 + k*ip)* - ido); - ch[(k + jc*l1)*ido] = ref(cc,(j2 + k*ip)*ido) + ref(cc,(j2 + k*ip)*ido); - } - } - - if (ido != 1) { - if (nbd >= l1) { - for (j=1; j<ipph; j++) { - jc = ip - j; - for (k=0; k<l1; k++) { - for (i=2; i<ido; i+=2) { - ic = ido - i; - ch[i - 1 + (k + j*l1)*ido] = ref(cc,i - 1 + (2*j + k*ip)*ido) + ref(cc, - ic - 1 + (2*j - 1 + k*ip)*ido); - ch[i - 1 + (k + jc*l1)*ido] = ref(cc,i - 1 + (2*j + k*ip)*ido) - - ref(cc,ic - 1 + (2*j - 1 + k*ip)*ido); - ch[i + (k + j*l1)*ido] = ref(cc,i + (2*j + k*ip)*ido) - ref(cc,ic - + (2*j - 1 + k*ip)*ido); - ch[i + (k + jc*l1)*ido] = ref(cc,i + (2*j + k*ip)*ido) + ref(cc,ic - + (2*j - 1 + k*ip)*ido); - } - } - } - } else { - for (j=1; j<ipph; j++) { - jc = ip - j; - for (i=2; i<ido; i+=2) { - ic = ido - i; - for (k=0; k<l1; k++) { - ch[i - 1 + (k + j*l1)*ido] = ref(cc,i - 1 + (2*j + k*ip)*ido) + ref(cc, - ic - 1 + (2*j - 1 + k*ip)*ido); - ch[i - 1 + (k + jc*l1)*ido] = ref(cc,i - 1 + (2*j + k*ip)*ido) - - ref(cc,ic - 1 + (2*j - 1 + k*ip)*ido); - ch[i + (k + j*l1)*ido] = ref(cc,i + (2*j + k*ip)*ido) - ref(cc,ic - + (2*j - 1 + k*ip)*ido); - ch[i + (k + jc*l1)*ido] = ref(cc,i + (2*j + k*ip)*ido) + ref(cc,ic - + (2*j - 1 + k*ip)*ido); - } - } - } - } - } - - ar1 = 1; - ai1 = 0; - for (l=1; l<ipph; l++) { - lc = ip - l; - ar1h = dcp*ar1 - dsp*ai1; - ai1 = dcp*ai1 + dsp*ar1; - ar1 = ar1h; - for (ik=0; ik<idl1; ik++) { - cc[ik + l*idl1] = ch[ik] + ar1*ch[ik + idl1]; - cc[ik + lc*idl1] = ai1*ch[ik + (ip-1)*idl1]; - } - dc2 = ar1; - ds2 = ai1; - ar2 = ar1; - ai2 = ai1; - for (j=2; j<ipph; j++) { - jc = ip - j; - ar2h = dc2*ar2 - ds2*ai2; - ai2 = dc2*ai2 + ds2*ar2; - ar2 = ar2h; - for (ik=0; ik<idl1; ik++) { - cc[ik + l*idl1] += ar2*ch[ik + j*idl1]; - cc[ik + lc*idl1] += ai2*ch[ik + jc*idl1]; - } - } - } - for (j=1; j<ipph; j++) { - for (ik=0; ik<idl1; ik++) { - ch[ik] += ch[ik + j*idl1]; - } - } - for (j=1; j<ipph; j++) { - jc = ip - j; - for (k=0; k<l1; k++) { - ch[(k + j*l1)*ido] = cc[(k + j*l1)*ido] - cc[(k + jc*l1)*ido]; - ch[(k + jc*l1)*ido] = cc[(k + j*l1)*ido] + cc[(k + jc*l1)*ido]; - } - } - - if (ido == 1) return; - if (nbd >= l1) { - for (j=1; j<ipph; j++) { - jc = ip - j; - for (k=0; k<l1; k++) { - for (i=2; i<ido; i+=2) { - ch[i - 1 + (k + j*l1)*ido] = cc[i - 1 + (k + j*l1)*ido] - cc[i + (k + jc*l1)*ido]; - ch[i - 1 + (k + jc*l1)*ido] = cc[i - 1 + (k + j*l1)*ido] + cc[i + (k + jc*l1)*ido]; - ch[i + (k + j*l1)*ido] = cc[i + (k + j*l1)*ido] + cc[i - 1 + (k + jc*l1)*ido]; - ch[i + (k + jc*l1)*ido] = cc[i + (k + j*l1)*ido] - cc[i - 1 + (k + jc*l1)*ido]; - } - } - } - } else { - for (j=1; j<ipph; j++) { - jc = ip - j; - for (i=2; i<ido; i+=2) { - for (k=0; k<l1; k++) { - ch[i - 1 + (k + j*l1)*ido] = cc[i - 1 + (k + j*l1)*ido] - cc[i + (k + jc*l1)*ido]; - ch[i - 1 + (k + jc*l1)*ido] = cc[i - 1 + (k + j *l1)*ido] + cc[i + (k + jc*l1)*ido]; - ch[i + (k + j*l1)*ido] = cc[i + (k + j*l1)*ido] + cc[i - 1 + (k + jc*l1)*ido]; - ch[i + (k + jc*l1)*ido] = cc[i + (k + j*l1)*ido] - cc[i - 1 + (k + jc*l1)*ido]; - } - } - } - } - for (ik=0; ik<idl1; ik++) cc[ik] = ch[ik]; - for (j=1; j<ip; j++) - for (k=0; k<l1; k++) - cc[(k + j*l1)*ido] = ch[(k + j*l1)*ido]; - if (nbd <= l1) { - is = -ido; - for (j=1; j<ip; j++) { - is += ido; - idij = is-1; - for (i=2; i<ido; i+=2) { - idij += 2; - for (k=0; k<l1; k++) { - cc[i - 1 + (k + j*l1)*ido] = wa[idij - 1]*ch[i - 1 + (k + j*l1)*ido] - wa[idij]* - ch[i + (k + j*l1)*ido]; - cc[i + (k + j*l1)*ido] = wa[idij - 1]*ch[i + (k + j*l1)*ido] + wa[idij]*ch[i - 1 + (k + j*l1)*ido]; - } - } - } - } else { - is = -ido; - for (j=1; j<ip; j++) { - is += ido; - for (k=0; k<l1; k++) { - idij = is - 1; - for (i=2; i<ido; i+=2) { - idij += 2; - cc[i - 1 + (k + j*l1)*ido] = wa[idij-1]*ch[i - 1 + (k + j*l1)*ido] - wa[idij]* - ch[i + (k + j*l1)*ido]; - cc[i + (k + j*l1)*ido] = wa[idij-1]*ch[i + (k + j*l1)*ido] + wa[idij]*ch[i - 1 + (k + j*l1)*ido]; - } - } - } - } - } /* radbg */ - - /* ------------------------------------------------------------ -cfftf1, npy_cfftf, npy_cfftb, cffti1, npy_cffti. Complex FFTs. ---------------------------------------------------------------- */ - -static void cfftf1(int n, Treal c[], Treal ch[], const Treal wa[], const int ifac[MAXFAC+2], int isign) - { - int idot, i; - int k1, l1, l2; - int na, nf, ip, iw, ix2, ix3, ix4, nac, ido, idl1; - Treal *cinput, *coutput; - nf = ifac[1]; - na = 0; - l1 = 1; - iw = 0; - for (k1=2; k1<=nf+1; k1++) { - ip = ifac[k1]; - l2 = ip*l1; - ido = n / l2; - idot = ido + ido; - idl1 = idot*l1; - if (na) { - cinput = ch; - coutput = c; - } else { - cinput = c; - coutput = ch; - } - switch (ip) { - case 4: - ix2 = iw + idot; - ix3 = ix2 + idot; - passf4(idot, l1, cinput, coutput, &wa[iw], &wa[ix2], &wa[ix3], isign); - na = !na; - break; - case 2: - passf2(idot, l1, cinput, coutput, &wa[iw], isign); - na = !na; - break; - case 3: - ix2 = iw + idot; - passf3(idot, l1, cinput, coutput, &wa[iw], &wa[ix2], isign); - na = !na; - break; - case 5: - ix2 = iw + idot; - ix3 = ix2 + idot; - ix4 = ix3 + idot; - passf5(idot, l1, cinput, coutput, &wa[iw], &wa[ix2], &wa[ix3], &wa[ix4], isign); - na = !na; - break; - default: - passf(&nac, idot, ip, l1, idl1, cinput, coutput, &wa[iw], isign); - if (nac != 0) na = !na; - } - l1 = l2; - iw += (ip - 1)*idot; - } - if (na == 0) return; - for (i=0; i<2*n; i++) c[i] = ch[i]; - } /* cfftf1 */ - - -NPY_VISIBILITY_HIDDEN void npy_cfftf(int n, Treal c[], Treal wsave[]) - { - int iw1, iw2; - if (n == 1) return; - iw1 = 2*n; - iw2 = iw1 + 2*n; - cfftf1(n, c, wsave, wsave+iw1, (int*)(wsave+iw2), -1); - } /* npy_cfftf */ - - -NPY_VISIBILITY_HIDDEN void npy_cfftb(int n, Treal c[], Treal wsave[]) - { - int iw1, iw2; - if (n == 1) return; - iw1 = 2*n; - iw2 = iw1 + 2*n; - cfftf1(n, c, wsave, wsave+iw1, (int*)(wsave+iw2), +1); - } /* npy_cfftb */ - - -static void factorize(int n, int ifac[MAXFAC+2], const int ntryh[NSPECIAL]) - /* Factorize n in factors in ntryh and rest. On exit, -ifac[0] contains n and ifac[1] contains number of factors, -the factors start from ifac[2]. */ - { - int ntry=3, i, j=0, ib, nf=0, nl=n, nq, nr; -startloop: - if (j < NSPECIAL) - ntry = ntryh[j]; - else - ntry+= 2; - j++; - do { - nq = nl / ntry; - nr = nl - ntry*nq; - if (nr != 0) goto startloop; - nf++; - ifac[nf + 1] = ntry; - nl = nq; - if (ntry == 2 && nf != 1) { - for (i=2; i<=nf; i++) { - ib = nf - i + 2; - ifac[ib + 1] = ifac[ib]; - } - ifac[2] = 2; - } - } while (nl != 1); - ifac[0] = n; - ifac[1] = nf; - } - - -static void cffti1(int n, Treal wa[], int ifac[MAXFAC+2]) - { - int fi, idot, i, j; - int i1, k1, l1, l2; - int ld, ii, nf, ip; - int ido, ipm; - - static const int ntryh[NSPECIAL] = { - 3,4,2,5 }; /* Do not change the order of these. */ - - factorize(n,ifac,ntryh); - nf = ifac[1]; - i = 1; - l1 = 1; - for (k1=1; k1<=nf; k1++) { - ip = ifac[k1+1]; - ld = 0; - l2 = l1*ip; - ido = n / l2; - idot = ido + ido + 2; - ipm = ip - 1; - for (j=1; j<=ipm; j++) { - i1 = i; - wa[i-1] = 1; - wa[i] = 0; - ld += l1; - fi = 0; - for (ii=4; ii<=idot; ii+=2) { - i+= 2; - fi+= 1; - sincos2pi(fi*ld, n, wa+i, wa+i-1); - } - if (ip > 5) { - wa[i1-1] = wa[i-1]; - wa[i1] = wa[i]; - } - } - l1 = l2; - } - } /* cffti1 */ - - -NPY_VISIBILITY_HIDDEN void npy_cffti(int n, Treal wsave[]) - { - int iw1, iw2; - if (n == 1) return; - iw1 = 2*n; - iw2 = iw1 + 2*n; - cffti1(n, wsave+iw1, (int*)(wsave+iw2)); - } /* npy_cffti */ - - /* ------------------------------------------------------------------- -rfftf1, rfftb1, npy_rfftf, npy_rfftb, rffti1, npy_rffti. Treal FFTs. ----------------------------------------------------------------------- */ - -static void rfftf1(int n, Treal c[], Treal ch[], const Treal wa[], const int ifac[MAXFAC+2]) - { - int i; - int k1, l1, l2, na, kh, nf, ip, iw, ix2, ix3, ix4, ido, idl1; - Treal *cinput, *coutput; - nf = ifac[1]; - na = 1; - l2 = n; - iw = n-1; - for (k1 = 1; k1 <= nf; ++k1) { - kh = nf - k1; - ip = ifac[kh + 2]; - l1 = l2 / ip; - ido = n / l2; - idl1 = ido*l1; - iw -= (ip - 1)*ido; - na = !na; - if (na) { - cinput = ch; - coutput = c; - } else { - cinput = c; - coutput = ch; - } - switch (ip) { - case 4: - ix2 = iw + ido; - ix3 = ix2 + ido; - radf4(ido, l1, cinput, coutput, &wa[iw], &wa[ix2], &wa[ix3]); - break; - case 2: - radf2(ido, l1, cinput, coutput, &wa[iw]); - break; - case 3: - ix2 = iw + ido; - radf3(ido, l1, cinput, coutput, &wa[iw], &wa[ix2]); - break; - case 5: - ix2 = iw + ido; - ix3 = ix2 + ido; - ix4 = ix3 + ido; - radf5(ido, l1, cinput, coutput, &wa[iw], &wa[ix2], &wa[ix3], &wa[ix4]); - break; - default: - if (ido == 1) - na = !na; - if (na == 0) { - radfg(ido, ip, l1, idl1, c, ch, &wa[iw]); - na = 1; - } else { - radfg(ido, ip, l1, idl1, ch, c, &wa[iw]); - na = 0; - } - } - l2 = l1; - } - if (na == 1) return; - for (i = 0; i < n; i++) c[i] = ch[i]; - } /* rfftf1 */ - - -static void rfftb1(int n, Treal c[], Treal ch[], const Treal wa[], const int ifac[MAXFAC+2]) - { - int i; - int k1, l1, l2, na, nf, ip, iw, ix2, ix3, ix4, ido, idl1; - Treal *cinput, *coutput; - nf = ifac[1]; - na = 0; - l1 = 1; - iw = 0; - for (k1=1; k1<=nf; k1++) { - ip = ifac[k1 + 1]; - l2 = ip*l1; - ido = n / l2; - idl1 = ido*l1; - if (na) { - cinput = ch; - coutput = c; - } else { - cinput = c; - coutput = ch; - } - switch (ip) { - case 4: - ix2 = iw + ido; - ix3 = ix2 + ido; - radb4(ido, l1, cinput, coutput, &wa[iw], &wa[ix2], &wa[ix3]); - na = !na; - break; - case 2: - radb2(ido, l1, cinput, coutput, &wa[iw]); - na = !na; - break; - case 3: - ix2 = iw + ido; - radb3(ido, l1, cinput, coutput, &wa[iw], &wa[ix2]); - na = !na; - break; - case 5: - ix2 = iw + ido; - ix3 = ix2 + ido; - ix4 = ix3 + ido; - radb5(ido, l1, cinput, coutput, &wa[iw], &wa[ix2], &wa[ix3], &wa[ix4]); - na = !na; - break; - default: - radbg(ido, ip, l1, idl1, cinput, coutput, &wa[iw]); - if (ido == 1) na = !na; - } - l1 = l2; - iw += (ip - 1)*ido; - } - if (na == 0) return; - for (i=0; i<n; i++) c[i] = ch[i]; - } /* rfftb1 */ - - -NPY_VISIBILITY_HIDDEN void npy_rfftf(int n, Treal r[], Treal wsave[]) - { - if (n == 1) return; - rfftf1(n, r, wsave, wsave+n, (int*)(wsave+2*n)); - } /* npy_rfftf */ - - -NPY_VISIBILITY_HIDDEN void npy_rfftb(int n, Treal r[], Treal wsave[]) - { - if (n == 1) return; - rfftb1(n, r, wsave, wsave+n, (int*)(wsave+2*n)); - } /* npy_rfftb */ - - -static void rffti1(int n, Treal wa[], int ifac[MAXFAC+2]) - { - int fi, i, j; - int k1, l1, l2; - int ld, ii, nf, ip, is; - int ido, ipm, nfm1; - static const int ntryh[NSPECIAL] = { - 4,2,3,5 }; /* Do not change the order of these. */ - factorize(n,ifac,ntryh); - nf = ifac[1]; - is = 0; - nfm1 = nf - 1; - l1 = 1; - if (nfm1 == 0) return; - for (k1 = 1; k1 <= nfm1; k1++) { - ip = ifac[k1 + 1]; - ld = 0; - l2 = l1*ip; - ido = n / l2; - ipm = ip - 1; - for (j = 1; j <= ipm; ++j) { - ld += l1; - i = is; - fi = 0; - for (ii = 3; ii <= ido; ii += 2) { - i += 2; - fi += 1; - sincos2pi(fi*ld, n, wa+i-1, wa+i-2); - } - is += ido; - } - l1 = l2; - } - } /* rffti1 */ - - -NPY_VISIBILITY_HIDDEN void npy_rffti(int n, Treal wsave[]) - { - if (n == 1) return; - rffti1(n, wsave+n, (int*)(wsave+2*n)); - } /* npy_rffti */ - -#ifdef __cplusplus -} -#endif diff --git a/numpy/fft/fftpack.h b/numpy/fft/fftpack.h deleted file mode 100644 index 5e8f4631c8ee..000000000000 --- a/numpy/fft/fftpack.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * This file is part of tela the Tensor Language. - * Copyright (c) 1994-1995 Pekka Janhunen - */ - -#ifdef __cplusplus -extern "C" { -#endif - -#define DOUBLE - -#ifdef DOUBLE -#define Treal double -#else -#define Treal float -#endif - -extern NPY_VISIBILITY_HIDDEN void npy_cfftf(int N, Treal data[], const Treal wrk[]); -extern NPY_VISIBILITY_HIDDEN void npy_cfftb(int N, Treal data[], const Treal wrk[]); -extern NPY_VISIBILITY_HIDDEN void npy_cffti(int N, Treal wrk[]); - -extern NPY_VISIBILITY_HIDDEN void npy_rfftf(int N, Treal data[], const Treal wrk[]); -extern NPY_VISIBILITY_HIDDEN void npy_rfftb(int N, Treal data[], const Treal wrk[]); -extern NPY_VISIBILITY_HIDDEN void npy_rffti(int N, Treal wrk[]); - -#ifdef __cplusplus -} -#endif diff --git a/numpy/fft/fftpack_litemodule.c b/numpy/fft/fftpack_litemodule.c deleted file mode 100644 index bd6cfc120b13..000000000000 --- a/numpy/fft/fftpack_litemodule.c +++ /dev/null @@ -1,366 +0,0 @@ -#define NPY_NO_DEPRECATED_API NPY_API_VERSION - -#include "Python.h" -#include "numpy/arrayobject.h" -#include "fftpack.h" - -static PyObject *ErrorObject; - -static const char fftpack_cfftf__doc__[] = ""; - -static PyObject * -fftpack_cfftf(PyObject *NPY_UNUSED(self), PyObject *args) -{ - PyObject *op1, *op2; - PyArrayObject *data; - PyArray_Descr *descr; - double *wsave, *dptr; - npy_intp nsave; - int npts, nrepeats, i; - - if(!PyArg_ParseTuple(args, "OO:cfftf", &op1, &op2)) { - return NULL; - } - data = (PyArrayObject *)PyArray_CopyFromObject(op1, - NPY_CDOUBLE, 1, 0); - if (data == NULL) { - return NULL; - } - descr = PyArray_DescrFromType(NPY_DOUBLE); - if (PyArray_AsCArray(&op2, (void *)&wsave, &nsave, 1, descr) == -1) { - goto fail; - } - if (data == NULL) { - goto fail; - } - - npts = PyArray_DIM(data, PyArray_NDIM(data) - 1); - if (nsave != npts*4 + 15) { - PyErr_SetString(ErrorObject, "invalid work array for fft size"); - goto fail; - } - - nrepeats = PyArray_SIZE(data)/npts; - dptr = (double *)PyArray_DATA(data); - Py_BEGIN_ALLOW_THREADS; - NPY_SIGINT_ON; - for (i = 0; i < nrepeats; i++) { - npy_cfftf(npts, dptr, wsave); - dptr += npts*2; - } - NPY_SIGINT_OFF; - Py_END_ALLOW_THREADS; - PyArray_Free(op2, (char *)wsave); - return (PyObject *)data; - -fail: - PyArray_Free(op2, (char *)wsave); - Py_DECREF(data); - return NULL; -} - -static const char fftpack_cfftb__doc__[] = ""; - -static PyObject * -fftpack_cfftb(PyObject *NPY_UNUSED(self), PyObject *args) -{ - PyObject *op1, *op2; - PyArrayObject *data; - PyArray_Descr *descr; - double *wsave, *dptr; - npy_intp nsave; - int npts, nrepeats, i; - - if(!PyArg_ParseTuple(args, "OO:cfftb", &op1, &op2)) { - return NULL; - } - data = (PyArrayObject *)PyArray_CopyFromObject(op1, - NPY_CDOUBLE, 1, 0); - if (data == NULL) { - return NULL; - } - descr = PyArray_DescrFromType(NPY_DOUBLE); - if (PyArray_AsCArray(&op2, (void *)&wsave, &nsave, 1, descr) == -1) { - goto fail; - } - if (data == NULL) { - goto fail; - } - - npts = PyArray_DIM(data, PyArray_NDIM(data) - 1); - if (nsave != npts*4 + 15) { - PyErr_SetString(ErrorObject, "invalid work array for fft size"); - goto fail; - } - - nrepeats = PyArray_SIZE(data)/npts; - dptr = (double *)PyArray_DATA(data); - Py_BEGIN_ALLOW_THREADS; - NPY_SIGINT_ON; - for (i = 0; i < nrepeats; i++) { - npy_cfftb(npts, dptr, wsave); - dptr += npts*2; - } - NPY_SIGINT_OFF; - Py_END_ALLOW_THREADS; - PyArray_Free(op2, (char *)wsave); - return (PyObject *)data; - -fail: - PyArray_Free(op2, (char *)wsave); - Py_DECREF(data); - return NULL; -} - -static const char fftpack_cffti__doc__[] = ""; - -static PyObject * -fftpack_cffti(PyObject *NPY_UNUSED(self), PyObject *args) -{ - PyArrayObject *op; - npy_intp dim; - long n; - - if (!PyArg_ParseTuple(args, "l:cffti", &n)) { - return NULL; - } - /*Magic size needed by npy_cffti*/ - dim = 4*n + 15; - /*Create a 1 dimensional array of dimensions of type double*/ - op = (PyArrayObject *)PyArray_SimpleNew(1, &dim, NPY_DOUBLE); - if (op == NULL) { - return NULL; - } - - Py_BEGIN_ALLOW_THREADS; - NPY_SIGINT_ON; - npy_cffti(n, (double *)PyArray_DATA((PyArrayObject*)op)); - NPY_SIGINT_OFF; - Py_END_ALLOW_THREADS; - - return (PyObject *)op; -} - -static const char fftpack_rfftf__doc__[] = ""; - -static PyObject * -fftpack_rfftf(PyObject *NPY_UNUSED(self), PyObject *args) -{ - PyObject *op1, *op2; - PyArrayObject *data, *ret; - PyArray_Descr *descr; - double *wsave = NULL, *dptr, *rptr; - npy_intp nsave; - int npts, nrepeats, i, rstep; - - if(!PyArg_ParseTuple(args, "OO:rfftf", &op1, &op2)) { - return NULL; - } - data = (PyArrayObject *)PyArray_ContiguousFromObject(op1, - NPY_DOUBLE, 1, 0); - if (data == NULL) { - return NULL; - } - /* FIXME, direct access changing contents of data->dimensions */ - npts = PyArray_DIM(data, PyArray_NDIM(data) - 1); - PyArray_DIMS(data)[PyArray_NDIM(data) - 1] = npts/2 + 1; - ret = (PyArrayObject *)PyArray_Zeros(PyArray_NDIM(data), - PyArray_DIMS(data), PyArray_DescrFromType(NPY_CDOUBLE), 0); - if (ret == NULL) { - goto fail; - } - PyArray_DIMS(data)[PyArray_NDIM(data) - 1] = npts; - rstep = PyArray_DIM(ret, PyArray_NDIM(ret) - 1)*2; - - descr = PyArray_DescrFromType(NPY_DOUBLE); - if (PyArray_AsCArray(&op2, (void *)&wsave, &nsave, 1, descr) == -1) { - goto fail; - } - if (data == NULL || ret == NULL) { - goto fail; - } - if (nsave != npts*2+15) { - PyErr_SetString(ErrorObject, "invalid work array for fft size"); - goto fail; - } - - nrepeats = PyArray_SIZE(data)/npts; - rptr = (double *)PyArray_DATA(ret); - dptr = (double *)PyArray_DATA(data); - - Py_BEGIN_ALLOW_THREADS; - NPY_SIGINT_ON; - for (i = 0; i < nrepeats; i++) { - memcpy((char *)(rptr+1), dptr, npts*sizeof(double)); - npy_rfftf(npts, rptr+1, wsave); - rptr[0] = rptr[1]; - rptr[1] = 0.0; - rptr += rstep; - dptr += npts; - } - NPY_SIGINT_OFF; - Py_END_ALLOW_THREADS; - PyArray_Free(op2, (char *)wsave); - Py_DECREF(data); - return (PyObject *)ret; - -fail: - PyArray_Free(op2, (char *)wsave); - Py_XDECREF(data); - Py_XDECREF(ret); - return NULL; -} - -static const char fftpack_rfftb__doc__[] = ""; - -static PyObject * -fftpack_rfftb(PyObject *NPY_UNUSED(self), PyObject *args) -{ - PyObject *op1, *op2; - PyArrayObject *data, *ret; - PyArray_Descr *descr; - double *wsave, *dptr, *rptr; - npy_intp nsave; - int npts, nrepeats, i; - - if(!PyArg_ParseTuple(args, "OO:rfftb", &op1, &op2)) { - return NULL; - } - data = (PyArrayObject *)PyArray_ContiguousFromObject(op1, - NPY_CDOUBLE, 1, 0); - if (data == NULL) { - return NULL; - } - npts = PyArray_DIM(data, PyArray_NDIM(data) - 1); - ret = (PyArrayObject *)PyArray_Zeros(PyArray_NDIM(data), PyArray_DIMS(data), - PyArray_DescrFromType(NPY_DOUBLE), 0); - - descr = PyArray_DescrFromType(NPY_DOUBLE); - if (PyArray_AsCArray(&op2, (void *)&wsave, &nsave, 1, descr) == -1) { - goto fail; - } - if (data == NULL || ret == NULL) { - goto fail; - } - if (nsave != npts*2 + 15) { - PyErr_SetString(ErrorObject, "invalid work array for fft size"); - goto fail; - } - - nrepeats = PyArray_SIZE(ret)/npts; - rptr = (double *)PyArray_DATA(ret); - dptr = (double *)PyArray_DATA(data); - - Py_BEGIN_ALLOW_THREADS; - NPY_SIGINT_ON; - for (i = 0; i < nrepeats; i++) { - memcpy((char *)(rptr + 1), (dptr + 2), (npts - 1)*sizeof(double)); - rptr[0] = dptr[0]; - npy_rfftb(npts, rptr, wsave); - rptr += npts; - dptr += npts*2; - } - NPY_SIGINT_OFF; - Py_END_ALLOW_THREADS; - PyArray_Free(op2, (char *)wsave); - Py_DECREF(data); - return (PyObject *)ret; - -fail: - PyArray_Free(op2, (char *)wsave); - Py_XDECREF(data); - Py_XDECREF(ret); - return NULL; -} - -static const char fftpack_rffti__doc__[] = ""; - -static PyObject * -fftpack_rffti(PyObject *NPY_UNUSED(self), PyObject *args) -{ - PyArrayObject *op; - npy_intp dim; - long n; - - if (!PyArg_ParseTuple(args, "l:rffti", &n)) { - return NULL; - } - /*Magic size needed by npy_rffti*/ - dim = 2*n + 15; - /*Create a 1 dimensional array of dimensions of type double*/ - op = (PyArrayObject *)PyArray_SimpleNew(1, &dim, NPY_DOUBLE); - if (op == NULL) { - return NULL; - } - Py_BEGIN_ALLOW_THREADS; - NPY_SIGINT_ON; - npy_rffti(n, (double *)PyArray_DATA((PyArrayObject*)op)); - NPY_SIGINT_OFF; - Py_END_ALLOW_THREADS; - - return (PyObject *)op; -} - - -/* List of methods defined in the module */ - -static struct PyMethodDef fftpack_methods[] = { - {"cfftf", fftpack_cfftf, 1, fftpack_cfftf__doc__}, - {"cfftb", fftpack_cfftb, 1, fftpack_cfftb__doc__}, - {"cffti", fftpack_cffti, 1, fftpack_cffti__doc__}, - {"rfftf", fftpack_rfftf, 1, fftpack_rfftf__doc__}, - {"rfftb", fftpack_rfftb, 1, fftpack_rfftb__doc__}, - {"rffti", fftpack_rffti, 1, fftpack_rffti__doc__}, - {NULL, NULL, 0, NULL} /* sentinel */ -}; - -#if PY_MAJOR_VERSION >= 3 -static struct PyModuleDef moduledef = { - PyModuleDef_HEAD_INIT, - "fftpack_lite", - NULL, - -1, - fftpack_methods, - NULL, - NULL, - NULL, - NULL -}; -#endif - -/* Initialization function for the module */ -#if PY_MAJOR_VERSION >= 3 -#define RETVAL(x) x -PyMODINIT_FUNC PyInit_fftpack_lite(void) -#else -#define RETVAL(x) -PyMODINIT_FUNC -initfftpack_lite(void) -#endif -{ - PyObject *m,*d; -#if PY_MAJOR_VERSION >= 3 - m = PyModule_Create(&moduledef); -#else - static const char fftpack_module_documentation[] = ""; - - m = Py_InitModule4("fftpack_lite", fftpack_methods, - fftpack_module_documentation, - (PyObject*)NULL,PYTHON_API_VERSION); -#endif - if (m == NULL) { - return RETVAL(NULL); - } - - /* Import the array object */ - import_array(); - - /* Add some symbolic constants to the module */ - d = PyModule_GetDict(m); - ErrorObject = PyErr_NewException("fftpack.error", NULL, NULL); - PyDict_SetItemString(d, "error", ErrorObject); - - /* XXXX Add constants here */ - - return RETVAL(m); -} diff --git a/numpy/fft/helper.py b/numpy/fft/helper.py index 729121f31981..927ee1af1622 100644 --- a/numpy/fft/helper.py +++ b/numpy/fft/helper.py @@ -2,23 +2,21 @@ Discrete Fourier Transforms - helper.py """ -from __future__ import division, absolute_import, print_function - -import collections -try: - import threading -except ImportError: - import dummy_threading as threading -from numpy.compat import integer_types from numpy.core import integer, empty, arange, asarray, roll +from numpy.core.overrides import array_function_dispatch, set_module # Created by Pearu Peterson, September 2002 __all__ = ['fftshift', 'ifftshift', 'fftfreq', 'rfftfreq'] -integer_types = integer_types + (integer,) +integer_types = (int, integer) +def _fftshift_dispatcher(x, axes=None): + return (x,) + + +@array_function_dispatch(_fftshift_dispatcher, module='numpy.fft') def fftshift(x, axes=None): """ Shift the zero-frequency component to the center of the spectrum. @@ -46,7 +44,7 @@ def fftshift(x, axes=None): -------- >>> freqs = np.fft.fftfreq(10, 0.1) >>> freqs - array([ 0., 1., 2., 3., 4., -5., -4., -3., -2., -1.]) + array([ 0., 1., 2., ..., -3., -2., -1.]) >>> np.fft.fftshift(freqs) array([-5., -4., -3., -2., -1., 0., 1., 2., 3., 4.]) @@ -75,6 +73,7 @@ def fftshift(x, axes=None): return roll(x, shift, axes) +@array_function_dispatch(_fftshift_dispatcher, module='numpy.fft') def ifftshift(x, axes=None): """ The inverse of `fftshift`. Although identical for even-length `x`, the @@ -121,6 +120,7 @@ def ifftshift(x, axes=None): return roll(x, shift, axes) +@set_module('numpy.fft') def fftfreq(n, d=1.0): """ Return the Discrete Fourier Transform sample frequencies. @@ -154,7 +154,7 @@ def fftfreq(n, d=1.0): >>> timestep = 0.1 >>> freq = np.fft.fftfreq(n, d=timestep) >>> freq - array([ 0. , 1.25, 2.5 , 3.75, -5. , -3.75, -2.5 , -1.25]) + array([ 0. , 1.25, 2.5 , ..., -3.75, -2.5 , -1.25]) """ if not isinstance(n, integer_types): @@ -167,9 +167,9 @@ def fftfreq(n, d=1.0): p2 = arange(-(n//2), 0, dtype=int) results[N:] = p2 return results * val - #return hstack((arange(0,(n-1)/2 + 1), arange(-(n/2),0))) / (n*d) +@set_module('numpy.fft') def rfftfreq(n, d=1.0): """ Return the Discrete Fourier Transform sample frequencies @@ -207,7 +207,7 @@ def rfftfreq(n, d=1.0): >>> sample_rate = 100 >>> freq = np.fft.fftfreq(n, d=1./sample_rate) >>> freq - array([ 0., 10., 20., 30., 40., -50., -40., -30., -20., -10.]) + array([ 0., 10., 20., ..., -30., -20., -10.]) >>> freq = np.fft.rfftfreq(n, d=1./sample_rate) >>> freq array([ 0., 10., 20., 30., 40., 50.]) @@ -219,99 +219,3 @@ def rfftfreq(n, d=1.0): N = n//2 + 1 results = arange(0, N, dtype=int) return results * val - - -class _FFTCache(object): - """ - Cache for the FFT twiddle factors as an LRU (least recently used) cache. - - Parameters - ---------- - max_size_in_mb : int - Maximum memory usage of the cache before items are being evicted. - max_item_count : int - Maximum item count of the cache before items are being evicted. - - Notes - ----- - Items will be evicted if either limit has been reached upon getting and - setting. The maximum memory usages is not strictly the given - ``max_size_in_mb`` but rather - ``max(max_size_in_mb, 1.5 * size_of_largest_item)``. Thus the cache will - never be completely cleared - at least one item will remain and a single - large item can cause the cache to retain several smaller items even if the - given maximum cache size has been exceeded. - """ - def __init__(self, max_size_in_mb, max_item_count): - self._max_size_in_bytes = max_size_in_mb * 1024 ** 2 - self._max_item_count = max_item_count - self._dict = collections.OrderedDict() - self._lock = threading.Lock() - - def put_twiddle_factors(self, n, factors): - """ - Store twiddle factors for an FFT of length n in the cache. - - Putting multiple twiddle factors for a certain n will store it multiple - times. - - Parameters - ---------- - n : int - Data length for the FFT. - factors : ndarray - The actual twiddle values. - """ - with self._lock: - # Pop + later add to move it to the end for LRU behavior. - # Internally everything is stored in a dictionary whose values are - # lists. - try: - value = self._dict.pop(n) - except KeyError: - value = [] - value.append(factors) - self._dict[n] = value - self._prune_cache() - - def pop_twiddle_factors(self, n): - """ - Pop twiddle factors for an FFT of length n from the cache. - - Will return None if the requested twiddle factors are not available in - the cache. - - Parameters - ---------- - n : int - Data length for the FFT. - - Returns - ------- - out : ndarray or None - The retrieved twiddle factors if available, else None. - """ - with self._lock: - if n not in self._dict or not self._dict[n]: - return None - # Pop + later add to move it to the end for LRU behavior. - all_values = self._dict.pop(n) - value = all_values.pop() - # Only put pack if there are still some arrays left in the list. - if all_values: - self._dict[n] = all_values - return value - - def _prune_cache(self): - # Always keep at least one item. - while len(self._dict) > 1 and ( - len(self._dict) > self._max_item_count or self._check_size()): - self._dict.popitem(last=False) - - def _check_size(self): - item_sizes = [sum(_j.nbytes for _j in _i) - for _i in self._dict.values() if _i] - if not item_sizes: - return False - max_size = max(self._max_size_in_bytes, 1.5 * max(item_sizes)) - return sum(item_sizes) > max_size diff --git a/numpy/fft/helper.pyi b/numpy/fft/helper.pyi new file mode 100644 index 000000000000..d75826f4e03e --- /dev/null +++ b/numpy/fft/helper.pyi @@ -0,0 +1,50 @@ +from typing import List, Any, TypeVar, overload + +from numpy import generic, dtype, integer, floating, complexfloating +from numpy.typing import ( + NDArray, + ArrayLike, + _ShapeLike, + _SupportsArray, + _FiniteNestedSequence, + _ArrayLikeFloat_co, + _ArrayLikeComplex_co, +) + +_SCT = TypeVar("_SCT", bound=generic) + +_ArrayLike = _FiniteNestedSequence[_SupportsArray[dtype[_SCT]]] + +__all__: List[str] + +@overload +def fftshift(x: _ArrayLike[_SCT], axes: None | _ShapeLike = ...) -> NDArray[_SCT]: ... +@overload +def fftshift(x: ArrayLike, axes: None | _ShapeLike = ...) -> NDArray[Any]: ... + +@overload +def ifftshift(x: _ArrayLike[_SCT], axes: None | _ShapeLike = ...) -> NDArray[_SCT]: ... +@overload +def ifftshift(x: ArrayLike, axes: None | _ShapeLike = ...) -> NDArray[Any]: ... + +@overload +def fftfreq( + n: int | integer[Any], + d: _ArrayLikeFloat_co, +) -> NDArray[floating[Any]]: ... +@overload +def fftfreq( + n: int | integer[Any], + d: _ArrayLikeComplex_co, +) -> NDArray[complexfloating[Any, Any]]: ... + +@overload +def rfftfreq( + n: int | integer[Any], + d: _ArrayLikeFloat_co, +) -> NDArray[floating[Any]]: ... +@overload +def rfftfreq( + n: int | integer[Any], + d: _ArrayLikeComplex_co, +) -> NDArray[complexfloating[Any, Any]]: ... diff --git a/numpy/fft/info.py b/numpy/fft/info.py deleted file mode 100644 index cb6526b447c4..000000000000 --- a/numpy/fft/info.py +++ /dev/null @@ -1,187 +0,0 @@ -""" -Discrete Fourier Transform (:mod:`numpy.fft`) -============================================= - -.. currentmodule:: numpy.fft - -Standard FFTs -------------- - -.. autosummary:: - :toctree: generated/ - - fft Discrete Fourier transform. - ifft Inverse discrete Fourier transform. - fft2 Discrete Fourier transform in two dimensions. - ifft2 Inverse discrete Fourier transform in two dimensions. - fftn Discrete Fourier transform in N-dimensions. - ifftn Inverse discrete Fourier transform in N dimensions. - -Real FFTs ---------- - -.. autosummary:: - :toctree: generated/ - - rfft Real discrete Fourier transform. - irfft Inverse real discrete Fourier transform. - rfft2 Real discrete Fourier transform in two dimensions. - irfft2 Inverse real discrete Fourier transform in two dimensions. - rfftn Real discrete Fourier transform in N dimensions. - irfftn Inverse real discrete Fourier transform in N dimensions. - -Hermitian FFTs --------------- - -.. autosummary:: - :toctree: generated/ - - hfft Hermitian discrete Fourier transform. - ihfft Inverse Hermitian discrete Fourier transform. - -Helper routines ---------------- - -.. autosummary:: - :toctree: generated/ - - fftfreq Discrete Fourier Transform sample frequencies. - rfftfreq DFT sample frequencies (for usage with rfft, irfft). - fftshift Shift zero-frequency component to center of spectrum. - ifftshift Inverse of fftshift. - - -Background information ----------------------- - -Fourier analysis is fundamentally a method for expressing a function as a -sum of periodic components, and for recovering the function from those -components. When both the function and its Fourier transform are -replaced with discretized counterparts, it is called the discrete Fourier -transform (DFT). The DFT has become a mainstay of numerical computing in -part because of a very fast algorithm for computing it, called the Fast -Fourier Transform (FFT), which was known to Gauss (1805) and was brought -to light in its current form by Cooley and Tukey [CT]_. Press et al. [NR]_ -provide an accessible introduction to Fourier analysis and its -applications. - -Because the discrete Fourier transform separates its input into -components that contribute at discrete frequencies, it has a great number -of applications in digital signal processing, e.g., for filtering, and in -this context the discretized input to the transform is customarily -referred to as a *signal*, which exists in the *time domain*. The output -is called a *spectrum* or *transform* and exists in the *frequency -domain*. - -Implementation details ----------------------- - -There are many ways to define the DFT, varying in the sign of the -exponent, normalization, etc. In this implementation, the DFT is defined -as - -.. math:: - A_k = \\sum_{m=0}^{n-1} a_m \\exp\\left\\{-2\\pi i{mk \\over n}\\right\\} - \\qquad k = 0,\\ldots,n-1. - -The DFT is in general defined for complex inputs and outputs, and a -single-frequency component at linear frequency :math:`f` is -represented by a complex exponential -:math:`a_m = \\exp\\{2\\pi i\\,f m\\Delta t\\}`, where :math:`\\Delta t` -is the sampling interval. - -The values in the result follow so-called "standard" order: If ``A = -fft(a, n)``, then ``A[0]`` contains the zero-frequency term (the sum of -the signal), which is always purely real for real inputs. Then ``A[1:n/2]`` -contains the positive-frequency terms, and ``A[n/2+1:]`` contains the -negative-frequency terms, in order of decreasingly negative frequency. -For an even number of input points, ``A[n/2]`` represents both positive and -negative Nyquist frequency, and is also purely real for real input. For -an odd number of input points, ``A[(n-1)/2]`` contains the largest positive -frequency, while ``A[(n+1)/2]`` contains the largest negative frequency. -The routine ``np.fft.fftfreq(n)`` returns an array giving the frequencies -of corresponding elements in the output. The routine -``np.fft.fftshift(A)`` shifts transforms and their frequencies to put the -zero-frequency components in the middle, and ``np.fft.ifftshift(A)`` undoes -that shift. - -When the input `a` is a time-domain signal and ``A = fft(a)``, ``np.abs(A)`` -is its amplitude spectrum and ``np.abs(A)**2`` is its power spectrum. -The phase spectrum is obtained by ``np.angle(A)``. - -The inverse DFT is defined as - -.. math:: - a_m = \\frac{1}{n}\\sum_{k=0}^{n-1}A_k\\exp\\left\\{2\\pi i{mk\\over n}\\right\\} - \\qquad m = 0,\\ldots,n-1. - -It differs from the forward transform by the sign of the exponential -argument and the default normalization by :math:`1/n`. - -Normalization -------------- -The default normalization has the direct transforms unscaled and the inverse -transforms are scaled by :math:`1/n`. It is possible to obtain unitary -transforms by setting the keyword argument ``norm`` to ``"ortho"`` (default is -`None`) so that both direct and inverse transforms will be scaled by -:math:`1/\\sqrt{n}`. - -Real and Hermitian transforms ------------------------------ - -When the input is purely real, its transform is Hermitian, i.e., the -component at frequency :math:`f_k` is the complex conjugate of the -component at frequency :math:`-f_k`, which means that for real -inputs there is no information in the negative frequency components that -is not already available from the positive frequency components. -The family of `rfft` functions is -designed to operate on real inputs, and exploits this symmetry by -computing only the positive frequency components, up to and including the -Nyquist frequency. Thus, ``n`` input points produce ``n/2+1`` complex -output points. The inverses of this family assumes the same symmetry of -its input, and for an output of ``n`` points uses ``n/2+1`` input points. - -Correspondingly, when the spectrum is purely real, the signal is -Hermitian. The `hfft` family of functions exploits this symmetry by -using ``n/2+1`` complex points in the input (time) domain for ``n`` real -points in the frequency domain. - -In higher dimensions, FFTs are used, e.g., for image analysis and -filtering. The computational efficiency of the FFT means that it can -also be a faster way to compute large convolutions, using the property -that a convolution in the time domain is equivalent to a point-by-point -multiplication in the frequency domain. - -Higher dimensions ------------------ - -In two dimensions, the DFT is defined as - -.. math:: - A_{kl} = \\sum_{m=0}^{M-1} \\sum_{n=0}^{N-1} - a_{mn}\\exp\\left\\{-2\\pi i \\left({mk\\over M}+{nl\\over N}\\right)\\right\\} - \\qquad k = 0, \\ldots, M-1;\\quad l = 0, \\ldots, N-1, - -which extends in the obvious way to higher dimensions, and the inverses -in higher dimensions also extend in the same way. - -References ----------- - -.. [CT] Cooley, James W., and John W. Tukey, 1965, "An algorithm for the - machine calculation of complex Fourier series," *Math. Comput.* - 19: 297-301. - -.. [NR] Press, W., Teukolsky, S., Vetterline, W.T., and Flannery, B.P., - 2007, *Numerical Recipes: The Art of Scientific Computing*, ch. - 12-13. Cambridge Univ. Press, Cambridge, UK. - -Examples --------- - -For examples, see the various functions. - -""" -from __future__ import division, absolute_import, print_function - -depends = ['core'] diff --git a/numpy/fft/setup.py b/numpy/fft/setup.py index cd99a82d7b51..477948a0986b 100644 --- a/numpy/fft/setup.py +++ b/numpy/fft/setup.py @@ -1,17 +1,20 @@ -from __future__ import division, print_function - +import sys def configuration(parent_package='',top_path=None): from numpy.distutils.misc_util import Configuration config = Configuration('fft', parent_package, top_path) - config.add_data_dir('tests') + config.add_subpackage('tests') - # Configure fftpack_lite - config.add_extension('fftpack_lite', - sources=['fftpack_litemodule.c', 'fftpack.c'] + # AIX needs to be told to use large file support - at all times + defs = [('_LARGE_FILES', None)] if sys.platform[:3] == "aix" else [] + # Configure pocketfft_internal + config.add_extension('_pocketfft_internal', + sources=['_pocketfft.c'], + define_macros=defs, ) + config.add_data_files('*.pyi') return config if __name__ == '__main__': diff --git a/numpy/fft/tests/test_fftpack.py b/numpy/fft/tests/test_fftpack.py deleted file mode 100644 index 8d6cd84070b6..000000000000 --- a/numpy/fft/tests/test_fftpack.py +++ /dev/null @@ -1,185 +0,0 @@ -from __future__ import division, absolute_import, print_function - -import numpy as np -from numpy.random import random -from numpy.testing import ( - assert_array_almost_equal, assert_array_equal, assert_raises, - ) -import threading -import sys -if sys.version_info[0] >= 3: - import queue -else: - import Queue as queue - - -def fft1(x): - L = len(x) - phase = -2j*np.pi*(np.arange(L)/float(L)) - phase = np.arange(L).reshape(-1, 1) * phase - return np.sum(x*np.exp(phase), axis=1) - - -class TestFFTShift(object): - - def test_fft_n(self): - assert_raises(ValueError, np.fft.fft, [1, 2, 3], 0) - - -class TestFFT1D(object): - - def test_fft(self): - x = random(30) + 1j*random(30) - assert_array_almost_equal(fft1(x), np.fft.fft(x)) - assert_array_almost_equal(fft1(x) / np.sqrt(30), - np.fft.fft(x, norm="ortho")) - - def test_ifft(self): - x = random(30) + 1j*random(30) - assert_array_almost_equal(x, np.fft.ifft(np.fft.fft(x))) - assert_array_almost_equal( - x, np.fft.ifft(np.fft.fft(x, norm="ortho"), norm="ortho")) - - def test_fft2(self): - x = random((30, 20)) + 1j*random((30, 20)) - assert_array_almost_equal(np.fft.fft(np.fft.fft(x, axis=1), axis=0), - np.fft.fft2(x)) - assert_array_almost_equal(np.fft.fft2(x) / np.sqrt(30 * 20), - np.fft.fft2(x, norm="ortho")) - - def test_ifft2(self): - x = random((30, 20)) + 1j*random((30, 20)) - assert_array_almost_equal(np.fft.ifft(np.fft.ifft(x, axis=1), axis=0), - np.fft.ifft2(x)) - assert_array_almost_equal(np.fft.ifft2(x) * np.sqrt(30 * 20), - np.fft.ifft2(x, norm="ortho")) - - def test_fftn(self): - x = random((30, 20, 10)) + 1j*random((30, 20, 10)) - assert_array_almost_equal( - np.fft.fft(np.fft.fft(np.fft.fft(x, axis=2), axis=1), axis=0), - np.fft.fftn(x)) - assert_array_almost_equal(np.fft.fftn(x) / np.sqrt(30 * 20 * 10), - np.fft.fftn(x, norm="ortho")) - - def test_ifftn(self): - x = random((30, 20, 10)) + 1j*random((30, 20, 10)) - assert_array_almost_equal( - np.fft.ifft(np.fft.ifft(np.fft.ifft(x, axis=2), axis=1), axis=0), - np.fft.ifftn(x)) - assert_array_almost_equal(np.fft.ifftn(x) * np.sqrt(30 * 20 * 10), - np.fft.ifftn(x, norm="ortho")) - - def test_rfft(self): - x = random(30) - for n in [x.size, 2*x.size]: - for norm in [None, 'ortho']: - assert_array_almost_equal( - np.fft.fft(x, n=n, norm=norm)[:(n//2 + 1)], - np.fft.rfft(x, n=n, norm=norm)) - assert_array_almost_equal(np.fft.rfft(x, n=n) / np.sqrt(n), - np.fft.rfft(x, n=n, norm="ortho")) - - def test_irfft(self): - x = random(30) - assert_array_almost_equal(x, np.fft.irfft(np.fft.rfft(x))) - assert_array_almost_equal( - x, np.fft.irfft(np.fft.rfft(x, norm="ortho"), norm="ortho")) - - def test_rfft2(self): - x = random((30, 20)) - assert_array_almost_equal(np.fft.fft2(x)[:, :11], np.fft.rfft2(x)) - assert_array_almost_equal(np.fft.rfft2(x) / np.sqrt(30 * 20), - np.fft.rfft2(x, norm="ortho")) - - def test_irfft2(self): - x = random((30, 20)) - assert_array_almost_equal(x, np.fft.irfft2(np.fft.rfft2(x))) - assert_array_almost_equal( - x, np.fft.irfft2(np.fft.rfft2(x, norm="ortho"), norm="ortho")) - - def test_rfftn(self): - x = random((30, 20, 10)) - assert_array_almost_equal(np.fft.fftn(x)[:, :, :6], np.fft.rfftn(x)) - assert_array_almost_equal(np.fft.rfftn(x) / np.sqrt(30 * 20 * 10), - np.fft.rfftn(x, norm="ortho")) - - def test_irfftn(self): - x = random((30, 20, 10)) - assert_array_almost_equal(x, np.fft.irfftn(np.fft.rfftn(x))) - assert_array_almost_equal( - x, np.fft.irfftn(np.fft.rfftn(x, norm="ortho"), norm="ortho")) - - def test_hfft(self): - x = random(14) + 1j*random(14) - x_herm = np.concatenate((random(1), x, random(1))) - x = np.concatenate((x_herm, x[::-1].conj())) - assert_array_almost_equal(np.fft.fft(x), np.fft.hfft(x_herm)) - assert_array_almost_equal(np.fft.hfft(x_herm) / np.sqrt(30), - np.fft.hfft(x_herm, norm="ortho")) - - def test_ihttf(self): - x = random(14) + 1j*random(14) - x_herm = np.concatenate((random(1), x, random(1))) - x = np.concatenate((x_herm, x[::-1].conj())) - assert_array_almost_equal(x_herm, np.fft.ihfft(np.fft.hfft(x_herm))) - assert_array_almost_equal( - x_herm, np.fft.ihfft(np.fft.hfft(x_herm, norm="ortho"), - norm="ortho")) - - def test_all_1d_norm_preserving(self): - # verify that round-trip transforms are norm-preserving - x = random(30) - x_norm = np.linalg.norm(x) - n = x.size * 2 - func_pairs = [(np.fft.fft, np.fft.ifft), - (np.fft.rfft, np.fft.irfft), - # hfft: order so the first function takes x.size samples - # (necessary for comparison to x_norm above) - (np.fft.ihfft, np.fft.hfft), - ] - for forw, back in func_pairs: - for n in [x.size, 2*x.size]: - for norm in [None, 'ortho']: - tmp = forw(x, n=n, norm=norm) - tmp = back(tmp, n=n, norm=norm) - assert_array_almost_equal(x_norm, - np.linalg.norm(tmp)) - -class TestFFTThreadSafe(object): - threads = 16 - input_shape = (800, 200) - - def _test_mtsame(self, func, *args): - def worker(args, q): - q.put(func(*args)) - - q = queue.Queue() - expected = func(*args) - - # Spin off a bunch of threads to call the same function simultaneously - t = [threading.Thread(target=worker, args=(args, q)) - for i in range(self.threads)] - [x.start() for x in t] - - [x.join() for x in t] - # Make sure all threads returned the correct value - for i in range(self.threads): - assert_array_equal(q.get(timeout=5), expected, - 'Function returned wrong value in multithreaded context') - - def test_fft(self): - a = np.ones(self.input_shape) * 1+0j - self._test_mtsame(np.fft.fft, a) - - def test_ifft(self): - a = np.ones(self.input_shape) * 1+0j - self._test_mtsame(np.fft.ifft, a) - - def test_rfft(self): - a = np.ones(self.input_shape) - self._test_mtsame(np.fft.rfft, a) - - def test_irfft(self): - a = np.ones(self.input_shape) * 1+0j - self._test_mtsame(np.fft.irfft, a) diff --git a/numpy/fft/tests/test_helper.py b/numpy/fft/tests/test_helper.py index 8d315fa02020..3fb700bb3d00 100644 --- a/numpy/fft/tests/test_helper.py +++ b/numpy/fft/tests/test_helper.py @@ -3,14 +3,12 @@ Copied from fftpack.helper by Pearu Peterson, October 2005 """ -from __future__ import division, absolute_import, print_function import numpy as np -from numpy.testing import assert_array_almost_equal, assert_equal +from numpy.testing import assert_array_almost_equal from numpy import fft, pi -from numpy.fft.helper import _FFTCache -class TestFFTShift(object): +class TestFFTShift: def test_definition(self): x = [0, 1, 2, 3, 4, -4, -3, -2, -1] @@ -87,7 +85,6 @@ def test_uneven_dims(self): def test_equal_to_original(self): """ Test that the new (>=v1.15) implementation (see #10073) is equal to the original (<=v1.14) """ - from numpy.compat import integer_types from numpy.core import asarray, concatenate, arange, take def original_fftshift(x, axes=None): @@ -96,7 +93,7 @@ def original_fftshift(x, axes=None): ndim = tmp.ndim if axes is None: axes = list(range(ndim)) - elif isinstance(axes, integer_types): + elif isinstance(axes, int): axes = (axes,) y = tmp for k in axes: @@ -112,7 +109,7 @@ def original_ifftshift(x, axes=None): ndim = tmp.ndim if axes is None: axes = list(range(ndim)) - elif isinstance(axes, integer_types): + elif isinstance(axes, int): axes = (axes,) y = tmp for k in axes: @@ -136,7 +133,7 @@ def original_ifftshift(x, axes=None): original_ifftshift(inp, axes_keyword)) -class TestFFTFreq(object): +class TestFFTFreq: def test_definition(self): x = [0, 1, 2, 3, 4, -4, -3, -2, -1] @@ -147,7 +144,7 @@ def test_definition(self): assert_array_almost_equal(10*pi*fft.fftfreq(10, pi), x) -class TestRFFTFreq(object): +class TestRFFTFreq: def test_definition(self): x = [0, 1, 2, 3, 4] @@ -158,7 +155,7 @@ def test_definition(self): assert_array_almost_equal(10*pi*fft.rfftfreq(10, pi), x) -class TestIRFFTN(object): +class TestIRFFTN: def test_not_last_axis_success(self): ar, ai = np.random.random((2, 16, 8, 32)) @@ -168,81 +165,3 @@ def test_not_last_axis_success(self): # Should not raise error fft.irfftn(a, axes=axes) - - -class TestFFTCache(object): - - def test_basic_behaviour(self): - c = _FFTCache(max_size_in_mb=1, max_item_count=4) - - # Put - c.put_twiddle_factors(1, np.ones(2, dtype=np.float32)) - c.put_twiddle_factors(2, np.zeros(2, dtype=np.float32)) - - # Get - assert_array_almost_equal(c.pop_twiddle_factors(1), - np.ones(2, dtype=np.float32)) - assert_array_almost_equal(c.pop_twiddle_factors(2), - np.zeros(2, dtype=np.float32)) - - # Nothing should be left. - assert_equal(len(c._dict), 0) - - # Now put everything in twice so it can be retrieved once and each will - # still have one item left. - for _ in range(2): - c.put_twiddle_factors(1, np.ones(2, dtype=np.float32)) - c.put_twiddle_factors(2, np.zeros(2, dtype=np.float32)) - assert_array_almost_equal(c.pop_twiddle_factors(1), - np.ones(2, dtype=np.float32)) - assert_array_almost_equal(c.pop_twiddle_factors(2), - np.zeros(2, dtype=np.float32)) - assert_equal(len(c._dict), 2) - - def test_automatic_pruning(self): - # That's around 2600 single precision samples. - c = _FFTCache(max_size_in_mb=0.01, max_item_count=4) - - c.put_twiddle_factors(1, np.ones(200, dtype=np.float32)) - c.put_twiddle_factors(2, np.ones(200, dtype=np.float32)) - assert_equal(list(c._dict.keys()), [1, 2]) - - # This is larger than the limit but should still be kept. - c.put_twiddle_factors(3, np.ones(3000, dtype=np.float32)) - assert_equal(list(c._dict.keys()), [1, 2, 3]) - # Add one more. - c.put_twiddle_factors(4, np.ones(3000, dtype=np.float32)) - # The other three should no longer exist. - assert_equal(list(c._dict.keys()), [4]) - - # Now test the max item count pruning. - c = _FFTCache(max_size_in_mb=0.01, max_item_count=2) - c.put_twiddle_factors(2, np.empty(2)) - c.put_twiddle_factors(1, np.empty(2)) - # Can still be accessed. - assert_equal(list(c._dict.keys()), [2, 1]) - - c.put_twiddle_factors(3, np.empty(2)) - # 1 and 3 can still be accessed - c[2] has been touched least recently - # and is thus evicted. - assert_equal(list(c._dict.keys()), [1, 3]) - - # One last test. We will add a single large item that is slightly - # bigger then the cache size. Some small items can still be added. - c = _FFTCache(max_size_in_mb=0.01, max_item_count=5) - c.put_twiddle_factors(1, np.ones(3000, dtype=np.float32)) - c.put_twiddle_factors(2, np.ones(2, dtype=np.float32)) - c.put_twiddle_factors(3, np.ones(2, dtype=np.float32)) - c.put_twiddle_factors(4, np.ones(2, dtype=np.float32)) - assert_equal(list(c._dict.keys()), [1, 2, 3, 4]) - - # One more big item. This time it is 6 smaller ones but they are - # counted as one big item. - for _ in range(6): - c.put_twiddle_factors(5, np.ones(500, dtype=np.float32)) - # '1' no longer in the cache. Rest still in the cache. - assert_equal(list(c._dict.keys()), [2, 3, 4, 5]) - - # Another big item - should now be the only item in the cache. - c.put_twiddle_factors(6, np.ones(4000, dtype=np.float32)) - assert_equal(list(c._dict.keys()), [6]) diff --git a/numpy/fft/tests/test_pocketfft.py b/numpy/fft/tests/test_pocketfft.py new file mode 100644 index 000000000000..392644237e0b --- /dev/null +++ b/numpy/fft/tests/test_pocketfft.py @@ -0,0 +1,307 @@ +import numpy as np +import pytest +from numpy.random import random +from numpy.testing import ( + assert_array_equal, assert_raises, assert_allclose + ) +import threading +import queue + + +def fft1(x): + L = len(x) + phase = -2j * np.pi * (np.arange(L) / L) + phase = np.arange(L).reshape(-1, 1) * phase + return np.sum(x*np.exp(phase), axis=1) + + +class TestFFTShift: + + def test_fft_n(self): + assert_raises(ValueError, np.fft.fft, [1, 2, 3], 0) + + +class TestFFT1D: + + def test_identity(self): + maxlen = 512 + x = random(maxlen) + 1j*random(maxlen) + xr = random(maxlen) + for i in range(1, maxlen): + assert_allclose(np.fft.ifft(np.fft.fft(x[0:i])), x[0:i], + atol=1e-12) + assert_allclose(np.fft.irfft(np.fft.rfft(xr[0:i]), i), + xr[0:i], atol=1e-12) + + def test_fft(self): + x = random(30) + 1j*random(30) + assert_allclose(fft1(x), np.fft.fft(x), atol=1e-6) + assert_allclose(fft1(x), np.fft.fft(x, norm="backward"), atol=1e-6) + assert_allclose(fft1(x) / np.sqrt(30), + np.fft.fft(x, norm="ortho"), atol=1e-6) + assert_allclose(fft1(x) / 30., + np.fft.fft(x, norm="forward"), atol=1e-6) + + @pytest.mark.parametrize('norm', (None, 'backward', 'ortho', 'forward')) + def test_ifft(self, norm): + x = random(30) + 1j*random(30) + assert_allclose( + x, np.fft.ifft(np.fft.fft(x, norm=norm), norm=norm), + atol=1e-6) + # Ensure we get the correct error message + with pytest.raises(ValueError, + match='Invalid number of FFT data points'): + np.fft.ifft([], norm=norm) + + def test_fft2(self): + x = random((30, 20)) + 1j*random((30, 20)) + assert_allclose(np.fft.fft(np.fft.fft(x, axis=1), axis=0), + np.fft.fft2(x), atol=1e-6) + assert_allclose(np.fft.fft2(x), + np.fft.fft2(x, norm="backward"), atol=1e-6) + assert_allclose(np.fft.fft2(x) / np.sqrt(30 * 20), + np.fft.fft2(x, norm="ortho"), atol=1e-6) + assert_allclose(np.fft.fft2(x) / (30. * 20.), + np.fft.fft2(x, norm="forward"), atol=1e-6) + + def test_ifft2(self): + x = random((30, 20)) + 1j*random((30, 20)) + assert_allclose(np.fft.ifft(np.fft.ifft(x, axis=1), axis=0), + np.fft.ifft2(x), atol=1e-6) + assert_allclose(np.fft.ifft2(x), + np.fft.ifft2(x, norm="backward"), atol=1e-6) + assert_allclose(np.fft.ifft2(x) * np.sqrt(30 * 20), + np.fft.ifft2(x, norm="ortho"), atol=1e-6) + assert_allclose(np.fft.ifft2(x) * (30. * 20.), + np.fft.ifft2(x, norm="forward"), atol=1e-6) + + def test_fftn(self): + x = random((30, 20, 10)) + 1j*random((30, 20, 10)) + assert_allclose( + np.fft.fft(np.fft.fft(np.fft.fft(x, axis=2), axis=1), axis=0), + np.fft.fftn(x), atol=1e-6) + assert_allclose(np.fft.fftn(x), + np.fft.fftn(x, norm="backward"), atol=1e-6) + assert_allclose(np.fft.fftn(x) / np.sqrt(30 * 20 * 10), + np.fft.fftn(x, norm="ortho"), atol=1e-6) + assert_allclose(np.fft.fftn(x) / (30. * 20. * 10.), + np.fft.fftn(x, norm="forward"), atol=1e-6) + + def test_ifftn(self): + x = random((30, 20, 10)) + 1j*random((30, 20, 10)) + assert_allclose( + np.fft.ifft(np.fft.ifft(np.fft.ifft(x, axis=2), axis=1), axis=0), + np.fft.ifftn(x), atol=1e-6) + assert_allclose(np.fft.ifftn(x), + np.fft.ifftn(x, norm="backward"), atol=1e-6) + assert_allclose(np.fft.ifftn(x) * np.sqrt(30 * 20 * 10), + np.fft.ifftn(x, norm="ortho"), atol=1e-6) + assert_allclose(np.fft.ifftn(x) * (30. * 20. * 10.), + np.fft.ifftn(x, norm="forward"), atol=1e-6) + + def test_rfft(self): + x = random(30) + for n in [x.size, 2*x.size]: + for norm in [None, 'backward', 'ortho', 'forward']: + assert_allclose( + np.fft.fft(x, n=n, norm=norm)[:(n//2 + 1)], + np.fft.rfft(x, n=n, norm=norm), atol=1e-6) + assert_allclose( + np.fft.rfft(x, n=n), + np.fft.rfft(x, n=n, norm="backward"), atol=1e-6) + assert_allclose( + np.fft.rfft(x, n=n) / np.sqrt(n), + np.fft.rfft(x, n=n, norm="ortho"), atol=1e-6) + assert_allclose( + np.fft.rfft(x, n=n) / n, + np.fft.rfft(x, n=n, norm="forward"), atol=1e-6) + + def test_irfft(self): + x = random(30) + assert_allclose(x, np.fft.irfft(np.fft.rfft(x)), atol=1e-6) + assert_allclose(x, np.fft.irfft(np.fft.rfft(x, norm="backward"), + norm="backward"), atol=1e-6) + assert_allclose(x, np.fft.irfft(np.fft.rfft(x, norm="ortho"), + norm="ortho"), atol=1e-6) + assert_allclose(x, np.fft.irfft(np.fft.rfft(x, norm="forward"), + norm="forward"), atol=1e-6) + + def test_rfft2(self): + x = random((30, 20)) + assert_allclose(np.fft.fft2(x)[:, :11], np.fft.rfft2(x), atol=1e-6) + assert_allclose(np.fft.rfft2(x), + np.fft.rfft2(x, norm="backward"), atol=1e-6) + assert_allclose(np.fft.rfft2(x) / np.sqrt(30 * 20), + np.fft.rfft2(x, norm="ortho"), atol=1e-6) + assert_allclose(np.fft.rfft2(x) / (30. * 20.), + np.fft.rfft2(x, norm="forward"), atol=1e-6) + + def test_irfft2(self): + x = random((30, 20)) + assert_allclose(x, np.fft.irfft2(np.fft.rfft2(x)), atol=1e-6) + assert_allclose(x, np.fft.irfft2(np.fft.rfft2(x, norm="backward"), + norm="backward"), atol=1e-6) + assert_allclose(x, np.fft.irfft2(np.fft.rfft2(x, norm="ortho"), + norm="ortho"), atol=1e-6) + assert_allclose(x, np.fft.irfft2(np.fft.rfft2(x, norm="forward"), + norm="forward"), atol=1e-6) + + def test_rfftn(self): + x = random((30, 20, 10)) + assert_allclose(np.fft.fftn(x)[:, :, :6], np.fft.rfftn(x), atol=1e-6) + assert_allclose(np.fft.rfftn(x), + np.fft.rfftn(x, norm="backward"), atol=1e-6) + assert_allclose(np.fft.rfftn(x) / np.sqrt(30 * 20 * 10), + np.fft.rfftn(x, norm="ortho"), atol=1e-6) + assert_allclose(np.fft.rfftn(x) / (30. * 20. * 10.), + np.fft.rfftn(x, norm="forward"), atol=1e-6) + + def test_irfftn(self): + x = random((30, 20, 10)) + assert_allclose(x, np.fft.irfftn(np.fft.rfftn(x)), atol=1e-6) + assert_allclose(x, np.fft.irfftn(np.fft.rfftn(x, norm="backward"), + norm="backward"), atol=1e-6) + assert_allclose(x, np.fft.irfftn(np.fft.rfftn(x, norm="ortho"), + norm="ortho"), atol=1e-6) + assert_allclose(x, np.fft.irfftn(np.fft.rfftn(x, norm="forward"), + norm="forward"), atol=1e-6) + + def test_hfft(self): + x = random(14) + 1j*random(14) + x_herm = np.concatenate((random(1), x, random(1))) + x = np.concatenate((x_herm, x[::-1].conj())) + assert_allclose(np.fft.fft(x), np.fft.hfft(x_herm), atol=1e-6) + assert_allclose(np.fft.hfft(x_herm), + np.fft.hfft(x_herm, norm="backward"), atol=1e-6) + assert_allclose(np.fft.hfft(x_herm) / np.sqrt(30), + np.fft.hfft(x_herm, norm="ortho"), atol=1e-6) + assert_allclose(np.fft.hfft(x_herm) / 30., + np.fft.hfft(x_herm, norm="forward"), atol=1e-6) + + def test_ihfft(self): + x = random(14) + 1j*random(14) + x_herm = np.concatenate((random(1), x, random(1))) + x = np.concatenate((x_herm, x[::-1].conj())) + assert_allclose(x_herm, np.fft.ihfft(np.fft.hfft(x_herm)), atol=1e-6) + assert_allclose(x_herm, np.fft.ihfft(np.fft.hfft(x_herm, + norm="backward"), norm="backward"), atol=1e-6) + assert_allclose(x_herm, np.fft.ihfft(np.fft.hfft(x_herm, + norm="ortho"), norm="ortho"), atol=1e-6) + assert_allclose(x_herm, np.fft.ihfft(np.fft.hfft(x_herm, + norm="forward"), norm="forward"), atol=1e-6) + + @pytest.mark.parametrize("op", [np.fft.fftn, np.fft.ifftn, + np.fft.rfftn, np.fft.irfftn]) + def test_axes(self, op): + x = random((30, 20, 10)) + axes = [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)] + for a in axes: + op_tr = op(np.transpose(x, a)) + tr_op = np.transpose(op(x, axes=a), a) + assert_allclose(op_tr, tr_op, atol=1e-6) + + def test_all_1d_norm_preserving(self): + # verify that round-trip transforms are norm-preserving + x = random(30) + x_norm = np.linalg.norm(x) + n = x.size * 2 + func_pairs = [(np.fft.fft, np.fft.ifft), + (np.fft.rfft, np.fft.irfft), + # hfft: order so the first function takes x.size samples + # (necessary for comparison to x_norm above) + (np.fft.ihfft, np.fft.hfft), + ] + for forw, back in func_pairs: + for n in [x.size, 2*x.size]: + for norm in [None, 'backward', 'ortho', 'forward']: + tmp = forw(x, n=n, norm=norm) + tmp = back(tmp, n=n, norm=norm) + assert_allclose(x_norm, + np.linalg.norm(tmp), atol=1e-6) + + @pytest.mark.parametrize("dtype", [np.half, np.single, np.double, + np.longdouble]) + def test_dtypes(self, dtype): + # make sure that all input precisions are accepted and internally + # converted to 64bit + x = random(30).astype(dtype) + assert_allclose(np.fft.ifft(np.fft.fft(x)), x, atol=1e-6) + assert_allclose(np.fft.irfft(np.fft.rfft(x)), x, atol=1e-6) + + +@pytest.mark.parametrize( + "dtype", + [np.float32, np.float64, np.complex64, np.complex128]) +@pytest.mark.parametrize("order", ["F", 'non-contiguous']) +@pytest.mark.parametrize( + "fft", + [np.fft.fft, np.fft.fft2, np.fft.fftn, + np.fft.ifft, np.fft.ifft2, np.fft.ifftn]) +def test_fft_with_order(dtype, order, fft): + # Check that FFT/IFFT produces identical results for C, Fortran and + # non contiguous arrays + rng = np.random.RandomState(42) + X = rng.rand(8, 7, 13).astype(dtype, copy=False) + # See discussion in pull/14178 + _tol = 8.0 * np.sqrt(np.log2(X.size)) * np.finfo(X.dtype).eps + if order == 'F': + Y = np.asfortranarray(X) + else: + # Make a non contiguous array + Y = X[::-1] + X = np.ascontiguousarray(X[::-1]) + + if fft.__name__.endswith('fft'): + for axis in range(3): + X_res = fft(X, axis=axis) + Y_res = fft(Y, axis=axis) + assert_allclose(X_res, Y_res, atol=_tol, rtol=_tol) + elif fft.__name__.endswith(('fft2', 'fftn')): + axes = [(0, 1), (1, 2), (0, 2)] + if fft.__name__.endswith('fftn'): + axes.extend([(0,), (1,), (2,), None]) + for ax in axes: + X_res = fft(X, axes=ax) + Y_res = fft(Y, axes=ax) + assert_allclose(X_res, Y_res, atol=_tol, rtol=_tol) + else: + raise ValueError() + + +class TestFFTThreadSafe: + threads = 16 + input_shape = (800, 200) + + def _test_mtsame(self, func, *args): + def worker(args, q): + q.put(func(*args)) + + q = queue.Queue() + expected = func(*args) + + # Spin off a bunch of threads to call the same function simultaneously + t = [threading.Thread(target=worker, args=(args, q)) + for i in range(self.threads)] + [x.start() for x in t] + + [x.join() for x in t] + # Make sure all threads returned the correct value + for i in range(self.threads): + assert_array_equal(q.get(timeout=5), expected, + 'Function returned wrong value in multithreaded context') + + def test_fft(self): + a = np.ones(self.input_shape) * 1+0j + self._test_mtsame(np.fft.fft, a) + + def test_ifft(self): + a = np.ones(self.input_shape) * 1+0j + self._test_mtsame(np.fft.ifft, a) + + def test_rfft(self): + a = np.ones(self.input_shape) + self._test_mtsame(np.fft.rfft, a) + + def test_irfft(self): + a = np.ones(self.input_shape) * 1+0j + self._test_mtsame(np.fft.irfft, a) diff --git a/numpy/lib/__init__.py b/numpy/lib/__init__.py index d764cdc7e14c..ad88ba3478e6 100644 --- a/numpy/lib/__init__.py +++ b/numpy/lib/__init__.py @@ -1,14 +1,29 @@ -from __future__ import division, absolute_import, print_function +""" +**Note:** almost all functions in the ``numpy.lib`` namespace +are also present in the main ``numpy`` namespace. Please use the +functions as ``np.<funcname>`` where possible. +``numpy.lib`` is mostly a space for implementing functions that don't +belong in core or in another NumPy submodule with a clear purpose +(e.g. ``random``, ``fft``, ``linalg``, ``ma``). + +Most contains basic functions that are used by several submodules and are +useful to have in the main name-space. + +""" import math -from .info import __doc__ from numpy.version import version as __version__ +# Public submodules +# Note: recfunctions and (maybe) format are public too, but not imported +from . import mixins +from . import scimath as emath + +# Private submodules from .type_check import * from .index_tricks import * from .function_base import * -from .mixins import * from .nanfunctions import * from .shape_base import * from .stride_tricks import * @@ -16,23 +31,19 @@ from .ufunclike import * from .histograms import * -from . import scimath as emath from .polynomial import * -#import convertcode from .utils import * from .arraysetops import * from .npyio import * -from .financial import * from .arrayterator import Arrayterator from .arraypad import * from ._version import * -from numpy.core.multiarray import tracemalloc_domain +from numpy.core._multiarray_umath import tracemalloc_domain -__all__ = ['emath', 'math', 'tracemalloc_domain'] +__all__ = ['emath', 'math', 'tracemalloc_domain', 'Arrayterator'] __all__ += type_check.__all__ __all__ += index_tricks.__all__ __all__ += function_base.__all__ -__all__ += mixins.__all__ __all__ += shape_base.__all__ __all__ += stride_tricks.__all__ __all__ += twodim_base.__all__ @@ -42,10 +53,9 @@ __all__ += utils.__all__ __all__ += arraysetops.__all__ __all__ += npyio.__all__ -__all__ += financial.__all__ __all__ += nanfunctions.__all__ __all__ += histograms.__all__ -from numpy.testing._private.pytesttester import PytestTester +from numpy._pytesttester import PytestTester test = PytestTester(__name__) del PytestTester diff --git a/numpy/lib/__init__.pyi b/numpy/lib/__init__.pyi new file mode 100644 index 000000000000..ae23b2ec4980 --- /dev/null +++ b/numpy/lib/__init__.pyi @@ -0,0 +1,245 @@ +import math as math +from typing import Any, List + +from numpy._pytesttester import PytestTester + +from numpy import ( + ndenumerate as ndenumerate, + ndindex as ndindex, +) + +from numpy.version import version + +from numpy.lib import ( + format as format, + mixins as mixins, + scimath as scimath, + stride_tricks as stride_stricks, +) + +from numpy.lib._version import ( + NumpyVersion as NumpyVersion, +) + +from numpy.lib.arraypad import ( + pad as pad, +) + +from numpy.lib.arraysetops import ( + ediff1d as ediff1d, + intersect1d as intersect1d, + setxor1d as setxor1d, + union1d as union1d, + setdiff1d as setdiff1d, + unique as unique, + in1d as in1d, + isin as isin, +) + +from numpy.lib.arrayterator import ( + Arrayterator as Arrayterator, +) + +from numpy.lib.function_base import ( + select as select, + piecewise as piecewise, + trim_zeros as trim_zeros, + copy as copy, + iterable as iterable, + percentile as percentile, + diff as diff, + gradient as gradient, + angle as angle, + unwrap as unwrap, + sort_complex as sort_complex, + disp as disp, + flip as flip, + rot90 as rot90, + extract as extract, + place as place, + vectorize as vectorize, + asarray_chkfinite as asarray_chkfinite, + average as average, + bincount as bincount, + digitize as digitize, + cov as cov, + corrcoef as corrcoef, + msort as msort, + median as median, + sinc as sinc, + hamming as hamming, + hanning as hanning, + bartlett as bartlett, + blackman as blackman, + kaiser as kaiser, + trapz as trapz, + i0 as i0, + add_newdoc as add_newdoc, + add_docstring as add_docstring, + meshgrid as meshgrid, + delete as delete, + insert as insert, + append as append, + interp as interp, + add_newdoc_ufunc as add_newdoc_ufunc, + quantile as quantile, +) + +from numpy.lib.histograms import ( + histogram_bin_edges as histogram_bin_edges, + histogram as histogram, + histogramdd as histogramdd, +) + +from numpy.lib.index_tricks import ( + ravel_multi_index as ravel_multi_index, + unravel_index as unravel_index, + mgrid as mgrid, + ogrid as ogrid, + r_ as r_, + c_ as c_, + s_ as s_, + index_exp as index_exp, + ix_ as ix_, + fill_diagonal as fill_diagonal, + diag_indices as diag_indices, + diag_indices_from as diag_indices_from, +) + +from numpy.lib.nanfunctions import ( + nansum as nansum, + nanmax as nanmax, + nanmin as nanmin, + nanargmax as nanargmax, + nanargmin as nanargmin, + nanmean as nanmean, + nanmedian as nanmedian, + nanpercentile as nanpercentile, + nanvar as nanvar, + nanstd as nanstd, + nanprod as nanprod, + nancumsum as nancumsum, + nancumprod as nancumprod, + nanquantile as nanquantile, +) + +from numpy.lib.npyio import ( + savetxt as savetxt, + loadtxt as loadtxt, + genfromtxt as genfromtxt, + recfromtxt as recfromtxt, + recfromcsv as recfromcsv, + load as load, + save as save, + savez as savez, + savez_compressed as savez_compressed, + packbits as packbits, + unpackbits as unpackbits, + fromregex as fromregex, + DataSource as DataSource, +) + +from numpy.lib.polynomial import ( + poly as poly, + roots as roots, + polyint as polyint, + polyder as polyder, + polyadd as polyadd, + polysub as polysub, + polymul as polymul, + polydiv as polydiv, + polyval as polyval, + polyfit as polyfit, + RankWarning as RankWarning, + poly1d as poly1d, +) + +from numpy.lib.shape_base import ( + column_stack as column_stack, + row_stack as row_stack, + dstack as dstack, + array_split as array_split, + split as split, + hsplit as hsplit, + vsplit as vsplit, + dsplit as dsplit, + apply_over_axes as apply_over_axes, + expand_dims as expand_dims, + apply_along_axis as apply_along_axis, + kron as kron, + tile as tile, + get_array_wrap as get_array_wrap, + take_along_axis as take_along_axis, + put_along_axis as put_along_axis, +) + +from numpy.lib.stride_tricks import ( + broadcast_to as broadcast_to, + broadcast_arrays as broadcast_arrays, + broadcast_shapes as broadcast_shapes, +) + +from numpy.lib.twodim_base import ( + diag as diag, + diagflat as diagflat, + eye as eye, + fliplr as fliplr, + flipud as flipud, + tri as tri, + triu as triu, + tril as tril, + vander as vander, + histogram2d as histogram2d, + mask_indices as mask_indices, + tril_indices as tril_indices, + tril_indices_from as tril_indices_from, + triu_indices as triu_indices, + triu_indices_from as triu_indices_from, +) + +from numpy.lib.type_check import ( + mintypecode as mintypecode, + asfarray as asfarray, + real as real, + imag as imag, + iscomplex as iscomplex, + isreal as isreal, + iscomplexobj as iscomplexobj, + isrealobj as isrealobj, + nan_to_num as nan_to_num, + real_if_close as real_if_close, + typename as typename, + common_type as common_type, +) + +from numpy.lib.ufunclike import ( + fix as fix, + isposinf as isposinf, + isneginf as isneginf, +) + +from numpy.lib.utils import ( + issubclass_ as issubclass_, + issubsctype as issubsctype, + issubdtype as issubdtype, + deprecate as deprecate, + deprecate_with_doc as deprecate_with_doc, + get_include as get_include, + info as info, + source as source, + who as who, + lookfor as lookfor, + byte_bounds as byte_bounds, + safe_eval as safe_eval, +) + +from numpy.core.multiarray import ( + tracemalloc_domain as tracemalloc_domain, +) + +__all__: List[str] +__path__: List[str] +test: PytestTester + +__version__ = version +emath = scimath diff --git a/numpy/lib/_datasource.py b/numpy/lib/_datasource.py index 6f1295f096f5..8201d3772887 100644 --- a/numpy/lib/_datasource.py +++ b/numpy/lib/_datasource.py @@ -20,28 +20,29 @@ Example:: >>> # Create a DataSource, use os.curdir (default) for local storage. - >>> ds = datasource.DataSource() + >>> from numpy import DataSource + >>> ds = DataSource() >>> >>> # Open a remote file. >>> # DataSource downloads the file, stores it locally in: >>> # './www.google.com/index.html' >>> # opens the file and returns a file object. - >>> fp = ds.open('http://www.google.com/index.html') + >>> fp = ds.open('http://www.google.com/') # doctest: +SKIP >>> >>> # Use the file as you normally would - >>> fp.read() - >>> fp.close() + >>> fp.read() # doctest: +SKIP + >>> fp.close() # doctest: +SKIP """ -from __future__ import division, absolute_import, print_function - import os -import sys -import shutil import io +from numpy.core.overrides import set_module + + _open = open + def _check_mode(mode, encoding, newline): """Check mode and that encoding and newline are compatible. @@ -65,75 +66,12 @@ def _check_mode(mode, encoding, newline): raise ValueError("Argument 'newline' not supported in binary mode") -def _python2_bz2open(fn, mode, encoding, newline): - """Wrapper to open bz2 in text mode. - - Parameters - ---------- - fn : str - File name - mode : {'r', 'w'} - File mode. Note that bz2 Text files are not supported. - encoding : str - Ignored, text bz2 files not supported in Python2. - newline : str - Ignored, text bz2 files not supported in Python2. - """ - import bz2 - - _check_mode(mode, encoding, newline) - - if "t" in mode: - # BZ2File is missing necessary functions for TextIOWrapper - raise ValueError("bz2 text files not supported in python2") - else: - return bz2.BZ2File(fn, mode) - -def _python2_gzipopen(fn, mode, encoding, newline): - """ Wrapper to open gzip in text mode. - - Parameters - ---------- - fn : str, bytes, file - File path or opened file. - mode : str - File mode. The actual files are opened as binary, but will decoded - using the specified `encoding` and `newline`. - encoding : str - Encoding to be used when reading/writing as text. - newline : str - Newline to be used when reading/writing as text. - - """ - import gzip - # gzip is lacking read1 needed for TextIOWrapper - class GzipWrap(gzip.GzipFile): - def read1(self, n): - return self.read(n) - - _check_mode(mode, encoding, newline) - - gz_mode = mode.replace("t", "") - - if isinstance(fn, (str, bytes)): - binary_file = GzipWrap(fn, gz_mode) - elif hasattr(fn, "read") or hasattr(fn, "write"): - binary_file = GzipWrap(None, gz_mode, fileobj=fn) - else: - raise TypeError("filename must be a str or bytes object, or a file") - - if "t" in mode: - return io.TextIOWrapper(binary_file, encoding, newline=newline) - else: - return binary_file - - # Using a class instead of a module-level dictionary # to reduce the initial 'import numpy' overhead by # deferring the import of lzma, bz2 and gzip until needed # TODO: .zip support, .tar support? -class _FileOpeners(object): +class _FileOpeners: """ Container for different methods to open (un-)compressed files. @@ -150,6 +88,7 @@ class _FileOpeners(object): Examples -------- + >>> import gzip >>> np.lib._datasource._file_openers.keys() [None, '.bz2', '.gz', '.xz', '.lzma'] >>> np.lib._datasource._file_openers['.gz'] is gzip.open @@ -167,19 +106,13 @@ def _load(self): try: import bz2 - if sys.version_info[0] >= 3: - self._file_openers[".bz2"] = bz2.open - else: - self._file_openers[".bz2"] = _python2_bz2open + self._file_openers[".bz2"] = bz2.open except ImportError: pass try: import gzip - if sys.version_info[0] >= 3: - self._file_openers[".gz"] = gzip.open - else: - self._file_openers[".gz"] = _python2_gzipopen + self._file_openers[".gz"] = gzip.open except ImportError: pass @@ -260,7 +193,8 @@ def open(path, mode='r', destpath=os.curdir, encoding=None, newline=None): return ds.open(path, mode, encoding=encoding, newline=newline) -class DataSource (object): +@set_module('numpy') +class DataSource: """ DataSource(destpath='.') @@ -283,7 +217,7 @@ class DataSource (object): URLs require a scheme string (``http://``) to be used, without it they will fail:: - >>> repos = DataSource() + >>> repos = np.DataSource() >>> repos.exists('www.google.com/index.html') False >>> repos.exists('http://www.google.com/index.html') @@ -295,17 +229,17 @@ class DataSource (object): -------- :: - >>> ds = DataSource('/home/guido') - >>> urlname = 'http://www.google.com/index.html' - >>> gfile = ds.open('http://www.google.com/index.html') # remote file + >>> ds = np.DataSource('/home/guido') + >>> urlname = 'http://www.google.com/' + >>> gfile = ds.open('http://www.google.com/') >>> ds.abspath(urlname) - '/home/guido/www.google.com/site/index.html' + '/home/guido/www.google.com/index.html' - >>> ds = DataSource(None) # use with temporary file + >>> ds = np.DataSource(None) # use with temporary file >>> ds.open('/home/guido/foobar.txt') <open file '/home/guido.foobar.txt', mode 'r' at 0x91d4430> >>> ds.abspath('/home/guido/foobar.txt') - '/tmp/tmpy4pgsP/home/guido/foobar.txt' + '/tmp/.../home/guido/foobar.txt' """ @@ -321,7 +255,9 @@ def __init__(self, destpath=os.curdir): def __del__(self): # Remove temp directories - if self._istmpdest: + if hasattr(self, '_istmpdest') and self._istmpdest: + import shutil + shutil.rmtree(self._destpath) def _iszip(self, filename): @@ -367,10 +303,7 @@ def _isurl(self, path): """Test if path is a net location. Tests the scheme and netloc.""" # We do this here to reduce the 'import numpy' initial import time. - if sys.version_info[0] >= 3: - from urllib.parse import urlparse - else: - from urlparse import urlparse + from urllib.parse import urlparse # BUG : URLs require a scheme string ('http://') to be used. # www.google.com will fail. @@ -387,14 +320,10 @@ def _cache(self, path): Creates a copy of the file in the datasource cache. """ - # We import these here because importing urllib2 is slow and + # We import these here because importing them is slow and # a significant fraction of numpy's total import time. - if sys.version_info[0] >= 3: - from urllib.request import urlopen - from urllib.error import URLError - else: - from urllib2 import urlopen - from urllib2 import URLError + import shutil + from urllib.request import urlopen upath = self.abspath(path) @@ -404,16 +333,9 @@ def _cache(self, path): # TODO: Doesn't handle compressed files! if self._isurl(path): - try: - openedurl = urlopen(path) - f = _open(upath, 'wb') - try: + with urlopen(path) as openedurl: + with _open(upath, 'wb') as f: shutil.copyfileobj(openedurl, f) - finally: - f.close() - openedurl.close() - except URLError: - raise URLError("URL not found: %s" % path) else: shutil.copyfile(path, upath) return upath @@ -473,10 +395,7 @@ def abspath(self, path): """ # We do this here to reduce the 'import numpy' initial import time. - if sys.version_info[0] >= 3: - from urllib.parse import urlparse - else: - from urlparse import urlparse + from urllib.parse import urlparse # TODO: This should be more robust. Handles case where path includes # the destpath, but not other sub-paths. Failing case: @@ -538,19 +457,16 @@ def exists(self, path): is accessible if it exists in either location. """ - # We import this here because importing urllib2 is slow and - # a significant fraction of numpy's total import time. - if sys.version_info[0] >= 3: - from urllib.request import urlopen - from urllib.error import URLError - else: - from urllib2 import urlopen - from urllib2 import URLError - # Test local path + # First test for local path if os.path.exists(path): return True + # We import this here because importing urllib is slow and + # a significant fraction of numpy's total import time. + from urllib.request import urlopen + from urllib.error import URLError + # Test cached url upath = self.abspath(path) if os.path.exists(upath): @@ -613,7 +529,7 @@ def open(self, path, mode='r', encoding=None, newline=None): return _file_openers[ext](found, mode=mode, encoding=encoding, newline=newline) else: - raise IOError("%s not found." % path) + raise FileNotFoundError(f"{path} not found.") class Repository (DataSource): diff --git a/numpy/lib/_iotools.py b/numpy/lib/_iotools.py index b604b8c52de3..4a5ac1285fa8 100644 --- a/numpy/lib/_iotools.py +++ b/numpy/lib/_iotools.py @@ -1,20 +1,11 @@ """A collection of functions designed to help I/O with ascii files. """ -from __future__ import division, absolute_import, print_function - __docformat__ = "restructuredtext en" -import sys import numpy as np import numpy.core.numeric as nx -from numpy.compat import asbytes, asunicode, bytes, asbytes_nested, basestring - -if sys.version_info[0] >= 3: - from builtins import bool, int, float, complex, object, str - unicode = str -else: - from __builtin__ import bool, int, float, complex, object, unicode, str +from numpy.compat import asbytes, asunicode def _decode_line(line, encoding=None): @@ -27,18 +18,18 @@ def _decode_line(line, encoding=None): ---------- line : str or bytes Line to be decoded. + encoding : str + Encoding used to decode `line`. Returns ------- - decoded_line : unicode - Unicode in Python 2, a str (unicode) in Python 3. + decoded_line : str """ if type(line) is bytes: if encoding is None: - line = line.decode('latin1') - else: - line = line.decode(encoding) + encoding = "latin1" + line = line.decode(encoding) return line @@ -65,40 +56,6 @@ def _is_bytes_like(obj): return True -def _to_filehandle(fname, flag='r', return_opened=False): - """ - Returns the filehandle corresponding to a string or a file. - If the string ends in '.gz', the file is automatically unzipped. - - Parameters - ---------- - fname : string, filehandle - Name of the file whose filehandle must be returned. - flag : string, optional - Flag indicating the status of the file ('r' for read, 'w' for write). - return_opened : boolean, optional - Whether to return the opening status of the file. - """ - if _is_string_like(fname): - if fname.endswith('.gz'): - import gzip - fhd = gzip.open(fname, flag) - elif fname.endswith('.bz2'): - import bz2 - fhd = bz2.BZ2File(fname) - else: - fhd = file(fname, flag) - opened = True - elif hasattr(fname, 'seek'): - fhd = fname - opened = False - else: - raise ValueError('fname must be a string or file handle') - if return_opened: - return fhd, opened - return fhd - - def has_nested_fields(ndtype): """ Returns whether one or several fields of a dtype are nested. @@ -121,7 +78,7 @@ def has_nested_fields(ndtype): """ for name in ndtype.names or (): - if ndtype[name].names: + if ndtype[name].names is not None: return True return False @@ -146,11 +103,17 @@ def flatten_dtype(ndtype, flatten_base=False): >>> dt = np.dtype([('name', 'S4'), ('x', float), ('y', float), ... ('block', int, (2, 3))]) >>> np.lib._iotools.flatten_dtype(dt) - [dtype('|S4'), dtype('float64'), dtype('float64'), dtype('int32')] + [dtype('S4'), dtype('float64'), dtype('float64'), dtype('int64')] >>> np.lib._iotools.flatten_dtype(dt, flatten_base=True) - [dtype('|S4'), dtype('float64'), dtype('float64'), dtype('int32'), - dtype('int32'), dtype('int32'), dtype('int32'), dtype('int32'), - dtype('int32')] + [dtype('S4'), + dtype('float64'), + dtype('float64'), + dtype('int64'), + dtype('int64'), + dtype('int64'), + dtype('int64'), + dtype('int64'), + dtype('int64')] """ names = ndtype.names @@ -167,7 +130,7 @@ def flatten_dtype(ndtype, flatten_base=False): return types -class LineSplitter(object): +class LineSplitter: """ Object to split a string at a given delimiter or at given places. @@ -202,16 +165,16 @@ def autostrip(self, method): """ return lambda input: [_.strip() for _ in method(input)] - # - def __init__(self, delimiter=None, comments='#', autostrip=True, encoding=None): + def __init__(self, delimiter=None, comments='#', autostrip=True, + encoding=None): delimiter = _decode_line(delimiter) comments = _decode_line(comments) self.comments = comments # Delimiter is a character - if (delimiter is None) or isinstance(delimiter, basestring): + if (delimiter is None) or isinstance(delimiter, str): delimiter = delimiter or None _handyman = self._delimited_splitter # Delimiter is a list of field widths @@ -231,7 +194,6 @@ def __init__(self, delimiter=None, comments='#', autostrip=True, encoding=None): else: self._handyman = _handyman self.encoding = encoding - # def _delimited_splitter(self, line): """Chop off comments, strip, and split at delimiter. """ @@ -241,7 +203,6 @@ def _delimited_splitter(self, line): if not line: return [] return line.split(self.delimiter) - # def _fixedwidth_splitter(self, line): if self.comments is not None: @@ -252,7 +213,6 @@ def _fixedwidth_splitter(self, line): fixed = self.delimiter slices = [slice(i, i + fixed) for i in range(0, len(line), fixed)] return [line[s] for s in slices] - # def _variablewidth_splitter(self, line): if self.comments is not None: @@ -261,13 +221,12 @@ def _variablewidth_splitter(self, line): return [] slices = self.delimiter return [line[s] for s in slices] - # def __call__(self, line): return self._handyman(_decode_line(line, self.encoding)) -class NameValidator(object): +class NameValidator: """ Object to validate a list of strings to use as field names. @@ -309,19 +268,18 @@ class NameValidator(object): -------- >>> validator = np.lib._iotools.NameValidator() >>> validator(['file', 'field2', 'with space', 'CaSe']) - ['file_', 'field2', 'with_space', 'CaSe'] + ('file_', 'field2', 'with_space', 'CaSe') >>> validator = np.lib._iotools.NameValidator(excludelist=['excl'], - deletechars='q', - case_sensitive='False') + ... deletechars='q', + ... case_sensitive=False) >>> validator(['excl', 'field2', 'no_q', 'with space', 'CaSe']) - ['excl_', 'field2', 'no_', 'with_space', 'case'] + ('EXCL', 'FIELD2', 'NO_Q', 'WITH_SPACE', 'CASE') """ - # + defaultexcludelist = ['return', 'file', 'print'] defaultdeletechars = set(r"""~!@#$%^&*()-=+~\|]}[{';: /?.>,<""") - # def __init__(self, excludelist=None, deletechars=None, case_sensitive=None, replace_space='_'): @@ -347,7 +305,7 @@ def __init__(self, excludelist=None, deletechars=None, else: msg = 'unrecognized case_sensitive value %s.' % case_sensitive raise ValueError(msg) - # + self.replace_space = replace_space def validate(self, names, defaultfmt="f%i", nbfields=None): @@ -381,7 +339,7 @@ def validate(self, names, defaultfmt="f%i", nbfields=None): if (nbfields is None): return None names = [] - if isinstance(names, basestring): + if isinstance(names, str): names = [names, ] if nbfields is not None: nbnames = len(names) @@ -398,7 +356,7 @@ def validate(self, names, defaultfmt="f%i", nbfields=None): validatednames = [] seen = dict() nbempty = 0 - # + for item in names: item = case_converter(item).strip() if replace_space: @@ -419,7 +377,6 @@ def validate(self, names, defaultfmt="f%i", nbfields=None): validatednames.append(item) seen[item] = cnt + 1 return tuple(validatednames) - # def __call__(self, names, defaultfmt="f%i", nbfields=None): return self.validate(names, defaultfmt=defaultfmt, nbfields=nbfields) @@ -490,7 +447,7 @@ class ConversionWarning(UserWarning): pass -class StringConverter(object): +class StringConverter: """ Factory class for function transforming a string into another object (int, float). @@ -538,70 +495,73 @@ class StringConverter(object): upgrade or not. Default is False. """ - # _mapper = [(nx.bool_, str2bool, False), - (nx.integer, int, -1)] + (nx.int_, int, -1),] # On 32-bit systems, we need to make sure that we explicitly include - # nx.int64 since ns.integer is nx.int32. - if nx.dtype(nx.integer).itemsize < nx.dtype(nx.int64).itemsize: + # nx.int64 since ns.int_ is nx.int32. + if nx.dtype(nx.int_).itemsize < nx.dtype(nx.int64).itemsize: _mapper.append((nx.int64, int, -1)) - _mapper.extend([(nx.floating, float, nx.nan), - (nx.complexfloating, complex, nx.nan + 0j), + _mapper.extend([(nx.float64, float, nx.nan), + (nx.complex128, complex, nx.nan + 0j), (nx.longdouble, nx.longdouble, nx.nan), + # If a non-default dtype is passed, fall back to generic + # ones (should only be used for the converter) + (nx.integer, int, -1), + (nx.floating, float, nx.nan), + (nx.complexfloating, complex, nx.nan + 0j), + # Last, try with the string types (must be last, because + # `_mapper[-1]` is used as default in some cases) (nx.unicode_, asunicode, '???'), - (nx.string_, asbytes, '???')]) - - (_defaulttype, _defaultfunc, _defaultfill) = zip(*_mapper) + (nx.string_, asbytes, '???'), + ]) @classmethod def _getdtype(cls, val): """Returns the dtype of the input variable.""" return np.array(val).dtype - # @classmethod def _getsubdtype(cls, val): """Returns the type of the dtype of the input variable.""" return np.array(val).dtype.type - # - # This is a bit annoying. We want to return the "general" type in most - # cases (ie. "string" rather than "S10"), but we want to return the - # specific type for datetime64 (ie. "datetime64[us]" rather than - # "datetime64"). @classmethod def _dtypeortype(cls, dtype): """Returns dtype for datetime64 and type of dtype otherwise.""" + + # This is a bit annoying. We want to return the "general" type in most + # cases (ie. "string" rather than "S10"), but we want to return the + # specific type for datetime64 (ie. "datetime64[us]" rather than + # "datetime64"). if dtype.type == np.datetime64: return dtype return dtype.type - # @classmethod def upgrade_mapper(cls, func, default=None): """ - Upgrade the mapper of a StringConverter by adding a new function and - its corresponding default. + Upgrade the mapper of a StringConverter by adding a new function and + its corresponding default. - The input function (or sequence of functions) and its associated - default value (if any) is inserted in penultimate position of the - mapper. The corresponding type is estimated from the dtype of the - default value. + The input function (or sequence of functions) and its associated + default value (if any) is inserted in penultimate position of the + mapper. The corresponding type is estimated from the dtype of the + default value. - Parameters - ---------- - func : var - Function, or sequence of functions - - Examples - -------- - >>> import dateutil.parser - >>> import datetime - >>> dateparser = datetustil.parser.parse - >>> defaultdate = datetime.date(2000, 1, 1) - >>> StringConverter.upgrade_mapper(dateparser, default=defaultdate) + Parameters + ---------- + func : var + Function, or sequence of functions + + Examples + -------- + >>> import dateutil.parser + >>> import datetime + >>> dateparser = dateutil.parser.parse + >>> defaultdate = datetime.date(2000, 1, 1) + >>> StringConverter.upgrade_mapper(dateparser, default=defaultdate) """ # Func is a single functions if hasattr(func, '__call__'): @@ -617,9 +577,22 @@ def upgrade_mapper(cls, func, default=None): else: default = list(default) default.append([None] * (len(func) - len(default))) - for (fct, dft) in zip(func, default): + for fct, dft in zip(func, default): cls._mapper.insert(-1, (cls._getsubdtype(dft), fct, dft)) - # + + @classmethod + def _find_map_entry(cls, dtype): + # if a converter for the specific dtype is available use that + for i, (deftype, func, default_def) in enumerate(cls._mapper): + if dtype.type == deftype: + return i, (deftype, func, default_def) + + # otherwise find an inexact match + for i, (deftype, func, default_def) in enumerate(cls._mapper): + if np.issubdtype(dtype.type, deftype): + return i, (deftype, func, default_def) + + raise LookupError def __init__(self, dtype_or_func=None, default=None, missing_values=None, locked=False): @@ -652,36 +625,26 @@ def __init__(self, dtype_or_func=None, default=None, missing_values=None, except ValueError: default = None dtype = self._getdtype(default) - # Set the status according to the dtype - _status = -1 - for (i, (deftype, func, default_def)) in enumerate(self._mapper): - if np.issubdtype(dtype.type, deftype): - _status = i - if default is None: - self.default = default_def - else: - self.default = default - break - # if a converter for the specific dtype is available use that - last_func = func - for (i, (deftype, func, default_def)) in enumerate(self._mapper): - if dtype.type == deftype: - _status = i - last_func = func - if default is None: - self.default = default_def - else: - self.default = default - break - func = last_func - if _status == -1: - # We never found a match in the _mapper... - _status = 0 + + # find the best match in our mapper + try: + self._status, (_, func, default_def) = self._find_map_entry(dtype) + except LookupError: + # no match self.default = default - self._status = _status + _, func, _ = self._mapper[-1] + self._status = 0 + else: + # use the found default only if we did not already have one + if default is None: + self.default = default_def + else: + self.default = default + # If the input was a dtype, set the function to the last we saw if self.func is None: self.func = func + # If the status is 1 (int), change the function to # something more robust. if self.func == self._mapper[1][1]: @@ -693,24 +656,22 @@ def __init__(self, dtype_or_func=None, default=None, missing_values=None, self.func = lambda x: int(float(x)) # Store the list of strings corresponding to missing values. if missing_values is None: - self.missing_values = set(['']) + self.missing_values = {''} else: - if isinstance(missing_values, basestring): + if isinstance(missing_values, str): missing_values = missing_values.split(",") self.missing_values = set(list(missing_values) + ['']) - # + self._callingfunction = self._strict_call self.type = self._dtypeortype(dtype) self._checked = False self._initial_default = default - # def _loose_call(self, value): try: return self.func(value) except ValueError: return self.default - # def _strict_call(self, value): try: @@ -736,11 +697,29 @@ def _strict_call(self, value): self._checked = False return self.default raise ValueError("Cannot convert string '%s'" % value) - # def __call__(self, value): return self._callingfunction(value) - # + + def _do_upgrade(self): + # Raise an exception if we locked the converter... + if self._locked: + errmsg = "Converter is locked and cannot be upgraded" + raise ConverterLockError(errmsg) + _statusmax = len(self._mapper) + # Complains if we try to upgrade by the maximum + _status = self._status + if _status == _statusmax: + errmsg = "Could not find a valid conversion function" + raise ConverterError(errmsg) + elif _status < _statusmax - 1: + _status += 1 + self.type, self.func, default = self._mapper[_status] + self._status = _status + if self._initial_default is not None: + self.default = self._initial_default + else: + self.default = default def upgrade(self, value): """ @@ -767,24 +746,7 @@ def upgrade(self, value): try: return self._strict_call(value) except ValueError: - # Raise an exception if we locked the converter... - if self._locked: - errmsg = "Converter is locked and cannot be upgraded" - raise ConverterLockError(errmsg) - _statusmax = len(self._mapper) - # Complains if we try to upgrade by the maximum - _status = self._status - if _status == _statusmax: - errmsg = "Could not find a valid conversion function" - raise ConverterError(errmsg) - elif _status < _statusmax - 1: - _status += 1 - (self.type, self.func, default) = self._mapper[_status] - self._status = _status - if self._initial_default is not None: - self.default = self._initial_default - else: - self.default = default + self._do_upgrade() return self.upgrade(value) def iterupgrade(self, value): @@ -796,25 +758,7 @@ def iterupgrade(self, value): for _m in value: _strict_call(_m) except ValueError: - # Raise an exception if we locked the converter... - if self._locked: - errmsg = "Converter is locked and cannot be upgraded" - raise ConverterLockError(errmsg) - _statusmax = len(self._mapper) - # Complains if we try to upgrade by the maximum - _status = self._status - if _status == _statusmax: - raise ConverterError( - "Could not find a valid conversion function" - ) - elif _status < _statusmax - 1: - _status += 1 - (self.type, self.func, default) = self._mapper[_status] - if self._initial_default is not None: - self.default = self._initial_default - else: - self.default = default - self._status = _status + self._do_upgrade() self.iterupgrade(value) def update(self, func, default=None, testing_value=None, @@ -870,7 +814,7 @@ def update(self, func, default=None, testing_value=None, else: if not np.iterable(missing_values): missing_values = [missing_values] - if not all(isinstance(v, basestring) for v in missing_values): + if not all(isinstance(v, str) for v in missing_values): raise TypeError("missing_values must be strings or unicode") self.missing_values.update(missing_values) @@ -920,33 +864,33 @@ def easy_dtype(ndtype, names=None, defaultfmt="f%i", **validationargs): nbfields = len(ndtype) if names is None: names = [''] * len(ndtype) - elif isinstance(names, basestring): + elif isinstance(names, str): names = names.split(",") names = validate(names, nbfields=nbfields, defaultfmt=defaultfmt) ndtype = np.dtype(dict(formats=ndtype, names=names)) else: - nbtypes = len(ndtype) # Explicit names if names is not None: validate = NameValidator(**validationargs) - if isinstance(names, basestring): + if isinstance(names, str): names = names.split(",") # Simple dtype: repeat to match the nb of names - if nbtypes == 0: + if ndtype.names is None: formats = tuple([ndtype.type] * len(names)) names = validate(names, defaultfmt=defaultfmt) ndtype = np.dtype(list(zip(names, formats))) # Structured dtype: just validate the names as needed else: - ndtype.names = validate(names, nbfields=nbtypes, + ndtype.names = validate(names, nbfields=len(ndtype.names), defaultfmt=defaultfmt) # No implicit names - elif (nbtypes > 0): + elif ndtype.names is not None: validate = NameValidator(**validationargs) # Default initial names : should we change the format ? - if ((ndtype.names == tuple("f%i" % i for i in range(nbtypes))) and - (defaultfmt != "f%i")): - ndtype.names = validate([''] * nbtypes, defaultfmt=defaultfmt) + numbered_names = tuple("f%i" % i for i in range(len(ndtype.names))) + if ((ndtype.names == numbered_names) and (defaultfmt != "f%i")): + ndtype.names = validate([''] * len(ndtype.names), + defaultfmt=defaultfmt) # Explicit initial names : just validate else: ndtype.names = validate(ndtype.names, defaultfmt=defaultfmt) diff --git a/numpy/lib/_version.py b/numpy/lib/_version.py index c3563a7fa8fe..bfac5f814501 100644 --- a/numpy/lib/_version.py +++ b/numpy/lib/_version.py @@ -5,12 +5,8 @@ work; they don't recognize anything like alpha/beta/rc/dev versions. """ -from __future__ import division, absolute_import, print_function - import re -from numpy.compat import basestring - __all__ = ['NumpyVersion'] @@ -19,7 +15,7 @@ class NumpyVersion(): """Parse and compare numpy version strings. NumPy has the following versioning scheme (numbers given are examples; they - can be > 9) in principle): + can be > 9 in principle): - Released version: '1.8.0', '1.8.1', etc. - Alpha: '1.8.0a1', '1.8.0a2', etc. @@ -47,15 +43,18 @@ class NumpyVersion(): >>> from numpy.lib import NumpyVersion >>> if NumpyVersion(np.__version__) < '1.7.0': ... print('skip') - skip + >>> # skip >>> NumpyVersion('1.7') # raises ValueError, add ".0" + Traceback (most recent call last): + ... + ValueError: Not a valid numpy version string """ def __init__(self, vstring): self.vstring = vstring - ver_main = re.match(r'\d[.]\d+[.]\d+', vstring) + ver_main = re.match(r'\d+\.\d+\.\d+', vstring) if not ver_main: raise ValueError("Not a valid numpy version string") @@ -113,10 +112,10 @@ def _compare_pre_release(self, other): return vercmp def _compare(self, other): - if not isinstance(other, (basestring, NumpyVersion)): + if not isinstance(other, (str, NumpyVersion)): raise ValueError("Invalid object to compare with NumpyVersion.") - if isinstance(other, basestring): + if isinstance(other, str): other = NumpyVersion(other) vercmp = self._compare_version(other) @@ -152,5 +151,5 @@ def __gt__(self, other): def __ge__(self, other): return self._compare(other) >= 0 - def __repr(self): + def __repr__(self): return "NumpyVersion(%s)" % self.vstring diff --git a/numpy/lib/_version.pyi b/numpy/lib/_version.pyi new file mode 100644 index 000000000000..3581d639bcdd --- /dev/null +++ b/numpy/lib/_version.pyi @@ -0,0 +1,19 @@ +from typing import Union, List + +__all__: List[str] + +class NumpyVersion: + vstring: str + version: str + major: int + minor: int + bugfix: int + pre_release: str + is_devversion: bool + def __init__(self, vstring: str) -> None: ... + def __lt__(self, other: Union[str, NumpyVersion]) -> bool: ... + def __le__(self, other: Union[str, NumpyVersion]) -> bool: ... + def __eq__(self, other: Union[str, NumpyVersion]) -> bool: ... # type: ignore[override] + def __ne__(self, other: Union[str, NumpyVersion]) -> bool: ... # type: ignore[override] + def __gt__(self, other: Union[str, NumpyVersion]) -> bool: ... + def __ge__(self, other: Union[str, NumpyVersion]) -> bool: ... diff --git a/numpy/lib/arraypad.py b/numpy/lib/arraypad.py index e9ca9de4d2fe..8830b814743c 100644 --- a/numpy/lib/arraypad.py +++ b/numpy/lib/arraypad.py @@ -3,9 +3,9 @@ of an n-dimensional array. """ -from __future__ import division, absolute_import, print_function - import numpy as np +from numpy.core.overrides import array_function_dispatch +from numpy.lib.index_tricks import ndindex __all__ = ['pad'] @@ -15,50 +15,7 @@ # Private utility functions. -def _arange_ndarray(arr, shape, axis, reverse=False): - """ - Create an ndarray of `shape` with increments along specified `axis` - - Parameters - ---------- - arr : ndarray - Input array of arbitrary shape. - shape : tuple of ints - Shape of desired array. Should be equivalent to `arr.shape` except - `shape[axis]` which may have any positive value. - axis : int - Axis to increment along. - reverse : bool - If False, increment in a positive fashion from 1 to `shape[axis]`, - inclusive. If True, the bounds are the same but the order reversed. - - Returns - ------- - padarr : ndarray - Output array sized to pad `arr` along `axis`, with linear range from - 1 to `shape[axis]` along specified `axis`. - - Notes - ----- - The range is deliberately 1-indexed for this specific use case. Think of - this algorithm as broadcasting `np.arange` to a single `axis` of an - arbitrarily shaped ndarray. - - """ - initshape = tuple(1 if i != axis else shape[axis] - for (i, x) in enumerate(arr.shape)) - if not reverse: - padarr = np.arange(1, shape[axis] + 1) - else: - padarr = np.arange(shape[axis], 0, -1) - padarr = padarr.reshape(initshape) - for i, dim in enumerate(shape): - if padarr.shape[i] != dim: - padarr = padarr.repeat(dim, axis=i) - return padarr - - -def _round_ifneeded(arr, dtype): +def _round_if_needed(arr, dtype): """ Rounds arr inplace if destination dtype is integer. @@ -68,936 +25,516 @@ def _round_ifneeded(arr, dtype): Input array. dtype : dtype The dtype of the destination array. - """ if np.issubdtype(dtype, np.integer): arr.round(out=arr) -def _slice_at_axis(shape, sl, axis): +def _slice_at_axis(sl, axis): """ - Construct a slice tuple the length of shape, with sl at the specified axis - """ - slice_tup = (slice(None),) - return slice_tup * axis + (sl,) + slice_tup * (len(shape) - axis - 1) - - -def _slice_first(shape, n, axis): - """ Construct a slice tuple to take the first n elements along axis """ - return _slice_at_axis(shape, slice(0, n), axis=axis) - - -def _slice_last(shape, n, axis): - """ Construct a slice tuple to take the last n elements along axis """ - dim = shape[axis] # doing this explicitly makes n=0 work - return _slice_at_axis(shape, slice(dim - n, dim), axis=axis) - - -def _do_prepend(arr, pad_chunk, axis): - return np.concatenate( - (pad_chunk.astype(arr.dtype, copy=False), arr), axis=axis) - - -def _do_append(arr, pad_chunk, axis): - return np.concatenate( - (arr, pad_chunk.astype(arr.dtype, copy=False)), axis=axis) - - -def _prepend_const(arr, pad_amt, val, axis=-1): - """ - Prepend constant `val` along `axis` of `arr`. + Construct tuple of slices to slice an array in the given dimension. Parameters ---------- - arr : ndarray - Input array of arbitrary shape. - pad_amt : int - Amount of padding to prepend. - val : scalar - Constant value to use. For best results should be of type `arr.dtype`; - if not `arr.dtype` will be cast to `arr.dtype`. + sl : slice + The slice for the given dimension. axis : int - Axis along which to pad `arr`. + The axis to which `sl` is applied. All other dimensions are left + "unsliced". Returns ------- - padarr : ndarray - Output array, with `pad_amt` constant `val` prepended along `axis`. - - """ - if pad_amt == 0: - return arr - padshape = tuple(x if i != axis else pad_amt - for (i, x) in enumerate(arr.shape)) - return _do_prepend(arr, np.full(padshape, val, dtype=arr.dtype), axis) - - -def _append_const(arr, pad_amt, val, axis=-1): - """ - Append constant `val` along `axis` of `arr`. - - Parameters - ---------- - arr : ndarray - Input array of arbitrary shape. - pad_amt : int - Amount of padding to append. - val : scalar - Constant value to use. For best results should be of type `arr.dtype`; - if not `arr.dtype` will be cast to `arr.dtype`. - axis : int - Axis along which to pad `arr`. - - Returns - ------- - padarr : ndarray - Output array, with `pad_amt` constant `val` appended along `axis`. - - """ - if pad_amt == 0: - return arr - padshape = tuple(x if i != axis else pad_amt - for (i, x) in enumerate(arr.shape)) - return _do_append(arr, np.full(padshape, val, dtype=arr.dtype), axis) - - - -def _prepend_edge(arr, pad_amt, axis=-1): - """ - Prepend `pad_amt` to `arr` along `axis` by extending edge values. - - Parameters - ---------- - arr : ndarray - Input array of arbitrary shape. - pad_amt : int - Amount of padding to prepend. - axis : int - Axis along which to pad `arr`. - - Returns - ------- - padarr : ndarray - Output array, extended by `pad_amt` edge values appended along `axis`. - - """ - if pad_amt == 0: - return arr - - edge_slice = _slice_first(arr.shape, 1, axis=axis) - edge_arr = arr[edge_slice] - return _do_prepend(arr, edge_arr.repeat(pad_amt, axis=axis), axis) - - -def _append_edge(arr, pad_amt, axis=-1): - """ - Append `pad_amt` to `arr` along `axis` by extending edge values. - - Parameters - ---------- - arr : ndarray - Input array of arbitrary shape. - pad_amt : int - Amount of padding to append. - axis : int - Axis along which to pad `arr`. + sl : tuple of slices + A tuple with slices matching `shape` in length. - Returns - ------- - padarr : ndarray - Output array, extended by `pad_amt` edge values prepended along - `axis`. - - """ - if pad_amt == 0: - return arr - - edge_slice = _slice_last(arr.shape, 1, axis=axis) - edge_arr = arr[edge_slice] - return _do_append(arr, edge_arr.repeat(pad_amt, axis=axis), axis) - - -def _prepend_ramp(arr, pad_amt, end, axis=-1): - """ - Prepend linear ramp along `axis`. - - Parameters - ---------- - arr : ndarray - Input array of arbitrary shape. - pad_amt : int - Amount of padding to prepend. - end : scalar - Constal value to use. For best results should be of type `arr.dtype`; - if not `arr.dtype` will be cast to `arr.dtype`. - axis : int - Axis along which to pad `arr`. - - Returns - ------- - padarr : ndarray - Output array, with `pad_amt` values prepended along `axis`. The - prepended region ramps linearly from the edge value to `end`. - - """ - if pad_amt == 0: - return arr - - # Generate shape for final concatenated array - padshape = tuple(x if i != axis else pad_amt - for (i, x) in enumerate(arr.shape)) - - # Generate an n-dimensional array incrementing along `axis` - ramp_arr = _arange_ndarray(arr, padshape, axis, - reverse=True).astype(np.float64) - - # Appropriate slicing to extract n-dimensional edge along `axis` - edge_slice = _slice_first(arr.shape, 1, axis=axis) - - # Extract edge, and extend along `axis` - edge_pad = arr[edge_slice].repeat(pad_amt, axis) - - # Linear ramp - slope = (end - edge_pad) / float(pad_amt) - ramp_arr = ramp_arr * slope - ramp_arr += edge_pad - _round_ifneeded(ramp_arr, arr.dtype) - - # Ramp values will most likely be float, cast them to the same type as arr - return _do_prepend(arr, ramp_arr, axis) - - -def _append_ramp(arr, pad_amt, end, axis=-1): + Examples + -------- + >>> _slice_at_axis(slice(None, 3, -1), 1) + (slice(None, None, None), slice(None, 3, -1), (...,)) """ - Append linear ramp along `axis`. + return (slice(None),) * axis + (sl,) + (...,) - Parameters - ---------- - arr : ndarray - Input array of arbitrary shape. - pad_amt : int - Amount of padding to append. - end : scalar - Constal value to use. For best results should be of type `arr.dtype`; - if not `arr.dtype` will be cast to `arr.dtype`. - axis : int - Axis along which to pad `arr`. - - Returns - ------- - padarr : ndarray - Output array, with `pad_amt` values appended along `axis`. The - appended region ramps linearly from the edge value to `end`. +def _view_roi(array, original_area_slice, axis): """ - if pad_amt == 0: - return arr + Get a view of the current region of interest during iterative padding. - # Generate shape for final concatenated array - padshape = tuple(x if i != axis else pad_amt - for (i, x) in enumerate(arr.shape)) - - # Generate an n-dimensional array incrementing along `axis` - ramp_arr = _arange_ndarray(arr, padshape, axis, - reverse=False).astype(np.float64) - - # Slice a chunk from the edge to calculate stats on - edge_slice = _slice_last(arr.shape, 1, axis=axis) - - # Extract edge, and extend along `axis` - edge_pad = arr[edge_slice].repeat(pad_amt, axis) - - # Linear ramp - slope = (end - edge_pad) / float(pad_amt) - ramp_arr = ramp_arr * slope - ramp_arr += edge_pad - _round_ifneeded(ramp_arr, arr.dtype) - - # Ramp values will most likely be float, cast them to the same type as arr - return _do_append(arr, ramp_arr, axis) - - -def _prepend_max(arr, pad_amt, num, axis=-1): - """ - Prepend `pad_amt` maximum values along `axis`. + When padding multiple dimensions iteratively corner values are + unnecessarily overwritten multiple times. This function reduces the + working area for the first dimensions so that corners are excluded. Parameters ---------- - arr : ndarray - Input array of arbitrary shape. - pad_amt : int - Amount of padding to prepend. - num : int - Depth into `arr` along `axis` to calculate maximum. - Range: [1, `arr.shape[axis]`] or None (entire axis) + array : ndarray + The array with the region of interest. + original_area_slice : tuple of slices + Denotes the area with original values of the unpadded array. axis : int - Axis along which to pad `arr`. + The currently padded dimension assuming that `axis` is padded before + `axis` + 1. Returns ------- - padarr : ndarray - Output array, with `pad_amt` values appended along `axis`. The - prepended region is the maximum of the first `num` values along - `axis`. - + roi : ndarray + The region of interest of the original `array`. """ - if pad_amt == 0: - return arr - - # Equivalent to edge padding for single value, so do that instead - if num == 1: - return _prepend_edge(arr, pad_amt, axis) - - # Use entire array if `num` is too large - if num is not None: - if num >= arr.shape[axis]: - num = None - - # Slice a chunk from the edge to calculate stats on - max_slice = _slice_first(arr.shape, num, axis=axis) + axis += 1 + sl = (slice(None),) * axis + original_area_slice[axis:] + return array[sl] - # Extract slice, calculate max - max_chunk = arr[max_slice].max(axis=axis, keepdims=True) - # Concatenate `arr` with `max_chunk`, extended along `axis` by `pad_amt` - return _do_prepend(arr, max_chunk.repeat(pad_amt, axis=axis), axis) - - -def _append_max(arr, pad_amt, num, axis=-1): +def _pad_simple(array, pad_width, fill_value=None): """ - Pad one `axis` of `arr` with the maximum of the last `num` elements. + Pad array on all sides with either a single value or undefined values. Parameters ---------- - arr : ndarray - Input array of arbitrary shape. - pad_amt : int - Amount of padding to append. - num : int - Depth into `arr` along `axis` to calculate maximum. - Range: [1, `arr.shape[axis]`] or None (entire axis) - axis : int - Axis along which to pad `arr`. + array : ndarray + Array to grow. + pad_width : sequence of tuple[int, int] + Pad width on both sides for each dimension in `arr`. + fill_value : scalar, optional + If provided the padded area is filled with this value, otherwise + the pad area left undefined. Returns ------- - padarr : ndarray - Output array, with `pad_amt` values appended along `axis`. The - appended region is the maximum of the final `num` values along `axis`. - + padded : ndarray + The padded array with the same dtype as`array`. Its order will default + to C-style if `array` is not F-contiguous. + original_area_slice : tuple + A tuple of slices pointing to the area of the original array. """ - if pad_amt == 0: - return arr - - # Equivalent to edge padding for single value, so do that instead - if num == 1: - return _append_edge(arr, pad_amt, axis) + # Allocate grown array + new_shape = tuple( + left + size + right + for size, (left, right) in zip(array.shape, pad_width) + ) + order = 'F' if array.flags.fnc else 'C' # Fortran and not also C-order + padded = np.empty(new_shape, dtype=array.dtype, order=order) - # Use entire array if `num` is too large - if num is not None: - if num >= arr.shape[axis]: - num = None - - # Slice a chunk from the edge to calculate stats on - if num is not None: - max_slice = _slice_last(arr.shape, num, axis=axis) - else: - max_slice = tuple(slice(None) for x in arr.shape) + if fill_value is not None: + padded.fill(fill_value) - # Extract slice, calculate max - max_chunk = arr[max_slice].max(axis=axis, keepdims=True) + # Copy old array into correct space + original_area_slice = tuple( + slice(left, left + size) + for size, (left, right) in zip(array.shape, pad_width) + ) + padded[original_area_slice] = array - # Concatenate `arr` with `max_chunk`, extended along `axis` by `pad_amt` - return _do_append(arr, max_chunk.repeat(pad_amt, axis=axis), axis) + return padded, original_area_slice -def _prepend_mean(arr, pad_amt, num, axis=-1): +def _set_pad_area(padded, axis, width_pair, value_pair): """ - Prepend `pad_amt` mean values along `axis`. + Set empty-padded area in given dimension. Parameters ---------- - arr : ndarray - Input array of arbitrary shape. - pad_amt : int - Amount of padding to prepend. - num : int - Depth into `arr` along `axis` to calculate mean. - Range: [1, `arr.shape[axis]`] or None (entire axis) + padded : ndarray + Array with the pad area which is modified inplace. axis : int - Axis along which to pad `arr`. - - Returns - ------- - padarr : ndarray - Output array, with `pad_amt` values prepended along `axis`. The - prepended region is the mean of the first `num` values along `axis`. - + Dimension with the pad area to set. + width_pair : (int, int) + Pair of widths that mark the pad area on both sides in the given + dimension. + value_pair : tuple of scalars or ndarrays + Values inserted into the pad area on each side. It must match or be + broadcastable to the shape of `arr`. """ - if pad_amt == 0: - return arr - - # Equivalent to edge padding for single value, so do that instead - if num == 1: - return _prepend_edge(arr, pad_amt, axis) - - # Use entire array if `num` is too large - if num is not None: - if num >= arr.shape[axis]: - num = None - - # Slice a chunk from the edge to calculate stats on - mean_slice = _slice_first(arr.shape, num, axis=axis) + left_slice = _slice_at_axis(slice(None, width_pair[0]), axis) + padded[left_slice] = value_pair[0] - # Extract slice, calculate mean - mean_chunk = arr[mean_slice].mean(axis, keepdims=True) - _round_ifneeded(mean_chunk, arr.dtype) + right_slice = _slice_at_axis( + slice(padded.shape[axis] - width_pair[1], None), axis) + padded[right_slice] = value_pair[1] - # Concatenate `arr` with `mean_chunk`, extended along `axis` by `pad_amt` - return _do_prepend(arr, mean_chunk.repeat(pad_amt, axis), axis=axis) - -def _append_mean(arr, pad_amt, num, axis=-1): +def _get_edges(padded, axis, width_pair): """ - Append `pad_amt` mean values along `axis`. + Retrieve edge values from empty-padded array in given dimension. Parameters ---------- - arr : ndarray - Input array of arbitrary shape. - pad_amt : int - Amount of padding to append. - num : int - Depth into `arr` along `axis` to calculate mean. - Range: [1, `arr.shape[axis]`] or None (entire axis) + padded : ndarray + Empty-padded array. axis : int - Axis along which to pad `arr`. + Dimension in which the edges are considered. + width_pair : (int, int) + Pair of widths that mark the pad area on both sides in the given + dimension. Returns ------- - padarr : ndarray - Output array, with `pad_amt` values appended along `axis`. The - appended region is the maximum of the final `num` values along `axis`. - + left_edge, right_edge : ndarray + Edge values of the valid area in `padded` in the given dimension. Its + shape will always match `padded` except for the dimension given by + `axis` which will have a length of 1. """ - if pad_amt == 0: - return arr - - # Equivalent to edge padding for single value, so do that instead - if num == 1: - return _append_edge(arr, pad_amt, axis) - - # Use entire array if `num` is too large - if num is not None: - if num >= arr.shape[axis]: - num = None - - # Slice a chunk from the edge to calculate stats on - if num is not None: - mean_slice = _slice_last(arr.shape, num, axis=axis) - else: - mean_slice = tuple(slice(None) for x in arr.shape) + left_index = width_pair[0] + left_slice = _slice_at_axis(slice(left_index, left_index + 1), axis) + left_edge = padded[left_slice] - # Extract slice, calculate mean - mean_chunk = arr[mean_slice].mean(axis=axis, keepdims=True) - _round_ifneeded(mean_chunk, arr.dtype) + right_index = padded.shape[axis] - width_pair[1] + right_slice = _slice_at_axis(slice(right_index - 1, right_index), axis) + right_edge = padded[right_slice] - # Concatenate `arr` with `mean_chunk`, extended along `axis` by `pad_amt` - return _do_append(arr, mean_chunk.repeat(pad_amt, axis), axis=axis) + return left_edge, right_edge -def _prepend_med(arr, pad_amt, num, axis=-1): +def _get_linear_ramps(padded, axis, width_pair, end_value_pair): """ - Prepend `pad_amt` median values along `axis`. + Construct linear ramps for empty-padded array in given dimension. Parameters ---------- - arr : ndarray - Input array of arbitrary shape. - pad_amt : int - Amount of padding to prepend. - num : int - Depth into `arr` along `axis` to calculate median. - Range: [1, `arr.shape[axis]`] or None (entire axis) + padded : ndarray + Empty-padded array. axis : int - Axis along which to pad `arr`. + Dimension in which the ramps are constructed. + width_pair : (int, int) + Pair of widths that mark the pad area on both sides in the given + dimension. + end_value_pair : (scalar, scalar) + End values for the linear ramps which form the edge of the fully padded + array. These values are included in the linear ramps. Returns ------- - padarr : ndarray - Output array, with `pad_amt` values prepended along `axis`. The - prepended region is the median of the first `num` values along `axis`. - + left_ramp, right_ramp : ndarray + Linear ramps to set on both sides of `padded`. """ - if pad_amt == 0: - return arr - - # Equivalent to edge padding for single value, so do that instead - if num == 1: - return _prepend_edge(arr, pad_amt, axis) + edge_pair = _get_edges(padded, axis, width_pair) - # Use entire array if `num` is too large - if num is not None: - if num >= arr.shape[axis]: - num = None + left_ramp, right_ramp = ( + np.linspace( + start=end_value, + stop=edge.squeeze(axis), # Dimension is replaced by linspace + num=width, + endpoint=False, + dtype=padded.dtype, + axis=axis + ) + for end_value, edge, width in zip( + end_value_pair, edge_pair, width_pair + ) + ) + + # Reverse linear space in appropriate dimension + right_ramp = right_ramp[_slice_at_axis(slice(None, None, -1), axis)] - # Slice a chunk from the edge to calculate stats on - med_slice = _slice_first(arr.shape, num, axis=axis) + return left_ramp, right_ramp - # Extract slice, calculate median - med_chunk = np.median(arr[med_slice], axis=axis, keepdims=True) - _round_ifneeded(med_chunk, arr.dtype) - # Concatenate `arr` with `med_chunk`, extended along `axis` by `pad_amt` - return _do_prepend(arr, med_chunk.repeat(pad_amt, axis), axis=axis) - - -def _append_med(arr, pad_amt, num, axis=-1): +def _get_stats(padded, axis, width_pair, length_pair, stat_func): """ - Append `pad_amt` median values along `axis`. + Calculate statistic for the empty-padded array in given dimension. Parameters ---------- - arr : ndarray - Input array of arbitrary shape. - pad_amt : int - Amount of padding to append. - num : int - Depth into `arr` along `axis` to calculate median. - Range: [1, `arr.shape[axis]`] or None (entire axis) + padded : ndarray + Empty-padded array. axis : int - Axis along which to pad `arr`. + Dimension in which the statistic is calculated. + width_pair : (int, int) + Pair of widths that mark the pad area on both sides in the given + dimension. + length_pair : 2-element sequence of None or int + Gives the number of values in valid area from each side that is + taken into account when calculating the statistic. If None the entire + valid area in `padded` is considered. + stat_func : function + Function to compute statistic. The expected signature is + ``stat_func(x: ndarray, axis: int, keepdims: bool) -> ndarray``. Returns ------- - padarr : ndarray - Output array, with `pad_amt` values appended along `axis`. The - appended region is the median of the final `num` values along `axis`. - - """ - if pad_amt == 0: - return arr - - # Equivalent to edge padding for single value, so do that instead - if num == 1: - return _append_edge(arr, pad_amt, axis) - - # Use entire array if `num` is too large - if num is not None: - if num >= arr.shape[axis]: - num = None - - # Slice a chunk from the edge to calculate stats on - if num is not None: - med_slice = _slice_last(arr.shape, num, axis=axis) - else: - med_slice = tuple(slice(None) for x in arr.shape) - - # Extract slice, calculate median - med_chunk = np.median(arr[med_slice], axis=axis, keepdims=True) - _round_ifneeded(med_chunk, arr.dtype) - - # Concatenate `arr` with `med_chunk`, extended along `axis` by `pad_amt` - return _do_append(arr, med_chunk.repeat(pad_amt, axis), axis=axis) - - -def _prepend_min(arr, pad_amt, num, axis=-1): + left_stat, right_stat : ndarray + Calculated statistic for both sides of `padded`. """ - Prepend `pad_amt` minimum values along `axis`. - - Parameters - ---------- - arr : ndarray - Input array of arbitrary shape. - pad_amt : int - Amount of padding to prepend. - num : int - Depth into `arr` along `axis` to calculate minimum. - Range: [1, `arr.shape[axis]`] or None (entire axis) - axis : int - Axis along which to pad `arr`. - - Returns - ------- - padarr : ndarray - Output array, with `pad_amt` values prepended along `axis`. The - prepended region is the minimum of the first `num` values along - `axis`. + # Calculate indices of the edges of the area with original values + left_index = width_pair[0] + right_index = padded.shape[axis] - width_pair[1] + # as well as its length + max_length = right_index - left_index - """ - if pad_amt == 0: - return arr + # Limit stat_lengths to max_length + left_length, right_length = length_pair + if left_length is None or max_length < left_length: + left_length = max_length + if right_length is None or max_length < right_length: + right_length = max_length - # Equivalent to edge padding for single value, so do that instead - if num == 1: - return _prepend_edge(arr, pad_amt, axis) + if (left_length == 0 or right_length == 0) \ + and stat_func in {np.amax, np.amin}: + # amax and amin can't operate on an empty array, + # raise a more descriptive warning here instead of the default one + raise ValueError("stat_length of 0 yields no value for padding") - # Use entire array if `num` is too large - if num is not None: - if num >= arr.shape[axis]: - num = None + # Calculate statistic for the left side + left_slice = _slice_at_axis( + slice(left_index, left_index + left_length), axis) + left_chunk = padded[left_slice] + left_stat = stat_func(left_chunk, axis=axis, keepdims=True) + _round_if_needed(left_stat, padded.dtype) - # Slice a chunk from the edge to calculate stats on - min_slice = _slice_first(arr.shape, num, axis=axis) + if left_length == right_length == max_length: + # return early as right_stat must be identical to left_stat + return left_stat, left_stat - # Extract slice, calculate min - min_chunk = arr[min_slice].min(axis=axis, keepdims=True) + # Calculate statistic for the right side + right_slice = _slice_at_axis( + slice(right_index - right_length, right_index), axis) + right_chunk = padded[right_slice] + right_stat = stat_func(right_chunk, axis=axis, keepdims=True) + _round_if_needed(right_stat, padded.dtype) - # Concatenate `arr` with `min_chunk`, extended along `axis` by `pad_amt` - return _do_prepend(arr, min_chunk.repeat(pad_amt, axis), axis=axis) + return left_stat, right_stat -def _append_min(arr, pad_amt, num, axis=-1): +def _set_reflect_both(padded, axis, width_pair, method, include_edge=False): """ - Append `pad_amt` median values along `axis`. + Pad `axis` of `arr` with reflection. Parameters ---------- - arr : ndarray + padded : ndarray Input array of arbitrary shape. - pad_amt : int - Amount of padding to append. - num : int - Depth into `arr` along `axis` to calculate minimum. - Range: [1, `arr.shape[axis]`] or None (entire axis) axis : int Axis along which to pad `arr`. - - Returns - ------- - padarr : ndarray - Output array, with `pad_amt` values appended along `axis`. The - appended region is the minimum of the final `num` values along `axis`. - - """ - if pad_amt == 0: - return arr - - # Equivalent to edge padding for single value, so do that instead - if num == 1: - return _append_edge(arr, pad_amt, axis) - - # Use entire array if `num` is too large - if num is not None: - if num >= arr.shape[axis]: - num = None - - # Slice a chunk from the edge to calculate stats on - if num is not None: - min_slice = _slice_last(arr.shape, num, axis=axis) - else: - min_slice = tuple(slice(None) for x in arr.shape) - - # Extract slice, calculate min - min_chunk = arr[min_slice].min(axis=axis, keepdims=True) - - # Concatenate `arr` with `min_chunk`, extended along `axis` by `pad_amt` - return _do_append(arr, min_chunk.repeat(pad_amt, axis), axis=axis) - - -def _pad_ref(arr, pad_amt, method, axis=-1): - """ - Pad `axis` of `arr` by reflection. - - Parameters - ---------- - arr : ndarray - Input array of arbitrary shape. - pad_amt : tuple of ints, length 2 - Padding to (prepend, append) along `axis`. + width_pair : (int, int) + Pair of widths that mark the pad area on both sides in the given + dimension. method : str Controls method of reflection; options are 'even' or 'odd'. - axis : int - Axis along which to pad `arr`. + include_edge : bool + If true, edge value is included in reflection, otherwise the edge + value forms the symmetric axis to the reflection. Returns ------- - padarr : ndarray - Output array, with `pad_amt[0]` values prepended and `pad_amt[1]` - values appended along `axis`. Both regions are padded with reflected - values from the original array. - - Notes - ----- - This algorithm does not pad with repetition, i.e. the edges are not - repeated in the reflection. For that behavior, use `mode='symmetric'`. - - The modes 'reflect', 'symmetric', and 'wrap' must be padded with a - single function, lest the indexing tricks in non-integer multiples of the - original shape would violate repetition in the final iteration. - - """ - # Implicit booleanness to test for zero (or None) in any scalar type - if pad_amt[0] == 0 and pad_amt[1] == 0: - return arr - - ########################################################################## - # Prepended region - - # Slice off a reverse indexed chunk from near edge to pad `arr` before - ref_slice = _slice_at_axis(arr.shape, slice(pad_amt[0], 0, -1), axis=axis) - - ref_chunk1 = arr[ref_slice] - - # Memory/computationally more expensive, only do this if `method='odd'` - if 'odd' in method and pad_amt[0] > 0: - edge_slice1 = _slice_first(arr.shape, 1, axis=axis) - edge_chunk = arr[edge_slice1] - ref_chunk1 = 2 * edge_chunk - ref_chunk1 - del edge_chunk - - ########################################################################## - # Appended region - - # Slice off a reverse indexed chunk from far edge to pad `arr` after - start = arr.shape[axis] - pad_amt[1] - 1 - end = arr.shape[axis] - 1 - ref_slice = _slice_at_axis(arr.shape, slice(start, end), axis=axis) - rev_idx = _slice_at_axis(arr.shape, slice(None, None, -1), axis=axis) - ref_chunk2 = arr[ref_slice][rev_idx] - - if 'odd' in method: - edge_slice2 = _slice_last(arr.shape, 1, axis=axis) - edge_chunk = arr[edge_slice2] - ref_chunk2 = 2 * edge_chunk - ref_chunk2 - del edge_chunk - - # Concatenate `arr` with both chunks, extending along `axis` - return np.concatenate((ref_chunk1, arr, ref_chunk2), axis=axis) - - -def _pad_sym(arr, pad_amt, method, axis=-1): - """ - Pad `axis` of `arr` by symmetry. - - Parameters - ---------- - arr : ndarray - Input array of arbitrary shape. pad_amt : tuple of ints, length 2 - Padding to (prepend, append) along `axis`. - method : str - Controls method of symmetry; options are 'even' or 'odd'. - axis : int - Axis along which to pad `arr`. - - Returns - ------- - padarr : ndarray - Output array, with `pad_amt[0]` values prepended and `pad_amt[1]` - values appended along `axis`. Both regions are padded with symmetric - values from the original array. - - Notes - ----- - This algorithm DOES pad with repetition, i.e. the edges are repeated. - For padding without repeated edges, use `mode='reflect'`. - - The modes 'reflect', 'symmetric', and 'wrap' must be padded with a - single function, lest the indexing tricks in non-integer multiples of the - original shape would violate repetition in the final iteration. - + New index positions of padding to do along the `axis`. If these are + both 0, padding is done in this dimension. """ - # Implicit booleanness to test for zero (or None) in any scalar type - if pad_amt[0] == 0 and pad_amt[1] == 0: - return arr - - ########################################################################## - # Prepended region - - # Slice off a reverse indexed chunk from near edge to pad `arr` before - sym_slice = _slice_first(arr.shape, pad_amt[0], axis=axis) - rev_idx = _slice_at_axis(arr.shape, slice(None, None, -1), axis=axis) - sym_chunk1 = arr[sym_slice][rev_idx] - - # Memory/computationally more expensive, only do this if `method='odd'` - if 'odd' in method and pad_amt[0] > 0: - edge_slice1 = _slice_first(arr.shape, 1, axis=axis) - edge_chunk = arr[edge_slice1] - sym_chunk1 = 2 * edge_chunk - sym_chunk1 - del edge_chunk - - ########################################################################## - # Appended region - - # Slice off a reverse indexed chunk from far edge to pad `arr` after - sym_slice = _slice_last(arr.shape, pad_amt[1], axis=axis) - sym_chunk2 = arr[sym_slice][rev_idx] - - if 'odd' in method: - edge_slice2 = _slice_last(arr.shape, 1, axis=axis) - edge_chunk = arr[edge_slice2] - sym_chunk2 = 2 * edge_chunk - sym_chunk2 - del edge_chunk - - # Concatenate `arr` with both chunks, extending along `axis` - return np.concatenate((sym_chunk1, arr, sym_chunk2), axis=axis) + left_pad, right_pad = width_pair + old_length = padded.shape[axis] - right_pad - left_pad - -def _pad_wrap(arr, pad_amt, axis=-1): - """ - Pad `axis` of `arr` via wrapping. + if include_edge: + # Edge is included, we need to offset the pad amount by 1 + edge_offset = 1 + else: + edge_offset = 0 # Edge is not included, no need to offset pad amount + old_length -= 1 # but must be omitted from the chunk + + if left_pad > 0: + # Pad with reflected values on left side: + # First limit chunk size which can't be larger than pad area + chunk_length = min(old_length, left_pad) + # Slice right to left, stop on or next to edge, start relative to stop + stop = left_pad - edge_offset + start = stop + chunk_length + left_slice = _slice_at_axis(slice(start, stop, -1), axis) + left_chunk = padded[left_slice] + + if method == "odd": + # Negate chunk and align with edge + edge_slice = _slice_at_axis(slice(left_pad, left_pad + 1), axis) + left_chunk = 2 * padded[edge_slice] - left_chunk + + # Insert chunk into padded area + start = left_pad - chunk_length + stop = left_pad + pad_area = _slice_at_axis(slice(start, stop), axis) + padded[pad_area] = left_chunk + # Adjust pointer to left edge for next iteration + left_pad -= chunk_length + + if right_pad > 0: + # Pad with reflected values on right side: + # First limit chunk size which can't be larger than pad area + chunk_length = min(old_length, right_pad) + # Slice right to left, start on or next to edge, stop relative to start + start = -right_pad + edge_offset - 2 + stop = start - chunk_length + right_slice = _slice_at_axis(slice(start, stop, -1), axis) + right_chunk = padded[right_slice] + + if method == "odd": + # Negate chunk and align with edge + edge_slice = _slice_at_axis( + slice(-right_pad - 1, -right_pad), axis) + right_chunk = 2 * padded[edge_slice] - right_chunk + + # Insert chunk into padded area + start = padded.shape[axis] - right_pad + stop = start + chunk_length + pad_area = _slice_at_axis(slice(start, stop), axis) + padded[pad_area] = right_chunk + # Adjust pointer to right edge for next iteration + right_pad -= chunk_length + + return left_pad, right_pad + + +def _set_wrap_both(padded, axis, width_pair): + """ + Pad `axis` of `arr` with wrapped values. Parameters ---------- - arr : ndarray + padded : ndarray Input array of arbitrary shape. - pad_amt : tuple of ints, length 2 - Padding to (prepend, append) along `axis`. axis : int Axis along which to pad `arr`. + width_pair : (int, int) + Pair of widths that mark the pad area on both sides in the given + dimension. Returns ------- - padarr : ndarray - Output array, with `pad_amt[0]` values prepended and `pad_amt[1]` - values appended along `axis`. Both regions are padded wrapped values - from the opposite end of `axis`. - - Notes - ----- - This method of padding is also known as 'tile' or 'tiling'. - - The modes 'reflect', 'symmetric', and 'wrap' must be padded with a - single function, lest the indexing tricks in non-integer multiples of the - original shape would violate repetition in the final iteration. - - """ - # Implicit booleanness to test for zero (or None) in any scalar type - if pad_amt[0] == 0 and pad_amt[1] == 0: - return arr - - ########################################################################## - # Prepended region - - # Slice off a reverse indexed chunk from near edge to pad `arr` before - wrap_slice = _slice_last(arr.shape, pad_amt[0], axis=axis) - wrap_chunk1 = arr[wrap_slice] - - ########################################################################## - # Appended region - - # Slice off a reverse indexed chunk from far edge to pad `arr` after - wrap_slice = _slice_first(arr.shape, pad_amt[1], axis=axis) - wrap_chunk2 = arr[wrap_slice] - - # Concatenate `arr` with both chunks, extending along `axis` - return np.concatenate((wrap_chunk1, arr, wrap_chunk2), axis=axis) - - -def _normalize_shape(ndarray, shape, cast_to_int=True): - """ - Private function which does some checks and normalizes the possibly - much simpler representations of 'pad_width', 'stat_length', - 'constant_values', 'end_values'. - - Parameters - ---------- - narray : ndarray - Input ndarray - shape : {sequence, array_like, float, int}, optional - The width of padding (pad_width), the number of elements on the - edge of the narray used for statistics (stat_length), the constant - value(s) to use when filling padded regions (constant_values), or the - endpoint target(s) for linear ramps (end_values). - ((before_1, after_1), ... (before_N, after_N)) unique number of - elements for each axis where `N` is rank of `narray`. - ((before, after),) yields same before and after constants for each - axis. - (constant,) or val is a shortcut for before = after = constant for - all axes. - cast_to_int : bool, optional - Controls if values in ``shape`` will be rounded and cast to int - before being returned. - - Returns - ------- - normalized_shape : tuple of tuples - val => ((val, val), (val, val), ...) - [[val1, val2], [val3, val4], ...] => ((val1, val2), (val3, val4), ...) - ((val1, val2), (val3, val4), ...) => no change - [[val1, val2], ] => ((val1, val2), (val1, val2), ...) - ((val1, val2), ) => ((val1, val2), (val1, val2), ...) - [[val , ], ] => ((val, val), (val, val), ...) - ((val , ), ) => ((val, val), (val, val), ...) - - """ - ndims = ndarray.ndim - - # Shortcut shape=None - if shape is None: - return ((None, None), ) * ndims - - # Convert any input `info` to a NumPy array - shape_arr = np.asarray(shape) - - try: - shape_arr = np.broadcast_to(shape_arr, (ndims, 2)) - except ValueError: - fmt = "Unable to create correctly shaped tuple from %s" - raise ValueError(fmt % (shape,)) - - # Cast if necessary - if cast_to_int is True: - shape_arr = np.round(shape_arr).astype(int) - - # Convert list of lists to tuple of tuples - return tuple(tuple(axis) for axis in shape_arr.tolist()) - - -def _validate_lengths(narray, number_elements): - """ - Private function which does some checks and reformats pad_width and - stat_length using _normalize_shape. + pad_amt : tuple of ints, length 2 + New index positions of padding to do along the `axis`. If these are + both 0, padding is done in this dimension. + """ + left_pad, right_pad = width_pair + period = padded.shape[axis] - right_pad - left_pad + + # If the current dimension of `arr` doesn't contain enough valid values + # (not part of the undefined pad area) we need to pad multiple times. + # Each time the pad area shrinks on both sides which is communicated with + # these variables. + new_left_pad = 0 + new_right_pad = 0 + + if left_pad > 0: + # Pad with wrapped values on left side + # First slice chunk from right side of the non-pad area. + # Use min(period, left_pad) to ensure that chunk is not larger than + # pad area + right_slice = _slice_at_axis( + slice(-right_pad - min(period, left_pad), + -right_pad if right_pad != 0 else None), + axis + ) + right_chunk = padded[right_slice] + + if left_pad > period: + # Chunk is smaller than pad area + pad_area = _slice_at_axis(slice(left_pad - period, left_pad), axis) + new_left_pad = left_pad - period + else: + # Chunk matches pad area + pad_area = _slice_at_axis(slice(None, left_pad), axis) + padded[pad_area] = right_chunk + + if right_pad > 0: + # Pad with wrapped values on right side + # First slice chunk from left side of the non-pad area. + # Use min(period, right_pad) to ensure that chunk is not larger than + # pad area + left_slice = _slice_at_axis( + slice(left_pad, left_pad + min(period, right_pad),), axis) + left_chunk = padded[left_slice] + + if right_pad > period: + # Chunk is smaller than pad area + pad_area = _slice_at_axis( + slice(-right_pad, -right_pad + period), axis) + new_right_pad = right_pad - period + else: + # Chunk matches pad area + pad_area = _slice_at_axis(slice(-right_pad, None), axis) + padded[pad_area] = left_chunk + + return new_left_pad, new_right_pad + + +def _as_pairs(x, ndim, as_index=False): + """ + Broadcast `x` to an array with the shape (`ndim`, 2). + + A helper function for `pad` that prepares and validates arguments like + `pad_width` for iteration in pairs. Parameters ---------- - narray : ndarray - Input ndarray - number_elements : {sequence, int}, optional - The width of padding (pad_width) or the number of elements on the edge - of the narray used for statistics (stat_length). - ((before_1, after_1), ... (before_N, after_N)) unique number of - elements for each axis. - ((before, after),) yields same before and after constants for each - axis. - (constant,) or int is a shortcut for before = after = constant for all - axes. + x : {None, scalar, array-like} + The object to broadcast to the shape (`ndim`, 2). + ndim : int + Number of pairs the broadcasted `x` will have. + as_index : bool, optional + If `x` is not None, try to round each element of `x` to an integer + (dtype `np.intp`) and ensure every element is positive. Returns ------- - _validate_lengths : tuple of tuples - int => ((int, int), (int, int), ...) - [[int1, int2], [int3, int4], ...] => ((int1, int2), (int3, int4), ...) - ((int1, int2), (int3, int4), ...) => no change - [[int1, int2], ] => ((int1, int2), (int1, int2), ...) - ((int1, int2), ) => ((int1, int2), (int1, int2), ...) - [[int , ], ] => ((int, int), (int, int), ...) - ((int , ), ) => ((int, int), (int, int), ...) - - """ - normshp = _normalize_shape(narray, number_elements) - for i in normshp: - chk = [1 if x is None else x for x in i] - chk = [1 if x >= 0 else -1 for x in chk] - if (chk[0] < 0) or (chk[1] < 0): - fmt = "%s cannot contain negative values." - raise ValueError(fmt % (number_elements,)) - return normshp + pairs : nested iterables, shape (`ndim`, 2) + The broadcasted version of `x`. + + Raises + ------ + ValueError + If `as_index` is True and `x` contains negative elements. + Or if `x` is not broadcastable to the shape (`ndim`, 2). + """ + if x is None: + # Pass through None as a special case, otherwise np.round(x) fails + # with an AttributeError + return ((None, None),) * ndim + + x = np.array(x) + if as_index: + x = np.round(x).astype(np.intp, copy=False) + + if x.ndim < 3: + # Optimization: Possibly use faster paths for cases where `x` has + # only 1 or 2 elements. `np.broadcast_to` could handle these as well + # but is currently slower + + if x.size == 1: + # x was supplied as a single value + x = x.ravel() # Ensure x[0] works for x.ndim == 0, 1, 2 + if as_index and x < 0: + raise ValueError("index can't contain negative values") + return ((x[0], x[0]),) * ndim + + if x.size == 2 and x.shape != (2, 1): + # x was supplied with a single value for each side + # but except case when each dimension has a single value + # which should be broadcasted to a pair, + # e.g. [[1], [2]] -> [[1, 1], [2, 2]] not [[1, 2], [1, 2]] + x = x.ravel() # Ensure x[0], x[1] works + if as_index and (x[0] < 0 or x[1] < 0): + raise ValueError("index can't contain negative values") + return ((x[0], x[1]),) * ndim + + if as_index and x.min() < 0: + raise ValueError("index can't contain negative values") + + # Converting the array with `tolist` seems to improve performance + # when iterating and indexing the result (see usage in `pad`) + return np.broadcast_to(x, (ndim, 2)).tolist() + + +def _pad_dispatcher(array, pad_width, mode=None, **kwargs): + return (array,) ############################################################################### # Public functions -def pad(array, pad_width, mode, **kwargs): +@array_function_dispatch(_pad_dispatcher, module='numpy') +def pad(array, pad_width, mode='constant', **kwargs): """ - Pads an array. + Pad an array. Parameters ---------- array : array_like of rank N - Input array + The array to pad. pad_width : {sequence, array_like, int} Number of values padded to the edges of each axis. ((before_1, after_1), ... (before_N, after_N)) unique pad widths @@ -1005,10 +542,10 @@ def pad(array, pad_width, mode, **kwargs): ((before, after),) yields same before and after pad for each axis. (pad,) or int is a shortcut for before = after = pad width for all axes. - mode : str or function + mode : str or function, optional One of the following string values or a user supplied function. - 'constant' + 'constant' (default) Pads with a constant value. 'edge' Pads with the edge values of array. @@ -1038,6 +575,11 @@ def pad(array, pad_width, mode, **kwargs): Pads with the wrap of the vector along the axis. The first values are used to pad the end and the end values are used to pad the beginning. + 'empty' + Pads with undefined values. + + .. versionadded:: 1.17 + <function> Padding function, see Notes. stat_length : sequence or int, optional @@ -1054,31 +596,31 @@ def pad(array, pad_width, mode, **kwargs): length for all axes. Default is ``None``, to use the entire axis. - constant_values : sequence or int, optional + constant_values : sequence or scalar, optional Used in 'constant'. The values to set the padded values for each axis. - ((before_1, after_1), ... (before_N, after_N)) unique pad constants + ``((before_1, after_1), ... (before_N, after_N))`` unique pad constants for each axis. - ((before, after),) yields same before and after constants for each + ``((before, after),)`` yields same before and after constants for each axis. - (constant,) or int is a shortcut for before = after = constant for + ``(constant,)`` or ``constant`` is a shortcut for ``before = after = constant`` for all axes. Default is 0. - end_values : sequence or int, optional + end_values : sequence or scalar, optional Used in 'linear_ramp'. The values used for the ending value of the linear_ramp and that will form the edge of the padded array. - ((before_1, after_1), ... (before_N, after_N)) unique end values + ``((before_1, after_1), ... (before_N, after_N))`` unique end values for each axis. - ((before, after),) yields same before and after end values for each + ``((before, after),)`` yields same before and after end values for each axis. - (constant,) or int is a shortcut for before = after = end value for + ``(constant,)`` or ``constant`` is a shortcut for ``before = after = constant`` for all axes. Default is 0. @@ -1103,9 +645,8 @@ def pad(array, pad_width, mode, **kwargs): think about with a rank 2 array where the corners of the padded array are calculated by using padded values from the first axis. - The padding function, if used, should return a rank 1 array equal in - length to the vector argument with padded values replaced. It has the - following signature:: + The padding function, if used, should modify a rank 1 array in-place. It + has the following signature:: padding_func(vector, iaxis_pad_width, iaxis, kwargs) @@ -1113,7 +654,7 @@ def pad(array, pad_width, mode, **kwargs): vector : ndarray A rank 1 array already padded with zeros. Padded values are - vector[:pad_tuple[0]] and vector[-pad_tuple[1]:]. + vector[:iaxis_pad_width[0]] and vector[-iaxis_pad_width[1]:]. iaxis_pad_width : tuple A 2-tuple of ints, iaxis_pad_width[0] represents the number of values padded at the beginning of vector where @@ -1127,11 +668,11 @@ def pad(array, pad_width, mode, **kwargs): Examples -------- >>> a = [1, 2, 3, 4, 5] - >>> np.pad(a, (2,3), 'constant', constant_values=(4, 6)) - array([4, 4, 1, 2, 3, 4, 5, 6, 6, 6]) + >>> np.pad(a, (2, 3), 'constant', constant_values=(4, 6)) + array([4, 4, 1, ..., 6, 6, 6]) >>> np.pad(a, (2, 3), 'edge') - array([1, 1, 1, 2, 3, 4, 5, 5, 5, 5]) + array([1, 1, 1, ..., 5, 5, 5]) >>> np.pad(a, (2, 3), 'linear_ramp', end_values=(5, -4)) array([ 5, 3, 1, 2, 3, 4, 5, 2, -1, -4]) @@ -1175,7 +716,6 @@ def pad(array, pad_width, mode, **kwargs): ... pad_value = kwargs.get('padder', 10) ... vector[:pad_width[0]] = pad_value ... vector[-pad_width[1]:] = pad_value - ... return vector >>> a = np.arange(6) >>> a = a.reshape((2, 3)) >>> np.pad(a, 2, pad_with) @@ -1193,15 +733,42 @@ def pad(array, pad_width, mode, **kwargs): [100, 100, 100, 100, 100, 100, 100], [100, 100, 100, 100, 100, 100, 100]]) """ - if not np.asarray(pad_width).dtype.kind == 'i': + array = np.asarray(array) + pad_width = np.asarray(pad_width) + + if not pad_width.dtype.kind == 'i': raise TypeError('`pad_width` must be of integral type.') - narray = np.array(array) - pad_width = _validate_lengths(narray, pad_width) + # Broadcast to shape (array.ndim, 2) + pad_width = _as_pairs(pad_width, array.ndim, as_index=True) + + if callable(mode): + # Old behavior: Use user-supplied function with np.apply_along_axis + function = mode + # Create a new zero padded array + padded, _ = _pad_simple(array, pad_width, fill_value=0) + # And apply along each axis + + for axis in range(padded.ndim): + # Iterate using ndindex as in apply_along_axis, but assuming that + # function operates inplace on the padded array. - allowedkwargs = { + # view with the iteration axis at the end + view = np.moveaxis(padded, axis, -1) + + # compute indices for the iteration axes, and append a trailing + # ellipsis to prevent 0d arrays decaying to scalars (gh-8642) + inds = ndindex(view.shape[:-1]) + inds = (ind + (Ellipsis,) for ind in inds) + for ind in inds: + function(view[ind], pad_width[axis], axis, kwargs) + + return padded + + # Make sure that no unsupported keywords were passed for the current mode + allowed_kwargs = { + 'empty': [], 'edge': [], 'wrap': [], 'constant': ['constant_values'], - 'edge': [], 'linear_ramp': ['end_values'], 'maximum': ['stat_length'], 'mean': ['stat_length'], @@ -1209,176 +776,101 @@ def pad(array, pad_width, mode, **kwargs): 'minimum': ['stat_length'], 'reflect': ['reflect_type'], 'symmetric': ['reflect_type'], - 'wrap': [], - } - - kwdefaults = { - 'stat_length': None, - 'constant_values': 0, - 'end_values': 0, - 'reflect_type': 'even', - } - - if isinstance(mode, np.compat.basestring): - # Make sure have allowed kwargs appropriate for mode - for key in kwargs: - if key not in allowedkwargs[mode]: - raise ValueError('%s keyword not in allowed keywords %s' % - (key, allowedkwargs[mode])) - - # Set kwarg defaults - for kw in allowedkwargs[mode]: - kwargs.setdefault(kw, kwdefaults[kw]) - - # Need to only normalize particular keywords. - for i in kwargs: - if i == 'stat_length': - kwargs[i] = _validate_lengths(narray, kwargs[i]) - if i in ['end_values', 'constant_values']: - kwargs[i] = _normalize_shape(narray, kwargs[i], - cast_to_int=False) - else: - # Drop back to old, slower np.apply_along_axis mode for user-supplied - # vector function - function = mode - - # Create a new padded array - rank = list(range(narray.ndim)) - total_dim_increase = [np.sum(pad_width[i]) for i in rank] - offset_slices = tuple( - slice(pad_width[i][0], pad_width[i][0] + narray.shape[i]) - for i in rank) - new_shape = np.array(narray.shape) + total_dim_increase - newmat = np.zeros(new_shape, narray.dtype) - - # Insert the original array into the padded array - newmat[offset_slices] = narray - - # This is the core of pad ... - for iaxis in rank: - np.apply_along_axis(function, - iaxis, - newmat, - pad_width[iaxis], - iaxis, - kwargs) - return newmat - - # If we get here, use new padding method - newmat = narray.copy() - - # API preserved, but completely new algorithm which pads by building the - # entire block to pad before/after `arr` with in one step, for each axis. - if mode == 'constant': - for axis, ((pad_before, pad_after), (before_val, after_val)) \ - in enumerate(zip(pad_width, kwargs['constant_values'])): - newmat = _prepend_const(newmat, pad_before, before_val, axis) - newmat = _append_const(newmat, pad_after, after_val, axis) - - elif mode == 'edge': - for axis, (pad_before, pad_after) in enumerate(pad_width): - newmat = _prepend_edge(newmat, pad_before, axis) - newmat = _append_edge(newmat, pad_after, axis) - - elif mode == 'linear_ramp': - for axis, ((pad_before, pad_after), (before_val, after_val)) \ - in enumerate(zip(pad_width, kwargs['end_values'])): - newmat = _prepend_ramp(newmat, pad_before, before_val, axis) - newmat = _append_ramp(newmat, pad_after, after_val, axis) - - elif mode == 'maximum': - for axis, ((pad_before, pad_after), (chunk_before, chunk_after)) \ - in enumerate(zip(pad_width, kwargs['stat_length'])): - newmat = _prepend_max(newmat, pad_before, chunk_before, axis) - newmat = _append_max(newmat, pad_after, chunk_after, axis) - - elif mode == 'mean': - for axis, ((pad_before, pad_after), (chunk_before, chunk_after)) \ - in enumerate(zip(pad_width, kwargs['stat_length'])): - newmat = _prepend_mean(newmat, pad_before, chunk_before, axis) - newmat = _append_mean(newmat, pad_after, chunk_after, axis) - - elif mode == 'median': - for axis, ((pad_before, pad_after), (chunk_before, chunk_after)) \ - in enumerate(zip(pad_width, kwargs['stat_length'])): - newmat = _prepend_med(newmat, pad_before, chunk_before, axis) - newmat = _append_med(newmat, pad_after, chunk_after, axis) - - elif mode == 'minimum': - for axis, ((pad_before, pad_after), (chunk_before, chunk_after)) \ - in enumerate(zip(pad_width, kwargs['stat_length'])): - newmat = _prepend_min(newmat, pad_before, chunk_before, axis) - newmat = _append_min(newmat, pad_after, chunk_after, axis) - - elif mode == 'reflect': - for axis, (pad_before, pad_after) in enumerate(pad_width): - if narray.shape[axis] == 0: - # Axes with non-zero padding cannot be empty. - if pad_before > 0 or pad_after > 0: - raise ValueError("There aren't any elements to reflect" - " in axis {} of `array`".format(axis)) - # Skip zero padding on empty axes. - continue - - # Recursive padding along any axis where `pad_amt` is too large - # for indexing tricks. We can only safely pad the original axis - # length, to keep the period of the reflections consistent. - if ((pad_before > 0) or - (pad_after > 0)) and newmat.shape[axis] == 1: + } + try: + unsupported_kwargs = set(kwargs) - set(allowed_kwargs[mode]) + except KeyError: + raise ValueError("mode '{}' is not supported".format(mode)) from None + if unsupported_kwargs: + raise ValueError("unsupported keyword arguments for mode '{}': {}" + .format(mode, unsupported_kwargs)) + + stat_functions = {"maximum": np.amax, "minimum": np.amin, + "mean": np.mean, "median": np.median} + + # Create array with final shape and original values + # (padded area is undefined) + padded, original_area_slice = _pad_simple(array, pad_width) + # And prepare iteration over all dimensions + # (zipping may be more readable than using enumerate) + axes = range(padded.ndim) + + if mode == "constant": + values = kwargs.get("constant_values", 0) + values = _as_pairs(values, padded.ndim) + for axis, width_pair, value_pair in zip(axes, pad_width, values): + roi = _view_roi(padded, original_area_slice, axis) + _set_pad_area(roi, axis, width_pair, value_pair) + + elif mode == "empty": + pass # Do nothing as _pad_simple already returned the correct result + + elif array.size == 0: + # Only modes "constant" and "empty" can extend empty axes, all other + # modes depend on `array` not being empty + # -> ensure every empty axis is only "padded with 0" + for axis, width_pair in zip(axes, pad_width): + if array.shape[axis] == 0 and any(width_pair): + raise ValueError( + "can't extend empty axis {} using modes other than " + "'constant' or 'empty'".format(axis) + ) + # passed, don't need to do anything more as _pad_simple already + # returned the correct result + + elif mode == "edge": + for axis, width_pair in zip(axes, pad_width): + roi = _view_roi(padded, original_area_slice, axis) + edge_pair = _get_edges(roi, axis, width_pair) + _set_pad_area(roi, axis, width_pair, edge_pair) + + elif mode == "linear_ramp": + end_values = kwargs.get("end_values", 0) + end_values = _as_pairs(end_values, padded.ndim) + for axis, width_pair, value_pair in zip(axes, pad_width, end_values): + roi = _view_roi(padded, original_area_slice, axis) + ramp_pair = _get_linear_ramps(roi, axis, width_pair, value_pair) + _set_pad_area(roi, axis, width_pair, ramp_pair) + + elif mode in stat_functions: + func = stat_functions[mode] + length = kwargs.get("stat_length", None) + length = _as_pairs(length, padded.ndim, as_index=True) + for axis, width_pair, length_pair in zip(axes, pad_width, length): + roi = _view_roi(padded, original_area_slice, axis) + stat_pair = _get_stats(roi, axis, width_pair, length_pair, func) + _set_pad_area(roi, axis, width_pair, stat_pair) + + elif mode in {"reflect", "symmetric"}: + method = kwargs.get("reflect_type", "even") + include_edge = True if mode == "symmetric" else False + for axis, (left_index, right_index) in zip(axes, pad_width): + if array.shape[axis] == 1 and (left_index > 0 or right_index > 0): # Extending singleton dimension for 'reflect' is legacy # behavior; it really should raise an error. - newmat = _prepend_edge(newmat, pad_before, axis) - newmat = _append_edge(newmat, pad_after, axis) + edge_pair = _get_edges(padded, axis, (left_index, right_index)) + _set_pad_area( + padded, axis, (left_index, right_index), edge_pair) continue - method = kwargs['reflect_type'] - safe_pad = newmat.shape[axis] - 1 - while ((pad_before > safe_pad) or (pad_after > safe_pad)): - pad_iter_b = min(safe_pad, - safe_pad * (pad_before // safe_pad)) - pad_iter_a = min(safe_pad, safe_pad * (pad_after // safe_pad)) - newmat = _pad_ref(newmat, (pad_iter_b, - pad_iter_a), method, axis) - pad_before -= pad_iter_b - pad_after -= pad_iter_a - safe_pad += pad_iter_b + pad_iter_a - newmat = _pad_ref(newmat, (pad_before, pad_after), method, axis) - - elif mode == 'symmetric': - for axis, (pad_before, pad_after) in enumerate(pad_width): - # Recursive padding along any axis where `pad_amt` is too large - # for indexing tricks. We can only safely pad the original axis - # length, to keep the period of the reflections consistent. - method = kwargs['reflect_type'] - safe_pad = newmat.shape[axis] - while ((pad_before > safe_pad) or - (pad_after > safe_pad)): - pad_iter_b = min(safe_pad, - safe_pad * (pad_before // safe_pad)) - pad_iter_a = min(safe_pad, safe_pad * (pad_after // safe_pad)) - newmat = _pad_sym(newmat, (pad_iter_b, - pad_iter_a), method, axis) - pad_before -= pad_iter_b - pad_after -= pad_iter_a - safe_pad += pad_iter_b + pad_iter_a - newmat = _pad_sym(newmat, (pad_before, pad_after), method, axis) - - elif mode == 'wrap': - for axis, (pad_before, pad_after) in enumerate(pad_width): - # Recursive padding along any axis where `pad_amt` is too large - # for indexing tricks. We can only safely pad the original axis - # length, to keep the period of the reflections consistent. - safe_pad = newmat.shape[axis] - while ((pad_before > safe_pad) or - (pad_after > safe_pad)): - pad_iter_b = min(safe_pad, - safe_pad * (pad_before // safe_pad)) - pad_iter_a = min(safe_pad, safe_pad * (pad_after // safe_pad)) - newmat = _pad_wrap(newmat, (pad_iter_b, pad_iter_a), axis) - - pad_before -= pad_iter_b - pad_after -= pad_iter_a - safe_pad += pad_iter_b + pad_iter_a - newmat = _pad_wrap(newmat, (pad_before, pad_after), axis) - - return newmat + roi = _view_roi(padded, original_area_slice, axis) + while left_index > 0 or right_index > 0: + # Iteratively pad until dimension is filled with reflected + # values. This is necessary if the pad area is larger than + # the length of the original values in the current dimension. + left_index, right_index = _set_reflect_both( + roi, axis, (left_index, right_index), + method, include_edge + ) + + elif mode == "wrap": + for axis, (left_index, right_index) in zip(axes, pad_width): + roi = _view_roi(padded, original_area_slice, axis) + while left_index > 0 or right_index > 0: + # Iteratively pad until dimension is filled with wrapped + # values. This is necessary if the pad area is larger than + # the length of the original values in the current dimension. + left_index, right_index = _set_wrap_both( + roi, axis, (left_index, right_index)) + + return padded diff --git a/numpy/lib/arraypad.pyi b/numpy/lib/arraypad.pyi new file mode 100644 index 000000000000..d7c5f48445fa --- /dev/null +++ b/numpy/lib/arraypad.pyi @@ -0,0 +1,91 @@ +from typing import ( + Literal as L, + Any, + Dict, + List, + overload, + Tuple, + TypeVar, + Protocol, +) + +from numpy import ndarray, dtype, generic + +from numpy.typing import ( + ArrayLike, + NDArray, + _ArrayLikeInt, + _FiniteNestedSequence, + _SupportsArray, +) + +_SCT = TypeVar("_SCT", bound=generic) + +class _ModeFunc(Protocol): + def __call__( + self, + vector: NDArray[Any], + iaxis_pad_width: Tuple[int, int], + iaxis: int, + kwargs: Dict[str, Any], + /, + ) -> None: ... + +_ModeKind = L[ + "constant", + "edge", + "linear_ramp", + "maximum", + "mean", + "median", + "minimum", + "reflect", + "symmetric", + "wrap", + "empty", +] + +_ArrayLike = _FiniteNestedSequence[_SupportsArray[dtype[_SCT]]] + +__all__: List[str] + +# TODO: In practice each keyword argument is exclusive to one or more +# specific modes. Consider adding more overloads to express this in the future. + +# Expand `**kwargs` into explicit keyword-only arguments +@overload +def pad( + array: _ArrayLike[_SCT], + pad_width: _ArrayLikeInt, + mode: _ModeKind = ..., + *, + stat_length: None | _ArrayLikeInt = ..., + constant_values: ArrayLike = ..., + end_values: ArrayLike = ..., + reflect_type: L["odd", "even"] = ..., +) -> NDArray[_SCT]: ... +@overload +def pad( + array: ArrayLike, + pad_width: _ArrayLikeInt, + mode: _ModeKind = ..., + *, + stat_length: None | _ArrayLikeInt = ..., + constant_values: ArrayLike = ..., + end_values: ArrayLike = ..., + reflect_type: L["odd", "even"] = ..., +) -> NDArray[Any]: ... +@overload +def pad( + array: _ArrayLike[_SCT], + pad_width: _ArrayLikeInt, + mode: _ModeFunc, + **kwargs: Any, +) -> NDArray[_SCT]: ... +@overload +def pad( + array: ArrayLike, + pad_width: _ArrayLikeInt, + mode: _ModeFunc, + **kwargs: Any, +) -> NDArray[Any]: ... diff --git a/numpy/lib/arraysetops.py b/numpy/lib/arraysetops.py index 4d3f35183a46..bd56b6975669 100644 --- a/numpy/lib/arraysetops.py +++ b/numpy/lib/arraysetops.py @@ -1,33 +1,27 @@ """ Set operations for arrays based on sorting. -:Contains: - unique, - isin, - ediff1d, - intersect1d, - setxor1d, - in1d, - union1d, - setdiff1d - -:Notes: +Notes +----- For floating point arrays, inaccurate results may appear due to usual round-off and floating point comparison issues. Speed could be gained in some operations by an implementation of -sort(), that can provide directly the permutation vectors, avoiding -thus calls to argsort(). +`numpy.sort`, that can provide directly the permutation vectors, thus avoiding +calls to `numpy.argsort`. -To do: Optionally return indices analogously to unique for all functions. - -:Author: Robert Cimrman +Original author: Robert Cimrman """ -from __future__ import division, absolute_import, print_function +import functools import numpy as np +from numpy.core import overrides + + +array_function_dispatch = functools.partial( + overrides.array_function_dispatch, module='numpy') __all__ = [ @@ -36,6 +30,11 @@ ] +def _ediff1d_dispatcher(ary, to_end=None, to_begin=None): + return (ary, to_end, to_begin) + + +@array_function_dispatch(_ediff1d_dispatcher) def ediff1d(ary, to_end=None, to_begin=None): """ The differences between consecutive elements of an array. @@ -70,7 +69,7 @@ def ediff1d(ary, to_end=None, to_begin=None): array([ 1, 2, 3, -7]) >>> np.ediff1d(x, to_begin=-99, to_end=np.array([88, 99])) - array([-99, 1, 2, 3, -7, 88, 99]) + array([-99, 1, 2, ..., -7, 88, 99]) The returned array is always 1D. @@ -82,6 +81,9 @@ def ediff1d(ary, to_end=None, to_begin=None): # force a 1d array ary = np.asanyarray(ary).ravel() + # enforce that the dtype of `ary` is used for the output + dtype_req = ary.dtype + # fast track default case if to_begin is None and to_end is None: return ary[1:] - ary[:-1] @@ -89,13 +91,23 @@ def ediff1d(ary, to_end=None, to_begin=None): if to_begin is None: l_begin = 0 else: - to_begin = np.asanyarray(to_begin).ravel() + to_begin = np.asanyarray(to_begin) + if not np.can_cast(to_begin, dtype_req, casting="same_kind"): + raise TypeError("dtype of `to_begin` must be compatible " + "with input `ary` under the `same_kind` rule.") + + to_begin = to_begin.ravel() l_begin = len(to_begin) if to_end is None: l_end = 0 else: - to_end = np.asanyarray(to_end).ravel() + to_end = np.asanyarray(to_end) + if not np.can_cast(to_end, dtype_req, casting="same_kind"): + raise TypeError("dtype of `to_end` must be compatible " + "with input `ary` under the `same_kind` rule.") + + to_end = to_end.ravel() l_end = len(to_end) # do the calculation in place and copy to_begin and to_end @@ -118,6 +130,12 @@ def _unpack_tuple(x): return x +def _unique_dispatcher(ar, return_index=None, return_inverse=None, + return_counts=None, axis=None): + return (ar,) + + +@array_function_dispatch(_unique_dispatcher) def unique(ar, return_index=False, return_inverse=False, return_counts=False, axis=None): """ @@ -177,11 +195,13 @@ def unique(ar, return_index=False, return_inverse=False, -------- numpy.lib.arraysetops : Module with a number of other functions for performing set operations on arrays. + repeat : Repeat elements of an array. Notes ----- When an axis is specified the subarrays indexed by the axis are sorted. This is done by making the specified axis the first dimension of the array + (move the axis to the first dimension to keep the order of the other axes) and then flattening the subarrays in C order. The flattened subarrays are then viewed as a structured type with each element given a label, with the effect that we end up with a 1-D array of structured types that can be @@ -189,6 +209,16 @@ def unique(ar, return_index=False, return_inverse=False, flattened subarrays are sorted in lexicographic order starting with the first element. + .. versionchanged: NumPy 1.21 + If nan values are in the input array, a single nan is put + to the end of the sorted unique values. + + Also for complex arrays all NaN values are considered equivalent + (no matter whether the NaN is in the real or imaginary part). + As the representant for the returned array the smallest one in the + lexicographical order is chosen - see np.sort for how the lexicographical + order is defined for complex arrays. + Examples -------- >>> np.unique([1, 1, 2, 2, 3, 3]) @@ -208,15 +238,13 @@ def unique(ar, return_index=False, return_inverse=False, >>> a = np.array(['a', 'b', 'b', 'c', 'a']) >>> u, indices = np.unique(a, return_index=True) >>> u - array(['a', 'b', 'c'], - dtype='|S1') + array(['a', 'b', 'c'], dtype='<U1') >>> indices array([0, 1, 3]) >>> a[indices] - array(['a', 'b', 'c'], - dtype='|S1') + array(['a', 'b', 'c'], dtype='<U1') - Reconstruct the input array from the unique values: + Reconstruct the input array from the unique values and inverse: >>> a = np.array([1, 2, 6, 4, 2, 3, 2]) >>> u, indices = np.unique(a, return_inverse=True) @@ -227,6 +255,17 @@ def unique(ar, return_index=False, return_inverse=False, >>> u[indices] array([1, 2, 6, 4, 2, 3, 2]) + Reconstruct the input values from the unique values and counts: + + >>> a = np.array([1, 2, 6, 4, 2, 3, 2]) + >>> values, counts = np.unique(a, return_counts=True) + >>> values + array([1, 2, 3, 4, 6]) + >>> counts + array([1, 3, 1, 1, 1]) + >>> np.repeat(values, counts) + array([1, 2, 2, 2, 3, 4, 6]) # original order not preserved + """ ar = np.asanyarray(ar) if axis is None: @@ -235,28 +274,41 @@ def unique(ar, return_index=False, return_inverse=False, # axis was specified and not None try: - ar = np.swapaxes(ar, axis, 0) + ar = np.moveaxis(ar, axis, 0) except np.AxisError: # this removes the "axis1" or "axis2" prefix from the error message - raise np.AxisError(axis, ar.ndim) + raise np.AxisError(axis, ar.ndim) from None # Must reshape to a contiguous 2D array for this to work... orig_shape, orig_dtype = ar.shape, ar.dtype - ar = ar.reshape(orig_shape[0], -1) + ar = ar.reshape(orig_shape[0], np.prod(orig_shape[1:], dtype=np.intp)) ar = np.ascontiguousarray(ar) dtype = [('f{i}'.format(i=i), ar.dtype) for i in range(ar.shape[1])] + # At this point, `ar` has shape `(n, m)`, and `dtype` is a structured + # data type with `m` fields where each field has the data type of `ar`. + # In the following, we create the array `consolidated`, which has + # shape `(n,)` with data type `dtype`. try: - consolidated = ar.view(dtype) - except TypeError: + if ar.shape[1] > 0: + consolidated = ar.view(dtype) + else: + # If ar.shape[1] == 0, then dtype will be `np.dtype([])`, which is + # a data type with itemsize 0, and the call `ar.view(dtype)` will + # fail. Instead, we'll use `np.empty` to explicitly create the + # array with shape `(len(ar),)`. Because `dtype` in this case has + # itemsize 0, the total size of the result is still 0 bytes. + consolidated = np.empty(len(ar), dtype=dtype) + except TypeError as e: # There's no good way to do this for object arrays, etc... msg = 'The axis argument to unique is not supported for dtype {dt}' - raise TypeError(msg.format(dt=ar.dtype)) + raise TypeError(msg.format(dt=ar.dtype)) from e def reshape_uniq(uniq): + n = len(uniq) uniq = uniq.view(orig_dtype) - uniq = uniq.reshape(-1, *orig_shape[1:]) - uniq = np.swapaxes(uniq, 0, axis) + uniq = uniq.reshape(n, *orig_shape[1:]) + uniq = np.moveaxis(uniq, 0, axis) return uniq output = _unique1d(consolidated, return_index, @@ -282,7 +334,18 @@ def _unique1d(ar, return_index=False, return_inverse=False, aux = ar mask = np.empty(aux.shape, dtype=np.bool_) mask[:1] = True - mask[1:] = aux[1:] != aux[:-1] + if aux.shape[0] > 0 and aux.dtype.kind in "cfmM" and np.isnan(aux[-1]): + if aux.dtype.kind == "c": # for complex all NaNs are considered equivalent + aux_firstnan = np.searchsorted(np.isnan(aux), True, side='left') + else: + aux_firstnan = np.searchsorted(aux, aux[-1], side='left') + if aux_firstnan > 0: + mask[1:aux_firstnan] = ( + aux[1:aux_firstnan] != aux[:aux_firstnan - 1]) + mask[aux_firstnan] = True + mask[aux_firstnan + 1:] = False + else: + mask[1:] = aux[1:] != aux[:-1] ret = (aux[mask],) if return_index: @@ -298,6 +361,12 @@ def _unique1d(ar, return_index=False, return_inverse=False, return ret +def _intersect1d_dispatcher( + ar1, ar2, assume_unique=None, return_indices=None): + return (ar1, ar2) + + +@array_function_dispatch(_intersect1d_dispatcher) def intersect1d(ar1, ar2, assume_unique=False, return_indices=False): """ Find the intersection of two arrays. @@ -310,14 +379,16 @@ def intersect1d(ar1, ar2, assume_unique=False, return_indices=False): Input arrays. Will be flattened if not already 1D. assume_unique : bool If True, the input arrays are both assumed to be unique, which - can speed up the calculation. Default is False. + can speed up the calculation. If True but ``ar1`` or ``ar2`` are not + unique, incorrect results and out-of-bounds indices could result. + Default is False. return_indices : bool - If True, the indices which correspond to the intersection of the - two arrays are returned. The first instance of a value is used - if there are multiple. Default is False. - - .. versionadded:: 1.15.0 - + If True, the indices which correspond to the intersection of the two + arrays are returned. The first instance of a value is used if there are + multiple. Default is False. + + .. versionadded:: 1.15.0 + Returns ------- intersect1d : ndarray @@ -326,7 +397,7 @@ def intersect1d(ar1, ar2, assume_unique=False, return_indices=False): The indices of the first occurrences of the common values in `ar1`. Only provided if `return_indices` is True. comm2 : ndarray - The indices of the first occurrences of the common values in `ar2`. + The indices of the first occurrences of the common values in `ar2`. Only provided if `return_indices` is True. @@ -345,9 +416,10 @@ def intersect1d(ar1, ar2, assume_unique=False, return_indices=False): >>> from functools import reduce >>> reduce(np.intersect1d, ([1, 3, 4, 3], [3, 1, 2, 1], [6, 3, 4, 2])) array([3]) - + To return the indices of the values common to the input arrays along with the intersected values: + >>> x = np.array([1, 1, 2, 3, 4]) >>> y = np.array([2, 1, 4, 6]) >>> xy, x_ind, y_ind = np.intersect1d(x, y, return_indices=True) @@ -355,8 +427,11 @@ def intersect1d(ar1, ar2, assume_unique=False, return_indices=False): (array([0, 2, 4]), array([1, 0, 2])) >>> xy, x[x_ind], y[y_ind] (array([1, 2, 4]), array([1, 2, 4]), array([1, 2, 4])) - + """ + ar1 = np.asanyarray(ar1) + ar2 = np.asanyarray(ar2) + if not assume_unique: if return_indices: ar1, ind1 = unique(ar1, return_index=True) @@ -367,7 +442,7 @@ def intersect1d(ar1, ar2, assume_unique=False, return_indices=False): else: ar1 = ar1.ravel() ar2 = ar2.ravel() - + aux = np.concatenate((ar1, ar2)) if return_indices: aux_sort_indices = np.argsort(aux, kind='mergesort') @@ -389,6 +464,12 @@ def intersect1d(ar1, ar2, assume_unique=False, return_indices=False): else: return int1d + +def _setxor1d_dispatcher(ar1, ar2, assume_unique=None): + return (ar1, ar2) + + +@array_function_dispatch(_setxor1d_dispatcher) def setxor1d(ar1, ar2, assume_unique=False): """ Find the set exclusive-or of two arrays. @@ -431,6 +512,11 @@ def setxor1d(ar1, ar2, assume_unique=False): return aux[flag[1:] & flag[:-1]] +def _in1d_dispatcher(ar1, ar2, assume_unique=None, invert=None): + return (ar1, ar2) + + +@array_function_dispatch(_in1d_dispatcher) def in1d(ar1, ar2, assume_unique=False, invert=False): """ Test whether each element of a 1-D array is also present in a second array. @@ -500,6 +586,10 @@ def in1d(ar1, ar2, assume_unique=False, invert=False): ar1 = np.asarray(ar1).ravel() ar2 = np.asarray(ar2).ravel() + # Ensure that iteration through object arrays yields size-1 arrays + if ar2.dtype == object: + ar2 = ar2.reshape(-1, 1) + # Check if one of the arrays may contain arbitrary objects contains_object = ar1.dtype.hasobject or ar2.dtype.hasobject @@ -543,6 +633,11 @@ def in1d(ar1, ar2, assume_unique=False, invert=False): return ret[rev_idx] +def _isin_dispatcher(element, test_elements, assume_unique=None, invert=None): + return (element, test_elements) + + +@array_function_dispatch(_isin_dispatcher) def isin(element, test_elements, assume_unique=False, invert=False): """ Calculates `element in test_elements`, broadcasting over `element` only. @@ -603,14 +698,22 @@ def isin(element, test_elements, assume_unique=False, invert=False): >>> test_elements = [1, 2, 4, 8] >>> mask = np.isin(element, test_elements) >>> mask - array([[ False, True], - [ True, False]]) + array([[False, True], + [ True, False]]) >>> element[mask] array([2, 4]) + + The indices of the matched values can be obtained with `nonzero`: + + >>> np.nonzero(mask) + (array([0, 1]), array([1, 0])) + + The test can also be inverted: + >>> mask = np.isin(element, test_elements, invert=True) >>> mask array([[ True, False], - [ False, True]]) + [False, True]]) >>> element[mask] array([0, 6]) @@ -619,20 +722,25 @@ def isin(element, test_elements, assume_unique=False, invert=False): >>> test_set = {1, 2, 4, 8} >>> np.isin(element, test_set) - array([[ False, False], - [ False, False]]) + array([[False, False], + [False, False]]) Casting the set to a list gives the expected result: >>> np.isin(element, list(test_set)) - array([[ False, True], - [ True, False]]) + array([[False, True], + [ True, False]]) """ element = np.asarray(element) return in1d(element, test_elements, assume_unique=assume_unique, invert=invert).reshape(element.shape) +def _union1d_dispatcher(ar1, ar2): + return (ar1, ar2) + + +@array_function_dispatch(_union1d_dispatcher) def union1d(ar1, ar2): """ Find the union of two arrays. @@ -668,11 +776,17 @@ def union1d(ar1, ar2): """ return unique(np.concatenate((ar1, ar2), axis=None)) + +def _setdiff1d_dispatcher(ar1, ar2, assume_unique=None): + return (ar1, ar2) + + +@array_function_dispatch(_setdiff1d_dispatcher) def setdiff1d(ar1, ar2, assume_unique=False): """ Find the set difference of two arrays. - Return the sorted, unique values in `ar1` that are not in `ar2`. + Return the unique values in `ar1` that are not in `ar2`. Parameters ---------- @@ -687,7 +801,9 @@ def setdiff1d(ar1, ar2, assume_unique=False): Returns ------- setdiff1d : ndarray - Sorted 1D array of values in `ar1` that are not in `ar2`. + 1D array of values in `ar1` that are not in `ar2`. The result + is sorted when `assume_unique=False`, but otherwise only sorted + if the input is sorted. See Also -------- @@ -708,4 +824,3 @@ def setdiff1d(ar1, ar2, assume_unique=False): ar1 = unique(ar1) ar2 = unique(ar2) return ar1[in1d(ar1, ar2, assume_unique=True, invert=True)] - diff --git a/numpy/lib/arraysetops.pyi b/numpy/lib/arraysetops.pyi new file mode 100644 index 000000000000..6f13ec74b82a --- /dev/null +++ b/numpy/lib/arraysetops.pyi @@ -0,0 +1,335 @@ +from typing import ( + Literal as L, + Any, + List, + Union, + TypeVar, + Tuple, + overload, + SupportsIndex, +) + +from numpy import ( + dtype, + generic, + number, + bool_, + ushort, + ubyte, + uintc, + uint, + ulonglong, + short, + int8, + byte, + intc, + int_, + intp, + longlong, + half, + single, + double, + longdouble, + csingle, + cdouble, + clongdouble, + timedelta64, + datetime64, + object_, + str_, + bytes_, + void, +) + +from numpy.typing import ( + ArrayLike, + NDArray, + _FiniteNestedSequence, + _SupportsArray, + _ArrayLikeBool_co, + _ArrayLikeDT64_co, + _ArrayLikeTD64_co, + _ArrayLikeObject_co, + _ArrayLikeNumber_co, +) + +_SCT = TypeVar("_SCT", bound=generic) +_NumberType = TypeVar("_NumberType", bound=number[Any]) + +# Explicitly set all allowed values to prevent accidental castings to +# abstract dtypes (their common super-type). +# +# Only relevant if two or more arguments are parametrized, (e.g. `setdiff1d`) +# which could result in, for example, `int64` and `float64`producing a +# `number[_64Bit]` array +_SCTNoCast = TypeVar( + "_SCTNoCast", + bool_, + ushort, + ubyte, + uintc, + uint, + ulonglong, + short, + byte, + intc, + int_, + longlong, + half, + single, + double, + longdouble, + csingle, + cdouble, + clongdouble, + timedelta64, + datetime64, + object_, + str_, + bytes_, + void, +) + +_ArrayLike = _FiniteNestedSequence[_SupportsArray[dtype[_SCT]]] + +__all__: List[str] + +@overload +def ediff1d( + ary: _ArrayLikeBool_co, + to_end: None | ArrayLike = ..., + to_begin: None | ArrayLike = ..., +) -> NDArray[int8]: ... +@overload +def ediff1d( + ary: _ArrayLike[_NumberType], + to_end: None | ArrayLike = ..., + to_begin: None | ArrayLike = ..., +) -> NDArray[_NumberType]: ... +@overload +def ediff1d( + ary: _ArrayLikeNumber_co, + to_end: None | ArrayLike = ..., + to_begin: None | ArrayLike = ..., +) -> NDArray[Any]: ... +@overload +def ediff1d( + ary: _ArrayLikeDT64_co | _ArrayLikeTD64_co, + to_end: None | ArrayLike = ..., + to_begin: None | ArrayLike = ..., +) -> NDArray[timedelta64]: ... +@overload +def ediff1d( + ary: _ArrayLikeObject_co, + to_end: None | ArrayLike = ..., + to_begin: None | ArrayLike = ..., +) -> NDArray[object_]: ... + +@overload +def unique( + ar: _ArrayLike[_SCT], + return_index: L[False] = ..., + return_inverse: L[False] = ..., + return_counts: L[False] = ..., + axis: None | SupportsIndex = ..., +) -> NDArray[_SCT]: ... +@overload +def unique( + ar: ArrayLike, + return_index: L[False] = ..., + return_inverse: L[False] = ..., + return_counts: L[False] = ..., + axis: None | SupportsIndex = ..., +) -> NDArray[Any]: ... +@overload +def unique( + ar: _ArrayLike[_SCT], + return_index: L[True] = ..., + return_inverse: L[False] = ..., + return_counts: L[False] = ..., + axis: None | SupportsIndex = ..., +) -> Tuple[NDArray[_SCT], NDArray[intp]]: ... +@overload +def unique( + ar: ArrayLike, + return_index: L[True] = ..., + return_inverse: L[False] = ..., + return_counts: L[False] = ..., + axis: None | SupportsIndex = ..., +) -> Tuple[NDArray[Any], NDArray[intp]]: ... +@overload +def unique( + ar: _ArrayLike[_SCT], + return_index: L[False] = ..., + return_inverse: L[True] = ..., + return_counts: L[False] = ..., + axis: None | SupportsIndex = ..., +) -> Tuple[NDArray[_SCT], NDArray[intp]]: ... +@overload +def unique( + ar: ArrayLike, + return_index: L[False] = ..., + return_inverse: L[True] = ..., + return_counts: L[False] = ..., + axis: None | SupportsIndex = ..., +) -> Tuple[NDArray[Any], NDArray[intp]]: ... +@overload +def unique( + ar: _ArrayLike[_SCT], + return_index: L[False] = ..., + return_inverse: L[False] = ..., + return_counts: L[True] = ..., + axis: None | SupportsIndex = ..., +) -> Tuple[NDArray[_SCT], NDArray[intp]]: ... +@overload +def unique( + ar: ArrayLike, + return_index: L[False] = ..., + return_inverse: L[False] = ..., + return_counts: L[True] = ..., + axis: None | SupportsIndex = ..., +) -> Tuple[NDArray[Any], NDArray[intp]]: ... +@overload +def unique( + ar: _ArrayLike[_SCT], + return_index: L[True] = ..., + return_inverse: L[True] = ..., + return_counts: L[False] = ..., + axis: None | SupportsIndex = ..., +) -> Tuple[NDArray[_SCT], NDArray[intp], NDArray[intp]]: ... +@overload +def unique( + ar: ArrayLike, + return_index: L[True] = ..., + return_inverse: L[True] = ..., + return_counts: L[False] = ..., + axis: None | SupportsIndex = ..., +) -> Tuple[NDArray[Any], NDArray[intp], NDArray[intp]]: ... +@overload +def unique( + ar: _ArrayLike[_SCT], + return_index: L[True] = ..., + return_inverse: L[False] = ..., + return_counts: L[True] = ..., + axis: None | SupportsIndex = ..., +) -> Tuple[NDArray[_SCT], NDArray[intp], NDArray[intp]]: ... +@overload +def unique( + ar: ArrayLike, + return_index: L[True] = ..., + return_inverse: L[False] = ..., + return_counts: L[True] = ..., + axis: None | SupportsIndex = ..., +) -> Tuple[NDArray[Any], NDArray[intp], NDArray[intp]]: ... +@overload +def unique( + ar: _ArrayLike[_SCT], + return_index: L[False] = ..., + return_inverse: L[True] = ..., + return_counts: L[True] = ..., + axis: None | SupportsIndex = ..., +) -> Tuple[NDArray[_SCT], NDArray[intp], NDArray[intp]]: ... +@overload +def unique( + ar: ArrayLike, + return_index: L[False] = ..., + return_inverse: L[True] = ..., + return_counts: L[True] = ..., + axis: None | SupportsIndex = ..., +) -> Tuple[NDArray[Any], NDArray[intp], NDArray[intp]]: ... +@overload +def unique( + ar: _ArrayLike[_SCT], + return_index: L[True] = ..., + return_inverse: L[True] = ..., + return_counts: L[True] = ..., + axis: None | SupportsIndex = ..., +) -> Tuple[NDArray[_SCT], NDArray[intp], NDArray[intp], NDArray[intp]]: ... +@overload +def unique( + ar: ArrayLike, + return_index: L[True] = ..., + return_inverse: L[True] = ..., + return_counts: L[True] = ..., + axis: None | SupportsIndex = ..., +) -> Tuple[NDArray[Any], NDArray[intp], NDArray[intp], NDArray[intp]]: ... + +@overload +def intersect1d( + ar1: _ArrayLike[_SCTNoCast], + ar2: _ArrayLike[_SCTNoCast], + assume_unique: bool = ..., + return_indices: L[False] = ..., +) -> NDArray[_SCTNoCast]: ... +@overload +def intersect1d( + ar1: ArrayLike, + ar2: ArrayLike, + assume_unique: bool = ..., + return_indices: L[False] = ..., +) -> NDArray[Any]: ... +@overload +def intersect1d( + ar1: _ArrayLike[_SCTNoCast], + ar2: _ArrayLike[_SCTNoCast], + assume_unique: bool = ..., + return_indices: L[True] = ..., +) -> Tuple[NDArray[_SCTNoCast], NDArray[intp], NDArray[intp]]: ... +@overload +def intersect1d( + ar1: ArrayLike, + ar2: ArrayLike, + assume_unique: bool = ..., + return_indices: L[True] = ..., +) -> Tuple[NDArray[Any], NDArray[intp], NDArray[intp]]: ... + +@overload +def setxor1d( + ar1: _ArrayLike[_SCTNoCast], + ar2: _ArrayLike[_SCTNoCast], + assume_unique: bool = ..., +) -> NDArray[_SCTNoCast]: ... +@overload +def setxor1d( + ar1: ArrayLike, + ar2: ArrayLike, + assume_unique: bool = ..., +) -> NDArray[Any]: ... + +def in1d( + ar1: ArrayLike, + ar2: ArrayLike, + assume_unique: bool = ..., + invert: bool = ..., +) -> NDArray[bool_]: ... + +def isin( + element: ArrayLike, + test_elements: ArrayLike, + assume_unique: bool = ..., + invert: bool = ..., +) -> NDArray[bool_]: ... + +@overload +def union1d( + ar1: _ArrayLike[_SCTNoCast], + ar2: _ArrayLike[_SCTNoCast], +) -> NDArray[_SCTNoCast]: ... +@overload +def union1d( + ar1: ArrayLike, + ar2: ArrayLike, +) -> NDArray[Any]: ... + +@overload +def setdiff1d( + ar1: _ArrayLike[_SCTNoCast], + ar2: _ArrayLike[_SCTNoCast], + assume_unique: bool = ..., +) -> NDArray[_SCTNoCast]: ... +@overload +def setdiff1d( + ar1: ArrayLike, + ar2: ArrayLike, + assume_unique: bool = ..., +) -> NDArray[Any]: ... diff --git a/numpy/lib/arrayterator.py b/numpy/lib/arrayterator.py index f2d4fe9fdccf..b9ea21f8e49f 100644 --- a/numpy/lib/arrayterator.py +++ b/numpy/lib/arrayterator.py @@ -7,17 +7,13 @@ a user-specified number of elements. """ -from __future__ import division, absolute_import, print_function - from operator import mul from functools import reduce -from numpy.compat import long - __all__ = ['Arrayterator'] -class Arrayterator(object): +class Arrayterator: """ Buffered iterator for big arrays. @@ -80,9 +76,8 @@ class Arrayterator(object): >>> for subarr in a_itor: ... if not subarr.all(): - ... print(subarr, subarr.shape) - ... - [[[[0 1]]]] (1, 1, 1, 2) + ... print(subarr, subarr.shape) # doctest: +SKIP + >>> # [[[[0 1]]]] (1, 1, 1, 2) """ @@ -111,7 +106,7 @@ def __getitem__(self, index): if slice_ is Ellipsis: fixed.extend([slice(None)] * (dims-length+1)) length = len(fixed) - elif isinstance(slice_, (int, long)): + elif isinstance(slice_, int): fixed.append(slice(slice_, slice_+1, 1)) else: fixed.append(slice_) @@ -160,12 +155,11 @@ def flat(self): ... if not subarr: ... print(subarr, type(subarr)) ... - 0 <type 'numpy.int32'> + 0 <class 'numpy.int64'> """ for block in self: - for value in block.flat: - yield value + yield from block.flat @property def shape(self): diff --git a/numpy/lib/arrayterator.pyi b/numpy/lib/arrayterator.pyi new file mode 100644 index 000000000000..82c66920640f --- /dev/null +++ b/numpy/lib/arrayterator.pyi @@ -0,0 +1,52 @@ +from typing import ( + List, + Any, + TypeVar, + Generator, + List, + Union, + Tuple, + overload, +) + +from numpy import ndarray, dtype, generic +from numpy.typing import DTypeLike + +# TODO: Set a shape bound once we've got proper shape support +_Shape = TypeVar("_Shape", bound=Any) +_DType = TypeVar("_DType", bound=dtype[Any]) +_ScalarType = TypeVar("_ScalarType", bound=generic) + +_Index = Union[ + Union[ellipsis, int, slice], + Tuple[Union[ellipsis, int, slice], ...], +] + +__all__: List[str] + +# NOTE: In reality `Arrayterator` does not actually inherit from `ndarray`, +# but its ``__getattr__` method does wrap around the former and thus has +# access to all its methods + +class Arrayterator(ndarray[_Shape, _DType]): + var: ndarray[_Shape, _DType] # type: ignore[assignment] + buf_size: None | int + start: List[int] + stop: List[int] + step: List[int] + + @property # type: ignore[misc] + def shape(self) -> Tuple[int, ...]: ... + @property + def flat( # type: ignore[override] + self: ndarray[Any, dtype[_ScalarType]] + ) -> Generator[_ScalarType, None, None]: ... + def __init__( + self, var: ndarray[_Shape, _DType], buf_size: None | int = ... + ) -> None: ... + @overload + def __array__(self, dtype: None = ...) -> ndarray[Any, _DType]: ... + @overload + def __array__(self, dtype: DTypeLike) -> ndarray[Any, dtype[Any]]: ... + def __getitem__(self, index: _Index) -> Arrayterator[Any, _DType]: ... + def __iter__(self) -> Generator[ndarray[Any, _DType], None, None]: ... diff --git a/numpy/lib/financial.py b/numpy/lib/financial.py deleted file mode 100644 index 06fa1bd927d9..000000000000 --- a/numpy/lib/financial.py +++ /dev/null @@ -1,761 +0,0 @@ -"""Some simple financial calculations - -patterned after spreadsheet computations. - -There is some complexity in each function -so that the functions behave like ufuncs with -broadcasting and being able to be called with scalars -or arrays (or other sequences). - -Functions support the :class:`decimal.Decimal` type unless -otherwise stated. -""" -from __future__ import division, absolute_import, print_function - -from decimal import Decimal - -import numpy as np - -__all__ = ['fv', 'pmt', 'nper', 'ipmt', 'ppmt', 'pv', 'rate', - 'irr', 'npv', 'mirr'] - -_when_to_num = {'end':0, 'begin':1, - 'e':0, 'b':1, - 0:0, 1:1, - 'beginning':1, - 'start':1, - 'finish':0} - -def _convert_when(when): - #Test to see if when has already been converted to ndarray - #This will happen if one function calls another, for example ppmt - if isinstance(when, np.ndarray): - return when - try: - return _when_to_num[when] - except (KeyError, TypeError): - return [_when_to_num[x] for x in when] - -def fv(rate, nper, pmt, pv, when='end'): - """ - Compute the future value. - - Given: - * a present value, `pv` - * an interest `rate` compounded once per period, of which - there are - * `nper` total - * a (fixed) payment, `pmt`, paid either - * at the beginning (`when` = {'begin', 1}) or the end - (`when` = {'end', 0}) of each period - - Return: - the value at the end of the `nper` periods - - Parameters - ---------- - rate : scalar or array_like of shape(M, ) - Rate of interest as decimal (not per cent) per period - nper : scalar or array_like of shape(M, ) - Number of compounding periods - pmt : scalar or array_like of shape(M, ) - Payment - pv : scalar or array_like of shape(M, ) - Present value - when : {{'begin', 1}, {'end', 0}}, {string, int}, optional - When payments are due ('begin' (1) or 'end' (0)). - Defaults to {'end', 0}. - - Returns - ------- - out : ndarray - Future values. If all input is scalar, returns a scalar float. If - any input is array_like, returns future values for each input element. - If multiple inputs are array_like, they all must have the same shape. - - Notes - ----- - The future value is computed by solving the equation:: - - fv + - pv*(1+rate)**nper + - pmt*(1 + rate*when)/rate*((1 + rate)**nper - 1) == 0 - - or, when ``rate == 0``:: - - fv + pv + pmt * nper == 0 - - References - ---------- - .. [WRW] Wheeler, D. A., E. Rathke, and R. Weir (Eds.) (2009, May). - Open Document Format for Office Applications (OpenDocument)v1.2, - Part 2: Recalculated Formula (OpenFormula) Format - Annotated Version, - Pre-Draft 12. Organization for the Advancement of Structured Information - Standards (OASIS). Billerica, MA, USA. [ODT Document]. - Available: - http://www.oasis-open.org/committees/documents.php?wg_abbrev=office-formula - OpenDocument-formula-20090508.odt - - Examples - -------- - What is the future value after 10 years of saving $100 now, with - an additional monthly savings of $100. Assume the interest rate is - 5% (annually) compounded monthly? - - >>> np.fv(0.05/12, 10*12, -100, -100) - 15692.928894335748 - - By convention, the negative sign represents cash flow out (i.e. money not - available today). Thus, saving $100 a month at 5% annual interest leads - to $15,692.93 available to spend in 10 years. - - If any input is array_like, returns an array of equal shape. Let's - compare different interest rates from the example above. - - >>> a = np.array((0.05, 0.06, 0.07))/12 - >>> np.fv(a, 10*12, -100, -100) - array([ 15692.92889434, 16569.87435405, 17509.44688102]) - - """ - when = _convert_when(when) - (rate, nper, pmt, pv, when) = map(np.asarray, [rate, nper, pmt, pv, when]) - temp = (1+rate)**nper - fact = np.where(rate == 0, nper, - (1 + rate*when)*(temp - 1)/rate) - return -(pv*temp + pmt*fact) - -def pmt(rate, nper, pv, fv=0, when='end'): - """ - Compute the payment against loan principal plus interest. - - Given: - * a present value, `pv` (e.g., an amount borrowed) - * a future value, `fv` (e.g., 0) - * an interest `rate` compounded once per period, of which - there are - * `nper` total - * and (optional) specification of whether payment is made - at the beginning (`when` = {'begin', 1}) or the end - (`when` = {'end', 0}) of each period - - Return: - the (fixed) periodic payment. - - Parameters - ---------- - rate : array_like - Rate of interest (per period) - nper : array_like - Number of compounding periods - pv : array_like - Present value - fv : array_like, optional - Future value (default = 0) - when : {{'begin', 1}, {'end', 0}}, {string, int} - When payments are due ('begin' (1) or 'end' (0)) - - Returns - ------- - out : ndarray - Payment against loan plus interest. If all input is scalar, returns a - scalar float. If any input is array_like, returns payment for each - input element. If multiple inputs are array_like, they all must have - the same shape. - - Notes - ----- - The payment is computed by solving the equation:: - - fv + - pv*(1 + rate)**nper + - pmt*(1 + rate*when)/rate*((1 + rate)**nper - 1) == 0 - - or, when ``rate == 0``:: - - fv + pv + pmt * nper == 0 - - for ``pmt``. - - Note that computing a monthly mortgage payment is only - one use for this function. For example, pmt returns the - periodic deposit one must make to achieve a specified - future balance given an initial deposit, a fixed, - periodically compounded interest rate, and the total - number of periods. - - References - ---------- - .. [WRW] Wheeler, D. A., E. Rathke, and R. Weir (Eds.) (2009, May). - Open Document Format for Office Applications (OpenDocument)v1.2, - Part 2: Recalculated Formula (OpenFormula) Format - Annotated Version, - Pre-Draft 12. Organization for the Advancement of Structured Information - Standards (OASIS). Billerica, MA, USA. [ODT Document]. - Available: - http://www.oasis-open.org/committees/documents.php - ?wg_abbrev=office-formulaOpenDocument-formula-20090508.odt - - Examples - -------- - What is the monthly payment needed to pay off a $200,000 loan in 15 - years at an annual interest rate of 7.5%? - - >>> np.pmt(0.075/12, 12*15, 200000) - -1854.0247200054619 - - In order to pay-off (i.e., have a future-value of 0) the $200,000 obtained - today, a monthly payment of $1,854.02 would be required. Note that this - example illustrates usage of `fv` having a default value of 0. - - """ - when = _convert_when(when) - (rate, nper, pv, fv, when) = map(np.array, [rate, nper, pv, fv, when]) - temp = (1 + rate)**nper - mask = (rate == 0) - masked_rate = np.where(mask, 1, rate) - fact = np.where(mask != 0, nper, - (1 + masked_rate*when)*(temp - 1)/masked_rate) - return -(fv + pv*temp) / fact - -def nper(rate, pmt, pv, fv=0, when='end'): - """ - Compute the number of periodic payments. - - :class:`decimal.Decimal` type is not supported. - - Parameters - ---------- - rate : array_like - Rate of interest (per period) - pmt : array_like - Payment - pv : array_like - Present value - fv : array_like, optional - Future value - when : {{'begin', 1}, {'end', 0}}, {string, int}, optional - When payments are due ('begin' (1) or 'end' (0)) - - Notes - ----- - The number of periods ``nper`` is computed by solving the equation:: - - fv + pv*(1+rate)**nper + pmt*(1+rate*when)/rate*((1+rate)**nper-1) = 0 - - but if ``rate = 0`` then:: - - fv + pv + pmt*nper = 0 - - Examples - -------- - If you only had $150/month to pay towards the loan, how long would it take - to pay-off a loan of $8,000 at 7% annual interest? - - >>> print(round(np.nper(0.07/12, -150, 8000), 5)) - 64.07335 - - So, over 64 months would be required to pay off the loan. - - The same analysis could be done with several different interest rates - and/or payments and/or total amounts to produce an entire table. - - >>> np.nper(*(np.ogrid[0.07/12: 0.08/12: 0.01/12, - ... -150 : -99 : 50 , - ... 8000 : 9001 : 1000])) - array([[[ 64.07334877, 74.06368256], - [ 108.07548412, 127.99022654]], - [[ 66.12443902, 76.87897353], - [ 114.70165583, 137.90124779]]]) - - """ - when = _convert_when(when) - (rate, pmt, pv, fv, when) = map(np.asarray, [rate, pmt, pv, fv, when]) - - use_zero_rate = False - with np.errstate(divide="raise"): - try: - z = pmt*(1+rate*when)/rate - except FloatingPointError: - use_zero_rate = True - - if use_zero_rate: - return (-fv + pv) / pmt - else: - A = -(fv + pv)/(pmt+0) - B = np.log((-fv+z) / (pv+z))/np.log(1+rate) - return np.where(rate == 0, A, B) - -def ipmt(rate, per, nper, pv, fv=0, when='end'): - """ - Compute the interest portion of a payment. - - Parameters - ---------- - rate : scalar or array_like of shape(M, ) - Rate of interest as decimal (not per cent) per period - per : scalar or array_like of shape(M, ) - Interest paid against the loan changes during the life or the loan. - The `per` is the payment period to calculate the interest amount. - nper : scalar or array_like of shape(M, ) - Number of compounding periods - pv : scalar or array_like of shape(M, ) - Present value - fv : scalar or array_like of shape(M, ), optional - Future value - when : {{'begin', 1}, {'end', 0}}, {string, int}, optional - When payments are due ('begin' (1) or 'end' (0)). - Defaults to {'end', 0}. - - Returns - ------- - out : ndarray - Interest portion of payment. If all input is scalar, returns a scalar - float. If any input is array_like, returns interest payment for each - input element. If multiple inputs are array_like, they all must have - the same shape. - - See Also - -------- - ppmt, pmt, pv - - Notes - ----- - The total payment is made up of payment against principal plus interest. - - ``pmt = ppmt + ipmt`` - - Examples - -------- - What is the amortization schedule for a 1 year loan of $2500 at - 8.24% interest per year compounded monthly? - - >>> principal = 2500.00 - - The 'per' variable represents the periods of the loan. Remember that - financial equations start the period count at 1! - - >>> per = np.arange(1*12) + 1 - >>> ipmt = np.ipmt(0.0824/12, per, 1*12, principal) - >>> ppmt = np.ppmt(0.0824/12, per, 1*12, principal) - - Each element of the sum of the 'ipmt' and 'ppmt' arrays should equal - 'pmt'. - - >>> pmt = np.pmt(0.0824/12, 1*12, principal) - >>> np.allclose(ipmt + ppmt, pmt) - True - - >>> fmt = '{0:2d} {1:8.2f} {2:8.2f} {3:8.2f}' - >>> for payment in per: - ... index = payment - 1 - ... principal = principal + ppmt[index] - ... print(fmt.format(payment, ppmt[index], ipmt[index], principal)) - 1 -200.58 -17.17 2299.42 - 2 -201.96 -15.79 2097.46 - 3 -203.35 -14.40 1894.11 - 4 -204.74 -13.01 1689.37 - 5 -206.15 -11.60 1483.22 - 6 -207.56 -10.18 1275.66 - 7 -208.99 -8.76 1066.67 - 8 -210.42 -7.32 856.25 - 9 -211.87 -5.88 644.38 - 10 -213.32 -4.42 431.05 - 11 -214.79 -2.96 216.26 - 12 -216.26 -1.49 -0.00 - - >>> interestpd = np.sum(ipmt) - >>> np.round(interestpd, 2) - -112.98 - - """ - when = _convert_when(when) - rate, per, nper, pv, fv, when = np.broadcast_arrays(rate, per, nper, - pv, fv, when) - total_pmt = pmt(rate, nper, pv, fv, when) - ipmt = _rbl(rate, per, total_pmt, pv, when)*rate - try: - ipmt = np.where(when == 1, ipmt/(1 + rate), ipmt) - ipmt = np.where(np.logical_and(when == 1, per == 1), 0, ipmt) - except IndexError: - pass - return ipmt - -def _rbl(rate, per, pmt, pv, when): - """ - This function is here to simply have a different name for the 'fv' - function to not interfere with the 'fv' keyword argument within the 'ipmt' - function. It is the 'remaining balance on loan' which might be useful as - it's own function, but is easily calculated with the 'fv' function. - """ - return fv(rate, (per - 1), pmt, pv, when) - -def ppmt(rate, per, nper, pv, fv=0, when='end'): - """ - Compute the payment against loan principal. - - Parameters - ---------- - rate : array_like - Rate of interest (per period) - per : array_like, int - Amount paid against the loan changes. The `per` is the period of - interest. - nper : array_like - Number of compounding periods - pv : array_like - Present value - fv : array_like, optional - Future value - when : {{'begin', 1}, {'end', 0}}, {string, int} - When payments are due ('begin' (1) or 'end' (0)) - - See Also - -------- - pmt, pv, ipmt - - """ - total = pmt(rate, nper, pv, fv, when) - return total - ipmt(rate, per, nper, pv, fv, when) - -def pv(rate, nper, pmt, fv=0, when='end'): - """ - Compute the present value. - - Given: - * a future value, `fv` - * an interest `rate` compounded once per period, of which - there are - * `nper` total - * a (fixed) payment, `pmt`, paid either - * at the beginning (`when` = {'begin', 1}) or the end - (`when` = {'end', 0}) of each period - - Return: - the value now - - Parameters - ---------- - rate : array_like - Rate of interest (per period) - nper : array_like - Number of compounding periods - pmt : array_like - Payment - fv : array_like, optional - Future value - when : {{'begin', 1}, {'end', 0}}, {string, int}, optional - When payments are due ('begin' (1) or 'end' (0)) - - Returns - ------- - out : ndarray, float - Present value of a series of payments or investments. - - Notes - ----- - The present value is computed by solving the equation:: - - fv + - pv*(1 + rate)**nper + - pmt*(1 + rate*when)/rate*((1 + rate)**nper - 1) = 0 - - or, when ``rate = 0``:: - - fv + pv + pmt * nper = 0 - - for `pv`, which is then returned. - - References - ---------- - .. [WRW] Wheeler, D. A., E. Rathke, and R. Weir (Eds.) (2009, May). - Open Document Format for Office Applications (OpenDocument)v1.2, - Part 2: Recalculated Formula (OpenFormula) Format - Annotated Version, - Pre-Draft 12. Organization for the Advancement of Structured Information - Standards (OASIS). Billerica, MA, USA. [ODT Document]. - Available: - http://www.oasis-open.org/committees/documents.php?wg_abbrev=office-formula - OpenDocument-formula-20090508.odt - - Examples - -------- - What is the present value (e.g., the initial investment) - of an investment that needs to total $15692.93 - after 10 years of saving $100 every month? Assume the - interest rate is 5% (annually) compounded monthly. - - >>> np.pv(0.05/12, 10*12, -100, 15692.93) - -100.00067131625819 - - By convention, the negative sign represents cash flow out - (i.e., money not available today). Thus, to end up with - $15,692.93 in 10 years saving $100 a month at 5% annual - interest, one's initial deposit should also be $100. - - If any input is array_like, ``pv`` returns an array of equal shape. - Let's compare different interest rates in the example above: - - >>> a = np.array((0.05, 0.04, 0.03))/12 - >>> np.pv(a, 10*12, -100, 15692.93) - array([ -100.00067132, -649.26771385, -1273.78633713]) - - So, to end up with the same $15692.93 under the same $100 per month - "savings plan," for annual interest rates of 4% and 3%, one would - need initial investments of $649.27 and $1273.79, respectively. - - """ - when = _convert_when(when) - (rate, nper, pmt, fv, when) = map(np.asarray, [rate, nper, pmt, fv, when]) - temp = (1+rate)**nper - fact = np.where(rate == 0, nper, (1+rate*when)*(temp-1)/rate) - return -(fv + pmt*fact)/temp - -# Computed with Sage -# (y + (r + 1)^n*x + p*((r + 1)^n - 1)*(r*w + 1)/r)/(n*(r + 1)^(n - 1)*x - -# p*((r + 1)^n - 1)*(r*w + 1)/r^2 + n*p*(r + 1)^(n - 1)*(r*w + 1)/r + -# p*((r + 1)^n - 1)*w/r) - -def _g_div_gp(r, n, p, x, y, w): - t1 = (r+1)**n - t2 = (r+1)**(n-1) - return ((y + t1*x + p*(t1 - 1)*(r*w + 1)/r) / - (n*t2*x - p*(t1 - 1)*(r*w + 1)/(r**2) + n*p*t2*(r*w + 1)/r + - p*(t1 - 1)*w/r)) - -# Use Newton's iteration until the change is less than 1e-6 -# for all values or a maximum of 100 iterations is reached. -# Newton's rule is -# r_{n+1} = r_{n} - g(r_n)/g'(r_n) -# where -# g(r) is the formula -# g'(r) is the derivative with respect to r. -def rate(nper, pmt, pv, fv, when='end', guess=None, tol=None, maxiter=100): - """ - Compute the rate of interest per period. - - Parameters - ---------- - nper : array_like - Number of compounding periods - pmt : array_like - Payment - pv : array_like - Present value - fv : array_like - Future value - when : {{'begin', 1}, {'end', 0}}, {string, int}, optional - When payments are due ('begin' (1) or 'end' (0)) - guess : Number, optional - Starting guess for solving the rate of interest, default 0.1 - tol : Number, optional - Required tolerance for the solution, default 1e-6 - maxiter : int, optional - Maximum iterations in finding the solution - - Notes - ----- - The rate of interest is computed by iteratively solving the - (non-linear) equation:: - - fv + pv*(1+rate)**nper + pmt*(1+rate*when)/rate * ((1+rate)**nper - 1) = 0 - - for ``rate``. - - References - ---------- - Wheeler, D. A., E. Rathke, and R. Weir (Eds.) (2009, May). Open Document - Format for Office Applications (OpenDocument)v1.2, Part 2: Recalculated - Formula (OpenFormula) Format - Annotated Version, Pre-Draft 12. - Organization for the Advancement of Structured Information Standards - (OASIS). Billerica, MA, USA. [ODT Document]. Available: - http://www.oasis-open.org/committees/documents.php?wg_abbrev=office-formula - OpenDocument-formula-20090508.odt - - """ - when = _convert_when(when) - default_type = Decimal if isinstance(pmt, Decimal) else float - - # Handle casting defaults to Decimal if/when pmt is a Decimal and - # guess and/or tol are not given default values - if guess is None: - guess = default_type('0.1') - - if tol is None: - tol = default_type('1e-6') - - (nper, pmt, pv, fv, when) = map(np.asarray, [nper, pmt, pv, fv, when]) - - rn = guess - iterator = 0 - close = False - while (iterator < maxiter) and not close: - rnp1 = rn - _g_div_gp(rn, nper, pmt, pv, fv, when) - diff = abs(rnp1-rn) - close = np.all(diff < tol) - iterator += 1 - rn = rnp1 - if not close: - # Return nan's in array of the same shape as rn - return np.nan + rn - else: - return rn - -def irr(values): - """ - Return the Internal Rate of Return (IRR). - - This is the "average" periodically compounded rate of return - that gives a net present value of 0.0; for a more complete explanation, - see Notes below. - - :class:`decimal.Decimal` type is not supported. - - Parameters - ---------- - values : array_like, shape(N,) - Input cash flows per time period. By convention, net "deposits" - are negative and net "withdrawals" are positive. Thus, for - example, at least the first element of `values`, which represents - the initial investment, will typically be negative. - - Returns - ------- - out : float - Internal Rate of Return for periodic input values. - - Notes - ----- - The IRR is perhaps best understood through an example (illustrated - using np.irr in the Examples section below). Suppose one invests 100 - units and then makes the following withdrawals at regular (fixed) - intervals: 39, 59, 55, 20. Assuming the ending value is 0, one's 100 - unit investment yields 173 units; however, due to the combination of - compounding and the periodic withdrawals, the "average" rate of return - is neither simply 0.73/4 nor (1.73)^0.25-1. Rather, it is the solution - (for :math:`r`) of the equation: - - .. math:: -100 + \\frac{39}{1+r} + \\frac{59}{(1+r)^2} - + \\frac{55}{(1+r)^3} + \\frac{20}{(1+r)^4} = 0 - - In general, for `values` :math:`= [v_0, v_1, ... v_M]`, - irr is the solution of the equation: [G]_ - - .. math:: \\sum_{t=0}^M{\\frac{v_t}{(1+irr)^{t}}} = 0 - - References - ---------- - .. [G] L. J. Gitman, "Principles of Managerial Finance, Brief," 3rd ed., - Addison-Wesley, 2003, pg. 348. - - Examples - -------- - >>> round(irr([-100, 39, 59, 55, 20]), 5) - 0.28095 - >>> round(irr([-100, 0, 0, 74]), 5) - -0.0955 - >>> round(irr([-100, 100, 0, -7]), 5) - -0.0833 - >>> round(irr([-100, 100, 0, 7]), 5) - 0.06206 - >>> round(irr([-5, 10.5, 1, -8, 1]), 5) - 0.0886 - - (Compare with the Example given for numpy.lib.financial.npv) - - """ - # `np.roots` call is why this function does not support Decimal type. - # - # Ultimately Decimal support needs to be added to np.roots, which has - # greater implications on the entire linear algebra module and how it does - # eigenvalue computations. - res = np.roots(values[::-1]) - mask = (res.imag == 0) & (res.real > 0) - if not mask.any(): - return np.nan - res = res[mask].real - # NPV(rate) = 0 can have more than one solution so we return - # only the solution closest to zero. - rate = 1/res - 1 - rate = rate.item(np.argmin(np.abs(rate))) - return rate - -def npv(rate, values): - """ - Returns the NPV (Net Present Value) of a cash flow series. - - Parameters - ---------- - rate : scalar - The discount rate. - values : array_like, shape(M, ) - The values of the time series of cash flows. The (fixed) time - interval between cash flow "events" must be the same as that for - which `rate` is given (i.e., if `rate` is per year, then precisely - a year is understood to elapse between each cash flow event). By - convention, investments or "deposits" are negative, income or - "withdrawals" are positive; `values` must begin with the initial - investment, thus `values[0]` will typically be negative. - - Returns - ------- - out : float - The NPV of the input cash flow series `values` at the discount - `rate`. - - Notes - ----- - Returns the result of: [G]_ - - .. math :: \\sum_{t=0}^{M-1}{\\frac{values_t}{(1+rate)^{t}}} - - References - ---------- - .. [G] L. J. Gitman, "Principles of Managerial Finance, Brief," 3rd ed., - Addison-Wesley, 2003, pg. 346. - - Examples - -------- - >>> np.npv(0.281,[-100, 39, 59, 55, 20]) - -0.0084785916384548798 - - (Compare with the Example given for numpy.lib.financial.irr) - - """ - values = np.asarray(values) - return (values / (1+rate)**np.arange(0, len(values))).sum(axis=0) - -def mirr(values, finance_rate, reinvest_rate): - """ - Modified internal rate of return. - - Parameters - ---------- - values : array_like - Cash flows (must contain at least one positive and one negative - value) or nan is returned. The first value is considered a sunk - cost at time zero. - finance_rate : scalar - Interest rate paid on the cash flows - reinvest_rate : scalar - Interest rate received on the cash flows upon reinvestment - - Returns - ------- - out : float - Modified internal rate of return - - """ - values = np.asarray(values) - n = values.size - - # Without this explicit cast the 1/(n - 1) computation below - # becomes a float, which causes TypeError when using Decimal - # values. - if isinstance(finance_rate, Decimal): - n = Decimal(n) - - pos = values > 0 - neg = values < 0 - if not (pos.any() and neg.any()): - return np.nan - numer = np.abs(npv(reinvest_rate, values*pos)) - denom = np.abs(npv(finance_rate, values*neg)) - return (numer/denom)**(1/(n - 1))*(1 + reinvest_rate) - 1 diff --git a/numpy/lib/format.py b/numpy/lib/format.py index 23eac7e7dafe..3967b43eefc2 100644 --- a/numpy/lib/format.py +++ b/numpy/lib/format.py @@ -41,10 +41,10 @@ - Is straightforward to reverse engineer. Datasets often live longer than the programs that created them. A competent developer should be able to create a solution in their preferred programming language to - read most ``.npy`` files that he has been given without much + read most ``.npy`` files that they have been given without much documentation. -- Allows memory-mapping of the data. See `open_memmep`. +- Allows memory-mapping of the data. See `open_memmap`. - Can be read from a filelike stream object instead of an actual file. @@ -146,28 +146,33 @@ "The next 4 bytes form a little-endian unsigned int: the length of the header data HEADER_LEN." +Format Version 3.0 +------------------ + +This version replaces the ASCII string (which in practice was latin1) with +a utf8-encoded string, so supports structured types with any unicode field +names. + Notes ----- The ``.npy`` format, including motivation for creating it and a comparison of -alternatives, is described in the `"npy-format" NEP -<http://www.numpy.org/neps/nep-0001-npy-format.html>`_, however details have +alternatives, is described in the +:doc:`"npy-format" NEP <neps:nep-0001-npy-format>`, however details have evolved with time and this document is more current. """ -from __future__ import division, absolute_import, print_function - import numpy -import sys -import io import warnings from numpy.lib.utils import safe_eval -from numpy.compat import asbytes, asstr, isfileobj, long, basestring +from numpy.compat import ( + isfileobj, os_fspath, pickle + ) + -if sys.version_info[0] >= 3: - import pickle -else: - import cPickle as pickle +__all__ = [] + +EXPECTED_KEYS = {'descr', 'fortran_order', 'shape'} MAGIC_PREFIX = b'\x93NUMPY' MAGIC_LEN = len(MAGIC_PREFIX) + 2 ARRAY_ALIGN = 64 # plausible values are powers of 2 between 16 and 4096 @@ -175,10 +180,16 @@ # difference between version 1.0 and 2.0 is a 4 byte (I) header length # instead of 2 bytes (H) allowing storage of large structured arrays +_header_size_info = { + (1, 0): ('<H', 'latin1'), + (2, 0): ('<I', 'latin1'), + (3, 0): ('<I', 'utf8'), +} + def _check_version(version): - if version not in [(1, 0), (2, 0), None]: - msg = "we only support format version (1,0) and (2, 0), not %s" + if version not in [(1, 0), (2, 0), (3, 0), None]: + msg = "we only support format version (1,0), (2,0), and (3,0), not %s" raise ValueError(msg % (version,)) def magic(major, minor): @@ -201,10 +212,7 @@ def magic(major, minor): raise ValueError("major version must be 0 <= major < 256") if minor < 0 or minor > 255: raise ValueError("minor version must be 0 <= minor < 256") - if sys.version_info[0] < 3: - return MAGIC_PREFIX + chr(major) + chr(minor) - else: - return MAGIC_PREFIX + bytes([major, minor]) + return MAGIC_PREFIX + bytes([major, minor]) def read_magic(fp): """ Read the magic string to get the version of the file format. @@ -222,12 +230,19 @@ def read_magic(fp): if magic_str[:-2] != MAGIC_PREFIX: msg = "the magic string is not correct; expected %r, got %r" raise ValueError(msg % (MAGIC_PREFIX, magic_str[:-2])) - if sys.version_info[0] < 3: - major, minor = map(ord, magic_str[-2:]) - else: - major, minor = magic_str[-2:] + major, minor = magic_str[-2:] return major, minor +def _has_metadata(dt): + if dt.metadata is not None: + return True + elif dt.names is not None: + return any(_has_metadata(dt[k]) for k in dt.names) + elif dt.subdtype is not None: + return _has_metadata(dt.base) + else: + return False + def dtype_to_descr(dtype): """ Get a serializable descriptor from the dtype. @@ -251,6 +266,10 @@ def dtype_to_descr(dtype): replicate the input dtype. """ + if _has_metadata(dtype): + warnings.warn("metadata on a dtype may be saved or ignored, but will " + "raise if saved when read. Use another form of storage.", + UserWarning, stacklevel=2) if dtype.names is not None: # This is a record array. The .descr is fine. XXX: parts of the # record array with an empty name, like padding bytes, still get @@ -260,6 +279,62 @@ def dtype_to_descr(dtype): else: return dtype.str +def descr_to_dtype(descr): + """ + Returns a dtype based off the given description. + + This is essentially the reverse of `dtype_to_descr()`. It will remove + the valueless padding fields created by, i.e. simple fields like + dtype('float32'), and then convert the description to its corresponding + dtype. + + Parameters + ---------- + descr : object + The object retrieved by dtype.descr. Can be passed to + `numpy.dtype()` in order to replicate the input dtype. + + Returns + ------- + dtype : dtype + The dtype constructed by the description. + + """ + if isinstance(descr, str): + # No padding removal needed + return numpy.dtype(descr) + elif isinstance(descr, tuple): + # subtype, will always have a shape descr[1] + dt = descr_to_dtype(descr[0]) + return numpy.dtype((dt, descr[1])) + + titles = [] + names = [] + formats = [] + offsets = [] + offset = 0 + for field in descr: + if len(field) == 2: + name, descr_str = field + dt = descr_to_dtype(descr_str) + else: + name, descr_str, shape = field + dt = numpy.dtype((descr_to_dtype(descr_str), shape)) + + # Ignore padding bytes, which will be void bytes with '' as name + # Once support for blank names is removed, only "if name == ''" needed) + is_pad = (name == '' and dt.type is numpy.void and dt.names is None) + if not is_pad: + title, name = name if isinstance(name, tuple) else (None, name) + titles.append(title) + names.append(name) + formats.append(dt) + offsets.append(offset) + offset += dt.itemsize + + return numpy.dtype({'names': names, 'formats': formats, 'titles': titles, + 'offsets': offsets, 'itemsize': offset}) + def header_data_from_array_1_0(array): """ Get the dictionary of header metadata from a numpy.ndarray. @@ -287,6 +362,56 @@ def header_data_from_array_1_0(array): d['descr'] = dtype_to_descr(array.dtype) return d + +def _wrap_header(header, version): + """ + Takes a stringified header, and attaches the prefix and padding to it + """ + import struct + assert version is not None + fmt, encoding = _header_size_info[version] + if not isinstance(header, bytes): # always true on python 3 + header = header.encode(encoding) + hlen = len(header) + 1 + padlen = ARRAY_ALIGN - ((MAGIC_LEN + struct.calcsize(fmt) + hlen) % ARRAY_ALIGN) + try: + header_prefix = magic(*version) + struct.pack(fmt, hlen + padlen) + except struct.error: + msg = "Header length {} too big for version={}".format(hlen, version) + raise ValueError(msg) from None + + # Pad the header with spaces and a final newline such that the magic + # string, the header-length short and the header are aligned on a + # ARRAY_ALIGN byte boundary. This supports memory mapping of dtypes + # aligned up to ARRAY_ALIGN on systems like Linux where mmap() + # offset must be page-aligned (i.e. the beginning of the file). + return header_prefix + header + b' '*padlen + b'\n' + + +def _wrap_header_guess_version(header): + """ + Like `_wrap_header`, but chooses an appropriate version given the contents + """ + try: + return _wrap_header(header, (1, 0)) + except ValueError: + pass + + try: + ret = _wrap_header(header, (2, 0)) + except UnicodeEncodeError: + pass + else: + warnings.warn("Stored array in format 2.0. It can only be" + "read by NumPy >= 1.9", UserWarning, stacklevel=2) + return ret + + header = _wrap_header(header, (3, 0)) + warnings.warn("Stored array in format 3.0. It can only be " + "read by NumPy >= 1.17", UserWarning, stacklevel=2) + return header + + def _write_array_header(fp, d, version=None): """ Write the header for an array and returns the version used @@ -300,48 +425,18 @@ def _write_array_header(fp, d, version=None): None means use oldest that works explicit version will raise a ValueError if the format does not allow saving this data. Default: None - Returns - ------- - version : tuple of int - the file version which needs to be used to store the data """ - import struct header = ["{"] for key, value in sorted(d.items()): # Need to use repr here, since we eval these when reading header.append("'%s': %s, " % (key, repr(value))) header.append("}") header = "".join(header) - header = asbytes(_filter_header(header)) - - hlen = len(header) + 1 # 1 for newline - padlen_v1 = ARRAY_ALIGN - ((MAGIC_LEN + struct.calcsize('<H') + hlen) % ARRAY_ALIGN) - padlen_v2 = ARRAY_ALIGN - ((MAGIC_LEN + struct.calcsize('<I') + hlen) % ARRAY_ALIGN) - - # Which version(s) we write depends on the total header size; v1 has a max of 65535 - if hlen + padlen_v1 < 2**16 and version in (None, (1, 0)): - version = (1, 0) - header_prefix = magic(1, 0) + struct.pack('<H', hlen + padlen_v1) - topad = padlen_v1 - elif hlen + padlen_v2 < 2**32 and version in (None, (2, 0)): - version = (2, 0) - header_prefix = magic(2, 0) + struct.pack('<I', hlen + padlen_v2) - topad = padlen_v2 + if version is None: + header = _wrap_header_guess_version(header) else: - msg = "Header length %s too big for version=%s" - msg %= (hlen, version) - raise ValueError(msg) - - # Pad the header with spaces and a final newline such that the magic - # string, the header-length short and the header are aligned on a - # ARRAY_ALIGN byte boundary. This supports memory mapping of dtypes - # aligned up to ARRAY_ALIGN on systems like Linux where mmap() - # offset must be page-aligned (i.e. the beginning of the file). - header = header + b' '*topad + b'\n' - - fp.write(header_prefix) + header = _wrap_header(header, version) fp.write(header) - return version def write_array_header_1_0(fp, d): """ Write the header for an array using the 1.0 format. @@ -444,7 +539,7 @@ def _filter_header(s): Parameters ---------- - s : byte string + s : string Npy file header. Returns @@ -454,16 +549,11 @@ def _filter_header(s): """ import tokenize - if sys.version_info[0] >= 3: - from io import StringIO - else: - from StringIO import StringIO + from io import StringIO tokens = [] last_token_was_number = False - # adding newline as python 2.7.5 workaround - string = asstr(s) + "\n" - for token in tokenize.generate_tokens(StringIO(string).readline): + for token in tokenize.generate_tokens(StringIO(s).readline): token_type = token[0] token_string = token[1] if (last_token_was_number and @@ -473,8 +563,7 @@ def _filter_header(s): else: tokens.append(token) last_token_was_number = (token_type == tokenize.NUMBER) - # removing newline (see above) as python 2.7.5 workaround - return tokenize.untokenize(tokens)[:-1] + return tokenize.untokenize(tokens) def _read_array_header(fp, version): @@ -484,16 +573,15 @@ def _read_array_header(fp, version): # Read an unsigned, little-endian short int which has the length of the # header. import struct - if version == (1, 0): - hlength_type = '<H' - elif version == (2, 0): - hlength_type = '<I' - else: - raise ValueError("Invalid version %r" % version) + hinfo = _header_size_info.get(version) + if hinfo is None: + raise ValueError("Invalid version {!r}".format(version)) + hlength_type, encoding = hinfo hlength_str = _read_bytes(fp, struct.calcsize(hlength_type), "array header length") header_length = struct.unpack(hlength_type, hlength_str)[0] header = _read_bytes(fp, header_length, "array header") + header = header.decode(encoding) # The header is a pretty-printed string representation of a literal # Python dictionary with trailing newlines padded to a ARRAY_ALIGN byte @@ -501,33 +589,37 @@ def _read_array_header(fp, version): # "shape" : tuple of int # "fortran_order" : bool # "descr" : dtype.descr - header = _filter_header(header) + # Versions (2, 0) and (1, 0) could have been created by a Python 2 + # implementation before header filtering was implemented. + if version <= (2, 0): + header = _filter_header(header) try: d = safe_eval(header) except SyntaxError as e: - msg = "Cannot parse header: %r\nException: %r" - raise ValueError(msg % (header, e)) + msg = "Cannot parse header: {!r}" + raise ValueError(msg.format(header)) from e if not isinstance(d, dict): - msg = "Header is not a dictionary: %r" - raise ValueError(msg % d) - keys = sorted(d.keys()) - if keys != ['descr', 'fortran_order', 'shape']: - msg = "Header does not contain the correct keys: %r" - raise ValueError(msg % (keys,)) + msg = "Header is not a dictionary: {!r}" + raise ValueError(msg.format(d)) + + if EXPECTED_KEYS != d.keys(): + keys = sorted(d.keys()) + msg = "Header does not contain the correct keys: {!r}" + raise ValueError(msg.format(keys)) # Sanity-check the values. if (not isinstance(d['shape'], tuple) or - not numpy.all([isinstance(x, (int, long)) for x in d['shape']])): - msg = "shape is not valid: %r" - raise ValueError(msg % (d['shape'],)) + not all(isinstance(x, int) for x in d['shape'])): + msg = "shape is not valid: {!r}" + raise ValueError(msg.format(d['shape'])) if not isinstance(d['fortran_order'], bool): - msg = "fortran_order is not a valid bool: %r" - raise ValueError(msg % (d['fortran_order'],)) + msg = "fortran_order is not a valid bool: {!r}" + raise ValueError(msg.format(d['fortran_order'])) try: - dtype = numpy.dtype(d['descr']) + dtype = descr_to_dtype(d['descr']) except TypeError as e: - msg = "descr is not a valid dtype descriptor: %r" - raise ValueError(msg % (d['descr'],)) + msg = "descr is not a valid dtype descriptor: {!r}" + raise ValueError(msg.format(d['descr'])) from e return d['shape'], d['fortran_order'], dtype @@ -568,12 +660,7 @@ def write_array(fp, array, version=None, allow_pickle=True, pickle_kwargs=None): """ _check_version(version) - used_ver = _write_array_header(fp, header_data_from_array_1_0(array), - version) - # this warning can be removed when 1.9 has aged enough - if version != (2, 0) and used_ver == (2, 0): - warnings.warn("Stored array in format 2.0. It can only be" - "read by NumPy >= 1.9", UserWarning, stacklevel=2) + _write_array_header(fp, header_data_from_array_1_0(array), version) if array.itemsize == 0: buffersize = 0 @@ -583,14 +670,13 @@ def write_array(fp, array, version=None, allow_pickle=True, pickle_kwargs=None): if array.dtype.hasobject: # We contain Python objects so we cannot write out the data - # directly. Instead, we will pickle it out with version 2 of the - # pickle protocol. + # directly. Instead, we will pickle it out if not allow_pickle: raise ValueError("Object arrays cannot be saved when " "allow_pickle=False") if pickle_kwargs is None: pickle_kwargs = {} - pickle.dump(array, fp, protocol=2, **pickle_kwargs) + pickle.dump(array, fp, protocol=3, **pickle_kwargs) elif array.flags.f_contiguous and not array.flags.c_contiguous: if isfileobj(fp): array.T.tofile(fp) @@ -609,7 +695,7 @@ def write_array(fp, array, version=None, allow_pickle=True, pickle_kwargs=None): fp.write(chunk.tobytes('C')) -def read_array(fp, allow_pickle=True, pickle_kwargs=None): +def read_array(fp, allow_pickle=False, pickle_kwargs=None): """ Read an array from an NPY file. @@ -619,7 +705,11 @@ def read_array(fp, allow_pickle=True, pickle_kwargs=None): If this is not a real file object, then this may take extra memory and time. allow_pickle : bool, optional - Whether to allow reading pickled data. Default: True + Whether to allow writing pickled data. Default: False + + .. versionchanged:: 1.16.3 + Made default False in response to CVE-2019-6446. + pickle_kwargs : dict Additional keyword arguments to pass to pickle.load. These are only useful when loading object arrays saved on Python 2 when using @@ -656,12 +746,10 @@ def read_array(fp, allow_pickle=True, pickle_kwargs=None): try: array = pickle.load(fp, **pickle_kwargs) except UnicodeError as err: - if sys.version_info[0] >= 3: - # Friendlier error message - raise UnicodeError("Unpickling a python object failed: %r\n" - "You may need to pass the encoding= option " - "to numpy.load" % (err,)) - raise + # Friendlier error message + raise UnicodeError("Unpickling a python object failed: %r\n" + "You may need to pass the encoding= option " + "to numpy.load" % (err,)) from err else: if isfileobj(fp): # We can use the fast fromfile() function. @@ -709,7 +797,7 @@ def open_memmap(filename, mode='r+', dtype=None, shape=None, Parameters ---------- - filename : str + filename : str or path-like The name of the file on disk. This may *not* be a file-like object. mode : str, optional @@ -742,17 +830,17 @@ def open_memmap(filename, mode='r+', dtype=None, shape=None, ------ ValueError If the data or the mode is invalid. - IOError + OSError If the file is not found or cannot be opened correctly. See Also -------- - memmap + numpy.memmap """ - if not isinstance(filename, basestring): - raise ValueError("Filename must be a string. Memmap cannot use" - " existing file handles.") + if isfileobj(filename): + raise ValueError("Filename must be a string or a path-like object." + " Memmap cannot use existing file handles.") if 'w' in mode: # We are creating the file, not reading it. @@ -770,20 +858,12 @@ def open_memmap(filename, mode='r+', dtype=None, shape=None, shape=shape, ) # If we got here, then it should be safe to create the file. - fp = open(filename, mode+'b') - try: - used_ver = _write_array_header(fp, d, version) - # this warning can be removed when 1.9 has aged enough - if version != (2, 0) and used_ver == (2, 0): - warnings.warn("Stored array in format 2.0. It can only be" - "read by NumPy >= 1.9", UserWarning, stacklevel=2) + with open(os_fspath(filename), mode+'b') as fp: + _write_array_header(fp, d, version) offset = fp.tell() - finally: - fp.close() else: # Read the header of the file first. - fp = open(filename, 'rb') - try: + with open(os_fspath(filename), 'rb') as fp: version = read_magic(fp) _check_version(version) @@ -792,8 +872,6 @@ def open_memmap(filename, mode='r+', dtype=None, shape=None, msg = "Array can't be memory-mapped: Python objects in dtype." raise ValueError(msg) offset = fp.tell() - finally: - fp.close() if fortran_order: order = 'F' @@ -830,7 +908,7 @@ def _read_bytes(fp, size, error_template="ran out of data"): data += r if len(r) == 0 or len(data) == size: break - except io.BlockingIOError: + except BlockingIOError: pass if len(data) != size: msg = "EOF: reading %s, expected %d bytes got %d" diff --git a/numpy/lib/format.pyi b/numpy/lib/format.pyi new file mode 100644 index 000000000000..092245daf01f --- /dev/null +++ b/numpy/lib/format.pyi @@ -0,0 +1,22 @@ +from typing import Any, List, Set, Literal, Final + +__all__: List[str] + +EXPECTED_KEYS: Final[Set[str]] +MAGIC_PREFIX: Final[bytes] +MAGIC_LEN: Literal[8] +ARRAY_ALIGN: Literal[64] +BUFFER_SIZE: Literal[262144] # 2**18 + +def magic(major, minor): ... +def read_magic(fp): ... +def dtype_to_descr(dtype): ... +def descr_to_dtype(descr): ... +def header_data_from_array_1_0(array): ... +def write_array_header_1_0(fp, d): ... +def write_array_header_2_0(fp, d): ... +def read_array_header_1_0(fp): ... +def read_array_header_2_0(fp): ... +def write_array(fp, array, version=..., allow_pickle=..., pickle_kwargs=...): ... +def read_array(fp, allow_pickle=..., pickle_kwargs=...): ... +def open_memmap(filename, mode=..., dtype=..., shape=..., fortran_order=..., version=...): ... diff --git a/numpy/lib/function_base.py b/numpy/lib/function_base.py index 95edb95fa53a..a215f63d3040 100644 --- a/numpy/lib/function_base.py +++ b/numpy/lib/function_base.py @@ -1,51 +1,43 @@ -from __future__ import division, absolute_import, print_function - -try: - # Accessing collections abstact classes from collections - # has been deprecated since Python 3.3 - import collections.abc as collections_abc -except ImportError: - import collections as collections_abc +import collections.abc +import functools import re import sys import warnings -import operator import numpy as np import numpy.core.numeric as _nx -from numpy.core import linspace, atleast_1d, atleast_2d, transpose +from numpy.core import transpose from numpy.core.numeric import ( - ones, zeros, arange, concatenate, array, asarray, asanyarray, empty, - empty_like, ndarray, around, floor, ceil, take, dot, where, intp, - integer, isscalar, absolute, AxisError + ones, zeros_like, arange, concatenate, array, asarray, asanyarray, empty, + ndarray, take, dot, where, intp, integer, isscalar, absolute ) from numpy.core.umath import ( - pi, multiply, add, arctan2, frompyfunc, cos, less_equal, sqrt, sin, - mod, exp, log10, not_equal, subtract + pi, add, arctan2, frompyfunc, cos, less_equal, sqrt, sin, + mod, exp, not_equal, subtract ) from numpy.core.fromnumeric import ( - ravel, nonzero, sort, partition, mean, any, sum + ravel, nonzero, partition, mean, any, sum ) -from numpy.core.numerictypes import typecodes, number +from numpy.core.numerictypes import typecodes +from numpy.core.overrides import set_module +from numpy.core import overrides +from numpy.core.function_base import add_newdoc from numpy.lib.twodim_base import diag -from .utils import deprecate from numpy.core.multiarray import ( - _insert, add_docstring, digitize, bincount, normalize_axis_index, + _insert, add_docstring, bincount, normalize_axis_index, _monotonicity, interp as compiled_interp, interp_complex as compiled_interp_complex ) from numpy.core.umath import _add_newdoc_ufunc as add_newdoc_ufunc -from numpy.compat import long -from numpy.compat.py3k import basestring -if sys.version_info[0] < 3: - # Force range to be a generator, for np.delete's usage. - range = xrange - import __builtin__ as builtins -else: - import builtins +import builtins # needed in this module for compatibility -from numpy.lib.histograms import histogram, histogramdd +from numpy.lib.histograms import histogram, histogramdd # noqa: F401 + + +array_function_dispatch = functools.partial( + overrides.array_function_dispatch, module='numpy') + __all__ = [ 'select', 'piecewise', 'trim_zeros', 'copy', 'iterable', 'percentile', @@ -58,8 +50,113 @@ 'quantile' ] - -def rot90(m, k=1, axes=(0,1)): +# _QuantileMethods is a dictionary listing all the supported methods to +# compute quantile/percentile. +# +# Below virtual_index refer to the index of the element where the percentile +# would be found in the sorted sample. +# When the sample contains exactly the percentile wanted, the virtual_index is +# an integer to the index of this element. +# When the percentile wanted is in between two elements, the virtual_index +# is made of a integer part (a.k.a 'i' or 'left') and a fractional part +# (a.k.a 'g' or 'gamma') +# +# Each method in _QuantileMethods has two properties +# get_virtual_index : Callable +# The function used to compute the virtual_index. +# fix_gamma : Callable +# A function used for discret methods to force the index to a specific value. +_QuantileMethods = dict( + # --- HYNDMAN and FAN METHODS + # Discrete methods + inverted_cdf=dict( + get_virtual_index=lambda n, quantiles: _inverted_cdf(n, quantiles), + fix_gamma=lambda gamma, _: gamma, # should never be called + ), + averaged_inverted_cdf=dict( + get_virtual_index=lambda n, quantiles: (n * quantiles) - 1, + fix_gamma=lambda gamma, _: _get_gamma_mask( + shape=gamma.shape, + default_value=1., + conditioned_value=0.5, + where=gamma == 0), + ), + closest_observation=dict( + get_virtual_index=lambda n, quantiles: _closest_observation(n, + quantiles), + fix_gamma=lambda gamma, _: gamma, # should never be called + ), + # Continuous methods + interpolated_inverted_cdf=dict( + get_virtual_index=lambda n, quantiles: + _compute_virtual_index(n, quantiles, 0, 1), + fix_gamma=lambda gamma, _: gamma, + ), + hazen=dict( + get_virtual_index=lambda n, quantiles: + _compute_virtual_index(n, quantiles, 0.5, 0.5), + fix_gamma=lambda gamma, _: gamma, + ), + weibull=dict( + get_virtual_index=lambda n, quantiles: + _compute_virtual_index(n, quantiles, 0, 0), + fix_gamma=lambda gamma, _: gamma, + ), + # Default method. + # To avoid some rounding issues, `(n-1) * quantiles` is preferred to + # `_compute_virtual_index(n, quantiles, 1, 1)`. + # They are mathematically equivalent. + linear=dict( + get_virtual_index=lambda n, quantiles: (n - 1) * quantiles, + fix_gamma=lambda gamma, _: gamma, + ), + median_unbiased=dict( + get_virtual_index=lambda n, quantiles: + _compute_virtual_index(n, quantiles, 1 / 3.0, 1 / 3.0), + fix_gamma=lambda gamma, _: gamma, + ), + normal_unbiased=dict( + get_virtual_index=lambda n, quantiles: + _compute_virtual_index(n, quantiles, 3 / 8.0, 3 / 8.0), + fix_gamma=lambda gamma, _: gamma, + ), + # --- OTHER METHODS + lower=dict( + get_virtual_index=lambda n, quantiles: np.floor( + (n - 1) * quantiles).astype(np.intp), + fix_gamma=lambda gamma, _: gamma, + # should never be called, index dtype is int + ), + higher=dict( + get_virtual_index=lambda n, quantiles: np.ceil( + (n - 1) * quantiles).astype(np.intp), + fix_gamma=lambda gamma, _: gamma, + # should never be called, index dtype is int + ), + midpoint=dict( + get_virtual_index=lambda n, quantiles: 0.5 * ( + np.floor((n - 1) * quantiles) + + np.ceil((n - 1) * quantiles)), + fix_gamma=lambda gamma, index: _get_gamma_mask( + shape=gamma.shape, + default_value=0.5, + conditioned_value=0., + where=index % 1 == 0), + ), + nearest=dict( + get_virtual_index=lambda n, quantiles: np.around( + (n - 1) * quantiles).astype(np.intp), + fix_gamma=lambda gamma, _: gamma, + # should never be called, index dtype is int + )) + + +def _rot90_dispatcher(m, k=None, axes=None): + return (m,) + + +@array_function_dispatch(_rot90_dispatcher) +def rot90(m, k=1, axes=(0, 1)): """ Rotate an array by 90 degrees in the plane specified by axes. @@ -90,8 +187,11 @@ def rot90(m, k=1, axes=(0,1)): Notes ----- - rot90(m, k=1, axes=(1,0)) is the reverse of rot90(m, k=1, axes=(0,1)) - rot90(m, k=1, axes=(1,0)) is equivalent to rot90(m, k=-1, axes=(0,1)) + ``rot90(m, k=1, axes=(1,0))`` is the reverse of + ``rot90(m, k=1, axes=(0,1))`` + + ``rot90(m, k=1, axes=(1,0))`` is equivalent to + ``rot90(m, k=-1, axes=(0,1))`` Examples -------- @@ -139,12 +239,17 @@ def rot90(m, k=1, axes=(0,1)): axes_list[axes[0]]) if k == 1: - return transpose(flip(m,axes[1]), axes_list) + return transpose(flip(m, axes[1]), axes_list) else: # k == 3 return flip(transpose(m, axes_list), axes[1]) +def _flip_dispatcher(m, axis=None): + return (m,) + + +@array_function_dispatch(_flip_dispatcher) def flip(m, axis=None): """ Reverse the order of elements in an array along the given axis. @@ -201,12 +306,12 @@ def flip(m, axis=None): [2, 3]], [[4, 5], [6, 7]]]) - >>> flip(A, 0) + >>> np.flip(A, 0) array([[[4, 5], [6, 7]], [[0, 1], [2, 3]]]) - >>> flip(A, 1) + >>> np.flip(A, 1) array([[[2, 3], [0, 1]], [[6, 7], @@ -222,7 +327,7 @@ def flip(m, axis=None): [[1, 0], [3, 2]]]) >>> A = np.random.randn(3,4,5) - >>> np.all(flip(A,2) == A[:,:,::-1,...]) + >>> np.all(np.flip(A,2) == A[:,:,::-1,...]) True """ if not hasattr(m, 'ndim'): @@ -238,6 +343,7 @@ def flip(m, axis=None): return m[indexer] +@set_module('numpy') def iterable(y): """ Check whether or not an object can be iterated over. @@ -261,6 +367,19 @@ def iterable(y): >>> np.iterable(2) False + Notes + ----- + In most cases, the results of ``np.iterable(obj)`` are consistent with + ``isinstance(obj, collections.abc.Iterable)``. One notable exception is + the treatment of 0-dimensional arrays:: + + >>> from collections.abc import Iterable + >>> a = np.array(1.0) # 0-dimensional numpy array + >>> isinstance(a, Iterable) + True + >>> np.iterable(a) + False + """ try: iter(y) @@ -269,6 +388,11 @@ def iterable(y): return True +def _average_dispatcher(a, axis=None, weights=None, returned=None): + return (a, weights) + + +@array_function_dispatch(_average_dispatcher) def average(a, axis=None, weights=None, returned=False): """ Compute the weighted average along the specified axis. @@ -294,22 +418,30 @@ def average(a, axis=None, weights=None, returned=False): The weights array can either be 1-D (in which case its length must be the size of `a` along the given axis) or of the same shape as `a`. If `weights=None`, then all data in `a` are assumed to have a - weight equal to one. + weight equal to one. The 1-D calculation is:: + + avg = sum(a * weights) / sum(weights) + + The only constraint on `weights` is that `sum(weights)` must not be 0. returned : bool, optional Default is `False`. If `True`, the tuple (`average`, `sum_of_weights`) is returned, otherwise only the average is returned. If `weights=None`, `sum_of_weights` is equivalent to the number of elements over which the average is taken. - Returns ------- - average, [sum_of_weights] : array_type or double - Return the average along the specified axis. When returned is `True`, + retval, [sum_of_weights] : array_type or double + Return the average along the specified axis. When `returned` is `True`, return a tuple with the average as the first element and the sum - of the weights as the second element. The return type is `Float` - if `a` is of integer type, otherwise it is of the same type as `a`. - `sum_of_weights` is of the same type as `average`. + of the weights as the second element. `sum_of_weights` is of the + same type as `retval`. The result dtype follows a genereal pattern. + If `weights` is None, the result dtype will be that of `a` , or ``float64`` + if `a` is integral. Otherwise, if `weights` is not None and `a` is non- + integral, the result type will be the type of lowest precision capable of + representing values of both `a` and `weights`. If `a` happens to be + integral, the previous rules still applies but the result dtype will + at least be ``float64``. Raises ------ @@ -326,15 +458,17 @@ def average(a, axis=None, weights=None, returned=False): ma.average : average for masked arrays -- useful if your data contains "missing" values + numpy.result_type : Returns the type that results from applying the + numpy type promotion rules to the arguments. Examples -------- - >>> data = range(1,5) + >>> data = np.arange(1, 5) >>> data - [1, 2, 3, 4] + array([1, 2, 3, 4]) >>> np.average(data) 2.5 - >>> np.average(range(1,11), weights=range(10,0,-1)) + >>> np.average(np.arange(1, 11), weights=np.arange(10, 0, -1)) 4.0 >>> data = np.arange(6).reshape((3,2)) @@ -343,12 +477,17 @@ def average(a, axis=None, weights=None, returned=False): [2, 3], [4, 5]]) >>> np.average(data, axis=1, weights=[1./4, 3./4]) - array([ 0.75, 2.75, 4.75]) + array([0.75, 2.75, 4.75]) >>> np.average(data, weights=[1./4, 3./4]) Traceback (most recent call last): - ... + ... TypeError: Axis must be specified when shapes of a and weights differ. + >>> a = np.ones(5, dtype=np.float128) + >>> w = np.ones(5, dtype=np.complex64) + >>> avg = np.average(a, weights=w) + >>> print(avg.dtype) + complex256 """ a = np.asanyarray(a) @@ -395,6 +534,7 @@ def average(a, axis=None, weights=None, returned=False): return avg +@set_module('numpy') def asarray_chkfinite(a, dtype=None, order=None): """Convert the input to an array, checking for NaNs or Infs. @@ -406,10 +546,13 @@ def asarray_chkfinite(a, dtype=None, order=None): of lists and ndarrays. Success requires no NaNs or Infs. dtype : data-type, optional By default, the data-type is inferred from the input data. - order : {'C', 'F'}, optional - Whether to use row-major (C-style) or - column-major (Fortran-style) memory representation. - Defaults to 'C'. + order : {'C', 'F', 'A', 'K'}, optional + Memory layout. 'A' and 'K' depend on the order of input array a. + 'C' row-major (C-style), + 'F' column-major (Fortran-style) memory representation. + 'A' (any) means 'F' if `a` is Fortran contiguous, 'C' otherwise + 'K' (keep) preserve input order + Defaults to 'C'. Returns ------- @@ -462,6 +605,14 @@ class ndarray is returned. return a +def _piecewise_dispatcher(x, condlist, funclist, *args, **kw): + yield x + # support the undocumented behavior of allowing scalars + if np.iterable(condlist): + yield from condlist + + +@array_function_dispatch(_piecewise_dispatcher) def piecewise(x, condlist, funclist, *args, **kw): """ Evaluate a piecewise-defined function. @@ -540,7 +691,7 @@ def piecewise(x, condlist, funclist, *args, **kw): ``x >= 0``. >>> np.piecewise(x, [x < 0, x >= 0], [lambda x: -x, lambda x: x]) - array([ 2.5, 1.5, 0.5, 0.5, 1.5, 2.5]) + array([2.5, 1.5, 0.5, 0.5, 1.5, 2.5]) Apply the same function to a scalar value. @@ -557,7 +708,7 @@ def piecewise(x, condlist, funclist, *args, **kw): not isinstance(condlist[0], (list, ndarray)) and x.ndim != 0): condlist = [condlist] - condlist = array(condlist, dtype=bool) + condlist = asarray(condlist, dtype=bool) n = len(condlist) if n == n2 - 1: # compute the "otherwise" condition. @@ -570,19 +721,24 @@ def piecewise(x, condlist, funclist, *args, **kw): .format(n, n, n+1) ) - y = zeros(x.shape, x.dtype) - for k in range(n): - item = funclist[k] - if not isinstance(item, collections_abc.Callable): - y[condlist[k]] = item + y = zeros_like(x) + for cond, func in zip(condlist, funclist): + if not isinstance(func, collections.abc.Callable): + y[cond] = func else: - vals = x[condlist[k]] + vals = x[cond] if vals.size > 0: - y[condlist[k]] = item(vals, *args, **kw) + y[cond] = func(vals, *args, **kw) return y +def _select_dispatcher(condlist, choicelist, default=None): + yield from condlist + yield from choicelist + + +@array_function_dispatch(_select_dispatcher) def select(condlist, choicelist, default=0): """ Return an array drawn from elements in choicelist, depending on conditions. @@ -613,11 +769,16 @@ def select(condlist, choicelist, default=0): Examples -------- - >>> x = np.arange(10) - >>> condlist = [x<3, x>5] + >>> x = np.arange(6) + >>> condlist = [x<3, x>3] + >>> choicelist = [x, x**2] + >>> np.select(condlist, choicelist, 42) + array([ 0, 1, 2, 42, 16, 25]) + + >>> condlist = [x<=4, x>3] >>> choicelist = [x, x**2] - >>> np.select(condlist, choicelist) - array([ 0, 1, 2, 0, 0, 0, 36, 49, 64, 81]) + >>> np.select(condlist, choicelist, 55) + array([ 0, 1, 2, 3, 4, 25]) """ # Check the size of condlist and choicelist are the same, or abort. @@ -627,18 +788,25 @@ def select(condlist, choicelist, default=0): # Now that the dtype is known, handle the deprecated select([], []) case if len(condlist) == 0: - # 2014-02-24, 1.9 - warnings.warn("select with an empty condition list is not possible" - "and will be deprecated", - DeprecationWarning, stacklevel=2) - return np.asarray(default)[()] + raise ValueError("select with an empty condition list is not possible") choicelist = [np.asarray(choice) for choice in choicelist] - choicelist.append(np.asarray(default)) + + try: + intermediate_dtype = np.result_type(*choicelist) + except TypeError as e: + msg = f'Choicelist elements do not have a common dtype: {e}' + raise TypeError(msg) from None + default_array = np.asarray(default) + choicelist.append(default_array) # need to get the result type before broadcasting for correct scalar # behaviour - dtype = np.result_type(*choicelist) + try: + dtype = np.result_type(intermediate_dtype, default_array) + except TypeError as e: + msg = f'Choicelists and default value do not have a common dtype: {e}' + raise TypeError(msg) from None # Convert conditions to arrays and broadcast conditions and choices # as the shape is needed for the result. Doing it separately optimizes @@ -647,25 +815,10 @@ def select(condlist, choicelist, default=0): choicelist = np.broadcast_arrays(*choicelist) # If cond array is not an ndarray in boolean format or scalar bool, abort. - deprecated_ints = False - for i in range(len(condlist)): - cond = condlist[i] + for i, cond in enumerate(condlist): if cond.dtype.type is not np.bool_: - if np.issubdtype(cond.dtype, np.integer): - # A previous implementation accepted int ndarrays accidentally. - # Supported here deliberately, but deprecated. - condlist[i] = condlist[i].astype(bool) - deprecated_ints = True - else: - raise ValueError( - 'invalid entry {} in condlist: should be boolean ndarray'.format(i)) - - if deprecated_ints: - # 2014-02-24, 1.9 - msg = "select condlists containing integer ndarrays is deprecated " \ - "and will be removed in the future. Use `.astype(bool)` to " \ - "convert to bools." - warnings.warn(msg, DeprecationWarning, stacklevel=2) + raise TypeError( + 'invalid entry {} in condlist: should be boolean ndarray'.format(i)) if choicelist[0].ndim == 0: # This may be common, so avoid the call. @@ -686,7 +839,12 @@ def select(condlist, choicelist, default=0): return result -def copy(a, order='K'): +def _copy_dispatcher(a, order=None, subok=None): + return (a,) + + +@array_function_dispatch(_copy_dispatcher) +def copy(a, order='K', subok=False): """ Return an array copy of the given object. @@ -701,12 +859,21 @@ def copy(a, order='K'): as possible. (Note that this function and :meth:`ndarray.copy` are very similar, but have different default values for their order= arguments.) + subok : bool, optional + If True, then sub-classes will be passed-through, otherwise the + returned array will be forced to be a base-class array (defaults to False). + + .. versionadded:: 1.19.0 Returns ------- arr : ndarray Array interpretation of `a`. + See Also + -------- + ndarray.copy : Preferred method for creating an array copy + Notes ----- This is equivalent to: @@ -729,13 +896,54 @@ def copy(a, order='K'): >>> x[0] == z[0] False + Note that, np.copy clears previously set WRITEABLE=False flag. + + >>> a = np.array([1, 2, 3]) + >>> a.flags["WRITEABLE"] = False + >>> b = np.copy(a) + >>> b.flags["WRITEABLE"] + True + >>> b[0] = 3 + >>> b + array([3, 2, 3]) + + Note that np.copy is a shallow copy and will not copy object + elements within arrays. This is mainly important for arrays + containing Python objects. The new array will contain the + same object which may lead to surprises if that object can + be modified (is mutable): + + >>> a = np.array([1, 'm', [2, 3, 4]], dtype=object) + >>> b = np.copy(a) + >>> b[2][0] = 10 + >>> a + array([1, 'm', list([10, 3, 4])], dtype=object) + + To ensure all elements within an ``object`` array are copied, + use `copy.deepcopy`: + + >>> import copy + >>> a = np.array([1, 'm', [2, 3, 4]], dtype=object) + >>> c = copy.deepcopy(a) + >>> c[2][0] = 10 + >>> c + array([1, 'm', list([10, 3, 4])], dtype=object) + >>> a + array([1, 'm', list([2, 3, 4])], dtype=object) + """ - return array(a, order=order, copy=True) + return array(a, order=order, subok=subok, copy=True) # Basic operations -def gradient(f, *varargs, **kwargs): +def _gradient_dispatcher(f, *varargs, axis=None, edge_order=None): + yield f + yield from varargs + + +@array_function_dispatch(_gradient_dispatcher) +def gradient(f, *varargs, axis=None, edge_order=1): """ Return the gradient of an N-dimensional array. @@ -780,7 +988,7 @@ def gradient(f, *varargs, **kwargs): Returns ------- gradient : ndarray or list of ndarray - A set of ndarrays (or a single ndarray if there is only one dimension) + A list of ndarrays (or a single ndarray if there is only one dimension) corresponding to the derivatives of f with respect to each dimension. Each derivative has the same shape as f. @@ -788,9 +996,9 @@ def gradient(f, *varargs, **kwargs): -------- >>> f = np.array([1, 2, 4, 7, 11, 16], dtype=float) >>> np.gradient(f) - array([ 1. , 1.5, 2.5, 3.5, 4.5, 5. ]) + array([1. , 1.5, 2.5, 3.5, 4.5, 5. ]) >>> np.gradient(f, 2) - array([ 0.5 , 0.75, 1.25, 1.75, 2.25, 2.5 ]) + array([0.5 , 0.75, 1.25, 1.75, 2.25, 2.5 ]) Spacing can be also specified with an array that represents the coordinates of the values F along the dimensions. @@ -798,13 +1006,13 @@ def gradient(f, *varargs, **kwargs): >>> x = np.arange(f.size) >>> np.gradient(f, x) - array([ 1. , 1.5, 2.5, 3.5, 4.5, 5. ]) + array([1. , 1.5, 2.5, 3.5, 4.5, 5. ]) Or a non uniform one: >>> x = np.array([0., 1., 1.5, 3.5, 4., 6.], dtype=float) >>> np.gradient(f, x) - array([ 1. , 3. , 3.5, 6.7, 6.9, 2.5]) + array([1. , 3. , 3.5, 6.7, 6.9, 2.5]) For two dimensional arrays, the return will be two arrays ordered by axis. In this example the first array stands for the gradient in @@ -812,8 +1020,8 @@ def gradient(f, *varargs, **kwargs): >>> np.gradient(np.array([[1, 2, 6], [3, 4, 5]], dtype=float)) [array([[ 2., 2., -1.], - [ 2., 2., -1.]]), array([[ 1. , 2.5, 4. ], - [ 1. , 1. , 1. ]])] + [ 2., 2., -1.]]), array([[1. , 2.5, 4. ], + [1. , 1. , 1. ]])] In this example the spacing is also specified: uniform for axis=0 and non uniform for axis=1 @@ -822,17 +1030,17 @@ def gradient(f, *varargs, **kwargs): >>> y = [1., 1.5, 3.5] >>> np.gradient(np.array([[1, 2, 6], [3, 4, 5]], dtype=float), dx, y) [array([[ 1. , 1. , -0.5], - [ 1. , 1. , -0.5]]), array([[ 2. , 2. , 2. ], - [ 2. , 1.7, 0.5]])] + [ 1. , 1. , -0.5]]), array([[2. , 2. , 2. ], + [2. , 1.7, 0.5]])] It is possible to specify how boundaries are treated using `edge_order` >>> x = np.array([0, 1, 2, 3, 4]) >>> f = x**2 >>> np.gradient(f, edge_order=1) - array([ 1., 2., 4., 6., 7.]) + array([1., 2., 4., 6., 7.]) >>> np.gradient(f, edge_order=2) - array([-0., 2., 4., 6., 8.]) + array([0., 2., 4., 6., 8.]) The `axis` keyword can be used to specify a subset of axes of which the gradient is calculated @@ -912,11 +1120,10 @@ def gradient(f, *varargs, **kwargs): f = np.asanyarray(f) N = f.ndim # number of dimensions - axes = kwargs.pop('axis', None) - if axes is None: + if axis is None: axes = tuple(range(N)) else: - axes = _nx.normalize_axis_tuple(axes, N) + axes = _nx.normalize_axis_tuple(axis, N) len_axes = len(axes) n = len(varargs) @@ -930,13 +1137,18 @@ def gradient(f, *varargs, **kwargs): # scalar or 1d array for each axis dx = list(varargs) for i, distances in enumerate(dx): - if np.ndim(distances) == 0: + distances = np.asanyarray(distances) + if distances.ndim == 0: continue - elif np.ndim(distances) != 1: + elif distances.ndim != 1: raise ValueError("distances must be either scalars or 1d") if len(distances) != f.shape[axes[i]]: raise ValueError("when 1d, distances must match " "the length of the corresponding dimension") + if np.issubdtype(distances.dtype, np.integer): + # Convert numpy integer types to float64 to avoid modular + # arithmetic in np.diff(distances). + distances = distances.astype(np.float64) diffx = np.diff(distances) # if distances are constant reduce to the scalar case # since it brings a consistent speedup @@ -946,10 +1158,6 @@ def gradient(f, *varargs, **kwargs): else: raise TypeError("invalid number of arguments") - edge_order = kwargs.pop('edge_order', 1) - if kwargs: - raise TypeError('"{}" are not valid keyword arguments.'.format( - '", "'.join(kwargs.keys()))) if edge_order > 2: raise ValueError("'edge_order' greater than 2 not supported") @@ -975,8 +1183,12 @@ def gradient(f, *varargs, **kwargs): elif np.issubdtype(otype, np.inexact): pass else: - # all other types convert to floating point - otype = np.double + # All other types convert to floating point. + # First check if f is a numpy integer type; if so, convert f to float64 + # to avoid modular arithmetic when computing the changes in f. + if np.issubdtype(otype, np.integer): + f = f.astype(np.float64) + otype = np.float64 for axis, ax_dx in zip(axes, dx): if f.shape[axis] < edge_order + 1: @@ -1076,11 +1288,16 @@ def gradient(f, *varargs, **kwargs): return outvals -def diff(a, n=1, axis=-1): +def _diff_dispatcher(a, n=None, axis=None, prepend=None, append=None): + return (a, prepend, append) + + +@array_function_dispatch(_diff_dispatcher) +def diff(a, n=1, axis=-1, prepend=np._NoValue, append=np._NoValue): """ Calculate the n-th discrete difference along the given axis. - The first difference is given by ``out[n] = a[n+1] - a[n]`` along + The first difference is given by ``out[i] = a[i+1] - a[i]`` along the given axis, higher differences are calculated by using `diff` recursively. @@ -1094,6 +1311,14 @@ def diff(a, n=1, axis=-1): axis : int, optional The axis along which the difference is taken, default is the last axis. + prepend, append : array_like, optional + Values to prepend or append to `a` along axis prior to + performing the difference. Scalar values are expanded to + arrays with length 1 in the direction of axis and the shape + of the input array in along all other axes. Otherwise the + dimension and shape must match `a` except along axis. + + .. versionadded:: 1.16.0 Returns ------- @@ -1123,7 +1348,7 @@ def diff(a, n=1, axis=-1): >>> np.diff(u8_arr) array([255], dtype=uint8) >>> u8_arr[1,...] - u8_arr[0,...] - array(255, np.uint8) + 255 If this is not desirable, then the array should be cast to a larger integer type first: @@ -1160,8 +1385,32 @@ def diff(a, n=1, axis=-1): a = asanyarray(a) nd = a.ndim + if nd == 0: + raise ValueError("diff requires input that is at least one dimensional") axis = normalize_axis_index(axis, nd) + combined = [] + if prepend is not np._NoValue: + prepend = np.asanyarray(prepend) + if prepend.ndim == 0: + shape = list(a.shape) + shape[axis] = 1 + prepend = np.broadcast_to(prepend, tuple(shape)) + combined.append(prepend) + + combined.append(a) + + if append is not np._NoValue: + append = np.asanyarray(append) + if append.ndim == 0: + shape = list(a.shape) + shape[axis] = 1 + append = np.broadcast_to(append, tuple(shape)) + combined.append(append) + + if len(combined) > 1: + a = np.concatenate(combined, axis) + slice1 = [slice(None)] * nd slice2 = [slice(None)] * nd slice1[axis] = slice(1, None) @@ -1176,9 +1425,14 @@ def diff(a, n=1, axis=-1): return a +def _interp_dispatcher(x, xp, fp, left=None, right=None, period=None): + return (x, xp, fp) + + +@array_function_dispatch(_interp_dispatcher) def interp(x, xp, fp, left=None, right=None, period=None): """ - One-dimensional linear interpolation. + One-dimensional linear interpolation for monotonically increasing sample points. Returns the one-dimensional piecewise linear interpolant to a function with given discrete data points (`xp`, `fp`), evaluated at `x`. @@ -1221,11 +1475,19 @@ def interp(x, xp, fp, left=None, right=None, period=None): If `xp` or `fp` are not 1-D sequences If `period == 0` - Notes - ----- - Does not check that the x-coordinate sequence `xp` is increasing. - If `xp` is not increasing, the results are nonsense. - A simple check for increasing is:: + See Also + -------- + scipy.interpolate + + Warnings + -------- + The x-coordinate sequence is expected to be increasing, but this is not + explicitly enforced. However, if the sequence `xp` is non-increasing, + interpolation results are meaningless. + + Note that, since NaN is unsortable, `xp` also cannot contain NaNs. + + A simple check for `xp` being strictly increasing is:: np.all(np.diff(xp) > 0) @@ -1236,7 +1498,7 @@ def interp(x, xp, fp, left=None, right=None, period=None): >>> np.interp(2.5, xp, fp) 1.0 >>> np.interp([0, 1, 1.5, 2.72, 3.14], xp, fp) - array([ 3. , 3. , 2.5 , 0.56, 0. ]) + array([3. , 3. , 2.5 , 0.56, 0. ]) >>> UNDEF = -99.0 >>> np.interp(3.14, xp, fp, right=UNDEF) -99.0 @@ -1260,7 +1522,7 @@ def interp(x, xp, fp, left=None, right=None, period=None): >>> xp = [190, -190, 350, -350] >>> fp = [5, 10, 3, 4] >>> np.interp(x, xp, fp, period=360) - array([7.5, 5., 8.75, 6.25, 3., 3.25, 3.5, 3.75]) + array([7.5 , 5. , 8.75, 6.25, 3. , 3.25, 3.5 , 3.75]) Complex interpolation: @@ -1268,7 +1530,7 @@ def interp(x, xp, fp, left=None, right=None, period=None): >>> xp = [2,3,5] >>> fp = [1.0j, 0, 2+3j] >>> np.interp(x, xp, fp) - array([ 0.+1.j , 1.+1.5j]) + array([0.+1.j , 1.+1.5j]) """ @@ -1308,7 +1570,12 @@ def interp(x, xp, fp, left=None, right=None, period=None): return interp_func(x, xp, fp, left, right) -def angle(z, deg=0): +def _angle_dispatcher(z, deg=None): + return (z,) + + +@array_function_dispatch(_angle_dispatcher) +def angle(z, deg=False): """ Return the angle of the complex argument. @@ -1322,51 +1589,78 @@ def angle(z, deg=0): Returns ------- angle : ndarray or scalar - The counterclockwise angle from the positive real axis on - the complex plane, with dtype as numpy.float64. + The counterclockwise angle from the positive real axis on the complex + plane in the range ``(-pi, pi]``, with dtype as numpy.float64. + + .. versionchanged:: 1.16.0 + This function works on subclasses of ndarray like `ma.array`. See Also -------- arctan2 absolute + Notes + ----- + Although the angle of the complex number 0 is undefined, ``numpy.angle(0)`` + returns the value 0. + Examples -------- >>> np.angle([1.0, 1.0j, 1+1j]) # in radians - array([ 0. , 1.57079633, 0.78539816]) + array([ 0. , 1.57079633, 0.78539816]) # may vary >>> np.angle(1+1j, deg=True) # in degrees 45.0 """ - if deg: - fact = 180/pi - else: - fact = 1.0 - z = asarray(z) - if (issubclass(z.dtype.type, _nx.complexfloating)): + z = asanyarray(z) + if issubclass(z.dtype.type, _nx.complexfloating): zimag = z.imag zreal = z.real else: zimag = 0 zreal = z - return arctan2(zimag, zreal) * fact + + a = arctan2(zimag, zreal) + if deg: + a *= 180/pi + return a -def unwrap(p, discont=pi, axis=-1): - """ - Unwrap by changing deltas between values to 2*pi complement. +def _unwrap_dispatcher(p, discont=None, axis=None, *, period=None): + return (p,) + - Unwrap radian phase `p` by changing absolute jumps greater than - `discont` to their 2*pi complement along the given axis. +@array_function_dispatch(_unwrap_dispatcher) +def unwrap(p, discont=None, axis=-1, *, period=2*pi): + r""" + Unwrap by taking the complement of large deltas with respect to the period. + + This unwraps a signal `p` by changing elements which have an absolute + difference from their predecessor of more than ``max(discont, period/2)`` + to their `period`-complementary values. + + For the default case where `period` is :math:`2\pi` and `discont` is + :math:`\pi`, this unwraps a radian phase `p` such that adjacent differences + are never greater than :math:`\pi` by adding :math:`2k\pi` for some + integer :math:`k`. Parameters ---------- p : array_like Input array. discont : float, optional - Maximum discontinuity between values, default is ``pi``. + Maximum discontinuity between values, default is ``period/2``. + Values below ``period/2`` are treated as if they were ``period/2``. + To have an effect different from the default, `discont` should be + larger than ``period/2``. axis : int, optional Axis along which unwrap will operate, default is the last axis. + period: float, optional + Size of the range over which the input wraps. By default, it is + ``2 pi``. + + .. versionadded:: 1.21.0 Returns ------- @@ -1379,35 +1673,65 @@ def unwrap(p, discont=pi, axis=-1): Notes ----- - If the discontinuity in `p` is smaller than ``pi``, but larger than - `discont`, no unwrapping is done because taking the 2*pi complement - would only make the discontinuity larger. + If the discontinuity in `p` is smaller than ``period/2``, + but larger than `discont`, no unwrapping is done because taking + the complement would only make the discontinuity larger. Examples -------- >>> phase = np.linspace(0, np.pi, num=5) >>> phase[3:] += np.pi >>> phase - array([ 0. , 0.78539816, 1.57079633, 5.49778714, 6.28318531]) + array([ 0. , 0.78539816, 1.57079633, 5.49778714, 6.28318531]) # may vary >>> np.unwrap(phase) - array([ 0. , 0.78539816, 1.57079633, -0.78539816, 0. ]) - + array([ 0. , 0.78539816, 1.57079633, -0.78539816, 0. ]) # may vary + >>> np.unwrap([0, 1, 2, -1, 0], period=4) + array([0, 1, 2, 3, 4]) + >>> np.unwrap([ 1, 2, 3, 4, 5, 6, 1, 2, 3], period=6) + array([1, 2, 3, 4, 5, 6, 7, 8, 9]) + >>> np.unwrap([2, 3, 4, 5, 2, 3, 4, 5], period=4) + array([2, 3, 4, 5, 6, 7, 8, 9]) + >>> phase_deg = np.mod(np.linspace(0 ,720, 19), 360) - 180 + >>> np.unwrap(phase_deg, period=360) + array([-180., -140., -100., -60., -20., 20., 60., 100., 140., + 180., 220., 260., 300., 340., 380., 420., 460., 500., + 540.]) """ p = asarray(p) nd = p.ndim dd = diff(p, axis=axis) + if discont is None: + discont = period/2 slice1 = [slice(None, None)]*nd # full slices slice1[axis] = slice(1, None) slice1 = tuple(slice1) - ddmod = mod(dd + pi, 2*pi) - pi - _nx.copyto(ddmod, pi, where=(ddmod == -pi) & (dd > 0)) + dtype = np.result_type(dd, period) + if _nx.issubdtype(dtype, _nx.integer): + interval_high, rem = divmod(period, 2) + boundary_ambiguous = rem == 0 + else: + interval_high = period / 2 + boundary_ambiguous = True + interval_low = -interval_high + ddmod = mod(dd - interval_low, period) + interval_low + if boundary_ambiguous: + # for `mask = (abs(dd) == period/2)`, the above line made + # `ddmod[mask] == -period/2`. correct these such that + # `ddmod[mask] == sign(dd[mask])*period/2`. + _nx.copyto(ddmod, interval_high, + where=(ddmod == interval_low) & (dd > 0)) ph_correct = ddmod - dd _nx.copyto(ph_correct, 0, where=abs(dd) < discont) - up = array(p, copy=True, dtype='d') + up = array(p, copy=True, dtype=dtype) up[slice1] = p[slice1] + ph_correct.cumsum(axis) return up +def _sort_complex(a): + return (a,) + + +@array_function_dispatch(_sort_complex) def sort_complex(a): """ Sort a complex array using the real part first, then the imaginary part. @@ -1425,10 +1749,10 @@ def sort_complex(a): Examples -------- >>> np.sort_complex([5, 3, 6, 2, 1]) - array([ 1.+0.j, 2.+0.j, 3.+0.j, 5.+0.j, 6.+0.j]) + array([1.+0.j, 2.+0.j, 3.+0.j, 5.+0.j, 6.+0.j]) >>> np.sort_complex([1 + 2j, 2 - 1j, 3 - 2j, 3 - 3j, 3 + 5j]) - array([ 1.+2.j, 2.-1.j, 3.-3.j, 3.-2.j, 3.+5.j]) + array([1.+2.j, 2.-1.j, 3.-3.j, 3.-2.j, 3.+5.j]) """ b = array(a, copy=True) @@ -1444,6 +1768,11 @@ def sort_complex(a): return b +def _trim_zeros(filt, trim=None): + return (filt,) + + +@array_function_dispatch(_trim_zeros) def trim_zeros(filt, trim='fb'): """ Trim the leading and/or trailing zeros from a 1-D array or sequence. @@ -1469,7 +1798,7 @@ def trim_zeros(filt, trim='fb'): array([1, 2, 3, 0, 2, 1]) >>> np.trim_zeros(a, 'b') - array([0, 0, 0, 1, 2, 3, 0, 2, 1]) + array([0, 0, 0, ..., 0, 2, 1]) The input data type is preserved, list/tuple in means list/tuple out. @@ -1477,6 +1806,7 @@ def trim_zeros(filt, trim='fb'): [1, 2] """ + first = 0 trim = trim.upper() if 'F' in trim: @@ -1495,24 +1825,11 @@ def trim_zeros(filt, trim='fb'): return filt[first:last] -@deprecate -def unique(x): - """ - This function is deprecated. Use numpy.lib.arraysetops.unique() - instead. - """ - try: - tmp = x.flatten() - if tmp.size == 0: - return tmp - tmp.sort() - idx = concatenate(([True], tmp[1:] != tmp[:-1])) - return tmp[idx] - except AttributeError: - items = sorted(set(x)) - return asarray(items) +def _extract_dispatcher(condition, arr): + return (condition, arr) +@array_function_dispatch(_extract_dispatcher) def extract(condition, arr): """ Return the elements of an array that satisfy some condition. @@ -1564,6 +1881,11 @@ def extract(condition, arr): return _nx.take(ravel(arr), nonzero(ravel(condition))[0]) +def _place_dispatcher(arr, mask, vals): + return (arr, mask, vals) + + +@array_function_dispatch(_place_dispatcher) def place(arr, mask, vals): """ Change elements of an array based on conditional and input values. @@ -1649,7 +1971,7 @@ def disp(mesg, device=None, linefeed=True): return -# See http://docs.scipy.org/doc/numpy/reference/c-api.generalized-ufuncs.html +# See https://docs.scipy.org/doc/numpy/reference/c-api.generalized-ufuncs.html _DIMENSION_NAME = r'\w+' _CORE_DIMENSION_LIST = '(?:{0:}(?:,{0:})*)?'.format(_DIMENSION_NAME) _ARGUMENT = r'\({}\)'.format(_CORE_DIMENSION_LIST) @@ -1672,6 +1994,8 @@ def _parse_gufunc_signature(signature): Tuple of input and output core dimensions parsed from the signature, each of the form List[Tuple[str, ...]]. """ + signature = re.sub(r'\s+', '', signature) + if not re.match(_SIGNATURE, signature): raise ValueError( 'not a valid gufunc signature: {}'.format(signature)) @@ -1749,15 +2073,24 @@ def _calculate_shapes(broadcast_shape, dim_sizes, list_of_core_dims): for core_dims in list_of_core_dims] -def _create_arrays(broadcast_shape, dim_sizes, list_of_core_dims, dtypes): +def _create_arrays(broadcast_shape, dim_sizes, list_of_core_dims, dtypes, + results=None): """Helper for creating output arrays in vectorize.""" shapes = _calculate_shapes(broadcast_shape, dim_sizes, list_of_core_dims) - arrays = tuple(np.empty(shape, dtype=dtype) - for shape, dtype in zip(shapes, dtypes)) + if dtypes is None: + dtypes = [None] * len(shapes) + if results is None: + arrays = tuple(np.empty(shape=shape, dtype=dtype) + for shape, dtype in zip(shapes, dtypes)) + else: + arrays = tuple(np.empty_like(result, shape=shape, dtype=dtype) + for result, shape, dtype + in zip(results, shapes, dtypes)) return arrays -class vectorize(object): +@set_module('numpy') +class vectorize: """ vectorize(pyfunc, otypes=None, doc=None, excluded=None, cache=False, signature=None) @@ -1765,8 +2098,8 @@ class vectorize(object): Generalized function class. Define a vectorized function which takes a nested sequence of objects or - numpy arrays as inputs and returns an single or tuple of numpy array as - output. The vectorized function evaluates `pyfunc` over successive tuples + numpy arrays as inputs and returns a single numpy array or a tuple of numpy + arrays. The vectorized function evaluates `pyfunc` over successive tuples of the input arrays like the python map function, except it uses the broadcasting rules of numpy. @@ -1783,7 +2116,7 @@ class vectorize(object): typecode characters or a list of data type specifiers. There should be one data type specifier for each output. doc : str, optional - The docstring for the function. If `None`, the docstring will be the + The docstring for the function. If None, the docstring will be the ``pyfunc.__doc__``. excluded : set, optional Set of strings or integers representing the positional or keyword @@ -1793,8 +2126,8 @@ class vectorize(object): .. versionadded:: 1.7.0 cache : bool, optional - If `True`, then cache the first function call that determines the number - of outputs if `otypes` is not provided. + If `True`, then cache the first function call that determines the number + of outputs if `otypes` is not provided. .. versionadded:: 1.7.0 @@ -1812,6 +2145,29 @@ class vectorize(object): vectorized : callable Vectorized function. + See Also + -------- + frompyfunc : Takes an arbitrary Python function and returns a ufunc + + Notes + ----- + The `vectorize` function is provided primarily for convenience, not for + performance. The implementation is essentially a for loop. + + If `otypes` is not specified, then a call to the function with the + first argument will be used to determine the number of outputs. The + results of this call will be cached if `cache` is `True` to prevent + calling the function twice. However, to implement the cache, the + original function must be wrapped which will slow down subsequent + calls, so only do this if your function is expensive. + + The new keyword argument interface and `excluded` argument support + further degrades performance. + + References + ---------- + .. [1] :doc:`/reference/c-api/generalized-ufuncs` + Examples -------- >>> def myfunc(a, b): @@ -1839,11 +2195,11 @@ class vectorize(object): >>> out = vfunc([1, 2, 3, 4], 2) >>> type(out[0]) - <type 'numpy.int32'> + <class 'numpy.int64'> >>> vfunc = np.vectorize(myfunc, otypes=[float]) >>> out = vfunc([1, 2, 3, 4], 2) >>> type(out[0]) - <type 'numpy.float64'> + <class 'numpy.float64'> The `excluded` argument can be used to prevent vectorizing over certain arguments. This can be useful for array-like arguments of a fixed length @@ -1871,7 +2227,7 @@ class vectorize(object): >>> import scipy.stats >>> pearsonr = np.vectorize(scipy.stats.pearsonr, - ... signature='(n),(n)->(),()') + ... signature='(n),(n)->(),()') >>> pearsonr([[0, 1, 2, 3]], [[1, 2, 3, 4], [4, 3, 2, 1]]) (array([ 1., -1.]), array([ 0., 0.])) @@ -1879,42 +2235,18 @@ class vectorize(object): >>> convolve = np.vectorize(np.convolve, signature='(n),(m)->(k)') >>> convolve(np.eye(4), [1, 2, 1]) - array([[ 1., 2., 1., 0., 0., 0.], - [ 0., 1., 2., 1., 0., 0.], - [ 0., 0., 1., 2., 1., 0.], - [ 0., 0., 0., 1., 2., 1.]]) - - See Also - -------- - frompyfunc : Takes an arbitrary Python function and returns a ufunc - - Notes - ----- - The `vectorize` function is provided primarily for convenience, not for - performance. The implementation is essentially a for loop. - - If `otypes` is not specified, then a call to the function with the - first argument will be used to determine the number of outputs. The - results of this call will be cached if `cache` is `True` to prevent - calling the function twice. However, to implement the cache, the - original function must be wrapped which will slow down subsequent - calls, so only do this if your function is expensive. - - The new keyword argument interface and `excluded` argument support - further degrades performance. + array([[1., 2., 1., 0., 0., 0.], + [0., 1., 2., 1., 0., 0.], + [0., 0., 1., 2., 1., 0.], + [0., 0., 0., 1., 2., 1.]]) - References - ---------- - .. [1] NumPy Reference, section `Generalized Universal Function API - <http://docs.scipy.org/doc/numpy/reference/c-api.generalized-ufuncs.html>`_. """ - def __init__(self, pyfunc, otypes=None, doc=None, excluded=None, cache=False, signature=None): self.pyfunc = pyfunc self.cache = cache self.signature = signature - self._ufunc = None # Caching to improve default performance + self._ufunc = {} # Caching to improve default performance if doc is None: self.__doc__ = pyfunc.__doc__ @@ -1979,14 +2311,22 @@ def _get_ufunc_and_otypes(self, func, args): if self.otypes is not None: otypes = self.otypes - nout = len(otypes) - # Note logic here: We only *use* self._ufunc if func is self.pyfunc - # even though we set self._ufunc regardless. - if func is self.pyfunc and self._ufunc is not None: - ufunc = self._ufunc + # self._ufunc is a dictionary whose keys are the number of + # arguments (i.e. len(args)) and whose values are ufuncs created + # by frompyfunc. len(args) can be different for different calls if + # self.pyfunc has parameters with default values. We only use the + # cache when func is self.pyfunc, which occurs when the call uses + # only positional arguments and no arguments are excluded. + + nin = len(args) + nout = len(self.otypes) + if func is not self.pyfunc or nin not in self._ufunc: + ufunc = frompyfunc(func, nin, nout) else: - ufunc = self._ufunc = frompyfunc(func, len(args), nout) + ufunc = None # We'll get it from self._ufunc + if func is self.pyfunc: + ufunc = self._ufunc.setdefault(nin, ufunc) else: # Get number of outputs and output types by calling the function on # the first entries of args. We also cache the result to prevent @@ -2042,15 +2382,14 @@ def _vectorize_call(self, func, args): ufunc, otypes = self._get_ufunc_and_otypes(func=func, args=args) # Convert args to object arrays first - inputs = [array(a, copy=False, subok=True, dtype=object) - for a in args] + inputs = [asanyarray(a, dtype=object) for a in args] outputs = ufunc(*inputs) if ufunc.nout == 1: - res = array(outputs, copy=False, subok=True, dtype=otypes[0]) + res = asanyarray(outputs, dtype=otypes[0]) else: - res = tuple([array(x, copy=False, subok=True, dtype=t) + res = tuple([asanyarray(x, dtype=t) for x, t in zip(outputs, otypes)]) return res @@ -2092,11 +2431,8 @@ def _vectorize_call_with_signature(self, func, args): for result, core_dims in zip(results, output_core_dims): _update_dim_sizes(dim_sizes, result, core_dims) - if otypes is None: - otypes = [asarray(result).dtype for result in results] - outputs = _create_arrays(broadcast_shape, dim_sizes, - output_core_dims, otypes) + output_core_dims, otypes, results) for output, result in zip(outputs, results): output[index] = result @@ -2118,8 +2454,14 @@ def _vectorize_call_with_signature(self, func, args): return outputs[0] if nout == 1 else outputs +def _cov_dispatcher(m, y=None, rowvar=None, bias=None, ddof=None, + fweights=None, aweights=None, *, dtype=None): + return (m, y, fweights, aweights) + + +@array_function_dispatch(_cov_dispatcher) def cov(m, y=None, rowvar=True, bias=False, ddof=None, fweights=None, - aweights=None): + aweights=None, *, dtype=None): """ Estimate a covariance matrix, given data and weights. @@ -2170,6 +2512,11 @@ def cov(m, y=None, rowvar=True, bias=False, ddof=None, fweights=None, weights can be used to assign probabilities to observation vectors. .. versionadded:: 1.10 + dtype : data-type, optional + Data-type of the result. By default, the return data-type will have + at least `numpy.float64` precision. + + .. versionadded:: 1.20 Returns ------- @@ -2186,10 +2533,14 @@ def cov(m, y=None, rowvar=True, bias=False, ddof=None, fweights=None, array `m` and let ``f = fweights`` and ``a = aweights`` for brevity. The steps to compute the weighted covariance are as follows:: + >>> m = np.arange(10, dtype=np.float64) + >>> f = np.arange(10) * 2 + >>> a = np.arange(10) ** 2. + >>> ddof = 1 >>> w = f * a >>> v1 = np.sum(w) >>> v2 = np.sum(w * a) - >>> m -= np.sum(m * w, axis=1, keepdims=True) / v1 + >>> m -= np.sum(m * w, axis=None, keepdims=True) / v1 >>> cov = np.dot(m * w, m.T) * v1 / (v1**2 - ddof * v2) Note that when ``a == 1``, the normalization factor @@ -2221,14 +2572,14 @@ def cov(m, y=None, rowvar=True, bias=False, ddof=None, fweights=None, >>> x = [-2.1, -1, 4.3] >>> y = [3, 1.1, 0.12] >>> X = np.stack((x, y), axis=0) - >>> print(np.cov(X)) - [[ 11.71 -4.286 ] - [ -4.286 2.14413333]] - >>> print(np.cov(x, y)) - [[ 11.71 -4.286 ] - [ -4.286 2.14413333]] - >>> print(np.cov(x)) - 11.71 + >>> np.cov(X) + array([[11.71 , -4.286 ], # may vary + [-4.286 , 2.144133]]) + >>> np.cov(x, y) + array([[11.71 , -4.286 ], # may vary + [-4.286 , 2.144133]]) + >>> np.cov(x) + array(11.71) """ # Check inputs @@ -2241,13 +2592,16 @@ def cov(m, y=None, rowvar=True, bias=False, ddof=None, fweights=None, if m.ndim > 2: raise ValueError("m has more than 2 dimensions") - if y is None: - dtype = np.result_type(m, np.float64) - else: + if y is not None: y = np.asarray(y) if y.ndim > 2: raise ValueError("y has more than 2 dimensions") - dtype = np.result_type(m, y, np.float64) + + if dtype is None: + if y is None: + dtype = np.result_type(m, np.float64) + else: + dtype = np.result_type(m, y, np.float64) X = array(m, ndmin=2, dtype=dtype) if not rowvar and X.shape[0] != 1: @@ -2314,7 +2668,7 @@ def cov(m, y=None, rowvar=True, bias=False, ddof=None, fweights=None, if fact <= 0: warnings.warn("Degrees of freedom <= 0 for slice", - RuntimeWarning, stacklevel=2) + RuntimeWarning, stacklevel=3) fact = 0.0 X -= avg[:, None] @@ -2327,7 +2681,14 @@ def cov(m, y=None, rowvar=True, bias=False, ddof=None, fweights=None, return c.squeeze() -def corrcoef(x, y=None, rowvar=True, bias=np._NoValue, ddof=np._NoValue): +def _corrcoef_dispatcher(x, y=None, rowvar=None, bias=None, ddof=None, *, + dtype=None): + return (x, y) + + +@array_function_dispatch(_corrcoef_dispatcher) +def corrcoef(x, y=None, rowvar=True, bias=np._NoValue, ddof=np._NoValue, *, + dtype=None): """ Return Pearson product-moment correlation coefficients. @@ -2361,6 +2722,11 @@ def corrcoef(x, y=None, rowvar=True, bias=np._NoValue, ddof=np._NoValue): Has no effect, do not use. .. deprecated:: 1.10.0 + dtype : data-type, optional + Data-type of the result. By default, the return data-type will have + at least `numpy.float64` precision. + + .. versionadded:: 1.20 Returns ------- @@ -2384,12 +2750,75 @@ def corrcoef(x, y=None, rowvar=True, bias=np._NoValue, ddof=np._NoValue): arguments had no effect on the return values of the function and can be safely ignored in this and previous versions of numpy. + Examples + -------- + In this example we generate two random arrays, ``xarr`` and ``yarr``, and + compute the row-wise and column-wise Pearson correlation coefficients, + ``R``. Since ``rowvar`` is true by default, we first find the row-wise + Pearson correlation coefficients between the variables of ``xarr``. + + >>> import numpy as np + >>> rng = np.random.default_rng(seed=42) + >>> xarr = rng.random((3, 3)) + >>> xarr + array([[0.77395605, 0.43887844, 0.85859792], + [0.69736803, 0.09417735, 0.97562235], + [0.7611397 , 0.78606431, 0.12811363]]) + >>> R1 = np.corrcoef(xarr) + >>> R1 + array([[ 1. , 0.99256089, -0.68080986], + [ 0.99256089, 1. , -0.76492172], + [-0.68080986, -0.76492172, 1. ]]) + + If we add another set of variables and observations ``yarr``, we can + compute the row-wise Pearson correlation coefficients between the + variables in ``xarr`` and ``yarr``. + + >>> yarr = rng.random((3, 3)) + >>> yarr + array([[0.45038594, 0.37079802, 0.92676499], + [0.64386512, 0.82276161, 0.4434142 ], + [0.22723872, 0.55458479, 0.06381726]]) + >>> R2 = np.corrcoef(xarr, yarr) + >>> R2 + array([[ 1. , 0.99256089, -0.68080986, 0.75008178, -0.934284 , + -0.99004057], + [ 0.99256089, 1. , -0.76492172, 0.82502011, -0.97074098, + -0.99981569], + [-0.68080986, -0.76492172, 1. , -0.99507202, 0.89721355, + 0.77714685], + [ 0.75008178, 0.82502011, -0.99507202, 1. , -0.93657855, + -0.83571711], + [-0.934284 , -0.97074098, 0.89721355, -0.93657855, 1. , + 0.97517215], + [-0.99004057, -0.99981569, 0.77714685, -0.83571711, 0.97517215, + 1. ]]) + + Finally if we use the option ``rowvar=False``, the columns are now + being treated as the variables and we will find the column-wise Pearson + correlation coefficients between variables in ``xarr`` and ``yarr``. + + >>> R3 = np.corrcoef(xarr, yarr, rowvar=False) + >>> R3 + array([[ 1. , 0.77598074, -0.47458546, -0.75078643, -0.9665554 , + 0.22423734], + [ 0.77598074, 1. , -0.92346708, -0.99923895, -0.58826587, + -0.44069024], + [-0.47458546, -0.92346708, 1. , 0.93773029, 0.23297648, + 0.75137473], + [-0.75078643, -0.99923895, 0.93773029, 1. , 0.55627469, + 0.47536961], + [-0.9665554 , -0.58826587, 0.23297648, 0.55627469, 1. , + -0.46666491], + [ 0.22423734, -0.44069024, 0.75137473, 0.47536961, -0.46666491, + 1. ]]) + """ if bias is not np._NoValue or ddof is not np._NoValue: # 2015-03-15, 1.10 warnings.warn('bias and ddof have no effect and are deprecated', - DeprecationWarning, stacklevel=2) - c = cov(x, y, rowvar) + DeprecationWarning, stacklevel=3) + c = cov(x, y, rowvar, dtype=dtype) try: d = diag(c) except ValueError: @@ -2410,6 +2839,7 @@ def corrcoef(x, y=None, rowvar=True, bias=np._NoValue, ddof=np._NoValue): return c +@set_module('numpy') def blackman(M): """ Return the Blackman window. @@ -2459,12 +2889,12 @@ def blackman(M): Examples -------- + >>> import matplotlib.pyplot as plt >>> np.blackman(12) - array([ -1.38777878e-17, 3.26064346e-02, 1.59903635e-01, - 4.14397981e-01, 7.36045180e-01, 9.67046769e-01, - 9.67046769e-01, 7.36045180e-01, 4.14397981e-01, - 1.59903635e-01, 3.26064346e-02, -1.38777878e-17]) - + array([-1.38777878e-17, 3.26064346e-02, 1.59903635e-01, # may vary + 4.14397981e-01, 7.36045180e-01, 9.67046769e-01, + 9.67046769e-01, 7.36045180e-01, 4.14397981e-01, + 1.59903635e-01, 3.26064346e-02, -1.38777878e-17]) Plot the window and the frequency response: @@ -2473,41 +2903,43 @@ def blackman(M): >>> plt.plot(window) [<matplotlib.lines.Line2D object at 0x...>] >>> plt.title("Blackman window") - <matplotlib.text.Text object at 0x...> + Text(0.5, 1.0, 'Blackman window') >>> plt.ylabel("Amplitude") - <matplotlib.text.Text object at 0x...> + Text(0, 0.5, 'Amplitude') >>> plt.xlabel("Sample") - <matplotlib.text.Text object at 0x...> + Text(0.5, 0, 'Sample') >>> plt.show() >>> plt.figure() - <matplotlib.figure.Figure object at 0x...> + <Figure size 640x480 with 0 Axes> >>> A = fft(window, 2048) / 25.5 >>> mag = np.abs(fftshift(A)) >>> freq = np.linspace(-0.5, 0.5, len(A)) - >>> response = 20 * np.log10(mag) + >>> with np.errstate(divide='ignore', invalid='ignore'): + ... response = 20 * np.log10(mag) + ... >>> response = np.clip(response, -100, 100) >>> plt.plot(freq, response) [<matplotlib.lines.Line2D object at 0x...>] >>> plt.title("Frequency response of Blackman window") - <matplotlib.text.Text object at 0x...> + Text(0.5, 1.0, 'Frequency response of Blackman window') >>> plt.ylabel("Magnitude [dB]") - <matplotlib.text.Text object at 0x...> + Text(0, 0.5, 'Magnitude [dB]') >>> plt.xlabel("Normalized frequency [cycles per sample]") - <matplotlib.text.Text object at 0x...> - >>> plt.axis('tight') - (-0.5, 0.5, -100.0, ...) + Text(0.5, 0, 'Normalized frequency [cycles per sample]') + >>> _ = plt.axis('tight') >>> plt.show() """ if M < 1: - return array([]) + return array([], dtype=np.result_type(M, 0.0)) if M == 1: - return ones(1, float) - n = arange(0, M) - return 0.42 - 0.5*cos(2.0*pi*n/(M-1)) + 0.08*cos(4.0*pi*n/(M-1)) + return ones(1, dtype=np.result_type(M, 0.0)) + n = arange(1-M, M, 2) + return 0.42 + 0.5*cos(pi*n/(M-1)) + 0.08*cos(2.0*pi*n/(M-1)) +@set_module('numpy') def bartlett(M): """ Return the Bartlett window. @@ -2561,14 +2993,15 @@ def bartlett(M): .. [3] A.V. Oppenheim and R.W. Schafer, "Discrete-Time Signal Processing", Prentice-Hall, 1999, pp. 468-471. .. [4] Wikipedia, "Window function", - http://en.wikipedia.org/wiki/Window_function + https://en.wikipedia.org/wiki/Window_function .. [5] W.H. Press, B.P. Flannery, S.A. Teukolsky, and W.T. Vetterling, "Numerical Recipes", Cambridge University Press, 1986, page 429. Examples -------- + >>> import matplotlib.pyplot as plt >>> np.bartlett(12) - array([ 0. , 0.18181818, 0.36363636, 0.54545455, 0.72727273, + array([ 0. , 0.18181818, 0.36363636, 0.54545455, 0.72727273, # may vary 0.90909091, 0.90909091, 0.72727273, 0.54545455, 0.36363636, 0.18181818, 0. ]) @@ -2579,41 +3012,43 @@ def bartlett(M): >>> plt.plot(window) [<matplotlib.lines.Line2D object at 0x...>] >>> plt.title("Bartlett window") - <matplotlib.text.Text object at 0x...> + Text(0.5, 1.0, 'Bartlett window') >>> plt.ylabel("Amplitude") - <matplotlib.text.Text object at 0x...> + Text(0, 0.5, 'Amplitude') >>> plt.xlabel("Sample") - <matplotlib.text.Text object at 0x...> + Text(0.5, 0, 'Sample') >>> plt.show() >>> plt.figure() - <matplotlib.figure.Figure object at 0x...> + <Figure size 640x480 with 0 Axes> >>> A = fft(window, 2048) / 25.5 >>> mag = np.abs(fftshift(A)) >>> freq = np.linspace(-0.5, 0.5, len(A)) - >>> response = 20 * np.log10(mag) + >>> with np.errstate(divide='ignore', invalid='ignore'): + ... response = 20 * np.log10(mag) + ... >>> response = np.clip(response, -100, 100) >>> plt.plot(freq, response) [<matplotlib.lines.Line2D object at 0x...>] >>> plt.title("Frequency response of Bartlett window") - <matplotlib.text.Text object at 0x...> + Text(0.5, 1.0, 'Frequency response of Bartlett window') >>> plt.ylabel("Magnitude [dB]") - <matplotlib.text.Text object at 0x...> + Text(0, 0.5, 'Magnitude [dB]') >>> plt.xlabel("Normalized frequency [cycles per sample]") - <matplotlib.text.Text object at 0x...> - >>> plt.axis('tight') - (-0.5, 0.5, -100.0, ...) + Text(0.5, 0, 'Normalized frequency [cycles per sample]') + >>> _ = plt.axis('tight') >>> plt.show() """ if M < 1: - return array([]) + return array([], dtype=np.result_type(M, 0.0)) if M == 1: - return ones(1, float) - n = arange(0, M) - return where(less_equal(n, (M-1)/2.0), 2.0*n/(M-1), 2.0 - 2.0*n/(M-1)) + return ones(1, dtype=np.result_type(M, 0.0)) + n = arange(1-M, M, 2) + return where(less_equal(n, 0), 1 + n/(M-1), 1 - n/(M-1)) +@set_module('numpy') def hanning(M): """ Return the Hanning window. @@ -2661,59 +3096,63 @@ def hanning(M): .. [2] E.R. Kanasewich, "Time Sequence Analysis in Geophysics", The University of Alberta Press, 1975, pp. 106-108. .. [3] Wikipedia, "Window function", - http://en.wikipedia.org/wiki/Window_function + https://en.wikipedia.org/wiki/Window_function .. [4] W.H. Press, B.P. Flannery, S.A. Teukolsky, and W.T. Vetterling, "Numerical Recipes", Cambridge University Press, 1986, page 425. Examples -------- >>> np.hanning(12) - array([ 0. , 0.07937323, 0.29229249, 0.57115742, 0.82743037, - 0.97974649, 0.97974649, 0.82743037, 0.57115742, 0.29229249, - 0.07937323, 0. ]) + array([0. , 0.07937323, 0.29229249, 0.57115742, 0.82743037, + 0.97974649, 0.97974649, 0.82743037, 0.57115742, 0.29229249, + 0.07937323, 0. ]) Plot the window and its frequency response: + >>> import matplotlib.pyplot as plt >>> from numpy.fft import fft, fftshift >>> window = np.hanning(51) >>> plt.plot(window) [<matplotlib.lines.Line2D object at 0x...>] >>> plt.title("Hann window") - <matplotlib.text.Text object at 0x...> + Text(0.5, 1.0, 'Hann window') >>> plt.ylabel("Amplitude") - <matplotlib.text.Text object at 0x...> + Text(0, 0.5, 'Amplitude') >>> plt.xlabel("Sample") - <matplotlib.text.Text object at 0x...> + Text(0.5, 0, 'Sample') >>> plt.show() >>> plt.figure() - <matplotlib.figure.Figure object at 0x...> + <Figure size 640x480 with 0 Axes> >>> A = fft(window, 2048) / 25.5 >>> mag = np.abs(fftshift(A)) >>> freq = np.linspace(-0.5, 0.5, len(A)) - >>> response = 20 * np.log10(mag) + >>> with np.errstate(divide='ignore', invalid='ignore'): + ... response = 20 * np.log10(mag) + ... >>> response = np.clip(response, -100, 100) >>> plt.plot(freq, response) [<matplotlib.lines.Line2D object at 0x...>] >>> plt.title("Frequency response of the Hann window") - <matplotlib.text.Text object at 0x...> + Text(0.5, 1.0, 'Frequency response of the Hann window') >>> plt.ylabel("Magnitude [dB]") - <matplotlib.text.Text object at 0x...> + Text(0, 0.5, 'Magnitude [dB]') >>> plt.xlabel("Normalized frequency [cycles per sample]") - <matplotlib.text.Text object at 0x...> + Text(0.5, 0, 'Normalized frequency [cycles per sample]') >>> plt.axis('tight') - (-0.5, 0.5, -100.0, ...) + ... >>> plt.show() """ if M < 1: - return array([]) + return array([], dtype=np.result_type(M, 0.0)) if M == 1: - return ones(1, float) - n = arange(0, M) - return 0.5 - 0.5*cos(2.0*pi*n/(M-1)) + return ones(1, dtype=np.result_type(M, 0.0)) + n = arange(1-M, M, 2) + return 0.5 + 0.5*cos(pi*n/(M-1)) +@set_module('numpy') def hamming(M): """ Return the Hamming window. @@ -2759,33 +3198,34 @@ def hamming(M): .. [2] E.R. Kanasewich, "Time Sequence Analysis in Geophysics", The University of Alberta Press, 1975, pp. 109-110. .. [3] Wikipedia, "Window function", - http://en.wikipedia.org/wiki/Window_function + https://en.wikipedia.org/wiki/Window_function .. [4] W.H. Press, B.P. Flannery, S.A. Teukolsky, and W.T. Vetterling, "Numerical Recipes", Cambridge University Press, 1986, page 425. Examples -------- >>> np.hamming(12) - array([ 0.08 , 0.15302337, 0.34890909, 0.60546483, 0.84123594, + array([ 0.08 , 0.15302337, 0.34890909, 0.60546483, 0.84123594, # may vary 0.98136677, 0.98136677, 0.84123594, 0.60546483, 0.34890909, 0.15302337, 0.08 ]) Plot the window and the frequency response: + >>> import matplotlib.pyplot as plt >>> from numpy.fft import fft, fftshift >>> window = np.hamming(51) >>> plt.plot(window) [<matplotlib.lines.Line2D object at 0x...>] >>> plt.title("Hamming window") - <matplotlib.text.Text object at 0x...> + Text(0.5, 1.0, 'Hamming window') >>> plt.ylabel("Amplitude") - <matplotlib.text.Text object at 0x...> + Text(0, 0.5, 'Amplitude') >>> plt.xlabel("Sample") - <matplotlib.text.Text object at 0x...> + Text(0.5, 0, 'Sample') >>> plt.show() >>> plt.figure() - <matplotlib.figure.Figure object at 0x...> + <Figure size 640x480 with 0 Axes> >>> A = fft(window, 2048) / 25.5 >>> mag = np.abs(fftshift(A)) >>> freq = np.linspace(-0.5, 0.5, len(A)) @@ -2794,22 +3234,23 @@ def hamming(M): >>> plt.plot(freq, response) [<matplotlib.lines.Line2D object at 0x...>] >>> plt.title("Frequency response of Hamming window") - <matplotlib.text.Text object at 0x...> + Text(0.5, 1.0, 'Frequency response of Hamming window') >>> plt.ylabel("Magnitude [dB]") - <matplotlib.text.Text object at 0x...> + Text(0, 0.5, 'Magnitude [dB]') >>> plt.xlabel("Normalized frequency [cycles per sample]") - <matplotlib.text.Text object at 0x...> + Text(0.5, 0, 'Normalized frequency [cycles per sample]') >>> plt.axis('tight') - (-0.5, 0.5, -100.0, ...) + ... >>> plt.show() """ if M < 1: - return array([]) + return array([], dtype=np.result_type(M, 0.0)) if M == 1: - return ones(1, float) - n = arange(0, M) - return 0.54 - 0.46*cos(2.0*pi*n/(M-1)) + return ones(1, dtype=np.result_type(M, 0.0)) + n = arange(1-M, M, 2) + return 0.54 + 0.46*cos(pi*n/(M-1)) + ## Code from cephes for i0 @@ -2895,35 +3336,36 @@ def _i0_2(x): return exp(x) * _chbevl(32.0/x - 2.0, _i0B) / sqrt(x) +def _i0_dispatcher(x): + return (x,) + + +@array_function_dispatch(_i0_dispatcher) def i0(x): """ Modified Bessel function of the first kind, order 0. - Usually denoted :math:`I_0`. This function does broadcast, but will *not* - "up-cast" int dtype arguments unless accompanied by at least one float or - complex dtype argument (see Raises below). + Usually denoted :math:`I_0`. Parameters ---------- - x : array_like, dtype float or complex + x : array_like of float Argument of the Bessel function. Returns ------- - out : ndarray, shape = x.shape, dtype = x.dtype + out : ndarray, shape = x.shape, dtype = float The modified Bessel function evaluated at each of the elements of `x`. - Raises - ------ - TypeError: array cannot be safely cast to required type - If argument consists exclusively of int dtypes. - See Also -------- - scipy.special.iv, scipy.special.ive + scipy.special.i0, scipy.special.iv, scipy.special.ive Notes ----- + The scipy implementation is recommended over this function: it is a + proper ufunc written in C, and more than an order of magnitude faster. + We use the algorithm published by Clenshaw [1]_ and referenced by Abramowitz and Stegun [2]_, for which the function domain is partitioned into the two intervals [0,8] and (8,inf), and Chebyshev @@ -2938,30 +3380,29 @@ def i0(x): Her Majesty's Stationery Office, 1962. .. [2] M. Abramowitz and I. A. Stegun, *Handbook of Mathematical Functions*, 10th printing, New York: Dover, 1964, pp. 379. - http://www.math.sfu.ca/~cbm/aands/page_379.htm - .. [3] http://kobesearch.cpan.org/htdocs/Math-Cephes/Math/Cephes.html + https://personal.math.ubc.ca/~cbm/aands/page_379.htm + .. [3] https://metacpan.org/pod/distribution/Math-Cephes/lib/Math/Cephes.pod#i0:-Modified-Bessel-function-of-order-zero Examples -------- - >>> np.i0([0.]) + >>> np.i0(0.) array(1.0) - >>> np.i0([0., 1. + 2j]) - array([ 1.00000000+0.j , 0.18785373+0.64616944j]) + >>> np.i0([0, 1, 2, 3]) + array([1. , 1.26606588, 2.2795853 , 4.88079259]) """ - x = atleast_1d(x).copy() - y = empty_like(x) - ind = (x < 0) - x[ind] = -x[ind] - ind = (x <= 8.0) - y[ind] = _i0_1(x[ind]) - ind2 = ~ind - y[ind2] = _i0_2(x[ind2]) - return y.squeeze() + x = np.asanyarray(x) + if x.dtype.kind == 'c': + raise TypeError("i0 not supported for complex values") + if x.dtype.kind != 'f': + x = x.astype(float) + x = np.abs(x) + return piecewise(x, [x <= 8.0], [_i0_1, _i0_2]) ## End of cephes code for i0 +@set_module('numpy') def kaiser(M, beta): """ Return the Kaiser window. @@ -3036,15 +3477,16 @@ def kaiser(M, beta): .. [2] E.R. Kanasewich, "Time Sequence Analysis in Geophysics", The University of Alberta Press, 1975, pp. 177-178. .. [3] Wikipedia, "Window function", - http://en.wikipedia.org/wiki/Window_function + https://en.wikipedia.org/wiki/Window_function Examples -------- + >>> import matplotlib.pyplot as plt >>> np.kaiser(12, 14) - array([ 7.72686684e-06, 3.46009194e-03, 4.65200189e-02, - 2.29737120e-01, 5.99885316e-01, 9.45674898e-01, - 9.45674898e-01, 5.99885316e-01, 2.29737120e-01, - 4.65200189e-02, 3.46009194e-03, 7.72686684e-06]) + array([7.72686684e-06, 3.46009194e-03, 4.65200189e-02, # may vary + 2.29737120e-01, 5.99885316e-01, 9.45674898e-01, + 9.45674898e-01, 5.99885316e-01, 2.29737120e-01, + 4.65200189e-02, 3.46009194e-03, 7.72686684e-06]) Plot the window and the frequency response: @@ -3054,15 +3496,15 @@ def kaiser(M, beta): >>> plt.plot(window) [<matplotlib.lines.Line2D object at 0x...>] >>> plt.title("Kaiser window") - <matplotlib.text.Text object at 0x...> + Text(0.5, 1.0, 'Kaiser window') >>> plt.ylabel("Amplitude") - <matplotlib.text.Text object at 0x...> + Text(0, 0.5, 'Amplitude') >>> plt.xlabel("Sample") - <matplotlib.text.Text object at 0x...> + Text(0.5, 0, 'Sample') >>> plt.show() >>> plt.figure() - <matplotlib.figure.Figure object at 0x...> + <Figure size 640x480 with 0 Axes> >>> A = fft(window, 2048) / 25.5 >>> mag = np.abs(fftshift(A)) >>> freq = np.linspace(-0.5, 0.5, len(A)) @@ -3071,29 +3513,40 @@ def kaiser(M, beta): >>> plt.plot(freq, response) [<matplotlib.lines.Line2D object at 0x...>] >>> plt.title("Frequency response of Kaiser window") - <matplotlib.text.Text object at 0x...> + Text(0.5, 1.0, 'Frequency response of Kaiser window') >>> plt.ylabel("Magnitude [dB]") - <matplotlib.text.Text object at 0x...> + Text(0, 0.5, 'Magnitude [dB]') >>> plt.xlabel("Normalized frequency [cycles per sample]") - <matplotlib.text.Text object at 0x...> + Text(0.5, 0, 'Normalized frequency [cycles per sample]') >>> plt.axis('tight') - (-0.5, 0.5, -100.0, ...) + (-0.5, 0.5, -100.0, ...) # may vary >>> plt.show() """ - from numpy.dual import i0 if M == 1: - return np.array([1.]) + return np.ones(1, dtype=np.result_type(M, 0.0)) n = arange(0, M) alpha = (M-1)/2.0 return i0(beta * sqrt(1-((n-alpha)/alpha)**2.0))/i0(float(beta)) +def _sinc_dispatcher(x): + return (x,) + + +@array_function_dispatch(_sinc_dispatcher) def sinc(x): - """ - Return the sinc function. + r""" + Return the normalized sinc function. + + The sinc function is :math:`\sin(\pi x)/(\pi x)`. - The sinc function is :math:`\\sin(\\pi x)/(\\pi x)`. + .. note:: + + Note the normalization factor of ``pi`` used in the definition. + This is the most commonly used definition in signal processing. + Use ``sinc(x / np.pi)`` to obtain the unnormalized sinc function + :math:`\sin(x)/(x)` that is more common in mathematics. Parameters ---------- @@ -3124,50 +3577,49 @@ def sinc(x): .. [1] Weisstein, Eric W. "Sinc Function." From MathWorld--A Wolfram Web Resource. http://mathworld.wolfram.com/SincFunction.html .. [2] Wikipedia, "Sinc function", - http://en.wikipedia.org/wiki/Sinc_function + https://en.wikipedia.org/wiki/Sinc_function Examples -------- + >>> import matplotlib.pyplot as plt >>> x = np.linspace(-4, 4, 41) >>> np.sinc(x) - array([ -3.89804309e-17, -4.92362781e-02, -8.40918587e-02, + array([-3.89804309e-17, -4.92362781e-02, -8.40918587e-02, # may vary -8.90384387e-02, -5.84680802e-02, 3.89804309e-17, - 6.68206631e-02, 1.16434881e-01, 1.26137788e-01, - 8.50444803e-02, -3.89804309e-17, -1.03943254e-01, + 6.68206631e-02, 1.16434881e-01, 1.26137788e-01, + 8.50444803e-02, -3.89804309e-17, -1.03943254e-01, -1.89206682e-01, -2.16236208e-01, -1.55914881e-01, - 3.89804309e-17, 2.33872321e-01, 5.04551152e-01, - 7.56826729e-01, 9.35489284e-01, 1.00000000e+00, - 9.35489284e-01, 7.56826729e-01, 5.04551152e-01, - 2.33872321e-01, 3.89804309e-17, -1.55914881e-01, - -2.16236208e-01, -1.89206682e-01, -1.03943254e-01, - -3.89804309e-17, 8.50444803e-02, 1.26137788e-01, - 1.16434881e-01, 6.68206631e-02, 3.89804309e-17, + 3.89804309e-17, 2.33872321e-01, 5.04551152e-01, + 7.56826729e-01, 9.35489284e-01, 1.00000000e+00, + 9.35489284e-01, 7.56826729e-01, 5.04551152e-01, + 2.33872321e-01, 3.89804309e-17, -1.55914881e-01, + -2.16236208e-01, -1.89206682e-01, -1.03943254e-01, + -3.89804309e-17, 8.50444803e-02, 1.26137788e-01, + 1.16434881e-01, 6.68206631e-02, 3.89804309e-17, -5.84680802e-02, -8.90384387e-02, -8.40918587e-02, -4.92362781e-02, -3.89804309e-17]) >>> plt.plot(x, np.sinc(x)) [<matplotlib.lines.Line2D object at 0x...>] >>> plt.title("Sinc Function") - <matplotlib.text.Text object at 0x...> + Text(0.5, 1.0, 'Sinc Function') >>> plt.ylabel("Amplitude") - <matplotlib.text.Text object at 0x...> + Text(0, 0.5, 'Amplitude') >>> plt.xlabel("X") - <matplotlib.text.Text object at 0x...> + Text(0.5, 0, 'X') >>> plt.show() - It works in 2-D as well: - - >>> x = np.linspace(-4, 4, 401) - >>> xx = np.outer(x, x) - >>> plt.imshow(np.sinc(xx)) - <matplotlib.image.AxesImage object at 0x...> - """ x = np.asanyarray(x) y = pi * where(x == 0, 1.0e-20, x) return sin(y)/y +def _msort_dispatcher(a): + return (a,) + + +@array_function_dispatch(_msort_dispatcher) def msort(a): """ Return a copy of an array sorted along the first axis. @@ -3251,6 +3703,12 @@ def _ureduce(a, func, **kwargs): return r, keepdim +def _median_dispatcher( + a, axis=None, out=None, overwrite_input=None, keepdims=None): + return (a, out) + + +@array_function_dispatch(_median_dispatcher) def median(a, axis=None, out=None, overwrite_input=False, keepdims=False): """ Compute the median along the specified axis. @@ -3313,18 +3771,18 @@ def median(a, axis=None, out=None, overwrite_input=False, keepdims=False): >>> np.median(a) 3.5 >>> np.median(a, axis=0) - array([ 6.5, 4.5, 2.5]) + array([6.5, 4.5, 2.5]) >>> np.median(a, axis=1) - array([ 7., 2.]) + array([7., 2.]) >>> m = np.median(a, axis=0) >>> out = np.zeros_like(m) >>> np.median(a, axis=0, out=m) - array([ 6.5, 4.5, 2.5]) + array([6.5, 4.5, 2.5]) >>> m - array([ 6.5, 4.5, 2.5]) + array([6.5, 4.5, 2.5]) >>> b = a.copy() >>> np.median(b, axis=1, overwrite_input=True) - array([ 7., 2.]) + array([7., 2.]) >>> assert not np.all(a==b) >>> b = a.copy() >>> np.median(b, axis=None, overwrite_input=True) @@ -3339,6 +3797,7 @@ def median(a, axis=None, out=None, overwrite_input=False, keepdims=False): else: return r + def _median(a, axis=None, out=None, overwrite_input=False): # can't be reasonably be implemented in terms of percentile as we have to # call mean to not break astropy @@ -3383,24 +3842,36 @@ def _median(a, axis=None, out=None, overwrite_input=False): indexer[axis] = slice(index-1, index+1) indexer = tuple(indexer) + # Use mean in both odd and even case to coerce data type, + # using out array if needed. + rout = mean(part[indexer], axis=axis, out=out) # Check if the array contains any nan's if np.issubdtype(a.dtype, np.inexact) and sz > 0: - # warn and return nans like mean would - rout = mean(part[indexer], axis=axis, out=out) - return np.lib.utils._median_nancheck(part, rout, axis, out) - else: - # if there are no nans - # Use mean in odd and even case to coerce data type - # and check, use out array. - return mean(part[indexer], axis=axis, out=out) + # If nans are possible, warn and replace by nans like mean would. + rout = np.lib.utils._median_nancheck(part, rout, axis) + + return rout + +def _percentile_dispatcher(a, q, axis=None, out=None, overwrite_input=None, + method=None, keepdims=None, *, interpolation=None): + return (a, q, out) -def percentile(a, q, axis=None, out=None, - overwrite_input=False, interpolation='linear', keepdims=False): + +@array_function_dispatch(_percentile_dispatcher) +def percentile(a, + q, + axis=None, + out=None, + overwrite_input=False, + method="linear", + keepdims=False, + *, + interpolation=None): """ - Compute the qth percentile of the data along the specified axis. + Compute the q-th percentile of the data along the specified axis. - Returns the qth percentile(s) of the array elements. + Returns the q-th percentile(s) of the array elements. Parameters ---------- @@ -3424,21 +3895,34 @@ def percentile(a, q, axis=None, out=None, If True, then allow the input array `a` to be modified by intermediate calculations, to save memory. In this case, the contents of the input `a` after this function completes is undefined. + method : str, optional + This parameter specifies the method to use for estimating the + percentile. There are many different methods, some unique to NumPy. + See the notes for explanation. The options sorted by their R type + as summarized in the H&F paper [1]_ are: + + 1. 'inverted_cdf' + 2. 'averaged_inverted_cdf' + 3. 'closest_observation' + 4. 'interpolated_inverted_cdf' + 5. 'hazen' + 6. 'weibull' + 7. 'linear' (default) + 8. 'median_unbiased' + 9. 'normal_unbiased' + + The first three methods are discontiuous. NumPy further defines the + following discontinuous variations of the default 'linear' (7.) option: + + * 'lower' + * 'higher', + * 'midpoint' + * 'nearest' + + .. versionchanged:: 1.22.0 + This argument was previously called "interpolation" and only + offered the "linear" default and last four options. - interpolation : {'linear', 'lower', 'higher', 'midpoint', 'nearest'} - This optional parameter specifies the interpolation method to - use when the desired percentile lies between two data points - ``i < j``: - - * 'linear': ``i + (j - i) * fraction``, where ``fraction`` - is the fractional part of the index surrounded by ``i`` - and ``j``. - * 'lower': ``i``. - * 'higher': ``j``. - * 'nearest': ``i`` or ``j``, whichever is nearest. - * 'midpoint': ``(i + j) / 2``. - - .. versionadded:: 1.9.0 keepdims : bool, optional If this is set to True, the axes which are reduced are left in the result as dimensions with size one. With this option, the @@ -3446,6 +3930,11 @@ def percentile(a, q, axis=None, out=None, .. versionadded:: 1.9.0 + interpolation : str, optional + Deprecated name for the method keyword argument. + + .. deprecated:: 1.22.0 + Returns ------- percentile : scalar or ndarray @@ -3463,18 +3952,109 @@ def percentile(a, q, axis=None, out=None, mean median : equivalent to ``percentile(..., 50)`` nanpercentile - quantile : equivalent to percentile, except with q in the range [0, 1]. + quantile : equivalent to percentile, except q in the range [0, 1]. Notes ----- - Given a vector ``V`` of length ``N``, the ``q``-th percentile of - ``V`` is the value ``q/100`` of the way from the minimum to the - maximum in a sorted copy of ``V``. The values and distances of - the two nearest neighbors as well as the `interpolation` parameter - will determine the percentile if the normalized ranking does not - match the location of ``q`` exactly. This function is the same as - the median if ``q=50``, the same as the minimum if ``q=0`` and the - same as the maximum if ``q=100``. + Given a vector ``V`` of length ``N``, the q-th percentile of ``V`` is + the value ``q/100`` of the way from the minimum to the maximum in a + sorted copy of ``V``. The values and distances of the two nearest + neighbors as well as the `method` parameter will determine the + percentile if the normalized ranking does not match the location of + ``q`` exactly. This function is the same as the median if ``q=50``, the + same as the minimum if ``q=0`` and the same as the maximum if + ``q=100``. + + This optional `method` parameter specifies the method to use when the + desired quantile lies between two data points ``i < j``. + If ``g`` is the fractional part of the index surrounded by ``i`` and + alpha and beta are correction constants modifying i and j. + + Below, 'q' is the quantile value, 'n' is the sample size and + alpha and beta are constants. + The following formula gives an interpolation "i + g" of where the quantile + would be in the sorted sample. + With 'i' being the floor and 'g' the fractional part of the result. + + .. math:: + i + g = (q - alpha) / ( n - alpha - beta + 1 ) + + The different methods then work as follows + + inverted_cdf: + method 1 of H&F [1]_. + This method gives discontinuous results: + * if g > 0 ; then take j + * if g = 0 ; then take i + + averaged_inverted_cdf: + method 2 of H&F [1]_. + This method give discontinuous results: + * if g > 0 ; then take j + * if g = 0 ; then average between bounds + + closest_observation: + method 3 of H&F [1]_. + This method give discontinuous results: + * if g > 0 ; then take j + * if g = 0 and index is odd ; then take j + * if g = 0 and index is even ; then take i + + interpolated_inverted_cdf: + method 4 of H&F [1]_. + This method give continuous results using: + * alpha = 0 + * beta = 1 + + hazen: + method 5 of H&F [1]_. + This method give continuous results using: + * alpha = 1/2 + * beta = 1/2 + + weibull: + method 6 of H&F [1]_. + This method give continuous results using: + * alpha = 0 + * beta = 0 + + linear: + method 7 of H&F [1]_. + This method give continuous results using: + * alpha = 1 + * beta = 1 + + median_unbiased: + method 8 of H&F [1]_. + This method is probably the best method if the sample + distribution function is unknown (see reference). + This method give continuous results using: + * alpha = 1/3 + * beta = 1/3 + + normal_unbiased: + method 9 of H&F [1]_. + This method is probably the best method if the sample + distribution function is known to be normal. + This method give continuous results using: + * alpha = 3/8 + * beta = 3/8 + + lower: + NumPy method kept for backwards compatibility. + Takes ``i`` as the interpolation point. + + higher: + NumPy method kept for backwards compatibility. + Takes ``j`` as the interpolation point. + + nearest: + NumPy method kept for backwards compatibility. + Takes ``i`` or ``j``, whichever is nearest. + + midpoint: + NumPy method kept for backwards compatibility. + Uses ``(i + j) / 2``. Examples -------- @@ -3485,26 +4065,26 @@ def percentile(a, q, axis=None, out=None, >>> np.percentile(a, 50) 3.5 >>> np.percentile(a, 50, axis=0) - array([[ 6.5, 4.5, 2.5]]) + array([6.5, 4.5, 2.5]) >>> np.percentile(a, 50, axis=1) - array([ 7., 2.]) + array([7., 2.]) >>> np.percentile(a, 50, axis=1, keepdims=True) - array([[ 7.], - [ 2.]]) + array([[7.], + [2.]]) >>> m = np.percentile(a, 50, axis=0) >>> out = np.zeros_like(m) >>> np.percentile(a, 50, axis=0, out=out) - array([[ 6.5, 4.5, 2.5]]) + array([6.5, 4.5, 2.5]) >>> m - array([[ 6.5, 4.5, 2.5]]) + array([6.5, 4.5, 2.5]) >>> b = a.copy() >>> np.percentile(b, 50, axis=1, overwrite_input=True) - array([ 7., 2.]) + array([7., 2.]) >>> assert not np.all(a == b) - The different types of interpolation can be visualized graphically: + The different methods can be visualized graphically: .. plot:: @@ -3514,37 +4094,66 @@ def percentile(a, q, axis=None, out=None, p = np.linspace(0, 100, 6001) ax = plt.gca() lines = [ - ('linear', None), - ('higher', '--'), - ('lower', '--'), - ('nearest', '-.'), - ('midpoint', '-.'), - ] - for interpolation, style in lines: + ('linear', '-', 'C0'), + ('inverted_cdf', ':', 'C1'), + # Almost the same as `inverted_cdf`: + ('averaged_inverted_cdf', '-.', 'C1'), + ('closest_observation', ':', 'C2'), + ('interpolated_inverted_cdf', '--', 'C1'), + ('hazen', '--', 'C3'), + ('weibull', '-.', 'C4'), + ('median_unbiased', '--', 'C5'), + ('normal_unbiased', '-.', 'C6'), + ] + for method, style, color in lines: ax.plot( - p, np.percentile(a, p, interpolation=interpolation), - label=interpolation, linestyle=style) + p, np.percentile(a, p, method=method), + label=method, linestyle=style, color=color) ax.set( - title='Interpolation methods for list: ' + str(a), + title='Percentiles for different methods and data: ' + str(a), xlabel='Percentile', - ylabel='List item returned', + ylabel='Estimated percentile value', yticks=a) ax.legend() plt.show() + References + ---------- + .. [1] R. J. Hyndman and Y. Fan, + "Sample quantiles in statistical packages," + The American Statistician, 50(4), pp. 361-365, 1996 + """ - q = np.true_divide(q, 100.0) # handles the asarray for us too + if interpolation is not None: + method = _check_interpolation_as_method( + method, interpolation, "percentile") + q = np.true_divide(q, 100) + q = asanyarray(q) # undo any decay that the ufunc performed (see gh-13105) if not _quantile_is_valid(q): raise ValueError("Percentiles must be in the range [0, 100]") return _quantile_unchecked( - a, q, axis, out, overwrite_input, interpolation, keepdims) + a, q, axis, out, overwrite_input, method, keepdims) + +def _quantile_dispatcher(a, q, axis=None, out=None, overwrite_input=None, + method=None, keepdims=None, *, interpolation=None): + return (a, q, out) -def quantile(a, q, axis=None, out=None, - overwrite_input=False, interpolation='linear', keepdims=False): + +@array_function_dispatch(_quantile_dispatcher) +def quantile(a, + q, + axis=None, + out=None, + overwrite_input=False, + method="linear", + keepdims=False, + *, + interpolation=None): """ - Compute the `q`th quantile of the data along the specified axis. - ..versionadded:: 1.15.0 + Compute the q-th quantile of the data along the specified axis. + + .. versionadded:: 1.15.0 Parameters ---------- @@ -3554,33 +4163,55 @@ def quantile(a, q, axis=None, out=None, Quantile or sequence of quantiles to compute, which must be between 0 and 1 inclusive. axis : {int, tuple of int, None}, optional - Axis or axes along which the quantiles are computed. The - default is to compute the quantile(s) along a flattened - version of the array. + Axis or axes along which the quantiles are computed. The default is + to compute the quantile(s) along a flattened version of the array. out : ndarray, optional - Alternative output array in which to place the result. It must - have the same shape and buffer length as the expected output, - but the type (of the output) will be cast if necessary. + Alternative output array in which to place the result. It must have + the same shape and buffer length as the expected output, but the + type (of the output) will be cast if necessary. overwrite_input : bool, optional - If True, then allow the input array `a` to be modified by intermediate - calculations, to save memory. In this case, the contents of the input - `a` after this function completes is undefined. - interpolation : {'linear', 'lower', 'higher', 'midpoint', 'nearest'} - This optional parameter specifies the interpolation method to - use when the desired quantile lies between two data points - ``i < j``: - * linear: ``i + (j - i) * fraction``, where ``fraction`` - is the fractional part of the index surrounded by ``i`` - and ``j``. - * lower: ``i``. - * higher: ``j``. - * nearest: ``i`` or ``j``, whichever is nearest. - * midpoint: ``(i + j) / 2``. + If True, then allow the input array `a` to be modified by + intermediate calculations, to save memory. In this case, the + contents of the input `a` after this function completes is + undefined. + method : str, optional + This parameter specifies the method to use for estimating the + quantile. There are many different methods, some unique to NumPy. + See the notes for explanation. The options sorted by their R type + as summarized in the H&F paper [1]_ are: + + 1. 'inverted_cdf' + 2. 'averaged_inverted_cdf' + 3. 'closest_observation' + 4. 'interpolated_inverted_cdf' + 5. 'hazen' + 6. 'weibull' + 7. 'linear' (default) + 8. 'median_unbiased' + 9. 'normal_unbiased' + + The first three methods are discontiuous. NumPy further defines the + following discontinuous variations of the default 'linear' (7.) option: + + * 'lower' + * 'higher', + * 'midpoint' + * 'nearest' + + .. versionchanged:: 1.22.0 + This argument was previously called "interpolation" and only + offered the "linear" default and last four options. + keepdims : bool, optional If this is set to True, the axes which are reduced are left in the result as dimensions with size one. With this option, the result will broadcast correctly against the original array `a`. + interpolation : str, optional + Deprecated name for the method keyword argument. + + .. deprecated:: 1.22.0 + Returns ------- quantile : scalar or ndarray @@ -3602,14 +4233,98 @@ def quantile(a, q, axis=None, out=None, Notes ----- - Given a vector ``V`` of length ``N``, the ``q``-th quantile of - ``V`` is the value ``q`` of the way from the minimum to the - maximum in a sorted copy of ``V``. The values and distances of - the two nearest neighbors as well as the `interpolation` parameter - will determine the quantile if the normalized ranking does not - match the location of ``q`` exactly. This function is the same as - the median if ``q=0.5``, the same as the minimum if ``q=0.0`` and the - same as the maximum if ``q=1.0``. + Given a vector ``V`` of length ``N``, the q-th quantile of ``V`` is the + value ``q`` of the way from the minimum to the maximum in a sorted copy of + ``V``. The values and distances of the two nearest neighbors as well as the + `method` parameter will determine the quantile if the normalized + ranking does not match the location of ``q`` exactly. This function is the + same as the median if ``q=0.5``, the same as the minimum if ``q=0.0`` and + the same as the maximum if ``q=1.0``. + + This optional `method` parameter specifies the method to use when the + desired quantile lies between two data points ``i < j``. + If ``g`` is the fractional part of the index surrounded by ``i`` and + alpha and beta are correction constants modifying i and j. + + .. math:: + i + g = (q - alpha) / ( n - alpha - beta + 1 ) + + The different methods then work as follows + + inverted_cdf: + method 1 of H&F [1]_. + This method gives discontinuous results: + * if g > 0 ; then take j + * if g = 0 ; then take i + + averaged_inverted_cdf: + method 2 of H&F [1]_. + This method give discontinuous results: + * if g > 0 ; then take j + * if g = 0 ; then average between bounds + + closest_observation: + method 3 of H&F [1]_. + This method give discontinuous results: + * if g > 0 ; then take j + * if g = 0 and index is odd ; then take j + * if g = 0 and index is even ; then take i + + interpolated_inverted_cdf: + method 4 of H&F [1]_. + This method give continuous results using: + * alpha = 0 + * beta = 1 + + hazen: + method 5 of H&F [1]_. + This method give continuous results using: + * alpha = 1/2 + * beta = 1/2 + + weibull: + method 6 of H&F [1]_. + This method give continuous results using: + * alpha = 0 + * beta = 0 + + linear: + method 7 of H&F [1]_. + This method give continuous results using: + * alpha = 1 + * beta = 1 + + median_unbiased: + method 8 of H&F [1]_. + This method is probably the best method if the sample + distribution function is unknown (see reference). + This method give continuous results using: + * alpha = 1/3 + * beta = 1/3 + + normal_unbiased: + method 9 of H&F [1]_. + This method is probably the best method if the sample + distribution function is known to be normal. + This method give continuous results using: + * alpha = 3/8 + * beta = 3/8 + + lower: + NumPy method kept for backwards compatibility. + Takes ``i`` as the interpolation point. + + higher: + NumPy method kept for backwards compatibility. + Takes ``j`` as the interpolation point. + + nearest: + NumPy method kept for backwards compatibility. + Takes ``i`` or ``j``, whichever is nearest. + + midpoint: + NumPy method kept for backwards compatibility. + Uses ``(i + j) / 2``. Examples -------- @@ -3620,36 +4335,58 @@ def quantile(a, q, axis=None, out=None, >>> np.quantile(a, 0.5) 3.5 >>> np.quantile(a, 0.5, axis=0) - array([[ 6.5, 4.5, 2.5]]) + array([6.5, 4.5, 2.5]) >>> np.quantile(a, 0.5, axis=1) - array([ 7., 2.]) + array([7., 2.]) >>> np.quantile(a, 0.5, axis=1, keepdims=True) - array([[ 7.], - [ 2.]]) + array([[7.], + [2.]]) >>> m = np.quantile(a, 0.5, axis=0) >>> out = np.zeros_like(m) >>> np.quantile(a, 0.5, axis=0, out=out) - array([[ 6.5, 4.5, 2.5]]) + array([6.5, 4.5, 2.5]) >>> m - array([[ 6.5, 4.5, 2.5]]) + array([6.5, 4.5, 2.5]) >>> b = a.copy() >>> np.quantile(b, 0.5, axis=1, overwrite_input=True) - array([ 7., 2.]) + array([7., 2.]) >>> assert not np.all(a == b) + + See also `numpy.percentile` for a visualization of most methods. + + References + ---------- + .. [1] R. J. Hyndman and Y. Fan, + "Sample quantiles in statistical packages," + The American Statistician, 50(4), pp. 361-365, 1996 + """ + if interpolation is not None: + method = _check_interpolation_as_method( + method, interpolation, "quantile") + q = np.asanyarray(q) if not _quantile_is_valid(q): raise ValueError("Quantiles must be in the range [0, 1]") return _quantile_unchecked( - a, q, axis, out, overwrite_input, interpolation, keepdims) + a, q, axis, out, overwrite_input, method, keepdims) -def _quantile_unchecked(a, q, axis=None, out=None, overwrite_input=False, - interpolation='linear', keepdims=False): +def _quantile_unchecked(a, + q, + axis=None, + out=None, + overwrite_input=False, + method="linear", + keepdims=False): """Assumes that q is in [0, 1], and is an ndarray""" - r, k = _ureduce(a, func=_quantile_ureduce_func, q=q, axis=axis, out=out, + r, k = _ureduce(a, + func=_quantile_ureduce_func, + q=q, + axis=axis, + out=out, overwrite_input=overwrite_input, - interpolation=interpolation) + method=method) if keepdims: return r.reshape(q.shape + k) else: @@ -3660,152 +4397,305 @@ def _quantile_is_valid(q): # avoid expensive reductions, relevant for arrays with < O(1000) elements if q.ndim == 1 and q.size < 10: for i in range(q.size): - if q[i] < 0.0 or q[i] > 1.0: + if not (0.0 <= q[i] <= 1.0): return False else: - # faster than any() - if np.count_nonzero(q < 0.0) or np.count_nonzero(q > 1.0): + if not (np.all(0 <= q) and np.all(q <= 1)): return False return True -def _quantile_ureduce_func(a, q, axis=None, out=None, overwrite_input=False, - interpolation='linear', keepdims=False): - a = asarray(a) - if q.ndim == 0: - # Do not allow 0-d arrays because following code fails for scalar - zerod = True - q = q[None] - else: - zerod = False - - # prepare a for partitioning - if overwrite_input: - if axis is None: - ap = a.ravel() - else: - ap = a - else: - if axis is None: - ap = a.flatten() - else: - ap = a.copy() - - if axis is None: - axis = 0 - - Nx = ap.shape[axis] - indices = q * (Nx - 1) - - # round fractional indices according to interpolation method - if interpolation == 'lower': - indices = floor(indices).astype(intp) - elif interpolation == 'higher': - indices = ceil(indices).astype(intp) - elif interpolation == 'midpoint': - indices = 0.5 * (floor(indices) + ceil(indices)) - elif interpolation == 'nearest': - indices = around(indices).astype(intp) - elif interpolation == 'linear': - pass # keep index as fraction and interpolate - else: - raise ValueError( - "interpolation can only be 'linear', 'lower' 'higher', " - "'midpoint', or 'nearest'") - - n = np.array(False, dtype=bool) # check for nan's flag - if indices.dtype == intp: # take the points along axis - # Check if the array contains any nan's - if np.issubdtype(a.dtype, np.inexact): - indices = concatenate((indices, [-1])) - - ap.partition(indices, axis=axis) - # ensure axis with qth is first - ap = np.moveaxis(ap, axis, 0) - axis = 0 - - # Check if the array contains any nan's - if np.issubdtype(a.dtype, np.inexact): - indices = indices[:-1] - n = np.isnan(ap[-1:, ...]) - - if zerod: - indices = indices[0] - r = take(ap, indices, axis=axis, out=out) - - - else: # weight the points above and below the indices - indices_below = floor(indices).astype(intp) - indices_above = indices_below + 1 - indices_above[indices_above > Nx - 1] = Nx - 1 +def _check_interpolation_as_method(method, interpolation, fname): + # Deprecated NumPy 1.22, 2021-11-08 + warnings.warn( + f"the `interpolation=` argument to {fname} was renamed to " + "`method=`, which has additional options.\n" + "Users of the modes 'nearest', 'lower', 'higher', or " + "'midpoint' are encouraged to review the method they. " + "(Deprecated NumPy 1.22)", + DeprecationWarning, stacklevel=4) + if method != "linear": + # sanity check, we assume this basically never happens + raise TypeError( + "You shall not pass both `method` and `interpolation`!\n" + "(`interpolation` is Deprecated in favor of `method`)") + return interpolation + + +def _compute_virtual_index(n, quantiles, alpha: float, beta: float): + """ + Compute the floating point indexes of an array for the linear + interpolation of quantiles. + n : array_like + The sample sizes. + quantiles : array_like + The quantiles values. + alpha : float + A constant used to correct the index computed. + beta : float + A constant used to correct the index computed. - # Check if the array contains any nan's - if np.issubdtype(a.dtype, np.inexact): - indices_above = concatenate((indices_above, [-1])) + alpha and beta values depend on the chosen method + (see quantile documentation) - weights_above = indices - indices_below - weights_below = 1.0 - weights_above + Reference: + Hyndman&Fan paper "Sample Quantiles in Statistical Packages", + DOI: 10.1080/00031305.1996.10473566 + """ + return n * quantiles + ( + alpha + quantiles * (1 - alpha - beta) + ) - 1 - weights_shape = [1, ] * ap.ndim - weights_shape[axis] = len(indices) - weights_below.shape = weights_shape - weights_above.shape = weights_shape - ap.partition(concatenate((indices_below, indices_above)), axis=axis) +def _get_gamma(virtual_indexes, previous_indexes, method): + """ + Compute gamma (a.k.a 'm' or 'weight') for the linear interpolation + of quantiles. + + virtual_indexes : array_like + The indexes where the percentile is supposed to be found in the sorted + sample. + previous_indexes : array_like + The floor values of virtual_indexes. + interpolation : dict + The interpolation method chosen, which may have a specific rule + modifying gamma. + + gamma is usually the fractional part of virtual_indexes but can be modified + by the interpolation method. + """ + gamma = np.asanyarray(virtual_indexes - previous_indexes) + gamma = method["fix_gamma"](gamma, virtual_indexes) + return np.asanyarray(gamma) - # ensure axis with qth is first - ap = np.moveaxis(ap, axis, 0) - weights_below = np.moveaxis(weights_below, axis, 0) - weights_above = np.moveaxis(weights_above, axis, 0) - axis = 0 - # Check if the array contains any nan's - if np.issubdtype(a.dtype, np.inexact): - indices_above = indices_above[:-1] - n = np.isnan(ap[-1:, ...]) +def _lerp(a, b, t, out=None): + """ + Compute the linear interpolation weighted by gamma on each point of + two same shape array. - x1 = take(ap, indices_below, axis=axis) * weights_below - x2 = take(ap, indices_above, axis=axis) * weights_above + a : array_like + Left bound. + b : array_like + Right bound. + t : array_like + The interpolation weight. + out : array_like + Output array. + """ + diff_b_a = subtract(b, a) + # asanyarray is a stop-gap until gh-13105 + lerp_interpolation = asanyarray(add(a, diff_b_a * t, out=out)) + subtract(b, diff_b_a * (1 - t), out=lerp_interpolation, where=t >= 0.5) + if lerp_interpolation.ndim == 0 and out is None: + lerp_interpolation = lerp_interpolation[()] # unpack 0d arrays + return lerp_interpolation + + +def _get_gamma_mask(shape, default_value, conditioned_value, where): + out = np.full(shape, default_value) + np.copyto(out, conditioned_value, where=where, casting="unsafe") + return out + + +def _discret_interpolation_to_boundaries(index, gamma_condition_fun): + previous = np.floor(index) + next = previous + 1 + gamma = index - previous + res = _get_gamma_mask(shape=index.shape, + default_value=next, + conditioned_value=previous, + where=gamma_condition_fun(gamma, index) + ).astype(np.intp) + # Some methods can lead to out-of-bound integers, clip them: + res[res < 0] = 0 + return res + + +def _closest_observation(n, quantiles): + gamma_fun = lambda gamma, index: (gamma == 0) & (np.floor(index) % 2 == 0) + return _discret_interpolation_to_boundaries((n * quantiles) - 1 - 0.5, + gamma_fun) + + +def _inverted_cdf(n, quantiles): + gamma_fun = lambda gamma, _: (gamma == 0) + return _discret_interpolation_to_boundaries((n * quantiles) - 1, + gamma_fun) + + +def _quantile_ureduce_func( + a: np.array, + q: np.array, + axis: int = None, + out=None, + overwrite_input: bool = False, + method="linear", +) -> np.array: + if q.ndim > 2: + # The code below works fine for nd, but it might not have useful + # semantics. For now, keep the supported dimensions the same as it was + # before. + raise ValueError("q must be a scalar or 1d") + if overwrite_input: + if axis is None: + axis = 0 + arr = a.ravel() + else: + arr = a + else: + if axis is None: + axis = 0 + arr = a.flatten() + else: + arr = a.copy() + result = _quantile(arr, + quantiles=q, + axis=axis, + method=method, + out=out) + return result - # ensure axis with qth is first - x1 = np.moveaxis(x1, axis, 0) - x2 = np.moveaxis(x2, axis, 0) - if zerod: - x1 = x1.squeeze(0) - x2 = x2.squeeze(0) +def _get_indexes(arr, virtual_indexes, valid_values_count): + """ + Get the valid indexes of arr neighbouring virtual_indexes. + Note + This is a companion function to linear interpolation of + Quantiles - if out is not None: - r = add(x1, x2, out=out) + Returns + ------- + (previous_indexes, next_indexes): Tuple + A Tuple of virtual_indexes neighbouring indexes + """ + previous_indexes = np.asanyarray(np.floor(virtual_indexes)) + next_indexes = np.asanyarray(previous_indexes + 1) + indexes_above_bounds = virtual_indexes >= valid_values_count - 1 + # When indexes is above max index, take the max value of the array + if indexes_above_bounds.any(): + previous_indexes[indexes_above_bounds] = -1 + next_indexes[indexes_above_bounds] = -1 + # When indexes is below min index, take the min value of the array + indexes_below_bounds = virtual_indexes < 0 + if indexes_below_bounds.any(): + previous_indexes[indexes_below_bounds] = 0 + next_indexes[indexes_below_bounds] = 0 + if np.issubdtype(arr.dtype, np.inexact): + # After the sort, slices having NaNs will have for last element a NaN + virtual_indexes_nans = np.isnan(virtual_indexes) + if virtual_indexes_nans.any(): + previous_indexes[virtual_indexes_nans] = -1 + next_indexes[virtual_indexes_nans] = -1 + previous_indexes = previous_indexes.astype(np.intp) + next_indexes = next_indexes.astype(np.intp) + return previous_indexes, next_indexes + + +def _quantile( + arr: np.array, + quantiles: np.array, + axis: int = -1, + method="linear", + out=None, +): + """ + Private function that doesn't support extended axis or keepdims. + These methods are extended to this function using _ureduce + See nanpercentile for parameter usage + It computes the quantiles of the array for the given axis. + A linear interpolation is performed based on the `interpolation`. + + By default, the method is "linear" where alpha == beta == 1 which + performs the 7th method of Hyndman&Fan. + With "median_unbiased" we get alpha == beta == 1/3 + thus the 8th method of Hyndman&Fan. + """ + # --- Setup + arr = np.asanyarray(arr) + values_count = arr.shape[axis] + # The dimensions of `q` are prepended to the output shape, so we need the + # axis being sampled from `arr` to be last. + DATA_AXIS = 0 + if axis != DATA_AXIS: # But moveaxis is slow, so only call it if axis!=0. + arr = np.moveaxis(arr, axis, destination=DATA_AXIS) + # --- Computation of indexes + # Index where to find the value in the sorted array. + # Virtual because it is a floating point value, not an valid index. + # The nearest neighbours are used for interpolation + try: + method = _QuantileMethods[method] + except KeyError: + raise ValueError( + f"{method!r} is not a valid method. Use one of: " + f"{_QuantileMethods.keys()}") from None + virtual_indexes = method["get_virtual_index"](values_count, quantiles) + virtual_indexes = np.asanyarray(virtual_indexes) + if np.issubdtype(virtual_indexes.dtype, np.integer): + # No interpolation needed, take the points along axis + if np.issubdtype(arr.dtype, np.inexact): + # may contain nan, which would sort to the end + arr.partition(concatenate((virtual_indexes.ravel(), [-1])), axis=0) + slices_having_nans = np.isnan(arr[-1]) else: - r = add(x1, x2) - - if np.any(n): - warnings.warn("Invalid value encountered in percentile", - RuntimeWarning, stacklevel=3) - if zerod: - if ap.ndim == 1: - if out is not None: - out[...] = a.dtype.type(np.nan) - r = out - else: - r = a.dtype.type(np.nan) - else: - r[..., n.squeeze(0)] = a.dtype.type(np.nan) + # cannot contain nan + arr.partition(virtual_indexes.ravel(), axis=0) + slices_having_nans = np.array(False, dtype=bool) + result = take(arr, virtual_indexes, axis=0, out=out) + else: + previous_indexes, next_indexes = _get_indexes(arr, + virtual_indexes, + values_count) + # --- Sorting + arr.partition( + np.unique(np.concatenate(([0, -1], + previous_indexes.ravel(), + next_indexes.ravel(), + ))), + axis=DATA_AXIS) + if np.issubdtype(arr.dtype, np.inexact): + slices_having_nans = np.isnan( + take(arr, indices=-1, axis=DATA_AXIS) + ) else: - if r.ndim == 1: - r[:] = a.dtype.type(np.nan) - else: - r[..., n.repeat(q.size, 0)] = a.dtype.type(np.nan) + slices_having_nans = None + # --- Get values from indexes + previous = np.take(arr, previous_indexes, axis=DATA_AXIS) + next = np.take(arr, next_indexes, axis=DATA_AXIS) + # --- Linear interpolation + gamma = _get_gamma(virtual_indexes, previous_indexes, method) + result_shape = virtual_indexes.shape + (1,) * (arr.ndim - 1) + gamma = gamma.reshape(result_shape) + result = _lerp(previous, + next, + gamma, + out=out) + if np.any(slices_having_nans): + if result.ndim == 0 and out is None: + # can't write to a scalar + result = arr.dtype.type(np.nan) + else: + result[..., slices_having_nans] = np.nan + return result + - return r +def _trapz_dispatcher(y, x=None, dx=None, axis=None): + return (y, x) +@array_function_dispatch(_trapz_dispatcher) def trapz(y, x=None, dx=1.0, axis=-1): - """ + r""" Integrate along the given axis using the composite trapezoidal rule. - Integrate `y` (`x`) along given axis. + If `x` is provided, the integration happens in sequence along its + elements - they are not sorted. + + Integrate `y` (`x`) along each 1d slice on the given axis, compute + :math:`\int y(x) dx`. + When `x` is specified, this integrates along the parametric curve, + computing :math:`\int_t y(t) dt = + \int_t y(t) \left.\frac{dx}{dt}\right|_{x=x(t)} dt`. Parameters ---------- @@ -3822,8 +4712,11 @@ def trapz(y, x=None, dx=1.0, axis=-1): Returns ------- - trapz : float - Definite integral as approximated by trapezoidal rule. + trapz : float or ndarray + Definite integral of 'y' = n-dimensional array as approximated along + a single axis by the trapezoidal rule. If 'y' is a 1-dimensional array, + then the result is a float. If 'n' is greater than 1, then the result + is an 'n-1' dimensional array. See Also -------- @@ -3840,10 +4733,10 @@ def trapz(y, x=None, dx=1.0, axis=-1): References ---------- - .. [1] Wikipedia page: http://en.wikipedia.org/wiki/Trapezoidal_rule + .. [1] Wikipedia page: https://en.wikipedia.org/wiki/Trapezoidal_rule .. [2] Illustration image: - http://en.wikipedia.org/wiki/File:Composite_trapezoidal_rule_illustration.png + https://en.wikipedia.org/wiki/File:Composite_trapezoidal_rule_illustration.png Examples -------- @@ -3853,15 +4746,28 @@ def trapz(y, x=None, dx=1.0, axis=-1): 8.0 >>> np.trapz([1,2,3], dx=2) 8.0 + + Using a decreasing `x` corresponds to integrating in reverse: + + >>> np.trapz([1,2,3], x=[8,6,4]) + -8.0 + + More generally `x` is used to integrate along a parametric curve. + This finds the area of a circle, noting we repeat the sample which closes + the curve: + + >>> theta = np.linspace(0, 2 * np.pi, num=1000, endpoint=True) + >>> np.trapz(np.cos(theta), x=np.sin(theta)) + 3.141571941375841 + >>> a = np.arange(6).reshape(2, 3) >>> a array([[0, 1, 2], [3, 4, 5]]) >>> np.trapz(a, axis=0) - array([ 1.5, 2.5, 3.5]) + array([1.5, 2.5, 3.5]) >>> np.trapz(a, axis=1) - array([ 2., 8.]) - + array([2., 8.]) """ y = asanyarray(y) if x is None: @@ -3891,43 +4797,13 @@ def trapz(y, x=None, dx=1.0, axis=-1): return ret -#always succeed -def add_newdoc(place, obj, doc): - """ - Adds documentation to obj which is in module place. - - If doc is a string add it to obj as a docstring - - If doc is a tuple, then the first element is interpreted as - an attribute of obj and the second as the docstring - (method, docstring) - - If doc is a list, then each element of the list should be a - sequence of length two --> [(method1, docstring1), - (method2, docstring2), ...] - - This routine never raises an error. - - This routine cannot modify read-only docstrings, as appear - in new-style classes or built-in functions. Because this - routine never raises an error the caller must check manually - that the docstrings were changed. - """ - try: - new = getattr(__import__(place, globals(), {}, [obj]), obj) - if isinstance(doc, str): - add_docstring(new, doc.strip()) - elif isinstance(doc, tuple): - add_docstring(getattr(new, doc[0]), doc[1].strip()) - elif isinstance(doc, list): - for val in doc: - add_docstring(getattr(new, val[0]), val[1].strip()) - except Exception: - pass +def _meshgrid_dispatcher(*xi, copy=None, sparse=None, indexing=None): + return xi # Based on scitools meshgrid -def meshgrid(*xi, **kwargs): +@array_function_dispatch(_meshgrid_dispatcher) +def meshgrid(*xi, copy=True, sparse=False, indexing='xy'): """ Return coordinate matrices from coordinate vectors. @@ -3948,7 +4824,13 @@ def meshgrid(*xi, **kwargs): .. versionadded:: 1.7.0 sparse : bool, optional - If True a sparse grid is returned in order to conserve memory. + If True the shape of the returned coordinate array for dimension *i* + is reduced from ``(N1, ..., Ni, ... Nn)`` to + ``(1, ..., 1, Ni, 1, ..., 1)``. These sparse coordinate grids are + intended to be use with :ref:`basics.broadcasting`. When all + coordinates are used in an expression, broadcasting still leads to a + fully-dimensonal result array. + Default is False. .. versionadded:: 1.7.0 @@ -3982,12 +4864,12 @@ def meshgrid(*xi, **kwargs): 'xy' indexing and (M, N, P) for 'ij' indexing. The difference is illustrated by the following code snippet:: - xv, yv = np.meshgrid(x, y, sparse=False, indexing='ij') + xv, yv = np.meshgrid(x, y, indexing='ij') for i in range(nx): for j in range(ny): # treat xv[i,j], yv[i,j] - xv, yv = np.meshgrid(x, y, sparse=False, indexing='xy') + xv, yv = np.meshgrid(x, y, indexing='xy') for i in range(nx): for j in range(ny): # treat xv[j,i], yv[j,i] @@ -3996,10 +4878,9 @@ def meshgrid(*xi, **kwargs): See Also -------- - index_tricks.mgrid : Construct a multi-dimensional "meshgrid" - using indexing notation. - index_tricks.ogrid : Construct an open multi-dimensional "meshgrid" - using indexing notation. + mgrid : Construct a multi-dimensional "meshgrid" using indexing notation. + ogrid : Construct an open multi-dimensional "meshgrid" using indexing + notation. Examples -------- @@ -4008,37 +4889,45 @@ def meshgrid(*xi, **kwargs): >>> y = np.linspace(0, 1, ny) >>> xv, yv = np.meshgrid(x, y) >>> xv - array([[ 0. , 0.5, 1. ], - [ 0. , 0.5, 1. ]]) + array([[0. , 0.5, 1. ], + [0. , 0.5, 1. ]]) >>> yv - array([[ 0., 0., 0.], - [ 1., 1., 1.]]) + array([[0., 0., 0.], + [1., 1., 1.]]) >>> xv, yv = np.meshgrid(x, y, sparse=True) # make sparse output arrays >>> xv - array([[ 0. , 0.5, 1. ]]) + array([[0. , 0.5, 1. ]]) >>> yv - array([[ 0.], - [ 1.]]) - - `meshgrid` is very useful to evaluate functions on a grid. - - >>> x = np.arange(-5, 5, 0.1) - >>> y = np.arange(-5, 5, 0.1) - >>> xx, yy = np.meshgrid(x, y, sparse=True) - >>> z = np.sin(xx**2 + yy**2) / (xx**2 + yy**2) - >>> h = plt.contourf(x,y,z) + array([[0.], + [1.]]) + + `meshgrid` is very useful to evaluate functions on a grid. If the + function depends on all coordinates, you can use the parameter + ``sparse=True`` to save memory and computation time. + + >>> x = np.linspace(-5, 5, 101) + >>> y = np.linspace(-5, 5, 101) + >>> # full coorindate arrays + >>> xx, yy = np.meshgrid(x, y) + >>> zz = np.sqrt(xx**2 + yy**2) + >>> xx.shape, yy.shape, zz.shape + ((101, 101), (101, 101), (101, 101)) + >>> # sparse coordinate arrays + >>> xs, ys = np.meshgrid(x, y, sparse=True) + >>> zs = np.sqrt(xs**2 + ys**2) + >>> xs.shape, ys.shape, zs.shape + ((1, 101), (101, 1), (101, 101)) + >>> np.array_equal(zz, zs) + True + >>> import matplotlib.pyplot as plt + >>> h = plt.contourf(x, y, zs) + >>> plt.axis('scaled') + >>> plt.colorbar() + >>> plt.show() """ ndim = len(xi) - copy_ = kwargs.pop('copy', True) - sparse = kwargs.pop('sparse', False) - indexing = kwargs.pop('indexing', 'xy') - - if kwargs: - raise TypeError("meshgrid() got an unexpected keyword argument '%s'" - % (list(kwargs)[0],)) - if indexing not in ['xy', 'ij']: raise ValueError( "Valid values for `indexing` are 'xy' and 'ij'.") @@ -4056,12 +4945,17 @@ def meshgrid(*xi, **kwargs): # Return the full N-D matrix (not only the 1-D vector) output = np.broadcast_arrays(*output, subok=True) - if copy_: + if copy: output = [x.copy() for x in output] return output +def _delete_dispatcher(arr, obj, axis=None): + return (arr, obj) + + +@array_function_dispatch(_delete_dispatcher) def delete(arr, obj, axis=None): """ Return a new array with sub-arrays along an axis deleted. For a one @@ -4071,12 +4965,17 @@ def delete(arr, obj, axis=None): Parameters ---------- arr : array_like - Input array. + Input array. obj : slice, int or array of ints - Indicate which sub-arrays to remove. + Indicate indices of sub-arrays to remove along the specified axis. + + .. versionchanged:: 1.19.0 + Boolean indices are now treated as a mask of elements to remove, + rather than being cast to the integers 0 and 1. + axis : int, optional - The axis along which to delete the subarray defined by `obj`. - If `axis` is None, `obj` is applied to the flattened array. + The axis along which to delete the subarray defined by `obj`. + If `axis` is None, `obj` is applied to the flattened array. Returns ------- @@ -4094,6 +4993,7 @@ def delete(arr, obj, axis=None): ----- Often it is preferable to use a boolean mask. For example: + >>> arr = np.arange(12) + 1 >>> mask = np.ones(len(arr), dtype=bool) >>> mask[[0,2,4]] = False >>> result = arr[mask,...] @@ -4133,20 +5033,11 @@ def delete(arr, obj, axis=None): if axis is None: if ndim != 1: arr = arr.ravel() + # needed for np.matrix, which is still not 1d after being ravelled ndim = arr.ndim - axis = -1 - - if ndim == 0: - # 2013-09-24, 1.9 - warnings.warn( - "in the future the special handling of scalars will be removed " - "from delete and raise an error", DeprecationWarning, stacklevel=2) - if wrap: - return wrap(arr) - else: - return arr.copy(order=arrorder) - - axis = normalize_axis_index(axis, ndim) + axis = ndim - 1 + else: + axis = normalize_axis_index(axis, ndim) slobj = [slice(None)]*ndim N = arr.shape[axis] @@ -4177,7 +5068,7 @@ def delete(arr, obj, axis=None): else: slobj[axis] = slice(None, start) new[tuple(slobj)] = arr[tuple(slobj)] - # copy end chunck + # copy end chunk if stop == N: pass else: @@ -4202,18 +5093,8 @@ def delete(arr, obj, axis=None): else: return new - _obj = obj - obj = np.asarray(obj) - # After removing the special handling of booleans and out of - # bounds values, the conversion to the array can be removed. - if obj.dtype == bool: - warnings.warn("in the future insert will treat boolean arrays and " - "array-likes as boolean index instead of casting it " - "to integer", FutureWarning, stacklevel=2) - obj = obj.astype(intp) - if isinstance(_obj, (int, long, integer)): + if isinstance(obj, (int, integer)) and not isinstance(obj, bool): # optimization for a single value - obj = obj.item() if (obj < -N or obj >= N): raise IndexError( "index %i is out of bounds for axis %i with " @@ -4229,35 +5110,23 @@ def delete(arr, obj, axis=None): slobj2[axis] = slice(obj+1, None) new[tuple(slobj)] = arr[tuple(slobj2)] else: + _obj = obj + obj = np.asarray(obj) if obj.size == 0 and not isinstance(_obj, np.ndarray): obj = obj.astype(intp) - if not np.can_cast(obj, intp, 'same_kind'): - # obj.size = 1 special case always failed and would just - # give superfluous warnings. - # 2013-09-24, 1.9 - warnings.warn( - "using a non-integer array as obj in delete will result in an " - "error in the future", DeprecationWarning, stacklevel=2) - obj = obj.astype(intp) - keep = ones(N, dtype=bool) - # Test if there are out of bound indices, this is deprecated - inside_bounds = (obj < N) & (obj >= -N) - if not inside_bounds.all(): - # 2013-09-24, 1.9 - warnings.warn( - "in the future out of bounds indices will raise an error " - "instead of being ignored by `numpy.delete`.", - DeprecationWarning, stacklevel=2) - obj = obj[inside_bounds] - positive_indices = obj >= 0 - if not positive_indices.all(): - warnings.warn( - "in the future negative indices will not be ignored by " - "`numpy.delete`.", FutureWarning, stacklevel=2) - obj = obj[positive_indices] + if obj.dtype == bool: + if obj.shape != (N,): + raise ValueError('boolean array argument obj to delete ' + 'must be one dimensional and match the axis ' + 'length of {}'.format(N)) + + # optimization, the other branch is slower + keep = ~obj + else: + keep = ones(N, dtype=bool) + keep[obj,] = False - keep[obj, ] = False slobj[axis] = keep new = arr[tuple(slobj)] @@ -4267,6 +5136,11 @@ def delete(arr, obj, axis=None): return new +def _insert_dispatcher(arr, obj, values, axis=None): + return (arr, obj, values) + + +@array_function_dispatch(_insert_dispatcher) def insert(arr, obj, values, axis=None): """ Insert values along the given axis before the given indices. @@ -4320,7 +5194,7 @@ def insert(arr, obj, values, axis=None): [2, 2], [3, 3]]) >>> np.insert(a, 1, 5) - array([1, 5, 1, 2, 2, 3, 3]) + array([1, 5, 1, ..., 2, 3, 3]) >>> np.insert(a, 1, 5, axis=1) array([[1, 5, 1], [2, 5, 2], @@ -4340,13 +5214,13 @@ def insert(arr, obj, values, axis=None): >>> b array([1, 1, 2, 2, 3, 3]) >>> np.insert(b, [2, 2], [5, 6]) - array([1, 1, 5, 6, 2, 2, 3, 3]) + array([1, 1, 5, ..., 2, 3, 3]) >>> np.insert(b, slice(2, 4), [5, 6]) - array([1, 1, 5, 2, 6, 2, 3, 3]) + array([1, 1, 5, ..., 2, 3, 3]) >>> np.insert(b, [2, 2], [7.13, False]) # type casting - array([1, 1, 7, 0, 2, 2, 3, 3]) + array([1, 1, 7, ..., 2, 3, 3]) >>> x = np.arange(8).reshape(2, 4) >>> idx = (1, 3) @@ -4368,19 +5242,9 @@ def insert(arr, obj, values, axis=None): if axis is None: if ndim != 1: arr = arr.ravel() + # needed for np.matrix, which is still not 1d after being ravelled ndim = arr.ndim axis = ndim - 1 - elif ndim == 0: - # 2013-09-24, 1.9 - warnings.warn( - "in the future the special handling of scalars will be removed " - "from insert and raise an error", DeprecationWarning, stacklevel=2) - arr = arr.copy(order=arrorder) - arr[...] = values - if wrap: - return wrap(arr) - else: - return arr else: axis = normalize_axis_index(axis, ndim) slobj = [slice(None)]*ndim @@ -4389,16 +5253,17 @@ def insert(arr, obj, values, axis=None): if isinstance(obj, slice): # turn it into a range object - indices = arange(*obj.indices(N), **{'dtype': intp}) + indices = arange(*obj.indices(N), dtype=intp) else: # need to copy obj, because indices will be changed in-place indices = np.array(obj) if indices.dtype == bool: # See also delete + # 2012-10-11, NumPy 1.8 warnings.warn( "in the future insert will treat boolean arrays and " "array-likes as a boolean index instead of casting it to " - "integer", FutureWarning, stacklevel=2) + "integer", FutureWarning, stacklevel=3) indices = indices.astype(intp) # Code after warning period: #if obj.ndim != 1: @@ -4412,9 +5277,8 @@ def insert(arr, obj, values, axis=None): if indices.size == 1: index = indices.item() if index < -N or index > N: - raise IndexError( - "index %i is out of bounds for axis %i with " - "size %i" % (obj, axis, N)) + raise IndexError(f"index {obj} is out of bounds for axis {axis} " + f"with size {N}") if (index < 0): index += N @@ -4444,13 +5308,6 @@ def insert(arr, obj, values, axis=None): # Can safely cast the empty list to intp indices = indices.astype(intp) - if not np.can_cast(indices, intp, 'same_kind'): - # 2013-09-24, 1.9 - warnings.warn( - "using a non-integer array as obj in insert will result in an " - "error in the future", DeprecationWarning, stacklevel=2) - indices = indices.astype(intp) - indices[indices < 0] += N numnew = len(indices) @@ -4473,6 +5330,11 @@ def insert(arr, obj, values, axis=None): return new +def _append_dispatcher(arr, values, axis=None): + return (arr, values) + + +@array_function_dispatch(_append_dispatcher) def append(arr, values, axis=None): """ Append values to the end of an array. @@ -4505,7 +5367,7 @@ def append(arr, values, axis=None): Examples -------- >>> np.append([1, 2, 3], [[4, 5, 6], [7, 8, 9]]) - array([1, 2, 3, 4, 5, 6, 7, 8, 9]) + array([1, 2, 3, ..., 7, 8, 9]) When `axis` is specified, `values` must have the correct shape. @@ -4515,8 +5377,10 @@ def append(arr, values, axis=None): [7, 8, 9]]) >>> np.append([[1, 2, 3], [4, 5, 6]], [7, 8, 9], axis=0) Traceback (most recent call last): - ... - ValueError: arrays must have same number of dimensions + ... + ValueError: all the input arrays must have same number of dimensions, but + the array at index 0 has 2 dimension(s) and the array at index 1 has 1 + dimension(s) """ arr = asanyarray(arr) @@ -4526,3 +5390,118 @@ def append(arr, values, axis=None): values = ravel(values) axis = arr.ndim-1 return concatenate((arr, values), axis=axis) + + +def _digitize_dispatcher(x, bins, right=None): + return (x, bins) + + +@array_function_dispatch(_digitize_dispatcher) +def digitize(x, bins, right=False): + """ + Return the indices of the bins to which each value in input array belongs. + + ========= ============= ============================ + `right` order of bins returned index `i` satisfies + ========= ============= ============================ + ``False`` increasing ``bins[i-1] <= x < bins[i]`` + ``True`` increasing ``bins[i-1] < x <= bins[i]`` + ``False`` decreasing ``bins[i-1] > x >= bins[i]`` + ``True`` decreasing ``bins[i-1] >= x > bins[i]`` + ========= ============= ============================ + + If values in `x` are beyond the bounds of `bins`, 0 or ``len(bins)`` is + returned as appropriate. + + Parameters + ---------- + x : array_like + Input array to be binned. Prior to NumPy 1.10.0, this array had to + be 1-dimensional, but can now have any shape. + bins : array_like + Array of bins. It has to be 1-dimensional and monotonic. + right : bool, optional + Indicating whether the intervals include the right or the left bin + edge. Default behavior is (right==False) indicating that the interval + does not include the right edge. The left bin end is open in this + case, i.e., bins[i-1] <= x < bins[i] is the default behavior for + monotonically increasing bins. + + Returns + ------- + indices : ndarray of ints + Output array of indices, of same shape as `x`. + + Raises + ------ + ValueError + If `bins` is not monotonic. + TypeError + If the type of the input is complex. + + See Also + -------- + bincount, histogram, unique, searchsorted + + Notes + ----- + If values in `x` are such that they fall outside the bin range, + attempting to index `bins` with the indices that `digitize` returns + will result in an IndexError. + + .. versionadded:: 1.10.0 + + `np.digitize` is implemented in terms of `np.searchsorted`. This means + that a binary search is used to bin the values, which scales much better + for larger number of bins than the previous linear search. It also removes + the requirement for the input array to be 1-dimensional. + + For monotonically _increasing_ `bins`, the following are equivalent:: + + np.digitize(x, bins, right=True) + np.searchsorted(bins, x, side='left') + + Note that as the order of the arguments are reversed, the side must be too. + The `searchsorted` call is marginally faster, as it does not do any + monotonicity checks. Perhaps more importantly, it supports all dtypes. + + Examples + -------- + >>> x = np.array([0.2, 6.4, 3.0, 1.6]) + >>> bins = np.array([0.0, 1.0, 2.5, 4.0, 10.0]) + >>> inds = np.digitize(x, bins) + >>> inds + array([1, 4, 3, 2]) + >>> for n in range(x.size): + ... print(bins[inds[n]-1], "<=", x[n], "<", bins[inds[n]]) + ... + 0.0 <= 0.2 < 1.0 + 4.0 <= 6.4 < 10.0 + 2.5 <= 3.0 < 4.0 + 1.0 <= 1.6 < 2.5 + + >>> x = np.array([1.2, 10.0, 12.4, 15.5, 20.]) + >>> bins = np.array([0, 5, 10, 15, 20]) + >>> np.digitize(x,bins,right=True) + array([1, 2, 3, 4, 4]) + >>> np.digitize(x,bins,right=False) + array([1, 3, 3, 4, 5]) + """ + x = _nx.asarray(x) + bins = _nx.asarray(bins) + + # here for compatibility, searchsorted below is happy to take this + if np.issubdtype(x.dtype, _nx.complexfloating): + raise TypeError("x may not be complex") + + mono = _monotonicity(bins) + if mono == 0: + raise ValueError("bins must be monotonically increasing or decreasing") + + # this is backwards because the arguments below are swapped + side = 'left' if right else 'right' + if mono == -1: + # reverse the bins, and invert the results + return len(bins) - _nx.searchsorted(bins[::-1], x, side=side) + else: + return _nx.searchsorted(bins, x, side=side) diff --git a/numpy/lib/function_base.pyi b/numpy/lib/function_base.pyi new file mode 100644 index 000000000000..7e227f9da52d --- /dev/null +++ b/numpy/lib/function_base.pyi @@ -0,0 +1,704 @@ +import sys +from typing import ( + Literal as L, + List, + Type, + Sequence, + Tuple, + Union, + Any, + TypeVar, + Iterator, + overload, + Callable, + Protocol, + SupportsIndex, + Iterable, + SupportsInt, +) + +if sys.version_info >= (3, 10): + from typing import TypeGuard +else: + from typing_extensions import TypeGuard + +from numpy import ( + vectorize as vectorize, + ufunc, + dtype, + generic, + floating, + complexfloating, + intp, + float64, + complex128, + timedelta64, + datetime64, + object_, + _OrderKACF, +) + +from numpy.typing import ( + NDArray, + ArrayLike, + DTypeLike, + _ShapeLike, + _ScalarLike_co, + _SupportsDType, + _FiniteNestedSequence, + _SupportsArray, + _ArrayLikeInt_co, + _ArrayLikeFloat_co, + _ArrayLikeComplex_co, + _ArrayLikeTD64_co, + _ArrayLikeDT64_co, + _ArrayLikeObject_co, + _FloatLike_co, + _ComplexLike_co, +) + +from numpy.core.function_base import ( + add_newdoc as add_newdoc, +) + +from numpy.core.multiarray import ( + add_docstring as add_docstring, + bincount as bincount, +) + +from numpy.core.umath import _add_newdoc_ufunc + +_T = TypeVar("_T") +_T_co = TypeVar("_T_co", covariant=True) +_SCT = TypeVar("_SCT", bound=generic) +_ArrayType = TypeVar("_ArrayType", bound=NDArray[Any]) + +_2Tuple = Tuple[_T, _T] +_ArrayLike = _FiniteNestedSequence[_SupportsArray[dtype[_SCT]]] +_DTypeLike = Union[ + dtype[_SCT], + Type[_SCT], + _SupportsDType[dtype[_SCT]], +] + +class _TrimZerosSequence(Protocol[_T_co]): + def __len__(self) -> int: ... + def __getitem__(self, key: slice, /) -> _T_co: ... + def __iter__(self) -> Iterator[Any]: ... + +class _SupportsWriteFlush(Protocol): + def write(self, s: str, /) -> object: ... + def flush(self) -> object: ... + +__all__: List[str] + +# NOTE: This is in reality a re-export of `np.core.umath._add_newdoc_ufunc` +def add_newdoc_ufunc(ufunc: ufunc, new_docstring: str, /) -> None: ... + +@overload +def rot90( + m: _ArrayLike[_SCT], + k: int = ..., + axes: Tuple[int, int] = ..., +) -> NDArray[_SCT]: ... +@overload +def rot90( + m: ArrayLike, + k: int = ..., + axes: Tuple[int, int] = ..., +) -> NDArray[Any]: ... + +@overload +def flip(m: _SCT, axis: None = ...) -> _SCT: ... +@overload +def flip(m: _ScalarLike_co, axis: None = ...) -> Any: ... +@overload +def flip(m: _ArrayLike[_SCT], axis: None | _ShapeLike = ...) -> NDArray[_SCT]: ... +@overload +def flip(m: ArrayLike, axis: None | _ShapeLike = ...) -> NDArray[Any]: ... + +def iterable(y: object) -> TypeGuard[Iterable[Any]]: ... + +@overload +def average( + a: _ArrayLikeFloat_co, + axis: None = ..., + weights: None | _ArrayLikeFloat_co= ..., + returned: L[False] = ..., +) -> floating[Any]: ... +@overload +def average( + a: _ArrayLikeComplex_co, + axis: None = ..., + weights: None | _ArrayLikeComplex_co = ..., + returned: L[False] = ..., +) -> complexfloating[Any, Any]: ... +@overload +def average( + a: _ArrayLikeObject_co, + axis: None = ..., + weights: None | Any = ..., + returned: L[False] = ..., +) -> Any: ... +@overload +def average( + a: _ArrayLikeFloat_co, + axis: None = ..., + weights: None | _ArrayLikeFloat_co= ..., + returned: L[True] = ..., +) -> _2Tuple[floating[Any]]: ... +@overload +def average( + a: _ArrayLikeComplex_co, + axis: None = ..., + weights: None | _ArrayLikeComplex_co = ..., + returned: L[True] = ..., +) -> _2Tuple[complexfloating[Any, Any]]: ... +@overload +def average( + a: _ArrayLikeObject_co, + axis: None = ..., + weights: None | Any = ..., + returned: L[True] = ..., +) -> _2Tuple[Any]: ... +@overload +def average( + a: _ArrayLikeComplex_co | _ArrayLikeObject_co, + axis: None | _ShapeLike = ..., + weights: None | Any = ..., + returned: L[False] = ..., +) -> Any: ... +@overload +def average( + a: _ArrayLikeComplex_co | _ArrayLikeObject_co, + axis: None | _ShapeLike = ..., + weights: None | Any = ..., + returned: L[True] = ..., +) -> _2Tuple[Any]: ... + +@overload +def asarray_chkfinite( + a: _ArrayLike[_SCT], + dtype: None = ..., + order: _OrderKACF = ..., +) -> NDArray[_SCT]: ... +@overload +def asarray_chkfinite( + a: object, + dtype: None = ..., + order: _OrderKACF = ..., +) -> NDArray[Any]: ... +@overload +def asarray_chkfinite( + a: Any, + dtype: _DTypeLike[_SCT], + order: _OrderKACF = ..., +) -> NDArray[_SCT]: ... +@overload +def asarray_chkfinite( + a: Any, + dtype: DTypeLike, + order: _OrderKACF = ..., +) -> NDArray[Any]: ... + +@overload +def piecewise( + x: _ArrayLike[_SCT], + condlist: ArrayLike, + funclist: Sequence[Any | Callable[..., Any]], + *args: Any, + **kw: Any, +) -> NDArray[_SCT]: ... +@overload +def piecewise( + x: ArrayLike, + condlist: ArrayLike, + funclist: Sequence[Any | Callable[..., Any]], + *args: Any, + **kw: Any, +) -> NDArray[Any]: ... + +def select( + condlist: Sequence[ArrayLike], + choicelist: Sequence[ArrayLike], + default: ArrayLike = ..., +) -> NDArray[Any]: ... + +@overload +def copy( + a: _ArrayType, + order: _OrderKACF, + subok: L[True], +) -> _ArrayType: ... +@overload +def copy( + a: _ArrayType, + order: _OrderKACF = ..., + *, + subok: L[True], +) -> _ArrayType: ... +@overload +def copy( + a: _ArrayLike[_SCT], + order: _OrderKACF = ..., + subok: L[False] = ..., +) -> NDArray[_SCT]: ... +@overload +def copy( + a: ArrayLike, + order: _OrderKACF = ..., + subok: L[False] = ..., +) -> NDArray[Any]: ... + +def gradient( + f: ArrayLike, + *varargs: ArrayLike, + axis: None | _ShapeLike = ..., + edge_order: L[1, 2] = ..., +) -> Any: ... + +@overload +def diff( + a: _T, + n: L[0], + axis: SupportsIndex = ..., + prepend: ArrayLike = ..., + append: ArrayLike = ..., +) -> _T: ... +@overload +def diff( + a: ArrayLike, + n: int = ..., + axis: SupportsIndex = ..., + prepend: ArrayLike = ..., + append: ArrayLike = ..., +) -> NDArray[Any]: ... + +@overload +def interp( + x: _ArrayLikeFloat_co, + xp: _ArrayLikeFloat_co, + fp: _ArrayLikeFloat_co, + left: None | _FloatLike_co = ..., + right: None | _FloatLike_co = ..., + period: None | _FloatLike_co = ..., +) -> NDArray[float64]: ... +@overload +def interp( + x: _ArrayLikeFloat_co, + xp: _ArrayLikeFloat_co, + fp: _ArrayLikeComplex_co, + left: None | _ComplexLike_co = ..., + right: None | _ComplexLike_co = ..., + period: None | _FloatLike_co = ..., +) -> NDArray[complex128]: ... + +@overload +def angle(z: _ArrayLikeFloat_co, deg: bool = ...) -> floating[Any]: ... +@overload +def angle(z: _ArrayLikeComplex_co, deg: bool = ...) -> complexfloating[Any, Any]: ... +@overload +def angle(z: _ArrayLikeObject_co, deg: bool = ...) -> Any: ... + +@overload +def unwrap( + p: _ArrayLikeFloat_co, + discont: None | float = ..., + axis: int = ..., + *, + period: float = ..., +) -> NDArray[floating[Any]]: ... +@overload +def unwrap( + p: _ArrayLikeObject_co, + discont: None | float = ..., + axis: int = ..., + *, + period: float = ..., +) -> NDArray[object_]: ... + +def sort_complex(a: ArrayLike) -> NDArray[complexfloating[Any, Any]]: ... + +def trim_zeros( + filt: _TrimZerosSequence[_T], + trim: L["f", "b", "fb", "bf"] = ..., +) -> _T: ... + +@overload +def extract(condition: ArrayLike, arr: _ArrayLike[_SCT]) -> NDArray[_SCT]: ... +@overload +def extract(condition: ArrayLike, arr: ArrayLike) -> NDArray[Any]: ... + +def place(arr: NDArray[Any], mask: ArrayLike, vals: Any) -> None: ... + +def disp( + mesg: object, + device: None | _SupportsWriteFlush = ..., + linefeed: bool = ..., +) -> None: ... + +@overload +def cov( + m: _ArrayLikeFloat_co, + y: None | _ArrayLikeFloat_co = ..., + rowvar: bool = ..., + bias: bool = ..., + ddof: None | SupportsIndex | SupportsInt = ..., + fweights: None | ArrayLike = ..., + aweights: None | ArrayLike = ..., + *, + dtype: None = ..., +) -> NDArray[floating[Any]]: ... +@overload +def cov( + m: _ArrayLikeComplex_co, + y: None | _ArrayLikeComplex_co = ..., + rowvar: bool = ..., + bias: bool = ..., + ddof: None | SupportsIndex | SupportsInt = ..., + fweights: None | ArrayLike = ..., + aweights: None | ArrayLike = ..., + *, + dtype: None = ..., +) -> NDArray[complexfloating[Any, Any]]: ... +@overload +def cov( + m: _ArrayLikeComplex_co, + y: None | _ArrayLikeComplex_co = ..., + rowvar: bool = ..., + bias: bool = ..., + ddof: None | SupportsIndex | SupportsInt = ..., + fweights: None | ArrayLike = ..., + aweights: None | ArrayLike = ..., + *, + dtype: _DTypeLike[_SCT], +) -> NDArray[_SCT]: ... +@overload +def cov( + m: _ArrayLikeComplex_co, + y: None | _ArrayLikeComplex_co = ..., + rowvar: bool = ..., + bias: bool = ..., + ddof: None | SupportsIndex | SupportsInt = ..., + fweights: None | ArrayLike = ..., + aweights: None | ArrayLike = ..., + *, + dtype: DTypeLike, +) -> NDArray[Any]: ... + +# NOTE `bias` and `ddof` have been deprecated +@overload +def corrcoef( + m: _ArrayLikeFloat_co, + y: None | _ArrayLikeFloat_co = ..., + rowvar: bool = ..., + *, + dtype: None = ..., +) -> NDArray[floating[Any]]: ... +@overload +def corrcoef( + m: _ArrayLikeComplex_co, + y: None | _ArrayLikeComplex_co = ..., + rowvar: bool = ..., + *, + dtype: None = ..., +) -> NDArray[complexfloating[Any, Any]]: ... +@overload +def corrcoef( + m: _ArrayLikeComplex_co, + y: None | _ArrayLikeComplex_co = ..., + rowvar: bool = ..., + *, + dtype: _DTypeLike[_SCT], +) -> NDArray[_SCT]: ... +@overload +def corrcoef( + m: _ArrayLikeComplex_co, + y: None | _ArrayLikeComplex_co = ..., + rowvar: bool = ..., + *, + dtype: DTypeLike, +) -> NDArray[Any]: ... + +def blackman(M: _FloatLike_co) -> NDArray[floating[Any]]: ... + +def bartlett(M: _FloatLike_co) -> NDArray[floating[Any]]: ... + +def hanning(M: _FloatLike_co) -> NDArray[floating[Any]]: ... + +def hamming(M: _FloatLike_co) -> NDArray[floating[Any]]: ... + +def i0(x: _ArrayLikeFloat_co) -> NDArray[floating[Any]]: ... + +def kaiser( + M: _FloatLike_co, + beta: _FloatLike_co, +) -> NDArray[floating[Any]]: ... + +@overload +def sinc(x: _FloatLike_co) -> floating[Any]: ... +@overload +def sinc(x: _ComplexLike_co) -> complexfloating[Any, Any]: ... +@overload +def sinc(x: _ArrayLikeFloat_co) -> NDArray[floating[Any]]: ... +@overload +def sinc(x: _ArrayLikeComplex_co) -> NDArray[complexfloating[Any, Any]]: ... + +@overload +def msort(a: _ArrayType) -> _ArrayType: ... +@overload +def msort(a: _ArrayLike[_SCT]) -> NDArray[_SCT]: ... +@overload +def msort(a: ArrayLike) -> NDArray[Any]: ... + +@overload +def median( + a: _ArrayLikeFloat_co, + axis: None = ..., + out: None = ..., + overwrite_input: bool = ..., + keepdims: L[False] = ..., +) -> floating[Any]: ... +@overload +def median( + a: _ArrayLikeComplex_co, + axis: None = ..., + out: None = ..., + overwrite_input: bool = ..., + keepdims: L[False] = ..., +) -> complexfloating[Any, Any]: ... +@overload +def median( + a: _ArrayLikeTD64_co, + axis: None = ..., + out: None = ..., + overwrite_input: bool = ..., + keepdims: L[False] = ..., +) -> timedelta64: ... +@overload +def median( + a: _ArrayLikeObject_co, + axis: None = ..., + out: None = ..., + overwrite_input: bool = ..., + keepdims: L[False] = ..., +) -> Any: ... +@overload +def median( + a: _ArrayLikeFloat_co | _ArrayLikeComplex_co | _ArrayLikeTD64_co | _ArrayLikeObject_co, + axis: None | _ShapeLike = ..., + out: None = ..., + overwrite_input: bool = ..., + keepdims: bool = ..., +) -> Any: ... +@overload +def median( + a: _ArrayLikeFloat_co | _ArrayLikeComplex_co | _ArrayLikeTD64_co | _ArrayLikeObject_co, + axis: None | _ShapeLike = ..., + out: _ArrayType = ..., + overwrite_input: bool = ..., + keepdims: bool = ..., +) -> _ArrayType: ... + +_MethodKind = L[ + "inverted_cdf", + "averaged_inverted_cdf", + "closest_observation", + "interpolated_inverted_cdf", + "hazen", + "weibull", + "linear", + "median_unbiased", + "normal_unbiased", + "lower", + "higher", + "midpoint", + "nearest", +] + +@overload +def percentile( + a: _ArrayLikeFloat_co, + q: _FloatLike_co, + axis: None = ..., + out: None = ..., + overwrite_input: bool = ..., + method: _MethodKind = ..., + keepdims: L[False] = ..., +) -> floating[Any]: ... +@overload +def percentile( + a: _ArrayLikeComplex_co, + q: _FloatLike_co, + axis: None = ..., + out: None = ..., + overwrite_input: bool = ..., + method: _MethodKind = ..., + keepdims: L[False] = ..., +) -> complexfloating[Any, Any]: ... +@overload +def percentile( + a: _ArrayLikeTD64_co, + q: _FloatLike_co, + axis: None = ..., + out: None = ..., + overwrite_input: bool = ..., + method: _MethodKind = ..., + keepdims: L[False] = ..., +) -> timedelta64: ... +@overload +def percentile( + a: _ArrayLikeDT64_co, + q: _FloatLike_co, + axis: None = ..., + out: None = ..., + overwrite_input: bool = ..., + method: _MethodKind = ..., + keepdims: L[False] = ..., +) -> datetime64: ... +@overload +def percentile( + a: _ArrayLikeObject_co, + q: _FloatLike_co, + axis: None = ..., + out: None = ..., + overwrite_input: bool = ..., + method: _MethodKind = ..., + keepdims: L[False] = ..., +) -> Any: ... +@overload +def percentile( + a: _ArrayLikeFloat_co, + q: _ArrayLikeFloat_co, + axis: None = ..., + out: None = ..., + overwrite_input: bool = ..., + method: _MethodKind = ..., + keepdims: L[False] = ..., +) -> NDArray[floating[Any]]: ... +@overload +def percentile( + a: _ArrayLikeComplex_co, + q: _ArrayLikeFloat_co, + axis: None = ..., + out: None = ..., + overwrite_input: bool = ..., + method: _MethodKind = ..., + keepdims: L[False] = ..., +) -> NDArray[complexfloating[Any, Any]]: ... +@overload +def percentile( + a: _ArrayLikeTD64_co, + q: _ArrayLikeFloat_co, + axis: None = ..., + out: None = ..., + overwrite_input: bool = ..., + method: _MethodKind = ..., + keepdims: L[False] = ..., +) -> NDArray[timedelta64]: ... +@overload +def percentile( + a: _ArrayLikeDT64_co, + q: _ArrayLikeFloat_co, + axis: None = ..., + out: None = ..., + overwrite_input: bool = ..., + method: _MethodKind = ..., + keepdims: L[False] = ..., +) -> NDArray[datetime64]: ... +@overload +def percentile( + a: _ArrayLikeObject_co, + q: _ArrayLikeFloat_co, + axis: None = ..., + out: None = ..., + overwrite_input: bool = ..., + method: _MethodKind = ..., + keepdims: L[False] = ..., +) -> NDArray[object_]: ... +@overload +def percentile( + a: _ArrayLikeComplex_co | _ArrayLikeTD64_co | _ArrayLikeTD64_co | _ArrayLikeObject_co, + q: _ArrayLikeFloat_co, + axis: None | _ShapeLike = ..., + out: None = ..., + overwrite_input: bool = ..., + method: _MethodKind = ..., + keepdims: bool = ..., +) -> Any: ... +@overload +def percentile( + a: _ArrayLikeComplex_co | _ArrayLikeTD64_co | _ArrayLikeTD64_co | _ArrayLikeObject_co, + q: _ArrayLikeFloat_co, + axis: None | _ShapeLike = ..., + out: _ArrayType = ..., + overwrite_input: bool = ..., + method: _MethodKind = ..., + keepdims: bool = ..., +) -> _ArrayType: ... + +# NOTE: Not an alias, but they do have identical signatures +# (that we can reuse) +quantile = percentile + +# TODO: Returns a scalar for <= 1D array-likes; returns an ndarray otherwise +def trapz( + y: _ArrayLikeComplex_co | _ArrayLikeTD64_co | _ArrayLikeObject_co, + x: None | _ArrayLikeComplex_co | _ArrayLikeTD64_co | _ArrayLikeObject_co = ..., + dx: float = ..., + axis: SupportsIndex = ..., +) -> Any: ... + +def meshgrid( + *xi: ArrayLike, + copy: bool = ..., + sparse: bool = ..., + indexing: L["xy", "ij"] = ..., +) -> List[NDArray[Any]]: ... + +@overload +def delete( + arr: _ArrayLike[_SCT], + obj: slice | _ArrayLikeInt_co, + axis: None | SupportsIndex = ..., +) -> NDArray[_SCT]: ... +@overload +def delete( + arr: ArrayLike, + obj: slice | _ArrayLikeInt_co, + axis: None | SupportsIndex = ..., +) -> NDArray[Any]: ... + +@overload +def insert( + arr: _ArrayLike[_SCT], + obj: slice | _ArrayLikeInt_co, + values: ArrayLike, + axis: None | SupportsIndex = ..., +) -> NDArray[_SCT]: ... +@overload +def insert( + arr: ArrayLike, + obj: slice | _ArrayLikeInt_co, + values: ArrayLike, + axis: None | SupportsIndex = ..., +) -> NDArray[Any]: ... + +def append( + arr: ArrayLike, + values: ArrayLike, + axis: None | SupportsIndex = ..., +) -> NDArray[Any]: ... + +@overload +def digitize( + x: _FloatLike_co, + bins: _ArrayLikeFloat_co, + right: bool = ..., +) -> intp: ... +@overload +def digitize( + x: _ArrayLikeFloat_co, + bins: _ArrayLikeFloat_co, + right: bool = ..., +) -> NDArray[intp]: ... diff --git a/numpy/lib/histograms.py b/numpy/lib/histograms.py index 1a47acf78ff3..b6909bc1d9e0 100644 --- a/numpy/lib/histograms.py +++ b/numpy/lib/histograms.py @@ -1,21 +1,35 @@ """ Histogram-related functions """ -from __future__ import division, absolute_import, print_function - +import contextlib +import functools import operator +import warnings import numpy as np -from numpy.compat.py3k import basestring +from numpy.core import overrides __all__ = ['histogram', 'histogramdd', 'histogram_bin_edges'] +array_function_dispatch = functools.partial( + overrides.array_function_dispatch, module='numpy') + # range is a keyword argument to many functions, so save the builtin so they can # use it. _range = range -def _hist_bin_sqrt(x): +def _ptp(x): + """Peak-to-peak value of x. + + This implementation avoids the problem of signed integer arrays having a + peak-to-peak value that cannot be represented with the array's data type. + This function returns an unsigned value for signed integer arrays. + """ + return _unsigned_subtract(x.max(), x.min()) + + +def _hist_bin_sqrt(x, range): """ Square root histogram bin estimator. @@ -32,10 +46,11 @@ def _hist_bin_sqrt(x): ------- h : An estimate of the optimal bin width for the given data. """ - return x.ptp() / np.sqrt(x.size) + del range # unused + return _ptp(x) / np.sqrt(x.size) -def _hist_bin_sturges(x): +def _hist_bin_sturges(x, range): """ Sturges histogram bin estimator. @@ -54,10 +69,11 @@ def _hist_bin_sturges(x): ------- h : An estimate of the optimal bin width for the given data. """ - return x.ptp() / (np.log2(x.size) + 1.0) + del range # unused + return _ptp(x) / (np.log2(x.size) + 1.0) -def _hist_bin_rice(x): +def _hist_bin_rice(x, range): """ Rice histogram bin estimator. @@ -77,10 +93,11 @@ def _hist_bin_rice(x): ------- h : An estimate of the optimal bin width for the given data. """ - return x.ptp() / (2.0 * x.size ** (1.0 / 3)) + del range # unused + return _ptp(x) / (2.0 * x.size ** (1.0 / 3)) -def _hist_bin_scott(x): +def _hist_bin_scott(x, range): """ Scott histogram bin estimator. @@ -98,10 +115,53 @@ def _hist_bin_scott(x): ------- h : An estimate of the optimal bin width for the given data. """ + del range # unused return (24.0 * np.pi**0.5 / x.size)**(1.0 / 3.0) * np.std(x) -def _hist_bin_doane(x): +def _hist_bin_stone(x, range): + """ + Histogram bin estimator based on minimizing the estimated integrated squared error (ISE). + + The number of bins is chosen by minimizing the estimated ISE against the unknown true distribution. + The ISE is estimated using cross-validation and can be regarded as a generalization of Scott's rule. + https://en.wikipedia.org/wiki/Histogram#Scott.27s_normal_reference_rule + + This paper by Stone appears to be the origination of this rule. + http://digitalassets.lib.berkeley.edu/sdtr/ucb/text/34.pdf + + Parameters + ---------- + x : array_like + Input data that is to be histogrammed, trimmed to range. May not + be empty. + range : (float, float) + The lower and upper range of the bins. + + Returns + ------- + h : An estimate of the optimal bin width for the given data. + """ + + n = x.size + ptp_x = _ptp(x) + if n <= 1 or ptp_x == 0: + return 0 + + def jhat(nbins): + hh = ptp_x / nbins + p_k = np.histogram(x, bins=nbins, range=range)[0] / n + return (2 - (n + 1) * p_k.dot(p_k)) / hh + + nbins_upper_bound = max(100, int(np.sqrt(n))) + nbins = min(_range(1, nbins_upper_bound + 1), key=jhat) + if nbins == nbins_upper_bound: + warnings.warn("The number of bins estimated may be suboptimal.", + RuntimeWarning, stacklevel=3) + return ptp_x / nbins + + +def _hist_bin_doane(x, range): """ Doane's histogram bin estimator. @@ -119,6 +179,7 @@ def _hist_bin_doane(x): ------- h : An estimate of the optimal bin width for the given data. """ + del range # unused if x.size > 2: sg1 = np.sqrt(6.0 * (x.size - 2) / ((x.size + 1.0) * (x.size + 3))) sigma = np.std(x) @@ -130,12 +191,12 @@ def _hist_bin_doane(x): np.true_divide(temp, sigma, temp) np.power(temp, 3, temp) g1 = np.mean(temp) - return x.ptp() / (1.0 + np.log2(x.size) + + return _ptp(x) / (1.0 + np.log2(x.size) + np.log2(1.0 + np.absolute(g1) / sg1)) return 0.0 -def _hist_bin_fd(x): +def _hist_bin_fd(x, range): """ The Freedman-Diaconis histogram bin estimator. @@ -146,7 +207,7 @@ def _hist_bin_fd(x): than the standard deviation, so it is less accurate, especially for long tailed distributions. - If the IQR is 0, this function returns 1 for the number of bins. + If the IQR is 0, this function returns 0 for the bin width. Binwidth is inversely proportional to the cube root of data size (asymptotically optimal). @@ -160,28 +221,29 @@ def _hist_bin_fd(x): ------- h : An estimate of the optimal bin width for the given data. """ + del range # unused iqr = np.subtract(*np.percentile(x, [75, 25])) return 2.0 * iqr * x.size ** (-1.0 / 3.0) -def _hist_bin_auto(x): +def _hist_bin_auto(x, range): """ Histogram bin estimator that uses the minimum width of the - Freedman-Diaconis and Sturges estimators if the FD bandwidth is non zero - and the Sturges estimator if the FD bandwidth is 0. + Freedman-Diaconis and Sturges estimators if the FD bin width is non-zero. + If the bin width from the FD estimator is 0, the Sturges estimator is used. The FD estimator is usually the most robust method, but its width estimate tends to be too large for small `x` and bad for data with limited variance. The Sturges estimator is quite good for small (<1000) datasets - and is the default in the R language. This method gives good off the shelf + and is the default in the R language. This method gives good off-the-shelf behaviour. .. versionchanged:: 1.15.0 If there is limited variance the IQR can be 0, which results in the FD bin width being 0 too. This is not a valid bin width, so ``np.histogram_bin_edges`` chooses 1 bin instead, which may not be optimal. - If the IQR is 0, it's unlikely any variance based estimators will be of - use, so we revert to the sturges estimator, which only uses the size of the + If the IQR is 0, it's unlikely any variance-based estimators will be of + use, so we revert to the Sturges estimator, which only uses the size of the dataset in its calculation. Parameters @@ -198,8 +260,9 @@ def _hist_bin_auto(x): -------- _hist_bin_fd, _hist_bin_sturges """ - fd_bw = _hist_bin_fd(x) - sturges_bw = _hist_bin_sturges(x) + fd_bw = _hist_bin_fd(x, range) + sturges_bw = _hist_bin_sturges(x, range) + del range # unused if fd_bw: return min(fd_bw, sturges_bw) else: @@ -207,7 +270,8 @@ def _hist_bin_auto(x): return sturges_bw # Private dict initialized at module load time -_hist_bin_selectors = {'auto': _hist_bin_auto, +_hist_bin_selectors = {'stone': _hist_bin_stone, + 'auto': _hist_bin_auto, 'doane': _hist_bin_doane, 'fd': _hist_bin_fd, 'rice': _hist_bin_rice, @@ -219,6 +283,14 @@ def _hist_bin_auto(x): def _ravel_and_check_weights(a, weights): """ Check a and weights have matching shapes, and ravel both """ a = np.asarray(a) + + # Ensure that the array is a "subtractable" dtype + if a.dtype == np.bool_: + warnings.warn("Converting input from {} to {} for compatibility." + .format(a.dtype, np.uint8), + RuntimeWarning, stacklevel=3) + a = a.astype(np.uint8) + if weights is not None: weights = np.asarray(weights) if weights.shape != a.shape: @@ -310,7 +382,7 @@ def _get_bin_edges(a, bins, range, weights): n_equal_bins = None bin_edges = None - if isinstance(bins, basestring): + if isinstance(bins, str): bin_name = bins # if `bins` is a string for an automatic method, # this will replace it with the number of bins calculated @@ -334,7 +406,7 @@ def _get_bin_edges(a, bins, range, weights): n_equal_bins = 1 else: # Do not call selectors on empty arrays - width = _hist_bin_selectors[bin_name](a) + width = _hist_bin_selectors[bin_name](a, (first_edge, last_edge)) if width: n_equal_bins = int(np.ceil(_unsigned_subtract(last_edge, first_edge) / width)) else: @@ -345,9 +417,9 @@ def _get_bin_edges(a, bins, range, weights): elif np.ndim(bins) == 0: try: n_equal_bins = operator.index(bins) - except TypeError: + except TypeError as e: raise TypeError( - '`bins` must be an integer, a string, or an array') + '`bins` must be an integer, a string, or an array') from e if n_equal_bins < 1: raise ValueError('`bins` must be positive, when an integer') @@ -391,9 +463,15 @@ def _search_sorted_inclusive(a, v): )) +def _histogram_bin_edges_dispatcher(a, bins=None, range=None, weights=None): + return (a, bins, weights) + + +@array_function_dispatch(_histogram_bin_edges_dispatcher) def histogram_bin_edges(a, bins=10, range=None, weights=None): r""" - Function to calculate only the edges of the bins used by the `histogram` function. + Function to calculate only the edges of the bins used by the `histogram` + function. Parameters ---------- @@ -431,6 +509,11 @@ def histogram_bin_edges(a, bins=10, range=None, weights=None): Less robust estimator that that takes into account data variability and data size. + 'stone' + Estimator based on leave-one-out cross-validation estimate of + the integrated squared error. Can be regarded as a generalization + of Scott's rule. + 'rice' Estimator does not take variability into account, only data size. Commonly overestimates number of bins required. @@ -479,16 +562,17 @@ def histogram_bin_edges(a, bins=10, range=None, weights=None): below, :math:`h` is the binwidth and :math:`n_h` is the number of bins. All estimators that compute bin counts are recast to bin width using the `ptp` of the data. The final bin count is obtained from - ``np.round(np.ceil(range / h))``. + ``np.round(np.ceil(range / h))``. The final bin width is often less + than what is returned by the estimators below. - 'Auto' (maximum of the 'Sturges' and 'FD' estimators) + 'auto' (maximum of the 'sturges' and 'fd' estimators) A compromise to get a good value. For small datasets the Sturges value will usually be chosen, while larger datasets will usually default to FD. Avoids the overly conservative behaviour of FD and Sturges for small and large datasets respectively. Switchover point is usually :math:`a.size \approx 1000`. - 'FD' (Freedman Diaconis Estimator) + 'fd' (Freedman Diaconis Estimator) .. math:: h = 2 \frac{IQR}{n^{1/3}} The binwidth is proportional to the interquartile range (IQR) @@ -496,7 +580,7 @@ def histogram_bin_edges(a, bins=10, range=None, weights=None): conservative for small datasets, but is quite good for large datasets. The IQR is very robust to outliers. - 'Scott' + 'scott' .. math:: h = \sigma \sqrt[3]{\frac{24 * \sqrt{\pi}}{n}} The binwidth is proportional to the standard deviation of the @@ -506,14 +590,14 @@ def histogram_bin_edges(a, bins=10, range=None, weights=None): outliers. Values are very similar to the Freedman-Diaconis estimator in the absence of outliers. - 'Rice' + 'rice' .. math:: n_h = 2n^{1/3} The number of bins is only proportional to cube root of ``a.size``. It tends to overestimate the number of bins and it does not take into account data variability. - 'Sturges' + 'sturges' .. math:: n_h = \log _{2}n+1 The number of bins is the base 2 log of ``a.size``. This @@ -521,7 +605,7 @@ def histogram_bin_edges(a, bins=10, range=None, weights=None): larger, non-normal datasets. This is the default method in R's ``hist`` method. - 'Doane' + 'doane' .. math:: n_h = 1 + \log_{2}(n) + \log_{2}(1 + \frac{|g_1|}{\sigma_{g_1}}) @@ -533,8 +617,9 @@ def histogram_bin_edges(a, bins=10, range=None, weights=None): estimates for non-normal datasets. This estimator attempts to account for the skew of the data. - 'Sqrt' + 'sqrt' .. math:: n_h = \sqrt n + The simplest and fastest estimator. Only takes into account the data size. @@ -572,7 +657,7 @@ def histogram_bin_edges(a, bins=10, range=None, weights=None): >>> hist_0, bins_0 = np.histogram(arr[group_id == 0], bins='auto') >>> hist_1, bins_1 = np.histogram(arr[group_id == 1], bins='auto') - >>> hist_0; hist1 + >>> hist_0; hist_1 array([1, 1, 1]) array([2, 1, 1, 2]) >>> bins_0; bins_1 @@ -585,10 +670,16 @@ def histogram_bin_edges(a, bins=10, range=None, weights=None): return bin_edges -def histogram(a, bins=10, range=None, normed=False, weights=None, +def _histogram_dispatcher( + a, bins=None, range=None, normed=None, weights=None, density=None): + return (a, bins, weights) + + +@array_function_dispatch(_histogram_dispatcher) +def histogram(a, bins=10, range=None, normed=None, weights=None, density=None): r""" - Compute the histogram of a set of data. + Compute the histogram of a dataset. Parameters ---------- @@ -597,8 +688,8 @@ def histogram(a, bins=10, range=None, normed=False, weights=None, bins : int or sequence of scalars or str, optional If `bins` is an int, it defines the number of equal-width bins in the given range (10, by default). If `bins` is a - sequence, it defines the bin edges, including the rightmost - edge, allowing for non-uniform bin widths. + sequence, it defines a monotonically increasing array of bin edges, + including the rightmost edge, allowing for non-uniform bin widths. .. versionadded:: 1.11.0 @@ -617,14 +708,12 @@ def histogram(a, bins=10, range=None, normed=False, weights=None, .. deprecated:: 1.6.0 - This keyword is deprecated in NumPy 1.6.0 due to confusing/buggy - behavior. It will be removed in NumPy 2.0.0. Use the ``density`` - keyword instead. If ``False``, the result will contain the - number of samples in each bin. If ``True``, the result is the - value of the probability *density* function at the bin, - normalized such that the *integral* over the range is 1. Note - that this latter behavior is known to be buggy with unequal bin - widths; use ``density`` instead. + This is equivalent to the `density` argument, but produces incorrect + results for unequal bin widths. It should not be used. + + .. versionchanged:: 1.15.0 + DeprecationWarnings are actually emitted. + weights : array_like, optional An array of weights, of the same shape as `a`. Each value in `a` only contributes its associated weight towards the bin count @@ -671,14 +760,14 @@ def histogram(a, bins=10, range=None, normed=False, weights=None, >>> np.histogram([1, 2, 1], bins=[0, 1, 2, 3]) (array([0, 2, 1]), array([0, 1, 2, 3])) >>> np.histogram(np.arange(4), bins=np.arange(5), density=True) - (array([ 0.25, 0.25, 0.25, 0.25]), array([0, 1, 2, 3, 4])) + (array([0.25, 0.25, 0.25, 0.25]), array([0, 1, 2, 3, 4])) >>> np.histogram([[1, 2, 1], [1, 0, 1]], bins=[0,1,2,3]) (array([1, 4, 1]), array([0, 1, 2, 3])) >>> a = np.arange(5) >>> hist, bin_edges = np.histogram(a, density=True) >>> hist - array([ 0.5, 0. , 0.5, 0. , 0. , 0.5, 0. , 0.5, 0. , 0.5]) + array([0.5, 0. , 0.5, 0. , 0. , 0.5, 0. , 0.5, 0. , 0.5]) >>> hist.sum() 2.4999999999999996 >>> np.sum(hist * np.diff(bin_edges)) @@ -693,8 +782,9 @@ def histogram(a, bins=10, range=None, normed=False, weights=None, >>> rng = np.random.RandomState(10) # deterministic random data >>> a = np.hstack((rng.normal(size=1000), ... rng.normal(loc=5, scale=2, size=1000))) - >>> plt.hist(a, bins='auto') # arguments are passed to np.histogram + >>> _ = plt.hist(a, bins='auto') # arguments are passed to np.histogram >>> plt.title("Histogram with 'auto' bins") + Text(0.5, 1.0, "Histogram with 'auto' bins") >>> plt.show() """ @@ -803,20 +893,56 @@ def histogram(a, bins=10, range=None, normed=False, weights=None, # density overrides the normed keyword if density is not None: - normed = False + if normed is not None: + # 2018-06-13, numpy 1.15.0 (this was not noisily deprecated in 1.6) + warnings.warn( + "The normed argument is ignored when density is provided. " + "In future passing both will result in an error.", + DeprecationWarning, stacklevel=3) + normed = None if density: db = np.array(np.diff(bin_edges), float) return n/db/n.sum(), bin_edges elif normed: - # deprecated, buggy behavior. Remove for NumPy 2.0.0 + # 2018-06-13, numpy 1.15.0 (this was not noisily deprecated in 1.6) + warnings.warn( + "Passing `normed=True` on non-uniform bins has always been " + "broken, and computes neither the probability density " + "function nor the probability mass function. " + "The result is only correct if the bins are uniform, when " + "density=True will produce the same result anyway. " + "The argument will be removed in a future version of " + "numpy.", + np.VisibleDeprecationWarning, stacklevel=3) + + # this normalization is incorrect, but db = np.array(np.diff(bin_edges), float) return n/(n*db).sum(), bin_edges else: + if normed is not None: + # 2018-06-13, numpy 1.15.0 (this was not noisily deprecated in 1.6) + warnings.warn( + "Passing normed=False is deprecated, and has no effect. " + "Consider passing the density argument instead.", + DeprecationWarning, stacklevel=3) return n, bin_edges -def histogramdd(sample, bins=10, range=None, normed=False, weights=None): +def _histogramdd_dispatcher(sample, bins=None, range=None, normed=None, + weights=None, density=None): + if hasattr(sample, 'shape'): # same condition as used in histogramdd + yield sample + else: + yield from sample + with contextlib.suppress(TypeError): + yield from bins + yield weights + + +@array_function_dispatch(_histogramdd_dispatcher) +def histogramdd(sample, bins=10, range=None, normed=None, weights=None, + density=None): """ Compute the multidimensional histogram of some data. @@ -828,16 +954,17 @@ def histogramdd(sample, bins=10, range=None, normed=False, weights=None): Note the unusual interpretation of sample when an array_like: * When an array, each row is a coordinate in a D-dimensional space - - such as ``histogramgramdd(np.array([p1, p2, p3]))``. + such as ``histogramdd(np.array([p1, p2, p3]))``. * When an array_like, each element is the list of values for single - coordinate - such as ``histogramgramdd((X, Y, Z))``. + coordinate - such as ``histogramdd((X, Y, Z))``. The first form should be preferred. bins : sequence or int, optional The bin specification: - * A sequence of arrays describing the bin edges along each dimension. + * A sequence of arrays describing the monotonically increasing bin + edges along each dimension. * The number of bins for each dimension (nx, ny, ... =bins) * The number of bins for all dimensions (nx=ny=...=bins). @@ -848,9 +975,14 @@ def histogramdd(sample, bins=10, range=None, normed=False, weights=None): An entry of None in the sequence results in the minimum and maximum values being used for the corresponding dimension. The default, None, is equivalent to passing a tuple of D None values. + density : bool, optional + If False, the default, returns the number of samples in each bin. + If True, returns the probability *density* function at the bin, + ``bin_count / sample_count / bin_volume``. normed : bool, optional - If False, returns the number of samples in each bin. If True, - returns the bin density ``bin_count / sample_count / bin_volume``. + An alias for the density argument that behaves identically. To avoid + confusion with the broken normed argument to `histogram`, `density` + should be preferred. weights : (N,) array_like, optional An array of values `w_i` weighing each sample `(x_i, y_i, z_i, ...)`. Weights are normalized to 1 if normed is True. If normed is False, @@ -916,7 +1048,15 @@ def histogramdd(sample, bins=10, range=None, normed=False, weights=None): raise ValueError( '`bins[{}]` must be positive, when an integer'.format(i)) smin, smax = _get_outer_edges(sample[:,i], range[i]) - edges[i] = np.linspace(smin, smax, bins[i] + 1) + try: + n = operator.index(bins[i]) + + except TypeError as e: + raise TypeError( + "`bins[{}]` must be an integer, when a scalar".format(i) + ) from e + + edges[i] = np.linspace(smin, smax, n + 1) elif np.ndim(bins[i]) == 1: edges[i] = np.asarray(bins[i]) if np.any(edges[i][:-1] > edges[i][1:]): @@ -964,8 +1104,18 @@ def histogramdd(sample, bins=10, range=None, normed=False, weights=None): core = D*(slice(1, -1),) hist = hist[core] - # Normalize if normed is True - if normed: + # handle the aliasing normed argument + if normed is None: + if density is None: + density = False + elif density is None: + # an explicit normed argument was passed, alias it to the new name + density = normed + else: + raise TypeError("Cannot specify both 'normed' and 'density'") + + if density: + # calculate the probability density function s = hist.sum() for i in _range(D): shape = np.ones(D, int) diff --git a/numpy/lib/histograms.pyi b/numpy/lib/histograms.pyi new file mode 100644 index 000000000000..2ceb60793c7e --- /dev/null +++ b/numpy/lib/histograms.pyi @@ -0,0 +1,51 @@ +from typing import ( + Literal as L, + List, + Tuple, + Any, + SupportsIndex, + Sequence, +) + +from numpy.typing import ( + NDArray, + ArrayLike, +) + +_BinKind = L[ + "stone", + "auto", + "doane", + "fd", + "rice", + "scott", + "sqrt", + "sturges", +] + +__all__: List[str] + +def histogram_bin_edges( + a: ArrayLike, + bins: _BinKind | SupportsIndex | ArrayLike = ..., + range: None | Tuple[float, float] = ..., + weights: None | ArrayLike = ..., +) -> NDArray[Any]: ... + +def histogram( + a: ArrayLike, + bins: _BinKind | SupportsIndex | ArrayLike = ..., + range: None | Tuple[float, float] = ..., + normed: None = ..., + weights: None | ArrayLike = ..., + density: bool = ..., +) -> Tuple[NDArray[Any], NDArray[Any]]: ... + +def histogramdd( + sample: ArrayLike, + bins: SupportsIndex | ArrayLike = ..., + range: Sequence[Tuple[float, float]] = ..., + normed: None | bool = ..., + weights: None | ArrayLike = ..., + density: None | bool = ..., +) -> Tuple[NDArray[Any], List[NDArray[Any]]]: ... diff --git a/numpy/lib/index_tricks.py b/numpy/lib/index_tricks.py index d2139338e8f8..2a4402c89e48 100644 --- a/numpy/lib/index_tricks.py +++ b/numpy/lib/index_tricks.py @@ -1,28 +1,38 @@ -from __future__ import division, absolute_import, print_function - +import functools import sys import math +import warnings import numpy.core.numeric as _nx from numpy.core.numeric import ( asarray, ScalarType, array, alltrue, cumprod, arange, ndim - ) +) from numpy.core.numerictypes import find_common_type, issubdtype -from . import function_base import numpy.matrixlib as matrixlib from .function_base import diff from numpy.core.multiarray import ravel_multi_index, unravel_index +from numpy.core.overrides import set_module +from numpy.core import overrides, linspace from numpy.lib.stride_tricks import as_strided +array_function_dispatch = functools.partial( + overrides.array_function_dispatch, module='numpy') + + __all__ = [ 'ravel_multi_index', 'unravel_index', 'mgrid', 'ogrid', 'r_', 'c_', 's_', 'index_exp', 'ix_', 'ndenumerate', 'ndindex', 'fill_diagonal', 'diag_indices', 'diag_indices_from' - ] +] + +def _ix__dispatcher(*args): + return args + +@array_function_dispatch(_ix__dispatcher) def ix_(*args): """ Construct an open mesh from multiple sequences. @@ -83,19 +93,21 @@ def ix_(*args): out = [] nd = len(args) for k, new in enumerate(args): - new = asarray(new) + if not isinstance(new, _nx.ndarray): + new = asarray(new) + if new.size == 0: + # Explicitly type empty arrays to avoid float default + new = new.astype(_nx.intp) if new.ndim != 1: raise ValueError("Cross index must be 1 dimensional") - if new.size == 0: - # Explicitly type empty arrays to avoid float default - new = new.astype(_nx.intp) if issubdtype(new.dtype, _nx.bool_): new, = new.nonzero() new = new.reshape((1,)*k + (new.size,) + (1,)*(nd-k-1)) out.append(new) return tuple(out) -class nd_grid(object): + +class nd_grid: """ Construct a multi-dimensional "meshgrid". @@ -121,39 +133,13 @@ class nd_grid(object): Notes ----- Two instances of `nd_grid` are made available in the NumPy namespace, - `mgrid` and `ogrid`:: + `mgrid` and `ogrid`, approximately defined as:: mgrid = nd_grid(sparse=False) ogrid = nd_grid(sparse=True) Users should use these pre-defined instances instead of using `nd_grid` directly. - - Examples - -------- - >>> mgrid = np.lib.index_tricks.nd_grid() - >>> mgrid[0:5,0:5] - array([[[0, 0, 0, 0, 0], - [1, 1, 1, 1, 1], - [2, 2, 2, 2, 2], - [3, 3, 3, 3, 3], - [4, 4, 4, 4, 4]], - [[0, 1, 2, 3, 4], - [0, 1, 2, 3, 4], - [0, 1, 2, 3, 4], - [0, 1, 2, 3, 4], - [0, 1, 2, 3, 4]]]) - >>> mgrid[-1:1:5j] - array([-1. , -0.5, 0. , 0.5, 1. ]) - - >>> ogrid = np.lib.index_tricks.nd_grid(sparse=True) - >>> ogrid[0:5,0:5] - [array([[0], - [1], - [2], - [3], - [4]]), array([[0, 1, 2, 3, 4]])] - """ def __init__(self, sparse=False): @@ -163,39 +149,39 @@ def __getitem__(self, key): try: size = [] typ = int - for k in range(len(key)): - step = key[k].step - start = key[k].start + for kk in key: + step = kk.step + start = kk.start if start is None: start = 0 if step is None: step = 1 - if isinstance(step, complex): + if isinstance(step, (_nx.complexfloating, complex)): size.append(int(abs(step))) typ = float else: size.append( - int(math.ceil((key[k].stop - start)/(step*1.0)))) - if (isinstance(step, float) or - isinstance(start, float) or - isinstance(key[k].stop, float)): + int(math.ceil((kk.stop - start) / (step * 1.0)))) + if (isinstance(step, (_nx.floating, float)) or + isinstance(start, (_nx.floating, float)) or + isinstance(kk.stop, (_nx.floating, float))): typ = float if self.sparse: nn = [_nx.arange(_x, dtype=_t) - for _x, _t in zip(size, (typ,)*len(size))] + for _x, _t in zip(size, (typ,)*len(size))] else: nn = _nx.indices(size, typ) - for k in range(len(size)): - step = key[k].step - start = key[k].start + for k, kk in enumerate(key): + step = kk.step + start = kk.start if start is None: start = 0 if step is None: step = 1 - if isinstance(step, complex): + if isinstance(step, (_nx.complexfloating, complex)): step = int(abs(step)) if step != 1: - step = (key[k].stop - start)/float(step-1) + step = (kk.stop - start) / float(step - 1) nn[k] = (nn[k]*step+start) if self.sparse: slobj = [_nx.newaxis]*len(size) @@ -210,25 +196,114 @@ def __getitem__(self, key): start = key.start if start is None: start = 0 - if isinstance(step, complex): + if isinstance(step, (_nx.complexfloating, complex)): step = abs(step) length = int(step) if step != 1: step = (key.stop-start)/float(step-1) - stop = key.stop + step return _nx.arange(0, length, 1, float)*step + start else: return _nx.arange(start, stop, step) - def __len__(self): - return 0 -mgrid = nd_grid(sparse=False) -ogrid = nd_grid(sparse=True) -mgrid.__doc__ = None # set in numpy.add_newdocs -ogrid.__doc__ = None # set in numpy.add_newdocs +class MGridClass(nd_grid): + """ + `nd_grid` instance which returns a dense multi-dimensional "meshgrid". + + An instance of `numpy.lib.index_tricks.nd_grid` which returns an dense + (or fleshed out) mesh-grid when indexed, so that each returned argument + has the same shape. The dimensions and number of the output arrays are + equal to the number of indexing dimensions. If the step length is not a + complex number, then the stop is not inclusive. + + However, if the step length is a **complex number** (e.g. 5j), then + the integer part of its magnitude is interpreted as specifying the + number of points to create between the start and stop values, where + the stop value **is inclusive**. + + Returns + ------- + mesh-grid `ndarrays` all of the same dimensions + + See Also + -------- + numpy.lib.index_tricks.nd_grid : class of `ogrid` and `mgrid` objects + ogrid : like mgrid but returns open (not fleshed out) mesh grids + r_ : array concatenator + + Examples + -------- + >>> np.mgrid[0:5,0:5] + array([[[0, 0, 0, 0, 0], + [1, 1, 1, 1, 1], + [2, 2, 2, 2, 2], + [3, 3, 3, 3, 3], + [4, 4, 4, 4, 4]], + [[0, 1, 2, 3, 4], + [0, 1, 2, 3, 4], + [0, 1, 2, 3, 4], + [0, 1, 2, 3, 4], + [0, 1, 2, 3, 4]]]) + >>> np.mgrid[-1:1:5j] + array([-1. , -0.5, 0. , 0.5, 1. ]) + + """ + + def __init__(self): + super().__init__(sparse=False) + + +mgrid = MGridClass() + -class AxisConcatenator(object): +class OGridClass(nd_grid): + """ + `nd_grid` instance which returns an open multi-dimensional "meshgrid". + + An instance of `numpy.lib.index_tricks.nd_grid` which returns an open + (i.e. not fleshed out) mesh-grid when indexed, so that only one dimension + of each returned array is greater than 1. The dimension and number of the + output arrays are equal to the number of indexing dimensions. If the step + length is not a complex number, then the stop is not inclusive. + + However, if the step length is a **complex number** (e.g. 5j), then + the integer part of its magnitude is interpreted as specifying the + number of points to create between the start and stop values, where + the stop value **is inclusive**. + + Returns + ------- + mesh-grid + `ndarrays` with only one dimension not equal to 1 + + See Also + -------- + np.lib.index_tricks.nd_grid : class of `ogrid` and `mgrid` objects + mgrid : like `ogrid` but returns dense (or fleshed out) mesh grids + r_ : array concatenator + + Examples + -------- + >>> from numpy import ogrid + >>> ogrid[-1:1:5j] + array([-1. , -0.5, 0. , 0.5, 1. ]) + >>> ogrid[0:5,0:5] + [array([[0], + [1], + [2], + [3], + [4]]), array([[0, 1, 2, 3, 4]])] + + """ + + def __init__(self): + super().__init__(sparse=True) + + +ogrid = OGridClass() + + +class AxisConcatenator: """ Translates slice objects to concatenation along an axis. @@ -275,9 +350,9 @@ def __getitem__(self, key): start = 0 if step is None: step = 1 - if isinstance(step, complex): + if isinstance(step, (_nx.complexfloating, complex)): size = int(abs(step)) - newobj = function_base.linspace(start, stop, num=size) + newobj = linspace(start, stop, num=size) else: newobj = _nx.arange(start, stop, step) if ndmin > 1: @@ -287,7 +362,7 @@ def __getitem__(self, key): elif isinstance(item, str): if k != 0: raise ValueError("special directives must be the " - "first entry.") + "first entry.") if item in ('r', 'c'): matrix = True col = (item == 'c') @@ -299,13 +374,15 @@ def __getitem__(self, key): if len(vec) == 3: trans1d = int(vec[2]) continue - except Exception: - raise ValueError("unknown special directive") + except Exception as e: + raise ValueError( + "unknown special directive {!r}".format(item) + ) from e try: axis = int(item) continue - except (ValueError, TypeError): - raise ValueError("unknown special directive") + except (ValueError, TypeError) as e: + raise ValueError("unknown special directive") from e elif type(item) in ScalarType: newobj = array(item, ndmin=ndmin) scalars.append(len(objs)) @@ -348,6 +425,7 @@ def __len__(self): # etc. because otherwise we couldn't get the doc string to come out right # in help(r_) + class RClass(AxisConcatenator): """ Translates slice objects to concatenation along the first axis. @@ -409,7 +487,7 @@ class RClass(AxisConcatenator): Examples -------- >>> np.r_[np.array([1,2,3]), 0, 0, np.array([4,5,6])] - array([1, 2, 3, 0, 0, 4, 5, 6]) + array([1, 2, 3, ..., 4, 5, 6]) >>> np.r_[-1:1:6j, [0]*3, 5, 6] array([-1. , -0.6, -0.2, 0.2, 0.6, 1. , 0. , 0. , 0. , 5. , 6. ]) @@ -446,8 +524,10 @@ class RClass(AxisConcatenator): def __init__(self): AxisConcatenator.__init__(self, 0) + r_ = RClass() + class CClass(AxisConcatenator): """ Translates slice objects to concatenation along the second axis. @@ -456,7 +536,7 @@ class CClass(AxisConcatenator): useful because of its common occurrence. In particular, arrays will be stacked along their last axis after being upgraded to at least 2-D with 1's post-pended to the shape (column vectors made out of 1-D arrays). - + See Also -------- column_stack : Stack 1-D arrays as columns into a 2-D array. @@ -469,16 +549,19 @@ class CClass(AxisConcatenator): [2, 5], [3, 6]]) >>> np.c_[np.array([[1,2,3]]), 0, 0, np.array([[4,5,6]])] - array([[1, 2, 3, 0, 0, 4, 5, 6]]) + array([[1, 2, 3, ..., 4, 5, 6]]) """ def __init__(self): AxisConcatenator.__init__(self, -1, ndmin=2, trans1d=0) + c_ = CClass() -class ndenumerate(object): + +@set_module('numpy') +class ndenumerate: """ Multidimensional index iterator. @@ -525,10 +608,9 @@ def __next__(self): def __iter__(self): return self - next = __next__ - -class ndindex(object): +@set_module('numpy') +class ndindex: """ An N-dimensional iterator object to index arrays. @@ -538,8 +620,9 @@ class ndindex(object): Parameters ---------- - `*args` : ints - The size of each dimension of the array. + shape : ints, or a single tuple of ints + The size of each dimension of the array can be passed as + individual parameters or as the elements of a tuple. See Also -------- @@ -547,6 +630,8 @@ class ndindex(object): Examples -------- + Dimensions as individual arguments + >>> for index in np.ndindex(3, 2, 1): ... print(index) (0, 0, 0) @@ -556,6 +641,17 @@ class ndindex(object): (2, 0, 0) (2, 1, 0) + Same dimensions - but in a tuple ``(3, 2, 1)`` + + >>> for index in np.ndindex((3, 2, 1)): + ... print(index) + (0, 0, 0) + (0, 1, 0) + (1, 0, 0) + (1, 1, 0) + (2, 0, 0) + (2, 1, 0) + """ def __init__(self, *shape): @@ -574,7 +670,15 @@ def ndincr(self): Increment the multi-dimensional index by one. This method is for backward compatibility only: do not use. + + .. deprecated:: 1.20.0 + This method has been advised against since numpy 1.8.0, but only + started emitting DeprecationWarning as of this version. """ + # NumPy 1.20.0, 2020-09-08 + warnings.warn( + "`ndindex.ndincr()` is deprecated, use `next(ndindex)` instead", + DeprecationWarning, stacklevel=2) next(self) def __next__(self): @@ -592,8 +696,6 @@ def __next__(self): next(self._it) return self._it.multi_index - next = __next__ - # You can do all this with slice() plus a few special objects, # but there's a lot to remember. This version is simpler because @@ -606,7 +708,7 @@ def __next__(self): # # -class IndexExpression(object): +class IndexExpression: """ A nicer way to build up index tuples for arrays. @@ -659,6 +761,7 @@ def __getitem__(self, item): else: return item + index_exp = IndexExpression(maketuple=True) s_ = IndexExpression(maketuple=False) @@ -668,6 +771,12 @@ def __getitem__(self, item): # The following functions complement those in twodim_base, but are # applicable to N-dimensions. + +def _fill_diagonal_dispatcher(a, val, wrap=None): + return (a,) + + +@array_function_dispatch(_fill_diagonal_dispatcher) def fill_diagonal(a, val, wrap=False): """Fill the main diagonal of the given array of any dimensionality. @@ -680,9 +789,11 @@ def fill_diagonal(a, val, wrap=False): a : array, at least 2-D. Array whose diagonal is to be filled, it gets modified in-place. - val : scalar - Value to be written on the diagonal, its type must be compatible with - that of the array a. + val : scalar or array_like + Value(s) to write on the diagonal. If `val` is scalar, the value is + written along the diagonal. If array-like, the flattened `val` is + written along the diagonal, repeating if necessary to fill all + diagonal entries. wrap : bool For tall matrices in NumPy version up to 1.6.2, the @@ -733,8 +844,8 @@ def fill_diagonal(a, val, wrap=False): The wrap option affects only tall matrices: >>> # tall matrices no wrap - >>> a = np.zeros((5, 3),int) - >>> fill_diagonal(a, 4) + >>> a = np.zeros((5, 3), int) + >>> np.fill_diagonal(a, 4) >>> a array([[4, 0, 0], [0, 4, 0], @@ -743,8 +854,8 @@ def fill_diagonal(a, val, wrap=False): [0, 0, 0]]) >>> # tall matrices wrap - >>> a = np.zeros((5, 3),int) - >>> fill_diagonal(a, 4, wrap=True) + >>> a = np.zeros((5, 3), int) + >>> np.fill_diagonal(a, 4, wrap=True) >>> a array([[4, 0, 0], [0, 4, 0], @@ -753,13 +864,30 @@ def fill_diagonal(a, val, wrap=False): [4, 0, 0]]) >>> # wide matrices - >>> a = np.zeros((3, 5),int) - >>> fill_diagonal(a, 4, wrap=True) + >>> a = np.zeros((3, 5), int) + >>> np.fill_diagonal(a, 4, wrap=True) >>> a array([[4, 0, 0, 0, 0], [0, 4, 0, 0, 0], [0, 0, 4, 0, 0]]) + The anti-diagonal can be filled by reversing the order of elements + using either `numpy.flipud` or `numpy.fliplr`. + + >>> a = np.zeros((3, 3), int); + >>> np.fill_diagonal(np.fliplr(a), [1,2,3]) # Horizontal flip + >>> a + array([[0, 0, 1], + [0, 2, 0], + [3, 0, 0]]) + >>> np.fill_diagonal(np.flipud(a), [1,2,3]) # Vertical flip + >>> a + array([[0, 0, 3], + [0, 2, 0], + [1, 0, 0]]) + + Note that the order in which the diagonal is filled varies depending + on the flip function. """ if a.ndim < 2: raise ValueError("array must be at least 2-d") @@ -768,7 +896,7 @@ def fill_diagonal(a, val, wrap=False): # Explicit, fast formula for the common case. For 2-d arrays, we # accept rectangular ones. step = a.shape[1] + 1 - #This is needed to don't have tall matrix have the diagonal wrap. + # This is needed to don't have tall matrix have the diagonal wrap. if not wrap: end = a.shape[1] * a.shape[1] else: @@ -782,6 +910,7 @@ def fill_diagonal(a, val, wrap=False): a.flat[:end:step] = val +@set_module('numpy') def diag_indices(n, ndim=2): """ Return the indices to access the main diagonal of an array. @@ -801,7 +930,7 @@ def diag_indices(n, ndim=2): ndim : int, optional The number of dimensions. - See also + See Also -------- diag_indices_from @@ -850,6 +979,11 @@ def diag_indices(n, ndim=2): return (idx,) * ndim +def _diag_indices_from(arr): + return (arr,) + + +@array_function_dispatch(_diag_indices_from) def diag_indices_from(arr): """ Return the indices to access the main diagonal of an n-dimensional array. diff --git a/numpy/lib/index_tricks.pyi b/numpy/lib/index_tricks.pyi new file mode 100644 index 000000000000..d16faf81a0bb --- /dev/null +++ b/numpy/lib/index_tricks.pyi @@ -0,0 +1,165 @@ +from typing import ( + Any, + Tuple, + TypeVar, + Generic, + overload, + List, + Union, + Sequence, + Literal, + SupportsIndex, +) + +from numpy import ( + # Circumvent a naming conflict with `AxisConcatenator.matrix` + matrix as _Matrix, + ndenumerate as ndenumerate, + ndindex as ndindex, + ndarray, + dtype, + integer, + str_, + bytes_, + bool_, + int_, + float_, + complex_, + intp, + _OrderCF, + _ModeKind, +) +from numpy.typing import ( + # Arrays + ArrayLike, + _NestedSequence, + _FiniteNestedSequence, + NDArray, + _ArrayLikeInt, + + # DTypes + DTypeLike, + _SupportsDType, + + # Shapes + _ShapeLike, +) + +from numpy.core.multiarray import ( + unravel_index as unravel_index, + ravel_multi_index as ravel_multi_index, +) + +_T = TypeVar("_T") +_DType = TypeVar("_DType", bound=dtype[Any]) +_BoolType = TypeVar("_BoolType", Literal[True], Literal[False]) +_TupType = TypeVar("_TupType", bound=Tuple[Any, ...]) +_ArrayType = TypeVar("_ArrayType", bound=ndarray[Any, Any]) + +__all__: List[str] + +@overload +def ix_(*args: _FiniteNestedSequence[_SupportsDType[_DType]]) -> Tuple[ndarray[Any, _DType], ...]: ... +@overload +def ix_(*args: str | _NestedSequence[str]) -> Tuple[NDArray[str_], ...]: ... +@overload +def ix_(*args: bytes | _NestedSequence[bytes]) -> Tuple[NDArray[bytes_], ...]: ... +@overload +def ix_(*args: bool | _NestedSequence[bool]) -> Tuple[NDArray[bool_], ...]: ... +@overload +def ix_(*args: int | _NestedSequence[int]) -> Tuple[NDArray[int_], ...]: ... +@overload +def ix_(*args: float | _NestedSequence[float]) -> Tuple[NDArray[float_], ...]: ... +@overload +def ix_(*args: complex | _NestedSequence[complex]) -> Tuple[NDArray[complex_], ...]: ... + +class nd_grid(Generic[_BoolType]): + sparse: _BoolType + def __init__(self, sparse: _BoolType = ...) -> None: ... + @overload + def __getitem__( + self: nd_grid[Literal[False]], + key: Union[slice, Sequence[slice]], + ) -> NDArray[Any]: ... + @overload + def __getitem__( + self: nd_grid[Literal[True]], + key: Union[slice, Sequence[slice]], + ) -> List[NDArray[Any]]: ... + +class MGridClass(nd_grid[Literal[False]]): + def __init__(self) -> None: ... + +mgrid: MGridClass + +class OGridClass(nd_grid[Literal[True]]): + def __init__(self) -> None: ... + +ogrid: OGridClass + +class AxisConcatenator: + axis: int + matrix: bool + ndmin: int + trans1d: int + def __init__( + self, + axis: int = ..., + matrix: bool = ..., + ndmin: int = ..., + trans1d: int = ..., + ) -> None: ... + @staticmethod + @overload + def concatenate( # type: ignore[misc] + *a: ArrayLike, axis: SupportsIndex = ..., out: None = ... + ) -> NDArray[Any]: ... + @staticmethod + @overload + def concatenate( + *a: ArrayLike, axis: SupportsIndex = ..., out: _ArrayType = ... + ) -> _ArrayType: ... + @staticmethod + def makemat( + data: ArrayLike, dtype: DTypeLike = ..., copy: bool = ... + ) -> _Matrix: ... + + # TODO: Sort out this `__getitem__` method + def __getitem__(self, key: Any) -> Any: ... + +class RClass(AxisConcatenator): + axis: Literal[0] + matrix: Literal[False] + ndmin: Literal[1] + trans1d: Literal[-1] + def __init__(self) -> None: ... + +r_: RClass + +class CClass(AxisConcatenator): + axis: Literal[-1] + matrix: Literal[False] + ndmin: Literal[2] + trans1d: Literal[0] + def __init__(self) -> None: ... + +c_: CClass + +class IndexExpression(Generic[_BoolType]): + maketuple: _BoolType + def __init__(self, maketuple: _BoolType) -> None: ... + @overload + def __getitem__(self, item: _TupType) -> _TupType: ... # type: ignore[misc] + @overload + def __getitem__(self: IndexExpression[Literal[True]], item: _T) -> Tuple[_T]: ... + @overload + def __getitem__(self: IndexExpression[Literal[False]], item: _T) -> _T: ... + +index_exp: IndexExpression[Literal[True]] +s_: IndexExpression[Literal[False]] + +def fill_diagonal(a: ndarray[Any, Any], val: Any, wrap: bool = ...) -> None: ... +def diag_indices(n: int, ndim: int = ...) -> Tuple[NDArray[int_], ...]: ... +def diag_indices_from(arr: ArrayLike) -> Tuple[NDArray[int_], ...]: ... + +# NOTE: see `numpy/__init__.pyi` for `ndenumerate` and `ndindex` diff --git a/numpy/lib/info.py b/numpy/lib/info.py deleted file mode 100644 index 8815a52f0a64..000000000000 --- a/numpy/lib/info.py +++ /dev/null @@ -1,160 +0,0 @@ -""" -Basic functions used by several sub-packages and -useful to have in the main name-space. - -Type Handling -------------- -================ =================== -iscomplexobj Test for complex object, scalar result -isrealobj Test for real object, scalar result -iscomplex Test for complex elements, array result -isreal Test for real elements, array result -imag Imaginary part -real Real part -real_if_close Turns complex number with tiny imaginary part to real -isneginf Tests for negative infinity, array result -isposinf Tests for positive infinity, array result -isnan Tests for nans, array result -isinf Tests for infinity, array result -isfinite Tests for finite numbers, array result -isscalar True if argument is a scalar -nan_to_num Replaces NaN's with 0 and infinities with large numbers -cast Dictionary of functions to force cast to each type -common_type Determine the minimum common type code for a group - of arrays -mintypecode Return minimal allowed common typecode. -================ =================== - -Index Tricks ------------- -================ =================== -mgrid Method which allows easy construction of N-d - 'mesh-grids' -``r_`` Append and construct arrays: turns slice objects into - ranges and concatenates them, for 2d arrays appends rows. -index_exp Konrad Hinsen's index_expression class instance which - can be useful for building complicated slicing syntax. -================ =================== - -Useful Functions ----------------- -================ =================== -select Extension of where to multiple conditions and choices -extract Extract 1d array from flattened array according to mask -insert Insert 1d array of values into Nd array according to mask -linspace Evenly spaced samples in linear space -logspace Evenly spaced samples in logarithmic space -fix Round x to nearest integer towards zero -mod Modulo mod(x,y) = x % y except keeps sign of y -amax Array maximum along axis -amin Array minimum along axis -ptp Array max-min along axis -cumsum Cumulative sum along axis -prod Product of elements along axis -cumprod Cumluative product along axis -diff Discrete differences along axis -angle Returns angle of complex argument -unwrap Unwrap phase along given axis (1-d algorithm) -sort_complex Sort a complex-array (based on real, then imaginary) -trim_zeros Trim the leading and trailing zeros from 1D array. -vectorize A class that wraps a Python function taking scalar - arguments into a generalized function which can handle - arrays of arguments using the broadcast rules of - numerix Python. -================ =================== - -Shape Manipulation ------------------- -================ =================== -squeeze Return a with length-one dimensions removed. -atleast_1d Force arrays to be >= 1D -atleast_2d Force arrays to be >= 2D -atleast_3d Force arrays to be >= 3D -vstack Stack arrays vertically (row on row) -hstack Stack arrays horizontally (column on column) -column_stack Stack 1D arrays as columns into 2D array -dstack Stack arrays depthwise (along third dimension) -stack Stack arrays along a new axis -split Divide array into a list of sub-arrays -hsplit Split into columns -vsplit Split into rows -dsplit Split along third dimension -================ =================== - -Matrix (2D Array) Manipulations -------------------------------- -================ =================== -fliplr 2D array with columns flipped -flipud 2D array with rows flipped -rot90 Rotate a 2D array a multiple of 90 degrees -eye Return a 2D array with ones down a given diagonal -diag Construct a 2D array from a vector, or return a given - diagonal from a 2D array. -mat Construct a Matrix -bmat Build a Matrix from blocks -================ =================== - -Polynomials ------------ -================ =================== -poly1d A one-dimensional polynomial class -poly Return polynomial coefficients from roots -roots Find roots of polynomial given coefficients -polyint Integrate polynomial -polyder Differentiate polynomial -polyadd Add polynomials -polysub Subtract polynomials -polymul Multiply polynomials -polydiv Divide polynomials -polyval Evaluate polynomial at given argument -================ =================== - -Iterators ---------- -================ =================== -Arrayterator A buffered iterator for big arrays. -================ =================== - -Import Tricks -------------- -================ =================== -ppimport Postpone module import until trying to use it -ppimport_attr Postpone module import until trying to use its attribute -ppresolve Import postponed module and return it. -================ =================== - -Machine Arithmetics -------------------- -================ =================== -machar_single Single precision floating point arithmetic parameters -machar_double Double precision floating point arithmetic parameters -================ =================== - -Threading Tricks ----------------- -================ =================== -ParallelExec Execute commands in parallel thread. -================ =================== - -Array Set Operations ------------------------ -Set operations for numeric arrays based on sort() function. - -================ =================== -unique Unique elements of an array. -isin Test whether each element of an ND array is present - anywhere within a second array. -ediff1d Array difference (auxiliary function). -intersect1d Intersection of 1D arrays with unique elements. -setxor1d Set exclusive-or of 1D arrays with unique elements. -in1d Test whether elements in a 1D array are also present in - another array. -union1d Union of 1D arrays with unique elements. -setdiff1d Set difference of 1D arrays with unique elements. -================ =================== - -""" -from __future__ import division, absolute_import, print_function - -depends = ['core', 'testing'] -global_symbols = ['*'] diff --git a/numpy/lib/mixins.py b/numpy/lib/mixins.py index 0379ecb1ada2..c81239f6b446 100644 --- a/numpy/lib/mixins.py +++ b/numpy/lib/mixins.py @@ -1,12 +1,8 @@ """Mixin classes for custom array types that don't inherit from ndarray.""" -from __future__ import division, absolute_import, print_function - -import sys - from numpy.core import umath as um -# Nothing should be exposed in the top-level NumPy module. -__all__ = [] + +__all__ = ['NDArrayOperatorsMixin'] def _disables_array_ufunc(obj): @@ -60,7 +56,7 @@ def func(self): return func -class NDArrayOperatorsMixin(object): +class NDArrayOperatorsMixin: """Mixin defining all operator special methods using __array_ufunc__. This class implements the special methods for almost all of Python's @@ -69,13 +65,10 @@ class NDArrayOperatorsMixin(object): deferring to the ``__array_ufunc__`` method, which subclasses must implement. - This class does not yet implement the special operators corresponding - to ``matmul`` (``@``), because ``np.matmul`` is not yet a NumPy ufunc. - It is useful for writing classes that do not inherit from `numpy.ndarray`, but that should support arithmetic and numpy universal functions like arrays as described in `A Mechanism for Overriding Ufuncs - <../../neps/nep-0013-ufunc-overrides.html>`_. + <https://numpy.org/neps/nep-0013-ufunc-overrides.html>`_. As an trivial example, consider this implementation of an ``ArrayLike`` class that simply wraps a NumPy array and ensures that the result of any @@ -155,9 +148,9 @@ def __repr__(self): __add__, __radd__, __iadd__ = _numeric_methods(um.add, 'add') __sub__, __rsub__, __isub__ = _numeric_methods(um.subtract, 'sub') __mul__, __rmul__, __imul__ = _numeric_methods(um.multiply, 'mul') - if sys.version_info.major < 3: - # Python 3 uses only __truediv__ and __floordiv__ - __div__, __rdiv__, __idiv__ = _numeric_methods(um.divide, 'div') + __matmul__, __rmatmul__, __imatmul__ = _numeric_methods( + um.matmul, 'matmul') + # Python 3 does not use __div__, __rdiv__, or __idiv__ __truediv__, __rtruediv__, __itruediv__ = _numeric_methods( um.true_divide, 'truediv') __floordiv__, __rfloordiv__, __ifloordiv__ = _numeric_methods( diff --git a/numpy/lib/mixins.pyi b/numpy/lib/mixins.pyi new file mode 100644 index 000000000000..f137bb5bcf4b --- /dev/null +++ b/numpy/lib/mixins.pyi @@ -0,0 +1,62 @@ +from typing import List +from abc import ABCMeta, abstractmethod + +__all__: List[str] + +# NOTE: `NDArrayOperatorsMixin` is not formally an abstract baseclass, +# even though it's reliant on subclasses implementing `__array_ufunc__` + +class NDArrayOperatorsMixin(metaclass=ABCMeta): + @abstractmethod + def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): ... + def __lt__(self, other): ... + def __le__(self, other): ... + def __eq__(self, other): ... + def __ne__(self, other): ... + def __gt__(self, other): ... + def __ge__(self, other): ... + def __add__(self, other): ... + def __radd__(self, other): ... + def __iadd__(self, other): ... + def __sub__(self, other): ... + def __rsub__(self, other): ... + def __isub__(self, other): ... + def __mul__(self, other): ... + def __rmul__(self, other): ... + def __imul__(self, other): ... + def __matmul__(self, other): ... + def __rmatmul__(self, other): ... + def __imatmul__(self, other): ... + def __truediv__(self, other): ... + def __rtruediv__(self, other): ... + def __itruediv__(self, other): ... + def __floordiv__(self, other): ... + def __rfloordiv__(self, other): ... + def __ifloordiv__(self, other): ... + def __mod__(self, other): ... + def __rmod__(self, other): ... + def __imod__(self, other): ... + def __divmod__(self, other): ... + def __rdivmod__(self, other): ... + def __pow__(self, other): ... + def __rpow__(self, other): ... + def __ipow__(self, other): ... + def __lshift__(self, other): ... + def __rlshift__(self, other): ... + def __ilshift__(self, other): ... + def __rshift__(self, other): ... + def __rrshift__(self, other): ... + def __irshift__(self, other): ... + def __and__(self, other): ... + def __rand__(self, other): ... + def __iand__(self, other): ... + def __xor__(self, other): ... + def __rxor__(self, other): ... + def __ixor__(self, other): ... + def __or__(self, other): ... + def __ror__(self, other): ... + def __ior__(self, other): ... + def __neg__(self): ... + def __pos__(self): ... + def __abs__(self): ... + def __invert__(self): ... diff --git a/numpy/lib/nanfunctions.py b/numpy/lib/nanfunctions.py index abd2da1a2031..d7ea1ca65b7d 100644 --- a/numpy/lib/nanfunctions.py +++ b/numpy/lib/nanfunctions.py @@ -20,11 +20,15 @@ - `nanpercentile` -- qth percentile of non-NaN values """ -from __future__ import division, absolute_import, print_function - +import functools import warnings import numpy as np from numpy.lib import function_base +from numpy.core import overrides + + +array_function_dispatch = functools.partial( + overrides.array_function_dispatch, module='numpy') __all__ = [ @@ -34,6 +38,33 @@ ] +def _nan_mask(a, out=None): + """ + Parameters + ---------- + a : array-like + Input array with at least 1 dimension. + out : ndarray, optional + Alternate output array in which to place the result. The default + is ``None``; if provided, it must have the same shape as the + expected output and will prevent the allocation of a new array. + + Returns + ------- + y : bool ndarray or True + A bool array where ``np.nan`` positions are marked with ``False`` + and other positions are marked with ``True``. If the type of ``a`` + is such that it can't possibly contain ``np.nan``, returns ``True``. + """ + # we assume that a is an array for this private function + + if a.dtype.kind not in 'fc': + return True + + y = np.isnan(a, out=out) + y = np.invert(y, out=y) + return y + def _replace_nan(a, val): """ If `a` is of inexact type, make a copy of `a`, replace NaNs with @@ -62,17 +93,18 @@ def _replace_nan(a, val): NaNs, otherwise return None. """ - a = np.array(a, subok=True, copy=True) + a = np.asanyarray(a) if a.dtype == np.object_: # object arrays do not support `isnan` (gh-9009), so make a guess - mask = a != a + mask = np.not_equal(a, a, dtype=bool) elif issubclass(a.dtype.type, np.inexact): mask = np.isnan(a) else: mask = None if mask is not None: + a = np.array(a, subok=True, copy=True) np.copyto(a, val, where=mask) return a, mask @@ -128,11 +160,16 @@ def _remove_nan_1d(arr1d, overwrite_input=False): True if `res` can be modified in place, given the constraint on the input """ + if arr1d.dtype == object: + # object arrays do not support `isnan` (gh-9009), so make a guess + c = np.not_equal(arr1d, arr1d, dtype=bool) + else: + c = np.isnan(arr1d) - c = np.isnan(arr1d) s = np.nonzero(c)[0] if s.size == arr1d.size: - warnings.warn("All-NaN slice encountered", RuntimeWarning, stacklevel=4) + warnings.warn("All-NaN slice encountered", RuntimeWarning, + stacklevel=5) return arr1d[:0], True elif s.size == 0: return arr1d, overwrite_input @@ -181,14 +218,25 @@ def _divide_by_count(a, b, out=None): return np.divide(a, b, out=out, casting='unsafe') else: if out is None: - return a.dtype.type(a / b) + # Precaution against reduced object arrays + try: + return a.dtype.type(a / b) + except AttributeError: + return a / b else: # This is questionable, but currently a numpy scalar can # be output to a zero dimensional array. return np.divide(a, b, out=out, casting='unsafe') -def nanmin(a, axis=None, out=None, keepdims=np._NoValue): +def _nanmin_dispatcher(a, axis=None, out=None, keepdims=None, + initial=None, where=None): + return (a, out) + + +@array_function_dispatch(_nanmin_dispatcher) +def nanmin(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue, + where=np._NoValue): """ Return minimum of an array or minimum along an axis, ignoring any NaNs. When all-NaN slices are encountered a ``RuntimeWarning`` is raised and @@ -205,8 +253,8 @@ def nanmin(a, axis=None, out=None, keepdims=np._NoValue): out : ndarray, optional Alternate output array in which to place the result. The default is ``None``; if provided, it must have the same shape as the - expected output, but the type will be cast if necessary. See - `doc.ufuncs` for details. + expected output, but the type will be cast if necessary. See + :ref:`ufuncs-output-type` for more details. .. versionadded:: 1.8.0 keepdims : bool, optional @@ -220,6 +268,16 @@ def nanmin(a, axis=None, out=None, keepdims=np._NoValue): does not implement `keepdims` any exceptions will be raised. .. versionadded:: 1.8.0 + initial : scalar, optional + The maximum value of an output element. Must be present to allow + computation on empty slice. See `~numpy.ufunc.reduce` for details. + + .. versionadded:: 1.22.0 + where : array_like of bool, optional + Elements to compare for the minimum. See `~numpy.ufunc.reduce` + for details. + + .. versionadded:: 1.22.0 Returns ------- @@ -260,9 +318,9 @@ def nanmin(a, axis=None, out=None, keepdims=np._NoValue): >>> np.nanmin(a) 1.0 >>> np.nanmin(a, axis=0) - array([ 1., 2.]) + array([1., 2.]) >>> np.nanmin(a, axis=1) - array([ 1., 3.]) + array([1., 3.]) When positive infinity and negative infinity are present: @@ -275,12 +333,18 @@ def nanmin(a, axis=None, out=None, keepdims=np._NoValue): kwargs = {} if keepdims is not np._NoValue: kwargs['keepdims'] = keepdims + if initial is not np._NoValue: + kwargs['initial'] = initial + if where is not np._NoValue: + kwargs['where'] = where + if type(a) is np.ndarray and a.dtype != np.object_: # Fast, but not safe for subclasses of ndarray, or object arrays, # which do not implement isnan (gh-9009), or fmin correctly (gh-8975) res = np.fmin.reduce(a, axis=axis, out=out, **kwargs) if np.isnan(res).any(): - warnings.warn("All-NaN slice encountered", RuntimeWarning, stacklevel=2) + warnings.warn("All-NaN slice encountered", RuntimeWarning, + stacklevel=3) else: # Slow, but safe for subclasses of ndarray a, mask = _replace_nan(a, +np.inf) @@ -289,14 +353,23 @@ def nanmin(a, axis=None, out=None, keepdims=np._NoValue): return res # Check for all-NaN axis + kwargs.pop("initial", None) mask = np.all(mask, axis=axis, **kwargs) if np.any(mask): res = _copyto(res, np.nan, mask) - warnings.warn("All-NaN axis encountered", RuntimeWarning, stacklevel=2) + warnings.warn("All-NaN axis encountered", RuntimeWarning, + stacklevel=3) return res -def nanmax(a, axis=None, out=None, keepdims=np._NoValue): +def _nanmax_dispatcher(a, axis=None, out=None, keepdims=None, + initial=None, where=None): + return (a, out) + + +@array_function_dispatch(_nanmax_dispatcher) +def nanmax(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue, + where=np._NoValue): """ Return the maximum of an array or maximum along an axis, ignoring any NaNs. When all-NaN slices are encountered a ``RuntimeWarning`` is @@ -313,8 +386,8 @@ def nanmax(a, axis=None, out=None, keepdims=np._NoValue): out : ndarray, optional Alternate output array in which to place the result. The default is ``None``; if provided, it must have the same shape as the - expected output, but the type will be cast if necessary. See - `doc.ufuncs` for details. + expected output, but the type will be cast if necessary. See + :ref:`ufuncs-output-type` for more details. .. versionadded:: 1.8.0 keepdims : bool, optional @@ -328,6 +401,16 @@ def nanmax(a, axis=None, out=None, keepdims=np._NoValue): does not implement `keepdims` any exceptions will be raised. .. versionadded:: 1.8.0 + initial : scalar, optional + The minimum value of an output element. Must be present to allow + computation on empty slice. See `~numpy.ufunc.reduce` for details. + + .. versionadded:: 1.22.0 + where : array_like of bool, optional + Elements to compare for the maximum. See `~numpy.ufunc.reduce` + for details. + + .. versionadded:: 1.22.0 Returns ------- @@ -368,9 +451,9 @@ def nanmax(a, axis=None, out=None, keepdims=np._NoValue): >>> np.nanmax(a) 3.0 >>> np.nanmax(a, axis=0) - array([ 3., 2.]) + array([3., 2.]) >>> np.nanmax(a, axis=1) - array([ 2., 3.]) + array([2., 3.]) When positive infinity and negative infinity are present: @@ -383,12 +466,18 @@ def nanmax(a, axis=None, out=None, keepdims=np._NoValue): kwargs = {} if keepdims is not np._NoValue: kwargs['keepdims'] = keepdims + if initial is not np._NoValue: + kwargs['initial'] = initial + if where is not np._NoValue: + kwargs['where'] = where + if type(a) is np.ndarray and a.dtype != np.object_: # Fast, but not safe for subclasses of ndarray, or object arrays, # which do not implement isnan (gh-9009), or fmax correctly (gh-8975) res = np.fmax.reduce(a, axis=axis, out=out, **kwargs) if np.isnan(res).any(): - warnings.warn("All-NaN slice encountered", RuntimeWarning, stacklevel=2) + warnings.warn("All-NaN slice encountered", RuntimeWarning, + stacklevel=3) else: # Slow, but safe for subclasses of ndarray a, mask = _replace_nan(a, -np.inf) @@ -397,14 +486,21 @@ def nanmax(a, axis=None, out=None, keepdims=np._NoValue): return res # Check for all-NaN axis + kwargs.pop("initial", None) mask = np.all(mask, axis=axis, **kwargs) if np.any(mask): res = _copyto(res, np.nan, mask) - warnings.warn("All-NaN axis encountered", RuntimeWarning, stacklevel=2) + warnings.warn("All-NaN axis encountered", RuntimeWarning, + stacklevel=3) return res -def nanargmin(a, axis=None): +def _nanargmin_dispatcher(a, axis=None, out=None, *, keepdims=None): + return (a,) + + +@array_function_dispatch(_nanargmin_dispatcher) +def nanargmin(a, axis=None, out=None, *, keepdims=np._NoValue): """ Return the indices of the minimum values in the specified axis ignoring NaNs. For all-NaN slices ``ValueError`` is raised. Warning: the results @@ -416,6 +512,17 @@ def nanargmin(a, axis=None): Input data. axis : int, optional Axis along which to operate. By default flattened input is used. + out : array, optional + If provided, the result will be inserted into this array. It should + be of the appropriate shape and dtype. + + .. versionadded:: 1.22.0 + keepdims : bool, optional + If this is set to True, the axes which are reduced are left + in the result as dimensions with size one. With this option, + the result will broadcast correctly against the array. + + .. versionadded:: 1.22.0 Returns ------- @@ -440,15 +547,20 @@ def nanargmin(a, axis=None): """ a, mask = _replace_nan(a, np.inf) - res = np.argmin(a, axis=axis) if mask is not None: mask = np.all(mask, axis=axis) if np.any(mask): raise ValueError("All-NaN slice encountered") + res = np.argmin(a, axis=axis, out=out, keepdims=keepdims) return res -def nanargmax(a, axis=None): +def _nanargmax_dispatcher(a, axis=None, out=None, *, keepdims=None): + return (a,) + + +@array_function_dispatch(_nanargmax_dispatcher) +def nanargmax(a, axis=None, out=None, *, keepdims=np._NoValue): """ Return the indices of the maximum values in the specified axis ignoring NaNs. For all-NaN slices ``ValueError`` is raised. Warning: the @@ -461,6 +573,17 @@ def nanargmax(a, axis=None): Input data. axis : int, optional Axis along which to operate. By default flattened input is used. + out : array, optional + If provided, the result will be inserted into this array. It should + be of the appropriate shape and dtype. + + .. versionadded:: 1.22.0 + keepdims : bool, optional + If this is set to True, the axes which are reduced are left + in the result as dimensions with size one. With this option, + the result will broadcast correctly against the array. + + .. versionadded:: 1.22.0 Returns ------- @@ -485,15 +608,22 @@ def nanargmax(a, axis=None): """ a, mask = _replace_nan(a, -np.inf) - res = np.argmax(a, axis=axis) if mask is not None: mask = np.all(mask, axis=axis) if np.any(mask): raise ValueError("All-NaN slice encountered") + res = np.argmax(a, axis=axis, out=out, keepdims=keepdims) return res -def nansum(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): +def _nansum_dispatcher(a, axis=None, dtype=None, out=None, keepdims=None, + initial=None, where=None): + return (a, out) + + +@array_function_dispatch(_nansum_dispatcher) +def nansum(a, axis=None, dtype=None, out=None, keepdims=np._NoValue, + initial=np._NoValue, where=np._NoValue): """ Return the sum of array elements over a given axis treating Not a Numbers (NaNs) as zero. @@ -522,8 +652,8 @@ def nansum(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): Alternate output array in which to place the result. The default is ``None``. If provided, it must have the same shape as the expected output, but the type will be cast if necessary. See - `doc.ufuncs` for details. The casting of NaN to integer can yield - unexpected results. + :ref:`ufuncs-output-type` for more details. The casting of NaN to integer + can yield unexpected results. .. versionadded:: 1.8.0 keepdims : bool, optional @@ -538,6 +668,14 @@ def nansum(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): does not implement `keepdims` any exceptions will be raised. .. versionadded:: 1.8.0 + initial : scalar, optional + Starting value for the sum. See `~numpy.ufunc.reduce` for details. + + .. versionadded:: 1.22.0 + where : array_like of bool, optional + Elements to include in the sum. See `~numpy.ufunc.reduce` for details. + + .. versionadded:: 1.22.0 Returns ------- @@ -551,7 +689,7 @@ def nansum(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): -------- numpy.sum : Sum across array propagating NaNs. isnan : Show which elements are NaN. - isfinite: Show which elements are not NaN or +/-inf. + isfinite : Show which elements are not NaN or +/-inf. Notes ----- @@ -570,20 +708,31 @@ def nansum(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): >>> np.nansum(a) 3.0 >>> np.nansum(a, axis=0) - array([ 2., 1.]) + array([2., 1.]) >>> np.nansum([1, np.nan, np.inf]) inf >>> np.nansum([1, np.nan, np.NINF]) -inf - >>> np.nansum([1, np.nan, np.inf, -np.inf]) # both +/- infinity present + >>> from numpy.testing import suppress_warnings + >>> with suppress_warnings() as sup: + ... sup.filter(RuntimeWarning) + ... np.nansum([1, np.nan, np.inf, -np.inf]) # both +/- infinity present nan """ a, mask = _replace_nan(a, 0) - return np.sum(a, axis=axis, dtype=dtype, out=out, keepdims=keepdims) + return np.sum(a, axis=axis, dtype=dtype, out=out, keepdims=keepdims, + initial=initial, where=where) + +def _nanprod_dispatcher(a, axis=None, dtype=None, out=None, keepdims=None, + initial=None, where=None): + return (a, out) -def nanprod(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): + +@array_function_dispatch(_nanprod_dispatcher) +def nanprod(a, axis=None, dtype=None, out=None, keepdims=np._NoValue, + initial=np._NoValue, where=np._NoValue): """ Return the product of array elements over a given axis treating Not a Numbers (NaNs) as ones. @@ -610,13 +759,23 @@ def nanprod(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): out : ndarray, optional Alternate output array in which to place the result. The default is ``None``. If provided, it must have the same shape as the - expected output, but the type will be cast if necessary. See - `doc.ufuncs` for details. The casting of NaN to integer can yield - unexpected results. + expected output, but the type will be cast if necessary. See + :ref:`ufuncs-output-type` for more details. The casting of NaN to integer + can yield unexpected results. keepdims : bool, optional If True, the axes which are reduced are left in the result as dimensions with size one. With this option, the result will broadcast correctly against the original `arr`. + initial : scalar, optional + The starting value for this product. See `~numpy.ufunc.reduce` + for details. + + .. versionadded:: 1.22.0 + where : array_like of bool, optional + Elements to include in the product. See `~numpy.ufunc.reduce` + for details. + + .. versionadded:: 1.22.0 Returns ------- @@ -641,13 +800,19 @@ def nanprod(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): >>> np.nanprod(a) 6.0 >>> np.nanprod(a, axis=0) - array([ 3., 2.]) + array([3., 2.]) """ a, mask = _replace_nan(a, 1) - return np.prod(a, axis=axis, dtype=dtype, out=out, keepdims=keepdims) + return np.prod(a, axis=axis, dtype=dtype, out=out, keepdims=keepdims, + initial=initial, where=where) + +def _nancumsum_dispatcher(a, axis=None, dtype=None, out=None): + return (a, out) + +@array_function_dispatch(_nancumsum_dispatcher) def nancumsum(a, axis=None, dtype=None, out=None): """ Return the cumulative sum of array elements over a given axis treating Not a @@ -674,8 +839,8 @@ def nancumsum(a, axis=None, dtype=None, out=None): out : ndarray, optional Alternative output array in which to place the result. It must have the same shape and buffer length as the expected output - but the type will be cast if necessary. See `doc.ufuncs` - (Section "Output arguments") for more details. + but the type will be cast if necessary. See :ref:`ufuncs-output-type` for + more details. Returns ------- @@ -697,22 +862,27 @@ def nancumsum(a, axis=None, dtype=None, out=None): >>> np.nancumsum([1]) array([1]) >>> np.nancumsum([1, np.nan]) - array([ 1., 1.]) + array([1., 1.]) >>> a = np.array([[1, 2], [3, np.nan]]) >>> np.nancumsum(a) - array([ 1., 3., 6., 6.]) + array([1., 3., 6., 6.]) >>> np.nancumsum(a, axis=0) - array([[ 1., 2.], - [ 4., 2.]]) + array([[1., 2.], + [4., 2.]]) >>> np.nancumsum(a, axis=1) - array([[ 1., 3.], - [ 3., 3.]]) + array([[1., 3.], + [3., 3.]]) """ a, mask = _replace_nan(a, 0) return np.cumsum(a, axis=axis, dtype=dtype, out=out) +def _nancumprod_dispatcher(a, axis=None, dtype=None, out=None): + return (a, out) + + +@array_function_dispatch(_nancumprod_dispatcher) def nancumprod(a, axis=None, dtype=None, out=None): """ Return the cumulative product of array elements over a given axis treating Not a @@ -759,23 +929,30 @@ def nancumprod(a, axis=None, dtype=None, out=None): >>> np.nancumprod([1]) array([1]) >>> np.nancumprod([1, np.nan]) - array([ 1., 1.]) + array([1., 1.]) >>> a = np.array([[1, 2], [3, np.nan]]) >>> np.nancumprod(a) - array([ 1., 2., 6., 6.]) + array([1., 2., 6., 6.]) >>> np.nancumprod(a, axis=0) - array([[ 1., 2.], - [ 3., 2.]]) + array([[1., 2.], + [3., 2.]]) >>> np.nancumprod(a, axis=1) - array([[ 1., 2.], - [ 3., 3.]]) + array([[1., 2.], + [3., 3.]]) """ a, mask = _replace_nan(a, 1) return np.cumprod(a, axis=axis, dtype=dtype, out=out) -def nanmean(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): +def _nanmean_dispatcher(a, axis=None, dtype=None, out=None, keepdims=None, + *, where=None): + return (a, out) + + +@array_function_dispatch(_nanmean_dispatcher) +def nanmean(a, axis=None, dtype=None, out=None, keepdims=np._NoValue, + *, where=np._NoValue): """ Compute the arithmetic mean along the specified axis, ignoring NaNs. @@ -802,8 +979,8 @@ def nanmean(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): out : ndarray, optional Alternate output array in which to place the result. The default is ``None``; if provided, it must have the same shape as the - expected output, but the type will be cast if necessary. See - `doc.ufuncs` for details. + expected output, but the type will be cast if necessary. See + :ref:`ufuncs-output-type` for more details. keepdims : bool, optional If this is set to True, the axes which are reduced are left in the result as dimensions with size one. With this option, @@ -813,6 +990,10 @@ def nanmean(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): `keepdims` will be passed through to the `mean` or `sum` methods of sub-classes of `ndarray`. If the sub-classes methods does not implement `keepdims` any exceptions will be raised. + where : array_like of bool, optional + Elements to include in the mean. See `~numpy.ufunc.reduce` for details. + + .. versionadded:: 1.22.0 Returns ------- @@ -844,14 +1025,15 @@ def nanmean(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): >>> np.nanmean(a) 2.6666666666666665 >>> np.nanmean(a, axis=0) - array([ 2., 4.]) + array([2., 4.]) >>> np.nanmean(a, axis=1) - array([ 1., 3.5]) + array([1., 3.5]) # may vary """ arr, mask = _replace_nan(a, 0) if mask is None: - return np.mean(arr, axis=axis, dtype=dtype, out=out, keepdims=keepdims) + return np.mean(arr, axis=axis, dtype=dtype, out=out, keepdims=keepdims, + where=where) if dtype is not None: dtype = np.dtype(dtype) @@ -860,13 +1042,15 @@ def nanmean(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): if out is not None and not issubclass(out.dtype.type, np.inexact): raise TypeError("If a is inexact, then out must be inexact") - cnt = np.sum(~mask, axis=axis, dtype=np.intp, keepdims=keepdims) - tot = np.sum(arr, axis=axis, dtype=dtype, out=out, keepdims=keepdims) + cnt = np.sum(~mask, axis=axis, dtype=np.intp, keepdims=keepdims, + where=where) + tot = np.sum(arr, axis=axis, dtype=dtype, out=out, keepdims=keepdims, + where=where) avg = _divide_by_count(tot, cnt, out=out) isbad = (cnt == 0) if isbad.any(): - warnings.warn("Mean of empty slice", RuntimeWarning, stacklevel=2) + warnings.warn("Mean of empty slice", RuntimeWarning, stacklevel=3) # NaN is the only possible bad value, so no further # action is needed to handle bad results. return avg @@ -877,12 +1061,16 @@ def _nanmedian1d(arr1d, overwrite_input=False): Private function for rank 1 arrays. Compute the median ignoring NaNs. See nanmedian for parameter usage """ - arr1d, overwrite_input = _remove_nan_1d(arr1d, - overwrite_input=overwrite_input) - if arr1d.size == 0: - return np.nan + arr1d_parsed, overwrite_input = _remove_nan_1d( + arr1d, overwrite_input=overwrite_input, + ) - return np.median(arr1d, overwrite_input=overwrite_input) + if arr1d_parsed.size == 0: + # Ensure that a nan-esque scalar of the appropriate type (and unit) + # is returned for `timedelta64` and `complexfloating` + return arr1d[-1] + + return np.median(arr1d_parsed, overwrite_input=overwrite_input) def _nanmedian(a, axis=None, out=None, overwrite_input=False): @@ -921,13 +1109,22 @@ def _nanmedian_small(a, axis=None, out=None, overwrite_input=False): a = np.ma.masked_array(a, np.isnan(a)) m = np.ma.median(a, axis=axis, overwrite_input=overwrite_input) for i in range(np.count_nonzero(m.mask.ravel())): - warnings.warn("All-NaN slice encountered", RuntimeWarning, stacklevel=3) + warnings.warn("All-NaN slice encountered", RuntimeWarning, + stacklevel=4) + + fill_value = np.timedelta64("NaT") if m.dtype.kind == "m" else np.nan if out is not None: - out[...] = m.filled(np.nan) + out[...] = m.filled(fill_value) return out - return m.filled(np.nan) + return m.filled(fill_value) + +def _nanmedian_dispatcher( + a, axis=None, out=None, overwrite_input=None, keepdims=None): + return (a, out) + +@array_function_dispatch(_nanmedian_dispatcher) def nanmedian(a, axis=None, out=None, overwrite_input=False, keepdims=np._NoValue): """ Compute the median along the specified axis, while ignoring NaNs. @@ -992,19 +1189,19 @@ def nanmedian(a, axis=None, out=None, overwrite_input=False, keepdims=np._NoValu >>> a = np.array([[10.0, 7, 4], [3, 2, 1]]) >>> a[0, 1] = np.nan >>> a - array([[ 10., nan, 4.], - [ 3., 2., 1.]]) + array([[10., nan, 4.], + [ 3., 2., 1.]]) >>> np.median(a) nan >>> np.nanmedian(a) 3.0 >>> np.nanmedian(a, axis=0) - array([ 6.5, 2., 2.5]) + array([6.5, 2. , 2.5]) >>> np.median(a, axis=1) - array([ 7., 2.]) + array([nan, 2.]) >>> b = a.copy() >>> np.nanmedian(b, axis=1, overwrite_input=True) - array([ 7., 2.]) + array([7., 2.]) >>> assert not np.all(a==b) >>> b = a.copy() >>> np.nanmedian(b, axis=None, overwrite_input=True) @@ -1026,8 +1223,24 @@ def nanmedian(a, axis=None, out=None, overwrite_input=False, keepdims=np._NoValu return r -def nanpercentile(a, q, axis=None, out=None, overwrite_input=False, - interpolation='linear', keepdims=np._NoValue): +def _nanpercentile_dispatcher( + a, q, axis=None, out=None, overwrite_input=None, + method=None, keepdims=None, *, interpolation=None): + return (a, q, out) + + +@array_function_dispatch(_nanpercentile_dispatcher) +def nanpercentile( + a, + q, + axis=None, + out=None, + overwrite_input=False, + method="linear", + keepdims=np._NoValue, + *, + interpolation=None, +): """ Compute the qth percentile of the data along the specified axis, while ignoring nan values. @@ -1042,32 +1255,49 @@ def nanpercentile(a, q, axis=None, out=None, overwrite_input=False, Input array or object that can be converted to an array, containing nan values to be ignored. q : array_like of float - Percentile or sequence of percentiles to compute, which must be between - 0 and 100 inclusive. + Percentile or sequence of percentiles to compute, which must be + between 0 and 100 inclusive. axis : {int, tuple of int, None}, optional - Axis or axes along which the percentiles are computed. The - default is to compute the percentile(s) along a flattened - version of the array. + Axis or axes along which the percentiles are computed. The default + is to compute the percentile(s) along a flattened version of the + array. out : ndarray, optional - Alternative output array in which to place the result. It must - have the same shape and buffer length as the expected output, - but the type (of the output) will be cast if necessary. + Alternative output array in which to place the result. It must have + the same shape and buffer length as the expected output, but the + type (of the output) will be cast if necessary. overwrite_input : bool, optional - If True, then allow the input array `a` to be modified by intermediate - calculations, to save memory. In this case, the contents of the input - `a` after this function completes is undefined. - interpolation : {'linear', 'lower', 'higher', 'midpoint', 'nearest'} - This optional parameter specifies the interpolation method to - use when the desired percentile lies between two data points - ``i < j``: - - * 'linear': ``i + (j - i) * fraction``, where ``fraction`` - is the fractional part of the index surrounded by ``i`` - and ``j``. - * 'lower': ``i``. - * 'higher': ``j``. - * 'nearest': ``i`` or ``j``, whichever is nearest. - * 'midpoint': ``(i + j) / 2``. + If True, then allow the input array `a` to be modified by + intermediate calculations, to save memory. In this case, the + contents of the input `a` after this function completes is + undefined. + method : str, optional + This parameter specifies the method to use for estimating the + percentile. There are many different methods, some unique to NumPy. + See the notes for explanation. The options sorted by their R type + as summarized in the H&F paper [1]_ are: + + 1. 'inverted_cdf' + 2. 'averaged_inverted_cdf' + 3. 'closest_observation' + 4. 'interpolated_inverted_cdf' + 5. 'hazen' + 6. 'weibull' + 7. 'linear' (default) + 8. 'median_unbiased' + 9. 'normal_unbiased' + + The first three methods are discontiuous. NumPy further defines the + following discontinuous variations of the default 'linear' (7.) option: + + * 'lower' + * 'higher', + * 'midpoint' + * 'nearest' + + .. versionchanged:: 1.22.0 + This argument was previously called "interpolation" and only + offered the "linear" default and last four options. + keepdims : bool, optional If this is set to True, the axes which are reduced are left in the result as dimensions with size one. With this option, the @@ -1079,6 +1309,11 @@ def nanpercentile(a, q, axis=None, out=None, overwrite_input=False, a sub-class and `mean` does not have the kwarg `keepdims` this will raise a RuntimeError. + interpolation : str, optional + Deprecated name for the method keyword argument. + + .. deprecated:: 1.22.0 + Returns ------- percentile : scalar or ndarray @@ -1096,62 +1331,83 @@ def nanpercentile(a, q, axis=None, out=None, overwrite_input=False, nanmean nanmedian : equivalent to ``nanpercentile(..., 50)`` percentile, median, mean - nanquantile : equivalent to nanpercentile, but with q in the range [0, 1]. + nanquantile : equivalent to nanpercentile, except q in range [0, 1]. Notes ----- - Given a vector ``V`` of length ``N``, the ``q``-th percentile of - ``V`` is the value ``q/100`` of the way from the minimum to the - maximum in a sorted copy of ``V``. The values and distances of - the two nearest neighbors as well as the `interpolation` parameter - will determine the percentile if the normalized ranking does not - match the location of ``q`` exactly. This function is the same as - the median if ``q=50``, the same as the minimum if ``q=0`` and the - same as the maximum if ``q=100``. + For more information please see `numpy.percentile` Examples -------- >>> a = np.array([[10., 7., 4.], [3., 2., 1.]]) >>> a[0][1] = np.nan >>> a - array([[ 10., nan, 4.], - [ 3., 2., 1.]]) + array([[10., nan, 4.], + [ 3., 2., 1.]]) >>> np.percentile(a, 50) nan >>> np.nanpercentile(a, 50) - 3.5 + 3.0 >>> np.nanpercentile(a, 50, axis=0) - array([ 6.5, 2., 2.5]) + array([6.5, 2. , 2.5]) >>> np.nanpercentile(a, 50, axis=1, keepdims=True) - array([[ 7.], - [ 2.]]) + array([[7.], + [2.]]) >>> m = np.nanpercentile(a, 50, axis=0) >>> out = np.zeros_like(m) >>> np.nanpercentile(a, 50, axis=0, out=out) - array([ 6.5, 2., 2.5]) + array([6.5, 2. , 2.5]) >>> m - array([ 6.5, 2. , 2.5]) + array([6.5, 2. , 2.5]) >>> b = a.copy() >>> np.nanpercentile(b, 50, axis=1, overwrite_input=True) - array([ 7., 2.]) + array([7., 2.]) >>> assert not np.all(a==b) + References + ---------- + .. [1] R. J. Hyndman and Y. Fan, + "Sample quantiles in statistical packages," + The American Statistician, 50(4), pp. 361-365, 1996 + """ + if interpolation is not None: + method = function_base._check_interpolation_as_method( + method, interpolation, "nanpercentile") + a = np.asanyarray(a) - q = np.true_divide(q, 100.0) # handles the asarray for us too + q = np.true_divide(q, 100.0) + # undo any decay that the ufunc performed (see gh-13105) + q = np.asanyarray(q) if not function_base._quantile_is_valid(q): raise ValueError("Percentiles must be in the range [0, 100]") return _nanquantile_unchecked( - a, q, axis, out, overwrite_input, interpolation, keepdims) - - -def nanquantile(a, q, axis=None, out=None, overwrite_input=False, - interpolation='linear', keepdims=np._NoValue): + a, q, axis, out, overwrite_input, method, keepdims) + + +def _nanquantile_dispatcher(a, q, axis=None, out=None, overwrite_input=None, + method=None, keepdims=None, *, interpolation=None): + return (a, q, out) + + +@array_function_dispatch(_nanquantile_dispatcher) +def nanquantile( + a, + q, + axis=None, + out=None, + overwrite_input=False, + method="linear", + keepdims=np._NoValue, + *, + interpolation=None, +): """ Compute the qth quantile of the data along the specified axis, while ignoring nan values. Returns the qth quantile(s) of the array elements. + .. versionadded:: 1.15.0 Parameters @@ -1174,17 +1430,34 @@ def nanquantile(a, q, axis=None, out=None, overwrite_input=False, If True, then allow the input array `a` to be modified by intermediate calculations, to save memory. In this case, the contents of the input `a` after this function completes is undefined. - interpolation : {'linear', 'lower', 'higher', 'midpoint', 'nearest'} - This optional parameter specifies the interpolation method to - use when the desired quantile lies between two data points - ``i < j``: - * linear: ``i + (j - i) * fraction``, where ``fraction`` - is the fractional part of the index surrounded by ``i`` - and ``j``. - * lower: ``i``. - * higher: ``j``. - * nearest: ``i`` or ``j``, whichever is nearest. - * midpoint: ``(i + j) / 2``. + method : str, optional + This parameter specifies the method to use for estimating the + quantile. There are many different methods, some unique to NumPy. + See the notes for explanation. The options sorted by their R type + as summarized in the H&F paper [1]_ are: + + 1. 'inverted_cdf' + 2. 'averaged_inverted_cdf' + 3. 'closest_observation' + 4. 'interpolated_inverted_cdf' + 5. 'hazen' + 6. 'weibull' + 7. 'linear' (default) + 8. 'median_unbiased' + 9. 'normal_unbiased' + + The first three methods are discontiuous. NumPy further defines the + following discontinuous variations of the default 'linear' (7.) option: + + * 'lower' + * 'higher', + * 'midpoint' + * 'nearest' + + .. versionchanged:: 1.22.0 + This argument was previously called "interpolation" and only + offered the "linear" default and last four options. + keepdims : bool, optional If this is set to True, the axes which are reduced are left in the result as dimensions with size one. With this option, the @@ -1196,6 +1469,11 @@ def nanquantile(a, q, axis=None, out=None, overwrite_input=False, a sub-class and `mean` does not have the kwarg `keepdims` this will raise a RuntimeError. + interpolation : str, optional + Deprecated name for the method keyword argument. + + .. deprecated:: 1.22.0 + Returns ------- quantile : scalar or ndarray @@ -1215,53 +1493,77 @@ def nanquantile(a, q, axis=None, out=None, overwrite_input=False, nanmedian : equivalent to ``nanquantile(..., 0.5)`` nanpercentile : same as nanquantile, but with q in the range [0, 100]. + Notes + ----- + For more information please see `numpy.quantile` + Examples -------- >>> a = np.array([[10., 7., 4.], [3., 2., 1.]]) >>> a[0][1] = np.nan >>> a - array([[ 10., nan, 4.], - [ 3., 2., 1.]]) + array([[10., nan, 4.], + [ 3., 2., 1.]]) >>> np.quantile(a, 0.5) nan >>> np.nanquantile(a, 0.5) - 3.5 + 3.0 >>> np.nanquantile(a, 0.5, axis=0) - array([ 6.5, 2., 2.5]) + array([6.5, 2. , 2.5]) >>> np.nanquantile(a, 0.5, axis=1, keepdims=True) - array([[ 7.], - [ 2.]]) + array([[7.], + [2.]]) >>> m = np.nanquantile(a, 0.5, axis=0) >>> out = np.zeros_like(m) >>> np.nanquantile(a, 0.5, axis=0, out=out) - array([ 6.5, 2., 2.5]) + array([6.5, 2. , 2.5]) >>> m - array([ 6.5, 2. , 2.5]) + array([6.5, 2. , 2.5]) >>> b = a.copy() >>> np.nanquantile(b, 0.5, axis=1, overwrite_input=True) - array([ 7., 2.]) + array([7., 2.]) >>> assert not np.all(a==b) + + References + ---------- + .. [1] R. J. Hyndman and Y. Fan, + "Sample quantiles in statistical packages," + The American Statistician, 50(4), pp. 361-365, 1996 + """ + if interpolation is not None: + method = function_base._check_interpolation_as_method( + method, interpolation, "nanquantile") + a = np.asanyarray(a) q = np.asanyarray(q) if not function_base._quantile_is_valid(q): raise ValueError("Quantiles must be in the range [0, 1]") return _nanquantile_unchecked( - a, q, axis, out, overwrite_input, interpolation, keepdims) - - -def _nanquantile_unchecked(a, q, axis=None, out=None, overwrite_input=False, - interpolation='linear', keepdims=np._NoValue): + a, q, axis, out, overwrite_input, method, keepdims) + + +def _nanquantile_unchecked( + a, + q, + axis=None, + out=None, + overwrite_input=False, + method="linear", + keepdims=np._NoValue, +): """Assumes that q is in [0, 1], and is an ndarray""" # apply_along_axis in _nanpercentile doesn't handle empty arrays well, # so deal them upfront if a.size == 0: return np.nanmean(a, axis, out=out, keepdims=keepdims) - - r, k = function_base._ureduce( - a, func=_nanquantile_ureduce_func, q=q, axis=axis, out=out, - overwrite_input=overwrite_input, interpolation=interpolation - ) + r, k = function_base._ureduce(a, + func=_nanquantile_ureduce_func, + q=q, + axis=axis, + out=out, + overwrite_input=overwrite_input, + method=method) if keepdims and keepdims is not np._NoValue: return r.reshape(q.shape + k) else: @@ -1269,7 +1571,7 @@ def _nanquantile_unchecked(a, q, axis=None, out=None, overwrite_input=False, def _nanquantile_ureduce_func(a, q, axis=None, out=None, overwrite_input=False, - interpolation='linear'): + method="linear"): """ Private function that doesn't support extended axis or keepdims. These methods are extended to this function using _ureduce @@ -1277,10 +1579,10 @@ def _nanquantile_ureduce_func(a, q, axis=None, out=None, overwrite_input=False, """ if axis is None or a.ndim == 1: part = a.ravel() - result = _nanquantile_1d(part, q, overwrite_input, interpolation) + result = _nanquantile_1d(part, q, overwrite_input, method) else: result = np.apply_along_axis(_nanquantile_1d, axis, a, q, - overwrite_input, interpolation) + overwrite_input, method) # apply_along_axis fills in collapsed axis with results. # Move that axis to the beginning to match percentile's # convention. @@ -1292,7 +1594,7 @@ def _nanquantile_ureduce_func(a, q, axis=None, out=None, overwrite_input=False, return result -def _nanquantile_1d(arr1d, q, overwrite_input=False, interpolation='linear'): +def _nanquantile_1d(arr1d, q, overwrite_input=False, method="linear"): """ Private function for rank 1 arrays. Compute quantile ignoring NaNs. See nanpercentile for parameter usage @@ -1300,13 +1602,21 @@ def _nanquantile_1d(arr1d, q, overwrite_input=False, interpolation='linear'): arr1d, overwrite_input = _remove_nan_1d(arr1d, overwrite_input=overwrite_input) if arr1d.size == 0: - return np.full(q.shape, np.nan)[()] # convert to scalar + # convert to scalar + return np.full(q.shape, np.nan, dtype=arr1d.dtype)[()] return function_base._quantile_unchecked( - arr1d, q, overwrite_input=overwrite_input, interpolation=interpolation) + arr1d, q, overwrite_input=overwrite_input, method=method) -def nanvar(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): +def _nanvar_dispatcher(a, axis=None, dtype=None, out=None, ddof=None, + keepdims=None, *, where=None): + return (a, out) + + +@array_function_dispatch(_nanvar_dispatcher) +def nanvar(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue, + *, where=np._NoValue): """ Compute the variance along the specified axis, while ignoring NaNs. @@ -1329,7 +1639,7 @@ def nanvar(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): the variance of the flattened array. dtype : data-type, optional Type to use in computing the variance. For arrays of integer type - the default is `float32`; for arrays of float types it is the same as + the default is `float64`; for arrays of float types it is the same as the array type. out : ndarray, optional Alternate output array in which to place the result. It must have @@ -1343,7 +1653,11 @@ def nanvar(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): If this is set to True, the axes which are reduced are left in the result as dimensions with size one. With this option, the result will broadcast correctly against the original `a`. + where : array_like of bool, optional + Elements to include in the variance. See `~numpy.ufunc.reduce` for + details. + .. versionadded:: 1.22.0 Returns ------- @@ -1359,7 +1673,7 @@ def nanvar(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): mean : Average var : Variance while not ignoring NaNs nanstd, nanmean - numpy.doc.ufuncs : Section "Output arguments" + :ref:`ufuncs-output-type` Notes ----- @@ -1388,18 +1702,18 @@ def nanvar(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): Examples -------- >>> a = np.array([[1, np.nan], [3, 4]]) - >>> np.var(a) + >>> np.nanvar(a) 1.5555555555555554 >>> np.nanvar(a, axis=0) - array([ 1., 0.]) + array([1., 0.]) >>> np.nanvar(a, axis=1) - array([ 0., 0.25]) + array([0., 0.25]) # may vary """ arr, mask = _replace_nan(a, 0) if mask is None: return np.var(arr, axis=axis, dtype=dtype, out=out, ddof=ddof, - keepdims=keepdims) + keepdims=keepdims, where=where) if dtype is not None: dtype = np.dtype(dtype) @@ -1418,21 +1732,29 @@ def nanvar(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): # keepdims=True, however matrix now raises an error in this case, but # the reason that it drops the keepdims kwarg is to force keepdims=True # so this used to work by serendipity. - cnt = np.sum(~mask, axis=axis, dtype=np.intp, keepdims=_keepdims) - avg = np.sum(arr, axis=axis, dtype=dtype, keepdims=_keepdims) + cnt = np.sum(~mask, axis=axis, dtype=np.intp, keepdims=_keepdims, + where=where) + avg = np.sum(arr, axis=axis, dtype=dtype, keepdims=_keepdims, where=where) avg = _divide_by_count(avg, cnt) # Compute squared deviation from mean. - np.subtract(arr, avg, out=arr, casting='unsafe') + np.subtract(arr, avg, out=arr, casting='unsafe', where=where) arr = _copyto(arr, 0, mask) if issubclass(arr.dtype.type, np.complexfloating): - sqr = np.multiply(arr, arr.conj(), out=arr).real + sqr = np.multiply(arr, arr.conj(), out=arr, where=where).real else: - sqr = np.multiply(arr, arr, out=arr) + sqr = np.multiply(arr, arr, out=arr, where=where) # Compute variance. - var = np.sum(sqr, axis=axis, dtype=dtype, out=out, keepdims=keepdims) - if var.ndim < cnt.ndim: + var = np.sum(sqr, axis=axis, dtype=dtype, out=out, keepdims=keepdims, + where=where) + + # Precaution against reduced object arrays + try: + var_ndim = var.ndim + except AttributeError: + var_ndim = np.ndim(var) + if var_ndim < cnt.ndim: # Subclasses of ndarray may ignore keepdims, so check here. cnt = cnt.squeeze(axis) dof = cnt - ddof @@ -1440,14 +1762,22 @@ def nanvar(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): isbad = (dof <= 0) if np.any(isbad): - warnings.warn("Degrees of freedom <= 0 for slice.", RuntimeWarning, stacklevel=2) + warnings.warn("Degrees of freedom <= 0 for slice.", RuntimeWarning, + stacklevel=3) # NaN, inf, or negative numbers are all possible bad # values, so explicitly replace them with NaN. var = _copyto(var, np.nan, isbad) return var -def nanstd(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): +def _nanstd_dispatcher(a, axis=None, dtype=None, out=None, ddof=None, + keepdims=None, *, where=None): + return (a, out) + + +@array_function_dispatch(_nanstd_dispatcher) +def nanstd(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue, + *, where=np._NoValue): """ Compute the standard deviation along the specified axis, while ignoring NaNs. @@ -1491,6 +1821,11 @@ def nanstd(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): as-is to the relevant functions of the sub-classes. If these functions do not have a `keepdims` kwarg, a RuntimeError will be raised. + where : array_like of bool, optional + Elements to include in the standard deviation. + See `~numpy.ufunc.reduce` for details. + + .. versionadded:: 1.22.0 Returns ------- @@ -1504,7 +1839,7 @@ def nanstd(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): -------- var, mean, std nanvar, nanmean - numpy.doc.ufuncs : Section "Output arguments" + :ref:`ufuncs-output-type` Notes ----- @@ -1536,15 +1871,17 @@ def nanstd(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): >>> np.nanstd(a) 1.247219128924647 >>> np.nanstd(a, axis=0) - array([ 1., 0.]) + array([1., 0.]) >>> np.nanstd(a, axis=1) - array([ 0., 0.5]) + array([0., 0.5]) # may vary """ var = nanvar(a, axis=axis, dtype=dtype, out=out, ddof=ddof, - keepdims=keepdims) + keepdims=keepdims, where=where) if isinstance(var, np.ndarray): std = np.sqrt(var, out=var) - else: + elif hasattr(var, 'dtype'): std = var.dtype.type(np.sqrt(var)) + else: + std = np.sqrt(var) return std diff --git a/numpy/lib/nanfunctions.pyi b/numpy/lib/nanfunctions.pyi new file mode 100644 index 000000000000..54b4a7e260a7 --- /dev/null +++ b/numpy/lib/nanfunctions.pyi @@ -0,0 +1,40 @@ +from typing import List + +from numpy.core.fromnumeric import ( + amin, + amax, + argmin, + argmax, + sum, + prod, + cumsum, + cumprod, + mean, + var, + std +) + +from numpy.lib.function_base import ( + median, + percentile, + quantile, +) + +__all__: List[str] + +# NOTE: In reaility these functions are not aliases but distinct functions +# with identical signatures. +nanmin = amin +nanmax = amax +nanargmin = argmin +nanargmax = argmax +nansum = sum +nanprod = prod +nancumsum = cumsum +nancumprod = cumprod +nanmean = mean +nanvar = var +nanstd = std +nanmedian = median +nanpercentile = percentile +nanquantile = quantile diff --git a/numpy/lib/npyio.py b/numpy/lib/npyio.py index 390927601fc2..6c34e95fef9a 100644 --- a/numpy/lib/npyio.py +++ b/numpy/lib/npyio.py @@ -1,18 +1,19 @@ -from __future__ import division, absolute_import, print_function - -import io -import sys import os import re +import functools import itertools import warnings import weakref -from operator import itemgetter, index as opindex +import contextlib +from operator import itemgetter, index as opindex, methodcaller +from collections.abc import Mapping import numpy as np from . import format from ._datasource import DataSource +from numpy.core import overrides from numpy.core.multiarray import packbits, unpackbits +from numpy.core.overrides import set_array_function_like_doc, set_module from ._iotools import ( LineSplitter, NameValidator, StringConverter, ConverterError, ConverterLockError, ConversionWarning, _is_string_like, @@ -20,35 +21,23 @@ ) from numpy.compat import ( - asbytes, asstr, asunicode, asbytes_nested, bytes, basestring, unicode, - is_pathlib_path + asbytes, asstr, asunicode, os_fspath, os_PathLike, + pickle ) -if sys.version_info[0] >= 3: - import pickle - from collections.abc import Mapping -else: - import cPickle as pickle - from future_builtins import map - from collections import Mapping - - -def loads(*args, **kwargs): - # NumPy 1.15.0, 2017-12-10 - warnings.warn( - "np.loads is deprecated, use pickle.loads instead", - DeprecationWarning, stacklevel=2) - return pickle.loads(*args, **kwargs) - __all__ = [ - 'savetxt', 'loadtxt', 'genfromtxt', 'ndfromtxt', 'mafromtxt', - 'recfromtxt', 'recfromcsv', 'load', 'loads', 'save', 'savez', + 'savetxt', 'loadtxt', 'genfromtxt', + 'recfromtxt', 'recfromcsv', 'load', 'save', 'savez', 'savez_compressed', 'packbits', 'unpackbits', 'fromregex', 'DataSource' ] -class BagObj(object): +array_function_dispatch = functools.partial( + overrides.array_function_dispatch, module='numpy') + + +class BagObj: """ BagObj(obj) @@ -62,7 +51,7 @@ class BagObj(object): Examples -------- >>> from numpy.lib.npyio import BagObj as BO - >>> class BagDemo(object): + >>> class BagDemo: ... def __getitem__(self, key): # An instance of BagObj(BagDemo) ... # will call this method when any ... # attribute look-up is required @@ -86,7 +75,7 @@ def __getattribute__(self, key): try: return object.__getattribute__(self, '_obj')[key] except KeyError: - raise AttributeError(key) + raise AttributeError(key) from None def __dir__(self): """ @@ -105,8 +94,8 @@ def zipfile_factory(file, *args, **kwargs): pathlib.Path objects. `args` and `kwargs` are passed to the zipfile.ZipFile constructor. """ - if is_pathlib_path(file): - file = str(file) + if not hasattr(file, 'read'): + file = os_fspath(file) import zipfile kwargs['allowZip64'] = True return zipfile.ZipFile(file, *args, **kwargs) @@ -139,7 +128,11 @@ class NpzFile(Mapping): An object on which attribute can be performed as an alternative to getitem access on the `NpzFile` instance itself. allow_pickle : bool, optional - Allow loading pickled data. Default: True + Allow loading pickled data. Default: False + + .. versionchanged:: 1.16.3 + Made default False in response to CVE-2019-6446. + pickle_kwargs : dict, optional Additional keyword arguments to pass on to pickle.load. These are only useful when loading object arrays saved on @@ -161,21 +154,24 @@ class NpzFile(Mapping): >>> x = np.arange(10) >>> y = np.sin(x) >>> np.savez(outfile, x=x, y=y) - >>> outfile.seek(0) + >>> _ = outfile.seek(0) >>> npz = np.load(outfile) >>> isinstance(npz, np.lib.io.NpzFile) True - >>> npz.files - ['y', 'x'] + >>> sorted(npz.files) + ['x', 'y'] >>> npz['x'] # getitem access array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) >>> npz.f.x # attribute lookup array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) """ + # Make __exit__ safe if zipfile_factory raises an exception + zip = None + fid = None - def __init__(self, fid, own_fid=False, allow_pickle=True, + def __init__(self, fid, own_fid=False, allow_pickle=False, pickle_kwargs=None): # Import is postponed to here since zipfile depends on gzip, an # optional component of the so-called standard library. @@ -193,8 +189,6 @@ def __init__(self, fid, own_fid=False, allow_pickle=True, self.f = BagObj(self) if own_fid: self.fid = fid - else: - self.fid = None def __enter__(self): return self @@ -254,34 +248,39 @@ def __getitem__(self, key): else: raise KeyError("%s is not a file in the archive" % key) - - if sys.version_info.major == 3: - # deprecate the python 2 dict apis that we supported by accident in - # python 3. We forgot to implement itervalues() at all in earlier - # versions of numpy, so no need to deprecated it here. - - def iteritems(self): - # Numpy 1.15, 2018-02-20 - warnings.warn( - "NpzFile.iteritems is deprecated in python 3, to match the " - "removal of dict.itertems. Use .items() instead.", - DeprecationWarning, stacklevel=2) - return self.items() - - def iterkeys(self): - # Numpy 1.15, 2018-02-20 - warnings.warn( - "NpzFile.iterkeys is deprecated in python 3, to match the " - "removal of dict.iterkeys. Use .keys() instead.", - DeprecationWarning, stacklevel=2) - return self.keys() - - -def load(file, mmap_mode=None, allow_pickle=True, fix_imports=True, + # deprecate the python 2 dict apis that we supported by accident in + # python 3. We forgot to implement itervalues() at all in earlier + # versions of numpy, so no need to deprecated it here. + + def iteritems(self): + # Numpy 1.15, 2018-02-20 + warnings.warn( + "NpzFile.iteritems is deprecated in python 3, to match the " + "removal of dict.itertems. Use .items() instead.", + DeprecationWarning, stacklevel=2) + return self.items() + + def iterkeys(self): + # Numpy 1.15, 2018-02-20 + warnings.warn( + "NpzFile.iterkeys is deprecated in python 3, to match the " + "removal of dict.iterkeys. Use .keys() instead.", + DeprecationWarning, stacklevel=2) + return self.keys() + + +@set_module('numpy') +def load(file, mmap_mode=None, allow_pickle=False, fix_imports=True, encoding='ASCII'): """ Load arrays or pickled objects from ``.npy``, ``.npz`` or pickled files. + .. warning:: Loading files that contain object arrays uses the ``pickle`` + module, which is not secure against erroneous or maliciously + constructed data. Consider passing ``allow_pickle=False`` to + load data that is known not to contain object arrays for the + safer handling of untrusted sources. + Parameters ---------- file : file-like object, string, or pathlib.Path @@ -299,8 +298,11 @@ def load(file, mmap_mode=None, allow_pickle=True, fix_imports=True, Allow loading pickled object arrays stored in npy files. Reasons for disallowing pickles include security, as loading pickled data can execute arbitrary code. If pickles are disallowed, loading object - arrays will fail. - Default: True + arrays will fail. Default: False + + .. versionchanged:: 1.16.3 + Made default False in response to CVE-2019-6446. + fix_imports : bool, optional Only useful when loading Python 2 generated pickled files on Python 3, which includes npy/npz files containing object arrays. If `fix_imports` @@ -321,10 +323,12 @@ def load(file, mmap_mode=None, allow_pickle=True, fix_imports=True, Raises ------ - IOError + OSError If the input file does not exist or cannot be read. + UnpicklingError + If ``allow_pickle=True``, but the file cannot be loaded as a pickle. ValueError - The file contains an object array, but allow_pickle=False given. + The file contains an object array, but ``allow_pickle=False`` given. See Also -------- @@ -379,16 +383,6 @@ def load(file, mmap_mode=None, allow_pickle=True, fix_imports=True, memmap([4, 5, 6]) """ - own_fid = False - if isinstance(file, basestring): - fid = open(file, "rb") - own_fid = True - elif is_pathlib_path(file): - fid = file.open("rb") - own_fid = True - else: - fid = file - if encoding not in ('ASCII', 'latin1', 'bytes'): # The 'encoding' value for pickle also affects what encoding # the serialized binary data of NumPy arrays is loaded @@ -403,27 +397,31 @@ def load(file, mmap_mode=None, allow_pickle=True, fix_imports=True, # result can similarly silently corrupt numerical data. raise ValueError("encoding must be 'ASCII', 'latin1', or 'bytes'") - if sys.version_info[0] >= 3: - pickle_kwargs = dict(encoding=encoding, fix_imports=fix_imports) - else: - # Nothing to do on Python 2 - pickle_kwargs = {} + pickle_kwargs = dict(encoding=encoding, fix_imports=fix_imports) + + with contextlib.ExitStack() as stack: + if hasattr(file, 'read'): + fid = file + own_fid = False + else: + fid = stack.enter_context(open(os_fspath(file), "rb")) + own_fid = True - try: # Code to distinguish from NumPy binary files and pickles. _ZIP_PREFIX = b'PK\x03\x04' + _ZIP_SUFFIX = b'PK\x05\x06' # empty zip files start with this N = len(format.MAGIC_PREFIX) magic = fid.read(N) # If the file size is less than N, we need to make sure not # to seek past the beginning of the file fid.seek(-min(N, len(magic)), 1) # back-up - if magic.startswith(_ZIP_PREFIX): + if magic.startswith(_ZIP_PREFIX) or magic.startswith(_ZIP_SUFFIX): # zip-file (assume .npz) - # Transfer file ownership to NpzFile - tmp = own_fid - own_fid = False - return NpzFile(fid, own_fid=tmp, allow_pickle=allow_pickle, - pickle_kwargs=pickle_kwargs) + # Potentially transfer file ownership to NpzFile + stack.pop_all() + ret = NpzFile(fid, own_fid=own_fid, allow_pickle=allow_pickle, + pickle_kwargs=pickle_kwargs) + return ret elif magic == format.MAGIC_PREFIX: # .npy file if mmap_mode: @@ -434,18 +432,20 @@ def load(file, mmap_mode=None, allow_pickle=True, fix_imports=True, else: # Try a pickle if not allow_pickle: - raise ValueError("allow_pickle=False, but file does not contain " - "non-pickled data") + raise ValueError("Cannot load file containing pickled data " + "when allow_pickle=False") try: return pickle.load(fid, **pickle_kwargs) - except Exception: - raise IOError( - "Failed to interpret file %s as a pickle" % repr(file)) - finally: - if own_fid: - fid.close() + except Exception as e: + raise pickle.UnpicklingError( + f"Failed to interpret file {file!r} as a pickle") from e + +def _save_dispatcher(file, arr, allow_pickle=None, fix_imports=None): + return (arr,) + +@array_function_dispatch(_save_dispatcher) def save(file, arr, allow_pickle=True, fix_imports=True): """ Save an array to a binary file in NumPy ``.npy`` format. @@ -455,7 +455,7 @@ def save(file, arr, allow_pickle=True, fix_imports=True): file : file, str, or pathlib.Path File or filename to which the data is saved. If file is a file-object, then the filename is unchanged. If file is a string or Path, a ``.npy`` - extension will be appended to the file name if it does not already + extension will be appended to the filename if it does not already have one. arr : array_like Array data to be saved. @@ -482,6 +482,8 @@ def save(file, arr, allow_pickle=True, fix_imports=True): ----- For a description of the ``.npy`` format, see :py:mod:`numpy.lib.format`. + Any data saved to the file is appended to the end of the file. + Examples -------- >>> from tempfile import TemporaryFile @@ -490,64 +492,63 @@ def save(file, arr, allow_pickle=True, fix_imports=True): >>> x = np.arange(10) >>> np.save(outfile, x) - >>> outfile.seek(0) # Only needed here to simulate closing & reopening file + >>> _ = outfile.seek(0) # Only needed here to simulate closing & reopening file >>> np.load(outfile) array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) + + >>> with open('test.npy', 'wb') as f: + ... np.save(f, np.array([1, 2])) + ... np.save(f, np.array([1, 3])) + >>> with open('test.npy', 'rb') as f: + ... a = np.load(f) + ... b = np.load(f) + >>> print(a, b) + # [1 2] [1 3] """ - own_fid = False - if isinstance(file, basestring): + if hasattr(file, 'write'): + file_ctx = contextlib.nullcontext(file) + else: + file = os_fspath(file) if not file.endswith('.npy'): file = file + '.npy' - fid = open(file, "wb") - own_fid = True - elif is_pathlib_path(file): - if not file.name.endswith('.npy'): - file = file.parent / (file.name + '.npy') - fid = file.open("wb") - own_fid = True - else: - fid = file + file_ctx = open(file, "wb") - if sys.version_info[0] >= 3: - pickle_kwargs = dict(fix_imports=fix_imports) - else: - # Nothing to do on Python 2 - pickle_kwargs = None - - try: + with file_ctx as fid: arr = np.asanyarray(arr) format.write_array(fid, arr, allow_pickle=allow_pickle, - pickle_kwargs=pickle_kwargs) - finally: - if own_fid: - fid.close() + pickle_kwargs=dict(fix_imports=fix_imports)) +def _savez_dispatcher(file, *args, **kwds): + yield from args + yield from kwds.values() + + +@array_function_dispatch(_savez_dispatcher) def savez(file, *args, **kwds): - """ - Save several arrays into a single file in uncompressed ``.npz`` format. + """Save several arrays into a single file in uncompressed ``.npz`` format. - If arguments are passed in with no keywords, the corresponding variable - names, in the ``.npz`` file, are 'arr_0', 'arr_1', etc. If keyword - arguments are given, the corresponding variable names, in the ``.npz`` - file will match the keyword names. + Provide arrays as keyword arguments to store them under the + corresponding name in the output file: ``savez(fn, x=x, y=y)``. + + If arrays are specified as positional arguments, i.e., ``savez(fn, + x, y)``, their names will be `arr_0`, `arr_1`, etc. Parameters ---------- file : str or file - Either the file name (string) or an open file (file-like object) + Either the filename (string) or an open file (file-like object) where the data will be saved. If file is a string or a Path, the - ``.npz`` extension will be appended to the file name if it is not + ``.npz`` extension will be appended to the filename if it is not already there. args : Arguments, optional - Arrays to save to the file. Since it is not possible for Python to - know the names of the arrays outside `savez`, the arrays will be saved - with names "arr_0", "arr_1", and so on. These arguments can be any - expression. + Arrays to save to the file. Please use keyword arguments (see + `kwds` below) to assign names to arrays. Arrays specified as + args will be named "arr_0", "arr_1", and so on. kwds : Keyword arguments, optional - Arrays to save to the file. Arrays will be saved in the file with the - keyword names. + Arrays to save to the file. Each array will be saved to the + output file with its corresponding keyword name. Returns ------- @@ -571,6 +572,14 @@ def savez(file, *args, **kwds): its list of arrays (with the ``.files`` attribute), and for the arrays themselves. + Keys passed in `kwds` are used as filenames inside the ZIP archive. + Therefore, keys should be valid filenames; e.g., avoid keys that begin with + ``/`` or contain ``.``. + + When naming variables with keyword arguments, it is not possible to name a + variable ``file``, as this would cause the ``file`` argument to be defined + twice in the call to ``savez``. + Examples -------- >>> from tempfile import TemporaryFile @@ -581,10 +590,10 @@ def savez(file, *args, **kwds): Using `savez` with \\*args, the arrays are saved with default names. >>> np.savez(outfile, x, y) - >>> outfile.seek(0) # Only needed here to simulate closing & reopening file + >>> _ = outfile.seek(0) # Only needed here to simulate closing & reopening file >>> npzfile = np.load(outfile) >>> npzfile.files - ['arr_1', 'arr_0'] + ['arr_0', 'arr_1'] >>> npzfile['arr_0'] array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) @@ -592,10 +601,10 @@ def savez(file, *args, **kwds): >>> outfile = TemporaryFile() >>> np.savez(outfile, x=x, y=y) - >>> outfile.seek(0) + >>> _ = outfile.seek(0) >>> npzfile = np.load(outfile) - >>> npzfile.files - ['y', 'x'] + >>> sorted(npzfile.files) + ['x', 'y'] >>> npzfile['x'] array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) @@ -603,29 +612,36 @@ def savez(file, *args, **kwds): _savez(file, args, kwds, False) +def _savez_compressed_dispatcher(file, *args, **kwds): + yield from args + yield from kwds.values() + + +@array_function_dispatch(_savez_compressed_dispatcher) def savez_compressed(file, *args, **kwds): """ Save several arrays into a single file in compressed ``.npz`` format. - If keyword arguments are given, then filenames are taken from the keywords. - If arguments are passed in with no keywords, then stored file names are - arr_0, arr_1, etc. + Provide arrays as keyword arguments to store them under the + corresponding name in the output file: ``savez(fn, x=x, y=y)``. + + If arrays are specified as positional arguments, i.e., ``savez(fn, + x, y)``, their names will be `arr_0`, `arr_1`, etc. Parameters ---------- file : str or file - Either the file name (string) or an open file (file-like object) + Either the filename (string) or an open file (file-like object) where the data will be saved. If file is a string or a Path, the - ``.npz`` extension will be appended to the file name if it is not + ``.npz`` extension will be appended to the filename if it is not already there. args : Arguments, optional - Arrays to save to the file. Since it is not possible for Python to - know the names of the arrays outside `savez`, the arrays will be saved - with names "arr_0", "arr_1", and so on. These arguments can be any - expression. + Arrays to save to the file. Please use keyword arguments (see + `kwds` below) to assign names to arrays. Arrays specified as + args will be named "arr_0", "arr_1", and so on. kwds : Keyword arguments, optional - Arrays to save to the file. Arrays will be saved in the file with the - keyword names. + Arrays to save to the file. Each array will be saved to the + output file with its corresponding keyword name. Returns ------- @@ -643,7 +659,7 @@ def savez_compressed(file, *args, **kwds): The ``.npz`` file format is a zipped archive of files named after the variables they contain. The archive is compressed with ``zipfile.ZIP_DEFLATED`` and each file in the archive contains one variable - in ``.npy`` format. For a description of the ``.npy`` format, see + in ``.npy`` format. For a description of the ``.npy`` format, see :py:mod:`numpy.lib.format`. @@ -672,12 +688,10 @@ def _savez(file, args, kwds, compress, allow_pickle=True, pickle_kwargs=None): # component of the so-called standard library. import zipfile - if isinstance(file, basestring): + if not hasattr(file, 'write'): + file = os_fspath(file) if not file.endswith('.npz'): file = file + '.npz' - elif is_pathlib_path(file): - if not file.name.endswith('.npz'): - file = file.parent / (file.name + '.npz') namedict = kwds for i, val in enumerate(args): @@ -694,96 +708,141 @@ def _savez(file, args, kwds, compress, allow_pickle=True, pickle_kwargs=None): zipf = zipfile_factory(file, mode="w", compression=compression) - if sys.version_info >= (3, 6): - # Since Python 3.6 it is possible to write directly to a ZIP file. - for key, val in namedict.items(): - fname = key + '.npy' - val = np.asanyarray(val) - force_zip64 = val.nbytes >= 2**30 - with zipf.open(fname, 'w', force_zip64=force_zip64) as fid: - format.write_array(fid, val, - allow_pickle=allow_pickle, - pickle_kwargs=pickle_kwargs) - else: - # Stage arrays in a temporary file on disk, before writing to zip. - - # Import deferred for startup time improvement - import tempfile - # Since target file might be big enough to exceed capacity of a global - # temporary directory, create temp file side-by-side with the target file. - file_dir, file_prefix = os.path.split(file) if _is_string_like(file) else (None, 'tmp') - fd, tmpfile = tempfile.mkstemp(prefix=file_prefix, dir=file_dir, suffix='-numpy.npy') - os.close(fd) - try: - for key, val in namedict.items(): - fname = key + '.npy' - fid = open(tmpfile, 'wb') - try: - format.write_array(fid, np.asanyarray(val), - allow_pickle=allow_pickle, - pickle_kwargs=pickle_kwargs) - fid.close() - fid = None - zipf.write(tmpfile, arcname=fname) - except IOError as exc: - raise IOError("Failed to write to %s: %s" % (tmpfile, exc)) - finally: - if fid: - fid.close() - finally: - os.remove(tmpfile) + for key, val in namedict.items(): + fname = key + '.npy' + val = np.asanyarray(val) + # always force zip64, gh-10776 + with zipf.open(fname, 'w', force_zip64=True) as fid: + format.write_array(fid, val, + allow_pickle=allow_pickle, + pickle_kwargs=pickle_kwargs) zipf.close() +def _floatconv(x): + try: + return float(x) # The fastest path. + except ValueError: + if '0x' in x: # Don't accidentally convert "a" ("0xa") to 10. + try: + return float.fromhex(x) + except ValueError: + pass + raise # Raise the original exception, which makes more sense. + + +_CONVERTERS = [ # These converters only ever get strs (not bytes) as input. + (np.bool_, lambda x: bool(int(x))), + (np.uint64, np.uint64), + (np.int64, np.int64), + (np.integer, lambda x: int(float(x))), + (np.longdouble, np.longdouble), + (np.floating, _floatconv), + (complex, lambda x: complex(x.replace('+-', '-'))), + (np.bytes_, methodcaller('encode', 'latin-1')), + (np.unicode_, str), +] + + def _getconv(dtype): - """ Find the correct dtype converter. Adapted from matplotlib """ - - def floatconv(x): - x.lower() - if '0x' in x: - return float.fromhex(x) - return float(x) - - typ = dtype.type - if issubclass(typ, np.bool_): - return lambda x: bool(int(x)) - if issubclass(typ, np.uint64): - return np.uint64 - if issubclass(typ, np.int64): - return np.int64 - if issubclass(typ, np.integer): - return lambda x: int(float(x)) - elif issubclass(typ, np.longdouble): - return np.longdouble - elif issubclass(typ, np.floating): - return floatconv - elif issubclass(typ, complex): - return lambda x: complex(asstr(x).replace('+-', '-')) - elif issubclass(typ, np.bytes_): - return asbytes - elif issubclass(typ, np.unicode_): - return asunicode + """ + Find the correct dtype converter. Adapted from matplotlib. + + Even when a lambda is returned, it is defined at the toplevel, to allow + testing for equality and enabling optimization for single-type data. + """ + for base, conv in _CONVERTERS: + if issubclass(dtype.type, base): + return conv + return str + + +# _loadtxt_flatten_dtype_internal and _loadtxt_pack_items are loadtxt helpers +# lifted to the toplevel because recursive inner functions cause either +# GC-dependent reference loops (because they are closures over loadtxt's +# internal variables) or large overheads if using a manual trampoline to hide +# the recursive calls. + + +# not to be confused with the flatten_dtype we import... +def _loadtxt_flatten_dtype_internal(dt): + """Unpack a structured data-type, and produce a packer function.""" + if dt.names is None: + # If the dtype is flattened, return. + # If the dtype has a shape, the dtype occurs + # in the list more than once. + shape = dt.shape + if len(shape) == 0: + return ([dt.base], None) + else: + packing = [(shape[-1], list)] + if len(shape) > 1: + for dim in dt.shape[-2::-1]: + packing = [(dim*packing[0][0], packing*dim)] + return ([dt.base] * int(np.prod(dt.shape)), + functools.partial(_loadtxt_pack_items, packing)) else: - return asstr + types = [] + packing = [] + for field in dt.names: + tp, bytes = dt.fields[field] + flat_dt, flat_packer = _loadtxt_flatten_dtype_internal(tp) + types.extend(flat_dt) + flat_packing = flat_packer.args[0] if flat_packer else None + # Avoid extra nesting for subarrays + if tp.ndim > 0: + packing.extend(flat_packing) + else: + packing.append((len(flat_dt), flat_packing)) + return (types, functools.partial(_loadtxt_pack_items, packing)) + + +def _loadtxt_pack_items(packing, items): + """Pack items into nested lists based on re-packing info.""" + if packing is None: + return items[0] + elif packing is tuple: + return tuple(items) + elif packing is list: + return list(items) + else: + start = 0 + ret = [] + for length, subpacking in packing: + ret.append( + _loadtxt_pack_items(subpacking, items[start:start+length])) + start += length + return tuple(ret) + # amount of lines loadtxt reads in one chunk, can be overridden for testing _loadtxt_chunksize = 50000 + +def _loadtxt_dispatcher(fname, dtype=None, comments=None, delimiter=None, + converters=None, skiprows=None, usecols=None, unpack=None, + ndmin=None, encoding=None, max_rows=None, *, like=None): + return (like,) + + +@set_array_function_like_doc +@set_module('numpy') def loadtxt(fname, dtype=float, comments='#', delimiter=None, converters=None, skiprows=0, usecols=None, unpack=False, - ndmin=0, encoding='bytes'): - """ + ndmin=0, encoding='bytes', max_rows=None, *, like=None): + r""" Load data from a text file. Each row in the text file must have the same number of values. Parameters ---------- - fname : file, str, or pathlib.Path - File, filename, or generator to read. If the filename extension is - ``.gz`` or ``.bz2``, the file is first decompressed. Note that - generators should return byte strings for Python 3k. + fname : file, str, pathlib.Path, list of str, generator + File, filename, list, or generator to read. If the filename + extension is ``.gz`` or ``.bz2``, the file is first decompressed. Note + that generators must return bytes or strings. The strings + in a list or produced by a generator are treated as lines. dtype : data-type, optional Data-type of the resulting array; default: float. If this is a structured data-type, the resulting array will be 1-dimensional, and @@ -805,7 +864,7 @@ def loadtxt(fname, dtype=float, comments='#', delimiter=None, `genfromtxt`): ``converters = {3: lambda s: float(s.strip() or 0)}``. Default: None. skiprows : int, optional - Skip the first `skiprows` lines; default: 0. + Skip the first `skiprows` lines, including comments; default: 0. usecols : int or sequence, optional Which columns to read, with 0 being the first. For example, ``usecols = (1,4,5)`` will extract the 2nd, 5th and 6th columns. @@ -817,8 +876,9 @@ def loadtxt(fname, dtype=float, comments='#', delimiter=None, fourth column the same way as ``usecols = (3,)`` would. unpack : bool, optional If True, the returned array is transposed, so that arguments may be - unpacked using ``x, y, z = loadtxt(...)``. When used with a structured - data-type, arrays are returned for each field. Default is False. + unpacked using ``x, y, z = loadtxt(...)``. When used with a + structured data-type, arrays are returned for each field. + Default is False. ndmin : int, optional The returned array will have at least `ndmin` dimensions. Otherwise mono-dimensional axes will be squeezed. @@ -834,6 +894,14 @@ def loadtxt(fname, dtype=float, comments='#', delimiter=None, the system default is used. The default value is 'bytes'. .. versionadded:: 1.14.0 + max_rows : int, optional + Read `max_rows` lines of content after `skiprows` lines. The default + is to read all the lines. + + .. versionadded:: 1.16.0 + ${ARRAY_FUNCTION_LIKE} + + .. versionadded:: 1.20.0 Returns ------- @@ -860,54 +928,91 @@ def loadtxt(fname, dtype=float, comments='#', delimiter=None, Examples -------- >>> from io import StringIO # StringIO behaves like a file object - >>> c = StringIO(u"0 1\\n2 3") + >>> c = StringIO("0 1\n2 3") >>> np.loadtxt(c) - array([[ 0., 1.], - [ 2., 3.]]) + array([[0., 1.], + [2., 3.]]) - >>> d = StringIO(u"M 21 72\\nF 35 58") + >>> d = StringIO("M 21 72\nF 35 58") >>> np.loadtxt(d, dtype={'names': ('gender', 'age', 'weight'), ... 'formats': ('S1', 'i4', 'f4')}) - array([('M', 21, 72.0), ('F', 35, 58.0)], - dtype=[('gender', '|S1'), ('age', '<i4'), ('weight', '<f4')]) + array([(b'M', 21, 72.), (b'F', 35, 58.)], + dtype=[('gender', 'S1'), ('age', '<i4'), ('weight', '<f4')]) - >>> c = StringIO(u"1,0,2\\n3,0,4") + >>> c = StringIO("1,0,2\n3,0,4") >>> x, y = np.loadtxt(c, delimiter=',', usecols=(0, 2), unpack=True) >>> x - array([ 1., 3.]) + array([1., 3.]) >>> y - array([ 2., 4.]) + array([2., 4.]) + This example shows how `converters` can be used to convert a field + with a trailing minus sign into a negative number. + + >>> s = StringIO('10.01 31.25-\n19.22 64.31\n17.57- 63.94') + >>> def conv(fld): + ... return -float(fld[:-1]) if fld.endswith(b'-') else float(fld) + ... + >>> np.loadtxt(s, converters={0: conv, 1: conv}) + array([[ 10.01, -31.25], + [ 19.22, 64.31], + [-17.57, 63.94]]) """ + + if like is not None: + return _loadtxt_with_like( + fname, dtype=dtype, comments=comments, delimiter=delimiter, + converters=converters, skiprows=skiprows, usecols=usecols, + unpack=unpack, ndmin=ndmin, encoding=encoding, + max_rows=max_rows, like=like + ) + + # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # Nested functions used by loadtxt. + # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + def split_line(line: str): + """Chop off comments, strip, and split at delimiter.""" + for comment in comments: # Much faster than using a single regex. + line = line.split(comment, 1)[0] + line = line.strip('\r\n') + return line.split(delimiter) if line else [] + + # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # Main body of loadtxt. + # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + # Check correctness of the values of `ndmin` + if ndmin not in [0, 1, 2]: + raise ValueError('Illegal value of ndmin keyword: %s' % ndmin) + # Type conversions for Py3 convenience if comments is not None: - if isinstance(comments, (basestring, bytes)): + if isinstance(comments, (str, bytes)): comments = [comments] comments = [_decode_line(x) for x in comments] - # Compile regex for comments beforehand - comments = (re.escape(comment) for comment in comments) - regex_comments = re.compile('|'.join(comments)) + else: + comments = [] if delimiter is not None: delimiter = _decode_line(delimiter) user_converters = converters + byte_converters = False if encoding == 'bytes': encoding = None byte_converters = True - else: - byte_converters = False if usecols is not None: - # Allow usecols to be a single int or a sequence of ints + # Copy usecols, allowing it to be a single int or a sequence of ints. try: - usecols_as_list = list(usecols) + usecols = list(usecols) except TypeError: - usecols_as_list = [usecols] - for col_idx in usecols_as_list: + usecols = [usecols] + for i, col_idx in enumerate(usecols): try: - opindex(col_idx) + usecols[i] = opindex(col_idx) # Cast to builtin int now. except TypeError as e: e.args = ( "usecols must be an int or a sequence of ints but " @@ -915,160 +1020,98 @@ def loadtxt(fname, dtype=float, comments='#', delimiter=None, type(col_idx), ) raise - # Fall back to existing code - usecols = usecols_as_list + if len(usecols) > 1: + usecols_getter = itemgetter(*usecols) + else: + # Get an iterable back, even if using a single column. + usecols_getter = lambda obj, c=usecols[0]: [obj[c]] + else: + usecols_getter = None - fown = False + # Make sure we're dealing with a proper dtype + dtype = np.dtype(dtype) + defconv = _getconv(dtype) + + dtype_types, packer = _loadtxt_flatten_dtype_internal(dtype) + + fh_closing_ctx = contextlib.nullcontext() try: - if is_pathlib_path(fname): - fname = str(fname) + if isinstance(fname, os_PathLike): + fname = os_fspath(fname) if _is_string_like(fname): fh = np.lib._datasource.open(fname, 'rt', encoding=encoding) fencoding = getattr(fh, 'encoding', 'latin1') - fh = iter(fh) - fown = True + line_iter = iter(fh) + fh_closing_ctx = contextlib.closing(fh) else: - fh = iter(fname) + line_iter = iter(fname) fencoding = getattr(fname, 'encoding', 'latin1') - except TypeError: - raise ValueError('fname must be a string, file handle, or generator') - - # input may be a python2 io stream - if encoding is not None: - fencoding = encoding - # we must assume local encoding - # TODO emit portability warning? - elif fencoding is None: - import locale - fencoding = locale.getpreferredencoding() - - # not to be confused with the flatten_dtype we import... - def flatten_dtype_internal(dt): - """Unpack a structured data-type, and produce re-packing info.""" - if dt.names is None: - # If the dtype is flattened, return. - # If the dtype has a shape, the dtype occurs - # in the list more than once. - shape = dt.shape - if len(shape) == 0: - return ([dt.base], None) + try: + first_line = next(line_iter) + except StopIteration: + pass # Nothing matters if line_iter is empty. else: - packing = [(shape[-1], list)] - if len(shape) > 1: - for dim in dt.shape[-2::-1]: - packing = [(dim*packing[0][0], packing*dim)] - return ([dt.base] * int(np.prod(dt.shape)), packing) - else: - types = [] - packing = [] - for field in dt.names: - tp, bytes = dt.fields[field] - flat_dt, flat_packing = flatten_dtype_internal(tp) - types.extend(flat_dt) - # Avoid extra nesting for subarrays - if tp.ndim > 0: - packing.extend(flat_packing) - else: - packing.append((len(flat_dt), flat_packing)) - return (types, packing) - - def pack_items(items, packing): - """Pack items into nested lists based on re-packing info.""" - if packing is None: - return items[0] - elif packing is tuple: - return tuple(items) - elif packing is list: - return list(items) - else: - start = 0 - ret = [] - for length, subpacking in packing: - ret.append(pack_items(items[start:start+length], subpacking)) - start += length - return tuple(ret) - - def split_line(line): - """Chop off comments, strip, and split at delimiter. """ - line = _decode_line(line, encoding=encoding) - - if comments is not None: - line = regex_comments.split(line, maxsplit=1)[0] - line = line.strip('\r\n') - if line: - return line.split(delimiter) - else: - return [] - - def read_data(chunk_size): - """Parse each line, including the first. - - The file read, `fh`, is a global defined above. - - Parameters - ---------- - chunk_size : int - At most `chunk_size` lines are read at a time, with iteration - until all lines are read. - - """ - X = [] - for i, line in enumerate(itertools.chain([first_line], fh)): - vals = split_line(line) - if len(vals) == 0: - continue - if usecols: - vals = [vals[j] for j in usecols] - if len(vals) != N: - line_num = i + skiprows + 1 - raise ValueError("Wrong number of columns at line %d" - % line_num) - - # Convert each value according to its column and store - items = [conv(val) for (conv, val) in zip(converters, vals)] - - # Then pack it according to the dtype's nesting - items = pack_items(items, packing) - X.append(items) - if len(X) > chunk_size: - yield X - X = [] - if X: - yield X - - try: - # Make sure we're dealing with a proper dtype - dtype = np.dtype(dtype) - defconv = _getconv(dtype) + # Put first_line back. + line_iter = itertools.chain([first_line], line_iter) + if isinstance(first_line, bytes): + # Using latin1 matches _decode_line's behavior. + decoder = methodcaller( + "decode", + encoding if encoding is not None else "latin1") + line_iter = map(decoder, line_iter) + except TypeError as e: + raise ValueError( + f"fname must be a string, filehandle, list of strings,\n" + f"or generator. Got {type(fname)} instead." + ) from e + + with fh_closing_ctx: + + # input may be a python2 io stream + if encoding is not None: + fencoding = encoding + # we must assume local encoding + # TODO emit portability warning? + elif fencoding is None: + import locale + fencoding = locale.getpreferredencoding() # Skip the first `skiprows` lines for i in range(skiprows): - next(fh) - - # Read until we find a line with some values, and use - # it to estimate the number of columns, N. - first_vals = None - try: - while not first_vals: - first_line = next(fh) - first_vals = split_line(first_line) - except StopIteration: - # End of lines reached - first_line = '' - first_vals = [] - warnings.warn('loadtxt: Empty input file: "%s"' % fname, stacklevel=2) - N = len(usecols or first_vals) - - dtype_types, packing = flatten_dtype_internal(dtype) + next(line_iter) + + # Read until we find a line with some values, and use it to determine + # the need for decoding and estimate the number of columns. + for first_line in line_iter: + ncols = len(usecols or split_line(first_line)) + if ncols: + # Put first_line back. + line_iter = itertools.chain([first_line], line_iter) + break + else: # End of lines reached + ncols = len(usecols or []) + warnings.warn('loadtxt: Empty input file: "%s"' % fname, + stacklevel=2) + + line_iter = itertools.islice(line_iter, max_rows) + lineno_words_iter = filter( + itemgetter(1), # item[1] is words; filter skips empty lines. + enumerate(map(split_line, line_iter), 1 + skiprows)) + + # Now that we know ncols, create the default converters list, and + # set packing, if necessary. if len(dtype_types) > 1: # We're dealing with a structured array, each field of # the dtype matches a column converters = [_getconv(dt) for dt in dtype_types] else: - # All fields have the same dtype - converters = [defconv for i in range(N)] - if N > 1: - packing = [(N, tuple)] + # All fields have the same dtype; use specialized packers which are + # much faster than those using _loadtxt_pack_items. + converters = [defconv for i in range(ncols)] + if ncols == 1: + packer = itemgetter(0) + else: + def packer(row): return row # By preference, use the converters specified by the user for i, conv in (user_converters or {}).items(): @@ -1079,42 +1122,56 @@ def read_data(chunk_size): # Unused converter specified continue if byte_converters: - # converters may use decode to workaround numpy's old behaviour, - # so encode the string again before passing to the user converter - def tobytes_first(x, conv): - if type(x) is bytes: - return conv(x) + # converters may use decode to workaround numpy's old + # behaviour, so encode the string again (converters are only + # called with strings) before passing to the user converter. + def tobytes_first(conv, x): return conv(x.encode("latin1")) - import functools - converters[i] = functools.partial(tobytes_first, conv=conv) + converters[i] = functools.partial(tobytes_first, conv) else: converters[i] = conv - converters = [conv if conv is not bytes else - lambda x: x.encode(fencoding) for conv in converters] + fencode = methodcaller("encode", fencoding) + converters = [conv if conv is not bytes else fencode + for conv in converters] + if len(set(converters)) == 1: + # Optimize single-type data. Note that this is only reached if + # `_getconv` returns equal callables (i.e. not local lambdas) on + # equal dtypes. + def convert_row(vals, _conv=converters[0]): + return [*map(_conv, vals)] + else: + def convert_row(vals): + return [conv(val) for conv, val in zip(converters, vals)] # read data in chunks and fill it into an array via resize # over-allocating and shrinking the array later may be faster but is # probably not relevant compared to the cost of actually reading and # converting the data X = None - for x in read_data(_loadtxt_chunksize): + while True: + chunk = [] + for lineno, words in itertools.islice( + lineno_words_iter, _loadtxt_chunksize): + if usecols_getter is not None: + words = usecols_getter(words) + elif len(words) != ncols: + raise ValueError( + f"Wrong number of columns at line {lineno}") + # Convert each value according to its column, then pack it + # according to the dtype's nesting, and store it. + chunk.append(packer(convert_row(words))) + if not chunk: # The islice is empty, i.e. we're done. + break + if X is None: - X = np.array(x, dtype) + X = np.array(chunk, dtype) else: nshape = list(X.shape) pos = nshape[0] - nshape[0] += len(x) + nshape[0] += len(chunk) X.resize(nshape, refcheck=False) - X[pos:, ...] = x - finally: - if fown: - fh.close() - # recursive closures have a cyclic reference to themselves, which - # requires gc to collect (gh-10620). To avoid this problem, for - # performance and PyPy friendliness, we break the cycle: - flatten_dtype_internal = None - pack_items = None + X[pos:, ...] = chunk if X is None: X = np.array([], dtype) @@ -1125,9 +1182,6 @@ def tobytes_first(x, conv): X.shape = (1, -1) # Verify that the array has at least dimensions `ndmin`. - # Check correctness of the values of `ndmin` - if ndmin not in [0, 1, 2]: - raise ValueError('Illegal value of ndmin keyword: %s' % ndmin) # Tweak the size and shape of the arrays - remove extraneous dimensions if X.ndim > ndmin: X = np.squeeze(X) @@ -1149,6 +1203,18 @@ def tobytes_first(x, conv): return X +_loadtxt_with_like = array_function_dispatch( + _loadtxt_dispatcher +)(loadtxt) + + +def _savetxt_dispatcher(fname, X, fmt=None, delimiter=None, newline=None, + header=None, footer=None, comments=None, + encoding=None): + return (X,) + + +@array_function_dispatch(_savetxt_dispatcher) def savetxt(fname, X, fmt='%.18e', delimiter=' ', newline='\n', header='', footer='', comments='# ', encoding=None): """ @@ -1259,8 +1325,8 @@ def savetxt(fname, X, fmt='%.18e', delimiter=' ', newline='\n', header='', References ---------- .. [1] `Format Specification Mini-Language - <http://docs.python.org/library/string.html# - format-specification-mini-language>`_, Python Documentation. + <https://docs.python.org/library/string.html#format-specification-mini-language>`_, + Python Documentation. Examples -------- @@ -1276,8 +1342,8 @@ def savetxt(fname, X, fmt='%.18e', delimiter=' ', newline='\n', header='', fmt = asstr(fmt) delimiter = asstr(delimiter) - class WriteWrap(object): - """Convert to unicode in py2 or to bytes on bytestream inputs. + class WriteWrap: + """Convert to bytes on bytestream inputs. """ def __init__(self, fh, encoding): @@ -1310,16 +1376,13 @@ def first_write(self, v): self.write = self.write_bytes own_fh = False - if is_pathlib_path(fname): - fname = str(fname) + if isinstance(fname, os_PathLike): + fname = os_fspath(fname) if _is_string_like(fname): # datasource doesn't support creating a new file ... open(fname, 'wt').close() fh = np.lib._datasource.open(fname, 'wt', encoding=encoding) own_fh = True - # need to convert str to unicode for text io output - if sys.version_info[0] == 2: - fh = WriteWrap(fh, encoding or 'latin1') elif hasattr(fname, 'write'): # wrap to handle byte output streams fh = WriteWrap(fname, encoding or 'latin1') @@ -1341,7 +1404,7 @@ def first_write(self, v): # Complex dtype -- each field indicates a separate column else: - ncol = len(X.dtype.descr) + ncol = len(X.dtype.names) else: ncol = X.shape[1] @@ -1385,10 +1448,10 @@ def first_write(self, v): for row in X: try: v = format % tuple(row) + newline - except TypeError: + except TypeError as e: raise TypeError("Mismatch between array dtype ('%s') and " "format specifier ('%s')" - % (str(X.dtype), format)) + % (str(X.dtype), format)) from e fh.write(v) if len(footer) > 0: @@ -1399,8 +1462,9 @@ def first_write(self, v): fh.close() +@set_module('numpy') def fromregex(file, regexp, dtype, encoding=None): - """ + r""" Construct an array from a text file, using regular expression parsing. The returned array is always a structured array, and is constructed from @@ -1409,13 +1473,16 @@ def fromregex(file, regexp, dtype, encoding=None): Parameters ---------- - file : str or file - File name or file object to read. + file : path or file + Filename or file object to read. + + .. versionchanged:: 1.22.0 + Now accepts `os.PathLike` implementations. regexp : str or regexp Regular expression used to parse the file. Groups in the regular expression correspond to fields in the dtype. dtype : dtype or list of dtypes - Dtype for the structured array. + Dtype for the structured array; must be a structured datatype. encoding : str, optional Encoding used to decode the inputfile. Does not apply to input streams. @@ -1440,37 +1507,39 @@ def fromregex(file, regexp, dtype, encoding=None): ----- Dtypes for structured arrays can be specified in several forms, but all forms specify at least the data type and field name. For details see - `doc.structured_arrays`. + `basics.rec`. Examples -------- - >>> f = open('test.dat', 'w') - >>> f.write("1312 foo\\n1534 bar\\n444 qux") - >>> f.close() + >>> from io import StringIO + >>> text = StringIO("1312 foo\n1534 bar\n444 qux") - >>> regexp = r"(\\d+)\\s+(...)" # match [digits, whitespace, anything] - >>> output = np.fromregex('test.dat', regexp, + >>> regexp = r"(\d+)\s+(...)" # match [digits, whitespace, anything] + >>> output = np.fromregex(text, regexp, ... [('num', np.int64), ('key', 'S3')]) >>> output - array([(1312L, 'foo'), (1534L, 'bar'), (444L, 'qux')], - dtype=[('num', '<i8'), ('key', '|S3')]) + array([(1312, b'foo'), (1534, b'bar'), ( 444, b'qux')], + dtype=[('num', '<i8'), ('key', 'S3')]) >>> output['num'] - array([1312, 1534, 444], dtype=int64) + array([1312, 1534, 444]) """ own_fh = False if not hasattr(file, "read"): + file = os.fspath(file) file = np.lib._datasource.open(file, 'rt', encoding=encoding) own_fh = True try: if not isinstance(dtype, np.dtype): dtype = np.dtype(dtype) + if dtype.names is None: + raise TypeError('dtype must be a structured datatype.') content = file.read() - if isinstance(content, bytes) and isinstance(regexp, np.unicode): + if isinstance(content, bytes) and isinstance(regexp, str): regexp = asbytes(regexp) - elif isinstance(content, np.unicode) and isinstance(regexp, bytes): + elif isinstance(content, str) and isinstance(regexp, bytes): regexp = asstr(regexp) if not hasattr(regexp, 'match'): @@ -1497,13 +1566,28 @@ def fromregex(file, regexp, dtype, encoding=None): #####-------------------------------------------------------------------------- +def _genfromtxt_dispatcher(fname, dtype=None, comments=None, delimiter=None, + skip_header=None, skip_footer=None, converters=None, + missing_values=None, filling_values=None, usecols=None, + names=None, excludelist=None, deletechars=None, + replace_space=None, autostrip=None, case_sensitive=None, + defaultfmt=None, unpack=None, usemask=None, loose=None, + invalid_raise=None, max_rows=None, encoding=None, *, + like=None): + return (like,) + + +@set_array_function_like_doc +@set_module('numpy') def genfromtxt(fname, dtype=float, comments='#', delimiter=None, skip_header=0, skip_footer=0, converters=None, missing_values=None, filling_values=None, usecols=None, - names=None, excludelist=None, deletechars=None, + names=None, excludelist=None, + deletechars=''.join(sorted(NameValidator.defaultdeletechars)), replace_space='_', autostrip=False, case_sensitive=True, defaultfmt="f%i", unpack=None, usemask=False, loose=True, - invalid_raise=True, max_rows=None, encoding='bytes'): + invalid_raise=True, max_rows=None, encoding='bytes', *, + like=None): """ Load data from a text file, with missing values handled as specified. @@ -1514,8 +1598,8 @@ def genfromtxt(fname, dtype=float, comments='#', delimiter=None, ---------- fname : file, str, pathlib.Path, list of str, generator File, filename, list, or generator to read. If the filename - extension is `.gz` or `.bz2`, the file is first decompressed. Note - that generators must return byte strings in Python 3k. The strings + extension is ``.gz`` or ``.bz2``, the file is first decompressed. Note + that generators must return bytes or strings. The strings in a list or produced by a generator are treated as lines. dtype : dtype, optional Data type of the resulting array. @@ -1523,7 +1607,7 @@ def genfromtxt(fname, dtype=float, comments='#', delimiter=None, column, individually. comments : str, optional The character used to indicate the start of a comment. - All the characters occurring on a line after a comment are discarded + All the characters occurring on a line after a comment are discarded. delimiter : str, int, or sequence, optional The string used to separate values. By default, any consecutive whitespaces act as delimiter. An integer or sequence of integers @@ -1550,15 +1634,15 @@ def genfromtxt(fname, dtype=float, comments='#', delimiter=None, ``usecols = (1, 4, 5)`` will extract the 2nd, 5th and 6th columns. names : {None, True, str, sequence}, optional If `names` is True, the field names are read from the first line after - the first `skip_header` lines. This line can optionally be proceeded + the first `skip_header` lines. This line can optionally be preceded by a comment delimiter. If `names` is a sequence or a single-string of comma-separated names, the names will be used to define the field names in a structured dtype. If `names` is None, the names of the dtype fields will be used, if any. excludelist : sequence, optional A list of names to exclude. This list is appended to the default list - ['return','file','print']. Excluded names are appended an underscore: - for example, `file` would become `file_`. + ['return','file','print']. Excluded names are appended with an + underscore: for example, `file` would become `file_`. deletechars : str, optional A string combining invalid characters that must be deleted from the names. @@ -1567,7 +1651,7 @@ def genfromtxt(fname, dtype=float, comments='#', delimiter=None, autostrip : bool, optional Whether to automatically strip white spaces from the variables. replace_space : char, optional - Character(s) used in replacement of white spaces in the variables + Character(s) used in replacement of white spaces in the variable names. By default, use a '_'. case_sensitive : {True, False, 'upper', 'lower'}, optional If True, field names are case sensitive. @@ -1575,7 +1659,9 @@ def genfromtxt(fname, dtype=float, comments='#', delimiter=None, If 'lower', field names are converted to lower case. unpack : bool, optional If True, the returned array is transposed, so that arguments may be - unpacked using ``x, y, z = loadtxt(...)`` + unpacked using ``x, y, z = genfromtxt(...)``. When used with a + structured data-type, arrays are returned for each field. + Default is False. usemask : bool, optional If True, return a masked array. If False, return a regular array. @@ -1600,6 +1686,9 @@ def genfromtxt(fname, dtype=float, comments='#', delimiter=None, to None the system default is used. The default value is 'bytes'. .. versionadded:: 1.14.0 + ${ARRAY_FUNCTION_LIKE} + + .. versionadded:: 1.20.0 Returns ------- @@ -1615,7 +1704,7 @@ def genfromtxt(fname, dtype=float, comments='#', delimiter=None, ----- * When spaces are used as delimiters, or when no delimiter has been given as input, there should not be any missing data between two fields. - * When the variables are named (either by a flexible dtype or with `names`, + * When the variables are named (either by a flexible dtype or with `names`), there must not be any header in the file (else a ValueError exception is raised). * Individual values are not stripped of spaces by default. @@ -1624,10 +1713,10 @@ def genfromtxt(fname, dtype=float, comments='#', delimiter=None, References ---------- .. [1] NumPy User Guide, section `I/O with NumPy - <http://docs.scipy.org/doc/numpy/user/basics.io.genfromtxt.html>`_. + <https://docs.scipy.org/doc/numpy/user/basics.io.genfromtxt.html>`_. Examples - --------- + -------- >>> from io import StringIO >>> import numpy as np @@ -1637,26 +1726,26 @@ def genfromtxt(fname, dtype=float, comments='#', delimiter=None, >>> data = np.genfromtxt(s, dtype=[('myint','i8'),('myfloat','f8'), ... ('mystring','S5')], delimiter=",") >>> data - array((1, 1.3, 'abcde'), - dtype=[('myint', '<i8'), ('myfloat', '<f8'), ('mystring', '|S5')]) + array((1, 1.3, b'abcde'), + dtype=[('myint', '<i8'), ('myfloat', '<f8'), ('mystring', 'S5')]) Using dtype = None - >>> s.seek(0) # needed for StringIO example only + >>> _ = s.seek(0) # needed for StringIO example only >>> data = np.genfromtxt(s, dtype=None, ... names = ['myint','myfloat','mystring'], delimiter=",") >>> data - array((1, 1.3, 'abcde'), - dtype=[('myint', '<i8'), ('myfloat', '<f8'), ('mystring', '|S5')]) + array((1, 1.3, b'abcde'), + dtype=[('myint', '<i8'), ('myfloat', '<f8'), ('mystring', 'S5')]) Specifying dtype and names - >>> s.seek(0) + >>> _ = s.seek(0) >>> data = np.genfromtxt(s, dtype="i8,f8,S5", ... names=['myint','myfloat','mystring'], delimiter=",") >>> data - array((1, 1.3, 'abcde'), - dtype=[('myint', '<i8'), ('myfloat', '<f8'), ('mystring', '|S5')]) + array((1, 1.3, b'abcde'), + dtype=[('myint', '<i8'), ('myfloat', '<f8'), ('mystring', 'S5')]) An example with fixed-width columns @@ -1664,10 +1753,35 @@ def genfromtxt(fname, dtype=float, comments='#', delimiter=None, >>> data = np.genfromtxt(s, dtype=None, names=['intvar','fltvar','strvar'], ... delimiter=[1,3,5]) >>> data - array((1, 1.3, 'abcde'), - dtype=[('intvar', '<i8'), ('fltvar', '<f8'), ('strvar', '|S5')]) + array((1, 1.3, b'abcde'), + dtype=[('intvar', '<i8'), ('fltvar', '<f8'), ('strvar', 'S5')]) + + An example to show comments + + >>> f = StringIO(''' + ... text,# of chars + ... hello world,11 + ... numpy,5''') + >>> np.genfromtxt(f, dtype='S12,S12', delimiter=',') + array([(b'text', b''), (b'hello world', b'11'), (b'numpy', b'5')], + dtype=[('f0', 'S12'), ('f1', 'S12')]) """ + + if like is not None: + return _genfromtxt_with_like( + fname, dtype=dtype, comments=comments, delimiter=delimiter, + skip_header=skip_header, skip_footer=skip_footer, + converters=converters, missing_values=missing_values, + filling_values=filling_values, usecols=usecols, names=names, + excludelist=excludelist, deletechars=deletechars, + replace_space=replace_space, autostrip=autostrip, + case_sensitive=case_sensitive, defaultfmt=defaultfmt, + unpack=unpack, usemask=usemask, loose=loose, + invalid_raise=invalid_raise, max_rows=max_rows, encoding=encoding, + like=like + ) + if max_rows is not None: if skip_footer: raise ValueError( @@ -1692,301 +1806,301 @@ def genfromtxt(fname, dtype=float, comments='#', delimiter=None, byte_converters = False # Initialize the filehandle, the LineSplitter and the NameValidator - own_fhd = False try: - if is_pathlib_path(fname): - fname = str(fname) - if isinstance(fname, basestring): - fhd = iter(np.lib._datasource.open(fname, 'rt', encoding=encoding)) - own_fhd = True + if isinstance(fname, os_PathLike): + fname = os_fspath(fname) + if isinstance(fname, str): + fid = np.lib._datasource.open(fname, 'rt', encoding=encoding) + fid_ctx = contextlib.closing(fid) else: - fhd = iter(fname) - except TypeError: + fid = fname + fid_ctx = contextlib.nullcontext(fid) + fhd = iter(fid) + except TypeError as e: raise TypeError( - "fname must be a string, filehandle, list of strings, " - "or generator. Got %s instead." % type(fname)) - - split_line = LineSplitter(delimiter=delimiter, comments=comments, - autostrip=autostrip, encoding=encoding) - validate_names = NameValidator(excludelist=excludelist, - deletechars=deletechars, - case_sensitive=case_sensitive, - replace_space=replace_space) - - # Skip the first `skip_header` rows - for i in range(skip_header): - next(fhd) - - # Keep on until we find the first valid values - first_values = None - try: - while not first_values: - first_line = _decode_line(next(fhd), encoding) - if (names is True) and (comments is not None): - if comments in first_line: - first_line = ( - ''.join(first_line.split(comments)[1:])) - first_values = split_line(first_line) - except StopIteration: - # return an empty array if the datafile is empty - first_line = '' - first_values = [] - warnings.warn('genfromtxt: Empty input file: "%s"' % fname, stacklevel=2) - - # Should we take the first values as names ? - if names is True: - fval = first_values[0].strip() - if comments is not None: - if fval in comments: - del first_values[0] - - # Check the columns to use: make sure `usecols` is a list - if usecols is not None: + f"fname must be a string, filehandle, list of strings,\n" + f"or generator. Got {type(fname)} instead." + ) from e + + with fid_ctx: + split_line = LineSplitter(delimiter=delimiter, comments=comments, + autostrip=autostrip, encoding=encoding) + validate_names = NameValidator(excludelist=excludelist, + deletechars=deletechars, + case_sensitive=case_sensitive, + replace_space=replace_space) + + # Skip the first `skip_header` rows try: - usecols = [_.strip() for _ in usecols.split(",")] - except AttributeError: + for i in range(skip_header): + next(fhd) + + # Keep on until we find the first valid values + first_values = None + + while not first_values: + first_line = _decode_line(next(fhd), encoding) + if (names is True) and (comments is not None): + if comments in first_line: + first_line = ( + ''.join(first_line.split(comments)[1:])) + first_values = split_line(first_line) + except StopIteration: + # return an empty array if the datafile is empty + first_line = '' + first_values = [] + warnings.warn('genfromtxt: Empty input file: "%s"' % fname, stacklevel=2) + + # Should we take the first values as names ? + if names is True: + fval = first_values[0].strip() + if comments is not None: + if fval in comments: + del first_values[0] + + # Check the columns to use: make sure `usecols` is a list + if usecols is not None: try: - usecols = list(usecols) - except TypeError: - usecols = [usecols, ] - nbcols = len(usecols or first_values) - - # Check the names and overwrite the dtype.names if needed - if names is True: - names = validate_names([str(_.strip()) for _ in first_values]) - first_line = '' - elif _is_string_like(names): - names = validate_names([_.strip() for _ in names.split(',')]) - elif names: - names = validate_names(names) - # Get the dtype - if dtype is not None: - dtype = easy_dtype(dtype, defaultfmt=defaultfmt, names=names, - excludelist=excludelist, - deletechars=deletechars, - case_sensitive=case_sensitive, - replace_space=replace_space) - # Make sure the names is a list (for 2.5) - if names is not None: - names = list(names) - - if usecols: - for (i, current) in enumerate(usecols): - # if usecols is a list of names, convert to a list of indices - if _is_string_like(current): - usecols[i] = names.index(current) - elif current < 0: - usecols[i] = current + len(first_values) - # If the dtype is not None, make sure we update it - if (dtype is not None) and (len(dtype) > nbcols): - descr = dtype.descr - dtype = np.dtype([descr[_] for _ in usecols]) - names = list(dtype.names) - # If `names` is not None, update the names - elif (names is not None) and (len(names) > nbcols): - names = [names[_] for _ in usecols] - elif (names is not None) and (dtype is not None): - names = list(dtype.names) - - # Process the missing values ............................... - # Rename missing_values for convenience - user_missing_values = missing_values or () - if isinstance(user_missing_values, bytes): - user_missing_values = user_missing_values.decode('latin1') - - # Define the list of missing_values (one column: one list) - missing_values = [list(['']) for _ in range(nbcols)] - - # We have a dictionary: process it field by field - if isinstance(user_missing_values, dict): - # Loop on the items - for (key, val) in user_missing_values.items(): - # Is the key a string ? - if _is_string_like(key): - try: - # Transform it into an integer - key = names.index(key) - except ValueError: - # We couldn't find it: the name must have been dropped - continue - # Redefine the key as needed if it's a column number - if usecols: + usecols = [_.strip() for _ in usecols.split(",")] + except AttributeError: try: - key = usecols.index(key) - except ValueError: - pass - # Transform the value as a list of string - if isinstance(val, (list, tuple)): - val = [str(_) for _ in val] + usecols = list(usecols) + except TypeError: + usecols = [usecols, ] + nbcols = len(usecols or first_values) + + # Check the names and overwrite the dtype.names if needed + if names is True: + names = validate_names([str(_.strip()) for _ in first_values]) + first_line = '' + elif _is_string_like(names): + names = validate_names([_.strip() for _ in names.split(',')]) + elif names: + names = validate_names(names) + # Get the dtype + if dtype is not None: + dtype = easy_dtype(dtype, defaultfmt=defaultfmt, names=names, + excludelist=excludelist, + deletechars=deletechars, + case_sensitive=case_sensitive, + replace_space=replace_space) + # Make sure the names is a list (for 2.5) + if names is not None: + names = list(names) + + if usecols: + for (i, current) in enumerate(usecols): + # if usecols is a list of names, convert to a list of indices + if _is_string_like(current): + usecols[i] = names.index(current) + elif current < 0: + usecols[i] = current + len(first_values) + # If the dtype is not None, make sure we update it + if (dtype is not None) and (len(dtype) > nbcols): + descr = dtype.descr + dtype = np.dtype([descr[_] for _ in usecols]) + names = list(dtype.names) + # If `names` is not None, update the names + elif (names is not None) and (len(names) > nbcols): + names = [names[_] for _ in usecols] + elif (names is not None) and (dtype is not None): + names = list(dtype.names) + + # Process the missing values ............................... + # Rename missing_values for convenience + user_missing_values = missing_values or () + if isinstance(user_missing_values, bytes): + user_missing_values = user_missing_values.decode('latin1') + + # Define the list of missing_values (one column: one list) + missing_values = [list(['']) for _ in range(nbcols)] + + # We have a dictionary: process it field by field + if isinstance(user_missing_values, dict): + # Loop on the items + for (key, val) in user_missing_values.items(): + # Is the key a string ? + if _is_string_like(key): + try: + # Transform it into an integer + key = names.index(key) + except ValueError: + # We couldn't find it: the name must have been dropped + continue + # Redefine the key as needed if it's a column number + if usecols: + try: + key = usecols.index(key) + except ValueError: + pass + # Transform the value as a list of string + if isinstance(val, (list, tuple)): + val = [str(_) for _ in val] + else: + val = [str(val), ] + # Add the value(s) to the current list of missing + if key is None: + # None acts as default + for miss in missing_values: + miss.extend(val) + else: + missing_values[key].extend(val) + # We have a sequence : each item matches a column + elif isinstance(user_missing_values, (list, tuple)): + for (value, entry) in zip(user_missing_values, missing_values): + value = str(value) + if value not in entry: + entry.append(value) + # We have a string : apply it to all entries + elif isinstance(user_missing_values, str): + user_value = user_missing_values.split(",") + for entry in missing_values: + entry.extend(user_value) + # We have something else: apply it to all entries + else: + for entry in missing_values: + entry.extend([str(user_missing_values)]) + + # Process the filling_values ............................... + # Rename the input for convenience + user_filling_values = filling_values + if user_filling_values is None: + user_filling_values = [] + # Define the default + filling_values = [None] * nbcols + # We have a dictionary : update each entry individually + if isinstance(user_filling_values, dict): + for (key, val) in user_filling_values.items(): + if _is_string_like(key): + try: + # Transform it into an integer + key = names.index(key) + except ValueError: + # We couldn't find it: the name must have been dropped, + continue + # Redefine the key if it's a column number and usecols is defined + if usecols: + try: + key = usecols.index(key) + except ValueError: + pass + # Add the value to the list + filling_values[key] = val + # We have a sequence : update on a one-to-one basis + elif isinstance(user_filling_values, (list, tuple)): + n = len(user_filling_values) + if (n <= nbcols): + filling_values[:n] = user_filling_values else: - val = [str(val), ] - # Add the value(s) to the current list of missing - if key is None: - # None acts as default - for miss in missing_values: - miss.extend(val) + filling_values = user_filling_values[:nbcols] + # We have something else : use it for all entries + else: + filling_values = [user_filling_values] * nbcols + + # Initialize the converters ................................ + if dtype is None: + # Note: we can't use a [...]*nbcols, as we would have 3 times the same + # ... converter, instead of 3 different converters. + converters = [StringConverter(None, missing_values=miss, default=fill) + for (miss, fill) in zip(missing_values, filling_values)] + else: + dtype_flat = flatten_dtype(dtype, flatten_base=True) + # Initialize the converters + if len(dtype_flat) > 1: + # Flexible type : get a converter from each dtype + zipit = zip(dtype_flat, missing_values, filling_values) + converters = [StringConverter(dt, locked=True, + missing_values=miss, default=fill) + for (dt, miss, fill) in zipit] else: - missing_values[key].extend(val) - # We have a sequence : each item matches a column - elif isinstance(user_missing_values, (list, tuple)): - for (value, entry) in zip(user_missing_values, missing_values): - value = str(value) - if value not in entry: - entry.append(value) - # We have a string : apply it to all entries - elif isinstance(user_missing_values, basestring): - user_value = user_missing_values.split(",") - for entry in missing_values: - entry.extend(user_value) - # We have something else: apply it to all entries - else: - for entry in missing_values: - entry.extend([str(user_missing_values)]) - - # Process the filling_values ............................... - # Rename the input for convenience - user_filling_values = filling_values - if user_filling_values is None: - user_filling_values = [] - # Define the default - filling_values = [None] * nbcols - # We have a dictionary : update each entry individually - if isinstance(user_filling_values, dict): - for (key, val) in user_filling_values.items(): - if _is_string_like(key): + # Set to a default converter (but w/ different missing values) + zipit = zip(missing_values, filling_values) + converters = [StringConverter(dtype, locked=True, + missing_values=miss, default=fill) + for (miss, fill) in zipit] + # Update the converters to use the user-defined ones + uc_update = [] + for (j, conv) in user_converters.items(): + # If the converter is specified by column names, use the index instead + if _is_string_like(j): try: - # Transform it into an integer - key = names.index(key) + j = names.index(j) + i = j except ValueError: - # We couldn't find it: the name must have been dropped, continue - # Redefine the key if it's a column number and usecols is defined - if usecols: + elif usecols: try: - key = usecols.index(key) + i = usecols.index(j) except ValueError: - pass - # Add the value to the list - filling_values[key] = val - # We have a sequence : update on a one-to-one basis - elif isinstance(user_filling_values, (list, tuple)): - n = len(user_filling_values) - if (n <= nbcols): - filling_values[:n] = user_filling_values - else: - filling_values = user_filling_values[:nbcols] - # We have something else : use it for all entries - else: - filling_values = [user_filling_values] * nbcols - - # Initialize the converters ................................ - if dtype is None: - # Note: we can't use a [...]*nbcols, as we would have 3 times the same - # ... converter, instead of 3 different converters. - converters = [StringConverter(None, missing_values=miss, default=fill) - for (miss, fill) in zip(missing_values, filling_values)] - else: - dtype_flat = flatten_dtype(dtype, flatten_base=True) - # Initialize the converters - if len(dtype_flat) > 1: - # Flexible type : get a converter from each dtype - zipit = zip(dtype_flat, missing_values, filling_values) - converters = [StringConverter(dt, locked=True, - missing_values=miss, default=fill) - for (dt, miss, fill) in zipit] - else: - # Set to a default converter (but w/ different missing values) - zipit = zip(missing_values, filling_values) - converters = [StringConverter(dtype, locked=True, - missing_values=miss, default=fill) - for (miss, fill) in zipit] - # Update the converters to use the user-defined ones - uc_update = [] - for (j, conv) in user_converters.items(): - # If the converter is specified by column names, use the index instead - if _is_string_like(j): - try: - j = names.index(j) + # Unused converter specified + continue + else: i = j - except ValueError: - continue - elif usecols: - try: - i = usecols.index(j) - except ValueError: - # Unused converter specified + # Find the value to test - first_line is not filtered by usecols: + if len(first_line): + testing_value = first_values[j] + else: + testing_value = None + if conv is bytes: + user_conv = asbytes + elif byte_converters: + # converters may use decode to workaround numpy's old behaviour, + # so encode the string again before passing to the user converter + def tobytes_first(x, conv): + if type(x) is bytes: + return conv(x) + return conv(x.encode("latin1")) + user_conv = functools.partial(tobytes_first, conv=conv) + else: + user_conv = conv + converters[i].update(user_conv, locked=True, + testing_value=testing_value, + default=filling_values[i], + missing_values=missing_values[i],) + uc_update.append((i, user_conv)) + # Make sure we have the corrected keys in user_converters... + user_converters.update(uc_update) + + # Fixme: possible error as following variable never used. + # miss_chars = [_.missing_values for _ in converters] + + # Initialize the output lists ... + # ... rows + rows = [] + append_to_rows = rows.append + # ... masks + if usemask: + masks = [] + append_to_masks = masks.append + # ... invalid + invalid = [] + append_to_invalid = invalid.append + + # Parse each line + for (i, line) in enumerate(itertools.chain([first_line, ], fhd)): + values = split_line(line) + nbvalues = len(values) + # Skip an empty line + if nbvalues == 0: continue - else: - i = j - # Find the value to test - first_line is not filtered by usecols: - if len(first_line): - testing_value = first_values[j] - else: - testing_value = None - if conv is bytes: - user_conv = asbytes - elif byte_converters: - # converters may use decode to workaround numpy's old behaviour, - # so encode the string again before passing to the user converter - def tobytes_first(x, conv): - if type(x) is bytes: - return conv(x) - return conv(x.encode("latin1")) - import functools - user_conv = functools.partial(tobytes_first, conv=conv) - else: - user_conv = conv - converters[i].update(user_conv, locked=True, - testing_value=testing_value, - default=filling_values[i], - missing_values=missing_values[i],) - uc_update.append((i, user_conv)) - # Make sure we have the corrected keys in user_converters... - user_converters.update(uc_update) - - # Fixme: possible error as following variable never used. - # miss_chars = [_.missing_values for _ in converters] - - # Initialize the output lists ... - # ... rows - rows = [] - append_to_rows = rows.append - # ... masks - if usemask: - masks = [] - append_to_masks = masks.append - # ... invalid - invalid = [] - append_to_invalid = invalid.append - - # Parse each line - for (i, line) in enumerate(itertools.chain([first_line, ], fhd)): - values = split_line(line) - nbvalues = len(values) - # Skip an empty line - if nbvalues == 0: - continue - if usecols: - # Select only the columns we need - try: - values = [values[_] for _ in usecols] - except IndexError: + if usecols: + # Select only the columns we need + try: + values = [values[_] for _ in usecols] + except IndexError: + append_to_invalid((i + skip_header + 1, nbvalues)) + continue + elif nbvalues != nbcols: append_to_invalid((i + skip_header + 1, nbvalues)) continue - elif nbvalues != nbcols: - append_to_invalid((i + skip_header + 1, nbvalues)) - continue - # Store the values - append_to_rows(tuple(values)) - if usemask: - append_to_masks(tuple([v.strip() in m - for (v, m) in zip(values, - missing_values)])) - if len(rows) == max_rows: - break - - if own_fhd: - fhd.close() + # Store the values + append_to_rows(tuple(values)) + if usemask: + append_to_masks(tuple([v.strip() in m + for (v, m) in zip(values, + missing_values)])) + if len(rows) == max_rows: + break # Upgrade the converters (if needed) if dtype is None: @@ -2089,10 +2203,10 @@ def encode_unicode_cols(row_tup): if names is None: # If the dtype is uniform (before sizing strings) - base = set([ + base = { c_type for c, c_type in zip(converters, column_types) - if c._checked]) + if c._checked} if len(base) == 1: uniform_type, = base (ddtype, mdtype) = (uniform_type, bool) @@ -2110,7 +2224,7 @@ def encode_unicode_cols(row_tup): outputmask = np.array(masks, dtype=mdtype) else: # Overwrite the initial dtype names if needed - if names and dtype.names: + if names and dtype.names is not None: dtype.names = names # Case 1. We have a structured type if len(dtype_flat) > 1: @@ -2160,7 +2274,7 @@ def encode_unicode_cols(row_tup): # output = np.array(data, dtype) if usemask: - if dtype.names: + if dtype.names is not None: mdtype = [(_, bool) for _ in dtype.names] else: mdtype = bool @@ -2177,43 +2291,23 @@ def encode_unicode_cols(row_tup): if usemask: output = output.view(MaskedArray) output._mask = outputmask + output = np.squeeze(output) if unpack: - return output.squeeze().T - return output.squeeze() - - -def ndfromtxt(fname, **kwargs): - """ - Load ASCII data stored in a file and return it as a single array. - - Parameters - ---------- - fname, kwargs : For a description of input parameters, see `genfromtxt`. - - See Also - -------- - numpy.genfromtxt : generic function. - - """ - kwargs['usemask'] = False - return genfromtxt(fname, **kwargs) - - -def mafromtxt(fname, **kwargs): - """ - Load ASCII data stored in a text file and return a masked array. - - Parameters - ---------- - fname, kwargs : For a description of input parameters, see `genfromtxt`. + if names is None: + return output.T + elif len(names) == 1: + # squeeze single-name dtypes too + return output[names[0]] + else: + # For structured arrays with multiple fields, + # return an array for each field. + return [output[field] for field in names] + return output - See Also - -------- - numpy.genfromtxt : generic function to load ASCII data. - """ - kwargs['usemask'] = True - return genfromtxt(fname, **kwargs) +_genfromtxt_with_like = array_function_dispatch( + _genfromtxt_dispatcher +)(genfromtxt) def recfromtxt(fname, **kwargs): diff --git a/numpy/lib/npyio.pyi b/numpy/lib/npyio.pyi new file mode 100644 index 000000000000..75d06e9e33dd --- /dev/null +++ b/numpy/lib/npyio.pyi @@ -0,0 +1,266 @@ +import os +import sys +import zipfile +import types +from typing import ( + Literal as L, + Any, + Mapping, + TypeVar, + Generic, + List, + Type, + Iterator, + Union, + IO, + overload, + Sequence, + Callable, + Pattern, + Protocol, + Iterable, +) + +from numpy import ( + DataSource as DataSource, + ndarray, + recarray, + dtype, + generic, + float64, + void, + record, +) + +from numpy.ma.mrecords import MaskedRecords +from numpy.typing import ArrayLike, DTypeLike, NDArray, _SupportsDType + +from numpy.core.multiarray import ( + packbits as packbits, + unpackbits as unpackbits, +) + +_T = TypeVar("_T") +_T_contra = TypeVar("_T_contra", contravariant=True) +_T_co = TypeVar("_T_co", covariant=True) +_SCT = TypeVar("_SCT", bound=generic) +_CharType_co = TypeVar("_CharType_co", str, bytes, covariant=True) +_CharType_contra = TypeVar("_CharType_contra", str, bytes, contravariant=True) + +_DTypeLike = Union[ + Type[_SCT], + dtype[_SCT], + _SupportsDType[dtype[_SCT]], +] + +class _SupportsGetItem(Protocol[_T_contra, _T_co]): + def __getitem__(self, key: _T_contra, /) -> _T_co: ... + +class _SupportsRead(Protocol[_CharType_co]): + def read(self) -> _CharType_co: ... + +class _SupportsReadSeek(Protocol[_CharType_co]): + def read(self, n: int, /) -> _CharType_co: ... + def seek(self, offset: int, whence: int, /) -> object: ... + +class _SupportsWrite(Protocol[_CharType_contra]): + def write(self, s: _CharType_contra, /) -> object: ... + +__all__: List[str] + +class BagObj(Generic[_T_co]): + def __init__(self, obj: _SupportsGetItem[str, _T_co]) -> None: ... + def __getattribute__(self, key: str) -> _T_co: ... + def __dir__(self) -> List[str]: ... + +class NpzFile(Mapping[str, NDArray[Any]]): + zip: zipfile.ZipFile + fid: None | IO[str] + files: List[str] + allow_pickle: bool + pickle_kwargs: None | Mapping[str, Any] + # Represent `f` as a mutable property so we can access the type of `self` + @property + def f(self: _T) -> BagObj[_T]: ... + @f.setter + def f(self: _T, value: BagObj[_T]) -> None: ... + def __init__( + self, + fid: IO[str], + own_fid: bool = ..., + allow_pickle: bool = ..., + pickle_kwargs: None | Mapping[str, Any] = ..., + ) -> None: ... + def __enter__(self: _T) -> _T: ... + def __exit__( + self, + exc_type: None | Type[BaseException], + exc_value: None | BaseException, + traceback: None | types.TracebackType, + /, + ) -> None: ... + def close(self) -> None: ... + def __del__(self) -> None: ... + def __iter__(self) -> Iterator[str]: ... + def __len__(self) -> int: ... + def __getitem__(self, key: str) -> NDArray[Any]: ... + +# NOTE: Returns a `NpzFile` if file is a zip file; +# returns an `ndarray`/`memmap` otherwise +def load( + file: str | bytes | os.PathLike[Any] | _SupportsReadSeek[bytes], + mmap_mode: L[None, "r+", "r", "w+", "c"] = ..., + allow_pickle: bool = ..., + fix_imports: bool = ..., + encoding: L["ASCII", "latin1", "bytes"] = ..., +) -> Any: ... + +def save( + file: str | os.PathLike[str] | _SupportsWrite[bytes], + arr: ArrayLike, + allow_pickle: bool = ..., + fix_imports: bool = ..., +) -> None: ... + +def savez( + file: str | os.PathLike[str] | _SupportsWrite[bytes], + *args: ArrayLike, + **kwds: ArrayLike, +) -> None: ... + +def savez_compressed( + file: str | os.PathLike[str] | _SupportsWrite[bytes], + *args: ArrayLike, + **kwds: ArrayLike, +) -> None: ... + +# File-like objects only have to implement `__iter__` and, +# optionally, `encoding` +@overload +def loadtxt( + fname: str | os.PathLike[str] | Iterable[str] | Iterable[bytes], + dtype: None = ..., + comments: None | str | Sequence[str] = ..., + delimiter: None | str = ..., + converters: None | Mapping[int | str, Callable[[str], Any]] = ..., + skiprows: int = ..., + usecols: int | Sequence[int] = ..., + unpack: bool = ..., + ndmin: L[0, 1, 2] = ..., + encoding: None | str = ..., + max_rows: None | int = ..., + *, + like: None | ArrayLike = ... +) -> NDArray[float64]: ... +@overload +def loadtxt( + fname: str | os.PathLike[str] | Iterable[str] | Iterable[bytes], + dtype: _DTypeLike[_SCT], + comments: None | str | Sequence[str] = ..., + delimiter: None | str = ..., + converters: None | Mapping[int | str, Callable[[str], Any]] = ..., + skiprows: int = ..., + usecols: int | Sequence[int] = ..., + unpack: bool = ..., + ndmin: L[0, 1, 2] = ..., + encoding: None | str = ..., + max_rows: None | int = ..., + *, + like: None | ArrayLike = ... +) -> NDArray[_SCT]: ... +@overload +def loadtxt( + fname: str | os.PathLike[str] | Iterable[str] | Iterable[bytes], + dtype: DTypeLike, + comments: None | str | Sequence[str] = ..., + delimiter: None | str = ..., + converters: None | Mapping[int | str, Callable[[str], Any]] = ..., + skiprows: int = ..., + usecols: int | Sequence[int] = ..., + unpack: bool = ..., + ndmin: L[0, 1, 2] = ..., + encoding: None | str = ..., + max_rows: None | int = ..., + *, + like: None | ArrayLike = ... +) -> NDArray[Any]: ... + +def savetxt( + fname: str | os.PathLike[str] | _SupportsWrite[str] | _SupportsWrite[bytes], + X: ArrayLike, + fmt: str | Sequence[str] = ..., + delimiter: str = ..., + newline: str = ..., + header: str = ..., + footer: str = ..., + comments: str = ..., + encoding: None | str = ..., +) -> None: ... + +@overload +def fromregex( + file: str | os.PathLike[str] | _SupportsRead[str] | _SupportsRead[bytes], + regexp: str | bytes | Pattern[Any], + dtype: _DTypeLike[_SCT], + encoding: None | str = ... +) -> NDArray[_SCT]: ... +@overload +def fromregex( + file: str | os.PathLike[str] | _SupportsRead[str] | _SupportsRead[bytes], + regexp: str | bytes | Pattern[Any], + dtype: DTypeLike, + encoding: None | str = ... +) -> NDArray[Any]: ... + +# TODO: Sort out arguments +@overload +def genfromtxt( + fname: str | os.PathLike[str] | Iterable[str] | Iterable[bytes], + dtype: None = ..., + *args: Any, + **kwargs: Any, +) -> NDArray[float64]: ... +@overload +def genfromtxt( + fname: str | os.PathLike[str] | Iterable[str] | Iterable[bytes], + dtype: _DTypeLike[_SCT], + *args: Any, + **kwargs: Any, +) -> NDArray[_SCT]: ... +@overload +def genfromtxt( + fname: str | os.PathLike[str] | Iterable[str] | Iterable[bytes], + dtype: DTypeLike, + *args: Any, + **kwargs: Any, +) -> NDArray[Any]: ... + +@overload +def recfromtxt( + fname: str | os.PathLike[str] | Iterable[str] | Iterable[bytes], + *, + usemask: L[False] = ..., + **kwargs: Any, +) -> recarray[Any, dtype[record]]: ... +@overload +def recfromtxt( + fname: str | os.PathLike[str] | Iterable[str] | Iterable[bytes], + *, + usemask: L[True], + **kwargs: Any, +) -> MaskedRecords[Any, dtype[void]]: ... + +@overload +def recfromcsv( + fname: str | os.PathLike[str] | Iterable[str] | Iterable[bytes], + *, + usemask: L[False] = ..., + **kwargs: Any, +) -> recarray[Any, dtype[record]]: ... +@overload +def recfromcsv( + fname: str | os.PathLike[str] | Iterable[str] | Iterable[bytes], + *, + usemask: L[True], + **kwargs: Any, +) -> MaskedRecords[Any, dtype[void]]: ... diff --git a/numpy/lib/polynomial.py b/numpy/lib/polynomial.py index 078608bbb04d..f824c4c5e2c1 100644 --- a/numpy/lib/polynomial.py +++ b/numpy/lib/polynomial.py @@ -2,23 +2,30 @@ Functions to operate on polynomials. """ -from __future__ import division, absolute_import, print_function - __all__ = ['poly', 'roots', 'polyint', 'polyder', 'polyadd', 'polysub', 'polymul', 'polydiv', 'polyval', 'poly1d', 'polyfit', 'RankWarning'] +import functools import re import warnings import numpy.core.numeric as NX from numpy.core import (isscalar, abs, finfo, atleast_1d, hstack, dot, array, ones) +from numpy.core import overrides +from numpy.core.overrides import set_module from numpy.lib.twodim_base import diag, vander from numpy.lib.function_base import trim_zeros from numpy.lib.type_check import iscomplex, real, imag, mintypecode from numpy.linalg import eigvals, lstsq, inv + +array_function_dispatch = functools.partial( + overrides.array_function_dispatch, module='numpy') + + +@set_module('numpy') class RankWarning(UserWarning): """ Issued by `polyfit` when the Vandermonde matrix is rank deficient. @@ -29,10 +36,22 @@ class RankWarning(UserWarning): """ pass + +def _poly_dispatcher(seq_of_zeros): + return seq_of_zeros + + +@array_function_dispatch(_poly_dispatcher) def poly(seq_of_zeros): """ Find the coefficients of a polynomial with the given sequence of roots. + .. note:: + This forms part of the old polynomial API. Since version 1.4, the + new polynomial API defined in `numpy.polynomial` is preferred. + A summary of the differences can be found in the + :doc:`transition guide </reference/routines.polynomials>`. + Returns the coefficients of the polynomial whose leading coefficient is one for the given sequence of zeros (multiple roots must be included in the sequence as many times as their multiplicity; see Examples). @@ -95,7 +114,7 @@ def poly(seq_of_zeros): Given a sequence of a polynomial's zeros: >>> np.poly((0, 0, 0)) # Multiple root example - array([1, 0, 0, 0]) + array([1., 0., 0., 0.]) The line above represents z**3 + 0*z**2 + 0*z + 0. @@ -104,14 +123,14 @@ def poly(seq_of_zeros): The line above represents z**3 - z/4 - >>> np.poly((np.random.random(1.)[0], 0, np.random.random(1.)[0])) - array([ 1. , -0.77086955, 0.08618131, 0. ]) #random + >>> np.poly((np.random.random(1)[0], 0, np.random.random(1)[0])) + array([ 1. , -0.77086955, 0.08618131, 0. ]) # random Given a square array object: >>> P = np.array([[0, 1./3], [-1./2, 0]]) >>> np.poly(P) - array([ 1. , 0. , 0.16666667]) + array([1. , 0. , 0.16666667]) Note how in all cases the leading coefficient is always 1. @@ -133,9 +152,8 @@ def poly(seq_of_zeros): return 1.0 dt = seq_of_zeros.dtype a = ones((1,), dtype=dt) - for k in range(len(seq_of_zeros)): - a = NX.convolve(a, array([1, -seq_of_zeros[k]], dtype=dt), - mode='full') + for zero in seq_of_zeros: + a = NX.convolve(a, array([1, -zero], dtype=dt), mode='full') if issubclass(a.dtype.type, NX.complexfloating): # if complex roots are all complex conjugates, the roots are real. @@ -145,10 +163,22 @@ def poly(seq_of_zeros): return a + +def _roots_dispatcher(p): + return p + + +@array_function_dispatch(_roots_dispatcher) def roots(p): """ Return the roots of a polynomial with coefficients given in p. + .. note:: + This forms part of the old polynomial API. Since version 1.4, the + new polynomial API defined in `numpy.polynomial` is preferred. + A summary of the differences can be found in the + :doc:`transition guide </reference/routines.polynomials>`. + The values in the rank-1 array `p` are coefficients of a polynomial. If the length of `p` is n+1 then the polynomial is described by:: @@ -229,10 +259,22 @@ def roots(p): roots = hstack((roots, NX.zeros(trailing_zeros, roots.dtype))) return roots + +def _polyint_dispatcher(p, m=None, k=None): + return (p,) + + +@array_function_dispatch(_polyint_dispatcher) def polyint(p, m=1, k=None): """ Return an antiderivative (indefinite integral) of a polynomial. + .. note:: + This forms part of the old polynomial API. Since version 1.4, the + new polynomial API defined in `numpy.polynomial` is preferred. + A summary of the differences can be found in the + :doc:`transition guide </reference/routines.polynomials>`. + The returned order `m` antiderivative `P` of polynomial `p` satisfies :math:`\\frac{d^m}{dx^m}P(x) = p(x)` and is defined up to `m - 1` integration constants `k`. The constants determine the low-order @@ -245,7 +287,7 @@ def polyint(p, m=1, k=None): Parameters ---------- p : array_like or poly1d - Polynomial to differentiate. + Polynomial to integrate. A sequence is interpreted as polynomial coefficients, see `poly1d`. m : int, optional Order of the antiderivative. (Default: 1) @@ -268,7 +310,7 @@ def polyint(p, m=1, k=None): >>> p = np.poly1d([1,1,1]) >>> P = np.polyint(p) >>> P - poly1d([ 0.33333333, 0.5 , 1. , 0. ]) + poly1d([ 0.33333333, 0.5 , 1. , 0. ]) # may vary >>> np.polyder(P) == p True @@ -283,7 +325,7 @@ def polyint(p, m=1, k=None): 0.0 >>> P = np.polyint(p, 3, k=[6,5,3]) >>> P - poly1d([ 0.01666667, 0.04166667, 0.16666667, 3. , 5. , 3. ]) + poly1d([ 0.01666667, 0.04166667, 0.16666667, 3. , 5. , 3. ]) # may vary Note that 3 = 6 / 2!, and that the constants are given in the order of integrations. Constant of the highest-order polynomial term comes first: @@ -322,10 +364,22 @@ def polyint(p, m=1, k=None): return poly1d(val) return val + +def _polyder_dispatcher(p, m=None): + return (p,) + + +@array_function_dispatch(_polyder_dispatcher) def polyder(p, m=1): """ Return the derivative of the specified order of a polynomial. + .. note:: + This forms part of the old polynomial API. Since version 1.4, the + new polynomial API defined in `numpy.polynomial` is preferred. + A summary of the differences can be found in the + :doc:`transition guide </reference/routines.polynomials>`. + Parameters ---------- p : poly1d or sequence @@ -371,7 +425,7 @@ def polyder(p, m=1): >>> np.polyder(p, 3) poly1d([6]) >>> np.polyder(p, 4) - poly1d([ 0.]) + poly1d([0]) """ m = int(m) @@ -390,13 +444,29 @@ def polyder(p, m=1): val = poly1d(val) return val + +def _polyfit_dispatcher(x, y, deg, rcond=None, full=None, w=None, cov=None): + return (x, y, w) + + +@array_function_dispatch(_polyfit_dispatcher) def polyfit(x, y, deg, rcond=None, full=False, w=None, cov=False): """ Least squares polynomial fit. + .. note:: + This forms part of the old polynomial API. Since version 1.4, the + new polynomial API defined in `numpy.polynomial` is preferred. + A summary of the differences can be found in the + :doc:`transition guide </reference/routines.polynomials>`. + Fit a polynomial ``p(x) = p[0] * x**deg + ... + p[deg]`` of degree `deg` to points `(x, y)`. Returns a vector of coefficients `p` that minimises - the squared error. + the squared error in the order `deg`, `deg-1`, ... `0`. + + The `Polynomial.fit <numpy.polynomial.polynomial.Polynomial.fit>` class + method is recommended for new code as it is more stable numerically. See + the documentation of the method for more information. Parameters ---------- @@ -418,11 +488,20 @@ def polyfit(x, y, deg, rcond=None, full=False, w=None, cov=False): default) just the coefficients are returned, when True diagnostic information from the singular value decomposition is also returned. w : array_like, shape (M,), optional - Weights to apply to the y-coordinates of the sample points. For - gaussian uncertainties, use 1/sigma (not 1/sigma**2). - cov : bool, optional - Return the estimate and the covariance matrix of the estimate - If full is True, then cov is not returned. + Weights. If not None, the weight ``w[i]`` applies to the unsquared + residual ``y[i] - y_hat[i]`` at ``x[i]``. Ideally the weights are + chosen so that the errors of the products ``w[i]*y[i]`` all have the + same variance. When using inverse-variance weighting, use + ``w[i] = 1/sigma(y[i])``. The default value is None. + cov : bool or str, optional + If given and not `False`, return not just the estimate but also its + covariance matrix. By default, the covariance are scaled by + chi2/dof, where dof = M - (deg + 1), i.e., the weights are presumed + to be unreliable except in a relative sense and everything is scaled + such that the reduced chi2 is unity. This scaling is omitted if + ``cov='unscaled'``, as is relevant for the case that the weights are + w = 1/sigma, with sigma known to be a reliable estimate of the + uncertainty. Returns ------- @@ -431,13 +510,19 @@ def polyfit(x, y, deg, rcond=None, full=False, w=None, cov=False): coefficients for `k`-th data set are in ``p[:,k]``. residuals, rank, singular_values, rcond - Present only if `full` = True. Residuals of the least-squares fit, - the effective rank of the scaled Vandermonde coefficient matrix, - its singular values, and the specified value of `rcond`. For more - details, see `linalg.lstsq`. + These values are only returned if ``full == True`` + + - residuals -- sum of squared residuals of the least squares fit + - rank -- the effective rank of the scaled Vandermonde + coefficient matrix + - singular_values -- singular values of the scaled Vandermonde + coefficient matrix + - rcond -- value of `rcond`. + + For more details, see `numpy.linalg.lstsq`. V : ndarray, shape (M,M) or (M,M,K) - Present only if `full` = False and `cov`=True. The covariance + Present only if ``full == False`` and ``cov == True``. The covariance matrix of the polynomial coefficient estimates. The diagonal of this matrix are the variance estimates for each coefficient. If y is a 2-D array, then the covariance matrix for the `k`-th data set @@ -448,7 +533,7 @@ def polyfit(x, y, deg, rcond=None, full=False, w=None, cov=False): ----- RankWarning The rank of the coefficient matrix in the least-squares fit is - deficient. The warning is only raised if `full` = False. + deficient. The warning is only raised if ``full == False``. The warnings can be turned off by @@ -465,7 +550,7 @@ def polyfit(x, y, deg, rcond=None, full=False, w=None, cov=False): ----- The solution minimizes the squared error - .. math :: + .. math:: E = \\sum_{j=0}^k |p(x_j) - y_j|^2 in the equations:: @@ -494,38 +579,41 @@ def polyfit(x, y, deg, rcond=None, full=False, w=None, cov=False): References ---------- .. [1] Wikipedia, "Curve fitting", - http://en.wikipedia.org/wiki/Curve_fitting + https://en.wikipedia.org/wiki/Curve_fitting .. [2] Wikipedia, "Polynomial interpolation", - http://en.wikipedia.org/wiki/Polynomial_interpolation + https://en.wikipedia.org/wiki/Polynomial_interpolation Examples -------- + >>> import warnings >>> x = np.array([0.0, 1.0, 2.0, 3.0, 4.0, 5.0]) >>> y = np.array([0.0, 0.8, 0.9, 0.1, -0.8, -1.0]) >>> z = np.polyfit(x, y, 3) >>> z - array([ 0.08703704, -0.81349206, 1.69312169, -0.03968254]) + array([ 0.08703704, -0.81349206, 1.69312169, -0.03968254]) # may vary It is convenient to use `poly1d` objects for dealing with polynomials: >>> p = np.poly1d(z) >>> p(0.5) - 0.6143849206349179 + 0.6143849206349179 # may vary >>> p(3.5) - -0.34732142857143039 + -0.34732142857143039 # may vary >>> p(10) - 22.579365079365115 + 22.579365079365115 # may vary High-order polynomials may oscillate wildly: - >>> p30 = np.poly1d(np.polyfit(x, y, 30)) - /... RankWarning: Polyfit may be poorly conditioned... + >>> with warnings.catch_warnings(): + ... warnings.simplefilter('ignore', np.RankWarning) + ... p30 = np.poly1d(np.polyfit(x, y, 30)) + ... >>> p30(4) - -0.80000000000000204 + -0.80000000000000204 # may vary >>> p30(5) - -0.99999999999999445 + -0.99999999999999445 # may vary >>> p30(4.5) - -0.10547061179440398 + -0.10547061179440398 # may vary Illustration: @@ -583,21 +671,24 @@ def polyfit(x, y, deg, rcond=None, full=False, w=None, cov=False): # warn on rank reduction, which indicates an ill conditioned matrix if rank != order and not full: msg = "Polyfit may be poorly conditioned" - warnings.warn(msg, RankWarning, stacklevel=2) + warnings.warn(msg, RankWarning, stacklevel=4) if full: return c, resids, rank, s, rcond elif cov: Vbase = inv(dot(lhs.T, lhs)) Vbase /= NX.outer(scale, scale) - # Some literature ignores the extra -2.0 factor in the denominator, but - # it is included here because the covariance of Multivariate Student-T - # (which is implied by a Bayesian uncertainty analysis) includes it. - # Plus, it gives a slightly more conservative estimate of uncertainty. - if len(x) <= order + 2: - raise ValueError("the number of data points must exceed order + 2 " - "for Bayesian estimate the covariance matrix") - fac = resids / (len(x) - order - 2.0) + if cov == "unscaled": + fac = 1 + else: + if len(x) <= order: + raise ValueError("the number of data points must exceed order " + "to scale the covariance matrix") + # note, this used to be: fac = resids / (len(x) - order - 2.0) + # it was deciced that the "- 2" (originally justified by "Bayesian + # uncertainty analysis") is not was the user expects + # (see gh-11196 and gh-11197) + fac = resids / (len(x) - order) if y.ndim == 1: return c, Vbase * fac else: @@ -606,16 +697,27 @@ def polyfit(x, y, deg, rcond=None, full=False, w=None, cov=False): return c +def _polyval_dispatcher(p, x): + return (p, x) + + +@array_function_dispatch(_polyval_dispatcher) def polyval(p, x): """ Evaluate a polynomial at specific values. + .. note:: + This forms part of the old polynomial API. Since version 1.4, the + new polynomial API defined in `numpy.polynomial` is preferred. + A summary of the differences can be found in the + :doc:`transition guide </reference/routines.polynomials>`. + If `p` is of length N, this function returns the value: ``p[0]*x**(N-1) + p[1]*x**(N-2) + ... + p[N-2]*x + p[N-1]`` - If `x` is a sequence, then `p(x)` is returned for each element of `x`. - If `x` is another polynomial then the composite polynomial `p(x(t))` + If `x` is a sequence, then ``p(x)`` is returned for each element of ``x``. + If `x` is another polynomial then the composite polynomial ``p(x(t))`` is returned. Parameters @@ -647,6 +749,8 @@ def polyval(p, x): for polynomials of high degree the values may be inaccurate due to rounding errors. Use carefully. + If `x` is a subtype of `ndarray` the return value will be of the same type. + References ---------- .. [1] I. N. Bronshtein, K. A. Semendyayev, and K. A. Hirsch (Eng. @@ -658,27 +762,39 @@ def polyval(p, x): >>> np.polyval([3,0,1], 5) # 3 * 5**2 + 0 * 5**1 + 1 76 >>> np.polyval([3,0,1], np.poly1d(5)) - poly1d([ 76.]) + poly1d([76]) >>> np.polyval(np.poly1d([3,0,1]), 5) 76 >>> np.polyval(np.poly1d([3,0,1]), np.poly1d(5)) - poly1d([ 76.]) + poly1d([76]) """ p = NX.asarray(p) if isinstance(x, poly1d): y = 0 else: - x = NX.asarray(x) + x = NX.asanyarray(x) y = NX.zeros_like(x) - for i in range(len(p)): - y = y * x + p[i] + for pv in p: + y = y * x + pv return y + +def _binary_op_dispatcher(a1, a2): + return (a1, a2) + + +@array_function_dispatch(_binary_op_dispatcher) def polyadd(a1, a2): """ Find the sum of two polynomials. + .. note:: + This forms part of the old polynomial API. Since version 1.4, the + new polynomial API defined in `numpy.polynomial` is preferred. + A summary of the differences can be found in the + :doc:`transition guide </reference/routines.polynomials>`. + Returns the polynomial resulting from the sum of two input polynomials. Each input must be either a poly1d object or a 1D sequence of polynomial coefficients, from highest to lowest degree. @@ -735,10 +851,18 @@ def polyadd(a1, a2): val = poly1d(val) return val + +@array_function_dispatch(_binary_op_dispatcher) def polysub(a1, a2): """ Difference (subtraction) of two polynomials. + .. note:: + This forms part of the old polynomial API. Since version 1.4, the + new polynomial API defined in `numpy.polynomial` is preferred. + A summary of the differences can be found in the + :doc:`transition guide </reference/routines.polynomials>`. + Given two polynomials `a1` and `a2`, returns ``a1 - a2``. `a1` and `a2` can be either array_like sequences of the polynomials' coefficients (including coefficients equal to zero), or `poly1d` objects. @@ -782,10 +906,17 @@ def polysub(a1, a2): return val +@array_function_dispatch(_binary_op_dispatcher) def polymul(a1, a2): """ Find the product of two polynomials. + .. note:: + This forms part of the old polynomial API. Since version 1.4, the + new polynomial API defined in `numpy.polynomial` is preferred. + A summary of the differences can be found in the + :doc:`transition guide </reference/routines.polynomials>`. + Finds the polynomial resulting from the multiplication of the two input polynomials. Each input must be either a poly1d object or a 1D sequence of polynomial coefficients, from highest to lowest degree. @@ -806,8 +937,7 @@ def polymul(a1, a2): See Also -------- poly1d : A one-dimensional polynomial class. - poly, polyadd, polyder, polydiv, polyfit, polyint, polysub, - polyval + poly, polyadd, polyder, polydiv, polyfit, polyint, polysub, polyval convolve : Array convolution. Same output as polymul, but has parameter for overlap mode. @@ -838,10 +968,22 @@ def polymul(a1, a2): val = poly1d(val) return val + +def _polydiv_dispatcher(u, v): + return (u, v) + + +@array_function_dispatch(_polydiv_dispatcher) def polydiv(u, v): """ Returns the quotient and remainder of polynomial division. + .. note:: + This forms part of the old polynomial API. Since version 1.4, the + new polynomial API defined in `numpy.polynomial` is preferred. + A summary of the differences can be found in the + :doc:`transition guide </reference/routines.polynomials>`. + The input arrays are the coefficients (including any coefficients equal to zero) of the "numerator" (dividend) and "denominator" (divisor) polynomials, respectively. @@ -863,7 +1005,7 @@ def polydiv(u, v): See Also -------- - poly, polyadd, polyder, polydiv, polyfit, polyint, polymul, polysub, + poly, polyadd, polyder, polydiv, polyfit, polyint, polymul, polysub polyval Notes @@ -880,10 +1022,10 @@ def polydiv(u, v): >>> x = np.array([3.0, 5.0, 2.0]) >>> y = np.array([2.0, 1.0]) >>> np.polydiv(x, y) - (array([ 1.5 , 1.75]), array([ 0.25])) + (array([1.5 , 1.75]), array([0.25])) """ - truepoly = (isinstance(u, poly1d) or isinstance(u, poly1d)) + truepoly = (isinstance(u, poly1d) or isinstance(v, poly1d)) u = atleast_1d(u) + 0.0 v = atleast_1d(v) + 0.0 # w has the common type @@ -903,7 +1045,7 @@ def polydiv(u, v): return poly1d(q), poly1d(r) return q, r -_poly_mat = re.compile(r"[*][*]([0-9]*)") +_poly_mat = re.compile(r"\*\*([0-9]*)") def _raise_power(astr, wrap=70): n = 0 line1 = '' @@ -931,10 +1073,17 @@ def _raise_power(astr, wrap=70): return output + astr[n:] -class poly1d(object): +@set_module('numpy') +class poly1d: """ A one-dimensional polynomial class. + .. note:: + This forms part of the old polynomial API. Since version 1.4, the + new polynomial API defined in `numpy.polynomial` is preferred. + A summary of the differences can be found in the + :doc:`transition guide </reference/routines.polynomials>`. + A convenience class, used to encapsulate "natural" operations on polynomials so that said operations may take on their customary form in code (see Examples). @@ -974,7 +1123,7 @@ class poly1d(object): >>> p.r array([-1.+1.41421356j, -1.-1.41421356j]) >>> p(p.r) - array([ -4.44089210e-16+0.j, -4.44089210e-16+0.j]) + array([ -4.44089210e-16+0.j, -4.44089210e-16+0.j]) # may vary These numbers in the previous line represent (0, 0) to machine precision @@ -1001,7 +1150,7 @@ class poly1d(object): poly1d([ 1, 4, 10, 12, 9]) >>> (p**3 + 4) / p - (poly1d([ 1., 4., 10., 12., 9.]), poly1d([ 4.])) + (poly1d([ 1., 4., 10., 12., 9.]), poly1d([4.])) ``asarray(p)`` gives the coefficient array, so polynomials can be used in all functions that accept arrays: @@ -1023,7 +1172,7 @@ class poly1d(object): Construct a polynomial from its roots: >>> np.poly1d([1, 2], True) - poly1d([ 1, -3, 2]) + poly1d([ 1., -3., 2.]) This is the same polynomial as obtained by: @@ -1035,8 +1184,14 @@ class poly1d(object): @property def coeffs(self): - """ A copy of the polynomial coefficients """ - return self._coeffs.copy() + """ The polynomial coefficients """ + return self._coeffs + + @coeffs.setter + def coeffs(self, value): + # allowing this makes p.coeffs *= 2 legal + if value is not self._coeffs: + raise AttributeError("Cannot set attribute") @property def variable(self): @@ -1089,7 +1244,7 @@ def __init__(self, c_or_r, r=False, variable=None): raise ValueError("Polynomial must be 1d only.") c_or_r = trim_zeros(c_or_r, trim='f') if len(c_or_r) == 0: - c_or_r = NX.array([0.]) + c_or_r = NX.array([0], dtype=c_or_r.dtype) self._coeffs = c_or_r if variable is None: variable = 'x' @@ -1123,14 +1278,14 @@ def fmt_float(q): s = s[:-5] return s - for k in range(len(coeffs)): - if not iscomplex(coeffs[k]): - coefstr = fmt_float(real(coeffs[k])) - elif real(coeffs[k]) == 0: - coefstr = '%sj' % fmt_float(imag(coeffs[k])) + for k, coeff in enumerate(coeffs): + if not iscomplex(coeff): + coefstr = fmt_float(real(coeff)) + elif real(coeff) == 0: + coefstr = '%sj' % fmt_float(imag(coeff)) else: - coefstr = '(%s + %sj)' % (fmt_float(real(coeffs[k])), - fmt_float(imag(coeffs[k]))) + coefstr = '(%s + %sj)' % (fmt_float(real(coeff)), + fmt_float(imag(coeff))) power = (N-k) if power == 0: @@ -1247,9 +1402,9 @@ def __ne__(self, other): def __getitem__(self, val): ind = self.order - val if val > self.order: - return 0 + return self.coeffs.dtype.type(0) if val < 0: - return 0 + return self.coeffs.dtype.type(0) return self.coeffs[ind] def __setitem__(self, key, val): diff --git a/numpy/lib/polynomial.pyi b/numpy/lib/polynomial.pyi new file mode 100644 index 000000000000..00065f53b227 --- /dev/null +++ b/numpy/lib/polynomial.pyi @@ -0,0 +1,305 @@ +from typing import ( + Literal as L, + List, + overload, + Any, + SupportsInt, + SupportsIndex, + TypeVar, + Tuple, + NoReturn, +) + +from numpy import ( + RankWarning as RankWarning, + poly1d as poly1d, + unsignedinteger, + signedinteger, + floating, + complexfloating, + bool_, + int32, + int64, + float64, + complex128, + object_, +) + +from numpy.typing import ( + NDArray, + ArrayLike, + _ArrayLikeBool_co, + _ArrayLikeUInt_co, + _ArrayLikeInt_co, + _ArrayLikeFloat_co, + _ArrayLikeComplex_co, + _ArrayLikeObject_co, +) + +_T = TypeVar("_T") + +_2Tup = Tuple[_T, _T] +_5Tup = Tuple[ + _T, + NDArray[float64], + NDArray[int32], + NDArray[float64], + NDArray[float64], +] + +__all__: List[str] + +def poly(seq_of_zeros: ArrayLike) -> NDArray[floating[Any]]: ... + +# Returns either a float or complex array depending on the input values. +# See `np.linalg.eigvals`. +def roots(p: ArrayLike) -> NDArray[complexfloating[Any, Any]] | NDArray[floating[Any]]: ... + +@overload +def polyint( + p: poly1d, + m: SupportsInt | SupportsIndex = ..., + k: None | _ArrayLikeComplex_co | _ArrayLikeObject_co = ..., +) -> poly1d: ... +@overload +def polyint( + p: _ArrayLikeFloat_co, + m: SupportsInt | SupportsIndex = ..., + k: None | _ArrayLikeFloat_co = ..., +) -> NDArray[floating[Any]]: ... +@overload +def polyint( + p: _ArrayLikeComplex_co, + m: SupportsInt | SupportsIndex = ..., + k: None | _ArrayLikeComplex_co = ..., +) -> NDArray[complexfloating[Any, Any]]: ... +@overload +def polyint( + p: _ArrayLikeObject_co, + m: SupportsInt | SupportsIndex = ..., + k: None | _ArrayLikeObject_co = ..., +) -> NDArray[object_]: ... + +@overload +def polyder( + p: poly1d, + m: SupportsInt | SupportsIndex = ..., +) -> poly1d: ... +@overload +def polyder( + p: _ArrayLikeFloat_co, + m: SupportsInt | SupportsIndex = ..., +) -> NDArray[floating[Any]]: ... +@overload +def polyder( + p: _ArrayLikeComplex_co, + m: SupportsInt | SupportsIndex = ..., +) -> NDArray[complexfloating[Any, Any]]: ... +@overload +def polyder( + p: _ArrayLikeObject_co, + m: SupportsInt | SupportsIndex = ..., +) -> NDArray[object_]: ... + +@overload +def polyfit( + x: _ArrayLikeFloat_co, + y: _ArrayLikeFloat_co, + deg: SupportsIndex | SupportsInt, + rcond: None | float = ..., + full: L[False] = ..., + w: None | _ArrayLikeFloat_co = ..., + cov: L[False] = ..., +) -> NDArray[float64]: ... +@overload +def polyfit( + x: _ArrayLikeComplex_co, + y: _ArrayLikeComplex_co, + deg: SupportsIndex | SupportsInt, + rcond: None | float = ..., + full: L[False] = ..., + w: None | _ArrayLikeFloat_co = ..., + cov: L[False] = ..., +) -> NDArray[complex128]: ... +@overload +def polyfit( + x: _ArrayLikeFloat_co, + y: _ArrayLikeFloat_co, + deg: SupportsIndex | SupportsInt, + rcond: None | float = ..., + full: L[False] = ..., + w: None | _ArrayLikeFloat_co = ..., + cov: L[True, "unscaled"] = ..., +) -> _2Tup[NDArray[float64]]: ... +@overload +def polyfit( + x: _ArrayLikeComplex_co, + y: _ArrayLikeComplex_co, + deg: SupportsIndex | SupportsInt, + rcond: None | float = ..., + full: L[False] = ..., + w: None | _ArrayLikeFloat_co = ..., + cov: L[True, "unscaled"] = ..., +) -> _2Tup[NDArray[complex128]]: ... +@overload +def polyfit( + x: _ArrayLikeFloat_co, + y: _ArrayLikeFloat_co, + deg: SupportsIndex | SupportsInt, + rcond: None | float = ..., + full: L[True] = ..., + w: None | _ArrayLikeFloat_co = ..., + cov: bool | L["unscaled"] = ..., +) -> _5Tup[NDArray[float64]]: ... +@overload +def polyfit( + x: _ArrayLikeComplex_co, + y: _ArrayLikeComplex_co, + deg: SupportsIndex | SupportsInt, + rcond: None | float = ..., + full: L[True] = ..., + w: None | _ArrayLikeFloat_co = ..., + cov: bool | L["unscaled"] = ..., +) -> _5Tup[NDArray[complex128]]: ... + +@overload +def polyval( + p: _ArrayLikeBool_co, + x: _ArrayLikeBool_co, +) -> NDArray[int64]: ... +@overload +def polyval( + p: _ArrayLikeUInt_co, + x: _ArrayLikeUInt_co, +) -> NDArray[unsignedinteger[Any]]: ... +@overload +def polyval( + p: _ArrayLikeInt_co, + x: _ArrayLikeInt_co, +) -> NDArray[signedinteger[Any]]: ... +@overload +def polyval( + p: _ArrayLikeFloat_co, + x: _ArrayLikeFloat_co, +) -> NDArray[floating[Any]]: ... +@overload +def polyval( + p: _ArrayLikeComplex_co, + x: _ArrayLikeComplex_co, +) -> NDArray[complexfloating[Any, Any]]: ... +@overload +def polyval( + p: _ArrayLikeObject_co, + x: _ArrayLikeObject_co, +) -> NDArray[object_]: ... + +@overload +def polyadd( + a1: poly1d, + a2: _ArrayLikeComplex_co | _ArrayLikeObject_co, +) -> poly1d: ... +@overload +def polyadd( + a1: _ArrayLikeComplex_co | _ArrayLikeObject_co, + a2: poly1d, +) -> poly1d: ... +@overload +def polyadd( + a1: _ArrayLikeBool_co, + a2: _ArrayLikeBool_co, +) -> NDArray[bool_]: ... +@overload +def polyadd( + a1: _ArrayLikeUInt_co, + a2: _ArrayLikeUInt_co, +) -> NDArray[unsignedinteger[Any]]: ... +@overload +def polyadd( + a1: _ArrayLikeInt_co, + a2: _ArrayLikeInt_co, +) -> NDArray[signedinteger[Any]]: ... +@overload +def polyadd( + a1: _ArrayLikeFloat_co, + a2: _ArrayLikeFloat_co, +) -> NDArray[floating[Any]]: ... +@overload +def polyadd( + a1: _ArrayLikeComplex_co, + a2: _ArrayLikeComplex_co, +) -> NDArray[complexfloating[Any, Any]]: ... +@overload +def polyadd( + a1: _ArrayLikeObject_co, + a2: _ArrayLikeObject_co, +) -> NDArray[object_]: ... + +@overload +def polysub( + a1: poly1d, + a2: _ArrayLikeComplex_co | _ArrayLikeObject_co, +) -> poly1d: ... +@overload +def polysub( + a1: _ArrayLikeComplex_co | _ArrayLikeObject_co, + a2: poly1d, +) -> poly1d: ... +@overload +def polysub( + a1: _ArrayLikeBool_co, + a2: _ArrayLikeBool_co, +) -> NoReturn: ... +@overload +def polysub( + a1: _ArrayLikeUInt_co, + a2: _ArrayLikeUInt_co, +) -> NDArray[unsignedinteger[Any]]: ... +@overload +def polysub( + a1: _ArrayLikeInt_co, + a2: _ArrayLikeInt_co, +) -> NDArray[signedinteger[Any]]: ... +@overload +def polysub( + a1: _ArrayLikeFloat_co, + a2: _ArrayLikeFloat_co, +) -> NDArray[floating[Any]]: ... +@overload +def polysub( + a1: _ArrayLikeComplex_co, + a2: _ArrayLikeComplex_co, +) -> NDArray[complexfloating[Any, Any]]: ... +@overload +def polysub( + a1: _ArrayLikeObject_co, + a2: _ArrayLikeObject_co, +) -> NDArray[object_]: ... + +# NOTE: Not an alias, but they do have the same signature (that we can reuse) +polymul = polyadd + +@overload +def polydiv( + u: poly1d, + v: _ArrayLikeComplex_co | _ArrayLikeObject_co, +) -> _2Tup[poly1d]: ... +@overload +def polydiv( + u: _ArrayLikeComplex_co | _ArrayLikeObject_co, + v: poly1d, +) -> _2Tup[poly1d]: ... +@overload +def polydiv( + u: _ArrayLikeFloat_co, + v: _ArrayLikeFloat_co, +) -> _2Tup[NDArray[floating[Any]]]: ... +@overload +def polydiv( + u: _ArrayLikeComplex_co, + v: _ArrayLikeComplex_co, +) -> _2Tup[NDArray[complexfloating[Any, Any]]]: ... +@overload +def polydiv( + u: _ArrayLikeObject_co, + v: _ArrayLikeObject_co, +) -> _2Tup[NDArray[Any]]: ... diff --git a/numpy/lib/recfunctions.py b/numpy/lib/recfunctions.py index b6453d5a2d64..a491f612e075 100644 --- a/numpy/lib/recfunctions.py +++ b/numpy/lib/recfunctions.py @@ -5,32 +5,35 @@ matplotlib. They have been rewritten and extended for convenience. """ -from __future__ import division, absolute_import, print_function - -import sys import itertools import numpy as np import numpy.ma as ma from numpy import ndarray, recarray from numpy.ma import MaskedArray from numpy.ma.mrecords import MaskedRecords +from numpy.core.overrides import array_function_dispatch from numpy.lib._iotools import _is_string_like -from numpy.compat import basestring - -if sys.version_info[0] < 3: - from future_builtins import zip +from numpy.testing import suppress_warnings _check_fill_value = np.ma.core._check_fill_value __all__ = [ - 'append_fields', 'drop_fields', 'find_duplicates', - 'get_fieldstructure', 'join_by', 'merge_arrays', - 'rec_append_fields', 'rec_drop_fields', 'rec_join', - 'recursive_fill_fields', 'rename_fields', 'stack_arrays', + 'append_fields', 'apply_along_fields', 'assign_fields_by_name', + 'drop_fields', 'find_duplicates', 'flatten_descr', + 'get_fieldstructure', 'get_names', 'get_names_flat', + 'join_by', 'merge_arrays', 'rec_append_fields', + 'rec_drop_fields', 'rec_join', 'recursive_fill_fields', + 'rename_fields', 'repack_fields', 'require_fields', + 'stack_arrays', 'structured_to_unstructured', 'unstructured_to_structured', ] +def _recursive_fill_fields_dispatcher(input, output): + return (input, output) + + +@array_function_dispatch(_recursive_fill_fields_dispatcher) def recursive_fill_fields(input, output): """ Fills fields from output with fields from input, @@ -50,11 +53,10 @@ def recursive_fill_fields(input, output): Examples -------- >>> from numpy.lib import recfunctions as rfn - >>> a = np.array([(1, 10.), (2, 20.)], dtype=[('A', int), ('B', float)]) + >>> a = np.array([(1, 10.), (2, 20.)], dtype=[('A', np.int64), ('B', np.float64)]) >>> b = np.zeros((3,), dtype=a.dtype) >>> rfn.recursive_fill_fields(a, b) - array([(1, 10.0), (2, 20.0), (0, 0.0)], - dtype=[('A', '<i4'), ('B', '<f8')]) + array([(1, 10.), (2, 20.), (0, 0.)], dtype=[('A', '<i8'), ('B', '<f8')]) """ newdtype = output.dtype @@ -63,14 +65,14 @@ def recursive_fill_fields(input, output): current = input[field] except ValueError: continue - if current.dtype.names: + if current.dtype.names is not None: recursive_fill_fields(current, output[field]) else: output[field][:len(current)] = current return output -def get_fieldspec(dtype): +def _get_fieldspec(dtype): """ Produce a list of name/dtype pairs corresponding to the dtype fields @@ -82,11 +84,11 @@ def get_fieldspec(dtype): Examples -------- - >>> dt = np.dtype([(('a', 'A'), int), ('b', float, 3)]) + >>> dt = np.dtype([(('a', 'A'), np.int64), ('b', np.double, 3)]) >>> dt.descr - [(('a', 'A'), '<i4'), ('b', '<f8', (3,))] - >>> get_fieldspec(dt) - [(('a', 'A'), dtype('int32')), ('b', dtype(('<f8', (3,))))] + [(('a', 'A'), '<i8'), ('b', '<f8', (3,))] + >>> _get_fieldspec(dt) + [(('a', 'A'), dtype('int64')), ('b', dtype(('<f8', (3,))))] """ if dtype.names is None: @@ -96,7 +98,7 @@ def get_fieldspec(dtype): fields = ((name, dtype.fields[name]) for name in dtype.names) # keep any titles, if present return [ - (name if len(f) == 2 else (f[2], name), f[0]) + (name if len(f) == 2 else (f[2], name), f[0]) for name, f in fields ] @@ -113,10 +115,15 @@ def get_names(adtype): Examples -------- >>> from numpy.lib import recfunctions as rfn - >>> rfn.get_names(np.empty((1,), dtype=int)) is None - True + >>> rfn.get_names(np.empty((1,), dtype=int)) + Traceback (most recent call last): + ... + AttributeError: 'numpy.ndarray' object has no attribute 'names' + >>> rfn.get_names(np.empty((1,), dtype=[('A',int), ('B', float)])) - ('A', 'B') + Traceback (most recent call last): + ... + AttributeError: 'numpy.ndarray' object has no attribute 'names' >>> adtype = np.dtype([('a', int), ('b', [('ba', int), ('bb', int)])]) >>> rfn.get_names(adtype) ('a', ('b', ('ba', 'bb'))) @@ -125,17 +132,17 @@ def get_names(adtype): names = adtype.names for name in names: current = adtype[name] - if current.names: + if current.names is not None: listnames.append((name, tuple(get_names(current)))) else: listnames.append(name) - return tuple(listnames) or None + return tuple(listnames) def get_names_flat(adtype): """ Returns the field names of the input datatype as a tuple. Nested structure - are flattend beforehand. + are flattened beforehand. Parameters ---------- @@ -146,9 +153,13 @@ def get_names_flat(adtype): -------- >>> from numpy.lib import recfunctions as rfn >>> rfn.get_names_flat(np.empty((1,), dtype=int)) is None - True + Traceback (most recent call last): + ... + AttributeError: 'numpy.ndarray' object has no attribute 'names' >>> rfn.get_names_flat(np.empty((1,), dtype=[('A',int), ('B', float)])) - ('A', 'B') + Traceback (most recent call last): + ... + AttributeError: 'numpy.ndarray' object has no attribute 'names' >>> adtype = np.dtype([('a', int), ('b', [('ba', int), ('bb', int)])]) >>> rfn.get_names_flat(adtype) ('a', 'b', 'ba', 'bb') @@ -158,9 +169,9 @@ def get_names_flat(adtype): for name in names: listnames.append(name) current = adtype[name] - if current.names: + if current.names is not None: listnames.extend(get_names_flat(current)) - return tuple(listnames) or None + return tuple(listnames) def flatten_descr(ndtype): @@ -182,14 +193,14 @@ def flatten_descr(ndtype): descr = [] for field in names: (typ, _) = ndtype.fields[field] - if typ.names: + if typ.names is not None: descr.extend(flatten_descr(typ)) else: descr.append((field, typ)) return tuple(descr) -def zip_dtype(seqarrays, flatten=False): +def _zip_dtype(seqarrays, flatten=False): newdtype = [] if flatten: for a in seqarrays: @@ -197,15 +208,15 @@ def zip_dtype(seqarrays, flatten=False): else: for a in seqarrays: current = a.dtype - if current.names and len(current.names) <= 1: - # special case - dtypes of 0 or 1 field are flattened - newdtype.extend(get_fieldspec(current)) + if current.names is not None and len(current.names) == 1: + # special case - dtypes of 1 field are flattened + newdtype.extend(_get_fieldspec(current)) else: newdtype.append(('', current)) return np.dtype(newdtype) -def zip_descr(seqarrays, flatten=False): +def _zip_descr(seqarrays, flatten=False): """ Combine the dtype description of a series of arrays. @@ -216,7 +227,7 @@ def zip_descr(seqarrays, flatten=False): flatten : {boolean}, optional Whether to collapse nested descriptions. """ - return zip_dtype(seqarrays, flatten=flatten).descr + return _zip_dtype(seqarrays, flatten=flatten).descr def get_fieldstructure(adtype, lastname=None, parents=None,): @@ -250,7 +261,7 @@ def get_fieldstructure(adtype, lastname=None, parents=None,): names = adtype.names for name in names: current = adtype[name] - if current.names: + if current.names is not None: if lastname: parents[name] = [lastname, ] else: @@ -263,7 +274,7 @@ def get_fieldstructure(adtype, lastname=None, parents=None,): elif lastname: lastparent = [lastname, ] parents[name] = lastparent or [] - return parents or None + return parents def _izip_fields_flat(iterable): @@ -274,8 +285,7 @@ def _izip_fields_flat(iterable): """ for element in iterable: if isinstance(element, np.void): - for f in _izip_fields_flat(tuple(element)): - yield f + yield from _izip_fields_flat(tuple(element)) else: yield element @@ -287,17 +297,16 @@ def _izip_fields(iterable): """ for element in iterable: if (hasattr(element, '__iter__') and - not isinstance(element, basestring)): - for f in _izip_fields(element): - yield f + not isinstance(element, str)): + yield from _izip_fields(element) elif isinstance(element, np.void) and len(tuple(element)) == 1: - for f in _izip_fields(element): - yield f + # this statement is the same from the previous expression + yield from _izip_fields(element) else: yield element -def izip_records(seqarrays, fill_value=None, flatten=True): +def _izip_records(seqarrays, fill_value=None, flatten=True): """ Returns an iterator of concatenated items from a sequence of arrays. @@ -317,12 +326,7 @@ def izip_records(seqarrays, fill_value=None, flatten=True): else: zipfunc = _izip_fields - if sys.version_info[0] >= 3: - zip_longest = itertools.zip_longest - else: - zip_longest = itertools.izip_longest - - for tup in zip_longest(*seqarrays, fillvalue=fill_value): + for tup in itertools.zip_longest(*seqarrays, fillvalue=fill_value): yield tuple(zipfunc(tup)) @@ -357,6 +361,12 @@ def _fix_defaults(output, defaults=None): return output +def _merge_arrays_dispatcher(seqarrays, fill_value=None, flatten=None, + usemask=None, asrecarray=None): + return seqarrays + + +@array_function_dispatch(_merge_arrays_dispatcher) def merge_arrays(seqarrays, fill_value=-1, flatten=False, usemask=False, asrecarray=False): """ @@ -379,20 +389,18 @@ def merge_arrays(seqarrays, fill_value=-1, flatten=False, -------- >>> from numpy.lib import recfunctions as rfn >>> rfn.merge_arrays((np.array([1, 2]), np.array([10., 20., 30.]))) - masked_array(data = [(1, 10.0) (2, 20.0) (--, 30.0)], - mask = [(False, False) (False, False) (True, False)], - fill_value = (999999, 1e+20), - dtype = [('f0', '<i4'), ('f1', '<f8')]) - - >>> rfn.merge_arrays((np.array([1, 2]), np.array([10., 20., 30.])), - ... usemask=False) - array([(1, 10.0), (2, 20.0), (-1, 30.0)], - dtype=[('f0', '<i4'), ('f1', '<f8')]) - >>> rfn.merge_arrays((np.array([1, 2]).view([('a', int)]), + array([( 1, 10.), ( 2, 20.), (-1, 30.)], + dtype=[('f0', '<i8'), ('f1', '<f8')]) + + >>> rfn.merge_arrays((np.array([1, 2], dtype=np.int64), + ... np.array([10., 20., 30.])), usemask=False) + array([(1, 10.0), (2, 20.0), (-1, 30.0)], + dtype=[('f0', '<i8'), ('f1', '<f8')]) + >>> rfn.merge_arrays((np.array([1, 2]).view([('a', np.int64)]), ... np.array([10., 20., 30.])), ... usemask=False, asrecarray=True) - rec.array([(1, 10.0), (2, 20.0), (-1, 30.0)], - dtype=[('a', '<i4'), ('f1', '<f8')]) + rec.array([( 1, 10.), ( 2, 20.), (-1, 30.)], + dtype=[('a', '<i8'), ('f1', '<f8')]) Notes ----- @@ -413,10 +421,10 @@ def merge_arrays(seqarrays, fill_value=-1, flatten=False, if isinstance(seqarrays, (ndarray, np.void)): seqdtype = seqarrays.dtype # Make sure we have named fields - if not seqdtype.names: + if seqdtype.names is None: seqdtype = np.dtype([('', seqdtype)]) - if not flatten or zip_dtype((seqarrays,), flatten=True) == seqdtype: - # Minimal processing needed: just make sure everythng's a-ok + if not flatten or _zip_dtype((seqarrays,), flatten=True) == seqdtype: + # Minimal processing needed: just make sure everything's a-ok seqarrays = seqarrays.ravel() # Find what type of array we must return if usemask: @@ -438,7 +446,7 @@ def merge_arrays(seqarrays, fill_value=-1, flatten=False, sizes = tuple(a.size for a in seqarrays) maxlength = max(sizes) # Get the dtype of the output (flattening if needed) - newdtype = zip_dtype(seqarrays, flatten=flatten) + newdtype = _zip_dtype(seqarrays, flatten=flatten) # Initialize the sequences for data and mask seqdata = [] seqmask = [] @@ -466,9 +474,9 @@ def merge_arrays(seqarrays, fill_value=-1, flatten=False, seqdata.append(itertools.chain(data, [fval] * nbmissing)) seqmask.append(itertools.chain(mask, [fmsk] * nbmissing)) # Create an iterator for the data - data = tuple(izip_records(seqdata, flatten=flatten)) + data = tuple(_izip_records(seqdata, flatten=flatten)) output = ma.array(np.fromiter(data, dtype=newdtype, count=maxlength), - mask=list(izip_records(seqmask, flatten=flatten))) + mask=list(_izip_records(seqmask, flatten=flatten))) if asrecarray: output = output.view(MaskedRecords) else: @@ -486,7 +494,7 @@ def merge_arrays(seqarrays, fill_value=-1, flatten=False, else: fval = None seqdata.append(itertools.chain(data, [fval] * nbmissing)) - output = np.fromiter(tuple(izip_records(seqdata, flatten=flatten)), + output = np.fromiter(tuple(_izip_records(seqdata, flatten=flatten)), dtype=newdtype, count=maxlength) if asrecarray: output = output.view(recarray) @@ -494,12 +502,21 @@ def merge_arrays(seqarrays, fill_value=-1, flatten=False, return output +def _drop_fields_dispatcher(base, drop_names, usemask=None, asrecarray=None): + return (base,) + + +@array_function_dispatch(_drop_fields_dispatcher) def drop_fields(base, drop_names, usemask=True, asrecarray=False): """ Return a new array with fields in `drop_names` dropped. Nested fields are supported. + .. versionchanged:: 1.18.0 + `drop_fields` returns an array with 0 fields if all fields are dropped, + rather than returning ``None`` as it did previously. + Parameters ---------- base : array @@ -518,16 +535,14 @@ def drop_fields(base, drop_names, usemask=True, asrecarray=False): -------- >>> from numpy.lib import recfunctions as rfn >>> a = np.array([(1, (2, 3.0)), (4, (5, 6.0))], - ... dtype=[('a', int), ('b', [('ba', float), ('bb', int)])]) + ... dtype=[('a', np.int64), ('b', [('ba', np.double), ('bb', np.int64)])]) >>> rfn.drop_fields(a, 'a') - array([((2.0, 3),), ((5.0, 6),)], - dtype=[('b', [('ba', '<f8'), ('bb', '<i4')])]) + array([((2., 3),), ((5., 6),)], + dtype=[('b', [('ba', '<f8'), ('bb', '<i8')])]) >>> rfn.drop_fields(a, 'ba') - array([(1, (3,)), (4, (6,))], - dtype=[('a', '<i4'), ('b', [('bb', '<i4')])]) + array([(1, (3,)), (4, (6,))], dtype=[('a', '<i8'), ('b', [('bb', '<i8')])]) >>> rfn.drop_fields(a, ['ba', 'bb']) - array([(1,), (4,)], - dtype=[('a', '<i4')]) + array([(1,), (4,)], dtype=[('a', '<i8')]) """ if _is_string_like(drop_names): drop_names = [drop_names] @@ -541,7 +556,7 @@ def _drop_descr(ndtype, drop_names): current = ndtype[name] if name in drop_names: continue - if current.names: + if current.names is not None: descr = _drop_descr(current, drop_names) if descr: newdtype.append((name, descr)) @@ -550,8 +565,6 @@ def _drop_descr(ndtype, drop_names): return newdtype newdtype = _drop_descr(base.dtype, drop_names) - if not newdtype: - return None output = np.empty(base.shape, dtype=newdtype) output = recursive_fill_fields(base, output) @@ -583,6 +596,11 @@ def _keep_fields(base, keep_names, usemask=True, asrecarray=False): return _fix_output(output, usemask=usemask, asrecarray=asrecarray) +def _rec_drop_fields_dispatcher(base, drop_names): + return (base,) + + +@array_function_dispatch(_rec_drop_fields_dispatcher) def rec_drop_fields(base, drop_names): """ Returns a new numpy.recarray with fields in `drop_names` dropped. @@ -590,6 +608,11 @@ def rec_drop_fields(base, drop_names): return drop_fields(base, drop_names, usemask=False, asrecarray=True) +def _rename_fields_dispatcher(base, namemapper): + return (base,) + + +@array_function_dispatch(_rename_fields_dispatcher) def rename_fields(base, namemapper): """ Rename the fields from a flexible-datatype ndarray or recarray. @@ -609,8 +632,8 @@ def rename_fields(base, namemapper): >>> a = np.array([(1, (2, [3.0, 30.])), (4, (5, [6.0, 60.]))], ... dtype=[('a', int),('b', [('ba', float), ('bb', (float, 2))])]) >>> rfn.rename_fields(a, {'a':'A', 'bb':'BB'}) - array([(1, (2.0, [3.0, 30.0])), (4, (5.0, [6.0, 60.0]))], - dtype=[('A', '<i4'), ('b', [('ba', '<f8'), ('BB', '<f8', 2)])]) + array([(1, (2., [ 3., 30.])), (4, (5., [ 6., 60.]))], + dtype=[('A', '<i8'), ('b', [('ba', '<f8'), ('BB', '<f8', (2,))])]) """ def _recursive_rename_fields(ndtype, namemapper): @@ -618,7 +641,7 @@ def _recursive_rename_fields(ndtype, namemapper): for name in ndtype.names: newname = namemapper.get(name, name) current = ndtype[name] - if current.names: + if current.names is not None: newdtype.append( (newname, _recursive_rename_fields(current, namemapper)) ) @@ -629,6 +652,13 @@ def _recursive_rename_fields(ndtype, namemapper): return base.view(newdtype) +def _append_fields_dispatcher(base, names, data, dtypes=None, + fill_value=None, usemask=None, asrecarray=None): + yield base + yield from data + + +@array_function_dispatch(_append_fields_dispatcher) def append_fields(base, names, data, dtypes=None, fill_value=-1, usemask=True, asrecarray=False): """ @@ -664,7 +694,7 @@ def append_fields(base, names, data, dtypes=None, if len(names) != len(data): msg = "The number of arrays does not match the number of names" raise ValueError(msg) - elif isinstance(names, basestring): + elif isinstance(names, str): names = [names, ] data = [data, ] # @@ -692,13 +722,19 @@ def append_fields(base, names, data, dtypes=None, # output = ma.masked_all( max(len(base), len(data)), - dtype=get_fieldspec(base.dtype) + get_fieldspec(data.dtype)) + dtype=_get_fieldspec(base.dtype) + _get_fieldspec(data.dtype)) output = recursive_fill_fields(base, output) output = recursive_fill_fields(data, output) # return _fix_output(output, usemask=usemask, asrecarray=asrecarray) +def _rec_append_fields_dispatcher(base, names, data, dtypes=None): + yield base + yield from data + + +@array_function_dispatch(_rec_append_fields_dispatcher) def rec_append_fields(base, names, data, dtypes=None): """ Add new fields to an existing array. @@ -732,6 +768,12 @@ def rec_append_fields(base, names, data, dtypes=None): return append_fields(base, names, data=data, dtypes=dtypes, asrecarray=True, usemask=False) + +def _repack_fields_dispatcher(a, align=None, recurse=None): + return (a,) + + +@array_function_dispatch(_repack_fields_dispatcher) def repack_fields(a, align=False, recurse=False): """ Re-pack the fields of a structured array or dtype in memory. @@ -770,22 +812,24 @@ def repack_fields(a, align=False, recurse=False): Examples -------- + >>> from numpy.lib import recfunctions as rfn >>> def print_offsets(d): ... print("offsets:", [d.fields[name][1] for name in d.names]) ... print("itemsize:", d.itemsize) ... - >>> dt = np.dtype('u1,i4,f4', align=True) + >>> dt = np.dtype('u1, <i8, <f8', align=True) >>> dt - dtype({'names':['f0','f1','f2'], 'formats':['u1','<i4','<f8'], 'offsets':[0,4,8], 'itemsize':16}, align=True) + dtype({'names': ['f0', 'f1', 'f2'], 'formats': ['u1', '<i8', '<f8'], \ +'offsets': [0, 8, 16], 'itemsize': 24}, align=True) >>> print_offsets(dt) - offsets: [0, 4, 8] - itemsize: 16 - >>> packed_dt = repack_fields(dt) + offsets: [0, 8, 16] + itemsize: 24 + >>> packed_dt = rfn.repack_fields(dt) >>> packed_dt - dtype([('f0', 'u1'), ('f1', '<i4'), ('f2', '<f8')]) + dtype([('f0', 'u1'), ('f1', '<i8'), ('f2', '<f8')]) >>> print_offsets(packed_dt) - offsets: [0, 1, 5] - itemsize: 13 + offsets: [0, 1, 9] + itemsize: 17 """ if not isinstance(a, np.dtype): @@ -811,6 +855,388 @@ def repack_fields(a, align=False, recurse=False): dt = np.dtype(fieldinfo, align=align) return np.dtype((a.type, dt)) +def _get_fields_and_offsets(dt, offset=0): + """ + Returns a flat list of (dtype, count, offset) tuples of all the + scalar fields in the dtype "dt", including nested fields, in left + to right order. + """ + + # counts up elements in subarrays, including nested subarrays, and returns + # base dtype and count + def count_elem(dt): + count = 1 + while dt.shape != (): + for size in dt.shape: + count *= size + dt = dt.base + return dt, count + + fields = [] + for name in dt.names: + field = dt.fields[name] + f_dt, f_offset = field[0], field[1] + f_dt, n = count_elem(f_dt) + + if f_dt.names is None: + fields.append((np.dtype((f_dt, (n,))), n, f_offset + offset)) + else: + subfields = _get_fields_and_offsets(f_dt, f_offset + offset) + size = f_dt.itemsize + + for i in range(n): + if i == 0: + # optimization: avoid list comprehension if no subarray + fields.extend(subfields) + else: + fields.extend([(d, c, o + i*size) for d, c, o in subfields]) + return fields + + +def _structured_to_unstructured_dispatcher(arr, dtype=None, copy=None, + casting=None): + return (arr,) + +@array_function_dispatch(_structured_to_unstructured_dispatcher) +def structured_to_unstructured(arr, dtype=None, copy=False, casting='unsafe'): + """ + Converts an n-D structured array into an (n+1)-D unstructured array. + + The new array will have a new last dimension equal in size to the + number of field-elements of the input array. If not supplied, the output + datatype is determined from the numpy type promotion rules applied to all + the field datatypes. + + Nested fields, as well as each element of any subarray fields, all count + as a single field-elements. + + Parameters + ---------- + arr : ndarray + Structured array or dtype to convert. Cannot contain object datatype. + dtype : dtype, optional + The dtype of the output unstructured array. + copy : bool, optional + See copy argument to `ndarray.astype`. If true, always return a copy. + If false, and `dtype` requirements are satisfied, a view is returned. + casting : {'no', 'equiv', 'safe', 'same_kind', 'unsafe'}, optional + See casting argument of `ndarray.astype`. Controls what kind of data + casting may occur. + + Returns + ------- + unstructured : ndarray + Unstructured array with one more dimension. + + Examples + -------- + + >>> from numpy.lib import recfunctions as rfn + >>> a = np.zeros(4, dtype=[('a', 'i4'), ('b', 'f4,u2'), ('c', 'f4', 2)]) + >>> a + array([(0, (0., 0), [0., 0.]), (0, (0., 0), [0., 0.]), + (0, (0., 0), [0., 0.]), (0, (0., 0), [0., 0.])], + dtype=[('a', '<i4'), ('b', [('f0', '<f4'), ('f1', '<u2')]), ('c', '<f4', (2,))]) + >>> rfn.structured_to_unstructured(a) + array([[0., 0., 0., 0., 0.], + [0., 0., 0., 0., 0.], + [0., 0., 0., 0., 0.], + [0., 0., 0., 0., 0.]]) + + >>> b = np.array([(1, 2, 5), (4, 5, 7), (7, 8 ,11), (10, 11, 12)], + ... dtype=[('x', 'i4'), ('y', 'f4'), ('z', 'f8')]) + >>> np.mean(rfn.structured_to_unstructured(b[['x', 'z']]), axis=-1) + array([ 3. , 5.5, 9. , 11. ]) + + """ + if arr.dtype.names is None: + raise ValueError('arr must be a structured array') + + fields = _get_fields_and_offsets(arr.dtype) + n_fields = len(fields) + if n_fields == 0 and dtype is None: + raise ValueError("arr has no fields. Unable to guess dtype") + elif n_fields == 0: + # too many bugs elsewhere for this to work now + raise NotImplementedError("arr with no fields is not supported") + + dts, counts, offsets = zip(*fields) + names = ['f{}'.format(n) for n in range(n_fields)] + + if dtype is None: + out_dtype = np.result_type(*[dt.base for dt in dts]) + else: + out_dtype = dtype + + # Use a series of views and casts to convert to an unstructured array: + + # first view using flattened fields (doesn't work for object arrays) + # Note: dts may include a shape for subarrays + flattened_fields = np.dtype({'names': names, + 'formats': dts, + 'offsets': offsets, + 'itemsize': arr.dtype.itemsize}) + with suppress_warnings() as sup: # until 1.16 (gh-12447) + sup.filter(FutureWarning, "Numpy has detected") + arr = arr.view(flattened_fields) + + # next cast to a packed format with all fields converted to new dtype + packed_fields = np.dtype({'names': names, + 'formats': [(out_dtype, dt.shape) for dt in dts]}) + arr = arr.astype(packed_fields, copy=copy, casting=casting) + + # finally is it safe to view the packed fields as the unstructured type + return arr.view((out_dtype, (sum(counts),))) + + +def _unstructured_to_structured_dispatcher(arr, dtype=None, names=None, + align=None, copy=None, casting=None): + return (arr,) + +@array_function_dispatch(_unstructured_to_structured_dispatcher) +def unstructured_to_structured(arr, dtype=None, names=None, align=False, + copy=False, casting='unsafe'): + """ + Converts an n-D unstructured array into an (n-1)-D structured array. + + The last dimension of the input array is converted into a structure, with + number of field-elements equal to the size of the last dimension of the + input array. By default all output fields have the input array's dtype, but + an output structured dtype with an equal number of fields-elements can be + supplied instead. + + Nested fields, as well as each element of any subarray fields, all count + towards the number of field-elements. + + Parameters + ---------- + arr : ndarray + Unstructured array or dtype to convert. + dtype : dtype, optional + The structured dtype of the output array + names : list of strings, optional + If dtype is not supplied, this specifies the field names for the output + dtype, in order. The field dtypes will be the same as the input array. + align : boolean, optional + Whether to create an aligned memory layout. + copy : bool, optional + See copy argument to `ndarray.astype`. If true, always return a copy. + If false, and `dtype` requirements are satisfied, a view is returned. + casting : {'no', 'equiv', 'safe', 'same_kind', 'unsafe'}, optional + See casting argument of `ndarray.astype`. Controls what kind of data + casting may occur. + + Returns + ------- + structured : ndarray + Structured array with fewer dimensions. + + Examples + -------- + + >>> from numpy.lib import recfunctions as rfn + >>> dt = np.dtype([('a', 'i4'), ('b', 'f4,u2'), ('c', 'f4', 2)]) + >>> a = np.arange(20).reshape((4,5)) + >>> a + array([[ 0, 1, 2, 3, 4], + [ 5, 6, 7, 8, 9], + [10, 11, 12, 13, 14], + [15, 16, 17, 18, 19]]) + >>> rfn.unstructured_to_structured(a, dt) + array([( 0, ( 1., 2), [ 3., 4.]), ( 5, ( 6., 7), [ 8., 9.]), + (10, (11., 12), [13., 14.]), (15, (16., 17), [18., 19.])], + dtype=[('a', '<i4'), ('b', [('f0', '<f4'), ('f1', '<u2')]), ('c', '<f4', (2,))]) + + """ + if arr.shape == (): + raise ValueError('arr must have at least one dimension') + n_elem = arr.shape[-1] + if n_elem == 0: + # too many bugs elsewhere for this to work now + raise NotImplementedError("last axis with size 0 is not supported") + + if dtype is None: + if names is None: + names = ['f{}'.format(n) for n in range(n_elem)] + out_dtype = np.dtype([(n, arr.dtype) for n in names], align=align) + fields = _get_fields_and_offsets(out_dtype) + dts, counts, offsets = zip(*fields) + else: + if names is not None: + raise ValueError("don't supply both dtype and names") + # sanity check of the input dtype + fields = _get_fields_and_offsets(dtype) + if len(fields) == 0: + dts, counts, offsets = [], [], [] + else: + dts, counts, offsets = zip(*fields) + + if n_elem != sum(counts): + raise ValueError('The length of the last dimension of arr must ' + 'be equal to the number of fields in dtype') + out_dtype = dtype + if align and not out_dtype.isalignedstruct: + raise ValueError("align was True but dtype is not aligned") + + names = ['f{}'.format(n) for n in range(len(fields))] + + # Use a series of views and casts to convert to a structured array: + + # first view as a packed structured array of one dtype + packed_fields = np.dtype({'names': names, + 'formats': [(arr.dtype, dt.shape) for dt in dts]}) + arr = np.ascontiguousarray(arr).view(packed_fields) + + # next cast to an unpacked but flattened format with varied dtypes + flattened_fields = np.dtype({'names': names, + 'formats': dts, + 'offsets': offsets, + 'itemsize': out_dtype.itemsize}) + arr = arr.astype(flattened_fields, copy=copy, casting=casting) + + # finally view as the final nested dtype and remove the last axis + return arr.view(out_dtype)[..., 0] + +def _apply_along_fields_dispatcher(func, arr): + return (arr,) + +@array_function_dispatch(_apply_along_fields_dispatcher) +def apply_along_fields(func, arr): + """ + Apply function 'func' as a reduction across fields of a structured array. + + This is similar to `apply_along_axis`, but treats the fields of a + structured array as an extra axis. The fields are all first cast to a + common type following the type-promotion rules from `numpy.result_type` + applied to the field's dtypes. + + Parameters + ---------- + func : function + Function to apply on the "field" dimension. This function must + support an `axis` argument, like np.mean, np.sum, etc. + arr : ndarray + Structured array for which to apply func. + + Returns + ------- + out : ndarray + Result of the recution operation + + Examples + -------- + + >>> from numpy.lib import recfunctions as rfn + >>> b = np.array([(1, 2, 5), (4, 5, 7), (7, 8 ,11), (10, 11, 12)], + ... dtype=[('x', 'i4'), ('y', 'f4'), ('z', 'f8')]) + >>> rfn.apply_along_fields(np.mean, b) + array([ 2.66666667, 5.33333333, 8.66666667, 11. ]) + >>> rfn.apply_along_fields(np.mean, b[['x', 'z']]) + array([ 3. , 5.5, 9. , 11. ]) + + """ + if arr.dtype.names is None: + raise ValueError('arr must be a structured array') + + uarr = structured_to_unstructured(arr) + return func(uarr, axis=-1) + # works and avoids axis requirement, but very, very slow: + #return np.apply_along_axis(func, -1, uarr) + +def _assign_fields_by_name_dispatcher(dst, src, zero_unassigned=None): + return dst, src + +@array_function_dispatch(_assign_fields_by_name_dispatcher) +def assign_fields_by_name(dst, src, zero_unassigned=True): + """ + Assigns values from one structured array to another by field name. + + Normally in numpy >= 1.14, assignment of one structured array to another + copies fields "by position", meaning that the first field from the src is + copied to the first field of the dst, and so on, regardless of field name. + + This function instead copies "by field name", such that fields in the dst + are assigned from the identically named field in the src. This applies + recursively for nested structures. This is how structure assignment worked + in numpy >= 1.6 to <= 1.13. + + Parameters + ---------- + dst : ndarray + src : ndarray + The source and destination arrays during assignment. + zero_unassigned : bool, optional + If True, fields in the dst for which there was no matching + field in the src are filled with the value 0 (zero). This + was the behavior of numpy <= 1.13. If False, those fields + are not modified. + """ + + if dst.dtype.names is None: + dst[...] = src + return + + for name in dst.dtype.names: + if name not in src.dtype.names: + if zero_unassigned: + dst[name] = 0 + else: + assign_fields_by_name(dst[name], src[name], + zero_unassigned) + +def _require_fields_dispatcher(array, required_dtype): + return (array,) + +@array_function_dispatch(_require_fields_dispatcher) +def require_fields(array, required_dtype): + """ + Casts a structured array to a new dtype using assignment by field-name. + + This function assigns from the old to the new array by name, so the + value of a field in the output array is the value of the field with the + same name in the source array. This has the effect of creating a new + ndarray containing only the fields "required" by the required_dtype. + + If a field name in the required_dtype does not exist in the + input array, that field is created and set to 0 in the output array. + + Parameters + ---------- + a : ndarray + array to cast + required_dtype : dtype + datatype for output array + + Returns + ------- + out : ndarray + array with the new dtype, with field values copied from the fields in + the input array with the same name + + Examples + -------- + + >>> from numpy.lib import recfunctions as rfn + >>> a = np.ones(4, dtype=[('a', 'i4'), ('b', 'f8'), ('c', 'u1')]) + >>> rfn.require_fields(a, [('b', 'f4'), ('c', 'u1')]) + array([(1., 1), (1., 1), (1., 1), (1., 1)], + dtype=[('b', '<f4'), ('c', 'u1')]) + >>> rfn.require_fields(a, [('b', 'f4'), ('newf', 'u1')]) + array([(1., 0), (1., 0), (1., 0), (1., 0)], + dtype=[('b', '<f4'), ('newf', 'u1')]) + + """ + out = np.empty(array.shape, dtype=required_dtype) + assign_fields_by_name(out, array) + return out + + +def _stack_arrays_dispatcher(arrays, defaults=None, usemask=None, + asrecarray=None, autoconvert=None): + return arrays + + +@array_function_dispatch(_stack_arrays_dispatcher) def stack_arrays(arrays, defaults=None, usemask=True, asrecarray=False, autoconvert=False): """ @@ -839,15 +1265,16 @@ def stack_arrays(arrays, defaults=None, usemask=True, asrecarray=False, True >>> z = np.array([('A', 1), ('B', 2)], dtype=[('A', '|S3'), ('B', float)]) >>> zz = np.array([('a', 10., 100.), ('b', 20., 200.), ('c', 30., 300.)], - ... dtype=[('A', '|S3'), ('B', float), ('C', float)]) + ... dtype=[('A', '|S3'), ('B', np.double), ('C', np.double)]) >>> test = rfn.stack_arrays((z,zz)) >>> test - masked_array(data = [('A', 1.0, --) ('B', 2.0, --) ('a', 10.0, 100.0) ('b', 20.0, 200.0) - ('c', 30.0, 300.0)], - mask = [(False, False, True) (False, False, True) (False, False, False) - (False, False, False) (False, False, False)], - fill_value = ('N/A', 1e+20, 1e+20), - dtype = [('A', '|S3'), ('B', '<f8'), ('C', '<f8')]) + masked_array(data=[(b'A', 1.0, --), (b'B', 2.0, --), (b'a', 10.0, 100.0), + (b'b', 20.0, 200.0), (b'c', 30.0, 300.0)], + mask=[(False, False, True), (False, False, True), + (False, False, False), (False, False, False), + (False, False, False)], + fill_value=(b'N/A', 1.e+20, 1.e+20), + dtype=[('A', 'S3'), ('B', '<f8'), ('C', '<f8')]) """ if isinstance(arrays, ndarray): @@ -860,10 +1287,10 @@ def stack_arrays(arrays, defaults=None, usemask=True, asrecarray=False, fldnames = [d.names for d in ndtype] # dtype_l = ndtype[0] - newdescr = get_fieldspec(dtype_l) + newdescr = _get_fieldspec(dtype_l) names = [n for n, d in newdescr] for dtype_n in ndtype[1:]: - for fname, fdtype in get_fieldspec(dtype_n): + for fname, fdtype in _get_fieldspec(dtype_n): if fname not in names: newdescr.append((fname, fdtype)) names.append(fname) @@ -897,6 +1324,12 @@ def stack_arrays(arrays, defaults=None, usemask=True, asrecarray=False, usemask=usemask, asrecarray=asrecarray) +def _find_duplicates_dispatcher( + a, key=None, ignoremask=None, return_index=None): + return (a,) + + +@array_function_dispatch(_find_duplicates_dispatcher) def find_duplicates(a, key=None, ignoremask=True, return_index=False): """ Find the duplicates in a structured array along a given key @@ -920,7 +1353,10 @@ def find_duplicates(a, key=None, ignoremask=True, return_index=False): >>> a = np.ma.array([1, 1, 1, 2, 2, 3, 3], ... mask=[0, 0, 1, 0, 0, 0, 1]).view(ndtype) >>> rfn.find_duplicates(a, ignoremask=True, return_index=True) - ... # XXX: judging by the output, the ignoremask flag has no effect + (masked_array(data=[(1,), (1,), (2,), (2,)], + mask=[(False,), (False,), (False,), (False,)], + fill_value=(999999,), + dtype=[('a', '<i8')]), array([0, 1, 3, 4])) """ a = np.asanyarray(a).ravel() # Get a dictionary of fields @@ -951,8 +1387,15 @@ def find_duplicates(a, key=None, ignoremask=True, return_index=False): return duplicates +def _join_by_dispatcher( + key, r1, r2, jointype=None, r1postfix=None, r2postfix=None, + defaults=None, usemask=None, asrecarray=None): + return (r1, r2) + + +@array_function_dispatch(_join_by_dispatcher) def join_by(key, r1, r2, jointype='inner', r1postfix='1', r2postfix='2', - defaults=None, usemask=True, asrecarray=False): + defaults=None, usemask=True, asrecarray=False): """ Join arrays `r1` and `r2` on key `key`. @@ -1008,7 +1451,7 @@ def join_by(key, r1, r2, jointype='inner', r1postfix='1', r2postfix='2', "'outer' or 'leftouter' (got '%s' instead)" % jointype ) # If we have a single key, put it in a tuple - if isinstance(key, basestring): + if isinstance(key, str): key = (key,) # Check the keys @@ -1070,15 +1513,15 @@ def join_by(key, r1, r2, jointype='inner', r1postfix='1', r2postfix='2', # # Build the new description of the output array ....... # Start with the key fields - ndtype = get_fieldspec(r1k.dtype) + ndtype = _get_fieldspec(r1k.dtype) # Add the fields from r1 - for fname, fdtype in get_fieldspec(r1.dtype): + for fname, fdtype in _get_fieldspec(r1.dtype): if fname not in key: ndtype.append((fname, fdtype)) # Add the fields from r2 - for fname, fdtype in get_fieldspec(r2.dtype): + for fname, fdtype in _get_fieldspec(r2.dtype): # Have we seen the current name already ? # we need to rebuild this list every time names = list(name for name, dtype in ndtype) @@ -1130,6 +1573,13 @@ def join_by(key, r1, r2, jointype='inner', r1postfix='1', r2postfix='2', return _fix_output(_fix_defaults(output, defaults), **kwargs) +def _rec_join_dispatcher( + key, r1, r2, jointype=None, r1postfix=None, r2postfix=None, + defaults=None): + return (r1, r2) + + +@array_function_dispatch(_rec_join_dispatcher) def rec_join(key, r1, r2, jointype='inner', r1postfix='1', r2postfix='2', defaults=None): """ diff --git a/numpy/lib/scimath.py b/numpy/lib/scimath.py index f1838fee6d37..308f1328bb35 100644 --- a/numpy/lib/scimath.py +++ b/numpy/lib/scimath.py @@ -7,19 +7,33 @@ module provide the mathematically valid answers in the complex plane:: >>> import math - >>> from numpy.lib import scimath - >>> scimath.log(-math.exp(1)) == (1+1j*math.pi) + >>> np.emath.log(-math.exp(1)) == (1+1j*math.pi) True Similarly, `sqrt`, other base logarithms, `power` and trig functions are correctly handled. See their respective docstrings for specific examples. -""" -from __future__ import division, absolute_import, print_function +Functions +--------- + +.. autosummary:: + :toctree: generated/ + + sqrt + log + log2 + logn + log10 + power + arccos + arcsin + arctanh +""" import numpy.core.numeric as nx import numpy.core.numerictypes as nt from numpy.core.numeric import asarray, any +from numpy.core.overrides import array_function_dispatch from numpy.lib.type_check import isreal @@ -58,7 +72,7 @@ def _tocomplex(arr): >>> a = np.array([1,2,3],np.short) >>> ac = np.lib.scimath._tocomplex(a); ac - array([ 1.+0.j, 2.+0.j, 3.+0.j], dtype=complex64) + array([1.+0.j, 2.+0.j, 3.+0.j], dtype=complex64) >>> ac.dtype dtype('complex64') @@ -69,7 +83,7 @@ def _tocomplex(arr): >>> b = np.array([1,2,3],np.double) >>> bc = np.lib.scimath._tocomplex(b); bc - array([ 1.+0.j, 2.+0.j, 3.+0.j]) + array([1.+0.j, 2.+0.j, 3.+0.j]) >>> bc.dtype dtype('complex128') @@ -80,13 +94,13 @@ def _tocomplex(arr): >>> c = np.array([1,2,3],np.csingle) >>> cc = np.lib.scimath._tocomplex(c); cc - array([ 1.+0.j, 2.+0.j, 3.+0.j], dtype=complex64) + array([1.+0.j, 2.+0.j, 3.+0.j], dtype=complex64) >>> c *= 2; c - array([ 2.+0.j, 4.+0.j, 6.+0.j], dtype=complex64) + array([2.+0.j, 4.+0.j, 6.+0.j], dtype=complex64) >>> cc - array([ 1.+0.j, 2.+0.j, 3.+0.j], dtype=complex64) + array([1.+0.j, 2.+0.j, 3.+0.j], dtype=complex64) """ if issubclass(arr.dtype.type, (nt.single, nt.byte, nt.short, nt.ubyte, nt.ushort, nt.csingle)): @@ -94,6 +108,7 @@ def _tocomplex(arr): else: return arr.astype(nt.cdouble) + def _fix_real_lt_zero(x): """Convert `x` to complex if it has real, negative components. @@ -121,6 +136,7 @@ def _fix_real_lt_zero(x): x = _tocomplex(x) return x + def _fix_int_lt_zero(x): """Convert `x` to double if it has real, negative components. @@ -147,6 +163,7 @@ def _fix_int_lt_zero(x): x = x * 1.0 return x + def _fix_real_abs_gt_1(x): """Convert `x` to complex if it has real components x_i with abs(x_i)>1. @@ -166,13 +183,19 @@ def _fix_real_abs_gt_1(x): array([0, 1]) >>> np.lib.scimath._fix_real_abs_gt_1([0,2]) - array([ 0.+0.j, 2.+0.j]) + array([0.+0.j, 2.+0.j]) """ x = asarray(x) if any(isreal(x) & (abs(x) > 1)): x = _tocomplex(x) return x + +def _unary_dispatcher(x): + return (x,) + + +@array_function_dispatch(_unary_dispatcher) def sqrt(x): """ Compute the square root of x. @@ -199,22 +222,24 @@ def sqrt(x): -------- For real, non-negative inputs this works just like `numpy.sqrt`: - >>> np.lib.scimath.sqrt(1) + >>> np.emath.sqrt(1) 1.0 - >>> np.lib.scimath.sqrt([1, 4]) - array([ 1., 2.]) + >>> np.emath.sqrt([1, 4]) + array([1., 2.]) But it automatically handles negative inputs: - >>> np.lib.scimath.sqrt(-1) - (0.0+1.0j) - >>> np.lib.scimath.sqrt([-1,4]) - array([ 0.+1.j, 2.+0.j]) + >>> np.emath.sqrt(-1) + 1j + >>> np.emath.sqrt([-1,4]) + array([0.+1.j, 2.+0.j]) """ x = _fix_real_lt_zero(x) return nx.sqrt(x) + +@array_function_dispatch(_unary_dispatcher) def log(x): """ Compute the natural logarithm of `x`. @@ -261,6 +286,8 @@ def log(x): x = _fix_real_lt_zero(x) return nx.log(x) + +@array_function_dispatch(_unary_dispatcher) def log10(x): """ Compute the logarithm base 10 of `x`. @@ -303,12 +330,18 @@ def log10(x): 1.0 >>> np.emath.log10([-10**1, -10**2, 10**2]) - array([ 1.+1.3644j, 2.+1.3644j, 2.+0.j ]) + array([1.+1.3644j, 2.+1.3644j, 2.+0.j ]) """ x = _fix_real_lt_zero(x) return nx.log10(x) + +def _logn_dispatcher(n, x): + return (n, x,) + + +@array_function_dispatch(_logn_dispatcher) def logn(n, x): """ Take log base n of x. @@ -318,8 +351,8 @@ def logn(n, x): Parameters ---------- - n : int - The base in which the log is taken. + n : array_like + The integer base(s) in which the log is taken. x : array_like The value(s) whose log base `n` is (are) required. @@ -333,16 +366,18 @@ def logn(n, x): -------- >>> np.set_printoptions(precision=4) - >>> np.lib.scimath.logn(2, [4, 8]) - array([ 2., 3.]) - >>> np.lib.scimath.logn(2, [-4, -8, 8]) - array([ 2.+4.5324j, 3.+4.5324j, 3.+0.j ]) + >>> np.emath.logn(2, [4, 8]) + array([2., 3.]) + >>> np.emath.logn(2, [-4, -8, 8]) + array([2.+4.5324j, 3.+4.5324j, 3.+0.j ]) """ x = _fix_real_lt_zero(x) n = _fix_real_lt_zero(n) return nx.log(x)/nx.log(n) + +@array_function_dispatch(_unary_dispatcher) def log2(x): """ Compute the logarithm base 2 of `x`. @@ -383,12 +418,18 @@ def log2(x): >>> np.emath.log2(8) 3.0 >>> np.emath.log2([-4, -8, 8]) - array([ 2.+4.5324j, 3.+4.5324j, 3.+0.j ]) + array([2.+4.5324j, 3.+4.5324j, 3.+0.j ]) """ x = _fix_real_lt_zero(x) return nx.log2(x) + +def _power_dispatcher(x, p): + return (x, p) + + +@array_function_dispatch(_power_dispatcher) def power(x, p): """ Return x to the power p, (x**p). @@ -420,18 +461,20 @@ def power(x, p): -------- >>> np.set_printoptions(precision=4) - >>> np.lib.scimath.power([2, 4], 2) + >>> np.emath.power([2, 4], 2) array([ 4, 16]) - >>> np.lib.scimath.power([2, 4], -2) - array([ 0.25 , 0.0625]) - >>> np.lib.scimath.power([-2, 4], 2) - array([ 4.+0.j, 16.+0.j]) + >>> np.emath.power([2, 4], -2) + array([0.25 , 0.0625]) + >>> np.emath.power([-2, 4], 2) + array([ 4.-0.j, 16.+0.j]) """ x = _fix_real_lt_zero(x) p = _fix_int_lt_zero(p) return nx.power(x, p) + +@array_function_dispatch(_unary_dispatcher) def arccos(x): """ Compute the inverse cosine of x. @@ -469,12 +512,14 @@ def arccos(x): 0.0 >>> np.emath.arccos([1,2]) - array([ 0.-0.j , 0.+1.317j]) + array([0.-0.j , 0.-1.317j]) """ x = _fix_real_abs_gt_1(x) return nx.arccos(x) + +@array_function_dispatch(_unary_dispatcher) def arcsin(x): """ Compute the inverse sine of x. @@ -513,21 +558,23 @@ def arcsin(x): 0.0 >>> np.emath.arcsin([0,1]) - array([ 0. , 1.5708]) + array([0. , 1.5708]) """ x = _fix_real_abs_gt_1(x) return nx.arcsin(x) + +@array_function_dispatch(_unary_dispatcher) def arctanh(x): """ Compute the inverse hyperbolic tangent of `x`. Return the "principal value" (for a description of this, see - `numpy.arctanh`) of `arctanh(x)`. For real `x` such that - `abs(x) < 1`, this is a real number. If `abs(x) > 1`, or if `x` is + `numpy.arctanh`) of ``arctanh(x)``. For real `x` such that + ``abs(x) < 1``, this is a real number. If `abs(x) > 1`, or if `x` is complex, the result is complex. Finally, `x = 1` returns``inf`` and - `x=-1` returns ``-inf``. + ``x=-1`` returns ``-inf``. Parameters ---------- @@ -549,17 +596,20 @@ def arctanh(x): ----- For an arctanh() that returns ``NAN`` when real `x` is not in the interval ``(-1,1)``, use `numpy.arctanh` (this latter, however, does - return +/-inf for `x = +/-1`). + return +/-inf for ``x = +/-1``). Examples -------- >>> np.set_printoptions(precision=4) - >>> np.emath.arctanh(np.eye(2)) - array([[ Inf, 0.], - [ 0., Inf]]) + >>> from numpy.testing import suppress_warnings + >>> with suppress_warnings() as sup: + ... sup.filter(RuntimeWarning) + ... np.emath.arctanh(np.eye(2)) + array([[inf, 0.], + [ 0., inf]]) >>> np.emath.arctanh([1j]) - array([ 0.+0.7854j]) + array([0.+0.7854j]) """ x = _fix_real_abs_gt_1(x) diff --git a/numpy/lib/scimath.pyi b/numpy/lib/scimath.pyi new file mode 100644 index 000000000000..d0d4af41eb0c --- /dev/null +++ b/numpy/lib/scimath.pyi @@ -0,0 +1,13 @@ +from typing import List + +__all__: List[str] + +def sqrt(x): ... +def log(x): ... +def log10(x): ... +def logn(n, x): ... +def log2(x): ... +def power(x, p): ... +def arccos(x): ... +def arcsin(x): ... +def arctanh(x): ... diff --git a/numpy/lib/setup.py b/numpy/lib/setup.py index d342410b8a85..7520b72d7ac0 100644 --- a/numpy/lib/setup.py +++ b/numpy/lib/setup.py @@ -1,10 +1,10 @@ -from __future__ import division, print_function - def configuration(parent_package='',top_path=None): from numpy.distutils.misc_util import Configuration config = Configuration('lib', parent_package, top_path) - config.add_data_dir('tests') + config.add_subpackage('tests') + config.add_data_dir('tests/data') + config.add_data_files('*.pyi') return config if __name__ == '__main__': diff --git a/numpy/lib/shape_base.py b/numpy/lib/shape_base.py index 65104115a4e4..a3fbee3d5fb3 100644 --- a/numpy/lib/shape_base.py +++ b/numpy/lib/shape_base.py @@ -1,14 +1,15 @@ -from __future__ import division, absolute_import, print_function - -import warnings +import functools import numpy.core.numeric as _nx from numpy.core.numeric import ( asarray, zeros, outer, concatenate, array, asanyarray ) -from numpy.core.fromnumeric import product, reshape, transpose +from numpy.core.fromnumeric import reshape, transpose from numpy.core.multiarray import normalize_axis_index +from numpy.core import overrides from numpy.core import vstack, atleast_3d +from numpy.core.numeric import normalize_axis_tuple +from numpy.core.shape_base import _arrays_for_stack_dispatcher from numpy.lib.index_tricks import ndindex from numpy.matrixlib.defmatrix import matrix # this raises all the right alarm bells @@ -21,8 +22,12 @@ ] +array_function_dispatch = functools.partial( + overrides.array_function_dispatch, module='numpy') + + def _make_along_axis_idx(arr_shape, indices, axis): - # compute dimensions to iterate over + # compute dimensions to iterate over if not _nx.issubdtype(indices.dtype, _nx.integer): raise IndexError('`indices` must be an integer array') if len(arr_shape) != indices.ndim: @@ -44,6 +49,11 @@ def _make_along_axis_idx(arr_shape, indices, axis): return tuple(fancy_index) +def _take_along_axis_dispatcher(arr, indices, axis): + return (arr, indices) + + +@array_function_dispatch(_take_along_axis_dispatcher) def take_along_axis(arr, indices, axis): """ Take values from the input array by matching 1d index and data slices. @@ -59,13 +69,13 @@ def take_along_axis(arr, indices, axis): Parameters ---------- - arr: ndarray (Ni..., M, Nk...) + arr : ndarray (Ni..., M, Nk...) Source array - indices: ndarray (Ni..., J, Nk...) + indices : ndarray (Ni..., J, Nk...) Indices to take along each 1d slice of `arr`. This must match the dimension of arr, but dimensions Ni and Nj only need to broadcast against `arr`. - axis: int + axis : int The axis to take 1d slices along. If axis is None, the input array is treated as if it had first been flattened to 1d, for consistency with `sort` and `argsort`. @@ -82,7 +92,7 @@ def take_along_axis(arr, indices, axis): Ni, M, Nk = a.shape[:axis], a.shape[axis], a.shape[axis+1:] J = indices.shape[axis] # Need not equal M - out = np.empty(Nk + (J,) + Nk) + out = np.empty(Ni + (J,) + Nk) for ii in ndindex(Ni): for kk in ndindex(Nk): @@ -116,7 +126,7 @@ def take_along_axis(arr, indices, axis): [40, 50, 60]]) >>> ai = np.argsort(a, axis=1); ai array([[0, 2, 1], - [1, 2, 0]], dtype=int64) + [1, 2, 0]]) >>> np.take_along_axis(a, ai, axis=1) array([[10, 20, 30], [40, 50, 60]]) @@ -129,7 +139,7 @@ def take_along_axis(arr, indices, axis): >>> ai = np.expand_dims(np.argmax(a, axis=1), axis=1) >>> ai array([[1], - [0], dtype=int64) + [0]]) >>> np.take_along_axis(a, ai, axis=1) array([[30], [60]]) @@ -139,10 +149,10 @@ def take_along_axis(arr, indices, axis): >>> ai_min = np.expand_dims(np.argmin(a, axis=1), axis=1) >>> ai_max = np.expand_dims(np.argmax(a, axis=1), axis=1) - >>> ai = np.concatenate([ai_min, ai_max], axis=axis) - >> ai + >>> ai = np.concatenate([ai_min, ai_max], axis=1) + >>> ai array([[0, 1], - [1, 0]], dtype=int64) + [1, 0]]) >>> np.take_along_axis(a, ai, axis=1) array([[10, 30], [40, 60]]) @@ -160,6 +170,11 @@ def take_along_axis(arr, indices, axis): return arr[_make_along_axis_idx(arr_shape, indices, axis)] +def _put_along_axis_dispatcher(arr, indices, values, axis): + return (arr, indices, values) + + +@array_function_dispatch(_put_along_axis_dispatcher) def put_along_axis(arr, indices, values, axis): """ Put values into the destination array by matching 1d index and data slices. @@ -175,16 +190,16 @@ def put_along_axis(arr, indices, values, axis): Parameters ---------- - arr: ndarray (Ni..., M, Nk...) + arr : ndarray (Ni..., M, Nk...) Destination array. - indices: ndarray (Ni..., J, Nk...) + indices : ndarray (Ni..., J, Nk...) Indices to change along each 1d slice of `arr`. This must match the dimension of arr, but dimensions in Ni and Nj may be 1 to broadcast against `arr`. - values: array_like (Ni..., J, Nk...) + values : array_like (Ni..., J, Nk...) values to insert at those indices. Its shape and dimension are broadcast to match that of `indices`. - axis: int + axis : int The axis to take 1d slices along. If axis is None, the destination array is treated as if a flattened 1d view had been created of it. @@ -225,7 +240,7 @@ def put_along_axis(arr, indices, values, axis): >>> ai = np.expand_dims(np.argmax(a, axis=1), axis=1) >>> ai array([[1], - [0]], dtype=int64) + [0]]) >>> np.put_along_axis(a, ai, 99, axis=1) >>> a array([[10, 99, 20], @@ -245,12 +260,17 @@ def put_along_axis(arr, indices, values, axis): arr[_make_along_axis_idx(arr_shape, indices, axis)] = values +def _apply_along_axis_dispatcher(func1d, axis, arr, *args, **kwargs): + return (arr,) + + +@array_function_dispatch(_apply_along_axis_dispatcher) def apply_along_axis(func1d, axis, arr, *args, **kwargs): """ Apply a function to 1-D slices along the given axis. - Execute `func1d(a, *args)` where `func1d` operates on 1-D arrays and `a` - is a 1-D slice of `arr` along `axis`. + Execute `func1d(a, *args, **kwargs)` where `func1d` operates on 1-D arrays + and `a` is a 1-D slice of `arr` along `axis`. This is equivalent to (but faster than) the following use of `ndindex` and `s_`, which sets each of ``ii``, ``jj``, and ``kk`` to a tuple of indices:: @@ -307,9 +327,9 @@ def apply_along_axis(func1d, axis, arr, *args, **kwargs): ... return (a[0] + a[-1]) * 0.5 >>> b = np.array([[1,2,3], [4,5,6], [7,8,9]]) >>> np.apply_along_axis(my_func, 0, b) - array([ 4., 5., 6.]) + array([4., 5., 6.]) >>> np.apply_along_axis(my_func, 1, b) - array([ 2., 5., 8.]) + array([2., 5., 8.]) For a function that returns a 1D array, the number of dimensions in `outarr` is the same as `arr`. @@ -352,8 +372,10 @@ def apply_along_axis(func1d, axis, arr, *args, **kwargs): # invoke the function on the first item try: ind0 = next(inds) - except StopIteration: - raise ValueError('Cannot apply_along_axis when any iteration dimensions are 0') + except StopIteration as e: + raise ValueError( + 'Cannot apply_along_axis when any iteration dimensions are 0' + ) from None res = asanyarray(func1d(inarr_view[ind0], *args, **kwargs)) # build a buffer for storing evaluations of func1d. @@ -392,6 +414,11 @@ def apply_along_axis(func1d, axis, arr, *args, **kwargs): return res.__array_wrap__(out_arr) +def _apply_over_axes_dispatcher(func, a, axes): + return (a,) + + +@array_function_dispatch(_apply_over_axes_dispatcher) def apply_over_axes(func, a, axes): """ Apply a function repeatedly over multiple axes. @@ -425,7 +452,7 @@ def apply_over_axes(func, a, axes): Apply a function to 1-D slices of an array along the given axis. Notes - ------ + ----- This function is equivalent to tuple axis arguments to reorderable ufuncs with keepdims=True. Tuple axis arguments to ufuncs have been available since version 1.7.0. @@ -474,9 +501,15 @@ def apply_over_axes(func, a, axes): val = res else: raise ValueError("function is not returning " - "an array of the correct shape") + "an array of the correct shape") return val + +def _expand_dims_dispatcher(a, axis): + return (a,) + + +@array_function_dispatch(_expand_dims_dispatcher) def expand_dims(a, axis): """ Expand the shape of an array. @@ -484,23 +517,26 @@ def expand_dims(a, axis): Insert a new axis that will appear at the `axis` position in the expanded array shape. - .. note:: Previous to NumPy 1.13.0, neither ``axis < -a.ndim - 1`` nor - ``axis > a.ndim`` raised errors or put the new axis where documented. - Those axis values are now deprecated and will raise an AxisError in the - future. - Parameters ---------- a : array_like Input array. - axis : int - Position in the expanded axes where the new axis is placed. + axis : int or tuple of ints + Position in the expanded axes where the new axis (or axes) is placed. + + .. deprecated:: 1.13.0 + Passing an axis where ``axis > a.ndim`` will be treated as + ``axis == a.ndim``, and passing ``axis < -a.ndim - 1`` will + be treated as ``axis == 0``. This behavior is deprecated. + + .. versionchanged:: 1.18.0 + A tuple of axes is now supported. Out of range axes as + described above are now forbidden and raise an `AxisError`. Returns ------- - res : ndarray - Output array. The number of dimensions is one greater than that of - the input array. + result : ndarray + View of `a` with the number of dimensions increased. See Also -------- @@ -510,11 +546,11 @@ def expand_dims(a, axis): Examples -------- - >>> x = np.array([1,2]) + >>> x = np.array([1, 2]) >>> x.shape (2,) - The following is equivalent to ``x[np.newaxis,:]`` or ``x[np.newaxis]``: + The following is equivalent to ``x[np.newaxis, :]`` or ``x[np.newaxis]``: >>> y = np.expand_dims(x, axis=0) >>> y @@ -522,13 +558,26 @@ def expand_dims(a, axis): >>> y.shape (1, 2) - >>> y = np.expand_dims(x, axis=1) # Equivalent to x[:,np.newaxis] + The following is equivalent to ``x[:, np.newaxis]``: + + >>> y = np.expand_dims(x, axis=1) >>> y array([[1], [2]]) >>> y.shape (2, 1) + ``axis`` may also be a tuple: + + >>> y = np.expand_dims(x, axis=(0, 1)) + >>> y + array([[[1, 2]]]) + + >>> y = np.expand_dims(x, axis=(2, 0)) + >>> y + array([[[1], + [2]]]) + Note that some examples may use ``None`` instead of ``np.newaxis``. These are the same objects: @@ -536,22 +585,31 @@ def expand_dims(a, axis): True """ - a = asarray(a) - shape = a.shape - if axis > a.ndim or axis < -a.ndim - 1: - # 2017-05-17, 1.13.0 - warnings.warn("Both axis > a.ndim and axis < -a.ndim - 1 are " - "deprecated and will raise an AxisError in the future.", - DeprecationWarning, stacklevel=2) - # When the deprecation period expires, delete this if block, - if axis < 0: - axis = axis + a.ndim + 1 - # and uncomment the following line. - # axis = normalize_axis_index(axis, a.ndim + 1) - return a.reshape(shape[:axis] + (1,) + shape[axis:]) + if isinstance(a, matrix): + a = asarray(a) + else: + a = asanyarray(a) + + if type(axis) not in (tuple, list): + axis = (axis,) + + out_ndim = len(axis) + a.ndim + axis = normalize_axis_tuple(axis, out_ndim) + + shape_it = iter(a.shape) + shape = [1 if ax in axis else next(shape_it) for ax in range(out_ndim)] + + return a.reshape(shape) + row_stack = vstack + +def _column_stack_dispatcher(tup): + return _arrays_for_stack_dispatcher(tup) + + +@array_function_dispatch(_column_stack_dispatcher) def column_stack(tup): """ Stack 1-D arrays as columns into a 2-D array. @@ -585,14 +643,24 @@ def column_stack(tup): [3, 4]]) """ + if not overrides.ARRAY_FUNCTION_ENABLED: + # raise warning if necessary + _arrays_for_stack_dispatcher(tup, stacklevel=2) + arrays = [] for v in tup: - arr = array(v, copy=False, subok=True) + arr = asanyarray(v) if arr.ndim < 2: arr = array(arr, copy=False, subok=True, ndmin=2).T arrays.append(arr) return _nx.concatenate(arrays, 1) + +def _dstack_dispatcher(tup): + return _arrays_for_stack_dispatcher(tup) + + +@array_function_dispatch(_dstack_dispatcher) def dstack(tup): """ Stack arrays in sequence depth wise (along third axis). @@ -620,10 +688,12 @@ def dstack(tup): See Also -------- - stack : Join a sequence of arrays along a new axis. - vstack : Stack along first axis. - hstack : Stack along second axis. concatenate : Join a sequence of arrays along an existing axis. + stack : Join a sequence of arrays along a new axis. + block : Assemble an nd-array from nested lists of blocks. + vstack : Stack arrays in sequence vertically (row wise). + hstack : Stack arrays in sequence horizontally (column wise). + column_stack : Stack 1-D arrays as columns into a 2-D array. dsplit : Split array along third axis. Examples @@ -643,7 +713,15 @@ def dstack(tup): [[3, 4]]]) """ - return _nx.concatenate([atleast_3d(_m) for _m in tup], 2) + if not overrides.ARRAY_FUNCTION_ENABLED: + # raise warning if necessary + _arrays_for_stack_dispatcher(tup, stacklevel=2) + + arrs = atleast_3d(*tup) + if not isinstance(arrs, list): + arrs = [arrs] + return _nx.concatenate(arrs, 2) + def _replace_zero_by_x_arrays(sub_arys): for i in range(len(sub_arys)): @@ -653,6 +731,12 @@ def _replace_zero_by_x_arrays(sub_arys): sub_arys[i] = _nx.empty(0, dtype=sub_arys[i].dtype) return sub_arys + +def _array_split_dispatcher(ary, indices_or_sections, axis=None): + return (ary, indices_or_sections) + + +@array_function_dispatch(_array_split_dispatcher) def array_split(ary, indices_or_sections, axis=0): """ Split an array into multiple sub-arrays. @@ -672,11 +756,11 @@ def array_split(ary, indices_or_sections, axis=0): -------- >>> x = np.arange(8.0) >>> np.array_split(x, 3) - [array([ 0., 1., 2.]), array([ 3., 4., 5.]), array([ 6., 7.])] + [array([0., 1., 2.]), array([3., 4., 5.]), array([6., 7.])] - >>> x = np.arange(7.0) - >>> np.array_split(x, 3) - [array([ 0., 1., 2.]), array([ 3., 4.]), array([ 5., 6.])] + >>> x = np.arange(9) + >>> np.array_split(x, 4) + [array([0, 1, 2]), array([3, 4]), array([5, 6]), array([7, 8])] """ try: @@ -684,19 +768,19 @@ def array_split(ary, indices_or_sections, axis=0): except AttributeError: Ntotal = len(ary) try: - # handle scalar case. + # handle array case. Nsections = len(indices_or_sections) + 1 div_points = [0] + list(indices_or_sections) + [Ntotal] except TypeError: # indices_or_sections is a scalar, not an array. Nsections = int(indices_or_sections) if Nsections <= 0: - raise ValueError('number sections must be larger than 0.') + raise ValueError('number sections must be larger than 0.') from None Neach_section, extras = divmod(Ntotal, Nsections) section_sizes = ([0] + extras * [Neach_section+1] + (Nsections-extras) * [Neach_section]) - div_points = _nx.array(section_sizes).cumsum() + div_points = _nx.array(section_sizes, dtype=_nx.intp).cumsum() sub_arys = [] sary = _nx.swapaxes(ary, axis, 0) @@ -708,9 +792,14 @@ def array_split(ary, indices_or_sections, axis=0): return sub_arys -def split(ary,indices_or_sections,axis=0): +def _split_dispatcher(ary, indices_or_sections, axis=None): + return (ary, indices_or_sections) + + +@array_function_dispatch(_split_dispatcher) +def split(ary, indices_or_sections, axis=0): """ - Split an array into multiple sub-arrays. + Split an array into multiple sub-arrays as views into `ary`. Parameters ---------- @@ -737,7 +826,7 @@ def split(ary,indices_or_sections,axis=0): Returns ------- sub-arrays : list of ndarrays - A list of sub-arrays. + A list of sub-arrays as views into `ary`. Raises ------ @@ -763,14 +852,14 @@ def split(ary,indices_or_sections,axis=0): -------- >>> x = np.arange(9.0) >>> np.split(x, 3) - [array([ 0., 1., 2.]), array([ 3., 4., 5.]), array([ 6., 7., 8.])] + [array([0., 1., 2.]), array([3., 4., 5.]), array([6., 7., 8.])] >>> x = np.arange(8.0) >>> np.split(x, [3, 5, 6, 10]) - [array([ 0., 1., 2.]), - array([ 3., 4.]), - array([ 5.]), - array([ 6., 7.]), + [array([0., 1., 2.]), + array([3., 4.]), + array([5.]), + array([6., 7.]), array([], dtype=float64)] """ @@ -781,10 +870,15 @@ def split(ary,indices_or_sections,axis=0): N = ary.shape[axis] if N % sections: raise ValueError( - 'array split does not result in an equal division') - res = array_split(ary, indices_or_sections, axis) - return res + 'array split does not result in an equal division') from None + return array_split(ary, indices_or_sections, axis) + + +def _hvdsplit_dispatcher(ary, indices_or_sections): + return (ary, indices_or_sections) + +@array_function_dispatch(_hvdsplit_dispatcher) def hsplit(ary, indices_or_sections): """ Split an array into multiple sub-arrays horizontally (column-wise). @@ -801,43 +895,43 @@ def hsplit(ary, indices_or_sections): -------- >>> x = np.arange(16.0).reshape(4, 4) >>> x - array([[ 0., 1., 2., 3.], - [ 4., 5., 6., 7.], - [ 8., 9., 10., 11.], - [ 12., 13., 14., 15.]]) + array([[ 0., 1., 2., 3.], + [ 4., 5., 6., 7.], + [ 8., 9., 10., 11.], + [12., 13., 14., 15.]]) >>> np.hsplit(x, 2) [array([[ 0., 1.], [ 4., 5.], [ 8., 9.], - [ 12., 13.]]), + [12., 13.]]), array([[ 2., 3.], [ 6., 7.], - [ 10., 11.], - [ 14., 15.]])] + [10., 11.], + [14., 15.]])] >>> np.hsplit(x, np.array([3, 6])) - [array([[ 0., 1., 2.], - [ 4., 5., 6.], - [ 8., 9., 10.], - [ 12., 13., 14.]]), - array([[ 3.], - [ 7.], - [ 11.], - [ 15.]]), - array([], dtype=float64)] + [array([[ 0., 1., 2.], + [ 4., 5., 6.], + [ 8., 9., 10.], + [12., 13., 14.]]), + array([[ 3.], + [ 7.], + [11.], + [15.]]), + array([], shape=(4, 0), dtype=float64)] With a higher dimensional array the split is still along the second axis. >>> x = np.arange(8.0).reshape(2, 2, 2) >>> x - array([[[ 0., 1.], - [ 2., 3.]], - [[ 4., 5.], - [ 6., 7.]]]) + array([[[0., 1.], + [2., 3.]], + [[4., 5.], + [6., 7.]]]) >>> np.hsplit(x, 2) - [array([[[ 0., 1.]], - [[ 4., 5.]]]), - array([[[ 2., 3.]], - [[ 6., 7.]]])] + [array([[[0., 1.]], + [[4., 5.]]]), + array([[[2., 3.]], + [[6., 7.]]])] """ if _nx.ndim(ary) == 0: @@ -847,6 +941,8 @@ def hsplit(ary, indices_or_sections): else: return split(ary, indices_or_sections, 0) + +@array_function_dispatch(_hvdsplit_dispatcher) def vsplit(ary, indices_or_sections): """ Split an array into multiple sub-arrays vertically (row-wise). @@ -863,41 +959,39 @@ def vsplit(ary, indices_or_sections): -------- >>> x = np.arange(16.0).reshape(4, 4) >>> x - array([[ 0., 1., 2., 3.], - [ 4., 5., 6., 7.], - [ 8., 9., 10., 11.], - [ 12., 13., 14., 15.]]) + array([[ 0., 1., 2., 3.], + [ 4., 5., 6., 7.], + [ 8., 9., 10., 11.], + [12., 13., 14., 15.]]) >>> np.vsplit(x, 2) - [array([[ 0., 1., 2., 3.], - [ 4., 5., 6., 7.]]), - array([[ 8., 9., 10., 11.], - [ 12., 13., 14., 15.]])] + [array([[0., 1., 2., 3.], + [4., 5., 6., 7.]]), array([[ 8., 9., 10., 11.], + [12., 13., 14., 15.]])] >>> np.vsplit(x, np.array([3, 6])) - [array([[ 0., 1., 2., 3.], - [ 4., 5., 6., 7.], - [ 8., 9., 10., 11.]]), - array([[ 12., 13., 14., 15.]]), - array([], dtype=float64)] + [array([[ 0., 1., 2., 3.], + [ 4., 5., 6., 7.], + [ 8., 9., 10., 11.]]), array([[12., 13., 14., 15.]]), array([], shape=(0, 4), dtype=float64)] With a higher dimensional array the split is still along the first axis. >>> x = np.arange(8.0).reshape(2, 2, 2) >>> x - array([[[ 0., 1.], - [ 2., 3.]], - [[ 4., 5.], - [ 6., 7.]]]) + array([[[0., 1.], + [2., 3.]], + [[4., 5.], + [6., 7.]]]) >>> np.vsplit(x, 2) - [array([[[ 0., 1.], - [ 2., 3.]]]), - array([[[ 4., 5.], - [ 6., 7.]]])] + [array([[[0., 1.], + [2., 3.]]]), array([[[4., 5.], + [6., 7.]]])] """ if _nx.ndim(ary) < 2: raise ValueError('vsplit only works on arrays of 2 or more dimensions') return split(ary, indices_or_sections, 0) + +@array_function_dispatch(_hvdsplit_dispatcher) def dsplit(ary, indices_or_sections): """ Split array into multiple sub-arrays along the 3rd axis (depth). @@ -914,30 +1008,28 @@ def dsplit(ary, indices_or_sections): -------- >>> x = np.arange(16.0).reshape(2, 2, 4) >>> x - array([[[ 0., 1., 2., 3.], - [ 4., 5., 6., 7.]], - [[ 8., 9., 10., 11.], - [ 12., 13., 14., 15.]]]) + array([[[ 0., 1., 2., 3.], + [ 4., 5., 6., 7.]], + [[ 8., 9., 10., 11.], + [12., 13., 14., 15.]]]) >>> np.dsplit(x, 2) - [array([[[ 0., 1.], - [ 4., 5.]], - [[ 8., 9.], - [ 12., 13.]]]), - array([[[ 2., 3.], - [ 6., 7.]], - [[ 10., 11.], - [ 14., 15.]]])] + [array([[[ 0., 1.], + [ 4., 5.]], + [[ 8., 9.], + [12., 13.]]]), array([[[ 2., 3.], + [ 6., 7.]], + [[10., 11.], + [14., 15.]]])] >>> np.dsplit(x, np.array([3, 6])) - [array([[[ 0., 1., 2.], - [ 4., 5., 6.]], - [[ 8., 9., 10.], - [ 12., 13., 14.]]]), - array([[[ 3.], - [ 7.]], - [[ 11.], - [ 15.]]]), - array([], dtype=float64)] - + [array([[[ 0., 1., 2.], + [ 4., 5., 6.]], + [[ 8., 9., 10.], + [12., 13., 14.]]]), + array([[[ 3.], + [ 7.]], + [[11.], + [15.]]]), + array([], shape=(2, 2, 0), dtype=float64)] """ if _nx.ndim(ary) < 3: raise ValueError('dsplit only works on arrays of 3 or more dimensions') @@ -967,6 +1059,12 @@ def get_array_wrap(*args): return wrappers[-1][-1] return None + +def _kron_dispatcher(a, b): + return (a, b) + + +@array_function_dispatch(_kron_dispatcher) def kron(a, b): """ Kronecker product of two arrays. @@ -990,8 +1088,8 @@ def kron(a, b): ----- The function assumes that the number of dimensions of `a` and `b` are the same, if necessary prepending the smallest with ones. - If `a.shape = (r0,r1,..,rN)` and `b.shape = (s0,s1,...,sN)`, - the Kronecker product has shape `(r0*s0, r1*s1, ..., rN*SN)`. + If ``a.shape = (r0,r1,..,rN)`` and ``b.shape = (s0,s1,...,sN)``, + the Kronecker product has shape ``(r0*s0, r1*s1, ..., rN*SN)``. The elements are products of elements from `a` and `b`, organized explicitly by:: @@ -1011,15 +1109,15 @@ def kron(a, b): Examples -------- >>> np.kron([1,10,100], [5,6,7]) - array([ 5, 6, 7, 50, 60, 70, 500, 600, 700]) + array([ 5, 6, 7, ..., 500, 600, 700]) >>> np.kron([5,6,7], [1,10,100]) - array([ 5, 50, 500, 6, 60, 600, 7, 70, 700]) + array([ 5, 50, 500, ..., 7, 70, 700]) >>> np.kron(np.eye(2), np.ones((2,2))) - array([[ 1., 1., 0., 0.], - [ 1., 1., 0., 0.], - [ 0., 0., 1., 1.], - [ 0., 0., 1., 1.]]) + array([[1., 1., 0., 0.], + [1., 1., 0., 0.], + [0., 0., 1., 1.], + [0., 0., 1., 1.]]) >>> a = np.arange(100).reshape((2,5,2,5)) >>> b = np.arange(24).reshape((2,3,4)) @@ -1066,6 +1164,11 @@ def kron(a, b): return result +def _tile_dispatcher(A, reps): + return (A, reps) + + +@array_function_dispatch(_tile_dispatcher) def tile(A, reps): """ Construct an array by repeating A the number of times given by reps. diff --git a/numpy/lib/shape_base.pyi b/numpy/lib/shape_base.pyi new file mode 100644 index 000000000000..17016c99928b --- /dev/null +++ b/numpy/lib/shape_base.pyi @@ -0,0 +1,216 @@ +from typing import List, TypeVar, Callable, Sequence, Any, overload, Tuple, SupportsIndex, Protocol + +from numpy import ( + generic, + integer, + dtype, + ufunc, + bool_, + unsignedinteger, + signedinteger, + floating, + complexfloating, + object_, +) + +from numpy.typing import ( + ArrayLike, + NDArray, + _ShapeLike, + _FiniteNestedSequence, + _SupportsArray, + _ArrayLikeBool_co, + _ArrayLikeUInt_co, + _ArrayLikeInt_co, + _ArrayLikeFloat_co, + _ArrayLikeComplex_co, + _ArrayLikeObject_co, +) + +from numpy.core.shape_base import vstack + +_SCT = TypeVar("_SCT", bound=generic) + +_ArrayLike = _FiniteNestedSequence[_SupportsArray[dtype[_SCT]]] + +# The signatures of `__array_wrap__` and `__array_prepare__` are the same; +# give them unique names for the sake of clarity +class _ArrayWrap(Protocol): + def __call__( + self, + array: NDArray[Any], + context: None | Tuple[ufunc, Tuple[Any, ...], int] = ..., + /, + ) -> Any: ... + +class _ArrayPrepare(Protocol): + def __call__( + self, + array: NDArray[Any], + context: None | Tuple[ufunc, Tuple[Any, ...], int] = ..., + /, + ) -> Any: ... + +class _SupportsArrayWrap(Protocol): + @property + def __array_wrap__(self) -> _ArrayWrap: ... + +class _SupportsArrayPrepare(Protocol): + @property + def __array_prepare__(self) -> _ArrayPrepare: ... + +__all__: List[str] + +row_stack = vstack + +def take_along_axis( + arr: _SCT | NDArray[_SCT], + indices: NDArray[integer[Any]], + axis: None | int, +) -> NDArray[_SCT]: ... + +def put_along_axis( + arr: NDArray[_SCT], + indices: NDArray[integer[Any]], + values: ArrayLike, + axis: None | int, +) -> None: ... + +@overload +def apply_along_axis( + func1d: Callable[..., _ArrayLike[_SCT]], + axis: SupportsIndex, + arr: ArrayLike, + *args: Any, + **kwargs: Any, +) -> NDArray[_SCT]: ... +@overload +def apply_along_axis( + func1d: Callable[..., ArrayLike], + axis: SupportsIndex, + arr: ArrayLike, + *args: Any, + **kwargs: Any, +) -> NDArray[Any]: ... + +def apply_over_axes( + func: Callable[[NDArray[Any], int], NDArray[_SCT]], + a: ArrayLike, + axes: int | Sequence[int], +) -> NDArray[_SCT]: ... + +@overload +def expand_dims( + a: _ArrayLike[_SCT], + axis: _ShapeLike, +) -> NDArray[_SCT]: ... +@overload +def expand_dims( + a: ArrayLike, + axis: _ShapeLike, +) -> NDArray[Any]: ... + +@overload +def column_stack(tup: Sequence[_ArrayLike[_SCT]]) -> NDArray[_SCT]: ... +@overload +def column_stack(tup: Sequence[ArrayLike]) -> NDArray[Any]: ... + +@overload +def dstack(tup: Sequence[_ArrayLike[_SCT]]) -> NDArray[_SCT]: ... +@overload +def dstack(tup: Sequence[ArrayLike]) -> NDArray[Any]: ... + +@overload +def array_split( + ary: _ArrayLike[_SCT], + indices_or_sections: _ShapeLike, + axis: SupportsIndex = ..., +) -> List[NDArray[_SCT]]: ... +@overload +def array_split( + ary: ArrayLike, + indices_or_sections: _ShapeLike, + axis: SupportsIndex = ..., +) -> List[NDArray[Any]]: ... + +@overload +def split( + ary: _ArrayLike[_SCT], + indices_or_sections: _ShapeLike, + axis: SupportsIndex = ..., +) -> List[NDArray[_SCT]]: ... +@overload +def split( + ary: ArrayLike, + indices_or_sections: _ShapeLike, + axis: SupportsIndex = ..., +) -> List[NDArray[Any]]: ... + +@overload +def hsplit( + ary: _ArrayLike[_SCT], + indices_or_sections: _ShapeLike, +) -> List[NDArray[_SCT]]: ... +@overload +def hsplit( + ary: ArrayLike, + indices_or_sections: _ShapeLike, +) -> List[NDArray[Any]]: ... + +@overload +def vsplit( + ary: _ArrayLike[_SCT], + indices_or_sections: _ShapeLike, +) -> List[NDArray[_SCT]]: ... +@overload +def vsplit( + ary: ArrayLike, + indices_or_sections: _ShapeLike, +) -> List[NDArray[Any]]: ... + +@overload +def dsplit( + ary: _ArrayLike[_SCT], + indices_or_sections: _ShapeLike, +) -> List[NDArray[_SCT]]: ... +@overload +def dsplit( + ary: ArrayLike, + indices_or_sections: _ShapeLike, +) -> List[NDArray[Any]]: ... + +@overload +def get_array_prepare(*args: _SupportsArrayPrepare) -> _ArrayPrepare: ... +@overload +def get_array_prepare(*args: object) -> None | _ArrayPrepare: ... + +@overload +def get_array_wrap(*args: _SupportsArrayWrap) -> _ArrayWrap: ... +@overload +def get_array_wrap(*args: object) -> None | _ArrayWrap: ... + +@overload +def kron(a: _ArrayLikeBool_co, b: _ArrayLikeBool_co) -> NDArray[bool_]: ... # type: ignore[misc] +@overload +def kron(a: _ArrayLikeUInt_co, b: _ArrayLikeUInt_co) -> NDArray[unsignedinteger[Any]]: ... # type: ignore[misc] +@overload +def kron(a: _ArrayLikeInt_co, b: _ArrayLikeInt_co) -> NDArray[signedinteger[Any]]: ... # type: ignore[misc] +@overload +def kron(a: _ArrayLikeFloat_co, b: _ArrayLikeFloat_co) -> NDArray[floating[Any]]: ... # type: ignore[misc] +@overload +def kron(a: _ArrayLikeComplex_co, b: _ArrayLikeComplex_co) -> NDArray[complexfloating[Any, Any]]: ... +@overload +def kron(a: _ArrayLikeObject_co, b: Any) -> NDArray[object_]: ... +@overload +def kron(a: Any, b: _ArrayLikeObject_co) -> NDArray[object_]: ... + +@overload +def tile( + A: _ArrayLike[_SCT], + reps: int | Sequence[int], +) -> NDArray[_SCT]: ... +@overload +def tile( + A: ArrayLike, + reps: int | Sequence[int], +) -> NDArray[Any]: ... diff --git a/numpy/lib/stride_tricks.py b/numpy/lib/stride_tricks.py index 2abe5cdd150c..5093993a9e92 100644 --- a/numpy/lib/stride_tricks.py +++ b/numpy/lib/stride_tricks.py @@ -5,14 +5,14 @@ NumPy reference guide. """ -from __future__ import division, absolute_import, print_function - import numpy as np +from numpy.core.numeric import normalize_axis_tuple +from numpy.core.overrides import array_function_dispatch, set_module -__all__ = ['broadcast_to', 'broadcast_arrays'] +__all__ = ['broadcast_to', 'broadcast_arrays', 'broadcast_shapes'] -class DummyArray(object): +class DummyArray: """Dummy object that just exists to hang __array_interface__ dictionaries and possibly keep alive a reference to a base array. """ @@ -66,8 +66,10 @@ def as_strided(x, shape=None, strides=None, subok=False, writeable=True): See also -------- - broadcast_to: broadcast an array to a given shape. + broadcast_to : broadcast an array to a given shape. reshape : reshape an array. + lib.stride_tricks.sliding_window_view : + userfriendly and safe function for the creation of sliding window views. Notes ----- @@ -112,6 +114,228 @@ def as_strided(x, shape=None, strides=None, subok=False, writeable=True): return view +def _sliding_window_view_dispatcher(x, window_shape, axis=None, *, + subok=None, writeable=None): + return (x,) + + +@array_function_dispatch(_sliding_window_view_dispatcher) +def sliding_window_view(x, window_shape, axis=None, *, + subok=False, writeable=False): + """ + Create a sliding window view into the array with the given window shape. + + Also known as rolling or moving window, the window slides across all + dimensions of the array and extracts subsets of the array at all window + positions. + + .. versionadded:: 1.20.0 + + Parameters + ---------- + x : array_like + Array to create the sliding window view from. + window_shape : int or tuple of int + Size of window over each axis that takes part in the sliding window. + If `axis` is not present, must have same length as the number of input + array dimensions. Single integers `i` are treated as if they were the + tuple `(i,)`. + axis : int or tuple of int, optional + Axis or axes along which the sliding window is applied. + By default, the sliding window is applied to all axes and + `window_shape[i]` will refer to axis `i` of `x`. + If `axis` is given as a `tuple of int`, `window_shape[i]` will refer to + the axis `axis[i]` of `x`. + Single integers `i` are treated as if they were the tuple `(i,)`. + subok : bool, optional + If True, sub-classes will be passed-through, otherwise the returned + array will be forced to be a base-class array (default). + writeable : bool, optional + When true, allow writing to the returned view. The default is false, + as this should be used with caution: the returned view contains the + same memory location multiple times, so writing to one location will + cause others to change. + + Returns + ------- + view : ndarray + Sliding window view of the array. The sliding window dimensions are + inserted at the end, and the original dimensions are trimmed as + required by the size of the sliding window. + That is, ``view.shape = x_shape_trimmed + window_shape``, where + ``x_shape_trimmed`` is ``x.shape`` with every entry reduced by one less + than the corresponding window size. + + See Also + -------- + lib.stride_tricks.as_strided: A lower-level and less safe routine for + creating arbitrary views from custom shape and strides. + broadcast_to: broadcast an array to a given shape. + + Notes + ----- + For many applications using a sliding window view can be convenient, but + potentially very slow. Often specialized solutions exist, for example: + + - `scipy.signal.fftconvolve` + + - filtering functions in `scipy.ndimage` + + - moving window functions provided by + `bottleneck <https://github.com/pydata/bottleneck>`_. + + As a rough estimate, a sliding window approach with an input size of `N` + and a window size of `W` will scale as `O(N*W)` where frequently a special + algorithm can achieve `O(N)`. That means that the sliding window variant + for a window size of 100 can be a 100 times slower than a more specialized + version. + + Nevertheless, for small window sizes, when no custom algorithm exists, or + as a prototyping and developing tool, this function can be a good solution. + + Examples + -------- + >>> x = np.arange(6) + >>> x.shape + (6,) + >>> v = sliding_window_view(x, 3) + >>> v.shape + (4, 3) + >>> v + array([[0, 1, 2], + [1, 2, 3], + [2, 3, 4], + [3, 4, 5]]) + + This also works in more dimensions, e.g. + + >>> i, j = np.ogrid[:3, :4] + >>> x = 10*i + j + >>> x.shape + (3, 4) + >>> x + array([[ 0, 1, 2, 3], + [10, 11, 12, 13], + [20, 21, 22, 23]]) + >>> shape = (2,2) + >>> v = sliding_window_view(x, shape) + >>> v.shape + (2, 3, 2, 2) + >>> v + array([[[[ 0, 1], + [10, 11]], + [[ 1, 2], + [11, 12]], + [[ 2, 3], + [12, 13]]], + [[[10, 11], + [20, 21]], + [[11, 12], + [21, 22]], + [[12, 13], + [22, 23]]]]) + + The axis can be specified explicitly: + + >>> v = sliding_window_view(x, 3, 0) + >>> v.shape + (1, 4, 3) + >>> v + array([[[ 0, 10, 20], + [ 1, 11, 21], + [ 2, 12, 22], + [ 3, 13, 23]]]) + + The same axis can be used several times. In that case, every use reduces + the corresponding original dimension: + + >>> v = sliding_window_view(x, (2, 3), (1, 1)) + >>> v.shape + (3, 1, 2, 3) + >>> v + array([[[[ 0, 1, 2], + [ 1, 2, 3]]], + [[[10, 11, 12], + [11, 12, 13]]], + [[[20, 21, 22], + [21, 22, 23]]]]) + + Combining with stepped slicing (`::step`), this can be used to take sliding + views which skip elements: + + >>> x = np.arange(7) + >>> sliding_window_view(x, 5)[:, ::2] + array([[0, 2, 4], + [1, 3, 5], + [2, 4, 6]]) + + or views which move by multiple elements + + >>> x = np.arange(7) + >>> sliding_window_view(x, 3)[::2, :] + array([[0, 1, 2], + [2, 3, 4], + [4, 5, 6]]) + + A common application of `sliding_window_view` is the calculation of running + statistics. The simplest example is the + `moving average <https://en.wikipedia.org/wiki/Moving_average>`_: + + >>> x = np.arange(6) + >>> x.shape + (6,) + >>> v = sliding_window_view(x, 3) + >>> v.shape + (4, 3) + >>> v + array([[0, 1, 2], + [1, 2, 3], + [2, 3, 4], + [3, 4, 5]]) + >>> moving_average = v.mean(axis=-1) + >>> moving_average + array([1., 2., 3., 4.]) + + Note that a sliding window approach is often **not** optimal (see Notes). + """ + window_shape = (tuple(window_shape) + if np.iterable(window_shape) + else (window_shape,)) + # first convert input to array, possibly keeping subclass + x = np.array(x, copy=False, subok=subok) + + window_shape_array = np.array(window_shape) + if np.any(window_shape_array < 0): + raise ValueError('`window_shape` cannot contain negative values') + + if axis is None: + axis = tuple(range(x.ndim)) + if len(window_shape) != len(axis): + raise ValueError(f'Since axis is `None`, must provide ' + f'window_shape for all dimensions of `x`; ' + f'got {len(window_shape)} window_shape elements ' + f'and `x.ndim` is {x.ndim}.') + else: + axis = normalize_axis_tuple(axis, x.ndim, allow_duplicate=True) + if len(window_shape) != len(axis): + raise ValueError(f'Must provide matching length window_shape and ' + f'axis; got {len(window_shape)} window_shape ' + f'elements and {len(axis)} axes elements.') + + out_strides = x.strides + tuple(x.strides[ax] for ax in axis) + + # note: same axis can be windowed repeatedly + x_shape_trimmed = list(x.shape) + for ax, dim in zip(axis, window_shape): + if x_shape_trimmed[ax] < dim: + raise ValueError( + 'window shape cannot be larger than input array shape') + x_shape_trimmed[ax] -= dim - 1 + out_shape = tuple(x_shape_trimmed) + window_shape + return as_strided(x, strides=out_strides, shape=out_shape, + subok=subok, writeable=writeable) + + def _broadcast_to(array, shape, subok, readonly): shape = tuple(shape) if np.iterable(shape) else (shape,) array = np.array(array, copy=False, subok=subok) @@ -120,21 +344,26 @@ def _broadcast_to(array, shape, subok, readonly): if any(size < 0 for size in shape): raise ValueError('all elements of broadcast shape must be non-' 'negative') - needs_writeable = not readonly and array.flags.writeable - extras = ['reduce_ok'] if needs_writeable else [] - op_flag = 'readwrite' if needs_writeable else 'readonly' + extras = [] it = np.nditer( (array,), flags=['multi_index', 'refs_ok', 'zerosize_ok'] + extras, - op_flags=[op_flag], itershape=shape, order='C') + op_flags=['readonly'], itershape=shape, order='C') with it: # never really has writebackifcopy semantics broadcast = it.itviews[0] result = _maybe_view_as_subclass(array, broadcast) - if needs_writeable and not result.flags.writeable: + # In a future version this will go away + if not readonly and array.flags._writeable_no_warn: result.flags.writeable = True + result.flags._warn_on_write = True return result +def _broadcast_to_dispatcher(array, shape, subok=None): + return (array,) + + +@array_function_dispatch(_broadcast_to_dispatcher, module='numpy') def broadcast_to(array, shape, subok=False): """Broadcast an array to a new shape. @@ -142,8 +371,9 @@ def broadcast_to(array, shape, subok=False): ---------- array : array_like The array to broadcast. - shape : tuple - The shape of the desired array. + shape : tuple or int + The shape of the desired array. A single integer ``i`` is interpreted + as ``(i,)``. subok : bool, optional If True, then sub-classes will be passed-through, otherwise the returned array will be forced to be a base-class array (default). @@ -161,6 +391,12 @@ def broadcast_to(array, shape, subok=False): If the array is not compatible with the new shape according to NumPy's broadcasting rules. + See Also + -------- + broadcast + broadcast_arrays + broadcast_shapes + Notes ----- .. versionadded:: 1.10.0 @@ -180,8 +416,6 @@ def _broadcast_shape(*args): """Returns the shape of the arrays that would result from broadcasting the supplied arrays against each other. """ - if not args: - return () # use the old-iterator because np.nditer does not handle size 0 arrays # consistently b = np.broadcast(*args[:32]) @@ -195,7 +429,55 @@ def _broadcast_shape(*args): return b.shape -def broadcast_arrays(*args, **kwargs): +@set_module('numpy') +def broadcast_shapes(*args): + """ + Broadcast the input shapes into a single shape. + + :ref:`Learn more about broadcasting here <basics.broadcasting>`. + + .. versionadded:: 1.20.0 + + Parameters + ---------- + `*args` : tuples of ints, or ints + The shapes to be broadcast against each other. + + Returns + ------- + tuple + Broadcasted shape. + + Raises + ------ + ValueError + If the shapes are not compatible and cannot be broadcast according + to NumPy's broadcasting rules. + + See Also + -------- + broadcast + broadcast_arrays + broadcast_to + + Examples + -------- + >>> np.broadcast_shapes((1, 2), (3, 1), (3, 2)) + (3, 2) + + >>> np.broadcast_shapes((6, 7), (5, 6, 1), (7,), (5, 1, 7)) + (5, 6, 7) + """ + arrays = [np.empty(x, dtype=[]) for x in args] + return _broadcast_shape(*arrays) + + +def _broadcast_arrays_dispatcher(*args, subok=None): + return args + + +@array_function_dispatch(_broadcast_arrays_dispatcher, module='numpy') +def broadcast_arrays(*args, subok=False): """ Broadcast any number of arrays against each other. @@ -213,29 +495,38 @@ def broadcast_arrays(*args, **kwargs): broadcasted : list of arrays These arrays are views on the original arrays. They are typically not contiguous. Furthermore, more than one element of a - broadcasted array may refer to a single memory location. If you - need to write to the arrays, make copies first. + broadcasted array may refer to a single memory location. If you need + to write to the arrays, make copies first. While you can set the + ``writable`` flag True, writing to a single output value may end up + changing more than one location in the output array. + + .. deprecated:: 1.17 + The output is currently marked so that if written to, a deprecation + warning will be emitted. A future version will set the + ``writable`` flag False so writing to it will raise an error. + + See Also + -------- + broadcast + broadcast_to + broadcast_shapes Examples -------- >>> x = np.array([[1,2,3]]) - >>> y = np.array([[1],[2],[3]]) + >>> y = np.array([[4],[5]]) >>> np.broadcast_arrays(x, y) [array([[1, 2, 3], - [1, 2, 3], - [1, 2, 3]]), array([[1, 1, 1], - [2, 2, 2], - [3, 3, 3]])] + [1, 2, 3]]), array([[4, 4, 4], + [5, 5, 5]])] Here is a useful idiom for getting contiguous copies instead of non-contiguous views. >>> [np.array(a) for a in np.broadcast_arrays(x, y)] [array([[1, 2, 3], - [1, 2, 3], - [1, 2, 3]]), array([[1, 1, 1], - [2, 2, 2], - [3, 3, 3]])] + [1, 2, 3]]), array([[4, 4, 4], + [5, 5, 5]])] """ # nditer is not used here to avoid the limit of 32 arrays. @@ -243,10 +534,6 @@ def broadcast_arrays(*args, **kwargs): # return np.nditer(args, flags=['multi_index', 'zerosize_ok'], # order='C').itviews - subok = kwargs.pop('subok', False) - if kwargs: - raise TypeError('broadcast_arrays() got an unexpected keyword ' - 'argument {!r}'.format(kwargs.keys()[0])) args = [np.array(_m, copy=False, subok=subok) for _m in args] shape = _broadcast_shape(*args) @@ -255,7 +542,5 @@ def broadcast_arrays(*args, **kwargs): # Common case where nothing needs to be broadcasted. return args - # TODO: consider making the results of broadcast_arrays readonly to match - # broadcast_to. This will require a deprecation cycle. return [_broadcast_to(array, shape, subok=subok, readonly=False) for array in args] diff --git a/numpy/lib/stride_tricks.pyi b/numpy/lib/stride_tricks.pyi new file mode 100644 index 000000000000..aad404107433 --- /dev/null +++ b/numpy/lib/stride_tricks.pyi @@ -0,0 +1,81 @@ +from typing import Any, List, Dict, Iterable, TypeVar, overload, SupportsIndex + +from numpy import dtype, generic +from numpy.typing import ( + NDArray, + ArrayLike, + _ShapeLike, + _Shape, + _FiniteNestedSequence, + _SupportsArray, +) + +_SCT = TypeVar("_SCT", bound=generic) +_ArrayLike = _FiniteNestedSequence[_SupportsArray[dtype[_SCT]]] + +__all__: List[str] + +class DummyArray: + __array_interface__: Dict[str, Any] + base: None | NDArray[Any] + def __init__( + self, + interface: Dict[str, Any], + base: None | NDArray[Any] = ..., + ) -> None: ... + +@overload +def as_strided( + x: _ArrayLike[_SCT], + shape: None | Iterable[int] = ..., + strides: None | Iterable[int] = ..., + subok: bool = ..., + writeable: bool = ..., +) -> NDArray[_SCT]: ... +@overload +def as_strided( + x: ArrayLike, + shape: None | Iterable[int] = ..., + strides: None | Iterable[int] = ..., + subok: bool = ..., + writeable: bool = ..., +) -> NDArray[Any]: ... + +@overload +def sliding_window_view( + x: _ArrayLike[_SCT], + window_shape: int | Iterable[int], + axis: None | SupportsIndex = ..., + *, + subok: bool = ..., + writeable: bool = ..., +) -> NDArray[_SCT]: ... +@overload +def sliding_window_view( + x: ArrayLike, + window_shape: int | Iterable[int], + axis: None | SupportsIndex = ..., + *, + subok: bool = ..., + writeable: bool = ..., +) -> NDArray[Any]: ... + +@overload +def broadcast_to( + array: _ArrayLike[_SCT], + shape: int | Iterable[int], + subok: bool = ..., +) -> NDArray[_SCT]: ... +@overload +def broadcast_to( + array: ArrayLike, + shape: int | Iterable[int], + subok: bool = ..., +) -> NDArray[Any]: ... + +def broadcast_shapes(*args: _ShapeLike) -> _Shape: ... + +def broadcast_arrays( + *args: ArrayLike, + subok: bool = ..., +) -> List[NDArray[Any]]: ... diff --git a/numpy/lib/tests/test__datasource.py b/numpy/lib/tests/test__datasource.py index 32812990c3b5..2738d41c4fbc 100644 --- a/numpy/lib/tests/test__datasource.py +++ b/numpy/lib/tests/test__datasource.py @@ -1,21 +1,14 @@ -from __future__ import division, absolute_import, print_function - import os -import sys +import pytest from tempfile import mkdtemp, mkstemp, NamedTemporaryFile from shutil import rmtree -from numpy.testing import assert_, assert_equal, assert_raises, SkipTest import numpy.lib._datasource as datasource +from numpy.testing import assert_, assert_equal, assert_raises -if sys.version_info[0] >= 3: - import urllib.request as urllib_request - from urllib.parse import urlparse - from urllib.error import URLError -else: - import urllib2 as urllib_request - from urlparse import urlparse - from urllib2 import URLError +import urllib.request as urllib_request +from urllib.parse import urlparse +from urllib.error import URLError def urlopen_stub(url, data=None): @@ -30,14 +23,14 @@ def urlopen_stub(url, data=None): old_urlopen = None -def setup(): +def setup_module(): global old_urlopen old_urlopen = urllib_request.urlopen urllib_request.urlopen = urlopen_stub -def teardown(): +def teardown_module(): urllib_request.urlopen = old_urlopen # A valid website for more robust testing @@ -93,7 +86,7 @@ def invalid_httpfile(): return http_fakefile -class TestDataSourceOpen(object): +class TestDataSourceOpen: def setup(self): self.tmpdir = mkdtemp() self.ds = datasource.DataSource(self.tmpdir) @@ -109,10 +102,10 @@ def test_ValidHTTP(self): def test_InvalidHTTP(self): url = invalid_httpurl() - assert_raises(IOError, self.ds.open, url) + assert_raises(OSError, self.ds.open, url) try: self.ds.open(url) - except IOError as e: + except OSError as e: # Regression test for bug fixed in r4342. assert_(e.errno is None) @@ -127,14 +120,14 @@ def test_ValidFile(self): def test_InvalidFile(self): invalid_file = invalid_textfile(self.tmpdir) - assert_raises(IOError, self.ds.open, invalid_file) + assert_raises(OSError, self.ds.open, invalid_file) def test_ValidGzipFile(self): try: import gzip except ImportError: # We don't have the gzip capabilities to test. - raise SkipTest + pytest.skip() # Test datasource's internal file_opener for Gzip files. filepath = os.path.join(self.tmpdir, 'foobar.txt.gz') fp = gzip.open(filepath, 'w') @@ -150,7 +143,7 @@ def test_ValidBz2File(self): import bz2 except ImportError: # We don't have the bz2 capabilities to test. - raise SkipTest + pytest.skip() # Test datasource's internal file_opener for BZip2 files. filepath = os.path.join(self.tmpdir, 'foobar.txt.bz2') fp = bz2.BZ2File(filepath, 'w') @@ -162,7 +155,7 @@ def test_ValidBz2File(self): assert_equal(magic_line, result) -class TestDataSourceExists(object): +class TestDataSourceExists: def setup(self): self.tmpdir = mkdtemp() self.ds = datasource.DataSource(self.tmpdir) @@ -192,7 +185,7 @@ def test_InvalidFile(self): assert_equal(self.ds.exists(tmpfile), False) -class TestDataSourceAbspath(object): +class TestDataSourceAbspath: def setup(self): self.tmpdir = os.path.abspath(mkdtemp()) self.ds = datasource.DataSource(self.tmpdir) @@ -257,7 +250,7 @@ def test_windows_os_sep(self): os.sep = orig_os_sep -class TestRepositoryAbspath(object): +class TestRepositoryAbspath: def setup(self): self.tmpdir = os.path.abspath(mkdtemp()) self.repos = datasource.Repository(valid_baseurl(), self.tmpdir) @@ -290,7 +283,7 @@ def test_windows_os_sep(self): os.sep = orig_os_sep -class TestRepositoryExists(object): +class TestRepositoryExists: def setup(self): self.tmpdir = mkdtemp() self.repos = datasource.Repository(valid_baseurl(), self.tmpdir) @@ -323,7 +316,7 @@ def test_CachedHTTPFile(self): assert_(self.repos.exists(tmpfile)) -class TestOpenFunc(object): +class TestOpenFunc: def setup(self): self.tmpdir = mkdtemp() @@ -340,3 +333,18 @@ def test_DataSourceOpen(self): fp = datasource.open(local_file) assert_(fp) fp.close() + +def test_del_attr_handling(): + # DataSource __del__ can be called + # even if __init__ fails when the + # Exception object is caught by the + # caller as happens in refguide_check + # is_deprecated() function + + ds = datasource.DataSource() + # simulate failed __init__ by removing key attribute + # produced within __init__ and expected by __del__ + del ds._istmpdest + # should not raise an AttributeError if __del__ + # gracefully handles failed __init__: + ds.__del__() diff --git a/numpy/lib/tests/test__iotools.py b/numpy/lib/tests/test__iotools.py index b4888f1bd732..a5b78702525e 100644 --- a/numpy/lib/tests/test__iotools.py +++ b/numpy/lib/tests/test__iotools.py @@ -1,6 +1,3 @@ -from __future__ import division, absolute_import, print_function - -import sys import time from datetime import date @@ -12,10 +9,9 @@ LineSplitter, NameValidator, StringConverter, has_nested_fields, easy_dtype, flatten_dtype ) -from numpy.compat import unicode -class TestLineSplitter(object): +class TestLineSplitter: "Tests the LineSplitter class." def test_no_delimiter(self): @@ -84,7 +80,7 @@ def test_variable_fixed_width(self): # ----------------------------------------------------------------------------- -class TestNameValidator(object): +class TestNameValidator: def test_case_sensitivity(self): "Test case sensitivity" @@ -142,7 +138,7 @@ def _bytes_to_date(s): return date(*time.strptime(s, "%Y-%m-%d")[:3]) -class TestStringConverter(object): +class TestStringConverter: "Test StringConverter" def test_creation(self): @@ -181,12 +177,12 @@ def test_upgrade(self): # test str # note that the longdouble type has been skipped, so the # _status increases by 2. Everything should succeed with - # unicode conversion (5). - for s in ['a', u'a', b'a']: + # unicode conversion (8). + for s in ['a', b'a']: res = converter.upgrade(s) - assert_(type(res) is unicode) - assert_equal(res, u'a') - assert_equal(converter._status, 5 + status_offset) + assert_(type(res) is str) + assert_equal(res, 'a') + assert_equal(converter._status, 8 + status_offset) def test_missing(self): "Tests the use of missing values." @@ -205,14 +201,18 @@ def test_missing(self): def test_upgrademapper(self): "Tests updatemapper" dateparser = _bytes_to_date - StringConverter.upgrade_mapper(dateparser, date(2000, 1, 1)) - convert = StringConverter(dateparser, date(2000, 1, 1)) - test = convert('2001-01-01') - assert_equal(test, date(2001, 1, 1)) - test = convert('2009-01-01') - assert_equal(test, date(2009, 1, 1)) - test = convert('') - assert_equal(test, date(2000, 1, 1)) + _original_mapper = StringConverter._mapper[:] + try: + StringConverter.upgrade_mapper(dateparser, date(2000, 1, 1)) + convert = StringConverter(dateparser, date(2000, 1, 1)) + test = convert('2001-01-01') + assert_equal(test, date(2001, 1, 1)) + test = convert('2009-01-01') + assert_equal(test, date(2009, 1, 1)) + test = convert('') + assert_equal(test, date(2000, 1, 1)) + finally: + StringConverter._mapper = _original_mapper def test_string_to_object(self): "Make sure that string-to-object functions are properly recognized" @@ -246,7 +246,7 @@ def test_keep_missing_values(self): converter = StringConverter(int, default=0, missing_values="N/A") assert_equal( - converter.missing_values, set(['', 'N/A'])) + converter.missing_values, {'', 'N/A'}) def test_int64_dtype(self): "Check that int64 integer types can be specified" @@ -263,7 +263,7 @@ def test_uint64_dtype(self): assert_(converter(val) == 9223372043271415339) -class TestMiscFunctions(object): +class TestMiscFunctions: def test_has_nested_dtype(self): "Test has_nested_dtype" diff --git a/numpy/lib/tests/test__version.py b/numpy/lib/tests/test__version.py index 8e66a0c0323f..e6d41ad93932 100644 --- a/numpy/lib/tests/test__version.py +++ b/numpy/lib/tests/test__version.py @@ -1,15 +1,13 @@ """Tests for the NumpyVersion class. """ -from __future__ import division, absolute_import, print_function - from numpy.testing import assert_, assert_raises from numpy.lib import NumpyVersion def test_main_versions(): assert_(NumpyVersion('1.8.0') == '1.8.0') - for ver in ['1.9.0', '2.0.0', '1.8.1']: + for ver in ['1.9.0', '2.0.0', '1.8.1', '10.0.1']: assert_(NumpyVersion('1.8.0') < ver) for ver in ['1.7.0', '1.7.1', '0.9.9']: diff --git a/numpy/lib/tests/test_arraypad.py b/numpy/lib/tests/test_arraypad.py index 8ba0370b0e36..75db5928b288 100644 --- a/numpy/lib/tests/test_arraypad.py +++ b/numpy/lib/tests/test_arraypad.py @@ -1,60 +1,143 @@ """Tests for the array padding functions. """ -from __future__ import division, absolute_import, print_function +import pytest import numpy as np -from numpy.testing import (assert_array_equal, assert_raises, assert_allclose,) -from numpy.lib import pad +from numpy.testing import assert_array_equal, assert_allclose, assert_equal +from numpy.lib.arraypad import _as_pairs + + +_numeric_dtypes = ( + np.sctypes["uint"] + + np.sctypes["int"] + + np.sctypes["float"] + + np.sctypes["complex"] +) +_all_modes = { + 'constant': {'constant_values': 0}, + 'edge': {}, + 'linear_ramp': {'end_values': 0}, + 'maximum': {'stat_length': None}, + 'mean': {'stat_length': None}, + 'median': {'stat_length': None}, + 'minimum': {'stat_length': None}, + 'reflect': {'reflect_type': 'even'}, + 'symmetric': {'reflect_type': 'even'}, + 'wrap': {}, + 'empty': {} +} + + +class TestAsPairs: + def test_single_value(self): + """Test casting for a single value.""" + expected = np.array([[3, 3]] * 10) + for x in (3, [3], [[3]]): + result = _as_pairs(x, 10) + assert_equal(result, expected) + # Test with dtype=object + obj = object() + assert_equal( + _as_pairs(obj, 10), + np.array([[obj, obj]] * 10) + ) + + def test_two_values(self): + """Test proper casting for two different values.""" + # Broadcasting in the first dimension with numbers + expected = np.array([[3, 4]] * 10) + for x in ([3, 4], [[3, 4]]): + result = _as_pairs(x, 10) + assert_equal(result, expected) + # and with dtype=object + obj = object() + assert_equal( + _as_pairs(["a", obj], 10), + np.array([["a", obj]] * 10) + ) + # Broadcasting in the second / last dimension with numbers + assert_equal( + _as_pairs([[3], [4]], 2), + np.array([[3, 3], [4, 4]]) + ) + # and with dtype=object + assert_equal( + _as_pairs([["a"], [obj]], 2), + np.array([["a", "a"], [obj, obj]]) + ) -class TestConditionalShortcuts(object): - def test_zero_padding_shortcuts(self): + def test_with_none(self): + expected = ((None, None), (None, None), (None, None)) + assert_equal( + _as_pairs(None, 3, as_index=False), + expected + ) + assert_equal( + _as_pairs(None, 3, as_index=True), + expected + ) + + def test_pass_through(self): + """Test if `x` already matching desired output are passed through.""" + expected = np.arange(12).reshape((6, 2)) + assert_equal( + _as_pairs(expected, 6), + expected + ) + + def test_as_index(self): + """Test results if `as_index=True`.""" + assert_equal( + _as_pairs([2.6, 3.3], 10, as_index=True), + np.array([[3, 3]] * 10, dtype=np.intp) + ) + assert_equal( + _as_pairs([2.6, 4.49], 10, as_index=True), + np.array([[3, 4]] * 10, dtype=np.intp) + ) + for x in (-3, [-3], [[-3]], [-3, 4], [3, -4], [[-3, 4]], [[4, -3]], + [[1, 2]] * 9 + [[1, -2]]): + with pytest.raises(ValueError, match="negative values"): + _as_pairs(x, 10, as_index=True) + + def test_exceptions(self): + """Ensure faulty usage is discovered.""" + with pytest.raises(ValueError, match="more dimensions than allowed"): + _as_pairs([[[3]]], 10) + with pytest.raises(ValueError, match="could not be broadcast"): + _as_pairs([[1, 2], [3, 4]], 3) + with pytest.raises(ValueError, match="could not be broadcast"): + _as_pairs(np.ones((2, 3)), 3) + + +class TestConditionalShortcuts: + @pytest.mark.parametrize("mode", _all_modes.keys()) + def test_zero_padding_shortcuts(self, mode): test = np.arange(120).reshape(4, 5, 6) - pad_amt = [(0, 0) for axis in test.shape] - modes = ['constant', - 'edge', - 'linear_ramp', - 'maximum', - 'mean', - 'median', - 'minimum', - 'reflect', - 'symmetric', - 'wrap', - ] - for mode in modes: - assert_array_equal(test, pad(test, pad_amt, mode=mode)) - - def test_shallow_statistic_range(self): + pad_amt = [(0, 0) for _ in test.shape] + assert_array_equal(test, np.pad(test, pad_amt, mode=mode)) + + @pytest.mark.parametrize("mode", ['maximum', 'mean', 'median', 'minimum',]) + def test_shallow_statistic_range(self, mode): test = np.arange(120).reshape(4, 5, 6) - pad_amt = [(1, 1) for axis in test.shape] - modes = ['maximum', - 'mean', - 'median', - 'minimum', - ] - for mode in modes: - assert_array_equal(pad(test, pad_amt, mode='edge'), - pad(test, pad_amt, mode=mode, stat_length=1)) - - def test_clip_statistic_range(self): + pad_amt = [(1, 1) for _ in test.shape] + assert_array_equal(np.pad(test, pad_amt, mode='edge'), + np.pad(test, pad_amt, mode=mode, stat_length=1)) + + @pytest.mark.parametrize("mode", ['maximum', 'mean', 'median', 'minimum',]) + def test_clip_statistic_range(self, mode): test = np.arange(30).reshape(5, 6) - pad_amt = [(3, 3) for axis in test.shape] - modes = ['maximum', - 'mean', - 'median', - 'minimum', - ] - for mode in modes: - assert_array_equal(pad(test, pad_amt, mode=mode), - pad(test, pad_amt, mode=mode, stat_length=30)) - - -class TestStatistic(object): + pad_amt = [(3, 3) for _ in test.shape] + assert_array_equal(np.pad(test, pad_amt, mode=mode), + np.pad(test, pad_amt, mode=mode, stat_length=30)) + + +class TestStatistic: def test_check_mean_stat_length(self): a = np.arange(100).astype('f') - a = pad(a, ((25, 20), ), 'mean', stat_length=((2, 3), )) + a = np.pad(a, ((25, 20), ), 'mean', stat_length=((2, 3), )) b = np.array( [0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, @@ -78,7 +161,7 @@ def test_check_mean_stat_length(self): def test_check_maximum_1(self): a = np.arange(100) - a = pad(a, (25, 20), 'maximum') + a = np.pad(a, (25, 20), 'maximum') b = np.array( [99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, @@ -102,7 +185,7 @@ def test_check_maximum_1(self): def test_check_maximum_2(self): a = np.arange(100) + 1 - a = pad(a, (25, 20), 'maximum') + a = np.pad(a, (25, 20), 'maximum') b = np.array( [100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, @@ -126,7 +209,7 @@ def test_check_maximum_2(self): def test_check_maximum_stat_length(self): a = np.arange(100) + 1 - a = pad(a, (25, 20), 'maximum', stat_length=10) + a = np.pad(a, (25, 20), 'maximum', stat_length=10) b = np.array( [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, @@ -150,7 +233,7 @@ def test_check_maximum_stat_length(self): def test_check_minimum_1(self): a = np.arange(100) - a = pad(a, (25, 20), 'minimum') + a = np.pad(a, (25, 20), 'minimum') b = np.array( [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -174,7 +257,7 @@ def test_check_minimum_1(self): def test_check_minimum_2(self): a = np.arange(100) + 2 - a = pad(a, (25, 20), 'minimum') + a = np.pad(a, (25, 20), 'minimum') b = np.array( [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, @@ -198,7 +281,7 @@ def test_check_minimum_2(self): def test_check_minimum_stat_length(self): a = np.arange(100) + 1 - a = pad(a, (25, 20), 'minimum', stat_length=10) + a = np.pad(a, (25, 20), 'minimum', stat_length=10) b = np.array( [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -222,7 +305,7 @@ def test_check_minimum_stat_length(self): def test_check_median(self): a = np.arange(100).astype('f') - a = pad(a, (25, 20), 'median') + a = np.pad(a, (25, 20), 'median') b = np.array( [49.5, 49.5, 49.5, 49.5, 49.5, 49.5, 49.5, 49.5, 49.5, 49.5, 49.5, 49.5, 49.5, 49.5, 49.5, 49.5, 49.5, 49.5, 49.5, 49.5, @@ -246,7 +329,7 @@ def test_check_median(self): def test_check_median_01(self): a = np.array([[3, 1, 4], [4, 5, 9], [9, 8, 2]]) - a = pad(a, 1, 'median') + a = np.pad(a, 1, 'median') b = np.array( [[4, 4, 5, 4, 4], @@ -260,7 +343,7 @@ def test_check_median_01(self): def test_check_median_02(self): a = np.array([[3, 1, 4], [4, 5, 9], [9, 8, 2]]) - a = pad(a.T, 1, 'median').T + a = np.pad(a.T, 1, 'median').T b = np.array( [[5, 4, 5, 4, 5], @@ -276,7 +359,7 @@ def test_check_median_stat_length(self): a = np.arange(100).astype('f') a[1] = 2. a[97] = 96. - a = pad(a, (25, 20), 'median', stat_length=(3, 5)) + a = np.pad(a, (25, 20), 'median', stat_length=(3, 5)) b = np.array( [ 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., @@ -300,7 +383,7 @@ def test_check_median_stat_length(self): def test_check_mean_shape_one(self): a = [[4, 5, 6]] - a = pad(a, (5, 7), 'mean', stat_length=2) + a = np.pad(a, (5, 7), 'mean', stat_length=2) b = np.array( [[4, 4, 4, 4, 4, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6], [4, 4, 4, 4, 4, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6], @@ -322,7 +405,7 @@ def test_check_mean_shape_one(self): def test_check_mean_2(self): a = np.arange(100).astype('f') - a = pad(a, (25, 20), 'mean') + a = np.pad(a, (25, 20), 'mean') b = np.array( [49.5, 49.5, 49.5, 49.5, 49.5, 49.5, 49.5, 49.5, 49.5, 49.5, 49.5, 49.5, 49.5, 49.5, 49.5, 49.5, 49.5, 49.5, 49.5, 49.5, @@ -344,11 +427,79 @@ def test_check_mean_2(self): ) assert_array_equal(a, b) + @pytest.mark.parametrize("mode", [ + "mean", + "median", + "minimum", + "maximum" + ]) + def test_same_prepend_append(self, mode): + """ Test that appended and prepended values are equal """ + # This test is constructed to trigger floating point rounding errors in + # a way that caused gh-11216 for mode=='mean' + a = np.array([-1, 2, -1]) + np.array([0, 1e-12, 0], dtype=np.float64) + a = np.pad(a, (1, 1), mode) + assert_equal(a[0], a[-1]) + + @pytest.mark.parametrize("mode", ["mean", "median", "minimum", "maximum"]) + @pytest.mark.parametrize( + "stat_length", [-2, (-2,), (3, -1), ((5, 2), (-2, 3)), ((-4,), (2,))] + ) + def test_check_negative_stat_length(self, mode, stat_length): + arr = np.arange(30).reshape((6, 5)) + match = "index can't contain negative values" + with pytest.raises(ValueError, match=match): + np.pad(arr, 2, mode, stat_length=stat_length) + + def test_simple_stat_length(self): + a = np.arange(30) + a = np.reshape(a, (6, 5)) + a = np.pad(a, ((2, 3), (3, 2)), mode='mean', stat_length=(3,)) + b = np.array( + [[6, 6, 6, 5, 6, 7, 8, 9, 8, 8], + [6, 6, 6, 5, 6, 7, 8, 9, 8, 8], -class TestConstant(object): + [1, 1, 1, 0, 1, 2, 3, 4, 3, 3], + [6, 6, 6, 5, 6, 7, 8, 9, 8, 8], + [11, 11, 11, 10, 11, 12, 13, 14, 13, 13], + [16, 16, 16, 15, 16, 17, 18, 19, 18, 18], + [21, 21, 21, 20, 21, 22, 23, 24, 23, 23], + [26, 26, 26, 25, 26, 27, 28, 29, 28, 28], + + [21, 21, 21, 20, 21, 22, 23, 24, 23, 23], + [21, 21, 21, 20, 21, 22, 23, 24, 23, 23], + [21, 21, 21, 20, 21, 22, 23, 24, 23, 23]] + ) + assert_array_equal(a, b) + + @pytest.mark.filterwarnings("ignore:Mean of empty slice:RuntimeWarning") + @pytest.mark.filterwarnings( + "ignore:invalid value encountered in (true_divide|double_scalars):" + "RuntimeWarning" + ) + @pytest.mark.parametrize("mode", ["mean", "median"]) + def test_zero_stat_length_valid(self, mode): + arr = np.pad([1., 2.], (1, 2), mode, stat_length=0) + expected = np.array([np.nan, 1., 2., np.nan, np.nan]) + assert_equal(arr, expected) + + @pytest.mark.parametrize("mode", ["minimum", "maximum"]) + def test_zero_stat_length_invalid(self, mode): + match = "stat_length of 0 yields no value for padding" + with pytest.raises(ValueError, match=match): + np.pad([1., 2.], 0, mode, stat_length=0) + with pytest.raises(ValueError, match=match): + np.pad([1., 2.], 0, mode, stat_length=(1, 0)) + with pytest.raises(ValueError, match=match): + np.pad([1., 2.], 1, mode, stat_length=0) + with pytest.raises(ValueError, match=match): + np.pad([1., 2.], 1, mode, stat_length=(1, 0)) + + +class TestConstant: def test_check_constant(self): a = np.arange(100) - a = pad(a, (25, 20), 'constant', constant_values=(10, 20)) + a = np.pad(a, (25, 20), 'constant', constant_values=(10, 20)) b = np.array( [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, @@ -372,7 +523,7 @@ def test_check_constant(self): def test_check_constant_zeros(self): a = np.arange(100) - a = pad(a, (25, 20), 'constant') + a = np.pad(a, (25, 20), 'constant') b = np.array( [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -398,7 +549,7 @@ def test_check_constant_float(self): # If input array is int, but constant_values are float, the dtype of # the array to be padded is kept arr = np.arange(30).reshape(5, 6) - test = pad(arr, (1, 2), mode='constant', + test = np.pad(arr, (1, 2), mode='constant', constant_values=1.1) expected = np.array( [[ 1, 1, 1, 1, 1, 1, 1, 1, 1], @@ -419,7 +570,7 @@ def test_check_constant_float2(self): # the array to be padded is kept - here retaining the float constants arr = np.arange(30).reshape(5, 6) arr_float = arr.astype(np.float64) - test = pad(arr_float, ((1, 2), (1, 2)), mode='constant', + test = np.pad(arr_float, ((1, 2), (1, 2)), mode='constant', constant_values=1.1) expected = np.array( [[ 1.1, 1.1, 1.1, 1.1, 1.1, 1.1, 1.1, 1.1, 1.1], @@ -437,7 +588,7 @@ def test_check_constant_float2(self): def test_check_constant_float3(self): a = np.arange(100, dtype=float) - a = pad(a, (25, 20), 'constant', constant_values=(-1.1, -1.2)) + a = np.pad(a, (25, 20), 'constant', constant_values=(-1.1, -1.2)) b = np.array( [-1.1, -1.1, -1.1, -1.1, -1.1, -1.1, -1.1, -1.1, -1.1, -1.1, -1.1, -1.1, -1.1, -1.1, -1.1, -1.1, -1.1, -1.1, -1.1, -1.1, @@ -461,7 +612,7 @@ def test_check_constant_float3(self): def test_check_constant_odd_pad_amount(self): arr = np.arange(30).reshape(5, 6) - test = pad(arr, ((1,), (2,)), mode='constant', + test = np.pad(arr, ((1,), (2,)), mode='constant', constant_values=3) expected = np.array( [[ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], @@ -502,11 +653,32 @@ def test_check_large_integers(self): expected = np.full(7, int64_max, dtype=np.int64) assert_array_equal(test, expected) + def test_check_object_array(self): + arr = np.empty(1, dtype=object) + obj_a = object() + arr[0] = obj_a + obj_b = object() + obj_c = object() + arr = np.pad(arr, pad_width=1, mode='constant', + constant_values=(obj_b, obj_c)) + + expected = np.empty((3,), dtype=object) + expected[0] = obj_b + expected[1] = obj_a + expected[2] = obj_c + + assert_array_equal(arr, expected) -class TestLinearRamp(object): + def test_pad_empty_dimension(self): + arr = np.zeros((3, 0, 2)) + result = np.pad(arr, [(0,), (2,), (1,)], mode="constant") + assert result.shape == (3, 4, 4) + + +class TestLinearRamp: def test_check_simple(self): a = np.arange(100).astype('f') - a = pad(a, (25, 20), 'linear_ramp', end_values=(4, 5)) + a = np.pad(a, (25, 20), 'linear_ramp', end_values=(4, 5)) b = np.array( [4.00, 3.84, 3.68, 3.52, 3.36, 3.20, 3.04, 2.88, 2.72, 2.56, 2.40, 2.24, 2.08, 1.92, 1.76, 1.60, 1.44, 1.28, 1.12, 0.96, @@ -530,7 +702,7 @@ def test_check_simple(self): def test_check_2d(self): arr = np.arange(20).reshape(4, 5).astype(np.float64) - test = pad(arr, (2, 2), mode='linear_ramp', end_values=(0, 0)) + test = np.pad(arr, (2, 2), mode='linear_ramp', end_values=(0, 0)) expected = np.array( [[0., 0., 0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0.5, 1., 1.5, 2., 1., 0.], @@ -542,11 +714,56 @@ def test_check_2d(self): [0., 0., 0., 0., 0., 0., 0., 0., 0.]]) assert_allclose(test, expected) - -class TestReflect(object): + @pytest.mark.xfail(exceptions=(AssertionError,)) + def test_object_array(self): + from fractions import Fraction + arr = np.array([Fraction(1, 2), Fraction(-1, 2)]) + actual = np.pad(arr, (2, 3), mode='linear_ramp', end_values=0) + + # deliberately chosen to have a non-power-of-2 denominator such that + # rounding to floats causes a failure. + expected = np.array([ + Fraction( 0, 12), + Fraction( 3, 12), + Fraction( 6, 12), + Fraction(-6, 12), + Fraction(-4, 12), + Fraction(-2, 12), + Fraction(-0, 12), + ]) + assert_equal(actual, expected) + + def test_end_values(self): + """Ensure that end values are exact.""" + a = np.pad(np.ones(10).reshape(2, 5), (223, 123), mode="linear_ramp") + assert_equal(a[:, 0], 0.) + assert_equal(a[:, -1], 0.) + assert_equal(a[0, :], 0.) + assert_equal(a[-1, :], 0.) + + @pytest.mark.parametrize("dtype", _numeric_dtypes) + def test_negative_difference(self, dtype): + """ + Check correct behavior of unsigned dtypes if there is a negative + difference between the edge to pad and `end_values`. Check both cases + to be independent of implementation. Test behavior for all other dtypes + in case dtype casting interferes with complex dtypes. See gh-14191. + """ + x = np.array([3], dtype=dtype) + result = np.pad(x, 3, mode="linear_ramp", end_values=0) + expected = np.array([0, 1, 2, 3, 2, 1, 0], dtype=dtype) + assert_equal(result, expected) + + x = np.array([0], dtype=dtype) + result = np.pad(x, 3, mode="linear_ramp", end_values=3) + expected = np.array([3, 2, 1, 0, 1, 2, 3], dtype=dtype) + assert_equal(result, expected) + + +class TestReflect: def test_check_simple(self): a = np.arange(100) - a = pad(a, (25, 20), 'reflect') + a = np.pad(a, (25, 20), 'reflect') b = np.array( [25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, @@ -570,7 +787,7 @@ def test_check_simple(self): def test_check_odd_method(self): a = np.arange(100) - a = pad(a, (25, 20), 'reflect', reflect_type='odd') + a = np.pad(a, (25, 20), 'reflect', reflect_type='odd') b = np.array( [-25, -24, -23, -22, -21, -20, -19, -18, -17, -16, -15, -14, -13, -12, -11, -10, -9, -8, -7, -6, @@ -594,7 +811,7 @@ def test_check_odd_method(self): def test_check_large_pad(self): a = [[4, 5, 6], [6, 7, 8]] - a = pad(a, (5, 7), 'reflect') + a = np.pad(a, (5, 7), 'reflect') b = np.array( [[7, 6, 7, 8, 7, 6, 7, 8, 7, 6, 7, 8, 7, 6, 7], [5, 4, 5, 6, 5, 4, 5, 6, 5, 4, 5, 6, 5, 4, 5], @@ -617,7 +834,7 @@ def test_check_large_pad(self): def test_check_shape(self): a = [[4, 5, 6]] - a = pad(a, (5, 7), 'reflect') + a = np.pad(a, (5, 7), 'reflect') b = np.array( [[5, 4, 5, 6, 5, 4, 5, 6, 5, 4, 5, 6, 5, 4, 5], [5, 4, 5, 6, 5, 4, 5, 6, 5, 4, 5, 6, 5, 4, 5], @@ -638,30 +855,49 @@ def test_check_shape(self): assert_array_equal(a, b) def test_check_01(self): - a = pad([1, 2, 3], 2, 'reflect') + a = np.pad([1, 2, 3], 2, 'reflect') b = np.array([3, 2, 1, 2, 3, 2, 1]) assert_array_equal(a, b) def test_check_02(self): - a = pad([1, 2, 3], 3, 'reflect') + a = np.pad([1, 2, 3], 3, 'reflect') b = np.array([2, 3, 2, 1, 2, 3, 2, 1, 2]) assert_array_equal(a, b) def test_check_03(self): - a = pad([1, 2, 3], 4, 'reflect') + a = np.pad([1, 2, 3], 4, 'reflect') b = np.array([1, 2, 3, 2, 1, 2, 3, 2, 1, 2, 3]) assert_array_equal(a, b) - def test_check_padding_an_empty_array(self): - a = pad(np.zeros((0, 3)), ((0,), (1,)), mode='reflect') - b = np.zeros((0, 5)) - assert_array_equal(a, b) +class TestEmptyArray: + """Check how padding behaves on arrays with an empty dimension.""" + + @pytest.mark.parametrize( + # Keep parametrization ordered, otherwise pytest-xdist might believe + # that different tests were collected during parallelization + "mode", sorted(_all_modes.keys() - {"constant", "empty"}) + ) + def test_pad_empty_dimension(self, mode): + match = ("can't extend empty axis 0 using modes other than 'constant' " + "or 'empty'") + with pytest.raises(ValueError, match=match): + np.pad([], 4, mode=mode) + with pytest.raises(ValueError, match=match): + np.pad(np.ndarray(0), 4, mode=mode) + with pytest.raises(ValueError, match=match): + np.pad(np.zeros((0, 3)), ((1,), (0,)), mode=mode) -class TestSymmetric(object): + @pytest.mark.parametrize("mode", _all_modes.keys()) + def test_pad_non_empty_dimension(self, mode): + result = np.pad(np.ones((2, 0, 2)), ((3,), (0,), (1,)), mode=mode) + assert result.shape == (8, 0, 4) + + +class TestSymmetric: def test_check_simple(self): a = np.arange(100) - a = pad(a, (25, 20), 'symmetric') + a = np.pad(a, (25, 20), 'symmetric') b = np.array( [24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, @@ -685,7 +921,7 @@ def test_check_simple(self): def test_check_odd_method(self): a = np.arange(100) - a = pad(a, (25, 20), 'symmetric', reflect_type='odd') + a = np.pad(a, (25, 20), 'symmetric', reflect_type='odd') b = np.array( [-24, -23, -22, -21, -20, -19, -18, -17, -16, -15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, @@ -709,7 +945,7 @@ def test_check_odd_method(self): def test_check_large_pad(self): a = [[4, 5, 6], [6, 7, 8]] - a = pad(a, (5, 7), 'symmetric') + a = np.pad(a, (5, 7), 'symmetric') b = np.array( [[5, 6, 6, 5, 4, 4, 5, 6, 6, 5, 4, 4, 5, 6, 6], [5, 6, 6, 5, 4, 4, 5, 6, 6, 5, 4, 4, 5, 6, 6], @@ -733,7 +969,7 @@ def test_check_large_pad(self): def test_check_large_pad_odd(self): a = [[4, 5, 6], [6, 7, 8]] - a = pad(a, (5, 7), 'symmetric', reflect_type='odd') + a = np.pad(a, (5, 7), 'symmetric', reflect_type='odd') b = np.array( [[-3, -2, -2, -1, 0, 0, 1, 2, 2, 3, 4, 4, 5, 6, 6], [-3, -2, -2, -1, 0, 0, 1, 2, 2, 3, 4, 4, 5, 6, 6], @@ -756,7 +992,7 @@ def test_check_large_pad_odd(self): def test_check_shape(self): a = [[4, 5, 6]] - a = pad(a, (5, 7), 'symmetric') + a = np.pad(a, (5, 7), 'symmetric') b = np.array( [[5, 6, 6, 5, 4, 4, 5, 6, 6, 5, 4, 4, 5, 6, 6], [5, 6, 6, 5, 4, 4, 5, 6, 6, 5, 4, 4, 5, 6, 6], @@ -777,25 +1013,25 @@ def test_check_shape(self): assert_array_equal(a, b) def test_check_01(self): - a = pad([1, 2, 3], 2, 'symmetric') + a = np.pad([1, 2, 3], 2, 'symmetric') b = np.array([2, 1, 1, 2, 3, 3, 2]) assert_array_equal(a, b) def test_check_02(self): - a = pad([1, 2, 3], 3, 'symmetric') + a = np.pad([1, 2, 3], 3, 'symmetric') b = np.array([3, 2, 1, 1, 2, 3, 3, 2, 1]) assert_array_equal(a, b) def test_check_03(self): - a = pad([1, 2, 3], 6, 'symmetric') + a = np.pad([1, 2, 3], 6, 'symmetric') b = np.array([1, 2, 3, 3, 2, 1, 1, 2, 3, 3, 2, 1, 1, 2, 3]) assert_array_equal(a, b) -class TestWrap(object): +class TestWrap: def test_check_simple(self): a = np.arange(100) - a = pad(a, (25, 20), 'wrap') + a = np.pad(a, (25, 20), 'wrap') b = np.array( [75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, @@ -820,7 +1056,7 @@ def test_check_simple(self): def test_check_large_pad(self): a = np.arange(12) a = np.reshape(a, (3, 4)) - a = pad(a, (10, 12), 'wrap') + a = np.pad(a, (10, 12), 'wrap') b = np.array( [[10, 11, 8, 9, 10, 11, 8, 9, 10, 11, 8, 9, 10, 11, 8, 9, 10, 11, 8, 9, 10, 11, 8, 9, 10, 11], @@ -878,44 +1114,39 @@ def test_check_large_pad(self): assert_array_equal(a, b) def test_check_01(self): - a = pad([1, 2, 3], 3, 'wrap') + a = np.pad([1, 2, 3], 3, 'wrap') b = np.array([1, 2, 3, 1, 2, 3, 1, 2, 3]) assert_array_equal(a, b) def test_check_02(self): - a = pad([1, 2, 3], 4, 'wrap') + a = np.pad([1, 2, 3], 4, 'wrap') b = np.array([3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1]) assert_array_equal(a, b) + def test_pad_with_zero(self): + a = np.ones((3, 5)) + b = np.pad(a, (0, 5), mode="wrap") + assert_array_equal(a, b[:-5, :-5]) -class TestStatLen(object): - def test_check_simple(self): - a = np.arange(30) - a = np.reshape(a, (6, 5)) - a = pad(a, ((2, 3), (3, 2)), mode='mean', stat_length=(3,)) - b = np.array( - [[6, 6, 6, 5, 6, 7, 8, 9, 8, 8], - [6, 6, 6, 5, 6, 7, 8, 9, 8, 8], + def test_repeated_wrapping(self): + """ + Check wrapping on each side individually if the wrapped area is longer + than the original array. + """ + a = np.arange(5) + b = np.pad(a, (12, 0), mode="wrap") + assert_array_equal(np.r_[a, a, a, a][3:], b) - [1, 1, 1, 0, 1, 2, 3, 4, 3, 3], - [6, 6, 6, 5, 6, 7, 8, 9, 8, 8], - [11, 11, 11, 10, 11, 12, 13, 14, 13, 13], - [16, 16, 16, 15, 16, 17, 18, 19, 18, 18], - [21, 21, 21, 20, 21, 22, 23, 24, 23, 23], - [26, 26, 26, 25, 26, 27, 28, 29, 28, 28], - - [21, 21, 21, 20, 21, 22, 23, 24, 23, 23], - [21, 21, 21, 20, 21, 22, 23, 24, 23, 23], - [21, 21, 21, 20, 21, 22, 23, 24, 23, 23]] - ) - assert_array_equal(a, b) + a = np.arange(5) + b = np.pad(a, (0, 12), mode="wrap") + assert_array_equal(np.r_[a, a, a, a][:-3], b) -class TestEdge(object): +class TestEdge: def test_check_simple(self): a = np.arange(12) a = np.reshape(a, (4, 3)) - a = pad(a, ((2, 3), (3, 2)), 'edge') + a = np.pad(a, ((2, 3), (3, 2)), 'edge') b = np.array( [[0, 0, 0, 0, 1, 2, 2, 2], [0, 0, 0, 0, 1, 2, 2, 2], @@ -935,56 +1166,128 @@ def test_check_width_shape_1_2(self): # Check a pad_width of the form ((1, 2),). # Regression test for issue gh-7808. a = np.array([1, 2, 3]) - padded = pad(a, ((1, 2),), 'edge') + padded = np.pad(a, ((1, 2),), 'edge') expected = np.array([1, 1, 2, 3, 3, 3]) assert_array_equal(padded, expected) a = np.array([[1, 2, 3], [4, 5, 6]]) - padded = pad(a, ((1, 2),), 'edge') - expected = pad(a, ((1, 2), (1, 2)), 'edge') + padded = np.pad(a, ((1, 2),), 'edge') + expected = np.pad(a, ((1, 2), (1, 2)), 'edge') assert_array_equal(padded, expected) a = np.arange(24).reshape(2, 3, 4) - padded = pad(a, ((1, 2),), 'edge') - expected = pad(a, ((1, 2), (1, 2), (1, 2)), 'edge') + padded = np.pad(a, ((1, 2),), 'edge') + expected = np.pad(a, ((1, 2), (1, 2), (1, 2)), 'edge') assert_array_equal(padded, expected) -class TestZeroPadWidth(object): - def test_zero_pad_width(self): - arr = np.arange(30) - arr = np.reshape(arr, (6, 5)) - for pad_width in (0, (0, 0), ((0, 0), (0, 0))): - assert_array_equal(arr, pad(arr, pad_width, mode='constant')) +class TestEmpty: + def test_simple(self): + arr = np.arange(24).reshape(4, 6) + result = np.pad(arr, [(2, 3), (3, 1)], mode="empty") + assert result.shape == (9, 10) + assert_equal(arr, result[2:-3, 3:-1]) + def test_pad_empty_dimension(self): + arr = np.zeros((3, 0, 2)) + result = np.pad(arr, [(0,), (2,), (1,)], mode="empty") + assert result.shape == (3, 4, 4) -class TestLegacyVectorFunction(object): - def test_legacy_vector_functionality(self): - def _padwithtens(vector, pad_width, iaxis, kwargs): - vector[:pad_width[0]] = 10 - vector[-pad_width[1]:] = 10 - return vector - a = np.arange(6).reshape(2, 3) - a = pad(a, 2, _padwithtens) - b = np.array( - [[10, 10, 10, 10, 10, 10, 10], - [10, 10, 10, 10, 10, 10, 10], +def test_legacy_vector_functionality(): + def _padwithtens(vector, pad_width, iaxis, kwargs): + vector[:pad_width[0]] = 10 + vector[-pad_width[1]:] = 10 - [10, 10, 0, 1, 2, 10, 10], - [10, 10, 3, 4, 5, 10, 10], - - [10, 10, 10, 10, 10, 10, 10], - [10, 10, 10, 10, 10, 10, 10]] - ) - assert_array_equal(a, b) + a = np.arange(6).reshape(2, 3) + a = np.pad(a, 2, _padwithtens) + b = np.array( + [[10, 10, 10, 10, 10, 10, 10], + [10, 10, 10, 10, 10, 10, 10], + [10, 10, 0, 1, 2, 10, 10], + [10, 10, 3, 4, 5, 10, 10], -class TestNdarrayPadWidth(object): - def test_check_simple(self): + [10, 10, 10, 10, 10, 10, 10], + [10, 10, 10, 10, 10, 10, 10]] + ) + assert_array_equal(a, b) + + +def test_unicode_mode(): + a = np.pad([1], 2, mode=u'constant') + b = np.array([0, 0, 1, 0, 0]) + assert_array_equal(a, b) + + +@pytest.mark.parametrize("mode", ["edge", "symmetric", "reflect", "wrap"]) +def test_object_input(mode): + # Regression test for issue gh-11395. + a = np.full((4, 3), fill_value=None) + pad_amt = ((2, 3), (3, 2)) + b = np.full((9, 8), fill_value=None) + assert_array_equal(np.pad(a, pad_amt, mode=mode), b) + + +class TestPadWidth: + @pytest.mark.parametrize("pad_width", [ + (4, 5, 6, 7), + ((1,), (2,), (3,)), + ((1, 2), (3, 4), (5, 6)), + ((3, 4, 5), (0, 1, 2)), + ]) + @pytest.mark.parametrize("mode", _all_modes.keys()) + def test_misshaped_pad_width(self, pad_width, mode): + arr = np.arange(30).reshape((6, 5)) + match = "operands could not be broadcast together" + with pytest.raises(ValueError, match=match): + np.pad(arr, pad_width, mode) + + @pytest.mark.parametrize("mode", _all_modes.keys()) + def test_misshaped_pad_width_2(self, mode): + arr = np.arange(30).reshape((6, 5)) + match = ("input operand has more dimensions than allowed by the axis " + "remapping") + with pytest.raises(ValueError, match=match): + np.pad(arr, (((3,), (4,), (5,)), ((0,), (1,), (2,))), mode) + + @pytest.mark.parametrize( + "pad_width", [-2, (-2,), (3, -1), ((5, 2), (-2, 3)), ((-4,), (2,))]) + @pytest.mark.parametrize("mode", _all_modes.keys()) + def test_negative_pad_width(self, pad_width, mode): + arr = np.arange(30).reshape((6, 5)) + match = "index can't contain negative values" + with pytest.raises(ValueError, match=match): + np.pad(arr, pad_width, mode) + + @pytest.mark.parametrize("pad_width, dtype", [ + ("3", None), + ("word", None), + (None, None), + (object(), None), + (3.4, None), + (((2, 3, 4), (3, 2)), object), + (complex(1, -1), None), + (((-2.1, 3), (3, 2)), None), + ]) + @pytest.mark.parametrize("mode", _all_modes.keys()) + def test_bad_type(self, pad_width, dtype, mode): + arr = np.arange(30).reshape((6, 5)) + match = "`pad_width` must be of integral type." + if dtype is not None: + # avoid DeprecationWarning when not specifying dtype + with pytest.raises(TypeError, match=match): + np.pad(arr, np.array(pad_width, dtype=dtype), mode) + else: + with pytest.raises(TypeError, match=match): + np.pad(arr, pad_width, mode) + with pytest.raises(TypeError, match=match): + np.pad(arr, np.array(pad_width), mode) + + def test_pad_width_as_ndarray(self): a = np.arange(12) a = np.reshape(a, (4, 3)) - a = pad(a, np.array(((2, 3), (3, 2))), 'edge') + a = np.pad(a, np.array(((2, 3), (3, 2))), 'edge') b = np.array( [[0, 0, 0, 0, 1, 2, 2, 2], [0, 0, 0, 0, 1, 2, 2, 2], @@ -1000,106 +1303,62 @@ def test_check_simple(self): ) assert_array_equal(a, b) - -class TestUnicodeInput(object): - def test_unicode_mode(self): - constant_mode = u'constant' - a = np.pad([1], 2, mode=constant_mode) - b = np.array([0, 0, 1, 0, 0]) - assert_array_equal(a, b) - - -class TestValueError1(object): - def test_check_simple(self): - arr = np.arange(30) - arr = np.reshape(arr, (6, 5)) - kwargs = dict(mode='mean', stat_length=(3, )) - assert_raises(ValueError, pad, arr, ((2, 3), (3, 2), (4, 5)), - **kwargs) - - def test_check_negative_stat_length(self): - arr = np.arange(30) - arr = np.reshape(arr, (6, 5)) - kwargs = dict(mode='mean', stat_length=(-3, )) - assert_raises(ValueError, pad, arr, ((2, 3), (3, 2)), - **kwargs) - - def test_check_negative_pad_width(self): - arr = np.arange(30) - arr = np.reshape(arr, (6, 5)) - kwargs = dict(mode='mean', stat_length=(3, )) - assert_raises(ValueError, pad, arr, ((-2, 3), (3, 2)), - **kwargs) - - def test_check_empty_array(self): - assert_raises(ValueError, pad, [], 4, mode='reflect') - assert_raises(ValueError, pad, np.ndarray(0), 4, mode='reflect') - assert_raises(ValueError, pad, np.zeros((0, 3)), ((1,), (0,)), - mode='reflect') - - -class TestValueError2(object): - def test_check_negative_pad_amount(self): - arr = np.arange(30) - arr = np.reshape(arr, (6, 5)) - kwargs = dict(mode='mean', stat_length=(3, )) - assert_raises(ValueError, pad, arr, ((-2, 3), (3, 2)), - **kwargs) - - -class TestValueError3(object): - def test_check_kwarg_not_allowed(self): - arr = np.arange(30).reshape(5, 6) - assert_raises(ValueError, pad, arr, 4, mode='mean', - reflect_type='odd') - - def test_mode_not_set(self): - arr = np.arange(30).reshape(5, 6) - assert_raises(TypeError, pad, arr, 4) - - def test_malformed_pad_amount(self): - arr = np.arange(30).reshape(5, 6) - assert_raises(ValueError, pad, arr, (4, 5, 6, 7), mode='constant') - - def test_malformed_pad_amount2(self): - arr = np.arange(30).reshape(5, 6) - assert_raises(ValueError, pad, arr, ((3, 4, 5), (0, 1, 2)), - mode='constant') - - def test_pad_too_many_axes(self): - arr = np.arange(30).reshape(5, 6) - - # Attempt to pad using a 3D array equivalent - bad_shape = (((3,), (4,), (5,)), ((0,), (1,), (2,))) - assert_raises(ValueError, pad, arr, bad_shape, - mode='constant') - - -class TestTypeError1(object): - def test_float(self): - arr = np.arange(30) - assert_raises(TypeError, pad, arr, ((-2.1, 3), (3, 2))) - assert_raises(TypeError, pad, arr, np.array(((-2.1, 3), (3, 2)))) - - def test_str(self): - arr = np.arange(30) - assert_raises(TypeError, pad, arr, 'foo') - assert_raises(TypeError, pad, arr, np.array('foo')) - - def test_object(self): - class FooBar(object): - pass - arr = np.arange(30) - assert_raises(TypeError, pad, arr, FooBar()) - - def test_complex(self): - arr = np.arange(30) - assert_raises(TypeError, pad, arr, complex(1, -1)) - assert_raises(TypeError, pad, arr, np.array(complex(1, -1))) - - def test_check_wrong_pad_amount(self): - arr = np.arange(30) - arr = np.reshape(arr, (6, 5)) - kwargs = dict(mode='mean', stat_length=(3, )) - assert_raises(TypeError, pad, arr, ((2, 3, 4), (3, 2)), - **kwargs) + @pytest.mark.parametrize("pad_width", [0, (0, 0), ((0, 0), (0, 0))]) + @pytest.mark.parametrize("mode", _all_modes.keys()) + def test_zero_pad_width(self, pad_width, mode): + arr = np.arange(30).reshape(6, 5) + assert_array_equal(arr, np.pad(arr, pad_width, mode=mode)) + + +@pytest.mark.parametrize("mode", _all_modes.keys()) +def test_kwargs(mode): + """Test behavior of pad's kwargs for the given mode.""" + allowed = _all_modes[mode] + not_allowed = {} + for kwargs in _all_modes.values(): + if kwargs != allowed: + not_allowed.update(kwargs) + # Test if allowed keyword arguments pass + np.pad([1, 2, 3], 1, mode, **allowed) + # Test if prohibited keyword arguments of other modes raise an error + for key, value in not_allowed.items(): + match = "unsupported keyword arguments for mode '{}'".format(mode) + with pytest.raises(ValueError, match=match): + np.pad([1, 2, 3], 1, mode, **{key: value}) + + +def test_constant_zero_default(): + arr = np.array([1, 1]) + assert_array_equal(np.pad(arr, 2), [0, 0, 1, 1, 0, 0]) + + +@pytest.mark.parametrize("mode", [1, "const", object(), None, True, False]) +def test_unsupported_mode(mode): + match= "mode '{}' is not supported".format(mode) + with pytest.raises(ValueError, match=match): + np.pad([1, 2, 3], 4, mode=mode) + + +@pytest.mark.parametrize("mode", _all_modes.keys()) +def test_non_contiguous_array(mode): + arr = np.arange(24).reshape(4, 6)[::2, ::2] + result = np.pad(arr, (2, 3), mode) + assert result.shape == (7, 8) + assert_equal(result[2:-3, 2:-3], arr) + + +@pytest.mark.parametrize("mode", _all_modes.keys()) +def test_memory_layout_persistence(mode): + """Test if C and F order is preserved for all pad modes.""" + x = np.ones((5, 10), order='C') + assert np.pad(x, 5, mode).flags["C_CONTIGUOUS"] + x = np.ones((5, 10), order='F') + assert np.pad(x, 5, mode).flags["F_CONTIGUOUS"] + + +@pytest.mark.parametrize("dtype", _numeric_dtypes) +@pytest.mark.parametrize("mode", _all_modes.keys()) +def test_dtype_persistence(dtype, mode): + arr = np.zeros((3, 2, 1), dtype=dtype) + result = np.pad(arr, 1, mode=mode) + assert result.dtype == dtype diff --git a/numpy/lib/tests/test_arraysetops.py b/numpy/lib/tests/test_arraysetops.py index dace5ade8cae..13385cd2409d 100644 --- a/numpy/lib/tests/test_arraysetops.py +++ b/numpy/lib/tests/test_arraysetops.py @@ -1,18 +1,17 @@ """Test functions for 1D array set operations. """ -from __future__ import division, absolute_import, print_function - import numpy as np -import sys -from numpy.testing import assert_array_equal, assert_equal, assert_raises +from numpy.testing import (assert_array_equal, assert_equal, + assert_raises, assert_raises_regex) from numpy.lib.arraysetops import ( ediff1d, intersect1d, setxor1d, union1d, setdiff1d, unique, in1d, isin ) +import pytest -class TestSetOps(object): +class TestSetOps: def test_intersect1d(self): # unique inputs @@ -30,19 +29,30 @@ def test_intersect1d(self): ed = np.array([1, 2, 5]) c = intersect1d(a, b) assert_array_equal(c, ed) - assert_array_equal([], intersect1d([], [])) - + + def test_intersect1d_array_like(self): + # See gh-11772 + class Test: + def __array__(self): + return np.arange(3) + + a = Test() + res = intersect1d(a, a) + assert_array_equal(res, a) + res = intersect1d([1, 2, 3], [1, 2, 3]) + assert_array_equal(res, [1, 2, 3]) + def test_intersect1d_indices(self): # unique inputs - a = np.array([1, 2, 3, 4]) + a = np.array([1, 2, 3, 4]) b = np.array([2, 1, 4, 6]) c, i1, i2 = intersect1d(a, b, assume_unique=True, return_indices=True) ee = np.array([1, 2, 4]) assert_array_equal(c, ee) assert_array_equal(a[i1], ee) assert_array_equal(b[i2], ee) - + # non-unique inputs a = np.array([1, 2, 2, 3, 4, 3, 2]) b = np.array([1, 8, 4, 2, 2, 3, 2, 3]) @@ -51,7 +61,7 @@ def test_intersect1d_indices(self): assert_array_equal(c, ef) assert_array_equal(a[i1], ef) assert_array_equal(b[i2], ef) - + # non1d, unique inputs a = np.array([[2, 4, 5, 6], [7, 8, 1, 15]]) b = np.array([[3, 2, 7, 6], [10, 12, 8, 9]]) @@ -61,7 +71,7 @@ def test_intersect1d_indices(self): ea = np.array([2, 6, 7, 8]) assert_array_equal(ea, a[ui1]) assert_array_equal(ea, b[ui2]) - + # non1d, not assumed to be uniqueinputs a = np.array([[2, 4, 5, 6, 6], [4, 7, 8, 7, 2]]) b = np.array([[3, 2, 7, 7], [10, 12, 8, 7]]) @@ -71,7 +81,7 @@ def test_intersect1d_indices(self): ea = np.array([2, 7, 8]) assert_array_equal(ea, a[ui1]) assert_array_equal(ea, b[ui2]) - + def test_setxor1d(self): a = np.array([5, 7, 1, 2]) b = np.array([2, 4, 3, 1, 5]) @@ -107,12 +117,83 @@ def test_ediff1d(self): assert_array_equal([-1, 0], ediff1d(zero_elem, to_begin=-1, to_end=0)) assert_array_equal([], ediff1d(one_elem)) assert_array_equal([1], ediff1d(two_elem)) - assert_array_equal([7,1,9], ediff1d(two_elem, to_begin=7, to_end=9)) - assert_array_equal([5,6,1,7,8], ediff1d(two_elem, to_begin=[5,6], to_end=[7,8])) - assert_array_equal([1,9], ediff1d(two_elem, to_end=9)) - assert_array_equal([1,7,8], ediff1d(two_elem, to_end=[7,8])) - assert_array_equal([7,1], ediff1d(two_elem, to_begin=7)) - assert_array_equal([5,6,1], ediff1d(two_elem, to_begin=[5,6])) + assert_array_equal([7, 1, 9], ediff1d(two_elem, to_begin=7, to_end=9)) + assert_array_equal([5, 6, 1, 7, 8], + ediff1d(two_elem, to_begin=[5, 6], to_end=[7, 8])) + assert_array_equal([1, 9], ediff1d(two_elem, to_end=9)) + assert_array_equal([1, 7, 8], ediff1d(two_elem, to_end=[7, 8])) + assert_array_equal([7, 1], ediff1d(two_elem, to_begin=7)) + assert_array_equal([5, 6, 1], ediff1d(two_elem, to_begin=[5, 6])) + + @pytest.mark.parametrize("ary, prepend, append, expected", [ + # should fail because trying to cast + # np.nan standard floating point value + # into an integer array: + (np.array([1, 2, 3], dtype=np.int64), + None, + np.nan, + 'to_end'), + # should fail because attempting + # to downcast to int type: + (np.array([1, 2, 3], dtype=np.int64), + np.array([5, 7, 2], dtype=np.float32), + None, + 'to_begin'), + # should fail because attempting to cast + # two special floating point values + # to integers (on both sides of ary), + # `to_begin` is in the error message as the impl checks this first: + (np.array([1., 3., 9.], dtype=np.int8), + np.nan, + np.nan, + 'to_begin'), + ]) + def test_ediff1d_forbidden_type_casts(self, ary, prepend, append, expected): + # verify resolution of gh-11490 + + # specifically, raise an appropriate + # Exception when attempting to append or + # prepend with an incompatible type + msg = 'dtype of `{}` must be compatible'.format(expected) + with assert_raises_regex(TypeError, msg): + ediff1d(ary=ary, + to_end=append, + to_begin=prepend) + + @pytest.mark.parametrize( + "ary,prepend,append,expected", + [ + (np.array([1, 2, 3], dtype=np.int16), + 2**16, # will be cast to int16 under same kind rule. + 2**16 + 4, + np.array([0, 1, 1, 4], dtype=np.int16)), + (np.array([1, 2, 3], dtype=np.float32), + np.array([5], dtype=np.float64), + None, + np.array([5, 1, 1], dtype=np.float32)), + (np.array([1, 2, 3], dtype=np.int32), + 0, + 0, + np.array([0, 1, 1, 0], dtype=np.int32)), + (np.array([1, 2, 3], dtype=np.int64), + 3, + -9, + np.array([3, 1, 1, -9], dtype=np.int64)), + ] + ) + def test_ediff1d_scalar_handling(self, + ary, + prepend, + append, + expected): + # maintain backwards-compatibility + # of scalar prepend / append behavior + # in ediff1d following fix for gh-11490 + actual = np.ediff1d(ary=ary, + to_end=append, + to_begin=prepend) + assert_equal(actual, expected) + assert actual.dtype == expected.dtype def test_isin(self): # the tests for in1d cover most of isin's behavior @@ -122,33 +203,34 @@ def _isin_slow(a, b): b = np.asarray(b).flatten().tolist() return a in b isin_slow = np.vectorize(_isin_slow, otypes=[bool], excluded={1}) + def assert_isin_equal(a, b): x = isin(a, b) y = isin_slow(a, b) assert_array_equal(x, y) - #multidimensional arrays in both arguments + # multidimensional arrays in both arguments a = np.arange(24).reshape([2, 3, 4]) b = np.array([[10, 20, 30], [0, 1, 3], [11, 22, 33]]) assert_isin_equal(a, b) - #array-likes as both arguments + # array-likes as both arguments c = [(9, 8), (7, 6)] d = (9, 7) assert_isin_equal(c, d) - #zero-d array: + # zero-d array: f = np.array(3) assert_isin_equal(f, b) assert_isin_equal(a, f) assert_isin_equal(f, f) - #scalar: + # scalar: assert_isin_equal(5, b) assert_isin_equal(a, 6) assert_isin_equal(5, 6) - #empty array-like: + # empty array-like: x = [] assert_isin_equal(x, b) assert_isin_equal(a, x) @@ -276,6 +358,39 @@ def test_in1d_both_arrays_have_structured_dtype(self): result = np.in1d(ar1, ar2) assert_array_equal(result, expected) + def test_in1d_with_arrays_containing_tuples(self): + ar1 = np.array([(1,), 2], dtype=object) + ar2 = np.array([(1,), 2], dtype=object) + expected = np.array([True, True]) + result = np.in1d(ar1, ar2) + assert_array_equal(result, expected) + result = np.in1d(ar1, ar2, invert=True) + assert_array_equal(result, np.invert(expected)) + + # An integer is added at the end of the array to make sure + # that the array builder will create the array with tuples + # and after it's created the integer is removed. + # There's a bug in the array constructor that doesn't handle + # tuples properly and adding the integer fixes that. + ar1 = np.array([(1,), (2, 1), 1], dtype=object) + ar1 = ar1[:-1] + ar2 = np.array([(1,), (2, 1), 1], dtype=object) + ar2 = ar2[:-1] + expected = np.array([True, True]) + result = np.in1d(ar1, ar2) + assert_array_equal(result, expected) + result = np.in1d(ar1, ar2, invert=True) + assert_array_equal(result, np.invert(expected)) + + ar1 = np.array([(1,), (2, 3), 1], dtype=object) + ar1 = ar1[:-1] + ar2 = np.array([(1,), 2], dtype=object) + expected = np.array([True, False]) + result = np.in1d(ar1, ar2) + assert_array_equal(result, expected) + result = np.in1d(ar1, ar2, invert=True) + assert_array_equal(result, np.invert(expected)) + def test_union1d(self): a = np.array([5, 4, 7, 1, 2]) b = np.array([2, 4, 3, 3, 2, 1, 5]) @@ -312,6 +427,13 @@ def test_setdiff1d(self): a = np.array((), np.uint32) assert_equal(setdiff1d(a, []).dtype, np.uint32) + def test_setdiff1d_unique(self): + a = np.array([3, 2, 1]) + b = np.array([7, 5, 2]) + expected = np.array([3, 1]) + actual = setdiff1d(a, b, assume_unique=True) + assert_equal(actual, expected) + def test_setdiff1d_char_array(self): a = np.array(['a', 'b', 'c']) b = np.array(['a', 'b', 's']) @@ -328,7 +450,7 @@ def test_manyways(self): assert_array_equal(c1, c2) -class TestUnique(object): +class TestUnique: def test_unique_1d(self): @@ -340,41 +462,41 @@ def check_all(a, b, i1, i2, c, dt): assert_array_equal(v, b, msg) msg = base_msg.format('return_index', dt) - v, j = unique(a, 1, 0, 0) + v, j = unique(a, True, False, False) assert_array_equal(v, b, msg) assert_array_equal(j, i1, msg) msg = base_msg.format('return_inverse', dt) - v, j = unique(a, 0, 1, 0) + v, j = unique(a, False, True, False) assert_array_equal(v, b, msg) assert_array_equal(j, i2, msg) msg = base_msg.format('return_counts', dt) - v, j = unique(a, 0, 0, 1) + v, j = unique(a, False, False, True) assert_array_equal(v, b, msg) assert_array_equal(j, c, msg) msg = base_msg.format('return_index and return_inverse', dt) - v, j1, j2 = unique(a, 1, 1, 0) + v, j1, j2 = unique(a, True, True, False) assert_array_equal(v, b, msg) assert_array_equal(j1, i1, msg) assert_array_equal(j2, i2, msg) msg = base_msg.format('return_index and return_counts', dt) - v, j1, j2 = unique(a, 1, 0, 1) + v, j1, j2 = unique(a, True, False, True) assert_array_equal(v, b, msg) assert_array_equal(j1, i1, msg) assert_array_equal(j2, c, msg) msg = base_msg.format('return_inverse and return_counts', dt) - v, j1, j2 = unique(a, 0, 1, 1) + v, j1, j2 = unique(a, False, True, True) assert_array_equal(v, b, msg) assert_array_equal(j1, i2, msg) assert_array_equal(j2, c, msg) msg = base_msg.format(('return_index, return_inverse ' 'and return_counts'), dt) - v, j1, j2, j3 = unique(a, 1, 1, 1) + v, j1, j2, j3 = unique(a, True, True, True) assert_array_equal(v, b, msg) assert_array_equal(j1, i1, msg) assert_array_equal(j2, i2, msg) @@ -435,12 +557,70 @@ def check_all(a, b, i1, i2, c, dt): a = [] a1_idx = np.unique(a, return_index=True)[1] a2_inv = np.unique(a, return_inverse=True)[1] - a3_idx, a3_inv = np.unique(a, return_index=True, return_inverse=True)[1:] + a3_idx, a3_inv = np.unique(a, return_index=True, + return_inverse=True)[1:] assert_equal(a1_idx.dtype, np.intp) assert_equal(a2_inv.dtype, np.intp) assert_equal(a3_idx.dtype, np.intp) assert_equal(a3_inv.dtype, np.intp) + # test for ticket 2111 - float + a = [2.0, np.nan, 1.0, np.nan] + ua = [1.0, 2.0, np.nan] + ua_idx = [2, 0, 1] + ua_inv = [1, 2, 0, 2] + ua_cnt = [1, 1, 2] + assert_equal(np.unique(a), ua) + assert_equal(np.unique(a, return_index=True), (ua, ua_idx)) + assert_equal(np.unique(a, return_inverse=True), (ua, ua_inv)) + assert_equal(np.unique(a, return_counts=True), (ua, ua_cnt)) + + # test for ticket 2111 - complex + a = [2.0-1j, np.nan, 1.0+1j, complex(0.0, np.nan), complex(1.0, np.nan)] + ua = [1.0+1j, 2.0-1j, complex(0.0, np.nan)] + ua_idx = [2, 0, 3] + ua_inv = [1, 2, 0, 2, 2] + ua_cnt = [1, 1, 3] + assert_equal(np.unique(a), ua) + assert_equal(np.unique(a, return_index=True), (ua, ua_idx)) + assert_equal(np.unique(a, return_inverse=True), (ua, ua_inv)) + assert_equal(np.unique(a, return_counts=True), (ua, ua_cnt)) + + # test for ticket 2111 - datetime64 + nat = np.datetime64('nat') + a = [np.datetime64('2020-12-26'), nat, np.datetime64('2020-12-24'), nat] + ua = [np.datetime64('2020-12-24'), np.datetime64('2020-12-26'), nat] + ua_idx = [2, 0, 1] + ua_inv = [1, 2, 0, 2] + ua_cnt = [1, 1, 2] + assert_equal(np.unique(a), ua) + assert_equal(np.unique(a, return_index=True), (ua, ua_idx)) + assert_equal(np.unique(a, return_inverse=True), (ua, ua_inv)) + assert_equal(np.unique(a, return_counts=True), (ua, ua_cnt)) + + # test for ticket 2111 - timedelta + nat = np.timedelta64('nat') + a = [np.timedelta64(1, 'D'), nat, np.timedelta64(1, 'h'), nat] + ua = [np.timedelta64(1, 'h'), np.timedelta64(1, 'D'), nat] + ua_idx = [2, 0, 1] + ua_inv = [1, 2, 0, 2] + ua_cnt = [1, 1, 2] + assert_equal(np.unique(a), ua) + assert_equal(np.unique(a, return_index=True), (ua, ua_idx)) + assert_equal(np.unique(a, return_inverse=True), (ua, ua_inv)) + assert_equal(np.unique(a, return_counts=True), (ua, ua_cnt)) + + # test for gh-19300 + all_nans = [np.nan] * 4 + ua = [np.nan] + ua_idx = [0] + ua_inv = [0, 0, 0, 0] + ua_cnt = [4] + assert_equal(np.unique(all_nans), ua) + assert_equal(np.unique(all_nans, return_index=True), (ua, ua_idx)) + assert_equal(np.unique(all_nans, return_inverse=True), (ua, ua_inv)) + assert_equal(np.unique(all_nans, return_counts=True), (ua, ua_cnt)) + def test_unique_axis_errors(self): assert_raises(TypeError, self._run_axis_tests, object) assert_raises(TypeError, self._run_axis_tests, @@ -478,9 +658,52 @@ def test_unique_axis(self): result = np.array([[-0.0, 0.0]]) assert_array_equal(unique(data, axis=0), result, msg) + @pytest.mark.parametrize("axis", [0, -1]) + def test_unique_1d_with_axis(self, axis): + x = np.array([4, 3, 2, 3, 2, 1, 2, 2]) + uniq = unique(x, axis=axis) + assert_array_equal(uniq, [1, 2, 3, 4]) + + def test_unique_axis_zeros(self): + # issue 15559 + single_zero = np.empty(shape=(2, 0), dtype=np.int8) + uniq, idx, inv, cnt = unique(single_zero, axis=0, return_index=True, + return_inverse=True, return_counts=True) + + # there's 1 element of shape (0,) along axis 0 + assert_equal(uniq.dtype, single_zero.dtype) + assert_array_equal(uniq, np.empty(shape=(1, 0))) + assert_array_equal(idx, np.array([0])) + assert_array_equal(inv, np.array([0, 0])) + assert_array_equal(cnt, np.array([2])) + + # there's 0 elements of shape (2,) along axis 1 + uniq, idx, inv, cnt = unique(single_zero, axis=1, return_index=True, + return_inverse=True, return_counts=True) + + assert_equal(uniq.dtype, single_zero.dtype) + assert_array_equal(uniq, np.empty(shape=(2, 0))) + assert_array_equal(idx, np.array([])) + assert_array_equal(inv, np.array([])) + assert_array_equal(cnt, np.array([])) + + # test a "complicated" shape + shape = (0, 2, 0, 3, 0, 4, 0) + multiple_zeros = np.empty(shape=shape) + for axis in range(len(shape)): + expected_shape = list(shape) + if shape[axis] == 0: + expected_shape[axis] = 0 + else: + expected_shape[axis] = 1 + + assert_array_equal(unique(multiple_zeros, axis=axis), + np.empty(shape=expected_shape)) + def test_unique_masked(self): # issue 8664 - x = np.array([64, 0, 1, 2, 3, 63, 63, 0, 0, 0, 1, 2, 0, 63, 0], dtype='uint8') + x = np.array([64, 0, 1, 2, 3, 63, 63, 0, 0, 0, 1, 2, 0, 63, 0], + dtype='uint8') y = np.ma.masked_equal(x, 0) v = np.unique(y) @@ -495,7 +718,7 @@ def test_unique_sort_order_with_axis(self): # as unsigned byte strings. See gh-10495. fmt = "sort order incorrect for integer type '%s'" for dt in 'bhilq': - a = np.array([[-1],[0]], dt) + a = np.array([[-1], [0]], dt) b = np.unique(a, axis=0) assert_array_equal(a, b, fmt % dt) @@ -518,8 +741,11 @@ def _run_axis_tests(self, dtype): assert_array_equal(unique(data, axis=1), result.astype(dtype), msg) msg = 'Unique with 3d array and axis=2 failed' - data3d = np.dstack([data] * 3) - result = data3d[..., :1] + data3d = np.array([[[1, 1], + [1, 0]], + [[0, 1], + [0, 0]]]).astype(dtype) + result = np.take(data3d, [1, 0], axis=2) assert_array_equal(unique(data3d, axis=2), result, msg) uniq, idx, inv, cnt = unique(data, axis=0, return_index=True, diff --git a/numpy/lib/tests/test_arrayterator.py b/numpy/lib/tests/test_arrayterator.py index 2ce4456a5b5c..c00ed13d7f30 100644 --- a/numpy/lib/tests/test_arrayterator.py +++ b/numpy/lib/tests/test_arrayterator.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - from operator import mul from functools import reduce diff --git a/numpy/lib/tests/test_financial.py b/numpy/lib/tests/test_financial.py deleted file mode 100644 index 524915041148..000000000000 --- a/numpy/lib/tests/test_financial.py +++ /dev/null @@ -1,340 +0,0 @@ -from __future__ import division, absolute_import, print_function - -from decimal import Decimal - -import numpy as np -from numpy.testing import ( - assert_, assert_almost_equal, assert_allclose, assert_equal, assert_raises - ) - - -class TestFinancial(object): - def test_rate(self): - assert_almost_equal( - np.rate(10, 0, -3500, 10000), - 0.1107, 4) - - def test_rate_decimal(self): - rate = np.rate(Decimal('10'), Decimal('0'), Decimal('-3500'), Decimal('10000')) - assert_equal(Decimal('0.1106908537142689284704528100'), rate) - - def test_irr(self): - v = [-150000, 15000, 25000, 35000, 45000, 60000] - assert_almost_equal(np.irr(v), 0.0524, 2) - v = [-100, 0, 0, 74] - assert_almost_equal(np.irr(v), -0.0955, 2) - v = [-100, 39, 59, 55, 20] - assert_almost_equal(np.irr(v), 0.28095, 2) - v = [-100, 100, 0, -7] - assert_almost_equal(np.irr(v), -0.0833, 2) - v = [-100, 100, 0, 7] - assert_almost_equal(np.irr(v), 0.06206, 2) - v = [-5, 10.5, 1, -8, 1] - assert_almost_equal(np.irr(v), 0.0886, 2) - - # Test that if there is no solution then np.irr returns nan - # Fixes gh-6744 - v = [-1, -2, -3] - assert_equal(np.irr(v), np.nan) - - def test_pv(self): - assert_almost_equal(np.pv(0.07, 20, 12000, 0), -127128.17, 2) - - def test_pv_decimal(self): - assert_equal(np.pv(Decimal('0.07'), Decimal('20'), Decimal('12000'), Decimal('0')), - Decimal('-127128.1709461939327295222005')) - - def test_fv(self): - assert_equal(np.fv(0.075, 20, -2000, 0, 0), 86609.362673042924) - - def test_fv_decimal(self): - assert_equal(np.fv(Decimal('0.075'), Decimal('20'), Decimal('-2000'), 0, 0), - Decimal('86609.36267304300040536731624')) - - def test_pmt(self): - res = np.pmt(0.08 / 12, 5 * 12, 15000) - tgt = -304.145914 - assert_allclose(res, tgt) - # Test the edge case where rate == 0.0 - res = np.pmt(0.0, 5 * 12, 15000) - tgt = -250.0 - assert_allclose(res, tgt) - # Test the case where we use broadcast and - # the arguments passed in are arrays. - res = np.pmt([[0.0, 0.8], [0.3, 0.8]], [12, 3], [2000, 20000]) - tgt = np.array([[-166.66667, -19311.258], [-626.90814, -19311.258]]) - assert_allclose(res, tgt) - - def test_pmt_decimal(self): - res = np.pmt(Decimal('0.08') / Decimal('12'), 5 * 12, 15000) - tgt = Decimal('-304.1459143262052370338701494') - assert_equal(res, tgt) - # Test the edge case where rate == 0.0 - res = np.pmt(Decimal('0'), Decimal('60'), Decimal('15000')) - tgt = -250 - assert_equal(res, tgt) - # Test the case where we use broadcast and - # the arguments passed in are arrays. - res = np.pmt([[Decimal('0'), Decimal('0.8')], [Decimal('0.3'), Decimal('0.8')]], - [Decimal('12'), Decimal('3')], [Decimal('2000'), Decimal('20000')]) - tgt = np.array([[Decimal('-166.6666666666666666666666667'), Decimal('-19311.25827814569536423841060')], - [Decimal('-626.9081401700757748402586600'), Decimal('-19311.25827814569536423841060')]]) - - # Cannot use the `assert_allclose` because it uses isfinite under the covers - # which does not support the Decimal type - # See issue: https://github.com/numpy/numpy/issues/9954 - assert_equal(res[0][0], tgt[0][0]) - assert_equal(res[0][1], tgt[0][1]) - assert_equal(res[1][0], tgt[1][0]) - assert_equal(res[1][1], tgt[1][1]) - - def test_ppmt(self): - assert_equal(np.round(np.ppmt(0.1 / 12, 1, 60, 55000), 2), -710.25) - - def test_ppmt_decimal(self): - assert_equal(np.ppmt(Decimal('0.1') / Decimal('12'), Decimal('1'), Decimal('60'), Decimal('55000')), - Decimal('-710.2541257864217612489830917')) - - # Two tests showing how Decimal is actually getting at a more exact result - # .23 / 12 does not come out nicely as a float but does as a decimal - def test_ppmt_special_rate(self): - assert_equal(np.round(np.ppmt(0.23 / 12, 1, 60, 10000000000), 8), -90238044.232277036) - - def test_ppmt_special_rate_decimal(self): - # When rounded out to 8 decimal places like the float based test, this should not equal the same value - # as the float, substituted for the decimal - def raise_error_because_not_equal(): - assert_equal( - round(np.ppmt(Decimal('0.23') / Decimal('12'), 1, 60, Decimal('10000000000')), 8), - Decimal('-90238044.232277036')) - - assert_raises(AssertionError, raise_error_because_not_equal) - assert_equal(np.ppmt(Decimal('0.23') / Decimal('12'), 1, 60, Decimal('10000000000')), - Decimal('-90238044.2322778884413969909')) - - def test_ipmt(self): - assert_almost_equal(np.round(np.ipmt(0.1 / 12, 1, 24, 2000), 2), -16.67) - - def test_ipmt_decimal(self): - result = np.ipmt(Decimal('0.1') / Decimal('12'), 1, 24, 2000) - assert_equal(result.flat[0], Decimal('-16.66666666666666666666666667')) - - def test_nper(self): - assert_almost_equal(np.nper(0.075, -2000, 0, 100000.), - 21.54, 2) - - def test_nper2(self): - assert_almost_equal(np.nper(0.0, -2000, 0, 100000.), - 50.0, 1) - - def test_npv(self): - assert_almost_equal( - np.npv(0.05, [-15000, 1500, 2500, 3500, 4500, 6000]), - 122.89, 2) - - def test_npv_decimal(self): - assert_equal( - np.npv(Decimal('0.05'), [-15000, 1500, 2500, 3500, 4500, 6000]), - Decimal('122.894854950942692161628715')) - - def test_mirr(self): - val = [-4500, -800, 800, 800, 600, 600, 800, 800, 700, 3000] - assert_almost_equal(np.mirr(val, 0.08, 0.055), 0.0666, 4) - - val = [-120000, 39000, 30000, 21000, 37000, 46000] - assert_almost_equal(np.mirr(val, 0.10, 0.12), 0.126094, 6) - - val = [100, 200, -50, 300, -200] - assert_almost_equal(np.mirr(val, 0.05, 0.06), 0.3428, 4) - - val = [39000, 30000, 21000, 37000, 46000] - assert_(np.isnan(np.mirr(val, 0.10, 0.12))) - - def test_mirr_decimal(self): - val = [Decimal('-4500'), Decimal('-800'), Decimal('800'), Decimal('800'), - Decimal('600'), Decimal('600'), Decimal('800'), Decimal('800'), - Decimal('700'), Decimal('3000')] - assert_equal(np.mirr(val, Decimal('0.08'), Decimal('0.055')), - Decimal('0.066597175031553548874239618')) - - val = [Decimal('-120000'), Decimal('39000'), Decimal('30000'), - Decimal('21000'), Decimal('37000'), Decimal('46000')] - assert_equal(np.mirr(val, Decimal('0.10'), Decimal('0.12')), Decimal('0.126094130365905145828421880')) - - val = [Decimal('100'), Decimal('200'), Decimal('-50'), - Decimal('300'), Decimal('-200')] - assert_equal(np.mirr(val, Decimal('0.05'), Decimal('0.06')), Decimal('0.342823387842176663647819868')) - - val = [Decimal('39000'), Decimal('30000'), Decimal('21000'), Decimal('37000'), Decimal('46000')] - assert_(np.isnan(np.mirr(val, Decimal('0.10'), Decimal('0.12')))) - - def test_when(self): - # begin - assert_equal(np.rate(10, 20, -3500, 10000, 1), - np.rate(10, 20, -3500, 10000, 'begin')) - # end - assert_equal(np.rate(10, 20, -3500, 10000), - np.rate(10, 20, -3500, 10000, 'end')) - assert_equal(np.rate(10, 20, -3500, 10000, 0), - np.rate(10, 20, -3500, 10000, 'end')) - - # begin - assert_equal(np.pv(0.07, 20, 12000, 0, 1), - np.pv(0.07, 20, 12000, 0, 'begin')) - # end - assert_equal(np.pv(0.07, 20, 12000, 0), - np.pv(0.07, 20, 12000, 0, 'end')) - assert_equal(np.pv(0.07, 20, 12000, 0, 0), - np.pv(0.07, 20, 12000, 0, 'end')) - - # begin - assert_equal(np.fv(0.075, 20, -2000, 0, 1), - np.fv(0.075, 20, -2000, 0, 'begin')) - # end - assert_equal(np.fv(0.075, 20, -2000, 0), - np.fv(0.075, 20, -2000, 0, 'end')) - assert_equal(np.fv(0.075, 20, -2000, 0, 0), - np.fv(0.075, 20, -2000, 0, 'end')) - - # begin - assert_equal(np.pmt(0.08 / 12, 5 * 12, 15000., 0, 1), - np.pmt(0.08 / 12, 5 * 12, 15000., 0, 'begin')) - # end - assert_equal(np.pmt(0.08 / 12, 5 * 12, 15000., 0), - np.pmt(0.08 / 12, 5 * 12, 15000., 0, 'end')) - assert_equal(np.pmt(0.08 / 12, 5 * 12, 15000., 0, 0), - np.pmt(0.08 / 12, 5 * 12, 15000., 0, 'end')) - - # begin - assert_equal(np.ppmt(0.1 / 12, 1, 60, 55000, 0, 1), - np.ppmt(0.1 / 12, 1, 60, 55000, 0, 'begin')) - # end - assert_equal(np.ppmt(0.1 / 12, 1, 60, 55000, 0), - np.ppmt(0.1 / 12, 1, 60, 55000, 0, 'end')) - assert_equal(np.ppmt(0.1 / 12, 1, 60, 55000, 0, 0), - np.ppmt(0.1 / 12, 1, 60, 55000, 0, 'end')) - - # begin - assert_equal(np.ipmt(0.1 / 12, 1, 24, 2000, 0, 1), - np.ipmt(0.1 / 12, 1, 24, 2000, 0, 'begin')) - # end - assert_equal(np.ipmt(0.1 / 12, 1, 24, 2000, 0), - np.ipmt(0.1 / 12, 1, 24, 2000, 0, 'end')) - assert_equal(np.ipmt(0.1 / 12, 1, 24, 2000, 0, 0), - np.ipmt(0.1 / 12, 1, 24, 2000, 0, 'end')) - - # begin - assert_equal(np.nper(0.075, -2000, 0, 100000., 1), - np.nper(0.075, -2000, 0, 100000., 'begin')) - # end - assert_equal(np.nper(0.075, -2000, 0, 100000.), - np.nper(0.075, -2000, 0, 100000., 'end')) - assert_equal(np.nper(0.075, -2000, 0, 100000., 0), - np.nper(0.075, -2000, 0, 100000., 'end')) - - def test_decimal_with_when(self): - """Test that decimals are still supported if the when argument is passed""" - # begin - assert_equal(np.rate(Decimal('10'), Decimal('20'), Decimal('-3500'), Decimal('10000'), Decimal('1')), - np.rate(Decimal('10'), Decimal('20'), Decimal('-3500'), Decimal('10000'), 'begin')) - # end - assert_equal(np.rate(Decimal('10'), Decimal('20'), Decimal('-3500'), Decimal('10000')), - np.rate(Decimal('10'), Decimal('20'), Decimal('-3500'), Decimal('10000'), 'end')) - assert_equal(np.rate(Decimal('10'), Decimal('20'), Decimal('-3500'), Decimal('10000'), Decimal('0')), - np.rate(Decimal('10'), Decimal('20'), Decimal('-3500'), Decimal('10000'), 'end')) - - # begin - assert_equal(np.pv(Decimal('0.07'), Decimal('20'), Decimal('12000'), Decimal('0'), Decimal('1')), - np.pv(Decimal('0.07'), Decimal('20'), Decimal('12000'), Decimal('0'), 'begin')) - # end - assert_equal(np.pv(Decimal('0.07'), Decimal('20'), Decimal('12000'), Decimal('0')), - np.pv(Decimal('0.07'), Decimal('20'), Decimal('12000'), Decimal('0'), 'end')) - assert_equal(np.pv(Decimal('0.07'), Decimal('20'), Decimal('12000'), Decimal('0'), Decimal('0')), - np.pv(Decimal('0.07'), Decimal('20'), Decimal('12000'), Decimal('0'), 'end')) - - # begin - assert_equal(np.fv(Decimal('0.075'), Decimal('20'), Decimal('-2000'), Decimal('0'), Decimal('1')), - np.fv(Decimal('0.075'), Decimal('20'), Decimal('-2000'), Decimal('0'), 'begin')) - # end - assert_equal(np.fv(Decimal('0.075'), Decimal('20'), Decimal('-2000'), Decimal('0')), - np.fv(Decimal('0.075'), Decimal('20'), Decimal('-2000'), Decimal('0'), 'end')) - assert_equal(np.fv(Decimal('0.075'), Decimal('20'), Decimal('-2000'), Decimal('0'), Decimal('0')), - np.fv(Decimal('0.075'), Decimal('20'), Decimal('-2000'), Decimal('0'), 'end')) - - # begin - assert_equal(np.pmt(Decimal('0.08') / Decimal('12'), Decimal('5') * Decimal('12'), Decimal('15000.'), - Decimal('0'), Decimal('1')), - np.pmt(Decimal('0.08') / Decimal('12'), Decimal('5') * Decimal('12'), Decimal('15000.'), - Decimal('0'), 'begin')) - # end - assert_equal(np.pmt(Decimal('0.08') / Decimal('12'), Decimal('5') * Decimal('12'), Decimal('15000.'), - Decimal('0')), - np.pmt(Decimal('0.08') / Decimal('12'), Decimal('5') * Decimal('12'), Decimal('15000.'), - Decimal('0'), 'end')) - assert_equal(np.pmt(Decimal('0.08') / Decimal('12'), Decimal('5') * Decimal('12'), Decimal('15000.'), - Decimal('0'), Decimal('0')), - np.pmt(Decimal('0.08') / Decimal('12'), Decimal('5') * Decimal('12'), Decimal('15000.'), - Decimal('0'), 'end')) - - # begin - assert_equal(np.ppmt(Decimal('0.1') / Decimal('12'), Decimal('1'), Decimal('60'), Decimal('55000'), - Decimal('0'), Decimal('1')), - np.ppmt(Decimal('0.1') / Decimal('12'), Decimal('1'), Decimal('60'), Decimal('55000'), - Decimal('0'), 'begin')) - # end - assert_equal(np.ppmt(Decimal('0.1') / Decimal('12'), Decimal('1'), Decimal('60'), Decimal('55000'), - Decimal('0')), - np.ppmt(Decimal('0.1') / Decimal('12'), Decimal('1'), Decimal('60'), Decimal('55000'), - Decimal('0'), 'end')) - assert_equal(np.ppmt(Decimal('0.1') / Decimal('12'), Decimal('1'), Decimal('60'), Decimal('55000'), - Decimal('0'), Decimal('0')), - np.ppmt(Decimal('0.1') / Decimal('12'), Decimal('1'), Decimal('60'), Decimal('55000'), - Decimal('0'), 'end')) - - # begin - assert_equal(np.ipmt(Decimal('0.1') / Decimal('12'), Decimal('1'), Decimal('24'), Decimal('2000'), - Decimal('0'), Decimal('1')).flat[0], - np.ipmt(Decimal('0.1') / Decimal('12'), Decimal('1'), Decimal('24'), Decimal('2000'), - Decimal('0'), 'begin').flat[0]) - # end - assert_equal(np.ipmt(Decimal('0.1') / Decimal('12'), Decimal('1'), Decimal('24'), Decimal('2000'), - Decimal('0')).flat[0], - np.ipmt(Decimal('0.1') / Decimal('12'), Decimal('1'), Decimal('24'), Decimal('2000'), - Decimal('0'), 'end').flat[0]) - assert_equal(np.ipmt(Decimal('0.1') / Decimal('12'), Decimal('1'), Decimal('24'), Decimal('2000'), - Decimal('0'), Decimal('0')).flat[0], - np.ipmt(Decimal('0.1') / Decimal('12'), Decimal('1'), Decimal('24'), Decimal('2000'), - Decimal('0'), 'end').flat[0]) - - def test_broadcast(self): - assert_almost_equal(np.nper(0.075, -2000, 0, 100000., [0, 1]), - [21.5449442, 20.76156441], 4) - - assert_almost_equal(np.ipmt(0.1 / 12, list(range(5)), 24, 2000), - [-17.29165168, -16.66666667, -16.03647345, - -15.40102862, -14.76028842], 4) - - assert_almost_equal(np.ppmt(0.1 / 12, list(range(5)), 24, 2000), - [-74.998201, -75.62318601, -76.25337923, - -76.88882405, -77.52956425], 4) - - assert_almost_equal(np.ppmt(0.1 / 12, list(range(5)), 24, 2000, 0, - [0, 0, 1, 'end', 'begin']), - [-74.998201, -75.62318601, -75.62318601, - -76.88882405, -76.88882405], 4) - - def test_broadcast_decimal(self): - # Use almost equal because precision is tested in the explicit tests, this test is to ensure - # broadcast with Decimal is not broken. - assert_almost_equal(np.ipmt(Decimal('0.1') / Decimal('12'), list(range(5)), Decimal('24'), Decimal('2000')), - [Decimal('-17.29165168'), Decimal('-16.66666667'), Decimal('-16.03647345'), - Decimal('-15.40102862'), Decimal('-14.76028842')], 4) - - assert_almost_equal(np.ppmt(Decimal('0.1') / Decimal('12'), list(range(5)), Decimal('24'), Decimal('2000')), - [Decimal('-74.998201'), Decimal('-75.62318601'), Decimal('-76.25337923'), - Decimal('-76.88882405'), Decimal('-77.52956425')], 4) - - assert_almost_equal(np.ppmt(Decimal('0.1') / Decimal('12'), list(range(5)), Decimal('24'), Decimal('2000'), - Decimal('0'), [Decimal('0'), Decimal('0'), Decimal('1'), 'end', 'begin']), - [Decimal('-74.998201'), Decimal('-75.62318601'), Decimal('-75.62318601'), - Decimal('-76.88882405'), Decimal('-76.88882405')], 4) diff --git a/numpy/lib/tests/test_financial_expired.py b/numpy/lib/tests/test_financial_expired.py new file mode 100644 index 000000000000..70b0cd7909b2 --- /dev/null +++ b/numpy/lib/tests/test_financial_expired.py @@ -0,0 +1,13 @@ +import sys +import pytest +import numpy as np + + +@pytest.mark.skipif(sys.version_info[:2] < (3, 7), + reason="requires python 3.7 or higher") +def test_financial_expired(): + match = 'NEP 32' + with pytest.warns(DeprecationWarning, match=match): + func = np.fv + with pytest.raises(RuntimeError, match=match): + func(1, 2, 3) diff --git a/numpy/lib/tests/test_format.py b/numpy/lib/tests/test_format.py index 38a9b8000661..78e67a89b5a3 100644 --- a/numpy/lib/tests/test_format.py +++ b/numpy/lib/tests/test_format.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - # doctest r''' Test the .npy file format. @@ -278,36 +276,18 @@ ''' import sys import os -import shutil -import tempfile import warnings import pytest from io import BytesIO import numpy as np from numpy.testing import ( - assert_, assert_array_equal, assert_raises, raises, SkipTest + assert_, assert_array_equal, assert_raises, assert_raises_regex, + assert_warns, ) from numpy.lib import format -tempdir = None - -# Module-level setup. - - -def setup_module(): - global tempdir - tempdir = tempfile.mkdtemp() - - -def teardown_module(): - global tempdir - if tempdir is not None and os.path.isdir(tempdir): - shutil.rmtree(tempdir) - tempdir = None - - # Generate some basic arrays to test with. scalars = [ np.uint8, @@ -411,6 +391,7 @@ def teardown_module(): np.array(NbufferT, dtype=np.dtype(Ndescr).newbyteorder('<')), np.array(PbufferT, dtype=np.dtype(Pdescr).newbyteorder('>')), np.array(NbufferT, dtype=np.dtype(Ndescr).newbyteorder('>')), + np.zeros(1, dtype=[('c', ('<f8', (5,)), (2,))]) ] @@ -419,14 +400,14 @@ class BytesIOSRandomSize(BytesIO): def read(self, size=None): import random size = random.randint(1, size) - return super(BytesIOSRandomSize, self).read(size) + return super().read(size) def roundtrip(arr): f = BytesIO() format.write_array(f, arr) f2 = BytesIO(f.getvalue()) - arr2 = format.read_array(f2) + arr2 = format.read_array(f2, allow_pickle=True) return arr2 @@ -477,74 +458,84 @@ def test_long_str(): assert_array_equal(long_str_arr, long_str_arr2) -@pytest.mark.slow -def test_memmap_roundtrip(): - # Fixme: test crashes nose on windows. - if not (sys.platform == 'win32' or sys.platform == 'cygwin'): - for arr in basic_arrays + record_arrays: - if arr.dtype.hasobject: - # Skip these since they can't be mmap'ed. - continue - # Write it out normally and through mmap. - nfn = os.path.join(tempdir, 'normal.npy') - mfn = os.path.join(tempdir, 'memmap.npy') - fp = open(nfn, 'wb') - try: - format.write_array(fp, arr) - finally: - fp.close() - - fortran_order = ( - arr.flags.f_contiguous and not arr.flags.c_contiguous) - ma = format.open_memmap(mfn, mode='w+', dtype=arr.dtype, - shape=arr.shape, fortran_order=fortran_order) - ma[...] = arr - del ma - - # Check that both of these files' contents are the same. - fp = open(nfn, 'rb') +def test_memmap_roundtrip(tmpdir): + for i, arr in enumerate(basic_arrays + record_arrays): + if arr.dtype.hasobject: + # Skip these since they can't be mmap'ed. + continue + # Write it out normally and through mmap. + nfn = os.path.join(tmpdir, f'normal{i}.npy') + mfn = os.path.join(tmpdir, f'memmap{i}.npy') + with open(nfn, 'wb') as fp: + format.write_array(fp, arr) + + fortran_order = ( + arr.flags.f_contiguous and not arr.flags.c_contiguous) + ma = format.open_memmap(mfn, mode='w+', dtype=arr.dtype, + shape=arr.shape, fortran_order=fortran_order) + ma[...] = arr + ma.flush() + + # Check that both of these files' contents are the same. + with open(nfn, 'rb') as fp: normal_bytes = fp.read() - fp.close() - fp = open(mfn, 'rb') + with open(mfn, 'rb') as fp: memmap_bytes = fp.read() - fp.close() - assert_equal_(normal_bytes, memmap_bytes) + assert_equal_(normal_bytes, memmap_bytes) - # Check that reading the file using memmap works. - ma = format.open_memmap(nfn, mode='r') - del ma + # Check that reading the file using memmap works. + ma = format.open_memmap(nfn, mode='r') + ma.flush() -def test_compressed_roundtrip(): +def test_compressed_roundtrip(tmpdir): arr = np.random.rand(200, 200) - npz_file = os.path.join(tempdir, 'compressed.npz') + npz_file = os.path.join(tmpdir, 'compressed.npz') np.savez_compressed(npz_file, arr=arr) - arr1 = np.load(npz_file)['arr'] + with np.load(npz_file) as npz: + arr1 = npz['arr'] + assert_array_equal(arr, arr1) + + +# aligned +dt1 = np.dtype('i1, i4, i1', align=True) +# non-aligned, explicit offsets +dt2 = np.dtype({'names': ['a', 'b'], 'formats': ['i4', 'i4'], + 'offsets': [1, 6]}) +# nested struct-in-struct +dt3 = np.dtype({'names': ['c', 'd'], 'formats': ['i4', dt2]}) +# field with '' name +dt4 = np.dtype({'names': ['a', '', 'b'], 'formats': ['i4']*3}) +# titles +dt5 = np.dtype({'names': ['a', 'b'], 'formats': ['i4', 'i4'], + 'offsets': [1, 6], 'titles': ['aa', 'bb']}) +# empty +dt6 = np.dtype({'names': [], 'formats': [], 'itemsize': 8}) + +@pytest.mark.parametrize("dt", [dt1, dt2, dt3, dt4, dt5, dt6]) +def test_load_padded_dtype(tmpdir, dt): + arr = np.zeros(3, dt) + for i in range(3): + arr[i] = i + 5 + npz_file = os.path.join(tmpdir, 'aligned.npz') + np.savez(npz_file, arr=arr) + with np.load(npz_file) as npz: + arr1 = npz['arr'] assert_array_equal(arr, arr1) def test_python2_python3_interoperability(): - if sys.version_info[0] >= 3: - fname = 'win64python2.npy' - else: - fname = 'python3.npy' + fname = 'win64python2.npy' path = os.path.join(os.path.dirname(__file__), 'data', fname) data = np.load(path) assert_array_equal(data, np.ones(2)) - def test_pickle_python2_python3(): # Test that loading object arrays saved on Python 2 works both on # Python 2 and Python 3 and vice versa data_dir = os.path.join(os.path.dirname(__file__), 'data') - if sys.version_info[0] >= 3: - xrange = range - else: - import __builtin__ - xrange = __builtin__.xrange - - expected = np.array([None, xrange, u'\u512a\u826f', + expected = np.array([None, range, u'\u512a\u826f', b'\xe4\xb8\x8d\xe8\x89\xaf'], dtype=object) @@ -553,41 +544,40 @@ def test_pickle_python2_python3(): path = os.path.join(data_dir, fname) for encoding in ['bytes', 'latin1']: - data_f = np.load(path, encoding=encoding) + data_f = np.load(path, allow_pickle=True, encoding=encoding) if fname.endswith('.npz'): data = data_f['x'] data_f.close() else: data = data_f - if sys.version_info[0] >= 3: - if encoding == 'latin1' and fname.startswith('py2'): - assert_(isinstance(data[3], str)) - assert_array_equal(data[:-1], expected[:-1]) - # mojibake occurs - assert_array_equal(data[-1].encode(encoding), expected[-1]) - else: - assert_(isinstance(data[3], bytes)) - assert_array_equal(data, expected) + if encoding == 'latin1' and fname.startswith('py2'): + assert_(isinstance(data[3], str)) + assert_array_equal(data[:-1], expected[:-1]) + # mojibake occurs + assert_array_equal(data[-1].encode(encoding), expected[-1]) else: + assert_(isinstance(data[3], bytes)) assert_array_equal(data, expected) - if sys.version_info[0] >= 3: - if fname.startswith('py2'): - if fname.endswith('.npz'): - data = np.load(path) - assert_raises(UnicodeError, data.__getitem__, 'x') - data.close() - data = np.load(path, fix_imports=False, encoding='latin1') - assert_raises(ImportError, data.__getitem__, 'x') - data.close() - else: - assert_raises(UnicodeError, np.load, path) - assert_raises(ImportError, np.load, path, - encoding='latin1', fix_imports=False) - - -def test_pickle_disallow(): + if fname.startswith('py2'): + if fname.endswith('.npz'): + data = np.load(path, allow_pickle=True) + assert_raises(UnicodeError, data.__getitem__, 'x') + data.close() + data = np.load(path, allow_pickle=True, fix_imports=False, + encoding='latin1') + assert_raises(ImportError, data.__getitem__, 'x') + data.close() + else: + assert_raises(UnicodeError, np.load, path, + allow_pickle=True) + assert_raises(ImportError, np.load, path, + allow_pickle=True, fix_imports=False, + encoding='latin1') + + +def test_pickle_disallow(tmpdir): data_dir = os.path.join(os.path.dirname(__file__), 'data') path = os.path.join(data_dir, 'py2-objarr.npy') @@ -595,13 +585,68 @@ def test_pickle_disallow(): allow_pickle=False, encoding='latin1') path = os.path.join(data_dir, 'py2-objarr.npz') - f = np.load(path, allow_pickle=False, encoding='latin1') - assert_raises(ValueError, f.__getitem__, 'x') + with np.load(path, allow_pickle=False, encoding='latin1') as f: + assert_raises(ValueError, f.__getitem__, 'x') - path = os.path.join(tempdir, 'pickle-disabled.npy') + path = os.path.join(tmpdir, 'pickle-disabled.npy') assert_raises(ValueError, np.save, path, np.array([None], dtype=object), allow_pickle=False) +@pytest.mark.parametrize('dt', [ + np.dtype(np.dtype([('a', np.int8), + ('b', np.int16), + ('c', np.int32), + ], align=True), + (3,)), + np.dtype([('x', np.dtype({'names':['a','b'], + 'formats':['i1','i1'], + 'offsets':[0,4], + 'itemsize':8, + }, + (3,)), + (4,), + )]), + np.dtype([('x', + ('<f8', (5,)), + (2,), + )]), + np.dtype([('x', np.dtype(( + np.dtype(( + np.dtype({'names':['a','b'], + 'formats':['i1','i1'], + 'offsets':[0,4], + 'itemsize':8}), + (3,) + )), + (4,) + ))) + ]), + np.dtype([ + ('a', np.dtype(( + np.dtype(( + np.dtype(( + np.dtype([ + ('a', int), + ('b', np.dtype({'names':['a','b'], + 'formats':['i1','i1'], + 'offsets':[0,4], + 'itemsize':8})), + ]), + (3,), + )), + (4,), + )), + (5,), + ))) + ]), + ]) + +def test_descr_to_dtype(dt): + dt1 = format.descr_to_dtype(dt.descr) + assert_equal_(dt1, dt) + arr1 = np.zeros(3, dt) + arr2 = roundtrip(arr1) + assert_array_equal(arr1, arr2) def test_version_2_0(): f = BytesIO() @@ -628,31 +673,33 @@ def test_version_2_0(): assert_raises(ValueError, format.write_array, f, d, (1, 0)) -@pytest.mark.slow -def test_version_2_0_memmap(): +def test_version_2_0_memmap(tmpdir): # requires more than 2 byte for header dt = [(("%d" % i) * 100, float) for i in range(500)] d = np.ones(1000, dtype=dt) - tf = tempfile.mktemp('', 'mmap', dir=tempdir) + tf1 = os.path.join(tmpdir, f'version2_01.npy') + tf2 = os.path.join(tmpdir, f'version2_02.npy') # 1.0 requested but data cannot be saved this way - assert_raises(ValueError, format.open_memmap, tf, mode='w+', dtype=d.dtype, + assert_raises(ValueError, format.open_memmap, tf1, mode='w+', dtype=d.dtype, shape=d.shape, version=(1, 0)) - ma = format.open_memmap(tf, mode='w+', dtype=d.dtype, + ma = format.open_memmap(tf1, mode='w+', dtype=d.dtype, shape=d.shape, version=(2, 0)) ma[...] = d - del ma + ma.flush() + ma = format.open_memmap(tf1, mode='r') + assert_array_equal(ma, d) with warnings.catch_warnings(record=True) as w: warnings.filterwarnings('always', '', UserWarning) - ma = format.open_memmap(tf, mode='w+', dtype=d.dtype, + ma = format.open_memmap(tf2, mode='w+', dtype=d.dtype, shape=d.shape, version=None) assert_(w[0].category is UserWarning) ma[...] = d - del ma + ma.flush() - ma = format.open_memmap(tf, mode='r') + ma = format.open_memmap(tf2, mode='r') assert_array_equal(ma, d) @@ -678,12 +725,9 @@ def test_write_version(): (255, 255), ] for version in bad_versions: - try: + with assert_raises_regex(ValueError, + 'we only support format version.*'): format.write_array(f, arr, version=version) - except ValueError: - pass - else: - raise AssertionError("we should have raised a ValueError for the bad version %r" % (version,)) bad_version_magic = [ @@ -807,11 +851,11 @@ def test_bad_header(): assert_raises(ValueError, format.read_array_header_1_0, s) -def test_large_file_support(): +def test_large_file_support(tmpdir): if (sys.platform == 'win32' or sys.platform == 'cygwin'): - raise SkipTest("Unknown if Windows has sparse filesystems") + pytest.skip("Unknown if Windows has sparse filesystems") # try creating a large sparse file - tf_name = os.path.join(tempdir, 'sparse_file') + tf_name = os.path.join(tmpdir, 'sparse_file') try: # seek past end would work too, but linux truncate somewhat # increases the chances that we have a sparse filesystem and can @@ -819,7 +863,7 @@ def test_large_file_support(): import subprocess as sp sp.check_call(["truncate", "-s", "5368709120", tf_name]) except Exception: - raise SkipTest("Could not create 5GB large file") + pytest.skip("Could not create 5GB large file") # write a small array to the end with open(tf_name, "wb") as f: f.seek(5368709120) @@ -835,15 +879,15 @@ def test_large_file_support(): @pytest.mark.skipif(np.dtype(np.intp).itemsize < 8, reason="test requires 64-bit system") @pytest.mark.slow -def test_large_archive(): +def test_large_archive(tmpdir): # Regression test for product of saving arrays with dimensions of array # having a product that doesn't fit in int32. See gh-7598 for details. try: a = np.empty((2**30, 2), dtype=np.uint8) except MemoryError: - raise SkipTest("Could not create large file") + pytest.skip("Could not create large file") - fname = os.path.join(tempdir, "large_archive") + fname = os.path.join(tmpdir, "large_archive") with open(fname, "wb") as f: np.savez(f, arr=a) @@ -852,3 +896,65 @@ def test_large_archive(): new_a = np.load(f)["arr"] assert_(a.shape == new_a.shape) + + +def test_empty_npz(tmpdir): + # Test for gh-9989 + fname = os.path.join(tmpdir, "nothing.npz") + np.savez(fname) + with np.load(fname) as nps: + pass + + +def test_unicode_field_names(tmpdir): + # gh-7391 + arr = np.array([ + (1, 3), + (1, 2), + (1, 3), + (1, 2) + ], dtype=[ + ('int', int), + (u'\N{CJK UNIFIED IDEOGRAPH-6574}\N{CJK UNIFIED IDEOGRAPH-5F62}', int) + ]) + fname = os.path.join(tmpdir, "unicode.npy") + with open(fname, 'wb') as f: + format.write_array(f, arr, version=(3, 0)) + with open(fname, 'rb') as f: + arr2 = format.read_array(f) + assert_array_equal(arr, arr2) + + # notifies the user that 3.0 is selected + with open(fname, 'wb') as f: + with assert_warns(UserWarning): + format.write_array(f, arr, version=None) + + +@pytest.mark.parametrize('dt, fail', [ + (np.dtype({'names': ['a', 'b'], 'formats': [float, np.dtype('S3', + metadata={'some': 'stuff'})]}), True), + (np.dtype(int, metadata={'some': 'stuff'}), False), + (np.dtype([('subarray', (int, (2,)))], metadata={'some': 'stuff'}), False), + # recursive: metadata on the field of a dtype + (np.dtype({'names': ['a', 'b'], 'formats': [ + float, np.dtype({'names': ['c'], 'formats': [np.dtype(int, metadata={})]}) + ]}), False) + ]) +def test_metadata_dtype(dt, fail): + # gh-14142 + arr = np.ones(10, dtype=dt) + buf = BytesIO() + with assert_warns(UserWarning): + np.save(buf, arr) + buf.seek(0) + if fail: + with assert_raises(ValueError): + np.load(buf) + else: + arr2 = np.load(buf) + # BUG: assert_array_equal does not check metadata + from numpy.lib.format import _has_metadata + assert_array_equal(arr, arr2) + assert _has_metadata(arr.dtype) + assert not _has_metadata(arr2.dtype) + diff --git a/numpy/lib/tests/test_function_base.py b/numpy/lib/tests/test_function_base.py index 4103a9eb3031..b67a31b1850e 100644 --- a/numpy/lib/tests/test_function_base.py +++ b/numpy/lib/tests/test_function_base.py @@ -1,31 +1,31 @@ -from __future__ import division, absolute_import, print_function - import operator import warnings import sys import decimal +from fractions import Fraction +import math import pytest +import hypothesis +from hypothesis.extra.numpy import arrays +import hypothesis.strategies as st + import numpy as np from numpy import ma from numpy.testing import ( assert_, assert_equal, assert_array_equal, assert_almost_equal, - assert_array_almost_equal, assert_raises, assert_allclose, - assert_array_max_ulp, assert_warns, assert_raises_regex, suppress_warnings, - HAS_REFCOUNT, + assert_array_almost_equal, assert_raises, assert_allclose, IS_PYPY, + assert_warns, assert_raises_regex, suppress_warnings, HAS_REFCOUNT, ) import numpy.lib.function_base as nfb from numpy.random import rand from numpy.lib import ( add_newdoc_ufunc, angle, average, bartlett, blackman, corrcoef, cov, delete, diff, digitize, extract, flipud, gradient, hamming, hanning, - histogram, histogramdd, i0, insert, interp, kaiser, meshgrid, msort, - piecewise, place, rot90, select, setxor1d, sinc, split, trapz, trim_zeros, - unwrap, unique, vectorize + i0, insert, interp, kaiser, meshgrid, msort, piecewise, place, rot90, + select, setxor1d, sinc, trapz, trim_zeros, unwrap, unique, vectorize ) -from numpy.compat import long - def get_mat(n): data = np.arange(n) @@ -33,7 +33,18 @@ def get_mat(n): return data -class TestRot90(object): +def _make_complex(real, imag): + """ + Like real + 1j * imag, but behaves as expected when imag contains non-finite + values + """ + ret = np.zeros(np.broadcast(real, imag).shape, np.complex_) + ret.real = real + ret.imag = imag + return ret + + +class TestRot90: def test_basic(self): assert_raises(ValueError, rot90, np.ones(4)) assert_raises(ValueError, rot90, np.ones((2,2,2)), axes=(0,1,2)) @@ -101,7 +112,7 @@ def test_rotation_axes(self): rot90(a_rot90_20, k=k-1, axes=(2, 0))) -class TestFlip(object): +class TestFlip: def test_axes(self): assert_raises(np.AxisError, np.flip, np.ones(4), axis=1) @@ -204,7 +215,7 @@ def test_multiple_axes(self): assert_equal(np.flip(a, axis=(1, 2)), c) -class TestAny(object): +class TestAny: def test_basic(self): y1 = [0, 0, 1, 0] @@ -221,7 +232,7 @@ def test_nd(self): assert_array_equal(np.sometrue(y1, axis=1), [0, 1, 1]) -class TestAll(object): +class TestAll: def test_basic(self): y1 = [0, 1, 1, 0] @@ -239,7 +250,7 @@ def test_nd(self): assert_array_equal(np.alltrue(y1, axis=1), [0, 0, 1]) -class TestCopy(object): +class TestCopy: def test_basic(self): a = np.array([[1, 2], [3, 4]]) @@ -266,8 +277,15 @@ def test_order(self): assert_(not a_fort_copy.flags.c_contiguous) assert_(a_fort_copy.flags.f_contiguous) + def test_subok(self): + mx = ma.ones(5) + assert_(not ma.isMaskedArray(np.copy(mx, subok=False))) + assert_(ma.isMaskedArray(np.copy(mx, subok=True))) + # Default behavior + assert_(not ma.isMaskedArray(np.copy(mx))) + -class TestAverage(object): +class TestAverage: def test_basic(self): y1 = np.array([1, 2, 3]) @@ -355,9 +373,9 @@ class subclass(np.ndarray): assert_equal(type(np.average(a, weights=w)), subclass) def test_upcasting(self): - types = [('i4', 'i4', 'f8'), ('i4', 'f4', 'f8'), ('f4', 'i4', 'f8'), + typs = [('i4', 'i4', 'f8'), ('i4', 'f4', 'f8'), ('f4', 'i4', 'f8'), ('f4', 'f4', 'f4'), ('f4', 'f8', 'f8')] - for at, wt, rt in types: + for at, wt, rt in typs: a = np.array([[1,2],[3,4]], dtype=at) w = np.array([[1,2],[3,4]], dtype=wt) assert_equal(np.average(a, weights=w).dtype, np.dtype(rt)) @@ -368,7 +386,7 @@ def test_object_dtype(self): w /= w.sum() assert_almost_equal(a.mean(0), average(a, weights=w)) -class TestSelect(object): +class TestSelect: choices = [np.array([1, 2, 3]), np.array([4, 5, 6]), np.array([7, 8, 9])] @@ -411,27 +429,17 @@ def test_return_dtype(self): assert_equal(select([m], [d]), [0, 0, 0, np.nan, 0, 0]) def test_deprecated_empty(self): - with warnings.catch_warnings(record=True): - warnings.simplefilter("always") - assert_equal(select([], [], 3j), 3j) - - with warnings.catch_warnings(): - warnings.simplefilter("always") - assert_warns(DeprecationWarning, select, [], []) - warnings.simplefilter("error") - assert_raises(DeprecationWarning, select, [], []) + assert_raises(ValueError, select, [], [], 3j) + assert_raises(ValueError, select, [], []) def test_non_bool_deprecation(self): choices = self.choices conditions = self.conditions[:] - with warnings.catch_warnings(): - warnings.filterwarnings("always") - conditions[0] = conditions[0].astype(np.int_) - assert_warns(DeprecationWarning, select, conditions, choices) - conditions[0] = conditions[0].astype(np.uint8) - assert_warns(DeprecationWarning, select, conditions, choices) - warnings.filterwarnings("error") - assert_raises(DeprecationWarning, select, conditions, choices) + conditions[0] = conditions[0].astype(np.int_) + assert_raises(TypeError, select, conditions, choices) + conditions[0] = conditions[0].astype(np.uint8) + assert_raises(TypeError, select, conditions, choices) + assert_raises(TypeError, select, conditions, choices) def test_many_arguments(self): # This used to be limited by NPY_MAXARGS == 32 @@ -440,7 +448,7 @@ def test_many_arguments(self): select(conditions, choices) -class TestInsert(object): +class TestInsert: def test_basic(self): a = [1, 2, 3] @@ -505,12 +513,11 @@ def test_multidim(self): insert(a, 1, a[:, 2, :], axis=1)) def test_0d(self): - # This is an error in the future a = np.array(1) - with warnings.catch_warnings(record=True) as w: - warnings.filterwarnings('always', '', DeprecationWarning) - assert_equal(insert(a, [], 2, axis=0), np.array(2)) - assert_(w[0].category is DeprecationWarning) + with pytest.raises(np.AxisError): + insert(a, [], 2, axis=0) + with pytest.raises(TypeError): + insert(a, [], 2, axis="nonsense") def test_subclass(self): class SubClass(np.ndarray): @@ -540,8 +547,19 @@ def test_structured_array(self): b = np.insert(a, [0, 2], val) assert_array_equal(b[[0, 3]], np.array(val, dtype=b.dtype)) + def test_index_floats(self): + with pytest.raises(IndexError): + np.insert([0, 1, 2], np.array([1.0, 2.0]), [10, 20]) + with pytest.raises(IndexError): + np.insert([0, 1, 2], np.array([], dtype=float), []) + + @pytest.mark.parametrize('idx', [4, -4]) + def test_index_out_of_bounds(self, idx): + with pytest.raises(IndexError, match='out of bounds'): + np.insert([0, 1, 2], [idx], [3, 4]) + -class TestAmax(object): +class TestAmax: def test_basic(self): a = [3, 4, 5, 10, -3, -5, 6.0] @@ -553,7 +571,7 @@ def test_basic(self): assert_equal(np.amax(b, axis=1), [9.0, 10.0, 8.0]) -class TestAmin(object): +class TestAmin: def test_basic(self): a = [3, 4, 5, 10, -3, -5, 6.0] @@ -565,7 +583,7 @@ def test_basic(self): assert_equal(np.amin(b, axis=1), [3.0, 4.0, 2.0]) -class TestPtp(object): +class TestPtp: def test_basic(self): a = np.array([3, 4, 5, 10, -3, -5, 6.0]) @@ -580,7 +598,7 @@ def test_basic(self): assert_equal(b.ptp(axis=(0,1), keepdims=True), [[8.0]]) -class TestCumsum(object): +class TestCumsum: def test_basic(self): ba = [1, 2, 10, 11, 6, 5, 4] @@ -603,7 +621,7 @@ def test_basic(self): assert_array_equal(np.cumsum(a2, axis=1), tgt) -class TestProd(object): +class TestProd: def test_basic(self): ba = [1, 2, 10, 11, 6, 5, 4] @@ -623,7 +641,7 @@ def test_basic(self): np.array([24, 1890, 600], ctype)) -class TestCumprod(object): +class TestCumprod: def test_basic(self): ba = [1, 2, 10, 11, 6, 5, 4] @@ -650,7 +668,7 @@ def test_basic(self): [10, 30, 120, 600]], ctype)) -class TestDiff(object): +class TestDiff: def test_basic(self): x = [1, 4, 6, 7, 12] @@ -684,6 +702,9 @@ def test_axis(self): assert_raises(np.AxisError, diff, x, axis=3) assert_raises(np.AxisError, diff, x, axis=-4) + x = np.array(1.11111111111, np.float64) + assert_raises(ValueError, diff, x) + def test_nd(self): x = 20 * rand(10, 20, 30) out1 = x[:, :, 1:] - x[:, :, :-1] @@ -734,8 +755,60 @@ def test_subclass(self): assert_array_equal(out3.mask, [[], [], [], [], []]) assert_(type(out3) is type(x)) + def test_prepend(self): + x = np.arange(5) + 1 + assert_array_equal(diff(x, prepend=0), np.ones(5)) + assert_array_equal(diff(x, prepend=[0]), np.ones(5)) + assert_array_equal(np.cumsum(np.diff(x, prepend=0)), x) + assert_array_equal(diff(x, prepend=[-1, 0]), np.ones(6)) + + x = np.arange(4).reshape(2, 2) + result = np.diff(x, axis=1, prepend=0) + expected = [[0, 1], [2, 1]] + assert_array_equal(result, expected) + result = np.diff(x, axis=1, prepend=[[0], [0]]) + assert_array_equal(result, expected) + + result = np.diff(x, axis=0, prepend=0) + expected = [[0, 1], [2, 2]] + assert_array_equal(result, expected) + result = np.diff(x, axis=0, prepend=[[0, 0]]) + assert_array_equal(result, expected) + + assert_raises(ValueError, np.diff, x, prepend=np.zeros((3,3))) + + assert_raises(np.AxisError, diff, x, prepend=0, axis=3) + + def test_append(self): + x = np.arange(5) + result = diff(x, append=0) + expected = [1, 1, 1, 1, -4] + assert_array_equal(result, expected) + result = diff(x, append=[0]) + assert_array_equal(result, expected) + result = diff(x, append=[0, 2]) + expected = expected + [2] + assert_array_equal(result, expected) + + x = np.arange(4).reshape(2, 2) + result = np.diff(x, axis=1, append=0) + expected = [[1, -1], [1, -3]] + assert_array_equal(result, expected) + result = np.diff(x, axis=1, append=[[0], [0]]) + assert_array_equal(result, expected) -class TestDelete(object): + result = np.diff(x, axis=0, append=0) + expected = [[2, 2], [-2, -3]] + assert_array_equal(result, expected) + result = np.diff(x, axis=0, append=[[0, 0]]) + assert_array_equal(result, expected) + + assert_raises(ValueError, np.diff, x, append=np.zeros((3,3))) + + assert_raises(np.AxisError, diff, x, append=0, axis=3) + + +class TestDelete: def setup(self): self.a = np.arange(5) @@ -745,10 +818,6 @@ def _check_inverse_of_slicing(self, indices): a_del = delete(self.a, indices) nd_a_del = delete(self.nd_a, indices, axis=1) msg = 'Delete failed for obj: %r' % indices - # NOTE: The cast should be removed after warning phase for bools - if not isinstance(indices, (slice, int, long, np.integer)): - indices = np.asarray(indices, dtype=np.intp) - indices = indices[(indices >= 0) & (indices < 5)] assert_array_equal(setxor1d(a_del, self.a[indices, ]), self.a, err_msg=msg) xor = setxor1d(nd_a_del[0,:, 0], self.nd_a[0, indices, 0]) @@ -764,19 +833,25 @@ def test_slices(self): self._check_inverse_of_slicing(s) def test_fancy(self): - # Deprecation/FutureWarning tests should be kept after change. self._check_inverse_of_slicing(np.array([[0, 1], [2, 1]])) - with warnings.catch_warnings(): - warnings.filterwarnings('error', category=DeprecationWarning) - assert_raises(DeprecationWarning, delete, self.a, [100]) - assert_raises(DeprecationWarning, delete, self.a, [-100]) - with warnings.catch_warnings(record=True) as w: - warnings.filterwarnings('always', category=FutureWarning) - self._check_inverse_of_slicing([0, -1, 2, 2]) - obj = np.array([True, False, False], dtype=bool) - self._check_inverse_of_slicing(obj) - assert_(w[0].category is FutureWarning) - assert_(w[1].category is FutureWarning) + with pytest.raises(IndexError): + delete(self.a, [100]) + with pytest.raises(IndexError): + delete(self.a, [-100]) + + self._check_inverse_of_slicing([0, -1, 2, 2]) + + self._check_inverse_of_slicing([True, False, False, True, False]) + + # not legal, indexing with these would change the dimension + with pytest.raises(ValueError): + delete(self.a, True) + with pytest.raises(ValueError): + delete(self.a, False) + + # not enough items + with pytest.raises(ValueError): + delete(self.a, [False]*4) def test_single(self): self._check_inverse_of_slicing(0) @@ -784,10 +859,10 @@ def test_single(self): def test_0d(self): a = np.array(1) - with warnings.catch_warnings(record=True) as w: - warnings.filterwarnings('always', '', DeprecationWarning) - assert_equal(delete(a, [], axis=0), a) - assert_(w[0].category is DeprecationWarning) + with pytest.raises(np.AxisError): + delete(a, [], axis=0) + with pytest.raises(TypeError): + delete(a, [], axis="nonsense") def test_subclass(self): class SubClass(np.ndarray): @@ -809,8 +884,14 @@ def test_array_order_preserve(self): assert_equal(m.flags.c_contiguous, k.flags.c_contiguous) assert_equal(m.flags.f_contiguous, k.flags.f_contiguous) + def test_index_floats(self): + with pytest.raises(IndexError): + np.delete([0, 1, 2], np.array([1.0, 2.0])) + with pytest.raises(IndexError): + np.delete([0, 1, 2], np.array([], dtype=float)) -class TestGradient(object): + +class TestGradient: def test_basic(self): v = [[1, 1], [3, 4]] @@ -881,7 +962,7 @@ def test_masked(self): assert_equal(type(out), type(x)) # And make sure that the output and input don't have aliased mask # arrays - assert_(x.mask is not out.mask) + assert_(x._mask is not out._mask) # Also check that edge_order=2 doesn't alter the original mask x2 = np.ma.arange(5) x2[2] = np.ma.masked @@ -1027,8 +1108,42 @@ def test_values(self): assert_raises(ValueError, gradient, np.arange(1), edge_order=2) assert_raises(ValueError, gradient, np.arange(2), edge_order=2) - -class TestAngle(object): + @pytest.mark.parametrize('f_dtype', [np.uint8, np.uint16, + np.uint32, np.uint64]) + def test_f_decreasing_unsigned_int(self, f_dtype): + f = np.array([5, 4, 3, 2, 1], dtype=f_dtype) + g = gradient(f) + assert_array_equal(g, [-1]*len(f)) + + @pytest.mark.parametrize('f_dtype', [np.int8, np.int16, + np.int32, np.int64]) + def test_f_signed_int_big_jump(self, f_dtype): + maxint = np.iinfo(f_dtype).max + x = np.array([1, 3]) + f = np.array([-1, maxint], dtype=f_dtype) + dfdx = gradient(f, x) + assert_array_equal(dfdx, [(maxint + 1) // 2]*2) + + @pytest.mark.parametrize('x_dtype', [np.uint8, np.uint16, + np.uint32, np.uint64]) + def test_x_decreasing_unsigned(self, x_dtype): + x = np.array([3, 2, 1], dtype=x_dtype) + f = np.array([0, 2, 4]) + dfdx = gradient(f, x) + assert_array_equal(dfdx, [-2]*len(x)) + + @pytest.mark.parametrize('x_dtype', [np.int8, np.int16, + np.int32, np.int64]) + def test_x_signed_int_big_jump(self, x_dtype): + minint = np.iinfo(x_dtype).min + maxint = np.iinfo(x_dtype).max + x = np.array([-1, maxint], dtype=x_dtype) + f = np.array([minint // 2, 0]) + dfdx = gradient(f, x) + assert_array_equal(dfdx, [0.5, 0.5]) + + +class TestAngle: def test_basic(self): x = [1 + 3j, np.sqrt(2) / 2.0 + 1j * np.sqrt(2) / 2, @@ -1038,36 +1153,88 @@ def test_basic(self): np.arctan(3.0 / 1.0), np.arctan(1.0), 0, np.pi / 2, np.pi, -np.pi / 2.0, -np.arctan(3.0 / 1.0), np.pi - np.arctan(3.0 / 1.0)] - z = angle(x, deg=1) + z = angle(x, deg=True) zo = np.array(yo) * 180 / np.pi assert_array_almost_equal(y, yo, 11) assert_array_almost_equal(z, zo, 11) + def test_subclass(self): + x = np.ma.array([1 + 3j, 1, np.sqrt(2)/2 * (1 + 1j)]) + x[1] = np.ma.masked + expected = np.ma.array([np.arctan(3.0 / 1.0), 0, np.arctan(1.0)]) + expected[1] = np.ma.masked + actual = angle(x) + assert_equal(type(actual), type(expected)) + assert_equal(actual.mask, expected.mask) + assert_equal(actual, expected) + -class TestTrimZeros(object): +class TestTrimZeros: - """ - Only testing for integer splits. + a = np.array([0, 0, 1, 0, 2, 3, 4, 0]) + b = a.astype(float) + c = a.astype(complex) + d = a.astype(object) - """ + def values(self): + attr_names = ('a', 'b', 'c', 'd') + return (getattr(self, name) for name in attr_names) def test_basic(self): - a = np.array([0, 0, 1, 2, 3, 4, 0]) - res = trim_zeros(a) - assert_array_equal(res, np.array([1, 2, 3, 4])) + slc = np.s_[2:-1] + for arr in self.values(): + res = trim_zeros(arr) + assert_array_equal(res, arr[slc]) def test_leading_skip(self): - a = np.array([0, 0, 1, 0, 2, 3, 4, 0]) - res = trim_zeros(a) - assert_array_equal(res, np.array([1, 0, 2, 3, 4])) + slc = np.s_[:-1] + for arr in self.values(): + res = trim_zeros(arr, trim='b') + assert_array_equal(res, arr[slc]) def test_trailing_skip(self): - a = np.array([0, 0, 1, 0, 2, 3, 0, 4, 0]) - res = trim_zeros(a) - assert_array_equal(res, np.array([1, 0, 2, 3, 0, 4])) + slc = np.s_[2:] + for arr in self.values(): + res = trim_zeros(arr, trim='F') + assert_array_equal(res, arr[slc]) + + def test_all_zero(self): + for _arr in self.values(): + arr = np.zeros_like(_arr, dtype=_arr.dtype) + + res1 = trim_zeros(arr, trim='B') + assert len(res1) == 0 + + res2 = trim_zeros(arr, trim='f') + assert len(res2) == 0 + + def test_size_zero(self): + arr = np.zeros(0) + res = trim_zeros(arr) + assert_array_equal(arr, res) + + @pytest.mark.parametrize( + 'arr', + [np.array([0, 2**62, 0]), + np.array([0, 2**63, 0]), + np.array([0, 2**64, 0])] + ) + def test_overflow(self, arr): + slc = np.s_[1:2] + res = trim_zeros(arr) + assert_array_equal(res, arr[slc]) + + def test_no_trim(self): + arr = np.array([None, 1, None]) + res = trim_zeros(arr) + assert_array_equal(arr, res) + + def test_list_to_list(self): + res = trim_zeros(self.a.tolist()) + assert isinstance(res, list) -class TestExtins(object): +class TestExtins: def test_basic(self): a = np.array([1, 3, 2, 1, 2, 3, 3]) @@ -1106,7 +1273,17 @@ def test_both(self): assert_array_equal(a, ac) -class TestVectorize(object): +# _foo1 and _foo2 are used in some tests in TestVectorize. + +def _foo1(x, y=1.0): + return y*math.floor(x) + + +def _foo2(x, y=1.0, z=0.0): + return y*math.floor(x) + z + + +class TestVectorize: def test_simple(self): def addsubtract(a, b): @@ -1137,7 +1314,6 @@ def test_large(self): assert_array_equal(y, x) def test_ufunc(self): - import math f = vectorize(math.cos) args = np.array([0, 0.5 * np.pi, np.pi, 1.5 * np.pi, 2 * np.pi]) r1 = f(args) @@ -1158,6 +1334,63 @@ def foo(a, b=1): r2 = np.array([3, 4, 5]) assert_array_equal(r1, r2) + def test_keywords_with_otypes_order1(self): + # gh-1620: The second call of f would crash with + # `ValueError: invalid number of arguments`. + f = vectorize(_foo1, otypes=[float]) + # We're testing the caching of ufuncs by vectorize, so the order + # of these function calls is an important part of the test. + r1 = f(np.arange(3.0), 1.0) + r2 = f(np.arange(3.0)) + assert_array_equal(r1, r2) + + def test_keywords_with_otypes_order2(self): + # gh-1620: The second call of f would crash with + # `ValueError: non-broadcastable output operand with shape () + # doesn't match the broadcast shape (3,)`. + f = vectorize(_foo1, otypes=[float]) + # We're testing the caching of ufuncs by vectorize, so the order + # of these function calls is an important part of the test. + r1 = f(np.arange(3.0)) + r2 = f(np.arange(3.0), 1.0) + assert_array_equal(r1, r2) + + def test_keywords_with_otypes_order3(self): + # gh-1620: The third call of f would crash with + # `ValueError: invalid number of arguments`. + f = vectorize(_foo1, otypes=[float]) + # We're testing the caching of ufuncs by vectorize, so the order + # of these function calls is an important part of the test. + r1 = f(np.arange(3.0)) + r2 = f(np.arange(3.0), y=1.0) + r3 = f(np.arange(3.0)) + assert_array_equal(r1, r2) + assert_array_equal(r1, r3) + + def test_keywords_with_otypes_several_kwd_args1(self): + # gh-1620 Make sure different uses of keyword arguments + # don't break the vectorized function. + f = vectorize(_foo2, otypes=[float]) + # We're testing the caching of ufuncs by vectorize, so the order + # of these function calls is an important part of the test. + r1 = f(10.4, z=100) + r2 = f(10.4, y=-1) + r3 = f(10.4) + assert_equal(r1, _foo2(10.4, z=100)) + assert_equal(r2, _foo2(10.4, y=-1)) + assert_equal(r3, _foo2(10.4)) + + def test_keywords_with_otypes_several_kwd_args2(self): + # gh-1620 Make sure different uses of keyword arguments + # don't break the vectorized function. + f = vectorize(_foo2, otypes=[float]) + # We're testing the caching of ufuncs by vectorize, so the order + # of these function calls is an important part of the test. + r1 = f(z=100, x=10.4, y=-1) + r2 = f(1, 2, 3) + assert_equal(r1, _foo2(z=100, x=10.4, y=-1)) + assert_equal(r2, _foo2(1, 2, 3)) + def test_keywords_no_func_code(self): # This needs to test a function that has keywords but # no func_code attribute, since otherwise vectorize will @@ -1300,6 +1533,21 @@ def test_parse_gufunc_signature(self): ([('x',)], [('y',), ()])) assert_equal(nfb._parse_gufunc_signature('(),(a,b,c),(d)->(d,e)'), ([(), ('a', 'b', 'c'), ('d',)], [('d', 'e')])) + + # Tests to check if whitespaces are ignored + assert_equal(nfb._parse_gufunc_signature('(x )->()'), ([('x',)], [()])) + assert_equal(nfb._parse_gufunc_signature('( x , y )->( )'), + ([('x', 'y')], [()])) + assert_equal(nfb._parse_gufunc_signature('(x),( y) ->()'), + ([('x',), ('y',)], [()])) + assert_equal(nfb._parse_gufunc_signature('( x)-> (y ) '), + ([('x',)], [('y',)])) + assert_equal(nfb._parse_gufunc_signature(' (x)->( y),( )'), + ([('x',)], [('y',), ()])) + assert_equal(nfb._parse_gufunc_signature( + '( ), ( a, b,c ) ,( d) -> (d , e)'), + ([(), ('a', 'b', 'c'), ('d',)], [('d', 'e')])) + with assert_raises(ValueError): nfb._parse_gufunc_signature('(x)(y)->()') with assert_raises(ValueError): @@ -1437,8 +1685,68 @@ def test_size_zero_output(self): with assert_raises_regex(ValueError, 'new output dimensions'): f(x) + def test_subclasses(self): + class subclass(np.ndarray): + pass + + m = np.array([[1., 0., 0.], + [0., 0., 1.], + [0., 1., 0.]]).view(subclass) + v = np.array([[1., 2., 3.], [4., 5., 6.], [7., 8., 9.]]).view(subclass) + # generalized (gufunc) + matvec = np.vectorize(np.matmul, signature='(m,m),(m)->(m)') + r = matvec(m, v) + assert_equal(type(r), subclass) + assert_equal(r, [[1., 3., 2.], [4., 6., 5.], [7., 9., 8.]]) + + # element-wise (ufunc) + mult = np.vectorize(lambda x, y: x*y) + r = mult(m, v) + assert_equal(type(r), subclass) + assert_equal(r, m * v) + + +class TestLeaks: + class A: + iters = 20 -class TestDigitize(object): + def bound(self, *args): + return 0 + + @staticmethod + def unbound(*args): + return 0 + + @pytest.mark.skipif(not HAS_REFCOUNT, reason="Python lacks refcounts") + @pytest.mark.parametrize('name, incr', [ + ('bound', A.iters), + ('unbound', 0), + ]) + def test_frompyfunc_leaks(self, name, incr): + # exposed in gh-11867 as np.vectorized, but the problem stems from + # frompyfunc. + # class.attribute = np.frompyfunc(<method>) creates a + # reference cycle if <method> is a bound class method. It requires a + # gc collection cycle to break the cycle (on CPython 3) + import gc + A_func = getattr(self.A, name) + gc.disable() + try: + refcount = sys.getrefcount(A_func) + for i in range(self.A.iters): + a = self.A() + a.f = np.frompyfunc(getattr(a, name), 1, 1) + out = a.f(np.arange(10)) + a = None + # A.func is part of a reference cycle if incr is non-zero + assert_equal(sys.getrefcount(A_func), refcount + incr) + for i in range(5): + gc.collect() + assert_equal(sys.getrefcount(A_func), refcount) + finally: + gc.enable() + +class TestDigitize: def test_forward(self): x = np.arange(-6, 5) @@ -1510,8 +1818,20 @@ class A(np.ndarray): assert_(not isinstance(digitize(b, a, False), A)) assert_(not isinstance(digitize(b, a, True), A)) + def test_large_integers_increasing(self): + # gh-11022 + x = 2**54 # loses precision in a float + assert_equal(np.digitize(x, [x - 1, x + 1]), 1) + + @pytest.mark.xfail( + reason="gh-11022: np.core.multiarray._monoticity loses precision") + def test_large_integers_decreasing(self): + # gh-11022 + x = 2**54 # loses precision in a float + assert_equal(np.digitize(x, [x + 1, x - 1]), 1) + -class TestUnwrap(object): +class TestUnwrap: def test_simple(self): # check that unwrap removes jumps greater that 2*pi @@ -1519,39 +1839,138 @@ def test_simple(self): # check that unwrap maintains continuity assert_(np.all(diff(unwrap(rand(10) * 100)) < np.pi)) + def test_period(self): + # check that unwrap removes jumps greater that 255 + assert_array_equal(unwrap([1, 1 + 256], period=255), [1, 2]) + # check that unwrap maintains continuity + assert_(np.all(diff(unwrap(rand(10) * 1000, period=255)) < 255)) + # check simple case + simple_seq = np.array([0, 75, 150, 225, 300]) + wrap_seq = np.mod(simple_seq, 255) + assert_array_equal(unwrap(wrap_seq, period=255), simple_seq) + # check custom discont value + uneven_seq = np.array([0, 75, 150, 225, 300, 430]) + wrap_uneven = np.mod(uneven_seq, 250) + no_discont = unwrap(wrap_uneven, period=250) + assert_array_equal(no_discont, [0, 75, 150, 225, 300, 180]) + sm_discont = unwrap(wrap_uneven, period=250, discont=140) + assert_array_equal(sm_discont, [0, 75, 150, 225, 300, 430]) + assert sm_discont.dtype == wrap_uneven.dtype + + +@pytest.mark.parametrize( + "dtype", "O" + np.typecodes["AllInteger"] + np.typecodes["Float"] +) +@pytest.mark.parametrize("M", [0, 1, 10]) +class TestFilterwindows: + + def test_hanning(self, dtype: str, M: int) -> None: + scalar = np.array(M, dtype=dtype)[()] + + w = hanning(scalar) + if dtype == "O": + ref_dtype = np.float64 + else: + ref_dtype = np.result_type(scalar.dtype, np.float64) + assert w.dtype == ref_dtype -class TestFilterwindows(object): + # check symmetry + assert_equal(w, flipud(w)) + + # check known value + if scalar < 1: + assert_array_equal(w, np.array([])) + elif scalar == 1: + assert_array_equal(w, np.ones(1)) + else: + assert_almost_equal(np.sum(w, axis=0), 4.500, 4) + + def test_hamming(self, dtype: str, M: int) -> None: + scalar = np.array(M, dtype=dtype)[()] + + w = hamming(scalar) + if dtype == "O": + ref_dtype = np.float64 + else: + ref_dtype = np.result_type(scalar.dtype, np.float64) + assert w.dtype == ref_dtype - def test_hanning(self): # check symmetry - w = hanning(10) - assert_array_almost_equal(w, flipud(w), 7) + assert_equal(w, flipud(w)) + # check known value - assert_almost_equal(np.sum(w, axis=0), 4.500, 4) + if scalar < 1: + assert_array_equal(w, np.array([])) + elif scalar == 1: + assert_array_equal(w, np.ones(1)) + else: + assert_almost_equal(np.sum(w, axis=0), 4.9400, 4) + + def test_bartlett(self, dtype: str, M: int) -> None: + scalar = np.array(M, dtype=dtype)[()] + + w = bartlett(scalar) + if dtype == "O": + ref_dtype = np.float64 + else: + ref_dtype = np.result_type(scalar.dtype, np.float64) + assert w.dtype == ref_dtype - def test_hamming(self): # check symmetry - w = hamming(10) - assert_array_almost_equal(w, flipud(w), 7) + assert_equal(w, flipud(w)) + # check known value - assert_almost_equal(np.sum(w, axis=0), 4.9400, 4) + if scalar < 1: + assert_array_equal(w, np.array([])) + elif scalar == 1: + assert_array_equal(w, np.ones(1)) + else: + assert_almost_equal(np.sum(w, axis=0), 4.4444, 4) + + def test_blackman(self, dtype: str, M: int) -> None: + scalar = np.array(M, dtype=dtype)[()] + + w = blackman(scalar) + if dtype == "O": + ref_dtype = np.float64 + else: + ref_dtype = np.result_type(scalar.dtype, np.float64) + assert w.dtype == ref_dtype - def test_bartlett(self): # check symmetry - w = bartlett(10) - assert_array_almost_equal(w, flipud(w), 7) + assert_equal(w, flipud(w)) + # check known value - assert_almost_equal(np.sum(w, axis=0), 4.4444, 4) + if scalar < 1: + assert_array_equal(w, np.array([])) + elif scalar == 1: + assert_array_equal(w, np.ones(1)) + else: + assert_almost_equal(np.sum(w, axis=0), 3.7800, 4) + + def test_kaiser(self, dtype: str, M: int) -> None: + scalar = np.array(M, dtype=dtype)[()] + + w = kaiser(scalar, 0) + if dtype == "O": + ref_dtype = np.float64 + else: + ref_dtype = np.result_type(scalar.dtype, np.float64) + assert w.dtype == ref_dtype - def test_blackman(self): # check symmetry - w = blackman(10) - assert_array_almost_equal(w, flipud(w), 7) + assert_equal(w, flipud(w)) + # check known value - assert_almost_equal(np.sum(w, axis=0), 3.7800, 4) + if scalar < 1: + assert_array_equal(w, np.array([])) + elif scalar == 1: + assert_array_equal(w, np.ones(1)) + else: + assert_almost_equal(np.sum(w, axis=0), 10, 15) -class TestTrapz(object): +class TestTrapz: def test_simple(self): x = np.arange(-10, 10, .1) @@ -1613,7 +2032,7 @@ def test_masked(self): assert_almost_equal(trapz(y, xm), r) -class TestSinc(object): +class TestSinc: def test_simple(self): assert_(sinc(0) == 1) @@ -1630,7 +2049,7 @@ def test_array_like(self): assert_array_equal(y1, y3) -class TestUnique(object): +class TestUnique: def test_simple(self): x = np.array([4, 3, 2, 1, 1, 2, 3, 4, 0]) @@ -1642,7 +2061,7 @@ def test_simple(self): assert_(np.all(unique(x) == [1 + 1j, 1 + 10j, 5 + 6j, 10])) -class TestCheckFinite(object): +class TestCheckFinite: def test_simple(self): a = [1, 2, 3] @@ -1659,7 +2078,7 @@ def test_dtype_order(self): assert_(a.dtype == np.float64) -class TestCorrCoef(object): +class TestCorrCoef: A = np.array( [[0.15391142, 0.18045767, 0.14197213], [0.70461506, 0.96474128, 0.27906989], @@ -1743,15 +2162,21 @@ def test_extreme(self): assert_array_almost_equal(c, np.array([[1., -1.], [-1., 1.]])) assert_(np.all(np.abs(c) <= 1.0)) + @pytest.mark.parametrize("test_type", [np.half, np.single, np.double, np.longdouble]) + def test_corrcoef_dtype(self, test_type): + cast_A = self.A.astype(test_type) + res = corrcoef(cast_A, dtype=test_type) + assert test_type == res.dtype + -class TestCov(object): +class TestCov: x1 = np.array([[0, 2], [1, 1], [2, 0]]).T res1 = np.array([[1., -1.], [-1., 1.]]) x2 = np.array([0.0, 1.0, 2.0], ndmin=2) frequencies = np.array([1, 4, 1]) x2_repeats = np.array([[0.0], [1.0], [1.0], [1.0], [1.0], [2.0]]).T res2 = np.array([[0.4, -0.4], [-0.4, 0.4]]) - unit_frequencies = np.ones(3, dtype=np.integer) + unit_frequencies = np.ones(3, dtype=np.int_) weights = np.array([1.0, 4.0, 1.0]) res3 = np.array([[2. / 3., -2. / 3.], [-2. / 3., 2. / 3.]]) unit_weights = np.ones(3) @@ -1788,9 +2213,9 @@ def test_wrong_ddof(self): [-np.inf, np.inf]])) def test_1D_rowvar(self): - assert_allclose(cov(self.x3), cov(self.x3, rowvar=0)) + assert_allclose(cov(self.x3), cov(self.x3, rowvar=False)) y = np.array([0.0780, 0.3107, 0.2111, 0.0334, 0.8501]) - assert_allclose(cov(self.x3, y), cov(self.x3, y, rowvar=0)) + assert_allclose(cov(self.x3, y), cov(self.x3, y, rowvar=False)) def test_1D_variance(self): assert_allclose(cov(self.x3, ddof=1), np.var(self.x3, ddof=1)) @@ -1804,11 +2229,11 @@ def test_fweights(self): self.res1) nonint = self.frequencies + 0.5 assert_raises(TypeError, cov, self.x1, fweights=nonint) - f = np.ones((2, 3), dtype=np.integer) + f = np.ones((2, 3), dtype=np.int_) assert_raises(RuntimeError, cov, self.x1, fweights=f) - f = np.ones(2, dtype=np.integer) + f = np.ones(2, dtype=np.int_) assert_raises(RuntimeError, cov, self.x1, fweights=f) - f = -1 * np.ones(3, dtype=np.integer) + f = -1 * np.ones(3, dtype=np.int_) assert_raises(ValueError, cov, self.x1, fweights=f) def test_aweights(self): @@ -1843,18 +2268,25 @@ def test_unit_fweights_and_aweights(self): aweights=self.unit_weights), self.res1) + @pytest.mark.parametrize("test_type", [np.half, np.single, np.double, np.longdouble]) + def test_cov_dtype(self, test_type): + cast_x1 = self.x1.astype(test_type) + res = cov(cast_x1, dtype=test_type) + assert test_type == res.dtype -class Test_I0(object): + +class Test_I0: def test_simple(self): assert_almost_equal( i0(0.5), np.array(1.0634833707413234)) - A = np.array([0.49842636, 0.6969809, 0.22011976, 0.0155549]) - assert_almost_equal( - i0(A), - np.array([1.06307822, 1.12518299, 1.01214991, 1.00006049])) + # need at least one test above 8, as the implementation is piecewise + A = np.array([0.49842636, 0.6969809, 0.22011976, 0.0155549, 10.0]) + expected = np.array([1.06307822, 1.12518299, 1.01214991, 1.00006049, 2815.71662847]) + assert_almost_equal(i0(A), expected) + assert_almost_equal(i0(-A), expected) B = np.array([[0.827002, 0.99959078], [0.89694769, 0.39298162], @@ -1868,9 +2300,33 @@ def test_simple(self): [1.03633899, 1.00067775], [1.03352052, 1.13557954], [1.05884290, 1.06432317]])) + # Regression test for gh-11205 + i0_0 = np.i0([0.]) + assert_equal(i0_0.shape, (1,)) + assert_array_equal(np.i0([0.]), np.array([1.])) + def test_non_array(self): + a = np.arange(4) + + class array_like: + __array_interface__ = a.__array_interface__ + + def __array_wrap__(self, arr): + return self + + # E.g. pandas series survive ufunc calls through array-wrap: + assert isinstance(np.abs(array_like()), array_like) + exp = np.i0(a) + res = np.i0(array_like()) + + assert_array_equal(exp, res) + + def test_complex(self): + a = np.array([0, 1 + 2j]) + with pytest.raises(TypeError, match="i0 not supported for complex values"): + res = i0(a) -class TestKaiser(object): +class TestKaiser: def test_simple(self): assert_(np.isfinite(kaiser(1, 1.0))) @@ -1889,7 +2345,7 @@ def test_int_beta(self): kaiser(3, 4) -class TestMsort(object): +class TestMsort: def test_simple(self): A = np.array([[0.44567325, 0.79115165, 0.54900530], @@ -1902,7 +2358,7 @@ def test_simple(self): [0.64864341, 0.79115165, 0.96098397]])) -class TestMeshgrid(object): +class TestMeshgrid: def test_simple(self): [X, Y] = meshgrid([1, 2, 3], [4, 5, 6, 7]) @@ -1990,8 +2446,29 @@ def test_writeback(self): assert_equal(x[0, :], 0) assert_equal(x[1, :], X) + def test_nd_shape(self): + a, b, c, d, e = np.meshgrid(*([0] * i for i in range(1, 6))) + expected_shape = (2, 1, 3, 4, 5) + assert_equal(a.shape, expected_shape) + assert_equal(b.shape, expected_shape) + assert_equal(c.shape, expected_shape) + assert_equal(d.shape, expected_shape) + assert_equal(e.shape, expected_shape) + + def test_nd_values(self): + a, b, c = np.meshgrid([0], [1, 2], [3, 4, 5]) + assert_equal(a, [[[0, 0, 0]], [[0, 0, 0]]]) + assert_equal(b, [[[1, 1, 1]], [[2, 2, 2]]]) + assert_equal(c, [[[3, 4, 5]], [[3, 4, 5]]]) + + def test_nd_indexing(self): + a, b, c = np.meshgrid([0], [1, 2], [3, 4, 5], indexing='ij') + assert_equal(a, [[[0, 0, 0], [0, 0, 0]]]) + assert_equal(b, [[[1, 1, 1], [2, 2, 2]]]) + assert_equal(c, [[[3, 4, 5], [3, 4, 5]]]) -class TestPiecewise(object): + +class TestPiecewise: def test_simple(self): # Condition is single bool list @@ -2082,8 +2559,16 @@ def test_multidimensional_extrafunc(self): assert_array_equal(y, np.array([[-1., -1., -1.], [3., 3., 1.]])) + def test_subclasses(self): + class subclass(np.ndarray): + pass + x = np.arange(5.).view(subclass) + r = piecewise(x, [x<2., x>=4], [-1., 1., 0.]) + assert_equal(type(r), subclass) + assert_equal(r, [-1., -1., 0., 0., 1.]) + -class TestBincount(object): +class TestBincount: def test_simple(self): y = np.bincount(np.arange(4)) @@ -2169,8 +2654,17 @@ def test_dtype_reference_leaks(self): assert_equal(sys.getrefcount(np.dtype(np.intp)), intp_refcount) assert_equal(sys.getrefcount(np.dtype(np.double)), double_refcount) + @pytest.mark.parametrize("vals", [[[2, 2]], 2]) + def test_error_not_1d(self, vals): + # Test that values has to be 1-D (both as array and nested list) + vals_arr = np.asarray(vals) + with assert_raises(ValueError): + np.bincount(vals_arr) + with assert_raises(ValueError): + np.bincount(vals) + -class TestInterp(object): +class TestInterp: def test_exceptions(self): assert_raises(ValueError, interp, 0, [], []) @@ -2237,6 +2731,72 @@ def test_scalar_interpolation_point(self): x0 = np.nan assert_almost_equal(np.interp(x0, x, y), x0) + def test_non_finite_behavior_exact_x(self): + x = [1, 2, 2.5, 3, 4] + xp = [1, 2, 3, 4] + fp = [1, 2, np.inf, 4] + assert_almost_equal(np.interp(x, xp, fp), [1, 2, np.inf, np.inf, 4]) + fp = [1, 2, np.nan, 4] + assert_almost_equal(np.interp(x, xp, fp), [1, 2, np.nan, np.nan, 4]) + + @pytest.fixture(params=[ + lambda x: np.float_(x), + lambda x: _make_complex(x, 0), + lambda x: _make_complex(0, x), + lambda x: _make_complex(x, np.multiply(x, -2)) + ], ids=[ + 'real', + 'complex-real', + 'complex-imag', + 'complex-both' + ]) + def sc(self, request): + """ scale function used by the below tests """ + return request.param + + def test_non_finite_any_nan(self, sc): + """ test that nans are propagated """ + assert_equal(np.interp(0.5, [np.nan, 1], sc([ 0, 10])), sc(np.nan)) + assert_equal(np.interp(0.5, [ 0, np.nan], sc([ 0, 10])), sc(np.nan)) + assert_equal(np.interp(0.5, [ 0, 1], sc([np.nan, 10])), sc(np.nan)) + assert_equal(np.interp(0.5, [ 0, 1], sc([ 0, np.nan])), sc(np.nan)) + + def test_non_finite_inf(self, sc): + """ Test that interp between opposite infs gives nan """ + assert_equal(np.interp(0.5, [-np.inf, +np.inf], sc([ 0, 10])), sc(np.nan)) + assert_equal(np.interp(0.5, [ 0, 1], sc([-np.inf, +np.inf])), sc(np.nan)) + assert_equal(np.interp(0.5, [ 0, 1], sc([+np.inf, -np.inf])), sc(np.nan)) + + # unless the y values are equal + assert_equal(np.interp(0.5, [-np.inf, +np.inf], sc([ 10, 10])), sc(10)) + + def test_non_finite_half_inf_xf(self, sc): + """ Test that interp where both axes have a bound at inf gives nan """ + assert_equal(np.interp(0.5, [-np.inf, 1], sc([-np.inf, 10])), sc(np.nan)) + assert_equal(np.interp(0.5, [-np.inf, 1], sc([+np.inf, 10])), sc(np.nan)) + assert_equal(np.interp(0.5, [-np.inf, 1], sc([ 0, -np.inf])), sc(np.nan)) + assert_equal(np.interp(0.5, [-np.inf, 1], sc([ 0, +np.inf])), sc(np.nan)) + assert_equal(np.interp(0.5, [ 0, +np.inf], sc([-np.inf, 10])), sc(np.nan)) + assert_equal(np.interp(0.5, [ 0, +np.inf], sc([+np.inf, 10])), sc(np.nan)) + assert_equal(np.interp(0.5, [ 0, +np.inf], sc([ 0, -np.inf])), sc(np.nan)) + assert_equal(np.interp(0.5, [ 0, +np.inf], sc([ 0, +np.inf])), sc(np.nan)) + + def test_non_finite_half_inf_x(self, sc): + """ Test interp where the x axis has a bound at inf """ + assert_equal(np.interp(0.5, [-np.inf, -np.inf], sc([0, 10])), sc(10)) + assert_equal(np.interp(0.5, [-np.inf, 1 ], sc([0, 10])), sc(10)) + assert_equal(np.interp(0.5, [ 0, +np.inf], sc([0, 10])), sc(0)) + assert_equal(np.interp(0.5, [+np.inf, +np.inf], sc([0, 10])), sc(0)) + + def test_non_finite_half_inf_f(self, sc): + """ Test interp where the f axis has a bound at inf """ + assert_equal(np.interp(0.5, [0, 1], sc([ 0, -np.inf])), sc(-np.inf)) + assert_equal(np.interp(0.5, [0, 1], sc([ 0, +np.inf])), sc(+np.inf)) + assert_equal(np.interp(0.5, [0, 1], sc([-np.inf, 10])), sc(-np.inf)) + assert_equal(np.interp(0.5, [0, 1], sc([+np.inf, 10])), sc(+np.inf)) + assert_equal(np.interp(0.5, [0, 1], sc([-np.inf, -np.inf])), sc(-np.inf)) + assert_equal(np.interp(0.5, [0, 1], sc([+np.inf, +np.inf])), sc(+np.inf)) + def test_complex_interp(self): # test complex interpolation x = np.linspace(0, 1, 5) @@ -2251,6 +2811,12 @@ def test_complex_interp(self): x0 = 2.0 right = 2 + 3.0j assert_almost_equal(np.interp(x0, x, y, right=right), right) + # test complex non finite + x = [1, 2, 2.5, 3, 4] + xp = [1, 2, 3, 4] + fp = [1, 2+1j, np.inf, 4] + y = [1, 2+1j, np.inf+0.5j, np.inf, 4] + assert_almost_equal(np.interp(x, xp, fp), y) # test complex periodic x = [-180, -170, -185, 185, -10, -5, 0, 365] xp = [190, -190, 350, -350] @@ -2292,12 +2858,7 @@ def test_period(self): assert_almost_equal(np.interp(x, xp, fp, period=360), y) -def compare_results(res, desired): - for i in range(len(desired)): - assert_array_equal(res[i], desired[i]) - - -class TestPercentile(object): +class TestPercentile: def test_basic(self): x = np.arange(8) * 0.5 @@ -2305,11 +2866,27 @@ def test_basic(self): assert_equal(np.percentile(x, 100), 3.5) assert_equal(np.percentile(x, 50), 1.75) x[1] = np.nan - with warnings.catch_warnings(record=True) as w: - warnings.filterwarnings('always', '', RuntimeWarning) - assert_equal(np.percentile(x, 0), np.nan) - assert_equal(np.percentile(x, 0, interpolation='nearest'), np.nan) - assert_(w[0].category is RuntimeWarning) + assert_equal(np.percentile(x, 0), np.nan) + assert_equal(np.percentile(x, 0, method='nearest'), np.nan) + + def test_fraction(self): + x = [Fraction(i, 2) for i in range(8)] + + p = np.percentile(x, Fraction(0)) + assert_equal(p, Fraction(0)) + assert_equal(type(p), Fraction) + + p = np.percentile(x, Fraction(100)) + assert_equal(p, Fraction(7, 2)) + assert_equal(type(p), Fraction) + + p = np.percentile(x, Fraction(50)) + assert_equal(p, Fraction(7, 4)) + assert_equal(type(p), Fraction) + + p = np.percentile(x, [Fraction(50)]) + assert_equal(p, np.array([Fraction(7, 4)])) + assert_equal(type(p), np.ndarray) def test_api(self): d = np.ones(5) @@ -2326,36 +2903,95 @@ def test_2D(self): [1, 1, 1]]) assert_array_equal(np.percentile(x, 50, axis=0), [1, 1, 1]) - def test_linear(self): - - # Test defaults - assert_equal(np.percentile(range(10), 50), 4.5) - - # explicitly specify interpolation_method 'linear' (the default) - assert_equal(np.percentile(range(10), 50, - interpolation='linear'), 4.5) - - def test_lower_higher(self): - - # interpolation_method 'lower'/'higher' - assert_equal(np.percentile(range(10), 50, - interpolation='lower'), 4) - assert_equal(np.percentile(range(10), 50, - interpolation='higher'), 5) - - def test_midpoint(self): - assert_equal(np.percentile(range(10), 51, - interpolation='midpoint'), 4.5) - assert_equal(np.percentile(range(11), 51, - interpolation='midpoint'), 5.5) - assert_equal(np.percentile(range(11), 50, - interpolation='midpoint'), 5) - - def test_nearest(self): - assert_equal(np.percentile(range(10), 51, - interpolation='nearest'), 5) - assert_equal(np.percentile(range(10), 49, - interpolation='nearest'), 4) + @pytest.mark.parametrize("dtype", np.typecodes["AllFloat"]) + def test_linear_nan_1D(self, dtype): + # METHOD 1 of H&F + arr = np.asarray([15.0, np.NAN, 35.0, 40.0, 50.0], dtype=dtype) + res = np.percentile( + arr, + 40.0, + method="linear") + np.testing.assert_equal(res, np.NAN) + np.testing.assert_equal(res.dtype, arr.dtype) + + H_F_TYPE_CODES = [(int_type, np.float64) + for int_type in np.typecodes["AllInteger"] + ] + [(np.float16, np.float64), + (np.float32, np.float64), + (np.float64, np.float64), + (np.longdouble, np.longdouble), + (np.complex64, np.complex128), + (np.complex128, np.complex128), + (np.clongdouble, np.clongdouble), + (np.dtype("O"), np.float64)] + + @pytest.mark.parametrize(["input_dtype", "expected_dtype"], H_F_TYPE_CODES) + @pytest.mark.parametrize(["method", "expected"], + [("inverted_cdf", 20), + ("averaged_inverted_cdf", 27.5), + ("closest_observation", 20), + ("interpolated_inverted_cdf", 20), + ("hazen", 27.5), + ("weibull", 26), + ("linear", 29), + ("median_unbiased", 27), + ("normal_unbiased", 27.125), + ]) + def test_linear_interpolation(self, + method, + expected, + input_dtype, + expected_dtype): + arr = np.asarray([15.0, 20.0, 35.0, 40.0, 50.0], dtype=input_dtype) + actual = np.percentile(arr, 40.0, method=method) + + np.testing.assert_almost_equal(actual, expected, 14) + + if method in ["inverted_cdf", "closest_observation"]: + if input_dtype == "O": + np.testing.assert_equal(np.asarray(actual).dtype, np.float64) + else: + np.testing.assert_equal(np.asarray(actual).dtype, + np.dtype(input_dtype)) + else: + np.testing.assert_equal(np.asarray(actual).dtype, + np.dtype(expected_dtype)) + + TYPE_CODES = np.typecodes["AllInteger"] + np.typecodes["AllFloat"] + "O" + + @pytest.mark.parametrize("dtype", TYPE_CODES) + def test_lower_higher(self, dtype): + assert_equal(np.percentile(np.arange(10, dtype=dtype), 50, + method='lower'), 4) + assert_equal(np.percentile(np.arange(10, dtype=dtype), 50, + method='higher'), 5) + + @pytest.mark.parametrize("dtype", TYPE_CODES) + def test_midpoint(self, dtype): + assert_equal(np.percentile(np.arange(10, dtype=dtype), 51, + method='midpoint'), 4.5) + assert_equal(np.percentile(np.arange(9, dtype=dtype) + 1, 50, + method='midpoint'), 5) + assert_equal(np.percentile(np.arange(11, dtype=dtype), 51, + method='midpoint'), 5.5) + assert_equal(np.percentile(np.arange(11, dtype=dtype), 50, + method='midpoint'), 5) + + @pytest.mark.parametrize("dtype", TYPE_CODES) + def test_nearest(self, dtype): + assert_equal(np.percentile(np.arange(10, dtype=dtype), 51, + method='nearest'), 5) + assert_equal(np.percentile(np.arange(10, dtype=dtype), 49, + method='nearest'), 4) + + def test_linear_interpolation_extrapolation(self): + arr = np.random.rand(5) + + actual = np.percentile(arr, 100) + np.testing.assert_equal(actual, arr.max()) + + actual = np.percentile(arr, 0) + np.testing.assert_equal(actual, arr.min()) def test_sequence(self): x = np.arange(8) * 0.5 @@ -2383,19 +3019,19 @@ def test_axis(self): assert_equal( np.percentile(x, (25, 50, 75), axis=1).shape, (3, 3, 5, 6)) assert_equal(np.percentile(x, (25, 50), - interpolation="higher").shape, (2,)) + method="higher").shape, (2,)) assert_equal(np.percentile(x, (25, 50, 75), - interpolation="higher").shape, (3,)) + method="higher").shape, (3,)) assert_equal(np.percentile(x, (25, 50), axis=0, - interpolation="higher").shape, (2, 4, 5, 6)) + method="higher").shape, (2, 4, 5, 6)) assert_equal(np.percentile(x, (25, 50), axis=1, - interpolation="higher").shape, (2, 3, 5, 6)) + method="higher").shape, (2, 3, 5, 6)) assert_equal(np.percentile(x, (25, 50), axis=2, - interpolation="higher").shape, (2, 3, 4, 6)) + method="higher").shape, (2, 3, 4, 6)) assert_equal(np.percentile(x, (25, 50), axis=3, - interpolation="higher").shape, (2, 3, 4, 5)) + method="higher").shape, (2, 3, 4, 5)) assert_equal(np.percentile(x, (25, 50, 75), axis=1, - interpolation="higher").shape, (3, 3, 5, 6)) + method="higher").shape, (3, 3, 5, 6)) def test_scalar_q(self): # test for no empty dimensions for compatibility with old percentile @@ -2421,33 +3057,33 @@ def test_scalar_q(self): # test for no empty dimensions for compatibility with old percentile x = np.arange(12).reshape(3, 4) - assert_equal(np.percentile(x, 50, interpolation='lower'), 5.) + assert_equal(np.percentile(x, 50, method='lower'), 5.) assert_(np.isscalar(np.percentile(x, 50))) r0 = np.array([4., 5., 6., 7.]) - c0 = np.percentile(x, 50, interpolation='lower', axis=0) + c0 = np.percentile(x, 50, method='lower', axis=0) assert_equal(c0, r0) assert_equal(c0.shape, r0.shape) r1 = np.array([1., 5., 9.]) - c1 = np.percentile(x, 50, interpolation='lower', axis=1) + c1 = np.percentile(x, 50, method='lower', axis=1) assert_almost_equal(c1, r1) assert_equal(c1.shape, r1.shape) out = np.empty((), dtype=x.dtype) - c = np.percentile(x, 50, interpolation='lower', out=out) + c = np.percentile(x, 50, method='lower', out=out) assert_equal(c, 5) assert_equal(out, 5) out = np.empty(4, dtype=x.dtype) - c = np.percentile(x, 50, interpolation='lower', axis=0, out=out) + c = np.percentile(x, 50, method='lower', axis=0, out=out) assert_equal(c, r0) assert_equal(out, r0) out = np.empty(3, dtype=x.dtype) - c = np.percentile(x, 50, interpolation='lower', axis=1, out=out) + c = np.percentile(x, 50, method='lower', axis=1, out=out) assert_equal(c, r1) assert_equal(out, r1) def test_exception(self): assert_raises(ValueError, np.percentile, [1, 2], 56, - interpolation='foobar') + method='foobar') assert_raises(ValueError, np.percentile, [1], 101) assert_raises(ValueError, np.percentile, [1], -1) assert_raises(ValueError, np.percentile, [1], list(range(50)) + [101]) @@ -2461,18 +3097,18 @@ def test_percentile_out(self): y = np.zeros((3,)) p = (1, 2, 3) np.percentile(x, p, out=y) - assert_equal(y, np.percentile(x, p)) + assert_equal(np.percentile(x, p), y) x = np.array([[1, 2, 3], [4, 5, 6]]) y = np.zeros((3, 3)) np.percentile(x, p, axis=0, out=y) - assert_equal(y, np.percentile(x, p, axis=0)) + assert_equal(np.percentile(x, p, axis=0), y) y = np.zeros((3, 2)) np.percentile(x, p, axis=1, out=y) - assert_equal(y, np.percentile(x, p, axis=1)) + assert_equal(np.percentile(x, p, axis=1), y) x = np.arange(12).reshape(3, 4) # q.dim > 1, float @@ -2488,12 +3124,12 @@ def test_percentile_out(self): # q.dim > 1, int r0 = np.array([[0, 1, 2, 3], [4, 5, 6, 7]]) out = np.empty((2, 4), dtype=x.dtype) - c = np.percentile(x, (25, 50), interpolation='lower', axis=0, out=out) + c = np.percentile(x, (25, 50), method='lower', axis=0, out=out) assert_equal(c, r0) assert_equal(out, r0) r1 = np.array([[0, 4, 8], [1, 5, 9]]) out = np.empty((2, 3), dtype=x.dtype) - c = np.percentile(x, (25, 50), interpolation='lower', axis=1, out=out) + c = np.percentile(x, (25, 50), method='lower', axis=1, out=out) assert_equal(c, r1) assert_equal(out, r1) @@ -2510,10 +3146,10 @@ def test_percentile_empty_dim(self): assert_array_equal(np.percentile(d, 50, axis=-4).shape, (1, 2, 1)) assert_array_equal(np.percentile(d, 50, axis=2, - interpolation='midpoint').shape, + method='midpoint').shape, (11, 1, 1)) assert_array_equal(np.percentile(d, 50, axis=-2, - interpolation='midpoint').shape, + method='midpoint').shape, (11, 1, 1)) assert_array_equal(np.array(np.percentile(d, [10, 50], axis=0)).shape, @@ -2536,10 +3172,10 @@ def test_percentile_no_overwrite(self): def test_no_p_overwrite(self): p = np.linspace(0., 100., num=5) - np.percentile(np.arange(100.), p, interpolation="midpoint") + np.percentile(np.arange(100.), p, method="midpoint") assert_array_equal(p, np.linspace(0., 100., num=5)) p = np.linspace(0., 100., num=5).tolist() - np.percentile(np.arange(100.), p, interpolation="midpoint") + np.percentile(np.arange(100.), p, method="midpoint") assert_array_equal(p, np.linspace(0., 100., num=5).tolist()) def test_percentile_overwrite(self): @@ -2617,14 +3253,14 @@ def test_out(self): o = np.zeros((4,)) d = np.ones((3, 4)) assert_equal(np.percentile(d, 0, 0, out=o), o) - assert_equal(np.percentile(d, 0, 0, interpolation='nearest', out=o), o) + assert_equal(np.percentile(d, 0, 0, method='nearest', out=o), o) o = np.zeros((3,)) assert_equal(np.percentile(d, 1, 1, out=o), o) - assert_equal(np.percentile(d, 1, 1, interpolation='nearest', out=o), o) + assert_equal(np.percentile(d, 1, 1, method='nearest', out=o), o) o = np.zeros(()) assert_equal(np.percentile(d, 2, out=o), o) - assert_equal(np.percentile(d, 2, interpolation='nearest', out=o), o) + assert_equal(np.percentile(d, 2, method='nearest', out=o), o) def test_out_nan(self): with warnings.catch_warnings(record=True): @@ -2634,123 +3270,248 @@ def test_out_nan(self): d[2, 1] = np.nan assert_equal(np.percentile(d, 0, 0, out=o), o) assert_equal( - np.percentile(d, 0, 0, interpolation='nearest', out=o), o) + np.percentile(d, 0, 0, method='nearest', out=o), o) o = np.zeros((3,)) assert_equal(np.percentile(d, 1, 1, out=o), o) assert_equal( - np.percentile(d, 1, 1, interpolation='nearest', out=o), o) + np.percentile(d, 1, 1, method='nearest', out=o), o) o = np.zeros(()) assert_equal(np.percentile(d, 1, out=o), o) assert_equal( - np.percentile(d, 1, interpolation='nearest', out=o), o) + np.percentile(d, 1, method='nearest', out=o), o) def test_nan_behavior(self): a = np.arange(24, dtype=float) a[2] = np.nan - with warnings.catch_warnings(record=True) as w: - warnings.filterwarnings('always', '', RuntimeWarning) - assert_equal(np.percentile(a, 0.3), np.nan) - assert_equal(np.percentile(a, 0.3, axis=0), np.nan) - assert_equal(np.percentile(a, [0.3, 0.6], axis=0), - np.array([np.nan] * 2)) - assert_(w[0].category is RuntimeWarning) - assert_(w[1].category is RuntimeWarning) - assert_(w[2].category is RuntimeWarning) + assert_equal(np.percentile(a, 0.3), np.nan) + assert_equal(np.percentile(a, 0.3, axis=0), np.nan) + assert_equal(np.percentile(a, [0.3, 0.6], axis=0), + np.array([np.nan] * 2)) a = np.arange(24, dtype=float).reshape(2, 3, 4) a[1, 2, 3] = np.nan a[1, 1, 2] = np.nan # no axis - with warnings.catch_warnings(record=True) as w: - warnings.filterwarnings('always', '', RuntimeWarning) - assert_equal(np.percentile(a, 0.3), np.nan) - assert_equal(np.percentile(a, 0.3).ndim, 0) - assert_(w[0].category is RuntimeWarning) + assert_equal(np.percentile(a, 0.3), np.nan) + assert_equal(np.percentile(a, 0.3).ndim, 0) # axis0 zerod b = np.percentile(np.arange(24, dtype=float).reshape(2, 3, 4), 0.3, 0) b[2, 3] = np.nan b[1, 2] = np.nan - with warnings.catch_warnings(record=True) as w: - warnings.filterwarnings('always', '', RuntimeWarning) - assert_equal(np.percentile(a, 0.3, 0), b) + assert_equal(np.percentile(a, 0.3, 0), b) # axis0 not zerod b = np.percentile(np.arange(24, dtype=float).reshape(2, 3, 4), [0.3, 0.6], 0) b[:, 2, 3] = np.nan b[:, 1, 2] = np.nan - with warnings.catch_warnings(record=True) as w: - warnings.filterwarnings('always', '', RuntimeWarning) - assert_equal(np.percentile(a, [0.3, 0.6], 0), b) + assert_equal(np.percentile(a, [0.3, 0.6], 0), b) # axis1 zerod b = np.percentile(np.arange(24, dtype=float).reshape(2, 3, 4), 0.3, 1) b[1, 3] = np.nan b[1, 2] = np.nan - with warnings.catch_warnings(record=True) as w: - warnings.filterwarnings('always', '', RuntimeWarning) - assert_equal(np.percentile(a, 0.3, 1), b) + assert_equal(np.percentile(a, 0.3, 1), b) # axis1 not zerod b = np.percentile( np.arange(24, dtype=float).reshape(2, 3, 4), [0.3, 0.6], 1) b[:, 1, 3] = np.nan b[:, 1, 2] = np.nan - with warnings.catch_warnings(record=True) as w: - warnings.filterwarnings('always', '', RuntimeWarning) - assert_equal(np.percentile(a, [0.3, 0.6], 1), b) + assert_equal(np.percentile(a, [0.3, 0.6], 1), b) # axis02 zerod b = np.percentile( np.arange(24, dtype=float).reshape(2, 3, 4), 0.3, (0, 2)) b[1] = np.nan b[2] = np.nan - with warnings.catch_warnings(record=True) as w: - warnings.filterwarnings('always', '', RuntimeWarning) - assert_equal(np.percentile(a, 0.3, (0, 2)), b) + assert_equal(np.percentile(a, 0.3, (0, 2)), b) # axis02 not zerod b = np.percentile(np.arange(24, dtype=float).reshape(2, 3, 4), [0.3, 0.6], (0, 2)) b[:, 1] = np.nan b[:, 2] = np.nan - with warnings.catch_warnings(record=True) as w: - warnings.filterwarnings('always', '', RuntimeWarning) - assert_equal(np.percentile(a, [0.3, 0.6], (0, 2)), b) - # axis02 not zerod with nearest interpolation + assert_equal(np.percentile(a, [0.3, 0.6], (0, 2)), b) + # axis02 not zerod with method='nearest' b = np.percentile(np.arange(24, dtype=float).reshape(2, 3, 4), - [0.3, 0.6], (0, 2), interpolation='nearest') + [0.3, 0.6], (0, 2), method='nearest') b[:, 1] = np.nan b[:, 2] = np.nan - with warnings.catch_warnings(record=True) as w: - warnings.filterwarnings('always', '', RuntimeWarning) - assert_equal(np.percentile( - a, [0.3, 0.6], (0, 2), interpolation='nearest'), b) - - -class TestQuantile(object): + assert_equal(np.percentile( + a, [0.3, 0.6], (0, 2), method='nearest'), b) + + def test_nan_q(self): + # GH18830 + with pytest.raises(ValueError, match="Percentiles must be in"): + np.percentile([1, 2, 3, 4.0], np.nan) + with pytest.raises(ValueError, match="Percentiles must be in"): + np.percentile([1, 2, 3, 4.0], [np.nan]) + q = np.linspace(1.0, 99.0, 16) + q[0] = np.nan + with pytest.raises(ValueError, match="Percentiles must be in"): + np.percentile([1, 2, 3, 4.0], q) + + +class TestQuantile: # most of this is already tested by TestPercentile + def test_max_ulp(self): + x = [0.0, 0.2, 0.4] + a = np.quantile(x, 0.45) + # The default linear method would result in 0 + 0.2 * (0.45/2) = 0.18. + # 0.18 is not exactly representable and the formula leads to a 1 ULP + # different result. Ensure it is this exact within 1 ULP, see gh-20331. + np.testing.assert_array_max_ulp(a, 0.18, maxulp=1) + def test_basic(self): x = np.arange(8) * 0.5 assert_equal(np.quantile(x, 0), 0.) assert_equal(np.quantile(x, 1), 3.5) assert_equal(np.quantile(x, 0.5), 1.75) + @pytest.mark.xfail(reason="See gh-19154") + def test_correct_quantile_value(self): + a = np.array([True]) + tf_quant = np.quantile(True, False) + assert_equal(tf_quant, a[0]) + assert_equal(type(tf_quant), a.dtype) + a = np.array([False, True, True]) + quant_res = np.quantile(a, a) + assert_array_equal(quant_res, a) + assert_equal(quant_res.dtype, a.dtype) + + def test_fraction(self): + # fractional input, integral quantile + x = [Fraction(i, 2) for i in range(8)] + q = np.quantile(x, 0) + assert_equal(q, 0) + assert_equal(type(q), Fraction) + + q = np.quantile(x, 1) + assert_equal(q, Fraction(7, 2)) + assert_equal(type(q), Fraction) + + q = np.quantile(x, Fraction(1, 2)) + assert_equal(q, Fraction(7, 4)) + assert_equal(type(q), Fraction) + + q = np.quantile(x, [Fraction(1, 2)]) + assert_equal(q, np.array([Fraction(7, 4)])) + assert_equal(type(q), np.ndarray) + + q = np.quantile(x, [[Fraction(1, 2)]]) + assert_equal(q, np.array([[Fraction(7, 4)]])) + assert_equal(type(q), np.ndarray) + + # repeat with integral input but fractional quantile + x = np.arange(8) + assert_equal(np.quantile(x, Fraction(1, 2)), Fraction(7, 2)) + def test_no_p_overwrite(self): # this is worth retesting, because quantile does not make a copy p0 = np.array([0, 0.75, 0.25, 0.5, 1.0]) p = p0.copy() - np.quantile(np.arange(100.), p, interpolation="midpoint") + np.quantile(np.arange(100.), p, method="midpoint") assert_array_equal(p, p0) p0 = p0.tolist() p = p.tolist() - np.quantile(np.arange(100.), p, interpolation="midpoint") + np.quantile(np.arange(100.), p, method="midpoint") assert_array_equal(p, p0) - -class TestMedian(object): + @pytest.mark.parametrize("dtype", np.typecodes["AllInteger"]) + def test_quantile_preserve_int_type(self, dtype): + res = np.quantile(np.array([1, 2], dtype=dtype), [0.5], + method="nearest") + assert res.dtype == dtype + + @pytest.mark.parametrize("method", + ['inverted_cdf', 'averaged_inverted_cdf', 'closest_observation', + 'interpolated_inverted_cdf', 'hazen', 'weibull', 'linear', + 'median_unbiased', 'normal_unbiased', + 'nearest', 'lower', 'higher', 'midpoint']) + def test_quantile_monotonic(self, method): + # GH 14685 + # test that the return value of quantile is monotonic if p0 is ordered + # Also tests that the boundary values are not mishandled. + p0 = np.linspace(0, 1, 101) + quantile = np.quantile(np.array([0, 1, 1, 2, 2, 3, 3, 4, 5, 5, 1, 1, 9, 9, 9, + 8, 8, 7]) * 0.1, p0, method=method) + assert_equal(np.sort(quantile), quantile) + + # Also test one where the number of data points is clearly divisible: + quantile = np.quantile([0., 1., 2., 3.], p0, method=method) + assert_equal(np.sort(quantile), quantile) + + @hypothesis.given( + arr=arrays(dtype=np.float64, + shape=st.integers(min_value=3, max_value=1000), + elements=st.floats(allow_infinity=False, allow_nan=False, + min_value=-1e300, max_value=1e300))) + def test_quantile_monotonic_hypo(self, arr): + p0 = np.arange(0, 1, 0.01) + quantile = np.quantile(arr, p0) + assert_equal(np.sort(quantile), quantile) + + def test_quantile_scalar_nan(self): + a = np.array([[10., 7., 4.], [3., 2., 1.]]) + a[0][1] = np.nan + actual = np.quantile(a, 0.5) + assert np.isscalar(actual) + assert_equal(np.quantile(a, 0.5), np.nan) + +class TestLerp: + @hypothesis.given(t0=st.floats(allow_nan=False, allow_infinity=False, + min_value=0, max_value=1), + t1=st.floats(allow_nan=False, allow_infinity=False, + min_value=0, max_value=1), + a = st.floats(allow_nan=False, allow_infinity=False, + min_value=-1e300, max_value=1e300), + b = st.floats(allow_nan=False, allow_infinity=False, + min_value=-1e300, max_value=1e300)) + def test_linear_interpolation_formula_monotonic(self, t0, t1, a, b): + l0 = nfb._lerp(a, b, t0) + l1 = nfb._lerp(a, b, t1) + if t0 == t1 or a == b: + assert l0 == l1 # uninteresting + elif (t0 < t1) == (a < b): + assert l0 <= l1 + else: + assert l0 >= l1 + + @hypothesis.given(t=st.floats(allow_nan=False, allow_infinity=False, + min_value=0, max_value=1), + a=st.floats(allow_nan=False, allow_infinity=False, + min_value=-1e300, max_value=1e300), + b=st.floats(allow_nan=False, allow_infinity=False, + min_value=-1e300, max_value=1e300)) + def test_linear_interpolation_formula_bounded(self, t, a, b): + if a <= b: + assert a <= nfb._lerp(a, b, t) <= b + else: + assert b <= nfb._lerp(a, b, t) <= a + + @hypothesis.given(t=st.floats(allow_nan=False, allow_infinity=False, + min_value=0, max_value=1), + a=st.floats(allow_nan=False, allow_infinity=False, + min_value=-1e300, max_value=1e300), + b=st.floats(allow_nan=False, allow_infinity=False, + min_value=-1e300, max_value=1e300)) + def test_linear_interpolation_formula_symmetric(self, t, a, b): + # double subtraction is needed to remove the extra precision of t < 0.5 + left = nfb._lerp(a, b, 1 - (1 - t)) + right = nfb._lerp(b, a, 1 - t) + assert left == right + + def test_linear_interpolation_formula_0d_inputs(self): + a = np.array(2) + b = np.array(5) + t = np.array(0.2) + assert nfb._lerp(a, b, t) == 2.6 + + +class TestMedian: def test_basic(self): a0 = np.array(1) @@ -2772,10 +3533,7 @@ def test_basic(self): # check array scalar result assert_equal(np.median(a).ndim, 0) a[1] = np.nan - with warnings.catch_warnings(record=True) as w: - warnings.filterwarnings('always', '', RuntimeWarning) - assert_equal(np.median(a).ndim, 0) - assert_(w[0].category is RuntimeWarning) + assert_equal(np.median(a).ndim, 0) def test_axis_keyword(self): a3 = np.array([[2, 3], @@ -2850,6 +3608,16 @@ def mean(self, axis=None, dtype=None, out=None): a = MySubClass([1, 2, 3]) assert_equal(np.median(a), -7) + @pytest.mark.parametrize('arr', + ([1., 2., 3.], [1., np.nan, 3.], np.nan, 0.)) + def test_subclass2(self, arr): + """Check that we return subclasses, even if a NaN scalar.""" + class MySubclass(np.ndarray): + pass + + m = np.median(np.array(arr).view(MySubclass)) + assert isinstance(m, MySubclass) + def test_out(self): o = np.zeros((4,)) d = np.ones((3, 4)) @@ -2874,58 +3642,43 @@ def test_out_nan(self): def test_nan_behavior(self): a = np.arange(24, dtype=float) a[2] = np.nan - with warnings.catch_warnings(record=True) as w: - warnings.filterwarnings('always', '', RuntimeWarning) - assert_equal(np.median(a), np.nan) - assert_equal(np.median(a, axis=0), np.nan) - assert_(w[0].category is RuntimeWarning) - assert_(w[1].category is RuntimeWarning) + assert_equal(np.median(a), np.nan) + assert_equal(np.median(a, axis=0), np.nan) a = np.arange(24, dtype=float).reshape(2, 3, 4) a[1, 2, 3] = np.nan a[1, 1, 2] = np.nan # no axis - with warnings.catch_warnings(record=True) as w: - warnings.filterwarnings('always', '', RuntimeWarning) - assert_equal(np.median(a), np.nan) - assert_equal(np.median(a).ndim, 0) - assert_(w[0].category is RuntimeWarning) + assert_equal(np.median(a), np.nan) + assert_equal(np.median(a).ndim, 0) # axis0 b = np.median(np.arange(24, dtype=float).reshape(2, 3, 4), 0) b[2, 3] = np.nan b[1, 2] = np.nan - with warnings.catch_warnings(record=True) as w: - warnings.filterwarnings('always', '', RuntimeWarning) - assert_equal(np.median(a, 0), b) - assert_equal(len(w), 1) + assert_equal(np.median(a, 0), b) # axis1 b = np.median(np.arange(24, dtype=float).reshape(2, 3, 4), 1) b[1, 3] = np.nan b[1, 2] = np.nan - with warnings.catch_warnings(record=True) as w: - warnings.filterwarnings('always', '', RuntimeWarning) - assert_equal(np.median(a, 1), b) - assert_equal(len(w), 1) + assert_equal(np.median(a, 1), b) # axis02 b = np.median(np.arange(24, dtype=float).reshape(2, 3, 4), (0, 2)) b[1] = np.nan b[2] = np.nan - with warnings.catch_warnings(record=True) as w: - warnings.filterwarnings('always', '', RuntimeWarning) - assert_equal(np.median(a, (0, 2)), b) - assert_equal(len(w), 1) + assert_equal(np.median(a, (0, 2)), b) def test_empty(self): - # empty arrays + # mean(empty array) emits two warnings: empty slice and divide by 0 a = np.array([], dtype=float) with warnings.catch_warnings(record=True) as w: warnings.filterwarnings('always', '', RuntimeWarning) assert_equal(np.median(a), np.nan) assert_(w[0].category is RuntimeWarning) + assert_equal(len(w), 2) # multiple dimensions a = np.array([], dtype=float, ndmin=3) @@ -3007,7 +3760,7 @@ def test_keepdims(self): (1, 1, 7, 1)) -class TestAdd_newdoc_ufunc(object): +class TestAdd_newdoc_ufunc: def test_ufunc_arg(self): assert_raises(TypeError, add_newdoc_ufunc, 2, "blah") @@ -3017,12 +3770,77 @@ def test_string_arg(self): assert_raises(TypeError, add_newdoc_ufunc, np.add, 3) -class TestAdd_newdoc(object): +class TestAdd_newdoc: @pytest.mark.skipif(sys.flags.optimize == 2, reason="Python running -OO") + @pytest.mark.xfail(IS_PYPY, reason="PyPy does not modify tp_doc") def test_add_doc(self): - # test np.add_newdoc + # test that np.add_newdoc did attach a docstring successfully: tgt = "Current flat index into the array." assert_equal(np.core.flatiter.index.__doc__[:len(tgt)], tgt) assert_(len(np.core.ufunc.identity.__doc__) > 300) assert_(len(np.lib.index_tricks.mgrid.__doc__) > 300) + + @pytest.mark.skipif(sys.flags.optimize == 2, reason="Python running -OO") + def test_errors_are_ignored(self): + prev_doc = np.core.flatiter.index.__doc__ + # nothing changed, but error ignored, this should probably + # give a warning (or even error) in the future. + np.add_newdoc("numpy.core", "flatiter", ("index", "bad docstring")) + assert prev_doc == np.core.flatiter.index.__doc__ + + +class TestAddDocstring(): + # Test should possibly be moved, but it also fits to be close to + # the newdoc tests... + @pytest.mark.skipif(sys.flags.optimize == 2, reason="Python running -OO") + @pytest.mark.skipif(IS_PYPY, reason="PyPy does not modify tp_doc") + def test_add_same_docstring(self): + # test for attributes (which are C-level defined) + np.add_docstring(np.ndarray.flat, np.ndarray.flat.__doc__) + # And typical functions: + def func(): + """docstring""" + return + + np.add_docstring(func, func.__doc__) + + @pytest.mark.skipif(sys.flags.optimize == 2, reason="Python running -OO") + def test_different_docstring_fails(self): + # test for attributes (which are C-level defined) + with assert_raises(RuntimeError): + np.add_docstring(np.ndarray.flat, "different docstring") + # And typical functions: + def func(): + """docstring""" + return + + with assert_raises(RuntimeError): + np.add_docstring(func, "different docstring") + + +class TestSortComplex: + + @pytest.mark.parametrize("type_in, type_out", [ + ('l', 'D'), + ('h', 'F'), + ('H', 'F'), + ('b', 'F'), + ('B', 'F'), + ('g', 'G'), + ]) + def test_sort_real(self, type_in, type_out): + # sort_complex() type casting for real input types + a = np.array([5, 3, 6, 2, 1], dtype=type_in) + actual = np.sort_complex(a) + expected = np.sort(a).astype(type_out) + assert_equal(actual, expected) + assert_equal(actual.dtype, expected.dtype) + + def test_sort_complex(self): + # sort_complex() handling of complex input + a = np.array([2 + 3j, 1 - 2j, 1 - 3j, 2 + 1j], dtype='D') + expected = np.array([1 - 3j, 1 - 2j, 2 + 1j, 2 + 3j], dtype='D') + actual = np.sort_complex(a) + assert_equal(actual, expected) + assert_equal(actual.dtype, expected.dtype) diff --git a/numpy/lib/tests/test_histograms.py b/numpy/lib/tests/test_histograms.py index 294dd539b7de..fc16b7396793 100644 --- a/numpy/lib/tests/test_histograms.py +++ b/numpy/lib/tests/test_histograms.py @@ -1,16 +1,15 @@ -from __future__ import division, absolute_import, print_function - import numpy as np from numpy.lib.histograms import histogram, histogramdd, histogram_bin_edges from numpy.testing import ( assert_, assert_equal, assert_array_equal, assert_almost_equal, assert_array_almost_equal, assert_raises, assert_allclose, - assert_array_max_ulp, assert_warns, assert_raises_regex, suppress_warnings, + assert_array_max_ulp, assert_raises_regex, suppress_warnings, ) +import pytest -class TestHistogram(object): +class TestHistogram: def setup(self): pass @@ -40,20 +39,28 @@ def test_one_bin(self): assert_allclose(e, np.array([1., 2.])) def test_normed(self): - # Check that the integral of the density equals 1. - n = 100 - v = np.random.rand(n) - a, b = histogram(v, normed=True) - area = np.sum(a * np.diff(b)) - assert_almost_equal(area, 1) + sup = suppress_warnings() + with sup: + rec = sup.record(np.VisibleDeprecationWarning, '.*normed.*') + # Check that the integral of the density equals 1. + n = 100 + v = np.random.rand(n) + a, b = histogram(v, normed=True) + area = np.sum(a * np.diff(b)) + assert_almost_equal(area, 1) + assert_equal(len(rec), 1) - # Check with non-constant bin widths (buggy but backwards - # compatible) - v = np.arange(10) - bins = [0, 1, 5, 9, 10] - a, b = histogram(v, bins, normed=True) - area = np.sum(a * np.diff(b)) - assert_almost_equal(area, 1) + sup = suppress_warnings() + with sup: + rec = sup.record(np.VisibleDeprecationWarning, '.*normed.*') + # Check with non-constant bin widths (buggy but backwards + # compatible) + v = np.arange(10) + bins = [0, 1, 5, 9, 10] + a, b = histogram(v, bins, normed=True) + area = np.sum(a * np.diff(b)) + assert_almost_equal(area, 1) + assert_equal(len(rec), 1) def test_density(self): # Check that the integral of the density equals 1. @@ -70,7 +77,11 @@ def test_density(self): assert_array_equal(a, .1) assert_equal(np.sum(a * np.diff(b)), 1) - # Variale bin widths are especially useful to deal with + # Test that passing False works too + a, b = histogram(v, bins, density=False) + assert_array_equal(a, [1, 2, 3, 4]) + + # Variable bin widths are especially useful to deal with # infinities. v = np.arange(10) bins = [0, 1, 3, 6, np.inf] @@ -96,24 +107,31 @@ def test_outliers(self): assert_equal(h.sum(), 9) # Normalization - h, b = histogram(a, range=[1, 9], normed=True) + h, b = histogram(a, range=[1, 9], density=True) assert_almost_equal((h * np.diff(b)).sum(), 1, decimal=15) # Weights w = np.arange(10) + .5 - h, b = histogram(a, range=[1, 9], weights=w, normed=True) + h, b = histogram(a, range=[1, 9], weights=w, density=True) assert_equal((h * np.diff(b)).sum(), 1) h, b = histogram(a, bins=8, range=[1, 9], weights=w) assert_equal(h, w[1:-1]) + def test_arr_weights_mismatch(self): + a = np.arange(10) + .5 + w = np.arange(11) + .5 + with assert_raises_regex(ValueError, "same shape as"): + h, b = histogram(a, range=[1, 9], weights=w, density=True) + + def test_type(self): # Check the type of the returned histogram a = np.arange(10) + .5 h, b = histogram(a) assert_(np.issubdtype(h.dtype, np.integer)) - h, b = histogram(a, normed=True) + h, b = histogram(a, density=True) assert_(np.issubdtype(h.dtype, np.floating)) h, b = histogram(a, weights=np.ones(10, int)) @@ -129,13 +147,30 @@ def test_f32_rounding(self): counts_hist, xedges, yedges = np.histogram2d(x, y, bins=100) assert_equal(counts_hist.sum(), 3.) + def test_bool_conversion(self): + # gh-12107 + # Reference integer histogram + a = np.array([1, 1, 0], dtype=np.uint8) + int_hist, int_edges = np.histogram(a) + + # Should raise an warning on booleans + # Ensure that the histograms are equivalent, need to suppress + # the warnings to get the actual outputs + with suppress_warnings() as sup: + rec = sup.record(RuntimeWarning, 'Converting input from .*') + hist, edges = np.histogram([True, True, False]) + # A warning should be issued + assert_equal(len(rec), 1) + assert_array_equal(hist, int_hist) + assert_array_equal(edges, int_edges) + def test_weights(self): v = np.random.rand(100) w = np.ones(100) * 5 a, b = histogram(v) - na, nb = histogram(v, normed=True) + na, nb = histogram(v, density=True) wa, wb = histogram(v, weights=w) - nwa, nwb = histogram(v, weights=w, normed=True) + nwa, nwb = histogram(v, weights=w, density=True) assert_array_almost_equal(a * 5, wa) assert_array_almost_equal(na, nwa) @@ -149,7 +184,7 @@ def test_weights(self): wa, wb = histogram([1, 2, 2, 4], bins=4, weights=[4, 3, 2, 1]) assert_array_equal(wa, [4, 5, 0, 1]) wa, wb = histogram( - [1, 2, 2, 4], bins=4, weights=[4, 3, 2, 1], normed=True) + [1, 2, 2, 4], bins=4, weights=[4, 3, 2, 1], density=True) assert_array_almost_equal(wa, np.array([4, 5, 0, 1]) / 10. / 3. * 4) # Check weights with non-uniform bin widths @@ -213,6 +248,12 @@ def test_finite_range(self): assert_raises(ValueError, histogram, vals, range=[np.nan,0.75]) assert_raises(ValueError, histogram, vals, range=[0.25,np.inf]) + def test_invalid_range(self): + # start of range must be < end of range + vals = np.linspace(0.0, 1.0, num=100) + with assert_raises_regex(ValueError, "max must be larger than"): + np.histogram(vals, range=[0.1, 0.01]) + def test_bin_edge_cases(self): # Ensure that floating-point computations correctly place edge cases. arr = np.array([337, 404, 739, 806, 1007, 1811, 2012]) @@ -229,6 +270,13 @@ def test_last_bin_inclusive_range(self): hist, edges = np.histogram(arr, bins=30, range=(-0.5, 5)) assert_equal(hist[-1], 1) + def test_bin_array_dims(self): + # gracefully handle bins object > 1 dimension + vals = np.linspace(0.0, 1.0, num=100) + bins = np.array([[0, 0.5], [0.6, 1.0]]) + with assert_raises_regex(ValueError, "must be 1d"): + np.histogram(vals, bins=bins) + def test_unsigned_monotonicity_check(self): # Ensures ValueError is raised if bins not increasing monotonically # when bins contain unsigned values (see #9222) @@ -240,13 +288,13 @@ def test_unsigned_monotonicity_check(self): def test_object_array_of_0d(self): # gh-7864 assert_raises(ValueError, - histogram, [np.array([0.4]) for i in range(10)] + [-np.inf]) + histogram, [np.array(0.4) for i in range(10)] + [-np.inf]) assert_raises(ValueError, - histogram, [np.array([0.4]) for i in range(10)] + [np.inf]) + histogram, [np.array(0.4) for i in range(10)] + [np.inf]) # these should not crash - np.histogram([np.array([0.5]) for i in range(10)] + [.500000000000001]) - np.histogram([np.array([0.5]) for i in range(10)] + [.5]) + np.histogram([np.array(0.5) for i in range(10)] + [.500000000000001]) + np.histogram([np.array(0.5) for i in range(10)] + [.5]) def test_some_nan_values(self): # gh-7503 @@ -374,7 +422,7 @@ def test_histogram_bin_edges(self): assert_array_equal(edges, e) -class TestHistogramOptimBinNums(object): +class TestHistogramOptimBinNums: """ Provide test coverage when using provided estimators for optimal number of bins @@ -382,7 +430,7 @@ class TestHistogramOptimBinNums(object): def test_empty(self): estimator_list = ['fd', 'scott', 'rice', 'sturges', - 'doane', 'sqrt', 'auto'] + 'doane', 'sqrt', 'auto', 'stone'] # check it can deal with empty data for estimator in estimator_list: a, b = histogram([], bins=estimator) @@ -398,11 +446,11 @@ def test_simple(self): # Some basic sanity checking, with some fixed data. # Checking for the correct number of bins basic_test = {50: {'fd': 4, 'scott': 4, 'rice': 8, 'sturges': 7, - 'doane': 8, 'sqrt': 8, 'auto': 7}, + 'doane': 8, 'sqrt': 8, 'auto': 7, 'stone': 2}, 500: {'fd': 8, 'scott': 8, 'rice': 16, 'sturges': 10, - 'doane': 12, 'sqrt': 23, 'auto': 10}, + 'doane': 12, 'sqrt': 23, 'auto': 10, 'stone': 9}, 5000: {'fd': 17, 'scott': 17, 'rice': 35, 'sturges': 14, - 'doane': 17, 'sqrt': 71, 'auto': 17}} + 'doane': 17, 'sqrt': 71, 'auto': 17, 'stone': 20}} for testlen, expectedResults in basic_test.items(): # Create some sort of non uniform data to test with @@ -422,11 +470,11 @@ def test_small(self): precalculated. """ small_dat = {1: {'fd': 1, 'scott': 1, 'rice': 1, 'sturges': 1, - 'doane': 1, 'sqrt': 1}, + 'doane': 1, 'sqrt': 1, 'stone': 1}, 2: {'fd': 2, 'scott': 1, 'rice': 3, 'sturges': 2, - 'doane': 1, 'sqrt': 2}, + 'doane': 1, 'sqrt': 2, 'stone': 1}, 3: {'fd': 2, 'scott': 2, 'rice': 3, 'sturges': 3, - 'doane': 3, 'sqrt': 2}} + 'doane': 3, 'sqrt': 2, 'stone': 1}} for testlen, expectedResults in small_dat.items(): testdat = np.arange(testlen) @@ -450,7 +498,7 @@ def test_novariance(self): """ novar_dataset = np.ones(100) novar_resultdict = {'fd': 1, 'scott': 1, 'rice': 1, 'sturges': 1, - 'doane': 1, 'sqrt': 1, 'auto': 1} + 'doane': 1, 'sqrt': 1, 'auto': 1, 'stone': 1} for estimator, numbins in novar_resultdict.items(): a, b = np.histogram(novar_dataset, estimator) @@ -489,12 +537,28 @@ def test_outlier(self): xcenter = np.linspace(-10, 10, 50) outlier_dataset = np.hstack((np.linspace(-110, -100, 5), xcenter)) - outlier_resultdict = {'fd': 21, 'scott': 5, 'doane': 11} + outlier_resultdict = {'fd': 21, 'scott': 5, 'doane': 11, 'stone': 6} for estimator, numbins in outlier_resultdict.items(): a, b = np.histogram(outlier_dataset, estimator) assert_equal(len(a), numbins) + def test_scott_vs_stone(self): + """Verify that Scott's rule and Stone's rule converges for normally distributed data""" + + def nbins_ratio(seed, size): + rng = np.random.RandomState(seed) + x = rng.normal(loc=0, scale=2, size=size) + a, b = len(np.histogram(x, 'stone')[0]), len(np.histogram(x, 'scott')[0]) + return a / (a + b) + + ll = [[nbins_ratio(seed, size) for size in np.geomspace(start=10, stop=100, num=4).round().astype(int)] + for seed in range(10)] + + # the average difference between the two methods decreases as the dataset size increases. + avg = abs(np.mean(ll, axis=0) - 0.5) + assert_almost_equal(avg, [0.15, 0.09, 0.08, 0.03], decimal=2) + def test_simple_range(self): """ Straightforward testing with a mixture of linspace data (for @@ -506,11 +570,11 @@ def test_simple_range(self): # Checking for the correct number of bins basic_test = { 50: {'fd': 8, 'scott': 8, 'rice': 15, - 'sturges': 14, 'auto': 14}, + 'sturges': 14, 'auto': 14, 'stone': 8}, 500: {'fd': 15, 'scott': 16, 'rice': 32, - 'sturges': 20, 'auto': 20}, + 'sturges': 20, 'auto': 20, 'stone': 80}, 5000: {'fd': 33, 'scott': 33, 'rice': 69, - 'sturges': 27, 'auto': 33} + 'sturges': 27, 'auto': 33, 'stone': 80} } for testlen, expectedResults in basic_test.items(): @@ -526,6 +590,16 @@ def test_simple_range(self): msg += " with datasize of {0}".format(testlen) assert_equal(len(a), numbins, err_msg=msg) + @pytest.mark.parametrize("bins", ['auto', 'fd', 'doane', 'scott', + 'stone', 'rice', 'sturges']) + def test_signed_integer_data(self, bins): + # Regression test for gh-14379. + a = np.array([-2, 0, 127], dtype=np.int8) + hist, edges = np.histogram(a, bins=bins) + hist32, edges32 = np.histogram(a.astype(np.int32), bins=bins) + assert_array_equal(hist, hist32) + assert_array_equal(edges, edges32) + def test_simple_weighted(self): """ Check that weighted data raises a TypeError @@ -536,7 +610,7 @@ def test_simple_weighted(self): estimator, weights=[1, 2, 3]) -class TestHistogramdd(object): +class TestHistogramdd: def test_simple(self): x = np.array([[-.5, .5, 1.5], [-.5, 1.5, 2.5], [-.5, 2.5, .5], @@ -549,13 +623,13 @@ def test_simple(self): # Check normalization ed = [[-2, 0, 2], [0, 1, 2, 3], [0, 1, 2, 3]] - H, edges = histogramdd(x, bins=ed, normed=True) + H, edges = histogramdd(x, bins=ed, density=True) assert_(np.all(H == answer / 12.)) # Check that H has the correct shape. H, edges = histogramdd(x, (2, 3, 4), range=[[-1, 1], [0, 3], [0, 4]], - normed=True) + density=True) answer = np.array([[[0, 1, 0, 0], [0, 0, 1, 0], [1, 0, 0, 0]], [[0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 1, 0]]]) assert_array_almost_equal(H, answer / 6., 4) @@ -601,10 +675,10 @@ def test_shape_4d(self): def test_weights(self): v = np.random.rand(100, 2) hist, edges = histogramdd(v) - n_hist, edges = histogramdd(v, normed=True) + n_hist, edges = histogramdd(v, density=True) w_hist, edges = histogramdd(v, weights=np.ones(100)) assert_array_equal(w_hist, hist) - w_hist, edges = histogramdd(v, weights=np.ones(100) * 2, normed=True) + w_hist, edges = histogramdd(v, weights=np.ones(100) * 2, density=True) assert_array_equal(w_hist, n_hist) w_hist, edges = histogramdd(v, weights=np.ones(100, int) * 2) assert_array_equal(w_hist, 2 * hist) @@ -709,3 +783,56 @@ def test_large_integers(self): hist, edges = histogramdd((x, y), bins=(x_edges, y_edges)) assert_equal(hist[0, 0], 1) + + def test_density_non_uniform_2d(self): + # Defines the following grid: + # + # 0 2 8 + # 0+-+-----+ + # + | + + # + | + + # 6+-+-----+ + # 8+-+-----+ + x_edges = np.array([0, 2, 8]) + y_edges = np.array([0, 6, 8]) + relative_areas = np.array([ + [3, 9], + [1, 3]]) + + # ensure the number of points in each region is proportional to its area + x = np.array([1] + [1]*3 + [7]*3 + [7]*9) + y = np.array([7] + [1]*3 + [7]*3 + [1]*9) + + # sanity check that the above worked as intended + hist, edges = histogramdd((y, x), bins=(y_edges, x_edges)) + assert_equal(hist, relative_areas) + + # resulting histogram should be uniform, since counts and areas are proportional + hist, edges = histogramdd((y, x), bins=(y_edges, x_edges), density=True) + assert_equal(hist, 1 / (8*8)) + + def test_density_non_uniform_1d(self): + # compare to histogram to show the results are the same + v = np.arange(10) + bins = np.array([0, 1, 3, 6, 10]) + hist, edges = histogram(v, bins, density=True) + hist_dd, edges_dd = histogramdd((v,), (bins,), density=True) + assert_equal(hist, hist_dd) + assert_equal(edges, edges_dd[0]) + + def test_density_via_normed(self): + # normed should simply alias to density argument + v = np.arange(10) + bins = np.array([0, 1, 3, 6, 10]) + hist, edges = histogram(v, bins, density=True) + hist_dd, edges_dd = histogramdd((v,), (bins,), normed=True) + assert_equal(hist, hist_dd) + assert_equal(edges, edges_dd[0]) + + def test_density_normed_redundancy(self): + v = np.arange(10) + bins = np.array([0, 1, 3, 6, 10]) + with assert_raises_regex(TypeError, "Cannot specify both"): + hist_dd, edges_dd = histogramdd((v,), (bins,), + density=True, + normed=True) diff --git a/numpy/lib/tests/test_index_tricks.py b/numpy/lib/tests/test_index_tricks.py index 315251daac6f..26a34be7e729 100644 --- a/numpy/lib/tests/test_index_tricks.py +++ b/numpy/lib/tests/test_index_tricks.py @@ -1,9 +1,9 @@ -from __future__ import division, absolute_import, print_function +import pytest import numpy as np from numpy.testing import ( assert_, assert_equal, assert_array_equal, assert_almost_equal, - assert_array_almost_equal, assert_raises, assert_raises_regex + assert_array_almost_equal, assert_raises, assert_raises_regex, ) from numpy.lib.index_tricks import ( mgrid, ogrid, ndenumerate, fill_diagonal, diag_indices, diag_indices_from, @@ -11,9 +11,29 @@ ) -class TestRavelUnravelIndex(object): +class TestRavelUnravelIndex: def test_basic(self): assert_equal(np.unravel_index(2, (2, 2)), (1, 0)) + + # test that new shape argument works properly + assert_equal(np.unravel_index(indices=2, + shape=(2, 2)), + (1, 0)) + + # test that an invalid second keyword argument + # is properly handled, including the old name `dims`. + with assert_raises(TypeError): + np.unravel_index(indices=2, hape=(2, 2)) + + with assert_raises(TypeError): + np.unravel_index(2, hape=(2, 2)) + + with assert_raises(TypeError): + np.unravel_index(254, ims=(17, 94)) + + with assert_raises(TypeError): + np.unravel_index(254, dims=(17, 94)) + assert_equal(np.ravel_multi_index((1, 0), (2, 2)), 2) assert_equal(np.unravel_index(254, (17, 94)), (2, 66)) assert_equal(np.ravel_multi_index((2, 66), (17, 94)), 254) @@ -47,6 +67,26 @@ def test_basic(self): [[3, 6, 6], [4, 5, 1]]) assert_equal(np.unravel_index(1621, (6, 7, 8, 9)), [3, 1, 4, 1]) + def test_empty_indices(self): + msg1 = 'indices must be integral: the provided empty sequence was' + msg2 = 'only int indices permitted' + assert_raises_regex(TypeError, msg1, np.unravel_index, [], (10, 3, 5)) + assert_raises_regex(TypeError, msg1, np.unravel_index, (), (10, 3, 5)) + assert_raises_regex(TypeError, msg2, np.unravel_index, np.array([]), + (10, 3, 5)) + assert_equal(np.unravel_index(np.array([],dtype=int), (10, 3, 5)), + [[], [], []]) + assert_raises_regex(TypeError, msg1, np.ravel_multi_index, ([], []), + (10, 3)) + assert_raises_regex(TypeError, msg1, np.ravel_multi_index, ([], ['abc']), + (10, 3)) + assert_raises_regex(TypeError, msg2, np.ravel_multi_index, + (np.array([]), np.array([])), (5, 3)) + assert_equal(np.ravel_multi_index( + (np.array([], dtype=int), np.array([], dtype=int)), (5, 3)), []) + assert_equal(np.ravel_multi_index(np.array([[], []], dtype=int), + (5, 3)), []) + def test_big_indices(self): # ravel_multi_index for big indices (issue #7546) if np.intp == np.int64: @@ -56,6 +96,9 @@ def test_big_indices(self): np.ravel_multi_index(arr, (41, 7, 120, 36, 2706, 8, 6)), [5627771580, 117259570957]) + # test unravel_index for big indices (issue #9538) + assert_raises(ValueError, np.unravel_index, 1, (2**32-1, 2**31+1)) + # test overflow checking for too big array (issue #7546) dummy_arr = ([0],[0]) half_max = np.iinfo(np.intp).max // 2 @@ -113,7 +156,6 @@ def test_writeability(self): assert_(x.flags.writeable) assert_(y.flags.writeable) - def test_0d(self): # gh-580 x = np.unravel_index(0, ()) @@ -123,8 +165,26 @@ def test_0d(self): assert_raises_regex( ValueError, "out of bounds", np.unravel_index, [1], ()) + @pytest.mark.parametrize("mode", ["clip", "wrap", "raise"]) + def test_empty_array_ravel(self, mode): + res = np.ravel_multi_index( + np.zeros((3, 0), dtype=np.intp), (2, 1, 0), mode=mode) + assert(res.shape == (0,)) + + with assert_raises(ValueError): + np.ravel_multi_index( + np.zeros((3, 1), dtype=np.intp), (2, 1, 0), mode=mode) -class TestGrid(object): + def test_empty_array_unravel(self): + res = np.unravel_index(np.zeros(0, dtype=np.intp), (2, 1, 0)) + # res is a tuple of three empty arrays + assert(len(res) == 3) + assert(all(a.shape == (0,) for a in res)) + + with assert_raises(ValueError): + np.unravel_index([1], (2, 1, 0)) + +class TestGrid: def test_basic(self): a = mgrid[-1:1:10j] b = mgrid[-1:1:0.1] @@ -138,7 +198,7 @@ def test_basic(self): assert_almost_equal(a[1]-a[0], 2.0/9.0, 11) def test_linspace_equivalence(self): - y, st = np.linspace(2, 10, retstep=1) + y, st = np.linspace(2, 10, retstep=True) assert_almost_equal(st, 8/49.0) assert_array_almost_equal(y, mgrid[2:10:50j], 13) @@ -165,8 +225,47 @@ def test_sparse(self): for f, b in zip(grid_full, grid_broadcast): assert_equal(f, b) + @pytest.mark.parametrize("start, stop, step, expected", [ + (None, 10, 10j, (200, 10)), + (-10, 20, None, (1800, 30)), + ]) + def test_mgrid_size_none_handling(self, start, stop, step, expected): + # regression test None value handling for + # start and step values used by mgrid; + # internally, this aims to cover previously + # unexplored code paths in nd_grid() + grid = mgrid[start:stop:step, start:stop:step] + # need a smaller grid to explore one of the + # untested code paths + grid_small = mgrid[start:stop:step] + assert_equal(grid.size, expected[0]) + assert_equal(grid_small.size, expected[1]) + + def test_accepts_npfloating(self): + # regression test for #16466 + grid64 = mgrid[0.1:0.33:0.1, ] + grid32 = mgrid[np.float32(0.1):np.float32(0.33):np.float32(0.1), ] + assert_(grid32.dtype == np.float64) + assert_array_almost_equal(grid64, grid32) + + # different code path for single slice + grid64 = mgrid[0.1:0.33:0.1] + grid32 = mgrid[np.float32(0.1):np.float32(0.33):np.float32(0.1)] + assert_(grid32.dtype == np.float64) + assert_array_almost_equal(grid64, grid32) + + def test_accepts_npcomplexfloating(self): + # Related to #16466 + assert_array_almost_equal( + mgrid[0.1:0.3:3j, ], mgrid[0.1:0.3:np.complex64(3j), ] + ) + + # different code path for single slice + assert_array_almost_equal( + mgrid[0.1:0.3:3j], mgrid[0.1:0.3:np.complex64(3j)] + ) -class TestConcatenator(object): +class TestConcatenator: def test_1d(self): assert_array_equal(r_[1, 2, 3, 4, 5, 6], np.array([1, 2, 3, 4, 5, 6])) b = np.ones(5) @@ -181,6 +280,15 @@ def test_more_mixed_type(self): g = r_[-10.1, np.array([1]), np.array([2, 3, 4]), 10.0] assert_(g.dtype == 'f8') + def test_complex_step(self): + # Regression test for #12262 + g = r_[0:36:100j] + assert_(g.shape == (100,)) + + # Related to #16466 + g = r_[0:36:np.complex64(100j)] + assert_(g.shape == (100,)) + def test_2d(self): b = np.random.rand(5, 5) c = np.random.rand(5, 5) @@ -199,14 +307,14 @@ def test_0d(self): assert_equal(r_[np.array(0), [1, 2, 3]], [0, 1, 2, 3]) -class TestNdenumerate(object): +class TestNdenumerate: def test_basic(self): a = np.array([[1, 2], [3, 4]]) assert_equal(list(ndenumerate(a)), [((0, 0), 1), ((0, 1), 2), ((1, 0), 3), ((1, 1), 4)]) -class TestIndexExpression(object): +class TestIndexExpression: def test_regression_1(self): # ticket #1196 a = np.arange(2) @@ -220,13 +328,18 @@ def test_simple_1(self): assert_equal(a[:, :3, [1, 2]], a[s_[:, :3, [1, 2]]]) -class TestIx_(object): +class TestIx_: def test_regression_1(self): - # Test empty inputs create outputs of indexing type, gh-5804 - # Test both lists and arrays - for func in (range, np.arange): - a, = np.ix_(func(0)) - assert_equal(a.dtype, np.intp) + # Test empty untyped inputs create outputs of indexing type, gh-5804 + a, = np.ix_(range(0)) + assert_equal(a.dtype, np.intp) + + a, = np.ix_([]) + assert_equal(a.dtype, np.intp) + + # but if the type is specified, don't change it + a, = np.ix_(np.array([], dtype=np.float32)) + assert_equal(a.dtype, np.float32) def test_shape_and_dtype(self): sizes = (4, 5, 3, 2) @@ -262,7 +375,7 @@ def test_c_(): assert_equal(a, [[1, 2, 3, 0, 0, 4, 5, 6]]) -class TestFillDiagonal(object): +class TestFillDiagonal: def test_basic(self): a = np.zeros((3, 3), int) fill_diagonal(a, 5) @@ -319,6 +432,19 @@ def test_operate_4d_array(self): i = np.array([0, 1, 2]) assert_equal(np.where(a != 0), (i, i, i, i)) + def test_low_dim_handling(self): + # raise error with low dimensionality + a = np.zeros(3, int) + with assert_raises_regex(ValueError, "at least 2-d"): + fill_diagonal(a, 5) + + def test_hetero_shape_handling(self): + # raise error with high dimensionality and + # shape mismatch + a = np.zeros((3,3,7,3), int) + with assert_raises_regex(ValueError, "equal length"): + fill_diagonal(a, 2) + def test_diag_indices(): di = diag_indices(4) @@ -348,11 +474,23 @@ def test_diag_indices(): ) -def test_diag_indices_from(): - x = np.random.random((4, 4)) - r, c = diag_indices_from(x) - assert_array_equal(r, np.arange(4)) - assert_array_equal(c, np.arange(4)) +class TestDiagIndicesFrom: + + def test_diag_indices_from(self): + x = np.random.random((4, 4)) + r, c = diag_indices_from(x) + assert_array_equal(r, np.arange(4)) + assert_array_equal(c, np.arange(4)) + + def test_error_small_input(self): + x = np.ones(7) + with assert_raises_regex(ValueError, "at least 2-d"): + diag_indices_from(x) + + def test_error_shape_mismatch(self): + x = np.zeros((3, 3, 2, 3), int) + with assert_raises_regex(ValueError, "equal length"): + diag_indices_from(x) def test_ndindex(): diff --git a/numpy/lib/tests/test_io.py b/numpy/lib/tests/test_io.py index f58c9e33dbc2..5201b8e6e33f 100644 --- a/numpy/lib/tests/test_io.py +++ b/numpy/lib/tests/test_io.py @@ -1,30 +1,33 @@ -from __future__ import division, absolute_import, print_function - import sys +import gc import gzip import os import threading import time import warnings -import gc import io import re import pytest +from pathlib import Path from tempfile import NamedTemporaryFile from io import BytesIO, StringIO from datetime import datetime import locale +from multiprocessing import Process, Value +from ctypes import c_bool import numpy as np import numpy.ma as ma from numpy.lib._iotools import ConverterError, ConversionWarning -from numpy.compat import asbytes, bytes, unicode, Path +from numpy.compat import asbytes from numpy.ma.testutils import assert_equal from numpy.testing import ( - assert_warns, assert_, SkipTest, assert_raises_regex, assert_raises, + assert_warns, assert_, assert_raises_regex, assert_raises, assert_allclose, assert_array_equal, temppath, tempdir, IS_PYPY, - HAS_REFCOUNT, suppress_warnings, assert_no_gc_cycles, + HAS_REFCOUNT, suppress_warnings, assert_no_gc_cycles, assert_no_warnings, + break_cycles ) +from numpy.testing._private.utils import requires_memory class TextIO(BytesIO): @@ -46,7 +49,6 @@ def writelines(self, lines): BytesIO.writelines(self, [asbytes(s) for s in lines]) -MAJVER, MINVER = sys.version_info[:2] IS_64BIT = sys.maxsize > 2**32 try: import bz2 @@ -71,7 +73,7 @@ def strptime(s, fmt=None): return datetime(*time.strptime(s, fmt)[:3]) -class RoundtripTest(object): +class RoundtripTest: def roundtrip(self, save_func, *args, **kwargs): """ save_func : callable @@ -88,7 +90,7 @@ def roundtrip(self, save_func, *args, **kwargs): """ save_kwds = kwargs.get('save_kwds', {}) - load_kwds = kwargs.get('load_kwds', {}) + load_kwds = kwargs.get('load_kwds', {"allow_pickle": True}) file_on_disk = kwargs.get('file_on_disk', False) if file_on_disk: @@ -278,8 +280,7 @@ def test_not_closing_opened_fid(self): fp.seek(0) assert_(not fp.closed) - #FIXME: Is this still true? - @pytest.mark.skipif(IS_PYPY, reason="Missing context manager on PyPy") + @pytest.mark.slow_pypy def test_closing_fid(self): # Test that issue #1517 (too many opened files) remains closed # It might be a "weak" test since failed to get triggered on @@ -292,17 +293,18 @@ def test_closing_fid(self): # numpy npz file returned by np.load when their reference count # goes to zero. Python 3 running in debug mode raises a # ResourceWarning when file closing is left to the garbage - # collector, so we catch the warnings. Because ResourceWarning - # is unknown in Python < 3.x, we take the easy way out and - # catch all warnings. + # collector, so we catch the warnings. with suppress_warnings() as sup: - sup.filter(Warning) # TODO: specify exact message + sup.filter(ResourceWarning) # TODO: specify exact message for i in range(1, 1025): try: np.load(tmp)["data"] except Exception as e: msg = "Failed to load data from a file: %s" % e raise AssertionError(msg) + finally: + if IS_PYPY: + gc.collect() def test_closing_zipfile_after_load(self): # Check that zipfile owns file and can close it. This needs to @@ -318,7 +320,7 @@ def test_closing_zipfile_after_load(self): assert_(fp.closed) -class TestSaveTxt(object): +class TestSaveTxt: def test_array(self): a = np.array([[1, 2], [3, 4]], float) fmt = "%.18e" @@ -348,14 +350,32 @@ def test_0D_3D(self): assert_raises(ValueError, np.savetxt, c, np.array(1)) assert_raises(ValueError, np.savetxt, c, np.array([[[1], [2]]])) - - def test_record(self): + def test_structured(self): a = np.array([(1, 2), (3, 4)], dtype=[('x', 'i4'), ('y', 'i4')]) c = BytesIO() np.savetxt(c, a, fmt='%d') c.seek(0) assert_equal(c.readlines(), [b'1 2\n', b'3 4\n']) + def test_structured_padded(self): + # gh-13297 + a = np.array([(1, 2, 3),(4, 5, 6)], dtype=[ + ('foo', 'i4'), ('bar', 'i4'), ('baz', 'i4') + ]) + c = BytesIO() + np.savetxt(c, a[['foo', 'baz']], fmt='%d') + c.seek(0) + assert_equal(c.readlines(), [b'1 3\n', b'4 6\n']) + + def test_multifield_view(self): + a = np.ones(1, dtype=[('x', 'i4'), ('y', 'i4'), ('z', 'f4')]) + v = a[['x', 'z']] + with temppath(suffix='.npy') as path: + path = Path(path) + np.save(path, v) + data = np.load(path) + assert_array_equal(data, v) + def test_delimiter(self): a = np.array([[1., 2.], [3., 4.]]) c = BytesIO() @@ -486,8 +506,6 @@ def test_complex_negative_exponent(self): b' (3.142e+00-2.718e+00j) (3.142e+00-2.718e+00j)\n']) - - def test_custom_writer(self): class CustomWriter(list): @@ -502,7 +520,7 @@ def write(self, text): def test_unicode(self): utf8 = b'\xcf\x96'.decode('UTF-8') - a = np.array([utf8], dtype=np.unicode) + a = np.array([utf8], dtype=np.unicode_) with tempdir() as tmpdir: # set encoding as on windows it may not be unicode even on py3 np.savetxt(os.path.join(tmpdir, 'test.csv'), a, fmt=['%s'], @@ -510,26 +528,24 @@ def test_unicode(self): def test_unicode_roundtrip(self): utf8 = b'\xcf\x96'.decode('UTF-8') - a = np.array([utf8], dtype=np.unicode) + a = np.array([utf8], dtype=np.unicode_) # our gz wrapper support encoding suffixes = ['', '.gz'] - # stdlib 2 versions do not support encoding - if MAJVER > 2: - if HAS_BZ2: - suffixes.append('.bz2') - if HAS_LZMA: - suffixes.extend(['.xz', '.lzma']) + if HAS_BZ2: + suffixes.append('.bz2') + if HAS_LZMA: + suffixes.extend(['.xz', '.lzma']) with tempdir() as tmpdir: for suffix in suffixes: np.savetxt(os.path.join(tmpdir, 'test.csv' + suffix), a, fmt=['%s'], encoding='UTF-16-LE') b = np.loadtxt(os.path.join(tmpdir, 'test.csv' + suffix), - encoding='UTF-16-LE', dtype=np.unicode) + encoding='UTF-16-LE', dtype=np.unicode_) assert_array_equal(a, b) def test_unicode_bytestream(self): utf8 = b'\xcf\x96'.decode('UTF-8') - a = np.array([utf8], dtype=np.unicode) + a = np.array([utf8], dtype=np.unicode_) s = BytesIO() np.savetxt(s, a, fmt=['%s'], encoding='UTF-8') s.seek(0) @@ -537,14 +553,58 @@ def test_unicode_bytestream(self): def test_unicode_stringstream(self): utf8 = b'\xcf\x96'.decode('UTF-8') - a = np.array([utf8], dtype=np.unicode) + a = np.array([utf8], dtype=np.unicode_) s = StringIO() np.savetxt(s, a, fmt=['%s'], encoding='UTF-8') s.seek(0) assert_equal(s.read(), utf8 + '\n') + @pytest.mark.parametrize("fmt", [u"%f", b"%f"]) + @pytest.mark.parametrize("iotype", [StringIO, BytesIO]) + def test_unicode_and_bytes_fmt(self, fmt, iotype): + # string type of fmt should not matter, see also gh-4053 + a = np.array([1.]) + s = iotype() + np.savetxt(s, a, fmt=fmt) + s.seek(0) + if iotype is StringIO: + assert_equal(s.read(), u"%f\n" % 1.) + else: + assert_equal(s.read(), b"%f\n" % 1.) -class LoadTxtBase(object): + @pytest.mark.skipif(sys.platform=='win32', reason="files>4GB may not work") + @pytest.mark.slow + @requires_memory(free_bytes=7e9) + def test_large_zip(self): + def check_large_zip(memoryerror_raised): + memoryerror_raised.value = False + try: + # The test takes at least 6GB of memory, writes a file larger + # than 4GB. This tests the ``allowZip64`` kwarg to ``zipfile`` + test_data = np.asarray([np.random.rand( + np.random.randint(50,100),4) + for i in range(800000)], dtype=object) + with tempdir() as tmpdir: + np.savez(os.path.join(tmpdir, 'test.npz'), + test_data=test_data) + except MemoryError: + memoryerror_raised.value = True + raise + # run in a subprocess to ensure memory is released on PyPy, see gh-15775 + # Use an object in shared memory to re-raise the MemoryError exception + # in our process if needed, see gh-16889 + memoryerror_raised = Value(c_bool) + p = Process(target=check_large_zip, args=(memoryerror_raised,)) + p.start() + p.join() + if memoryerror_raised.value: + raise MemoryError("Child process raised a MemoryError exception") + # -9 indicates a SIGKILL, probably an OOM. + if p.exitcode == -9: + pytest.xfail("subprocess got a SIGKILL, apparently free memory was not sufficient") + assert p.exitcode == 0 + +class LoadTxtBase: def check_compressed(self, fopen, suffixes): # Test that we can load data from a compressed file wanted = np.arange(6).reshape((2, 3)) @@ -561,19 +621,15 @@ def check_compressed(self, fopen, suffixes): res = self.loadfunc(f) assert_array_equal(res, wanted) - # Python2 .open does not support encoding - @pytest.mark.skipif(MAJVER == 2, reason="Needs Python version >= 3") def test_compressed_gzip(self): self.check_compressed(gzip.open, ('.gz',)) @pytest.mark.skipif(not HAS_BZ2, reason="Needs bz2") - @pytest.mark.skipif(MAJVER == 2, reason="Needs Python version >= 3") - def test_compressed_gzip(self): + def test_compressed_bz2(self): self.check_compressed(bz2.open, ('.bz2',)) @pytest.mark.skipif(not HAS_LZMA, reason="Needs lzma") - @pytest.mark.skipif(MAJVER == 2, reason="Needs Python version >= 3") - def test_compressed_gzip(self): + def test_compressed_lzma(self): self.check_compressed(lzma.open, ('.xz', '.lzma')) def test_encoding(self): @@ -589,12 +645,12 @@ def test_stringload(self): with temppath() as path: with open(path, "wb") as f: f.write(nonascii.encode("UTF-16")) - x = self.loadfunc(path, encoding="UTF-16", dtype=np.unicode) + x = self.loadfunc(path, encoding="UTF-16", dtype=np.unicode_) assert_array_equal(x, nonascii) def test_binary_decode(self): utf16 = b'\xff\xfeh\x04 \x00i\x04 \x00j\x04' - v = self.loadfunc(BytesIO(utf16), dtype=np.unicode, encoding='UTF-16') + v = self.loadfunc(BytesIO(utf16), dtype=np.unicode_, encoding='UTF-16') assert_array_equal(v, np.array(utf16.decode('UTF-16').split())) def test_converters_decode(self): @@ -602,7 +658,7 @@ def test_converters_decode(self): c = TextIO() c.write(b'\xcf\x96') c.seek(0) - x = self.loadfunc(c, dtype=np.unicode, + x = self.loadfunc(c, dtype=np.unicode_, converters={0: lambda x: x.decode('UTF-8')}) a = np.array([b'\xcf\x96'.decode('UTF-8')]) assert_array_equal(x, a) @@ -613,7 +669,7 @@ def test_converters_nodecode(self): with temppath() as path: with io.open(path, 'wt', encoding='UTF-8') as f: f.write(utf8) - x = self.loadfunc(path, dtype=np.unicode, + x = self.loadfunc(path, dtype=np.unicode_, converters={0: lambda x: x + 't'}, encoding='UTF-8') a = np.array([utf8 + 't']) @@ -786,7 +842,7 @@ def test_usecols(self): assert_array_equal(x, a[:, 1]) # Testing with some crazy custom integer type - class CrazyInt(object): + class CrazyInt: def __index__(self): return 1 @@ -928,6 +984,28 @@ def test_from_float_hex(self): res = np.loadtxt(c, dtype=dt) assert_equal(res, tgt, err_msg="%s" % dt) + def test_default_float_converter_no_default_hex_conversion(self): + """ + Ensure that fromhex is only used for values with the correct prefix and + is not called by default. Regression test related to gh-19598. + """ + c = TextIO("a b c") + with pytest.raises( + ValueError, match="could not convert string to float" + ): + np.loadtxt(c) + + def test_default_float_converter_exception(self): + """ + Ensure that the exception message raised during failed floating point + conversion is correct. Regression test related to gh-19598. + """ + c = TextIO("qrs tuv") # Invalid values for default float converter + with pytest.raises( + ValueError, match="could not convert string to float" + ): + np.loadtxt(c) + def test_from_complex(self): tgt = (complex(1, 1), complex(1, -1)) c = TextIO() @@ -973,7 +1051,7 @@ def test_empty_field_after_tab(self): a = np.array([b'start ', b' ', b'']) assert_array_equal(x['comment'], a) - def test_structure_unpack(self): + def test_unpack_structured(self): txt = TextIO("M 21 72\nF 35 58") dt = {'names': ('a', 'b', 'c'), 'formats': ('|S1', '<i4', '<f4')} a, b, c = np.loadtxt(txt, dtype=dt, unpack=True) @@ -1061,7 +1139,7 @@ def test_binary_load(self): with open(path, "wb") as f: f.write(butf8) with open(path, "rb") as f: - x = np.loadtxt(f, encoding="UTF-8", dtype=np.unicode) + x = np.loadtxt(f, encoding="UTF-8", dtype=np.unicode_) assert_array_equal(x, sutf8) # test broken latin1 conversion people now rely on with open(path, "rb") as f: @@ -1069,7 +1147,57 @@ def test_binary_load(self): x = [b'5,6,7,\xc3\x95scarscar', b'15,2,3,hello', b'20,2,3,\xc3\x95scar'] assert_array_equal(x, np.array(x, dtype="S")) -class Testfromregex(object): + def test_max_rows(self): + c = TextIO() + c.write('1,2,3,5\n4,5,7,8\n2,1,4,5') + c.seek(0) + x = np.loadtxt(c, dtype=int, delimiter=',', + max_rows=1) + a = np.array([1, 2, 3, 5], int) + assert_array_equal(x, a) + + def test_max_rows_with_skiprows(self): + c = TextIO() + c.write('comments\n1,2,3,5\n4,5,7,8\n2,1,4,5') + c.seek(0) + x = np.loadtxt(c, dtype=int, delimiter=',', + skiprows=1, max_rows=1) + a = np.array([1, 2, 3, 5], int) + assert_array_equal(x, a) + + c = TextIO() + c.write('comment\n1,2,3,5\n4,5,7,8\n2,1,4,5') + c.seek(0) + x = np.loadtxt(c, dtype=int, delimiter=',', + skiprows=1, max_rows=2) + a = np.array([[1, 2, 3, 5], [4, 5, 7, 8]], int) + assert_array_equal(x, a) + + def test_max_rows_with_read_continuation(self): + c = TextIO() + c.write('1,2,3,5\n4,5,7,8\n2,1,4,5') + c.seek(0) + x = np.loadtxt(c, dtype=int, delimiter=',', + max_rows=2) + a = np.array([[1, 2, 3, 5], [4, 5, 7, 8]], int) + assert_array_equal(x, a) + # test continuation + x = np.loadtxt(c, dtype=int, delimiter=',') + a = np.array([2,1,4,5], int) + assert_array_equal(x, a) + + def test_max_rows_larger(self): + #test max_rows > num rows + c = TextIO() + c.write('comment\n1,2,3,5\n4,5,7,8\n2,1,4,5') + c.seek(0) + x = np.loadtxt(c, dtype=int, delimiter=',', + skiprows=1, max_rows=6) + a = np.array([[1, 2, 3, 5], [4, 5, 7, 8], [2, 1, 4, 5]], int) + assert_array_equal(x, a) + + +class Testfromregex: def test_record(self): c = TextIO() c.write('1.312 foo\n1.534 bar\n4.444 qux') @@ -1102,9 +1230,11 @@ def test_record_3(self): a = np.array([(1312,), (1534,), (4444,)], dtype=dt) assert_array_equal(x, a) - def test_record_unicode(self): + @pytest.mark.parametrize("path_type", [str, Path]) + def test_record_unicode(self, path_type): utf8 = b'\xcf\x96' - with temppath() as path: + with temppath() as str_path: + path = path_type(str_path) with open(path, 'wb') as f: f.write(b'1.312 foo' + utf8 + b' \n1.534 bar\n4.444 qux') @@ -1126,6 +1256,13 @@ def test_compiled_bytes(self): x = np.fromregex(c, regexp, dt) assert_array_equal(x, a) + def test_bad_dtype_not_structured(self): + regexp = re.compile(b'(\\d)') + c = BytesIO(b'123') + with pytest.raises(TypeError, match='structured datatype'): + np.fromregex(c, regexp, dtype=np.float64) + + #####-------------------------------------------------------------------------- @@ -1135,7 +1272,7 @@ class TestFromTxt(LoadTxtBase): def test_record(self): # Test w/ explicit dtype data = TextIO('1 2\n3 4') - test = np.ndfromtxt(data, dtype=[('x', np.int32), ('y', np.int32)]) + test = np.genfromtxt(data, dtype=[('x', np.int32), ('y', np.int32)]) control = np.array([(1, 2), (3, 4)], dtype=[('x', 'i4'), ('y', 'i4')]) assert_equal(test, control) # @@ -1144,14 +1281,14 @@ def test_record(self): 'formats': ('S1', 'i4', 'f4')} control = np.array([('M', 64.0, 75.0), ('F', 25.0, 60.0)], dtype=descriptor) - test = np.ndfromtxt(data, dtype=descriptor) + test = np.genfromtxt(data, dtype=descriptor) assert_equal(test, control) def test_array(self): # Test outputting a standard ndarray data = TextIO('1 2\n3 4') control = np.array([[1, 2], [3, 4]], dtype=int) - test = np.ndfromtxt(data, dtype=int) + test = np.genfromtxt(data, dtype=int) assert_array_equal(test, control) # data.seek(0) @@ -1164,11 +1301,11 @@ def test_1D(self): control = np.array([1, 2, 3, 4], int) # data = TextIO('1\n2\n3\n4\n') - test = np.ndfromtxt(data, dtype=int) + test = np.genfromtxt(data, dtype=int) assert_array_equal(test, control) # data = TextIO('1,2,3,4\n') - test = np.ndfromtxt(data, dtype=int, delimiter=',') + test = np.genfromtxt(data, dtype=int, delimiter=',') assert_array_equal(test, control) def test_comments(self): @@ -1176,11 +1313,11 @@ def test_comments(self): control = np.array([1, 2, 3, 5], int) # Comment on its own line data = TextIO('# comment\n1,2,3,5\n') - test = np.ndfromtxt(data, dtype=int, delimiter=',', comments='#') + test = np.genfromtxt(data, dtype=int, delimiter=',', comments='#') assert_equal(test, control) # Comment at the end of a line data = TextIO('1,2,3,5# comment\n') - test = np.ndfromtxt(data, dtype=int, delimiter=',', comments='#') + test = np.genfromtxt(data, dtype=int, delimiter=',', comments='#') assert_equal(test, control) def test_skiprows(self): @@ -1189,7 +1326,7 @@ def test_skiprows(self): kwargs = dict(dtype=int, delimiter=',') # data = TextIO('comment\n1,2,3,5\n') - test = np.ndfromtxt(data, skip_header=1, **kwargs) + test = np.genfromtxt(data, skip_header=1, **kwargs) assert_equal(test, control) # data = TextIO('# comment\n1,2,3,5\n') @@ -1236,7 +1373,7 @@ def test_header(self): data = TextIO('gender age weight\nM 64.0 75.0\nF 25.0 60.0') with warnings.catch_warnings(record=True) as w: warnings.filterwarnings('always', '', np.VisibleDeprecationWarning) - test = np.ndfromtxt(data, dtype=None, names=True) + test = np.genfromtxt(data, dtype=None, names=True) assert_(w[0].category is np.VisibleDeprecationWarning) control = {'gender': np.array([b'M', b'F']), 'age': np.array([64.0, 25.0]), @@ -1250,7 +1387,7 @@ def test_auto_dtype(self): data = TextIO('A 64 75.0 3+4j True\nBCD 25 60.0 5+6j False') with warnings.catch_warnings(record=True) as w: warnings.filterwarnings('always', '', np.VisibleDeprecationWarning) - test = np.ndfromtxt(data, dtype=None) + test = np.genfromtxt(data, dtype=None) assert_(w[0].category is np.VisibleDeprecationWarning) control = [np.array([b'A', b'BCD']), np.array([64, 25]), @@ -1264,7 +1401,7 @@ def test_auto_dtype(self): def test_auto_dtype_uniform(self): # Tests whether the output dtype can be uniformized data = TextIO('1 2 3 4\n5 6 7 8\n') - test = np.ndfromtxt(data, dtype=None) + test = np.genfromtxt(data, dtype=None) control = np.array([[1, 2, 3, 4], [5, 6, 7, 8]]) assert_equal(test, control) @@ -1272,7 +1409,7 @@ def test_fancy_dtype(self): # Check that a nested dtype isn't MIA data = TextIO('1,2,3.0\n4,5,6.0\n') fancydtype = np.dtype([('x', int), ('y', [('t', int), ('s', float)])]) - test = np.ndfromtxt(data, dtype=fancydtype, delimiter=',') + test = np.genfromtxt(data, dtype=fancydtype, delimiter=',') control = np.array([(1, (2, 3.0)), (4, (5, 6.0))], dtype=fancydtype) assert_equal(test, control) @@ -1282,7 +1419,7 @@ def test_names_overwrite(self): 'formats': ('S1', 'i4', 'f4')} data = TextIO(b'M 64.0 75.0\nF 25.0 60.0') names = ('gender', 'age', 'weight') - test = np.ndfromtxt(data, dtype=descriptor, names=names) + test = np.genfromtxt(data, dtype=descriptor, names=names) descriptor['names'] = names control = np.array([('M', 64.0, 75.0), ('F', 25.0, 60.0)], dtype=descriptor) @@ -1324,12 +1461,25 @@ def test_names_and_comments_none(self): control = np.array([(1, 2), (3, 4)], dtype=[('col1', int), ('col2', int)]) assert_equal(test, control) + def test_file_is_closed_on_error(self): + # gh-13200 + with tempdir() as tmpdir: + fpath = os.path.join(tmpdir, "test.csv") + with open(fpath, "wb") as f: + f.write(u'\N{GREEK PI SYMBOL}'.encode('utf8')) + + # ResourceWarnings are emitted from a destructor, so won't be + # detected by regular propagation to errors. + with assert_no_warnings(): + with pytest.raises(UnicodeDecodeError): + np.genfromtxt(fpath, encoding="ascii") + def test_autonames_and_usecols(self): # Tests names and usecols data = TextIO('A B C D\n aaaa 121 45 9.1') with warnings.catch_warnings(record=True) as w: warnings.filterwarnings('always', '', np.VisibleDeprecationWarning) - test = np.ndfromtxt(data, usecols=('A', 'C', 'D'), + test = np.genfromtxt(data, usecols=('A', 'C', 'D'), names=True, dtype=None) assert_(w[0].category is np.VisibleDeprecationWarning) control = np.array(('aaaa', 45, 9.1), @@ -1339,7 +1489,7 @@ def test_autonames_and_usecols(self): def test_converters_with_usecols(self): # Test the combination user-defined converters and usecol data = TextIO('1,2,3,,5\n6,7,8,9,10\n') - test = np.ndfromtxt(data, dtype=int, delimiter=',', + test = np.genfromtxt(data, dtype=int, delimiter=',', converters={3: lambda s: int(s or - 999)}, usecols=(1, 3,)) control = np.array([[2, -999], [7, 9]], int) @@ -1350,7 +1500,7 @@ def test_converters_with_usecols_and_names(self): data = TextIO('A B C D\n aaaa 121 45 9.1') with warnings.catch_warnings(record=True) as w: warnings.filterwarnings('always', '', np.VisibleDeprecationWarning) - test = np.ndfromtxt(data, usecols=('A', 'C', 'D'), names=True, + test = np.genfromtxt(data, usecols=('A', 'C', 'D'), names=True, dtype=None, converters={'C': lambda s: 2 * int(s)}) assert_(w[0].category is np.VisibleDeprecationWarning) @@ -1363,7 +1513,7 @@ def test_converters_cornercases(self): converter = { 'date': lambda s: strptime(s, '%Y-%m-%d %H:%M:%SZ')} data = TextIO('2009-02-03 12:00:00Z, 72214.0') - test = np.ndfromtxt(data, delimiter=',', dtype=None, + test = np.genfromtxt(data, delimiter=',', dtype=None, names=['date', 'stid'], converters=converter) control = np.array((datetime(2009, 2, 3), 72214.), dtype=[('date', np.object_), ('stid', float)]) @@ -1374,7 +1524,7 @@ def test_converters_cornercases2(self): converter = { 'date': lambda s: np.datetime64(strptime(s, '%Y-%m-%d %H:%M:%SZ'))} data = TextIO('2009-02-03 12:00:00Z, 72214.0') - test = np.ndfromtxt(data, delimiter=',', dtype=None, + test = np.genfromtxt(data, delimiter=',', dtype=None, names=['date', 'stid'], converters=converter) control = np.array((datetime(2009, 2, 3), 72214.), dtype=[('date', 'datetime64[us]'), ('stid', float)]) @@ -1383,12 +1533,12 @@ def test_converters_cornercases2(self): def test_unused_converter(self): # Test whether unused converters are forgotten data = TextIO("1 21\n 3 42\n") - test = np.ndfromtxt(data, usecols=(1,), + test = np.genfromtxt(data, usecols=(1,), converters={0: lambda s: int(s, 16)}) assert_equal(test, [21, 42]) # data.seek(0) - test = np.ndfromtxt(data, usecols=(1,), + test = np.genfromtxt(data, usecols=(1,), converters={1: lambda s: int(s, 16)}) assert_equal(test, [33, 66]) @@ -1415,12 +1565,12 @@ def test_tricky_converter_bug1666(self): def test_dtype_with_converters(self): dstr = "2009; 23; 46" - test = np.ndfromtxt(TextIO(dstr,), + test = np.genfromtxt(TextIO(dstr,), delimiter=";", dtype=float, converters={0: bytes}) control = np.array([('2009', 23., 46)], dtype=[('f0', '|S4'), ('f1', float), ('f2', float)]) assert_equal(test, control) - test = np.ndfromtxt(TextIO(dstr,), + test = np.genfromtxt(TextIO(dstr,), delimiter=";", dtype=float, converters={0: float}) control = np.array([2009., 23., 46],) assert_equal(test, control) @@ -1455,14 +1605,24 @@ def test_dtype_with_object(self): assert_equal(test, control) ndtype = [('nest', [('idx', int), ('code', object)])] - try: + with assert_raises_regex(NotImplementedError, + 'Nested fields.* not supported.*'): test = np.genfromtxt(TextIO(data), delimiter=";", dtype=ndtype, converters=converters) - except NotImplementedError: - pass - else: - errmsg = "Nested dtype involving objects should be supported." - raise AssertionError(errmsg) + + # nested but empty fields also aren't supported + ndtype = [('idx', int), ('code', object), ('nest', [])] + with assert_raises_regex(NotImplementedError, + 'Nested fields.* not supported.*'): + test = np.genfromtxt(TextIO(data), delimiter=";", + dtype=ndtype, converters=converters) + + def test_dtype_with_object_no_converter(self): + # Object without a converter uses bytes: + parsed = np.genfromtxt(TextIO("1"), dtype=object) + assert parsed[()] == b"1" + parsed = np.genfromtxt(TextIO("string"), dtype=object) + assert parsed[()] == b"string" def test_userconverters_with_explicit_dtype(self): # Test user_converters w/ explicit (standard) dtype @@ -1479,7 +1639,7 @@ def test_utf8_userconverters_with_explicit_dtype(self): with open(path, 'wb') as f: f.write(b'skip,skip,2001-01-01' + utf8 + b',1.0,skip') test = np.genfromtxt(path, delimiter=",", names=None, dtype=float, - usecols=(2, 3), converters={2: np.unicode}, + usecols=(2, 3), converters={2: np.compat.unicode}, encoding='UTF-8') control = np.array([('2001-01-01' + utf8.decode('UTF-8'), 1.)], dtype=[('', '|U11'), ('', float)]) @@ -1488,7 +1648,7 @@ def test_utf8_userconverters_with_explicit_dtype(self): def test_spacedelimiter(self): # Test space delimiter data = TextIO("1 2 3 4 5\n6 7 8 9 10") - test = np.ndfromtxt(data) + test = np.genfromtxt(data) control = np.array([[1., 2., 3., 4., 5.], [6., 7., 8., 9., 10.]]) assert_equal(test, control) @@ -1502,7 +1662,7 @@ def test_integer_delimiter(self): def test_missing(self): data = TextIO('1,2,3,,5\n') - test = np.ndfromtxt(data, dtype=int, delimiter=',', + test = np.genfromtxt(data, dtype=int, delimiter=',', converters={3: lambda s: int(s or - 999)}) control = np.array([1, 2, 3, -999, 5], int) assert_equal(test, control) @@ -1524,18 +1684,18 @@ def test_usecols(self): data = TextIO() np.savetxt(data, control) data.seek(0) - test = np.ndfromtxt(data, dtype=float, usecols=(1,)) + test = np.genfromtxt(data, dtype=float, usecols=(1,)) assert_equal(test, control[:, 1]) # control = np.array([[1, 2, 3], [3, 4, 5]], float) data = TextIO() np.savetxt(data, control) data.seek(0) - test = np.ndfromtxt(data, dtype=float, usecols=(1, 2)) + test = np.genfromtxt(data, dtype=float, usecols=(1, 2)) assert_equal(test, control[:, 1:]) # Testing with arrays instead of tuples. data.seek(0) - test = np.ndfromtxt(data, dtype=float, usecols=np.array([1, 2])) + test = np.genfromtxt(data, dtype=float, usecols=np.array([1, 2])) assert_equal(test, control[:, 1:]) def test_usecols_as_css(self): @@ -1551,7 +1711,7 @@ def test_usecols_with_structured_dtype(self): data = TextIO("JOE 70.1 25.3\nBOB 60.5 27.9") names = ['stid', 'temp'] dtypes = ['S4', 'f8'] - test = np.ndfromtxt( + test = np.genfromtxt( data, usecols=(0, 2), dtype=list(zip(names, dtypes))) assert_equal(test['stid'], [b"JOE", b"BOB"]) assert_equal(test['temp'], [25.3, 27.9]) @@ -1580,11 +1740,15 @@ def test_empty_file(self): test = np.genfromtxt(data) assert_equal(test, np.array([])) + # when skip_header > 0 + test = np.genfromtxt(data, skip_header=1) + assert_equal(test, np.array([])) + def test_fancy_dtype_alt(self): # Check that a nested dtype isn't MIA data = TextIO('1,2,3.0\n4,5,6.0\n') fancydtype = np.dtype([('x', int), ('y', [('t', int), ('s', float)])]) - test = np.mafromtxt(data, dtype=fancydtype, delimiter=',') + test = np.genfromtxt(data, dtype=fancydtype, delimiter=',', usemask=True) control = ma.array([(1, (2, 3.0)), (4, (5, 6.0))], dtype=fancydtype) assert_equal(test, control) @@ -1592,7 +1756,7 @@ def test_shaped_dtype(self): c = TextIO("aaaa 1.0 8.0 1 2 3 4 5 6") dt = np.dtype([('name', 'S4'), ('x', float), ('y', float), ('block', int, (2, 3))]) - x = np.ndfromtxt(c, dtype=dt) + x = np.genfromtxt(c, dtype=dt) a = np.array([('aaaa', 1.0, 8.0, [[1, 2, 3], [4, 5, 6]])], dtype=dt) assert_array_equal(x, a) @@ -1600,7 +1764,7 @@ def test_shaped_dtype(self): def test_withmissing(self): data = TextIO('A,B\n0,1\n2,N/A') kwargs = dict(delimiter=",", missing_values="N/A", names=True) - test = np.mafromtxt(data, dtype=None, **kwargs) + test = np.genfromtxt(data, dtype=None, usemask=True, **kwargs) control = ma.array([(0, 1), (2, -1)], mask=[(False, False), (False, True)], dtype=[('A', int), ('B', int)]) @@ -1608,7 +1772,7 @@ def test_withmissing(self): assert_equal(test.mask, control.mask) # data.seek(0) - test = np.mafromtxt(data, **kwargs) + test = np.genfromtxt(data, usemask=True, **kwargs) control = ma.array([(0, 1), (2, -1)], mask=[(False, False), (False, True)], dtype=[('A', float), ('B', float)]) @@ -1620,7 +1784,7 @@ def test_user_missing_values(self): basekwargs = dict(dtype=None, delimiter=",", names=True,) mdtype = [('A', int), ('B', float), ('C', complex)] # - test = np.mafromtxt(TextIO(data), missing_values="N/A", + test = np.genfromtxt(TextIO(data), missing_values="N/A", **basekwargs) control = ma.array([(0, 0.0, 0j), (1, -999, 1j), (-9, 2.2, -999j), (3, -99, 3j)], @@ -1629,16 +1793,17 @@ def test_user_missing_values(self): assert_equal(test, control) # basekwargs['dtype'] = mdtype - test = np.mafromtxt(TextIO(data), - missing_values={0: -9, 1: -99, 2: -999j}, **basekwargs) + test = np.genfromtxt(TextIO(data), + missing_values={0: -9, 1: -99, 2: -999j}, usemask=True, **basekwargs) control = ma.array([(0, 0.0, 0j), (1, -999, 1j), (-9, 2.2, -999j), (3, -99, 3j)], mask=[(0, 0, 0), (0, 1, 0), (1, 0, 1), (0, 1, 0)], dtype=mdtype) assert_equal(test, control) # - test = np.mafromtxt(TextIO(data), + test = np.genfromtxt(TextIO(data), missing_values={0: -9, 'B': -99, 'C': -999j}, + usemask=True, **basekwargs) control = ma.array([(0, 0.0, 0j), (1, -999, 1j), (-9, 2.2, -999j), (3, -99, 3j)], @@ -1676,8 +1841,8 @@ def test_user_filling_values(self): def test_withmissing_float(self): data = TextIO('A,B\n0,1.5\n2,-999.00') - test = np.mafromtxt(data, dtype=None, delimiter=',', - missing_values='-999.0', names=True,) + test = np.genfromtxt(data, dtype=None, delimiter=',', + missing_values='-999.0', names=True, usemask=True) control = ma.array([(0, 1.5), (2, -1.)], mask=[(False, False), (False, True)], dtype=[('A', int), ('B', float)]) @@ -1709,21 +1874,16 @@ def test_invalid_raise(self): data[10 * i] = "2, 2, 2, 2 2" data.insert(0, "a, b, c, d, e") mdata = TextIO("\n".join(data)) - # + kwargs = dict(delimiter=",", dtype=None, names=True) - # XXX: is there a better way to get the return value of the - # callable in assert_warns ? - ret = {} - - def f(_ret={}): - _ret['mtest'] = np.ndfromtxt(mdata, invalid_raise=False, **kwargs) - assert_warns(ConversionWarning, f, _ret=ret) - mtest = ret['mtest'] + def f(): + return np.genfromtxt(mdata, invalid_raise=False, **kwargs) + mtest = assert_warns(ConversionWarning, f) assert_equal(len(mtest), 45) assert_equal(mtest, np.ones(45, dtype=[(_, int) for _ in 'abcde'])) # mdata.seek(0) - assert_raises(ValueError, np.ndfromtxt, mdata, + assert_raises(ValueError, np.genfromtxt, mdata, delimiter=",", names=True) def test_invalid_raise_with_usecols(self): @@ -1733,21 +1893,17 @@ def test_invalid_raise_with_usecols(self): data[10 * i] = "2, 2, 2, 2 2" data.insert(0, "a, b, c, d, e") mdata = TextIO("\n".join(data)) + kwargs = dict(delimiter=",", dtype=None, names=True, invalid_raise=False) - # XXX: is there a better way to get the return value of the - # callable in assert_warns ? - ret = {} - - def f(_ret={}): - _ret['mtest'] = np.ndfromtxt(mdata, usecols=(0, 4), **kwargs) - assert_warns(ConversionWarning, f, _ret=ret) - mtest = ret['mtest'] + def f(): + return np.genfromtxt(mdata, usecols=(0, 4), **kwargs) + mtest = assert_warns(ConversionWarning, f) assert_equal(len(mtest), 45) assert_equal(mtest, np.ones(45, dtype=[(_, int) for _ in 'ae'])) # mdata.seek(0) - mtest = np.ndfromtxt(mdata, usecols=(0, 1), **kwargs) + mtest = np.genfromtxt(mdata, usecols=(0, 1), **kwargs) assert_equal(len(mtest), 50) control = np.ones(50, dtype=[(_, int) for _ in 'ab']) control[[10 * _ for _ in range(5)]] = (2, 2) @@ -1758,7 +1914,7 @@ def test_inconsistent_dtype(self): data = ["1, 1, 1, 1, -1.1"] * 50 mdata = TextIO("\n".join(data)) - converters = {4: lambda x: "(%s)" % x} + converters = {4: lambda x: "(%s)" % x.decode()} kwargs = dict(delimiter=",", converters=converters, dtype=[(_, int) for _ in 'abcde'],) assert_raises(ValueError, np.genfromtxt, mdata, **kwargs) @@ -1766,7 +1922,7 @@ def test_inconsistent_dtype(self): def test_default_field_format(self): # Test default format data = "0, 1, 2.3\n4, 5, 6.7" - mtest = np.ndfromtxt(TextIO(data), + mtest = np.genfromtxt(TextIO(data), delimiter=",", dtype=None, defaultfmt="f%02i") ctrl = np.array([(0, 1, 2.3), (4, 5, 6.7)], dtype=[("f00", int), ("f01", int), ("f02", float)]) @@ -1775,7 +1931,7 @@ def test_default_field_format(self): def test_single_dtype_wo_names(self): # Test single dtype w/o names data = "0, 1, 2.3\n4, 5, 6.7" - mtest = np.ndfromtxt(TextIO(data), + mtest = np.genfromtxt(TextIO(data), delimiter=",", dtype=float, defaultfmt="f%02i") ctrl = np.array([[0., 1., 2.3], [4., 5., 6.7]], dtype=float) assert_equal(mtest, ctrl) @@ -1783,7 +1939,7 @@ def test_single_dtype_wo_names(self): def test_single_dtype_w_explicit_names(self): # Test single dtype w explicit names data = "0, 1, 2.3\n4, 5, 6.7" - mtest = np.ndfromtxt(TextIO(data), + mtest = np.genfromtxt(TextIO(data), delimiter=",", dtype=float, names="a, b, c") ctrl = np.array([(0., 1., 2.3), (4., 5., 6.7)], dtype=[(_, float) for _ in "abc"]) @@ -1792,7 +1948,7 @@ def test_single_dtype_w_explicit_names(self): def test_single_dtype_w_implicit_names(self): # Test single dtype w implicit names data = "a, b, c\n0, 1, 2.3\n4, 5, 6.7" - mtest = np.ndfromtxt(TextIO(data), + mtest = np.genfromtxt(TextIO(data), delimiter=",", dtype=float, names=True) ctrl = np.array([(0., 1., 2.3), (4., 5., 6.7)], dtype=[(_, float) for _ in "abc"]) @@ -1801,7 +1957,7 @@ def test_single_dtype_w_implicit_names(self): def test_easy_structured_dtype(self): # Test easy structured dtype data = "0, 1, 2.3\n4, 5, 6.7" - mtest = np.ndfromtxt(TextIO(data), delimiter=",", + mtest = np.genfromtxt(TextIO(data), delimiter=",", dtype=(int, float, float), defaultfmt="f_%02i") ctrl = np.array([(0, 1., 2.3), (4, 5., 6.7)], dtype=[("f_00", int), ("f_01", float), ("f_02", float)]) @@ -1813,14 +1969,14 @@ def test_autostrip(self): kwargs = dict(delimiter=",", dtype=None) with warnings.catch_warnings(record=True) as w: warnings.filterwarnings('always', '', np.VisibleDeprecationWarning) - mtest = np.ndfromtxt(TextIO(data), **kwargs) + mtest = np.genfromtxt(TextIO(data), **kwargs) assert_(w[0].category is np.VisibleDeprecationWarning) ctrl = np.array([('01/01/2003 ', 1.3, ' abcde')], dtype=[('f0', '|S12'), ('f1', float), ('f2', '|S8')]) assert_equal(mtest, ctrl) with warnings.catch_warnings(record=True) as w: warnings.filterwarnings('always', '', np.VisibleDeprecationWarning) - mtest = np.ndfromtxt(TextIO(data), autostrip=True, **kwargs) + mtest = np.genfromtxt(TextIO(data), autostrip=True, **kwargs) assert_(w[0].category is np.VisibleDeprecationWarning) ctrl = np.array([('01/01/2003', 1.3, 'abcde')], dtype=[('f0', '|S10'), ('f1', float), ('f2', '|S5')]) @@ -1881,12 +2037,12 @@ def test_incomplete_names(self): # w/ dtype=None ctrl = np.array([(0, 1, 2), (3, 4, 5)], dtype=[(_, int) for _ in ('A', 'f0', 'C')]) - test = np.ndfromtxt(TextIO(data), dtype=None, **kwargs) + test = np.genfromtxt(TextIO(data), dtype=None, **kwargs) assert_equal(test, ctrl) # w/ default dtype ctrl = np.array([(0, 1, 2), (3, 4, 5)], dtype=[(_, float) for _ in ('A', 'f0', 'C')]) - test = np.ndfromtxt(TextIO(data), **kwargs) + test = np.genfromtxt(TextIO(data), **kwargs) def test_names_auto_completion(self): # Make sure that names are properly completed @@ -1922,13 +2078,13 @@ def test_fixed_width_names(self): kwargs = dict(delimiter=(5, 5, 4), names=True, dtype=None) ctrl = np.array([(0, 1, 2.3), (45, 67, 9.)], dtype=[('A', int), ('B', int), ('C', float)]) - test = np.ndfromtxt(TextIO(data), **kwargs) + test = np.genfromtxt(TextIO(data), **kwargs) assert_equal(test, ctrl) # kwargs = dict(delimiter=5, names=True, dtype=None) ctrl = np.array([(0, 1, 2.3), (45, 67, 9.)], dtype=[('A', int), ('B', int), ('C', float)]) - test = np.ndfromtxt(TextIO(data), **kwargs) + test = np.genfromtxt(TextIO(data), **kwargs) assert_equal(test, ctrl) def test_filling_values(self): @@ -1936,7 +2092,7 @@ def test_filling_values(self): data = b"1, 2, 3\n1, , 5\n0, 6, \n" kwargs = dict(delimiter=",", dtype=None, filling_values=-999) ctrl = np.array([[1, 2, 3], [1, -999, 5], [0, 6, -999]], dtype=int) - test = np.ndfromtxt(TextIO(data), **kwargs) + test = np.genfromtxt(TextIO(data), **kwargs) assert_equal(test, ctrl) def test_comments_is_none(self): @@ -2005,7 +2161,6 @@ def test_utf8_byte_encoding(self): def test_utf8_file(self): utf8 = b"\xcf\x96" - latin1 = b"\xf6\xfc\xf6" with temppath() as path: with open(path, "wb") as f: f.write((b"test1,testNonethe" + utf8 + b",test3\n") * 2) @@ -2014,7 +2169,7 @@ def test_utf8_file(self): ctl = np.array([ ["test1", "testNonethe" + utf8.decode("UTF-8"), "test3"], ["test1", "testNonethe" + utf8.decode("UTF-8"), "test3"]], - dtype=np.unicode) + dtype=np.unicode_) assert_array_equal(test, ctl) # test a mixed dtype @@ -2025,7 +2180,6 @@ def test_utf8_file(self): assert_equal(test['f0'], 0) assert_equal(test['f1'], "testNonethe" + utf8.decode("UTF-8")) - def test_utf8_file_nodtype_unicode(self): # bytes encoding with non-latin1 -> unicode upcast utf8 = u'\u03d6' @@ -2039,8 +2193,8 @@ def test_utf8_file_nodtype_unicode(self): encoding = locale.getpreferredencoding() utf8.encode(encoding) except (UnicodeError, ImportError): - raise SkipTest('Skipping test_utf8_file_nodtype_unicode, ' - 'unable to encode utf8 in preferred encoding') + pytest.skip('Skipping test_utf8_file_nodtype_unicode, ' + 'unable to encode utf8 in preferred encoding') with temppath() as path: with io.open(path, "wt") as f: @@ -2058,7 +2212,7 @@ def test_utf8_file_nodtype_unicode(self): ["norm1", "norm2", "norm3"], ["norm1", latin1, "norm3"], ["test1", "testNonethe" + utf8, "test3"]], - dtype=np.unicode) + dtype=np.unicode_) assert_array_equal(test, ctl) def test_recfromtxt(self): @@ -2227,21 +2381,65 @@ def test_auto_dtype_largeint(self): data = TextIO('73786976294838206464 17179869184 1024') - test = np.ndfromtxt(data, dtype=None) + test = np.genfromtxt(data, dtype=None) assert_equal(test.dtype.names, ['f0', 'f1', 'f2']) assert_(test.dtype['f0'] == float) assert_(test.dtype['f1'] == np.int64) - assert_(test.dtype['f2'] == np.integer) + assert_(test.dtype['f2'] == np.int_) assert_allclose(test['f0'], 73786976294838206464.) assert_equal(test['f1'], 17179869184) assert_equal(test['f2'], 1024) + def test_unpack_structured(self): + # Regression test for gh-4341 + # Unpacking should work on structured arrays + txt = TextIO("M 21 72\nF 35 58") + dt = {'names': ('a', 'b', 'c'), 'formats': ('S1', 'i4', 'f4')} + a, b, c = np.genfromtxt(txt, dtype=dt, unpack=True) + assert_equal(a.dtype, np.dtype('S1')) + assert_equal(b.dtype, np.dtype('i4')) + assert_equal(c.dtype, np.dtype('f4')) + assert_array_equal(a, np.array([b'M', b'F'])) + assert_array_equal(b, np.array([21, 35])) + assert_array_equal(c, np.array([72., 58.])) -@pytest.mark.skipif(Path is None, reason="No pathlib.Path") -class TestPathUsage(object): + def test_unpack_auto_dtype(self): + # Regression test for gh-4341 + # Unpacking should work when dtype=None + txt = TextIO("M 21 72.\nF 35 58.") + expected = (np.array(["M", "F"]), np.array([21, 35]), np.array([72., 58.])) + test = np.genfromtxt(txt, dtype=None, unpack=True, encoding="utf-8") + for arr, result in zip(expected, test): + assert_array_equal(arr, result) + assert_equal(arr.dtype, result.dtype) + + def test_unpack_single_name(self): + # Regression test for gh-4341 + # Unpacking should work when structured dtype has only one field + txt = TextIO("21\n35") + dt = {'names': ('a',), 'formats': ('i4',)} + expected = np.array([21, 35], dtype=np.int32) + test = np.genfromtxt(txt, dtype=dt, unpack=True) + assert_array_equal(expected, test) + assert_equal(expected.dtype, test.dtype) + + def test_squeeze_scalar(self): + # Regression test for gh-4341 + # Unpacking a scalar should give zero-dim output, + # even if dtype is structured + txt = TextIO("1") + dt = {'names': ('a',), 'formats': ('i4',)} + expected = np.array((1,), dtype=np.int32) + test = np.genfromtxt(txt, dtype=dt, unpack=True) + assert_array_equal(expected, test) + assert_equal((), test.shape) + assert_equal(expected.dtype, test.dtype) + + +class TestPathUsage: # Test that pathlib.Path can be used def test_loadtxt(self): with temppath(suffix='.txt') as path: @@ -2252,11 +2450,41 @@ def test_loadtxt(self): assert_array_equal(x, a) def test_save_load(self): - # Test that pathlib.Path instances can be used with savez. + # Test that pathlib.Path instances can be used with save. + with temppath(suffix='.npy') as path: + path = Path(path) + a = np.array([[1, 2], [3, 4]], int) + np.save(path, a) + data = np.load(path) + assert_array_equal(data, a) + + def test_save_load_memmap(self): + # Test that pathlib.Path instances can be loaded mem-mapped. + with temppath(suffix='.npy') as path: + path = Path(path) + a = np.array([[1, 2], [3, 4]], int) + np.save(path, a) + data = np.load(path, mmap_mode='r') + assert_array_equal(data, a) + # close the mem-mapped file + del data + if IS_PYPY: + break_cycles() + break_cycles() + + def test_save_load_memmap_readwrite(self): + # Test that pathlib.Path instances can be written mem-mapped. with temppath(suffix='.npy') as path: path = Path(path) a = np.array([[1, 2], [3, 4]], int) np.save(path, a) + b = np.load(path, mmap_mode='r+') + a[0][0] = 5 + b[0][0] = 5 + del b # closes the file + if IS_PYPY: + break_cycles() + break_cycles() data = np.load(path) assert_array_equal(data, a) @@ -2267,7 +2495,7 @@ def test_savez_load(self): np.savez(path, lab='place holder') with np.load(path) as data: assert_array_equal(data['lab'], 'place holder') - + def test_savez_compressed_load(self): # Test that pathlib.Path instances can be used with savez. with temppath(suffix='.npz') as path: @@ -2285,28 +2513,6 @@ def test_genfromtxt(self): data = np.genfromtxt(path) assert_array_equal(a, data) - def test_ndfromtxt(self): - # Test outputting a standard ndarray - with temppath(suffix='.txt') as path: - path = Path(path) - with path.open('w') as f: - f.write(u'1 2\n3 4') - - control = np.array([[1, 2], [3, 4]], dtype=int) - test = np.ndfromtxt(path, dtype=int) - assert_array_equal(test, control) - - def test_mafromtxt(self): - # From `test_fancy_dtype_alt` above - with temppath(suffix='.txt') as path: - path = Path(path) - with path.open('w') as f: - f.write(u'1,2,3.0\n4,5,6.0\n') - - test = np.mafromtxt(path, delimiter=',') - control = ma.array([(1.0, 2.0, 3.0), (4.0, 5.0, 6.0)]) - assert_equal(test, control) - def test_recfromtxt(self): with temppath(suffix='.txt') as path: path = Path(path) @@ -2348,6 +2554,44 @@ def test_gzip_load(): assert_array_equal(np.load(f), a) +# These next two classes encode the minimal API needed to save()/load() arrays. +# The `test_ducktyping` ensures they work correctly +class JustWriter: + def __init__(self, base): + self.base = base + + def write(self, s): + return self.base.write(s) + + def flush(self): + return self.base.flush() + +class JustReader: + def __init__(self, base): + self.base = base + + def read(self, n): + return self.base.read(n) + + def seek(self, off, whence=0): + return self.base.seek(off, whence) + + +def test_ducktyping(): + a = np.random.random((5, 5)) + + s = BytesIO() + f = JustWriter(s) + + np.save(f, a) + f.flush() + s.seek(0) + + f = JustReader(s) + assert_array_equal(np.load(f), a) + + + def test_gzip_loadtxt(): # Thanks to another windows brokenness, we can't use # NamedTemporaryFile: a file created from this function cannot be @@ -2418,3 +2662,9 @@ def test_load_refcount(): with assert_no_gc_cycles(): np.load(f) + + f.seek(0) + dt = [("a", 'u1', 2), ("b", 'u1', 2)] + with assert_no_gc_cycles(): + x = np.loadtxt(TextIO("0 1 2 3"), dtype=dt) + assert_equal(x, np.array([((0, 1), (2, 3))], dtype=dt)) diff --git a/numpy/lib/tests/test_mixins.py b/numpy/lib/tests/test_mixins.py index f2d9155029a7..632058763b7d 100644 --- a/numpy/lib/tests/test_mixins.py +++ b/numpy/lib/tests/test_mixins.py @@ -1,16 +1,10 @@ -from __future__ import division, absolute_import, print_function - import numbers import operator -import sys import numpy as np from numpy.testing import assert_, assert_equal, assert_raises -PY2 = sys.version_info.major < 3 - - # NOTE: This class should be kept as an exact copy of the example from the # docstring for NDArrayOperatorsMixin. @@ -86,7 +80,6 @@ def _assert_equal_type_and_value(result, expected, err_msg=None): operator.mul, operator.truediv, operator.floordiv, - # TODO: test div on Python 2, only operator.mod, divmod, pow, @@ -98,7 +91,7 @@ def _assert_equal_type_and_value(result, expected, err_msg=None): ] -class TestNDArrayOperatorsMixin(object): +class TestNDArrayOperatorsMixin: def test_array_like_add(self): @@ -128,7 +121,7 @@ def test_inplace(self): def test_opt_out(self): - class OptOut(object): + class OptOut: """Object that opts out of __array_ufunc__.""" __array_ufunc__ = None @@ -199,6 +192,16 @@ def test_reflected_binary_methods(self): err_msg = 'failed for operator {}'.format(op) _assert_equal_type_and_value(expected, actual, err_msg=err_msg) + def test_matmul(self): + array = np.array([1, 2], dtype=np.float64) + array_like = ArrayLike(array) + expected = ArrayLike(np.float64(5)) + _assert_equal_type_and_value(expected, np.matmul(array_like, array)) + _assert_equal_type_and_value( + expected, operator.matmul(array_like, array)) + _assert_equal_type_and_value( + expected, operator.matmul(array, array_like)) + def test_ufunc_at(self): array = ArrayLike(np.array([1, 2, 3, 4])) assert_(np.negative.at(array, np.array([0, 1])) is None) diff --git a/numpy/lib/tests/test_nanfunctions.py b/numpy/lib/tests/test_nanfunctions.py index 504372faf5f6..733a077ea1fc 100644 --- a/numpy/lib/tests/test_nanfunctions.py +++ b/numpy/lib/tests/test_nanfunctions.py @@ -1,11 +1,12 @@ -from __future__ import division, absolute_import, print_function - import warnings +import pytest +import inspect import numpy as np +from numpy.lib.nanfunctions import _nan_mask, _replace_nan from numpy.testing import ( - assert_, assert_equal, assert_almost_equal, assert_no_warnings, - assert_raises, assert_array_equal, suppress_warnings + assert_, assert_equal, assert_almost_equal, assert_raises, + assert_array_equal, suppress_warnings ) @@ -35,7 +36,54 @@ [0.1610, 0.0, 0.0, 0.1859, 0.3146, 0.0]]) -class TestNanFunctions_MinMax(object): +class TestSignatureMatch: + NANFUNCS = { + np.nanmin: np.amin, + np.nanmax: np.amax, + np.nanargmin: np.argmin, + np.nanargmax: np.argmax, + np.nansum: np.sum, + np.nanprod: np.prod, + np.nancumsum: np.cumsum, + np.nancumprod: np.cumprod, + np.nanmean: np.mean, + np.nanmedian: np.median, + np.nanpercentile: np.percentile, + np.nanquantile: np.quantile, + np.nanvar: np.var, + np.nanstd: np.std, + } + IDS = [k.__name__ for k in NANFUNCS] + + @staticmethod + def get_signature(func, default="..."): + """Construct a signature and replace all default parameter-values.""" + prm_list = [] + signature = inspect.signature(func) + for prm in signature.parameters.values(): + if prm.default is inspect.Parameter.empty: + prm_list.append(prm) + else: + prm_list.append(prm.replace(default=default)) + return inspect.Signature(prm_list) + + @pytest.mark.parametrize("nan_func,func", NANFUNCS.items(), ids=IDS) + def test_signature_match(self, nan_func, func): + # Ignore the default parameter-values as they can sometimes differ + # between the two functions (*e.g.* one has `False` while the other + # has `np._NoValue`) + signature = self.get_signature(func) + nan_signature = self.get_signature(nan_func) + np.testing.assert_equal(signature, nan_signature) + + def test_exhaustiveness(self): + """Validate that all nan functions are actually tested.""" + np.testing.assert_equal( + set(self.IDS), set(np.lib.nanfunctions.__all__) + ) + + +class TestNanFunctions_MinMax: nanfuncs = [np.nanmin, np.nanmax] stdfuncs = [np.min, np.max] @@ -83,21 +131,23 @@ def test_result_values(self): res = nf(_ndat, axis=1) assert_almost_equal(res, tgt) - def test_allnans(self): - mat = np.array([np.nan]*9).reshape(3, 3) - for f in self.nanfuncs: - for axis in [None, 0, 1]: - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter('always') - assert_(np.isnan(f(mat, axis=axis)).all()) - assert_(len(w) == 1, 'no warning raised') - assert_(issubclass(w[0].category, RuntimeWarning)) - # Check scalars - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter('always') - assert_(np.isnan(f(np.nan))) - assert_(len(w) == 1, 'no warning raised') - assert_(issubclass(w[0].category, RuntimeWarning)) + @pytest.mark.parametrize("axis", [None, 0, 1]) + @pytest.mark.parametrize("dtype", np.typecodes["AllFloat"]) + @pytest.mark.parametrize("array", [ + np.array(np.nan), + np.full((3, 3), np.nan), + ], ids=["0d", "2d"]) + def test_allnans(self, axis, dtype, array): + if axis is not None and array.ndim == 0: + pytest.skip(f"`axis != None` not supported for 0d arrays") + + array = array.astype(dtype) + match = "All-NaN slice encountered" + for func in self.nanfuncs: + with pytest.warns(RuntimeWarning, match=match): + out = func(array, axis=axis) + assert np.isnan(out).all() + assert out.dtype == array.dtype def test_masked(self): mat = np.ma.fix_invalid(_ndat) @@ -168,8 +218,48 @@ def test_object_array(self): assert_(len(w) == 1, 'no warning raised') assert_(issubclass(w[0].category, RuntimeWarning)) + @pytest.mark.parametrize("dtype", np.typecodes["AllFloat"]) + def test_initial(self, dtype): + class MyNDArray(np.ndarray): + pass + + ar = np.arange(9).astype(dtype) + ar[:5] = np.nan + + for f in self.nanfuncs: + initial = 100 if f is np.nanmax else 0 + + ret1 = f(ar, initial=initial) + assert ret1.dtype == dtype + assert ret1 == initial + + ret2 = f(ar.view(MyNDArray), initial=initial) + assert ret2.dtype == dtype + assert ret2 == initial + + @pytest.mark.parametrize("dtype", np.typecodes["AllFloat"]) + def test_where(self, dtype): + class MyNDArray(np.ndarray): + pass + + ar = np.arange(9).reshape(3, 3).astype(dtype) + ar[0, :] = np.nan + where = np.ones_like(ar, dtype=np.bool_) + where[:, 0] = False -class TestNanFunctions_ArgminArgmax(object): + for f in self.nanfuncs: + reference = 4 if f is np.nanmin else 8 + + ret1 = f(ar, where=where, initial=5) + assert ret1.dtype == dtype + assert ret1 == reference + + ret2 = f(ar.view(MyNDArray), where=where, initial=5) + assert ret2.dtype == dtype + assert ret2 == reference + + +class TestNanFunctions_ArgminArgmax: nanfuncs = [np.nanargmin, np.nanargmax] @@ -193,12 +283,20 @@ def test_result_values(self): assert_(not fcmp(val, row).any()) assert_(not np.equal(val, row[:ind]).any()) - def test_allnans(self): - mat = np.array([np.nan]*9).reshape(3, 3) - for f in self.nanfuncs: - for axis in [None, 0, 1]: - assert_raises(ValueError, f, mat, axis=axis) - assert_raises(ValueError, f, np.nan) + @pytest.mark.parametrize("axis", [None, 0, 1]) + @pytest.mark.parametrize("dtype", np.typecodes["AllFloat"]) + @pytest.mark.parametrize("array", [ + np.array(np.nan), + np.full((3, 3), np.nan), + ], ids=["0d", "2d"]) + def test_allnans(self, axis, dtype, array): + if axis is not None and array.ndim == 0: + pytest.skip(f"`axis != None` not supported for 0d arrays") + + array = array.astype(dtype) + for func in self.nanfuncs: + with pytest.raises(ValueError, match="All-NaN slice encountered"): + func(array, axis=axis) def test_empty(self): mat = np.zeros((0, 3)) @@ -230,83 +328,108 @@ class MyNDArray(np.ndarray): res = f(mine) assert_(res.shape == ()) + @pytest.mark.parametrize("dtype", np.typecodes["AllFloat"]) + def test_keepdims(self, dtype): + ar = np.arange(9).astype(dtype) + ar[:5] = np.nan -class TestNanFunctions_IntTypes(object): - - int_types = (np.int8, np.int16, np.int32, np.int64, np.uint8, - np.uint16, np.uint32, np.uint64) - - mat = np.array([127, 39, 93, 87, 46]) - - def integer_arrays(self): - for dtype in self.int_types: - yield self.mat.astype(dtype) - - def test_nanmin(self): - tgt = np.min(self.mat) - for mat in self.integer_arrays(): - assert_equal(np.nanmin(mat), tgt) - - def test_nanmax(self): - tgt = np.max(self.mat) - for mat in self.integer_arrays(): - assert_equal(np.nanmax(mat), tgt) - - def test_nanargmin(self): - tgt = np.argmin(self.mat) - for mat in self.integer_arrays(): - assert_equal(np.nanargmin(mat), tgt) - - def test_nanargmax(self): - tgt = np.argmax(self.mat) - for mat in self.integer_arrays(): - assert_equal(np.nanargmax(mat), tgt) - - def test_nansum(self): - tgt = np.sum(self.mat) - for mat in self.integer_arrays(): - assert_equal(np.nansum(mat), tgt) - - def test_nanprod(self): - tgt = np.prod(self.mat) - for mat in self.integer_arrays(): - assert_equal(np.nanprod(mat), tgt) - - def test_nancumsum(self): - tgt = np.cumsum(self.mat) - for mat in self.integer_arrays(): - assert_equal(np.nancumsum(mat), tgt) - - def test_nancumprod(self): - tgt = np.cumprod(self.mat) - for mat in self.integer_arrays(): - assert_equal(np.nancumprod(mat), tgt) - - def test_nanmean(self): - tgt = np.mean(self.mat) - for mat in self.integer_arrays(): - assert_equal(np.nanmean(mat), tgt) - - def test_nanvar(self): - tgt = np.var(self.mat) - for mat in self.integer_arrays(): - assert_equal(np.nanvar(mat), tgt) + for f in self.nanfuncs: + reference = 5 if f is np.nanargmin else 8 + ret = f(ar, keepdims=True) + assert ret.ndim == ar.ndim + assert ret == reference - tgt = np.var(mat, ddof=1) - for mat in self.integer_arrays(): - assert_equal(np.nanvar(mat, ddof=1), tgt) + @pytest.mark.parametrize("dtype", np.typecodes["AllFloat"]) + def test_out(self, dtype): + ar = np.arange(9).astype(dtype) + ar[:5] = np.nan - def test_nanstd(self): - tgt = np.std(self.mat) - for mat in self.integer_arrays(): - assert_equal(np.nanstd(mat), tgt) + for f in self.nanfuncs: + out = np.zeros((), dtype=np.intp) + reference = 5 if f is np.nanargmin else 8 + ret = f(ar, out=out) + assert ret is out + assert ret == reference + + + +_TEST_ARRAYS = { + "0d": np.array(5), + "1d": np.array([127, 39, 93, 87, 46]) +} +for _v in _TEST_ARRAYS.values(): + _v.setflags(write=False) + + +@pytest.mark.parametrize( + "dtype", + np.typecodes["AllInteger"] + np.typecodes["AllFloat"] + "O", +) +@pytest.mark.parametrize("mat", _TEST_ARRAYS.values(), ids=_TEST_ARRAYS.keys()) +class TestNanFunctions_NumberTypes: + nanfuncs = { + np.nanmin: np.min, + np.nanmax: np.max, + np.nanargmin: np.argmin, + np.nanargmax: np.argmax, + np.nansum: np.sum, + np.nanprod: np.prod, + np.nancumsum: np.cumsum, + np.nancumprod: np.cumprod, + np.nanmean: np.mean, + np.nanmedian: np.median, + np.nanvar: np.var, + np.nanstd: np.std, + } + nanfunc_ids = [i.__name__ for i in nanfuncs] + + @pytest.mark.parametrize("nanfunc,func", nanfuncs.items(), ids=nanfunc_ids) + @np.errstate(over="ignore") + def test_nanfunc(self, mat, dtype, nanfunc, func): + mat = mat.astype(dtype) + tgt = func(mat) + out = nanfunc(mat) + + assert_almost_equal(out, tgt) + if dtype == "O": + assert type(out) is type(tgt) + else: + assert out.dtype == tgt.dtype + + @pytest.mark.parametrize( + "nanfunc,func", + [(np.nanquantile, np.quantile), (np.nanpercentile, np.percentile)], + ids=["nanquantile", "nanpercentile"], + ) + def test_nanfunc_q(self, mat, dtype, nanfunc, func): + mat = mat.astype(dtype) + tgt = func(mat, q=1) + out = nanfunc(mat, q=1) + + assert_almost_equal(out, tgt) + if dtype == "O": + assert type(out) is type(tgt) + else: + assert out.dtype == tgt.dtype + + @pytest.mark.parametrize( + "nanfunc,func", + [(np.nanvar, np.var), (np.nanstd, np.std)], + ids=["nanvar", "nanstd"], + ) + def test_nanfunc_ddof(self, mat, dtype, nanfunc, func): + mat = mat.astype(dtype) + tgt = func(mat, ddof=0.5) + out = nanfunc(mat, ddof=0.5) - tgt = np.std(self.mat, ddof=1) - for mat in self.integer_arrays(): - assert_equal(np.nanstd(mat, ddof=1), tgt) + assert_almost_equal(out, tgt) + if dtype == "O": + assert type(out) is type(tgt) + else: + assert out.dtype == tgt.dtype -class SharedNanFunctionsTestsMixin(object): +class SharedNanFunctionsTestsMixin: def test_mutation(self): # Check that passed array is not modified. ndat = _ndat.copy() @@ -416,20 +539,21 @@ class TestNanFunctions_SumProd(SharedNanFunctionsTestsMixin): nanfuncs = [np.nansum, np.nanprod] stdfuncs = [np.sum, np.prod] - def test_allnans(self): - # Check for FutureWarning - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter('always') - res = np.nansum([np.nan]*3, axis=None) - assert_(res == 0, 'result is not 0') - assert_(len(w) == 0, 'warning raised') - # Check scalar - res = np.nansum(np.nan) - assert_(res == 0, 'result is not 0') - assert_(len(w) == 0, 'warning raised') - # Check there is no warning for not all-nan - np.nansum([0]*3, axis=None) - assert_(len(w) == 0, 'unwanted warning raised') + @pytest.mark.parametrize("axis", [None, 0, 1]) + @pytest.mark.parametrize("dtype", np.typecodes["AllFloat"]) + @pytest.mark.parametrize("array", [ + np.array(np.nan), + np.full((3, 3), np.nan), + ], ids=["0d", "2d"]) + def test_allnans(self, axis, dtype, array): + if axis is not None and array.ndim == 0: + pytest.skip(f"`axis != None` not supported for 0d arrays") + + array = array.astype(dtype) + for func, identity in zip(self.nanfuncs, [0, 1]): + out = func(array, axis=axis) + assert np.all(out == identity) + assert out.dtype == array.dtype def test_empty(self): for f, tgt_value in zip([np.nansum, np.nanprod], [0, 1]): @@ -444,25 +568,51 @@ def test_empty(self): res = f(mat, axis=None) assert_equal(res, tgt) + @pytest.mark.parametrize("dtype", np.typecodes["AllFloat"]) + def test_initial(self, dtype): + ar = np.arange(9).astype(dtype) + ar[:5] = np.nan + + for f in self.nanfuncs: + reference = 28 if f is np.nansum else 3360 + ret = f(ar, initial=2) + assert ret.dtype == dtype + assert ret == reference + + @pytest.mark.parametrize("dtype", np.typecodes["AllFloat"]) + def test_where(self, dtype): + ar = np.arange(9).reshape(3, 3).astype(dtype) + ar[0, :] = np.nan + where = np.ones_like(ar, dtype=np.bool_) + where[:, 0] = False + + for f in self.nanfuncs: + reference = 26 if f is np.nansum else 2240 + ret = f(ar, where=where, initial=2) + assert ret.dtype == dtype + assert ret == reference + class TestNanFunctions_CumSumProd(SharedNanFunctionsTestsMixin): nanfuncs = [np.nancumsum, np.nancumprod] stdfuncs = [np.cumsum, np.cumprod] - def test_allnans(self): - for f, tgt_value in zip(self.nanfuncs, [0, 1]): - # Unlike other nan-functions, sum/prod/cumsum/cumprod don't warn on all nan input - with assert_no_warnings(): - res = f([np.nan]*3, axis=None) - tgt = tgt_value*np.ones((3)) - assert_(np.array_equal(res, tgt), 'result is not %s * np.ones((3))' % (tgt_value)) - # Check scalar - res = f(np.nan) - tgt = tgt_value*np.ones((1)) - assert_(np.array_equal(res, tgt), 'result is not %s * np.ones((1))' % (tgt_value)) - # Check there is no warning for not all-nan - f([0]*3, axis=None) + @pytest.mark.parametrize("axis", [None, 0, 1]) + @pytest.mark.parametrize("dtype", np.typecodes["AllFloat"]) + @pytest.mark.parametrize("array", [ + np.array(np.nan), + np.full((3, 3), np.nan) + ], ids=["0d", "2d"]) + def test_allnans(self, axis, dtype, array): + if axis is not None and array.ndim == 0: + pytest.skip(f"`axis != None` not supported for 0d arrays") + + array = array.astype(dtype) + for func, identity in zip(self.nanfuncs, [0, 1]): + out = func(array) + assert np.all(out == identity) + assert out.dtype == array.dtype def test_empty(self): for f, tgt_value in zip(self.nanfuncs, [0, 1]): @@ -558,19 +708,29 @@ def test_ddof_too_big(self): else: assert_(len(sup.log) == 0) - def test_allnans(self): - mat = np.array([np.nan]*9).reshape(3, 3) - for f in self.nanfuncs: - for axis in [None, 0, 1]: - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter('always') - assert_(np.isnan(f(mat, axis=axis)).all()) - assert_(len(w) == 1) - assert_(issubclass(w[0].category, RuntimeWarning)) - # Check scalar - assert_(np.isnan(f(np.nan))) - assert_(len(w) == 2) - assert_(issubclass(w[0].category, RuntimeWarning)) + @pytest.mark.parametrize("axis", [None, 0, 1]) + @pytest.mark.parametrize("dtype", np.typecodes["AllFloat"]) + @pytest.mark.parametrize("array", [ + np.array(np.nan), + np.full((3, 3), np.nan), + ], ids=["0d", "2d"]) + def test_allnans(self, axis, dtype, array): + if axis is not None and array.ndim == 0: + pytest.skip(f"`axis != None` not supported for 0d arrays") + + array = array.astype(dtype) + match = "(Degrees of freedom <= 0 for slice.)|(Mean of empty slice)" + for func in self.nanfuncs: + with pytest.warns(RuntimeWarning, match=match): + out = func(array, axis=axis) + assert np.isnan(out).all() + + # `nanvar` and `nanstd` convert complex inputs to their + # corresponding floating dtype + if func is np.nanmean: + assert out.dtype == array.dtype + else: + assert out.dtype == np.abs(array).dtype def test_empty(self): mat = np.zeros((0, 3)) @@ -587,8 +747,32 @@ def test_empty(self): assert_equal(f(mat, axis=axis), np.zeros([])) assert_(len(w) == 0) + @pytest.mark.parametrize("dtype", np.typecodes["AllFloat"]) + def test_where(self, dtype): + ar = np.arange(9).reshape(3, 3).astype(dtype) + ar[0, :] = np.nan + where = np.ones_like(ar, dtype=np.bool_) + where[:, 0] = False + + for f, f_std in zip(self.nanfuncs, self.stdfuncs): + reference = f_std(ar[where][2:]) + dtype_reference = dtype if f is np.nanmean else ar.real.dtype + + ret = f(ar, where=where) + assert ret.dtype == dtype_reference + np.testing.assert_allclose(ret, reference) -class TestNanFunctions_Median(object): + +_TIME_UNITS = ( + "Y", "M", "W", "D", "h", "m", "s", "ms", "us", "ns", "ps", "fs", "as" +) + +# All `inexact` + `timdelta64` type codes +_TYPE_CODES = list(np.typecodes["AllFloat"]) +_TYPE_CODES += [f"m8[{unit}]" for unit in _TIME_UNITS] + + +class TestNanFunctions_Median: def test_mutation(self): # Check that passed array is not modified. @@ -662,23 +846,32 @@ def test_result_values(self): res = np.nanmedian(_ndat, axis=1) assert_almost_equal(res, tgt) - def test_allnans(self): - mat = np.array([np.nan]*9).reshape(3, 3) - for axis in [None, 0, 1]: - with suppress_warnings() as sup: - sup.record(RuntimeWarning) + @pytest.mark.parametrize("axis", [None, 0, 1]) + @pytest.mark.parametrize("dtype", _TYPE_CODES) + def test_allnans(self, dtype, axis): + mat = np.full((3, 3), np.nan).astype(dtype) + with suppress_warnings() as sup: + sup.record(RuntimeWarning) - assert_(np.isnan(np.nanmedian(mat, axis=axis)).all()) - if axis is None: - assert_(len(sup.log) == 1) - else: - assert_(len(sup.log) == 3) - # Check scalar - assert_(np.isnan(np.nanmedian(np.nan))) - if axis is None: - assert_(len(sup.log) == 2) - else: - assert_(len(sup.log) == 4) + output = np.nanmedian(mat, axis=axis) + assert output.dtype == mat.dtype + assert np.isnan(output).all() + + if axis is None: + assert_(len(sup.log) == 1) + else: + assert_(len(sup.log) == 3) + + # Check scalar + scalar = np.array(np.nan).astype(dtype)[()] + output_scalar = np.nanmedian(scalar) + assert output_scalar.dtype == scalar.dtype + assert np.isnan(output_scalar) + + if axis is None: + assert_(len(sup.log) == 2) + else: + assert_(len(sup.log) == 4) def test_empty(self): mat = np.zeros((0, 3)) @@ -752,7 +945,7 @@ def test_float_special(self): ([np.nan] * i) + [-inf] * j) -class TestNanFunctions_Percentile(object): +class TestNanFunctions_Percentile: def test_mutation(self): # Check that passed array is not modified. @@ -816,24 +1009,21 @@ def test_result_values(self): res = np.nanpercentile(_ndat, (28, 98), axis=1) assert_almost_equal(res, tgt) - def test_allnans(self): - mat = np.array([np.nan]*9).reshape(3, 3) - for axis in [None, 0, 1]: - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter('always') - assert_(np.isnan(np.nanpercentile(mat, 60, axis=axis)).all()) - if axis is None: - assert_(len(w) == 1) - else: - assert_(len(w) == 3) - assert_(issubclass(w[0].category, RuntimeWarning)) - # Check scalar - assert_(np.isnan(np.nanpercentile(np.nan, 60))) - if axis is None: - assert_(len(w) == 2) - else: - assert_(len(w) == 4) - assert_(issubclass(w[0].category, RuntimeWarning)) + @pytest.mark.parametrize("axis", [None, 0, 1]) + @pytest.mark.parametrize("dtype", np.typecodes["AllFloat"]) + @pytest.mark.parametrize("array", [ + np.array(np.nan), + np.full((3, 3), np.nan), + ], ids=["0d", "2d"]) + def test_allnans(self, axis, dtype, array): + if axis is not None and array.ndim == 0: + pytest.skip(f"`axis != None` not supported for 0d arrays") + + array = array.astype(dtype) + with pytest.warns(RuntimeWarning, match="All-NaN slice encountered"): + out = np.nanpercentile(array, 60, axis=axis) + assert np.isnan(out).all() + assert out.dtype == array.dtype def test_empty(self): mat = np.zeros((0, 3)) @@ -891,7 +1081,7 @@ def test_multiple_percentiles(self): assert_equal(np.nanpercentile(megamat, perc, axis=(1, 2)).shape, (2, 3, 6)) -class TestNanFunctions_Quantile(object): +class TestNanFunctions_Quantile: # most of this is already tested by TestPercentile def test_regression(self): @@ -918,10 +1108,79 @@ def test_no_p_overwrite(self): # this is worth retesting, because quantile does not make a copy p0 = np.array([0, 0.75, 0.25, 0.5, 1.0]) p = p0.copy() - np.nanquantile(np.arange(100.), p, interpolation="midpoint") + np.nanquantile(np.arange(100.), p, method="midpoint") assert_array_equal(p, p0) p0 = p0.tolist() p = p.tolist() - np.nanquantile(np.arange(100.), p, interpolation="midpoint") + np.nanquantile(np.arange(100.), p, method="midpoint") assert_array_equal(p, p0) + + @pytest.mark.parametrize("axis", [None, 0, 1]) + @pytest.mark.parametrize("dtype", np.typecodes["AllFloat"]) + @pytest.mark.parametrize("array", [ + np.array(np.nan), + np.full((3, 3), np.nan), + ], ids=["0d", "2d"]) + def test_allnans(self, axis, dtype, array): + if axis is not None and array.ndim == 0: + pytest.skip(f"`axis != None` not supported for 0d arrays") + + array = array.astype(dtype) + with pytest.warns(RuntimeWarning, match="All-NaN slice encountered"): + out = np.nanquantile(array, 1, axis=axis) + assert np.isnan(out).all() + assert out.dtype == array.dtype + +@pytest.mark.parametrize("arr, expected", [ + # array of floats with some nans + (np.array([np.nan, 5.0, np.nan, np.inf]), + np.array([False, True, False, True])), + # int64 array that can't possibly have nans + (np.array([1, 5, 7, 9], dtype=np.int64), + True), + # bool array that can't possibly have nans + (np.array([False, True, False, True]), + True), + # 2-D complex array with nans + (np.array([[np.nan, 5.0], + [np.nan, np.inf]], dtype=np.complex64), + np.array([[False, True], + [False, True]])), + ]) +def test__nan_mask(arr, expected): + for out in [None, np.empty(arr.shape, dtype=np.bool_)]: + actual = _nan_mask(arr, out=out) + assert_equal(actual, expected) + # the above won't distinguish between True proper + # and an array of True values; we want True proper + # for types that can't possibly contain NaN + if type(expected) is not np.ndarray: + assert actual is True + + +def test__replace_nan(): + """ Test that _replace_nan returns the original array if there are no + NaNs, not a copy. + """ + for dtype in [np.bool_, np.int32, np.int64]: + arr = np.array([0, 1], dtype=dtype) + result, mask = _replace_nan(arr, 0) + assert mask is None + # do not make a copy if there are no nans + assert result is arr + + for dtype in [np.float32, np.float64]: + arr = np.array([0, 1], dtype=dtype) + result, mask = _replace_nan(arr, 2) + assert (mask == False).all() + # mask is not None, so we make a copy + assert result is not arr + assert_equal(result, arr) + + arr_nan = np.array([0, 1, np.nan], dtype=dtype) + result_nan, mask_nan = _replace_nan(arr_nan, 2) + assert_equal(mask_nan, np.array([False, False, True])) + assert result_nan is not arr_nan + assert_equal(result_nan, np.array([0, 1, 2])) + assert np.isnan(arr_nan[-1]) diff --git a/numpy/lib/tests/test_packbits.py b/numpy/lib/tests/test_packbits.py index fde5c37f2e6a..5b07f41c6260 100644 --- a/numpy/lib/tests/test_packbits.py +++ b/numpy/lib/tests/test_packbits.py @@ -1,8 +1,7 @@ -from __future__ import division, absolute_import, print_function - import numpy as np from numpy.testing import assert_array_equal, assert_equal, assert_raises - +import pytest +from itertools import chain def test_packbits(): # Copied from the docstring. @@ -50,8 +49,8 @@ def test_packbits_empty_with_axis(): assert_equal(b.dtype, np.uint8) assert_equal(b.shape, out_shape) - -def test_packbits_large(): +@pytest.mark.parametrize('bitorder', ('little', 'big')) +def test_packbits_large(bitorder): # test data large enough for 16 byte vectorization a = np.array([1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, @@ -71,7 +70,7 @@ def test_packbits_large(): a = a.repeat(3) for dtype in '?bBhHiIlLqQ': arr = np.array(a, dtype=dtype) - b = np.packbits(arr, axis=None) + b = np.packbits(arr, axis=None, bitorder=bitorder) assert_equal(b.dtype, np.uint8) r = [252, 127, 192, 3, 254, 7, 252, 0, 7, 31, 240, 0, 28, 1, 255, 252, 113, 248, 3, 255, 192, 28, 15, 192, 28, 126, 0, 224, 127, 255, @@ -81,9 +80,10 @@ def test_packbits_large(): 255, 224, 1, 255, 252, 126, 63, 0, 1, 192, 252, 14, 63, 0, 15, 199, 252, 113, 255, 3, 128, 56, 252, 14, 7, 0, 113, 255, 255, 142, 56, 227, 129, 248, 227, 129, 199, 31, 128] - assert_array_equal(b, r) + if bitorder == 'big': + assert_array_equal(b, r) # equal for size being multiple of 8 - assert_array_equal(np.unpackbits(b)[:-4], a) + assert_array_equal(np.unpackbits(b, bitorder=bitorder)[:-4], a) # check last byte of different remainders (16 byte vectorization) b = [np.packbits(arr[:-i], axis=None)[-1] for i in range(1, 16)] @@ -229,6 +229,20 @@ def test_unpackbits(): [0, 0, 0, 0, 0, 1, 1, 1], [0, 0, 0, 1, 0, 1, 1, 1]])) +def test_pack_unpack_order(): + a = np.array([[2], [7], [23]], dtype=np.uint8) + b = np.unpackbits(a, axis=1) + assert_equal(b.dtype, np.uint8) + b_little = np.unpackbits(a, axis=1, bitorder='little') + b_big = np.unpackbits(a, axis=1, bitorder='big') + assert_array_equal(b, b_big) + assert_array_equal(a, np.packbits(b_little, axis=1, bitorder='little')) + assert_array_equal(b[:,::-1], b_little) + assert_array_equal(a, np.packbits(b_big, axis=1, bitorder='big')) + assert_raises(ValueError, np.unpackbits, a, bitorder='r') + assert_raises(TypeError, np.unpackbits, a, bitorder=10) + + def test_unpackbits_empty(): a = np.empty((0,), dtype=np.uint8) @@ -266,3 +280,97 @@ def test_unpackbits_large(): assert_array_equal(np.packbits(np.unpackbits(d, axis=1), axis=1), d) d = d.T.copy() assert_array_equal(np.packbits(np.unpackbits(d, axis=0), axis=0), d) + + +class TestCount(): + x = np.array([ + [1, 0, 1, 0, 0, 1, 0], + [0, 1, 1, 1, 0, 0, 0], + [0, 0, 1, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 1, 1], + [1, 0, 1, 0, 1, 0, 1], + [0, 0, 1, 1, 1, 0, 0], + [0, 1, 0, 1, 0, 1, 0], + ], dtype=np.uint8) + padded1 = np.zeros(57, dtype=np.uint8) + padded1[:49] = x.ravel() + padded1b = np.zeros(57, dtype=np.uint8) + padded1b[:49] = x[::-1].copy().ravel() + padded2 = np.zeros((9, 9), dtype=np.uint8) + padded2[:7, :7] = x + + @pytest.mark.parametrize('bitorder', ('little', 'big')) + @pytest.mark.parametrize('count', chain(range(58), range(-1, -57, -1))) + def test_roundtrip(self, bitorder, count): + if count < 0: + # one extra zero of padding + cutoff = count - 1 + else: + cutoff = count + # test complete invertibility of packbits and unpackbits with count + packed = np.packbits(self.x, bitorder=bitorder) + unpacked = np.unpackbits(packed, count=count, bitorder=bitorder) + assert_equal(unpacked.dtype, np.uint8) + assert_array_equal(unpacked, self.padded1[:cutoff]) + + @pytest.mark.parametrize('kwargs', [ + {}, {'count': None}, + ]) + def test_count(self, kwargs): + packed = np.packbits(self.x) + unpacked = np.unpackbits(packed, **kwargs) + assert_equal(unpacked.dtype, np.uint8) + assert_array_equal(unpacked, self.padded1[:-1]) + + @pytest.mark.parametrize('bitorder', ('little', 'big')) + # delta==-1 when count<0 because one extra zero of padding + @pytest.mark.parametrize('count', chain(range(8), range(-1, -9, -1))) + def test_roundtrip_axis(self, bitorder, count): + if count < 0: + # one extra zero of padding + cutoff = count - 1 + else: + cutoff = count + packed0 = np.packbits(self.x, axis=0, bitorder=bitorder) + unpacked0 = np.unpackbits(packed0, axis=0, count=count, + bitorder=bitorder) + assert_equal(unpacked0.dtype, np.uint8) + assert_array_equal(unpacked0, self.padded2[:cutoff, :self.x.shape[1]]) + + packed1 = np.packbits(self.x, axis=1, bitorder=bitorder) + unpacked1 = np.unpackbits(packed1, axis=1, count=count, + bitorder=bitorder) + assert_equal(unpacked1.dtype, np.uint8) + assert_array_equal(unpacked1, self.padded2[:self.x.shape[0], :cutoff]) + + @pytest.mark.parametrize('kwargs', [ + {}, {'count': None}, + {'bitorder' : 'little'}, + {'bitorder': 'little', 'count': None}, + {'bitorder' : 'big'}, + {'bitorder': 'big', 'count': None}, + ]) + def test_axis_count(self, kwargs): + packed0 = np.packbits(self.x, axis=0) + unpacked0 = np.unpackbits(packed0, axis=0, **kwargs) + assert_equal(unpacked0.dtype, np.uint8) + if kwargs.get('bitorder', 'big') == 'big': + assert_array_equal(unpacked0, self.padded2[:-1, :self.x.shape[1]]) + else: + assert_array_equal(unpacked0[::-1, :], self.padded2[:-1, :self.x.shape[1]]) + + packed1 = np.packbits(self.x, axis=1) + unpacked1 = np.unpackbits(packed1, axis=1, **kwargs) + assert_equal(unpacked1.dtype, np.uint8) + if kwargs.get('bitorder', 'big') == 'big': + assert_array_equal(unpacked1, self.padded2[:self.x.shape[0], :-1]) + else: + assert_array_equal(unpacked1[:, ::-1], self.padded2[:self.x.shape[0], :-1]) + + def test_bad_count(self): + packed0 = np.packbits(self.x, axis=0) + assert_raises(ValueError, np.unpackbits, packed0, axis=0, count=-9) + packed1 = np.packbits(self.x, axis=1) + assert_raises(ValueError, np.unpackbits, packed1, axis=1, count=-9) + packed = np.packbits(self.x) + assert_raises(ValueError, np.unpackbits, packed, count=-57) diff --git a/numpy/lib/tests/test_polynomial.py b/numpy/lib/tests/test_polynomial.py index 7f6fca4a4502..3734344d2a85 100644 --- a/numpy/lib/tests/test_polynomial.py +++ b/numpy/lib/tests/test_polynomial.py @@ -1,93 +1,98 @@ -''' ->>> p = np.poly1d([1.,2,3]) ->>> p -poly1d([1., 2., 3.]) ->>> print(p) - 2 -1 x + 2 x + 3 ->>> q = np.poly1d([3.,2,1]) ->>> q -poly1d([3., 2., 1.]) ->>> print(q) - 2 -3 x + 2 x + 1 ->>> print(np.poly1d([1.89999+2j, -3j, -5.12345678, 2+1j])) - 3 2 -(1.9 + 2j) x - 3j x - 5.123 x + (2 + 1j) ->>> print(np.poly1d([-3, -2, -1])) - 2 --3 x - 2 x - 1 - ->>> p(0) -3.0 ->>> p(5) -38.0 ->>> q(0) -1.0 ->>> q(5) -86.0 - ->>> p * q -poly1d([ 3., 8., 14., 8., 3.]) ->>> p / q -(poly1d([0.33333333]), poly1d([1.33333333, 2.66666667])) ->>> p + q -poly1d([4., 4., 4.]) ->>> p - q -poly1d([-2., 0., 2.]) ->>> p ** 4 -poly1d([ 1., 8., 36., 104., 214., 312., 324., 216., 81.]) - ->>> p(q) -poly1d([ 9., 12., 16., 8., 6.]) ->>> q(p) -poly1d([ 3., 12., 32., 40., 34.]) - ->>> np.asarray(p) -array([1., 2., 3.]) ->>> len(p) -2 - ->>> p[0], p[1], p[2], p[3] -(3.0, 2.0, 1.0, 0) - ->>> p.integ() -poly1d([0.33333333, 1. , 3. , 0. ]) ->>> p.integ(1) -poly1d([0.33333333, 1. , 3. , 0. ]) ->>> p.integ(5) -poly1d([0.00039683, 0.00277778, 0.025 , 0. , 0. , - 0. , 0. , 0. ]) ->>> p.deriv() -poly1d([2., 2.]) ->>> p.deriv(2) -poly1d([2.]) - ->>> q = np.poly1d([1.,2,3], variable='y') ->>> print(q) - 2 -1 y + 2 y + 3 ->>> q = np.poly1d([1.,2,3], variable='lambda') ->>> print(q) - 2 -1 lambda + 2 lambda + 3 - ->>> np.polydiv(np.poly1d([1,0,-1]), np.poly1d([1,1])) -(poly1d([ 1., -1.]), poly1d([0.])) - -''' -from __future__ import division, absolute_import, print_function - import numpy as np from numpy.testing import ( assert_, assert_equal, assert_array_equal, assert_almost_equal, - assert_array_almost_equal, assert_raises, rundocs + assert_array_almost_equal, assert_raises, assert_allclose ) +import pytest + +# `poly1d` has some support for `bool_` and `timedelta64`, +# but it is limited and they are therefore excluded here +TYPE_CODES = np.typecodes["AllInteger"] + np.typecodes["AllFloat"] + "O" -class TestDocs(object): - def test_doctests(self): - return rundocs() + +class TestPolynomial: + def test_poly1d_str_and_repr(self): + p = np.poly1d([1., 2, 3]) + assert_equal(repr(p), 'poly1d([1., 2., 3.])') + assert_equal(str(p), + ' 2\n' + '1 x + 2 x + 3') + + q = np.poly1d([3., 2, 1]) + assert_equal(repr(q), 'poly1d([3., 2., 1.])') + assert_equal(str(q), + ' 2\n' + '3 x + 2 x + 1') + + r = np.poly1d([1.89999 + 2j, -3j, -5.12345678, 2 + 1j]) + assert_equal(str(r), + ' 3 2\n' + '(1.9 + 2j) x - 3j x - 5.123 x + (2 + 1j)') + + assert_equal(str(np.poly1d([-3, -2, -1])), + ' 2\n' + '-3 x - 2 x - 1') + + def test_poly1d_resolution(self): + p = np.poly1d([1., 2, 3]) + q = np.poly1d([3., 2, 1]) + assert_equal(p(0), 3.0) + assert_equal(p(5), 38.0) + assert_equal(q(0), 1.0) + assert_equal(q(5), 86.0) + + def test_poly1d_math(self): + # here we use some simple coeffs to make calculations easier + p = np.poly1d([1., 2, 4]) + q = np.poly1d([4., 2, 1]) + assert_equal(p/q, (np.poly1d([0.25]), np.poly1d([1.5, 3.75]))) + assert_equal(p.integ(), np.poly1d([1/3, 1., 4., 0.])) + assert_equal(p.integ(1), np.poly1d([1/3, 1., 4., 0.])) + + p = np.poly1d([1., 2, 3]) + q = np.poly1d([3., 2, 1]) + assert_equal(p * q, np.poly1d([3., 8., 14., 8., 3.])) + assert_equal(p + q, np.poly1d([4., 4., 4.])) + assert_equal(p - q, np.poly1d([-2., 0., 2.])) + assert_equal(p ** 4, np.poly1d([1., 8., 36., 104., 214., 312., 324., 216., 81.])) + assert_equal(p(q), np.poly1d([9., 12., 16., 8., 6.])) + assert_equal(q(p), np.poly1d([3., 12., 32., 40., 34.])) + assert_equal(p.deriv(), np.poly1d([2., 2.])) + assert_equal(p.deriv(2), np.poly1d([2.])) + assert_equal(np.polydiv(np.poly1d([1, 0, -1]), np.poly1d([1, 1])), + (np.poly1d([1., -1.]), np.poly1d([0.]))) + + @pytest.mark.parametrize("type_code", TYPE_CODES) + def test_poly1d_misc(self, type_code: str) -> None: + dtype = np.dtype(type_code) + ar = np.array([1, 2, 3], dtype=dtype) + p = np.poly1d(ar) + + # `__eq__` + assert_equal(np.asarray(p), ar) + assert_equal(np.asarray(p).dtype, dtype) + assert_equal(len(p), 2) + + # `__getitem__` + comparison_dct = {-1: 0, 0: 3, 1: 2, 2: 1, 3: 0} + for index, ref in comparison_dct.items(): + scalar = p[index] + assert_equal(scalar, ref) + if dtype == np.object_: + assert isinstance(scalar, int) + else: + assert_equal(scalar.dtype, dtype) + + def test_poly1d_variable_arg(self): + q = np.poly1d([1., 2, 3], variable='y') + assert_equal(str(q), + ' 2\n' + '1 y + 2 y + 3') + q = np.poly1d([1., 2, 3], variable='lambda') + assert_equal(str(q), + ' 2\n' + '1 lambda + 2 lambda + 3') def test_poly(self): assert_array_almost_equal(np.poly([3, -np.sqrt(2), np.sqrt(2)]), @@ -136,27 +141,34 @@ def test_polyfit(self): weights = np.arange(8, 1, -1)**2/7.0 # Check exception when too few points for variance estimate. Note that - # the Bayesian estimate requires the number of data points to exceed - # degree + 3. + # the estimate requires the number of data points to exceed + # degree + 1 assert_raises(ValueError, np.polyfit, - [0, 1, 3], [0, 1, 3], deg=0, cov=True) + [1], [1], deg=0, cov=True) # check 1D case m, cov = np.polyfit(x, y+err, 2, cov=True) est = [3.8571, 0.2857, 1.619] assert_almost_equal(est, m, decimal=4) - val0 = [[2.9388, -5.8776, 1.6327], - [-5.8776, 12.7347, -4.2449], - [1.6327, -4.2449, 2.3220]] + val0 = [[ 1.4694, -2.9388, 0.8163], + [-2.9388, 6.3673, -2.1224], + [ 0.8163, -2.1224, 1.161 ]] assert_almost_equal(val0, cov, decimal=4) m2, cov2 = np.polyfit(x, y+err, 2, w=weights, cov=True) assert_almost_equal([4.8927, -1.0177, 1.7768], m2, decimal=4) - val = [[8.7929, -10.0103, 0.9756], - [-10.0103, 13.6134, -1.8178], - [0.9756, -1.8178, 0.6674]] + val = [[ 4.3964, -5.0052, 0.4878], + [-5.0052, 6.8067, -0.9089], + [ 0.4878, -0.9089, 0.3337]] assert_almost_equal(val, cov2, decimal=4) + m3, cov3 = np.polyfit(x, y+err, 2, w=weights, cov="unscaled") + assert_almost_equal([4.8927, -1.0177, 1.7768], m3, decimal=4) + val = [[ 0.1473, -0.1677, 0.0163], + [-0.1677, 0.228 , -0.0304], + [ 0.0163, -0.0304, 0.0112]] + assert_almost_equal(val, cov3, decimal=4) + # check 2D (n,1) case y = y[:, np.newaxis] c = c[:, np.newaxis] @@ -172,6 +184,29 @@ def test_polyfit(self): assert_almost_equal(val0, cov[:, :, 0], decimal=4) assert_almost_equal(val0, cov[:, :, 1], decimal=4) + # check order 1 (deg=0) case, were the analytic results are simple + np.random.seed(123) + y = np.random.normal(size=(4, 10000)) + mean, cov = np.polyfit(np.zeros(y.shape[0]), y, deg=0, cov=True) + # Should get sigma_mean = sigma/sqrt(N) = 1./sqrt(4) = 0.5. + assert_allclose(mean.std(), 0.5, atol=0.01) + assert_allclose(np.sqrt(cov.mean()), 0.5, atol=0.01) + # Without scaling, since reduced chi2 is 1, the result should be the same. + mean, cov = np.polyfit(np.zeros(y.shape[0]), y, w=np.ones(y.shape[0]), + deg=0, cov="unscaled") + assert_allclose(mean.std(), 0.5, atol=0.01) + assert_almost_equal(np.sqrt(cov.mean()), 0.5) + # If we estimate our errors wrong, no change with scaling: + w = np.full(y.shape[0], 1./0.5) + mean, cov = np.polyfit(np.zeros(y.shape[0]), y, w=w, deg=0, cov=True) + assert_allclose(mean.std(), 0.5, atol=0.01) + assert_allclose(np.sqrt(cov.mean()), 0.5, atol=0.01) + # But if we do not scale, our estimate for the error in the mean will + # differ. + mean, cov = np.polyfit(np.zeros(y.shape[0]), y, w=w, deg=0, cov="unscaled") + assert_allclose(mean.std(), 0.5, atol=0.01) + assert_almost_equal(np.sqrt(cov.mean()), 0.25) + def test_objects(self): from decimal import Decimal p = np.poly1d([Decimal('4.0'), Decimal('3.0'), Decimal('2.0')]) @@ -213,6 +248,20 @@ def test_poly_int_overflow(self): v = np.arange(1, 21) assert_almost_equal(np.poly(v), np.poly(np.diag(v))) + def test_zero_poly_dtype(self): + """ + Regression test for gh-16354. + """ + z = np.array([0, 0, 0]) + p = np.poly1d(z.astype(np.int64)) + assert_equal(p.coeffs.dtype, np.int64) + + p = np.poly1d(z.astype(np.float32)) + assert_equal(p.coeffs.dtype, np.float32) + + p = np.poly1d(z.astype(np.complex64)) + assert_equal(p.coeffs.dtype, np.complex64) + def test_poly_eq(self): p = np.poly1d([1, 2, 3]) p2 = np.poly1d([1, 2, 4]) @@ -230,16 +279,25 @@ def test_polydiv(self): assert_equal(r.coeffs.dtype, np.complex128) assert_equal(q*a + r, b) - def test_poly_coeffs_immutable(self): - """ Coefficients should not be modifiable """ + c = [1, 2, 3] + d = np.poly1d([1, 2, 3]) + s, t = np.polydiv(c, d) + assert isinstance(s, np.poly1d) + assert isinstance(t, np.poly1d) + u, v = np.polydiv(d, c) + assert isinstance(u, np.poly1d) + assert isinstance(v, np.poly1d) + + def test_poly_coeffs_mutable(self): + """ Coefficients should be modifiable """ p = np.poly1d([1, 2, 3]) - try: - # despite throwing an exception, this used to change state - p.coeffs += 1 - except Exception: - pass - assert_equal(p.coeffs, [1, 2, 3]) + p.coeffs += 1 + assert_equal(p.coeffs, [2, 3, 4]) p.coeffs[2] += 10 - assert_equal(p.coeffs, [1, 2, 3]) + assert_equal(p.coeffs, [2, 3, 14]) + + # this never used to be allowed - let's not add features to deprecated + # APIs + assert_raises(AttributeError, setattr, p, 'coeffs', np.array(1)) diff --git a/numpy/lib/tests/test_recfunctions.py b/numpy/lib/tests/test_recfunctions.py index d4828bc1f82d..2f3c14df31f0 100644 --- a/numpy/lib/tests/test_recfunctions.py +++ b/numpy/lib/tests/test_recfunctions.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - import pytest import numpy as np @@ -10,13 +8,16 @@ from numpy.lib.recfunctions import ( drop_fields, rename_fields, get_fieldstructure, recursive_fill_fields, find_duplicates, merge_arrays, append_fields, stack_arrays, join_by, - repack_fields) + repack_fields, unstructured_to_structured, structured_to_unstructured, + apply_along_fields, require_fields, assign_fields_by_name) +get_fieldspec = np.lib.recfunctions._get_fieldspec get_names = np.lib.recfunctions.get_names get_names_flat = np.lib.recfunctions.get_names_flat -zip_descr = np.lib.recfunctions.zip_descr +zip_descr = np.lib.recfunctions._zip_descr +zip_dtype = np.lib.recfunctions._zip_dtype -class TestRecFunctions(object): +class TestRecFunctions: # Misc tests def setup(self): @@ -88,8 +89,10 @@ def test_drop_fields(self): control = np.array([(1,), (4,)], dtype=[('a', int)]) assert_equal(test, control) + # dropping all fields results in an array with no fields test = drop_fields(a, ['a', 'b']) - assert_(test is None) + control = np.array([(), ()], dtype=[]) + assert_equal(test, control) def test_rename_fields(self): # Test rename fields @@ -112,6 +115,14 @@ def test_get_names(self): test = get_names(ndtype) assert_equal(test, ('a', ('b', ('ba', 'bb')))) + ndtype = np.dtype([('a', int), ('b', [])]) + test = get_names(ndtype) + assert_equal(test, ('a', ('b', ()))) + + ndtype = np.dtype([]) + test = get_names(ndtype) + assert_equal(test, ()) + def test_get_names_flat(self): # Test get_names_flat ndtype = np.dtype([('A', '|S3'), ('B', float)]) @@ -122,6 +133,14 @@ def test_get_names_flat(self): test = get_names_flat(ndtype) assert_equal(test, ('a', 'b', 'ba', 'bb')) + ndtype = np.dtype([('a', int), ('b', [])]) + test = get_names_flat(ndtype) + assert_equal(test, ('a', 'b')) + + ndtype = np.dtype([]) + test = get_names_flat(ndtype) + assert_equal(test, ()) + def test_get_fieldstructure(self): # Test get_fieldstructure @@ -144,6 +163,11 @@ def test_get_fieldstructure(self): 'BBA': ['B', 'BB'], 'BBB': ['B', 'BB']} assert_equal(test, control) + # 0 fields + ndtype = np.dtype([]) + test = get_fieldstructure(ndtype) + assert_equal(test, {}) + def test_find_duplicates(self): # Test find_duplicates a = ma.array([(2, (2., 'B')), (1, (2., 'B')), (2, (2., 'B')), @@ -204,8 +228,125 @@ def test_repack_fields(self): dt = np.dtype((np.record, dt)) assert_(repack_fields(dt).type is np.record) - -class TestRecursiveFillFields(object): + def test_structured_to_unstructured(self): + a = np.zeros(4, dtype=[('a', 'i4'), ('b', 'f4,u2'), ('c', 'f4', 2)]) + out = structured_to_unstructured(a) + assert_equal(out, np.zeros((4,5), dtype='f8')) + + b = np.array([(1, 2, 5), (4, 5, 7), (7, 8 ,11), (10, 11, 12)], + dtype=[('x', 'i4'), ('y', 'f4'), ('z', 'f8')]) + out = np.mean(structured_to_unstructured(b[['x', 'z']]), axis=-1) + assert_equal(out, np.array([ 3. , 5.5, 9. , 11. ])) + out = np.mean(structured_to_unstructured(b[['x']]), axis=-1) + assert_equal(out, np.array([ 1. , 4. , 7. , 10. ])) + + c = np.arange(20).reshape((4,5)) + out = unstructured_to_structured(c, a.dtype) + want = np.array([( 0, ( 1., 2), [ 3., 4.]), + ( 5, ( 6., 7), [ 8., 9.]), + (10, (11., 12), [13., 14.]), + (15, (16., 17), [18., 19.])], + dtype=[('a', 'i4'), + ('b', [('f0', 'f4'), ('f1', 'u2')]), + ('c', 'f4', (2,))]) + assert_equal(out, want) + + d = np.array([(1, 2, 5), (4, 5, 7), (7, 8 ,11), (10, 11, 12)], + dtype=[('x', 'i4'), ('y', 'f4'), ('z', 'f8')]) + assert_equal(apply_along_fields(np.mean, d), + np.array([ 8.0/3, 16.0/3, 26.0/3, 11. ])) + assert_equal(apply_along_fields(np.mean, d[['x', 'z']]), + np.array([ 3. , 5.5, 9. , 11. ])) + + # check that for uniform field dtypes we get a view, not a copy: + d = np.array([(1, 2, 5), (4, 5, 7), (7, 8 ,11), (10, 11, 12)], + dtype=[('x', 'i4'), ('y', 'i4'), ('z', 'i4')]) + dd = structured_to_unstructured(d) + ddd = unstructured_to_structured(dd, d.dtype) + assert_(dd.base is d) + assert_(ddd.base is d) + + # including uniform fields with subarrays unpacked + d = np.array([(1, [2, 3], [[ 4, 5], [ 6, 7]]), + (8, [9, 10], [[11, 12], [13, 14]])], + dtype=[('x0', 'i4'), ('x1', ('i4', 2)), + ('x2', ('i4', (2, 2)))]) + dd = structured_to_unstructured(d) + ddd = unstructured_to_structured(dd, d.dtype) + assert_(dd.base is d) + assert_(ddd.base is d) + + # test that nested fields with identical names don't break anything + point = np.dtype([('x', int), ('y', int)]) + triangle = np.dtype([('a', point), ('b', point), ('c', point)]) + arr = np.zeros(10, triangle) + res = structured_to_unstructured(arr, dtype=int) + assert_equal(res, np.zeros((10, 6), dtype=int)) + + + # test nested combinations of subarrays and structured arrays, gh-13333 + def subarray(dt, shape): + return np.dtype((dt, shape)) + + def structured(*dts): + return np.dtype([('x{}'.format(i), dt) for i, dt in enumerate(dts)]) + + def inspect(dt, dtype=None): + arr = np.zeros((), dt) + ret = structured_to_unstructured(arr, dtype=dtype) + backarr = unstructured_to_structured(ret, dt) + return ret.shape, ret.dtype, backarr.dtype + + dt = structured(subarray(structured(np.int32, np.int32), 3)) + assert_equal(inspect(dt), ((6,), np.int32, dt)) + + dt = structured(subarray(subarray(np.int32, 2), 2)) + assert_equal(inspect(dt), ((4,), np.int32, dt)) + + dt = structured(np.int32) + assert_equal(inspect(dt), ((1,), np.int32, dt)) + + dt = structured(np.int32, subarray(subarray(np.int32, 2), 2)) + assert_equal(inspect(dt), ((5,), np.int32, dt)) + + dt = structured() + assert_raises(ValueError, structured_to_unstructured, np.zeros(3, dt)) + + # these currently don't work, but we may make it work in the future + assert_raises(NotImplementedError, structured_to_unstructured, + np.zeros(3, dt), dtype=np.int32) + assert_raises(NotImplementedError, unstructured_to_structured, + np.zeros((3,0), dtype=np.int32)) + + def test_field_assignment_by_name(self): + a = np.ones(2, dtype=[('a', 'i4'), ('b', 'f8'), ('c', 'u1')]) + newdt = [('b', 'f4'), ('c', 'u1')] + + assert_equal(require_fields(a, newdt), np.ones(2, newdt)) + + b = np.array([(1,2), (3,4)], dtype=newdt) + assign_fields_by_name(a, b, zero_unassigned=False) + assert_equal(a, np.array([(1,1,2),(1,3,4)], dtype=a.dtype)) + assign_fields_by_name(a, b) + assert_equal(a, np.array([(0,1,2),(0,3,4)], dtype=a.dtype)) + + # test nested fields + a = np.ones(2, dtype=[('a', [('b', 'f8'), ('c', 'u1')])]) + newdt = [('a', [('c', 'u1')])] + assert_equal(require_fields(a, newdt), np.ones(2, newdt)) + b = np.array([((2,),), ((3,),)], dtype=newdt) + assign_fields_by_name(a, b, zero_unassigned=False) + assert_equal(a, np.array([((1,2),), ((1,3),)], dtype=a.dtype)) + assign_fields_by_name(a, b) + assert_equal(a, np.array([((0,2),), ((0,3),)], dtype=a.dtype)) + + # test unstructured code path for 0d arrays + a, b = np.array(3), np.array(0) + assign_fields_by_name(b, a) + assert_equal(b[()], 3) + + +class TestRecursiveFillFields: # Test recursive_fill_fields. def test_simple_flexible(self): # Test recursive_fill_fields on flexible-array @@ -228,7 +369,7 @@ def test_masked_flexible(self): assert_equal(test, control) -class TestMergeArrays(object): +class TestMergeArrays: # Test merge_arrays def setup(self): @@ -237,8 +378,8 @@ def setup(self): z = np.array( [('A', 1.), ('B', 2.)], dtype=[('A', '|S3'), ('B', float)]) w = np.array( - [(1, (2, 3.0)), (4, (5, 6.0))], - dtype=[('a', int), ('b', [('ba', float), ('bb', int)])]) + [(1, (2, 3.0, ())), (4, (5, 6.0, ()))], + dtype=[('a', int), ('b', [('ba', float), ('bb', int), ('bc', [])])]) self.data = (w, x, y, z) def test_solo(self): @@ -309,8 +450,8 @@ def test_flatten_wflexible(self): test = merge_arrays((x, w), flatten=False) controldtype = [('f0', int), ('f1', [('a', int), - ('b', [('ba', float), ('bb', int)])])] - control = np.array([(1., (1, (2, 3.0))), (2, (4, (5, 6.0)))], + ('b', [('ba', float), ('bb', int), ('bc', [])])])] + control = np.array([(1., (1, (2, 3.0, ()))), (2, (4, (5, 6.0, ())))], dtype=controldtype) assert_equal(test, control) @@ -361,7 +502,7 @@ def test_singlerecord(self): assert_equal(test, control) -class TestAppendFields(object): +class TestAppendFields: # Test append_fields def setup(self): @@ -415,7 +556,7 @@ def test_append_on_nested(self): assert_equal(test, control) -class TestStackArrays(object): +class TestStackArrays: # Test stack_arrays def setup(self): x = np.array([1, 2, ]) @@ -541,12 +682,8 @@ def test_autoconversion(self): test = stack_arrays((a, b), autoconvert=True) assert_equal(test, control) assert_equal(test.mask, control.mask) - try: - test = stack_arrays((a, b), autoconvert=False) - except TypeError: - pass - else: - raise AssertionError + with assert_raises(TypeError): + stack_arrays((a, b), autoconvert=False) def test_checktitles(self): # Test using titles in the field names @@ -590,7 +727,7 @@ def test_subdtype(self): assert_equal(res.mask, expected.mask) -class TestJoinBy(object): +class TestJoinBy: def setup(self): self.a = np.array(list(zip(np.arange(10), np.arange(50, 60), np.arange(100, 110))), @@ -633,7 +770,6 @@ def test_join(self): def test_join_subdtype(self): # tests the bug in https://stackoverflow.com/q/44769632/102441 - from numpy.lib import recfunctions as rfn foo = np.array([(1,)], dtype=[('key', int)]) bar = np.array([(1, np.array([1,2,3]))], @@ -756,7 +892,7 @@ def test_padded_dtype(self): assert_equal(res.dtype, expected_dtype) -class TestJoinBy2(object): +class TestJoinBy2: @classmethod def setup(cls): cls.a = np.array(list(zip(np.arange(10), np.arange(50, 60), @@ -821,7 +957,7 @@ def test_two_keys_two_vars(self): assert_equal(test.dtype, control.dtype) assert_equal(test, control) -class TestAppendFieldsObj(object): +class TestAppendFieldsObj: """ Test append_fields with arrays containing objects """ diff --git a/numpy/lib/tests/test_regression.py b/numpy/lib/tests/test_regression.py index 4c46bc46b5ac..55df2a6752c2 100644 --- a/numpy/lib/tests/test_regression.py +++ b/numpy/lib/tests/test_regression.py @@ -1,17 +1,13 @@ -from __future__ import division, absolute_import, print_function - import os -import sys import numpy as np from numpy.testing import ( assert_, assert_equal, assert_array_equal, assert_array_almost_equal, assert_raises, _assert_valid_refcount, ) -from numpy.compat import unicode -class TestRegression(object): +class TestRegression: def test_poly1d(self): # Ticket #28 assert_equal(np.poly1d([1]) - np.poly1d([1, 0]), @@ -21,8 +17,8 @@ def test_cov_parameters(self): # Ticket #91 x = np.random.random((3, 3)) y = x.copy() - np.cov(x, rowvar=1) - np.cov(y, rowvar=0) + np.cov(x, rowvar=True) + np.cov(y, rowvar=False) assert_array_equal(x, y) def test_mem_digitize(self): @@ -56,7 +52,7 @@ def p(x, y): def test_poly1d_nan_roots(self): # Ticket #396 - p = np.poly1d([np.nan, np.nan, 1], r=0) + p = np.poly1d([np.nan, np.nan, 1], r=False) assert_raises(np.linalg.LinAlgError, getattr, p, "r") def test_mem_polymul(self): @@ -183,7 +179,7 @@ def test_include_dirs(self): # related to ticket #1405. include_dirs = [np.get_include()] for path in include_dirs: - assert_(isinstance(path, (str, unicode))) + assert_(isinstance(path, str)) assert_(path != '') def test_polyder_return_type(self): @@ -208,10 +204,7 @@ def test_append_fields_dtype_list(self): def test_loadtxt_fields_subarrays(self): # For ticket #1936 - if sys.version_info[0] >= 3: - from io import StringIO - else: - from StringIO import StringIO + from io import StringIO dt = [("a", 'u1', 2), ("b", 'u1', 2)] x = np.loadtxt(StringIO("0 1 2 3"), dtype=dt) diff --git a/numpy/lib/tests/test_shape_base.py b/numpy/lib/tests/test_shape_base.py index c95894f94ce6..a148e53da68a 100644 --- a/numpy/lib/tests/test_shape_base.py +++ b/numpy/lib/tests/test_shape_base.py @@ -1,8 +1,7 @@ -from __future__ import division, absolute_import, print_function - import numpy as np -import warnings import functools +import sys +import pytest from numpy.lib.shape_base import ( apply_along_axis, apply_over_axes, array_split, split, hsplit, dsplit, @@ -14,6 +13,9 @@ ) +IS_64BIT = sys.maxsize > 2**32 + + def _add_keepdims(func): """ hack in keepdims behavior into a function taking an axis """ @functools.wraps(func) @@ -25,7 +27,7 @@ def wrapped(a, axis, **kwargs): return wrapped -class TestTakeAlongAxis(object): +class TestTakeAlongAxis: def test_argequivalent(self): """ Test it translates from arg<func> to <func> """ from numpy.random import rand @@ -77,7 +79,7 @@ def test_broadcast(self): assert_equal(actual.shape, (3, 2, 5)) -class TestPutAlongAxis(object): +class TestPutAlongAxis: def test_replace_max(self): a_base = np.array([[10, 30, 20], [60, 40, 50]]) @@ -102,7 +104,7 @@ def test_broadcast(self): assert_equal(take_along_axis(a, ai, axis=1), 20) -class TestApplyAlongAxis(object): +class TestApplyAlongAxis: def test_simple(self): a = np.ones((20, 10), 'd') assert_array_equal( @@ -255,8 +257,8 @@ def empty_to_1(x): def test_with_iterable_object(self): # from issue 5248 d = np.array([ - [set([1, 11]), set([2, 22]), set([3, 33])], - [set([4, 44]), set([5, 55]), set([6, 66])] + [{1, 11}, {2, 22}, {3, 33}], + [{4, 44}, {5, 55}, {6, 66}] ]) actual = np.apply_along_axis(lambda a: set.union(*a), 0, d) expected = np.array([{1, 11, 4, 44}, {2, 22, 5, 55}, {3, 33, 6, 66}]) @@ -268,14 +270,14 @@ def test_with_iterable_object(self): assert_equal(type(actual[i]), type(expected[i])) -class TestApplyOverAxes(object): +class TestApplyOverAxes: def test_simple(self): a = np.arange(24).reshape(2, 3, 4) aoa_a = apply_over_axes(np.sum, a, [0, 2]) assert_array_equal(aoa_a, np.array([[[60], [92], [124]]])) -class TestExpandDims(object): +class TestExpandDims: def test_functionality(self): s = (2, 3, 4, 5) a = np.empty(s) @@ -284,17 +286,38 @@ def test_functionality(self): assert_(b.shape[axis] == 1) assert_(np.squeeze(b).shape == s) - def test_deprecations(self): - # 2017-05-17, 1.13.0 + def test_axis_tuple(self): + a = np.empty((3, 3, 3)) + assert np.expand_dims(a, axis=(0, 1, 2)).shape == (1, 1, 1, 3, 3, 3) + assert np.expand_dims(a, axis=(0, -1, -2)).shape == (1, 3, 3, 3, 1, 1) + assert np.expand_dims(a, axis=(0, 3, 5)).shape == (1, 3, 3, 1, 3, 1) + assert np.expand_dims(a, axis=(0, -3, -5)).shape == (1, 1, 3, 1, 3, 3) + + def test_axis_out_of_range(self): s = (2, 3, 4, 5) a = np.empty(s) - with warnings.catch_warnings(): - warnings.simplefilter("always") - assert_warns(DeprecationWarning, expand_dims, a, -6) - assert_warns(DeprecationWarning, expand_dims, a, 5) + assert_raises(np.AxisError, expand_dims, a, -6) + assert_raises(np.AxisError, expand_dims, a, 5) + + a = np.empty((3, 3, 3)) + assert_raises(np.AxisError, expand_dims, a, (0, -6)) + assert_raises(np.AxisError, expand_dims, a, (0, 5)) + def test_repeated_axis(self): + a = np.empty((3, 3, 3)) + assert_raises(ValueError, expand_dims, a, axis=(1, 1)) -class TestArraySplit(object): + def test_subclasses(self): + a = np.arange(10).reshape((2, 5)) + a = np.ma.array(a, mask=a%3 == 0) + + expanded = np.expand_dims(a, axis=1) + assert_(isinstance(expanded, np.ma.MaskedArray)) + assert_equal(expanded.shape, (2, 1, 5)) + assert_equal(expanded.mask.shape, (2, 1, 5)) + + +class TestArraySplit: def test_integer_0_split(self): a = np.arange(10) assert_raises(ValueError, array_split, a, 0) @@ -369,7 +392,7 @@ def test_integer_split_2D_rows(self): assert_(a.dtype.type is res[-1].dtype.type) # Same thing for manual splits: - res = array_split(a, [0, 1, 2], axis=0) + res = array_split(a, [0, 1], axis=0) tgt = [np.zeros((0, 10)), np.array([np.arange(10)]), np.array([np.arange(10)])] compare_results(res, tgt) @@ -394,6 +417,15 @@ def test_integer_split_2D_default(self): assert_(a.dtype.type is res[-1].dtype.type) # perhaps should check higher dimensions + @pytest.mark.skipif(not IS_64BIT, reason="Needs 64bit platform") + def test_integer_split_2D_rows_greater_max_int32(self): + a = np.broadcast_to([0], (1 << 32, 2)) + res = array_split(a, 4) + chunk = np.broadcast_to([0], (1 << 30, 2)) + tgt = [chunk] * 4 + for i in range(len(tgt)): + assert_equal(res[i].shape, tgt[i].shape) + def test_index_split_simple(self): a = np.arange(10) indices = [1, 5, 7] @@ -419,7 +451,7 @@ def test_index_split_high_bound(self): compare_results(res, desired) -class TestSplit(object): +class TestSplit: # The split function is essentially the same as array_split, # except that it test if splitting will result in an # equal split. Only test for this case. @@ -434,12 +466,37 @@ def test_unequal_split(self): a = np.arange(10) assert_raises(ValueError, split, a, 3) -class TestColumnStack(object): + +class TestColumnStack: def test_non_iterable(self): assert_raises(TypeError, column_stack, 1) + def test_1D_arrays(self): + # example from docstring + a = np.array((1, 2, 3)) + b = np.array((2, 3, 4)) + expected = np.array([[1, 2], + [2, 3], + [3, 4]]) + actual = np.column_stack((a, b)) + assert_equal(actual, expected) + + def test_2D_arrays(self): + # same as hstack 2D docstring example + a = np.array([[1], [2], [3]]) + b = np.array([[2], [3], [4]]) + expected = np.array([[1, 2], + [2, 3], + [3, 4]]) + actual = np.column_stack((a, b)) + assert_equal(actual, expected) + + def test_generator(self): + with assert_warns(FutureWarning): + column_stack((np.arange(3) for _ in range(2))) -class TestDstack(object): + +class TestDstack: def test_non_iterable(self): assert_raises(TypeError, dstack, 1) @@ -471,10 +528,14 @@ def test_2D_array2(self): desired = np.array([[[1, 1], [2, 2]]]) assert_array_equal(res, desired) + def test_generator(self): + with assert_warns(FutureWarning): + dstack((np.arange(3) for _ in range(2))) + # array_split has more comprehensive test of splitting. # only do simple test on hsplit, vsplit, and dsplit -class TestHsplit(object): +class TestHsplit: """Only testing for integer splits. """ @@ -503,7 +564,7 @@ def test_2D_array(self): compare_results(res, desired) -class TestVsplit(object): +class TestVsplit: """Only testing for integer splits. """ @@ -530,7 +591,7 @@ def test_2D_array(self): compare_results(res, desired) -class TestDsplit(object): +class TestDsplit: # Only testing for integer splits. def test_non_iterable(self): assert_raises(ValueError, dsplit, 1, 1) @@ -563,7 +624,7 @@ def test_3D_array(self): compare_results(res, desired) -class TestSqueeze(object): +class TestSqueeze: def test_basic(self): from numpy.random import rand @@ -582,7 +643,7 @@ def test_basic(self): assert_equal(type(res), np.ndarray) -class TestKron(object): +class TestKron: def test_return_type(self): class myarray(np.ndarray): __array_priority__ = 0.0 @@ -595,7 +656,7 @@ class myarray(np.ndarray): assert_equal(type(kron(ma, a)), myarray) -class TestTile(object): +class TestTile: def test_basic(self): a = np.array([0, 1, 2]) b = [[1, 2], [3, 4]] @@ -635,7 +696,7 @@ def test_kroncompare(self): assert_equal(large, klarge) -class TestMayShareMemory(object): +class TestMayShareMemory: def test_basic(self): d = np.ones((50, 60)) d2 = np.ones((30, 60, 6)) @@ -652,5 +713,9 @@ def test_basic(self): # Utility def compare_results(res, desired): - for i in range(len(desired)): - assert_array_equal(res[i], desired[i]) + """Compare lists of arrays.""" + if len(res) != len(desired): + raise ValueError("Iterables have different lengths") + # See also PEP 618 for Python 3.10 + for x, y in zip(res, desired): + assert_array_equal(x, y) diff --git a/numpy/lib/tests/test_stride_tricks.py b/numpy/lib/tests/test_stride_tricks.py index 3c2ca8b8793f..efec5d24dad4 100644 --- a/numpy/lib/tests/test_stride_tricks.py +++ b/numpy/lib/tests/test_stride_tricks.py @@ -1,13 +1,15 @@ -from __future__ import division, absolute_import, print_function - import numpy as np from numpy.core._rational_tests import rational from numpy.testing import ( - assert_equal, assert_array_equal, assert_raises, assert_ + assert_equal, assert_array_equal, assert_raises, assert_, + assert_raises_regex, assert_warns, ) from numpy.lib.stride_tricks import ( - as_strided, broadcast_arrays, _broadcast_shape, broadcast_to + as_strided, broadcast_arrays, _broadcast_shape, broadcast_to, + broadcast_shapes, sliding_window_view, ) +import pytest + def assert_shapes_correct(input_shapes, expected_shape): # Broadcast a list of arrays with the given input shapes and check the @@ -57,6 +59,16 @@ def test_same(): assert_array_equal(x, bx) assert_array_equal(y, by) +def test_broadcast_kwargs(): + # ensure that a TypeError is appropriately raised when + # np.broadcast_arrays() is called with any keyword + # argument other than 'subok' + x = np.arange(10) + y = np.arange(10) + + with assert_raises_regex(TypeError, 'got an unexpected keyword'): + broadcast_arrays(x, y, dtype='float64') + def test_one_off(): x = np.array([[1, 2, 3]]) @@ -265,7 +277,9 @@ def test_broadcast_to_raises(): def test_broadcast_shape(): - # broadcast_shape is already exercized indirectly by broadcast_arrays + # tests internal _broadcast_shape + # _broadcast_shape is already exercised indirectly by broadcast_arrays + # _broadcast_shape is also exercised by the public broadcast_shapes function assert_equal(_broadcast_shape(), ()) assert_equal(_broadcast_shape([1, 2]), (2,)) assert_equal(_broadcast_shape(np.ones((1, 1))), (1, 1)) @@ -279,6 +293,64 @@ def test_broadcast_shape(): assert_raises(ValueError, lambda: _broadcast_shape(*bad_args)) +def test_broadcast_shapes_succeeds(): + # tests public broadcast_shapes + data = [ + [[], ()], + [[()], ()], + [[(7,)], (7,)], + [[(1, 2), (2,)], (1, 2)], + [[(1, 1)], (1, 1)], + [[(1, 1), (3, 4)], (3, 4)], + [[(6, 7), (5, 6, 1), (7,), (5, 1, 7)], (5, 6, 7)], + [[(5, 6, 1)], (5, 6, 1)], + [[(1, 3), (3, 1)], (3, 3)], + [[(1, 0), (0, 0)], (0, 0)], + [[(0, 1), (0, 0)], (0, 0)], + [[(1, 0), (0, 1)], (0, 0)], + [[(1, 1), (0, 0)], (0, 0)], + [[(1, 1), (1, 0)], (1, 0)], + [[(1, 1), (0, 1)], (0, 1)], + [[(), (0,)], (0,)], + [[(0,), (0, 0)], (0, 0)], + [[(0,), (0, 1)], (0, 0)], + [[(1,), (0, 0)], (0, 0)], + [[(), (0, 0)], (0, 0)], + [[(1, 1), (0,)], (1, 0)], + [[(1,), (0, 1)], (0, 1)], + [[(1,), (1, 0)], (1, 0)], + [[(), (1, 0)], (1, 0)], + [[(), (0, 1)], (0, 1)], + [[(1,), (3,)], (3,)], + [[2, (3, 2)], (3, 2)], + ] + for input_shapes, target_shape in data: + assert_equal(broadcast_shapes(*input_shapes), target_shape) + + assert_equal(broadcast_shapes(*([(1, 2)] * 32)), (1, 2)) + assert_equal(broadcast_shapes(*([(1, 2)] * 100)), (1, 2)) + + # regression tests for gh-5862 + assert_equal(broadcast_shapes(*([(2,)] * 32)), (2,)) + + +def test_broadcast_shapes_raises(): + # tests public broadcast_shapes + data = [ + [(3,), (4,)], + [(2, 3), (2,)], + [(3,), (3,), (4,)], + [(1, 3, 4), (2, 3, 3)], + [(1, 2), (3,1), (3,2), (10, 5)], + [2, (2, 3)], + ] + for input_shapes in data: + assert_raises(ValueError, lambda: broadcast_shapes(*input_shapes)) + + bad_args = [(2,)] * 32 + [(3,)] * 32 + assert_raises(ValueError, lambda: broadcast_shapes(*bad_args)) + + def test_as_strided(): a = np.array([None]) a_view = as_strided(a) @@ -324,6 +396,109 @@ def test_as_strided(): assert_equal(a.dtype, a_view.dtype) assert_array_equal([r] * 3, a_view) + +class TestSlidingWindowView: + def test_1d(self): + arr = np.arange(5) + arr_view = sliding_window_view(arr, 2) + expected = np.array([[0, 1], + [1, 2], + [2, 3], + [3, 4]]) + assert_array_equal(arr_view, expected) + + def test_2d(self): + i, j = np.ogrid[:3, :4] + arr = 10*i + j + shape = (2, 2) + arr_view = sliding_window_view(arr, shape) + expected = np.array([[[[0, 1], [10, 11]], + [[1, 2], [11, 12]], + [[2, 3], [12, 13]]], + [[[10, 11], [20, 21]], + [[11, 12], [21, 22]], + [[12, 13], [22, 23]]]]) + assert_array_equal(arr_view, expected) + + def test_2d_with_axis(self): + i, j = np.ogrid[:3, :4] + arr = 10*i + j + arr_view = sliding_window_view(arr, 3, 0) + expected = np.array([[[0, 10, 20], + [1, 11, 21], + [2, 12, 22], + [3, 13, 23]]]) + assert_array_equal(arr_view, expected) + + def test_2d_repeated_axis(self): + i, j = np.ogrid[:3, :4] + arr = 10*i + j + arr_view = sliding_window_view(arr, (2, 3), (1, 1)) + expected = np.array([[[[0, 1, 2], + [1, 2, 3]]], + [[[10, 11, 12], + [11, 12, 13]]], + [[[20, 21, 22], + [21, 22, 23]]]]) + assert_array_equal(arr_view, expected) + + def test_2d_without_axis(self): + i, j = np.ogrid[:4, :4] + arr = 10*i + j + shape = (2, 3) + arr_view = sliding_window_view(arr, shape) + expected = np.array([[[[0, 1, 2], [10, 11, 12]], + [[1, 2, 3], [11, 12, 13]]], + [[[10, 11, 12], [20, 21, 22]], + [[11, 12, 13], [21, 22, 23]]], + [[[20, 21, 22], [30, 31, 32]], + [[21, 22, 23], [31, 32, 33]]]]) + assert_array_equal(arr_view, expected) + + def test_errors(self): + i, j = np.ogrid[:4, :4] + arr = 10*i + j + with pytest.raises(ValueError, match='cannot contain negative values'): + sliding_window_view(arr, (-1, 3)) + with pytest.raises( + ValueError, + match='must provide window_shape for all dimensions of `x`'): + sliding_window_view(arr, (1,)) + with pytest.raises( + ValueError, + match='Must provide matching length window_shape and axis'): + sliding_window_view(arr, (1, 3, 4), axis=(0, 1)) + with pytest.raises( + ValueError, + match='window shape cannot be larger than input array'): + sliding_window_view(arr, (5, 5)) + + def test_writeable(self): + arr = np.arange(5) + view = sliding_window_view(arr, 2, writeable=False) + assert_(not view.flags.writeable) + with pytest.raises( + ValueError, + match='assignment destination is read-only'): + view[0, 0] = 3 + view = sliding_window_view(arr, 2, writeable=True) + assert_(view.flags.writeable) + view[0, 1] = 3 + assert_array_equal(arr, np.array([0, 3, 2, 3, 4])) + + def test_subok(self): + class MyArray(np.ndarray): + pass + + arr = np.arange(5).view(MyArray) + assert_(not isinstance(sliding_window_view(arr, 2, + subok=False), + MyArray)) + assert_(isinstance(sliding_window_view(arr, 2, subok=True), MyArray)) + # Default behavior + assert_(not isinstance(sliding_window_view(arr, 2), MyArray)) + + def as_strided_writeable(): arr = np.ones(10) view = as_strided(arr, writeable=False) @@ -344,14 +519,12 @@ def as_strided_writeable(): class VerySimpleSubClass(np.ndarray): def __new__(cls, *args, **kwargs): - kwargs['subok'] = True - return np.array(*args, **kwargs).view(cls) + return np.array(*args, subok=True, **kwargs).view(cls) class SimpleSubClass(VerySimpleSubClass): def __new__(cls, *args, **kwargs): - kwargs['subok'] = True - self = np.array(*args, **kwargs).view(cls) + self = np.array(*args, subok=True, **kwargs).view(cls) self.info = 'simple' return self @@ -403,12 +576,32 @@ def test_writeable(): assert_equal(result.flags.writeable, False) assert_raises(ValueError, result.__setitem__, slice(None), 0) - # but the result of broadcast_arrays needs to be writeable (for now), to + # but the result of broadcast_arrays needs to be writeable, to # preserve backwards compatibility + for is_broadcast, results in [(False, broadcast_arrays(original,)), + (True, broadcast_arrays(0, original))]: + for result in results: + # This will change to False in a future version + if is_broadcast: + with assert_warns(FutureWarning): + assert_equal(result.flags.writeable, True) + with assert_warns(DeprecationWarning): + result[:] = 0 + # Warning not emitted, writing to the array resets it + assert_equal(result.flags.writeable, True) + else: + # No warning: + assert_equal(result.flags.writeable, True) + for results in [broadcast_arrays(original), broadcast_arrays(0, original)]: for result in results: + # resets the warn_on_write DeprecationWarning + result.flags.writeable = True + # check: no warning emitted assert_equal(result.flags.writeable, True) + result[:] = 0 + # keep readonly input readonly original.flags.writeable = False _, result = broadcast_arrays(0, original) @@ -423,6 +616,25 @@ def test_writeable(): assert_(first.shape == second.shape) +def test_writeable_memoryview(): + # The result of broadcast_arrays exports as a non-writeable memoryview + # because otherwise there is no good way to opt in to the new behaviour + # (i.e. you would need to set writeable to False explicitly). + # See gh-13929. + original = np.array([1, 2, 3]) + + for is_broadcast, results in [(False, broadcast_arrays(original,)), + (True, broadcast_arrays(0, original))]: + for result in results: + # This will change to False in a future version + if is_broadcast: + # memoryview(result, writable=True) will give warning but cannot + # be tested using the python API. + assert memoryview(result).readonly + else: + assert not memoryview(result).readonly + + def test_reference_types(): input_array = np.array('a', dtype=object) expected = np.array(['a'] * 3, dtype=object) diff --git a/numpy/lib/tests/test_twodim_base.py b/numpy/lib/tests/test_twodim_base.py index d3a072af33f5..c1c5a1615d78 100644 --- a/numpy/lib/tests/test_twodim_base.py +++ b/numpy/lib/tests/test_twodim_base.py @@ -1,11 +1,9 @@ """Test functions for matrix module """ -from __future__ import division, absolute_import, print_function - from numpy.testing import ( assert_equal, assert_array_equal, assert_array_max_ulp, - assert_array_almost_equal, assert_raises, + assert_array_almost_equal, assert_raises, assert_ ) from numpy import ( @@ -17,13 +15,19 @@ import numpy as np +from numpy.core.tests.test_overrides import requires_array_function + + +import pytest + + def get_mat(n): data = arange(n) data = add.outer(data, data) return data -class TestEye(object): +class TestEye: def test_basic(self): assert_equal(eye(4), array([[1, 0, 0, 0], @@ -105,7 +109,7 @@ def test_order(self): assert mat_f.flags.f_contiguous -class TestDiag(object): +class TestDiag: def test_vector(self): vals = (100 * arange(5)).astype('l') b = zeros((5, 5)) @@ -152,7 +156,7 @@ def test_failure(self): assert_raises(ValueError, diag, [[[1]]]) -class TestFliplr(object): +class TestFliplr: def test_basic(self): assert_raises(ValueError, fliplr, ones(4)) a = get_mat(4) @@ -165,7 +169,7 @@ def test_basic(self): assert_equal(fliplr(a), b) -class TestFlipud(object): +class TestFlipud: def test_basic(self): a = get_mat(4) b = a[::-1, :] @@ -177,7 +181,7 @@ def test_basic(self): assert_equal(flipud(a), b) -class TestHistogram2d(object): +class TestHistogram2d: def test_simple(self): x = array( [0.41702200, 0.72032449, 1.1437481e-4, 0.302332573, 0.146755891]) @@ -208,7 +212,7 @@ def test_asym(self): x = array([1, 1, 2, 3, 4, 4, 4, 5]) y = array([1, 3, 2, 0, 1, 2, 3, 4]) H, xed, yed = histogram2d( - x, y, (6, 5), range=[[0, 6], [0, 5]], normed=True) + x, y, (6, 5), range=[[0, 6], [0, 5]], density=True) answer = array( [[0., 0, 0, 0, 0], [0, 1, 0, 1, 0], @@ -220,11 +224,11 @@ def test_asym(self): assert_array_equal(xed, np.linspace(0, 6, 7)) assert_array_equal(yed, np.linspace(0, 5, 6)) - def test_norm(self): + def test_density(self): x = array([1, 2, 3, 1, 2, 3, 1, 2, 3]) y = array([1, 1, 1, 2, 2, 2, 3, 3, 3]) H, xed, yed = histogram2d( - x, y, [[1, 2, 3, 5], [1, 2, 3, 5]], normed=True) + x, y, [[1, 2, 3, 5], [1, 2, 3, 5]], density=True) answer = array([[1, 1, .5], [1, 1, .5], [.5, .5, .25]])/9. @@ -273,8 +277,36 @@ def test_binparameter_combination(self): assert_array_equal(H, answer) assert_array_equal(xe, array([0., 0.25, 0.5, 0.75, 1])) - -class TestTri(object): + @requires_array_function + def test_dispatch(self): + class ShouldDispatch: + def __array_function__(self, function, types, args, kwargs): + return types, args, kwargs + + xy = [1, 2] + s_d = ShouldDispatch() + r = histogram2d(s_d, xy) + # Cannot use assert_equal since that dispatches... + assert_(r == ((ShouldDispatch,), (s_d, xy), {})) + r = histogram2d(xy, s_d) + assert_(r == ((ShouldDispatch,), (xy, s_d), {})) + r = histogram2d(xy, xy, bins=s_d) + assert_(r, ((ShouldDispatch,), (xy, xy), dict(bins=s_d))) + r = histogram2d(xy, xy, bins=[s_d, 5]) + assert_(r, ((ShouldDispatch,), (xy, xy), dict(bins=[s_d, 5]))) + assert_raises(Exception, histogram2d, xy, xy, bins=[s_d]) + r = histogram2d(xy, xy, weights=s_d) + assert_(r, ((ShouldDispatch,), (xy, xy), dict(weights=s_d))) + + @pytest.mark.parametrize(("x_len", "y_len"), [(10, 11), (20, 19)]) + def test_bad_length(self, x_len, y_len): + x, y = np.ones(x_len), np.ones(y_len) + with pytest.raises(ValueError, + match='x and y must have the same length.'): + histogram2d(x, y) + + +class TestTri: def test_dtype(self): out = array([[1, 0, 0], [1, 1, 0], @@ -412,7 +444,7 @@ def test_tril_indices(): [-10, -10, -10, -10, -10]])) -class TestTriuIndices(object): +class TestTriuIndices: def test_triu_indices(self): iu1 = triu_indices(4) iu2 = triu_indices(4, k=2) @@ -462,21 +494,21 @@ def test_triu_indices(self): [16, 17, 18, -1, -1]])) -class TestTrilIndicesFrom(object): +class TestTrilIndicesFrom: def test_exceptions(self): assert_raises(ValueError, tril_indices_from, np.ones((2,))) assert_raises(ValueError, tril_indices_from, np.ones((2, 2, 2))) # assert_raises(ValueError, tril_indices_from, np.ones((2, 3))) -class TestTriuIndicesFrom(object): +class TestTriuIndicesFrom: def test_exceptions(self): assert_raises(ValueError, triu_indices_from, np.ones((2,))) assert_raises(ValueError, triu_indices_from, np.ones((2, 2, 2))) # assert_raises(ValueError, triu_indices_from, np.ones((2, 3))) -class TestVander(object): +class TestVander: def test_basic(self): c = np.array([0, 1, -2, 3]) v = vander(c) diff --git a/numpy/lib/tests/test_type_check.py b/numpy/lib/tests/test_type_check.py index 2982ca31a3c2..3f4ca630960e 100644 --- a/numpy/lib/tests/test_type_check.py +++ b/numpy/lib/tests/test_type_check.py @@ -1,7 +1,4 @@ -from __future__ import division, absolute_import, print_function - import numpy as np -from numpy.compat import long from numpy.testing import ( assert_, assert_equal, assert_array_equal, assert_raises ) @@ -15,7 +12,7 @@ def assert_all(x): assert_(np.all(x), x) -class TestCommonType(object): +class TestCommonType: def test_basic(self): ai32 = np.array([[1, 2], [3, 4]], dtype=np.int32) af16 = np.array([[1, 2], [3, 4]], dtype=np.float16) @@ -31,7 +28,7 @@ def test_basic(self): assert_(common_type(acd) == np.cdouble) -class TestMintypecode(object): +class TestMintypecode: def test_default_1(self): for itype in '1bcsuwil': @@ -81,18 +78,17 @@ def test_default_3(self): assert_equal(mintypecode('idD'), 'D') -class TestIsscalar(object): +class TestIsscalar: def test_basic(self): assert_(np.isscalar(3)) assert_(not np.isscalar([3])) assert_(not np.isscalar((3,))) assert_(np.isscalar(3j)) - assert_(np.isscalar(long(10))) assert_(np.isscalar(4.0)) -class TestReal(object): +class TestReal: def test_real(self): y = np.random.rand(10,) @@ -123,7 +119,7 @@ def test_cmplx(self): assert_(not isinstance(out, np.ndarray)) -class TestImag(object): +class TestImag: def test_real(self): y = np.random.rand(10,) @@ -154,7 +150,7 @@ def test_cmplx(self): assert_(not isinstance(out, np.ndarray)) -class TestIscomplex(object): +class TestIscomplex: def test_fail(self): z = np.array([-1, 0, 1]) @@ -167,7 +163,7 @@ def test_pass(self): assert_array_equal(res, [1, 0, 0]) -class TestIsreal(object): +class TestIsreal: def test_pass(self): z = np.array([-1, 0, 1j]) @@ -180,7 +176,7 @@ def test_fail(self): assert_array_equal(res, [0, 1, 1]) -class TestIscomplexobj(object): +class TestIscomplexobj: def test_basic(self): z = np.array([-1, 0, 1]) @@ -209,7 +205,7 @@ def test_pandas_duck(self): # (pandas.core.dtypes) class PdComplex(np.complex128): pass - class PdDtype(object): + class PdDtype: name = 'category' names = None type = PdComplex @@ -233,7 +229,7 @@ def dtype(self): assert_(iscomplexobj(a)) -class TestIsrealobj(object): +class TestIsrealobj: def test_basic(self): z = np.array([-1, 0, 1]) assert_(isrealobj(z)) @@ -241,7 +237,7 @@ def test_basic(self): assert_(not isrealobj(z)) -class TestIsnan(object): +class TestIsnan: def test_goodvalues(self): z = np.array((-1., 0., 1.)) @@ -271,7 +267,7 @@ def test_complex1(self): assert_all(np.isnan(np.array(0+0j)/0.) == 1) -class TestIsfinite(object): +class TestIsfinite: # Fixme, wrong place, isfinite now ufunc def test_goodvalues(self): @@ -302,7 +298,7 @@ def test_complex1(self): assert_all(np.isfinite(np.array(1+1j)/0.) == 0) -class TestIsinf(object): +class TestIsinf: # Fixme, wrong place, isinf now ufunc def test_goodvalues(self): @@ -331,7 +327,7 @@ def test_ind(self): assert_all(np.isinf(np.array((0.,))/0.) == 0) -class TestIsposinf(object): +class TestIsposinf: def test_generic(self): with np.errstate(divide='ignore', invalid='ignore'): @@ -341,7 +337,7 @@ def test_generic(self): assert_(vals[2] == 1) -class TestIsneginf(object): +class TestIsneginf: def test_generic(self): with np.errstate(divide='ignore', invalid='ignore'): @@ -351,7 +347,7 @@ def test_generic(self): assert_(vals[2] == 0) -class TestNanToNum(object): +class TestNanToNum: def test_generic(self): with np.errstate(divide='ignore', invalid='ignore'): @@ -360,6 +356,14 @@ def test_generic(self): assert_(vals[1] == 0) assert_all(vals[2] > 1e10) and assert_all(np.isfinite(vals[2])) assert_equal(type(vals), np.ndarray) + + # perform the same tests but with nan, posinf and neginf keywords + with np.errstate(divide='ignore', invalid='ignore'): + vals = nan_to_num(np.array((-1., 0, 1))/0., + nan=10, posinf=20, neginf=30) + assert_equal(vals, [30, 10, 20]) + assert_all(np.isfinite(vals[[0, 2]])) + assert_equal(type(vals), np.ndarray) # perform the same test but in-place with np.errstate(divide='ignore', invalid='ignore'): @@ -371,26 +375,48 @@ def test_generic(self): assert_(vals[1] == 0) assert_all(vals[2] > 1e10) and assert_all(np.isfinite(vals[2])) assert_equal(type(vals), np.ndarray) + + # perform the same test but in-place + with np.errstate(divide='ignore', invalid='ignore'): + vals = np.array((-1., 0, 1))/0. + result = nan_to_num(vals, copy=False, nan=10, posinf=20, neginf=30) + + assert_(result is vals) + assert_equal(vals, [30, 10, 20]) + assert_all(np.isfinite(vals[[0, 2]])) + assert_equal(type(vals), np.ndarray) def test_array(self): vals = nan_to_num([1]) assert_array_equal(vals, np.array([1], int)) assert_equal(type(vals), np.ndarray) + vals = nan_to_num([1], nan=10, posinf=20, neginf=30) + assert_array_equal(vals, np.array([1], int)) + assert_equal(type(vals), np.ndarray) def test_integer(self): vals = nan_to_num(1) assert_all(vals == 1) assert_equal(type(vals), np.int_) + vals = nan_to_num(1, nan=10, posinf=20, neginf=30) + assert_all(vals == 1) + assert_equal(type(vals), np.int_) def test_float(self): vals = nan_to_num(1.0) assert_all(vals == 1.0) assert_equal(type(vals), np.float_) + vals = nan_to_num(1.1, nan=10, posinf=20, neginf=30) + assert_all(vals == 1.1) + assert_equal(type(vals), np.float_) def test_complex_good(self): vals = nan_to_num(1+1j) assert_all(vals == 1+1j) assert_equal(type(vals), np.complex_) + vals = nan_to_num(1+1j, nan=10, posinf=20, neginf=30) + assert_all(vals == 1+1j) + assert_equal(type(vals), np.complex_) def test_complex_bad(self): with np.errstate(divide='ignore', invalid='ignore'): @@ -414,9 +440,19 @@ def test_complex_bad2(self): # !! inf. Comment out for now, and see if it # !! changes #assert_all(vals.real < -1e10) and assert_all(np.isfinite(vals)) + + def test_do_not_rewrite_previous_keyword(self): + # This is done to test that when, for instance, nan=np.inf then these + # values are not rewritten by posinf keyword to the posinf value. + with np.errstate(divide='ignore', invalid='ignore'): + vals = nan_to_num(np.array((-1., 0, 1))/0., nan=np.inf, posinf=999) + assert_all(np.isfinite(vals[[0, 2]])) + assert_all(vals[0] < -1e10) + assert_equal(vals[[1, 2]], [np.inf, 999]) + assert_equal(type(vals), np.ndarray) -class TestRealIfClose(object): +class TestRealIfClose: def test_basic(self): a = np.random.rand(10) @@ -429,7 +465,7 @@ def test_basic(self): assert_all(isrealobj(b)) -class TestArrayConversion(object): +class TestArrayConversion: def test_asfarray(self): a = asfarray(np.array([1, 2, 3])) diff --git a/numpy/lib/tests/test_ufunclike.py b/numpy/lib/tests/test_ufunclike.py index 5604b3744aee..c280b696921a 100644 --- a/numpy/lib/tests/test_ufunclike.py +++ b/numpy/lib/tests/test_ufunclike.py @@ -1,14 +1,12 @@ -from __future__ import division, absolute_import, print_function - import numpy as np import numpy.core as nx import numpy.lib.ufunclike as ufl from numpy.testing import ( - assert_, assert_equal, assert_array_equal, assert_warns - ) + assert_, assert_equal, assert_array_equal, assert_warns, assert_raises +) -class TestUfunclike(object): +class TestUfunclike: def test_isposinf(self): a = nx.array([nx.inf, -nx.inf, nx.nan, 0.0, 3.0, -3.0]) @@ -21,6 +19,10 @@ def test_isposinf(self): assert_equal(res, tgt) assert_equal(out, tgt) + a = a.astype(np.complex_) + with assert_raises(TypeError): + ufl.isposinf(a) + def test_isneginf(self): a = nx.array([nx.inf, -nx.inf, nx.nan, 0.0, 3.0, -3.0]) out = nx.zeros(a.shape, bool) @@ -32,6 +34,10 @@ def test_isneginf(self): assert_equal(res, tgt) assert_equal(out, tgt) + a = a.astype(np.complex_) + with assert_raises(TypeError): + ufl.isneginf(a) + def test_fix(self): a = nx.array([[1.0, 1.1, 1.5, 1.8], [-1.0, -1.1, -1.5, -1.8]]) out = nx.zeros(a.shape, float) @@ -52,7 +58,8 @@ def __new__(cls, data, metadata=None): return res def __array_wrap__(self, obj, context=None): - obj.metadata = self.metadata + if isinstance(obj, MyArray): + obj.metadata = self.metadata return obj def __array_finalize__(self, obj): diff --git a/numpy/lib/tests/test_utils.py b/numpy/lib/tests/test_utils.py index c27c3cbf58c4..72c91836ff2b 100644 --- a/numpy/lib/tests/test_utils.py +++ b/numpy/lib/tests/test_utils.py @@ -1,20 +1,20 @@ -from __future__ import division, absolute_import, print_function - +import inspect import sys import pytest from numpy.core import arange from numpy.testing import assert_, assert_equal, assert_raises_regex -from numpy.lib import deprecate +from numpy.lib import deprecate, deprecate_with_doc import numpy.lib.utils as utils -if sys.version_info[0] >= 3: - from io import StringIO -else: - from StringIO import StringIO +from io import StringIO @pytest.mark.skipif(sys.flags.optimize == 2, reason="Python running -OO") +@pytest.mark.skipif( + sys.version_info == (3, 10, 0, "candidate", 1), + reason="Broken as of bpo-44524", +) def test_lookfor(): out = StringIO() utils.lookfor('eigenvalue', module='numpy', output=out, @@ -38,6 +38,37 @@ def old_func3(self, x): new_func3 = deprecate(old_func3, old_name="old_func3", new_name="new_func3") +def old_func4(self, x): + """Summary. + + Further info. + """ + return x +new_func4 = deprecate(old_func4) + + +def old_func5(self, x): + """Summary. + + Bizarre indentation. + """ + return x +new_func5 = deprecate(old_func5, message="This function is\ndeprecated.") + + +def old_func6(self, x): + """ + Also in PEP-257. + """ + return x +new_func6 = deprecate(old_func6) + + +@deprecate_with_doc(msg="Rather use new_func7") +def old_func7(self,x): + return x + + def test_deprecate_decorator(): assert_('deprecated' in old_func.__doc__) @@ -51,17 +82,93 @@ def test_deprecate_fn(): assert_('new_func3' in new_func3.__doc__) +def test_deprecate_with_doc_decorator_message(): + assert_('Rather use new_func7' in old_func7.__doc__) + + +@pytest.mark.skipif(sys.flags.optimize == 2, reason="-OO discards docstrings") +@pytest.mark.parametrize('old_func, new_func', [ + (old_func4, new_func4), + (old_func5, new_func5), + (old_func6, new_func6), +]) +def test_deprecate_help_indentation(old_func, new_func): + _compare_docs(old_func, new_func) + # Ensure we don't mess up the indentation + for knd, func in (('old', old_func), ('new', new_func)): + for li, line in enumerate(func.__doc__.split('\n')): + if li == 0: + assert line.startswith(' ') or not line.startswith(' '), knd + elif line: + assert line.startswith(' '), knd + + +def _compare_docs(old_func, new_func): + old_doc = inspect.getdoc(old_func) + new_doc = inspect.getdoc(new_func) + index = new_doc.index('\n\n') + 2 + assert_equal(new_doc[index:], old_doc) + + +@pytest.mark.skipif(sys.flags.optimize == 2, reason="-OO discards docstrings") +def test_deprecate_preserve_whitespace(): + assert_('\n Bizarre' in new_func5.__doc__) + + def test_safe_eval_nameconstant(): # Test if safe_eval supports Python 3.4 _ast.NameConstant utils.safe_eval('None') -def test_byte_bounds(): - a = arange(12).reshape(3, 4) - low, high = utils.byte_bounds(a) - assert_equal(high - low, a.size * a.itemsize) +class TestByteBounds: + + def test_byte_bounds(self): + # pointer difference matches size * itemsize + # due to contiguity + a = arange(12).reshape(3, 4) + low, high = utils.byte_bounds(a) + assert_equal(high - low, a.size * a.itemsize) + + def test_unusual_order_positive_stride(self): + a = arange(12).reshape(3, 4) + b = a.T + low, high = utils.byte_bounds(b) + assert_equal(high - low, b.size * b.itemsize) + + def test_unusual_order_negative_stride(self): + a = arange(12).reshape(3, 4) + b = a.T[::-1] + low, high = utils.byte_bounds(b) + assert_equal(high - low, b.size * b.itemsize) + + def test_strided(self): + a = arange(12) + b = a[::2] + low, high = utils.byte_bounds(b) + # the largest pointer address is lost (even numbers only in the + # stride), and compensate addresses for striding by 2 + assert_equal(high - low, b.size * 2 * b.itemsize - b.itemsize) def test_assert_raises_regex_context_manager(): with assert_raises_regex(ValueError, 'no deprecation warning'): raise ValueError('no deprecation warning') + + +def test_info_method_heading(): + # info(class) should only print "Methods:" heading if methods exist + + class NoPublicMethods: + pass + + class WithPublicMethods: + def first_method(): + pass + + def _has_method_heading(cls): + out = StringIO() + utils.info(cls, output=out) + return 'Methods:' in out.getvalue() + + assert _has_method_heading(WithPublicMethods) + assert not _has_method_heading(NoPublicMethods) diff --git a/numpy/lib/twodim_base.py b/numpy/lib/twodim_base.py index cca316e9a9b4..3e5ad31ff0d5 100644 --- a/numpy/lib/twodim_base.py +++ b/numpy/lib/twodim_base.py @@ -1,14 +1,17 @@ """ Basic functions for manipulating 2d arrays """ -from __future__ import division, absolute_import, print_function +import functools from numpy.core.numeric import ( - absolute, asanyarray, arange, zeros, greater_equal, multiply, ones, - asarray, where, int8, int16, int32, int64, empty, promote_types, diagonal, - nonzero + asanyarray, arange, zeros, greater_equal, multiply, ones, + asarray, where, int8, int16, int32, int64, intp, empty, promote_types, + diagonal, nonzero, indices ) -from numpy.core import iinfo, transpose +from numpy.core.overrides import set_array_function_like_doc, set_module +from numpy.core import overrides +from numpy.core import iinfo +from numpy.lib.stride_tricks import broadcast_to __all__ = [ @@ -17,6 +20,10 @@ 'tril_indices_from', 'triu_indices', 'triu_indices_from', ] +array_function_dispatch = functools.partial( + overrides.array_function_dispatch, module='numpy') + + i1 = iinfo(int8) i2 = iinfo(int16) i4 = iinfo(int32) @@ -33,12 +40,18 @@ def _min_int(low, high): return int64 +def _flip_dispatcher(m): + return (m,) + + +@array_function_dispatch(_flip_dispatcher) def fliplr(m): """ - Flip array in the left/right direction. + Reverse the order of elements along axis 1 (left/right). - Flip the entries in each row in the left/right direction. - Columns are preserved, but appear in a different order than before. + For a 2-D array, this flips the entries in each row in the left/right + direction. Columns are preserved, but appear in a different order than + before. Parameters ---------- @@ -54,23 +67,25 @@ def fliplr(m): See Also -------- flipud : Flip array in the up/down direction. + flip : Flip array in one or more dimensions. rot90 : Rotate array counterclockwise. Notes ----- - Equivalent to m[:,::-1]. Requires the array to be at least 2-D. + Equivalent to ``m[:,::-1]`` or ``np.flip(m, axis=1)``. + Requires the array to be at least 2-D. Examples -------- >>> A = np.diag([1.,2.,3.]) >>> A - array([[ 1., 0., 0.], - [ 0., 2., 0.], - [ 0., 0., 3.]]) + array([[1., 0., 0.], + [0., 2., 0.], + [0., 0., 3.]]) >>> np.fliplr(A) - array([[ 0., 0., 1.], - [ 0., 2., 0.], - [ 3., 0., 0.]]) + array([[0., 0., 1.], + [0., 2., 0.], + [3., 0., 0.]]) >>> A = np.random.randn(2,3,5) >>> np.all(np.fliplr(A) == A[:,::-1,...]) @@ -83,12 +98,13 @@ def fliplr(m): return m[:, ::-1] +@array_function_dispatch(_flip_dispatcher) def flipud(m): """ - Flip array in the up/down direction. + Reverse the order of elements along axis 0 (up/down). - Flip the entries in each column in the up/down direction. - Rows are preserved, but appear in a different order than before. + For a 2-D array, this flips the entries in each column in the up/down + direction. Rows are preserved, but appear in a different order than before. Parameters ---------- @@ -104,24 +120,25 @@ def flipud(m): See Also -------- fliplr : Flip array in the left/right direction. + flip : Flip array in one or more dimensions. rot90 : Rotate array counterclockwise. Notes ----- - Equivalent to ``m[::-1,...]``. - Does not require the array to be two-dimensional. + Equivalent to ``m[::-1, ...]`` or ``np.flip(m, axis=0)``. + Requires the array to be at least 1-D. Examples -------- >>> A = np.diag([1.0, 2, 3]) >>> A - array([[ 1., 0., 0.], - [ 0., 2., 0.], - [ 0., 0., 3.]]) + array([[1., 0., 0.], + [0., 2., 0.], + [0., 0., 3.]]) >>> np.flipud(A) - array([[ 0., 0., 3.], - [ 0., 2., 0.], - [ 1., 0., 0.]]) + array([[0., 0., 3.], + [0., 2., 0.], + [1., 0., 0.]]) >>> A = np.random.randn(2,3,5) >>> np.all(np.flipud(A) == A[::-1,...]) @@ -137,7 +154,13 @@ def flipud(m): return m[::-1, ...] -def eye(N, M=None, k=0, dtype=float, order='C'): +def _eye_dispatcher(N, M=None, k=None, dtype=None, order=None, *, like=None): + return (like,) + + +@set_array_function_like_doc +@set_module('numpy') +def eye(N, M=None, k=0, dtype=float, order='C', *, like=None): """ Return a 2-D array with ones on the diagonal and zeros elsewhere. @@ -158,6 +181,9 @@ def eye(N, M=None, k=0, dtype=float, order='C'): column-major (Fortran-style) order in memory. .. versionadded:: 1.14.0 + ${ARRAY_FUNCTION_LIKE} + + .. versionadded:: 1.20.0 Returns ------- @@ -176,11 +202,13 @@ def eye(N, M=None, k=0, dtype=float, order='C'): array([[1, 0], [0, 1]]) >>> np.eye(3, k=1) - array([[ 0., 1., 0.], - [ 0., 0., 1.], - [ 0., 0., 0.]]) + array([[0., 1., 0.], + [0., 0., 1.], + [0., 0., 0.]]) """ + if like is not None: + return _eye_with_like(N, M=M, k=k, dtype=dtype, order=order, like=like) if M is None: M = N m = zeros((N, M), dtype=dtype, order=order) @@ -194,6 +222,16 @@ def eye(N, M=None, k=0, dtype=float, order='C'): return m +_eye_with_like = array_function_dispatch( + _eye_dispatcher +)(eye) + + +def _diag_dispatcher(v, k=None): + return (v,) + + +@array_function_dispatch(_diag_dispatcher) def diag(v, k=0): """ Extract a diagonal or construct a diagonal array. @@ -265,6 +303,7 @@ def diag(v, k=0): raise ValueError("Input must be 1- or 2-d.") +@array_function_dispatch(_diag_dispatcher) def diagflat(v, k=0): """ Create a two-dimensional array with the flattened input as a diagonal. @@ -313,10 +352,10 @@ def diagflat(v, k=0): n = s + abs(k) res = zeros((n, n), v.dtype) if (k >= 0): - i = arange(0, n-k) + i = arange(0, n-k, dtype=intp) fi = i+k+i*n else: - i = arange(0, n+k) + i = arange(0, n+k, dtype=intp) fi = i+(i-k)*n res.flat[fi] = v if not wrap: @@ -324,7 +363,13 @@ def diagflat(v, k=0): return wrap(res) -def tri(N, M=None, k=0, dtype=float): +def _tri_dispatcher(N, M=None, k=None, dtype=None, *, like=None): + return (like,) + + +@set_array_function_like_doc +@set_module('numpy') +def tri(N, M=None, k=0, dtype=float, *, like=None): """ An array with ones at and below the given diagonal and zeros elsewhere. @@ -341,12 +386,15 @@ def tri(N, M=None, k=0, dtype=float): and `k` > 0 is above. The default is 0. dtype : dtype, optional Data type of the returned array. The default is float. + ${ARRAY_FUNCTION_LIKE} + + .. versionadded:: 1.20.0 Returns ------- tri : ndarray of shape (N, M) Array with its lower triangle filled with ones and zero elsewhere; - in other words ``T[i,j] == 1`` for ``i <= j + k``, 0 otherwise. + in other words ``T[i,j] == 1`` for ``j <= i + k``, 0 otherwise. Examples -------- @@ -356,11 +404,14 @@ def tri(N, M=None, k=0, dtype=float): [1, 1, 1, 1, 1]]) >>> np.tri(3, 5, -1) - array([[ 0., 0., 0., 0., 0.], - [ 1., 0., 0., 0., 0.], - [ 1., 1., 0., 0., 0.]]) + array([[0., 0., 0., 0., 0.], + [1., 0., 0., 0., 0.], + [1., 1., 0., 0., 0.]]) """ + if like is not None: + return _tri_with_like(N, M=M, k=k, dtype=dtype, like=like) + if M is None: M = N @@ -373,15 +424,27 @@ def tri(N, M=None, k=0, dtype=float): return m +_tri_with_like = array_function_dispatch( + _tri_dispatcher +)(tri) + + +def _trilu_dispatcher(m, k=None): + return (m,) + + +@array_function_dispatch(_trilu_dispatcher) def tril(m, k=0): """ Lower triangle of an array. Return a copy of an array with elements above the `k`-th diagonal zeroed. + For arrays with ``ndim`` exceeding 2, `tril` will apply to the final two + axes. Parameters ---------- - m : array_like, shape (M, N) + m : array_like, shape (..., M, N) Input array. k : int, optional Diagonal above which to zero elements. `k = 0` (the default) is the @@ -389,7 +452,7 @@ def tril(m, k=0): Returns ------- - tril : ndarray, shape (M, N) + tril : ndarray, shape (..., M, N) Lower triangle of `m`, of same shape and data-type as `m`. See Also @@ -404,6 +467,20 @@ def tril(m, k=0): [ 7, 8, 0], [10, 11, 12]]) + >>> np.tril(np.arange(3*4*5).reshape(3, 4, 5)) + array([[[ 0, 0, 0, 0, 0], + [ 5, 6, 0, 0, 0], + [10, 11, 12, 0, 0], + [15, 16, 17, 18, 0]], + [[20, 0, 0, 0, 0], + [25, 26, 0, 0, 0], + [30, 31, 32, 0, 0], + [35, 36, 37, 38, 0]], + [[40, 0, 0, 0, 0], + [45, 46, 0, 0, 0], + [50, 51, 52, 0, 0], + [55, 56, 57, 58, 0]]]) + """ m = asanyarray(m) mask = tri(*m.shape[-2:], k=k, dtype=bool) @@ -411,12 +488,14 @@ def tril(m, k=0): return where(mask, m, zeros(1, m.dtype)) +@array_function_dispatch(_trilu_dispatcher) def triu(m, k=0): """ Upper triangle of an array. - Return a copy of a matrix with the elements below the `k`-th diagonal - zeroed. + Return a copy of an array with the elements below the `k`-th diagonal + zeroed. For arrays with ``ndim`` exceeding 2, `triu` will apply to the final + two axes. Please refer to the documentation for `tril` for further details. @@ -432,6 +511,20 @@ def triu(m, k=0): [ 0, 8, 9], [ 0, 0, 12]]) + >>> np.triu(np.arange(3*4*5).reshape(3, 4, 5)) + array([[[ 0, 1, 2, 3, 4], + [ 0, 6, 7, 8, 9], + [ 0, 0, 12, 13, 14], + [ 0, 0, 0, 18, 19]], + [[20, 21, 22, 23, 24], + [ 0, 26, 27, 28, 29], + [ 0, 0, 32, 33, 34], + [ 0, 0, 0, 38, 39]], + [[40, 41, 42, 43, 44], + [ 0, 46, 47, 48, 49], + [ 0, 0, 52, 53, 54], + [ 0, 0, 0, 58, 59]]]) + """ m = asanyarray(m) mask = tri(*m.shape[-2:], k=k-1, dtype=bool) @@ -439,7 +532,12 @@ def triu(m, k=0): return where(mask, zeros(1, m.dtype), m) +def _vander_dispatcher(x, N=None, increasing=None): + return (x,) + + # Originally borrowed from John Hunter and matplotlib +@array_function_dispatch(_vander_dispatcher) def vander(x, N=None, increasing=False): """ Generate a Vandermonde matrix. @@ -507,7 +605,7 @@ def vander(x, N=None, increasing=False): of the differences between the values of the input vector: >>> np.linalg.det(np.vander(x)) - 48.000000000000043 + 48.000000000000043 # may vary >>> (5-3)*(5-2)*(5-1)*(3-2)*(3-1)*(2-1) 48 @@ -530,7 +628,27 @@ def vander(x, N=None, increasing=False): return v -def histogram2d(x, y, bins=10, range=None, normed=False, weights=None): +def _histogram2d_dispatcher(x, y, bins=None, range=None, normed=None, + weights=None, density=None): + yield x + yield y + + # This terrible logic is adapted from the checks in histogram2d + try: + N = len(bins) + except TypeError: + N = 1 + if N == 2: + yield from bins # bins=[x, y] + else: + yield bins + + yield weights + + +@array_function_dispatch(_histogram2d_dispatcher) +def histogram2d(x, y, bins=10, range=None, normed=None, weights=None, + density=None): """ Compute the bi-dimensional histogram of two data samples. @@ -560,9 +678,14 @@ def histogram2d(x, y, bins=10, range=None, normed=False, weights=None): (if not specified explicitly in the `bins` parameters): ``[[xmin, xmax], [ymin, ymax]]``. All values outside of this range will be considered outliers and not tallied in the histogram. + density : bool, optional + If False, the default, returns the number of samples in each bin. + If True, returns the probability *density* function at the bin, + ``bin_count / sample_count / bin_area``. normed : bool, optional - If False, returns the number of samples in each bin. If True, - returns the bin density ``bin_count / sample_count / bin_area``. + An alias for the density argument that behaves identically. To avoid + confusion with the broken normed argument to `histogram`, `density` + should be preferred. weights : array_like, shape(N,), optional An array of values ``w_i`` weighing each sample ``(x_i, y_i)``. Weights are normalized to 1 if `normed` is True. If `normed` is @@ -599,7 +722,7 @@ def histogram2d(x, y, bins=10, range=None, normed=False, weights=None): Examples -------- - >>> import matplotlib as mpl + >>> from matplotlib.image import NonUniformImage >>> import matplotlib.pyplot as plt Construct a 2-D histogram with variable bin width. First define the bin @@ -613,14 +736,17 @@ def histogram2d(x, y, bins=10, range=None, normed=False, weights=None): >>> x = np.random.normal(2, 1, 100) >>> y = np.random.normal(1, 1, 100) >>> H, xedges, yedges = np.histogram2d(x, y, bins=(xedges, yedges)) - >>> H = H.T # Let each row list bins with common y range. + >>> # Histogram does not follow Cartesian convention (see Notes), + >>> # therefore transpose H for visualization purposes. + >>> H = H.T :func:`imshow <matplotlib.pyplot.imshow>` can only display square bins: >>> fig = plt.figure(figsize=(7, 3)) >>> ax = fig.add_subplot(131, title='imshow: square bins') - >>> plt.imshow(H, interpolation='nearest', origin='low', + >>> plt.imshow(H, interpolation='nearest', origin='lower', ... extent=[xedges[0], xedges[-1], yedges[0], yedges[-1]]) + <matplotlib.image.AxesImage object at 0x...> :func:`pcolormesh <matplotlib.pyplot.pcolormesh>` can display actual edges: @@ -628,21 +754,59 @@ def histogram2d(x, y, bins=10, range=None, normed=False, weights=None): ... aspect='equal') >>> X, Y = np.meshgrid(xedges, yedges) >>> ax.pcolormesh(X, Y, H) + <matplotlib.collections.QuadMesh object at 0x...> :class:`NonUniformImage <matplotlib.image.NonUniformImage>` can be used to display actual bin edges with interpolation: >>> ax = fig.add_subplot(133, title='NonUniformImage: interpolated', ... aspect='equal', xlim=xedges[[0, -1]], ylim=yedges[[0, -1]]) - >>> im = mpl.image.NonUniformImage(ax, interpolation='bilinear') + >>> im = NonUniformImage(ax, interpolation='bilinear') >>> xcenters = (xedges[:-1] + xedges[1:]) / 2 >>> ycenters = (yedges[:-1] + yedges[1:]) / 2 >>> im.set_data(xcenters, ycenters, H) >>> ax.images.append(im) >>> plt.show() + It is also possible to construct a 2-D histogram without specifying bin + edges: + + >>> # Generate non-symmetric test data + >>> n = 10000 + >>> x = np.linspace(1, 100, n) + >>> y = 2*np.log(x) + np.random.rand(n) - 0.5 + >>> # Compute 2d histogram. Note the order of x/y and xedges/yedges + >>> H, yedges, xedges = np.histogram2d(y, x, bins=20) + + Now we can plot the histogram using + :func:`pcolormesh <matplotlib.pyplot.pcolormesh>`, and a + :func:`hexbin <matplotlib.pyplot.hexbin>` for comparison. + + >>> # Plot histogram using pcolormesh + >>> fig, (ax1, ax2) = plt.subplots(ncols=2, sharey=True) + >>> ax1.pcolormesh(xedges, yedges, H, cmap='rainbow') + >>> ax1.plot(x, 2*np.log(x), 'k-') + >>> ax1.set_xlim(x.min(), x.max()) + >>> ax1.set_ylim(y.min(), y.max()) + >>> ax1.set_xlabel('x') + >>> ax1.set_ylabel('y') + >>> ax1.set_title('histogram2d') + >>> ax1.grid() + + >>> # Create hexbin plot for comparison + >>> ax2.hexbin(x, y, gridsize=20, cmap='rainbow') + >>> ax2.plot(x, 2*np.log(x), 'k-') + >>> ax2.set_title('hexbin') + >>> ax2.set_xlim(x.min(), x.max()) + >>> ax2.set_xlabel('x') + >>> ax2.grid() + + >>> plt.show() """ from numpy import histogramdd + + if len(x) != len(y): + raise ValueError('x and y must have the same length.') try: N = len(bins) @@ -652,10 +816,11 @@ def histogram2d(x, y, bins=10, range=None, normed=False, weights=None): if N != 1 and N != 2: xedges = yedges = asarray(bins) bins = [xedges, yedges] - hist, edges = histogramdd([x, y], bins, range, normed, weights) + hist, edges = histogramdd([x, y], bins, range, normed, weights, density) return hist, edges[0], edges[1] +@set_module('numpy') def mask_indices(n, mask_func, k=0): """ Return the indices to access (n, n) arrays, given a masking function. @@ -726,6 +891,7 @@ def mask_indices(n, mask_func, k=0): return nonzero(a != 0) +@set_module('numpy') def tril_indices(n, k=0, m=None): """ Return the indices for the lower-triangle of an (n, m) array. @@ -782,7 +948,7 @@ def tril_indices(n, k=0, m=None): Both for indexing: >>> a[il1] - array([ 0, 4, 5, 8, 9, 10, 12, 13, 14, 15]) + array([ 0, 4, 5, ..., 13, 14, 15]) And for assigning values: @@ -803,9 +969,17 @@ def tril_indices(n, k=0, m=None): [-10, -10, -10, -10]]) """ - return nonzero(tri(n, m, k=k, dtype=bool)) + tri_ = tri(n, m, k=k, dtype=bool) + + return tuple(broadcast_to(inds, tri_.shape)[tri_] + for inds in indices(tri_.shape, sparse=True)) + +def _trilu_indices_form_dispatcher(arr, k=None): + return (arr,) + +@array_function_dispatch(_trilu_indices_form_dispatcher) def tril_indices_from(arr, k=0): """ Return the indices for the lower-triangle of arr. @@ -834,6 +1008,7 @@ def tril_indices_from(arr, k=0): return tril_indices(arr.shape[-2], k=k, m=arr.shape[-1]) +@set_module('numpy') def triu_indices(n, k=0, m=None): """ Return the indices for the upper-triangle of an (n, m) array. @@ -891,7 +1066,7 @@ def triu_indices(n, k=0, m=None): Both for indexing: >>> a[iu1] - array([ 0, 1, 2, 3, 5, 6, 7, 10, 11, 15]) + array([ 0, 1, 2, ..., 10, 11, 15]) And for assigning values: @@ -913,9 +1088,13 @@ def triu_indices(n, k=0, m=None): [ 12, 13, 14, -1]]) """ - return nonzero(~tri(n, m, k=k-1, dtype=bool)) + tri_ = ~tri(n, m, k=k - 1, dtype=bool) + + return tuple(broadcast_to(inds, tri_.shape)[tri_] + for inds in indices(tri_.shape, sparse=True)) +@array_function_dispatch(_trilu_indices_form_dispatcher) def triu_indices_from(arr, k=0): """ Return the indices for the upper-triangle of arr. diff --git a/numpy/lib/twodim_base.pyi b/numpy/lib/twodim_base.pyi new file mode 100644 index 000000000000..cba503ca3558 --- /dev/null +++ b/numpy/lib/twodim_base.pyi @@ -0,0 +1,255 @@ +from typing import ( + Any, + Callable, + List, + Sequence, + overload, + Tuple, + Type, + TypeVar, + Union, +) + +from numpy import ( + ndarray, + dtype, + generic, + number, + bool_, + timedelta64, + datetime64, + int_, + intp, + float64, + signedinteger, + floating, + complexfloating, + object_, + _OrderCF, +) + +from numpy.typing import ( + DTypeLike, + _SupportsDType, + ArrayLike, + NDArray, + _FiniteNestedSequence, + _SupportsArray, + _ArrayLikeInt_co, + _ArrayLikeFloat_co, + _ArrayLikeComplex_co, + _ArrayLikeObject_co, +) + +_T = TypeVar("_T") +_SCT = TypeVar("_SCT", bound=generic) + +# The returned arrays dtype must be compatible with `np.equal` +_MaskFunc = Callable[ + [NDArray[int_], _T], + NDArray[Union[number[Any], bool_, timedelta64, datetime64, object_]], +] + +_DTypeLike = Union[ + Type[_SCT], + dtype[_SCT], + _SupportsDType[dtype[_SCT]], +] +_ArrayLike = _FiniteNestedSequence[_SupportsArray[dtype[_SCT]]] + +__all__: List[str] + +@overload +def fliplr(m: _ArrayLike[_SCT]) -> NDArray[_SCT]: ... +@overload +def fliplr(m: ArrayLike) -> NDArray[Any]: ... + +@overload +def flipud(m: _ArrayLike[_SCT]) -> NDArray[_SCT]: ... +@overload +def flipud(m: ArrayLike) -> NDArray[Any]: ... + +@overload +def eye( + N: int, + M: None | int = ..., + k: int = ..., + dtype: None = ..., + order: _OrderCF = ..., + *, + like: None | ArrayLike = ..., +) -> NDArray[float64]: ... +@overload +def eye( + N: int, + M: None | int = ..., + k: int = ..., + dtype: _DTypeLike[_SCT] = ..., + order: _OrderCF = ..., + *, + like: None | ArrayLike = ..., +) -> NDArray[_SCT]: ... +@overload +def eye( + N: int, + M: None | int = ..., + k: int = ..., + dtype: DTypeLike = ..., + order: _OrderCF = ..., + *, + like: None | ArrayLike = ..., +) -> NDArray[Any]: ... + +@overload +def diag(v: _ArrayLike[_SCT], k: int = ...) -> NDArray[_SCT]: ... +@overload +def diag(v: ArrayLike, k: int = ...) -> NDArray[Any]: ... + +@overload +def diagflat(v: _ArrayLike[_SCT], k: int = ...) -> NDArray[_SCT]: ... +@overload +def diagflat(v: ArrayLike, k: int = ...) -> NDArray[Any]: ... + +@overload +def tri( + N: int, + M: None | int = ..., + k: int = ..., + dtype: None = ..., + *, + like: None | ArrayLike = ... +) -> NDArray[float64]: ... +@overload +def tri( + N: int, + M: None | int = ..., + k: int = ..., + dtype: _DTypeLike[_SCT] = ..., + *, + like: None | ArrayLike = ... +) -> NDArray[_SCT]: ... +@overload +def tri( + N: int, + M: None | int = ..., + k: int = ..., + dtype: DTypeLike = ..., + *, + like: None | ArrayLike = ... +) -> NDArray[Any]: ... + +@overload +def tril(v: _ArrayLike[_SCT], k: int = ...) -> NDArray[_SCT]: ... +@overload +def tril(v: ArrayLike, k: int = ...) -> NDArray[Any]: ... + +@overload +def triu(v: _ArrayLike[_SCT], k: int = ...) -> NDArray[_SCT]: ... +@overload +def triu(v: ArrayLike, k: int = ...) -> NDArray[Any]: ... + +@overload +def vander( # type: ignore[misc] + x: _ArrayLikeInt_co, + N: None | int = ..., + increasing: bool = ..., +) -> NDArray[signedinteger[Any]]: ... +@overload +def vander( # type: ignore[misc] + x: _ArrayLikeFloat_co, + N: None | int = ..., + increasing: bool = ..., +) -> NDArray[floating[Any]]: ... +@overload +def vander( + x: _ArrayLikeComplex_co, + N: None | int = ..., + increasing: bool = ..., +) -> NDArray[complexfloating[Any, Any]]: ... +@overload +def vander( + x: _ArrayLikeObject_co, + N: None | int = ..., + increasing: bool = ..., +) -> NDArray[object_]: ... + +@overload +def histogram2d( # type: ignore[misc] + x: _ArrayLikeFloat_co, + y: _ArrayLikeFloat_co, + bins: int | Sequence[int] = ..., + range: None | _ArrayLikeFloat_co = ..., + normed: None | bool = ..., + weights: None | _ArrayLikeFloat_co = ..., + density: None | bool = ..., +) -> Tuple[ + NDArray[float64], + NDArray[floating[Any]], + NDArray[floating[Any]], +]: ... +@overload +def histogram2d( + x: _ArrayLikeComplex_co, + y: _ArrayLikeComplex_co, + bins: int | Sequence[int] = ..., + range: None | _ArrayLikeFloat_co = ..., + normed: None | bool = ..., + weights: None | _ArrayLikeFloat_co = ..., + density: None | bool = ..., +) -> Tuple[ + NDArray[float64], + NDArray[complexfloating[Any, Any]], + NDArray[complexfloating[Any, Any]], +]: ... +@overload # TODO: Sort out `bins` +def histogram2d( + x: _ArrayLikeComplex_co, + y: _ArrayLikeComplex_co, + bins: Sequence[_ArrayLikeInt_co], + range: None | _ArrayLikeFloat_co = ..., + normed: None | bool = ..., + weights: None | _ArrayLikeFloat_co = ..., + density: None | bool = ..., +) -> Tuple[ + NDArray[float64], + NDArray[Any], + NDArray[Any], +]: ... + +# NOTE: we're assuming/demanding here the `mask_func` returns +# an ndarray of shape `(n, n)`; otherwise there is the possibility +# of the output tuple having more or less than 2 elements +@overload +def mask_indices( + n: int, + mask_func: _MaskFunc[int], + k: int = ..., +) -> Tuple[NDArray[intp], NDArray[intp]]: ... +@overload +def mask_indices( + n: int, + mask_func: _MaskFunc[_T], + k: _T, +) -> Tuple[NDArray[intp], NDArray[intp]]: ... + +def tril_indices( + n: int, + k: int = ..., + m: None | int = ..., +) -> Tuple[NDArray[int_], NDArray[int_]]: ... + +def tril_indices_from( + arr: NDArray[Any], + k: int = ..., +) -> Tuple[NDArray[int_], NDArray[int_]]: ... + +def triu_indices( + n: int, + k: int = ..., + m: None | int = ..., +) -> Tuple[NDArray[int_], NDArray[int_]]: ... + +def triu_indices_from( + arr: NDArray[Any], + k: int = ..., +) -> Tuple[NDArray[int_], NDArray[int_]]: ... diff --git a/numpy/lib/type_check.py b/numpy/lib/type_check.py index 1664e6ebb08d..56afd83ce335 100644 --- a/numpy/lib/type_check.py +++ b/numpy/lib/type_check.py @@ -1,7 +1,8 @@ """Automatically adapted for numpy Sep 19, 2005 by convertcode.py """ -from __future__ import division, absolute_import, print_function +import functools +import warnings __all__ = ['iscomplexobj', 'isrealobj', 'imag', 'iscomplex', 'isreal', 'nan_to_num', 'real', 'real_if_close', @@ -9,12 +10,21 @@ 'common_type'] import numpy.core.numeric as _nx -from numpy.core.numeric import asarray, asanyarray, array, isnan, zeros +from numpy.core.numeric import asarray, asanyarray, isnan, zeros +from numpy.core.overrides import set_module +from numpy.core import overrides from .ufunclike import isneginf, isposinf + +array_function_dispatch = functools.partial( + overrides.array_function_dispatch, module='numpy') + + _typecodes_by_elsize = 'GDFgdfQqLlIiHhBb?' -def mintypecode(typechars,typeset='GDFgdf',default='d'): + +@set_module('numpy') +def mintypecode(typechars, typeset='GDFgdf', default='d'): """ Return the character for the minimum-size type to which given types can be safely cast. @@ -57,20 +67,21 @@ def mintypecode(typechars,typeset='GDFgdf',default='d'): 'G' """ - typecodes = [(isinstance(t, str) and t) or asarray(t).dtype.char - for t in typechars] - intersection = [t for t in typecodes if t in typeset] + typecodes = ((isinstance(t, str) and t) or asarray(t).dtype.char + for t in typechars) + intersection = set(t for t in typecodes if t in typeset) if not intersection: return default if 'F' in intersection and 'd' in intersection: return 'D' - l = [] - for t in intersection: - i = _typecodes_by_elsize.index(t) - l.append((i, t)) - l.sort() - return l[0][1] + return min(intersection, key=_typecodes_by_elsize.index) + + +def _asfarray_dispatcher(a, dtype=None): + return (a,) + +@array_function_dispatch(_asfarray_dispatcher) def asfarray(a, dtype=_nx.float_): """ Return an array converted to a float type. @@ -91,11 +102,11 @@ def asfarray(a, dtype=_nx.float_): Examples -------- >>> np.asfarray([2, 3]) - array([ 2., 3.]) + array([2., 3.]) >>> np.asfarray([2, 3], dtype='float') - array([ 2., 3.]) + array([2., 3.]) >>> np.asfarray([2, 3], dtype='int8') - array([ 2., 3.]) + array([2., 3.]) """ if not _nx.issubdtype(dtype, _nx.inexact): @@ -103,6 +114,11 @@ def asfarray(a, dtype=_nx.float_): return asarray(a, dtype=dtype) +def _real_dispatcher(val): + return (val,) + + +@array_function_dispatch(_real_dispatcher) def real(val): """ Return the real part of the complex argument. @@ -127,13 +143,13 @@ def real(val): -------- >>> a = np.array([1+2j, 3+4j, 5+6j]) >>> a.real - array([ 1., 3., 5.]) + array([1., 3., 5.]) >>> a.real = 9 >>> a - array([ 9.+2.j, 9.+4.j, 9.+6.j]) + array([9.+2.j, 9.+4.j, 9.+6.j]) >>> a.real = np.array([9, 8, 7]) >>> a - array([ 9.+2.j, 8.+4.j, 7.+6.j]) + array([9.+2.j, 8.+4.j, 7.+6.j]) >>> np.real(1 + 1j) 1.0 @@ -144,6 +160,11 @@ def real(val): return asanyarray(val).real +def _imag_dispatcher(val): + return (val,) + + +@array_function_dispatch(_imag_dispatcher) def imag(val): """ Return the imaginary part of the complex argument. @@ -168,10 +189,10 @@ def imag(val): -------- >>> a = np.array([1+2j, 3+4j, 5+6j]) >>> a.imag - array([ 2., 4., 6.]) + array([2., 4., 6.]) >>> a.imag = np.array([8, 10, 12]) >>> a - array([ 1. +8.j, 3.+10.j, 5.+12.j]) + array([1. +8.j, 3.+10.j, 5.+12.j]) >>> np.imag(1 + 1j) 1.0 @@ -182,6 +203,11 @@ def imag(val): return asanyarray(val).imag +def _is_type_dispatcher(x): + return (x,) + + +@array_function_dispatch(_is_type_dispatcher) def iscomplex(x): """ Returns a bool array, where True if input element is complex. @@ -215,8 +241,10 @@ def iscomplex(x): if issubclass(ax.dtype.type, _nx.complexfloating): return ax.imag != 0 res = zeros(ax.shape, bool) - return +res # convert to array-scalar if needed + return res[()] # convert to scalar if needed + +@array_function_dispatch(_is_type_dispatcher) def isreal(x): """ Returns a bool array, where True if input element is real. @@ -234,6 +262,10 @@ def isreal(x): out : ndarray, bool Boolean array of same shape as `x`. + Notes + ----- + `isreal` may behave unexpectedly for string or object arrays (see examples) + See Also -------- iscomplex @@ -241,12 +273,34 @@ def isreal(x): Examples -------- - >>> np.isreal([1+1j, 1+0j, 4.5, 3, 2, 2j]) + >>> a = np.array([1+1j, 1+0j, 4.5, 3, 2, 2j], dtype=complex) + >>> np.isreal(a) array([False, True, True, True, True, False]) + + The function does not work on string arrays. + + >>> a = np.array([2j, "a"], dtype="U") + >>> np.isreal(a) # Warns about non-elementwise comparison + False + + Returns True for all elements in input array of ``dtype=object`` even if + any of the elements is complex. + + >>> a = np.array([1, "2", 3+4j], dtype=object) + >>> np.isreal(a) + array([ True, True, True]) + + isreal should not be used with object arrays + + >>> a = np.array([1+2j, 2+1j], dtype=object) + >>> np.isreal(a) + array([ True, True]) """ return imag(x) == 0 + +@array_function_dispatch(_is_type_dispatcher) def iscomplexobj(x): """ Check for a complex type or an array of complex numbers. @@ -287,6 +341,7 @@ def iscomplexobj(x): return issubclass(type_, _nx.complexfloating) +@array_function_dispatch(_is_type_dispatcher) def isrealobj(x): """ Return True if x is a not complex type or an array of complex numbers. @@ -309,6 +364,19 @@ def isrealobj(x): -------- iscomplexobj, isreal + Notes + ----- + The function is only meant for arrays with numerical values but it + accepts all other objects. Since it assumes array input, the return + value of other objects may be True. + + >>> np.isrealobj('A string') + True + >>> np.isrealobj(False) + True + >>> np.isrealobj(None) + True + Examples -------- >>> np.isrealobj(1) @@ -328,13 +396,24 @@ def _getmaxmin(t): f = getlimits.finfo(t) return f.max, f.min -def nan_to_num(x, copy=True): + +def _nan_to_num_dispatcher(x, copy=None, nan=None, posinf=None, neginf=None): + return (x,) + + +@array_function_dispatch(_nan_to_num_dispatcher) +def nan_to_num(x, copy=True, nan=0.0, posinf=None, neginf=None): """ - Replace NaN with zero and infinity with large finite numbers. + Replace NaN with zero and infinity with large finite numbers (default + behaviour) or with the numbers defined by the user using the `nan`, + `posinf` and/or `neginf` keywords. - If `x` is inexact, NaN is replaced by zero, and infinity and -infinity - replaced by the respectively largest and most negative finite floating - point values representable by ``x.dtype``. + If `x` is inexact, NaN is replaced by zero or by the user defined value in + `nan` keyword, infinity is replaced by the largest finite floating point + values representable by ``x.dtype`` or by the user defined value in + `posinf` keyword and -infinity is replaced by the most negative finite + floating point values representable by ``x.dtype`` or by the user defined + value in `neginf` keyword. For complex dtypes, the above is applied to each of the real and imaginary components of `x` separately. @@ -350,8 +429,27 @@ def nan_to_num(x, copy=True): in-place (False). The in-place operation only occurs if casting to an array does not require a copy. Default is True. - + .. versionadded:: 1.13 + nan : int, float, optional + Value to be used to fill NaN values. If no value is passed + then NaN values will be replaced with 0.0. + + .. versionadded:: 1.17 + posinf : int, float, optional + Value to be used to fill positive infinity values. If no value is + passed then positive infinity values will be replaced with a very + large number. + + .. versionadded:: 1.17 + neginf : int, float, optional + Value to be used to fill negative infinity values. If no value is + passed then negative infinity values will be replaced with a very + small (or negative) number. + + .. versionadded:: 1.17 + + Returns ------- @@ -382,13 +480,20 @@ def nan_to_num(x, copy=True): 0.0 >>> x = np.array([np.inf, -np.inf, np.nan, -128, 128]) >>> np.nan_to_num(x) - array([ 1.79769313e+308, -1.79769313e+308, 0.00000000e+000, - -1.28000000e+002, 1.28000000e+002]) + array([ 1.79769313e+308, -1.79769313e+308, 0.00000000e+000, # may vary + -1.28000000e+002, 1.28000000e+002]) + >>> np.nan_to_num(x, nan=-9999, posinf=33333333, neginf=33333333) + array([ 3.3333333e+07, 3.3333333e+07, -9.9990000e+03, + -1.2800000e+02, 1.2800000e+02]) >>> y = np.array([complex(np.inf, np.nan), np.nan, complex(np.nan, np.inf)]) + array([ 1.79769313e+308, -1.79769313e+308, 0.00000000e+000, # may vary + -1.28000000e+002, 1.28000000e+002]) >>> np.nan_to_num(y) - array([ 1.79769313e+308 +0.00000000e+000j, + array([ 1.79769313e+308 +0.00000000e+000j, # may vary 0.00000000e+000 +0.00000000e+000j, 0.00000000e+000 +1.79769313e+308j]) + >>> np.nan_to_num(y, nan=111111, posinf=222222) + array([222222.+111111.j, 111111. +0.j, 111111.+222222.j]) """ x = _nx.array(x, subok=True, copy=copy) xtype = x.dtype.type @@ -402,17 +507,30 @@ def nan_to_num(x, copy=True): dest = (x.real, x.imag) if iscomplex else (x,) maxf, minf = _getmaxmin(x.real.dtype) + if posinf is not None: + maxf = posinf + if neginf is not None: + minf = neginf for d in dest: - _nx.copyto(d, 0.0, where=isnan(d)) - _nx.copyto(d, maxf, where=isposinf(d)) - _nx.copyto(d, minf, where=isneginf(d)) + idx_nan = isnan(d) + idx_posinf = isposinf(d) + idx_neginf = isneginf(d) + _nx.copyto(d, nan, where=idx_nan) + _nx.copyto(d, maxf, where=idx_posinf) + _nx.copyto(d, minf, where=idx_neginf) return x[()] if isscalar else x #----------------------------------------------------------------------------- -def real_if_close(a,tol=100): +def _real_if_close_dispatcher(a, tol=None): + return (a,) + + +@array_function_dispatch(_real_if_close_dispatcher) +def real_if_close(a, tol=100): """ - If complex input returns a real array if complex parts are close to zero. + If input is complex with all imaginary parts close to zero, return + real parts. "Close to zero" is defined as `tol` * (machine epsilon of the type for `a`). @@ -445,12 +563,12 @@ def real_if_close(a,tol=100): Examples -------- >>> np.finfo(float).eps - 2.2204460492503131e-16 + 2.2204460492503131e-16 # may vary - >>> np.real_if_close([2.1 + 4e-14j], tol=1000) - array([ 2.1]) - >>> np.real_if_close([2.1 + 4e-13j], tol=1000) - array([ 2.1 +4.00000000e-13j]) + >>> np.real_if_close([2.1 + 4e-14j, 5.2 + 3e-15j], tol=1000) + array([2.1, 5.2]) + >>> np.real_if_close([2.1 + 4e-13j, 5.2 + 3e-15j], tol=1000) + array([2.1+4.e-13j, 5.2 + 3e-15j]) """ a = asanyarray(a) @@ -465,10 +583,22 @@ def real_if_close(a,tol=100): return a +def _asscalar_dispatcher(a): + # 2018-10-10, 1.16 + warnings.warn('np.asscalar(a) is deprecated since NumPy v1.16, use ' + 'a.item() instead', DeprecationWarning, stacklevel=3) + return (a,) + + +@array_function_dispatch(_asscalar_dispatcher) def asscalar(a): """ Convert an array of size 1 to its scalar equivalent. + .. deprecated:: 1.16 + + Deprecated, use `numpy.ndarray.item()` instead. + Parameters ---------- a : ndarray @@ -484,7 +614,6 @@ def asscalar(a): -------- >>> np.asscalar(np.array([24])) 24 - """ return a.item() @@ -514,6 +643,7 @@ def asscalar(a): 'O': 'object' } +@set_module('numpy') def typename(char): """ Return a description for the given data type code. @@ -577,6 +707,13 @@ def typename(char): _nx.csingle: 1, _nx.cdouble: 2, _nx.clongdouble: 3} + + +def _common_type_dispatcher(*arrays): + return arrays + + +@array_function_dispatch(_common_type_dispatcher) def common_type(*arrays): """ Return a scalar type which is common to the input arrays. @@ -606,11 +743,11 @@ def common_type(*arrays): Examples -------- >>> np.common_type(np.arange(2, dtype=np.float32)) - <type 'numpy.float32'> + <class 'numpy.float32'> >>> np.common_type(np.arange(2, dtype=np.float32), np.arange(2)) - <type 'numpy.float64'> + <class 'numpy.float64'> >>> np.common_type(np.arange(4), np.array([45, 6.j]), np.array([45.0])) - <type 'numpy.complex128'> + <class 'numpy.complex128'> """ is_complex = False diff --git a/numpy/lib/type_check.pyi b/numpy/lib/type_check.pyi new file mode 100644 index 000000000000..0a55dbf21347 --- /dev/null +++ b/numpy/lib/type_check.pyi @@ -0,0 +1,231 @@ +from typing import ( + Literal as L, + Any, + Container, + Iterable, + List, + overload, + Type, + TypeVar, + Protocol, +) + +from numpy import ( + dtype, + generic, + bool_, + floating, + float64, + complexfloating, + integer, +) + +from numpy.typing import ( + ArrayLike, + DTypeLike, + NBitBase, + NDArray, + _64Bit, + _SupportsDType, + _ScalarLike_co, + _FiniteNestedSequence, + _SupportsArray, + _DTypeLikeComplex, +) + +_T = TypeVar("_T") +_T_co = TypeVar("_T_co", covariant=True) +_SCT = TypeVar("_SCT", bound=generic) +_NBit1 = TypeVar("_NBit1", bound=NBitBase) +_NBit2 = TypeVar("_NBit2", bound=NBitBase) + +_ArrayLike = _FiniteNestedSequence[_SupportsArray[dtype[_SCT]]] + +class _SupportsReal(Protocol[_T_co]): + @property + def real(self) -> _T_co: ... + +class _SupportsImag(Protocol[_T_co]): + @property + def imag(self) -> _T_co: ... + +__all__: List[str] + +def mintypecode( + typechars: Iterable[str | ArrayLike], + typeset: Container[str] = ..., + default: str = ..., +) -> str: ... + +# `asfarray` ignores dtypes if they're not inexact + +@overload +def asfarray( + a: object, + dtype: None | Type[float] = ..., +) -> NDArray[float64]: ... +@overload +def asfarray( # type: ignore[misc] + a: Any, + dtype: _DTypeLikeComplex, +) -> NDArray[complexfloating[Any, Any]]: ... +@overload +def asfarray( + a: Any, + dtype: DTypeLike, +) -> NDArray[floating[Any]]: ... + +@overload +def real(val: _SupportsReal[_T]) -> _T: ... +@overload +def real(val: ArrayLike) -> NDArray[Any]: ... + +@overload +def imag(val: _SupportsImag[_T]) -> _T: ... +@overload +def imag(val: ArrayLike) -> NDArray[Any]: ... + +@overload +def iscomplex(x: _ScalarLike_co) -> bool_: ... # type: ignore[misc] +@overload +def iscomplex(x: ArrayLike) -> NDArray[bool_]: ... + +@overload +def isreal(x: _ScalarLike_co) -> bool_: ... # type: ignore[misc] +@overload +def isreal(x: ArrayLike) -> NDArray[bool_]: ... + +def iscomplexobj(x: _SupportsDType[dtype[Any]] | ArrayLike) -> bool: ... + +def isrealobj(x: _SupportsDType[dtype[Any]] | ArrayLike) -> bool: ... + +@overload +def nan_to_num( # type: ignore[misc] + x: _SCT, + copy: bool = ..., + nan: float = ..., + posinf: None | float = ..., + neginf: None | float = ..., +) -> _SCT: ... +@overload +def nan_to_num( + x: _ScalarLike_co, + copy: bool = ..., + nan: float = ..., + posinf: None | float = ..., + neginf: None | float = ..., +) -> Any: ... +@overload +def nan_to_num( + x: _ArrayLike[_SCT], + copy: bool = ..., + nan: float = ..., + posinf: None | float = ..., + neginf: None | float = ..., +) -> NDArray[_SCT]: ... +@overload +def nan_to_num( + x: ArrayLike, + copy: bool = ..., + nan: float = ..., + posinf: None | float = ..., + neginf: None | float = ..., +) -> NDArray[Any]: ... + +# If one passes a complex array to `real_if_close`, then one is reasonably +# expected to verify the output dtype (so we can return an unsafe union here) + +@overload +def real_if_close( # type: ignore[misc] + a: _ArrayLike[complexfloating[_NBit1, _NBit1]], + tol: float = ..., +) -> NDArray[floating[_NBit1]] | NDArray[complexfloating[_NBit1, _NBit1]]: ... +@overload +def real_if_close( + a: _ArrayLike[_SCT], + tol: float = ..., +) -> NDArray[_SCT]: ... +@overload +def real_if_close( + a: ArrayLike, + tol: float = ..., +) -> NDArray[Any]: ... + +# NOTE: deprecated +# def asscalar(a): ... + +@overload +def typename(char: L['S1']) -> L['character']: ... +@overload +def typename(char: L['?']) -> L['bool']: ... +@overload +def typename(char: L['b']) -> L['signed char']: ... +@overload +def typename(char: L['B']) -> L['unsigned char']: ... +@overload +def typename(char: L['h']) -> L['short']: ... +@overload +def typename(char: L['H']) -> L['unsigned short']: ... +@overload +def typename(char: L['i']) -> L['integer']: ... +@overload +def typename(char: L['I']) -> L['unsigned integer']: ... +@overload +def typename(char: L['l']) -> L['long integer']: ... +@overload +def typename(char: L['L']) -> L['unsigned long integer']: ... +@overload +def typename(char: L['q']) -> L['long long integer']: ... +@overload +def typename(char: L['Q']) -> L['unsigned long long integer']: ... +@overload +def typename(char: L['f']) -> L['single precision']: ... +@overload +def typename(char: L['d']) -> L['double precision']: ... +@overload +def typename(char: L['g']) -> L['long precision']: ... +@overload +def typename(char: L['F']) -> L['complex single precision']: ... +@overload +def typename(char: L['D']) -> L['complex double precision']: ... +@overload +def typename(char: L['G']) -> L['complex long double precision']: ... +@overload +def typename(char: L['S']) -> L['string']: ... +@overload +def typename(char: L['U']) -> L['unicode']: ... +@overload +def typename(char: L['V']) -> L['void']: ... +@overload +def typename(char: L['O']) -> L['object']: ... + +@overload +def common_type( # type: ignore[misc] + *arrays: _SupportsDType[dtype[ + integer[Any] + ]] +) -> Type[floating[_64Bit]]: ... +@overload +def common_type( # type: ignore[misc] + *arrays: _SupportsDType[dtype[ + floating[_NBit1] + ]] +) -> Type[floating[_NBit1]]: ... +@overload +def common_type( # type: ignore[misc] + *arrays: _SupportsDType[dtype[ + integer[Any] | floating[_NBit1] + ]] +) -> Type[floating[_NBit1 | _64Bit]]: ... +@overload +def common_type( # type: ignore[misc] + *arrays: _SupportsDType[dtype[ + floating[_NBit1] | complexfloating[_NBit2, _NBit2] + ]] +) -> Type[complexfloating[_NBit1 | _NBit2, _NBit1 | _NBit2]]: ... +@overload +def common_type( + *arrays: _SupportsDType[dtype[ + integer[Any] | floating[_NBit1] | complexfloating[_NBit2, _NBit2] + ]] +) -> Type[complexfloating[_64Bit | _NBit1 | _NBit2, _64Bit | _NBit1 | _NBit2]]: ... diff --git a/numpy/lib/ufunclike.py b/numpy/lib/ufunclike.py index e0bd95182a69..a93c4773bc4e 100644 --- a/numpy/lib/ufunclike.py +++ b/numpy/lib/ufunclike.py @@ -3,14 +3,16 @@ storing results in an output array. """ -from __future__ import division, absolute_import, print_function - __all__ = ['fix', 'isneginf', 'isposinf'] import numpy.core.numeric as nx +from numpy.core.overrides import ( + array_function_dispatch, ARRAY_FUNCTION_ENABLED, +) import warnings import functools + def _deprecate_out_named_y(f): """ Allow the out argument to be passed as the name `y` (deprecated) @@ -36,7 +38,40 @@ def func(x, out=None, **kwargs): return func +def _fix_out_named_y(f): + """ + Allow the out argument to be passed as the name `y` (deprecated) + + This decorator should only be used if _deprecate_out_named_y is used on + a corresponding dispatcher function. + """ + @functools.wraps(f) + def func(x, out=None, **kwargs): + if 'y' in kwargs: + # we already did error checking in _deprecate_out_named_y + out = kwargs.pop('y') + return f(x, out=out, **kwargs) + + return func + + +def _fix_and_maybe_deprecate_out_named_y(f): + """ + Use the appropriate decorator, depending upon if dispatching is being used. + """ + if ARRAY_FUNCTION_ENABLED: + return _fix_out_named_y(f) + else: + return _deprecate_out_named_y(f) + + @_deprecate_out_named_y +def _dispatcher(x, out=None): + return (x, out) + + +@array_function_dispatch(_dispatcher, verify=False, module='numpy') +@_fix_and_maybe_deprecate_out_named_y def fix(x, out=None): """ Round to nearest integer towards zero. @@ -48,17 +83,24 @@ def fix(x, out=None): ---------- x : array_like An array of floats to be rounded - y : ndarray, optional - Output array + out : ndarray, optional + A location into which the result is stored. If provided, it must have + a shape that the input broadcasts to. If not provided or None, a + freshly-allocated array is returned. Returns ------- out : ndarray of floats - The array of rounded numbers + A float array with the same dimensions as the input. + If second argument is not supplied then a float array is returned + with the rounded values. + + If a second argument is supplied the result is stored there. + The return value `out` is then a reference to that array. See Also -------- - trunc, floor, ceil + rint, trunc, floor, ceil around : Round to given number of decimals Examples @@ -81,7 +123,9 @@ def fix(x, out=None): res = res[()] return res -@_deprecate_out_named_y + +@array_function_dispatch(_dispatcher, verify=False, module='numpy') +@_fix_and_maybe_deprecate_out_named_y def isposinf(x, out=None): """ Test element-wise for positive infinity, return result as bool array. @@ -90,8 +134,10 @@ def isposinf(x, out=None): ---------- x : array_like The input array. - y : array_like, optional - A boolean array with the same shape as `x` to store the result. + out : array_like, optional + A location into which the result is stored. If provided, it must have a + shape that the input broadcasts to. If not provided or None, a + freshly-allocated boolean array is returned. Returns ------- @@ -116,17 +162,18 @@ def isposinf(x, out=None): NumPy uses the IEEE Standard for Binary Floating-Point for Arithmetic (IEEE 754). - Errors result if the second argument is also supplied when `x` is a - scalar input, or if first and second arguments have different shapes. + Errors result if the second argument is also supplied when x is a scalar + input, if first and second arguments have different shapes, or if the + first argument has complex values Examples -------- >>> np.isposinf(np.PINF) - array(True, dtype=bool) + True >>> np.isposinf(np.inf) - array(True, dtype=bool) + True >>> np.isposinf(np.NINF) - array(False, dtype=bool) + False >>> np.isposinf([-np.inf, 0., np.inf]) array([False, False, True]) @@ -138,10 +185,19 @@ def isposinf(x, out=None): array([0, 0, 1]) """ - return nx.logical_and(nx.isinf(x), ~nx.signbit(x), out) - - -@_deprecate_out_named_y + is_inf = nx.isinf(x) + try: + signbit = ~nx.signbit(x) + except TypeError as e: + dtype = nx.asanyarray(x).dtype + raise TypeError(f'This operation is not supported for {dtype} values ' + 'because it would be ambiguous.') from e + else: + return nx.logical_and(is_inf, signbit, out) + + +@array_function_dispatch(_dispatcher, verify=False, module='numpy') +@_fix_and_maybe_deprecate_out_named_y def isneginf(x, out=None): """ Test element-wise for negative infinity, return result as bool array. @@ -151,8 +207,9 @@ def isneginf(x, out=None): x : array_like The input array. out : array_like, optional - A boolean array with the same shape and type as `x` to store the - result. + A location into which the result is stored. If provided, it must have a + shape that the input broadcasts to. If not provided or None, a + freshly-allocated boolean array is returned. Returns ------- @@ -178,16 +235,17 @@ def isneginf(x, out=None): (IEEE 754). Errors result if the second argument is also supplied when x is a scalar - input, or if first and second arguments have different shapes. + input, if first and second arguments have different shapes, or if the + first argument has complex values. Examples -------- >>> np.isneginf(np.NINF) - array(True, dtype=bool) + True >>> np.isneginf(np.inf) - array(False, dtype=bool) + False >>> np.isneginf(np.PINF) - array(False, dtype=bool) + False >>> np.isneginf([-np.inf, 0., np.inf]) array([ True, False, False]) @@ -199,4 +257,12 @@ def isneginf(x, out=None): array([1, 0, 0]) """ - return nx.logical_and(nx.isinf(x), nx.signbit(x), out) + is_inf = nx.isinf(x) + try: + signbit = nx.signbit(x) + except TypeError as e: + dtype = nx.asanyarray(x).dtype + raise TypeError(f'This operation is not supported for {dtype} values ' + 'because it would be ambiguous.') from e + else: + return nx.logical_and(is_inf, signbit, out) diff --git a/numpy/lib/ufunclike.pyi b/numpy/lib/ufunclike.pyi new file mode 100644 index 000000000000..03f08ebffea3 --- /dev/null +++ b/numpy/lib/ufunclike.pyi @@ -0,0 +1,66 @@ +from typing import Any, overload, TypeVar, List, Union + +from numpy import floating, bool_, object_, ndarray +from numpy.typing import ( + NDArray, + _FloatLike_co, + _ArrayLikeFloat_co, + _ArrayLikeObject_co, +) + +_ArrayType = TypeVar("_ArrayType", bound=ndarray[Any, Any]) + +__all__: List[str] + +@overload +def fix( # type: ignore[misc] + x: _FloatLike_co, + out: None = ..., +) -> floating[Any]: ... +@overload +def fix( + x: _ArrayLikeFloat_co, + out: None = ..., +) -> NDArray[floating[Any]]: ... +@overload +def fix( + x: _ArrayLikeObject_co, + out: None = ..., +) -> NDArray[object_]: ... +@overload +def fix( + x: Union[_ArrayLikeFloat_co, _ArrayLikeObject_co], + out: _ArrayType, +) -> _ArrayType: ... + +@overload +def isposinf( # type: ignore[misc] + x: _FloatLike_co, + out: None = ..., +) -> bool_: ... +@overload +def isposinf( + x: _ArrayLikeFloat_co, + out: None = ..., +) -> NDArray[bool_]: ... +@overload +def isposinf( + x: _ArrayLikeFloat_co, + out: _ArrayType, +) -> _ArrayType: ... + +@overload +def isneginf( # type: ignore[misc] + x: _FloatLike_co, + out: None = ..., +) -> bool_: ... +@overload +def isneginf( + x: _ArrayLikeFloat_co, + out: None = ..., +) -> NDArray[bool_]: ... +@overload +def isneginf( + x: _ArrayLikeFloat_co, + out: _ArrayType, +) -> _ArrayType: ... diff --git a/numpy/lib/user_array.py b/numpy/lib/user_array.py index f1510a7b11c9..0e96b477ef74 100644 --- a/numpy/lib/user_array.py +++ b/numpy/lib/user_array.py @@ -5,18 +5,15 @@ complete. """ -from __future__ import division, absolute_import, print_function - from numpy.core import ( array, asarray, absolute, add, subtract, multiply, divide, remainder, power, left_shift, right_shift, bitwise_and, bitwise_or, bitwise_xor, invert, less, less_equal, not_equal, equal, greater, greater_equal, shape, reshape, arange, sin, sqrt, transpose ) -from numpy.compat import long -class container(object): +class container: """ container(data, dtype=None, copy=True) @@ -198,9 +195,6 @@ def __float__(self): def __int__(self): return self._scalarfunc(int) - def __long__(self): - return self._scalarfunc(long) - def __hex__(self): return self._scalarfunc(hex) @@ -233,6 +227,10 @@ def tostring(self): "" return self.array.tostring() + def tobytes(self): + "" + return self.array.tobytes() + def byteswap(self): "" return self._rc(self.array.byteswap()) diff --git a/numpy/lib/utils.py b/numpy/lib/utils.py index 1ecd334afc74..1df2ab09b29d 100644 --- a/numpy/lib/utils.py +++ b/numpy/lib/utils.py @@ -1,18 +1,15 @@ -from __future__ import division, absolute_import, print_function - import os import sys +import textwrap import types import re import warnings from numpy.core.numerictypes import issubclass_, issubsctype, issubdtype +from numpy.core.overrides import set_module from numpy.core import ndarray, ufunc, asarray import numpy as np -# getargspec and formatargspec were removed in Python 3.6 -from numpy.compat import getargspec, formatargspec - __all__ = [ 'issubclass_', 'issubsctype', 'issubdtype', 'deprecate', 'deprecate_with_doc', 'get_include', 'info', 'source', 'who', @@ -54,7 +51,7 @@ def _set_function_name(func, name): return func -class _Deprecate(object): +class _Deprecate: """ Decorator class to deprecate old functions. @@ -80,7 +77,6 @@ def __call__(self, func, *args, **kwargs): new_name = self.new_name message = self.message - import warnings if old_name is None: try: old_name = func.__name__ @@ -105,6 +101,21 @@ def newfunc(*args,**kwds): if doc is None: doc = depdoc else: + lines = doc.expandtabs().split('\n') + indent = _get_indent(lines[1:]) + if lines[0].lstrip(): + # Indent the original first line to let inspect.cleandoc() + # dedent the docstring despite the deprecation notice. + doc = indent * ' ' + doc + else: + # Remove the same leading blank lines as cleandoc() would. + skip = len(lines[0]) + 1 + for line in lines[1:]: + if len(line) > indent: + break + skip += len(line) + 1 + doc = doc[skip:] + depdoc = textwrap.indent(depdoc, ' ' * indent) doc = '\n\n'.join([depdoc, doc]) newfunc.__doc__ = doc try: @@ -115,6 +126,21 @@ def newfunc(*args,**kwds): newfunc.__dict__.update(d) return newfunc + +def _get_indent(lines): + """ + Determines the leading whitespace that could be removed from all the lines. + """ + indent = sys.maxsize + for line in lines: + content = len(line.lstrip()) + if content: + indent = min(indent, len(line) - content) + if indent == sys.maxsize: + indent = 0 + return indent + + def deprecate(*args, **kwargs): """ Issues a DeprecationWarning, adds warning to `old_name`'s @@ -150,10 +176,8 @@ def deprecate(*args, **kwargs): Warning: >>> olduint = np.deprecate(np.uint) + DeprecationWarning: `uint64` is deprecated! # may vary >>> olduint(6) - /usr/lib/python2.5/site-packages/numpy/lib/utils.py:114: - DeprecationWarning: uint32 is deprecated - warnings.warn(str1, DeprecationWarning, stacklevel=2) 6 """ @@ -165,18 +189,36 @@ def deprecate(*args, **kwargs): fn = args[0] args = args[1:] - # backward compatibility -- can be removed - # after next release - if 'newname' in kwargs: - kwargs['new_name'] = kwargs.pop('newname') - if 'oldname' in kwargs: - kwargs['old_name'] = kwargs.pop('oldname') - return _Deprecate(*args, **kwargs)(fn) else: return _Deprecate(*args, **kwargs) -deprecate_with_doc = lambda msg: _Deprecate(message=msg) + +def deprecate_with_doc(msg): + """ + Deprecates a function and includes the deprecation in its docstring. + + This function is used as a decorator. It returns an object that can be + used to issue a DeprecationWarning, by passing the to-be decorated + function as argument, this adds warning to the to-be decorated function's + docstring and returns the new function object. + + See Also + -------- + deprecate : Decorate a function such that it issues a `DeprecationWarning` + + Parameters + ---------- + msg : str + Additional explanation of the deprecation. Displayed in the + docstring after the warning. + + Returns + ------- + obj : object + + """ + return _Deprecate(message=msg) #-------------------------------------------- @@ -208,8 +250,8 @@ def byte_bounds(a): >>> low, high = np.byte_bounds(I) >>> high - low == I.size*I.itemsize True - >>> I = np.eye(2, dtype='G'); I.dtype - dtype('complex192') + >>> I = np.eye(2); I.dtype + dtype('float64') >>> low, high = np.byte_bounds(I) >>> high - low == I.size*I.itemsize True @@ -270,17 +312,17 @@ def who(vardict=None): >>> np.who() Name Shape Bytes Type =========================================================== - a 10 40 int32 + a 10 80 int64 b 20 160 float64 - Upper bound on total bytes = 200 + Upper bound on total bytes = 240 >>> d = {'x': np.arange(2.0), 'y': np.arange(3.0), 'txt': 'Some str', ... 'idx':5} >>> np.who(d) Name Shape Bytes Type =========================================================== - y 3 24 float64 x 2 16 float64 + y 3 24 float64 Upper bound on total bytes = 40 """ @@ -309,8 +351,7 @@ def who(vardict=None): maxshape = 0 maxbyte = 0 totalbytes = 0 - for k in range(len(sta)): - val = sta[k] + for val in sta: if maxname < len(val[0]): maxname = len(val[0]) if maxshape < len(val[1]): @@ -327,8 +368,7 @@ def who(vardict=None): prval = "Name %s Shape %s Bytes %s Type" % (sp1*' ', sp2*' ', sp3*' ') print(prval + "\n" + "="*(len(prval)+5) + "\n") - for k in range(len(sta)): - val = sta[k] + for val in sta: print("%s %s %s %s %s %s %s" % (val[0], ' '*(sp1-len(val[0])+4), val[1], ' '*(sp2-len(val[1])+5), val[2], ' '*(sp3-len(val[2])+5), @@ -440,6 +480,7 @@ def _info(obj, output=sys.stdout): print("type: %s" % obj.dtype, file=output) +@set_module('numpy') def info(object=None, maxwidth=76, output=sys.stdout, toplevel='numpy'): """ Get help information for a function, class, or module. @@ -533,9 +574,12 @@ def info(object=None, maxwidth=76, output=sys.stdout, toplevel='numpy'): file=output ) - elif inspect.isfunction(object): + elif inspect.isfunction(object) or inspect.ismethod(object): name = object.__name__ - arguments = formatargspec(*getargspec(object)) + try: + arguments = str(inspect.signature(object)) + except Exception: + arguments = "()" if len(name+arguments) > maxwidth: argstr = _split_line(name, arguments, maxwidth) @@ -547,18 +591,10 @@ def info(object=None, maxwidth=76, output=sys.stdout, toplevel='numpy'): elif inspect.isclass(object): name = object.__name__ - arguments = "()" try: - if hasattr(object, '__init__'): - arguments = formatargspec( - *getargspec(object.__init__.__func__) - ) - arglist = arguments.split(', ') - if len(arglist) > 1: - arglist[1] = "("+arglist[1] - arguments = ", ".join(arglist[1:]) + arguments = str(inspect.signature(object)) except Exception: - pass + arguments = "()" if len(name+arguments) > maxwidth: argstr = _split_line(name, arguments, maxwidth) @@ -574,11 +610,11 @@ def info(object=None, maxwidth=76, output=sys.stdout, toplevel='numpy'): print(inspect.getdoc(object), file=output) methods = pydoc.allmethods(object) - if methods != []: + + public_methods = [meth for meth in methods if meth[0] != '_'] + if public_methods: print("\n\nMethods:\n", file=output) - for meth in methods: - if meth[0] == '_': - continue + for meth in public_methods: thisobj = getattr(object, meth, None) if thisobj is not None: methstr, other = pydoc.splitdoc( @@ -586,65 +622,11 @@ def info(object=None, maxwidth=76, output=sys.stdout, toplevel='numpy'): ) print(" %s -- %s" % (meth, methstr), file=output) - elif (sys.version_info[0] < 3 - and isinstance(object, types.InstanceType)): - # check for __call__ method - # types.InstanceType is the type of the instances of oldstyle classes - print("Instance of class: ", object.__class__.__name__, file=output) - print(file=output) - if hasattr(object, '__call__'): - arguments = formatargspec( - *getargspec(object.__call__.__func__) - ) - arglist = arguments.split(', ') - if len(arglist) > 1: - arglist[1] = "("+arglist[1] - arguments = ", ".join(arglist[1:]) - else: - arguments = "()" - - if hasattr(object, 'name'): - name = "%s" % object.name - else: - name = "<name>" - if len(name+arguments) > maxwidth: - argstr = _split_line(name, arguments, maxwidth) - else: - argstr = name + arguments - - print(" " + argstr + "\n", file=output) - doc = inspect.getdoc(object.__call__) - if doc is not None: - print(inspect.getdoc(object.__call__), file=output) - print(inspect.getdoc(object), file=output) - - else: - print(inspect.getdoc(object), file=output) - - elif inspect.ismethod(object): - name = object.__name__ - arguments = formatargspec( - *getargspec(object.__func__) - ) - arglist = arguments.split(', ') - if len(arglist) > 1: - arglist[1] = "("+arglist[1] - arguments = ", ".join(arglist[1:]) - else: - arguments = "()" - - if len(name+arguments) > maxwidth: - argstr = _split_line(name, arguments, maxwidth) - else: - argstr = name + arguments - - print(" " + argstr + "\n", file=output) - print(inspect.getdoc(object), file=output) - elif hasattr(object, '__doc__'): print(inspect.getdoc(object), file=output) +@set_module('numpy') def source(object, output=sys.stdout): """ Print or write to a file the source code for a NumPy object. @@ -702,6 +684,8 @@ def interp(x, xp, fp, left=None, right=None): # signature _function_signature_re = re.compile(r"[a-z0-9_]+\(.*[,=].*\)", re.I) + +@set_module('numpy') def lookfor(what, module=None, import_modules=True, regenerate=False, output=None): """ @@ -736,7 +720,7 @@ def lookfor(what, module=None, import_modules=True, regenerate=False, Examples -------- - >>> np.lookfor('binary representation') + >>> np.lookfor('binary representation') # doctest: +SKIP Search results for 'binary representation' ------------------------------------------ numpy.binary_repr @@ -764,13 +748,8 @@ def lookfor(what, module=None, import_modules=True, regenerate=False, if kind in ('module', 'object'): # don't show modules or objects continue - ok = True doc = docstring.lower() - for w in whats: - if w not in doc: - ok = False - break - if ok: + if all(w in doc for w in whats): found.append(name) # Relevance sort @@ -850,15 +829,10 @@ def _lookfor_generate_cache(module, import_modules, regenerate): or newly generated. """ - global _lookfor_caches # Local import to speed up numpy's import time. import inspect - if sys.version_info[0] >= 3: - # In Python3 stderr, stdout are text files. - from io import StringIO - else: - from StringIO import StringIO + from io import StringIO if module is None: module = "numpy" @@ -928,7 +902,7 @@ def _lookfor_generate_cache(module, import_modules, regenerate): sys.stdout = old_stdout sys.stderr = old_stderr # Catch SystemExit, too - except BaseException: + except (Exception, SystemExit): continue for n, v in _getmembers(item): @@ -979,93 +953,6 @@ def _getmembers(item): if hasattr(item, x)] return members -#----------------------------------------------------------------------------- - -# The following SafeEval class and company are adapted from Michael Spencer's -# ASPN Python Cookbook recipe: -# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/364469 -# Accordingly it is mostly Copyright 2006 by Michael Spencer. -# The recipe, like most of the other ASPN Python Cookbook recipes was made -# available under the Python license. -# http://www.python.org/license - -# It has been modified to: -# * handle unary -/+ -# * support True/False/None -# * raise SyntaxError instead of a custom exception. - -class SafeEval(object): - """ - Object to evaluate constant string expressions. - - This includes strings with lists, dicts and tuples using the abstract - syntax tree created by ``compiler.parse``. - - .. deprecated:: 1.10.0 - - See Also - -------- - safe_eval - - """ - def __init__(self): - # 2014-10-15, 1.10 - warnings.warn("SafeEval is deprecated in 1.10 and will be removed.", - DeprecationWarning, stacklevel=2) - - def visit(self, node): - cls = node.__class__ - meth = getattr(self, 'visit' + cls.__name__, self.default) - return meth(node) - - def default(self, node): - raise SyntaxError("Unsupported source construct: %s" - % node.__class__) - - def visitExpression(self, node): - return self.visit(node.body) - - def visitNum(self, node): - return node.n - - def visitStr(self, node): - return node.s - - def visitBytes(self, node): - return node.s - - def visitDict(self, node,**kw): - return dict([(self.visit(k), self.visit(v)) - for k, v in zip(node.keys, node.values)]) - - def visitTuple(self, node): - return tuple([self.visit(i) for i in node.elts]) - - def visitList(self, node): - return [self.visit(i) for i in node.elts] - - def visitUnaryOp(self, node): - import ast - if isinstance(node.op, ast.UAdd): - return +self.visit(node.operand) - elif isinstance(node.op, ast.USub): - return -self.visit(node.operand) - else: - raise SyntaxError("Unknown unary op: %r" % node.op) - - def visitName(self, node): - if node.id == 'False': - return False - elif node.id == 'True': - return True - elif node.id == 'None': - return None - else: - raise SyntaxError("Unknown name: %s" % node.id) - - def visitNameConstant(self, node): - return node.value - def safe_eval(source): """ @@ -1107,16 +994,15 @@ def safe_eval(source): >>> np.safe_eval('open("/home/user/.ssh/id_dsa").read()') Traceback (most recent call last): ... - SyntaxError: Unsupported source construct: compiler.ast.CallFunc + ValueError: malformed node or string: <_ast.Call object at 0x...> """ # Local import to speed up numpy's import time. import ast - return ast.literal_eval(source) -def _median_nancheck(data, result, axis, out): +def _median_nancheck(data, result, axis): """ Utility function to check median result from data for NaN values at the end and return NaN in that case. Input result can also be a MaskedArray. @@ -1124,39 +1010,58 @@ def _median_nancheck(data, result, axis, out): Parameters ---------- data : array - Input data to median function + Sorted input data to median function result : Array or MaskedArray - Result of median function - axis : {int, sequence of int, None}, optional - Axis or axes along which the median was computed. - out : ndarray, optional - Output array in which to place the result. + Result of median function. + axis : int + Axis along which the median was computed. + Returns ------- - median : scalar or ndarray - Median or NaN in axes which contained NaN in the input. + result : scalar or ndarray + Median or NaN in axes which contained NaN in the input. If the input + was an array, NaN will be inserted in-place. If a scalar, either the + input itself or a scalar NaN. """ if data.size == 0: return result - data = np.moveaxis(data, axis, -1) - n = np.isnan(data[..., -1]) + n = np.isnan(data.take(-1, axis=axis)) # masked NaN values are ok if np.ma.isMaskedArray(n): n = n.filled(False) - if result.ndim == 0: - if n == True: - warnings.warn("Invalid value encountered in median", - RuntimeWarning, stacklevel=3) - if out is not None: - out[...] = data.dtype.type(np.nan) - result = out - else: - result = data.dtype.type(np.nan) - elif np.count_nonzero(n.ravel()) > 0: - warnings.warn("Invalid value encountered in median for" + - " %d results" % np.count_nonzero(n.ravel()), - RuntimeWarning, stacklevel=3) + if np.count_nonzero(n.ravel()) > 0: + # Without given output, it is possible that the current result is a + # numpy scalar, which is not writeable. If so, just return nan. + if isinstance(result, np.generic): + return data.dtype.type(np.nan) + result[n] = np.nan return result +def _opt_info(): + """ + Returns a string contains the supported CPU features by the current build. + + The string format can be explained as follows: + - dispatched features that are supported by the running machine + end with `*`. + - dispatched features that are "not" supported by the running machine + end with `?`. + - remained features are representing the baseline. + """ + from numpy.core._multiarray_umath import ( + __cpu_features__, __cpu_baseline__, __cpu_dispatch__ + ) + + if len(__cpu_baseline__) == 0 and len(__cpu_dispatch__) == 0: + return '' + + enabled_features = ' '.join(__cpu_baseline__) + for feature in __cpu_dispatch__: + if __cpu_features__[feature]: + enabled_features += f" {feature}*" + else: + enabled_features += f" {feature}?" + + return enabled_features #----------------------------------------------------------------------------- diff --git a/numpy/lib/utils.pyi b/numpy/lib/utils.pyi new file mode 100644 index 000000000000..f0a8797ad61e --- /dev/null +++ b/numpy/lib/utils.pyi @@ -0,0 +1,95 @@ +from ast import AST +from typing import ( + Any, + Callable, + List, + Mapping, + Optional, + overload, + Sequence, + Tuple, + TypeVar, + Union, + Protocol, +) + +from numpy import ndarray, generic + +from numpy.core.numerictypes import ( + issubclass_ as issubclass_, + issubdtype as issubdtype, + issubsctype as issubsctype, +) + +_T_contra = TypeVar("_T_contra", contravariant=True) +_FuncType = TypeVar("_FuncType", bound=Callable[..., Any]) + +# A file-like object opened in `w` mode +class _SupportsWrite(Protocol[_T_contra]): + def write(self, s: _T_contra, /) -> Any: ... + +__all__: List[str] + +class _Deprecate: + old_name: Optional[str] + new_name: Optional[str] + message: Optional[str] + def __init__( + self, + old_name: Optional[str] = ..., + new_name: Optional[str] = ..., + message: Optional[str] = ..., + ) -> None: ... + # NOTE: `__call__` can in principle take arbitrary `*args` and `**kwargs`, + # even though they aren't used for anything + def __call__(self, func: _FuncType) -> _FuncType: ... + +def get_include() -> str: ... + +@overload +def deprecate( + *, + old_name: Optional[str] = ..., + new_name: Optional[str] = ..., + message: Optional[str] = ..., +) -> _Deprecate: ... +@overload +def deprecate( + func: _FuncType, + /, + old_name: Optional[str] = ..., + new_name: Optional[str] = ..., + message: Optional[str] = ..., +) -> _FuncType: ... + +def deprecate_with_doc(msg: Optional[str]) -> _Deprecate: ... + +# NOTE: In practice `byte_bounds` can (potentially) take any object +# implementing the `__array_interface__` protocol. The caveat is +# that certain keys, marked as optional in the spec, must be present for +# `byte_bounds`. This concerns `"strides"` and `"data"`. +def byte_bounds(a: Union[generic, ndarray[Any, Any]]) -> Tuple[int, int]: ... + +def who(vardict: Optional[Mapping[str, ndarray[Any, Any]]] = ...) -> None: ... + +def info( + object: object = ..., + maxwidth: int = ..., + output: Optional[_SupportsWrite[str]] = ..., + toplevel: str = ..., +) -> None: ... + +def source( + object: object, + output: Optional[_SupportsWrite[str]] = ..., +) -> None: ... + +def lookfor( + what: str, + module: Union[None, str, Sequence[str]] = ..., + import_modules: bool = ..., + regenerate: bool = ..., + output: Optional[_SupportsWrite[str]] =..., +) -> None: ... + +def safe_eval(source: Union[str, AST]) -> Any: ... diff --git a/numpy/linalg/__init__.py b/numpy/linalg/__init__.py index 37bd275743a1..93943de3896c 100644 --- a/numpy/linalg/__init__.py +++ b/numpy/linalg/__init__.py @@ -1,55 +1,80 @@ """ -Core Linear Algebra Tools -========================= - -=============== ========================================================== -Linear algebra basics -========================================================================== -norm Vector or matrix norm -inv Inverse of a square matrix -solve Solve a linear system of equations -det Determinant of a square matrix -slogdet Logarithm of the determinant of a square matrix -lstsq Solve linear least-squares problem -pinv Pseudo-inverse (Moore-Penrose) calculated using a singular - value decomposition -matrix_power Integer power of a square matrix -matrix_rank Calculate matrix rank using an SVD-based method -=============== ========================================================== - -=============== ========================================================== -Eigenvalues and decompositions -========================================================================== -eig Eigenvalues and vectors of a square matrix -eigh Eigenvalues and eigenvectors of a Hermitian matrix -eigvals Eigenvalues of a square matrix -eigvalsh Eigenvalues of a Hermitian matrix -qr QR decomposition of a matrix -svd Singular value decomposition of a matrix -cholesky Cholesky decomposition of a matrix -=============== ========================================================== - -=============== ========================================================== -Tensor operations -========================================================================== -tensorsolve Solve a linear tensor equation -tensorinv Calculate an inverse of a tensor -=============== ========================================================== - -=============== ========================================================== +``numpy.linalg`` +================ + +The NumPy linear algebra functions rely on BLAS and LAPACK to provide efficient +low level implementations of standard linear algebra algorithms. Those +libraries may be provided by NumPy itself using C versions of a subset of their +reference implementations but, when possible, highly optimized libraries that +take advantage of specialized processor functionality are preferred. Examples +of such libraries are OpenBLAS, MKL (TM), and ATLAS. Because those libraries +are multithreaded and processor dependent, environmental variables and external +packages such as threadpoolctl may be needed to control the number of threads +or specify the processor architecture. + +- OpenBLAS: https://www.openblas.net/ +- threadpoolctl: https://github.com/joblib/threadpoolctl + +Please note that the most-used linear algebra functions in NumPy are present in +the main ``numpy`` namespace rather than in ``numpy.linalg``. There are: +``dot``, ``vdot``, ``inner``, ``outer``, ``matmul``, ``tensordot``, ``einsum``, +``einsum_path`` and ``kron``. + +Functions present in numpy.linalg are listed below. + + +Matrix and vector products +-------------------------- + + multi_dot + matrix_power + +Decompositions +-------------- + + cholesky + qr + svd + +Matrix eigenvalues +------------------ + + eig + eigh + eigvals + eigvalsh + +Norms and other numbers +----------------------- + + norm + cond + det + matrix_rank + slogdet + +Solving equations and inverting matrices +---------------------------------------- + + solve + tensorsolve + lstsq + inv + pinv + tensorinv + Exceptions -========================================================================== -LinAlgError Indicates a failed linear algebra operation -=============== ========================================================== +---------- -""" -from __future__ import division, absolute_import, print_function + LinAlgError +""" # To get sub-modules -from .info import __doc__ - +from . import linalg from .linalg import * -from numpy.testing._private.pytesttester import PytestTester +__all__ = linalg.__all__.copy() + +from numpy._pytesttester import PytestTester test = PytestTester(__name__) del PytestTester diff --git a/numpy/linalg/__init__.pyi b/numpy/linalg/__init__.pyi new file mode 100644 index 000000000000..d457f153a02e --- /dev/null +++ b/numpy/linalg/__init__.pyi @@ -0,0 +1,32 @@ +from typing import Any, List + +from numpy.linalg.linalg import ( + matrix_power as matrix_power, + solve as solve, + tensorsolve as tensorsolve, + tensorinv as tensorinv, + inv as inv, + cholesky as cholesky, + eigvals as eigvals, + eigvalsh as eigvalsh, + pinv as pinv, + slogdet as slogdet, + det as det, + svd as svd, + eig as eig, + eigh as eigh, + lstsq as lstsq, + norm as norm, + qr as qr, + cond as cond, + matrix_rank as matrix_rank, + multi_dot as multi_dot, +) + +from numpy._pytesttester import PytestTester + +__all__: List[str] +__path__: List[str] +test: PytestTester + +class LinAlgError(Exception): ... diff --git a/numpy/linalg/info.py b/numpy/linalg/info.py deleted file mode 100644 index 646ecda04aa9..000000000000 --- a/numpy/linalg/info.py +++ /dev/null @@ -1,37 +0,0 @@ -"""\ -Core Linear Algebra Tools -------------------------- -Linear algebra basics: - -- norm Vector or matrix norm -- inv Inverse of a square matrix -- solve Solve a linear system of equations -- det Determinant of a square matrix -- lstsq Solve linear least-squares problem -- pinv Pseudo-inverse (Moore-Penrose) calculated using a singular - value decomposition -- matrix_power Integer power of a square matrix - -Eigenvalues and decompositions: - -- eig Eigenvalues and vectors of a square matrix -- eigh Eigenvalues and eigenvectors of a Hermitian matrix -- eigvals Eigenvalues of a square matrix -- eigvalsh Eigenvalues of a Hermitian matrix -- qr QR decomposition of a matrix -- svd Singular value decomposition of a matrix -- cholesky Cholesky decomposition of a matrix - -Tensor operations: - -- tensorsolve Solve a linear tensor equation -- tensorinv Calculate an inverse of a tensor - -Exceptions: - -- LinAlgError Indicates a failed linear algebra operation - -""" -from __future__ import division, absolute_import, print_function - -depends = ['core'] diff --git a/numpy/linalg/lapack_lite/README.rst b/numpy/linalg/lapack_lite/README.rst index 1343d25f8a94..8baa1d8ff600 100644 --- a/numpy/linalg/lapack_lite/README.rst +++ b/numpy/linalg/lapack_lite/README.rst @@ -12,15 +12,18 @@ automatically from a directory of LAPACK source files. You'll need `plex 2.0.0dev`_, available from PyPI, installed to do the appropriate scrubbing. As of writing, **this is only available for python 2.7**, and is unlikely to ever be ported to python 3. +As a result, all the Python scripts in this directory must remain compatible +with Python 2.7, even though NumPy itself no longer supports this version, +until these scripts are rewritten to use something other than ``plex``. .. _plex 2.0.0dev: https://pypi.python.org/pypi/plex/ The routines that ``lapack_litemodule.c`` wraps are listed in ``wrapped_routines``, along with a few exceptions that aren't picked up properly. Assuming that you have an unpacked LAPACK source tree in -``~/LAPACK``, you generate the new routines in a directory ``new-lite/`` with:: +``/tmp/lapack-3.x.x``, you generate the new routines in this directory with:: -$ python2 ./make_lite.py wrapped_routines ~/LAPACK new-lite/ +$ ./make_lite.py wrapped_routines /tmp/lapack-3.x.x This will grab the right routines, with dependencies, put them into the appropriate ``f2c_*.f`` files, run ``f2c`` over them, then do some scrubbing diff --git a/numpy/linalg/lapack_lite/clapack_scrub.py b/numpy/linalg/lapack_lite/clapack_scrub.py index e72a39e64e98..fffd70910481 100644 --- a/numpy/linalg/lapack_lite/clapack_scrub.py +++ b/numpy/linalg/lapack_lite/clapack_scrub.py @@ -1,17 +1,17 @@ -#!/usr/bin/env python -from __future__ import division, absolute_import, print_function - -import sys, os +#!/usr/bin/env python2.7 +# WARNING! This a Python 2 script. Read README.rst for rationale. +import os import re +import sys + from plex import Scanner, Str, Lexicon, Opt, Bol, State, AnyChar, TEXT, IGNORE from plex.traditional import re as Re -PY2 = sys.version_info < (3, 0) +try: + from io import BytesIO as UStringIO # Python 2 +except ImportError: + from io import StringIO as UStringIO # Python 3 -if PY2: - from io import BytesIO as UStringIO -else: - from io import StringIO as UStringIO class MyScanner(Scanner): def __init__(self, info, name='<default>'): @@ -106,7 +106,7 @@ def cleanSource(source): source = re.sub(r'\n\n\n\n+', r'\n\n\n', source) return source -class LineQueue(object): +class LineQueue: def __init__(self): object.__init__(self) self._queue = [] @@ -228,15 +228,18 @@ def OutOfHeader(line): return lines.getValue() def removeSubroutinePrototypes(source): - expression = re.compile( - r'/[*] Subroutine [*]/^\s*(?:(?:inline|static)\s+){0,2}(?!else|typedef|return)\w+\s+\*?\s*(\w+)\s*\([^0]+\)\s*;?' - ) - lines = LineQueue() - for line in UStringIO(source): - if not expression.match(line): - lines.add(line) - - return lines.getValue() + # This function has never worked as advertised by its name: + # - "/* Subroutine */" declarations may span multiple lines and + # cannot be matched by a line by line approach. + # - The caret in the initial regex would prevent any match, even + # of single line "/* Subroutine */" declarations. + # + # While we could "fix" this function to do what the name implies + # it should do, we have no hint of what it should really do. + # + # Therefore we keep the existing (non-)functionaity, documenting + # this function as doing nothing at all. + return source def removeBuiltinFunctions(source): lines = LineQueue() @@ -294,9 +297,8 @@ def scrubSource(source, nsteps=None, verbose=False): if __name__ == '__main__': filename = sys.argv[1] outfilename = os.path.join(sys.argv[2], os.path.basename(filename)) - fo = open(filename, 'r') - source = fo.read() - fo.close() + with open(filename, 'r') as fo: + source = fo.read() if len(sys.argv) > 3: nsteps = int(sys.argv[3]) @@ -305,6 +307,5 @@ def scrubSource(source, nsteps=None, verbose=False): source = scrub_source(source, nsteps, verbose=True) - writefo = open(outfilename, 'w') - writefo.write(source) - writefo.close() + with open(outfilename, 'w') as writefo: + writefo.write(source) diff --git a/numpy/linalg/lapack_lite/f2c.c b/numpy/linalg/lapack_lite/f2c.c index 1114bef3b1c3..9a1e9cec1d2b 100644 --- a/numpy/linalg/lapack_lite/f2c.c +++ b/numpy/linalg/lapack_lite/f2c.c @@ -567,7 +567,7 @@ if( (abi = b->i) < 0.f) abi = - abi; if( abr <= abi ) { - /*Let IEEE Infinties handle this ;( */ + /*Let IEEE Infinities handle this ;( */ /*if(abi == 0) sig_die("complex division by zero", 1);*/ ratio = b->r / b->i ; @@ -603,7 +603,7 @@ if( (abi = b->i) < 0.) abi = - abi; if( abr <= abi ) { - /*Let IEEE Infinties handle this ;( */ + /*Let IEEE Infinities handle this ;( */ /*if(abi == 0) sig_die("complex division by zero", 1);*/ ratio = b->r / b->i ; diff --git a/numpy/linalg/lapack_lite/f2c.h b/numpy/linalg/lapack_lite/f2c.h index 80f1a12b19df..d3fbfc1771f8 100644 --- a/numpy/linalg/lapack_lite/f2c.h +++ b/numpy/linalg/lapack_lite/f2c.h @@ -8,15 +8,19 @@ #define F2C_INCLUDE #include <math.h> +#include "numpy/npy_common.h" +#include "npy_cblas.h" -typedef int integer; +#include "lapack_lite_names.h" + +typedef CBLAS_INT integer; typedef char *address; typedef short int shortint; typedef float real; typedef double doublereal; typedef struct { real r, i; } complex; typedef struct { doublereal r, i; } doublecomplex; -typedef int logical; +typedef CBLAS_INT logical; typedef short int shortlogical; typedef char logical1; typedef char integer1; @@ -37,9 +41,9 @@ typedef short flag; typedef short ftnlen; typedef short ftnint; #else -typedef int flag; -typedef int ftnlen; -typedef int ftnint; +typedef CBLAS_INT flag; +typedef CBLAS_INT ftnlen; +typedef CBLAS_INT ftnint; #endif /*external read, write*/ @@ -352,7 +356,7 @@ extern void s_copy(char *, char *, ftnlen, ftnlen); extern int s_paus(char *, ftnlen); extern integer s_rdfe(cilist *); extern integer s_rdue(cilist *); -extern integer s_rnge(char *, integer, char *, integer); +extern int s_rnge(char *, int, char *, int); extern integer s_rsfe(cilist *); extern integer s_rsfi(icilist *); extern integer s_rsle(cilist *); @@ -381,6 +385,9 @@ extern void z_log(doublecomplex *, doublecomplex *); extern void z_sin(doublecomplex *, doublecomplex *); extern void z_sqrt(doublecomplex *, doublecomplex *); +extern double f__cabs(double, double); +extern double f__cabsf(float, float); + #ifdef __cplusplus } #endif diff --git a/numpy/linalg/lapack_lite/f2c_blas.c b/numpy/linalg/lapack_lite/f2c_blas.c index 3af506b71247..65286892fb64 100644 --- a/numpy/linalg/lapack_lite/f2c_blas.c +++ b/numpy/linalg/lapack_lite/f2c_blas.c @@ -1,7 +1,7 @@ /* -NOTE: This is generated code. Look in Misc/lapack_lite for information on - remaking this file. -*/ + * NOTE: This is generated code. Look in numpy/linalg/lapack_lite for + * information on remaking this file. + */ #include "f2c.h" #ifdef HAVE_CONFIG diff --git a/numpy/linalg/lapack_lite/f2c_c_lapack.c b/numpy/linalg/lapack_lite/f2c_c_lapack.c index f52e1e1572c9..c36c0e3683a8 100644 --- a/numpy/linalg/lapack_lite/f2c_c_lapack.c +++ b/numpy/linalg/lapack_lite/f2c_c_lapack.c @@ -1,7 +1,7 @@ /* -NOTE: This is generated code. Look in Misc/lapack_lite for information on - remaking this file. -*/ + * NOTE: This is generated code. Look in numpy/linalg/lapack_lite for + * information on remaking this file. + */ #include "f2c.h" #ifdef HAVE_CONFIG diff --git a/numpy/linalg/lapack_lite/f2c_config.c b/numpy/linalg/lapack_lite/f2c_config.c index 2fe608227fae..3f59e026378f 100644 --- a/numpy/linalg/lapack_lite/f2c_config.c +++ b/numpy/linalg/lapack_lite/f2c_config.c @@ -1,7 +1,7 @@ /* -NOTE: This is generated code. Look in Misc/lapack_lite for information on - remaking this file. -*/ + * NOTE: This is generated code. Look in numpy/linalg/lapack_lite for + * information on remaking this file. + */ #include "f2c.h" #ifdef HAVE_CONFIG diff --git a/numpy/linalg/lapack_lite/f2c_d_lapack.c b/numpy/linalg/lapack_lite/f2c_d_lapack.c index 1a6675ef119a..233db74b996f 100644 --- a/numpy/linalg/lapack_lite/f2c_d_lapack.c +++ b/numpy/linalg/lapack_lite/f2c_d_lapack.c @@ -1,7 +1,7 @@ /* -NOTE: This is generated code. Look in Misc/lapack_lite for information on - remaking this file. -*/ + * NOTE: This is generated code. Look in numpy/linalg/lapack_lite for + * information on remaking this file. + */ #include "f2c.h" #ifdef HAVE_CONFIG diff --git a/numpy/linalg/lapack_lite/f2c_lapack.c b/numpy/linalg/lapack_lite/f2c_lapack.c index d956ddbbb744..752261044bf8 100644 --- a/numpy/linalg/lapack_lite/f2c_lapack.c +++ b/numpy/linalg/lapack_lite/f2c_lapack.c @@ -1,7 +1,7 @@ /* -NOTE: This is generated code. Look in Misc/lapack_lite for information on - remaking this file. -*/ + * NOTE: This is generated code. Look in numpy/linalg/lapack_lite for + * information on remaking this file. + */ #include "f2c.h" #ifdef HAVE_CONFIG diff --git a/numpy/linalg/lapack_lite/f2c_s_lapack.c b/numpy/linalg/lapack_lite/f2c_s_lapack.c index fccb1f58b553..2a32315c71b5 100644 --- a/numpy/linalg/lapack_lite/f2c_s_lapack.c +++ b/numpy/linalg/lapack_lite/f2c_s_lapack.c @@ -1,7 +1,7 @@ /* -NOTE: This is generated code. Look in Misc/lapack_lite for information on - remaking this file. -*/ + * NOTE: This is generated code. Look in numpy/linalg/lapack_lite for + * information on remaking this file. + */ #include "f2c.h" #ifdef HAVE_CONFIG diff --git a/numpy/linalg/lapack_lite/f2c_z_lapack.c b/numpy/linalg/lapack_lite/f2c_z_lapack.c index 0f11f2e725e3..8234eca41080 100644 --- a/numpy/linalg/lapack_lite/f2c_z_lapack.c +++ b/numpy/linalg/lapack_lite/f2c_z_lapack.c @@ -1,7 +1,7 @@ /* -NOTE: This is generated code. Look in Misc/lapack_lite for information on - remaking this file. -*/ + * NOTE: This is generated code. Look in numpy/linalg/lapack_lite for + * information on remaking this file. + */ #include "f2c.h" #ifdef HAVE_CONFIG diff --git a/numpy/linalg/lapack_lite/fortran.py b/numpy/linalg/lapack_lite/fortran.py index 3b6ac70f001b..2a5c9c05ee23 100644 --- a/numpy/linalg/lapack_lite/fortran.py +++ b/numpy/linalg/lapack_lite/fortran.py @@ -1,5 +1,4 @@ -from __future__ import division, absolute_import, print_function - +# WARNING! This a Python 2 script. Read README.rst for rationale. import re import itertools @@ -14,7 +13,7 @@ def isContinuation(line): COMMENT, STATEMENT, CONTINUATION = 0, 1, 2 def lineType(line): - """Return the type of a line of Fortan code.""" + """Return the type of a line of Fortran code.""" if isBlank(line): return COMMENT elif isLabel(line): @@ -26,7 +25,7 @@ def lineType(line): else: return STATEMENT -class LineIterator(object): +class LineIterator: """LineIterator(iterable) Return rstrip()'d lines from iterable, while keeping a count of the @@ -49,12 +48,12 @@ def __next__(self): next = __next__ -class PushbackIterator(object): +class PushbackIterator: """PushbackIterator(iterable) Return an iterator for which items can be pushed back into. Call the .pushback(item) method to have item returned as the next - value of .next(). + value of next(). """ def __init__(self, iterable): object.__init__(self) @@ -110,15 +109,14 @@ def getDependencies(filename): """For a Fortran source file, return a list of routines declared as EXTERNAL in it. """ - fo = open(filename) external_pat = re.compile(r'^\s*EXTERNAL\s', re.I) routines = [] - for lineno, line in fortranSourceLines(fo): - m = external_pat.match(line) - if m: - names = line = line[m.end():].strip().split(',') - names = [n.strip().lower() for n in names] - names = [n for n in names if n] - routines.extend(names) - fo.close() + with open(filename) as fo: + for lineno, line in fortranSourceLines(fo): + m = external_pat.match(line) + if m: + names = line[m.end():].strip().split(',') + names = [n.strip().lower() for n in names] + names = [n for n in names if n] + routines.extend(names) return routines diff --git a/numpy/linalg/lapack_lite/lapack_lite_names.h b/numpy/linalg/lapack_lite/lapack_lite_names.h new file mode 100644 index 000000000000..08fd7257de23 --- /dev/null +++ b/numpy/linalg/lapack_lite/lapack_lite_names.h @@ -0,0 +1,691 @@ +/* + * NOTE: This is generated code. Look in numpy/linalg/lapack_lite for + * information on remaking this file. + */ +/* + * This file renames all BLAS/LAPACK and f2c symbols to avoid + * dynamic symbol name conflicts, in cases where e.g. + * integer sizes do not match with 'standard' ABI. + */ +#define caxpy_ BLAS_FUNC(caxpy) +#define ccopy_ BLAS_FUNC(ccopy) +#define cdotc_ BLAS_FUNC(cdotc) +#define cdotu_ BLAS_FUNC(cdotu) +#define cgebak_ BLAS_FUNC(cgebak) +#define cgebal_ BLAS_FUNC(cgebal) +#define cgebd2_ BLAS_FUNC(cgebd2) +#define cgebrd_ BLAS_FUNC(cgebrd) +#define cgeev_ BLAS_FUNC(cgeev) +#define cgehd2_ BLAS_FUNC(cgehd2) +#define cgehrd_ BLAS_FUNC(cgehrd) +#define cgelq2_ BLAS_FUNC(cgelq2) +#define cgelqf_ BLAS_FUNC(cgelqf) +#define cgelsd_ BLAS_FUNC(cgelsd) +#define cgemm_ BLAS_FUNC(cgemm) +#define cgemv_ BLAS_FUNC(cgemv) +#define cgeqr2_ BLAS_FUNC(cgeqr2) +#define cgeqrf_ BLAS_FUNC(cgeqrf) +#define cgerc_ BLAS_FUNC(cgerc) +#define cgeru_ BLAS_FUNC(cgeru) +#define cgesdd_ BLAS_FUNC(cgesdd) +#define cgesv_ BLAS_FUNC(cgesv) +#define cgetf2_ BLAS_FUNC(cgetf2) +#define cgetrf_ BLAS_FUNC(cgetrf) +#define cgetrs_ BLAS_FUNC(cgetrs) +#define cheevd_ BLAS_FUNC(cheevd) +#define chemv_ BLAS_FUNC(chemv) +#define cher2_ BLAS_FUNC(cher2) +#define cher2k_ BLAS_FUNC(cher2k) +#define cherk_ BLAS_FUNC(cherk) +#define chetd2_ BLAS_FUNC(chetd2) +#define chetrd_ BLAS_FUNC(chetrd) +#define chseqr_ BLAS_FUNC(chseqr) +#define clabrd_ BLAS_FUNC(clabrd) +#define clacgv_ BLAS_FUNC(clacgv) +#define clacp2_ BLAS_FUNC(clacp2) +#define clacpy_ BLAS_FUNC(clacpy) +#define clacrm_ BLAS_FUNC(clacrm) +#define cladiv_ BLAS_FUNC(cladiv) +#define claed0_ BLAS_FUNC(claed0) +#define claed7_ BLAS_FUNC(claed7) +#define claed8_ BLAS_FUNC(claed8) +#define clahqr_ BLAS_FUNC(clahqr) +#define clahr2_ BLAS_FUNC(clahr2) +#define clals0_ BLAS_FUNC(clals0) +#define clalsa_ BLAS_FUNC(clalsa) +#define clalsd_ BLAS_FUNC(clalsd) +#define clange_ BLAS_FUNC(clange) +#define clanhe_ BLAS_FUNC(clanhe) +#define claqr0_ BLAS_FUNC(claqr0) +#define claqr1_ BLAS_FUNC(claqr1) +#define claqr2_ BLAS_FUNC(claqr2) +#define claqr3_ BLAS_FUNC(claqr3) +#define claqr4_ BLAS_FUNC(claqr4) +#define claqr5_ BLAS_FUNC(claqr5) +#define clarcm_ BLAS_FUNC(clarcm) +#define clarf_ BLAS_FUNC(clarf) +#define clarfb_ BLAS_FUNC(clarfb) +#define clarfg_ BLAS_FUNC(clarfg) +#define clarft_ BLAS_FUNC(clarft) +#define clartg_ BLAS_FUNC(clartg) +#define clascl_ BLAS_FUNC(clascl) +#define claset_ BLAS_FUNC(claset) +#define clasr_ BLAS_FUNC(clasr) +#define classq_ BLAS_FUNC(classq) +#define claswp_ BLAS_FUNC(claswp) +#define clatrd_ BLAS_FUNC(clatrd) +#define clatrs_ BLAS_FUNC(clatrs) +#define clauu2_ BLAS_FUNC(clauu2) +#define clauum_ BLAS_FUNC(clauum) +#define cpotf2_ BLAS_FUNC(cpotf2) +#define cpotrf_ BLAS_FUNC(cpotrf) +#define cpotri_ BLAS_FUNC(cpotri) +#define cpotrs_ BLAS_FUNC(cpotrs) +#define crot_ BLAS_FUNC(crot) +#define cscal_ BLAS_FUNC(cscal) +#define csrot_ BLAS_FUNC(csrot) +#define csscal_ BLAS_FUNC(csscal) +#define cstedc_ BLAS_FUNC(cstedc) +#define csteqr_ BLAS_FUNC(csteqr) +#define cswap_ BLAS_FUNC(cswap) +#define ctrevc_ BLAS_FUNC(ctrevc) +#define ctrexc_ BLAS_FUNC(ctrexc) +#define ctrmm_ BLAS_FUNC(ctrmm) +#define ctrmv_ BLAS_FUNC(ctrmv) +#define ctrsm_ BLAS_FUNC(ctrsm) +#define ctrsv_ BLAS_FUNC(ctrsv) +#define ctrti2_ BLAS_FUNC(ctrti2) +#define ctrtri_ BLAS_FUNC(ctrtri) +#define cung2r_ BLAS_FUNC(cung2r) +#define cungbr_ BLAS_FUNC(cungbr) +#define cunghr_ BLAS_FUNC(cunghr) +#define cungl2_ BLAS_FUNC(cungl2) +#define cunglq_ BLAS_FUNC(cunglq) +#define cungqr_ BLAS_FUNC(cungqr) +#define cunm2l_ BLAS_FUNC(cunm2l) +#define cunm2r_ BLAS_FUNC(cunm2r) +#define cunmbr_ BLAS_FUNC(cunmbr) +#define cunmhr_ BLAS_FUNC(cunmhr) +#define cunml2_ BLAS_FUNC(cunml2) +#define cunmlq_ BLAS_FUNC(cunmlq) +#define cunmql_ BLAS_FUNC(cunmql) +#define cunmqr_ BLAS_FUNC(cunmqr) +#define cunmtr_ BLAS_FUNC(cunmtr) +#define daxpy_ BLAS_FUNC(daxpy) +#define dbdsdc_ BLAS_FUNC(dbdsdc) +#define dbdsqr_ BLAS_FUNC(dbdsqr) +#define dcabs1_ BLAS_FUNC(dcabs1) +#define dcopy_ BLAS_FUNC(dcopy) +#define ddot_ BLAS_FUNC(ddot) +#define dgebak_ BLAS_FUNC(dgebak) +#define dgebal_ BLAS_FUNC(dgebal) +#define dgebd2_ BLAS_FUNC(dgebd2) +#define dgebrd_ BLAS_FUNC(dgebrd) +#define dgeev_ BLAS_FUNC(dgeev) +#define dgehd2_ BLAS_FUNC(dgehd2) +#define dgehrd_ BLAS_FUNC(dgehrd) +#define dgelq2_ BLAS_FUNC(dgelq2) +#define dgelqf_ BLAS_FUNC(dgelqf) +#define dgelsd_ BLAS_FUNC(dgelsd) +#define dgemm_ BLAS_FUNC(dgemm) +#define dgemv_ BLAS_FUNC(dgemv) +#define dgeqr2_ BLAS_FUNC(dgeqr2) +#define dgeqrf_ BLAS_FUNC(dgeqrf) +#define dger_ BLAS_FUNC(dger) +#define dgesdd_ BLAS_FUNC(dgesdd) +#define dgesv_ BLAS_FUNC(dgesv) +#define dgetf2_ BLAS_FUNC(dgetf2) +#define dgetrf_ BLAS_FUNC(dgetrf) +#define dgetrs_ BLAS_FUNC(dgetrs) +#define dhseqr_ BLAS_FUNC(dhseqr) +#define disnan_ BLAS_FUNC(disnan) +#define dlabad_ BLAS_FUNC(dlabad) +#define dlabrd_ BLAS_FUNC(dlabrd) +#define dlacpy_ BLAS_FUNC(dlacpy) +#define dladiv_ BLAS_FUNC(dladiv) +#define dlae2_ BLAS_FUNC(dlae2) +#define dlaed0_ BLAS_FUNC(dlaed0) +#define dlaed1_ BLAS_FUNC(dlaed1) +#define dlaed2_ BLAS_FUNC(dlaed2) +#define dlaed3_ BLAS_FUNC(dlaed3) +#define dlaed4_ BLAS_FUNC(dlaed4) +#define dlaed5_ BLAS_FUNC(dlaed5) +#define dlaed6_ BLAS_FUNC(dlaed6) +#define dlaed7_ BLAS_FUNC(dlaed7) +#define dlaed8_ BLAS_FUNC(dlaed8) +#define dlaed9_ BLAS_FUNC(dlaed9) +#define dlaeda_ BLAS_FUNC(dlaeda) +#define dlaev2_ BLAS_FUNC(dlaev2) +#define dlaexc_ BLAS_FUNC(dlaexc) +#define dlahqr_ BLAS_FUNC(dlahqr) +#define dlahr2_ BLAS_FUNC(dlahr2) +#define dlaisnan_ BLAS_FUNC(dlaisnan) +#define dlaln2_ BLAS_FUNC(dlaln2) +#define dlals0_ BLAS_FUNC(dlals0) +#define dlalsa_ BLAS_FUNC(dlalsa) +#define dlalsd_ BLAS_FUNC(dlalsd) +#define dlamc1_ BLAS_FUNC(dlamc1) +#define dlamc2_ BLAS_FUNC(dlamc2) +#define dlamc3_ BLAS_FUNC(dlamc3) +#define dlamc4_ BLAS_FUNC(dlamc4) +#define dlamc5_ BLAS_FUNC(dlamc5) +#define dlamch_ BLAS_FUNC(dlamch) +#define dlamrg_ BLAS_FUNC(dlamrg) +#define dlange_ BLAS_FUNC(dlange) +#define dlanst_ BLAS_FUNC(dlanst) +#define dlansy_ BLAS_FUNC(dlansy) +#define dlanv2_ BLAS_FUNC(dlanv2) +#define dlapy2_ BLAS_FUNC(dlapy2) +#define dlapy3_ BLAS_FUNC(dlapy3) +#define dlaqr0_ BLAS_FUNC(dlaqr0) +#define dlaqr1_ BLAS_FUNC(dlaqr1) +#define dlaqr2_ BLAS_FUNC(dlaqr2) +#define dlaqr3_ BLAS_FUNC(dlaqr3) +#define dlaqr4_ BLAS_FUNC(dlaqr4) +#define dlaqr5_ BLAS_FUNC(dlaqr5) +#define dlarf_ BLAS_FUNC(dlarf) +#define dlarfb_ BLAS_FUNC(dlarfb) +#define dlarfg_ BLAS_FUNC(dlarfg) +#define dlarft_ BLAS_FUNC(dlarft) +#define dlarfx_ BLAS_FUNC(dlarfx) +#define dlartg_ BLAS_FUNC(dlartg) +#define dlas2_ BLAS_FUNC(dlas2) +#define dlascl_ BLAS_FUNC(dlascl) +#define dlasd0_ BLAS_FUNC(dlasd0) +#define dlasd1_ BLAS_FUNC(dlasd1) +#define dlasd2_ BLAS_FUNC(dlasd2) +#define dlasd3_ BLAS_FUNC(dlasd3) +#define dlasd4_ BLAS_FUNC(dlasd4) +#define dlasd5_ BLAS_FUNC(dlasd5) +#define dlasd6_ BLAS_FUNC(dlasd6) +#define dlasd7_ BLAS_FUNC(dlasd7) +#define dlasd8_ BLAS_FUNC(dlasd8) +#define dlasda_ BLAS_FUNC(dlasda) +#define dlasdq_ BLAS_FUNC(dlasdq) +#define dlasdt_ BLAS_FUNC(dlasdt) +#define dlaset_ BLAS_FUNC(dlaset) +#define dlasq1_ BLAS_FUNC(dlasq1) +#define dlasq2_ BLAS_FUNC(dlasq2) +#define dlasq3_ BLAS_FUNC(dlasq3) +#define dlasq4_ BLAS_FUNC(dlasq4) +#define dlasq5_ BLAS_FUNC(dlasq5) +#define dlasq6_ BLAS_FUNC(dlasq6) +#define dlasr_ BLAS_FUNC(dlasr) +#define dlasrt_ BLAS_FUNC(dlasrt) +#define dlassq_ BLAS_FUNC(dlassq) +#define dlasv2_ BLAS_FUNC(dlasv2) +#define dlaswp_ BLAS_FUNC(dlaswp) +#define dlasy2_ BLAS_FUNC(dlasy2) +#define dlatrd_ BLAS_FUNC(dlatrd) +#define dlauu2_ BLAS_FUNC(dlauu2) +#define dlauum_ BLAS_FUNC(dlauum) +#define dnrm2_ BLAS_FUNC(dnrm2) +#define dorg2r_ BLAS_FUNC(dorg2r) +#define dorgbr_ BLAS_FUNC(dorgbr) +#define dorghr_ BLAS_FUNC(dorghr) +#define dorgl2_ BLAS_FUNC(dorgl2) +#define dorglq_ BLAS_FUNC(dorglq) +#define dorgqr_ BLAS_FUNC(dorgqr) +#define dorm2l_ BLAS_FUNC(dorm2l) +#define dorm2r_ BLAS_FUNC(dorm2r) +#define dormbr_ BLAS_FUNC(dormbr) +#define dormhr_ BLAS_FUNC(dormhr) +#define dorml2_ BLAS_FUNC(dorml2) +#define dormlq_ BLAS_FUNC(dormlq) +#define dormql_ BLAS_FUNC(dormql) +#define dormqr_ BLAS_FUNC(dormqr) +#define dormtr_ BLAS_FUNC(dormtr) +#define dpotf2_ BLAS_FUNC(dpotf2) +#define dpotrf_ BLAS_FUNC(dpotrf) +#define dpotri_ BLAS_FUNC(dpotri) +#define dpotrs_ BLAS_FUNC(dpotrs) +#define drot_ BLAS_FUNC(drot) +#define dscal_ BLAS_FUNC(dscal) +#define dstedc_ BLAS_FUNC(dstedc) +#define dsteqr_ BLAS_FUNC(dsteqr) +#define dsterf_ BLAS_FUNC(dsterf) +#define dswap_ BLAS_FUNC(dswap) +#define dsyevd_ BLAS_FUNC(dsyevd) +#define dsymv_ BLAS_FUNC(dsymv) +#define dsyr2_ BLAS_FUNC(dsyr2) +#define dsyr2k_ BLAS_FUNC(dsyr2k) +#define dsyrk_ BLAS_FUNC(dsyrk) +#define dsytd2_ BLAS_FUNC(dsytd2) +#define dsytrd_ BLAS_FUNC(dsytrd) +#define dtrevc_ BLAS_FUNC(dtrevc) +#define dtrexc_ BLAS_FUNC(dtrexc) +#define dtrmm_ BLAS_FUNC(dtrmm) +#define dtrmv_ BLAS_FUNC(dtrmv) +#define dtrsm_ BLAS_FUNC(dtrsm) +#define dtrti2_ BLAS_FUNC(dtrti2) +#define dtrtri_ BLAS_FUNC(dtrtri) +#define dzasum_ BLAS_FUNC(dzasum) +#define dznrm2_ BLAS_FUNC(dznrm2) +#define icamax_ BLAS_FUNC(icamax) +#define idamax_ BLAS_FUNC(idamax) +#define ieeeck_ BLAS_FUNC(ieeeck) +#define ilaclc_ BLAS_FUNC(ilaclc) +#define ilaclr_ BLAS_FUNC(ilaclr) +#define iladlc_ BLAS_FUNC(iladlc) +#define iladlr_ BLAS_FUNC(iladlr) +#define ilaenv_ BLAS_FUNC(ilaenv) +#define ilaslc_ BLAS_FUNC(ilaslc) +#define ilaslr_ BLAS_FUNC(ilaslr) +#define ilazlc_ BLAS_FUNC(ilazlc) +#define ilazlr_ BLAS_FUNC(ilazlr) +#define iparmq_ BLAS_FUNC(iparmq) +#define isamax_ BLAS_FUNC(isamax) +#define izamax_ BLAS_FUNC(izamax) +#define lsame_ BLAS_FUNC(lsame) +#define saxpy_ BLAS_FUNC(saxpy) +#define sbdsdc_ BLAS_FUNC(sbdsdc) +#define sbdsqr_ BLAS_FUNC(sbdsqr) +#define scabs1_ BLAS_FUNC(scabs1) +#define scasum_ BLAS_FUNC(scasum) +#define scnrm2_ BLAS_FUNC(scnrm2) +#define scopy_ BLAS_FUNC(scopy) +#define sdot_ BLAS_FUNC(sdot) +#define sgebak_ BLAS_FUNC(sgebak) +#define sgebal_ BLAS_FUNC(sgebal) +#define sgebd2_ BLAS_FUNC(sgebd2) +#define sgebrd_ BLAS_FUNC(sgebrd) +#define sgeev_ BLAS_FUNC(sgeev) +#define sgehd2_ BLAS_FUNC(sgehd2) +#define sgehrd_ BLAS_FUNC(sgehrd) +#define sgelq2_ BLAS_FUNC(sgelq2) +#define sgelqf_ BLAS_FUNC(sgelqf) +#define sgelsd_ BLAS_FUNC(sgelsd) +#define sgemm_ BLAS_FUNC(sgemm) +#define sgemv_ BLAS_FUNC(sgemv) +#define sgeqr2_ BLAS_FUNC(sgeqr2) +#define sgeqrf_ BLAS_FUNC(sgeqrf) +#define sger_ BLAS_FUNC(sger) +#define sgesdd_ BLAS_FUNC(sgesdd) +#define sgesv_ BLAS_FUNC(sgesv) +#define sgetf2_ BLAS_FUNC(sgetf2) +#define sgetrf_ BLAS_FUNC(sgetrf) +#define sgetrs_ BLAS_FUNC(sgetrs) +#define shseqr_ BLAS_FUNC(shseqr) +#define sisnan_ BLAS_FUNC(sisnan) +#define slabad_ BLAS_FUNC(slabad) +#define slabrd_ BLAS_FUNC(slabrd) +#define slacpy_ BLAS_FUNC(slacpy) +#define sladiv_ BLAS_FUNC(sladiv) +#define slae2_ BLAS_FUNC(slae2) +#define slaed0_ BLAS_FUNC(slaed0) +#define slaed1_ BLAS_FUNC(slaed1) +#define slaed2_ BLAS_FUNC(slaed2) +#define slaed3_ BLAS_FUNC(slaed3) +#define slaed4_ BLAS_FUNC(slaed4) +#define slaed5_ BLAS_FUNC(slaed5) +#define slaed6_ BLAS_FUNC(slaed6) +#define slaed7_ BLAS_FUNC(slaed7) +#define slaed8_ BLAS_FUNC(slaed8) +#define slaed9_ BLAS_FUNC(slaed9) +#define slaeda_ BLAS_FUNC(slaeda) +#define slaev2_ BLAS_FUNC(slaev2) +#define slaexc_ BLAS_FUNC(slaexc) +#define slahqr_ BLAS_FUNC(slahqr) +#define slahr2_ BLAS_FUNC(slahr2) +#define slaisnan_ BLAS_FUNC(slaisnan) +#define slaln2_ BLAS_FUNC(slaln2) +#define slals0_ BLAS_FUNC(slals0) +#define slalsa_ BLAS_FUNC(slalsa) +#define slalsd_ BLAS_FUNC(slalsd) +#define slamc1_ BLAS_FUNC(slamc1) +#define slamc2_ BLAS_FUNC(slamc2) +#define slamc3_ BLAS_FUNC(slamc3) +#define slamc4_ BLAS_FUNC(slamc4) +#define slamc5_ BLAS_FUNC(slamc5) +#define slamch_ BLAS_FUNC(slamch) +#define slamrg_ BLAS_FUNC(slamrg) +#define slange_ BLAS_FUNC(slange) +#define slanst_ BLAS_FUNC(slanst) +#define slansy_ BLAS_FUNC(slansy) +#define slanv2_ BLAS_FUNC(slanv2) +#define slapy2_ BLAS_FUNC(slapy2) +#define slapy3_ BLAS_FUNC(slapy3) +#define slaqr0_ BLAS_FUNC(slaqr0) +#define slaqr1_ BLAS_FUNC(slaqr1) +#define slaqr2_ BLAS_FUNC(slaqr2) +#define slaqr3_ BLAS_FUNC(slaqr3) +#define slaqr4_ BLAS_FUNC(slaqr4) +#define slaqr5_ BLAS_FUNC(slaqr5) +#define slarf_ BLAS_FUNC(slarf) +#define slarfb_ BLAS_FUNC(slarfb) +#define slarfg_ BLAS_FUNC(slarfg) +#define slarft_ BLAS_FUNC(slarft) +#define slarfx_ BLAS_FUNC(slarfx) +#define slartg_ BLAS_FUNC(slartg) +#define slas2_ BLAS_FUNC(slas2) +#define slascl_ BLAS_FUNC(slascl) +#define slasd0_ BLAS_FUNC(slasd0) +#define slasd1_ BLAS_FUNC(slasd1) +#define slasd2_ BLAS_FUNC(slasd2) +#define slasd3_ BLAS_FUNC(slasd3) +#define slasd4_ BLAS_FUNC(slasd4) +#define slasd5_ BLAS_FUNC(slasd5) +#define slasd6_ BLAS_FUNC(slasd6) +#define slasd7_ BLAS_FUNC(slasd7) +#define slasd8_ BLAS_FUNC(slasd8) +#define slasda_ BLAS_FUNC(slasda) +#define slasdq_ BLAS_FUNC(slasdq) +#define slasdt_ BLAS_FUNC(slasdt) +#define slaset_ BLAS_FUNC(slaset) +#define slasq1_ BLAS_FUNC(slasq1) +#define slasq2_ BLAS_FUNC(slasq2) +#define slasq3_ BLAS_FUNC(slasq3) +#define slasq4_ BLAS_FUNC(slasq4) +#define slasq5_ BLAS_FUNC(slasq5) +#define slasq6_ BLAS_FUNC(slasq6) +#define slasr_ BLAS_FUNC(slasr) +#define slasrt_ BLAS_FUNC(slasrt) +#define slassq_ BLAS_FUNC(slassq) +#define slasv2_ BLAS_FUNC(slasv2) +#define slaswp_ BLAS_FUNC(slaswp) +#define slasy2_ BLAS_FUNC(slasy2) +#define slatrd_ BLAS_FUNC(slatrd) +#define slauu2_ BLAS_FUNC(slauu2) +#define slauum_ BLAS_FUNC(slauum) +#define snrm2_ BLAS_FUNC(snrm2) +#define sorg2r_ BLAS_FUNC(sorg2r) +#define sorgbr_ BLAS_FUNC(sorgbr) +#define sorghr_ BLAS_FUNC(sorghr) +#define sorgl2_ BLAS_FUNC(sorgl2) +#define sorglq_ BLAS_FUNC(sorglq) +#define sorgqr_ BLAS_FUNC(sorgqr) +#define sorm2l_ BLAS_FUNC(sorm2l) +#define sorm2r_ BLAS_FUNC(sorm2r) +#define sormbr_ BLAS_FUNC(sormbr) +#define sormhr_ BLAS_FUNC(sormhr) +#define sorml2_ BLAS_FUNC(sorml2) +#define sormlq_ BLAS_FUNC(sormlq) +#define sormql_ BLAS_FUNC(sormql) +#define sormqr_ BLAS_FUNC(sormqr) +#define sormtr_ BLAS_FUNC(sormtr) +#define spotf2_ BLAS_FUNC(spotf2) +#define spotrf_ BLAS_FUNC(spotrf) +#define spotri_ BLAS_FUNC(spotri) +#define spotrs_ BLAS_FUNC(spotrs) +#define srot_ BLAS_FUNC(srot) +#define sscal_ BLAS_FUNC(sscal) +#define sstedc_ BLAS_FUNC(sstedc) +#define ssteqr_ BLAS_FUNC(ssteqr) +#define ssterf_ BLAS_FUNC(ssterf) +#define sswap_ BLAS_FUNC(sswap) +#define ssyevd_ BLAS_FUNC(ssyevd) +#define ssymv_ BLAS_FUNC(ssymv) +#define ssyr2_ BLAS_FUNC(ssyr2) +#define ssyr2k_ BLAS_FUNC(ssyr2k) +#define ssyrk_ BLAS_FUNC(ssyrk) +#define ssytd2_ BLAS_FUNC(ssytd2) +#define ssytrd_ BLAS_FUNC(ssytrd) +#define strevc_ BLAS_FUNC(strevc) +#define strexc_ BLAS_FUNC(strexc) +#define strmm_ BLAS_FUNC(strmm) +#define strmv_ BLAS_FUNC(strmv) +#define strsm_ BLAS_FUNC(strsm) +#define strti2_ BLAS_FUNC(strti2) +#define strtri_ BLAS_FUNC(strtri) +#define xerbla_ BLAS_FUNC(xerbla) +#define zaxpy_ BLAS_FUNC(zaxpy) +#define zcopy_ BLAS_FUNC(zcopy) +#define zdotc_ BLAS_FUNC(zdotc) +#define zdotu_ BLAS_FUNC(zdotu) +#define zdrot_ BLAS_FUNC(zdrot) +#define zdscal_ BLAS_FUNC(zdscal) +#define zgebak_ BLAS_FUNC(zgebak) +#define zgebal_ BLAS_FUNC(zgebal) +#define zgebd2_ BLAS_FUNC(zgebd2) +#define zgebrd_ BLAS_FUNC(zgebrd) +#define zgeev_ BLAS_FUNC(zgeev) +#define zgehd2_ BLAS_FUNC(zgehd2) +#define zgehrd_ BLAS_FUNC(zgehrd) +#define zgelq2_ BLAS_FUNC(zgelq2) +#define zgelqf_ BLAS_FUNC(zgelqf) +#define zgelsd_ BLAS_FUNC(zgelsd) +#define zgemm_ BLAS_FUNC(zgemm) +#define zgemv_ BLAS_FUNC(zgemv) +#define zgeqr2_ BLAS_FUNC(zgeqr2) +#define zgeqrf_ BLAS_FUNC(zgeqrf) +#define zgerc_ BLAS_FUNC(zgerc) +#define zgeru_ BLAS_FUNC(zgeru) +#define zgesdd_ BLAS_FUNC(zgesdd) +#define zgesv_ BLAS_FUNC(zgesv) +#define zgetf2_ BLAS_FUNC(zgetf2) +#define zgetrf_ BLAS_FUNC(zgetrf) +#define zgetrs_ BLAS_FUNC(zgetrs) +#define zheevd_ BLAS_FUNC(zheevd) +#define zhemv_ BLAS_FUNC(zhemv) +#define zher2_ BLAS_FUNC(zher2) +#define zher2k_ BLAS_FUNC(zher2k) +#define zherk_ BLAS_FUNC(zherk) +#define zhetd2_ BLAS_FUNC(zhetd2) +#define zhetrd_ BLAS_FUNC(zhetrd) +#define zhseqr_ BLAS_FUNC(zhseqr) +#define zlabrd_ BLAS_FUNC(zlabrd) +#define zlacgv_ BLAS_FUNC(zlacgv) +#define zlacp2_ BLAS_FUNC(zlacp2) +#define zlacpy_ BLAS_FUNC(zlacpy) +#define zlacrm_ BLAS_FUNC(zlacrm) +#define zladiv_ BLAS_FUNC(zladiv) +#define zlaed0_ BLAS_FUNC(zlaed0) +#define zlaed7_ BLAS_FUNC(zlaed7) +#define zlaed8_ BLAS_FUNC(zlaed8) +#define zlahqr_ BLAS_FUNC(zlahqr) +#define zlahr2_ BLAS_FUNC(zlahr2) +#define zlals0_ BLAS_FUNC(zlals0) +#define zlalsa_ BLAS_FUNC(zlalsa) +#define zlalsd_ BLAS_FUNC(zlalsd) +#define zlange_ BLAS_FUNC(zlange) +#define zlanhe_ BLAS_FUNC(zlanhe) +#define zlaqr0_ BLAS_FUNC(zlaqr0) +#define zlaqr1_ BLAS_FUNC(zlaqr1) +#define zlaqr2_ BLAS_FUNC(zlaqr2) +#define zlaqr3_ BLAS_FUNC(zlaqr3) +#define zlaqr4_ BLAS_FUNC(zlaqr4) +#define zlaqr5_ BLAS_FUNC(zlaqr5) +#define zlarcm_ BLAS_FUNC(zlarcm) +#define zlarf_ BLAS_FUNC(zlarf) +#define zlarfb_ BLAS_FUNC(zlarfb) +#define zlarfg_ BLAS_FUNC(zlarfg) +#define zlarft_ BLAS_FUNC(zlarft) +#define zlartg_ BLAS_FUNC(zlartg) +#define zlascl_ BLAS_FUNC(zlascl) +#define zlaset_ BLAS_FUNC(zlaset) +#define zlasr_ BLAS_FUNC(zlasr) +#define zlassq_ BLAS_FUNC(zlassq) +#define zlaswp_ BLAS_FUNC(zlaswp) +#define zlatrd_ BLAS_FUNC(zlatrd) +#define zlatrs_ BLAS_FUNC(zlatrs) +#define zlauu2_ BLAS_FUNC(zlauu2) +#define zlauum_ BLAS_FUNC(zlauum) +#define zpotf2_ BLAS_FUNC(zpotf2) +#define zpotrf_ BLAS_FUNC(zpotrf) +#define zpotri_ BLAS_FUNC(zpotri) +#define zpotrs_ BLAS_FUNC(zpotrs) +#define zrot_ BLAS_FUNC(zrot) +#define zscal_ BLAS_FUNC(zscal) +#define zstedc_ BLAS_FUNC(zstedc) +#define zsteqr_ BLAS_FUNC(zsteqr) +#define zswap_ BLAS_FUNC(zswap) +#define ztrevc_ BLAS_FUNC(ztrevc) +#define ztrexc_ BLAS_FUNC(ztrexc) +#define ztrmm_ BLAS_FUNC(ztrmm) +#define ztrmv_ BLAS_FUNC(ztrmv) +#define ztrsm_ BLAS_FUNC(ztrsm) +#define ztrsv_ BLAS_FUNC(ztrsv) +#define ztrti2_ BLAS_FUNC(ztrti2) +#define ztrtri_ BLAS_FUNC(ztrtri) +#define zung2r_ BLAS_FUNC(zung2r) +#define zungbr_ BLAS_FUNC(zungbr) +#define zunghr_ BLAS_FUNC(zunghr) +#define zungl2_ BLAS_FUNC(zungl2) +#define zunglq_ BLAS_FUNC(zunglq) +#define zungqr_ BLAS_FUNC(zungqr) +#define zunm2l_ BLAS_FUNC(zunm2l) +#define zunm2r_ BLAS_FUNC(zunm2r) +#define zunmbr_ BLAS_FUNC(zunmbr) +#define zunmhr_ BLAS_FUNC(zunmhr) +#define zunml2_ BLAS_FUNC(zunml2) +#define zunmlq_ BLAS_FUNC(zunmlq) +#define zunmql_ BLAS_FUNC(zunmql) +#define zunmqr_ BLAS_FUNC(zunmqr) +#define zunmtr_ BLAS_FUNC(zunmtr) + +/* Symbols exported by f2c.c */ +#define abort_ numpy_lapack_lite_abort_ +#define c_abs numpy_lapack_lite_c_abs +#define c_cos numpy_lapack_lite_c_cos +#define c_div numpy_lapack_lite_c_div +#define c_exp numpy_lapack_lite_c_exp +#define c_log numpy_lapack_lite_c_log +#define c_sin numpy_lapack_lite_c_sin +#define c_sqrt numpy_lapack_lite_c_sqrt +#define d_abs numpy_lapack_lite_d_abs +#define d_acos numpy_lapack_lite_d_acos +#define d_asin numpy_lapack_lite_d_asin +#define d_atan numpy_lapack_lite_d_atan +#define d_atn2 numpy_lapack_lite_d_atn2 +#define d_cnjg numpy_lapack_lite_d_cnjg +#define d_cos numpy_lapack_lite_d_cos +#define d_cosh numpy_lapack_lite_d_cosh +#define d_dim numpy_lapack_lite_d_dim +#define d_exp numpy_lapack_lite_d_exp +#define d_imag numpy_lapack_lite_d_imag +#define d_int numpy_lapack_lite_d_int +#define d_lg10 numpy_lapack_lite_d_lg10 +#define d_log numpy_lapack_lite_d_log +#define d_mod numpy_lapack_lite_d_mod +#define d_nint numpy_lapack_lite_d_nint +#define d_prod numpy_lapack_lite_d_prod +#define d_sign numpy_lapack_lite_d_sign +#define d_sin numpy_lapack_lite_d_sin +#define d_sinh numpy_lapack_lite_d_sinh +#define d_sqrt numpy_lapack_lite_d_sqrt +#define d_tan numpy_lapack_lite_d_tan +#define d_tanh numpy_lapack_lite_d_tanh +#define derf_ numpy_lapack_lite_derf_ +#define derfc_ numpy_lapack_lite_derfc_ +#define do_fio numpy_lapack_lite_do_fio +#define do_lio numpy_lapack_lite_do_lio +#define do_uio numpy_lapack_lite_do_uio +#define e_rdfe numpy_lapack_lite_e_rdfe +#define e_rdue numpy_lapack_lite_e_rdue +#define e_rsfe numpy_lapack_lite_e_rsfe +#define e_rsfi numpy_lapack_lite_e_rsfi +#define e_rsle numpy_lapack_lite_e_rsle +#define e_rsli numpy_lapack_lite_e_rsli +#define e_rsue numpy_lapack_lite_e_rsue +#define e_wdfe numpy_lapack_lite_e_wdfe +#define e_wdue numpy_lapack_lite_e_wdue +#define e_wsfe numpy_lapack_lite_e_wsfe +#define e_wsfi numpy_lapack_lite_e_wsfi +#define e_wsle numpy_lapack_lite_e_wsle +#define e_wsli numpy_lapack_lite_e_wsli +#define e_wsue numpy_lapack_lite_e_wsue +#define ef1asc_ numpy_lapack_lite_ef1asc_ +#define ef1cmc_ numpy_lapack_lite_ef1cmc_ +#define erf_ numpy_lapack_lite_erf_ +#define erfc_ numpy_lapack_lite_erfc_ +#define f__cabs numpy_lapack_lite_f__cabs +#define f__cabsf numpy_lapack_lite_f__cabsf +#define f_back numpy_lapack_lite_f_back +#define f_clos numpy_lapack_lite_f_clos +#define f_end numpy_lapack_lite_f_end +#define f_exit numpy_lapack_lite_f_exit +#define f_inqu numpy_lapack_lite_f_inqu +#define f_open numpy_lapack_lite_f_open +#define f_rew numpy_lapack_lite_f_rew +#define flush_ numpy_lapack_lite_flush_ +#define getarg_ numpy_lapack_lite_getarg_ +#define getenv_ numpy_lapack_lite_getenv_ +#define h_abs numpy_lapack_lite_h_abs +#define h_dim numpy_lapack_lite_h_dim +#define h_dnnt numpy_lapack_lite_h_dnnt +#define h_indx numpy_lapack_lite_h_indx +#define h_len numpy_lapack_lite_h_len +#define h_mod numpy_lapack_lite_h_mod +#define h_nint numpy_lapack_lite_h_nint +#define h_sign numpy_lapack_lite_h_sign +#define hl_ge numpy_lapack_lite_hl_ge +#define hl_gt numpy_lapack_lite_hl_gt +#define hl_le numpy_lapack_lite_hl_le +#define hl_lt numpy_lapack_lite_hl_lt +#define i_abs numpy_lapack_lite_i_abs +#define i_dim numpy_lapack_lite_i_dim +#define i_dnnt numpy_lapack_lite_i_dnnt +#define i_indx numpy_lapack_lite_i_indx +#define i_len numpy_lapack_lite_i_len +#define i_mod numpy_lapack_lite_i_mod +#define i_nint numpy_lapack_lite_i_nint +#define i_sign numpy_lapack_lite_i_sign +#define iargc_ numpy_lapack_lite_iargc_ +#define l_ge numpy_lapack_lite_l_ge +#define l_gt numpy_lapack_lite_l_gt +#define l_le numpy_lapack_lite_l_le +#define l_lt numpy_lapack_lite_l_lt +#define pow_ci numpy_lapack_lite_pow_ci +#define pow_dd numpy_lapack_lite_pow_dd +#define pow_di numpy_lapack_lite_pow_di +#define pow_hh numpy_lapack_lite_pow_hh +#define pow_ii numpy_lapack_lite_pow_ii +#define pow_ri numpy_lapack_lite_pow_ri +#define pow_zi numpy_lapack_lite_pow_zi +#define pow_zz numpy_lapack_lite_pow_zz +#define r_abs numpy_lapack_lite_r_abs +#define r_acos numpy_lapack_lite_r_acos +#define r_asin numpy_lapack_lite_r_asin +#define r_atan numpy_lapack_lite_r_atan +#define r_atn2 numpy_lapack_lite_r_atn2 +#define r_cnjg numpy_lapack_lite_r_cnjg +#define r_cos numpy_lapack_lite_r_cos +#define r_cosh numpy_lapack_lite_r_cosh +#define r_dim numpy_lapack_lite_r_dim +#define r_exp numpy_lapack_lite_r_exp +#define r_imag numpy_lapack_lite_r_imag +#define r_int numpy_lapack_lite_r_int +#define r_lg10 numpy_lapack_lite_r_lg10 +#define r_log numpy_lapack_lite_r_log +#define r_mod numpy_lapack_lite_r_mod +#define r_nint numpy_lapack_lite_r_nint +#define r_sign numpy_lapack_lite_r_sign +#define r_sin numpy_lapack_lite_r_sin +#define r_sinh numpy_lapack_lite_r_sinh +#define r_sqrt numpy_lapack_lite_r_sqrt +#define r_tan numpy_lapack_lite_r_tan +#define r_tanh numpy_lapack_lite_r_tanh +#define s_cat numpy_lapack_lite_s_cat +#define s_cmp numpy_lapack_lite_s_cmp +#define s_copy numpy_lapack_lite_s_copy +#define s_paus numpy_lapack_lite_s_paus +#define s_rdfe numpy_lapack_lite_s_rdfe +#define s_rdue numpy_lapack_lite_s_rdue +#define s_rnge numpy_lapack_lite_s_rnge +#define s_rsfe numpy_lapack_lite_s_rsfe +#define s_rsfi numpy_lapack_lite_s_rsfi +#define s_rsle numpy_lapack_lite_s_rsle +#define s_rsli numpy_lapack_lite_s_rsli +#define s_rsne numpy_lapack_lite_s_rsne +#define s_rsni numpy_lapack_lite_s_rsni +#define s_rsue numpy_lapack_lite_s_rsue +#define s_stop numpy_lapack_lite_s_stop +#define s_wdfe numpy_lapack_lite_s_wdfe +#define s_wdue numpy_lapack_lite_s_wdue +#define s_wsfe numpy_lapack_lite_s_wsfe +#define s_wsfi numpy_lapack_lite_s_wsfi +#define s_wsle numpy_lapack_lite_s_wsle +#define s_wsli numpy_lapack_lite_s_wsli +#define s_wsne numpy_lapack_lite_s_wsne +#define s_wsni numpy_lapack_lite_s_wsni +#define s_wsue numpy_lapack_lite_s_wsue +#define sig_die numpy_lapack_lite_sig_die +#define signal_ numpy_lapack_lite_signal_ +#define system_ numpy_lapack_lite_system_ +#define z_abs numpy_lapack_lite_z_abs +#define z_cos numpy_lapack_lite_z_cos +#define z_div numpy_lapack_lite_z_div +#define z_exp numpy_lapack_lite_z_exp +#define z_log numpy_lapack_lite_z_log +#define z_sin numpy_lapack_lite_z_sin +#define z_sqrt numpy_lapack_lite_z_sqrt diff --git a/numpy/linalg/lapack_lite/make_lite.py b/numpy/linalg/lapack_lite/make_lite.py index 61102d6ab07e..ca8d4c62cc57 100755 --- a/numpy/linalg/lapack_lite/make_lite.py +++ b/numpy/linalg/lapack_lite/make_lite.py @@ -1,6 +1,7 @@ -#!/usr/bin/env python +#!/usr/bin/env python2.7 +# WARNING! This a Python 2 script. Read README.rst for rationale. """ -Usage: make_lite.py <wrapped_routines_file> <lapack_dir> <output_dir> +Usage: make_lite.py <wrapped_routines_file> <lapack_dir> Typical invocation: @@ -11,22 +12,19 @@ * patch """ -from __future__ import division, absolute_import, print_function - import sys import os +import re import subprocess import shutil import fortran import clapack_scrub -PY2 = sys.version_info < (3, 0) - -if PY2: - from distutils.spawn import find_executable as which -else: - from shutil import which +try: + from distutils.spawn import find_executable as which # Python 2 +except ImportError: + from shutil import which # Python 3 # Arguments to pass to f2c. You'll always want -A for ANSI C prototypes # Others of interest: -a to not make variables static by default @@ -35,11 +33,14 @@ # The header to add to the top of the f2c_*.c file. Note that dlamch_() calls # will be replaced by the macros below by clapack_scrub.scrub_source() -HEADER = '''\ +HEADER_BLURB = '''\ /* -NOTE: This is generated code. Look in Misc/lapack_lite for information on - remaking this file. -*/ + * NOTE: This is generated code. Look in numpy/linalg/lapack_lite for + * information on remaking this file. + */ +''' + +HEADER = HEADER_BLURB + '''\ #include "f2c.h" #ifdef HAVE_CONFIG @@ -65,7 +66,7 @@ #endif ''' -class FortranRoutine(object): +class FortranRoutine: """Wrapper for a Fortran routine in a file. """ type = 'generic' @@ -84,7 +85,8 @@ def dependencies(self): return self._dependencies def __repr__(self): - return "FortranRoutine({!r}, filename={!r})".format(self.name, self.filename) + return "FortranRoutine({!r}, filename={!r})".format(self.name, + self.filename) class UnknownFortranRoutine(FortranRoutine): """Wrapper for a Fortran routine for which the corresponding file @@ -97,7 +99,7 @@ def __init__(self, name): def dependencies(self): return [] -class FortranLibrary(object): +class FortranLibrary: """Container for a bunch of Fortran routines. """ def __init__(self, src_dirs): @@ -278,9 +280,55 @@ def scrubF2CSource(c_file): def ensure_executable(name): try: which(name) - except: + except Exception: raise SystemExit(name + ' not found') +def create_name_header(output_dir): + routine_re = re.compile(r'^ (subroutine|.* function)\s+(\w+)\(.*$', + re.I) + extern_re = re.compile(r'^extern [a-z]+ ([a-z0-9_]+)\(.*$') + + # BLAS/LAPACK symbols + symbols = set(['xerbla']) + for fn in os.listdir(output_dir): + fn = os.path.join(output_dir, fn) + + if not fn.endswith('.f'): + continue + + with open(fn, 'r') as f: + for line in f: + m = routine_re.match(line) + if m: + symbols.add(m.group(2).lower()) + + # f2c symbols + f2c_symbols = set() + with open('f2c.h', 'r') as f: + for line in f: + m = extern_re.match(line) + if m: + f2c_symbols.add(m.group(1)) + + with open(os.path.join(output_dir, 'lapack_lite_names.h'), 'w') as f: + f.write(HEADER_BLURB) + f.write( + "/*\n" + " * This file renames all BLAS/LAPACK and f2c symbols to avoid\n" + " * dynamic symbol name conflicts, in cases where e.g.\n" + " * integer sizes do not match with 'standard' ABI.\n" + " */\n") + + # Rename BLAS/LAPACK symbols + for name in sorted(symbols): + f.write("#define %s_ BLAS_FUNC(%s)\n" % (name, name)) + + # Rename also symbols that f2c exports itself + f.write("\n" + "/* Symbols exported by f2c.c */\n") + for name in sorted(f2c_symbols): + f.write("#define %s numpy_lapack_lite_%s\n" % (name, name)) + def main(): if len(sys.argv) != 3: print(__doc__) @@ -293,10 +341,7 @@ def main(): lapack_src_dir = sys.argv[2] output_dir = os.path.join(os.path.dirname(__file__), 'build') - try: - shutil.rmtree(output_dir) - except: - pass + shutil.rmtree(output_dir, ignore_errors=True) os.makedirs(output_dir) wrapped_routines, ignores = getWrappedRoutineNames(wrapped_routines_file) @@ -307,7 +352,7 @@ def main(): for typename in types: fortran_file = os.path.join(output_dir, 'f2c_%s.f' % typename) c_file = fortran_file[:-2] + '.c' - print('creating %s ...' % c_file) + print('creating %s ...' % c_file) routines = library.allRoutinesByType(typename) concatenateRoutines(routines, fortran_file) @@ -330,12 +375,14 @@ def main(): print() + create_name_header(output_dir) + for fname in os.listdir(output_dir): - if fname.endswith('.c'): + if fname.endswith('.c') or fname == 'lapack_lite_names.h': print('Copying ' + fname) shutil.copy( os.path.join(output_dir, fname), - os.path.dirname(__file__), + os.path.abspath(os.path.dirname(__file__)), ) diff --git a/numpy/linalg/lapack_lite/python_xerbla.c b/numpy/linalg/lapack_lite/python_xerbla.c index dfc195556b4b..37a41408be22 100644 --- a/numpy/linalg/lapack_lite/python_xerbla.c +++ b/numpy/linalg/lapack_lite/python_xerbla.c @@ -1,7 +1,8 @@ -#include "Python.h" +#define PY_SSIZE_T_CLEAN +#include <Python.h> -#undef c_abs -#include "f2c.h" +#include "numpy/npy_common.h" +#include "npy_cblas.h" /* From the original manpage: @@ -20,7 +21,7 @@ info: Number of the invalid parameter. */ -int xerbla_(char *srname, integer *info) +CBLAS_INT BLAS_FUNC(xerbla)(char *srname, CBLAS_INT *info) { static const char format[] = "On entry to %.*s" \ " parameter number %d had an illegal value"; @@ -38,7 +39,7 @@ int xerbla_(char *srname, integer *info) #ifdef WITH_THREAD save = PyGILState_Ensure(); #endif - PyOS_snprintf(buf, sizeof(buf), format, len, srname, *info); + PyOS_snprintf(buf, sizeof(buf), format, len, srname, (int)*info); PyErr_SetString(PyExc_ValueError, buf); #ifdef WITH_THREAD PyGILState_Release(save); diff --git a/numpy/linalg/lapack_litemodule.c b/numpy/linalg/lapack_litemodule.c index 696a6d87401c..2fed0f2b0478 100644 --- a/numpy/linalg/lapack_litemodule.c +++ b/numpy/linalg/lapack_litemodule.c @@ -4,48 +4,69 @@ More modifications by Jeff Whitaker */ #define NPY_NO_DEPRECATED_API NPY_API_VERSION -#include "Python.h" +#define PY_SSIZE_T_CLEAN +#include <Python.h> + #include "numpy/arrayobject.h" +#include "npy_cblas.h" + +#define FNAME(name) BLAS_FUNC(name) + +typedef CBLAS_INT fortran_int; + +#ifdef HAVE_BLAS_ILP64 + +#if NPY_BITSOF_SHORT == 64 +#define FINT_PYFMT "h" +#elif NPY_BITSOF_INT == 64 +#define FINT_PYFMT "i" +#elif NPY_BITSOF_LONG == 64 +#define FINT_PYFMT "l" +#elif NPY_BITSOF_LONGLONG == 64 +#define FINT_PYFMT "L" +#else +#error No compatible 64-bit integer size. \ + Please contact NumPy maintainers and give detailed information about your \ + compiler and platform, or set NPY_USE_BLAS64_=0 +#endif -#ifdef NO_APPEND_FORTRAN -# define FNAME(x) x #else -# define FNAME(x) x##_ +#define FINT_PYFMT "i" #endif typedef struct { float r, i; } f2c_complex; typedef struct { double r, i; } f2c_doublecomplex; /* typedef long int (*L_fp)(); */ -extern int FNAME(dgelsd)(int *m, int *n, int *nrhs, - double a[], int *lda, double b[], int *ldb, - double s[], double *rcond, int *rank, - double work[], int *lwork, int iwork[], int *info); +extern fortran_int FNAME(dgelsd)(fortran_int *m, fortran_int *n, fortran_int *nrhs, + double a[], fortran_int *lda, double b[], fortran_int *ldb, + double s[], double *rcond, fortran_int *rank, + double work[], fortran_int *lwork, fortran_int iwork[], fortran_int *info); -extern int FNAME(zgelsd)(int *m, int *n, int *nrhs, - f2c_doublecomplex a[], int *lda, - f2c_doublecomplex b[], int *ldb, - double s[], double *rcond, int *rank, - f2c_doublecomplex work[], int *lwork, - double rwork[], int iwork[], int *info); +extern fortran_int FNAME(zgelsd)(fortran_int *m, fortran_int *n, fortran_int *nrhs, + f2c_doublecomplex a[], fortran_int *lda, + f2c_doublecomplex b[], fortran_int *ldb, + double s[], double *rcond, fortran_int *rank, + f2c_doublecomplex work[], fortran_int *lwork, + double rwork[], fortran_int iwork[], fortran_int *info); -extern int FNAME(dgeqrf)(int *m, int *n, double a[], int *lda, +extern fortran_int FNAME(dgeqrf)(fortran_int *m, fortran_int *n, double a[], fortran_int *lda, double tau[], double work[], - int *lwork, int *info); + fortran_int *lwork, fortran_int *info); -extern int FNAME(zgeqrf)(int *m, int *n, f2c_doublecomplex a[], int *lda, +extern fortran_int FNAME(zgeqrf)(fortran_int *m, fortran_int *n, f2c_doublecomplex a[], fortran_int *lda, f2c_doublecomplex tau[], f2c_doublecomplex work[], - int *lwork, int *info); + fortran_int *lwork, fortran_int *info); -extern int FNAME(dorgqr)(int *m, int *n, int *k, double a[], int *lda, +extern fortran_int FNAME(dorgqr)(fortran_int *m, fortran_int *n, fortran_int *k, double a[], fortran_int *lda, double tau[], double work[], - int *lwork, int *info); + fortran_int *lwork, fortran_int *info); -extern int FNAME(zungqr)(int *m, int *n, int *k, f2c_doublecomplex a[], - int *lda, f2c_doublecomplex tau[], - f2c_doublecomplex work[], int *lwork, int *info); +extern fortran_int FNAME(zungqr)(fortran_int *m, fortran_int *n, fortran_int *k, f2c_doublecomplex a[], + fortran_int *lda, f2c_doublecomplex tau[], + f2c_doublecomplex work[], fortran_int *lwork, fortran_int *info); -extern int FNAME(xerbla)(char *srname, int *info); +extern fortran_int FNAME(xerbla)(char *srname, fortran_int *info); static PyObject *LapackError; @@ -90,27 +111,31 @@ check_object(PyObject *ob, int t, char *obname, #define FDATA(p) ((float *) PyArray_DATA((PyArrayObject *)p)) #define CDATA(p) ((f2c_complex *) PyArray_DATA((PyArrayObject *)p)) #define ZDATA(p) ((f2c_doublecomplex *) PyArray_DATA((PyArrayObject *)p)) -#define IDATA(p) ((int *) PyArray_DATA((PyArrayObject *)p)) +#define IDATA(p) ((fortran_int *) PyArray_DATA((PyArrayObject *)p)) static PyObject * lapack_lite_dgelsd(PyObject *NPY_UNUSED(self), PyObject *args) { - int lapack_lite_status; - int m; - int n; - int nrhs; + fortran_int lapack_lite_status; + fortran_int m; + fortran_int n; + fortran_int nrhs; PyObject *a; - int lda; + fortran_int lda; PyObject *b; - int ldb; + fortran_int ldb; PyObject *s; double rcond; - int rank; + fortran_int rank; PyObject *work; PyObject *iwork; - int lwork; - int info; - TRY(PyArg_ParseTuple(args,"iiiOiOiOdiOiOi:dgelsd", + fortran_int lwork; + fortran_int info; + + TRY(PyArg_ParseTuple(args, + (FINT_PYFMT FINT_PYFMT FINT_PYFMT "O" FINT_PYFMT "O" + FINT_PYFMT "O" "d" FINT_PYFMT "O" FINT_PYFMT "O" + FINT_PYFMT ":dgelsd"), &m,&n,&nrhs,&a,&lda,&b,&ldb,&s,&rcond, &rank,&work,&lwork,&iwork,&info)); @@ -118,7 +143,11 @@ lapack_lite_dgelsd(PyObject *NPY_UNUSED(self), PyObject *args) TRY(check_object(b,NPY_DOUBLE,"b","NPY_DOUBLE","dgelsd")); TRY(check_object(s,NPY_DOUBLE,"s","NPY_DOUBLE","dgelsd")); TRY(check_object(work,NPY_DOUBLE,"work","NPY_DOUBLE","dgelsd")); +#ifndef NPY_UMATH_USE_BLAS64_ TRY(check_object(iwork,NPY_INT,"iwork","NPY_INT","dgelsd")); +#else + TRY(check_object(iwork,NPY_INT64,"iwork","NPY_INT64","dgelsd")); +#endif lapack_lite_status = FNAME(dgelsd)(&m,&n,&nrhs,DDATA(a),&lda,DDATA(b),&ldb, @@ -128,8 +157,11 @@ lapack_lite_dgelsd(PyObject *NPY_UNUSED(self), PyObject *args) return NULL; } - return Py_BuildValue("{s:i,s:i,s:i,s:i,s:i,s:i,s:d,s:i,s:i,s:i}","dgelsd_", - lapack_lite_status,"m",m,"n",n,"nrhs",nrhs, + return Py_BuildValue(("{s:" FINT_PYFMT ",s:" FINT_PYFMT ",s:" FINT_PYFMT + ",s:" FINT_PYFMT ",s:" FINT_PYFMT ",s:" FINT_PYFMT + ",s:d,s:" FINT_PYFMT ",s:" FINT_PYFMT + ",s:" FINT_PYFMT "}"), + "dgelsd_",lapack_lite_status,"m",m,"n",n,"nrhs",nrhs, "lda",lda,"ldb",ldb,"rcond",rcond,"rank",rank, "lwork",lwork,"info",info); } @@ -137,13 +169,16 @@ lapack_lite_dgelsd(PyObject *NPY_UNUSED(self), PyObject *args) static PyObject * lapack_lite_dgeqrf(PyObject *NPY_UNUSED(self), PyObject *args) { - int lapack_lite_status; - int m, n, lwork; + fortran_int lapack_lite_status; + fortran_int m, n, lwork; PyObject *a, *tau, *work; - int lda; - int info; + fortran_int lda; + fortran_int info; - TRY(PyArg_ParseTuple(args,"iiOiOOii:dgeqrf",&m,&n,&a,&lda,&tau,&work,&lwork,&info)); + TRY(PyArg_ParseTuple(args, + (FINT_PYFMT FINT_PYFMT "O" FINT_PYFMT "OO" + FINT_PYFMT FINT_PYFMT ":dgeqrf"), + &m,&n,&a,&lda,&tau,&work,&lwork,&info)); /* check objects and convert to right storage order */ TRY(check_object(a,NPY_DOUBLE,"a","NPY_DOUBLE","dgeqrf")); @@ -157,7 +192,9 @@ lapack_lite_dgeqrf(PyObject *NPY_UNUSED(self), PyObject *args) return NULL; } - return Py_BuildValue("{s:i,s:i,s:i,s:i,s:i,s:i}","dgeqrf_", + return Py_BuildValue(("{s:" FINT_PYFMT ",s:" FINT_PYFMT ",s:" FINT_PYFMT + ",s:" FINT_PYFMT ",s:" FINT_PYFMT ",s:" FINT_PYFMT "}"), + "dgeqrf_", lapack_lite_status,"m",m,"n",n,"lda",lda, "lwork",lwork,"info",info); } @@ -166,13 +203,17 @@ lapack_lite_dgeqrf(PyObject *NPY_UNUSED(self), PyObject *args) static PyObject * lapack_lite_dorgqr(PyObject *NPY_UNUSED(self), PyObject *args) { - int lapack_lite_status; - int m, n, k, lwork; + fortran_int lapack_lite_status; + fortran_int m, n, k, lwork; PyObject *a, *tau, *work; - int lda; - int info; - - TRY(PyArg_ParseTuple(args,"iiiOiOOii:dorgqr", &m, &n, &k, &a, &lda, &tau, &work, &lwork, &info)); + fortran_int lda; + fortran_int info; + + TRY(PyArg_ParseTuple(args, + (FINT_PYFMT FINT_PYFMT FINT_PYFMT "O" + FINT_PYFMT "OO" FINT_PYFMT FINT_PYFMT + ":dorgqr"), + &m, &n, &k, &a, &lda, &tau, &work, &lwork, &info)); TRY(check_object(a,NPY_DOUBLE,"a","NPY_DOUBLE","dorgqr")); TRY(check_object(tau,NPY_DOUBLE,"tau","NPY_DOUBLE","dorgqr")); TRY(check_object(work,NPY_DOUBLE,"work","NPY_DOUBLE","dorgqr")); @@ -191,23 +232,26 @@ lapack_lite_dorgqr(PyObject *NPY_UNUSED(self), PyObject *args) static PyObject * lapack_lite_zgelsd(PyObject *NPY_UNUSED(self), PyObject *args) { - int lapack_lite_status; - int m; - int n; - int nrhs; + fortran_int lapack_lite_status; + fortran_int m; + fortran_int n; + fortran_int nrhs; PyObject *a; - int lda; + fortran_int lda; PyObject *b; - int ldb; + fortran_int ldb; PyObject *s; double rcond; - int rank; + fortran_int rank; PyObject *work; - int lwork; + fortran_int lwork; PyObject *rwork; PyObject *iwork; - int info; - TRY(PyArg_ParseTuple(args,"iiiOiOiOdiOiOOi:zgelsd", + fortran_int info; + TRY(PyArg_ParseTuple(args, + (FINT_PYFMT FINT_PYFMT FINT_PYFMT "O" FINT_PYFMT + "O" FINT_PYFMT "Od" FINT_PYFMT "O" FINT_PYFMT + "OO" FINT_PYFMT ":zgelsd"), &m,&n,&nrhs,&a,&lda,&b,&ldb,&s,&rcond, &rank,&work,&lwork,&rwork,&iwork,&info)); @@ -216,7 +260,11 @@ lapack_lite_zgelsd(PyObject *NPY_UNUSED(self), PyObject *args) TRY(check_object(s,NPY_DOUBLE,"s","NPY_DOUBLE","zgelsd")); TRY(check_object(work,NPY_CDOUBLE,"work","NPY_CDOUBLE","zgelsd")); TRY(check_object(rwork,NPY_DOUBLE,"rwork","NPY_DOUBLE","zgelsd")); +#ifndef NPY_UMATH_USE_BLAS64_ TRY(check_object(iwork,NPY_INT,"iwork","NPY_INT","zgelsd")); +#else + TRY(check_object(iwork,NPY_INT64,"iwork","NPY_INT64","zgelsd")); +#endif lapack_lite_status = FNAME(zgelsd)(&m,&n,&nrhs,ZDATA(a),&lda,ZDATA(b),&ldb,DDATA(s),&rcond, @@ -225,7 +273,11 @@ lapack_lite_zgelsd(PyObject *NPY_UNUSED(self), PyObject *args) return NULL; } - return Py_BuildValue("{s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i}","zgelsd_", + return Py_BuildValue(("{s:" FINT_PYFMT ",s:" FINT_PYFMT ",s:" FINT_PYFMT + ",s:" FINT_PYFMT ",s:" FINT_PYFMT ",s:" FINT_PYFMT + ",s:" FINT_PYFMT ",s:" FINT_PYFMT ",s:" FINT_PYFMT + "}"), + "zgelsd_", lapack_lite_status,"m",m,"n",n,"nrhs",nrhs,"lda",lda, "ldb",ldb,"rank",rank,"lwork",lwork,"info",info); } @@ -233,13 +285,16 @@ lapack_lite_zgelsd(PyObject *NPY_UNUSED(self), PyObject *args) static PyObject * lapack_lite_zgeqrf(PyObject *NPY_UNUSED(self), PyObject *args) { - int lapack_lite_status; - int m, n, lwork; + fortran_int lapack_lite_status; + fortran_int m, n, lwork; PyObject *a, *tau, *work; - int lda; - int info; + fortran_int lda; + fortran_int info; - TRY(PyArg_ParseTuple(args,"iiOiOOii:zgeqrf",&m,&n,&a,&lda,&tau,&work,&lwork,&info)); + TRY(PyArg_ParseTuple(args, + (FINT_PYFMT FINT_PYFMT "O" FINT_PYFMT "OO" + FINT_PYFMT "" FINT_PYFMT ":zgeqrf"), + &m,&n,&a,&lda,&tau,&work,&lwork,&info)); /* check objects and convert to right storage order */ TRY(check_object(a,NPY_CDOUBLE,"a","NPY_CDOUBLE","zgeqrf")); @@ -253,20 +308,27 @@ lapack_lite_zgeqrf(PyObject *NPY_UNUSED(self), PyObject *args) return NULL; } - return Py_BuildValue("{s:i,s:i,s:i,s:i,s:i,s:i}","zgeqrf_",lapack_lite_status,"m",m,"n",n,"lda",lda,"lwork",lwork,"info",info); + return Py_BuildValue(("{s:" FINT_PYFMT ",s:" FINT_PYFMT + ",s:" FINT_PYFMT ",s:" FINT_PYFMT + ",s:" FINT_PYFMT ",s:" FINT_PYFMT "}"), + "zgeqrf_",lapack_lite_status,"m",m,"n",n,"lda",lda,"lwork",lwork,"info",info); } static PyObject * lapack_lite_zungqr(PyObject *NPY_UNUSED(self), PyObject *args) { - int lapack_lite_status; - int m, n, k, lwork; + fortran_int lapack_lite_status; + fortran_int m, n, k, lwork; PyObject *a, *tau, *work; - int lda; - int info; - - TRY(PyArg_ParseTuple(args,"iiiOiOOii:zungqr", &m, &n, &k, &a, &lda, &tau, &work, &lwork, &info)); + fortran_int lda; + fortran_int info; + + TRY(PyArg_ParseTuple(args, + (FINT_PYFMT FINT_PYFMT FINT_PYFMT "O" + FINT_PYFMT "OO" FINT_PYFMT "" FINT_PYFMT + ":zungqr"), + &m, &n, &k, &a, &lda, &tau, &work, &lwork, &info)); TRY(check_object(a,NPY_CDOUBLE,"a","NPY_CDOUBLE","zungqr")); TRY(check_object(tau,NPY_CDOUBLE,"tau","NPY_CDOUBLE","zungqr")); TRY(check_object(work,NPY_CDOUBLE,"work","NPY_CDOUBLE","zungqr")); @@ -279,7 +341,8 @@ lapack_lite_zungqr(PyObject *NPY_UNUSED(self), PyObject *args) return NULL; } - return Py_BuildValue("{s:i,s:i}","zungqr_",lapack_lite_status, + return Py_BuildValue(("{s:" FINT_PYFMT ",s:" FINT_PYFMT "}"), + "zungqr_",lapack_lite_status, "info",info); } @@ -287,7 +350,7 @@ lapack_lite_zungqr(PyObject *NPY_UNUSED(self), PyObject *args) static PyObject * lapack_lite_xerbla(PyObject *NPY_UNUSED(self), PyObject *args) { - int info = -1; + fortran_int info = -1; NPY_BEGIN_THREADS_DEF; NPY_BEGIN_THREADS; @@ -315,7 +378,6 @@ static struct PyMethodDef lapack_lite_module_methods[] = { }; -#if PY_MAJOR_VERSION >= 3 static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "lapack_lite", @@ -327,32 +389,25 @@ static struct PyModuleDef moduledef = { NULL, NULL }; -#endif /* Initialization function for the module */ -#if PY_MAJOR_VERSION >= 3 -#define RETVAL(x) x PyMODINIT_FUNC PyInit_lapack_lite(void) -#else -#define RETVAL(x) -PyMODINIT_FUNC -initlapack_lite(void) -#endif { PyObject *m,*d; -#if PY_MAJOR_VERSION >= 3 m = PyModule_Create(&moduledef); -#else - m = Py_InitModule4("lapack_lite", lapack_lite_module_methods, - "", (PyObject*)NULL,PYTHON_API_VERSION); -#endif if (m == NULL) { - return RETVAL(NULL); + return NULL; } import_array(); d = PyModule_GetDict(m); LapackError = PyErr_NewException("lapack_lite.LapackError", NULL, NULL); PyDict_SetItemString(d, "LapackError", LapackError); - return RETVAL(m); +#ifdef HAVE_BLAS_ILP64 + PyDict_SetItemString(d, "_ilp64", Py_True); +#else + PyDict_SetItemString(d, "_ilp64", Py_False); +#endif + + return m; } diff --git a/numpy/linalg/linalg.py b/numpy/linalg/linalg.py index 98af0733bd5c..0c27e063175a 100644 --- a/numpy/linalg/linalg.py +++ b/numpy/linalg/linalg.py @@ -8,14 +8,13 @@ dgeev, zgeev, dgesdd, zgesdd, dgelsd, zgelsd, dsyevd, zheevd, dgetrf, zgetrf, dpotrf, zpotrf, dgeqrf, zgeqrf, zungqr, dorgqr. """ -from __future__ import division, absolute_import, print_function - __all__ = ['matrix_power', 'solve', 'tensorsolve', 'tensorinv', 'inv', 'cholesky', 'eigvals', 'eigvalsh', 'pinv', 'slogdet', 'det', 'svd', 'eig', 'eigh', 'lstsq', 'norm', 'qr', 'cond', 'matrix_rank', 'LinAlgError', 'multi_dot'] +import functools import operator import warnings @@ -25,22 +24,23 @@ add, multiply, sqrt, fastCopyAndTranspose, sum, isfinite, finfo, errstate, geterrobj, moveaxis, amin, amax, product, abs, atleast_2d, intp, asanyarray, object_, matmul, - swapaxes, divide, count_nonzero, isnan + swapaxes, divide, count_nonzero, isnan, sign, argsort, sort ) from numpy.core.multiarray import normalize_axis_index +from numpy.core.overrides import set_module +from numpy.core import overrides from numpy.lib.twodim_base import triu, eye -from numpy.linalg import lapack_lite, _umath_linalg +from numpy.linalg import _umath_linalg + + +array_function_dispatch = functools.partial( + overrides.array_function_dispatch, module='numpy.linalg') -# For Python2/3 compatibility -_N = b'N' -_V = b'V' -_A = b'A' -_S = b'S' -_L = b'L' fortran_int = intc -# Error object + +@set_module('numpy.linalg') class LinAlgError(Exception): """ Generic Python-exception-derived object raised by linalg functions. @@ -68,7 +68,6 @@ class LinAlgError(Exception): numpy.linalg.LinAlgError: Singular matrix """ - pass def _determine_error_states(): @@ -100,6 +99,10 @@ def _raise_linalgerror_svd_nonconvergence(err, flag): def _raise_linalgerror_lstsq(err, flag): raise LinAlgError("SVD did not converge in Linear Least Squares") +def _raise_linalgerror_qr(err, flag): + raise LinAlgError("Incorrect argument found while performing " + "QR factorization") + def get_linalg_error_extobj(callback): extobj = list(_linalg_error_extobj) # make a copy extobj[2] = callback @@ -129,15 +132,6 @@ def _realType(t, default=double): def _complexType(t, default=cdouble): return _complex_types_map.get(t, default) -def _linalgRealType(t): - """Cast the type t to either double or cdouble.""" - return double - -_complex_types_map = {single : csingle, - double : cdouble, - csingle : csingle, - cdouble : cdouble} - def _commonType(*arrays): # in lite version, use higher precision (always double or cdouble) result_type = single @@ -182,51 +176,41 @@ def _to_native_byte_order(*arrays): def _fastCopyAndTranspose(type, *arrays): cast_arrays = () for a in arrays: - if a.dtype.type is type: - cast_arrays = cast_arrays + (_fastCT(a),) - else: - cast_arrays = cast_arrays + (_fastCT(a.astype(type)),) + if a.dtype.type is not type: + a = a.astype(type) + cast_arrays = cast_arrays + (_fastCT(a),) if len(cast_arrays) == 1: return cast_arrays[0] else: return cast_arrays -def _assertRank2(*arrays): +def _assert_2d(*arrays): for a in arrays: if a.ndim != 2: raise LinAlgError('%d-dimensional array given. Array must be ' 'two-dimensional' % a.ndim) -def _assertRankAtLeast2(*arrays): +def _assert_stacked_2d(*arrays): for a in arrays: if a.ndim < 2: raise LinAlgError('%d-dimensional array given. Array must be ' 'at least two-dimensional' % a.ndim) -def _assertSquareness(*arrays): - for a in arrays: - if max(a.shape) != min(a.shape): - raise LinAlgError('Array must be square') - -def _assertNdSquareness(*arrays): +def _assert_stacked_square(*arrays): for a in arrays: m, n = a.shape[-2:] if m != n: raise LinAlgError('Last 2 dimensions of the array must be square') -def _assertFinite(*arrays): +def _assert_finite(*arrays): for a in arrays: - if not (isfinite(a).all()): + if not isfinite(a).all(): raise LinAlgError("Array must not contain infs or NaNs") -def _isEmpty2d(arr): +def _is_empty_2d(arr): # check size first for efficiency return arr.size == 0 and product(arr.shape[-2:]) == 0 -def _assertNoEmpty2d(*arrays): - for a in arrays: - if _isEmpty2d(a): - raise LinAlgError("Arrays cannot be empty") def transpose(a): """ @@ -247,6 +231,11 @@ def transpose(a): # Linear equations +def _tensorsolve_dispatcher(a, b, axes=None): + return (a, b) + + +@array_function_dispatch(_tensorsolve_dispatcher) def tensorsolve(a, b, axes=None): """ Solve the tensor equation ``a x = b`` for x. @@ -316,6 +305,12 @@ def tensorsolve(a, b, axes=None): res.shape = oldshape return res + +def _solve_dispatcher(a, b): + return (a, b) + + +@array_function_dispatch(_solve_dispatcher) def solve(a, b): """ Solve a linear matrix equation, or system of linear scalar equations. @@ -340,6 +335,10 @@ def solve(a, b): LinAlgError If `a` is singular or not square. + See Also + -------- + scipy.linalg.solve : Similar function in SciPy. + Notes ----- @@ -348,7 +347,7 @@ def solve(a, b): Broadcasting rules apply, see the `numpy.linalg` documentation for details. - The solutions are computed using LAPACK routine _gesv + The solutions are computed using LAPACK routine ``_gesv``. `a` must be square and of full-rank, i.e., all rows (or, equivalently, columns) must be linearly independent; if either is not true, use @@ -362,13 +361,13 @@ def solve(a, b): Examples -------- - Solve the system of equations ``3 * x0 + x1 = 9`` and ``x0 + 2 * x1 = 8``: + Solve the system of equations ``x0 + 2 * x1 = 1`` and ``3 * x0 + 5 * x1 = 2``: - >>> a = np.array([[3,1], [1,2]]) - >>> b = np.array([9,8]) + >>> a = np.array([[1, 2], [3, 5]]) + >>> b = np.array([1, 2]) >>> x = np.linalg.solve(a, b) >>> x - array([ 2., 3.]) + array([-1., 1.]) Check that the solution is correct: @@ -377,8 +376,8 @@ def solve(a, b): """ a, _ = _makearray(a) - _assertRankAtLeast2(a) - _assertNdSquareness(a) + _assert_stacked_2d(a) + _assert_stacked_square(a) b, wrap = _makearray(b) t, result_t = _commonType(a, b) @@ -396,6 +395,11 @@ def solve(a, b): return wrap(r.astype(result_t, copy=False)) +def _tensorinv_dispatcher(a, ind=None): + return (a,) + + +@array_function_dispatch(_tensorinv_dispatcher) def tensorinv(a, ind=2): """ Compute the 'inverse' of an N-dimensional array. @@ -465,6 +469,11 @@ def tensorinv(a, ind=2): # Matrix inversion +def _unary_dispatcher(a): + return (a,) + + +@array_function_dispatch(_unary_dispatcher) def inv(a): """ Compute the (multiplicative) inverse of a matrix. @@ -487,6 +496,10 @@ def inv(a): LinAlgError If `a` is not square or inversion fails. + See Also + -------- + scipy.linalg.inv : Similar function in SciPy. + Notes ----- @@ -516,15 +529,15 @@ def inv(a): >>> a = np.array([[[1., 2.], [3., 4.]], [[1, 3], [3, 5]]]) >>> inv(a) - array([[[-2. , 1. ], - [ 1.5, -0.5]], - [[-5. , 2. ], - [ 3. , -1. ]]]) + array([[[-2. , 1. ], + [ 1.5 , -0.5 ]], + [[-1.25, 0.75], + [ 0.75, -0.25]]]) """ a, wrap = _makearray(a) - _assertRankAtLeast2(a) - _assertNdSquareness(a) + _assert_stacked_2d(a) + _assert_stacked_square(a) t, result_t = _commonType(a) signature = 'D->D' if isComplexType(t) else 'd->d' @@ -533,6 +546,11 @@ def inv(a): return wrap(ainv.astype(result_t, copy=False)) +def _matrix_power_dispatcher(a, n): + return (a,) + + +@array_function_dispatch(_matrix_power_dispatcher) def matrix_power(a, n): """ Raise a square matrix to the (integer) power `n`. @@ -542,10 +560,12 @@ def matrix_power(a, n): of the same shape as M is returned. If ``n < 0``, the inverse is computed and then raised to the ``abs(n)``. + .. note:: Stacks of object matrices are not currently supported. + Parameters ---------- a : (..., M, M) array_like - Matrix to be "powered." + Matrix to be "powered". n : int The exponent can be any integer or long integer, positive, negative, or zero. @@ -596,13 +616,23 @@ def matrix_power(a, n): """ a = asanyarray(a) - _assertRankAtLeast2(a) - _assertNdSquareness(a) + _assert_stacked_2d(a) + _assert_stacked_square(a) try: n = operator.index(n) - except TypeError: - raise TypeError("exponent must be an integer") + except TypeError as e: + raise TypeError("exponent must be an integer") from e + + # Fall back on dot for object arrays. Object arrays are not supported by + # the current implementation of matmul using einsum + if a.dtype != object: + fmatmul = matmul + elif a.ndim == 2: + fmatmul = dot + else: + raise NotImplementedError( + "matrix_power not supported for stacks of object arrays") if n == 0: a = empty_like(a) @@ -618,26 +648,28 @@ def matrix_power(a, n): return a elif n == 2: - return matmul(a, a) + return fmatmul(a, a) elif n == 3: - return matmul(matmul(a, a), a) + return fmatmul(fmatmul(a, a), a) # Use binary decomposition to reduce the number of matrix multiplications. # Here, we iterate over the bits of n, from LSB to MSB, raise `a` to # increasing powers of 2, and multiply into the result as needed. z = result = None while n > 0: - z = a if z is None else matmul(z, z) + z = a if z is None else fmatmul(z, z) n, bit = divmod(n, 2) if bit: - result = z if result is None else matmul(result, z) + result = z if result is None else fmatmul(result, z) return result # Cholesky decomposition + +@array_function_dispatch(_unary_dispatcher) def cholesky(a): """ Cholesky decomposition. @@ -645,8 +677,10 @@ def cholesky(a): Return the Cholesky decomposition, `L * L.H`, of the square matrix `a`, where `L` is lower-triangular and .H is the conjugate transpose operator (which is the ordinary transpose if `a` is real-valued). `a` must be - Hermitian (symmetric if real-valued) and positive-definite. Only `L` is - actually returned. + Hermitian (symmetric if real-valued) and positive-definite. No + checking is performed to verify whether `a` is Hermitian or not. + In addition, only the lower-triangular and diagonal elements of `a` + are used. Only `L` is actually returned. Parameters ---------- @@ -666,6 +700,14 @@ def cholesky(a): If the decomposition fails, for example, if `a` is not positive-definite. + See Also + -------- + scipy.linalg.cholesky : Similar function in SciPy. + scipy.linalg.cholesky_banded : Cholesky decompose a banded Hermitian + positive-definite matrix. + scipy.linalg.cho_factor : Cholesky decomposition of a matrix, to use in + `scipy.linalg.cho_solve`. + Notes ----- @@ -692,21 +734,21 @@ def cholesky(a): -------- >>> A = np.array([[1,-2j],[2j,5]]) >>> A - array([[ 1.+0.j, 0.-2.j], + array([[ 1.+0.j, -0.-2.j], [ 0.+2.j, 5.+0.j]]) >>> L = np.linalg.cholesky(A) >>> L - array([[ 1.+0.j, 0.+0.j], - [ 0.+2.j, 1.+0.j]]) + array([[1.+0.j, 0.+0.j], + [0.+2.j, 1.+0.j]]) >>> np.dot(L, L.T.conj()) # verify that L * L.H = A - array([[ 1.+0.j, 0.-2.j], - [ 0.+2.j, 5.+0.j]]) + array([[1.+0.j, 0.-2.j], + [0.+2.j, 5.+0.j]]) >>> A = [[1,-2j],[2j,5]] # what happens if A is only array_like? >>> np.linalg.cholesky(A) # an ndarray object is returned - array([[ 1.+0.j, 0.+0.j], - [ 0.+2.j, 1.+0.j]]) + array([[1.+0.j, 0.+0.j], + [0.+2.j, 1.+0.j]]) >>> # But a matrix object is returned if A is a matrix object - >>> LA.cholesky(np.matrix(A)) + >>> np.linalg.cholesky(np.matrix(A)) matrix([[ 1.+0.j, 0.+0.j], [ 0.+2.j, 1.+0.j]]) @@ -714,15 +756,21 @@ def cholesky(a): extobj = get_linalg_error_extobj(_raise_linalgerror_nonposdef) gufunc = _umath_linalg.cholesky_lo a, wrap = _makearray(a) - _assertRankAtLeast2(a) - _assertNdSquareness(a) + _assert_stacked_2d(a) + _assert_stacked_square(a) t, result_t = _commonType(a) signature = 'D->D' if isComplexType(t) else 'd->d' r = gufunc(a, signature=signature, extobj=extobj) return wrap(r.astype(result_t, copy=False)) -# QR decompostion +# QR decomposition + +def _qr_dispatcher(a, mode=None): + return (a,) + + +@array_function_dispatch(_qr_dispatcher) def qr(a, mode='reduced'): """ Compute the qr factorization of a matrix. @@ -732,17 +780,16 @@ def qr(a, mode='reduced'): Parameters ---------- - a : array_like, shape (M, N) - Matrix to be factored. - mode : {'reduced', 'complete', 'r', 'raw', 'full', 'economic'}, optional + a : array_like, shape (..., M, N) + An array-like object with the dimensionality of at least 2. + mode : {'reduced', 'complete', 'r', 'raw'}, optional If K = min(M, N), then - * 'reduced' : returns q, r with dimensions (M, K), (K, N) (default) - * 'complete' : returns q, r with dimensions (M, M), (M, N) - * 'r' : returns r only with dimensions (K, N) - * 'raw' : returns h, tau with dimensions (N, M), (K,) - * 'full' : alias of 'reduced', deprecated - * 'economic' : returns h from 'raw', deprecated. + * 'reduced' : returns q, r with dimensions + (..., M, K), (..., K, N) (default) + * 'complete' : returns q, r with dimensions (..., M, M), (..., M, N) + * 'r' : returns r only with dimensions (..., K, N) + * 'raw' : returns h, tau with dimensions (..., N, M), (..., K,) The options 'reduced', 'complete, and 'raw' are new in numpy 1.8, see the notes for more information. The default is 'reduced', and to @@ -761,9 +808,13 @@ def qr(a, mode='reduced'): A matrix with orthonormal columns. When mode = 'complete' the result is an orthogonal/unitary matrix depending on whether or not a is real/complex. The determinant may be either +/- 1 in that - case. + case. In case the number of dimensions in the input array is + greater than 2 then a stack of the matrices with above properties + is returned. r : ndarray of float or complex, optional - The upper-triangular matrix. + The upper-triangular matrix or a stack of upper-triangular + matrices if the number of dimensions in the input array is greater + than 2. (h, tau) : ndarrays of np.double or np.cdouble, optional The array h contains the Householder reflectors that generate q along with r. The tau array contains scaling factors for the @@ -774,13 +825,18 @@ def qr(a, mode='reduced'): LinAlgError If factoring fails. + See Also + -------- + scipy.linalg.qr : Similar function in SciPy. + scipy.linalg.rq : Compute RQ decomposition of a matrix. + Notes ----- - This is an interface to the LAPACK routines dgeqrf, zgeqrf, - dorgqr, and zungqr. + This is an interface to the LAPACK routines ``dgeqrf``, ``zgeqrf``, + ``dorgqr``, and ``zungqr``. For more information on the qr factorization, see for example: - http://en.wikipedia.org/wiki/QR_factorization + https://en.wikipedia.org/wiki/QR_factorization Subclasses of `ndarray` are preserved except for the 'raw' mode. So if `a` is of type `matrix`, all the return values will be matrices too. @@ -804,11 +860,15 @@ def qr(a, mode='reduced'): >>> np.allclose(a, np.dot(q, r)) # a does equal qr True >>> r2 = np.linalg.qr(a, mode='r') - >>> r3 = np.linalg.qr(a, mode='economic') >>> np.allclose(r, r2) # mode='r' returns the same r as mode='full' True - >>> # But only triu parts are guaranteed equal when mode='economic' - >>> np.allclose(r, np.triu(r3[:6,:6], k=0)) + >>> a = np.random.normal(size=(3, 2, 2)) # Stack of 2 x 2 matrices as input + >>> q, r = np.linalg.qr(a) + >>> q.shape + (3, 2, 2) + >>> r.shape + (3, 2, 2) + >>> np.allclose(a, np.matmul(q, r)) True Example illustrating a common use of `qr`: solving of least squares @@ -834,9 +894,9 @@ def qr(a, mode='reduced'): [1, 1], [2, 1]]) >>> b = np.array([1, 0, 2, 1]) - >>> q, r = LA.qr(A) + >>> q, r = np.linalg.qr(A) >>> p = np.dot(q.T, b) - >>> np.dot(LA.inv(r), p) + >>> np.dot(np.linalg.inv(r), p) array([ 1.1e-16, 1.0e+00]) """ @@ -846,98 +906,74 @@ def qr(a, mode='reduced'): msg = "".join(( "The 'full' option is deprecated in favor of 'reduced'.\n", "For backward compatibility let mode default.")) - warnings.warn(msg, DeprecationWarning, stacklevel=2) + warnings.warn(msg, DeprecationWarning, stacklevel=3) mode = 'reduced' elif mode in ('e', 'economic'): # 2013-04-01, 1.8 msg = "The 'economic' option is deprecated." - warnings.warn(msg, DeprecationWarning, stacklevel=2) + warnings.warn(msg, DeprecationWarning, stacklevel=3) mode = 'economic' else: - raise ValueError("Unrecognized mode '%s'" % mode) + raise ValueError(f"Unrecognized mode '{mode}'") a, wrap = _makearray(a) - _assertRank2(a) - _assertNoEmpty2d(a) - m, n = a.shape + _assert_stacked_2d(a) + m, n = a.shape[-2:] t, result_t = _commonType(a) - a = _fastCopyAndTranspose(t, a) + a = a.astype(t, copy=True) a = _to_native_byte_order(a) mn = min(m, n) - tau = zeros((mn,), t) - if isComplexType(t): - lapack_routine = lapack_lite.zgeqrf - routine_name = 'zgeqrf' + + if m <= n: + gufunc = _umath_linalg.qr_r_raw_m else: - lapack_routine = lapack_lite.dgeqrf - routine_name = 'dgeqrf' - - # calculate optimal size of work data 'work' - lwork = 1 - work = zeros((lwork,), t) - results = lapack_routine(m, n, a, m, tau, work, -1, 0) - if results['info'] != 0: - raise LinAlgError('%s returns %d' % (routine_name, results['info'])) - - # do qr decomposition - lwork = int(abs(work[0])) - work = zeros((lwork,), t) - results = lapack_routine(m, n, a, m, tau, work, lwork, 0) - if results['info'] != 0: - raise LinAlgError('%s returns %d' % (routine_name, results['info'])) + gufunc = _umath_linalg.qr_r_raw_n + + signature = 'D->D' if isComplexType(t) else 'd->d' + extobj = get_linalg_error_extobj(_raise_linalgerror_qr) + tau = gufunc(a, signature=signature, extobj=extobj) # handle modes that don't return q if mode == 'r': - r = _fastCopyAndTranspose(result_t, a[:, :mn]) - return wrap(triu(r)) + r = triu(a[..., :mn, :]) + r = r.astype(result_t, copy=False) + return wrap(r) if mode == 'raw': - return a, tau + q = transpose(a) + q = q.astype(result_t, copy=False) + tau = tau.astype(result_t, copy=False) + return wrap(q), tau if mode == 'economic': - if t != result_t : - a = a.astype(result_t, copy=False) - return wrap(a.T) + a = a.astype(result_t, copy=False) + return wrap(a) - # generate q from a + # mc is the number of columns in the resulting q + # matrix. If the mode is complete then it is + # same as number of rows, and if the mode is reduced, + # then it is the minimum of number of rows and columns. if mode == 'complete' and m > n: mc = m - q = empty((m, m), t) + gufunc = _umath_linalg.qr_complete else: mc = mn - q = empty((n, m), t) - q[:n] = a - - if isComplexType(t): - lapack_routine = lapack_lite.zungqr - routine_name = 'zungqr' - else: - lapack_routine = lapack_lite.dorgqr - routine_name = 'dorgqr' - - # determine optimal lwork - lwork = 1 - work = zeros((lwork,), t) - results = lapack_routine(m, mc, mn, q, m, tau, work, -1, 0) - if results['info'] != 0: - raise LinAlgError('%s returns %d' % (routine_name, results['info'])) + gufunc = _umath_linalg.qr_reduced - # compute q - lwork = int(abs(work[0])) - work = zeros((lwork,), t) - results = lapack_routine(m, mc, mn, q, m, tau, work, lwork, 0) - if results['info'] != 0: - raise LinAlgError('%s returns %d' % (routine_name, results['info'])) - - q = _fastCopyAndTranspose(result_t, q[:mc]) - r = _fastCopyAndTranspose(result_t, a[:, :mc]) + signature = 'DD->D' if isComplexType(t) else 'dd->d' + extobj = get_linalg_error_extobj(_raise_linalgerror_qr) + q = gufunc(a, tau, signature=signature, extobj=extobj) + r = triu(a[..., :mc, :]) - return wrap(q), wrap(triu(r)) + q = q.astype(result_t, copy=False) + r = r.astype(result_t, copy=False) + return wrap(q), wrap(r) # Eigenvalues +@array_function_dispatch(_unary_dispatcher) def eigvals(a): """ Compute the eigenvalues of a general matrix. @@ -965,8 +1001,11 @@ def eigvals(a): See Also -------- eig : eigenvalues and right eigenvectors of general arrays - eigvalsh : eigenvalues of symmetric or Hermitian arrays. - eigh : eigenvalues and eigenvectors of symmetric/Hermitian arrays. + eigvalsh : eigenvalues of real symmetric or complex Hermitian + (conjugate symmetric) arrays. + eigh : eigenvalues and eigenvectors of real symmetric or complex + Hermitian (conjugate symmetric) arrays. + scipy.linalg.eigvals : Similar function in SciPy. Notes ----- @@ -976,7 +1015,7 @@ def eigvals(a): Broadcasting rules apply, see the `numpy.linalg` documentation for details. - This is implemented using the _geev LAPACK routines which compute + This is implemented using the ``_geev`` LAPACK routines which compute the eigenvalues and eigenvectors of general square arrays. Examples @@ -994,7 +1033,7 @@ def eigvals(a): >>> LA.norm(Q[0, :]), LA.norm(Q[1, :]), np.dot(Q[0, :],Q[1, :]) (1.0, 1.0, 0.0) - Now multiply a diagonal matrix by Q on one side and by Q.T on the other: + Now multiply a diagonal matrix by ``Q`` on one side and by ``Q.T`` on the other: >>> D = np.diag((-1,1)) >>> LA.eigvals(D) @@ -1002,13 +1041,13 @@ def eigvals(a): >>> A = np.dot(Q, D) >>> A = np.dot(A, Q.T) >>> LA.eigvals(A) - array([ 1., -1.]) + array([ 1., -1.]) # random """ a, wrap = _makearray(a) - _assertRankAtLeast2(a) - _assertNdSquareness(a) - _assertFinite(a) + _assert_stacked_2d(a) + _assert_stacked_square(a) + _assert_finite(a) t, result_t = _commonType(a) extobj = get_linalg_error_extobj( @@ -1025,9 +1064,15 @@ def eigvals(a): return w.astype(result_t, copy=False) + +def _eigvalsh_dispatcher(a, UPLO=None): + return (a,) + + +@array_function_dispatch(_eigvalsh_dispatcher) def eigvalsh(a, UPLO='L'): """ - Compute the eigenvalues of a Hermitian or real symmetric matrix. + Compute the eigenvalues of a complex Hermitian or real symmetric matrix. Main difference from eigh: the eigenvectors are not computed. @@ -1057,10 +1102,12 @@ def eigvalsh(a, UPLO='L'): See Also -------- - eigh : eigenvalues and eigenvectors of symmetric/Hermitian arrays. + eigh : eigenvalues and eigenvectors of real symmetric or complex Hermitian + (conjugate symmetric) arrays. eigvals : eigenvalues of general real or complex arrays. eig : eigenvalues and right eigenvectors of general real or complex arrays. + scipy.linalg.eigvalsh : Similar function in SciPy. Notes ----- @@ -1070,31 +1117,31 @@ def eigvalsh(a, UPLO='L'): Broadcasting rules apply, see the `numpy.linalg` documentation for details. - The eigenvalues are computed using LAPACK routines _syevd, _heevd + The eigenvalues are computed using LAPACK routines ``_syevd``, ``_heevd``. Examples -------- >>> from numpy import linalg as LA >>> a = np.array([[1, -2j], [2j, 5]]) >>> LA.eigvalsh(a) - array([ 0.17157288, 5.82842712]) + array([ 0.17157288, 5.82842712]) # may vary >>> # demonstrate the treatment of the imaginary part of the diagonal >>> a = np.array([[5+2j, 9-2j], [0+2j, 2-1j]]) >>> a - array([[ 5.+2.j, 9.-2.j], - [ 0.+2.j, 2.-1.j]]) + array([[5.+2.j, 9.-2.j], + [0.+2.j, 2.-1.j]]) >>> # with UPLO='L' this is numerically equivalent to using LA.eigvals() >>> # with: >>> b = np.array([[5.+0.j, 0.-2.j], [0.+2.j, 2.-0.j]]) >>> b - array([[ 5.+0.j, 0.-2.j], - [ 0.+2.j, 2.+0.j]]) + array([[5.+0.j, 0.-2.j], + [0.+2.j, 2.+0.j]]) >>> wa = LA.eigvalsh(a) >>> wb = LA.eigvals(b) >>> wa; wb - array([ 1., 6.]) - array([ 6.+0.j, 1.+0.j]) + array([1., 6.]) + array([6.+0.j, 1.+0.j]) """ UPLO = UPLO.upper() @@ -1109,8 +1156,8 @@ def eigvalsh(a, UPLO='L'): gufunc = _umath_linalg.eigvalsh_up a, wrap = _makearray(a) - _assertRankAtLeast2(a) - _assertNdSquareness(a) + _assert_stacked_2d(a) + _assert_stacked_square(a) t, result_t = _commonType(a) signature = 'D->d' if isComplexType(t) else 'd->d' w = gufunc(a, signature=signature, extobj=extobj) @@ -1125,6 +1172,7 @@ def _convertarray(a): # Eigenvectors +@array_function_dispatch(_unary_dispatcher) def eig(a): """ Compute the eigenvalues and right eigenvectors of a square array. @@ -1158,12 +1206,14 @@ def eig(a): See Also -------- eigvals : eigenvalues of a non-symmetric array. - - eigh : eigenvalues and eigenvectors of a symmetric or Hermitian - (conjugate symmetric) array. - - eigvalsh : eigenvalues of a symmetric or Hermitian (conjugate symmetric) - array. + eigh : eigenvalues and eigenvectors of a real symmetric or complex + Hermitian (conjugate symmetric) array. + eigvalsh : eigenvalues of a real symmetric or complex Hermitian + (conjugate symmetric) array. + scipy.linalg.eig : Similar function in SciPy that also solves the + generalized eigenvalue problem. + scipy.linalg.schur : Best choice for unitary and other non-Hermitian + normal matrices. Notes ----- @@ -1173,25 +1223,30 @@ def eig(a): Broadcasting rules apply, see the `numpy.linalg` documentation for details. - This is implemented using the _geev LAPACK routines which compute + This is implemented using the ``_geev`` LAPACK routines which compute the eigenvalues and eigenvectors of general square arrays. The number `w` is an eigenvalue of `a` if there exists a vector - `v` such that ``dot(a,v) = w * v``. Thus, the arrays `a`, `w`, and - `v` satisfy the equations ``dot(a[:,:], v[:,i]) = w[i] * v[:,i]`` + `v` such that ``a @ v = w * v``. Thus, the arrays `a`, `w`, and + `v` satisfy the equations ``a @ v[:,i] = w[i] * v[:,i]`` for :math:`i \\in \\{0,...,M-1\\}`. The array `v` of eigenvectors may not be of maximum rank, that is, some of the columns may be linearly dependent, although round-off error may obscure that fact. If the eigenvalues are all different, then theoretically - the eigenvectors are linearly independent. Likewise, the (complex-valued) - matrix of eigenvectors `v` is unitary if the matrix `a` is normal, i.e., - if ``dot(a, a.H) = dot(a.H, a)``, where `a.H` denotes the conjugate - transpose of `a`. + the eigenvectors are linearly independent and `a` can be diagonalized by + a similarity transformation using `v`, i.e, ``inv(v) @ a @ v`` is diagonal. + + For non-Hermitian normal matrices the SciPy function `scipy.linalg.schur` + is preferred because the matrix `v` is guaranteed to be unitary, which is + not the case when using `eig`. The Schur factorization produces an + upper triangular matrix rather than a diagonal matrix, but for normal + matrices only the diagonal of the upper triangular matrix is needed, the + rest is roundoff error. Finally, it is emphasized that `v` consists of the *right* (as in right-hand side) eigenvectors of `a`. A vector `y` satisfying - ``dot(y.T, a) = z * y.T`` for some number `z` is called a *left* + ``y.T @ a = z * y.T`` for some number `z` is called a *left* eigenvector of `a`, and, in general, the left and right eigenvectors of a matrix are not necessarily the (perhaps conjugate) transposes of each other. @@ -1209,29 +1264,29 @@ def eig(a): >>> w, v = LA.eig(np.diag((1, 2, 3))) >>> w; v - array([ 1., 2., 3.]) - array([[ 1., 0., 0.], - [ 0., 1., 0.], - [ 0., 0., 1.]]) + array([1., 2., 3.]) + array([[1., 0., 0.], + [0., 1., 0.], + [0., 0., 1.]]) Real matrix possessing complex e-values and e-vectors; note that the e-values are complex conjugates of each other. >>> w, v = LA.eig(np.array([[1, -1], [1, 1]])) >>> w; v - array([ 1. + 1.j, 1. - 1.j]) - array([[ 0.70710678+0.j , 0.70710678+0.j ], - [ 0.00000000-0.70710678j, 0.00000000+0.70710678j]]) + array([1.+1.j, 1.-1.j]) + array([[0.70710678+0.j , 0.70710678-0.j ], + [0. -0.70710678j, 0. +0.70710678j]]) Complex-valued matrix with real e-values (but complex-valued e-vectors); - note that a.conj().T = a, i.e., a is Hermitian. + note that ``a.conj().T == a``, i.e., `a` is Hermitian. >>> a = np.array([[1, 1j], [-1j, 1]]) >>> w, v = LA.eig(a) >>> w; v - array([ 2.00000000e+00+0.j, 5.98651912e-36+0.j]) # i.e., {2, 0} - array([[ 0.00000000+0.70710678j, 0.70710678+0.j ], - [ 0.70710678+0.j , 0.00000000+0.70710678j]]) + array([2.+0.j, 0.+0.j]) + array([[ 0. +0.70710678j, 0.70710678+0.j ], # may vary + [ 0.70710678+0.j , -0. +0.70710678j]]) Be careful about round-off error! @@ -1239,15 +1294,15 @@ def eig(a): >>> # Theor. e-values are 1 +/- 1e-9 >>> w, v = LA.eig(a) >>> w; v - array([ 1., 1.]) - array([[ 1., 0.], - [ 0., 1.]]) + array([1., 1.]) + array([[1., 0.], + [0., 1.]]) """ a, wrap = _makearray(a) - _assertRankAtLeast2(a) - _assertNdSquareness(a) - _assertFinite(a) + _assert_stacked_2d(a) + _assert_stacked_square(a) + _assert_finite(a) t, result_t = _commonType(a) extobj = get_linalg_error_extobj( @@ -1266,9 +1321,11 @@ def eig(a): return w.astype(result_t, copy=False), wrap(vt) +@array_function_dispatch(_eigvalsh_dispatcher) def eigh(a, UPLO='L'): """ - Return the eigenvalues and eigenvectors of a Hermitian or symmetric matrix. + Return the eigenvalues and eigenvectors of a complex Hermitian + (conjugate symmetric) or a real symmetric matrix. Returns two objects, a 1-D array containing the eigenvalues of `a`, and a 2-D square array or matrix (depending on the input type) of the @@ -1277,7 +1334,7 @@ def eigh(a, UPLO='L'): Parameters ---------- a : (..., M, M) array - Hermitian/Symmetric matrices whose eigenvalues and + Hermitian or real symmetric matrices whose eigenvalues and eigenvectors are to be computed. UPLO : {'L', 'U'}, optional Specifies whether the calculation is done with the lower triangular @@ -1304,9 +1361,12 @@ def eigh(a, UPLO='L'): See Also -------- - eigvalsh : eigenvalues of symmetric or Hermitian arrays. + eigvalsh : eigenvalues of real symmetric or complex Hermitian + (conjugate symmetric) arrays. eig : eigenvalues and right eigenvectors for non-symmetric arrays. eigvals : eigenvalues of non-symmetric arrays. + scipy.linalg.eigh : Similar function in SciPy (but also solves the + generalized eigenvalue problem). Notes ----- @@ -1316,8 +1376,8 @@ def eigh(a, UPLO='L'): Broadcasting rules apply, see the `numpy.linalg` documentation for details. - The eigenvalues/eigenvectors are computed using LAPACK routines _syevd, - _heevd + The eigenvalues/eigenvectors are computed using LAPACK routines ``_syevd``, + ``_heevd``. The eigenvalues of real symmetric or complex Hermitian matrices are always real. [1]_ The array `v` of (column) eigenvectors is unitary @@ -1334,57 +1394,57 @@ def eigh(a, UPLO='L'): >>> from numpy import linalg as LA >>> a = np.array([[1, -2j], [2j, 5]]) >>> a - array([[ 1.+0.j, 0.-2.j], + array([[ 1.+0.j, -0.-2.j], [ 0.+2.j, 5.+0.j]]) >>> w, v = LA.eigh(a) >>> w; v - array([ 0.17157288, 5.82842712]) - array([[-0.92387953+0.j , -0.38268343+0.j ], - [ 0.00000000+0.38268343j, 0.00000000-0.92387953j]]) + array([0.17157288, 5.82842712]) + array([[-0.92387953+0.j , -0.38268343+0.j ], # may vary + [ 0. +0.38268343j, 0. -0.92387953j]]) >>> np.dot(a, v[:, 0]) - w[0] * v[:, 0] # verify 1st e-val/vec pair - array([2.77555756e-17 + 0.j, 0. + 1.38777878e-16j]) + array([5.55111512e-17+0.0000000e+00j, 0.00000000e+00+1.2490009e-16j]) >>> np.dot(a, v[:, 1]) - w[1] * v[:, 1] # verify 2nd e-val/vec pair - array([ 0.+0.j, 0.+0.j]) + array([0.+0.j, 0.+0.j]) >>> A = np.matrix(a) # what happens if input is a matrix object >>> A - matrix([[ 1.+0.j, 0.-2.j], + matrix([[ 1.+0.j, -0.-2.j], [ 0.+2.j, 5.+0.j]]) >>> w, v = LA.eigh(A) >>> w; v - array([ 0.17157288, 5.82842712]) - matrix([[-0.92387953+0.j , -0.38268343+0.j ], - [ 0.00000000+0.38268343j, 0.00000000-0.92387953j]]) + array([0.17157288, 5.82842712]) + matrix([[-0.92387953+0.j , -0.38268343+0.j ], # may vary + [ 0. +0.38268343j, 0. -0.92387953j]]) >>> # demonstrate the treatment of the imaginary part of the diagonal >>> a = np.array([[5+2j, 9-2j], [0+2j, 2-1j]]) >>> a - array([[ 5.+2.j, 9.-2.j], - [ 0.+2.j, 2.-1.j]]) + array([[5.+2.j, 9.-2.j], + [0.+2.j, 2.-1.j]]) >>> # with UPLO='L' this is numerically equivalent to using LA.eig() with: >>> b = np.array([[5.+0.j, 0.-2.j], [0.+2.j, 2.-0.j]]) >>> b - array([[ 5.+0.j, 0.-2.j], - [ 0.+2.j, 2.+0.j]]) + array([[5.+0.j, 0.-2.j], + [0.+2.j, 2.+0.j]]) >>> wa, va = LA.eigh(a) >>> wb, vb = LA.eig(b) >>> wa; wb - array([ 1., 6.]) - array([ 6.+0.j, 1.+0.j]) + array([1., 6.]) + array([6.+0.j, 1.+0.j]) >>> va; vb - array([[-0.44721360-0.j , -0.89442719+0.j ], - [ 0.00000000+0.89442719j, 0.00000000-0.4472136j ]]) - array([[ 0.89442719+0.j , 0.00000000-0.4472136j], - [ 0.00000000-0.4472136j, 0.89442719+0.j ]]) + array([[-0.4472136 +0.j , -0.89442719+0.j ], # may vary + [ 0. +0.89442719j, 0. -0.4472136j ]]) + array([[ 0.89442719+0.j , -0. +0.4472136j], + [-0. +0.4472136j, 0.89442719+0.j ]]) """ UPLO = UPLO.upper() if UPLO not in ('L', 'U'): raise ValueError("UPLO argument must be 'L' or 'U'") a, wrap = _makearray(a) - _assertRankAtLeast2(a) - _assertNdSquareness(a) + _assert_stacked_2d(a) + _assert_stacked_square(a) t, result_t = _commonType(a) extobj = get_linalg_error_extobj( @@ -1403,7 +1463,12 @@ def eigh(a, UPLO='L'): # Singular value decomposition -def svd(a, full_matrices=True, compute_uv=True): +def _svd_dispatcher(a, full_matrices=None, compute_uv=None, hermitian=None): + return (a,) + + +@array_function_dispatch(_svd_dispatcher) +def svd(a, full_matrices=True, compute_uv=True, hermitian=False): """ Singular Value Decomposition. @@ -1424,6 +1489,12 @@ def svd(a, full_matrices=True, compute_uv=True): compute_uv : bool, optional Whether or not to compute `u` and `vh` in addition to `s`. True by default. + hermitian : bool, optional + If True, `a` is assumed to be Hermitian (symmetric if real-valued), + enabling a more efficient method for finding singular values. + Defaults to False. + + .. versionadded:: 1.17.0 Returns ------- @@ -1447,6 +1518,11 @@ def svd(a, full_matrices=True, compute_uv=True): LinAlgError If SVD computation does not converge. + See Also + -------- + scipy.linalg.svd : Similar function in SciPy. + scipy.linalg.svdvals : Compute singular values of a matrix. + Notes ----- @@ -1526,9 +1602,31 @@ def svd(a, full_matrices=True, compute_uv=True): True """ + import numpy as _nx a, wrap = _makearray(a) - _assertNoEmpty2d(a) - _assertRankAtLeast2(a) + + if hermitian: + # note: lapack svd returns eigenvalues with s ** 2 sorted descending, + # but eig returns s sorted ascending, so we re-order the eigenvalues + # and related arrays to have the correct order + if compute_uv: + s, u = eigh(a) + sgn = sign(s) + s = abs(s) + sidx = argsort(s)[..., ::-1] + sgn = _nx.take_along_axis(sgn, sidx, axis=-1) + s = _nx.take_along_axis(s, sidx, axis=-1) + u = _nx.take_along_axis(u, sidx[..., None, :], axis=-1) + # singular values are unsigned, move the sign into v + vt = transpose(u * sgn[..., None, :]).conjugate() + return wrap(u), s, wrap(vt) + else: + s = eigvalsh(a) + s = s[..., ::-1] + s = abs(s) + return sort(s)[..., ::-1] + + _assert_stacked_2d(a) t, result_t = _commonType(a) extobj = get_linalg_error_extobj(_raise_linalgerror_svd_nonconvergence) @@ -1564,6 +1662,11 @@ def svd(a, full_matrices=True, compute_uv=True): return s +def _cond_dispatcher(x, p=None): + return (x,) + + +@array_function_dispatch(_cond_dispatcher) def cond(x, p=None): """ Compute the condition number of a matrix. @@ -1577,7 +1680,7 @@ def cond(x, p=None): x : (..., M, N) array_like The matrix whose condition number is sought. p : {None, 1, -1, 2, -2, inf, -inf, 'fro'}, optional - Order of the norm: + Order of the norm used in the condition number computation: ===== ============================ p norm for matrices @@ -1592,7 +1695,7 @@ def cond(x, p=None): -2 smallest singular value ===== ============================ - inf means the numpy.inf object, and the Frobenius norm is + inf means the `numpy.inf` object, and the Frobenius norm is the root-of-sum-of-squares norm. Returns @@ -1638,12 +1741,14 @@ def cond(x, p=None): >>> LA.cond(a, 2) 1.4142135623730951 >>> LA.cond(a, -2) - 0.70710678118654746 - >>> min(LA.svd(a, compute_uv=0))*min(LA.svd(LA.inv(a), compute_uv=0)) - 0.70710678118654746 + 0.70710678118654746 # may vary + >>> min(LA.svd(a, compute_uv=False))*min(LA.svd(LA.inv(a), compute_uv=False)) + 0.70710678118654746 # may vary """ x = asarray(x) # in case we have a matrix + if _is_empty_2d(x): + raise LinAlgError("cond is not defined on empty arrays") if p is None or p == 2 or p == -2: s = svd(x, compute_uv=False) with errstate(all='ignore'): @@ -1654,8 +1759,8 @@ def cond(x, p=None): else: # Call inv(x) ignoring errors. The result array will # contain nans in the entries where inversion failed. - _assertRankAtLeast2(x) - _assertNdSquareness(x) + _assert_stacked_2d(x) + _assert_stacked_square(x) t, result_t = _commonType(x) signature = 'D->D' if isComplexType(t) else 'd->d' with errstate(all='ignore'): @@ -1680,7 +1785,12 @@ def cond(x, p=None): return r -def matrix_rank(M, tol=None, hermitian=False): +def _matrix_rank_dispatcher(A, tol=None, hermitian=None): + return (A,) + + +@array_function_dispatch(_matrix_rank_dispatcher) +def matrix_rank(A, tol=None, hermitian=False): """ Return matrix rank of array using SVD method @@ -1692,54 +1802,59 @@ def matrix_rank(M, tol=None, hermitian=False): Parameters ---------- - M : {(M,), (..., M, N)} array_like - input vector or stack of matrices + A : {(M,), (..., M, N)} array_like + Input vector or stack of matrices. tol : (...) array_like, float, optional - threshold below which SVD values are considered zero. If `tol` is + Threshold below which SVD values are considered zero. If `tol` is None, and ``S`` is an array with singular values for `M`, and ``eps`` is the epsilon value for datatype of ``S``, then `tol` is - set to ``S.max() * max(M.shape) * eps``. + set to ``S.max() * max(M, N) * eps``. .. versionchanged:: 1.14 Broadcasted against the stack of matrices hermitian : bool, optional - If True, `M` is assumed to be Hermitian (symmetric if real-valued), + If True, `A` is assumed to be Hermitian (symmetric if real-valued), enabling a more efficient method for finding singular values. Defaults to False. .. versionadded:: 1.14 + Returns + ------- + rank : (...) array_like + Rank of A. + Notes ----- The default threshold to detect rank deficiency is a test on the magnitude - of the singular values of `M`. By default, we identify singular values less - than ``S.max() * max(M.shape) * eps`` as indicating rank deficiency (with + of the singular values of `A`. By default, we identify singular values less + than ``S.max() * max(M, N) * eps`` as indicating rank deficiency (with the symbols defined above). This is the algorithm MATLAB uses [1]. It also appears in *Numerical recipes* in the discussion of SVD solutions for linear least squares [2]. This default threshold is designed to detect rank deficiency accounting for the numerical errors of the SVD computation. Imagine that there is a column - in `M` that is an exact (in floating point) linear combination of other - columns in `M`. Computing the SVD on `M` will not produce a singular value + in `A` that is an exact (in floating point) linear combination of other + columns in `A`. Computing the SVD on `A` will not produce a singular value exactly equal to 0 in general: any difference of the smallest SVD value from 0 will be caused by numerical imprecision in the calculation of the SVD. Our threshold for small SVD values takes this numerical imprecision into account, and the default threshold will detect such numerical rank - deficiency. The threshold may declare a matrix `M` rank deficient even if - the linear combination of some columns of `M` is not exactly equal to - another column of `M` but only numerically very close to another column of - `M`. + deficiency. The threshold may declare a matrix `A` rank deficient even if + the linear combination of some columns of `A` is not exactly equal to + another column of `A` but only numerically very close to another column of + `A`. We chose our default threshold because it is in wide use. Other thresholds are possible. For example, elsewhere in the 2007 edition of *Numerical recipes* there is an alternative threshold of ``S.max() * - np.finfo(M.dtype).eps / 2. * np.sqrt(m + n + 1.)``. The authors describe + np.finfo(A.dtype).eps / 2. * np.sqrt(m + n + 1.)``. The authors describe this threshold as being based on "expected roundoff error" (p 71). The thresholds above deal with floating point roundoff error in the calculation of the SVD. However, you may have more information about the - sources of error in `M` that would make you consider other tolerance values + sources of error in `A` that would make you consider other tolerance values to detect *effective* rank deficiency. The most useful measure of the tolerance depends on the operations you intend to use on your matrix. For example, if your data come from uncertain measurements with uncertainties @@ -1749,8 +1864,8 @@ def matrix_rank(M, tol=None, hermitian=False): References ---------- - .. [1] MATLAB reference documention, "Rank" - http://www.mathworks.com/help/techdoc/ref/rank.html + .. [1] MATLAB reference documentation, "Rank" + https://www.mathworks.com/help/techdoc/ref/rank.html .. [2] W. H. Press, S. A. Teukolsky, W. T. Vetterling and B. P. Flannery, "Numerical Recipes (3rd edition)", Cambridge University Press, 2007, page 795. @@ -1768,15 +1883,12 @@ def matrix_rank(M, tol=None, hermitian=False): >>> matrix_rank(np.zeros((4,))) 0 """ - M = asarray(M) - if M.ndim < 2: - return int(not all(M==0)) - if hermitian: - S = abs(eigvalsh(M)) - else: - S = svd(M, compute_uv=False) + A = asarray(A) + if A.ndim < 2: + return int(not all(A==0)) + S = svd(A, compute_uv=False, hermitian=hermitian) if tol is None: - tol = S.max(axis=-1, keepdims=True) * max(M.shape[-2:]) * finfo(S.dtype).eps + tol = S.max(axis=-1, keepdims=True) * max(A.shape[-2:]) * finfo(S.dtype).eps else: tol = asarray(tol)[..., newaxis] return count_nonzero(S > tol, axis=-1) @@ -1784,7 +1896,12 @@ def matrix_rank(M, tol=None, hermitian=False): # Generalized inverse -def pinv(a, rcond=1e-15 ): +def _pinv_dispatcher(a, rcond=None, hermitian=None): + return (a,) + + +@array_function_dispatch(_pinv_dispatcher) +def pinv(a, rcond=1e-15, hermitian=False): """ Compute the (Moore-Penrose) pseudo-inverse of a matrix. @@ -1801,9 +1918,15 @@ def pinv(a, rcond=1e-15 ): Matrix or stack of matrices to be pseudo-inverted. rcond : (...) array_like of float Cutoff for small singular values. - Singular values smaller (in modulus) than - `rcond` * largest_singular_value (again, in modulus) - are set to zero. Broadcasts against the stack of matrices + Singular values less than or equal to + ``rcond * largest_singular_value`` are set to zero. + Broadcasts against the stack of matrices. + hermitian : bool, optional + If True, `a` is assumed to be Hermitian (symmetric if real-valued), + enabling a more efficient method for finding singular values. + Defaults to False. + + .. versionadded:: 1.17.0 Returns ------- @@ -1816,6 +1939,13 @@ def pinv(a, rcond=1e-15 ): LinAlgError If the SVD computation does not converge. + See Also + -------- + scipy.linalg.pinv : Similar function in SciPy. + scipy.linalg.pinv2 : Similar function in SciPy (SVD-based). + scipy.linalg.pinvh : Compute the (Moore-Penrose) pseudo-inverse of a + Hermitian matrix. + Notes ----- The pseudo-inverse of a matrix A, denoted :math:`A^+`, is @@ -1852,12 +1982,12 @@ def pinv(a, rcond=1e-15 ): """ a, wrap = _makearray(a) rcond = asarray(rcond) - if _isEmpty2d(a): + if _is_empty_2d(a): m, n = a.shape[-2:] res = empty(a.shape[:-2] + (n, m), dtype=a.dtype) return wrap(res) a = a.conjugate() - u, s, vt = svd(a, full_matrices=False) + u, s, vt = svd(a, full_matrices=False, hermitian=hermitian) # discard small singular values cutoff = rcond[..., newaxis] * amax(s, axis=-1, keepdims=True) @@ -1868,8 +1998,11 @@ def pinv(a, rcond=1e-15 ): res = matmul(transpose(vt), multiply(s[..., newaxis], transpose(u))) return wrap(res) + # Determinant + +@array_function_dispatch(_unary_dispatcher) def slogdet(a): """ Compute the sign and (natural) logarithm of the determinant of an array. @@ -1911,7 +2044,7 @@ def slogdet(a): .. versionadded:: 1.6.0 The determinant is computed via LU factorization using the LAPACK - routine z/dgetrf. + routine ``z/dgetrf``. Examples @@ -1921,7 +2054,7 @@ def slogdet(a): >>> a = np.array([[1, 2], [3, 4]]) >>> (sign, logdet) = np.linalg.slogdet(a) >>> (sign, logdet) - (-1, 0.69314718055994529) + (-1, 0.69314718055994529) # may vary >>> sign * np.exp(logdet) -2.0 @@ -1945,8 +2078,8 @@ def slogdet(a): """ a = asarray(a) - _assertRankAtLeast2(a) - _assertNdSquareness(a) + _assert_stacked_2d(a) + _assert_stacked_square(a) t, result_t = _commonType(a) real_t = _realType(result_t) signature = 'D->Dd' if isComplexType(t) else 'd->dd' @@ -1955,6 +2088,8 @@ def slogdet(a): logdet = logdet.astype(real_t, copy=False) return sign, logdet + +@array_function_dispatch(_unary_dispatcher) def det(a): """ Compute the determinant of an array. @@ -1973,6 +2108,7 @@ def det(a): -------- slogdet : Another way to represent the determinant, more suitable for large matrices where underflow/overflow may occur. + scipy.linalg.det : Similar function in SciPy. Notes ----- @@ -1983,7 +2119,7 @@ def det(a): details. The determinant is computed via LU factorization using the LAPACK - routine z/dgetrf. + routine ``z/dgetrf``. Examples -------- @@ -1991,7 +2127,7 @@ def det(a): >>> a = np.array([[1, 2], [3, 4]]) >>> np.linalg.det(a) - -2.0 + -2.0 # may vary Computing determinants for a stack of matrices: @@ -2003,27 +2139,34 @@ def det(a): """ a = asarray(a) - _assertRankAtLeast2(a) - _assertNdSquareness(a) + _assert_stacked_2d(a) + _assert_stacked_square(a) t, result_t = _commonType(a) signature = 'D->D' if isComplexType(t) else 'd->d' r = _umath_linalg.det(a, signature=signature) r = r.astype(result_t, copy=False) return r + # Linear Least Squares +def _lstsq_dispatcher(a, b, rcond=None): + return (a, b) + + +@array_function_dispatch(_lstsq_dispatcher) def lstsq(a, b, rcond="warn"): - """ + r""" Return the least-squares solution to a linear matrix equation. - Solves the equation `a x = b` by computing a vector `x` that - minimizes the Euclidean 2-norm `|| b - a x ||^2`. The equation may - be under-, well-, or over- determined (i.e., the number of - linearly independent rows of `a` can be less than, equal to, or - greater than its number of linearly independent columns). If `a` - is square and of full rank, then `x` (but for round-off error) is - the "exact" solution of the equation. + Computes the vector `x` that approximately solves the equation + ``a @ x = b``. The equation may be under-, well-, or over-determined + (i.e., the number of linearly independent rows of `a` can be less than, + equal to, or greater than its number of linearly independent columns). + If `a` is square and of full rank, then `x` (but for round-off error) + is the "exact" solution of the equation. Else, `x` minimizes the + Euclidean 2-norm :math:`||b - ax||`. If there are multiple minimizing + solutions, the one with the smallest 2-norm :math:`||x||` is returned. Parameters ---------- @@ -2052,8 +2195,8 @@ def lstsq(a, b, rcond="warn"): Least-squares solution. If `b` is two-dimensional, the solutions are in the `K` columns of `x`. residuals : {(1,), (K,), (0,)} ndarray - Sums of residuals; squared Euclidean 2-norm for each column in - ``b - a*x``. + Sums of squared residuals: Squared Euclidean 2-norm for each column in + ``b - a @ x``. If the rank of `a` is < N or M <= N, this is an empty array. If `b` is 1-dimensional, this is a (1,) shape array. Otherwise the shape is (K,). @@ -2067,6 +2210,10 @@ def lstsq(a, b, rcond="warn"): LinAlgError If computation does not converge. + See Also + -------- + scipy.linalg.lstsq : Similar function in SciPy. + Notes ----- If `b` is a matrix, then all array results are returned as matrices. @@ -2092,15 +2239,15 @@ def lstsq(a, b, rcond="warn"): [ 3., 1.]]) >>> m, c = np.linalg.lstsq(A, y, rcond=None)[0] - >>> print(m, c) - 1.0 -0.95 + >>> m, c + (1.0 -0.95) # may vary Plot the data along with the fitted line: >>> import matplotlib.pyplot as plt - >>> plt.plot(x, y, 'o', label='Original data', markersize=10) - >>> plt.plot(x, m*x + c, 'r', label='Fitted line') - >>> plt.legend() + >>> _ = plt.plot(x, y, 'o', label='Original data', markersize=10) + >>> _ = plt.plot(x, m*x + c, 'r', label='Fitted line') + >>> _ = plt.legend() >>> plt.show() """ @@ -2109,15 +2256,13 @@ def lstsq(a, b, rcond="warn"): is_1d = b.ndim == 1 if is_1d: b = b[:, newaxis] - _assertRank2(a, b) - _assertNoEmpty2d(a, b) # TODO: relax this constraint + _assert_2d(a, b) m, n = a.shape[-2:] m2, n_rhs = b.shape[-2:] if m != m2: raise LinAlgError('Incompatible dimensions') t, result_t = _commonType(a, b) - real_t = _linalgRealType(t) result_real_t = _realType(result_t) # Determine default rcond value @@ -2129,7 +2274,7 @@ def lstsq(a, b, rcond="warn"): "To use the future default and silence this warning " "we advise to pass `rcond=None`, to keep using the old, " "explicitly pass `rcond=-1`.", - FutureWarning, stacklevel=2) + FutureWarning, stacklevel=3) rcond = -1 if rcond is None: rcond = finfo(t).eps * max(n, m) @@ -2141,7 +2286,16 @@ def lstsq(a, b, rcond="warn"): signature = 'DDd->Ddid' if isComplexType(t) else 'ddd->ddid' extobj = get_linalg_error_extobj(_raise_linalgerror_lstsq) + if n_rhs == 0: + # lapack can't handle n_rhs = 0 - so allocate the array one larger in that axis + b = zeros(b.shape[:-2] + (m, n_rhs + 1), dtype=b.dtype) x, resids, rank, s = gufunc(a, b, rcond, signature=signature, extobj=extobj) + if m == 0: + x[...] = 0 + if n_rhs == 0: + # remove the item we added + x = x[..., :n_rhs] + resids = resids[..., :n_rhs] # remove the axis we added if is_1d: @@ -2163,7 +2317,7 @@ def lstsq(a, b, rcond="warn"): def _multi_svd_norm(x, row_axis, col_axis, op): """Compute a function of the singular values of the 2-D matrices in `x`. - This is a private utility function used by numpy.linalg.norm(). + This is a private utility function used by `numpy.linalg.norm()`. Parameters ---------- @@ -2171,7 +2325,7 @@ def _multi_svd_norm(x, row_axis, col_axis, op): row_axis, col_axis : int The axes of `x` that hold the 2-D matrices. op : callable - This should be either numpy.amin or numpy.amax or numpy.sum. + This should be either numpy.amin or `numpy.amax` or `numpy.sum`. Returns ------- @@ -2184,10 +2338,15 @@ def _multi_svd_norm(x, row_axis, col_axis, op): """ y = moveaxis(x, (row_axis, col_axis), (-2, -1)) - result = op(svd(y, compute_uv=0), axis=-1) + result = op(svd(y, compute_uv=False), axis=-1) return result +def _norm_dispatcher(x, ord=None, axis=None, keepdims=None): + return (x,) + + +@array_function_dispatch(_norm_dispatcher) def norm(x, ord=None, axis=None, keepdims=False): """ Matrix or vector norm. @@ -2199,16 +2358,19 @@ def norm(x, ord=None, axis=None, keepdims=False): Parameters ---------- x : array_like - Input array. If `axis` is None, `x` must be 1-D or 2-D. + Input array. If `axis` is None, `x` must be 1-D or 2-D, unless `ord` + is None. If both `axis` and `ord` are None, the 2-norm of + ``x.ravel`` will be returned. ord : {non-zero int, inf, -inf, 'fro', 'nuc'}, optional Order of the norm (see table under ``Notes``). inf means numpy's - `inf` object. - axis : {int, 2-tuple of ints, None}, optional + `inf` object. The default is None. + axis : {None, int, 2-tuple of ints}, optional. If `axis` is an integer, it specifies the axis of `x` along which to compute the vector norms. If `axis` is a 2-tuple, it specifies the axes that hold 2-D matrices, and the matrix norms of these matrices are computed. If `axis` is None then either a vector norm (when `x` - is 1-D) or a matrix norm (when `x` is 2-D) is returned. + is 1-D) or a matrix norm (when `x` is 2-D) is returned. The default + is None. .. versionadded:: 1.8.0 @@ -2224,9 +2386,13 @@ def norm(x, ord=None, axis=None, keepdims=False): n : float or ndarray Norm of the matrix or vector(s). + See Also + -------- + scipy.linalg.norm : Similar function in SciPy. + Notes ----- - For values of ``ord <= 0``, the result is, strictly speaking, not a + For values of ``ord < 1``, the result is, strictly speaking, not a mathematical 'norm', but it may still be useful for various numerical purposes. @@ -2254,6 +2420,9 @@ def norm(x, ord=None, axis=None, keepdims=False): The nuclear norm is the sum of the singular values. + Both the Frobenius and nuclear norm orders are only defined for + matrices and raise a ValueError when ``x.ndim != 2``. + References ---------- .. [1] G. H. Golub and C. F. Van Loan, *Matrix Computations*, @@ -2264,7 +2433,7 @@ def norm(x, ord=None, axis=None, keepdims=False): >>> from numpy import linalg as LA >>> a = np.arange(9) - 4 >>> a - array([-4, -3, -2, -1, 0, 1, 2, 3, 4]) + array([-4, -3, -2, ..., 2, 3, 4]) >>> b = a.reshape((3, 3)) >>> b array([[-4, -3, -2], @@ -2300,13 +2469,13 @@ def norm(x, ord=None, axis=None, keepdims=False): 7.3484692283495345 >>> LA.norm(a, -2) - nan + 0.0 >>> LA.norm(b, -2) - 1.8570331885190563e-016 + 1.8570331885190563e-016 # may vary >>> LA.norm(a, 3) - 5.8480354764257312 + 5.8480354764257312 # may vary >>> LA.norm(a, -3) - nan + 0.0 Using the `axis` argument to compute vector norms: @@ -2357,8 +2526,8 @@ def norm(x, ord=None, axis=None, keepdims=False): elif not isinstance(axis, tuple): try: axis = int(axis) - except Exception: - raise TypeError("'axis' must be None, an integer or a tuple of integers") + except Exception as e: + raise TypeError("'axis' must be None, an integer or a tuple of integers") from e axis = (axis,) if len(axis) == 1: @@ -2376,11 +2545,11 @@ def norm(x, ord=None, axis=None, keepdims=False): # special case for speedup s = (x.conj() * x).real return sqrt(add.reduce(s, axis=axis, keepdims=keepdims)) + # None of the str-type keywords for ord ('fro', 'nuc') + # are valid for vectors + elif isinstance(ord, str): + raise ValueError(f"Invalid norm order '{ord}' for vectors") else: - try: - ord + 1 - except TypeError: - raise ValueError("Invalid norm order for vectors.") absx = abs(x) absx **= ord ret = add.reduce(absx, axis=axis, keepdims=keepdims) @@ -2430,7 +2599,13 @@ def norm(x, ord=None, axis=None, keepdims=False): # multi_dot -def multi_dot(arrays): +def _multidot_dispatcher(arrays, *, out=None): + yield from arrays + yield out + + +@array_function_dispatch(_multidot_dispatcher) +def multi_dot(arrays, *, out=None): """ Compute the dot product of two or more arrays in a single function call, while automatically selecting the fastest evaluation order. @@ -2454,6 +2629,15 @@ def multi_dot(arrays): return functools.reduce(np.dot, arrays) If the first argument is 1-D it is treated as row vector. If the last argument is 1-D it is treated as column vector. The other arguments must be 2-D. + out : ndarray, optional + Output argument. This must have the exact kind that would be returned + if it was not used. In particular, it must have the right type, must be + C-contiguous, and its dtype must be the dtype that would be returned + for `dot(a, b)`. This is a performance feature. Therefore, if these + conditions are not met, an exception is raised, instead of attempting + to be flexible. + + .. versionadded:: 1.19.0 Returns ------- @@ -2462,13 +2646,13 @@ def multi_dot(arrays): return functools.reduce(np.dot, arrays) See Also -------- - dot : dot multiplication with two arguments. + numpy.dot : dot multiplication with two arguments. References ---------- .. [1] Cormen, "Introduction to Algorithms", Chapter 15.2, p. 370-378 - .. [2] http://en.wikipedia.org/wiki/Matrix_chain_multiplication + .. [2] https://en.wikipedia.org/wiki/Matrix_chain_multiplication Examples -------- @@ -2476,18 +2660,18 @@ def multi_dot(arrays): return functools.reduce(np.dot, arrays) >>> from numpy.linalg import multi_dot >>> # Prepare some data - >>> A = np.random.random(10000, 100) - >>> B = np.random.random(100, 1000) - >>> C = np.random.random(1000, 5) - >>> D = np.random.random(5, 333) + >>> A = np.random.random((10000, 100)) + >>> B = np.random.random((100, 1000)) + >>> C = np.random.random((1000, 5)) + >>> D = np.random.random((5, 333)) >>> # the actual dot multiplication - >>> multi_dot([A, B, C, D]) + >>> _ = multi_dot([A, B, C, D]) instead of:: - >>> np.dot(np.dot(np.dot(A, B), C), D) + >>> _ = np.dot(np.dot(np.dot(A, B), C), D) >>> # or - >>> A.dot(B).dot(C).dot(D) + >>> _ = A.dot(B).dot(C).dot(D) Notes ----- @@ -2497,7 +2681,7 @@ def multi_dot(arrays): return functools.reduce(np.dot, arrays) def cost(A, B): return A.shape[0] * A.shape[1] * B.shape[1] - Let's assume we have three matrices + Assume we have three matrices :math:`A_{10x100}, B_{100x5}, C_{5x50}`. The costs for the two different parenthesizations are as follows:: @@ -2511,7 +2695,7 @@ def cost(A, B): if n < 2: raise ValueError("Expecting at least two arrays.") elif n == 2: - return dot(arrays[0], arrays[1]) + return dot(arrays[0], arrays[1], out=out) arrays = [asanyarray(a) for a in arrays] @@ -2523,14 +2707,14 @@ def cost(A, B): arrays[0] = atleast_2d(arrays[0]) if arrays[-1].ndim == 1: arrays[-1] = atleast_2d(arrays[-1]).T - _assertRank2(*arrays) + _assert_2d(*arrays) # _multi_dot_three is much faster than _multi_dot_matrix_chain_order if n == 3: - result = _multi_dot_three(arrays[0], arrays[1], arrays[2]) + result = _multi_dot_three(arrays[0], arrays[1], arrays[2], out=out) else: order = _multi_dot_matrix_chain_order(arrays) - result = _multi_dot(arrays, order, 0, n - 1) + result = _multi_dot(arrays, order, 0, n - 1, out=out) # return proper shape if ndim_first == 1 and ndim_last == 1: @@ -2541,7 +2725,7 @@ def cost(A, B): return result -def _multi_dot_three(A, B, C): +def _multi_dot_three(A, B, C, out=None): """ Find the best order for three arrays and do the multiplication. @@ -2557,9 +2741,9 @@ def _multi_dot_three(A, B, C): cost2 = a1b0 * c1 * (a0 + b1c0) if cost1 < cost2: - return dot(dot(A, B), C) + return dot(dot(A, B), C, out=out) else: - return dot(A, dot(B, C)) + return dot(A, dot(B, C), out=out) def _multi_dot_matrix_chain_order(arrays, return_costs=False): @@ -2603,10 +2787,14 @@ def _multi_dot_matrix_chain_order(arrays, return_costs=False): return (s, m) if return_costs else s -def _multi_dot(arrays, order, i, j): +def _multi_dot(arrays, order, i, j, out=None): """Actually do the multiplication with the given order.""" if i == j: + # the initial call with non-None out should never get here + assert out is None + return arrays[i] else: return dot(_multi_dot(arrays, order, i, order[i, j]), - _multi_dot(arrays, order, order[i, j] + 1, j)) + _multi_dot(arrays, order, order[i, j] + 1, j), + out=out) diff --git a/numpy/linalg/linalg.pyi b/numpy/linalg/linalg.pyi new file mode 100644 index 000000000000..a60b9539e848 --- /dev/null +++ b/numpy/linalg/linalg.pyi @@ -0,0 +1,284 @@ +from typing import ( + Literal as L, + List, + Iterable, + overload, + TypeVar, + Any, + SupportsIndex, + SupportsInt, + Tuple, +) + +from numpy import ( + generic, + floating, + complexfloating, + int32, + float64, + complex128, +) + +from numpy.linalg import LinAlgError as LinAlgError + +from numpy.typing import ( + NDArray, + ArrayLike, + _ArrayLikeInt_co, + _ArrayLikeFloat_co, + _ArrayLikeComplex_co, + _ArrayLikeTD64_co, + _ArrayLikeObject_co, +) + +_T = TypeVar("_T") +_ArrayType = TypeVar("_ArrayType", bound=NDArray[Any]) + +_2Tuple = Tuple[_T, _T] +_ModeKind = L["reduced", "complete", "r", "raw"] + +__all__: List[str] + +@overload +def tensorsolve( + a: _ArrayLikeInt_co, + b: _ArrayLikeInt_co, + axes: None | Iterable[int] =..., +) -> NDArray[float64]: ... +@overload +def tensorsolve( + a: _ArrayLikeFloat_co, + b: _ArrayLikeFloat_co, + axes: None | Iterable[int] =..., +) -> NDArray[floating[Any]]: ... +@overload +def tensorsolve( + a: _ArrayLikeComplex_co, + b: _ArrayLikeComplex_co, + axes: None | Iterable[int] =..., +) -> NDArray[complexfloating[Any, Any]]: ... + +@overload +def solve( + a: _ArrayLikeInt_co, + b: _ArrayLikeInt_co, +) -> NDArray[float64]: ... +@overload +def solve( + a: _ArrayLikeFloat_co, + b: _ArrayLikeFloat_co, +) -> NDArray[floating[Any]]: ... +@overload +def solve( + a: _ArrayLikeComplex_co, + b: _ArrayLikeComplex_co, +) -> NDArray[complexfloating[Any, Any]]: ... + +@overload +def tensorinv( + a: _ArrayLikeInt_co, + ind: int = ..., +) -> NDArray[float64]: ... +@overload +def tensorinv( + a: _ArrayLikeFloat_co, + ind: int = ..., +) -> NDArray[floating[Any]]: ... +@overload +def tensorinv( + a: _ArrayLikeComplex_co, + ind: int = ..., +) -> NDArray[complexfloating[Any, Any]]: ... + +@overload +def inv(a: _ArrayLikeInt_co) -> NDArray[float64]: ... +@overload +def inv(a: _ArrayLikeFloat_co) -> NDArray[floating[Any]]: ... +@overload +def inv(a: _ArrayLikeComplex_co) -> NDArray[complexfloating[Any, Any]]: ... + +# TODO: The supported input and output dtypes are dependent on the value of `n`. +# For example: `n < 0` always casts integer types to float64 +def matrix_power( + a: _ArrayLikeComplex_co | _ArrayLikeObject_co, + n: SupportsIndex, +) -> NDArray[Any]: ... + +@overload +def cholesky(a: _ArrayLikeInt_co) -> NDArray[float64]: ... +@overload +def cholesky(a: _ArrayLikeFloat_co) -> NDArray[floating[Any]]: ... +@overload +def cholesky(a: _ArrayLikeComplex_co) -> NDArray[complexfloating[Any, Any]]: ... + +@overload +def qr(a: _ArrayLikeInt_co, mode: _ModeKind = ...) -> _2Tuple[NDArray[float64]]: ... +@overload +def qr(a: _ArrayLikeFloat_co, mode: _ModeKind = ...) -> _2Tuple[NDArray[floating[Any]]]: ... +@overload +def qr(a: _ArrayLikeComplex_co, mode: _ModeKind = ...) -> _2Tuple[NDArray[complexfloating[Any, Any]]]: ... + +@overload +def eigvals(a: _ArrayLikeInt_co) -> NDArray[float64] | NDArray[complex128]: ... +@overload +def eigvals(a: _ArrayLikeFloat_co) -> NDArray[floating[Any]] | NDArray[complexfloating[Any, Any]]: ... +@overload +def eigvals(a: _ArrayLikeComplex_co) -> NDArray[complexfloating[Any, Any]]: ... + +@overload +def eigvalsh(a: _ArrayLikeInt_co, UPLO: L["L", "U", "l", "u"] = ...) -> NDArray[float64]: ... +@overload +def eigvalsh(a: _ArrayLikeComplex_co, UPLO: L["L", "U", "l", "u"] = ...) -> NDArray[floating[Any]]: ... + +@overload +def eig(a: _ArrayLikeInt_co) -> _2Tuple[NDArray[float64]] | _2Tuple[NDArray[complex128]]: ... +@overload +def eig(a: _ArrayLikeFloat_co) -> _2Tuple[NDArray[floating[Any]]] | _2Tuple[NDArray[complexfloating[Any, Any]]]: ... +@overload +def eig(a: _ArrayLikeComplex_co) -> _2Tuple[NDArray[complexfloating[Any, Any]]]: ... + +@overload +def eigh( + a: _ArrayLikeInt_co, + UPLO: L["L", "U", "l", "u"] = ..., +) -> Tuple[NDArray[float64], NDArray[float64]]: ... +@overload +def eigh( + a: _ArrayLikeFloat_co, + UPLO: L["L", "U", "l", "u"] = ..., +) -> Tuple[NDArray[floating[Any]], NDArray[floating[Any]]]: ... +@overload +def eigh( + a: _ArrayLikeComplex_co, + UPLO: L["L", "U", "l", "u"] = ..., +) -> Tuple[NDArray[floating[Any]], NDArray[complexfloating[Any, Any]]]: ... + +@overload +def svd( + a: _ArrayLikeInt_co, + full_matrices: bool = ..., + compute_uv: L[True] = ..., + hermitian: bool = ..., +) -> Tuple[ + NDArray[float64], + NDArray[float64], + NDArray[float64], +]: ... +@overload +def svd( + a: _ArrayLikeFloat_co, + full_matrices: bool = ..., + compute_uv: L[True] = ..., + hermitian: bool = ..., +) -> Tuple[ + NDArray[floating[Any]], + NDArray[floating[Any]], + NDArray[floating[Any]], +]: ... +@overload +def svd( + a: _ArrayLikeComplex_co, + full_matrices: bool = ..., + compute_uv: L[True] = ..., + hermitian: bool = ..., +) -> Tuple[ + NDArray[complexfloating[Any, Any]], + NDArray[floating[Any]], + NDArray[complexfloating[Any, Any]], +]: ... +@overload +def svd( + a: _ArrayLikeInt_co, + full_matrices: bool = ..., + compute_uv: L[False] = ..., + hermitian: bool = ..., +) -> NDArray[float64]: ... +@overload +def svd( + a: _ArrayLikeComplex_co, + full_matrices: bool = ..., + compute_uv: L[False] = ..., + hermitian: bool = ..., +) -> NDArray[floating[Any]]: ... + +# TODO: Returns a scalar for 2D arrays and +# a `(x.ndim - 2)`` dimensionl array otherwise +def cond(x: _ArrayLikeComplex_co, p: None | float | L["fro", "nuc"] = ...) -> Any: ... + +# TODO: Returns `int` for <2D arrays and `intp` otherwise +def matrix_rank( + A: _ArrayLikeComplex_co, + tol: None | _ArrayLikeFloat_co = ..., + hermitian: bool = ..., +) -> Any: ... + +@overload +def pinv( + a: _ArrayLikeInt_co, + rcond: _ArrayLikeFloat_co = ..., + hermitian: bool = ..., +) -> NDArray[float64]: ... +@overload +def pinv( + a: _ArrayLikeFloat_co, + rcond: _ArrayLikeFloat_co = ..., + hermitian: bool = ..., +) -> NDArray[floating[Any]]: ... +@overload +def pinv( + a: _ArrayLikeComplex_co, + rcond: _ArrayLikeFloat_co = ..., + hermitian: bool = ..., +) -> NDArray[complexfloating[Any, Any]]: ... + +# TODO: Returns a 2-tuple of scalars for 2D arrays and +# a 2-tuple of `(a.ndim - 2)`` dimensionl arrays otherwise +def slogdet(a: _ArrayLikeComplex_co) -> _2Tuple[Any]: ... + +# TODO: Returns a 2-tuple of scalars for 2D arrays and +# a 2-tuple of `(a.ndim - 2)`` dimensionl arrays otherwise +def det(a: _ArrayLikeComplex_co) -> Any: ... + +@overload +def lstsq(a: _ArrayLikeInt_co, b: _ArrayLikeInt_co, rcond: None | float = ...) -> Tuple[ + NDArray[float64], + NDArray[float64], + int32, + NDArray[float64], +]: ... +@overload +def lstsq(a: _ArrayLikeFloat_co, b: _ArrayLikeFloat_co, rcond: None | float = ...) -> Tuple[ + NDArray[floating[Any]], + NDArray[floating[Any]], + int32, + NDArray[floating[Any]], +]: ... +@overload +def lstsq(a: _ArrayLikeComplex_co, b: _ArrayLikeComplex_co, rcond: None | float = ...) -> Tuple[ + NDArray[complexfloating[Any, Any]], + NDArray[floating[Any]], + int32, + NDArray[floating[Any]], +]: ... + +@overload +def norm( + x: ArrayLike, + ord: None | float | L["fro", "nuc"] = ..., + axis: None = ..., + keepdims: bool = ..., +) -> floating[Any]: ... +@overload +def norm( + x: ArrayLike, + ord: None | float | L["fro", "nuc"] = ..., + axis: SupportsInt | SupportsIndex | Tuple[int, ...] = ..., + keepdims: bool = ..., +) -> Any: ... + +# TODO: Returns a scalar or array +def multi_dot( + arrays: Iterable[_ArrayLikeComplex_co | _ArrayLikeObject_co | _ArrayLikeTD64_co], + *, + out: None | NDArray[Any] = ..., +) -> Any: ... diff --git a/numpy/linalg/setup.py b/numpy/linalg/setup.py index 66c07c9e1e48..94536bb2c055 100644 --- a/numpy/linalg/setup.py +++ b/numpy/linalg/setup.py @@ -1,14 +1,12 @@ -from __future__ import division, print_function - import os import sys def configuration(parent_package='', top_path=None): from numpy.distutils.misc_util import Configuration - from numpy.distutils.system_info import get_info + from numpy.distutils.system_info import get_info, system_info config = Configuration('linalg', parent_package, top_path) - config.add_data_dir('tests') + config.add_subpackage('tests') # Configure lapack_lite @@ -26,10 +24,33 @@ def configuration(parent_package='', top_path=None): ] all_sources = config.paths(lapack_lite_src) - lapack_info = get_info('lapack_opt', 0) # and {} + if os.environ.get('NPY_USE_BLAS_ILP64', "0") != "0": + lapack_info = get_info('lapack_ilp64_opt', 2) + else: + lapack_info = get_info('lapack_opt', 0) # and {} + + use_lapack_lite = not lapack_info + + if use_lapack_lite: + # This makes numpy.distutils write the fact that lapack_lite + # is being used to numpy.__config__ + class numpy_linalg_lapack_lite(system_info): + def calc_info(self): + info = {'language': 'c'} + if sys.maxsize > 2**32: + # Build lapack-lite in 64-bit integer mode. + # The suffix is arbitrary (lapack_lite symbols follow it), + # but use the "64_" convention here. + info['define_macros'] = [ + ('HAVE_BLAS_ILP64', None), + ('BLAS_SYMBOL_SUFFIX', '64_') + ] + self.set_info(**info) + + lapack_info = numpy_linalg_lapack_lite().get_info(2) def get_lapack_lite_sources(ext, build_dir): - if not lapack_info: + if use_lapack_lite: print("### Warning: Using unoptimized lapack ###") return all_sources else: @@ -53,6 +74,7 @@ def get_lapack_lite_sources(ext, build_dir): extra_info=lapack_info, libraries=['npymath'], ) + config.add_data_files('*.pyi') return config if __name__ == '__main__': diff --git a/numpy/linalg/tests/test_build.py b/numpy/linalg/tests/test_build.py deleted file mode 100644 index 921390da3314..000000000000 --- a/numpy/linalg/tests/test_build.py +++ /dev/null @@ -1,55 +0,0 @@ -from __future__ import division, absolute_import, print_function - -from subprocess import PIPE, Popen -import sys -import re -import pytest - -from numpy.linalg import lapack_lite -from numpy.testing import assert_ - - -class FindDependenciesLdd(object): - - def __init__(self): - self.cmd = ['ldd'] - - try: - p = Popen(self.cmd, stdout=PIPE, stderr=PIPE) - stdout, stderr = p.communicate() - except OSError: - raise RuntimeError("command %s cannot be run" % self.cmd) - - def get_dependencies(self, lfile): - p = Popen(self.cmd + [lfile], stdout=PIPE, stderr=PIPE) - stdout, stderr = p.communicate() - if not (p.returncode == 0): - raise RuntimeError("failed dependencies check for %s" % lfile) - - return stdout - - def grep_dependencies(self, lfile, deps): - stdout = self.get_dependencies(lfile) - - rdeps = dict([(dep, re.compile(dep)) for dep in deps]) - founds = [] - for l in stdout.splitlines(): - for k, v in rdeps.items(): - if v.search(l): - founds.append(k) - - return founds - - -class TestF77Mismatch(object): - - @pytest.mark.skipif(not(sys.platform[:5] == 'linux'), - reason="no fortran compiler on non-Linux platform") - def test_lapack(self): - f = FindDependenciesLdd() - deps = f.grep_dependencies(lapack_lite.__file__, - [b'libg2c', b'libgfortran']) - assert_(len(deps) <= 1, - """Both g77 and gfortran runtimes linked in lapack_lite ! This is likely to -cause random crashes and wrong results. See numpy INSTALL.txt for more -information.""") diff --git a/numpy/linalg/tests/test_deprecations.py b/numpy/linalg/tests/test_deprecations.py index e12755e0d586..cd4c10832e7e 100644 --- a/numpy/linalg/tests/test_deprecations.py +++ b/numpy/linalg/tests/test_deprecations.py @@ -1,8 +1,6 @@ """Test deprecation and future warnings. """ -from __future__ import division, absolute_import, print_function - import numpy as np from numpy.testing import assert_warns diff --git a/numpy/linalg/tests/test_linalg.py b/numpy/linalg/tests/test_linalg.py index 87dfe988a323..c1ba84a8e674 100644 --- a/numpy/linalg/tests/test_linalg.py +++ b/numpy/linalg/tests/test_linalg.py @@ -1,8 +1,6 @@ """ Test functions for linalg module """ -from __future__ import division, absolute_import, print_function - import os import sys import itertools @@ -13,13 +11,15 @@ import numpy as np from numpy import array, single, double, csingle, cdouble, dot, identity, matmul -from numpy import multiply, atleast_2d, inf, asarray, matrix +from numpy.core import swapaxes +from numpy import multiply, atleast_2d, inf, asarray from numpy import linalg from numpy.linalg import matrix_power, norm, matrix_rank, multi_dot, LinAlgError from numpy.linalg.linalg import _multi_dot_matrix_chain_order from numpy.testing import ( assert_, assert_equal, assert_raises, assert_array_equal, - assert_almost_equal, assert_allclose, SkipTest, suppress_warnings + assert_almost_equal, assert_allclose, suppress_warnings, + assert_raises_regex, HAS_LAPACK64, ) @@ -66,7 +66,7 @@ def get_rtol(dtype): } -class LinalgCase(object): +class LinalgCase: def __init__(self, name, a, b, tags=set()): """ A bundle of arguments to be passed to a test case, with an identifying @@ -85,7 +85,7 @@ def check(self, do): do(self.a, self.b, tags=self.tags) def __repr__(self): - return "<LinalgCase: %s>" % (self.name,) + return f'<LinalgCase: {self.name}>' def apply_tag(tag, cases): @@ -331,7 +331,7 @@ def _make_strided_cases(): # # Test different routines against the above cases # -class LinalgTestCase(object): +class LinalgTestCase: TEST_CASES = CASES def check_cases(self, require=set(), exclude=set()): @@ -348,10 +348,10 @@ def check_cases(self, require=set(), exclude=set()): try: case.check(self.do) - except Exception: - msg = "In test case: %r\n\n" % case + except Exception as e: + msg = f'In test case: {case!r}\n\n' msg += traceback.format_exc() - raise AssertionError(msg) + raise AssertionError(msg) from e class LinalgSquareTestCase(LinalgTestCase): @@ -462,12 +462,10 @@ def do(self, a, b, tags): class TestSolve(SolveCases): - def test_types(self): - def check(dtype): - x = np.array([[1, 0.5], [0.5, 1]], dtype=dtype) - assert_equal(linalg.solve(x, x).dtype, dtype) - for dtype in [single, double, csingle, cdouble]: - check(dtype) + @pytest.mark.parametrize('dtype', [single, double, csingle, cdouble]) + def test_types(self, dtype): + x = np.array([[1, 0.5], [0.5, 1]], dtype=dtype) + assert_equal(linalg.solve(x, x).dtype, dtype) def test_0_size(self): class ArraySubclass(np.ndarray): @@ -531,12 +529,10 @@ def do(self, a, b, tags): class TestInv(InvCases): - def test_types(self): - def check(dtype): - x = np.array([[1, 0.5], [0.5, 1]], dtype=dtype) - assert_equal(linalg.inv(x).dtype, dtype) - for dtype in [single, double, csingle, cdouble]: - check(dtype) + @pytest.mark.parametrize('dtype', [single, double, csingle, cdouble]) + def test_types(self, dtype): + x = np.array([[1, 0.5], [0.5, 1]], dtype=dtype) + assert_equal(linalg.inv(x).dtype, dtype) def test_0_size(self): # Check that all kinds of 0-sized arrays work @@ -564,14 +560,12 @@ def do(self, a, b, tags): class TestEigvals(EigvalsCases): - def test_types(self): - def check(dtype): - x = np.array([[1, 0.5], [0.5, 1]], dtype=dtype) - assert_equal(linalg.eigvals(x).dtype, dtype) - x = np.array([[1, 0.5], [-1, 1]], dtype=dtype) - assert_equal(linalg.eigvals(x).dtype, get_complex_dtype(dtype)) - for dtype in [single, double, csingle, cdouble]: - check(dtype) + @pytest.mark.parametrize('dtype', [single, double, csingle, cdouble]) + def test_types(self, dtype): + x = np.array([[1, 0.5], [0.5, 1]], dtype=dtype) + assert_equal(linalg.eigvals(x).dtype, dtype) + x = np.array([[1, 0.5], [-1, 1]], dtype=dtype) + assert_equal(linalg.eigvals(x).dtype, get_complex_dtype(dtype)) def test_0_size(self): # Check that all kinds of 0-sized arrays work @@ -603,20 +597,17 @@ def do(self, a, b, tags): class TestEig(EigCases): - def test_types(self): - def check(dtype): - x = np.array([[1, 0.5], [0.5, 1]], dtype=dtype) - w, v = np.linalg.eig(x) - assert_equal(w.dtype, dtype) - assert_equal(v.dtype, dtype) - - x = np.array([[1, 0.5], [-1, 1]], dtype=dtype) - w, v = np.linalg.eig(x) - assert_equal(w.dtype, get_complex_dtype(dtype)) - assert_equal(v.dtype, get_complex_dtype(dtype)) - - for dtype in [single, double, csingle, cdouble]: - check(dtype) + @pytest.mark.parametrize('dtype', [single, double, csingle, cdouble]) + def test_types(self, dtype): + x = np.array([[1, 0.5], [0.5, 1]], dtype=dtype) + w, v = np.linalg.eig(x) + assert_equal(w.dtype, dtype) + assert_equal(v.dtype, dtype) + + x = np.array([[1, 0.5], [-1, 1]], dtype=dtype) + w, v = np.linalg.eig(x) + assert_equal(w.dtype, get_complex_dtype(dtype)) + assert_equal(v.dtype, get_complex_dtype(dtype)) def test_0_size(self): # Check that all kinds of 0-sized arrays work @@ -641,14 +632,24 @@ class ArraySubclass(np.ndarray): assert_(isinstance(a, np.ndarray)) +class SVDBaseTests: + hermitian = False + + @pytest.mark.parametrize('dtype', [single, double, csingle, cdouble]) + def test_types(self, dtype): + x = np.array([[1, 0.5], [0.5, 1]], dtype=dtype) + u, s, vh = linalg.svd(x) + assert_equal(u.dtype, dtype) + assert_equal(s.dtype, get_real_dtype(dtype)) + assert_equal(vh.dtype, dtype) + s = linalg.svd(x, compute_uv=False, hermitian=self.hermitian) + assert_equal(s.dtype, get_real_dtype(dtype)) + + class SVDCases(LinalgSquareTestCase, LinalgGeneralizedSquareTestCase): def do(self, a, b, tags): - if 'size-0' in tags: - assert_raises(LinAlgError, linalg.svd, a, 0) - return - - u, s, vt = linalg.svd(a, 0) + u, s, vt = linalg.svd(a, False) assert_allclose(a, dot_generalized(np.asarray(u) * np.asarray(s)[..., None, :], np.asarray(vt)), rtol=get_rtol(u.dtype)) @@ -656,29 +657,43 @@ def do(self, a, b, tags): assert_(consistent_subclass(vt, a)) -class TestSVD(SVDCases): - def test_types(self): - def check(dtype): - x = np.array([[1, 0.5], [0.5, 1]], dtype=dtype) - u, s, vh = linalg.svd(x) - assert_equal(u.dtype, dtype) - assert_equal(s.dtype, get_real_dtype(dtype)) - assert_equal(vh.dtype, dtype) - s = linalg.svd(x, compute_uv=False) - assert_equal(s.dtype, get_real_dtype(dtype)) +class TestSVD(SVDCases, SVDBaseTests): + def test_empty_identity(self): + """ Empty input should put an identity matrix in u or vh """ + x = np.empty((4, 0)) + u, s, vh = linalg.svd(x, compute_uv=True, hermitian=self.hermitian) + assert_equal(u.shape, (4, 4)) + assert_equal(vh.shape, (0, 0)) + assert_equal(u, np.eye(4)) - for dtype in [single, double, csingle, cdouble]: - check(dtype) + x = np.empty((0, 4)) + u, s, vh = linalg.svd(x, compute_uv=True, hermitian=self.hermitian) + assert_equal(u.shape, (0, 0)) + assert_equal(vh.shape, (4, 4)) + assert_equal(vh, np.eye(4)) + + +class SVDHermitianCases(HermitianTestCase, HermitianGeneralizedTestCase): + + def do(self, a, b, tags): + u, s, vt = linalg.svd(a, False, hermitian=True) + assert_allclose(a, dot_generalized(np.asarray(u) * np.asarray(s)[..., None, :], + np.asarray(vt)), + rtol=get_rtol(u.dtype)) + def hermitian(mat): + axes = list(range(mat.ndim)) + axes[-1], axes[-2] = axes[-2], axes[-1] + return np.conj(np.transpose(mat, axes=axes)) + + assert_almost_equal(np.matmul(u, hermitian(u)), np.broadcast_to(np.eye(u.shape[-1]), u.shape)) + assert_almost_equal(np.matmul(vt, hermitian(vt)), np.broadcast_to(np.eye(vt.shape[-1]), vt.shape)) + assert_equal(np.sort(s)[..., ::-1], s) + assert_(consistent_subclass(u, a)) + assert_(consistent_subclass(vt, a)) - def test_0_size(self): - # These raise errors currently - # (which does not mean that it may not make sense) - a = np.zeros((0, 0), dtype=np.complex64) - assert_raises(linalg.LinAlgError, linalg.svd, a) - a = np.zeros((0, 1), dtype=np.complex64) - assert_raises(linalg.LinAlgError, linalg.svd, a) - a = np.zeros((1, 0), dtype=np.complex64) - assert_raises(linalg.LinAlgError, linalg.svd, a) + +class TestSVDHermitian(SVDHermitianCases, SVDBaseTests): + hermitian = True class CondCases(LinalgSquareTestCase, LinalgGeneralizedSquareTestCase): @@ -751,6 +766,9 @@ def test_singular(self): for A, p in itertools.product(As, p_neg): linalg.cond(A, p) + @pytest.mark.xfail(True, run=False, + reason="Platform/LAPACK-dependent failure, " + "see gh-18914") def test_nan(self): # nans should be passed through, not converted to infs ps = [None, 1, -1, 2, -2, 'fro'] @@ -808,6 +826,20 @@ class TestPinv(PinvCases): pass +class PinvHermitianCases(HermitianTestCase, HermitianGeneralizedTestCase): + + def do(self, a, b, tags): + a_ginv = linalg.pinv(a, hermitian=True) + # `a @ a_ginv == I` does not hold if a is singular + dot = dot_generalized + assert_almost_equal(dot(dot(a, a_ginv), a), a, single_decimal=5, double_decimal=11) + assert_(consistent_subclass(a_ginv, a)) + + +class TestPinvHermitian(PinvHermitianCases): + pass + + class DetCases(LinalgSquareTestCase, LinalgGeneralizedSquareTestCase): def do(self, a, b, tags): @@ -842,15 +874,13 @@ def test_zero(self): assert_equal(type(linalg.slogdet([[0.0j]])[0]), cdouble) assert_equal(type(linalg.slogdet([[0.0j]])[1]), double) - def test_types(self): - def check(dtype): - x = np.array([[1, 0.5], [0.5, 1]], dtype=dtype) - assert_equal(np.linalg.det(x).dtype, dtype) - ph, s = np.linalg.slogdet(x) - assert_equal(s.dtype, get_real_dtype(dtype)) - assert_equal(ph.dtype, dtype) - for dtype in [single, double, csingle, cdouble]: - check(dtype) + @pytest.mark.parametrize('dtype', [single, double, csingle, cdouble]) + def test_types(self, dtype): + x = np.array([[1, 0.5], [0.5, 1]], dtype=dtype) + assert_equal(np.linalg.det(x).dtype, dtype) + ph, s = np.linalg.slogdet(x) + assert_equal(s.dtype, get_real_dtype(dtype)) + assert_equal(ph.dtype, dtype) def test_0_size(self): a = np.zeros((0, 0), dtype=np.complex64) @@ -875,14 +905,12 @@ def test_0_size(self): class LstsqCases(LinalgSquareTestCase, LinalgNonsquareTestCase): def do(self, a, b, tags): - if 'size-0' in tags: - assert_raises(LinAlgError, linalg.lstsq, a, b) - return - arr = np.asarray(a) m, n = arr.shape - u, s, vt = linalg.svd(a, 0) + u, s, vt = linalg.svd(a, False) x, residuals, rank, sv = linalg.lstsq(a, b, rcond=-1) + if m == 0: + assert_((x == 0).all()) if m <= n: assert_almost_equal(b, dot(a, x)) assert_equal(rank, m) @@ -923,78 +951,123 @@ def test_future_rcond(self): # Warning should be raised exactly once (first command) assert_(len(w) == 1) - -class TestMatrixPower(object): - R90 = array([[0, 1], [-1, 0]]) - Arb22 = array([[4, -7], [-2, 10]]) + @pytest.mark.parametrize(["m", "n", "n_rhs"], [ + (4, 2, 2), + (0, 4, 1), + (0, 4, 2), + (4, 0, 1), + (4, 0, 2), + (4, 2, 0), + (0, 0, 0) + ]) + def test_empty_a_b(self, m, n, n_rhs): + a = np.arange(m * n).reshape(m, n) + b = np.ones((m, n_rhs)) + x, residuals, rank, s = linalg.lstsq(a, b, rcond=None) + if m == 0: + assert_((x == 0).all()) + assert_equal(x.shape, (n, n_rhs)) + assert_equal(residuals.shape, ((n_rhs,) if m > n else (0,))) + if m > n and n_rhs > 0: + # residuals are exactly the squared norms of b's columns + r = b - np.dot(a, x) + assert_almost_equal(residuals, (r * r).sum(axis=-2)) + assert_equal(rank, min(m, n)) + assert_equal(s.shape, (min(m, n),)) + + def test_incompatible_dims(self): + # use modified version of docstring example + x = np.array([0, 1, 2, 3]) + y = np.array([-1, 0.2, 0.9, 2.1, 3.3]) + A = np.vstack([x, np.ones(len(x))]).T + with assert_raises_regex(LinAlgError, "Incompatible dimensions"): + linalg.lstsq(A, y, rcond=None) + + +@pytest.mark.parametrize('dt', [np.dtype(c) for c in '?bBhHiIqQefdgFDGO']) +class TestMatrixPower: + + rshft_0 = np.eye(4) + rshft_1 = rshft_0[[3, 0, 1, 2]] + rshft_2 = rshft_0[[2, 3, 0, 1]] + rshft_3 = rshft_0[[1, 2, 3, 0]] + rshft_all = [rshft_0, rshft_1, rshft_2, rshft_3] noninv = array([[1, 0], [0, 0]]) - arbfloat = array([[[0.1, 3.2], [1.2, 0.7]], - [[0.2, 6.4], [2.4, 1.4]]]) + stacked = np.block([[[rshft_0]]]*2) + #FIXME the 'e' dtype might work in future + dtnoinv = [object, np.dtype('e'), np.dtype('g'), np.dtype('G')] - large = identity(10) - t = large[1, :].copy() - large[1, :] = large[0, :] - large[0, :] = t - - def test_large_power(self): + def test_large_power(self, dt): + rshft = self.rshft_1.astype(dt) assert_equal( - matrix_power(self.R90, 2 ** 100 + 2 ** 10 + 2 ** 5 + 1), self.R90) + matrix_power(rshft, 2**100 + 2**10 + 2**5 + 0), self.rshft_0) assert_equal( - matrix_power(self.R90, 2 ** 100 + 2 ** 10 + 1), self.R90) + matrix_power(rshft, 2**100 + 2**10 + 2**5 + 1), self.rshft_1) assert_equal( - matrix_power(self.R90, 2 ** 100 + 2 + 1), -self.R90) - - def test_large_power_trailing_zero(self): + matrix_power(rshft, 2**100 + 2**10 + 2**5 + 2), self.rshft_2) assert_equal( - matrix_power(self.R90, 2 ** 100 + 2 ** 10 + 2 ** 5), identity(2)) + matrix_power(rshft, 2**100 + 2**10 + 2**5 + 3), self.rshft_3) - def testip_zero(self): + def test_power_is_zero(self, dt): def tz(M): mz = matrix_power(M, 0) assert_equal(mz, identity_like_generalized(M)) assert_equal(mz.dtype, M.dtype) - for M in [self.Arb22, self.arbfloat, self.large]: - tz(M) - - def testip_one(self): - def tz(M): - mz = matrix_power(M, 1) - assert_equal(mz, M) - assert_equal(mz.dtype, M.dtype) - for M in [self.Arb22, self.arbfloat, self.large]: - tz(M) - - def testip_two(self): - def tz(M): - mz = matrix_power(M, 2) - assert_equal(mz, matmul(M, M)) - assert_equal(mz.dtype, M.dtype) - for M in [self.Arb22, self.arbfloat, self.large]: - tz(M) - - def testip_invert(self): - def tz(M): - mz = matrix_power(M, -1) - assert_almost_equal(matmul(mz, M), identity_like_generalized(M)) - for M in [self.R90, self.Arb22, self.arbfloat, self.large]: - tz(M) - def test_invert_noninvertible(self): - assert_raises(LinAlgError, matrix_power, self.noninv, -1) - - def test_invalid(self): - assert_raises(TypeError, matrix_power, self.R90, 1.5) - assert_raises(TypeError, matrix_power, self.R90, [1]) - assert_raises(LinAlgError, matrix_power, np.array([1]), 1) - assert_raises(LinAlgError, matrix_power, np.array([[1], [2]]), 1) - assert_raises(LinAlgError, matrix_power, np.ones((4, 3, 2)), 1) - - -class TestBoolPower(object): - - def test_square(self): - A = array([[True, False], [True, True]]) - assert_equal(matrix_power(A, 2), A) + for mat in self.rshft_all: + tz(mat.astype(dt)) + if dt != object: + tz(self.stacked.astype(dt)) + + def test_power_is_one(self, dt): + def tz(mat): + mz = matrix_power(mat, 1) + assert_equal(mz, mat) + assert_equal(mz.dtype, mat.dtype) + + for mat in self.rshft_all: + tz(mat.astype(dt)) + if dt != object: + tz(self.stacked.astype(dt)) + + def test_power_is_two(self, dt): + def tz(mat): + mz = matrix_power(mat, 2) + mmul = matmul if mat.dtype != object else dot + assert_equal(mz, mmul(mat, mat)) + assert_equal(mz.dtype, mat.dtype) + + for mat in self.rshft_all: + tz(mat.astype(dt)) + if dt != object: + tz(self.stacked.astype(dt)) + + def test_power_is_minus_one(self, dt): + def tz(mat): + invmat = matrix_power(mat, -1) + mmul = matmul if mat.dtype != object else dot + assert_almost_equal( + mmul(invmat, mat), identity_like_generalized(mat)) + + for mat in self.rshft_all: + if dt not in self.dtnoinv: + tz(mat.astype(dt)) + + def test_exceptions_bad_power(self, dt): + mat = self.rshft_0.astype(dt) + assert_raises(TypeError, matrix_power, mat, 1.5) + assert_raises(TypeError, matrix_power, mat, [1]) + + def test_exceptions_non_square(self, dt): + assert_raises(LinAlgError, matrix_power, np.array([1], dt), 1) + assert_raises(LinAlgError, matrix_power, np.array([[1], [2]], dt), 1) + assert_raises(LinAlgError, matrix_power, np.ones((4, 3, 2), dt), 1) + + def test_exceptions_not_invertible(self, dt): + if dt in self.dtnoinv: + return + mat = self.noninv.astype(dt) + assert_raises(LinAlgError, matrix_power, mat, -1) class TestEigvalshCases(HermitianTestCase, HermitianGeneralizedTestCase): @@ -1011,14 +1084,12 @@ def do(self, a, b, tags): assert_allclose(ev2, evalues, rtol=get_rtol(ev.dtype)) -class TestEigvalsh(object): - def test_types(self): - def check(dtype): - x = np.array([[1, 0.5], [0.5, 1]], dtype=dtype) - w = np.linalg.eigvalsh(x) - assert_equal(w.dtype, get_real_dtype(dtype)) - for dtype in [single, double, csingle, cdouble]: - check(dtype) +class TestEigvalsh: + @pytest.mark.parametrize('dtype', [single, double, csingle, cdouble]) + def test_types(self, dtype): + x = np.array([[1, 0.5], [0.5, 1]], dtype=dtype) + w = np.linalg.eigvalsh(x) + assert_equal(w.dtype, get_real_dtype(dtype)) def test_invalid(self): x = np.array([[1, 0.5], [0.5, 1]], dtype=np.float32) @@ -1089,15 +1160,13 @@ def do(self, a, b, tags): rtol=get_rtol(ev.dtype), err_msg=repr(a)) -class TestEigh(object): - def test_types(self): - def check(dtype): - x = np.array([[1, 0.5], [0.5, 1]], dtype=dtype) - w, v = np.linalg.eigh(x) - assert_equal(w.dtype, get_real_dtype(dtype)) - assert_equal(v.dtype, dtype) - for dtype in [single, double, csingle, cdouble]: - check(dtype) +class TestEigh: + @pytest.mark.parametrize('dtype', [single, double, csingle, cdouble]) + def test_types(self, dtype): + x = np.array([[1, 0.5], [0.5, 1]], dtype=dtype) + w, v = np.linalg.eigh(x) + assert_equal(w.dtype, get_real_dtype(dtype)) + assert_equal(v.dtype, dtype) def test_invalid(self): x = np.array([[1, 0.5], [0.5, 1]], dtype=np.float32) @@ -1150,7 +1219,7 @@ class ArraySubclass(np.ndarray): assert_(isinstance(a, np.ndarray)) -class _TestNormBase(object): +class _TestNormBase: dt = None dec = None @@ -1413,11 +1482,12 @@ def test_bad_args(self): # Using `axis=<integer>` or passing in a 1-D array implies vector # norms are being computed, so also using `ord='fro'` - # or `ord='nuc'` raises a ValueError. + # or `ord='nuc'` or any other string raises a ValueError. assert_raises(ValueError, norm, A, 'fro', 0) assert_raises(ValueError, norm, A, 'nuc', 0) assert_raises(ValueError, norm, [3, 4], 'fro', None) assert_raises(ValueError, norm, [3, 4], 'nuc', None) + assert_raises(ValueError, norm, [3, 4], 'test', None) # Similarly, norm should raise an exception when ord is any finite # number other than 1, 2, -1 or -2 when computing matrix norms. @@ -1436,7 +1506,7 @@ class _TestNorm(_TestNorm2D, _TestNormGeneral): pass -class TestNorm_NonSystematic(object): +class TestNorm_NonSystematic: def test_longdouble_norm(self): # Non-regression test: p-norm of longdouble would previously raise @@ -1491,7 +1561,7 @@ class TestNormInt64(_TestNorm, _TestNormInt64Base): pass -class TestMatrixRank(object): +class TestMatrixRank: def test_matrix_rank(self): # Full rank matrix @@ -1540,7 +1610,7 @@ def test_reduced_rank(): assert_equal(matrix_rank(X), 8) -class TestQR(object): +class TestQR: # Define the array class here, so run this on matrices elsewhere. array = np.array @@ -1582,9 +1652,23 @@ def check_qr(self, a): assert_(isinstance(r2, a_type)) assert_almost_equal(r2, r1) - def test_qr_empty(self): - a = np.zeros((0, 2)) - assert_raises(linalg.LinAlgError, linalg.qr, a) + + @pytest.mark.parametrize(["m", "n"], [ + (3, 0), + (0, 3), + (0, 0) + ]) + def test_qr_empty(self, m, n): + k = min(m, n) + a = np.empty((m, n)) + + self.check_qr(a) + + h, tau = np.linalg.qr(a, mode='raw') + assert_equal(h.dtype, np.double) + assert_equal(tau.dtype, np.double) + assert_equal(h.shape, (n, m)) + assert_equal(tau.shape, (k,)) def test_mode_raw(self): # The factorization is not unique and varies between libraries, @@ -1625,17 +1709,68 @@ def test_mode_all_but_economic(self): self.check_qr(m2) self.check_qr(m2.T) - def test_0_size(self): - # There may be good ways to do (some of this) reasonably: - a = np.zeros((0, 0)) - assert_raises(linalg.LinAlgError, linalg.qr, a) - a = np.zeros((0, 1)) - assert_raises(linalg.LinAlgError, linalg.qr, a) - a = np.zeros((1, 0)) - assert_raises(linalg.LinAlgError, linalg.qr, a) + def check_qr_stacked(self, a): + # This test expects the argument `a` to be an ndarray or + # a subclass of an ndarray of inexact type. + a_type = type(a) + a_dtype = a.dtype + m, n = a.shape[-2:] + k = min(m, n) + + # mode == 'complete' + q, r = linalg.qr(a, mode='complete') + assert_(q.dtype == a_dtype) + assert_(r.dtype == a_dtype) + assert_(isinstance(q, a_type)) + assert_(isinstance(r, a_type)) + assert_(q.shape[-2:] == (m, m)) + assert_(r.shape[-2:] == (m, n)) + assert_almost_equal(matmul(q, r), a) + I_mat = np.identity(q.shape[-1]) + stack_I_mat = np.broadcast_to(I_mat, + q.shape[:-2] + (q.shape[-1],)*2) + assert_almost_equal(matmul(swapaxes(q, -1, -2).conj(), q), stack_I_mat) + assert_almost_equal(np.triu(r[..., :, :]), r) + + # mode == 'reduced' + q1, r1 = linalg.qr(a, mode='reduced') + assert_(q1.dtype == a_dtype) + assert_(r1.dtype == a_dtype) + assert_(isinstance(q1, a_type)) + assert_(isinstance(r1, a_type)) + assert_(q1.shape[-2:] == (m, k)) + assert_(r1.shape[-2:] == (k, n)) + assert_almost_equal(matmul(q1, r1), a) + I_mat = np.identity(q1.shape[-1]) + stack_I_mat = np.broadcast_to(I_mat, + q1.shape[:-2] + (q1.shape[-1],)*2) + assert_almost_equal(matmul(swapaxes(q1, -1, -2).conj(), q1), + stack_I_mat) + assert_almost_equal(np.triu(r1[..., :, :]), r1) + + # mode == 'r' + r2 = linalg.qr(a, mode='r') + assert_(r2.dtype == a_dtype) + assert_(isinstance(r2, a_type)) + assert_almost_equal(r2, r1) + @pytest.mark.parametrize("size", [ + (3, 4), (4, 3), (4, 4), + (3, 0), (0, 3)]) + @pytest.mark.parametrize("outer_size", [ + (2, 2), (2,), (2, 3, 4)]) + @pytest.mark.parametrize("dt", [ + np.single, np.double, + np.csingle, np.cdouble]) + def test_stacked_inputs(self, outer_size, size, dt): -class TestCholesky(object): + A = np.random.normal(size=outer_size + size).astype(dt) + B = np.random.normal(size=outer_size + size).astype(dt) + self.check_qr_stacked(A) + self.check_qr_stacked(A + 1.j*B) + + +class TestCholesky: # TODO: are there no other tests for cholesky? def test_basic_property(self): @@ -1659,7 +1794,7 @@ def test_basic_property(self): b = np.matmul(c, c.transpose(t).conj()) assert_allclose(b, a, - err_msg="{} {}\n{}\n{}".format(shape, dtype, a, c), + err_msg=f'{shape} {dtype}\n{a}\n{c}', atol=500 * a.shape[0] * np.finfo(dtype).eps) def test_0_size(self): @@ -1725,7 +1860,7 @@ def test_xerbla_override(): pid = os.fork() except (OSError, AttributeError): # fork failed, or not running on POSIX - raise SkipTest("Not POSIX or fork failed.") + pytest.skip("Not POSIX or fork failed.") if pid == 0: # child; close i/o file handles @@ -1760,9 +1895,10 @@ def test_xerbla_override(): # parent pid, status = os.wait() if os.WEXITSTATUS(status) != XERBLA_OK: - raise SkipTest('Numpy xerbla not linked in.') + pytest.skip('Numpy xerbla not linked in.') +@pytest.mark.slow def test_sdot_bug_8577(): # Regression test that loading certain other libraries does not # result to wrong results in float32 linear algebra. @@ -1797,7 +1933,7 @@ def test_sdot_bug_8577(): subprocess.check_call([sys.executable, "-c", code]) -class TestMultiDot(object): +class TestMultiDot: def test_basic_function_with_three_arguments(self): # multi_dot with three arguments uses a fast hand coded algorithm to @@ -1809,8 +1945,16 @@ def test_basic_function_with_three_arguments(self): assert_almost_equal(multi_dot([A, B, C]), A.dot(B).dot(C)) assert_almost_equal(multi_dot([A, B, C]), np.dot(A, np.dot(B, C))) - def test_basic_function_with_dynamic_programing_optimization(self): - # multi_dot with four or more arguments uses the dynamic programing + def test_basic_function_with_two_arguments(self): + # separate code path with two arguments + A = np.random.random((6, 2)) + B = np.random.random((2, 6)) + + assert_almost_equal(multi_dot([A, B]), A.dot(B)) + assert_almost_equal(multi_dot([A, B]), np.dot(A, B)) + + def test_basic_function_with_dynamic_programming_optimization(self): + # multi_dot with four or more arguments uses the dynamic programming # optimization and therefore deserve a separate A = np.random.random((6, 2)) B = np.random.random((2, 6)) @@ -1848,6 +1992,41 @@ def test_vector_as_first_and_last_argument(self): # the result should be a scalar assert_equal(multi_dot([A1d, B, C, D1d]).shape, ()) + def test_three_arguments_and_out(self): + # multi_dot with three arguments uses a fast hand coded algorithm to + # determine the optimal order. Therefore test it separately. + A = np.random.random((6, 2)) + B = np.random.random((2, 6)) + C = np.random.random((6, 2)) + + out = np.zeros((6, 2)) + ret = multi_dot([A, B, C], out=out) + assert out is ret + assert_almost_equal(out, A.dot(B).dot(C)) + assert_almost_equal(out, np.dot(A, np.dot(B, C))) + + def test_two_arguments_and_out(self): + # separate code path with two arguments + A = np.random.random((6, 2)) + B = np.random.random((2, 6)) + out = np.zeros((6, 6)) + ret = multi_dot([A, B], out=out) + assert out is ret + assert_almost_equal(out, A.dot(B)) + assert_almost_equal(out, np.dot(A, B)) + + def test_dynamic_programming_optimization_and_out(self): + # multi_dot with four or more arguments uses the dynamic programming + # optimization and therefore deserve a separate test + A = np.random.random((6, 2)) + B = np.random.random((2, 6)) + C = np.random.random((6, 2)) + D = np.random.random((2, 1)) + out = np.zeros((6, 1)) + ret = multi_dot([A, B, C, D], out=out) + assert out is ret + assert_almost_equal(out, A.dot(B).dot(C).dot(D)) + def test_dynamic_programming_logic(self): # Test for the dynamic programming part # This test is directly taken from Cormen page 376. @@ -1881,3 +2060,93 @@ def test_dynamic_programming_logic(self): def test_too_few_input_arrays(self): assert_raises(ValueError, multi_dot, []) assert_raises(ValueError, multi_dot, [np.random.random((3, 3))]) + + +class TestTensorinv: + + @pytest.mark.parametrize("arr, ind", [ + (np.ones((4, 6, 8, 2)), 2), + (np.ones((3, 3, 2)), 1), + ]) + def test_non_square_handling(self, arr, ind): + with assert_raises(LinAlgError): + linalg.tensorinv(arr, ind=ind) + + @pytest.mark.parametrize("shape, ind", [ + # examples from docstring + ((4, 6, 8, 3), 2), + ((24, 8, 3), 1), + ]) + def test_tensorinv_shape(self, shape, ind): + a = np.eye(24) + a.shape = shape + ainv = linalg.tensorinv(a=a, ind=ind) + expected = a.shape[ind:] + a.shape[:ind] + actual = ainv.shape + assert_equal(actual, expected) + + @pytest.mark.parametrize("ind", [ + 0, -2, + ]) + def test_tensorinv_ind_limit(self, ind): + a = np.eye(24) + a.shape = (4, 6, 8, 3) + with assert_raises(ValueError): + linalg.tensorinv(a=a, ind=ind) + + def test_tensorinv_result(self): + # mimic a docstring example + a = np.eye(24) + a.shape = (24, 8, 3) + ainv = linalg.tensorinv(a, ind=1) + b = np.ones(24) + assert_allclose(np.tensordot(ainv, b, 1), np.linalg.tensorsolve(a, b)) + + +def test_unsupported_commontype(): + # linalg gracefully handles unsupported type + arr = np.array([[1, -2], [2, 5]], dtype='float16') + with assert_raises_regex(TypeError, "unsupported in linalg"): + linalg.cholesky(arr) + + +#@pytest.mark.slow +#@pytest.mark.xfail(not HAS_LAPACK64, run=False, +# reason="Numpy not compiled with 64-bit BLAS/LAPACK") +#@requires_memory(free_bytes=16e9) +@pytest.mark.skip(reason="Bad memory reports lead to OOM in ci testing") +def test_blas64_dot(): + n = 2**32 + a = np.zeros([1, n], dtype=np.float32) + b = np.ones([1, 1], dtype=np.float32) + a[0,-1] = 1 + c = np.dot(b, a) + assert_equal(c[0,-1], 1) + + +@pytest.mark.xfail(not HAS_LAPACK64, + reason="Numpy not compiled with 64-bit BLAS/LAPACK") +def test_blas64_geqrf_lwork_smoketest(): + # Smoke test LAPACK geqrf lwork call with 64-bit integers + dtype = np.float64 + lapack_routine = np.linalg.lapack_lite.dgeqrf + + m = 2**32 + 1 + n = 2**32 + 1 + lda = m + + # Dummy arrays, not referenced by the lapack routine, so don't + # need to be of the right size + a = np.zeros([1, 1], dtype=dtype) + work = np.zeros([1], dtype=dtype) + tau = np.zeros([1], dtype=dtype) + + # Size query + results = lapack_routine(m, n, a, lda, tau, work, -1, 0) + assert_equal(results['info'], 0) + assert_equal(results['m'], m) + assert_equal(results['n'], m) + + # Should result to an integer of a reasonable size + lwork = int(work.item()) + assert_(2**32 < lwork < 2**42) diff --git a/numpy/linalg/tests/test_regression.py b/numpy/linalg/tests/test_regression.py index bd3a45872cb1..7ed932bc928d 100644 --- a/numpy/linalg/tests/test_regression.py +++ b/numpy/linalg/tests/test_regression.py @@ -1,7 +1,5 @@ """ Test functions for linalg module """ -from __future__ import division, absolute_import, print_function - import warnings import numpy as np @@ -12,7 +10,7 @@ ) -class TestRegression(object): +class TestRegression: def test_eig_build(self): # Ticket #652 @@ -59,8 +57,8 @@ def test_svd_build(self): assert_array_almost_equal(b, np.zeros((2, 2))) def test_norm_vector_badarg(self): - # Regression for #786: Froebenius norm for vectors raises - # TypeError. + # Regression for #786: Frobenius norm for vectors raises + # ValueError. assert_raises(ValueError, linalg.norm, array([1., 2., 3.]), 'fro') def test_lapack_endian(self): diff --git a/numpy/linalg/umath_linalg.c.src b/numpy/linalg/umath_linalg.c.src index 7dc1cb0cbcca..f8a15444547d 100644 --- a/numpy/linalg/umath_linalg.c.src +++ b/numpy/linalg/umath_linalg.c.src @@ -5,9 +5,10 @@ ** INCLUDES ** ***************************************************************************** */ -#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#define PY_SSIZE_T_CLEAN +#include <Python.h> -#include "Python.h" +#define NPY_NO_DEPRECATED_API NPY_API_VERSION #include "numpy/arrayobject.h" #include "numpy/ufuncobject.h" @@ -15,6 +16,8 @@ #include "npy_config.h" +#include "npy_cblas.h" + #include <stddef.h> #include <stdio.h> #include <assert.h> @@ -62,301 +65,322 @@ dbg_stack_trace() ***************************************************************************** */ -#ifdef NO_APPEND_FORTRAN -# define FNAME(x) x -#else -# define FNAME(x) x##_ -#endif +#define FNAME(x) BLAS_FUNC(x) + +typedef CBLAS_INT fortran_int; typedef struct { float r, i; } f2c_complex; typedef struct { double r, i; } f2c_doublecomplex; /* typedef long int (*L_fp)(); */ -extern int -FNAME(sgeev)(char *jobvl, char *jobvr, int *n, - float a[], int *lda, float wr[], float wi[], - float vl[], int *ldvl, float vr[], int *ldvr, - float work[], int lwork[], - int *info); -extern int -FNAME(dgeev)(char *jobvl, char *jobvr, int *n, - double a[], int *lda, double wr[], double wi[], - double vl[], int *ldvl, double vr[], int *ldvr, - double work[], int lwork[], - int *info); -extern int -FNAME(cgeev)(char *jobvl, char *jobvr, int *n, - f2c_doublecomplex a[], int *lda, +typedef float fortran_real; +typedef double fortran_doublereal; +typedef f2c_complex fortran_complex; +typedef f2c_doublecomplex fortran_doublecomplex; + +extern fortran_int +FNAME(sgeev)(char *jobvl, char *jobvr, fortran_int *n, + float a[], fortran_int *lda, float wr[], float wi[], + float vl[], fortran_int *ldvl, float vr[], fortran_int *ldvr, + float work[], fortran_int lwork[], + fortran_int *info); +extern fortran_int +FNAME(dgeev)(char *jobvl, char *jobvr, fortran_int *n, + double a[], fortran_int *lda, double wr[], double wi[], + double vl[], fortran_int *ldvl, double vr[], fortran_int *ldvr, + double work[], fortran_int lwork[], + fortran_int *info); +extern fortran_int +FNAME(cgeev)(char *jobvl, char *jobvr, fortran_int *n, + f2c_doublecomplex a[], fortran_int *lda, f2c_doublecomplex w[], - f2c_doublecomplex vl[], int *ldvl, - f2c_doublecomplex vr[], int *ldvr, - f2c_doublecomplex work[], int *lwork, + f2c_doublecomplex vl[], fortran_int *ldvl, + f2c_doublecomplex vr[], fortran_int *ldvr, + f2c_doublecomplex work[], fortran_int *lwork, double rwork[], - int *info); -extern int -FNAME(zgeev)(char *jobvl, char *jobvr, int *n, - f2c_doublecomplex a[], int *lda, + fortran_int *info); +extern fortran_int +FNAME(zgeev)(char *jobvl, char *jobvr, fortran_int *n, + f2c_doublecomplex a[], fortran_int *lda, f2c_doublecomplex w[], - f2c_doublecomplex vl[], int *ldvl, - f2c_doublecomplex vr[], int *ldvr, - f2c_doublecomplex work[], int *lwork, + f2c_doublecomplex vl[], fortran_int *ldvl, + f2c_doublecomplex vr[], fortran_int *ldvr, + f2c_doublecomplex work[], fortran_int *lwork, double rwork[], - int *info); - -extern int -FNAME(ssyevd)(char *jobz, char *uplo, int *n, - float a[], int *lda, float w[], float work[], - int *lwork, int iwork[], int *liwork, - int *info); -extern int -FNAME(dsyevd)(char *jobz, char *uplo, int *n, - double a[], int *lda, double w[], double work[], - int *lwork, int iwork[], int *liwork, - int *info); -extern int -FNAME(cheevd)(char *jobz, char *uplo, int *n, - f2c_complex a[], int *lda, + fortran_int *info); + +extern fortran_int +FNAME(ssyevd)(char *jobz, char *uplo, fortran_int *n, + float a[], fortran_int *lda, float w[], float work[], + fortran_int *lwork, fortran_int iwork[], fortran_int *liwork, + fortran_int *info); +extern fortran_int +FNAME(dsyevd)(char *jobz, char *uplo, fortran_int *n, + double a[], fortran_int *lda, double w[], double work[], + fortran_int *lwork, fortran_int iwork[], fortran_int *liwork, + fortran_int *info); +extern fortran_int +FNAME(cheevd)(char *jobz, char *uplo, fortran_int *n, + f2c_complex a[], fortran_int *lda, float w[], f2c_complex work[], - int *lwork, float rwork[], int *lrwork, int iwork[], - int *liwork, - int *info); -extern int -FNAME(zheevd)(char *jobz, char *uplo, int *n, - f2c_doublecomplex a[], int *lda, + fortran_int *lwork, float rwork[], fortran_int *lrwork, fortran_int iwork[], + fortran_int *liwork, + fortran_int *info); +extern fortran_int +FNAME(zheevd)(char *jobz, char *uplo, fortran_int *n, + f2c_doublecomplex a[], fortran_int *lda, double w[], f2c_doublecomplex work[], - int *lwork, double rwork[], int *lrwork, int iwork[], - int *liwork, - int *info); - -extern int -FNAME(sgelsd)(int *m, int *n, int *nrhs, - float a[], int *lda, float b[], int *ldb, - float s[], float *rcond, int *rank, - float work[], int *lwork, int iwork[], - int *info); -extern int -FNAME(dgelsd)(int *m, int *n, int *nrhs, - double a[], int *lda, double b[], int *ldb, - double s[], double *rcond, int *rank, - double work[], int *lwork, int iwork[], - int *info); -extern int -FNAME(cgelsd)(int *m, int *n, int *nrhs, - f2c_complex a[], int *lda, - f2c_complex b[], int *ldb, - float s[], float *rcond, int *rank, - f2c_complex work[], int *lwork, - float rwork[], int iwork[], - int *info); -extern int -FNAME(zgelsd)(int *m, int *n, int *nrhs, - f2c_doublecomplex a[], int *lda, - f2c_doublecomplex b[], int *ldb, - double s[], double *rcond, int *rank, - f2c_doublecomplex work[], int *lwork, - double rwork[], int iwork[], - int *info); - -extern int -FNAME(sgesv)(int *n, int *nrhs, - float a[], int *lda, - int ipiv[], - float b[], int *ldb, - int *info); -extern int -FNAME(dgesv)(int *n, int *nrhs, - double a[], int *lda, - int ipiv[], - double b[], int *ldb, - int *info); -extern int -FNAME(cgesv)(int *n, int *nrhs, - f2c_complex a[], int *lda, - int ipiv[], - f2c_complex b[], int *ldb, - int *info); -extern int -FNAME(zgesv)(int *n, int *nrhs, - f2c_doublecomplex a[], int *lda, - int ipiv[], - f2c_doublecomplex b[], int *ldb, - int *info); - -extern int -FNAME(sgetrf)(int *m, int *n, - float a[], int *lda, - int ipiv[], - int *info); -extern int -FNAME(dgetrf)(int *m, int *n, - double a[], int *lda, - int ipiv[], - int *info); -extern int -FNAME(cgetrf)(int *m, int *n, - f2c_complex a[], int *lda, - int ipiv[], - int *info); -extern int -FNAME(zgetrf)(int *m, int *n, - f2c_doublecomplex a[], int *lda, - int ipiv[], - int *info); - -extern int -FNAME(spotrf)(char *uplo, int *n, - float a[], int *lda, - int *info); -extern int -FNAME(dpotrf)(char *uplo, int *n, - double a[], int *lda, - int *info); -extern int -FNAME(cpotrf)(char *uplo, int *n, - f2c_complex a[], int *lda, - int *info); -extern int -FNAME(zpotrf)(char *uplo, int *n, - f2c_doublecomplex a[], int *lda, - int *info); - -extern int -FNAME(sgesdd)(char *jobz, int *m, int *n, - float a[], int *lda, float s[], float u[], - int *ldu, float vt[], int *ldvt, float work[], - int *lwork, int iwork[], int *info); -extern int -FNAME(dgesdd)(char *jobz, int *m, int *n, - double a[], int *lda, double s[], double u[], - int *ldu, double vt[], int *ldvt, double work[], - int *lwork, int iwork[], int *info); -extern int -FNAME(cgesdd)(char *jobz, int *m, int *n, - f2c_complex a[], int *lda, - float s[], f2c_complex u[], int *ldu, - f2c_complex vt[], int *ldvt, - f2c_complex work[], int *lwork, - float rwork[], int iwork[], int *info); -extern int -FNAME(zgesdd)(char *jobz, int *m, int *n, - f2c_doublecomplex a[], int *lda, - double s[], f2c_doublecomplex u[], int *ldu, - f2c_doublecomplex vt[], int *ldvt, - f2c_doublecomplex work[], int *lwork, - double rwork[], int iwork[], int *info); - -extern int -FNAME(spotrs)(char *uplo, int *n, int *nrhs, - float a[], int *lda, - float b[], int *ldb, - int *info); -extern int -FNAME(dpotrs)(char *uplo, int *n, int *nrhs, - double a[], int *lda, - double b[], int *ldb, - int *info); -extern int -FNAME(cpotrs)(char *uplo, int *n, int *nrhs, - f2c_complex a[], int *lda, - f2c_complex b[], int *ldb, - int *info); -extern int -FNAME(zpotrs)(char *uplo, int *n, int *nrhs, - f2c_doublecomplex a[], int *lda, - f2c_doublecomplex b[], int *ldb, - int *info); - -extern int -FNAME(spotri)(char *uplo, int *n, - float a[], int *lda, - int *info); -extern int -FNAME(dpotri)(char *uplo, int *n, - double a[], int *lda, - int *info); -extern int -FNAME(cpotri)(char *uplo, int *n, - f2c_complex a[], int *lda, - int *info); -extern int -FNAME(zpotri)(char *uplo, int *n, - f2c_doublecomplex a[], int *lda, - int *info); - -extern int -FNAME(scopy)(int *n, - float *sx, int *incx, - float *sy, int *incy); -extern int -FNAME(dcopy)(int *n, - double *sx, int *incx, - double *sy, int *incy); -extern int -FNAME(ccopy)(int *n, - f2c_complex *sx, int *incx, - f2c_complex *sy, int *incy); -extern int -FNAME(zcopy)(int *n, - f2c_doublecomplex *sx, int *incx, - f2c_doublecomplex *sy, int *incy); + fortran_int *lwork, double rwork[], fortran_int *lrwork, fortran_int iwork[], + fortran_int *liwork, + fortran_int *info); + +extern fortran_int +FNAME(sgelsd)(fortran_int *m, fortran_int *n, fortran_int *nrhs, + float a[], fortran_int *lda, float b[], fortran_int *ldb, + float s[], float *rcond, fortran_int *rank, + float work[], fortran_int *lwork, fortran_int iwork[], + fortran_int *info); +extern fortran_int +FNAME(dgelsd)(fortran_int *m, fortran_int *n, fortran_int *nrhs, + double a[], fortran_int *lda, double b[], fortran_int *ldb, + double s[], double *rcond, fortran_int *rank, + double work[], fortran_int *lwork, fortran_int iwork[], + fortran_int *info); +extern fortran_int +FNAME(cgelsd)(fortran_int *m, fortran_int *n, fortran_int *nrhs, + f2c_complex a[], fortran_int *lda, + f2c_complex b[], fortran_int *ldb, + float s[], float *rcond, fortran_int *rank, + f2c_complex work[], fortran_int *lwork, + float rwork[], fortran_int iwork[], + fortran_int *info); +extern fortran_int +FNAME(zgelsd)(fortran_int *m, fortran_int *n, fortran_int *nrhs, + f2c_doublecomplex a[], fortran_int *lda, + f2c_doublecomplex b[], fortran_int *ldb, + double s[], double *rcond, fortran_int *rank, + f2c_doublecomplex work[], fortran_int *lwork, + double rwork[], fortran_int iwork[], + fortran_int *info); + +extern fortran_int +FNAME(dgeqrf)(fortran_int *m, fortran_int *n, double a[], fortran_int *lda, + double tau[], double work[], + fortran_int *lwork, fortran_int *info); +extern fortran_int +FNAME(zgeqrf)(fortran_int *m, fortran_int *n, f2c_doublecomplex a[], fortran_int *lda, + f2c_doublecomplex tau[], f2c_doublecomplex work[], + fortran_int *lwork, fortran_int *info); + +extern fortran_int +FNAME(dorgqr)(fortran_int *m, fortran_int *n, fortran_int *k, double a[], fortran_int *lda, + double tau[], double work[], + fortran_int *lwork, fortran_int *info); +extern fortran_int +FNAME(zungqr)(fortran_int *m, fortran_int *n, fortran_int *k, f2c_doublecomplex a[], + fortran_int *lda, f2c_doublecomplex tau[], + f2c_doublecomplex work[], fortran_int *lwork, fortran_int *info); + +extern fortran_int +FNAME(sgesv)(fortran_int *n, fortran_int *nrhs, + float a[], fortran_int *lda, + fortran_int ipiv[], + float b[], fortran_int *ldb, + fortran_int *info); +extern fortran_int +FNAME(dgesv)(fortran_int *n, fortran_int *nrhs, + double a[], fortran_int *lda, + fortran_int ipiv[], + double b[], fortran_int *ldb, + fortran_int *info); +extern fortran_int +FNAME(cgesv)(fortran_int *n, fortran_int *nrhs, + f2c_complex a[], fortran_int *lda, + fortran_int ipiv[], + f2c_complex b[], fortran_int *ldb, + fortran_int *info); +extern fortran_int +FNAME(zgesv)(fortran_int *n, fortran_int *nrhs, + f2c_doublecomplex a[], fortran_int *lda, + fortran_int ipiv[], + f2c_doublecomplex b[], fortran_int *ldb, + fortran_int *info); + +extern fortran_int +FNAME(sgetrf)(fortran_int *m, fortran_int *n, + float a[], fortran_int *lda, + fortran_int ipiv[], + fortran_int *info); +extern fortran_int +FNAME(dgetrf)(fortran_int *m, fortran_int *n, + double a[], fortran_int *lda, + fortran_int ipiv[], + fortran_int *info); +extern fortran_int +FNAME(cgetrf)(fortran_int *m, fortran_int *n, + f2c_complex a[], fortran_int *lda, + fortran_int ipiv[], + fortran_int *info); +extern fortran_int +FNAME(zgetrf)(fortran_int *m, fortran_int *n, + f2c_doublecomplex a[], fortran_int *lda, + fortran_int ipiv[], + fortran_int *info); + +extern fortran_int +FNAME(spotrf)(char *uplo, fortran_int *n, + float a[], fortran_int *lda, + fortran_int *info); +extern fortran_int +FNAME(dpotrf)(char *uplo, fortran_int *n, + double a[], fortran_int *lda, + fortran_int *info); +extern fortran_int +FNAME(cpotrf)(char *uplo, fortran_int *n, + f2c_complex a[], fortran_int *lda, + fortran_int *info); +extern fortran_int +FNAME(zpotrf)(char *uplo, fortran_int *n, + f2c_doublecomplex a[], fortran_int *lda, + fortran_int *info); + +extern fortran_int +FNAME(sgesdd)(char *jobz, fortran_int *m, fortran_int *n, + float a[], fortran_int *lda, float s[], float u[], + fortran_int *ldu, float vt[], fortran_int *ldvt, float work[], + fortran_int *lwork, fortran_int iwork[], fortran_int *info); +extern fortran_int +FNAME(dgesdd)(char *jobz, fortran_int *m, fortran_int *n, + double a[], fortran_int *lda, double s[], double u[], + fortran_int *ldu, double vt[], fortran_int *ldvt, double work[], + fortran_int *lwork, fortran_int iwork[], fortran_int *info); +extern fortran_int +FNAME(cgesdd)(char *jobz, fortran_int *m, fortran_int *n, + f2c_complex a[], fortran_int *lda, + float s[], f2c_complex u[], fortran_int *ldu, + f2c_complex vt[], fortran_int *ldvt, + f2c_complex work[], fortran_int *lwork, + float rwork[], fortran_int iwork[], fortran_int *info); +extern fortran_int +FNAME(zgesdd)(char *jobz, fortran_int *m, fortran_int *n, + f2c_doublecomplex a[], fortran_int *lda, + double s[], f2c_doublecomplex u[], fortran_int *ldu, + f2c_doublecomplex vt[], fortran_int *ldvt, + f2c_doublecomplex work[], fortran_int *lwork, + double rwork[], fortran_int iwork[], fortran_int *info); + +extern fortran_int +FNAME(spotrs)(char *uplo, fortran_int *n, fortran_int *nrhs, + float a[], fortran_int *lda, + float b[], fortran_int *ldb, + fortran_int *info); +extern fortran_int +FNAME(dpotrs)(char *uplo, fortran_int *n, fortran_int *nrhs, + double a[], fortran_int *lda, + double b[], fortran_int *ldb, + fortran_int *info); +extern fortran_int +FNAME(cpotrs)(char *uplo, fortran_int *n, fortran_int *nrhs, + f2c_complex a[], fortran_int *lda, + f2c_complex b[], fortran_int *ldb, + fortran_int *info); +extern fortran_int +FNAME(zpotrs)(char *uplo, fortran_int *n, fortran_int *nrhs, + f2c_doublecomplex a[], fortran_int *lda, + f2c_doublecomplex b[], fortran_int *ldb, + fortran_int *info); + +extern fortran_int +FNAME(spotri)(char *uplo, fortran_int *n, + float a[], fortran_int *lda, + fortran_int *info); +extern fortran_int +FNAME(dpotri)(char *uplo, fortran_int *n, + double a[], fortran_int *lda, + fortran_int *info); +extern fortran_int +FNAME(cpotri)(char *uplo, fortran_int *n, + f2c_complex a[], fortran_int *lda, + fortran_int *info); +extern fortran_int +FNAME(zpotri)(char *uplo, fortran_int *n, + f2c_doublecomplex a[], fortran_int *lda, + fortran_int *info); + +extern fortran_int +FNAME(scopy)(fortran_int *n, + float *sx, fortran_int *incx, + float *sy, fortran_int *incy); +extern fortran_int +FNAME(dcopy)(fortran_int *n, + double *sx, fortran_int *incx, + double *sy, fortran_int *incy); +extern fortran_int +FNAME(ccopy)(fortran_int *n, + f2c_complex *sx, fortran_int *incx, + f2c_complex *sy, fortran_int *incy); +extern fortran_int +FNAME(zcopy)(fortran_int *n, + f2c_doublecomplex *sx, fortran_int *incx, + f2c_doublecomplex *sy, fortran_int *incy); extern float -FNAME(sdot)(int *n, - float *sx, int *incx, - float *sy, int *incy); +FNAME(sdot)(fortran_int *n, + float *sx, fortran_int *incx, + float *sy, fortran_int *incy); extern double -FNAME(ddot)(int *n, - double *sx, int *incx, - double *sy, int *incy); +FNAME(ddot)(fortran_int *n, + double *sx, fortran_int *incx, + double *sy, fortran_int *incy); extern void -FNAME(cdotu)(f2c_complex *ret, int *n, - f2c_complex *sx, int *incx, - f2c_complex *sy, int *incy); +FNAME(cdotu)(f2c_complex *ret, fortran_int *n, + f2c_complex *sx, fortran_int *incx, + f2c_complex *sy, fortran_int *incy); extern void -FNAME(zdotu)(f2c_doublecomplex *ret, int *n, - f2c_doublecomplex *sx, int *incx, - f2c_doublecomplex *sy, int *incy); +FNAME(zdotu)(f2c_doublecomplex *ret, fortran_int *n, + f2c_doublecomplex *sx, fortran_int *incx, + f2c_doublecomplex *sy, fortran_int *incy); extern void -FNAME(cdotc)(f2c_complex *ret, int *n, - f2c_complex *sx, int *incx, - f2c_complex *sy, int *incy); +FNAME(cdotc)(f2c_complex *ret, fortran_int *n, + f2c_complex *sx, fortran_int *incx, + f2c_complex *sy, fortran_int *incy); extern void -FNAME(zdotc)(f2c_doublecomplex *ret, int *n, - f2c_doublecomplex *sx, int *incx, - f2c_doublecomplex *sy, int *incy); +FNAME(zdotc)(f2c_doublecomplex *ret, fortran_int *n, + f2c_doublecomplex *sx, fortran_int *incx, + f2c_doublecomplex *sy, fortran_int *incy); -extern int +extern fortran_int FNAME(sgemm)(char *transa, char *transb, - int *m, int *n, int *k, + fortran_int *m, fortran_int *n, fortran_int *k, float *alpha, - float *a, int *lda, - float *b, int *ldb, + float *a, fortran_int *lda, + float *b, fortran_int *ldb, float *beta, - float *c, int *ldc); -extern int + float *c, fortran_int *ldc); +extern fortran_int FNAME(dgemm)(char *transa, char *transb, - int *m, int *n, int *k, + fortran_int *m, fortran_int *n, fortran_int *k, double *alpha, - double *a, int *lda, - double *b, int *ldb, + double *a, fortran_int *lda, + double *b, fortran_int *ldb, double *beta, - double *c, int *ldc); -extern int + double *c, fortran_int *ldc); +extern fortran_int FNAME(cgemm)(char *transa, char *transb, - int *m, int *n, int *k, + fortran_int *m, fortran_int *n, fortran_int *k, f2c_complex *alpha, - f2c_complex *a, int *lda, - f2c_complex *b, int *ldb, + f2c_complex *a, fortran_int *lda, + f2c_complex *b, fortran_int *ldb, f2c_complex *beta, - f2c_complex *c, int *ldc); -extern int + f2c_complex *c, fortran_int *ldc); +extern fortran_int FNAME(zgemm)(char *transa, char *transb, - int *m, int *n, int *k, + fortran_int *m, fortran_int *n, fortran_int *k, f2c_doublecomplex *alpha, - f2c_doublecomplex *a, int *lda, - f2c_doublecomplex *b, int *ldb, + f2c_doublecomplex *a, fortran_int *lda, + f2c_doublecomplex *b, fortran_int *ldb, f2c_doublecomplex *beta, - f2c_doublecomplex *c, int *ldc); + f2c_doublecomplex *c, fortran_int *ldc); #define LAPACK_T(FUNC) \ @@ -369,12 +393,6 @@ FNAME(zgemm)(char *transa, char *transb, #define LAPACK(FUNC) \ FNAME(FUNC) -typedef int fortran_int; -typedef float fortran_real; -typedef double fortran_doublereal; -typedef f2c_complex fortran_complex; -typedef f2c_doublecomplex fortran_doublecomplex; - /* ***************************************************************************** @@ -1085,8 +1103,8 @@ static NPY_INLINE void static void @TYPE@_slogdet(char **args, - npy_intp *dimensions, - npy_intp *steps, + npy_intp const *dimensions, + npy_intp const *steps, void *NPY_UNUSED(func)) { fortran_int m; @@ -1128,8 +1146,8 @@ static void static void @TYPE@_det(char **args, - npy_intp *dimensions, - npy_intp *steps, + npy_intp const *dimensions, + npy_intp const *steps, void *NPY_UNUSED(func)) { fortran_int m; @@ -1427,8 +1445,8 @@ static NPY_INLINE void @TYPE@_eigh_wrapper(char JOBZ, char UPLO, char**args, - npy_intp* dimensions, - npy_intp* steps) + npy_intp const *dimensions, + npy_intp const *steps) { ptrdiff_t outer_steps[3]; size_t iter; @@ -1502,8 +1520,8 @@ static NPY_INLINE void */ static void @TYPE@_eighlo(char **args, - npy_intp *dimensions, - npy_intp *steps, + npy_intp const *dimensions, + npy_intp const *steps, void *NPY_UNUSED(func)) { @TYPE@_eigh_wrapper('V', 'L', args, dimensions, steps); @@ -1511,8 +1529,8 @@ static void static void @TYPE@_eighup(char **args, - npy_intp *dimensions, - npy_intp *steps, + npy_intp const *dimensions, + npy_intp const *steps, void* NPY_UNUSED(func)) { @TYPE@_eigh_wrapper('V', 'U', args, dimensions, steps); @@ -1520,8 +1538,8 @@ static void static void @TYPE@_eigvalshlo(char **args, - npy_intp *dimensions, - npy_intp *steps, + npy_intp const *dimensions, + npy_intp const *steps, void* NPY_UNUSED(func)) { @TYPE@_eigh_wrapper('N', 'L', args, dimensions, steps); @@ -1529,8 +1547,8 @@ static void static void @TYPE@_eigvalshup(char **args, - npy_intp *dimensions, - npy_intp *steps, + npy_intp const *dimensions, + npy_intp const *steps, void* NPY_UNUSED(func)) { @TYPE@_eigh_wrapper('N', 'U', args, dimensions, steps); @@ -1619,7 +1637,7 @@ release_@lapack_func@(GESV_PARAMS_t *params) } static void -@TYPE@_solve(char **args, npy_intp *dimensions, npy_intp *steps, +@TYPE@_solve(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { GESV_PARAMS_t params; @@ -1656,7 +1674,7 @@ static void } static void -@TYPE@_solve1(char **args, npy_intp *dimensions, npy_intp *steps, +@TYPE@_solve1(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { GESV_PARAMS_t params; @@ -1691,7 +1709,7 @@ static void } static void -@TYPE@_inv(char **args, npy_intp *dimensions, npy_intp *steps, +@TYPE@_inv(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { GESV_PARAMS_t params; @@ -1793,7 +1811,7 @@ release_@lapack_func@(POTR_PARAMS_t *params) } static void -@TYPE@_cholesky(char uplo, char **args, npy_intp *dimensions, npy_intp *steps) +@TYPE@_cholesky(char uplo, char **args, npy_intp const *dimensions, npy_intp const *steps) { POTR_PARAMS_t params; int error_occurred = get_fp_invalid_and_clear(); @@ -1826,7 +1844,7 @@ static void } static void -@TYPE@_cholesky_lo(char **args, npy_intp *dimensions, npy_intp *steps, +@TYPE@_cholesky_lo(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) { @TYPE@_cholesky('L', args, dimensions, steps); @@ -1842,7 +1860,7 @@ typedef struct geev_params_struct { void *WR; /* RWORK in complex versions, REAL W buffer for (sd)geev*/ void *WI; void *VLR; /* REAL VL buffers for _geev where _ is s, d */ - void *VRR; /* REAL VR buffers for _geev hwere _ is s, d */ + void *VRR; /* REAL VR buffers for _geev where _ is s, d */ void *WORK; void *W; /* final w */ void *VL; /* final vl */ @@ -2236,8 +2254,8 @@ static NPY_INLINE void @TYPE@_eig_wrapper(char JOBVL, char JOBVR, char**args, - npy_intp* dimensions, - npy_intp* steps) + npy_intp const *dimensions, + npy_intp const *steps) { ptrdiff_t outer_steps[4]; size_t iter; @@ -2330,8 +2348,8 @@ static NPY_INLINE void static void @TYPE@_eig(char **args, - npy_intp *dimensions, - npy_intp *steps, + npy_intp const *dimensions, + npy_intp const *steps, void *NPY_UNUSED(func)) { @TYPE@_eig_wrapper('N', 'V', args, dimensions, steps); @@ -2339,8 +2357,8 @@ static void static void @TYPE@_eigvals(char **args, - npy_intp *dimensions, - npy_intp *steps, + npy_intp const *dimensions, + npy_intp const *steps, void *NPY_UNUSED(func)) { @TYPE@_eig_wrapper('N', 'N', args, dimensions, steps); @@ -2522,8 +2540,6 @@ init_@lapack_func@(GESDD_PARAMS_t *params, params->VT = vt; params->RWORK = NULL; params->IWORK = iwork; - params->M = m; - params->N = n; params->LDA = ld; params->LDU = ld; params->LDVT = vt_column_count; @@ -2715,8 +2731,8 @@ release_@lapack_func@(GESDD_PARAMS_t* params) static NPY_INLINE void @TYPE@_svd_wrapper(char JOBZ, char **args, - npy_intp* dimensions, - npy_intp* steps) + npy_intp const *dimensions, + npy_intp const *steps) { ptrdiff_t outer_steps[4]; int error_occurred = get_fp_invalid_and_clear(); @@ -2735,19 +2751,18 @@ static NPY_INLINE void (fortran_int)dimensions[0], (fortran_int)dimensions[1])) { LINEARIZE_DATA_t a_in, u_out, s_out, v_out; + fortran_int min_m_n = params.M < params.N ? params.M : params.N; init_linearize_data(&a_in, params.N, params.M, steps[1], steps[0]); if ('N' == params.JOBZ) { /* only the singular values are wanted */ - fortran_int min_m_n = params.M < params.N? params.M : params.N; init_linearize_data(&s_out, 1, min_m_n, 0, steps[2]); } else { fortran_int u_columns, v_rows; - fortran_int min_m_n = params.M < params.N? params.M : params.N; if ('S' == params.JOBZ) { u_columns = min_m_n; v_rows = min_m_n; - } else { + } else { /* JOBZ == 'A' */ u_columns = params.M; v_rows = params.N; } @@ -2771,6 +2786,15 @@ static NPY_INLINE void if ('N' == params.JOBZ) { delinearize_@REALTYPE@_matrix(args[1], params.S, &s_out); } else { + if ('A' == params.JOBZ && min_m_n == 0) { + /* Lapack has betrayed us and left these uninitialized, + * so produce an identity matrix for whichever of u + * and v is not empty. + */ + identity_@TYPE@_matrix(params.U, params.M); + identity_@TYPE@_matrix(params.VT, params.N); + } + delinearize_@TYPE@_matrix(args[1], params.U, &u_out); delinearize_@REALTYPE@_matrix(args[2], params.S, &s_out); delinearize_@TYPE@_matrix(args[3], params.VT, &v_out); @@ -2802,8 +2826,8 @@ static NPY_INLINE void */ static void @TYPE@_svd_N(char **args, - npy_intp *dimensions, - npy_intp *steps, + npy_intp const *dimensions, + npy_intp const *steps, void *NPY_UNUSED(func)) { @TYPE@_svd_wrapper('N', args, dimensions, steps); @@ -2811,8 +2835,8 @@ static void static void @TYPE@_svd_S(char **args, - npy_intp *dimensions, - npy_intp *steps, + npy_intp const *dimensions, + npy_intp const *steps, void *NPY_UNUSED(func)) { @TYPE@_svd_wrapper('S', args, dimensions, steps); @@ -2820,8 +2844,8 @@ static void static void @TYPE@_svd_A(char **args, - npy_intp *dimensions, - npy_intp *steps, + npy_intp const *dimensions, + npy_intp const *steps, void *NPY_UNUSED(func)) { @TYPE@_svd_wrapper('A', args, dimensions, steps); @@ -2829,170 +2853,131 @@ static void /**end repeat**/ - /* -------------------------------------------------------------------------- */ - /* least squares */ + /* qr (modes - r, raw) */ -typedef struct gelsd_params_struct +typedef struct geqfr_params_struct { fortran_int M; fortran_int N; - fortran_int NRHS; void *A; fortran_int LDA; - void *B; - fortran_int LDB; - void *S; - void *RCOND; - fortran_int RANK; + void* TAU; void *WORK; fortran_int LWORK; - void *RWORK; - void *IWORK; -} GELSD_PARAMS_t; +} GEQRF_PARAMS_t; static inline void -dump_gelsd_params(const char *name, - GELSD_PARAMS_t *params) +dump_geqrf_params(const char *name, + GEQRF_PARAMS_t *params) { TRACE_TXT("\n%s:\n"\ "%14s: %18p\n"\ "%14s: %18p\n"\ "%14s: %18p\n"\ - "%14s: %18p\n"\ - "%14s: %18p\n"\ - "%14s: %18p\n"\ - - "%14s: %18d\n"\ "%14s: %18d\n"\ "%14s: %18d\n"\ "%14s: %18d\n"\ - "%14s: %18d\n"\ - "%14s: %18d\n"\ - "%14s: %18d\n"\ - - "%14s: %18p\n", + "%14s: %18d\n", name, "A", params->A, - "B", params->B, - "S", params->S, + "TAU", params->TAU, "WORK", params->WORK, - "RWORK", params->RWORK, - "IWORK", params->IWORK, "M", (int)params->M, "N", (int)params->N, - "NRHS", (int)params->NRHS, "LDA", (int)params->LDA, - "LDB", (int)params->LDB, - "LWORK", (int)params->LWORK, - "RANK", (int)params->RANK, - - "RCOND", params->RCOND); + "LWORK", (int)params->LWORK); } - /**begin repeat - #TYPE=FLOAT,DOUBLE# - #lapack_func=sgelsd,dgelsd# - #ftyp=fortran_real,fortran_doublereal# + #lapack_func=dgeqrf,zgeqrf# */ static inline fortran_int -call_@lapack_func@(GELSD_PARAMS_t *params) +call_@lapack_func@(GEQRF_PARAMS_t *params) { fortran_int rv; - LAPACK(@lapack_func@)(¶ms->M, ¶ms->N, ¶ms->NRHS, + LAPACK(@lapack_func@)(¶ms->M, ¶ms->N, params->A, ¶ms->LDA, - params->B, ¶ms->LDB, - params->S, - params->RCOND, ¶ms->RANK, + params->TAU, params->WORK, ¶ms->LWORK, - params->IWORK, &rv); return rv; } +/**end repeat**/ + +/**begin repeat + #TYPE=DOUBLE# + #lapack_func=dgeqrf# + #ftyp=fortran_doublereal# + */ static inline int -init_@lapack_func@(GELSD_PARAMS_t *params, +init_@lapack_func@(GEQRF_PARAMS_t *params, fortran_int m, - fortran_int n, - fortran_int nrhs) + fortran_int n) { npy_uint8 *mem_buff = NULL; npy_uint8 *mem_buff2 = NULL; - npy_uint8 *a, *b, *s, *work, *iwork; + npy_uint8 *a, *tau, *work; fortran_int min_m_n = fortran_int_min(m, n); - fortran_int max_m_n = fortran_int_max(m, n); size_t safe_min_m_n = min_m_n; - size_t safe_max_m_n = max_m_n; size_t safe_m = m; size_t safe_n = n; - size_t safe_nrhs = nrhs; size_t a_size = safe_m * safe_n * sizeof(@ftyp@); - size_t b_size = safe_max_m_n * safe_nrhs * sizeof(@ftyp@); - size_t s_size = safe_min_m_n * sizeof(@ftyp@); + size_t tau_size = safe_min_m_n * sizeof(@ftyp@); fortran_int work_count; size_t work_size; - size_t iwork_size; fortran_int lda = fortran_int_max(1, m); - fortran_int ldb = fortran_int_max(1, fortran_int_max(m,n)); - mem_buff = malloc(a_size + b_size + s_size); + mem_buff = malloc(a_size + tau_size); if (!mem_buff) goto error; a = mem_buff; - b = a + a_size; - s = b + b_size; + tau = a + a_size; + memset(tau, 0, tau_size); params->M = m; params->N = n; - params->NRHS = nrhs; params->A = a; - params->B = b; - params->S = s; + params->TAU = tau; params->LDA = lda; - params->LDB = ldb; { /* compute optimal work size */ + @ftyp@ work_size_query; - fortran_int iwork_size_query; params->WORK = &work_size_query; - params->IWORK = &iwork_size_query; - params->RWORK = NULL; params->LWORK = -1; if (call_@lapack_func@(params) != 0) goto error; - work_count = (fortran_int)work_size_query; + work_count = (fortran_int) *(@ftyp@*) params->WORK; - work_size = (size_t) work_size_query * sizeof(@ftyp@); - iwork_size = (size_t)iwork_size_query * sizeof(fortran_int); } - mem_buff2 = malloc(work_size + iwork_size); + params->LWORK = fortran_int_max(fortran_int_max(1, n), work_count); + + work_size = (size_t) params->LWORK * sizeof(@ftyp@); + mem_buff2 = malloc(work_size); if (!mem_buff2) goto error; work = mem_buff2; - iwork = work + work_size; params->WORK = work; - params->RWORK = NULL; - params->IWORK = iwork; - params->LWORK = work_count; return 1; error: @@ -3007,106 +2992,73 @@ init_@lapack_func@(GELSD_PARAMS_t *params, /**end repeat**/ /**begin repeat - #TYPE=CFLOAT,CDOUBLE# - #ftyp=fortran_complex,fortran_doublecomplex# - #frealtyp=fortran_real,fortran_doublereal# - #typ=COMPLEX_t,DOUBLECOMPLEX_t# - #lapack_func=cgelsd,zgelsd# + #TYPE=CDOUBLE# + #lapack_func=zgeqrf# + #ftyp=fortran_doublecomplex# */ - -static inline fortran_int -call_@lapack_func@(GELSD_PARAMS_t *params) -{ - fortran_int rv; - LAPACK(@lapack_func@)(¶ms->M, ¶ms->N, ¶ms->NRHS, - params->A, ¶ms->LDA, - params->B, ¶ms->LDB, - params->S, - params->RCOND, ¶ms->RANK, - params->WORK, ¶ms->LWORK, - params->RWORK, params->IWORK, - &rv); - return rv; -} - static inline int -init_@lapack_func@(GELSD_PARAMS_t *params, +init_@lapack_func@(GEQRF_PARAMS_t *params, fortran_int m, - fortran_int n, - fortran_int nrhs) + fortran_int n) { npy_uint8 *mem_buff = NULL; npy_uint8 *mem_buff2 = NULL; - npy_uint8 *a, *b, *s, *work, *iwork, *rwork; + npy_uint8 *a, *tau, *work; fortran_int min_m_n = fortran_int_min(m, n); - fortran_int max_m_n = fortran_int_max(m, n); size_t safe_min_m_n = min_m_n; - size_t safe_max_m_n = max_m_n; size_t safe_m = m; size_t safe_n = n; - size_t safe_nrhs = nrhs; size_t a_size = safe_m * safe_n * sizeof(@ftyp@); - size_t b_size = safe_max_m_n * safe_nrhs * sizeof(@ftyp@); - size_t s_size = safe_min_m_n * sizeof(@frealtyp@); + size_t tau_size = safe_min_m_n * sizeof(@ftyp@); fortran_int work_count; - size_t work_size, rwork_size, iwork_size; + size_t work_size; fortran_int lda = fortran_int_max(1, m); - fortran_int ldb = fortran_int_max(1, fortran_int_max(m,n)); - mem_buff = malloc(a_size + b_size + s_size); + mem_buff = malloc(a_size + tau_size); if (!mem_buff) goto error; a = mem_buff; - b = a + a_size; - s = b + b_size; + tau = a + a_size; + memset(tau, 0, tau_size); params->M = m; params->N = n; - params->NRHS = nrhs; params->A = a; - params->B = b; - params->S = s; + params->TAU = tau; params->LDA = lda; - params->LDB = ldb; { /* compute optimal work size */ + @ftyp@ work_size_query; - @frealtyp@ rwork_size_query; - fortran_int iwork_size_query; params->WORK = &work_size_query; - params->IWORK = &iwork_size_query; - params->RWORK = &rwork_size_query; params->LWORK = -1; if (call_@lapack_func@(params) != 0) goto error; - work_count = (fortran_int)work_size_query.r; + work_count = (fortran_int) ((@ftyp@*)params->WORK)->r; - work_size = (size_t )work_size_query.r * sizeof(@ftyp@); - rwork_size = (size_t)rwork_size_query * sizeof(@frealtyp@); - iwork_size = (size_t)iwork_size_query * sizeof(fortran_int); } - mem_buff2 = malloc(work_size + rwork_size + iwork_size); + params->LWORK = fortran_int_max(fortran_int_max(1, n), + work_count); + + work_size = (size_t) params->LWORK * sizeof(@ftyp@); + + mem_buff2 = malloc(work_size); if (!mem_buff2) goto error; work = mem_buff2; - rwork = work + work_size; - iwork = rwork + rwork_size; params->WORK = work; - params->RWORK = rwork; - params->IWORK = iwork; - params->LWORK = work_count; return 1; error: @@ -3120,20 +3072,11 @@ init_@lapack_func@(GELSD_PARAMS_t *params, /**end repeat**/ - /**begin repeat - #TYPE=FLOAT,DOUBLE,CFLOAT,CDOUBLE# - #REALTYPE=FLOAT,DOUBLE,FLOAT,DOUBLE# - #lapack_func=sgelsd,dgelsd,cgelsd,zgelsd# - #dot_func=sdot,ddot,cdotc,zdotc# - #typ = npy_float, npy_double, npy_cfloat, npy_cdouble# - #basetyp = npy_float, npy_double, npy_float, npy_double# - #ftyp = fortran_real, fortran_doublereal, - fortran_complex, fortran_doublecomplex# - #cmplx = 0, 0, 1, 1# + #lapack_func=dgeqrf,zgeqrf# */ static inline void -release_@lapack_func@(GELSD_PARAMS_t* params) +release_@lapack_func@(GEQRF_PARAMS_t* params) { /* A and WORK contain allocated blocks */ free(params->A); @@ -3141,84 +3084,46 @@ release_@lapack_func@(GELSD_PARAMS_t* params) memset(params, 0, sizeof(*params)); } -/** Compute the squared l2 norm of a contiguous vector */ -static @basetyp@ -@TYPE@_abs2(@typ@ *p, npy_intp n) { - npy_intp i; - @basetyp@ res = 0; - for (i = 0; i < n; i++) { - @typ@ el = p[i]; -#if @cmplx@ - res += el.real*el.real + el.imag*el.imag; -#else - res += el*el; -#endif - } - return res; -} +/**end repeat**/ +/**begin repeat + #TYPE=DOUBLE,CDOUBLE# + #REALTYPE=DOUBLE,DOUBLE# + #lapack_func=dgeqrf,zgeqrf# + #typ = npy_double,npy_cdouble# + #basetyp = npy_double,npy_double# + #ftyp = fortran_doublereal,fortran_doublecomplex# + #cmplx = 0, 1# + */ static void -@TYPE@_lstsq(char **args, npy_intp *dimensions, npy_intp *steps, - void *NPY_UNUSED(func)) +@TYPE@_qr_r_raw(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)) { - GELSD_PARAMS_t params; + GEQRF_PARAMS_t params; int error_occurred = get_fp_invalid_and_clear(); - fortran_int n, m, nrhs; - fortran_int excess; + fortran_int n, m; - INIT_OUTER_LOOP_7 + INIT_OUTER_LOOP_2 m = (fortran_int)dimensions[0]; n = (fortran_int)dimensions[1]; - nrhs = (fortran_int)dimensions[2]; - excess = m - n; - if (init_@lapack_func@(¶ms, m, n, nrhs)) { - LINEARIZE_DATA_t a_in, b_in, x_out, s_out, r_out; + if (init_@lapack_func@(¶ms, m, n)) { + LINEARIZE_DATA_t a_in, tau_out; init_linearize_data(&a_in, n, m, steps[1], steps[0]); - init_linearize_data_ex(&b_in, nrhs, m, steps[3], steps[2], fortran_int_max(n, m)); - init_linearize_data_ex(&x_out, nrhs, n, steps[5], steps[4], fortran_int_max(n, m)); - init_linearize_data(&r_out, 1, nrhs, 1, steps[6]); - init_linearize_data(&s_out, 1, fortran_int_min(n, m), 1, steps[7]); + init_linearize_data(&tau_out, 1, fortran_int_min(m, n), 1, steps[2]); - BEGIN_OUTER_LOOP_7 + BEGIN_OUTER_LOOP_2 int not_ok; linearize_@TYPE@_matrix(params.A, args[0], &a_in); - linearize_@TYPE@_matrix(params.B, args[1], &b_in); - params.RCOND = args[2]; not_ok = call_@lapack_func@(¶ms); if (!not_ok) { - delinearize_@TYPE@_matrix(args[3], params.B, &x_out); - *(npy_int*) args[5] = params.RANK; - delinearize_@REALTYPE@_matrix(args[6], params.S, &s_out); - - /* Note that linalg.lstsq discards this when excess == 0 */ - if (excess >= 0 && params.RANK == n) { - /* Compute the residuals as the square sum of each column */ - int i; - char *resid = args[4]; - @ftyp@ *components = (@ftyp@ *)params.B + n; - for (i = 0; i < nrhs; i++) { - @ftyp@ *vector = components + i*m; - /* Numpy and fortran floating types are the same size, - * so this cast is safe */ - @basetyp@ abs2 = @TYPE@_abs2((@typ@ *)vector, excess); - memcpy( - resid + i*r_out.column_strides, - &abs2, sizeof(abs2)); - } - } - else { - /* Note that this is always discarded by linalg.lstsq */ - nan_@REALTYPE@_matrix(args[4], &r_out); - } + delinearize_@TYPE@_matrix(args[0], params.A, &a_in); + delinearize_@TYPE@_matrix(args[1], params.TAU, &tau_out); } else { error_occurred = 1; - nan_@TYPE@_matrix(args[3], &x_out); - nan_@REALTYPE@_matrix(args[4], &r_out); - *(npy_int*) args[5] = -1; - nan_@REALTYPE@_matrix(args[6], &s_out); + nan_@TYPE@_matrix(args[1], &tau_out); } END_OUTER_LOOP @@ -3230,25 +3135,808 @@ static void /**end repeat**/ -#pragma GCC diagnostic pop - /* -------------------------------------------------------------------------- */ - /* gufunc registration */ + /* qr common code (modes - reduced and complete) */ -static void *array_of_nulls[] = { - (void *)NULL, - (void *)NULL, - (void *)NULL, - (void *)NULL, +typedef struct gqr_params_struct +{ + fortran_int M; + fortran_int MC; + fortran_int MN; + void* A; + void *Q; + fortran_int LDA; + void* TAU; + void *WORK; + fortran_int LWORK; +} GQR_PARAMS_t; - (void *)NULL, - (void *)NULL, - (void *)NULL, - (void *)NULL, +/**begin repeat + #lapack_func=dorgqr,zungqr# + */ +static inline fortran_int +call_@lapack_func@(GQR_PARAMS_t *params) +{ + fortran_int rv; + LAPACK(@lapack_func@)(¶ms->M, ¶ms->MC, ¶ms->MN, + params->Q, ¶ms->LDA, + params->TAU, + params->WORK, ¶ms->LWORK, + &rv); + return rv; +} + +/**end repeat**/ + +/**begin repeat + #lapack_func=dorgqr# + #ftyp=fortran_doublereal# + */ +static inline int +init_@lapack_func@_common(GQR_PARAMS_t *params, + fortran_int m, + fortran_int n, + fortran_int mc) +{ + npy_uint8 *mem_buff = NULL; + npy_uint8 *mem_buff2 = NULL; + npy_uint8 *a, *q, *tau, *work; + fortran_int min_m_n = fortran_int_min(m, n); + size_t safe_mc = mc; + size_t safe_min_m_n = min_m_n; + size_t safe_m = m; + size_t safe_n = n; + size_t a_size = safe_m * safe_n * sizeof(@ftyp@); + size_t q_size = safe_m * safe_mc * sizeof(@ftyp@); + size_t tau_size = safe_min_m_n * sizeof(@ftyp@); + + fortran_int work_count; + size_t work_size; + fortran_int lda = fortran_int_max(1, m); + + mem_buff = malloc(q_size + tau_size + a_size); + + if (!mem_buff) + goto error; + + q = mem_buff; + tau = q + q_size; + a = tau + tau_size; + + + params->M = m; + params->MC = mc; + params->MN = min_m_n; + params->A = a; + params->Q = q; + params->TAU = tau; + params->LDA = lda; + + { + /* compute optimal work size */ + @ftyp@ work_size_query; + + params->WORK = &work_size_query; + params->LWORK = -1; + + if (call_@lapack_func@(params) != 0) + goto error; + + work_count = (fortran_int) *(@ftyp@*) params->WORK; + + } + + params->LWORK = fortran_int_max(fortran_int_max(1, n), work_count); + + work_size = (size_t) params->LWORK * sizeof(@ftyp@); + + mem_buff2 = malloc(work_size); + if (!mem_buff2) + goto error; + + work = mem_buff2; + + params->WORK = work; + + return 1; + error: + TRACE_TXT("%s failed init\n", __FUNCTION__); + free(mem_buff); + free(mem_buff2); + memset(params, 0, sizeof(*params)); + + return 0; +} + +/**end repeat**/ + +/**begin repeat + #lapack_func=zungqr# + #ftyp=fortran_doublecomplex# + */ +static inline int +init_@lapack_func@_common(GQR_PARAMS_t *params, + fortran_int m, + fortran_int n, + fortran_int mc) +{ + npy_uint8 *mem_buff = NULL; + npy_uint8 *mem_buff2 = NULL; + npy_uint8 *a, *q, *tau, *work; + fortran_int min_m_n = fortran_int_min(m, n); + size_t safe_mc = mc; + size_t safe_min_m_n = min_m_n; + size_t safe_m = m; + size_t safe_n = n; + + size_t a_size = safe_m * safe_n * sizeof(@ftyp@); + size_t q_size = safe_m * safe_mc * sizeof(@ftyp@); + size_t tau_size = safe_min_m_n * sizeof(@ftyp@); + + fortran_int work_count; + size_t work_size; + fortran_int lda = fortran_int_max(1, m); + + mem_buff = malloc(q_size + tau_size + a_size); + + if (!mem_buff) + goto error; + + q = mem_buff; + tau = q + q_size; + a = tau + tau_size; + + + params->M = m; + params->MC = mc; + params->MN = min_m_n; + params->A = a; + params->Q = q; + params->TAU = tau; + params->LDA = lda; + + { + /* compute optimal work size */ + @ftyp@ work_size_query; + + params->WORK = &work_size_query; + params->LWORK = -1; + + if (call_@lapack_func@(params) != 0) + goto error; + + work_count = (fortran_int) ((@ftyp@*)params->WORK)->r; + + } + + params->LWORK = fortran_int_max(fortran_int_max(1, n), + work_count); + + work_size = (size_t) params->LWORK * sizeof(@ftyp@); + + mem_buff2 = malloc(work_size); + if (!mem_buff2) + goto error; + + work = mem_buff2; + + params->WORK = work; + params->LWORK = work_count; + + return 1; + error: + TRACE_TXT("%s failed init\n", __FUNCTION__); + free(mem_buff); + free(mem_buff2); + memset(params, 0, sizeof(*params)); + + return 0; +} + +/**end repeat**/ + +/* -------------------------------------------------------------------------- */ + /* qr (modes - reduced) */ + + +static inline void +dump_gqr_params(const char *name, + GQR_PARAMS_t *params) +{ + TRACE_TXT("\n%s:\n"\ + + "%14s: %18p\n"\ + "%14s: %18p\n"\ + "%14s: %18p\n"\ + "%14s: %18d\n"\ + "%14s: %18d\n"\ + "%14s: %18d\n"\ + "%14s: %18d\n"\ + "%14s: %18d\n", + + name, + + "Q", params->Q, + "TAU", params->TAU, + "WORK", params->WORK, + + "M", (int)params->M, + "MC", (int)params->MC, + "MN", (int)params->MN, + "LDA", (int)params->LDA, + "LWORK", (int)params->LWORK); +} + +/**begin repeat + #lapack_func=dorgqr,zungqr# + #ftyp=fortran_doublereal,fortran_doublecomplex# + */ +static inline int +init_@lapack_func@(GQR_PARAMS_t *params, + fortran_int m, + fortran_int n) +{ + return init_@lapack_func@_common( + params, m, n, + fortran_int_min(m, n)); +} + +/**end repeat**/ + +/**begin repeat + #lapack_func=dorgqr,zungqr# + */ +static inline void +release_@lapack_func@(GQR_PARAMS_t* params) +{ + /* A and WORK contain allocated blocks */ + free(params->Q); + free(params->WORK); + memset(params, 0, sizeof(*params)); +} + +/**end repeat**/ + +/**begin repeat + #TYPE=DOUBLE,CDOUBLE# + #REALTYPE=DOUBLE,DOUBLE# + #lapack_func=dorgqr,zungqr# + #typ = npy_double, npy_cdouble# + #basetyp = npy_double, npy_double# + #ftyp = fortran_doublereal,fortran_doublecomplex# + #cmplx = 0, 1# + */ +static void +@TYPE@_qr_reduced(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)) +{ + GQR_PARAMS_t params; + int error_occurred = get_fp_invalid_and_clear(); + fortran_int n, m; + + INIT_OUTER_LOOP_3 + + m = (fortran_int)dimensions[0]; + n = (fortran_int)dimensions[1]; + + if (init_@lapack_func@(¶ms, m, n)) { + LINEARIZE_DATA_t a_in, tau_in, q_out; + + init_linearize_data(&a_in, n, m, steps[1], steps[0]); + init_linearize_data(&tau_in, 1, fortran_int_min(m, n), 1, steps[2]); + init_linearize_data(&q_out, fortran_int_min(m, n), m, steps[4], steps[3]); + + BEGIN_OUTER_LOOP_3 + int not_ok; + linearize_@TYPE@_matrix(params.A, args[0], &a_in); + linearize_@TYPE@_matrix(params.Q, args[0], &a_in); + linearize_@TYPE@_matrix(params.TAU, args[1], &tau_in); + not_ok = call_@lapack_func@(¶ms); + if (!not_ok) { + delinearize_@TYPE@_matrix(args[2], params.Q, &q_out); + } else { + error_occurred = 1; + nan_@TYPE@_matrix(args[2], &q_out); + } + END_OUTER_LOOP + + release_@lapack_func@(¶ms); + } + + set_fp_invalid_or_clear(error_occurred); +} + +/**end repeat**/ + +/* -------------------------------------------------------------------------- */ + /* qr (modes - complete) */ + +/**begin repeat + #lapack_func=dorgqr,zungqr# + #ftyp=fortran_doublereal,fortran_doublecomplex# + */ +static inline int +init_@lapack_func@_complete(GQR_PARAMS_t *params, + fortran_int m, + fortran_int n) +{ + return init_@lapack_func@_common(params, m, n, m); +} + +/**end repeat**/ + +/**begin repeat + #TYPE=DOUBLE,CDOUBLE# + #REALTYPE=DOUBLE,DOUBLE# + #lapack_func=dorgqr,zungqr# + #typ = npy_double,npy_cdouble# + #basetyp = npy_double,npy_double# + #ftyp = fortran_doublereal,fortran_doublecomplex# + #cmplx = 0, 1# + */ +static void +@TYPE@_qr_complete(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)) +{ + GQR_PARAMS_t params; + int error_occurred = get_fp_invalid_and_clear(); + fortran_int n, m; + + INIT_OUTER_LOOP_3 + + m = (fortran_int)dimensions[0]; + n = (fortran_int)dimensions[1]; + + + if (init_@lapack_func@_complete(¶ms, m, n)) { + LINEARIZE_DATA_t a_in, tau_in, q_out; + + init_linearize_data(&a_in, n, m, steps[1], steps[0]); + init_linearize_data(&tau_in, 1, fortran_int_min(m, n), 1, steps[2]); + init_linearize_data(&q_out, m, m, steps[4], steps[3]); + + BEGIN_OUTER_LOOP_3 + int not_ok; + linearize_@TYPE@_matrix(params.A, args[0], &a_in); + linearize_@TYPE@_matrix(params.Q, args[0], &a_in); + linearize_@TYPE@_matrix(params.TAU, args[1], &tau_in); + not_ok = call_@lapack_func@(¶ms); + if (!not_ok) { + delinearize_@TYPE@_matrix(args[2], params.Q, &q_out); + } else { + error_occurred = 1; + nan_@TYPE@_matrix(args[2], &q_out); + } + END_OUTER_LOOP + + release_@lapack_func@(¶ms); + } + + set_fp_invalid_or_clear(error_occurred); +} + +/**end repeat**/ + +/* -------------------------------------------------------------------------- */ + /* least squares */ + +typedef struct gelsd_params_struct +{ + fortran_int M; + fortran_int N; + fortran_int NRHS; + void *A; + fortran_int LDA; + void *B; + fortran_int LDB; + void *S; + void *RCOND; + fortran_int RANK; + void *WORK; + fortran_int LWORK; + void *RWORK; + void *IWORK; +} GELSD_PARAMS_t; + + +static inline void +dump_gelsd_params(const char *name, + GELSD_PARAMS_t *params) +{ + TRACE_TXT("\n%s:\n"\ + + "%14s: %18p\n"\ + "%14s: %18p\n"\ + "%14s: %18p\n"\ + "%14s: %18p\n"\ + "%14s: %18p\n"\ + "%14s: %18p\n"\ + + "%14s: %18d\n"\ + "%14s: %18d\n"\ + "%14s: %18d\n"\ + "%14s: %18d\n"\ + "%14s: %18d\n"\ + "%14s: %18d\n"\ + "%14s: %18d\n"\ + + "%14s: %18p\n", + + name, + + "A", params->A, + "B", params->B, + "S", params->S, + "WORK", params->WORK, + "RWORK", params->RWORK, + "IWORK", params->IWORK, + + "M", (int)params->M, + "N", (int)params->N, + "NRHS", (int)params->NRHS, + "LDA", (int)params->LDA, + "LDB", (int)params->LDB, + "LWORK", (int)params->LWORK, + "RANK", (int)params->RANK, + + "RCOND", params->RCOND); +} + + +/**begin repeat + #TYPE=FLOAT,DOUBLE# + #lapack_func=sgelsd,dgelsd# + #ftyp=fortran_real,fortran_doublereal# + */ + +static inline fortran_int +call_@lapack_func@(GELSD_PARAMS_t *params) +{ + fortran_int rv; + LAPACK(@lapack_func@)(¶ms->M, ¶ms->N, ¶ms->NRHS, + params->A, ¶ms->LDA, + params->B, ¶ms->LDB, + params->S, + params->RCOND, ¶ms->RANK, + params->WORK, ¶ms->LWORK, + params->IWORK, + &rv); + return rv; +} + +static inline int +init_@lapack_func@(GELSD_PARAMS_t *params, + fortran_int m, + fortran_int n, + fortran_int nrhs) +{ + npy_uint8 *mem_buff = NULL; + npy_uint8 *mem_buff2 = NULL; + npy_uint8 *a, *b, *s, *work, *iwork; + fortran_int min_m_n = fortran_int_min(m, n); + fortran_int max_m_n = fortran_int_max(m, n); + size_t safe_min_m_n = min_m_n; + size_t safe_max_m_n = max_m_n; + size_t safe_m = m; + size_t safe_n = n; + size_t safe_nrhs = nrhs; + + size_t a_size = safe_m * safe_n * sizeof(@ftyp@); + size_t b_size = safe_max_m_n * safe_nrhs * sizeof(@ftyp@); + size_t s_size = safe_min_m_n * sizeof(@ftyp@); + + fortran_int work_count; + size_t work_size; + size_t iwork_size; + fortran_int lda = fortran_int_max(1, m); + fortran_int ldb = fortran_int_max(1, fortran_int_max(m,n)); + + mem_buff = malloc(a_size + b_size + s_size); + + if (!mem_buff) + goto error; + + a = mem_buff; + b = a + a_size; + s = b + b_size; + + + params->M = m; + params->N = n; + params->NRHS = nrhs; + params->A = a; + params->B = b; + params->S = s; + params->LDA = lda; + params->LDB = ldb; + + { + /* compute optimal work size */ + @ftyp@ work_size_query; + fortran_int iwork_size_query; + + params->WORK = &work_size_query; + params->IWORK = &iwork_size_query; + params->RWORK = NULL; + params->LWORK = -1; + + if (call_@lapack_func@(params) != 0) + goto error; + + work_count = (fortran_int)work_size_query; + + work_size = (size_t) work_size_query * sizeof(@ftyp@); + iwork_size = (size_t)iwork_size_query * sizeof(fortran_int); + } + + mem_buff2 = malloc(work_size + iwork_size); + if (!mem_buff2) + goto error; + + work = mem_buff2; + iwork = work + work_size; + + params->WORK = work; + params->RWORK = NULL; + params->IWORK = iwork; + params->LWORK = work_count; + + return 1; + error: + TRACE_TXT("%s failed init\n", __FUNCTION__); + free(mem_buff); + free(mem_buff2); + memset(params, 0, sizeof(*params)); + + return 0; +} + +/**end repeat**/ + +/**begin repeat + #TYPE=CFLOAT,CDOUBLE# + #ftyp=fortran_complex,fortran_doublecomplex# + #frealtyp=fortran_real,fortran_doublereal# + #typ=COMPLEX_t,DOUBLECOMPLEX_t# + #lapack_func=cgelsd,zgelsd# + */ + +static inline fortran_int +call_@lapack_func@(GELSD_PARAMS_t *params) +{ + fortran_int rv; + LAPACK(@lapack_func@)(¶ms->M, ¶ms->N, ¶ms->NRHS, + params->A, ¶ms->LDA, + params->B, ¶ms->LDB, + params->S, + params->RCOND, ¶ms->RANK, + params->WORK, ¶ms->LWORK, + params->RWORK, params->IWORK, + &rv); + return rv; +} + +static inline int +init_@lapack_func@(GELSD_PARAMS_t *params, + fortran_int m, + fortran_int n, + fortran_int nrhs) +{ + npy_uint8 *mem_buff = NULL; + npy_uint8 *mem_buff2 = NULL; + npy_uint8 *a, *b, *s, *work, *iwork, *rwork; + fortran_int min_m_n = fortran_int_min(m, n); + fortran_int max_m_n = fortran_int_max(m, n); + size_t safe_min_m_n = min_m_n; + size_t safe_max_m_n = max_m_n; + size_t safe_m = m; + size_t safe_n = n; + size_t safe_nrhs = nrhs; + + size_t a_size = safe_m * safe_n * sizeof(@ftyp@); + size_t b_size = safe_max_m_n * safe_nrhs * sizeof(@ftyp@); + size_t s_size = safe_min_m_n * sizeof(@frealtyp@); + + fortran_int work_count; + size_t work_size, rwork_size, iwork_size; + fortran_int lda = fortran_int_max(1, m); + fortran_int ldb = fortran_int_max(1, fortran_int_max(m,n)); + + mem_buff = malloc(a_size + b_size + s_size); + + if (!mem_buff) + goto error; + + a = mem_buff; + b = a + a_size; + s = b + b_size; + + + params->M = m; + params->N = n; + params->NRHS = nrhs; + params->A = a; + params->B = b; + params->S = s; + params->LDA = lda; + params->LDB = ldb; + + { + /* compute optimal work size */ + @ftyp@ work_size_query; + @frealtyp@ rwork_size_query; + fortran_int iwork_size_query; + + params->WORK = &work_size_query; + params->IWORK = &iwork_size_query; + params->RWORK = &rwork_size_query; + params->LWORK = -1; + + if (call_@lapack_func@(params) != 0) + goto error; + + work_count = (fortran_int)work_size_query.r; + + work_size = (size_t )work_size_query.r * sizeof(@ftyp@); + rwork_size = (size_t)rwork_size_query * sizeof(@frealtyp@); + iwork_size = (size_t)iwork_size_query * sizeof(fortran_int); + } + + mem_buff2 = malloc(work_size + rwork_size + iwork_size); + if (!mem_buff2) + goto error; + + work = mem_buff2; + rwork = work + work_size; + iwork = rwork + rwork_size; + + params->WORK = work; + params->RWORK = rwork; + params->IWORK = iwork; + params->LWORK = work_count; + + return 1; + error: + TRACE_TXT("%s failed init\n", __FUNCTION__); + free(mem_buff); + free(mem_buff2); + memset(params, 0, sizeof(*params)); + + return 0; +} + +/**end repeat**/ + + +/**begin repeat + #TYPE=FLOAT,DOUBLE,CFLOAT,CDOUBLE# + #REALTYPE=FLOAT,DOUBLE,FLOAT,DOUBLE# + #lapack_func=sgelsd,dgelsd,cgelsd,zgelsd# + #dot_func=sdot,ddot,cdotc,zdotc# + #typ = npy_float, npy_double, npy_cfloat, npy_cdouble# + #basetyp = npy_float, npy_double, npy_float, npy_double# + #ftyp = fortran_real, fortran_doublereal, + fortran_complex, fortran_doublecomplex# + #cmplx = 0, 0, 1, 1# + */ +static inline void +release_@lapack_func@(GELSD_PARAMS_t* params) +{ + /* A and WORK contain allocated blocks */ + free(params->A); + free(params->WORK); + memset(params, 0, sizeof(*params)); +} + +/** Compute the squared l2 norm of a contiguous vector */ +static @basetyp@ +@TYPE@_abs2(@typ@ *p, npy_intp n) { + npy_intp i; + @basetyp@ res = 0; + for (i = 0; i < n; i++) { + @typ@ el = p[i]; +#if @cmplx@ + res += el.real*el.real + el.imag*el.imag; +#else + res += el*el; +#endif + } + return res; +} + +static void +@TYPE@_lstsq(char **args, npy_intp const *dimensions, npy_intp const *steps, + void *NPY_UNUSED(func)) +{ + GELSD_PARAMS_t params; + int error_occurred = get_fp_invalid_and_clear(); + fortran_int n, m, nrhs; + fortran_int excess; + + INIT_OUTER_LOOP_7 + + m = (fortran_int)dimensions[0]; + n = (fortran_int)dimensions[1]; + nrhs = (fortran_int)dimensions[2]; + excess = m - n; + + if (init_@lapack_func@(¶ms, m, n, nrhs)) { + LINEARIZE_DATA_t a_in, b_in, x_out, s_out, r_out; + + init_linearize_data(&a_in, n, m, steps[1], steps[0]); + init_linearize_data_ex(&b_in, nrhs, m, steps[3], steps[2], fortran_int_max(n, m)); + init_linearize_data_ex(&x_out, nrhs, n, steps[5], steps[4], fortran_int_max(n, m)); + init_linearize_data(&r_out, 1, nrhs, 1, steps[6]); + init_linearize_data(&s_out, 1, fortran_int_min(n, m), 1, steps[7]); + + BEGIN_OUTER_LOOP_7 + int not_ok; + linearize_@TYPE@_matrix(params.A, args[0], &a_in); + linearize_@TYPE@_matrix(params.B, args[1], &b_in); + params.RCOND = args[2]; + not_ok = call_@lapack_func@(¶ms); + if (!not_ok) { + delinearize_@TYPE@_matrix(args[3], params.B, &x_out); + *(npy_int*) args[5] = params.RANK; + delinearize_@REALTYPE@_matrix(args[6], params.S, &s_out); + + /* Note that linalg.lstsq discards this when excess == 0 */ + if (excess >= 0 && params.RANK == n) { + /* Compute the residuals as the square sum of each column */ + int i; + char *resid = args[4]; + @ftyp@ *components = (@ftyp@ *)params.B + n; + for (i = 0; i < nrhs; i++) { + @ftyp@ *vector = components + i*m; + /* Numpy and fortran floating types are the same size, + * so this cast is safe */ + @basetyp@ abs2 = @TYPE@_abs2((@typ@ *)vector, excess); + memcpy( + resid + i*r_out.column_strides, + &abs2, sizeof(abs2)); + } + } + else { + /* Note that this is always discarded by linalg.lstsq */ + nan_@REALTYPE@_matrix(args[4], &r_out); + } + } else { + error_occurred = 1; + nan_@TYPE@_matrix(args[3], &x_out); + nan_@REALTYPE@_matrix(args[4], &r_out); + *(npy_int*) args[5] = -1; + nan_@REALTYPE@_matrix(args[6], &s_out); + } + END_OUTER_LOOP + + release_@lapack_func@(¶ms); + } + + set_fp_invalid_or_clear(error_occurred); +} + +/**end repeat**/ + +#pragma GCC diagnostic pop + +/* -------------------------------------------------------------------------- */ + /* gufunc registration */ + +static void *array_of_nulls[] = { + (void *)NULL, + (void *)NULL, + (void *)NULL, + (void *)NULL, + + (void *)NULL, + (void *)NULL, + (void *)NULL, + (void *)NULL, + + (void *)NULL, (void *)NULL, (void *)NULL, - (void *)NULL, (void *)NULL, (void *)NULL, @@ -3286,6 +3974,17 @@ static void *array_of_nulls[] = { CDOUBLE_ ## NAME \ } +/* The single precision functions are not used at all, + * due to input data being promoted to double precision + * in Python, so they are not implemented here. + */ +#define GUFUNC_FUNC_ARRAY_QR(NAME) \ + static PyUFuncGenericFunction \ + FUNC_ARRAY_NAME(NAME)[] = { \ + DOUBLE_ ## NAME, \ + CDOUBLE_ ## NAME \ + } + GUFUNC_FUNC_ARRAY_REAL_COMPLEX(slogdet); GUFUNC_FUNC_ARRAY_REAL_COMPLEX(det); @@ -3300,6 +3999,9 @@ GUFUNC_FUNC_ARRAY_REAL_COMPLEX(cholesky_lo); GUFUNC_FUNC_ARRAY_REAL_COMPLEX(svd_N); GUFUNC_FUNC_ARRAY_REAL_COMPLEX(svd_S); GUFUNC_FUNC_ARRAY_REAL_COMPLEX(svd_A); +GUFUNC_FUNC_ARRAY_QR(qr_r_raw); +GUFUNC_FUNC_ARRAY_QR(qr_reduced); +GUFUNC_FUNC_ARRAY_QR(qr_complete); GUFUNC_FUNC_ARRAY_REAL_COMPLEX(lstsq); GUFUNC_FUNC_ARRAY_EIG(eig); GUFUNC_FUNC_ARRAY_EIG(eigvals); @@ -3366,6 +4068,24 @@ static char svd_1_3_types[] = { NPY_CDOUBLE, NPY_CDOUBLE, NPY_DOUBLE, NPY_CDOUBLE }; +/* A, tau */ +static char qr_r_raw_types[] = { + NPY_DOUBLE, NPY_DOUBLE, + NPY_CDOUBLE, NPY_CDOUBLE, +}; + +/* A, tau, q */ +static char qr_reduced_types[] = { + NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE, + NPY_CDOUBLE, NPY_CDOUBLE, NPY_CDOUBLE, +}; + +/* A, tau, q */ +static char qr_complete_types[] = { + NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE, + NPY_CDOUBLE, NPY_CDOUBLE, NPY_CDOUBLE, +}; + /* A, b, rcond, x, resid, rank, s, */ static char lstsq_types[] = { NPY_FLOAT, NPY_FLOAT, NPY_FLOAT, NPY_FLOAT, NPY_FLOAT, NPY_INT, NPY_FLOAT, @@ -3565,6 +4285,42 @@ GUFUNC_DESCRIPTOR_t gufunc_descriptors [] = { FUNC_ARRAY_NAME(eigvals), eigvals_types }, + { + "qr_r_raw_m", + "(m,n)->(m)", + "Compute TAU vector for the last two dimensions \n"\ + "and broadcast to the rest. For m <= n. \n", + 2, 1, 1, + FUNC_ARRAY_NAME(qr_r_raw), + qr_r_raw_types + }, + { + "qr_r_raw_n", + "(m,n)->(n)", + "Compute TAU vector for the last two dimensions \n"\ + "and broadcast to the rest. For m > n. \n", + 2, 1, 1, + FUNC_ARRAY_NAME(qr_r_raw), + qr_r_raw_types + }, + { + "qr_reduced", + "(m,n),(k)->(m,k)", + "Compute Q matrix for the last two dimensions \n"\ + "and broadcast to the rest. \n", + 2, 2, 1, + FUNC_ARRAY_NAME(qr_reduced), + qr_reduced_types + }, + { + "qr_complete", + "(m,n),(n)->(m,m)", + "Compute Q matrix for the last two dimensions \n"\ + "and broadcast to the rest. For m > n. \n", + 2, 2, 1, + FUNC_ARRAY_NAME(qr_complete), + qr_complete_types + }, { "lstsq_m", "(m,n),(m,nrhs),()->(n,nrhs),(nrhs),(),(m)", @@ -3585,7 +4341,7 @@ GUFUNC_DESCRIPTOR_t gufunc_descriptors [] = { } }; -static void +static int addUfuncs(PyObject *dictionary) { PyObject *f; int i; @@ -3604,12 +4360,19 @@ addUfuncs(PyObject *dictionary) { d->doc, 0, d->signature); - PyDict_SetItemString(dictionary, d->name, f); + if (f == NULL) { + return -1; + } #if 0 dump_ufunc_object((PyUFuncObject*) f); #endif + int ret = PyDict_SetItemString(dictionary, d->name, f); Py_DECREF(f); + if (ret < 0) { + return -1; + } } + return 0; } @@ -3621,7 +4384,6 @@ static PyMethodDef UMath_LinAlgMethods[] = { {NULL, NULL, 0, NULL} /* Sentinel */ }; -#if defined(NPY_PY3K) static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, UMATH_LINALG_MODULE_NAME, @@ -3633,48 +4395,41 @@ static struct PyModuleDef moduledef = { NULL, NULL }; -#endif -#if defined(NPY_PY3K) -#define RETVAL(x) x -PyObject *PyInit__umath_linalg(void) -#else -#define RETVAL(x) -PyMODINIT_FUNC -init_umath_linalg(void) -#endif +PyMODINIT_FUNC PyInit__umath_linalg(void) { PyObject *m; PyObject *d; PyObject *version; init_constants(); -#if defined(NPY_PY3K) m = PyModule_Create(&moduledef); -#else - m = Py_InitModule(UMATH_LINALG_MODULE_NAME, UMath_LinAlgMethods); -#endif if (m == NULL) { - return RETVAL(NULL); + return NULL; } import_array(); import_ufunc(); d = PyModule_GetDict(m); + if (d == NULL) { + return NULL; + } - version = PyString_FromString(umath_linalg_version_string); - PyDict_SetItemString(d, "__version__", version); + version = PyUnicode_FromString(umath_linalg_version_string); + if (version == NULL) { + return NULL; + } + int ret = PyDict_SetItemString(d, "__version__", version); Py_DECREF(version); + if (ret < 0) { + return NULL; + } /* Load the ufunc operators into the module's namespace */ - addUfuncs(d); - - if (PyErr_Occurred()) { - PyErr_SetString(PyExc_RuntimeError, - "cannot load _umath_linalg module."); - return RETVAL(NULL); + if (addUfuncs(d) < 0) { + return NULL; } - return RETVAL(m); + return m; } diff --git a/numpy/ma/README.txt b/numpy/ma/README.rst similarity index 99% rename from numpy/ma/README.txt rename to numpy/ma/README.rst index ef9635e57443..47f20d6458e8 100644 --- a/numpy/ma/README.txt +++ b/numpy/ma/README.rst @@ -4,7 +4,7 @@ A Guide to Masked Arrays in NumPy .. Contents:: -See http://www.scipy.org/scipy/numpy/wiki/MaskedArray +See http://www.scipy.org/scipy/numpy/wiki/MaskedArray (dead link) for updates of this document. @@ -18,7 +18,7 @@ that could store some additional information along with numerical values, while keeping the possibility for missing data (picture storing a series of dates along with measurements, what would later become the `TimeSeries Scikit <http://projects.scipy.org/scipy/scikits/wiki/TimeSeries>`__ -. +(dead link). I started to implement such a class, but then quickly realized that any additional information disappeared when processing these subarrays diff --git a/numpy/ma/__init__.py b/numpy/ma/__init__.py index 34f21b8b1360..870cc4ef2daa 100644 --- a/numpy/ma/__init__.py +++ b/numpy/ma/__init__.py @@ -39,8 +39,6 @@ .. moduleauthor:: Jarrod Millman """ -from __future__ import division, absolute_import, print_function - from . import core from .core import * @@ -51,6 +49,6 @@ __all__ += core.__all__ __all__ += extras.__all__ -from numpy.testing._private.pytesttester import PytestTester +from numpy._pytesttester import PytestTester test = PytestTester(__name__) del PytestTester diff --git a/numpy/ma/__init__.pyi b/numpy/ma/__init__.pyi new file mode 100644 index 000000000000..26d44b508c47 --- /dev/null +++ b/numpy/ma/__init__.pyi @@ -0,0 +1,236 @@ +from typing import Any, List + +from numpy._pytesttester import PytestTester + +from numpy.ma import extras as extras + +from numpy.ma.core import ( + MAError as MAError, + MaskError as MaskError, + MaskType as MaskType, + MaskedArray as MaskedArray, + abs as abs, + absolute as absolute, + add as add, + all as all, + allclose as allclose, + allequal as allequal, + alltrue as alltrue, + amax as amax, + amin as amin, + angle as angle, + anom as anom, + anomalies as anomalies, + any as any, + append as append, + arange as arange, + arccos as arccos, + arccosh as arccosh, + arcsin as arcsin, + arcsinh as arcsinh, + arctan as arctan, + arctan2 as arctan2, + arctanh as arctanh, + argmax as argmax, + argmin as argmin, + argsort as argsort, + around as around, + array as array, + asanyarray as asanyarray, + asarray as asarray, + bitwise_and as bitwise_and, + bitwise_or as bitwise_or, + bitwise_xor as bitwise_xor, + bool_ as bool_, + ceil as ceil, + choose as choose, + clip as clip, + common_fill_value as common_fill_value, + compress as compress, + compressed as compressed, + concatenate as concatenate, + conjugate as conjugate, + convolve as convolve, + copy as copy, + correlate as correlate, + cos as cos, + cosh as cosh, + count as count, + cumprod as cumprod, + cumsum as cumsum, + default_fill_value as default_fill_value, + diag as diag, + diagonal as diagonal, + diff as diff, + divide as divide, + empty as empty, + empty_like as empty_like, + equal as equal, + exp as exp, + expand_dims as expand_dims, + fabs as fabs, + filled as filled, + fix_invalid as fix_invalid, + flatten_mask as flatten_mask, + flatten_structured_array as flatten_structured_array, + floor as floor, + floor_divide as floor_divide, + fmod as fmod, + frombuffer as frombuffer, + fromflex as fromflex, + fromfunction as fromfunction, + getdata as getdata, + getmask as getmask, + getmaskarray as getmaskarray, + greater as greater, + greater_equal as greater_equal, + harden_mask as harden_mask, + hypot as hypot, + identity as identity, + ids as ids, + indices as indices, + inner as inner, + innerproduct as innerproduct, + isMA as isMA, + isMaskedArray as isMaskedArray, + is_mask as is_mask, + is_masked as is_masked, + isarray as isarray, + left_shift as left_shift, + less as less, + less_equal as less_equal, + log as log, + log10 as log10, + log2 as log2, + logical_and as logical_and, + logical_not as logical_not, + logical_or as logical_or, + logical_xor as logical_xor, + make_mask as make_mask, + make_mask_descr as make_mask_descr, + make_mask_none as make_mask_none, + mask_or as mask_or, + masked as masked, + masked_array as masked_array, + masked_equal as masked_equal, + masked_greater as masked_greater, + masked_greater_equal as masked_greater_equal, + masked_inside as masked_inside, + masked_invalid as masked_invalid, + masked_less as masked_less, + masked_less_equal as masked_less_equal, + masked_not_equal as masked_not_equal, + masked_object as masked_object, + masked_outside as masked_outside, + masked_print_option as masked_print_option, + masked_singleton as masked_singleton, + masked_values as masked_values, + masked_where as masked_where, + max as max, + maximum as maximum, + maximum_fill_value as maximum_fill_value, + mean as mean, + min as min, + minimum as minimum, + minimum_fill_value as minimum_fill_value, + mod as mod, + multiply as multiply, + mvoid as mvoid, + ndim as ndim, + negative as negative, + nomask as nomask, + nonzero as nonzero, + not_equal as not_equal, + ones as ones, + outer as outer, + outerproduct as outerproduct, + power as power, + prod as prod, + product as product, + ptp as ptp, + put as put, + putmask as putmask, + ravel as ravel, + remainder as remainder, + repeat as repeat, + reshape as reshape, + resize as resize, + right_shift as right_shift, + round as round, + round_ as round_, + set_fill_value as set_fill_value, + shape as shape, + sin as sin, + sinh as sinh, + size as size, + soften_mask as soften_mask, + sometrue as sometrue, + sort as sort, + sqrt as sqrt, + squeeze as squeeze, + std as std, + subtract as subtract, + sum as sum, + swapaxes as swapaxes, + take as take, + tan as tan, + tanh as tanh, + trace as trace, + transpose as transpose, + true_divide as true_divide, + var as var, + where as where, + zeros as zeros, +) + +from numpy.ma.extras import ( + apply_along_axis as apply_along_axis, + apply_over_axes as apply_over_axes, + atleast_1d as atleast_1d, + atleast_2d as atleast_2d, + atleast_3d as atleast_3d, + average as average, + clump_masked as clump_masked, + clump_unmasked as clump_unmasked, + column_stack as column_stack, + compress_cols as compress_cols, + compress_nd as compress_nd, + compress_rowcols as compress_rowcols, + compress_rows as compress_rows, + count_masked as count_masked, + corrcoef as corrcoef, + cov as cov, + diagflat as diagflat, + dot as dot, + dstack as dstack, + ediff1d as ediff1d, + flatnotmasked_contiguous as flatnotmasked_contiguous, + flatnotmasked_edges as flatnotmasked_edges, + hsplit as hsplit, + hstack as hstack, + isin as isin, + in1d as in1d, + intersect1d as intersect1d, + mask_cols as mask_cols, + mask_rowcols as mask_rowcols, + mask_rows as mask_rows, + masked_all as masked_all, + masked_all_like as masked_all_like, + median as median, + mr_ as mr_, + notmasked_contiguous as notmasked_contiguous, + notmasked_edges as notmasked_edges, + polyfit as polyfit, + row_stack as row_stack, + setdiff1d as setdiff1d, + setxor1d as setxor1d, + stack as stack, + unique as unique, + union1d as union1d, + vander as vander, + vstack as vstack, +) + +__all__: List[str] +__path__: List[str] +test: PytestTester diff --git a/numpy/ma/bench.py b/numpy/ma/bench.py index a9ba42deae40..56865683d9cb 100644 --- a/numpy/ma/bench.py +++ b/numpy/ma/bench.py @@ -1,7 +1,4 @@ -#! /usr/bin/env python -# -*- coding: utf-8 -*- - -from __future__ import division, print_function +#!/usr/bin/env python3 import timeit import numpy @@ -60,7 +57,7 @@ def compare_functions_1v(func, nloop=500, xs=xs, nmxs=nmxs, xl=xl, nmxl=nmxl): funcname = func.__name__ print("-"*50) - print("%s on small arrays" % funcname) + print(f'{funcname} on small arrays') module, data = "numpy.ma", "nmxs" timer("%(module)s.%(funcname)s(%(data)s)" % locals(), v="%11s" % module, nloop=nloop) @@ -72,8 +69,8 @@ def compare_functions_1v(func, nloop=500, def compare_methods(methodname, args, vars='x', nloop=500, test=True, xs=xs, nmxs=nmxs, xl=xl, nmxl=nmxl): print("-"*50) - print("%s on small arrays" % methodname) - data, ver = "nm%ss" % vars, 'numpy.ma' + print(f'{methodname} on small arrays') + data, ver = f'nm{vars}l', 'numpy.ma' timer("%(data)s.%(methodname)s(%(args)s)" % locals(), v=ver, nloop=nloop) print("%s on large arrays" % methodname) @@ -88,11 +85,11 @@ def compare_functions_2v(func, nloop=500, test=True, yl=yl, nmyl=nmyl): funcname = func.__name__ print("-"*50) - print("%s on small arrays" % funcname) + print(f'{funcname} on small arrays') module, data = "numpy.ma", "nmxs,nmys" timer("%(module)s.%(funcname)s(%(data)s)" % locals(), v="%11s" % module, nloop=nloop) - print("%s on large arrays" % funcname) + print(f'{funcname} on large arrays') module, data = "numpy.ma", "nmxl,nmyl" timer("%(module)s.%(funcname)s(%(data)s)" % locals(), v="%11s" % module, nloop=nloop) return diff --git a/numpy/ma/core.py b/numpy/ma/core.py index 091ab4e20ffa..491c2c60550f 100644 --- a/numpy/ma/core.py +++ b/numpy/ma/core.py @@ -20,20 +20,14 @@ """ # pylint: disable-msg=E1002 -from __future__ import division, absolute_import, print_function - -import sys +import builtins +import inspect import operator import warnings import textwrap import re from functools import reduce -if sys.version_info[0] >= 3: - import builtins -else: - import __builtin__ as builtins - import numpy as np import numpy.core.umath as umath import numpy.core.numerictypes as ntypes @@ -41,18 +35,12 @@ from numpy import array as narray from numpy.lib.function_base import angle from numpy.compat import ( - getargspec, formatargspec, long, basestring, unicode, bytes + getargspec, formatargspec, long, unicode, bytes ) -from numpy import expand_dims as n_expand_dims -from numpy.core.multiarray import normalize_axis_index +from numpy import expand_dims from numpy.core.numeric import normalize_axis_tuple -if sys.version_info[0] >= 3: - import pickle -else: - import cPickle as pickle - __all__ = [ 'MAError', 'MaskError', 'MaskType', 'MaskedArray', 'abs', 'absolute', 'add', 'all', 'allclose', 'allequal', 'alltrue', 'amax', 'amin', @@ -63,14 +51,14 @@ 'choose', 'clip', 'common_fill_value', 'compress', 'compressed', 'concatenate', 'conjugate', 'convolve', 'copy', 'correlate', 'cos', 'cosh', 'count', 'cumprod', 'cumsum', 'default_fill_value', 'diag', 'diagonal', - 'diff', 'divide', 'dump', 'dumps', 'empty', 'empty_like', 'equal', 'exp', + 'diff', 'divide', 'empty', 'empty_like', 'equal', 'exp', 'expand_dims', 'fabs', 'filled', 'fix_invalid', 'flatten_mask', 'flatten_structured_array', 'floor', 'floor_divide', 'fmod', 'frombuffer', 'fromflex', 'fromfunction', 'getdata', 'getmask', 'getmaskarray', 'greater', 'greater_equal', 'harden_mask', 'hypot', 'identity', 'ids', 'indices', 'inner', 'innerproduct', 'isMA', 'isMaskedArray', 'is_mask', 'is_masked', 'isarray', 'left_shift', - 'less', 'less_equal', 'load', 'loads', 'log', 'log10', 'log2', + 'less', 'less_equal', 'log', 'log10', 'log2', 'logical_and', 'logical_not', 'logical_or', 'logical_xor', 'make_mask', 'make_mask_descr', 'make_mask_none', 'mask_or', 'masked', 'masked_array', 'masked_equal', 'masked_greater', @@ -80,13 +68,13 @@ 'masked_singleton', 'masked_values', 'masked_where', 'max', 'maximum', 'maximum_fill_value', 'mean', 'min', 'minimum', 'minimum_fill_value', 'mod', 'multiply', 'mvoid', 'ndim', 'negative', 'nomask', 'nonzero', - 'not_equal', 'ones', 'outer', 'outerproduct', 'power', 'prod', - 'product', 'ptp', 'put', 'putmask', 'rank', 'ravel', 'remainder', + 'not_equal', 'ones', 'ones_like', 'outer', 'outerproduct', 'power', 'prod', + 'product', 'ptp', 'put', 'putmask', 'ravel', 'remainder', 'repeat', 'reshape', 'resize', 'right_shift', 'round', 'round_', 'set_fill_value', 'shape', 'sin', 'sinh', 'size', 'soften_mask', 'sometrue', 'sort', 'sqrt', 'squeeze', 'std', 'subtract', 'sum', 'swapaxes', 'take', 'tan', 'tanh', 'trace', 'transpose', 'true_divide', - 'var', 'where', 'zeros', + 'var', 'where', 'zeros', 'zeros_like', ] MaskType = np.bool_ @@ -105,7 +93,7 @@ def _deprecate_argsort_axis(arr): The array which argsort was called on np.ma.argsort has a long-term bug where the default of the axis argument - is wrong (gh-8701), which now must be kept for backwards compatibiity. + is wrong (gh-8701), which now must be kept for backwards compatibility. Thankfully, this only makes a difference when arrays are 2- or more- dimensional, so we only need a warning then. """ @@ -133,15 +121,8 @@ def doc_note(initialdoc, note): if note is None: return initialdoc - notesplit = re.split(r'\n\s*?Notes\n\s*?-----', initialdoc) - - notedoc = """\ -Notes - ----- - %s""" % note - - if len(notesplit) > 1: - notedoc = '\n\n ' + notedoc + '\n' + notesplit = re.split(r'\n\s*?Notes\n\s*?-----', inspect.cleandoc(initialdoc)) + notedoc = "\n\nNotes\n-----\n%s\n" % inspect.cleandoc(note) return ''.join(notesplit[:1] + [notedoc] + notesplit[1:]) @@ -202,20 +183,23 @@ class MaskError(MAError): default_filler["M8[" + v + "]"] = np.datetime64("NaT", v) default_filler["m8[" + v + "]"] = np.timedelta64("NaT", v) +float_types_list = [np.half, np.single, np.double, np.longdouble, + np.csingle, np.cdouble, np.clongdouble] max_filler = ntypes._minvals -max_filler.update([(k, -np.inf) for k in [np.float32, np.float64]]) +max_filler.update([(k, -np.inf) for k in float_types_list[:4]]) +max_filler.update([(k, complex(-np.inf, -np.inf)) for k in float_types_list[-3:]]) + min_filler = ntypes._maxvals -min_filler.update([(k, +np.inf) for k in [np.float32, np.float64]]) -if 'float128' in ntypes.typeDict: - max_filler.update([(np.float128, -np.inf)]) - min_filler.update([(np.float128, +np.inf)]) +min_filler.update([(k, +np.inf) for k in float_types_list[:4]]) +min_filler.update([(k, complex(+np.inf, +np.inf)) for k in float_types_list[-3:]]) +del float_types_list def _recursive_fill_value(dtype, f): """ Recursively produce a fill value for `dtype`, calling f on scalar dtypes """ - if dtype.names: + if dtype.names is not None: vals = tuple(_recursive_fill_value(dtype[name], f) for name in dtype.names) return np.array(vals, dtype=dtype)[()] # decay to void scalar from 0d elif dtype.subdtype: @@ -296,11 +280,10 @@ def _extremum_fill_value(obj, extremum, extremum_name): def _scalar_fill_value(dtype): try: return extremum[dtype] - except KeyError: + except KeyError as e: raise TypeError( - "Unsuitable type {} for calculating {}." - .format(dtype, extremum_name) - ) + f"Unsuitable type {dtype} for calculating {extremum_name}." + ) from None dtype = _get_dtype_of(obj) return _recursive_fill_value(dtype, _scalar_fill_value) @@ -414,10 +397,10 @@ def _recursive_set_fill_value(fillvalue, dt): Parameters ---------- - fillvalue: scalar or array_like + fillvalue : scalar or array_like Scalar or array representing the fill value. If it is of shorter length than the number of fields in dt, it will be resized. - dt: dtype + dt : dtype The structured dtype for which to create the fill value. Returns @@ -433,7 +416,7 @@ def _recursive_set_fill_value(fillvalue, dt): if cdtype.subdtype: cdtype = cdtype.subdtype[0] - if cdtype.names: + if cdtype.names is not None: output_value.append(tuple(_recursive_set_fill_value(fval, cdtype))) else: output_value.append(np.array(fval, dtype=cdtype).item()) @@ -449,37 +432,38 @@ def _check_fill_value(fill_value, ndtype): If fill_value is not None, its value is forced to the given dtype. The result is always a 0d array. + """ ndtype = np.dtype(ndtype) - fields = ndtype.fields if fill_value is None: fill_value = default_fill_value(ndtype) - elif fields: - fdtype = [(_[0], _[1]) for _ in ndtype.descr] + elif ndtype.names is not None: if isinstance(fill_value, (ndarray, np.void)): try: - fill_value = np.array(fill_value, copy=False, dtype=fdtype) - except ValueError: + fill_value = np.array(fill_value, copy=False, dtype=ndtype) + except ValueError as e: err_msg = "Unable to transform %s to dtype %s" - raise ValueError(err_msg % (fill_value, fdtype)) + raise ValueError(err_msg % (fill_value, ndtype)) from e else: fill_value = np.asarray(fill_value, dtype=object) fill_value = np.array(_recursive_set_fill_value(fill_value, ndtype), dtype=ndtype) else: - if isinstance(fill_value, basestring) and (ndtype.char not in 'OSVU'): + if isinstance(fill_value, str) and (ndtype.char not in 'OSVU'): + # Note this check doesn't work if fill_value is not a scalar err_msg = "Cannot set fill value of string with array of dtype %s" raise TypeError(err_msg % ndtype) else: # In case we want to convert 1e20 to int. + # Also in case of converting string arrays. try: fill_value = np.array(fill_value, copy=False, dtype=ndtype) - except OverflowError: - # Raise TypeError instead of OverflowError. OverflowError - # is seldom used, and the real problem here is that the - # passed fill_value is not compatible with the ndtype. - err_msg = "Fill value %s overflows dtype %s" - raise TypeError(err_msg % (fill_value, ndtype)) + except (OverflowError, ValueError) as e: + # Raise TypeError instead of OverflowError or ValueError. + # OverflowError is seldom used, and the real problem here is + # that the passed fill_value is not compatible with the ndtype. + err_msg = "Cannot convert fill_value %s to dtype %s" + raise TypeError(err_msg % (fill_value, ndtype)) from e return np.array(fill_value) @@ -518,18 +502,18 @@ def set_fill_value(a, fill_value): array([0, 1, 2, 3, 4]) >>> a = ma.masked_where(a < 3, a) >>> a - masked_array(data = [-- -- -- 3 4], - mask = [ True True True False False], - fill_value=999999) + masked_array(data=[--, --, --, 3, 4], + mask=[ True, True, True, False, False], + fill_value=999999) >>> ma.set_fill_value(a, -999) >>> a - masked_array(data = [-- -- -- 3 4], - mask = [ True True True False False], - fill_value=-999) + masked_array(data=[--, --, --, 3, 4], + mask=[ True, True, True, False, False], + fill_value=-999) Nothing happens if `a` is not a masked array. - >>> a = range(5) + >>> a = list(range(5)) >>> a [0, 1, 2, 3, 4] >>> ma.set_fill_value(a, 100) @@ -605,8 +589,10 @@ def filled(a, fill_value=None): ---------- a : MaskedArray or array_like An input object. - fill_value : scalar, optional - Filling value. Default is None. + fill_value : array_like, optional. + Can be scalar or non-scalar. If non-scalar, the + resulting filled array should be broadcastable + over input array. Default is None. Returns ------- @@ -626,10 +612,19 @@ def filled(a, fill_value=None): array([[999999, 1, 2], [999999, 4, 5], [ 6, 7, 8]]) + >>> x.filled(fill_value=333) + array([[333, 1, 2], + [333, 4, 5], + [ 6, 7, 8]]) + >>> x.filled(fill_value=np.arange(3)) + array([[0, 1, 2], + [0, 4, 5], + [6, 7, 8]]) """ if hasattr(a, 'filled'): return a.filled(fill_value) + elif isinstance(a, ndarray): # Should we check for contiguity ? and a.flags['CONTIGUOUS']: return a @@ -691,13 +686,12 @@ def getdata(a, subok=True): >>> import numpy.ma as ma >>> a = ma.masked_equal([[1,2],[3,4]], 2) >>> a - masked_array(data = - [[1 --] - [3 4]], - mask = - [[False True] - [False False]], - fill_value=999999) + masked_array( + data=[[1, --], + [3, 4]], + mask=[[False, True], + [False, False]], + fill_value=2) >>> ma.getdata(a) array([[1, 2], [3, 4]]) @@ -754,20 +748,19 @@ def fix_invalid(a, mask=nomask, copy=True, fill_value=None): -------- >>> x = np.ma.array([1., -1, np.nan, np.inf], mask=[1] + [0]*3) >>> x - masked_array(data = [-- -1.0 nan inf], - mask = [ True False False False], - fill_value = 1e+20) + masked_array(data=[--, -1.0, nan, inf], + mask=[ True, False, False, False], + fill_value=1e+20) >>> np.ma.fix_invalid(x) - masked_array(data = [-- -1.0 -- --], - mask = [ True False True True], - fill_value = 1e+20) + masked_array(data=[--, -1.0, --, --], + mask=[ True, False, True, True], + fill_value=1e+20) >>> fixed = np.ma.fix_invalid(x) >>> fixed.data - array([ 1.00000000e+00, -1.00000000e+00, 1.00000000e+20, - 1.00000000e+20]) + array([ 1.e+00, -1.e+00, 1.e+20, 1.e+20]) >>> x.data - array([ 1., -1., NaN, Inf]) + array([ 1., -1., nan, inf]) """ a = masked_array(a, copy=copy, mask=mask, subok=True) @@ -780,6 +773,10 @@ def fix_invalid(a, mask=nomask, copy=True, fill_value=None): a._data[invalid] = fill_value return a +def is_string_or_list_of_strings(val): + return (isinstance(val, str) or + (isinstance(val, list) and val and + builtins.all(isinstance(s, str) for s in val))) ############################################################################### # Ufuncs # @@ -790,7 +787,7 @@ def fix_invalid(a, mask=nomask, copy=True, fill_value=None): ufunc_fills = {} -class _DomainCheckInterval(object): +class _DomainCheckInterval: """ Define a valid interval, so that : @@ -801,7 +798,7 @@ class _DomainCheckInterval(object): def __init__(self, a, b): "domain_check_interval(a,b)(x) = true where x < a or y > b" - if (a > b): + if a > b: (a, b) = (b, a) self.a = a self.b = b @@ -815,7 +812,7 @@ def __call__(self, x): umath.less(x, self.a)) -class _DomainTan(object): +class _DomainTan: """ Define a valid interval for the `tan` function, so that: @@ -833,7 +830,7 @@ def __call__(self, x): return umath.less(umath.absolute(umath.cos(x)), self.eps) -class _DomainSafeDivide(object): +class _DomainSafeDivide: """ Define a domain for safe division. @@ -854,7 +851,7 @@ def __call__(self, a, b): return umath.absolute(a) * self.tolerance >= umath.absolute(b) -class _DomainGreater(object): +class _DomainGreater: """ DomainGreater(v)(x) is True where x <= v. @@ -870,7 +867,7 @@ def __call__(self, x): return umath.less_equal(x, self.critical_value) -class _DomainGreaterEqual(object): +class _DomainGreaterEqual: """ DomainGreaterEqual(v)(x) is True where x < v. @@ -886,14 +883,14 @@ def __call__(self, x): return umath.less(x, self.critical_value) -class _MaskedUFunc(object): +class _MaskedUFunc: def __init__(self, ufunc): self.f = ufunc self.__doc__ = ufunc.__doc__ self.__name__ = ufunc.__name__ def __str__(self): - return "Masked version of {}".format(self.f) + return f"Masked version of {self.f}" class _MaskedUnaryOperation(_MaskedUFunc): @@ -915,7 +912,7 @@ class _MaskedUnaryOperation(_MaskedUFunc): """ def __init__(self, mufunc, fill=0, domain=None): - super(_MaskedUnaryOperation, self).__init__(mufunc) + super().__init__(mufunc) self.fill = fill self.domain = domain ufunc_domain[mufunc] = domain @@ -997,7 +994,7 @@ def __init__(self, mbfunc, fillx=0, filly=0): abfunc(x, filly) = x for all x to enable reduce. """ - super(_MaskedBinaryOperation, self).__init__(mbfunc) + super().__init__(mbfunc) self.fillx = fillx self.filly = filly ufunc_domain[mbfunc] = None @@ -1061,14 +1058,14 @@ def reduce(self, target, axis=0, dtype=None): if t.shape == (): t = t.reshape(1) if m is not nomask: - m = make_mask(m, copy=1) + m = make_mask(m, copy=True) m.shape = (1,) if m is nomask: tr = self.f.reduce(t, axis) mr = nomask else: - tr = self.f.reduce(t, axis, dtype=dtype or t.dtype) + tr = self.f.reduce(t, axis, dtype=dtype) mr = umath.logical_and.reduce(m, axis) if not tr.shape: @@ -1143,7 +1140,7 @@ def __init__(self, dbfunc, domain, fillx=0, filly=0): """abfunc(fillx, filly) must be defined. abfunc(x, filly) = x for all x to enable reduce. """ - super(_DomainedBinaryOperation, self).__init__(dbfunc) + super().__init__(dbfunc) self.domain = domain self.fillx = fillx self.filly = filly @@ -1166,7 +1163,7 @@ def __call__(self, a, b, *args, **kwargs): if domain is not None: m |= domain(da, db) # Take care of the scalar case first - if (not m.ndim): + if not m.ndim: if m: return masked else: @@ -1198,7 +1195,6 @@ def __call__(self, a, b, *args, **kwargs): conjugate = _MaskedUnaryOperation(umath.conjugate) sin = _MaskedUnaryOperation(umath.sin) cos = _MaskedUnaryOperation(umath.cos) -tan = _MaskedUnaryOperation(umath.tan) arctan = _MaskedUnaryOperation(umath.arctan) arcsinh = _MaskedUnaryOperation(umath.arcsinh) sinh = _MaskedUnaryOperation(umath.sinh) @@ -1282,7 +1278,7 @@ def _replace_dtype_fields_recursive(dtype, primitive_dtype): _recurse = _replace_dtype_fields_recursive # Do we have some name fields ? - if dtype.names: + if dtype.names is not None: descr = [] for name in dtype.names: field = dtype.fields[name] @@ -1344,9 +1340,9 @@ def make_mask_descr(ndtype): -------- >>> import numpy.ma as ma >>> dtype = np.dtype({'names':['foo', 'bar'], - 'formats':[np.float32, int]}) + ... 'formats':[np.float32, np.int64]}) >>> dtype - dtype([('foo', '<f4'), ('bar', '<i4')]) + dtype([('foo', '<f4'), ('bar', '<i8')]) >>> ma.make_mask_descr(dtype) dtype([('foo', '|b1'), ('bar', '|b1')]) >>> ma.make_mask_descr(np.float32) @@ -1379,13 +1375,12 @@ def getmask(a): >>> import numpy.ma as ma >>> a = ma.masked_equal([[1,2],[3,4]], 2) >>> a - masked_array(data = - [[1 --] - [3 4]], - mask = - [[False True] - [False False]], - fill_value=999999) + masked_array( + data=[[1, --], + [3, 4]], + mask=[[False, True], + [False, False]], + fill_value=2) >>> ma.getmask(a) array([[False, True], [False, False]]) @@ -1400,12 +1395,11 @@ def getmask(a): >>> b = ma.masked_array([[1,2],[3,4]]) >>> b - masked_array(data = - [[1 2] - [3 4]], - mask = - False, - fill_value=999999) + masked_array( + data=[[1, 2], + [3, 4]], + mask=False, + fill_value=999999) >>> ma.nomask False >>> ma.getmask(b) == ma.nomask @@ -1443,13 +1437,12 @@ def getmaskarray(arr): >>> import numpy.ma as ma >>> a = ma.masked_equal([[1,2],[3,4]], 2) >>> a - masked_array(data = - [[1 --] - [3 4]], - mask = - [[False True] - [False False]], - fill_value=999999) + masked_array( + data=[[1, --], + [3, 4]], + mask=[[False, True], + [False, False]], + fill_value=2) >>> ma.getmaskarray(a) array([[False, True], [False, False]]) @@ -1458,13 +1451,12 @@ def getmaskarray(arr): >>> b = ma.masked_array([[1,2],[3,4]]) >>> b - masked_array(data = - [[1 2] - [3 4]], - mask = - False, - fill_value=999999) - >>> >ma.getmaskarray(b) + masked_array( + data=[[1, 2], + [3, 4]], + mask=False, + fill_value=999999) + >>> ma.getmaskarray(b) array([[False, False], [False, False]]) @@ -1495,16 +1487,16 @@ def is_mask(m): See Also -------- - isMaskedArray : Test whether input is an instance of MaskedArray. + ma.isMaskedArray : Test whether input is an instance of MaskedArray. Examples -------- >>> import numpy.ma as ma >>> m = ma.masked_equal([0, 1, 0, 2, 3], 0) >>> m - masked_array(data = [-- 1 -- 2 3], - mask = [ True False True False False], - fill_value=999999) + masked_array(data=[--, 1, --, 2, 3], + mask=[ True, False, True, False, False], + fill_value=0) >>> ma.is_mask(m) False >>> ma.is_mask(m.mask) @@ -1525,14 +1517,14 @@ def is_mask(m): Arrays with complex dtypes don't return True. >>> dtype = np.dtype({'names':['monty', 'pithon'], - 'formats':[bool, bool]}) + ... 'formats':[bool, bool]}) >>> dtype dtype([('monty', '|b1'), ('pithon', '|b1')]) >>> m = np.array([(True, False), (False, True), (True, False)], - dtype=dtype) + ... dtype=dtype) >>> m - array([(True, False), (False, True), (True, False)], - dtype=[('monty', '|b1'), ('pithon', '|b1')]) + array([( True, False), (False, True), ( True, False)], + dtype=[('monty', '?'), ('pithon', '?')]) >>> ma.is_mask(m) False @@ -1547,7 +1539,7 @@ def _shrink_mask(m): """ Shrink a mask to nomask if possible """ - if not m.dtype.names and not m.any(): + if m.dtype.names is None and not m.any(): return nomask else: return m @@ -1560,7 +1552,7 @@ def make_mask(m, copy=False, shrink=True, dtype=MaskType): Return `m` as a boolean mask, creating a copy if necessary or requested. The function can accept any sequence that is convertible to integers, or ``nomask``. Does not require that contents must be 0s and 1s, values - of 0 are interepreted as False, everything else as True. + of 0 are interpreted as False, everything else as True. Parameters ---------- @@ -1598,7 +1590,7 @@ def make_mask(m, copy=False, shrink=True, dtype=MaskType): >>> m = np.zeros(4) >>> m - array([ 0., 0., 0., 0.]) + array([0., 0., 0., 0.]) >>> ma.make_mask(m) False >>> ma.make_mask(m, shrink=False) @@ -1614,11 +1606,11 @@ def make_mask(m, copy=False, shrink=True, dtype=MaskType): >>> arr [(1, 0), (0, 1), (1, 0), (1, 0)] >>> dtype = np.dtype({'names':['man', 'mouse'], - 'formats':[int, int]}) + ... 'formats':[np.int64, np.int64]}) >>> arr = np.array(arr, dtype=dtype) >>> arr array([(1, 0), (0, 1), (1, 0), (1, 0)], - dtype=[('man', '<i4'), ('mouse', '<i4')]) + dtype=[('man', '<i8'), ('mouse', '<i8')]) >>> ma.make_mask(arr, dtype=dtype) array([(True, False), (False, True), (True, False), (True, False)], dtype=[('man', '|b1'), ('mouse', '|b1')]) @@ -1677,9 +1669,9 @@ def make_mask_none(newshape, dtype=None): Defining a more complex dtype. >>> dtype = np.dtype({'names':['foo', 'bar'], - 'formats':[np.float32, int]}) + ... 'formats':[np.float32, np.int64]}) >>> dtype - dtype([('foo', '<f4'), ('bar', '<i4')]) + dtype([('foo', '<f4'), ('bar', '<i8')]) >>> ma.make_mask_none((3,), dtype=dtype) array([(False, False), (False, False), (False, False)], dtype=[('foo', '|b1'), ('bar', '|b1')]) @@ -1692,6 +1684,16 @@ def make_mask_none(newshape, dtype=None): return result +def _recursive_mask_or(m1, m2, newmask): + names = m1.dtype.names + for name in names: + current1 = m1[name] + if current1.dtype.names is not None: + _recursive_mask_or(current1, m2[name], newmask[name]) + else: + umath.logical_or(current1, m2[name], newmask[name]) + + def mask_or(m1, m2, copy=False, shrink=True): """ Combine two masks with the ``logical_or`` operator. @@ -1729,16 +1731,6 @@ def mask_or(m1, m2, copy=False, shrink=True): """ - def _recursive_mask_or(m1, m2, newmask): - names = m1.dtype.names - for name in names: - current1 = m1[name] - if current1.dtype.names: - _recursive_mask_or(current1, m2[name], newmask[name]) - else: - umath.logical_or(current1, m2[name], newmask[name]) - return - if (m1 is nomask) or (m1 is False): dtype = getattr(m2, 'dtype', MaskType) return make_mask(m2, copy=copy, shrink=shrink, dtype=dtype) @@ -1748,9 +1740,9 @@ def _recursive_mask_or(m1, m2, newmask): if m1 is m2 and is_mask(m1): return m1 (dtype1, dtype2) = (getattr(m1, 'dtype', None), getattr(m2, 'dtype', None)) - if (dtype1 != dtype2): + if dtype1 != dtype2: raise ValueError("Incompatible dtypes '%s'<>'%s'" % (dtype1, dtype2)) - if dtype1.names: + if dtype1.names is not None: # Allocate an output mask array with the properly broadcast shape. newmask = np.empty(np.broadcast(m1, m2).shape, dtype1) _recursive_mask_or(m1, m2, newmask) @@ -1776,16 +1768,16 @@ def flatten_mask(mask): Examples -------- >>> mask = np.array([0, 0, 1]) - >>> flatten_mask(mask) + >>> np.ma.flatten_mask(mask) array([False, False, True]) >>> mask = np.array([(0, 0), (0, 1)], dtype=[('a', bool), ('b', bool)]) - >>> flatten_mask(mask) + >>> np.ma.flatten_mask(mask) array([False, False, False, True]) >>> mdtype = [('a', bool), ('b', [('ba', bool), ('bb', bool)])] >>> mask = np.array([(0, (0, 0)), (0, (0, 1))], dtype=mdtype) - >>> flatten_mask(mask) + >>> np.ma.flatten_mask(mask) array([False, False, False, False, False, True]) """ @@ -1793,7 +1785,7 @@ def flatten_mask(mask): def _flatmask(mask): "Flatten the mask and returns a (maybe nested) sequence of booleans." mnames = mask.dtype.names - if mnames: + if mnames is not None: return [flatten_mask(mask[name]) for name in mnames] else: return mask @@ -1803,8 +1795,7 @@ def _flatsequence(sequence): try: for element in sequence: if hasattr(element, '__iter__'): - for f in _flatsequence(element): - yield f + yield from _flatsequence(element) else: yield element except TypeError: @@ -1870,38 +1861,39 @@ def masked_where(condition, a, copy=True): >>> a array([0, 1, 2, 3]) >>> ma.masked_where(a <= 2, a) - masked_array(data = [-- -- -- 3], - mask = [ True True True False], - fill_value=999999) + masked_array(data=[--, --, --, 3], + mask=[ True, True, True, False], + fill_value=999999) Mask array `b` conditional on `a`. >>> b = ['a', 'b', 'c', 'd'] >>> ma.masked_where(a == 2, b) - masked_array(data = [a b -- d], - mask = [False False True False], - fill_value=N/A) + masked_array(data=['a', 'b', --, 'd'], + mask=[False, False, True, False], + fill_value='N/A', + dtype='<U1') Effect of the `copy` argument. >>> c = ma.masked_where(a <= 2, a) >>> c - masked_array(data = [-- -- -- 3], - mask = [ True True True False], - fill_value=999999) + masked_array(data=[--, --, --, 3], + mask=[ True, True, True, False], + fill_value=999999) >>> c[0] = 99 >>> c - masked_array(data = [99 -- -- 3], - mask = [False True True False], - fill_value=999999) + masked_array(data=[99, --, --, 3], + mask=[False, True, True, False], + fill_value=999999) >>> a array([0, 1, 2, 3]) >>> c = ma.masked_where(a <= 2, a, copy=False) >>> c[0] = 99 >>> c - masked_array(data = [99 -- -- 3], - mask = [False True True False], - fill_value=999999) + masked_array(data=[99, --, --, 3], + mask=[False, True, True, False], + fill_value=999999) >>> a array([99, 1, 2, 3]) @@ -1910,19 +1902,19 @@ def masked_where(condition, a, copy=True): >>> a = np.arange(4) >>> a = ma.masked_where(a == 2, a) >>> a - masked_array(data = [0 1 -- 3], - mask = [False False True False], - fill_value=999999) + masked_array(data=[0, 1, --, 3], + mask=[False, False, True, False], + fill_value=999999) >>> b = np.arange(4) >>> b = ma.masked_where(b == 0, b) >>> b - masked_array(data = [-- 1 2 3], - mask = [ True False False False], - fill_value=999999) + masked_array(data=[--, 1, 2, 3], + mask=[ True, False, False, False], + fill_value=999999) >>> ma.masked_where(a == 3, b) - masked_array(data = [-- 1 -- --], - mask = [ True False True True], - fill_value=999999) + masked_array(data=[--, 1, --, --], + mask=[ True, False, True, True], + fill_value=999999) """ # Make sure that condition is a valid standard-type mask. @@ -1941,6 +1933,10 @@ def masked_where(condition, a, copy=True): result = a.view(cls) # Assign to *.mask so that structured masks are handled correctly. result.mask = _shrink_mask(cond) + # There is no view of a boolean so when 'a' is a MaskedArray with nomask + # the update to the result's mask has no effect. + if not copy and hasattr(a, '_mask') and getmask(a) is nomask: + a._mask = result._mask.view() return result @@ -1962,9 +1958,9 @@ def masked_greater(x, value, copy=True): >>> a array([0, 1, 2, 3]) >>> ma.masked_greater(a, 2) - masked_array(data = [0 1 2 --], - mask = [False False False True], - fill_value=999999) + masked_array(data=[0, 1, 2, --], + mask=[False, False, False, True], + fill_value=999999) """ return masked_where(greater(x, value), x, copy=copy) @@ -1988,9 +1984,9 @@ def masked_greater_equal(x, value, copy=True): >>> a array([0, 1, 2, 3]) >>> ma.masked_greater_equal(a, 2) - masked_array(data = [0 1 -- --], - mask = [False False True True], - fill_value=999999) + masked_array(data=[0, 1, --, --], + mask=[False, False, True, True], + fill_value=999999) """ return masked_where(greater_equal(x, value), x, copy=copy) @@ -2014,9 +2010,9 @@ def masked_less(x, value, copy=True): >>> a array([0, 1, 2, 3]) >>> ma.masked_less(a, 2) - masked_array(data = [-- -- 2 3], - mask = [ True True False False], - fill_value=999999) + masked_array(data=[--, --, 2, 3], + mask=[ True, True, False, False], + fill_value=999999) """ return masked_where(less(x, value), x, copy=copy) @@ -2040,9 +2036,9 @@ def masked_less_equal(x, value, copy=True): >>> a array([0, 1, 2, 3]) >>> ma.masked_less_equal(a, 2) - masked_array(data = [-- -- -- 3], - mask = [ True True True False], - fill_value=999999) + masked_array(data=[--, --, --, 3], + mask=[ True, True, True, False], + fill_value=999999) """ return masked_where(less_equal(x, value), x, copy=copy) @@ -2066,9 +2062,9 @@ def masked_not_equal(x, value, copy=True): >>> a array([0, 1, 2, 3]) >>> ma.masked_not_equal(a, 2) - masked_array(data = [-- -- 2 --], - mask = [ True True False True], - fill_value=999999) + masked_array(data=[--, --, 2, --], + mask=[ True, True, False, True], + fill_value=999999) """ return masked_where(not_equal(x, value), x, copy=copy) @@ -2094,9 +2090,9 @@ def masked_equal(x, value, copy=True): >>> a array([0, 1, 2, 3]) >>> ma.masked_equal(a, 2) - masked_array(data = [0 1 -- 3], - mask = [False False True False], - fill_value=999999) + masked_array(data=[0, 1, --, 3], + mask=[False, False, True, False], + fill_value=2) """ output = masked_where(equal(x, value), x, copy=copy) @@ -2125,16 +2121,16 @@ def masked_inside(x, v1, v2, copy=True): >>> import numpy.ma as ma >>> x = [0.31, 1.2, 0.01, 0.2, -0.4, -1.1] >>> ma.masked_inside(x, -0.3, 0.3) - masked_array(data = [0.31 1.2 -- -- -0.4 -1.1], - mask = [False False True True False False], - fill_value=1e+20) + masked_array(data=[0.31, 1.2, --, --, -0.4, -1.1], + mask=[False, False, True, True, False, False], + fill_value=1e+20) The order of `v1` and `v2` doesn't matter. >>> ma.masked_inside(x, 0.3, -0.3) - masked_array(data = [0.31 1.2 -- -- -0.4 -1.1], - mask = [False False True True False False], - fill_value=1e+20) + masked_array(data=[0.31, 1.2, --, --, -0.4, -1.1], + mask=[False, False, True, True, False, False], + fill_value=1e+20) """ if v2 < v1: @@ -2165,16 +2161,16 @@ def masked_outside(x, v1, v2, copy=True): >>> import numpy.ma as ma >>> x = [0.31, 1.2, 0.01, 0.2, -0.4, -1.1] >>> ma.masked_outside(x, -0.3, 0.3) - masked_array(data = [-- -- 0.01 0.2 -- --], - mask = [ True True False False True True], - fill_value=1e+20) + masked_array(data=[--, --, 0.01, 0.2, --, --], + mask=[ True, True, False, False, True, True], + fill_value=1e+20) The order of `v1` and `v2` doesn't matter. >>> ma.masked_outside(x, 0.3, -0.3) - masked_array(data = [-- -- 0.01 0.2 -- --], - mask = [ True True False False True True], - fill_value=1e+20) + masked_array(data=[--, --, 0.01, 0.2, --, --], + mask=[ True, True, False, False, True, True], + fill_value=1e+20) """ if v2 < v1: @@ -2219,20 +2215,27 @@ def masked_object(x, value, copy=True, shrink=True): >>> food = np.array(['green_eggs', 'ham'], dtype=object) >>> # don't eat spoiled food >>> eat = ma.masked_object(food, 'green_eggs') - >>> print(eat) - [-- ham] + >>> eat + masked_array(data=[--, 'ham'], + mask=[ True, False], + fill_value='green_eggs', + dtype=object) >>> # plain ol` ham is boring >>> fresh_food = np.array(['cheese', 'ham', 'pineapple'], dtype=object) >>> eat = ma.masked_object(fresh_food, 'green_eggs') - >>> print(eat) - [cheese ham pineapple] + >>> eat + masked_array(data=['cheese', 'ham', 'pineapple'], + mask=False, + fill_value='green_eggs', + dtype=object) Note that `mask` is set to ``nomask`` if possible. >>> eat - masked_array(data = [cheese ham pineapple], - mask = False, - fill_value=?) + masked_array(data=['cheese', 'ham', 'pineapple'], + mask=False, + fill_value='green_eggs', + dtype=object) """ if isMaskedArray(x): @@ -2287,16 +2290,16 @@ def masked_values(x, value, rtol=1e-5, atol=1e-8, copy=True, shrink=True): >>> import numpy.ma as ma >>> x = np.array([1, 1.1, 2, 1.1, 3]) >>> ma.masked_values(x, 1.1) - masked_array(data = [1.0 -- 2.0 -- 3.0], - mask = [False True False True False], - fill_value=1.1) + masked_array(data=[1.0, --, 2.0, --, 3.0], + mask=[False, True, False, True, False], + fill_value=1.1) Note that `mask` is set to ``nomask`` if possible. >>> ma.masked_values(x, 1.5) - masked_array(data = [ 1. 1.1 2. 1.1 3. ], - mask = False, - fill_value=1.5) + masked_array(data=[1. , 1.1, 2. , 1.1, 3. ], + mask=False, + fill_value=1.5) For integers, the fill value will be different in general to the result of ``masked_equal``. @@ -2305,13 +2308,13 @@ def masked_values(x, value, rtol=1e-5, atol=1e-8, copy=True, shrink=True): >>> x array([0, 1, 2, 3, 4]) >>> ma.masked_values(x, 2) - masked_array(data = [0 1 -- 3 4], - mask = [False False True False False], - fill_value=2) + masked_array(data=[0, 1, --, 3, 4], + mask=[False, False, True, False, False], + fill_value=2) >>> ma.masked_equal(x, 2) - masked_array(data = [0 1 -- 3 4], - mask = [False False True False False], - fill_value=999999) + masked_array(data=[0, 1, --, 3, 4], + mask=[False, False, True, False, False], + fill_value=2) """ xnew = filled(x, value) @@ -2345,11 +2348,11 @@ def masked_invalid(a, copy=True): >>> a[2] = np.NaN >>> a[3] = np.PINF >>> a - array([ 0., 1., NaN, Inf, 4.]) + array([ 0., 1., nan, inf, 4.]) >>> ma.masked_invalid(a) - masked_array(data = [0.0 1.0 -- -- 4.0], - mask = [False False True True False], - fill_value=1e+20) + masked_array(data=[0.0, 1.0, --, --, 4.0], + mask=[False, False, True, True, False], + fill_value=1e+20) """ a = np.array(a, copy=copy, subok=True) @@ -2372,7 +2375,7 @@ def masked_invalid(a, copy=True): ############################################################################### -class _MaskedPrintOption(object): +class _MaskedPrintOption: """ Handle the string used to represent missing data in a masked array. @@ -2431,7 +2434,7 @@ def _recursive_printoption(result, mask, printopt): """ names = result.dtype.names - if names: + if names is not None: for name in names: curdata = result[name] curmask = mask[name] @@ -2483,7 +2486,7 @@ def _recursive_filled(a, mask, fill_value): names = a.dtype.names for name in names: current = a[name] - if current.dtype.names: + if current.dtype.names is not None: _recursive_filled(current, mask[name], fill_value[name]) else: np.copyto(current, fill_value[name], where=mask[name]) @@ -2510,7 +2513,7 @@ def flatten_structured_array(a): -------- >>> ndtype = [('a', int), ('b', float)] >>> a = np.array([(1, 1), (2, 2)], dtype=ndtype) - >>> flatten_structured_array(a) + >>> np.ma.flatten_structured_array(a) array([[1., 1.], [2., 2.]]) @@ -2523,8 +2526,7 @@ def flatten_sequence(iterable): """ for elm in iter(iterable): if hasattr(elm, '__iter__'): - for f in flatten_sequence(elm): - yield f + yield from flatten_sequence(elm) else: yield elm @@ -2590,7 +2592,7 @@ def wrapped_method(self, *args, **params): return wrapped_method -class MaskedIterator(object): +class MaskedIterator: """ Flat iterator object to iterate over masked arrays. @@ -2678,17 +2680,13 @@ def __next__(self): -------- >>> x = np.ma.array([3, 2], mask=[0, 1]) >>> fl = x.flat - >>> fl.next() + >>> next(fl) 3 - >>> fl.next() - masked_array(data = --, - mask = True, - fill_value = 1e+20) - >>> fl.next() + >>> next(fl) + masked + >>> next(fl) Traceback (most recent call last): - File "<stdin>", line 1, in <module> - File "/home/ralf/python/numpy/numpy/ma/core.py", line 2243, in next - d = self.dataiter.next() + ... StopIteration """ @@ -2701,8 +2699,6 @@ def __next__(self): return masked return d - next = __next__ - class MaskedArray(ndarray): """ @@ -2757,6 +2753,52 @@ class MaskedArray(ndarray): in any order (either C-, Fortran-contiguous, or even discontiguous), unless a copy is required, in which case it will be C-contiguous. + Examples + -------- + + The ``mask`` can be initialized with an array of boolean values + with the same shape as ``data``. + + >>> data = np.arange(6).reshape((2, 3)) + >>> np.ma.MaskedArray(data, mask=[[False, True, False], + ... [False, False, True]]) + masked_array( + data=[[0, --, 2], + [3, 4, --]], + mask=[[False, True, False], + [False, False, True]], + fill_value=999999) + + Alternatively, the ``mask`` can be initialized to homogeneous boolean + array with the same shape as ``data`` by passing in a scalar + boolean value: + + >>> np.ma.MaskedArray(data, mask=False) + masked_array( + data=[[0, 1, 2], + [3, 4, 5]], + mask=[[False, False, False], + [False, False, False]], + fill_value=999999) + + >>> np.ma.MaskedArray(data, mask=True) + masked_array( + data=[[--, --, --], + [--, --, --]], + mask=[[ True, True, True], + [ True, True, True]], + fill_value=999999, + dtype=int64) + + .. note:: + The recommended practice for initializing ``mask`` with a scalar + boolean value is to use ``True``/``False`` rather than + ``np.True_``/``np.False_``. The reason is :attr:`nomask` + is represented internally as ``np.False_``. + + >>> np.False_ is np.ma.nomask + True + """ __array_priority__ = 15 @@ -2771,7 +2813,7 @@ class MaskedArray(ndarray): def __new__(cls, data=None, mask=nomask, dtype=None, copy=False, subok=True, ndmin=0, fill_value=None, keep_mask=True, - hard_mask=None, shrink=True, order=None, **options): + hard_mask=None, shrink=True, order=None): """ Create a new masked array from scratch. @@ -2795,11 +2837,12 @@ def __new__(cls, data=None, mask=nomask, dtype=None, copy=False, _data = ndarray.view(_data, type(data)) else: _data = ndarray.view(_data, cls) - # Backwards compatibility w/ numpy.core.ma. + + # Handle the case where data is not a subclass of ndarray, but + # still has the _mask attribute like MaskedArrays if hasattr(data, '_mask') and not isinstance(data, ndarray): _data._mask = data._mask - # FIXME _sharedmask is never used. - _sharedmask = True + # FIXME: should we set `_data._sharedmask = True`? # Process mask. # Type of the mask mdtype = make_mask_descr(_data.dtype) @@ -2818,8 +2861,9 @@ def __new__(cls, data=None, mask=nomask, dtype=None, copy=False, elif isinstance(data, (tuple, list)): try: # If data is a sequence of masked array - mask = np.array([getmaskarray(m) for m in data], - dtype=mdtype) + mask = np.array( + [getmaskarray(np.asanyarray(m, dtype=_data.dtype)) + for m in data], dtype=mdtype) except ValueError: # If data is nested mask = nomask @@ -2870,12 +2914,12 @@ def __new__(cls, data=None, mask=nomask, dtype=None, copy=False, _data._mask = mask _data._sharedmask = not copy else: - if _data.dtype.names: + if _data.dtype.names is not None: def _recursive_or(a, b): "do a|=b on each field of a, recursively" for name in a.dtype.names: (af, bf) = (a[name], b[name]) - if af.dtype.names: + if af.dtype.names is not None: _recursive_or(af, bf) else: af |= bf @@ -2962,7 +3006,7 @@ def __array_finalize__(self, obj): if isinstance(obj, ndarray): # XX: This looks like a bug -- shouldn't it check self.dtype # instead? - if obj.dtype.names: + if obj.dtype.names is not None: _mask = getmaskarray(obj) else: _mask = getmask(obj) @@ -3010,11 +3054,13 @@ def __array_finalize__(self, obj): except (TypeError, AttributeError): # When _mask.shape is not writable (because it's a void) pass - # Finalize the fill_value for structured arrays - if self.dtype.names: - if self._fill_value is None: - self._fill_value = _check_fill_value(None, self.dtype) - return + + # Finalize the fill_value + if self._fill_value is not None: + self._fill_value = _check_fill_value(self._fill_value, self.dtype) + elif self.dtype.names is not None: + # Finalize the default fill_value for structured arrays + self._fill_value = _check_fill_value(None, self.dtype) def __array_wrap__(self, obj, context=None): """ @@ -3074,7 +3120,7 @@ def __array_wrap__(self, obj, context=None): def view(self, dtype=None, type=None, fill_value=None): """ - Return a view of the MaskedArray data + Return a view of the MaskedArray data. Parameters ---------- @@ -3088,6 +3134,14 @@ def view(self, dtype=None, type=None, fill_value=None): type : Python type, optional Type of the returned view, either ndarray or a subclass. The default None results in type preservation. + fill_value : scalar, optional + The value to use for invalid entries (None by default). + If None, then this argument is inferred from the passed `dtype`, or + in its absence the original array, as discussed in the notes below. + + See Also + -------- + numpy.ndarray.view : Equivalent method on ndarray object. Notes ----- @@ -3140,7 +3194,7 @@ def view(self, dtype=None, type=None, fill_value=None): # also make the mask be a view (so attr changes to the view's # mask do no affect original object's mask) # (especially important to avoid affecting np.masked singleton) - if (getmask(output) is not nomask): + if getmask(output) is not nomask: output._mask = output._mask.view() # Make sure to reset the _fill_value if needed @@ -3153,7 +3207,6 @@ def view(self, dtype=None, type=None, fill_value=None): else: output.fill_value = fill_value return output - view.__doc__ = ndarray.view.__doc__ def __getitem__(self, indx): """ @@ -3242,10 +3295,14 @@ def _scalar_heuristic(arr, elem): # Inherit attributes from self dout._update_from(self) # Check the fill_value - if isinstance(indx, basestring): + if is_string_or_list_of_strings(indx): if self._fill_value is not None: dout._fill_value = self._fill_value[indx] + # Something like gh-15895 has happened if this check fails. + # _fill_value should always be an ndarray. + if not isinstance(dout._fill_value, np.ndarray): + raise RuntimeError('Internal NumPy error.') # If we're indexing a multidimensional field in a # structured array (such as dtype("(2,)i2,(2,)i1")), # dimensionality goes up (M[field].ndim == M.ndim + @@ -3253,19 +3310,21 @@ def _scalar_heuristic(arr, elem): # M[field] but problematic for M[field].fill_value # which should have shape () to avoid breaking several # methods. There is no great way out, so set to - # first element. See issue #6723. + # first element. See issue #6723. if dout._fill_value.ndim > 0: if not (dout._fill_value == dout._fill_value.flat[0]).all(): warnings.warn( "Upon accessing multidimensional field " - "{indx:s}, need to keep dimensionality " + f"{indx!s}, need to keep dimensionality " "of fill_value at 0. Discarding " "heterogeneous fill_value and setting " - "all to {fv!s}.".format(indx=indx, - fv=dout._fill_value[0]), + f"all to {dout._fill_value[0]!s}.", stacklevel=2) - dout._fill_value = dout._fill_value.flat[0] + # Need to use `.flat[0:1].squeeze(...)` instead of just + # `.flat[0]` to ensure the result is a 0d array and not + # a scalar. + dout._fill_value = dout._fill_value.flat[0:1].squeeze(axis=0) dout._isfield = True # Update the mask if needed if mout is not nomask: @@ -3287,7 +3346,7 @@ def __setitem__(self, indx, value): raise MaskError('Cannot alter the masked element.') _data = self._data _mask = self._mask - if isinstance(indx, basestring): + if isinstance(indx, str): _data[indx] = value if _mask is nomask: self._mask = _mask = make_mask_none(self.shape, self.dtype) @@ -3295,15 +3354,14 @@ def __setitem__(self, indx, value): return _dtype = _data.dtype - nbfields = len(_dtype.names or ()) if value is masked: # The mask wasn't set: create a full version. if _mask is nomask: _mask = self._mask = make_mask_none(self.shape, _dtype) # Now, set the mask to its value. - if nbfields: - _mask[indx] = tuple([True] * nbfields) + if _dtype.names is not None: + _mask[indx] = tuple([True] * len(_dtype.names)) else: _mask[indx] = True return @@ -3312,8 +3370,8 @@ def __setitem__(self, indx, value): dval = getattr(value, '_data', value) # Get the _mask part of the new value mval = getmask(value) - if nbfields and mval is nomask: - mval = tuple([False] * nbfields) + if _dtype.names is not None and mval is nomask: + mval = tuple([False] * len(_dtype.names)) if _mask is nomask: # Set the data, then the mask _data[indx] = dval @@ -3322,13 +3380,17 @@ def __setitem__(self, indx, value): _mask[indx] = mval elif not self._hardmask: # Set the data, then the mask - _data[indx] = dval - _mask[indx] = mval + if (isinstance(indx, masked_array) and + not isinstance(value, masked_array)): + _data[indx.data] = dval + else: + _data[indx] = dval + _mask[indx] = mval elif hasattr(indx, 'dtype') and (indx.dtype == MaskType): indx = indx * umath.logical_not(_mask) _data[indx] = dval else: - if nbfields: + if _dtype.names is not None: err_msg = "Flexible 'hard' masks are not yet supported." raise NotImplementedError(err_msg) mindx = mask_or(_mask[indx], mval, copy=True) @@ -3344,7 +3406,7 @@ def __setitem__(self, indx, value): # Define so that we can overwrite the setter. @property def dtype(self): - return super(MaskedArray, self).dtype + return super().dtype @dtype.setter def dtype(self, dtype): @@ -3360,7 +3422,7 @@ def dtype(self, dtype): @property def shape(self): - return super(MaskedArray, self).shape + return super().shape @shape.setter def shape(self, shape): @@ -3380,7 +3442,7 @@ def __setmask__(self, mask, copy=False): if mask is masked: mask = True - if (current_mask is nomask): + if current_mask is nomask: # Make sure the mask is set # Just don't do anything if there's nothing to do. if mask is nomask: @@ -3441,49 +3503,54 @@ def __setmask__(self, mask, copy=False): _set_mask = __setmask__ - def _get_mask(self): - """Return the current mask. + @property + def mask(self): + """ Current mask. """ - """ # We could try to force a reshape, but that wouldn't work in some # cases. - return self._mask + # Return a view so that the dtype and shape cannot be changed in place + # This still preserves nomask by identity + return self._mask.view() - mask = property(fget=_get_mask, fset=__setmask__, doc="Mask") + @mask.setter + def mask(self, value): + self.__setmask__(value) - def _get_recordmask(self): + @property + def recordmask(self): """ - Return the mask of the records. - - A record is masked when all the fields are masked. + Get or set the mask of the array if it has no named fields. For + structured arrays, returns a ndarray of booleans where entries are + ``True`` if **all** the fields are masked, ``False`` otherwise: + >>> x = np.ma.array([(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)], + ... mask=[(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)], + ... dtype=[('a', int), ('b', int)]) + >>> x.recordmask + array([False, False, True, False, False]) """ + _mask = self._mask.view(ndarray) if _mask.dtype.names is None: return _mask return np.all(flatten_structured_array(_mask), axis=-1) - def _set_recordmask(self): - """ - Return the mask of the records. - - A record is masked when all the fields are masked. - - """ + @recordmask.setter + def recordmask(self, mask): raise NotImplementedError("Coming soon: setting the mask per records!") - recordmask = property(fget=_get_recordmask) - def harden_mask(self): """ Force the mask to hard. Whether the mask of a masked array is hard or soft is determined by - its `hardmask` property. `harden_mask` sets `hardmask` to True. + its `~ma.MaskedArray.hardmask` property. `harden_mask` sets + `~ma.MaskedArray.hardmask` to ``True``. See Also -------- - hardmask + ma.MaskedArray.hardmask """ self._hardmask = True @@ -3494,18 +3561,21 @@ def soften_mask(self): Force the mask to soft. Whether the mask of a masked array is hard or soft is determined by - its `hardmask` property. `soften_mask` sets `hardmask` to False. + its `~ma.MaskedArray.hardmask` property. `soften_mask` sets + `~ma.MaskedArray.hardmask` to ``False``. See Also -------- - hardmask + ma.MaskedArray.hardmask """ self._hardmask = False return self - hardmask = property(fget=lambda self: self._hardmask, - doc="Hardness of the mask") + @property + def hardmask(self): + """ Hardness of the mask """ + return self._hardmask def unshare_mask(self): """ @@ -3525,8 +3595,10 @@ def unshare_mask(self): self._sharedmask = False return self - sharedmask = property(fget=lambda self: self._sharedmask, - doc="Share status of the mask (read-only).") + @property + def sharedmask(self): + """ Share status of the mask (read-only). """ + return self._sharedmask def shrink_mask(self): """ @@ -3547,6 +3619,11 @@ def shrink_mask(self): array([[False, False], [False, False]]) >>> x.shrink_mask() + masked_array( + data=[[1, 2], + [3, 4]], + mask=False, + fill_value=999999) >>> x.mask False @@ -3554,39 +3631,46 @@ def shrink_mask(self): self._mask = _shrink_mask(self._mask) return self - baseclass = property(fget=lambda self: self._baseclass, - doc="Class of the underlying data (read-only).") + @property + def baseclass(self): + """ Class of the underlying data (read-only). """ + return self._baseclass def _get_data(self): - """Return the current data, as a view of the original - underlying data. + """ + Returns the underlying data, as a view of the masked array. + If the underlying data is a subclass of :class:`numpy.ndarray`, it is + returned as such. + + >>> x = np.ma.array(np.matrix([[1, 2], [3, 4]]), mask=[[0, 1], [1, 0]]) + >>> x.data + matrix([[1, 2], + [3, 4]]) + + The type of the data can be accessed through the :attr:`baseclass` + attribute. """ return ndarray.view(self, self._baseclass) _data = property(fget=_get_data) data = property(fget=_get_data) - def _get_flat(self): - "Return a flat iterator." + @property + def flat(self): + """ Return a flat iterator, or set a flattened version of self to value. """ return MaskedIterator(self) - def _set_flat(self, value): - "Set a flattened version of self to value." + @flat.setter + def flat(self, value): y = self.ravel() y[:] = value - flat = property(fget=_get_flat, fset=_set_flat, - doc="Flat version of the array.") - - def get_fill_value(self): + @property + def fill_value(self): """ - Return the filling value of the masked array. - - Returns - ------- - fill_value : scalar - The filling value. + The filling value of the masked array is a scalar. When setting, None + will set to a default based on the data type. Examples -------- @@ -3599,8 +3683,17 @@ def get_fill_value(self): (1e+20+0j) >>> x = np.ma.array([0, 1.], fill_value=-np.inf) - >>> x.get_fill_value() + >>> x.fill_value -inf + >>> x.fill_value = np.pi + >>> x.fill_value + 3.1415926535897931 # may vary + + Reset to default: + + >>> x.fill_value = None + >>> x.fill_value + 1e+20 """ if self._fill_value is None: @@ -3614,37 +3707,17 @@ def get_fill_value(self): return self._fill_value[()] return self._fill_value - def set_fill_value(self, value=None): - """ - Set the filling value of the masked array. - - Parameters - ---------- - value : scalar, optional - The new filling value. Default is None, in which case a default - based on the data type is used. - - See Also - -------- - ma.set_fill_value : Equivalent function. - - Examples - -------- - >>> x = np.ma.array([0, 1.], fill_value=-np.inf) - >>> x.fill_value - -inf - >>> x.set_fill_value(np.pi) - >>> x.fill_value - 3.1415926535897931 - - Reset to default: - - >>> x.set_fill_value() - >>> x.fill_value - 1e+20 - - """ + @fill_value.setter + def fill_value(self, value=None): target = _check_fill_value(value, self.dtype) + if not target.ndim == 0: + # 2019-11-12, 1.18.0 + warnings.warn( + "Non-scalar arrays for the fill value are deprecated. Use " + "arrays with scalar values instead. The filled function " + "still supports any array as `fill_value`.", + DeprecationWarning, stacklevel=2) + _fill_value = self._fill_value if _fill_value is None: # Create the attribute if it was undefined @@ -3653,8 +3726,9 @@ def set_fill_value(self, value=None): # Don't overwrite the attribute, just fill it (for propagation) _fill_value[()] = target - fill_value = property(fget=get_fill_value, fset=set_fill_value, - doc="Filling value.") + # kept for compatibility + get_fill_value = fill_value.fget + set_fill_value = fill_value.fset def filled(self, fill_value=None): """ @@ -3664,9 +3738,11 @@ def filled(self, fill_value=None): Parameters ---------- - fill_value : scalar, optional - The value to use for invalid entries (None by default). - If None, the `fill_value` attribute of the array is used instead. + fill_value : array_like, optional + The value to use for invalid entries. Can be scalar or non-scalar. + If non-scalar, the resulting ndarray must be broadcastable over + input array. Default is None, in which case, the `fill_value` + attribute of the array is used instead. Returns ------- @@ -3684,9 +3760,11 @@ def filled(self, fill_value=None): -------- >>> x = np.ma.array([1,2,3,4,5], mask=[0,0,1,0,1], fill_value=-999) >>> x.filled() - array([1, 2, -999, 4, -999]) + array([ 1, 2, -999, 4, -999]) + >>> x.filled(fill_value=1000) + array([ 1, 2, 1000, 4, 1000]) >>> type(x.filled()) - <type 'numpy.ndarray'> + <class 'numpy.ndarray'> Subclassing is preserved. This means that if, e.g., the data part of the masked array is a recarray, `filled` returns a recarray: @@ -3709,7 +3787,7 @@ def filled(self, fill_value=None): if self is masked_singleton: return np.asanyarray(fill_value) - if m.dtype.names: + if m.dtype.names is not None: result = self._data.copy('K') _recursive_filled(result, self._mask, fill_value) elif not m.any(): @@ -3751,7 +3829,7 @@ def compressed(self): >>> x.compressed() array([0, 1]) >>> type(x.compressed()) - <type 'numpy.ndarray'> + <class 'numpy.ndarray'> """ data = ndarray.ravel(self._data) @@ -3763,7 +3841,7 @@ def compress(self, condition, axis=None, out=None): """ Return `a` where condition is ``True``. - If condition is a `MaskedArray`, missing values are considered + If condition is a `~ma.MaskedArray`, missing values are considered as ``False``. Parameters @@ -3782,7 +3860,7 @@ def compress(self, condition, axis=None, out=None): Returns ------- result : MaskedArray - A :class:`MaskedArray` object. + A :class:`~ma.MaskedArray` object. Notes ----- @@ -3793,25 +3871,29 @@ def compress(self, condition, axis=None, out=None): Examples -------- >>> x = np.ma.array([[1,2,3],[4,5,6],[7,8,9]], mask=[0] + [1,0]*4) - >>> print(x) - [[1 -- 3] - [-- 5 --] - [7 -- 9]] + >>> x + masked_array( + data=[[1, --, 3], + [--, 5, --], + [7, --, 9]], + mask=[[False, True, False], + [ True, False, True], + [False, True, False]], + fill_value=999999) >>> x.compress([1, 0, 1]) - masked_array(data = [1 3], - mask = [False False], - fill_value=999999) + masked_array(data=[1, 3], + mask=[False, False], + fill_value=999999) >>> x.compress([1, 0, 1], axis=1) - masked_array(data = - [[1 3] - [-- --] - [7 9]], - mask = - [[False False] - [ True True] - [False False]], - fill_value=999999) + masked_array( + data=[[1, 3], + [--, --], + [7, 9]], + mask=[[False, False], + [ True, True], + [False, False]], + fill_value=999999) """ # Get the basic components @@ -3819,7 +3901,7 @@ def compress(self, condition, axis=None, out=None): # Force the condition to a regular ndarray and forget the missing # values. - condition = np.array(condition, copy=False, subok=False) + condition = np.asarray(condition) _new = _data.compress(condition, axis=axis, out=out).view(type(self)) _new._update_from(self) @@ -3861,10 +3943,6 @@ def _insert_masked_print(self): def __str__(self): return str(self._insert_masked_print()) - if sys.version_info.major < 3: - def __unicode__(self): - return unicode(self._insert_masked_print()) - def __repr__(self): """ Literal string representation. @@ -3877,7 +3955,7 @@ def __repr__(self): # 2016-11-19: Demoted to legacy format - if np.get_printoptions()['legacy'] == '1.13': + if np.core.arrayprint._get_legacy_print_mode() <= 113: is_long = self.ndim > 1 parameters = dict( name=name, @@ -3894,7 +3972,7 @@ def __repr__(self): ) return _legacy_print_templates[key] % parameters - prefix = 'masked_{}('.format(name) + prefix = f"masked_{name}(" dtype_needed = ( not np.core.arrayprint.dtype_is_implied(self.dtype) or @@ -3979,7 +4057,7 @@ def _comparison(self, other, compare): mask = mask_or(smask, omask, copy=True) odata = getdata(other) - if mask.dtype.names: + if mask.dtype.names is not None: # For possibly masked structured arrays we need to be careful, # since the standard structured array comparison will use all # fields, masked or not. To avoid masked fields influencing the @@ -4015,6 +4093,16 @@ def _comparison(self, other, compare): check = check.view(type(self)) check._update_from(self) check._mask = mask + + # Cast fill value to bool_ if needed. If it cannot be cast, the + # default boolean fill value is used. + if check._fill_value is not None: + try: + fill = _check_fill_value(check._fill_value, np.bool_) + except (TypeError, ValueError): + fill = _check_fill_value(None, np.bool_) + check._fill_value = fill + return check def __eq__(self, other): @@ -4298,86 +4386,59 @@ def __int__(self): raise MaskError('Cannot convert masked element to a Python int.') return int(self.item()) - def __long__(self): - """ - Convert to long. - """ - if self.size > 1: - raise TypeError("Only length-1 arrays can be converted " - "to Python scalars") - elif self._mask: - raise MaskError('Cannot convert masked element to a Python long.') - return long(self.item()) - - - def get_imag(self): + @property + def imag(self): """ - Return the imaginary part of the masked array. - - The returned array is a view on the imaginary part of the `MaskedArray` - whose `get_imag` method is called. + The imaginary part of the masked array. - Parameters - ---------- - None - - Returns - ------- - result : MaskedArray - The imaginary part of the masked array. + This property is a view on the imaginary part of this `MaskedArray`. See Also -------- - get_real, real, imag + real Examples -------- >>> x = np.ma.array([1+1.j, -2j, 3.45+1.6j], mask=[False, True, False]) - >>> x.get_imag() - masked_array(data = [1.0 -- 1.6], - mask = [False True False], - fill_value = 1e+20) + >>> x.imag + masked_array(data=[1.0, --, 1.6], + mask=[False, True, False], + fill_value=1e+20) """ result = self._data.imag.view(type(self)) result.__setmask__(self._mask) return result - imag = property(fget=get_imag, doc="Imaginary part.") + # kept for compatibility + get_imag = imag.fget - def get_real(self): + @property + def real(self): """ - Return the real part of the masked array. + The real part of the masked array. - The returned array is a view on the real part of the `MaskedArray` - whose `get_real` method is called. - - Parameters - ---------- - None - - Returns - ------- - result : MaskedArray - The real part of the masked array. + This property is a view on the real part of this `MaskedArray`. See Also -------- - get_imag, real, imag + imag Examples -------- >>> x = np.ma.array([1+1.j, -2j, 3.45+1.6j], mask=[False, True, False]) - >>> x.get_real() - masked_array(data = [1.0 -- 3.45], - mask = [False True False], - fill_value = 1e+20) + >>> x.real + masked_array(data=[1.0, --, 3.45], + mask=[False, True, False], + fill_value=1e+20) """ result = self._data.real.view(type(self)) result.__setmask__(self._mask) return result - real = property(fget=get_real, doc="Real part") + + # kept for compatibility + get_real = real.fget def count(self, axis=None, keepdims=np._NoValue): """ @@ -4387,7 +4448,7 @@ def count(self, axis=None, keepdims=np._NoValue): ---------- axis : None or int or tuple of ints, optional Axis or axes along which the count is performed. - The default (`axis` = `None`) performs the count over all + The default, None, performs the count over all the dimensions of the input array. `axis` may be negative, in which case it counts from the last to the first axis. @@ -4409,7 +4470,7 @@ def count(self, axis=None, keepdims=np._NoValue): See Also -------- - count_masked : Count masked elements in array or along a given axis. + ma.count_masked : Count masked elements in array or along a given axis. Examples -------- @@ -4417,13 +4478,12 @@ def count(self, axis=None, keepdims=np._NoValue): >>> a = ma.arange(6).reshape((2, 3)) >>> a[1, :] = ma.masked >>> a - masked_array(data = - [[0 1 2] - [-- -- --]], - mask = - [[False False False] - [ True True True]], - fill_value = 999999) + masked_array( + data=[[0, 1, 2], + [--, --, --]], + mask=[[False, False, False], + [ True, True, True]], + fill_value=999999) >>> a.count() 3 @@ -4449,7 +4509,7 @@ def count(self, axis=None, keepdims=np._NoValue): if m is nomask: # compare to _count_reduce_items in _methods.py - if self.shape is (): + if self.shape == (): if axis not in (None, 0): raise np.AxisError(axis=axis, ndim=self.ndim) return 1 @@ -4508,12 +4568,20 @@ def ravel(self, order='C'): Examples -------- >>> x = np.ma.array([[1,2,3],[4,5,6],[7,8,9]], mask=[0] + [1,0]*4) - >>> print(x) - [[1 -- 3] - [-- 5 --] - [7 -- 9]] - >>> print(x.ravel()) - [1 -- 3 -- 5 -- 7 -- 9] + >>> x + masked_array( + data=[[1, --, 3], + [--, 5, --], + [7, --, 9]], + mask=[[False, True, False], + [ True, False, True], + [False, True, False]], + fill_value=999999) + >>> x.ravel() + masked_array(data=[1, --, 3, --, 5, --, 7, --, 9], + mask=[False, True, False, True, False, True, False, True, + False], + fill_value=999999) """ r = ndarray.ravel(self._data, order=order).view(type(self)) @@ -4562,15 +4630,25 @@ def reshape(self, *s, **kwargs): Examples -------- >>> x = np.ma.array([[1,2],[3,4]], mask=[1,0,0,1]) - >>> print(x) - [[-- 2] - [3 --]] + >>> x + masked_array( + data=[[--, 2], + [3, --]], + mask=[[ True, False], + [False, True]], + fill_value=999999) >>> x = x.reshape((4,1)) - >>> print(x) - [[--] - [2] - [3] - [--]] + >>> x + masked_array( + data=[[--], + [2], + [3], + [--]], + mask=[[ True], + [False], + [False], + [ True]], + fill_value=999999) """ kwargs.update(order=kwargs.get('order', 'C')) @@ -4627,21 +4705,36 @@ def put(self, indices, values, mode='raise'): Examples -------- >>> x = np.ma.array([[1,2,3],[4,5,6],[7,8,9]], mask=[0] + [1,0]*4) - >>> print(x) - [[1 -- 3] - [-- 5 --] - [7 -- 9]] + >>> x + masked_array( + data=[[1, --, 3], + [--, 5, --], + [7, --, 9]], + mask=[[False, True, False], + [ True, False, True], + [False, True, False]], + fill_value=999999) >>> x.put([0,4,8],[10,20,30]) - >>> print(x) - [[10 -- 3] - [-- 20 --] - [7 -- 30]] + >>> x + masked_array( + data=[[10, --, 3], + [--, 20, --], + [7, --, 30]], + mask=[[False, True, False], + [ True, False, True], + [False, True, False]], + fill_value=999999) >>> x.put(4,999) - >>> print(x) - [[10 -- 3] - [-- 999 --] - [7 -- 30]] + >>> x + masked_array( + data=[[10, --, 3], + [--, 999, --], + [7, --, 30]], + mask=[[False, True, False], + [ True, False, True], + [False, True, False]], + fill_value=999999) """ # Hard mask: Get rid of the values/indices that fall on masked data @@ -4681,14 +4774,14 @@ def ids(self): -------- >>> x = np.ma.array([1, 2, 3], mask=[0, 1, 1]) >>> x.ids() - (166670640, 166659832) + (166670640, 166659832) # may vary If the array has no mask, the address of `nomask` is returned. This address is typically not close to the data in memory: >>> x = np.ma.array([1, 2, 3]) >>> x.ids() - (166691080, 3083169284L) + (166691080, 3083169284) # may vary """ if self._mask is nomask: @@ -4735,7 +4828,7 @@ def all(self, axis=None, out=None, keepdims=np._NoValue): See Also -------- - ndarray.all : corresponding function for ndarrays + numpy.ndarray.all : corresponding function for ndarrays numpy.all : equivalent function Examples @@ -4773,7 +4866,7 @@ def any(self, axis=None, out=None, keepdims=np._NoValue): See Also -------- - ndarray.any : corresponding function for ndarrays + numpy.ndarray.any : corresponding function for ndarrays numpy.any : equivalent function """ @@ -4827,7 +4920,7 @@ def nonzero(self): flatnonzero : Return indices that are non-zero in the flattened version of the input array. - ndarray.nonzero : + numpy.ndarray.nonzero : Equivalent ndarray method. count_nonzero : Counts the number of non-zero elements in the input array. @@ -4837,13 +4930,12 @@ def nonzero(self): >>> import numpy.ma as ma >>> x = ma.array(np.eye(3)) >>> x - masked_array(data = - [[ 1. 0. 0.] - [ 0. 1. 0.] - [ 0. 0. 1.]], - mask = - False, - fill_value=1e+20) + masked_array( + data=[[1., 0., 0.], + [0., 1., 0.], + [0., 0., 1.]], + mask=False, + fill_value=1e+20) >>> x.nonzero() (array([0, 1, 2]), array([0, 1, 2])) @@ -4851,15 +4943,14 @@ def nonzero(self): >>> x[1, 1] = ma.masked >>> x - masked_array(data = - [[1.0 0.0 0.0] - [0.0 -- 0.0] - [0.0 0.0 1.0]], - mask = - [[False False False] - [False True False] - [False False False]], - fill_value=1e+20) + masked_array( + data=[[1.0, 0.0, 0.0], + [0.0, --, 0.0], + [0.0, 0.0, 1.0]], + mask=[[False, False, False], + [False, True, False], + [False, False, False]], + fill_value=1e+20) >>> x.nonzero() (array([0, 2]), array([0, 2])) @@ -4876,13 +4967,12 @@ def nonzero(self): >>> a = ma.array([[1,2,3],[4,5,6],[7,8,9]]) >>> a > 3 - masked_array(data = - [[False False False] - [ True True True] - [ True True True]], - mask = - False, - fill_value=999999) + masked_array( + data=[[False, False, False], + [ True, True, True], + [ True, True, True]], + mask=False, + fill_value=True) >>> ma.nonzero(a > 3) (array([1, 1, 1, 2, 2, 2]), array([0, 1, 2, 0, 1, 2])) @@ -4901,8 +4991,8 @@ def trace(self, offset=0, axis1=0, axis2=1, dtype=None, out=None): #!!!: implement out + test! m = self._mask if m is nomask: - result = super(MaskedArray, self).trace(offset=offset, axis1=axis1, - axis2=axis2, out=out) + result = super().trace(offset=offset, axis1=axis1, axis2=axis2, + out=out) return result.astype(dtype) else: D = self.diagonal(offset=offset, axis1=axis1, axis2=axis2) @@ -4958,24 +5048,33 @@ def sum(self, axis=None, dtype=None, out=None, keepdims=np._NoValue): See Also -------- - ndarray.sum : corresponding function for ndarrays + numpy.ndarray.sum : corresponding function for ndarrays numpy.sum : equivalent function Examples -------- >>> x = np.ma.array([[1,2,3],[4,5,6],[7,8,9]], mask=[0] + [1,0]*4) - >>> print(x) - [[1 -- 3] - [-- 5 --] - [7 -- 9]] - >>> print(x.sum()) + >>> x + masked_array( + data=[[1, --, 3], + [--, 5, --], + [7, --, 9]], + mask=[[False, True, False], + [ True, False, True], + [False, True, False]], + fill_value=999999) + >>> x.sum() 25 - >>> print(x.sum(axis=1)) - [4 5 16] - >>> print(x.sum(axis=0)) - [8 5 12] + >>> x.sum(axis=1) + masked_array(data=[4, 5, 16], + mask=[False, False, False], + fill_value=999999) + >>> x.sum(axis=0) + masked_array(data=[8, 5, 12], + mask=[False, False, False], + fill_value=999999) >>> print(type(x.sum(axis=0, dtype=np.int64)[0])) - <type 'numpy.int64'> + <class 'numpy.int64'> """ kwargs = {} if keepdims is np._NoValue else {'keepdims': keepdims} @@ -4996,7 +5095,7 @@ def sum(self, axis=None, dtype=None, out=None, keepdims=np._NoValue): result = self.filled(0).sum(axis, dtype=dtype, out=out, **kwargs) if isinstance(out, MaskedArray): outmask = getmask(out) - if (outmask is nomask): + if outmask is nomask: outmask = out._mask = make_mask_none(out.shape) outmask.flat = newmask return out @@ -5013,21 +5112,24 @@ def cumsum(self, axis=None, dtype=None, out=None): Notes ----- - The mask is lost if `out` is not a valid :class:`MaskedArray` ! + The mask is lost if `out` is not a valid :class:`ma.MaskedArray` ! Arithmetic is modular when using integer types, and no error is raised on overflow. See Also -------- - ndarray.cumsum : corresponding function for ndarrays + numpy.ndarray.cumsum : corresponding function for ndarrays numpy.cumsum : equivalent function Examples -------- >>> marr = np.ma.array(np.arange(10), mask=[0,0,0,1,1,1,0,0,0,0]) - >>> print(marr.cumsum()) - [0 1 3 -- -- -- 9 16 24 33] + >>> marr.cumsum() + masked_array(data=[0, 1, 3, --, --, --, 9, 16, 24, 33], + mask=[False, False, False, True, True, True, False, False, + False, False], + fill_value=999999) """ result = self.filled(0).cumsum(axis=axis, dtype=dtype, out=out) @@ -5054,7 +5156,7 @@ def prod(self, axis=None, dtype=None, out=None, keepdims=np._NoValue): See Also -------- - ndarray.prod : corresponding function for ndarrays + numpy.ndarray.prod : corresponding function for ndarrays numpy.prod : equivalent function """ kwargs = {} if keepdims is np._NoValue else {'keepdims': keepdims} @@ -5075,7 +5177,7 @@ def prod(self, axis=None, dtype=None, out=None, keepdims=np._NoValue): result = self.filled(1).prod(axis, dtype=dtype, out=out, **kwargs) if isinstance(out, MaskedArray): outmask = getmask(out) - if (outmask is nomask): + if outmask is nomask: outmask = out._mask = make_mask_none(out.shape) outmask.flat = newmask return out @@ -5100,7 +5202,7 @@ def cumprod(self, axis=None, dtype=None, out=None): See Also -------- - ndarray.cumprod : corresponding function for ndarrays + numpy.ndarray.cumprod : corresponding function for ndarrays numpy.cumprod : equivalent function """ result = self.filled(1).cumprod(axis=axis, dtype=dtype, out=out) @@ -5123,17 +5225,17 @@ def mean(self, axis=None, dtype=None, out=None, keepdims=np._NoValue): See Also -------- - ndarray.mean : corresponding function for ndarrays + numpy.ndarray.mean : corresponding function for ndarrays numpy.mean : Equivalent function - numpy.ma.average: Weighted average. + numpy.ma.average : Weighted average. Examples -------- >>> a = np.ma.array([1,2,3], mask=[False, False, True]) >>> a - masked_array(data = [1 2 --], - mask = [False False True], - fill_value = 999999) + masked_array(data=[1, 2, --], + mask=[False, False, True], + fill_value=999999) >>> a.mean() 1.5 @@ -5141,8 +5243,7 @@ def mean(self, axis=None, dtype=None, out=None, keepdims=np._NoValue): kwargs = {} if keepdims is np._NoValue else {'keepdims': keepdims} if self._mask is nomask: - result = super(MaskedArray, self).mean(axis=axis, - dtype=dtype, **kwargs)[()] + result = super().mean(axis=axis, dtype=dtype, **kwargs)[()] else: dsum = self.sum(axis=axis, dtype=dtype, **kwargs) cnt = self.count(axis=axis, **kwargs) @@ -5154,7 +5255,7 @@ def mean(self, axis=None, dtype=None, out=None, keepdims=np._NoValue): out.flat = result if isinstance(out, MaskedArray): outmask = getmask(out) - if (outmask is nomask): + if outmask is nomask: outmask = out._mask = make_mask_none(out.shape) outmask.flat = getmask(result) return out @@ -5186,19 +5287,16 @@ def anom(self, axis=None, dtype=None): -------- >>> a = np.ma.array([1,2,3]) >>> a.anom() - masked_array(data = [-1. 0. 1.], - mask = False, - fill_value = 1e+20) + masked_array(data=[-1., 0., 1.], + mask=False, + fill_value=1e+20) """ m = self.mean(axis, dtype) - if m is masked: - return m - if not axis: - return (self - m) + return self - m else: - return (self - expand_dims(m, axis)) + return self - expand_dims(m, axis) def var(self, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): @@ -5212,15 +5310,15 @@ def var(self, axis=None, dtype=None, out=None, ddof=0, See Also -------- - ndarray.var : corresponding function for ndarrays + numpy.ndarray.var : corresponding function for ndarrays numpy.var : Equivalent function """ kwargs = {} if keepdims is np._NoValue else {'keepdims': keepdims} # Easy case: nomask, business as usual if self._mask is nomask: - ret = super(MaskedArray, self).var(axis=axis, dtype=dtype, out=out, - ddof=ddof, **kwargs)[()] + ret = super().var(axis=axis, dtype=dtype, out=out, ddof=ddof, + **kwargs)[()] if out is not None: if isinstance(out, MaskedArray): out.__setmask__(nomask) @@ -5275,7 +5373,7 @@ def std(self, axis=None, dtype=None, out=None, ddof=0, See Also -------- - ndarray.std : corresponding function for ndarrays + numpy.ndarray.std : corresponding function for ndarrays numpy.std : Equivalent function """ kwargs = {} if keepdims is np._NoValue else {'keepdims': keepdims} @@ -5296,7 +5394,7 @@ def round(self, decimals=0, out=None): See Also -------- - ndarray.around : corresponding function for ndarrays + numpy.ndarray.round : corresponding function for ndarrays numpy.around : equivalent function """ result = self._data.round(decimals=decimals, out=out).view(type(self)) @@ -5313,7 +5411,7 @@ def round(self, decimals=0, out=None): out.__setmask__(self._mask) return out - def argsort(self, axis=np._NoValue, kind='quicksort', order=None, + def argsort(self, axis=np._NoValue, kind=None, order=None, endwith=True, fill_value=None): """ Return an ndarray of indices that sort the array along the @@ -5333,7 +5431,7 @@ def argsort(self, axis=np._NoValue, kind='quicksort', order=None, Until then, the axis should be given explicitly when ``arr.ndim > 1``, to avoid a FutureWarning. kind : {'quicksort', 'mergesort', 'heapsort', 'stable'}, optional - Sorting algorithm. + The sorting algorithm used. order : list, optional When `a` is an array with fields defined, this argument specifies which fields to compare first, second, etc. Not all fields need be @@ -5344,7 +5442,7 @@ def argsort(self, axis=np._NoValue, kind='quicksort', order=None, When the array contains unmasked values at the same extremes of the datatype, the ordering of these values and the masked values is undefined. - fill_value : {var}, optional + fill_value : scalar or None, optional Value used internally for the masked values. If ``fill_value`` is not None, it supersedes ``endwith``. @@ -5356,9 +5454,9 @@ def argsort(self, axis=np._NoValue, kind='quicksort', order=None, See Also -------- - MaskedArray.sort : Describes sorting algorithms used. + ma.MaskedArray.sort : Describes sorting algorithms used. lexsort : Indirect stable sort with multiple keys. - ndarray.sort : Inplace sort. + numpy.ndarray.sort : Inplace sort. Notes ----- @@ -5368,9 +5466,9 @@ def argsort(self, axis=np._NoValue, kind='quicksort', order=None, -------- >>> a = np.ma.array([3,2,1], mask=[False, False, True]) >>> a - masked_array(data = [3 2 --], - mask = [False False True], - fill_value = 999999) + masked_array(data=[3, 2, --], + mask=[False, False, True], + fill_value=999999) >>> a.argsort() array([1, 0, 2]) @@ -5393,7 +5491,8 @@ def argsort(self, axis=np._NoValue, kind='quicksort', order=None, filled = self.filled(fill_value) return filled.argsort(axis=axis, kind=kind, order=order) - def argmin(self, axis=None, fill_value=None, out=None): + def argmin(self, axis=None, fill_value=None, out=None, *, + keepdims=np._NoValue): """ Return array of indices to the minimum values along the given axis. @@ -5402,7 +5501,7 @@ def argmin(self, axis=None, fill_value=None, out=None): axis : {None, integer} If None, the index is into the flattened array, otherwise along the specified axis - fill_value : {var}, optional + fill_value : scalar or None, optional Value used to fill in the masked values. If None, the output of minimum_fill_value(self._data) is used instead. out : {None, array}, optional @@ -5418,23 +5517,29 @@ def argmin(self, axis=None, fill_value=None, out=None): Examples -------- - >>> x = np.ma.array(arange(4), mask=[1,1,0,0]) + >>> x = np.ma.array(np.arange(4), mask=[1,1,0,0]) >>> x.shape = (2,2) - >>> print(x) - [[-- --] - [2 3]] - >>> print(x.argmin(axis=0, fill_value=-1)) - [0 0] - >>> print(x.argmin(axis=0, fill_value=9)) - [1 1] + >>> x + masked_array( + data=[[--, --], + [2, 3]], + mask=[[ True, True], + [False, False]], + fill_value=999999) + >>> x.argmin(axis=0, fill_value=-1) + array([0, 0]) + >>> x.argmin(axis=0, fill_value=9) + array([1, 1]) """ if fill_value is None: fill_value = minimum_fill_value(self) d = self.filled(fill_value).view(ndarray) - return d.argmin(axis, out=out) + keepdims = False if keepdims is np._NoValue else bool(keepdims) + return d.argmin(axis, out=out, keepdims=keepdims) - def argmax(self, axis=None, fill_value=None, out=None): + def argmax(self, axis=None, fill_value=None, out=None, *, + keepdims=np._NoValue): """ Returns array of indices of the maximum values along the given axis. Masked values are treated as if they had the value fill_value. @@ -5444,7 +5549,7 @@ def argmax(self, axis=None, fill_value=None, out=None): axis : {None, integer} If None, the index is into the flattened array, otherwise along the specified axis - fill_value : {var}, optional + fill_value : scalar or None, optional Value used to fill in the masked values. If None, the output of maximum_fill_value(self._data) is used instead. out : {None, array}, optional @@ -5469,9 +5574,10 @@ def argmax(self, axis=None, fill_value=None, out=None): if fill_value is None: fill_value = maximum_fill_value(self._data) d = self.filled(fill_value).view(ndarray) - return d.argmax(axis, out=out) + keepdims = False if keepdims is np._NoValue else bool(keepdims) + return d.argmax(axis, out=out, keepdims=keepdims) - def sort(self, axis=-1, kind='quicksort', order=None, + def sort(self, axis=-1, kind=None, order=None, endwith=True, fill_value=None): """ Sort the array, in-place @@ -5484,7 +5590,7 @@ def sort(self, axis=-1, kind='quicksort', order=None, Axis along which to sort. If None, the array is flattened before sorting. The default is -1, which sorts along the last axis. kind : {'quicksort', 'mergesort', 'heapsort', 'stable'}, optional - Sorting algorithm. Default is 'quicksort'. + The sorting algorithm used. order : list, optional When `a` is a structured array, this argument specifies which fields to compare first, second, and so on. This list does not need to @@ -5492,10 +5598,10 @@ def sort(self, axis=-1, kind='quicksort', order=None, endwith : {True, False}, optional Whether missing values (if any) should be treated as the largest values (True) or the smallest values (False) - When the array contains unmasked values at the same extremes of the + When the array contains unmasked values sorting at the same extremes of the datatype, the ordering of these values and the masked values is undefined. - fill_value : {var}, optional + fill_value : scalar or None, optional Value used internally for the masked values. If ``fill_value`` is not None, it supersedes ``endwith``. @@ -5506,7 +5612,7 @@ def sort(self, axis=-1, kind='quicksort', order=None, See Also -------- - ndarray.sort : Method to sort an array in-place. + numpy.ndarray.sort : Method to sort an array in-place. argsort : Indirect sort. lexsort : Indirect stable sort on multiple keys. searchsorted : Find elements in a sorted array. @@ -5517,23 +5623,29 @@ def sort(self, axis=-1, kind='quicksort', order=None, Examples -------- - >>> a = ma.array([1, 2, 5, 4, 3],mask=[0, 1, 0, 1, 0]) + >>> a = np.ma.array([1, 2, 5, 4, 3],mask=[0, 1, 0, 1, 0]) >>> # Default >>> a.sort() - >>> print(a) - [1 3 5 -- --] + >>> a + masked_array(data=[1, 3, 5, --, --], + mask=[False, False, False, True, True], + fill_value=999999) - >>> a = ma.array([1, 2, 5, 4, 3],mask=[0, 1, 0, 1, 0]) + >>> a = np.ma.array([1, 2, 5, 4, 3],mask=[0, 1, 0, 1, 0]) >>> # Put missing values in the front >>> a.sort(endwith=False) - >>> print(a) - [-- -- 1 3 5] + >>> a + masked_array(data=[--, --, 1, 3, 5], + mask=[ True, True, False, False, False], + fill_value=999999) - >>> a = ma.array([1, 2, 5, 4, 3],mask=[0, 1, 0, 1, 0]) + >>> a = np.ma.array([1, 2, 5, 4, 3],mask=[0, 1, 0, 1, 0]) >>> # fill_value takes over endwith >>> a.sort(endwith=False, fill_value=3) - >>> print(a) - [1 -- -- 3 5] + >>> a + masked_array(data=[1, --, --, 3, 5], + mask=[False, True, True, False, False], + fill_value=999999) """ if self._mask is nomask: @@ -5560,9 +5672,13 @@ def min(self, axis=None, out=None, fill_value=None, keepdims=np._NoValue): out : array_like, optional Alternative output array in which to place the result. Must be of the same shape and buffer length as the expected output. - fill_value : {var}, optional + fill_value : scalar or None, optional Value used to fill in the masked values. If None, use the output of `minimum_fill_value`. + keepdims : bool, optional + If this is set to True, the axes which are reduced are left + in the result as dimensions with size one. With this option, + the result will broadcast correctly against the array. Returns ------- @@ -5572,7 +5688,7 @@ def min(self, axis=None, out=None, fill_value=None, keepdims=np._NoValue): See Also -------- - minimum_fill_value + ma.minimum_fill_value Returns the minimum filling value for a given datatype. """ @@ -5599,7 +5715,7 @@ def min(self, axis=None, out=None, fill_value=None, keepdims=np._NoValue): result = self.filled(fill_value).min(axis=axis, out=out, **kwargs) if isinstance(out, MaskedArray): outmask = getmask(out) - if (outmask is nomask): + if outmask is nomask: outmask = out._mask = make_mask_none(out.shape) outmask.flat = newmask else: @@ -5639,27 +5755,36 @@ def mini(self, axis=None): Examples -------- >>> x = np.ma.array(np.arange(6), mask=[0 ,1, 0, 0, 0 ,1]).reshape(3, 2) - >>> print(x) - [[0 --] - [2 3] - [4 --]] + >>> x + masked_array( + data=[[0, --], + [2, 3], + [4, --]], + mask=[[False, True], + [False, False], + [False, True]], + fill_value=999999) >>> x.mini() - 0 + masked_array(data=0, + mask=False, + fill_value=999999) >>> x.mini(axis=0) - masked_array(data = [0 3], - mask = [False False], - fill_value = 999999) - >>> print(x.mini(axis=1)) - [0 2 4] + masked_array(data=[0, 3], + mask=[False, False], + fill_value=999999) + >>> x.mini(axis=1) + masked_array(data=[0, 2, 4], + mask=[False, False, False], + fill_value=999999) There is a small difference between `mini` and `min`: >>> x[:,1].mini(axis=0) - masked_array(data = --, - mask = True, - fill_value = 999999) + masked_array(data=3, + mask=False, + fill_value=999999) >>> x[:,1].min(axis=0) - masked + 3 """ # 2016-04-13, 1.13.0, gh-8764 @@ -5681,9 +5806,13 @@ def max(self, axis=None, out=None, fill_value=None, keepdims=np._NoValue): out : array_like, optional Alternative output array in which to place the result. Must be of the same shape and buffer length as the expected output. - fill_value : {var}, optional + fill_value : scalar or None, optional Value used to fill in the masked values. If None, use the output of maximum_fill_value(). + keepdims : bool, optional + If this is set to True, the axes which are reduced are left + in the result as dimensions with size one. With this option, + the result will broadcast correctly against the array. Returns ------- @@ -5693,7 +5822,7 @@ def max(self, axis=None, out=None, fill_value=None, keepdims=np._NoValue): See Also -------- - maximum_fill_value + ma.maximum_fill_value Returns the maximum filling value for a given datatype. """ @@ -5720,7 +5849,7 @@ def max(self, axis=None, out=None, fill_value=None, keepdims=np._NoValue): result = self.filled(fill_value).max(axis=axis, out=out, **kwargs) if isinstance(out, MaskedArray): outmask = getmask(out) - if (outmask is nomask): + if outmask is nomask: outmask = out._mask = make_mask_none(out.shape) outmask.flat = newmask else: @@ -5737,6 +5866,14 @@ def ptp(self, axis=None, out=None, fill_value=None, keepdims=False): Return (maximum - minimum) along the given dimension (i.e. peak-to-peak value). + .. warning:: + `ptp` preserves the data type of the array. This means the + return value for an input of signed integers with n bits + (e.g. `np.int8`, `np.int16`, etc) is also a signed integer + with n bits. In that case, peak-to-peak values greater than + ``2**(n-1)-1`` will be returned as negative values. An example + with a work-around is shown below. + Parameters ---------- axis : {None, int}, optional @@ -5746,8 +5883,12 @@ def ptp(self, axis=None, out=None, fill_value=None, keepdims=False): Alternative output array in which to place the result. It must have the same shape and buffer length as the expected output but the type will be cast if necessary. - fill_value : {var}, optional + fill_value : scalar or None, optional Value used to fill in the masked values. + keepdims : bool, optional + If this is set to True, the axes which are reduced are left + in the result as dimensions with size one. With this option, + the result will broadcast correctly against the array. Returns ------- @@ -5755,6 +5896,45 @@ def ptp(self, axis=None, out=None, fill_value=None, keepdims=False): A new array holding the result, unless ``out`` was specified, in which case a reference to ``out`` is returned. + Examples + -------- + >>> x = np.ma.MaskedArray([[4, 9, 2, 10], + ... [6, 9, 7, 12]]) + + >>> x.ptp(axis=1) + masked_array(data=[8, 6], + mask=False, + fill_value=999999) + + >>> x.ptp(axis=0) + masked_array(data=[2, 0, 5, 2], + mask=False, + fill_value=999999) + + >>> x.ptp() + 10 + + This example shows that a negative value can be returned when + the input is an array of signed integers. + + >>> y = np.ma.MaskedArray([[1, 127], + ... [0, 127], + ... [-1, 127], + ... [-2, 127]], dtype=np.int8) + >>> y.ptp(axis=1) + masked_array(data=[ 126, 127, -128, -127], + mask=False, + fill_value=999999, + dtype=int8) + + A work-around is to use the `view()` method to view the result as + unsigned integers with the same bit width: + + >>> y.ptp(axis=1).view(np.uint8) + masked_array(data=[126, 127, 128, 129], + mask=False, + fill_value=999999, + dtype=uint8) """ if out is None: result = self.max(axis=axis, fill_value=fill_value, @@ -5771,15 +5951,15 @@ def ptp(self, axis=None, out=None, fill_value=None, keepdims=False): def partition(self, *args, **kwargs): warnings.warn("Warning: 'partition' will ignore the 'mask' " - "of the {}.".format(self.__class__.__name__), + f"of the {self.__class__.__name__}.", stacklevel=2) - return super(MaskedArray, self).partition(*args, **kwargs) + return super().partition(*args, **kwargs) def argpartition(self, *args, **kwargs): warnings.warn("Warning: 'argpartition' will ignore the 'mask' " - "of the {}.".format(self.__class__.__name__), + f"of the {self.__class__.__name__}.", stacklevel=2) - return super(MaskedArray, self).argpartition(*args, **kwargs) + return super().argpartition(*args, **kwargs) def take(self, indices, axis=None, out=None, mode='raise'): """ @@ -5808,7 +5988,6 @@ def take(self, indices, axis=None, out=None, mode='raise'): return out[()] # Array methods - clip = _arraymethod('clip', onmask=False) copy = _arraymethod('copy') diagonal = _arraymethod('diagonal') flatten = _arraymethod('flatten') @@ -5870,12 +6049,19 @@ def tolist(self, fill_value=None): return result.tolist() def tostring(self, fill_value=None, order='C'): + r""" + A compatibility alias for `tobytes`, with exactly the same behavior. + + Despite its name, it returns `bytes` not `str`\ s. + + .. deprecated:: 1.19.0 """ - This function is a compatibility alias for tobytes. Despite its name it - returns bytes not strings. - """ + # 2020-03-30, Numpy 1.19.0 + warnings.warn( + "tostring() is deprecated. Use tobytes() instead.", + DeprecationWarning, stacklevel=2) - return self.tobytes(fill_value, order='C') + return self.tobytes(fill_value, order=order) def tobytes(self, fill_value=None, order='C'): """ @@ -5900,7 +6086,7 @@ def tobytes(self, fill_value=None, order='C'): See Also -------- - ndarray.tobytes + numpy.ndarray.tobytes tolist, tofile Notes @@ -5912,7 +6098,7 @@ def tobytes(self, fill_value=None, order='C'): -------- >>> x = np.ma.array(np.array([[1, 2], [3, 4]]), mask=[[0, 1], [1, 0]]) >>> x.tobytes() - '\\x01\\x00\\x00\\x00?B\\x0f\\x00?B\\x0f\\x00\\x04\\x00\\x00\\x00' + b'\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00?B\\x0f\\x00\\x00\\x00\\x00\\x00?B\\x0f\\x00\\x00\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00' """ return self.filled(fill_value).tobytes(order=order) @@ -5960,14 +6146,20 @@ def toflex(self): Examples -------- >>> x = np.ma.array([[1,2,3],[4,5,6],[7,8,9]], mask=[0] + [1,0]*4) - >>> print(x) - [[1 -- 3] - [-- 5 --] - [7 -- 9]] - >>> print(x.toflex()) - [[(1, False) (2, True) (3, False)] - [(4, True) (5, False) (6, True)] - [(7, False) (8, True) (9, False)]] + >>> x + masked_array( + data=[[1, --, 3], + [--, 5, --], + [7, --, 9]], + mask=[[False, True, False], + [ True, False, True], + [False, True, False]], + fill_value=999999) + >>> x.toflex() + array([[(1, False), (2, True), (3, False)], + [(4, True), (5, False), (6, True)], + [(7, False), (8, True), (9, False)]], + dtype=[('_data', '<i8'), ('_mask', '?')]) """ # Get the basic dtype. @@ -5993,7 +6185,7 @@ def __getstate__(self): """ cf = 'CF'[self.flags.fnc] - data_state = super(MaskedArray, self).__reduce__()[2] + data_state = super().__reduce__()[2] return data_state + (getmaskarray(self).tobytes(cf), self._fill_value) def __setstate__(self, state): @@ -6009,7 +6201,7 @@ def __setstate__(self, state): """ (_, shp, typ, isf, raw, msk, flv) = state - super(MaskedArray, self).__setstate__((shp, typ, isf, raw)) + super().__setstate__((shp, typ, isf, raw)) self._mask.__setstate__((shp, make_mask_descr(typ), isf, msk)) self.fill_value = flv @@ -6067,11 +6259,10 @@ def __new__(self, data, mask=nomask, dtype=None, fill_value=None, _data.fill_value = fill_value return _data - def _get_data(self): + @property + def _data(self): # Make sure that the _data part is a np.void - return super(mvoid, self)._data[()] - - _data = property(fget=_get_data) + return super()._data[()] def __getitem__(self, indx): """ @@ -6108,7 +6299,7 @@ def __str__(self): return str(self._data) rdtype = _replace_dtype_fields(self._data.dtype, "O") - data_arr = super(mvoid, self)._data + data_arr = super()._data res = data_arr.astype(rdtype) _recursive_printoption(res, self._mask, masked_print_option) return str(res) @@ -6119,8 +6310,7 @@ def __iter__(self): "Defines an iterator for mvoid" (_data, _mask) = (self._data, self._mask) if _mask is nomask: - for d in _data: - yield d + yield from _data else: for (d, m) in zip(_data, _mask): if m: @@ -6137,9 +6327,11 @@ def filled(self, fill_value=None): Parameters ---------- - fill_value : scalar, optional - The value to use for invalid entries (None by default). - If None, the `fill_value` attribute is used instead. + fill_value : array_like, optional + The value to use for invalid entries. Can be scalar or + non-scalar. If latter is the case, the filled array should + be broadcastable over input array. Default is None, in + which case the `fill_value` attribute is used instead. Returns ------- @@ -6214,15 +6406,14 @@ def isMaskedArray(x): [ 0., 0., 1.]]) >>> m = ma.masked_values(a, 0) >>> m - masked_array(data = - [[1.0 -- --] - [-- 1.0 --] - [-- -- 1.0]], - mask = - [[False True True] - [ True False True] - [ True True False]], - fill_value=0.0) + masked_array( + data=[[1.0, --, --], + [--, 1.0, --], + [--, --, 1.0]], + mask=[[False, True, True], + [ True, False, True], + [ True, True, False]], + fill_value=0.0) >>> ma.isMaskedArray(a) False >>> ma.isMaskedArray(m) @@ -6270,7 +6461,7 @@ def __array_finalize__(self, obj): if not self.__has_singleton(): # this handles the `.view` in __new__, which we want to copy across # properties normally - return super(MaskedConstant, self).__array_finalize__(obj) + return super().__array_finalize__(obj) elif self is self.__singleton: # not clear how this can happen, play it safe pass @@ -6289,10 +6480,6 @@ def __array_wrap__(self, obj, context=None): def __str__(self): return str(masked_print_option._display) - if sys.version_info.major < 3: - def __unicode__(self): - return unicode(masked_print_option._display) - def __repr__(self): if self is MaskedConstant.__singleton: return 'masked' @@ -6300,6 +6487,21 @@ def __repr__(self): # it's a subclass, or something is wrong, make it obvious return object.__repr__(self) + def __format__(self, format_spec): + # Replace ndarray.__format__ with the default, which supports no format characters. + # Supporting format characters is unwise here, because we do not know what type + # the user was expecting - better to not guess. + try: + return object.__format__(self, format_spec) + except TypeError: + # 2020-03-23, NumPy 1.19.0 + warnings.warn( + "Format strings passed to MaskedConstant are ignored, but in future may " + "error or produce different behavior", + FutureWarning, stacklevel=2 + ) + return object.__format__(self, "") + def __reduce__(self): """Override of MaskedArray's __reduce__. """ @@ -6326,21 +6528,21 @@ def copy(self, *args, **kwargs): def __copy__(self): return self - + def __deepcopy__(self, memo): return self def __setattr__(self, attr, value): if not self.__has_singleton(): # allow the singleton to be initialized - return super(MaskedConstant, self).__setattr__(attr, value) + return super().__setattr__(attr, value) elif self is self.__singleton: raise AttributeError( - "attributes of {!r} are not writeable".format(self)) + f"attributes of {self!r} are not writeable") else: # duplicate instance - we can end up here from __array_finalize__, # where we set the __class__ attribute - return super(MaskedConstant, self).__setattr__(attr, value) + return super().__setattr__(attr, value) masked = masked_singleton = MaskedConstant() @@ -6386,16 +6588,16 @@ def is_masked(x): >>> import numpy.ma as ma >>> x = ma.masked_equal([0, 1, 0, 2, 3], 0) >>> x - masked_array(data = [-- 1 -- 2 3], - mask = [ True False True False False], - fill_value=999999) + masked_array(data=[--, 1, --, 2, 3], + mask=[ True, False, True, False, False], + fill_value=0) >>> ma.is_masked(x) True >>> x = ma.masked_equal([0, 1, 0, 2, 3], 42) >>> x - masked_array(data = [0 1 0 2 3], - mask = False, - fill_value=999999) + masked_array(data=[0, 1, 0, 2, 3], + mask=False, + fill_value=42) >>> ma.is_masked(x) False @@ -6432,7 +6634,7 @@ class _extrema_operation(_MaskedUFunc): """ def __init__(self, ufunc, compare, fill_value): - super(_extrema_operation, self).__init__(ufunc) + super().__init__(ufunc) self.compare = compare self.fill_value_func = fill_value @@ -6441,8 +6643,8 @@ def __call__(self, a, b=None): if b is None: # 2016-04-13, 1.13.0 warnings.warn( - "Single-argument form of np.ma.{0} is deprecated. Use " - "np.ma.{0}.reduce instead.".format(self.__name__), + f"Single-argument form of np.ma.{self.__name__} is deprecated. Use " + f"np.ma.{self.__name__}.reduce instead.", DeprecationWarning, stacklevel=2) return self.reduce(a) return where(self.compare(a, b), a, b) @@ -6455,11 +6657,9 @@ def reduce(self, target, axis=np._NoValue): if axis is np._NoValue and target.ndim > 1: # 2017-05-06, Numpy 1.13.0: warn on axis default warnings.warn( - "In the future the default for ma.{0}.reduce will be axis=0, " - "not the current None, to match np.{0}.reduce. " - "Explicitly pass 0 or None to silence this warning.".format( - self.__name__ - ), + f"In the future the default for ma.{self.__name__}.reduce will be axis=0, " + f"not the current None, to match np.{self.__name__}.reduce. " + "Explicitly pass 0 or None to silence this warning.", MaskedArrayFutureWarning, stacklevel=2) axis = None @@ -6539,7 +6739,7 @@ def ptp(obj, axis=None, out=None, fill_value=None, keepdims=np._NoValue): ############################################################################## -class _frommethod(object): +class _frommethod: """ Define functions from existing MaskedArray methods. @@ -6655,7 +6855,7 @@ def power(a, b, third=None): invalid = np.logical_not(np.isfinite(result.view(ndarray))) # Add the initial mask if m is not nomask: - if not (result.ndim): + if not result.ndim: return masked result._mask = np.logical_or(m, invalid) # Fix the invalid parts @@ -6670,7 +6870,7 @@ def power(a, b, third=None): argmin = _frommethod('argmin') argmax = _frommethod('argmax') -def argsort(a, axis=np._NoValue, kind='quicksort', order=None, endwith=True, fill_value=None): +def argsort(a, axis=np._NoValue, kind=None, order=None, endwith=True, fill_value=None): "Function version of the eponymous method." a = np.asanyarray(a) @@ -6685,8 +6885,19 @@ def argsort(a, axis=np._NoValue, kind='quicksort', order=None, endwith=True, fil return a.argsort(axis=axis, kind=kind, order=order) argsort.__doc__ = MaskedArray.argsort.__doc__ -def sort(a, axis=-1, kind='quicksort', order=None, endwith=True, fill_value=None): - "Function version of the eponymous method." +def sort(a, axis=-1, kind=None, order=None, endwith=True, fill_value=None): + """ + Return a sorted copy of the masked array. + + Equivalent to creating a copy of the array + and applying the MaskedArray ``sort()`` method. + + Refer to ``MaskedArray.sort`` for the full documentation + + See Also + -------- + MaskedArray.sort : equivalent method + """ a = np.array(a, copy=True, subok=True) if axis is None: a = a.flatten() @@ -6698,7 +6909,6 @@ def sort(a, axis=-1, kind='quicksort', order=None, endwith=True, fill_value=None else: a.sort(axis=axis, kind=kind, order=order) return a -sort.__doc__ = MaskedArray.sort.__doc__ def compressed(x): @@ -6706,12 +6916,11 @@ def compressed(x): Return all the non-masked data as a 1-D array. This function is equivalent to calling the "compressed" method of a - `MaskedArray`, see `MaskedArray.compressed` for details. + `ma.MaskedArray`, see `ma.MaskedArray.compressed` for details. See Also -------- - MaskedArray.compressed - Equivalent method. + ma.MaskedArray.compressed : Equivalent method. """ return asanyarray(x).compressed() @@ -6745,17 +6954,17 @@ def concatenate(arrays, axis=0): >>> a[1] = ma.masked >>> b = ma.arange(2, 5) >>> a - masked_array(data = [0 -- 2], - mask = [False True False], - fill_value = 999999) + masked_array(data=[0, --, 2], + mask=[False, True, False], + fill_value=999999) >>> b - masked_array(data = [2 3 4], - mask = False, - fill_value = 999999) + masked_array(data=[2, 3, 4], + mask=False, + fill_value=999999) >>> ma.concatenate([a, b]) - masked_array(data = [0 -- 2 2 3 4], - mask = [False True False False False False], - fill_value = 999999) + masked_array(data=[0, --, 2, 2, 3, 4], + mask=[False, True, False, False, False, False], + fill_value=999999) """ d = np.concatenate([getdata(a) for a in arrays], axis) @@ -6795,56 +7004,6 @@ def diag(v, k=0): return output -def expand_dims(x, axis): - """ - Expand the shape of an array. - - Expands the shape of the array by including a new axis before the one - specified by the `axis` parameter. This function behaves the same as - `numpy.expand_dims` but preserves masked elements. - - See Also - -------- - numpy.expand_dims : Equivalent function in top-level NumPy module. - - Examples - -------- - >>> import numpy.ma as ma - >>> x = ma.array([1, 2, 4]) - >>> x[1] = ma.masked - >>> x - masked_array(data = [1 -- 4], - mask = [False True False], - fill_value = 999999) - >>> np.expand_dims(x, axis=0) - array([[1, 2, 4]]) - >>> ma.expand_dims(x, axis=0) - masked_array(data = - [[1 -- 4]], - mask = - [[False True False]], - fill_value = 999999) - - The same result can be achieved using slicing syntax with `np.newaxis`. - - >>> x[np.newaxis, :] - masked_array(data = - [[1 -- 4]], - mask = - [[False True False]], - fill_value = 999999) - - """ - result = n_expand_dims(x, axis) - if isinstance(x, MaskedArray): - new_shape = result.shape - result = x.view() - result.shape = new_shape - if result._mask is not nomask: - result._mask.shape = new_shape - return result - - def left_shift(a, n): """ Shift the bits of an integer to the left. @@ -6960,24 +7119,21 @@ def transpose(a, axes=None): >>> import numpy.ma as ma >>> x = ma.arange(4).reshape((2,2)) >>> x[1, 1] = ma.masked - >>>> x - masked_array(data = - [[0 1] - [2 --]], - mask = - [[False False] - [False True]], - fill_value = 999999) + >>> x + masked_array( + data=[[0, 1], + [2, --]], + mask=[[False, False], + [False, True]], + fill_value=999999) >>> ma.transpose(x) - masked_array(data = - [[0 2] - [1 --]], - mask = - [[False False] - [False True]], - fill_value = 999999) - + masked_array( + data=[[0, 2], + [1, --]], + mask=[[False, False], + [False, True]], + fill_value=999999) """ # We can't use 'frommethod', as 'transpose' doesn't take keywords try: @@ -7024,39 +7180,39 @@ def resize(x, new_shape): >>> a = ma.array([[1, 2] ,[3, 4]]) >>> a[0, 1] = ma.masked >>> a - masked_array(data = - [[1 --] - [3 4]], - mask = - [[False True] - [False False]], - fill_value = 999999) + masked_array( + data=[[1, --], + [3, 4]], + mask=[[False, True], + [False, False]], + fill_value=999999) >>> np.resize(a, (3, 3)) - array([[1, 2, 3], - [4, 1, 2], - [3, 4, 1]]) + masked_array( + data=[[1, 2, 3], + [4, 1, 2], + [3, 4, 1]], + mask=False, + fill_value=999999) >>> ma.resize(a, (3, 3)) - masked_array(data = - [[1 -- 3] - [4 1 --] - [3 4 1]], - mask = - [[False True False] - [False False True] - [False False False]], - fill_value = 999999) + masked_array( + data=[[1, --, 3], + [4, 1, --], + [3, 4, 1]], + mask=[[False, True, False], + [False, False, True], + [False, False, False]], + fill_value=999999) A MaskedArray is always returned, regardless of the input type. >>> a = np.array([[1, 2] ,[3, 4]]) >>> ma.resize(a, (3, 3)) - masked_array(data = - [[1 2 3] - [4 1 2] - [3 4 1]], - mask = - False, - fill_value = 999999) + masked_array( + data=[[1, 2, 3], + [4, 1, 2], + [3, 4, 1]], + mask=False, + fill_value=999999) """ # We can't use _frommethods here, as N.resize is notoriously whiny. @@ -7069,23 +7225,6 @@ def resize(x, new_shape): return result -def rank(obj): - """ - maskedarray version of the numpy function. - - .. note:: - Deprecated since 1.10.0 - - """ - # 2015-04-12, 1.10.0 - warnings.warn( - "`rank` is deprecated; use the `ndim` function instead. ", - np.VisibleDeprecationWarning, stacklevel=2) - return np.ndim(getdata(obj)) - -rank.__doc__ = np.rank.__doc__ - - def ndim(obj): """ maskedarray version of the numpy function. @@ -7115,49 +7254,56 @@ def size(obj, axis=None): def where(condition, x=_NoValue, y=_NoValue): """ - Return a masked array with elements from x or y, depending on condition. + Return a masked array with elements from `x` or `y`, depending on condition. - Returns a masked array, shaped like condition, where the elements - are from `x` when `condition` is True, and from `y` otherwise. - If neither `x` nor `y` are given, the function returns a tuple of - indices where `condition` is True (the result of - ``condition.nonzero()``). + .. note:: + When only `condition` is provided, this function is identical to + `nonzero`. The rest of this documentation covers only the case where + all three arguments are provided. Parameters ---------- condition : array_like, bool - The condition to meet. For each True element, yield the corresponding - element from `x`, otherwise from `y`. + Where True, yield `x`, otherwise yield `y`. x, y : array_like, optional Values from which to choose. `x`, `y` and `condition` need to be broadcastable to some shape. Returns ------- - out : MaskedArray or tuple of ndarrays - The resulting masked array if `x` and `y` were given, otherwise - the result of ``condition.nonzero()``. + out : MaskedArray + An masked array with `masked` elements where the condition is masked, + elements from `x` where `condition` is True, and elements from `y` + elsewhere. See Also -------- numpy.where : Equivalent function in the top-level NumPy module. + nonzero : The function that is called when x and y are omitted Examples -------- >>> x = np.ma.array(np.arange(9.).reshape(3, 3), mask=[[0, 1, 0], ... [1, 0, 1], ... [0, 1, 0]]) - >>> print(x) - [[0.0 -- 2.0] - [-- 4.0 --] - [6.0 -- 8.0]] - >>> np.ma.where(x > 5) # return the indices where x > 5 - (array([2, 2]), array([0, 2])) - - >>> print(np.ma.where(x > 5, x, -3.1416)) - [[-3.1416 -- -3.1416] - [-- -3.1416 --] - [6.0 -- 8.0]] + >>> x + masked_array( + data=[[0.0, --, 2.0], + [--, 4.0, --], + [6.0, --, 8.0]], + mask=[[False, True, False], + [ True, False, True], + [False, True, False]], + fill_value=1e+20) + >>> np.ma.where(x > 5, x, -3.1416) + masked_array( + data=[[-3.1416, --, -3.1416], + [--, -3.1416, --], + [6.0, --, 8.0]], + mask=[[False, True, False], + [ True, False, True], + [False, True, False]], + fill_value=1e+20) """ @@ -7199,16 +7345,16 @@ def where(condition, x=_NoValue, y=_NoValue): def choose(indices, choices, out=None, mode='raise'): """ - Use an index array to construct a new array from a set of choices. + Use an index array to construct a new array from a list of choices. - Given an array of integers and a set of n choice arrays, this method + Given an array of integers and a list of n choice arrays, this method will create a new array that merges each of the choice arrays. Where a - value in `a` is i, the new array will have the value that choices[i] + value in `index` is i, the new array will have the value that choices[i] contains in the same place. Parameters ---------- - a : ndarray of ints + indices : ndarray of ints This array must contain integers in ``[0, n-1]``, where n is the number of choices. choices : sequence of arrays @@ -7237,9 +7383,9 @@ def choose(indices, choices, out=None, mode='raise'): >>> choice = np.array([[1,1,1], [2,2,2], [3,3,3]]) >>> a = np.array([2, 1, 0]) >>> np.ma.choose(a, choice) - masked_array(data = [3 2 1], - mask = False, - fill_value=999999) + masked_array(data=[3, 2, 1], + mask=False, + fill_value=999999) """ def fmask(x): @@ -7261,7 +7407,7 @@ def nmask(x): # Construct the mask outputmask = np.choose(c, masks, mode=mode) outputmask = make_mask(mask_or(outputmask, getmask(indices)), - copy=0, shrink=True) + copy=False, shrink=True) # Get the choices. d = np.choose(c, data, mode=mode, out=out).view(MaskedArray) if out is not None: @@ -7362,25 +7508,23 @@ def mask_rowcols(a, axis=None): [0, 0, 0]]) >>> a = ma.masked_equal(a, 1) >>> a - masked_array(data = - [[0 0 0] - [0 -- 0] - [0 0 0]], - mask = - [[False False False] - [False True False] - [False False False]], - fill_value=999999) + masked_array( + data=[[0, 0, 0], + [0, --, 0], + [0, 0, 0]], + mask=[[False, False, False], + [False, True, False], + [False, False, False]], + fill_value=1) >>> ma.mask_rowcols(a) - masked_array(data = - [[0 -- 0] - [-- -- --] - [0 -- 0]], - mask = - [[False True False] - [ True True True] - [False True False]], - fill_value=999999) + masked_array( + data=[[0, --, 0], + [--, --, --], + [0, --, 0]], + mask=[[False, True, False], + [ True, True, True], + [False, True, False]], + fill_value=1) """ a = array(a, subok=False) @@ -7441,24 +7585,22 @@ def dot(a, b, strict=False, out=None): Examples -------- - >>> a = ma.array([[1, 2, 3], [4, 5, 6]], mask=[[1, 0, 0], [0, 0, 0]]) - >>> b = ma.array([[1, 2], [3, 4], [5, 6]], mask=[[1, 0], [0, 0], [0, 0]]) + >>> a = np.ma.array([[1, 2, 3], [4, 5, 6]], mask=[[1, 0, 0], [0, 0, 0]]) + >>> b = np.ma.array([[1, 2], [3, 4], [5, 6]], mask=[[1, 0], [0, 0], [0, 0]]) >>> np.ma.dot(a, b) - masked_array(data = - [[21 26] - [45 64]], - mask = - [[False False] - [False False]], - fill_value = 999999) + masked_array( + data=[[21, 26], + [45, 64]], + mask=[[False, False], + [False, False]], + fill_value=999999) >>> np.ma.dot(a, b, strict=True) - masked_array(data = - [[-- --] - [-- 64]], - mask = - [[ True True] - [ True False]], - fill_value = 999999) + masked_array( + data=[[--, --], + [--, 64]], + mask=[[ True, True], + [ True, False]], + fill_value=999999) """ # !!!: Works only with 2D arrays. There should be a way to get it to run @@ -7517,7 +7659,7 @@ def outer(a, b): return masked_array(d) ma = getmaskarray(a) mb = getmaskarray(b) - m = make_mask(1 - np.outer(1 - ma, 1 - mb), copy=0) + m = make_mask(1 - np.outer(1 - ma, 1 - mb), copy=False) return masked_array(d, mask=m) outer.__doc__ = doc_note(np.outer.__doc__, "Masked values are replaced by 0.") @@ -7626,18 +7768,18 @@ def allequal(a, b, fill_value=True): Examples -------- - >>> a = ma.array([1e10, 1e-7, 42.0], mask=[0, 0, 1]) + >>> a = np.ma.array([1e10, 1e-7, 42.0], mask=[0, 0, 1]) >>> a - masked_array(data = [10000000000.0 1e-07 --], - mask = [False False True], - fill_value=1e+20) + masked_array(data=[10000000000.0, 1e-07, --], + mask=[False, False, True], + fill_value=1e+20) - >>> b = array([1e10, 1e-7, -42.0]) + >>> b = np.array([1e10, 1e-7, -42.0]) >>> b array([ 1.00000000e+10, 1.00000000e-07, -4.20000000e+01]) - >>> ma.allequal(a, b, fill_value=False) + >>> np.ma.allequal(a, b, fill_value=False) False - >>> ma.allequal(a, b) + >>> np.ma.allequal(a, b) True """ @@ -7703,29 +7845,29 @@ def allclose(a, b, masked_equal=True, rtol=1e-5, atol=1e-8): Examples -------- - >>> a = ma.array([1e10, 1e-7, 42.0], mask=[0, 0, 1]) + >>> a = np.ma.array([1e10, 1e-7, 42.0], mask=[0, 0, 1]) >>> a - masked_array(data = [10000000000.0 1e-07 --], - mask = [False False True], - fill_value = 1e+20) - >>> b = ma.array([1e10, 1e-8, -42.0], mask=[0, 0, 1]) - >>> ma.allclose(a, b) + masked_array(data=[10000000000.0, 1e-07, --], + mask=[False, False, True], + fill_value=1e+20) + >>> b = np.ma.array([1e10, 1e-8, -42.0], mask=[0, 0, 1]) + >>> np.ma.allclose(a, b) False - >>> a = ma.array([1e10, 1e-8, 42.0], mask=[0, 0, 1]) - >>> b = ma.array([1.00001e10, 1e-9, -42.0], mask=[0, 0, 1]) - >>> ma.allclose(a, b) + >>> a = np.ma.array([1e10, 1e-8, 42.0], mask=[0, 0, 1]) + >>> b = np.ma.array([1.00001e10, 1e-9, -42.0], mask=[0, 0, 1]) + >>> np.ma.allclose(a, b) True - >>> ma.allclose(a, b, masked_equal=False) + >>> np.ma.allclose(a, b, masked_equal=False) False Masked values are not compared directly. - >>> a = ma.array([1e10, 1e-8, 42.0], mask=[0, 0, 1]) - >>> b = ma.array([1.00001e10, 1e-9, 42.0], mask=[0, 0, 1]) - >>> ma.allclose(a, b) + >>> a = np.ma.array([1e10, 1e-8, 42.0], mask=[0, 0, 1]) + >>> b = np.ma.array([1.00001e10, 1e-9, 42.0], mask=[0, 0, 1]) + >>> np.ma.allclose(a, b) True - >>> ma.allclose(a, b, masked_equal=False) + >>> np.ma.allclose(a, b, masked_equal=False) False """ @@ -7734,9 +7876,14 @@ def allclose(a, b, masked_equal=True, rtol=1e-5, atol=1e-8): # make sure y is an inexact type to avoid abs(MIN_INT); will cause # casting of x later. - dtype = np.result_type(y, 1.) - if y.dtype != dtype: - y = masked_array(y, dtype=dtype, copy=False) + # NOTE: We explicitly allow timedelta, which used to work. This could + # possibly be deprecated. See also gh-18286. + # timedelta works if `atol` is an integer or also a timedelta. + # Although, the default tolerances are unlikely to be useful + if y.dtype.kind != "m": + dtype = np.result_type(y, 1.) + if y.dtype != dtype: + y = masked_array(y, dtype=dtype, copy=False) m = mask_or(getmask(x), getmask(y)) xinf = np.isinf(masked_array(x, copy=False, mask=m)).filled(False) @@ -7792,15 +7939,14 @@ def asarray(a, dtype=None, order=None): -------- >>> x = np.arange(10.).reshape(2, 5) >>> x - array([[ 0., 1., 2., 3., 4.], - [ 5., 6., 7., 8., 9.]]) + array([[0., 1., 2., 3., 4.], + [5., 6., 7., 8., 9.]]) >>> np.ma.asarray(x) - masked_array(data = - [[ 0. 1. 2. 3. 4.] - [ 5. 6. 7. 8. 9.]], - mask = - False, - fill_value = 1e+20) + masked_array( + data=[[0., 1., 2., 3., 4.], + [5., 6., 7., 8., 9.]], + mask=False, + fill_value=1e+20) >>> type(np.ma.asarray(x)) <class 'numpy.ma.core.MaskedArray'> @@ -7840,15 +7986,14 @@ def asanyarray(a, dtype=None): -------- >>> x = np.arange(10.).reshape(2, 5) >>> x - array([[ 0., 1., 2., 3., 4.], - [ 5., 6., 7., 8., 9.]]) + array([[0., 1., 2., 3., 4.], + [5., 6., 7., 8., 9.]]) >>> np.ma.asanyarray(x) - masked_array(data = - [[ 0. 1. 2. 3. 4.] - [ 5. 6. 7. 8. 9.]], - mask = - False, - fill_value = 1e+20) + masked_array( + data=[[0., 1., 2., 3., 4.], + [5., 6., 7., 8., 9.]], + mask=False, + fill_value=1e+20) >>> type(np.ma.asanyarray(x)) <class 'numpy.ma.core.MaskedArray'> @@ -7867,97 +8012,8 @@ def asanyarray(a, dtype=None): def _pickle_warn(method): # NumPy 1.15.0, 2017-12-10 warnings.warn( - "np.ma.{method} is deprecated, use pickle.{method} instead" - .format(method=method), - DeprecationWarning, - stacklevel=3) - - -def dump(a, F): - """ - Pickle a masked array to a file. - - This is a wrapper around ``cPickle.dump``. - - Parameters - ---------- - a : MaskedArray - The array to be pickled. - F : str or file-like object - The file to pickle `a` to. If a string, the full path to the file. - - """ - _pickle_warn('dump') - if not hasattr(F, 'readline'): - with open(F, 'w') as F: - pickle.dump(a, F) - else: - pickle.dump(a, F) - - -def dumps(a): - """ - Return a string corresponding to the pickling of a masked array. - - This is a wrapper around ``cPickle.dumps``. - - Parameters - ---------- - a : MaskedArray - The array for which the string representation of the pickle is - returned. - - """ - _pickle_warn('dumps') - return pickle.dumps(a) - - -def load(F): - """ - Wrapper around ``cPickle.load`` which accepts either a file-like object - or a filename. - - Parameters - ---------- - F : str or file - The file or file name to load. - - See Also - -------- - dump : Pickle an array - - Notes - ----- - This is different from `numpy.load`, which does not use cPickle but loads - the NumPy binary .npy format. - - """ - _pickle_warn('load') - if not hasattr(F, 'readline'): - with open(F, 'r') as F: - return pickle.load(F) - else: - return pickle.load(F) - - -def loads(strg): - """ - Load a pickle from the current string. - - The result of ``cPickle.loads(strg)`` is returned. - - Parameters - ---------- - strg : str - The string to load. - - See Also - -------- - dumps : Return a string corresponding to the pickling of a masked array. - - """ - _pickle_warn('loads') - return pickle.loads(strg) + f"np.ma.{method} is deprecated, use pickle.{method} instead", + DeprecationWarning, stacklevel=3) def fromfile(file, dtype=float, count=-1, sep=''): @@ -7992,45 +8048,44 @@ def fromflex(fxarray): >>> x = np.ma.array(np.arange(9).reshape(3, 3), mask=[0] + [1, 0] * 4) >>> rec = x.toflex() >>> rec - array([[(0, False), (1, True), (2, False)], - [(3, True), (4, False), (5, True)], - [(6, False), (7, True), (8, False)]], - dtype=[('_data', '<i4'), ('_mask', '|b1')]) + array([[(0, False), (1, True), (2, False)], + [(3, True), (4, False), (5, True)], + [(6, False), (7, True), (8, False)]], + dtype=[('_data', '<i8'), ('_mask', '?')]) >>> x2 = np.ma.fromflex(rec) >>> x2 - masked_array(data = - [[0 -- 2] - [-- 4 --] - [6 -- 8]], - mask = - [[False True False] - [ True False True] - [False True False]], - fill_value = 999999) + masked_array( + data=[[0, --, 2], + [--, 4, --], + [6, --, 8]], + mask=[[False, True, False], + [ True, False, True], + [False, True, False]], + fill_value=999999) Extra fields can be present in the structured array but are discarded: >>> dt = [('_data', '<i4'), ('_mask', '|b1'), ('field3', '<f4')] >>> rec2 = np.zeros((2, 2), dtype=dt) >>> rec2 - array([[(0, False, 0.0), (0, False, 0.0)], - [(0, False, 0.0), (0, False, 0.0)]], - dtype=[('_data', '<i4'), ('_mask', '|b1'), ('field3', '<f4')]) + array([[(0, False, 0.), (0, False, 0.)], + [(0, False, 0.), (0, False, 0.)]], + dtype=[('_data', '<i4'), ('_mask', '?'), ('field3', '<f4')]) >>> y = np.ma.fromflex(rec2) >>> y - masked_array(data = - [[0 0] - [0 0]], - mask = - [[False False] - [False False]], - fill_value = 999999) + masked_array( + data=[[0, 0], + [0, 0]], + mask=[[False, False], + [False, False]], + fill_value=999999, + dtype=int32) """ return masked_array(fxarray['_data'], mask=fxarray['_mask']) -class _convert2ma(object): +class _convert2ma: """ Convert functions from numpy to numpy.ma. @@ -8043,22 +8098,51 @@ class _convert2ma(object): """ __doc__ = None - def __init__(self, funcname, params=None): + def __init__(self, funcname, np_ret, np_ma_ret, params=None): self._func = getattr(np, funcname) - self.__doc__ = self.getdoc() + self.__doc__ = self.getdoc(np_ret, np_ma_ret) self._extras = params or {} - def getdoc(self): + def getdoc(self, np_ret, np_ma_ret): "Return the doc of the function (from the doc of the method)." doc = getattr(self._func, '__doc__', None) sig = get_object_signature(self._func) if doc: + doc = self._replace_return_type(doc, np_ret, np_ma_ret) # Add the signature of the function at the beginning of the doc if sig: sig = "%s%s\n" % (self._func.__name__, sig) doc = sig + doc return doc + def _replace_return_type(self, doc, np_ret, np_ma_ret): + """ + Replace documentation of ``np`` function's return type. + + Replaces it with the proper type for the ``np.ma`` function. + + Parameters + ---------- + doc : str + The documentation of the ``np`` method. + np_ret : str + The return type string of the ``np`` method that we want to + replace. (e.g. "out : ndarray") + np_ma_ret : str + The return type string of the ``np.ma`` method. + (e.g. "out : MaskedArray") + """ + if np_ret not in doc: + raise RuntimeError( + f"Failed to replace `{np_ret}` with `{np_ma_ret}`. " + f"The documentation string for return type, {np_ret}, is not " + f"found in the docstring for `np.{self._func.__name__}`. " + f"Fix the docstring for `np.{self._func.__name__}` or " + "update the expected string for return type." + ) + + return doc.replace(np_ret, np_ma_ret) + def __call__(self, *args, **params): # Find the common parameters to the call and the definition _extras = self._extras @@ -8074,21 +8158,86 @@ def __call__(self, *args, **params): result._hardmask = bool(_extras.get("hard_mask", False)) return result -arange = _convert2ma('arange', params=dict(fill_value=None, hardmask=False)) -clip = np.clip -diff = np.diff -empty = _convert2ma('empty', params=dict(fill_value=None, hardmask=False)) -empty_like = _convert2ma('empty_like') -frombuffer = _convert2ma('frombuffer') -fromfunction = _convert2ma('fromfunction') + +arange = _convert2ma( + 'arange', + params=dict(fill_value=None, hardmask=False), + np_ret='arange : ndarray', + np_ma_ret='arange : MaskedArray', +) +clip = _convert2ma( + 'clip', + params=dict(fill_value=None, hardmask=False), + np_ret='clipped_array : ndarray', + np_ma_ret='clipped_array : MaskedArray', +) +diff = _convert2ma( + 'diff', + params=dict(fill_value=None, hardmask=False), + np_ret='diff : ndarray', + np_ma_ret='diff : MaskedArray', +) +empty = _convert2ma( + 'empty', + params=dict(fill_value=None, hardmask=False), + np_ret='out : ndarray', + np_ma_ret='out : MaskedArray', +) +empty_like = _convert2ma( + 'empty_like', + np_ret='out : ndarray', + np_ma_ret='out : MaskedArray', +) +frombuffer = _convert2ma( + 'frombuffer', + np_ret='out : ndarray', + np_ma_ret='out: MaskedArray', +) +fromfunction = _convert2ma( + 'fromfunction', + np_ret='fromfunction : any', + np_ma_ret='fromfunction: MaskedArray', +) identity = _convert2ma( - 'identity', params=dict(fill_value=None, hardmask=False)) -indices = np.indices -ones = _convert2ma('ones', params=dict(fill_value=None, hardmask=False)) -ones_like = np.ones_like -squeeze = np.squeeze -zeros = _convert2ma('zeros', params=dict(fill_value=None, hardmask=False)) -zeros_like = np.zeros_like + 'identity', + params=dict(fill_value=None, hardmask=False), + np_ret='out : ndarray', + np_ma_ret='out : MaskedArray', +) +indices = _convert2ma( + 'indices', + params=dict(fill_value=None, hardmask=False), + np_ret='grid : one ndarray or tuple of ndarrays', + np_ma_ret='grid : one MaskedArray or tuple of MaskedArrays', +) +ones = _convert2ma( + 'ones', + params=dict(fill_value=None, hardmask=False), + np_ret='out : ndarray', + np_ma_ret='out : MaskedArray', +) +ones_like = _convert2ma( + 'ones_like', + np_ret='out : ndarray', + np_ma_ret='out : MaskedArray', +) +squeeze = _convert2ma( + 'squeeze', + params=dict(fill_value=None, hardmask=False), + np_ret='squeezed : ndarray', + np_ma_ret='squeezed : MaskedArray', +) +zeros = _convert2ma( + 'zeros', + params=dict(fill_value=None, hardmask=False), + np_ret='out : ndarray', + np_ma_ret='out : MaskedArray', +) +zeros_like = _convert2ma( + 'zeros_like', + np_ret='out : ndarray', + np_ma_ret='out : MaskedArray', +) def append(a, b, axis=None): @@ -8125,7 +8274,10 @@ def append(a, b, axis=None): >>> import numpy.ma as ma >>> a = ma.masked_values([1, 2, 3], 2) >>> b = ma.masked_values([[4, 5, 6], [7, 8, 9]], 7) - >>> print(ma.append(a, b)) - [1 -- 3 4 5 6 -- 8 9] + >>> ma.append(a, b) + masked_array(data=[1, --, 3, 4, 5, 6, --, 8, 9], + mask=[False, True, False, False, False, False, True, False, + False], + fill_value=999999) """ return concatenate([a, b], axis) diff --git a/numpy/ma/core.pyi b/numpy/ma/core.pyi new file mode 100644 index 000000000000..bc1f45a8d5ad --- /dev/null +++ b/numpy/ma/core.pyi @@ -0,0 +1,468 @@ +from typing import Any, List, TypeVar, Callable +from numpy import ndarray, dtype, float64 + +from numpy import ( + amax as amax, + amin as amin, + bool_ as bool_, + expand_dims as expand_dims, + diff as diff, + clip as clip, + indices as indices, + ones_like as ones_like, + squeeze as squeeze, + zeros_like as zeros_like, +) + +from numpy.lib.function_base import ( + angle as angle, +) + +# TODO: Set the `bound` to something more suitable once we +# have proper shape support +_ShapeType = TypeVar("_ShapeType", bound=Any) +_DType_co = TypeVar("_DType_co", bound=dtype[Any], covariant=True) + +__all__: List[str] + +MaskType = bool_ +nomask: bool_ + +class MaskedArrayFutureWarning(FutureWarning): ... +class MAError(Exception): ... +class MaskError(MAError): ... + +def default_fill_value(obj): ... +def minimum_fill_value(obj): ... +def maximum_fill_value(obj): ... +def set_fill_value(a, fill_value): ... +def common_fill_value(a, b): ... +def filled(a, fill_value=...): ... +def getdata(a, subok=...): ... +get_data = getdata + +def fix_invalid(a, mask=..., copy=..., fill_value=...): ... + +class _MaskedUFunc: + f: Any + __doc__: Any + __name__: Any + def __init__(self, ufunc): ... + +class _MaskedUnaryOperation(_MaskedUFunc): + fill: Any + domain: Any + def __init__(self, mufunc, fill=..., domain=...): ... + def __call__(self, a, *args, **kwargs): ... + +class _MaskedBinaryOperation(_MaskedUFunc): + fillx: Any + filly: Any + def __init__(self, mbfunc, fillx=..., filly=...): ... + def __call__(self, a, b, *args, **kwargs): ... + def reduce(self, target, axis=..., dtype=...): ... + def outer(self, a, b): ... + def accumulate(self, target, axis=...): ... + +class _DomainedBinaryOperation(_MaskedUFunc): + domain: Any + fillx: Any + filly: Any + def __init__(self, dbfunc, domain, fillx=..., filly=...): ... + def __call__(self, a, b, *args, **kwargs): ... + +exp: _MaskedUnaryOperation +conjugate: _MaskedUnaryOperation +sin: _MaskedUnaryOperation +cos: _MaskedUnaryOperation +arctan: _MaskedUnaryOperation +arcsinh: _MaskedUnaryOperation +sinh: _MaskedUnaryOperation +cosh: _MaskedUnaryOperation +tanh: _MaskedUnaryOperation +abs: _MaskedUnaryOperation +absolute: _MaskedUnaryOperation +fabs: _MaskedUnaryOperation +negative: _MaskedUnaryOperation +floor: _MaskedUnaryOperation +ceil: _MaskedUnaryOperation +around: _MaskedUnaryOperation +logical_not: _MaskedUnaryOperation +sqrt: _MaskedUnaryOperation +log: _MaskedUnaryOperation +log2: _MaskedUnaryOperation +log10: _MaskedUnaryOperation +tan: _MaskedUnaryOperation +arcsin: _MaskedUnaryOperation +arccos: _MaskedUnaryOperation +arccosh: _MaskedUnaryOperation +arctanh: _MaskedUnaryOperation + +add: _MaskedBinaryOperation +subtract: _MaskedBinaryOperation +multiply: _MaskedBinaryOperation +arctan2: _MaskedBinaryOperation +equal: _MaskedBinaryOperation +not_equal: _MaskedBinaryOperation +less_equal: _MaskedBinaryOperation +greater_equal: _MaskedBinaryOperation +less: _MaskedBinaryOperation +greater: _MaskedBinaryOperation +logical_and: _MaskedBinaryOperation +alltrue: _MaskedBinaryOperation +logical_or: _MaskedBinaryOperation +sometrue: Callable[..., Any] +logical_xor: _MaskedBinaryOperation +bitwise_and: _MaskedBinaryOperation +bitwise_or: _MaskedBinaryOperation +bitwise_xor: _MaskedBinaryOperation +hypot: _MaskedBinaryOperation +divide: _MaskedBinaryOperation +true_divide: _MaskedBinaryOperation +floor_divide: _MaskedBinaryOperation +remainder: _MaskedBinaryOperation +fmod: _MaskedBinaryOperation +mod: _MaskedBinaryOperation + +def make_mask_descr(ndtype): ... +def getmask(a): ... +get_mask = getmask + +def getmaskarray(arr): ... +def is_mask(m): ... +def make_mask(m, copy=..., shrink=..., dtype=...): ... +def make_mask_none(newshape, dtype=...): ... +def mask_or(m1, m2, copy=..., shrink=...): ... +def flatten_mask(mask): ... +def masked_where(condition, a, copy=...): ... +def masked_greater(x, value, copy=...): ... +def masked_greater_equal(x, value, copy=...): ... +def masked_less(x, value, copy=...): ... +def masked_less_equal(x, value, copy=...): ... +def masked_not_equal(x, value, copy=...): ... +def masked_equal(x, value, copy=...): ... +def masked_inside(x, v1, v2, copy=...): ... +def masked_outside(x, v1, v2, copy=...): ... +def masked_object(x, value, copy=..., shrink=...): ... +def masked_values(x, value, rtol=..., atol=..., copy=..., shrink=...): ... +def masked_invalid(a, copy=...): ... + +class _MaskedPrintOption: + def __init__(self, display): ... + def display(self): ... + def set_display(self, s): ... + def enabled(self): ... + def enable(self, shrink=...): ... + +masked_print_option: _MaskedPrintOption + +def flatten_structured_array(a): ... + +class MaskedIterator: + ma: Any + dataiter: Any + maskiter: Any + def __init__(self, ma): ... + def __iter__(self): ... + def __getitem__(self, indx): ... + def __setitem__(self, index, value): ... + def __next__(self): ... + +class MaskedArray(ndarray[_ShapeType, _DType_co]): + __array_priority__: Any + def __new__(cls, data=..., mask=..., dtype=..., copy=..., subok=..., ndmin=..., fill_value=..., keep_mask=..., hard_mask=..., shrink=..., order=...): ... + def __array_finalize__(self, obj): ... + def __array_wrap__(self, obj, context=...): ... + def view(self, dtype=..., type=..., fill_value=...): ... + def __getitem__(self, indx): ... + def __setitem__(self, indx, value): ... + @property + def dtype(self): ... + @dtype.setter + def dtype(self, dtype): ... + @property + def shape(self): ... + @shape.setter + def shape(self, shape): ... + def __setmask__(self, mask, copy=...): ... + @property + def mask(self): ... + @mask.setter + def mask(self, value): ... + @property + def recordmask(self): ... + @recordmask.setter + def recordmask(self, mask): ... + def harden_mask(self): ... + def soften_mask(self): ... + @property + def hardmask(self): ... + def unshare_mask(self): ... + @property + def sharedmask(self): ... + def shrink_mask(self): ... + @property + def baseclass(self): ... + data: Any + @property + def flat(self): ... + @flat.setter + def flat(self, value): ... + @property + def fill_value(self): ... + @fill_value.setter + def fill_value(self, value=...): ... + get_fill_value: Any + set_fill_value: Any + def filled(self, fill_value=...): ... + def compressed(self): ... + def compress(self, condition, axis=..., out=...): ... + def __eq__(self, other): ... + def __ne__(self, other): ... + def __add__(self, other): ... + def __radd__(self, other): ... + def __sub__(self, other): ... + def __rsub__(self, other): ... + def __mul__(self, other): ... + def __rmul__(self, other): ... + def __div__(self, other): ... + def __truediv__(self, other): ... + def __rtruediv__(self, other): ... + def __floordiv__(self, other): ... + def __rfloordiv__(self, other): ... + def __pow__(self, other): ... + def __rpow__(self, other): ... + def __iadd__(self, other): ... + def __isub__(self, other): ... + def __imul__(self, other): ... + def __idiv__(self, other): ... + def __ifloordiv__(self, other): ... + def __itruediv__(self, other): ... + def __ipow__(self, other): ... + def __float__(self): ... + def __int__(self): ... + @property # type: ignore[misc] + def imag(self): ... + get_imag: Any + @property # type: ignore[misc] + def real(self): ... + get_real: Any + def count(self, axis=..., keepdims=...): ... + def ravel(self, order=...): ... + def reshape(self, *s, **kwargs): ... + def resize(self, newshape, refcheck=..., order=...): ... + def put(self, indices, values, mode=...): ... + def ids(self): ... + def iscontiguous(self): ... + def all(self, axis=..., out=..., keepdims=...): ... + def any(self, axis=..., out=..., keepdims=...): ... + def nonzero(self): ... + def trace(self, offset=..., axis1=..., axis2=..., dtype=..., out=...): ... + def dot(self, b, out=..., strict=...): ... + def sum(self, axis=..., dtype=..., out=..., keepdims=...): ... + def cumsum(self, axis=..., dtype=..., out=...): ... + def prod(self, axis=..., dtype=..., out=..., keepdims=...): ... + product: Any + def cumprod(self, axis=..., dtype=..., out=...): ... + def mean(self, axis=..., dtype=..., out=..., keepdims=...): ... + def anom(self, axis=..., dtype=...): ... + def var(self, axis=..., dtype=..., out=..., ddof=..., keepdims=...): ... + def std(self, axis=..., dtype=..., out=..., ddof=..., keepdims=...): ... + def round(self, decimals=..., out=...): ... + def argsort(self, axis=..., kind=..., order=..., endwith=..., fill_value=...): ... + def argmin(self, axis=..., fill_value=..., out=..., *, keepdims=...): ... + def argmax(self, axis=..., fill_value=..., out=..., *, keepdims=...): ... + def sort(self, axis=..., kind=..., order=..., endwith=..., fill_value=...): ... + def min(self, axis=..., out=..., fill_value=..., keepdims=...): ... + # NOTE: deprecated + # def mini(self, axis=...): ... + # def tostring(self, fill_value=..., order=...): ... + def max(self, axis=..., out=..., fill_value=..., keepdims=...): ... + def ptp(self, axis=..., out=..., fill_value=..., keepdims=...): ... + def partition(self, *args, **kwargs): ... + def argpartition(self, *args, **kwargs): ... + def take(self, indices, axis=..., out=..., mode=...): ... + copy: Any + diagonal: Any + flatten: Any + repeat: Any + squeeze: Any + swapaxes: Any + T: Any + transpose: Any + def tolist(self, fill_value=...): ... + def tobytes(self, fill_value=..., order=...): ... + def tofile(self, fid, sep=..., format=...): ... + def toflex(self): ... + torecords: Any + def __reduce__(self): ... + def __deepcopy__(self, memo=...): ... + +class mvoid(MaskedArray[_ShapeType, _DType_co]): + def __new__( + self, + data, + mask=..., + dtype=..., + fill_value=..., + hardmask=..., + copy=..., + subok=..., + ): ... + def __getitem__(self, indx): ... + def __setitem__(self, indx, value): ... + def __iter__(self): ... + def __len__(self): ... + def filled(self, fill_value=...): ... + def tolist(self): ... + +def isMaskedArray(x): ... +isarray = isMaskedArray +isMA = isMaskedArray + +# 0D float64 array +class MaskedConstant(MaskedArray[Any, dtype[float64]]): + def __new__(cls): ... + __class__: Any + def __array_finalize__(self, obj): ... + def __array_prepare__(self, obj, context=...): ... + def __array_wrap__(self, obj, context=...): ... + def __format__(self, format_spec): ... + def __reduce__(self): ... + def __iop__(self, other): ... + __iadd__: Any + __isub__: Any + __imul__: Any + __ifloordiv__: Any + __itruediv__: Any + __ipow__: Any + def copy(self, *args, **kwargs): ... + def __copy__(self): ... + def __deepcopy__(self, memo): ... + def __setattr__(self, attr, value): ... + +masked: MaskedConstant +masked_singleton: MaskedConstant +masked_array = MaskedArray + +def array( + data, + dtype=..., + copy=..., + order=..., + mask=..., + fill_value=..., + keep_mask=..., + hard_mask=..., + shrink=..., + subok=..., + ndmin=..., +): ... +def is_masked(x): ... + +class _extrema_operation(_MaskedUFunc): + compare: Any + fill_value_func: Any + def __init__(self, ufunc, compare, fill_value): ... + # NOTE: in practice `b` has a default value, but users should + # explicitly provide a value here as the default is deprecated + def __call__(self, a, b): ... + def reduce(self, target, axis=...): ... + def outer(self, a, b): ... + +def min(obj, axis=..., out=..., fill_value=..., keepdims=...): ... +def max(obj, axis=..., out=..., fill_value=..., keepdims=...): ... +def ptp(obj, axis=..., out=..., fill_value=..., keepdims=...): ... + +class _frommethod: + __name__: Any + __doc__: Any + reversed: Any + def __init__(self, methodname, reversed=...): ... + def getdoc(self): ... + def __call__(self, a, *args, **params): ... + +all: _frommethod +anomalies: _frommethod +anom: _frommethod +any: _frommethod +compress: _frommethod +cumprod: _frommethod +cumsum: _frommethod +copy: _frommethod +diagonal: _frommethod +harden_mask: _frommethod +ids: _frommethod +mean: _frommethod +nonzero: _frommethod +prod: _frommethod +product: _frommethod +ravel: _frommethod +repeat: _frommethod +soften_mask: _frommethod +std: _frommethod +sum: _frommethod +swapaxes: _frommethod +trace: _frommethod +var: _frommethod +count: _frommethod +argmin: _frommethod +argmax: _frommethod + +minimum: _extrema_operation +maximum: _extrema_operation + +def take(a, indices, axis=..., out=..., mode=...): ... +def power(a, b, third=...): ... +def argsort(a, axis=..., kind=..., order=..., endwith=..., fill_value=...): ... +def sort(a, axis=..., kind=..., order=..., endwith=..., fill_value=...): ... +def compressed(x): ... +def concatenate(arrays, axis=...): ... +def diag(v, k=...): ... +def left_shift(a, n): ... +def right_shift(a, n): ... +def put(a, indices, values, mode=...): ... +def putmask(a, mask, values): ... +def transpose(a, axes=...): ... +def reshape(a, new_shape, order=...): ... +def resize(x, new_shape): ... +def ndim(obj): ... +def shape(obj): ... +def size(obj, axis=...): ... +def where(condition, x=..., y=...): ... +def choose(indices, choices, out=..., mode=...): ... +def round_(a, decimals=..., out=...): ... +round = round_ + +def inner(a, b): ... +innerproduct = inner + +def outer(a, b): ... +outerproduct = outer + +def correlate(a, v, mode=..., propagate_mask=...): ... +def convolve(a, v, mode=..., propagate_mask=...): ... +def allequal(a, b, fill_value=...): ... +def allclose(a, b, masked_equal=..., rtol=..., atol=...): ... +def asarray(a, dtype=..., order=...): ... +def asanyarray(a, dtype=...): ... +def fromflex(fxarray): ... + +class _convert2ma: + __doc__: Any + def __init__(self, funcname, params=...): ... + def getdoc(self): ... + def __call__(self, *args, **params): ... + +arange: _convert2ma +empty: _convert2ma +empty_like: _convert2ma +frombuffer: _convert2ma +fromfunction: _convert2ma +identity: _convert2ma +ones: _convert2ma +zeros: _convert2ma + +def append(a, b, axis=...): ... +def dot(a, b, strict=..., out=...): ... +def mask_rowcols(a, axis=...): ... diff --git a/numpy/ma/extras.py b/numpy/ma/extras.py index 3be4d3625b58..38bf1f0e8395 100644 --- a/numpy/ma/extras.py +++ b/numpy/ma/extras.py @@ -8,8 +8,6 @@ :version: $Id: extras.py 3473 2007-10-29 15:18:13Z jarrod.millman $ """ -from __future__ import division, absolute_import, print_function - __all__ = [ 'apply_along_axis', 'apply_over_axes', 'atleast_1d', 'atleast_2d', 'atleast_3d', 'average', 'clump_masked', 'clump_unmasked', @@ -35,7 +33,6 @@ import numpy as np from numpy import ndarray, array as nxarray -import numpy.core.umath as umath from numpy.core.multiarray import normalize_axis_index from numpy.core.numeric import normalize_axis_tuple from numpy.lib.function_base import _ureduce @@ -81,15 +78,14 @@ def count_masked(arr, axis=None): >>> a[1, 2] = ma.masked >>> a[2, 1] = ma.masked >>> a - masked_array(data = - [[0 1 2] - [-- 4 --] - [6 -- 8]], - mask = - [[False False False] - [ True False True] - [False True False]], - fill_value=999999) + masked_array( + data=[[0, 1, 2], + [--, 4, --], + [6, --, 8]], + mask=[[False, False, False], + [ True, False, True], + [False, True, False]], + fill_value=999999) >>> ma.count_masked(a) 3 @@ -132,15 +128,15 @@ def masked_all(shape, dtype=float): -------- >>> import numpy.ma as ma >>> ma.masked_all((3, 3)) - masked_array(data = - [[-- -- --] - [-- -- --] - [-- -- --]], - mask = - [[ True True True] - [ True True True] - [ True True True]], - fill_value=1e+20) + masked_array( + data=[[--, --, --], + [--, --, --], + [--, --, --]], + mask=[[ True, True, True], + [ True, True, True], + [ True, True, True]], + fill_value=1e+20, + dtype=float64) The `dtype` parameter defines the underlying data type. @@ -188,16 +184,16 @@ def masked_all_like(arr): >>> import numpy.ma as ma >>> arr = np.zeros((2, 3), dtype=np.float32) >>> arr - array([[ 0., 0., 0.], - [ 0., 0., 0.]], dtype=float32) + array([[0., 0., 0.], + [0., 0., 0.]], dtype=float32) >>> ma.masked_all_like(arr) - masked_array(data = - [[-- -- --] - [-- -- --]], - mask = - [[ True True True] - [ True True True]], - fill_value=1e+20) + masked_array( + data=[[--, --, --], + [--, --, --]], + mask=[[ True, True, True], + [ True, True, True]], + fill_value=1e+20, + dtype=float32) The dtype of the masked array matches the dtype of `arr`. @@ -215,7 +211,7 @@ def masked_all_like(arr): #####-------------------------------------------------------------------------- #---- --- Standard functions --- #####-------------------------------------------------------------------------- -class _fromnxfunction(object): +class _fromnxfunction: """ Defines a wrapper to adapt NumPy functions to masked arrays. @@ -247,11 +243,6 @@ def getdoc(self): the new masked array version of the function. A note on application of the function to the mask is appended. - .. warning:: - If the function docstring already contained a Notes section, the - new docstring will have two Notes sections instead of appending a note - to the existing section. - Parameters ---------- None @@ -261,9 +252,9 @@ def getdoc(self): doc = getattr(npfunc, '__doc__', None) if doc: sig = self.__name__ + ma.get_object_signature(npfunc) - locdoc = "Notes\n-----\nThe function is applied to both the _data"\ - " and the _mask, if any." - return '\n'.join((sig, doc, locdoc)) + doc = ma.doc_note(doc, "The function is applied to both the _data " + "and the _mask, if any.") + return '\n\n'.join((sig, doc)) return def __call__(self, *args, **params): @@ -391,7 +382,6 @@ def apply_along_axis(func1d, axis, arr, *args, **kwargs): i[axis] = slice(None, None) outshape = np.asarray(arr.shape).take(indlist) i.put(indlist, ind) - j = i.copy() res = func1d(arr[tuple(i.tolist())], *args, **kwargs) # if res is a number, then we have a smaller output array asscalar = np.isscalar(res) @@ -492,28 +482,45 @@ def apply_over_axes(func, a, axes): Examples -------- - >>> a = ma.arange(24).reshape(2,3,4) - >>> a[:,0,1] = ma.masked - >>> a[:,1,:] = ma.masked - >>> print(a) - [[[0 -- 2 3] - [-- -- -- --] - [8 9 10 11]] - - [[12 -- 14 15] - [-- -- -- --] - [20 21 22 23]]] - >>> print(ma.apply_over_axes(ma.sum, a, [0,2])) - [[[46] - [--] - [124]]] + >>> a = np.ma.arange(24).reshape(2,3,4) + >>> a[:,0,1] = np.ma.masked + >>> a[:,1,:] = np.ma.masked + >>> a + masked_array( + data=[[[0, --, 2, 3], + [--, --, --, --], + [8, 9, 10, 11]], + [[12, --, 14, 15], + [--, --, --, --], + [20, 21, 22, 23]]], + mask=[[[False, True, False, False], + [ True, True, True, True], + [False, False, False, False]], + [[False, True, False, False], + [ True, True, True, True], + [False, False, False, False]]], + fill_value=999999) + >>> np.ma.apply_over_axes(np.ma.sum, a, [0,2]) + masked_array( + data=[[[46], + [--], + [124]]], + mask=[[[False], + [ True], + [False]]], + fill_value=999999) Tuple axis arguments to ufuncs are equivalent: - >>> print(ma.sum(a, axis=(0,2)).reshape((1,-1,1))) - [[[46] - [--] - [124]]] + >>> np.ma.sum(a, axis=(0,2)).reshape((1,-1,1)) + masked_array( + data=[[[46], + [--], + [124]]], + mask=[[[False], + [ True], + [False]]], + fill_value=999999) """ @@ -527,15 +534,18 @@ def average(a, axis=None, weights=None, returned=False): Data to be averaged. Masked entries are not taken into account in the computation. axis : int, optional - Axis along which to average `a`. If `None`, averaging is done over + Axis along which to average `a`. If None, averaging is done over the flattened array. weights : array_like, optional The importance that each element has in the computation of the average. The weights array can either be 1-D (in which case its length must be the size of `a` along the given axis) or of the same shape as `a`. If ``weights=None``, then all data in `a` are assumed to have a - weight equal to one. If `weights` is complex, the imaginary parts - are ignored. + weight equal to one. The 1-D calculation is:: + + avg = sum(a * weights) / sum(weights) + + The only constraint on `weights` is that `sum(weights)` must not be 0. returned : bool, optional Flag indicating whether a tuple ``(result, sum of weights)`` should be returned as output (True), or just the result (False). @@ -558,14 +568,19 @@ def average(a, axis=None, weights=None, returned=False): 1.25 >>> x = np.ma.arange(6.).reshape(3, 2) - >>> print(x) - [[ 0. 1.] - [ 2. 3.] - [ 4. 5.]] + >>> x + masked_array( + data=[[0., 1.], + [2., 3.], + [4., 5.]], + mask=False, + fill_value=1e+20) >>> avg, sumweights = np.ma.average(x, axis=0, weights=[1, 2, 3], ... returned=True) - >>> print(avg) - [2.66666666667 3.66666666667] + >>> avg + masked_array(data=[2.6666666666666665, 3.6666666666666665], + mask=[False, False], + fill_value=1e+20) """ a = asarray(a) @@ -598,7 +613,7 @@ def average(a, axis=None, weights=None, returned=False): "Length of weights not compatible with specified axis.") # setup wgt to broadcast along axis - wgt = np.broadcast_to(wgt, (a.ndim-1)*(1,) + wgt.shape) + wgt = np.broadcast_to(wgt, (a.ndim-1)*(1,) + wgt.shape, subok=True) wgt = wgt.swapaxes(-1, axis) if m is not nomask: @@ -676,9 +691,9 @@ def median(a, axis=None, out=None, overwrite_input=False, keepdims=False): >>> np.ma.median(x) 2.5 >>> np.ma.median(x, axis=-1, overwrite_input=True) - masked_array(data = [ 2. 5.], - mask = False, - fill_value = 1e+20) + masked_array(data=[2.0, 5.0], + mask=[False, False], + fill_value=1e+20) """ if not hasattr(a, 'mask'): @@ -728,7 +743,6 @@ def _median(a, axis=None, out=None, overwrite_input=False): return np.ma.mean(asorted[indexer], axis=axis, out=out) if asorted.ndim == 1: - counts = count(asorted) idx, odd = divmod(count(asorted), 2) mid = asorted[idx + odd - 1:idx + 1] if np.issubdtype(asorted.dtype, np.inexact) and asorted.size > 0: @@ -736,7 +750,7 @@ def _median(a, axis=None, out=None, overwrite_input=False): s = mid.sum(out=out) if not odd: s = np.true_divide(s, 2., casting='safe', out=out) - s = np.lib.utils._median_nancheck(asorted, s, axis, out) + s = np.lib.utils._median_nancheck(asorted, s, axis) else: s = mid.mean(out=out) @@ -776,7 +790,7 @@ def replace_masked(s): s = np.ma.sum(low_high, axis=axis, out=out) np.true_divide(s.data, 2., casting='unsafe', out=s.data) - s = np.lib.utils._median_nancheck(asorted, s, axis, out) + s = np.lib.utils._median_nancheck(asorted, s, axis) else: s = np.ma.mean(low_high, axis=axis, out=out) @@ -790,7 +804,7 @@ def compress_nd(x, axis=None): ---------- x : array_like, MaskedArray The array to operate on. If not a MaskedArray instance (or if no array - elements are masked, `x` is interpreted as a MaskedArray with `mask` + elements are masked), `x` is interpreted as a MaskedArray with `mask` set to `nomask`. axis : tuple of ints or int, optional Which dimensions to suppress slices from can be configured with this @@ -856,15 +870,14 @@ def compress_rowcols(x, axis=None): ... [1, 0, 0], ... [0, 0, 0]]) >>> x - masked_array(data = - [[-- 1 2] - [-- 4 5] - [6 7 8]], - mask = - [[ True False False] - [ True False False] - [False False False]], - fill_value = 999999) + masked_array( + data=[[--, 1, 2], + [--, 4, 5], + [6, 7, 8]], + mask=[[ True, False, False], + [ True, False, False], + [False, False, False]], + fill_value=999999) >>> np.ma.compress_rowcols(x) array([[7, 8]]) @@ -886,11 +899,11 @@ def compress_rows(a): Suppress whole rows of a 2-D array that contain masked values. This is equivalent to ``np.ma.compress_rowcols(a, 0)``, see - `extras.compress_rowcols` for details. + `compress_rowcols` for details. See Also -------- - extras.compress_rowcols + compress_rowcols """ a = asarray(a) @@ -903,11 +916,11 @@ def compress_cols(a): Suppress whole columns of a 2-D array that contain masked values. This is equivalent to ``np.ma.compress_rowcols(a, 1)``, see - `extras.compress_rowcols` for details. + `compress_rowcols` for details. See Also -------- - extras.compress_rowcols + compress_rowcols """ a = asarray(a) @@ -915,7 +928,7 @@ def compress_cols(a): raise NotImplementedError("compress_cols works for 2D arrays only.") return compress_rowcols(a, 1) -def mask_rows(a, axis=None): +def mask_rows(a, axis=np._NoValue): """ Mask rows of a 2D array that contain masked values. @@ -937,30 +950,35 @@ def mask_rows(a, axis=None): [0, 0, 0]]) >>> a = ma.masked_equal(a, 1) >>> a - masked_array(data = - [[0 0 0] - [0 -- 0] - [0 0 0]], - mask = - [[False False False] - [False True False] - [False False False]], - fill_value=999999) - >>> ma.mask_rows(a) - masked_array(data = - [[0 0 0] - [-- -- --] - [0 0 0]], - mask = - [[False False False] - [ True True True] - [False False False]], - fill_value=999999) + masked_array( + data=[[0, 0, 0], + [0, --, 0], + [0, 0, 0]], + mask=[[False, False, False], + [False, True, False], + [False, False, False]], + fill_value=1) - """ + >>> ma.mask_rows(a) + masked_array( + data=[[0, 0, 0], + [--, --, --], + [0, 0, 0]], + mask=[[False, False, False], + [ True, True, True], + [False, False, False]], + fill_value=1) + + """ + if axis is not np._NoValue: + # remove the axis argument when this deprecation expires + # NumPy 1.18.0, 2019-11-28 + warnings.warn( + "The axis argument has always been ignored, in future passing it " + "will raise TypeError", DeprecationWarning, stacklevel=2) return mask_rowcols(a, 0) -def mask_cols(a, axis=None): +def mask_cols(a, axis=np._NoValue): """ Mask columns of a 2D array that contain masked values. @@ -982,27 +1000,31 @@ def mask_cols(a, axis=None): [0, 0, 0]]) >>> a = ma.masked_equal(a, 1) >>> a - masked_array(data = - [[0 0 0] - [0 -- 0] - [0 0 0]], - mask = - [[False False False] - [False True False] - [False False False]], - fill_value=999999) + masked_array( + data=[[0, 0, 0], + [0, --, 0], + [0, 0, 0]], + mask=[[False, False, False], + [False, True, False], + [False, False, False]], + fill_value=1) >>> ma.mask_cols(a) - masked_array(data = - [[0 -- 0] - [0 -- 0] - [0 -- 0]], - mask = - [[False True False] - [False True False] - [False True False]], - fill_value=999999) - - """ + masked_array( + data=[[0, --, 0], + [0, --, 0], + [0, --, 0]], + mask=[[False, True, False], + [False, True, False], + [False, True, False]], + fill_value=1) + + """ + if axis is not np._NoValue: + # remove the axis argument when this deprecation expires + # NumPy 1.18.0, 2019-11-28 + warnings.warn( + "The axis argument has always been ignored, in future passing it " + "will raise TypeError", DeprecationWarning, stacklevel=2) return mask_rowcols(a, 1) @@ -1078,12 +1100,12 @@ def intersect1d(ar1, ar2, assume_unique=False): Examples -------- - >>> x = array([1, 3, 3, 3], mask=[0, 0, 0, 1]) - >>> y = array([3, 1, 1, 1], mask=[0, 0, 0, 1]) - >>> intersect1d(x, y) - masked_array(data = [1 3 --], - mask = [False False True], - fill_value = 999999) + >>> x = np.ma.array([1, 3, 3, 3], mask=[0, 0, 0, 1]) + >>> y = np.ma.array([3, 1, 1, 1], mask=[0, 0, 0, 1]) + >>> np.ma.intersect1d(x, y) + masked_array(data=[1, 3, --], + mask=[False, False, True], + fill_value=999999) """ if assume_unique: @@ -1193,7 +1215,7 @@ def union1d(ar1, ar2): The output is always a masked array. See `numpy.union1d` for more details. - See also + See Also -------- numpy.union1d : Equivalent function for ndarrays. @@ -1216,9 +1238,9 @@ def setdiff1d(ar1, ar2, assume_unique=False): -------- >>> x = np.ma.array([1, 2, 3, 4], mask=[0, 1, 0, 1]) >>> np.ma.setdiff1d(x, [1, 2]) - masked_array(data = [3 --], - mask = [False True], - fill_value = 999999) + masked_array(data=[3, --], + mask=[False, True], + fill_value=999999) """ if assume_unique: @@ -1298,7 +1320,7 @@ def cov(x, y=None, rowvar=True, bias=False, allow_masked=True, ddof=None): observation of all those variables. Also see `rowvar` below. y : array_like, optional An additional set of variables and observations. `y` has the same - form as `x`. + shape as `x`. rowvar : bool, optional If `rowvar` is True (default), then each row represents a variable, with observations in the columns. Otherwise, the relationship @@ -1459,7 +1481,7 @@ def makemat(cls, arr): # deprecate that class. In preparation, we use the unmasked version # to construct the matrix (with copy=False for backwards compatibility # with the .view) - data = super(MAxisConcatenator, cls).makemat(arr.data, copy=False) + data = super().makemat(arr.data, copy=False) return array(data, mask=arr.mask) def __getitem__(self, key): @@ -1467,7 +1489,7 @@ def __getitem__(self, key): if isinstance(key, str): raise MAError("Unavailable for masked array.") - return super(MAxisConcatenator, self).__getitem__(key) + return super().__getitem__(key) class mr_class(MAxisConcatenator): @@ -1483,7 +1505,9 @@ class mr_class(MAxisConcatenator): Examples -------- >>> np.ma.mr_[np.ma.array([1,2,3]), 0, 0, np.ma.array([4,5,6])] - array([1, 2, 3, 0, 0, 4, 5, 6]) + masked_array(data=[1, 2, 3, ..., 4, 5, 6], + mask=False, + fill_value=999999) """ def __init__(self): @@ -1514,7 +1538,7 @@ def flatnotmasked_edges(a): See Also -------- - flatnotmasked_contiguous, notmasked_contiguous, notmasked_edges, + flatnotmasked_contiguous, notmasked_contiguous, notmasked_edges clump_masked, clump_unmasked Notes @@ -1524,19 +1548,19 @@ def flatnotmasked_edges(a): Examples -------- >>> a = np.ma.arange(10) - >>> flatnotmasked_edges(a) - [0,-1] + >>> np.ma.flatnotmasked_edges(a) + array([0, 9]) >>> mask = (a < 3) | (a > 8) | (a == 5) >>> a[mask] = np.ma.masked >>> np.array(a[~a.mask]) array([3, 4, 6, 7, 8]) - >>> flatnotmasked_edges(a) + >>> np.ma.flatnotmasked_edges(a) array([3, 8]) >>> a[:] = np.ma.masked - >>> print(flatnotmasked_edges(ma)) + >>> print(np.ma.flatnotmasked_edges(a)) None """ @@ -1575,7 +1599,7 @@ def notmasked_edges(a, axis=None): See Also -------- - flatnotmasked_contiguous, flatnotmasked_edges, notmasked_contiguous, + flatnotmasked_contiguous, flatnotmasked_edges, notmasked_contiguous clump_masked, clump_unmasked Examples @@ -1588,7 +1612,7 @@ def notmasked_edges(a, axis=None): >>> np.array(am[~am.mask]) array([0, 1, 2, 3, 6]) - >>> np.ma.notmasked_edges(ma) + >>> np.ma.notmasked_edges(am) array([0, 6]) """ @@ -1615,12 +1639,12 @@ def flatnotmasked_contiguous(a): slice_list : list A sorted sequence of `slice` objects (start index, end index). - ..versionchanged:: 1.15.0 + .. versionchanged:: 1.15.0 Now returns an empty list instead of None for a fully masked array See Also -------- - flatnotmasked_edges, notmasked_contiguous, notmasked_edges, + flatnotmasked_edges, notmasked_contiguous, notmasked_edges clump_masked, clump_unmasked Notes @@ -1680,7 +1704,7 @@ def notmasked_contiguous(a, axis=None): See Also -------- - flatnotmasked_edges, flatnotmasked_contiguous, notmasked_edges, + flatnotmasked_edges, flatnotmasked_contiguous, notmasked_edges clump_masked, clump_unmasked Notes @@ -1709,15 +1733,11 @@ def notmasked_contiguous(a, axis=None): [slice(0, 1, None), slice(2, 4, None), slice(7, 9, None), slice(11, 12, None)] >>> np.ma.notmasked_contiguous(ma, axis=0) - [[slice(0, 1, None), slice(2, 3, None)], # column broken into two segments - [], # fully masked column - [slice(0, 1, None)], - [slice(0, 3, None)]] + [[slice(0, 1, None), slice(2, 3, None)], [], [slice(0, 1, None)], [slice(0, 3, None)]] >>> np.ma.notmasked_contiguous(ma, axis=1) - [[slice(0, 1, None), slice(2, 4, None)], # row broken into two segments - [slice(3, 4, None)], - [slice(0, 1, None), slice(3, 4, None)]] + [[slice(0, 1, None), slice(2, 4, None)], [slice(3, 4, None)], [slice(0, 1, None), slice(3, 4, None)]] + """ a = asarray(a) nd = a.ndim @@ -1789,7 +1809,7 @@ def clump_unmasked(a): See Also -------- - flatnotmasked_edges, flatnotmasked_contiguous, notmasked_edges, + flatnotmasked_edges, flatnotmasked_contiguous, notmasked_edges notmasked_contiguous, clump_masked Examples @@ -1828,7 +1848,7 @@ def clump_masked(a): See Also -------- - flatnotmasked_edges, flatnotmasked_contiguous, notmasked_edges, + flatnotmasked_edges, flatnotmasked_contiguous, notmasked_edges notmasked_contiguous, clump_unmasked Examples diff --git a/numpy/ma/extras.pyi b/numpy/ma/extras.pyi new file mode 100644 index 000000000000..e58e43badf23 --- /dev/null +++ b/numpy/ma/extras.pyi @@ -0,0 +1,84 @@ +from typing import Any, List +from numpy.lib.index_tricks import AxisConcatenator + +from numpy.ma.core import ( + dot as dot, + mask_rowcols as mask_rowcols, +) + +__all__: List[str] + +def count_masked(arr, axis=...): ... +def masked_all(shape, dtype = ...): ... +def masked_all_like(arr): ... + +class _fromnxfunction: + __name__: Any + __doc__: Any + def __init__(self, funcname): ... + def getdoc(self): ... + def __call__(self, *args, **params): ... + +class _fromnxfunction_single(_fromnxfunction): + def __call__(self, x, *args, **params): ... + +class _fromnxfunction_seq(_fromnxfunction): + def __call__(self, x, *args, **params): ... + +class _fromnxfunction_allargs(_fromnxfunction): + def __call__(self, *args, **params): ... + +atleast_1d: _fromnxfunction_allargs +atleast_2d: _fromnxfunction_allargs +atleast_3d: _fromnxfunction_allargs + +vstack: _fromnxfunction_seq +row_stack: _fromnxfunction_seq +hstack: _fromnxfunction_seq +column_stack: _fromnxfunction_seq +dstack: _fromnxfunction_seq +stack: _fromnxfunction_seq + +hsplit: _fromnxfunction_single +diagflat: _fromnxfunction_single + +def apply_along_axis(func1d, axis, arr, *args, **kwargs): ... +def apply_over_axes(func, a, axes): ... +def average(a, axis=..., weights=..., returned=...): ... +def median(a, axis=..., out=..., overwrite_input=..., keepdims=...): ... +def compress_nd(x, axis=...): ... +def compress_rowcols(x, axis=...): ... +def compress_rows(a): ... +def compress_cols(a): ... +def mask_rows(a, axis = ...): ... +def mask_cols(a, axis = ...): ... +def ediff1d(arr, to_end=..., to_begin=...): ... +def unique(ar1, return_index=..., return_inverse=...): ... +def intersect1d(ar1, ar2, assume_unique=...): ... +def setxor1d(ar1, ar2, assume_unique=...): ... +def in1d(ar1, ar2, assume_unique=..., invert=...): ... +def isin(element, test_elements, assume_unique=..., invert=...): ... +def union1d(ar1, ar2): ... +def setdiff1d(ar1, ar2, assume_unique=...): ... +def cov(x, y=..., rowvar=..., bias=..., allow_masked=..., ddof=...): ... +def corrcoef(x, y=..., rowvar=..., bias = ..., allow_masked=..., ddof = ...): ... + +class MAxisConcatenator(AxisConcatenator): + concatenate: Any + @classmethod + def makemat(cls, arr): ... + def __getitem__(self, key): ... + +class mr_class(MAxisConcatenator): + def __init__(self): ... + +mr_: mr_class + +def flatnotmasked_edges(a): ... +def notmasked_edges(a, axis=...): ... +def flatnotmasked_contiguous(a): ... +def notmasked_contiguous(a, axis=...): ... +def clump_unmasked(a): ... +def clump_masked(a): ... +def vander(x, n=...): ... +def polyfit(x, y, deg, rcond=..., full=..., w=..., cov=...): ... diff --git a/numpy/ma/mrecords.py b/numpy/ma/mrecords.py index 90a5141b395e..1e8103bcf632 100644 --- a/numpy/ma/mrecords.py +++ b/numpy/ma/mrecords.py @@ -8,34 +8,28 @@ .. moduleauthor:: Pierre Gerard-Marchant """ -from __future__ import division, absolute_import, print_function - # We should make sure that no field is called '_mask','mask','_fieldmask', # or whatever restricted keywords. An idea would be to no bother in the # first place, and then rename the invalid fields with a trailing # underscore. Maybe we could just overload the parser function ? -import sys +from numpy.ma import ( + MAError, MaskedArray, masked, nomask, masked_array, getdata, + getmaskarray, filled +) +import numpy.ma as ma import warnings import numpy as np -import numpy.core.numerictypes as ntypes -from numpy.compat import basestring from numpy import ( - bool_, dtype, ndarray, recarray, array as narray - ) + bool_, dtype, ndarray, recarray, array as narray +) from numpy.core.records import ( - fromarrays as recfromarrays, fromrecords as recfromrecords - ) + fromarrays as recfromarrays, fromrecords as recfromrecords +) _byteorderconv = np.core.records._byteorderconv -_typestr = ntypes._typestr -import numpy.ma as ma -from numpy.ma import ( - MAError, MaskedArray, masked, nomask, masked_array, getdata, - getmaskarray, filled - ) _check_fill_value = ma.core._check_fill_value @@ -43,29 +37,11 @@ __all__ = [ 'MaskedRecords', 'mrecarray', 'fromarrays', 'fromrecords', 'fromtextfile', 'addfield', - ] +] reserved_fields = ['_data', '_mask', '_fieldmask', 'dtype'] -def _getformats(data): - """ - Returns the formats of arrays in arraylist as a comma-separated string. - - """ - if hasattr(data, 'dtype'): - return ",".join([desc[1] for desc in data.dtype.descr]) - - formats = '' - for obj in data: - obj = np.asarray(obj) - formats += _typestr[obj.dtype.type] - if issubclass(obj.dtype.type, ntypes.flexible): - formats += repr(obj.itemsize) - formats += ',' - return formats[:-1] - - def _checknames(descr, names=None): """ Checks that field names ``descr`` are not reserved keywords. @@ -84,7 +60,7 @@ def _checknames(descr, names=None): elif isinstance(names, str): new_names = names.split(',') else: - raise NameError("illegal input names %s" % repr(names)) + raise NameError(f'illegal input names {names!r}') nnames = len(new_names) if nnames < ndescr: new_names += default_names[nnames:] @@ -107,7 +83,7 @@ def _get_fieldmask(self): return fdmask -class MaskedRecords(MaskedArray, object): +class MaskedRecords(MaskedArray): """ Attributes @@ -153,7 +129,6 @@ def __new__(cls, shape, dtype=None, buf=None, offset=0, strides=None, msg = "Mask and data not compatible: data size is %i, " + \ "mask size is %i." raise MAError(msg % (nd, nm)) - copy = True if not keep_mask: self.__setmask__(mask) self._sharedmask = True @@ -186,24 +161,22 @@ def __array_finalize__(self, obj): _dict['_baseclass'] = recarray return - def _getdata(self): + @property + def _data(self): """ Returns the data as a recarray. """ return ndarray.view(self, recarray) - _data = property(fget=_getdata) - - def _getfieldmask(self): + @property + def _fieldmask(self): """ Alias to mask. """ return self._mask - _fieldmask = property(fget=_getfieldmask) - def __len__(self): """ Returns the length @@ -224,13 +197,14 @@ def __getattribute__(self, attr): fielddict = ndarray.__getattribute__(self, 'dtype').fields try: res = fielddict[attr][:2] - except (TypeError, KeyError): - raise AttributeError("record array has no attribute %s" % attr) + except (TypeError, KeyError) as e: + raise AttributeError( + f'record array has no attribute {attr}') from e # So far, so good _localdict = ndarray.__getattribute__(self, '__dict__') _data = ndarray.view(self, _localdict['_baseclass']) obj = _data.getfield(*res) - if obj.dtype.fields: + if obj.dtype.names is not None: raise NotImplementedError("MaskedRecords is currently limited to" "simple records.") # Get some special attributes @@ -243,7 +217,8 @@ def __getattribute__(self, attr): except IndexError: # Couldn't find a mask: use the default (nomask) pass - hasmasked = _mask.view((bool, (len(_mask.dtype) or 1))).any() + tp_len = len(_mask.dtype) + hasmasked = _mask.view((bool, ((tp_len,) if tp_len else ()))).any() if (obj.shape or hasmasked): obj = obj.view(MaskedArray) obj._baseclass = ndarray @@ -281,8 +256,7 @@ def __setattr__(self, attr, val): fielddict = ndarray.__getattribute__(self, 'dtype').fields or {} optinfo = ndarray.__getattribute__(self, '_optinfo') or {} if not (attr in fielddict or attr in optinfo): - exctype, value = sys.exc_info()[:2] - raise exctype(value) + raise else: # Get the list of names fielddict = ndarray.__getattribute__(self, 'dtype').fields or {} @@ -299,8 +273,9 @@ def __setattr__(self, attr, val): # Let's try to set the field try: res = fielddict[attr][:2] - except (TypeError, KeyError): - raise AttributeError("record array has no attribute %s" % attr) + except (TypeError, KeyError) as e: + raise AttributeError( + f'record array has no attribute {attr}') from e if val is masked: _fill_value = _localdict['_fill_value'] @@ -327,7 +302,7 @@ def __getitem__(self, indx): _mask = ndarray.__getattribute__(self, '_mask') _data = ndarray.view(self, _localdict['_baseclass']) # We want a field - if isinstance(indx, basestring): + if isinstance(indx, str): # Make sure _sharedmask is True to propagate back to _fieldmask # Don't use _set_mask, there are some copies being made that # break propagation Don't force the mask to nomask, that wreaks @@ -354,7 +329,7 @@ def __setitem__(self, indx, value): """ MaskedArray.__setitem__(self, indx, value) - if isinstance(indx, basestring): + if isinstance(indx, str): self._mask[indx] = ma.getmaskarray(value) def __str__(self): @@ -363,13 +338,13 @@ def __str__(self): """ if self.size > 1: - mstr = ["(%s)" % ",".join([str(i) for i in s]) + mstr = [f"({','.join([str(i) for i in s])})" for s in zip(*[getattr(self, f) for f in self.dtype.names])] - return "[%s]" % ", ".join(mstr) + return f"[{', '.join(mstr)}]" else: - mstr = ["%s" % ",".join([str(i) for i in s]) + mstr = [f"{','.join([str(i) for i in s])}" for s in zip([getattr(self, f) for f in self.dtype.names])] - return "(%s)" % ", ".join(mstr) + return f"({', '.join(mstr)})" def __repr__(self): """ @@ -381,7 +356,7 @@ def __repr__(self): reprstr = [fmt % (f, getattr(self, f)) for f in self.dtype.names] reprstr.insert(0, 'masked_records(') reprstr.extend([fmt % (' fill_value', self.fill_value), - ' )']) + ' )']) return str("\n".join(reprstr)) def view(self, dtype=None, type=None): @@ -400,7 +375,6 @@ def view(self, dtype=None, type=None): try: if issubclass(dtype, ndarray): output = ndarray.view(self, dtype) - dtype = None else: output = ndarray.view(self, dtype) # OK, there's the change @@ -509,6 +483,7 @@ def __reduce__(self): (self.__class__, self._baseclass, (0,), 'b',), self.__getstate__()) + def _mrreconstruct(subtype, baseclass, baseshape, basetype,): """ Build a new MaskedArray from the information stored in a pickle. @@ -682,8 +657,8 @@ def openfile(fname): # Try to open the file and guess its type try: f = open(fname) - except IOError: - raise IOError("No such file: '%s'" % fname) + except FileNotFoundError as e: + raise FileNotFoundError(f"No such file: '{fname}'") from e if f.readline()[:2] != "\\x": f.seek(0, 0) return f @@ -691,8 +666,9 @@ def openfile(fname): raise NotImplementedError("Wow, binary file") -def fromtextfile(fname, delimitor=None, commentchar='#', missingchar='', - varnames=None, vartypes=None): +def fromtextfile(fname, delimiter=None, commentchar='#', missingchar='', + varnames=None, vartypes=None, + *, delimitor=np._NoValue): # backwards compatibility """ Creates a mrecarray from data stored in the file `filename`. @@ -700,7 +676,7 @@ def fromtextfile(fname, delimitor=None, commentchar='#', missingchar='', ---------- fname : {file name/handle} Handle of an opened file. - delimitor : {None, string}, optional + delimiter : {None, string}, optional Alphanumeric character used to separate columns in the file. If None, any (group of) white spacestring(s) will be used. commentchar : {'#', string}, optional @@ -716,6 +692,17 @@ def fromtextfile(fname, delimitor=None, commentchar='#', missingchar='', Ultra simple: the varnames are in the header, one line""" + if delimitor is not np._NoValue: + if delimiter is not None: + raise TypeError("fromtextfile() got multiple values for argument " + "'delimiter'") + # NumPy 1.22.0, 2021-09-23 + warnings.warn("The 'delimitor' keyword argument of " + "numpy.ma.mrecords.fromtextfile() is deprecated " + "since NumPy 1.22.0, use 'delimiter' instead.", + DeprecationWarning, stacklevel=2) + delimiter = delimitor + # Try to open the file. ftext = openfile(fname) @@ -723,14 +710,14 @@ def fromtextfile(fname, delimitor=None, commentchar='#', missingchar='', while True: line = ftext.readline() firstline = line[:line.find(commentchar)].strip() - _varnames = firstline.split(delimitor) + _varnames = firstline.split(delimiter) if len(_varnames) > 1: break if varnames is None: varnames = _varnames # Get the data. - _variables = masked_array([line.strip().split(delimitor) for line in ftext + _variables = masked_array([line.strip().split(delimiter) for line in ftext if line[0] != commentchar and len(line) > 1]) (_, nfields) = _variables.shape ftext.close() @@ -778,7 +765,7 @@ def addfield(mrecord, newfield, newfieldname=None): newdata = recarray(_data.shape, newdtype) # Add the existing field [newdata.setfield(_data.getfield(*f), *f) - for f in _data.dtype.fields.values()] + for f in _data.dtype.fields.values()] # Add the new field newdata.setfield(newfield._data, *newdata.dtype.fields[newfieldname]) newdata = newdata.view(MaskedRecords) @@ -788,7 +775,7 @@ def addfield(mrecord, newfield, newfieldname=None): newmask = recarray(_data.shape, newmdtype) # Add the old masks [newmask.setfield(_mask.getfield(*f), *f) - for f in _mask.dtype.fields.values()] + for f in _mask.dtype.fields.values()] # Add the mask of the new field newmask.setfield(getmaskarray(newfield), *newmask.dtype.fields[newfieldname]) diff --git a/numpy/ma/mrecords.pyi b/numpy/ma/mrecords.pyi new file mode 100644 index 000000000000..7bd8678cf12d --- /dev/null +++ b/numpy/ma/mrecords.pyi @@ -0,0 +1,90 @@ +from typing import List, Any, TypeVar + +from numpy import dtype +from numpy.ma import MaskedArray + +__all__: List[str] + +# TODO: Set the `bound` to something more suitable once we +# have proper shape support +_ShapeType = TypeVar("_ShapeType", bound=Any) +_DType_co = TypeVar("_DType_co", bound=dtype[Any], covariant=True) + +class MaskedRecords(MaskedArray[_ShapeType, _DType_co]): + def __new__( + cls, + shape, + dtype=..., + buf=..., + offset=..., + strides=..., + formats=..., + names=..., + titles=..., + byteorder=..., + aligned=..., + mask=..., + hard_mask=..., + fill_value=..., + keep_mask=..., + copy=..., + **options, + ): ... + _mask: Any + _fill_value: Any + @property + def _data(self): ... + @property + def _fieldmask(self): ... + def __array_finalize__(self, obj): ... + def __len__(self): ... + def __getattribute__(self, attr): ... + def __setattr__(self, attr, val): ... + def __getitem__(self, indx): ... + def __setitem__(self, indx, value): ... + def view(self, dtype=..., type=...): ... + def harden_mask(self): ... + def soften_mask(self): ... + def copy(self): ... + def tolist(self, fill_value=...): ... + def __reduce__(self): ... + +mrecarray = MaskedRecords + +def fromarrays( + arraylist, + dtype=..., + shape=..., + formats=..., + names=..., + titles=..., + aligned=..., + byteorder=..., + fill_value=..., +): ... + +def fromrecords( + reclist, + dtype=..., + shape=..., + formats=..., + names=..., + titles=..., + aligned=..., + byteorder=..., + fill_value=..., + mask=..., +): ... + +def fromtextfile( + fname, + delimiter=..., + commentchar=..., + missingchar=..., + varnames=..., + vartypes=..., + # NOTE: deprecated: NumPy 1.22.0, 2021-09-23 + # delimitor=..., +): ... + +def addfield(mrecord, newfield, newfieldname=...): ... diff --git a/numpy/ma/setup.py b/numpy/ma/setup.py index d1d6c89b5139..018d38cdd500 100644 --- a/numpy/ma/setup.py +++ b/numpy/ma/setup.py @@ -1,10 +1,9 @@ -#!/usr/bin/env python -from __future__ import division, print_function - +#!/usr/bin/env python3 def configuration(parent_package='',top_path=None): from numpy.distutils.misc_util import Configuration config = Configuration('ma', parent_package, top_path) - config.add_data_dir('tests') + config.add_subpackage('tests') + config.add_data_files('*.pyi') return config if __name__ == "__main__": diff --git a/numpy/ma/tests/test_core.py b/numpy/ma/tests/test_core.py index 67a9186a8aa1..c8f7f42692d4 100644 --- a/numpy/ma/tests/test_core.py +++ b/numpy/ma/tests/test_core.py @@ -4,13 +4,10 @@ :author: Pierre Gerard-Marchant :contact: pierregm_at_uga_dot_edu """ -from __future__ import division, absolute_import, print_function - __author__ = "Pierre GF Gerard-Marchant" import sys import warnings -import pickle import operator import itertools import textwrap @@ -27,7 +24,7 @@ assert_raises, assert_warns, suppress_warnings ) from numpy import ndarray -from numpy.compat import asbytes, asbytes_nested +from numpy.compat import asbytes from numpy.ma.testutils import ( assert_, assert_array_equal, assert_equal, assert_almost_equal, assert_equal_records, fail_if_equal, assert_not_equal, @@ -37,8 +34,8 @@ MAError, MaskError, MaskType, MaskedArray, abs, absolute, add, all, allclose, allequal, alltrue, angle, anom, arange, arccos, arccosh, arctan2, arcsin, arctan, argsort, array, asarray, choose, concatenate, - conjugate, cos, cosh, count, default_fill_value, diag, divide, empty, - empty_like, equal, exp, flatten_mask, filled, fix_invalid, + conjugate, cos, cosh, count, default_fill_value, diag, divide, doc_note, + empty, empty_like, equal, exp, flatten_mask, filled, fix_invalid, flatten_structured_array, fromflex, getmask, getmaskarray, greater, greater_equal, identity, inner, isMaskedArray, less, less_equal, log, log10, make_mask, make_mask_descr, mask_or, masked, masked_array, @@ -46,10 +43,11 @@ masked_less, masked_less_equal, masked_not_equal, masked_outside, masked_print_option, masked_values, masked_where, max, maximum, maximum_fill_value, min, minimum, minimum_fill_value, mod, multiply, - mvoid, nomask, not_equal, ones, outer, power, product, put, putmask, - ravel, repeat, reshape, resize, shape, sin, sinh, sometrue, sort, sqrt, - subtract, sum, take, tan, tanh, transpose, where, zeros, + mvoid, nomask, not_equal, ones, ones_like, outer, power, product, put, + putmask, ravel, repeat, reshape, resize, shape, sin, sinh, sometrue, sort, + sqrt, subtract, sum, take, tan, tanh, transpose, where, zeros, zeros_like, ) +from numpy.compat import pickle pi = np.pi @@ -60,7 +58,12 @@ "setting an item on a masked array which has a shared mask will not copy") -class TestMaskedArray(object): +# For parametrized numeric testing +num_dts = [np.dtype(dt_) for dt_ in '?bhilqBHILQefdgFD'] +num_ids = [dt_.char for dt_ in num_dts] + + +class TestMaskedArray: # Base test class for MaskedArrays. def setup(self): @@ -212,6 +215,17 @@ def test_creation_maskcreation(self): y = array([1, 2, 3], mask=x._mask, copy=True) assert_(not np.may_share_memory(x.mask, y.mask)) + def test_masked_singleton_array_creation_warns(self): + # The first works, but should not (ideally), there may be no way + # to solve this, however, as long as `np.ma.masked` is an ndarray. + np.array(np.ma.masked) + with pytest.warns(UserWarning): + # Tries to create a float array, using `float(np.ma.masked)`. + # We may want to define this is invalid behaviour in the future! + # (requiring np.ma.masked to be a known NumPy scalar probably + # with a DType.) + np.array([3., np.ma.masked]) + def test_creation_with_list_of_maskedarrays(self): # Tests creating a masked array from a list of masked arrays. x = array(np.arange(5), mask=[1, 0, 0, 0, 0]) @@ -224,11 +238,35 @@ def test_creation_with_list_of_maskedarrays(self): assert_equal(data, [[0, 1, 2, 3, 4], [4, 3, 2, 1, 0]]) assert_(data.mask is nomask) + def test_creation_with_list_of_maskedarrays_no_bool_cast(self): + # Tests the regression in gh-18551 + masked_str = np.ma.masked_array(['a', 'b'], mask=[True, False]) + normal_int = np.arange(2) + res = np.ma.asarray([masked_str, normal_int], dtype="U21") + assert_array_equal(res.mask, [[True, False], [False, False]]) + + # The above only failed due a long chain of oddity, try also with + # an object array that cannot be converted to bool always: + class NotBool(): + def __bool__(self): + raise ValueError("not a bool!") + masked_obj = np.ma.masked_array([NotBool(), 'b'], mask=[True, False]) + # Check that the NotBool actually fails like we would expect: + with pytest.raises(ValueError, match="not a bool!"): + np.asarray([masked_obj], dtype=bool) + + res = np.ma.asarray([masked_obj, normal_int]) + assert_array_equal(res.mask, [[True, False], [False, False]]) + def test_creation_from_ndarray_with_padding(self): x = np.array([('A', 0)], dtype={'names':['f0','f1'], 'formats':['S4','i8'], 'offsets':[0,8]}) - data = array(x) # used to fail due to 'V' padding field in x.dtype.descr + array(x) # used to fail due to 'V' padding field in x.dtype.descr + + def test_unknown_keyword_parameter(self): + with pytest.raises(TypeError, match="unexpected keyword argument"): + MaskedArray([1, 2, 3], maks=[0, 1, 0]) # `mask` is misspelled. def test_asarray(self): (x, y, a10, m1, m2, xm, ym, z, zm, xf) = self.d @@ -342,7 +380,7 @@ def test_copy(self): m = make_mask(n) m2 = make_mask(m) assert_(m is m2) - m3 = make_mask(m, copy=1) + m3 = make_mask(m, copy=True) assert_(m is not m3) x1 = np.arange(5) @@ -369,12 +407,12 @@ def test_copy(self): y2a = array(x1, mask=m, copy=1) assert_(y2a._data.__array_interface__ != x1.__array_interface__) - #assert_( y2a.mask is not m) + #assert_( y2a._mask is not m) assert_(y2a._mask.__array_interface__ != m.__array_interface__) assert_(y2a[2] is masked) y2a[2] = 9 assert_(y2a[2] is not masked) - #assert_( y2a.mask is not m) + #assert_( y2a._mask is not m) assert_(y2a._mask.__array_interface__ != m.__array_interface__) assert_(allequal(y2a.mask, 0)) @@ -444,6 +482,21 @@ def test_deepcopy(self): assert_equal(copied.mask, [0, 0, 0]) assert_equal(a.mask, [0, 1, 0]) + def test_format(self): + a = array([0, 1, 2], mask=[False, True, False]) + assert_equal(format(a), "[0 -- 2]") + assert_equal(format(masked), "--") + assert_equal(format(masked, ""), "--") + + # Postponed from PR #15410, perhaps address in the future. + # assert_equal(format(masked, " >5"), " --") + # assert_equal(format(masked, " <5"), "-- ") + + # Expect a FutureWarning for using format_spec with MaskedElement + with assert_warns(FutureWarning): + with_format_string = format(masked, " >5") + assert_equal(with_format_string, "--") + def test_str_repr(self): a = array([0, 1, 2], mask=[False, True, False]) assert_equal(str(a), '[0 -- 2]') @@ -514,8 +567,6 @@ def test_str_repr(self): fill_value=999999)''') ) - - def test_str_repr_legacy(self): oldopts = np.get_printoptions() np.set_printoptions(legacy='1.13') @@ -557,50 +608,55 @@ def test_pickling(self): True, # Fully masked False) # Fully unmasked - for mask in masks: - a.mask = mask - a_pickled = pickle.loads(a.dumps()) - assert_equal(a_pickled._mask, a._mask) - assert_equal(a_pickled._data, a._data) - if dtype in (object, int): - assert_equal(a_pickled.fill_value, 999) - else: - assert_equal(a_pickled.fill_value, dtype(999)) - assert_array_equal(a_pickled.mask, mask) + for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): + for mask in masks: + a.mask = mask + a_pickled = pickle.loads(pickle.dumps(a, protocol=proto)) + assert_equal(a_pickled._mask, a._mask) + assert_equal(a_pickled._data, a._data) + if dtype in (object, int): + assert_equal(a_pickled.fill_value, 999) + else: + assert_equal(a_pickled.fill_value, dtype(999)) + assert_array_equal(a_pickled.mask, mask) def test_pickling_subbaseclass(self): # Test pickling w/ a subclass of ndarray x = np.array([(1.0, 2), (3.0, 4)], dtype=[('x', float), ('y', int)]).view(np.recarray) a = masked_array(x, mask=[(True, False), (False, True)]) - a_pickled = pickle.loads(a.dumps()) - assert_equal(a_pickled._mask, a._mask) - assert_equal(a_pickled, a) - assert_(isinstance(a_pickled._data, np.recarray)) + for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): + a_pickled = pickle.loads(pickle.dumps(a, protocol=proto)) + assert_equal(a_pickled._mask, a._mask) + assert_equal(a_pickled, a) + assert_(isinstance(a_pickled._data, np.recarray)) def test_pickling_maskedconstant(self): # Test pickling MaskedConstant mc = np.ma.masked - mc_pickled = pickle.loads(mc.dumps()) - assert_equal(mc_pickled._baseclass, mc._baseclass) - assert_equal(mc_pickled._mask, mc._mask) - assert_equal(mc_pickled._data, mc._data) + for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): + mc_pickled = pickle.loads(pickle.dumps(mc, protocol=proto)) + assert_equal(mc_pickled._baseclass, mc._baseclass) + assert_equal(mc_pickled._mask, mc._mask) + assert_equal(mc_pickled._data, mc._data) def test_pickling_wstructured(self): # Tests pickling w/ structured array a = array([(1, 1.), (2, 2.)], mask=[(0, 0), (0, 1)], dtype=[('a', int), ('b', float)]) - a_pickled = pickle.loads(a.dumps()) - assert_equal(a_pickled._mask, a._mask) - assert_equal(a_pickled, a) + for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): + a_pickled = pickle.loads(pickle.dumps(a, protocol=proto)) + assert_equal(a_pickled._mask, a._mask) + assert_equal(a_pickled, a) def test_pickling_keepalignment(self): # Tests pickling w/ F_CONTIGUOUS arrays a = arange(10) a.shape = (-1, 2) b = a.T - test = pickle.loads(pickle.dumps(b)) - assert_equal(test, b) + for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): + test = pickle.loads(pickle.dumps(b, protocol=proto)) + assert_equal(test, b) def test_single_element_subscript(self): # Tests single element subscripts of Maskedarrays. @@ -788,7 +844,6 @@ def test_fancy_printoptions(self): control = "(0, [[--, 0.0, --], [0.0, 0.0, --]], 0.0)" assert_equal(str(t_2d0), control) - def test_flatten_structured_array(self): # Test flatten_structured_array on arrays # On ndarray @@ -929,7 +984,7 @@ def test_mvoid_multidim_print(self): def test_object_with_array(self): mx1 = masked_array([1.], mask=[True]) mx2 = masked_array([1., 2.]) - mx = masked_array([mx1, mx2], mask=[False, True]) + mx = masked_array([mx1, mx2], mask=[False, True], dtype=object) assert_(mx[0] is mx1) assert_(mx[1] is not mx2) assert_(np.all(mx[1].data == mx2.data)) @@ -939,7 +994,7 @@ def test_object_with_array(self): assert_(mx2[0] == 0.) -class TestMaskedArrayArithmetic(object): +class TestMaskedArrayArithmetic: # Base test class for MaskedArrays. def setup(self): @@ -1016,7 +1071,7 @@ def test_divide_on_different_shapes(self): assert_equal(z.mask, [[1, 1, 1], [0, 0, 0]]) def test_mixed_arithmetic(self): - # Tests mixed arithmetics. + # Tests mixed arithmetic. na = np.array([1]) ma = array([1]) assert_(isinstance(na + ma, MaskedArray)) @@ -1029,7 +1084,7 @@ def test_limits_arithmetic(self): assert_equal(getmaskarray(2 / a), [1, 0, 1]) def test_masked_singleton_arithmetic(self): - # Tests some scalar arithmetics on MaskedArrays. + # Tests some scalar arithmetic on MaskedArrays. # Masked singleton should remain masked no matter what xm = array(0, mask=1) assert_((1 / array(0)).mask) @@ -1225,6 +1280,48 @@ def test_minmax_methods(self): assert_(x.max() is masked) assert_(x.ptp() is masked) + def test_minmax_dtypes(self): + # Additional tests on max/min for non-standard float and complex dtypes + x = np.array([1., 1., 1., -2., pi/2.0, 4., 5., -10., 10., 1., 2., 3.]) + a10 = 10. + an10 = -10.0 + m1 = [1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0] + xm = masked_array(x, mask=m1) + xm.set_fill_value(1e+20) + float_dtypes = [np.half, np.single, np.double, + np.longdouble, np.cfloat, np.cdouble, np.clongdouble] + for float_dtype in float_dtypes: + assert_equal(masked_array(x, mask=m1, dtype=float_dtype).max(), + float_dtype(a10)) + assert_equal(masked_array(x, mask=m1, dtype=float_dtype).min(), + float_dtype(an10)) + + assert_equal(xm.min(), an10) + assert_equal(xm.max(), a10) + + # Non-complex type only test + for float_dtype in float_dtypes[:4]: + assert_equal(masked_array(x, mask=m1, dtype=float_dtype).max(), + float_dtype(a10)) + assert_equal(masked_array(x, mask=m1, dtype=float_dtype).min(), + float_dtype(an10)) + + # Complex types only test + for float_dtype in float_dtypes[-3:]: + ym = masked_array([1e20+1j, 1e20-2j, 1e20-1j], mask=[0, 1, 0], + dtype=float_dtype) + assert_equal(ym.min(), float_dtype(1e20-1j)) + assert_equal(ym.max(), float_dtype(1e20+1j)) + + zm = masked_array([np.inf+2j, np.inf+3j, -np.inf-1j], mask=[0, 1, 0], + dtype=float_dtype) + assert_equal(zm.min(), float_dtype(-np.inf-1j)) + assert_equal(zm.max(), float_dtype(np.inf+2j)) + + cmax = np.inf - 1j * np.finfo(np.float64).max + assert masked_array([-cmax, 0], mask=[0, 1]).max() == -cmax + assert masked_array([cmax, 0], mask=[0, 1]).min() == cmax + def test_addsumprod(self): # Tests add, sum, product. (x, y, a10, m1, m2, xm, ym, z, zm, xf) = self.d @@ -1413,23 +1510,34 @@ def test_eq_on_structured(self): # Test the equality of structured arrays ndtype = [('A', int), ('B', int)] a = array([(1, 1), (2, 2)], mask=[(0, 1), (0, 0)], dtype=ndtype) + test = (a == a) assert_equal(test.data, [True, True]) assert_equal(test.mask, [False, False]) + assert_(test.fill_value == True) + test = (a == a[0]) assert_equal(test.data, [True, False]) assert_equal(test.mask, [False, False]) + assert_(test.fill_value == True) + b = array([(1, 1), (2, 2)], mask=[(1, 0), (0, 0)], dtype=ndtype) test = (a == b) assert_equal(test.data, [False, True]) assert_equal(test.mask, [True, False]) + assert_(test.fill_value == True) + test = (a[0] == b) assert_equal(test.data, [False, False]) assert_equal(test.mask, [True, False]) + assert_(test.fill_value == True) + b = array([(1, 1), (2, 2)], mask=[(0, 1), (1, 0)], dtype=ndtype) test = (a == b) assert_equal(test.data, [True, True]) assert_equal(test.mask, [False, False]) + assert_(test.fill_value == True) + # complicated dtype, 2-dimensional array. ndtype = [('A', int), ('B', [('BA', int), ('BB', int)])] a = array([[(1, (1, 1)), (2, (2, 2))], @@ -1439,28 +1547,40 @@ def test_eq_on_structured(self): test = (a[0, 0] == a) assert_equal(test.data, [[True, False], [False, False]]) assert_equal(test.mask, [[False, False], [False, True]]) + assert_(test.fill_value == True) def test_ne_on_structured(self): # Test the equality of structured arrays ndtype = [('A', int), ('B', int)] a = array([(1, 1), (2, 2)], mask=[(0, 1), (0, 0)], dtype=ndtype) + test = (a != a) assert_equal(test.data, [False, False]) assert_equal(test.mask, [False, False]) + assert_(test.fill_value == True) + test = (a != a[0]) assert_equal(test.data, [False, True]) assert_equal(test.mask, [False, False]) + assert_(test.fill_value == True) + b = array([(1, 1), (2, 2)], mask=[(1, 0), (0, 0)], dtype=ndtype) test = (a != b) assert_equal(test.data, [True, False]) assert_equal(test.mask, [True, False]) + assert_(test.fill_value == True) + test = (a[0] != b) assert_equal(test.data, [True, True]) assert_equal(test.mask, [True, False]) + assert_(test.fill_value == True) + b = array([(1, 1), (2, 2)], mask=[(0, 1), (1, 0)], dtype=ndtype) test = (a != b) assert_equal(test.data, [False, False]) assert_equal(test.mask, [False, False]) + assert_(test.fill_value == True) + # complicated dtype, 2-dimensional array. ndtype = [('A', int), ('B', [('BA', int), ('BB', int)])] a = array([[(1, (1, 1)), (2, (2, 2))], @@ -1470,6 +1590,7 @@ def test_ne_on_structured(self): test = (a[0, 0] != a) assert_equal(test.data, [[False, True], [True, True]]) assert_equal(test.mask, [[False, False], [False, True]]) + assert_(test.fill_value == True) def test_eq_ne_structured_extra(self): # ensure simple examples are symmetric and make sense. @@ -1505,6 +1626,136 @@ def test_eq_ne_structured_extra(self): el_by_el = [m1[name] != m2[name] for name in dt.names] assert_equal(array(el_by_el, dtype=bool).any(), ne_expected) + @pytest.mark.parametrize('dt', ['S', 'U']) + @pytest.mark.parametrize('fill', [None, 'A']) + def test_eq_for_strings(self, dt, fill): + # Test the equality of structured arrays + a = array(['a', 'b'], dtype=dt, mask=[0, 1], fill_value=fill) + + test = (a == a) + assert_equal(test.data, [True, True]) + assert_equal(test.mask, [False, True]) + assert_(test.fill_value == True) + + test = (a == a[0]) + assert_equal(test.data, [True, False]) + assert_equal(test.mask, [False, True]) + assert_(test.fill_value == True) + + b = array(['a', 'b'], dtype=dt, mask=[1, 0], fill_value=fill) + test = (a == b) + assert_equal(test.data, [False, False]) + assert_equal(test.mask, [True, True]) + assert_(test.fill_value == True) + + test = (a[0] == b) + assert_equal(test.data, [False, False]) + assert_equal(test.mask, [True, False]) + assert_(test.fill_value == True) + + test = (b == a[0]) + assert_equal(test.data, [False, False]) + assert_equal(test.mask, [True, False]) + assert_(test.fill_value == True) + + @pytest.mark.parametrize('dt', ['S', 'U']) + @pytest.mark.parametrize('fill', [None, 'A']) + def test_ne_for_strings(self, dt, fill): + # Test the equality of structured arrays + a = array(['a', 'b'], dtype=dt, mask=[0, 1], fill_value=fill) + + test = (a != a) + assert_equal(test.data, [False, False]) + assert_equal(test.mask, [False, True]) + assert_(test.fill_value == True) + + test = (a != a[0]) + assert_equal(test.data, [False, True]) + assert_equal(test.mask, [False, True]) + assert_(test.fill_value == True) + + b = array(['a', 'b'], dtype=dt, mask=[1, 0], fill_value=fill) + test = (a != b) + assert_equal(test.data, [True, True]) + assert_equal(test.mask, [True, True]) + assert_(test.fill_value == True) + + test = (a[0] != b) + assert_equal(test.data, [True, True]) + assert_equal(test.mask, [True, False]) + assert_(test.fill_value == True) + + test = (b != a[0]) + assert_equal(test.data, [True, True]) + assert_equal(test.mask, [True, False]) + assert_(test.fill_value == True) + + @pytest.mark.parametrize('dt1', num_dts, ids=num_ids) + @pytest.mark.parametrize('dt2', num_dts, ids=num_ids) + @pytest.mark.parametrize('fill', [None, 1]) + def test_eq_for_numeric(self, dt1, dt2, fill): + # Test the equality of structured arrays + a = array([0, 1], dtype=dt1, mask=[0, 1], fill_value=fill) + + test = (a == a) + assert_equal(test.data, [True, True]) + assert_equal(test.mask, [False, True]) + assert_(test.fill_value == True) + + test = (a == a[0]) + assert_equal(test.data, [True, False]) + assert_equal(test.mask, [False, True]) + assert_(test.fill_value == True) + + b = array([0, 1], dtype=dt2, mask=[1, 0], fill_value=fill) + test = (a == b) + assert_equal(test.data, [False, False]) + assert_equal(test.mask, [True, True]) + assert_(test.fill_value == True) + + test = (a[0] == b) + assert_equal(test.data, [False, False]) + assert_equal(test.mask, [True, False]) + assert_(test.fill_value == True) + + test = (b == a[0]) + assert_equal(test.data, [False, False]) + assert_equal(test.mask, [True, False]) + assert_(test.fill_value == True) + + @pytest.mark.parametrize('dt1', num_dts, ids=num_ids) + @pytest.mark.parametrize('dt2', num_dts, ids=num_ids) + @pytest.mark.parametrize('fill', [None, 1]) + def test_ne_for_numeric(self, dt1, dt2, fill): + # Test the equality of structured arrays + a = array([0, 1], dtype=dt1, mask=[0, 1], fill_value=fill) + + test = (a != a) + assert_equal(test.data, [False, False]) + assert_equal(test.mask, [False, True]) + assert_(test.fill_value == True) + + test = (a != a[0]) + assert_equal(test.data, [False, True]) + assert_equal(test.mask, [False, True]) + assert_(test.fill_value == True) + + b = array([0, 1], dtype=dt2, mask=[1, 0], fill_value=fill) + test = (a != b) + assert_equal(test.data, [True, True]) + assert_equal(test.mask, [True, True]) + assert_(test.fill_value == True) + + test = (a[0] != b) + assert_equal(test.data, [True, True]) + assert_equal(test.mask, [True, False]) + assert_(test.fill_value == True) + + test = (b != a[0]) + assert_equal(test.data, [True, True]) + assert_equal(test.mask, [True, False]) + assert_(test.fill_value == True) + def test_eq_with_None(self): # Really, comparisons with None should not be done, but check them # anyway. Note that pep8 will flag these tests. @@ -1553,7 +1804,7 @@ def test_eq_different_dimensions(self): assert_equal(test.mask, [[False, True], [False, True]]) - def test_numpyarithmetics(self): + def test_numpyarithmetic(self): # Check that the mask is not back-propagated when using numpy functions a = masked_array([-1, 0, 1, 2, 3], mask=[0, 0, 0, 0, 1]) control = masked_array([np.nan, np.nan, 0, np.log(2), -1], @@ -1570,7 +1821,7 @@ def test_numpyarithmetics(self): assert_equal(a.mask, [0, 0, 0, 0, 1]) -class TestMaskedArrayAttributes(object): +class TestMaskedArrayAttributes: def test_keepmask(self): # Tests the keep mask flag @@ -1746,7 +1997,7 @@ def assign(): assert_equal(m._mask, np.ma.nomask) -class TestFillingValues(object): +class TestFillingValues: def test_check_on_scalar(self): # Test _check_fill_value set to valid and invalid values @@ -1884,6 +2135,17 @@ def test_fillvalue(self): assert_equal(x.fill_value, 999.) assert_equal(x._fill_value, np.array(999.)) + def test_subarray_fillvalue(self): + # gh-10483 test multi-field index fill value + fields = array([(1, 1, 1)], + dtype=[('i', int), ('s', '|S8'), ('f', float)]) + with suppress_warnings() as sup: + sup.filter(FutureWarning, "Numpy has detected") + subfields = fields[['i', 'f']] + assert_equal(tuple(subfields.fill_value), (999999, 1.e+20)) + # test comparison does not raise: + subfields[1:] == subfields[:-1] + def test_fillvalue_exotic_dtype(self): # Tests yet more exotic flexible dtypes _check_fill_value = np.ma.core._check_fill_value @@ -2073,7 +2335,7 @@ def test_fillvalue_bytes_or_str(self): assert_equal(a["f1"].fill_value, default_fill_value("eggs")) -class TestUfuncs(object): +class TestUfuncs: # Test class for the application of ufuncs on MaskedArrays. def setup(self): @@ -2153,7 +2415,7 @@ def test_treatment_of_NotImplemented(self): assert_raises(TypeError, operator.mul, a, "abc") assert_raises(TypeError, operator.truediv, a, "abc") - class MyClass(object): + class MyClass: __array_priority__ = a.__array_priority__ + 1 def __mul__(self, other): @@ -2167,7 +2429,7 @@ def __rmul__(self, other): assert_(a * me == "My rmul") # and that __array_priority__ is respected - class MyClass2(object): + class MyClass2: __array_priority__ = 100 def __mul__(self, other): @@ -2217,8 +2479,8 @@ def test_no_masked_nan_warnings(self): # also check that allclose uses ma ufuncs, to avoid warning allclose(m, 0.5) -class TestMaskedArrayInPlaceArithmetics(object): - # Test MaskedArray Arithmetics +class TestMaskedArrayInPlaceArithmetic: + # Test MaskedArray Arithmetic def setup(self): x = arange(10) @@ -2245,9 +2507,9 @@ def test_inplace_addition_scalar(self): assert_equal(xm, y + 1) (x, _, xm) = self.floatdata - id1 = x.data.ctypes._data + id1 = x.data.ctypes.data x += 1. - assert_(id1 == x.data.ctypes._data) + assert_(id1 == x.data.ctypes.data) assert_equal(x, y + 1.) def test_inplace_addition_array(self): @@ -2510,7 +2772,7 @@ def test_inplace_addition_scalar_type(self): xm += t(1) assert_equal(xm, y + t(1)) - assert_equal(len(w), 0, "Failed on type=%s." % t) + assert_equal(len(w), 0, f'Failed on type={t}.') def test_inplace_addition_array_type(self): # Test of inplace additions @@ -2527,7 +2789,7 @@ def test_inplace_addition_array_type(self): assert_equal(xm, y + a) assert_equal(xm.mask, mask_or(m, a.mask)) - assert_equal(len(w), 0, "Failed on type=%s." % t) + assert_equal(len(w), 0, f'Failed on type={t}.') def test_inplace_subtraction_scalar_type(self): # Test of inplace subtractions @@ -2540,7 +2802,7 @@ def test_inplace_subtraction_scalar_type(self): xm -= t(1) assert_equal(xm, y - t(1)) - assert_equal(len(w), 0, "Failed on type=%s." % t) + assert_equal(len(w), 0, f'Failed on type={t}.') def test_inplace_subtraction_array_type(self): # Test of inplace subtractions @@ -2557,7 +2819,7 @@ def test_inplace_subtraction_array_type(self): assert_equal(xm, y - a) assert_equal(xm.mask, mask_or(m, a.mask)) - assert_equal(len(w), 0, "Failed on type=%s." % t) + assert_equal(len(w), 0, f'Failed on type={t}.') def test_inplace_multiplication_scalar_type(self): # Test of inplace multiplication @@ -2570,7 +2832,7 @@ def test_inplace_multiplication_scalar_type(self): xm *= t(2) assert_equal(xm, y * t(2)) - assert_equal(len(w), 0, "Failed on type=%s." % t) + assert_equal(len(w), 0, f'Failed on type={t}.') def test_inplace_multiplication_array_type(self): # Test of inplace multiplication @@ -2587,10 +2849,12 @@ def test_inplace_multiplication_array_type(self): assert_equal(xm, y * a) assert_equal(xm.mask, mask_or(m, a.mask)) - assert_equal(len(w), 0, "Failed on type=%s." % t) + assert_equal(len(w), 0, f'Failed on type={t}.') def test_inplace_floor_division_scalar_type(self): # Test of inplace division + # Check for TypeError in case of unsupported types + unsupported = {np.dtype(t).type for t in np.typecodes["Complex"]} for t in self.othertypes: with warnings.catch_warnings(record=True) as w: warnings.filterwarnings("always") @@ -2598,15 +2862,21 @@ def test_inplace_floor_division_scalar_type(self): x = arange(10, dtype=t) * t(2) xm = arange(10, dtype=t) * t(2) xm[2] = masked - x //= t(2) - xm //= t(2) - assert_equal(x, y) - assert_equal(xm, y) + try: + x //= t(2) + xm //= t(2) + assert_equal(x, y) + assert_equal(xm, y) - assert_equal(len(w), 0, "Failed on type=%s." % t) + assert_equal(len(w), 0, "Failed on type=%s." % t) + except TypeError: + msg = f"Supported type {t} throwing TypeError" + assert t in unsupported, msg def test_inplace_floor_division_array_type(self): # Test of inplace division + # Check for TypeError in case of unsupported types + unsupported = {np.dtype(t).type for t in np.typecodes["Complex"]} for t in self.othertypes: with warnings.catch_warnings(record=True) as w: warnings.filterwarnings("always") @@ -2614,16 +2884,20 @@ def test_inplace_floor_division_array_type(self): m = xm.mask a = arange(10, dtype=t) a[-1] = masked - x //= a - xm //= a - assert_equal(x, y // a) - assert_equal(xm, y // a) - assert_equal( - xm.mask, - mask_or(mask_or(m, a.mask), (a == t(0))) - ) + try: + x //= a + xm //= a + assert_equal(x, y // a) + assert_equal(xm, y // a) + assert_equal( + xm.mask, + mask_or(mask_or(m, a.mask), (a == t(0))) + ) - assert_equal(len(w), 0, "Failed on type=%s." % t) + assert_equal(len(w), 0, f'Failed on type={t}.') + except TypeError: + msg = f"Supported type {t} throwing TypeError" + assert t in unsupported, msg def test_inplace_division_scalar_type(self): # Test of inplace division @@ -2657,9 +2931,9 @@ def test_inplace_division_scalar_type(self): warnings.warn(str(e), stacklevel=1) if issubclass(t, np.integer): - assert_equal(len(sup.log), 2, "Failed on type=%s." % t) + assert_equal(len(sup.log), 2, f'Failed on type={t}.') else: - assert_equal(len(sup.log), 0, "Failed on type=%s." % t) + assert_equal(len(sup.log), 0, f'Failed on type={t}.') def test_inplace_division_array_type(self): # Test of inplace division @@ -2696,9 +2970,9 @@ def test_inplace_division_array_type(self): warnings.warn(str(e), stacklevel=1) if issubclass(t, np.integer): - assert_equal(len(sup.log), 2, "Failed on type=%s." % t) + assert_equal(len(sup.log), 2, f'Failed on type={t}.') else: - assert_equal(len(sup.log), 0, "Failed on type=%s." % t) + assert_equal(len(sup.log), 0, f'Failed on type={t}.') def test_inplace_pow_type(self): # Test keeping data w/ (inplace) power @@ -2716,10 +2990,10 @@ def test_inplace_pow_type(self): assert_equal(x.data, xx_r.data) assert_equal(x.mask, xx_r.mask) - assert_equal(len(w), 0, "Failed on type=%s." % t) + assert_equal(len(w), 0, f'Failed on type={t}.') -class TestMaskedArrayMethods(object): +class TestMaskedArrayMethods: # Test class for miscellaneous MaskedArrays methods. def setup(self): # Base data definition. @@ -2795,6 +3069,13 @@ def test_allclose(self): a = masked_array([np.iinfo(np.int_).min], dtype=np.int_) assert_(allclose(a, a)) + def test_allclose_timedelta(self): + # Allclose currently works for timedelta64 as long as `atol` is + # an integer or also a timedelta64 + a = np.array([[1, 2, 3, 4]], dtype="m8[ns]") + assert allclose(a, a, atol=0) + assert allclose(a, a, atol=np.timedelta64(1, "ns")) + def test_allany(self): # Checks the any/all methods/functions. x = np.array([[0.13, 0.26, 0.90], @@ -2879,6 +3160,13 @@ def test_clip(self): assert_equal(clipped._data, x.clip(2, 8)) assert_equal(clipped._data, mx._data.clip(2, 8)) + def test_clip_out(self): + # gh-14140 + a = np.arange(10) + m = np.ma.MaskedArray(a, mask=[0, 1] * 5) + m.clip(0, 5, out=m) + assert_equal(m.mask, [0, 1] * 5) + def test_compress(self): # test compress a = masked_array([1., 2., 3., 4., 5.], fill_value=9999) @@ -2941,6 +3229,50 @@ def test_empty(self): b = a.view(masked_array) assert_(np.may_share_memory(a.mask, b.mask)) + def test_zeros(self): + # Tests zeros/like + datatype = [('a', int), ('b', float), ('c', '|S8')] + a = masked_array([(1, 1.1, '1.1'), (2, 2.2, '2.2'), (3, 3.3, '3.3')], + dtype=datatype) + assert_equal(len(a.fill_value.item()), len(datatype)) + + b = zeros(len(a), dtype=datatype) + assert_equal(b.shape, a.shape) + assert_equal(b.fill_value, a.fill_value) + + b = zeros_like(a) + assert_equal(b.shape, a.shape) + assert_equal(b.fill_value, a.fill_value) + + # check zeros_like mask handling + a = masked_array([1, 2, 3], mask=[False, True, False]) + b = zeros_like(a) + assert_(not np.may_share_memory(a.mask, b.mask)) + b = a.view() + assert_(np.may_share_memory(a.mask, b.mask)) + + def test_ones(self): + # Tests ones/like + datatype = [('a', int), ('b', float), ('c', '|S8')] + a = masked_array([(1, 1.1, '1.1'), (2, 2.2, '2.2'), (3, 3.3, '3.3')], + dtype=datatype) + assert_equal(len(a.fill_value.item()), len(datatype)) + + b = ones(len(a), dtype=datatype) + assert_equal(b.shape, a.shape) + assert_equal(b.fill_value, a.fill_value) + + b = ones_like(a) + assert_equal(b.shape, a.shape) + assert_equal(b.fill_value, a.fill_value) + + # check ones_like mask handling + a = masked_array([1, 2, 3], mask=[False, True, False]) + b = ones_like(a) + assert_(not np.may_share_memory(a.mask, b.mask)) + b = a.view() + assert_(np.may_share_memory(a.mask, b.mask)) + @suppress_copy_mask_on_assignment def test_put(self): # Tests put. @@ -3090,6 +3422,10 @@ def test_sort(self): assert_equal(sortedx._data, [1, 2, -2, -1, 0]) assert_equal(sortedx._mask, [1, 1, 0, 0, 0]) + x = array([0, -1], dtype=np.int8) + sortedx = sort(x, kind="stable") + assert_equal(sortedx, array([-1, 0], dtype=np.int8)) + def test_stable_sort(self): x = array([1, 2, 3, 1, 2, 3], dtype=np.uint8) expected = array([0, 3, 1, 4, 2, 5]) @@ -3174,18 +3510,13 @@ def test_sort_flexible(self): assert_equal(test.mask, mask_first.mask) # Test sort on dtype with subarray (gh-8069) + # Just check that the sort does not error, structured array subarrays + # are treated as byte strings and that leads to differing behavior + # depending on endianness and `endwith`. dt = np.dtype([('v', int, 2)]) a = a.view(dt) - mask_last = mask_last.view(dt) - mask_first = mask_first.view(dt) - test = sort(a) - assert_equal(test, mask_last) - assert_equal(test.mask, mask_last.mask) - test = sort(a, endwith=False) - assert_equal(test, mask_first) - assert_equal(test.mask, mask_first.mask) def test_argsort(self): # Test argsort @@ -3424,7 +3755,7 @@ def test_diagonal_view(self): assert_equal(xd.data, x.diagonal().data) -class TestMaskedArrayMathMethods(object): +class TestMaskedArrayMathMethods: def setup(self): # Base data definition. @@ -3532,6 +3863,30 @@ def test_meananom_object(self): assert_equal(a.mean(), 2) assert_equal(a.anom(), [-1, 0, 1]) + def test_anom_shape(self): + a = masked_array([1, 2, 3]) + assert_equal(a.anom().shape, a.shape) + a.mask = True + assert_equal(a.anom().shape, a.shape) + assert_(np.ma.is_masked(a.anom())) + + def test_anom(self): + a = masked_array(np.arange(1, 7).reshape(2, 3)) + assert_almost_equal(a.anom(), + [[-2.5, -1.5, -0.5], [0.5, 1.5, 2.5]]) + assert_almost_equal(a.anom(axis=0), + [[-1.5, -1.5, -1.5], [1.5, 1.5, 1.5]]) + assert_almost_equal(a.anom(axis=1), + [[-1., 0., 1.], [-1., 0., 1.]]) + a.mask = [[0, 0, 1], [0, 1, 0]] + mval = -99 + assert_almost_equal(a.anom().filled(mval), + [[-2.25, -1.25, mval], [0.75, mval, 2.75]]) + assert_almost_equal(a.anom(axis=0).filled(mval), + [[-1.5, 0.0, mval], [1.5, mval, 0.0]]) + assert_almost_equal(a.anom(axis=1).filled(mval), + [[-0.5, 0.5, mval], [-1.0, mval, 1.0]]) + def test_trace(self): # Tests trace on MaskedArrays. (x, X, XX, m, mx, mX, mXX, m2x, m2X, m2XX) = self.d @@ -3618,8 +3973,6 @@ def test_varstd(self): assert_almost_equal(np.sqrt(mXvar0[k]), mX[:, k].compressed().std()) - @pytest.mark.skipif(sys.platform=='win32' and sys.version_info < (3, 6), - reason='Fails on Python < 3.6 on Windows, gh-9671') @suppress_copy_mask_on_assignment def test_varstd_specialcases(self): # Test a special case for var @@ -3702,7 +4055,7 @@ def test_axis_methods_nomask(self): assert_equal(a.max(1), [3, 6]) -class TestMaskedArrayMathMethodsComplex(object): +class TestMaskedArrayMathMethodsComplex: # Test class for miscellaneous MaskedArrays methods. def setup(self): # Base data definition. @@ -3755,7 +4108,7 @@ def test_varstd(self): mX[:, k].compressed().std()) -class TestMaskedArrayFunctions(object): +class TestMaskedArrayFunctions: # Test class for miscellaneous functions. def setup(self): @@ -3814,12 +4167,8 @@ def test_masked_where_oddities(self): def test_masked_where_shape_constraint(self): a = arange(10) - try: - test = masked_equal(1, a) - except IndexError: - pass - else: - raise AssertionError("Should have failed...") + with assert_raises(IndexError): + masked_equal(1, a) test = masked_equal(a, 1) assert_equal(test.mask, [0, 1, 0, 0, 0, 0, 0, 0, 0, 0]) @@ -4367,7 +4716,7 @@ class A(np.ndarray): class M(MaskedArray): pass - test = np.ma.compressed(M(shape=(0,1,2))) + test = np.ma.compressed(M([[[]], [[]]])) assert_equal(test.ndim, 1) # with .compressed() overridden @@ -4375,7 +4724,7 @@ class M(MaskedArray): def compressed(self): return 42 - test = np.ma.compressed(M(shape=(0,1,2))) + test = np.ma.compressed(M([[[]], [[]]])) assert_equal(test, 42) def test_convolve(self): @@ -4398,7 +4747,7 @@ def test_convolve(self): assert_equal(test, masked_equal([-1, -1, -1, -1, -1], -1)) -class TestMaskedFields(object): +class TestMaskedFields: def setup(self): ilist = [1, 2, 3, 4, 5] @@ -4560,7 +4909,7 @@ def test_element_len(self): assert_equal(len(rec), len(self.data['ddtype'])) -class TestMaskedObjectArray(object): +class TestMaskedObjectArray: def test_getitem(self): arr = np.ma.array([None, None]) @@ -4608,7 +4957,7 @@ def test_nested_ma(self): assert_(arr[0] is np.ma.masked) -class TestMaskedView(object): +class TestMaskedView: def setup(self): iterator = list(zip(np.arange(10), np.random.rand(10))) @@ -4686,7 +5035,7 @@ def test_view_to_dtype_and_type(self): assert_(not isinstance(test, MaskedArray)) -class TestOptionalArgs(object): +class TestOptionalArgs: def test_ndarrayfuncs(self): # test axis arg behaves the same as ndarray (including multiple axes) @@ -4773,7 +5122,7 @@ def test_count(self): assert_raises(np.AxisError, count, np.ma.array(1), axis=1) -class TestMaskedConstant(object): +class TestMaskedConstant: def _do_add_test(self, add): # sanity check assert_(add(np.ma.masked, 1) is np.ma.masked) @@ -4813,13 +5162,13 @@ def test_repr(self): def test_pickle(self): from io import BytesIO - import pickle - with BytesIO() as f: - pickle.dump(np.ma.masked, f) - f.seek(0) - res = pickle.load(f) - assert_(res is np.ma.masked) + for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): + with BytesIO() as f: + pickle.dump(np.ma.masked, f, protocol=proto) + f.seek(0) + res = pickle.load(f) + assert_(res is np.ma.masked) def test_copy(self): # gh-9328 @@ -4854,11 +5203,6 @@ def test_coercion_int(self): assert_raises(MaskError, operator.setitem, a_i, (), np.ma.masked) assert_raises(MaskError, int, np.ma.masked) - @pytest.mark.skipif(sys.version_info.major == 3, - reason="long doesn't exist in Python 3") - def test_coercion_long(self): - assert_raises(MaskError, long, np.ma.masked) - def test_coercion_float(self): a_f = np.zeros((), float) assert_warns(UserWarning, operator.setitem, a_f, (), np.ma.masked) @@ -4890,7 +5234,7 @@ def test_attributes_readonly(self): assert_raises(AttributeError, setattr, np.ma.masked, 'dtype', np.int64) -class TestMaskedWhereAliases(object): +class TestMaskedWhereAliases: # TODO: Test masked_object, masked_equal, ... @@ -4915,6 +5259,16 @@ def test_masked_array(): a = np.ma.array([0, 1, 2, 3], mask=[0, 0, 1, 0]) assert_equal(np.argwhere(a), [[1], [3]]) +def test_masked_array_no_copy(): + # check nomask array is updated in place + a = np.ma.array([1, 2, 3, 4]) + _ = np.ma.masked_where(a == 3, a, copy=False) + assert_array_equal(a.mask, [False, False, True, False]) + # check masked array is updated in place + a = np.ma.array([1, 2, 3, 4], mask=[1, 0, 0, 0]) + _ = np.ma.masked_where(a == 3, a, copy=False) + assert_array_equal(a.mask, [True, False, True, False]) + def test_append_masked_array(): a = np.ma.masked_equal([1,2,3], value=2) b = np.ma.masked_equal([4,3,2], value=2) @@ -4953,7 +5307,6 @@ def test_append_masked_array_along_axis(): assert_array_equal(result.data, expected.data) assert_array_equal(result.mask, expected.mask) - def test_default_fill_value_complex(): # regression test for Python 3, where 'unicode' was not defined assert_(default_fill_value(1 + 1j) == 1.e20 + 0.0j) @@ -4992,7 +5345,7 @@ def test_ufunc_with_out_varied(): assert_equal(res_pos.data, expected.data) -def test_astype(): +def test_astype_mask_ordering(): descr = [('v', int, 3), ('x', [('y', float)])] x = array([ [([1, 2, 3], (1.0,)), ([1, 2, 3], (2.0,))], @@ -5022,3 +5375,74 @@ def test_astype(): x_f2 = np.array(x, dtype=x.dtype, order='F', subok=True) assert_(x_f2.flags.f_contiguous) assert_(x_f2.mask.flags.f_contiguous) + + +@pytest.mark.parametrize('dt1', num_dts, ids=num_ids) +@pytest.mark.parametrize('dt2', num_dts, ids=num_ids) +@pytest.mark.filterwarnings('ignore::numpy.ComplexWarning') +def test_astype_basic(dt1, dt2): + # See gh-12070 + src = np.ma.array(ones(3, dt1), fill_value=1) + dst = src.astype(dt2) + + assert_(src.fill_value == 1) + assert_(src.dtype == dt1) + assert_(src.fill_value.dtype == dt1) + + assert_(dst.fill_value == 1) + assert_(dst.dtype == dt2) + assert_(dst.fill_value.dtype == dt2) + + assert_equal(src, dst) + + +def test_fieldless_void(): + dt = np.dtype([]) # a void dtype with no fields + x = np.empty(4, dt) + + # these arrays contain no values, so there's little to test - but this + # shouldn't crash + mx = np.ma.array(x) + assert_equal(mx.dtype, x.dtype) + assert_equal(mx.shape, x.shape) + + mx = np.ma.array(x, mask=x) + assert_equal(mx.dtype, x.dtype) + assert_equal(mx.shape, x.shape) + + +def test_mask_shape_assignment_does_not_break_masked(): + a = np.ma.masked + b = np.ma.array(1, mask=a.mask) + b.shape = (1,) + assert_equal(a.mask.shape, ()) + +@pytest.mark.skipif(sys.flags.optimize > 1, + reason="no docstrings present to inspect when PYTHONOPTIMIZE/Py_OptimizeFlag > 1") +def test_doc_note(): + def method(self): + """This docstring + + Has multiple lines + + And notes + + Notes + ----- + original note + """ + pass + + expected_doc = """This docstring + +Has multiple lines + +And notes + +Notes +----- +note + +original note""" + + assert_equal(np.ma.core.doc_note(method.__doc__, "note"), expected_doc) diff --git a/numpy/ma/tests/test_deprecations.py b/numpy/ma/tests/test_deprecations.py index 72cc29aa046e..3e0e09fdd1c8 100644 --- a/numpy/ma/tests/test_deprecations.py +++ b/numpy/ma/tests/test_deprecations.py @@ -1,14 +1,15 @@ """Test deprecation and future warnings. """ -from __future__ import division, absolute_import, print_function - +import pytest import numpy as np from numpy.testing import assert_warns from numpy.ma.testutils import assert_equal from numpy.ma.core import MaskedArrayFutureWarning +import io +import textwrap -class TestArgsort(object): +class TestArgsort: """ gh-8701 """ def _test_base(self, argsort, cls): arr_0d = np.array(1).view(cls) @@ -37,7 +38,7 @@ def test_method(self): return self._test_base(np.ma.MaskedArray.argsort, np.ma.MaskedArray) -class TestMinimumMaximum(object): +class TestMinimumMaximum: def test_minimum(self): assert_warns(DeprecationWarning, np.ma.minimum, np.ma.array([1, 2])) @@ -68,3 +69,21 @@ def test_axis_default(self): result = ma_max(data1d) assert_equal(result, ma_max(data1d, axis=None)) assert_equal(result, ma_max(data1d, axis=0)) + + +class TestFromtextfile: + def test_fromtextfile_delimitor(self): + # NumPy 1.22.0, 2021-09-23 + + textfile = io.StringIO(textwrap.dedent( + """ + A,B,C,D + 'string 1';1;1.0;'mixed column' + 'string 2';2;2.0; + 'string 3';3;3.0;123 + 'string 4';4;4.0;3.14 + """ + )) + + with pytest.warns(DeprecationWarning): + result = np.ma.mrecords.fromtextfile(textfile, delimitor=';') diff --git a/numpy/ma/tests/test_extras.py b/numpy/ma/tests/test_extras.py index c29bec2bdf84..e735b9bc77fa 100644 --- a/numpy/ma/tests/test_extras.py +++ b/numpy/ma/tests/test_extras.py @@ -7,14 +7,13 @@ :version: $Id: test_extras.py 3473 2007-10-29 15:18:13Z jarrod.millman $ """ -from __future__ import division, absolute_import, print_function - import warnings import itertools +import pytest import numpy as np from numpy.testing import ( - assert_warns, suppress_warnings, assert_raises, + assert_warns, suppress_warnings ) from numpy.ma.testutils import ( assert_, assert_array_equal, assert_equal, assert_almost_equal @@ -29,12 +28,11 @@ ediff1d, apply_over_axes, apply_along_axis, compress_nd, compress_rowcols, mask_rowcols, clump_masked, clump_unmasked, flatnotmasked_contiguous, notmasked_contiguous, notmasked_edges, masked_all, masked_all_like, isin, - diagflat, stack, vstack, hstack + diagflat, stack, vstack ) -import numpy.ma.extras as mae -class TestGeneric(object): +class TestGeneric: # def test_masked_all(self): # Tests masked_all @@ -66,6 +64,28 @@ def test_masked_all(self): control = array([[(1, (1, 1))]], mask=[[(1, (1, 1))]], dtype=dt) assert_equal(test, control) + def test_masked_all_with_object_nested(self): + # Test masked_all works with nested array with dtype of an 'object' + # refers to issue #15895 + my_dtype = np.dtype([('b', ([('c', object)], (1,)))]) + masked_arr = np.ma.masked_all((1,), my_dtype) + + assert_equal(type(masked_arr['b']), np.ma.core.MaskedArray) + assert_equal(type(masked_arr['b']['c']), np.ma.core.MaskedArray) + assert_equal(len(masked_arr['b']['c']), 1) + assert_equal(masked_arr['b']['c'].shape, (1, 1)) + assert_equal(masked_arr['b']['c']._fill_value.shape, ()) + + def test_masked_all_with_object(self): + # same as above except that the array is not nested + my_dtype = np.dtype([('b', (object, (1,)))]) + masked_arr = np.ma.masked_all((1,), my_dtype) + + assert_equal(type(masked_arr['b']), np.ma.core.MaskedArray) + assert_equal(len(masked_arr['b']), 1) + assert_equal(masked_arr['b'].shape, (1, 1)) + assert_equal(masked_arr['b']._fill_value.shape, ()) + def test_masked_all_like(self): # Tests masked_all # Standard dtype @@ -142,14 +162,14 @@ def test_flatnotmasked_contiguous(self): assert_equal(test, []) -class TestAverage(object): +class TestAverage: # Several tests of average. Why so many ? Good point... def test_testAverage1(self): # Test of average. ott = array([0., 1., 2., 3.], mask=[True, False, False, False]) assert_equal(2.0, average(ott, axis=0)) assert_equal(2.0, average(ott, weights=[1., 1., 2., 1.])) - result, wts = average(ott, weights=[1., 1., 2., 1.], returned=1) + result, wts = average(ott, weights=[1., 1., 2., 1.], returned=True) assert_equal(2.0, result) assert_(wts == 4.0) ott[:] = masked @@ -160,7 +180,7 @@ def test_testAverage1(self): assert_equal(average(ott, axis=0), [2.0, 0.0]) assert_equal(average(ott, axis=1).mask[0], [True]) assert_equal([2., 0.], average(ott, axis=0)) - result, wts = average(ott, axis=0, returned=1) + result, wts = average(ott, axis=0, returned=True) assert_equal(wts, [1., 0.]) def test_testAverage2(self): @@ -201,14 +221,14 @@ def test_testAverage3(self): # Yet more tests of average! a = arange(6) b = arange(6) * 3 - r1, w1 = average([[a, b], [b, a]], axis=1, returned=1) + r1, w1 = average([[a, b], [b, a]], axis=1, returned=True) assert_equal(shape(r1), shape(w1)) assert_equal(r1.shape, w1.shape) - r2, w2 = average(ones((2, 2, 3)), axis=0, weights=[3, 1], returned=1) + r2, w2 = average(ones((2, 2, 3)), axis=0, weights=[3, 1], returned=True) assert_equal(shape(w2), shape(r2)) - r2, w2 = average(ones((2, 2, 3)), returned=1) + r2, w2 = average(ones((2, 2, 3)), returned=True) assert_equal(shape(w2), shape(r2)) - r2, w2 = average(ones((2, 2, 3)), weights=ones((2, 2, 3)), returned=1) + r2, w2 = average(ones((2, 2, 3)), weights=ones((2, 2, 3)), returned=True) assert_equal(shape(w2), shape(r2)) a2d = array([[1, 2], [0, 4]], float) a2dm = masked_array(a2d, [[False, False], [True, False]]) @@ -272,8 +292,25 @@ def test_complex(self): assert_almost_equal(wav1.real, expected1.real) assert_almost_equal(wav1.imag, expected1.imag) + def test_masked_weights(self): + # Test with masked weights. + # (Regression test for https://github.com/numpy/numpy/issues/10438) + a = np.ma.array(np.arange(9).reshape(3, 3), + mask=[[1, 0, 0], [1, 0, 0], [0, 0, 0]]) + weights_unmasked = masked_array([5, 28, 31], mask=False) + weights_masked = masked_array([5, 28, 31], mask=[1, 0, 0]) + + avg_unmasked = average(a, axis=0, + weights=weights_unmasked, returned=False) + expected_unmasked = np.array([6.0, 5.21875, 6.21875]) + assert_almost_equal(avg_unmasked, expected_unmasked) + + avg_masked = average(a, axis=0, weights=weights_masked, returned=False) + expected_masked = np.array([6.0, 5.576271186440678, 6.576271186440678]) + assert_almost_equal(avg_masked, expected_masked) -class TestConcatenator(object): + +class TestConcatenator: # Tests for mr_, the equivalent of r_ for masked arrays. def test_1d(self): @@ -317,7 +354,7 @@ def test_masked_constant(self): assert_equal(actual.data[:2], [1, 2]) -class TestNotMasked(object): +class TestNotMasked: # Tests notmasked_edges and notmasked_contiguous. def test_edges(self): @@ -387,7 +424,7 @@ def test_contiguous(self): ]) -class TestCompressFunctions(object): +class TestCompressFunctions: def test_compress_nd(self): # Tests compress_nd @@ -553,6 +590,18 @@ def test_mask_rowcols(self): assert_(mask_rowcols(x, 0).mask.all()) assert_(mask_rowcols(x, 1).mask.all()) + @pytest.mark.parametrize("axis", [None, 0, 1]) + @pytest.mark.parametrize(["func", "rowcols_axis"], + [(np.ma.mask_rows, 0), (np.ma.mask_cols, 1)]) + def test_mask_row_cols_axis_deprecation(self, axis, func, rowcols_axis): + # Test deprecation of the axis argument to `mask_rows` and `mask_cols` + x = array(np.arange(9).reshape(3, 3), + mask=[[1, 0, 0], [0, 0, 0], [0, 0, 0]]) + + with assert_warns(DeprecationWarning): + res = func(x, axis=axis) + assert_equal(res, mask_rowcols(x, rowcols_axis)) + def test_dot(self): # Tests dot product n = np.arange(1, 7) @@ -640,7 +689,7 @@ def test_dot_out(self): assert_equal(a, res) -class TestApplyAlongAxis(object): +class TestApplyAlongAxis: # Tests 2D functions def test_3d(self): a = arange(12.).reshape(2, 2, 3) @@ -662,7 +711,7 @@ def myfunc(b, offset=0): assert_equal(xa, [[2, 5], [8, 11]]) -class TestApplyOverAxes(object): +class TestApplyOverAxes: # Tests apply_over_axes def test_basic(self): a = arange(24).reshape(2, 3, 4) @@ -675,7 +724,7 @@ def test_basic(self): assert_equal(test, ctrl) -class TestMedian(object): +class TestMedian: def test_pytype(self): r = np.ma.median([[np.inf, np.inf], [np.inf, np.inf]], axis=-1) assert_equal(r, np.inf) @@ -892,61 +941,51 @@ def test_single_non_masked_value_on_axis(self): expected) def test_nan(self): - with suppress_warnings() as w: - w.record(RuntimeWarning) - for mask in (False, np.zeros(6, dtype=bool)): - dm = np.ma.array([[1, np.nan, 3], [1, 2, 3]]) - dm.mask = mask - - # scalar result - r = np.ma.median(dm, axis=None) - assert_(np.isscalar(r)) - assert_array_equal(r, np.nan) - r = np.ma.median(dm.ravel(), axis=0) - assert_(np.isscalar(r)) - assert_array_equal(r, np.nan) - - r = np.ma.median(dm, axis=0) - assert_equal(type(r), MaskedArray) - assert_array_equal(r, [1, np.nan, 3]) - r = np.ma.median(dm, axis=1) - assert_equal(type(r), MaskedArray) - assert_array_equal(r, [np.nan, 2]) - r = np.ma.median(dm, axis=-1) - assert_equal(type(r), MaskedArray) - assert_array_equal(r, [np.nan, 2]) - + for mask in (False, np.zeros(6, dtype=bool)): dm = np.ma.array([[1, np.nan, 3], [1, 2, 3]]) - dm[:, 2] = np.ma.masked - assert_array_equal(np.ma.median(dm, axis=None), np.nan) - assert_array_equal(np.ma.median(dm, axis=0), [1, np.nan, 3]) - assert_array_equal(np.ma.median(dm, axis=1), [np.nan, 1.5]) - assert_equal([x.category is RuntimeWarning for x in w.log], - [True]*13) + dm.mask = mask + + # scalar result + r = np.ma.median(dm, axis=None) + assert_(np.isscalar(r)) + assert_array_equal(r, np.nan) + r = np.ma.median(dm.ravel(), axis=0) + assert_(np.isscalar(r)) + assert_array_equal(r, np.nan) + + r = np.ma.median(dm, axis=0) + assert_equal(type(r), MaskedArray) + assert_array_equal(r, [1, np.nan, 3]) + r = np.ma.median(dm, axis=1) + assert_equal(type(r), MaskedArray) + assert_array_equal(r, [np.nan, 2]) + r = np.ma.median(dm, axis=-1) + assert_equal(type(r), MaskedArray) + assert_array_equal(r, [np.nan, 2]) + + dm = np.ma.array([[1, np.nan, 3], [1, 2, 3]]) + dm[:, 2] = np.ma.masked + assert_array_equal(np.ma.median(dm, axis=None), np.nan) + assert_array_equal(np.ma.median(dm, axis=0), [1, np.nan, 3]) + assert_array_equal(np.ma.median(dm, axis=1), [np.nan, 1.5]) def test_out_nan(self): - with warnings.catch_warnings(record=True): - warnings.filterwarnings('always', '', RuntimeWarning) - o = np.ma.masked_array(np.zeros((4,))) - d = np.ma.masked_array(np.ones((3, 4))) - d[2, 1] = np.nan - d[2, 2] = np.ma.masked - assert_equal(np.ma.median(d, 0, out=o), o) - o = np.ma.masked_array(np.zeros((3,))) - assert_equal(np.ma.median(d, 1, out=o), o) - o = np.ma.masked_array(np.zeros(())) - assert_equal(np.ma.median(d, out=o), o) + o = np.ma.masked_array(np.zeros((4,))) + d = np.ma.masked_array(np.ones((3, 4))) + d[2, 1] = np.nan + d[2, 2] = np.ma.masked + assert_equal(np.ma.median(d, 0, out=o), o) + o = np.ma.masked_array(np.zeros((3,))) + assert_equal(np.ma.median(d, 1, out=o), o) + o = np.ma.masked_array(np.zeros(())) + assert_equal(np.ma.median(d, out=o), o) def test_nan_behavior(self): a = np.ma.masked_array(np.arange(24, dtype=float)) a[::3] = np.ma.masked a[2] = np.nan - with suppress_warnings() as w: - w.record(RuntimeWarning) - assert_array_equal(np.ma.median(a), np.nan) - assert_array_equal(np.ma.median(a, axis=0), np.nan) - assert_(w.log[0].category is RuntimeWarning) - assert_(w.log[1].category is RuntimeWarning) + assert_array_equal(np.ma.median(a), np.nan) + assert_array_equal(np.ma.median(a, axis=0), np.nan) a = np.ma.masked_array(np.arange(24, dtype=float).reshape(2, 3, 4)) a.mask = np.arange(a.size) % 2 == 1 @@ -955,39 +994,26 @@ def test_nan_behavior(self): a[1, 1, 2] = np.nan # no axis - with suppress_warnings() as w: - w.record(RuntimeWarning) - warnings.filterwarnings('always', '', RuntimeWarning) - assert_array_equal(np.ma.median(a), np.nan) - assert_(np.isscalar(np.ma.median(a))) - assert_(w.log[0].category is RuntimeWarning) + assert_array_equal(np.ma.median(a), np.nan) + assert_(np.isscalar(np.ma.median(a))) # axis0 b = np.ma.median(aorig, axis=0) b[2, 3] = np.nan b[1, 2] = np.nan - with warnings.catch_warnings(record=True) as w: - warnings.filterwarnings('always', '', RuntimeWarning) - assert_equal(np.ma.median(a, 0), b) - assert_equal(len(w), 1) + assert_equal(np.ma.median(a, 0), b) # axis1 b = np.ma.median(aorig, axis=1) b[1, 3] = np.nan b[1, 2] = np.nan - with warnings.catch_warnings(record=True) as w: - warnings.filterwarnings('always', '', RuntimeWarning) - assert_equal(np.ma.median(a, 1), b) - assert_equal(len(w), 1) + assert_equal(np.ma.median(a, 1), b) # axis02 b = np.ma.median(aorig, axis=(0, 2)) b[1] = np.nan b[2] = np.nan - with warnings.catch_warnings(record=True) as w: - warnings.filterwarnings('always', '', RuntimeWarning) - assert_equal(np.ma.median(a, (0, 2)), b) - assert_equal(len(w), 1) + assert_equal(np.ma.median(a, (0, 2)), b) def test_ambigous_fill(self): # 255 is max value, used as filler for sort @@ -1077,7 +1103,7 @@ def test_object(self): assert_(type(np.ma.median(o.astype(object))), float) -class TestCov(object): +class TestCov: def setup(self): self.data = array(np.random.rand(12)) @@ -1144,7 +1170,7 @@ def test_2d_with_missing(self): x.shape[0] / frac)) -class TestCorrcoef(object): +class TestCorrcoef: def setup(self): self.data = array(np.random.rand(12)) @@ -1251,7 +1277,7 @@ def test_2d_with_missing(self): control[:-1, :-1]) -class TestPolynomial(object): +class TestPolynomial: # def test_polyfit(self): # Tests polyfit @@ -1309,7 +1335,7 @@ def test_polyfit_with_masked_NaNs(self): assert_almost_equal(a, a_) -class TestArraySetOps(object): +class TestArraySetOps: def test_unique_onlist(self): # Test unique on list @@ -1541,7 +1567,7 @@ def test_setdiff1d_char_array(self): assert_array_equal(setdiff1d(a, b), np.array(['c'])) -class TestShapeBase(object): +class TestShapeBase: def test_atleast_2d(self): # Test atleast_2d @@ -1597,7 +1623,7 @@ def test_shape_scalar(self): assert_equal(b.mask.shape, b.data.shape) -class TestStack(object): +class TestStack: def test_stack_1d(self): a = masked_array([0, 1, 2], mask=[0, 1, 0]) diff --git a/numpy/ma/tests/test_mrecords.py b/numpy/ma/tests/test_mrecords.py index e08dc1326c14..4b2c01df9947 100644 --- a/numpy/ma/tests/test_mrecords.py +++ b/numpy/ma/tests/test_mrecords.py @@ -5,11 +5,6 @@ :contact: pierregm_at_uga_dot_edu """ -from __future__ import division, absolute_import, print_function - -import warnings -import pickle - import numpy as np import numpy.ma as ma from numpy import recarray @@ -26,9 +21,10 @@ assert_, assert_equal, assert_equal_records, ) +from numpy.compat import pickle -class TestMRecords(object): +class TestMRecords: ilist = [1, 2, 3, 4, 5] flist = [1.1, 2.2, 3.3, 4.4, 5.5] @@ -288,12 +284,13 @@ def test_pickling(self): # Test pickling base = self.base.copy() mrec = base.view(mrecarray) - _ = pickle.dumps(mrec) - mrec_ = pickle.loads(_) - assert_equal(mrec_.dtype, mrec.dtype) - assert_equal_records(mrec_._data, mrec._data) - assert_equal(mrec_._mask, mrec._mask) - assert_equal_records(mrec_._mask, mrec._mask) + for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): + _ = pickle.dumps(mrec, protocol=proto) + mrec_ = pickle.loads(_) + assert_equal(mrec_.dtype, mrec.dtype) + assert_equal_records(mrec_._data, mrec._data) + assert_equal(mrec_._mask, mrec._mask) + assert_equal_records(mrec_._mask, mrec._mask) def test_filled(self): # Test filling the array @@ -349,7 +346,7 @@ def test_exotic_formats(self): dtype=mult.dtype)) -class TestView(object): +class TestView: def setup(self): (a, b) = (np.arange(10), np.random.rand(10)) @@ -387,7 +384,7 @@ def test_view_flexible_type(self): ############################################################################## -class TestMRecordsImport(object): +class TestMRecordsImport: _a = ma.array([1, 2, 3], mask=[0, 0, 1], dtype=int) _b = ma.array([1.1, 2.2, 3.3], mask=[0, 0, 1], dtype=float) @@ -408,7 +405,7 @@ def test_fromarrays(self): for (f, l) in zip(('a', 'b', 'c'), (_a, _b, _c)): assert_equal(getattr(mrec, f)._mask, l._mask) # One record only - _x = ma.array([1, 1.1, 'one'], mask=[1, 0, 0],) + _x = ma.array([1, 1.1, 'one'], mask=[1, 0, 0], dtype=object) assert_equal_records(fromarrays(_x, dtype=mrec.dtype), mrec[0]) def test_fromrecords(self): @@ -471,7 +468,7 @@ def test_fromtextfile(self): with temppath() as path: with open(path, 'w') as f: f.write(fcontent) - mrectxt = fromtextfile(path, delimitor=',', varnames='ABCDEFG') + mrectxt = fromtextfile(path, delimiter=',', varnames='ABCDEFG') assert_(isinstance(mrectxt, MaskedRecords)) assert_equal(mrectxt.F, [1, 1, 1, 1]) assert_equal(mrectxt.E._mask, [1, 1, 1, 1]) diff --git a/numpy/ma/tests/test_old_ma.py b/numpy/ma/tests/test_old_ma.py index d7b1e3c18b0e..2b3034f9cb2b 100644 --- a/numpy/ma/tests/test_old_ma.py +++ b/numpy/ma/tests/test_old_ma.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - from functools import reduce import numpy as np @@ -8,7 +6,6 @@ from numpy.testing import ( assert_, assert_raises, assert_equal, ) -from numpy.ma.testutils import assert_array_equal from numpy.ma import ( MaskType, MaskedArray, absolute, add, all, allclose, allequal, alltrue, arange, arccos, arcsin, arctan, arctan2, array, average, choose, @@ -22,6 +19,7 @@ repeat, resize, shape, sin, sinh, sometrue, sort, sqrt, subtract, sum, take, tan, tanh, transpose, where, zeros, ) +from numpy.compat import pickle pi = np.pi @@ -29,11 +27,11 @@ def eq(v, w, msg=''): result = allclose(v, w) if not result: - print("Not eq:%s\n%s\n----%s" % (msg, str(v), str(w))) + print(f'Not eq:{msg}\n{v}\n----{w}') return result -class TestMa(object): +class TestMa: def setup(self): x = np.array([1., 1., 1., -2., pi/2.0, 4., 5., -10., 10., 1., 2., 3.]) @@ -263,14 +261,14 @@ def test_testCopySize(self): m = make_mask(n) m2 = make_mask(m) assert_(m is m2) - m3 = make_mask(m, copy=1) + m3 = make_mask(m, copy=True) assert_(m is not m3) x1 = np.arange(5) y1 = array(x1, mask=m) assert_(y1._data is not x1) assert_(allequal(x1, y1._data)) - assert_(y1.mask is m) + assert_(y1._mask is m) y1a = array(y1, copy=0) # For copy=False, one might expect that the array would just @@ -280,19 +278,19 @@ def test_testCopySize(self): y1._mask.__array_interface__) y2 = array(x1, mask=m3, copy=0) - assert_(y2.mask is m3) + assert_(y2._mask is m3) assert_(y2[2] is masked) y2[2] = 9 assert_(y2[2] is not masked) - assert_(y2.mask is m3) + assert_(y2._mask is m3) assert_(allequal(y2.mask, 0)) y2a = array(x1, mask=m, copy=1) - assert_(y2a.mask is not m) + assert_(y2a._mask is not m) assert_(y2a[2] is masked) y2a[2] = 9 assert_(y2a[2] is not masked) - assert_(y2a.mask is not m) + assert_(y2a._mask is not m) assert_(allequal(y2a.mask, 0)) y3 = array(x1 * 1.0, mask=m) @@ -318,14 +316,14 @@ def test_testPut(self): assert_(x[3] is masked) assert_(x[4] is masked) x[[1, 4]] = [10, 40] - assert_(x.mask is m) + assert_(x._mask is m) assert_(x[3] is masked) assert_(x[4] is not masked) assert_(eq(x, [0, 10, 2, -1, 40])) x = array(d, mask=m2, copy=True) x.put([0, 1, 2], [-1, 100, 200]) - assert_(x.mask is not m2) + assert_(x._mask is not m2) assert_(x[3] is masked) assert_(x[4] is masked) assert_(eq(x, [-1, 100, 200, 0, 0])) @@ -549,13 +547,13 @@ def test_testInplace(self): def test_testPickle(self): # Test of pickling - import pickle x = arange(12) x[4:10:2] = masked x = x.reshape(4, 3) - s = pickle.dumps(x) - y = pickle.loads(s) - assert_(eq(x, y)) + for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): + s = pickle.dumps(x, protocol=proto) + y = pickle.loads(s) + assert_(eq(x, y)) def test_testMasked(self): # Test of masked element @@ -570,7 +568,7 @@ def test_testAverage1(self): ott = array([0., 1., 2., 3.], mask=[1, 0, 0, 0]) assert_(eq(2.0, average(ott, axis=0))) assert_(eq(2.0, average(ott, weights=[1., 1., 2., 1.]))) - result, wts = average(ott, weights=[1., 1., 2., 1.], returned=1) + result, wts = average(ott, weights=[1., 1., 2., 1.], returned=True) assert_(eq(2.0, result)) assert_(wts == 4.0) ott[:] = masked @@ -581,7 +579,7 @@ def test_testAverage1(self): assert_(eq(average(ott, axis=0), [2.0, 0.0])) assert_(average(ott, axis=1)[0] is masked) assert_(eq([2., 0.], average(ott, axis=0))) - result, wts = average(ott, axis=0, returned=1) + result, wts = average(ott, axis=0, returned=True) assert_(eq(wts, [1., 0.])) def test_testAverage2(self): @@ -622,14 +620,14 @@ def test_testAverage2(self): a = arange(6) b = arange(6) * 3 - r1, w1 = average([[a, b], [b, a]], axis=1, returned=1) + r1, w1 = average([[a, b], [b, a]], axis=1, returned=True) assert_equal(shape(r1), shape(w1)) assert_equal(r1.shape, w1.shape) - r2, w2 = average(ones((2, 2, 3)), axis=0, weights=[3, 1], returned=1) + r2, w2 = average(ones((2, 2, 3)), axis=0, weights=[3, 1], returned=True) assert_equal(shape(w2), shape(r2)) - r2, w2 = average(ones((2, 2, 3)), returned=1) + r2, w2 = average(ones((2, 2, 3)), returned=True) assert_equal(shape(w2), shape(r2)) - r2, w2 = average(ones((2, 2, 3)), weights=ones((2, 2, 3)), returned=1) + r2, w2 = average(ones((2, 2, 3)), weights=ones((2, 2, 3)), returned=True) assert_(shape(w2) == shape(r2)) a2d = array([[1, 2], [0, 4]], float) a2dm = masked_array(a2d, [[0, 0], [1, 0]]) @@ -699,8 +697,24 @@ def test_testSingleElementSubscript(self): assert_equal(b[0].shape, ()) assert_equal(b[1].shape, ()) + def test_assignment_by_condition(self): + # Test for gh-18951 + a = array([1, 2, 3, 4], mask=[1, 0, 1, 0]) + c = a >= 3 + a[c] = 5 + assert_(a[2] is masked) + + def test_assignment_by_condition_2(self): + # gh-19721 + a = masked_array([0, 1], mask=[False, False]) + b = masked_array([0, 1], mask=[True, True]) + mask = a < 1 + b[mask] = a[mask] + expected_mask = [False, True] + assert_equal(b.mask, expected_mask) + -class TestUfuncs(object): +class TestUfuncs: def setup(self): self.d = (array([1.0, 0, -1, pi / 2] * 2, mask=[0, 1] + [0] * 6), array([1.0, 0, -1, pi / 2] * 2, mask=[1, 0] + [0] * 6),) @@ -765,7 +779,7 @@ def test_nonzero(self): assert_(eq(nonzero(x), [0])) -class TestArrayMethods(object): +class TestArrayMethods: def setup(self): x = np.array([8.375, 7.545, 8.828, 8.5, 1.757, 5.928, diff --git a/numpy/ma/tests/test_regression.py b/numpy/ma/tests/test_regression.py index 96c418a5121b..7e76eb05491b 100644 --- a/numpy/ma/tests/test_regression.py +++ b/numpy/ma/tests/test_regression.py @@ -1,14 +1,10 @@ -from __future__ import division, absolute_import, print_function - -import warnings - import numpy as np from numpy.testing import ( assert_, assert_array_equal, assert_allclose, suppress_warnings ) -class TestRegression(object): +class TestRegression: def test_masked_array_create(self): # Ticket #17 x = np.ma.masked_array([0, 1, 2, 3, 0, 4, 5, 6], @@ -84,3 +80,12 @@ def test_mask_not_backmangled(self): assert_(a.mask.shape == (2,)) assert_(b.shape == (2, 2)) assert_(b.mask.shape == (2, 2)) + + def test_empty_list_on_structured(self): + # See gh-12464. Indexing with empty list should give empty result. + ma = np.ma.MaskedArray([(1, 1.), (2, 2.), (3, 3.)], dtype='i4,f4') + assert_array_equal(ma[[]], ma[:0]) + + def test_masked_array_tobytes_fortran(self): + ma = np.ma.arange(4).reshape((2,2)) + assert_array_equal(ma.tobytes(order='F'), ma.T.tobytes()) diff --git a/numpy/ma/tests/test_subclassing.py b/numpy/ma/tests/test_subclassing.py index f8ab52bb9e10..83a9b2f5187c 100644 --- a/numpy/ma/tests/test_subclassing.py +++ b/numpy/ma/tests/test_subclassing.py @@ -6,8 +6,6 @@ :version: $Id: test_subclassing.py 3473 2007-10-29 15:18:13Z jarrod.millman $ """ -from __future__ import division, absolute_import, print_function - import numpy as np from numpy.testing import assert_, assert_raises from numpy.ma.testutils import assert_equal @@ -30,19 +28,18 @@ def __new__(cls,arr,info={}): return x def __array_finalize__(self, obj): - if callable(getattr(super(SubArray, self), - '__array_finalize__', None)): - super(SubArray, self).__array_finalize__(obj) + if callable(getattr(super(), '__array_finalize__', None)): + super().__array_finalize__(obj) self.info = getattr(obj, 'info', {}).copy() return def __add__(self, other): - result = super(SubArray, self).__add__(other) + result = super().__add__(other) result.info['added'] = result.info.get('added', 0) + 1 return result def __iadd__(self, other): - result = super(SubArray, self).__iadd__(other) + result = super().__iadd__(other) result.info['iadded'] = result.info.get('iadded', 0) + 1 return result @@ -53,7 +50,7 @@ def __iadd__(self, other): class SubMaskedArray(MaskedArray): """Pure subclass of MaskedArray, keeping some info on subclass.""" def __new__(cls, info=None, **kwargs): - obj = super(SubMaskedArray, cls).__new__(cls, **kwargs) + obj = super().__new__(cls, **kwargs) obj._optinfo['info'] = info return obj @@ -66,11 +63,11 @@ def __new__(cls, data, info={}, mask=nomask): _data.info = subarr.info return _data - def _get_series(self): + @property + def _series(self): _view = self.view(MaskedArray) _view._sharedmask = False return _view - _series = property(fget=_get_series) msubarray = MSubArray @@ -80,7 +77,7 @@ def _get_series(self): # and overrides __array_wrap__, updating the info dict, to check that this # doesn't get destroyed by MaskedArray._update_from. But this one also needs # its own iterator... -class CSAIterator(object): +class CSAIterator: """ Flat iterator object that uses its own setter/getter (works around ndarray.flat not propagating subclass setters/getters @@ -107,17 +104,15 @@ def __setitem__(self, index, value): def __next__(self): return next(self._dataiter).__array__().view(type(self._original)) - next = __next__ - class ComplicatedSubArray(SubArray): def __str__(self): - return 'myprefix {0} mypostfix'.format(self.view(SubArray)) + return f'myprefix {self.view(SubArray)} mypostfix' def __repr__(self): # Return a repr that does not start with 'name(' - return '<{0} {1}>'.format(self.__class__.__name__, self) + return f'<{self.__class__.__name__} {self}>' def _validate_input(self, value): if not isinstance(value, ComplicatedSubArray): @@ -127,12 +122,11 @@ def _validate_input(self, value): def __setitem__(self, item, value): # validation ensures direct assignment with ndarray or # masked_print_option will fail - super(ComplicatedSubArray, self).__setitem__( - item, self._validate_input(value)) + super().__setitem__(item, self._validate_input(value)) def __getitem__(self, item): # ensure getter returns our own class also for scalars - value = super(ComplicatedSubArray, self).__getitem__(item) + value = super().__getitem__(item) if not isinstance(value, np.ndarray): # scalar value = value.__array__().view(ComplicatedSubArray) return value @@ -147,14 +141,14 @@ def flat(self, value): y[:] = value def __array_wrap__(self, obj, context=None): - obj = super(ComplicatedSubArray, self).__array_wrap__(obj, context) + obj = super().__array_wrap__(obj, context) if context is not None and context[0] is np.multiply: obj.info['multiplied'] = obj.info.get('multiplied', 0) + 1 return obj -class TestSubclassing(object): +class TestSubclassing: # Test suite for masked subclasses of ndarray. def setup(self): @@ -321,8 +315,8 @@ def test_subclass_repr(self): assert_startswith(repr(mx), 'masked_array') xsub = SubArray(x) mxsub = masked_array(xsub, mask=[True, False, True, False, False]) - assert_startswith(repr(mxsub), - 'masked_{0}(data=[--, 1, --, 3, 4]'.format(SubArray.__name__)) + assert_startswith(repr(mxsub), + f'masked_{SubArray.__name__}(data=[--, 1, --, 3, 4]') def test_subclass_str(self): """test str with subclass that has overridden str, setitem""" @@ -349,3 +343,45 @@ def test_pure_subclass_info_preservation(self): diff2 = arr1 - arr2 assert_('info' in diff2._optinfo) assert_(diff2._optinfo['info'] == 'test') + + +class ArrayNoInheritance: + """Quantity-like class that does not inherit from ndarray""" + def __init__(self, data, units): + self.magnitude = data + self.units = units + + def __getattr__(self, attr): + return getattr(self.magnitude, attr) + + +def test_array_no_inheritance(): + data_masked = np.ma.array([1, 2, 3], mask=[True, False, True]) + data_masked_units = ArrayNoInheritance(data_masked, 'meters') + + # Get the masked representation of the Quantity-like class + new_array = np.ma.array(data_masked_units) + assert_equal(data_masked.data, new_array.data) + assert_equal(data_masked.mask, new_array.mask) + # Test sharing the mask + data_masked.mask = [True, False, False] + assert_equal(data_masked.mask, new_array.mask) + assert_(new_array.sharedmask) + + # Get the masked representation of the Quantity-like class + new_array = np.ma.array(data_masked_units, copy=True) + assert_equal(data_masked.data, new_array.data) + assert_equal(data_masked.mask, new_array.mask) + # Test that the mask is not shared when copy=True + data_masked.mask = [True, False, True] + assert_equal([True, False, False], new_array.mask) + assert_(not new_array.sharedmask) + + # Get the masked representation of the Quantity-like class + new_array = np.ma.array(data_masked_units, keep_mask=False) + assert_equal(data_masked.data, new_array.data) + # The change did not affect the original mask + assert_equal(data_masked.mask, [True, False, True]) + # Test that the mask is False and not shared when keep_mask=False + assert_(not new_array.mask) + assert_(not new_array.sharedmask) diff --git a/numpy/ma/testutils.py b/numpy/ma/testutils.py index c0deaa9f4884..2dd479abe480 100644 --- a/numpy/ma/testutils.py +++ b/numpy/ma/testutils.py @@ -5,8 +5,6 @@ :version: $Id: testutils.py 3529 2007-11-13 08:01:14Z jarrod.millman $ """ -from __future__ import division, absolute_import, print_function - import operator import numpy as np @@ -88,7 +86,7 @@ def _assert_equal_on_sequences(actual, desired, err_msg=''): """ assert_equal(len(actual), len(desired), err_msg) for k in range(len(desired)): - assert_equal(actual[k], desired[k], 'item=%r\n%s' % (k, err_msg)) + assert_equal(actual[k], desired[k], f'item={k!r}\n{err_msg}') return @@ -119,8 +117,8 @@ def assert_equal(actual, desired, err_msg=''): assert_equal(len(actual), len(desired), err_msg) for k, i in desired.items(): if k not in actual: - raise AssertionError("%s not in %s" % (k, actual)) - assert_equal(actual[k], desired[k], 'key=%r\n%s' % (k, err_msg)) + raise AssertionError(f"{k} not in {actual}") + assert_equal(actual[k], desired[k], f'key={k!r}\n{err_msg}') return # Case #2: lists ..... if isinstance(desired, (list, tuple)) and isinstance(actual, (list, tuple)): @@ -136,8 +134,8 @@ def assert_equal(actual, desired, err_msg=''): msg = build_err_msg([actual, desired], err_msg, header='', names=('x', 'y')) raise ValueError(msg) - actual = np.array(actual, copy=False, subok=True) - desired = np.array(desired, copy=False, subok=True) + actual = np.asanyarray(actual) + desired = np.asanyarray(desired) (actual_dtype, desired_dtype) = (actual.dtype, desired.dtype) if actual_dtype.char == "S" and desired_dtype.char == "S": return _assert_equal_on_sequences(actual.tolist(), @@ -158,12 +156,12 @@ def fail_if_equal(actual, desired, err_msg='',): for k, i in desired.items(): if k not in actual: raise AssertionError(repr(k)) - fail_if_equal(actual[k], desired[k], 'key=%r\n%s' % (k, err_msg)) + fail_if_equal(actual[k], desired[k], f'key={k!r}\n{err_msg}') return if isinstance(desired, (list, tuple)) and isinstance(actual, (list, tuple)): fail_if_equal(len(actual), len(desired), err_msg) for k in range(len(desired)): - fail_if_equal(actual[k], desired[k], 'item=%r\n%s' % (k, err_msg)) + fail_if_equal(actual[k], desired[k], f'item={k!r}\n{err_msg}') return if isinstance(actual, np.ndarray) or isinstance(desired, np.ndarray): return fail_if_array_equal(actual, desired, err_msg) diff --git a/numpy/ma/timer_comparison.py b/numpy/ma/timer_comparison.py index 68104ed0affb..9eb1a23cd693 100644 --- a/numpy/ma/timer_comparison.py +++ b/numpy/ma/timer_comparison.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - import timeit from functools import reduce @@ -9,13 +7,10 @@ from numpy.testing import build_err_msg -# Fixme: this does not look right. -np.seterr(all='ignore') pi = np.pi - -class ModuleTester(object): +class ModuleTester: def __init__(self, module): self.module = module self.allequal = module.allequal @@ -79,8 +74,7 @@ def assert_array_compare(self, comparison, x, y, err_msg='', header='', if not cond: msg = build_err_msg([x, y], err_msg - + '\n(shapes %s, %s mismatch)' % (x.shape, - y.shape), + + f'\n(shapes {x.shape}, {y.shape} mismatch)', header=header, names=('x', 'y')) assert cond, msg @@ -102,9 +96,9 @@ def assert_array_compare(self, comparison, x, y, err_msg='', header='', header=header, names=('x', 'y')) assert cond, msg - except ValueError: + except ValueError as e: msg = build_err_msg([x, y], err_msg, header=header, names=('x', 'y')) - raise ValueError(msg) + raise ValueError(msg) from e def assert_array_equal(self, x, y, err_msg=''): """ @@ -114,6 +108,7 @@ def assert_array_equal(self, x, y, err_msg=''): self.assert_array_compare(self.equal, x, y, err_msg=err_msg, header='Arrays are not equal') + @np.errstate(all='ignore') def test_0(self): """ Tests creation @@ -124,6 +119,7 @@ def test_0(self): xm = self.masked_array(x, mask=m) xm[0] + @np.errstate(all='ignore') def test_1(self): """ Tests creation @@ -151,6 +147,7 @@ def test_1(self): xf.shape = s assert(self.count(xm) == len(m1) - reduce(lambda x, y:x+y, m1)) + @np.errstate(all='ignore') def test_2(self): """ Tests conversions and indexing. @@ -193,6 +190,7 @@ def test_2(self): m3 = self.make_mask(m, copy=1) assert(m is not m3) + @np.errstate(all='ignore') def test_3(self): """ Tests resize/repeat @@ -212,6 +210,7 @@ def test_3(self): y8 = x4.repeat(2, 0) assert self.allequal(y5, y8) + @np.errstate(all='ignore') def test_4(self): """ Test of take, transpose, inner, outer products. @@ -235,6 +234,7 @@ def test_4(self): assert t[1] == 2 assert t[2] == 3 + @np.errstate(all='ignore') def test_5(self): """ Tests inplace w/ scalar @@ -287,6 +287,7 @@ def test_5(self): x += 1. assert self.allequal(x, y + 1.) + @np.errstate(all='ignore') def test_6(self): """ Tests inplace w/ array @@ -338,6 +339,7 @@ def test_6(self): x /= a xm /= a + @np.errstate(all='ignore') def test_7(self): "Tests ufunc" d = (self.array([1.0, 0, -1, pi/2]*2, mask=[0, 1]+[0]*6), @@ -372,6 +374,7 @@ def test_7(self): self.assert_array_equal(ur.filled(0), mr.filled(0), f) self.assert_array_equal(ur._mask, mr._mask) + @np.errstate(all='ignore') def test_99(self): # test average ott = self.array([0., 1., 2., 3.], mask=[1, 0, 0, 0]) @@ -417,6 +420,7 @@ def test_99(self): self.assert_array_equal(self.average(z, axis=1), [2.5, 5.0]) self.assert_array_equal(self.average(z, axis=0, weights=w2), [0., 1., 99., 99., 4.0, 10.0]) + @np.errstate(all='ignore') def test_A(self): x = self.arange(24) x[5:6] = self.masked @@ -430,11 +434,10 @@ def test_A(self): setup_cur = "import numpy.ma.core as module\n" + setup_base (nrepeat, nloop) = (10, 10) - if 1: - for i in range(1, 8): - func = 'tester.test_%i()' % i - cur = timeit.Timer(func, setup_cur).repeat(nrepeat, nloop*10) - cur = np.sort(cur) - print("#%i" % i + 50*'.') - print(eval("ModuleTester.test_%i.__doc__" % i)) - print("core_current : %.3f - %.3f" % (cur[0], cur[1])) + for i in range(1, 8): + func = 'tester.test_%i()' % i + cur = timeit.Timer(func, setup_cur).repeat(nrepeat, nloop*10) + cur = np.sort(cur) + print("#%i" % i + 50*'.') + print(eval("ModuleTester.test_%i.__doc__" % i)) + print(f'core_current : {cur[0]:.3f} - {cur[1]:.3f}') diff --git a/numpy/ma/version.py b/numpy/ma/version.py deleted file mode 100644 index a2c5c42a806a..000000000000 --- a/numpy/ma/version.py +++ /dev/null @@ -1,14 +0,0 @@ -"""Version number - -""" -from __future__ import division, absolute_import, print_function - -version = '1.00' -release = False - -if not release: - from . import core - from . import extras - revision = [core.__revision__.split(':')[-1][:-1].strip(), - extras.__revision__.split(':')[-1][:-1].strip(),] - version += '.dev%04i' % max([int(rev) for rev in revision]) diff --git a/numpy/matlib.py b/numpy/matlib.py index 004e5f0c82ea..bd6b632891fc 100644 --- a/numpy/matlib.py +++ b/numpy/matlib.py @@ -1,9 +1,20 @@ -from __future__ import division, absolute_import, print_function +import warnings + +# 2018-05-29, PendingDeprecationWarning added to matrix.__new__ +# 2020-01-23, numpy 1.19.0 PendingDeprecatonWarning +warnings.warn("Importing from numpy.matlib is deprecated since 1.19.0. " + "The matrix subclass is not the recommended way to represent " + "matrices or deal with linear algebra (see " + "https://docs.scipy.org/doc/numpy/user/numpy-for-matlab-users.html). " + "Please adjust your code to use regular ndarray. ", + PendingDeprecationWarning, stacklevel=2) import numpy as np from numpy.matrixlib.defmatrix import matrix, asmatrix -# need * as we're copying the numpy namespace -from numpy import * +# Matlib.py contains all functions in the numpy namespace with a few +# replacements. See doc/source/reference/routines.matlib.rst for details. +# Need * as we're copying the numpy namespace. +from numpy import * # noqa: F403 __version__ = np.__version__ @@ -39,11 +50,11 @@ def empty(shape, dtype=None, order='C'): -------- >>> import numpy.matlib >>> np.matlib.empty((2, 2)) # filled with random data - matrix([[ 6.76425276e-320, 9.79033856e-307], - [ 7.39337286e-309, 3.22135945e-309]]) #random + matrix([[ 6.76425276e-320, 9.79033856e-307], # random + [ 7.39337286e-309, 3.22135945e-309]]) >>> np.matlib.empty((2, 2), dtype=int) - matrix([[ 6600475, 0], - [ 6586976, 22740995]]) #random + matrix([[ 6600475, 0], # random + [ 6586976, 22740995]]) """ return ndarray.__new__(matrix, shape, dtype, order=order) @@ -82,11 +93,11 @@ def ones(shape, dtype=None, order='C'): Examples -------- >>> np.matlib.ones((2,3)) - matrix([[ 1., 1., 1.], - [ 1., 1., 1.]]) + matrix([[1., 1., 1.], + [1., 1., 1.]]) >>> np.matlib.ones(2) - matrix([[ 1., 1.]]) + matrix([[1., 1.]]) """ a = ndarray.__new__(matrix, shape, dtype, order=order) @@ -126,11 +137,11 @@ def zeros(shape, dtype=None, order='C'): -------- >>> import numpy.matlib >>> np.matlib.zeros((2, 3)) - matrix([[ 0., 0., 0.], - [ 0., 0., 0.]]) + matrix([[0., 0., 0.], + [0., 0., 0.]]) >>> np.matlib.zeros(2) - matrix([[ 0., 0.]]) + matrix([[0., 0.]]) """ a = ndarray.__new__(matrix, shape, dtype, order=order) @@ -210,9 +221,9 @@ def eye(n,M=None, k=0, dtype=float, order='C'): -------- >>> import numpy.matlib >>> np.matlib.eye(3, k=1, dtype=float) - matrix([[ 0., 1., 0.], - [ 0., 0., 1.], - [ 0., 0., 0.]]) + matrix([[0., 1., 0.], + [0., 0., 1.], + [0., 0., 0.]]) """ return asmatrix(np.eye(n, M=M, k=k, dtype=dtype, order=order)) @@ -239,23 +250,24 @@ def rand(*args): See Also -------- - randn, numpy.random.rand + randn, numpy.random.RandomState.rand Examples -------- + >>> np.random.seed(123) >>> import numpy.matlib >>> np.matlib.rand(2, 3) - matrix([[ 0.68340382, 0.67926887, 0.83271405], - [ 0.00793551, 0.20468222, 0.95253525]]) #random + matrix([[0.69646919, 0.28613933, 0.22685145], + [0.55131477, 0.71946897, 0.42310646]]) >>> np.matlib.rand((2, 3)) - matrix([[ 0.84682055, 0.73626594, 0.11308016], - [ 0.85429008, 0.3294825 , 0.89139555]]) #random + matrix([[0.9807642 , 0.68482974, 0.4809319 ], + [0.39211752, 0.34317802, 0.72904971]]) If the first argument is a tuple, other arguments are ignored: >>> np.matlib.rand((2, 3), 4) - matrix([[ 0.46898646, 0.15163588, 0.95188261], - [ 0.59208621, 0.09561818, 0.00583606]]) #random + matrix([[0.43857224, 0.0596779 , 0.39804426], + [0.73799541, 0.18249173, 0.17545176]]) """ if isinstance(args[0], tuple): @@ -284,7 +296,7 @@ def randn(*args): See Also -------- - rand, random.randn + rand, numpy.random.RandomState.randn Notes ----- @@ -294,18 +306,19 @@ def randn(*args): Examples -------- + >>> np.random.seed(123) >>> import numpy.matlib >>> np.matlib.randn(1) - matrix([[-0.09542833]]) #random + matrix([[-1.0856306]]) >>> np.matlib.randn(1, 2, 3) - matrix([[ 0.16198284, 0.0194571 , 0.18312985], - [-0.7509172 , 1.61055 , 0.45298599]]) #random + matrix([[ 0.99734545, 0.2829785 , -1.50629471], + [-0.57860025, 1.65143654, -2.42667924]]) Two-by-four matrix of samples from :math:`N(3, 6.25)`: >>> 2.5 * np.matlib.randn((2, 4)) + 3 - matrix([[ 4.74085004, 8.89381862, 4.09042411, 4.83721922], - [ 7.52373709, 5.07933944, -2.64043543, 0.45610557]]) #random + matrix([[1.92771843, 6.16484065, 0.83314899, 1.30278462], + [2.76322758, 6.72847407, 1.40274501, 1.8900451 ]]) """ if isinstance(args[0], tuple): diff --git a/numpy/matrixlib/__init__.py b/numpy/matrixlib/__init__.py index 3ad3a95491bd..54154d11f750 100644 --- a/numpy/matrixlib/__init__.py +++ b/numpy/matrixlib/__init__.py @@ -1,12 +1,10 @@ """Sub-package containing the matrix class and related functions. """ -from __future__ import division, absolute_import, print_function - from .defmatrix import * __all__ = defmatrix.__all__ -from numpy.testing._private.pytesttester import PytestTester +from numpy._pytesttester import PytestTester test = PytestTester(__name__) del PytestTester diff --git a/numpy/matrixlib/__init__.pyi b/numpy/matrixlib/__init__.pyi new file mode 100644 index 000000000000..c1b82d2ecdb7 --- /dev/null +++ b/numpy/matrixlib/__init__.pyi @@ -0,0 +1,17 @@ +from typing import List + +from numpy._pytesttester import PytestTester + +from numpy import ( + matrix as matrix, +) + +from numpy.matrixlib.defmatrix import ( + bmat as bmat, + mat as mat, + asmatrix as asmatrix, +) + +__all__: List[str] +__path__: List[str] +test: PytestTester diff --git a/numpy/matrixlib/defmatrix.py b/numpy/matrixlib/defmatrix.py index 7baa401a8245..a414ee9bbd54 100644 --- a/numpy/matrixlib/defmatrix.py +++ b/numpy/matrixlib/defmatrix.py @@ -1,5 +1,3 @@ -from __future__ import division, absolute_import, print_function - __all__ = ['matrix', 'bmat', 'mat', 'asmatrix'] import sys @@ -7,6 +5,7 @@ import ast import numpy.core.numeric as N from numpy.core.numeric import concatenate, isscalar +from numpy.core.overrides import set_module # While not in __all__, matrix_power used to be defined here, so we import # it for backward compatibility. from numpy.linalg import matrix_power @@ -33,6 +32,8 @@ def _convert_from_string(data): newdata.append(newrow) return newdata + +@set_module('numpy') def asmatrix(data, dtype=None): """ Interpret the input as a matrix. @@ -67,6 +68,8 @@ def asmatrix(data, dtype=None): """ return matrix(data, dtype=dtype, copy=False) + +@set_module('numpy') class matrix(N.ndarray): """ matrix(data, dtype=None, copy=True) @@ -99,9 +102,9 @@ class matrix(N.ndarray): Examples -------- >>> a = np.matrix('1 2; 3 4') - >>> print(a) - [[1 2] - [3 4]] + >>> a + matrix([[1, 2], + [3, 4]]) >>> np.matrix([[1, 2], [3, 4]]) matrix([[1, 2], @@ -305,12 +308,12 @@ def sum(self, axis=None, dtype=None, out=None): matrix([[3], [7]]) >>> x.sum(axis=1, dtype='float') - matrix([[ 3.], - [ 7.]]) - >>> out = np.zeros((1, 2), dtype='float') - >>> x.sum(axis=1, dtype='float', out=out) - matrix([[ 3.], - [ 7.]]) + matrix([[3.], + [7.]]) + >>> out = np.zeros((2, 1), dtype='float') + >>> x.sum(axis=1, dtype='float', out=np.asmatrix(out)) + matrix([[3.], + [7.]]) """ return N.ndarray.sum(self, axis, dtype, out, keepdims=True)._collapse(axis) @@ -326,7 +329,7 @@ def squeeze(self, axis=None): Parameters ---------- axis : None or int or tuple of ints, optional - Selects a subset of the single-dimensional entries in the shape. + Selects a subset of the axes of length one in the shape. If an axis is selected with shape entry greater than one, an error is raised. @@ -432,7 +435,7 @@ def mean(self, axis=None, dtype=None, out=None): >>> x.mean() 5.5 >>> x.mean(0) - matrix([[ 4., 5., 6., 7.]]) + matrix([[4., 5., 6., 7.]]) >>> x.mean(1) matrix([[ 1.5], [ 5.5], @@ -464,9 +467,9 @@ def std(self, axis=None, dtype=None, out=None, ddof=0): [ 4, 5, 6, 7], [ 8, 9, 10, 11]]) >>> x.std() - 3.4520525295346629 + 3.4520525295346629 # may vary >>> x.std(0) - matrix([[ 3.26598632, 3.26598632, 3.26598632, 3.26598632]]) + matrix([[ 3.26598632, 3.26598632, 3.26598632, 3.26598632]]) # may vary >>> x.std(1) matrix([[ 1.11803399], [ 1.11803399], @@ -500,11 +503,11 @@ def var(self, axis=None, dtype=None, out=None, ddof=0): >>> x.var() 11.916666666666666 >>> x.var(0) - matrix([[ 10.66666667, 10.66666667, 10.66666667, 10.66666667]]) + matrix([[ 10.66666667, 10.66666667, 10.66666667, 10.66666667]]) # may vary >>> x.var(1) - matrix([[ 1.25], - [ 1.25], - [ 1.25]]) + matrix([[1.25], + [1.25], + [1.25]]) """ return N.ndarray.var(self, axis, dtype, out, ddof, keepdims=True)._collapse(axis) @@ -786,7 +789,8 @@ def ptp(self, axis=None, out=None): """ return N.ndarray.ptp(self, axis, out)._align(axis) - def getI(self): + @property + def I(self): """ Returns the (multiplicative) inverse of invertible `self`. @@ -798,7 +802,7 @@ def getI(self): ------- ret : matrix object If `self` is non-singular, `ret` is such that ``ret * self`` == - ``self * ret`` == ``np.matrix(np.eye(self[0,:].size)`` all return + ``self * ret`` == ``np.matrix(np.eye(self[0,:].size))`` all return ``True``. Raises @@ -819,18 +823,19 @@ def getI(self): matrix([[-2. , 1. ], [ 1.5, -0.5]]) >>> m.getI() * m - matrix([[ 1., 0.], + matrix([[ 1., 0.], # may vary [ 0., 1.]]) """ M, N = self.shape if M == N: - from numpy.dual import inv as func + from numpy.linalg import inv as func else: - from numpy.dual import pinv as func + from numpy.linalg import pinv as func return asmatrix(func(self)) - def getA(self): + @property + def A(self): """ Return `self` as an `ndarray` object. @@ -859,7 +864,8 @@ def getA(self): """ return self.__array__() - def getA1(self): + @property + def A1(self): """ Return `self` as a flattened `ndarray`. @@ -881,7 +887,8 @@ def getA1(self): [ 4, 5, 6, 7], [ 8, 9, 10, 11]]) >>> x.getA1() - array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) + array([ 0, 1, 2, ..., 9, 10, 11]) + """ return self.__array__().ravel() @@ -925,8 +932,8 @@ def ravel(self, order='C'): """ return N.ndarray.ravel(self, order=order) - - def getT(self): + @property + def T(self): """ Returns the transpose of the matrix. @@ -958,7 +965,8 @@ def getT(self): """ return self.transpose() - def getH(self): + @property + def H(self): """ Returns the (complex) conjugate transpose of `self`. @@ -981,10 +989,10 @@ def getH(self): [ 4. -4.j, 5. -5.j, 6. -6.j, 7. -7.j], [ 8. -8.j, 9. -9.j, 10.-10.j, 11.-11.j]]) >>> z.getH() - matrix([[ 0. +0.j, 4. +4.j, 8. +8.j], - [ 1. +1.j, 5. +5.j, 9. +9.j], - [ 2. +2.j, 6. +6.j, 10.+10.j], - [ 3. +3.j, 7. +7.j, 11.+11.j]]) + matrix([[ 0. -0.j, 4. +4.j, 8. +8.j], + [ 1. +1.j, 5. +5.j, 9. +9.j], + [ 2. +2.j, 6. +6.j, 10.+10.j], + [ 3. +3.j, 7. +7.j, 11.+11.j]]) """ if issubclass(self.dtype.type, N.complexfloating): @@ -992,11 +1000,12 @@ def getH(self): else: return self.transpose() - T = property(getT, None) - A = property(getA, None) - A1 = property(getA1, None) - H = property(getH, None) - I = property(getI, None) + # kept for compatibility + getT = T.fget + getA = A.fget + getA1 = A1.fget + getH = H.fget + getI = I.fget def _from_string(str, gdict, ldict): rows = str.split(';') @@ -1015,14 +1024,15 @@ def _from_string(str, gdict, ldict): except KeyError: try: thismat = gdict[col] - except KeyError: - raise KeyError("%s not found" % (col,)) + except KeyError as e: + raise NameError(f"name {col!r} is not defined") from None coltup.append(thismat) rowtup.append(concatenate(coltup, axis=-1)) return concatenate(rowtup, axis=0) +@set_module('numpy') def bmat(obj, ldict=None, gdict=None): """ Build a matrix object from a string, nested sequence, or array. @@ -1034,7 +1044,7 @@ def bmat(obj, ldict=None, gdict=None): referenced by name. ldict : dict, optional A dictionary that replaces local operands in current frame. - Ignored if `obj` is not a string or `gdict` is `None`. + Ignored if `obj` is not a string or `gdict` is None. gdict : dict, optional A dictionary that replaces global operands in current frame. Ignored if `obj` is not a string. diff --git a/numpy/matrixlib/defmatrix.pyi b/numpy/matrixlib/defmatrix.pyi new file mode 100644 index 000000000000..6c86ea1ef769 --- /dev/null +++ b/numpy/matrixlib/defmatrix.pyi @@ -0,0 +1,15 @@ +from typing import List, Any, Sequence, Mapping +from numpy import matrix as matrix +from numpy.typing import ArrayLike, DTypeLike, NDArray + +__all__: List[str] + +def bmat( + obj: str | Sequence[ArrayLike] | NDArray[Any], + ldict: None | Mapping[str, Any] = ..., + gdict: None | Mapping[str, Any] = ..., +) -> matrix[Any, Any]: ... + +def asmatrix(data: ArrayLike, dtype: DTypeLike = ...) -> matrix[Any, Any]: ... + +mat = asmatrix diff --git a/numpy/matrixlib/setup.py b/numpy/matrixlib/setup.py index 8c383cecec7b..4fed75de1cbc 100644 --- a/numpy/matrixlib/setup.py +++ b/numpy/matrixlib/setup.py @@ -1,12 +1,9 @@ -#!/usr/bin/env python -from __future__ import division, print_function - -import os - +#!/usr/bin/env python3 def configuration(parent_package='', top_path=None): from numpy.distutils.misc_util import Configuration config = Configuration('matrixlib', parent_package, top_path) - config.add_data_dir('tests') + config.add_subpackage('tests') + config.add_data_files('*.pyi') return config if __name__ == "__main__": diff --git a/numpy/matrixlib/tests/test_defmatrix.py b/numpy/matrixlib/tests/test_defmatrix.py index e74e83cdb065..4cb5f3a375e9 100644 --- a/numpy/matrixlib/tests/test_defmatrix.py +++ b/numpy/matrixlib/tests/test_defmatrix.py @@ -1,19 +1,4 @@ -from __future__ import division, absolute_import, print_function - -# As we are testing matrices, we ignore its PendingDeprecationWarnings -try: - import pytest - pytestmark = pytest.mark.filterwarnings( - 'ignore:the matrix subclass is not:PendingDeprecationWarning') -except ImportError: - pass - -try: - # Accessing collections abstract classes from collections - # has been deprecated since Python 3.3 - import collections.abc as collections_abc -except ImportError: - import collections as collections_abc +import collections.abc import numpy as np from numpy import matrix, asmatrix, bmat @@ -24,7 +9,7 @@ from numpy.linalg import matrix_power from numpy.matrixlib import mat -class TestCtor(object): +class TestCtor: def test_basic(self): A = np.array([[1, 2], [3, 4]]) mA = matrix(A) @@ -71,7 +56,7 @@ def test_bmat_nondefault_str(self): assert_(np.all(b2 == mixresult)) -class TestProperties(object): +class TestProperties: def test_sum(self): """Test whether matrix.sum(axis=1) preserves orientation. Fails in NumPy <= 0.9.6.2127. @@ -204,7 +189,7 @@ def test_make_bool_matrix_from_str(self): B = matrix([[True], [True], [False]]) assert_array_equal(A, B) -class TestCasting(object): +class TestCasting: def test_basic(self): A = np.arange(100).reshape(10, 10) mA = matrix(A) @@ -223,7 +208,7 @@ def test_basic(self): assert_(np.all(mA != mB)) -class TestAlgebra(object): +class TestAlgebra: def test_basic(self): import numpy.linalg as linalg @@ -274,23 +259,15 @@ def test_notimplemented(self): [3., 4.]]) # __rpow__ - try: + with assert_raises(TypeError): 1.0**A - except TypeError: - pass - else: - self.fail("matrix.__rpow__ doesn't raise a TypeError") # __mul__ with something not a list, ndarray, tuple, or scalar - try: + with assert_raises(TypeError): A*object() - except TypeError: - pass - else: - self.fail("matrix.__mul__ with non-numeric object doesn't raise" - "a TypeError") -class TestMatrixReturn(object): + +class TestMatrixReturn: def test_instance_methods(self): a = matrix([1.0], dtype='f8') methodargs = { @@ -315,7 +292,7 @@ def test_instance_methods(self): if attrib.startswith('_') or attrib in excluded_methods: continue f = getattr(a, attrib) - if isinstance(f, collections_abc.Callable): + if isinstance(f, collections.abc.Callable): # reset contents of a a.astype('f8') a.fill(1.0) @@ -332,7 +309,7 @@ def test_instance_methods(self): assert_(type(d) is np.ndarray) -class TestIndexing(object): +class TestIndexing: def test_basic(self): x = asmatrix(np.zeros((3, 2), float)) y = np.zeros((3, 1), float) @@ -341,7 +318,7 @@ def test_basic(self): assert_equal(x, [[0, 1], [0, 0], [0, 0]]) -class TestNewScalarIndexing(object): +class TestNewScalarIndexing: a = matrix([[1, 2], [3, 4]]) def test_dimesions(self): @@ -408,7 +385,7 @@ def test_list_indexing(self): assert_array_equal(x[[2, 1, 0],:], x[::-1,:]) -class TestPower(object): +class TestPower: def test_returntype(self): a = np.array([[0, 1], [0, 0]]) assert_(type(matrix_power(a, 2)) is np.ndarray) @@ -419,7 +396,7 @@ def test_list(self): assert_array_equal(matrix_power([[0, 1], [0, 0]], 2), [[0, 0], [0, 0]]) -class TestShape(object): +class TestShape: a = np.array([[1], [2]]) m = matrix([[1], [2]]) @@ -466,3 +443,11 @@ def test_array_memory_sharing(self): def test_matrix_memory_sharing(self): assert_(np.may_share_memory(self.m, self.m.ravel())) assert_(not np.may_share_memory(self.m, self.m.flatten())) + + def test_expand_dims_matrix(self): + # matrices are always 2d - so expand_dims only makes sense when the + # type is changed away from matrix. + a = np.arange(10).reshape((2, 5)).view(np.matrix) + expanded = np.expand_dims(a, axis=1) + assert_equal(expanded.ndim, 3) + assert_(not isinstance(expanded, np.matrix)) diff --git a/numpy/matrixlib/tests/test_interaction.py b/numpy/matrixlib/tests/test_interaction.py index fb4d8f98c709..5154bd621c61 100644 --- a/numpy/matrixlib/tests/test_interaction.py +++ b/numpy/matrixlib/tests/test_interaction.py @@ -2,15 +2,7 @@ Note that tests with MaskedArray and linalg are done in separate files. """ -from __future__ import division, absolute_import, print_function - -# As we are testing matrices, we ignore its PendingDeprecationWarnings -try: - import pytest - pytestmark = pytest.mark.filterwarnings( - 'ignore:the matrix subclass is not:PendingDeprecationWarning') -except ImportError: - pass +import pytest import textwrap import warnings @@ -296,7 +288,7 @@ def test_kron_matrix(): assert_equal(type(np.kron(m, a)), np.matrix) -class TestConcatenatorMatrix(object): +class TestConcatenatorMatrix: # 2018-04-29: moved here from core.tests.test_index_tricks. def test_matrix(self): a = [1, 2] @@ -332,24 +324,17 @@ def test_matrix_builder(self): def test_array_equal_error_message_matrix(): # 2018-04-29: moved here from testing.tests.test_utils. - try: + with pytest.raises(AssertionError) as exc_info: assert_equal(np.array([1, 2]), np.matrix([1, 2])) - except AssertionError as e: - msg = str(e) - msg2 = msg.replace("shapes (2L,), (1L, 2L)", "shapes (2,), (1, 2)") - msg_reference = textwrap.dedent("""\ - - Arrays are not equal - - (shapes (2,), (1, 2) mismatch) - x: array([1, 2]) - y: matrix([[1, 2]])""") - try: - assert_equal(msg, msg_reference) - except AssertionError: - assert_equal(msg2, msg_reference) - else: - raise AssertionError("Did not raise") + msg = str(exc_info.value) + msg_reference = textwrap.dedent("""\ + + Arrays are not equal + + (shapes (2,), (1, 2) mismatch) + x: array([1, 2]) + y: matrix([[1, 2]])""") + assert_equal(msg, msg_reference) def test_array_almost_equal_matrix(): diff --git a/numpy/matrixlib/tests/test_masked_matrix.py b/numpy/matrixlib/tests/test_masked_matrix.py index adc2e54199cd..95d3f44b6196 100644 --- a/numpy/matrixlib/tests/test_masked_matrix.py +++ b/numpy/matrixlib/tests/test_masked_matrix.py @@ -1,22 +1,12 @@ -from __future__ import division, absolute_import, print_function - -# As we are testing matrices, we ignore its PendingDeprecationWarnings -try: - import pytest - pytestmark = pytest.mark.filterwarnings( - 'ignore:the matrix subclass is not:PendingDeprecationWarning') -except ImportError: - pass - -import pickle - import numpy as np +from numpy.testing import assert_warns from numpy.ma.testutils import (assert_, assert_equal, assert_raises, assert_array_equal) from numpy.ma.core import (masked_array, masked_values, masked, allequal, MaskType, getmask, MaskedArray, nomask, log, add, hypot, divide) from numpy.ma.extras import mr_ +from numpy.compat import pickle class MMatrix(MaskedArray, np.matrix,): @@ -31,14 +21,14 @@ def __array_finalize__(self, obj): MaskedArray.__array_finalize__(self, obj) return - def _get_series(self): + @property + def _series(self): _view = self.view(MaskedArray) _view._sharedmask = False return _view - _series = property(fget=_get_series) -class TestMaskedMatrix(object): +class TestMaskedMatrix: def test_matrix_indexing(self): # Tests conversions and indexing x1 = np.matrix([[1, 2, 3], [4, 3, 2]]) @@ -86,10 +76,11 @@ def test_matrix_indexing(self): def test_pickling_subbaseclass(self): # Test pickling w/ a subclass of ndarray a = masked_array(np.matrix(list(range(10))), mask=[1, 0, 1, 0, 0] * 2) - a_pickled = pickle.loads(a.dumps()) - assert_equal(a_pickled._mask, a._mask) - assert_equal(a_pickled, a) - assert_(isinstance(a_pickled._data, np.matrix)) + for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): + a_pickled = pickle.loads(pickle.dumps(a, protocol=proto)) + assert_equal(a_pickled._mask, a._mask) + assert_equal(a_pickled, a) + assert_(isinstance(a_pickled._data, np.matrix)) def test_count_mean_with_matrix(self): m = masked_array(np.matrix([[1, 2], [3, 4]]), mask=np.zeros((2, 2))) @@ -179,7 +170,7 @@ def test_view(self): assert_(not isinstance(test, MaskedArray)) -class TestSubclassing(object): +class TestSubclassing: # Test suite for masked subclasses of ndarray. def setup(self): @@ -208,7 +199,8 @@ def test_masked_binary_operations(self): # Result should work assert_equal(add(mx, x), mx+x) assert_(isinstance(add(mx, mx)._data, np.matrix)) - assert_(isinstance(add.outer(mx, mx), MMatrix)) + with assert_warns(DeprecationWarning): + assert_(isinstance(add.outer(mx, mx), MMatrix)) assert_(isinstance(hypot(mx, mx), MMatrix)) assert_(isinstance(hypot(mx, x), MMatrix)) @@ -220,7 +212,7 @@ def test_masked_binary_operations2(self): assert_(isinstance(divide(mx, x), MMatrix)) assert_equal(divide(mx, mx), divide(xmx, xmx)) -class TestConcatenator(object): +class TestConcatenator: # Tests for mr_, the equivalent of r_ for masked arrays. def test_matrix_builder(self): diff --git a/numpy/matrixlib/tests/test_matrix_linalg.py b/numpy/matrixlib/tests/test_matrix_linalg.py index 85c7693b4a39..106c2e38217a 100644 --- a/numpy/matrixlib/tests/test_matrix_linalg.py +++ b/numpy/matrixlib/tests/test_matrix_linalg.py @@ -1,14 +1,4 @@ """ Test functions for linalg module using the matrix class.""" -from __future__ import division, absolute_import, print_function - -# As we are testing matrices, we ignore its PendingDeprecationWarnings -try: - import pytest - pytestmark = pytest.mark.filterwarnings( - 'ignore:the matrix subclass is not:PendingDeprecationWarning') -except ImportError: - pass - import numpy as np from numpy.linalg.tests.test_linalg import ( diff --git a/numpy/matrixlib/tests/test_multiarray.py b/numpy/matrixlib/tests/test_multiarray.py index 2f04b49d61b6..638d0d1534de 100644 --- a/numpy/matrixlib/tests/test_multiarray.py +++ b/numpy/matrixlib/tests/test_multiarray.py @@ -1,17 +1,7 @@ -from __future__ import division, absolute_import, print_function - -# As we are testing matrices, we ignore its PendingDeprecationWarnings -try: - import pytest - pytestmark = pytest.mark.filterwarnings( - 'ignore:the matrix subclass is not:PendingDeprecationWarning') -except ImportError: - pass - import numpy as np from numpy.testing import assert_, assert_equal, assert_array_equal -class TestView(object): +class TestView: def test_type(self): x = np.array([1, 2, 3]) assert_(isinstance(x.view(np.matrix), np.matrix)) diff --git a/numpy/matrixlib/tests/test_numeric.py b/numpy/matrixlib/tests/test_numeric.py index cfdada126a61..a772bb388847 100644 --- a/numpy/matrixlib/tests/test_numeric.py +++ b/numpy/matrixlib/tests/test_numeric.py @@ -1,17 +1,7 @@ -from __future__ import division, absolute_import, print_function - -# As we are testing matrices, we ignore its PendingDeprecationWarnings -try: - import pytest - pytestmark = pytest.mark.filterwarnings( - 'ignore:the matrix subclass is not:PendingDeprecationWarning') -except ImportError: - pass - import numpy as np from numpy.testing import assert_equal -class TestDot(object): +class TestDot: def test_matscalar(self): b1 = np.matrix(np.ones((3, 3), dtype=complex)) assert_equal(b1*1.0, b1) diff --git a/numpy/matrixlib/tests/test_regression.py b/numpy/matrixlib/tests/test_regression.py index 439704ccf01d..a54d44020a70 100644 --- a/numpy/matrixlib/tests/test_regression.py +++ b/numpy/matrixlib/tests/test_regression.py @@ -1,18 +1,8 @@ -from __future__ import division, absolute_import, print_function - -# As we are testing matrices, we ignore its PendingDeprecationWarnings -try: - import pytest - pytestmark = pytest.mark.filterwarnings( - 'ignore:the matrix subclass is not:PendingDeprecationWarning') -except ImportError: - pass - import numpy as np from numpy.testing import assert_, assert_equal, assert_raises -class TestRegression(object): +class TestRegression: def test_kron_matrix(self): # Ticket #71 x = np.matrix('[1 0; 1 0]') diff --git a/numpy/polynomial/__init__.py b/numpy/polynomial/__init__.py index c18bebedb677..5a3addf4cd3d 100644 --- a/numpy/polynomial/__init__.py +++ b/numpy/polynomial/__init__.py @@ -12,9 +12,107 @@ implemented as operations on the coefficients. Additional (module-specific) information can be found in the docstring for the module of interest. -""" -from __future__ import division, absolute_import, print_function +This package provides *convenience classes* for each of six different kinds +of polynomials: + + ======================== ================ + **Name** **Provides** + ======================== ================ + `~polynomial.Polynomial` Power series + `~chebyshev.Chebyshev` Chebyshev series + `~legendre.Legendre` Legendre series + `~laguerre.Laguerre` Laguerre series + `~hermite.Hermite` Hermite series + `~hermite_e.HermiteE` HermiteE series + ======================== ================ + +These *convenience classes* provide a consistent interface for creating, +manipulating, and fitting data with polynomials of different bases. +The convenience classes are the preferred interface for the `~numpy.polynomial` +package, and are available from the ``numpy.polynomial`` namespace. +This eliminates the need to navigate to the corresponding submodules, e.g. +``np.polynomial.Polynomial`` or ``np.polynomial.Chebyshev`` instead of +``np.polynomial.polynomial.Polynomial`` or +``np.polynomial.chebyshev.Chebyshev``, respectively. +The classes provide a more consistent and concise interface than the +type-specific functions defined in the submodules for each type of polynomial. +For example, to fit a Chebyshev polynomial with degree ``1`` to data given +by arrays ``xdata`` and ``ydata``, the +`~chebyshev.Chebyshev.fit` class method:: + + >>> from numpy.polynomial import Chebyshev + >>> c = Chebyshev.fit(xdata, ydata, deg=1) + +is preferred over the `chebyshev.chebfit` function from the +``np.polynomial.chebyshev`` module:: + + >>> from numpy.polynomial.chebyshev import chebfit + >>> c = chebfit(xdata, ydata, deg=1) + +See :doc:`routines.polynomials.classes` for more details. + +Convenience Classes +=================== + +The following lists the various constants and methods common to all of +the classes representing the various kinds of polynomials. In the following, +the term ``Poly`` represents any one of the convenience classes (e.g. +`~polynomial.Polynomial`, `~chebyshev.Chebyshev`, `~hermite.Hermite`, etc.) +while the lowercase ``p`` represents an **instance** of a polynomial class. + +Constants +--------- + +- ``Poly.domain`` -- Default domain +- ``Poly.window`` -- Default window +- ``Poly.basis_name`` -- String used to represent the basis +- ``Poly.maxpower`` -- Maximum value ``n`` such that ``p**n`` is allowed +- ``Poly.nickname`` -- String used in printing + +Creation +-------- + +Methods for creating polynomial instances. + +- ``Poly.basis(degree)`` -- Basis polynomial of given degree +- ``Poly.identity()`` -- ``p`` where ``p(x) = x`` for all ``x`` +- ``Poly.fit(x, y, deg)`` -- ``p`` of degree ``deg`` with coefficients + determined by the least-squares fit to the data ``x``, ``y`` +- ``Poly.fromroots(roots)`` -- ``p`` with specified roots +- ``p.copy()`` -- Create a copy of ``p`` + +Conversion +---------- +Methods for converting a polynomial instance of one kind to another. + +- ``p.cast(Poly)`` -- Convert ``p`` to instance of kind ``Poly`` +- ``p.convert(Poly)`` -- Convert ``p`` to instance of kind ``Poly`` or map + between ``domain`` and ``window`` + +Calculus +-------- +- ``p.deriv()`` -- Take the derivative of ``p`` +- ``p.integ()`` -- Integrate ``p`` + +Validation +---------- +- ``Poly.has_samecoef(p1, p2)`` -- Check if coefficients match +- ``Poly.has_samedomain(p1, p2)`` -- Check if domains match +- ``Poly.has_sametype(p1, p2)`` -- Check if types match +- ``Poly.has_samewindow(p1, p2)`` -- Check if windows match + +Misc +---- +- ``p.linspace()`` -- Return ``x, p(x)`` at equally-spaced points in ``domain`` +- ``p.mapparms()`` -- Return the parameters for the linear mapping between + ``domain`` and ``window``. +- ``p.roots()`` -- Return the roots of `p`. +- ``p.trim()`` -- Remove trailing coefficients. +- ``p.cutdeg(degree)`` -- Truncate p to given degree +- ``p.truncate(size)`` -- Truncate p to given size + +""" from .polynomial import Polynomial from .chebyshev import Chebyshev from .legendre import Legendre @@ -22,6 +120,66 @@ from .hermite_e import HermiteE from .laguerre import Laguerre -from numpy.testing._private.pytesttester import PytestTester +__all__ = [ + "set_default_printstyle", + "polynomial", "Polynomial", + "chebyshev", "Chebyshev", + "legendre", "Legendre", + "hermite", "Hermite", + "hermite_e", "HermiteE", + "laguerre", "Laguerre", +] + + +def set_default_printstyle(style): + """ + Set the default format for the string representation of polynomials. + + Values for ``style`` must be valid inputs to ``__format__``, i.e. 'ascii' + or 'unicode'. + + Parameters + ---------- + style : str + Format string for default printing style. Must be either 'ascii' or + 'unicode'. + + Notes + ----- + The default format depends on the platform: 'unicode' is used on + Unix-based systems and 'ascii' on Windows. This determination is based on + default font support for the unicode superscript and subscript ranges. + + Examples + -------- + >>> p = np.polynomial.Polynomial([1, 2, 3]) + >>> c = np.polynomial.Chebyshev([1, 2, 3]) + >>> np.polynomial.set_default_printstyle('unicode') + >>> print(p) + 1.0 + 2.0·x¹ + 3.0·x² + >>> print(c) + 1.0 + 2.0·T₁(x) + 3.0·T₂(x) + >>> np.polynomial.set_default_printstyle('ascii') + >>> print(p) + 1.0 + 2.0 x**1 + 3.0 x**2 + >>> print(c) + 1.0 + 2.0 T_1(x) + 3.0 T_2(x) + >>> # Formatting supersedes all class/package-level defaults + >>> print(f"{p:unicode}") + 1.0 + 2.0·x¹ + 3.0·x² + """ + if style not in ('unicode', 'ascii'): + raise ValueError( + f"Unsupported format string '{style}'. Valid options are 'ascii' " + f"and 'unicode'" + ) + _use_unicode = True + if style == 'ascii': + _use_unicode = False + from ._polybase import ABCPolyBase + ABCPolyBase._use_unicode = _use_unicode + + +from numpy._pytesttester import PytestTester test = PytestTester(__name__) del PytestTester diff --git a/numpy/polynomial/__init__.pyi b/numpy/polynomial/__init__.pyi new file mode 100644 index 000000000000..e0cfedd7aae7 --- /dev/null +++ b/numpy/polynomial/__init__.pyi @@ -0,0 +1,24 @@ +from typing import List + +from numpy._pytesttester import PytestTester + +from numpy.polynomial import ( + chebyshev as chebyshev, + hermite as hermite, + hermite_e as hermite_e, + laguerre as laguerre, + legendre as legendre, + polynomial as polynomial, +) +from numpy.polynomial.chebyshev import Chebyshev as Chebyshev +from numpy.polynomial.hermite import Hermite as Hermite +from numpy.polynomial.hermite_e import HermiteE as HermiteE +from numpy.polynomial.laguerre import Laguerre as Laguerre +from numpy.polynomial.legendre import Legendre as Legendre +from numpy.polynomial.polynomial import Polynomial as Polynomial + +__all__: List[str] +__path__: List[str] +test: PytestTester + +def set_default_printstyle(style): ... diff --git a/numpy/polynomial/_polybase.py b/numpy/polynomial/_polybase.py index 78392d2a2356..155d7280591b 100644 --- a/numpy/polynomial/_polybase.py +++ b/numpy/polynomial/_polybase.py @@ -6,18 +6,17 @@ abc module from the stdlib, hence it is only available for Python >= 2.6. """ -from __future__ import division, absolute_import, print_function - -from abc import ABCMeta, abstractmethod, abstractproperty -from numbers import Number +import os +import abc +import numbers import numpy as np from . import polyutils as pu __all__ = ['ABCPolyBase'] -class ABCPolyBase(object): - """An abstract base class for series classes. +class ABCPolyBase(abc.ABC): + """An abstract base class for immutable series classes. ABCPolyBase provides the standard Python numerical methods '+', '-', '*', '//', '%', 'divmod', '**', and '()' along with the @@ -59,7 +58,6 @@ class ABCPolyBase(object): Default window of the class. """ - __metaclass__ = ABCMeta # Not hashable __hash__ = None @@ -70,64 +68,110 @@ class ABCPolyBase(object): # Limit runaway size. T_n^m has degree n*m maxpower = 100 - @abstractproperty + # Unicode character mappings for improved __str__ + _superscript_mapping = str.maketrans({ + "0": "⁰", + "1": "¹", + "2": "²", + "3": "³", + "4": "⁴", + "5": "⁵", + "6": "⁶", + "7": "⁷", + "8": "⁸", + "9": "⁹" + }) + _subscript_mapping = str.maketrans({ + "0": "₀", + "1": "₁", + "2": "₂", + "3": "₃", + "4": "₄", + "5": "₅", + "6": "₆", + "7": "₇", + "8": "₈", + "9": "₉" + }) + # Some fonts don't support full unicode character ranges necessary for + # the full set of superscripts and subscripts, including common/default + # fonts in Windows shells/terminals. Therefore, default to ascii-only + # printing on windows. + _use_unicode = not os.name == 'nt' + + @property + @abc.abstractmethod def domain(self): pass - @abstractproperty + @property + @abc.abstractmethod def window(self): pass - @abstractproperty - def nickname(self): + @property + @abc.abstractmethod + def basis_name(self): pass - @abstractmethod - def _add(self): + @staticmethod + @abc.abstractmethod + def _add(c1, c2): pass - @abstractmethod - def _sub(self): + @staticmethod + @abc.abstractmethod + def _sub(c1, c2): pass - @abstractmethod - def _mul(self): + @staticmethod + @abc.abstractmethod + def _mul(c1, c2): pass - @abstractmethod - def _div(self): + @staticmethod + @abc.abstractmethod + def _div(c1, c2): pass - @abstractmethod - def _pow(self): + @staticmethod + @abc.abstractmethod + def _pow(c, pow, maxpower=None): pass - @abstractmethod - def _val(self): + @staticmethod + @abc.abstractmethod + def _val(x, c): pass - @abstractmethod - def _int(self): + @staticmethod + @abc.abstractmethod + def _int(c, m, k, lbnd, scl): pass - @abstractmethod - def _der(self): + @staticmethod + @abc.abstractmethod + def _der(c, m, scl): pass - @abstractmethod - def _fit(self): + @staticmethod + @abc.abstractmethod + def _fit(x, y, deg, rcond, full): pass - @abstractmethod - def _line(self): + @staticmethod + @abc.abstractmethod + def _line(off, scl): pass - @abstractmethod - def _roots(self): + @staticmethod + @abc.abstractmethod + def _roots(c): pass - @abstractmethod - def _fromroots(self): + @staticmethod + @abc.abstractmethod + def _fromroots(r): pass def has_samecoef(self, other): @@ -260,18 +304,164 @@ def __init__(self, coef, domain=None, window=None): self.window = window def __repr__(self): - format = "%s(%s, domain=%s, window=%s)" coef = repr(self.coef)[6:-1] domain = repr(self.domain)[6:-1] window = repr(self.window)[6:-1] name = self.__class__.__name__ - return format % (name, coef, domain, window) + return f"{name}({coef}, domain={domain}, window={window})" + + def __format__(self, fmt_str): + if fmt_str == '': + return self.__str__() + if fmt_str not in ('ascii', 'unicode'): + raise ValueError( + f"Unsupported format string '{fmt_str}' passed to " + f"{self.__class__}.__format__. Valid options are " + f"'ascii' and 'unicode'" + ) + if fmt_str == 'ascii': + return self._generate_string(self._str_term_ascii) + return self._generate_string(self._str_term_unicode) def __str__(self): - format = "%s(%s)" - coef = str(self.coef) - name = self.nickname - return format % (name, coef) + if self._use_unicode: + return self._generate_string(self._str_term_unicode) + return self._generate_string(self._str_term_ascii) + + def _generate_string(self, term_method): + """ + Generate the full string representation of the polynomial, using + ``term_method`` to generate each polynomial term. + """ + # Get configuration for line breaks + linewidth = np.get_printoptions().get('linewidth', 75) + if linewidth < 1: + linewidth = 1 + out = f"{self.coef[0]}" + for i, coef in enumerate(self.coef[1:]): + out += " " + power = str(i + 1) + # Polynomial coefficient + # The coefficient array can be an object array with elements that + # will raise a TypeError with >= 0 (e.g. strings or Python + # complex). In this case, represent the coefficient as-is. + try: + if coef >= 0: + next_term = f"+ {coef}" + else: + next_term = f"- {-coef}" + except TypeError: + next_term = f"+ {coef}" + # Polynomial term + next_term += term_method(power, "x") + # Length of the current line with next term added + line_len = len(out.split('\n')[-1]) + len(next_term) + # If not the last term in the polynomial, it will be two + # characters longer due to the +/- with the next term + if i < len(self.coef[1:]) - 1: + line_len += 2 + # Handle linebreaking + if line_len >= linewidth: + next_term = next_term.replace(" ", "\n", 1) + out += next_term + return out + + @classmethod + def _str_term_unicode(cls, i, arg_str): + """ + String representation of single polynomial term using unicode + characters for superscripts and subscripts. + """ + if cls.basis_name is None: + raise NotImplementedError( + "Subclasses must define either a basis_name, or override " + "_str_term_unicode(cls, i, arg_str)" + ) + return (f"·{cls.basis_name}{i.translate(cls._subscript_mapping)}" + f"({arg_str})") + + @classmethod + def _str_term_ascii(cls, i, arg_str): + """ + String representation of a single polynomial term using ** and _ to + represent superscripts and subscripts, respectively. + """ + if cls.basis_name is None: + raise NotImplementedError( + "Subclasses must define either a basis_name, or override " + "_str_term_ascii(cls, i, arg_str)" + ) + return f" {cls.basis_name}_{i}({arg_str})" + + @classmethod + def _repr_latex_term(cls, i, arg_str, needs_parens): + if cls.basis_name is None: + raise NotImplementedError( + "Subclasses must define either a basis name, or override " + "_repr_latex_term(i, arg_str, needs_parens)") + # since we always add parens, we don't care if the expression needs them + return f"{{{cls.basis_name}}}_{{{i}}}({arg_str})" + + @staticmethod + def _repr_latex_scalar(x): + # TODO: we're stuck with disabling math formatting until we handle + # exponents in this function + return r'\text{{{}}}'.format(x) + + def _repr_latex_(self): + # get the scaled argument string to the basis functions + off, scale = self.mapparms() + if off == 0 and scale == 1: + term = 'x' + needs_parens = False + elif scale == 1: + term = f"{self._repr_latex_scalar(off)} + x" + needs_parens = True + elif off == 0: + term = f"{self._repr_latex_scalar(scale)}x" + needs_parens = True + else: + term = ( + f"{self._repr_latex_scalar(off)} + " + f"{self._repr_latex_scalar(scale)}x" + ) + needs_parens = True + + mute = r"\color{{LightGray}}{{{}}}".format + + parts = [] + for i, c in enumerate(self.coef): + # prevent duplication of + and - signs + if i == 0: + coef_str = f"{self._repr_latex_scalar(c)}" + elif not isinstance(c, numbers.Real): + coef_str = f" + ({self._repr_latex_scalar(c)})" + elif not np.signbit(c): + coef_str = f" + {self._repr_latex_scalar(c)}" + else: + coef_str = f" - {self._repr_latex_scalar(-c)}" + + # produce the string for the term + term_str = self._repr_latex_term(i, term, needs_parens) + if term_str == '1': + part = coef_str + else: + part = rf"{coef_str}\,{term_str}" + + if c == 0: + part = mute(part) + + parts.append(part) + + if parts: + body = ''.join(parts) + else: + # in case somehow there are no coefficients at all + body = '0' + + return rf"$x \mapsto {body}$" + + # Pickle and copy @@ -330,17 +520,15 @@ def __mul__(self, other): return NotImplemented return self.__class__(coef, self.domain, self.window) - def __div__(self, other): - # set to __floordiv__, /, for now. - return self.__floordiv__(other) - def __truediv__(self, other): # there is no true divide if the rhs is not a Number, although it # could return the first n elements of an infinite series. # It is hard to see where n would come from, though. - if not isinstance(other, Number) or isinstance(other, bool): - form = "unsupported types for true division: '%s', '%s'" - raise TypeError(form % (type(self), type(other))) + if not isinstance(other, numbers.Number) or isinstance(other, bool): + raise TypeError( + f"unsupported types for true division: " + f"'{type(self)}', '{type(other)}'" + ) return self.__floordiv__(other) def __floordiv__(self, other): @@ -359,8 +547,8 @@ def __divmod__(self, other): othercoef = self._get_coefficients(other) try: quo, rem = self._div(self.coef, othercoef) - except ZeroDivisionError as e: - raise e + except ZeroDivisionError: + raise except Exception: return NotImplemented quo = self.__class__(quo, self.domain, self.window) @@ -417,17 +605,14 @@ def __rmod__(self, other): def __rdivmod__(self, other): try: quo, rem = self._div(other, self.coef) - except ZeroDivisionError as e: - raise e + except ZeroDivisionError: + raise except Exception: return NotImplemented quo = self.__class__(quo, self.domain, self.window) rem = self.__class__(rem, self.domain, self.window) return quo, rem - # Enhance me - # some augmented arithmetic operations could be added here - def __eq__(self, other): res = (isinstance(other, self.__class__) and np.all(self.domain == other.domain) and @@ -509,7 +694,7 @@ def trim(self, tol=0): Returns ------- new_series : series - Contains the new set of coefficients. + New instance of series with trimmed coefficients. """ coef = pu.trimcoef(self.coef, tol) @@ -572,9 +757,6 @@ def convert(self, domain=None, kind=None, window=None): Conversion between domains and class types can result in numerically ill defined series. - Examples - -------- - """ if kind is None: kind = self.__class__ @@ -729,10 +911,8 @@ def fit(cls, x, y, deg, domain=None, rcond=None, full=False, w=None, ---------- x : array_like, shape (M,) x-coordinates of the M sample points ``(x[i], y[i])``. - y : array_like, shape (M,) or (M, K) - y-coordinates of the sample points. Several data sets of sample - points sharing the same x-coordinates can be fitted at once by - passing in a 2D-array that contains one dataset per column. + y : array_like, shape (M,) + y-coordinates of the M sample points ``(x[i], y[i])``. deg : int or 1-D array_like Degree(s) of the fitting polynomials. If `deg` is a single integer all terms up to and including the `deg`'th term are included in the @@ -756,11 +936,11 @@ class domain in NumPy 1.4 and ``None`` in later versions. diagnostic information from the singular value decomposition is also returned. w : array_like, shape (M,), optional - Weights. If not None the contribution of each point - ``(x[i],y[i])`` to the fit is weighted by `w[i]`. Ideally the - weights are chosen so that the errors of the products - ``w[i]*y[i]`` all have the same variance. The default value is - None. + Weights. If not None, the weight ``w[i]`` applies to the unsquared + residual ``y[i] - y_hat[i]`` at ``x[i]``. Ideally the weights are + chosen so that the errors of the products ``w[i]*y[i]`` all have + the same variance. When using inverse-variance weighting, use + ``w[i] = 1/sigma(y[i])``. The default value is None. .. versionadded:: 1.5.0 window : {[beg, end]}, optional @@ -773,15 +953,17 @@ class domain in NumPy 1.4 and ``None`` in later versions. ------- new_series : series A series that represents the least squares fit to the data and - has the domain specified in the call. + has the domain and window specified in the call. If the + coefficients for the unscaled and unshifted basis polynomials are + of interest, do ``new_series.convert().coef``. [resid, rank, sv, rcond] : list - These values are only returned if `full` = True + These values are only returned if ``full == True`` - resid -- sum of squared residuals of the least squares fit - rank -- the numerical rank of the scaled Vandermonde matrix - sv -- singular values of the scaled Vandermonde matrix - rcond -- value of `rcond`. + - resid -- sum of squared residuals of the least squares fit + - rank -- the numerical rank of the scaled Vandermonde matrix + - sv -- singular values of the scaled Vandermonde matrix + - rcond -- value of `rcond`. For more details, see `linalg.lstsq`. diff --git a/numpy/polynomial/_polybase.pyi b/numpy/polynomial/_polybase.pyi new file mode 100644 index 000000000000..c4160146947f --- /dev/null +++ b/numpy/polynomial/_polybase.pyi @@ -0,0 +1,69 @@ +import abc +from typing import Any, List, ClassVar + +__all__: List[str] + +class ABCPolyBase(abc.ABC): + __hash__: ClassVar[None] # type: ignore[assignment] + __array_ufunc__: ClassVar[None] + maxpower: ClassVar[int] + coef: Any + @property + @abc.abstractmethod + def domain(self): ... + @property + @abc.abstractmethod + def window(self): ... + @property + @abc.abstractmethod + def basis_name(self): ... + def has_samecoef(self, other): ... + def has_samedomain(self, other): ... + def has_samewindow(self, other): ... + def has_sametype(self, other): ... + def __init__(self, coef, domain=..., window=...): ... + def __format__(self, fmt_str): ... + def __call__(self, arg): ... + def __iter__(self): ... + def __len__(self): ... + def __neg__(self): ... + def __pos__(self): ... + def __add__(self, other): ... + def __sub__(self, other): ... + def __mul__(self, other): ... + def __truediv__(self, other): ... + def __floordiv__(self, other): ... + def __mod__(self, other): ... + def __divmod__(self, other): ... + def __pow__(self, other): ... + def __radd__(self, other): ... + def __rsub__(self, other): ... + def __rmul__(self, other): ... + def __rdiv__(self, other): ... + def __rtruediv__(self, other): ... + def __rfloordiv__(self, other): ... + def __rmod__(self, other): ... + def __rdivmod__(self, other): ... + def __eq__(self, other): ... + def __ne__(self, other): ... + def copy(self): ... + def degree(self): ... + def cutdeg(self, deg): ... + def trim(self, tol=...): ... + def truncate(self, size): ... + def convert(self, domain=..., kind=..., window=...): ... + def mapparms(self): ... + def integ(self, m=..., k = ..., lbnd=...): ... + def deriv(self, m=...): ... + def roots(self): ... + def linspace(self, n=..., domain=...): ... + @classmethod + def fit(cls, x, y, deg, domain=..., rcond=..., full=..., w=..., window=...): ... + @classmethod + def fromroots(cls, roots, domain = ..., window=...): ... + @classmethod + def identity(cls, domain=..., window=...): ... + @classmethod + def basis(cls, deg, domain=..., window=...): ... + @classmethod + def cast(cls, series, domain=..., window=...): ... diff --git a/numpy/polynomial/chebyshev.py b/numpy/polynomial/chebyshev.py index 8add0acbc90f..89ce815d571e 100644 --- a/numpy/polynomial/chebyshev.py +++ b/numpy/polynomial/chebyshev.py @@ -1,5 +1,7 @@ """ -Objects for dealing with Chebyshev series. +==================================================== +Chebyshev Series (:mod:`numpy.polynomial.chebyshev`) +==================================================== This module provides a number of objects (mostly functions) useful for dealing with Chebyshev series, including a `Chebyshev` class that @@ -7,56 +9,75 @@ on how this module represents and works with such polynomials is in the docstring for its "parent" sub-package, `numpy.polynomial`). +Classes +------- + +.. autosummary:: + :toctree: generated/ + + Chebyshev + + Constants --------- -- `chebdomain` -- Chebyshev series default domain, [-1,1]. -- `chebzero` -- (Coefficients of the) Chebyshev series that evaluates - identically to 0. -- `chebone` -- (Coefficients of the) Chebyshev series that evaluates - identically to 1. -- `chebx` -- (Coefficients of the) Chebyshev series for the identity map, - ``f(x) = x``. + +.. autosummary:: + :toctree: generated/ + + chebdomain + chebzero + chebone + chebx Arithmetic ---------- -- `chebadd` -- add two Chebyshev series. -- `chebsub` -- subtract one Chebyshev series from another. -- `chebmul` -- multiply two Chebyshev series. -- `chebdiv` -- divide one Chebyshev series by another. -- `chebpow` -- raise a Chebyshev series to an positive integer power -- `chebval` -- evaluate a Chebyshev series at given points. -- `chebval2d` -- evaluate a 2D Chebyshev series at given points. -- `chebval3d` -- evaluate a 3D Chebyshev series at given points. -- `chebgrid2d` -- evaluate a 2D Chebyshev series on a Cartesian product. -- `chebgrid3d` -- evaluate a 3D Chebyshev series on a Cartesian product. + +.. autosummary:: + :toctree: generated/ + + chebadd + chebsub + chebmulx + chebmul + chebdiv + chebpow + chebval + chebval2d + chebval3d + chebgrid2d + chebgrid3d Calculus -------- -- `chebder` -- differentiate a Chebyshev series. -- `chebint` -- integrate a Chebyshev series. + +.. autosummary:: + :toctree: generated/ + + chebder + chebint Misc Functions -------------- -- `chebfromroots` -- create a Chebyshev series with specified roots. -- `chebroots` -- find the roots of a Chebyshev series. -- `chebvander` -- Vandermonde-like matrix for Chebyshev polynomials. -- `chebvander2d` -- Vandermonde-like matrix for 2D power series. -- `chebvander3d` -- Vandermonde-like matrix for 3D power series. -- `chebgauss` -- Gauss-Chebyshev quadrature, points and weights. -- `chebweight` -- Chebyshev weight function. -- `chebcompanion` -- symmetrized companion matrix in Chebyshev form. -- `chebfit` -- least-squares fit returning a Chebyshev series. -- `chebpts1` -- Chebyshev points of the first kind. -- `chebpts2` -- Chebyshev points of the second kind. -- `chebtrim` -- trim leading coefficients from a Chebyshev series. -- `chebline` -- Chebyshev series representing given straight line. -- `cheb2poly` -- convert a Chebyshev series to a polynomial. -- `poly2cheb` -- convert a polynomial to a Chebyshev series. -- `chebinterpolate` -- interpolate a function at the Chebyshev points. -Classes -------- -- `Chebyshev` -- A Chebyshev series class. +.. autosummary:: + :toctree: generated/ + + chebfromroots + chebroots + chebvander + chebvander2d + chebvander3d + chebgauss + chebweight + chebcompanion + chebfit + chebpts1 + chebpts2 + chebtrim + chebline + cheb2poly + poly2cheb + chebinterpolate See also -------- @@ -67,13 +88,13 @@ The implementations of multiplication, division, integration, and differentiation use the algebraic identities [1]_: -.. math :: +.. math:: T_n(x) = \\frac{z^n + z^{-n}}{2} \\\\ z\\frac{dx}{dz} = \\frac{z - z^{-1}}{2}. where -.. math :: x = \\frac{z + z^{-1}}{2}. +.. math:: x = \\frac{z + z^{-1}}{2}. These identities allow a Chebyshev series to be expressed as a finite, symmetric Laurent series. In this module, this sort of Laurent series @@ -83,13 +104,9 @@ ---------- .. [1] A. T. Benjamin, et al., "Combinatorial Trigonometry with Chebyshev Polynomials," *Journal of Statistical Planning and Inference 14*, 2008 - (preprint: http://www.math.hmc.edu/~benjamin/papers/CombTrig.pdf, pg. 4) + (https://web.archive.org/web/20080221202153/https://www.math.hmc.edu/~benjamin/papers/CombTrig.pdf, pg. 4) """ -from __future__ import division, absolute_import, print_function - -import numbers -import warnings import numpy as np import numpy.linalg as la from numpy.core.multiarray import normalize_axis_index @@ -114,9 +131,9 @@ # def _cseries_to_zseries(c): - """Covert Chebyshev series to z-series. + """Convert Chebyshev series to z-series. - Covert a Chebyshev series to the equivalent z-series. The result is + Convert a Chebyshev series to the equivalent z-series. The result is never an empty array. The dtype of the return is the same as that of the input. No checks are run on the arguments as this routine is for internal use. @@ -139,9 +156,9 @@ def _cseries_to_zseries(c): def _zseries_to_cseries(zs): - """Covert z-series to a Chebyshev series. + """Convert z-series to a Chebyshev series. - Covert a z series to the equivalent Chebyshev series. The result is + Convert a z series to the equivalent Chebyshev series. The result is never an empty array. The dtype of the return is the same as that of the input. No checks are run on the arguments as this routine is for internal use. @@ -225,15 +242,15 @@ def _zseries_div(z1, z2): """ z1 = z1.copy() z2 = z2.copy() - len1 = len(z1) - len2 = len(z2) - if len2 == 1: + lc1 = len(z1) + lc2 = len(z2) + if lc2 == 1: z1 /= z2 return z1, z1[:1]*0 - elif len1 < len2: + elif lc1 < lc2: return z1[:1]*0, z1 else: - dlen = len1 - len2 + dlen = lc1 - lc2 scl = z2[0] z2 /= scl quo = np.empty(dlen + 1, dtype=z1.dtype) @@ -244,16 +261,16 @@ def _zseries_div(z1, z2): quo[i] = z1[i] quo[dlen - i] = r tmp = r*z2 - z1[i:i+len2] -= tmp - z1[j:j+len2] -= tmp + z1[i:i+lc2] -= tmp + z1[j:j+lc2] -= tmp i += 1 j -= 1 r = z1[i] quo[i] = r tmp = r*z2 - z1[i:i+len2] -= tmp + z1[i:i+lc2] -= tmp quo /= scl - rem = z1[i+1:i-1+len2].copy() + rem = z1[i+1:i-1+lc2].copy() return quo, rem @@ -361,12 +378,12 @@ def poly2cheb(pol): >>> from numpy import polynomial as P >>> p = P.Polynomial(range(4)) >>> p - Polynomial([ 0., 1., 2., 3.], domain=[-1, 1], window=[-1, 1]) + Polynomial([0., 1., 2., 3.], domain=[-1, 1], window=[-1, 1]) >>> c = p.convert(kind=P.Chebyshev) >>> c - Chebyshev([ 1. , 3.25, 1. , 0.75], domain=[-1, 1], window=[-1, 1]) - >>> P.poly2cheb(range(4)) - array([ 1. , 3.25, 1. , 0.75]) + Chebyshev([1. , 3.25, 1. , 0.75], domain=[-1., 1.], window=[-1., 1.]) + >>> P.chebyshev.poly2cheb(range(4)) + array([1. , 3.25, 1. , 0.75]) """ [pol] = pu.as_series([pol]) @@ -413,12 +430,12 @@ def cheb2poly(c): >>> from numpy import polynomial as P >>> c = P.Chebyshev(range(4)) >>> c - Chebyshev([ 0., 1., 2., 3.], [-1., 1.]) + Chebyshev([0., 1., 2., 3.], domain=[-1, 1], window=[-1, 1]) >>> p = c.convert(kind=P.Polynomial) >>> p - Polynomial([ -2., -8., 4., 12.], [-1., 1.]) - >>> P.cheb2poly(range(4)) - array([ -2., -8., 4., 12.]) + Polynomial([-2., -8., 4., 12.], domain=[-1., 1.], window=[-1., 1.]) + >>> P.chebyshev.cheb2poly(range(4)) + array([-2., -8., 4., 12.]) """ from .polynomial import polyadd, polysub, polymulx @@ -460,8 +477,6 @@ def chebline(off, scl): """ Chebyshev series whose graph is a straight line. - - Parameters ---------- off, scl : scalars @@ -475,7 +490,11 @@ def chebline(off, scl): See Also -------- - polyline + numpy.polynomial.polynomial.polyline + numpy.polynomial.legendre.legline + numpy.polynomial.laguerre.lagline + numpy.polynomial.hermite.hermline + numpy.polynomial.hermite_e.hermeline Examples -------- @@ -528,8 +547,11 @@ def chebfromroots(roots): See Also -------- - polyfromroots, legfromroots, lagfromroots, hermfromroots, - hermefromroots. + numpy.polynomial.polynomial.polyfromroots + numpy.polynomial.legendre.legfromroots + numpy.polynomial.laguerre.lagfromroots + numpy.polynomial.hermite.hermfromroots + numpy.polynomial.hermite_e.hermefromroots Examples -------- @@ -538,24 +560,10 @@ def chebfromroots(roots): array([ 0. , -0.25, 0. , 0.25]) >>> j = complex(0,1) >>> C.chebfromroots((-j,j)) # x^2 + 1 relative to the standard basis - array([ 1.5+0.j, 0.0+0.j, 0.5+0.j]) + array([1.5+0.j, 0. +0.j, 0.5+0.j]) """ - if len(roots) == 0: - return np.ones(1) - else: - [roots] = pu.as_series([roots], trim=False) - roots.sort() - p = [chebline(-r, 1) for r in roots] - n = len(p) - while n > 1: - m, r = divmod(n, 2) - tmp = [chebmul(p[i], p[i+m]) for i in range(m)] - if r: - tmp[0] = chebmul(tmp[0], p[-1]) - p = tmp - n = m - return p[0] + return pu._fromroots(chebline, chebmul, roots) def chebadd(c1, c2): @@ -579,7 +587,7 @@ def chebadd(c1, c2): See Also -------- - chebsub, chebmul, chebdiv, chebpow + chebsub, chebmulx, chebmul, chebdiv, chebpow Notes ----- @@ -594,18 +602,10 @@ def chebadd(c1, c2): >>> c1 = (1,2,3) >>> c2 = (3,2,1) >>> C.chebadd(c1,c2) - array([ 4., 4., 4.]) + array([4., 4., 4.]) """ - # c1, c2 are trimmed copies - [c1, c2] = pu.as_series([c1, c2]) - if len(c1) > len(c2): - c1[:c2.size] += c2 - ret = c1 - else: - c2[:c1.size] += c1 - ret = c2 - return pu.trimseq(ret) + return pu._add(c1, c2) def chebsub(c1, c2): @@ -629,7 +629,7 @@ def chebsub(c1, c2): See Also -------- - chebadd, chebmul, chebdiv, chebpow + chebadd, chebmulx, chebmul, chebdiv, chebpow Notes ----- @@ -649,16 +649,7 @@ def chebsub(c1, c2): array([ 2., 0., -2.]) """ - # c1, c2 are trimmed copies - [c1, c2] = pu.as_series([c1, c2]) - if len(c1) > len(c2): - c1[:c2.size] -= c2 - ret = c1 - else: - c2 = -c2 - c2[:c1.size] += c1 - ret = c2 - return pu.trimseq(ret) + return pu._sub(c1, c2) def chebmulx(c): @@ -684,6 +675,12 @@ def chebmulx(c): .. versionadded:: 1.5.0 + Examples + -------- + >>> from numpy.polynomial import chebyshev as C + >>> C.chebmulx([1,2,3]) + array([1. , 2.5, 1. , 1.5]) + """ # c is a trimmed copy [c] = pu.as_series([c]) @@ -722,7 +719,7 @@ def chebmul(c1, c2): See Also -------- - chebadd, chebsub, chebdiv, chebpow + chebadd, chebsub, chebmulx, chebdiv, chebpow Notes ----- @@ -773,7 +770,7 @@ def chebdiv(c1, c2): See Also -------- - chebadd, chebsub, chebmul, chebpow + chebadd, chebsub, chebmulx, chebmul, chebpow Notes ----- @@ -790,10 +787,10 @@ def chebdiv(c1, c2): >>> c1 = (1,2,3) >>> c2 = (3,2,1) >>> C.chebdiv(c1,c2) # quotient "intuitive," remainder not - (array([ 3.]), array([-8., -4.])) + (array([3.]), array([-8., -4.])) >>> c2 = (0,1,2,3) >>> C.chebdiv(c2,c1) # neither "intuitive" - (array([ 0., 2.]), array([-2., -4.])) + (array([0., 2.]), array([-2., -4.])) """ # c1, c2 are trimmed copies @@ -801,6 +798,7 @@ def chebdiv(c1, c2): if c2[-1] == 0: raise ZeroDivisionError() + # note: this is more efficient than `pu._div(chebmul, c1, c2)` lc1 = len(c1) lc2 = len(c2) if lc1 < lc2: @@ -841,12 +839,18 @@ def chebpow(c, pow, maxpower=16): See Also -------- - chebadd, chebsub, chebmul, chebdiv + chebadd, chebsub, chebmulx, chebmul, chebdiv Examples -------- + >>> from numpy.polynomial import chebyshev as C + >>> C.chebpow([1, 2, 3, 4], 2) + array([15.5, 22. , 16. , ..., 12.5, 12. , 8. ]) """ + # note: this is more efficient than `pu._pow(chebmul, c1, c2)`, as it + # avoids converting between z and c series repeatedly + # c is a trimmed copy [c] = pu.as_series([c]) power = int(pow) @@ -919,26 +923,22 @@ def chebder(c, m=1, scl=1, axis=0): >>> from numpy.polynomial import chebyshev as C >>> c = (1,2,3,4) >>> C.chebder(c) - array([ 14., 12., 24.]) + array([14., 12., 24.]) >>> C.chebder(c,3) - array([ 96.]) + array([96.]) >>> C.chebder(c,scl=-1) array([-14., -12., -24.]) >>> C.chebder(c,2,-1) - array([ 12., 96.]) + array([12., 96.]) """ - c = np.array(c, ndmin=1, copy=1) + c = np.array(c, ndmin=1, copy=True) if c.dtype.char in '?bBhHiIlLqQpP': c = c.astype(np.double) - cnt, iaxis = [int(t) for t in [m, axis]] - - if cnt != m: - raise ValueError("The order of derivation must be integer") + cnt = pu._deprecate_as_int(m, "the order of derivation") + iaxis = pu._deprecate_as_int(axis, "the axis") if cnt < 0: raise ValueError("The order of derivation must be non-negative") - if iaxis != axis: - raise ValueError("The axis must be integer") iaxis = normalize_axis_index(iaxis, c.ndim) if cnt == 0: @@ -1039,8 +1039,8 @@ def chebint(c, m=1, k=[], lbnd=0, scl=1, axis=0): >>> C.chebint(c) array([ 0.5, -0.5, 0.5, 0.5]) >>> C.chebint(c,3) - array([ 0.03125 , -0.1875 , 0.04166667, -0.05208333, 0.01041667, - 0.00625 ]) + array([ 0.03125 , -0.1875 , 0.04166667, -0.05208333, 0.01041667, # may vary + 0.00625 ]) >>> C.chebint(c, k=3) array([ 3.5, -0.5, 0.5, 0.5]) >>> C.chebint(c,lbnd=-2) @@ -1049,15 +1049,13 @@ def chebint(c, m=1, k=[], lbnd=0, scl=1, axis=0): array([-1., 1., -1., -1.]) """ - c = np.array(c, ndmin=1, copy=1) + c = np.array(c, ndmin=1, copy=True) if c.dtype.char in '?bBhHiIlLqQpP': c = c.astype(np.double) if not np.iterable(k): k = [k] - cnt, iaxis = [int(t) for t in [m, axis]] - - if cnt != m: - raise ValueError("The order of integration must be integer") + cnt = pu._deprecate_as_int(m, "the order of integration") + iaxis = pu._deprecate_as_int(axis, "the axis") if cnt < 0: raise ValueError("The order of integration must be non-negative") if len(k) > cnt: @@ -1066,8 +1064,6 @@ def chebint(c, m=1, k=[], lbnd=0, scl=1, axis=0): raise ValueError("lbnd must be a scalar.") if np.ndim(scl) != 0: raise ValueError("scl must be a scalar.") - if iaxis != axis: - raise ValueError("The axis must be integer") iaxis = normalize_axis_index(iaxis, c.ndim) if cnt == 0: @@ -1087,7 +1083,6 @@ def chebint(c, m=1, k=[], lbnd=0, scl=1, axis=0): if n > 1: tmp[2] = c[1]/4 for j in range(2, n): - t = c[j]/(2*j + 1) tmp[j + 1] = c[j]/(2*(j + 1)) tmp[j - 1] -= c[j]/(2*(j - 1)) tmp[0] += k[i] - chebval(lbnd, tmp) @@ -1154,11 +1149,8 @@ def chebval(x, c, tensor=True): ----- The evaluation uses Clenshaw recursion, aka synthetic division. - Examples - -------- - """ - c = np.array(c, ndmin=1, copy=1) + c = np.array(c, ndmin=1, copy=True) if c.dtype.char in '?bBhHiIlLqQpP': c = c.astype(np.double) if isinstance(x, (tuple, list)): @@ -1229,14 +1221,7 @@ def chebval2d(x, y, c): .. versionadded:: 1.7.0 """ - try: - x, y = np.array((x, y), copy=0) - except Exception: - raise ValueError('x, y are incompatible') - - c = chebval(x, c) - c = chebval(y, c, tensor=False) - return c + return pu._valnd(chebval, c, x, y) def chebgrid2d(x, y, c): @@ -1289,9 +1274,7 @@ def chebgrid2d(x, y, c): .. versionadded:: 1.7.0 """ - c = chebval(x, c) - c = chebval(y, c) - return c + return pu._gridnd(chebval, c, x, y) def chebval3d(x, y, z, c): @@ -1342,15 +1325,7 @@ def chebval3d(x, y, z, c): .. versionadded:: 1.7.0 """ - try: - x, y, z = np.array((x, y, z), copy=0) - except Exception: - raise ValueError('x, y, z are incompatible') - - c = chebval(x, c) - c = chebval(y, c, tensor=False) - c = chebval(z, c, tensor=False) - return c + return pu._valnd(chebval, c, x, y, z) def chebgrid3d(x, y, z, c): @@ -1406,10 +1381,7 @@ def chebgrid3d(x, y, z, c): .. versionadded:: 1.7.0 """ - c = chebval(x, c) - c = chebval(y, c) - c = chebval(z, c) - return c + return pu._gridnd(chebval, c, x, y, z) def chebvander(x, deg): @@ -1447,13 +1419,11 @@ def chebvander(x, deg): the converted `x`. """ - ideg = int(deg) - if ideg != deg: - raise ValueError("deg must be integer") + ideg = pu._deprecate_as_int(deg, "deg") if ideg < 0: raise ValueError("deg must be non-negative") - x = np.array(x, copy=0, ndmin=1) + 0.0 + x = np.array(x, copy=False, ndmin=1) + 0.0 dims = (ideg + 1,) + x.shape dtyp = x.dtype v = np.empty(dims, dtype=dtyp) @@ -1504,12 +1474,12 @@ def chebvander2d(x, y, deg): ------- vander2d : ndarray The shape of the returned matrix is ``x.shape + (order,)``, where - :math:`order = (deg[0]+1)*(deg([1]+1)`. The dtype will be the same + :math:`order = (deg[0]+1)*(deg[1]+1)`. The dtype will be the same as the converted `x` and `y`. See Also -------- - chebvander, chebvander3d. chebval2d, chebval3d + chebvander, chebvander3d, chebval2d, chebval3d Notes ----- @@ -1517,17 +1487,7 @@ def chebvander2d(x, y, deg): .. versionadded:: 1.7.0 """ - ideg = [int(d) for d in deg] - is_valid = [id == d and id >= 0 for id, d in zip(ideg, deg)] - if is_valid != [1, 1]: - raise ValueError("degrees must be non-negative integers") - degx, degy = ideg - x, y = np.array((x, y), copy=0) + 0.0 - - vx = chebvander(x, degx) - vy = chebvander(y, degy) - v = vx[..., None]*vy[..., None,:] - return v.reshape(v.shape[:-2] + (-1,)) + return pu._vander_nd_flat((chebvander, chebvander), (x, y), deg) def chebvander3d(x, y, z, deg): @@ -1568,12 +1528,12 @@ def chebvander3d(x, y, z, deg): ------- vander3d : ndarray The shape of the returned matrix is ``x.shape + (order,)``, where - :math:`order = (deg[0]+1)*(deg([1]+1)*(deg[2]+1)`. The dtype will + :math:`order = (deg[0]+1)*(deg[1]+1)*(deg[2]+1)`. The dtype will be the same as the converted `x`, `y`, and `z`. See Also -------- - chebvander, chebvander3d. chebval2d, chebval3d + chebvander, chebvander3d, chebval2d, chebval3d Notes ----- @@ -1581,18 +1541,7 @@ def chebvander3d(x, y, z, deg): .. versionadded:: 1.7.0 """ - ideg = [int(d) for d in deg] - is_valid = [id == d and id >= 0 for id, d in zip(ideg, deg)] - if is_valid != [1, 1, 1]: - raise ValueError("degrees must be non-negative integers") - degx, degy, degz = ideg - x, y, z = np.array((x, y, z), copy=0) + 0.0 - - vx = chebvander(x, degx) - vy = chebvander(y, degy) - vz = chebvander(z, degz) - v = vx[..., None, None]*vy[..., None,:, None]*vz[..., None, None,:] - return v.reshape(v.shape[:-3] + (-1,)) + return pu._vander_nd_flat((chebvander, chebvander, chebvander), (x, y, z), deg) def chebfit(x, y, deg, rcond=None, full=False, w=None): @@ -1633,10 +1582,11 @@ def chebfit(x, y, deg, rcond=None, full=False, w=None): default) just the coefficients are returned, when True diagnostic information from the singular value decomposition is also returned. w : array_like, shape (`M`,), optional - Weights. If not None, the contribution of each point - ``(x[i],y[i])`` to the fit is weighted by `w[i]`. Ideally the - weights are chosen so that the errors of the products ``w[i]*y[i]`` - all have the same variance. The default value is None. + Weights. If not None, the weight ``w[i]`` applies to the unsquared + residual ``y[i] - y_hat[i]`` at ``x[i]``. Ideally the weights are + chosen so that the errors of the products ``w[i]*y[i]`` all have the + same variance. When using inverse-variance weighting, use + ``w[i] = 1/sigma(y[i])``. The default value is None. .. versionadded:: 1.5.0 @@ -1648,32 +1598,36 @@ def chebfit(x, y, deg, rcond=None, full=False, w=None): `k`. [residuals, rank, singular_values, rcond] : list - These values are only returned if `full` = True + These values are only returned if ``full == True`` - resid -- sum of squared residuals of the least squares fit - rank -- the numerical rank of the scaled Vandermonde matrix - sv -- singular values of the scaled Vandermonde matrix - rcond -- value of `rcond`. + - residuals -- sum of squared residuals of the least squares fit + - rank -- the numerical rank of the scaled Vandermonde matrix + - singular_values -- singular values of the scaled Vandermonde matrix + - rcond -- value of `rcond`. - For more details, see `linalg.lstsq`. + For more details, see `numpy.linalg.lstsq`. Warns ----- RankWarning The rank of the coefficient matrix in the least-squares fit is - deficient. The warning is only raised if `full` = False. The + deficient. The warning is only raised if ``full == False``. The warnings can be turned off by >>> import warnings - >>> warnings.simplefilter('ignore', RankWarning) + >>> warnings.simplefilter('ignore', np.RankWarning) See Also -------- - polyfit, legfit, lagfit, hermfit, hermefit + numpy.polynomial.polynomial.polyfit + numpy.polynomial.legendre.legfit + numpy.polynomial.laguerre.lagfit + numpy.polynomial.hermite.hermfit + numpy.polynomial.hermite_e.hermefit chebval : Evaluates a Chebyshev series. chebvander : Vandermonde matrix of Chebyshev series. chebweight : Chebyshev weight function. - linalg.lstsq : Computes a least-squares fit from the matrix. + numpy.linalg.lstsq : Computes a least-squares fit from the matrix. scipy.interpolate.UnivariateSpline : Computes spline fits. Notes @@ -1708,87 +1662,13 @@ def chebfit(x, y, deg, rcond=None, full=False, w=None): References ---------- .. [1] Wikipedia, "Curve fitting", - http://en.wikipedia.org/wiki/Curve_fitting + https://en.wikipedia.org/wiki/Curve_fitting Examples -------- """ - x = np.asarray(x) + 0.0 - y = np.asarray(y) + 0.0 - deg = np.asarray(deg) - - # check arguments. - if deg.ndim > 1 or deg.dtype.kind not in 'iu' or deg.size == 0: - raise TypeError("deg must be an int or non-empty 1-D array of int") - if deg.min() < 0: - raise ValueError("expected deg >= 0") - if x.ndim != 1: - raise TypeError("expected 1D vector for x") - if x.size == 0: - raise TypeError("expected non-empty vector for x") - if y.ndim < 1 or y.ndim > 2: - raise TypeError("expected 1D or 2D array for y") - if len(x) != len(y): - raise TypeError("expected x and y to have same length") - - if deg.ndim == 0: - lmax = deg - order = lmax + 1 - van = chebvander(x, lmax) - else: - deg = np.sort(deg) - lmax = deg[-1] - order = len(deg) - van = chebvander(x, lmax)[:, deg] - - # set up the least squares matrices in transposed form - lhs = van.T - rhs = y.T - if w is not None: - w = np.asarray(w) + 0.0 - if w.ndim != 1: - raise TypeError("expected 1D vector for w") - if len(x) != len(w): - raise TypeError("expected x and w to have same length") - # apply weights. Don't use inplace operations as they - # can cause problems with NA. - lhs = lhs * w - rhs = rhs * w - - # set rcond - if rcond is None: - rcond = len(x)*np.finfo(x.dtype).eps - - # Determine the norms of the design matrix columns. - if issubclass(lhs.dtype.type, np.complexfloating): - scl = np.sqrt((np.square(lhs.real) + np.square(lhs.imag)).sum(1)) - else: - scl = np.sqrt(np.square(lhs).sum(1)) - scl[scl == 0] = 1 - - # Solve the least squares problem. - c, resids, rank, s = la.lstsq(lhs.T/scl, rhs.T, rcond) - c = (c.T/scl).T - - # Expand c to include non-fitted coefficients which are set to zero - if deg.ndim > 0: - if c.ndim == 2: - cc = np.zeros((lmax + 1, c.shape[1]), dtype=c.dtype) - else: - cc = np.zeros(lmax + 1, dtype=c.dtype) - cc[deg] = c - c = cc - - # warn on rank reduction - if rank != order and not full: - msg = "The fit may be poorly conditioned" - warnings.warn(msg, pu.RankWarning, stacklevel=2) - - if full: - return c, [resids, rank, s, rcond] - else: - return c + return pu._fit(chebvander, x, y, deg, rcond, full, w) def chebcompanion(c): @@ -1857,7 +1737,11 @@ def chebroots(c): See Also -------- - polyroots, legroots, lagroots, hermroots, hermeroots + numpy.polynomial.polynomial.polyroots + numpy.polynomial.legendre.legroots + numpy.polynomial.laguerre.lagroots + numpy.polynomial.hermite.hermroots + numpy.polynomial.hermite_e.hermeroots Notes ----- @@ -1876,7 +1760,7 @@ def chebroots(c): -------- >>> import numpy.polynomial.chebyshev as cheb >>> cheb.chebroots((-1, 1,-1, 1)) # T3 - T2 + T1 - T0 has real roots - array([ -5.00000000e-01, 2.60860684e-17, 1.00000000e+00]) + array([ -5.00000000e-01, 2.60860684e-17, 1.00000000e+00]) # may vary """ # c is a trimmed copy @@ -1886,7 +1770,8 @@ def chebroots(c): if len(c) == 2: return np.array([-c[0]/c[1]]) - m = chebcompanion(c) + # rotated companion matrix reduces error + m = chebcompanion(c)[::-1,::-1] r = la.eigvals(m) r.sort() return r @@ -1994,9 +1879,9 @@ def chebgauss(deg): .. math:: w_i = \\pi / n """ - ideg = int(deg) - if ideg != deg or ideg < 1: - raise ValueError("deg must be a non-negative integer") + ideg = pu._deprecate_as_int(deg, "deg") + if ideg <= 0: + raise ValueError("deg must be a positive integer") x = np.cos(np.pi * np.arange(1, 2*ideg, 2) / (2.0*ideg)) w = np.ones(ideg)*(np.pi/ideg) @@ -2065,8 +1950,8 @@ def chebpts1(npts): if _npts < 1: raise ValueError("npts must be >= 1") - x = np.linspace(-np.pi, 0, _npts, endpoint=False) + np.pi/(2*_npts) - return np.cos(x) + x = 0.5 * np.pi / _npts * np.arange(-_npts+1, _npts+1, 2) + return np.sin(x) def chebpts2(npts): @@ -2185,6 +2070,6 @@ def interpolate(cls, func, deg, domain=None, args=()): return cls(coef, domain=domain) # Virtual properties - nickname = 'cheb' domain = np.array(chebdomain) window = np.array(chebdomain) + basis_name = 'T' diff --git a/numpy/polynomial/chebyshev.pyi b/numpy/polynomial/chebyshev.pyi new file mode 100644 index 000000000000..841c0859b1b0 --- /dev/null +++ b/numpy/polynomial/chebyshev.pyi @@ -0,0 +1,51 @@ +from typing import Any, List + +from numpy import ndarray, dtype, int_ +from numpy.polynomial._polybase import ABCPolyBase +from numpy.polynomial.polyutils import trimcoef + +__all__: List[str] + +chebtrim = trimcoef + +def poly2cheb(pol): ... +def cheb2poly(c): ... + +chebdomain: ndarray[Any, dtype[int_]] +chebzero: ndarray[Any, dtype[int_]] +chebone: ndarray[Any, dtype[int_]] +chebx: ndarray[Any, dtype[int_]] + +def chebline(off, scl): ... +def chebfromroots(roots): ... +def chebadd(c1, c2): ... +def chebsub(c1, c2): ... +def chebmulx(c): ... +def chebmul(c1, c2): ... +def chebdiv(c1, c2): ... +def chebpow(c, pow, maxpower=...): ... +def chebder(c, m=..., scl=..., axis=...): ... +def chebint(c, m=..., k = ..., lbnd=..., scl=..., axis=...): ... +def chebval(x, c, tensor=...): ... +def chebval2d(x, y, c): ... +def chebgrid2d(x, y, c): ... +def chebval3d(x, y, z, c): ... +def chebgrid3d(x, y, z, c): ... +def chebvander(x, deg): ... +def chebvander2d(x, y, deg): ... +def chebvander3d(x, y, z, deg): ... +def chebfit(x, y, deg, rcond=..., full=..., w=...): ... +def chebcompanion(c): ... +def chebroots(c): ... +def chebinterpolate(func, deg, args = ...): ... +def chebgauss(deg): ... +def chebweight(x): ... +def chebpts1(npts): ... +def chebpts2(npts): ... + +class Chebyshev(ABCPolyBase): + @classmethod + def interpolate(cls, func, deg, domain=..., args = ...): ... + domain: Any + window: Any + basis_name: Any diff --git a/numpy/polynomial/hermite.py b/numpy/polynomial/hermite.py index 58e9e180f153..9b0735a9aad3 100644 --- a/numpy/polynomial/hermite.py +++ b/numpy/polynomial/hermite.py @@ -1,5 +1,7 @@ """ -Objects for dealing with Hermite series. +============================================================== +Hermite Series, "Physicists" (:mod:`numpy.polynomial.hermite`) +============================================================== This module provides a number of objects (mostly functions) useful for dealing with Hermite series, including a `Hermite` class that @@ -7,59 +9,72 @@ on how this module represents and works with such polynomials is in the docstring for its "parent" sub-package, `numpy.polynomial`). +Classes +------- +.. autosummary:: + :toctree: generated/ + + Hermite + Constants --------- -- `hermdomain` -- Hermite series default domain, [-1,1]. -- `hermzero` -- Hermite series that evaluates identically to 0. -- `hermone` -- Hermite series that evaluates identically to 1. -- `hermx` -- Hermite series for the identity map, ``f(x) = x``. +.. autosummary:: + :toctree: generated/ + + hermdomain + hermzero + hermone + hermx Arithmetic ---------- -- `hermmulx` -- multiply a Hermite series in ``P_i(x)`` by ``x``. -- `hermadd` -- add two Hermite series. -- `hermsub` -- subtract one Hermite series from another. -- `hermmul` -- multiply two Hermite series. -- `hermdiv` -- divide one Hermite series by another. -- `hermval` -- evaluate a Hermite series at given points. -- `hermval2d` -- evaluate a 2D Hermite series at given points. -- `hermval3d` -- evaluate a 3D Hermite series at given points. -- `hermgrid2d` -- evaluate a 2D Hermite series on a Cartesian product. -- `hermgrid3d` -- evaluate a 3D Hermite series on a Cartesian product. +.. autosummary:: + :toctree: generated/ + + hermadd + hermsub + hermmulx + hermmul + hermdiv + hermpow + hermval + hermval2d + hermval3d + hermgrid2d + hermgrid3d Calculus -------- -- `hermder` -- differentiate a Hermite series. -- `hermint` -- integrate a Hermite series. +.. autosummary:: + :toctree: generated/ + + hermder + hermint Misc Functions -------------- -- `hermfromroots` -- create a Hermite series with specified roots. -- `hermroots` -- find the roots of a Hermite series. -- `hermvander` -- Vandermonde-like matrix for Hermite polynomials. -- `hermvander2d` -- Vandermonde-like matrix for 2D power series. -- `hermvander3d` -- Vandermonde-like matrix for 3D power series. -- `hermgauss` -- Gauss-Hermite quadrature, points and weights. -- `hermweight` -- Hermite weight function. -- `hermcompanion` -- symmetrized companion matrix in Hermite form. -- `hermfit` -- least-squares fit returning a Hermite series. -- `hermtrim` -- trim leading coefficients from a Hermite series. -- `hermline` -- Hermite series of given straight line. -- `herm2poly` -- convert a Hermite series to a polynomial. -- `poly2herm` -- convert a polynomial to a Hermite series. - -Classes -------- -- `Hermite` -- A Hermite series class. +.. autosummary:: + :toctree: generated/ + + hermfromroots + hermroots + hermvander + hermvander2d + hermvander3d + hermgauss + hermweight + hermcompanion + hermfit + hermtrim + hermline + herm2poly + poly2herm See also -------- `numpy.polynomial` """ -from __future__ import division, absolute_import, print_function - -import warnings import numpy as np import numpy.linalg as la from numpy.core.multiarray import normalize_axis_index @@ -113,7 +128,7 @@ def poly2herm(pol): -------- >>> from numpy.polynomial.hermite import poly2herm >>> poly2herm(np.arange(4)) - array([ 1. , 2.75 , 0.5 , 0.375]) + array([1. , 2.75 , 0.5 , 0.375]) """ [pol] = pu.as_series([pol]) @@ -159,7 +174,7 @@ def herm2poly(c): -------- >>> from numpy.polynomial.hermite import herm2poly >>> herm2poly([ 1. , 2.75 , 0.5 , 0.375]) - array([ 0., 1., 2., 3.]) + array([0., 1., 2., 3.]) """ from .polynomial import polyadd, polysub, polymulx @@ -218,7 +233,11 @@ def hermline(off, scl): See Also -------- - polyline, chebline + numpy.polynomial.polynomial.polyline + numpy.polynomial.chebyshev.chebline + numpy.polynomial.legendre.legline + numpy.polynomial.laguerre.lagline + numpy.polynomial.hermite_e.hermeline Examples -------- @@ -271,35 +290,24 @@ def hermfromroots(roots): See Also -------- - polyfromroots, legfromroots, lagfromroots, chebfromroots, - hermefromroots. + numpy.polynomial.polynomial.polyfromroots + numpy.polynomial.legendre.legfromroots + numpy.polynomial.laguerre.lagfromroots + numpy.polynomial.chebyshev.chebfromroots + numpy.polynomial.hermite_e.hermefromroots Examples -------- >>> from numpy.polynomial.hermite import hermfromroots, hermval >>> coef = hermfromroots((-1, 0, 1)) >>> hermval((-1, 0, 1), coef) - array([ 0., 0., 0.]) + array([0., 0., 0.]) >>> coef = hermfromroots((-1j, 1j)) >>> hermval((-1j, 1j), coef) - array([ 0.+0.j, 0.+0.j]) + array([0.+0.j, 0.+0.j]) """ - if len(roots) == 0: - return np.ones(1) - else: - [roots] = pu.as_series([roots], trim=False) - roots.sort() - p = [hermline(-r, 1) for r in roots] - n = len(p) - while n > 1: - m, r = divmod(n, 2) - tmp = [hermmul(p[i], p[i+m]) for i in range(m)] - if r: - tmp[0] = hermmul(tmp[0], p[-1]) - p = tmp - n = m - return p[0] + return pu._fromroots(hermline, hermmul, roots) def hermadd(c1, c2): @@ -323,7 +331,7 @@ def hermadd(c1, c2): See Also -------- - hermsub, hermmul, hermdiv, hermpow + hermsub, hermmulx, hermmul, hermdiv, hermpow Notes ----- @@ -336,18 +344,10 @@ def hermadd(c1, c2): -------- >>> from numpy.polynomial.hermite import hermadd >>> hermadd([1, 2, 3], [1, 2, 3, 4]) - array([ 2., 4., 6., 4.]) + array([2., 4., 6., 4.]) """ - # c1, c2 are trimmed copies - [c1, c2] = pu.as_series([c1, c2]) - if len(c1) > len(c2): - c1[:c2.size] += c2 - ret = c1 - else: - c2[:c1.size] += c1 - ret = c2 - return pu.trimseq(ret) + return pu._add(c1, c2) def hermsub(c1, c2): @@ -371,7 +371,7 @@ def hermsub(c1, c2): See Also -------- - hermadd, hermmul, hermdiv, hermpow + hermadd, hermmulx, hermmul, hermdiv, hermpow Notes ----- @@ -384,19 +384,10 @@ def hermsub(c1, c2): -------- >>> from numpy.polynomial.hermite import hermsub >>> hermsub([1, 2, 3, 4], [1, 2, 3]) - array([ 0., 0., 0., 4.]) + array([0., 0., 0., 4.]) """ - # c1, c2 are trimmed copies - [c1, c2] = pu.as_series([c1, c2]) - if len(c1) > len(c2): - c1[:c2.size] -= c2 - ret = c1 - else: - c2 = -c2 - c2[:c1.size] += c1 - ret = c2 - return pu.trimseq(ret) + return pu._sub(c1, c2) def hermmulx(c): @@ -417,6 +408,10 @@ def hermmulx(c): out : ndarray Array representing the result of the multiplication. + See Also + -------- + hermadd, hermsub, hermmul, hermdiv, hermpow + Notes ----- The multiplication uses the recursion relationship for Hermite @@ -424,13 +419,13 @@ def hermmulx(c): .. math:: - xP_i(x) = (P_{i + 1}(x)/2 + i*P_{i - 1}(x)) + xP_i(x) = (P_{i + 1}(x)/2 + i*P_{i - 1}(x)) Examples -------- >>> from numpy.polynomial.hermite import hermmulx >>> hermmulx([1, 2, 3]) - array([ 2. , 6.5, 1. , 1.5]) + array([2. , 6.5, 1. , 1.5]) """ # c is a trimmed copy @@ -469,7 +464,7 @@ def hermmul(c1, c2): See Also -------- - hermadd, hermsub, hermdiv, hermpow + hermadd, hermsub, hermmulx, hermdiv, hermpow Notes ----- @@ -483,7 +478,7 @@ def hermmul(c1, c2): -------- >>> from numpy.polynomial.hermite import hermmul >>> hermmul([1, 2, 3], [0, 1, 2]) - array([ 52., 29., 52., 7., 6.]) + array([52., 29., 52., 7., 6.]) """ # s1, s2 are trimmed copies @@ -537,7 +532,7 @@ def hermdiv(c1, c2): See Also -------- - hermadd, hermsub, hermmul, hermpow + hermadd, hermsub, hermmulx, hermmul, hermpow Notes ----- @@ -552,33 +547,14 @@ def hermdiv(c1, c2): -------- >>> from numpy.polynomial.hermite import hermdiv >>> hermdiv([ 52., 29., 52., 7., 6.], [0, 1, 2]) - (array([ 1., 2., 3.]), array([ 0.])) + (array([1., 2., 3.]), array([0.])) >>> hermdiv([ 54., 31., 52., 7., 6.], [0, 1, 2]) - (array([ 1., 2., 3.]), array([ 2., 2.])) + (array([1., 2., 3.]), array([2., 2.])) >>> hermdiv([ 53., 30., 52., 7., 6.], [0, 1, 2]) - (array([ 1., 2., 3.]), array([ 1., 1.])) + (array([1., 2., 3.]), array([1., 1.])) """ - # c1, c2 are trimmed copies - [c1, c2] = pu.as_series([c1, c2]) - if c2[-1] == 0: - raise ZeroDivisionError() - - lc1 = len(c1) - lc2 = len(c2) - if lc1 < lc2: - return c1[:1]*0, c1 - elif lc2 == 1: - return c1/c2[-1], c1[:1]*0 - else: - quo = np.empty(lc1 - lc2 + 1, dtype=c1.dtype) - rem = c1 - for i in range(lc1 - lc2, - 1, -1): - p = hermmul([0]*i + [1], c2) - q = rem[-1]/p[-1] - rem = rem[:-1] - q*p[:-1] - quo[i] = q - return quo, pu.trimseq(rem) + return pu._div(hermmul, c1, c2) def hermpow(c, pow, maxpower=16): @@ -606,33 +582,16 @@ def hermpow(c, pow, maxpower=16): See Also -------- - hermadd, hermsub, hermmul, hermdiv + hermadd, hermsub, hermmulx, hermmul, hermdiv Examples -------- >>> from numpy.polynomial.hermite import hermpow >>> hermpow([1, 2, 3], 2) - array([ 81., 52., 82., 12., 9.]) + array([81., 52., 82., 12., 9.]) """ - # c is a trimmed copy - [c] = pu.as_series([c]) - power = int(pow) - if power != pow or power < 0: - raise ValueError("Power must be a non-negative integer.") - elif maxpower is not None and power > maxpower: - raise ValueError("Power is too large") - elif power == 0: - return np.array([1], dtype=c.dtype) - elif power == 1: - return c - else: - # This can be made more efficient by using powers of two - # in the usual way. - prd = c - for i in range(2, power + 1): - prd = hermmul(prd, c) - return prd + return pu._pow(hermmul, c, pow, maxpower) def hermder(c, m=1, scl=1, axis=0): @@ -685,22 +644,18 @@ def hermder(c, m=1, scl=1, axis=0): -------- >>> from numpy.polynomial.hermite import hermder >>> hermder([ 1. , 0.5, 0.5, 0.5]) - array([ 1., 2., 3.]) + array([1., 2., 3.]) >>> hermder([-0.5, 1./2., 1./8., 1./12., 1./16.], m=2) - array([ 1., 2., 3.]) + array([1., 2., 3.]) """ - c = np.array(c, ndmin=1, copy=1) + c = np.array(c, ndmin=1, copy=True) if c.dtype.char in '?bBhHiIlLqQpP': c = c.astype(np.double) - cnt, iaxis = [int(t) for t in [m, axis]] - - if cnt != m: - raise ValueError("The order of derivation must be integer") + cnt = pu._deprecate_as_int(m, "the order of derivation") + iaxis = pu._deprecate_as_int(axis, "the axis") if cnt < 0: raise ValueError("The order of derivation must be non-negative") - if iaxis != axis: - raise ValueError("The axis must be integer") iaxis = normalize_axis_index(iaxis, c.ndim) if cnt == 0: @@ -794,26 +749,24 @@ def hermint(c, m=1, k=[], lbnd=0, scl=1, axis=0): -------- >>> from numpy.polynomial.hermite import hermint >>> hermint([1,2,3]) # integrate once, value 0 at 0. - array([ 1. , 0.5, 0.5, 0.5]) + array([1. , 0.5, 0.5, 0.5]) >>> hermint([1,2,3], m=2) # integrate twice, value & deriv 0 at 0 - array([-0.5 , 0.5 , 0.125 , 0.08333333, 0.0625 ]) + array([-0.5 , 0.5 , 0.125 , 0.08333333, 0.0625 ]) # may vary >>> hermint([1,2,3], k=1) # integrate once, value 1 at 0. - array([ 2. , 0.5, 0.5, 0.5]) + array([2. , 0.5, 0.5, 0.5]) >>> hermint([1,2,3], lbnd=-1) # integrate once, value 0 at -1 array([-2. , 0.5, 0.5, 0.5]) >>> hermint([1,2,3], m=2, k=[1,2], lbnd=-1) - array([ 1.66666667, -0.5 , 0.125 , 0.08333333, 0.0625 ]) + array([ 1.66666667, -0.5 , 0.125 , 0.08333333, 0.0625 ]) # may vary """ - c = np.array(c, ndmin=1, copy=1) + c = np.array(c, ndmin=1, copy=True) if c.dtype.char in '?bBhHiIlLqQpP': c = c.astype(np.double) if not np.iterable(k): k = [k] - cnt, iaxis = [int(t) for t in [m, axis]] - - if cnt != m: - raise ValueError("The order of integration must be integer") + cnt = pu._deprecate_as_int(m, "the order of integration") + iaxis = pu._deprecate_as_int(axis, "the axis") if cnt < 0: raise ValueError("The order of integration must be non-negative") if len(k) > cnt: @@ -822,8 +775,6 @@ def hermint(c, m=1, k=[], lbnd=0, scl=1, axis=0): raise ValueError("lbnd must be a scalar.") if np.ndim(scl) != 0: raise ValueError("scl must be a scalar.") - if iaxis != axis: - raise ValueError("The axis must be integer") iaxis = normalize_axis_index(iaxis, c.ndim) if cnt == 0: @@ -913,11 +864,11 @@ def hermval(x, c, tensor=True): >>> hermval(1, coef) 11.0 >>> hermval([[1,2],[3,4]], coef) - array([[ 11., 51.], - [ 115., 203.]]) + array([[ 11., 51.], + [115., 203.]]) """ - c = np.array(c, ndmin=1, copy=0) + c = np.array(c, ndmin=1, copy=False) if c.dtype.char in '?bBhHiIlLqQpP': c = c.astype(np.double) if isinstance(x, (tuple, list)): @@ -990,14 +941,7 @@ def hermval2d(x, y, c): .. versionadded:: 1.7.0 """ - try: - x, y = np.array((x, y), copy=0) - except Exception: - raise ValueError('x, y are incompatible') - - c = hermval(x, c) - c = hermval(y, c, tensor=False) - return c + return pu._valnd(hermval, c, x, y) def hermgrid2d(x, y, c): @@ -1050,9 +994,7 @@ def hermgrid2d(x, y, c): .. versionadded:: 1.7.0 """ - c = hermval(x, c) - c = hermval(y, c) - return c + return pu._gridnd(hermval, c, x, y) def hermval3d(x, y, z, c): @@ -1103,15 +1045,7 @@ def hermval3d(x, y, z, c): .. versionadded:: 1.7.0 """ - try: - x, y, z = np.array((x, y, z), copy=0) - except Exception: - raise ValueError('x, y, z are incompatible') - - c = hermval(x, c) - c = hermval(y, c, tensor=False) - c = hermval(z, c, tensor=False) - return c + return pu._valnd(hermval, c, x, y, z) def hermgrid3d(x, y, z, c): @@ -1167,10 +1101,7 @@ def hermgrid3d(x, y, z, c): .. versionadded:: 1.7.0 """ - c = hermval(x, c) - c = hermval(y, c) - c = hermval(z, c) - return c + return pu._gridnd(hermval, c, x, y, z) def hermvander(x, deg): @@ -1217,13 +1148,11 @@ def hermvander(x, deg): [ 1., 2., 2., -4.]]) """ - ideg = int(deg) - if ideg != deg: - raise ValueError("deg must be integer") + ideg = pu._deprecate_as_int(deg, "deg") if ideg < 0: raise ValueError("deg must be non-negative") - x = np.array(x, copy=0, ndmin=1) + 0.0 + x = np.array(x, copy=False, ndmin=1) + 0.0 dims = (ideg + 1,) + x.shape dtyp = x.dtype v = np.empty(dims, dtype=dtyp) @@ -1273,12 +1202,12 @@ def hermvander2d(x, y, deg): ------- vander2d : ndarray The shape of the returned matrix is ``x.shape + (order,)``, where - :math:`order = (deg[0]+1)*(deg([1]+1)`. The dtype will be the same + :math:`order = (deg[0]+1)*(deg[1]+1)`. The dtype will be the same as the converted `x` and `y`. See Also -------- - hermvander, hermvander3d. hermval2d, hermval3d + hermvander, hermvander3d, hermval2d, hermval3d Notes ----- @@ -1286,17 +1215,7 @@ def hermvander2d(x, y, deg): .. versionadded:: 1.7.0 """ - ideg = [int(d) for d in deg] - is_valid = [id == d and id >= 0 for id, d in zip(ideg, deg)] - if is_valid != [1, 1]: - raise ValueError("degrees must be non-negative integers") - degx, degy = ideg - x, y = np.array((x, y), copy=0) + 0.0 - - vx = hermvander(x, degx) - vy = hermvander(y, degy) - v = vx[..., None]*vy[..., None,:] - return v.reshape(v.shape[:-2] + (-1,)) + return pu._vander_nd_flat((hermvander, hermvander), (x, y), deg) def hermvander3d(x, y, z, deg): @@ -1337,12 +1256,12 @@ def hermvander3d(x, y, z, deg): ------- vander3d : ndarray The shape of the returned matrix is ``x.shape + (order,)``, where - :math:`order = (deg[0]+1)*(deg([1]+1)*(deg[2]+1)`. The dtype will + :math:`order = (deg[0]+1)*(deg[1]+1)*(deg[2]+1)`. The dtype will be the same as the converted `x`, `y`, and `z`. See Also -------- - hermvander, hermvander3d. hermval2d, hermval3d + hermvander, hermvander3d, hermval2d, hermval3d Notes ----- @@ -1350,18 +1269,7 @@ def hermvander3d(x, y, z, deg): .. versionadded:: 1.7.0 """ - ideg = [int(d) for d in deg] - is_valid = [id == d and id >= 0 for id, d in zip(ideg, deg)] - if is_valid != [1, 1, 1]: - raise ValueError("degrees must be non-negative integers") - degx, degy, degz = ideg - x, y, z = np.array((x, y, z), copy=0) + 0.0 - - vx = hermvander(x, degx) - vy = hermvander(y, degy) - vz = hermvander(z, degz) - v = vx[..., None, None]*vy[..., None,:, None]*vz[..., None, None,:] - return v.reshape(v.shape[:-3] + (-1,)) + return pu._vander_nd_flat((hermvander, hermvander, hermvander), (x, y, z), deg) def hermfit(x, y, deg, rcond=None, full=False, w=None): @@ -1402,10 +1310,11 @@ def hermfit(x, y, deg, rcond=None, full=False, w=None): default) just the coefficients are returned, when True diagnostic information from the singular value decomposition is also returned. w : array_like, shape (`M`,), optional - Weights. If not None, the contribution of each point - ``(x[i],y[i])`` to the fit is weighted by `w[i]`. Ideally the - weights are chosen so that the errors of the products ``w[i]*y[i]`` - all have the same variance. The default value is None. + Weights. If not None, the weight ``w[i]`` applies to the unsquared + residual ``y[i] - y_hat[i]`` at ``x[i]``. Ideally the weights are + chosen so that the errors of the products ``w[i]*y[i]`` all have the + same variance. When using inverse-variance weighting, use + ``w[i] = 1/sigma(y[i])``. The default value is None. Returns ------- @@ -1415,32 +1324,36 @@ def hermfit(x, y, deg, rcond=None, full=False, w=None): `k`. [residuals, rank, singular_values, rcond] : list - These values are only returned if `full` = True + These values are only returned if ``full == True`` - resid -- sum of squared residuals of the least squares fit - rank -- the numerical rank of the scaled Vandermonde matrix - sv -- singular values of the scaled Vandermonde matrix - rcond -- value of `rcond`. + - residuals -- sum of squared residuals of the least squares fit + - rank -- the numerical rank of the scaled Vandermonde matrix + - singular_values -- singular values of the scaled Vandermonde matrix + - rcond -- value of `rcond`. - For more details, see `linalg.lstsq`. + For more details, see `numpy.linalg.lstsq`. Warns ----- RankWarning The rank of the coefficient matrix in the least-squares fit is - deficient. The warning is only raised if `full` = False. The + deficient. The warning is only raised if ``full == False``. The warnings can be turned off by >>> import warnings - >>> warnings.simplefilter('ignore', RankWarning) + >>> warnings.simplefilter('ignore', np.RankWarning) See Also -------- - chebfit, legfit, lagfit, polyfit, hermefit + numpy.polynomial.chebyshev.chebfit + numpy.polynomial.legendre.legfit + numpy.polynomial.laguerre.lagfit + numpy.polynomial.polynomial.polyfit + numpy.polynomial.hermite_e.hermefit hermval : Evaluates a Hermite series. hermvander : Vandermonde matrix of Hermite series. hermweight : Hermite weight function - linalg.lstsq : Computes a least-squares fit from the matrix. + numpy.linalg.lstsq : Computes a least-squares fit from the matrix. scipy.interpolate.UnivariateSpline : Computes spline fits. Notes @@ -1469,14 +1382,14 @@ def hermfit(x, y, deg, rcond=None, full=False, w=None): Fits using Hermite series are probably most useful when the data can be approximated by ``sqrt(w(x)) * p(x)``, where `w(x)` is the Hermite - weight. In that case the weight ``sqrt(w(x[i])`` should be used - together with data values ``y[i]/sqrt(w(x[i])``. The weight function is + weight. In that case the weight ``sqrt(w(x[i]))`` should be used + together with data values ``y[i]/sqrt(w(x[i]))``. The weight function is available as `hermweight`. References ---------- .. [1] Wikipedia, "Curve fitting", - http://en.wikipedia.org/wiki/Curve_fitting + https://en.wikipedia.org/wiki/Curve_fitting Examples -------- @@ -1485,84 +1398,10 @@ def hermfit(x, y, deg, rcond=None, full=False, w=None): >>> err = np.random.randn(len(x))/10 >>> y = hermval(x, [1, 2, 3]) + err >>> hermfit(x, y, 2) - array([ 0.97902637, 1.99849131, 3.00006 ]) + array([1.0218, 1.9986, 2.9999]) # may vary """ - x = np.asarray(x) + 0.0 - y = np.asarray(y) + 0.0 - deg = np.asarray(deg) - - # check arguments. - if deg.ndim > 1 or deg.dtype.kind not in 'iu' or deg.size == 0: - raise TypeError("deg must be an int or non-empty 1-D array of int") - if deg.min() < 0: - raise ValueError("expected deg >= 0") - if x.ndim != 1: - raise TypeError("expected 1D vector for x") - if x.size == 0: - raise TypeError("expected non-empty vector for x") - if y.ndim < 1 or y.ndim > 2: - raise TypeError("expected 1D or 2D array for y") - if len(x) != len(y): - raise TypeError("expected x and y to have same length") - - if deg.ndim == 0: - lmax = deg - order = lmax + 1 - van = hermvander(x, lmax) - else: - deg = np.sort(deg) - lmax = deg[-1] - order = len(deg) - van = hermvander(x, lmax)[:, deg] - - # set up the least squares matrices in transposed form - lhs = van.T - rhs = y.T - if w is not None: - w = np.asarray(w) + 0.0 - if w.ndim != 1: - raise TypeError("expected 1D vector for w") - if len(x) != len(w): - raise TypeError("expected x and w to have same length") - # apply weights. Don't use inplace operations as they - # can cause problems with NA. - lhs = lhs * w - rhs = rhs * w - - # set rcond - if rcond is None: - rcond = len(x)*np.finfo(x.dtype).eps - - # Determine the norms of the design matrix columns. - if issubclass(lhs.dtype.type, np.complexfloating): - scl = np.sqrt((np.square(lhs.real) + np.square(lhs.imag)).sum(1)) - else: - scl = np.sqrt(np.square(lhs).sum(1)) - scl[scl == 0] = 1 - - # Solve the least squares problem. - c, resids, rank, s = la.lstsq(lhs.T/scl, rhs.T, rcond) - c = (c.T/scl).T - - # Expand c to include non-fitted coefficients which are set to zero - if deg.ndim > 0: - if c.ndim == 2: - cc = np.zeros((lmax+1, c.shape[1]), dtype=c.dtype) - else: - cc = np.zeros(lmax+1, dtype=c.dtype) - cc[deg] = c - c = cc - - # warn on rank reduction - if rank != order and not full: - msg = "The fit may be poorly conditioned" - warnings.warn(msg, pu.RankWarning, stacklevel=2) - - if full: - return c, [resids, rank, s, rcond] - else: - return c + return pu._fit(hermvander, x, y, deg, rcond, full, w) def hermcompanion(c): @@ -1631,7 +1470,11 @@ def hermroots(c): See Also -------- - polyroots, legroots, lagroots, chebroots, hermeroots + numpy.polynomial.polynomial.polyroots + numpy.polynomial.legendre.legroots + numpy.polynomial.laguerre.lagroots + numpy.polynomial.chebyshev.chebroots + numpy.polynomial.hermite_e.hermeroots Notes ----- @@ -1651,9 +1494,9 @@ def hermroots(c): >>> from numpy.polynomial.hermite import hermroots, hermfromroots >>> coef = hermfromroots([-1, 0, 1]) >>> coef - array([ 0. , 0.25 , 0. , 0.125]) + array([0. , 0.25 , 0. , 0.125]) >>> hermroots(coef) - array([ -1.00000000e+00, -1.38777878e-17, 1.00000000e+00]) + array([-1.00000000e+00, -1.38777878e-17, 1.00000000e+00]) """ # c is a trimmed copy @@ -1663,7 +1506,8 @@ def hermroots(c): if len(c) == 2: return np.array([-.5*c[0]/c[1]]) - m = hermcompanion(c) + # rotated companion matrix reduces error + m = hermcompanion(c)[::-1,::-1] r = la.eigvals(m) r.sort() return r @@ -1699,7 +1543,7 @@ def _normed_hermite_n(x, n): """ if n == 0: - return np.ones(x.shape)/np.sqrt(np.sqrt(np.pi)) + return np.full(x.shape, 1/np.sqrt(np.sqrt(np.pi))) c0 = 0. c1 = 1./np.sqrt(np.sqrt(np.pi)) @@ -1748,9 +1592,9 @@ def hermgauss(deg): the right value when integrating 1. """ - ideg = int(deg) - if ideg != deg or ideg < 1: - raise ValueError("deg must be a non-negative integer") + ideg = pu._deprecate_as_int(deg, "deg") + if ideg <= 0: + raise ValueError("deg must be a positive integer") # first approximation of roots. We use the fact that the companion # matrix is symmetric in this case in order to obtain better zeros. @@ -1848,6 +1692,6 @@ class Hermite(ABCPolyBase): _fromroots = staticmethod(hermfromroots) # Virtual properties - nickname = 'herm' domain = np.array(hermdomain) window = np.array(hermdomain) + basis_name = 'H' diff --git a/numpy/polynomial/hermite.pyi b/numpy/polynomial/hermite.pyi new file mode 100644 index 000000000000..8364a5b0fcbc --- /dev/null +++ b/numpy/polynomial/hermite.pyi @@ -0,0 +1,46 @@ +from typing import Any, List + +from numpy import ndarray, dtype, int_, float_ +from numpy.polynomial._polybase import ABCPolyBase +from numpy.polynomial.polyutils import trimcoef + +__all__: list[str] + +hermtrim = trimcoef + +def poly2herm(pol): ... +def herm2poly(c): ... + +hermdomain: ndarray[Any, dtype[int_]] +hermzero: ndarray[Any, dtype[int_]] +hermone: ndarray[Any, dtype[int_]] +hermx: ndarray[Any, dtype[float_]] + +def hermline(off, scl): ... +def hermfromroots(roots): ... +def hermadd(c1, c2): ... +def hermsub(c1, c2): ... +def hermmulx(c): ... +def hermmul(c1, c2): ... +def hermdiv(c1, c2): ... +def hermpow(c, pow, maxpower=...): ... +def hermder(c, m=..., scl=..., axis=...): ... +def hermint(c, m=..., k = ..., lbnd=..., scl=..., axis=...): ... +def hermval(x, c, tensor=...): ... +def hermval2d(x, y, c): ... +def hermgrid2d(x, y, c): ... +def hermval3d(x, y, z, c): ... +def hermgrid3d(x, y, z, c): ... +def hermvander(x, deg): ... +def hermvander2d(x, y, deg): ... +def hermvander3d(x, y, z, deg): ... +def hermfit(x, y, deg, rcond=..., full=..., w=...): ... +def hermcompanion(c): ... +def hermroots(c): ... +def hermgauss(deg): ... +def hermweight(x): ... + +class Hermite(ABCPolyBase): + domain: Any + window: Any + basis_name: Any diff --git a/numpy/polynomial/hermite_e.py b/numpy/polynomial/hermite_e.py index 47b2a9fb4e1d..182c562c2962 100644 --- a/numpy/polynomial/hermite_e.py +++ b/numpy/polynomial/hermite_e.py @@ -1,5 +1,7 @@ """ -Objects for dealing with Hermite_e series. +=================================================================== +HermiteE Series, "Probabilists" (:mod:`numpy.polynomial.hermite_e`) +=================================================================== This module provides a number of objects (mostly functions) useful for dealing with Hermite_e series, including a `HermiteE` class that @@ -7,59 +9,72 @@ on how this module represents and works with such polynomials is in the docstring for its "parent" sub-package, `numpy.polynomial`). +Classes +------- +.. autosummary:: + :toctree: generated/ + + HermiteE + Constants --------- -- `hermedomain` -- Hermite_e series default domain, [-1,1]. -- `hermezero` -- Hermite_e series that evaluates identically to 0. -- `hermeone` -- Hermite_e series that evaluates identically to 1. -- `hermex` -- Hermite_e series for the identity map, ``f(x) = x``. +.. autosummary:: + :toctree: generated/ + + hermedomain + hermezero + hermeone + hermex Arithmetic ---------- -- `hermemulx` -- multiply a Hermite_e series in ``P_i(x)`` by ``x``. -- `hermeadd` -- add two Hermite_e series. -- `hermesub` -- subtract one Hermite_e series from another. -- `hermemul` -- multiply two Hermite_e series. -- `hermediv` -- divide one Hermite_e series by another. -- `hermeval` -- evaluate a Hermite_e series at given points. -- `hermeval2d` -- evaluate a 2D Hermite_e series at given points. -- `hermeval3d` -- evaluate a 3D Hermite_e series at given points. -- `hermegrid2d` -- evaluate a 2D Hermite_e series on a Cartesian product. -- `hermegrid3d` -- evaluate a 3D Hermite_e series on a Cartesian product. +.. autosummary:: + :toctree: generated/ + + hermeadd + hermesub + hermemulx + hermemul + hermediv + hermepow + hermeval + hermeval2d + hermeval3d + hermegrid2d + hermegrid3d Calculus -------- -- `hermeder` -- differentiate a Hermite_e series. -- `hermeint` -- integrate a Hermite_e series. +.. autosummary:: + :toctree: generated/ + + hermeder + hermeint Misc Functions -------------- -- `hermefromroots` -- create a Hermite_e series with specified roots. -- `hermeroots` -- find the roots of a Hermite_e series. -- `hermevander` -- Vandermonde-like matrix for Hermite_e polynomials. -- `hermevander2d` -- Vandermonde-like matrix for 2D power series. -- `hermevander3d` -- Vandermonde-like matrix for 3D power series. -- `hermegauss` -- Gauss-Hermite_e quadrature, points and weights. -- `hermeweight` -- Hermite_e weight function. -- `hermecompanion` -- symmetrized companion matrix in Hermite_e form. -- `hermefit` -- least-squares fit returning a Hermite_e series. -- `hermetrim` -- trim leading coefficients from a Hermite_e series. -- `hermeline` -- Hermite_e series of given straight line. -- `herme2poly` -- convert a Hermite_e series to a polynomial. -- `poly2herme` -- convert a polynomial to a Hermite_e series. - -Classes -------- -- `HermiteE` -- A Hermite_e series class. +.. autosummary:: + :toctree: generated/ + + hermefromroots + hermeroots + hermevander + hermevander2d + hermevander3d + hermegauss + hermeweight + hermecompanion + hermefit + hermetrim + hermeline + herme2poly + poly2herme See also -------- `numpy.polynomial` """ -from __future__ import division, absolute_import, print_function - -import warnings import numpy as np import numpy.linalg as la from numpy.core.multiarray import normalize_axis_index @@ -160,7 +175,7 @@ def herme2poly(c): -------- >>> from numpy.polynomial.hermite_e import herme2poly >>> herme2poly([ 2., 10., 2., 3.]) - array([ 0., 1., 2., 3.]) + array([0., 1., 2., 3.]) """ from .polynomial import polyadd, polysub, polymulx @@ -203,8 +218,6 @@ def hermeline(off, scl): """ Hermite series whose graph is a straight line. - - Parameters ---------- off, scl : scalars @@ -218,7 +231,11 @@ def hermeline(off, scl): See Also -------- - polyline, chebline + numpy.polynomial.polynomial.polyline + numpy.polynomial.chebyshev.chebline + numpy.polynomial.legendre.legline + numpy.polynomial.laguerre.lagline + numpy.polynomial.hermite.hermline Examples -------- @@ -272,35 +289,24 @@ def hermefromroots(roots): See Also -------- - polyfromroots, legfromroots, lagfromroots, hermfromroots, - chebfromroots. + numpy.polynomial.polynomial.polyfromroots + numpy.polynomial.legendre.legfromroots + numpy.polynomial.laguerre.lagfromroots + numpy.polynomial.hermite.hermfromroots + numpy.polynomial.chebyshev.chebfromroots Examples -------- >>> from numpy.polynomial.hermite_e import hermefromroots, hermeval >>> coef = hermefromroots((-1, 0, 1)) >>> hermeval((-1, 0, 1), coef) - array([ 0., 0., 0.]) + array([0., 0., 0.]) >>> coef = hermefromroots((-1j, 1j)) >>> hermeval((-1j, 1j), coef) - array([ 0.+0.j, 0.+0.j]) + array([0.+0.j, 0.+0.j]) """ - if len(roots) == 0: - return np.ones(1) - else: - [roots] = pu.as_series([roots], trim=False) - roots.sort() - p = [hermeline(-r, 1) for r in roots] - n = len(p) - while n > 1: - m, r = divmod(n, 2) - tmp = [hermemul(p[i], p[i+m]) for i in range(m)] - if r: - tmp[0] = hermemul(tmp[0], p[-1]) - p = tmp - n = m - return p[0] + return pu._fromroots(hermeline, hermemul, roots) def hermeadd(c1, c2): @@ -324,7 +330,7 @@ def hermeadd(c1, c2): See Also -------- - hermesub, hermemul, hermediv, hermepow + hermesub, hermemulx, hermemul, hermediv, hermepow Notes ----- @@ -337,18 +343,10 @@ def hermeadd(c1, c2): -------- >>> from numpy.polynomial.hermite_e import hermeadd >>> hermeadd([1, 2, 3], [1, 2, 3, 4]) - array([ 2., 4., 6., 4.]) + array([2., 4., 6., 4.]) """ - # c1, c2 are trimmed copies - [c1, c2] = pu.as_series([c1, c2]) - if len(c1) > len(c2): - c1[:c2.size] += c2 - ret = c1 - else: - c2[:c1.size] += c1 - ret = c2 - return pu.trimseq(ret) + return pu._add(c1, c2) def hermesub(c1, c2): @@ -372,7 +370,7 @@ def hermesub(c1, c2): See Also -------- - hermeadd, hermemul, hermediv, hermepow + hermeadd, hermemulx, hermemul, hermediv, hermepow Notes ----- @@ -385,19 +383,10 @@ def hermesub(c1, c2): -------- >>> from numpy.polynomial.hermite_e import hermesub >>> hermesub([1, 2, 3, 4], [1, 2, 3]) - array([ 0., 0., 0., 4.]) + array([0., 0., 0., 4.]) """ - # c1, c2 are trimmed copies - [c1, c2] = pu.as_series([c1, c2]) - if len(c1) > len(c2): - c1[:c2.size] -= c2 - ret = c1 - else: - c2 = -c2 - c2[:c1.size] += c1 - ret = c2 - return pu.trimseq(ret) + return pu._sub(c1, c2) def hermemulx(c): @@ -425,13 +414,13 @@ def hermemulx(c): .. math:: - xP_i(x) = (P_{i + 1}(x) + iP_{i - 1}(x))) + xP_i(x) = (P_{i + 1}(x) + iP_{i - 1}(x))) Examples -------- >>> from numpy.polynomial.hermite_e import hermemulx >>> hermemulx([1, 2, 3]) - array([ 2., 7., 2., 3.]) + array([2., 7., 2., 3.]) """ # c is a trimmed copy @@ -470,7 +459,7 @@ def hermemul(c1, c2): See Also -------- - hermeadd, hermesub, hermediv, hermepow + hermeadd, hermesub, hermemulx, hermediv, hermepow Notes ----- @@ -484,7 +473,7 @@ def hermemul(c1, c2): -------- >>> from numpy.polynomial.hermite_e import hermemul >>> hermemul([1, 2, 3], [0, 1, 2]) - array([ 14., 15., 28., 7., 6.]) + array([14., 15., 28., 7., 6.]) """ # s1, s2 are trimmed copies @@ -538,7 +527,7 @@ def hermediv(c1, c2): See Also -------- - hermeadd, hermesub, hermemul, hermepow + hermeadd, hermesub, hermemulx, hermemul, hermepow Notes ----- @@ -553,31 +542,12 @@ def hermediv(c1, c2): -------- >>> from numpy.polynomial.hermite_e import hermediv >>> hermediv([ 14., 15., 28., 7., 6.], [0, 1, 2]) - (array([ 1., 2., 3.]), array([ 0.])) + (array([1., 2., 3.]), array([0.])) >>> hermediv([ 15., 17., 28., 7., 6.], [0, 1, 2]) - (array([ 1., 2., 3.]), array([ 1., 2.])) + (array([1., 2., 3.]), array([1., 2.])) """ - # c1, c2 are trimmed copies - [c1, c2] = pu.as_series([c1, c2]) - if c2[-1] == 0: - raise ZeroDivisionError() - - lc1 = len(c1) - lc2 = len(c2) - if lc1 < lc2: - return c1[:1]*0, c1 - elif lc2 == 1: - return c1/c2[-1], c1[:1]*0 - else: - quo = np.empty(lc1 - lc2 + 1, dtype=c1.dtype) - rem = c1 - for i in range(lc1 - lc2, - 1, -1): - p = hermemul([0]*i + [1], c2) - q = rem[-1]/p[-1] - rem = rem[:-1] - q*p[:-1] - quo[i] = q - return quo, pu.trimseq(rem) + return pu._div(hermemul, c1, c2) def hermepow(c, pow, maxpower=16): @@ -605,33 +575,16 @@ def hermepow(c, pow, maxpower=16): See Also -------- - hermeadd, hermesub, hermemul, hermediv + hermeadd, hermesub, hermemulx, hermemul, hermediv Examples -------- >>> from numpy.polynomial.hermite_e import hermepow >>> hermepow([1, 2, 3], 2) - array([ 23., 28., 46., 12., 9.]) + array([23., 28., 46., 12., 9.]) """ - # c is a trimmed copy - [c] = pu.as_series([c]) - power = int(pow) - if power != pow or power < 0: - raise ValueError("Power must be a non-negative integer.") - elif maxpower is not None and power > maxpower: - raise ValueError("Power is too large") - elif power == 0: - return np.array([1], dtype=c.dtype) - elif power == 1: - return c - else: - # This can be made more efficient by using powers of two - # in the usual way. - prd = c - for i in range(2, power + 1): - prd = hermemul(prd, c) - return prd + return pu._pow(hermemul, c, pow, maxpower) def hermeder(c, m=1, scl=1, axis=0): @@ -684,22 +637,18 @@ def hermeder(c, m=1, scl=1, axis=0): -------- >>> from numpy.polynomial.hermite_e import hermeder >>> hermeder([ 1., 1., 1., 1.]) - array([ 1., 2., 3.]) + array([1., 2., 3.]) >>> hermeder([-0.25, 1., 1./2., 1./3., 1./4 ], m=2) - array([ 1., 2., 3.]) + array([1., 2., 3.]) """ - c = np.array(c, ndmin=1, copy=1) + c = np.array(c, ndmin=1, copy=True) if c.dtype.char in '?bBhHiIlLqQpP': c = c.astype(np.double) - cnt, iaxis = [int(t) for t in [m, axis]] - - if cnt != m: - raise ValueError("The order of derivation must be integer") + cnt = pu._deprecate_as_int(m, "the order of derivation") + iaxis = pu._deprecate_as_int(axis, "the axis") if cnt < 0: raise ValueError("The order of derivation must be non-negative") - if iaxis != axis: - raise ValueError("The axis must be integer") iaxis = normalize_axis_index(iaxis, c.ndim) if cnt == 0: @@ -793,26 +742,24 @@ def hermeint(c, m=1, k=[], lbnd=0, scl=1, axis=0): -------- >>> from numpy.polynomial.hermite_e import hermeint >>> hermeint([1, 2, 3]) # integrate once, value 0 at 0. - array([ 1., 1., 1., 1.]) + array([1., 1., 1., 1.]) >>> hermeint([1, 2, 3], m=2) # integrate twice, value & deriv 0 at 0 - array([-0.25 , 1. , 0.5 , 0.33333333, 0.25 ]) + array([-0.25 , 1. , 0.5 , 0.33333333, 0.25 ]) # may vary >>> hermeint([1, 2, 3], k=1) # integrate once, value 1 at 0. - array([ 2., 1., 1., 1.]) + array([2., 1., 1., 1.]) >>> hermeint([1, 2, 3], lbnd=-1) # integrate once, value 0 at -1 array([-1., 1., 1., 1.]) >>> hermeint([1, 2, 3], m=2, k=[1, 2], lbnd=-1) - array([ 1.83333333, 0. , 0.5 , 0.33333333, 0.25 ]) + array([ 1.83333333, 0. , 0.5 , 0.33333333, 0.25 ]) # may vary """ - c = np.array(c, ndmin=1, copy=1) + c = np.array(c, ndmin=1, copy=True) if c.dtype.char in '?bBhHiIlLqQpP': c = c.astype(np.double) if not np.iterable(k): k = [k] - cnt, iaxis = [int(t) for t in [m, axis]] - - if cnt != m: - raise ValueError("The order of integration must be integer") + cnt = pu._deprecate_as_int(m, "the order of integration") + iaxis = pu._deprecate_as_int(axis, "the axis") if cnt < 0: raise ValueError("The order of integration must be non-negative") if len(k) > cnt: @@ -821,8 +768,6 @@ def hermeint(c, m=1, k=[], lbnd=0, scl=1, axis=0): raise ValueError("lbnd must be a scalar.") if np.ndim(scl) != 0: raise ValueError("scl must be a scalar.") - if iaxis != axis: - raise ValueError("The axis must be integer") iaxis = normalize_axis_index(iaxis, c.ndim) if cnt == 0: @@ -912,11 +857,11 @@ def hermeval(x, c, tensor=True): >>> hermeval(1, coef) 3.0 >>> hermeval([[1,2],[3,4]], coef) - array([[ 3., 14.], - [ 31., 54.]]) + array([[ 3., 14.], + [31., 54.]]) """ - c = np.array(c, ndmin=1, copy=0) + c = np.array(c, ndmin=1, copy=False) if c.dtype.char in '?bBhHiIlLqQpP': c = c.astype(np.double) if isinstance(x, (tuple, list)): @@ -988,14 +933,7 @@ def hermeval2d(x, y, c): .. versionadded:: 1.7.0 """ - try: - x, y = np.array((x, y), copy=0) - except Exception: - raise ValueError('x, y are incompatible') - - c = hermeval(x, c) - c = hermeval(y, c, tensor=False) - return c + return pu._valnd(hermeval, c, x, y) def hermegrid2d(x, y, c): @@ -1048,9 +986,7 @@ def hermegrid2d(x, y, c): .. versionadded:: 1.7.0 """ - c = hermeval(x, c) - c = hermeval(y, c) - return c + return pu._gridnd(hermeval, c, x, y) def hermeval3d(x, y, z, c): @@ -1101,15 +1037,7 @@ def hermeval3d(x, y, z, c): .. versionadded:: 1.7.0 """ - try: - x, y, z = np.array((x, y, z), copy=0) - except Exception: - raise ValueError('x, y, z are incompatible') - - c = hermeval(x, c) - c = hermeval(y, c, tensor=False) - c = hermeval(z, c, tensor=False) - return c + return pu._valnd(hermeval, c, x, y, z) def hermegrid3d(x, y, z, c): @@ -1165,10 +1093,7 @@ def hermegrid3d(x, y, z, c): .. versionadded:: 1.7.0 """ - c = hermeval(x, c) - c = hermeval(y, c) - c = hermeval(z, c) - return c + return pu._gridnd(hermeval, c, x, y, z) def hermevander(x, deg): @@ -1215,13 +1140,11 @@ def hermevander(x, deg): [ 1., 1., 0., -2.]]) """ - ideg = int(deg) - if ideg != deg: - raise ValueError("deg must be integer") + ideg = pu._deprecate_as_int(deg, "deg") if ideg < 0: raise ValueError("deg must be non-negative") - x = np.array(x, copy=0, ndmin=1) + 0.0 + x = np.array(x, copy=False, ndmin=1) + 0.0 dims = (ideg + 1,) + x.shape dtyp = x.dtype v = np.empty(dims, dtype=dtyp) @@ -1270,12 +1193,12 @@ def hermevander2d(x, y, deg): ------- vander2d : ndarray The shape of the returned matrix is ``x.shape + (order,)``, where - :math:`order = (deg[0]+1)*(deg([1]+1)`. The dtype will be the same + :math:`order = (deg[0]+1)*(deg[1]+1)`. The dtype will be the same as the converted `x` and `y`. See Also -------- - hermevander, hermevander3d. hermeval2d, hermeval3d + hermevander, hermevander3d, hermeval2d, hermeval3d Notes ----- @@ -1283,17 +1206,7 @@ def hermevander2d(x, y, deg): .. versionadded:: 1.7.0 """ - ideg = [int(d) for d in deg] - is_valid = [id == d and id >= 0 for id, d in zip(ideg, deg)] - if is_valid != [1, 1]: - raise ValueError("degrees must be non-negative integers") - degx, degy = ideg - x, y = np.array((x, y), copy=0) + 0.0 - - vx = hermevander(x, degx) - vy = hermevander(y, degy) - v = vx[..., None]*vy[..., None,:] - return v.reshape(v.shape[:-2] + (-1,)) + return pu._vander_nd_flat((hermevander, hermevander), (x, y), deg) def hermevander3d(x, y, z, deg): @@ -1334,12 +1247,12 @@ def hermevander3d(x, y, z, deg): ------- vander3d : ndarray The shape of the returned matrix is ``x.shape + (order,)``, where - :math:`order = (deg[0]+1)*(deg([1]+1)*(deg[2]+1)`. The dtype will + :math:`order = (deg[0]+1)*(deg[1]+1)*(deg[2]+1)`. The dtype will be the same as the converted `x`, `y`, and `z`. See Also -------- - hermevander, hermevander3d. hermeval2d, hermeval3d + hermevander, hermevander3d, hermeval2d, hermeval3d Notes ----- @@ -1347,18 +1260,7 @@ def hermevander3d(x, y, z, deg): .. versionadded:: 1.7.0 """ - ideg = [int(d) for d in deg] - is_valid = [id == d and id >= 0 for id, d in zip(ideg, deg)] - if is_valid != [1, 1, 1]: - raise ValueError("degrees must be non-negative integers") - degx, degy, degz = ideg - x, y, z = np.array((x, y, z), copy=0) + 0.0 - - vx = hermevander(x, degx) - vy = hermevander(y, degy) - vz = hermevander(z, degz) - v = vx[..., None, None]*vy[..., None,:, None]*vz[..., None, None,:] - return v.reshape(v.shape[:-3] + (-1,)) + return pu._vander_nd_flat((hermevander, hermevander, hermevander), (x, y, z), deg) def hermefit(x, y, deg, rcond=None, full=False, w=None): @@ -1399,10 +1301,11 @@ def hermefit(x, y, deg, rcond=None, full=False, w=None): default) just the coefficients are returned, when True diagnostic information from the singular value decomposition is also returned. w : array_like, shape (`M`,), optional - Weights. If not None, the contribution of each point - ``(x[i],y[i])`` to the fit is weighted by `w[i]`. Ideally the - weights are chosen so that the errors of the products ``w[i]*y[i]`` - all have the same variance. The default value is None. + Weights. If not None, the weight ``w[i]`` applies to the unsquared + residual ``y[i] - y_hat[i]`` at ``x[i]``. Ideally the weights are + chosen so that the errors of the products ``w[i]*y[i]`` all have the + same variance. When using inverse-variance weighting, use + ``w[i] = 1/sigma(y[i])``. The default value is None. Returns ------- @@ -1412,32 +1315,36 @@ def hermefit(x, y, deg, rcond=None, full=False, w=None): `k`. [residuals, rank, singular_values, rcond] : list - These values are only returned if `full` = True + These values are only returned if ``full == True`` - resid -- sum of squared residuals of the least squares fit - rank -- the numerical rank of the scaled Vandermonde matrix - sv -- singular values of the scaled Vandermonde matrix - rcond -- value of `rcond`. + - residuals -- sum of squared residuals of the least squares fit + - rank -- the numerical rank of the scaled Vandermonde matrix + - singular_values -- singular values of the scaled Vandermonde matrix + - rcond -- value of `rcond`. - For more details, see `linalg.lstsq`. + For more details, see `numpy.linalg.lstsq`. Warns ----- RankWarning The rank of the coefficient matrix in the least-squares fit is - deficient. The warning is only raised if `full` = False. The + deficient. The warning is only raised if ``full = False``. The warnings can be turned off by >>> import warnings - >>> warnings.simplefilter('ignore', RankWarning) + >>> warnings.simplefilter('ignore', np.RankWarning) See Also -------- - chebfit, legfit, polyfit, hermfit, polyfit + numpy.polynomial.chebyshev.chebfit + numpy.polynomial.legendre.legfit + numpy.polynomial.polynomial.polyfit + numpy.polynomial.hermite.hermfit + numpy.polynomial.laguerre.lagfit hermeval : Evaluates a Hermite series. hermevander : pseudo Vandermonde matrix of Hermite series. hermeweight : HermiteE weight function. - linalg.lstsq : Computes a least-squares fit from the matrix. + numpy.linalg.lstsq : Computes a least-squares fit from the matrix. scipy.interpolate.UnivariateSpline : Computes spline fits. Notes @@ -1466,100 +1373,27 @@ def hermefit(x, y, deg, rcond=None, full=False, w=None): Fits using HermiteE series are probably most useful when the data can be approximated by ``sqrt(w(x)) * p(x)``, where `w(x)` is the HermiteE - weight. In that case the weight ``sqrt(w(x[i])`` should be used - together with data values ``y[i]/sqrt(w(x[i])``. The weight function is + weight. In that case the weight ``sqrt(w(x[i]))`` should be used + together with data values ``y[i]/sqrt(w(x[i]))``. The weight function is available as `hermeweight`. References ---------- .. [1] Wikipedia, "Curve fitting", - http://en.wikipedia.org/wiki/Curve_fitting + https://en.wikipedia.org/wiki/Curve_fitting Examples -------- >>> from numpy.polynomial.hermite_e import hermefit, hermeval >>> x = np.linspace(-10, 10) + >>> np.random.seed(123) >>> err = np.random.randn(len(x))/10 >>> y = hermeval(x, [1, 2, 3]) + err >>> hermefit(x, y, 2) - array([ 1.01690445, 1.99951418, 2.99948696]) + array([ 1.01690445, 1.99951418, 2.99948696]) # may vary """ - x = np.asarray(x) + 0.0 - y = np.asarray(y) + 0.0 - deg = np.asarray(deg) - - # check arguments. - if deg.ndim > 1 or deg.dtype.kind not in 'iu' or deg.size == 0: - raise TypeError("deg must be an int or non-empty 1-D array of int") - if deg.min() < 0: - raise ValueError("expected deg >= 0") - if x.ndim != 1: - raise TypeError("expected 1D vector for x") - if x.size == 0: - raise TypeError("expected non-empty vector for x") - if y.ndim < 1 or y.ndim > 2: - raise TypeError("expected 1D or 2D array for y") - if len(x) != len(y): - raise TypeError("expected x and y to have same length") - - if deg.ndim == 0: - lmax = deg - order = lmax + 1 - van = hermevander(x, lmax) - else: - deg = np.sort(deg) - lmax = deg[-1] - order = len(deg) - van = hermevander(x, lmax)[:, deg] - - # set up the least squares matrices in transposed form - lhs = van.T - rhs = y.T - if w is not None: - w = np.asarray(w) + 0.0 - if w.ndim != 1: - raise TypeError("expected 1D vector for w") - if len(x) != len(w): - raise TypeError("expected x and w to have same length") - # apply weights. Don't use inplace operations as they - # can cause problems with NA. - lhs = lhs * w - rhs = rhs * w - - # set rcond - if rcond is None: - rcond = len(x)*np.finfo(x.dtype).eps - - # Determine the norms of the design matrix columns. - if issubclass(lhs.dtype.type, np.complexfloating): - scl = np.sqrt((np.square(lhs.real) + np.square(lhs.imag)).sum(1)) - else: - scl = np.sqrt(np.square(lhs).sum(1)) - scl[scl == 0] = 1 - - # Solve the least squares problem. - c, resids, rank, s = la.lstsq(lhs.T/scl, rhs.T, rcond) - c = (c.T/scl).T - - # Expand c to include non-fitted coefficients which are set to zero - if deg.ndim > 0: - if c.ndim == 2: - cc = np.zeros((lmax+1, c.shape[1]), dtype=c.dtype) - else: - cc = np.zeros(lmax+1, dtype=c.dtype) - cc[deg] = c - c = cc - - # warn on rank reduction - if rank != order and not full: - msg = "The fit may be poorly conditioned" - warnings.warn(msg, pu.RankWarning, stacklevel=2) - - if full: - return c, [resids, rank, s, rcond] - else: - return c + return pu._fit(hermevander, x, y, deg, rcond, full, w) def hermecompanion(c): @@ -1629,7 +1463,11 @@ def hermeroots(c): See Also -------- - polyroots, legroots, lagroots, hermroots, chebroots + numpy.polynomial.polynomial.polyroots + numpy.polynomial.legendre.legroots + numpy.polynomial.laguerre.lagroots + numpy.polynomial.hermite.hermroots + numpy.polynomial.chebyshev.chebroots Notes ----- @@ -1649,9 +1487,9 @@ def hermeroots(c): >>> from numpy.polynomial.hermite_e import hermeroots, hermefromroots >>> coef = hermefromroots([-1, 0, 1]) >>> coef - array([ 0., 2., 0., 1.]) + array([0., 2., 0., 1.]) >>> hermeroots(coef) - array([-1., 0., 1.]) + array([-1., 0., 1.]) # may vary """ # c is a trimmed copy @@ -1661,7 +1499,8 @@ def hermeroots(c): if len(c) == 2: return np.array([-c[0]/c[1]]) - m = hermecompanion(c) + # rotated companion matrix reduces error + m = hermecompanion(c)[::-1,::-1] r = la.eigvals(m) r.sort() return r @@ -1697,7 +1536,7 @@ def _normed_hermite_e_n(x, n): """ if n == 0: - return np.ones(x.shape)/np.sqrt(np.sqrt(2*np.pi)) + return np.full(x.shape, 1/np.sqrt(np.sqrt(2*np.pi))) c0 = 0. c1 = 1./np.sqrt(np.sqrt(2*np.pi)) @@ -1746,9 +1585,9 @@ def hermegauss(deg): the right value when integrating 1. """ - ideg = int(deg) - if ideg != deg or ideg < 1: - raise ValueError("deg must be a non-negative integer") + ideg = pu._deprecate_as_int(deg, "deg") + if ideg <= 0: + raise ValueError("deg must be a positive integer") # first approximation of roots. We use the fact that the companion # matrix is symmetric in this case in order to obtain better zeros. @@ -1845,6 +1684,6 @@ class HermiteE(ABCPolyBase): _fromroots = staticmethod(hermefromroots) # Virtual properties - nickname = 'herme' domain = np.array(hermedomain) window = np.array(hermedomain) + basis_name = 'He' diff --git a/numpy/polynomial/hermite_e.pyi b/numpy/polynomial/hermite_e.pyi new file mode 100644 index 000000000000..c029bfda7788 --- /dev/null +++ b/numpy/polynomial/hermite_e.pyi @@ -0,0 +1,46 @@ +from typing import Any, List + +from numpy import ndarray, dtype, int_ +from numpy.polynomial._polybase import ABCPolyBase +from numpy.polynomial.polyutils import trimcoef + +__all__: list[str] + +hermetrim = trimcoef + +def poly2herme(pol): ... +def herme2poly(c): ... + +hermedomain: ndarray[Any, dtype[int_]] +hermezero: ndarray[Any, dtype[int_]] +hermeone: ndarray[Any, dtype[int_]] +hermex: ndarray[Any, dtype[int_]] + +def hermeline(off, scl): ... +def hermefromroots(roots): ... +def hermeadd(c1, c2): ... +def hermesub(c1, c2): ... +def hermemulx(c): ... +def hermemul(c1, c2): ... +def hermediv(c1, c2): ... +def hermepow(c, pow, maxpower=...): ... +def hermeder(c, m=..., scl=..., axis=...): ... +def hermeint(c, m=..., k = ..., lbnd=..., scl=..., axis=...): ... +def hermeval(x, c, tensor=...): ... +def hermeval2d(x, y, c): ... +def hermegrid2d(x, y, c): ... +def hermeval3d(x, y, z, c): ... +def hermegrid3d(x, y, z, c): ... +def hermevander(x, deg): ... +def hermevander2d(x, y, deg): ... +def hermevander3d(x, y, z, deg): ... +def hermefit(x, y, deg, rcond=..., full=..., w=...): ... +def hermecompanion(c): ... +def hermeroots(c): ... +def hermegauss(deg): ... +def hermeweight(x): ... + +class HermiteE(ABCPolyBase): + domain: Any + window: Any + basis_name: Any diff --git a/numpy/polynomial/laguerre.py b/numpy/polynomial/laguerre.py index 5a9a5111abd3..d9ca373ddd5c 100644 --- a/numpy/polynomial/laguerre.py +++ b/numpy/polynomial/laguerre.py @@ -1,5 +1,7 @@ """ -Objects for dealing with Laguerre series. +================================================== +Laguerre Series (:mod:`numpy.polynomial.laguerre`) +================================================== This module provides a number of objects (mostly functions) useful for dealing with Laguerre series, including a `Laguerre` class that @@ -7,59 +9,72 @@ on how this module represents and works with such polynomials is in the docstring for its "parent" sub-package, `numpy.polynomial`). +Classes +------- +.. autosummary:: + :toctree: generated/ + + Laguerre + Constants --------- -- `lagdomain` -- Laguerre series default domain, [-1,1]. -- `lagzero` -- Laguerre series that evaluates identically to 0. -- `lagone` -- Laguerre series that evaluates identically to 1. -- `lagx` -- Laguerre series for the identity map, ``f(x) = x``. +.. autosummary:: + :toctree: generated/ + + lagdomain + lagzero + lagone + lagx Arithmetic ---------- -- `lagmulx` -- multiply a Laguerre series in ``P_i(x)`` by ``x``. -- `lagadd` -- add two Laguerre series. -- `lagsub` -- subtract one Laguerre series from another. -- `lagmul` -- multiply two Laguerre series. -- `lagdiv` -- divide one Laguerre series by another. -- `lagval` -- evaluate a Laguerre series at given points. -- `lagval2d` -- evaluate a 2D Laguerre series at given points. -- `lagval3d` -- evaluate a 3D Laguerre series at given points. -- `laggrid2d` -- evaluate a 2D Laguerre series on a Cartesian product. -- `laggrid3d` -- evaluate a 3D Laguerre series on a Cartesian product. +.. autosummary:: + :toctree: generated/ + + lagadd + lagsub + lagmulx + lagmul + lagdiv + lagpow + lagval + lagval2d + lagval3d + laggrid2d + laggrid3d Calculus -------- -- `lagder` -- differentiate a Laguerre series. -- `lagint` -- integrate a Laguerre series. +.. autosummary:: + :toctree: generated/ + + lagder + lagint Misc Functions -------------- -- `lagfromroots` -- create a Laguerre series with specified roots. -- `lagroots` -- find the roots of a Laguerre series. -- `lagvander` -- Vandermonde-like matrix for Laguerre polynomials. -- `lagvander2d` -- Vandermonde-like matrix for 2D power series. -- `lagvander3d` -- Vandermonde-like matrix for 3D power series. -- `laggauss` -- Gauss-Laguerre quadrature, points and weights. -- `lagweight` -- Laguerre weight function. -- `lagcompanion` -- symmetrized companion matrix in Laguerre form. -- `lagfit` -- least-squares fit returning a Laguerre series. -- `lagtrim` -- trim leading coefficients from a Laguerre series. -- `lagline` -- Laguerre series of given straight line. -- `lag2poly` -- convert a Laguerre series to a polynomial. -- `poly2lag` -- convert a polynomial to a Laguerre series. - -Classes -------- -- `Laguerre` -- A Laguerre series class. +.. autosummary:: + :toctree: generated/ + + lagfromroots + lagroots + lagvander + lagvander2d + lagvander3d + laggauss + lagweight + lagcompanion + lagfit + lagtrim + lagline + lag2poly + poly2lag See also -------- `numpy.polynomial` """ -from __future__ import division, absolute_import, print_function - -import warnings import numpy as np import numpy.linalg as la from numpy.core.multiarray import normalize_axis_index @@ -117,10 +132,9 @@ def poly2lag(pol): """ [pol] = pu.as_series([pol]) - deg = len(pol) - 1 res = 0 - for i in range(deg, -1, -1): - res = lagadd(lagmulx(res), pol[i]) + for p in pol[::-1]: + res = lagadd(lagmulx(res), p) return res @@ -159,7 +173,7 @@ def lag2poly(c): -------- >>> from numpy.polynomial.laguerre import lag2poly >>> lag2poly([ 23., -63., 58., -18.]) - array([ 0., 1., 2., 3.]) + array([0., 1., 2., 3.]) """ from .polynomial import polyadd, polysub, polymulx @@ -200,8 +214,6 @@ def lagline(off, scl): """ Laguerre series whose graph is a straight line. - - Parameters ---------- off, scl : scalars @@ -215,7 +227,11 @@ def lagline(off, scl): See Also -------- - polyline, chebline + numpy.polynomial.polynomial.polyline + numpy.polynomial.chebyshev.chebline + numpy.polynomial.legendre.legline + numpy.polynomial.hermite.hermline + numpy.polynomial.hermite_e.hermeline Examples -------- @@ -268,35 +284,24 @@ def lagfromroots(roots): See Also -------- - polyfromroots, legfromroots, chebfromroots, hermfromroots, - hermefromroots. + numpy.polynomial.polynomial.polyfromroots + numpy.polynomial.legendre.legfromroots + numpy.polynomial.chebyshev.chebfromroots + numpy.polynomial.hermite.hermfromroots + numpy.polynomial.hermite_e.hermefromroots Examples -------- >>> from numpy.polynomial.laguerre import lagfromroots, lagval >>> coef = lagfromroots((-1, 0, 1)) >>> lagval((-1, 0, 1), coef) - array([ 0., 0., 0.]) + array([0., 0., 0.]) >>> coef = lagfromroots((-1j, 1j)) >>> lagval((-1j, 1j), coef) - array([ 0.+0.j, 0.+0.j]) + array([0.+0.j, 0.+0.j]) """ - if len(roots) == 0: - return np.ones(1) - else: - [roots] = pu.as_series([roots], trim=False) - roots.sort() - p = [lagline(-r, 1) for r in roots] - n = len(p) - while n > 1: - m, r = divmod(n, 2) - tmp = [lagmul(p[i], p[i+m]) for i in range(m)] - if r: - tmp[0] = lagmul(tmp[0], p[-1]) - p = tmp - n = m - return p[0] + return pu._fromroots(lagline, lagmul, roots) def lagadd(c1, c2): @@ -320,7 +325,7 @@ def lagadd(c1, c2): See Also -------- - lagsub, lagmul, lagdiv, lagpow + lagsub, lagmulx, lagmul, lagdiv, lagpow Notes ----- @@ -333,19 +338,11 @@ def lagadd(c1, c2): -------- >>> from numpy.polynomial.laguerre import lagadd >>> lagadd([1, 2, 3], [1, 2, 3, 4]) - array([ 2., 4., 6., 4.]) + array([2., 4., 6., 4.]) """ - # c1, c2 are trimmed copies - [c1, c2] = pu.as_series([c1, c2]) - if len(c1) > len(c2): - c1[:c2.size] += c2 - ret = c1 - else: - c2[:c1.size] += c1 - ret = c2 - return pu.trimseq(ret) + return pu._add(c1, c2) def lagsub(c1, c2): @@ -369,7 +366,7 @@ def lagsub(c1, c2): See Also -------- - lagadd, lagmul, lagdiv, lagpow + lagadd, lagmulx, lagmul, lagdiv, lagpow Notes ----- @@ -382,19 +379,10 @@ def lagsub(c1, c2): -------- >>> from numpy.polynomial.laguerre import lagsub >>> lagsub([1, 2, 3, 4], [1, 2, 3]) - array([ 0., 0., 0., 4.]) + array([0., 0., 0., 4.]) """ - # c1, c2 are trimmed copies - [c1, c2] = pu.as_series([c1, c2]) - if len(c1) > len(c2): - c1[:c2.size] -= c2 - ret = c1 - else: - c2 = -c2 - c2[:c1.size] += c1 - ret = c2 - return pu.trimseq(ret) + return pu._sub(c1, c2) def lagmulx(c): @@ -415,6 +403,10 @@ def lagmulx(c): out : ndarray Array representing the result of the multiplication. + See Also + -------- + lagadd, lagsub, lagmul, lagdiv, lagpow + Notes ----- The multiplication uses the recursion relationship for Laguerre @@ -422,13 +414,13 @@ def lagmulx(c): .. math:: - xP_i(x) = (-(i + 1)*P_{i + 1}(x) + (2i + 1)P_{i}(x) - iP_{i - 1}(x)) + xP_i(x) = (-(i + 1)*P_{i + 1}(x) + (2i + 1)P_{i}(x) - iP_{i - 1}(x)) Examples -------- >>> from numpy.polynomial.laguerre import lagmulx >>> lagmulx([1, 2, 3]) - array([ -1., -1., 11., -9.]) + array([-1., -1., 11., -9.]) """ # c is a trimmed copy @@ -468,7 +460,7 @@ def lagmul(c1, c2): See Also -------- - lagadd, lagsub, lagdiv, lagpow + lagadd, lagsub, lagmulx, lagdiv, lagpow Notes ----- @@ -536,7 +528,7 @@ def lagdiv(c1, c2): See Also -------- - lagadd, lagsub, lagmul, lagpow + lagadd, lagsub, lagmulx, lagmul, lagpow Notes ----- @@ -551,31 +543,12 @@ def lagdiv(c1, c2): -------- >>> from numpy.polynomial.laguerre import lagdiv >>> lagdiv([ 8., -13., 38., -51., 36.], [0, 1, 2]) - (array([ 1., 2., 3.]), array([ 0.])) + (array([1., 2., 3.]), array([0.])) >>> lagdiv([ 9., -12., 38., -51., 36.], [0, 1, 2]) - (array([ 1., 2., 3.]), array([ 1., 1.])) + (array([1., 2., 3.]), array([1., 1.])) """ - # c1, c2 are trimmed copies - [c1, c2] = pu.as_series([c1, c2]) - if c2[-1] == 0: - raise ZeroDivisionError() - - lc1 = len(c1) - lc2 = len(c2) - if lc1 < lc2: - return c1[:1]*0, c1 - elif lc2 == 1: - return c1/c2[-1], c1[:1]*0 - else: - quo = np.empty(lc1 - lc2 + 1, dtype=c1.dtype) - rem = c1 - for i in range(lc1 - lc2, - 1, -1): - p = lagmul([0]*i + [1], c2) - q = rem[-1]/p[-1] - rem = rem[:-1] - q*p[:-1] - quo[i] = q - return quo, pu.trimseq(rem) + return pu._div(lagmul, c1, c2) def lagpow(c, pow, maxpower=16): @@ -603,7 +576,7 @@ def lagpow(c, pow, maxpower=16): See Also -------- - lagadd, lagsub, lagmul, lagdiv + lagadd, lagsub, lagmulx, lagmul, lagdiv Examples -------- @@ -612,24 +585,7 @@ def lagpow(c, pow, maxpower=16): array([ 14., -16., 56., -72., 54.]) """ - # c is a trimmed copy - [c] = pu.as_series([c]) - power = int(pow) - if power != pow or power < 0: - raise ValueError("Power must be a non-negative integer.") - elif maxpower is not None and power > maxpower: - raise ValueError("Power is too large") - elif power == 0: - return np.array([1], dtype=c.dtype) - elif power == 1: - return c - else: - # This can be made more efficient by using powers of two - # in the usual way. - prd = c - for i in range(2, power + 1): - prd = lagmul(prd, c) - return prd + return pu._pow(lagmul, c, pow, maxpower) def lagder(c, m=1, scl=1, axis=0): @@ -682,22 +638,19 @@ def lagder(c, m=1, scl=1, axis=0): -------- >>> from numpy.polynomial.laguerre import lagder >>> lagder([ 1., 1., 1., -3.]) - array([ 1., 2., 3.]) + array([1., 2., 3.]) >>> lagder([ 1., 0., 0., -4., 3.], m=2) - array([ 1., 2., 3.]) + array([1., 2., 3.]) """ - c = np.array(c, ndmin=1, copy=1) + c = np.array(c, ndmin=1, copy=True) if c.dtype.char in '?bBhHiIlLqQpP': c = c.astype(np.double) - cnt, iaxis = [int(t) for t in [m, axis]] - if cnt != m: - raise ValueError("The order of derivation must be integer") + cnt = pu._deprecate_as_int(m, "the order of derivation") + iaxis = pu._deprecate_as_int(axis, "the axis") if cnt < 0: raise ValueError("The order of derivation must be non-negative") - if iaxis != axis: - raise ValueError("The axis must be integer") iaxis = normalize_axis_index(iaxis, c.ndim) if cnt == 0: @@ -800,20 +753,18 @@ def lagint(c, m=1, k=[], lbnd=0, scl=1, axis=0): >>> lagint([1,2,3], k=1) array([ 2., 1., 1., -3.]) >>> lagint([1,2,3], lbnd=-1) - array([ 11.5, 1. , 1. , -3. ]) + array([11.5, 1. , 1. , -3. ]) >>> lagint([1,2], m=2, k=[1,2], lbnd=-1) - array([ 11.16666667, -5. , -3. , 2. ]) + array([ 11.16666667, -5. , -3. , 2. ]) # may vary """ - c = np.array(c, ndmin=1, copy=1) + c = np.array(c, ndmin=1, copy=True) if c.dtype.char in '?bBhHiIlLqQpP': c = c.astype(np.double) if not np.iterable(k): k = [k] - cnt, iaxis = [int(t) for t in [m, axis]] - - if cnt != m: - raise ValueError("The order of integration must be integer") + cnt = pu._deprecate_as_int(m, "the order of integration") + iaxis = pu._deprecate_as_int(axis, "the axis") if cnt < 0: raise ValueError("The order of integration must be non-negative") if len(k) > cnt: @@ -822,8 +773,6 @@ def lagint(c, m=1, k=[], lbnd=0, scl=1, axis=0): raise ValueError("lbnd must be a scalar.") if np.ndim(scl) != 0: raise ValueError("scl must be a scalar.") - if iaxis != axis: - raise ValueError("The axis must be integer") iaxis = normalize_axis_index(iaxis, c.ndim) if cnt == 0: @@ -918,7 +867,7 @@ def lagval(x, c, tensor=True): [-4.5, -2. ]]) """ - c = np.array(c, ndmin=1, copy=0) + c = np.array(c, ndmin=1, copy=False) if c.dtype.char in '?bBhHiIlLqQpP': c = c.astype(np.double) if isinstance(x, (tuple, list)): @@ -990,14 +939,7 @@ def lagval2d(x, y, c): .. versionadded:: 1.7.0 """ - try: - x, y = np.array((x, y), copy=0) - except Exception: - raise ValueError('x, y are incompatible') - - c = lagval(x, c) - c = lagval(y, c, tensor=False) - return c + return pu._valnd(lagval, c, x, y) def laggrid2d(x, y, c): @@ -1050,9 +992,7 @@ def laggrid2d(x, y, c): .. versionadded:: 1.7.0 """ - c = lagval(x, c) - c = lagval(y, c) - return c + return pu._gridnd(lagval, c, x, y) def lagval3d(x, y, z, c): @@ -1090,7 +1030,7 @@ def lagval3d(x, y, z, c): Returns ------- values : ndarray, compatible object - The values of the multidimension polynomial on points formed with + The values of the multidimensional polynomial on points formed with triples of corresponding values from `x`, `y`, and `z`. See Also @@ -1103,15 +1043,7 @@ def lagval3d(x, y, z, c): .. versionadded:: 1.7.0 """ - try: - x, y, z = np.array((x, y, z), copy=0) - except Exception: - raise ValueError('x, y, z are incompatible') - - c = lagval(x, c) - c = lagval(y, c, tensor=False) - c = lagval(z, c, tensor=False) - return c + return pu._valnd(lagval, c, x, y, z) def laggrid3d(x, y, z, c): @@ -1167,10 +1099,7 @@ def laggrid3d(x, y, z, c): .. versionadded:: 1.7.0 """ - c = lagval(x, c) - c = lagval(y, c) - c = lagval(z, c) - return c + return pu._gridnd(lagval, c, x, y, z) def lagvander(x, deg): @@ -1217,13 +1146,11 @@ def lagvander(x, deg): [ 1. , -1. , -1. , -0.33333333]]) """ - ideg = int(deg) - if ideg != deg: - raise ValueError("deg must be integer") + ideg = pu._deprecate_as_int(deg, "deg") if ideg < 0: raise ValueError("deg must be non-negative") - x = np.array(x, copy=0, ndmin=1) + 0.0 + x = np.array(x, copy=False, ndmin=1) + 0.0 dims = (ideg + 1,) + x.shape dtyp = x.dtype v = np.empty(dims, dtype=dtyp) @@ -1272,12 +1199,12 @@ def lagvander2d(x, y, deg): ------- vander2d : ndarray The shape of the returned matrix is ``x.shape + (order,)``, where - :math:`order = (deg[0]+1)*(deg([1]+1)`. The dtype will be the same + :math:`order = (deg[0]+1)*(deg[1]+1)`. The dtype will be the same as the converted `x` and `y`. See Also -------- - lagvander, lagvander3d. lagval2d, lagval3d + lagvander, lagvander3d, lagval2d, lagval3d Notes ----- @@ -1285,17 +1212,7 @@ def lagvander2d(x, y, deg): .. versionadded:: 1.7.0 """ - ideg = [int(d) for d in deg] - is_valid = [id == d and id >= 0 for id, d in zip(ideg, deg)] - if is_valid != [1, 1]: - raise ValueError("degrees must be non-negative integers") - degx, degy = ideg - x, y = np.array((x, y), copy=0) + 0.0 - - vx = lagvander(x, degx) - vy = lagvander(y, degy) - v = vx[..., None]*vy[..., None,:] - return v.reshape(v.shape[:-2] + (-1,)) + return pu._vander_nd_flat((lagvander, lagvander), (x, y), deg) def lagvander3d(x, y, z, deg): @@ -1336,12 +1253,12 @@ def lagvander3d(x, y, z, deg): ------- vander3d : ndarray The shape of the returned matrix is ``x.shape + (order,)``, where - :math:`order = (deg[0]+1)*(deg([1]+1)*(deg[2]+1)`. The dtype will + :math:`order = (deg[0]+1)*(deg[1]+1)*(deg[2]+1)`. The dtype will be the same as the converted `x`, `y`, and `z`. See Also -------- - lagvander, lagvander3d. lagval2d, lagval3d + lagvander, lagvander3d, lagval2d, lagval3d Notes ----- @@ -1349,18 +1266,7 @@ def lagvander3d(x, y, z, deg): .. versionadded:: 1.7.0 """ - ideg = [int(d) for d in deg] - is_valid = [id == d and id >= 0 for id, d in zip(ideg, deg)] - if is_valid != [1, 1, 1]: - raise ValueError("degrees must be non-negative integers") - degx, degy, degz = ideg - x, y, z = np.array((x, y, z), copy=0) + 0.0 - - vx = lagvander(x, degx) - vy = lagvander(y, degy) - vz = lagvander(z, degz) - v = vx[..., None, None]*vy[..., None,:, None]*vz[..., None, None,:] - return v.reshape(v.shape[:-3] + (-1,)) + return pu._vander_nd_flat((lagvander, lagvander, lagvander), (x, y, z), deg) def lagfit(x, y, deg, rcond=None, full=False, w=None): @@ -1401,10 +1307,11 @@ def lagfit(x, y, deg, rcond=None, full=False, w=None): default) just the coefficients are returned, when True diagnostic information from the singular value decomposition is also returned. w : array_like, shape (`M`,), optional - Weights. If not None, the contribution of each point - ``(x[i],y[i])`` to the fit is weighted by `w[i]`. Ideally the - weights are chosen so that the errors of the products ``w[i]*y[i]`` - all have the same variance. The default value is None. + Weights. If not None, the weight ``w[i]`` applies to the unsquared + residual ``y[i] - y_hat[i]`` at ``x[i]``. Ideally the weights are + chosen so that the errors of the products ``w[i]*y[i]`` all have the + same variance. When using inverse-variance weighting, use + ``w[i] = 1/sigma(y[i])``. The default value is None. Returns ------- @@ -1414,32 +1321,36 @@ def lagfit(x, y, deg, rcond=None, full=False, w=None): `k`. [residuals, rank, singular_values, rcond] : list - These values are only returned if `full` = True + These values are only returned if ``full == True`` - resid -- sum of squared residuals of the least squares fit - rank -- the numerical rank of the scaled Vandermonde matrix - sv -- singular values of the scaled Vandermonde matrix - rcond -- value of `rcond`. + - residuals -- sum of squared residuals of the least squares fit + - rank -- the numerical rank of the scaled Vandermonde matrix + - singular_values -- singular values of the scaled Vandermonde matrix + - rcond -- value of `rcond`. - For more details, see `linalg.lstsq`. + For more details, see `numpy.linalg.lstsq`. Warns ----- RankWarning The rank of the coefficient matrix in the least-squares fit is - deficient. The warning is only raised if `full` = False. The + deficient. The warning is only raised if ``full == False``. The warnings can be turned off by >>> import warnings - >>> warnings.simplefilter('ignore', RankWarning) + >>> warnings.simplefilter('ignore', np.RankWarning) See Also -------- - chebfit, legfit, polyfit, hermfit, hermefit + numpy.polynomial.polynomial.polyfit + numpy.polynomial.legendre.legfit + numpy.polynomial.chebyshev.chebfit + numpy.polynomial.hermite.hermfit + numpy.polynomial.hermite_e.hermefit lagval : Evaluates a Laguerre series. lagvander : pseudo Vandermonde matrix of Laguerre series. lagweight : Laguerre weight function. - linalg.lstsq : Computes a least-squares fit from the matrix. + numpy.linalg.lstsq : Computes a least-squares fit from the matrix. scipy.interpolate.UnivariateSpline : Computes spline fits. Notes @@ -1468,14 +1379,14 @@ def lagfit(x, y, deg, rcond=None, full=False, w=None): Fits using Laguerre series are probably most useful when the data can be approximated by ``sqrt(w(x)) * p(x)``, where `w(x)` is the Laguerre - weight. In that case the weight ``sqrt(w(x[i])`` should be used - together with data values ``y[i]/sqrt(w(x[i])``. The weight function is + weight. In that case the weight ``sqrt(w(x[i]))`` should be used + together with data values ``y[i]/sqrt(w(x[i]))``. The weight function is available as `lagweight`. References ---------- .. [1] Wikipedia, "Curve fitting", - http://en.wikipedia.org/wiki/Curve_fitting + https://en.wikipedia.org/wiki/Curve_fitting Examples -------- @@ -1484,84 +1395,10 @@ def lagfit(x, y, deg, rcond=None, full=False, w=None): >>> err = np.random.randn(len(x))/10 >>> y = lagval(x, [1, 2, 3]) + err >>> lagfit(x, y, 2) - array([ 0.96971004, 2.00193749, 3.00288744]) + array([ 0.96971004, 2.00193749, 3.00288744]) # may vary """ - x = np.asarray(x) + 0.0 - y = np.asarray(y) + 0.0 - deg = np.asarray(deg) - - # check arguments. - if deg.ndim > 1 or deg.dtype.kind not in 'iu' or deg.size == 0: - raise TypeError("deg must be an int or non-empty 1-D array of int") - if deg.min() < 0: - raise ValueError("expected deg >= 0") - if x.ndim != 1: - raise TypeError("expected 1D vector for x") - if x.size == 0: - raise TypeError("expected non-empty vector for x") - if y.ndim < 1 or y.ndim > 2: - raise TypeError("expected 1D or 2D array for y") - if len(x) != len(y): - raise TypeError("expected x and y to have same length") - - if deg.ndim == 0: - lmax = deg - order = lmax + 1 - van = lagvander(x, lmax) - else: - deg = np.sort(deg) - lmax = deg[-1] - order = len(deg) - van = lagvander(x, lmax)[:, deg] - - # set up the least squares matrices in transposed form - lhs = van.T - rhs = y.T - if w is not None: - w = np.asarray(w) + 0.0 - if w.ndim != 1: - raise TypeError("expected 1D vector for w") - if len(x) != len(w): - raise TypeError("expected x and w to have same length") - # apply weights. Don't use inplace operations as they - # can cause problems with NA. - lhs = lhs * w - rhs = rhs * w - - # set rcond - if rcond is None: - rcond = len(x)*np.finfo(x.dtype).eps - - # Determine the norms of the design matrix columns. - if issubclass(lhs.dtype.type, np.complexfloating): - scl = np.sqrt((np.square(lhs.real) + np.square(lhs.imag)).sum(1)) - else: - scl = np.sqrt(np.square(lhs).sum(1)) - scl[scl == 0] = 1 - - # Solve the least squares problem. - c, resids, rank, s = la.lstsq(lhs.T/scl, rhs.T, rcond) - c = (c.T/scl).T - - # Expand c to include non-fitted coefficients which are set to zero - if deg.ndim > 0: - if c.ndim == 2: - cc = np.zeros((lmax+1, c.shape[1]), dtype=c.dtype) - else: - cc = np.zeros(lmax+1, dtype=c.dtype) - cc[deg] = c - c = cc - - # warn on rank reduction - if rank != order and not full: - msg = "The fit may be poorly conditioned" - warnings.warn(msg, pu.RankWarning, stacklevel=2) - - if full: - return c, [resids, rank, s, rcond] - else: - return c + return pu._fit(lagvander, x, y, deg, rcond, full, w) def lagcompanion(c): @@ -1629,7 +1466,11 @@ def lagroots(c): See Also -------- - polyroots, legroots, chebroots, hermroots, hermeroots + numpy.polynomial.polynomial.polyroots + numpy.polynomial.legendre.legroots + numpy.polynomial.chebyshev.chebroots + numpy.polynomial.hermite.hermroots + numpy.polynomial.hermite_e.hermeroots Notes ----- @@ -1651,7 +1492,7 @@ def lagroots(c): >>> coef array([ 2., -8., 12., -6.]) >>> lagroots(coef) - array([ -4.44089210e-16, 1.00000000e+00, 2.00000000e+00]) + array([-4.4408921e-16, 1.0000000e+00, 2.0000000e+00]) """ # c is a trimmed copy @@ -1661,7 +1502,8 @@ def lagroots(c): if len(c) == 2: return np.array([1 + c[0]/c[1]]) - m = lagcompanion(c) + # rotated companion matrix reduces error + m = lagcompanion(c)[::-1,::-1] r = la.eigvals(m) r.sort() return r @@ -1703,9 +1545,9 @@ def laggauss(deg): the right value when integrating 1. """ - ideg = int(deg) - if ideg != deg or ideg < 1: - raise ValueError("deg must be a non-negative integer") + ideg = pu._deprecate_as_int(deg, "deg") + if ideg <= 0: + raise ValueError("deg must be a positive integer") # first approximation of roots. We use the fact that the companion # matrix is symmetric in this case in order to obtain better zeros. @@ -1798,6 +1640,6 @@ class Laguerre(ABCPolyBase): _fromroots = staticmethod(lagfromroots) # Virtual properties - nickname = 'lag' domain = np.array(lagdomain) window = np.array(lagdomain) + basis_name = 'L' diff --git a/numpy/polynomial/laguerre.pyi b/numpy/polynomial/laguerre.pyi new file mode 100644 index 000000000000..2b9ab34e0afa --- /dev/null +++ b/numpy/polynomial/laguerre.pyi @@ -0,0 +1,46 @@ +from typing import Any, List + +from numpy import ndarray, dtype, int_ +from numpy.polynomial._polybase import ABCPolyBase +from numpy.polynomial.polyutils import trimcoef + +__all__: list[str] + +lagtrim = trimcoef + +def poly2lag(pol): ... +def lag2poly(c): ... + +lagdomain: ndarray[Any, dtype[int_]] +lagzero: ndarray[Any, dtype[int_]] +lagone: ndarray[Any, dtype[int_]] +lagx: ndarray[Any, dtype[int_]] + +def lagline(off, scl): ... +def lagfromroots(roots): ... +def lagadd(c1, c2): ... +def lagsub(c1, c2): ... +def lagmulx(c): ... +def lagmul(c1, c2): ... +def lagdiv(c1, c2): ... +def lagpow(c, pow, maxpower=...): ... +def lagder(c, m=..., scl=..., axis=...): ... +def lagint(c, m=..., k = ..., lbnd=..., scl=..., axis=...): ... +def lagval(x, c, tensor=...): ... +def lagval2d(x, y, c): ... +def laggrid2d(x, y, c): ... +def lagval3d(x, y, z, c): ... +def laggrid3d(x, y, z, c): ... +def lagvander(x, deg): ... +def lagvander2d(x, y, deg): ... +def lagvander3d(x, y, z, deg): ... +def lagfit(x, y, deg, rcond=..., full=..., w=...): ... +def lagcompanion(c): ... +def lagroots(c): ... +def laggauss(deg): ... +def lagweight(x): ... + +class Laguerre(ABCPolyBase): + domain: Any + window: Any + basis_name: Any diff --git a/numpy/polynomial/legendre.py b/numpy/polynomial/legendre.py index 0d4a49afc955..2e8052e7c007 100644 --- a/numpy/polynomial/legendre.py +++ b/numpy/polynomial/legendre.py @@ -1,8 +1,7 @@ """ -Legendre Series (:mod: `numpy.polynomial.legendre`) -=================================================== - -.. currentmodule:: numpy.polynomial.polynomial +================================================== +Legendre Series (:mod:`numpy.polynomial.legendre`) +================================================== This module provides a number of objects (mostly functions) useful for dealing with Legendre series, including a `Legendre` class that @@ -10,16 +9,23 @@ on how this module represents and works with such polynomials is in the docstring for its "parent" sub-package, `numpy.polynomial`). +Classes +------- +.. autosummary:: + :toctree: generated/ + + Legendre + Constants --------- .. autosummary:: :toctree: generated/ - legdomain Legendre series default domain, [-1,1]. - legzero Legendre series that evaluates identically to 0. - legone Legendre series that evaluates identically to 1. - legx Legendre series for the identity map, ``f(x) = x``. + legdomain + legzero + legone + legx Arithmetic ---------- @@ -27,17 +33,17 @@ .. autosummary:: :toctree: generated/ - legmulx multiply a Legendre series in P_i(x) by x. - legadd add two Legendre series. - legsub subtract one Legendre series from another. - legmul multiply two Legendre series. - legdiv divide one Legendre series by another. - legpow raise a Legendre series to an positive integer power - legval evaluate a Legendre series at given points. - legval2d evaluate a 2D Legendre series at given points. - legval3d evaluate a 3D Legendre series at given points. - leggrid2d evaluate a 2D Legendre series on a Cartesian product. - leggrid3d evaluate a 3D Legendre series on a Cartesian product. + legadd + legsub + legmulx + legmul + legdiv + legpow + legval + legval2d + legval3d + leggrid2d + leggrid3d Calculus -------- @@ -45,8 +51,8 @@ .. autosummary:: :toctree: generated/ - legder differentiate a Legendre series. - legint integrate a Legendre series. + legder + legint Misc Functions -------------- @@ -54,36 +60,25 @@ .. autosummary:: :toctree: generated/ - legfromroots create a Legendre series with specified roots. - legroots find the roots of a Legendre series. - legvander Vandermonde-like matrix for Legendre polynomials. - legvander2d Vandermonde-like matrix for 2D power series. - legvander3d Vandermonde-like matrix for 3D power series. - leggauss Gauss-Legendre quadrature, points and weights. - legweight Legendre weight function. - legcompanion symmetrized companion matrix in Legendre form. - legfit least-squares fit returning a Legendre series. - legtrim trim leading coefficients from a Legendre series. - legline Legendre series representing given straight line. - leg2poly convert a Legendre series to a polynomial. - poly2leg convert a polynomial to a Legendre series. - -Classes -------- - Legendre A Legendre series class. + legfromroots + legroots + legvander + legvander2d + legvander3d + leggauss + legweight + legcompanion + legfit + legtrim + legline + leg2poly + poly2leg See also -------- -numpy.polynomial.polynomial -numpy.polynomial.chebyshev -numpy.polynomial.laguerre -numpy.polynomial.hermite -numpy.polynomial.hermite_e +numpy.polynomial """ -from __future__ import division, absolute_import, print_function - -import warnings import numpy as np import numpy.linalg as la from numpy.core.multiarray import normalize_axis_index @@ -136,10 +131,10 @@ def poly2leg(pol): >>> from numpy import polynomial as P >>> p = P.Polynomial(np.arange(4)) >>> p - Polynomial([ 0., 1., 2., 3.], domain=[-1, 1], window=[-1, 1]) + Polynomial([0., 1., 2., 3.], domain=[-1, 1], window=[-1, 1]) >>> c = P.Legendre(P.legendre.poly2leg(p.coef)) >>> c - Legendre([ 1. , 3.25, 1. , 0.75], domain=[-1, 1], window=[-1, 1]) + Legendre([ 1. , 3.25, 1. , 0.75], domain=[-1, 1], window=[-1, 1]) # may vary """ [pol] = pu.as_series([pol]) @@ -183,12 +178,13 @@ def leg2poly(c): Examples -------- + >>> from numpy import polynomial as P >>> c = P.Legendre(range(4)) >>> c - Legendre([ 0., 1., 2., 3.], [-1., 1.]) + Legendre([0., 1., 2., 3.], domain=[-1, 1], window=[-1, 1]) >>> p = c.convert(kind=P.Polynomial) >>> p - Polynomial([-1. , -3.5, 3. , 7.5], [-1., 1.]) + Polynomial([-1. , -3.5, 3. , 7.5], domain=[-1., 1.], window=[-1., 1.]) >>> P.leg2poly(range(4)) array([-1. , -3.5, 3. , 7.5]) @@ -247,7 +243,11 @@ def legline(off, scl): See Also -------- - polyline, chebline + numpy.polynomial.polynomial.polyline + numpy.polynomial.chebyshev.chebline + numpy.polynomial.laguerre.lagline + numpy.polynomial.hermite.hermline + numpy.polynomial.hermite_e.hermeline Examples -------- @@ -300,8 +300,11 @@ def legfromroots(roots): See Also -------- - polyfromroots, chebfromroots, lagfromroots, hermfromroots, - hermefromroots. + numpy.polynomial.polynomial.polyfromroots + numpy.polynomial.chebyshev.chebfromroots + numpy.polynomial.laguerre.lagfromroots + numpy.polynomial.hermite.hermfromroots + numpy.polynomial.hermite_e.hermefromroots Examples -------- @@ -310,24 +313,10 @@ def legfromroots(roots): array([ 0. , -0.4, 0. , 0.4]) >>> j = complex(0,1) >>> L.legfromroots((-j,j)) # x^2 + 1 relative to the standard basis - array([ 1.33333333+0.j, 0.00000000+0.j, 0.66666667+0.j]) + array([ 1.33333333+0.j, 0.00000000+0.j, 0.66666667+0.j]) # may vary """ - if len(roots) == 0: - return np.ones(1) - else: - [roots] = pu.as_series([roots], trim=False) - roots.sort() - p = [legline(-r, 1) for r in roots] - n = len(p) - while n > 1: - m, r = divmod(n, 2) - tmp = [legmul(p[i], p[i+m]) for i in range(m)] - if r: - tmp[0] = legmul(tmp[0], p[-1]) - p = tmp - n = m - return p[0] + return pu._fromroots(legline, legmul, roots) def legadd(c1, c2): @@ -351,7 +340,7 @@ def legadd(c1, c2): See Also -------- - legsub, legmul, legdiv, legpow + legsub, legmulx, legmul, legdiv, legpow Notes ----- @@ -366,18 +355,10 @@ def legadd(c1, c2): >>> c1 = (1,2,3) >>> c2 = (3,2,1) >>> L.legadd(c1,c2) - array([ 4., 4., 4.]) + array([4., 4., 4.]) """ - # c1, c2 are trimmed copies - [c1, c2] = pu.as_series([c1, c2]) - if len(c1) > len(c2): - c1[:c2.size] += c2 - ret = c1 - else: - c2[:c1.size] += c1 - ret = c2 - return pu.trimseq(ret) + return pu._add(c1, c2) def legsub(c1, c2): @@ -401,7 +382,7 @@ def legsub(c1, c2): See Also -------- - legadd, legmul, legdiv, legpow + legadd, legmulx, legmul, legdiv, legpow Notes ----- @@ -421,16 +402,7 @@ def legsub(c1, c2): array([ 2., 0., -2.]) """ - # c1, c2 are trimmed copies - [c1, c2] = pu.as_series([c1, c2]) - if len(c1) > len(c2): - c1[:c2.size] -= c2 - ret = c1 - else: - c2 = -c2 - c2[:c1.size] += c1 - ret = c2 - return pu.trimseq(ret) + return pu._sub(c1, c2) def legmulx(c): @@ -451,6 +423,10 @@ def legmulx(c): out : ndarray Array representing the result of the multiplication. + See Also + -------- + legadd, legmul, legdiv, legpow + Notes ----- The multiplication uses the recursion relationship for Legendre @@ -460,6 +436,12 @@ def legmulx(c): xP_i(x) = ((i + 1)*P_{i + 1}(x) + i*P_{i - 1}(x))/(2i + 1) + Examples + -------- + >>> from numpy.polynomial import legendre as L + >>> L.legmulx([1,2,3]) + array([ 0.66666667, 2.2, 1.33333333, 1.8]) # may vary + """ # c is a trimmed copy [c] = pu.as_series([c]) @@ -500,7 +482,7 @@ def legmul(c1, c2): See Also -------- - legadd, legsub, legdiv, legpow + legadd, legsub, legmulx, legdiv, legpow Notes ----- @@ -515,8 +497,8 @@ def legmul(c1, c2): >>> from numpy.polynomial import legendre as L >>> c1 = (1,2,3) >>> c2 = (3,2) - >>> P.legmul(c1,c2) # multiplication requires "reprojection" - array([ 4.33333333, 10.4 , 11.66666667, 3.6 ]) + >>> L.legmul(c1,c2) # multiplication requires "reprojection" + array([ 4.33333333, 10.4 , 11.66666667, 3.6 ]) # may vary """ # s1, s2 are trimmed copies @@ -570,7 +552,7 @@ def legdiv(c1, c2): See Also -------- - legadd, legsub, legmul, legpow + legadd, legsub, legmulx, legmul, legpow Notes ----- @@ -587,32 +569,13 @@ def legdiv(c1, c2): >>> c1 = (1,2,3) >>> c2 = (3,2,1) >>> L.legdiv(c1,c2) # quotient "intuitive," remainder not - (array([ 3.]), array([-8., -4.])) + (array([3.]), array([-8., -4.])) >>> c2 = (0,1,2,3) >>> L.legdiv(c2,c1) # neither "intuitive" - (array([-0.07407407, 1.66666667]), array([-1.03703704, -2.51851852])) + (array([-0.07407407, 1.66666667]), array([-1.03703704, -2.51851852])) # may vary """ - # c1, c2 are trimmed copies - [c1, c2] = pu.as_series([c1, c2]) - if c2[-1] == 0: - raise ZeroDivisionError() - - lc1 = len(c1) - lc2 = len(c2) - if lc1 < lc2: - return c1[:1]*0, c1 - elif lc2 == 1: - return c1/c2[-1], c1[:1]*0 - else: - quo = np.empty(lc1 - lc2 + 1, dtype=c1.dtype) - rem = c1 - for i in range(lc1 - lc2, - 1, -1): - p = legmul([0]*i + [1], c2) - q = rem[-1]/p[-1] - rem = rem[:-1] - q*p[:-1] - quo[i] = q - return quo, pu.trimseq(rem) + return pu._div(legmul, c1, c2) def legpow(c, pow, maxpower=16): @@ -640,30 +603,10 @@ def legpow(c, pow, maxpower=16): See Also -------- - legadd, legsub, legmul, legdiv - - Examples - -------- + legadd, legsub, legmulx, legmul, legdiv """ - # c is a trimmed copy - [c] = pu.as_series([c]) - power = int(pow) - if power != pow or power < 0: - raise ValueError("Power must be a non-negative integer.") - elif maxpower is not None and power > maxpower: - raise ValueError("Power is too large") - elif power == 0: - return np.array([1], dtype=c.dtype) - elif power == 1: - return c - else: - # This can be made more efficient by using powers of two - # in the usual way. - prd = c - for i in range(2, power + 1): - prd = legmul(prd, c) - return prd + return pu._pow(legmul, c, pow, maxpower) def legder(c, m=1, scl=1, axis=0): @@ -719,24 +662,20 @@ def legder(c, m=1, scl=1, axis=0): >>> L.legder(c) array([ 6., 9., 20.]) >>> L.legder(c, 3) - array([ 60.]) + array([60.]) >>> L.legder(c, scl=-1) array([ -6., -9., -20.]) >>> L.legder(c, 2,-1) array([ 9., 60.]) """ - c = np.array(c, ndmin=1, copy=1) + c = np.array(c, ndmin=1, copy=True) if c.dtype.char in '?bBhHiIlLqQpP': c = c.astype(np.double) - cnt, iaxis = [int(t) for t in [m, axis]] - - if cnt != m: - raise ValueError("The order of derivation must be integer") + cnt = pu._deprecate_as_int(m, "the order of derivation") + iaxis = pu._deprecate_as_int(axis, "the axis") if cnt < 0: raise ValueError("The order of derivation must be non-negative") - if iaxis != axis: - raise ValueError("The axis must be integer") iaxis = normalize_axis_index(iaxis, c.ndim) if cnt == 0: @@ -835,27 +774,25 @@ def legint(c, m=1, k=[], lbnd=0, scl=1, axis=0): >>> from numpy.polynomial import legendre as L >>> c = (1,2,3) >>> L.legint(c) - array([ 0.33333333, 0.4 , 0.66666667, 0.6 ]) + array([ 0.33333333, 0.4 , 0.66666667, 0.6 ]) # may vary >>> L.legint(c, 3) - array([ 1.66666667e-02, -1.78571429e-02, 4.76190476e-02, - -1.73472348e-18, 1.90476190e-02, 9.52380952e-03]) + array([ 1.66666667e-02, -1.78571429e-02, 4.76190476e-02, # may vary + -1.73472348e-18, 1.90476190e-02, 9.52380952e-03]) >>> L.legint(c, k=3) - array([ 3.33333333, 0.4 , 0.66666667, 0.6 ]) + array([ 3.33333333, 0.4 , 0.66666667, 0.6 ]) # may vary >>> L.legint(c, lbnd=-2) - array([ 7.33333333, 0.4 , 0.66666667, 0.6 ]) + array([ 7.33333333, 0.4 , 0.66666667, 0.6 ]) # may vary >>> L.legint(c, scl=2) - array([ 0.66666667, 0.8 , 1.33333333, 1.2 ]) + array([ 0.66666667, 0.8 , 1.33333333, 1.2 ]) # may vary """ - c = np.array(c, ndmin=1, copy=1) + c = np.array(c, ndmin=1, copy=True) if c.dtype.char in '?bBhHiIlLqQpP': c = c.astype(np.double) if not np.iterable(k): k = [k] - cnt, iaxis = [int(t) for t in [m, axis]] - - if cnt != m: - raise ValueError("The order of integration must be integer") + cnt = pu._deprecate_as_int(m, "the order of integration") + iaxis = pu._deprecate_as_int(axis, "the axis") if cnt < 0: raise ValueError("The order of integration must be non-negative") if len(k) > cnt: @@ -864,8 +801,6 @@ def legint(c, m=1, k=[], lbnd=0, scl=1, axis=0): raise ValueError("lbnd must be a scalar.") if np.ndim(scl) != 0: raise ValueError("scl must be a scalar.") - if iaxis != axis: - raise ValueError("The axis must be integer") iaxis = normalize_axis_index(iaxis, c.ndim) if cnt == 0: @@ -952,11 +887,8 @@ def legval(x, c, tensor=True): ----- The evaluation uses Clenshaw recursion, aka synthetic division. - Examples - -------- - """ - c = np.array(c, ndmin=1, copy=0) + c = np.array(c, ndmin=1, copy=False) if c.dtype.char in '?bBhHiIlLqQpP': c = c.astype(np.double) if isinstance(x, (tuple, list)): @@ -1028,14 +960,7 @@ def legval2d(x, y, c): .. versionadded:: 1.7.0 """ - try: - x, y = np.array((x, y), copy=0) - except Exception: - raise ValueError('x, y are incompatible') - - c = legval(x, c) - c = legval(y, c, tensor=False) - return c + return pu._valnd(legval, c, x, y) def leggrid2d(x, y, c): @@ -1088,9 +1013,7 @@ def leggrid2d(x, y, c): .. versionadded:: 1.7.0 """ - c = legval(x, c) - c = legval(y, c) - return c + return pu._gridnd(legval, c, x, y) def legval3d(x, y, z, c): @@ -1141,15 +1064,7 @@ def legval3d(x, y, z, c): .. versionadded:: 1.7.0 """ - try: - x, y, z = np.array((x, y, z), copy=0) - except Exception: - raise ValueError('x, y, z are incompatible') - - c = legval(x, c) - c = legval(y, c, tensor=False) - c = legval(z, c, tensor=False) - return c + return pu._valnd(legval, c, x, y, z) def leggrid3d(x, y, z, c): @@ -1205,10 +1120,7 @@ def leggrid3d(x, y, z, c): .. versionadded:: 1.7.0 """ - c = legval(x, c) - c = legval(y, c) - c = legval(z, c) - return c + return pu._gridnd(legval, c, x, y, z) def legvander(x, deg): @@ -1246,13 +1158,11 @@ def legvander(x, deg): the converted `x`. """ - ideg = int(deg) - if ideg != deg: - raise ValueError("deg must be integer") + ideg = pu._deprecate_as_int(deg, "deg") if ideg < 0: raise ValueError("deg must be non-negative") - x = np.array(x, copy=0, ndmin=1) + 0.0 + x = np.array(x, copy=False, ndmin=1) + 0.0 dims = (ideg + 1,) + x.shape dtyp = x.dtype v = np.empty(dims, dtype=dtyp) @@ -1303,12 +1213,12 @@ def legvander2d(x, y, deg): ------- vander2d : ndarray The shape of the returned matrix is ``x.shape + (order,)``, where - :math:`order = (deg[0]+1)*(deg([1]+1)`. The dtype will be the same + :math:`order = (deg[0]+1)*(deg[1]+1)`. The dtype will be the same as the converted `x` and `y`. See Also -------- - legvander, legvander3d. legval2d, legval3d + legvander, legvander3d, legval2d, legval3d Notes ----- @@ -1316,17 +1226,7 @@ def legvander2d(x, y, deg): .. versionadded:: 1.7.0 """ - ideg = [int(d) for d in deg] - is_valid = [id == d and id >= 0 for id, d in zip(ideg, deg)] - if is_valid != [1, 1]: - raise ValueError("degrees must be non-negative integers") - degx, degy = ideg - x, y = np.array((x, y), copy=0) + 0.0 - - vx = legvander(x, degx) - vy = legvander(y, degy) - v = vx[..., None]*vy[..., None,:] - return v.reshape(v.shape[:-2] + (-1,)) + return pu._vander_nd_flat((legvander, legvander), (x, y), deg) def legvander3d(x, y, z, deg): @@ -1367,12 +1267,12 @@ def legvander3d(x, y, z, deg): ------- vander3d : ndarray The shape of the returned matrix is ``x.shape + (order,)``, where - :math:`order = (deg[0]+1)*(deg([1]+1)*(deg[2]+1)`. The dtype will + :math:`order = (deg[0]+1)*(deg[1]+1)*(deg[2]+1)`. The dtype will be the same as the converted `x`, `y`, and `z`. See Also -------- - legvander, legvander3d. legval2d, legval3d + legvander, legvander3d, legval2d, legval3d Notes ----- @@ -1380,18 +1280,7 @@ def legvander3d(x, y, z, deg): .. versionadded:: 1.7.0 """ - ideg = [int(d) for d in deg] - is_valid = [id == d and id >= 0 for id, d in zip(ideg, deg)] - if is_valid != [1, 1, 1]: - raise ValueError("degrees must be non-negative integers") - degx, degy, degz = ideg - x, y, z = np.array((x, y, z), copy=0) + 0.0 - - vx = legvander(x, degx) - vy = legvander(y, degy) - vz = legvander(z, degz) - v = vx[..., None, None]*vy[..., None,:, None]*vz[..., None, None,:] - return v.reshape(v.shape[:-3] + (-1,)) + return pu._vander_nd_flat((legvander, legvander, legvander), (x, y, z), deg) def legfit(x, y, deg, rcond=None, full=False, w=None): @@ -1432,10 +1321,11 @@ def legfit(x, y, deg, rcond=None, full=False, w=None): default) just the coefficients are returned, when True diagnostic information from the singular value decomposition is also returned. w : array_like, shape (`M`,), optional - Weights. If not None, the contribution of each point - ``(x[i],y[i])`` to the fit is weighted by `w[i]`. Ideally the - weights are chosen so that the errors of the products ``w[i]*y[i]`` - all have the same variance. The default value is None. + Weights. If not None, the weight ``w[i]`` applies to the unsquared + residual ``y[i] - y_hat[i]`` at ``x[i]``. Ideally the weights are + chosen so that the errors of the products ``w[i]*y[i]`` all have the + same variance. When using inverse-variance weighting, use + ``w[i] = 1/sigma(y[i])``. The default value is None. .. versionadded:: 1.5.0 @@ -1449,32 +1339,36 @@ def legfit(x, y, deg, rcond=None, full=False, w=None): returned `coef`. [residuals, rank, singular_values, rcond] : list - These values are only returned if `full` = True + These values are only returned if ``full == True`` - resid -- sum of squared residuals of the least squares fit - rank -- the numerical rank of the scaled Vandermonde matrix - sv -- singular values of the scaled Vandermonde matrix - rcond -- value of `rcond`. + - residuals -- sum of squared residuals of the least squares fit + - rank -- the numerical rank of the scaled Vandermonde matrix + - singular_values -- singular values of the scaled Vandermonde matrix + - rcond -- value of `rcond`. - For more details, see `linalg.lstsq`. + For more details, see `numpy.linalg.lstsq`. Warns ----- RankWarning The rank of the coefficient matrix in the least-squares fit is - deficient. The warning is only raised if `full` = False. The + deficient. The warning is only raised if ``full == False``. The warnings can be turned off by >>> import warnings - >>> warnings.simplefilter('ignore', RankWarning) + >>> warnings.simplefilter('ignore', np.RankWarning) See Also -------- - chebfit, polyfit, lagfit, hermfit, hermefit + numpy.polynomial.polynomial.polyfit + numpy.polynomial.chebyshev.chebfit + numpy.polynomial.laguerre.lagfit + numpy.polynomial.hermite.hermfit + numpy.polynomial.hermite_e.hermefit legval : Evaluates a Legendre series. legvander : Vandermonde matrix of Legendre series. legweight : Legendre weight function (= 1). - linalg.lstsq : Computes a least-squares fit from the matrix. + numpy.linalg.lstsq : Computes a least-squares fit from the matrix. scipy.interpolate.UnivariateSpline : Computes spline fits. Notes @@ -1509,87 +1403,13 @@ def legfit(x, y, deg, rcond=None, full=False, w=None): References ---------- .. [1] Wikipedia, "Curve fitting", - http://en.wikipedia.org/wiki/Curve_fitting + https://en.wikipedia.org/wiki/Curve_fitting Examples -------- """ - x = np.asarray(x) + 0.0 - y = np.asarray(y) + 0.0 - deg = np.asarray(deg) - - # check arguments. - if deg.ndim > 1 or deg.dtype.kind not in 'iu' or deg.size == 0: - raise TypeError("deg must be an int or non-empty 1-D array of int") - if deg.min() < 0: - raise ValueError("expected deg >= 0") - if x.ndim != 1: - raise TypeError("expected 1D vector for x") - if x.size == 0: - raise TypeError("expected non-empty vector for x") - if y.ndim < 1 or y.ndim > 2: - raise TypeError("expected 1D or 2D array for y") - if len(x) != len(y): - raise TypeError("expected x and y to have same length") - - if deg.ndim == 0: - lmax = deg - order = lmax + 1 - van = legvander(x, lmax) - else: - deg = np.sort(deg) - lmax = deg[-1] - order = len(deg) - van = legvander(x, lmax)[:, deg] - - # set up the least squares matrices in transposed form - lhs = van.T - rhs = y.T - if w is not None: - w = np.asarray(w) + 0.0 - if w.ndim != 1: - raise TypeError("expected 1D vector for w") - if len(x) != len(w): - raise TypeError("expected x and w to have same length") - # apply weights. Don't use inplace operations as they - # can cause problems with NA. - lhs = lhs * w - rhs = rhs * w - - # set rcond - if rcond is None: - rcond = len(x)*np.finfo(x.dtype).eps - - # Determine the norms of the design matrix columns. - if issubclass(lhs.dtype.type, np.complexfloating): - scl = np.sqrt((np.square(lhs.real) + np.square(lhs.imag)).sum(1)) - else: - scl = np.sqrt(np.square(lhs).sum(1)) - scl[scl == 0] = 1 - - # Solve the least squares problem. - c, resids, rank, s = la.lstsq(lhs.T/scl, rhs.T, rcond) - c = (c.T/scl).T - - # Expand c to include non-fitted coefficients which are set to zero - if deg.ndim > 0: - if c.ndim == 2: - cc = np.zeros((lmax+1, c.shape[1]), dtype=c.dtype) - else: - cc = np.zeros(lmax+1, dtype=c.dtype) - cc[deg] = c - c = cc - - # warn on rank reduction - if rank != order and not full: - msg = "The fit may be poorly conditioned" - warnings.warn(msg, pu.RankWarning, stacklevel=2) - - if full: - return c, [resids, rank, s, rcond] - else: - return c + return pu._fit(legvander, x, y, deg, rcond, full, w) def legcompanion(c): @@ -1657,7 +1477,11 @@ def legroots(c): See Also -------- - polyroots, chebroots, lagroots, hermroots, hermeroots + numpy.polynomial.polynomial.polyroots + numpy.polynomial.chebyshev.chebroots + numpy.polynomial.laguerre.lagroots + numpy.polynomial.hermite.hermroots + numpy.polynomial.hermite_e.hermeroots Notes ----- @@ -1676,7 +1500,7 @@ def legroots(c): -------- >>> import numpy.polynomial.legendre as leg >>> leg.legroots((1, 2, 3, 4)) # 4L_3 + 3L_2 + 2L_1 + 1L_0, all real roots - array([-0.85099543, -0.11407192, 0.51506735]) + array([-0.85099543, -0.11407192, 0.51506735]) # may vary """ # c is a trimmed copy @@ -1686,7 +1510,8 @@ def legroots(c): if len(c) == 2: return np.array([-c[0]/c[1]]) - m = legcompanion(c) + # rotated companion matrix reduces error + m = legcompanion(c)[::-1,::-1] r = la.eigvals(m) r.sort() return r @@ -1728,9 +1553,9 @@ def leggauss(deg): the right value when integrating 1. """ - ideg = int(deg) - if ideg != deg or ideg < 1: - raise ValueError("deg must be a non-negative integer") + ideg = pu._deprecate_as_int(deg, "deg") + if ideg <= 0: + raise ValueError("deg must be a positive integer") # first approximation of roots. We use the fact that the companion # matrix is symmetric in this case in order to obtain better zeros. @@ -1828,6 +1653,6 @@ class Legendre(ABCPolyBase): _fromroots = staticmethod(legfromroots) # Virtual properties - nickname = 'leg' domain = np.array(legdomain) window = np.array(legdomain) + basis_name = 'P' diff --git a/numpy/polynomial/legendre.pyi b/numpy/polynomial/legendre.pyi new file mode 100644 index 000000000000..86aef179304e --- /dev/null +++ b/numpy/polynomial/legendre.pyi @@ -0,0 +1,46 @@ +from typing import Any, List + +from numpy import ndarray, dtype, int_ +from numpy.polynomial._polybase import ABCPolyBase +from numpy.polynomial.polyutils import trimcoef + +__all__: list[str] + +legtrim = trimcoef + +def poly2leg(pol): ... +def leg2poly(c): ... + +legdomain: ndarray[Any, dtype[int_]] +legzero: ndarray[Any, dtype[int_]] +legone: ndarray[Any, dtype[int_]] +legx: ndarray[Any, dtype[int_]] + +def legline(off, scl): ... +def legfromroots(roots): ... +def legadd(c1, c2): ... +def legsub(c1, c2): ... +def legmulx(c): ... +def legmul(c1, c2): ... +def legdiv(c1, c2): ... +def legpow(c, pow, maxpower=...): ... +def legder(c, m=..., scl=..., axis=...): ... +def legint(c, m=..., k = ..., lbnd=..., scl=..., axis=...): ... +def legval(x, c, tensor=...): ... +def legval2d(x, y, c): ... +def leggrid2d(x, y, c): ... +def legval3d(x, y, z, c): ... +def leggrid3d(x, y, z, c): ... +def legvander(x, deg): ... +def legvander2d(x, y, deg): ... +def legvander3d(x, y, z, deg): ... +def legfit(x, y, deg, rcond=..., full=..., w=...): ... +def legcompanion(c): ... +def legroots(c): ... +def leggauss(deg): ... +def legweight(x): ... + +class Legendre(ABCPolyBase): + domain: Any + window: Any + basis_name: Any diff --git a/numpy/polynomial/polynomial.py b/numpy/polynomial/polynomial.py index adbf30234b6e..3c2663b6cc95 100644 --- a/numpy/polynomial/polynomial.py +++ b/numpy/polynomial/polynomial.py @@ -1,5 +1,7 @@ """ -Objects for dealing with polynomials. +================================================= +Power Series (:mod:`numpy.polynomial.polynomial`) +================================================= This module provides a number of objects (mostly functions) useful for dealing with polynomials, including a `Polynomial` class that @@ -7,55 +9,69 @@ on how this module represents and works with polynomial objects is in the docstring for its "parent" sub-package, `numpy.polynomial`). +Classes +------- +.. autosummary:: + :toctree: generated/ + + Polynomial + Constants --------- -- `polydomain` -- Polynomial default domain, [-1,1]. -- `polyzero` -- (Coefficients of the) "zero polynomial." -- `polyone` -- (Coefficients of the) constant polynomial 1. -- `polyx` -- (Coefficients of the) identity map polynomial, ``f(x) = x``. +.. autosummary:: + :toctree: generated/ + + polydomain + polyzero + polyone + polyx Arithmetic ---------- -- `polyadd` -- add two polynomials. -- `polysub` -- subtract one polynomial from another. -- `polymul` -- multiply two polynomials. -- `polydiv` -- divide one polynomial by another. -- `polypow` -- raise a polynomial to an positive integer power -- `polyval` -- evaluate a polynomial at given points. -- `polyval2d` -- evaluate a 2D polynomial at given points. -- `polyval3d` -- evaluate a 3D polynomial at given points. -- `polygrid2d` -- evaluate a 2D polynomial on a Cartesian product. -- `polygrid3d` -- evaluate a 3D polynomial on a Cartesian product. +.. autosummary:: + :toctree: generated/ + + polyadd + polysub + polymulx + polymul + polydiv + polypow + polyval + polyval2d + polyval3d + polygrid2d + polygrid3d Calculus -------- -- `polyder` -- differentiate a polynomial. -- `polyint` -- integrate a polynomial. +.. autosummary:: + :toctree: generated/ + + polyder + polyint Misc Functions -------------- -- `polyfromroots` -- create a polynomial with specified roots. -- `polyroots` -- find the roots of a polynomial. -- `polyvalfromroots` -- evaluate a polynomial at given points from roots. -- `polyvander` -- Vandermonde-like matrix for powers. -- `polyvander2d` -- Vandermonde-like matrix for 2D power series. -- `polyvander3d` -- Vandermonde-like matrix for 3D power series. -- `polycompanion` -- companion matrix in power series form. -- `polyfit` -- least-squares fit returning a polynomial. -- `polytrim` -- trim leading coefficients from a polynomial. -- `polyline` -- polynomial representing given straight line. - -Classes -------- -- `Polynomial` -- polynomial class. +.. autosummary:: + :toctree: generated/ + + polyfromroots + polyroots + polyvalfromroots + polyvander + polyvander2d + polyvander3d + polycompanion + polyfit + polytrim + polyline See Also -------- `numpy.polynomial` """ -from __future__ import division, absolute_import, print_function - __all__ = [ 'polyzero', 'polyone', 'polyx', 'polydomain', 'polyline', 'polyadd', 'polysub', 'polymulx', 'polymul', 'polydiv', 'polypow', 'polyval', @@ -63,7 +79,6 @@ 'polyfit', 'polytrim', 'polyroots', 'Polynomial', 'polyval2d', 'polyval3d', 'polygrid2d', 'polygrid3d', 'polyvander2d', 'polyvander3d'] -import warnings import numpy as np import numpy.linalg as la from numpy.core.multiarray import normalize_axis_index @@ -112,7 +127,11 @@ def polyline(off, scl): See Also -------- - chebline + numpy.polynomial.chebyshev.chebline + numpy.polynomial.legendre.legline + numpy.polynomial.laguerre.lagline + numpy.polynomial.hermite.hermline + numpy.polynomial.hermite_e.hermeline Examples -------- @@ -137,7 +156,7 @@ def polyfromroots(roots): .. math:: p(x) = (x - r_0) * (x - r_1) * ... * (x - r_n), - where the `r_n` are the roots specified in `roots`. If a zero has + where the ``r_n`` are the roots specified in `roots`. If a zero has multiplicity n, then it must appear in `roots` n times. For instance, if 2 is a root of multiplicity three and 3 is a root of multiplicity 2, then `roots` looks something like [2, 2, 2, 3, 3]. The roots can appear @@ -164,17 +183,20 @@ def polyfromroots(roots): See Also -------- - chebfromroots, legfromroots, lagfromroots, hermfromroots - hermefromroots + numpy.polynomial.chebyshev.chebfromroots + numpy.polynomial.legendre.legfromroots + numpy.polynomial.laguerre.lagfromroots + numpy.polynomial.hermite.hermfromroots + numpy.polynomial.hermite_e.hermefromroots Notes ----- The coefficients are determined by multiplying together linear factors - of the form `(x - r_i)`, i.e. + of the form ``(x - r_i)``, i.e. .. math:: p(x) = (x - r_0) (x - r_1) ... (x - r_n) - where ``n == len(roots) - 1``; note that this implies that `1` is always + where ``n == len(roots) - 1``; note that this implies that ``1`` is always returned for :math:`a_n`. Examples @@ -184,24 +206,10 @@ def polyfromroots(roots): array([ 0., -1., 0., 1.]) >>> j = complex(0,1) >>> P.polyfromroots((-j,j)) # complex returned, though values are real - array([ 1.+0.j, 0.+0.j, 1.+0.j]) + array([1.+0.j, 0.+0.j, 1.+0.j]) """ - if len(roots) == 0: - return np.ones(1) - else: - [roots] = pu.as_series([roots], trim=False) - roots.sort() - p = [polyline(-r, 1) for r in roots] - n = len(p) - while n > 1: - m, r = divmod(n, 2) - tmp = [polymul(p[i], p[i+m]) for i in range(m)] - if r: - tmp[0] = polymul(tmp[0], p[-1]) - p = tmp - n = m - return p[0] + return pu._fromroots(polyline, polymul, roots) def polyadd(c1, c2): @@ -224,7 +232,7 @@ def polyadd(c1, c2): See Also -------- - polysub, polymul, polydiv, polypow + polysub, polymulx, polymul, polydiv, polypow Examples -------- @@ -232,20 +240,12 @@ def polyadd(c1, c2): >>> c1 = (1,2,3) >>> c2 = (3,2,1) >>> sum = P.polyadd(c1,c2); sum - array([ 4., 4., 4.]) + array([4., 4., 4.]) >>> P.polyval(2, sum) # 4 + 4(2) + 4(2**2) 28.0 """ - # c1, c2 are trimmed copies - [c1, c2] = pu.as_series([c1, c2]) - if len(c1) > len(c2): - c1[:c2.size] += c2 - ret = c1 - else: - c2[:c1.size] += c1 - ret = c2 - return pu.trimseq(ret) + return pu._add(c1, c2) def polysub(c1, c2): @@ -269,7 +269,7 @@ def polysub(c1, c2): See Also -------- - polyadd, polymul, polydiv, polypow + polyadd, polymulx, polymul, polydiv, polypow Examples -------- @@ -282,16 +282,7 @@ def polysub(c1, c2): array([ 2., 0., -2.]) """ - # c1, c2 are trimmed copies - [c1, c2] = pu.as_series([c1, c2]) - if len(c1) > len(c2): - c1[:c2.size] -= c2 - ret = c1 - else: - c2 = -c2 - c2[:c1.size] += c1 - ret = c2 - return pu.trimseq(ret) + return pu._sub(c1, c2) def polymulx(c): @@ -312,6 +303,10 @@ def polymulx(c): out : ndarray Array representing the result of the multiplication. + See Also + -------- + polyadd, polysub, polymul, polydiv, polypow + Notes ----- @@ -351,7 +346,7 @@ def polymul(c1, c2): See Also -------- - polyadd, polysub, polydiv, polypow + polyadd, polysub, polymulx, polydiv, polypow Examples -------- @@ -388,7 +383,7 @@ def polydiv(c1, c2): See Also -------- - polyadd, polysub, polymul, polypow + polyadd, polysub, polymulx, polymul, polypow Examples -------- @@ -396,9 +391,9 @@ def polydiv(c1, c2): >>> c1 = (1,2,3) >>> c2 = (3,2,1) >>> P.polydiv(c1,c2) - (array([ 3.]), array([-8., -4.])) + (array([3.]), array([-8., -4.])) >>> P.polydiv(c2,c1) - (array([ 0.33333333]), array([ 2.66666667, 1.33333333])) + (array([ 0.33333333]), array([ 2.66666667, 1.33333333])) # may vary """ # c1, c2 are trimmed copies @@ -406,18 +401,19 @@ def polydiv(c1, c2): if c2[-1] == 0: raise ZeroDivisionError() - len1 = len(c1) - len2 = len(c2) - if len2 == 1: - return c1/c2[-1], c1[:1]*0 - elif len1 < len2: + # note: this is more efficient than `pu._div(polymul, c1, c2)` + lc1 = len(c1) + lc2 = len(c2) + if lc1 < lc2: return c1[:1]*0, c1 + elif lc2 == 1: + return c1/c2[-1], c1[:1]*0 else: - dlen = len1 - len2 + dlen = lc1 - lc2 scl = c2[-1] c2 = c2[:-1]/scl i = dlen - j = len1 - 1 + j = lc1 - 1 while i >= 0: c1[i:j] -= c2*c1[j] i -= 1 @@ -450,30 +446,18 @@ def polypow(c, pow, maxpower=None): See Also -------- - polyadd, polysub, polymul, polydiv + polyadd, polysub, polymulx, polymul, polydiv Examples -------- + >>> from numpy.polynomial import polynomial as P + >>> P.polypow([1,2,3], 2) + array([ 1., 4., 10., 12., 9.]) """ - # c is a trimmed copy - [c] = pu.as_series([c]) - power = int(pow) - if power != pow or power < 0: - raise ValueError("Power must be a non-negative integer.") - elif maxpower is not None and power > maxpower: - raise ValueError("Power is too large") - elif power == 0: - return np.array([1], dtype=c.dtype) - elif power == 1: - return c - else: - # This can be made more efficient by using powers of two - # in the usual way. - prd = c - for i in range(2, power + 1): - prd = np.convolve(prd, c) - return prd + # note: this is more efficient than `pu._pow(polymul, c1, c2)`, as it + # avoids calling `as_series` repeatedly + return pu._pow(np.convolve, c, pow, maxpower) def polyder(c, m=1, scl=1, axis=0): @@ -521,26 +505,22 @@ def polyder(c, m=1, scl=1, axis=0): >>> P.polyder(c) # (d/dx)(c) = 2 + 6x + 12x**2 array([ 2., 6., 12.]) >>> P.polyder(c,3) # (d**3/dx**3)(c) = 24 - array([ 24.]) + array([24.]) >>> P.polyder(c,scl=-1) # (d/d(-x))(c) = -2 - 6x - 12x**2 array([ -2., -6., -12.]) >>> P.polyder(c,2,-1) # (d**2/d(-x)**2)(c) = 6 + 24x array([ 6., 24.]) """ - c = np.array(c, ndmin=1, copy=1) + c = np.array(c, ndmin=1, copy=True) if c.dtype.char in '?bBhHiIlLqQpP': # astype fails with NA c = c + 0.0 cdt = c.dtype - cnt, iaxis = [int(t) for t in [m, axis]] - - if cnt != m: - raise ValueError("The order of derivation must be integer") + cnt = pu._deprecate_as_int(m, "the order of derivation") + iaxis = pu._deprecate_as_int(axis, "the axis") if cnt < 0: raise ValueError("The order of derivation must be non-negative") - if iaxis != axis: - raise ValueError("The axis must be integer") iaxis = normalize_axis_index(iaxis, c.ndim) if cnt == 0: @@ -628,29 +608,27 @@ def polyint(c, m=1, k=[], lbnd=0, scl=1, axis=0): >>> from numpy.polynomial import polynomial as P >>> c = (1,2,3) >>> P.polyint(c) # should return array([0, 1, 1, 1]) - array([ 0., 1., 1., 1.]) + array([0., 1., 1., 1.]) >>> P.polyint(c,3) # should return array([0, 0, 0, 1/6, 1/12, 1/20]) - array([ 0. , 0. , 0. , 0.16666667, 0.08333333, - 0.05 ]) + array([ 0. , 0. , 0. , 0.16666667, 0.08333333, # may vary + 0.05 ]) >>> P.polyint(c,k=3) # should return array([3, 1, 1, 1]) - array([ 3., 1., 1., 1.]) + array([3., 1., 1., 1.]) >>> P.polyint(c,lbnd=-2) # should return array([6, 1, 1, 1]) - array([ 6., 1., 1., 1.]) + array([6., 1., 1., 1.]) >>> P.polyint(c,scl=-2) # should return array([0, -2, -2, -2]) array([ 0., -2., -2., -2.]) """ - c = np.array(c, ndmin=1, copy=1) + c = np.array(c, ndmin=1, copy=True) if c.dtype.char in '?bBhHiIlLqQpP': # astype doesn't preserve mask attribute. c = c + 0.0 cdt = c.dtype if not np.iterable(k): k = [k] - cnt, iaxis = [int(t) for t in [m, axis]] - - if cnt != m: - raise ValueError("The order of integration must be integer") + cnt = pu._deprecate_as_int(m, "the order of integration") + iaxis = pu._deprecate_as_int(axis, "the axis") if cnt < 0: raise ValueError("The order of integration must be non-negative") if len(k) > cnt: @@ -659,8 +637,6 @@ def polyint(c, m=1, k=[], lbnd=0, scl=1, axis=0): raise ValueError("lbnd must be a scalar.") if np.ndim(scl) != 0: raise ValueError("scl must be a scalar.") - if iaxis != axis: - raise ValueError("The axis must be integer") iaxis = normalize_axis_index(iaxis, c.ndim) if cnt == 0: @@ -753,20 +729,20 @@ def polyval(x, c, tensor=True): array([[0, 1], [2, 3]]) >>> polyval(a, [1,2,3]) - array([[ 1., 6.], - [ 17., 34.]]) + array([[ 1., 6.], + [17., 34.]]) >>> coef = np.arange(4).reshape(2,2) # multidimensional coefficients >>> coef array([[0, 1], [2, 3]]) >>> polyval([1,2], coef, tensor=True) - array([[ 2., 4.], - [ 4., 7.]]) + array([[2., 4.], + [4., 7.]]) >>> polyval([1,2], coef, tensor=False) - array([ 2., 7.]) + array([2., 7.]) """ - c = np.array(c, ndmin=1, copy=0) + c = np.array(c, ndmin=1, copy=False) if c.dtype.char in '?bBhHiIlLqQpP': # astype fails with NA c = c + 0.0 @@ -843,8 +819,8 @@ def polyvalfromroots(x, r, tensor=True): array([[0, 1], [2, 3]]) >>> polyvalfromroots(a, [-1, 0, 1]) - array([[ -0., 0.], - [ 6., 24.]]) + array([[-0., 0.], + [ 6., 24.]]) >>> r = np.arange(-2, 2).reshape(2,2) # multidimensional coefficients >>> r # each column of r defines one polynomial array([[-2, -1], @@ -856,7 +832,7 @@ def polyvalfromroots(x, r, tensor=True): >>> polyvalfromroots(b, r, tensor=False) array([-0., 0.]) """ - r = np.array(r, ndmin=1, copy=0) + r = np.array(r, ndmin=1, copy=False) if r.dtype.char in '?bBhHiIlLqQpP': r = r.astype(np.double) if isinstance(x, (tuple, list)): @@ -916,14 +892,7 @@ def polyval2d(x, y, c): .. versionadded:: 1.7.0 """ - try: - x, y = np.array((x, y), copy=0) - except Exception: - raise ValueError('x, y are incompatible') - - c = polyval(x, c) - c = polyval(y, c, tensor=False) - return c + return pu._valnd(polyval, c, x, y) def polygrid2d(x, y, c): @@ -976,9 +945,7 @@ def polygrid2d(x, y, c): .. versionadded:: 1.7.0 """ - c = polyval(x, c) - c = polyval(y, c) - return c + return pu._gridnd(polyval, c, x, y) def polyval3d(x, y, z, c): @@ -1029,15 +996,7 @@ def polyval3d(x, y, z, c): .. versionadded:: 1.7.0 """ - try: - x, y, z = np.array((x, y, z), copy=0) - except Exception: - raise ValueError('x, y, z are incompatible') - - c = polyval(x, c) - c = polyval(y, c, tensor=False) - c = polyval(z, c, tensor=False) - return c + return pu._valnd(polyval, c, x, y, z) def polygrid3d(x, y, z, c): @@ -1093,10 +1052,7 @@ def polygrid3d(x, y, z, c): .. versionadded:: 1.7.0 """ - c = polyval(x, c) - c = polyval(y, c) - c = polyval(z, c) - return c + return pu._gridnd(polyval, c, x, y, z) def polyvander(x, deg): @@ -1137,13 +1093,11 @@ def polyvander(x, deg): polyvander2d, polyvander3d """ - ideg = int(deg) - if ideg != deg: - raise ValueError("deg must be integer") + ideg = pu._deprecate_as_int(deg, "deg") if ideg < 0: raise ValueError("deg must be non-negative") - x = np.array(x, copy=0, ndmin=1) + 0.0 + x = np.array(x, copy=False, ndmin=1) + 0.0 dims = (ideg + 1,) + x.shape dtyp = x.dtype v = np.empty(dims, dtype=dtyp) @@ -1197,22 +1151,10 @@ def polyvander2d(x, y, deg): See Also -------- - polyvander, polyvander3d. polyval2d, polyval3d + polyvander, polyvander3d, polyval2d, polyval3d """ - ideg = [int(d) for d in deg] - is_valid = [id == d and id >= 0 for id, d in zip(ideg, deg)] - if is_valid != [1, 1]: - raise ValueError("degrees must be non-negative integers") - degx, degy = ideg - x, y = np.array((x, y), copy=0) + 0.0 - - vx = polyvander(x, degx) - vy = polyvander(y, degy) - v = vx[..., None]*vy[..., None,:] - # einsum bug - #v = np.einsum("...i,...j->...ij", vx, vy) - return v.reshape(v.shape[:-2] + (-1,)) + return pu._vander_nd_flat((polyvander, polyvander), (x, y), deg) def polyvander3d(x, y, z, deg): @@ -1258,7 +1200,7 @@ def polyvander3d(x, y, z, deg): See Also -------- - polyvander, polyvander3d. polyval2d, polyval3d + polyvander, polyvander3d, polyval2d, polyval3d Notes ----- @@ -1266,20 +1208,7 @@ def polyvander3d(x, y, z, deg): .. versionadded:: 1.7.0 """ - ideg = [int(d) for d in deg] - is_valid = [id == d and id >= 0 for id, d in zip(ideg, deg)] - if is_valid != [1, 1, 1]: - raise ValueError("degrees must be non-negative integers") - degx, degy, degz = ideg - x, y, z = np.array((x, y, z), copy=0) + 0.0 - - vx = polyvander(x, degx) - vy = polyvander(y, degy) - vz = polyvander(z, degz) - v = vx[..., None, None]*vy[..., None,:, None]*vz[..., None, None,:] - # einsum bug - #v = np.einsum("...i, ...j, ...k->...ijk", vx, vy, vz) - return v.reshape(v.shape[:-3] + (-1,)) + return pu._vander_nd_flat((polyvander, polyvander, polyvander), (x, y, z), deg) def polyfit(x, y, deg, rcond=None, full=False, w=None): @@ -1323,10 +1252,11 @@ def polyfit(x, y, deg, rcond=None, full=False, w=None): diagnostic information from the singular value decomposition (used to solve the fit's matrix equation) is also returned. w : array_like, shape (`M`,), optional - Weights. If not None, the contribution of each point - ``(x[i],y[i])`` to the fit is weighted by `w[i]`. Ideally the - weights are chosen so that the errors of the products ``w[i]*y[i]`` - all have the same variance. The default value is None. + Weights. If not None, the weight ``w[i]`` applies to the unsquared + residual ``y[i] - y_hat[i]`` at ``x[i]``. Ideally the weights are + chosen so that the errors of the products ``w[i]*y[i]`` all have the + same variance. When using inverse-variance weighting, use + ``w[i] = 1/sigma(y[i])``. The default value is None. .. versionadded:: 1.5.0 @@ -1338,31 +1268,35 @@ def polyfit(x, y, deg, rcond=None, full=False, w=None): fit to the data in `y`'s `k`-th column. [residuals, rank, singular_values, rcond] : list - These values are only returned if `full` = True + These values are only returned if ``full == True`` - resid -- sum of squared residuals of the least squares fit - rank -- the numerical rank of the scaled Vandermonde matrix - sv -- singular values of the scaled Vandermonde matrix - rcond -- value of `rcond`. + - residuals -- sum of squared residuals of the least squares fit + - rank -- the numerical rank of the scaled Vandermonde matrix + - singular_values -- singular values of the scaled Vandermonde matrix + - rcond -- value of `rcond`. - For more details, see `linalg.lstsq`. + For more details, see `numpy.linalg.lstsq`. Raises ------ RankWarning Raised if the matrix in the least-squares fit is rank deficient. - The warning is only raised if `full` == False. The warnings can + The warning is only raised if ``full == False``. The warnings can be turned off by: >>> import warnings - >>> warnings.simplefilter('ignore', RankWarning) + >>> warnings.simplefilter('ignore', np.RankWarning) See Also -------- - chebfit, legfit, lagfit, hermfit, hermefit + numpy.polynomial.chebyshev.chebfit + numpy.polynomial.legendre.legfit + numpy.polynomial.laguerre.lagfit + numpy.polynomial.hermite.hermfit + numpy.polynomial.hermite_e.hermefit polyval : Evaluates a polynomial. polyvander : Vandermonde matrix for powers. - linalg.lstsq : Computes a least-squares fit from the matrix. + numpy.linalg.lstsq : Computes a least-squares fit from the matrix. scipy.interpolate.UnivariateSpline : Computes spline fits. Notes @@ -1370,12 +1304,12 @@ def polyfit(x, y, deg, rcond=None, full=False, w=None): The solution is the coefficients of the polynomial `p` that minimizes the sum of the weighted squared errors - .. math :: E = \\sum_j w_j^2 * |y_j - p(x_j)|^2, + .. math:: E = \\sum_j w_j^2 * |y_j - p(x_j)|^2, where the :math:`w_j` are the weights. This problem is solved by setting up the (typically) over-determined matrix equation: - .. math :: V(x) * c = w * y, + .. math:: V(x) * c = w * y, where `V` is the weighted pseudo Vandermonde matrix of `x`, `c` are the coefficients to be solved for, `w` are the weights, and `y` are the @@ -1402,103 +1336,30 @@ def polyfit(x, y, deg, rcond=None, full=False, w=None): Examples -------- + >>> np.random.seed(123) >>> from numpy.polynomial import polynomial as P >>> x = np.linspace(-1,1,51) # x "data": [-1, -0.96, ..., 0.96, 1] >>> y = x**3 - x + np.random.randn(len(x)) # x^3 - x + N(0,1) "noise" >>> c, stats = P.polyfit(x,y,3,full=True) + >>> np.random.seed(123) >>> c # c[0], c[2] should be approx. 0, c[1] approx. -1, c[3] approx. 1 - array([ 0.01909725, -1.30598256, -0.00577963, 1.02644286]) + array([ 0.01909725, -1.30598256, -0.00577963, 1.02644286]) # may vary >>> stats # note the large SSR, explaining the rather poor results - [array([ 38.06116253]), 4, array([ 1.38446749, 1.32119158, 0.50443316, - 0.28853036]), 1.1324274851176597e-014] + [array([ 38.06116253]), 4, array([ 1.38446749, 1.32119158, 0.50443316, # may vary + 0.28853036]), 1.1324274851176597e-014] Same thing without the added noise >>> y = x**3 - x >>> c, stats = P.polyfit(x,y,3,full=True) >>> c # c[0], c[2] should be "very close to 0", c[1] ~= -1, c[3] ~= 1 - array([ -1.73362882e-17, -1.00000000e+00, -2.67471909e-16, - 1.00000000e+00]) + array([-6.36925336e-18, -1.00000000e+00, -4.08053781e-16, 1.00000000e+00]) >>> stats # note the minuscule SSR - [array([ 7.46346754e-31]), 4, array([ 1.38446749, 1.32119158, - 0.50443316, 0.28853036]), 1.1324274851176597e-014] + [array([ 7.46346754e-31]), 4, array([ 1.38446749, 1.32119158, # may vary + 0.50443316, 0.28853036]), 1.1324274851176597e-014] """ - x = np.asarray(x) + 0.0 - y = np.asarray(y) + 0.0 - deg = np.asarray(deg) - - # check arguments. - if deg.ndim > 1 or deg.dtype.kind not in 'iu' or deg.size == 0: - raise TypeError("deg must be an int or non-empty 1-D array of int") - if deg.min() < 0: - raise ValueError("expected deg >= 0") - if x.ndim != 1: - raise TypeError("expected 1D vector for x") - if x.size == 0: - raise TypeError("expected non-empty vector for x") - if y.ndim < 1 or y.ndim > 2: - raise TypeError("expected 1D or 2D array for y") - if len(x) != len(y): - raise TypeError("expected x and y to have same length") - - if deg.ndim == 0: - lmax = deg - order = lmax + 1 - van = polyvander(x, lmax) - else: - deg = np.sort(deg) - lmax = deg[-1] - order = len(deg) - van = polyvander(x, lmax)[:, deg] - - # set up the least squares matrices in transposed form - lhs = van.T - rhs = y.T - if w is not None: - w = np.asarray(w) + 0.0 - if w.ndim != 1: - raise TypeError("expected 1D vector for w") - if len(x) != len(w): - raise TypeError("expected x and w to have same length") - # apply weights. Don't use inplace operations as they - # can cause problems with NA. - lhs = lhs * w - rhs = rhs * w - - # set rcond - if rcond is None: - rcond = len(x)*np.finfo(x.dtype).eps - - # Determine the norms of the design matrix columns. - if issubclass(lhs.dtype.type, np.complexfloating): - scl = np.sqrt((np.square(lhs.real) + np.square(lhs.imag)).sum(1)) - else: - scl = np.sqrt(np.square(lhs).sum(1)) - scl[scl == 0] = 1 - - # Solve the least squares problem. - c, resids, rank, s = la.lstsq(lhs.T/scl, rhs.T, rcond) - c = (c.T/scl).T - - # Expand c to include non-fitted coefficients which are set to zero - if deg.ndim == 1: - if c.ndim == 2: - cc = np.zeros((lmax + 1, c.shape[1]), dtype=c.dtype) - else: - cc = np.zeros(lmax + 1, dtype=c.dtype) - cc[deg] = c - c = cc - - # warn on rank reduction - if rank != order and not full: - msg = "The fit may be poorly conditioned" - warnings.warn(msg, pu.RankWarning, stacklevel=2) - - if full: - return c, [resids, rank, s, rcond] - else: - return c + return pu._fit(polyvander, x, y, deg, rcond, full, w) def polycompanion(c): @@ -1562,7 +1423,11 @@ def polyroots(c): See Also -------- - chebroots + numpy.polynomial.chebyshev.chebroots + numpy.polynomial.legendre.legroots + numpy.polynomial.laguerre.lagroots + numpy.polynomial.hermite.hermroots + numpy.polynomial.hermite_e.hermeroots Notes ----- @@ -1583,7 +1448,7 @@ def polyroots(c): dtype('float64') >>> j = complex(0,1) >>> poly.polyroots(poly.polyfromroots((-j,0,j))) - array([ 0.00000000e+00+0.j, 0.00000000e+00+1.j, 2.77555756e-17-1.j]) + array([ 0.00000000e+00+0.j, 0.00000000e+00+1.j, 2.77555756e-17-1.j]) # may vary """ # c is a trimmed copy @@ -1593,7 +1458,8 @@ def polyroots(c): if len(c) == 2: return np.array([-c[0]/c[1]]) - m = polycompanion(c) + # rotated companion matrix reduces error + m = polycompanion(c)[::-1,::-1] r = la.eigvals(m) r.sort() return r @@ -1640,6 +1506,25 @@ class Polynomial(ABCPolyBase): _fromroots = staticmethod(polyfromroots) # Virtual properties - nickname = 'poly' domain = np.array(polydomain) window = np.array(polydomain) + basis_name = None + + @classmethod + def _str_term_unicode(cls, i, arg_str): + return f"·{arg_str}{i.translate(cls._superscript_mapping)}" + + @staticmethod + def _str_term_ascii(i, arg_str): + return f" {arg_str}**{i}" + + @staticmethod + def _repr_latex_term(i, arg_str, needs_parens): + if needs_parens: + arg_str = rf"\left({arg_str}\right)" + if i == 0: + return '1' + elif i == 1: + return arg_str + else: + return f"{arg_str}^{{{i}}}" diff --git a/numpy/polynomial/polynomial.pyi b/numpy/polynomial/polynomial.pyi new file mode 100644 index 000000000000..f779300a9c5a --- /dev/null +++ b/numpy/polynomial/polynomial.pyi @@ -0,0 +1,41 @@ +from typing import Any, List + +from numpy import ndarray, dtype, int_ +from numpy.polynomial._polybase import ABCPolyBase +from numpy.polynomial.polyutils import trimcoef + +__all__: list[str] + +polytrim = trimcoef + +polydomain: ndarray[Any, dtype[int_]] +polyzero: ndarray[Any, dtype[int_]] +polyone: ndarray[Any, dtype[int_]] +polyx: ndarray[Any, dtype[int_]] + +def polyline(off, scl): ... +def polyfromroots(roots): ... +def polyadd(c1, c2): ... +def polysub(c1, c2): ... +def polymulx(c): ... +def polymul(c1, c2): ... +def polydiv(c1, c2): ... +def polypow(c, pow, maxpower=...): ... +def polyder(c, m=..., scl=..., axis=...): ... +def polyint(c, m=..., k=..., lbnd=..., scl=..., axis=...): ... +def polyval(x, c, tensor=...): ... +def polyvalfromroots(x, r, tensor=...): ... +def polyval2d(x, y, c): ... +def polygrid2d(x, y, c): ... +def polyval3d(x, y, z, c): ... +def polygrid3d(x, y, z, c): ... +def polyvander(x, deg): ... +def polyvander2d(x, y, deg): ... +def polyvander3d(x, y, z, deg): ... +def polyfit(x, y, deg, rcond=..., full=..., w=...): ... +def polyroots(c): ... + +class Polynomial(ABCPolyBase): + domain: Any + window: Any + basis_name: Any diff --git a/numpy/polynomial/polyutils.py b/numpy/polynomial/polyutils.py index c1ed0c9b3e64..a2bc75a4d331 100644 --- a/numpy/polynomial/polyutils.py +++ b/numpy/polynomial/polyutils.py @@ -4,15 +4,6 @@ This module provides: error and warning objects; a polynomial base class; and some routines used in both the `polynomial` and `chebyshev` modules. -Error objects -------------- - -.. autosummary:: - :toctree: generated/ - - PolyError base class for this sub-package's errors. - PolyDomainError raised when domains are mismatched. - Warning objects --------------- @@ -21,14 +12,6 @@ RankWarning raised in least-squares fit for rank-deficient matrix. -Base class ----------- - -.. autosummary:: - :toctree: generated/ - - PolyBase Obsolete base class for the polynomial classes. Do not use. - Functions --------- @@ -43,13 +26,15 @@ mapparms parameters of the linear map between domains. """ -from __future__ import division, absolute_import, print_function +import operator +import functools +import warnings import numpy as np __all__ = [ - 'RankWarning', 'PolyError', 'PolyDomainError', 'as_series', 'trimseq', - 'trimcoef', 'getdomain', 'mapdomain', 'mapparms', 'PolyBase'] + 'RankWarning', 'as_series', 'trimseq', + 'trimcoef', 'getdomain', 'mapdomain', 'mapparms'] # # Warnings and Exceptions @@ -59,35 +44,6 @@ class RankWarning(UserWarning): """Issued by chebfit when the design matrix is rank deficient.""" pass -class PolyError(Exception): - """Base class for errors in this module.""" - pass - -class PolyDomainError(PolyError): - """Issued by the generic Poly class when two domains don't match. - - This is raised when an binary operation is passed Poly objects with - different domains. - - """ - pass - -# -# Base class for all polynomial types -# - -class PolyBase(object): - """ - Base class for all polynomial types. - - Deprecated in numpy 1.9.0, use the abstract - ABCPolyBase class instead. Note that the latter - requires a number of virtual functions to be - implemented. - - """ - pass - # # Helper functions to convert inputs to 1-D arrays # @@ -156,30 +112,30 @@ def as_series(alist, trim=True): >>> from numpy.polynomial import polyutils as pu >>> a = np.arange(4) >>> pu.as_series(a) - [array([ 0.]), array([ 1.]), array([ 2.]), array([ 3.])] + [array([0.]), array([1.]), array([2.]), array([3.])] >>> b = np.arange(6).reshape((2,3)) >>> pu.as_series(b) - [array([ 0., 1., 2.]), array([ 3., 4., 5.])] + [array([0., 1., 2.]), array([3., 4., 5.])] >>> pu.as_series((1, np.arange(3), np.arange(2, dtype=np.float16))) - [array([ 1.]), array([ 0., 1., 2.]), array([ 0., 1.])] + [array([1.]), array([0., 1., 2.]), array([0., 1.])] >>> pu.as_series([2, [1.1, 0.]]) - [array([ 2.]), array([ 1.1])] + [array([2.]), array([1.1])] >>> pu.as_series([2, [1.1, 0.]], trim=False) - [array([ 2.]), array([ 1.1, 0. ])] + [array([2.]), array([1.1, 0. ])] """ - arrays = [np.array(a, ndmin=1, copy=0) for a in alist] + arrays = [np.array(a, ndmin=1, copy=False) for a in alist] if min([a.size for a in arrays]) == 0: raise ValueError("Coefficient array is empty") - if any([a.ndim != 1 for a in arrays]): + if any(a.ndim != 1 for a in arrays): raise ValueError("Coefficient array is not 1-d") if trim: arrays = [trimseq(a) for a in arrays] - if any([a.dtype == np.dtype(object) for a in arrays]): + if any(a.dtype == np.dtype(object) for a in arrays): ret = [] for a in arrays: if a.dtype != np.dtype(object): @@ -191,9 +147,9 @@ def as_series(alist, trim=True): else: try: dtype = np.common_type(*arrays) - except Exception: - raise ValueError("Coefficient arrays have no common type") - ret = [np.array(a, copy=1, dtype=dtype) for a in arrays] + except Exception as e: + raise ValueError("Coefficient arrays have no common type") from e + ret = [np.array(a, copy=True, dtype=dtype) for a in arrays] return ret @@ -233,12 +189,12 @@ def trimcoef(c, tol=0): -------- >>> from numpy.polynomial import polyutils as pu >>> pu.trimcoef((0,0,3,0,5,0,0)) - array([ 0., 0., 3., 0., 5.]) + array([0., 0., 3., 0., 5.]) >>> pu.trimcoef((0,0,1e-3,0,1e-5,0,0),1e-3) # item == tol is trimmed - array([ 0.]) + array([0.]) >>> i = complex(0,1) # works for complex >>> pu.trimcoef((3e-4,1e-3*(1-i),5e-4,2e-5*(1+i)), 1e-3) - array([ 0.0003+0.j , 0.0010-0.001j]) + array([0.0003+0.j , 0.001 -0.001j]) """ if tol < 0: @@ -332,10 +288,10 @@ def mapparms(old, new): >>> pu.mapparms((-1,1),(-1,1)) (0.0, 1.0) >>> pu.mapparms((1,-1),(-1,1)) - (0.0, -1.0) + (-0.0, -1.0) >>> i = complex(0,1) >>> pu.mapparms((-i,-1),(1,i)) - ((1+1j), (1+0j)) + ((1+1j), (1-0j)) """ oldlen = old[1] - old[0] @@ -374,12 +330,12 @@ def mapdomain(x, old, new): ----- Effectively, this implements: - .. math :: + .. math:: x\\_out = new[0] + m(x - old[0]) where - .. math :: + .. math:: m = \\frac{new[1]-new[0]}{old[1]-old[0]} Examples @@ -390,10 +346,10 @@ def mapdomain(x, old, new): >>> x = np.linspace(-1,1,6); x array([-1. , -0.6, -0.2, 0.2, 0.6, 1. ]) >>> x_out = pu.mapdomain(x, old_domain, new_domain); x_out - array([ 0. , 1.25663706, 2.51327412, 3.76991118, 5.02654825, + array([ 0. , 1.25663706, 2.51327412, 3.76991118, 5.02654825, # may vary 6.28318531]) >>> x - pu.mapdomain(x_out, new_domain, old_domain) - array([ 0., 0., 0., 0., 0., 0.]) + array([0., 0., 0., 0., 0., 0.]) Also works for complex numbers (and thus can be used to map any line in the complex plane to any other line therein). @@ -402,11 +358,393 @@ def mapdomain(x, old, new): >>> old = (-1 - i, 1 + i) >>> new = (-1 + i, 1 - i) >>> z = np.linspace(old[0], old[1], 6); z - array([-1.0-1.j , -0.6-0.6j, -0.2-0.2j, 0.2+0.2j, 0.6+0.6j, 1.0+1.j ]) - >>> new_z = P.mapdomain(z, old, new); new_z - array([-1.0+1.j , -0.6+0.6j, -0.2+0.2j, 0.2-0.2j, 0.6-0.6j, 1.0-1.j ]) + array([-1. -1.j , -0.6-0.6j, -0.2-0.2j, 0.2+0.2j, 0.6+0.6j, 1. +1.j ]) + >>> new_z = pu.mapdomain(z, old, new); new_z + array([-1.0+1.j , -0.6+0.6j, -0.2+0.2j, 0.2-0.2j, 0.6-0.6j, 1.0-1.j ]) # may vary """ x = np.asanyarray(x) off, scl = mapparms(old, new) return off + scl*x + + +def _nth_slice(i, ndim): + sl = [np.newaxis] * ndim + sl[i] = slice(None) + return tuple(sl) + + +def _vander_nd(vander_fs, points, degrees): + r""" + A generalization of the Vandermonde matrix for N dimensions + + The result is built by combining the results of 1d Vandermonde matrices, + + .. math:: + W[i_0, \ldots, i_M, j_0, \ldots, j_N] = \prod_{k=0}^N{V_k(x_k)[i_0, \ldots, i_M, j_k]} + + where + + .. math:: + N &= \texttt{len(points)} = \texttt{len(degrees)} = \texttt{len(vander\_fs)} \\ + M &= \texttt{points[k].ndim} \\ + V_k &= \texttt{vander\_fs[k]} \\ + x_k &= \texttt{points[k]} \\ + 0 \le j_k &\le \texttt{degrees[k]} + + Expanding the one-dimensional :math:`V_k` functions gives: + + .. math:: + W[i_0, \ldots, i_M, j_0, \ldots, j_N] = \prod_{k=0}^N{B_{k, j_k}(x_k[i_0, \ldots, i_M])} + + where :math:`B_{k,m}` is the m'th basis of the polynomial construction used along + dimension :math:`k`. For a regular polynomial, :math:`B_{k, m}(x) = P_m(x) = x^m`. + + Parameters + ---------- + vander_fs : Sequence[function(array_like, int) -> ndarray] + The 1d vander function to use for each axis, such as ``polyvander`` + points : Sequence[array_like] + Arrays of point coordinates, all of the same shape. The dtypes + will be converted to either float64 or complex128 depending on + whether any of the elements are complex. Scalars are converted to + 1-D arrays. + This must be the same length as `vander_fs`. + degrees : Sequence[int] + The maximum degree (inclusive) to use for each axis. + This must be the same length as `vander_fs`. + + Returns + ------- + vander_nd : ndarray + An array of shape ``points[0].shape + tuple(d + 1 for d in degrees)``. + """ + n_dims = len(vander_fs) + if n_dims != len(points): + raise ValueError( + f"Expected {n_dims} dimensions of sample points, got {len(points)}") + if n_dims != len(degrees): + raise ValueError( + f"Expected {n_dims} dimensions of degrees, got {len(degrees)}") + if n_dims == 0: + raise ValueError("Unable to guess a dtype or shape when no points are given") + + # convert to the same shape and type + points = tuple(np.array(tuple(points), copy=False) + 0.0) + + # produce the vandermonde matrix for each dimension, placing the last + # axis of each in an independent trailing axis of the output + vander_arrays = ( + vander_fs[i](points[i], degrees[i])[(...,) + _nth_slice(i, n_dims)] + for i in range(n_dims) + ) + + # we checked this wasn't empty already, so no `initial` needed + return functools.reduce(operator.mul, vander_arrays) + + +def _vander_nd_flat(vander_fs, points, degrees): + """ + Like `_vander_nd`, but flattens the last ``len(degrees)`` axes into a single axis + + Used to implement the public ``<type>vander<n>d`` functions. + """ + v = _vander_nd(vander_fs, points, degrees) + return v.reshape(v.shape[:-len(degrees)] + (-1,)) + + +def _fromroots(line_f, mul_f, roots): + """ + Helper function used to implement the ``<type>fromroots`` functions. + + Parameters + ---------- + line_f : function(float, float) -> ndarray + The ``<type>line`` function, such as ``polyline`` + mul_f : function(array_like, array_like) -> ndarray + The ``<type>mul`` function, such as ``polymul`` + roots + See the ``<type>fromroots`` functions for more detail + """ + if len(roots) == 0: + return np.ones(1) + else: + [roots] = as_series([roots], trim=False) + roots.sort() + p = [line_f(-r, 1) for r in roots] + n = len(p) + while n > 1: + m, r = divmod(n, 2) + tmp = [mul_f(p[i], p[i+m]) for i in range(m)] + if r: + tmp[0] = mul_f(tmp[0], p[-1]) + p = tmp + n = m + return p[0] + + +def _valnd(val_f, c, *args): + """ + Helper function used to implement the ``<type>val<n>d`` functions. + + Parameters + ---------- + val_f : function(array_like, array_like, tensor: bool) -> array_like + The ``<type>val`` function, such as ``polyval`` + c, args + See the ``<type>val<n>d`` functions for more detail + """ + args = [np.asanyarray(a) for a in args] + shape0 = args[0].shape + if not all((a.shape == shape0 for a in args[1:])): + if len(args) == 3: + raise ValueError('x, y, z are incompatible') + elif len(args) == 2: + raise ValueError('x, y are incompatible') + else: + raise ValueError('ordinates are incompatible') + it = iter(args) + x0 = next(it) + + # use tensor on only the first + c = val_f(x0, c) + for xi in it: + c = val_f(xi, c, tensor=False) + return c + + +def _gridnd(val_f, c, *args): + """ + Helper function used to implement the ``<type>grid<n>d`` functions. + + Parameters + ---------- + val_f : function(array_like, array_like, tensor: bool) -> array_like + The ``<type>val`` function, such as ``polyval`` + c, args + See the ``<type>grid<n>d`` functions for more detail + """ + for xi in args: + c = val_f(xi, c) + return c + + +def _div(mul_f, c1, c2): + """ + Helper function used to implement the ``<type>div`` functions. + + Implementation uses repeated subtraction of c2 multiplied by the nth basis. + For some polynomial types, a more efficient approach may be possible. + + Parameters + ---------- + mul_f : function(array_like, array_like) -> array_like + The ``<type>mul`` function, such as ``polymul`` + c1, c2 + See the ``<type>div`` functions for more detail + """ + # c1, c2 are trimmed copies + [c1, c2] = as_series([c1, c2]) + if c2[-1] == 0: + raise ZeroDivisionError() + + lc1 = len(c1) + lc2 = len(c2) + if lc1 < lc2: + return c1[:1]*0, c1 + elif lc2 == 1: + return c1/c2[-1], c1[:1]*0 + else: + quo = np.empty(lc1 - lc2 + 1, dtype=c1.dtype) + rem = c1 + for i in range(lc1 - lc2, - 1, -1): + p = mul_f([0]*i + [1], c2) + q = rem[-1]/p[-1] + rem = rem[:-1] - q*p[:-1] + quo[i] = q + return quo, trimseq(rem) + + +def _add(c1, c2): + """ Helper function used to implement the ``<type>add`` functions. """ + # c1, c2 are trimmed copies + [c1, c2] = as_series([c1, c2]) + if len(c1) > len(c2): + c1[:c2.size] += c2 + ret = c1 + else: + c2[:c1.size] += c1 + ret = c2 + return trimseq(ret) + + +def _sub(c1, c2): + """ Helper function used to implement the ``<type>sub`` functions. """ + # c1, c2 are trimmed copies + [c1, c2] = as_series([c1, c2]) + if len(c1) > len(c2): + c1[:c2.size] -= c2 + ret = c1 + else: + c2 = -c2 + c2[:c1.size] += c1 + ret = c2 + return trimseq(ret) + + +def _fit(vander_f, x, y, deg, rcond=None, full=False, w=None): + """ + Helper function used to implement the ``<type>fit`` functions. + + Parameters + ---------- + vander_f : function(array_like, int) -> ndarray + The 1d vander function, such as ``polyvander`` + c1, c2 + See the ``<type>fit`` functions for more detail + """ + x = np.asarray(x) + 0.0 + y = np.asarray(y) + 0.0 + deg = np.asarray(deg) + + # check arguments. + if deg.ndim > 1 or deg.dtype.kind not in 'iu' or deg.size == 0: + raise TypeError("deg must be an int or non-empty 1-D array of int") + if deg.min() < 0: + raise ValueError("expected deg >= 0") + if x.ndim != 1: + raise TypeError("expected 1D vector for x") + if x.size == 0: + raise TypeError("expected non-empty vector for x") + if y.ndim < 1 or y.ndim > 2: + raise TypeError("expected 1D or 2D array for y") + if len(x) != len(y): + raise TypeError("expected x and y to have same length") + + if deg.ndim == 0: + lmax = deg + order = lmax + 1 + van = vander_f(x, lmax) + else: + deg = np.sort(deg) + lmax = deg[-1] + order = len(deg) + van = vander_f(x, lmax)[:, deg] + + # set up the least squares matrices in transposed form + lhs = van.T + rhs = y.T + if w is not None: + w = np.asarray(w) + 0.0 + if w.ndim != 1: + raise TypeError("expected 1D vector for w") + if len(x) != len(w): + raise TypeError("expected x and w to have same length") + # apply weights. Don't use inplace operations as they + # can cause problems with NA. + lhs = lhs * w + rhs = rhs * w + + # set rcond + if rcond is None: + rcond = len(x)*np.finfo(x.dtype).eps + + # Determine the norms of the design matrix columns. + if issubclass(lhs.dtype.type, np.complexfloating): + scl = np.sqrt((np.square(lhs.real) + np.square(lhs.imag)).sum(1)) + else: + scl = np.sqrt(np.square(lhs).sum(1)) + scl[scl == 0] = 1 + + # Solve the least squares problem. + c, resids, rank, s = np.linalg.lstsq(lhs.T/scl, rhs.T, rcond) + c = (c.T/scl).T + + # Expand c to include non-fitted coefficients which are set to zero + if deg.ndim > 0: + if c.ndim == 2: + cc = np.zeros((lmax+1, c.shape[1]), dtype=c.dtype) + else: + cc = np.zeros(lmax+1, dtype=c.dtype) + cc[deg] = c + c = cc + + # warn on rank reduction + if rank != order and not full: + msg = "The fit may be poorly conditioned" + warnings.warn(msg, RankWarning, stacklevel=2) + + if full: + return c, [resids, rank, s, rcond] + else: + return c + + +def _pow(mul_f, c, pow, maxpower): + """ + Helper function used to implement the ``<type>pow`` functions. + + Parameters + ---------- + mul_f : function(array_like, array_like) -> ndarray + The ``<type>mul`` function, such as ``polymul`` + c : array_like + 1-D array of array of series coefficients + pow, maxpower + See the ``<type>pow`` functions for more detail + """ + # c is a trimmed copy + [c] = as_series([c]) + power = int(pow) + if power != pow or power < 0: + raise ValueError("Power must be a non-negative integer.") + elif maxpower is not None and power > maxpower: + raise ValueError("Power is too large") + elif power == 0: + return np.array([1], dtype=c.dtype) + elif power == 1: + return c + else: + # This can be made more efficient by using powers of two + # in the usual way. + prd = c + for i in range(2, power + 1): + prd = mul_f(prd, c) + return prd + + +def _deprecate_as_int(x, desc): + """ + Like `operator.index`, but emits a deprecation warning when passed a float + + Parameters + ---------- + x : int-like, or float with integral value + Value to interpret as an integer + desc : str + description to include in any error message + + Raises + ------ + TypeError : if x is a non-integral float or non-numeric + DeprecationWarning : if x is an integral float + """ + try: + return operator.index(x) + except TypeError as e: + # Numpy 1.17.0, 2019-03-11 + try: + ix = int(x) + except TypeError: + pass + else: + if ix == x: + warnings.warn( + f"In future, this will raise TypeError, as {desc} will " + "need to be an integer not just an integral float.", + DeprecationWarning, + stacklevel=3 + ) + return ix + + raise TypeError(f"{desc} must be an integer") from e diff --git a/numpy/polynomial/polyutils.pyi b/numpy/polynomial/polyutils.pyi new file mode 100644 index 000000000000..52c9cfc4a607 --- /dev/null +++ b/numpy/polynomial/polyutils.pyi @@ -0,0 +1,12 @@ +from typing import List + +__all__: List[str] + +class RankWarning(UserWarning): ... + +def trimseq(seq): ... +def as_series(alist, trim=...): ... +def trimcoef(c, tol=...): ... +def getdomain(x): ... +def mapparms(old, new): ... +def mapdomain(x, old, new): ... diff --git a/numpy/polynomial/setup.py b/numpy/polynomial/setup.py index cb59ee1e56d9..b58e867a133f 100644 --- a/numpy/polynomial/setup.py +++ b/numpy/polynomial/setup.py @@ -1,9 +1,8 @@ -from __future__ import division, print_function - def configuration(parent_package='',top_path=None): from numpy.distutils.misc_util import Configuration config = Configuration('polynomial', parent_package, top_path) - config.add_data_dir('tests') + config.add_subpackage('tests') + config.add_data_files('*.pyi') return config if __name__ == '__main__': diff --git a/numpy/polynomial/tests/test_chebyshev.py b/numpy/polynomial/tests/test_chebyshev.py index 439dfa08daac..2f54bebfdb27 100644 --- a/numpy/polynomial/tests/test_chebyshev.py +++ b/numpy/polynomial/tests/test_chebyshev.py @@ -1,7 +1,7 @@ """Tests for chebyshev module. """ -from __future__ import division, absolute_import, print_function +from functools import reduce import numpy as np import numpy.polynomial.chebyshev as cheb @@ -28,7 +28,7 @@ def trim(x): Tlist = [T0, T1, T2, T3, T4, T5, T6, T7, T8, T9] -class TestPrivate(object): +class TestPrivate: def test__cseries_to_zseries(self): for i in range(5): @@ -45,7 +45,7 @@ def test__zseries_to_cseries(self): assert_equal(res, tgt) -class TestConstants(object): +class TestConstants: def test_chebdomain(self): assert_equal(cheb.chebdomain, [-1, 1]) @@ -60,12 +60,12 @@ def test_chebx(self): assert_equal(cheb.chebx, [0, 1]) -class TestArithmetic(object): +class TestArithmetic: def test_chebadd(self): for i in range(5): for j in range(5): - msg = "At i=%d, j=%d" % (i, j) + msg = f"At i={i}, j={j}" tgt = np.zeros(max(i, j) + 1) tgt[i] += 1 tgt[j] += 1 @@ -75,7 +75,7 @@ def test_chebadd(self): def test_chebsub(self): for i in range(5): for j in range(5): - msg = "At i=%d, j=%d" % (i, j) + msg = f"At i={i}, j={j}" tgt = np.zeros(max(i, j) + 1) tgt[i] += 1 tgt[j] -= 1 @@ -93,7 +93,7 @@ def test_chebmulx(self): def test_chebmul(self): for i in range(5): for j in range(5): - msg = "At i=%d, j=%d" % (i, j) + msg = f"At i={i}, j={j}" tgt = np.zeros(i + j + 1) tgt[i + j] += .5 tgt[abs(i - j)] += .5 @@ -103,7 +103,7 @@ def test_chebmul(self): def test_chebdiv(self): for i in range(5): for j in range(5): - msg = "At i=%d, j=%d" % (i, j) + msg = f"At i={i}, j={j}" ci = [0]*i + [1] cj = [0]*j + [1] tgt = cheb.chebadd(ci, cj) @@ -111,8 +111,17 @@ def test_chebdiv(self): res = cheb.chebadd(cheb.chebmul(quo, ci), rem) assert_equal(trim(res), trim(tgt), err_msg=msg) + def test_chebpow(self): + for i in range(5): + for j in range(5): + msg = f"At i={i}, j={j}" + c = np.arange(i + 1) + tgt = reduce(cheb.chebmul, [c]*j, np.array([1])) + res = cheb.chebpow(c, j) + assert_equal(trim(res), trim(tgt), err_msg=msg) + -class TestEvaluation(object): +class TestEvaluation: # coefficients of 1 + 2*x + 3*x**2 c1d = np.array([2.5, 2., 1.5]) c2d = np.einsum('i,j->ij', c1d, c1d) @@ -130,7 +139,7 @@ def test_chebval(self): x = np.linspace(-1, 1) y = [polyval(x, c) for c in Tlist] for i in range(10): - msg = "At i=%d" % i + msg = f"At i={i}" tgt = y[i] res = cheb.chebval(x, [0]*i + [1]) assert_almost_equal(res, tgt, err_msg=msg) @@ -206,16 +215,16 @@ def test_chebgrid3d(self): assert_(res.shape == (2, 3)*3) -class TestIntegral(object): +class TestIntegral: def test_chebint(self): # check exceptions - assert_raises(ValueError, cheb.chebint, [0], .5) + assert_raises(TypeError, cheb.chebint, [0], .5) assert_raises(ValueError, cheb.chebint, [0], -1) assert_raises(ValueError, cheb.chebint, [0], 1, [0, 0]) assert_raises(ValueError, cheb.chebint, [0], lbnd=[0]) assert_raises(ValueError, cheb.chebint, [0], scl=[0]) - assert_raises(ValueError, cheb.chebint, [0], axis=.5) + assert_raises(TypeError, cheb.chebint, [0], axis=.5) # test integration of zero polynomial for i in range(2, 5): @@ -308,11 +317,11 @@ def test_chebint_axis(self): assert_almost_equal(res, tgt) -class TestDerivative(object): +class TestDerivative: def test_chebder(self): # check exceptions - assert_raises(ValueError, cheb.chebder, [0], .5) + assert_raises(TypeError, cheb.chebder, [0], .5) assert_raises(ValueError, cheb.chebder, [0], -1) # check that zeroth derivative does nothing @@ -348,7 +357,7 @@ def test_chebder_axis(self): assert_almost_equal(res, tgt) -class TestVander(object): +class TestVander: # some random values in [-1, 1) x = np.random.random((3, 5))*2 - 1 @@ -396,7 +405,7 @@ def test_chebvander3d(self): assert_(van.shape == (1, 5, 24)) -class TestFitting(object): +class TestFitting: def test_chebfit(self): def f(x): @@ -473,7 +482,7 @@ def f2(x): assert_almost_equal(coef1, coef2) -class TestInterpolate(object): +class TestInterpolate: def f(self, x): return x * (x - 1) * (x - 2) @@ -498,7 +507,7 @@ def powx(x, p): assert_almost_equal(cheb.chebval(x, c), powx(x, p), decimal=12) -class TestCompanion(object): +class TestCompanion: def test_raises(self): assert_raises(ValueError, cheb.chebcompanion, []) @@ -513,7 +522,7 @@ def test_linear_root(self): assert_(cheb.chebcompanion([1, 2])[0, 0] == -.5) -class TestGauss(object): +class TestGauss: def test_100(self): x, w = cheb.chebgauss(100) @@ -532,7 +541,7 @@ def test_100(self): assert_almost_equal(w.sum(), tgt) -class TestMisc(object): +class TestMisc: def test_chebfromroots(self): res = cheb.chebfromroots([]) diff --git a/numpy/polynomial/tests/test_classes.py b/numpy/polynomial/tests/test_classes.py index 738741668e4f..6322062f29ec 100644 --- a/numpy/polynomial/tests/test_classes.py +++ b/numpy/polynomial/tests/test_classes.py @@ -3,8 +3,6 @@ This tests the convert and cast methods of all the polynomial classes. """ -from __future__ import division, absolute_import, print_function - import operator as op from numbers import Number @@ -15,8 +13,7 @@ from numpy.testing import ( assert_almost_equal, assert_raises, assert_equal, assert_, ) -from numpy.compat import long - +from numpy.polynomial.polyutils import RankWarning # # fixtures @@ -44,7 +41,7 @@ def assert_poly_almost_equal(p1, p2, msg=""): assert_(np.all(p1.window == p2.window)) assert_almost_equal(p1.coef, p2.coef) except AssertionError: - msg = "Result: %s\nTarget: %s", (p1, p2) + msg = f"Result: {p1}\nTarget: {p2}" raise AssertionError(msg) @@ -133,6 +130,17 @@ def test_fromroots(Poly): assert_almost_equal(p2.coef[-1], 1) +def test_bad_conditioned_fit(Poly): + + x = [0., 0., 1.] + y = [1., 2., 3.] + + # check RankWarning is raised + with pytest.warns(RankWarning) as record: + Poly.fit(x, y, 2) + assert record[0].message.args[0] == "The fit may be poorly conditioned" + + def test_fit(Poly): def f(x): @@ -306,7 +314,7 @@ def test_truediv(Poly): s = stype(5) assert_poly_almost_equal(op.truediv(p2, s), p1) assert_raises(TypeError, op.truediv, s, p2) - for stype in (int, long, float): + for stype in (int, float): s = stype(5) assert_poly_almost_equal(op.truediv(p2, s), p1) assert_raises(TypeError, op.truediv, s, p2) @@ -567,7 +575,7 @@ def test_ufunc_override(Poly): # -class TestInterpolate(object): +class TestInterpolate: def f(self, x): return x * (x - 1) * (x - 2) @@ -589,4 +597,4 @@ def powx(x, p): for deg in range(0, 10): for t in range(0, deg + 1): p = Chebyshev.interpolate(powx, deg, domain=[0, 2], args=(t,)) - assert_almost_equal(p(x), powx(x, t), decimal=12) + assert_almost_equal(p(x), powx(x, t), decimal=11) diff --git a/numpy/polynomial/tests/test_hermite.py b/numpy/polynomial/tests/test_hermite.py index 18c26af8f3d9..53ee0844e3c5 100644 --- a/numpy/polynomial/tests/test_hermite.py +++ b/numpy/polynomial/tests/test_hermite.py @@ -1,7 +1,7 @@ """Tests for hermite module. """ -from __future__ import division, absolute_import, print_function +from functools import reduce import numpy as np import numpy.polynomial.hermite as herm @@ -28,7 +28,7 @@ def trim(x): return herm.hermtrim(x, tol=1e-6) -class TestConstants(object): +class TestConstants: def test_hermdomain(self): assert_equal(herm.hermdomain, [-1, 1]) @@ -43,13 +43,13 @@ def test_hermx(self): assert_equal(herm.hermx, [0, .5]) -class TestArithmetic(object): +class TestArithmetic: x = np.linspace(-3, 3, 100) def test_hermadd(self): for i in range(5): for j in range(5): - msg = "At i=%d, j=%d" % (i, j) + msg = f"At i={i}, j={j}" tgt = np.zeros(max(i, j) + 1) tgt[i] += 1 tgt[j] += 1 @@ -59,7 +59,7 @@ def test_hermadd(self): def test_hermsub(self): for i in range(5): for j in range(5): - msg = "At i=%d, j=%d" % (i, j) + msg = f"At i={i}, j={j}" tgt = np.zeros(max(i, j) + 1) tgt[i] += 1 tgt[j] -= 1 @@ -80,7 +80,7 @@ def test_hermmul(self): pol1 = [0]*i + [1] val1 = herm.hermval(self.x, pol1) for j in range(5): - msg = "At i=%d, j=%d" % (i, j) + msg = f"At i={i}, j={j}" pol2 = [0]*j + [1] val2 = herm.hermval(self.x, pol2) pol3 = herm.hermmul(pol1, pol2) @@ -91,7 +91,7 @@ def test_hermmul(self): def test_hermdiv(self): for i in range(5): for j in range(5): - msg = "At i=%d, j=%d" % (i, j) + msg = f"At i={i}, j={j}" ci = [0]*i + [1] cj = [0]*j + [1] tgt = herm.hermadd(ci, cj) @@ -99,8 +99,17 @@ def test_hermdiv(self): res = herm.hermadd(herm.hermmul(quo, ci), rem) assert_equal(trim(res), trim(tgt), err_msg=msg) + def test_hermpow(self): + for i in range(5): + for j in range(5): + msg = f"At i={i}, j={j}" + c = np.arange(i + 1) + tgt = reduce(herm.hermmul, [c]*j, np.array([1])) + res = herm.hermpow(c, j) + assert_equal(trim(res), trim(tgt), err_msg=msg) + -class TestEvaluation(object): +class TestEvaluation: # coefficients of 1 + 2*x + 3*x**2 c1d = np.array([2.5, 1., .75]) c2d = np.einsum('i,j->ij', c1d, c1d) @@ -118,7 +127,7 @@ def test_hermval(self): x = np.linspace(-1, 1) y = [polyval(x, c) for c in Hlist] for i in range(10): - msg = "At i=%d" % i + msg = f"At i={i}" tgt = y[i] res = herm.hermval(x, [0]*i + [1]) assert_almost_equal(res, tgt, err_msg=msg) @@ -194,16 +203,16 @@ def test_hermgrid3d(self): assert_(res.shape == (2, 3)*3) -class TestIntegral(object): +class TestIntegral: def test_hermint(self): # check exceptions - assert_raises(ValueError, herm.hermint, [0], .5) + assert_raises(TypeError, herm.hermint, [0], .5) assert_raises(ValueError, herm.hermint, [0], -1) assert_raises(ValueError, herm.hermint, [0], 1, [0, 0]) assert_raises(ValueError, herm.hermint, [0], lbnd=[0]) assert_raises(ValueError, herm.hermint, [0], scl=[0]) - assert_raises(ValueError, herm.hermint, [0], axis=.5) + assert_raises(TypeError, herm.hermint, [0], axis=.5) # test integration of zero polynomial for i in range(2, 5): @@ -296,11 +305,11 @@ def test_hermint_axis(self): assert_almost_equal(res, tgt) -class TestDerivative(object): +class TestDerivative: def test_hermder(self): # check exceptions - assert_raises(ValueError, herm.hermder, [0], .5) + assert_raises(TypeError, herm.hermder, [0], .5) assert_raises(ValueError, herm.hermder, [0], -1) # check that zeroth derivative does nothing @@ -336,7 +345,7 @@ def test_hermder_axis(self): assert_almost_equal(res, tgt) -class TestVander(object): +class TestVander: # some random values in [-1, 1) x = np.random.random((3, 5))*2 - 1 @@ -384,7 +393,7 @@ def test_hermvander3d(self): assert_(van.shape == (1, 5, 24)) -class TestFitting(object): +class TestFitting: def test_hermfit(self): def f(x): @@ -461,7 +470,7 @@ def f2(x): assert_almost_equal(coef1, coef2) -class TestCompanion(object): +class TestCompanion: def test_raises(self): assert_raises(ValueError, herm.hermcompanion, []) @@ -476,7 +485,7 @@ def test_linear_root(self): assert_(herm.hermcompanion([1, 2])[0, 0] == -.25) -class TestGauss(object): +class TestGauss: def test_100(self): x, w = herm.hermgauss(100) @@ -495,7 +504,7 @@ def test_100(self): assert_almost_equal(w.sum(), tgt) -class TestMisc(object): +class TestMisc: def test_hermfromroots(self): res = herm.hermfromroots([]) diff --git a/numpy/polynomial/tests/test_hermite_e.py b/numpy/polynomial/tests/test_hermite_e.py index 58d74dae9d5e..2d262a330622 100644 --- a/numpy/polynomial/tests/test_hermite_e.py +++ b/numpy/polynomial/tests/test_hermite_e.py @@ -1,7 +1,7 @@ """Tests for hermite_e module. """ -from __future__ import division, absolute_import, print_function +from functools import reduce import numpy as np import numpy.polynomial.hermite_e as herme @@ -28,7 +28,7 @@ def trim(x): return herme.hermetrim(x, tol=1e-6) -class TestConstants(object): +class TestConstants: def test_hermedomain(self): assert_equal(herme.hermedomain, [-1, 1]) @@ -43,13 +43,13 @@ def test_hermex(self): assert_equal(herme.hermex, [0, 1]) -class TestArithmetic(object): +class TestArithmetic: x = np.linspace(-3, 3, 100) def test_hermeadd(self): for i in range(5): for j in range(5): - msg = "At i=%d, j=%d" % (i, j) + msg = f"At i={i}, j={j}" tgt = np.zeros(max(i, j) + 1) tgt[i] += 1 tgt[j] += 1 @@ -59,7 +59,7 @@ def test_hermeadd(self): def test_hermesub(self): for i in range(5): for j in range(5): - msg = "At i=%d, j=%d" % (i, j) + msg = f"At i={i}, j={j}" tgt = np.zeros(max(i, j) + 1) tgt[i] += 1 tgt[j] -= 1 @@ -80,7 +80,7 @@ def test_hermemul(self): pol1 = [0]*i + [1] val1 = herme.hermeval(self.x, pol1) for j in range(5): - msg = "At i=%d, j=%d" % (i, j) + msg = f"At i={i}, j={j}" pol2 = [0]*j + [1] val2 = herme.hermeval(self.x, pol2) pol3 = herme.hermemul(pol1, pol2) @@ -91,7 +91,7 @@ def test_hermemul(self): def test_hermediv(self): for i in range(5): for j in range(5): - msg = "At i=%d, j=%d" % (i, j) + msg = f"At i={i}, j={j}" ci = [0]*i + [1] cj = [0]*j + [1] tgt = herme.hermeadd(ci, cj) @@ -99,8 +99,17 @@ def test_hermediv(self): res = herme.hermeadd(herme.hermemul(quo, ci), rem) assert_equal(trim(res), trim(tgt), err_msg=msg) + def test_hermepow(self): + for i in range(5): + for j in range(5): + msg = f"At i={i}, j={j}" + c = np.arange(i + 1) + tgt = reduce(herme.hermemul, [c]*j, np.array([1])) + res = herme.hermepow(c, j) + assert_equal(trim(res), trim(tgt), err_msg=msg) + -class TestEvaluation(object): +class TestEvaluation: # coefficients of 1 + 2*x + 3*x**2 c1d = np.array([4., 2., 3.]) c2d = np.einsum('i,j->ij', c1d, c1d) @@ -118,7 +127,7 @@ def test_hermeval(self): x = np.linspace(-1, 1) y = [polyval(x, c) for c in Helist] for i in range(10): - msg = "At i=%d" % i + msg = f"At i={i}" tgt = y[i] res = herme.hermeval(x, [0]*i + [1]) assert_almost_equal(res, tgt, err_msg=msg) @@ -194,16 +203,16 @@ def test_hermegrid3d(self): assert_(res.shape == (2, 3)*3) -class TestIntegral(object): +class TestIntegral: def test_hermeint(self): # check exceptions - assert_raises(ValueError, herme.hermeint, [0], .5) + assert_raises(TypeError, herme.hermeint, [0], .5) assert_raises(ValueError, herme.hermeint, [0], -1) assert_raises(ValueError, herme.hermeint, [0], 1, [0, 0]) assert_raises(ValueError, herme.hermeint, [0], lbnd=[0]) assert_raises(ValueError, herme.hermeint, [0], scl=[0]) - assert_raises(ValueError, herme.hermeint, [0], axis=.5) + assert_raises(TypeError, herme.hermeint, [0], axis=.5) # test integration of zero polynomial for i in range(2, 5): @@ -296,11 +305,11 @@ def test_hermeint_axis(self): assert_almost_equal(res, tgt) -class TestDerivative(object): +class TestDerivative: def test_hermeder(self): # check exceptions - assert_raises(ValueError, herme.hermeder, [0], .5) + assert_raises(TypeError, herme.hermeder, [0], .5) assert_raises(ValueError, herme.hermeder, [0], -1) # check that zeroth derivative does nothing @@ -337,7 +346,7 @@ def test_hermeder_axis(self): assert_almost_equal(res, tgt) -class TestVander(object): +class TestVander: # some random values in [-1, 1) x = np.random.random((3, 5))*2 - 1 @@ -385,7 +394,7 @@ def test_hermevander3d(self): assert_(van.shape == (1, 5, 24)) -class TestFitting(object): +class TestFitting: def test_hermefit(self): def f(x): @@ -462,7 +471,7 @@ def f2(x): assert_almost_equal(coef1, coef2) -class TestCompanion(object): +class TestCompanion: def test_raises(self): assert_raises(ValueError, herme.hermecompanion, []) @@ -477,7 +486,7 @@ def test_linear_root(self): assert_(herme.hermecompanion([1, 2])[0, 0] == -.5) -class TestGauss(object): +class TestGauss: def test_100(self): x, w = herme.hermegauss(100) @@ -496,7 +505,7 @@ def test_100(self): assert_almost_equal(w.sum(), tgt) -class TestMisc(object): +class TestMisc: def test_hermefromroots(self): res = herme.hermefromroots([]) diff --git a/numpy/polynomial/tests/test_laguerre.py b/numpy/polynomial/tests/test_laguerre.py index 3cb630e4693e..227ef3c5576d 100644 --- a/numpy/polynomial/tests/test_laguerre.py +++ b/numpy/polynomial/tests/test_laguerre.py @@ -1,7 +1,7 @@ """Tests for laguerre module. """ -from __future__ import division, absolute_import, print_function +from functools import reduce import numpy as np import numpy.polynomial.laguerre as lag @@ -25,7 +25,7 @@ def trim(x): return lag.lagtrim(x, tol=1e-6) -class TestConstants(object): +class TestConstants: def test_lagdomain(self): assert_equal(lag.lagdomain, [0, 1]) @@ -40,13 +40,13 @@ def test_lagx(self): assert_equal(lag.lagx, [1, -1]) -class TestArithmetic(object): +class TestArithmetic: x = np.linspace(-3, 3, 100) def test_lagadd(self): for i in range(5): for j in range(5): - msg = "At i=%d, j=%d" % (i, j) + msg = f"At i={i}, j={j}" tgt = np.zeros(max(i, j) + 1) tgt[i] += 1 tgt[j] += 1 @@ -56,7 +56,7 @@ def test_lagadd(self): def test_lagsub(self): for i in range(5): for j in range(5): - msg = "At i=%d, j=%d" % (i, j) + msg = f"At i={i}, j={j}" tgt = np.zeros(max(i, j) + 1) tgt[i] += 1 tgt[j] -= 1 @@ -77,7 +77,7 @@ def test_lagmul(self): pol1 = [0]*i + [1] val1 = lag.lagval(self.x, pol1) for j in range(5): - msg = "At i=%d, j=%d" % (i, j) + msg = f"At i={i}, j={j}" pol2 = [0]*j + [1] val2 = lag.lagval(self.x, pol2) pol3 = lag.lagmul(pol1, pol2) @@ -88,7 +88,7 @@ def test_lagmul(self): def test_lagdiv(self): for i in range(5): for j in range(5): - msg = "At i=%d, j=%d" % (i, j) + msg = f"At i={i}, j={j}" ci = [0]*i + [1] cj = [0]*j + [1] tgt = lag.lagadd(ci, cj) @@ -96,8 +96,17 @@ def test_lagdiv(self): res = lag.lagadd(lag.lagmul(quo, ci), rem) assert_almost_equal(trim(res), trim(tgt), err_msg=msg) + def test_lagpow(self): + for i in range(5): + for j in range(5): + msg = f"At i={i}, j={j}" + c = np.arange(i + 1) + tgt = reduce(lag.lagmul, [c]*j, np.array([1])) + res = lag.lagpow(c, j) + assert_equal(trim(res), trim(tgt), err_msg=msg) + -class TestEvaluation(object): +class TestEvaluation: # coefficients of 1 + 2*x + 3*x**2 c1d = np.array([9., -14., 6.]) c2d = np.einsum('i,j->ij', c1d, c1d) @@ -115,7 +124,7 @@ def test_lagval(self): x = np.linspace(-1, 1) y = [polyval(x, c) for c in Llist] for i in range(7): - msg = "At i=%d" % i + msg = f"At i={i}" tgt = y[i] res = lag.lagval(x, [0]*i + [1]) assert_almost_equal(res, tgt, err_msg=msg) @@ -191,16 +200,16 @@ def test_laggrid3d(self): assert_(res.shape == (2, 3)*3) -class TestIntegral(object): +class TestIntegral: def test_lagint(self): # check exceptions - assert_raises(ValueError, lag.lagint, [0], .5) + assert_raises(TypeError, lag.lagint, [0], .5) assert_raises(ValueError, lag.lagint, [0], -1) assert_raises(ValueError, lag.lagint, [0], 1, [0, 0]) assert_raises(ValueError, lag.lagint, [0], lbnd=[0]) assert_raises(ValueError, lag.lagint, [0], scl=[0]) - assert_raises(ValueError, lag.lagint, [0], axis=.5) + assert_raises(TypeError, lag.lagint, [0], axis=.5) # test integration of zero polynomial for i in range(2, 5): @@ -293,11 +302,11 @@ def test_lagint_axis(self): assert_almost_equal(res, tgt) -class TestDerivative(object): +class TestDerivative: def test_lagder(self): # check exceptions - assert_raises(ValueError, lag.lagder, [0], .5) + assert_raises(TypeError, lag.lagder, [0], .5) assert_raises(ValueError, lag.lagder, [0], -1) # check that zeroth derivative does nothing @@ -333,7 +342,7 @@ def test_lagder_axis(self): assert_almost_equal(res, tgt) -class TestVander(object): +class TestVander: # some random values in [-1, 1) x = np.random.random((3, 5))*2 - 1 @@ -381,7 +390,7 @@ def test_lagvander3d(self): assert_(van.shape == (1, 5, 24)) -class TestFitting(object): +class TestFitting: def test_lagfit(self): def f(x): @@ -443,7 +452,7 @@ def f(x): assert_almost_equal(lag.lagfit(x, x, [0, 1]), [1, -1]) -class TestCompanion(object): +class TestCompanion: def test_raises(self): assert_raises(ValueError, lag.lagcompanion, []) @@ -458,7 +467,7 @@ def test_linear_root(self): assert_(lag.lagcompanion([1, 2])[0, 0] == 1.5) -class TestGauss(object): +class TestGauss: def test_100(self): x, w = lag.laggauss(100) @@ -477,7 +486,7 @@ def test_100(self): assert_almost_equal(w.sum(), tgt) -class TestMisc(object): +class TestMisc: def test_lagfromroots(self): res = lag.lagfromroots([]) diff --git a/numpy/polynomial/tests/test_legendre.py b/numpy/polynomial/tests/test_legendre.py index aeecd8775d63..92399c160ecb 100644 --- a/numpy/polynomial/tests/test_legendre.py +++ b/numpy/polynomial/tests/test_legendre.py @@ -1,7 +1,7 @@ """Tests for legendre module. """ -from __future__ import division, absolute_import, print_function +from functools import reduce import numpy as np import numpy.polynomial.legendre as leg @@ -28,7 +28,7 @@ def trim(x): return leg.legtrim(x, tol=1e-6) -class TestConstants(object): +class TestConstants: def test_legdomain(self): assert_equal(leg.legdomain, [-1, 1]) @@ -43,13 +43,13 @@ def test_legx(self): assert_equal(leg.legx, [0, 1]) -class TestArithmetic(object): +class TestArithmetic: x = np.linspace(-1, 1, 100) def test_legadd(self): for i in range(5): for j in range(5): - msg = "At i=%d, j=%d" % (i, j) + msg = f"At i={i}, j={j}" tgt = np.zeros(max(i, j) + 1) tgt[i] += 1 tgt[j] += 1 @@ -59,7 +59,7 @@ def test_legadd(self): def test_legsub(self): for i in range(5): for j in range(5): - msg = "At i=%d, j=%d" % (i, j) + msg = f"At i={i}, j={j}" tgt = np.zeros(max(i, j) + 1) tgt[i] += 1 tgt[j] -= 1 @@ -81,7 +81,7 @@ def test_legmul(self): pol1 = [0]*i + [1] val1 = leg.legval(self.x, pol1) for j in range(5): - msg = "At i=%d, j=%d" % (i, j) + msg = f"At i={i}, j={j}" pol2 = [0]*j + [1] val2 = leg.legval(self.x, pol2) pol3 = leg.legmul(pol1, pol2) @@ -92,7 +92,7 @@ def test_legmul(self): def test_legdiv(self): for i in range(5): for j in range(5): - msg = "At i=%d, j=%d" % (i, j) + msg = f"At i={i}, j={j}" ci = [0]*i + [1] cj = [0]*j + [1] tgt = leg.legadd(ci, cj) @@ -100,8 +100,17 @@ def test_legdiv(self): res = leg.legadd(leg.legmul(quo, ci), rem) assert_equal(trim(res), trim(tgt), err_msg=msg) + def test_legpow(self): + for i in range(5): + for j in range(5): + msg = f"At i={i}, j={j}" + c = np.arange(i + 1) + tgt = reduce(leg.legmul, [c]*j, np.array([1])) + res = leg.legpow(c, j) + assert_equal(trim(res), trim(tgt), err_msg=msg) + -class TestEvaluation(object): +class TestEvaluation: # coefficients of 1 + 2*x + 3*x**2 c1d = np.array([2., 2., 2.]) c2d = np.einsum('i,j->ij', c1d, c1d) @@ -119,7 +128,7 @@ def test_legval(self): x = np.linspace(-1, 1) y = [polyval(x, c) for c in Llist] for i in range(10): - msg = "At i=%d" % i + msg = f"At i={i}" tgt = y[i] res = leg.legval(x, [0]*i + [1]) assert_almost_equal(res, tgt, err_msg=msg) @@ -195,16 +204,16 @@ def test_leggrid3d(self): assert_(res.shape == (2, 3)*3) -class TestIntegral(object): +class TestIntegral: def test_legint(self): # check exceptions - assert_raises(ValueError, leg.legint, [0], .5) + assert_raises(TypeError, leg.legint, [0], .5) assert_raises(ValueError, leg.legint, [0], -1) assert_raises(ValueError, leg.legint, [0], 1, [0, 0]) assert_raises(ValueError, leg.legint, [0], lbnd=[0]) assert_raises(ValueError, leg.legint, [0], scl=[0]) - assert_raises(ValueError, leg.legint, [0], axis=.5) + assert_raises(TypeError, leg.legint, [0], axis=.5) # test integration of zero polynomial for i in range(2, 5): @@ -296,12 +305,15 @@ def test_legint_axis(self): res = leg.legint(c2d, k=3, axis=1) assert_almost_equal(res, tgt) + def test_legint_zerointord(self): + assert_equal(leg.legint((1, 2, 3), 0), (1, 2, 3)) -class TestDerivative(object): + +class TestDerivative: def test_legder(self): # check exceptions - assert_raises(ValueError, leg.legder, [0], .5) + assert_raises(TypeError, leg.legder, [0], .5) assert_raises(ValueError, leg.legder, [0], -1) # check that zeroth derivative does nothing @@ -336,8 +348,11 @@ def test_legder_axis(self): res = leg.legder(c2d, axis=1) assert_almost_equal(res, tgt) + def test_legder_orderhigherthancoeff(self): + c = (1, 2, 3, 4) + assert_equal(leg.legder(c, 4), [0]) -class TestVander(object): +class TestVander: # some random values in [-1, 1) x = np.random.random((3, 5))*2 - 1 @@ -384,8 +399,11 @@ def test_legvander3d(self): van = leg.legvander3d([x1], [x2], [x3], [1, 2, 3]) assert_(van.shape == (1, 5, 24)) + def test_legvander_negdeg(self): + assert_raises(ValueError, leg.legvander, (1, 2, 3), -1) + -class TestFitting(object): +class TestFitting: def test_legfit(self): def f(x): @@ -462,7 +480,7 @@ def f2(x): assert_almost_equal(coef1, coef2) -class TestCompanion(object): +class TestCompanion: def test_raises(self): assert_raises(ValueError, leg.legcompanion, []) @@ -477,7 +495,7 @@ def test_linear_root(self): assert_(leg.legcompanion([1, 2])[0, 0] == -.5) -class TestGauss(object): +class TestGauss: def test_100(self): x, w = leg.leggauss(100) @@ -496,7 +514,7 @@ def test_100(self): assert_almost_equal(w.sum(), tgt) -class TestMisc(object): +class TestMisc: def test_legfromroots(self): res = leg.legfromroots([]) @@ -532,6 +550,9 @@ def test_legtrim(self): def test_legline(self): assert_equal(leg.legline(3, 4), [3, 4]) + def test_legline_zeroscl(self): + assert_equal(leg.legline(3, 0), [3]) + def test_leg2poly(self): for i in range(10): assert_almost_equal(leg.leg2poly([0]*i + [1]), Llist[i]) diff --git a/numpy/polynomial/tests/test_polynomial.py b/numpy/polynomial/tests/test_polynomial.py index 67728e35ec81..a0a09fcf4a93 100644 --- a/numpy/polynomial/tests/test_polynomial.py +++ b/numpy/polynomial/tests/test_polynomial.py @@ -1,13 +1,13 @@ """Tests for polynomial module. """ -from __future__ import division, absolute_import, print_function +from functools import reduce import numpy as np import numpy.polynomial.polynomial as poly from numpy.testing import ( assert_almost_equal, assert_raises, assert_equal, assert_, - ) + assert_warns, assert_array_equal, assert_raises_regex) def trim(x): @@ -27,7 +27,7 @@ def trim(x): Tlist = [T0, T1, T2, T3, T4, T5, T6, T7, T8, T9] -class TestConstants(object): +class TestConstants: def test_polydomain(self): assert_equal(poly.polydomain, [-1, 1]) @@ -42,12 +42,12 @@ def test_polyx(self): assert_equal(poly.polyx, [0, 1]) -class TestArithmetic(object): +class TestArithmetic: def test_polyadd(self): for i in range(5): for j in range(5): - msg = "At i=%d, j=%d" % (i, j) + msg = f"At i={i}, j={j}" tgt = np.zeros(max(i, j) + 1) tgt[i] += 1 tgt[j] += 1 @@ -57,7 +57,7 @@ def test_polyadd(self): def test_polysub(self): for i in range(5): for j in range(5): - msg = "At i=%d, j=%d" % (i, j) + msg = f"At i={i}, j={j}" tgt = np.zeros(max(i, j) + 1) tgt[i] += 1 tgt[j] -= 1 @@ -75,7 +75,7 @@ def test_polymulx(self): def test_polymul(self): for i in range(5): for j in range(5): - msg = "At i=%d, j=%d" % (i, j) + msg = f"At i={i}, j={j}" tgt = np.zeros(i + j + 1) tgt[i + j] += 1 res = poly.polymul([0]*i + [1], [0]*j + [1]) @@ -94,7 +94,7 @@ def test_polydiv(self): # check rest. for i in range(5): for j in range(5): - msg = "At i=%d, j=%d" % (i, j) + msg = f"At i={i}, j={j}" ci = [0]*i + [1, 2] cj = [0]*j + [1, 2] tgt = poly.polyadd(ci, cj) @@ -102,8 +102,17 @@ def test_polydiv(self): res = poly.polyadd(poly.polymul(quo, ci), rem) assert_equal(res, tgt, err_msg=msg) + def test_polypow(self): + for i in range(5): + for j in range(5): + msg = f"At i={i}, j={j}" + c = np.arange(i + 1) + tgt = reduce(poly.polymul, [c]*j, np.array([1])) + res = poly.polypow(c, j) + assert_equal(trim(res), trim(tgt), err_msg=msg) -class TestEvaluation(object): + +class TestEvaluation: # coefficients of 1 + 2*x + 3*x**2 c1d = np.array([1., 2., 3.]) c2d = np.einsum('i,j->ij', c1d, c1d) @@ -136,6 +145,19 @@ def test_polyval(self): assert_equal(poly.polyval(x, [1, 0]).shape, dims) assert_equal(poly.polyval(x, [1, 0, 0]).shape, dims) + #check masked arrays are processed correctly + mask = [False, True, False] + mx = np.ma.array([1, 2, 3], mask=mask) + res = np.polyval([7, 5, 3], mx) + assert_array_equal(res.mask, mask) + + #check subtypes of ndarray are preserved + class C(np.ndarray): + pass + + cx = np.array([1, 2, 3]).view(C) + assert_equal(type(np.polyval([2, 3, 4], cx)), C) + def test_polyvalfromroots(self): # check exception for broadcasting x values over root array with # too few dimensions @@ -205,7 +227,8 @@ def test_polyval2d(self): y1, y2, y3 = self.y #test exceptions - assert_raises(ValueError, poly.polyval2d, x1, x2[:2], self.c2d) + assert_raises_regex(ValueError, 'incompatible', + poly.polyval2d, x1, x2[:2], self.c2d) #test values tgt = y1*y2 @@ -222,7 +245,8 @@ def test_polyval3d(self): y1, y2, y3 = self.y #test exceptions - assert_raises(ValueError, poly.polyval3d, x1, x2, x3[:2], self.c3d) + assert_raises_regex(ValueError, 'incompatible', + poly.polyval3d, x1, x2, x3[:2], self.c3d) #test values tgt = y1*y2*y3 @@ -263,16 +287,18 @@ def test_polygrid3d(self): assert_(res.shape == (2, 3)*3) -class TestIntegral(object): +class TestIntegral: def test_polyint(self): # check exceptions - assert_raises(ValueError, poly.polyint, [0], .5) + assert_raises(TypeError, poly.polyint, [0], .5) assert_raises(ValueError, poly.polyint, [0], -1) assert_raises(ValueError, poly.polyint, [0], 1, [0, 0]) assert_raises(ValueError, poly.polyint, [0], lbnd=[0]) assert_raises(ValueError, poly.polyint, [0], scl=[0]) - assert_raises(ValueError, poly.polyint, [0], axis=.5) + assert_raises(TypeError, poly.polyint, [0], axis=.5) + with assert_warns(DeprecationWarning): + poly.polyint([1, 1], 1.) # test integration of zero polynomial for i in range(2, 5): @@ -360,11 +386,11 @@ def test_polyint_axis(self): assert_almost_equal(res, tgt) -class TestDerivative(object): +class TestDerivative: def test_polyder(self): # check exceptions - assert_raises(ValueError, poly.polyder, [0], .5) + assert_raises(TypeError, poly.polyder, [0], .5) assert_raises(ValueError, poly.polyder, [0], -1) # check that zeroth derivative does nothing @@ -400,7 +426,7 @@ def test_polyder_axis(self): assert_almost_equal(res, tgt) -class TestVander(object): +class TestVander: # some random values in [-1, 1) x = np.random.random((3, 5))*2 - 1 @@ -447,8 +473,12 @@ def test_polyvander3d(self): van = poly.polyvander3d([x1], [x2], [x3], [1, 2, 3]) assert_(van.shape == (1, 5, 24)) + def test_polyvandernegdeg(self): + x = np.arange(3) + assert_raises(ValueError, poly.polyvander, x, -1) -class TestCompanion(object): + +class TestCompanion: def test_raises(self): assert_raises(ValueError, poly.polycompanion, []) @@ -463,7 +493,7 @@ def test_linear_root(self): assert_(poly.polycompanion([1, 2])[0, 0] == -.5) -class TestMisc(object): +class TestMisc: def test_polyfromroots(self): res = poly.polyfromroots([]) @@ -565,3 +595,6 @@ def test_polytrim(self): def test_polyline(self): assert_equal(poly.polyline(3, 4), [3, 4]) + + def test_polyline_zero(self): + assert_equal(poly.polyline(3, 0), [3]) diff --git a/numpy/polynomial/tests/test_polyutils.py b/numpy/polynomial/tests/test_polyutils.py index 801c558ccca4..cc630790da1c 100644 --- a/numpy/polynomial/tests/test_polyutils.py +++ b/numpy/polynomial/tests/test_polyutils.py @@ -1,8 +1,6 @@ """Tests for polyutils module. """ -from __future__ import division, absolute_import, print_function - import numpy as np import numpy.polynomial.polyutils as pu from numpy.testing import ( @@ -10,7 +8,7 @@ ) -class TestMisc(object): +class TestMisc: def test_trimseq(self): for i in range(5): @@ -42,8 +40,23 @@ def test_trimcoef(self): assert_equal(pu.trimcoef(coef, 1), coef[:-3]) assert_equal(pu.trimcoef(coef, 2), [0]) + def test_vander_nd_exception(self): + # n_dims != len(points) + assert_raises(ValueError, pu._vander_nd, (), (1, 2, 3), [90]) + # n_dims != len(degrees) + assert_raises(ValueError, pu._vander_nd, (), (), [90.65]) + # n_dims == 0 + assert_raises(ValueError, pu._vander_nd, (), (), []) + + def test_div_zerodiv(self): + # c2[-1] == 0 + assert_raises(ZeroDivisionError, pu._div, pu._div, (1, 2, 3), [0]) + + def test_pow_too_large(self): + # power > maxpower + assert_raises(ValueError, pu._pow, (), [1, 2, 3], 5, 4) -class TestDomain(object): +class TestDomain: def test_getdomain(self): # test for real values diff --git a/numpy/polynomial/tests/test_printing.py b/numpy/polynomial/tests/test_printing.py index 3f1236402228..4e9902a69588 100644 --- a/numpy/polynomial/tests/test_printing.py +++ b/numpy/polynomial/tests/test_printing.py @@ -1,42 +1,315 @@ -from __future__ import division, absolute_import, print_function - +import pytest +from numpy.core import array, arange, printoptions import numpy.polynomial as poly -from numpy.testing import assert_equal +from numpy.testing import assert_equal, assert_ +# For testing polynomial printing with object arrays +from fractions import Fraction +from decimal import Decimal -class TestStr(object): - def test_polynomial_str(self): - res = str(poly.Polynomial([0, 1])) - tgt = 'poly([0. 1.])' + +class TestStrUnicodeSuperSubscripts: + + @pytest.fixture(scope='class', autouse=True) + def use_unicode(self): + poly.set_default_printstyle('unicode') + + @pytest.mark.parametrize(('inp', 'tgt'), ( + ([1, 2, 3], "1.0 + 2.0·x¹ + 3.0·x²"), + ([-1, 0, 3, -1], "-1.0 + 0.0·x¹ + 3.0·x² - 1.0·x³"), + (arange(12), ("0.0 + 1.0·x¹ + 2.0·x² + 3.0·x³ + 4.0·x⁴ + 5.0·x⁵ + " + "6.0·x⁶ + 7.0·x⁷ +\n8.0·x⁸ + 9.0·x⁹ + 10.0·x¹⁰ + " + "11.0·x¹¹")), + )) + def test_polynomial_str(self, inp, tgt): + res = str(poly.Polynomial(inp)) assert_equal(res, tgt) - def test_chebyshev_str(self): - res = str(poly.Chebyshev([0, 1])) - tgt = 'cheb([0. 1.])' + @pytest.mark.parametrize(('inp', 'tgt'), ( + ([1, 2, 3], "1.0 + 2.0·T₁(x) + 3.0·T₂(x)"), + ([-1, 0, 3, -1], "-1.0 + 0.0·T₁(x) + 3.0·T₂(x) - 1.0·T₃(x)"), + (arange(12), ("0.0 + 1.0·T₁(x) + 2.0·T₂(x) + 3.0·T₃(x) + 4.0·T₄(x) + " + "5.0·T₅(x) +\n6.0·T₆(x) + 7.0·T₇(x) + 8.0·T₈(x) + " + "9.0·T₉(x) + 10.0·T₁₀(x) + 11.0·T₁₁(x)")), + )) + def test_chebyshev_str(self, inp, tgt): + res = str(poly.Chebyshev(inp)) + assert_equal(res, tgt) + + @pytest.mark.parametrize(('inp', 'tgt'), ( + ([1, 2, 3], "1.0 + 2.0·P₁(x) + 3.0·P₂(x)"), + ([-1, 0, 3, -1], "-1.0 + 0.0·P₁(x) + 3.0·P₂(x) - 1.0·P₃(x)"), + (arange(12), ("0.0 + 1.0·P₁(x) + 2.0·P₂(x) + 3.0·P₃(x) + 4.0·P₄(x) + " + "5.0·P₅(x) +\n6.0·P₆(x) + 7.0·P₇(x) + 8.0·P₈(x) + " + "9.0·P₉(x) + 10.0·P₁₀(x) + 11.0·P₁₁(x)")), + )) + def test_legendre_str(self, inp, tgt): + res = str(poly.Legendre(inp)) + assert_equal(res, tgt) + + @pytest.mark.parametrize(('inp', 'tgt'), ( + ([1, 2, 3], "1.0 + 2.0·H₁(x) + 3.0·H₂(x)"), + ([-1, 0, 3, -1], "-1.0 + 0.0·H₁(x) + 3.0·H₂(x) - 1.0·H₃(x)"), + (arange(12), ("0.0 + 1.0·H₁(x) + 2.0·H₂(x) + 3.0·H₃(x) + 4.0·H₄(x) + " + "5.0·H₅(x) +\n6.0·H₆(x) + 7.0·H₇(x) + 8.0·H₈(x) + " + "9.0·H₉(x) + 10.0·H₁₀(x) + 11.0·H₁₁(x)")), + )) + def test_hermite_str(self, inp, tgt): + res = str(poly.Hermite(inp)) + assert_equal(res, tgt) + + @pytest.mark.parametrize(('inp', 'tgt'), ( + ([1, 2, 3], "1.0 + 2.0·He₁(x) + 3.0·He₂(x)"), + ([-1, 0, 3, -1], "-1.0 + 0.0·He₁(x) + 3.0·He₂(x) - 1.0·He₃(x)"), + (arange(12), ("0.0 + 1.0·He₁(x) + 2.0·He₂(x) + 3.0·He₃(x) + " + "4.0·He₄(x) + 5.0·He₅(x) +\n6.0·He₆(x) + 7.0·He₇(x) + " + "8.0·He₈(x) + 9.0·He₉(x) + 10.0·He₁₀(x) +\n" + "11.0·He₁₁(x)")), + )) + def test_hermiteE_str(self, inp, tgt): + res = str(poly.HermiteE(inp)) + assert_equal(res, tgt) + + @pytest.mark.parametrize(('inp', 'tgt'), ( + ([1, 2, 3], "1.0 + 2.0·L₁(x) + 3.0·L₂(x)"), + ([-1, 0, 3, -1], "-1.0 + 0.0·L₁(x) + 3.0·L₂(x) - 1.0·L₃(x)"), + (arange(12), ("0.0 + 1.0·L₁(x) + 2.0·L₂(x) + 3.0·L₃(x) + 4.0·L₄(x) + " + "5.0·L₅(x) +\n6.0·L₆(x) + 7.0·L₇(x) + 8.0·L₈(x) + " + "9.0·L₉(x) + 10.0·L₁₀(x) + 11.0·L₁₁(x)")), + )) + def test_laguerre_str(self, inp, tgt): + res = str(poly.Laguerre(inp)) assert_equal(res, tgt) - def test_legendre_str(self): - res = str(poly.Legendre([0, 1])) - tgt = 'leg([0. 1.])' + +class TestStrAscii: + + @pytest.fixture(scope='class', autouse=True) + def use_ascii(self): + poly.set_default_printstyle('ascii') + + @pytest.mark.parametrize(('inp', 'tgt'), ( + ([1, 2, 3], "1.0 + 2.0 x**1 + 3.0 x**2"), + ([-1, 0, 3, -1], "-1.0 + 0.0 x**1 + 3.0 x**2 - 1.0 x**3"), + (arange(12), ("0.0 + 1.0 x**1 + 2.0 x**2 + 3.0 x**3 + 4.0 x**4 + " + "5.0 x**5 + 6.0 x**6 +\n7.0 x**7 + 8.0 x**8 + " + "9.0 x**9 + 10.0 x**10 + 11.0 x**11")), + )) + def test_polynomial_str(self, inp, tgt): + res = str(poly.Polynomial(inp)) assert_equal(res, tgt) - def test_hermite_str(self): - res = str(poly.Hermite([0, 1])) - tgt = 'herm([0. 1.])' + @pytest.mark.parametrize(('inp', 'tgt'), ( + ([1, 2, 3], "1.0 + 2.0 T_1(x) + 3.0 T_2(x)"), + ([-1, 0, 3, -1], "-1.0 + 0.0 T_1(x) + 3.0 T_2(x) - 1.0 T_3(x)"), + (arange(12), ("0.0 + 1.0 T_1(x) + 2.0 T_2(x) + 3.0 T_3(x) + " + "4.0 T_4(x) + 5.0 T_5(x) +\n6.0 T_6(x) + 7.0 T_7(x) + " + "8.0 T_8(x) + 9.0 T_9(x) + 10.0 T_10(x) +\n" + "11.0 T_11(x)")), + )) + def test_chebyshev_str(self, inp, tgt): + res = str(poly.Chebyshev(inp)) assert_equal(res, tgt) - def test_hermiteE_str(self): - res = str(poly.HermiteE([0, 1])) - tgt = 'herme([0. 1.])' + @pytest.mark.parametrize(('inp', 'tgt'), ( + ([1, 2, 3], "1.0 + 2.0 P_1(x) + 3.0 P_2(x)"), + ([-1, 0, 3, -1], "-1.0 + 0.0 P_1(x) + 3.0 P_2(x) - 1.0 P_3(x)"), + (arange(12), ("0.0 + 1.0 P_1(x) + 2.0 P_2(x) + 3.0 P_3(x) + " + "4.0 P_4(x) + 5.0 P_5(x) +\n6.0 P_6(x) + 7.0 P_7(x) + " + "8.0 P_8(x) + 9.0 P_9(x) + 10.0 P_10(x) +\n" + "11.0 P_11(x)")), + )) + def test_legendre_str(self, inp, tgt): + res = str(poly.Legendre(inp)) assert_equal(res, tgt) - def test_laguerre_str(self): - res = str(poly.Laguerre([0, 1])) - tgt = 'lag([0. 1.])' + @pytest.mark.parametrize(('inp', 'tgt'), ( + ([1, 2, 3], "1.0 + 2.0 H_1(x) + 3.0 H_2(x)"), + ([-1, 0, 3, -1], "-1.0 + 0.0 H_1(x) + 3.0 H_2(x) - 1.0 H_3(x)"), + (arange(12), ("0.0 + 1.0 H_1(x) + 2.0 H_2(x) + 3.0 H_3(x) + " + "4.0 H_4(x) + 5.0 H_5(x) +\n6.0 H_6(x) + 7.0 H_7(x) + " + "8.0 H_8(x) + 9.0 H_9(x) + 10.0 H_10(x) +\n" + "11.0 H_11(x)")), + )) + def test_hermite_str(self, inp, tgt): + res = str(poly.Hermite(inp)) assert_equal(res, tgt) + @pytest.mark.parametrize(('inp', 'tgt'), ( + ([1, 2, 3], "1.0 + 2.0 He_1(x) + 3.0 He_2(x)"), + ([-1, 0, 3, -1], "-1.0 + 0.0 He_1(x) + 3.0 He_2(x) - 1.0 He_3(x)"), + (arange(12), ("0.0 + 1.0 He_1(x) + 2.0 He_2(x) + 3.0 He_3(x) + " + "4.0 He_4(x) +\n5.0 He_5(x) + 6.0 He_6(x) + " + "7.0 He_7(x) + 8.0 He_8(x) + 9.0 He_9(x) +\n" + "10.0 He_10(x) + 11.0 He_11(x)")), + )) + def test_hermiteE_str(self, inp, tgt): + res = str(poly.HermiteE(inp)) + assert_equal(res, tgt) + + @pytest.mark.parametrize(('inp', 'tgt'), ( + ([1, 2, 3], "1.0 + 2.0 L_1(x) + 3.0 L_2(x)"), + ([-1, 0, 3, -1], "-1.0 + 0.0 L_1(x) + 3.0 L_2(x) - 1.0 L_3(x)"), + (arange(12), ("0.0 + 1.0 L_1(x) + 2.0 L_2(x) + 3.0 L_3(x) + " + "4.0 L_4(x) + 5.0 L_5(x) +\n6.0 L_6(x) + 7.0 L_7(x) + " + "8.0 L_8(x) + 9.0 L_9(x) + 10.0 L_10(x) +\n" + "11.0 L_11(x)")), + )) + def test_laguerre_str(self, inp, tgt): + res = str(poly.Laguerre(inp)) + assert_equal(res, tgt) + + +class TestLinebreaking: + + @pytest.fixture(scope='class', autouse=True) + def use_ascii(self): + poly.set_default_printstyle('ascii') + + def test_single_line_one_less(self): + # With 'ascii' style, len(str(p)) is default linewidth - 1 (i.e. 74) + p = poly.Polynomial([123456789, 123456789, 123456789, 1234, 1]) + assert_equal(len(str(p)), 74) + assert_equal(str(p), ( + '123456789.0 + 123456789.0 x**1 + 123456789.0 x**2 + ' + '1234.0 x**3 + 1.0 x**4' + )) + + def test_num_chars_is_linewidth(self): + # len(str(p)) == default linewidth == 75 + p = poly.Polynomial([123456789, 123456789, 123456789, 1234, 10]) + assert_equal(len(str(p)), 75) + assert_equal(str(p), ( + '123456789.0 + 123456789.0 x**1 + 123456789.0 x**2 + ' + '1234.0 x**3 +\n10.0 x**4' + )) + + def test_first_linebreak_multiline_one_less_than_linewidth(self): + # Multiline str where len(first_line) + len(next_term) == lw - 1 == 74 + p = poly.Polynomial( + [123456789, 123456789, 123456789, 12, 1, 123456789] + ) + assert_equal(len(str(p).split('\n')[0]), 74) + assert_equal(str(p), ( + '123456789.0 + 123456789.0 x**1 + 123456789.0 x**2 + ' + '12.0 x**3 + 1.0 x**4 +\n123456789.0 x**5' + )) -class TestRepr(object): + def test_first_linebreak_multiline_on_linewidth(self): + # First line is one character longer than previous test + p = poly.Polynomial( + [123456789, 123456789, 123456789, 123, 1, 123456789] + ) + assert_equal(str(p), ( + '123456789.0 + 123456789.0 x**1 + 123456789.0 x**2 + ' + '123.0 x**3 +\n1.0 x**4 + 123456789.0 x**5' + )) + + @pytest.mark.parametrize(('lw', 'tgt'), ( + (75, ('0.0 + 10.0 x**1 + 200.0 x**2 + 3000.0 x**3 + 40000.0 x**4 +\n' + '500000.0 x**5 + 600000.0 x**6 + 70000.0 x**7 + 8000.0 x**8 + ' + '900.0 x**9')), + (45, ('0.0 + 10.0 x**1 + 200.0 x**2 + 3000.0 x**3 +\n40000.0 x**4 + ' + '500000.0 x**5 +\n600000.0 x**6 + 70000.0 x**7 + 8000.0 x**8 +\n' + '900.0 x**9')), + (132, ('0.0 + 10.0 x**1 + 200.0 x**2 + 3000.0 x**3 + 40000.0 x**4 + ' + '500000.0 x**5 + 600000.0 x**6 + 70000.0 x**7 + 8000.0 x**8 + ' + '900.0 x**9')), + )) + def test_linewidth_printoption(self, lw, tgt): + p = poly.Polynomial( + [0, 10, 200, 3000, 40000, 500000, 600000, 70000, 8000, 900] + ) + with printoptions(linewidth=lw): + assert_equal(str(p), tgt) + for line in str(p).split('\n'): + assert_(len(line) < lw) + + +def test_set_default_printoptions(): + p = poly.Polynomial([1, 2, 3]) + c = poly.Chebyshev([1, 2, 3]) + poly.set_default_printstyle('ascii') + assert_equal(str(p), "1.0 + 2.0 x**1 + 3.0 x**2") + assert_equal(str(c), "1.0 + 2.0 T_1(x) + 3.0 T_2(x)") + poly.set_default_printstyle('unicode') + assert_equal(str(p), "1.0 + 2.0·x¹ + 3.0·x²") + assert_equal(str(c), "1.0 + 2.0·T₁(x) + 3.0·T₂(x)") + with pytest.raises(ValueError): + poly.set_default_printstyle('invalid_input') + + +def test_complex_coefficients(): + """Test both numpy and built-in complex.""" + coefs = [0+1j, 1+1j, -2+2j, 3+0j] + # numpy complex + p1 = poly.Polynomial(coefs) + # Python complex + p2 = poly.Polynomial(array(coefs, dtype=object)) + poly.set_default_printstyle('unicode') + assert_equal(str(p1), "1j + (1+1j)·x¹ - (2-2j)·x² + (3+0j)·x³") + assert_equal(str(p2), "1j + (1+1j)·x¹ + (-2+2j)·x² + (3+0j)·x³") + poly.set_default_printstyle('ascii') + assert_equal(str(p1), "1j + (1+1j) x**1 - (2-2j) x**2 + (3+0j) x**3") + assert_equal(str(p2), "1j + (1+1j) x**1 + (-2+2j) x**2 + (3+0j) x**3") + + +@pytest.mark.parametrize(('coefs', 'tgt'), ( + (array([Fraction(1, 2), Fraction(3, 4)], dtype=object), ( + "1/2 + 3/4·x¹" + )), + (array([1, 2, Fraction(5, 7)], dtype=object), ( + "1 + 2·x¹ + 5/7·x²" + )), + (array([Decimal('1.00'), Decimal('2.2'), 3], dtype=object), ( + "1.00 + 2.2·x¹ + 3·x²" + )), +)) +def test_numeric_object_coefficients(coefs, tgt): + p = poly.Polynomial(coefs) + poly.set_default_printstyle('unicode') + assert_equal(str(p), tgt) + + +@pytest.mark.parametrize(('coefs', 'tgt'), ( + (array([1, 2, 'f'], dtype=object), '1 + 2·x¹ + f·x²'), + (array([1, 2, [3, 4]], dtype=object), '1 + 2·x¹ + [3, 4]·x²'), +)) +def test_nonnumeric_object_coefficients(coefs, tgt): + """ + Test coef fallback for object arrays of non-numeric coefficients. + """ + p = poly.Polynomial(coefs) + poly.set_default_printstyle('unicode') + assert_equal(str(p), tgt) + + +class TestFormat: + def test_format_unicode(self): + poly.set_default_printstyle('ascii') + p = poly.Polynomial([1, 2, 0, -1]) + assert_equal(format(p, 'unicode'), "1.0 + 2.0·x¹ + 0.0·x² - 1.0·x³") + + def test_format_ascii(self): + poly.set_default_printstyle('unicode') + p = poly.Polynomial([1, 2, 0, -1]) + assert_equal( + format(p, 'ascii'), "1.0 + 2.0 x**1 + 0.0 x**2 - 1.0 x**3" + ) + + def test_empty_formatstr(self): + poly.set_default_printstyle('ascii') + p = poly.Polynomial([1, 2, 3]) + assert_equal(format(p), "1.0 + 2.0 x**1 + 3.0 x**2") + assert_equal(f"{p}", "1.0 + 2.0 x**1 + 3.0 x**2") + + def test_bad_formatstr(self): + p = poly.Polynomial([1, 2, 0, -1]) + with pytest.raises(ValueError): + format(p, '.2f') + + +class TestRepr: def test_polynomial_str(self): res = repr(poly.Polynomial([0, 1])) tgt = 'Polynomial([0., 1.], domain=[-1, 1], window=[-1, 1])' @@ -66,3 +339,52 @@ def test_laguerre_repr(self): res = repr(poly.Laguerre([0, 1])) tgt = 'Laguerre([0., 1.], domain=[0, 1], window=[0, 1])' assert_equal(res, tgt) + + +class TestLatexRepr: + """Test the latex repr used by Jupyter""" + + def as_latex(self, obj): + # right now we ignore the formatting of scalars in our tests, since + # it makes them too verbose. Ideally, the formatting of scalars will + # be fixed such that tests below continue to pass + obj._repr_latex_scalar = lambda x: str(x) + try: + return obj._repr_latex_() + finally: + del obj._repr_latex_scalar + + def test_simple_polynomial(self): + # default input + p = poly.Polynomial([1, 2, 3]) + assert_equal(self.as_latex(p), + r'$x \mapsto 1.0 + 2.0\,x + 3.0\,x^{2}$') + + # translated input + p = poly.Polynomial([1, 2, 3], domain=[-2, 0]) + assert_equal(self.as_latex(p), + r'$x \mapsto 1.0 + 2.0\,\left(1.0 + x\right) + 3.0\,\left(1.0 + x\right)^{2}$') + + # scaled input + p = poly.Polynomial([1, 2, 3], domain=[-0.5, 0.5]) + assert_equal(self.as_latex(p), + r'$x \mapsto 1.0 + 2.0\,\left(2.0x\right) + 3.0\,\left(2.0x\right)^{2}$') + + # affine input + p = poly.Polynomial([1, 2, 3], domain=[-1, 0]) + assert_equal(self.as_latex(p), + r'$x \mapsto 1.0 + 2.0\,\left(1.0 + 2.0x\right) + 3.0\,\left(1.0 + 2.0x\right)^{2}$') + + def test_basis_func(self): + p = poly.Chebyshev([1, 2, 3]) + assert_equal(self.as_latex(p), + r'$x \mapsto 1.0\,{T}_{0}(x) + 2.0\,{T}_{1}(x) + 3.0\,{T}_{2}(x)$') + # affine input - check no surplus parens are added + p = poly.Chebyshev([1, 2, 3], domain=[-1, 0]) + assert_equal(self.as_latex(p), + r'$x \mapsto 1.0\,{T}_{0}(1.0 + 2.0x) + 2.0\,{T}_{1}(1.0 + 2.0x) + 3.0\,{T}_{2}(1.0 + 2.0x)$') + + def test_multichar_basis_func(self): + p = poly.HermiteE([1, 2, 3]) + assert_equal(self.as_latex(p), + r'$x \mapsto 1.0\,{He}_{0}(x) + 2.0\,{He}_{1}(x) + 3.0\,{He}_{2}(x)$') diff --git a/numpy/py.typed b/numpy/py.typed new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/numpy/random/.gitignore b/numpy/random/.gitignore new file mode 100644 index 000000000000..fea3f955ac5b --- /dev/null +++ b/numpy/random/.gitignore @@ -0,0 +1,3 @@ +# generated files +_bounded_integers.pyx +_bounded_integers.pxd diff --git a/numpy/random/LICENSE.md b/numpy/random/LICENSE.md new file mode 100644 index 000000000000..a6cf1b17e997 --- /dev/null +++ b/numpy/random/LICENSE.md @@ -0,0 +1,71 @@ +**This software is dual-licensed under the The University of Illinois/NCSA +Open Source License (NCSA) and The 3-Clause BSD License** + +# NCSA Open Source License +**Copyright (c) 2019 Kevin Sheppard. All rights reserved.** + +Developed by: Kevin Sheppard (<kevin.sheppard@economics.ox.ac.uk>, +<kevin.k.sheppard@gmail.com>) +[http://www.kevinsheppard.com](http://www.kevinsheppard.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal with +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimers. + +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimers in the documentation and/or +other materials provided with the distribution. + +Neither the names of Kevin Sheppard, nor the names of any contributors may be +used to endorse or promote products derived from this Software without specific +prior written permission. + +**THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH +THE SOFTWARE.** + + +# 3-Clause BSD License +**Copyright (c) 2019 Kevin Sheppard. All rights reserved.** + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +**THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGE.** + +# Components + +Many parts of this module have been derived from original sources, +often the algorithm's designer. Component licenses are located with +the component code. diff --git a/numpy/random/__init__.pxd b/numpy/random/__init__.pxd new file mode 100644 index 000000000000..1f9057296ba9 --- /dev/null +++ b/numpy/random/__init__.pxd @@ -0,0 +1,14 @@ +cimport numpy as np +from libc.stdint cimport uint32_t, uint64_t + +cdef extern from "numpy/random/bitgen.h": + struct bitgen: + void *state + uint64_t (*next_uint64)(void *st) nogil + uint32_t (*next_uint32)(void *st) nogil + double (*next_double)(void *st) nogil + uint64_t (*next_raw)(void *st) nogil + + ctypedef bitgen bitgen_t + +from numpy.random.bit_generator cimport BitGenerator, SeedSequence diff --git a/numpy/random/__init__.py b/numpy/random/__init__.py index 81cb94cc14f9..2e8f99fe3045 100644 --- a/numpy/random/__init__.py +++ b/numpy/random/__init__.py @@ -3,34 +3,68 @@ Random Number Generation ======================== +Use ``default_rng()`` to create a `Generator` and call its methods. + +=============== ========================================================= +Generator +--------------- --------------------------------------------------------- +Generator Class implementing all of the random number distributions +default_rng Default constructor for ``Generator`` +=============== ========================================================= + +============================================= === +BitGenerator Streams that work with Generator +--------------------------------------------- --- +MT19937 +PCG64 +PCG64DXSM +Philox +SFC64 +============================================= === + +============================================= === +Getting entropy to initialize a BitGenerator +--------------------------------------------- --- +SeedSequence +============================================= === + + +Legacy +------ + +For backwards compatibility with previous versions of numpy before 1.17, the +various aliases to the global `RandomState` methods are left alone and do not +use the new `Generator` API. + ==================== ========================================================= Utility functions -============================================================================== -random Uniformly distributed values of a given shape. +-------------------- --------------------------------------------------------- +random Uniformly distributed floats over ``[0, 1)`` bytes Uniformly distributed random bytes. -random_integers Uniformly distributed integers in a given range. -random_sample Uniformly distributed floats in a given range. -random Alias for random_sample -ranf Alias for random_sample -sample Alias for random_sample -choice Generate a weighted random sample from a given array-like permutation Randomly permute a sequence / generate a random sequence. shuffle Randomly permute a sequence in place. -seed Seed the random number generator. +choice Random sample from 1-D array. ==================== ========================================================= ==================== ========================================================= -Compatibility functions -============================================================================== +Compatibility +functions - removed +in the new API +-------------------- --------------------------------------------------------- rand Uniformly distributed values. randn Normally distributed values. ranf Uniformly distributed floating point numbers. -randint Uniformly distributed integers in a given range. +random_integers Uniformly distributed integers in a given range. + (deprecated, use ``integers(..., closed=True)`` instead) +random_sample Alias for `random_sample` +randint Uniformly distributed integers in a given range +seed Seed the legacy random number generator. ==================== ========================================================= ==================== ========================================================= -Univariate distributions -============================================================================== +Univariate +distributions +-------------------- --------------------------------------------------------- beta Beta distribution over ``[0, 1]``. binomial Binomial distribution. chisquare :math:`\\chi^2` distribution. @@ -60,17 +94,19 @@ zipf Zipf's distribution over ranked data. ==================== ========================================================= -==================== ========================================================= -Multivariate distributions -============================================================================== +==================== ========================================================== +Multivariate +distributions +-------------------- ---------------------------------------------------------- dirichlet Multivariate generalization of Beta distribution. multinomial Multivariate generalization of the binomial distribution. multivariate_normal Multivariate generalization of the normal distribution. -==================== ========================================================= +==================== ========================================================== ==================== ========================================================= -Standard distributions -============================================================================== +Standard +distributions +-------------------- --------------------------------------------------------- standard_cauchy Standard Cauchy-Lorentz distribution. standard_exponential Standard exponential distribution. standard_gamma Standard Gamma distribution. @@ -80,43 +116,100 @@ ==================== ========================================================= Internal functions -============================================================================== +-------------------- --------------------------------------------------------- get_state Get tuple representing internal state of generator. set_state Set state of generator. ==================== ========================================================= -""" -from __future__ import division, absolute_import, print_function -import warnings - -# To get sub-modules -from .info import __doc__, __all__ - - -with warnings.catch_warnings(): - warnings.filterwarnings("ignore", message="numpy.ndarray size changed") - from .mtrand import * +""" +__all__ = [ + 'beta', + 'binomial', + 'bytes', + 'chisquare', + 'choice', + 'dirichlet', + 'exponential', + 'f', + 'gamma', + 'geometric', + 'get_state', + 'gumbel', + 'hypergeometric', + 'laplace', + 'logistic', + 'lognormal', + 'logseries', + 'multinomial', + 'multivariate_normal', + 'negative_binomial', + 'noncentral_chisquare', + 'noncentral_f', + 'normal', + 'pareto', + 'permutation', + 'poisson', + 'power', + 'rand', + 'randint', + 'randn', + 'random', + 'random_integers', + 'random_sample', + 'ranf', + 'rayleigh', + 'sample', + 'seed', + 'set_state', + 'shuffle', + 'standard_cauchy', + 'standard_exponential', + 'standard_gamma', + 'standard_normal', + 'standard_t', + 'triangular', + 'uniform', + 'vonmises', + 'wald', + 'weibull', + 'zipf', +] + +# add these for module-freeze analysis (like PyInstaller) +from . import _pickle +from . import _common +from . import _bounded_integers + +from ._generator import Generator, default_rng +from .bit_generator import SeedSequence, BitGenerator +from ._mt19937 import MT19937 +from ._pcg64 import PCG64, PCG64DXSM +from ._philox import Philox +from ._sfc64 import SFC64 +from .mtrand import * + +__all__ += ['Generator', 'RandomState', 'SeedSequence', 'MT19937', + 'Philox', 'PCG64', 'PCG64DXSM', 'SFC64', 'default_rng', + 'BitGenerator'] -# Some aliases: -ranf = random = sample = random_sample -__all__.extend(['ranf', 'random', 'sample']) def __RandomState_ctor(): """Return a RandomState instance. This function exists solely to assist (un)pickling. - Note that the state of the RandomState returned here is irrelevant, as this function's - entire purpose is to return a newly allocated RandomState whose state pickle can set. - Consequently the RandomState returned by this function is a freshly allocated copy - with a seed=0. + Note that the state of the RandomState returned here is irrelevant, as this + function's entire purpose is to return a newly allocated RandomState whose + state pickle can set. Consequently the RandomState returned by this function + is a freshly allocated copy with a seed=0. See https://github.com/numpy/numpy/issues/4763 for a detailed discussion """ return RandomState(seed=0) -from numpy.testing._private.pytesttester import PytestTester + +from numpy._pytesttester import PytestTester test = PytestTester(__name__) del PytestTester diff --git a/numpy/random/__init__.pyi b/numpy/random/__init__.pyi new file mode 100644 index 000000000000..bf6147697b2d --- /dev/null +++ b/numpy/random/__init__.pyi @@ -0,0 +1,72 @@ +from typing import List + +from numpy._pytesttester import PytestTester + +from numpy.random._generator import Generator as Generator +from numpy.random._generator import default_rng as default_rng +from numpy.random._mt19937 import MT19937 as MT19937 +from numpy.random._pcg64 import ( + PCG64 as PCG64, + PCG64DXSM as PCG64DXSM, +) +from numpy.random._philox import Philox as Philox +from numpy.random._sfc64 import SFC64 as SFC64 +from numpy.random.bit_generator import BitGenerator as BitGenerator +from numpy.random.bit_generator import SeedSequence as SeedSequence +from numpy.random.mtrand import ( + RandomState as RandomState, + beta as beta, + binomial as binomial, + bytes as bytes, + chisquare as chisquare, + choice as choice, + dirichlet as dirichlet, + exponential as exponential, + f as f, + gamma as gamma, + geometric as geometric, + get_state as get_state, + gumbel as gumbel, + hypergeometric as hypergeometric, + laplace as laplace, + logistic as logistic, + lognormal as lognormal, + logseries as logseries, + multinomial as multinomial, + multivariate_normal as multivariate_normal, + negative_binomial as negative_binomial, + noncentral_chisquare as noncentral_chisquare, + noncentral_f as noncentral_f, + normal as normal, + pareto as pareto, + permutation as permutation, + poisson as poisson, + power as power, + rand as rand, + randint as randint, + randn as randn, + random as random, + random_integers as random_integers, + random_sample as random_sample, + ranf as ranf, + rayleigh as rayleigh, + sample as sample, + seed as seed, + set_state as set_state, + shuffle as shuffle, + standard_cauchy as standard_cauchy, + standard_exponential as standard_exponential, + standard_gamma as standard_gamma, + standard_normal as standard_normal, + standard_t as standard_t, + triangular as triangular, + uniform as uniform, + vonmises as vonmises, + wald as wald, + weibull as weibull, + zipf as zipf, +) + +__all__: List[str] +__path__: List[str] +test: PytestTester diff --git a/numpy/random/_bounded_integers.pxd.in b/numpy/random/_bounded_integers.pxd.in new file mode 100644 index 000000000000..5ae5a806715c --- /dev/null +++ b/numpy/random/_bounded_integers.pxd.in @@ -0,0 +1,26 @@ +from libc.stdint cimport (uint8_t, uint16_t, uint32_t, uint64_t, + int8_t, int16_t, int32_t, int64_t, intptr_t) +import numpy as np +cimport numpy as np +ctypedef np.npy_bool bool_t + +from numpy.random cimport bitgen_t + +cdef inline uint64_t _gen_mask(uint64_t max_val) nogil: + """Mask generator for use in bounded random numbers""" + # Smallest bit mask >= max + cdef uint64_t mask = max_val + mask |= mask >> 1 + mask |= mask >> 2 + mask |= mask >> 4 + mask |= mask >> 8 + mask |= mask >> 16 + mask |= mask >> 32 + return mask +{{ +py: +inttypes = ('uint64','uint32','uint16','uint8','bool','int64','int32','int16','int8') +}} +{{for inttype in inttypes}} +cdef object _rand_{{inttype}}(object low, object high, object size, bint use_masked, bint closed, bitgen_t *state, object lock) +{{endfor}} diff --git a/numpy/random/_bounded_integers.pyx.in b/numpy/random/_bounded_integers.pyx.in new file mode 100644 index 000000000000..7eb6aff1e9f1 --- /dev/null +++ b/numpy/random/_bounded_integers.pyx.in @@ -0,0 +1,343 @@ +#!python +#cython: wraparound=False, nonecheck=False, boundscheck=False, cdivision=True + +import numpy as np +cimport numpy as np + +__all__ = [] + +np.import_array() + + +cdef extern from "numpy/random/distributions.h": + # Generate random numbers in closed interval [off, off + rng]. + uint64_t random_bounded_uint64(bitgen_t *bitgen_state, + uint64_t off, uint64_t rng, + uint64_t mask, bint use_masked) nogil + uint32_t random_buffered_bounded_uint32(bitgen_t *bitgen_state, + uint32_t off, uint32_t rng, + uint32_t mask, bint use_masked, + int *bcnt, uint32_t *buf) nogil + uint16_t random_buffered_bounded_uint16(bitgen_t *bitgen_state, + uint16_t off, uint16_t rng, + uint16_t mask, bint use_masked, + int *bcnt, uint32_t *buf) nogil + uint8_t random_buffered_bounded_uint8(bitgen_t *bitgen_state, + uint8_t off, uint8_t rng, + uint8_t mask, bint use_masked, + int *bcnt, uint32_t *buf) nogil + np.npy_bool random_buffered_bounded_bool(bitgen_t *bitgen_state, + np.npy_bool off, np.npy_bool rng, + np.npy_bool mask, bint use_masked, + int *bcnt, uint32_t *buf) nogil + void random_bounded_uint64_fill(bitgen_t *bitgen_state, + uint64_t off, uint64_t rng, np.npy_intp cnt, + bint use_masked, + uint64_t *out) nogil + void random_bounded_uint32_fill(bitgen_t *bitgen_state, + uint32_t off, uint32_t rng, np.npy_intp cnt, + bint use_masked, + uint32_t *out) nogil + void random_bounded_uint16_fill(bitgen_t *bitgen_state, + uint16_t off, uint16_t rng, np.npy_intp cnt, + bint use_masked, + uint16_t *out) nogil + void random_bounded_uint8_fill(bitgen_t *bitgen_state, + uint8_t off, uint8_t rng, np.npy_intp cnt, + bint use_masked, + uint8_t *out) nogil + void random_bounded_bool_fill(bitgen_t *bitgen_state, + np.npy_bool off, np.npy_bool rng, np.npy_intp cnt, + bint use_masked, + np.npy_bool *out) nogil + + +cdef object format_bounds_error(bint closed, object low): + # Special case low == 0 to provide a better exception for users + # since low = 0 is the default single-argument case. + if not np.any(low): + comp = '<' if closed else '<=' + return f'high {comp} 0' + else: + comp = '>' if closed else '>=' + return f'low {comp} high' + + +{{ +py: +type_info = (('uint32', 'uint32', 'uint64', 'NPY_UINT64', 0, 0, 0, '0X100000000ULL'), + ('uint16', 'uint16', 'uint32', 'NPY_UINT32', 1, 16, 0, '0X10000UL'), + ('uint8', 'uint8', 'uint16', 'NPY_UINT16', 3, 8, 0, '0X100UL'), + ('bool','bool', 'uint8', 'NPY_UINT8', 31, 1, 0, '0x2UL'), + ('int32', 'uint32', 'uint64', 'NPY_INT64', 0, 0, '-0x80000000LL', '0x80000000LL'), + ('int16', 'uint16', 'uint32', 'NPY_INT32', 1, 16, '-0x8000LL', '0x8000LL' ), + ('int8', 'uint8', 'uint16', 'NPY_INT16', 3, 8, '-0x80LL', '0x80LL' ), +)}} +{{for nptype, utype, nptype_up, npctype, remaining, bitshift, lb, ub in type_info}} +{{ py: otype = nptype + '_' if nptype == 'bool' else nptype }} +cdef object _rand_{{nptype}}_broadcast(np.ndarray low, np.ndarray high, object size, + bint use_masked, bint closed, + bitgen_t *state, object lock): + """ + Array path for smaller integer types + + This path is simpler since the high value in the open interval [low, high) + must be in-range for the next larger type, {{nptype_up}}. Here we case to + this type for checking and the recast to {{nptype}} when producing the + random integers. + """ + cdef {{utype}}_t rng, last_rng, off, val, mask, out_val, is_open + cdef uint32_t buf + cdef {{utype}}_t *out_data + cdef {{nptype_up}}_t low_v, high_v + cdef np.ndarray low_arr, high_arr, out_arr + cdef np.npy_intp i, cnt + cdef np.broadcast it + cdef int buf_rem = 0 + + # Array path + is_open = not closed + low_arr = <np.ndarray>low + high_arr = <np.ndarray>high + if np.any(np.less(low_arr, {{lb}})): + raise ValueError('low is out of bounds for {{nptype}}') + if closed: + high_comp = np.greater_equal + low_high_comp = np.greater + else: + high_comp = np.greater + low_high_comp = np.greater_equal + + if np.any(high_comp(high_arr, {{ub}})): + raise ValueError('high is out of bounds for {{nptype}}') + if np.any(low_high_comp(low_arr, high_arr)): + raise ValueError(format_bounds_error(closed, low_arr)) + + low_arr = <np.ndarray>np.PyArray_FROM_OTF(low, np.{{npctype}}, np.NPY_ALIGNED | np.NPY_FORCECAST) + high_arr = <np.ndarray>np.PyArray_FROM_OTF(high, np.{{npctype}}, np.NPY_ALIGNED | np.NPY_FORCECAST) + + if size is not None: + out_arr = <np.ndarray>np.empty(size, np.{{otype}}) + else: + it = np.PyArray_MultiIterNew2(low_arr, high_arr) + out_arr = <np.ndarray>np.empty(it.shape, np.{{otype}}) + + it = np.PyArray_MultiIterNew3(low_arr, high_arr, out_arr) + out_data = <{{utype}}_t *>np.PyArray_DATA(out_arr) + cnt = np.PyArray_SIZE(out_arr) + mask = last_rng = 0 + with lock, nogil: + for i in range(cnt): + low_v = (<{{nptype_up}}_t*>np.PyArray_MultiIter_DATA(it, 0))[0] + high_v = (<{{nptype_up}}_t*>np.PyArray_MultiIter_DATA(it, 1))[0] + # Subtract 1 since generator produces values on the closed int [off, off+rng] + rng = <{{utype}}_t>((high_v - is_open) - low_v) + off = <{{utype}}_t>(<{{nptype_up}}_t>low_v) + + if rng != last_rng: + # Smallest bit mask >= max + mask = <{{utype}}_t>_gen_mask(rng) + + out_data[i] = random_buffered_bounded_{{utype}}(state, off, rng, mask, use_masked, &buf_rem, &buf) + + np.PyArray_MultiIter_NEXT(it) + return out_arr +{{endfor}} +{{ +py: +big_type_info = (('uint64', 'uint64', 'NPY_UINT64', '0x0ULL', '0xFFFFFFFFFFFFFFFFULL'), + ('int64', 'uint64', 'NPY_INT64', '-0x8000000000000000LL', '0x7FFFFFFFFFFFFFFFLL' ) +)}} +{{for nptype, utype, npctype, lb, ub in big_type_info}} +{{ py: otype = nptype}} +cdef object _rand_{{nptype}}_broadcast(object low, object high, object size, + bint use_masked, bint closed, + bitgen_t *state, object lock): + """ + Array path for 64-bit integer types + + Requires special treatment since the high value can be out-of-range for + the largest (64 bit) integer type since the generator is specified on the + interval [low,high). + + The internal generator does not have this issue since it generates from + the closes interval [low, high-1] and high-1 is always in range for the + 64 bit integer type. + """ + + cdef np.ndarray low_arr, high_arr, out_arr, highm1_arr + cdef np.npy_intp i, cnt, n + cdef np.broadcast it + cdef object closed_upper + cdef uint64_t *out_data + cdef {{nptype}}_t *highm1_data + cdef {{nptype}}_t low_v, high_v + cdef uint64_t rng, last_rng, val, mask, off, out_val + + low_arr = <np.ndarray>low + high_arr = <np.ndarray>high + + if np.any(np.less(low_arr, {{lb}})): + raise ValueError('low is out of bounds for {{nptype}}') + dt = high_arr.dtype + if closed or np.issubdtype(dt, np.integer): + # Avoid object dtype path if already an integer + high_lower_comp = np.less if closed else np.less_equal + if np.any(high_lower_comp(high_arr, {{lb}})): + raise ValueError(format_bounds_error(closed, low_arr)) + high_m1 = high_arr if closed else high_arr - dt.type(1) + if np.any(np.greater(high_m1, {{ub}})): + raise ValueError('high is out of bounds for {{nptype}}') + highm1_arr = <np.ndarray>np.PyArray_FROM_OTF(high_m1, np.{{npctype}}, np.NPY_ALIGNED | np.NPY_FORCECAST) + else: + # If input is object or a floating type + highm1_arr = <np.ndarray>np.empty_like(high_arr, dtype=np.{{otype}}) + highm1_data = <{{nptype}}_t *>np.PyArray_DATA(highm1_arr) + cnt = np.PyArray_SIZE(high_arr) + flat = high_arr.flat + for i in range(cnt): + # Subtract 1 since generator produces values on the closed int [off, off+rng] + closed_upper = int(flat[i]) - 1 + if closed_upper > {{ub}}: + raise ValueError('high is out of bounds for {{nptype}}') + if closed_upper < {{lb}}: + raise ValueError(format_bounds_error(closed, low_arr)) + highm1_data[i] = <{{nptype}}_t>closed_upper + + if np.any(np.greater(low_arr, highm1_arr)): + raise ValueError(format_bounds_error(closed, low_arr)) + + high_arr = highm1_arr + low_arr = <np.ndarray>np.PyArray_FROM_OTF(low, np.{{npctype}}, np.NPY_ALIGNED | np.NPY_FORCECAST) + + if size is not None: + out_arr = <np.ndarray>np.empty(size, np.{{otype}}) + else: + it = np.PyArray_MultiIterNew2(low_arr, high_arr) + out_arr = <np.ndarray>np.empty(it.shape, np.{{otype}}) + + it = np.PyArray_MultiIterNew3(low_arr, high_arr, out_arr) + out_data = <uint64_t *>np.PyArray_DATA(out_arr) + n = np.PyArray_SIZE(out_arr) + mask = last_rng = 0 + with lock, nogil: + for i in range(n): + low_v = (<{{nptype}}_t*>np.PyArray_MultiIter_DATA(it, 0))[0] + high_v = (<{{nptype}}_t*>np.PyArray_MultiIter_DATA(it, 1))[0] + # Generator produces values on the closed int [off, off+rng], -1 subtracted above + rng = <{{utype}}_t>(high_v - low_v) + off = <{{utype}}_t>(<{{nptype}}_t>low_v) + + if rng != last_rng: + mask = _gen_mask(rng) + out_data[i] = random_bounded_uint64(state, off, rng, mask, use_masked) + + np.PyArray_MultiIter_NEXT(it) + + return out_arr +{{endfor}} +{{ +py: +type_info = (('uint64', 'uint64', '0x0ULL', '0xFFFFFFFFFFFFFFFFULL'), + ('uint32', 'uint32', '0x0UL', '0XFFFFFFFFUL'), + ('uint16', 'uint16', '0x0UL', '0XFFFFUL'), + ('uint8', 'uint8', '0x0UL', '0XFFUL'), + ('bool', 'bool', '0x0UL', '0x1UL'), + ('int64', 'uint64', '-0x8000000000000000LL', '0x7FFFFFFFFFFFFFFFL'), + ('int32', 'uint32', '-0x80000000L', '0x7FFFFFFFL'), + ('int16', 'uint16', '-0x8000L', '0x7FFFL' ), + ('int8', 'uint8', '-0x80L', '0x7FL' ) +)}} +{{for nptype, utype, lb, ub in type_info}} +{{ py: otype = nptype + '_' if nptype == 'bool' else nptype }} +cdef object _rand_{{nptype}}(object low, object high, object size, + bint use_masked, bint closed, + bitgen_t *state, object lock): + """ + _rand_{{nptype}}(low, high, size, use_masked, *state, lock) + + Return random `np.{{otype}}` integers from `low` (inclusive) to `high` (exclusive). + + Return random integers from the "discrete uniform" distribution in the + interval [`low`, `high`). If `high` is None (the default), + then results are from [0, `low`). On entry the arguments are presumed + to have been validated for size and order for the `np.{{otype}}` type. + + Parameters + ---------- + low : int or array-like + Lowest (signed) integer to be drawn from the distribution (unless + ``high=None``, in which case this parameter is the *highest* such + integer). + high : int or array-like + If provided, one above the largest (signed) integer to be drawn from the + distribution (see above for behavior if ``high=None``). + size : int or tuple of ints + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. Default is None, in which case a + single value is returned. + use_masked : bool + If True then rejection sampling with a range mask is used else Lemire's algorithm is used. + closed : bool + If True then sample from [low, high]. If False, sample [low, high) + state : bit generator + Bit generator state to use in the core random number generators + lock : threading.Lock + Lock to prevent multiple using a single generator simultaneously + + Returns + ------- + out : python scalar or ndarray of np.{{otype}} + `size`-shaped array of random integers from the appropriate + distribution, or a single such random int if `size` not provided. + + Notes + ----- + The internal integer generator produces values from the closed + interval [low, high-(not closed)]. This requires some care since + high can be out-of-range for {{utype}}. The scalar path leaves + integers as Python integers until the 1 has been subtracted to + avoid needing to cast to a larger type. + """ + cdef np.ndarray out_arr, low_arr, high_arr + cdef {{utype}}_t rng, off, out_val + cdef {{utype}}_t *out_data + cdef np.npy_intp i, n, cnt + + if size is not None: + if (np.prod(size) == 0): + return np.empty(size, dtype=np.{{otype}}) + + low_arr = <np.ndarray>np.array(low, copy=False) + high_arr = <np.ndarray>np.array(high, copy=False) + low_ndim = np.PyArray_NDIM(low_arr) + high_ndim = np.PyArray_NDIM(high_arr) + if low_ndim == 0 and high_ndim == 0: + low = int(low_arr) + high = int(high_arr) + # Subtract 1 since internal generator produces on closed interval [low, high] + if not closed: + high -= 1 + + if low < {{lb}}: + raise ValueError("low is out of bounds for {{nptype}}") + if high > {{ub}}: + raise ValueError("high is out of bounds for {{nptype}}") + if low > high: # -1 already subtracted, closed interval + raise ValueError(format_bounds_error(closed, low)) + + rng = <{{utype}}_t>(high - low) + off = <{{utype}}_t>(<{{nptype}}_t>low) + if size is None: + with lock: + random_bounded_{{utype}}_fill(state, off, rng, 1, use_masked, &out_val) + return np.{{otype}}(<{{nptype}}_t>out_val) + else: + out_arr = <np.ndarray>np.empty(size, np.{{otype}}) + cnt = np.PyArray_SIZE(out_arr) + out_data = <{{utype}}_t *>np.PyArray_DATA(out_arr) + with lock, nogil: + random_bounded_{{utype}}_fill(state, off, rng, cnt, use_masked, out_data) + return out_arr + return _rand_{{nptype}}_broadcast(low_arr, high_arr, size, use_masked, closed, state, lock) +{{endfor}} diff --git a/numpy/random/_common.pxd b/numpy/random/_common.pxd new file mode 100644 index 000000000000..9f2e8c3ca117 --- /dev/null +++ b/numpy/random/_common.pxd @@ -0,0 +1,106 @@ +#cython: language_level=3 + +from libc.stdint cimport uint32_t, uint64_t, int32_t, int64_t + +import numpy as np +cimport numpy as np + +from numpy.random cimport bitgen_t + +cdef double POISSON_LAM_MAX +cdef double LEGACY_POISSON_LAM_MAX +cdef uint64_t MAXSIZE + +cdef enum ConstraintType: + CONS_NONE + CONS_NON_NEGATIVE + CONS_POSITIVE + CONS_POSITIVE_NOT_NAN + CONS_BOUNDED_0_1 + CONS_BOUNDED_0_1_NOTNAN + CONS_BOUNDED_GT_0_1 + CONS_GT_1 + CONS_GTE_1 + CONS_POISSON + LEGACY_CONS_POISSON + +ctypedef ConstraintType constraint_type + +cdef object benchmark(bitgen_t *bitgen, object lock, Py_ssize_t cnt, object method) +cdef object random_raw(bitgen_t *bitgen, object lock, object size, object output) +cdef object prepare_cffi(bitgen_t *bitgen) +cdef object prepare_ctypes(bitgen_t *bitgen) +cdef int check_constraint(double val, object name, constraint_type cons) except -1 +cdef int check_array_constraint(np.ndarray val, object name, constraint_type cons) except -1 + +cdef extern from "include/aligned_malloc.h": + cdef void *PyArray_realloc_aligned(void *p, size_t n) + cdef void *PyArray_malloc_aligned(size_t n) + cdef void *PyArray_calloc_aligned(size_t n, size_t s) + cdef void PyArray_free_aligned(void *p) + +ctypedef void (*random_double_fill)(bitgen_t *state, np.npy_intp count, double* out) nogil +ctypedef double (*random_double_0)(void *state) nogil +ctypedef double (*random_double_1)(void *state, double a) nogil +ctypedef double (*random_double_2)(void *state, double a, double b) nogil +ctypedef double (*random_double_3)(void *state, double a, double b, double c) nogil + +ctypedef double (*random_float_fill)(bitgen_t *state, np.npy_intp count, float* out) nogil +ctypedef float (*random_float_0)(bitgen_t *state) nogil +ctypedef float (*random_float_1)(bitgen_t *state, float a) nogil + +ctypedef int64_t (*random_uint_0)(void *state) nogil +ctypedef int64_t (*random_uint_d)(void *state, double a) nogil +ctypedef int64_t (*random_uint_dd)(void *state, double a, double b) nogil +ctypedef int64_t (*random_uint_di)(void *state, double a, uint64_t b) nogil +ctypedef int64_t (*random_uint_i)(void *state, int64_t a) nogil +ctypedef int64_t (*random_uint_iii)(void *state, int64_t a, int64_t b, int64_t c) nogil + +ctypedef uint32_t (*random_uint_0_32)(bitgen_t *state) nogil +ctypedef uint32_t (*random_uint_1_i_32)(bitgen_t *state, uint32_t a) nogil + +ctypedef int32_t (*random_int_2_i_32)(bitgen_t *state, int32_t a, int32_t b) nogil +ctypedef int64_t (*random_int_2_i)(bitgen_t *state, int64_t a, int64_t b) nogil + +cdef double kahan_sum(double *darr, np.npy_intp n) + +cdef inline double uint64_to_double(uint64_t rnd) nogil: + return (rnd >> 11) * (1.0 / 9007199254740992.0) + +cdef object double_fill(void *func, bitgen_t *state, object size, object lock, object out) + +cdef object float_fill(void *func, bitgen_t *state, object size, object lock, object out) + +cdef object float_fill_from_double(void *func, bitgen_t *state, object size, object lock, object out) + +cdef object wrap_int(object val, object bits) + +cdef np.ndarray int_to_array(object value, object name, object bits, object uint_size) + +cdef validate_output_shape(iter_shape, np.ndarray output) + +cdef object cont(void *func, void *state, object size, object lock, int narg, + object a, object a_name, constraint_type a_constraint, + object b, object b_name, constraint_type b_constraint, + object c, object c_name, constraint_type c_constraint, + object out) + +cdef object disc(void *func, void *state, object size, object lock, + int narg_double, int narg_int64, + object a, object a_name, constraint_type a_constraint, + object b, object b_name, constraint_type b_constraint, + object c, object c_name, constraint_type c_constraint) + +cdef object cont_f(void *func, bitgen_t *state, object size, object lock, + object a, object a_name, constraint_type a_constraint, + object out) + +cdef object cont_broadcast_3(void *func, void *state, object size, object lock, + np.ndarray a_arr, object a_name, constraint_type a_constraint, + np.ndarray b_arr, object b_name, constraint_type b_constraint, + np.ndarray c_arr, object c_name, constraint_type c_constraint) + +cdef object discrete_broadcast_iii(void *func, void *state, object size, object lock, + np.ndarray a_arr, object a_name, constraint_type a_constraint, + np.ndarray b_arr, object b_name, constraint_type b_constraint, + np.ndarray c_arr, object c_name, constraint_type c_constraint) diff --git a/numpy/random/_common.pyx b/numpy/random/_common.pyx new file mode 100644 index 000000000000..ad43f281226a --- /dev/null +++ b/numpy/random/_common.pyx @@ -0,0 +1,1028 @@ +#!python +#cython: wraparound=False, nonecheck=False, boundscheck=False, cdivision=True, language_level=3 +from collections import namedtuple +from cpython cimport PyFloat_AsDouble +import sys +import numpy as np +cimport numpy as np + +from libc.stdint cimport uintptr_t + +__all__ = ['interface'] + +np.import_array() + +interface = namedtuple('interface', ['state_address', 'state', 'next_uint64', + 'next_uint32', 'next_double', + 'bit_generator']) + +cdef double LEGACY_POISSON_LAM_MAX = <double>np.iinfo('l').max - np.sqrt(np.iinfo('l').max)*10 +cdef double POISSON_LAM_MAX = <double>np.iinfo('int64').max - np.sqrt(np.iinfo('int64').max)*10 + +cdef uint64_t MAXSIZE = <uint64_t>sys.maxsize + + +cdef object benchmark(bitgen_t *bitgen, object lock, Py_ssize_t cnt, object method): + """Benchmark command used by BitGenerator""" + cdef Py_ssize_t i + if method=='uint64': + with lock, nogil: + for i in range(cnt): + bitgen.next_uint64(bitgen.state) + elif method=='double': + with lock, nogil: + for i in range(cnt): + bitgen.next_double(bitgen.state) + else: + raise ValueError('Unknown method') + + +cdef object random_raw(bitgen_t *bitgen, object lock, object size, object output): + """ + random_raw(self, size=None) + + Return randoms as generated by the underlying PRNG + + Parameters + ---------- + bitgen : BitGenerator + Address of the bit generator struct + lock : Threading.Lock + Lock provided by the bit generator + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. Default is None, in which case a + single value is returned. + output : bool, optional + Output values. Used for performance testing since the generated + values are not returned. + + Returns + ------- + out : uint or ndarray + Drawn samples. + + Notes + ----- + This method directly exposes the the raw underlying pseudo-random + number generator. All values are returned as unsigned 64-bit + values irrespective of the number of bits produced by the PRNG. + + See the class docstring for the number of bits returned. + """ + cdef np.ndarray randoms + cdef uint64_t *randoms_data + cdef Py_ssize_t i, n + + if not output: + if size is None: + with lock: + bitgen.next_raw(bitgen.state) + return None + n = np.asarray(size).sum() + with lock, nogil: + for i in range(n): + bitgen.next_raw(bitgen.state) + return None + + if size is None: + with lock: + return bitgen.next_raw(bitgen.state) + + randoms = <np.ndarray>np.empty(size, np.uint64) + randoms_data = <uint64_t*>np.PyArray_DATA(randoms) + n = np.PyArray_SIZE(randoms) + + with lock, nogil: + for i in range(n): + randoms_data[i] = bitgen.next_raw(bitgen.state) + return randoms + +cdef object prepare_cffi(bitgen_t *bitgen): + """ + Bundles the interfaces to interact with a BitGenerator using cffi + + Parameters + ---------- + bitgen : pointer + A pointer to a BitGenerator instance + + Returns + ------- + interface : namedtuple + The functions required to interface with the BitGenerator using cffi + + * state_address - Memory address of the state struct + * state - pointer to the state struct + * next_uint64 - function pointer to produce 64 bit integers + * next_uint32 - function pointer to produce 32 bit integers + * next_double - function pointer to produce doubles + * bit_generator - pointer to the BitGenerator struct + """ + try: + import cffi + except ImportError as e: + raise ImportError('cffi cannot be imported.') from e + + ffi = cffi.FFI() + _cffi = interface(<uintptr_t>bitgen.state, + ffi.cast('void *', <uintptr_t>bitgen.state), + ffi.cast('uint64_t (*)(void *)', <uintptr_t>bitgen.next_uint64), + ffi.cast('uint32_t (*)(void *)', <uintptr_t>bitgen.next_uint32), + ffi.cast('double (*)(void *)', <uintptr_t>bitgen.next_double), + ffi.cast('void *', <uintptr_t>bitgen)) + return _cffi + +cdef object prepare_ctypes(bitgen_t *bitgen): + """ + Bundles the interfaces to interact with a BitGenerator using ctypes + + Parameters + ---------- + bitgen : pointer + A pointer to a BitGenerator instance + + Returns + ------- + interface : namedtuple + The functions required to interface with the BitGenerator using ctypes: + + * state_address - Memory address of the state struct + * state - pointer to the state struct + * next_uint64 - function pointer to produce 64 bit integers + * next_uint32 - function pointer to produce 32 bit integers + * next_double - function pointer to produce doubles + * bit_generator - pointer to the BitGenerator struct + """ + import ctypes + + _ctypes = interface(<uintptr_t>bitgen.state, + ctypes.c_void_p(<uintptr_t>bitgen.state), + ctypes.cast(<uintptr_t>bitgen.next_uint64, + ctypes.CFUNCTYPE(ctypes.c_uint64, + ctypes.c_void_p)), + ctypes.cast(<uintptr_t>bitgen.next_uint32, + ctypes.CFUNCTYPE(ctypes.c_uint32, + ctypes.c_void_p)), + ctypes.cast(<uintptr_t>bitgen.next_double, + ctypes.CFUNCTYPE(ctypes.c_double, + ctypes.c_void_p)), + ctypes.c_void_p(<uintptr_t>bitgen)) + return _ctypes + +cdef double kahan_sum(double *darr, np.npy_intp n): + cdef double c, y, t, sum + cdef np.npy_intp i + sum = darr[0] + c = 0.0 + for i in range(1, n): + y = darr[i] - c + t = sum + y + c = (t-sum) - y + sum = t + return sum + + +cdef object wrap_int(object val, object bits): + """Wraparound to place an integer into the interval [0, 2**bits)""" + mask = ~(~int(0) << bits) + return val & mask + + +cdef np.ndarray int_to_array(object value, object name, object bits, object uint_size): + """Convert a large integer to an array of unsigned integers""" + len = bits // uint_size + value = np.asarray(value) + if uint_size == 32: + dtype = np.uint32 + elif uint_size == 64: + dtype = np.uint64 + else: + raise ValueError('Unknown uint_size') + if value.shape == (): + value = int(value) + upper = int(2)**int(bits) + if value < 0 or value >= upper: + raise ValueError('{name} must be positive and ' + 'less than 2**{bits}.'.format(name=name, bits=bits)) + + out = np.empty(len, dtype=dtype) + for i in range(len): + out[i] = value % 2**int(uint_size) + value >>= int(uint_size) + else: + out = value.astype(dtype) + if out.shape != (len,): + raise ValueError('{name} must have {len} elements when using ' + 'array form'.format(name=name, len=len)) + return out + + +cdef validate_output_shape(iter_shape, np.ndarray output): + cdef np.npy_intp *dims + cdef np.npy_intp ndim, i + cdef bint error + dims = np.PyArray_DIMS(output) + ndim = np.PyArray_NDIM(output) + output_shape = tuple((dims[i] for i in range(ndim))) + if iter_shape != output_shape: + raise ValueError( + f"Output size {output_shape} is not compatible with broadcast " + f"dimensions of inputs {iter_shape}." + ) + + +cdef check_output(object out, object dtype, object size, bint require_c_array): + """ + Check user-supplied output array properties and shape + + Parameters + ---------- + out : {ndarray, None} + The array to check. If None, returns immediately. + dtype : dtype + The required dtype of out. + size : {None, int, tuple[int]} + The size passed. If out is an ndarray, verifies that the shape of out + matches size. + require_c_array : bool + Whether out must be a C-array. If False, out can be either C- or F- + ordered. If True, must be C-ordered. In either case, must be + contiguous, writable, aligned and in native byte-order. + """ + if out is None: + return + cdef np.ndarray out_array = <np.ndarray>out + if not (np.PyArray_ISCARRAY(out_array) or + (np.PyArray_ISFARRAY(out_array) and not require_c_array)): + req = "C-" if require_c_array else "" + raise ValueError( + f'Supplied output array must be {req}contiguous, writable, ' + f'aligned, and in machine byte-order.' + ) + if out_array.dtype != dtype: + raise TypeError('Supplied output array has the wrong type. ' + 'Expected {0}, got {1}'.format(np.dtype(dtype), out_array.dtype)) + if size is not None: + try: + tup_size = tuple(size) + except TypeError: + tup_size = tuple([size]) + if tup_size != out.shape: + raise ValueError('size must match out.shape when used together') + + +cdef object double_fill(void *func, bitgen_t *state, object size, object lock, object out): + cdef random_double_fill random_func = (<random_double_fill>func) + cdef double out_val + cdef double *out_array_data + cdef np.ndarray out_array + cdef np.npy_intp i, n + + if size is None and out is None: + with lock: + random_func(state, 1, &out_val) + return out_val + + if out is not None: + check_output(out, np.float64, size, False) + out_array = <np.ndarray>out + else: + out_array = <np.ndarray>np.empty(size, np.double) + + n = np.PyArray_SIZE(out_array) + out_array_data = <double *>np.PyArray_DATA(out_array) + with lock, nogil: + random_func(state, n, out_array_data) + return out_array + +cdef object float_fill(void *func, bitgen_t *state, object size, object lock, object out): + cdef random_float_fill random_func = (<random_float_fill>func) + cdef float out_val + cdef float *out_array_data + cdef np.ndarray out_array + cdef np.npy_intp i, n + + if size is None and out is None: + with lock: + random_func(state, 1, &out_val) + return out_val + + if out is not None: + check_output(out, np.float32, size, False) + out_array = <np.ndarray>out + else: + out_array = <np.ndarray>np.empty(size, np.float32) + + n = np.PyArray_SIZE(out_array) + out_array_data = <float *>np.PyArray_DATA(out_array) + with lock, nogil: + random_func(state, n, out_array_data) + return out_array + +cdef object float_fill_from_double(void *func, bitgen_t *state, object size, object lock, object out): + cdef random_double_0 random_func = (<random_double_0>func) + cdef float *out_array_data + cdef np.ndarray out_array + cdef np.npy_intp i, n + + if size is None and out is None: + with lock: + return <float>random_func(state) + + if out is not None: + check_output(out, np.float32, size, False) + out_array = <np.ndarray>out + else: + out_array = <np.ndarray>np.empty(size, np.float32) + + n = np.PyArray_SIZE(out_array) + out_array_data = <float *>np.PyArray_DATA(out_array) + with lock, nogil: + for i in range(n): + out_array_data[i] = <float>random_func(state) + return out_array + + +cdef int check_array_constraint(np.ndarray val, object name, constraint_type cons) except -1: + if cons == CONS_NON_NEGATIVE: + if np.any(np.logical_and(np.logical_not(np.isnan(val)), np.signbit(val))): + raise ValueError(name + " < 0") + elif cons == CONS_POSITIVE or cons == CONS_POSITIVE_NOT_NAN: + if cons == CONS_POSITIVE_NOT_NAN and np.any(np.isnan(val)): + raise ValueError(name + " must not be NaN") + elif np.any(np.less_equal(val, 0)): + raise ValueError(name + " <= 0") + elif cons == CONS_BOUNDED_0_1: + if not np.all(np.greater_equal(val, 0)) or \ + not np.all(np.less_equal(val, 1)): + raise ValueError("{0} < 0, {0} > 1 or {0} contains NaNs".format(name)) + elif cons == CONS_BOUNDED_GT_0_1: + if not np.all(np.greater(val, 0)) or not np.all(np.less_equal(val, 1)): + raise ValueError("{0} <= 0, {0} > 1 or {0} contains NaNs".format(name)) + elif cons == CONS_GT_1: + if not np.all(np.greater(val, 1)): + raise ValueError("{0} <= 1 or {0} contains NaNs".format(name)) + elif cons == CONS_GTE_1: + if not np.all(np.greater_equal(val, 1)): + raise ValueError("{0} < 1 or {0} contains NaNs".format(name)) + elif cons == CONS_POISSON: + if not np.all(np.less_equal(val, POISSON_LAM_MAX)): + raise ValueError("{0} value too large".format(name)) + elif not np.all(np.greater_equal(val, 0.0)): + raise ValueError("{0} < 0 or {0} contains NaNs".format(name)) + elif cons == LEGACY_CONS_POISSON: + if not np.all(np.less_equal(val, LEGACY_POISSON_LAM_MAX)): + raise ValueError("{0} value too large".format(name)) + elif not np.all(np.greater_equal(val, 0.0)): + raise ValueError("{0} < 0 or {0} contains NaNs".format(name)) + + return 0 + + +cdef int check_constraint(double val, object name, constraint_type cons) except -1: + cdef bint is_nan + if cons == CONS_NON_NEGATIVE: + if not np.isnan(val) and np.signbit(val): + raise ValueError(name + " < 0") + elif cons == CONS_POSITIVE or cons == CONS_POSITIVE_NOT_NAN: + if cons == CONS_POSITIVE_NOT_NAN and np.isnan(val): + raise ValueError(name + " must not be NaN") + elif val <= 0: + raise ValueError(name + " <= 0") + elif cons == CONS_BOUNDED_0_1: + if not (val >= 0) or not (val <= 1): + raise ValueError("{0} < 0, {0} > 1 or {0} is NaN".format(name)) + elif cons == CONS_BOUNDED_GT_0_1: + if not val >0 or not val <= 1: + raise ValueError("{0} <= 0, {0} > 1 or {0} contains NaNs".format(name)) + elif cons == CONS_GT_1: + if not (val > 1): + raise ValueError("{0} <= 1 or {0} is NaN".format(name)) + elif cons == CONS_GTE_1: + if not (val >= 1): + raise ValueError("{0} < 1 or {0} is NaN".format(name)) + elif cons == CONS_POISSON: + if not (val >= 0): + raise ValueError("{0} < 0 or {0} is NaN".format(name)) + elif not (val <= POISSON_LAM_MAX): + raise ValueError(name + " value too large") + elif cons == LEGACY_CONS_POISSON: + if not (val >= 0): + raise ValueError("{0} < 0 or {0} is NaN".format(name)) + elif not (val <= LEGACY_POISSON_LAM_MAX): + raise ValueError(name + " value too large") + + return 0 + +cdef object cont_broadcast_1(void *func, void *state, object size, object lock, + np.ndarray a_arr, object a_name, constraint_type a_constraint, + object out): + + cdef np.ndarray randoms + cdef double a_val + cdef double *randoms_data + cdef np.broadcast it + cdef random_double_1 f = (<random_double_1>func) + cdef np.npy_intp i, n + + if a_constraint != CONS_NONE: + check_array_constraint(a_arr, a_name, a_constraint) + + if size is not None and out is None: + randoms = <np.ndarray>np.empty(size, np.double) + elif out is None: + randoms = np.PyArray_SimpleNew(np.PyArray_NDIM(a_arr), np.PyArray_DIMS(a_arr), np.NPY_DOUBLE) + else: + randoms = <np.ndarray>out + + randoms_data = <double *>np.PyArray_DATA(randoms) + n = np.PyArray_SIZE(randoms) + it = np.PyArray_MultiIterNew2(randoms, a_arr) + validate_output_shape(it.shape, randoms) + + with lock, nogil: + for i in range(n): + a_val = (<double*>np.PyArray_MultiIter_DATA(it, 1))[0] + randoms_data[i] = f(state, a_val) + + np.PyArray_MultiIter_NEXT(it) + + return randoms + +cdef object cont_broadcast_2(void *func, void *state, object size, object lock, + np.ndarray a_arr, object a_name, constraint_type a_constraint, + np.ndarray b_arr, object b_name, constraint_type b_constraint): + cdef np.ndarray randoms + cdef double a_val, b_val + cdef double *randoms_data + cdef np.broadcast it + cdef random_double_2 f = (<random_double_2>func) + cdef np.npy_intp i, n + + if a_constraint != CONS_NONE: + check_array_constraint(a_arr, a_name, a_constraint) + + if b_constraint != CONS_NONE: + check_array_constraint(b_arr, b_name, b_constraint) + + if size is not None: + randoms = <np.ndarray>np.empty(size, np.double) + else: + it = np.PyArray_MultiIterNew2(a_arr, b_arr) + randoms = <np.ndarray>np.empty(it.shape, np.double) + # randoms = np.PyArray_SimpleNew(it.nd, np.PyArray_DIMS(it), np.NPY_DOUBLE) + + randoms_data = <double *>np.PyArray_DATA(randoms) + n = np.PyArray_SIZE(randoms) + + it = np.PyArray_MultiIterNew3(randoms, a_arr, b_arr) + validate_output_shape(it.shape, randoms) + + with lock, nogil: + for i in range(n): + a_val = (<double*>np.PyArray_MultiIter_DATA(it, 1))[0] + b_val = (<double*>np.PyArray_MultiIter_DATA(it, 2))[0] + randoms_data[i] = f(state, a_val, b_val) + + np.PyArray_MultiIter_NEXT(it) + + return randoms + +cdef object cont_broadcast_3(void *func, void *state, object size, object lock, + np.ndarray a_arr, object a_name, constraint_type a_constraint, + np.ndarray b_arr, object b_name, constraint_type b_constraint, + np.ndarray c_arr, object c_name, constraint_type c_constraint): + cdef np.ndarray randoms + cdef double a_val, b_val, c_val + cdef double *randoms_data + cdef np.broadcast it + cdef random_double_3 f = (<random_double_3>func) + cdef np.npy_intp i, n + + if a_constraint != CONS_NONE: + check_array_constraint(a_arr, a_name, a_constraint) + + if b_constraint != CONS_NONE: + check_array_constraint(b_arr, b_name, b_constraint) + + if c_constraint != CONS_NONE: + check_array_constraint(c_arr, c_name, c_constraint) + + if size is not None: + randoms = <np.ndarray>np.empty(size, np.double) + else: + it = np.PyArray_MultiIterNew3(a_arr, b_arr, c_arr) + # randoms = np.PyArray_SimpleNew(it.nd, np.PyArray_DIMS(it), np.NPY_DOUBLE) + randoms = <np.ndarray>np.empty(it.shape, np.double) + + randoms_data = <double *>np.PyArray_DATA(randoms) + n = np.PyArray_SIZE(randoms) + + it = np.PyArray_MultiIterNew4(randoms, a_arr, b_arr, c_arr) + validate_output_shape(it.shape, randoms) + + with lock, nogil: + for i in range(n): + a_val = (<double*>np.PyArray_MultiIter_DATA(it, 1))[0] + b_val = (<double*>np.PyArray_MultiIter_DATA(it, 2))[0] + c_val = (<double*>np.PyArray_MultiIter_DATA(it, 3))[0] + randoms_data[i] = f(state, a_val, b_val, c_val) + + np.PyArray_MultiIter_NEXT(it) + + return randoms + +cdef object cont(void *func, void *state, object size, object lock, int narg, + object a, object a_name, constraint_type a_constraint, + object b, object b_name, constraint_type b_constraint, + object c, object c_name, constraint_type c_constraint, + object out): + + cdef np.ndarray a_arr, b_arr, c_arr + cdef double _a = 0.0, _b = 0.0, _c = 0.0 + cdef bint is_scalar = True + check_output(out, np.float64, size, narg > 0) + if narg > 0: + a_arr = <np.ndarray>np.PyArray_FROM_OTF(a, np.NPY_DOUBLE, np.NPY_ALIGNED) + is_scalar = is_scalar and np.PyArray_NDIM(a_arr) == 0 + if narg > 1: + b_arr = <np.ndarray>np.PyArray_FROM_OTF(b, np.NPY_DOUBLE, np.NPY_ALIGNED) + is_scalar = is_scalar and np.PyArray_NDIM(b_arr) == 0 + if narg == 3: + c_arr = <np.ndarray>np.PyArray_FROM_OTF(c, np.NPY_DOUBLE, np.NPY_ALIGNED) + is_scalar = is_scalar and np.PyArray_NDIM(c_arr) == 0 + + if not is_scalar: + if narg == 1: + return cont_broadcast_1(func, state, size, lock, + a_arr, a_name, a_constraint, + out) + elif narg == 2: + return cont_broadcast_2(func, state, size, lock, + a_arr, a_name, a_constraint, + b_arr, b_name, b_constraint) + else: + return cont_broadcast_3(func, state, size, lock, + a_arr, a_name, a_constraint, + b_arr, b_name, b_constraint, + c_arr, c_name, c_constraint) + + if narg > 0: + _a = PyFloat_AsDouble(a) + if a_constraint != CONS_NONE and is_scalar: + check_constraint(_a, a_name, a_constraint) + if narg > 1: + _b = PyFloat_AsDouble(b) + if b_constraint != CONS_NONE: + check_constraint(_b, b_name, b_constraint) + if narg == 3: + _c = PyFloat_AsDouble(c) + if c_constraint != CONS_NONE and is_scalar: + check_constraint(_c, c_name, c_constraint) + + if size is None and out is None: + with lock: + if narg == 0: + return (<random_double_0>func)(state) + elif narg == 1: + return (<random_double_1>func)(state, _a) + elif narg == 2: + return (<random_double_2>func)(state, _a, _b) + elif narg == 3: + return (<random_double_3>func)(state, _a, _b, _c) + + cdef np.npy_intp i, n + cdef np.ndarray randoms + if out is None: + randoms = <np.ndarray>np.empty(size) + else: + randoms = <np.ndarray>out + n = np.PyArray_SIZE(randoms) + + cdef double *randoms_data = <double *>np.PyArray_DATA(randoms) + cdef random_double_0 f0 + cdef random_double_1 f1 + cdef random_double_2 f2 + cdef random_double_3 f3 + + with lock, nogil: + if narg == 0: + f0 = (<random_double_0>func) + for i in range(n): + randoms_data[i] = f0(state) + elif narg == 1: + f1 = (<random_double_1>func) + for i in range(n): + randoms_data[i] = f1(state, _a) + elif narg == 2: + f2 = (<random_double_2>func) + for i in range(n): + randoms_data[i] = f2(state, _a, _b) + elif narg == 3: + f3 = (<random_double_3>func) + for i in range(n): + randoms_data[i] = f3(state, _a, _b, _c) + + if out is None: + return randoms + else: + return out + +cdef object discrete_broadcast_d(void *func, void *state, object size, object lock, + np.ndarray a_arr, object a_name, constraint_type a_constraint): + + cdef np.ndarray randoms + cdef int64_t *randoms_data + cdef np.broadcast it + cdef random_uint_d f = (<random_uint_d>func) + cdef np.npy_intp i, n + + if a_constraint != CONS_NONE: + check_array_constraint(a_arr, a_name, a_constraint) + + if size is not None: + randoms = np.empty(size, np.int64) + else: + # randoms = np.empty(np.shape(a_arr), np.double) + randoms = np.PyArray_SimpleNew(np.PyArray_NDIM(a_arr), np.PyArray_DIMS(a_arr), np.NPY_INT64) + + randoms_data = <int64_t *>np.PyArray_DATA(randoms) + n = np.PyArray_SIZE(randoms) + + it = np.PyArray_MultiIterNew2(randoms, a_arr) + validate_output_shape(it.shape, randoms) + + with lock, nogil: + for i in range(n): + a_val = (<double*>np.PyArray_MultiIter_DATA(it, 1))[0] + randoms_data[i] = f(state, a_val) + + np.PyArray_MultiIter_NEXT(it) + + return randoms + +cdef object discrete_broadcast_dd(void *func, void *state, object size, object lock, + np.ndarray a_arr, object a_name, constraint_type a_constraint, + np.ndarray b_arr, object b_name, constraint_type b_constraint): + cdef np.ndarray randoms + cdef int64_t *randoms_data + cdef np.broadcast it + cdef random_uint_dd f = (<random_uint_dd>func) + cdef np.npy_intp i, n + + if a_constraint != CONS_NONE: + check_array_constraint(a_arr, a_name, a_constraint) + if b_constraint != CONS_NONE: + check_array_constraint(b_arr, b_name, b_constraint) + + if size is not None: + randoms = <np.ndarray>np.empty(size, np.int64) + else: + it = np.PyArray_MultiIterNew2(a_arr, b_arr) + randoms = <np.ndarray>np.empty(it.shape, np.int64) + # randoms = np.PyArray_SimpleNew(it.nd, np.PyArray_DIMS(it), np.NPY_INT64) + + randoms_data = <int64_t *>np.PyArray_DATA(randoms) + n = np.PyArray_SIZE(randoms) + + it = np.PyArray_MultiIterNew3(randoms, a_arr, b_arr) + validate_output_shape(it.shape, randoms) + + with lock, nogil: + for i in range(n): + a_val = (<double*>np.PyArray_MultiIter_DATA(it, 1))[0] + b_val = (<double*>np.PyArray_MultiIter_DATA(it, 2))[0] + randoms_data[i] = f(state, a_val, b_val) + + np.PyArray_MultiIter_NEXT(it) + + return randoms + +cdef object discrete_broadcast_di(void *func, void *state, object size, object lock, + np.ndarray a_arr, object a_name, constraint_type a_constraint, + np.ndarray b_arr, object b_name, constraint_type b_constraint): + cdef np.ndarray randoms + cdef int64_t *randoms_data + cdef np.broadcast it + cdef random_uint_di f = (<random_uint_di>func) + cdef np.npy_intp i, n + + if a_constraint != CONS_NONE: + check_array_constraint(a_arr, a_name, a_constraint) + + if b_constraint != CONS_NONE: + check_array_constraint(b_arr, b_name, b_constraint) + + if size is not None: + randoms = <np.ndarray>np.empty(size, np.int64) + else: + it = np.PyArray_MultiIterNew2(a_arr, b_arr) + randoms = <np.ndarray>np.empty(it.shape, np.int64) + + randoms_data = <int64_t *>np.PyArray_DATA(randoms) + n = np.PyArray_SIZE(randoms) + + it = np.PyArray_MultiIterNew3(randoms, a_arr, b_arr) + validate_output_shape(it.shape, randoms) + + with lock, nogil: + for i in range(n): + a_val = (<double*>np.PyArray_MultiIter_DATA(it, 1))[0] + b_val = (<int64_t*>np.PyArray_MultiIter_DATA(it, 2))[0] + (<int64_t*>np.PyArray_MultiIter_DATA(it, 0))[0] = f(state, a_val, b_val) + + np.PyArray_MultiIter_NEXT(it) + + return randoms + +cdef object discrete_broadcast_iii(void *func, void *state, object size, object lock, + np.ndarray a_arr, object a_name, constraint_type a_constraint, + np.ndarray b_arr, object b_name, constraint_type b_constraint, + np.ndarray c_arr, object c_name, constraint_type c_constraint): + cdef np.ndarray randoms + cdef int64_t *randoms_data + cdef np.broadcast it + cdef random_uint_iii f = (<random_uint_iii>func) + cdef np.npy_intp i, n + + if a_constraint != CONS_NONE: + check_array_constraint(a_arr, a_name, a_constraint) + + if b_constraint != CONS_NONE: + check_array_constraint(b_arr, b_name, b_constraint) + + if c_constraint != CONS_NONE: + check_array_constraint(c_arr, c_name, c_constraint) + + if size is not None: + randoms = <np.ndarray>np.empty(size, np.int64) + else: + it = np.PyArray_MultiIterNew3(a_arr, b_arr, c_arr) + randoms = <np.ndarray>np.empty(it.shape, np.int64) + + randoms_data = <int64_t *>np.PyArray_DATA(randoms) + n = np.PyArray_SIZE(randoms) + + it = np.PyArray_MultiIterNew4(randoms, a_arr, b_arr, c_arr) + validate_output_shape(it.shape, randoms) + + with lock, nogil: + for i in range(n): + a_val = (<int64_t*>np.PyArray_MultiIter_DATA(it, 1))[0] + b_val = (<int64_t*>np.PyArray_MultiIter_DATA(it, 2))[0] + c_val = (<int64_t*>np.PyArray_MultiIter_DATA(it, 3))[0] + randoms_data[i] = f(state, a_val, b_val, c_val) + + np.PyArray_MultiIter_NEXT(it) + + return randoms + +cdef object discrete_broadcast_i(void *func, void *state, object size, object lock, + np.ndarray a_arr, object a_name, constraint_type a_constraint): + cdef np.ndarray randoms + cdef int64_t *randoms_data + cdef np.broadcast it + cdef random_uint_i f = (<random_uint_i>func) + cdef np.npy_intp i, n + + if a_constraint != CONS_NONE: + check_array_constraint(a_arr, a_name, a_constraint) + + if size is not None: + randoms = <np.ndarray>np.empty(size, np.int64) + else: + randoms = np.PyArray_SimpleNew(np.PyArray_NDIM(a_arr), np.PyArray_DIMS(a_arr), np.NPY_INT64) + + randoms_data = <int64_t *>np.PyArray_DATA(randoms) + n = np.PyArray_SIZE(randoms) + + it = np.PyArray_MultiIterNew2(randoms, a_arr) + validate_output_shape(it.shape, randoms) + + with lock, nogil: + for i in range(n): + a_val = (<int64_t*>np.PyArray_MultiIter_DATA(it, 1))[0] + randoms_data[i] = f(state, a_val) + + np.PyArray_MultiIter_NEXT(it) + + return randoms + +# Needs double <vec>, double-double <vec>, double-int64_t<vec>, int64_t <vec>, int64_t-int64_t-int64_t +cdef object disc(void *func, void *state, object size, object lock, + int narg_double, int narg_int64, + object a, object a_name, constraint_type a_constraint, + object b, object b_name, constraint_type b_constraint, + object c, object c_name, constraint_type c_constraint): + + cdef double _da = 0, _db = 0 + cdef int64_t _ia = 0, _ib = 0, _ic = 0 + cdef bint is_scalar = True + if narg_double > 0: + a_arr = <np.ndarray>np.PyArray_FROM_OTF(a, np.NPY_DOUBLE, np.NPY_ALIGNED) + is_scalar = is_scalar and np.PyArray_NDIM(a_arr) == 0 + if narg_double > 1: + b_arr = <np.ndarray>np.PyArray_FROM_OTF(b, np.NPY_DOUBLE, np.NPY_ALIGNED) + is_scalar = is_scalar and np.PyArray_NDIM(b_arr) == 0 + elif narg_int64 == 1: + b_arr = <np.ndarray>np.PyArray_FROM_OTF(b, np.NPY_INT64, np.NPY_ALIGNED) + is_scalar = is_scalar and np.PyArray_NDIM(b_arr) == 0 + else: + if narg_int64 > 0: + a_arr = <np.ndarray>np.PyArray_FROM_OTF(a, np.NPY_INT64, np.NPY_ALIGNED) + is_scalar = is_scalar and np.PyArray_NDIM(a_arr) == 0 + if narg_int64 > 1: + b_arr = <np.ndarray>np.PyArray_FROM_OTF(b, np.NPY_INT64, np.NPY_ALIGNED) + is_scalar = is_scalar and np.PyArray_NDIM(b_arr) == 0 + if narg_int64 > 2: + c_arr = <np.ndarray>np.PyArray_FROM_OTF(c, np.NPY_INT64, np.NPY_ALIGNED) + is_scalar = is_scalar and np.PyArray_NDIM(c_arr) == 0 + + if not is_scalar: + if narg_int64 == 0: + if narg_double == 1: + return discrete_broadcast_d(func, state, size, lock, + a_arr, a_name, a_constraint) + elif narg_double == 2: + return discrete_broadcast_dd(func, state, size, lock, + a_arr, a_name, a_constraint, + b_arr, b_name, b_constraint) + elif narg_int64 == 1: + if narg_double == 0: + return discrete_broadcast_i(func, state, size, lock, + a_arr, a_name, a_constraint) + elif narg_double == 1: + return discrete_broadcast_di(func, state, size, lock, + a_arr, a_name, a_constraint, + b_arr, b_name, b_constraint) + else: + raise NotImplementedError("No vector path available") + + if narg_double > 0: + _da = PyFloat_AsDouble(a) + if a_constraint != CONS_NONE and is_scalar: + check_constraint(_da, a_name, a_constraint) + + if narg_double > 1: + _db = PyFloat_AsDouble(b) + if b_constraint != CONS_NONE and is_scalar: + check_constraint(_db, b_name, b_constraint) + elif narg_int64 == 1: + _ib = <int64_t>b + if b_constraint != CONS_NONE and is_scalar: + check_constraint(<double>_ib, b_name, b_constraint) + else: + if narg_int64 > 0: + _ia = <int64_t>a + if a_constraint != CONS_NONE and is_scalar: + check_constraint(<double>_ia, a_name, a_constraint) + if narg_int64 > 1: + _ib = <int64_t>b + if b_constraint != CONS_NONE and is_scalar: + check_constraint(<double>_ib, b_name, b_constraint) + if narg_int64 > 2: + _ic = <int64_t>c + if c_constraint != CONS_NONE and is_scalar: + check_constraint(<double>_ic, c_name, c_constraint) + + if size is None: + with lock: + if narg_int64 == 0: + if narg_double == 0: + return (<random_uint_0>func)(state) + elif narg_double == 1: + return (<random_uint_d>func)(state, _da) + elif narg_double == 2: + return (<random_uint_dd>func)(state, _da, _db) + elif narg_int64 == 1: + if narg_double == 0: + return (<random_uint_i>func)(state, _ia) + if narg_double == 1: + return (<random_uint_di>func)(state, _da, _ib) + else: + return (<random_uint_iii>func)(state, _ia, _ib, _ic) + + cdef np.npy_intp i, n + cdef np.ndarray randoms = <np.ndarray>np.empty(size, np.int64) + cdef np.int64_t *randoms_data + cdef random_uint_0 f0 + cdef random_uint_d fd + cdef random_uint_dd fdd + cdef random_uint_di fdi + cdef random_uint_i fi + cdef random_uint_iii fiii + + n = np.PyArray_SIZE(randoms) + randoms_data = <np.int64_t *>np.PyArray_DATA(randoms) + + with lock, nogil: + if narg_int64 == 0: + if narg_double == 0: + f0 = (<random_uint_0>func) + for i in range(n): + randoms_data[i] = f0(state) + elif narg_double == 1: + fd = (<random_uint_d>func) + for i in range(n): + randoms_data[i] = fd(state, _da) + elif narg_double == 2: + fdd = (<random_uint_dd>func) + for i in range(n): + randoms_data[i] = fdd(state, _da, _db) + elif narg_int64 == 1: + if narg_double == 0: + fi = (<random_uint_i>func) + for i in range(n): + randoms_data[i] = fi(state, _ia) + if narg_double == 1: + fdi = (<random_uint_di>func) + for i in range(n): + randoms_data[i] = fdi(state, _da, _ib) + else: + fiii = (<random_uint_iii>func) + for i in range(n): + randoms_data[i] = fiii(state, _ia, _ib, _ic) + + return randoms + + +cdef object cont_broadcast_1_f(void *func, bitgen_t *state, object size, object lock, + np.ndarray a_arr, object a_name, constraint_type a_constraint, + object out): + + cdef np.ndarray randoms + cdef float a_val + cdef float *randoms_data + cdef np.broadcast it + cdef random_float_1 f = (<random_float_1>func) + cdef np.npy_intp i, n + + if a_constraint != CONS_NONE: + check_array_constraint(a_arr, a_name, a_constraint) + + if size is not None and out is None: + randoms = <np.ndarray>np.empty(size, np.float32) + elif out is None: + randoms = np.PyArray_SimpleNew(np.PyArray_NDIM(a_arr), + np.PyArray_DIMS(a_arr), + np.NPY_FLOAT32) + else: + randoms = <np.ndarray>out + + randoms_data = <float *>np.PyArray_DATA(randoms) + n = np.PyArray_SIZE(randoms) + it = np.PyArray_MultiIterNew2(randoms, a_arr) + validate_output_shape(it.shape, randoms) + + with lock, nogil: + for i in range(n): + a_val = (<float*>np.PyArray_MultiIter_DATA(it, 1))[0] + randoms_data[i] = f(state, a_val) + + np.PyArray_MultiIter_NEXT(it) + + return randoms + +cdef object cont_f(void *func, bitgen_t *state, object size, object lock, + object a, object a_name, constraint_type a_constraint, + object out): + + cdef np.ndarray a_arr, b_arr, c_arr + cdef float _a + cdef bint is_scalar = True + cdef int requirements = np.NPY_ALIGNED | np.NPY_FORCECAST + check_output(out, np.float32, size, True) + a_arr = <np.ndarray>np.PyArray_FROMANY(a, np.NPY_FLOAT32, 0, 0, requirements) + is_scalar = np.PyArray_NDIM(a_arr) == 0 + + if not is_scalar: + return cont_broadcast_1_f(func, state, size, lock, a_arr, a_name, a_constraint, out) + + _a = <float>PyFloat_AsDouble(a) + if a_constraint != CONS_NONE: + check_constraint(_a, a_name, a_constraint) + + if size is None and out is None: + with lock: + return (<random_float_1>func)(state, _a) + + cdef np.npy_intp i, n + cdef np.ndarray randoms + if out is None: + randoms = <np.ndarray>np.empty(size, np.float32) + else: + randoms = <np.ndarray>out + n = np.PyArray_SIZE(randoms) + + cdef float *randoms_data = <float *>np.PyArray_DATA(randoms) + cdef random_float_1 f1 = <random_float_1>func + + with lock, nogil: + for i in range(n): + randoms_data[i] = f1(state, _a) + + if out is None: + return randoms + else: + return out diff --git a/numpy/random/_examples/cffi/extending.py b/numpy/random/_examples/cffi/extending.py new file mode 100644 index 000000000000..8440d400ea91 --- /dev/null +++ b/numpy/random/_examples/cffi/extending.py @@ -0,0 +1,40 @@ +""" +Use cffi to access any of the underlying C functions from distributions.h +""" +import os +import numpy as np +import cffi +from .parse import parse_distributions_h +ffi = cffi.FFI() + +inc_dir = os.path.join(np.get_include(), 'numpy') + +# Basic numpy types +ffi.cdef(''' + typedef intptr_t npy_intp; + typedef unsigned char npy_bool; + +''') + +parse_distributions_h(ffi, inc_dir) + +lib = ffi.dlopen(np.random._generator.__file__) + +# Compare the distributions.h random_standard_normal_fill to +# Generator.standard_random +bit_gen = np.random.PCG64() +rng = np.random.Generator(bit_gen) +state = bit_gen.state + +interface = rng.bit_generator.cffi +n = 100 +vals_cffi = ffi.new('double[%d]' % n) +lib.random_standard_normal_fill(interface.bit_generator, n, vals_cffi) + +# reset the state +bit_gen.state = state + +vals = rng.standard_normal(n) + +for i in range(n): + assert vals[i] == vals_cffi[i] diff --git a/numpy/random/_examples/cffi/parse.py b/numpy/random/_examples/cffi/parse.py new file mode 100644 index 000000000000..daff6bdece01 --- /dev/null +++ b/numpy/random/_examples/cffi/parse.py @@ -0,0 +1,55 @@ +import os + + +def parse_distributions_h(ffi, inc_dir): + """ + Parse distributions.h located in inc_dir for CFFI, filling in the ffi.cdef + + Read the function declarations without the "#define ..." macros that will + be filled in when loading the library. + """ + + with open(os.path.join(inc_dir, 'random', 'bitgen.h')) as fid: + s = [] + for line in fid: + # massage the include file + if line.strip().startswith('#'): + continue + s.append(line) + ffi.cdef('\n'.join(s)) + + with open(os.path.join(inc_dir, 'random', 'distributions.h')) as fid: + s = [] + in_skip = 0 + ignoring = False + for line in fid: + # check for and remove extern "C" guards + if ignoring: + if line.strip().startswith('#endif'): + ignoring = False + continue + if line.strip().startswith('#ifdef __cplusplus'): + ignoring = True + + # massage the include file + if line.strip().startswith('#'): + continue + + # skip any inlined function definition + # which starts with 'static NPY_INLINE xxx(...) {' + # and ends with a closing '}' + if line.strip().startswith('static NPY_INLINE'): + in_skip += line.count('{') + continue + elif in_skip > 0: + in_skip += line.count('{') + in_skip -= line.count('}') + continue + + # replace defines with their value or remove them + line = line.replace('DECLDIR', '') + line = line.replace('NPY_INLINE', '') + line = line.replace('RAND_INT_TYPE', 'int64_t') + s.append(line) + ffi.cdef('\n'.join(s)) + diff --git a/numpy/random/_examples/cython/extending.pyx b/numpy/random/_examples/cython/extending.pyx new file mode 100644 index 000000000000..3a7f81aa0466 --- /dev/null +++ b/numpy/random/_examples/cython/extending.pyx @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 +#cython: language_level=3 + +from libc.stdint cimport uint32_t +from cpython.pycapsule cimport PyCapsule_IsValid, PyCapsule_GetPointer + +import numpy as np +cimport numpy as np +cimport cython + +from numpy.random cimport bitgen_t +from numpy.random import PCG64 + +np.import_array() + + +@cython.boundscheck(False) +@cython.wraparound(False) +def uniform_mean(Py_ssize_t n): + cdef Py_ssize_t i + cdef bitgen_t *rng + cdef const char *capsule_name = "BitGenerator" + cdef double[::1] random_values + cdef np.ndarray randoms + + x = PCG64() + capsule = x.capsule + if not PyCapsule_IsValid(capsule, capsule_name): + raise ValueError("Invalid pointer to anon_func_state") + rng = <bitgen_t *> PyCapsule_GetPointer(capsule, capsule_name) + random_values = np.empty(n) + # Best practice is to acquire the lock whenever generating random values. + # This prevents other threads from modifying the state. Acquiring the lock + # is only necessary if if the GIL is also released, as in this example. + with x.lock, nogil: + for i in range(n): + random_values[i] = rng.next_double(rng.state) + randoms = np.asarray(random_values) + return randoms.mean() + + +# This function is declared nogil so it can be used without the GIL below +cdef uint32_t bounded_uint(uint32_t lb, uint32_t ub, bitgen_t *rng) nogil: + cdef uint32_t mask, delta, val + mask = delta = ub - lb + mask |= mask >> 1 + mask |= mask >> 2 + mask |= mask >> 4 + mask |= mask >> 8 + mask |= mask >> 16 + + val = rng.next_uint32(rng.state) & mask + while val > delta: + val = rng.next_uint32(rng.state) & mask + + return lb + val + + +@cython.boundscheck(False) +@cython.wraparound(False) +def bounded_uints(uint32_t lb, uint32_t ub, Py_ssize_t n): + cdef Py_ssize_t i + cdef bitgen_t *rng + cdef uint32_t[::1] out + cdef const char *capsule_name = "BitGenerator" + + x = PCG64() + out = np.empty(n, dtype=np.uint32) + capsule = x.capsule + + if not PyCapsule_IsValid(capsule, capsule_name): + raise ValueError("Invalid pointer to anon_func_state") + rng = <bitgen_t *>PyCapsule_GetPointer(capsule, capsule_name) + + with x.lock, nogil: + for i in range(n): + out[i] = bounded_uint(lb, ub, rng) + return np.asarray(out) diff --git a/numpy/random/_examples/cython/extending_distributions.pyx b/numpy/random/_examples/cython/extending_distributions.pyx new file mode 100644 index 000000000000..d908e92d01b0 --- /dev/null +++ b/numpy/random/_examples/cython/extending_distributions.pyx @@ -0,0 +1,117 @@ +#!/usr/bin/env python3 +#cython: language_level=3 +""" +This file shows how the to use a BitGenerator to create a distribution. +""" +import numpy as np +cimport numpy as np +cimport cython +from cpython.pycapsule cimport PyCapsule_IsValid, PyCapsule_GetPointer +from libc.stdint cimport uint16_t, uint64_t +from numpy.random cimport bitgen_t +from numpy.random import PCG64 +from numpy.random.c_distributions cimport ( + random_standard_uniform_fill, random_standard_uniform_fill_f) + + +@cython.boundscheck(False) +@cython.wraparound(False) +def uniforms(Py_ssize_t n): + """ + Create an array of `n` uniformly distributed doubles. + A 'real' distribution would want to process the values into + some non-uniform distribution + """ + cdef Py_ssize_t i + cdef bitgen_t *rng + cdef const char *capsule_name = "BitGenerator" + cdef double[::1] random_values + + x = PCG64() + capsule = x.capsule + # Optional check that the capsule if from a BitGenerator + if not PyCapsule_IsValid(capsule, capsule_name): + raise ValueError("Invalid pointer to anon_func_state") + # Cast the pointer + rng = <bitgen_t *> PyCapsule_GetPointer(capsule, capsule_name) + random_values = np.empty(n, dtype='float64') + with x.lock, nogil: + for i in range(n): + # Call the function + random_values[i] = rng.next_double(rng.state) + randoms = np.asarray(random_values) + + return randoms + +# cython example 2 +@cython.boundscheck(False) +@cython.wraparound(False) +def uint10_uniforms(Py_ssize_t n): + """Uniform 10 bit integers stored as 16-bit unsigned integers""" + cdef Py_ssize_t i + cdef bitgen_t *rng + cdef const char *capsule_name = "BitGenerator" + cdef uint16_t[::1] random_values + cdef int bits_remaining + cdef int width = 10 + cdef uint64_t buff, mask = 0x3FF + + x = PCG64() + capsule = x.capsule + if not PyCapsule_IsValid(capsule, capsule_name): + raise ValueError("Invalid pointer to anon_func_state") + rng = <bitgen_t *> PyCapsule_GetPointer(capsule, capsule_name) + random_values = np.empty(n, dtype='uint16') + # Best practice is to release GIL and acquire the lock + bits_remaining = 0 + with x.lock, nogil: + for i in range(n): + if bits_remaining < width: + buff = rng.next_uint64(rng.state) + random_values[i] = buff & mask + buff >>= width + + randoms = np.asarray(random_values) + return randoms + +# cython example 3 +def uniforms_ex(bit_generator, Py_ssize_t n, dtype=np.float64): + """ + Create an array of `n` uniformly distributed doubles via a "fill" function. + + A 'real' distribution would want to process the values into + some non-uniform distribution + + Parameters + ---------- + bit_generator: BitGenerator instance + n: int + Output vector length + dtype: {str, dtype}, optional + Desired dtype, either 'd' (or 'float64') or 'f' (or 'float32'). The + default dtype value is 'd' + """ + cdef Py_ssize_t i + cdef bitgen_t *rng + cdef const char *capsule_name = "BitGenerator" + cdef np.ndarray randoms + + capsule = bit_generator.capsule + # Optional check that the capsule if from a BitGenerator + if not PyCapsule_IsValid(capsule, capsule_name): + raise ValueError("Invalid pointer to anon_func_state") + # Cast the pointer + rng = <bitgen_t *> PyCapsule_GetPointer(capsule, capsule_name) + + _dtype = np.dtype(dtype) + randoms = np.empty(n, dtype=_dtype) + if _dtype == np.float32: + with bit_generator.lock: + random_standard_uniform_fill_f(rng, n, <float*>np.PyArray_DATA(randoms)) + elif _dtype == np.float64: + with bit_generator.lock: + random_standard_uniform_fill(rng, n, <double*>np.PyArray_DATA(randoms)) + else: + raise TypeError('Unsupported dtype %r for random' % _dtype) + return randoms + diff --git a/numpy/random/_examples/cython/setup.py b/numpy/random/_examples/cython/setup.py new file mode 100644 index 000000000000..f41150fdb2fe --- /dev/null +++ b/numpy/random/_examples/cython/setup.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +""" +Build the Cython demonstrations of low-level access to NumPy random + +Usage: python setup.py build_ext -i +""" +import setuptools # triggers monkeypatching distutils +from distutils.core import setup +from os.path import dirname, join, abspath + +import numpy as np +from Cython.Build import cythonize +from numpy.distutils.misc_util import get_info +from setuptools.extension import Extension + +path = dirname(__file__) +src_dir = join(dirname(path), '..', 'src') +defs = [('NPY_NO_DEPRECATED_API', 0)] +inc_path = np.get_include() +lib_path = [abspath(join(np.get_include(), '..', '..', 'random', 'lib'))] +lib_path += get_info('npymath')['library_dirs'] + +extending = Extension("extending", + sources=[join('.', 'extending.pyx')], + include_dirs=[ + np.get_include(), + join(path, '..', '..') + ], + define_macros=defs, + ) +distributions = Extension("extending_distributions", + sources=[join('.', 'extending_distributions.pyx')], + include_dirs=[inc_path], + library_dirs=lib_path, + libraries=['npyrandom', 'npymath'], + define_macros=defs, + ) + +extensions = [extending, distributions] + +setup( + ext_modules=cythonize(extensions) +) diff --git a/numpy/random/_examples/numba/extending.py b/numpy/random/_examples/numba/extending.py new file mode 100644 index 000000000000..f387db69502a --- /dev/null +++ b/numpy/random/_examples/numba/extending.py @@ -0,0 +1,84 @@ +import numpy as np +import numba as nb + +from numpy.random import PCG64 +from timeit import timeit + +bit_gen = PCG64() +next_d = bit_gen.cffi.next_double +state_addr = bit_gen.cffi.state_address + +def normals(n, state): + out = np.empty(n) + for i in range((n + 1) // 2): + x1 = 2.0 * next_d(state) - 1.0 + x2 = 2.0 * next_d(state) - 1.0 + r2 = x1 * x1 + x2 * x2 + while r2 >= 1.0 or r2 == 0.0: + x1 = 2.0 * next_d(state) - 1.0 + x2 = 2.0 * next_d(state) - 1.0 + r2 = x1 * x1 + x2 * x2 + f = np.sqrt(-2.0 * np.log(r2) / r2) + out[2 * i] = f * x1 + if 2 * i + 1 < n: + out[2 * i + 1] = f * x2 + return out + +# Compile using Numba +normalsj = nb.jit(normals, nopython=True) +# Must use state address not state with numba +n = 10000 + +def numbacall(): + return normalsj(n, state_addr) + +rg = np.random.Generator(PCG64()) + +def numpycall(): + return rg.normal(size=n) + +# Check that the functions work +r1 = numbacall() +r2 = numpycall() +assert r1.shape == (n,) +assert r1.shape == r2.shape + +t1 = timeit(numbacall, number=1000) +print(f'{t1:.2f} secs for {n} PCG64 (Numba/PCG64) gaussian randoms') +t2 = timeit(numpycall, number=1000) +print(f'{t2:.2f} secs for {n} PCG64 (NumPy/PCG64) gaussian randoms') + +# example 2 + +next_u32 = bit_gen.ctypes.next_uint32 +ctypes_state = bit_gen.ctypes.state + +@nb.jit(nopython=True) +def bounded_uint(lb, ub, state): + mask = delta = ub - lb + mask |= mask >> 1 + mask |= mask >> 2 + mask |= mask >> 4 + mask |= mask >> 8 + mask |= mask >> 16 + + val = next_u32(state) & mask + while val > delta: + val = next_u32(state) & mask + + return lb + val + + +print(bounded_uint(323, 2394691, ctypes_state.value)) + + +@nb.jit(nopython=True) +def bounded_uints(lb, ub, n, state): + out = np.empty(n, dtype=np.uint32) + for i in range(n): + out[i] = bounded_uint(lb, ub, state) + + +bounded_uints(323, 2394691, 10000000, ctypes_state.value) + + diff --git a/numpy/random/_examples/numba/extending_distributions.py b/numpy/random/_examples/numba/extending_distributions.py new file mode 100644 index 000000000000..7cf8bf0b0535 --- /dev/null +++ b/numpy/random/_examples/numba/extending_distributions.py @@ -0,0 +1,67 @@ +r""" +Building the required library in this example requires a source distribution +of NumPy or clone of the NumPy git repository since distributions.c is not +included in binary distributions. + +On *nix, execute in numpy/random/src/distributions + +export ${PYTHON_VERSION}=3.8 # Python version +export PYTHON_INCLUDE=#path to Python's include folder, usually \ + ${PYTHON_HOME}/include/python${PYTHON_VERSION}m +export NUMPY_INCLUDE=#path to numpy's include folder, usually \ + ${PYTHON_HOME}/lib/python${PYTHON_VERSION}/site-packages/numpy/core/include +gcc -shared -o libdistributions.so -fPIC distributions.c \ + -I${NUMPY_INCLUDE} -I${PYTHON_INCLUDE} +mv libdistributions.so ../../_examples/numba/ + +On Windows + +rem PYTHON_HOME and PYTHON_VERSION are setup dependent, this is an example +set PYTHON_HOME=c:\Anaconda +set PYTHON_VERSION=38 +cl.exe /LD .\distributions.c -DDLL_EXPORT \ + -I%PYTHON_HOME%\lib\site-packages\numpy\core\include \ + -I%PYTHON_HOME%\include %PYTHON_HOME%\libs\python%PYTHON_VERSION%.lib +move distributions.dll ../../_examples/numba/ +""" +import os + +import numba as nb +import numpy as np +from cffi import FFI + +from numpy.random import PCG64 + +ffi = FFI() +if os.path.exists('./distributions.dll'): + lib = ffi.dlopen('./distributions.dll') +elif os.path.exists('./libdistributions.so'): + lib = ffi.dlopen('./libdistributions.so') +else: + raise RuntimeError('Required DLL/so file was not found.') + +ffi.cdef(""" +double random_standard_normal(void *bitgen_state); +""") +x = PCG64() +xffi = x.cffi +bit_generator = xffi.bit_generator + +random_standard_normal = lib.random_standard_normal + + +def normals(n, bit_generator): + out = np.empty(n) + for i in range(n): + out[i] = random_standard_normal(bit_generator) + return out + + +normalsj = nb.jit(normals, nopython=True) + +# Numba requires a memory address for void * +# Can also get address from x.ctypes.bit_generator.value +bit_generator_address = int(ffi.cast('uintptr_t', bit_generator)) + +norm = normalsj(1000, bit_generator_address) +print(norm[:12]) diff --git a/numpy/random/_generator.pyi b/numpy/random/_generator.pyi new file mode 100644 index 000000000000..c574bef9a5cb --- /dev/null +++ b/numpy/random/_generator.pyi @@ -0,0 +1,647 @@ +from typing import Any, Callable, Dict, Optional, Tuple, Type, Union, overload, TypeVar, Literal + +from numpy import ( + bool_, + dtype, + float32, + float64, + int8, + int16, + int32, + int64, + int_, + ndarray, + uint, + uint8, + uint16, + uint32, + uint64, +) +from numpy.random import BitGenerator, SeedSequence +from numpy.typing import ( + ArrayLike, + _ArrayLikeFloat_co, + _ArrayLikeInt_co, + _DoubleCodes, + _DTypeLikeBool, + _DTypeLikeInt, + _DTypeLikeUInt, + _Float32Codes, + _Float64Codes, + _Int8Codes, + _Int16Codes, + _Int32Codes, + _Int64Codes, + _IntCodes, + _ShapeLike, + _SingleCodes, + _SupportsDType, + _UInt8Codes, + _UInt16Codes, + _UInt32Codes, + _UInt64Codes, + _UIntCodes, +) + +_ArrayType = TypeVar("_ArrayType", bound=ndarray[Any, Any]) + +_DTypeLikeFloat32 = Union[ + dtype[float32], + _SupportsDType[dtype[float32]], + Type[float32], + _Float32Codes, + _SingleCodes, +] + +_DTypeLikeFloat64 = Union[ + dtype[float64], + _SupportsDType[dtype[float64]], + Type[float], + Type[float64], + _Float64Codes, + _DoubleCodes, +] + +class Generator: + def __init__(self, bit_generator: BitGenerator) -> None: ... + def __repr__(self) -> str: ... + def __str__(self) -> str: ... + def __getstate__(self) -> Dict[str, Any]: ... + def __setstate__(self, state: Dict[str, Any]) -> None: ... + def __reduce__(self) -> Tuple[Callable[[str], Generator], Tuple[str], Dict[str, Any]]: ... + @property + def bit_generator(self) -> BitGenerator: ... + def bytes(self, length: int) -> bytes: ... + @overload + def standard_normal( # type: ignore[misc] + self, + size: None = ..., + dtype: Union[_DTypeLikeFloat32, _DTypeLikeFloat64] = ..., + out: None = ..., + ) -> float: ... + @overload + def standard_normal( # type: ignore[misc] + self, + size: _ShapeLike = ..., + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def standard_normal( # type: ignore[misc] + self, + *, + out: ndarray[Any, dtype[float64]] = ..., + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def standard_normal( # type: ignore[misc] + self, + size: _ShapeLike = ..., + dtype: _DTypeLikeFloat32 = ..., + out: Optional[ndarray[Any, dtype[float32]]] = ..., + ) -> ndarray[Any, dtype[float32]]: ... + @overload + def standard_normal( # type: ignore[misc] + self, + size: _ShapeLike = ..., + dtype: _DTypeLikeFloat64 = ..., + out: Optional[ndarray[Any, dtype[float64]]] = ..., + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def permutation(self, x: int, axis: int = ...) -> ndarray[Any, dtype[int64]]: ... + @overload + def permutation(self, x: ArrayLike, axis: int = ...) -> ndarray[Any, Any]: ... + @overload + def standard_exponential( # type: ignore[misc] + self, + size: None = ..., + dtype: Union[_DTypeLikeFloat32, _DTypeLikeFloat64] = ..., + method: Literal["zig", "inv"] = ..., + out: None = ..., + ) -> float: ... + @overload + def standard_exponential( + self, + size: _ShapeLike = ..., + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def standard_exponential( + self, + *, + out: ndarray[Any, dtype[float64]] = ..., + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def standard_exponential( + self, + size: _ShapeLike = ..., + *, + method: Literal["zig", "inv"] = ..., + out: Optional[ndarray[Any, dtype[float64]]] = ..., + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def standard_exponential( + self, + size: _ShapeLike = ..., + dtype: _DTypeLikeFloat32 = ..., + method: Literal["zig", "inv"] = ..., + out: Optional[ndarray[Any, dtype[float32]]] = ..., + ) -> ndarray[Any, dtype[float32]]: ... + @overload + def standard_exponential( + self, + size: _ShapeLike = ..., + dtype: _DTypeLikeFloat64 = ..., + method: Literal["zig", "inv"] = ..., + out: Optional[ndarray[Any, dtype[float64]]] = ..., + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def random( # type: ignore[misc] + self, + size: None = ..., + dtype: Union[_DTypeLikeFloat32, _DTypeLikeFloat64] = ..., + out: None = ..., + ) -> float: ... + @overload + def random( + self, + *, + out: ndarray[Any, dtype[float64]] = ..., + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def random( + self, + size: _ShapeLike = ..., + *, + out: Optional[ndarray[Any, dtype[float64]]] = ..., + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def random( + self, + size: _ShapeLike = ..., + dtype: _DTypeLikeFloat32 = ..., + out: Optional[ndarray[Any, dtype[float32]]] = ..., + ) -> ndarray[Any, dtype[float32]]: ... + @overload + def random( + self, + size: _ShapeLike = ..., + dtype: _DTypeLikeFloat64 = ..., + out: Optional[ndarray[Any, dtype[float64]]] = ..., + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def beta(self, a: float, b: float, size: None = ...) -> float: ... # type: ignore[misc] + @overload + def beta( + self, a: _ArrayLikeFloat_co, b: _ArrayLikeFloat_co, size: Optional[_ShapeLike] = ... + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def exponential(self, scale: float = ..., size: None = ...) -> float: ... # type: ignore[misc] + @overload + def exponential( + self, scale: _ArrayLikeFloat_co = ..., size: Optional[_ShapeLike] = ... + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def integers( # type: ignore[misc] + self, + low: int, + high: Optional[int] = ..., + ) -> int: ... + @overload + def integers( # type: ignore[misc] + self, + low: int, + high: Optional[int] = ..., + size: None = ..., + dtype: _DTypeLikeBool = ..., + endpoint: bool = ..., + ) -> bool: ... + @overload + def integers( # type: ignore[misc] + self, + low: int, + high: Optional[int] = ..., + size: None = ..., + dtype: Union[_DTypeLikeInt, _DTypeLikeUInt] = ..., + endpoint: bool = ..., + ) -> int: ... + @overload + def integers( # type: ignore[misc] + self, + low: _ArrayLikeInt_co, + high: Optional[_ArrayLikeInt_co] = ..., + size: Optional[_ShapeLike] = ..., + ) -> ndarray[Any, dtype[int64]]: ... + @overload + def integers( # type: ignore[misc] + self, + low: _ArrayLikeInt_co, + high: Optional[_ArrayLikeInt_co] = ..., + size: Optional[_ShapeLike] = ..., + dtype: _DTypeLikeBool = ..., + endpoint: bool = ..., + ) -> ndarray[Any, dtype[bool_]]: ... + @overload + def integers( # type: ignore[misc] + self, + low: _ArrayLikeInt_co, + high: Optional[_ArrayLikeInt_co] = ..., + size: Optional[_ShapeLike] = ..., + dtype: Union[dtype[int8], Type[int8], _Int8Codes, _SupportsDType[dtype[int8]]] = ..., + endpoint: bool = ..., + ) -> ndarray[Any, dtype[int8]]: ... + @overload + def integers( # type: ignore[misc] + self, + low: _ArrayLikeInt_co, + high: Optional[_ArrayLikeInt_co] = ..., + size: Optional[_ShapeLike] = ..., + dtype: Union[dtype[int16], Type[int16], _Int16Codes, _SupportsDType[dtype[int16]]] = ..., + endpoint: bool = ..., + ) -> ndarray[Any, dtype[int16]]: ... + @overload + def integers( # type: ignore[misc] + self, + low: _ArrayLikeInt_co, + high: Optional[_ArrayLikeInt_co] = ..., + size: Optional[_ShapeLike] = ..., + dtype: Union[dtype[int32], Type[int32], _Int32Codes, _SupportsDType[dtype[int32]]] = ..., + endpoint: bool = ..., + ) -> ndarray[Any, dtype[Union[int32]]]: ... + @overload + def integers( # type: ignore[misc] + self, + low: _ArrayLikeInt_co, + high: Optional[_ArrayLikeInt_co] = ..., + size: Optional[_ShapeLike] = ..., + dtype: Optional[ + Union[dtype[int64], Type[int64], _Int64Codes, _SupportsDType[dtype[int64]]] + ] = ..., + endpoint: bool = ..., + ) -> ndarray[Any, dtype[int64]]: ... + @overload + def integers( # type: ignore[misc] + self, + low: _ArrayLikeInt_co, + high: Optional[_ArrayLikeInt_co] = ..., + size: Optional[_ShapeLike] = ..., + dtype: Union[dtype[uint8], Type[uint8], _UInt8Codes, _SupportsDType[dtype[uint8]]] = ..., + endpoint: bool = ..., + ) -> ndarray[Any, dtype[uint8]]: ... + @overload + def integers( # type: ignore[misc] + self, + low: _ArrayLikeInt_co, + high: Optional[_ArrayLikeInt_co] = ..., + size: Optional[_ShapeLike] = ..., + dtype: Union[ + dtype[uint16], Type[uint16], _UInt16Codes, _SupportsDType[dtype[uint16]] + ] = ..., + endpoint: bool = ..., + ) -> ndarray[Any, dtype[Union[uint16]]]: ... + @overload + def integers( # type: ignore[misc] + self, + low: _ArrayLikeInt_co, + high: Optional[_ArrayLikeInt_co] = ..., + size: Optional[_ShapeLike] = ..., + dtype: Union[ + dtype[uint32], Type[uint32], _UInt32Codes, _SupportsDType[dtype[uint32]] + ] = ..., + endpoint: bool = ..., + ) -> ndarray[Any, dtype[uint32]]: ... + @overload + def integers( # type: ignore[misc] + self, + low: _ArrayLikeInt_co, + high: Optional[_ArrayLikeInt_co] = ..., + size: Optional[_ShapeLike] = ..., + dtype: Union[ + dtype[uint64], Type[uint64], _UInt64Codes, _SupportsDType[dtype[uint64]] + ] = ..., + endpoint: bool = ..., + ) -> ndarray[Any, dtype[uint64]]: ... + @overload + def integers( # type: ignore[misc] + self, + low: _ArrayLikeInt_co, + high: Optional[_ArrayLikeInt_co] = ..., + size: Optional[_ShapeLike] = ..., + dtype: Union[ + dtype[int_], Type[int], Type[int_], _IntCodes, _SupportsDType[dtype[int_]] + ] = ..., + endpoint: bool = ..., + ) -> ndarray[Any, dtype[int_]]: ... + @overload + def integers( # type: ignore[misc] + self, + low: _ArrayLikeInt_co, + high: Optional[_ArrayLikeInt_co] = ..., + size: Optional[_ShapeLike] = ..., + dtype: Union[dtype[uint], Type[uint], _UIntCodes, _SupportsDType[dtype[uint]]] = ..., + endpoint: bool = ..., + ) -> ndarray[Any, dtype[uint]]: ... + # TODO: Use a TypeVar _T here to get away from Any output? Should be int->ndarray[Any,dtype[int64]], ArrayLike[_T] -> Union[_T, ndarray[Any,Any]] + @overload + def choice( + self, + a: int, + size: None = ..., + replace: bool = ..., + p: Optional[_ArrayLikeFloat_co] = ..., + axis: int = ..., + shuffle: bool = ..., + ) -> int: ... + @overload + def choice( + self, + a: int, + size: _ShapeLike = ..., + replace: bool = ..., + p: Optional[_ArrayLikeFloat_co] = ..., + axis: int = ..., + shuffle: bool = ..., + ) -> ndarray[Any, dtype[int64]]: ... + @overload + def choice( + self, + a: ArrayLike, + size: None = ..., + replace: bool = ..., + p: Optional[_ArrayLikeFloat_co] = ..., + axis: int = ..., + shuffle: bool = ..., + ) -> Any: ... + @overload + def choice( + self, + a: ArrayLike, + size: _ShapeLike = ..., + replace: bool = ..., + p: Optional[_ArrayLikeFloat_co] = ..., + axis: int = ..., + shuffle: bool = ..., + ) -> ndarray[Any, Any]: ... + @overload + def uniform(self, low: float = ..., high: float = ..., size: None = ...) -> float: ... # type: ignore[misc] + @overload + def uniform( + self, + low: _ArrayLikeFloat_co = ..., + high: _ArrayLikeFloat_co = ..., + size: Optional[_ShapeLike] = ..., + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def normal(self, loc: float = ..., scale: float = ..., size: None = ...) -> float: ... # type: ignore[misc] + @overload + def normal( + self, + loc: _ArrayLikeFloat_co = ..., + scale: _ArrayLikeFloat_co = ..., + size: Optional[_ShapeLike] = ..., + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def standard_gamma( # type: ignore[misc] + self, + shape: float, + size: None = ..., + dtype: Union[_DTypeLikeFloat32, _DTypeLikeFloat64] = ..., + out: None = ..., + ) -> float: ... + @overload + def standard_gamma( + self, + shape: _ArrayLikeFloat_co, + size: Optional[_ShapeLike] = ..., + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def standard_gamma( + self, + shape: _ArrayLikeFloat_co, + *, + out: ndarray[Any, dtype[float64]] = ..., + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def standard_gamma( + self, + shape: _ArrayLikeFloat_co, + size: Optional[_ShapeLike] = ..., + dtype: _DTypeLikeFloat32 = ..., + out: Optional[ndarray[Any, dtype[float32]]] = ..., + ) -> ndarray[Any, dtype[float32]]: ... + @overload + def standard_gamma( + self, + shape: _ArrayLikeFloat_co, + size: Optional[_ShapeLike] = ..., + dtype: _DTypeLikeFloat64 = ..., + out: Optional[ndarray[Any, dtype[float64]]] = ..., + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def gamma(self, shape: float, scale: float = ..., size: None = ...) -> float: ... # type: ignore[misc] + @overload + def gamma( + self, + shape: _ArrayLikeFloat_co, + scale: _ArrayLikeFloat_co = ..., + size: Optional[_ShapeLike] = ..., + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def f(self, dfnum: float, dfden: float, size: None = ...) -> float: ... # type: ignore[misc] + @overload + def f( + self, dfnum: _ArrayLikeFloat_co, dfden: _ArrayLikeFloat_co, size: Optional[_ShapeLike] = ... + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def noncentral_f(self, dfnum: float, dfden: float, nonc: float, size: None = ...) -> float: ... # type: ignore[misc] + @overload + def noncentral_f( + self, + dfnum: _ArrayLikeFloat_co, + dfden: _ArrayLikeFloat_co, + nonc: _ArrayLikeFloat_co, + size: Optional[_ShapeLike] = ..., + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def chisquare(self, df: float, size: None = ...) -> float: ... # type: ignore[misc] + @overload + def chisquare( + self, df: _ArrayLikeFloat_co, size: Optional[_ShapeLike] = ... + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def noncentral_chisquare(self, df: float, nonc: float, size: None = ...) -> float: ... # type: ignore[misc] + @overload + def noncentral_chisquare( + self, df: _ArrayLikeFloat_co, nonc: _ArrayLikeFloat_co, size: Optional[_ShapeLike] = ... + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def standard_t(self, df: float, size: None = ...) -> float: ... # type: ignore[misc] + @overload + def standard_t( + self, df: _ArrayLikeFloat_co, size: None = ... + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def standard_t( + self, df: _ArrayLikeFloat_co, size: _ShapeLike = ... + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def vonmises(self, mu: float, kappa: float, size: None = ...) -> float: ... # type: ignore[misc] + @overload + def vonmises( + self, mu: _ArrayLikeFloat_co, kappa: _ArrayLikeFloat_co, size: Optional[_ShapeLike] = ... + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def pareto(self, a: float, size: None = ...) -> float: ... # type: ignore[misc] + @overload + def pareto( + self, a: _ArrayLikeFloat_co, size: Optional[_ShapeLike] = ... + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def weibull(self, a: float, size: None = ...) -> float: ... # type: ignore[misc] + @overload + def weibull( + self, a: _ArrayLikeFloat_co, size: Optional[_ShapeLike] = ... + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def power(self, a: float, size: None = ...) -> float: ... # type: ignore[misc] + @overload + def power( + self, a: _ArrayLikeFloat_co, size: Optional[_ShapeLike] = ... + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def standard_cauchy(self, size: None = ...) -> float: ... # type: ignore[misc] + @overload + def standard_cauchy(self, size: _ShapeLike = ...) -> ndarray[Any, dtype[float64]]: ... + @overload + def laplace(self, loc: float = ..., scale: float = ..., size: None = ...) -> float: ... # type: ignore[misc] + @overload + def laplace( + self, + loc: _ArrayLikeFloat_co = ..., + scale: _ArrayLikeFloat_co = ..., + size: Optional[_ShapeLike] = ..., + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def gumbel(self, loc: float = ..., scale: float = ..., size: None = ...) -> float: ... # type: ignore[misc] + @overload + def gumbel( + self, + loc: _ArrayLikeFloat_co = ..., + scale: _ArrayLikeFloat_co = ..., + size: Optional[_ShapeLike] = ..., + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def logistic(self, loc: float = ..., scale: float = ..., size: None = ...) -> float: ... # type: ignore[misc] + @overload + def logistic( + self, + loc: _ArrayLikeFloat_co = ..., + scale: _ArrayLikeFloat_co = ..., + size: Optional[_ShapeLike] = ..., + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def lognormal(self, mean: float = ..., sigma: float = ..., size: None = ...) -> float: ... # type: ignore[misc] + @overload + def lognormal( + self, + mean: _ArrayLikeFloat_co = ..., + sigma: _ArrayLikeFloat_co = ..., + size: Optional[_ShapeLike] = ..., + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def rayleigh(self, scale: float = ..., size: None = ...) -> float: ... # type: ignore[misc] + @overload + def rayleigh( + self, scale: _ArrayLikeFloat_co = ..., size: Optional[_ShapeLike] = ... + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def wald(self, mean: float, scale: float, size: None = ...) -> float: ... # type: ignore[misc] + @overload + def wald( + self, mean: _ArrayLikeFloat_co, scale: _ArrayLikeFloat_co, size: Optional[_ShapeLike] = ... + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def triangular(self, left: float, mode: float, right: float, size: None = ...) -> float: ... # type: ignore[misc] + @overload + def triangular( + self, + left: _ArrayLikeFloat_co, + mode: _ArrayLikeFloat_co, + right: _ArrayLikeFloat_co, + size: Optional[_ShapeLike] = ..., + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def binomial(self, n: int, p: float, size: None = ...) -> int: ... # type: ignore[misc] + @overload + def binomial( + self, n: _ArrayLikeInt_co, p: _ArrayLikeFloat_co, size: Optional[_ShapeLike] = ... + ) -> ndarray[Any, dtype[int64]]: ... + @overload + def negative_binomial(self, n: float, p: float, size: None = ...) -> int: ... # type: ignore[misc] + @overload + def negative_binomial( + self, n: _ArrayLikeFloat_co, p: _ArrayLikeFloat_co, size: Optional[_ShapeLike] = ... + ) -> ndarray[Any, dtype[int64]]: ... + @overload + def poisson(self, lam: float = ..., size: None = ...) -> int: ... # type: ignore[misc] + @overload + def poisson( + self, lam: _ArrayLikeFloat_co = ..., size: Optional[_ShapeLike] = ... + ) -> ndarray[Any, dtype[int64]]: ... + @overload + def zipf(self, a: float, size: None = ...) -> int: ... # type: ignore[misc] + @overload + def zipf( + self, a: _ArrayLikeFloat_co, size: Optional[_ShapeLike] = ... + ) -> ndarray[Any, dtype[int64]]: ... + @overload + def geometric(self, p: float, size: None = ...) -> int: ... # type: ignore[misc] + @overload + def geometric( + self, p: _ArrayLikeFloat_co, size: Optional[_ShapeLike] = ... + ) -> ndarray[Any, dtype[int64]]: ... + @overload + def hypergeometric(self, ngood: int, nbad: int, nsample: int, size: None = ...) -> int: ... # type: ignore[misc] + @overload + def hypergeometric( + self, + ngood: _ArrayLikeInt_co, + nbad: _ArrayLikeInt_co, + nsample: _ArrayLikeInt_co, + size: Optional[_ShapeLike] = ..., + ) -> ndarray[Any, dtype[int64]]: ... + @overload + def logseries(self, p: float, size: None = ...) -> int: ... # type: ignore[misc] + @overload + def logseries( + self, p: _ArrayLikeFloat_co, size: Optional[_ShapeLike] = ... + ) -> ndarray[Any, dtype[int64]]: ... + def multivariate_normal( + self, + mean: _ArrayLikeFloat_co, + cov: _ArrayLikeFloat_co, + size: Optional[_ShapeLike] = ..., + check_valid: Literal["warn", "raise", "ignore"] = ..., + tol: float = ..., + *, + method: Literal["svd", "eigh", "cholesky"] = ..., + ) -> ndarray[Any, dtype[float64]]: ... + def multinomial( + self, n: _ArrayLikeInt_co, + pvals: _ArrayLikeFloat_co, + size: Optional[_ShapeLike] = ... + ) -> ndarray[Any, dtype[int64]]: ... + def multivariate_hypergeometric( + self, + colors: _ArrayLikeInt_co, + nsample: int, + size: Optional[_ShapeLike] = ..., + method: Literal["marginals", "count"] = ..., + ) -> ndarray[Any, dtype[int64]]: ... + def dirichlet( + self, alpha: _ArrayLikeFloat_co, size: Optional[_ShapeLike] = ... + ) -> ndarray[Any, dtype[float64]]: ... + def permuted( + self, x: ArrayLike, *, axis: Optional[int] = ..., out: Optional[ndarray[Any, Any]] = ... + ) -> ndarray[Any, Any]: ... + def shuffle(self, x: ArrayLike, axis: int = ...) -> None: ... + +def default_rng( + seed: Union[None, _ArrayLikeInt_co, SeedSequence, BitGenerator, Generator] = ... +) -> Generator: ... diff --git a/numpy/random/_generator.pyx b/numpy/random/_generator.pyx new file mode 100644 index 000000000000..391987a1ecd3 --- /dev/null +++ b/numpy/random/_generator.pyx @@ -0,0 +1,4745 @@ +#!python +#cython: wraparound=False, nonecheck=False, boundscheck=False, cdivision=True, language_level=3 +import operator +import warnings +from collections.abc import Sequence + +from cpython.pycapsule cimport PyCapsule_IsValid, PyCapsule_GetPointer +from cpython cimport (Py_INCREF, PyFloat_AsDouble) +from cpython.mem cimport PyMem_Malloc, PyMem_Free + +cimport cython +import numpy as np +cimport numpy as np +from numpy.core.multiarray import normalize_axis_index + +from .c_distributions cimport * +from libc cimport string +from libc.stdint cimport (uint8_t, uint16_t, uint32_t, uint64_t, + int32_t, int64_t, INT64_MAX, SIZE_MAX) +from ._bounded_integers cimport (_rand_bool, _rand_int32, _rand_int64, + _rand_int16, _rand_int8, _rand_uint64, _rand_uint32, _rand_uint16, + _rand_uint8, _gen_mask) +from ._pcg64 import PCG64 +from numpy.random cimport bitgen_t +from ._common cimport (POISSON_LAM_MAX, CONS_POSITIVE, CONS_NONE, + CONS_NON_NEGATIVE, CONS_BOUNDED_0_1, CONS_BOUNDED_GT_0_1, + CONS_GT_1, CONS_POSITIVE_NOT_NAN, CONS_POISSON, + double_fill, cont, kahan_sum, cont_broadcast_3, float_fill, cont_f, + check_array_constraint, check_constraint, disc, discrete_broadcast_iii, + validate_output_shape + ) + +cdef extern from "numpy/arrayobject.h": + int PyArray_ResolveWritebackIfCopy(np.ndarray) + object PyArray_FromArray(np.PyArrayObject *, np.PyArray_Descr *, int) + + enum: + NPY_ARRAY_WRITEBACKIFCOPY + +np.import_array() + +cdef int64_t _safe_sum_nonneg_int64(size_t num_colors, int64_t *colors): + """ + Sum the values in the array `colors`. + + Return -1 if an overflow occurs. + The values in *colors are assumed to be nonnegative. + """ + cdef size_t i + cdef int64_t sum + + sum = 0 + for i in range(num_colors): + if colors[i] > INT64_MAX - sum: + return -1 + sum += colors[i] + return sum + + +cdef inline void _shuffle_raw_wrap(bitgen_t *bitgen, np.npy_intp n, + np.npy_intp first, np.npy_intp itemsize, + np.npy_intp stride, + char* data, char* buf) nogil: + # We trick gcc into providing a specialized implementation for + # the most common case, yielding a ~33% performance improvement. + # Note that apparently, only one branch can ever be specialized. + if itemsize == sizeof(np.npy_intp): + _shuffle_raw(bitgen, n, first, sizeof(np.npy_intp), stride, data, buf) + else: + _shuffle_raw(bitgen, n, first, itemsize, stride, data, buf) + + +cdef inline void _shuffle_raw(bitgen_t *bitgen, np.npy_intp n, + np.npy_intp first, np.npy_intp itemsize, + np.npy_intp stride, + char* data, char* buf) nogil: + """ + Parameters + ---------- + bitgen + Pointer to a bitgen_t instance. + n + Number of elements in data + first + First observation to shuffle. Shuffles n-1, + n-2, ..., first, so that when first=1 the entire + array is shuffled + itemsize + Size in bytes of item + stride + Array stride + data + Location of data + buf + Location of buffer (itemsize) + """ + cdef np.npy_intp i, j + + for i in reversed(range(first, n)): + j = random_interval(bitgen, i) + string.memcpy(buf, data + j * stride, itemsize) + string.memcpy(data + j * stride, data + i * stride, itemsize) + string.memcpy(data + i * stride, buf, itemsize) + + +cdef inline void _shuffle_int(bitgen_t *bitgen, np.npy_intp n, + np.npy_intp first, int64_t* data) nogil: + """ + Parameters + ---------- + bitgen + Pointer to a bitgen_t instance. + n + Number of elements in data + first + First observation to shuffle. Shuffles n-1, + n-2, ..., first, so that when first=1 the entire + array is shuffled + data + Location of data + """ + cdef np.npy_intp i, j + cdef int64_t temp + for i in reversed(range(first, n)): + j = random_bounded_uint64(bitgen, 0, i, 0, 0) + temp = data[j] + data[j] = data[i] + data[i] = temp + + +cdef bint _check_bit_generator(object bitgen): + """Check if an object satisfies the BitGenerator interface. + """ + if not hasattr(bitgen, "capsule"): + return False + cdef const char *name = "BitGenerator" + return PyCapsule_IsValid(bitgen.capsule, name) + + +cdef class Generator: + """ + Generator(bit_generator) + + Container for the BitGenerators. + + ``Generator`` exposes a number of methods for generating random + numbers drawn from a variety of probability distributions. In addition to + the distribution-specific arguments, each method takes a keyword argument + `size` that defaults to ``None``. If `size` is ``None``, then a single + value is generated and returned. If `size` is an integer, then a 1-D + array filled with generated values is returned. If `size` is a tuple, + then an array with that shape is filled and returned. + + The function :func:`numpy.random.default_rng` will instantiate + a `Generator` with numpy's default `BitGenerator`. + + **No Compatibility Guarantee** + + ``Generator`` does not provide a version compatibility guarantee. In + particular, as better algorithms evolve the bit stream may change. + + Parameters + ---------- + bit_generator : BitGenerator + BitGenerator to use as the core generator. + + Notes + ----- + The Python stdlib module `random` contains pseudo-random number generator + with a number of methods that are similar to the ones available in + ``Generator``. It uses Mersenne Twister, and this bit generator can + be accessed using ``MT19937``. ``Generator``, besides being + NumPy-aware, has the advantage that it provides a much larger number + of probability distributions to choose from. + + Examples + -------- + >>> from numpy.random import Generator, PCG64 + >>> rng = Generator(PCG64()) + >>> rng.standard_normal() + -0.203 # random + + See Also + -------- + default_rng : Recommended constructor for `Generator`. + """ + cdef public object _bit_generator + cdef bitgen_t _bitgen + cdef binomial_t _binomial + cdef object lock + _poisson_lam_max = POISSON_LAM_MAX + + def __init__(self, bit_generator): + self._bit_generator = bit_generator + + capsule = bit_generator.capsule + cdef const char *name = "BitGenerator" + if not PyCapsule_IsValid(capsule, name): + raise ValueError("Invalid bit generator. The bit generator must " + "be instantiated.") + self._bitgen = (<bitgen_t *> PyCapsule_GetPointer(capsule, name))[0] + self.lock = bit_generator.lock + + def __repr__(self): + return self.__str__() + ' at 0x{:X}'.format(id(self)) + + def __str__(self): + _str = self.__class__.__name__ + _str += '(' + self.bit_generator.__class__.__name__ + ')' + return _str + + # Pickling support: + def __getstate__(self): + return self.bit_generator.state + + def __setstate__(self, state): + self.bit_generator.state = state + + def __reduce__(self): + from ._pickle import __generator_ctor + return __generator_ctor, (self.bit_generator.state['bit_generator'],), self.bit_generator.state + + @property + def bit_generator(self): + """ + Gets the bit generator instance used by the generator + + Returns + ------- + bit_generator : BitGenerator + The bit generator instance used by the generator + """ + return self._bit_generator + + def random(self, size=None, dtype=np.float64, out=None): + """ + random(size=None, dtype=np.float64, out=None) + + Return random floats in the half-open interval [0.0, 1.0). + + Results are from the "continuous uniform" distribution over the + stated interval. To sample :math:`Unif[a, b), b > a` multiply + the output of `random` by `(b-a)` and add `a`:: + + (b - a) * random() + a + + Parameters + ---------- + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. Default is None, in which case a + single value is returned. + dtype : dtype, optional + Desired dtype of the result, only `float64` and `float32` are supported. + Byteorder must be native. The default value is np.float64. + out : ndarray, optional + Alternative output array in which to place the result. If size is not None, + it must have the same shape as the provided size and must match the type of + the output values. + + Returns + ------- + out : float or ndarray of floats + Array of random floats of shape `size` (unless ``size=None``, in which + case a single float is returned). + + Examples + -------- + >>> rng = np.random.default_rng() + >>> rng.random() + 0.47108547995356098 # random + >>> type(rng.random()) + <class 'float'> + >>> rng.random((5,)) + array([ 0.30220482, 0.86820401, 0.1654503 , 0.11659149, 0.54323428]) # random + + Three-by-two array of random numbers from [-5, 0): + + >>> 5 * rng.random((3, 2)) - 5 + array([[-3.99149989, -0.52338984], # random + [-2.99091858, -0.79479508], + [-1.23204345, -1.75224494]]) + + """ + cdef double temp + _dtype = np.dtype(dtype) + if _dtype == np.float64: + return double_fill(&random_standard_uniform_fill, &self._bitgen, size, self.lock, out) + elif _dtype == np.float32: + return float_fill(&random_standard_uniform_fill_f, &self._bitgen, size, self.lock, out) + else: + raise TypeError('Unsupported dtype %r for random' % _dtype) + + def beta(self, a, b, size=None): + """ + beta(a, b, size=None) + + Draw samples from a Beta distribution. + + The Beta distribution is a special case of the Dirichlet distribution, + and is related to the Gamma distribution. It has the probability + distribution function + + .. math:: f(x; a,b) = \\frac{1}{B(\\alpha, \\beta)} x^{\\alpha - 1} + (1 - x)^{\\beta - 1}, + + where the normalization, B, is the beta function, + + .. math:: B(\\alpha, \\beta) = \\int_0^1 t^{\\alpha - 1} + (1 - t)^{\\beta - 1} dt. + + It is often seen in Bayesian inference and order statistics. + + Parameters + ---------- + a : float or array_like of floats + Alpha, positive (>0). + b : float or array_like of floats + Beta, positive (>0). + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``a`` and ``b`` are both scalars. + Otherwise, ``np.broadcast(a, b).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized beta distribution. + + """ + return cont(&random_beta, &self._bitgen, size, self.lock, 2, + a, 'a', CONS_POSITIVE, + b, 'b', CONS_POSITIVE, + 0.0, '', CONS_NONE, None) + + def exponential(self, scale=1.0, size=None): + """ + exponential(scale=1.0, size=None) + + Draw samples from an exponential distribution. + + Its probability density function is + + .. math:: f(x; \\frac{1}{\\beta}) = \\frac{1}{\\beta} \\exp(-\\frac{x}{\\beta}), + + for ``x > 0`` and 0 elsewhere. :math:`\\beta` is the scale parameter, + which is the inverse of the rate parameter :math:`\\lambda = 1/\\beta`. + The rate parameter is an alternative, widely used parameterization + of the exponential distribution [3]_. + + The exponential distribution is a continuous analogue of the + geometric distribution. It describes many common situations, such as + the size of raindrops measured over many rainstorms [1]_, or the time + between page requests to Wikipedia [2]_. + + Parameters + ---------- + scale : float or array_like of floats + The scale parameter, :math:`\\beta = 1/\\lambda`. Must be + non-negative. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``scale`` is a scalar. Otherwise, + ``np.array(scale).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized exponential distribution. + + References + ---------- + .. [1] Peyton Z. Peebles Jr., "Probability, Random Variables and + Random Signal Principles", 4th ed, 2001, p. 57. + .. [2] Wikipedia, "Poisson process", + https://en.wikipedia.org/wiki/Poisson_process + .. [3] Wikipedia, "Exponential distribution", + https://en.wikipedia.org/wiki/Exponential_distribution + + """ + return cont(&random_exponential, &self._bitgen, size, self.lock, 1, + scale, 'scale', CONS_NON_NEGATIVE, + 0.0, '', CONS_NONE, + 0.0, '', CONS_NONE, + None) + + def standard_exponential(self, size=None, dtype=np.float64, method='zig', out=None): + """ + standard_exponential(size=None, dtype=np.float64, method='zig', out=None) + + Draw samples from the standard exponential distribution. + + `standard_exponential` is identical to the exponential distribution + with a scale parameter of 1. + + Parameters + ---------- + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. Default is None, in which case a + single value is returned. + dtype : dtype, optional + Desired dtype of the result, only `float64` and `float32` are supported. + Byteorder must be native. The default value is np.float64. + method : str, optional + Either 'inv' or 'zig'. 'inv' uses the default inverse CDF method. + 'zig' uses the much faster Ziggurat method of Marsaglia and Tsang. + out : ndarray, optional + Alternative output array in which to place the result. If size is not None, + it must have the same shape as the provided size and must match the type of + the output values. + + Returns + ------- + out : float or ndarray + Drawn samples. + + Examples + -------- + Output a 3x8000 array: + + >>> n = np.random.default_rng().standard_exponential((3, 8000)) + + """ + _dtype = np.dtype(dtype) + if _dtype == np.float64: + if method == 'zig': + return double_fill(&random_standard_exponential_fill, &self._bitgen, size, self.lock, out) + else: + return double_fill(&random_standard_exponential_inv_fill, &self._bitgen, size, self.lock, out) + elif _dtype == np.float32: + if method == 'zig': + return float_fill(&random_standard_exponential_fill_f, &self._bitgen, size, self.lock, out) + else: + return float_fill(&random_standard_exponential_inv_fill_f, &self._bitgen, size, self.lock, out) + else: + raise TypeError('Unsupported dtype %r for standard_exponential' + % _dtype) + + def integers(self, low, high=None, size=None, dtype=np.int64, endpoint=False): + """ + integers(low, high=None, size=None, dtype=np.int64, endpoint=False) + + Return random integers from `low` (inclusive) to `high` (exclusive), or + if endpoint=True, `low` (inclusive) to `high` (inclusive). Replaces + `RandomState.randint` (with endpoint=False) and + `RandomState.random_integers` (with endpoint=True) + + Return random integers from the "discrete uniform" distribution of + the specified dtype. If `high` is None (the default), then results are + from 0 to `low`. + + Parameters + ---------- + low : int or array-like of ints + Lowest (signed) integers to be drawn from the distribution (unless + ``high=None``, in which case this parameter is 0 and this value is + used for `high`). + high : int or array-like of ints, optional + If provided, one above the largest (signed) integer to be drawn + from the distribution (see above for behavior if ``high=None``). + If array-like, must contain integer values + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. Default is None, in which case a + single value is returned. + dtype : dtype, optional + Desired dtype of the result. Byteorder must be native. + The default value is np.int64. + endpoint : bool, optional + If true, sample from the interval [low, high] instead of the + default [low, high) + Defaults to False + + Returns + ------- + out : int or ndarray of ints + `size`-shaped array of random integers from the appropriate + distribution, or a single such random int if `size` not provided. + + Notes + ----- + When using broadcasting with uint64 dtypes, the maximum value (2**64) + cannot be represented as a standard integer type. The high array (or + low if high is None) must have object dtype, e.g., array([2**64]). + + Examples + -------- + >>> rng = np.random.default_rng() + >>> rng.integers(2, size=10) + array([1, 0, 0, 0, 1, 1, 0, 0, 1, 0]) # random + >>> rng.integers(1, size=10) + array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) + + Generate a 2 x 4 array of ints between 0 and 4, inclusive: + + >>> rng.integers(5, size=(2, 4)) + array([[4, 0, 2, 1], + [3, 2, 2, 0]]) # random + + Generate a 1 x 3 array with 3 different upper bounds + + >>> rng.integers(1, [3, 5, 10]) + array([2, 2, 9]) # random + + Generate a 1 by 3 array with 3 different lower bounds + + >>> rng.integers([1, 5, 7], 10) + array([9, 8, 7]) # random + + Generate a 2 by 4 array using broadcasting with dtype of uint8 + + >>> rng.integers([1, 3, 5, 7], [[10], [20]], dtype=np.uint8) + array([[ 8, 6, 9, 7], + [ 1, 16, 9, 12]], dtype=uint8) # random + + References + ---------- + .. [1] Daniel Lemire., "Fast Random Integer Generation in an Interval", + ACM Transactions on Modeling and Computer Simulation 29 (1), 2019, + http://arxiv.org/abs/1805.10941. + + """ + if high is None: + high = low + low = 0 + + _dtype = np.dtype(dtype) + + # Implementation detail: the old API used a masked method to generate + # bounded uniform integers. Lemire's method is preferable since it is + # faster. randomgen allows a choice, we will always use the faster one. + cdef bint _masked = False + + if _dtype == np.int32: + ret = _rand_int32(low, high, size, _masked, endpoint, &self._bitgen, self.lock) + elif _dtype == np.int64: + ret = _rand_int64(low, high, size, _masked, endpoint, &self._bitgen, self.lock) + elif _dtype == np.int16: + ret = _rand_int16(low, high, size, _masked, endpoint, &self._bitgen, self.lock) + elif _dtype == np.int8: + ret = _rand_int8(low, high, size, _masked, endpoint, &self._bitgen, self.lock) + elif _dtype == np.uint64: + ret = _rand_uint64(low, high, size, _masked, endpoint, &self._bitgen, self.lock) + elif _dtype == np.uint32: + ret = _rand_uint32(low, high, size, _masked, endpoint, &self._bitgen, self.lock) + elif _dtype == np.uint16: + ret = _rand_uint16(low, high, size, _masked, endpoint, &self._bitgen, self.lock) + elif _dtype == np.uint8: + ret = _rand_uint8(low, high, size, _masked, endpoint, &self._bitgen, self.lock) + elif _dtype == np.bool_: + ret = _rand_bool(low, high, size, _masked, endpoint, &self._bitgen, self.lock) + elif not _dtype.isnative: + raise ValueError('Providing a dtype with a non-native byteorder ' + 'is not supported. If you require ' + 'platform-independent byteorder, call byteswap ' + 'when required.') + else: + raise TypeError('Unsupported dtype %r for integers' % _dtype) + + + if size is None and dtype in (bool, int): + if np.array(ret).shape == (): + return dtype(ret) + return ret + + def bytes(self, np.npy_intp length): + """ + bytes(length) + + Return random bytes. + + Parameters + ---------- + length : int + Number of random bytes. + + Returns + ------- + out : bytes + String of length `length`. + + Examples + -------- + >>> np.random.default_rng().bytes(10) + b'\\xfeC\\x9b\\x86\\x17\\xf2\\xa1\\xafcp' # random + + """ + cdef Py_ssize_t n_uint32 = ((length - 1) // 4 + 1) + # Interpret the uint32s as little-endian to convert them to bytes + # consistently. + return self.integers(0, 4294967296, size=n_uint32, + dtype=np.uint32).astype('<u4').tobytes()[:length] + + @cython.wraparound(True) + def choice(self, a, size=None, replace=True, p=None, axis=0, bint shuffle=True): + """ + choice(a, size=None, replace=True, p=None, axis=0, shuffle=True) + + Generates a random sample from a given array + + Parameters + ---------- + a : {array_like, int} + If an ndarray, a random sample is generated from its elements. + If an int, the random sample is generated from np.arange(a). + size : {int, tuple[int]}, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn from the 1-d `a`. If `a` has more + than one dimension, the `size` shape will be inserted into the + `axis` dimension, so the output ``ndim`` will be ``a.ndim - 1 + + len(size)``. Default is None, in which case a single value is + returned. + replace : bool, optional + Whether the sample is with or without replacement. Default is True, + meaning that a value of ``a`` can be selected multiple times. + p : 1-D array_like, optional + The probabilities associated with each entry in a. + If not given, the sample assumes a uniform distribution over all + entries in ``a``. + axis : int, optional + The axis along which the selection is performed. The default, 0, + selects by row. + shuffle : bool, optional + Whether the sample is shuffled when sampling without replacement. + Default is True, False provides a speedup. + + Returns + ------- + samples : single item or ndarray + The generated random samples + + Raises + ------ + ValueError + If a is an int and less than zero, if p is not 1-dimensional, if + a is array-like with a size 0, if p is not a vector of + probabilities, if a and p have different lengths, or if + replace=False and the sample size is greater than the population + size. + + See Also + -------- + integers, shuffle, permutation + + Notes + ----- + Setting user-specified probabilities through ``p`` uses a more general but less + efficient sampler than the default. The general sampler produces a different sample + than the optimized sampler even if each element of ``p`` is 1 / len(a). + + Examples + -------- + Generate a uniform random sample from np.arange(5) of size 3: + + >>> rng = np.random.default_rng() + >>> rng.choice(5, 3) + array([0, 3, 4]) # random + >>> #This is equivalent to rng.integers(0,5,3) + + Generate a non-uniform random sample from np.arange(5) of size 3: + + >>> rng.choice(5, 3, p=[0.1, 0, 0.3, 0.6, 0]) + array([3, 3, 0]) # random + + Generate a uniform random sample from np.arange(5) of size 3 without + replacement: + + >>> rng.choice(5, 3, replace=False) + array([3,1,0]) # random + >>> #This is equivalent to rng.permutation(np.arange(5))[:3] + + Generate a uniform random sample from a 2-D array along the first + axis (the default), without replacement: + + >>> rng.choice([[0, 1, 2], [3, 4, 5], [6, 7, 8]], 2, replace=False) + array([[3, 4, 5], # random + [0, 1, 2]]) + + Generate a non-uniform random sample from np.arange(5) of size + 3 without replacement: + + >>> rng.choice(5, 3, replace=False, p=[0.1, 0, 0.3, 0.6, 0]) + array([2, 3, 0]) # random + + Any of the above can be repeated with an arbitrary array-like + instead of just integers. For instance: + + >>> aa_milne_arr = ['pooh', 'rabbit', 'piglet', 'Christopher'] + >>> rng.choice(aa_milne_arr, 5, p=[0.5, 0.1, 0.1, 0.3]) + array(['pooh', 'pooh', 'pooh', 'Christopher', 'piglet'], # random + dtype='<U11') + + """ + + cdef int64_t val, t, loc, size_i, pop_size_i + cdef int64_t *idx_data + cdef np.npy_intp j + cdef uint64_t set_size, mask + cdef uint64_t[::1] hash_set + # Format and Verify input + a_original = a + a = np.array(a, copy=False) + if a.ndim == 0: + try: + # __index__ must return an integer by python rules. + pop_size = operator.index(a.item()) + except TypeError as exc: + raise ValueError("a must be a sequence or an integer, " + f"not {type(a_original)}") from exc + if pop_size <= 0 and np.prod(size) != 0: + raise ValueError("a must be a positive integer unless no " + "samples are taken") + else: + pop_size = a.shape[axis] + if pop_size == 0 and np.prod(size) != 0: + raise ValueError("a cannot be empty unless no samples are " + "taken") + + if p is not None: + d = len(p) + + atol = np.sqrt(np.finfo(np.float64).eps) + if isinstance(p, np.ndarray): + if np.issubdtype(p.dtype, np.floating): + atol = max(atol, np.sqrt(np.finfo(p.dtype).eps)) + + p = <np.ndarray>np.PyArray_FROM_OTF( + p, np.NPY_DOUBLE, np.NPY_ALIGNED | np.NPY_ARRAY_C_CONTIGUOUS) + pix = <double*>np.PyArray_DATA(p) + + if p.ndim != 1: + raise ValueError("p must be 1-dimensional") + if p.size != pop_size: + raise ValueError("a and p must have same size") + p_sum = kahan_sum(pix, d) + if np.isnan(p_sum): + raise ValueError("probabilities contain NaN") + if np.logical_or.reduce(p < 0): + raise ValueError("probabilities are not non-negative") + if abs(p_sum - 1.) > atol: + raise ValueError("probabilities do not sum to 1") + + # `shape == None` means `shape == ()`, but with scalar unpacking at the + # end + is_scalar = size is None + if not is_scalar: + shape = size + size = np.prod(shape, dtype=np.intp) + else: + shape = () + size = 1 + + # Actual sampling + if replace: + if p is not None: + cdf = p.cumsum() + cdf /= cdf[-1] + uniform_samples = self.random(shape) + idx = cdf.searchsorted(uniform_samples, side='right') + # searchsorted returns a scalar + idx = np.array(idx, copy=False, dtype=np.int64) + else: + idx = self.integers(0, pop_size, size=shape, dtype=np.int64) + else: + if size > pop_size: + raise ValueError("Cannot take a larger sample than " + "population when replace is False") + elif size < 0: + raise ValueError("negative dimensions are not allowed") + + if p is not None: + if np.count_nonzero(p > 0) < size: + raise ValueError("Fewer non-zero entries in p than size") + n_uniq = 0 + p = p.copy() + found = np.zeros(shape, dtype=np.int64) + flat_found = found.ravel() + while n_uniq < size: + x = self.random((size - n_uniq,)) + if n_uniq > 0: + p[flat_found[0:n_uniq]] = 0 + cdf = np.cumsum(p) + cdf /= cdf[-1] + new = cdf.searchsorted(x, side='right') + _, unique_indices = np.unique(new, return_index=True) + unique_indices.sort() + new = new.take(unique_indices) + flat_found[n_uniq:n_uniq + new.size] = new + n_uniq += new.size + idx = found + else: + size_i = size + pop_size_i = pop_size + # This is a heuristic tuning. should be improvable + if shuffle: + cutoff = 50 + else: + cutoff = 20 + if pop_size_i > 10000 and (size_i > (pop_size_i // cutoff)): + # Tail shuffle size elements + idx = np.PyArray_Arange(0, pop_size_i, 1, np.NPY_INT64) + idx_data = <int64_t*>(<np.ndarray>idx).data + with self.lock, nogil: + _shuffle_int(&self._bitgen, pop_size_i, + max(pop_size_i - size_i, 1), idx_data) + # Copy to allow potentially large array backing idx to be gc + idx = idx[(pop_size - size):].copy() + else: + # Floyd's algorithm + idx = np.empty(size, dtype=np.int64) + idx_data = <int64_t*>np.PyArray_DATA(<np.ndarray>idx) + # smallest power of 2 larger than 1.2 * size + set_size = <uint64_t>(1.2 * size_i) + mask = _gen_mask(set_size) + set_size = 1 + mask + hash_set = np.full(set_size, <uint64_t>-1, np.uint64) + with self.lock, cython.wraparound(False), nogil: + for j in range(pop_size_i - size_i, pop_size_i): + val = random_bounded_uint64(&self._bitgen, 0, j, 0, 0) + loc = val & mask + while hash_set[loc] != <uint64_t>-1 and hash_set[loc] != <uint64_t>val: + loc = (loc + 1) & mask + if hash_set[loc] == <uint64_t>-1: # then val not in hash_set + hash_set[loc] = val + idx_data[j - pop_size_i + size_i] = val + else: # we need to insert j instead + loc = j & mask + while hash_set[loc] != <uint64_t>-1: + loc = (loc + 1) & mask + hash_set[loc] = j + idx_data[j - pop_size_i + size_i] = j + if shuffle: + _shuffle_int(&self._bitgen, size_i, 1, idx_data) + idx.shape = shape + + if is_scalar and isinstance(idx, np.ndarray): + # In most cases a scalar will have been made an array + idx = idx.item(0) + + # Use samples as indices for a if a is array-like + if a.ndim == 0: + return idx + + if not is_scalar and idx.ndim == 0: + # If size == () then the user requested a 0-d array as opposed to + # a scalar object when size is None. However a[idx] is always a + # scalar and not an array. So this makes sure the result is an + # array, taking into account that np.array(item) may not work + # for object arrays. + res = np.empty((), dtype=a.dtype) + res[()] = a[idx] + return res + + # asarray downcasts on 32-bit platforms, always safe + # no-op on 64-bit platforms + return a.take(np.asarray(idx, dtype=np.intp), axis=axis) + + def uniform(self, low=0.0, high=1.0, size=None): + """ + uniform(low=0.0, high=1.0, size=None) + + Draw samples from a uniform distribution. + + Samples are uniformly distributed over the half-open interval + ``[low, high)`` (includes low, but excludes high). In other words, + any value within the given interval is equally likely to be drawn + by `uniform`. + + Parameters + ---------- + low : float or array_like of floats, optional + Lower boundary of the output interval. All values generated will be + greater than or equal to low. The default value is 0. + high : float or array_like of floats + Upper boundary of the output interval. All values generated will be + less than high. The high limit may be included in the returned array of + floats due to floating-point rounding in the equation + ``low + (high-low) * random_sample()``. high - low must be + non-negative. The default value is 1.0. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``low`` and ``high`` are both scalars. + Otherwise, ``np.broadcast(low, high).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized uniform distribution. + + See Also + -------- + integers : Discrete uniform distribution, yielding integers. + random : Floats uniformly distributed over ``[0, 1)``. + + Notes + ----- + The probability density function of the uniform distribution is + + .. math:: p(x) = \\frac{1}{b - a} + + anywhere within the interval ``[a, b)``, and zero elsewhere. + + When ``high`` == ``low``, values of ``low`` will be returned. + + Examples + -------- + Draw samples from the distribution: + + >>> s = np.random.default_rng().uniform(-1,0,1000) + + All values are within the given interval: + + >>> np.all(s >= -1) + True + >>> np.all(s < 0) + True + + Display the histogram of the samples, along with the + probability density function: + + >>> import matplotlib.pyplot as plt + >>> count, bins, ignored = plt.hist(s, 15, density=True) + >>> plt.plot(bins, np.ones_like(bins), linewidth=2, color='r') + >>> plt.show() + + """ + cdef bint is_scalar = True + cdef np.ndarray alow, ahigh, arange + cdef double _low, _high, rng + cdef object temp + + alow = <np.ndarray>np.PyArray_FROM_OTF(low, np.NPY_DOUBLE, np.NPY_ALIGNED) + ahigh = <np.ndarray>np.PyArray_FROM_OTF(high, np.NPY_DOUBLE, np.NPY_ALIGNED) + + if np.PyArray_NDIM(alow) == np.PyArray_NDIM(ahigh) == 0: + _low = PyFloat_AsDouble(low) + _high = PyFloat_AsDouble(high) + rng = _high - _low + if not np.isfinite(rng): + raise OverflowError('high - low range exceeds valid bounds') + + return cont(&random_uniform, &self._bitgen, size, self.lock, 2, + _low, '', CONS_NONE, + rng, 'high - low', CONS_NON_NEGATIVE, + 0.0, '', CONS_NONE, + None) + + temp = np.subtract(ahigh, alow) + # needed to get around Pyrex's automatic reference-counting + # rules because EnsureArray steals a reference + Py_INCREF(temp) + + arange = <np.ndarray>np.PyArray_EnsureArray(temp) + if not np.all(np.isfinite(arange)): + raise OverflowError('Range exceeds valid bounds') + return cont(&random_uniform, &self._bitgen, size, self.lock, 2, + alow, '', CONS_NONE, + arange, 'high - low', CONS_NON_NEGATIVE, + 0.0, '', CONS_NONE, + None) + + # Complicated, continuous distributions: + def standard_normal(self, size=None, dtype=np.float64, out=None): + """ + standard_normal(size=None, dtype=np.float64, out=None) + + Draw samples from a standard Normal distribution (mean=0, stdev=1). + + Parameters + ---------- + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. Default is None, in which case a + single value is returned. + dtype : dtype, optional + Desired dtype of the result, only `float64` and `float32` are supported. + Byteorder must be native. The default value is np.float64. + out : ndarray, optional + Alternative output array in which to place the result. If size is not None, + it must have the same shape as the provided size and must match the type of + the output values. + + Returns + ------- + out : float or ndarray + A floating-point array of shape ``size`` of drawn samples, or a + single sample if ``size`` was not specified. + + See Also + -------- + normal : + Equivalent function with additional ``loc`` and ``scale`` arguments + for setting the mean and standard deviation. + + Notes + ----- + For random samples from :math:`N(\\mu, \\sigma^2)`, use one of:: + + mu + sigma * rng.standard_normal(size=...) + rng.normal(mu, sigma, size=...) + + Examples + -------- + >>> rng = np.random.default_rng() + >>> rng.standard_normal() + 2.1923875335537315 # random + + >>> s = rng.standard_normal(8000) + >>> s + array([ 0.6888893 , 0.78096262, -0.89086505, ..., 0.49876311, # random + -0.38672696, -0.4685006 ]) # random + >>> s.shape + (8000,) + >>> s = rng.standard_normal(size=(3, 4, 2)) + >>> s.shape + (3, 4, 2) + + Two-by-four array of samples from :math:`N(3, 6.25)`: + + >>> 3 + 2.5 * rng.standard_normal(size=(2, 4)) + array([[-4.49401501, 4.00950034, -1.81814867, 7.29718677], # random + [ 0.39924804, 4.68456316, 4.99394529, 4.84057254]]) # random + + """ + _dtype = np.dtype(dtype) + if _dtype == np.float64: + return double_fill(&random_standard_normal_fill, &self._bitgen, size, self.lock, out) + elif _dtype == np.float32: + return float_fill(&random_standard_normal_fill_f, &self._bitgen, size, self.lock, out) + else: + raise TypeError('Unsupported dtype %r for standard_normal' % _dtype) + + def normal(self, loc=0.0, scale=1.0, size=None): + """ + normal(loc=0.0, scale=1.0, size=None) + + Draw random samples from a normal (Gaussian) distribution. + + The probability density function of the normal distribution, first + derived by De Moivre and 200 years later by both Gauss and Laplace + independently [2]_, is often called the bell curve because of + its characteristic shape (see the example below). + + The normal distributions occurs often in nature. For example, it + describes the commonly occurring distribution of samples influenced + by a large number of tiny, random disturbances, each with its own + unique distribution [2]_. + + Parameters + ---------- + loc : float or array_like of floats + Mean ("centre") of the distribution. + scale : float or array_like of floats + Standard deviation (spread or "width") of the distribution. Must be + non-negative. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``loc`` and ``scale`` are both scalars. + Otherwise, ``np.broadcast(loc, scale).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized normal distribution. + + See Also + -------- + scipy.stats.norm : probability density function, distribution or + cumulative density function, etc. + + Notes + ----- + The probability density for the Gaussian distribution is + + .. math:: p(x) = \\frac{1}{\\sqrt{ 2 \\pi \\sigma^2 }} + e^{ - \\frac{ (x - \\mu)^2 } {2 \\sigma^2} }, + + where :math:`\\mu` is the mean and :math:`\\sigma` the standard + deviation. The square of the standard deviation, :math:`\\sigma^2`, + is called the variance. + + The function has its peak at the mean, and its "spread" increases with + the standard deviation (the function reaches 0.607 times its maximum at + :math:`x + \\sigma` and :math:`x - \\sigma` [2]_). This implies that + :meth:`normal` is more likely to return samples lying close to the + mean, rather than those far away. + + References + ---------- + .. [1] Wikipedia, "Normal distribution", + https://en.wikipedia.org/wiki/Normal_distribution + .. [2] P. R. Peebles Jr., "Central Limit Theorem" in "Probability, + Random Variables and Random Signal Principles", 4th ed., 2001, + pp. 51, 51, 125. + + Examples + -------- + Draw samples from the distribution: + + >>> mu, sigma = 0, 0.1 # mean and standard deviation + >>> s = np.random.default_rng().normal(mu, sigma, 1000) + + Verify the mean and the variance: + + >>> abs(mu - np.mean(s)) + 0.0 # may vary + + >>> abs(sigma - np.std(s, ddof=1)) + 0.0 # may vary + + Display the histogram of the samples, along with + the probability density function: + + >>> import matplotlib.pyplot as plt + >>> count, bins, ignored = plt.hist(s, 30, density=True) + >>> plt.plot(bins, 1/(sigma * np.sqrt(2 * np.pi)) * + ... np.exp( - (bins - mu)**2 / (2 * sigma**2) ), + ... linewidth=2, color='r') + >>> plt.show() + + Two-by-four array of samples from N(3, 6.25): + + >>> np.random.default_rng().normal(3, 2.5, size=(2, 4)) + array([[-4.49401501, 4.00950034, -1.81814867, 7.29718677], # random + [ 0.39924804, 4.68456316, 4.99394529, 4.84057254]]) # random + + """ + return cont(&random_normal, &self._bitgen, size, self.lock, 2, + loc, '', CONS_NONE, + scale, 'scale', CONS_NON_NEGATIVE, + 0.0, '', CONS_NONE, + None) + + def standard_gamma(self, shape, size=None, dtype=np.float64, out=None): + """ + standard_gamma(shape, size=None, dtype=np.float64, out=None) + + Draw samples from a standard Gamma distribution. + + Samples are drawn from a Gamma distribution with specified parameters, + shape (sometimes designated "k") and scale=1. + + Parameters + ---------- + shape : float or array_like of floats + Parameter, must be non-negative. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``shape`` is a scalar. Otherwise, + ``np.array(shape).size`` samples are drawn. + dtype : dtype, optional + Desired dtype of the result, only `float64` and `float32` are supported. + Byteorder must be native. The default value is np.float64. + out : ndarray, optional + Alternative output array in which to place the result. If size is + not None, it must have the same shape as the provided size and + must match the type of the output values. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized standard gamma distribution. + + See Also + -------- + scipy.stats.gamma : probability density function, distribution or + cumulative density function, etc. + + Notes + ----- + The probability density for the Gamma distribution is + + .. math:: p(x) = x^{k-1}\\frac{e^{-x/\\theta}}{\\theta^k\\Gamma(k)}, + + where :math:`k` is the shape and :math:`\\theta` the scale, + and :math:`\\Gamma` is the Gamma function. + + The Gamma distribution is often used to model the times to failure of + electronic components, and arises naturally in processes for which the + waiting times between Poisson distributed events are relevant. + + References + ---------- + .. [1] Weisstein, Eric W. "Gamma Distribution." From MathWorld--A + Wolfram Web Resource. + http://mathworld.wolfram.com/GammaDistribution.html + .. [2] Wikipedia, "Gamma distribution", + https://en.wikipedia.org/wiki/Gamma_distribution + + Examples + -------- + Draw samples from the distribution: + + >>> shape, scale = 2., 1. # mean and width + >>> s = np.random.default_rng().standard_gamma(shape, 1000000) + + Display the histogram of the samples, along with + the probability density function: + + >>> import matplotlib.pyplot as plt + >>> import scipy.special as sps # doctest: +SKIP + >>> count, bins, ignored = plt.hist(s, 50, density=True) + >>> y = bins**(shape-1) * ((np.exp(-bins/scale))/ # doctest: +SKIP + ... (sps.gamma(shape) * scale**shape)) + >>> plt.plot(bins, y, linewidth=2, color='r') # doctest: +SKIP + >>> plt.show() + + """ + cdef void *func + _dtype = np.dtype(dtype) + if _dtype == np.float64: + return cont(&random_standard_gamma, &self._bitgen, size, self.lock, 1, + shape, 'shape', CONS_NON_NEGATIVE, + 0.0, '', CONS_NONE, + 0.0, '', CONS_NONE, + out) + if _dtype == np.float32: + return cont_f(&random_standard_gamma_f, &self._bitgen, size, self.lock, + shape, 'shape', CONS_NON_NEGATIVE, + out) + else: + raise TypeError('Unsupported dtype %r for standard_gamma' % _dtype) + + def gamma(self, shape, scale=1.0, size=None): + """ + gamma(shape, scale=1.0, size=None) + + Draw samples from a Gamma distribution. + + Samples are drawn from a Gamma distribution with specified parameters, + `shape` (sometimes designated "k") and `scale` (sometimes designated + "theta"), where both parameters are > 0. + + Parameters + ---------- + shape : float or array_like of floats + The shape of the gamma distribution. Must be non-negative. + scale : float or array_like of floats, optional + The scale of the gamma distribution. Must be non-negative. + Default is equal to 1. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``shape`` and ``scale`` are both scalars. + Otherwise, ``np.broadcast(shape, scale).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized gamma distribution. + + See Also + -------- + scipy.stats.gamma : probability density function, distribution or + cumulative density function, etc. + + Notes + ----- + The probability density for the Gamma distribution is + + .. math:: p(x) = x^{k-1}\\frac{e^{-x/\\theta}}{\\theta^k\\Gamma(k)}, + + where :math:`k` is the shape and :math:`\\theta` the scale, + and :math:`\\Gamma` is the Gamma function. + + The Gamma distribution is often used to model the times to failure of + electronic components, and arises naturally in processes for which the + waiting times between Poisson distributed events are relevant. + + References + ---------- + .. [1] Weisstein, Eric W. "Gamma Distribution." From MathWorld--A + Wolfram Web Resource. + http://mathworld.wolfram.com/GammaDistribution.html + .. [2] Wikipedia, "Gamma distribution", + https://en.wikipedia.org/wiki/Gamma_distribution + + Examples + -------- + Draw samples from the distribution: + + >>> shape, scale = 2., 2. # mean=4, std=2*sqrt(2) + >>> s = np.random.default_rng().gamma(shape, scale, 1000) + + Display the histogram of the samples, along with + the probability density function: + + >>> import matplotlib.pyplot as plt + >>> import scipy.special as sps # doctest: +SKIP + >>> count, bins, ignored = plt.hist(s, 50, density=True) + >>> y = bins**(shape-1)*(np.exp(-bins/scale) / # doctest: +SKIP + ... (sps.gamma(shape)*scale**shape)) + >>> plt.plot(bins, y, linewidth=2, color='r') # doctest: +SKIP + >>> plt.show() + + """ + return cont(&random_gamma, &self._bitgen, size, self.lock, 2, + shape, 'shape', CONS_NON_NEGATIVE, + scale, 'scale', CONS_NON_NEGATIVE, + 0.0, '', CONS_NONE, None) + + def f(self, dfnum, dfden, size=None): + """ + f(dfnum, dfden, size=None) + + Draw samples from an F distribution. + + Samples are drawn from an F distribution with specified parameters, + `dfnum` (degrees of freedom in numerator) and `dfden` (degrees of + freedom in denominator), where both parameters must be greater than + zero. + + The random variate of the F distribution (also known as the + Fisher distribution) is a continuous probability distribution + that arises in ANOVA tests, and is the ratio of two chi-square + variates. + + Parameters + ---------- + dfnum : float or array_like of floats + Degrees of freedom in numerator, must be > 0. + dfden : float or array_like of float + Degrees of freedom in denominator, must be > 0. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``dfnum`` and ``dfden`` are both scalars. + Otherwise, ``np.broadcast(dfnum, dfden).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized Fisher distribution. + + See Also + -------- + scipy.stats.f : probability density function, distribution or + cumulative density function, etc. + + Notes + ----- + The F statistic is used to compare in-group variances to between-group + variances. Calculating the distribution depends on the sampling, and + so it is a function of the respective degrees of freedom in the + problem. The variable `dfnum` is the number of samples minus one, the + between-groups degrees of freedom, while `dfden` is the within-groups + degrees of freedom, the sum of the number of samples in each group + minus the number of groups. + + References + ---------- + .. [1] Glantz, Stanton A. "Primer of Biostatistics.", McGraw-Hill, + Fifth Edition, 2002. + .. [2] Wikipedia, "F-distribution", + https://en.wikipedia.org/wiki/F-distribution + + Examples + -------- + An example from Glantz[1], pp 47-40: + + Two groups, children of diabetics (25 people) and children from people + without diabetes (25 controls). Fasting blood glucose was measured, + case group had a mean value of 86.1, controls had a mean value of + 82.2. Standard deviations were 2.09 and 2.49 respectively. Are these + data consistent with the null hypothesis that the parents diabetic + status does not affect their children's blood glucose levels? + Calculating the F statistic from the data gives a value of 36.01. + + Draw samples from the distribution: + + >>> dfnum = 1. # between group degrees of freedom + >>> dfden = 48. # within groups degrees of freedom + >>> s = np.random.default_rng().f(dfnum, dfden, 1000) + + The lower bound for the top 1% of the samples is : + + >>> np.sort(s)[-10] + 7.61988120985 # random + + So there is about a 1% chance that the F statistic will exceed 7.62, + the measured value is 36, so the null hypothesis is rejected at the 1% + level. + + """ + return cont(&random_f, &self._bitgen, size, self.lock, 2, + dfnum, 'dfnum', CONS_POSITIVE, + dfden, 'dfden', CONS_POSITIVE, + 0.0, '', CONS_NONE, None) + + def noncentral_f(self, dfnum, dfden, nonc, size=None): + """ + noncentral_f(dfnum, dfden, nonc, size=None) + + Draw samples from the noncentral F distribution. + + Samples are drawn from an F distribution with specified parameters, + `dfnum` (degrees of freedom in numerator) and `dfden` (degrees of + freedom in denominator), where both parameters > 1. + `nonc` is the non-centrality parameter. + + Parameters + ---------- + dfnum : float or array_like of floats + Numerator degrees of freedom, must be > 0. + + .. versionchanged:: 1.14.0 + Earlier NumPy versions required dfnum > 1. + dfden : float or array_like of floats + Denominator degrees of freedom, must be > 0. + nonc : float or array_like of floats + Non-centrality parameter, the sum of the squares of the numerator + means, must be >= 0. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``dfnum``, ``dfden``, and ``nonc`` + are all scalars. Otherwise, ``np.broadcast(dfnum, dfden, nonc).size`` + samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized noncentral Fisher distribution. + + Notes + ----- + When calculating the power of an experiment (power = probability of + rejecting the null hypothesis when a specific alternative is true) the + non-central F statistic becomes important. When the null hypothesis is + true, the F statistic follows a central F distribution. When the null + hypothesis is not true, then it follows a non-central F statistic. + + References + ---------- + .. [1] Weisstein, Eric W. "Noncentral F-Distribution." + From MathWorld--A Wolfram Web Resource. + http://mathworld.wolfram.com/NoncentralF-Distribution.html + .. [2] Wikipedia, "Noncentral F-distribution", + https://en.wikipedia.org/wiki/Noncentral_F-distribution + + Examples + -------- + In a study, testing for a specific alternative to the null hypothesis + requires use of the Noncentral F distribution. We need to calculate the + area in the tail of the distribution that exceeds the value of the F + distribution for the null hypothesis. We'll plot the two probability + distributions for comparison. + + >>> rng = np.random.default_rng() + >>> dfnum = 3 # between group deg of freedom + >>> dfden = 20 # within groups degrees of freedom + >>> nonc = 3.0 + >>> nc_vals = rng.noncentral_f(dfnum, dfden, nonc, 1000000) + >>> NF = np.histogram(nc_vals, bins=50, density=True) + >>> c_vals = rng.f(dfnum, dfden, 1000000) + >>> F = np.histogram(c_vals, bins=50, density=True) + >>> import matplotlib.pyplot as plt + >>> plt.plot(F[1][1:], F[0]) + >>> plt.plot(NF[1][1:], NF[0]) + >>> plt.show() + + """ + return cont(&random_noncentral_f, &self._bitgen, size, self.lock, 3, + dfnum, 'dfnum', CONS_POSITIVE, + dfden, 'dfden', CONS_POSITIVE, + nonc, 'nonc', CONS_NON_NEGATIVE, None) + + def chisquare(self, df, size=None): + """ + chisquare(df, size=None) + + Draw samples from a chi-square distribution. + + When `df` independent random variables, each with standard normal + distributions (mean 0, variance 1), are squared and summed, the + resulting distribution is chi-square (see Notes). This distribution + is often used in hypothesis testing. + + Parameters + ---------- + df : float or array_like of floats + Number of degrees of freedom, must be > 0. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``df`` is a scalar. Otherwise, + ``np.array(df).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized chi-square distribution. + + Raises + ------ + ValueError + When `df` <= 0 or when an inappropriate `size` (e.g. ``size=-1``) + is given. + + Notes + ----- + The variable obtained by summing the squares of `df` independent, + standard normally distributed random variables: + + .. math:: Q = \\sum_{i=0}^{\\mathtt{df}} X^2_i + + is chi-square distributed, denoted + + .. math:: Q \\sim \\chi^2_k. + + The probability density function of the chi-squared distribution is + + .. math:: p(x) = \\frac{(1/2)^{k/2}}{\\Gamma(k/2)} + x^{k/2 - 1} e^{-x/2}, + + where :math:`\\Gamma` is the gamma function, + + .. math:: \\Gamma(x) = \\int_0^{-\\infty} t^{x - 1} e^{-t} dt. + + References + ---------- + .. [1] NIST "Engineering Statistics Handbook" + https://www.itl.nist.gov/div898/handbook/eda/section3/eda3666.htm + + Examples + -------- + >>> np.random.default_rng().chisquare(2,4) + array([ 1.89920014, 9.00867716, 3.13710533, 5.62318272]) # random + + """ + return cont(&random_chisquare, &self._bitgen, size, self.lock, 1, + df, 'df', CONS_POSITIVE, + 0.0, '', CONS_NONE, + 0.0, '', CONS_NONE, None) + + def noncentral_chisquare(self, df, nonc, size=None): + """ + noncentral_chisquare(df, nonc, size=None) + + Draw samples from a noncentral chi-square distribution. + + The noncentral :math:`\\chi^2` distribution is a generalization of + the :math:`\\chi^2` distribution. + + Parameters + ---------- + df : float or array_like of floats + Degrees of freedom, must be > 0. + + .. versionchanged:: 1.10.0 + Earlier NumPy versions required dfnum > 1. + nonc : float or array_like of floats + Non-centrality, must be non-negative. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``df`` and ``nonc`` are both scalars. + Otherwise, ``np.broadcast(df, nonc).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized noncentral chi-square distribution. + + Notes + ----- + The probability density function for the noncentral Chi-square + distribution is + + .. math:: P(x;df,nonc) = \\sum^{\\infty}_{i=0} + \\frac{e^{-nonc/2}(nonc/2)^{i}}{i!} + P_{Y_{df+2i}}(x), + + where :math:`Y_{q}` is the Chi-square with q degrees of freedom. + + References + ---------- + .. [1] Wikipedia, "Noncentral chi-squared distribution" + https://en.wikipedia.org/wiki/Noncentral_chi-squared_distribution + + Examples + -------- + Draw values from the distribution and plot the histogram + + >>> rng = np.random.default_rng() + >>> import matplotlib.pyplot as plt + >>> values = plt.hist(rng.noncentral_chisquare(3, 20, 100000), + ... bins=200, density=True) + >>> plt.show() + + Draw values from a noncentral chisquare with very small noncentrality, + and compare to a chisquare. + + >>> plt.figure() + >>> values = plt.hist(rng.noncentral_chisquare(3, .0000001, 100000), + ... bins=np.arange(0., 25, .1), density=True) + >>> values2 = plt.hist(rng.chisquare(3, 100000), + ... bins=np.arange(0., 25, .1), density=True) + >>> plt.plot(values[1][0:-1], values[0]-values2[0], 'ob') + >>> plt.show() + + Demonstrate how large values of non-centrality lead to a more symmetric + distribution. + + >>> plt.figure() + >>> values = plt.hist(rng.noncentral_chisquare(3, 20, 100000), + ... bins=200, density=True) + >>> plt.show() + + """ + return cont(&random_noncentral_chisquare, &self._bitgen, size, self.lock, 2, + df, 'df', CONS_POSITIVE, + nonc, 'nonc', CONS_NON_NEGATIVE, + 0.0, '', CONS_NONE, None) + + def standard_cauchy(self, size=None): + """ + standard_cauchy(size=None) + + Draw samples from a standard Cauchy distribution with mode = 0. + + Also known as the Lorentz distribution. + + Parameters + ---------- + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. Default is None, in which case a + single value is returned. + + Returns + ------- + samples : ndarray or scalar + The drawn samples. + + Notes + ----- + The probability density function for the full Cauchy distribution is + + .. math:: P(x; x_0, \\gamma) = \\frac{1}{\\pi \\gamma \\bigl[ 1+ + (\\frac{x-x_0}{\\gamma})^2 \\bigr] } + + and the Standard Cauchy distribution just sets :math:`x_0=0` and + :math:`\\gamma=1` + + The Cauchy distribution arises in the solution to the driven harmonic + oscillator problem, and also describes spectral line broadening. It + also describes the distribution of values at which a line tilted at + a random angle will cut the x axis. + + When studying hypothesis tests that assume normality, seeing how the + tests perform on data from a Cauchy distribution is a good indicator of + their sensitivity to a heavy-tailed distribution, since the Cauchy looks + very much like a Gaussian distribution, but with heavier tails. + + References + ---------- + .. [1] NIST/SEMATECH e-Handbook of Statistical Methods, "Cauchy + Distribution", + https://www.itl.nist.gov/div898/handbook/eda/section3/eda3663.htm + .. [2] Weisstein, Eric W. "Cauchy Distribution." From MathWorld--A + Wolfram Web Resource. + http://mathworld.wolfram.com/CauchyDistribution.html + .. [3] Wikipedia, "Cauchy distribution" + https://en.wikipedia.org/wiki/Cauchy_distribution + + Examples + -------- + Draw samples and plot the distribution: + + >>> import matplotlib.pyplot as plt + >>> s = np.random.default_rng().standard_cauchy(1000000) + >>> s = s[(s>-25) & (s<25)] # truncate distribution so it plots well + >>> plt.hist(s, bins=100) + >>> plt.show() + + """ + return cont(&random_standard_cauchy, &self._bitgen, size, self.lock, 0, + 0.0, '', CONS_NONE, 0.0, '', CONS_NONE, 0.0, '', CONS_NONE, None) + + def standard_t(self, df, size=None): + """ + standard_t(df, size=None) + + Draw samples from a standard Student's t distribution with `df` degrees + of freedom. + + A special case of the hyperbolic distribution. As `df` gets + large, the result resembles that of the standard normal + distribution (`standard_normal`). + + Parameters + ---------- + df : float or array_like of floats + Degrees of freedom, must be > 0. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``df`` is a scalar. Otherwise, + ``np.array(df).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized standard Student's t distribution. + + Notes + ----- + The probability density function for the t distribution is + + .. math:: P(x, df) = \\frac{\\Gamma(\\frac{df+1}{2})}{\\sqrt{\\pi df} + \\Gamma(\\frac{df}{2})}\\Bigl( 1+\\frac{x^2}{df} \\Bigr)^{-(df+1)/2} + + The t test is based on an assumption that the data come from a + Normal distribution. The t test provides a way to test whether + the sample mean (that is the mean calculated from the data) is + a good estimate of the true mean. + + The derivation of the t-distribution was first published in + 1908 by William Gosset while working for the Guinness Brewery + in Dublin. Due to proprietary issues, he had to publish under + a pseudonym, and so he used the name Student. + + References + ---------- + .. [1] Dalgaard, Peter, "Introductory Statistics With R", + Springer, 2002. + .. [2] Wikipedia, "Student's t-distribution" + https://en.wikipedia.org/wiki/Student's_t-distribution + + Examples + -------- + From Dalgaard page 83 [1]_, suppose the daily energy intake for 11 + women in kilojoules (kJ) is: + + >>> intake = np.array([5260., 5470, 5640, 6180, 6390, 6515, 6805, 7515, \\ + ... 7515, 8230, 8770]) + + Does their energy intake deviate systematically from the recommended + value of 7725 kJ? Our null hypothesis will be the absence of deviation, + and the alternate hypothesis will be the presence of an effect that could be + either positive or negative, hence making our test 2-tailed. + + Because we are estimating the mean and we have N=11 values in our sample, + we have N-1=10 degrees of freedom. We set our significance level to 95% and + compute the t statistic using the empirical mean and empirical standard + deviation of our intake. We use a ddof of 1 to base the computation of our + empirical standard deviation on an unbiased estimate of the variance (note: + the final estimate is not unbiased due to the concave nature of the square + root). + + >>> np.mean(intake) + 6753.636363636364 + >>> intake.std(ddof=1) + 1142.1232221373727 + >>> t = (np.mean(intake)-7725)/(intake.std(ddof=1)/np.sqrt(len(intake))) + >>> t + -2.8207540608310198 + + We draw 1000000 samples from Student's t distribution with the adequate + degrees of freedom. + + >>> import matplotlib.pyplot as plt + >>> s = np.random.default_rng().standard_t(10, size=1000000) + >>> h = plt.hist(s, bins=100, density=True) + + Does our t statistic land in one of the two critical regions found at + both tails of the distribution? + + >>> np.sum(np.abs(t) < np.abs(s)) / float(len(s)) + 0.018318 #random < 0.05, statistic is in critical region + + The probability value for this 2-tailed test is about 1.83%, which is + lower than the 5% pre-determined significance threshold. + + Therefore, the probability of observing values as extreme as our intake + conditionally on the null hypothesis being true is too low, and we reject + the null hypothesis of no deviation. + + """ + return cont(&random_standard_t, &self._bitgen, size, self.lock, 1, + df, 'df', CONS_POSITIVE, + 0, '', CONS_NONE, + 0, '', CONS_NONE, + None) + + def vonmises(self, mu, kappa, size=None): + """ + vonmises(mu, kappa, size=None) + + Draw samples from a von Mises distribution. + + Samples are drawn from a von Mises distribution with specified mode + (mu) and dispersion (kappa), on the interval [-pi, pi]. + + The von Mises distribution (also known as the circular normal + distribution) is a continuous probability distribution on the unit + circle. It may be thought of as the circular analogue of the normal + distribution. + + Parameters + ---------- + mu : float or array_like of floats + Mode ("center") of the distribution. + kappa : float or array_like of floats + Dispersion of the distribution, has to be >=0. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``mu`` and ``kappa`` are both scalars. + Otherwise, ``np.broadcast(mu, kappa).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized von Mises distribution. + + See Also + -------- + scipy.stats.vonmises : probability density function, distribution, or + cumulative density function, etc. + + Notes + ----- + The probability density for the von Mises distribution is + + .. math:: p(x) = \\frac{e^{\\kappa cos(x-\\mu)}}{2\\pi I_0(\\kappa)}, + + where :math:`\\mu` is the mode and :math:`\\kappa` the dispersion, + and :math:`I_0(\\kappa)` is the modified Bessel function of order 0. + + The von Mises is named for Richard Edler von Mises, who was born in + Austria-Hungary, in what is now the Ukraine. He fled to the United + States in 1939 and became a professor at Harvard. He worked in + probability theory, aerodynamics, fluid mechanics, and philosophy of + science. + + References + ---------- + .. [1] Abramowitz, M. and Stegun, I. A. (Eds.). "Handbook of + Mathematical Functions with Formulas, Graphs, and Mathematical + Tables, 9th printing," New York: Dover, 1972. + .. [2] von Mises, R., "Mathematical Theory of Probability + and Statistics", New York: Academic Press, 1964. + + Examples + -------- + Draw samples from the distribution: + + >>> mu, kappa = 0.0, 4.0 # mean and dispersion + >>> s = np.random.default_rng().vonmises(mu, kappa, 1000) + + Display the histogram of the samples, along with + the probability density function: + + >>> import matplotlib.pyplot as plt + >>> from scipy.special import i0 # doctest: +SKIP + >>> plt.hist(s, 50, density=True) + >>> x = np.linspace(-np.pi, np.pi, num=51) + >>> y = np.exp(kappa*np.cos(x-mu))/(2*np.pi*i0(kappa)) # doctest: +SKIP + >>> plt.plot(x, y, linewidth=2, color='r') # doctest: +SKIP + >>> plt.show() + + """ + return cont(&random_vonmises, &self._bitgen, size, self.lock, 2, + mu, 'mu', CONS_NONE, + kappa, 'kappa', CONS_NON_NEGATIVE, + 0.0, '', CONS_NONE, None) + + def pareto(self, a, size=None): + """ + pareto(a, size=None) + + Draw samples from a Pareto II or Lomax distribution with + specified shape. + + The Lomax or Pareto II distribution is a shifted Pareto + distribution. The classical Pareto distribution can be + obtained from the Lomax distribution by adding 1 and + multiplying by the scale parameter ``m`` (see Notes). The + smallest value of the Lomax distribution is zero while for the + classical Pareto distribution it is ``mu``, where the standard + Pareto distribution has location ``mu = 1``. Lomax can also + be considered as a simplified version of the Generalized + Pareto distribution (available in SciPy), with the scale set + to one and the location set to zero. + + The Pareto distribution must be greater than zero, and is + unbounded above. It is also known as the "80-20 rule". In + this distribution, 80 percent of the weights are in the lowest + 20 percent of the range, while the other 20 percent fill the + remaining 80 percent of the range. + + Parameters + ---------- + a : float or array_like of floats + Shape of the distribution. Must be positive. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``a`` is a scalar. Otherwise, + ``np.array(a).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized Pareto distribution. + + See Also + -------- + scipy.stats.lomax : probability density function, distribution or + cumulative density function, etc. + scipy.stats.genpareto : probability density function, distribution or + cumulative density function, etc. + + Notes + ----- + The probability density for the Pareto distribution is + + .. math:: p(x) = \\frac{am^a}{x^{a+1}} + + where :math:`a` is the shape and :math:`m` the scale. + + The Pareto distribution, named after the Italian economist + Vilfredo Pareto, is a power law probability distribution + useful in many real world problems. Outside the field of + economics it is generally referred to as the Bradford + distribution. Pareto developed the distribution to describe + the distribution of wealth in an economy. It has also found + use in insurance, web page access statistics, oil field sizes, + and many other problems, including the download frequency for + projects in Sourceforge [1]_. It is one of the so-called + "fat-tailed" distributions. + + + References + ---------- + .. [1] Francis Hunt and Paul Johnson, On the Pareto Distribution of + Sourceforge projects. + .. [2] Pareto, V. (1896). Course of Political Economy. Lausanne. + .. [3] Reiss, R.D., Thomas, M.(2001), Statistical Analysis of Extreme + Values, Birkhauser Verlag, Basel, pp 23-30. + .. [4] Wikipedia, "Pareto distribution", + https://en.wikipedia.org/wiki/Pareto_distribution + + Examples + -------- + Draw samples from the distribution: + + >>> a, m = 3., 2. # shape and mode + >>> s = (np.random.default_rng().pareto(a, 1000) + 1) * m + + Display the histogram of the samples, along with the probability + density function: + + >>> import matplotlib.pyplot as plt + >>> count, bins, _ = plt.hist(s, 100, density=True) + >>> fit = a*m**a / bins**(a+1) + >>> plt.plot(bins, max(count)*fit/max(fit), linewidth=2, color='r') + >>> plt.show() + + """ + return cont(&random_pareto, &self._bitgen, size, self.lock, 1, + a, 'a', CONS_POSITIVE, + 0.0, '', CONS_NONE, + 0.0, '', CONS_NONE, None) + + def weibull(self, a, size=None): + """ + weibull(a, size=None) + + Draw samples from a Weibull distribution. + + Draw samples from a 1-parameter Weibull distribution with the given + shape parameter `a`. + + .. math:: X = (-ln(U))^{1/a} + + Here, U is drawn from the uniform distribution over (0,1]. + + The more common 2-parameter Weibull, including a scale parameter + :math:`\\lambda` is just :math:`X = \\lambda(-ln(U))^{1/a}`. + + Parameters + ---------- + a : float or array_like of floats + Shape parameter of the distribution. Must be nonnegative. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``a`` is a scalar. Otherwise, + ``np.array(a).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized Weibull distribution. + + See Also + -------- + scipy.stats.weibull_max + scipy.stats.weibull_min + scipy.stats.genextreme + gumbel + + Notes + ----- + The Weibull (or Type III asymptotic extreme value distribution + for smallest values, SEV Type III, or Rosin-Rammler + distribution) is one of a class of Generalized Extreme Value + (GEV) distributions used in modeling extreme value problems. + This class includes the Gumbel and Frechet distributions. + + The probability density for the Weibull distribution is + + .. math:: p(x) = \\frac{a} + {\\lambda}(\\frac{x}{\\lambda})^{a-1}e^{-(x/\\lambda)^a}, + + where :math:`a` is the shape and :math:`\\lambda` the scale. + + The function has its peak (the mode) at + :math:`\\lambda(\\frac{a-1}{a})^{1/a}`. + + When ``a = 1``, the Weibull distribution reduces to the exponential + distribution. + + References + ---------- + .. [1] Waloddi Weibull, Royal Technical University, Stockholm, + 1939 "A Statistical Theory Of The Strength Of Materials", + Ingeniorsvetenskapsakademiens Handlingar Nr 151, 1939, + Generalstabens Litografiska Anstalts Forlag, Stockholm. + .. [2] Waloddi Weibull, "A Statistical Distribution Function of + Wide Applicability", Journal Of Applied Mechanics ASME Paper + 1951. + .. [3] Wikipedia, "Weibull distribution", + https://en.wikipedia.org/wiki/Weibull_distribution + + Examples + -------- + Draw samples from the distribution: + + >>> rng = np.random.default_rng() + >>> a = 5. # shape + >>> s = rng.weibull(a, 1000) + + Display the histogram of the samples, along with + the probability density function: + + >>> import matplotlib.pyplot as plt + >>> x = np.arange(1,100.)/50. + >>> def weib(x,n,a): + ... return (a / n) * (x / n)**(a - 1) * np.exp(-(x / n)**a) + + >>> count, bins, ignored = plt.hist(rng.weibull(5.,1000)) + >>> x = np.arange(1,100.)/50. + >>> scale = count.max()/weib(x, 1., 5.).max() + >>> plt.plot(x, weib(x, 1., 5.)*scale) + >>> plt.show() + + """ + return cont(&random_weibull, &self._bitgen, size, self.lock, 1, + a, 'a', CONS_NON_NEGATIVE, + 0.0, '', CONS_NONE, + 0.0, '', CONS_NONE, None) + + def power(self, a, size=None): + """ + power(a, size=None) + + Draws samples in [0, 1] from a power distribution with positive + exponent a - 1. + + Also known as the power function distribution. + + Parameters + ---------- + a : float or array_like of floats + Parameter of the distribution. Must be non-negative. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``a`` is a scalar. Otherwise, + ``np.array(a).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized power distribution. + + Raises + ------ + ValueError + If a <= 0. + + Notes + ----- + The probability density function is + + .. math:: P(x; a) = ax^{a-1}, 0 \\le x \\le 1, a>0. + + The power function distribution is just the inverse of the Pareto + distribution. It may also be seen as a special case of the Beta + distribution. + + It is used, for example, in modeling the over-reporting of insurance + claims. + + References + ---------- + .. [1] Christian Kleiber, Samuel Kotz, "Statistical size distributions + in economics and actuarial sciences", Wiley, 2003. + .. [2] Heckert, N. A. and Filliben, James J. "NIST Handbook 148: + Dataplot Reference Manual, Volume 2: Let Subcommands and Library + Functions", National Institute of Standards and Technology + Handbook Series, June 2003. + https://www.itl.nist.gov/div898/software/dataplot/refman2/auxillar/powpdf.pdf + + Examples + -------- + Draw samples from the distribution: + + >>> rng = np.random.default_rng() + >>> a = 5. # shape + >>> samples = 1000 + >>> s = rng.power(a, samples) + + Display the histogram of the samples, along with + the probability density function: + + >>> import matplotlib.pyplot as plt + >>> count, bins, ignored = plt.hist(s, bins=30) + >>> x = np.linspace(0, 1, 100) + >>> y = a*x**(a-1.) + >>> normed_y = samples*np.diff(bins)[0]*y + >>> plt.plot(x, normed_y) + >>> plt.show() + + Compare the power function distribution to the inverse of the Pareto. + + >>> from scipy import stats # doctest: +SKIP + >>> rvs = rng.power(5, 1000000) + >>> rvsp = rng.pareto(5, 1000000) + >>> xx = np.linspace(0,1,100) + >>> powpdf = stats.powerlaw.pdf(xx,5) # doctest: +SKIP + + >>> plt.figure() + >>> plt.hist(rvs, bins=50, density=True) + >>> plt.plot(xx,powpdf,'r-') # doctest: +SKIP + >>> plt.title('power(5)') + + >>> plt.figure() + >>> plt.hist(1./(1.+rvsp), bins=50, density=True) + >>> plt.plot(xx,powpdf,'r-') # doctest: +SKIP + >>> plt.title('inverse of 1 + Generator.pareto(5)') + + >>> plt.figure() + >>> plt.hist(1./(1.+rvsp), bins=50, density=True) + >>> plt.plot(xx,powpdf,'r-') # doctest: +SKIP + >>> plt.title('inverse of stats.pareto(5)') + + """ + return cont(&random_power, &self._bitgen, size, self.lock, 1, + a, 'a', CONS_POSITIVE, + 0.0, '', CONS_NONE, + 0.0, '', CONS_NONE, None) + + def laplace(self, loc=0.0, scale=1.0, size=None): + """ + laplace(loc=0.0, scale=1.0, size=None) + + Draw samples from the Laplace or double exponential distribution with + specified location (or mean) and scale (decay). + + The Laplace distribution is similar to the Gaussian/normal distribution, + but is sharper at the peak and has fatter tails. It represents the + difference between two independent, identically distributed exponential + random variables. + + Parameters + ---------- + loc : float or array_like of floats, optional + The position, :math:`\\mu`, of the distribution peak. Default is 0. + scale : float or array_like of floats, optional + :math:`\\lambda`, the exponential decay. Default is 1. Must be non- + negative. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``loc`` and ``scale`` are both scalars. + Otherwise, ``np.broadcast(loc, scale).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized Laplace distribution. + + Notes + ----- + It has the probability density function + + .. math:: f(x; \\mu, \\lambda) = \\frac{1}{2\\lambda} + \\exp\\left(-\\frac{|x - \\mu|}{\\lambda}\\right). + + The first law of Laplace, from 1774, states that the frequency + of an error can be expressed as an exponential function of the + absolute magnitude of the error, which leads to the Laplace + distribution. For many problems in economics and health + sciences, this distribution seems to model the data better + than the standard Gaussian distribution. + + References + ---------- + .. [1] Abramowitz, M. and Stegun, I. A. (Eds.). "Handbook of + Mathematical Functions with Formulas, Graphs, and Mathematical + Tables, 9th printing," New York: Dover, 1972. + .. [2] Kotz, Samuel, et. al. "The Laplace Distribution and + Generalizations, " Birkhauser, 2001. + .. [3] Weisstein, Eric W. "Laplace Distribution." + From MathWorld--A Wolfram Web Resource. + http://mathworld.wolfram.com/LaplaceDistribution.html + .. [4] Wikipedia, "Laplace distribution", + https://en.wikipedia.org/wiki/Laplace_distribution + + Examples + -------- + Draw samples from the distribution + + >>> loc, scale = 0., 1. + >>> s = np.random.default_rng().laplace(loc, scale, 1000) + + Display the histogram of the samples, along with + the probability density function: + + >>> import matplotlib.pyplot as plt + >>> count, bins, ignored = plt.hist(s, 30, density=True) + >>> x = np.arange(-8., 8., .01) + >>> pdf = np.exp(-abs(x-loc)/scale)/(2.*scale) + >>> plt.plot(x, pdf) + + Plot Gaussian for comparison: + + >>> g = (1/(scale * np.sqrt(2 * np.pi)) * + ... np.exp(-(x - loc)**2 / (2 * scale**2))) + >>> plt.plot(x,g) + + """ + return cont(&random_laplace, &self._bitgen, size, self.lock, 2, + loc, 'loc', CONS_NONE, + scale, 'scale', CONS_NON_NEGATIVE, + 0.0, '', CONS_NONE, None) + + def gumbel(self, loc=0.0, scale=1.0, size=None): + """ + gumbel(loc=0.0, scale=1.0, size=None) + + Draw samples from a Gumbel distribution. + + Draw samples from a Gumbel distribution with specified location and + scale. For more information on the Gumbel distribution, see + Notes and References below. + + Parameters + ---------- + loc : float or array_like of floats, optional + The location of the mode of the distribution. Default is 0. + scale : float or array_like of floats, optional + The scale parameter of the distribution. Default is 1. Must be non- + negative. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``loc`` and ``scale`` are both scalars. + Otherwise, ``np.broadcast(loc, scale).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized Gumbel distribution. + + See Also + -------- + scipy.stats.gumbel_l + scipy.stats.gumbel_r + scipy.stats.genextreme + weibull + + Notes + ----- + The Gumbel (or Smallest Extreme Value (SEV) or the Smallest Extreme + Value Type I) distribution is one of a class of Generalized Extreme + Value (GEV) distributions used in modeling extreme value problems. + The Gumbel is a special case of the Extreme Value Type I distribution + for maximums from distributions with "exponential-like" tails. + + The probability density for the Gumbel distribution is + + .. math:: p(x) = \\frac{e^{-(x - \\mu)/ \\beta}}{\\beta} e^{ -e^{-(x - \\mu)/ + \\beta}}, + + where :math:`\\mu` is the mode, a location parameter, and + :math:`\\beta` is the scale parameter. + + The Gumbel (named for German mathematician Emil Julius Gumbel) was used + very early in the hydrology literature, for modeling the occurrence of + flood events. It is also used for modeling maximum wind speed and + rainfall rates. It is a "fat-tailed" distribution - the probability of + an event in the tail of the distribution is larger than if one used a + Gaussian, hence the surprisingly frequent occurrence of 100-year + floods. Floods were initially modeled as a Gaussian process, which + underestimated the frequency of extreme events. + + It is one of a class of extreme value distributions, the Generalized + Extreme Value (GEV) distributions, which also includes the Weibull and + Frechet. + + The function has a mean of :math:`\\mu + 0.57721\\beta` and a variance + of :math:`\\frac{\\pi^2}{6}\\beta^2`. + + References + ---------- + .. [1] Gumbel, E. J., "Statistics of Extremes," + New York: Columbia University Press, 1958. + .. [2] Reiss, R.-D. and Thomas, M., "Statistical Analysis of Extreme + Values from Insurance, Finance, Hydrology and Other Fields," + Basel: Birkhauser Verlag, 2001. + + Examples + -------- + Draw samples from the distribution: + + >>> rng = np.random.default_rng() + >>> mu, beta = 0, 0.1 # location and scale + >>> s = rng.gumbel(mu, beta, 1000) + + Display the histogram of the samples, along with + the probability density function: + + >>> import matplotlib.pyplot as plt + >>> count, bins, ignored = plt.hist(s, 30, density=True) + >>> plt.plot(bins, (1/beta)*np.exp(-(bins - mu)/beta) + ... * np.exp( -np.exp( -(bins - mu) /beta) ), + ... linewidth=2, color='r') + >>> plt.show() + + Show how an extreme value distribution can arise from a Gaussian process + and compare to a Gaussian: + + >>> means = [] + >>> maxima = [] + >>> for i in range(0,1000) : + ... a = rng.normal(mu, beta, 1000) + ... means.append(a.mean()) + ... maxima.append(a.max()) + >>> count, bins, ignored = plt.hist(maxima, 30, density=True) + >>> beta = np.std(maxima) * np.sqrt(6) / np.pi + >>> mu = np.mean(maxima) - 0.57721*beta + >>> plt.plot(bins, (1/beta)*np.exp(-(bins - mu)/beta) + ... * np.exp(-np.exp(-(bins - mu)/beta)), + ... linewidth=2, color='r') + >>> plt.plot(bins, 1/(beta * np.sqrt(2 * np.pi)) + ... * np.exp(-(bins - mu)**2 / (2 * beta**2)), + ... linewidth=2, color='g') + >>> plt.show() + + """ + return cont(&random_gumbel, &self._bitgen, size, self.lock, 2, + loc, 'loc', CONS_NONE, + scale, 'scale', CONS_NON_NEGATIVE, + 0.0, '', CONS_NONE, None) + + def logistic(self, loc=0.0, scale=1.0, size=None): + """ + logistic(loc=0.0, scale=1.0, size=None) + + Draw samples from a logistic distribution. + + Samples are drawn from a logistic distribution with specified + parameters, loc (location or mean, also median), and scale (>0). + + Parameters + ---------- + loc : float or array_like of floats, optional + Parameter of the distribution. Default is 0. + scale : float or array_like of floats, optional + Parameter of the distribution. Must be non-negative. + Default is 1. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``loc`` and ``scale`` are both scalars. + Otherwise, ``np.broadcast(loc, scale).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized logistic distribution. + + See Also + -------- + scipy.stats.logistic : probability density function, distribution or + cumulative density function, etc. + + Notes + ----- + The probability density for the Logistic distribution is + + .. math:: P(x) = P(x) = \\frac{e^{-(x-\\mu)/s}}{s(1+e^{-(x-\\mu)/s})^2}, + + where :math:`\\mu` = location and :math:`s` = scale. + + The Logistic distribution is used in Extreme Value problems where it + can act as a mixture of Gumbel distributions, in Epidemiology, and by + the World Chess Federation (FIDE) where it is used in the Elo ranking + system, assuming the performance of each player is a logistically + distributed random variable. + + References + ---------- + .. [1] Reiss, R.-D. and Thomas M. (2001), "Statistical Analysis of + Extreme Values, from Insurance, Finance, Hydrology and Other + Fields," Birkhauser Verlag, Basel, pp 132-133. + .. [2] Weisstein, Eric W. "Logistic Distribution." From + MathWorld--A Wolfram Web Resource. + http://mathworld.wolfram.com/LogisticDistribution.html + .. [3] Wikipedia, "Logistic-distribution", + https://en.wikipedia.org/wiki/Logistic_distribution + + Examples + -------- + Draw samples from the distribution: + + >>> loc, scale = 10, 1 + >>> s = np.random.default_rng().logistic(loc, scale, 10000) + >>> import matplotlib.pyplot as plt + >>> count, bins, ignored = plt.hist(s, bins=50) + + # plot against distribution + + >>> def logist(x, loc, scale): + ... return np.exp((loc-x)/scale)/(scale*(1+np.exp((loc-x)/scale))**2) + >>> lgst_val = logist(bins, loc, scale) + >>> plt.plot(bins, lgst_val * count.max() / lgst_val.max()) + >>> plt.show() + + """ + return cont(&random_logistic, &self._bitgen, size, self.lock, 2, + loc, 'loc', CONS_NONE, + scale, 'scale', CONS_NON_NEGATIVE, + 0.0, '', CONS_NONE, None) + + def lognormal(self, mean=0.0, sigma=1.0, size=None): + """ + lognormal(mean=0.0, sigma=1.0, size=None) + + Draw samples from a log-normal distribution. + + Draw samples from a log-normal distribution with specified mean, + standard deviation, and array shape. Note that the mean and standard + deviation are not the values for the distribution itself, but of the + underlying normal distribution it is derived from. + + Parameters + ---------- + mean : float or array_like of floats, optional + Mean value of the underlying normal distribution. Default is 0. + sigma : float or array_like of floats, optional + Standard deviation of the underlying normal distribution. Must be + non-negative. Default is 1. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``mean`` and ``sigma`` are both scalars. + Otherwise, ``np.broadcast(mean, sigma).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized log-normal distribution. + + See Also + -------- + scipy.stats.lognorm : probability density function, distribution, + cumulative density function, etc. + + Notes + ----- + A variable `x` has a log-normal distribution if `log(x)` is normally + distributed. The probability density function for the log-normal + distribution is: + + .. math:: p(x) = \\frac{1}{\\sigma x \\sqrt{2\\pi}} + e^{(-\\frac{(ln(x)-\\mu)^2}{2\\sigma^2})} + + where :math:`\\mu` is the mean and :math:`\\sigma` is the standard + deviation of the normally distributed logarithm of the variable. + A log-normal distribution results if a random variable is the *product* + of a large number of independent, identically-distributed variables in + the same way that a normal distribution results if the variable is the + *sum* of a large number of independent, identically-distributed + variables. + + References + ---------- + .. [1] Limpert, E., Stahel, W. A., and Abbt, M., "Log-normal + Distributions across the Sciences: Keys and Clues," + BioScience, Vol. 51, No. 5, May, 2001. + https://stat.ethz.ch/~stahel/lognormal/bioscience.pdf + .. [2] Reiss, R.D. and Thomas, M., "Statistical Analysis of Extreme + Values," Basel: Birkhauser Verlag, 2001, pp. 31-32. + + Examples + -------- + Draw samples from the distribution: + + >>> rng = np.random.default_rng() + >>> mu, sigma = 3., 1. # mean and standard deviation + >>> s = rng.lognormal(mu, sigma, 1000) + + Display the histogram of the samples, along with + the probability density function: + + >>> import matplotlib.pyplot as plt + >>> count, bins, ignored = plt.hist(s, 100, density=True, align='mid') + + >>> x = np.linspace(min(bins), max(bins), 10000) + >>> pdf = (np.exp(-(np.log(x) - mu)**2 / (2 * sigma**2)) + ... / (x * sigma * np.sqrt(2 * np.pi))) + + >>> plt.plot(x, pdf, linewidth=2, color='r') + >>> plt.axis('tight') + >>> plt.show() + + Demonstrate that taking the products of random samples from a uniform + distribution can be fit well by a log-normal probability density + function. + + >>> # Generate a thousand samples: each is the product of 100 random + >>> # values, drawn from a normal distribution. + >>> rng = rng + >>> b = [] + >>> for i in range(1000): + ... a = 10. + rng.standard_normal(100) + ... b.append(np.product(a)) + + >>> b = np.array(b) / np.min(b) # scale values to be positive + >>> count, bins, ignored = plt.hist(b, 100, density=True, align='mid') + >>> sigma = np.std(np.log(b)) + >>> mu = np.mean(np.log(b)) + + >>> x = np.linspace(min(bins), max(bins), 10000) + >>> pdf = (np.exp(-(np.log(x) - mu)**2 / (2 * sigma**2)) + ... / (x * sigma * np.sqrt(2 * np.pi))) + + >>> plt.plot(x, pdf, color='r', linewidth=2) + >>> plt.show() + + """ + return cont(&random_lognormal, &self._bitgen, size, self.lock, 2, + mean, 'mean', CONS_NONE, + sigma, 'sigma', CONS_NON_NEGATIVE, + 0.0, '', CONS_NONE, None) + + def rayleigh(self, scale=1.0, size=None): + """ + rayleigh(scale=1.0, size=None) + + Draw samples from a Rayleigh distribution. + + The :math:`\\chi` and Weibull distributions are generalizations of the + Rayleigh. + + Parameters + ---------- + scale : float or array_like of floats, optional + Scale, also equals the mode. Must be non-negative. Default is 1. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``scale`` is a scalar. Otherwise, + ``np.array(scale).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized Rayleigh distribution. + + Notes + ----- + The probability density function for the Rayleigh distribution is + + .. math:: P(x;scale) = \\frac{x}{scale^2}e^{\\frac{-x^2}{2 \\cdotp scale^2}} + + The Rayleigh distribution would arise, for example, if the East + and North components of the wind velocity had identical zero-mean + Gaussian distributions. Then the wind speed would have a Rayleigh + distribution. + + References + ---------- + .. [1] Brighton Webs Ltd., "Rayleigh Distribution," + https://web.archive.org/web/20090514091424/http://brighton-webs.co.uk:80/distributions/rayleigh.asp + .. [2] Wikipedia, "Rayleigh distribution" + https://en.wikipedia.org/wiki/Rayleigh_distribution + + Examples + -------- + Draw values from the distribution and plot the histogram + + >>> from matplotlib.pyplot import hist + >>> rng = np.random.default_rng() + >>> values = hist(rng.rayleigh(3, 100000), bins=200, density=True) + + Wave heights tend to follow a Rayleigh distribution. If the mean wave + height is 1 meter, what fraction of waves are likely to be larger than 3 + meters? + + >>> meanvalue = 1 + >>> modevalue = np.sqrt(2 / np.pi) * meanvalue + >>> s = rng.rayleigh(modevalue, 1000000) + + The percentage of waves larger than 3 meters is: + + >>> 100.*sum(s>3)/1000000. + 0.087300000000000003 # random + + """ + return cont(&random_rayleigh, &self._bitgen, size, self.lock, 1, + scale, 'scale', CONS_NON_NEGATIVE, + 0.0, '', CONS_NONE, + 0.0, '', CONS_NONE, None) + + def wald(self, mean, scale, size=None): + """ + wald(mean, scale, size=None) + + Draw samples from a Wald, or inverse Gaussian, distribution. + + As the scale approaches infinity, the distribution becomes more like a + Gaussian. Some references claim that the Wald is an inverse Gaussian + with mean equal to 1, but this is by no means universal. + + The inverse Gaussian distribution was first studied in relationship to + Brownian motion. In 1956 M.C.K. Tweedie used the name inverse Gaussian + because there is an inverse relationship between the time to cover a + unit distance and distance covered in unit time. + + Parameters + ---------- + mean : float or array_like of floats + Distribution mean, must be > 0. + scale : float or array_like of floats + Scale parameter, must be > 0. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``mean`` and ``scale`` are both scalars. + Otherwise, ``np.broadcast(mean, scale).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized Wald distribution. + + Notes + ----- + The probability density function for the Wald distribution is + + .. math:: P(x;mean,scale) = \\sqrt{\\frac{scale}{2\\pi x^3}}e^ + \\frac{-scale(x-mean)^2}{2\\cdotp mean^2x} + + As noted above the inverse Gaussian distribution first arise + from attempts to model Brownian motion. It is also a + competitor to the Weibull for use in reliability modeling and + modeling stock returns and interest rate processes. + + References + ---------- + .. [1] Brighton Webs Ltd., Wald Distribution, + https://web.archive.org/web/20090423014010/http://www.brighton-webs.co.uk:80/distributions/wald.asp + .. [2] Chhikara, Raj S., and Folks, J. Leroy, "The Inverse Gaussian + Distribution: Theory : Methodology, and Applications", CRC Press, + 1988. + .. [3] Wikipedia, "Inverse Gaussian distribution" + https://en.wikipedia.org/wiki/Inverse_Gaussian_distribution + + Examples + -------- + Draw values from the distribution and plot the histogram: + + >>> import matplotlib.pyplot as plt + >>> h = plt.hist(np.random.default_rng().wald(3, 2, 100000), bins=200, density=True) + >>> plt.show() + + """ + return cont(&random_wald, &self._bitgen, size, self.lock, 2, + mean, 'mean', CONS_POSITIVE, + scale, 'scale', CONS_POSITIVE, + 0.0, '', CONS_NONE, None) + + def triangular(self, left, mode, right, size=None): + """ + triangular(left, mode, right, size=None) + + Draw samples from the triangular distribution over the + interval ``[left, right]``. + + The triangular distribution is a continuous probability + distribution with lower limit left, peak at mode, and upper + limit right. Unlike the other distributions, these parameters + directly define the shape of the pdf. + + Parameters + ---------- + left : float or array_like of floats + Lower limit. + mode : float or array_like of floats + The value where the peak of the distribution occurs. + The value must fulfill the condition ``left <= mode <= right``. + right : float or array_like of floats + Upper limit, must be larger than `left`. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``left``, ``mode``, and ``right`` + are all scalars. Otherwise, ``np.broadcast(left, mode, right).size`` + samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized triangular distribution. + + Notes + ----- + The probability density function for the triangular distribution is + + .. math:: P(x;l, m, r) = \\begin{cases} + \\frac{2(x-l)}{(r-l)(m-l)}& \\text{for $l \\leq x \\leq m$},\\\\ + \\frac{2(r-x)}{(r-l)(r-m)}& \\text{for $m \\leq x \\leq r$},\\\\ + 0& \\text{otherwise}. + \\end{cases} + + The triangular distribution is often used in ill-defined + problems where the underlying distribution is not known, but + some knowledge of the limits and mode exists. Often it is used + in simulations. + + References + ---------- + .. [1] Wikipedia, "Triangular distribution" + https://en.wikipedia.org/wiki/Triangular_distribution + + Examples + -------- + Draw values from the distribution and plot the histogram: + + >>> import matplotlib.pyplot as plt + >>> h = plt.hist(np.random.default_rng().triangular(-3, 0, 8, 100000), bins=200, + ... density=True) + >>> plt.show() + + """ + cdef bint is_scalar = True + cdef double fleft, fmode, fright + cdef np.ndarray oleft, omode, oright + + oleft = <np.ndarray>np.PyArray_FROM_OTF(left, np.NPY_DOUBLE, np.NPY_ALIGNED) + omode = <np.ndarray>np.PyArray_FROM_OTF(mode, np.NPY_DOUBLE, np.NPY_ALIGNED) + oright = <np.ndarray>np.PyArray_FROM_OTF(right, np.NPY_DOUBLE, np.NPY_ALIGNED) + + if np.PyArray_NDIM(oleft) == np.PyArray_NDIM(omode) == np.PyArray_NDIM(oright) == 0: + fleft = PyFloat_AsDouble(left) + fright = PyFloat_AsDouble(right) + fmode = PyFloat_AsDouble(mode) + + if fleft > fmode: + raise ValueError("left > mode") + if fmode > fright: + raise ValueError("mode > right") + if fleft == fright: + raise ValueError("left == right") + return cont(&random_triangular, &self._bitgen, size, self.lock, 3, + fleft, '', CONS_NONE, + fmode, '', CONS_NONE, + fright, '', CONS_NONE, None) + + if np.any(np.greater(oleft, omode)): + raise ValueError("left > mode") + if np.any(np.greater(omode, oright)): + raise ValueError("mode > right") + if np.any(np.equal(oleft, oright)): + raise ValueError("left == right") + + return cont_broadcast_3(&random_triangular, &self._bitgen, size, self.lock, + oleft, '', CONS_NONE, + omode, '', CONS_NONE, + oright, '', CONS_NONE) + + # Complicated, discrete distributions: + def binomial(self, n, p, size=None): + """ + binomial(n, p, size=None) + + Draw samples from a binomial distribution. + + Samples are drawn from a binomial distribution with specified + parameters, n trials and p probability of success where + n an integer >= 0 and p is in the interval [0,1]. (n may be + input as a float, but it is truncated to an integer in use) + + Parameters + ---------- + n : int or array_like of ints + Parameter of the distribution, >= 0. Floats are also accepted, + but they will be truncated to integers. + p : float or array_like of floats + Parameter of the distribution, >= 0 and <=1. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``n`` and ``p`` are both scalars. + Otherwise, ``np.broadcast(n, p).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized binomial distribution, where + each sample is equal to the number of successes over the n trials. + + See Also + -------- + scipy.stats.binom : probability density function, distribution or + cumulative density function, etc. + + Notes + ----- + The probability density for the binomial distribution is + + .. math:: P(N) = \\binom{n}{N}p^N(1-p)^{n-N}, + + where :math:`n` is the number of trials, :math:`p` is the probability + of success, and :math:`N` is the number of successes. + + When estimating the standard error of a proportion in a population by + using a random sample, the normal distribution works well unless the + product p*n <=5, where p = population proportion estimate, and n = + number of samples, in which case the binomial distribution is used + instead. For example, a sample of 15 people shows 4 who are left + handed, and 11 who are right handed. Then p = 4/15 = 27%. 0.27*15 = 4, + so the binomial distribution should be used in this case. + + References + ---------- + .. [1] Dalgaard, Peter, "Introductory Statistics with R", + Springer-Verlag, 2002. + .. [2] Glantz, Stanton A. "Primer of Biostatistics.", McGraw-Hill, + Fifth Edition, 2002. + .. [3] Lentner, Marvin, "Elementary Applied Statistics", Bogden + and Quigley, 1972. + .. [4] Weisstein, Eric W. "Binomial Distribution." From MathWorld--A + Wolfram Web Resource. + http://mathworld.wolfram.com/BinomialDistribution.html + .. [5] Wikipedia, "Binomial distribution", + https://en.wikipedia.org/wiki/Binomial_distribution + + Examples + -------- + Draw samples from the distribution: + + >>> rng = np.random.default_rng() + >>> n, p = 10, .5 # number of trials, probability of each trial + >>> s = rng.binomial(n, p, 1000) + # result of flipping a coin 10 times, tested 1000 times. + + A real world example. A company drills 9 wild-cat oil exploration + wells, each with an estimated probability of success of 0.1. All nine + wells fail. What is the probability of that happening? + + Let's do 20,000 trials of the model, and count the number that + generate zero positive results. + + >>> sum(rng.binomial(9, 0.1, 20000) == 0)/20000. + # answer = 0.38885, or 39%. + + """ + + # Uses a custom implementation since self._binomial is required + cdef double _dp = 0 + cdef int64_t _in = 0 + cdef bint is_scalar = True + cdef np.npy_intp i, cnt + cdef np.ndarray randoms + cdef np.int64_t *randoms_data + cdef np.broadcast it + + p_arr = <np.ndarray>np.PyArray_FROM_OTF(p, np.NPY_DOUBLE, np.NPY_ALIGNED) + is_scalar = is_scalar and np.PyArray_NDIM(p_arr) == 0 + n_arr = <np.ndarray>np.PyArray_FROM_OTF(n, np.NPY_INT64, np.NPY_ALIGNED) + is_scalar = is_scalar and np.PyArray_NDIM(n_arr) == 0 + + if not is_scalar: + check_array_constraint(p_arr, 'p', CONS_BOUNDED_0_1) + check_array_constraint(n_arr, 'n', CONS_NON_NEGATIVE) + if size is not None: + randoms = <np.ndarray>np.empty(size, np.int64) + else: + it = np.PyArray_MultiIterNew2(p_arr, n_arr) + randoms = <np.ndarray>np.empty(it.shape, np.int64) + + cnt = np.PyArray_SIZE(randoms) + + it = np.PyArray_MultiIterNew3(randoms, p_arr, n_arr) + validate_output_shape(it.shape, randoms) + with self.lock, nogil: + for i in range(cnt): + _dp = (<double*>np.PyArray_MultiIter_DATA(it, 1))[0] + _in = (<int64_t*>np.PyArray_MultiIter_DATA(it, 2))[0] + (<int64_t*>np.PyArray_MultiIter_DATA(it, 0))[0] = random_binomial(&self._bitgen, _dp, _in, &self._binomial) + + np.PyArray_MultiIter_NEXT(it) + + return randoms + + _dp = PyFloat_AsDouble(p) + _in = <int64_t>n + check_constraint(_dp, 'p', CONS_BOUNDED_0_1) + check_constraint(<double>_in, 'n', CONS_NON_NEGATIVE) + + if size is None: + with self.lock: + return random_binomial(&self._bitgen, _dp, _in, &self._binomial) + + randoms = <np.ndarray>np.empty(size, np.int64) + cnt = np.PyArray_SIZE(randoms) + randoms_data = <np.int64_t *>np.PyArray_DATA(randoms) + + with self.lock, nogil: + for i in range(cnt): + randoms_data[i] = random_binomial(&self._bitgen, _dp, _in, + &self._binomial) + + return randoms + + def negative_binomial(self, n, p, size=None): + """ + negative_binomial(n, p, size=None) + + Draw samples from a negative binomial distribution. + + Samples are drawn from a negative binomial distribution with specified + parameters, `n` successes and `p` probability of success where `n` + is > 0 and `p` is in the interval (0, 1]. + + Parameters + ---------- + n : float or array_like of floats + Parameter of the distribution, > 0. + p : float or array_like of floats + Parameter of the distribution. Must satisfy 0 < p <= 1. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``n`` and ``p`` are both scalars. + Otherwise, ``np.broadcast(n, p).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized negative binomial distribution, + where each sample is equal to N, the number of failures that + occurred before a total of n successes was reached. + + Notes + ----- + The probability mass function of the negative binomial distribution is + + .. math:: P(N;n,p) = \\frac{\\Gamma(N+n)}{N!\\Gamma(n)}p^{n}(1-p)^{N}, + + where :math:`n` is the number of successes, :math:`p` is the + probability of success, :math:`N+n` is the number of trials, and + :math:`\\Gamma` is the gamma function. When :math:`n` is an integer, + :math:`\\frac{\\Gamma(N+n)}{N!\\Gamma(n)} = \\binom{N+n-1}{N}`, which is + the more common form of this term in the the pmf. The negative + binomial distribution gives the probability of N failures given n + successes, with a success on the last trial. + + If one throws a die repeatedly until the third time a "1" appears, + then the probability distribution of the number of non-"1"s that + appear before the third "1" is a negative binomial distribution. + + References + ---------- + .. [1] Weisstein, Eric W. "Negative Binomial Distribution." From + MathWorld--A Wolfram Web Resource. + http://mathworld.wolfram.com/NegativeBinomialDistribution.html + .. [2] Wikipedia, "Negative binomial distribution", + https://en.wikipedia.org/wiki/Negative_binomial_distribution + + Examples + -------- + Draw samples from the distribution: + + A real world example. A company drills wild-cat oil + exploration wells, each with an estimated probability of + success of 0.1. What is the probability of having one success + for each successive well, that is what is the probability of a + single success after drilling 5 wells, after 6 wells, etc.? + + >>> s = np.random.default_rng().negative_binomial(1, 0.1, 100000) + >>> for i in range(1, 11): # doctest: +SKIP + ... probability = sum(s<i) / 100000. + ... print(i, "wells drilled, probability of one success =", probability) + + """ + return disc(&random_negative_binomial, &self._bitgen, size, self.lock, 2, 0, + n, 'n', CONS_POSITIVE_NOT_NAN, + p, 'p', CONS_BOUNDED_GT_0_1, + 0.0, '', CONS_NONE) + + def poisson(self, lam=1.0, size=None): + """ + poisson(lam=1.0, size=None) + + Draw samples from a Poisson distribution. + + The Poisson distribution is the limit of the binomial distribution + for large N. + + Parameters + ---------- + lam : float or array_like of floats + Expected number of events occurring in a fixed-time interval, + must be >= 0. A sequence must be broadcastable over the requested + size. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``lam`` is a scalar. Otherwise, + ``np.array(lam).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized Poisson distribution. + + Notes + ----- + The Poisson distribution + + .. math:: f(k; \\lambda)=\\frac{\\lambda^k e^{-\\lambda}}{k!} + + For events with an expected separation :math:`\\lambda` the Poisson + distribution :math:`f(k; \\lambda)` describes the probability of + :math:`k` events occurring within the observed + interval :math:`\\lambda`. + + Because the output is limited to the range of the C int64 type, a + ValueError is raised when `lam` is within 10 sigma of the maximum + representable value. + + References + ---------- + .. [1] Weisstein, Eric W. "Poisson Distribution." + From MathWorld--A Wolfram Web Resource. + http://mathworld.wolfram.com/PoissonDistribution.html + .. [2] Wikipedia, "Poisson distribution", + https://en.wikipedia.org/wiki/Poisson_distribution + + Examples + -------- + Draw samples from the distribution: + + >>> import numpy as np + >>> rng = np.random.default_rng() + >>> s = rng.poisson(5, 10000) + + Display histogram of the sample: + + >>> import matplotlib.pyplot as plt + >>> count, bins, ignored = plt.hist(s, 14, density=True) + >>> plt.show() + + Draw each 100 values for lambda 100 and 500: + + >>> s = rng.poisson(lam=(100., 500.), size=(100, 2)) + + """ + return disc(&random_poisson, &self._bitgen, size, self.lock, 1, 0, + lam, 'lam', CONS_POISSON, + 0.0, '', CONS_NONE, + 0.0, '', CONS_NONE) + + def zipf(self, a, size=None): + """ + zipf(a, size=None) + + Draw samples from a Zipf distribution. + + Samples are drawn from a Zipf distribution with specified parameter + `a` > 1. + + The Zipf distribution (also known as the zeta distribution) is a + discrete probability distribution that satisfies Zipf's law: the + frequency of an item is inversely proportional to its rank in a + frequency table. + + Parameters + ---------- + a : float or array_like of floats + Distribution parameter. Must be greater than 1. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``a`` is a scalar. Otherwise, + ``np.array(a).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized Zipf distribution. + + See Also + -------- + scipy.stats.zipf : probability density function, distribution, or + cumulative density function, etc. + + Notes + ----- + The probability density for the Zipf distribution is + + .. math:: p(k) = \\frac{k^{-a}}{\\zeta(a)}, + + for integers :math:`k \geq 1`, where :math:`\\zeta` is the Riemann Zeta + function. + + It is named for the American linguist George Kingsley Zipf, who noted + that the frequency of any word in a sample of a language is inversely + proportional to its rank in the frequency table. + + References + ---------- + .. [1] Zipf, G. K., "Selected Studies of the Principle of Relative + Frequency in Language," Cambridge, MA: Harvard Univ. Press, + 1932. + + Examples + -------- + Draw samples from the distribution: + + >>> a = 4.0 + >>> n = 20000 + >>> s = np.random.default_rng().zipf(a, size=n) + + Display the histogram of the samples, along with + the expected histogram based on the probability + density function: + + >>> import matplotlib.pyplot as plt + >>> from scipy.special import zeta # doctest: +SKIP + + `bincount` provides a fast histogram for small integers. + + >>> count = np.bincount(s) + >>> k = np.arange(1, s.max() + 1) + + >>> plt.bar(k, count[1:], alpha=0.5, label='sample count') + >>> plt.plot(k, n*(k**-a)/zeta(a), 'k.-', alpha=0.5, + ... label='expected count') # doctest: +SKIP + >>> plt.semilogy() + >>> plt.grid(alpha=0.4) + >>> plt.legend() + >>> plt.title(f'Zipf sample, a={a}, size={n}') + >>> plt.show() + + """ + return disc(&random_zipf, &self._bitgen, size, self.lock, 1, 0, + a, 'a', CONS_GT_1, + 0.0, '', CONS_NONE, + 0.0, '', CONS_NONE) + + def geometric(self, p, size=None): + """ + geometric(p, size=None) + + Draw samples from the geometric distribution. + + Bernoulli trials are experiments with one of two outcomes: + success or failure (an example of such an experiment is flipping + a coin). The geometric distribution models the number of trials + that must be run in order to achieve success. It is therefore + supported on the positive integers, ``k = 1, 2, ...``. + + The probability mass function of the geometric distribution is + + .. math:: f(k) = (1 - p)^{k - 1} p + + where `p` is the probability of success of an individual trial. + + Parameters + ---------- + p : float or array_like of floats + The probability of success of an individual trial. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``p`` is a scalar. Otherwise, + ``np.array(p).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized geometric distribution. + + Examples + -------- + Draw ten thousand values from the geometric distribution, + with the probability of an individual success equal to 0.35: + + >>> z = np.random.default_rng().geometric(p=0.35, size=10000) + + How many trials succeeded after a single run? + + >>> (z == 1).sum() / 10000. + 0.34889999999999999 # random + + """ + return disc(&random_geometric, &self._bitgen, size, self.lock, 1, 0, + p, 'p', CONS_BOUNDED_GT_0_1, + 0.0, '', CONS_NONE, + 0.0, '', CONS_NONE) + + def hypergeometric(self, ngood, nbad, nsample, size=None): + """ + hypergeometric(ngood, nbad, nsample, size=None) + + Draw samples from a Hypergeometric distribution. + + Samples are drawn from a hypergeometric distribution with specified + parameters, `ngood` (ways to make a good selection), `nbad` (ways to make + a bad selection), and `nsample` (number of items sampled, which is less + than or equal to the sum ``ngood + nbad``). + + Parameters + ---------- + ngood : int or array_like of ints + Number of ways to make a good selection. Must be nonnegative and + less than 10**9. + nbad : int or array_like of ints + Number of ways to make a bad selection. Must be nonnegative and + less than 10**9. + nsample : int or array_like of ints + Number of items sampled. Must be nonnegative and less than + ``ngood + nbad``. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if `ngood`, `nbad`, and `nsample` + are all scalars. Otherwise, ``np.broadcast(ngood, nbad, nsample).size`` + samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized hypergeometric distribution. Each + sample is the number of good items within a randomly selected subset of + size `nsample` taken from a set of `ngood` good items and `nbad` bad items. + + See Also + -------- + multivariate_hypergeometric : Draw samples from the multivariate + hypergeometric distribution. + scipy.stats.hypergeom : probability density function, distribution or + cumulative density function, etc. + + Notes + ----- + The probability density for the Hypergeometric distribution is + + .. math:: P(x) = \\frac{\\binom{g}{x}\\binom{b}{n-x}}{\\binom{g+b}{n}}, + + where :math:`0 \\le x \\le n` and :math:`n-b \\le x \\le g` + + for P(x) the probability of ``x`` good results in the drawn sample, + g = `ngood`, b = `nbad`, and n = `nsample`. + + Consider an urn with black and white marbles in it, `ngood` of them + are black and `nbad` are white. If you draw `nsample` balls without + replacement, then the hypergeometric distribution describes the + distribution of black balls in the drawn sample. + + Note that this distribution is very similar to the binomial + distribution, except that in this case, samples are drawn without + replacement, whereas in the Binomial case samples are drawn with + replacement (or the sample space is infinite). As the sample space + becomes large, this distribution approaches the binomial. + + The arguments `ngood` and `nbad` each must be less than `10**9`. For + extremely large arguments, the algorithm that is used to compute the + samples [4]_ breaks down because of loss of precision in floating point + calculations. For such large values, if `nsample` is not also large, + the distribution can be approximated with the binomial distribution, + `binomial(n=nsample, p=ngood/(ngood + nbad))`. + + References + ---------- + .. [1] Lentner, Marvin, "Elementary Applied Statistics", Bogden + and Quigley, 1972. + .. [2] Weisstein, Eric W. "Hypergeometric Distribution." From + MathWorld--A Wolfram Web Resource. + http://mathworld.wolfram.com/HypergeometricDistribution.html + .. [3] Wikipedia, "Hypergeometric distribution", + https://en.wikipedia.org/wiki/Hypergeometric_distribution + .. [4] Stadlober, Ernst, "The ratio of uniforms approach for generating + discrete random variates", Journal of Computational and Applied + Mathematics, 31, pp. 181-189 (1990). + + Examples + -------- + Draw samples from the distribution: + + >>> rng = np.random.default_rng() + >>> ngood, nbad, nsamp = 100, 2, 10 + # number of good, number of bad, and number of samples + >>> s = rng.hypergeometric(ngood, nbad, nsamp, 1000) + >>> from matplotlib.pyplot import hist + >>> hist(s) + # note that it is very unlikely to grab both bad items + + Suppose you have an urn with 15 white and 15 black marbles. + If you pull 15 marbles at random, how likely is it that + 12 or more of them are one color? + + >>> s = rng.hypergeometric(15, 15, 15, 100000) + >>> sum(s>=12)/100000. + sum(s<=3)/100000. + # answer = 0.003 ... pretty unlikely! + + """ + DEF HYPERGEOM_MAX = 10**9 + cdef bint is_scalar = True + cdef np.ndarray ongood, onbad, onsample + cdef int64_t lngood, lnbad, lnsample + + ongood = <np.ndarray>np.PyArray_FROM_OTF(ngood, np.NPY_INT64, np.NPY_ALIGNED) + onbad = <np.ndarray>np.PyArray_FROM_OTF(nbad, np.NPY_INT64, np.NPY_ALIGNED) + onsample = <np.ndarray>np.PyArray_FROM_OTF(nsample, np.NPY_INT64, np.NPY_ALIGNED) + + if np.PyArray_NDIM(ongood) == np.PyArray_NDIM(onbad) == np.PyArray_NDIM(onsample) == 0: + + lngood = <int64_t>ngood + lnbad = <int64_t>nbad + lnsample = <int64_t>nsample + + if lngood >= HYPERGEOM_MAX or lnbad >= HYPERGEOM_MAX: + raise ValueError("both ngood and nbad must be less than %d" % + HYPERGEOM_MAX) + if lngood + lnbad < lnsample: + raise ValueError("ngood + nbad < nsample") + return disc(&random_hypergeometric, &self._bitgen, size, self.lock, 0, 3, + lngood, 'ngood', CONS_NON_NEGATIVE, + lnbad, 'nbad', CONS_NON_NEGATIVE, + lnsample, 'nsample', CONS_NON_NEGATIVE) + + if np.any(ongood >= HYPERGEOM_MAX) or np.any(onbad >= HYPERGEOM_MAX): + raise ValueError("both ngood and nbad must be less than %d" % + HYPERGEOM_MAX) + + if np.any(np.less(np.add(ongood, onbad), onsample)): + raise ValueError("ngood + nbad < nsample") + + return discrete_broadcast_iii(&random_hypergeometric, &self._bitgen, size, self.lock, + ongood, 'ngood', CONS_NON_NEGATIVE, + onbad, 'nbad', CONS_NON_NEGATIVE, + onsample, 'nsample', CONS_NON_NEGATIVE) + + def logseries(self, p, size=None): + """ + logseries(p, size=None) + + Draw samples from a logarithmic series distribution. + + Samples are drawn from a log series distribution with specified + shape parameter, 0 < ``p`` < 1. + + Parameters + ---------- + p : float or array_like of floats + Shape parameter for the distribution. Must be in the range (0, 1). + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``p`` is a scalar. Otherwise, + ``np.array(p).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized logarithmic series distribution. + + See Also + -------- + scipy.stats.logser : probability density function, distribution or + cumulative density function, etc. + + Notes + ----- + The probability mass function for the Log Series distribution is + + .. math:: P(k) = \\frac{-p^k}{k \\ln(1-p)}, + + where p = probability. + + The log series distribution is frequently used to represent species + richness and occurrence, first proposed by Fisher, Corbet, and + Williams in 1943 [2]. It may also be used to model the numbers of + occupants seen in cars [3]. + + References + ---------- + .. [1] Buzas, Martin A.; Culver, Stephen J., Understanding regional + species diversity through the log series distribution of + occurrences: BIODIVERSITY RESEARCH Diversity & Distributions, + Volume 5, Number 5, September 1999 , pp. 187-195(9). + .. [2] Fisher, R.A,, A.S. Corbet, and C.B. Williams. 1943. The + relation between the number of species and the number of + individuals in a random sample of an animal population. + Journal of Animal Ecology, 12:42-58. + .. [3] D. J. Hand, F. Daly, D. Lunn, E. Ostrowski, A Handbook of Small + Data Sets, CRC Press, 1994. + .. [4] Wikipedia, "Logarithmic distribution", + https://en.wikipedia.org/wiki/Logarithmic_distribution + + Examples + -------- + Draw samples from the distribution: + + >>> a = .6 + >>> s = np.random.default_rng().logseries(a, 10000) + >>> import matplotlib.pyplot as plt + >>> count, bins, ignored = plt.hist(s) + + # plot against distribution + + >>> def logseries(k, p): + ... return -p**k/(k*np.log(1-p)) + >>> plt.plot(bins, logseries(bins, a) * count.max()/ + ... logseries(bins, a).max(), 'r') + >>> plt.show() + + """ + return disc(&random_logseries, &self._bitgen, size, self.lock, 1, 0, + p, 'p', CONS_BOUNDED_0_1, + 0.0, '', CONS_NONE, + 0.0, '', CONS_NONE) + + # Multivariate distributions: + def multivariate_normal(self, mean, cov, size=None, check_valid='warn', + tol=1e-8, *, method='svd'): + """ + multivariate_normal(mean, cov, size=None, check_valid='warn', + tol=1e-8, *, method='svd') + + Draw random samples from a multivariate normal distribution. + + The multivariate normal, multinormal or Gaussian distribution is a + generalization of the one-dimensional normal distribution to higher + dimensions. Such a distribution is specified by its mean and + covariance matrix. These parameters are analogous to the mean + (average or "center") and variance (standard deviation, or "width," + squared) of the one-dimensional normal distribution. + + Parameters + ---------- + mean : 1-D array_like, of length N + Mean of the N-dimensional distribution. + cov : 2-D array_like, of shape (N, N) + Covariance matrix of the distribution. It must be symmetric and + positive-semidefinite for proper sampling. + size : int or tuple of ints, optional + Given a shape of, for example, ``(m,n,k)``, ``m*n*k`` samples are + generated, and packed in an `m`-by-`n`-by-`k` arrangement. Because + each sample is `N`-dimensional, the output shape is ``(m,n,k,N)``. + If no shape is specified, a single (`N`-D) sample is returned. + check_valid : { 'warn', 'raise', 'ignore' }, optional + Behavior when the covariance matrix is not positive semidefinite. + tol : float, optional + Tolerance when checking the singular values in covariance matrix. + cov is cast to double before the check. + method : { 'svd', 'eigh', 'cholesky'}, optional + The cov input is used to compute a factor matrix A such that + ``A @ A.T = cov``. This argument is used to select the method + used to compute the factor matrix A. The default method 'svd' is + the slowest, while 'cholesky' is the fastest but less robust than + the slowest method. The method `eigh` uses eigen decomposition to + compute A and is faster than svd but slower than cholesky. + + .. versionadded:: 1.18.0 + + Returns + ------- + out : ndarray + The drawn samples, of shape *size*, if that was provided. If not, + the shape is ``(N,)``. + + In other words, each entry ``out[i,j,...,:]`` is an N-dimensional + value drawn from the distribution. + + Notes + ----- + The mean is a coordinate in N-dimensional space, which represents the + location where samples are most likely to be generated. This is + analogous to the peak of the bell curve for the one-dimensional or + univariate normal distribution. + + Covariance indicates the level to which two variables vary together. + From the multivariate normal distribution, we draw N-dimensional + samples, :math:`X = [x_1, x_2, ... x_N]`. The covariance matrix + element :math:`C_{ij}` is the covariance of :math:`x_i` and :math:`x_j`. + The element :math:`C_{ii}` is the variance of :math:`x_i` (i.e. its + "spread"). + + Instead of specifying the full covariance matrix, popular + approximations include: + + - Spherical covariance (`cov` is a multiple of the identity matrix) + - Diagonal covariance (`cov` has non-negative elements, and only on + the diagonal) + + This geometrical property can be seen in two dimensions by plotting + generated data-points: + + >>> mean = [0, 0] + >>> cov = [[1, 0], [0, 100]] # diagonal covariance + + Diagonal covariance means that points are oriented along x or y-axis: + + >>> import matplotlib.pyplot as plt + >>> x, y = np.random.default_rng().multivariate_normal(mean, cov, 5000).T + >>> plt.plot(x, y, 'x') + >>> plt.axis('equal') + >>> plt.show() + + Note that the covariance matrix must be positive semidefinite (a.k.a. + nonnegative-definite). Otherwise, the behavior of this method is + undefined and backwards compatibility is not guaranteed. + + References + ---------- + .. [1] Papoulis, A., "Probability, Random Variables, and Stochastic + Processes," 3rd ed., New York: McGraw-Hill, 1991. + .. [2] Duda, R. O., Hart, P. E., and Stork, D. G., "Pattern + Classification," 2nd ed., New York: Wiley, 2001. + + Examples + -------- + >>> mean = (1, 2) + >>> cov = [[1, 0], [0, 1]] + >>> rng = np.random.default_rng() + >>> x = rng.multivariate_normal(mean, cov, (3, 3)) + >>> x.shape + (3, 3, 2) + + We can use a different method other than the default to factorize cov: + + >>> y = rng.multivariate_normal(mean, cov, (3, 3), method='cholesky') + >>> y.shape + (3, 3, 2) + + The following is probably true, given that 0.6 is roughly twice the + standard deviation: + + >>> list((x[0,0,:] - mean) < 0.6) + [True, True] # random + + """ + if method not in {'eigh', 'svd', 'cholesky'}: + raise ValueError( + "method must be one of {'eigh', 'svd', 'cholesky'}") + + # Check preconditions on arguments + mean = np.array(mean) + cov = np.array(cov) + if size is None: + shape = [] + elif isinstance(size, (int, long, np.integer)): + shape = [size] + else: + shape = size + + if len(mean.shape) != 1: + raise ValueError("mean must be 1 dimensional") + if (len(cov.shape) != 2) or (cov.shape[0] != cov.shape[1]): + raise ValueError("cov must be 2 dimensional and square") + if mean.shape[0] != cov.shape[0]: + raise ValueError("mean and cov must have same length") + + # Compute shape of output and create a matrix of independent + # standard normally distributed random numbers. The matrix has rows + # with the same length as mean and as many rows are necessary to + # form a matrix of shape final_shape. + final_shape = list(shape[:]) + final_shape.append(mean.shape[0]) + x = self.standard_normal(final_shape).reshape(-1, mean.shape[0]) + + # Transform matrix of standard normals into matrix where each row + # contains multivariate normals with the desired covariance. + # Compute A such that dot(transpose(A),A) == cov. + # Then the matrix products of the rows of x and A has the desired + # covariance. Note that sqrt(s)*v where (u,s,v) is the singular value + # decomposition of cov is such an A. + # + # Also check that cov is positive-semidefinite. If so, the u.T and v + # matrices should be equal up to roundoff error if cov is + # symmetric and the singular value of the corresponding row is + # not zero. We continue to use the SVD rather than Cholesky in + # order to preserve current outputs. Note that symmetry has not + # been checked. + + # GH10839, ensure double to make tol meaningful + cov = cov.astype(np.double) + if method == 'svd': + from numpy.linalg import svd + (u, s, vh) = svd(cov) + elif method == 'eigh': + from numpy.linalg import eigh + # could call linalg.svd(hermitian=True), but that calculates a vh we don't need + (s, u) = eigh(cov) + else: + from numpy.linalg import cholesky + l = cholesky(cov) + + # make sure check_valid is ignored when method == 'cholesky' + # since the decomposition will have failed if cov is not valid. + if check_valid != 'ignore' and method != 'cholesky': + if check_valid != 'warn' and check_valid != 'raise': + raise ValueError( + "check_valid must equal 'warn', 'raise', or 'ignore'") + if method == 'svd': + psd = np.allclose(np.dot(vh.T * s, vh), cov, rtol=tol, atol=tol) + else: + psd = not np.any(s < -tol) + if not psd: + if check_valid == 'warn': + warnings.warn("covariance is not positive-semidefinite.", + RuntimeWarning) + else: + raise ValueError("covariance is not positive-semidefinite.") + + if method == 'cholesky': + _factor = l + elif method == 'eigh': + # if check_valid == 'ignore' we need to ensure that np.sqrt does not + # return a NaN if s is a very small negative number that is + # approximately zero or when the covariance is not positive-semidefinite + _factor = u * np.sqrt(abs(s)) + else: + _factor = u * np.sqrt(s) + + x = mean + x @ _factor.T + x.shape = tuple(final_shape) + return x + + def multinomial(self, object n, object pvals, size=None): + """ + multinomial(n, pvals, size=None) + + Draw samples from a multinomial distribution. + + The multinomial distribution is a multivariate generalization of the + binomial distribution. Take an experiment with one of ``p`` + possible outcomes. An example of such an experiment is throwing a dice, + where the outcome can be 1 through 6. Each sample drawn from the + distribution represents `n` such experiments. Its values, + ``X_i = [X_0, X_1, ..., X_p]``, represent the number of times the + outcome was ``i``. + + Parameters + ---------- + n : int or array-like of ints + Number of experiments. + pvals : array-like of floats + Probabilities of each of the ``p`` different outcomes with shape + ``(k0, k1, ..., kn, p)``. Each element ``pvals[i,j,...,:]`` must + sum to 1 (however, the last element is always assumed to account + for the remaining probability, as long as + ``sum(pvals[..., :-1], axis=-1) <= 1.0``. Must have at least 1 + dimension where pvals.shape[-1] > 0. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn each with ``p`` elements. Default + is None where the output size is determined by the broadcast shape + of ``n`` and all by the final dimension of ``pvals``, which is + denoted as ``b=(b0, b1, ..., bq)``. If size is not None, then it + must be compatible with the broadcast shape ``b``. Specifically, + size must have ``q`` or more elements and size[-(q-j):] must equal + ``bj``. + + Returns + ------- + out : ndarray + The drawn samples, of shape size, if provided. When size is + provided, the output shape is size + (p,) If not specified, + the shape is determined by the broadcast shape of ``n`` and + ``pvals``, ``(b0, b1, ..., bq)`` augmented with the dimension of + the multinomial, ``p``, so that that output shape is + ``(b0, b1, ..., bq, p)``. + + Each entry ``out[i,j,...,:]`` is a ``p``-dimensional value drawn + from the distribution. + + Examples + -------- + Throw a dice 20 times: + + >>> rng = np.random.default_rng() + >>> rng.multinomial(20, [1/6.]*6, size=1) + array([[4, 1, 7, 5, 2, 1]]) # random + + It landed 4 times on 1, once on 2, etc. + + Now, throw the dice 20 times, and 20 times again: + + >>> rng.multinomial(20, [1/6.]*6, size=2) + array([[3, 4, 3, 3, 4, 3], + [2, 4, 3, 4, 0, 7]]) # random + + For the first run, we threw 3 times 1, 4 times 2, etc. For the second, + we threw 2 times 1, 4 times 2, etc. + + Now, do one experiment throwing the dice 10 time, and 10 times again, + and another throwing the dice 20 times, and 20 times again: + + >>> rng.multinomial([[10], [20]], [1/6.]*6, size=(2, 2)) + array([[[2, 4, 0, 1, 2, 1], + [1, 3, 0, 3, 1, 2]], + [[1, 4, 4, 4, 4, 3], + [3, 3, 2, 5, 5, 2]]]) # random + + The first array shows the outcomes of throwing the dice 10 times, and + the second shows the outcomes from throwing the dice 20 times. + + A loaded die is more likely to land on number 6: + + >>> rng.multinomial(100, [1/7.]*5 + [2/7.]) + array([11, 16, 14, 17, 16, 26]) # random + + Simulate 10 throws of a 4-sided die and 20 throws of a 6-sided die + + >>> rng.multinomial([10, 20],[[1/4]*4 + [0]*2, [1/6]*6]) + array([[2, 1, 4, 3, 0, 0], + [3, 3, 3, 6, 1, 4]], dtype=int64) # random + + Generate categorical random variates from two categories where the + first has 3 outcomes and the second has 2. + + >>> rng.multinomial(1, [[.1, .5, .4 ], [.3, .7, .0]]) + array([[0, 0, 1], + [0, 1, 0]], dtype=int64) # random + + ``argmax(axis=-1)`` is then used to return the categories. + + >>> pvals = [[.1, .5, .4 ], [.3, .7, .0]] + >>> rvs = rng.multinomial(1, pvals, size=(4,2)) + >>> rvs.argmax(axis=-1) + array([[0, 1], + [2, 0], + [2, 1], + [2, 0]], dtype=int64) # random + + The same output dimension can be produced using broadcasting. + + >>> rvs = rng.multinomial([[1]] * 4, pvals) + >>> rvs.argmax(axis=-1) + array([[0, 1], + [2, 0], + [2, 1], + [2, 0]], dtype=int64) # random + + The probability inputs should be normalized. As an implementation + detail, the value of the last entry is ignored and assumed to take + up any leftover probability mass, but this should not be relied on. + A biased coin which has twice as much weight on one side as on the + other should be sampled like so: + + >>> rng.multinomial(100, [1.0 / 3, 2.0 / 3]) # RIGHT + array([38, 62]) # random + + not like: + + >>> rng.multinomial(100, [1.0, 2.0]) # WRONG + Traceback (most recent call last): + ValueError: pvals < 0, pvals > 1 or pvals contains NaNs + """ + + cdef np.npy_intp d, i, sz, offset, pi + cdef np.ndarray parr, mnarr, on, temp_arr + cdef double *pix + cdef int ndim + cdef int64_t *mnix + cdef int64_t ni + cdef np.broadcast it + on = <np.ndarray>np.PyArray_FROM_OTF(n, + np.NPY_INT64, + np.NPY_ARRAY_ALIGNED | + np.NPY_ARRAY_C_CONTIGUOUS) + parr = <np.ndarray>np.PyArray_FROM_OTF(pvals, + np.NPY_DOUBLE, + np.NPY_ARRAY_ALIGNED | + np.NPY_ARRAY_C_CONTIGUOUS) + ndim = parr.ndim + d = parr.shape[ndim - 1] if ndim >= 1 else 0 + if d == 0: + raise ValueError( + "pvals must have at least 1 dimension and the last dimension " + "of pvals must be greater than 0." + ) + + check_array_constraint(parr, 'pvals', CONS_BOUNDED_0_1) + pix = <double*>np.PyArray_DATA(parr) + sz = np.PyArray_SIZE(parr) + # Cython 0.29.20 would not correctly translate the range-based for + # loop to a C for loop + # for offset in range(<np.npy_intp>0, sz, d): + offset = 0 + while offset < sz: + if kahan_sum(pix + offset, d-1) > (1.0 + 1e-12): + # When floating, but not float dtype, and close, improve the error + # 1.0001 works for float16 and float32 + slice_repr = "[:-1]" if ndim == 1 else "[...,:-1]" + if (isinstance(pvals, np.ndarray) + and np.issubdtype(pvals.dtype, np.floating) + and pvals.dtype != float + and pvals.sum() < 1.0001): + msg = (f"sum(pvals{slice_repr}.astype(np.float64)) > 1.0." + " The pvals array is cast to 64-bit floating" + " point prior to checking the sum. Precision " + "changes when casting may cause problems even " + "if the sum of the original pvals is valid.") + else: + msg = f"sum(pvals{slice_repr}) > 1.0" + raise ValueError(msg) + offset += d + + if np.PyArray_NDIM(on) != 0 or ndim > 1: # vector + check_array_constraint(on, 'n', CONS_NON_NEGATIVE) + # This provides the offsets to use in the C-contig parr when + # broadcasting + offsets = <np.ndarray>np.arange( + 0, np.PyArray_SIZE(parr), d, dtype=np.intp + ).reshape((<object>parr).shape[:ndim - 1]) + if size is None: + it = np.PyArray_MultiIterNew2(on, offsets) + else: + temp = np.empty(size, dtype=np.int8) + temp_arr = <np.ndarray>temp + it = np.PyArray_MultiIterNew3(on, offsets, temp_arr) + # Validate size and the broadcast shape + try: + size = (operator.index(size),) + except: + size = tuple(size) + # This test verifies that an axis with dim 1 in size has not + # been increased by broadcasting with the input + if it.shape != size: + raise ValueError( + f"Output size {size} is not compatible with " + f"broadcast dimensions of inputs {it.shape}." + ) + shape = it.shape + (d,) + multin = np.zeros(shape, dtype=np.int64) + mnarr = <np.ndarray>multin + mnix = <int64_t*>np.PyArray_DATA(mnarr) + offset = 0 + sz = it.size + with self.lock, nogil: + for i in range(sz): + ni = (<int64_t*>np.PyArray_MultiIter_DATA(it, 0))[0] + pi = (<np.npy_intp*>np.PyArray_MultiIter_DATA(it, 1))[0] + random_multinomial(&self._bitgen, ni, &mnix[offset], &pix[pi], d, &self._binomial) + offset += d + np.PyArray_MultiIter_NEXT(it) + return multin + + if size is None: + shape = (d,) + else: + try: + shape = (operator.index(size), d) + except: + shape = tuple(size) + (d,) + + multin = np.zeros(shape, dtype=np.int64) + mnarr = <np.ndarray>multin + mnix = <int64_t*>np.PyArray_DATA(mnarr) + sz = np.PyArray_SIZE(mnarr) + ni = n + check_constraint(ni, 'n', CONS_NON_NEGATIVE) + offset = 0 + with self.lock, nogil: + for i in range(sz // d): + random_multinomial(&self._bitgen, ni, &mnix[offset], pix, d, &self._binomial) + offset += d + + return multin + + def multivariate_hypergeometric(self, object colors, object nsample, + size=None, method='marginals'): + """ + multivariate_hypergeometric(colors, nsample, size=None, + method='marginals') + + Generate variates from a multivariate hypergeometric distribution. + + The multivariate hypergeometric distribution is a generalization + of the hypergeometric distribution. + + Choose ``nsample`` items at random without replacement from a + collection with ``N`` distinct types. ``N`` is the length of + ``colors``, and the values in ``colors`` are the number of occurrences + of that type in the collection. The total number of items in the + collection is ``sum(colors)``. Each random variate generated by this + function is a vector of length ``N`` holding the counts of the + different types that occurred in the ``nsample`` items. + + The name ``colors`` comes from a common description of the + distribution: it is the probability distribution of the number of + marbles of each color selected without replacement from an urn + containing marbles of different colors; ``colors[i]`` is the number + of marbles in the urn with color ``i``. + + Parameters + ---------- + colors : sequence of integers + The number of each type of item in the collection from which + a sample is drawn. The values in ``colors`` must be nonnegative. + To avoid loss of precision in the algorithm, ``sum(colors)`` + must be less than ``10**9`` when `method` is "marginals". + nsample : int + The number of items selected. ``nsample`` must not be greater + than ``sum(colors)``. + size : int or tuple of ints, optional + The number of variates to generate, either an integer or a tuple + holding the shape of the array of variates. If the given size is, + e.g., ``(k, m)``, then ``k * m`` variates are drawn, where one + variate is a vector of length ``len(colors)``, and the return value + has shape ``(k, m, len(colors))``. If `size` is an integer, the + output has shape ``(size, len(colors))``. Default is None, in + which case a single variate is returned as an array with shape + ``(len(colors),)``. + method : string, optional + Specify the algorithm that is used to generate the variates. + Must be 'count' or 'marginals' (the default). See the Notes + for a description of the methods. + + Returns + ------- + variates : ndarray + Array of variates drawn from the multivariate hypergeometric + distribution. + + See Also + -------- + hypergeometric : Draw samples from the (univariate) hypergeometric + distribution. + + Notes + ----- + The two methods do not return the same sequence of variates. + + The "count" algorithm is roughly equivalent to the following numpy + code:: + + choices = np.repeat(np.arange(len(colors)), colors) + selection = np.random.choice(choices, nsample, replace=False) + variate = np.bincount(selection, minlength=len(colors)) + + The "count" algorithm uses a temporary array of integers with length + ``sum(colors)``. + + The "marginals" algorithm generates a variate by using repeated + calls to the univariate hypergeometric sampler. It is roughly + equivalent to:: + + variate = np.zeros(len(colors), dtype=np.int64) + # `remaining` is the cumulative sum of `colors` from the last + # element to the first; e.g. if `colors` is [3, 1, 5], then + # `remaining` is [9, 6, 5]. + remaining = np.cumsum(colors[::-1])[::-1] + for i in range(len(colors)-1): + if nsample < 1: + break + variate[i] = hypergeometric(colors[i], remaining[i+1], + nsample) + nsample -= variate[i] + variate[-1] = nsample + + The default method is "marginals". For some cases (e.g. when + `colors` contains relatively small integers), the "count" method + can be significantly faster than the "marginals" method. If + performance of the algorithm is important, test the two methods + with typical inputs to decide which works best. + + .. versionadded:: 1.18.0 + + Examples + -------- + >>> colors = [16, 8, 4] + >>> seed = 4861946401452 + >>> gen = np.random.Generator(np.random.PCG64(seed)) + >>> gen.multivariate_hypergeometric(colors, 6) + array([5, 0, 1]) + >>> gen.multivariate_hypergeometric(colors, 6, size=3) + array([[5, 0, 1], + [2, 2, 2], + [3, 3, 0]]) + >>> gen.multivariate_hypergeometric(colors, 6, size=(2, 2)) + array([[[3, 2, 1], + [3, 2, 1]], + [[4, 1, 1], + [3, 2, 1]]]) + """ + cdef int64_t nsamp + cdef size_t num_colors + cdef int64_t total + cdef int64_t *colors_ptr + cdef int64_t max_index + cdef size_t num_variates + cdef int64_t *variates_ptr + cdef int result + + if method not in ['count', 'marginals']: + raise ValueError('method must be "count" or "marginals".') + + try: + operator.index(nsample) + except TypeError: + raise ValueError('nsample must be an integer') + + if nsample < 0: + raise ValueError("nsample must be nonnegative.") + if nsample > INT64_MAX: + raise ValueError("nsample must not exceed %d" % INT64_MAX) + nsamp = nsample + + # Validation of colors, a 1-d sequence of nonnegative integers. + invalid_colors = False + try: + colors = np.asarray(colors) + if colors.ndim != 1: + invalid_colors = True + elif colors.size > 0 and not np.issubdtype(colors.dtype, + np.integer): + invalid_colors = True + elif np.any((colors < 0) | (colors > INT64_MAX)): + invalid_colors = True + except ValueError: + invalid_colors = True + if invalid_colors: + raise ValueError('colors must be a one-dimensional sequence ' + 'of nonnegative integers not exceeding %d.' % + INT64_MAX) + + colors = np.ascontiguousarray(colors, dtype=np.int64) + num_colors = colors.size + + colors_ptr = <int64_t *> np.PyArray_DATA(colors) + + total = _safe_sum_nonneg_int64(num_colors, colors_ptr) + if total == -1: + raise ValueError("sum(colors) must not exceed the maximum value " + "of a 64 bit signed integer (%d)" % INT64_MAX) + + if method == 'marginals' and total >= 1000000000: + raise ValueError('When method is "marginals", sum(colors) must ' + 'be less than 1000000000.') + + # The C code that implements the 'count' method will malloc an + # array of size total*sizeof(size_t). Here we ensure that that + # product does not overflow. + if SIZE_MAX > <uint64_t>INT64_MAX: + max_index = INT64_MAX // sizeof(size_t) + else: + max_index = SIZE_MAX // sizeof(size_t) + if method == 'count' and total > max_index: + raise ValueError("When method is 'count', sum(colors) must not " + "exceed %d" % max_index) + if nsamp > total: + raise ValueError("nsample > sum(colors)") + + # Figure out the shape of the return array. + if size is None: + shape = (num_colors,) + elif np.isscalar(size): + shape = (size, num_colors) + else: + shape = tuple(size) + (num_colors,) + variates = np.zeros(shape, dtype=np.int64) + + if num_colors == 0: + return variates + + # One variate is a vector of length num_colors. + num_variates = variates.size // num_colors + variates_ptr = <int64_t *> np.PyArray_DATA(variates) + + if method == 'count': + with self.lock, nogil: + result = random_multivariate_hypergeometric_count(&self._bitgen, + total, num_colors, colors_ptr, nsamp, + num_variates, variates_ptr) + if result == -1: + raise MemoryError("Insufficient memory for multivariate_" + "hypergeometric with method='count' and " + "sum(colors)=%d" % total) + else: + with self.lock, nogil: + random_multivariate_hypergeometric_marginals(&self._bitgen, + total, num_colors, colors_ptr, nsamp, + num_variates, variates_ptr) + return variates + + def dirichlet(self, object alpha, size=None): + """ + dirichlet(alpha, size=None) + + Draw samples from the Dirichlet distribution. + + Draw `size` samples of dimension k from a Dirichlet distribution. A + Dirichlet-distributed random variable can be seen as a multivariate + generalization of a Beta distribution. The Dirichlet distribution + is a conjugate prior of a multinomial distribution in Bayesian + inference. + + Parameters + ---------- + alpha : sequence of floats, length k + Parameter of the distribution (length ``k`` for sample of + length ``k``). + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n)``, then + ``m * n * k`` samples are drawn. Default is None, in which case a + vector of length ``k`` is returned. + + Returns + ------- + samples : ndarray, + The drawn samples, of shape ``(size, k)``. + + Raises + ------ + ValueError + If any value in ``alpha`` is less than or equal to zero + + Notes + ----- + The Dirichlet distribution is a distribution over vectors + :math:`x` that fulfil the conditions :math:`x_i>0` and + :math:`\\sum_{i=1}^k x_i = 1`. + + The probability density function :math:`p` of a + Dirichlet-distributed random vector :math:`X` is + proportional to + + .. math:: p(x) \\propto \\prod_{i=1}^{k}{x^{\\alpha_i-1}_i}, + + where :math:`\\alpha` is a vector containing the positive + concentration parameters. + + The method uses the following property for computation: let :math:`Y` + be a random vector which has components that follow a standard gamma + distribution, then :math:`X = \\frac{1}{\\sum_{i=1}^k{Y_i}} Y` + is Dirichlet-distributed + + References + ---------- + .. [1] David McKay, "Information Theory, Inference and Learning + Algorithms," chapter 23, + http://www.inference.org.uk/mackay/itila/ + .. [2] Wikipedia, "Dirichlet distribution", + https://en.wikipedia.org/wiki/Dirichlet_distribution + + Examples + -------- + Taking an example cited in Wikipedia, this distribution can be used if + one wanted to cut strings (each of initial length 1.0) into K pieces + with different lengths, where each piece had, on average, a designated + average length, but allowing some variation in the relative sizes of + the pieces. + + >>> s = np.random.default_rng().dirichlet((10, 5, 3), 20).transpose() + + >>> import matplotlib.pyplot as plt + >>> plt.barh(range(20), s[0]) + >>> plt.barh(range(20), s[1], left=s[0], color='g') + >>> plt.barh(range(20), s[2], left=s[0]+s[1], color='r') + >>> plt.title("Lengths of Strings") + + """ + + # ================= + # Pure python algo + # ================= + # alpha = N.atleast_1d(alpha) + # k = alpha.size + + # if n == 1: + # val = N.zeros(k) + # for i in range(k): + # val[i] = sgamma(alpha[i], n) + # val /= N.sum(val) + # else: + # val = N.zeros((k, n)) + # for i in range(k): + # val[i] = sgamma(alpha[i], n) + # val /= N.sum(val, axis = 0) + # val = val.T + # return val + + cdef np.npy_intp k, totsize, i, j + cdef np.ndarray alpha_arr, val_arr, alpha_csum_arr + cdef double csum + cdef double *alpha_data + cdef double *alpha_csum_data + cdef double *val_data + cdef double acc, invacc, v + + k = len(alpha) + alpha_arr = <np.ndarray>np.PyArray_FROMANY( + alpha, np.NPY_DOUBLE, 1, 1, + np.NPY_ARRAY_ALIGNED | np.NPY_ARRAY_C_CONTIGUOUS) + if np.any(np.less_equal(alpha_arr, 0)): + raise ValueError('alpha <= 0') + alpha_data = <double*>np.PyArray_DATA(alpha_arr) + + if size is None: + shape = (k,) + else: + try: + shape = (operator.index(size), k) + except: + shape = tuple(size) + (k,) + + diric = np.zeros(shape, np.float64) + val_arr = <np.ndarray>diric + val_data= <double*>np.PyArray_DATA(val_arr) + + i = 0 + totsize = np.PyArray_SIZE(val_arr) + + # Select one of the following two algorithms for the generation + # of Dirichlet random variates (RVs) + # + # A) Small alpha case: Use the stick-breaking approach with beta + # random variates (RVs). + # B) Standard case: Perform unit normalisation of a vector + # of gamma random variates + # + # A) prevents NaNs resulting from 0/0 that may occur in B) + # when all values in the vector ':math:\\alpha' are smaller + # than 1, then there is a nonzero probability that all + # generated gamma RVs will be 0. When that happens, the + # normalization process ends up computing 0/0, giving nan. A) + # does not use divisions, so that a situation in which 0/0 has + # to be computed cannot occur. A) is slower than B) as + # generation of beta RVs is slower than generation of gamma + # RVs. A) is selected whenever `alpha.max() < t`, where `t < + # 1` is a threshold that controls the probability of + # generating a NaN value when B) is used. For a given + # threshold `t` this probability can be bounded by + # `gammainc(t, d)` where `gammainc` is the regularized + # incomplete gamma function and `d` is the smallest positive + # floating point number that can be represented with a given + # precision. For the chosen threshold `t=0.1` this probability + # is smaller than `1.8e-31` for double precision floating + # point numbers. + + if (k > 0) and (alpha_arr.max() < 0.1): + # Small alpha case: Use stick-breaking approach with beta + # random variates (RVs). + # alpha_csum_data will hold the cumulative sum, right to + # left, of alpha_arr. + # Use a numpy array for memory management only. We could just as + # well have malloc'd alpha_csum_data. alpha_arr is a C-contiguous + # double array, therefore so is alpha_csum_arr. + alpha_csum_arr = np.empty_like(alpha_arr) + alpha_csum_data = <double*>np.PyArray_DATA(alpha_csum_arr) + csum = 0.0 + for j in range(k - 1, -1, -1): + csum += alpha_data[j] + alpha_csum_data[j] = csum + + with self.lock, nogil: + while i < totsize: + acc = 1. + for j in range(k - 1): + v = random_beta(&self._bitgen, alpha_data[j], + alpha_csum_data[j + 1]) + val_data[i + j] = acc * v + acc *= (1. - v) + val_data[i + k - 1] = acc + i = i + k + + else: + # Standard case: Unit normalisation of a vector of gamma random + # variates + with self.lock, nogil: + while i < totsize: + acc = 0. + for j in range(k): + val_data[i + j] = random_standard_gamma(&self._bitgen, + alpha_data[j]) + acc = acc + val_data[i + j] + invacc = 1. / acc + for j in range(k): + val_data[i + j] = val_data[i + j] * invacc + i = i + k + + return diric + + def permuted(self, object x, *, axis=None, out=None): + """ + permuted(x, axis=None, out=None) + + Randomly permute `x` along axis `axis`. + + Unlike `shuffle`, each slice along the given axis is shuffled + independently of the others. + + Parameters + ---------- + x : array_like, at least one-dimensional + Array to be shuffled. + axis : int, optional + Slices of `x` in this axis are shuffled. Each slice + is shuffled independently of the others. If `axis` is + None, the flattened array is shuffled. + out : ndarray, optional + If given, this is the destinaton of the shuffled array. + If `out` is None, a shuffled copy of the array is returned. + + Returns + ------- + ndarray + If `out` is None, a shuffled copy of `x` is returned. + Otherwise, the shuffled array is stored in `out`, + and `out` is returned + + See Also + -------- + shuffle + permutation + + Examples + -------- + Create a `numpy.random.Generator` instance: + + >>> rng = np.random.default_rng() + + Create a test array: + + >>> x = np.arange(24).reshape(3, 8) + >>> x + array([[ 0, 1, 2, 3, 4, 5, 6, 7], + [ 8, 9, 10, 11, 12, 13, 14, 15], + [16, 17, 18, 19, 20, 21, 22, 23]]) + + Shuffle the rows of `x`: + + >>> y = rng.permuted(x, axis=1) + >>> y + array([[ 4, 3, 6, 7, 1, 2, 5, 0], # random + [15, 10, 14, 9, 12, 11, 8, 13], + [17, 16, 20, 21, 18, 22, 23, 19]]) + + `x` has not been modified: + + >>> x + array([[ 0, 1, 2, 3, 4, 5, 6, 7], + [ 8, 9, 10, 11, 12, 13, 14, 15], + [16, 17, 18, 19, 20, 21, 22, 23]]) + + To shuffle the rows of `x` in-place, pass `x` as the `out` + parameter: + + >>> y = rng.permuted(x, axis=1, out=x) + >>> x + array([[ 3, 0, 4, 7, 1, 6, 2, 5], # random + [ 8, 14, 13, 9, 12, 11, 15, 10], + [17, 18, 16, 22, 19, 23, 20, 21]]) + + Note that when the ``out`` parameter is given, the return + value is ``out``: + + >>> y is x + True + """ + + cdef int ax + cdef np.npy_intp axlen, axstride, itemsize + cdef void *buf + cdef np.flatiter it + cdef np.ndarray to_shuffle + cdef int status + cdef int flags + + x = np.asarray(x) + + if out is None: + out = x.copy(order='K') + else: + if type(out) is not np.ndarray: + raise TypeError('out must be a numpy array') + if out.shape != x.shape: + raise ValueError('out must have the same shape as x') + np.copyto(out, x, casting='safe') + + if axis is None: + if x.ndim > 1: + if not (np.PyArray_FLAGS(out) & (np.NPY_ARRAY_C_CONTIGUOUS | + np.NPY_ARRAY_F_CONTIGUOUS)): + flags = (np.NPY_ARRAY_C_CONTIGUOUS | + NPY_ARRAY_WRITEBACKIFCOPY) + to_shuffle = PyArray_FromArray(<np.PyArrayObject *>out, + <np.PyArray_Descr *>NULL, flags) + self.shuffle(to_shuffle.ravel(order='K')) + # Because we only execute this block if out is not + # contiguous, we know this call will always result in a + # copy of to_shuffle back to out. I.e. status will be 1. + status = PyArray_ResolveWritebackIfCopy(to_shuffle) + assert status == 1 + else: + # out is n-d with n > 1, but is either C- or F-contiguous, + # so we know out.ravel(order='A') is a view. + self.shuffle(out.ravel(order='A')) + else: + # out is 1-d + self.shuffle(out) + return out + + ax = normalize_axis_index(axis, np.ndim(out)) + itemsize = out.itemsize + axlen = out.shape[ax] + axstride = out.strides[ax] + + it = np.PyArray_IterAllButAxis(out, &ax) + + buf = PyMem_Malloc(itemsize) + if buf == NULL: + raise MemoryError('memory allocation failed in permuted') + + if out.dtype.hasobject: + # Keep the GIL when shuffling an object array. + with self.lock: + while np.PyArray_ITER_NOTDONE(it): + _shuffle_raw_wrap(&self._bitgen, axlen, 0, itemsize, + axstride, + <char *>np.PyArray_ITER_DATA(it), + <char *>buf) + np.PyArray_ITER_NEXT(it) + else: + # out is not an object array, so we can release the GIL. + with self.lock, nogil: + while np.PyArray_ITER_NOTDONE(it): + _shuffle_raw_wrap(&self._bitgen, axlen, 0, itemsize, + axstride, + <char *>np.PyArray_ITER_DATA(it), + <char *>buf) + np.PyArray_ITER_NEXT(it) + + PyMem_Free(buf) + return out + + def shuffle(self, object x, axis=0): + """ + shuffle(x, axis=0) + + Modify an array or sequence in-place by shuffling its contents. + + The order of sub-arrays is changed but their contents remains the same. + + Parameters + ---------- + x : ndarray or MutableSequence + The array, list or mutable sequence to be shuffled. + axis : int, optional + The axis which `x` is shuffled along. Default is 0. + It is only supported on `ndarray` objects. + + Returns + ------- + None + + Examples + -------- + >>> rng = np.random.default_rng() + >>> arr = np.arange(10) + >>> rng.shuffle(arr) + >>> arr + [1 7 5 2 9 4 3 6 0 8] # random + + >>> arr = np.arange(9).reshape((3, 3)) + >>> rng.shuffle(arr) + >>> arr + array([[3, 4, 5], # random + [6, 7, 8], + [0, 1, 2]]) + + >>> arr = np.arange(9).reshape((3, 3)) + >>> rng.shuffle(arr, axis=1) + >>> arr + array([[2, 0, 1], # random + [5, 3, 4], + [8, 6, 7]]) + """ + cdef: + np.npy_intp i, j, n = len(x), stride, itemsize + char* x_ptr + char* buf_ptr + + if isinstance(x, np.ndarray): + # Only call ndim on ndarrays, see GH 18142 + axis = normalize_axis_index(axis, np.ndim(x)) + + if type(x) is np.ndarray and x.ndim == 1 and x.size: + # Fast, statically typed path: shuffle the underlying buffer. + # Only for non-empty, 1d objects of class ndarray (subclasses such + # as MaskedArrays may not support this approach). + x_ptr = np.PyArray_BYTES(x) + stride = x.strides[0] + itemsize = x.dtype.itemsize + # As the array x could contain python objects we use a buffer + # of bytes for the swaps to avoid leaving one of the objects + # within the buffer and erroneously decrementing it's refcount + # when the function exits. + buf = np.empty(itemsize, dtype=np.int8) # GC'd at function exit + buf_ptr = np.PyArray_BYTES(buf) + if x.dtype.hasobject: + with self.lock: + _shuffle_raw_wrap(&self._bitgen, n, 1, itemsize, stride, + x_ptr, buf_ptr) + else: + # Same as above, but the GIL is released. + with self.lock, nogil: + _shuffle_raw_wrap(&self._bitgen, n, 1, itemsize, stride, + x_ptr, buf_ptr) + elif isinstance(x, np.ndarray): + if x.size == 0: + # shuffling is a no-op + return + + x = np.swapaxes(x, 0, axis) + buf = np.empty_like(x[0, ...]) + with self.lock: + for i in reversed(range(1, len(x))): + j = random_interval(&self._bitgen, i) + if i == j: + # i == j is not needed and memcpy is undefined. + continue + buf[...] = x[j, ...] + x[j, ...] = x[i, ...] + x[i, ...] = buf + else: + # Untyped path. + if not isinstance(x, Sequence): + # See gh-18206. We may decide to deprecate here in the future. + warnings.warn( + f"you are shuffling a '{type(x).__name__}' object " + "which is not a subclass of 'Sequence'; " + "`shuffle` is not guaranteed to behave correctly. " + "E.g., non-numpy array/tensor objects with view semantics " + "may contain duplicates after shuffling.", + UserWarning, stacklevel=1) # Cython does not add a level + + if axis != 0: + raise NotImplementedError("Axis argument is only supported " + "on ndarray objects") + with self.lock: + for i in reversed(range(1, n)): + j = random_interval(&self._bitgen, i) + x[i], x[j] = x[j], x[i] + + def permutation(self, object x, axis=0): + """ + permutation(x, axis=0) + + Randomly permute a sequence, or return a permuted range. + + Parameters + ---------- + x : int or array_like + If `x` is an integer, randomly permute ``np.arange(x)``. + If `x` is an array, make a copy and shuffle the elements + randomly. + axis : int, optional + The axis which `x` is shuffled along. Default is 0. + + Returns + ------- + out : ndarray + Permuted sequence or array range. + + Examples + -------- + >>> rng = np.random.default_rng() + >>> rng.permutation(10) + array([1, 7, 4, 3, 0, 9, 2, 5, 8, 6]) # random + + >>> rng.permutation([1, 4, 9, 12, 15]) + array([15, 1, 9, 4, 12]) # random + + >>> arr = np.arange(9).reshape((3, 3)) + >>> rng.permutation(arr) + array([[6, 7, 8], # random + [0, 1, 2], + [3, 4, 5]]) + + >>> rng.permutation("abc") + Traceback (most recent call last): + ... + numpy.AxisError: axis 0 is out of bounds for array of dimension 0 + + >>> arr = np.arange(9).reshape((3, 3)) + >>> rng.permutation(arr, axis=1) + array([[0, 2, 1], # random + [3, 5, 4], + [6, 8, 7]]) + + """ + if isinstance(x, (int, np.integer)): + arr = np.arange(x) + self.shuffle(arr) + return arr + + arr = np.asarray(x) + + axis = normalize_axis_index(axis, arr.ndim) + + # shuffle has fast-path for 1-d + if arr.ndim == 1: + # Return a copy if same memory + if np.may_share_memory(arr, x): + arr = np.array(arr) + self.shuffle(arr) + return arr + + # Shuffle index array, dtype to ensure fast path + idx = np.arange(arr.shape[axis], dtype=np.intp) + self.shuffle(idx) + slices = [slice(None)]*arr.ndim + slices[axis] = idx + return arr[tuple(slices)] + + +def default_rng(seed=None): + """Construct a new Generator with the default BitGenerator (PCG64). + + Parameters + ---------- + seed : {None, int, array_like[ints], SeedSequence, BitGenerator, Generator}, optional + A seed to initialize the `BitGenerator`. If None, then fresh, + unpredictable entropy will be pulled from the OS. If an ``int`` or + ``array_like[ints]`` is passed, then it will be passed to + `SeedSequence` to derive the initial `BitGenerator` state. One may also + pass in a `SeedSequence` instance. + Additionally, when passed a `BitGenerator`, it will be wrapped by + `Generator`. If passed a `Generator`, it will be returned unaltered. + + Returns + ------- + Generator + The initialized generator object. + + Notes + ----- + If ``seed`` is not a `BitGenerator` or a `Generator`, a new `BitGenerator` + is instantiated. This function does not manage a default global instance. + + Examples + -------- + ``default_rng`` is the recommended constructor for the random number class + ``Generator``. Here are several ways we can construct a random + number generator using ``default_rng`` and the ``Generator`` class. + + Here we use ``default_rng`` to generate a random float: + + >>> import numpy as np + >>> rng = np.random.default_rng(12345) + >>> print(rng) + Generator(PCG64) + >>> rfloat = rng.random() + >>> rfloat + 0.22733602246716966 + >>> type(rfloat) + <class 'float'> + + Here we use ``default_rng`` to generate 3 random integers between 0 + (inclusive) and 10 (exclusive): + + >>> import numpy as np + >>> rng = np.random.default_rng(12345) + >>> rints = rng.integers(low=0, high=10, size=3) + >>> rints + array([6, 2, 7]) + >>> type(rints[0]) + <class 'numpy.int64'> + + Here we specify a seed so that we have reproducible results: + + >>> import numpy as np + >>> rng = np.random.default_rng(seed=42) + >>> print(rng) + Generator(PCG64) + >>> arr1 = rng.random((3, 3)) + >>> arr1 + array([[0.77395605, 0.43887844, 0.85859792], + [0.69736803, 0.09417735, 0.97562235], + [0.7611397 , 0.78606431, 0.12811363]]) + + If we exit and restart our Python interpreter, we'll see that we + generate the same random numbers again: + + >>> import numpy as np + >>> rng = np.random.default_rng(seed=42) + >>> arr2 = rng.random((3, 3)) + >>> arr2 + array([[0.77395605, 0.43887844, 0.85859792], + [0.69736803, 0.09417735, 0.97562235], + [0.7611397 , 0.78606431, 0.12811363]]) + + """ + if _check_bit_generator(seed): + # We were passed a BitGenerator, so just wrap it up. + return Generator(seed) + elif isinstance(seed, Generator): + # Pass through a Generator. + return seed + # Otherwise we need to instantiate a new BitGenerator and Generator as + # normal. + return Generator(PCG64(seed)) diff --git a/numpy/random/_mt19937.pyi b/numpy/random/_mt19937.pyi new file mode 100644 index 000000000000..820f27392f0f --- /dev/null +++ b/numpy/random/_mt19937.pyi @@ -0,0 +1,22 @@ +from typing import Any, Union, TypedDict + +from numpy import dtype, ndarray, uint32 +from numpy.random.bit_generator import BitGenerator, SeedSequence +from numpy.typing import _ArrayLikeInt_co + +class _MT19937Internal(TypedDict): + key: ndarray[Any, dtype[uint32]] + pos: int + +class _MT19937State(TypedDict): + bit_generator: str + state: _MT19937Internal + +class MT19937(BitGenerator): + def __init__(self, seed: Union[None, _ArrayLikeInt_co, SeedSequence] = ...) -> None: ... + def _legacy_seeding(self, seed: _ArrayLikeInt_co) -> None: ... + def jumped(self, jumps: int = ...) -> MT19937: ... + @property + def state(self) -> _MT19937State: ... + @state.setter + def state(self, value: _MT19937State) -> None: ... diff --git a/numpy/random/_mt19937.pyx b/numpy/random/_mt19937.pyx new file mode 100644 index 000000000000..16a377cc63b9 --- /dev/null +++ b/numpy/random/_mt19937.pyx @@ -0,0 +1,290 @@ +import operator + +import numpy as np +cimport numpy as np + +from libc.stdint cimport uint32_t, uint64_t +from numpy.random cimport BitGenerator, SeedSequence + +__all__ = ['MT19937'] + +np.import_array() + +cdef extern from "src/mt19937/mt19937.h": + + struct s_mt19937_state: + uint32_t key[624] + int pos + + ctypedef s_mt19937_state mt19937_state + + uint64_t mt19937_next64(mt19937_state *state) nogil + uint32_t mt19937_next32(mt19937_state *state) nogil + double mt19937_next_double(mt19937_state *state) nogil + void mt19937_init_by_array(mt19937_state *state, uint32_t *init_key, int key_length) + void mt19937_seed(mt19937_state *state, uint32_t seed) + void mt19937_jump(mt19937_state *state) + + enum: + RK_STATE_LEN + +cdef uint64_t mt19937_uint64(void *st) nogil: + return mt19937_next64(<mt19937_state *> st) + +cdef uint32_t mt19937_uint32(void *st) nogil: + return mt19937_next32(<mt19937_state *> st) + +cdef double mt19937_double(void *st) nogil: + return mt19937_next_double(<mt19937_state *> st) + +cdef uint64_t mt19937_raw(void *st) nogil: + return <uint64_t>mt19937_next32(<mt19937_state *> st) + +cdef class MT19937(BitGenerator): + """ + MT19937(seed=None) + + Container for the Mersenne Twister pseudo-random number generator. + + Parameters + ---------- + seed : {None, int, array_like[ints], SeedSequence}, optional + A seed to initialize the `BitGenerator`. If None, then fresh, + unpredictable entropy will be pulled from the OS. If an ``int`` or + ``array_like[ints]`` is passed, then it will be passed to + `SeedSequence` to derive the initial `BitGenerator` state. One may also + pass in a `SeedSequence` instance. + + Attributes + ---------- + lock: threading.Lock + Lock instance that is shared so that the same bit git generator can + be used in multiple Generators without corrupting the state. Code that + generates values from a bit generator should hold the bit generator's + lock. + + Notes + ----- + ``MT19937`` provides a capsule containing function pointers that produce + doubles, and unsigned 32 and 64- bit integers [1]_. These are not + directly consumable in Python and must be consumed by a ``Generator`` + or similar object that supports low-level access. + + The Python stdlib module "random" also contains a Mersenne Twister + pseudo-random number generator. + + **State and Seeding** + + The ``MT19937`` state vector consists of a 624-element array of + 32-bit unsigned integers plus a single integer value between 0 and 624 + that indexes the current position within the main array. + + The input seed is processed by `SeedSequence` to fill the whole state. The + first element is reset such that only its most significant bit is set. + + **Parallel Features** + + The preferred way to use a BitGenerator in parallel applications is to use + the `SeedSequence.spawn` method to obtain entropy values, and to use these + to generate new BitGenerators: + + >>> from numpy.random import Generator, MT19937, SeedSequence + >>> sg = SeedSequence(1234) + >>> rg = [Generator(MT19937(s)) for s in sg.spawn(10)] + + Another method is to use `MT19937.jumped` which advances the state as-if + :math:`2^{128}` random numbers have been generated ([1]_, [2]_). This + allows the original sequence to be split so that distinct segments can be + used in each worker process. All generators should be chained to ensure + that the segments come from the same sequence. + + >>> from numpy.random import Generator, MT19937, SeedSequence + >>> sg = SeedSequence(1234) + >>> bit_generator = MT19937(sg) + >>> rg = [] + >>> for _ in range(10): + ... rg.append(Generator(bit_generator)) + ... # Chain the BitGenerators + ... bit_generator = bit_generator.jumped() + + **Compatibility Guarantee** + + ``MT19937`` makes a guarantee that a fixed seed and will always produce + the same random integer stream. + + References + ---------- + .. [1] Hiroshi Haramoto, Makoto Matsumoto, and Pierre L\'Ecuyer, "A Fast + Jump Ahead Algorithm for Linear Recurrences in a Polynomial Space", + Sequences and Their Applications - SETA, 290--298, 2008. + .. [2] Hiroshi Haramoto, Makoto Matsumoto, Takuji Nishimura, François + Panneton, Pierre L\'Ecuyer, "Efficient Jump Ahead for F2-Linear + Random Number Generators", INFORMS JOURNAL ON COMPUTING, Vol. 20, + No. 3, Summer 2008, pp. 385-390. + + """ + cdef mt19937_state rng_state + + def __init__(self, seed=None): + BitGenerator.__init__(self, seed) + val = self._seed_seq.generate_state(RK_STATE_LEN, np.uint32) + # MSB is 1; assuring non-zero initial array + self.rng_state.key[0] = 0x80000000UL + for i in range(1, RK_STATE_LEN): + self.rng_state.key[i] = val[i] + self.rng_state.pos = i + + self._bitgen.state = &self.rng_state + self._bitgen.next_uint64 = &mt19937_uint64 + self._bitgen.next_uint32 = &mt19937_uint32 + self._bitgen.next_double = &mt19937_double + self._bitgen.next_raw = &mt19937_raw + + def _legacy_seeding(self, seed): + """ + _legacy_seeding(seed) + + Seed the generator in a backward compatible way. For modern + applications, creating a new instance is preferable. Calling this + overrides self._seed_seq + + Parameters + ---------- + seed : {None, int, array_like} + Random seed initializing the pseudo-random number generator. + Can be an integer in [0, 2**32-1], array of integers in + [0, 2**32-1], a `SeedSequence, or ``None``. If `seed` + is ``None``, then fresh, unpredictable entropy will be pulled from + the OS. + + Raises + ------ + ValueError + If seed values are out of range for the PRNG. + """ + cdef np.ndarray obj + with self.lock: + try: + if seed is None: + seed = SeedSequence() + val = seed.generate_state(RK_STATE_LEN) + # MSB is 1; assuring non-zero initial array + self.rng_state.key[0] = 0x80000000UL + for i in range(1, RK_STATE_LEN): + self.rng_state.key[i] = val[i] + else: + if hasattr(seed, 'squeeze'): + seed = seed.squeeze() + idx = operator.index(seed) + if idx > int(2**32 - 1) or idx < 0: + raise ValueError("Seed must be between 0 and 2**32 - 1") + mt19937_seed(&self.rng_state, seed) + except TypeError: + obj = np.asarray(seed) + if obj.size == 0: + raise ValueError("Seed must be non-empty") + obj = obj.astype(np.int64, casting='safe') + if obj.ndim != 1: + raise ValueError("Seed array must be 1-d") + if ((obj > int(2**32 - 1)) | (obj < 0)).any(): + raise ValueError("Seed must be between 0 and 2**32 - 1") + obj = obj.astype(np.uint32, casting='unsafe', order='C') + mt19937_init_by_array(&self.rng_state, <uint32_t*> obj.data, np.PyArray_DIM(obj, 0)) + self._seed_seq = None + + cdef jump_inplace(self, iter): + """ + Jump state in-place + + Not part of public API + + Parameters + ---------- + iter : integer, positive + Number of times to jump the state of the rng. + """ + cdef np.npy_intp i + for i in range(iter): + mt19937_jump(&self.rng_state) + + + def jumped(self, np.npy_intp jumps=1): + """ + jumped(jumps=1) + + Returns a new bit generator with the state jumped + + The state of the returned big generator is jumped as-if + 2**(128 * jumps) random numbers have been generated. + + Parameters + ---------- + jumps : integer, positive + Number of times to jump the state of the bit generator returned + + Returns + ------- + bit_generator : MT19937 + New instance of generator jumped iter times + + Notes + ----- + The jump step is computed using a modified version of Matsumoto's + implementation of Horner's method. The step polynomial is precomputed + to perform 2**128 steps. The jumped state has been verified to match + the state produced using Matsumoto's original code. + + References + ---------- + .. [1] Matsumoto, M, Generating multiple disjoint streams of + pseudorandom number sequences. Accessed on: May 6, 2020. + http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/JUMP/ + .. [2] Hiroshi Haramoto, Makoto Matsumoto, Takuji Nishimura, François + Panneton, Pierre L\'Ecuyer, "Efficient Jump Ahead for F2-Linear + Random Number Generators", INFORMS JOURNAL ON COMPUTING, Vol. 20, + No. 3, Summer 2008, pp. 385-390. + """ + cdef MT19937 bit_generator + + bit_generator = self.__class__() + bit_generator.state = self.state + bit_generator.jump_inplace(jumps) + + return bit_generator + + @property + def state(self): + """ + Get or set the PRNG state + + Returns + ------- + state : dict + Dictionary containing the information required to describe the + state of the PRNG + """ + key = np.zeros(624, dtype=np.uint32) + for i in range(624): + key[i] = self.rng_state.key[i] + + return {'bit_generator': self.__class__.__name__, + 'state': {'key': key, 'pos': self.rng_state.pos}} + + @state.setter + def state(self, value): + if isinstance(value, tuple): + if value[0] != 'MT19937' or len(value) not in (3, 5): + raise ValueError('state is not a legacy MT19937 state') + value ={'bit_generator': 'MT19937', + 'state': {'key': value[1], 'pos': value[2]}} + + if not isinstance(value, dict): + raise TypeError('state must be a dict') + bitgen = value.get('bit_generator', '') + if bitgen != self.__class__.__name__: + raise ValueError('state must be for a {0} ' + 'PRNG'.format(self.__class__.__name__)) + key = value['state']['key'] + for i in range(624): + self.rng_state.key[i] = key[i] + self.rng_state.pos = value['state']['pos'] diff --git a/numpy/random/_pcg64.pyi b/numpy/random/_pcg64.pyi new file mode 100644 index 000000000000..4881a987e2a7 --- /dev/null +++ b/numpy/random/_pcg64.pyi @@ -0,0 +1,42 @@ +from typing import Union, TypedDict + +from numpy.random.bit_generator import BitGenerator, SeedSequence +from numpy.typing import _ArrayLikeInt_co + +class _PCG64Internal(TypedDict): + state: int + inc: int + +class _PCG64State(TypedDict): + bit_generator: str + state: _PCG64Internal + has_uint32: int + uinteger: int + +class PCG64(BitGenerator): + def __init__(self, seed: Union[None, _ArrayLikeInt_co, SeedSequence] = ...) -> None: ... + def jumped(self, jumps: int = ...) -> PCG64: ... + @property + def state( + self, + ) -> _PCG64State: ... + @state.setter + def state( + self, + value: _PCG64State, + ) -> None: ... + def advance(self, delta: int) -> PCG64: ... + +class PCG64DXSM(BitGenerator): + def __init__(self, seed: Union[None, _ArrayLikeInt_co, SeedSequence] = ...) -> None: ... + def jumped(self, jumps: int = ...) -> PCG64DXSM: ... + @property + def state( + self, + ) -> _PCG64State: ... + @state.setter + def state( + self, + value: _PCG64State, + ) -> None: ... + def advance(self, delta: int) -> PCG64DXSM: ... diff --git a/numpy/random/_pcg64.pyx b/numpy/random/_pcg64.pyx new file mode 100644 index 000000000000..c0a10a812525 --- /dev/null +++ b/numpy/random/_pcg64.pyx @@ -0,0 +1,518 @@ +import numpy as np +cimport numpy as np + +from libc.stdint cimport uint32_t, uint64_t +from ._common cimport uint64_to_double, wrap_int +from numpy.random cimport BitGenerator + +__all__ = ['PCG64'] + +cdef extern from "src/pcg64/pcg64.h": + # Use int as generic type, actual type read from pcg64.h and is platform dependent + ctypedef int pcg64_random_t + + struct s_pcg64_state: + pcg64_random_t *pcg_state + int has_uint32 + uint32_t uinteger + + ctypedef s_pcg64_state pcg64_state + + uint64_t pcg64_next64(pcg64_state *state) nogil + uint32_t pcg64_next32(pcg64_state *state) nogil + void pcg64_jump(pcg64_state *state) + void pcg64_advance(pcg64_state *state, uint64_t *step) + void pcg64_set_seed(pcg64_state *state, uint64_t *seed, uint64_t *inc) + void pcg64_get_state(pcg64_state *state, uint64_t *state_arr, int *has_uint32, uint32_t *uinteger) + void pcg64_set_state(pcg64_state *state, uint64_t *state_arr, int has_uint32, uint32_t uinteger) + + uint64_t pcg64_cm_next64(pcg64_state *state) nogil + uint32_t pcg64_cm_next32(pcg64_state *state) nogil + void pcg64_cm_advance(pcg64_state *state, uint64_t *step) + +cdef uint64_t pcg64_uint64(void* st) nogil: + return pcg64_next64(<pcg64_state *>st) + +cdef uint32_t pcg64_uint32(void *st) nogil: + return pcg64_next32(<pcg64_state *> st) + +cdef double pcg64_double(void* st) nogil: + return uint64_to_double(pcg64_next64(<pcg64_state *>st)) + +cdef uint64_t pcg64_cm_uint64(void* st) nogil: + return pcg64_cm_next64(<pcg64_state *>st) + +cdef uint32_t pcg64_cm_uint32(void *st) nogil: + return pcg64_cm_next32(<pcg64_state *> st) + +cdef double pcg64_cm_double(void* st) nogil: + return uint64_to_double(pcg64_cm_next64(<pcg64_state *>st)) + +cdef class PCG64(BitGenerator): + """ + PCG64(seed=None) + + BitGenerator for the PCG-64 pseudo-random number generator. + + Parameters + ---------- + seed : {None, int, array_like[ints], SeedSequence}, optional + A seed to initialize the `BitGenerator`. If None, then fresh, + unpredictable entropy will be pulled from the OS. If an ``int`` or + ``array_like[ints]`` is passed, then it will be passed to + `SeedSequence` to derive the initial `BitGenerator` state. One may also + pass in a `SeedSequence` instance. + + Notes + ----- + PCG-64 is a 128-bit implementation of O'Neill's permutation congruential + generator ([1]_, [2]_). PCG-64 has a period of :math:`2^{128}` and supports + advancing an arbitrary number of steps as well as :math:`2^{127}` streams. + The specific member of the PCG family that we use is PCG XSL RR 128/64 + as described in the paper ([2]_). + + ``PCG64`` provides a capsule containing function pointers that produce + doubles, and unsigned 32 and 64- bit integers. These are not + directly consumable in Python and must be consumed by a ``Generator`` + or similar object that supports low-level access. + + Supports the method :meth:`advance` to advance the RNG an arbitrary number of + steps. The state of the PCG-64 RNG is represented by 2 128-bit unsigned + integers. + + **State and Seeding** + + The ``PCG64`` state vector consists of 2 unsigned 128-bit values, + which are represented externally as Python ints. One is the state of the + PRNG, which is advanced by a linear congruential generator (LCG). The + second is a fixed odd increment used in the LCG. + + The input seed is processed by `SeedSequence` to generate both values. The + increment is not independently settable. + + **Parallel Features** + + The preferred way to use a BitGenerator in parallel applications is to use + the `SeedSequence.spawn` method to obtain entropy values, and to use these + to generate new BitGenerators: + + >>> from numpy.random import Generator, PCG64, SeedSequence + >>> sg = SeedSequence(1234) + >>> rg = [Generator(PCG64(s)) for s in sg.spawn(10)] + + **Compatibility Guarantee** + + ``PCG64`` makes a guarantee that a fixed seed will always produce + the same random integer stream. + + References + ---------- + .. [1] `"PCG, A Family of Better Random Number Generators" + <http://www.pcg-random.org/>`_ + .. [2] O'Neill, Melissa E. `"PCG: A Family of Simple Fast Space-Efficient + Statistically Good Algorithms for Random Number Generation" + <https://www.cs.hmc.edu/tr/hmc-cs-2014-0905.pdf>`_ + """ + + cdef pcg64_state rng_state + cdef pcg64_random_t pcg64_random_state + + def __init__(self, seed=None): + BitGenerator.__init__(self, seed) + self.rng_state.pcg_state = &self.pcg64_random_state + + self._bitgen.state = <void *>&self.rng_state + self._bitgen.next_uint64 = &pcg64_uint64 + self._bitgen.next_uint32 = &pcg64_uint32 + self._bitgen.next_double = &pcg64_double + self._bitgen.next_raw = &pcg64_uint64 + # Seed the _bitgen + val = self._seed_seq.generate_state(4, np.uint64) + pcg64_set_seed(&self.rng_state, + <uint64_t *>np.PyArray_DATA(val), + (<uint64_t *>np.PyArray_DATA(val) + 2)) + self._reset_state_variables() + + cdef _reset_state_variables(self): + self.rng_state.has_uint32 = 0 + self.rng_state.uinteger = 0 + + cdef jump_inplace(self, jumps): + """ + Jump state in-place + Not part of public API + + Parameters + ---------- + jumps : integer, positive + Number of times to jump the state of the rng. + + Notes + ----- + The step size is phi-1 when multiplied by 2**128 where phi is the + golden ratio. + """ + step = 0x9e3779b97f4a7c15f39cc0605cedc835 + self.advance(step * int(jumps)) + + def jumped(self, jumps=1): + """ + jumped(jumps=1) + + Returns a new bit generator with the state jumped. + + Jumps the state as-if jumps * 210306068529402873165736369884012333109 + random numbers have been generated. + + Parameters + ---------- + jumps : integer, positive + Number of times to jump the state of the bit generator returned + + Returns + ------- + bit_generator : PCG64 + New instance of generator jumped iter times + + Notes + ----- + The step size is phi-1 when multiplied by 2**128 where phi is the + golden ratio. + """ + cdef PCG64 bit_generator + + bit_generator = self.__class__() + bit_generator.state = self.state + bit_generator.jump_inplace(jumps) + + return bit_generator + + @property + def state(self): + """ + Get or set the PRNG state + + Returns + ------- + state : dict + Dictionary containing the information required to describe the + state of the PRNG + """ + cdef np.ndarray state_vec + cdef int has_uint32 + cdef uint32_t uinteger + + # state_vec is state.high, state.low, inc.high, inc.low + state_vec = <np.ndarray>np.empty(4, dtype=np.uint64) + pcg64_get_state(&self.rng_state, + <uint64_t *>np.PyArray_DATA(state_vec), + &has_uint32, &uinteger) + state = int(state_vec[0]) * 2**64 + int(state_vec[1]) + inc = int(state_vec[2]) * 2**64 + int(state_vec[3]) + return {'bit_generator': self.__class__.__name__, + 'state': {'state': state, 'inc': inc}, + 'has_uint32': has_uint32, + 'uinteger': uinteger} + + @state.setter + def state(self, value): + cdef np.ndarray state_vec + cdef int has_uint32 + cdef uint32_t uinteger + if not isinstance(value, dict): + raise TypeError('state must be a dict') + bitgen = value.get('bit_generator', '') + if bitgen != self.__class__.__name__: + raise ValueError('state must be for a {0} ' + 'RNG'.format(self.__class__.__name__)) + state_vec = <np.ndarray>np.empty(4, dtype=np.uint64) + state_vec[0] = value['state']['state'] // 2 ** 64 + state_vec[1] = value['state']['state'] % 2 ** 64 + state_vec[2] = value['state']['inc'] // 2 ** 64 + state_vec[3] = value['state']['inc'] % 2 ** 64 + has_uint32 = value['has_uint32'] + uinteger = value['uinteger'] + pcg64_set_state(&self.rng_state, + <uint64_t *>np.PyArray_DATA(state_vec), + has_uint32, uinteger) + + def advance(self, delta): + """ + advance(delta) + + Advance the underlying RNG as-if delta draws have occurred. + + Parameters + ---------- + delta : integer, positive + Number of draws to advance the RNG. Must be less than the + size state variable in the underlying RNG. + + Returns + ------- + self : PCG64 + RNG advanced delta steps + + Notes + ----- + Advancing a RNG updates the underlying RNG state as-if a given + number of calls to the underlying RNG have been made. In general + there is not a one-to-one relationship between the number output + random values from a particular distribution and the number of + draws from the core RNG. This occurs for two reasons: + + * The random values are simulated using a rejection-based method + and so, on average, more than one value from the underlying + RNG is required to generate an single draw. + * The number of bits required to generate a simulated value + differs from the number of bits generated by the underlying + RNG. For example, two 16-bit integer values can be simulated + from a single draw of a 32-bit RNG. + + Advancing the RNG state resets any pre-computed random numbers. + This is required to ensure exact reproducibility. + """ + delta = wrap_int(delta, 128) + + cdef np.ndarray d = np.empty(2, dtype=np.uint64) + d[0] = delta // 2**64 + d[1] = delta % 2**64 + pcg64_advance(&self.rng_state, <uint64_t *>np.PyArray_DATA(d)) + self._reset_state_variables() + return self + + +cdef class PCG64DXSM(BitGenerator): + """ + PCG64DXSM(seed=None) + + BitGenerator for the PCG-64 DXSM pseudo-random number generator. + + Parameters + ---------- + seed : {None, int, array_like[ints], SeedSequence}, optional + A seed to initialize the `BitGenerator`. If None, then fresh, + unpredictable entropy will be pulled from the OS. If an ``int`` or + ``array_like[ints]`` is passed, then it will be passed to + `SeedSequence` to derive the initial `BitGenerator` state. One may also + pass in a `SeedSequence` instance. + + Notes + ----- + PCG-64 DXSM is a 128-bit implementation of O'Neill's permutation congruential + generator ([1]_, [2]_). PCG-64 DXSM has a period of :math:`2^{128}` and supports + advancing an arbitrary number of steps as well as :math:`2^{127}` streams. + The specific member of the PCG family that we use is PCG CM DXSM 128/64. It + differs from ``PCG64`` in that it uses the stronger DXSM output function, + a 64-bit "cheap multiplier" in the LCG, and outputs from the state before + advancing it rather than advance-then-output. + + ``PCG64DXSM`` provides a capsule containing function pointers that produce + doubles, and unsigned 32 and 64- bit integers. These are not + directly consumable in Python and must be consumed by a ``Generator`` + or similar object that supports low-level access. + + Supports the method :meth:`advance` to advance the RNG an arbitrary number of + steps. The state of the PCG-64 DXSM RNG is represented by 2 128-bit unsigned + integers. + + **State and Seeding** + + The ``PCG64DXSM`` state vector consists of 2 unsigned 128-bit values, + which are represented externally as Python ints. One is the state of the + PRNG, which is advanced by a linear congruential generator (LCG). The + second is a fixed odd increment used in the LCG. + + The input seed is processed by `SeedSequence` to generate both values. The + increment is not independently settable. + + **Parallel Features** + + The preferred way to use a BitGenerator in parallel applications is to use + the `SeedSequence.spawn` method to obtain entropy values, and to use these + to generate new BitGenerators: + + >>> from numpy.random import Generator, PCG64DXSM, SeedSequence + >>> sg = SeedSequence(1234) + >>> rg = [Generator(PCG64DXSM(s)) for s in sg.spawn(10)] + + **Compatibility Guarantee** + + ``PCG64DXSM`` makes a guarantee that a fixed seed will always produce + the same random integer stream. + + References + ---------- + .. [1] `"PCG, A Family of Better Random Number Generators" + <http://www.pcg-random.org/>`_ + .. [2] O'Neill, Melissa E. `"PCG: A Family of Simple Fast Space-Efficient + Statistically Good Algorithms for Random Number Generation" + <https://www.cs.hmc.edu/tr/hmc-cs-2014-0905.pdf>`_ + """ + cdef pcg64_state rng_state + cdef pcg64_random_t pcg64_random_state + + def __init__(self, seed=None): + BitGenerator.__init__(self, seed) + self.rng_state.pcg_state = &self.pcg64_random_state + + self._bitgen.state = <void *>&self.rng_state + self._bitgen.next_uint64 = &pcg64_cm_uint64 + self._bitgen.next_uint32 = &pcg64_cm_uint32 + self._bitgen.next_double = &pcg64_cm_double + self._bitgen.next_raw = &pcg64_cm_uint64 + # Seed the _bitgen + val = self._seed_seq.generate_state(4, np.uint64) + pcg64_set_seed(&self.rng_state, + <uint64_t *>np.PyArray_DATA(val), + (<uint64_t *>np.PyArray_DATA(val) + 2)) + self._reset_state_variables() + + cdef _reset_state_variables(self): + self.rng_state.has_uint32 = 0 + self.rng_state.uinteger = 0 + + cdef jump_inplace(self, jumps): + """ + Jump state in-place + Not part of public API + + Parameters + ---------- + jumps : integer, positive + Number of times to jump the state of the rng. + + Notes + ----- + The step size is phi-1 when multiplied by 2**128 where phi is the + golden ratio. + """ + step = 0x9e3779b97f4a7c15f39cc0605cedc835 + self.advance(step * int(jumps)) + + def jumped(self, jumps=1): + """ + jumped(jumps=1) + + Returns a new bit generator with the state jumped. + + Jumps the state as-if jumps * 210306068529402873165736369884012333109 + random numbers have been generated. + + Parameters + ---------- + jumps : integer, positive + Number of times to jump the state of the bit generator returned + + Returns + ------- + bit_generator : PCG64DXSM + New instance of generator jumped iter times + + Notes + ----- + The step size is phi-1 when multiplied by 2**128 where phi is the + golden ratio. + """ + cdef PCG64DXSM bit_generator + + bit_generator = self.__class__() + bit_generator.state = self.state + bit_generator.jump_inplace(jumps) + + return bit_generator + + @property + def state(self): + """ + Get or set the PRNG state + + Returns + ------- + state : dict + Dictionary containing the information required to describe the + state of the PRNG + """ + cdef np.ndarray state_vec + cdef int has_uint32 + cdef uint32_t uinteger + + # state_vec is state.high, state.low, inc.high, inc.low + state_vec = <np.ndarray>np.empty(4, dtype=np.uint64) + pcg64_get_state(&self.rng_state, + <uint64_t *>np.PyArray_DATA(state_vec), + &has_uint32, &uinteger) + state = int(state_vec[0]) * 2**64 + int(state_vec[1]) + inc = int(state_vec[2]) * 2**64 + int(state_vec[3]) + return {'bit_generator': self.__class__.__name__, + 'state': {'state': state, 'inc': inc}, + 'has_uint32': has_uint32, + 'uinteger': uinteger} + + @state.setter + def state(self, value): + cdef np.ndarray state_vec + cdef int has_uint32 + cdef uint32_t uinteger + if not isinstance(value, dict): + raise TypeError('state must be a dict') + bitgen = value.get('bit_generator', '') + if bitgen != self.__class__.__name__: + raise ValueError('state must be for a {0} ' + 'RNG'.format(self.__class__.__name__)) + state_vec = <np.ndarray>np.empty(4, dtype=np.uint64) + state_vec[0] = value['state']['state'] // 2 ** 64 + state_vec[1] = value['state']['state'] % 2 ** 64 + state_vec[2] = value['state']['inc'] // 2 ** 64 + state_vec[3] = value['state']['inc'] % 2 ** 64 + has_uint32 = value['has_uint32'] + uinteger = value['uinteger'] + pcg64_set_state(&self.rng_state, + <uint64_t *>np.PyArray_DATA(state_vec), + has_uint32, uinteger) + + def advance(self, delta): + """ + advance(delta) + + Advance the underlying RNG as-if delta draws have occurred. + + Parameters + ---------- + delta : integer, positive + Number of draws to advance the RNG. Must be less than the + size state variable in the underlying RNG. + + Returns + ------- + self : PCG64 + RNG advanced delta steps + + Notes + ----- + Advancing a RNG updates the underlying RNG state as-if a given + number of calls to the underlying RNG have been made. In general + there is not a one-to-one relationship between the number output + random values from a particular distribution and the number of + draws from the core RNG. This occurs for two reasons: + + * The random values are simulated using a rejection-based method + and so, on average, more than one value from the underlying + RNG is required to generate an single draw. + * The number of bits required to generate a simulated value + differs from the number of bits generated by the underlying + RNG. For example, two 16-bit integer values can be simulated + from a single draw of a 32-bit RNG. + + Advancing the RNG state resets any pre-computed random numbers. + This is required to ensure exact reproducibility. + """ + delta = wrap_int(delta, 128) + + cdef np.ndarray d = np.empty(2, dtype=np.uint64) + d[0] = delta // 2**64 + d[1] = delta % 2**64 + pcg64_cm_advance(&self.rng_state, <uint64_t *>np.PyArray_DATA(d)) + self._reset_state_variables() + return self + diff --git a/numpy/random/_philox.pyi b/numpy/random/_philox.pyi new file mode 100644 index 000000000000..dd1c5e6e9bab --- /dev/null +++ b/numpy/random/_philox.pyi @@ -0,0 +1,36 @@ +from typing import Any, Union, TypedDict + +from numpy import dtype, ndarray, uint64 +from numpy.random.bit_generator import BitGenerator, SeedSequence +from numpy.typing import _ArrayLikeInt_co + +class _PhiloxInternal(TypedDict): + counter: ndarray[Any, dtype[uint64]] + key: ndarray[Any, dtype[uint64]] + +class _PhiloxState(TypedDict): + bit_generator: str + state: _PhiloxInternal + buffer: ndarray[Any, dtype[uint64]] + buffer_pos: int + has_uint32: int + uinteger: int + +class Philox(BitGenerator): + def __init__( + self, + seed: Union[None, _ArrayLikeInt_co, SeedSequence] = ..., + counter: Union[None, _ArrayLikeInt_co] = ..., + key: Union[None, _ArrayLikeInt_co] = ..., + ) -> None: ... + @property + def state( + self, + ) -> _PhiloxState: ... + @state.setter + def state( + self, + value: _PhiloxState, + ) -> None: ... + def jumped(self, jumps: int = ...) -> Philox: ... + def advance(self, delta: int) -> Philox: ... diff --git a/numpy/random/_philox.pyx b/numpy/random/_philox.pyx new file mode 100644 index 000000000000..0fe8ebd7cd5f --- /dev/null +++ b/numpy/random/_philox.pyx @@ -0,0 +1,332 @@ +from cpython.pycapsule cimport PyCapsule_New + +import numpy as np +cimport numpy as np + +from libc.stdint cimport uint32_t, uint64_t +from ._common cimport uint64_to_double, int_to_array, wrap_int +from numpy.random cimport BitGenerator + +__all__ = ['Philox'] + +np.import_array() + +DEF PHILOX_BUFFER_SIZE=4 + +cdef extern from 'src/philox/philox.h': + struct s_r123array2x64: + uint64_t v[2] + + struct s_r123array4x64: + uint64_t v[4] + + ctypedef s_r123array4x64 r123array4x64 + ctypedef s_r123array2x64 r123array2x64 + + ctypedef r123array4x64 philox4x64_ctr_t + ctypedef r123array2x64 philox4x64_key_t + + struct s_philox_state: + philox4x64_ctr_t *ctr + philox4x64_key_t *key + int buffer_pos + uint64_t buffer[PHILOX_BUFFER_SIZE] + int has_uint32 + uint32_t uinteger + + ctypedef s_philox_state philox_state + + uint64_t philox_next64(philox_state *state) nogil + uint32_t philox_next32(philox_state *state) nogil + void philox_jump(philox_state *state) + void philox_advance(uint64_t *step, philox_state *state) + + +cdef uint64_t philox_uint64(void*st) nogil: + return philox_next64(<philox_state *> st) + +cdef uint32_t philox_uint32(void *st) nogil: + return philox_next32(<philox_state *> st) + +cdef double philox_double(void*st) nogil: + return uint64_to_double(philox_next64(<philox_state *> st)) + +cdef class Philox(BitGenerator): + """ + Philox(seed=None, counter=None, key=None) + + Container for the Philox (4x64) pseudo-random number generator. + + Parameters + ---------- + seed : {None, int, array_like[ints], SeedSequence}, optional + A seed to initialize the `BitGenerator`. If None, then fresh, + unpredictable entropy will be pulled from the OS. If an ``int`` or + ``array_like[ints]`` is passed, then it will be passed to + `SeedSequence` to derive the initial `BitGenerator` state. One may also + pass in a `SeedSequence` instance. + counter : {None, int, array_like}, optional + Counter to use in the Philox state. Can be either + a Python int (long in 2.x) in [0, 2**256) or a 4-element uint64 array. + If not provided, the RNG is initialized at 0. + key : {None, int, array_like}, optional + Key to use in the Philox state. Unlike ``seed``, the value in key is + directly set. Can be either a Python int in [0, 2**128) or a 2-element + uint64 array. `key` and ``seed`` cannot both be used. + + Attributes + ---------- + lock: threading.Lock + Lock instance that is shared so that the same bit git generator can + be used in multiple Generators without corrupting the state. Code that + generates values from a bit generator should hold the bit generator's + lock. + + Notes + ----- + Philox is a 64-bit PRNG that uses a counter-based design based on weaker + (and faster) versions of cryptographic functions [1]_. Instances using + different values of the key produce independent sequences. Philox has a + period of :math:`2^{256} - 1` and supports arbitrary advancing and jumping + the sequence in increments of :math:`2^{128}`. These features allow + multiple non-overlapping sequences to be generated. + + ``Philox`` provides a capsule containing function pointers that produce + doubles, and unsigned 32 and 64- bit integers. These are not + directly consumable in Python and must be consumed by a ``Generator`` + or similar object that supports low-level access. + + **State and Seeding** + + The ``Philox`` state vector consists of a 256-bit value encoded as + a 4-element uint64 array and a 128-bit value encoded as a 2-element uint64 + array. The former is a counter which is incremented by 1 for every 4 64-bit + randoms produced. The second is a key which determined the sequence + produced. Using different keys produces independent sequences. + + The input ``seed`` is processed by `SeedSequence` to generate the key. The + counter is set to 0. + + Alternately, one can omit the ``seed`` parameter and set the ``key`` and + ``counter`` directly. + + **Parallel Features** + + The preferred way to use a BitGenerator in parallel applications is to use + the `SeedSequence.spawn` method to obtain entropy values, and to use these + to generate new BitGenerators: + + >>> from numpy.random import Generator, Philox, SeedSequence + >>> sg = SeedSequence(1234) + >>> rg = [Generator(Philox(s)) for s in sg.spawn(10)] + + ``Philox`` can be used in parallel applications by calling the ``jumped`` + method to advances the state as-if :math:`2^{128}` random numbers have + been generated. Alternatively, ``advance`` can be used to advance the + counter for any positive step in [0, 2**256). When using ``jumped``, all + generators should be chained to ensure that the segments come from the same + sequence. + + >>> from numpy.random import Generator, Philox + >>> bit_generator = Philox(1234) + >>> rg = [] + >>> for _ in range(10): + ... rg.append(Generator(bit_generator)) + ... bit_generator = bit_generator.jumped() + + Alternatively, ``Philox`` can be used in parallel applications by using + a sequence of distinct keys where each instance uses different key. + + >>> key = 2**96 + 2**33 + 2**17 + 2**9 + >>> rg = [Generator(Philox(key=key+i)) for i in range(10)] + + **Compatibility Guarantee** + + ``Philox`` makes a guarantee that a fixed ``seed`` will always produce + the same random integer stream. + + Examples + -------- + >>> from numpy.random import Generator, Philox + >>> rg = Generator(Philox(1234)) + >>> rg.standard_normal() + 0.123 # random + + References + ---------- + .. [1] John K. Salmon, Mark A. Moraes, Ron O. Dror, and David E. Shaw, + "Parallel Random Numbers: As Easy as 1, 2, 3," Proceedings of + the International Conference for High Performance Computing, + Networking, Storage and Analysis (SC11), New York, NY: ACM, 2011. + """ + cdef philox_state rng_state + cdef philox4x64_key_t philox_key + cdef philox4x64_ctr_t philox_ctr + + def __init__(self, seed=None, counter=None, key=None): + if seed is not None and key is not None: + raise ValueError('seed and key cannot be both used') + BitGenerator.__init__(self, seed) + self.rng_state.ctr = &self.philox_ctr + self.rng_state.key = &self.philox_key + if key is not None: + key = int_to_array(key, 'key', 128, 64) + for i in range(2): + self.rng_state.key.v[i] = key[i] + # The seed sequence is invalid. + self._seed_seq = None + else: + key = self._seed_seq.generate_state(2, np.uint64) + for i in range(2): + self.rng_state.key.v[i] = key[i] + counter = 0 if counter is None else counter + counter = int_to_array(counter, 'counter', 256, 64) + for i in range(4): + self.rng_state.ctr.v[i] = counter[i] + + self._reset_state_variables() + + self._bitgen.state = <void *>&self.rng_state + self._bitgen.next_uint64 = &philox_uint64 + self._bitgen.next_uint32 = &philox_uint32 + self._bitgen.next_double = &philox_double + self._bitgen.next_raw = &philox_uint64 + + cdef _reset_state_variables(self): + self.rng_state.has_uint32 = 0 + self.rng_state.uinteger = 0 + self.rng_state.buffer_pos = PHILOX_BUFFER_SIZE + for i in range(PHILOX_BUFFER_SIZE): + self.rng_state.buffer[i] = 0 + + @property + def state(self): + """ + Get or set the PRNG state + + Returns + ------- + state : dict + Dictionary containing the information required to describe the + state of the PRNG + """ + ctr = np.empty(4, dtype=np.uint64) + key = np.empty(2, dtype=np.uint64) + buffer = np.empty(PHILOX_BUFFER_SIZE, dtype=np.uint64) + for i in range(4): + ctr[i] = self.rng_state.ctr.v[i] + if i < 2: + key[i] = self.rng_state.key.v[i] + for i in range(PHILOX_BUFFER_SIZE): + buffer[i] = self.rng_state.buffer[i] + + state = {'counter': ctr, 'key': key} + return {'bit_generator': self.__class__.__name__, + 'state': state, + 'buffer': buffer, + 'buffer_pos': self.rng_state.buffer_pos, + 'has_uint32': self.rng_state.has_uint32, + 'uinteger': self.rng_state.uinteger} + + @state.setter + def state(self, value): + if not isinstance(value, dict): + raise TypeError('state must be a dict') + bitgen = value.get('bit_generator', '') + if bitgen != self.__class__.__name__: + raise ValueError('state must be for a {0} ' + 'PRNG'.format(self.__class__.__name__)) + for i in range(4): + self.rng_state.ctr.v[i] = <uint64_t> value['state']['counter'][i] + if i < 2: + self.rng_state.key.v[i] = <uint64_t> value['state']['key'][i] + for i in range(PHILOX_BUFFER_SIZE): + self.rng_state.buffer[i] = <uint64_t> value['buffer'][i] + + self.rng_state.has_uint32 = value['has_uint32'] + self.rng_state.uinteger = value['uinteger'] + self.rng_state.buffer_pos = value['buffer_pos'] + + cdef jump_inplace(self, iter): + """ + Jump state in-place + + Not part of public API + + Parameters + ---------- + iter : integer, positive + Number of times to jump the state of the rng. + """ + self.advance(iter * int(2 ** 128)) + + def jumped(self, jumps=1): + """ + jumped(jumps=1) + + Returns a new bit generator with the state jumped + + The state of the returned big generator is jumped as-if + 2**(128 * jumps) random numbers have been generated. + + Parameters + ---------- + jumps : integer, positive + Number of times to jump the state of the bit generator returned + + Returns + ------- + bit_generator : Philox + New instance of generator jumped iter times + """ + cdef Philox bit_generator + + bit_generator = self.__class__() + bit_generator.state = self.state + bit_generator.jump_inplace(jumps) + + return bit_generator + + def advance(self, delta): + """ + advance(delta) + + Advance the underlying RNG as-if delta draws have occurred. + + Parameters + ---------- + delta : integer, positive + Number of draws to advance the RNG. Must be less than the + size state variable in the underlying RNG. + + Returns + ------- + self : Philox + RNG advanced delta steps + + Notes + ----- + Advancing a RNG updates the underlying RNG state as-if a given + number of calls to the underlying RNG have been made. In general + there is not a one-to-one relationship between the number output + random values from a particular distribution and the number of + draws from the core RNG. This occurs for two reasons: + + * The random values are simulated using a rejection-based method + and so, on average, more than one value from the underlying + RNG is required to generate an single draw. + * The number of bits required to generate a simulated value + differs from the number of bits generated by the underlying + RNG. For example, two 16-bit integer values can be simulated + from a single draw of a 32-bit RNG. + + Advancing the RNG state resets any pre-computed random numbers. + This is required to ensure exact reproducibility. + """ + delta = wrap_int(delta, 256) + + cdef np.ndarray delta_a + delta_a = int_to_array(delta, 'step', 256, 64) + philox_advance(<uint64_t *> delta_a.data, &self.rng_state) + self._reset_state_variables() + return self diff --git a/numpy/random/_pickle.py b/numpy/random/_pickle.py new file mode 100644 index 000000000000..a32f64f4a3d3 --- /dev/null +++ b/numpy/random/_pickle.py @@ -0,0 +1,83 @@ +from .mtrand import RandomState +from ._philox import Philox +from ._pcg64 import PCG64, PCG64DXSM +from ._sfc64 import SFC64 + +from ._generator import Generator +from ._mt19937 import MT19937 + +BitGenerators = {'MT19937': MT19937, + 'PCG64': PCG64, + 'PCG64DXSM': PCG64DXSM, + 'Philox': Philox, + 'SFC64': SFC64, + } + + +def __generator_ctor(bit_generator_name='MT19937'): + """ + Pickling helper function that returns a Generator object + + Parameters + ---------- + bit_generator_name : str + String containing the core BitGenerator + + Returns + ------- + rg: Generator + Generator using the named core BitGenerator + """ + if bit_generator_name in BitGenerators: + bit_generator = BitGenerators[bit_generator_name] + else: + raise ValueError(str(bit_generator_name) + ' is not a known ' + 'BitGenerator module.') + + return Generator(bit_generator()) + + +def __bit_generator_ctor(bit_generator_name='MT19937'): + """ + Pickling helper function that returns a bit generator object + + Parameters + ---------- + bit_generator_name : str + String containing the name of the BitGenerator + + Returns + ------- + bit_generator: BitGenerator + BitGenerator instance + """ + if bit_generator_name in BitGenerators: + bit_generator = BitGenerators[bit_generator_name] + else: + raise ValueError(str(bit_generator_name) + ' is not a known ' + 'BitGenerator module.') + + return bit_generator() + + +def __randomstate_ctor(bit_generator_name='MT19937'): + """ + Pickling helper function that returns a legacy RandomState-like object + + Parameters + ---------- + bit_generator_name : str + String containing the core BitGenerator + + Returns + ------- + rs: RandomState + Legacy RandomState using the named core BitGenerator + """ + if bit_generator_name in BitGenerators: + bit_generator = BitGenerators[bit_generator_name] + else: + raise ValueError(str(bit_generator_name) + ' is not a known ' + 'BitGenerator module.') + + return RandomState(bit_generator()) diff --git a/numpy/random/_sfc64.pyi b/numpy/random/_sfc64.pyi new file mode 100644 index 000000000000..94d11a210fe6 --- /dev/null +++ b/numpy/random/_sfc64.pyi @@ -0,0 +1,28 @@ +from typing import Any, Union, TypedDict + +from numpy import dtype as dtype +from numpy import ndarray as ndarray +from numpy import uint64 +from numpy.random.bit_generator import BitGenerator, SeedSequence +from numpy.typing import _ArrayLikeInt_co + +class _SFC64Internal(TypedDict): + state: ndarray[Any, dtype[uint64]] + +class _SFC64State(TypedDict): + bit_generator: str + state: _SFC64Internal + has_uint32: int + uinteger: int + +class SFC64(BitGenerator): + def __init__(self, seed: Union[None, _ArrayLikeInt_co, SeedSequence] = ...) -> None: ... + @property + def state( + self, + ) -> _SFC64State: ... + @state.setter + def state( + self, + value: _SFC64State, + ) -> None: ... diff --git a/numpy/random/_sfc64.pyx b/numpy/random/_sfc64.pyx new file mode 100644 index 000000000000..1daee34f8635 --- /dev/null +++ b/numpy/random/_sfc64.pyx @@ -0,0 +1,144 @@ +import numpy as np +cimport numpy as np + +from libc.stdint cimport uint32_t, uint64_t +from ._common cimport uint64_to_double +from numpy.random cimport BitGenerator + +__all__ = ['SFC64'] + +cdef extern from "src/sfc64/sfc64.h": + struct s_sfc64_state: + uint64_t s[4] + int has_uint32 + uint32_t uinteger + + ctypedef s_sfc64_state sfc64_state + uint64_t sfc64_next64(sfc64_state *state) nogil + uint32_t sfc64_next32(sfc64_state *state) nogil + void sfc64_set_seed(sfc64_state *state, uint64_t *seed) + void sfc64_get_state(sfc64_state *state, uint64_t *state_arr, int *has_uint32, uint32_t *uinteger) + void sfc64_set_state(sfc64_state *state, uint64_t *state_arr, int has_uint32, uint32_t uinteger) + + +cdef uint64_t sfc64_uint64(void* st) nogil: + return sfc64_next64(<sfc64_state *>st) + +cdef uint32_t sfc64_uint32(void *st) nogil: + return sfc64_next32(<sfc64_state *> st) + +cdef double sfc64_double(void* st) nogil: + return uint64_to_double(sfc64_next64(<sfc64_state *>st)) + + +cdef class SFC64(BitGenerator): + """ + SFC64(seed=None) + + BitGenerator for Chris Doty-Humphrey's Small Fast Chaotic PRNG. + + Parameters + ---------- + seed : {None, int, array_like[ints], SeedSequence}, optional + A seed to initialize the `BitGenerator`. If None, then fresh, + unpredictable entropy will be pulled from the OS. If an ``int`` or + ``array_like[ints]`` is passed, then it will be passed to + `SeedSequence` to derive the initial `BitGenerator` state. One may also + pass in a `SeedSequence` instance. + + Notes + ----- + ``SFC64`` is a 256-bit implementation of Chris Doty-Humphrey's Small Fast + Chaotic PRNG ([1]_). ``SFC64`` has a few different cycles that one might be + on, depending on the seed; the expected period will be about + :math:`2^{255}` ([2]_). ``SFC64`` incorporates a 64-bit counter which means + that the absolute minimum cycle length is :math:`2^{64}` and that distinct + seeds will not run into each other for at least :math:`2^{64}` iterations. + + ``SFC64`` provides a capsule containing function pointers that produce + doubles, and unsigned 32 and 64- bit integers. These are not + directly consumable in Python and must be consumed by a ``Generator`` + or similar object that supports low-level access. + + **State and Seeding** + + The ``SFC64`` state vector consists of 4 unsigned 64-bit values. The last + is a 64-bit counter that increments by 1 each iteration. + + The input seed is processed by `SeedSequence` to generate the first + 3 values, then the ``SFC64`` algorithm is iterated a small number of times + to mix. + + **Compatibility Guarantee** + + ``SFC64`` makes a guarantee that a fixed seed will always produce the same + random integer stream. + + References + ---------- + .. [1] `"PractRand" + <http://pracrand.sourceforge.net/RNG_engines.txt>`_ + .. [2] `"Random Invertible Mapping Statistics" + <http://www.pcg-random.org/posts/random-invertible-mapping-statistics.html>`_ + """ + + cdef sfc64_state rng_state + + def __init__(self, seed=None): + BitGenerator.__init__(self, seed) + self._bitgen.state = <void *>&self.rng_state + self._bitgen.next_uint64 = &sfc64_uint64 + self._bitgen.next_uint32 = &sfc64_uint32 + self._bitgen.next_double = &sfc64_double + self._bitgen.next_raw = &sfc64_uint64 + # Seed the _bitgen + val = self._seed_seq.generate_state(3, np.uint64) + sfc64_set_seed(&self.rng_state, <uint64_t*>np.PyArray_DATA(val)) + self._reset_state_variables() + + cdef _reset_state_variables(self): + self.rng_state.has_uint32 = 0 + self.rng_state.uinteger = 0 + + @property + def state(self): + """ + Get or set the PRNG state + + Returns + ------- + state : dict + Dictionary containing the information required to describe the + state of the PRNG + """ + cdef np.ndarray state_vec + cdef int has_uint32 + cdef uint32_t uinteger + + state_vec = <np.ndarray>np.empty(4, dtype=np.uint64) + sfc64_get_state(&self.rng_state, + <uint64_t *>np.PyArray_DATA(state_vec), + &has_uint32, &uinteger) + return {'bit_generator': self.__class__.__name__, + 'state': {'state': state_vec}, + 'has_uint32': has_uint32, + 'uinteger': uinteger} + + @state.setter + def state(self, value): + cdef np.ndarray state_vec + cdef int has_uint32 + cdef uint32_t uinteger + if not isinstance(value, dict): + raise TypeError('state must be a dict') + bitgen = value.get('bit_generator', '') + if bitgen != self.__class__.__name__: + raise ValueError('state must be for a {0} ' + 'RNG'.format(self.__class__.__name__)) + state_vec = <np.ndarray>np.empty(4, dtype=np.uint64) + state_vec[:] = value['state']['state'] + has_uint32 = value['has_uint32'] + uinteger = value['uinteger'] + sfc64_set_state(&self.rng_state, + <uint64_t *>np.PyArray_DATA(state_vec), + has_uint32, uinteger) diff --git a/numpy/random/bit_generator.pxd b/numpy/random/bit_generator.pxd new file mode 100644 index 000000000000..dfa7d0a71c08 --- /dev/null +++ b/numpy/random/bit_generator.pxd @@ -0,0 +1,35 @@ +cimport numpy as np +from libc.stdint cimport uint32_t, uint64_t + +cdef extern from "numpy/random/bitgen.h": + struct bitgen: + void *state + uint64_t (*next_uint64)(void *st) nogil + uint32_t (*next_uint32)(void *st) nogil + double (*next_double)(void *st) nogil + uint64_t (*next_raw)(void *st) nogil + + ctypedef bitgen bitgen_t + +cdef class BitGenerator(): + cdef readonly object _seed_seq + cdef readonly object lock + cdef bitgen_t _bitgen + cdef readonly object _ctypes + cdef readonly object _cffi + cdef readonly object capsule + + +cdef class SeedSequence(): + cdef readonly object entropy + cdef readonly tuple spawn_key + cdef readonly Py_ssize_t pool_size + cdef readonly object pool + cdef readonly uint32_t n_children_spawned + + cdef mix_entropy(self, np.ndarray[np.npy_uint32, ndim=1] mixer, + np.ndarray[np.npy_uint32, ndim=1] entropy_array) + cdef get_assembled_entropy(self) + +cdef class SeedlessSequence(): + pass diff --git a/numpy/random/bit_generator.pyi b/numpy/random/bit_generator.pyi new file mode 100644 index 000000000000..fa2f1ab12c90 --- /dev/null +++ b/numpy/random/bit_generator.pyi @@ -0,0 +1,116 @@ +import abc +from threading import Lock +from typing import ( + Any, + Callable, + Dict, + List, + Mapping, + NamedTuple, + Optional, + Sequence, + Tuple, + Type, + TypedDict, + TypeVar, + Union, + overload, + Literal, +) + +from numpy import dtype, ndarray, uint32, uint64 +from numpy.typing import _ArrayLikeInt_co, _ShapeLike, _SupportsDType, _UInt32Codes, _UInt64Codes + +_T = TypeVar("_T") + +_DTypeLikeUint32 = Union[ + dtype[uint32], + _SupportsDType[dtype[uint32]], + Type[uint32], + _UInt32Codes, +] +_DTypeLikeUint64 = Union[ + dtype[uint64], + _SupportsDType[dtype[uint64]], + Type[uint64], + _UInt64Codes, +] + +class _SeedSeqState(TypedDict): + entropy: Union[None, int, Sequence[int]] + spawn_key: Tuple[int, ...] + pool_size: int + n_children_spawned: int + +class _Interface(NamedTuple): + state_address: Any + state: Any + next_uint64: Any + next_uint32: Any + next_double: Any + bit_generator: Any + +class ISeedSequence(abc.ABC): + @abc.abstractmethod + def generate_state( + self, n_words: int, dtype: Union[_DTypeLikeUint32, _DTypeLikeUint64] = ... + ) -> ndarray[Any, dtype[Union[uint32, uint64]]]: ... + +class ISpawnableSeedSequence(ISeedSequence): + @abc.abstractmethod + def spawn(self: _T, n_children: int) -> List[_T]: ... + +class SeedlessSeedSequence(ISpawnableSeedSequence): + def generate_state( + self, n_words: int, dtype: Union[_DTypeLikeUint32, _DTypeLikeUint64] = ... + ) -> ndarray[Any, dtype[Union[uint32, uint64]]]: ... + def spawn(self: _T, n_children: int) -> List[_T]: ... + +class SeedSequence(ISpawnableSeedSequence): + entropy: Union[None, int, Sequence[int]] + spawn_key: Tuple[int, ...] + pool_size: int + n_children_spawned: int + pool: ndarray[Any, dtype[uint32]] + def __init__( + self, + entropy: Union[None, int, Sequence[int], _ArrayLikeInt_co] = ..., + *, + spawn_key: Sequence[int] = ..., + pool_size: int = ..., + n_children_spawned: int = ..., + ) -> None: ... + def __repr__(self) -> str: ... + @property + def state( + self, + ) -> _SeedSeqState: ... + def generate_state( + self, n_words: int, dtype: Union[_DTypeLikeUint32, _DTypeLikeUint64] = ... + ) -> ndarray[Any, dtype[Union[uint32, uint64]]]: ... + def spawn(self, n_children: int) -> List[SeedSequence]: ... + +class BitGenerator(abc.ABC): + lock: Lock + def __init__(self, seed: Union[None, _ArrayLikeInt_co, SeedSequence] = ...) -> None: ... + def __getstate__(self) -> Dict[str, Any]: ... + def __setstate__(self, state: Dict[str, Any]) -> None: ... + def __reduce__( + self, + ) -> Tuple[Callable[[str], BitGenerator], Tuple[str], Tuple[Dict[str, Any]]]: ... + @abc.abstractmethod + @property + def state(self) -> Mapping[str, Any]: ... + @state.setter + def state(self, value: Mapping[str, Any]) -> None: ... + @overload + def random_raw(self, size: None = ..., output: Literal[True] = ...) -> int: ... # type: ignore[misc] + @overload + def random_raw(self, size: _ShapeLike = ..., output: Literal[True] = ...) -> ndarray[Any, dtype[uint64]]: ... # type: ignore[misc] + @overload + def random_raw(self, size: Optional[_ShapeLike] = ..., output: Literal[False] = ...) -> None: ... # type: ignore[misc] + def _benchmark(self, cnt: int, method: str = ...) -> None: ... + @property + def ctypes(self) -> _Interface: ... + @property + def cffi(self) -> _Interface: ... diff --git a/numpy/random/bit_generator.pyx b/numpy/random/bit_generator.pyx new file mode 100644 index 000000000000..123d77b40e2e --- /dev/null +++ b/numpy/random/bit_generator.pyx @@ -0,0 +1,632 @@ +""" +BitGenerator base class and SeedSequence used to seed the BitGenerators. + +SeedSequence is derived from Melissa E. O'Neill's C++11 `std::seed_seq` +implementation, as it has a lot of nice properties that we want. + +https://gist.github.com/imneme/540829265469e673d045 +http://www.pcg-random.org/posts/developing-a-seed_seq-alternative.html + +The MIT License (MIT) + +Copyright (c) 2015 Melissa E. O'Neill +Copyright (c) 2019 NumPy Developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + +import abc +import sys +from itertools import cycle +import re + +try: + from secrets import randbits +except ImportError: + # secrets unavailable on python 3.5 and before + from random import SystemRandom + randbits = SystemRandom().getrandbits + +from threading import Lock + +from cpython.pycapsule cimport PyCapsule_New + +import numpy as np +cimport numpy as np + +from ._common cimport (random_raw, benchmark, prepare_ctypes, prepare_cffi) + +__all__ = ['SeedSequence', 'BitGenerator'] + +np.import_array() + +DECIMAL_RE = re.compile(r'[0-9]+') + +cdef uint32_t DEFAULT_POOL_SIZE = 4 # Appears also in docstring for pool_size +cdef uint32_t INIT_A = 0x43b0d7e5 +cdef uint32_t MULT_A = 0x931e8875 +cdef uint32_t INIT_B = 0x8b51f9dd +cdef uint32_t MULT_B = 0x58f38ded +cdef uint32_t MIX_MULT_L = 0xca01f9dd +cdef uint32_t MIX_MULT_R = 0x4973f715 +cdef uint32_t XSHIFT = np.dtype(np.uint32).itemsize * 8 // 2 +cdef uint32_t MASK32 = 0xFFFFFFFF + +def _int_to_uint32_array(n): + arr = [] + if n < 0: + raise ValueError("expected non-negative integer") + if n == 0: + arr.append(np.uint32(n)) + if isinstance(n, np.unsignedinteger): + # Cannot do n & MASK32, convert to python int + n = int(n) + while n > 0: + arr.append(np.uint32(n & MASK32)) + n //= (2**32) + return np.array(arr, dtype=np.uint32) + +def _coerce_to_uint32_array(x): + """ Coerce an input to a uint32 array. + + If a `uint32` array, pass it through directly. + If a non-negative integer, then break it up into `uint32` words, lowest + bits first. + If a string starting with "0x", then interpret as a hex integer, as above. + If a string of decimal digits, interpret as a decimal integer, as above. + If a sequence of ints or strings, interpret each element as above and + concatenate. + + Note that the handling of `int64` or `uint64` arrays are not just + straightforward views as `uint32` arrays. If an element is small enough to + fit into a `uint32`, then it will only take up one `uint32` element in the + output. This is to make sure that the interpretation of a sequence of + integers is the same regardless of numpy's default integer type, which + differs on different platforms. + + Parameters + ---------- + x : int, str, sequence of int or str + + Returns + ------- + seed_array : uint32 array + + Examples + -------- + >>> import numpy as np + >>> from numpy.random.bit_generator import _coerce_to_uint32_array + >>> _coerce_to_uint32_array(12345) + array([12345], dtype=uint32) + >>> _coerce_to_uint32_array('12345') + array([12345], dtype=uint32) + >>> _coerce_to_uint32_array('0x12345') + array([74565], dtype=uint32) + >>> _coerce_to_uint32_array([12345, '67890']) + array([12345, 67890], dtype=uint32) + >>> _coerce_to_uint32_array(np.array([12345, 67890], dtype=np.uint32)) + array([12345, 67890], dtype=uint32) + >>> _coerce_to_uint32_array(np.array([12345, 67890], dtype=np.int64)) + array([12345, 67890], dtype=uint32) + >>> _coerce_to_uint32_array([12345, 0x10deadbeef, 67890, 0xdeadbeef]) + array([ 12345, 3735928559, 16, 67890, 3735928559], + dtype=uint32) + >>> _coerce_to_uint32_array(1234567890123456789012345678901234567890) + array([3460238034, 2898026390, 3235640248, 2697535605, 3], + dtype=uint32) + """ + if isinstance(x, np.ndarray) and x.dtype == np.dtype(np.uint32): + return x.copy() + elif isinstance(x, str): + if x.startswith('0x'): + x = int(x, base=16) + elif DECIMAL_RE.match(x): + x = int(x) + else: + raise ValueError("unrecognized seed string") + if isinstance(x, (int, np.integer)): + return _int_to_uint32_array(x) + elif isinstance(x, (float, np.inexact)): + raise TypeError('seed must be integer') + else: + if len(x) == 0: + return np.array([], dtype=np.uint32) + # Should be a sequence of interpretable-as-ints. Convert each one to + # a uint32 array and concatenate. + subseqs = [_coerce_to_uint32_array(v) for v in x] + return np.concatenate(subseqs) + + +cdef uint32_t hashmix(uint32_t value, uint32_t * hash_const): + # We are modifying the multiplier as we go along, so it is input-output + value ^= hash_const[0] + hash_const[0] *= MULT_A + value *= hash_const[0] + value ^= value >> XSHIFT + return value + +cdef uint32_t mix(uint32_t x, uint32_t y): + cdef uint32_t result = (MIX_MULT_L * x - MIX_MULT_R * y) + result ^= result >> XSHIFT + return result + + +class ISeedSequence(abc.ABC): + """ + Abstract base class for seed sequences. + + ``BitGenerator`` implementations should treat any object that adheres to + this interface as a seed sequence. + + See Also + -------- + SeedSequence, SeedlessSeedSequence + """ + + @abc.abstractmethod + def generate_state(self, n_words, dtype=np.uint32): + """ + generate_state(n_words, dtype=np.uint32) + + Return the requested number of words for PRNG seeding. + + A BitGenerator should call this method in its constructor with + an appropriate `n_words` parameter to properly seed itself. + + Parameters + ---------- + n_words : int + dtype : np.uint32 or np.uint64, optional + The size of each word. This should only be either `uint32` or + `uint64`. Strings (`'uint32'`, `'uint64'`) are fine. Note that + requesting `uint64` will draw twice as many bits as `uint32` for + the same `n_words`. This is a convenience for `BitGenerator`s that + express their states as `uint64` arrays. + + Returns + ------- + state : uint32 or uint64 array, shape=(n_words,) + """ + + +class ISpawnableSeedSequence(ISeedSequence): + """ + Abstract base class for seed sequences that can spawn. + """ + + @abc.abstractmethod + def spawn(self, n_children): + """ + spawn(n_children) + + Spawn a number of child `SeedSequence` s by extending the + `spawn_key`. + + Parameters + ---------- + n_children : int + + Returns + ------- + seqs : list of `SeedSequence` s + """ + + +cdef class SeedlessSeedSequence(): + """ + A seed sequence for BitGenerators with no need for seed state. + + See Also + -------- + SeedSequence, ISeedSequence + """ + + def generate_state(self, n_words, dtype=np.uint32): + raise NotImplementedError('seedless SeedSequences cannot generate state') + + def spawn(self, n_children): + return [self] * n_children + + +# We cannot directly subclass a `cdef class` type from an `ABC` in Cython, so +# we must register it after the fact. +ISpawnableSeedSequence.register(SeedlessSeedSequence) + + +cdef class SeedSequence(): + """ + SeedSequence(entropy=None, *, spawn_key=(), pool_size=4) + + SeedSequence mixes sources of entropy in a reproducible way to set the + initial state for independent and very probably non-overlapping + BitGenerators. + + Once the SeedSequence is instantiated, you can call the `generate_state` + method to get an appropriately sized seed. Calling `spawn(n) <spawn>` will + create ``n`` SeedSequences that can be used to seed independent + BitGenerators, i.e. for different threads. + + Parameters + ---------- + entropy : {None, int, sequence[int]}, optional + The entropy for creating a `SeedSequence`. + spawn_key : {(), sequence[int]}, optional + A third source of entropy, used internally when calling + `SeedSequence.spawn` + pool_size : {int}, optional + Size of the pooled entropy to store. Default is 4 to give a 128-bit + entropy pool. 8 (for 256 bits) is another reasonable choice if working + with larger PRNGs, but there is very little to be gained by selecting + another value. + n_children_spawned : {int}, optional + The number of children already spawned. Only pass this if + reconstructing a `SeedSequence` from a serialized form. + + Notes + ----- + + Best practice for achieving reproducible bit streams is to use + the default ``None`` for the initial entropy, and then use + `SeedSequence.entropy` to log/pickle the `entropy` for reproducibility: + + >>> sq1 = np.random.SeedSequence() + >>> sq1.entropy + 243799254704924441050048792905230269161 # random + >>> sq2 = np.random.SeedSequence(sq1.entropy) + >>> np.all(sq1.generate_state(10) == sq2.generate_state(10)) + True + """ + + def __init__(self, entropy=None, *, spawn_key=(), + pool_size=DEFAULT_POOL_SIZE, n_children_spawned=0): + if pool_size < DEFAULT_POOL_SIZE: + raise ValueError("The size of the entropy pool should be at least " + f"{DEFAULT_POOL_SIZE}") + if entropy is None: + entropy = randbits(pool_size * 32) + elif not isinstance(entropy, (int, np.integer, list, tuple, range, + np.ndarray)): + raise TypeError('SeedSequence expects int or sequence of ints for ' + 'entropy not {}'.format(entropy)) + self.entropy = entropy + self.spawn_key = tuple(spawn_key) + self.pool_size = pool_size + self.n_children_spawned = n_children_spawned + + self.pool = np.zeros(pool_size, dtype=np.uint32) + self.mix_entropy(self.pool, self.get_assembled_entropy()) + + def __repr__(self): + lines = [ + f'{type(self).__name__}(', + f' entropy={self.entropy!r},', + ] + # Omit some entries if they are left as the defaults in order to + # simplify things. + if self.spawn_key: + lines.append(f' spawn_key={self.spawn_key!r},') + if self.pool_size != DEFAULT_POOL_SIZE: + lines.append(f' pool_size={self.pool_size!r},') + if self.n_children_spawned != 0: + lines.append(f' n_children_spawned={self.n_children_spawned!r},') + lines.append(')') + text = '\n'.join(lines) + return text + + @property + def state(self): + return {k:getattr(self, k) for k in + ['entropy', 'spawn_key', 'pool_size', + 'n_children_spawned'] + if getattr(self, k) is not None} + + cdef mix_entropy(self, np.ndarray[np.npy_uint32, ndim=1] mixer, + np.ndarray[np.npy_uint32, ndim=1] entropy_array): + """ Mix in the given entropy to mixer. + + Parameters + ---------- + mixer : 1D uint32 array, modified in-place + entropy_array : 1D uint32 array + """ + cdef uint32_t hash_const[1] + hash_const[0] = INIT_A + + # Add in the entropy up to the pool size. + for i in range(len(mixer)): + if i < len(entropy_array): + mixer[i] = hashmix(entropy_array[i], hash_const) + else: + # Our pool size is bigger than our entropy, so just keep + # running the hash out. + mixer[i] = hashmix(0, hash_const) + + # Mix all bits together so late bits can affect earlier bits. + for i_src in range(len(mixer)): + for i_dst in range(len(mixer)): + if i_src != i_dst: + mixer[i_dst] = mix(mixer[i_dst], + hashmix(mixer[i_src], hash_const)) + + # Add any remaining entropy, mixing each new entropy word with each + # pool word. + for i_src in range(len(mixer), len(entropy_array)): + for i_dst in range(len(mixer)): + mixer[i_dst] = mix(mixer[i_dst], + hashmix(entropy_array[i_src], hash_const)) + + cdef get_assembled_entropy(self): + """ Convert and assemble all entropy sources into a uniform uint32 + array. + + Returns + ------- + entropy_array : 1D uint32 array + """ + # Convert run-entropy and the spawn key into uint32 + # arrays and concatenate them. + + # We MUST have at least some run-entropy. The others are optional. + assert self.entropy is not None + run_entropy = _coerce_to_uint32_array(self.entropy) + spawn_entropy = _coerce_to_uint32_array(self.spawn_key) + if len(spawn_entropy) > 0 and len(run_entropy) < self.pool_size: + # Explicitly fill out the entropy with 0s to the pool size to avoid + # conflict with spawn keys. We changed this in 1.19.0 to fix + # gh-16539. In order to preserve stream-compatibility with + # unspawned SeedSequences with small entropy inputs, we only do + # this when a spawn_key is specified. + diff = self.pool_size - len(run_entropy) + run_entropy = np.concatenate( + [run_entropy, np.zeros(diff, dtype=np.uint32)]) + entropy_array = np.concatenate([run_entropy, spawn_entropy]) + return entropy_array + + @np.errstate(over='ignore') + def generate_state(self, n_words, dtype=np.uint32): + """ + generate_state(n_words, dtype=np.uint32) + + Return the requested number of words for PRNG seeding. + + A BitGenerator should call this method in its constructor with + an appropriate `n_words` parameter to properly seed itself. + + Parameters + ---------- + n_words : int + dtype : np.uint32 or np.uint64, optional + The size of each word. This should only be either `uint32` or + `uint64`. Strings (`'uint32'`, `'uint64'`) are fine. Note that + requesting `uint64` will draw twice as many bits as `uint32` for + the same `n_words`. This is a convenience for `BitGenerator`s that + express their states as `uint64` arrays. + + Returns + ------- + state : uint32 or uint64 array, shape=(n_words,) + """ + cdef uint32_t hash_const = INIT_B + cdef uint32_t data_val + + out_dtype = np.dtype(dtype) + if out_dtype == np.dtype(np.uint32): + pass + elif out_dtype == np.dtype(np.uint64): + n_words *= 2 + else: + raise ValueError("only support uint32 or uint64") + state = np.zeros(n_words, dtype=np.uint32) + src_cycle = cycle(self.pool) + for i_dst in range(n_words): + data_val = next(src_cycle) + data_val ^= hash_const + hash_const *= MULT_B + data_val *= hash_const + data_val ^= data_val >> XSHIFT + state[i_dst] = data_val + if out_dtype == np.dtype(np.uint64): + # For consistency across different endiannesses, view first as + # little-endian then convert the values to the native endianness. + state = state.astype('<u4').view('<u8').astype(np.uint64) + return state + + def spawn(self, n_children): + """ + spawn(n_children) + + Spawn a number of child `SeedSequence` s by extending the + `spawn_key`. + + Parameters + ---------- + n_children : int + + Returns + ------- + seqs : list of `SeedSequence` s + """ + cdef uint32_t i + + seqs = [] + for i in range(self.n_children_spawned, + self.n_children_spawned + n_children): + seqs.append(type(self)( + self.entropy, + spawn_key=self.spawn_key + (i,), + pool_size=self.pool_size, + )) + self.n_children_spawned += n_children + return seqs + + +ISpawnableSeedSequence.register(SeedSequence) + + +cdef class BitGenerator(): + """ + BitGenerator(seed=None) + + Base Class for generic BitGenerators, which provide a stream + of random bits based on different algorithms. Must be overridden. + + Parameters + ---------- + seed : {None, int, array_like[ints], SeedSequence}, optional + A seed to initialize the `BitGenerator`. If None, then fresh, + unpredictable entropy will be pulled from the OS. If an ``int`` or + ``array_like[ints]`` is passed, then it will be passed to + ~`numpy.random.SeedSequence` to derive the initial `BitGenerator` state. + One may also pass in a `SeedSequence` instance. + + Attributes + ---------- + lock : threading.Lock + Lock instance that is shared so that the same BitGenerator can + be used in multiple Generators without corrupting the state. Code that + generates values from a bit generator should hold the bit generator's + lock. + + See Also + -------- + SeedSequence + """ + + def __init__(self, seed=None): + self.lock = Lock() + self._bitgen.state = <void *>0 + if type(self) is BitGenerator: + raise NotImplementedError('BitGenerator is a base class and cannot be instantized') + + self._ctypes = None + self._cffi = None + + cdef const char *name = "BitGenerator" + self.capsule = PyCapsule_New(<void *>&self._bitgen, name, NULL) + if not isinstance(seed, ISeedSequence): + seed = SeedSequence(seed) + self._seed_seq = seed + + # Pickling support: + def __getstate__(self): + return self.state + + def __setstate__(self, state): + self.state = state + + def __reduce__(self): + from ._pickle import __bit_generator_ctor + return __bit_generator_ctor, (self.state['bit_generator'],), self.state + + @property + def state(self): + """ + Get or set the PRNG state + + The base BitGenerator.state must be overridden by a subclass + + Returns + ------- + state : dict + Dictionary containing the information required to describe the + state of the PRNG + """ + raise NotImplementedError('Not implemented in base BitGenerator') + + @state.setter + def state(self, value): + raise NotImplementedError('Not implemented in base BitGenerator') + + def random_raw(self, size=None, output=True): + """ + random_raw(self, size=None) + + Return randoms as generated by the underlying BitGenerator + + Parameters + ---------- + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. Default is None, in which case a + single value is returned. + output : bool, optional + Output values. Used for performance testing since the generated + values are not returned. + + Returns + ------- + out : uint or ndarray + Drawn samples. + + Notes + ----- + This method directly exposes the the raw underlying pseudo-random + number generator. All values are returned as unsigned 64-bit + values irrespective of the number of bits produced by the PRNG. + + See the class docstring for the number of bits returned. + """ + return random_raw(&self._bitgen, self.lock, size, output) + + def _benchmark(self, Py_ssize_t cnt, method='uint64'): + """Used in tests""" + return benchmark(&self._bitgen, self.lock, cnt, method) + + @property + def ctypes(self): + """ + ctypes interface + + Returns + ------- + interface : namedtuple + Named tuple containing ctypes wrapper + + * state_address - Memory address of the state struct + * state - pointer to the state struct + * next_uint64 - function pointer to produce 64 bit integers + * next_uint32 - function pointer to produce 32 bit integers + * next_double - function pointer to produce doubles + * bitgen - pointer to the bit generator struct + """ + if self._ctypes is None: + self._ctypes = prepare_ctypes(&self._bitgen) + + return self._ctypes + + @property + def cffi(self): + """ + CFFI interface + + Returns + ------- + interface : namedtuple + Named tuple containing CFFI wrapper + + * state_address - Memory address of the state struct + * state - pointer to the state struct + * next_uint64 - function pointer to produce 64 bit integers + * next_uint32 - function pointer to produce 32 bit integers + * next_double - function pointer to produce doubles + * bitgen - pointer to the bit generator struct + """ + if self._cffi is None: + self._cffi = prepare_cffi(&self._bitgen) + return self._cffi diff --git a/numpy/random/c_distributions.pxd b/numpy/random/c_distributions.pxd new file mode 100644 index 000000000000..6f905edc1131 --- /dev/null +++ b/numpy/random/c_distributions.pxd @@ -0,0 +1,114 @@ +#!python +#cython: wraparound=False, nonecheck=False, boundscheck=False, cdivision=True, language_level=3 +from numpy cimport npy_intp + +from libc.stdint cimport (uint64_t, int32_t, int64_t) +from numpy.random cimport bitgen_t + +cdef extern from "numpy/random/distributions.h": + + struct s_binomial_t: + int has_binomial + double psave + int64_t nsave + double r + double q + double fm + int64_t m + double p1 + double xm + double xl + double xr + double c + double laml + double lamr + double p2 + double p3 + double p4 + + ctypedef s_binomial_t binomial_t + + double random_standard_uniform(bitgen_t *bitgen_state) nogil + void random_standard_uniform_fill(bitgen_t* bitgen_state, npy_intp cnt, double *out) nogil + double random_standard_exponential(bitgen_t *bitgen_state) nogil + double random_standard_exponential_f(bitgen_t *bitgen_state) nogil + void random_standard_exponential_fill(bitgen_t *bitgen_state, npy_intp cnt, double *out) nogil + void random_standard_exponential_fill_f(bitgen_t *bitgen_state, npy_intp cnt, double *out) nogil + void random_standard_exponential_inv_fill(bitgen_t *bitgen_state, npy_intp cnt, double *out) nogil + void random_standard_exponential_inv_fill_f(bitgen_t *bitgen_state, npy_intp cnt, double *out) nogil + double random_standard_normal(bitgen_t* bitgen_state) nogil + void random_standard_normal_fill(bitgen_t *bitgen_state, npy_intp count, double *out) nogil + void random_standard_normal_fill_f(bitgen_t *bitgen_state, npy_intp count, float *out) nogil + double random_standard_gamma(bitgen_t *bitgen_state, double shape) nogil + + float random_standard_uniform_f(bitgen_t *bitgen_state) nogil + void random_standard_uniform_fill_f(bitgen_t* bitgen_state, npy_intp cnt, float *out) nogil + float random_standard_normal_f(bitgen_t* bitgen_state) nogil + float random_standard_gamma_f(bitgen_t *bitgen_state, float shape) nogil + + int64_t random_positive_int64(bitgen_t *bitgen_state) nogil + int32_t random_positive_int32(bitgen_t *bitgen_state) nogil + int64_t random_positive_int(bitgen_t *bitgen_state) nogil + uint64_t random_uint(bitgen_t *bitgen_state) nogil + + double random_normal(bitgen_t *bitgen_state, double loc, double scale) nogil + + double random_gamma(bitgen_t *bitgen_state, double shape, double scale) nogil + float random_gamma_f(bitgen_t *bitgen_state, float shape, float scale) nogil + + double random_exponential(bitgen_t *bitgen_state, double scale) nogil + double random_uniform(bitgen_t *bitgen_state, double lower, double range) nogil + double random_beta(bitgen_t *bitgen_state, double a, double b) nogil + double random_chisquare(bitgen_t *bitgen_state, double df) nogil + double random_f(bitgen_t *bitgen_state, double dfnum, double dfden) nogil + double random_standard_cauchy(bitgen_t *bitgen_state) nogil + double random_pareto(bitgen_t *bitgen_state, double a) nogil + double random_weibull(bitgen_t *bitgen_state, double a) nogil + double random_power(bitgen_t *bitgen_state, double a) nogil + double random_laplace(bitgen_t *bitgen_state, double loc, double scale) nogil + double random_gumbel(bitgen_t *bitgen_state, double loc, double scale) nogil + double random_logistic(bitgen_t *bitgen_state, double loc, double scale) nogil + double random_lognormal(bitgen_t *bitgen_state, double mean, double sigma) nogil + double random_rayleigh(bitgen_t *bitgen_state, double mode) nogil + double random_standard_t(bitgen_t *bitgen_state, double df) nogil + double random_noncentral_chisquare(bitgen_t *bitgen_state, double df, + double nonc) nogil + double random_noncentral_f(bitgen_t *bitgen_state, double dfnum, + double dfden, double nonc) nogil + double random_wald(bitgen_t *bitgen_state, double mean, double scale) nogil + double random_vonmises(bitgen_t *bitgen_state, double mu, double kappa) nogil + double random_triangular(bitgen_t *bitgen_state, double left, double mode, + double right) nogil + + int64_t random_poisson(bitgen_t *bitgen_state, double lam) nogil + int64_t random_negative_binomial(bitgen_t *bitgen_state, double n, double p) nogil + int64_t random_binomial(bitgen_t *bitgen_state, double p, int64_t n, binomial_t *binomial) nogil + int64_t random_logseries(bitgen_t *bitgen_state, double p) nogil + int64_t random_geometric_search(bitgen_t *bitgen_state, double p) nogil + int64_t random_geometric_inversion(bitgen_t *bitgen_state, double p) nogil + int64_t random_geometric(bitgen_t *bitgen_state, double p) nogil + int64_t random_zipf(bitgen_t *bitgen_state, double a) nogil + int64_t random_hypergeometric(bitgen_t *bitgen_state, int64_t good, int64_t bad, + int64_t sample) nogil + + uint64_t random_interval(bitgen_t *bitgen_state, uint64_t max) nogil + + # Generate random uint64 numbers in closed interval [off, off + rng]. + uint64_t random_bounded_uint64(bitgen_t *bitgen_state, + uint64_t off, uint64_t rng, + uint64_t mask, bint use_masked) nogil + + void random_multinomial(bitgen_t *bitgen_state, int64_t n, int64_t *mnix, + double *pix, npy_intp d, binomial_t *binomial) nogil + + int random_multivariate_hypergeometric_count(bitgen_t *bitgen_state, + int64_t total, + size_t num_colors, int64_t *colors, + int64_t nsample, + size_t num_variates, int64_t *variates) nogil + void random_multivariate_hypergeometric_marginals(bitgen_t *bitgen_state, + int64_t total, + size_t num_colors, int64_t *colors, + int64_t nsample, + size_t num_variates, int64_t *variates) nogil + diff --git a/numpy/random/include/aligned_malloc.h b/numpy/random/include/aligned_malloc.h new file mode 100644 index 000000000000..43f68253d137 --- /dev/null +++ b/numpy/random/include/aligned_malloc.h @@ -0,0 +1,54 @@ +#ifndef _RANDOMDGEN__ALIGNED_MALLOC_H_ +#define _RANDOMDGEN__ALIGNED_MALLOC_H_ + +#include <Python.h> +#include "numpy/npy_common.h" + +#define NPY_MEMALIGN 16 /* 16 for SSE2, 32 for AVX, 64 for Xeon Phi */ + +static NPY_INLINE void *PyArray_realloc_aligned(void *p, size_t n) +{ + void *p1, **p2, *base; + size_t old_offs, offs = NPY_MEMALIGN - 1 + sizeof(void *); + if (NPY_UNLIKELY(p != NULL)) + { + base = *(((void **)p) - 1); + if (NPY_UNLIKELY((p1 = PyMem_Realloc(base, n + offs)) == NULL)) + return NULL; + if (NPY_LIKELY(p1 == base)) + return p; + p2 = (void **)(((Py_uintptr_t)(p1) + offs) & ~(NPY_MEMALIGN - 1)); + old_offs = (size_t)((Py_uintptr_t)p - (Py_uintptr_t)base); + memmove((void *)p2, ((char *)p1) + old_offs, n); + } + else + { + if (NPY_UNLIKELY((p1 = PyMem_Malloc(n + offs)) == NULL)) + return NULL; + p2 = (void **)(((Py_uintptr_t)(p1) + offs) & ~(NPY_MEMALIGN - 1)); + } + *(p2 - 1) = p1; + return (void *)p2; +} + +static NPY_INLINE void *PyArray_malloc_aligned(size_t n) +{ + return PyArray_realloc_aligned(NULL, n); +} + +static NPY_INLINE void *PyArray_calloc_aligned(size_t n, size_t s) +{ + void *p; + if (NPY_UNLIKELY((p = PyArray_realloc_aligned(NULL, n * s)) == NULL)) + return NULL; + memset(p, 0, n * s); + return p; +} + +static NPY_INLINE void PyArray_free_aligned(void *p) +{ + void *base = *(((void **)p) - 1); + PyMem_Free(base); +} + +#endif diff --git a/numpy/random/include/legacy-distributions.h b/numpy/random/include/legacy-distributions.h new file mode 100644 index 000000000000..f6c5cf0532d1 --- /dev/null +++ b/numpy/random/include/legacy-distributions.h @@ -0,0 +1,51 @@ +#ifndef _RANDOMDGEN__DISTRIBUTIONS_LEGACY_H_ +#define _RANDOMDGEN__DISTRIBUTIONS_LEGACY_H_ + + +#include "numpy/random/distributions.h" + +typedef struct aug_bitgen { + bitgen_t *bit_generator; + int has_gauss; + double gauss; +} aug_bitgen_t; + +extern double legacy_gauss(aug_bitgen_t *aug_state); +extern double legacy_standard_exponential(aug_bitgen_t *aug_state); +extern double legacy_pareto(aug_bitgen_t *aug_state, double a); +extern double legacy_weibull(aug_bitgen_t *aug_state, double a); +extern double legacy_power(aug_bitgen_t *aug_state, double a); +extern double legacy_gamma(aug_bitgen_t *aug_state, double shape, double scale); +extern double legacy_chisquare(aug_bitgen_t *aug_state, double df); +extern double legacy_rayleigh(bitgen_t *bitgen_state, double mode); +extern double legacy_noncentral_chisquare(aug_bitgen_t *aug_state, double df, + double nonc); +extern double legacy_noncentral_f(aug_bitgen_t *aug_state, double dfnum, + double dfden, double nonc); +extern double legacy_wald(aug_bitgen_t *aug_state, double mean, double scale); +extern double legacy_lognormal(aug_bitgen_t *aug_state, double mean, + double sigma); +extern double legacy_standard_t(aug_bitgen_t *aug_state, double df); +extern double legacy_standard_cauchy(aug_bitgen_t *state); +extern double legacy_beta(aug_bitgen_t *aug_state, double a, double b); +extern double legacy_f(aug_bitgen_t *aug_state, double dfnum, double dfden); +extern double legacy_normal(aug_bitgen_t *aug_state, double loc, double scale); +extern double legacy_standard_gamma(aug_bitgen_t *aug_state, double shape); +extern double legacy_exponential(aug_bitgen_t *aug_state, double scale); +extern double legacy_vonmises(bitgen_t *bitgen_state, double mu, double kappa); +extern int64_t legacy_random_binomial(bitgen_t *bitgen_state, double p, + int64_t n, binomial_t *binomial); +extern int64_t legacy_negative_binomial(aug_bitgen_t *aug_state, double n, + double p); +extern int64_t legacy_random_hypergeometric(bitgen_t *bitgen_state, + int64_t good, int64_t bad, + int64_t sample); +extern int64_t legacy_logseries(bitgen_t *bitgen_state, double p); +extern int64_t legacy_random_poisson(bitgen_t *bitgen_state, double lam); +extern int64_t legacy_random_zipf(bitgen_t *bitgen_state, double a); +extern int64_t legacy_random_geometric(bitgen_t *bitgen_state, double p); +void legacy_random_multinomial(bitgen_t *bitgen_state, RAND_INT_TYPE n, + RAND_INT_TYPE *mnix, double *pix, npy_intp d, + binomial_t *binomial); + +#endif diff --git a/numpy/random/info.py b/numpy/random/info.py deleted file mode 100644 index be9c8d9bd286..000000000000 --- a/numpy/random/info.py +++ /dev/null @@ -1,139 +0,0 @@ -""" -======================== -Random Number Generation -======================== - -==================== ========================================================= -Utility functions -============================================================================== -random_sample Uniformly distributed floats over ``[0, 1)``. -random Alias for `random_sample`. -bytes Uniformly distributed random bytes. -random_integers Uniformly distributed integers in a given range. -permutation Randomly permute a sequence / generate a random sequence. -shuffle Randomly permute a sequence in place. -seed Seed the random number generator. -choice Random sample from 1-D array. - -==================== ========================================================= - -==================== ========================================================= -Compatibility functions -============================================================================== -rand Uniformly distributed values. -randn Normally distributed values. -ranf Uniformly distributed floating point numbers. -randint Uniformly distributed integers in a given range. -==================== ========================================================= - -==================== ========================================================= -Univariate distributions -============================================================================== -beta Beta distribution over ``[0, 1]``. -binomial Binomial distribution. -chisquare :math:`\\chi^2` distribution. -exponential Exponential distribution. -f F (Fisher-Snedecor) distribution. -gamma Gamma distribution. -geometric Geometric distribution. -gumbel Gumbel distribution. -hypergeometric Hypergeometric distribution. -laplace Laplace distribution. -logistic Logistic distribution. -lognormal Log-normal distribution. -logseries Logarithmic series distribution. -negative_binomial Negative binomial distribution. -noncentral_chisquare Non-central chi-square distribution. -noncentral_f Non-central F distribution. -normal Normal / Gaussian distribution. -pareto Pareto distribution. -poisson Poisson distribution. -power Power distribution. -rayleigh Rayleigh distribution. -triangular Triangular distribution. -uniform Uniform distribution. -vonmises Von Mises circular distribution. -wald Wald (inverse Gaussian) distribution. -weibull Weibull distribution. -zipf Zipf's distribution over ranked data. -==================== ========================================================= - -==================== ========================================================= -Multivariate distributions -============================================================================== -dirichlet Multivariate generalization of Beta distribution. -multinomial Multivariate generalization of the binomial distribution. -multivariate_normal Multivariate generalization of the normal distribution. -==================== ========================================================= - -==================== ========================================================= -Standard distributions -============================================================================== -standard_cauchy Standard Cauchy-Lorentz distribution. -standard_exponential Standard exponential distribution. -standard_gamma Standard Gamma distribution. -standard_normal Standard normal distribution. -standard_t Standard Student's t-distribution. -==================== ========================================================= - -==================== ========================================================= -Internal functions -============================================================================== -get_state Get tuple representing internal state of generator. -set_state Set state of generator. -==================== ========================================================= - -""" -from __future__ import division, absolute_import, print_function - -depends = ['core'] - -__all__ = [ - 'beta', - 'binomial', - 'bytes', - 'chisquare', - 'choice', - 'dirichlet', - 'exponential', - 'f', - 'gamma', - 'geometric', - 'get_state', - 'gumbel', - 'hypergeometric', - 'laplace', - 'logistic', - 'lognormal', - 'logseries', - 'multinomial', - 'multivariate_normal', - 'negative_binomial', - 'noncentral_chisquare', - 'noncentral_f', - 'normal', - 'pareto', - 'permutation', - 'poisson', - 'power', - 'rand', - 'randint', - 'randn', - 'random_integers', - 'random_sample', - 'rayleigh', - 'seed', - 'set_state', - 'shuffle', - 'standard_cauchy', - 'standard_exponential', - 'standard_gamma', - 'standard_normal', - 'standard_t', - 'triangular', - 'uniform', - 'vonmises', - 'wald', - 'weibull', - 'zipf' -] diff --git a/numpy/random/mtrand.pyi b/numpy/random/mtrand.pyi new file mode 100644 index 000000000000..cbe87a299842 --- /dev/null +++ b/numpy/random/mtrand.pyi @@ -0,0 +1,573 @@ +from typing import Any, Callable, Dict, Optional, Tuple, Type, Union, overload, Literal + +from numpy import ( + bool_, + dtype, + float32, + float64, + int8, + int16, + int32, + int64, + int_, + ndarray, + uint, + uint8, + uint16, + uint32, + uint64, +) +from numpy.random.bit_generator import BitGenerator +from numpy.typing import ( + ArrayLike, + _ArrayLikeFloat_co, + _ArrayLikeInt_co, + _DoubleCodes, + _DTypeLikeBool, + _DTypeLikeInt, + _DTypeLikeUInt, + _Float32Codes, + _Float64Codes, + _Int8Codes, + _Int16Codes, + _Int32Codes, + _Int64Codes, + _IntCodes, + _ShapeLike, + _SingleCodes, + _SupportsDType, + _UInt8Codes, + _UInt16Codes, + _UInt32Codes, + _UInt64Codes, + _UIntCodes, +) + +_DTypeLikeFloat32 = Union[ + dtype[float32], + _SupportsDType[dtype[float32]], + Type[float32], + _Float32Codes, + _SingleCodes, +] + +_DTypeLikeFloat64 = Union[ + dtype[float64], + _SupportsDType[dtype[float64]], + Type[float], + Type[float64], + _Float64Codes, + _DoubleCodes, +] + +class RandomState: + _bit_generator: BitGenerator + def __init__(self, seed: Union[None, _ArrayLikeInt_co, BitGenerator] = ...) -> None: ... + def __repr__(self) -> str: ... + def __str__(self) -> str: ... + def __getstate__(self) -> Dict[str, Any]: ... + def __setstate__(self, state: Dict[str, Any]) -> None: ... + def __reduce__(self) -> Tuple[Callable[[str], RandomState], Tuple[str], Dict[str, Any]]: ... + def seed(self, seed: Optional[_ArrayLikeFloat_co] = ...) -> None: ... + @overload + def get_state(self, legacy: Literal[False] = ...) -> Dict[str, Any]: ... + @overload + def get_state( + self, legacy: Literal[True] = ... + ) -> Union[Dict[str, Any], Tuple[str, ndarray[Any, dtype[uint32]], int, int, float]]: ... + def set_state( + self, state: Union[Dict[str, Any], Tuple[str, ndarray[Any, dtype[uint32]], int, int, float]] + ) -> None: ... + @overload + def random_sample(self, size: None = ...) -> float: ... # type: ignore[misc] + @overload + def random_sample(self, size: _ShapeLike = ...) -> ndarray[Any, dtype[float64]]: ... + @overload + def random(self, size: None = ...) -> float: ... # type: ignore[misc] + @overload + def random(self, size: _ShapeLike = ...) -> ndarray[Any, dtype[float64]]: ... + @overload + def beta(self, a: float, b: float, size: None = ...) -> float: ... # type: ignore[misc] + @overload + def beta( + self, a: _ArrayLikeFloat_co, b: _ArrayLikeFloat_co, size: Optional[_ShapeLike] = ... + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def exponential(self, scale: float = ..., size: None = ...) -> float: ... # type: ignore[misc] + @overload + def exponential( + self, scale: _ArrayLikeFloat_co = ..., size: Optional[_ShapeLike] = ... + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def standard_exponential(self, size: None = ...) -> float: ... # type: ignore[misc] + @overload + def standard_exponential(self, size: _ShapeLike = ...) -> ndarray[Any, dtype[float64]]: ... + @overload + def tomaxint(self, size: None = ...) -> int: ... # type: ignore[misc] + @overload + def tomaxint(self, size: _ShapeLike = ...) -> ndarray[Any, dtype[int_]]: ... + @overload + def randint( # type: ignore[misc] + self, + low: int, + high: Optional[int] = ..., + ) -> int: ... + @overload + def randint( # type: ignore[misc] + self, + low: int, + high: Optional[int] = ..., + size: None = ..., + dtype: _DTypeLikeBool = ..., + ) -> bool: ... + @overload + def randint( # type: ignore[misc] + self, + low: int, + high: Optional[int] = ..., + size: None = ..., + dtype: Union[_DTypeLikeInt, _DTypeLikeUInt] = ..., + ) -> int: ... + @overload + def randint( # type: ignore[misc] + self, + low: _ArrayLikeInt_co, + high: Optional[_ArrayLikeInt_co] = ..., + size: Optional[_ShapeLike] = ..., + ) -> ndarray[Any, dtype[int_]]: ... + @overload + def randint( # type: ignore[misc] + self, + low: _ArrayLikeInt_co, + high: Optional[_ArrayLikeInt_co] = ..., + size: Optional[_ShapeLike] = ..., + dtype: _DTypeLikeBool = ..., + ) -> ndarray[Any, dtype[bool_]]: ... + @overload + def randint( # type: ignore[misc] + self, + low: _ArrayLikeInt_co, + high: Optional[_ArrayLikeInt_co] = ..., + size: Optional[_ShapeLike] = ..., + dtype: Union[dtype[int8], Type[int8], _Int8Codes, _SupportsDType[dtype[int8]]] = ..., + ) -> ndarray[Any, dtype[int8]]: ... + @overload + def randint( # type: ignore[misc] + self, + low: _ArrayLikeInt_co, + high: Optional[_ArrayLikeInt_co] = ..., + size: Optional[_ShapeLike] = ..., + dtype: Union[dtype[int16], Type[int16], _Int16Codes, _SupportsDType[dtype[int16]]] = ..., + ) -> ndarray[Any, dtype[int16]]: ... + @overload + def randint( # type: ignore[misc] + self, + low: _ArrayLikeInt_co, + high: Optional[_ArrayLikeInt_co] = ..., + size: Optional[_ShapeLike] = ..., + dtype: Union[dtype[int32], Type[int32], _Int32Codes, _SupportsDType[dtype[int32]]] = ..., + ) -> ndarray[Any, dtype[Union[int32]]]: ... + @overload + def randint( # type: ignore[misc] + self, + low: _ArrayLikeInt_co, + high: Optional[_ArrayLikeInt_co] = ..., + size: Optional[_ShapeLike] = ..., + dtype: Optional[ + Union[dtype[int64], Type[int64], _Int64Codes, _SupportsDType[dtype[int64]]] + ] = ..., + ) -> ndarray[Any, dtype[int64]]: ... + @overload + def randint( # type: ignore[misc] + self, + low: _ArrayLikeInt_co, + high: Optional[_ArrayLikeInt_co] = ..., + size: Optional[_ShapeLike] = ..., + dtype: Union[dtype[uint8], Type[uint8], _UInt8Codes, _SupportsDType[dtype[uint8]]] = ..., + ) -> ndarray[Any, dtype[uint8]]: ... + @overload + def randint( # type: ignore[misc] + self, + low: _ArrayLikeInt_co, + high: Optional[_ArrayLikeInt_co] = ..., + size: Optional[_ShapeLike] = ..., + dtype: Union[ + dtype[uint16], Type[uint16], _UInt16Codes, _SupportsDType[dtype[uint16]] + ] = ..., + ) -> ndarray[Any, dtype[Union[uint16]]]: ... + @overload + def randint( # type: ignore[misc] + self, + low: _ArrayLikeInt_co, + high: Optional[_ArrayLikeInt_co] = ..., + size: Optional[_ShapeLike] = ..., + dtype: Union[ + dtype[uint32], Type[uint32], _UInt32Codes, _SupportsDType[dtype[uint32]] + ] = ..., + ) -> ndarray[Any, dtype[uint32]]: ... + @overload + def randint( # type: ignore[misc] + self, + low: _ArrayLikeInt_co, + high: Optional[_ArrayLikeInt_co] = ..., + size: Optional[_ShapeLike] = ..., + dtype: Union[ + dtype[uint64], Type[uint64], _UInt64Codes, _SupportsDType[dtype[uint64]] + ] = ..., + ) -> ndarray[Any, dtype[uint64]]: ... + @overload + def randint( # type: ignore[misc] + self, + low: _ArrayLikeInt_co, + high: Optional[_ArrayLikeInt_co] = ..., + size: Optional[_ShapeLike] = ..., + dtype: Union[ + dtype[int_], Type[int], Type[int_], _IntCodes, _SupportsDType[dtype[int_]] + ] = ..., + ) -> ndarray[Any, dtype[int_]]: ... + @overload + def randint( # type: ignore[misc] + self, + low: _ArrayLikeInt_co, + high: Optional[_ArrayLikeInt_co] = ..., + size: Optional[_ShapeLike] = ..., + dtype: Union[dtype[uint], Type[uint], _UIntCodes, _SupportsDType[dtype[uint]]] = ..., + ) -> ndarray[Any, dtype[uint]]: ... + def bytes(self, length: int) -> bytes: ... + @overload + def choice( + self, + a: int, + size: None = ..., + replace: bool = ..., + p: Optional[_ArrayLikeFloat_co] = ..., + ) -> int: ... + @overload + def choice( + self, + a: int, + size: _ShapeLike = ..., + replace: bool = ..., + p: Optional[_ArrayLikeFloat_co] = ..., + ) -> ndarray[Any, dtype[int_]]: ... + @overload + def choice( + self, + a: ArrayLike, + size: None = ..., + replace: bool = ..., + p: Optional[_ArrayLikeFloat_co] = ..., + ) -> Any: ... + @overload + def choice( + self, + a: ArrayLike, + size: _ShapeLike = ..., + replace: bool = ..., + p: Optional[_ArrayLikeFloat_co] = ..., + ) -> ndarray[Any, Any]: ... + @overload + def uniform(self, low: float = ..., high: float = ..., size: None = ...) -> float: ... # type: ignore[misc] + @overload + def uniform( + self, + low: _ArrayLikeFloat_co = ..., + high: _ArrayLikeFloat_co = ..., + size: Optional[_ShapeLike] = ..., + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def rand(self) -> float: ... + @overload + def rand(self, *args: int) -> ndarray[Any, dtype[float64]]: ... + @overload + def randn(self) -> float: ... + @overload + def randn(self, *args: int) -> ndarray[Any, dtype[float64]]: ... + @overload + def random_integers(self, low: int, high: Optional[int] = ..., size: None = ...) -> int: ... # type: ignore[misc] + @overload + def random_integers( + self, + low: _ArrayLikeInt_co, + high: Optional[_ArrayLikeInt_co] = ..., + size: Optional[_ShapeLike] = ..., + ) -> ndarray[Any, dtype[int_]]: ... + @overload + def standard_normal(self, size: None = ...) -> float: ... # type: ignore[misc] + @overload + def standard_normal( # type: ignore[misc] + self, size: _ShapeLike = ... + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def normal(self, loc: float = ..., scale: float = ..., size: None = ...) -> float: ... # type: ignore[misc] + @overload + def normal( + self, + loc: _ArrayLikeFloat_co = ..., + scale: _ArrayLikeFloat_co = ..., + size: Optional[_ShapeLike] = ..., + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def standard_gamma( # type: ignore[misc] + self, + shape: float, + size: None = ..., + ) -> float: ... + @overload + def standard_gamma( + self, + shape: _ArrayLikeFloat_co, + size: Optional[_ShapeLike] = ..., + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def gamma(self, shape: float, scale: float = ..., size: None = ...) -> float: ... # type: ignore[misc] + @overload + def gamma( + self, + shape: _ArrayLikeFloat_co, + scale: _ArrayLikeFloat_co = ..., + size: Optional[_ShapeLike] = ..., + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def f(self, dfnum: float, dfden: float, size: None = ...) -> float: ... # type: ignore[misc] + @overload + def f( + self, dfnum: _ArrayLikeFloat_co, dfden: _ArrayLikeFloat_co, size: Optional[_ShapeLike] = ... + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def noncentral_f(self, dfnum: float, dfden: float, nonc: float, size: None = ...) -> float: ... # type: ignore[misc] + @overload + def noncentral_f( + self, + dfnum: _ArrayLikeFloat_co, + dfden: _ArrayLikeFloat_co, + nonc: _ArrayLikeFloat_co, + size: Optional[_ShapeLike] = ..., + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def chisquare(self, df: float, size: None = ...) -> float: ... # type: ignore[misc] + @overload + def chisquare( + self, df: _ArrayLikeFloat_co, size: Optional[_ShapeLike] = ... + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def noncentral_chisquare(self, df: float, nonc: float, size: None = ...) -> float: ... # type: ignore[misc] + @overload + def noncentral_chisquare( + self, df: _ArrayLikeFloat_co, nonc: _ArrayLikeFloat_co, size: Optional[_ShapeLike] = ... + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def standard_t(self, df: float, size: None = ...) -> float: ... # type: ignore[misc] + @overload + def standard_t( + self, df: _ArrayLikeFloat_co, size: None = ... + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def standard_t( + self, df: _ArrayLikeFloat_co, size: _ShapeLike = ... + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def vonmises(self, mu: float, kappa: float, size: None = ...) -> float: ... # type: ignore[misc] + @overload + def vonmises( + self, mu: _ArrayLikeFloat_co, kappa: _ArrayLikeFloat_co, size: Optional[_ShapeLike] = ... + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def pareto(self, a: float, size: None = ...) -> float: ... # type: ignore[misc] + @overload + def pareto( + self, a: _ArrayLikeFloat_co, size: Optional[_ShapeLike] = ... + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def weibull(self, a: float, size: None = ...) -> float: ... # type: ignore[misc] + @overload + def weibull( + self, a: _ArrayLikeFloat_co, size: Optional[_ShapeLike] = ... + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def power(self, a: float, size: None = ...) -> float: ... # type: ignore[misc] + @overload + def power( + self, a: _ArrayLikeFloat_co, size: Optional[_ShapeLike] = ... + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def standard_cauchy(self, size: None = ...) -> float: ... # type: ignore[misc] + @overload + def standard_cauchy(self, size: _ShapeLike = ...) -> ndarray[Any, dtype[float64]]: ... + @overload + def laplace(self, loc: float = ..., scale: float = ..., size: None = ...) -> float: ... # type: ignore[misc] + @overload + def laplace( + self, + loc: _ArrayLikeFloat_co = ..., + scale: _ArrayLikeFloat_co = ..., + size: Optional[_ShapeLike] = ..., + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def gumbel(self, loc: float = ..., scale: float = ..., size: None = ...) -> float: ... # type: ignore[misc] + @overload + def gumbel( + self, + loc: _ArrayLikeFloat_co = ..., + scale: _ArrayLikeFloat_co = ..., + size: Optional[_ShapeLike] = ..., + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def logistic(self, loc: float = ..., scale: float = ..., size: None = ...) -> float: ... # type: ignore[misc] + @overload + def logistic( + self, + loc: _ArrayLikeFloat_co = ..., + scale: _ArrayLikeFloat_co = ..., + size: Optional[_ShapeLike] = ..., + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def lognormal(self, mean: float = ..., sigma: float = ..., size: None = ...) -> float: ... # type: ignore[misc] + @overload + def lognormal( + self, + mean: _ArrayLikeFloat_co = ..., + sigma: _ArrayLikeFloat_co = ..., + size: Optional[_ShapeLike] = ..., + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def rayleigh(self, scale: float = ..., size: None = ...) -> float: ... # type: ignore[misc] + @overload + def rayleigh( + self, scale: _ArrayLikeFloat_co = ..., size: Optional[_ShapeLike] = ... + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def wald(self, mean: float, scale: float, size: None = ...) -> float: ... # type: ignore[misc] + @overload + def wald( + self, mean: _ArrayLikeFloat_co, scale: _ArrayLikeFloat_co, size: Optional[_ShapeLike] = ... + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def triangular(self, left: float, mode: float, right: float, size: None = ...) -> float: ... # type: ignore[misc] + @overload + def triangular( + self, + left: _ArrayLikeFloat_co, + mode: _ArrayLikeFloat_co, + right: _ArrayLikeFloat_co, + size: Optional[_ShapeLike] = ..., + ) -> ndarray[Any, dtype[float64]]: ... + @overload + def binomial(self, n: int, p: float, size: None = ...) -> int: ... # type: ignore[misc] + @overload + def binomial( + self, n: _ArrayLikeInt_co, p: _ArrayLikeFloat_co, size: Optional[_ShapeLike] = ... + ) -> ndarray[Any, dtype[int_]]: ... + @overload + def negative_binomial(self, n: float, p: float, size: None = ...) -> int: ... # type: ignore[misc] + @overload + def negative_binomial( + self, n: _ArrayLikeFloat_co, p: _ArrayLikeFloat_co, size: Optional[_ShapeLike] = ... + ) -> ndarray[Any, dtype[int_]]: ... + @overload + def poisson(self, lam: float = ..., size: None = ...) -> int: ... # type: ignore[misc] + @overload + def poisson( + self, lam: _ArrayLikeFloat_co = ..., size: Optional[_ShapeLike] = ... + ) -> ndarray[Any, dtype[int_]]: ... + @overload + def zipf(self, a: float, size: None = ...) -> int: ... # type: ignore[misc] + @overload + def zipf( + self, a: _ArrayLikeFloat_co, size: Optional[_ShapeLike] = ... + ) -> ndarray[Any, dtype[int_]]: ... + @overload + def geometric(self, p: float, size: None = ...) -> int: ... # type: ignore[misc] + @overload + def geometric( + self, p: _ArrayLikeFloat_co, size: Optional[_ShapeLike] = ... + ) -> ndarray[Any, dtype[int_]]: ... + @overload + def hypergeometric(self, ngood: int, nbad: int, nsample: int, size: None = ...) -> int: ... # type: ignore[misc] + @overload + def hypergeometric( + self, + ngood: _ArrayLikeInt_co, + nbad: _ArrayLikeInt_co, + nsample: _ArrayLikeInt_co, + size: Optional[_ShapeLike] = ..., + ) -> ndarray[Any, dtype[int_]]: ... + @overload + def logseries(self, p: float, size: None = ...) -> int: ... # type: ignore[misc] + @overload + def logseries( + self, p: _ArrayLikeFloat_co, size: Optional[_ShapeLike] = ... + ) -> ndarray[Any, dtype[int_]]: ... + def multivariate_normal( + self, + mean: _ArrayLikeFloat_co, + cov: _ArrayLikeFloat_co, + size: Optional[_ShapeLike] = ..., + check_valid: Literal["warn", "raise", "ignore"] = ..., + tol: float = ..., + ) -> ndarray[Any, dtype[float64]]: ... + def multinomial( + self, n: _ArrayLikeInt_co, pvals: _ArrayLikeFloat_co, size: Optional[_ShapeLike] = ... + ) -> ndarray[Any, dtype[int_]]: ... + def dirichlet( + self, alpha: _ArrayLikeFloat_co, size: Optional[_ShapeLike] = ... + ) -> ndarray[Any, dtype[float64]]: ... + def shuffle(self, x: ArrayLike) -> None: ... + @overload + def permutation(self, x: int) -> ndarray[Any, dtype[int_]]: ... + @overload + def permutation(self, x: ArrayLike) -> ndarray[Any, Any]: ... + +_rand: RandomState + +beta = _rand.beta +binomial = _rand.binomial +bytes = _rand.bytes +chisquare = _rand.chisquare +choice = _rand.choice +dirichlet = _rand.dirichlet +exponential = _rand.exponential +f = _rand.f +gamma = _rand.gamma +get_state = _rand.get_state +geometric = _rand.geometric +gumbel = _rand.gumbel +hypergeometric = _rand.hypergeometric +laplace = _rand.laplace +logistic = _rand.logistic +lognormal = _rand.lognormal +logseries = _rand.logseries +multinomial = _rand.multinomial +multivariate_normal = _rand.multivariate_normal +negative_binomial = _rand.negative_binomial +noncentral_chisquare = _rand.noncentral_chisquare +noncentral_f = _rand.noncentral_f +normal = _rand.normal +pareto = _rand.pareto +permutation = _rand.permutation +poisson = _rand.poisson +power = _rand.power +rand = _rand.rand +randint = _rand.randint +randn = _rand.randn +random = _rand.random +random_integers = _rand.random_integers +random_sample = _rand.random_sample +rayleigh = _rand.rayleigh +seed = _rand.seed +set_state = _rand.set_state +shuffle = _rand.shuffle +standard_cauchy = _rand.standard_cauchy +standard_exponential = _rand.standard_exponential +standard_gamma = _rand.standard_gamma +standard_normal = _rand.standard_normal +standard_t = _rand.standard_t +triangular = _rand.triangular +uniform = _rand.uniform +vonmises = _rand.vonmises +wald = _rand.wald +weibull = _rand.weibull +zipf = _rand.zipf +# Two legacy that are trivial wrappers around random_sample +sample = _rand.random_sample +ranf = _rand.random_sample diff --git a/numpy/random/mtrand/mtrand.pyx b/numpy/random/mtrand.pyx similarity index 63% rename from numpy/random/mtrand/mtrand.pyx rename to numpy/random/mtrand.pyx index b45b3146f145..ce09a041c94e 100644 --- a/numpy/random/mtrand/mtrand.pyx +++ b/numpy/random/mtrand.pyx @@ -1,627 +1,159 @@ -# mtrand.pyx -- A Pyrex wrapper of Jean-Sebastien Roy's RandomKit -# -# Copyright 2005 Robert Kern (robert.kern@gmail.com) -# -# Permission is hereby granted, free of charge, to any person obtaining a -# copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -include "Python.pxi" -include "randint_helpers.pxi" -include "numpy.pxd" -include "cpython/pycapsule.pxd" - -from libc cimport string - -cdef extern from "math.h": - double exp(double x) - double log(double x) - double floor(double x) - double sin(double x) - double cos(double x) - -cdef extern from "numpy/npy_math.h": - int npy_isfinite(double x) +#!python +#cython: wraparound=False, nonecheck=False, boundscheck=False, cdivision=True, language_level=3 +import operator +import warnings +from collections.abc import Sequence -cdef extern from "mtrand_py_helper.h": - object empty_py_bytes(npy_intp length, void **bytes) +import numpy as np -cdef extern from "randomkit.h": +from cpython.pycapsule cimport PyCapsule_IsValid, PyCapsule_GetPointer +from cpython cimport (Py_INCREF, PyFloat_AsDouble) +cimport cython +cimport numpy as np - ctypedef struct rk_state: - unsigned long key[624] - int pos +from libc cimport string +from libc.stdint cimport int64_t, uint64_t +from ._bounded_integers cimport (_rand_bool, _rand_int32, _rand_int64, + _rand_int16, _rand_int8, _rand_uint64, _rand_uint32, _rand_uint16, + _rand_uint8,) +from ._mt19937 import MT19937 as _MT19937 +from numpy.random cimport bitgen_t +from ._common cimport (POISSON_LAM_MAX, CONS_POSITIVE, CONS_NONE, + CONS_NON_NEGATIVE, CONS_BOUNDED_0_1, CONS_BOUNDED_GT_0_1, CONS_GTE_1, + CONS_GT_1, LEGACY_CONS_POISSON, + double_fill, cont, kahan_sum, cont_broadcast_3, + check_array_constraint, check_constraint, disc, discrete_broadcast_iii, + validate_output_shape + ) + +cdef extern from "numpy/random/distributions.h": + struct s_binomial_t: + int has_binomial + double psave + int64_t nsave + double r + double q + double fm + int64_t m + double p1 + double xm + double xl + double xr + double c + double laml + double lamr + double p2 + double p3 + double p4 + + ctypedef s_binomial_t binomial_t + + void random_standard_uniform_fill(bitgen_t* bitgen_state, np.npy_intp cnt, double *out) nogil + int64_t random_positive_int(bitgen_t *bitgen_state) nogil + double random_uniform(bitgen_t *bitgen_state, double lower, double range) nogil + double random_laplace(bitgen_t *bitgen_state, double loc, double scale) nogil + double random_gumbel(bitgen_t *bitgen_state, double loc, double scale) nogil + double random_logistic(bitgen_t *bitgen_state, double loc, double scale) nogil + double random_rayleigh(bitgen_t *bitgen_state, double mode) nogil + double random_triangular(bitgen_t *bitgen_state, double left, double mode, + double right) nogil + uint64_t random_interval(bitgen_t *bitgen_state, uint64_t max) nogil + +cdef extern from "include/legacy-distributions.h": + struct aug_bitgen: + bitgen_t *bit_generator int has_gauss double gauss - ctypedef enum rk_error: - RK_NOERR = 0 - RK_ENODEV = 1 - RK_ERR_MAX = 2 - - char *rk_strerror[2] - - # 0xFFFFFFFFUL - unsigned long RK_MAX - - void rk_seed(unsigned long seed, rk_state *state) - rk_error rk_randomseed(rk_state *state) - unsigned long rk_random(rk_state *state) - long rk_long(rk_state *state) nogil - unsigned long rk_ulong(rk_state *state) nogil - unsigned long rk_interval(unsigned long max, rk_state *state) nogil - double rk_double(rk_state *state) nogil - void rk_fill(void *buffer, size_t size, rk_state *state) nogil - rk_error rk_devfill(void *buffer, size_t size, int strong) - rk_error rk_altfill(void *buffer, size_t size, int strong, - rk_state *state) nogil - double rk_gauss(rk_state *state) nogil - void rk_random_uint64(npy_uint64 off, npy_uint64 rng, npy_intp cnt, - npy_uint64 *out, rk_state *state) nogil - void rk_random_uint32(npy_uint32 off, npy_uint32 rng, npy_intp cnt, - npy_uint32 *out, rk_state *state) nogil - void rk_random_uint16(npy_uint16 off, npy_uint16 rng, npy_intp cnt, - npy_uint16 *out, rk_state *state) nogil - void rk_random_uint8(npy_uint8 off, npy_uint8 rng, npy_intp cnt, - npy_uint8 *out, rk_state *state) nogil - void rk_random_bool(npy_bool off, npy_bool rng, npy_intp cnt, - npy_bool *out, rk_state *state) nogil - - -cdef extern from "distributions.h": - # do not need the GIL, but they do need a lock on the state !! */ - - double rk_normal(rk_state *state, double loc, double scale) nogil - double rk_standard_exponential(rk_state *state) nogil - double rk_exponential(rk_state *state, double scale) nogil - double rk_uniform(rk_state *state, double loc, double scale) nogil - double rk_standard_gamma(rk_state *state, double shape) nogil - double rk_gamma(rk_state *state, double shape, double scale) nogil - double rk_beta(rk_state *state, double a, double b) nogil - double rk_chisquare(rk_state *state, double df) nogil - double rk_noncentral_chisquare(rk_state *state, double df, double nonc) nogil - double rk_f(rk_state *state, double dfnum, double dfden) nogil - double rk_noncentral_f(rk_state *state, double dfnum, double dfden, double nonc) nogil - double rk_standard_cauchy(rk_state *state) nogil - double rk_standard_t(rk_state *state, double df) nogil - double rk_vonmises(rk_state *state, double mu, double kappa) nogil - double rk_pareto(rk_state *state, double a) nogil - double rk_weibull(rk_state *state, double a) nogil - double rk_power(rk_state *state, double a) nogil - double rk_laplace(rk_state *state, double loc, double scale) nogil - double rk_gumbel(rk_state *state, double loc, double scale) nogil - double rk_logistic(rk_state *state, double loc, double scale) nogil - double rk_lognormal(rk_state *state, double mode, double sigma) nogil - double rk_rayleigh(rk_state *state, double mode) nogil - double rk_wald(rk_state *state, double mean, double scale) nogil - double rk_triangular(rk_state *state, double left, double mode, double right) nogil - - long rk_binomial(rk_state *state, long n, double p) nogil - long rk_binomial_btpe(rk_state *state, long n, double p) nogil - long rk_binomial_inversion(rk_state *state, long n, double p) nogil - long rk_negative_binomial(rk_state *state, double n, double p) nogil - long rk_poisson(rk_state *state, double lam) nogil - long rk_poisson_mult(rk_state *state, double lam) nogil - long rk_poisson_ptrs(rk_state *state, double lam) nogil - long rk_zipf(rk_state *state, double a) nogil - long rk_geometric(rk_state *state, double p) nogil - long rk_hypergeometric(rk_state *state, long good, long bad, long sample) nogil - long rk_logseries(rk_state *state, double p) nogil - -ctypedef double (* rk_cont0)(rk_state *state) nogil -ctypedef double (* rk_cont1)(rk_state *state, double a) nogil -ctypedef double (* rk_cont2)(rk_state *state, double a, double b) nogil -ctypedef double (* rk_cont3)(rk_state *state, double a, double b, double c) nogil - -ctypedef long (* rk_disc0)(rk_state *state) nogil -ctypedef long (* rk_discnp)(rk_state *state, long n, double p) nogil -ctypedef long (* rk_discdd)(rk_state *state, double n, double p) nogil -ctypedef long (* rk_discnmN)(rk_state *state, long n, long m, long N) nogil -ctypedef long (* rk_discd)(rk_state *state, double a) nogil - - -cdef extern from "initarray.h": - void init_by_array(rk_state *self, unsigned long *init_key, - npy_intp key_length) - -# Initialize numpy -import_array() - -cimport cython -import numpy as np -import operator -import warnings + ctypedef aug_bitgen aug_bitgen_t + + double legacy_gauss(aug_bitgen_t *aug_state) nogil + double legacy_pareto(aug_bitgen_t *aug_state, double a) nogil + double legacy_weibull(aug_bitgen_t *aug_state, double a) nogil + double legacy_standard_gamma(aug_bitgen_t *aug_state, double shape) nogil + double legacy_normal(aug_bitgen_t *aug_state, double loc, double scale) nogil + double legacy_standard_t(aug_bitgen_t *aug_state, double df) nogil + + double legacy_standard_exponential(aug_bitgen_t *aug_state) nogil + double legacy_power(aug_bitgen_t *aug_state, double a) nogil + double legacy_gamma(aug_bitgen_t *aug_state, double shape, double scale) nogil + double legacy_power(aug_bitgen_t *aug_state, double a) nogil + double legacy_chisquare(aug_bitgen_t *aug_state, double df) nogil + double legacy_rayleigh(aug_bitgen_t *aug_state, double mode) nogil + double legacy_noncentral_chisquare(aug_bitgen_t *aug_state, double df, + double nonc) nogil + double legacy_noncentral_f(aug_bitgen_t *aug_state, double dfnum, double dfden, + double nonc) nogil + double legacy_wald(aug_bitgen_t *aug_state, double mean, double scale) nogil + double legacy_lognormal(aug_bitgen_t *aug_state, double mean, double sigma) nogil + int64_t legacy_random_binomial(bitgen_t *bitgen_state, double p, + int64_t n, binomial_t *binomial) nogil + int64_t legacy_negative_binomial(aug_bitgen_t *aug_state, double n, double p) nogil + int64_t legacy_random_hypergeometric(bitgen_t *bitgen_state, int64_t good, int64_t bad, int64_t sample) nogil + int64_t legacy_logseries(bitgen_t *bitgen_state, double p) nogil + int64_t legacy_random_poisson(bitgen_t *bitgen_state, double lam) nogil + int64_t legacy_random_zipf(bitgen_t *bitgen_state, double a) nogil + int64_t legacy_random_geometric(bitgen_t *bitgen_state, double p) nogil + void legacy_random_multinomial(bitgen_t *bitgen_state, long n, long *mnix, double *pix, np.npy_intp d, binomial_t *binomial) nogil + double legacy_standard_cauchy(aug_bitgen_t *state) nogil + double legacy_beta(aug_bitgen_t *aug_state, double a, double b) nogil + double legacy_f(aug_bitgen_t *aug_state, double dfnum, double dfden) nogil + double legacy_exponential(aug_bitgen_t *aug_state, double scale) nogil + double legacy_power(aug_bitgen_t *state, double a) nogil + double legacy_vonmises(bitgen_t *bitgen_state, double mu, double kappa) nogil + +np.import_array() + +cdef object int64_to_long(object x): + """ + Convert int64 to long for legacy compatibility, which used long for integer + distributions + """ + cdef int64_t x64 -try: - from threading import Lock -except ImportError: - from dummy_threading import Lock - -cdef object cont0_array(rk_state *state, rk_cont0 func, object size, - object lock): - cdef double *array_data - cdef ndarray array "arrayObject" - cdef npy_intp length - cdef npy_intp i - - if size is None: - with lock, nogil: - rv = func(state) - return rv - else: - array = <ndarray>np.empty(size, np.float64) - length = PyArray_SIZE(array) - array_data = <double *>PyArray_DATA(array) - with lock, nogil: - for i from 0 <= i < length: - array_data[i] = func(state) - return array - - -cdef object cont1_array_sc(rk_state *state, rk_cont1 func, object size, double a, - object lock): - cdef double *array_data - cdef ndarray array "arrayObject" - cdef npy_intp length - cdef npy_intp i - - if size is None: - with lock, nogil: - rv = func(state, a) - return rv - else: - array = <ndarray>np.empty(size, np.float64) - length = PyArray_SIZE(array) - array_data = <double *>PyArray_DATA(array) - with lock, nogil: - for i from 0 <= i < length: - array_data[i] = func(state, a) - return array - -cdef object cont1_array(rk_state *state, rk_cont1 func, object size, - ndarray oa, object lock): - cdef double *array_data - cdef double *oa_data - cdef ndarray array "arrayObject" - cdef npy_intp length - cdef npy_intp i - cdef flatiter itera - cdef broadcast multi - - if size is None: - array = <ndarray>PyArray_SimpleNew(PyArray_NDIM(oa), - PyArray_DIMS(oa) , NPY_DOUBLE) - length = PyArray_SIZE(array) - array_data = <double *>PyArray_DATA(array) - itera = <flatiter>PyArray_IterNew(<object>oa) - with lock, nogil: - for i from 0 <= i < length: - array_data[i] = func(state, (<double *>PyArray_ITER_DATA(itera))[0]) - PyArray_ITER_NEXT(itera) - else: - array = <ndarray>np.empty(size, np.float64) - array_data = <double *>PyArray_DATA(array) - multi = <broadcast>PyArray_MultiIterNew(2, <void *>array, - <void *>oa) - if (multi.size != PyArray_SIZE(array)): - raise ValueError("size is not compatible with inputs") - with lock, nogil: - for i from 0 <= i < multi.size: - oa_data = <double *>PyArray_MultiIter_DATA(multi, 1) - array_data[i] = func(state, oa_data[0]) - PyArray_MultiIter_NEXTi(multi, 1) - return array - -cdef object cont2_array_sc(rk_state *state, rk_cont2 func, object size, double a, - double b, object lock): - cdef double *array_data - cdef ndarray array "arrayObject" - cdef npy_intp length - cdef npy_intp i - - if size is None: - with lock, nogil: - rv = func(state, a, b) - return rv - else: - array = <ndarray>np.empty(size, np.float64) - length = PyArray_SIZE(array) - array_data = <double *>PyArray_DATA(array) - with lock, nogil: - for i from 0 <= i < length: - array_data[i] = func(state, a, b) - return array - - -cdef object cont2_array(rk_state *state, rk_cont2 func, object size, - ndarray oa, ndarray ob, object lock): - cdef double *array_data - cdef double *oa_data - cdef double *ob_data - cdef ndarray array "arrayObject" - cdef npy_intp i - cdef broadcast multi - - if size is None: - multi = <broadcast>np.broadcast(oa, ob) - array = <ndarray>np.empty(multi.shape, dtype=np.float64) - else: - array = <ndarray>np.empty(size, dtype=np.float64) - multi = <broadcast>np.broadcast(oa, ob, array) - if multi.shape != array.shape: - raise ValueError("size is not compatible with inputs") - - array_data = <double *>PyArray_DATA(array) - - with lock, nogil: - for i in range(multi.size): - oa_data = <double *>PyArray_MultiIter_DATA(multi, 0) - ob_data = <double *>PyArray_MultiIter_DATA(multi, 1) - array_data[i] = func(state, oa_data[0], ob_data[0]) - PyArray_MultiIter_NEXT(multi) - - return array - -cdef object cont3_array_sc(rk_state *state, rk_cont3 func, object size, double a, - double b, double c, object lock): - - cdef double *array_data - cdef ndarray array "arrayObject" - cdef npy_intp length - cdef npy_intp i - - if size is None: - with lock, nogil: - rv = func(state, a, b, c) - return rv - else: - array = <ndarray>np.empty(size, np.float64) - length = PyArray_SIZE(array) - array_data = <double *>PyArray_DATA(array) - with lock, nogil: - for i from 0 <= i < length: - array_data[i] = func(state, a, b, c) - return array - -cdef object cont3_array(rk_state *state, rk_cont3 func, object size, - ndarray oa, ndarray ob, ndarray oc, object lock): - - cdef double *array_data - cdef double *oa_data - cdef double *ob_data - cdef double *oc_data - cdef ndarray array "arrayObject" - cdef npy_intp i - cdef broadcast multi - - if size is None: - multi = <broadcast>np.broadcast(oa, ob, oc) - array = <ndarray>np.empty(multi.shape, dtype=np.float64) - else: - array = <ndarray>np.empty(size, dtype=np.float64) - multi = <broadcast>np.broadcast(oa, ob, oc, array) - if multi.shape != array.shape: - raise ValueError("size is not compatible with inputs") - - array_data = <double *>PyArray_DATA(array) - - with lock, nogil: - for i in range(multi.size): - oa_data = <double *>PyArray_MultiIter_DATA(multi, 0) - ob_data = <double *>PyArray_MultiIter_DATA(multi, 1) - oc_data = <double *>PyArray_MultiIter_DATA(multi, 2) - array_data[i] = func(state, oa_data[0], ob_data[0], oc_data[0]) - PyArray_MultiIter_NEXT(multi) - - return array - -cdef object disc0_array(rk_state *state, rk_disc0 func, object size, object lock): - cdef long *array_data - cdef ndarray array "arrayObject" - cdef npy_intp length - cdef npy_intp i - - if size is None: - with lock, nogil: - rv = func(state) - return rv - else: - array = <ndarray>np.empty(size, int) - length = PyArray_SIZE(array) - array_data = <long *>PyArray_DATA(array) - with lock, nogil: - for i from 0 <= i < length: - array_data[i] = func(state) - return array - -cdef object discnp_array_sc(rk_state *state, rk_discnp func, object size, - long n, double p, object lock): - cdef long *array_data - cdef ndarray array "arrayObject" - cdef npy_intp length - cdef npy_intp i - - if size is None: - with lock, nogil: - rv = func(state, n, p) - return rv - else: - array = <ndarray>np.empty(size, int) - length = PyArray_SIZE(array) - array_data = <long *>PyArray_DATA(array) - with lock, nogil: - for i from 0 <= i < length: - array_data[i] = func(state, n, p) - return array - -cdef object discnp_array(rk_state *state, rk_discnp func, object size, - ndarray on, ndarray op, object lock): - cdef long *array_data - cdef ndarray array "arrayObject" - cdef npy_intp i - cdef double *op_data - cdef long *on_data - cdef broadcast multi - - if size is None: - multi = <broadcast>np.broadcast(on, op) - array = <ndarray>np.empty(multi.shape, dtype=int) - else: - array = <ndarray>np.empty(size, dtype=int) - multi = <broadcast>np.broadcast(on, op, array) - if multi.shape != array.shape: - raise ValueError("size is not compatible with inputs") - - array_data = <long *>PyArray_DATA(array) - - with lock, nogil: - for i in range(multi.size): - on_data = <long *>PyArray_MultiIter_DATA(multi, 0) - op_data = <double *>PyArray_MultiIter_DATA(multi, 1) - array_data[i] = func(state, on_data[0], op_data[0]) - PyArray_MultiIter_NEXT(multi) - - return array - -cdef object discdd_array_sc(rk_state *state, rk_discdd func, object size, - double n, double p, object lock): - cdef long *array_data - cdef ndarray array "arrayObject" - cdef npy_intp length - cdef npy_intp i - - if size is None: - with lock, nogil: - rv = func(state, n, p) - return rv - else: - array = <ndarray>np.empty(size, int) - length = PyArray_SIZE(array) - array_data = <long *>PyArray_DATA(array) - with lock, nogil: - for i from 0 <= i < length: - array_data[i] = func(state, n, p) - return array - -cdef object discdd_array(rk_state *state, rk_discdd func, object size, - ndarray on, ndarray op, object lock): - cdef long *array_data - cdef ndarray array "arrayObject" - cdef npy_intp i - cdef double *op_data - cdef double *on_data - cdef broadcast multi - - if size is None: - multi = <broadcast>np.broadcast(on, op) - array = <ndarray>np.empty(multi.shape, dtype=int) - else: - array = <ndarray>np.empty(size, dtype=int) - multi = <broadcast>np.broadcast(on, op, array) - if multi.shape != array.shape: - raise ValueError("size is not compatible with inputs") - - array_data = <long *>PyArray_DATA(array) - - with lock, nogil: - for i in range(multi.size): - on_data = <double *>PyArray_MultiIter_DATA(multi, 0) - op_data = <double *>PyArray_MultiIter_DATA(multi, 1) - array_data[i] = func(state, on_data[0], op_data[0]) - PyArray_MultiIter_NEXT(multi) - - return array - -cdef object discnmN_array_sc(rk_state *state, rk_discnmN func, object size, - long n, long m, long N, object lock): - cdef long *array_data - cdef ndarray array "arrayObject" - cdef npy_intp length - cdef npy_intp i - - if size is None: - with lock, nogil: - rv = func(state, n, m, N) - return rv - else: - array = <ndarray>np.empty(size, int) - length = PyArray_SIZE(array) - array_data = <long *>PyArray_DATA(array) - with lock, nogil: - for i from 0 <= i < length: - array_data[i] = func(state, n, m, N) - return array - -cdef object discnmN_array(rk_state *state, rk_discnmN func, object size, - ndarray on, ndarray om, ndarray oN, object lock): - cdef long *array_data - cdef long *on_data - cdef long *om_data - cdef long *oN_data - cdef ndarray array "arrayObject" - cdef npy_intp i - cdef broadcast multi - - if size is None: - multi = <broadcast>np.broadcast(on, om, oN) - array = <ndarray>np.empty(multi.shape, dtype=int) - else: - array = <ndarray>np.empty(size, dtype=int) - multi = <broadcast>np.broadcast(on, om, oN, array) - if multi.shape != array.shape: - raise ValueError("size is not compatible with inputs") - - array_data = <long *>PyArray_DATA(array) - - with lock, nogil: - for i in range(multi.size): - on_data = <long *>PyArray_MultiIter_DATA(multi, 0) - om_data = <long *>PyArray_MultiIter_DATA(multi, 1) - oN_data = <long *>PyArray_MultiIter_DATA(multi, 2) - array_data[i] = func(state, on_data[0], om_data[0], oN_data[0]) - PyArray_MultiIter_NEXT(multi) - - return array - -cdef object discd_array_sc(rk_state *state, rk_discd func, object size, - double a, object lock): - cdef long *array_data - cdef ndarray array "arrayObject" - cdef npy_intp length - cdef npy_intp i - - if size is None: - with lock, nogil: - rv = func(state, a) - return rv - else: - array = <ndarray>np.empty(size, int) - length = PyArray_SIZE(array) - array_data = <long *>PyArray_DATA(array) - with lock, nogil: - for i from 0 <= i < length: - array_data[i] = func(state, a) - return array - -cdef object discd_array(rk_state *state, rk_discd func, object size, ndarray oa, - object lock): - cdef long *array_data - cdef double *oa_data - cdef ndarray array "arrayObject" - cdef npy_intp length - cdef npy_intp i - cdef broadcast multi - cdef flatiter itera - - if size is None: - array = <ndarray>PyArray_SimpleNew(PyArray_NDIM(oa), - PyArray_DIMS(oa), NPY_LONG) - length = PyArray_SIZE(array) - array_data = <long *>PyArray_DATA(array) - itera = <flatiter>PyArray_IterNew(<object>oa) - with lock, nogil: - for i from 0 <= i < length: - array_data[i] = func(state, (<double *>PyArray_ITER_DATA(itera))[0]) - PyArray_ITER_NEXT(itera) - else: - array = <ndarray>np.empty(size, int) - array_data = <long *>PyArray_DATA(array) - multi = <broadcast>PyArray_MultiIterNew(2, <void *>array, <void *>oa) - if (multi.size != PyArray_SIZE(array)): - raise ValueError("size is not compatible with inputs") - with lock, nogil: - for i from 0 <= i < multi.size: - oa_data = <double *>PyArray_MultiIter_DATA(multi, 1) - array_data[i] = func(state, oa_data[0]) - PyArray_MultiIter_NEXTi(multi, 1) - return array - -cdef double kahan_sum(double *darr, npy_intp n): - cdef double c, y, t, sum - cdef npy_intp i - sum = darr[0] - c = 0.0 - for i from 1 <= i < n: - y = darr[i] - c - t = sum + y - c = (t-sum) - y - sum = t - return sum - -def _shape_from_size(size, d): - if size is None: - shape = (d,) - else: - try: - shape = (operator.index(size), d) - except TypeError: - shape = tuple(size) + (d,) - return shape - -# Look up table for randint functions keyed by type name. The stored data -# is a tuple (lbnd, ubnd, func), where lbnd is the smallest value for the -# type, ubnd is one greater than the largest value, and func is the -# function to call. -_randint_type = { - 'bool': (0, 2, _rand_bool), - 'int8': (-2**7, 2**7, _rand_int8), - 'int16': (-2**15, 2**15, _rand_int16), - 'int32': (-2**31, 2**31, _rand_int32), - 'int64': (-2**63, 2**63, _rand_int64), - 'uint8': (0, 2**8, _rand_uint8), - 'uint16': (0, 2**16, _rand_uint16), - 'uint32': (0, 2**32, _rand_uint32), - 'uint64': (0, 2**64, _rand_uint64) - } + if np.isscalar(x): + x64 = x + return <long>x64 + return x.astype('l', casting='unsafe') cdef class RandomState: """ RandomState(seed=None) - Container for the Mersenne Twister pseudo-random number generator. - - `RandomState` exposes a number of methods for generating random numbers - drawn from a variety of probability distributions. In addition to the - distribution-specific arguments, each method takes a keyword argument - `size` that defaults to ``None``. If `size` is ``None``, then a single - value is generated and returned. If `size` is an integer, then a 1-D - array filled with generated values is returned. If `size` is a tuple, - then an array with that shape is filled and returned. - - *Compatibility Guarantee* - A fixed seed and a fixed series of calls to 'RandomState' methods using - the same parameters will always produce the same results up to roundoff - error except when the values were incorrect. Incorrect values will be - fixed and the NumPy version in which the fix was made will be noted in - the relevant docstring. Extension of existing parameter ranges and the - addition of new parameters is allowed as long the previous behavior - remains unchanged. + Container for the slow Mersenne Twister pseudo-random number generator. + Consider using a different BitGenerator with the Generator container + instead. + + `RandomState` and `Generator` expose a number of methods for generating + random numbers drawn from a variety of probability distributions. In + addition to the distribution-specific arguments, each method takes a + keyword argument `size` that defaults to ``None``. If `size` is ``None``, + then a single value is generated and returned. If `size` is an integer, + then a 1-D array filled with generated values is returned. If `size` is a + tuple, then an array with that shape is filled and returned. + + **Compatibility Guarantee** + + A fixed bit generator using a fixed seed and a fixed series of calls to + 'RandomState' methods using the same parameters will always produce the + same results up to roundoff error except when the values were incorrect. + `RandomState` is effectively frozen and will only receive updates that + are required by changes in the the internals of Numpy. More substantial + changes, including algorithmic improvements, are reserved for + `Generator`. Parameters ---------- - seed : {None, int, array_like}, optional - Random seed used to initialize the pseudo-random number generator. Can - be any integer between 0 and 2**32 - 1 inclusive, an array (or other - sequence) of such integers, or ``None`` (the default). If `seed` is - ``None``, then `RandomState` will try to read data from - ``/dev/urandom`` (or the Windows analogue) if available or seed from - the clock otherwise. + seed : {None, int, array_like, BitGenerator}, optional + Random seed used to initialize the pseudo-random number generator or + an instantized BitGenerator. If an integer or array, used as a seed for + the MT19937 BitGenerator. Values can be any integer between 0 and + 2**32 - 1 inclusive, an array (or other sequence) of such integers, + or ``None`` (the default). If `seed` is ``None``, then the `MT19937` + BitGenerator is initialized by reading data from ``/dev/urandom`` + (or the Windows analogue) if available or seed from the clock + otherwise. Notes ----- @@ -631,70 +163,90 @@ cdef class RandomState: NumPy-aware, has the advantage that it provides a much larger number of probability distributions to choose from. + See Also + -------- + Generator + MT19937 + numpy.random.BitGenerator + """ - cdef rk_state *internal_state + cdef public object _bit_generator + cdef bitgen_t _bitgen + cdef aug_bitgen_t _aug_state + cdef binomial_t _binomial cdef object lock - cdef object state_address - poisson_lam_max = np.iinfo('l').max - np.sqrt(np.iinfo('l').max)*10 + _poisson_lam_max = POISSON_LAM_MAX def __init__(self, seed=None): - self.internal_state = <rk_state*>PyMem_Malloc(sizeof(rk_state)) - self.state_address = PyCapsule_New(self.internal_state, NULL, NULL) - self.lock = Lock() - self.seed(seed) + if seed is None: + bit_generator = _MT19937() + elif not hasattr(seed, 'capsule'): + bit_generator = _MT19937() + bit_generator._legacy_seeding(seed) + else: + bit_generator = seed + + self._bit_generator = bit_generator + capsule = bit_generator.capsule + cdef const char *name = "BitGenerator" + if not PyCapsule_IsValid(capsule, name): + raise ValueError("Invalid bit generator. The bit generator must " + "be instantized.") + self._bitgen = (<bitgen_t *> PyCapsule_GetPointer(capsule, name))[0] + self._aug_state.bit_generator = &self._bitgen + self._reset_gauss() + self.lock = bit_generator.lock + + def __repr__(self): + return self.__str__() + ' at 0x{:X}'.format(id(self)) + + def __str__(self): + _str = self.__class__.__name__ + _str += '(' + self._bit_generator.__class__.__name__ + ')' + return _str - def __dealloc__(self): - if self.internal_state != NULL: - PyMem_Free(self.internal_state) - self.internal_state = NULL + # Pickling support: + def __getstate__(self): + return self.get_state(legacy=False) + + def __setstate__(self, state): + self.set_state(state) + + def __reduce__(self): + state = self.get_state(legacy=False) + from ._pickle import __randomstate_ctor + return __randomstate_ctor, (state['bit_generator'],), state + + cdef _reset_gauss(self): + self._aug_state.has_gauss = 0 + self._aug_state.gauss = 0.0 def seed(self, seed=None): """ - seed(seed=None) - - Seed the generator. + seed(self, seed=None) - This method is called when `RandomState` is initialized. It can be - called again to re-seed the generator. For details, see `RandomState`. + Reseed a legacy MT19937 BitGenerator - Parameters - ---------- - seed : int or 1-d array_like, optional - Seed for `RandomState`. - Must be convertible to 32 bit unsigned integers. + Notes + ----- + This is a convenience, legacy function. - See Also - -------- - RandomState + The best practice is to **not** reseed a BitGenerator, rather to + recreate a new one. This method is here for legacy reasons. + This example demonstrates best practice. + >>> from numpy.random import MT19937 + >>> from numpy.random import RandomState, SeedSequence + >>> rs = RandomState(MT19937(SeedSequence(123456789))) + # Later, you want to restart the stream + >>> rs = RandomState(MT19937(SeedSequence(987654321))) """ - cdef rk_error errcode - cdef ndarray obj "arrayObject_obj" - try: - if seed is None: - with self.lock: - errcode = rk_randomseed(self.internal_state) - else: - idx = operator.index(seed) - if (idx >= 2**32) or (idx < 0): - raise ValueError("Seed must be between 0 and 2**32 - 1") - with self.lock: - rk_seed(idx, self.internal_state) - except TypeError: - obj = np.asarray(seed) - if obj.size == 0: - raise ValueError("Seed must be non-empty") - obj = obj.astype(np.int64, casting='safe') - if obj.ndim != 1: - raise ValueError("Seed array must be 1-d") - if ((obj >= 2**32) | (obj < 0)).any(): - raise ValueError("Seed values must be between 0 and 2**32 - 1") - obj = obj.astype('L', casting='unsafe') - with self.lock: - init_by_array(self.internal_state, <unsigned long *>PyArray_DATA(obj), - PyArray_DIM(obj, 0)) + if not isinstance(self._bit_generator, _MT19937): + raise TypeError('can only re-seed a MT19937 BitGenerator') + self._bit_generator._legacy_seeding(seed) + self._reset_gauss() - def get_state(self): + def get_state(self, legacy=True): """ get_state() @@ -702,9 +254,15 @@ cdef class RandomState: For more details, see `set_state`. + Parameters + ---------- + legacy : bool, optional + Flag indicating to return a legacy tuple state when the BitGenerator + is MT19937, instead of a dict. + Returns ------- - out : tuple(str, ndarray of 624 uints, int, int, float) + out : {tuple(str, ndarray of 624 uints, int, int, float), dict} The returned tuple has the following items: 1. the string 'MT19937'. @@ -713,6 +271,9 @@ cdef class RandomState: 4. an integer ``has_gauss``. 5. a float ``cached_gaussian``. + If `legacy` is False, or the BitGenerator is not MT19937, then + state is returned as a dictionary. + See Also -------- set_state @@ -724,15 +285,18 @@ cdef class RandomState: the user should know exactly what he/she is doing. """ - cdef ndarray state "arrayObject_state" - state = <ndarray>np.empty(624, np.uint) - with self.lock: - memcpy(<void*>PyArray_DATA(state), <void*>(self.internal_state.key), 624*sizeof(long)) - has_gauss = self.internal_state.has_gauss - gauss = self.internal_state.gauss - pos = self.internal_state.pos - state = <ndarray>np.asarray(state, np.uint32) - return ('MT19937', state, pos, has_gauss, gauss) + st = self._bit_generator.state + if st['bit_generator'] != 'MT19937' and legacy: + warnings.warn('get_state and legacy can only be used with the ' + 'MT19937 BitGenerator. To silence this warning, ' + 'set `legacy` to False.', RuntimeWarning) + legacy = False + st['has_gauss'] = self._aug_state.has_gauss + st['gauss'] = self._aug_state.gauss + if legacy: + return (st['bit_generator'], st['state']['key'], st['state']['pos'], + st['has_gauss'], st['gauss']) + return st def set_state(self, state): """ @@ -740,12 +304,14 @@ cdef class RandomState: Set the internal state of the generator from a tuple. - For use if one has reason to manually (re-)set the internal state of the - "Mersenne Twister"[1]_ pseudo-random number generating algorithm. + For use if one has reason to manually (re-)set the internal state of + the bit generator used by the RandomState instance. By default, + RandomState uses the "Mersenne Twister"[1]_ pseudo-random number + generating algorithm. Parameters ---------- - state : tuple(str, ndarray of 624 uints, int, int, float) + state : {tuple(str, ndarray of 624 uints, int, int, float), dict} The `state` tuple has the following items: 1. the string 'MT19937', specifying the Mersenne Twister algorithm. @@ -754,6 +320,9 @@ cdef class RandomState: 4. an integer ``has_gauss``. 5. a float ``cached_gaussian``. + If state is a dictionary, it is directly set using the BitGenerators + `state` property. + Returns ------- out : None @@ -781,41 +350,27 @@ cdef class RandomState: Vol. 8, No. 1, pp. 3-30, Jan. 1998. """ - cdef ndarray obj "arrayObject_obj" - cdef int pos - algorithm_name = state[0] - if algorithm_name != 'MT19937': - raise ValueError("algorithm must be 'MT19937'") - key, pos = state[1:3] - if len(state) == 3: - has_gauss = 0 - cached_gaussian = 0.0 + if isinstance(state, dict): + if 'bit_generator' not in state or 'state' not in state: + raise ValueError('state dictionary is not valid.') + st = state else: - has_gauss, cached_gaussian = state[3:5] - try: - obj = <ndarray>PyArray_ContiguousFromObject(key, NPY_ULONG, 1, 1) - except TypeError: - # compatibility -- could be an older pickle - obj = <ndarray>PyArray_ContiguousFromObject(key, NPY_LONG, 1, 1) - if PyArray_DIM(obj, 0) != 624: - raise ValueError("state must be 624 longs") - with self.lock: - memcpy(<void*>(self.internal_state.key), <void*>PyArray_DATA(obj), 624*sizeof(long)) - self.internal_state.pos = pos - self.internal_state.has_gauss = has_gauss - self.internal_state.gauss = cached_gaussian - - # Pickling support: - def __getstate__(self): - return self.get_state() - - def __setstate__(self, state): - self.set_state(state) + if not isinstance(state, (tuple, list)): + raise TypeError('state must be a dict or a tuple.') + if state[0] != 'MT19937': + raise ValueError('set_state can only be used with legacy MT19937' + 'state instances.') + st = {'bit_generator': state[0], + 'state': {'key': state[1], 'pos': state[2]}} + if len(state) > 3: + st['has_gauss'] = state[3] + st['gauss'] = state[4] + value = st + + self._aug_state.gauss = st.get('gauss', 0.0) + self._aug_state.has_gauss = st.get('has_gauss', 0) + self._bit_generator.state = st - def __reduce__(self): - return (np.random.__RandomState_ctor, (), self.get_state()) - - # Basic distributions: def random_sample(self, size=None): """ random_sample(size=None) @@ -828,6 +383,10 @@ cdef class RandomState: (b - a) * random_sample() + a + .. note:: + New code should use the ``random`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. + Parameters ---------- size : int or tuple of ints, optional @@ -841,33 +400,198 @@ cdef class RandomState: Array of random floats of shape `size` (unless ``size=None``, in which case a single float is returned). + See Also + -------- + Generator.random: which should be used for new code. + Examples -------- >>> np.random.random_sample() - 0.47108547995356098 + 0.47108547995356098 # random >>> type(np.random.random_sample()) - <type 'float'> + <class 'float'> >>> np.random.random_sample((5,)) - array([ 0.30220482, 0.86820401, 0.1654503 , 0.11659149, 0.54323428]) + array([ 0.30220482, 0.86820401, 0.1654503 , 0.11659149, 0.54323428]) # random Three-by-two array of random numbers from [-5, 0): >>> 5 * np.random.random_sample((3, 2)) - 5 - array([[-3.99149989, -0.52338984], + array([[-3.99149989, -0.52338984], # random [-2.99091858, -0.79479508], [-1.23204345, -1.75224494]]) """ - return cont0_array(self.internal_state, rk_double, size, self.lock) + cdef double temp + return double_fill(&random_standard_uniform_fill, &self._bitgen, size, self.lock, None) + + def random(self, size=None): + """ + random(size=None) + + Return random floats in the half-open interval [0.0, 1.0). Alias for + `random_sample` to ease forward-porting to the new random API. + """ + return self.random_sample(size=size) + + def beta(self, a, b, size=None): + """ + beta(a, b, size=None) + + Draw samples from a Beta distribution. + + The Beta distribution is a special case of the Dirichlet distribution, + and is related to the Gamma distribution. It has the probability + distribution function + + .. math:: f(x; a,b) = \\frac{1}{B(\\alpha, \\beta)} x^{\\alpha - 1} + (1 - x)^{\\beta - 1}, + + where the normalization, B, is the beta function, + + .. math:: B(\\alpha, \\beta) = \\int_0^1 t^{\\alpha - 1} + (1 - t)^{\\beta - 1} dt. + + It is often seen in Bayesian inference and order statistics. + + .. note:: + New code should use the ``beta`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. + + Parameters + ---------- + a : float or array_like of floats + Alpha, positive (>0). + b : float or array_like of floats + Beta, positive (>0). + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``a`` and ``b`` are both scalars. + Otherwise, ``np.broadcast(a, b).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized beta distribution. + + See Also + -------- + Generator.beta: which should be used for new code. + """ + return cont(&legacy_beta, &self._aug_state, size, self.lock, 2, + a, 'a', CONS_POSITIVE, + b, 'b', CONS_POSITIVE, + 0.0, '', CONS_NONE, None) + + def exponential(self, scale=1.0, size=None): + """ + exponential(scale=1.0, size=None) + + Draw samples from an exponential distribution. + + Its probability density function is + + .. math:: f(x; \\frac{1}{\\beta}) = \\frac{1}{\\beta} \\exp(-\\frac{x}{\\beta}), + + for ``x > 0`` and 0 elsewhere. :math:`\\beta` is the scale parameter, + which is the inverse of the rate parameter :math:`\\lambda = 1/\\beta`. + The rate parameter is an alternative, widely used parameterization + of the exponential distribution [3]_. + + The exponential distribution is a continuous analogue of the + geometric distribution. It describes many common situations, such as + the size of raindrops measured over many rainstorms [1]_, or the time + between page requests to Wikipedia [2]_. + + .. note:: + New code should use the ``exponential`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. + + Parameters + ---------- + scale : float or array_like of floats + The scale parameter, :math:`\\beta = 1/\\lambda`. Must be + non-negative. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``scale`` is a scalar. Otherwise, + ``np.array(scale).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized exponential distribution. + + See Also + -------- + Generator.exponential: which should be used for new code. + + References + ---------- + .. [1] Peyton Z. Peebles Jr., "Probability, Random Variables and + Random Signal Principles", 4th ed, 2001, p. 57. + .. [2] Wikipedia, "Poisson process", + https://en.wikipedia.org/wiki/Poisson_process + .. [3] Wikipedia, "Exponential distribution", + https://en.wikipedia.org/wiki/Exponential_distribution + + """ + return cont(&legacy_exponential, &self._aug_state, size, self.lock, 1, + scale, 'scale', CONS_NON_NEGATIVE, + 0.0, '', CONS_NONE, + 0.0, '', CONS_NONE, + None) + + def standard_exponential(self, size=None): + """ + standard_exponential(size=None) + + Draw samples from the standard exponential distribution. + + `standard_exponential` is identical to the exponential distribution + with a scale parameter of 1. + + .. note:: + New code should use the ``standard_exponential`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. + + Parameters + ---------- + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. Default is None, in which case a + single value is returned. + + Returns + ------- + out : float or ndarray + Drawn samples. + + See Also + -------- + Generator.standard_exponential: which should be used for new code. + + Examples + -------- + Output a 3x8000 array: + + >>> n = np.random.standard_exponential((3, 8000)) + + """ + return cont(&legacy_standard_exponential, &self._aug_state, size, self.lock, 0, + None, None, CONS_NONE, + None, None, CONS_NONE, + None, None, CONS_NONE, + None) def tomaxint(self, size=None): """ tomaxint(size=None) - Random integers between 0 and ``sys.maxint``, inclusive. - Return a sample of uniformly distributed random integers in the interval - [0, ``sys.maxint``]. + [0, ``np.iinfo(np.int_).max``]. The `np.int_` type translates to the C long + integer type and its precision is platform dependent. Parameters ---------- @@ -889,27 +613,39 @@ cdef class RandomState: Examples -------- - >>> RS = np.random.mtrand.RandomState() # need a RandomState object - >>> RS.tomaxint((2,2,2)) - array([[[1170048599, 1600360186], + >>> rs = np.random.RandomState() # need a RandomState object + >>> rs.tomaxint((2,2,2)) + array([[[1170048599, 1600360186], # random [ 739731006, 1947757578]], [[1871712945, 752307660], [1601631370, 1479324245]]]) - >>> import sys - >>> sys.maxint - 2147483647 - >>> RS.tomaxint((2,2,2)) < sys.maxint + >>> rs.tomaxint((2,2,2)) < np.iinfo(np.int_).max array([[[ True, True], [ True, True]], [[ True, True], [ True, True]]]) """ - return disc0_array(self.internal_state, rk_long, size, self.lock) + cdef np.npy_intp n + cdef np.ndarray randoms + cdef int64_t *randoms_data + + if size is None: + with self.lock: + return random_positive_int(&self._bitgen) + + randoms = <np.ndarray>np.empty(size, dtype=np.int64) + randoms_data = <int64_t*>np.PyArray_DATA(randoms) + n = np.PyArray_SIZE(randoms) + + for i in range(n): + with self.lock, nogil: + randoms_data[i] = random_positive_int(&self._bitgen) + return randoms def randint(self, low, high=None, size=None, dtype=int): """ - randint(low, high=None, size=None, dtype='l') + randint(low, high=None, size=None, dtype=int) Return random integers from `low` (inclusive) to `high` (exclusive). @@ -917,24 +653,27 @@ cdef class RandomState: the specified dtype in the "half-open" interval [`low`, `high`). If `high` is None (the default), then results are from [0, `low`). + .. note:: + New code should use the ``integers`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. + Parameters ---------- - low : int - Lowest (signed) integer to be drawn from the distribution (unless + low : int or array-like of ints + Lowest (signed) integers to be drawn from the distribution (unless ``high=None``, in which case this parameter is one above the *highest* such integer). - high : int, optional + high : int or array-like of ints, optional If provided, one above the largest (signed) integer to be drawn from the distribution (see above for behavior if ``high=None``). + If array-like, must contain integer values size : int or tuple of ints, optional Output shape. If the given shape is, e.g., ``(m, n, k)``, then ``m * n * k`` samples are drawn. Default is None, in which case a single value is returned. dtype : dtype, optional - Desired dtype of the result. All dtypes are determined by their - name, i.e., 'int64', 'int', etc, so byteorder is not available - and a specific precision may have different C types depending - on the platform. The default value is 'np.int'. + Desired dtype of the result. Byteorder must be native. + The default value is int. .. versionadded:: 1.11.0 @@ -946,67 +685,99 @@ cdef class RandomState: See Also -------- - random.random_integers : similar to `randint`, only for the closed + random_integers : similar to `randint`, only for the closed interval [`low`, `high`], and 1 is the lowest value if `high` is - omitted. In particular, this other one is the one to use to generate - uniformly distributed discrete non-integers. + omitted. + Generator.integers: which should be used for new code. Examples -------- >>> np.random.randint(2, size=10) - array([1, 0, 0, 0, 1, 1, 0, 0, 1, 0]) + array([1, 0, 0, 0, 1, 1, 0, 0, 1, 0]) # random >>> np.random.randint(1, size=10) array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) Generate a 2 x 4 array of ints between 0 and 4, inclusive: >>> np.random.randint(5, size=(2, 4)) - array([[4, 0, 2, 1], + array([[4, 0, 2, 1], # random [3, 2, 2, 0]]) - """ - if high is None: - high = low - low = 0 + Generate a 1 x 3 array with 3 different upper bounds - # '_randint_type' is defined in - # 'generate_randint_helpers.py' - key = np.dtype(dtype).name - if key not in _randint_type: - raise TypeError('Unsupported dtype "%s" for randint' % key) + >>> np.random.randint(1, [3, 5, 10]) + array([2, 2, 9]) # random - lowbnd, highbnd, randfunc = _randint_type[key] + Generate a 1 by 3 array with 3 different lower bounds - # TODO: Do not cast these inputs to Python int - # - # This is a workaround until gh-8851 is resolved (bug in NumPy - # integer comparison and subtraction involving uint64 and non- - # uint64). Afterwards, remove these two lines. - ilow = int(low) - ihigh = int(high) + >>> np.random.randint([1, 5, 7], 10) + array([9, 8, 7]) # random - if ilow < lowbnd: - raise ValueError("low is out of bounds for %s" % (key,)) - if ihigh > highbnd: - raise ValueError("high is out of bounds for %s" % (key,)) - if ilow >= ihigh: - raise ValueError("low >= high") + Generate a 2 by 4 array using broadcasting with dtype of uint8 - with self.lock: - ret = randfunc(ilow, ihigh - 1, size, self.state_address) + >>> np.random.randint([1, 3, 5, 7], [[10], [20]], dtype=np.uint8) + array([[ 8, 6, 9, 7], # random + [ 1, 16, 9, 12]], dtype=uint8) + """ - if size is None: - if dtype in (np.bool, np.int, np.long): - return dtype(ret) + if high is None: + high = low + low = 0 - return ret + _dtype = np.dtype(dtype) + + if not _dtype.isnative: + # numpy 1.17.0, 2019-05-28 + warnings.warn('Providing a dtype with a non-native byteorder is ' + 'not supported. If you require platform-independent ' + 'byteorder, call byteswap when required.\nIn future ' + 'version, providing byteorder will raise a ' + 'ValueError', DeprecationWarning) + _dtype = _dtype.newbyteorder() + + # Implementation detail: the use a masked method to generate + # bounded uniform integers. Lemire's method is preferable since it is + # faster. randomgen allows a choice, we will always use the slower but + # backward compatible one. + cdef bint _masked = True + cdef bint _endpoint = False + + if _dtype == np.int32: + ret = _rand_int32(low, high, size, _masked, _endpoint, &self._bitgen, self.lock) + elif _dtype == np.int64: + ret = _rand_int64(low, high, size, _masked, _endpoint, &self._bitgen, self.lock) + elif _dtype == np.int16: + ret = _rand_int16(low, high, size, _masked, _endpoint, &self._bitgen, self.lock) + elif _dtype == np.int8: + ret = _rand_int8(low, high, size, _masked, _endpoint, &self._bitgen, self.lock) + elif _dtype == np.uint64: + ret = _rand_uint64(low, high, size, _masked, _endpoint, &self._bitgen, self.lock) + elif _dtype == np.uint32: + ret = _rand_uint32(low, high, size, _masked, _endpoint, &self._bitgen, self.lock) + elif _dtype == np.uint16: + ret = _rand_uint16(low, high, size, _masked, _endpoint, &self._bitgen, self.lock) + elif _dtype == np.uint8: + ret = _rand_uint8(low, high, size, _masked, _endpoint, &self._bitgen, self.lock) + elif _dtype == np.bool_: + ret = _rand_bool(low, high, size, _masked, _endpoint, &self._bitgen, self.lock) + else: + raise TypeError('Unsupported dtype %r for randint' % _dtype) - def bytes(self, npy_intp length): + if size is None and dtype in (bool, int): + if np.array(ret).shape == (): + return dtype(ret) + return ret + + def bytes(self, np.npy_intp length): """ bytes(length) Return random bytes. + .. note:: + New code should use the ``bytes`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. + Parameters ---------- length : int @@ -1014,53 +785,61 @@ cdef class RandomState: Returns ------- - out : str + out : bytes String of length `length`. + See Also + -------- + Generator.bytes: which should be used for new code. + Examples -------- >>> np.random.bytes(10) - ' eh\\x85\\x022SZ\\xbf\\xa4' #random - + b' eh\\x85\\x022SZ\\xbf\\xa4' #random """ - cdef void *bytes - bytestring = empty_py_bytes(length, &bytes) - with self.lock, nogil: - rk_fill(bytes, length, self.internal_state) - return bytestring - + cdef Py_ssize_t n_uint32 = ((length - 1) // 4 + 1) + # Interpret the uint32s as little-endian to convert them to bytes + # consistently. + return self.randint(0, 4294967296, size=n_uint32, + dtype=np.uint32).astype('<u4').tobytes()[:length] + @cython.wraparound(True) def choice(self, a, size=None, replace=True, p=None): """ choice(a, size=None, replace=True, p=None) Generates a random sample from a given 1-D array - .. versionadded:: 1.7.0 + .. versionadded:: 1.7.0 + + .. note:: + New code should use the ``choice`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. Parameters - ----------- + ---------- a : 1-D array-like or int If an ndarray, a random sample is generated from its elements. - If an int, the random sample is generated as if a were np.arange(a) + If an int, the random sample is generated as if it were ``np.arange(a)`` size : int or tuple of ints, optional Output shape. If the given shape is, e.g., ``(m, n, k)``, then ``m * n * k`` samples are drawn. Default is None, in which case a single value is returned. replace : boolean, optional - Whether the sample is with or without replacement + Whether the sample is with or without replacement. Default is True, + meaning that a value of ``a`` can be selected multiple times. p : 1-D array-like, optional The probabilities associated with each entry in a. - If not given the sample assumes a uniform distribution over all - entries in a. + If not given, the sample assumes a uniform distribution over all + entries in ``a``. Returns - -------- + ------- samples : single item or ndarray The generated random samples Raises - ------- + ------ ValueError If a is an int and less than zero, if a or p are not 1-dimensional, if a is an array-like of size 0, if p is not a vector of @@ -1069,42 +848,52 @@ cdef class RandomState: size See Also - --------- + -------- randint, shuffle, permutation + Generator.choice: which should be used in new code + + Notes + ----- + Setting user-specified probabilities through ``p`` uses a more general but less + efficient sampler than the default. The general sampler produces a different sample + than the optimized sampler even if each element of ``p`` is 1 / len(a). + + Sampling random rows from a 2-D array is not possible with this function, + but is possible with `Generator.choice` through its ``axis`` keyword. Examples - --------- + -------- Generate a uniform random sample from np.arange(5) of size 3: >>> np.random.choice(5, 3) - array([0, 3, 4]) + array([0, 3, 4]) # random >>> #This is equivalent to np.random.randint(0,5,3) Generate a non-uniform random sample from np.arange(5) of size 3: >>> np.random.choice(5, 3, p=[0.1, 0, 0.3, 0.6, 0]) - array([3, 3, 0]) + array([3, 3, 0]) # random Generate a uniform random sample from np.arange(5) of size 3 without replacement: >>> np.random.choice(5, 3, replace=False) - array([3,1,0]) + array([3,1,0]) # random >>> #This is equivalent to np.random.permutation(np.arange(5))[:3] Generate a non-uniform random sample from np.arange(5) of size 3 without replacement: >>> np.random.choice(5, 3, replace=False, p=[0.1, 0, 0.3, 0.6, 0]) - array([2, 3, 0]) + array([2, 3, 0]) # random Any of the above can be repeated with an arbitrary array-like instead of just integers. For instance: >>> aa_milne_arr = ['pooh', 'rabbit', 'piglet', 'Christopher'] >>> np.random.choice(aa_milne_arr, 5, p=[0.5, 0.1, 0.1, 0.3]) - array(['pooh', 'pooh', 'pooh', 'Christopher', 'piglet'], - dtype='|S11') + array(['pooh', 'pooh', 'pooh', 'Christopher', 'piglet'], # random + dtype='<U11') """ @@ -1116,14 +905,14 @@ cdef class RandomState: pop_size = operator.index(a.item()) except TypeError: raise ValueError("a must be 1-dimensional or an integer") - if pop_size <= 0: - raise ValueError("a must be greater than 0") + if pop_size <= 0 and np.prod(size) != 0: + raise ValueError("a must be greater than 0 unless no samples are taken") elif a.ndim != 1: raise ValueError("a must be 1-dimensional") else: pop_size = a.shape[0] - if pop_size is 0: - raise ValueError("a must be non-empty") + if pop_size is 0 and np.prod(size) != 0: + raise ValueError("'a' cannot be empty unless no samples are taken") if p is not None: d = len(p) @@ -1133,22 +922,30 @@ cdef class RandomState: if np.issubdtype(p.dtype, np.floating): atol = max(atol, np.sqrt(np.finfo(p.dtype).eps)) - p = <ndarray>PyArray_ContiguousFromObject(p, NPY_DOUBLE, 1, 1) - pix = <double*>PyArray_DATA(p) + p = <np.ndarray>np.PyArray_FROM_OTF( + p, np.NPY_DOUBLE, np.NPY_ALIGNED | np.NPY_ARRAY_C_CONTIGUOUS) + pix = <double*>np.PyArray_DATA(p) if p.ndim != 1: - raise ValueError("p must be 1-dimensional") + raise ValueError("'p' must be 1-dimensional") if p.size != pop_size: - raise ValueError("a and p must have same size") + raise ValueError("'a' and 'p' must have same size") + p_sum = kahan_sum(pix, d) + if np.isnan(p_sum): + raise ValueError("probabilities contain NaN") if np.logical_or.reduce(p < 0): raise ValueError("probabilities are not non-negative") - if abs(kahan_sum(pix, d) - 1.) > atol: + if abs(p_sum - 1.) > atol: raise ValueError("probabilities do not sum to 1") - shape = size - if shape is not None: + # `shape == None` means `shape == ()`, but with scalar unpacking at the + # end + is_scalar = size is None + if not is_scalar: + shape = size size = np.prod(shape, dtype=np.intp) else: + shape = () size = 1 # Actual sampling @@ -1158,20 +955,24 @@ cdef class RandomState: cdf /= cdf[-1] uniform_samples = self.random_sample(shape) idx = cdf.searchsorted(uniform_samples, side='right') - idx = np.array(idx, copy=False) # searchsorted returns a scalar + # searchsorted returns a scalar + # force cast to int for LLP64 + idx = np.array(idx, copy=False).astype(int, casting='unsafe') else: idx = self.randint(0, pop_size, size=shape) else: if size > pop_size: raise ValueError("Cannot take a larger sample than " "population when 'replace=False'") + elif size < 0: + raise ValueError("Negative dimensions are not allowed") if p is not None: if np.count_nonzero(p > 0) < size: raise ValueError("Fewer non-zero entries in p than size") n_uniq = 0 p = p.copy() - found = np.zeros(shape, dtype=np.int) + found = np.zeros(shape, dtype=int) flat_found = found.ravel() while n_uniq < size: x = self.rand(size - n_uniq) @@ -1188,18 +989,17 @@ cdef class RandomState: idx = found else: idx = self.permutation(pop_size)[:size] - if shape is not None: - idx.shape = shape + idx.shape = shape - if shape is None and isinstance(idx, np.ndarray): + if is_scalar and isinstance(idx, np.ndarray): # In most cases a scalar will have been made an array idx = idx.item(0) - #Use samples as indices for a if a is array-like + # Use samples as indices for a if a is array-like if a.ndim == 0: return idx - if shape is not None and idx.ndim == 0: + if not is_scalar and idx.ndim == 0: # If size == () then the user requested a 0-d array as opposed to # a scalar object when size is None. However a[idx] is always a # scalar and not an array. So this makes sure the result is an @@ -1211,7 +1011,6 @@ cdef class RandomState: return a[idx] - def uniform(self, low=0.0, high=1.0, size=None): """ uniform(low=0.0, high=1.0, size=None) @@ -1223,6 +1022,10 @@ cdef class RandomState: any value within the given interval is equally likely to be drawn by `uniform`. + .. note:: + New code should use the ``uniform`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. + Parameters ---------- low : float or array_like of floats, optional @@ -1230,7 +1033,10 @@ cdef class RandomState: greater than or equal to low. The default value is 0. high : float or array_like of floats Upper boundary of the output interval. All values generated will be - less than high. The default value is 1.0. + less than or equal to high. The high limit may be included in the + returned array of floats due to floating-point rounding in the + equation ``low + (high-low) * random_sample()``. The default value + is 1.0. size : int or tuple of ints, optional Output shape. If the given shape is, e.g., ``(m, n, k)``, then ``m * n * k`` samples are drawn. If size is ``None`` (default), @@ -1252,6 +1058,7 @@ cdef class RandomState: rand : Convenience function that accepts dimensions as input, e.g., ``rand(2,2)`` would generate a 2-by-2 array of floats, uniformly distributed over ``[0, 1)``. + Generator.uniform: which should be used for new code. Notes ----- @@ -1265,7 +1072,14 @@ cdef class RandomState: If ``high`` < ``low``, the results are officially undefined and may eventually raise an error, i.e. do not rely on this function to behave when passed arguments satisfying that - inequality condition. + inequality condition. The ``high`` limit may be included in the + returned array of floats due to floating-point rounding in the + equation ``low + (high-low) * random_sample()``. For example: + + >>> x = np.float32(5*0.99999999) + >>> x + 5.0 + Examples -------- @@ -1289,34 +1103,39 @@ cdef class RandomState: >>> plt.show() """ - cdef ndarray olow, ohigh, odiff - cdef double flow, fhigh, fscale + cdef bint is_scalar = True + cdef np.ndarray alow, ahigh, arange + cdef double _low, _high, range cdef object temp - olow = <ndarray>PyArray_FROM_OTF(low, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - ohigh = <ndarray>PyArray_FROM_OTF(high, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - - if olow.shape == ohigh.shape == (): - flow = PyFloat_AsDouble(low) - fhigh = PyFloat_AsDouble(high) - fscale = fhigh - flow + alow = <np.ndarray>np.PyArray_FROM_OTF(low, np.NPY_DOUBLE, np.NPY_ALIGNED) + ahigh = <np.ndarray>np.PyArray_FROM_OTF(high, np.NPY_DOUBLE, np.NPY_ALIGNED) - if not npy_isfinite(fscale): + if np.PyArray_NDIM(alow) == np.PyArray_NDIM(ahigh) == 0: + _low = PyFloat_AsDouble(low) + _high = PyFloat_AsDouble(high) + range = _high - _low + if not np.isfinite(range): raise OverflowError('Range exceeds valid bounds') - return cont2_array_sc(self.internal_state, rk_uniform, size, flow, - fscale, self.lock) - - temp = np.subtract(ohigh, olow) - Py_INCREF(temp) # needed to get around Pyrex's automatic reference-counting - # rules because EnsureArray steals a reference - odiff = <ndarray>PyArray_EnsureArray(temp) - - if not np.all(np.isfinite(odiff)): + return cont(&random_uniform, &self._bitgen, size, self.lock, 2, + _low, '', CONS_NONE, + range, '', CONS_NONE, + 0.0, '', CONS_NONE, + None) + + temp = np.subtract(ahigh, alow) + Py_INCREF(temp) + # needed to get around Pyrex's automatic reference-counting + # rules because EnsureArray steals a reference + arange = <np.ndarray>np.PyArray_EnsureArray(temp) + if not np.all(np.isfinite(arange)): raise OverflowError('Range exceeds valid bounds') - - return cont2_array(self.internal_state, rk_uniform, size, olow, odiff, - self.lock) + return cont(&random_uniform, &self._bitgen, size, self.lock, 2, + alow, '', CONS_NONE, + arange, '', CONS_NONE, + 0.0, '', CONS_NONE, + None) def rand(self, *args): """ @@ -1324,6 +1143,12 @@ cdef class RandomState: Random values in a given shape. + .. note:: + This is a convenience function for users porting code from Matlab, + and wraps `random_sample`. That function takes a + tuple to specify the size of the output, which is consistent with + other NumPy functions like `numpy.zeros` and `numpy.ones`. + Create an array of the given shape and populate it with random samples from a uniform distribution over ``[0, 1)``. @@ -1331,7 +1156,7 @@ cdef class RandomState: Parameters ---------- d0, d1, ..., dn : int, optional - The dimensions of the returned array, should all be positive. + The dimensions of the returned array, must be non-negative. If no argument is given a single Python float is returned. Returns @@ -1343,12 +1168,6 @@ cdef class RandomState: -------- random - Notes - ----- - This is a convenience function. If you want an interface that - takes a shape-tuple as the first argument, refer to - np.random.random_sample . - Examples -------- >>> np.random.rand(3,2) @@ -1368,21 +1187,26 @@ cdef class RandomState: Return a sample (or samples) from the "standard normal" distribution. - If positive, int_like or int-convertible arguments are provided, - `randn` generates an array of shape ``(d0, d1, ..., dn)``, filled - with random floats sampled from a univariate "normal" (Gaussian) - distribution of mean 0 and variance 1 (if any of the :math:`d_i` are - floats, they are first converted to integers by truncation). A single - float randomly sampled from the distribution is returned if no - argument is provided. + .. note:: + This is a convenience function for users porting code from Matlab, + and wraps `standard_normal`. That function takes a + tuple to specify the size of the output, which is consistent with + other NumPy functions like `numpy.zeros` and `numpy.ones`. + + .. note:: + New code should use the ``standard_normal`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. - This is a convenience function. If you want an interface that takes a - tuple as the first argument, use `numpy.random.standard_normal` instead. + If positive int_like arguments are provided, `randn` generates an array + of shape ``(d0, d1, ..., dn)``, filled + with random floats sampled from a univariate "normal" (Gaussian) + distribution of mean 0 and variance 1. A single float randomly sampled + from the distribution is returned if no argument is provided. Parameters ---------- d0, d1, ..., dn : int, optional - The dimensions of the returned array, should be all positive. + The dimensions of the returned array, must be non-negative. If no argument is given a single Python float is returned. Returns @@ -1395,6 +1219,8 @@ cdef class RandomState: See Also -------- standard_normal : Similar, but takes a tuple as its argument. + normal : Also accepts mu and sigma arguments. + Generator.standard_normal: which should be used for new code. Notes ----- @@ -1405,31 +1231,31 @@ cdef class RandomState: Examples -------- >>> np.random.randn() - 2.1923875335537315 #random + 2.1923875335537315 # random Two-by-four array of samples from N(3, 6.25): - >>> 2.5 * np.random.randn(2, 4) + 3 - array([[-4.49401501, 4.00950034, -1.81814867, 7.29718677], #random - [ 0.39924804, 4.68456316, 4.99394529, 4.84057254]]) #random + >>> 3 + 2.5 * np.random.randn(2, 4) + array([[-4.49401501, 4.00950034, -1.81814867, 7.29718677], # random + [ 0.39924804, 4.68456316, 4.99394529, 4.84057254]]) # random """ if len(args) == 0: return self.standard_normal() else: - return self.standard_normal(args) + return self.standard_normal(size=args) def random_integers(self, low, high=None, size=None): """ random_integers(low, high=None, size=None) - Random integers of type np.int between `low` and `high`, inclusive. + Random integers of type `np.int_` between `low` and `high`, inclusive. - Return random integers of type np.int from the "discrete uniform" + Return random integers of type `np.int_` from the "discrete uniform" distribution in the closed interval [`low`, `high`]. If `high` is - None (the default), then results are from [1, `low`]. The np.int - type translates to the C long type used by Python 2 for "short" - integers and its precision is platform dependent. + None (the default), then results are from [1, `low`]. The `np.int_` + type translates to the C long integer type and its precision + is platform dependent. This function has been deprecated. Use randint instead. @@ -1471,11 +1297,11 @@ cdef class RandomState: Examples -------- >>> np.random.random_integers(5) - 4 + 4 # random >>> type(np.random.random_integers(5)) - <type 'int'> + <class 'numpy.int64'> >>> np.random.random_integers(5, size=(3,2)) - array([[5, 4], + array([[5, 4], # random [3, 3], [4, 5]]) @@ -1484,7 +1310,7 @@ cdef class RandomState: :math:`{0, 5/8, 10/8, 15/8, 20/8}`): >>> 2.5 * (np.random.random_integers(5, size=(5,)) - 1) / 4. - array([ 0.625, 1.25 , 0.625, 0.625, 2.5 ]) + array([ 0.625, 1.25 , 0.625, 0.625, 2.5 ]) # random Roll two six sided dice 1000 times and sum the results: @@ -1508,12 +1334,11 @@ cdef class RandomState: else: warnings.warn(("This function is deprecated. Please call " - "randint({low}, {high} + 1) instead".format( - low=low, high=high)), DeprecationWarning) - - return self.randint(low, high + 1, size=size, dtype='l') - + "randint({low}, {high} + 1) " + "instead".format(low=low, high=high)), + DeprecationWarning) + return self.randint(low, int(high) + 1, size=size, dtype='l') # Complicated, continuous distributions: def standard_normal(self, size=None): @@ -1522,6 +1347,10 @@ cdef class RandomState: Draw samples from a standard Normal distribution (mean=0, stdev=1). + .. note:: + New code should use the ``standard_normal`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. + Parameters ---------- size : int or tuple of ints, optional @@ -1532,22 +1361,50 @@ cdef class RandomState: Returns ------- out : float or ndarray - Drawn samples. + A floating-point array of shape ``size`` of drawn samples, or a + single sample if ``size`` was not specified. + + See Also + -------- + normal : + Equivalent function with additional ``loc`` and ``scale`` arguments + for setting the mean and standard deviation. + Generator.standard_normal: which should be used for new code. + + Notes + ----- + For random samples from :math:`N(\\mu, \\sigma^2)`, use one of:: + + mu + sigma * np.random.standard_normal(size=...) + np.random.normal(mu, sigma, size=...) Examples -------- + >>> np.random.standard_normal() + 2.1923875335537315 #random + >>> s = np.random.standard_normal(8000) >>> s - array([ 0.6888893 , 0.78096262, -0.89086505, ..., 0.49876311, #random - -0.38672696, -0.4685006 ]) #random + array([ 0.6888893 , 0.78096262, -0.89086505, ..., 0.49876311, # random + -0.38672696, -0.4685006 ]) # random >>> s.shape (8000,) >>> s = np.random.standard_normal(size=(3, 4, 2)) >>> s.shape (3, 4, 2) + Two-by-four array of samples from :math:`N(3, 6.25)`: + + >>> 3 + 2.5 * np.random.standard_normal(size=(2, 4)) + array([[-4.49401501, 4.00950034, -1.81814867, 7.29718677], # random + [ 0.39924804, 4.68456316, 4.99394529, 4.84057254]]) # random + """ - return cont0_array(self.internal_state, rk_gauss, size, self.lock) + return cont(&legacy_gauss, &self._aug_state, size, self.lock, 0, + None, None, CONS_NONE, + None, None, CONS_NONE, + None, None, CONS_NONE, + None) def normal(self, loc=0.0, scale=1.0, size=None): """ @@ -1565,12 +1422,17 @@ cdef class RandomState: by a large number of tiny, random disturbances, each with its own unique distribution [2]_. + .. note:: + New code should use the ``normal`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. + Parameters ---------- loc : float or array_like of floats Mean ("centre") of the distribution. scale : float or array_like of floats - Standard deviation (spread or "width") of the distribution. + Standard deviation (spread or "width") of the distribution. Must be + non-negative. size : int or tuple of ints, optional Output shape. If the given shape is, e.g., ``(m, n, k)``, then ``m * n * k`` samples are drawn. If size is ``None`` (default), @@ -1586,6 +1448,7 @@ cdef class RandomState: -------- scipy.stats.norm : probability density function, distribution or cumulative density function, etc. + Generator.normal: which should be used for new code. Notes ----- @@ -1601,13 +1464,13 @@ cdef class RandomState: The function has its peak at the mean, and its "spread" increases with the standard deviation (the function reaches 0.607 times its maximum at :math:`x + \\sigma` and :math:`x - \\sigma` [2]_). This implies that - `numpy.random.normal` is more likely to return samples lying close to - the mean, rather than those far away. + normal is more likely to return samples lying close to the mean, rather + than those far away. References ---------- .. [1] Wikipedia, "Normal distribution", - http://en.wikipedia.org/wiki/Normal_distribution + https://en.wikipedia.org/wiki/Normal_distribution .. [2] P. R. Peebles Jr., "Central Limit Theorem" in "Probability, Random Variables and Random Signal Principles", 4th ed., 2001, pp. 51, 51, 125. @@ -1621,11 +1484,11 @@ cdef class RandomState: Verify the mean and the variance: - >>> abs(mu - np.mean(s)) < 0.01 - True + >>> abs(mu - np.mean(s)) + 0.0 # may vary - >>> abs(sigma - np.std(s, ddof=1)) < 0.01 - True + >>> abs(sigma - np.std(s, ddof=1)) + 0.1 # may vary Display the histogram of the samples, along with the probability density function: @@ -1637,180 +1500,18 @@ cdef class RandomState: ... linewidth=2, color='r') >>> plt.show() - """ - cdef ndarray oloc, oscale - cdef double floc, fscale - - oloc = <ndarray>PyArray_FROM_OTF(loc, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - oscale = <ndarray>PyArray_FROM_OTF(scale, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - - if oloc.shape == oscale.shape == (): - floc = PyFloat_AsDouble(loc) - fscale = PyFloat_AsDouble(scale) - if np.signbit(fscale): - raise ValueError("scale < 0") - return cont2_array_sc(self.internal_state, rk_normal, size, floc, - fscale, self.lock) - - if np.any(np.signbit(oscale)): - raise ValueError("scale < 0") - return cont2_array(self.internal_state, rk_normal, size, oloc, oscale, - self.lock) - - def beta(self, a, b, size=None): - """ - beta(a, b, size=None) - - Draw samples from a Beta distribution. - - The Beta distribution is a special case of the Dirichlet distribution, - and is related to the Gamma distribution. It has the probability - distribution function - - .. math:: f(x; a,b) = \\frac{1}{B(\\alpha, \\beta)} x^{\\alpha - 1} - (1 - x)^{\\beta - 1}, - - where the normalisation, B, is the beta function, - - .. math:: B(\\alpha, \\beta) = \\int_0^1 t^{\\alpha - 1} - (1 - t)^{\\beta - 1} dt. - - It is often seen in Bayesian inference and order statistics. - - Parameters - ---------- - a : float or array_like of floats - Alpha, non-negative. - b : float or array_like of floats - Beta, non-negative. - size : int or tuple of ints, optional - Output shape. If the given shape is, e.g., ``(m, n, k)``, then - ``m * n * k`` samples are drawn. If size is ``None`` (default), - a single value is returned if ``a`` and ``b`` are both scalars. - Otherwise, ``np.broadcast(a, b).size`` samples are drawn. - - Returns - ------- - out : ndarray or scalar - Drawn samples from the parameterized beta distribution. - - """ - cdef ndarray oa, ob - cdef double fa, fb - - oa = <ndarray>PyArray_FROM_OTF(a, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - ob = <ndarray>PyArray_FROM_OTF(b, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - - if oa.shape == ob.shape == (): - fa = PyFloat_AsDouble(a) - fb = PyFloat_AsDouble(b) - - if fa <= 0: - raise ValueError("a <= 0") - if fb <= 0: - raise ValueError("b <= 0") - return cont2_array_sc(self.internal_state, rk_beta, size, fa, fb, - self.lock) - - if np.any(np.less_equal(oa, 0)): - raise ValueError("a <= 0") - if np.any(np.less_equal(ob, 0)): - raise ValueError("b <= 0") - return cont2_array(self.internal_state, rk_beta, size, oa, ob, - self.lock) - - def exponential(self, scale=1.0, size=None): - """ - exponential(scale=1.0, size=None) - - Draw samples from an exponential distribution. - - Its probability density function is - - .. math:: f(x; \\frac{1}{\\beta}) = \\frac{1}{\\beta} \\exp(-\\frac{x}{\\beta}), - - for ``x > 0`` and 0 elsewhere. :math:`\\beta` is the scale parameter, - which is the inverse of the rate parameter :math:`\\lambda = 1/\\beta`. - The rate parameter is an alternative, widely used parameterization - of the exponential distribution [3]_. - - The exponential distribution is a continuous analogue of the - geometric distribution. It describes many common situations, such as - the size of raindrops measured over many rainstorms [1]_, or the time - between page requests to Wikipedia [2]_. - - Parameters - ---------- - scale : float or array_like of floats - The scale parameter, :math:`\\beta = 1/\\lambda`. - size : int or tuple of ints, optional - Output shape. If the given shape is, e.g., ``(m, n, k)``, then - ``m * n * k`` samples are drawn. If size is ``None`` (default), - a single value is returned if ``scale`` is a scalar. Otherwise, - ``np.array(scale).size`` samples are drawn. - - Returns - ------- - out : ndarray or scalar - Drawn samples from the parameterized exponential distribution. - - References - ---------- - .. [1] Peyton Z. Peebles Jr., "Probability, Random Variables and - Random Signal Principles", 4th ed, 2001, p. 57. - .. [2] Wikipedia, "Poisson process", - http://en.wikipedia.org/wiki/Poisson_process - .. [3] Wikipedia, "Exponential distribution", - http://en.wikipedia.org/wiki/Exponential_distribution - - """ - cdef ndarray oscale - cdef double fscale - - oscale = <ndarray>PyArray_FROM_OTF(scale, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - - if oscale.shape == (): - fscale = PyFloat_AsDouble(scale) - if np.signbit(fscale): - raise ValueError("scale < 0") - return cont1_array_sc(self.internal_state, rk_exponential, size, - fscale, self.lock) - - if np.any(np.signbit(oscale)): - raise ValueError("scale < 0") - return cont1_array(self.internal_state, rk_exponential, size, oscale, - self.lock) - - def standard_exponential(self, size=None): - """ - standard_exponential(size=None) - - Draw samples from the standard exponential distribution. - - `standard_exponential` is identical to the exponential distribution - with a scale parameter of 1. - - Parameters - ---------- - size : int or tuple of ints, optional - Output shape. If the given shape is, e.g., ``(m, n, k)``, then - ``m * n * k`` samples are drawn. Default is None, in which case a - single value is returned. - - Returns - ------- - out : float or ndarray - Drawn samples. - - Examples - -------- - Output a 3x8000 array: + Two-by-four array of samples from N(3, 6.25): - >>> n = np.random.standard_exponential((3, 8000)) + >>> np.random.normal(3, 2.5, size=(2, 4)) + array([[-4.49401501, 4.00950034, -1.81814867, 7.29718677], # random + [ 0.39924804, 4.68456316, 4.99394529, 4.84057254]]) # random """ - return cont0_array(self.internal_state, rk_standard_exponential, size, - self.lock) + return cont(&legacy_normal, &self._aug_state, size, self.lock, 2, + loc, '', CONS_NONE, + scale, 'scale', CONS_NON_NEGATIVE, + 0.0, '', CONS_NONE, + None) def standard_gamma(self, shape, size=None): """ @@ -1821,10 +1522,14 @@ cdef class RandomState: Samples are drawn from a Gamma distribution with specified parameters, shape (sometimes designated "k") and scale=1. + .. note:: + New code should use the ``standard_gamma`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. + Parameters ---------- shape : float or array_like of floats - Parameter, should be > 0. + Parameter, must be non-negative. size : int or tuple of ints, optional Output shape. If the given shape is, e.g., ``(m, n, k)``, then ``m * n * k`` samples are drawn. If size is ``None`` (default), @@ -1840,6 +1545,7 @@ cdef class RandomState: -------- scipy.stats.gamma : probability density function, distribution or cumulative density function, etc. + Generator.standard_gamma: which should be used for new code. Notes ----- @@ -1860,7 +1566,7 @@ cdef class RandomState: Wolfram Web Resource. http://mathworld.wolfram.com/GammaDistribution.html .. [2] Wikipedia, "Gamma distribution", - http://en.wikipedia.org/wiki/Gamma_distribution + https://en.wikipedia.org/wiki/Gamma_distribution Examples -------- @@ -1873,30 +1579,19 @@ cdef class RandomState: the probability density function: >>> import matplotlib.pyplot as plt - >>> import scipy.special as sps + >>> import scipy.special as sps # doctest: +SKIP >>> count, bins, ignored = plt.hist(s, 50, density=True) - >>> y = bins**(shape-1) * ((np.exp(-bins/scale))/ \\ + >>> y = bins**(shape-1) * ((np.exp(-bins/scale))/ # doctest: +SKIP ... (sps.gamma(shape) * scale**shape)) - >>> plt.plot(bins, y, linewidth=2, color='r') + >>> plt.plot(bins, y, linewidth=2, color='r') # doctest: +SKIP >>> plt.show() """ - cdef ndarray oshape - cdef double fshape - - oshape = <ndarray>PyArray_FROM_OTF(shape, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - - if oshape.shape == (): - fshape = PyFloat_AsDouble(shape) - if np.signbit(fshape): - raise ValueError("shape < 0") - return cont1_array_sc(self.internal_state, rk_standard_gamma, - size, fshape, self.lock) - - if np.any(np.signbit(oshape)): - raise ValueError("shape < 0") - return cont1_array(self.internal_state, rk_standard_gamma, size, - oshape, self.lock) + return cont(&legacy_standard_gamma, &self._aug_state, size, self.lock, 1, + shape, 'shape', CONS_NON_NEGATIVE, + 0.0, '', CONS_NONE, + 0.0, '', CONS_NONE, + None) def gamma(self, shape, scale=1.0, size=None): """ @@ -1908,12 +1603,16 @@ cdef class RandomState: `shape` (sometimes designated "k") and `scale` (sometimes designated "theta"), where both parameters are > 0. + .. note:: + New code should use the ``gamma`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. + Parameters ---------- shape : float or array_like of floats - The shape of the gamma distribution. Should be greater than zero. + The shape of the gamma distribution. Must be non-negative. scale : float or array_like of floats, optional - The scale of the gamma distribution. Should be greater than zero. + The scale of the gamma distribution. Must be non-negative. Default is equal to 1. size : int or tuple of ints, optional Output shape. If the given shape is, e.g., ``(m, n, k)``, then @@ -1930,6 +1629,7 @@ cdef class RandomState: -------- scipy.stats.gamma : probability density function, distribution or cumulative density function, etc. + Generator.gamma: which should be used for new code. Notes ----- @@ -1950,7 +1650,7 @@ cdef class RandomState: Wolfram Web Resource. http://mathworld.wolfram.com/GammaDistribution.html .. [2] Wikipedia, "Gamma distribution", - http://en.wikipedia.org/wiki/Gamma_distribution + https://en.wikipedia.org/wiki/Gamma_distribution Examples -------- @@ -1963,36 +1663,18 @@ cdef class RandomState: the probability density function: >>> import matplotlib.pyplot as plt - >>> import scipy.special as sps + >>> import scipy.special as sps # doctest: +SKIP >>> count, bins, ignored = plt.hist(s, 50, density=True) - >>> y = bins**(shape-1)*(np.exp(-bins/scale) / + >>> y = bins**(shape-1)*(np.exp(-bins/scale) / # doctest: +SKIP ... (sps.gamma(shape)*scale**shape)) - >>> plt.plot(bins, y, linewidth=2, color='r') + >>> plt.plot(bins, y, linewidth=2, color='r') # doctest: +SKIP >>> plt.show() """ - cdef ndarray oshape, oscale - cdef double fshape, fscale - - oshape = <ndarray>PyArray_FROM_OTF(shape, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - oscale = <ndarray>PyArray_FROM_OTF(scale, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - - if oshape.shape == oscale.shape == (): - fshape = PyFloat_AsDouble(shape) - fscale = PyFloat_AsDouble(scale) - if np.signbit(fshape): - raise ValueError("shape < 0") - if np.signbit(fscale): - raise ValueError("scale < 0") - return cont2_array_sc(self.internal_state, rk_gamma, size, fshape, - fscale, self.lock) - - if np.any(np.signbit(oshape)): - raise ValueError("shape < 0") - if np.any(np.signbit(oscale)): - raise ValueError("scale < 0") - return cont2_array(self.internal_state, rk_gamma, size, oshape, oscale, - self.lock) + return cont(&legacy_gamma, &self._aug_state, size, self.lock, 2, + shape, 'shape', CONS_NON_NEGATIVE, + scale, 'scale', CONS_NON_NEGATIVE, + 0.0, '', CONS_NONE, None) def f(self, dfnum, dfden, size=None): """ @@ -2002,7 +1684,7 @@ cdef class RandomState: Samples are drawn from an F distribution with specified parameters, `dfnum` (degrees of freedom in numerator) and `dfden` (degrees of - freedom in denominator), where both parameters should be greater than + freedom in denominator), where both parameters must be greater than zero. The random variate of the F distribution (also known as the @@ -2010,12 +1692,16 @@ cdef class RandomState: that arises in ANOVA tests, and is the ratio of two chi-square variates. + .. note:: + New code should use the ``f`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. + Parameters ---------- dfnum : float or array_like of floats - Degrees of freedom in numerator, should be > 0. + Degrees of freedom in numerator, must be > 0. dfden : float or array_like of float - Degrees of freedom in denominator, should be > 0. + Degrees of freedom in denominator, must be > 0. size : int or tuple of ints, optional Output shape. If the given shape is, e.g., ``(m, n, k)``, then ``m * n * k`` samples are drawn. If size is ``None`` (default), @@ -2031,6 +1717,7 @@ cdef class RandomState: -------- scipy.stats.f : probability density function, distribution or cumulative density function, etc. + Generator.f: which should be used for new code. Notes ----- @@ -2047,7 +1734,7 @@ cdef class RandomState: .. [1] Glantz, Stanton A. "Primer of Biostatistics.", McGraw-Hill, Fifth Edition, 2002. .. [2] Wikipedia, "F-distribution", - http://en.wikipedia.org/wiki/F-distribution + https://en.wikipedia.org/wiki/F-distribution Examples -------- @@ -2069,37 +1756,18 @@ cdef class RandomState: The lower bound for the top 1% of the samples is : - >>> sort(s)[-10] - 7.61988120985 + >>> np.sort(s)[-10] + 7.61988120985 # random So there is about a 1% chance that the F statistic will exceed 7.62, the measured value is 36, so the null hypothesis is rejected at the 1% level. """ - cdef ndarray odfnum, odfden - cdef double fdfnum, fdfden - - odfnum = <ndarray>PyArray_FROM_OTF(dfnum, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - odfden = <ndarray>PyArray_FROM_OTF(dfden, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - - if odfnum.shape == odfden.shape == (): - fdfnum = PyFloat_AsDouble(dfnum) - fdfden = PyFloat_AsDouble(dfden) - - if fdfnum <= 0: - raise ValueError("dfnum <= 0") - if fdfden <= 0: - raise ValueError("dfden <= 0") - return cont2_array_sc(self.internal_state, rk_f, size, fdfnum, - fdfden, self.lock) - - if np.any(np.less_equal(odfnum, 0.0)): - raise ValueError("dfnum <= 0") - if np.any(np.less_equal(odfden, 0.0)): - raise ValueError("dfden <= 0") - return cont2_array(self.internal_state, rk_f, size, odfnum, odfden, - self.lock) + return cont(&legacy_f, &self._aug_state, size, self.lock, 2, + dfnum, 'dfnum', CONS_POSITIVE, + dfden, 'dfden', CONS_POSITIVE, + 0.0, '', CONS_NONE, None) def noncentral_f(self, dfnum, dfden, nonc, size=None): """ @@ -2112,18 +1780,22 @@ cdef class RandomState: freedom in denominator), where both parameters > 1. `nonc` is the non-centrality parameter. + .. note:: + New code should use the ``noncentral_f`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. + Parameters ---------- dfnum : float or array_like of floats - Numerator degrees of freedom, should be > 0. + Numerator degrees of freedom, must be > 0. .. versionchanged:: 1.14.0 Earlier NumPy versions required dfnum > 1. dfden : float or array_like of floats - Denominator degrees of freedom, should be > 0. + Denominator degrees of freedom, must be > 0. nonc : float or array_like of floats Non-centrality parameter, the sum of the squares of the numerator - means, should be >= 0. + means, must be >= 0. size : int or tuple of ints, optional Output shape. If the given shape is, e.g., ``(m, n, k)``, then ``m * n * k`` samples are drawn. If size is ``None`` (default), @@ -2136,6 +1808,10 @@ cdef class RandomState: out : ndarray or scalar Drawn samples from the parameterized noncentral Fisher distribution. + See Also + -------- + Generator.noncentral_f: which should be used for new code. + Notes ----- When calculating the power of an experiment (power = probability of @@ -2150,7 +1826,7 @@ cdef class RandomState: From MathWorld--A Wolfram Web Resource. http://mathworld.wolfram.com/NoncentralF-Distribution.html .. [2] Wikipedia, "Noncentral F-distribution", - http://en.wikipedia.org/wiki/Noncentral_F-distribution + https://en.wikipedia.org/wiki/Noncentral_F-distribution Examples -------- @@ -2167,40 +1843,16 @@ cdef class RandomState: >>> NF = np.histogram(nc_vals, bins=50, density=True) >>> c_vals = np.random.f(dfnum, dfden, 1000000) >>> F = np.histogram(c_vals, bins=50, density=True) + >>> import matplotlib.pyplot as plt >>> plt.plot(F[1][1:], F[0]) >>> plt.plot(NF[1][1:], NF[0]) >>> plt.show() """ - cdef ndarray odfnum, odfden, ononc - cdef double fdfnum, fdfden, fnonc - - odfnum = <ndarray>PyArray_FROM_OTF(dfnum, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - odfden = <ndarray>PyArray_FROM_OTF(dfden, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - ononc = <ndarray>PyArray_FROM_OTF(nonc, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - - if odfnum.shape == odfden.shape == ononc.shape == (): - fdfnum = PyFloat_AsDouble(dfnum) - fdfden = PyFloat_AsDouble(dfden) - fnonc = PyFloat_AsDouble(nonc) - - if fdfnum <= 0: - raise ValueError("dfnum <= 0") - if fdfden <= 0: - raise ValueError("dfden <= 0") - if fnonc < 0: - raise ValueError("nonc < 0") - return cont3_array_sc(self.internal_state, rk_noncentral_f, size, - fdfnum, fdfden, fnonc, self.lock) - - if np.any(np.less_equal(odfnum, 0.0)): - raise ValueError("dfnum <= 0") - if np.any(np.less_equal(odfden, 0.0)): - raise ValueError("dfden <= 0") - if np.any(np.less(ononc, 0.0)): - raise ValueError("nonc < 0") - return cont3_array(self.internal_state, rk_noncentral_f, size, odfnum, - odfden, ononc, self.lock) + return cont(&legacy_noncentral_f, &self._aug_state, size, self.lock, 3, + dfnum, 'dfnum', CONS_POSITIVE, + dfden, 'dfden', CONS_POSITIVE, + nonc, 'nonc', CONS_NON_NEGATIVE, None) def chisquare(self, df, size=None): """ @@ -2213,10 +1865,14 @@ cdef class RandomState: resulting distribution is chi-square (see Notes). This distribution is often used in hypothesis testing. + .. note:: + New code should use the ``chisquare`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. + Parameters ---------- df : float or array_like of floats - Number of degrees of freedom, should be > 0. + Number of degrees of freedom, must be > 0. size : int or tuple of ints, optional Output shape. If the given shape is, e.g., ``(m, n, k)``, then ``m * n * k`` samples are drawn. If size is ``None`` (default), @@ -2234,6 +1890,10 @@ cdef class RandomState: When `df` <= 0 or when an inappropriate `size` (e.g. ``size=-1``) is given. + See Also + -------- + Generator.chisquare: which should be used for new code. + Notes ----- The variable obtained by summing the squares of `df` independent, @@ -2257,31 +1917,17 @@ cdef class RandomState: References ---------- .. [1] NIST "Engineering Statistics Handbook" - http://www.itl.nist.gov/div898/handbook/eda/section3/eda3666.htm + https://www.itl.nist.gov/div898/handbook/eda/section3/eda3666.htm Examples -------- >>> np.random.chisquare(2,4) - array([ 1.89920014, 9.00867716, 3.13710533, 5.62318272]) - - """ - cdef ndarray odf - cdef double fdf - - odf = <ndarray>PyArray_FROM_OTF(df, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - - if odf.shape == (): - fdf = PyFloat_AsDouble(df) - - if fdf <= 0: - raise ValueError("df <= 0") - return cont1_array_sc(self.internal_state, rk_chisquare, size, fdf, - self.lock) - - if np.any(np.less_equal(odf, 0.0)): - raise ValueError("df <= 0") - return cont1_array(self.internal_state, rk_chisquare, size, odf, - self.lock) + array([ 1.89920014, 9.00867716, 3.13710533, 5.62318272]) # random + """ + return cont(&legacy_chisquare, &self._aug_state, size, self.lock, 1, + df, 'df', CONS_POSITIVE, + 0.0, '', CONS_NONE, + 0.0, '', CONS_NONE, None) def noncentral_chisquare(self, df, nonc, size=None): """ @@ -2289,18 +1935,22 @@ cdef class RandomState: Draw samples from a noncentral chi-square distribution. - The noncentral :math:`\\chi^2` distribution is a generalisation of + The noncentral :math:`\\chi^2` distribution is a generalization of the :math:`\\chi^2` distribution. + .. note:: + New code should use the ``noncentral_chisquare`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. + Parameters ---------- df : float or array_like of floats - Degrees of freedom, should be > 0. + Degrees of freedom, must be > 0. .. versionchanged:: 1.10.0 Earlier NumPy versions required dfnum > 1. nonc : float or array_like of floats - Non-centrality, should be non-negative. + Non-centrality, must be non-negative. size : int or tuple of ints, optional Output shape. If the given shape is, e.g., ``(m, n, k)``, then ``m * n * k`` samples are drawn. If size is ``None`` (default), @@ -2312,6 +1962,10 @@ cdef class RandomState: out : ndarray or scalar Drawn samples from the parameterized noncentral chi-square distribution. + See Also + -------- + Generator.noncentral_chisquare: which should be used for new code. + Notes ----- The probability density function for the noncentral Chi-square @@ -2319,22 +1973,14 @@ cdef class RandomState: .. math:: P(x;df,nonc) = \\sum^{\\infty}_{i=0} \\frac{e^{-nonc/2}(nonc/2)^{i}}{i!} - \\P_{Y_{df+2i}}(x), + P_{Y_{df+2i}}(x), where :math:`Y_{q}` is the Chi-square with q degrees of freedom. - In Delhi (2007), it is noted that the noncentral chi-square is - useful in bombing and coverage problems, the probability of - killing the point target given by the noncentral chi-squared - distribution. - References ---------- - .. [1] Delhi, M.S. Holla, "On a noncentral chi-square distribution in - the analysis of weapon systems effectiveness", Metrika, - Volume 15, Number 1 / December, 1970. - .. [2] Wikipedia, "Noncentral chi-square distribution" - http://en.wikipedia.org/wiki/Noncentral_chi-square_distribution + .. [1] Wikipedia, "Noncentral chi-squared distribution" + https://en.wikipedia.org/wiki/Noncentral_chi-squared_distribution Examples -------- @@ -2365,29 +2011,10 @@ cdef class RandomState: >>> plt.show() """ - cdef ndarray odf, ononc - cdef double fdf, fnonc - - odf = <ndarray>PyArray_FROM_OTF(df, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - ononc = <ndarray>PyArray_FROM_OTF(nonc, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - - if odf.shape == ononc.shape == (): - fdf = PyFloat_AsDouble(df) - fnonc = PyFloat_AsDouble(nonc) - - if fdf <= 0: - raise ValueError("df <= 0") - if fnonc < 0: - raise ValueError("nonc < 0") - return cont2_array_sc(self.internal_state, rk_noncentral_chisquare, - size, fdf, fnonc, self.lock) - - if np.any(np.less_equal(odf, 0.0)): - raise ValueError("df <= 0") - if np.any(np.less(ononc, 0.0)): - raise ValueError("nonc < 0") - return cont2_array(self.internal_state, rk_noncentral_chisquare, size, - odf, ononc, self.lock) + return cont(&legacy_noncentral_chisquare, &self._aug_state, size, self.lock, 2, + df, 'df', CONS_POSITIVE, + nonc, 'nonc', CONS_NON_NEGATIVE, + 0.0, '', CONS_NONE, None) def standard_cauchy(self, size=None): """ @@ -2397,6 +2024,10 @@ cdef class RandomState: Also known as the Lorentz distribution. + .. note:: + New code should use the ``standard_cauchy`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. + Parameters ---------- size : int or tuple of ints, optional @@ -2409,6 +2040,10 @@ cdef class RandomState: samples : ndarray or scalar The drawn samples. + See Also + -------- + Generator.standard_cauchy: which should be used for new code. + Notes ----- The probability density function for the full Cauchy distribution is @@ -2433,25 +2068,26 @@ cdef class RandomState: ---------- .. [1] NIST/SEMATECH e-Handbook of Statistical Methods, "Cauchy Distribution", - http://www.itl.nist.gov/div898/handbook/eda/section3/eda3663.htm + https://www.itl.nist.gov/div898/handbook/eda/section3/eda3663.htm .. [2] Weisstein, Eric W. "Cauchy Distribution." From MathWorld--A Wolfram Web Resource. http://mathworld.wolfram.com/CauchyDistribution.html .. [3] Wikipedia, "Cauchy distribution" - http://en.wikipedia.org/wiki/Cauchy_distribution + https://en.wikipedia.org/wiki/Cauchy_distribution Examples -------- Draw samples and plot the distribution: + >>> import matplotlib.pyplot as plt >>> s = np.random.standard_cauchy(1000000) >>> s = s[(s>-25) & (s<25)] # truncate distribution so it plots well >>> plt.hist(s, bins=100) >>> plt.show() """ - return cont0_array(self.internal_state, rk_standard_cauchy, size, - self.lock) + return cont(&legacy_standard_cauchy, &self._aug_state, size, self.lock, 0, + 0.0, '', CONS_NONE, 0.0, '', CONS_NONE, 0.0, '', CONS_NONE, None) def standard_t(self, df, size=None): """ @@ -2464,10 +2100,14 @@ cdef class RandomState: large, the result resembles that of the standard normal distribution (`standard_normal`). + .. note:: + New code should use the ``standard_t`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. + Parameters ---------- df : float or array_like of floats - Degrees of freedom, should be > 0. + Degrees of freedom, must be > 0. size : int or tuple of ints, optional Output shape. If the given shape is, e.g., ``(m, n, k)``, then ``m * n * k`` samples are drawn. If size is ``None`` (default), @@ -2479,6 +2119,10 @@ cdef class RandomState: out : ndarray or scalar Drawn samples from the parameterized standard Student's t distribution. + See Also + -------- + Generator.standard_t: which should be used for new code. + Notes ----- The probability density function for the t distribution is @@ -2501,63 +2145,63 @@ cdef class RandomState: .. [1] Dalgaard, Peter, "Introductory Statistics With R", Springer, 2002. .. [2] Wikipedia, "Student's t-distribution" - http://en.wikipedia.org/wiki/Student's_t-distribution + https://en.wikipedia.org/wiki/Student's_t-distribution Examples -------- From Dalgaard page 83 [1]_, suppose the daily energy intake for 11 - women in Kj is: + women in kilojoules (kJ) is: >>> intake = np.array([5260., 5470, 5640, 6180, 6390, 6515, 6805, 7515, \\ ... 7515, 8230, 8770]) Does their energy intake deviate systematically from the recommended - value of 7725 kJ? + value of 7725 kJ? Our null hypothesis will be the absence of deviation, + and the alternate hypothesis will be the presence of an effect that could be + either positive or negative, hence making our test 2-tailed. + + Because we are estimating the mean and we have N=11 values in our sample, + we have N-1=10 degrees of freedom. We set our significance level to 95% and + compute the t statistic using the empirical mean and empirical standard + deviation of our intake. We use a ddof of 1 to base the computation of our + empirical standard deviation on an unbiased estimate of the variance (note: + the final estimate is not unbiased due to the concave nature of the square + root). - We have 10 degrees of freedom, so is the sample mean within 95% of the - recommended value? - - >>> s = np.random.standard_t(10, size=100000) >>> np.mean(intake) 6753.636363636364 >>> intake.std(ddof=1) 1142.1232221373727 + >>> t = (np.mean(intake)-7725)/(intake.std(ddof=1)/np.sqrt(len(intake))) + >>> t + -2.8207540608310198 - Calculate the t statistic, setting the ddof parameter to the unbiased - value so the divisor in the standard deviation will be degrees of - freedom, N-1. + We draw 1000000 samples from Student's t distribution with the adequate + degrees of freedom. - >>> t = (np.mean(intake)-7725)/(intake.std(ddof=1)/np.sqrt(len(intake))) >>> import matplotlib.pyplot as plt + >>> s = np.random.standard_t(10, size=1000000) >>> h = plt.hist(s, bins=100, density=True) - For a one-sided t-test, how far out in the distribution does the t - statistic appear? - - >>> np.sum(s<t) / float(len(s)) - 0.0090699999999999999 #random + Does our t statistic land in one of the two critical regions found at + both tails of the distribution? - So the p-value is about 0.009, which says the null hypothesis has a - probability of about 99% of being true. + >>> np.sum(np.abs(t) < np.abs(s)) / float(len(s)) + 0.018318 #random < 0.05, statistic is in critical region - """ - cdef ndarray odf - cdef double fdf - - odf = <ndarray> PyArray_FROM_OTF(df, NPY_DOUBLE, NPY_ARRAY_ALIGNED) + The probability value for this 2-tailed test is about 1.83%, which is + lower than the 5% pre-determined significance threshold. - if odf.shape == (): - fdf = PyFloat_AsDouble(df) + Therefore, the probability of observing values as extreme as our intake + conditionally on the null hypothesis being true is too low, and we reject + the null hypothesis of no deviation. - if fdf <= 0: - raise ValueError("df <= 0") - return cont1_array_sc(self.internal_state, rk_standard_t, size, - fdf, self.lock) - - if np.any(np.less_equal(odf, 0.0)): - raise ValueError("df <= 0") - return cont1_array(self.internal_state, rk_standard_t, size, odf, - self.lock) + """ + return cont(&legacy_standard_t, &self._aug_state, size, self.lock, 1, + df, 'df', CONS_POSITIVE, + 0, '', CONS_NONE, + 0, '', CONS_NONE, + None) def vonmises(self, mu, kappa, size=None): """ @@ -2573,6 +2217,10 @@ cdef class RandomState: circle. It may be thought of as the circular analogue of the normal distribution. + .. note:: + New code should use the ``vonmises`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. + Parameters ---------- mu : float or array_like of floats @@ -2594,6 +2242,7 @@ cdef class RandomState: -------- scipy.stats.vonmises : probability density function, distribution, or cumulative density function, etc. + Generator.vonmises: which should be used for new code. Notes ----- @@ -2629,33 +2278,18 @@ cdef class RandomState: the probability density function: >>> import matplotlib.pyplot as plt - >>> from scipy.special import i0 + >>> from scipy.special import i0 # doctest: +SKIP >>> plt.hist(s, 50, density=True) >>> x = np.linspace(-np.pi, np.pi, num=51) - >>> y = np.exp(kappa*np.cos(x-mu))/(2*np.pi*i0(kappa)) - >>> plt.plot(x, y, linewidth=2, color='r') + >>> y = np.exp(kappa*np.cos(x-mu))/(2*np.pi*i0(kappa)) # doctest: +SKIP + >>> plt.plot(x, y, linewidth=2, color='r') # doctest: +SKIP >>> plt.show() """ - cdef ndarray omu, okappa - cdef double fmu, fkappa - - omu = <ndarray> PyArray_FROM_OTF(mu, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - okappa = <ndarray> PyArray_FROM_OTF(kappa, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - - if omu.shape == okappa.shape == (): - fmu = PyFloat_AsDouble(mu) - fkappa = PyFloat_AsDouble(kappa) - - if fkappa < 0: - raise ValueError("kappa < 0") - return cont2_array_sc(self.internal_state, rk_vonmises, size, fmu, - fkappa, self.lock) - - if np.any(np.less(okappa, 0.0)): - raise ValueError("kappa < 0") - return cont2_array(self.internal_state, rk_vonmises, size, omu, okappa, - self.lock) + return cont(&legacy_vonmises, &self._bitgen, size, self.lock, 2, + mu, 'mu', CONS_NONE, + kappa, 'kappa', CONS_NON_NEGATIVE, + 0.0, '', CONS_NONE, None) def pareto(self, a, size=None): """ @@ -2681,10 +2315,14 @@ cdef class RandomState: 20 percent of the range, while the other 20 percent fill the remaining 80 percent of the range. + .. note:: + New code should use the ``pareto`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. + Parameters ---------- a : float or array_like of floats - Shape of the distribution. Should be greater than zero. + Shape of the distribution. Must be positive. size : int or tuple of ints, optional Output shape. If the given shape is, e.g., ``(m, n, k)``, then ``m * n * k`` samples are drawn. If size is ``None`` (default), @@ -2702,6 +2340,7 @@ cdef class RandomState: cumulative density function, etc. scipy.stats.genpareto : probability density function, distribution or cumulative density function, etc. + Generator.pareto: which should be used for new code. Notes ----- @@ -2722,7 +2361,6 @@ cdef class RandomState: projects in Sourceforge [1]_. It is one of the so-called "fat-tailed" distributions. - References ---------- .. [1] Francis Hunt and Paul Johnson, On the Pareto Distribution of @@ -2731,7 +2369,7 @@ cdef class RandomState: .. [3] Reiss, R.D., Thomas, M.(2001), Statistical Analysis of Extreme Values, Birkhauser Verlag, Basel, pp 23-30. .. [4] Wikipedia, "Pareto distribution", - http://en.wikipedia.org/wiki/Pareto_distribution + https://en.wikipedia.org/wiki/Pareto_distribution Examples -------- @@ -2750,22 +2388,10 @@ cdef class RandomState: >>> plt.show() """ - cdef ndarray oa - cdef double fa - - oa = <ndarray>PyArray_FROM_OTF(a, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - - if oa.shape == (): - fa = PyFloat_AsDouble(a) - - if fa <= 0: - raise ValueError("a <= 0") - return cont1_array_sc(self.internal_state, rk_pareto, size, fa, - self.lock) - - if np.any(np.less_equal(oa, 0.0)): - raise ValueError("a <= 0") - return cont1_array(self.internal_state, rk_pareto, size, oa, self.lock) + return cont(&legacy_pareto, &self._aug_state, size, self.lock, 1, + a, 'a', CONS_POSITIVE, + 0.0, '', CONS_NONE, + 0.0, '', CONS_NONE, None) def weibull(self, a, size=None): """ @@ -2783,10 +2409,14 @@ cdef class RandomState: The more common 2-parameter Weibull, including a scale parameter :math:`\\lambda` is just :math:`X = \\lambda(-ln(U))^{1/a}`. + .. note:: + New code should use the ``weibull`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. + Parameters ---------- a : float or array_like of floats - Shape of the distribution. Should be greater than zero. + Shape parameter of the distribution. Must be nonnegative. size : int or tuple of ints, optional Output shape. If the given shape is, e.g., ``(m, n, k)``, then ``m * n * k`` samples are drawn. If size is ``None`` (default), @@ -2804,6 +2434,7 @@ cdef class RandomState: scipy.stats.weibull_min scipy.stats.genextreme gumbel + Generator.weibull: which should be used for new code. Notes ----- @@ -2836,7 +2467,7 @@ cdef class RandomState: Wide Applicability", Journal Of Applied Mechanics ASME Paper 1951. .. [3] Wikipedia, "Weibull distribution", - http://en.wikipedia.org/wiki/Weibull_distribution + https://en.wikipedia.org/wiki/Weibull_distribution Examples -------- @@ -2860,22 +2491,10 @@ cdef class RandomState: >>> plt.show() """ - cdef ndarray oa - cdef double fa - - oa = <ndarray>PyArray_FROM_OTF(a, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - - if oa.shape == (): - fa = PyFloat_AsDouble(a) - if np.signbit(fa): - raise ValueError("a < 0") - return cont1_array_sc(self.internal_state, rk_weibull, size, fa, - self.lock) - - if np.any(np.signbit(oa)): - raise ValueError("a < 0") - return cont1_array(self.internal_state, rk_weibull, size, oa, - self.lock) + return cont(&legacy_weibull, &self._aug_state, size, self.lock, 1, + a, 'a', CONS_NON_NEGATIVE, + 0.0, '', CONS_NONE, + 0.0, '', CONS_NONE, None) def power(self, a, size=None): """ @@ -2886,10 +2505,14 @@ cdef class RandomState: Also known as the power function distribution. + .. note:: + New code should use the ``power`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. + Parameters ---------- a : float or array_like of floats - Parameter of the distribution. Should be greater than zero. + Parameter of the distribution. Must be non-negative. size : int or tuple of ints, optional Output shape. If the given shape is, e.g., ``(m, n, k)``, then ``m * n * k`` samples are drawn. If size is ``None`` (default), @@ -2904,7 +2527,11 @@ cdef class RandomState: Raises ------ ValueError - If a < 1. + If a <= 0. + + See Also + -------- + Generator.power: which should be used for new code. Notes ----- @@ -2927,7 +2554,7 @@ cdef class RandomState: Dataplot Reference Manual, Volume 2: Let Subcommands and Library Functions", National Institute of Standards and Technology Handbook Series, June 2003. - http://www.itl.nist.gov/div898/software/dataplot/refman2/auxillar/powpdf.pdf + https://www.itl.nist.gov/div898/software/dataplot/refman2/auxillar/powpdf.pdf Examples -------- @@ -2950,43 +2577,32 @@ cdef class RandomState: Compare the power function distribution to the inverse of the Pareto. - >>> from scipy import stats + >>> from scipy import stats # doctest: +SKIP >>> rvs = np.random.power(5, 1000000) >>> rvsp = np.random.pareto(5, 1000000) >>> xx = np.linspace(0,1,100) - >>> powpdf = stats.powerlaw.pdf(xx,5) + >>> powpdf = stats.powerlaw.pdf(xx,5) # doctest: +SKIP >>> plt.figure() >>> plt.hist(rvs, bins=50, density=True) - >>> plt.plot(xx,powpdf,'r-') + >>> plt.plot(xx,powpdf,'r-') # doctest: +SKIP >>> plt.title('np.random.power(5)') >>> plt.figure() >>> plt.hist(1./(1.+rvsp), bins=50, density=True) - >>> plt.plot(xx,powpdf,'r-') + >>> plt.plot(xx,powpdf,'r-') # doctest: +SKIP >>> plt.title('inverse of 1 + np.random.pareto(5)') >>> plt.figure() >>> plt.hist(1./(1.+rvsp), bins=50, density=True) - >>> plt.plot(xx,powpdf,'r-') + >>> plt.plot(xx,powpdf,'r-') # doctest: +SKIP >>> plt.title('inverse of stats.pareto(5)') """ - cdef ndarray oa - cdef double fa - - oa = <ndarray>PyArray_FROM_OTF(a, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - - if oa.shape == (): - fa = PyFloat_AsDouble(a) - if np.signbit(fa): - raise ValueError("a < 0") - return cont1_array_sc(self.internal_state, rk_power, size, fa, - self.lock) - - if np.any(np.signbit(oa)): - raise ValueError("a < 0") - return cont1_array(self.internal_state, rk_power, size, oa, self.lock) + return cont(&legacy_power, &self._aug_state, size, self.lock, 1, + a, 'a', CONS_POSITIVE, + 0.0, '', CONS_NONE, + 0.0, '', CONS_NONE, None) def laplace(self, loc=0.0, scale=1.0, size=None): """ @@ -3000,12 +2616,17 @@ cdef class RandomState: difference between two independent, identically distributed exponential random variables. + .. note:: + New code should use the ``laplace`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. + Parameters ---------- loc : float or array_like of floats, optional The position, :math:`\\mu`, of the distribution peak. Default is 0. scale : float or array_like of floats, optional - :math:`\\lambda`, the exponential decay. Default is 1. + :math:`\\lambda`, the exponential decay. Default is 1. Must be non- + negative. size : int or tuple of ints, optional Output shape. If the given shape is, e.g., ``(m, n, k)``, then ``m * n * k`` samples are drawn. If size is ``None`` (default), @@ -3017,6 +2638,10 @@ cdef class RandomState: out : ndarray or scalar Drawn samples from the parameterized Laplace distribution. + See Also + -------- + Generator.laplace: which should be used for new code. + Notes ----- It has the probability density function @@ -3042,7 +2667,7 @@ cdef class RandomState: From MathWorld--A Wolfram Web Resource. http://mathworld.wolfram.com/LaplaceDistribution.html .. [4] Wikipedia, "Laplace distribution", - http://en.wikipedia.org/wiki/Laplace_distribution + https://en.wikipedia.org/wiki/Laplace_distribution Examples -------- @@ -3067,24 +2692,10 @@ cdef class RandomState: >>> plt.plot(x,g) """ - cdef ndarray oloc, oscale - cdef double floc, fscale - - oloc = PyArray_FROM_OTF(loc, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - oscale = PyArray_FROM_OTF(scale, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - - if oloc.shape == oscale.shape == (): - floc = PyFloat_AsDouble(loc) - fscale = PyFloat_AsDouble(scale) - if np.signbit(fscale): - raise ValueError("scale < 0") - return cont2_array_sc(self.internal_state, rk_laplace, size, floc, - fscale, self.lock) - - if np.any(np.signbit(oscale)): - raise ValueError("scale < 0") - return cont2_array(self.internal_state, rk_laplace, size, oloc, oscale, - self.lock) + return cont(&random_laplace, &self._bitgen, size, self.lock, 2, + loc, 'loc', CONS_NONE, + scale, 'scale', CONS_NON_NEGATIVE, + 0.0, '', CONS_NONE, None) def gumbel(self, loc=0.0, scale=1.0, size=None): """ @@ -3096,12 +2707,17 @@ cdef class RandomState: scale. For more information on the Gumbel distribution, see Notes and References below. + .. note:: + New code should use the ``gumbel`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. + Parameters ---------- loc : float or array_like of floats, optional The location of the mode of the distribution. Default is 0. scale : float or array_like of floats, optional - The scale parameter of the distribution. Default is 1. + The scale parameter of the distribution. Default is 1. Must be non- + negative. size : int or tuple of ints, optional Output shape. If the given shape is, e.g., ``(m, n, k)``, then ``m * n * k`` samples are drawn. If size is ``None`` (default), @@ -3119,6 +2735,7 @@ cdef class RandomState: scipy.stats.gumbel_r scipy.stats.genextreme weibull + Generator.gumbel: which should be used for new code. Notes ----- @@ -3198,24 +2815,10 @@ cdef class RandomState: >>> plt.show() """ - cdef ndarray oloc, oscale - cdef double floc, fscale - - oloc = PyArray_FROM_OTF(loc, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - oscale = PyArray_FROM_OTF(scale, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - - if oloc.shape == oscale.shape == (): - floc = PyFloat_AsDouble(loc) - fscale = PyFloat_AsDouble(scale) - if np.signbit(fscale): - raise ValueError("scale < 0") - return cont2_array_sc(self.internal_state, rk_gumbel, size, floc, - fscale, self.lock) - - if np.any(np.signbit(oscale)): - raise ValueError("scale < 0") - return cont2_array(self.internal_state, rk_gumbel, size, oloc, oscale, - self.lock) + return cont(&random_gumbel, &self._bitgen, size, self.lock, 2, + loc, 'loc', CONS_NONE, + scale, 'scale', CONS_NON_NEGATIVE, + 0.0, '', CONS_NONE, None) def logistic(self, loc=0.0, scale=1.0, size=None): """ @@ -3226,12 +2829,16 @@ cdef class RandomState: Samples are drawn from a logistic distribution with specified parameters, loc (location or mean, also median), and scale (>0). + .. note:: + New code should use the ``logistic`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. + Parameters ---------- loc : float or array_like of floats, optional Parameter of the distribution. Default is 0. scale : float or array_like of floats, optional - Parameter of the distribution. Should be greater than zero. + Parameter of the distribution. Must be non-negative. Default is 1. size : int or tuple of ints, optional Output shape. If the given shape is, e.g., ``(m, n, k)``, then @@ -3248,6 +2855,7 @@ cdef class RandomState: -------- scipy.stats.logistic : probability density function, distribution or cumulative density function, etc. + Generator.logistic: which should be used for new code. Notes ----- @@ -3272,7 +2880,7 @@ cdef class RandomState: MathWorld--A Wolfram Web Resource. http://mathworld.wolfram.com/LogisticDistribution.html .. [3] Wikipedia, "Logistic-distribution", - http://en.wikipedia.org/wiki/Logistic_distribution + https://en.wikipedia.org/wiki/Logistic_distribution Examples -------- @@ -3280,35 +2888,22 @@ cdef class RandomState: >>> loc, scale = 10, 1 >>> s = np.random.logistic(loc, scale, 10000) + >>> import matplotlib.pyplot as plt >>> count, bins, ignored = plt.hist(s, bins=50) # plot against distribution >>> def logist(x, loc, scale): - ... return exp((loc-x)/scale)/(scale*(1+exp((loc-x)/scale))**2) - >>> plt.plot(bins, logist(bins, loc, scale)*count.max()/\\ - ... logist(bins, loc, scale).max()) + ... return np.exp((loc-x)/scale)/(scale*(1+np.exp((loc-x)/scale))**2) + >>> lgst_val = logist(bins, loc, scale) + >>> plt.plot(bins, lgst_val * count.max() / lgst_val.max()) >>> plt.show() """ - cdef ndarray oloc, oscale - cdef double floc, fscale - - oloc = PyArray_FROM_OTF(loc, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - oscale = PyArray_FROM_OTF(scale, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - - if oloc.shape == oscale.shape == (): - floc = PyFloat_AsDouble(loc) - fscale = PyFloat_AsDouble(scale) - if np.signbit(fscale): - raise ValueError("scale < 0") - return cont2_array_sc(self.internal_state, rk_logistic, size, floc, - fscale, self.lock) - - if np.any(np.signbit(oscale)): - raise ValueError("scale < 0") - return cont2_array(self.internal_state, rk_logistic, size, oloc, - oscale, self.lock) + return cont(&random_logistic, &self._bitgen, size, self.lock, 2, + loc, 'loc', CONS_NONE, + scale, 'scale', CONS_NON_NEGATIVE, + 0.0, '', CONS_NONE, None) def lognormal(self, mean=0.0, sigma=1.0, size=None): """ @@ -3321,13 +2916,17 @@ cdef class RandomState: deviation are not the values for the distribution itself, but of the underlying normal distribution it is derived from. + .. note:: + New code should use the ``lognormal`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. + Parameters ---------- mean : float or array_like of floats, optional Mean value of the underlying normal distribution. Default is 0. sigma : float or array_like of floats, optional - Standard deviation of the underlying normal distribution. Should - be greater than zero. Default is 1. + Standard deviation of the underlying normal distribution. Must be + non-negative. Default is 1. size : int or tuple of ints, optional Output shape. If the given shape is, e.g., ``(m, n, k)``, then ``m * n * k`` samples are drawn. If size is ``None`` (default), @@ -3343,6 +2942,7 @@ cdef class RandomState: -------- scipy.stats.lognorm : probability density function, distribution, cumulative density function, etc. + Generator.lognormal: which should be used for new code. Notes ----- @@ -3366,7 +2966,7 @@ cdef class RandomState: .. [1] Limpert, E., Stahel, W. A., and Abbt, M., "Log-normal Distributions across the Sciences: Keys and Clues," BioScience, Vol. 51, No. 5, May, 2001. - http://stat.ethz.ch/~stahel/lognormal/bioscience.pdf + https://stat.ethz.ch/~stahel/lognormal/bioscience.pdf .. [2] Reiss, R.D. and Thomas, M., "Statistical Analysis of Extreme Values," Basel: Birkhauser Verlag, 2001, pp. 31-32. @@ -3399,7 +2999,7 @@ cdef class RandomState: >>> # values, drawn from a normal distribution. >>> b = [] >>> for i in range(1000): - ... a = 10. + np.random.random(100) + ... a = 10. + np.random.standard_normal(100) ... b.append(np.product(a)) >>> b = np.array(b) / np.min(b) # scale values to be positive @@ -3415,24 +3015,10 @@ cdef class RandomState: >>> plt.show() """ - cdef ndarray omean, osigma - cdef double fmean, fsigma - - omean = PyArray_FROM_OTF(mean, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - osigma = PyArray_FROM_OTF(sigma, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - - if omean.shape == osigma.shape == (): - fmean = PyFloat_AsDouble(mean) - fsigma = PyFloat_AsDouble(sigma) - if np.signbit(fsigma): - raise ValueError("sigma < 0") - return cont2_array_sc(self.internal_state, rk_lognormal, size, - fmean, fsigma, self.lock) - - if np.any(np.signbit(osigma)): - raise ValueError("sigma < 0.0") - return cont2_array(self.internal_state, rk_lognormal, size, omean, - osigma, self.lock) + return cont(&legacy_lognormal, &self._aug_state, size, self.lock, 2, + mean, 'mean', CONS_NONE, + sigma, 'sigma', CONS_NON_NEGATIVE, + 0.0, '', CONS_NONE, None) def rayleigh(self, scale=1.0, size=None): """ @@ -3443,10 +3029,14 @@ cdef class RandomState: The :math:`\\chi` and Weibull distributions are generalizations of the Rayleigh. + .. note:: + New code should use the ``rayleigh`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. + Parameters ---------- scale : float or array_like of floats, optional - Scale, also equals the mode. Should be >= 0. Default is 1. + Scale, also equals the mode. Must be non-negative. Default is 1. size : int or tuple of ints, optional Output shape. If the given shape is, e.g., ``(m, n, k)``, then ``m * n * k`` samples are drawn. If size is ``None`` (default), @@ -3458,6 +3048,10 @@ cdef class RandomState: out : ndarray or scalar Drawn samples from the parameterized Rayleigh distribution. + See Also + -------- + Generator.rayleigh: which should be used for new code. + Notes ----- The probability density function for the Rayleigh distribution is @@ -3472,14 +3066,15 @@ cdef class RandomState: References ---------- .. [1] Brighton Webs Ltd., "Rayleigh Distribution," - http://www.brighton-webs.co.uk/distributions/rayleigh.asp + https://web.archive.org/web/20090514091424/http://brighton-webs.co.uk:80/distributions/rayleigh.asp .. [2] Wikipedia, "Rayleigh distribution" - http://en.wikipedia.org/wiki/Rayleigh_distribution + https://en.wikipedia.org/wiki/Rayleigh_distribution Examples -------- Draw values from the distribution and plot the histogram + >>> from matplotlib.pyplot import hist >>> values = hist(np.random.rayleigh(3, 100000), bins=200, density=True) Wave heights tend to follow a Rayleigh distribution. If the mean wave @@ -3493,25 +3088,13 @@ cdef class RandomState: The percentage of waves larger than 3 meters is: >>> 100.*sum(s>3)/1000000. - 0.087300000000000003 + 0.087300000000000003 # random """ - cdef ndarray oscale - cdef double fscale - - oscale = <ndarray>PyArray_FROM_OTF(scale, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - - if oscale.shape == (): - fscale = PyFloat_AsDouble(scale) - if np.signbit(fscale): - raise ValueError("scale < 0") - return cont1_array_sc(self.internal_state, rk_rayleigh, size, - fscale, self.lock) - - if np.any(np.signbit(oscale)): - raise ValueError("scale < 0.0") - return cont1_array(self.internal_state, rk_rayleigh, size, oscale, - self.lock) + return cont(&legacy_rayleigh, &self._bitgen, size, self.lock, 1, + scale, 'scale', CONS_NON_NEGATIVE, + 0.0, '', CONS_NONE, + 0.0, '', CONS_NONE, None) def wald(self, mean, scale, size=None): """ @@ -3528,12 +3111,16 @@ cdef class RandomState: because there is an inverse relationship between the time to cover a unit distance and distance covered in unit time. + .. note:: + New code should use the ``wald`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. + Parameters ---------- mean : float or array_like of floats - Distribution mean, should be > 0. + Distribution mean, must be > 0. scale : float or array_like of floats - Scale parameter, should be >= 0. + Scale parameter, must be > 0. size : int or tuple of ints, optional Output shape. If the given shape is, e.g., ``(m, n, k)``, then ``m * n * k`` samples are drawn. If size is ``None`` (default), @@ -3545,6 +3132,10 @@ cdef class RandomState: out : ndarray or scalar Drawn samples from the parameterized Wald distribution. + See Also + -------- + Generator.wald: which should be used for new code. + Notes ----- The probability density function for the Wald distribution is @@ -3560,12 +3151,12 @@ cdef class RandomState: References ---------- .. [1] Brighton Webs Ltd., Wald Distribution, - http://www.brighton-webs.co.uk/distributions/wald.asp + https://web.archive.org/web/20090423014010/http://www.brighton-webs.co.uk:80/distributions/wald.asp .. [2] Chhikara, Raj S., and Folks, J. Leroy, "The Inverse Gaussian Distribution: Theory : Methodology, and Applications", CRC Press, 1988. - .. [3] Wikipedia, "Wald distribution" - http://en.wikipedia.org/wiki/Wald_distribution + .. [3] Wikipedia, "Inverse Gaussian distribution" + https://en.wikipedia.org/wiki/Inverse_Gaussian_distribution Examples -------- @@ -3576,29 +3167,10 @@ cdef class RandomState: >>> plt.show() """ - cdef ndarray omean, oscale - cdef double fmean, fscale - - omean = PyArray_FROM_OTF(mean, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - oscale = PyArray_FROM_OTF(scale, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - - if omean.shape == oscale.shape == (): - fmean = PyFloat_AsDouble(mean) - fscale = PyFloat_AsDouble(scale) - - if fmean <= 0: - raise ValueError("mean <= 0") - if fscale <= 0: - raise ValueError("scale <= 0") - return cont2_array_sc(self.internal_state, rk_wald, size, fmean, - fscale, self.lock) - - if np.any(np.less_equal(omean,0.0)): - raise ValueError("mean <= 0.0") - elif np.any(np.less_equal(oscale,0.0)): - raise ValueError("scale <= 0.0") - return cont2_array(self.internal_state, rk_wald, size, omean, oscale, - self.lock) + return cont(&legacy_wald, &self._aug_state, size, self.lock, 2, + mean, 'mean', CONS_POSITIVE, + scale, 'scale', CONS_POSITIVE, + 0.0, '', CONS_NONE, None) def triangular(self, left, mode, right, size=None): """ @@ -3612,15 +3184,19 @@ cdef class RandomState: limit right. Unlike the other distributions, these parameters directly define the shape of the pdf. + .. note:: + New code should use the ``triangular`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. + Parameters ---------- left : float or array_like of floats Lower limit. mode : float or array_like of floats The value where the peak of the distribution occurs. - The value should fulfill the condition ``left <= mode <= right``. + The value must fulfill the condition ``left <= mode <= right``. right : float or array_like of floats - Upper limit, should be larger than `left`. + Upper limit, must be larger than `left`. size : int or tuple of ints, optional Output shape. If the given shape is, e.g., ``(m, n, k)``, then ``m * n * k`` samples are drawn. If size is ``None`` (default), @@ -3633,6 +3209,10 @@ cdef class RandomState: out : ndarray or scalar Drawn samples from the parameterized triangular distribution. + See Also + -------- + Generator.triangular: which should be used for new code. + Notes ----- The probability density function for the triangular distribution is @@ -3651,7 +3231,7 @@ cdef class RandomState: References ---------- .. [1] Wikipedia, "Triangular distribution" - http://en.wikipedia.org/wiki/Triangular_distribution + https://en.wikipedia.org/wiki/Triangular_distribution Examples -------- @@ -3663,14 +3243,15 @@ cdef class RandomState: >>> plt.show() """ - cdef ndarray oleft, omode, oright + cdef bint is_scalar = True cdef double fleft, fmode, fright + cdef np.ndarray oleft, omode, oright - oleft = <ndarray>PyArray_FROM_OTF(left, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - omode = <ndarray>PyArray_FROM_OTF(mode, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - oright = <ndarray>PyArray_FROM_OTF(right, NPY_DOUBLE, NPY_ARRAY_ALIGNED) + oleft = <np.ndarray>np.PyArray_FROM_OTF(left, np.NPY_DOUBLE, np.NPY_ALIGNED) + omode = <np.ndarray>np.PyArray_FROM_OTF(mode, np.NPY_DOUBLE, np.NPY_ALIGNED) + oright = <np.ndarray>np.PyArray_FROM_OTF(right, np.NPY_DOUBLE, np.NPY_ALIGNED) - if oleft.shape == omode.shape == oright.shape == (): + if np.PyArray_NDIM(oleft) == np.PyArray_NDIM(omode) == np.PyArray_NDIM(oright) == 0: fleft = PyFloat_AsDouble(left) fright = PyFloat_AsDouble(right) fmode = PyFloat_AsDouble(mode) @@ -3681,8 +3262,10 @@ cdef class RandomState: raise ValueError("mode > right") if fleft == fright: raise ValueError("left == right") - return cont3_array_sc(self.internal_state, rk_triangular, size, - fleft, fmode, fright, self.lock) + return cont(&random_triangular, &self._bitgen, size, self.lock, 3, + fleft, '', CONS_NONE, + fmode, '', CONS_NONE, + fright, '', CONS_NONE, None) if np.any(np.greater(oleft, omode)): raise ValueError("left > mode") @@ -3690,8 +3273,11 @@ cdef class RandomState: raise ValueError("mode > right") if np.any(np.equal(oleft, oright)): raise ValueError("left == right") - return cont3_array(self.internal_state, rk_triangular, size, oleft, - omode, oright, self.lock) + + return cont_broadcast_3(&random_triangular, &self._bitgen, size, self.lock, + oleft, '', CONS_NONE, + omode, '', CONS_NONE, + oright, '', CONS_NONE) # Complicated, discrete distributions: def binomial(self, n, p, size=None): @@ -3705,6 +3291,10 @@ cdef class RandomState: n an integer >= 0 and p is in the interval [0,1]. (n may be input as a float, but it is truncated to an integer in use) + .. note:: + New code should use the ``binomial`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. + Parameters ---------- n : int or array_like of ints @@ -3728,6 +3318,7 @@ cdef class RandomState: -------- scipy.stats.binom : probability density function, distribution or cumulative density function, etc. + Generator.binomial: which should be used for new code. Notes ----- @@ -3758,7 +3349,7 @@ cdef class RandomState: Wolfram Web Resource. http://mathworld.wolfram.com/BinomialDistribution.html .. [5] Wikipedia, "Binomial distribution", - http://en.wikipedia.org/wiki/Binomial_distribution + https://en.wikipedia.org/wiki/Binomial_distribution Examples -------- @@ -3779,36 +3370,66 @@ cdef class RandomState: # answer = 0.38885, or 38%. """ - cdef ndarray on, op - cdef long ln - cdef double fp - - on = <ndarray>PyArray_FROM_OTF(n, NPY_LONG, NPY_ARRAY_ALIGNED) - op = <ndarray>PyArray_FROM_OTF(p, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - - if on.shape == op.shape == (): - fp = PyFloat_AsDouble(p) - ln = PyInt_AsLong(n) - - if ln < 0: - raise ValueError("n < 0") - if fp < 0: - raise ValueError("p < 0") - elif fp > 1: - raise ValueError("p > 1") - elif np.isnan(fp): - raise ValueError("p is nan") - return discnp_array_sc(self.internal_state, rk_binomial, size, ln, - fp, self.lock) - - if np.any(np.less(n, 0)): - raise ValueError("n < 0") - if np.any(np.less(p, 0)): - raise ValueError("p < 0") - if np.any(np.greater(p, 1)): - raise ValueError("p > 1") - return discnp_array(self.internal_state, rk_binomial, size, on, op, - self.lock) + + # Uses a custom implementation since self._binomial is required + cdef double _dp = 0 + cdef long _in = 0 + cdef bint is_scalar = True + cdef np.npy_intp i, cnt + cdef np.ndarray randoms + cdef long *randoms_data + cdef np.broadcast it + + p_arr = <np.ndarray>np.PyArray_FROM_OTF(p, np.NPY_DOUBLE, np.NPY_ALIGNED) + is_scalar = is_scalar and np.PyArray_NDIM(p_arr) == 0 + n_arr = <np.ndarray>np.PyArray_FROM_OTF(n, np.NPY_LONG, np.NPY_ALIGNED) + is_scalar = is_scalar and np.PyArray_NDIM(n_arr) == 0 + + if not is_scalar: + check_array_constraint(p_arr, 'p', CONS_BOUNDED_0_1) + check_array_constraint(n_arr, 'n', CONS_NON_NEGATIVE) + if size is not None: + randoms = <np.ndarray>np.empty(size, int) + else: + it = np.PyArray_MultiIterNew2(p_arr, n_arr) + randoms = <np.ndarray>np.empty(it.shape, int) + + cnt = np.PyArray_SIZE(randoms) + + it = np.PyArray_MultiIterNew3(randoms, p_arr, n_arr) + validate_output_shape(it.shape, randoms) + with self.lock, nogil: + for i in range(cnt): + _dp = (<double*>np.PyArray_MultiIter_DATA(it, 1))[0] + _in = (<long*>np.PyArray_MultiIter_DATA(it, 2))[0] + (<long*>np.PyArray_MultiIter_DATA(it, 0))[0] = \ + legacy_random_binomial(&self._bitgen, _dp, _in, + &self._binomial) + + np.PyArray_MultiIter_NEXT(it) + + return randoms + + _dp = PyFloat_AsDouble(p) + _in = <long>n + check_constraint(_dp, 'p', CONS_BOUNDED_0_1) + check_constraint(<double>_in, 'n', CONS_NON_NEGATIVE) + + if size is None: + with self.lock: + return <long>legacy_random_binomial(&self._bitgen, _dp, _in, + &self._binomial) + + randoms = <np.ndarray>np.empty(size, int) + cnt = np.PyArray_SIZE(randoms) + randoms_data = <long *>np.PyArray_DATA(randoms) + + with self.lock, nogil: + for i in range(cnt): + randoms_data[i] = legacy_random_binomial(&self._bitgen, _dp, _in, + &self._binomial) + + return randoms def negative_binomial(self, n, p, size=None): """ @@ -3817,14 +3438,17 @@ cdef class RandomState: Draw samples from a negative binomial distribution. Samples are drawn from a negative binomial distribution with specified - parameters, `n` successes and `p` probability of success where `n` is an - integer > 0 and `p` is in the interval [0, 1]. + parameters, `n` successes and `p` probability of success where `n` + is > 0 and `p` is in the interval [0, 1]. + + .. note:: + New code should use the ``negative_binomial`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. Parameters ---------- - n : int or array_like of ints - Parameter of the distribution, > 0. Floats are also accepted, - but they will be truncated to integers. + n : float or array_like of floats + Parameter of the distribution, > 0. p : float or array_like of floats Parameter of the distribution, >= 0 and <=1. size : int or tuple of ints, optional @@ -3840,16 +3464,23 @@ cdef class RandomState: where each sample is equal to N, the number of failures that occurred before a total of n successes was reached. + See Also + -------- + Generator.negative_binomial: which should be used for new code. + Notes ----- - The probability density for the negative binomial distribution is + The probability mass function of the negative binomial distribution is - .. math:: P(N;n,p) = \\binom{N+n-1}{N}p^{n}(1-p)^{N}, + .. math:: P(N;n,p) = \\frac{\\Gamma(N+n)}{N!\\Gamma(n)}p^{n}(1-p)^{N}, where :math:`n` is the number of successes, :math:`p` is the - probability of success, and :math:`N+n` is the number of trials. - The negative binomial distribution gives the probability of N - failures given n successes, with a success on the last trial. + probability of success, :math:`N+n` is the number of trials, and + :math:`\\Gamma` is the gamma function. When :math:`n` is an integer, + :math:`\\frac{\\Gamma(N+n)}{N!\\Gamma(n)} = \\binom{N+n-1}{N}`, which is + the more common form of this term in the the pmf. The negative + binomial distribution gives the probability of N failures given n + successes, with a success on the last trial. If one throws a die repeatedly until the third time a "1" appears, then the probability distribution of the number of non-"1"s that @@ -3861,7 +3492,7 @@ cdef class RandomState: MathWorld--A Wolfram Web Resource. http://mathworld.wolfram.com/NegativeBinomialDistribution.html .. [2] Wikipedia, "Negative binomial distribution", - http://en.wikipedia.org/wiki/Negative_binomial_distribution + https://en.wikipedia.org/wiki/Negative_binomial_distribution Examples -------- @@ -3874,40 +3505,17 @@ cdef class RandomState: single success after drilling 5 wells, after 6 wells, etc.? >>> s = np.random.negative_binomial(1, 0.1, 100000) - >>> for i in range(1, 11): + >>> for i in range(1, 11): # doctest: +SKIP ... probability = sum(s<i) / 100000. - ... print i, "wells drilled, probability of one success =", probability + ... print(i, "wells drilled, probability of one success =", probability) """ - cdef ndarray on - cdef ndarray op - cdef double fn - cdef double fp - - on = <ndarray>PyArray_FROM_OTF(n, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - op = <ndarray>PyArray_FROM_OTF(p, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - - if on.shape == op.shape == (): - fp = PyFloat_AsDouble(p) - fn = PyFloat_AsDouble(n) - - if fn <= 0: - raise ValueError("n <= 0") - if fp < 0: - raise ValueError("p < 0") - elif fp > 1: - raise ValueError("p > 1") - return discdd_array_sc(self.internal_state, rk_negative_binomial, - size, fn, fp, self.lock) - - if np.any(np.less_equal(n, 0)): - raise ValueError("n <= 0") - if np.any(np.less(p, 0)): - raise ValueError("p < 0") - if np.any(np.greater(p, 1)): - raise ValueError("p > 1") - return discdd_array(self.internal_state, rk_negative_binomial, size, - on, op, self.lock) + out = disc(&legacy_negative_binomial, &self._aug_state, size, self.lock, 2, 0, + n, 'n', CONS_POSITIVE, + p, 'p', CONS_BOUNDED_0_1, + 0.0, '', CONS_NONE) + # Match historical output type + return int64_to_long(out) def poisson(self, lam=1.0, size=None): """ @@ -3918,11 +3526,16 @@ cdef class RandomState: The Poisson distribution is the limit of the binomial distribution for large N. + .. note:: + New code should use the ``poisson`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. + Parameters ---------- lam : float or array_like of floats - Expectation of interval, should be >= 0. A sequence of expectation - intervals must be broadcastable over the requested size. + Expected number of events occurring in a fixed-time interval, + must be >= 0. A sequence must be broadcastable over the requested + size. size : int or tuple of ints, optional Output shape. If the given shape is, e.g., ``(m, n, k)``, then ``m * n * k`` samples are drawn. If size is ``None`` (default), @@ -3934,6 +3547,10 @@ cdef class RandomState: out : ndarray or scalar Drawn samples from the parameterized Poisson distribution. + See Also + -------- + Generator.poisson: which should be used for new code. + Notes ----- The Poisson distribution @@ -3945,7 +3562,7 @@ cdef class RandomState: :math:`k` events occurring within the observed interval :math:`\\lambda`. - Because the output is limited to the range of the C long type, a + Because the output is limited to the range of the C int64 type, a ValueError is raised when `lam` is within 10 sigma of the maximum representable value. @@ -3955,7 +3572,7 @@ cdef class RandomState: From MathWorld--A Wolfram Web Resource. http://mathworld.wolfram.com/PoissonDistribution.html .. [2] Wikipedia, "Poisson distribution", - http://en.wikipedia.org/wiki/Poisson_distribution + https://en.wikipedia.org/wiki/Poisson_distribution Examples -------- @@ -3975,27 +3592,12 @@ cdef class RandomState: >>> s = np.random.poisson(lam=(100., 500.), size=(100, 2)) """ - cdef ndarray olam - cdef double flam - - olam = <ndarray>PyArray_FROM_OTF(lam, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - - if olam.shape == (): - flam = PyFloat_AsDouble(lam) - - if lam < 0: - raise ValueError("lam < 0") - if lam > self.poisson_lam_max: - raise ValueError("lam value too large") - return discd_array_sc(self.internal_state, rk_poisson, size, flam, - self.lock) - - if np.any(np.less(olam, 0)): - raise ValueError("lam < 0") - if np.any(np.greater(olam, self.poisson_lam_max)): - raise ValueError("lam value too large.") - return discd_array(self.internal_state, rk_poisson, size, olam, - self.lock) + out = disc(&legacy_random_poisson, &self._bitgen, size, self.lock, 1, 0, + lam, 'lam', LEGACY_CONS_POISSON, + 0.0, '', CONS_NONE, + 0.0, '', CONS_NONE) + # Match historical output type + return int64_to_long(out) def zipf(self, a, size=None): """ @@ -4007,14 +3609,18 @@ cdef class RandomState: `a` > 1. The Zipf distribution (also known as the zeta distribution) is a - continuous probability distribution that satisfies Zipf's law: the + discrete probability distribution that satisfies Zipf's law: the frequency of an item is inversely proportional to its rank in a frequency table. + .. note:: + New code should use the ``zipf`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. + Parameters ---------- a : float or array_like of floats - Distribution parameter. Should be greater than 1. + Distribution parameter. Must be greater than 1. size : int or tuple of ints, optional Output shape. If the given shape is, e.g., ``(m, n, k)``, then ``m * n * k`` samples are drawn. If size is ``None`` (default), @@ -4030,14 +3636,16 @@ cdef class RandomState: -------- scipy.stats.zipf : probability density function, distribution, or cumulative density function, etc. + Generator.zipf: which should be used for new code. Notes ----- The probability density for the Zipf distribution is - .. math:: p(x) = \\frac{x^{-a}}{\\zeta(a)}, + .. math:: p(k) = \\frac{k^{-a}}{\\zeta(a)}, - where :math:`\\zeta` is the Riemann Zeta function. + for integers :math:`k \geq 1`, where :math:`\\zeta` is the Riemann Zeta + function. It is named for the American linguist George Kingsley Zipf, who noted that the frequency of any word in a sample of a language is inversely @@ -4053,42 +3661,38 @@ cdef class RandomState: -------- Draw samples from the distribution: - >>> a = 2. # parameter - >>> s = np.random.zipf(a, 1000) + >>> a = 4.0 + >>> n = 20000 + >>> s = np.random.zipf(a, n) Display the histogram of the samples, along with - the probability density function: + the expected histogram based on the probability + density function: >>> import matplotlib.pyplot as plt - >>> from scipy import special + >>> from scipy.special import zeta # doctest: +SKIP + + `bincount` provides a fast histogram for small integers. - Truncate s values at 50 so plot is interesting: + >>> count = np.bincount(s) + >>> k = np.arange(1, s.max() + 1) - >>> count, bins, ignored = plt.hist(s[s<50], 50, density=True) - >>> x = np.arange(1., 50.) - >>> y = x**(-a) / special.zetac(a) - >>> plt.plot(x, y/max(y), linewidth=2, color='r') + >>> plt.bar(k, count[1:], alpha=0.5, label='sample count') + >>> plt.plot(k, n*(k**-a)/zeta(a), 'k.-', alpha=0.5, + ... label='expected count') # doctest: +SKIP + >>> plt.semilogy() + >>> plt.grid(alpha=0.4) + >>> plt.legend() + >>> plt.title(f'Zipf sample, a={a}, size={n}') >>> plt.show() """ - cdef ndarray oa - cdef double fa - - oa = <ndarray>PyArray_FROM_OTF(a, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - - if oa.shape == (): - fa = PyFloat_AsDouble(a) - - # use logic that ensures NaN is rejected. - if not fa > 1.0: - raise ValueError("'a' must be a valid float > 1.0") - return discd_array_sc(self.internal_state, rk_zipf, size, fa, - self.lock) - - # use logic that ensures NaN is rejected. - if not np.all(np.greater(oa, 1.0)): - raise ValueError("'a' must contain valid floats > 1.0") - return discd_array(self.internal_state, rk_zipf, size, oa, self.lock) + out = disc(&legacy_random_zipf, &self._bitgen, size, self.lock, 1, 0, + a, 'a', CONS_GT_1, + 0.0, '', CONS_NONE, + 0.0, '', CONS_NONE) + # Match historical output type + return int64_to_long(out) def geometric(self, p, size=None): """ @@ -4108,6 +3712,10 @@ cdef class RandomState: where `p` is the probability of success of an individual trial. + .. note:: + New code should use the ``geometric`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. + Parameters ---------- p : float or array_like of floats @@ -4123,6 +3731,10 @@ cdef class RandomState: out : ndarray or scalar Drawn samples from the parameterized geometric distribution. + See Also + -------- + Generator.geometric: which should be used for new code. + Examples -------- Draw ten thousand values from the geometric distribution, @@ -4136,27 +3748,12 @@ cdef class RandomState: 0.34889999999999999 #random """ - cdef ndarray op - cdef double fp - - op = <ndarray>PyArray_FROM_OTF(p, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - - if op.shape == (): - fp = PyFloat_AsDouble(p) - - if fp < 0.0: - raise ValueError("p < 0.0") - if fp > 1.0: - raise ValueError("p > 1.0") - return discd_array_sc(self.internal_state, rk_geometric, size, fp, - self.lock) - - if np.any(np.less(op, 0.0)): - raise ValueError("p < 0.0") - if np.any(np.greater(op, 1.0)): - raise ValueError("p > 1.0") - return discd_array(self.internal_state, rk_geometric, size, op, - self.lock) + out = disc(&legacy_random_geometric, &self._bitgen, size, self.lock, 1, 0, + p, 'p', CONS_BOUNDED_GT_0_1, + 0.0, '', CONS_NONE, + 0.0, '', CONS_NONE) + # Match historical output type + return int64_to_long(out) def hypergeometric(self, ngood, nbad, nsample, size=None): """ @@ -4165,9 +3762,13 @@ cdef class RandomState: Draw samples from a Hypergeometric distribution. Samples are drawn from a hypergeometric distribution with specified - parameters, ngood (ways to make a good selection), nbad (ways to make - a bad selection), and nsample = number of items sampled, which is less - than or equal to the sum ngood + nbad. + parameters, `ngood` (ways to make a good selection), `nbad` (ways to make + a bad selection), and `nsample` (number of items sampled, which is less + than or equal to the sum ``ngood + nbad``). + + .. note:: + New code should use the ``hypergeometric`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. Parameters ---------- @@ -4181,33 +3782,36 @@ cdef class RandomState: size : int or tuple of ints, optional Output shape. If the given shape is, e.g., ``(m, n, k)``, then ``m * n * k`` samples are drawn. If size is ``None`` (default), - a single value is returned if ``ngood``, ``nbad``, and ``nsample`` + a single value is returned if `ngood`, `nbad`, and `nsample` are all scalars. Otherwise, ``np.broadcast(ngood, nbad, nsample).size`` samples are drawn. Returns ------- out : ndarray or scalar - Drawn samples from the parameterized hypergeometric distribution. + Drawn samples from the parameterized hypergeometric distribution. Each + sample is the number of good items within a randomly selected subset of + size `nsample` taken from a set of `ngood` good items and `nbad` bad items. See Also -------- scipy.stats.hypergeom : probability density function, distribution or cumulative density function, etc. + Generator.hypergeometric: which should be used for new code. Notes ----- The probability density for the Hypergeometric distribution is - .. math:: P(x) = \\frac{\\binom{m}{n}\\binom{N-m}{n-x}}{\\binom{N}{n}}, + .. math:: P(x) = \\frac{\\binom{g}{x}\\binom{b}{n-x}}{\\binom{g+b}{n}}, - where :math:`0 \\le x \\le m` and :math:`n+m-N \\le x \\le n` + where :math:`0 \\le x \\le n` and :math:`n-b \\le x \\le g` - for P(x) the probability of x successes, n = ngood, m = nbad, and - N = number of samples. + for P(x) the probability of ``x`` good results in the drawn sample, + g = `ngood`, b = `nbad`, and n = `nsample`. - Consider an urn with black and white marbles in it, ngood of them - black and nbad are white. If you draw nsample balls without + Consider an urn with black and white marbles in it, `ngood` of them + are black and `nbad` are white. If you draw `nsample` balls without replacement, then the hypergeometric distribution describes the distribution of black balls in the drawn sample. @@ -4225,7 +3829,7 @@ cdef class RandomState: MathWorld--A Wolfram Web Resource. http://mathworld.wolfram.com/HypergeometricDistribution.html .. [3] Wikipedia, "Hypergeometric distribution", - http://en.wikipedia.org/wiki/Hypergeometric_distribution + https://en.wikipedia.org/wiki/Hypergeometric_distribution Examples -------- @@ -4234,6 +3838,7 @@ cdef class RandomState: >>> ngood, nbad, nsamp = 100, 2, 10 # number of good, number of bad, and number of samples >>> s = np.random.hypergeometric(ngood, nbad, nsamp, 1000) + >>> from matplotlib.pyplot import hist >>> hist(s) # note that it is very unlikely to grab both bad items @@ -4246,39 +3851,42 @@ cdef class RandomState: # answer = 0.003 ... pretty unlikely! """ - cdef ndarray ongood, onbad, onsample - cdef long lngood, lnbad, lnsample - - ongood = <ndarray>PyArray_FROM_OTF(ngood, NPY_LONG, NPY_ARRAY_ALIGNED) - onbad = <ndarray>PyArray_FROM_OTF(nbad, NPY_LONG, NPY_ARRAY_ALIGNED) - onsample = <ndarray>PyArray_FROM_OTF(nsample, NPY_LONG, NPY_ARRAY_ALIGNED) - - if ongood.shape == onbad.shape == onsample.shape == (): - lngood = PyInt_AsLong(ngood) - lnbad = PyInt_AsLong(nbad) - lnsample = PyInt_AsLong(nsample) - - if lngood < 0: - raise ValueError("ngood < 0") - if lnbad < 0: - raise ValueError("nbad < 0") - if lnsample < 1: - raise ValueError("nsample < 1") + cdef bint is_scalar = True + cdef np.ndarray ongood, onbad, onsample + cdef int64_t lngood, lnbad, lnsample + + # This cast to long is required to ensure that the values are inbounds + ongood = <np.ndarray>np.PyArray_FROM_OTF(ngood, np.NPY_LONG, np.NPY_ALIGNED) + onbad = <np.ndarray>np.PyArray_FROM_OTF(nbad, np.NPY_LONG, np.NPY_ALIGNED) + onsample = <np.ndarray>np.PyArray_FROM_OTF(nsample, np.NPY_LONG, np.NPY_ALIGNED) + + if np.PyArray_NDIM(ongood) == np.PyArray_NDIM(onbad) == np.PyArray_NDIM(onsample) == 0: + + lngood = <int64_t>ngood + lnbad = <int64_t>nbad + lnsample = <int64_t>nsample + if lngood + lnbad < lnsample: raise ValueError("ngood + nbad < nsample") - return discnmN_array_sc(self.internal_state, rk_hypergeometric, - size, lngood, lnbad, lnsample, self.lock) - - if np.any(np.less(ongood, 0)): - raise ValueError("ngood < 0") - if np.any(np.less(onbad, 0)): - raise ValueError("nbad < 0") - if np.any(np.less(onsample, 1)): - raise ValueError("nsample < 1") - if np.any(np.less(np.add(ongood, onbad),onsample)): + out = disc(&legacy_random_hypergeometric, &self._bitgen, size, self.lock, 0, 3, + lngood, 'ngood', CONS_NON_NEGATIVE, + lnbad, 'nbad', CONS_NON_NEGATIVE, + lnsample, 'nsample', CONS_GTE_1) + # Match historical output type + return int64_to_long(out) + + if np.any(np.less(np.add(ongood, onbad), onsample)): raise ValueError("ngood + nbad < nsample") - return discnmN_array(self.internal_state, rk_hypergeometric, size, - ongood, onbad, onsample, self.lock) + # Convert to int64, if necessary, to use int64 infrastructure + ongood = ongood.astype(np.int64) + onbad = onbad.astype(np.int64) + onsample = onsample.astype(np.int64) + out = discrete_broadcast_iii(&legacy_random_hypergeometric,&self._bitgen, size, self.lock, + ongood, 'ngood', CONS_NON_NEGATIVE, + onbad, 'nbad', CONS_NON_NEGATIVE, + onsample, 'nsample', CONS_GTE_1) + # Match historical output type + return int64_to_long(out) def logseries(self, p, size=None): """ @@ -4289,6 +3897,10 @@ cdef class RandomState: Samples are drawn from a log series distribution with specified shape parameter, 0 < ``p`` < 1. + .. note:: + New code should use the ``logseries`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. + Parameters ---------- p : float or array_like of floats @@ -4308,6 +3920,7 @@ cdef class RandomState: -------- scipy.stats.logser : probability density function, distribution or cumulative density function, etc. + Generator.logseries: which should be used for new code. Notes ----- @@ -4335,7 +3948,7 @@ cdef class RandomState: .. [3] D. J. Hand, F. Daly, D. Lunn, E. Ostrowski, A Handbook of Small Data Sets, CRC Press, 1994. .. [4] Wikipedia, "Logarithmic distribution", - http://en.wikipedia.org/wiki/Logarithmic_distribution + https://en.wikipedia.org/wiki/Logarithmic_distribution Examples -------- @@ -4343,44 +3956,30 @@ cdef class RandomState: >>> a = .6 >>> s = np.random.logseries(a, 10000) + >>> import matplotlib.pyplot as plt >>> count, bins, ignored = plt.hist(s) # plot against distribution >>> def logseries(k, p): - ... return -p**k/(k*log(1-p)) + ... return -p**k/(k*np.log(1-p)) >>> plt.plot(bins, logseries(bins, a)*count.max()/ - logseries(bins, a).max(), 'r') + ... logseries(bins, a).max(), 'r') >>> plt.show() """ - cdef ndarray op - cdef double fp - - op = <ndarray>PyArray_FROM_OTF(p, NPY_DOUBLE, NPY_ARRAY_ALIGNED) - - if op.shape == (): - fp = PyFloat_AsDouble(p) - - if fp <= 0.0: - raise ValueError("p <= 0.0") - if fp >= 1.0: - raise ValueError("p >= 1.0") - return discd_array_sc(self.internal_state, rk_logseries, size, fp, - self.lock) - - if np.any(np.less_equal(op, 0.0)): - raise ValueError("p <= 0.0") - if np.any(np.greater_equal(op, 1.0)): - raise ValueError("p >= 1.0") - return discd_array(self.internal_state, rk_logseries, size, op, - self.lock) + out = disc(&legacy_logseries, &self._bitgen, size, self.lock, 1, 0, + p, 'p', CONS_BOUNDED_0_1, + 0.0, '', CONS_NONE, + 0.0, '', CONS_NONE) + # Match historical output type + return int64_to_long(out) # Multivariate distributions: def multivariate_normal(self, mean, cov, size=None, check_valid='warn', tol=1e-8): """ - multivariate_normal(mean, cov[, size, check_valid, tol]) + multivariate_normal(mean, cov, size=None, check_valid='warn', tol=1e-8) Draw random samples from a multivariate normal distribution. @@ -4391,6 +3990,10 @@ cdef class RandomState: (average or "center") and variance (standard deviation, or "width," squared) of the one-dimensional normal distribution. + .. note:: + New code should use the ``multivariate_normal`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. + Parameters ---------- mean : 1-D array_like, of length N @@ -4407,6 +4010,7 @@ cdef class RandomState: Behavior when the covariance matrix is not positive semidefinite. tol : float, optional Tolerance when checking the singular values in covariance matrix. + cov is cast to double before the check. Returns ------- @@ -4417,6 +4021,10 @@ cdef class RandomState: In other words, each entry ``out[i,j,...,:]`` is an N-dimensional value drawn from the distribution. + See Also + -------- + Generator.multivariate_normal: which should be used for new code. + Notes ----- The mean is a coordinate in N-dimensional space, which represents the @@ -4475,17 +4083,17 @@ cdef class RandomState: standard deviation: >>> list((x[0,0,:] - mean) < 0.6) - [True, True] + [True, True] # random """ - from numpy.dual import svd + from numpy.linalg import svd # Check preconditions on arguments mean = np.array(mean) cov = np.array(cov) if size is None: shape = [] - elif isinstance(size, (int, long, np.integer)): + elif isinstance(size, (int, np.integer)): shape = [size] else: shape = size @@ -4512,40 +4120,43 @@ cdef class RandomState: # covariance. Note that sqrt(s)*v where (u,s,v) is the singular value # decomposition of cov is such an A. # - # Also check that cov is symmetric positive-semidefinite. If so, the u.T and v + # Also check that cov is positive-semidefinite. If so, the u.T and v # matrices should be equal up to roundoff error if cov is # symmetric and the singular value of the corresponding row is # not zero. We continue to use the SVD rather than Cholesky in - # order to preserve current outputs. + # order to preserve current outputs. Note that symmetry has not + # been checked. + # GH10839, ensure double to make tol meaningful + cov = cov.astype(np.double) (u, s, v) = svd(cov) if check_valid != 'ignore': if check_valid != 'warn' and check_valid != 'raise': - raise ValueError("check_valid must equal 'warn', 'raise', or 'ignore'") + raise ValueError( + "check_valid must equal 'warn', 'raise', or 'ignore'") psd = np.allclose(np.dot(v.T * s, v), cov, rtol=tol, atol=tol) if not psd: if check_valid == 'warn': - warnings.warn( - "covariance is not symmetric positive-semidefinite.", + warnings.warn("covariance is not positive-semidefinite.", RuntimeWarning) else: raise ValueError( - "covariance is not symmetric positive-semidefinite.") + "covariance is not positive-semidefinite.") x = np.dot(x, np.sqrt(s)[:, None] * v) x += mean x.shape = tuple(final_shape) return x - def multinomial(self, npy_intp n, object pvals, size=None): + def multinomial(self, np.npy_intp n, object pvals, size=None): """ multinomial(n, pvals, size=None) Draw samples from a multinomial distribution. - The multinomial distribution is a multivariate generalisation of the + The multinomial distribution is a multivariate generalization of the binomial distribution. Take an experiment with one of ``p`` possible outcomes. An example of such an experiment is throwing a dice, where the outcome can be 1 through 6. Each sample drawn from the @@ -4553,13 +4164,17 @@ cdef class RandomState: ``X_i = [X_0, X_1, ..., X_p]``, represent the number of times the outcome was ``i``. + .. note:: + New code should use the ``multinomial`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. + Parameters ---------- n : int Number of experiments. pvals : sequence of floats, length p Probabilities of each of the ``p`` different outcomes. These - should sum to 1 (however, the last element is always assumed to + must sum to 1 (however, the last element is always assumed to account for the remaining probability, as long as ``sum(pvals[:-1]) <= 1)``. size : int or tuple of ints, optional @@ -4576,19 +4191,23 @@ cdef class RandomState: In other words, each entry ``out[i,j,...,:]`` is an N-dimensional value drawn from the distribution. + See Also + -------- + Generator.multinomial: which should be used for new code. + Examples -------- Throw a dice 20 times: >>> np.random.multinomial(20, [1/6.]*6, size=1) - array([[4, 1, 7, 5, 2, 1]]) + array([[4, 1, 7, 5, 2, 1]]) # random It landed 4 times on 1, once on 2, etc. Now, throw the dice 20 times, and 20 times again: >>> np.random.multinomial(20, [1/6.]*6, size=2) - array([[3, 4, 3, 3, 4, 3], + array([[3, 4, 3, 3, 4, 3], # random [2, 4, 3, 4, 0, 7]]) For the first run, we threw 3 times 1, 4 times 2, etc. For the second, @@ -4597,7 +4216,7 @@ cdef class RandomState: A loaded die is more likely to land on number 6: >>> np.random.multinomial(100, [1/7.]*5 + [2/7.]) - array([11, 16, 14, 17, 16, 26]) + array([11, 16, 14, 17, 16, 26]) # random The probability inputs should be normalized. As an implementation detail, the value of the last entry is ignored and assumed to take @@ -4606,49 +4225,64 @@ cdef class RandomState: other should be sampled like so: >>> np.random.multinomial(100, [1.0 / 3, 2.0 / 3]) # RIGHT - array([38, 62]) + array([38, 62]) # random not like: >>> np.random.multinomial(100, [1.0, 2.0]) # WRONG - array([100, 0]) + Traceback (most recent call last): + ValueError: pvals < 0, pvals > 1 or pvals contains NaNs """ - cdef npy_intp d - cdef ndarray parr "arrayObject_parr", mnarr "arrayObject_mnarr" + cdef np.npy_intp d, i, sz, offset, niter + cdef np.ndarray parr, mnarr cdef double *pix cdef long *mnix - cdef npy_intp i, j, dn, sz - cdef double Sum - - d = len(pvals) - parr = <ndarray>PyArray_ContiguousFromObject(pvals, NPY_DOUBLE, 1, 1) - pix = <double*>PyArray_DATA(parr) - - if kahan_sum(pix, d-1) > (1.0 + 1e-12): - raise ValueError("sum(pvals[:-1]) > 1.0") - - shape = _shape_from_size(size, d) - - multin = np.zeros(shape, int) - mnarr = <ndarray>multin - mnix = <long*>PyArray_DATA(mnarr) - sz = PyArray_SIZE(mnarr) - with self.lock, nogil, cython.cdivision(True): - i = 0 - while i < sz: - Sum = 1.0 - dn = n - for j from 0 <= j < d-1: - mnix[i+j] = rk_binomial(self.internal_state, dn, pix[j]/Sum) - dn = dn - mnix[i+j] - if dn <= 0: - break - Sum = Sum - pix[j] - if dn > 0: - mnix[i+d-1] = dn - - i = i + d + cdef long ni + + parr = <np.ndarray>np.PyArray_FROMANY( + pvals, np.NPY_DOUBLE, 0, 1, np.NPY_ARRAY_ALIGNED | np.NPY_ARRAY_C_CONTIGUOUS) + if np.PyArray_NDIM(parr) == 0: + raise TypeError("pvals must be a 1-d sequence") + d = np.PyArray_SIZE(parr) + pix = <double*>np.PyArray_DATA(parr) + check_array_constraint(parr, 'pvals', CONS_BOUNDED_0_1) + # Only check if pvals is non-empty due no checks in kahan_sum + if d and kahan_sum(pix, d-1) > (1.0 + 1e-12): + # When floating, but not float dtype, and close, improve the error + # 1.0001 works for float16 and float32 + if (isinstance(pvals, np.ndarray) + and np.issubdtype(pvals.dtype, np.floating) + and pvals.dtype != float + and pvals.sum() < 1.0001): + msg = ("sum(pvals[:-1].astype(np.float64)) > 1.0. The pvals " + "array is cast to 64-bit floating point prior to " + "checking the sum. Precision changes when casting may " + "cause problems even if the sum of the original pvals " + "is valid.") + else: + msg = "sum(pvals[:-1]) > 1.0" + raise ValueError(msg) + if size is None: + shape = (d,) + else: + try: + shape = (operator.index(size), d) + except: + shape = tuple(size) + (d,) + multin = np.zeros(shape, dtype=int) + mnarr = <np.ndarray>multin + mnix = <long*>np.PyArray_DATA(mnarr) + sz = np.PyArray_SIZE(mnarr) + ni = n + check_constraint(ni, 'n', CONS_NON_NEGATIVE) + offset = 0 + # gh-20483: Avoids divide by 0 + niter = sz // d if d else 0 + with self.lock, nogil: + for i in range(niter): + legacy_random_multinomial(&self._bitgen, ni, &mnix[offset], pix, d, &self._binomial) + offset += d return multin @@ -4660,46 +4294,65 @@ cdef class RandomState: Draw `size` samples of dimension k from a Dirichlet distribution. A Dirichlet-distributed random variable can be seen as a multivariate - generalization of a Beta distribution. Dirichlet pdf is the conjugate - prior of a multinomial in Bayesian inference. + generalization of a Beta distribution. The Dirichlet distribution + is a conjugate prior of a multinomial distribution in Bayesian + inference. + + .. note:: + New code should use the ``dirichlet`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. Parameters ---------- - alpha : array - Parameter of the distribution (k dimension for sample of - dimension k). + alpha : sequence of floats, length k + Parameter of the distribution (length ``k`` for sample of + length ``k``). size : int or tuple of ints, optional - Output shape. If the given shape is, e.g., ``(m, n, k)``, then + Output shape. If the given shape is, e.g., ``(m, n)``, then ``m * n * k`` samples are drawn. Default is None, in which case a - single value is returned. + vector of length ``k`` is returned. Returns ------- samples : ndarray, - The drawn samples, of shape (size, alpha.ndim). + The drawn samples, of shape ``(size, k)``. Raises ------- ValueError - If any value in alpha is less than or equal to zero + If any value in ``alpha`` is less than or equal to zero + + See Also + -------- + Generator.dirichlet: which should be used for new code. Notes ----- - .. math:: X \\approx \\prod_{i=1}^{k}{x^{\\alpha_i-1}_i} + The Dirichlet distribution is a distribution over vectors + :math:`x` that fulfil the conditions :math:`x_i>0` and + :math:`\\sum_{i=1}^k x_i = 1`. + + The probability density function :math:`p` of a + Dirichlet-distributed random vector :math:`X` is + proportional to + + .. math:: p(x) \\propto \\prod_{i=1}^{k}{x^{\\alpha_i-1}_i}, - Uses the following property for computation: for each dimension, - draw a random sample y_i from a standard gamma generator of shape - `alpha_i`, then - :math:`X = \\frac{1}{\\sum_{i=1}^k{y_i}} (y_1, \\ldots, y_n)` is - Dirichlet distributed. + where :math:`\\alpha` is a vector containing the positive + concentration parameters. + + The method uses the following property for computation: let :math:`Y` + be a random vector which has components that follow a standard gamma + distribution, then :math:`X = \\frac{1}{\\sum_{i=1}^k{Y_i}} Y` + is Dirichlet-distributed References ---------- .. [1] David McKay, "Information Theory, Inference and Learning Algorithms," chapter 23, - http://www.inference.phy.cam.ac.uk/mackay/ + http://www.inference.org.uk/mackay/itila/ .. [2] Wikipedia, "Dirichlet distribution", - http://en.wikipedia.org/wiki/Dirichlet_distribution + https://en.wikipedia.org/wiki/Dirichlet_distribution Examples -------- @@ -4711,6 +4364,7 @@ cdef class RandomState: >>> s = np.random.dirichlet((10, 5, 3), 20).transpose() + >>> import matplotlib.pyplot as plt >>> plt.barh(range(20), s[0]) >>> plt.barh(range(20), s[1], left=s[0], color='g') >>> plt.barh(range(20), s[2], left=s[0]+s[1], color='r') @@ -4718,58 +4372,63 @@ cdef class RandomState: """ - #================= + # ================= # Pure python algo - #================= - #alpha = N.atleast_1d(alpha) - #k = alpha.size - - #if n == 1: - # val = N.zeros(k) - # for i in range(k): - # val[i] = sgamma(alpha[i], n) - # val /= N.sum(val) - #else: - # val = N.zeros((k, n)) - # for i in range(k): - # val[i] = sgamma(alpha[i], n) - # val /= N.sum(val, axis = 0) - # val = val.T - - #return val - - cdef npy_intp k - cdef npy_intp totsize - cdef ndarray alpha_arr, val_arr - cdef double *alpha_data - cdef double *val_data - cdef npy_intp i, j - cdef double acc, invacc - - k = len(alpha) - alpha_arr = <ndarray>PyArray_ContiguousFromObject(alpha, NPY_DOUBLE, 1, 1) + # ================= + # alpha = N.atleast_1d(alpha) + # k = alpha.size + + # if n == 1: + # val = N.zeros(k) + # for i in range(k): + # val[i] = sgamma(alpha[i], n) + # val /= N.sum(val) + # else: + # val = N.zeros((k, n)) + # for i in range(k): + # val[i] = sgamma(alpha[i], n) + # val /= N.sum(val, axis = 0) + # val = val.T + # return val + + cdef np.npy_intp k, totsize, i, j + cdef np.ndarray alpha_arr, val_arr + cdef double *alpha_data + cdef double *val_data + cdef double acc, invacc + + k = len(alpha) + alpha_arr = <np.ndarray>np.PyArray_FROMANY( + alpha, np.NPY_DOUBLE, 1, 1, + np.NPY_ARRAY_ALIGNED | np.NPY_ARRAY_C_CONTIGUOUS) if np.any(np.less_equal(alpha_arr, 0)): raise ValueError('alpha <= 0') - alpha_data = <double*>PyArray_DATA(alpha_arr) + alpha_data = <double*>np.PyArray_DATA(alpha_arr) - shape = _shape_from_size(size, k) + if size is None: + shape = (k,) + else: + try: + shape = (operator.index(size), k) + except: + shape = tuple(size) + (k,) - diric = np.zeros(shape, np.float64) - val_arr = <ndarray>diric - val_data= <double*>PyArray_DATA(val_arr) + diric = np.zeros(shape, np.float64) + val_arr = <np.ndarray>diric + val_data = <double*>np.PyArray_DATA(val_arr) i = 0 - totsize = PyArray_SIZE(val_arr) + totsize = np.PyArray_SIZE(val_arr) with self.lock, nogil: while i < totsize: acc = 0.0 - for j from 0 <= j < k: - val_data[i+j] = rk_standard_gamma(self.internal_state, - alpha_data[j]) - acc = acc + val_data[i+j] - invacc = 1/acc - for j from 0 <= j < k: - val_data[i+j] = val_data[i+j] * invacc + for j in range(k): + val_data[i+j] = legacy_standard_gamma(&self._aug_state, + alpha_data[j]) + acc = acc + val_data[i + j] + invacc = 1/acc + for j in range(k): + val_data[i + j] = val_data[i + j] * invacc i = i + k return diric @@ -4785,34 +4444,42 @@ cdef class RandomState: multi-dimensional array. The order of sub-arrays is changed but their contents remains the same. + .. note:: + New code should use the ``shuffle`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. + Parameters ---------- - x : array_like - The array or list to be shuffled. + x : ndarray or MutableSequence + The array, list or mutable sequence to be shuffled. Returns ------- None + See Also + -------- + Generator.shuffle: which should be used for new code. + Examples -------- >>> arr = np.arange(10) >>> np.random.shuffle(arr) >>> arr - [1 7 5 2 9 4 3 6 0 8] + [1 7 5 2 9 4 3 6 0 8] # random Multi-dimensional arrays are only shuffled along the first axis: >>> arr = np.arange(9).reshape((3, 3)) >>> np.random.shuffle(arr) >>> arr - array([[3, 4, 5], + array([[3, 4, 5], # random [6, 7, 8], [0, 1, 2]]) """ cdef: - npy_intp i, j, n = len(x), stride, itemsize + np.npy_intp i, j, n = len(x), stride, itemsize char* x_ptr char* buf_ptr @@ -4820,45 +4487,70 @@ cdef class RandomState: # Fast, statically typed path: shuffle the underlying buffer. # Only for non-empty, 1d objects of class ndarray (subclasses such # as MaskedArrays may not support this approach). - x_ptr = <char*><size_t>x.ctypes.data + x_ptr = np.PyArray_BYTES(x) stride = x.strides[0] itemsize = x.dtype.itemsize # As the array x could contain python objects we use a buffer # of bytes for the swaps to avoid leaving one of the objects # within the buffer and erroneously decrementing it's refcount # when the function exits. - buf = np.empty(itemsize, dtype=np.int8) # GC'd at function exit - buf_ptr = <char*><size_t>buf.ctypes.data + buf = np.empty(itemsize, dtype=np.int8) # GC'd at function exit + buf_ptr = np.PyArray_BYTES(buf) with self.lock: # We trick gcc into providing a specialized implementation for # the most common case, yielding a ~33% performance improvement. # Note that apparently, only one branch can ever be specialized. - if itemsize == sizeof(npy_intp): - self._shuffle_raw(n, sizeof(npy_intp), stride, x_ptr, buf_ptr) + if itemsize == sizeof(np.npy_intp): + self._shuffle_raw(n, sizeof(np.npy_intp), stride, x_ptr, buf_ptr) else: self._shuffle_raw(n, itemsize, stride, x_ptr, buf_ptr) - elif isinstance(x, np.ndarray) and x.ndim > 1 and x.size: - # Multidimensional ndarrays require a bounce buffer. - buf = np.empty_like(x[0]) + elif isinstance(x, np.ndarray): + if x.size == 0: + # shuffling is a no-op + return + + if x.ndim == 1 and x.dtype.type is np.object_: + warnings.warn( + "Shuffling a one dimensional array subclass containing " + "objects gives incorrect results for most array " + "subclasses. " + "Please us the new random number API instead: " + "https://numpy.org/doc/stable/reference/random/index.html\n" + "The new API fixes this issue. This version will not " + "be fixed due to stability guarantees of the API.", + UserWarning, stacklevel=1) # Cython adds no stacklevel + + buf = np.empty_like(x[0, ...]) with self.lock: for i in reversed(range(1, n)): - j = rk_interval(i, self.internal_state) + j = random_interval(&self._bitgen, i) + if i == j: + continue # i == j is not needed and memcpy is undefined. buf[...] = x[j] x[j] = x[i] x[i] = buf else: # Untyped path. + if not isinstance(x, Sequence): + # See gh-18206. We may decide to deprecate here in the future. + warnings.warn( + f"you are shuffling a '{type(x).__name__}' object " + "which is not a subclass of 'Sequence'; " + "`shuffle` is not guaranteed to behave correctly. " + "E.g., non-numpy array/tensor objects with view semantics " + "may contain duplicates after shuffling.", + UserWarning, stacklevel=1) # Cython does not add a level + with self.lock: for i in reversed(range(1, n)): - j = rk_interval(i, self.internal_state) + j = random_interval(&self._bitgen, i) x[i], x[j] = x[j], x[i] - cdef inline _shuffle_raw(self, npy_intp n, npy_intp itemsize, - npy_intp stride, char* data, char* buf): - cdef npy_intp i, j + cdef inline _shuffle_raw(self, np.npy_intp n, np.npy_intp itemsize, + np.npy_intp stride, char* data, char* buf): + cdef np.npy_intp i, j for i in reversed(range(1, n)): - j = rk_interval(i, self.internal_state) - if i == j : continue # i == j is not needed and memcpy is undefined. + j = random_interval(&self._bitgen, i) string.memcpy(buf, data + j * stride, itemsize) string.memcpy(data + j * stride, data + i * stride, itemsize) string.memcpy(data + i * stride, buf, itemsize) @@ -4872,6 +4564,10 @@ cdef class RandomState: If `x` is a multi-dimensional array, it is only shuffled along its first index. + .. note:: + New code should use the ``permutation`` method of a ``default_rng()`` + instance instead; please see the :ref:`random-quick-start`. + Parameters ---------- x : int or array_like @@ -4884,32 +4580,39 @@ cdef class RandomState: out : ndarray Permuted sequence or array range. + See Also + -------- + Generator.permutation: which should be used for new code. + Examples -------- >>> np.random.permutation(10) - array([1, 7, 4, 3, 0, 9, 2, 5, 8, 6]) + array([1, 7, 4, 3, 0, 9, 2, 5, 8, 6]) # random >>> np.random.permutation([1, 4, 9, 12, 15]) - array([15, 1, 9, 4, 12]) + array([15, 1, 9, 4, 12]) # random >>> arr = np.arange(9).reshape((3, 3)) >>> np.random.permutation(arr) - array([[6, 7, 8], + array([[6, 7, 8], # random [0, 1, 2], [3, 4, 5]]) """ - if isinstance(x, (int, long, np.integer)): + + if isinstance(x, (int, np.integer)): arr = np.arange(x) self.shuffle(arr) return arr arr = np.asarray(x) - + if arr.ndim < 1: + raise IndexError("x must be an integer or at least 1-dimensional") + # shuffle has fast-path for 1-d if arr.ndim == 1: - # must return a copy - if arr is x: + # Return a copy if same memory + if np.may_share_memory(arr, x): arr = np.array(arr) self.shuffle(arr) return arr @@ -4918,56 +4621,123 @@ cdef class RandomState: idx = np.arange(arr.shape[0], dtype=np.intp) self.shuffle(idx) return arr[idx] - _rand = RandomState() -seed = _rand.seed -get_state = _rand.get_state -set_state = _rand.set_state -random_sample = _rand.random_sample -choice = _rand.choice -randint = _rand.randint + +beta = _rand.beta +binomial = _rand.binomial bytes = _rand.bytes -uniform = _rand.uniform +chisquare = _rand.chisquare +choice = _rand.choice +dirichlet = _rand.dirichlet +exponential = _rand.exponential +f = _rand.f +gamma = _rand.gamma +get_state = _rand.get_state +geometric = _rand.geometric +gumbel = _rand.gumbel +hypergeometric = _rand.hypergeometric +laplace = _rand.laplace +logistic = _rand.logistic +lognormal = _rand.lognormal +logseries = _rand.logseries +multinomial = _rand.multinomial +multivariate_normal = _rand.multivariate_normal +negative_binomial = _rand.negative_binomial +noncentral_chisquare = _rand.noncentral_chisquare +noncentral_f = _rand.noncentral_f +normal = _rand.normal +pareto = _rand.pareto +permutation = _rand.permutation +poisson = _rand.poisson +power = _rand.power rand = _rand.rand +randint = _rand.randint randn = _rand.randn +random = _rand.random random_integers = _rand.random_integers -standard_normal = _rand.standard_normal -normal = _rand.normal -beta = _rand.beta -exponential = _rand.exponential +random_sample = _rand.random_sample +rayleigh = _rand.rayleigh +seed = _rand.seed +set_state = _rand.set_state +shuffle = _rand.shuffle +standard_cauchy = _rand.standard_cauchy standard_exponential = _rand.standard_exponential standard_gamma = _rand.standard_gamma -gamma = _rand.gamma -f = _rand.f -noncentral_f = _rand.noncentral_f -chisquare = _rand.chisquare -noncentral_chisquare = _rand.noncentral_chisquare -standard_cauchy = _rand.standard_cauchy +standard_normal = _rand.standard_normal standard_t = _rand.standard_t +triangular = _rand.triangular +uniform = _rand.uniform vonmises = _rand.vonmises -pareto = _rand.pareto -weibull = _rand.weibull -power = _rand.power -laplace = _rand.laplace -gumbel = _rand.gumbel -logistic = _rand.logistic -lognormal = _rand.lognormal -rayleigh = _rand.rayleigh wald = _rand.wald -triangular = _rand.triangular - -binomial = _rand.binomial -negative_binomial = _rand.negative_binomial -poisson = _rand.poisson +weibull = _rand.weibull zipf = _rand.zipf -geometric = _rand.geometric -hypergeometric = _rand.hypergeometric -logseries = _rand.logseries -multivariate_normal = _rand.multivariate_normal -multinomial = _rand.multinomial -dirichlet = _rand.dirichlet +# Old aliases that should not be removed +def sample(*args, **kwargs): + """ + This is an alias of `random_sample`. See `random_sample` for the complete + documentation. + """ + return _rand.random_sample(*args, **kwargs) -shuffle = _rand.shuffle -permutation = _rand.permutation +def ranf(*args, **kwargs): + """ + This is an alias of `random_sample`. See `random_sample` for the complete + documentation. + """ + return _rand.random_sample(*args, **kwargs) + +__all__ = [ + 'beta', + 'binomial', + 'bytes', + 'chisquare', + 'choice', + 'dirichlet', + 'exponential', + 'f', + 'gamma', + 'geometric', + 'get_state', + 'gumbel', + 'hypergeometric', + 'laplace', + 'logistic', + 'lognormal', + 'logseries', + 'multinomial', + 'multivariate_normal', + 'negative_binomial', + 'noncentral_chisquare', + 'noncentral_f', + 'normal', + 'pareto', + 'permutation', + 'poisson', + 'power', + 'rand', + 'randint', + 'randn', + 'random', + 'random_integers', + 'random_sample', + 'ranf', + 'rayleigh', + 'sample', + 'seed', + 'set_state', + 'shuffle', + 'standard_cauchy', + 'standard_exponential', + 'standard_gamma', + 'standard_normal', + 'standard_t', + 'triangular', + 'uniform', + 'vonmises', + 'wald', + 'weibull', + 'zipf', + 'RandomState', +] diff --git a/numpy/random/mtrand/Python.pxi b/numpy/random/mtrand/Python.pxi deleted file mode 100644 index 08aadbaa1160..000000000000 --- a/numpy/random/mtrand/Python.pxi +++ /dev/null @@ -1,43 +0,0 @@ -# :Author: Robert Kern -# :Copyright: 2004, Enthought, Inc. -# :License: BSD Style - - -cdef extern from "Python.h": - # Not part of the Python API, but we might as well define it here. - # Note that the exact type doesn't actually matter for Pyrex. - ctypedef int size_t - - # String API - char* PyString_AsString(object string) - char* PyString_AS_STRING(object string) - object PyString_FromString(char* c_string) - object PyString_FromStringAndSize(char* c_string, int length) - - # Float API - double PyFloat_AsDouble(object ob) except? -1.0 - long PyInt_AsLong(object ob) except? -1 - - # Memory API - void* PyMem_Malloc(size_t n) - void* PyMem_Realloc(void* buf, size_t n) - void PyMem_Free(void* buf) - - void Py_DECREF(object obj) - void Py_XDECREF(object obj) - void Py_INCREF(object obj) - void Py_XINCREF(object obj) - - # TypeCheck API - int PyFloat_Check(object obj) - int PyInt_Check(object obj) - - # Error API - int PyErr_Occurred() - void PyErr_Clear() - -cdef extern from "string.h": - void *memcpy(void *s1, void *s2, int n) - -cdef extern from "math.h": - double fabs(double x) diff --git a/numpy/random/mtrand/distributions.c b/numpy/random/mtrand/distributions.c deleted file mode 100644 index b7e1579158bf..000000000000 --- a/numpy/random/mtrand/distributions.c +++ /dev/null @@ -1,926 +0,0 @@ -/* Copyright 2005 Robert Kern (robert.kern@gmail.com) - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* The implementations of rk_hypergeometric_hyp(), rk_hypergeometric_hrua(), - * and rk_triangular() were adapted from Ivan Frohne's rv.py which has this - * license: - * - * Copyright 1998 by Ivan Frohne; Wasilla, Alaska, U.S.A. - * All Rights Reserved - * - * Permission to use, copy, modify and distribute this software and its - * documentation for any purpose, free of charge, is granted subject to the - * following conditions: - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the software. - * - * THE SOFTWARE AND DOCUMENTATION IS PROVIDED WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR - * OR COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM OR DAMAGES IN A CONTRACT - * ACTION, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR ITS DOCUMENTATION. - */ - -#include "distributions.h" -#include <stdio.h> -#include <math.h> -#include <stdlib.h> -#include <limits.h> - -#ifndef min -#define min(x,y) ((x<y)?x:y) -#define max(x,y) ((x>y)?x:y) -#endif - -#ifndef M_PI -#define M_PI 3.14159265358979323846264338328 -#endif - -/* - * log-gamma function to support some of these distributions. The - * algorithm comes from SPECFUN by Shanjie Zhang and Jianming Jin and their - * book "Computation of Special Functions", 1996, John Wiley & Sons, Inc. - */ -static double loggam(double x) -{ - double x0, x2, xp, gl, gl0; - long k, n; - - static double a[10] = {8.333333333333333e-02,-2.777777777777778e-03, - 7.936507936507937e-04,-5.952380952380952e-04, - 8.417508417508418e-04,-1.917526917526918e-03, - 6.410256410256410e-03,-2.955065359477124e-02, - 1.796443723688307e-01,-1.39243221690590e+00}; - x0 = x; - n = 0; - if ((x == 1.0) || (x == 2.0)) - { - return 0.0; - } - else if (x <= 7.0) - { - n = (long)(7 - x); - x0 = x + n; - } - x2 = 1.0/(x0*x0); - xp = 2*M_PI; - gl0 = a[9]; - for (k=8; k>=0; k--) - { - gl0 *= x2; - gl0 += a[k]; - } - gl = gl0/x0 + 0.5*log(xp) + (x0-0.5)*log(x0) - x0; - if (x <= 7.0) - { - for (k=1; k<=n; k++) - { - gl -= log(x0-1.0); - x0 -= 1.0; - } - } - return gl; -} - -double rk_normal(rk_state *state, double loc, double scale) -{ - return loc + scale*rk_gauss(state); -} - -double rk_standard_exponential(rk_state *state) -{ - /* We use -log(1-U) since U is [0, 1) */ - return -log(1.0 - rk_double(state)); -} - -double rk_exponential(rk_state *state, double scale) -{ - return scale * rk_standard_exponential(state); -} - -double rk_uniform(rk_state *state, double loc, double scale) -{ - return loc + scale*rk_double(state); -} - -double rk_standard_gamma(rk_state *state, double shape) -{ - double b, c; - double U, V, X, Y; - - if (shape == 1.0) - { - return rk_standard_exponential(state); - } - else if (shape < 1.0) - { - for (;;) - { - U = rk_double(state); - V = rk_standard_exponential(state); - if (U <= 1.0 - shape) - { - X = pow(U, 1./shape); - if (X <= V) - { - return X; - } - } - else - { - Y = -log((1-U)/shape); - X = pow(1.0 - shape + shape*Y, 1./shape); - if (X <= (V + Y)) - { - return X; - } - } - } - } - else - { - b = shape - 1./3.; - c = 1./sqrt(9*b); - for (;;) - { - do - { - X = rk_gauss(state); - V = 1.0 + c*X; - } while (V <= 0.0); - - V = V*V*V; - U = rk_double(state); - if (U < 1.0 - 0.0331*(X*X)*(X*X)) return (b*V); - if (log(U) < 0.5*X*X + b*(1. - V + log(V))) return (b*V); - } - } -} - -double rk_gamma(rk_state *state, double shape, double scale) -{ - return scale * rk_standard_gamma(state, shape); -} - -double rk_beta(rk_state *state, double a, double b) -{ - double Ga, Gb; - - if ((a <= 1.0) && (b <= 1.0)) - { - double U, V, X, Y; - /* Use Johnk's algorithm */ - - while (1) - { - U = rk_double(state); - V = rk_double(state); - X = pow(U, 1.0/a); - Y = pow(V, 1.0/b); - - if ((X + Y) <= 1.0) - { - if (X +Y > 0) - { - return X / (X + Y); - } - else - { - double logX = log(U) / a; - double logY = log(V) / b; - double logM = logX > logY ? logX : logY; - logX -= logM; - logY -= logM; - - return exp(logX - log(exp(logX) + exp(logY))); - } - } - } - } - else - { - Ga = rk_standard_gamma(state, a); - Gb = rk_standard_gamma(state, b); - return Ga/(Ga + Gb); - } -} - -double rk_chisquare(rk_state *state, double df) -{ - return 2.0*rk_standard_gamma(state, df/2.0); -} - -double rk_noncentral_chisquare(rk_state *state, double df, double nonc) -{ - if (nonc == 0){ - return rk_chisquare(state, df); - } - if(1 < df) - { - const double Chi2 = rk_chisquare(state, df - 1); - const double N = rk_gauss(state) + sqrt(nonc); - return Chi2 + N*N; - } - else - { - const long i = rk_poisson(state, nonc / 2.0); - return rk_chisquare(state, df + 2 * i); - } -} - -double rk_f(rk_state *state, double dfnum, double dfden) -{ - return ((rk_chisquare(state, dfnum) * dfden) / - (rk_chisquare(state, dfden) * dfnum)); -} - -double rk_noncentral_f(rk_state *state, double dfnum, double dfden, double nonc) -{ - double t = rk_noncentral_chisquare(state, dfnum, nonc) * dfden; - return t / (rk_chisquare(state, dfden) * dfnum); -} - -long rk_binomial_btpe(rk_state *state, long n, double p) -{ - double r,q,fm,p1,xm,xl,xr,c,laml,lamr,p2,p3,p4; - double a,u,v,s,F,rho,t,A,nrq,x1,x2,f1,f2,z,z2,w,w2,x; - long m,y,k,i; - - if (!(state->has_binomial) || - (state->nsave != n) || - (state->psave != p)) - { - /* initialize */ - state->nsave = n; - state->psave = p; - state->has_binomial = 1; - state->r = r = min(p, 1.0-p); - state->q = q = 1.0 - r; - state->fm = fm = n*r+r; - state->m = m = (long)floor(state->fm); - state->p1 = p1 = floor(2.195*sqrt(n*r*q)-4.6*q) + 0.5; - state->xm = xm = m + 0.5; - state->xl = xl = xm - p1; - state->xr = xr = xm + p1; - state->c = c = 0.134 + 20.5/(15.3 + m); - a = (fm - xl)/(fm-xl*r); - state->laml = laml = a*(1.0 + a/2.0); - a = (xr - fm)/(xr*q); - state->lamr = lamr = a*(1.0 + a/2.0); - state->p2 = p2 = p1*(1.0 + 2.0*c); - state->p3 = p3 = p2 + c/laml; - state->p4 = p4 = p3 + c/lamr; - } - else - { - r = state->r; - q = state->q; - fm = state->fm; - m = state->m; - p1 = state->p1; - xm = state->xm; - xl = state->xl; - xr = state->xr; - c = state->c; - laml = state->laml; - lamr = state->lamr; - p2 = state->p2; - p3 = state->p3; - p4 = state->p4; - } - - /* sigh ... */ - Step10: - nrq = n*r*q; - u = rk_double(state)*p4; - v = rk_double(state); - if (u > p1) goto Step20; - y = (long)floor(xm - p1*v + u); - goto Step60; - - Step20: - if (u > p2) goto Step30; - x = xl + (u - p1)/c; - v = v*c + 1.0 - fabs(m - x + 0.5)/p1; - if (v > 1.0) goto Step10; - y = (long)floor(x); - goto Step50; - - Step30: - if (u > p3) goto Step40; - y = (long)floor(xl + log(v)/laml); - if (y < 0) goto Step10; - v = v*(u-p2)*laml; - goto Step50; - - Step40: - y = (long)floor(xr - log(v)/lamr); - if (y > n) goto Step10; - v = v*(u-p3)*lamr; - - Step50: - k = labs(y - m); - if ((k > 20) && (k < ((nrq)/2.0 - 1))) goto Step52; - - s = r/q; - a = s*(n+1); - F = 1.0; - if (m < y) - { - for (i=m+1; i<=y; i++) - { - F *= (a/i - s); - } - } - else if (m > y) - { - for (i=y+1; i<=m; i++) - { - F /= (a/i - s); - } - } - if (v > F) goto Step10; - goto Step60; - - Step52: - rho = (k/(nrq))*((k*(k/3.0 + 0.625) + 0.16666666666666666)/nrq + 0.5); - t = -k*k/(2*nrq); - A = log(v); - if (A < (t - rho)) goto Step60; - if (A > (t + rho)) goto Step10; - - x1 = y+1; - f1 = m+1; - z = n+1-m; - w = n-y+1; - x2 = x1*x1; - f2 = f1*f1; - z2 = z*z; - w2 = w*w; - if (A > (xm*log(f1/x1) - + (n-m+0.5)*log(z/w) - + (y-m)*log(w*r/(x1*q)) - + (13680.-(462.-(132.-(99.-140./f2)/f2)/f2)/f2)/f1/166320. - + (13680.-(462.-(132.-(99.-140./z2)/z2)/z2)/z2)/z/166320. - + (13680.-(462.-(132.-(99.-140./x2)/x2)/x2)/x2)/x1/166320. - + (13680.-(462.-(132.-(99.-140./w2)/w2)/w2)/w2)/w/166320.)) - { - goto Step10; - } - - Step60: - if (p > 0.5) - { - y = n - y; - } - - return y; -} - -long rk_binomial_inversion(rk_state *state, long n, double p) -{ - double q, qn, np, px, U; - long X, bound; - - if (!(state->has_binomial) || - (state->nsave != n) || - (state->psave != p)) - { - state->nsave = n; - state->psave = p; - state->has_binomial = 1; - state->q = q = 1.0 - p; - state->r = qn = exp(n * log(q)); - state->c = np = n*p; - state->m = bound = min(n, np + 10.0*sqrt(np*q + 1)); - } else - { - q = state->q; - qn = state->r; - np = state->c; - bound = state->m; - } - X = 0; - px = qn; - U = rk_double(state); - while (U > px) - { - X++; - if (X > bound) - { - X = 0; - px = qn; - U = rk_double(state); - } else - { - U -= px; - px = ((n-X+1) * p * px)/(X*q); - } - } - return X; -} - -long rk_binomial(rk_state *state, long n, double p) -{ - double q; - - if (p <= 0.5) - { - if (p*n <= 30.0) - { - return rk_binomial_inversion(state, n, p); - } - else - { - return rk_binomial_btpe(state, n, p); - } - } - else - { - q = 1.0-p; - if (q*n <= 30.0) - { - return n - rk_binomial_inversion(state, n, q); - } - else - { - return n - rk_binomial_btpe(state, n, q); - } - } - -} - -long rk_negative_binomial(rk_state *state, double n, double p) -{ - double Y; - - Y = rk_gamma(state, n, (1-p)/p); - return rk_poisson(state, Y); -} - -long rk_poisson_mult(rk_state *state, double lam) -{ - long X; - double prod, U, enlam; - - enlam = exp(-lam); - X = 0; - prod = 1.0; - while (1) - { - U = rk_double(state); - prod *= U; - if (prod > enlam) - { - X += 1; - } - else - { - return X; - } - } -} - -/* - * The transformed rejection method for generating Poisson random variables - * W. Hoermann - * Insurance: Mathematics and Economics 12, 39-45 (1993) - */ -#define LS2PI 0.91893853320467267 -#define TWELFTH 0.083333333333333333333333 -long rk_poisson_ptrs(rk_state *state, double lam) -{ - long k; - double U, V, slam, loglam, a, b, invalpha, vr, us; - - slam = sqrt(lam); - loglam = log(lam); - b = 0.931 + 2.53*slam; - a = -0.059 + 0.02483*b; - invalpha = 1.1239 + 1.1328/(b-3.4); - vr = 0.9277 - 3.6224/(b-2); - - while (1) - { - U = rk_double(state) - 0.5; - V = rk_double(state); - us = 0.5 - fabs(U); - k = (long)floor((2*a/us + b)*U + lam + 0.43); - if ((us >= 0.07) && (V <= vr)) - { - return k; - } - if ((k < 0) || - ((us < 0.013) && (V > us))) - { - continue; - } - if ((log(V) + log(invalpha) - log(a/(us*us)+b)) <= - (-lam + k*loglam - loggam(k+1))) - { - return k; - } - - - } - -} - -long rk_poisson(rk_state *state, double lam) -{ - if (lam >= 10) - { - return rk_poisson_ptrs(state, lam); - } - else if (lam == 0) - { - return 0; - } - else - { - return rk_poisson_mult(state, lam); - } -} - -double rk_standard_cauchy(rk_state *state) -{ - return rk_gauss(state) / rk_gauss(state); -} - -double rk_standard_t(rk_state *state, double df) -{ - double N, G, X; - - N = rk_gauss(state); - G = rk_standard_gamma(state, df/2); - X = sqrt(df/2)*N/sqrt(G); - return X; -} - -/* Uses the rejection algorithm compared against the wrapped Cauchy - distribution suggested by Best and Fisher and documented in - Chapter 9 of Luc's Non-Uniform Random Variate Generation. - http://cg.scs.carleton.ca/~luc/rnbookindex.html - (but corrected to match the algorithm in R and Python) -*/ -double rk_vonmises(rk_state *state, double mu, double kappa) -{ - double s; - double U, V, W, Y, Z; - double result, mod; - int neg; - - if (kappa < 1e-8) - { - return M_PI * (2*rk_double(state)-1); - } - else - { - /* with double precision rho is zero until 1.4e-8 */ - if (kappa < 1e-5) { - /* - * second order taylor expansion around kappa = 0 - * precise until relatively large kappas as second order is 0 - */ - s = (1./kappa + kappa); - } - else { - double r = 1 + sqrt(1 + 4*kappa*kappa); - double rho = (r - sqrt(2*r)) / (2*kappa); - s = (1 + rho*rho)/(2*rho); - } - - while (1) - { - U = rk_double(state); - Z = cos(M_PI*U); - W = (1 + s*Z)/(s + Z); - Y = kappa * (s - W); - V = rk_double(state); - if ((Y*(2-Y) - V >= 0) || (log(Y/V)+1 - Y >= 0)) - { - break; - } - } - - U = rk_double(state); - - result = acos(W); - if (U < 0.5) - { - result = -result; - } - result += mu; - neg = (result < 0); - mod = fabs(result); - mod = (fmod(mod+M_PI, 2*M_PI)-M_PI); - if (neg) - { - mod *= -1; - } - - return mod; - } -} - -double rk_pareto(rk_state *state, double a) -{ - return exp(rk_standard_exponential(state)/a) - 1; -} - -double rk_weibull(rk_state *state, double a) -{ - return pow(rk_standard_exponential(state), 1./a); -} - -double rk_power(rk_state *state, double a) -{ - return pow(1 - exp(-rk_standard_exponential(state)), 1./a); -} - -double rk_laplace(rk_state *state, double loc, double scale) -{ - double U; - - U = rk_double(state); - if (U < 0.5) - { - U = loc + scale * log(U + U); - } else - { - U = loc - scale * log(2.0 - U - U); - } - return U; -} - -double rk_gumbel(rk_state *state, double loc, double scale) -{ - double U; - - U = 1.0 - rk_double(state); - return loc - scale * log(-log(U)); -} - -double rk_logistic(rk_state *state, double loc, double scale) -{ - double U; - - U = rk_double(state); - return loc + scale * log(U/(1.0 - U)); -} - -double rk_lognormal(rk_state *state, double mean, double sigma) -{ - return exp(rk_normal(state, mean, sigma)); -} - -double rk_rayleigh(rk_state *state, double mode) -{ - return mode*sqrt(-2.0 * log(1.0 - rk_double(state))); -} - -double rk_wald(rk_state *state, double mean, double scale) -{ - double U, X, Y; - double mu_2l; - - mu_2l = mean / (2*scale); - Y = rk_gauss(state); - Y = mean*Y*Y; - X = mean + mu_2l*(Y - sqrt(4*scale*Y + Y*Y)); - U = rk_double(state); - if (U <= mean/(mean+X)) - { - return X; - } else - { - return mean*mean/X; - } -} - -long rk_zipf(rk_state *state, double a) -{ - double am1, b; - - am1 = a - 1.0; - b = pow(2.0, am1); - while (1) { - double T, U, V, X; - - U = 1.0 - rk_double(state); - V = rk_double(state); - X = floor(pow(U, -1.0/am1)); - /* - * The real result may be above what can be represented in a signed - * long. Since this is a straightforward rejection algorithm, we can - * just reject this value. This function then models a Zipf - * distribution truncated to sys.maxint. - */ - if (X > LONG_MAX || X < 1.0) { - continue; - } - - T = pow(1.0 + 1.0/X, am1); - if (V*X*(T - 1.0)/(b - 1.0) <= T/b) { - return (long)X; - } - } -} - -long rk_geometric_search(rk_state *state, double p) -{ - double U; - long X; - double sum, prod, q; - - X = 1; - sum = prod = p; - q = 1.0 - p; - U = rk_double(state); - while (U > sum) - { - prod *= q; - sum += prod; - X++; - } - return X; -} - -long rk_geometric_inversion(rk_state *state, double p) -{ - return (long)ceil(log(1.0-rk_double(state))/log(1.0-p)); -} - -long rk_geometric(rk_state *state, double p) -{ - if (p >= 0.333333333333333333333333) - { - return rk_geometric_search(state, p); - } else - { - return rk_geometric_inversion(state, p); - } -} - -long rk_hypergeometric_hyp(rk_state *state, long good, long bad, long sample) -{ - long d1, K, Z; - double d2, U, Y; - - d1 = bad + good - sample; - d2 = (double)min(bad, good); - - Y = d2; - K = sample; - while (Y > 0.0) - { - U = rk_double(state); - Y -= (long)floor(U + Y/(d1 + K)); - K--; - if (K == 0) break; - } - Z = (long)(d2 - Y); - if (good > bad) Z = sample - Z; - return Z; -} - -/* D1 = 2*sqrt(2/e) */ -/* D2 = 3 - 2*sqrt(3/e) */ -#define D1 1.7155277699214135 -#define D2 0.8989161620588988 -long rk_hypergeometric_hrua(rk_state *state, long good, long bad, long sample) -{ - long mingoodbad, maxgoodbad, popsize, m, d9; - double d4, d5, d6, d7, d8, d10, d11; - long Z; - double T, W, X, Y; - - mingoodbad = min(good, bad); - popsize = good + bad; - maxgoodbad = max(good, bad); - m = min(sample, popsize - sample); - d4 = ((double)mingoodbad) / popsize; - d5 = 1.0 - d4; - d6 = m*d4 + 0.5; - d7 = sqrt((double)(popsize - m) * sample * d4 * d5 / (popsize - 1) + 0.5); - d8 = D1*d7 + D2; - d9 = (long)floor((double)(m + 1) * (mingoodbad + 1) / (popsize + 2)); - d10 = (loggam(d9+1) + loggam(mingoodbad-d9+1) + loggam(m-d9+1) + - loggam(maxgoodbad-m+d9+1)); - d11 = min(min(m, mingoodbad)+1.0, floor(d6+16*d7)); - /* 16 for 16-decimal-digit precision in D1 and D2 */ - - while (1) - { - X = rk_double(state); - Y = rk_double(state); - W = d6 + d8*(Y- 0.5)/X; - - /* fast rejection: */ - if ((W < 0.0) || (W >= d11)) continue; - - Z = (long)floor(W); - T = d10 - (loggam(Z+1) + loggam(mingoodbad-Z+1) + loggam(m-Z+1) + - loggam(maxgoodbad-m+Z+1)); - - /* fast acceptance: */ - if ((X*(4.0-X)-3.0) <= T) break; - - /* fast rejection: */ - if (X*(X-T) >= 1) continue; - - if (2.0*log(X) <= T) break; /* acceptance */ - } - - /* this is a correction to HRUA* by Ivan Frohne in rv.py */ - if (good > bad) Z = m - Z; - - /* another fix from rv.py to allow sample to exceed popsize/2 */ - if (m < sample) Z = good - Z; - - return Z; -} -#undef D1 -#undef D2 - -long rk_hypergeometric(rk_state *state, long good, long bad, long sample) -{ - if (sample > 10) - { - return rk_hypergeometric_hrua(state, good, bad, sample); - } else - { - return rk_hypergeometric_hyp(state, good, bad, sample); - } -} - -double rk_triangular(rk_state *state, double left, double mode, double right) -{ - double base, leftbase, ratio, leftprod, rightprod; - double U; - - base = right - left; - leftbase = mode - left; - ratio = leftbase / base; - leftprod = leftbase*base; - rightprod = (right - mode)*base; - - U = rk_double(state); - if (U <= ratio) - { - return left + sqrt(U*leftprod); - } else - { - return right - sqrt((1.0 - U) * rightprod); - } -} - -long rk_logseries(rk_state *state, double p) -{ - double q, r, U, V; - long result; - - r = log(1.0 - p); - - while (1) { - V = rk_double(state); - if (V >= p) { - return 1; - } - U = rk_double(state); - q = 1.0 - exp(r*U); - if (V <= q*q) { - result = (long)floor(1 + log(V)/log(q)); - if (result < 1) { - continue; - } - else { - return result; - } - } - if (V >= q) { - return 1; - } - return 2; - } -} diff --git a/numpy/random/mtrand/distributions.h b/numpy/random/mtrand/distributions.h deleted file mode 100644 index 0b42bc79442a..000000000000 --- a/numpy/random/mtrand/distributions.h +++ /dev/null @@ -1,185 +0,0 @@ -/* Copyright 2005 Robert Kern (robert.kern@gmail.com) - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef _RK_DISTR_ -#define _RK_DISTR_ - -#include "randomkit.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* References: - * - * Devroye, Luc. _Non-Uniform Random Variate Generation_. - * Springer-Verlag, New York, 1986. - * http://cgm.cs.mcgill.ca/~luc/rnbookindex.html - * - * Kachitvichyanukul, V. and Schmeiser, B. W. Binomial Random Variate - * Generation. Communications of the ACM, 31, 2 (February, 1988) 216. - * - * Hoermann, W. The Transformed Rejection Method for Generating Poisson Random - * Variables. Insurance: Mathematics and Economics, (to appear) - * http://citeseer.csail.mit.edu/151115.html - * - * Marsaglia, G. and Tsang, W. W. A Simple Method for Generating Gamma - * Variables. ACM Transactions on Mathematical Software, Vol. 26, No. 3, - * September 2000, Pages 363–372. - */ - -/* Normal distribution with mean=loc and standard deviation=scale. */ -extern double rk_normal(rk_state *state, double loc, double scale); - -/* Standard exponential distribution (mean=1) computed by inversion of the - * CDF. */ -extern double rk_standard_exponential(rk_state *state); - -/* Exponential distribution with mean=scale. */ -extern double rk_exponential(rk_state *state, double scale); - -/* Uniform distribution on interval [loc, loc+scale). */ -extern double rk_uniform(rk_state *state, double loc, double scale); - -/* Standard gamma distribution with shape parameter. - * When shape < 1, the algorithm given by (Devroye p. 304) is used. - * When shape == 1, a Exponential variate is generated. - * When shape > 1, the small and fast method of (Marsaglia and Tsang 2000) - * is used. - */ -extern double rk_standard_gamma(rk_state *state, double shape); - -/* Gamma distribution with shape and scale. */ -extern double rk_gamma(rk_state *state, double shape, double scale); - -/* Beta distribution computed by combining two gamma variates (Devroye p. 432). - */ -extern double rk_beta(rk_state *state, double a, double b); - -/* Chi^2 distribution computed by transforming a gamma variate (it being a - * special case Gamma(df/2, 2)). */ -extern double rk_chisquare(rk_state *state, double df); - -/* Noncentral Chi^2 distribution computed by modifying a Chi^2 variate. */ -extern double rk_noncentral_chisquare(rk_state *state, double df, double nonc); - -/* F distribution computed by taking the ratio of two Chi^2 variates. */ -extern double rk_f(rk_state *state, double dfnum, double dfden); - -/* Noncentral F distribution computed by taking the ratio of a noncentral Chi^2 - * and a Chi^2 variate. */ -extern double rk_noncentral_f(rk_state *state, double dfnum, double dfden, double nonc); - -/* Binomial distribution with n Bernoulli trials with success probability p. - * When n*p <= 30, the "Second waiting time method" given by (Devroye p. 525) is - * used. Otherwise, the BTPE algorithm of (Kachitvichyanukul and Schmeiser 1988) - * is used. */ -extern long rk_binomial(rk_state *state, long n, double p); - -/* Binomial distribution using BTPE. */ -extern long rk_binomial_btpe(rk_state *state, long n, double p); - -/* Binomial distribution using inversion and chop-down */ -extern long rk_binomial_inversion(rk_state *state, long n, double p); - -/* Negative binomial distribution computed by generating a Gamma(n, (1-p)/p) - * variate Y and returning a Poisson(Y) variate (Devroye p. 543). */ -extern long rk_negative_binomial(rk_state *state, double n, double p); - -/* Poisson distribution with mean=lam. - * When lam < 10, a basic algorithm using repeated multiplications of uniform - * variates is used (Devroye p. 504). - * When lam >= 10, algorithm PTRS from (Hoermann 1992) is used. - */ -extern long rk_poisson(rk_state *state, double lam); - -/* Poisson distribution computed by repeated multiplication of uniform variates. - */ -extern long rk_poisson_mult(rk_state *state, double lam); - -/* Poisson distribution computer by the PTRS algorithm. */ -extern long rk_poisson_ptrs(rk_state *state, double lam); - -/* Standard Cauchy distribution computed by dividing standard gaussians - * (Devroye p. 451). */ -extern double rk_standard_cauchy(rk_state *state); - -/* Standard t-distribution with df degrees of freedom (Devroye p. 445 as - * corrected in the Errata). */ -extern double rk_standard_t(rk_state *state, double df); - -/* von Mises circular distribution with center mu and shape kappa on [-pi,pi] - * (Devroye p. 476 as corrected in the Errata). */ -extern double rk_vonmises(rk_state *state, double mu, double kappa); - -/* Pareto distribution via inversion (Devroye p. 262) */ -extern double rk_pareto(rk_state *state, double a); - -/* Weibull distribution via inversion (Devroye p. 262) */ -extern double rk_weibull(rk_state *state, double a); - -/* Power distribution via inversion (Devroye p. 262) */ -extern double rk_power(rk_state *state, double a); - -/* Laplace distribution */ -extern double rk_laplace(rk_state *state, double loc, double scale); - -/* Gumbel distribution */ -extern double rk_gumbel(rk_state *state, double loc, double scale); - -/* Logistic distribution */ -extern double rk_logistic(rk_state *state, double loc, double scale); - -/* Log-normal distribution */ -extern double rk_lognormal(rk_state *state, double mean, double sigma); - -/* Rayleigh distribution */ -extern double rk_rayleigh(rk_state *state, double mode); - -/* Wald distribution */ -extern double rk_wald(rk_state *state, double mean, double scale); - -/* Zipf distribution */ -extern long rk_zipf(rk_state *state, double a); - -/* Geometric distribution */ -extern long rk_geometric(rk_state *state, double p); -extern long rk_geometric_search(rk_state *state, double p); -extern long rk_geometric_inversion(rk_state *state, double p); - -/* Hypergeometric distribution */ -extern long rk_hypergeometric(rk_state *state, long good, long bad, long sample); -extern long rk_hypergeometric_hyp(rk_state *state, long good, long bad, long sample); -extern long rk_hypergeometric_hrua(rk_state *state, long good, long bad, long sample); - -/* Triangular distribution */ -extern double rk_triangular(rk_state *state, double left, double mode, double right); - -/* Logarithmic series distribution */ -extern long rk_logseries(rk_state *state, double p); - -#ifdef __cplusplus -} -#endif - - -#endif /* _RK_DISTR_ */ diff --git a/numpy/random/mtrand/generate_mtrand_c.py b/numpy/random/mtrand/generate_mtrand_c.py deleted file mode 100644 index ec935e6ddf09..000000000000 --- a/numpy/random/mtrand/generate_mtrand_c.py +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env python -from __future__ import division, absolute_import, print_function - -import sys -import re -import os - -unused_internal_funcs = ['__Pyx_PrintItem', - '__Pyx_PrintNewline', - '__Pyx_ReRaise', - #'__Pyx_GetExcValue', - '__Pyx_ArgTypeTest', - '__Pyx_SetVtable', - '__Pyx_GetVtable', - '__Pyx_CreateClass'] - -if __name__ == '__main__': - # Use cython here so that long docstrings are broken up. - # This is needed for some VC++ compilers. - os.system('cython mtrand.pyx') - mtrand_c = open('mtrand.c', 'r') - processed = open('mtrand_pp.c', 'w') - unused_funcs_str = '(' + '|'.join(unused_internal_funcs) + ')' - uifpat = re.compile(r'static \w+ \*?'+unused_funcs_str+r'.*/\*proto\*/') - linepat = re.compile(r'/\* ".*/mtrand.pyx":') - for linenum, line in enumerate(mtrand_c): - m = re.match(r'^(\s+arrayObject\w*\s*=\s*[(])[(]PyObject\s*[*][)]', - line) - if m: - line = '%s(PyArrayObject *)%s' % (m.group(1), line[m.end():]) - m = uifpat.match(line) - if m: - line = '' - m = re.search(unused_funcs_str, line) - if m: - print("%s was declared unused, but is used at line %d" % (m.group(), - linenum+1), file=sys.stderr) - line = linepat.sub(r'/* "mtrand.pyx":', line) - processed.write(line) - mtrand_c.close() - processed.close() - os.rename('mtrand_pp.c', 'mtrand.c') diff --git a/numpy/random/mtrand/initarray.c b/numpy/random/mtrand/initarray.c deleted file mode 100644 index 21f1dc05a931..000000000000 --- a/numpy/random/mtrand/initarray.c +++ /dev/null @@ -1,152 +0,0 @@ -/* - * These function have been adapted from Python 2.4.1's _randommodule.c - * - * The following changes have been made to it in 2005 by Robert Kern: - * - * * init_by_array has been declared extern, has a void return, and uses the - * rk_state structure to hold its data. - * - * The original file has the following verbatim comments: - * - * ------------------------------------------------------------------ - * The code in this module was based on a download from: - * http://www.math.keio.ac.jp/~matumoto/MT2002/emt19937ar.html - * - * It was modified in 2002 by Raymond Hettinger as follows: - * - * * the principal computational lines untouched except for tabbing. - * - * * renamed genrand_res53() to random_random() and wrapped - * in python calling/return code. - * - * * genrand_int32() and the helper functions, init_genrand() - * and init_by_array(), were declared static, wrapped in - * Python calling/return code. also, their global data - * references were replaced with structure references. - * - * * unused functions from the original were deleted. - * new, original C python code was added to implement the - * Random() interface. - * - * The following are the verbatim comments from the original code: - * - * A C-program for MT19937, with initialization improved 2002/1/26. - * Coded by Takuji Nishimura and Makoto Matsumoto. - * - * Before using, initialize the state by using init_genrand(seed) - * or init_by_array(init_key, key_length). - * - * Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * 3. The names of its contributors may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * - * Any feedback is very welcome. - * http://www.math.keio.ac.jp/matumoto/emt.html - * email: matumoto@math.keio.ac.jp - */ -#define NPY_NO_DEPRECATED_API NPY_API_VERSION - -#include "initarray.h" - -static void -init_genrand(rk_state *self, unsigned long s); - -/* initializes mt[RK_STATE_LEN] with a seed */ -static void -init_genrand(rk_state *self, unsigned long s) -{ - int mti; - unsigned long *mt = self->key; - - mt[0] = s & 0xffffffffUL; - for (mti = 1; mti < RK_STATE_LEN; mti++) { - /* - * See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. - * In the previous versions, MSBs of the seed affect - * only MSBs of the array mt[]. - * 2002/01/09 modified by Makoto Matsumoto - */ - mt[mti] = (1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti); - /* for > 32 bit machines */ - mt[mti] &= 0xffffffffUL; - } - self->pos = mti; - return; -} - - -/* - * initialize by an array with array-length - * init_key is the array for initializing keys - * key_length is its length - */ -extern void -init_by_array(rk_state *self, unsigned long init_key[], npy_intp key_length) -{ - /* was signed in the original code. RDH 12/16/2002 */ - npy_intp i = 1; - npy_intp j = 0; - unsigned long *mt = self->key; - npy_intp k; - - init_genrand(self, 19650218UL); - k = (RK_STATE_LEN > key_length ? RK_STATE_LEN : key_length); - for (; k; k--) { - /* non linear */ - mt[i] = (mt[i] ^ ((mt[i - 1] ^ (mt[i - 1] >> 30)) * 1664525UL)) - + init_key[j] + j; - /* for > 32 bit machines */ - mt[i] &= 0xffffffffUL; - i++; - j++; - if (i >= RK_STATE_LEN) { - mt[0] = mt[RK_STATE_LEN - 1]; - i = 1; - } - if (j >= key_length) { - j = 0; - } - } - for (k = RK_STATE_LEN - 1; k; k--) { - mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941UL)) - - i; /* non linear */ - mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */ - i++; - if (i >= RK_STATE_LEN) { - mt[0] = mt[RK_STATE_LEN - 1]; - i = 1; - } - } - - mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */ - self->gauss = 0; - self->has_gauss = 0; - self->has_binomial = 0; -} diff --git a/numpy/random/mtrand/initarray.h b/numpy/random/mtrand/initarray.h deleted file mode 100644 index f5e5e5332d22..000000000000 --- a/numpy/random/mtrand/initarray.h +++ /dev/null @@ -1,8 +0,0 @@ -#include "Python.h" -#define NO_IMPORT_ARRAY -#include "numpy/arrayobject.h" -#include "randomkit.h" - -extern void -init_by_array(rk_state *self, unsigned long init_key[], - npy_intp key_length); diff --git a/numpy/random/mtrand/mtrand_py_helper.h b/numpy/random/mtrand/mtrand_py_helper.h deleted file mode 100644 index 266847cbe9fc..000000000000 --- a/numpy/random/mtrand/mtrand_py_helper.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef _MTRAND_PY_HELPER_H_ -#define _MTRAND_PY_HELPER_H_ - -#include <Python.h> - -static PyObject *empty_py_bytes(npy_intp length, void **bytes) -{ - PyObject *b; -#if PY_MAJOR_VERSION >= 3 - b = PyBytes_FromStringAndSize(NULL, length); - if (b) { - *bytes = PyBytes_AS_STRING(b); - } -#else - b = PyString_FromStringAndSize(NULL, length); - if (b) { - *bytes = PyString_AS_STRING(b); - } -#endif - return b; -} - -#endif /* _MTRAND_PY_HELPER_H_ */ diff --git a/numpy/random/mtrand/numpy.pxd b/numpy/random/mtrand/numpy.pxd deleted file mode 100644 index 9092fa113fe9..000000000000 --- a/numpy/random/mtrand/numpy.pxd +++ /dev/null @@ -1,163 +0,0 @@ -# :Author: Travis Oliphant -from cpython.exc cimport PyErr_Print - -cdef extern from "numpy/npy_no_deprecated_api.h": pass - -cdef extern from "numpy/arrayobject.h": - - cdef enum NPY_TYPES: - NPY_BOOL - NPY_BYTE - NPY_UBYTE - NPY_SHORT - NPY_USHORT - NPY_INT - NPY_UINT - NPY_LONG - NPY_ULONG - NPY_LONGLONG - NPY_ULONGLONG - NPY_FLOAT - NPY_DOUBLE - NPY_LONGDOUBLE - NPY_CFLOAT - NPY_CDOUBLE - NPY_CLONGDOUBLE - NPY_OBJECT - NPY_STRING - NPY_UNICODE - NPY_VOID - NPY_NTYPES - NPY_NOTYPE - - cdef enum requirements: - NPY_ARRAY_C_CONTIGUOUS - NPY_ARRAY_F_CONTIGUOUS - NPY_ARRAY_OWNDATA - NPY_ARRAY_FORCECAST - NPY_ARRAY_ENSURECOPY - NPY_ARRAY_ENSUREARRAY - NPY_ARRAY_ELEMENTSTRIDES - NPY_ARRAY_ALIGNED - NPY_ARRAY_NOTSWAPPED - NPY_ARRAY_WRITEABLE - NPY_ARRAY_WRITEBACKIFCOPY - NPY_ARR_HAS_DESCR - - NPY_ARRAY_BEHAVED - NPY_ARRAY_BEHAVED_NS - NPY_ARRAY_CARRAY - NPY_ARRAY_CARRAY_RO - NPY_ARRAY_FARRAY - NPY_ARRAY_FARRAY_RO - NPY_ARRAY_DEFAULT - - NPY_ARRAY_IN_ARRAY - NPY_ARRAY_OUT_ARRAY - NPY_ARRAY_INOUT_ARRAY - NPY_ARRAY_IN_FARRAY - NPY_ARRAY_OUT_FARRAY - NPY_ARRAY_INOUT_FARRAY - - NPY_ARRAY_UPDATE_ALL - - cdef enum defines: - NPY_MAXDIMS - - ctypedef struct npy_cdouble: - double real - double imag - - ctypedef struct npy_cfloat: - double real - double imag - - ctypedef int npy_int - ctypedef int npy_intp - ctypedef int npy_int64 - ctypedef int npy_uint64 - ctypedef int npy_int32 - ctypedef int npy_uint32 - ctypedef int npy_int16 - ctypedef int npy_uint16 - ctypedef int npy_int8 - ctypedef int npy_uint8 - ctypedef int npy_bool - - ctypedef extern class numpy.dtype [object PyArray_Descr]: pass - - ctypedef extern class numpy.ndarray [object PyArrayObject]: pass - - ctypedef extern class numpy.flatiter [object PyArrayIterObject]: - cdef int nd_m1 - cdef npy_intp index, size - cdef ndarray ao - cdef char *dataptr - - ctypedef extern class numpy.broadcast [object PyArrayMultiIterObject]: - cdef int numiter - cdef npy_intp size, index - cdef int nd - cdef npy_intp *dimensions - cdef void **iters - - object PyArray_ZEROS(int ndims, npy_intp* dims, NPY_TYPES type_num, int fortran) - object PyArray_EMPTY(int ndims, npy_intp* dims, NPY_TYPES type_num, int fortran) - dtype PyArray_DescrFromTypeNum(NPY_TYPES type_num) - object PyArray_SimpleNew(int ndims, npy_intp* dims, NPY_TYPES type_num) - int PyArray_Check(object obj) - object PyArray_ContiguousFromAny(object obj, NPY_TYPES type, - int mindim, int maxdim) - object PyArray_ContiguousFromObject(object obj, NPY_TYPES type, - int mindim, int maxdim) - npy_intp PyArray_SIZE(ndarray arr) - npy_intp PyArray_NBYTES(ndarray arr) - object PyArray_FromAny(object obj, dtype newtype, int mindim, int maxdim, - int requirements, object context) - object PyArray_FROMANY(object obj, NPY_TYPES type_num, int min, - int max, int requirements) - object PyArray_NewFromDescr(object subtype, dtype newtype, int nd, - npy_intp* dims, npy_intp* strides, void* data, - int flags, object parent) - - object PyArray_FROM_OTF(object obj, NPY_TYPES type, int flags) - object PyArray_EnsureArray(object) - - object PyArray_MultiIterNew(int n, ...) - - char *PyArray_MultiIter_DATA(broadcast multi, int i) nogil - void PyArray_MultiIter_NEXTi(broadcast multi, int i) nogil - void PyArray_MultiIter_NEXT(broadcast multi) nogil - - object PyArray_IterNew(object arr) - void PyArray_ITER_NEXT(flatiter it) nogil - void* PyArray_ITER_DATA(flatiter it) nogil - - dtype PyArray_DescrFromType(int) - - int _import_array() except -1 - -# include functions that were once macros in the new api - - int PyArray_NDIM(ndarray arr) - char * PyArray_DATA(ndarray arr) - npy_intp * PyArray_DIMS(ndarray arr) - npy_intp * PyArray_STRIDES(ndarray arr) - npy_intp PyArray_DIM(ndarray arr, int idim) - npy_intp PyArray_STRIDE(ndarray arr, int istride) - object PyArray_BASE(ndarray arr) - dtype PyArray_DESCR(ndarray arr) - int PyArray_FLAGS(ndarray arr) - npy_intp PyArray_ITEMSIZE(ndarray arr) - int PyArray_TYPE(ndarray arr) - int PyArray_CHKFLAGS(ndarray arr, int flags) - object PyArray_GETITEM(ndarray arr, char *itemptr) - - -# copied from cython version with addition of PyErr_Print. -cdef inline int import_array() except -1: - try: - _import_array() - except Exception: - PyErr_Print() - raise ImportError("numpy.core.multiarray failed to import") diff --git a/numpy/random/mtrand/randint_helpers.pxi.in b/numpy/random/mtrand/randint_helpers.pxi.in deleted file mode 100644 index 4bd7cd35614e..000000000000 --- a/numpy/random/mtrand/randint_helpers.pxi.in +++ /dev/null @@ -1,77 +0,0 @@ -""" -Template for each `dtype` helper function in `np.random.randint`. -""" - -{{py: - -dtypes = ( - ('bool', 'bool', 'bool_'), - ('int8', 'uint8', 'int8'), - ('int16', 'uint16', 'int16'), - ('int32', 'uint32', 'int32'), - ('int64', 'uint64', 'int64'), - ('uint8', 'uint8', 'uint8'), - ('uint16', 'uint16', 'uint16'), - ('uint32', 'uint32', 'uint32'), - ('uint64', 'uint64', 'uint64'), -) - -def get_dispatch(dtypes): - for npy_dt, npy_udt, np_dt in dtypes: - yield npy_dt, npy_udt, np_dt -}} - -{{for npy_dt, npy_udt, np_dt in get_dispatch(dtypes)}} - -def _rand_{{npy_dt}}(low, high, size, rngstate): - """ - _rand_{{npy_dt}}(low, high, size, rngstate) - - Return random np.{{np_dt}} integers between ``low`` and ``high``, inclusive. - - Return random integers from the "discrete uniform" distribution in the - closed interval [``low``, ``high``). On entry the arguments are presumed - to have been validated for size and order for the np.{{np_dt}} type. - - Parameters - ---------- - low : int - Lowest (signed) integer to be drawn from the distribution. - high : int - Highest (signed) integer to be drawn from the distribution. - size : int or tuple of ints - Output shape. If the given shape is, e.g., ``(m, n, k)``, then - ``m * n * k`` samples are drawn. Default is None, in which case a - single value is returned. - rngstate : encapsulated pointer to rk_state - The specific type depends on the python version. In Python 2 it is - a PyCObject, in Python 3 a PyCapsule object. - - Returns - ------- - out : python integer or ndarray of np.{{np_dt}} - `size`-shaped array of random integers from the appropriate - distribution, or a single such random int if `size` not provided. - - """ - cdef npy_{{npy_udt}} off, rng, buf - cdef npy_{{npy_udt}} *out - cdef ndarray array "arrayObject" - cdef npy_intp cnt - cdef rk_state *state = <rk_state *>PyCapsule_GetPointer(rngstate, NULL) - - rng = <npy_{{npy_udt}}>(high - low) - off = <npy_{{npy_udt}}>(<npy_{{npy_dt}}>low) - - if size is None: - rk_random_{{npy_udt}}(off, rng, 1, &buf, state) - return np.{{np_dt}}(<npy_{{npy_dt}}>buf) - else: - array = <ndarray>np.empty(size, np.{{np_dt}}) - cnt = PyArray_SIZE(array) - array_data = <npy_{{npy_udt}} *>PyArray_DATA(array) - with nogil: - rk_random_{{npy_udt}}(off, rng, cnt, array_data, state) - return array - -{{endfor}} diff --git a/numpy/random/mtrand/randomkit.c b/numpy/random/mtrand/randomkit.c deleted file mode 100644 index 380917180490..000000000000 --- a/numpy/random/mtrand/randomkit.c +++ /dev/null @@ -1,626 +0,0 @@ -/* Random kit 1.3 */ - -/* - * Copyright (c) 2003-2005, Jean-Sebastien Roy (js@jeannot.org) - * - * The rk_random and rk_seed functions algorithms and the original design of - * the Mersenne Twister RNG: - * - * Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * 3. The names of its contributors may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Original algorithm for the implementation of rk_interval function from - * Richard J. Wagner's implementation of the Mersenne Twister RNG, optimised by - * Magnus Jonsson. - * - * Constants used in the rk_double implementation by Isaku Wada. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* static char const rcsid[] = - "@(#) $Jeannot: randomkit.c,v 1.28 2005/07/21 22:14:09 js Exp $"; */ - -#ifdef _WIN32 -/* - * Windows - * XXX: we have to use this ugly defined(__GNUC__) because it is not easy to - * detect the compiler used in distutils itself - */ -#if (defined(__GNUC__) && defined(NPY_NEEDS_MINGW_TIME_WORKAROUND)) - -/* - * FIXME: ideally, we should set this to the real version of MSVCRT. We need - * something higher than 0x601 to enable _ftime64 and co - */ -#define __MSVCRT_VERSION__ 0x0700 -#include <time.h> -#include <sys/timeb.h> - -/* - * mingw msvcr lib import wrongly export _ftime, which does not exist in the - * actual msvc runtime for version >= 8; we make it an alias to _ftime64, which - * is available in those versions of the runtime - */ -#define _FTIME(x) _ftime64((x)) -#else -#include <time.h> -#include <sys/timeb.h> -#define _FTIME(x) _ftime((x)) -#endif - -#ifndef RK_NO_WINCRYPT -/* Windows crypto */ -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0400 -#endif -#include <windows.h> -#include <wincrypt.h> -#endif - -/* - * Do not move this include. randomkit.h must be included - * after windows timeb.h is included. - */ -#include "randomkit.h" - -#else -/* Unix */ -#include "randomkit.h" -#include <time.h> -#include <sys/time.h> -#include <unistd.h> -#endif - -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <errno.h> -#include <limits.h> -#include <math.h> -#include <assert.h> - -#ifndef RK_DEV_URANDOM -#define RK_DEV_URANDOM "/dev/urandom" -#endif - -#ifndef RK_DEV_RANDOM -#define RK_DEV_RANDOM "/dev/random" -#endif - -char *rk_strerror[RK_ERR_MAX] = -{ - "no error", - "random device unvavailable" -}; - -/* static functions */ -static unsigned long rk_hash(unsigned long key); - -void -rk_seed(unsigned long seed, rk_state *state) -{ - int pos; - seed &= 0xffffffffUL; - - /* Knuth's PRNG as used in the Mersenne Twister reference implementation */ - for (pos = 0; pos < RK_STATE_LEN; pos++) { - state->key[pos] = seed; - seed = (1812433253UL * (seed ^ (seed >> 30)) + pos + 1) & 0xffffffffUL; - } - state->pos = RK_STATE_LEN; - state->gauss = 0; - state->has_gauss = 0; - state->has_binomial = 0; -} - -/* Thomas Wang 32 bits integer hash function */ -unsigned long -rk_hash(unsigned long key) -{ - key += ~(key << 15); - key ^= (key >> 10); - key += (key << 3); - key ^= (key >> 6); - key += ~(key << 11); - key ^= (key >> 16); - return key; -} - -rk_error -rk_randomseed(rk_state *state) -{ -#ifndef _WIN32 - struct timeval tv; -#else - struct _timeb tv; -#endif - int i; - - if (rk_devfill(state->key, sizeof(state->key), 0) == RK_NOERR) { - /* ensures non-zero key */ - state->key[0] |= 0x80000000UL; - state->pos = RK_STATE_LEN; - state->gauss = 0; - state->has_gauss = 0; - state->has_binomial = 0; - - for (i = 0; i < 624; i++) { - state->key[i] &= 0xffffffffUL; - } - return RK_NOERR; - } - -#ifndef _WIN32 - gettimeofday(&tv, NULL); - rk_seed(rk_hash(getpid()) ^ rk_hash(tv.tv_sec) ^ rk_hash(tv.tv_usec) - ^ rk_hash(clock()), state); -#else - _FTIME(&tv); - rk_seed(rk_hash(tv.time) ^ rk_hash(tv.millitm) ^ rk_hash(clock()), state); -#endif - - return RK_ENODEV; -} - -/* Magic Mersenne Twister constants */ -#define N 624 -#define M 397 -#define MATRIX_A 0x9908b0dfUL -#define UPPER_MASK 0x80000000UL -#define LOWER_MASK 0x7fffffffUL - -/* - * Slightly optimised reference implementation of the Mersenne Twister - * Note that regardless of the precision of long, only 32 bit random - * integers are produced - */ -unsigned long -rk_random(rk_state *state) -{ - unsigned long y; - - if (state->pos == RK_STATE_LEN) { - int i; - - for (i = 0; i < N - M; i++) { - y = (state->key[i] & UPPER_MASK) | (state->key[i+1] & LOWER_MASK); - state->key[i] = state->key[i+M] ^ (y>>1) ^ (-(y & 1) & MATRIX_A); - } - for (; i < N - 1; i++) { - y = (state->key[i] & UPPER_MASK) | (state->key[i+1] & LOWER_MASK); - state->key[i] = state->key[i+(M-N)] ^ (y>>1) ^ (-(y & 1) & MATRIX_A); - } - y = (state->key[N - 1] & UPPER_MASK) | (state->key[0] & LOWER_MASK); - state->key[N - 1] = state->key[M - 1] ^ (y >> 1) ^ (-(y & 1) & MATRIX_A); - - state->pos = 0; - } - y = state->key[state->pos++]; - - /* Tempering */ - y ^= (y >> 11); - y ^= (y << 7) & 0x9d2c5680UL; - y ^= (y << 15) & 0xefc60000UL; - y ^= (y >> 18); - - return y; -} - - -/* - * Returns an unsigned 64 bit random integer. - */ -NPY_INLINE static npy_uint64 -rk_uint64(rk_state *state) -{ - npy_uint64 upper = (npy_uint64)rk_random(state) << 32; - npy_uint64 lower = (npy_uint64)rk_random(state); - return upper | lower; -} - - -/* - * Returns an unsigned 32 bit random integer. - */ -NPY_INLINE static npy_uint32 -rk_uint32(rk_state *state) -{ - return (npy_uint32)rk_random(state); -} - - -/* - * Fills an array with cnt random npy_uint64 between off and off + rng - * inclusive. The numbers wrap if rng is sufficiently large. - */ -void -rk_random_uint64(npy_uint64 off, npy_uint64 rng, npy_intp cnt, - npy_uint64 *out, rk_state *state) -{ - npy_uint64 val, mask = rng; - npy_intp i; - - if (rng == 0) { - for (i = 0; i < cnt; i++) { - out[i] = off; - } - return; - } - - /* Smallest bit mask >= max */ - mask |= mask >> 1; - mask |= mask >> 2; - mask |= mask >> 4; - mask |= mask >> 8; - mask |= mask >> 16; - mask |= mask >> 32; - - for (i = 0; i < cnt; i++) { - if (rng <= 0xffffffffUL) { - while ((val = (rk_uint32(state) & mask)) > rng); - } - else { - while ((val = (rk_uint64(state) & mask)) > rng); - } - out[i] = off + val; - } -} - - -/* - * Fills an array with cnt random npy_uint32 between off and off + rng - * inclusive. The numbers wrap if rng is sufficiently large. - */ -void -rk_random_uint32(npy_uint32 off, npy_uint32 rng, npy_intp cnt, - npy_uint32 *out, rk_state *state) -{ - npy_uint32 val, mask = rng; - npy_intp i; - - if (rng == 0) { - for (i = 0; i < cnt; i++) { - out[i] = off; - } - return; - } - - /* Smallest bit mask >= max */ - mask |= mask >> 1; - mask |= mask >> 2; - mask |= mask >> 4; - mask |= mask >> 8; - mask |= mask >> 16; - - for (i = 0; i < cnt; i++) { - while ((val = (rk_uint32(state) & mask)) > rng); - out[i] = off + val; - } -} - - -/* - * Fills an array with cnt random npy_uint16 between off and off + rng - * inclusive. The numbers wrap if rng is sufficiently large. - */ -void -rk_random_uint16(npy_uint16 off, npy_uint16 rng, npy_intp cnt, - npy_uint16 *out, rk_state *state) -{ - npy_uint16 val, mask = rng; - npy_intp i; - npy_uint32 buf; - int bcnt = 0; - - if (rng == 0) { - for (i = 0; i < cnt; i++) { - out[i] = off; - } - return; - } - - /* Smallest bit mask >= max */ - mask |= mask >> 1; - mask |= mask >> 2; - mask |= mask >> 4; - mask |= mask >> 8; - - for (i = 0; i < cnt; i++) { - do { - if (!bcnt) { - buf = rk_uint32(state); - bcnt = 1; - } - else { - buf >>= 16; - bcnt--; - } - val = (npy_uint16)buf & mask; - } while (val > rng); - out[i] = off + val; - } -} - - -/* - * Fills an array with cnt random npy_uint8 between off and off + rng - * inclusive. The numbers wrap if rng is sufficiently large. - */ -void -rk_random_uint8(npy_uint8 off, npy_uint8 rng, npy_intp cnt, - npy_uint8 *out, rk_state *state) -{ - npy_uint8 val, mask = rng; - npy_intp i; - npy_uint32 buf; - int bcnt = 0; - - if (rng == 0) { - for (i = 0; i < cnt; i++) { - out[i] = off; - } - return; - } - - /* Smallest bit mask >= max */ - mask |= mask >> 1; - mask |= mask >> 2; - mask |= mask >> 4; - - for (i = 0; i < cnt; i++) { - do { - if (!bcnt) { - buf = rk_uint32(state); - bcnt = 3; - } - else { - buf >>= 8; - bcnt--; - } - val = (npy_uint8)buf & mask; - } while (val > rng); - out[i] = off + val; - } -} - - -/* - * Fills an array with cnt random npy_bool between off and off + rng - * inclusive. - */ -void -rk_random_bool(npy_bool off, npy_bool rng, npy_intp cnt, - npy_bool *out, rk_state *state) -{ - npy_intp i; - npy_uint32 buf; - int bcnt = 0; - - if (rng == 0) { - for (i = 0; i < cnt; i++) { - out[i] = off; - } - return; - } - - /* If we reach here rng and mask are one and off is zero */ - assert(rng == 1 && off == 0); - for (i = 0; i < cnt; i++) { - if (!bcnt) { - buf = rk_uint32(state); - bcnt = 31; - } - else { - buf >>= 1; - bcnt--; - } - out[i] = (buf & 0x00000001) != 0; - } -} - - -long -rk_long(rk_state *state) -{ - return rk_ulong(state) >> 1; -} - -unsigned long -rk_ulong(rk_state *state) -{ -#if ULONG_MAX <= 0xffffffffUL - return rk_random(state); -#else - return (rk_random(state) << 32) | (rk_random(state)); -#endif -} - -unsigned long -rk_interval(unsigned long max, rk_state *state) -{ - unsigned long mask = max, value; - - if (max == 0) { - return 0; - } - /* Smallest bit mask >= max */ - mask |= mask >> 1; - mask |= mask >> 2; - mask |= mask >> 4; - mask |= mask >> 8; - mask |= mask >> 16; -#if ULONG_MAX > 0xffffffffUL - mask |= mask >> 32; -#endif - - /* Search a random value in [0..mask] <= max */ -#if ULONG_MAX > 0xffffffffUL - if (max <= 0xffffffffUL) { - while ((value = (rk_random(state) & mask)) > max); - } - else { - while ((value = (rk_ulong(state) & mask)) > max); - } -#else - while ((value = (rk_ulong(state) & mask)) > max); -#endif - return value; -} - -double -rk_double(rk_state *state) -{ - /* shifts : 67108864 = 0x4000000, 9007199254740992 = 0x20000000000000 */ - long a = rk_random(state) >> 5, b = rk_random(state) >> 6; - return (a * 67108864.0 + b) / 9007199254740992.0; -} - -void -rk_fill(void *buffer, size_t size, rk_state *state) -{ - unsigned long r; - unsigned char *buf = buffer; - - for (; size >= 4; size -= 4) { - r = rk_random(state); - *(buf++) = r & 0xFF; - *(buf++) = (r >> 8) & 0xFF; - *(buf++) = (r >> 16) & 0xFF; - *(buf++) = (r >> 24) & 0xFF; - } - - if (!size) { - return; - } - r = rk_random(state); - for (; size; r >>= 8, size --) { - *(buf++) = (unsigned char)(r & 0xFF); - } -} - -rk_error -rk_devfill(void *buffer, size_t size, int strong) -{ -#ifndef _WIN32 - FILE *rfile; - int done; - - if (strong) { - rfile = fopen(RK_DEV_RANDOM, "rb"); - } - else { - rfile = fopen(RK_DEV_URANDOM, "rb"); - } - if (rfile == NULL) { - return RK_ENODEV; - } - done = fread(buffer, size, 1, rfile); - fclose(rfile); - if (done) { - return RK_NOERR; - } -#else - -#ifndef RK_NO_WINCRYPT - HCRYPTPROV hCryptProv; - BOOL done; - - if (!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, - CRYPT_VERIFYCONTEXT) || !hCryptProv) { - return RK_ENODEV; - } - done = CryptGenRandom(hCryptProv, size, (unsigned char *)buffer); - CryptReleaseContext(hCryptProv, 0); - if (done) { - return RK_NOERR; - } -#endif - -#endif - return RK_ENODEV; -} - -rk_error -rk_altfill(void *buffer, size_t size, int strong, rk_state *state) -{ - rk_error err; - - err = rk_devfill(buffer, size, strong); - if (err) { - rk_fill(buffer, size, state); - } - return err; -} - -double -rk_gauss(rk_state *state) -{ - if (state->has_gauss) { - const double tmp = state->gauss; - state->gauss = 0; - state->has_gauss = 0; - return tmp; - } - else { - double f, x1, x2, r2; - - do { - x1 = 2.0*rk_double(state) - 1.0; - x2 = 2.0*rk_double(state) - 1.0; - r2 = x1*x1 + x2*x2; - } - while (r2 >= 1.0 || r2 == 0.0); - - /* Box-Muller transform */ - f = sqrt(-2.0*log(r2)/r2); - /* Keep for next call */ - state->gauss = f*x1; - state->has_gauss = 1; - return f*x2; - } -} diff --git a/numpy/random/setup.py b/numpy/random/setup.py index 3f3b773a49b1..866c0cb2f0ab 100644 --- a/numpy/random/setup.py +++ b/numpy/random/setup.py @@ -1,23 +1,15 @@ -from __future__ import division, print_function - -from os.path import join, split, dirname import os +import platform import sys -from distutils.dep_util import newer -from distutils.msvccompiler import get_build_version as get_msvc_build_version +from os.path import join + +from numpy.distutils.system_info import platform_bits -def needs_mingw_ftime_workaround(): - # We need the mingw workaround for _ftime if the msvc runtime version is - # 7.1 or above and we build with mingw ... - # ... but we can't easily detect compiler version outside distutils command - # context, so we will need to detect in randomkit whether we build with gcc - msver = get_msvc_build_version() - if msver and msver >= 8: - return True +is_msvc = (platform.platform().startswith('Windows') and + platform.python_compiler().startswith('MS')) - return False -def configuration(parent_package='',top_path=None): +def configuration(parent_package='', top_path=None): from numpy.distutils.misc_util import Configuration, get_mathlibs config = Configuration('random', parent_package, top_path) @@ -25,40 +17,146 @@ def generate_libraries(ext, build_dir): config_cmd = config.get_config_cmd() libs = get_mathlibs() if sys.platform == 'win32': - libs.append('Advapi32') + libs.extend(['Advapi32', 'Kernel32']) ext.libraries.extend(libs) return None # enable unix large file support on 32 bit systems # (64 bit off_t, lseek -> lseek64 etc.) - if sys.platform[:3] == "aix": + if sys.platform[:3] == 'aix': defs = [('_LARGE_FILES', None)] else: defs = [('_FILE_OFFSET_BITS', '64'), ('_LARGEFILE_SOURCE', '1'), ('_LARGEFILE64_SOURCE', '1')] - if needs_mingw_ftime_workaround(): - defs.append(("NPY_NEEDS_MINGW_TIME_WORKAROUND", None)) - libs = [] - # Configure mtrand - config.add_extension('mtrand', - sources=[join('mtrand', x) for x in - ['mtrand.c', 'randomkit.c', 'initarray.c', - 'distributions.c']]+[generate_libraries], - libraries=libs, - depends=[join('mtrand', '*.h'), - join('mtrand', '*.pyx'), - join('mtrand', '*.pxi'),], - define_macros=defs, - ) + defs.append(('NPY_NO_DEPRECATED_API', 0)) + config.add_subpackage('tests') + config.add_data_dir('tests/data') + config.add_data_dir('_examples') + + EXTRA_LINK_ARGS = [] + EXTRA_LIBRARIES = ['npyrandom'] + if os.name != 'nt': + # Math lib + EXTRA_LIBRARIES.append('m') + # Some bit generators exclude GCC inlining + EXTRA_COMPILE_ARGS = ['-U__GNUC_GNU_INLINE__'] + + if is_msvc and platform_bits == 32: + # 32-bit windows requires explicit sse2 option + EXTRA_COMPILE_ARGS += ['/arch:SSE2'] + elif not is_msvc: + # Some bit generators require c99 + EXTRA_COMPILE_ARGS += ['-std=c99'] + + # Use legacy integer variable sizes + LEGACY_DEFS = [('NP_RANDOM_LEGACY', '1')] + PCG64_DEFS = [] + # One can force emulated 128-bit arithmetic if one wants. + #PCG64_DEFS += [('PCG_FORCE_EMULATED_128BIT_MATH', '1')] + depends = ['__init__.pxd', 'c_distributions.pxd', 'bit_generator.pxd'] - config.add_data_files(('.', join('mtrand', 'randomkit.h'))) - config.add_data_dir('tests') + # npyrandom - a library like npymath + npyrandom_sources = [ + 'src/distributions/logfactorial.c', + 'src/distributions/distributions.c', + 'src/distributions/random_mvhg_count.c', + 'src/distributions/random_mvhg_marginals.c', + 'src/distributions/random_hypergeometric.c', + ] + def gl_if_msvc(build_cmd): + """ Add flag if we are using MSVC compiler + + We can't see this in our scope, because we have not initialized the + distutils build command, so use this deferred calculation to run when + we are building the library. + """ + # Keep in sync with numpy/core/setup.py + if build_cmd.compiler.compiler_type == 'msvc': + # explicitly disable whole-program optimization + return ['/GL-'] + return [] + + config.add_installed_library('npyrandom', + sources=npyrandom_sources, + install_dir='lib', + build_info={ + 'include_dirs' : [], # empty list required for creating npyrandom.h + 'extra_compiler_args': [gl_if_msvc], + }) + + for gen in ['mt19937']: + # gen.pyx, src/gen/gen.c, src/gen/gen-jump.c + config.add_extension(f'_{gen}', + sources=[f'_{gen}.c', + f'src/{gen}/{gen}.c', + f'src/{gen}/{gen}-jump.c'], + include_dirs=['.', 'src', join('src', gen)], + libraries=EXTRA_LIBRARIES, + extra_compile_args=EXTRA_COMPILE_ARGS, + extra_link_args=EXTRA_LINK_ARGS, + depends=depends + [f'_{gen}.pyx'], + define_macros=defs, + ) + for gen in ['philox', 'pcg64', 'sfc64']: + # gen.pyx, src/gen/gen.c + _defs = defs + PCG64_DEFS if gen == 'pcg64' else defs + config.add_extension(f'_{gen}', + sources=[f'_{gen}.c', + f'src/{gen}/{gen}.c'], + include_dirs=['.', 'src', join('src', gen)], + libraries=EXTRA_LIBRARIES, + extra_compile_args=EXTRA_COMPILE_ARGS, + extra_link_args=EXTRA_LINK_ARGS, + depends=depends + [f'_{gen}.pyx', + 'bit_generator.pyx', 'bit_generator.pxd'], + define_macros=_defs, + ) + for gen in ['_common', 'bit_generator']: + # gen.pyx + config.add_extension(gen, + sources=[f'{gen}.c'], + libraries=EXTRA_LIBRARIES, + extra_compile_args=EXTRA_COMPILE_ARGS, + extra_link_args=EXTRA_LINK_ARGS, + include_dirs=['.', 'src'], + depends=depends + [f'{gen}.pyx', f'{gen}.pxd',], + define_macros=defs, + ) + config.add_data_files(f'{gen}.pxd') + for gen in ['_generator', '_bounded_integers']: + # gen.pyx, src/distributions/distributions.c + config.add_extension(gen, + sources=[f'{gen}.c'], + libraries=EXTRA_LIBRARIES + ['npymath'], + extra_compile_args=EXTRA_COMPILE_ARGS, + include_dirs=['.', 'src'], + extra_link_args=EXTRA_LINK_ARGS, + depends=depends + [f'{gen}.pyx'], + define_macros=defs, + ) + config.add_data_files('_bounded_integers.pxd') + mtrand_libs = ['m', 'npymath'] if os.name != 'nt' else ['npymath'] + config.add_extension('mtrand', + sources=['mtrand.c', + 'src/legacy/legacy-distributions.c', + 'src/distributions/distributions.c', + ], + include_dirs=['.', 'src', 'src/legacy'], + libraries=mtrand_libs, + extra_compile_args=EXTRA_COMPILE_ARGS, + extra_link_args=EXTRA_LINK_ARGS, + depends=depends + ['mtrand.pyx'], + define_macros=defs + LEGACY_DEFS, + ) + config.add_data_files(*depends) + config.add_data_files('*.pyi') return config if __name__ == '__main__': from numpy.distutils.core import setup + setup(configuration=configuration) diff --git a/numpy/random/src/distributions/LICENSE.md b/numpy/random/src/distributions/LICENSE.md new file mode 100644 index 000000000000..31576ba4b1f2 --- /dev/null +++ b/numpy/random/src/distributions/LICENSE.md @@ -0,0 +1,61 @@ +## NumPy + +Copyright (c) 2005-2017, NumPy Developers. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + +* Neither the name of the NumPy Developers nor the names of any + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +## Julia + +The ziggurat methods were derived from Julia. + +Copyright (c) 2009-2019: Jeff Bezanson, Stefan Karpinski, Viral B. Shah, +and other contributors: + +https://github.com/JuliaLang/julia/contributors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/numpy/random/src/distributions/distributions.c b/numpy/random/src/distributions/distributions.c new file mode 100644 index 000000000000..bd1e1faa4835 --- /dev/null +++ b/numpy/random/src/distributions/distributions.c @@ -0,0 +1,1689 @@ +#include "numpy/random/distributions.h" +#include "ziggurat_constants.h" +#include "logfactorial.h" + +#if defined(_MSC_VER) && defined(_WIN64) +#include <intrin.h> +#endif + +#include <assert.h> + +/* Inline generators for internal use */ +static NPY_INLINE uint32_t next_uint32(bitgen_t *bitgen_state) { + return bitgen_state->next_uint32(bitgen_state->state); +} +static NPY_INLINE uint64_t next_uint64(bitgen_t *bitgen_state) { + return bitgen_state->next_uint64(bitgen_state->state); +} + +static NPY_INLINE float next_float(bitgen_t *bitgen_state) { + return (next_uint32(bitgen_state) >> 8) * (1.0f / 16777216.0f); +} + +/* Random generators for external use */ +float random_standard_uniform_f(bitgen_t *bitgen_state) { + return next_float(bitgen_state); +} + +double random_standard_uniform(bitgen_t *bitgen_state) { + return next_double(bitgen_state); +} + +void random_standard_uniform_fill(bitgen_t *bitgen_state, npy_intp cnt, double *out) { + npy_intp i; + for (i = 0; i < cnt; i++) { + out[i] = next_double(bitgen_state); + } +} + +void random_standard_uniform_fill_f(bitgen_t *bitgen_state, npy_intp cnt, float *out) { + npy_intp i; + for (i = 0; i < cnt; i++) { + out[i] = next_float(bitgen_state); + } +} + +static double standard_exponential_unlikely(bitgen_t *bitgen_state, + uint8_t idx, double x) { + if (idx == 0) { + /* Switch to 1.0 - U to avoid log(0.0), see GH 13361 */ + return ziggurat_exp_r - npy_log1p(-next_double(bitgen_state)); + } else if ((fe_double[idx - 1] - fe_double[idx]) * next_double(bitgen_state) + + fe_double[idx] < + exp(-x)) { + return x; + } else { + return random_standard_exponential(bitgen_state); + } +} + +double random_standard_exponential(bitgen_t *bitgen_state) { + uint64_t ri; + uint8_t idx; + double x; + ri = next_uint64(bitgen_state); + ri >>= 3; + idx = ri & 0xFF; + ri >>= 8; + x = ri * we_double[idx]; + if (ri < ke_double[idx]) { + return x; /* 98.9% of the time we return here 1st try */ + } + return standard_exponential_unlikely(bitgen_state, idx, x); +} + +void random_standard_exponential_fill(bitgen_t * bitgen_state, npy_intp cnt, double * out) +{ + npy_intp i; + for (i = 0; i < cnt; i++) { + out[i] = random_standard_exponential(bitgen_state); + } +} + +static float standard_exponential_unlikely_f(bitgen_t *bitgen_state, + uint8_t idx, float x) { + if (idx == 0) { + /* Switch to 1.0 - U to avoid log(0.0), see GH 13361 */ + return ziggurat_exp_r_f - npy_log1pf(-next_float(bitgen_state)); + } else if ((fe_float[idx - 1] - fe_float[idx]) * next_float(bitgen_state) + + fe_float[idx] < + expf(-x)) { + return x; + } else { + return random_standard_exponential_f(bitgen_state); + } +} + +float random_standard_exponential_f(bitgen_t *bitgen_state) { + uint32_t ri; + uint8_t idx; + float x; + ri = next_uint32(bitgen_state); + ri >>= 1; + idx = ri & 0xFF; + ri >>= 8; + x = ri * we_float[idx]; + if (ri < ke_float[idx]) { + return x; /* 98.9% of the time we return here 1st try */ + } + return standard_exponential_unlikely_f(bitgen_state, idx, x); +} + +void random_standard_exponential_fill_f(bitgen_t * bitgen_state, npy_intp cnt, float * out) +{ + npy_intp i; + for (i = 0; i < cnt; i++) { + out[i] = random_standard_exponential_f(bitgen_state); + } +} + +void random_standard_exponential_inv_fill(bitgen_t * bitgen_state, npy_intp cnt, double * out) +{ + npy_intp i; + for (i = 0; i < cnt; i++) { + out[i] = -npy_log1p(-next_double(bitgen_state)); + } +} + +void random_standard_exponential_inv_fill_f(bitgen_t * bitgen_state, npy_intp cnt, float * out) +{ + npy_intp i; + for (i = 0; i < cnt; i++) { + out[i] = -npy_log1p(-next_float(bitgen_state)); + } +} + + +double random_standard_normal(bitgen_t *bitgen_state) { + uint64_t r; + int sign; + uint64_t rabs; + int idx; + double x, xx, yy; + for (;;) { + /* r = e3n52sb8 */ + r = next_uint64(bitgen_state); + idx = r & 0xff; + r >>= 8; + sign = r & 0x1; + rabs = (r >> 1) & 0x000fffffffffffff; + x = rabs * wi_double[idx]; + if (sign & 0x1) + x = -x; + if (rabs < ki_double[idx]) + return x; /* 99.3% of the time return here */ + if (idx == 0) { + for (;;) { + /* Switch to 1.0 - U to avoid log(0.0), see GH 13361 */ + xx = -ziggurat_nor_inv_r * npy_log1p(-next_double(bitgen_state)); + yy = -npy_log1p(-next_double(bitgen_state)); + if (yy + yy > xx * xx) + return ((rabs >> 8) & 0x1) ? -(ziggurat_nor_r + xx) + : ziggurat_nor_r + xx; + } + } else { + if (((fi_double[idx - 1] - fi_double[idx]) * next_double(bitgen_state) + + fi_double[idx]) < exp(-0.5 * x * x)) + return x; + } + } +} + +void random_standard_normal_fill(bitgen_t *bitgen_state, npy_intp cnt, double *out) { + npy_intp i; + for (i = 0; i < cnt; i++) { + out[i] = random_standard_normal(bitgen_state); + } +} + +float random_standard_normal_f(bitgen_t *bitgen_state) { + uint32_t r; + int sign; + uint32_t rabs; + int idx; + float x, xx, yy; + for (;;) { + /* r = n23sb8 */ + r = next_uint32(bitgen_state); + idx = r & 0xff; + sign = (r >> 8) & 0x1; + rabs = (r >> 9) & 0x0007fffff; + x = rabs * wi_float[idx]; + if (sign & 0x1) + x = -x; + if (rabs < ki_float[idx]) + return x; /* # 99.3% of the time return here */ + if (idx == 0) { + for (;;) { + /* Switch to 1.0 - U to avoid log(0.0), see GH 13361 */ + xx = -ziggurat_nor_inv_r_f * npy_log1pf(-next_float(bitgen_state)); + yy = -npy_log1pf(-next_float(bitgen_state)); + if (yy + yy > xx * xx) + return ((rabs >> 8) & 0x1) ? -(ziggurat_nor_r_f + xx) + : ziggurat_nor_r_f + xx; + } + } else { + if (((fi_float[idx - 1] - fi_float[idx]) * next_float(bitgen_state) + + fi_float[idx]) < exp(-0.5 * x * x)) + return x; + } + } +} + +void random_standard_normal_fill_f(bitgen_t *bitgen_state, npy_intp cnt, float *out) { + npy_intp i; + for (i = 0; i < cnt; i++) { + out[i] = random_standard_normal_f(bitgen_state); + } +} + +double random_standard_gamma(bitgen_t *bitgen_state, + double shape) { + double b, c; + double U, V, X, Y; + + if (shape == 1.0) { + return random_standard_exponential(bitgen_state); + } else if (shape == 0.0) { + return 0.0; + } else if (shape < 1.0) { + for (;;) { + U = next_double(bitgen_state); + V = random_standard_exponential(bitgen_state); + if (U <= 1.0 - shape) { + X = pow(U, 1. / shape); + if (X <= V) { + return X; + } + } else { + Y = -log((1 - U) / shape); + X = pow(1.0 - shape + shape * Y, 1. / shape); + if (X <= (V + Y)) { + return X; + } + } + } + } else { + b = shape - 1. / 3.; + c = 1. / sqrt(9 * b); + for (;;) { + do { + X = random_standard_normal(bitgen_state); + V = 1.0 + c * X; + } while (V <= 0.0); + + V = V * V * V; + U = next_double(bitgen_state); + if (U < 1.0 - 0.0331 * (X * X) * (X * X)) + return (b * V); + /* log(0.0) ok here */ + if (log(U) < 0.5 * X * X + b * (1. - V + log(V))) + return (b * V); + } + } +} + +float random_standard_gamma_f(bitgen_t *bitgen_state, + float shape) { + float b, c; + float U, V, X, Y; + + if (shape == 1.0f) { + return random_standard_exponential_f(bitgen_state); + } else if (shape == 0.0) { + return 0.0; + } else if (shape < 1.0f) { + for (;;) { + U = next_float(bitgen_state); + V = random_standard_exponential_f(bitgen_state); + if (U <= 1.0f - shape) { + X = powf(U, 1.0f / shape); + if (X <= V) { + return X; + } + } else { + Y = -logf((1.0f - U) / shape); + X = powf(1.0f - shape + shape * Y, 1.0f / shape); + if (X <= (V + Y)) { + return X; + } + } + } + } else { + b = shape - 1.0f / 3.0f; + c = 1.0f / sqrtf(9.0f * b); + for (;;) { + do { + X = random_standard_normal_f(bitgen_state); + V = 1.0f + c * X; + } while (V <= 0.0f); + + V = V * V * V; + U = next_float(bitgen_state); + if (U < 1.0f - 0.0331f * (X * X) * (X * X)) + return (b * V); + /* logf(0.0) ok here */ + if (logf(U) < 0.5f * X * X + b * (1.0f - V + logf(V))) + return (b * V); + } + } +} + +int64_t random_positive_int64(bitgen_t *bitgen_state) { + return next_uint64(bitgen_state) >> 1; +} + +int32_t random_positive_int32(bitgen_t *bitgen_state) { + return next_uint32(bitgen_state) >> 1; +} + +int64_t random_positive_int(bitgen_t *bitgen_state) { +#if ULONG_MAX <= 0xffffffffUL + return (int64_t)(next_uint32(bitgen_state) >> 1); +#else + return (int64_t)(next_uint64(bitgen_state) >> 1); +#endif +} + +uint64_t random_uint(bitgen_t *bitgen_state) { +#if ULONG_MAX <= 0xffffffffUL + return next_uint32(bitgen_state); +#else + return next_uint64(bitgen_state); +#endif +} + +/* + * log-gamma function to support some of these distributions. The + * algorithm comes from SPECFUN by Shanjie Zhang and Jianming Jin and their + * book "Computation of Special Functions", 1996, John Wiley & Sons, Inc. + * + * If random_loggam(k+1) is being used to compute log(k!) for an integer k, consider + * using logfactorial(k) instead. + */ +double random_loggam(double x) { + double x0, x2, lg2pi, gl, gl0; + RAND_INT_TYPE k, n; + + static double a[10] = {8.333333333333333e-02, -2.777777777777778e-03, + 7.936507936507937e-04, -5.952380952380952e-04, + 8.417508417508418e-04, -1.917526917526918e-03, + 6.410256410256410e-03, -2.955065359477124e-02, + 1.796443723688307e-01, -1.39243221690590e+00}; + + if ((x == 1.0) || (x == 2.0)) { + return 0.0; + } else if (x < 7.0) { + n = (RAND_INT_TYPE)(7 - x); + } else { + n = 0; + } + x0 = x + n; + x2 = (1.0 / x0) * (1.0 / x0); + /* log(2 * M_PI) */ + lg2pi = 1.8378770664093453e+00; + gl0 = a[9]; + for (k = 8; k >= 0; k--) { + gl0 *= x2; + gl0 += a[k]; + } + gl = gl0 / x0 + 0.5 * lg2pi + (x0 - 0.5) * log(x0) - x0; + if (x < 7.0) { + for (k = 1; k <= n; k++) { + gl -= log(x0 - 1.0); + x0 -= 1.0; + } + } + return gl; +} + +/* +double random_normal(bitgen_t *bitgen_state, double loc, double scale) { + return loc + scale * random_gauss(bitgen_state); +} +*/ + +double random_normal(bitgen_t *bitgen_state, double loc, double scale) { + return loc + scale * random_standard_normal(bitgen_state); +} + +double random_exponential(bitgen_t *bitgen_state, double scale) { + return scale * random_standard_exponential(bitgen_state); +} + +double random_uniform(bitgen_t *bitgen_state, double lower, double range) { + return lower + range * next_double(bitgen_state); +} + +double random_gamma(bitgen_t *bitgen_state, double shape, double scale) { + return scale * random_standard_gamma(bitgen_state, shape); +} + +float random_gamma_f(bitgen_t *bitgen_state, float shape, float scale) { + return scale * random_standard_gamma_f(bitgen_state, shape); +} + +double random_beta(bitgen_t *bitgen_state, double a, double b) { + double Ga, Gb; + + if ((a <= 1.0) && (b <= 1.0)) { + double U, V, X, Y, XpY; + /* Use Johnk's algorithm */ + + while (1) { + U = next_double(bitgen_state); + V = next_double(bitgen_state); + X = pow(U, 1.0 / a); + Y = pow(V, 1.0 / b); + XpY = X + Y; + /* Reject if both U and V are 0.0, which is approx 1 in 10^106 */ + if ((XpY <= 1.0) && (XpY > 0.0)) { + if (X + Y > 0) { + return X / XpY; + } else { + double logX = log(U) / a; + double logY = log(V) / b; + double logM = logX > logY ? logX : logY; + logX -= logM; + logY -= logM; + + return exp(logX - log(exp(logX) + exp(logY))); + } + } + } + } else { + Ga = random_standard_gamma(bitgen_state, a); + Gb = random_standard_gamma(bitgen_state, b); + return Ga / (Ga + Gb); + } +} + +double random_chisquare(bitgen_t *bitgen_state, double df) { + return 2.0 * random_standard_gamma(bitgen_state, df / 2.0); +} + +double random_f(bitgen_t *bitgen_state, double dfnum, double dfden) { + return ((random_chisquare(bitgen_state, dfnum) * dfden) / + (random_chisquare(bitgen_state, dfden) * dfnum)); +} + +double random_standard_cauchy(bitgen_t *bitgen_state) { + return random_standard_normal(bitgen_state) / random_standard_normal(bitgen_state); +} + +double random_pareto(bitgen_t *bitgen_state, double a) { + return expm1(random_standard_exponential(bitgen_state) / a); +} + +double random_weibull(bitgen_t *bitgen_state, double a) { + if (a == 0.0) { + return 0.0; + } + return pow(random_standard_exponential(bitgen_state), 1. / a); +} + +double random_power(bitgen_t *bitgen_state, double a) { + return pow(-expm1(-random_standard_exponential(bitgen_state)), 1. / a); +} + +double random_laplace(bitgen_t *bitgen_state, double loc, double scale) { + double U; + + U = next_double(bitgen_state); + if (U >= 0.5) { + U = loc - scale * log(2.0 - U - U); + } else if (U > 0.0) { + U = loc + scale * log(U + U); + } else { + /* Reject U == 0.0 and call again to get next value */ + U = random_laplace(bitgen_state, loc, scale); + } + return U; +} + +double random_gumbel(bitgen_t *bitgen_state, double loc, double scale) { + double U; + + U = 1.0 - next_double(bitgen_state); + if (U < 1.0) { + return loc - scale * log(-log(U)); + } + /* Reject U == 1.0 and call again to get next value */ + return random_gumbel(bitgen_state, loc, scale); +} + +double random_logistic(bitgen_t *bitgen_state, double loc, double scale) { + double U; + + U = next_double(bitgen_state); + if (U > 0.0) { + return loc + scale * log(U / (1.0 - U)); + } + /* Reject U == 0.0 and call again to get next value */ + return random_logistic(bitgen_state, loc, scale); +} + +double random_lognormal(bitgen_t *bitgen_state, double mean, double sigma) { + return exp(random_normal(bitgen_state, mean, sigma)); +} + +double random_rayleigh(bitgen_t *bitgen_state, double mode) { + return mode * sqrt(2.0 * random_standard_exponential(bitgen_state)); +} + +double random_standard_t(bitgen_t *bitgen_state, double df) { + double num, denom; + + num = random_standard_normal(bitgen_state); + denom = random_standard_gamma(bitgen_state, df / 2); + return sqrt(df / 2) * num / sqrt(denom); +} + +static RAND_INT_TYPE random_poisson_mult(bitgen_t *bitgen_state, double lam) { + RAND_INT_TYPE X; + double prod, U, enlam; + + enlam = exp(-lam); + X = 0; + prod = 1.0; + while (1) { + U = next_double(bitgen_state); + prod *= U; + if (prod > enlam) { + X += 1; + } else { + return X; + } + } +} + +/* + * The transformed rejection method for generating Poisson random variables + * W. Hoermann + * Insurance: Mathematics and Economics 12, 39-45 (1993) + */ +#define LS2PI 0.91893853320467267 +#define TWELFTH 0.083333333333333333333333 +static RAND_INT_TYPE random_poisson_ptrs(bitgen_t *bitgen_state, double lam) { + RAND_INT_TYPE k; + double U, V, slam, loglam, a, b, invalpha, vr, us; + + slam = sqrt(lam); + loglam = log(lam); + b = 0.931 + 2.53 * slam; + a = -0.059 + 0.02483 * b; + invalpha = 1.1239 + 1.1328 / (b - 3.4); + vr = 0.9277 - 3.6224 / (b - 2); + + while (1) { + U = next_double(bitgen_state) - 0.5; + V = next_double(bitgen_state); + us = 0.5 - fabs(U); + k = (RAND_INT_TYPE)floor((2 * a / us + b) * U + lam + 0.43); + if ((us >= 0.07) && (V <= vr)) { + return k; + } + if ((k < 0) || ((us < 0.013) && (V > us))) { + continue; + } + /* log(V) == log(0.0) ok here */ + /* if U==0.0 so that us==0.0, log is ok since always returns */ + if ((log(V) + log(invalpha) - log(a / (us * us) + b)) <= + (-lam + k * loglam - random_loggam(k + 1))) { + return k; + } + } +} + +RAND_INT_TYPE random_poisson(bitgen_t *bitgen_state, double lam) { + if (lam >= 10) { + return random_poisson_ptrs(bitgen_state, lam); + } else if (lam == 0) { + return 0; + } else { + return random_poisson_mult(bitgen_state, lam); + } +} + +RAND_INT_TYPE random_negative_binomial(bitgen_t *bitgen_state, double n, + double p) { + double Y = random_gamma(bitgen_state, n, (1 - p) / p); + return random_poisson(bitgen_state, Y); +} + +RAND_INT_TYPE random_binomial_btpe(bitgen_t *bitgen_state, RAND_INT_TYPE n, + double p, binomial_t *binomial) { + double r, q, fm, p1, xm, xl, xr, c, laml, lamr, p2, p3, p4; + double a, u, v, s, F, rho, t, A, nrq, x1, x2, f1, f2, z, z2, w, w2, x; + RAND_INT_TYPE m, y, k, i; + + if (!(binomial->has_binomial) || (binomial->nsave != n) || + (binomial->psave != p)) { + /* initialize */ + binomial->nsave = n; + binomial->psave = p; + binomial->has_binomial = 1; + binomial->r = r = MIN(p, 1.0 - p); + binomial->q = q = 1.0 - r; + binomial->fm = fm = n * r + r; + binomial->m = m = (RAND_INT_TYPE)floor(binomial->fm); + binomial->p1 = p1 = floor(2.195 * sqrt(n * r * q) - 4.6 * q) + 0.5; + binomial->xm = xm = m + 0.5; + binomial->xl = xl = xm - p1; + binomial->xr = xr = xm + p1; + binomial->c = c = 0.134 + 20.5 / (15.3 + m); + a = (fm - xl) / (fm - xl * r); + binomial->laml = laml = a * (1.0 + a / 2.0); + a = (xr - fm) / (xr * q); + binomial->lamr = lamr = a * (1.0 + a / 2.0); + binomial->p2 = p2 = p1 * (1.0 + 2.0 * c); + binomial->p3 = p3 = p2 + c / laml; + binomial->p4 = p4 = p3 + c / lamr; + } else { + r = binomial->r; + q = binomial->q; + fm = binomial->fm; + m = binomial->m; + p1 = binomial->p1; + xm = binomial->xm; + xl = binomial->xl; + xr = binomial->xr; + c = binomial->c; + laml = binomial->laml; + lamr = binomial->lamr; + p2 = binomial->p2; + p3 = binomial->p3; + p4 = binomial->p4; + } + +/* sigh ... */ +Step10: + nrq = n * r * q; + u = next_double(bitgen_state) * p4; + v = next_double(bitgen_state); + if (u > p1) + goto Step20; + y = (RAND_INT_TYPE)floor(xm - p1 * v + u); + goto Step60; + +Step20: + if (u > p2) + goto Step30; + x = xl + (u - p1) / c; + v = v * c + 1.0 - fabs(m - x + 0.5) / p1; + if (v > 1.0) + goto Step10; + y = (RAND_INT_TYPE)floor(x); + goto Step50; + +Step30: + if (u > p3) + goto Step40; + y = (RAND_INT_TYPE)floor(xl + log(v) / laml); + /* Reject if v==0.0 since previous cast is undefined */ + if ((y < 0) || (v == 0.0)) + goto Step10; + v = v * (u - p2) * laml; + goto Step50; + +Step40: + y = (RAND_INT_TYPE)floor(xr - log(v) / lamr); + /* Reject if v==0.0 since previous cast is undefined */ + if ((y > n) || (v == 0.0)) + goto Step10; + v = v * (u - p3) * lamr; + +Step50: + k = llabs(y - m); + if ((k > 20) && (k < ((nrq) / 2.0 - 1))) + goto Step52; + + s = r / q; + a = s * (n + 1); + F = 1.0; + if (m < y) { + for (i = m + 1; i <= y; i++) { + F *= (a / i - s); + } + } else if (m > y) { + for (i = y + 1; i <= m; i++) { + F /= (a / i - s); + } + } + if (v > F) + goto Step10; + goto Step60; + +Step52: + rho = + (k / (nrq)) * ((k * (k / 3.0 + 0.625) + 0.16666666666666666) / nrq + 0.5); + t = -k * k / (2 * nrq); + /* log(0.0) ok here */ + A = log(v); + if (A < (t - rho)) + goto Step60; + if (A > (t + rho)) + goto Step10; + + x1 = y + 1; + f1 = m + 1; + z = n + 1 - m; + w = n - y + 1; + x2 = x1 * x1; + f2 = f1 * f1; + z2 = z * z; + w2 = w * w; + if (A > (xm * log(f1 / x1) + (n - m + 0.5) * log(z / w) + + (y - m) * log(w * r / (x1 * q)) + + (13680. - (462. - (132. - (99. - 140. / f2) / f2) / f2) / f2) / f1 / + 166320. + + (13680. - (462. - (132. - (99. - 140. / z2) / z2) / z2) / z2) / z / + 166320. + + (13680. - (462. - (132. - (99. - 140. / x2) / x2) / x2) / x2) / x1 / + 166320. + + (13680. - (462. - (132. - (99. - 140. / w2) / w2) / w2) / w2) / w / + 166320.)) { + goto Step10; + } + +Step60: + if (p > 0.5) { + y = n - y; + } + + return y; +} + +RAND_INT_TYPE random_binomial_inversion(bitgen_t *bitgen_state, RAND_INT_TYPE n, + double p, binomial_t *binomial) { + double q, qn, np, px, U; + RAND_INT_TYPE X, bound; + + if (!(binomial->has_binomial) || (binomial->nsave != n) || + (binomial->psave != p)) { + binomial->nsave = n; + binomial->psave = p; + binomial->has_binomial = 1; + binomial->q = q = 1.0 - p; + binomial->r = qn = exp(n * log(q)); + binomial->c = np = n * p; + binomial->m = bound = (RAND_INT_TYPE)MIN(n, np + 10.0 * sqrt(np * q + 1)); + } else { + q = binomial->q; + qn = binomial->r; + np = binomial->c; + bound = binomial->m; + } + X = 0; + px = qn; + U = next_double(bitgen_state); + while (U > px) { + X++; + if (X > bound) { + X = 0; + px = qn; + U = next_double(bitgen_state); + } else { + U -= px; + px = ((n - X + 1) * p * px) / (X * q); + } + } + return X; +} + +int64_t random_binomial(bitgen_t *bitgen_state, double p, int64_t n, + binomial_t *binomial) { + double q; + + if ((n == 0LL) || (p == 0.0f)) + return 0; + + if (p <= 0.5) { + if (p * n <= 30.0) { + return random_binomial_inversion(bitgen_state, n, p, binomial); + } else { + return random_binomial_btpe(bitgen_state, n, p, binomial); + } + } else { + q = 1.0 - p; + if (q * n <= 30.0) { + return n - random_binomial_inversion(bitgen_state, n, q, binomial); + } else { + return n - random_binomial_btpe(bitgen_state, n, q, binomial); + } + } +} + +double random_noncentral_chisquare(bitgen_t *bitgen_state, double df, + double nonc) { + if (npy_isnan(nonc)) { + return NPY_NAN; + } + if (nonc == 0) { + return random_chisquare(bitgen_state, df); + } + if (1 < df) { + const double Chi2 = random_chisquare(bitgen_state, df - 1); + const double n = random_standard_normal(bitgen_state) + sqrt(nonc); + return Chi2 + n * n; + } else { + const RAND_INT_TYPE i = random_poisson(bitgen_state, nonc / 2.0); + return random_chisquare(bitgen_state, df + 2 * i); + } +} + +double random_noncentral_f(bitgen_t *bitgen_state, double dfnum, double dfden, + double nonc) { + double t = random_noncentral_chisquare(bitgen_state, dfnum, nonc) * dfden; + return t / (random_chisquare(bitgen_state, dfden) * dfnum); +} + +double random_wald(bitgen_t *bitgen_state, double mean, double scale) { + double U, X, Y; + double mu_2l; + + mu_2l = mean / (2 * scale); + Y = random_standard_normal(bitgen_state); + Y = mean * Y * Y; + X = mean + mu_2l * (Y - sqrt(4 * scale * Y + Y * Y)); + U = next_double(bitgen_state); + if (U <= mean / (mean + X)) { + return X; + } else { + return mean * mean / X; + } +} + +double random_vonmises(bitgen_t *bitgen_state, double mu, double kappa) { + double s; + double U, V, W, Y, Z; + double result, mod; + int neg; + if (npy_isnan(kappa)) { + return NPY_NAN; + } + if (kappa < 1e-8) { + /* Use a uniform for very small values of kappa */ + return M_PI * (2 * next_double(bitgen_state) - 1); + } else { + /* with double precision rho is zero until 1.4e-8 */ + if (kappa < 1e-5) { + /* + * second order taylor expansion around kappa = 0 + * precise until relatively large kappas as second order is 0 + */ + s = (1. / kappa + kappa); + } else { + if (kappa <= 1e6) { + /* Path for 1e-5 <= kappa <= 1e6 */ + double r = 1 + sqrt(1 + 4 * kappa * kappa); + double rho = (r - sqrt(2 * r)) / (2 * kappa); + s = (1 + rho * rho) / (2 * rho); + } else { + /* Fallback to wrapped normal distribution for kappa > 1e6 */ + result = mu + sqrt(1. / kappa) * random_standard_normal(bitgen_state); + /* Ensure result is within bounds */ + if (result < -M_PI) { + result += 2*M_PI; + } + if (result > M_PI) { + result -= 2*M_PI; + } + return result; + } + } + + while (1) { + U = next_double(bitgen_state); + Z = cos(M_PI * U); + W = (1 + s * Z) / (s + Z); + Y = kappa * (s - W); + V = next_double(bitgen_state); + /* + * V==0.0 is ok here since Y >= 0 always leads + * to accept, while Y < 0 always rejects + */ + if ((Y * (2 - Y) - V >= 0) || (log(Y / V) + 1 - Y >= 0)) { + break; + } + } + + U = next_double(bitgen_state); + + result = acos(W); + if (U < 0.5) { + result = -result; + } + result += mu; + neg = (result < 0); + mod = fabs(result); + mod = (fmod(mod + M_PI, 2 * M_PI) - M_PI); + if (neg) { + mod *= -1; + } + + return mod; + } +} + +int64_t random_logseries(bitgen_t *bitgen_state, double p) { + double q, r, U, V; + int64_t result; + + r = npy_log1p(-p); + + while (1) { + V = next_double(bitgen_state); + if (V >= p) { + return 1; + } + U = next_double(bitgen_state); + q = -expm1(r * U); + if (V <= q * q) { + result = (int64_t)floor(1 + log(V) / log(q)); + if ((result < 1) || (V == 0.0)) { + continue; + } else { + return result; + } + } + if (V >= q) { + return 1; + } + return 2; + } +} + +/* + * RAND_INT_TYPE is used to share integer generators with RandomState which + * used long in place of int64_t. If changing a distribution that uses + * RAND_INT_TYPE, then the original unmodified copy must be retained for + * use in RandomState by copying to the legacy distributions source file. + */ + +/* Still used but both generator and mtrand via legacy_random_geometric */ +RAND_INT_TYPE random_geometric_search(bitgen_t *bitgen_state, double p) { + double U; + RAND_INT_TYPE X; + double sum, prod, q; + + X = 1; + sum = prod = p; + q = 1.0 - p; + U = next_double(bitgen_state); + while (U > sum) { + prod *= q; + sum += prod; + X++; + } + return X; +} + +int64_t random_geometric_inversion(bitgen_t *bitgen_state, double p) { + return (int64_t)ceil(-random_standard_exponential(bitgen_state) / npy_log1p(-p)); +} + +int64_t random_geometric(bitgen_t *bitgen_state, double p) { + if (p >= 0.333333333333333333333333) { + return random_geometric_search(bitgen_state, p); + } else { + return random_geometric_inversion(bitgen_state, p); + } +} + +RAND_INT_TYPE random_zipf(bitgen_t *bitgen_state, double a) { + double am1, b; + + am1 = a - 1.0; + b = pow(2.0, am1); + while (1) { + double T, U, V, X; + + U = 1.0 - next_double(bitgen_state); + V = next_double(bitgen_state); + X = floor(pow(U, -1.0 / am1)); + /* + * The real result may be above what can be represented in a signed + * long. Since this is a straightforward rejection algorithm, we can + * just reject this value. This function then models a Zipf + * distribution truncated to sys.maxint. + */ + if (X > (double)RAND_INT_MAX || X < 1.0) { + continue; + } + + T = pow(1.0 + 1.0 / X, am1); + if (V * X * (T - 1.0) / (b - 1.0) <= T / b) { + return (RAND_INT_TYPE)X; + } + } +} + +double random_triangular(bitgen_t *bitgen_state, double left, double mode, + double right) { + double base, leftbase, ratio, leftprod, rightprod; + double U; + + base = right - left; + leftbase = mode - left; + ratio = leftbase / base; + leftprod = leftbase * base; + rightprod = (right - mode) * base; + + U = next_double(bitgen_state); + if (U <= ratio) { + return left + sqrt(U * leftprod); + } else { + return right - sqrt((1.0 - U) * rightprod); + } +} + + +uint64_t random_interval(bitgen_t *bitgen_state, uint64_t max) { + uint64_t mask, value; + if (max == 0) { + return 0; + } + + mask = max; + + /* Smallest bit mask >= max */ + mask |= mask >> 1; + mask |= mask >> 2; + mask |= mask >> 4; + mask |= mask >> 8; + mask |= mask >> 16; + mask |= mask >> 32; + + /* Search a random value in [0..mask] <= max */ + if (max <= 0xffffffffUL) { + while ((value = (next_uint32(bitgen_state) & mask)) > max) + ; + } else { + while ((value = (next_uint64(bitgen_state) & mask)) > max) + ; + } + return value; +} + +/* Bounded generators */ +static NPY_INLINE uint64_t gen_mask(uint64_t max) { + uint64_t mask = max; + mask |= mask >> 1; + mask |= mask >> 2; + mask |= mask >> 4; + mask |= mask >> 8; + mask |= mask >> 16; + mask |= mask >> 32; + return mask; +} + +/* Generate 16 bit random numbers using a 32 bit buffer. */ +static NPY_INLINE uint16_t buffered_uint16(bitgen_t *bitgen_state, int *bcnt, + uint32_t *buf) { + if (!(bcnt[0])) { + buf[0] = next_uint32(bitgen_state); + bcnt[0] = 1; + } else { + buf[0] >>= 16; + bcnt[0] -= 1; + } + + return (uint16_t)buf[0]; +} + +/* Generate 8 bit random numbers using a 32 bit buffer. */ +static NPY_INLINE uint8_t buffered_uint8(bitgen_t *bitgen_state, int *bcnt, + uint32_t *buf) { + if (!(bcnt[0])) { + buf[0] = next_uint32(bitgen_state); + bcnt[0] = 3; + } else { + buf[0] >>= 8; + bcnt[0] -= 1; + } + + return (uint8_t)buf[0]; +} + +/* Static `masked rejection` function called by random_bounded_uint64(...) */ +static NPY_INLINE uint64_t bounded_masked_uint64(bitgen_t *bitgen_state, + uint64_t rng, uint64_t mask) { + uint64_t val; + + while ((val = (next_uint64(bitgen_state) & mask)) > rng) + ; + + return val; +} + +/* Static `masked rejection` function called by + * random_buffered_bounded_uint32(...) */ +static NPY_INLINE uint32_t +buffered_bounded_masked_uint32(bitgen_t *bitgen_state, uint32_t rng, + uint32_t mask, int *bcnt, uint32_t *buf) { + /* + * The buffer and buffer count are not used here but are included to allow + * this function to be templated with the similar uint8 and uint16 + * functions + */ + + uint32_t val; + + while ((val = (next_uint32(bitgen_state) & mask)) > rng) + ; + + return val; +} + +/* Static `masked rejection` function called by + * random_buffered_bounded_uint16(...) */ +static NPY_INLINE uint16_t +buffered_bounded_masked_uint16(bitgen_t *bitgen_state, uint16_t rng, + uint16_t mask, int *bcnt, uint32_t *buf) { + uint16_t val; + + while ((val = (buffered_uint16(bitgen_state, bcnt, buf) & mask)) > rng) + ; + + return val; +} + +/* Static `masked rejection` function called by + * random_buffered_bounded_uint8(...) */ +static NPY_INLINE uint8_t buffered_bounded_masked_uint8(bitgen_t *bitgen_state, + uint8_t rng, + uint8_t mask, int *bcnt, + uint32_t *buf) { + uint8_t val; + + while ((val = (buffered_uint8(bitgen_state, bcnt, buf) & mask)) > rng) + ; + + return val; +} + +static NPY_INLINE npy_bool buffered_bounded_bool(bitgen_t *bitgen_state, + npy_bool off, npy_bool rng, + npy_bool mask, int *bcnt, + uint32_t *buf) { + if (rng == 0) + return off; + if (!(bcnt[0])) { + buf[0] = next_uint32(bitgen_state); + bcnt[0] = 31; + } else { + buf[0] >>= 1; + bcnt[0] -= 1; + } + return (buf[0] & 0x00000001UL) != 0; +} + +/* Static `Lemire rejection` function called by random_bounded_uint64(...) */ +static NPY_INLINE uint64_t bounded_lemire_uint64(bitgen_t *bitgen_state, + uint64_t rng) { + /* + * Uses Lemire's algorithm - https://arxiv.org/abs/1805.10941 + * + * Note: `rng` should not be 0xFFFFFFFFFFFFFFFF. When this happens `rng_excl` + * becomes zero. + */ + const uint64_t rng_excl = rng + 1; + + assert(rng != 0xFFFFFFFFFFFFFFFFULL); + +#if __SIZEOF_INT128__ + /* 128-bit uint available (e.g. GCC/clang). `m` is the __uint128_t scaled + * integer. */ + __uint128_t m; + uint64_t leftover; + + /* Generate a scaled random number. */ + m = ((__uint128_t)next_uint64(bitgen_state)) * rng_excl; + + /* Rejection sampling to remove any bias. */ + leftover = m & 0xFFFFFFFFFFFFFFFFULL; + + if (leftover < rng_excl) { + /* `rng_excl` is a simple upper bound for `threshold`. */ + const uint64_t threshold = (UINT64_MAX - rng) % rng_excl; + + while (leftover < threshold) { + m = ((__uint128_t)next_uint64(bitgen_state)) * rng_excl; + leftover = m & 0xFFFFFFFFFFFFFFFFULL; + } + } + + return (m >> 64); +#else + /* 128-bit uint NOT available (e.g. MSVS). `m1` is the upper 64-bits of the + * scaled integer. */ + uint64_t m1; + uint64_t x; + uint64_t leftover; + + x = next_uint64(bitgen_state); + + /* Rejection sampling to remove any bias. */ + leftover = x * rng_excl; /* The lower 64-bits of the mult. */ + + if (leftover < rng_excl) { + /* `rng_excl` is a simple upper bound for `threshold`. */ + const uint64_t threshold = (UINT64_MAX - rng) % rng_excl; + + while (leftover < threshold) { + x = next_uint64(bitgen_state); + leftover = x * rng_excl; + } + } + +#if defined(_MSC_VER) && defined(_WIN64) + /* _WIN64 architecture. Use the __umulh intrinsic to calc `m1`. */ + m1 = __umulh(x, rng_excl); +#else + /* 32-bit architecture. Emulate __umulh to calc `m1`. */ + { + uint64_t x0, x1, rng_excl0, rng_excl1; + uint64_t w0, w1, w2, t; + + x0 = x & 0xFFFFFFFFULL; + x1 = x >> 32; + rng_excl0 = rng_excl & 0xFFFFFFFFULL; + rng_excl1 = rng_excl >> 32; + w0 = x0 * rng_excl0; + t = x1 * rng_excl0 + (w0 >> 32); + w1 = t & 0xFFFFFFFFULL; + w2 = t >> 32; + w1 += x0 * rng_excl1; + m1 = x1 * rng_excl1 + w2 + (w1 >> 32); + } +#endif + + return m1; +#endif +} + +/* Static `Lemire rejection` function called by + * random_buffered_bounded_uint32(...) */ +static NPY_INLINE uint32_t buffered_bounded_lemire_uint32( + bitgen_t *bitgen_state, uint32_t rng, int *bcnt, uint32_t *buf) { + /* + * Uses Lemire's algorithm - https://arxiv.org/abs/1805.10941 + * + * The buffer and buffer count are not used here but are included to allow + * this function to be templated with the similar uint8 and uint16 + * functions + * + * Note: `rng` should not be 0xFFFFFFFF. When this happens `rng_excl` becomes + * zero. + */ + const uint32_t rng_excl = rng + 1; + + uint64_t m; + uint32_t leftover; + + assert(rng != 0xFFFFFFFFUL); + + /* Generate a scaled random number. */ + m = ((uint64_t)next_uint32(bitgen_state)) * rng_excl; + + /* Rejection sampling to remove any bias */ + leftover = m & 0xFFFFFFFFUL; + + if (leftover < rng_excl) { + /* `rng_excl` is a simple upper bound for `threshold`. */ + const uint32_t threshold = (UINT32_MAX - rng) % rng_excl; + + while (leftover < threshold) { + m = ((uint64_t)next_uint32(bitgen_state)) * rng_excl; + leftover = m & 0xFFFFFFFFUL; + } + } + + return (m >> 32); +} + +/* Static `Lemire rejection` function called by + * random_buffered_bounded_uint16(...) */ +static NPY_INLINE uint16_t buffered_bounded_lemire_uint16( + bitgen_t *bitgen_state, uint16_t rng, int *bcnt, uint32_t *buf) { + /* + * Uses Lemire's algorithm - https://arxiv.org/abs/1805.10941 + * + * Note: `rng` should not be 0xFFFF. When this happens `rng_excl` becomes + * zero. + */ + const uint16_t rng_excl = rng + 1; + + uint32_t m; + uint16_t leftover; + + assert(rng != 0xFFFFU); + + /* Generate a scaled random number. */ + m = ((uint32_t)buffered_uint16(bitgen_state, bcnt, buf)) * rng_excl; + + /* Rejection sampling to remove any bias */ + leftover = m & 0xFFFFUL; + + if (leftover < rng_excl) { + /* `rng_excl` is a simple upper bound for `threshold`. */ + const uint16_t threshold = (UINT16_MAX - rng) % rng_excl; + + while (leftover < threshold) { + m = ((uint32_t)buffered_uint16(bitgen_state, bcnt, buf)) * rng_excl; + leftover = m & 0xFFFFUL; + } + } + + return (m >> 16); +} + +/* Static `Lemire rejection` function called by + * random_buffered_bounded_uint8(...) */ +static NPY_INLINE uint8_t buffered_bounded_lemire_uint8(bitgen_t *bitgen_state, + uint8_t rng, int *bcnt, + uint32_t *buf) { + /* + * Uses Lemire's algorithm - https://arxiv.org/abs/1805.10941 + * + * Note: `rng` should not be 0xFF. When this happens `rng_excl` becomes + * zero. + */ + const uint8_t rng_excl = rng + 1; + + uint16_t m; + uint8_t leftover; + + assert(rng != 0xFFU); + + + /* Generate a scaled random number. */ + m = ((uint16_t)buffered_uint8(bitgen_state, bcnt, buf)) * rng_excl; + + /* Rejection sampling to remove any bias */ + leftover = m & 0xFFUL; + + if (leftover < rng_excl) { + /* `rng_excl` is a simple upper bound for `threshold`. */ + const uint8_t threshold = (UINT8_MAX - rng) % rng_excl; + + while (leftover < threshold) { + m = ((uint16_t)buffered_uint8(bitgen_state, bcnt, buf)) * rng_excl; + leftover = m & 0xFFUL; + } + } + + return (m >> 8); +} + +/* + * Returns a single random npy_uint64 between off and off + rng + * inclusive. The numbers wrap if rng is sufficiently large. + */ +uint64_t random_bounded_uint64(bitgen_t *bitgen_state, uint64_t off, + uint64_t rng, uint64_t mask, bool use_masked) { + if (rng == 0) { + return off; + } else if (rng <= 0xFFFFFFFFUL) { + /* Call 32-bit generator if range in 32-bit. */ + if (rng == 0xFFFFFFFFUL) { + /* + * The 32-bit Lemire method does not handle rng=0xFFFFFFFF, so we'll + * call next_uint32 directly. This also works when use_masked is True, + * so we handle both cases here. + */ + return off + (uint64_t) next_uint32(bitgen_state); + } + if (use_masked) { + return off + buffered_bounded_masked_uint32(bitgen_state, rng, mask, NULL, + NULL); + } else { + return off + + buffered_bounded_lemire_uint32(bitgen_state, rng, NULL, NULL); + } + } else if (rng == 0xFFFFFFFFFFFFFFFFULL) { + /* Lemire64 doesn't support inclusive rng = 0xFFFFFFFFFFFFFFFF. */ + return off + next_uint64(bitgen_state); + } else { + if (use_masked) { + return off + bounded_masked_uint64(bitgen_state, rng, mask); + } else { + return off + bounded_lemire_uint64(bitgen_state, rng); + } + } +} + +/* + * Returns a single random npy_uint64 between off and off + rng + * inclusive. The numbers wrap if rng is sufficiently large. + */ +uint32_t random_buffered_bounded_uint32(bitgen_t *bitgen_state, uint32_t off, + uint32_t rng, uint32_t mask, + bool use_masked, int *bcnt, + uint32_t *buf) { + /* + * Unused bcnt and buf are here only to allow templating with other uint + * generators. + */ + if (rng == 0) { + return off; + } else if (rng == 0xFFFFFFFFUL) { + /* Lemire32 doesn't support inclusive rng = 0xFFFFFFFF. */ + return off + next_uint32(bitgen_state); + } else { + if (use_masked) { + return off + + buffered_bounded_masked_uint32(bitgen_state, rng, mask, bcnt, buf); + } else { + return off + buffered_bounded_lemire_uint32(bitgen_state, rng, bcnt, buf); + } + } +} + +/* + * Returns a single random npy_uint16 between off and off + rng + * inclusive. The numbers wrap if rng is sufficiently large. + */ +uint16_t random_buffered_bounded_uint16(bitgen_t *bitgen_state, uint16_t off, + uint16_t rng, uint16_t mask, + bool use_masked, int *bcnt, + uint32_t *buf) { + if (rng == 0) { + return off; + } else if (rng == 0xFFFFUL) { + /* Lemire16 doesn't support inclusive rng = 0xFFFF. */ + return off + buffered_uint16(bitgen_state, bcnt, buf); + } else { + if (use_masked) { + return off + + buffered_bounded_masked_uint16(bitgen_state, rng, mask, bcnt, buf); + } else { + return off + buffered_bounded_lemire_uint16(bitgen_state, rng, bcnt, buf); + } + } +} + +/* + * Returns a single random npy_uint8 between off and off + rng + * inclusive. The numbers wrap if rng is sufficiently large. + */ +uint8_t random_buffered_bounded_uint8(bitgen_t *bitgen_state, uint8_t off, + uint8_t rng, uint8_t mask, + bool use_masked, int *bcnt, + uint32_t *buf) { + if (rng == 0) { + return off; + } else if (rng == 0xFFUL) { + /* Lemire8 doesn't support inclusive rng = 0xFF. */ + return off + buffered_uint8(bitgen_state, bcnt, buf); + } else { + if (use_masked) { + return off + + buffered_bounded_masked_uint8(bitgen_state, rng, mask, bcnt, buf); + } else { + return off + buffered_bounded_lemire_uint8(bitgen_state, rng, bcnt, buf); + } + } +} + +npy_bool random_buffered_bounded_bool(bitgen_t *bitgen_state, npy_bool off, + npy_bool rng, npy_bool mask, + bool use_masked, int *bcnt, + uint32_t *buf) { + return buffered_bounded_bool(bitgen_state, off, rng, mask, bcnt, buf); +} + +/* + * Fills an array with cnt random npy_uint64 between off and off + rng + * inclusive. The numbers wrap if rng is sufficiently large. + */ +void random_bounded_uint64_fill(bitgen_t *bitgen_state, uint64_t off, + uint64_t rng, npy_intp cnt, bool use_masked, + uint64_t *out) { + npy_intp i; + + if (rng == 0) { + for (i = 0; i < cnt; i++) { + out[i] = off; + } + } else if (rng <= 0xFFFFFFFFUL) { + /* Call 32-bit generator if range in 32-bit. */ + + /* + * The 32-bit Lemire method does not handle rng=0xFFFFFFFF, so we'll + * call next_uint32 directly. This also works when use_masked is True, + * so we handle both cases here. + */ + if (rng == 0xFFFFFFFFUL) { + for (i = 0; i < cnt; i++) { + out[i] = off + (uint64_t) next_uint32(bitgen_state); + } + } else { + uint32_t buf = 0; + int bcnt = 0; + + if (use_masked) { + /* Smallest bit mask >= max */ + uint64_t mask = gen_mask(rng); + + for (i = 0; i < cnt; i++) { + out[i] = off + buffered_bounded_masked_uint32(bitgen_state, rng, mask, + &bcnt, &buf); + } + } else { + for (i = 0; i < cnt; i++) { + out[i] = off + + buffered_bounded_lemire_uint32(bitgen_state, rng, &bcnt, &buf); + } + } + } + } else if (rng == 0xFFFFFFFFFFFFFFFFULL) { + /* Lemire64 doesn't support rng = 0xFFFFFFFFFFFFFFFF. */ + for (i = 0; i < cnt; i++) { + out[i] = off + next_uint64(bitgen_state); + } + } else { + if (use_masked) { + /* Smallest bit mask >= max */ + uint64_t mask = gen_mask(rng); + + for (i = 0; i < cnt; i++) { + out[i] = off + bounded_masked_uint64(bitgen_state, rng, mask); + } + } else { + for (i = 0; i < cnt; i++) { + out[i] = off + bounded_lemire_uint64(bitgen_state, rng); + } + } + } +} + +/* + * Fills an array with cnt random npy_uint32 between off and off + rng + * inclusive. The numbers wrap if rng is sufficiently large. + */ +void random_bounded_uint32_fill(bitgen_t *bitgen_state, uint32_t off, + uint32_t rng, npy_intp cnt, bool use_masked, + uint32_t *out) { + npy_intp i; + uint32_t buf = 0; + int bcnt = 0; + + if (rng == 0) { + for (i = 0; i < cnt; i++) { + out[i] = off; + } + } else if (rng == 0xFFFFFFFFUL) { + /* Lemire32 doesn't support rng = 0xFFFFFFFF. */ + for (i = 0; i < cnt; i++) { + out[i] = off + next_uint32(bitgen_state); + } + } else { + if (use_masked) { + /* Smallest bit mask >= max */ + uint32_t mask = (uint32_t)gen_mask(rng); + + for (i = 0; i < cnt; i++) { + out[i] = off + buffered_bounded_masked_uint32(bitgen_state, rng, mask, + &bcnt, &buf); + } + } else { + for (i = 0; i < cnt; i++) { + out[i] = off + + buffered_bounded_lemire_uint32(bitgen_state, rng, &bcnt, &buf); + } + } + } +} + +/* + * Fills an array with cnt random npy_uint16 between off and off + rng + * inclusive. The numbers wrap if rng is sufficiently large. + */ +void random_bounded_uint16_fill(bitgen_t *bitgen_state, uint16_t off, + uint16_t rng, npy_intp cnt, bool use_masked, + uint16_t *out) { + npy_intp i; + uint32_t buf = 0; + int bcnt = 0; + + if (rng == 0) { + for (i = 0; i < cnt; i++) { + out[i] = off; + } + } else if (rng == 0xFFFFUL) { + /* Lemire16 doesn't support rng = 0xFFFF. */ + for (i = 0; i < cnt; i++) { + out[i] = off + buffered_uint16(bitgen_state, &bcnt, &buf); + } + } else { + if (use_masked) { + /* Smallest bit mask >= max */ + uint16_t mask = (uint16_t)gen_mask(rng); + + for (i = 0; i < cnt; i++) { + out[i] = off + buffered_bounded_masked_uint16(bitgen_state, rng, mask, + &bcnt, &buf); + } + } else { + for (i = 0; i < cnt; i++) { + out[i] = off + + buffered_bounded_lemire_uint16(bitgen_state, rng, &bcnt, &buf); + } + } + } +} + +/* + * Fills an array with cnt random npy_uint8 between off and off + rng + * inclusive. The numbers wrap if rng is sufficiently large. + */ +void random_bounded_uint8_fill(bitgen_t *bitgen_state, uint8_t off, uint8_t rng, + npy_intp cnt, bool use_masked, uint8_t *out) { + npy_intp i; + uint32_t buf = 0; + int bcnt = 0; + + if (rng == 0) { + for (i = 0; i < cnt; i++) { + out[i] = off; + } + } else if (rng == 0xFFUL) { + /* Lemire8 doesn't support rng = 0xFF. */ + for (i = 0; i < cnt; i++) { + out[i] = off + buffered_uint8(bitgen_state, &bcnt, &buf); + } + } else { + if (use_masked) { + /* Smallest bit mask >= max */ + uint8_t mask = (uint8_t)gen_mask(rng); + + for (i = 0; i < cnt; i++) { + out[i] = off + buffered_bounded_masked_uint8(bitgen_state, rng, mask, + &bcnt, &buf); + } + } else { + for (i = 0; i < cnt; i++) { + out[i] = + off + buffered_bounded_lemire_uint8(bitgen_state, rng, &bcnt, &buf); + } + } + } +} + +/* + * Fills an array with cnt random npy_bool between off and off + rng + * inclusive. + */ +void random_bounded_bool_fill(bitgen_t *bitgen_state, npy_bool off, + npy_bool rng, npy_intp cnt, bool use_masked, + npy_bool *out) { + npy_bool mask = 0; + npy_intp i; + uint32_t buf = 0; + int bcnt = 0; + + for (i = 0; i < cnt; i++) { + out[i] = buffered_bounded_bool(bitgen_state, off, rng, mask, &bcnt, &buf); + } +} + +void random_multinomial(bitgen_t *bitgen_state, RAND_INT_TYPE n, + RAND_INT_TYPE *mnix, double *pix, npy_intp d, + binomial_t *binomial) { + double remaining_p = 1.0; + npy_intp j; + RAND_INT_TYPE dn = n; + for (j = 0; j < (d - 1); j++) { + mnix[j] = random_binomial(bitgen_state, pix[j] / remaining_p, dn, binomial); + dn = dn - mnix[j]; + if (dn <= 0) { + break; + } + remaining_p -= pix[j]; + } + if (dn > 0) { + mnix[d - 1] = dn; + } +} diff --git a/numpy/random/src/distributions/logfactorial.c b/numpy/random/src/distributions/logfactorial.c new file mode 100644 index 000000000000..1305164699fa --- /dev/null +++ b/numpy/random/src/distributions/logfactorial.c @@ -0,0 +1,158 @@ + +#include <math.h> +#include <stdint.h> + +/* + * logfact[k] holds log(k!) for k = 0, 1, 2, ..., 125. + */ + +static const double logfact[] = { + 0, + 0, + 0.69314718055994529, + 1.791759469228055, + 3.1780538303479458, + 4.7874917427820458, + 6.5792512120101012, + 8.5251613610654147, + 10.604602902745251, + 12.801827480081469, + 15.104412573075516, + 17.502307845873887, + 19.987214495661885, + 22.552163853123425, + 25.19122118273868, + 27.89927138384089, + 30.671860106080672, + 33.505073450136891, + 36.395445208033053, + 39.339884187199495, + 42.335616460753485, + 45.380138898476908, + 48.471181351835227, + 51.606675567764377, + 54.784729398112319, + 58.003605222980518, + 61.261701761002001, + 64.557538627006338, + 67.88974313718154, + 71.257038967168015, + 74.658236348830158, + 78.092223553315307, + 81.557959456115043, + 85.054467017581516, + 88.580827542197682, + 92.136175603687093, + 95.719694542143202, + 99.330612454787428, + 102.96819861451381, + 106.63176026064346, + 110.32063971475739, + 114.03421178146171, + 117.77188139974507, + 121.53308151543864, + 125.3172711493569, + 129.12393363912722, + 132.95257503561632, + 136.80272263732635, + 140.67392364823425, + 144.5657439463449, + 148.47776695177302, + 152.40959258449735, + 156.3608363030788, + 160.3311282166309, + 164.32011226319517, + 168.32744544842765, + 172.35279713916279, + 176.39584840699735, + 180.45629141754378, + 184.53382886144948, + 188.6281734236716, + 192.7390472878449, + 196.86618167289001, + 201.00931639928152, + 205.1681994826412, + 209.34258675253685, + 213.53224149456327, + 217.73693411395422, + 221.95644181913033, + 226.1905483237276, + 230.43904356577696, + 234.70172344281826, + 238.97838956183432, + 243.26884900298271, + 247.57291409618688, + 251.89040220972319, + 256.22113555000954, + 260.56494097186322, + 264.92164979855278, + 269.29109765101981, + 273.67312428569369, + 278.06757344036612, + 282.4742926876304, + 286.89313329542699, + 291.32395009427029, + 295.76660135076065, + 300.22094864701415, + 304.68685676566872, + 309.1641935801469, + 313.65282994987905, + 318.1526396202093, + 322.66349912672615, + 327.1852877037752, + 331.71788719692847, + 336.26118197919845, + 340.81505887079902, + 345.37940706226686, + 349.95411804077025, + 354.53908551944079, + 359.1342053695754, + 363.73937555556347, + 368.35449607240474, + 372.97946888568902, + 377.61419787391867, + 382.25858877306001, + 386.91254912321756, + 391.57598821732961, + 396.24881705179155, + 400.93094827891576, + 405.6222961611449, + 410.32277652693733, + 415.03230672824964, + 419.75080559954472, + 424.47819341825709, + 429.21439186665157, + 433.95932399501481, + 438.71291418612117, + 443.47508812091894, + 448.24577274538461, + 453.02489623849613, + 457.81238798127816, + 462.60817852687489, + 467.4121995716082, + 472.22438392698058, + 477.04466549258564, + 481.87297922988796 +}; + +/* + * Compute log(k!) + */ + +double logfactorial(int64_t k) +{ + const double halfln2pi = 0.9189385332046728; + + if (k < (int64_t) (sizeof(logfact)/sizeof(logfact[0]))) { + /* Use the lookup table. */ + return logfact[k]; + } + + /* + * Use the Stirling series, truncated at the 1/k**3 term. + * (In a Python implementation of this approximation, the result + * was within 2 ULP of the best 64 bit floating point value for + * k up to 10000000.) + */ + return (k + 0.5)*log(k) - k + (halfln2pi + (1.0/k)*(1/12.0 - 1/(360.0*k*k))); +} diff --git a/numpy/random/src/distributions/logfactorial.h b/numpy/random/src/distributions/logfactorial.h new file mode 100644 index 000000000000..1fedef3f6eaa --- /dev/null +++ b/numpy/random/src/distributions/logfactorial.h @@ -0,0 +1,9 @@ + +#ifndef LOGFACTORIAL_H +#define LOGFACTORIAL_H + +#include <stdint.h> + +double logfactorial(int64_t k); + +#endif diff --git a/numpy/random/src/distributions/random_hypergeometric.c b/numpy/random/src/distributions/random_hypergeometric.c new file mode 100644 index 000000000000..d8510bfca9bb --- /dev/null +++ b/numpy/random/src/distributions/random_hypergeometric.c @@ -0,0 +1,260 @@ +#include "numpy/random/distributions.h" +#include "logfactorial.h" +#include <stdint.h> + +/* + * Generate a sample from the hypergeometric distribution. + * + * Assume sample is not greater than half the total. See below + * for how the opposite case is handled. + * + * We initialize the following: + * computed_sample = sample + * remaining_good = good + * remaining_total = good + bad + * + * In the loop: + * * computed_sample counts down to 0; + * * remaining_good is the number of good choices not selected yet; + * * remaining_total is the total number of choices not selected yet. + * + * In the loop, we select items by choosing a random integer in + * the interval [0, remaining_total), and if the value is less + * than remaining_good, it means we have selected a good one, + * so remaining_good is decremented. Then, regardless of that + * result, computed_sample is decremented. The loop continues + * until either computed_sample is 0, remaining_good is 0, or + * remaining_total == remaining_good. In the latter case, it + * means there are only good choices left, so we can stop the + * loop early and select what is left of computed_sample from + * the good choices (i.e. decrease remaining_good by computed_sample). + * + * When the loop exits, the actual number of good choices is + * good - remaining_good. + * + * If sample is more than half the total, then initially we set + * computed_sample = total - sample + * and at the end we return remaining_good (i.e. the loop in effect + * selects the complement of the result). + * + * It is assumed that when this function is called: + * * good, bad and sample are nonnegative; + * * the sum good+bad will not result in overflow; + * * sample <= good+bad. + */ + +static int64_t hypergeometric_sample(bitgen_t *bitgen_state, + int64_t good, int64_t bad, int64_t sample) +{ + int64_t remaining_total, remaining_good, result, computed_sample; + int64_t total = good + bad; + + if (sample > total/2) { + computed_sample = total - sample; + } + else { + computed_sample = sample; + } + + remaining_total = total; + remaining_good = good; + + while ((computed_sample > 0) && (remaining_good > 0) && + (remaining_total > remaining_good)) { + // random_interval(bitgen_state, max) returns an integer in + // [0, max] *inclusive*, so we decrement remaining_total before + // passing it to random_interval(). + --remaining_total; + if ((int64_t) random_interval(bitgen_state, + remaining_total) < remaining_good) { + // Selected a "good" one, so decrement remaining_good. + --remaining_good; + } + --computed_sample; + } + + if (remaining_total == remaining_good) { + // Only "good" choices are left. + remaining_good -= computed_sample; + } + + if (sample > total/2) { + result = remaining_good; + } + else { + result = good - remaining_good; + } + + return result; +} + + +// D1 = 2*sqrt(2/e) +// D2 = 3 - 2*sqrt(3/e) +#define D1 1.7155277699214135 +#define D2 0.8989161620588988 + +/* + * Generate variates from the hypergeometric distribution + * using the ratio-of-uniforms method. + * + * In the code, the variable names a, b, c, g, h, m, p, q, K, T, + * U and X match the names used in "Algorithm HRUA" beginning on + * page 82 of Stadlober's 1989 thesis. + * + * It is assumed that when this function is called: + * * good, bad and sample are nonnegative; + * * the sum good+bad will not result in overflow; + * * sample <= good+bad. + * + * References: + * - Ernst Stadlober's thesis "Sampling from Poisson, Binomial and + * Hypergeometric Distributions: Ratio of Uniforms as a Simple and + * Fast Alternative" (1989) + * - Ernst Stadlober, "The ratio of uniforms approach for generating + * discrete random variates", Journal of Computational and Applied + * Mathematics, 31, pp. 181-189 (1990). + */ + +static int64_t hypergeometric_hrua(bitgen_t *bitgen_state, + int64_t good, int64_t bad, int64_t sample) +{ + int64_t mingoodbad, maxgoodbad, popsize; + int64_t computed_sample; + double p, q; + double mu, var; + double a, c, b, h, g; + int64_t m, K; + + popsize = good + bad; + computed_sample = MIN(sample, popsize - sample); + mingoodbad = MIN(good, bad); + maxgoodbad = MAX(good, bad); + + /* + * Variables that do not match Stadlober (1989) + * Here Stadlober + * ---------------- --------- + * mingoodbad M + * popsize N + * computed_sample n + */ + + p = ((double) mingoodbad) / popsize; + q = ((double) maxgoodbad) / popsize; + + // mu is the mean of the distribution. + mu = computed_sample * p; + + a = mu + 0.5; + + // var is the variance of the distribution. + var = ((double)(popsize - computed_sample) * + computed_sample * p * q / (popsize - 1)); + + c = sqrt(var + 0.5); + + /* + * h is 2*s_hat (See Stadlober's thesis (1989), Eq. (5.17); or + * Stadlober (1990), Eq. 8). s_hat is the scale of the "table mountain" + * function that dominates the scaled hypergeometric PMF ("scaled" means + * normalized to have a maximum value of 1). + */ + h = D1*c + D2; + + m = (int64_t) floor((double)(computed_sample + 1) * (mingoodbad + 1) / + (popsize + 2)); + + g = (logfactorial(m) + + logfactorial(mingoodbad - m) + + logfactorial(computed_sample - m) + + logfactorial(maxgoodbad - computed_sample + m)); + + /* + * b is the upper bound for random samples: + * ... min(computed_sample, mingoodbad) + 1 is the length of the support. + * ... floor(a + 16*c) is 16 standard deviations beyond the mean. + * + * The idea behind the second upper bound is that values that far out in + * the tail have negligible probabilities. + * + * There is a comment in a previous version of this algorithm that says + * "16 for 16-decimal-digit precision in D1 and D2", + * but there is no documented justification for this value. A lower value + * might work just as well, but I've kept the value 16 here. + */ + b = MIN(MIN(computed_sample, mingoodbad) + 1, floor(a + 16*c)); + + while (1) { + double U, V, X, T; + double gp; + U = next_double(bitgen_state); + V = next_double(bitgen_state); // "U star" in Stadlober (1989) + X = a + h*(V - 0.5) / U; + + // fast rejection: + if ((X < 0.0) || (X >= b)) { + continue; + } + + K = (int64_t) floor(X); + + gp = (logfactorial(K) + + logfactorial(mingoodbad - K) + + logfactorial(computed_sample - K) + + logfactorial(maxgoodbad - computed_sample + K)); + + T = g - gp; + + // fast acceptance: + if ((U*(4.0 - U) - 3.0) <= T) { + break; + } + + // fast rejection: + if (U*(U - T) >= 1) { + continue; + } + + if (2.0*log(U) <= T) { + // acceptance + break; + } + } + + if (good > bad) { + K = computed_sample - K; + } + + if (computed_sample < sample) { + K = good - K; + } + + return K; +} + + +/* + * Draw a sample from the hypergeometric distribution. + * + * It is assumed that when this function is called: + * * good, bad and sample are nonnegative; + * * the sum good+bad will not result in overflow; + * * sample <= good+bad. + */ + +int64_t random_hypergeometric(bitgen_t *bitgen_state, + int64_t good, int64_t bad, int64_t sample) +{ + int64_t r; + + if ((sample >= 10) && (sample <= good + bad - 10)) { + // This will use the ratio-of-uniforms method. + r = hypergeometric_hrua(bitgen_state, good, bad, sample); + } + else { + // The simpler implementation is faster for small samples. + r = hypergeometric_sample(bitgen_state, good, bad, sample); + } + return r; +} diff --git a/numpy/random/src/distributions/random_mvhg_count.c b/numpy/random/src/distributions/random_mvhg_count.c new file mode 100644 index 000000000000..1d4ed978ed35 --- /dev/null +++ b/numpy/random/src/distributions/random_mvhg_count.c @@ -0,0 +1,131 @@ +#include "numpy/random/distributions.h" +#include <stdint.h> +#include <stdlib.h> +#include <stdbool.h> + + +/* + * random_multivariate_hypergeometric_count + * + * Draw variates from the multivariate hypergeometric distribution-- + * the "count" algorithm. + * + * Parameters + * ---------- + * bitgen_t *bitgen_state + * Pointer to a `bitgen_t` instance. + * int64_t total + * The sum of the values in the array `colors`. (This is redundant + * information, but we know the caller has already computed it, so + * we might as well use it.) + * size_t num_colors + * The length of the `colors` array. + * int64_t *colors + * The array of colors (i.e. the number of each type in the collection + * from which the random variate is drawn). + * int64_t nsample + * The number of objects drawn without replacement for each variate. + * `nsample` must not exceed sum(colors). This condition is not checked; + * it is assumed that the caller has already validated the value. + * size_t num_variates + * The number of variates to be produced and put in the array + * pointed to by `variates`. One variate is a vector of length + * `num_colors`, so the array pointed to by `variates` must have length + * `num_variates * num_colors`. + * int64_t *variates + * The array that will hold the result. It must have length + * `num_variates * num_colors`. + * The array is not initialized in the function; it is expected that the + * array has been initialized with zeros when the function is called. + * + * Notes + * ----- + * The "count" algorithm for drawing one variate is roughly equivalent to the + * following numpy code: + * + * choices = np.repeat(np.arange(len(colors)), colors) + * selection = np.random.choice(choices, nsample, replace=False) + * variate = np.bincount(selection, minlength=len(colors)) + * + * This function uses a temporary array with length sum(colors). + * + * Assumptions on the arguments (not checked in the function): + * * colors[k] >= 0 for k in range(num_colors) + * * total = sum(colors) + * * 0 <= nsample <= total + * * the product total * sizeof(size_t) does not exceed SIZE_MAX + * * the product num_variates * num_colors does not overflow + */ + +int random_multivariate_hypergeometric_count(bitgen_t *bitgen_state, + int64_t total, + size_t num_colors, int64_t *colors, + int64_t nsample, + size_t num_variates, int64_t *variates) +{ + size_t *choices; + bool more_than_half; + + if ((total == 0) || (nsample == 0) || (num_variates == 0)) { + // Nothing to do. + return 0; + } + + choices = malloc(total * (sizeof *choices)); + if (choices == NULL) { + return -1; + } + + /* + * If colors contains, for example, [3 2 5], then choices + * will contain [0 0 0 1 1 2 2 2 2 2]. + */ + for (size_t i = 0, k = 0; i < num_colors; ++i) { + for (int64_t j = 0; j < colors[i]; ++j) { + choices[k] = i; + ++k; + } + } + + more_than_half = nsample > (total / 2); + if (more_than_half) { + nsample = total - nsample; + } + + for (size_t i = 0; i < num_variates * num_colors; i += num_colors) { + /* + * Fisher-Yates shuffle, but only loop through the first + * `nsample` entries of `choices`. After the loop, + * choices[:nsample] contains a random sample from the + * the full array. + */ + for (size_t j = 0; j < (size_t) nsample; ++j) { + size_t tmp, k; + // Note: nsample is not greater than total, so there is no danger + // of integer underflow in `(size_t) total - j - 1`. + k = j + (size_t) random_interval(bitgen_state, + (size_t) total - j - 1); + tmp = choices[k]; + choices[k] = choices[j]; + choices[j] = tmp; + } + /* + * Count the number of occurrences of each value in choices[:nsample]. + * The result, stored in sample[i:i+num_colors], is the sample from + * the multivariate hypergeometric distribution. + */ + for (size_t j = 0; j < (size_t) nsample; ++j) { + variates[i + choices[j]] += 1; + } + + if (more_than_half) { + for (size_t k = 0; k < num_colors; ++k) { + variates[i + k] = colors[k] - variates[i + k]; + } + } + } + + free(choices); + + return 0; +} diff --git a/numpy/random/src/distributions/random_mvhg_marginals.c b/numpy/random/src/distributions/random_mvhg_marginals.c new file mode 100644 index 000000000000..689a856711b6 --- /dev/null +++ b/numpy/random/src/distributions/random_mvhg_marginals.c @@ -0,0 +1,138 @@ +#include "numpy/random/distributions.h" +#include <stdint.h> +#include <stddef.h> +#include <stdbool.h> +#include <math.h> + +#include "logfactorial.h" + + +/* + * random_multivariate_hypergeometric_marginals + * + * Draw samples from the multivariate hypergeometric distribution-- + * the "marginals" algorithm. + * + * This version generates the sample by iteratively calling + * hypergeometric() (the univariate hypergeometric distribution). + * + * Parameters + * ---------- + * bitgen_t *bitgen_state + * Pointer to a `bitgen_t` instance. + * int64_t total + * The sum of the values in the array `colors`. (This is redundant + * information, but we know the caller has already computed it, so + * we might as well use it.) + * size_t num_colors + * The length of the `colors` array. The functions assumes + * num_colors > 0. + * int64_t *colors + * The array of colors (i.e. the number of each type in the collection + * from which the random variate is drawn). + * int64_t nsample + * The number of objects drawn without replacement for each variate. + * `nsample` must not exceed sum(colors). This condition is not checked; + * it is assumed that the caller has already validated the value. + * size_t num_variates + * The number of variates to be produced and put in the array + * pointed to by `variates`. One variate is a vector of length + * `num_colors`, so the array pointed to by `variates` must have length + * `num_variates * num_colors`. + * int64_t *variates + * The array that will hold the result. It must have length + * `num_variates * num_colors`. + * The array is not initialized in the function; it is expected that the + * array has been initialized with zeros when the function is called. + * + * Notes + * ----- + * Here's an example that demonstrates the idea of this algorithm. + * + * Suppose the urn contains red, green, blue and yellow marbles. + * Let nred be the number of red marbles, and define the quantities for + * the other colors similarly. The total number of marbles is + * + * total = nred + ngreen + nblue + nyellow. + * + * To generate a sample using rk_hypergeometric: + * + * red_sample = hypergeometric(ngood=nred, nbad=total - nred, + * nsample=nsample) + * + * This gives us the number of red marbles in the sample. The number of + * marbles in the sample that are *not* red is nsample - red_sample. + * To figure out the distribution of those marbles, we again use + * rk_hypergeometric: + * + * green_sample = hypergeometric(ngood=ngreen, + * nbad=total - nred - ngreen, + * nsample=nsample - red_sample) + * + * Similarly, + * + * blue_sample = hypergeometric( + * ngood=nblue, + * nbad=total - nred - ngreen - nblue, + * nsample=nsample - red_sample - green_sample) + * + * Finally, + * + * yellow_sample = total - (red_sample + green_sample + blue_sample). + * + * The above sequence of steps is implemented as a loop for an arbitrary + * number of colors in the innermost loop in the code below. `remaining` + * is the value passed to `nbad`; it is `total - colors[0]` in the first + * call to random_hypergeometric(), and then decreases by `colors[j]` in + * each iteration. `num_to_sample` is the `nsample` argument. It + * starts at this function's `nsample` input, and is decreased by the + * result of the call to random_hypergeometric() in each iteration. + * + * Assumptions on the arguments (not checked in the function): + * * colors[k] >= 0 for k in range(num_colors) + * * total = sum(colors) + * * 0 <= nsample <= total + * * the product num_variates * num_colors does not overflow + */ + +void random_multivariate_hypergeometric_marginals(bitgen_t *bitgen_state, + int64_t total, + size_t num_colors, int64_t *colors, + int64_t nsample, + size_t num_variates, int64_t *variates) +{ + bool more_than_half; + + if ((total == 0) || (nsample == 0) || (num_variates == 0)) { + // Nothing to do. + return; + } + + more_than_half = nsample > (total / 2); + if (more_than_half) { + nsample = total - nsample; + } + + for (size_t i = 0; i < num_variates * num_colors; i += num_colors) { + int64_t num_to_sample = nsample; + int64_t remaining = total; + for (size_t j = 0; (num_to_sample > 0) && (j + 1 < num_colors); ++j) { + int64_t r; + remaining -= colors[j]; + r = random_hypergeometric(bitgen_state, + colors[j], remaining, num_to_sample); + variates[i + j] = r; + num_to_sample -= r; + } + + if (num_to_sample > 0) { + variates[i + num_colors - 1] = num_to_sample; + } + + if (more_than_half) { + for (size_t k = 0; k < num_colors; ++k) { + variates[i + k] = colors[k] - variates[i + k]; + } + } + } +} diff --git a/numpy/random/src/distributions/ziggurat_constants.h b/numpy/random/src/distributions/ziggurat_constants.h new file mode 100644 index 000000000000..c254466deb8d --- /dev/null +++ b/numpy/random/src/distributions/ziggurat_constants.h @@ -0,0 +1,1206 @@ +static const uint64_t ki_double[] = { + 0x000EF33D8025EF6AULL, 0x0000000000000000ULL, 0x000C08BE98FBC6A8ULL, + 0x000DA354FABD8142ULL, 0x000E51F67EC1EEEAULL, 0x000EB255E9D3F77EULL, + 0x000EEF4B817ECAB9ULL, 0x000F19470AFA44AAULL, 0x000F37ED61FFCB18ULL, + 0x000F4F469561255CULL, 0x000F61A5E41BA396ULL, 0x000F707A755396A4ULL, + 0x000F7CB2EC28449AULL, 0x000F86F10C6357D3ULL, 0x000F8FA6578325DEULL, + 0x000F9724C74DD0DAULL, 0x000F9DA907DBF509ULL, 0x000FA360F581FA74ULL, + 0x000FA86FDE5B4BF8ULL, 0x000FACF160D354DCULL, 0x000FB0FB6718B90FULL, + 0x000FB49F8D5374C6ULL, 0x000FB7EC2366FE77ULL, 0x000FBAECE9A1E50EULL, + 0x000FBDAB9D040BEDULL, 0x000FC03060FF6C57ULL, 0x000FC2821037A248ULL, + 0x000FC4A67AE25BD1ULL, 0x000FC6A2977AEE31ULL, 0x000FC87AA92896A4ULL, + 0x000FCA325E4BDE85ULL, 0x000FCBCCE902231AULL, 0x000FCD4D12F839C4ULL, + 0x000FCEB54D8FEC99ULL, 0x000FD007BF1DC930ULL, 0x000FD1464DD6C4E6ULL, + 0x000FD272A8E2F450ULL, 0x000FD38E4FF0C91EULL, 0x000FD49A9990B478ULL, + 0x000FD598B8920F53ULL, 0x000FD689C08E99ECULL, 0x000FD76EA9C8E832ULL, + 0x000FD848547B08E8ULL, 0x000FD9178BAD2C8CULL, 0x000FD9DD07A7ADD2ULL, + 0x000FDA9970105E8CULL, 0x000FDB4D5DC02E20ULL, 0x000FDBF95C5BFCD0ULL, + 0x000FDC9DEBB99A7DULL, 0x000FDD3B8118729DULL, 0x000FDDD288342F90ULL, + 0x000FDE6364369F64ULL, 0x000FDEEE708D514EULL, 0x000FDF7401A6B42EULL, + 0x000FDFF46599ED40ULL, 0x000FE06FE4BC24F2ULL, 0x000FE0E6C225A258ULL, + 0x000FE1593C28B84CULL, 0x000FE1C78CBC3F99ULL, 0x000FE231E9DB1CAAULL, + 0x000FE29885DA1B91ULL, 0x000FE2FB8FB54186ULL, 0x000FE35B33558D4AULL, + 0x000FE3B799D0002AULL, 0x000FE410E99EAD7FULL, 0x000FE46746D47734ULL, + 0x000FE4BAD34C095CULL, 0x000FE50BAED29524ULL, 0x000FE559F74EBC78ULL, + 0x000FE5A5C8E41212ULL, 0x000FE5EF3E138689ULL, 0x000FE6366FD91078ULL, + 0x000FE67B75C6D578ULL, 0x000FE6BE661E11AAULL, 0x000FE6FF55E5F4F2ULL, + 0x000FE73E5900A702ULL, 0x000FE77B823E9E39ULL, 0x000FE7B6E37070A2ULL, + 0x000FE7F08D774243ULL, 0x000FE8289053F08CULL, 0x000FE85EFB35173AULL, + 0x000FE893DC840864ULL, 0x000FE8C741F0CEBCULL, 0x000FE8F9387D4EF6ULL, + 0x000FE929CC879B1DULL, 0x000FE95909D388EAULL, 0x000FE986FB939AA2ULL, + 0x000FE9B3AC714866ULL, 0x000FE9DF2694B6D5ULL, 0x000FEA0973ABE67CULL, + 0x000FEA329CF166A4ULL, 0x000FEA5AAB32952CULL, 0x000FEA81A6D5741AULL, + 0x000FEAA797DE1CF0ULL, 0x000FEACC85F3D920ULL, 0x000FEAF07865E63CULL, + 0x000FEB13762FEC13ULL, 0x000FEB3585FE2A4AULL, 0x000FEB56AE3162B4ULL, + 0x000FEB76F4E284FAULL, 0x000FEB965FE62014ULL, 0x000FEBB4F4CF9D7CULL, + 0x000FEBD2B8F449D0ULL, 0x000FEBEFB16E2E3EULL, 0x000FEC0BE31EBDE8ULL, + 0x000FEC2752B15A15ULL, 0x000FEC42049DAFD3ULL, 0x000FEC5BFD29F196ULL, + 0x000FEC75406CEEF4ULL, 0x000FEC8DD2500CB4ULL, 0x000FECA5B6911F12ULL, + 0x000FECBCF0C427FEULL, 0x000FECD38454FB15ULL, 0x000FECE97488C8B3ULL, + 0x000FECFEC47F91B7ULL, 0x000FED1377358528ULL, 0x000FED278F844903ULL, + 0x000FED3B10242F4CULL, 0x000FED4DFBAD586EULL, 0x000FED605498C3DDULL, + 0x000FED721D414FE8ULL, 0x000FED8357E4A982ULL, 0x000FED9406A42CC8ULL, + 0x000FEDA42B85B704ULL, 0x000FEDB3C8746AB4ULL, 0x000FEDC2DF416652ULL, + 0x000FEDD171A46E52ULL, 0x000FEDDF813C8AD3ULL, 0x000FEDED0F909980ULL, + 0x000FEDFA1E0FD414ULL, 0x000FEE06AE124BC4ULL, 0x000FEE12C0D95A06ULL, + 0x000FEE1E579006E0ULL, 0x000FEE29734B6524ULL, 0x000FEE34150AE4BCULL, + 0x000FEE3E3DB89B3CULL, 0x000FEE47EE2982F4ULL, 0x000FEE51271DB086ULL, + 0x000FEE59E9407F41ULL, 0x000FEE623528B42EULL, 0x000FEE6A0B5897F1ULL, + 0x000FEE716C3E077AULL, 0x000FEE7858327B82ULL, 0x000FEE7ECF7B06BAULL, + 0x000FEE84D2484AB2ULL, 0x000FEE8A60B66343ULL, 0x000FEE8F7ACCC851ULL, + 0x000FEE94207E25DAULL, 0x000FEE9851A829EAULL, 0x000FEE9C0E13485CULL, + 0x000FEE9F557273F4ULL, 0x000FEEA22762CCAEULL, 0x000FEEA4836B42ACULL, + 0x000FEEA668FC2D71ULL, 0x000FEEA7D76ED6FAULL, 0x000FEEA8CE04FA0AULL, + 0x000FEEA94BE8333BULL, 0x000FEEA950296410ULL, 0x000FEEA8D9C0075EULL, + 0x000FEEA7E7897654ULL, 0x000FEEA678481D24ULL, 0x000FEEA48AA29E83ULL, + 0x000FEEA21D22E4DAULL, 0x000FEE9F2E352024ULL, 0x000FEE9BBC26AF2EULL, + 0x000FEE97C524F2E4ULL, 0x000FEE93473C0A3AULL, 0x000FEE8E40557516ULL, + 0x000FEE88AE369C7AULL, 0x000FEE828E7F3DFDULL, 0x000FEE7BDEA7B888ULL, + 0x000FEE749BFF37FFULL, 0x000FEE6CC3A9BD5EULL, 0x000FEE64529E007EULL, + 0x000FEE5B45A32888ULL, 0x000FEE51994E57B6ULL, 0x000FEE474A0006CFULL, + 0x000FEE3C53E12C50ULL, 0x000FEE30B2E02AD8ULL, 0x000FEE2462AD8205ULL, + 0x000FEE175EB83C5AULL, 0x000FEE09A22A1447ULL, 0x000FEDFB27E349CCULL, + 0x000FEDEBEA76216CULL, 0x000FEDDBE422047EULL, 0x000FEDCB0ECE39D3ULL, + 0x000FEDB964042CF4ULL, 0x000FEDA6DCE938C9ULL, 0x000FED937237E98DULL, + 0x000FED7F1C38A836ULL, 0x000FED69D2B9C02BULL, 0x000FED538D06AE00ULL, + 0x000FED3C41DEA422ULL, 0x000FED23E76A2FD8ULL, 0x000FED0A732FE644ULL, + 0x000FECEFDA07FE34ULL, 0x000FECD4100EB7B8ULL, 0x000FECB708956EB4ULL, + 0x000FEC98B61230C1ULL, 0x000FEC790A0DA978ULL, 0x000FEC57F50F31FEULL, + 0x000FEC356686C962ULL, 0x000FEC114CB4B335ULL, 0x000FEBEB948E6FD0ULL, + 0x000FEBC429A0B692ULL, 0x000FEB9AF5EE0CDCULL, 0x000FEB6FE1C98542ULL, + 0x000FEB42D3AD1F9EULL, 0x000FEB13B00B2D4BULL, 0x000FEAE2591A02E9ULL, + 0x000FEAAEAE992257ULL, 0x000FEA788D8EE326ULL, 0x000FEA3FCFFD73E5ULL, + 0x000FEA044C8DD9F6ULL, 0x000FE9C5D62F563BULL, 0x000FE9843BA947A4ULL, + 0x000FE93F471D4728ULL, 0x000FE8F6BD76C5D6ULL, 0x000FE8AA5DC4E8E6ULL, + 0x000FE859E07AB1EAULL, 0x000FE804F690A940ULL, 0x000FE7AB488233C0ULL, + 0x000FE74C751F6AA5ULL, 0x000FE6E8102AA202ULL, 0x000FE67DA0B6ABD8ULL, + 0x000FE60C9F38307EULL, 0x000FE5947338F742ULL, 0x000FE51470977280ULL, + 0x000FE48BD436F458ULL, 0x000FE3F9BFFD1E37ULL, 0x000FE35D35EEB19CULL, + 0x000FE2B5122FE4FEULL, 0x000FE20003995557ULL, 0x000FE13C82788314ULL, + 0x000FE068C4EE67B0ULL, 0x000FDF82B02B71AAULL, 0x000FDE87C57EFEAAULL, + 0x000FDD7509C63BFDULL, 0x000FDC46E529BF13ULL, 0x000FDAF8F82E0282ULL, + 0x000FD985E1B2BA75ULL, 0x000FD7E6EF48CF04ULL, 0x000FD613ADBD650BULL, + 0x000FD40149E2F012ULL, 0x000FD1A1A7B4C7ACULL, 0x000FCEE204761F9EULL, + 0x000FCBA8D85E11B2ULL, 0x000FC7D26ECD2D22ULL, 0x000FC32B2F1E22EDULL, + 0x000FBD6581C0B83AULL, 0x000FB606C4005434ULL, 0x000FAC40582A2874ULL, + 0x000F9E971E014598ULL, 0x000F89FA48A41DFCULL, 0x000F66C5F7F0302CULL, + 0x000F1A5A4B331C4AULL}; + +static const double wi_double[] = { + 8.68362706080130616677e-16, 4.77933017572773682428e-17, + 6.35435241740526230246e-17, 7.45487048124769627714e-17, + 8.32936681579309972857e-17, 9.06806040505948228243e-17, + 9.71486007656776183958e-17, 1.02947503142410192108e-16, + 1.08234302884476839838e-16, 1.13114701961090307945e-16, + 1.17663594570229211411e-16, 1.21936172787143633280e-16, + 1.25974399146370927864e-16, 1.29810998862640315416e-16, + 1.33472037368241227547e-16, 1.36978648425712032797e-16, + 1.40348230012423820659e-16, 1.43595294520569430270e-16, + 1.46732087423644219083e-16, 1.49769046683910367425e-16, + 1.52715150035961979750e-16, 1.55578181694607639484e-16, + 1.58364940092908853989e-16, 1.61081401752749279325e-16, + 1.63732852039698532012e-16, 1.66323990584208352778e-16, + 1.68859017086765964015e-16, 1.71341701765596607184e-16, + 1.73775443658648593310e-16, 1.76163319230009959832e-16, + 1.78508123169767272927e-16, 1.80812402857991522674e-16, + 1.83078487648267501776e-16, 1.85308513886180189386e-16, + 1.87504446393738816849e-16, 1.89668097007747596212e-16, + 1.91801140648386198029e-16, 1.93905129306251037069e-16, + 1.95981504266288244037e-16, 1.98031606831281739736e-16, + 2.00056687762733300198e-16, 2.02057915620716538808e-16, + 2.04036384154802118313e-16, 2.05993118874037063144e-16, + 2.07929082904140197311e-16, 2.09845182223703516690e-16, + 2.11742270357603418769e-16, 2.13621152594498681022e-16, + 2.15482589785814580926e-16, 2.17327301775643674990e-16, + 2.19155970504272708519e-16, 2.20969242822353175995e-16, + 2.22767733047895534948e-16, 2.24552025294143552381e-16, + 2.26322675592856786566e-16, 2.28080213834501706782e-16, + 2.29825145544246839061e-16, 2.31557953510408037008e-16, + 2.33279099280043561128e-16, 2.34989024534709550938e-16, + 2.36688152357916037468e-16, 2.38376888404542434981e-16, + 2.40055621981350627349e-16, 2.41724727046750252175e-16, + 2.43384563137110286400e-16, 2.45035476226149539878e-16, + 2.46677799523270498158e-16, 2.48311854216108767769e-16, + 2.49937950162045242375e-16, 2.51556386532965786439e-16, + 2.53167452417135826983e-16, 2.54771427381694417303e-16, + 2.56368581998939683749e-16, 2.57959178339286723500e-16, + 2.59543470433517070146e-16, 2.61121704706701939097e-16, + 2.62694120385972564623e-16, 2.64260949884118951286e-16, + 2.65822419160830680292e-16, 2.67378748063236329361e-16, + 2.68930150647261591777e-16, 2.70476835481199518794e-16, + 2.72019005932773206655e-16, 2.73556860440867908686e-16, + 2.75090592773016664571e-16, 2.76620392269639032183e-16, + 2.78146444075954410103e-16, 2.79668929362423005309e-16, + 2.81188025534502074329e-16, 2.82703906432447923059e-16, + 2.84216742521840606520e-16, 2.85726701075460149289e-16, + 2.87233946347097994381e-16, 2.88738639737848191815e-16, + 2.90240939955384233230e-16, 2.91741003166694553259e-16, + 2.93238983144718163965e-16, 2.94735031409293489611e-16, + 2.96229297362806647792e-16, 2.97721928420902891115e-16, + 2.99213070138601307081e-16, 3.00702866332133102993e-16, + 3.02191459196806151971e-16, 3.03678989421180184427e-16, + 3.05165596297821922381e-16, 3.06651417830895451744e-16, + 3.08136590840829717032e-16, 3.09621251066292253306e-16, + 3.11105533263689296831e-16, 3.12589571304399892784e-16, + 3.14073498269944617203e-16, 3.15557446545280064031e-16, + 3.17041547910402852545e-16, 3.18525933630440648871e-16, + 3.20010734544401137886e-16, 3.21496081152744704901e-16, + 3.22982103703941557538e-16, 3.24468932280169778077e-16, + 3.25956696882307838340e-16, 3.27445527514370671802e-16, + 3.28935554267536967851e-16, 3.30426907403912838589e-16, + 3.31919717440175233652e-16, 3.33414115231237245918e-16, + 3.34910232054077845412e-16, 3.36408199691876507948e-16, + 3.37908150518594979994e-16, 3.39410217584148914282e-16, + 3.40914534700312603713e-16, 3.42421236527501816058e-16, + 3.43930458662583133920e-16, 3.45442337727858401604e-16, + 3.46957011461378353333e-16, 3.48474618808741370700e-16, + 3.49995300016538099813e-16, 3.51519196727607440975e-16, + 3.53046452078274009054e-16, 3.54577210797743572160e-16, + 3.56111619309838843415e-16, 3.57649825837265051035e-16, + 3.59191980508602994994e-16, 3.60738235468235137839e-16, + 3.62288744989419151904e-16, 3.63843665590734438546e-16, + 3.65403156156136995766e-16, 3.66967378058870090021e-16, + 3.68536495289491401456e-16, 3.70110674588289834952e-16, + 3.71690085582382297792e-16, 3.73274900927794352614e-16, + 3.74865296456848868882e-16, 3.76461451331202869131e-16, + 3.78063548200896037651e-16, 3.79671773369794425924e-16, + 3.81286316967837738238e-16, 3.82907373130524317507e-16, + 3.84535140186095955858e-16, 3.86169820850914927119e-16, + 3.87811622433558721164e-16, 3.89460757048192620674e-16, + 3.91117441837820542060e-16, 3.92781899208054153270e-16, + 3.94454357072087711446e-16, 3.96135049107613542983e-16, + 3.97824215026468259474e-16, 3.99522100857856502444e-16, + 4.01228959246062907451e-16, 4.02945049763632792393e-16, + 4.04670639241074995115e-16, 4.06406002114225038723e-16, + 4.08151420790493873480e-16, 4.09907186035326643447e-16, + 4.11673597380302570170e-16, 4.13450963554423599878e-16, + 4.15239602940268833891e-16, 4.17039844056831587498e-16, + 4.18852026071011229572e-16, 4.20676499339901510978e-16, + 4.22513625986204937320e-16, 4.24363780509307796137e-16, + 4.26227350434779809917e-16, 4.28104737005311666397e-16, + 4.29996355916383230161e-16, 4.31902638100262944617e-16, + 4.33824030562279080411e-16, 4.35760997273684900553e-16, + 4.37714020125858747008e-16, 4.39683599951052137423e-16, + 4.41670257615420348435e-16, 4.43674535190656726604e-16, + 4.45696997211204306674e-16, 4.47738232024753387312e-16, + 4.49798853244554968009e-16, 4.51879501313005876278e-16, + 4.53980845187003400947e-16, 4.56103584156742206384e-16, + 4.58248449810956667052e-16, 4.60416208163115281428e-16, + 4.62607661954784567754e-16, 4.64823653154320737780e-16, + 4.67065065671263059081e-16, 4.69332828309332890697e-16, + 4.71627917983835129766e-16, 4.73951363232586715165e-16, + 4.76304248053313737663e-16, 4.78687716104872284247e-16, + 4.81102975314741720538e-16, 4.83551302941152515162e-16, + 4.86034051145081195402e-16, 4.88552653135360343280e-16, + 4.91108629959526955862e-16, 4.93703598024033454728e-16, + 4.96339277440398725619e-16, 4.99017501309182245754e-16, + 5.01740226071808946011e-16, 5.04509543081872748637e-16, + 5.07327691573354207058e-16, 5.10197073234156184149e-16, + 5.13120268630678373200e-16, 5.16100055774322824569e-16, + 5.19139431175769859873e-16, 5.22241633800023428760e-16, + 5.25410172417759732697e-16, 5.28648856950494511482e-16, + 5.31961834533840037535e-16, 5.35353631181649688145e-16, + 5.38829200133405320160e-16, 5.42393978220171234073e-16, + 5.46053951907478041166e-16, 5.49815735089281410703e-16, + 5.53686661246787600374e-16, 5.57674893292657647836e-16, + 5.61789555355541665830e-16, 5.66040892008242216739e-16, + 5.70440462129138908417e-16, 5.75001376891989523684e-16, + 5.79738594572459365014e-16, 5.84669289345547900201e-16, + 5.89813317647789942685e-16, 5.95193814964144415532e-16, + 6.00837969627190832234e-16, 6.06778040933344851394e-16, + 6.13052720872528159123e-16, 6.19708989458162555387e-16, + 6.26804696330128439415e-16, 6.34412240712750598627e-16, + 6.42623965954805540945e-16, 6.51560331734499356881e-16, + 6.61382788509766415145e-16, 6.72315046250558662913e-16, + 6.84680341756425875856e-16, 6.98971833638761995415e-16, + 7.15999493483066421560e-16, 7.37242430179879890722e-16, + 7.65893637080557275482e-16, 8.11384933765648418565e-16}; + +static const double fi_double[] = { + 1.00000000000000000000e+00, 9.77101701267671596263e-01, + 9.59879091800106665211e-01, 9.45198953442299649730e-01, + 9.32060075959230460718e-01, 9.19991505039347012840e-01, + 9.08726440052130879366e-01, 8.98095921898343418910e-01, + 8.87984660755833377088e-01, 8.78309655808917399966e-01, + 8.69008688036857046555e-01, 8.60033621196331532488e-01, + 8.51346258458677951353e-01, 8.42915653112204177333e-01, + 8.34716292986883434679e-01, 8.26726833946221373317e-01, + 8.18929191603702366642e-01, 8.11307874312656274185e-01, + 8.03849483170964274059e-01, 7.96542330422958966274e-01, + 7.89376143566024590648e-01, 7.82341832654802504798e-01, + 7.75431304981187174974e-01, 7.68637315798486264740e-01, + 7.61953346836795386565e-01, 7.55373506507096115214e-01, + 7.48892447219156820459e-01, 7.42505296340151055290e-01, + 7.36207598126862650112e-01, 7.29995264561476231435e-01, + 7.23864533468630222401e-01, 7.17811932630721960535e-01, + 7.11834248878248421200e-01, 7.05928501332754310127e-01, + 7.00091918136511615067e-01, 6.94321916126116711609e-01, + 6.88616083004671808432e-01, 6.82972161644994857355e-01, + 6.77388036218773526009e-01, 6.71861719897082099173e-01, + 6.66391343908750100056e-01, 6.60975147776663107813e-01, + 6.55611470579697264149e-01, 6.50298743110816701574e-01, + 6.45035480820822293424e-01, 6.39820277453056585060e-01, + 6.34651799287623608059e-01, 6.29528779924836690007e-01, + 6.24450015547026504592e-01, 6.19414360605834324325e-01, + 6.14420723888913888899e-01, 6.09468064925773433949e-01, + 6.04555390697467776029e-01, 5.99681752619125263415e-01, + 5.94846243767987448159e-01, 5.90047996332826008015e-01, + 5.85286179263371453274e-01, 5.80559996100790898232e-01, + 5.75868682972353718164e-01, 5.71211506735253227163e-01, + 5.66587763256164445025e-01, 5.61996775814524340831e-01, + 5.57437893618765945014e-01, 5.52910490425832290562e-01, + 5.48413963255265812791e-01, 5.43947731190026262382e-01, + 5.39511234256952132426e-01, 5.35103932380457614215e-01, + 5.30725304403662057062e-01, 5.26374847171684479008e-01, + 5.22052074672321841931e-01, 5.17756517229756352272e-01, + 5.13487720747326958914e-01, 5.09245245995747941592e-01, + 5.05028667943468123624e-01, 5.00837575126148681903e-01, + 4.96671569052489714213e-01, 4.92530263643868537748e-01, + 4.88413284705458028423e-01, 4.84320269426683325253e-01, + 4.80250865909046753544e-01, 4.76204732719505863248e-01, + 4.72181538467730199660e-01, 4.68180961405693596422e-01, + 4.64202689048174355069e-01, 4.60246417812842867345e-01, + 4.56311852678716434184e-01, 4.52398706861848520777e-01, + 4.48506701507203064949e-01, 4.44635565395739396077e-01, + 4.40785034665803987508e-01, 4.36954852547985550526e-01, + 4.33144769112652261445e-01, 4.29354541029441427735e-01, + 4.25583931338021970170e-01, 4.21832709229495894654e-01, + 4.18100649837848226120e-01, 4.14387534040891125642e-01, + 4.10693148270188157500e-01, 4.07017284329473372217e-01, + 4.03359739221114510510e-01, 3.99720314980197222177e-01, + 3.96098818515832451492e-01, 3.92495061459315619512e-01, + 3.88908860018788715696e-01, 3.85340034840077283462e-01, + 3.81788410873393657674e-01, 3.78253817245619183840e-01, + 3.74736087137891138443e-01, 3.71235057668239498696e-01, + 3.67750569779032587814e-01, 3.64282468129004055601e-01, + 3.60830600989648031529e-01, 3.57394820145780500731e-01, + 3.53974980800076777232e-01, 3.50570941481406106455e-01, + 3.47182563956793643900e-01, 3.43809713146850715049e-01, + 3.40452257044521866547e-01, 3.37110066637006045021e-01, + 3.33783015830718454708e-01, 3.30470981379163586400e-01, + 3.27173842813601400970e-01, 3.23891482376391093290e-01, + 3.20623784956905355514e-01, 3.17370638029913609834e-01, + 3.14131931596337177215e-01, 3.10907558126286509559e-01, + 3.07697412504292056035e-01, 3.04501391976649993243e-01, + 3.01319396100803049698e-01, 2.98151326696685481377e-01, + 2.94997087799961810184e-01, 2.91856585617095209972e-01, + 2.88729728482182923521e-01, 2.85616426815501756042e-01, + 2.82516593083707578948e-01, 2.79430141761637940157e-01, + 2.76356989295668320494e-01, 2.73297054068577072172e-01, + 2.70250256365875463072e-01, 2.67216518343561471038e-01, + 2.64195763997261190426e-01, 2.61187919132721213522e-01, + 2.58192911337619235290e-01, 2.55210669954661961700e-01, + 2.52241126055942177508e-01, 2.49284212418528522415e-01, + 2.46339863501263828249e-01, 2.43408015422750312329e-01, + 2.40488605940500588254e-01, 2.37581574431238090606e-01, + 2.34686861872330010392e-01, 2.31804410824338724684e-01, + 2.28934165414680340644e-01, 2.26076071322380278694e-01, + 2.23230075763917484855e-01, 2.20396127480151998723e-01, + 2.17574176724331130872e-01, 2.14764175251173583536e-01, + 2.11966076307030182324e-01, 2.09179834621125076977e-01, + 2.06405406397880797353e-01, 2.03642749310334908452e-01, + 2.00891822494656591136e-01, 1.98152586545775138971e-01, + 1.95425003514134304483e-01, 1.92709036903589175926e-01, + 1.90004651670464985713e-01, 1.87311814223800304768e-01, + 1.84630492426799269756e-01, 1.81960655599522513892e-01, + 1.79302274522847582272e-01, 1.76655321443734858455e-01, + 1.74019770081838553999e-01, 1.71395595637505754327e-01, + 1.68782774801211288285e-01, 1.66181285764481906364e-01, + 1.63591108232365584074e-01, 1.61012223437511009516e-01, + 1.58444614155924284882e-01, 1.55888264724479197465e-01, + 1.53343161060262855866e-01, 1.50809290681845675763e-01, + 1.48286642732574552861e-01, 1.45775208005994028060e-01, + 1.43274978973513461566e-01, 1.40785949814444699690e-01, + 1.38308116448550733057e-01, 1.35841476571253755301e-01, + 1.33386029691669155683e-01, 1.30941777173644358090e-01, + 1.28508722279999570981e-01, 1.26086870220185887081e-01, + 1.23676228201596571932e-01, 1.21276805484790306533e-01, + 1.18888613442910059947e-01, 1.16511665625610869035e-01, + 1.14145977827838487895e-01, 1.11791568163838089811e-01, + 1.09448457146811797824e-01, 1.07116667774683801961e-01, + 1.04796225622487068629e-01, 1.02487158941935246892e-01, + 1.00189498768810017482e-01, 9.79032790388624646338e-02, + 9.56285367130089991594e-02, 9.33653119126910124859e-02, + 9.11136480663737591268e-02, 8.88735920682758862021e-02, + 8.66451944505580717859e-02, 8.44285095703534715916e-02, + 8.22235958132029043366e-02, 8.00305158146630696292e-02, + 7.78493367020961224423e-02, 7.56801303589271778804e-02, + 7.35229737139813238622e-02, 7.13779490588904025339e-02, + 6.92451443970067553879e-02, 6.71246538277884968737e-02, + 6.50165779712428976156e-02, 6.29210244377581412456e-02, + 6.08381083495398780614e-02, 5.87679529209337372930e-02, + 5.67106901062029017391e-02, 5.46664613248889208474e-02, + 5.26354182767921896513e-02, 5.06177238609477817000e-02, + 4.86135532158685421122e-02, 4.66230949019303814174e-02, + 4.46465522512944634759e-02, 4.26841449164744590750e-02, + 4.07361106559409394401e-02, 3.88027074045261474722e-02, + 3.68842156885673053135e-02, 3.49809414617161251737e-02, + 3.30932194585785779961e-02, 3.12214171919203004046e-02, + 2.93659397581333588001e-02, 2.75272356696031131329e-02, + 2.57058040085489103443e-02, 2.39022033057958785407e-02, + 2.21170627073088502113e-02, 2.03510962300445102935e-02, + 1.86051212757246224594e-02, 1.68800831525431419000e-02, + 1.51770883079353092332e-02, 1.34974506017398673818e-02, + 1.18427578579078790488e-02, 1.02149714397014590439e-02, + 8.61658276939872638800e-03, 7.05087547137322242369e-03, + 5.52240329925099155545e-03, 4.03797259336302356153e-03, + 2.60907274610215926189e-03, 1.26028593049859797236e-03}; + +static const uint32_t ki_float[] = { + 0x007799ECUL, 0x00000000UL, 0x006045F5UL, 0x006D1AA8UL, 0x00728FB4UL, + 0x007592AFUL, 0x00777A5CUL, 0x0078CA38UL, 0x0079BF6BUL, 0x007A7A35UL, + 0x007B0D2FUL, 0x007B83D4UL, 0x007BE597UL, 0x007C3788UL, 0x007C7D33UL, + 0x007CB926UL, 0x007CED48UL, 0x007D1B08UL, 0x007D437FUL, 0x007D678BUL, + 0x007D87DBUL, 0x007DA4FCUL, 0x007DBF61UL, 0x007DD767UL, 0x007DED5DUL, + 0x007E0183UL, 0x007E1411UL, 0x007E2534UL, 0x007E3515UL, 0x007E43D5UL, + 0x007E5193UL, 0x007E5E67UL, 0x007E6A69UL, 0x007E75AAUL, 0x007E803EUL, + 0x007E8A32UL, 0x007E9395UL, 0x007E9C72UL, 0x007EA4D5UL, 0x007EACC6UL, + 0x007EB44EUL, 0x007EBB75UL, 0x007EC243UL, 0x007EC8BCUL, 0x007ECEE8UL, + 0x007ED4CCUL, 0x007EDA6BUL, 0x007EDFCBUL, 0x007EE4EFUL, 0x007EE9DCUL, + 0x007EEE94UL, 0x007EF31BUL, 0x007EF774UL, 0x007EFBA0UL, 0x007EFFA3UL, + 0x007F037FUL, 0x007F0736UL, 0x007F0ACAUL, 0x007F0E3CUL, 0x007F118FUL, + 0x007F14C4UL, 0x007F17DCUL, 0x007F1ADAUL, 0x007F1DBDUL, 0x007F2087UL, + 0x007F233AUL, 0x007F25D7UL, 0x007F285DUL, 0x007F2AD0UL, 0x007F2D2EUL, + 0x007F2F7AUL, 0x007F31B3UL, 0x007F33DCUL, 0x007F35F3UL, 0x007F37FBUL, + 0x007F39F3UL, 0x007F3BDCUL, 0x007F3DB7UL, 0x007F3F84UL, 0x007F4145UL, + 0x007F42F8UL, 0x007F449FUL, 0x007F463AUL, 0x007F47CAUL, 0x007F494EUL, + 0x007F4AC8UL, 0x007F4C38UL, 0x007F4D9DUL, 0x007F4EF9UL, 0x007F504CUL, + 0x007F5195UL, 0x007F52D5UL, 0x007F540DUL, 0x007F553DUL, 0x007F5664UL, + 0x007F5784UL, 0x007F589CUL, 0x007F59ACUL, 0x007F5AB5UL, 0x007F5BB8UL, + 0x007F5CB3UL, 0x007F5DA8UL, 0x007F5E96UL, 0x007F5F7EUL, 0x007F605FUL, + 0x007F613BUL, 0x007F6210UL, 0x007F62E0UL, 0x007F63AAUL, 0x007F646FUL, + 0x007F652EUL, 0x007F65E8UL, 0x007F669CUL, 0x007F674CUL, 0x007F67F6UL, + 0x007F689CUL, 0x007F693CUL, 0x007F69D9UL, 0x007F6A70UL, 0x007F6B03UL, + 0x007F6B91UL, 0x007F6C1BUL, 0x007F6CA0UL, 0x007F6D21UL, 0x007F6D9EUL, + 0x007F6E17UL, 0x007F6E8CUL, 0x007F6EFCUL, 0x007F6F68UL, 0x007F6FD1UL, + 0x007F7035UL, 0x007F7096UL, 0x007F70F3UL, 0x007F714CUL, 0x007F71A1UL, + 0x007F71F2UL, 0x007F723FUL, 0x007F7289UL, 0x007F72CFUL, 0x007F7312UL, + 0x007F7350UL, 0x007F738BUL, 0x007F73C3UL, 0x007F73F6UL, 0x007F7427UL, + 0x007F7453UL, 0x007F747CUL, 0x007F74A1UL, 0x007F74C3UL, 0x007F74E0UL, + 0x007F74FBUL, 0x007F7511UL, 0x007F7524UL, 0x007F7533UL, 0x007F753FUL, + 0x007F7546UL, 0x007F754AUL, 0x007F754BUL, 0x007F7547UL, 0x007F753FUL, + 0x007F7534UL, 0x007F7524UL, 0x007F7511UL, 0x007F74F9UL, 0x007F74DEUL, + 0x007F74BEUL, 0x007F749AUL, 0x007F7472UL, 0x007F7445UL, 0x007F7414UL, + 0x007F73DFUL, 0x007F73A5UL, 0x007F7366UL, 0x007F7323UL, 0x007F72DAUL, + 0x007F728DUL, 0x007F723AUL, 0x007F71E3UL, 0x007F7186UL, 0x007F7123UL, + 0x007F70BBUL, 0x007F704DUL, 0x007F6FD9UL, 0x007F6F5FUL, 0x007F6EDFUL, + 0x007F6E58UL, 0x007F6DCBUL, 0x007F6D37UL, 0x007F6C9CUL, 0x007F6BF9UL, + 0x007F6B4FUL, 0x007F6A9CUL, 0x007F69E2UL, 0x007F691FUL, 0x007F6854UL, + 0x007F677FUL, 0x007F66A1UL, 0x007F65B8UL, 0x007F64C6UL, 0x007F63C8UL, + 0x007F62C0UL, 0x007F61ABUL, 0x007F608AUL, 0x007F5F5DUL, 0x007F5E21UL, + 0x007F5CD8UL, 0x007F5B7FUL, 0x007F5A17UL, 0x007F589EUL, 0x007F5713UL, + 0x007F5575UL, 0x007F53C4UL, 0x007F51FEUL, 0x007F5022UL, 0x007F4E2FUL, + 0x007F4C22UL, 0x007F49FAUL, 0x007F47B6UL, 0x007F4553UL, 0x007F42CFUL, + 0x007F4028UL, 0x007F3D5AUL, 0x007F3A64UL, 0x007F3741UL, 0x007F33EDUL, + 0x007F3065UL, 0x007F2CA4UL, 0x007F28A4UL, 0x007F245FUL, 0x007F1FCEUL, + 0x007F1AEAUL, 0x007F15A9UL, 0x007F1000UL, 0x007F09E4UL, 0x007F0346UL, + 0x007EFC16UL, 0x007EF43EUL, 0x007EEBA8UL, 0x007EE237UL, 0x007ED7C8UL, + 0x007ECC2FUL, 0x007EBF37UL, 0x007EB09DUL, 0x007EA00AUL, 0x007E8D0DUL, + 0x007E7710UL, 0x007E5D47UL, 0x007E3E93UL, 0x007E1959UL, 0x007DEB2CUL, + 0x007DB036UL, 0x007D6203UL, 0x007CF4B9UL, 0x007C4FD2UL, 0x007B3630UL, + 0x0078D2D2UL}; + +static const float wi_float[] = { + 4.66198677960027669255e-07f, 2.56588335019207033255e-08f, + 3.41146697750176784592e-08f, 4.00230311410932959821e-08f, + 4.47179475877737745459e-08f, 4.86837785973537366722e-08f, + 5.21562578925932412861e-08f, 5.52695199001886257153e-08f, + 5.81078488992733116465e-08f, 6.07279932024587421409e-08f, + 6.31701613261172047795e-08f, 6.54639842900233842742e-08f, + 6.76319905583641815324e-08f, 6.96917493470166688656e-08f, + 7.16572544283857476692e-08f, 7.35398519048393832969e-08f, + 7.53488822443557479279e-08f, 7.70921367281667127885e-08f, + 7.87761895947956022626e-08f, 8.04066446825615346857e-08f, + 8.19883218760237408659e-08f, 8.35254002936857088917e-08f, + 8.50215298165053411740e-08f, 8.64799190652369040985e-08f, + 8.79034055989140110861e-08f, 8.92945125124233511541e-08f, + 9.06554945027956262312e-08f, 9.19883756905278607229e-08f, + 9.32949809202232869780e-08f, 9.45769618559625849039e-08f, + 9.58358188855612866442e-08f, 9.70729196232813152662e-08f, + 9.82895146313061088986e-08f, 9.94867508514382224721e-08f, + 1.00665683139461669691e-07f, 1.01827284217853923044e-07f, + 1.02972453302539369464e-07f, 1.04102023612124921572e-07f, + 1.05216768930574060431e-07f, 1.06317409364335657741e-07f, + 1.07404616410877866490e-07f, 1.08479017436113134283e-07f, + 1.09541199642370962438e-07f, 1.10591713595628691212e-07f, + 1.11631076370069356306e-07f, 1.12659774359245895023e-07f, + 1.13678265795837113569e-07f, 1.14686983015899673063e-07f, + 1.15686334498432158725e-07f, 1.16676706706789039179e-07f, + 1.17658465754873988919e-07f, 1.18631958917986203582e-07f, + 1.19597516005596215528e-07f, 1.20555450611113917226e-07f, + 1.21506061251817163689e-07f, 1.22449632410483948386e-07f, + 1.23386435488872536840e-07f, 1.24316729681986364321e-07f, + 1.25240762781015530062e-07f, 1.26158771911939892267e-07f, + 1.27070984215989333455e-07f, 1.27977617477468922011e-07f, + 1.28878880703854958297e-07f, 1.29774974662539874521e-07f, + 1.30666092378141980504e-07f, 1.31552419593887221722e-07f, + 1.32434135200211397569e-07f, 1.33311411633413359243e-07f, + 1.34184415246907777059e-07f, 1.35053306657377859830e-07f, + 1.35918241067904315860e-07f, 1.36779368569952053923e-07f, + 1.37636834425917531047e-07f, 1.38490779333783508675e-07f, + 1.39341339675287344817e-07f, 1.40188647748881762555e-07f, + 1.41032831988654882776e-07f, 1.41874017170273235693e-07f, + 1.42712324604921442006e-07f, 1.43547872322127921816e-07f, + 1.44380775242292721080e-07f, 1.45211145339665544509e-07f, + 1.46039091796461362146e-07f, 1.46864721148745476208e-07f, + 1.47688137424670065700e-07f, 1.48509442275598857119e-07f, + 1.49328735100614641423e-07f, 1.50146113164867617390e-07f, + 1.50961671712187416111e-07f, 1.51775504072350982845e-07f, + 1.52587701763369746341e-07f, 1.53398354589133671168e-07f, + 1.54207550732725568797e-07f, 1.55015376845697999657e-07f, + 1.55821918133584372604e-07f, 1.56627258437898192833e-07f, + 1.57431480314857468671e-07f, 1.58234665111056041043e-07f, + 1.59036893036289199880e-07f, 1.59838243233728855017e-07f, + 1.60638793847630850137e-07f, 1.61438622088746393909e-07f, + 1.62237804297600106296e-07f, 1.63036416005787357730e-07f, + 1.63834531995435479082e-07f, 1.64632226356965902954e-07f, + 1.65429572545287097020e-07f, 1.66226643434541294491e-07f, + 1.67023511371523209274e-07f, 1.67820248227882200051e-07f, + 1.68616925451215588827e-07f, 1.69413614115155757272e-07f, + 1.70210384968549673733e-07f, 1.71007308483826142122e-07f, + 1.71804454904642543391e-07f, 1.72601894292900061024e-07f, + 1.73399696575213681990e-07f, 1.74197931588920988271e-07f, + 1.74996669127712165834e-07f, 1.75795978986961275677e-07f, + 1.76595931008838063924e-07f, 1.77396595127278238022e-07f, + 1.78198041412889183130e-07f, 1.79000340117867431104e-07f, + 1.79803561721004406185e-07f, 1.80607776972855859813e-07f, + 1.81413056941151359868e-07f, 1.82219473056520464354e-07f, + 1.83027097158612474240e-07f, 1.83836001542687613069e-07f, + 1.84646259006759307383e-07f, 1.85457942899367347876e-07f, + 1.86271127168064649331e-07f, 1.87085886408701333260e-07f, + 1.87902295915592424729e-07f, 1.88720431732658022414e-07f, + 1.89540370705627262627e-07f, 1.90362190535400839128e-07f, + 1.91185969832669990437e-07f, 1.92011788173893651535e-07f, + 1.92839726158739913768e-07f, 1.93669865469102145482e-07f, + 1.94502288929804890433e-07f, 1.95337080571120616772e-07f, + 1.96174325693223683314e-07f, 1.97014110932714374919e-07f, + 1.97856524331352952716e-07f, 1.98701655407150388211e-07f, + 1.99549595227971635348e-07f, 2.00400436487814600236e-07f, + 2.01254273585938820883e-07f, 2.02111202709026498408e-07f, + 2.02971321916571014951e-07f, 2.03834731229698846698e-07f, + 2.04701532723644121196e-07f, 2.05571830624108885378e-07f, + 2.06445731407757185541e-07f, 2.07323343907107312957e-07f, + 2.08204779420104330037e-07f, 2.09090151824673600213e-07f, + 2.09979577698577670508e-07f, 2.10873176444920111011e-07f, + 2.11771070423665379388e-07f, 2.12673385089569268965e-07f, + 2.13580249136944118603e-07f, 2.14491794651713402832e-07f, + 2.15408157271244625533e-07f, 2.16329476352486921685e-07f, + 2.17255895148978920488e-07f, 2.18187560997337924713e-07f, + 2.19124625513888206785e-07f, 2.20067244802139479285e-07f, + 2.21015579671883851683e-07f, 2.21969795870742159701e-07f, + 2.22930064329060010376e-07f, 2.23896561419128954210e-07f, + 2.24869469229791575583e-07f, 2.25848975857580322189e-07f, + 2.26835275715640744118e-07f, 2.27828569861799901001e-07f, + 2.28829066347263833069e-07f, 2.29836980587561823183e-07f, + 2.30852535757505260518e-07f, 2.31875963212094114516e-07f, + 2.32907502935486642699e-07f, 2.33947404020352726160e-07f, + 2.34995925180156140289e-07f, 2.36053335297164516378e-07f, + 2.37119914009265667728e-07f, 2.38195952338983970691e-07f, + 2.39281753368440712742e-07f, 2.40377632964396957621e-07f, + 2.41483920557958384709e-07f, 2.42600959984018662258e-07f, + 2.43729110386077326413e-07f, 2.44868747192698939290e-07f, + 2.46020263172594533433e-07f, 2.47184069576113545901e-07f, + 2.48360597371852893654e-07f, 2.49550298588131851232e-07f, + 2.50753647770270890721e-07f, 2.51971143565970967140e-07f, + 2.53203310452642767375e-07f, 2.54450700622322097890e-07f, + 2.55713896041856770961e-07f, 2.56993510708419870887e-07f, + 2.58290193123138874550e-07f, 2.59604629008804833146e-07f, + 2.60937544301314385690e-07f, 2.62289708448800566945e-07f, + 2.63661938057441759882e-07f, 2.65055100928844238758e-07f, + 2.66470120540847889467e-07f, 2.67907981031821866252e-07f, + 2.69369732758258246335e-07f, 2.70856498507068313229e-07f, + 2.72369480457841388042e-07f, 2.73909968006952220135e-07f, + 2.75479346585437289399e-07f, 2.77079107626811561009e-07f, + 2.78710859870496796972e-07f, 2.80376342222588603820e-07f, + 2.82077438439999912690e-07f, 2.83816193958769527230e-07f, + 2.85594835255375795814e-07f, 2.87415792215003905739e-07f, + 2.89281724087851835900e-07f, 2.91195549750371467233e-07f, + 2.93160483161771875581e-07f, 2.95180075129332912389e-07f, + 2.97258262785797916083e-07f, 2.99399428561531794298e-07f, + 3.01608470935804138388e-07f, 3.03890889921758510417e-07f, + 3.06252891144972267537e-07f, 3.08701513613258141075e-07f, + 3.11244787989714509378e-07f, 3.13891934589336184321e-07f, + 3.16653613755314681314e-07f, 3.19542246256559459667e-07f, + 3.22572428717978242099e-07f, 3.25761480217458181578e-07f, + 3.29130173358915628534e-07f, 3.32703730345002116955e-07f, + 3.36513208964639108346e-07f, 3.40597478255417943913e-07f, + 3.45006114675213401550e-07f, 3.49803789521323211592e-07f, + 3.55077180848341416206e-07f, 3.60946392031859609868e-07f, + 3.67584959507244041831e-07f, 3.75257645787954431030e-07f, + 3.84399301057791926300e-07f, 3.95804015855768440983e-07f, + 4.11186015434435801956e-07f, 4.35608969373823260746e-07f}; + +static const float fi_float[] = { + 1.00000000000000000000e+00f, 9.77101701267671596263e-01f, + 9.59879091800106665211e-01f, 9.45198953442299649730e-01f, + 9.32060075959230460718e-01f, 9.19991505039347012840e-01f, + 9.08726440052130879366e-01f, 8.98095921898343418910e-01f, + 8.87984660755833377088e-01f, 8.78309655808917399966e-01f, + 8.69008688036857046555e-01f, 8.60033621196331532488e-01f, + 8.51346258458677951353e-01f, 8.42915653112204177333e-01f, + 8.34716292986883434679e-01f, 8.26726833946221373317e-01f, + 8.18929191603702366642e-01f, 8.11307874312656274185e-01f, + 8.03849483170964274059e-01f, 7.96542330422958966274e-01f, + 7.89376143566024590648e-01f, 7.82341832654802504798e-01f, + 7.75431304981187174974e-01f, 7.68637315798486264740e-01f, + 7.61953346836795386565e-01f, 7.55373506507096115214e-01f, + 7.48892447219156820459e-01f, 7.42505296340151055290e-01f, + 7.36207598126862650112e-01f, 7.29995264561476231435e-01f, + 7.23864533468630222401e-01f, 7.17811932630721960535e-01f, + 7.11834248878248421200e-01f, 7.05928501332754310127e-01f, + 7.00091918136511615067e-01f, 6.94321916126116711609e-01f, + 6.88616083004671808432e-01f, 6.82972161644994857355e-01f, + 6.77388036218773526009e-01f, 6.71861719897082099173e-01f, + 6.66391343908750100056e-01f, 6.60975147776663107813e-01f, + 6.55611470579697264149e-01f, 6.50298743110816701574e-01f, + 6.45035480820822293424e-01f, 6.39820277453056585060e-01f, + 6.34651799287623608059e-01f, 6.29528779924836690007e-01f, + 6.24450015547026504592e-01f, 6.19414360605834324325e-01f, + 6.14420723888913888899e-01f, 6.09468064925773433949e-01f, + 6.04555390697467776029e-01f, 5.99681752619125263415e-01f, + 5.94846243767987448159e-01f, 5.90047996332826008015e-01f, + 5.85286179263371453274e-01f, 5.80559996100790898232e-01f, + 5.75868682972353718164e-01f, 5.71211506735253227163e-01f, + 5.66587763256164445025e-01f, 5.61996775814524340831e-01f, + 5.57437893618765945014e-01f, 5.52910490425832290562e-01f, + 5.48413963255265812791e-01f, 5.43947731190026262382e-01f, + 5.39511234256952132426e-01f, 5.35103932380457614215e-01f, + 5.30725304403662057062e-01f, 5.26374847171684479008e-01f, + 5.22052074672321841931e-01f, 5.17756517229756352272e-01f, + 5.13487720747326958914e-01f, 5.09245245995747941592e-01f, + 5.05028667943468123624e-01f, 5.00837575126148681903e-01f, + 4.96671569052489714213e-01f, 4.92530263643868537748e-01f, + 4.88413284705458028423e-01f, 4.84320269426683325253e-01f, + 4.80250865909046753544e-01f, 4.76204732719505863248e-01f, + 4.72181538467730199660e-01f, 4.68180961405693596422e-01f, + 4.64202689048174355069e-01f, 4.60246417812842867345e-01f, + 4.56311852678716434184e-01f, 4.52398706861848520777e-01f, + 4.48506701507203064949e-01f, 4.44635565395739396077e-01f, + 4.40785034665803987508e-01f, 4.36954852547985550526e-01f, + 4.33144769112652261445e-01f, 4.29354541029441427735e-01f, + 4.25583931338021970170e-01f, 4.21832709229495894654e-01f, + 4.18100649837848226120e-01f, 4.14387534040891125642e-01f, + 4.10693148270188157500e-01f, 4.07017284329473372217e-01f, + 4.03359739221114510510e-01f, 3.99720314980197222177e-01f, + 3.96098818515832451492e-01f, 3.92495061459315619512e-01f, + 3.88908860018788715696e-01f, 3.85340034840077283462e-01f, + 3.81788410873393657674e-01f, 3.78253817245619183840e-01f, + 3.74736087137891138443e-01f, 3.71235057668239498696e-01f, + 3.67750569779032587814e-01f, 3.64282468129004055601e-01f, + 3.60830600989648031529e-01f, 3.57394820145780500731e-01f, + 3.53974980800076777232e-01f, 3.50570941481406106455e-01f, + 3.47182563956793643900e-01f, 3.43809713146850715049e-01f, + 3.40452257044521866547e-01f, 3.37110066637006045021e-01f, + 3.33783015830718454708e-01f, 3.30470981379163586400e-01f, + 3.27173842813601400970e-01f, 3.23891482376391093290e-01f, + 3.20623784956905355514e-01f, 3.17370638029913609834e-01f, + 3.14131931596337177215e-01f, 3.10907558126286509559e-01f, + 3.07697412504292056035e-01f, 3.04501391976649993243e-01f, + 3.01319396100803049698e-01f, 2.98151326696685481377e-01f, + 2.94997087799961810184e-01f, 2.91856585617095209972e-01f, + 2.88729728482182923521e-01f, 2.85616426815501756042e-01f, + 2.82516593083707578948e-01f, 2.79430141761637940157e-01f, + 2.76356989295668320494e-01f, 2.73297054068577072172e-01f, + 2.70250256365875463072e-01f, 2.67216518343561471038e-01f, + 2.64195763997261190426e-01f, 2.61187919132721213522e-01f, + 2.58192911337619235290e-01f, 2.55210669954661961700e-01f, + 2.52241126055942177508e-01f, 2.49284212418528522415e-01f, + 2.46339863501263828249e-01f, 2.43408015422750312329e-01f, + 2.40488605940500588254e-01f, 2.37581574431238090606e-01f, + 2.34686861872330010392e-01f, 2.31804410824338724684e-01f, + 2.28934165414680340644e-01f, 2.26076071322380278694e-01f, + 2.23230075763917484855e-01f, 2.20396127480151998723e-01f, + 2.17574176724331130872e-01f, 2.14764175251173583536e-01f, + 2.11966076307030182324e-01f, 2.09179834621125076977e-01f, + 2.06405406397880797353e-01f, 2.03642749310334908452e-01f, + 2.00891822494656591136e-01f, 1.98152586545775138971e-01f, + 1.95425003514134304483e-01f, 1.92709036903589175926e-01f, + 1.90004651670464985713e-01f, 1.87311814223800304768e-01f, + 1.84630492426799269756e-01f, 1.81960655599522513892e-01f, + 1.79302274522847582272e-01f, 1.76655321443734858455e-01f, + 1.74019770081838553999e-01f, 1.71395595637505754327e-01f, + 1.68782774801211288285e-01f, 1.66181285764481906364e-01f, + 1.63591108232365584074e-01f, 1.61012223437511009516e-01f, + 1.58444614155924284882e-01f, 1.55888264724479197465e-01f, + 1.53343161060262855866e-01f, 1.50809290681845675763e-01f, + 1.48286642732574552861e-01f, 1.45775208005994028060e-01f, + 1.43274978973513461566e-01f, 1.40785949814444699690e-01f, + 1.38308116448550733057e-01f, 1.35841476571253755301e-01f, + 1.33386029691669155683e-01f, 1.30941777173644358090e-01f, + 1.28508722279999570981e-01f, 1.26086870220185887081e-01f, + 1.23676228201596571932e-01f, 1.21276805484790306533e-01f, + 1.18888613442910059947e-01f, 1.16511665625610869035e-01f, + 1.14145977827838487895e-01f, 1.11791568163838089811e-01f, + 1.09448457146811797824e-01f, 1.07116667774683801961e-01f, + 1.04796225622487068629e-01f, 1.02487158941935246892e-01f, + 1.00189498768810017482e-01f, 9.79032790388624646338e-02f, + 9.56285367130089991594e-02f, 9.33653119126910124859e-02f, + 9.11136480663737591268e-02f, 8.88735920682758862021e-02f, + 8.66451944505580717859e-02f, 8.44285095703534715916e-02f, + 8.22235958132029043366e-02f, 8.00305158146630696292e-02f, + 7.78493367020961224423e-02f, 7.56801303589271778804e-02f, + 7.35229737139813238622e-02f, 7.13779490588904025339e-02f, + 6.92451443970067553879e-02f, 6.71246538277884968737e-02f, + 6.50165779712428976156e-02f, 6.29210244377581412456e-02f, + 6.08381083495398780614e-02f, 5.87679529209337372930e-02f, + 5.67106901062029017391e-02f, 5.46664613248889208474e-02f, + 5.26354182767921896513e-02f, 5.06177238609477817000e-02f, + 4.86135532158685421122e-02f, 4.66230949019303814174e-02f, + 4.46465522512944634759e-02f, 4.26841449164744590750e-02f, + 4.07361106559409394401e-02f, 3.88027074045261474722e-02f, + 3.68842156885673053135e-02f, 3.49809414617161251737e-02f, + 3.30932194585785779961e-02f, 3.12214171919203004046e-02f, + 2.93659397581333588001e-02f, 2.75272356696031131329e-02f, + 2.57058040085489103443e-02f, 2.39022033057958785407e-02f, + 2.21170627073088502113e-02f, 2.03510962300445102935e-02f, + 1.86051212757246224594e-02f, 1.68800831525431419000e-02f, + 1.51770883079353092332e-02f, 1.34974506017398673818e-02f, + 1.18427578579078790488e-02f, 1.02149714397014590439e-02f, + 8.61658276939872638800e-03f, 7.05087547137322242369e-03f, + 5.52240329925099155545e-03f, 4.03797259336302356153e-03f, + 2.60907274610215926189e-03f, 1.26028593049859797236e-03f}; + +static const uint64_t ke_double[] = { + 0x001C5214272497C6, 0x0000000000000000, 0x00137D5BD79C317E, + 0x00186EF58E3F3C10, 0x001A9BB7320EB0AE, 0x001BD127F719447C, + 0x001C951D0F88651A, 0x001D1BFE2D5C3972, 0x001D7E5BD56B18B2, + 0x001DC934DD172C70, 0x001E0409DFAC9DC8, 0x001E337B71D47836, + 0x001E5A8B177CB7A2, 0x001E7B42096F046C, 0x001E970DAF08AE3E, + 0x001EAEF5B14EF09E, 0x001EC3BD07B46556, 0x001ED5F6F08799CE, + 0x001EE614AE6E5688, 0x001EF46ECA361CD0, 0x001F014B76DDD4A4, + 0x001F0CE313A796B6, 0x001F176369F1F77A, 0x001F20F20C452570, + 0x001F29AE1951A874, 0x001F31B18FB95532, 0x001F39125157C106, + 0x001F3FE2EB6E694C, 0x001F463332D788FA, 0x001F4C10BF1D3A0E, + 0x001F51874C5C3322, 0x001F56A109C3ECC0, 0x001F5B66D9099996, + 0x001F5FE08210D08C, 0x001F6414DD445772, 0x001F6809F6859678, + 0x001F6BC52A2B02E6, 0x001F6F4B3D32E4F4, 0x001F72A07190F13A, + 0x001F75C8974D09D6, 0x001F78C71B045CC0, 0x001F7B9F12413FF4, + 0x001F7E5346079F8A, 0x001F80E63BE21138, 0x001F835A3DAD9162, + 0x001F85B16056B912, 0x001F87ED89B24262, 0x001F8A10759374FA, + 0x001F8C1BBA3D39AC, 0x001F8E10CC45D04A, 0x001F8FF102013E16, + 0x001F91BD968358E0, 0x001F9377AC47AFD8, 0x001F95204F8B64DA, + 0x001F96B878633892, 0x001F98410C968892, 0x001F99BAE146BA80, + 0x001F9B26BC697F00, 0x001F9C85561B717A, 0x001F9DD759CFD802, + 0x001F9F1D6761A1CE, 0x001FA058140936C0, 0x001FA187EB3A3338, + 0x001FA2AD6F6BC4FC, 0x001FA3C91ACE0682, 0x001FA4DB5FEE6AA2, + 0x001FA5E4AA4D097C, 0x001FA6E55EE46782, 0x001FA7DDDCA51EC4, + 0x001FA8CE7CE6A874, 0x001FA9B793CE5FEE, 0x001FAA9970ADB858, + 0x001FAB745E588232, 0x001FAC48A3740584, 0x001FAD1682BF9FE8, + 0x001FADDE3B5782C0, 0x001FAEA008F21D6C, 0x001FAF5C2418B07E, + 0x001FB012C25B7A12, 0x001FB0C41681DFF4, 0x001FB17050B6F1FA, + 0x001FB2179EB2963A, 0x001FB2BA2BDFA84A, 0x001FB358217F4E18, + 0x001FB3F1A6C9BE0C, 0x001FB486E10CACD6, 0x001FB517F3C793FC, + 0x001FB5A500C5FDAA, 0x001FB62E2837FE58, 0x001FB6B388C9010A, + 0x001FB7353FB50798, 0x001FB7B368DC7DA8, 0x001FB82E1ED6BA08, + 0x001FB8A57B0347F6, 0x001FB919959A0F74, 0x001FB98A85BA7204, + 0x001FB9F861796F26, 0x001FBA633DEEE286, 0x001FBACB2F41EC16, + 0x001FBB3048B49144, 0x001FBB929CAEA4E2, 0x001FBBF23CC8029E, + 0x001FBC4F39D22994, 0x001FBCA9A3E140D4, 0x001FBD018A548F9E, + 0x001FBD56FBDE729C, 0x001FBDAA068BD66A, 0x001FBDFAB7CB3F40, + 0x001FBE491C7364DE, 0x001FBE9540C9695E, 0x001FBEDF3086B128, + 0x001FBF26F6DE6174, 0x001FBF6C9E828AE2, 0x001FBFB031A904C4, + 0x001FBFF1BA0FFDB0, 0x001FC03141024588, 0x001FC06ECF5B54B2, + 0x001FC0AA6D8B1426, 0x001FC0E42399698A, 0x001FC11BF9298A64, + 0x001FC151F57D1942, 0x001FC1861F770F4A, 0x001FC1B87D9E74B4, + 0x001FC1E91620EA42, 0x001FC217EED505DE, 0x001FC2450D3C83FE, + 0x001FC27076864FC2, 0x001FC29A2F90630E, 0x001FC2C23CE98046, + 0x001FC2E8A2D2C6B4, 0x001FC30D654122EC, 0x001FC33087DE9C0E, + 0x001FC3520E0B7EC6, 0x001FC371FADF66F8, 0x001FC390512A2886, + 0x001FC3AD137497FA, 0x001FC3C844013348, 0x001FC3E1E4CCAB40, + 0x001FC3F9F78E4DA8, 0x001FC4107DB85060, 0x001FC4257877FD68, + 0x001FC438E8B5BFC6, 0x001FC44ACF15112A, 0x001FC45B2BF447E8, + 0x001FC469FF6C4504, 0x001FC477495001B2, 0x001FC483092BFBB8, + 0x001FC48D3E457FF6, 0x001FC495E799D21A, 0x001FC49D03DD30B0, + 0x001FC4A29179B432, 0x001FC4A68E8E07FC, 0x001FC4A8F8EBFB8C, + 0x001FC4A9CE16EA9E, 0x001FC4A90B41FA34, 0x001FC4A6AD4E28A0, + 0x001FC4A2B0C82E74, 0x001FC49D11E62DE2, 0x001FC495CC852DF4, + 0x001FC48CDC265EC0, 0x001FC4823BEC237A, 0x001FC475E696DEE6, + 0x001FC467D6817E82, 0x001FC458059DC036, 0x001FC4466D702E20, + 0x001FC433070BCB98, 0x001FC41DCB0D6E0E, 0x001FC406B196BBF6, + 0x001FC3EDB248CB62, 0x001FC3D2C43E593C, 0x001FC3B5DE0591B4, + 0x001FC396F599614C, 0x001FC376005A4592, 0x001FC352F3069370, + 0x001FC32DC1B22818, 0x001FC3065FBD7888, 0x001FC2DCBFCBF262, + 0x001FC2B0D3B99F9E, 0x001FC2828C8FFCF0, 0x001FC251DA79F164, + 0x001FC21EACB6D39E, 0x001FC1E8F18C6756, 0x001FC1B09637BB3C, + 0x001FC17586DCCD10, 0x001FC137AE74D6B6, 0x001FC0F6F6BB2414, + 0x001FC0B348184DA4, 0x001FC06C898BAFF0, 0x001FC022A092F364, + 0x001FBFD5710F72B8, 0x001FBF84DD29488E, 0x001FBF30C52FC60A, + 0x001FBED907770CC6, 0x001FBE7D80327DDA, 0x001FBE1E094BA614, + 0x001FBDBA7A354408, 0x001FBD52A7B9F826, 0x001FBCE663C6201A, + 0x001FBC757D2C4DE4, 0x001FBBFFBF63B7AA, 0x001FBB84F23FE6A2, + 0x001FBB04D9A0D18C, 0x001FBA7F351A70AC, 0x001FB9F3BF92B618, + 0x001FB9622ED4ABFC, 0x001FB8CA33174A16, 0x001FB82B76765B54, + 0x001FB7859C5B895C, 0x001FB6D840D55594, 0x001FB622F7D96942, + 0x001FB5654C6F37E0, 0x001FB49EBFBF69D2, 0x001FB3CEC803E746, + 0x001FB2F4CF539C3E, 0x001FB21032442852, 0x001FB1203E5A9604, + 0x001FB0243042E1C2, 0x001FAF1B31C479A6, 0x001FAE045767E104, + 0x001FACDE9DBF2D72, 0x001FABA8E640060A, 0x001FAA61F399FF28, + 0x001FA908656F66A2, 0x001FA79AB3508D3C, 0x001FA61726D1F214, + 0x001FA47BD48BEA00, 0x001FA2C693C5C094, 0x001FA0F4F47DF314, + 0x001F9F04336BBE0A, 0x001F9CF12B79F9BC, 0x001F9AB84415ABC4, + 0x001F98555B782FB8, 0x001F95C3ABD03F78, 0x001F92FDA9CEF1F2, + 0x001F8FFCDA9AE41C, 0x001F8CB99E7385F8, 0x001F892AEC479606, + 0x001F8545F904DB8E, 0x001F80FDC336039A, 0x001F7C427839E926, + 0x001F7700A3582ACC, 0x001F71200F1A241C, 0x001F6A8234B7352A, + 0x001F630000A8E266, 0x001F5A66904FE3C4, 0x001F50724ECE1172, + 0x001F44C7665C6FDA, 0x001F36E5A38A59A2, 0x001F26143450340A, + 0x001F113E047B0414, 0x001EF6AEFA57CBE6, 0x001ED38CA188151E, + 0x001EA2A61E122DB0, 0x001E5961C78B267C, 0x001DDDF62BAC0BB0, + 0x001CDB4DD9E4E8C0}; + +static const double we_double[] = { + 9.655740063209182975e-16, 7.089014243955414331e-18, + 1.163941249669122378e-17, 1.524391512353216015e-17, + 1.833284885723743916e-17, 2.108965109464486630e-17, + 2.361128077843138196e-17, 2.595595772310893952e-17, + 2.816173554197752338e-17, 3.025504130321382330e-17, + 3.225508254836375280e-17, 3.417632340185027033e-17, + 3.602996978734452488e-17, 3.782490776869649048e-17, + 3.956832198097553231e-17, 4.126611778175946428e-17, + 4.292321808442525631e-17, 4.454377743282371417e-17, + 4.613133981483185932e-17, 4.768895725264635940e-17, + 4.921928043727962847e-17, 5.072462904503147014e-17, + 5.220704702792671737e-17, 5.366834661718192181e-17, + 5.511014372835094717e-17, 5.653388673239667134e-17, + 5.794088004852766616e-17, 5.933230365208943081e-17, + 6.070922932847179572e-17, 6.207263431163193485e-17, + 6.342341280303076511e-17, 6.476238575956142121e-17, + 6.609030925769405241e-17, 6.740788167872722244e-17, + 6.871574991183812442e-17, 7.001451473403929616e-17, + 7.130473549660643409e-17, 7.258693422414648352e-17, + 7.386159921381791997e-17, 7.512918820723728089e-17, + 7.639013119550825792e-17, 7.764483290797848102e-17, + 7.889367502729790548e-17, 8.013701816675454434e-17, + 8.137520364041762206e-17, 8.260855505210038174e-17, + 8.383737972539139383e-17, 8.506196999385323132e-17, + 8.628260436784112996e-17, 8.749954859216182511e-17, + 8.871305660690252281e-17, 8.992337142215357066e-17, + 9.113072591597909173e-17, 9.233534356381788123e-17, + 9.353743910649128938e-17, 9.473721916312949566e-17, + 9.593488279457997317e-17, 9.713062202221521206e-17, + 9.832462230649511362e-17, 9.951706298915071878e-17, + 1.007081177024294931e-16, 1.018979547484694078e-16, + 1.030867374515421954e-16, 1.042746244856188556e-16, + 1.054617701794576406e-16, 1.066483248011914702e-16, + 1.078344348241948498e-16, 1.090202431758350473e-16, + 1.102058894705578110e-16, 1.113915102286197502e-16, + 1.125772390816567488e-16, 1.137632069661684705e-16, + 1.149495423059009298e-16, 1.161363711840218308e-16, + 1.173238175059045788e-16, 1.185120031532669434e-16, + 1.197010481303465158e-16, 1.208910707027385520e-16, + 1.220821875294706151e-16, 1.232745137888415193e-16, + 1.244681632985112523e-16, 1.256632486302898513e-16, + 1.268598812200397542e-16, 1.280581714730749379e-16, + 1.292582288654119552e-16, 1.304601620412028847e-16, + 1.316640789066572582e-16, 1.328700867207380889e-16, + 1.340782921828999433e-16, 1.352888015181175458e-16, + 1.365017205594397770e-16, 1.377171548282880964e-16, + 1.389352096127063919e-16, 1.401559900437571538e-16, + 1.413796011702485188e-16, 1.426061480319665444e-16, + 1.438357357315790180e-16, 1.450684695053687684e-16, + 1.463044547929475721e-16, 1.475437973060951633e-16, + 1.487866030968626066e-16, 1.500329786250736949e-16, + 1.512830308253539427e-16, 1.525368671738125550e-16, + 1.537945957544996933e-16, 1.550563253257577148e-16, + 1.563221653865837505e-16, 1.575922262431176140e-16, + 1.588666190753684151e-16, 1.601454560042916733e-16, + 1.614288501593278662e-16, 1.627169157465130500e-16, + 1.640097681172717950e-16, 1.653075238380036909e-16, + 1.666103007605742067e-16, 1.679182180938228863e-16, + 1.692313964762022267e-16, 1.705499580496629830e-16, + 1.718740265349031656e-16, 1.732037273081008369e-16, + 1.745391874792533975e-16, 1.758805359722491379e-16, + 1.772279036068006489e-16, 1.785814231823732619e-16, + 1.799412295642463721e-16, 1.813074597718501559e-16, + 1.826802530695252266e-16, 1.840597510598587828e-16, + 1.854460977797569461e-16, 1.868394397994192684e-16, + 1.882399263243892051e-16, 1.896477093008616722e-16, + 1.910629435244376536e-16, 1.924857867525243818e-16, + 1.939163998205899420e-16, 1.953549467624909132e-16, + 1.968015949351037382e-16, 1.982565151475019047e-16, + 1.997198817949342081e-16, 2.011918729978734671e-16, + 2.026726707464198289e-16, 2.041624610503588774e-16, + 2.056614340951917875e-16, 2.071697844044737034e-16, + 2.086877110088159721e-16, 2.102154176219292789e-16, + 2.117531128241075913e-16, 2.133010102535779087e-16, + 2.148593288061663316e-16, 2.164282928437604723e-16, + 2.180081324120784027e-16, 2.195990834682870728e-16, + 2.212013881190495942e-16, 2.228152948696180545e-16, + 2.244410588846308588e-16, 2.260789422613173739e-16, + 2.277292143158621037e-16, 2.293921518837311354e-16, + 2.310680396348213318e-16, 2.327571704043534613e-16, + 2.344598455404957859e-16, 2.361763752697773994e-16, + 2.379070790814276700e-16, 2.396522861318623520e-16, + 2.414123356706293277e-16, 2.431875774892255956e-16, + 2.449783723943070217e-16, 2.467850927069288738e-16, + 2.486081227895851719e-16, 2.504478596029557040e-16, + 2.523047132944217013e-16, 2.541791078205812227e-16, + 2.560714816061770759e-16, 2.579822882420530896e-16, + 2.599119972249746917e-16, 2.618610947423924219e-16, + 2.638300845054942823e-16, 2.658194886341845120e-16, + 2.678298485979525166e-16, 2.698617262169488933e-16, + 2.719157047279818500e-16, 2.739923899205814823e-16, + 2.760924113487617126e-16, 2.782164236246436081e-16, + 2.803651078006983464e-16, 2.825391728480253184e-16, + 2.847393572388174091e-16, 2.869664306419817679e-16, + 2.892211957417995598e-16, 2.915044901905293183e-16, + 2.938171887070028633e-16, 2.961602053345465687e-16, + 2.985344958730045276e-16, 3.009410605012618141e-16, + 3.033809466085003416e-16, 3.058552518544860874e-16, + 3.083651274815310004e-16, 3.109117819034266344e-16, + 3.134964845996663118e-16, 3.161205703467105734e-16, + 3.187854438219713117e-16, 3.214925846206797361e-16, + 3.242435527309451638e-16, 3.270399945182240440e-16, + 3.298836492772283149e-16, 3.327763564171671408e-16, + 3.357200633553244075e-16, 3.387168342045505162e-16, + 3.417688593525636996e-16, 3.448784660453423890e-16, + 3.480481301037442286e-16, 3.512804889222979418e-16, + 3.545783559224791863e-16, 3.579447366604276541e-16, + 3.613828468219060593e-16, 3.648961323764542545e-16, + 3.684882922095621322e-16, 3.721633036080207290e-16, + 3.759254510416256532e-16, 3.797793587668874387e-16, + 3.837300278789213687e-16, 3.877828785607895292e-16, + 3.919437984311428867e-16, 3.962191980786774996e-16, + 4.006160751056541688e-16, 4.051420882956573177e-16, + 4.098056438903062509e-16, 4.146159964290904582e-16, + 4.195833672073398926e-16, 4.247190841824385048e-16, + 4.300357481667470702e-16, 4.355474314693952008e-16, + 4.412699169036069903e-16, 4.472209874259932285e-16, + 4.534207798565834480e-16, 4.598922204905932469e-16, + 4.666615664711475780e-16, 4.737590853262492027e-16, + 4.812199172829237933e-16, 4.890851827392209900e-16, + 4.974034236191939753e-16, 5.062325072144159699e-16, + 5.156421828878082953e-16, 5.257175802022274839e-16, + 5.365640977112021618e-16, 5.483144034258703912e-16, + 5.611387454675159622e-16, 5.752606481503331688e-16, + 5.909817641652102998e-16, 6.087231416180907671e-16, + 6.290979034877557049e-16, 6.530492053564040799e-16, + 6.821393079028928626e-16, 7.192444966089361564e-16, + 7.706095350032096755e-16, 8.545517038584027421e-16}; + +static const double fe_double[] = { + 1.000000000000000000e+00, 9.381436808621747003e-01, + 9.004699299257464817e-01, 8.717043323812035949e-01, + 8.477855006239896074e-01, 8.269932966430503241e-01, + 8.084216515230083777e-01, 7.915276369724956185e-01, + 7.759568520401155522e-01, 7.614633888498962833e-01, + 7.478686219851951034e-01, 7.350380924314234843e-01, + 7.228676595935720206e-01, 7.112747608050760117e-01, + 7.001926550827881623e-01, 6.895664961170779872e-01, + 6.793505722647653622e-01, 6.695063167319247333e-01, + 6.600008410789997004e-01, 6.508058334145710999e-01, + 6.418967164272660897e-01, 6.332519942143660652e-01, + 6.248527387036659775e-01, 6.166821809152076561e-01, + 6.087253820796220127e-01, 6.009689663652322267e-01, + 5.934009016917334289e-01, 5.860103184772680329e-01, + 5.787873586028450257e-01, 5.717230486648258170e-01, + 5.648091929124001709e-01, 5.580382822625874484e-01, + 5.514034165406412891e-01, 5.448982376724396115e-01, + 5.385168720028619127e-01, 5.322538802630433219e-01, + 5.261042139836197284e-01, 5.200631773682335979e-01, + 5.141263938147485613e-01, 5.082897764106428795e-01, + 5.025495018413477233e-01, 4.969019872415495476e-01, + 4.913438695940325340e-01, 4.858719873418849144e-01, + 4.804833639304542103e-01, 4.751751930373773747e-01, + 4.699448252839599771e-01, 4.647897562504261781e-01, + 4.597076156421376902e-01, 4.546961574746155033e-01, + 4.497532511627549967e-01, 4.448768734145485126e-01, + 4.400651008423538957e-01, 4.353161032156365740e-01, + 4.306281372884588343e-01, 4.259995411430343437e-01, + 4.214287289976165751e-01, 4.169141864330028757e-01, + 4.124544659971611793e-01, 4.080481831520323954e-01, + 4.036940125305302773e-01, 3.993906844752310725e-01, + 3.951369818332901573e-01, 3.909317369847971069e-01, + 3.867738290841376547e-01, 3.826621814960098344e-01, + 3.785957594095807899e-01, 3.745735676159021588e-01, + 3.705946484351460013e-01, 3.666580797815141568e-01, + 3.627629733548177748e-01, 3.589084729487497794e-01, + 3.550937528667874599e-01, 3.513180164374833381e-01, + 3.475804946216369817e-01, 3.438804447045024082e-01, + 3.402171490667800224e-01, 3.365899140286776059e-01, + 3.329980687618089852e-01, 3.294409642641363267e-01, + 3.259179723935561879e-01, 3.224284849560891675e-01, + 3.189719128449572394e-01, 3.155476852271289490e-01, + 3.121552487741795501e-01, 3.087940669345601852e-01, + 3.054636192445902565e-01, 3.021634006756935276e-01, + 2.988929210155817917e-01, 2.956517042812611962e-01, + 2.924392881618925744e-01, 2.892552234896777485e-01, + 2.860990737370768255e-01, 2.829704145387807457e-01, + 2.798688332369729248e-01, 2.767939284485173568e-01, + 2.737453096528029706e-01, 2.707225967990600224e-01, + 2.677254199320447947e-01, 2.647534188350622042e-01, + 2.618062426893629779e-01, 2.588835497490162285e-01, + 2.559850070304153791e-01, 2.531102900156294577e-01, + 2.502590823688622956e-01, 2.474310756653276266e-01, + 2.446259691318921070e-01, 2.418434693988772144e-01, + 2.390832902624491774e-01, 2.363451524570596429e-01, + 2.336287834374333461e-01, 2.309339171696274118e-01, + 2.282602939307167011e-01, 2.256076601166840667e-01, + 2.229757680581201940e-01, 2.203643758433594946e-01, + 2.177732471487005272e-01, 2.152021510753786837e-01, + 2.126508619929782795e-01, 2.101191593889882581e-01, + 2.076068277242220372e-01, 2.051136562938377095e-01, + 2.026394390937090173e-01, 2.001839746919112650e-01, + 1.977470661050988732e-01, 1.953285206795632167e-01, + 1.929281499767713515e-01, 1.905457696631953912e-01, + 1.881811994042543179e-01, 1.858342627621971110e-01, + 1.835047870977674633e-01, 1.811926034754962889e-01, + 1.788975465724783054e-01, 1.766194545904948843e-01, + 1.743581691713534942e-01, 1.721135353153200598e-01, + 1.698854013025276610e-01, 1.676736186172501919e-01, + 1.654780418749360049e-01, 1.632985287519018169e-01, + 1.611349399175920349e-01, 1.589871389693142123e-01, + 1.568549923693652315e-01, 1.547383693844680830e-01, + 1.526371420274428570e-01, 1.505511850010398944e-01, + 1.484803756438667910e-01, 1.464245938783449441e-01, + 1.443837221606347754e-01, 1.423576454324722018e-01, + 1.403462510748624548e-01, 1.383494288635802039e-01, + 1.363670709264288572e-01, 1.343990717022136294e-01, + 1.324453279013875218e-01, 1.305057384683307731e-01, + 1.285802045452281717e-01, 1.266686294375106714e-01, + 1.247709185808309612e-01, 1.228869795095451356e-01, + 1.210167218266748335e-01, 1.191600571753276827e-01, + 1.173168992115555670e-01, 1.154871635786335338e-01, + 1.136707678827443141e-01, 1.118676316700562973e-01, + 1.100776764051853845e-01, 1.083008254510337970e-01, + 1.065370040500016602e-01, 1.047861393065701724e-01, + 1.030481601712577161e-01, 1.013229974259536315e-01, + 9.961058367063713170e-02, 9.791085331149219917e-02, + 9.622374255043279756e-02, 9.454918937605585882e-02, + 9.288713355604354127e-02, 9.123751663104015530e-02, + 8.960028191003285847e-02, 8.797537446727021759e-02, + 8.636274114075691288e-02, 8.476233053236811865e-02, + 8.317409300963238272e-02, 8.159798070923741931e-02, + 8.003394754231990538e-02, 7.848194920160642130e-02, + 7.694194317048050347e-02, 7.541388873405840965e-02, + 7.389774699236474620e-02, 7.239348087570873780e-02, + 7.090105516237182881e-02, 6.942043649872875477e-02, + 6.795159342193660135e-02, 6.649449638533977414e-02, + 6.504911778675374900e-02, 6.361543199980733421e-02, + 6.219341540854099459e-02, 6.078304644547963265e-02, + 5.938430563342026597e-02, 5.799717563120065922e-02, + 5.662164128374287675e-02, 5.525768967669703741e-02, + 5.390531019604608703e-02, 5.256449459307169225e-02, + 5.123523705512628146e-02, 4.991753428270637172e-02, + 4.861138557337949667e-02, 4.731679291318154762e-02, + 4.603376107617516977e-02, 4.476229773294328196e-02, + 4.350241356888818328e-02, 4.225412241331623353e-02, + 4.101744138041481941e-02, 3.979239102337412542e-02, + 3.857899550307485742e-02, 3.737728277295936097e-02, + 3.618728478193142251e-02, 3.500903769739741045e-02, + 3.384258215087432992e-02, 3.268796350895953468e-02, + 3.154523217289360859e-02, 3.041444391046660423e-02, + 2.929566022463739317e-02, 2.818894876397863569e-02, + 2.709438378095579969e-02, 2.601204664513421735e-02, + 2.494202641973178314e-02, 2.388442051155817078e-02, + 2.283933540638524023e-02, 2.180688750428358066e-02, + 2.078720407257811723e-02, 1.978042433800974303e-02, + 1.878670074469603046e-02, 1.780620041091136169e-02, + 1.683910682603994777e-02, 1.588562183997316302e-02, + 1.494596801169114850e-02, 1.402039140318193759e-02, + 1.310916493125499106e-02, 1.221259242625538123e-02, + 1.133101359783459695e-02, 1.046481018102997894e-02, + 9.614413642502209895e-03, 8.780314985808975251e-03, + 7.963077438017040002e-03, 7.163353183634983863e-03, + 6.381905937319179087e-03, 5.619642207205483020e-03, + 4.877655983542392333e-03, 4.157295120833795314e-03, + 3.460264777836904049e-03, 2.788798793574076128e-03, + 2.145967743718906265e-03, 1.536299780301572356e-03, + 9.672692823271745359e-04, 4.541343538414967652e-04}; + +static const uint32_t ke_float[] = { + 0x00714851UL, 0x00000000UL, 0x004DF56FUL, 0x0061BBD6UL, 0x006A6EDDUL, + 0x006F44A0UL, 0x00725474UL, 0x00746FF9UL, 0x0075F96FUL, 0x007724D3UL, + 0x00781027UL, 0x0078CDEEUL, 0x00796A2CUL, 0x0079ED08UL, 0x007A5C37UL, + 0x007ABBD7UL, 0x007B0EF4UL, 0x007B57DCUL, 0x007B9853UL, 0x007BD1BBUL, + 0x007C052EUL, 0x007C338CUL, 0x007C5D8EUL, 0x007C83C8UL, 0x007CA6B8UL, + 0x007CC6C6UL, 0x007CE449UL, 0x007CFF8CUL, 0x007D18CDUL, 0x007D3043UL, + 0x007D461DUL, 0x007D5A84UL, 0x007D6D9BUL, 0x007D7F82UL, 0x007D9053UL, + 0x007DA028UL, 0x007DAF15UL, 0x007DBD2DUL, 0x007DCA82UL, 0x007DD722UL, + 0x007DE31CUL, 0x007DEE7CUL, 0x007DF94DUL, 0x007E0399UL, 0x007E0D69UL, + 0x007E16C6UL, 0x007E1FB6UL, 0x007E2842UL, 0x007E306FUL, 0x007E3843UL, + 0x007E3FC4UL, 0x007E46F6UL, 0x007E4DDFUL, 0x007E5481UL, 0x007E5AE2UL, + 0x007E6104UL, 0x007E66ECUL, 0x007E6C9BUL, 0x007E7215UL, 0x007E775DUL, + 0x007E7C76UL, 0x007E8160UL, 0x007E8620UL, 0x007E8AB6UL, 0x007E8F24UL, + 0x007E936DUL, 0x007E9793UL, 0x007E9B95UL, 0x007E9F77UL, 0x007EA33AUL, + 0x007EA6DEUL, 0x007EAA66UL, 0x007EADD1UL, 0x007EB123UL, 0x007EB45AUL, + 0x007EB779UL, 0x007EBA80UL, 0x007EBD71UL, 0x007EC04BUL, 0x007EC310UL, + 0x007EC5C1UL, 0x007EC85EUL, 0x007ECAE9UL, 0x007ECD61UL, 0x007ECFC7UL, + 0x007ED21CUL, 0x007ED460UL, 0x007ED694UL, 0x007ED8B9UL, 0x007EDACEUL, + 0x007EDCD5UL, 0x007EDECEUL, 0x007EE0B8UL, 0x007EE296UL, 0x007EE466UL, + 0x007EE62AUL, 0x007EE7E2UL, 0x007EE98DUL, 0x007EEB2DUL, 0x007EECC1UL, + 0x007EEE4AUL, 0x007EEFC9UL, 0x007EF13DUL, 0x007EF2A7UL, 0x007EF406UL, + 0x007EF55CUL, 0x007EF6A8UL, 0x007EF7EBUL, 0x007EF924UL, 0x007EFA55UL, + 0x007EFB7DUL, 0x007EFC9CUL, 0x007EFDB2UL, 0x007EFEC1UL, 0x007EFFC7UL, + 0x007F00C5UL, 0x007F01BBUL, 0x007F02AAUL, 0x007F0391UL, 0x007F0470UL, + 0x007F0548UL, 0x007F0618UL, 0x007F06E2UL, 0x007F07A4UL, 0x007F0860UL, + 0x007F0914UL, 0x007F09C2UL, 0x007F0A69UL, 0x007F0B09UL, 0x007F0BA3UL, + 0x007F0C36UL, 0x007F0CC2UL, 0x007F0D48UL, 0x007F0DC8UL, 0x007F0E41UL, + 0x007F0EB4UL, 0x007F0F21UL, 0x007F0F88UL, 0x007F0FE8UL, 0x007F1042UL, + 0x007F1096UL, 0x007F10E4UL, 0x007F112BUL, 0x007F116DUL, 0x007F11A8UL, + 0x007F11DDUL, 0x007F120CUL, 0x007F1235UL, 0x007F1258UL, 0x007F1274UL, + 0x007F128AUL, 0x007F129AUL, 0x007F12A4UL, 0x007F12A7UL, 0x007F12A4UL, + 0x007F129BUL, 0x007F128BUL, 0x007F1274UL, 0x007F1257UL, 0x007F1233UL, + 0x007F1209UL, 0x007F11D8UL, 0x007F119FUL, 0x007F1160UL, 0x007F111AUL, + 0x007F10CCUL, 0x007F1077UL, 0x007F101BUL, 0x007F0FB7UL, 0x007F0F4BUL, + 0x007F0ED7UL, 0x007F0E5CUL, 0x007F0DD8UL, 0x007F0D4CUL, 0x007F0CB7UL, + 0x007F0C19UL, 0x007F0B73UL, 0x007F0AC3UL, 0x007F0A0AUL, 0x007F0947UL, + 0x007F087BUL, 0x007F07A4UL, 0x007F06C2UL, 0x007F05D6UL, 0x007F04DFUL, + 0x007F03DCUL, 0x007F02CDUL, 0x007F01B2UL, 0x007F008BUL, 0x007EFF56UL, + 0x007EFE13UL, 0x007EFCC3UL, 0x007EFB64UL, 0x007EF9F6UL, 0x007EF878UL, + 0x007EF6EAUL, 0x007EF54BUL, 0x007EF39AUL, 0x007EF1D6UL, 0x007EEFFFUL, + 0x007EEE14UL, 0x007EEC13UL, 0x007EE9FDUL, 0x007EE7CFUL, 0x007EE589UL, + 0x007EE329UL, 0x007EE0AEUL, 0x007EDE16UL, 0x007EDB61UL, 0x007ED88CUL, + 0x007ED595UL, 0x007ED27BUL, 0x007ECF3BUL, 0x007ECBD3UL, 0x007EC841UL, + 0x007EC481UL, 0x007EC091UL, 0x007EBC6DUL, 0x007EB811UL, 0x007EB37AUL, + 0x007EAEA4UL, 0x007EA988UL, 0x007EA422UL, 0x007E9E6BUL, 0x007E985DUL, + 0x007E91EFUL, 0x007E8B1AUL, 0x007E83D4UL, 0x007E7C11UL, 0x007E73C5UL, + 0x007E6AE1UL, 0x007E6155UL, 0x007E570FUL, 0x007E4BF7UL, 0x007E3FF3UL, + 0x007E32E6UL, 0x007E24ACUL, 0x007E1518UL, 0x007E03F7UL, 0x007DF10AUL, + 0x007DDC03UL, 0x007DC480UL, 0x007DAA09UL, 0x007D8C00UL, 0x007D699AUL, + 0x007D41C9UL, 0x007D131EUL, 0x007CDB97UL, 0x007C9851UL, 0x007C44F8UL, + 0x007BDABCUL, 0x007B4E33UL, 0x007A8A98UL, 0x00796587UL, 0x007777D9UL, + 0x00736D37UL, +}; +static const float we_float[] = { + 1.03677719e-06F, 7.61177108e-09F, 1.24977240e-08F, 1.63680292e-08F, + 1.96847466e-08F, 2.26448404e-08F, 2.53524197e-08F, 2.78699974e-08F, + 3.02384333e-08F, 3.24861032e-08F, 3.46336312e-08F, 3.66965478e-08F, + 3.86868855e-08F, 4.06141855e-08F, 4.24861622e-08F, 4.43091566e-08F, + 4.60884545e-08F, 4.78285168e-08F, 4.95331490e-08F, 5.12056279e-08F, + 5.28488000e-08F, 5.44651557e-08F, 5.60568899e-08F, 5.76259484e-08F, + 5.91740662e-08F, 6.07027987e-08F, 6.22135462e-08F, 6.37075759e-08F, + 6.51860386e-08F, 6.66499836e-08F, 6.81003709e-08F, 6.95380822e-08F, + 7.09639292e-08F, 7.23786618e-08F, 7.37829746e-08F, 7.51775128e-08F, + 7.65628768e-08F, 7.79396272e-08F, 7.93082883e-08F, 8.06693516e-08F, + 8.20232788e-08F, 8.33705045e-08F, 8.47114385e-08F, 8.60464681e-08F, + 8.73759596e-08F, 8.87002606e-08F, 9.00197010e-08F, 9.13345948e-08F, + 9.26452410e-08F, 9.39519249e-08F, 9.52549192e-08F, 9.65544849e-08F, + 9.78508719e-08F, 9.91443202e-08F, 1.00435060e-07F, 1.01723315e-07F, + 1.03009296e-07F, 1.04293211e-07F, 1.05575259e-07F, 1.06855633e-07F, + 1.08134518e-07F, 1.09412096e-07F, 1.10688542e-07F, 1.11964025e-07F, + 1.13238713e-07F, 1.14512767e-07F, 1.15786343e-07F, 1.17059595e-07F, + 1.18332673e-07F, 1.19605723e-07F, 1.20878890e-07F, 1.22152313e-07F, + 1.23426131e-07F, 1.24700479e-07F, 1.25975490e-07F, 1.27251294e-07F, + 1.28528022e-07F, 1.29805799e-07F, 1.31084751e-07F, 1.32365001e-07F, + 1.33646673e-07F, 1.34929886e-07F, 1.36214760e-07F, 1.37501415e-07F, + 1.38789966e-07F, 1.40080532e-07F, 1.41373228e-07F, 1.42668169e-07F, + 1.43965470e-07F, 1.45265245e-07F, 1.46567606e-07F, 1.47872669e-07F, + 1.49180545e-07F, 1.50491348e-07F, 1.51805191e-07F, 1.53122186e-07F, + 1.54442445e-07F, 1.55766083e-07F, 1.57093212e-07F, 1.58423946e-07F, + 1.59758399e-07F, 1.61096684e-07F, 1.62438917e-07F, 1.63785214e-07F, + 1.65135690e-07F, 1.66490462e-07F, 1.67849647e-07F, 1.69213364e-07F, + 1.70581733e-07F, 1.71954874e-07F, 1.73332908e-07F, 1.74715958e-07F, + 1.76104148e-07F, 1.77497602e-07F, 1.78896448e-07F, 1.80300814e-07F, + 1.81710828e-07F, 1.83126623e-07F, 1.84548331e-07F, 1.85976086e-07F, + 1.87410026e-07F, 1.88850288e-07F, 1.90297012e-07F, 1.91750343e-07F, + 1.93210424e-07F, 1.94677403e-07F, 1.96151428e-07F, 1.97632653e-07F, + 1.99121231e-07F, 2.00617321e-07F, 2.02121082e-07F, 2.03632677e-07F, + 2.05152273e-07F, 2.06680040e-07F, 2.08216149e-07F, 2.09760777e-07F, + 2.11314104e-07F, 2.12876312e-07F, 2.14447590e-07F, 2.16028129e-07F, + 2.17618123e-07F, 2.19217773e-07F, 2.20827283e-07F, 2.22446862e-07F, + 2.24076723e-07F, 2.25717086e-07F, 2.27368174e-07F, 2.29030216e-07F, + 2.30703448e-07F, 2.32388110e-07F, 2.34084450e-07F, 2.35792720e-07F, + 2.37513182e-07F, 2.39246101e-07F, 2.40991752e-07F, 2.42750416e-07F, + 2.44522382e-07F, 2.46307948e-07F, 2.48107418e-07F, 2.49921109e-07F, + 2.51749342e-07F, 2.53592452e-07F, 2.55450781e-07F, 2.57324683e-07F, + 2.59214522e-07F, 2.61120673e-07F, 2.63043524e-07F, 2.64983476e-07F, + 2.66940939e-07F, 2.68916342e-07F, 2.70910123e-07F, 2.72922739e-07F, + 2.74954660e-07F, 2.77006373e-07F, 2.79078382e-07F, 2.81171210e-07F, + 2.83285396e-07F, 2.85421503e-07F, 2.87580110e-07F, 2.89761822e-07F, + 2.91967265e-07F, 2.94197089e-07F, 2.96451969e-07F, 2.98732610e-07F, + 3.01039742e-07F, 3.03374127e-07F, 3.05736557e-07F, 3.08127859e-07F, + 3.10548894e-07F, 3.13000563e-07F, 3.15483804e-07F, 3.17999599e-07F, + 3.20548974e-07F, 3.23133003e-07F, 3.25752811e-07F, 3.28409576e-07F, + 3.31104534e-07F, 3.33838984e-07F, 3.36614287e-07F, 3.39431878e-07F, + 3.42293264e-07F, 3.45200034e-07F, 3.48153864e-07F, 3.51156520e-07F, + 3.54209871e-07F, 3.57315892e-07F, 3.60476673e-07F, 3.63694431e-07F, + 3.66971518e-07F, 3.70310433e-07F, 3.73713834e-07F, 3.77184553e-07F, + 3.80725611e-07F, 3.84340234e-07F, 3.88031877e-07F, 3.91804239e-07F, + 3.95661291e-07F, 3.99607304e-07F, 4.03646879e-07F, 4.07784981e-07F, + 4.12026980e-07F, 4.16378695e-07F, 4.20846449e-07F, 4.25437124e-07F, + 4.30158235e-07F, 4.35018005e-07F, 4.40025460e-07F, 4.45190536e-07F, + 4.50524210e-07F, 4.56038644e-07F, 4.61747369e-07F, 4.67665494e-07F, + 4.73809965e-07F, 4.80199879e-07F, 4.86856855e-07F, 4.93805512e-07F, + 5.01074042e-07F, 5.08694944e-07F, 5.16705952e-07F, 5.25151216e-07F, + 5.34082859e-07F, 5.43563016e-07F, 5.53666578e-07F, 5.64484953e-07F, + 5.76131313e-07F, 5.88748108e-07F, 6.02518140e-07F, 6.17681418e-07F, + 6.34561837e-07F, 6.53611496e-07F, 6.75488730e-07F, 7.01206245e-07F, + 7.32441505e-07F, 7.72282898e-07F, 8.27435688e-07F, 9.17567905e-07F, +}; +static const float fe_float[] = { + 1.00000000e+00F, 9.38143681e-01F, 9.00469930e-01F, 8.71704332e-01F, + 8.47785501e-01F, 8.26993297e-01F, 8.08421652e-01F, 7.91527637e-01F, + 7.75956852e-01F, 7.61463389e-01F, 7.47868622e-01F, 7.35038092e-01F, + 7.22867660e-01F, 7.11274761e-01F, 7.00192655e-01F, 6.89566496e-01F, + 6.79350572e-01F, 6.69506317e-01F, 6.60000841e-01F, 6.50805833e-01F, + 6.41896716e-01F, 6.33251994e-01F, 6.24852739e-01F, 6.16682181e-01F, + 6.08725382e-01F, 6.00968966e-01F, 5.93400902e-01F, 5.86010318e-01F, + 5.78787359e-01F, 5.71723049e-01F, 5.64809193e-01F, 5.58038282e-01F, + 5.51403417e-01F, 5.44898238e-01F, 5.38516872e-01F, 5.32253880e-01F, + 5.26104214e-01F, 5.20063177e-01F, 5.14126394e-01F, 5.08289776e-01F, + 5.02549502e-01F, 4.96901987e-01F, 4.91343870e-01F, 4.85871987e-01F, + 4.80483364e-01F, 4.75175193e-01F, 4.69944825e-01F, 4.64789756e-01F, + 4.59707616e-01F, 4.54696157e-01F, 4.49753251e-01F, 4.44876873e-01F, + 4.40065101e-01F, 4.35316103e-01F, 4.30628137e-01F, 4.25999541e-01F, + 4.21428729e-01F, 4.16914186e-01F, 4.12454466e-01F, 4.08048183e-01F, + 4.03694013e-01F, 3.99390684e-01F, 3.95136982e-01F, 3.90931737e-01F, + 3.86773829e-01F, 3.82662181e-01F, 3.78595759e-01F, 3.74573568e-01F, + 3.70594648e-01F, 3.66658080e-01F, 3.62762973e-01F, 3.58908473e-01F, + 3.55093753e-01F, 3.51318016e-01F, 3.47580495e-01F, 3.43880445e-01F, + 3.40217149e-01F, 3.36589914e-01F, 3.32998069e-01F, 3.29440964e-01F, + 3.25917972e-01F, 3.22428485e-01F, 3.18971913e-01F, 3.15547685e-01F, + 3.12155249e-01F, 3.08794067e-01F, 3.05463619e-01F, 3.02163401e-01F, + 2.98892921e-01F, 2.95651704e-01F, 2.92439288e-01F, 2.89255223e-01F, + 2.86099074e-01F, 2.82970415e-01F, 2.79868833e-01F, 2.76793928e-01F, + 2.73745310e-01F, 2.70722597e-01F, 2.67725420e-01F, 2.64753419e-01F, + 2.61806243e-01F, 2.58883550e-01F, 2.55985007e-01F, 2.53110290e-01F, + 2.50259082e-01F, 2.47431076e-01F, 2.44625969e-01F, 2.41843469e-01F, + 2.39083290e-01F, 2.36345152e-01F, 2.33628783e-01F, 2.30933917e-01F, + 2.28260294e-01F, 2.25607660e-01F, 2.22975768e-01F, 2.20364376e-01F, + 2.17773247e-01F, 2.15202151e-01F, 2.12650862e-01F, 2.10119159e-01F, + 2.07606828e-01F, 2.05113656e-01F, 2.02639439e-01F, 2.00183975e-01F, + 1.97747066e-01F, 1.95328521e-01F, 1.92928150e-01F, 1.90545770e-01F, + 1.88181199e-01F, 1.85834263e-01F, 1.83504787e-01F, 1.81192603e-01F, + 1.78897547e-01F, 1.76619455e-01F, 1.74358169e-01F, 1.72113535e-01F, + 1.69885401e-01F, 1.67673619e-01F, 1.65478042e-01F, 1.63298529e-01F, + 1.61134940e-01F, 1.58987139e-01F, 1.56854992e-01F, 1.54738369e-01F, + 1.52637142e-01F, 1.50551185e-01F, 1.48480376e-01F, 1.46424594e-01F, + 1.44383722e-01F, 1.42357645e-01F, 1.40346251e-01F, 1.38349429e-01F, + 1.36367071e-01F, 1.34399072e-01F, 1.32445328e-01F, 1.30505738e-01F, + 1.28580205e-01F, 1.26668629e-01F, 1.24770919e-01F, 1.22886980e-01F, + 1.21016722e-01F, 1.19160057e-01F, 1.17316899e-01F, 1.15487164e-01F, + 1.13670768e-01F, 1.11867632e-01F, 1.10077676e-01F, 1.08300825e-01F, + 1.06537004e-01F, 1.04786139e-01F, 1.03048160e-01F, 1.01322997e-01F, + 9.96105837e-02F, 9.79108533e-02F, 9.62237426e-02F, 9.45491894e-02F, + 9.28871336e-02F, 9.12375166e-02F, 8.96002819e-02F, 8.79753745e-02F, + 8.63627411e-02F, 8.47623305e-02F, 8.31740930e-02F, 8.15979807e-02F, + 8.00339475e-02F, 7.84819492e-02F, 7.69419432e-02F, 7.54138887e-02F, + 7.38977470e-02F, 7.23934809e-02F, 7.09010552e-02F, 6.94204365e-02F, + 6.79515934e-02F, 6.64944964e-02F, 6.50491178e-02F, 6.36154320e-02F, + 6.21934154e-02F, 6.07830464e-02F, 5.93843056e-02F, 5.79971756e-02F, + 5.66216413e-02F, 5.52576897e-02F, 5.39053102e-02F, 5.25644946e-02F, + 5.12352371e-02F, 4.99175343e-02F, 4.86113856e-02F, 4.73167929e-02F, + 4.60337611e-02F, 4.47622977e-02F, 4.35024136e-02F, 4.22541224e-02F, + 4.10174414e-02F, 3.97923910e-02F, 3.85789955e-02F, 3.73772828e-02F, + 3.61872848e-02F, 3.50090377e-02F, 3.38425822e-02F, 3.26879635e-02F, + 3.15452322e-02F, 3.04144439e-02F, 2.92956602e-02F, 2.81889488e-02F, + 2.70943838e-02F, 2.60120466e-02F, 2.49420264e-02F, 2.38844205e-02F, + 2.28393354e-02F, 2.18068875e-02F, 2.07872041e-02F, 1.97804243e-02F, + 1.87867007e-02F, 1.78062004e-02F, 1.68391068e-02F, 1.58856218e-02F, + 1.49459680e-02F, 1.40203914e-02F, 1.31091649e-02F, 1.22125924e-02F, + 1.13310136e-02F, 1.04648102e-02F, 9.61441364e-03F, 8.78031499e-03F, + 7.96307744e-03F, 7.16335318e-03F, 6.38190594e-03F, 5.61964221e-03F, + 4.87765598e-03F, 4.15729512e-03F, 3.46026478e-03F, 2.78879879e-03F, + 2.14596774e-03F, 1.53629978e-03F, 9.67269282e-04F, 4.54134354e-04F, +}; + + +static const double ziggurat_nor_r = 3.6541528853610087963519472518; +static const double ziggurat_nor_inv_r = + 0.27366123732975827203338247596; // 1.0 / ziggurat_nor_r; +static const double ziggurat_exp_r = 7.6971174701310497140446280481; + +static const float ziggurat_nor_r_f = 3.6541528853610087963519472518f; +static const float ziggurat_nor_inv_r_f = 0.27366123732975827203338247596f; +static const float ziggurat_exp_r_f = 7.6971174701310497140446280481f; diff --git a/numpy/random/src/legacy/legacy-distributions.c b/numpy/random/src/legacy/legacy-distributions.c new file mode 100644 index 000000000000..443c1a4bf781 --- /dev/null +++ b/numpy/random/src/legacy/legacy-distributions.c @@ -0,0 +1,497 @@ +/* + * This file contains generation code for distribution that have been modified + * since Generator was introduced. These are preserved using identical code + * to what was in NumPy 1.16 so that the stream of values generated by + * RandomState is not changed when there are changes that affect Generator. + * + * These functions should not be changed except if they contain code that + * cannot be compiled. They should not be changed for bug fixes, performance + * improvements that can change the values produced, or enhancements to precision. + */ +#include "include/legacy-distributions.h" + + +static NPY_INLINE double legacy_double(aug_bitgen_t *aug_state) { + return aug_state->bit_generator->next_double(aug_state->bit_generator->state); +} + +double legacy_gauss(aug_bitgen_t *aug_state) { + if (aug_state->has_gauss) { + const double temp = aug_state->gauss; + aug_state->has_gauss = false; + aug_state->gauss = 0.0; + return temp; + } else { + double f, x1, x2, r2; + + do { + x1 = 2.0 * legacy_double(aug_state) - 1.0; + x2 = 2.0 * legacy_double(aug_state) - 1.0; + r2 = x1 * x1 + x2 * x2; + } while (r2 >= 1.0 || r2 == 0.0); + + /* Polar method, a more efficient version of the Box-Muller approach. */ + f = sqrt(-2.0 * log(r2) / r2); + /* Keep for next call */ + aug_state->gauss = f * x1; + aug_state->has_gauss = true; + return f * x2; + } +} + +double legacy_standard_exponential(aug_bitgen_t *aug_state) { + /* We use -log(1-U) since U is [0, 1) */ + return -log(1.0 - legacy_double(aug_state)); +} + +double legacy_standard_gamma(aug_bitgen_t *aug_state, double shape) { + double b, c; + double U, V, X, Y; + + if (shape == 1.0) { + return legacy_standard_exponential(aug_state); + } + else if (shape == 0.0) { + return 0.0; + } else if (shape < 1.0) { + for (;;) { + U = legacy_double(aug_state); + V = legacy_standard_exponential(aug_state); + if (U <= 1.0 - shape) { + X = pow(U, 1. / shape); + if (X <= V) { + return X; + } + } else { + Y = -log((1 - U) / shape); + X = pow(1.0 - shape + shape * Y, 1. / shape); + if (X <= (V + Y)) { + return X; + } + } + } + } else { + b = shape - 1. / 3.; + c = 1. / sqrt(9 * b); + for (;;) { + do { + X = legacy_gauss(aug_state); + V = 1.0 + c * X; + } while (V <= 0.0); + + V = V * V * V; + U = legacy_double(aug_state); + if (U < 1.0 - 0.0331 * (X * X) * (X * X)) + return (b * V); + if (log(U) < 0.5 * X * X + b * (1. - V + log(V))) + return (b * V); + } + } +} + +double legacy_gamma(aug_bitgen_t *aug_state, double shape, double scale) { + return scale * legacy_standard_gamma(aug_state, shape); +} + +double legacy_pareto(aug_bitgen_t *aug_state, double a) { + return exp(legacy_standard_exponential(aug_state) / a) - 1; +} + +double legacy_weibull(aug_bitgen_t *aug_state, double a) { + if (a == 0.0) { + return 0.0; + } + return pow(legacy_standard_exponential(aug_state), 1. / a); +} + +double legacy_power(aug_bitgen_t *aug_state, double a) { + return pow(1 - exp(-legacy_standard_exponential(aug_state)), 1. / a); +} + +double legacy_chisquare(aug_bitgen_t *aug_state, double df) { + return 2.0 * legacy_standard_gamma(aug_state, df / 2.0); +} + +double legacy_rayleigh(bitgen_t *bitgen_state, double mode) { + return mode * sqrt(-2.0 * npy_log1p(-next_double(bitgen_state))); +} + +double legacy_noncentral_chisquare(aug_bitgen_t *aug_state, double df, + double nonc) { + double out; + if (nonc == 0) { + return legacy_chisquare(aug_state, df); + } + if (1 < df) { + const double Chi2 = legacy_chisquare(aug_state, df - 1); + const double n = legacy_gauss(aug_state) + sqrt(nonc); + return Chi2 + n * n; + } else { + const long i = random_poisson(aug_state->bit_generator, nonc / 2.0); + out = legacy_chisquare(aug_state, df + 2 * i); + /* Insert nan guard here to avoid changing the stream */ + if (npy_isnan(nonc)){ + return NPY_NAN; + } else { + return out; + } + } +} + +double legacy_noncentral_f(aug_bitgen_t *aug_state, double dfnum, double dfden, + double nonc) { + double t = legacy_noncentral_chisquare(aug_state, dfnum, nonc) * dfden; + return t / (legacy_chisquare(aug_state, dfden) * dfnum); +} + +double legacy_wald(aug_bitgen_t *aug_state, double mean, double scale) { + double U, X, Y; + double mu_2l; + + mu_2l = mean / (2 * scale); + Y = legacy_gauss(aug_state); + Y = mean * Y * Y; + X = mean + mu_2l * (Y - sqrt(4 * scale * Y + Y * Y)); + U = legacy_double(aug_state); + if (U <= mean / (mean + X)) { + return X; + } else { + return mean * mean / X; + } +} + +double legacy_normal(aug_bitgen_t *aug_state, double loc, double scale) { + return loc + scale * legacy_gauss(aug_state); +} + +double legacy_lognormal(aug_bitgen_t *aug_state, double mean, double sigma) { + return exp(legacy_normal(aug_state, mean, sigma)); +} + +double legacy_standard_t(aug_bitgen_t *aug_state, double df) { + double num, denom; + + num = legacy_gauss(aug_state); + denom = legacy_standard_gamma(aug_state, df / 2); + return sqrt(df / 2) * num / sqrt(denom); +} + +int64_t legacy_negative_binomial(aug_bitgen_t *aug_state, double n, double p) { + double Y = legacy_gamma(aug_state, n, (1 - p) / p); + return (int64_t)random_poisson(aug_state->bit_generator, Y); +} + +double legacy_standard_cauchy(aug_bitgen_t *aug_state) { + return legacy_gauss(aug_state) / legacy_gauss(aug_state); +} + +double legacy_beta(aug_bitgen_t *aug_state, double a, double b) { + double Ga, Gb; + + if ((a <= 1.0) && (b <= 1.0)) { + double U, V, X, Y; + /* Use Johnk's algorithm */ + + while (1) { + U = legacy_double(aug_state); + V = legacy_double(aug_state); + X = pow(U, 1.0 / a); + Y = pow(V, 1.0 / b); + + if ((X + Y) <= 1.0) { + if (X + Y > 0) { + return X / (X + Y); + } else { + double logX = log(U) / a; + double logY = log(V) / b; + double logM = logX > logY ? logX : logY; + logX -= logM; + logY -= logM; + + return exp(logX - log(exp(logX) + exp(logY))); + } + } + } + } else { + Ga = legacy_standard_gamma(aug_state, a); + Gb = legacy_standard_gamma(aug_state, b); + return Ga / (Ga + Gb); + } +} + +double legacy_f(aug_bitgen_t *aug_state, double dfnum, double dfden) { + return ((legacy_chisquare(aug_state, dfnum) * dfden) / + (legacy_chisquare(aug_state, dfden) * dfnum)); +} + +double legacy_exponential(aug_bitgen_t *aug_state, double scale) { + return scale * legacy_standard_exponential(aug_state); +} + + +static RAND_INT_TYPE legacy_random_binomial_original(bitgen_t *bitgen_state, + double p, + RAND_INT_TYPE n, + binomial_t *binomial) { + double q; + + if (p <= 0.5) { + if (p * n <= 30.0) { + return random_binomial_inversion(bitgen_state, n, p, binomial); + } else { + return random_binomial_btpe(bitgen_state, n, p, binomial); + } + } else { + q = 1.0 - p; + if (q * n <= 30.0) { + return n - random_binomial_inversion(bitgen_state, n, q, binomial); + } else { + return n - random_binomial_btpe(bitgen_state, n, q, binomial); + } + } +} + + +int64_t legacy_random_binomial(bitgen_t *bitgen_state, double p, + int64_t n, binomial_t *binomial) { + return (int64_t) legacy_random_binomial_original(bitgen_state, p, + (RAND_INT_TYPE) n, + binomial); +} + + +static RAND_INT_TYPE random_hypergeometric_hyp(bitgen_t *bitgen_state, + RAND_INT_TYPE good, + RAND_INT_TYPE bad, + RAND_INT_TYPE sample) { + RAND_INT_TYPE d1, k, z; + double d2, u, y; + + d1 = bad + good - sample; + d2 = (double)MIN(bad, good); + + y = d2; + k = sample; + while (y > 0.0) { + u = next_double(bitgen_state); + y -= (RAND_INT_TYPE)floor(u + y / (d1 + k)); + k--; + if (k == 0) + break; + } + z = (RAND_INT_TYPE)(d2 - y); + if (good > bad) + z = sample - z; + return z; +} + +/* D1 = 2*sqrt(2/e) */ +/* D2 = 3 - 2*sqrt(3/e) */ +#define D1 1.7155277699214135 +#define D2 0.8989161620588988 +static RAND_INT_TYPE random_hypergeometric_hrua(bitgen_t *bitgen_state, + RAND_INT_TYPE good, + RAND_INT_TYPE bad, + RAND_INT_TYPE sample) { + RAND_INT_TYPE mingoodbad, maxgoodbad, popsize, m, d9; + double d4, d5, d6, d7, d8, d10, d11; + RAND_INT_TYPE Z; + double T, W, X, Y; + + mingoodbad = MIN(good, bad); + popsize = good + bad; + maxgoodbad = MAX(good, bad); + m = MIN(sample, popsize - sample); + d4 = ((double)mingoodbad) / popsize; + d5 = 1.0 - d4; + d6 = m * d4 + 0.5; + d7 = sqrt((double)(popsize - m) * sample * d4 * d5 / (popsize - 1) + 0.5); + d8 = D1 * d7 + D2; + d9 = (RAND_INT_TYPE)floor((double)(m + 1) * (mingoodbad + 1) / (popsize + 2)); + d10 = (random_loggam(d9 + 1) + random_loggam(mingoodbad - d9 + 1) + + random_loggam(m - d9 + 1) + random_loggam(maxgoodbad - m + d9 + 1)); + d11 = MIN(MIN(m, mingoodbad) + 1.0, floor(d6 + 16 * d7)); + /* 16 for 16-decimal-digit precision in D1 and D2 */ + + while (1) { + X = next_double(bitgen_state); + Y = next_double(bitgen_state); + W = d6 + d8 * (Y - 0.5) / X; + + /* fast rejection: */ + if ((W < 0.0) || (W >= d11)) + continue; + + Z = (RAND_INT_TYPE)floor(W); + T = d10 - (random_loggam(Z + 1) + random_loggam(mingoodbad - Z + 1) + + random_loggam(m - Z + 1) + random_loggam(maxgoodbad - m + Z + 1)); + + /* fast acceptance: */ + if ((X * (4.0 - X) - 3.0) <= T) + break; + + /* fast rejection: */ + if (X * (X - T) >= 1) + continue; + /* log(0.0) is ok here, since always accept */ + if (2.0 * log(X) <= T) + break; /* acceptance */ + } + + /* this is a correction to HRUA* by Ivan Frohne in rv.py */ + if (good > bad) + Z = m - Z; + + /* another fix from rv.py to allow sample to exceed popsize/2 */ + if (m < sample) + Z = good - Z; + + return Z; +} +#undef D1 +#undef D2 + +static RAND_INT_TYPE random_hypergeometric_original(bitgen_t *bitgen_state, + RAND_INT_TYPE good, + RAND_INT_TYPE bad, + RAND_INT_TYPE sample) +{ + if (sample > 10) { + return random_hypergeometric_hrua(bitgen_state, good, bad, sample); + } else if (sample > 0) { + return random_hypergeometric_hyp(bitgen_state, good, bad, sample); + } else { + return 0; + } +} + + +/* + * This is a wrapper function that matches the expected template. In the legacy + * generator, all int types are long, so this accepts int64 and then converts + * them to longs. These values must be in bounds for long and this is checked + * outside this function + * + * The remaining are included for the return type only + */ +int64_t legacy_random_hypergeometric(bitgen_t *bitgen_state, int64_t good, + int64_t bad, int64_t sample) { + return (int64_t)random_hypergeometric_original(bitgen_state, + (RAND_INT_TYPE)good, + (RAND_INT_TYPE)bad, + (RAND_INT_TYPE)sample); +} + + +int64_t legacy_random_poisson(bitgen_t *bitgen_state, double lam) { + return (int64_t)random_poisson(bitgen_state, lam); +} + +int64_t legacy_random_zipf(bitgen_t *bitgen_state, double a) { + return (int64_t)random_zipf(bitgen_state, a); +} + + +static long legacy_geometric_inversion(bitgen_t *bitgen_state, double p) { + return (long)ceil(npy_log1p(-next_double(bitgen_state)) / log(1 - p)); +} + +int64_t legacy_random_geometric(bitgen_t *bitgen_state, double p) { + if (p >= 0.333333333333333333333333) { + return (int64_t)random_geometric_search(bitgen_state, p); + } else { + return (int64_t)legacy_geometric_inversion(bitgen_state, p); + } +} + +void legacy_random_multinomial(bitgen_t *bitgen_state, RAND_INT_TYPE n, + RAND_INT_TYPE *mnix, double *pix, npy_intp d, + binomial_t *binomial) { + return random_multinomial(bitgen_state, n, mnix, pix, d, binomial); +} + +double legacy_vonmises(bitgen_t *bitgen_state, double mu, double kappa) { + double s; + double U, V, W, Y, Z; + double result, mod; + int neg; + if (npy_isnan(kappa)) { + return NPY_NAN; + } + if (kappa < 1e-8) { + return M_PI * (2 * next_double(bitgen_state) - 1); + } else { + /* with double precision rho is zero until 1.4e-8 */ + if (kappa < 1e-5) { + /* + * second order taylor expansion around kappa = 0 + * precise until relatively large kappas as second order is 0 + */ + s = (1. / kappa + kappa); + } else { + /* Path for 1e-5 <= kappa <= 1e6 */ + double r = 1 + sqrt(1 + 4 * kappa * kappa); + double rho = (r - sqrt(2 * r)) / (2 * kappa); + s = (1 + rho * rho) / (2 * rho); + } + + while (1) { + U = next_double(bitgen_state); + Z = cos(M_PI * U); + W = (1 + s * Z) / (s + Z); + Y = kappa * (s - W); + V = next_double(bitgen_state); + /* + * V==0.0 is ok here since Y >= 0 always leads + * to accept, while Y < 0 always rejects + */ + if ((Y * (2 - Y) - V >= 0) || (log(Y / V) + 1 - Y >= 0)) { + break; + } + } + + U = next_double(bitgen_state); + + result = acos(W); + if (U < 0.5) { + result = -result; + } + result += mu; + neg = (result < 0); + mod = fabs(result); + mod = (fmod(mod + M_PI, 2 * M_PI) - M_PI); + if (neg) { + mod *= -1; + } + + return mod; + } +} + +int64_t legacy_logseries(bitgen_t *bitgen_state, double p) { + double q, r, U, V; + long result; + + r = log(1.0 - p); + + while (1) { + V = next_double(bitgen_state); + if (V >= p) { + return 1; + } + U = next_double(bitgen_state); + q = 1.0 - exp(r * U); + if (V <= q * q) { + result = (long)floor(1 + log(V) / log(q)); + if ((result < 1) || (V == 0.0)) { + continue; + } else { + return (int64_t)result; + } + } + if (V >= q) { + return 1; + } + return 2; + } +} \ No newline at end of file diff --git a/numpy/random/src/mt19937/LICENSE.md b/numpy/random/src/mt19937/LICENSE.md new file mode 100644 index 000000000000..f65c3d46e624 --- /dev/null +++ b/numpy/random/src/mt19937/LICENSE.md @@ -0,0 +1,61 @@ +# MT19937 + +Copyright (c) 2003-2005, Jean-Sebastien Roy (js@jeannot.org) + +The rk_random and rk_seed functions algorithms and the original design of +the Mersenne Twister RNG: + + Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. The names of its contributors may not be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Original algorithm for the implementation of rk_interval function from +Richard J. Wagner's implementation of the Mersenne Twister RNG, optimised by +Magnus Jonsson. + +Constants used in the rk_double implementation by Isaku Wada. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/numpy/random/src/mt19937/mt19937-benchmark.c b/numpy/random/src/mt19937/mt19937-benchmark.c new file mode 100644 index 000000000000..039f8030af65 --- /dev/null +++ b/numpy/random/src/mt19937/mt19937-benchmark.c @@ -0,0 +1,31 @@ +/* + * cl mt19937-benchmark.c mt19937.c /Ox + * Measure-Command { .\mt19937-benchmark.exe } + * + * gcc mt19937-benchmark.c mt19937.c -O3 -o mt19937-benchmark + * time ./mt19937-benchmark + */ +#include "mt19937.h" +#include <inttypes.h> +#include <stdio.h> +#include <time.h> + +#define Q 1000000000 + +int main() { + int i; + uint32_t seed = 0x0; + uint64_t sum = 0, count = 0; + mt19937_state state; + mt19937_seed(&state, seed); + clock_t begin = clock(); + for (i = 0; i < Q; i++) { + sum += mt19937_next64(&state); + count++; + } + clock_t end = clock(); + double time_spent = (double)(end - begin) / CLOCKS_PER_SEC; + printf("0x%" PRIx64 "\ncount: %" PRIu64 "\n", sum, count); + printf("%" PRIu64 " randoms per second\n", + (uint64_t)(Q / time_spent) / 1000000 * 1000000); +} diff --git a/numpy/random/src/mt19937/mt19937-jump.c b/numpy/random/src/mt19937/mt19937-jump.c new file mode 100644 index 000000000000..1a83a4c2e23b --- /dev/null +++ b/numpy/random/src/mt19937/mt19937-jump.c @@ -0,0 +1,114 @@ +#include "mt19937-jump.h" +#include "mt19937.h" + +/* 32-bits function */ +/* return the i-th coefficient of the polynomial pf */ +unsigned long get_coef(unsigned long *pf, unsigned int deg) { + if ((pf[deg >> 5] & (LSB << (deg & 0x1ful))) != 0) + return (1); + else + return (0); +} + +void copy_state(mt19937_state *target_state, mt19937_state *state) { + int i; + + for (i = 0; i < N; i++) + target_state->key[i] = state->key[i]; + + target_state->pos = state->pos; +} + +/* next state generating function */ +void gen_next(mt19937_state *state) { + int num; + unsigned long y; + static unsigned long mag02[2] = {0x0ul, MATRIX_A}; + + num = state->pos; + if (num < N - M) { + y = (state->key[num] & UPPER_MASK) | (state->key[num + 1] & LOWER_MASK); + state->key[num] = state->key[num + M] ^ (y >> 1) ^ mag02[y % 2]; + state->pos++; + } else if (num < N - 1) { + y = (state->key[num] & UPPER_MASK) | (state->key[num + 1] & LOWER_MASK); + state->key[num] = state->key[num + (M - N)] ^ (y >> 1) ^ mag02[y % 2]; + state->pos++; + } else if (num == N - 1) { + y = (state->key[N - 1] & UPPER_MASK) | (state->key[0] & LOWER_MASK); + state->key[N - 1] = state->key[M - 1] ^ (y >> 1) ^ mag02[y % 2]; + state->pos = 0; + } +} + +void add_state(mt19937_state *state1, mt19937_state *state2) { + int i, pt1 = state1->pos, pt2 = state2->pos; + + if (pt2 - pt1 >= 0) { + for (i = 0; i < N - pt2; i++) + state1->key[i + pt1] ^= state2->key[i + pt2]; + for (; i < N - pt1; i++) + state1->key[i + pt1] ^= state2->key[i + (pt2 - N)]; + for (; i < N; i++) + state1->key[i + (pt1 - N)] ^= state2->key[i + (pt2 - N)]; + } else { + for (i = 0; i < N - pt1; i++) + state1->key[i + pt1] ^= state2->key[i + pt2]; + for (; i < N - pt2; i++) + state1->key[i + (pt1 - N)] ^= state2->key[i + pt2]; + for (; i < N; i++) + state1->key[i + (pt1 - N)] ^= state2->key[i + (pt2 - N)]; + } +} + +/* compute pf(ss) using standard Horner method */ +void horner1(unsigned long *pf, mt19937_state *state) { + int i = MEXP - 1; + mt19937_state *temp; + + temp = (mt19937_state *)calloc(1, sizeof(mt19937_state)); + + while (get_coef(pf, i) == 0) + i--; + + if (i > 0) { + copy_state(temp, state); + gen_next(temp); + i--; + for (; i > 0; i--) { + if (get_coef(pf, i) != 0) + add_state(temp, state); + else + ; + gen_next(temp); + } + if (get_coef(pf, 0) != 0) + add_state(temp, state); + else + ; + } else if (i == 0) + copy_state(temp, state); + else + ; + + copy_state(state, temp); + free(temp); +} + +void mt19937_jump_state(mt19937_state *state) { + unsigned long *pf; + int i; + + pf = (unsigned long *)calloc(P_SIZE, sizeof(unsigned long)); + for (i = 0; i<P_SIZE; i++) { + pf[i] = poly_coef[i]; + } + + if (state->pos >= N) { + state->pos = 0; + } + + horner1(pf, state); + + free(pf); +} diff --git a/numpy/random/src/mt19937/mt19937-jump.h b/numpy/random/src/mt19937/mt19937-jump.h new file mode 100644 index 000000000000..8371cbd5fc5b --- /dev/null +++ b/numpy/random/src/mt19937/mt19937-jump.h @@ -0,0 +1,151 @@ +#pragma once +#include "mt19937.h" +#include <stdlib.h> + +/* parameters for computing Jump */ +#define W_SIZE 32 /* size of unsigned long */ +#define MEXP 19937 +#define P_SIZE ((MEXP / W_SIZE) + 1) +#define LSB 0x00000001UL +#define QQ 7 +#define LL 128 /* LL = 2^(QQ) */ + +void mt19937_jump_state(mt19937_state *state); + +void set_coef(unsigned long *pf, unsigned int deg, unsigned long v); + +/* + * 2**128 step polynomial produced using the file mt19937-generate-jump-poly.c + * (randomgen) which is a modified version of minipoly_mt19937.c as distributed + * in + * http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/JUMP/jump_ahead_1.02.tar.gz + * + * These files are not part of NumPy. + */ + +static const unsigned long poly_coef[624] = { + 1927166307UL, 3044056772UL, 2284297142UL, 2820929765UL, 651705945UL, + 69149273UL, 3892165397UL, 2337412983UL, 1219880790UL, 3207074517UL, + 3836784057UL, 189286826UL, 1049791363UL, 3916249550UL, 2942382547UL, + 166392552UL, 861176918UL, 3246476411UL, 2302311555UL, 4273801148UL, + 29196903UL, 1363664063UL, 3802562022UL, 2600400244UL, 3090369801UL, + 4040416970UL, 1432485208UL, 3632558139UL, 4015816763UL, 3013316418UL, + 551532385UL, 3592224467UL, 3479125595UL, 1195467127UL, 2391032553UL, + 2393493419UL, 1482493632UL, 1625159565UL, 748389672UL, 4042774030UL, + 2998615036UL, 3393119101UL, 2177492569UL, 2265897321UL, 2507383006UL, + 3461498961UL, 2003319700UL, 1942857197UL, 1455226044UL, 4097545580UL, + 529653268UL, 3204756480UL, 2486748289UL, 495294513UL, 3396001954UL, + 2643963605UL, 2655404568UL, 3881604377UL, 624710790UL, 3443737948UL, + 1941294296UL, 2139259604UL, 3368734020UL, 422436761UL, 3602810182UL, + 1384691081UL, 3035786407UL, 2551797119UL, 537227499UL, 65486120UL, + 642436100UL, 2023822537UL, 2515598203UL, 1122953367UL, 2882306242UL, + 1743213032UL, 321965189UL, 336496623UL, 2436602518UL, 3556266590UL, + 1055117829UL, 463541647UL, 743234441UL, 527083645UL, 2606668346UL, + 2274046499UL, 2761475053UL, 2760669048UL, 2538258534UL, 487125077UL, + 3365962306UL, 3604906217UL, 2714700608UL, 680709708UL, 2217161159UL, + 1614899374UL, 3710119533UL, 3201300658UL, 3752620679UL, 2755041105UL, + 3129723037UL, 1247297753UL, 2812642690UL, 4114340845UL, 3485092247UL, + 2752814364UL, 3586551747UL, 4073138437UL, 3462966585UL, 2924318358UL, + 4061374901UL, 3314086806UL, 2640385723UL, 744590670UL, 3007586513UL, + 3959120371UL, 997207767UL, 3420235506UL, 2092400998UL, 3190305685UL, + 60965738UL, 549507222UL, 3784354415UL, 3209279509UL, 1238863299UL, + 2605037827UL, 178570440UL, 1743491299UL, 4079686640UL, 2136795825UL, + 3435430548UL, 1679732443UL, 1835708342UL, 2159367000UL, 1924487218UL, + 4059723674UL, 996192116UL, 2308091645UL, 1336281586UL, 674600050UL, + 1642572529UL, 1383973289UL, 2202960007UL, 3165481279UL, 3385474038UL, + 2501318550UL, 2671842890UL, 3084085109UL, 3475033915UL, 1551329147UL, + 4101397249UL, 1205851807UL, 3641536021UL, 3607635071UL, 1609126163UL, + 2910426664UL, 3324508658UL, 4244311266UL, 254034382UL, 1258304384UL, + 1914048768UL, 1358592011UL, 527610138UL, 3072108727UL, 4289413885UL, + 1417001678UL, 2445445945UL, 896462712UL, 339855811UL, 3699378285UL, + 2529457297UL, 3049459401UL, 2723472429UL, 2838633181UL, 2520397330UL, + 3272339035UL, 1667003847UL, 3742634787UL, 942706520UL, 2301027215UL, + 1907791250UL, 2306299096UL, 1021173342UL, 1539334516UL, 2907834628UL, + 3199959207UL, 1556251860UL, 3642580275UL, 2355865416UL, 285806145UL, + 867932457UL, 1177354172UL, 3291107470UL, 4022765061UL, 1613380116UL, + 588147929UL, 650574324UL, 1236855601UL, 1371354511UL, 2085218212UL, + 1203081931UL, 420526905UL, 1022192219UL, 2903287064UL, 2470845899UL, + 3649873273UL, 2502333582UL, 3972385637UL, 4246356763UL, 199084157UL, + 1567178788UL, 2107121836UL, 4293612856UL, 1902910177UL, 332397359UL, + 83422598UL, 3614961721UL, 456321943UL, 2277615967UL, 2302518510UL, + 3258315116UL, 2521897172UL, 3900282042UL, 4186973154UL, 3146532165UL, + 2299685029UL, 3889120948UL, 1293301857UL, 187455105UL, 3395849230UL, + 913321567UL, 3093513909UL, 1440944571UL, 1923481911UL, 338680924UL, + 1204882963UL, 2739724491UL, 2886241328UL, 2408907774UL, 1299817192UL, + 2474012871UL, 45400213UL, 553186784UL, 134558656UL, 2180943666UL, + 2870807589UL, 76511085UL, 3053566760UL, 2516601415UL, 4172865902UL, + 1751297915UL, 1251975234UL, 2964780642UL, 1412975316UL, 2739978478UL, + 2171013719UL, 637935041UL, 975972384UL, 3044407449UL, 3111425639UL, + 1938684970UL, 2860857400UL, 13419586UL, 2772079268UL, 3484375614UL, + 3184054178UL, 159924837UL, 1386213021UL, 2765617231UL, 2523689118UL, + 1283505218UL, 3510789588UL, 4125878259UL, 2990287597UL, 2152014833UL, + 3084155970UL, 2815101609UL, 1932985704UL, 114887365UL, 1712687646UL, + 2550515629UL, 3299051916UL, 2022747614UL, 2143630992UL, 2244188960UL, + 3309469192UL, 3234358520UL, 800720365UL, 3278176634UL, 554357439UL, + 2415629802UL, 1620877315UL, 2389462898UL, 2229691332UL, 1007748450UL, + 1966873768UL, 2264971043UL, 1214524156UL, 346854700UL, 3471905342UL, + 3984889660UL, 4034246840UL, 216712649UL, 4027196762UL, 3754772604UL, + 2121785562UL, 2347070732UL, 7457687UL, 1443375102UL, 683948143UL, + 2940226032UL, 3211475670UL, 2836507357UL, 774899409UL, 1588968308UL, + 780438009UL, 3278878781UL, 2217181540UL, 2184194887UL, 1642129086UL, + 69346830UL, 297114710UL, 3841068188UL, 2631265450UL, 4167492314UL, + 2613519651UL, 1388582503UL, 2171556668UL, 1201873758UL, 2698772382UL, + 207791958UL, 3936134563UL, 3725025702UL, 3306317801UL, 1055730422UL, + 4069230694UL, 1767821343UL, 4252407395UL, 2422583118UL, 3158834399UL, + 3754582617UL, 1112422556UL, 376187931UL, 3137549150UL, 712221089UL, + 3300799453UL, 3868250200UL, 1165257666UL, 2494837767UL, 131304831UL, + 1619349427UL, 1958236644UL, 3678218946UL, 3651007751UL, 2261987899UL, + 1567368524UL, 2193599522UL, 3034394674UL, 2994602555UL, 3072727647UL, + 889094521UL, 1089692095UL, 1822324824UL, 3876999182UL, 1703361286UL, + 902229515UL, 4213728487UL, 3838170364UL, 672727494UL, 2240733828UL, + 3858539469UL, 1149254245UL, 4166055926UL, 4193525313UL, 1709921593UL, + 2278290377UL, 3190784116UL, 2919588882UL, 1012709717UL, 3640562031UL, + 2931984863UL, 3515665246UL, 250577343UL, 1147230194UL, 1183856202UL, + 3734511989UL, 3243867808UL, 3499383067UL, 2985115159UL, 2036821626UL, + 3298159553UL, 2726542838UL, 1686910320UL, 1778823772UL, 965412224UL, + 233509772UL, 3843098861UL, 1312622954UL, 500855830UL, 2950562091UL, + 1915683607UL, 3405781138UL, 596073719UL, 2195150546UL, 3381728478UL, + 546426436UL, 3527890868UL, 2324975353UL, 2241074266UL, 3992514859UL, + 2576108287UL, 4077653225UL, 2632319392UL, 3127212632UL, 917000669UL, + 2498161805UL, 3980835128UL, 2259526768UL, 1083920509UL, 1187452089UL, + 97018536UL, 3056075838UL, 2059706760UL, 2373335692UL, 182196406UL, + 2136713111UL, 1762080153UL, 1572125803UL, 1145919955UL, 1023966754UL, + 3921694345UL, 1632005969UL, 1418372326UL, 354407429UL, 2438288265UL, + 1620072033UL, 1586320921UL, 1044153697UL, 969324572UL, 613487980UL, + 4230993062UL, 397726764UL, 2194259193UL, 735511759UL, 2066049260UL, + 88093248UL, 1562536153UL, 2114157419UL, 3630951546UL, 589238503UL, + 3120654384UL, 2521793793UL, 2746692127UL, 2557723425UL, 889897693UL, + 2778878177UL, 643269509UL, 3342389831UL, 19218890UL, 3442706236UL, + 3314581273UL, 3503147052UL, 1546343434UL, 1448529060UL, 529038801UL, + 2748942264UL, 2213019208UL, 111314040UL, 2488697563UL, 1180642808UL, + 2605272289UL, 4207476668UL, 1502558669UL, 2972370981UL, 4204339995UL, + 1046225278UL, 992840610UL, 3847290298UL, 2387673094UL, 2221565747UL, + 1045901716UL, 3997739302UL, 1556952765UL, 1103336648UL, 279418400UL, + 2711316466UL, 2336215718UL, 2317900806UL, 974624729UL, 909575434UL, + 1675610631UL, 1922393214UL, 2054896570UL, 3197007361UL, 3932554569UL, + 1008619802UL, 3349254938UL, 113511461UL, 932630384UL, 2098759268UL, + 3436837432UL, 3119972401UL, 1612590197UL, 2281609013UL, 4174211248UL, + 4016332246UL, 2097525539UL, 1398632760UL, 1543697535UL, 2419227174UL, + 1676465074UL, 2882923045UL, 23216933UL, 808195649UL, 3690720147UL, + 484419260UL, 2254772642UL, 2975434733UL, 288528113UL, 204598404UL, + 589968818UL, 3021152400UL, 2463155141UL, 1397846755UL, 157285579UL, + 4230258857UL, 2469135246UL, 625357422UL, 3435224647UL, 465239124UL, + 1022535736UL, 2823317040UL, 274194469UL, 2214966446UL, 3661001613UL, + 518802547UL, 2293436304UL, 1335881988UL, 2247010176UL, 1856732584UL, + 1088028094UL, 1877563709UL, 1015352636UL, 1700817932UL, 2960695857UL, + 1882229300UL, 1666906557UL, 1838841022UL, 3983797810UL, 1667630361UL, + 385998221UL, 241341791UL, 403550441UL, 2629200403UL, 3552759102UL, + 2029750442UL, 2247999048UL, 2726665298UL, 2507798776UL, 2419064129UL, + 1266444923UL, 526255242UL, 2384866697UL, 1886200981UL, 3954956408UL, + 2171436866UL, 2295200753UL, 1047315850UL, 1967809707UL, 2860382973UL, + 3918334466UL, 3057439479UL, 952682588UL, 1925559679UL, 3112119050UL, + 3833190964UL, 1430139895UL, 2089165610UL, 3009202424UL, 3989186157UL, + 3395807230UL, 347600520UL, 120428923UL, 3017004655UL, 1384933954UL, + 303039929UL, 234010146UL, 2278760249UL, 315514836UL, 3987659575UL, + 1239335668UL, 2387869477UL, 3885908826UL, 1983922602UL, 698609264UL, + 3009002846UL, 1520611399UL, 809159940UL, 3089771783UL, 374838722UL, + 2789914419UL, 2500831937UL, 3751970335UL, 4279852547UL, 2362894437UL, + 1588814060UL, 1671213155UL, 434218829UL, 2126587176UL, 2002526422UL, + 2756464095UL, 141700479UL, 2965974322UL, 2211530172UL, 992085992UL, + 1943691492UL, 2705131817UL, 2519208889UL, 1938768395UL, 3949294294UL, + 354046666UL, 2158272751UL, 602858583UL, 0UL}; diff --git a/numpy/random/src/mt19937/mt19937-test-data-gen.c b/numpy/random/src/mt19937/mt19937-test-data-gen.c new file mode 100644 index 000000000000..4f4ec1d6458d --- /dev/null +++ b/numpy/random/src/mt19937/mt19937-test-data-gen.c @@ -0,0 +1,59 @@ +/* + * Generate testing csv files + * + * cl mt19937-test-data-gen.c randomkit.c + * -IC:\Anaconda\Lib\site-packages\numpy\core\include -IC:\Anaconda\include + * Advapi32.lib Kernel32.lib C:\Anaconda\libs\python36.lib -DRK_NO_WINCRYPT=1 + * + */ +#include "randomkit.h" +#include <inttypes.h> +#include <stdio.h> + +#define N 1000 + +int main() { + uint64_t sum = 0; + uint32_t seed = 0xDEADBEAF; + int i; + rk_state state; + rk_seed(seed, &state); + uint64_t store[N]; + for (i = 0; i < N; i++) { + store[i] = (uint64_t)rk_random(&state); + } + + FILE *fp; + fp = fopen("mt19937-testset-1.csv", "w"); + if (fp == NULL) { + printf("Couldn't open file\n"); + return -1; + } + fprintf(fp, "seed, 0x%" PRIx32 "\n", seed); + for (i = 0; i < N; i++) { + fprintf(fp, "%d, 0x%" PRIx64 "\n", i, store[i]); + if (i == 999) { + printf("%d, 0x%" PRIx64 "\n", i, store[i]); + } + } + fclose(fp); + + seed = 0; + rk_seed(seed, &state); + for (i = 0; i < N; i++) { + store[i] = (uint64_t)rk_random(&state); + } + fp = fopen("mt19937-testset-2.csv", "w"); + if (fp == NULL) { + printf("Couldn't open file\n"); + return -1; + } + fprintf(fp, "seed, 0x%" PRIx32 "\n", seed); + for (i = 0; i < N; i++) { + fprintf(fp, "%d, 0x%" PRIx64 "\n", i, store[i]); + if (i == 999) { + printf("%d, 0x%" PRIx64 "\n", i, store[i]); + } + } + fclose(fp); +} diff --git a/numpy/random/src/mt19937/mt19937.c b/numpy/random/src/mt19937/mt19937.c new file mode 100644 index 000000000000..bec518af8059 --- /dev/null +++ b/numpy/random/src/mt19937/mt19937.c @@ -0,0 +1,106 @@ +#include "mt19937.h" +#include "mt19937-jump.h" + +void mt19937_seed(mt19937_state *state, uint32_t seed) { + int pos; + seed &= 0xffffffffUL; + + /* Knuth's PRNG as used in the Mersenne Twister reference implementation */ + for (pos = 0; pos < RK_STATE_LEN; pos++) { + state->key[pos] = seed; + seed = (1812433253UL * (seed ^ (seed >> 30)) + pos + 1) & 0xffffffffUL; + } + state->pos = RK_STATE_LEN; +} + +/* initializes mt[RK_STATE_LEN] with a seed */ +static void init_genrand(mt19937_state *state, uint32_t s) { + int mti; + uint32_t *mt = state->key; + + mt[0] = s & 0xffffffffUL; + for (mti = 1; mti < RK_STATE_LEN; mti++) { + /* + * See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. + * In the previous versions, MSBs of the seed affect + * only MSBs of the array mt[]. + * 2002/01/09 modified by Makoto Matsumoto + */ + mt[mti] = (1812433253UL * (mt[mti - 1] ^ (mt[mti - 1] >> 30)) + mti); + /* for > 32 bit machines */ + mt[mti] &= 0xffffffffUL; + } + state->pos = mti; + return; +} + +/* + * initialize by an array with array-length + * init_key is the array for initializing keys + * key_length is its length + */ +void mt19937_init_by_array(mt19937_state *state, uint32_t *init_key, + int key_length) { + /* was signed in the original code. RDH 12/16/2002 */ + int i = 1; + int j = 0; + uint32_t *mt = state->key; + int k; + + init_genrand(state, 19650218UL); + k = (RK_STATE_LEN > key_length ? RK_STATE_LEN : key_length); + for (; k; k--) { + /* non linear */ + mt[i] = (mt[i] ^ ((mt[i - 1] ^ (mt[i - 1] >> 30)) * 1664525UL)) + + init_key[j] + j; + /* for > 32 bit machines */ + mt[i] &= 0xffffffffUL; + i++; + j++; + if (i >= RK_STATE_LEN) { + mt[0] = mt[RK_STATE_LEN - 1]; + i = 1; + } + if (j >= key_length) { + j = 0; + } + } + for (k = RK_STATE_LEN - 1; k; k--) { + mt[i] = (mt[i] ^ ((mt[i - 1] ^ (mt[i - 1] >> 30)) * 1566083941UL)) - + i; /* non linear */ + mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */ + i++; + if (i >= RK_STATE_LEN) { + mt[0] = mt[RK_STATE_LEN - 1]; + i = 1; + } + } + + mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */ +} + +void mt19937_gen(mt19937_state *state) { + uint32_t y; + int i; + + for (i = 0; i < N - M; i++) { + y = (state->key[i] & UPPER_MASK) | (state->key[i + 1] & LOWER_MASK); + state->key[i] = state->key[i + M] ^ (y >> 1) ^ (-(y & 1) & MATRIX_A); + } + for (; i < N - 1; i++) { + y = (state->key[i] & UPPER_MASK) | (state->key[i + 1] & LOWER_MASK); + state->key[i] = state->key[i + (M - N)] ^ (y >> 1) ^ (-(y & 1) & MATRIX_A); + } + y = (state->key[N - 1] & UPPER_MASK) | (state->key[0] & LOWER_MASK); + state->key[N - 1] = state->key[M - 1] ^ (y >> 1) ^ (-(y & 1) & MATRIX_A); + + state->pos = 0; +} + +extern inline uint64_t mt19937_next64(mt19937_state *state); + +extern inline uint32_t mt19937_next32(mt19937_state *state); + +extern inline double mt19937_next_double(mt19937_state *state); + +void mt19937_jump(mt19937_state *state) { mt19937_jump_state(state); } diff --git a/numpy/random/src/mt19937/mt19937.h b/numpy/random/src/mt19937/mt19937.h new file mode 100644 index 000000000000..1b39e0b6434c --- /dev/null +++ b/numpy/random/src/mt19937/mt19937.h @@ -0,0 +1,61 @@ +#pragma once +#include <math.h> +#include <stdint.h> + +#ifdef _WIN32 +#define inline __forceinline +#endif + +#define RK_STATE_LEN 624 + +#define N 624 +#define M 397 +#define MATRIX_A 0x9908b0dfUL +#define UPPER_MASK 0x80000000UL +#define LOWER_MASK 0x7fffffffUL + +typedef struct s_mt19937_state { + uint32_t key[RK_STATE_LEN]; + int pos; +} mt19937_state; + +extern void mt19937_seed(mt19937_state *state, uint32_t seed); + +extern void mt19937_gen(mt19937_state *state); + +/* Slightly optimized reference implementation of the Mersenne Twister */ +static inline uint32_t mt19937_next(mt19937_state *state) { + uint32_t y; + + if (state->pos == RK_STATE_LEN) { + // Move to function to help inlining + mt19937_gen(state); + } + y = state->key[state->pos++]; + + /* Tempering */ + y ^= (y >> 11); + y ^= (y << 7) & 0x9d2c5680UL; + y ^= (y << 15) & 0xefc60000UL; + y ^= (y >> 18); + + return y; +} + +extern void mt19937_init_by_array(mt19937_state *state, uint32_t *init_key, + int key_length); + +static inline uint64_t mt19937_next64(mt19937_state *state) { + return (uint64_t)mt19937_next(state) << 32 | mt19937_next(state); +} + +static inline uint32_t mt19937_next32(mt19937_state *state) { + return mt19937_next(state); +} + +static inline double mt19937_next_double(mt19937_state *state) { + int32_t a = mt19937_next(state) >> 5, b = mt19937_next(state) >> 6; + return (a * 67108864.0 + b) / 9007199254740992.0; +} + +void mt19937_jump(mt19937_state *state); diff --git a/numpy/random/src/mt19937/randomkit.c b/numpy/random/src/mt19937/randomkit.c new file mode 100644 index 000000000000..f8ed4b49e2fd --- /dev/null +++ b/numpy/random/src/mt19937/randomkit.c @@ -0,0 +1,578 @@ +/* Random kit 1.3 */ + +/* + * Copyright (c) 2003-2005, Jean-Sebastien Roy (js@jeannot.org) + * + * The rk_random and rk_seed functions algorithms and the original design of + * the Mersenne Twister RNG: + * + * Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. The names of its contributors may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Original algorithm for the implementation of rk_interval function from + * Richard J. Wagner's implementation of the Mersenne Twister RNG, optimised by + * Magnus Jonsson. + * + * Constants used in the rk_double implementation by Isaku Wada. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* static char const rcsid[] = + "@(#) $Jeannot: randomkit.c,v 1.28 2005/07/21 22:14:09 js Exp $"; */ + +#ifdef _WIN32 +/* + * Windows + * XXX: we have to use this ugly defined(__GNUC__) because it is not easy to + * detect the compiler used in distutils itself + */ +#if (defined(__GNUC__) && defined(NPY_NEEDS_MINGW_TIME_WORKAROUND)) + +/* + * FIXME: ideally, we should set this to the real version of MSVCRT. We need + * something higher than 0x601 to enable _ftime64 and co + */ +#define __MSVCRT_VERSION__ 0x0700 +#include <sys/timeb.h> +#include <time.h> + +/* + * mingw msvcr lib import wrongly export _ftime, which does not exist in the + * actual msvc runtime for version >= 8; we make it an alias to _ftime64, which + * is available in those versions of the runtime + */ +#define _FTIME(x) _ftime64((x)) +#else +#include <sys/timeb.h> +#include <time.h> + +#define _FTIME(x) _ftime((x)) +#endif + +#ifndef RK_NO_WINCRYPT +/* Windows crypto */ +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0400 +#endif +#include <wincrypt.h> +#include <windows.h> + +#endif + +/* + * Do not move this include. randomkit.h must be included + * after windows timeb.h is included. + */ +#include "randomkit.h" + +#else +/* Unix */ +#include "randomkit.h" +#include <sys/time.h> +#include <time.h> +#include <unistd.h> + +#endif + +#include <assert.h> +#include <errno.h> +#include <limits.h> +#include <math.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> + +#ifndef RK_DEV_URANDOM +#define RK_DEV_URANDOM "/dev/urandom" +#endif + +#ifndef RK_DEV_RANDOM +#define RK_DEV_RANDOM "/dev/random" +#endif + +char *rk_strerror[RK_ERR_MAX] = {"no error", "random device unvavailable"}; + +/* static functions */ +static unsigned long rk_hash(unsigned long key); + +void rk_seed(unsigned long seed, rk_state *state) { + int pos; + seed &= 0xffffffffUL; + + /* Knuth's PRNG as used in the Mersenne Twister reference implementation */ + for (pos = 0; pos < RK_STATE_LEN; pos++) { + state->key[pos] = seed; + seed = (1812433253UL * (seed ^ (seed >> 30)) + pos + 1) & 0xffffffffUL; + } + state->pos = RK_STATE_LEN; + state->gauss = 0; + state->has_gauss = 0; + state->has_binomial = 0; +} + +/* Thomas Wang 32 bits integer hash function */ +unsigned long rk_hash(unsigned long key) { + key += ~(key << 15); + key ^= (key >> 10); + key += (key << 3); + key ^= (key >> 6); + key += ~(key << 11); + key ^= (key >> 16); + return key; +} + +rk_error rk_randomseed(rk_state *state) { +#ifndef _WIN32 + struct timeval tv; +#else + struct _timeb tv; +#endif + int i; + + if (rk_devfill(state->key, sizeof(state->key), 0) == RK_NOERR) { + /* ensures non-zero key */ + state->key[0] |= 0x80000000UL; + state->pos = RK_STATE_LEN; + state->gauss = 0; + state->has_gauss = 0; + state->has_binomial = 0; + + for (i = 0; i < 624; i++) { + state->key[i] &= 0xffffffffUL; + } + return RK_NOERR; + } + +#ifndef _WIN32 + gettimeofday(&tv, NULL); + rk_seed(rk_hash(getpid()) ^ rk_hash(tv.tv_sec) ^ rk_hash(tv.tv_usec) ^ + rk_hash(clock()), + state); +#else + _FTIME(&tv); + rk_seed(rk_hash(tv.time) ^ rk_hash(tv.millitm) ^ rk_hash(clock()), state); +#endif + + return RK_ENODEV; +} + +/* Magic Mersenne Twister constants */ +#define N 624 +#define M 397 +#define MATRIX_A 0x9908b0dfUL +#define UPPER_MASK 0x80000000UL +#define LOWER_MASK 0x7fffffffUL + +/* + * Slightly optimised reference implementation of the Mersenne Twister + * Note that regardless of the precision of long, only 32 bit random + * integers are produced + */ +unsigned long rk_random(rk_state *state) { + unsigned long y; + + if (state->pos == RK_STATE_LEN) { + int i; + + for (i = 0; i < N - M; i++) { + y = (state->key[i] & UPPER_MASK) | (state->key[i + 1] & LOWER_MASK); + state->key[i] = state->key[i + M] ^ (y >> 1) ^ (-(y & 1) & MATRIX_A); + } + for (; i < N - 1; i++) { + y = (state->key[i] & UPPER_MASK) | (state->key[i + 1] & LOWER_MASK); + state->key[i] = + state->key[i + (M - N)] ^ (y >> 1) ^ (-(y & 1) & MATRIX_A); + } + y = (state->key[N - 1] & UPPER_MASK) | (state->key[0] & LOWER_MASK); + state->key[N - 1] = state->key[M - 1] ^ (y >> 1) ^ (-(y & 1) & MATRIX_A); + + state->pos = 0; + } + y = state->key[state->pos++]; + + /* Tempering */ + y ^= (y >> 11); + y ^= (y << 7) & 0x9d2c5680UL; + y ^= (y << 15) & 0xefc60000UL; + y ^= (y >> 18); + + return y; +} + +/* + * Returns an unsigned 64 bit random integer. + */ +NPY_INLINE static npy_uint64 rk_uint64(rk_state *state) { + npy_uint64 upper = (npy_uint64)rk_random(state) << 32; + npy_uint64 lower = (npy_uint64)rk_random(state); + return upper | lower; +} + +/* + * Returns an unsigned 32 bit random integer. + */ +NPY_INLINE static npy_uint32 rk_uint32(rk_state *state) { + return (npy_uint32)rk_random(state); +} + +/* + * Fills an array with cnt random npy_uint64 between off and off + rng + * inclusive. The numbers wrap if rng is sufficiently large. + */ +void rk_random_uint64(npy_uint64 off, npy_uint64 rng, npy_intp cnt, + npy_uint64 *out, rk_state *state) { + npy_uint64 val, mask = rng; + npy_intp i; + + if (rng == 0) { + for (i = 0; i < cnt; i++) { + out[i] = off; + } + return; + } + + /* Smallest bit mask >= max */ + mask |= mask >> 1; + mask |= mask >> 2; + mask |= mask >> 4; + mask |= mask >> 8; + mask |= mask >> 16; + mask |= mask >> 32; + + for (i = 0; i < cnt; i++) { + if (rng <= 0xffffffffUL) { + while ((val = (rk_uint32(state) & mask)) > rng) + ; + } else { + while ((val = (rk_uint64(state) & mask)) > rng) + ; + } + out[i] = off + val; + } +} + +/* + * Fills an array with cnt random npy_uint32 between off and off + rng + * inclusive. The numbers wrap if rng is sufficiently large. + */ +void rk_random_uint32(npy_uint32 off, npy_uint32 rng, npy_intp cnt, + npy_uint32 *out, rk_state *state) { + npy_uint32 val, mask = rng; + npy_intp i; + + if (rng == 0) { + for (i = 0; i < cnt; i++) { + out[i] = off; + } + return; + } + + /* Smallest bit mask >= max */ + mask |= mask >> 1; + mask |= mask >> 2; + mask |= mask >> 4; + mask |= mask >> 8; + mask |= mask >> 16; + + for (i = 0; i < cnt; i++) { + while ((val = (rk_uint32(state) & mask)) > rng) + ; + out[i] = off + val; + } +} + +/* + * Fills an array with cnt random npy_uint16 between off and off + rng + * inclusive. The numbers wrap if rng is sufficiently large. + */ +void rk_random_uint16(npy_uint16 off, npy_uint16 rng, npy_intp cnt, + npy_uint16 *out, rk_state *state) { + npy_uint16 val, mask = rng; + npy_intp i; + npy_uint32 buf; + int bcnt = 0; + + if (rng == 0) { + for (i = 0; i < cnt; i++) { + out[i] = off; + } + return; + } + + /* Smallest bit mask >= max */ + mask |= mask >> 1; + mask |= mask >> 2; + mask |= mask >> 4; + mask |= mask >> 8; + + for (i = 0; i < cnt; i++) { + do { + if (!bcnt) { + buf = rk_uint32(state); + bcnt = 1; + } else { + buf >>= 16; + bcnt--; + } + val = (npy_uint16)buf & mask; + } while (val > rng); + out[i] = off + val; + } +} + +/* + * Fills an array with cnt random npy_uint8 between off and off + rng + * inclusive. The numbers wrap if rng is sufficiently large. + */ +void rk_random_uint8(npy_uint8 off, npy_uint8 rng, npy_intp cnt, npy_uint8 *out, + rk_state *state) { + npy_uint8 val, mask = rng; + npy_intp i; + npy_uint32 buf; + int bcnt = 0; + + if (rng == 0) { + for (i = 0; i < cnt; i++) { + out[i] = off; + } + return; + } + + /* Smallest bit mask >= max */ + mask |= mask >> 1; + mask |= mask >> 2; + mask |= mask >> 4; + + for (i = 0; i < cnt; i++) { + do { + if (!bcnt) { + buf = rk_uint32(state); + bcnt = 3; + } else { + buf >>= 8; + bcnt--; + } + val = (npy_uint8)buf & mask; + } while (val > rng); + out[i] = off + val; + } +} + +/* + * Fills an array with cnt random npy_bool between off and off + rng + * inclusive. + */ +void rk_random_bool(npy_bool off, npy_bool rng, npy_intp cnt, npy_bool *out, + rk_state *state) { + npy_intp i; + npy_uint32 buf; + int bcnt = 0; + + if (rng == 0) { + for (i = 0; i < cnt; i++) { + out[i] = off; + } + return; + } + + /* If we reach here rng and mask are one and off is zero */ + assert(rng == 1 && off == 0); + for (i = 0; i < cnt; i++) { + if (!bcnt) { + buf = rk_uint32(state); + bcnt = 31; + } else { + buf >>= 1; + bcnt--; + } + out[i] = (buf & 0x00000001) != 0; + } +} + +long rk_long(rk_state *state) { return rk_ulong(state) >> 1; } + +unsigned long rk_ulong(rk_state *state) { +#if ULONG_MAX <= 0xffffffffUL + return rk_random(state); +#else + return (rk_random(state) << 32) | (rk_random(state)); +#endif +} + +unsigned long rk_interval(unsigned long max, rk_state *state) { + unsigned long mask = max, value; + + if (max == 0) { + return 0; + } + /* Smallest bit mask >= max */ + mask |= mask >> 1; + mask |= mask >> 2; + mask |= mask >> 4; + mask |= mask >> 8; + mask |= mask >> 16; +#if ULONG_MAX > 0xffffffffUL + mask |= mask >> 32; +#endif + + /* Search a random value in [0..mask] <= max */ +#if ULONG_MAX > 0xffffffffUL + if (max <= 0xffffffffUL) { + while ((value = (rk_random(state) & mask)) > max) + ; + } else { + while ((value = (rk_ulong(state) & mask)) > max) + ; + } +#else + while ((value = (rk_ulong(state) & mask)) > max) + ; +#endif + return value; +} + +double rk_double(rk_state *state) { + /* shifts : 67108864 = 0x4000000, 9007199254740992 = 0x20000000000000 */ + long a = rk_random(state) >> 5, b = rk_random(state) >> 6; + return (a * 67108864.0 + b) / 9007199254740992.0; +} + +void rk_fill(void *buffer, size_t size, rk_state *state) { + unsigned long r; + unsigned char *buf = buffer; + + for (; size >= 4; size -= 4) { + r = rk_random(state); + *(buf++) = r & 0xFF; + *(buf++) = (r >> 8) & 0xFF; + *(buf++) = (r >> 16) & 0xFF; + *(buf++) = (r >> 24) & 0xFF; + } + + if (!size) { + return; + } + r = rk_random(state); + for (; size; r >>= 8, size--) { + *(buf++) = (unsigned char)(r & 0xFF); + } +} + +rk_error rk_devfill(void *buffer, size_t size, int strong) { +#ifndef _WIN32 + FILE *rfile; + int done; + + if (strong) { + rfile = fopen(RK_DEV_RANDOM, "rb"); + } else { + rfile = fopen(RK_DEV_URANDOM, "rb"); + } + if (rfile == NULL) { + return RK_ENODEV; + } + done = fread(buffer, size, 1, rfile); + fclose(rfile); + if (done) { + return RK_NOERR; + } +#else + +#ifndef RK_NO_WINCRYPT + HCRYPTPROV hCryptProv; + BOOL done; + + if (!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT) || + !hCryptProv) { + return RK_ENODEV; + } + done = CryptGenRandom(hCryptProv, size, (unsigned char *)buffer); + CryptReleaseContext(hCryptProv, 0); + if (done) { + return RK_NOERR; + } +#endif + +#endif + return RK_ENODEV; +} + +rk_error rk_altfill(void *buffer, size_t size, int strong, rk_state *state) { + rk_error err; + + err = rk_devfill(buffer, size, strong); + if (err) { + rk_fill(buffer, size, state); + } + return err; +} + +double rk_gauss(rk_state *state) { + if (state->has_gauss) { + const double tmp = state->gauss; + state->gauss = 0; + state->has_gauss = 0; + return tmp; + } else { + double f, x1, x2, r2; + + do { + x1 = 2.0 * rk_double(state) - 1.0; + x2 = 2.0 * rk_double(state) - 1.0; + r2 = x1 * x1 + x2 * x2; + } while (r2 >= 1.0 || r2 == 0.0); + + /* Polar method, a more efficient version of the Box-Muller approach. */ + f = sqrt(-2.0 * log(r2) / r2); + /* Keep for next call */ + state->gauss = f * x1; + state->has_gauss = 1; + return f * x2; + } +} diff --git a/numpy/random/mtrand/randomkit.h b/numpy/random/src/mt19937/randomkit.h similarity index 86% rename from numpy/random/mtrand/randomkit.h rename to numpy/random/src/mt19937/randomkit.h index a24dabebfed7..abb082cb2ed8 100644 --- a/numpy/random/mtrand/randomkit.h +++ b/numpy/random/src/mt19937/randomkit.h @@ -59,50 +59,47 @@ #ifndef _RANDOMKIT_ #define _RANDOMKIT_ -#include <stddef.h> #include <numpy/npy_common.h> - +#include <stddef.h> #define RK_STATE_LEN 624 -typedef struct rk_state_ -{ - unsigned long key[RK_STATE_LEN]; - int pos; - int has_gauss; /* !=0: gauss contains a gaussian deviate */ - double gauss; - - /* The rk_state structure has been extended to store the following - * information for the binomial generator. If the input values of n or p - * are different than nsave and psave, then the other parameters will be - * recomputed. RTK 2005-09-02 */ - - int has_binomial; /* !=0: following parameters initialized for - binomial */ - double psave; - long nsave; - double r; - double q; - double fm; - long m; - double p1; - double xm; - double xl; - double xr; - double c; - double laml; - double lamr; - double p2; - double p3; - double p4; - -} -rk_state; +typedef struct rk_state_ { + unsigned long key[RK_STATE_LEN]; + int pos; + int has_gauss; /* !=0: gauss contains a gaussian deviate */ + double gauss; + + /* The rk_state structure has been extended to store the following + * information for the binomial generator. If the input values of n or p + * are different than nsave and psave, then the other parameters will be + * recomputed. RTK 2005-09-02 */ + + int has_binomial; /* !=0: following parameters initialized for + binomial */ + double psave; + long nsave; + double r; + double q; + double fm; + long m; + double p1; + double xm; + double xl; + double xr; + double c; + double laml; + double lamr; + double p2; + double p3; + double p4; + +} rk_state; typedef enum { - RK_NOERR = 0, /* no error */ - RK_ENODEV = 1, /* no RK_DEV_RANDOM device */ - RK_ERR_MAX = 2 + RK_NOERR = 0, /* no error */ + RK_ENODEV = 1, /* no RK_DEV_RANDOM device */ + RK_ERR_MAX = 2 } rk_error; /* error strings */ @@ -212,7 +209,7 @@ extern rk_error rk_devfill(void *buffer, size_t size, int strong); * Returns RK_ENODEV if the device is unavailable, or RK_NOERR if it is */ extern rk_error rk_altfill(void *buffer, size_t size, int strong, - rk_state *state); + rk_state *state); /* * return a random gaussian deviate with variance unity and zero mean. diff --git a/numpy/random/src/pcg64/LICENSE.md b/numpy/random/src/pcg64/LICENSE.md new file mode 100644 index 000000000000..7aac7a51c96a --- /dev/null +++ b/numpy/random/src/pcg64/LICENSE.md @@ -0,0 +1,22 @@ +# PCG64 + +## The MIT License + +PCG Random Number Generation for C. + +Copyright 2014 Melissa O'Neill <oneill@pcg-random.org> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/numpy/random/src/pcg64/pcg64-benchmark.c b/numpy/random/src/pcg64/pcg64-benchmark.c new file mode 100644 index 000000000000..76f3ec78c300 --- /dev/null +++ b/numpy/random/src/pcg64/pcg64-benchmark.c @@ -0,0 +1,42 @@ +/* + * cl pcg64-benchmark.c pcg64.c ../splitmix64/splitmix64.c /Ox + * Measure-Command { .\xoroshiro128-benchmark.exe } + * + * gcc pcg64-benchmark.c pcg64.c ../splitmix64/splitmix64.c -O3 -o + * pcg64-benchmark + * time ./pcg64-benchmark + */ +#include "../splitmix64/splitmix64.h" +#include "pcg64.h" +#include <inttypes.h> +#include <stdio.h> +#include <time.h> + +#define N 1000000000 + +int main() { + pcg64_random_t rng; + uint64_t sum = 0, count = 0; + uint64_t seed = 0xDEADBEAF; + int i; +#if __SIZEOF_INT128__ && !defined(PCG_FORCE_EMULATED_128BIT_MATH) + rng.state = (__uint128_t)splitmix64_next(&seed) << 64; + rng.state |= splitmix64_next(&seed); + rng.inc = (__uint128_t)1; +#else + rng.state.high = splitmix64_next(&seed); + rng.state.low = splitmix64_next(&seed); + rng.inc.high = 0; + rng.inc.low = 1; +#endif + clock_t begin = clock(); + for (i = 0; i < N; i++) { + sum += pcg64_random_r(&rng); + count++; + } + clock_t end = clock(); + double time_spent = (double)(end - begin) / CLOCKS_PER_SEC; + printf("0x%" PRIx64 "\ncount: %" PRIu64 "\n", sum, count); + printf("%" PRIu64 " randoms per second\n", + (uint64_t)(N / time_spent) / 1000000 * 1000000); +} diff --git a/numpy/random/src/pcg64/pcg64-test-data-gen.c b/numpy/random/src/pcg64/pcg64-test-data-gen.c new file mode 100644 index 000000000000..0c2b079a3e15 --- /dev/null +++ b/numpy/random/src/pcg64/pcg64-test-data-gen.c @@ -0,0 +1,73 @@ +/* + * Generate testing csv files + * + * GCC only + * + * gcc pcg64-test-data-gen.c pcg64.orig.c ../splitmix64/splitmix64.c -o + * pgc64-test-data-gen + */ + +#include "pcg64.orig.h" +#include <inttypes.h> +#include <stdio.h> + +#define N 1000 + +int main() { + pcg64_random_t rng; + uint64_t state, seed = 0xDEADBEAF; + state = seed; + __uint128_t temp, s, inc; + int i; + uint64_t store[N]; + s = (__uint128_t)seed; + inc = (__uint128_t)0; + pcg64_srandom_r(&rng, s, inc); + printf("0x%" PRIx64, (uint64_t)(rng.state >> 64)); + printf("%" PRIx64 "\n", (uint64_t)rng.state); + printf("0x%" PRIx64, (uint64_t)(rng.inc >> 64)); + printf("%" PRIx64 "\n", (uint64_t)rng.inc); + for (i = 0; i < N; i++) { + store[i] = pcg64_random_r(&rng); + } + + FILE *fp; + fp = fopen("pcg64-testset-1.csv", "w"); + if (fp == NULL) { + printf("Couldn't open file\n"); + return -1; + } + fprintf(fp, "seed, 0x%" PRIx64 "\n", seed); + for (i = 0; i < N; i++) { + fprintf(fp, "%d, 0x%" PRIx64 "\n", i, store[i]); + if (i == 999) { + printf("%d, 0x%" PRIx64 "\n", i, store[i]); + } + } + fclose(fp); + + state = seed = 0; + s = (__uint128_t)seed; + i = (__uint128_t)0; + pcg64_srandom_r(&rng, s, i); + printf("0x%" PRIx64, (uint64_t)(rng.state >> 64)); + printf("%" PRIx64 "\n", (uint64_t)rng.state); + printf("0x%" PRIx64, (uint64_t)(rng.inc >> 64)); + printf("%" PRIx64 "\n", (uint64_t)rng.inc); + for (i = 0; i < N; i++) { + store[i] = pcg64_random_r(&rng); + } + fp = fopen("pcg64-testset-2.csv", "w"); + if (fp == NULL) { + printf("Couldn't open file\n"); + return -1; + } + fprintf(fp, "seed, 0x%" PRIx64 "\n", seed); + for (i = 0; i < N; i++) { + fprintf(fp, "%d, 0x%" PRIx64 "\n", i, store[i]); + if (i == 999) { + printf("%d, 0x%" PRIx64 "\n", i, store[i]); + } + } + fclose(fp); +} diff --git a/numpy/random/src/pcg64/pcg64.c b/numpy/random/src/pcg64/pcg64.c new file mode 100644 index 000000000000..b9be1e39d350 --- /dev/null +++ b/numpy/random/src/pcg64/pcg64.c @@ -0,0 +1,204 @@ +/* + * PCG64 Random Number Generation for C. + * + * Copyright 2014 Melissa O'Neill <oneill@pcg-random.org> + * Copyright 2015 Robert Kern <robert.kern@gmail.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For additional information about the PCG random number generation scheme, + * including its license and other licensing options, visit + * + * http://www.pcg-random.org + * + * Relicensed MIT in May 2019 + * + * The MIT License + * + * PCG Random Number Generation for C. + * + * Copyright 2014 Melissa O'Neill <oneill@pcg-random.org> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pcg64.h" + +extern inline void pcg_setseq_128_step_r(pcg_state_setseq_128 *rng); +extern inline uint64_t pcg_output_xsl_rr_128_64(pcg128_t state); +extern inline void pcg_setseq_128_srandom_r(pcg_state_setseq_128 *rng, + pcg128_t initstate, + pcg128_t initseq); +extern inline uint64_t +pcg_setseq_128_xsl_rr_64_random_r(pcg_state_setseq_128 *rng); +extern inline uint64_t +pcg_setseq_128_xsl_rr_64_boundedrand_r(pcg_state_setseq_128 *rng, + uint64_t bound); +extern inline void pcg_setseq_128_advance_r(pcg_state_setseq_128 *rng, + pcg128_t delta); +extern inline uint64_t pcg_cm_random_r(pcg_state_setseq_128 *rng); +extern inline void pcg_cm_step_r(pcg_state_setseq_128 *rng); +extern inline uint64_t pcg_output_cm_128_64(pcg128_t state); +extern inline void pcg_cm_srandom_r(pcg_state_setseq_128 *rng, pcg128_t initstate, pcg128_t initseq); + +/* Multi-step advance functions (jump-ahead, jump-back) + * + * The method used here is based on Brown, "Random Number Generation + * with Arbitrary Stride,", Transactions of the American Nuclear + * Society (Nov. 1994). The algorithm is very similar to fast + * exponentiation. + * + * Even though delta is an unsigned integer, we can pass a + * signed integer to go backwards, it just goes "the long way round". + */ + +#ifndef PCG_EMULATED_128BIT_MATH + +pcg128_t pcg_advance_lcg_128(pcg128_t state, pcg128_t delta, pcg128_t cur_mult, + pcg128_t cur_plus) { + pcg128_t acc_mult = 1u; + pcg128_t acc_plus = 0u; + while (delta > 0) { + if (delta & 1) { + acc_mult *= cur_mult; + acc_plus = acc_plus * cur_mult + cur_plus; + } + cur_plus = (cur_mult + 1) * cur_plus; + cur_mult *= cur_mult; + delta /= 2; + } + return acc_mult * state + acc_plus; +} + +#else + +pcg128_t pcg_advance_lcg_128(pcg128_t state, pcg128_t delta, pcg128_t cur_mult, + pcg128_t cur_plus) { + pcg128_t acc_mult = PCG_128BIT_CONSTANT(0u, 1u); + pcg128_t acc_plus = PCG_128BIT_CONSTANT(0u, 0u); + while ((delta.high > 0) || (delta.low > 0)) { + if (delta.low & 1) { + acc_mult = pcg128_mult(acc_mult, cur_mult); + acc_plus = pcg128_add(pcg128_mult(acc_plus, cur_mult), cur_plus); + } + cur_plus = pcg128_mult(pcg128_add(cur_mult, PCG_128BIT_CONSTANT(0u, 1u)), + cur_plus); + cur_mult = pcg128_mult(cur_mult, cur_mult); + delta.low = (delta.low >> 1) | (delta.high << 63); + delta.high >>= 1; + } + return pcg128_add(pcg128_mult(acc_mult, state), acc_plus); +} + +#endif + +extern inline uint64_t pcg64_next64(pcg64_state *state); +extern inline uint32_t pcg64_next32(pcg64_state *state); + +extern inline uint64_t pcg64_cm_next64(pcg64_state *state); +extern inline uint32_t pcg64_cm_next32(pcg64_state *state); + +extern void pcg64_advance(pcg64_state *state, uint64_t *step) { + pcg128_t delta; +#ifndef PCG_EMULATED_128BIT_MATH + delta = (((pcg128_t)step[0]) << 64) | step[1]; +#else + delta.high = step[0]; + delta.low = step[1]; +#endif + pcg64_advance_r(state->pcg_state, delta); +} + +extern void pcg64_cm_advance(pcg64_state *state, uint64_t *step) { + pcg128_t delta; +#ifndef PCG_EMULATED_128BIT_MATH + delta = (((pcg128_t)step[0]) << 64) | step[1]; +#else + delta.high = step[0]; + delta.low = step[1]; +#endif + pcg_cm_advance_r(state->pcg_state, delta); +} + +extern void pcg64_set_seed(pcg64_state *state, uint64_t *seed, uint64_t *inc) { + pcg128_t s, i; +#ifndef PCG_EMULATED_128BIT_MATH + s = (((pcg128_t)seed[0]) << 64) | seed[1]; + i = (((pcg128_t)inc[0]) << 64) | inc[1]; +#else + s.high = seed[0]; + s.low = seed[1]; + i.high = inc[0]; + i.low = inc[1]; +#endif + pcg64_srandom_r(state->pcg_state, s, i); +} + +extern void pcg64_get_state(pcg64_state *state, uint64_t *state_arr, + int *has_uint32, uint32_t *uinteger) { + /* + * state_arr contains state.high, state.low, inc.high, inc.low + * which are interpreted as the upper 64 bits (high) or lower + * 64 bits of a uint128_t variable + * + */ +#ifndef PCG_EMULATED_128BIT_MATH + state_arr[0] = (uint64_t)(state->pcg_state->state >> 64); + state_arr[1] = (uint64_t)(state->pcg_state->state & 0xFFFFFFFFFFFFFFFFULL); + state_arr[2] = (uint64_t)(state->pcg_state->inc >> 64); + state_arr[3] = (uint64_t)(state->pcg_state->inc & 0xFFFFFFFFFFFFFFFFULL); +#else + state_arr[0] = (uint64_t)state->pcg_state->state.high; + state_arr[1] = (uint64_t)state->pcg_state->state.low; + state_arr[2] = (uint64_t)state->pcg_state->inc.high; + state_arr[3] = (uint64_t)state->pcg_state->inc.low; +#endif + has_uint32[0] = state->has_uint32; + uinteger[0] = state->uinteger; +} + +extern void pcg64_set_state(pcg64_state *state, uint64_t *state_arr, + int has_uint32, uint32_t uinteger) { + /* + * state_arr contains state.high, state.low, inc.high, inc.low + * which are interpreted as the upper 64 bits (high) or lower + * 64 bits of a uint128_t variable + * + */ +#ifndef PCG_EMULATED_128BIT_MATH + state->pcg_state->state = (((pcg128_t)state_arr[0]) << 64) | state_arr[1]; + state->pcg_state->inc = (((pcg128_t)state_arr[2]) << 64) | state_arr[3]; +#else + state->pcg_state->state.high = state_arr[0]; + state->pcg_state->state.low = state_arr[1]; + state->pcg_state->inc.high = state_arr[2]; + state->pcg_state->inc.low = state_arr[3]; +#endif + state->has_uint32 = has_uint32; + state->uinteger = uinteger; +} diff --git a/numpy/random/src/pcg64/pcg64.h b/numpy/random/src/pcg64/pcg64.h new file mode 100644 index 000000000000..90a83fd5edf9 --- /dev/null +++ b/numpy/random/src/pcg64/pcg64.h @@ -0,0 +1,422 @@ +/* + * PCG64 Random Number Generation for C. + * + * Copyright 2014 Melissa O'Neill <oneill@pcg-random.org> + * Copyright 2015 Robert Kern <robert.kern@gmail.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For additional information about the PCG random number generation scheme, + * including its license and other licensing options, visit + * + * http://www.pcg-random.org + * + * Relicensed MIT in May 2019 + * + * The MIT License + * + * PCG Random Number Generation for C. + * + * Copyright 2014 Melissa O'Neill <oneill@pcg-random.org> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef PCG64_H_INCLUDED +#define PCG64_H_INCLUDED 1 + +#include <inttypes.h> + +#ifdef _WIN32 +#include <stdlib.h> +#define inline __forceinline +#endif + +#if defined(__GNUC_GNU_INLINE__) && !defined(__cplusplus) +#error Nonstandard GNU inlining semantics. Compile with -std=c99 or better. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(__SIZEOF_INT128__) && !defined(PCG_FORCE_EMULATED_128BIT_MATH) +typedef __uint128_t pcg128_t; +#define PCG_128BIT_CONSTANT(high, low) (((pcg128_t)(high) << 64) + low) +#else +typedef struct { + uint64_t high; + uint64_t low; +} pcg128_t; + +static inline pcg128_t PCG_128BIT_CONSTANT(uint64_t high, uint64_t low) { + pcg128_t result; + result.high = high; + result.low = low; + return result; +} + +#define PCG_EMULATED_128BIT_MATH 1 +#endif + +typedef struct { pcg128_t state; } pcg_state_128; + +typedef struct { + pcg128_t state; + pcg128_t inc; +} pcg_state_setseq_128; + +#define PCG_DEFAULT_MULTIPLIER_HIGH 2549297995355413924ULL +#define PCG_DEFAULT_MULTIPLIER_LOW 4865540595714422341ULL + +#define PCG_DEFAULT_MULTIPLIER_128 \ + PCG_128BIT_CONSTANT(PCG_DEFAULT_MULTIPLIER_HIGH, PCG_DEFAULT_MULTIPLIER_LOW) +#define PCG_DEFAULT_INCREMENT_128 \ + PCG_128BIT_CONSTANT(6364136223846793005ULL, 1442695040888963407ULL) +#define PCG_STATE_SETSEQ_128_INITIALIZER \ + { \ + PCG_128BIT_CONSTANT(0x979c9a98d8462005ULL, 0x7d3e9cb6cfe0549bULL) \ + , PCG_128BIT_CONSTANT(0x0000000000000001ULL, 0xda3e39cb94b95bdbULL) \ + } + +#define PCG_CHEAP_MULTIPLIER_128 (0xda942042e4dd58b5ULL) + + +static inline uint64_t pcg_rotr_64(uint64_t value, unsigned int rot) { +#ifdef _WIN32 + return _rotr64(value, rot); +#else + return (value >> rot) | (value << ((-rot) & 63)); +#endif +} + +#ifdef PCG_EMULATED_128BIT_MATH + +static inline pcg128_t pcg128_add(pcg128_t a, pcg128_t b) { + pcg128_t result; + + result.low = a.low + b.low; + result.high = a.high + b.high + (result.low < b.low); + return result; +} + +static inline void _pcg_mult64(uint64_t x, uint64_t y, uint64_t *z1, + uint64_t *z0) { + +#if defined _WIN32 && _MSC_VER >= 1900 && _M_AMD64 + z0[0] = _umul128(x, y, z1); +#else + uint64_t x0, x1, y0, y1; + uint64_t w0, w1, w2, t; + /* Lower 64 bits are straightforward clock-arithmetic. */ + *z0 = x * y; + + x0 = x & 0xFFFFFFFFULL; + x1 = x >> 32; + y0 = y & 0xFFFFFFFFULL; + y1 = y >> 32; + w0 = x0 * y0; + t = x1 * y0 + (w0 >> 32); + w1 = t & 0xFFFFFFFFULL; + w2 = t >> 32; + w1 += x0 * y1; + *z1 = x1 * y1 + w2 + (w1 >> 32); +#endif +} + +static inline pcg128_t pcg128_mult(pcg128_t a, pcg128_t b) { + uint64_t h1; + pcg128_t result; + + h1 = a.high * b.low + a.low * b.high; + _pcg_mult64(a.low, b.low, &(result.high), &(result.low)); + result.high += h1; + return result; +} + +static inline void pcg_setseq_128_step_r(pcg_state_setseq_128 *rng) { + rng->state = pcg128_add(pcg128_mult(rng->state, PCG_DEFAULT_MULTIPLIER_128), + rng->inc); +} + +static inline uint64_t pcg_output_xsl_rr_128_64(pcg128_t state) { + return pcg_rotr_64(state.high ^ state.low, state.high >> 58u); +} + +static inline void pcg_setseq_128_srandom_r(pcg_state_setseq_128 *rng, + pcg128_t initstate, + pcg128_t initseq) { + rng->state = PCG_128BIT_CONSTANT(0ULL, 0ULL); + rng->inc.high = initseq.high << 1u; + rng->inc.high |= initseq.low >> 63u; + rng->inc.low = (initseq.low << 1u) | 1u; + pcg_setseq_128_step_r(rng); + rng->state = pcg128_add(rng->state, initstate); + pcg_setseq_128_step_r(rng); +} + +static inline uint64_t +pcg_setseq_128_xsl_rr_64_random_r(pcg_state_setseq_128 *rng) { +#if defined _WIN32 && _MSC_VER >= 1900 && _M_AMD64 + uint64_t h1; + pcg128_t product; + + /* Manually inline the multiplication and addition using intrinsics */ + h1 = rng->state.high * PCG_DEFAULT_MULTIPLIER_LOW + + rng->state.low * PCG_DEFAULT_MULTIPLIER_HIGH; + product.low = + _umul128(rng->state.low, PCG_DEFAULT_MULTIPLIER_LOW, &(product.high)); + product.high += h1; + _addcarry_u64(_addcarry_u64(0, product.low, rng->inc.low, &(rng->state.low)), + product.high, rng->inc.high, &(rng->state.high)); + return _rotr64(rng->state.high ^ rng->state.low, rng->state.high >> 58u); +#else + pcg_setseq_128_step_r(rng); + return pcg_output_xsl_rr_128_64(rng->state); +#endif +} + +static inline pcg128_t pcg128_mult_64(pcg128_t a, uint64_t b) { + uint64_t h1; + pcg128_t result; + + h1 = a.high * b; + _pcg_mult64(a.low, b, &(result.high), &(result.low)); + result.high += h1; + return result; +} + +static inline void pcg_cm_step_r(pcg_state_setseq_128 *rng) { +#if defined _WIN32 && _MSC_VER >= 1900 && _M_AMD64 + uint64_t h1; + pcg128_t product; + + /* Manually inline the multiplication and addition using intrinsics */ + h1 = rng->state.high * PCG_CHEAP_MULTIPLIER_128; + product.low = + _umul128(rng->state.low, PCG_CHEAP_MULTIPLIER_128, &(product.high)); + product.high += h1; + _addcarry_u64(_addcarry_u64(0, product.low, rng->inc.low, &(rng->state.low)), + product.high, rng->inc.high, &(rng->state.high)); +#else + rng->state = pcg128_add(pcg128_mult_64(rng->state, PCG_CHEAP_MULTIPLIER_128), + rng->inc); +#endif +} + + +static inline void pcg_cm_srandom_r(pcg_state_setseq_128 *rng, pcg128_t initstate, pcg128_t initseq) { + rng->state = PCG_128BIT_CONSTANT(0ULL, 0ULL); + rng->inc.high = initseq.high << 1u; + rng->inc.high |= initseq.low >> 63u; + rng->inc.low = (initseq.low << 1u) | 1u; + pcg_cm_step_r(rng); + rng->state = pcg128_add(rng->state, initstate); + pcg_cm_step_r(rng); +} + +static inline uint64_t pcg_cm_random_r(pcg_state_setseq_128* rng) +{ + /* Lots of manual inlining to help out certain compilers to generate + * performant code. */ + uint64_t hi = rng->state.high; + uint64_t lo = rng->state.low; + + /* Run the DXSM output function on the pre-iterated state. */ + lo |= 1; + hi ^= hi >> 32; + hi *= 0xda942042e4dd58b5ULL; + hi ^= hi >> 48; + hi *= lo; + + /* Run the CM step. */ +#if defined _WIN32 && _MSC_VER >= 1900 && _M_AMD64 + uint64_t h1; + pcg128_t product; + + /* Manually inline the multiplication and addition using intrinsics */ + h1 = rng->state.high * PCG_CHEAP_MULTIPLIER_128; + product.low = + _umul128(rng->state.low, PCG_CHEAP_MULTIPLIER_128, &(product.high)); + product.high += h1; + _addcarry_u64(_addcarry_u64(0, product.low, rng->inc.low, &(rng->state.low)), + product.high, rng->inc.high, &(rng->state.high)); +#else + rng->state = pcg128_add(pcg128_mult_64(rng->state, PCG_CHEAP_MULTIPLIER_128), + rng->inc); +#endif + return hi; +} +#else /* PCG_EMULATED_128BIT_MATH */ + +static inline void pcg_setseq_128_step_r(pcg_state_setseq_128 *rng) { + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_128 + rng->inc; +} + +static inline uint64_t pcg_output_xsl_rr_128_64(pcg128_t state) { + return pcg_rotr_64(((uint64_t)(state >> 64u)) ^ (uint64_t)state, + state >> 122u); +} + +static inline void pcg_cm_step_r(pcg_state_setseq_128 *rng) { + rng-> state = rng->state * PCG_CHEAP_MULTIPLIER_128 + rng->inc; +} + +static inline uint64_t pcg_output_cm_128_64(pcg128_t state) { + uint64_t hi = state >> 64; + uint64_t lo = state; + + lo |= 1; + hi ^= hi >> 32; + hi *= 0xda942042e4dd58b5ULL; + hi ^= hi >> 48; + hi *= lo; + return hi; +} + +static inline void pcg_cm_srandom_r(pcg_state_setseq_128 *rng, pcg128_t initstate, pcg128_t initseq) { + rng->state = 0U; + rng->inc = (initseq << 1u) | 1u; + pcg_cm_step_r(rng); + rng->state += initstate; + pcg_cm_step_r(rng); +} + +static inline uint64_t pcg_cm_random_r(pcg_state_setseq_128* rng) +{ + uint64_t ret = pcg_output_cm_128_64(rng->state); + pcg_cm_step_r(rng); + return ret; +} + +static inline uint64_t +pcg_setseq_128_xsl_rr_64_random_r(pcg_state_setseq_128* rng) +{ + pcg_setseq_128_step_r(rng); + return pcg_output_xsl_rr_128_64(rng->state); +} + +static inline void pcg_setseq_128_srandom_r(pcg_state_setseq_128 *rng, + pcg128_t initstate, + pcg128_t initseq) { + rng->state = 0U; + rng->inc = (initseq << 1u) | 1u; + pcg_setseq_128_step_r(rng); + rng->state += initstate; + pcg_setseq_128_step_r(rng); +} + +#endif /* PCG_EMULATED_128BIT_MATH */ + +static inline uint64_t +pcg_setseq_128_xsl_rr_64_boundedrand_r(pcg_state_setseq_128 *rng, + uint64_t bound) { + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_setseq_128_xsl_rr_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +extern pcg128_t pcg_advance_lcg_128(pcg128_t state, pcg128_t delta, + pcg128_t cur_mult, pcg128_t cur_plus); + +static inline void pcg_setseq_128_advance_r(pcg_state_setseq_128 *rng, + pcg128_t delta) { + rng->state = pcg_advance_lcg_128(rng->state, delta, + PCG_DEFAULT_MULTIPLIER_128, rng->inc); +} + +static inline void pcg_cm_advance_r(pcg_state_setseq_128 *rng, pcg128_t delta) { + rng->state = pcg_advance_lcg_128(rng->state, delta, + PCG_128BIT_CONSTANT(0, PCG_CHEAP_MULTIPLIER_128), + rng->inc); +} + +typedef pcg_state_setseq_128 pcg64_random_t; +#define pcg64_random_r pcg_setseq_128_xsl_rr_64_random_r +#define pcg64_boundedrand_r pcg_setseq_128_xsl_rr_64_boundedrand_r +#define pcg64_srandom_r pcg_setseq_128_srandom_r +#define pcg64_advance_r pcg_setseq_128_advance_r +#define PCG64_INITIALIZER PCG_STATE_SETSEQ_128_INITIALIZER + +#ifdef __cplusplus +} +#endif + +typedef struct s_pcg64_state { + pcg64_random_t *pcg_state; + int has_uint32; + uint32_t uinteger; +} pcg64_state; + +static inline uint64_t pcg64_next64(pcg64_state *state) { + return pcg64_random_r(state->pcg_state); +} + +static inline uint32_t pcg64_next32(pcg64_state *state) { + uint64_t next; + if (state->has_uint32) { + state->has_uint32 = 0; + return state->uinteger; + } + next = pcg64_random_r(state->pcg_state); + state->has_uint32 = 1; + state->uinteger = (uint32_t)(next >> 32); + return (uint32_t)(next & 0xffffffff); +} + +static inline uint64_t pcg64_cm_next64(pcg64_state *state) { + return pcg_cm_random_r(state->pcg_state); +} + +static inline uint32_t pcg64_cm_next32(pcg64_state *state) { + uint64_t next; + if (state->has_uint32) { + state->has_uint32 = 0; + return state->uinteger; + } + next = pcg_cm_random_r(state->pcg_state); + state->has_uint32 = 1; + state->uinteger = (uint32_t)(next >> 32); + return (uint32_t)(next & 0xffffffff); +} + +void pcg64_advance(pcg64_state *state, uint64_t *step); +void pcg64_cm_advance(pcg64_state *state, uint64_t *step); + +void pcg64_set_seed(pcg64_state *state, uint64_t *seed, uint64_t *inc); + +void pcg64_get_state(pcg64_state *state, uint64_t *state_arr, int *has_uint32, + uint32_t *uinteger); + +void pcg64_set_state(pcg64_state *state, uint64_t *state_arr, int has_uint32, + uint32_t uinteger); + +#endif /* PCG64_H_INCLUDED */ diff --git a/numpy/random/src/pcg64/pcg64.orig.c b/numpy/random/src/pcg64/pcg64.orig.c new file mode 100644 index 000000000000..07e97e4b6d97 --- /dev/null +++ b/numpy/random/src/pcg64/pcg64.orig.c @@ -0,0 +1,11 @@ +#include "pcg64.orig.h" + +extern inline void pcg_setseq_128_srandom_r(pcg64_random_t *rng, + pcg128_t initstate, + pcg128_t initseq); + +extern uint64_t pcg_rotr_64(uint64_t value, unsigned int rot); +extern inline uint64_t pcg_output_xsl_rr_128_64(pcg128_t state); +extern void pcg_setseq_128_step_r(struct pcg_state_setseq_128 *rng); +extern uint64_t +pcg_setseq_128_xsl_rr_64_random_r(struct pcg_state_setseq_128 *rng); diff --git a/numpy/random/src/pcg64/pcg64.orig.h b/numpy/random/src/pcg64/pcg64.orig.h new file mode 100644 index 000000000000..74be91f31a50 --- /dev/null +++ b/numpy/random/src/pcg64/pcg64.orig.h @@ -0,0 +1,2025 @@ +/* + * PCG Random Number Generation for C. + * + * Copyright 2014 Melissa O'Neill <oneill@pcg-random.org> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For additional information about the PCG random number generation scheme, + * including its license and other licensing options, visit + * + * http://www.pcg-random.org + */ + +/* + * This code is derived from the canonical C++ PCG implementation, which + * has many additional features and is preferable if you can use C++ in + * your project. + * + * Much of the derivation was performed mechanically. In particular, the + * output functions were generated by compiling the C++ output functions + * into LLVM bitcode and then transforming that using the LLVM C backend + * (from https://github.com/draperlaboratory/llvm-cbe), and then + * postprocessing and hand editing the output. + * + * Much of the remaining code was generated by C-preprocessor metaprogramming. + */ + +#ifndef PCG_VARIANTS_H_INCLUDED +#define PCG_VARIANTS_H_INCLUDED 1 + +#include <inttypes.h> + +#if __SIZEOF_INT128__ +typedef __uint128_t pcg128_t; +#define PCG_128BIT_CONSTANT(high, low) ((((pcg128_t)high) << 64) + low) +#define PCG_HAS_128BIT_OPS 1 +#endif + +#if __GNUC_GNU_INLINE__ && !defined(__cplusplus) +#error Nonstandard GNU inlining semantics. Compile with -std=c99 or better. +// We could instead use macros PCG_INLINE and PCG_EXTERN_INLINE +// but better to just reject ancient C code. +#endif + +#if __cplusplus +extern "C" { +#endif + +/* + * Rotate helper functions. + */ + +inline uint8_t pcg_rotr_8(uint8_t value, unsigned int rot) { +/* Unfortunately, clang is kinda pathetic when it comes to properly + * recognizing idiomatic rotate code, so for clang we actually provide + * assembler directives (enabled with PCG_USE_INLINE_ASM). Boo, hiss. + */ +#if PCG_USE_INLINE_ASM && __clang__ && (__x86_64__ || __i386__) + asm("rorb %%cl, %0" : "=r"(value) : "0"(value), "c"(rot)); + return value; +#else + return (value >> rot) | (value << ((-rot) & 7)); +#endif +} + +inline uint16_t pcg_rotr_16(uint16_t value, unsigned int rot) { +#if PCG_USE_INLINE_ASM && __clang__ && (__x86_64__ || __i386__) + asm("rorw %%cl, %0" : "=r"(value) : "0"(value), "c"(rot)); + return value; +#else + return (value >> rot) | (value << ((-rot) & 15)); +#endif +} + +inline uint32_t pcg_rotr_32(uint32_t value, unsigned int rot) { +#if PCG_USE_INLINE_ASM && __clang__ && (__x86_64__ || __i386__) + asm("rorl %%cl, %0" : "=r"(value) : "0"(value), "c"(rot)); + return value; +#else + return (value >> rot) | (value << ((-rot) & 31)); +#endif +} + +inline uint64_t pcg_rotr_64(uint64_t value, unsigned int rot) { +#if 0 && PCG_USE_INLINE_ASM && __clang__ && __x86_64__ + // For whatever reason, clang actually *does* generate rotq by + // itself, so we don't need this code. + asm ("rorq %%cl, %0" : "=r" (value) : "0" (value), "c" (rot)); + return value; +#else + return (value >> rot) | (value << ((-rot) & 63)); +#endif +} + +#if PCG_HAS_128BIT_OPS +inline pcg128_t pcg_rotr_128(pcg128_t value, unsigned int rot) { + return (value >> rot) | (value << ((-rot) & 127)); +} +#endif + +/* + * Output functions. These are the core of the PCG generation scheme. + */ + +// XSH RS + +inline uint8_t pcg_output_xsh_rs_16_8(uint16_t state) { + return (uint8_t)(((state >> 7u) ^ state) >> ((state >> 14u) + 3u)); +} + +inline uint16_t pcg_output_xsh_rs_32_16(uint32_t state) { + return (uint16_t)(((state >> 11u) ^ state) >> ((state >> 30u) + 11u)); +} + +inline uint32_t pcg_output_xsh_rs_64_32(uint64_t state) { + + return (uint32_t)(((state >> 22u) ^ state) >> ((state >> 61u) + 22u)); +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_output_xsh_rs_128_64(pcg128_t state) { + return (uint64_t)(((state >> 43u) ^ state) >> ((state >> 124u) + 45u)); +} +#endif + +// XSH RR + +inline uint8_t pcg_output_xsh_rr_16_8(uint16_t state) { + return pcg_rotr_8(((state >> 5u) ^ state) >> 5u, state >> 13u); +} + +inline uint16_t pcg_output_xsh_rr_32_16(uint32_t state) { + return pcg_rotr_16(((state >> 10u) ^ state) >> 12u, state >> 28u); +} + +inline uint32_t pcg_output_xsh_rr_64_32(uint64_t state) { + return pcg_rotr_32(((state >> 18u) ^ state) >> 27u, state >> 59u); +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_output_xsh_rr_128_64(pcg128_t state) { + return pcg_rotr_64(((state >> 29u) ^ state) >> 58u, state >> 122u); +} +#endif + +// RXS M XS + +inline uint8_t pcg_output_rxs_m_xs_8_8(uint8_t state) { + uint8_t word = ((state >> ((state >> 6u) + 2u)) ^ state) * 217u; + return (word >> 6u) ^ word; +} + +inline uint16_t pcg_output_rxs_m_xs_16_16(uint16_t state) { + uint16_t word = ((state >> ((state >> 13u) + 3u)) ^ state) * 62169u; + return (word >> 11u) ^ word; +} + +inline uint32_t pcg_output_rxs_m_xs_32_32(uint32_t state) { + uint32_t word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u; + return (word >> 22u) ^ word; +} + +inline uint64_t pcg_output_rxs_m_xs_64_64(uint64_t state) { + uint64_t word = + ((state >> ((state >> 59u) + 5u)) ^ state) * 12605985483714917081ull; + return (word >> 43u) ^ word; +} + +#if PCG_HAS_128BIT_OPS +inline pcg128_t pcg_output_rxs_m_xs_128_128(pcg128_t state) { + pcg128_t word = + ((state >> ((state >> 122u) + 6u)) ^ state) * + (PCG_128BIT_CONSTANT(17766728186571221404ULL, 12605985483714917081ULL)); + // 327738287884841127335028083622016905945 + return (word >> 86u) ^ word; +} +#endif + +// XSL RR (only defined for >= 64 bits) + +inline uint32_t pcg_output_xsl_rr_64_32(uint64_t state) { + return pcg_rotr_32(((uint32_t)(state >> 32u)) ^ (uint32_t)state, + state >> 59u); +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_output_xsl_rr_128_64(pcg128_t state) { + return pcg_rotr_64(((uint64_t)(state >> 64u)) ^ (uint64_t)state, + state >> 122u); +} +#endif + +// XSL RR RR (only defined for >= 64 bits) + +inline uint64_t pcg_output_xsl_rr_rr_64_64(uint64_t state) { + uint32_t rot1 = (uint32_t)(state >> 59u); + uint32_t high = (uint32_t)(state >> 32u); + uint32_t low = (uint32_t)state; + uint32_t xored = high ^ low; + uint32_t newlow = pcg_rotr_32(xored, rot1); + uint32_t newhigh = pcg_rotr_32(high, newlow & 31u); + return (((uint64_t)newhigh) << 32u) | newlow; +} + +#if PCG_HAS_128BIT_OPS +inline pcg128_t pcg_output_xsl_rr_rr_128_128(pcg128_t state) { + uint32_t rot1 = (uint32_t)(state >> 122u); + uint64_t high = (uint64_t)(state >> 64u); + uint64_t low = (uint64_t)state; + uint64_t xored = high ^ low; + uint64_t newlow = pcg_rotr_64(xored, rot1); + uint64_t newhigh = pcg_rotr_64(high, newlow & 63u); + return (((pcg128_t)newhigh) << 64u) | newlow; +} +#endif + +#define PCG_DEFAULT_MULTIPLIER_8 141U +#define PCG_DEFAULT_MULTIPLIER_16 12829U +#define PCG_DEFAULT_MULTIPLIER_32 747796405U +#define PCG_DEFAULT_MULTIPLIER_64 6364136223846793005ULL + +#define PCG_DEFAULT_INCREMENT_8 77U +#define PCG_DEFAULT_INCREMENT_16 47989U +#define PCG_DEFAULT_INCREMENT_32 2891336453U +#define PCG_DEFAULT_INCREMENT_64 1442695040888963407ULL + +#if PCG_HAS_128BIT_OPS +#define PCG_DEFAULT_MULTIPLIER_128 \ + PCG_128BIT_CONSTANT(2549297995355413924ULL, 4865540595714422341ULL) +#define PCG_DEFAULT_INCREMENT_128 \ + PCG_128BIT_CONSTANT(6364136223846793005ULL, 1442695040888963407ULL) +#endif + + /* + * Static initialization constants (if you can't call srandom for some + * bizarre reason). + */ + +#define PCG_STATE_ONESEQ_8_INITIALIZER \ + { 0xd7U } +#define PCG_STATE_ONESEQ_16_INITIALIZER \ + { 0x20dfU } +#define PCG_STATE_ONESEQ_32_INITIALIZER \ + { 0x46b56677U } +#define PCG_STATE_ONESEQ_64_INITIALIZER \ + { 0x4d595df4d0f33173ULL } +#if PCG_HAS_128BIT_OPS +#define PCG_STATE_ONESEQ_128_INITIALIZER \ + { PCG_128BIT_CONSTANT(0xb8dc10e158a92392ULL, 0x98046df007ec0a53ULL) } +#endif + +#define PCG_STATE_UNIQUE_8_INITIALIZER PCG_STATE_ONESEQ_8_INITIALIZER +#define PCG_STATE_UNIQUE_16_INITIALIZER PCG_STATE_ONESEQ_16_INITIALIZER +#define PCG_STATE_UNIQUE_32_INITIALIZER PCG_STATE_ONESEQ_32_INITIALIZER +#define PCG_STATE_UNIQUE_64_INITIALIZER PCG_STATE_ONESEQ_64_INITIALIZER +#if PCG_HAS_128BIT_OPS +#define PCG_STATE_UNIQUE_128_INITIALIZER PCG_STATE_ONESEQ_128_INITIALIZER +#endif + +#define PCG_STATE_MCG_8_INITIALIZER \ + { 0xe5U } +#define PCG_STATE_MCG_16_INITIALIZER \ + { 0xa5e5U } +#define PCG_STATE_MCG_32_INITIALIZER \ + { 0xd15ea5e5U } +#define PCG_STATE_MCG_64_INITIALIZER \ + { 0xcafef00dd15ea5e5ULL } +#if PCG_HAS_128BIT_OPS +#define PCG_STATE_MCG_128_INITIALIZER \ + { PCG_128BIT_CONSTANT(0x0000000000000000ULL, 0xcafef00dd15ea5e5ULL) } +#endif + +#define PCG_STATE_SETSEQ_8_INITIALIZER \ + { 0x9bU, 0xdbU } +#define PCG_STATE_SETSEQ_16_INITIALIZER \ + { 0xe39bU, 0x5bdbU } +#define PCG_STATE_SETSEQ_32_INITIALIZER \ + { 0xec02d89bU, 0x94b95bdbU } +#define PCG_STATE_SETSEQ_64_INITIALIZER \ + { 0x853c49e6748fea9bULL, 0xda3e39cb94b95bdbULL } +#if PCG_HAS_128BIT_OPS +#define PCG_STATE_SETSEQ_128_INITIALIZER \ + { \ + PCG_128BIT_CONSTANT(0x979c9a98d8462005ULL, 0x7d3e9cb6cfe0549bULL) \ + , PCG_128BIT_CONSTANT(0x0000000000000001ULL, 0xda3e39cb94b95bdbULL) \ + } +#endif + +/* Representations for the oneseq, mcg, and unique variants */ + +struct pcg_state_8 { + uint8_t state; +}; + +struct pcg_state_16 { + uint16_t state; +}; + +struct pcg_state_32 { + uint32_t state; +}; + +struct pcg_state_64 { + uint64_t state; +}; + +#if PCG_HAS_128BIT_OPS +struct pcg_state_128 { + pcg128_t state; +}; +#endif + +/* Representations setseq variants */ + +struct pcg_state_setseq_8 { + uint8_t state; + uint8_t inc; +}; + +struct pcg_state_setseq_16 { + uint16_t state; + uint16_t inc; +}; + +struct pcg_state_setseq_32 { + uint32_t state; + uint32_t inc; +}; + +struct pcg_state_setseq_64 { + uint64_t state; + uint64_t inc; +}; + +#if PCG_HAS_128BIT_OPS +struct pcg_state_setseq_128 { + pcg128_t state; + pcg128_t inc; +}; +#endif + +/* Multi-step advance functions (jump-ahead, jump-back) */ + +extern uint8_t pcg_advance_lcg_8(uint8_t state, uint8_t delta, uint8_t cur_mult, + uint8_t cur_plus); +extern uint16_t pcg_advance_lcg_16(uint16_t state, uint16_t delta, + uint16_t cur_mult, uint16_t cur_plus); +extern uint32_t pcg_advance_lcg_32(uint32_t state, uint32_t delta, + uint32_t cur_mult, uint32_t cur_plus); +extern uint64_t pcg_advance_lcg_64(uint64_t state, uint64_t delta, + uint64_t cur_mult, uint64_t cur_plus); + +#if PCG_HAS_128BIT_OPS +extern pcg128_t pcg_advance_lcg_128(pcg128_t state, pcg128_t delta, + pcg128_t cur_mult, pcg128_t cur_plus); +#endif + +/* Functions to advance the underlying LCG, one version for each size and + * each style. These functions are considered semi-private. There is rarely + * a good reason to call them directly. + */ + +inline void pcg_oneseq_8_step_r(struct pcg_state_8 *rng) { + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_8 + PCG_DEFAULT_INCREMENT_8; +} + +inline void pcg_oneseq_8_advance_r(struct pcg_state_8 *rng, uint8_t delta) { + rng->state = pcg_advance_lcg_8(rng->state, delta, PCG_DEFAULT_MULTIPLIER_8, + PCG_DEFAULT_INCREMENT_8); +} + +inline void pcg_mcg_8_step_r(struct pcg_state_8 *rng) { + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_8; +} + +inline void pcg_mcg_8_advance_r(struct pcg_state_8 *rng, uint8_t delta) { + rng->state = + pcg_advance_lcg_8(rng->state, delta, PCG_DEFAULT_MULTIPLIER_8, 0u); +} + +inline void pcg_unique_8_step_r(struct pcg_state_8 *rng) { + rng->state = + rng->state * PCG_DEFAULT_MULTIPLIER_8 + (uint8_t)(((intptr_t)rng) | 1u); +} + +inline void pcg_unique_8_advance_r(struct pcg_state_8 *rng, uint8_t delta) { + rng->state = pcg_advance_lcg_8(rng->state, delta, PCG_DEFAULT_MULTIPLIER_8, + (uint8_t)(((intptr_t)rng) | 1u)); +} + +inline void pcg_setseq_8_step_r(struct pcg_state_setseq_8 *rng) { + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_8 + rng->inc; +} + +inline void pcg_setseq_8_advance_r(struct pcg_state_setseq_8 *rng, + uint8_t delta) { + rng->state = + pcg_advance_lcg_8(rng->state, delta, PCG_DEFAULT_MULTIPLIER_8, rng->inc); +} + +inline void pcg_oneseq_16_step_r(struct pcg_state_16 *rng) { + rng->state = + rng->state * PCG_DEFAULT_MULTIPLIER_16 + PCG_DEFAULT_INCREMENT_16; +} + +inline void pcg_oneseq_16_advance_r(struct pcg_state_16 *rng, uint16_t delta) { + rng->state = pcg_advance_lcg_16(rng->state, delta, PCG_DEFAULT_MULTIPLIER_16, + PCG_DEFAULT_INCREMENT_16); +} + +inline void pcg_mcg_16_step_r(struct pcg_state_16 *rng) { + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_16; +} + +inline void pcg_mcg_16_advance_r(struct pcg_state_16 *rng, uint16_t delta) { + rng->state = + pcg_advance_lcg_16(rng->state, delta, PCG_DEFAULT_MULTIPLIER_16, 0u); +} + +inline void pcg_unique_16_step_r(struct pcg_state_16 *rng) { + rng->state = + rng->state * PCG_DEFAULT_MULTIPLIER_16 + (uint16_t)(((intptr_t)rng) | 1u); +} + +inline void pcg_unique_16_advance_r(struct pcg_state_16 *rng, uint16_t delta) { + rng->state = pcg_advance_lcg_16(rng->state, delta, PCG_DEFAULT_MULTIPLIER_16, + (uint16_t)(((intptr_t)rng) | 1u)); +} + +inline void pcg_setseq_16_step_r(struct pcg_state_setseq_16 *rng) { + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_16 + rng->inc; +} + +inline void pcg_setseq_16_advance_r(struct pcg_state_setseq_16 *rng, + uint16_t delta) { + rng->state = pcg_advance_lcg_16(rng->state, delta, PCG_DEFAULT_MULTIPLIER_16, + rng->inc); +} + +inline void pcg_oneseq_32_step_r(struct pcg_state_32 *rng) { + rng->state = + rng->state * PCG_DEFAULT_MULTIPLIER_32 + PCG_DEFAULT_INCREMENT_32; +} + +inline void pcg_oneseq_32_advance_r(struct pcg_state_32 *rng, uint32_t delta) { + rng->state = pcg_advance_lcg_32(rng->state, delta, PCG_DEFAULT_MULTIPLIER_32, + PCG_DEFAULT_INCREMENT_32); +} + +inline void pcg_mcg_32_step_r(struct pcg_state_32 *rng) { + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_32; +} + +inline void pcg_mcg_32_advance_r(struct pcg_state_32 *rng, uint32_t delta) { + rng->state = + pcg_advance_lcg_32(rng->state, delta, PCG_DEFAULT_MULTIPLIER_32, 0u); +} + +inline void pcg_unique_32_step_r(struct pcg_state_32 *rng) { + rng->state = + rng->state * PCG_DEFAULT_MULTIPLIER_32 + (uint32_t)(((intptr_t)rng) | 1u); +} + +inline void pcg_unique_32_advance_r(struct pcg_state_32 *rng, uint32_t delta) { + rng->state = pcg_advance_lcg_32(rng->state, delta, PCG_DEFAULT_MULTIPLIER_32, + (uint32_t)(((intptr_t)rng) | 1u)); +} + +inline void pcg_setseq_32_step_r(struct pcg_state_setseq_32 *rng) { + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_32 + rng->inc; +} + +inline void pcg_setseq_32_advance_r(struct pcg_state_setseq_32 *rng, + uint32_t delta) { + rng->state = pcg_advance_lcg_32(rng->state, delta, PCG_DEFAULT_MULTIPLIER_32, + rng->inc); +} + +inline void pcg_oneseq_64_step_r(struct pcg_state_64 *rng) { + rng->state = + rng->state * PCG_DEFAULT_MULTIPLIER_64 + PCG_DEFAULT_INCREMENT_64; +} + +inline void pcg_oneseq_64_advance_r(struct pcg_state_64 *rng, uint64_t delta) { + rng->state = pcg_advance_lcg_64(rng->state, delta, PCG_DEFAULT_MULTIPLIER_64, + PCG_DEFAULT_INCREMENT_64); +} + +inline void pcg_mcg_64_step_r(struct pcg_state_64 *rng) { + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_64; +} + +inline void pcg_mcg_64_advance_r(struct pcg_state_64 *rng, uint64_t delta) { + rng->state = + pcg_advance_lcg_64(rng->state, delta, PCG_DEFAULT_MULTIPLIER_64, 0u); +} + +inline void pcg_unique_64_step_r(struct pcg_state_64 *rng) { + rng->state = + rng->state * PCG_DEFAULT_MULTIPLIER_64 + (uint64_t)(((intptr_t)rng) | 1u); +} + +inline void pcg_unique_64_advance_r(struct pcg_state_64 *rng, uint64_t delta) { + rng->state = pcg_advance_lcg_64(rng->state, delta, PCG_DEFAULT_MULTIPLIER_64, + (uint64_t)(((intptr_t)rng) | 1u)); +} + +inline void pcg_setseq_64_step_r(struct pcg_state_setseq_64 *rng) { + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_64 + rng->inc; +} + +inline void pcg_setseq_64_advance_r(struct pcg_state_setseq_64 *rng, + uint64_t delta) { + rng->state = pcg_advance_lcg_64(rng->state, delta, PCG_DEFAULT_MULTIPLIER_64, + rng->inc); +} + +#if PCG_HAS_128BIT_OPS +inline void pcg_oneseq_128_step_r(struct pcg_state_128 *rng) { + rng->state = + rng->state * PCG_DEFAULT_MULTIPLIER_128 + PCG_DEFAULT_INCREMENT_128; +} +#endif + +#if PCG_HAS_128BIT_OPS +inline void pcg_oneseq_128_advance_r(struct pcg_state_128 *rng, + pcg128_t delta) { + rng->state = pcg_advance_lcg_128( + rng->state, delta, PCG_DEFAULT_MULTIPLIER_128, PCG_DEFAULT_INCREMENT_128); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline void pcg_mcg_128_step_r(struct pcg_state_128 *rng) { + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_128; +} +#endif + +#if PCG_HAS_128BIT_OPS +inline void pcg_mcg_128_advance_r(struct pcg_state_128 *rng, pcg128_t delta) { + rng->state = + pcg_advance_lcg_128(rng->state, delta, PCG_DEFAULT_MULTIPLIER_128, 0u); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline void pcg_unique_128_step_r(struct pcg_state_128 *rng) { + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_128 + + (pcg128_t)(((intptr_t)rng) | 1u); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline void pcg_unique_128_advance_r(struct pcg_state_128 *rng, + pcg128_t delta) { + rng->state = + pcg_advance_lcg_128(rng->state, delta, PCG_DEFAULT_MULTIPLIER_128, + (pcg128_t)(((intptr_t)rng) | 1u)); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline void pcg_setseq_128_step_r(struct pcg_state_setseq_128 *rng) { + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_128 + rng->inc; +} +#endif + +#if PCG_HAS_128BIT_OPS +inline void pcg_setseq_128_advance_r(struct pcg_state_setseq_128 *rng, + pcg128_t delta) { + rng->state = pcg_advance_lcg_128(rng->state, delta, + PCG_DEFAULT_MULTIPLIER_128, rng->inc); +} +#endif + +/* Functions to seed the RNG state, one version for each size and each + * style. Unlike the step functions, regular users can and should call + * these functions. + */ + +inline void pcg_oneseq_8_srandom_r(struct pcg_state_8 *rng, uint8_t initstate) { + rng->state = 0U; + pcg_oneseq_8_step_r(rng); + rng->state += initstate; + pcg_oneseq_8_step_r(rng); +} + +inline void pcg_mcg_8_srandom_r(struct pcg_state_8 *rng, uint8_t initstate) { + rng->state = initstate | 1u; +} + +inline void pcg_unique_8_srandom_r(struct pcg_state_8 *rng, uint8_t initstate) { + rng->state = 0U; + pcg_unique_8_step_r(rng); + rng->state += initstate; + pcg_unique_8_step_r(rng); +} + +inline void pcg_setseq_8_srandom_r(struct pcg_state_setseq_8 *rng, + uint8_t initstate, uint8_t initseq) { + rng->state = 0U; + rng->inc = (initseq << 1u) | 1u; + pcg_setseq_8_step_r(rng); + rng->state += initstate; + pcg_setseq_8_step_r(rng); +} + +inline void pcg_oneseq_16_srandom_r(struct pcg_state_16 *rng, + uint16_t initstate) { + rng->state = 0U; + pcg_oneseq_16_step_r(rng); + rng->state += initstate; + pcg_oneseq_16_step_r(rng); +} + +inline void pcg_mcg_16_srandom_r(struct pcg_state_16 *rng, uint16_t initstate) { + rng->state = initstate | 1u; +} + +inline void pcg_unique_16_srandom_r(struct pcg_state_16 *rng, + uint16_t initstate) { + rng->state = 0U; + pcg_unique_16_step_r(rng); + rng->state += initstate; + pcg_unique_16_step_r(rng); +} + +inline void pcg_setseq_16_srandom_r(struct pcg_state_setseq_16 *rng, + uint16_t initstate, uint16_t initseq) { + rng->state = 0U; + rng->inc = (initseq << 1u) | 1u; + pcg_setseq_16_step_r(rng); + rng->state += initstate; + pcg_setseq_16_step_r(rng); +} + +inline void pcg_oneseq_32_srandom_r(struct pcg_state_32 *rng, + uint32_t initstate) { + rng->state = 0U; + pcg_oneseq_32_step_r(rng); + rng->state += initstate; + pcg_oneseq_32_step_r(rng); +} + +inline void pcg_mcg_32_srandom_r(struct pcg_state_32 *rng, uint32_t initstate) { + rng->state = initstate | 1u; +} + +inline void pcg_unique_32_srandom_r(struct pcg_state_32 *rng, + uint32_t initstate) { + rng->state = 0U; + pcg_unique_32_step_r(rng); + rng->state += initstate; + pcg_unique_32_step_r(rng); +} + +inline void pcg_setseq_32_srandom_r(struct pcg_state_setseq_32 *rng, + uint32_t initstate, uint32_t initseq) { + rng->state = 0U; + rng->inc = (initseq << 1u) | 1u; + pcg_setseq_32_step_r(rng); + rng->state += initstate; + pcg_setseq_32_step_r(rng); +} + +inline void pcg_oneseq_64_srandom_r(struct pcg_state_64 *rng, + uint64_t initstate) { + rng->state = 0U; + pcg_oneseq_64_step_r(rng); + rng->state += initstate; + pcg_oneseq_64_step_r(rng); +} + +inline void pcg_mcg_64_srandom_r(struct pcg_state_64 *rng, uint64_t initstate) { + rng->state = initstate | 1u; +} + +inline void pcg_unique_64_srandom_r(struct pcg_state_64 *rng, + uint64_t initstate) { + rng->state = 0U; + pcg_unique_64_step_r(rng); + rng->state += initstate; + pcg_unique_64_step_r(rng); +} + +inline void pcg_setseq_64_srandom_r(struct pcg_state_setseq_64 *rng, + uint64_t initstate, uint64_t initseq) { + rng->state = 0U; + rng->inc = (initseq << 1u) | 1u; + pcg_setseq_64_step_r(rng); + rng->state += initstate; + pcg_setseq_64_step_r(rng); +} + +#if PCG_HAS_128BIT_OPS +inline void pcg_oneseq_128_srandom_r(struct pcg_state_128 *rng, + pcg128_t initstate) { + rng->state = 0U; + pcg_oneseq_128_step_r(rng); + rng->state += initstate; + pcg_oneseq_128_step_r(rng); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline void pcg_mcg_128_srandom_r(struct pcg_state_128 *rng, + pcg128_t initstate) { + rng->state = initstate | 1u; +} +#endif + +#if PCG_HAS_128BIT_OPS +inline void pcg_unique_128_srandom_r(struct pcg_state_128 *rng, + pcg128_t initstate) { + rng->state = 0U; + pcg_unique_128_step_r(rng); + rng->state += initstate; + pcg_unique_128_step_r(rng); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline void pcg_setseq_128_srandom_r(struct pcg_state_setseq_128 *rng, + pcg128_t initstate, pcg128_t initseq) { + rng->state = 0U; + rng->inc = (initseq << 1u) | 1u; + pcg_setseq_128_step_r(rng); + rng->state += initstate; + pcg_setseq_128_step_r(rng); +} +#endif + +/* Now, finally we create each of the individual generators. We provide + * a random_r function that provides a random number of the appropriate + * type (using the full range of the type) and a boundedrand_r version + * that provides + * + * Implementation notes for boundedrand_r: + * + * To avoid bias, we need to make the range of the RNG a multiple of + * bound, which we do by dropping output less than a threshold. + * Let's consider a 32-bit case... A naive scheme to calculate the + * threshold would be to do + * + * uint32_t threshold = 0x100000000ull % bound; + * + * but 64-bit div/mod is slower than 32-bit div/mod (especially on + * 32-bit platforms). In essence, we do + * + * uint32_t threshold = (0x100000000ull-bound) % bound; + * + * because this version will calculate the same modulus, but the LHS + * value is less than 2^32. + * + * (Note that using modulo is only wise for good RNGs, poorer RNGs + * such as raw LCGs do better using a technique based on division.) + * Empricical tests show that division is preferable to modulus for + * reducting the range of an RNG. It's faster, and sometimes it can + * even be statistically prefereable. + */ + +/* Generation functions for XSH RS */ + +inline uint8_t pcg_oneseq_16_xsh_rs_8_random_r(struct pcg_state_16 *rng) { + uint16_t oldstate = rng->state; + pcg_oneseq_16_step_r(rng); + return pcg_output_xsh_rs_16_8(oldstate); +} + +inline uint8_t pcg_oneseq_16_xsh_rs_8_boundedrand_r(struct pcg_state_16 *rng, + uint8_t bound) { + uint8_t threshold = ((uint8_t)(-bound)) % bound; + for (;;) { + uint8_t r = pcg_oneseq_16_xsh_rs_8_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint16_t pcg_oneseq_32_xsh_rs_16_random_r(struct pcg_state_32 *rng) { + uint32_t oldstate = rng->state; + pcg_oneseq_32_step_r(rng); + return pcg_output_xsh_rs_32_16(oldstate); +} + +inline uint16_t pcg_oneseq_32_xsh_rs_16_boundedrand_r(struct pcg_state_32 *rng, + uint16_t bound) { + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_oneseq_32_xsh_rs_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t pcg_oneseq_64_xsh_rs_32_random_r(struct pcg_state_64 *rng) { + uint64_t oldstate = rng->state; + pcg_oneseq_64_step_r(rng); + return pcg_output_xsh_rs_64_32(oldstate); +} + +inline uint32_t pcg_oneseq_64_xsh_rs_32_boundedrand_r(struct pcg_state_64 *rng, + uint32_t bound) { + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_oneseq_64_xsh_rs_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_oneseq_128_xsh_rs_64_random_r(struct pcg_state_128 *rng) { + pcg_oneseq_128_step_r(rng); + return pcg_output_xsh_rs_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_oneseq_128_xsh_rs_64_boundedrand_r(struct pcg_state_128 *rng, + uint64_t bound) { + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_oneseq_128_xsh_rs_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint8_t pcg_unique_16_xsh_rs_8_random_r(struct pcg_state_16 *rng) { + uint16_t oldstate = rng->state; + pcg_unique_16_step_r(rng); + return pcg_output_xsh_rs_16_8(oldstate); +} + +inline uint8_t pcg_unique_16_xsh_rs_8_boundedrand_r(struct pcg_state_16 *rng, + uint8_t bound) { + uint8_t threshold = ((uint8_t)(-bound)) % bound; + for (;;) { + uint8_t r = pcg_unique_16_xsh_rs_8_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint16_t pcg_unique_32_xsh_rs_16_random_r(struct pcg_state_32 *rng) { + uint32_t oldstate = rng->state; + pcg_unique_32_step_r(rng); + return pcg_output_xsh_rs_32_16(oldstate); +} + +inline uint16_t pcg_unique_32_xsh_rs_16_boundedrand_r(struct pcg_state_32 *rng, + uint16_t bound) { + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_unique_32_xsh_rs_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t pcg_unique_64_xsh_rs_32_random_r(struct pcg_state_64 *rng) { + uint64_t oldstate = rng->state; + pcg_unique_64_step_r(rng); + return pcg_output_xsh_rs_64_32(oldstate); +} + +inline uint32_t pcg_unique_64_xsh_rs_32_boundedrand_r(struct pcg_state_64 *rng, + uint32_t bound) { + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_unique_64_xsh_rs_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_unique_128_xsh_rs_64_random_r(struct pcg_state_128 *rng) { + pcg_unique_128_step_r(rng); + return pcg_output_xsh_rs_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_unique_128_xsh_rs_64_boundedrand_r(struct pcg_state_128 *rng, + uint64_t bound) { + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_unique_128_xsh_rs_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint8_t +pcg_setseq_16_xsh_rs_8_random_r(struct pcg_state_setseq_16 *rng) { + uint16_t oldstate = rng->state; + pcg_setseq_16_step_r(rng); + return pcg_output_xsh_rs_16_8(oldstate); +} + +inline uint8_t +pcg_setseq_16_xsh_rs_8_boundedrand_r(struct pcg_state_setseq_16 *rng, + uint8_t bound) { + uint8_t threshold = ((uint8_t)(-bound)) % bound; + for (;;) { + uint8_t r = pcg_setseq_16_xsh_rs_8_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint16_t +pcg_setseq_32_xsh_rs_16_random_r(struct pcg_state_setseq_32 *rng) { + uint32_t oldstate = rng->state; + pcg_setseq_32_step_r(rng); + return pcg_output_xsh_rs_32_16(oldstate); +} + +inline uint16_t +pcg_setseq_32_xsh_rs_16_boundedrand_r(struct pcg_state_setseq_32 *rng, + uint16_t bound) { + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_setseq_32_xsh_rs_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t +pcg_setseq_64_xsh_rs_32_random_r(struct pcg_state_setseq_64 *rng) { + uint64_t oldstate = rng->state; + pcg_setseq_64_step_r(rng); + return pcg_output_xsh_rs_64_32(oldstate); +} + +inline uint32_t +pcg_setseq_64_xsh_rs_32_boundedrand_r(struct pcg_state_setseq_64 *rng, + uint32_t bound) { + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_setseq_64_xsh_rs_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_setseq_128_xsh_rs_64_random_r(struct pcg_state_setseq_128 *rng) { + pcg_setseq_128_step_r(rng); + return pcg_output_xsh_rs_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_setseq_128_xsh_rs_64_boundedrand_r(struct pcg_state_setseq_128 *rng, + uint64_t bound) { + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_setseq_128_xsh_rs_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint8_t pcg_mcg_16_xsh_rs_8_random_r(struct pcg_state_16 *rng) { + uint16_t oldstate = rng->state; + pcg_mcg_16_step_r(rng); + return pcg_output_xsh_rs_16_8(oldstate); +} + +inline uint8_t pcg_mcg_16_xsh_rs_8_boundedrand_r(struct pcg_state_16 *rng, + uint8_t bound) { + uint8_t threshold = ((uint8_t)(-bound)) % bound; + for (;;) { + uint8_t r = pcg_mcg_16_xsh_rs_8_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint16_t pcg_mcg_32_xsh_rs_16_random_r(struct pcg_state_32 *rng) { + uint32_t oldstate = rng->state; + pcg_mcg_32_step_r(rng); + return pcg_output_xsh_rs_32_16(oldstate); +} + +inline uint16_t pcg_mcg_32_xsh_rs_16_boundedrand_r(struct pcg_state_32 *rng, + uint16_t bound) { + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_mcg_32_xsh_rs_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t pcg_mcg_64_xsh_rs_32_random_r(struct pcg_state_64 *rng) { + uint64_t oldstate = rng->state; + pcg_mcg_64_step_r(rng); + return pcg_output_xsh_rs_64_32(oldstate); +} + +inline uint32_t pcg_mcg_64_xsh_rs_32_boundedrand_r(struct pcg_state_64 *rng, + uint32_t bound) { + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_mcg_64_xsh_rs_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_mcg_128_xsh_rs_64_random_r(struct pcg_state_128 *rng) { + pcg_mcg_128_step_r(rng); + return pcg_output_xsh_rs_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_mcg_128_xsh_rs_64_boundedrand_r(struct pcg_state_128 *rng, + uint64_t bound) { + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_mcg_128_xsh_rs_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +/* Generation functions for XSH RR */ + +inline uint8_t pcg_oneseq_16_xsh_rr_8_random_r(struct pcg_state_16 *rng) { + uint16_t oldstate = rng->state; + pcg_oneseq_16_step_r(rng); + return pcg_output_xsh_rr_16_8(oldstate); +} + +inline uint8_t pcg_oneseq_16_xsh_rr_8_boundedrand_r(struct pcg_state_16 *rng, + uint8_t bound) { + uint8_t threshold = ((uint8_t)(-bound)) % bound; + for (;;) { + uint8_t r = pcg_oneseq_16_xsh_rr_8_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint16_t pcg_oneseq_32_xsh_rr_16_random_r(struct pcg_state_32 *rng) { + uint32_t oldstate = rng->state; + pcg_oneseq_32_step_r(rng); + return pcg_output_xsh_rr_32_16(oldstate); +} + +inline uint16_t pcg_oneseq_32_xsh_rr_16_boundedrand_r(struct pcg_state_32 *rng, + uint16_t bound) { + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_oneseq_32_xsh_rr_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t pcg_oneseq_64_xsh_rr_32_random_r(struct pcg_state_64 *rng) { + uint64_t oldstate = rng->state; + pcg_oneseq_64_step_r(rng); + return pcg_output_xsh_rr_64_32(oldstate); +} + +inline uint32_t pcg_oneseq_64_xsh_rr_32_boundedrand_r(struct pcg_state_64 *rng, + uint32_t bound) { + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_oneseq_64_xsh_rr_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_oneseq_128_xsh_rr_64_random_r(struct pcg_state_128 *rng) { + pcg_oneseq_128_step_r(rng); + return pcg_output_xsh_rr_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_oneseq_128_xsh_rr_64_boundedrand_r(struct pcg_state_128 *rng, + uint64_t bound) { + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_oneseq_128_xsh_rr_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint8_t pcg_unique_16_xsh_rr_8_random_r(struct pcg_state_16 *rng) { + uint16_t oldstate = rng->state; + pcg_unique_16_step_r(rng); + return pcg_output_xsh_rr_16_8(oldstate); +} + +inline uint8_t pcg_unique_16_xsh_rr_8_boundedrand_r(struct pcg_state_16 *rng, + uint8_t bound) { + uint8_t threshold = ((uint8_t)(-bound)) % bound; + for (;;) { + uint8_t r = pcg_unique_16_xsh_rr_8_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint16_t pcg_unique_32_xsh_rr_16_random_r(struct pcg_state_32 *rng) { + uint32_t oldstate = rng->state; + pcg_unique_32_step_r(rng); + return pcg_output_xsh_rr_32_16(oldstate); +} + +inline uint16_t pcg_unique_32_xsh_rr_16_boundedrand_r(struct pcg_state_32 *rng, + uint16_t bound) { + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_unique_32_xsh_rr_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t pcg_unique_64_xsh_rr_32_random_r(struct pcg_state_64 *rng) { + uint64_t oldstate = rng->state; + pcg_unique_64_step_r(rng); + return pcg_output_xsh_rr_64_32(oldstate); +} + +inline uint32_t pcg_unique_64_xsh_rr_32_boundedrand_r(struct pcg_state_64 *rng, + uint32_t bound) { + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_unique_64_xsh_rr_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_unique_128_xsh_rr_64_random_r(struct pcg_state_128 *rng) { + pcg_unique_128_step_r(rng); + return pcg_output_xsh_rr_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_unique_128_xsh_rr_64_boundedrand_r(struct pcg_state_128 *rng, + uint64_t bound) { + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_unique_128_xsh_rr_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint8_t +pcg_setseq_16_xsh_rr_8_random_r(struct pcg_state_setseq_16 *rng) { + uint16_t oldstate = rng->state; + pcg_setseq_16_step_r(rng); + return pcg_output_xsh_rr_16_8(oldstate); +} + +inline uint8_t +pcg_setseq_16_xsh_rr_8_boundedrand_r(struct pcg_state_setseq_16 *rng, + uint8_t bound) { + uint8_t threshold = ((uint8_t)(-bound)) % bound; + for (;;) { + uint8_t r = pcg_setseq_16_xsh_rr_8_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint16_t +pcg_setseq_32_xsh_rr_16_random_r(struct pcg_state_setseq_32 *rng) { + uint32_t oldstate = rng->state; + pcg_setseq_32_step_r(rng); + return pcg_output_xsh_rr_32_16(oldstate); +} + +inline uint16_t +pcg_setseq_32_xsh_rr_16_boundedrand_r(struct pcg_state_setseq_32 *rng, + uint16_t bound) { + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_setseq_32_xsh_rr_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t +pcg_setseq_64_xsh_rr_32_random_r(struct pcg_state_setseq_64 *rng) { + uint64_t oldstate = rng->state; + pcg_setseq_64_step_r(rng); + return pcg_output_xsh_rr_64_32(oldstate); +} + +inline uint32_t +pcg_setseq_64_xsh_rr_32_boundedrand_r(struct pcg_state_setseq_64 *rng, + uint32_t bound) { + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_setseq_64_xsh_rr_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_setseq_128_xsh_rr_64_random_r(struct pcg_state_setseq_128 *rng) { + pcg_setseq_128_step_r(rng); + return pcg_output_xsh_rr_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_setseq_128_xsh_rr_64_boundedrand_r(struct pcg_state_setseq_128 *rng, + uint64_t bound) { + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_setseq_128_xsh_rr_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint8_t pcg_mcg_16_xsh_rr_8_random_r(struct pcg_state_16 *rng) { + uint16_t oldstate = rng->state; + pcg_mcg_16_step_r(rng); + return pcg_output_xsh_rr_16_8(oldstate); +} + +inline uint8_t pcg_mcg_16_xsh_rr_8_boundedrand_r(struct pcg_state_16 *rng, + uint8_t bound) { + uint8_t threshold = ((uint8_t)(-bound)) % bound; + for (;;) { + uint8_t r = pcg_mcg_16_xsh_rr_8_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint16_t pcg_mcg_32_xsh_rr_16_random_r(struct pcg_state_32 *rng) { + uint32_t oldstate = rng->state; + pcg_mcg_32_step_r(rng); + return pcg_output_xsh_rr_32_16(oldstate); +} + +inline uint16_t pcg_mcg_32_xsh_rr_16_boundedrand_r(struct pcg_state_32 *rng, + uint16_t bound) { + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_mcg_32_xsh_rr_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t pcg_mcg_64_xsh_rr_32_random_r(struct pcg_state_64 *rng) { + uint64_t oldstate = rng->state; + pcg_mcg_64_step_r(rng); + return pcg_output_xsh_rr_64_32(oldstate); +} + +inline uint32_t pcg_mcg_64_xsh_rr_32_boundedrand_r(struct pcg_state_64 *rng, + uint32_t bound) { + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_mcg_64_xsh_rr_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_mcg_128_xsh_rr_64_random_r(struct pcg_state_128 *rng) { + pcg_mcg_128_step_r(rng); + return pcg_output_xsh_rr_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_mcg_128_xsh_rr_64_boundedrand_r(struct pcg_state_128 *rng, + uint64_t bound) { + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_mcg_128_xsh_rr_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +/* Generation functions for RXS M XS (no MCG versions because they + * don't make sense when you want to use the entire state) + */ + +inline uint8_t pcg_oneseq_8_rxs_m_xs_8_random_r(struct pcg_state_8 *rng) { + uint8_t oldstate = rng->state; + pcg_oneseq_8_step_r(rng); + return pcg_output_rxs_m_xs_8_8(oldstate); +} + +inline uint8_t pcg_oneseq_8_rxs_m_xs_8_boundedrand_r(struct pcg_state_8 *rng, + uint8_t bound) { + uint8_t threshold = ((uint8_t)(-bound)) % bound; + for (;;) { + uint8_t r = pcg_oneseq_8_rxs_m_xs_8_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint16_t pcg_oneseq_16_rxs_m_xs_16_random_r(struct pcg_state_16 *rng) { + uint16_t oldstate = rng->state; + pcg_oneseq_16_step_r(rng); + return pcg_output_rxs_m_xs_16_16(oldstate); +} + +inline uint16_t +pcg_oneseq_16_rxs_m_xs_16_boundedrand_r(struct pcg_state_16 *rng, + uint16_t bound) { + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_oneseq_16_rxs_m_xs_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t pcg_oneseq_32_rxs_m_xs_32_random_r(struct pcg_state_32 *rng) { + uint32_t oldstate = rng->state; + pcg_oneseq_32_step_r(rng); + return pcg_output_rxs_m_xs_32_32(oldstate); +} + +inline uint32_t +pcg_oneseq_32_rxs_m_xs_32_boundedrand_r(struct pcg_state_32 *rng, + uint32_t bound) { + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_oneseq_32_rxs_m_xs_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint64_t pcg_oneseq_64_rxs_m_xs_64_random_r(struct pcg_state_64 *rng) { + uint64_t oldstate = rng->state; + pcg_oneseq_64_step_r(rng); + return pcg_output_rxs_m_xs_64_64(oldstate); +} + +inline uint64_t +pcg_oneseq_64_rxs_m_xs_64_boundedrand_r(struct pcg_state_64 *rng, + uint64_t bound) { + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_oneseq_64_rxs_m_xs_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline pcg128_t +pcg_oneseq_128_rxs_m_xs_128_random_r(struct pcg_state_128 *rng) { + pcg_oneseq_128_step_r(rng); + return pcg_output_rxs_m_xs_128_128(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline pcg128_t +pcg_oneseq_128_rxs_m_xs_128_boundedrand_r(struct pcg_state_128 *rng, + pcg128_t bound) { + pcg128_t threshold = -bound % bound; + for (;;) { + pcg128_t r = pcg_oneseq_128_rxs_m_xs_128_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint16_t pcg_unique_16_rxs_m_xs_16_random_r(struct pcg_state_16 *rng) { + uint16_t oldstate = rng->state; + pcg_unique_16_step_r(rng); + return pcg_output_rxs_m_xs_16_16(oldstate); +} + +inline uint16_t +pcg_unique_16_rxs_m_xs_16_boundedrand_r(struct pcg_state_16 *rng, + uint16_t bound) { + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_unique_16_rxs_m_xs_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t pcg_unique_32_rxs_m_xs_32_random_r(struct pcg_state_32 *rng) { + uint32_t oldstate = rng->state; + pcg_unique_32_step_r(rng); + return pcg_output_rxs_m_xs_32_32(oldstate); +} + +inline uint32_t +pcg_unique_32_rxs_m_xs_32_boundedrand_r(struct pcg_state_32 *rng, + uint32_t bound) { + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_unique_32_rxs_m_xs_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint64_t pcg_unique_64_rxs_m_xs_64_random_r(struct pcg_state_64 *rng) { + uint64_t oldstate = rng->state; + pcg_unique_64_step_r(rng); + return pcg_output_rxs_m_xs_64_64(oldstate); +} + +inline uint64_t +pcg_unique_64_rxs_m_xs_64_boundedrand_r(struct pcg_state_64 *rng, + uint64_t bound) { + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_unique_64_rxs_m_xs_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline pcg128_t +pcg_unique_128_rxs_m_xs_128_random_r(struct pcg_state_128 *rng) { + pcg_unique_128_step_r(rng); + return pcg_output_rxs_m_xs_128_128(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline pcg128_t +pcg_unique_128_rxs_m_xs_128_boundedrand_r(struct pcg_state_128 *rng, + pcg128_t bound) { + pcg128_t threshold = -bound % bound; + for (;;) { + pcg128_t r = pcg_unique_128_rxs_m_xs_128_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint8_t +pcg_setseq_8_rxs_m_xs_8_random_r(struct pcg_state_setseq_8 *rng) { + uint8_t oldstate = rng->state; + pcg_setseq_8_step_r(rng); + return pcg_output_rxs_m_xs_8_8(oldstate); +} + +inline uint8_t +pcg_setseq_8_rxs_m_xs_8_boundedrand_r(struct pcg_state_setseq_8 *rng, + uint8_t bound) { + uint8_t threshold = ((uint8_t)(-bound)) % bound; + for (;;) { + uint8_t r = pcg_setseq_8_rxs_m_xs_8_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint16_t +pcg_setseq_16_rxs_m_xs_16_random_r(struct pcg_state_setseq_16 *rng) { + uint16_t oldstate = rng->state; + pcg_setseq_16_step_r(rng); + return pcg_output_rxs_m_xs_16_16(oldstate); +} + +inline uint16_t +pcg_setseq_16_rxs_m_xs_16_boundedrand_r(struct pcg_state_setseq_16 *rng, + uint16_t bound) { + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_setseq_16_rxs_m_xs_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t +pcg_setseq_32_rxs_m_xs_32_random_r(struct pcg_state_setseq_32 *rng) { + uint32_t oldstate = rng->state; + pcg_setseq_32_step_r(rng); + return pcg_output_rxs_m_xs_32_32(oldstate); +} + +inline uint32_t +pcg_setseq_32_rxs_m_xs_32_boundedrand_r(struct pcg_state_setseq_32 *rng, + uint32_t bound) { + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_setseq_32_rxs_m_xs_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint64_t +pcg_setseq_64_rxs_m_xs_64_random_r(struct pcg_state_setseq_64 *rng) { + uint64_t oldstate = rng->state; + pcg_setseq_64_step_r(rng); + return pcg_output_rxs_m_xs_64_64(oldstate); +} + +inline uint64_t +pcg_setseq_64_rxs_m_xs_64_boundedrand_r(struct pcg_state_setseq_64 *rng, + uint64_t bound) { + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_setseq_64_rxs_m_xs_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline pcg128_t +pcg_setseq_128_rxs_m_xs_128_random_r(struct pcg_state_setseq_128 *rng) { + pcg_setseq_128_step_r(rng); + return pcg_output_rxs_m_xs_128_128(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline pcg128_t +pcg_setseq_128_rxs_m_xs_128_boundedrand_r(struct pcg_state_setseq_128 *rng, + pcg128_t bound) { + pcg128_t threshold = -bound % bound; + for (;;) { + pcg128_t r = pcg_setseq_128_rxs_m_xs_128_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +/* Generation functions for XSL RR (only defined for "large" types) */ + +inline uint32_t pcg_oneseq_64_xsl_rr_32_random_r(struct pcg_state_64 *rng) { + uint64_t oldstate = rng->state; + pcg_oneseq_64_step_r(rng); + return pcg_output_xsl_rr_64_32(oldstate); +} + +inline uint32_t pcg_oneseq_64_xsl_rr_32_boundedrand_r(struct pcg_state_64 *rng, + uint32_t bound) { + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_oneseq_64_xsl_rr_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_oneseq_128_xsl_rr_64_random_r(struct pcg_state_128 *rng) { + pcg_oneseq_128_step_r(rng); + return pcg_output_xsl_rr_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_oneseq_128_xsl_rr_64_boundedrand_r(struct pcg_state_128 *rng, + uint64_t bound) { + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_oneseq_128_xsl_rr_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint32_t pcg_unique_64_xsl_rr_32_random_r(struct pcg_state_64 *rng) { + uint64_t oldstate = rng->state; + pcg_unique_64_step_r(rng); + return pcg_output_xsl_rr_64_32(oldstate); +} + +inline uint32_t pcg_unique_64_xsl_rr_32_boundedrand_r(struct pcg_state_64 *rng, + uint32_t bound) { + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_unique_64_xsl_rr_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_unique_128_xsl_rr_64_random_r(struct pcg_state_128 *rng) { + pcg_unique_128_step_r(rng); + return pcg_output_xsl_rr_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_unique_128_xsl_rr_64_boundedrand_r(struct pcg_state_128 *rng, + uint64_t bound) { + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_unique_128_xsl_rr_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint32_t +pcg_setseq_64_xsl_rr_32_random_r(struct pcg_state_setseq_64 *rng) { + uint64_t oldstate = rng->state; + pcg_setseq_64_step_r(rng); + return pcg_output_xsl_rr_64_32(oldstate); +} + +inline uint32_t +pcg_setseq_64_xsl_rr_32_boundedrand_r(struct pcg_state_setseq_64 *rng, + uint32_t bound) { + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_setseq_64_xsl_rr_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_setseq_128_xsl_rr_64_random_r(struct pcg_state_setseq_128 *rng) { + pcg_setseq_128_step_r(rng); + return pcg_output_xsl_rr_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_setseq_128_xsl_rr_64_boundedrand_r(struct pcg_state_setseq_128 *rng, + uint64_t bound) { + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_setseq_128_xsl_rr_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint32_t pcg_mcg_64_xsl_rr_32_random_r(struct pcg_state_64 *rng) { + uint64_t oldstate = rng->state; + pcg_mcg_64_step_r(rng); + return pcg_output_xsl_rr_64_32(oldstate); +} + +inline uint32_t pcg_mcg_64_xsl_rr_32_boundedrand_r(struct pcg_state_64 *rng, + uint32_t bound) { + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_mcg_64_xsl_rr_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_mcg_128_xsl_rr_64_random_r(struct pcg_state_128 *rng) { + pcg_mcg_128_step_r(rng); + return pcg_output_xsl_rr_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_mcg_128_xsl_rr_64_boundedrand_r(struct pcg_state_128 *rng, + uint64_t bound) { + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_mcg_128_xsl_rr_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +/* Generation functions for XSL RR RR (only defined for "large" types) */ + +inline uint64_t pcg_oneseq_64_xsl_rr_rr_64_random_r(struct pcg_state_64 *rng) { + uint64_t oldstate = rng->state; + pcg_oneseq_64_step_r(rng); + return pcg_output_xsl_rr_rr_64_64(oldstate); +} + +inline uint64_t +pcg_oneseq_64_xsl_rr_rr_64_boundedrand_r(struct pcg_state_64 *rng, + uint64_t bound) { + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_oneseq_64_xsl_rr_rr_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline pcg128_t +pcg_oneseq_128_xsl_rr_rr_128_random_r(struct pcg_state_128 *rng) { + pcg_oneseq_128_step_r(rng); + return pcg_output_xsl_rr_rr_128_128(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline pcg128_t +pcg_oneseq_128_xsl_rr_rr_128_boundedrand_r(struct pcg_state_128 *rng, + pcg128_t bound) { + pcg128_t threshold = -bound % bound; + for (;;) { + pcg128_t r = pcg_oneseq_128_xsl_rr_rr_128_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint64_t pcg_unique_64_xsl_rr_rr_64_random_r(struct pcg_state_64 *rng) { + uint64_t oldstate = rng->state; + pcg_unique_64_step_r(rng); + return pcg_output_xsl_rr_rr_64_64(oldstate); +} + +inline uint64_t +pcg_unique_64_xsl_rr_rr_64_boundedrand_r(struct pcg_state_64 *rng, + uint64_t bound) { + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_unique_64_xsl_rr_rr_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline pcg128_t +pcg_unique_128_xsl_rr_rr_128_random_r(struct pcg_state_128 *rng) { + pcg_unique_128_step_r(rng); + return pcg_output_xsl_rr_rr_128_128(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline pcg128_t +pcg_unique_128_xsl_rr_rr_128_boundedrand_r(struct pcg_state_128 *rng, + pcg128_t bound) { + pcg128_t threshold = -bound % bound; + for (;;) { + pcg128_t r = pcg_unique_128_xsl_rr_rr_128_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint64_t +pcg_setseq_64_xsl_rr_rr_64_random_r(struct pcg_state_setseq_64 *rng) { + uint64_t oldstate = rng->state; + pcg_setseq_64_step_r(rng); + return pcg_output_xsl_rr_rr_64_64(oldstate); +} + +inline uint64_t +pcg_setseq_64_xsl_rr_rr_64_boundedrand_r(struct pcg_state_setseq_64 *rng, + uint64_t bound) { + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_setseq_64_xsl_rr_rr_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline pcg128_t +pcg_setseq_128_xsl_rr_rr_128_random_r(struct pcg_state_setseq_128 *rng) { + pcg_setseq_128_step_r(rng); + return pcg_output_xsl_rr_rr_128_128(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline pcg128_t +pcg_setseq_128_xsl_rr_rr_128_boundedrand_r(struct pcg_state_setseq_128 *rng, + pcg128_t bound) { + pcg128_t threshold = -bound % bound; + for (;;) { + pcg128_t r = pcg_setseq_128_xsl_rr_rr_128_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +//// Typedefs +typedef struct pcg_state_setseq_64 pcg32_random_t; +typedef struct pcg_state_64 pcg32s_random_t; +typedef struct pcg_state_64 pcg32u_random_t; +typedef struct pcg_state_64 pcg32f_random_t; +//// random_r +#define pcg32_random_r pcg_setseq_64_xsh_rr_32_random_r +#define pcg32s_random_r pcg_oneseq_64_xsh_rr_32_random_r +#define pcg32u_random_r pcg_unique_64_xsh_rr_32_random_r +#define pcg32f_random_r pcg_mcg_64_xsh_rs_32_random_r +//// boundedrand_r +#define pcg32_boundedrand_r pcg_setseq_64_xsh_rr_32_boundedrand_r +#define pcg32s_boundedrand_r pcg_oneseq_64_xsh_rr_32_boundedrand_r +#define pcg32u_boundedrand_r pcg_unique_64_xsh_rr_32_boundedrand_r +#define pcg32f_boundedrand_r pcg_mcg_64_xsh_rs_32_boundedrand_r +//// srandom_r +#define pcg32_srandom_r pcg_setseq_64_srandom_r +#define pcg32s_srandom_r pcg_oneseq_64_srandom_r +#define pcg32u_srandom_r pcg_unique_64_srandom_r +#define pcg32f_srandom_r pcg_mcg_64_srandom_r +//// advance_r +#define pcg32_advance_r pcg_setseq_64_advance_r +#define pcg32s_advance_r pcg_oneseq_64_advance_r +#define pcg32u_advance_r pcg_unique_64_advance_r +#define pcg32f_advance_r pcg_mcg_64_advance_r + +#if PCG_HAS_128BIT_OPS +//// Typedefs +typedef struct pcg_state_setseq_128 pcg64_random_t; +typedef struct pcg_state_128 pcg64s_random_t; +typedef struct pcg_state_128 pcg64u_random_t; +typedef struct pcg_state_128 pcg64f_random_t; +//// random_r +#define pcg64_random_r pcg_setseq_128_xsl_rr_64_random_r +#define pcg64s_random_r pcg_oneseq_128_xsl_rr_64_random_r +#define pcg64u_random_r pcg_unique_128_xsl_rr_64_random_r +#define pcg64f_random_r pcg_mcg_128_xsl_rr_64_random_r +//// boundedrand_r +#define pcg64_boundedrand_r pcg_setseq_128_xsl_rr_64_boundedrand_r +#define pcg64s_boundedrand_r pcg_oneseq_128_xsl_rr_64_boundedrand_r +#define pcg64u_boundedrand_r pcg_unique_128_xsl_rr_64_boundedrand_r +#define pcg64f_boundedrand_r pcg_mcg_128_xsl_rr_64_boundedrand_r +//// srandom_r +#define pcg64_srandom_r pcg_setseq_128_srandom_r +#define pcg64s_srandom_r pcg_oneseq_128_srandom_r +#define pcg64u_srandom_r pcg_unique_128_srandom_r +#define pcg64f_srandom_r pcg_mcg_128_srandom_r +//// advance_r +#define pcg64_advance_r pcg_setseq_128_advance_r +#define pcg64s_advance_r pcg_oneseq_128_advance_r +#define pcg64u_advance_r pcg_unique_128_advance_r +#define pcg64f_advance_r pcg_mcg_128_advance_r +#endif + +//// Typedefs +typedef struct pcg_state_8 pcg8si_random_t; +typedef struct pcg_state_16 pcg16si_random_t; +typedef struct pcg_state_32 pcg32si_random_t; +typedef struct pcg_state_64 pcg64si_random_t; +//// random_r +#define pcg8si_random_r pcg_oneseq_8_rxs_m_xs_8_random_r +#define pcg16si_random_r pcg_oneseq_16_rxs_m_xs_16_random_r +#define pcg32si_random_r pcg_oneseq_32_rxs_m_xs_32_random_r +#define pcg64si_random_r pcg_oneseq_64_rxs_m_xs_64_random_r +//// boundedrand_r +#define pcg8si_boundedrand_r pcg_oneseq_8_rxs_m_xs_8_boundedrand_r +#define pcg16si_boundedrand_r pcg_oneseq_16_rxs_m_xs_16_boundedrand_r +#define pcg32si_boundedrand_r pcg_oneseq_32_rxs_m_xs_32_boundedrand_r +#define pcg64si_boundedrand_r pcg_oneseq_64_rxs_m_xs_64_boundedrand_r +//// srandom_r +#define pcg8si_srandom_r pcg_oneseq_8_srandom_r +#define pcg16si_srandom_r pcg_oneseq_16_srandom_r +#define pcg32si_srandom_r pcg_oneseq_32_srandom_r +#define pcg64si_srandom_r pcg_oneseq_64_srandom_r +//// advance_r +#define pcg8si_advance_r pcg_oneseq_8_advance_r +#define pcg16si_advance_r pcg_oneseq_16_advance_r +#define pcg32si_advance_r pcg_oneseq_32_advance_r +#define pcg64si_advance_r pcg_oneseq_64_advance_r + +#if PCG_HAS_128BIT_OPS +typedef struct pcg_state_128 pcg128si_random_t; +#define pcg128si_random_r pcg_oneseq_128_rxs_m_xs_128_random_r +#define pcg128si_boundedrand_r pcg_oneseq_128_rxs_m_xs_128_boundedrand_r +#define pcg128si_srandom_r pcg_oneseq_128_srandom_r +#define pcg128si_advance_r pcg_oneseq_128_advance_r +#endif + +//// Typedefs +typedef struct pcg_state_setseq_8 pcg8i_random_t; +typedef struct pcg_state_setseq_16 pcg16i_random_t; +typedef struct pcg_state_setseq_32 pcg32i_random_t; +typedef struct pcg_state_setseq_64 pcg64i_random_t; +//// random_r +#define pcg8i_random_r pcg_setseq_8_rxs_m_xs_8_random_r +#define pcg16i_random_r pcg_setseq_16_rxs_m_xs_16_random_r +#define pcg32i_random_r pcg_setseq_32_rxs_m_xs_32_random_r +#define pcg64i_random_r pcg_setseq_64_rxs_m_xs_64_random_r +//// boundedrand_r +#define pcg8i_boundedrand_r pcg_setseq_8_rxs_m_xs_8_boundedrand_r +#define pcg16i_boundedrand_r pcg_setseq_16_rxs_m_xs_16_boundedrand_r +#define pcg32i_boundedrand_r pcg_setseq_32_rxs_m_xs_32_boundedrand_r +#define pcg64i_boundedrand_r pcg_setseq_64_rxs_m_xs_64_boundedrand_r +//// srandom_r +#define pcg8i_srandom_r pcg_setseq_8_srandom_r +#define pcg16i_srandom_r pcg_setseq_16_srandom_r +#define pcg32i_srandom_r pcg_setseq_32_srandom_r +#define pcg64i_srandom_r pcg_setseq_64_srandom_r +//// advance_r +#define pcg8i_advance_r pcg_setseq_8_advance_r +#define pcg16i_advance_r pcg_setseq_16_advance_r +#define pcg32i_advance_r pcg_setseq_32_advance_r +#define pcg64i_advance_r pcg_setseq_64_advance_r + +#if PCG_HAS_128BIT_OPS +typedef struct pcg_state_setseq_128 pcg128i_random_t; +#define pcg128i_random_r pcg_setseq_128_rxs_m_xs_128_random_r +#define pcg128i_boundedrand_r pcg_setseq_128_rxs_m_xs_128_boundedrand_r +#define pcg128i_srandom_r pcg_setseq_128_srandom_r +#define pcg128i_advance_r pcg_setseq_128_advance_r +#endif + +extern uint32_t pcg32_random(); +extern uint32_t pcg32_boundedrand(uint32_t bound); +extern void pcg32_srandom(uint64_t seed, uint64_t seq); +extern void pcg32_advance(uint64_t delta); + +#if PCG_HAS_128BIT_OPS +extern uint64_t pcg64_random(); +extern uint64_t pcg64_boundedrand(uint64_t bound); +extern void pcg64_srandom(pcg128_t seed, pcg128_t seq); +extern void pcg64_advance(pcg128_t delta); +#endif + +/* + * Static initialization constants (if you can't call srandom for some + * bizarre reason). + */ + +#define PCG32_INITIALIZER PCG_STATE_SETSEQ_64_INITIALIZER +#define PCG32U_INITIALIZER PCG_STATE_UNIQUE_64_INITIALIZER +#define PCG32S_INITIALIZER PCG_STATE_ONESEQ_64_INITIALIZER +#define PCG32F_INITIALIZER PCG_STATE_MCG_64_INITIALIZER + +#if PCG_HAS_128BIT_OPS +#define PCG64_INITIALIZER PCG_STATE_SETSEQ_128_INITIALIZER +#define PCG64U_INITIALIZER PCG_STATE_UNIQUE_128_INITIALIZER +#define PCG64S_INITIALIZER PCG_STATE_ONESEQ_128_INITIALIZER +#define PCG64F_INITIALIZER PCG_STATE_MCG_128_INITIALIZER +#endif + +#define PCG8SI_INITIALIZER PCG_STATE_ONESEQ_8_INITIALIZER +#define PCG16SI_INITIALIZER PCG_STATE_ONESEQ_16_INITIALIZER +#define PCG32SI_INITIALIZER PCG_STATE_ONESEQ_32_INITIALIZER +#define PCG64SI_INITIALIZER PCG_STATE_ONESEQ_64_INITIALIZER +#if PCG_HAS_128BIT_OPS +#define PCG128SI_INITIALIZER PCG_STATE_ONESEQ_128_INITIALIZER +#endif + +#define PCG8I_INITIALIZER PCG_STATE_SETSEQ_8_INITIALIZER +#define PCG16I_INITIALIZER PCG_STATE_SETSEQ_16_INITIALIZER +#define PCG32I_INITIALIZER PCG_STATE_SETSEQ_32_INITIALIZER +#define PCG64I_INITIALIZER PCG_STATE_SETSEQ_64_INITIALIZER +#if PCG_HAS_128BIT_OPS +#define PCG128I_INITIALIZER PCG_STATE_SETSEQ_128_INITIALIZER +#endif + +#if __cplusplus +} +#endif + +#endif // PCG_VARIANTS_H_INCLUDED diff --git a/numpy/random/src/philox/LICENSE.md b/numpy/random/src/philox/LICENSE.md new file mode 100644 index 000000000000..9738e44de3b4 --- /dev/null +++ b/numpy/random/src/philox/LICENSE.md @@ -0,0 +1,31 @@ +# PHILOX + +Copyright 2010-2012, D. E. Shaw Research. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions, and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions, and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of D. E. Shaw Research nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/numpy/random/src/philox/philox-benchmark.c b/numpy/random/src/philox/philox-benchmark.c new file mode 100644 index 000000000000..9856a9b8e2a4 --- /dev/null +++ b/numpy/random/src/philox/philox-benchmark.c @@ -0,0 +1,38 @@ +/* + * Simple benchmark command + * + * cl philox-benchmark.c /Ox + * + * gcc philox-benchmark.c -O3 -o philox-benchmark + * + * Requires the Random123 directory containing header files to be located in the + * same directory (not included). + */ +#include "Random123/philox.h" +#include <inttypes.h> +#include <stdio.h> +#include <time.h> + +#define N 1000000000 + +int main() { + philox4x64_ctr_t ctr = {{0, 0, 0, 0}}; + philox4x64_key_t key = {{0, 0xDEADBEAF}}; + philox4x64_ctr_t out; + uint64_t count = 0, sum = 0; + int i, j; + clock_t begin = clock(); + for (i = 0; i < N / 4UL; i++) { + ctr.v[0]++; + out = philox4x64_R(philox4x64_rounds, ctr, key); + for (j = 0; j < 4; j++) { + sum += out.v[j]; + count++; + } + } + clock_t end = clock(); + double time_spent = (double)(end - begin) / CLOCKS_PER_SEC; + printf("0x%" PRIx64 "\ncount: %" PRIu64 "\n", sum, count); + printf("%" PRIu64 " randoms per second\n", + (uint64_t)(N / time_spent) / 1000000 * 1000000); +} diff --git a/numpy/random/src/philox/philox-test-data-gen.c b/numpy/random/src/philox/philox-test-data-gen.c new file mode 100644 index 000000000000..a5fcaa690151 --- /dev/null +++ b/numpy/random/src/philox/philox-test-data-gen.c @@ -0,0 +1,82 @@ +/* + * Generate testing csv files + * + * cl philox-test-data-gen.c /Ox + * philox-test-data-gen.exe + * + * gcc philox-test-data-gen.c -o philox-test-data-gen + * ./philox-test-data-gen + * + * Requires the Random123 directory containing header files to be located in the + * same directory (not included). + * + */ + +#include "../splitmix64/splitmix64.h" +#include "Random123/philox.h" +#include <inttypes.h> +#include <stdio.h> + +#define N 1000 + +int main() { + philox4x64_ctr_t ctr = {{0, 0, 0, 0}}; + philox4x64_key_t key = {{0, 0}}; + uint64_t state, seed = 0xDEADBEAF; + philox4x64_ctr_t out; + uint64_t store[N]; + state = seed; + int i, j; + for (i = 0; i < 2; i++) { + key.v[i] = splitmix64_next(&state); + } + for (i = 0; i < N / 4UL; i++) { + ctr.v[0]++; + out = philox4x64_R(philox4x64_rounds, ctr, key); + for (j = 0; j < 4; j++) { + store[i * 4 + j] = out.v[j]; + } + } + + FILE *fp; + fp = fopen("philox-testset-1.csv", "w"); + if (fp == NULL) { + printf("Couldn't open file\n"); + return -1; + } + fprintf(fp, "seed, 0x%" PRIx64 "\n", seed); + for (i = 0; i < N; i++) { + fprintf(fp, "%d, 0x%" PRIx64 "\n", i, store[i]); + if (i == 999) { + printf("%d, 0x%" PRIx64 "\n", i, store[i]); + } + } + fclose(fp); + + ctr.v[0] = 0; + state = seed = 0; + for (i = 0; i < 2; i++) { + key.v[i] = splitmix64_next(&state); + } + for (i = 0; i < N / 4UL; i++) { + ctr.v[0]++; + out = philox4x64_R(philox4x64_rounds, ctr, key); + for (j = 0; j < 4; j++) { + store[i * 4 + j] = out.v[j]; + } + } + + fp = fopen("philox-testset-2.csv", "w"); + if (fp == NULL) { + printf("Couldn't open file\n"); + return -1; + } + fprintf(fp, "seed, 0x%" PRIx64 "\n", seed); + for (i = 0; i < N; i++) { + fprintf(fp, "%d, 0x%" PRIx64 "\n", i, store[i]); + if (i == 999) { + printf("%d, 0x%" PRIx64 "\n", i, store[i]); + } + } + fclose(fp); +} diff --git a/numpy/random/src/philox/philox.c b/numpy/random/src/philox/philox.c new file mode 100644 index 000000000000..6f2fad5a4384 --- /dev/null +++ b/numpy/random/src/philox/philox.c @@ -0,0 +1,29 @@ +#include "philox.h" + +extern NPY_INLINE uint64_t philox_next64(philox_state *state); + +extern NPY_INLINE uint32_t philox_next32(philox_state *state); + +extern void philox_jump(philox_state *state) { + /* Advances state as-if 2^128 draws were made */ + state->ctr->v[2]++; + if (state->ctr->v[2] == 0) { + state->ctr->v[3]++; + } +} + +extern void philox_advance(uint64_t *step, philox_state *state) { + int i, carry = 0; + uint64_t v_orig; + for (i = 0; i < 4; i++) { + if (carry == 1) { + state->ctr->v[i]++; + carry = state->ctr->v[i] == 0 ? 1 : 0; + } + v_orig = state->ctr->v[i]; + state->ctr->v[i] += step[i]; + if (state->ctr->v[i] < v_orig && carry == 0) { + carry = 1; + } + } +} diff --git a/numpy/random/src/philox/philox.h b/numpy/random/src/philox/philox.h new file mode 100644 index 000000000000..8844acc15725 --- /dev/null +++ b/numpy/random/src/philox/philox.h @@ -0,0 +1,254 @@ +#ifndef _RANDOMDGEN__PHILOX_H_ +#define _RANDOMDGEN__PHILOX_H_ + +#include "numpy/npy_common.h" +#include <inttypes.h> + +#define PHILOX_BUFFER_SIZE 4L + +struct r123array2x64 { + uint64_t v[2]; +}; +struct r123array4x64 { + uint64_t v[4]; +}; + +enum r123_enum_philox4x64 { philox4x64_rounds = 10 }; +typedef struct r123array4x64 philox4x64_ctr_t; +typedef struct r123array2x64 philox4x64_key_t; +typedef struct r123array2x64 philox4x64_ukey_t; + +static NPY_INLINE struct r123array2x64 +_philox4x64bumpkey(struct r123array2x64 key) { + key.v[0] += (0x9E3779B97F4A7C15ULL); + key.v[1] += (0xBB67AE8584CAA73BULL); + return key; +} + +/* Prefer uint128 if available: GCC, clang, ICC */ +#ifdef __SIZEOF_INT128__ +static NPY_INLINE uint64_t mulhilo64(uint64_t a, uint64_t b, uint64_t *hip) { + __uint128_t product = ((__uint128_t)a) * ((__uint128_t)b); + *hip = product >> 64; + return (uint64_t)product; +} +#else +#if defined(_WIN32) +#include <intrin.h> +#if defined(_WIN64) && defined(_M_AMD64) +#pragma intrinsic(_umul128) +#elif defined(_WIN64) && defined(_M_ARM64) +#pragma intrinsic(__umulh) +static NPY_INLINE uint64_t _umul128(uint64_t a, uint64_t b, uint64_t *high) { + *high = __umulh(a, b); + return a * b; +} +#else +#pragma intrinsic(__emulu) +static NPY_INLINE uint64_t _umul128(uint64_t a, uint64_t b, uint64_t *high) { + + uint64_t a_lo, a_hi, b_lo, b_hi, a_x_b_hi, a_x_b_mid, a_x_b_lo, b_x_a_mid, + carry_bit; + a_lo = (uint32_t)a; + a_hi = a >> 32; + b_lo = (uint32_t)b; + b_hi = b >> 32; + + a_x_b_hi = __emulu(a_hi, b_hi); + a_x_b_mid = __emulu(a_hi, b_lo); + b_x_a_mid = __emulu(b_hi, a_lo); + a_x_b_lo = __emulu(a_lo, b_lo); + + carry_bit = ((uint64_t)(uint32_t)a_x_b_mid + (uint64_t)(uint32_t)b_x_a_mid + + (a_x_b_lo >> 32)) >> + 32; + + *high = a_x_b_hi + (a_x_b_mid >> 32) + (b_x_a_mid >> 32) + carry_bit; + + return a_x_b_lo + ((a_x_b_mid + b_x_a_mid) << 32); +} +#endif +static NPY_INLINE uint64_t mulhilo64(uint64_t a, uint64_t b, uint64_t *hip) { + return _umul128(a, b, hip); +} +#else +static NPY_INLINE uint64_t _umul128(uint64_t a, uint64_t b, uint64_t *high) { + + uint64_t a_lo, a_hi, b_lo, b_hi, a_x_b_hi, a_x_b_mid, a_x_b_lo, b_x_a_mid, + carry_bit; + a_lo = (uint32_t)a; + a_hi = a >> 32; + b_lo = (uint32_t)b; + b_hi = b >> 32; + + a_x_b_hi = a_hi * b_hi; + a_x_b_mid = a_hi * b_lo; + b_x_a_mid = b_hi * a_lo; + a_x_b_lo = a_lo * b_lo; + + carry_bit = ((uint64_t)(uint32_t)a_x_b_mid + (uint64_t)(uint32_t)b_x_a_mid + + (a_x_b_lo >> 32)) >> + 32; + + *high = a_x_b_hi + (a_x_b_mid >> 32) + (b_x_a_mid >> 32) + carry_bit; + + return a_x_b_lo + ((a_x_b_mid + b_x_a_mid) << 32); +} +static NPY_INLINE uint64_t mulhilo64(uint64_t a, uint64_t b, uint64_t *hip) { + return _umul128(a, b, hip); +} +#endif +#endif + +static NPY_INLINE struct r123array4x64 _philox4x64round(struct r123array4x64 ctr, + struct r123array2x64 key); + +static NPY_INLINE struct r123array4x64 _philox4x64round(struct r123array4x64 ctr, + struct r123array2x64 key) { + uint64_t hi0; + uint64_t hi1; + uint64_t lo0 = mulhilo64((0xD2E7470EE14C6C93ULL), ctr.v[0], &hi0); + uint64_t lo1 = mulhilo64((0xCA5A826395121157ULL), ctr.v[2], &hi1); + struct r123array4x64 out = { + {hi1 ^ ctr.v[1] ^ key.v[0], lo1, hi0 ^ ctr.v[3] ^ key.v[1], lo0}}; + return out; +} + +static NPY_INLINE philox4x64_key_t philox4x64keyinit(philox4x64_ukey_t uk) { + return uk; +} +static NPY_INLINE philox4x64_ctr_t philox4x64_R(unsigned int R, + philox4x64_ctr_t ctr, + philox4x64_key_t key); + +static NPY_INLINE philox4x64_ctr_t philox4x64_R(unsigned int R, + philox4x64_ctr_t ctr, + philox4x64_key_t key) { + if (R > 0) { + ctr = _philox4x64round(ctr, key); + } + if (R > 1) { + key = _philox4x64bumpkey(key); + ctr = _philox4x64round(ctr, key); + } + if (R > 2) { + key = _philox4x64bumpkey(key); + ctr = _philox4x64round(ctr, key); + } + if (R > 3) { + key = _philox4x64bumpkey(key); + ctr = _philox4x64round(ctr, key); + } + if (R > 4) { + key = _philox4x64bumpkey(key); + ctr = _philox4x64round(ctr, key); + } + if (R > 5) { + key = _philox4x64bumpkey(key); + ctr = _philox4x64round(ctr, key); + } + if (R > 6) { + key = _philox4x64bumpkey(key); + ctr = _philox4x64round(ctr, key); + } + if (R > 7) { + key = _philox4x64bumpkey(key); + ctr = _philox4x64round(ctr, key); + } + if (R > 8) { + key = _philox4x64bumpkey(key); + ctr = _philox4x64round(ctr, key); + } + if (R > 9) { + key = _philox4x64bumpkey(key); + ctr = _philox4x64round(ctr, key); + } + if (R > 10) { + key = _philox4x64bumpkey(key); + ctr = _philox4x64round(ctr, key); + } + if (R > 11) { + key = _philox4x64bumpkey(key); + ctr = _philox4x64round(ctr, key); + } + if (R > 12) { + key = _philox4x64bumpkey(key); + ctr = _philox4x64round(ctr, key); + } + if (R > 13) { + key = _philox4x64bumpkey(key); + ctr = _philox4x64round(ctr, key); + } + if (R > 14) { + key = _philox4x64bumpkey(key); + ctr = _philox4x64round(ctr, key); + } + if (R > 15) { + key = _philox4x64bumpkey(key); + ctr = _philox4x64round(ctr, key); + } + return ctr; +} + +typedef struct s_philox_state { + philox4x64_ctr_t *ctr; + philox4x64_key_t *key; + int buffer_pos; + uint64_t buffer[PHILOX_BUFFER_SIZE]; + int has_uint32; + uint32_t uinteger; +} philox_state; + +static NPY_INLINE uint64_t philox_next(philox_state *state) { + uint64_t out; + int i; + philox4x64_ctr_t ct; + + if (state->buffer_pos < PHILOX_BUFFER_SIZE) { + out = state->buffer[state->buffer_pos]; + state->buffer_pos++; + return out; + } + /* generate 4 new uint64_t */ + state->ctr->v[0]++; + /* Handle carry */ + if (state->ctr->v[0] == 0) { + state->ctr->v[1]++; + if (state->ctr->v[1] == 0) { + state->ctr->v[2]++; + if (state->ctr->v[2] == 0) { + state->ctr->v[3]++; + } + } + } + ct = philox4x64_R(philox4x64_rounds, *state->ctr, *state->key); + for (i = 0; i < 4; i++) { + state->buffer[i] = ct.v[i]; + } + state->buffer_pos = 1; + return state->buffer[0]; +} + +static NPY_INLINE uint64_t philox_next64(philox_state *state) { + return philox_next(state); +} + +static NPY_INLINE uint32_t philox_next32(philox_state *state) { + uint64_t next; + + if (state->has_uint32) { + state->has_uint32 = 0; + return state->uinteger; + } + next = philox_next(state); + + state->has_uint32 = 1; + state->uinteger = (uint32_t)(next >> 32); + return (uint32_t)(next & 0xffffffff); +} + +extern void philox_jump(philox_state *state); + +extern void philox_advance(uint64_t *step, philox_state *state); + +#endif diff --git a/numpy/random/src/sfc64/LICENSE.md b/numpy/random/src/sfc64/LICENSE.md new file mode 100644 index 000000000000..21dd604afe16 --- /dev/null +++ b/numpy/random/src/sfc64/LICENSE.md @@ -0,0 +1,27 @@ +# SFC64 + +## The MIT License + +Adapted from a C++ implementation of Chris Doty-Humphrey's SFC PRNG. + +https://gist.github.com/imneme/f1f7821f07cf76504a97f6537c818083 + +Copyright (c) 2018 Melissa E. O'Neill + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/numpy/random/src/sfc64/sfc64.c b/numpy/random/src/sfc64/sfc64.c new file mode 100644 index 000000000000..5546fff08ba5 --- /dev/null +++ b/numpy/random/src/sfc64/sfc64.c @@ -0,0 +1,39 @@ +#include "sfc64.h" + +extern void sfc64_set_seed(sfc64_state *state, uint64_t *seed) { + /* Conservatively stick with the original formula. With SeedSequence, it + * might be fine to just set the state with 4 uint64s and be done. + */ + int i; + + state->s[0] = seed[0]; + state->s[1] = seed[1]; + state->s[2] = seed[2]; + state->s[3] = 1; + + for (i=0; i<12; i++) { + (void)sfc64_next(state->s); + } +} + +extern void sfc64_get_state(sfc64_state *state, uint64_t *state_arr, int *has_uint32, + uint32_t *uinteger) { + int i; + + for (i=0; i<4; i++) { + state_arr[i] = state->s[i]; + } + has_uint32[0] = state->has_uint32; + uinteger[0] = state->uinteger; +} + +extern void sfc64_set_state(sfc64_state *state, uint64_t *state_arr, int has_uint32, + uint32_t uinteger) { + int i; + + for (i=0; i<4; i++) { + state->s[i] = state_arr[i]; + } + state->has_uint32 = has_uint32; + state->uinteger = uinteger; +} diff --git a/numpy/random/src/sfc64/sfc64.h b/numpy/random/src/sfc64/sfc64.h new file mode 100644 index 000000000000..75c4118d3196 --- /dev/null +++ b/numpy/random/src/sfc64/sfc64.h @@ -0,0 +1,60 @@ +#ifndef _RANDOMDGEN__SFC64_H_ +#define _RANDOMDGEN__SFC64_H_ + +#include "numpy/npy_common.h" +#include <inttypes.h> +#ifdef _WIN32 +#include <stdlib.h> +#endif + +typedef struct s_sfc64_state { + uint64_t s[4]; + int has_uint32; + uint32_t uinteger; +} sfc64_state; + + +static NPY_INLINE uint64_t rotl(const uint64_t value, unsigned int rot) { +#ifdef _WIN32 + return _rotl64(value, rot); +#else + return (value << rot) | (value >> ((-rot) & 63)); +#endif +} + +static NPY_INLINE uint64_t sfc64_next(uint64_t *s) { + const uint64_t tmp = s[0] + s[1] + s[3]++; + + s[0] = s[1] ^ (s[1] >> 11); + s[1] = s[2] + (s[2] << 3); + s[2] = rotl(s[2], 24) + tmp; + + return tmp; +} + + +static NPY_INLINE uint64_t sfc64_next64(sfc64_state *state) { + return sfc64_next(&state->s[0]); +} + +static NPY_INLINE uint32_t sfc64_next32(sfc64_state *state) { + uint64_t next; + if (state->has_uint32) { + state->has_uint32 = 0; + return state->uinteger; + } + next = sfc64_next(&state->s[0]); + state->has_uint32 = 1; + state->uinteger = (uint32_t)(next >> 32); + return (uint32_t)(next & 0xffffffff); +} + +void sfc64_set_seed(sfc64_state *state, uint64_t *seed); + +void sfc64_get_state(sfc64_state *state, uint64_t *state_arr, int *has_uint32, + uint32_t *uinteger); + +void sfc64_set_state(sfc64_state *state, uint64_t *state_arr, int has_uint32, + uint32_t uinteger); + +#endif diff --git a/numpy/random/src/splitmix64/LICENSE.md b/numpy/random/src/splitmix64/LICENSE.md new file mode 100644 index 000000000000..3c4d73b920f6 --- /dev/null +++ b/numpy/random/src/splitmix64/LICENSE.md @@ -0,0 +1,9 @@ +# SPLITMIX64 + +Written in 2015 by Sebastiano Vigna (vigna@acm.org) + +To the extent possible under law, the author has dedicated all copyright +and related and neighboring rights to this software to the public domain +worldwide. This software is distributed without any warranty. + +See <http://creativecommons.org/publicdomain/zero/1.0/>. \ No newline at end of file diff --git a/numpy/random/src/splitmix64/splitmix64.c b/numpy/random/src/splitmix64/splitmix64.c new file mode 100644 index 000000000000..79a845982c5f --- /dev/null +++ b/numpy/random/src/splitmix64/splitmix64.c @@ -0,0 +1,29 @@ +/* Written in 2015 by Sebastiano Vigna (vigna@acm.org) + +To the extent possible under law, the author has dedicated all copyright +and related and neighboring rights to this software to the public domain +worldwide. This software is distributed without any warranty. + +See <http://creativecommons.org/publicdomain/zero/1.0/>. + +Modified 2018 by Kevin Sheppard. Modifications licensed under the NCSA +license. +*/ + +/* This is a fixed-increment version of Java 8's SplittableRandom generator + See http://dx.doi.org/10.1145/2714064.2660195 and + http://docs.oracle.com/javase/8/docs/api/java/util/SplittableRandom.html + + It is a very fast generator passing BigCrush, and it can be useful if + for some reason you absolutely want 64 bits of state; otherwise, we + rather suggest to use a xoroshiro128+ (for moderately parallel + computations) or xorshift1024* (for massively parallel computations) + generator. */ + +#include "splitmix64.h" + +extern inline uint64_t splitmix64_next(uint64_t *state); + +extern inline uint64_t splitmix64_next64(splitmix64_state *state); + +extern inline uint32_t splitmix64_next32(splitmix64_state *state); diff --git a/numpy/random/src/splitmix64/splitmix64.h b/numpy/random/src/splitmix64/splitmix64.h new file mode 100644 index 000000000000..d5877905ea1a --- /dev/null +++ b/numpy/random/src/splitmix64/splitmix64.h @@ -0,0 +1,30 @@ +#include <inttypes.h> + +typedef struct s_splitmix64_state { + uint64_t state; + int has_uint32; + uint32_t uinteger; +} splitmix64_state; + +static inline uint64_t splitmix64_next(uint64_t *state) { + uint64_t z = (state[0] += 0x9e3779b97f4a7c15); + z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9; + z = (z ^ (z >> 27)) * 0x94d049bb133111eb; + return z ^ (z >> 31); +} + +static inline uint64_t splitmix64_next64(splitmix64_state *state) { + return splitmix64_next(&state->state); +} + +static inline uint32_t splitmix64_next32(splitmix64_state *state) { + uint64_t next; + if (state->has_uint32) { + state->has_uint32 = 0; + return state->uinteger; + } + next = splitmix64_next64(state); + state->has_uint32 = 1; + state->uinteger = (uint32_t)(next >> 32); + return (uint32_t)(next & 0xffffffff); +} diff --git a/numpy/random/src/splitmix64/splitmix64.orig.c b/numpy/random/src/splitmix64/splitmix64.orig.c new file mode 100644 index 000000000000..df6133aabf4d --- /dev/null +++ b/numpy/random/src/splitmix64/splitmix64.orig.c @@ -0,0 +1,28 @@ +/* Written in 2015 by Sebastiano Vigna (vigna@acm.org) + +To the extent possible under law, the author has dedicated all copyright +and related and neighboring rights to this software to the public domain +worldwide. This software is distributed without any warranty. + +See <http://creativecommons.org/publicdomain/zero/1.0/>. */ + +#include <stdint.h> + +/* This is a fixed-increment version of Java 8's SplittableRandom generator + See http://dx.doi.org/10.1145/2714064.2660195 and + http://docs.oracle.com/javase/8/docs/api/java/util/SplittableRandom.html + + It is a very fast generator passing BigCrush, and it can be useful if + for some reason you absolutely want 64 bits of state; otherwise, we + rather suggest to use a xoroshiro128+ (for moderately parallel + computations) or xorshift1024* (for massively parallel computations) + generator. */ + +uint64_t x; /* The state can be seeded with any value. */ + +uint64_t next() { + uint64_t z = (x += 0x9e3779b97f4a7c15); + z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9; + z = (z ^ (z >> 27)) * 0x94d049bb133111eb; + return z ^ (z >> 31); +} diff --git a/numpy/random/tests/data/__init__.py b/numpy/random/tests/data/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/numpy/random/tests/data/mt19937-testset-1.csv b/numpy/random/tests/data/mt19937-testset-1.csv new file mode 100644 index 000000000000..b97bfa66f72f --- /dev/null +++ b/numpy/random/tests/data/mt19937-testset-1.csv @@ -0,0 +1,1001 @@ +seed, 0xdeadbeaf +0, 0xc816921f +1, 0xb3623c6d +2, 0x5fa391bb +3, 0x40178d9 +4, 0x7dcc9811 +5, 0x548eb8e6 +6, 0x92ba3125 +7, 0x65fde68d +8, 0x2f81ec95 +9, 0xbd94f7a2 +10, 0xdc4d9bcc +11, 0xa672bf13 +12, 0xb41113e +13, 0xec7e0066 +14, 0x50239372 +15, 0xd9d66b1d +16, 0xab72a161 +17, 0xddc2e29f +18, 0x7ea29ab4 +19, 0x80d141ba +20, 0xb1c7edf1 +21, 0x44d29203 +22, 0xe224d98 +23, 0x5b3e9d26 +24, 0x14fd567c +25, 0x27d98c96 +26, 0x838779fc +27, 0x92a138a +28, 0x5d08965b +29, 0x531e0ad6 +30, 0x984ee8f4 +31, 0x1ed78539 +32, 0x32bd6d8d +33, 0xc37c8516 +34, 0x9aef5c6b +35, 0x3aacd139 +36, 0xd96ed154 +37, 0x489cd1ed +38, 0x2cba4b3b +39, 0x76c6ae72 +40, 0x2dae02b9 +41, 0x52ac5fd6 +42, 0xc2b5e265 +43, 0x630e6a28 +44, 0x3f560d5d +45, 0x9315bdf3 +46, 0xf1055aba +47, 0x840e42c6 +48, 0xf2099c6b +49, 0x15ff7696 +50, 0x7948d146 +51, 0x97342961 +52, 0x7a7a21c +53, 0xc66f4fb1 +54, 0x23c4103e +55, 0xd7321f98 +56, 0xeb7efb75 +57, 0xe02490b5 +58, 0x2aa02de +59, 0x8bee0bf7 +60, 0xfc2da059 +61, 0xae835034 +62, 0x678f2075 +63, 0x6d03094b +64, 0x56455e05 +65, 0x18b32373 +66, 0x8ff0356b +67, 0x1fe442fb +68, 0x3f1ab6c3 +69, 0xb6fd21b +70, 0xfc310eb2 +71, 0xb19e9a4d +72, 0x17ddee72 +73, 0xfd534251 +74, 0x9e500564 +75, 0x9013a036 +76, 0xcf08f118 +77, 0x6b6d5969 +78, 0x3ccf1977 +79, 0x7cc11497 +80, 0x651c6ac9 +81, 0x4d6b104b +82, 0x9a28314e +83, 0x14c237be +84, 0x9cfc8d52 +85, 0x2947fad5 +86, 0xd71eff49 +87, 0x5188730e +88, 0x4b894614 +89, 0xf4fa2a34 +90, 0x42f7cc69 +91, 0x4089c9e8 +92, 0xbf0bbfe4 +93, 0x3cea65c +94, 0xc6221207 +95, 0x1bb71a8f +96, 0x54843fe7 +97, 0xbc59de4c +98, 0x79c6ee64 +99, 0x14e57a26 +100, 0x68d88fe +101, 0x2b86ef64 +102, 0x8ffff3c1 +103, 0x5bdd573f +104, 0x85671813 +105, 0xefe32ca2 +106, 0x105ded1e +107, 0x90ca2769 +108, 0xb33963ac +109, 0x363fbbc3 +110, 0x3b3763ae +111, 0x1d50ab88 +112, 0xc9ec01eb +113, 0xc8bbeada +114, 0x5d704692 +115, 0x5fd9e40 +116, 0xe61c125 +117, 0x2fe05792 +118, 0xda8afb72 +119, 0x4cbaa653 +120, 0xdd2243df +121, 0x896fd3f5 +122, 0x5bc23db +123, 0xa1c4e807 +124, 0x57d1a24d +125, 0x66503ddc +126, 0xcf7c0838 +127, 0x19e034fc +128, 0x66807450 +129, 0xfc219b3b +130, 0xe8a843e7 +131, 0x9ce61f08 +132, 0x92b950d6 +133, 0xce955ec4 +134, 0xda0d1f0d +135, 0x960c6250 +136, 0x39552432 +137, 0xde845e84 +138, 0xff3b4b11 +139, 0x5d918e6f +140, 0xbb930df2 +141, 0x7cfb0993 +142, 0x5400e1e9 +143, 0x3bfa0954 +144, 0x7e2605fb +145, 0x11941591 +146, 0x887e6994 +147, 0xdc8bed45 +148, 0x45b3fb50 +149, 0xfbdf8358 +150, 0x41507468 +151, 0x34c87166 +152, 0x17f64d77 +153, 0x3bbaf4f8 +154, 0x4f26f37e +155, 0x4a56ebf2 +156, 0x81100f1 +157, 0x96d94eae +158, 0xca88fda5 +159, 0x2eef3a60 +160, 0x952afbd3 +161, 0x2bec88c7 +162, 0x52335c4b +163, 0x8296db8e +164, 0x4da7d00a +165, 0xc00ac899 +166, 0xadff8c72 +167, 0xbecf26cf +168, 0x8835c83c +169, 0x1d13c804 +170, 0xaa940ddc +171, 0x68222cfe +172, 0x4569c0e1 +173, 0x29077976 +174, 0x32d4a5af +175, 0xd31fcdef +176, 0xdc60682b +177, 0x7c95c368 +178, 0x75a70213 +179, 0x43021751 +180, 0x5e52e0a6 +181, 0xf7e190b5 +182, 0xee3e4bb +183, 0x2fe3b150 +184, 0xcf419c07 +185, 0x478a4570 +186, 0xe5c3ea50 +187, 0x417f30a8 +188, 0xf0cfdaa0 +189, 0xd1f7f738 +190, 0x2c70fc23 +191, 0x54fc89f9 +192, 0x444dcf01 +193, 0xec2a002d +194, 0xef0c3a88 +195, 0xde21be9 +196, 0x88ab3296 +197, 0x3028897c +198, 0x264b200b +199, 0xd8ae0706 +200, 0x9eef901a +201, 0xbd1b96e0 +202, 0xea71366c +203, 0x1465b694 +204, 0x5a794650 +205, 0x83df52d4 +206, 0x8262413d +207, 0x5bc148c0 +208, 0xe0ecd80c +209, 0x40649571 +210, 0xb4d2ee5f +211, 0xedfd7d09 +212, 0xa082e25f +213, 0xc62992d1 +214, 0xbc7e65ee +215, 0x5499cf8a +216, 0xac28f775 +217, 0x649840fb +218, 0xd4c54805 +219, 0x1d166ba6 +220, 0xbeb1171f +221, 0x45b66703 +222, 0x78c03349 +223, 0x38d2a6ff +224, 0x935cae8b +225, 0x1d07dc3f +226, 0x6c1ed365 +227, 0x579fc585 +228, 0x1320c0ec +229, 0x632757eb +230, 0xd265a397 +231, 0x70e9b6c2 +232, 0xc81e322c +233, 0xa27153cf +234, 0x2118ba19 +235, 0x514ec400 +236, 0x2bd0ecd6 +237, 0xc3e7dae3 +238, 0xfa39355e +239, 0x48f23cc1 +240, 0xbcf75948 +241, 0x53ccc70c +242, 0x75346423 +243, 0x951181e0 +244, 0x348e90df +245, 0x14365d7f +246, 0xfbc95d7a +247, 0xdc98a9e6 +248, 0xed202df7 +249, 0xa59ec913 +250, 0x6b6e9ae2 +251, 0x1697f265 +252, 0x15d322d0 +253, 0xa2e7ee0a +254, 0x88860b7e +255, 0x455d8b9d +256, 0x2f5c59cb +257, 0xac49c9f1 +258, 0xa6a6a039 +259, 0xc057f56b +260, 0xf1ff1208 +261, 0x5eb8dc9d +262, 0xe6702509 +263, 0xe238b0ed +264, 0x5ae32e3d +265, 0xa88ebbdf +266, 0xef885ae7 +267, 0xafa6d49b +268, 0xc94499e0 +269, 0x1a196325 +270, 0x88938da3 +271, 0x14f4345 +272, 0xd8e33637 +273, 0xa3551bd5 +274, 0x73fe35c7 +275, 0x9561e94b +276, 0xd673bf68 +277, 0x16134872 +278, 0x68c42f9f +279, 0xdf7574c8 +280, 0x8809bab9 +281, 0x1432cf69 +282, 0xafb66bf1 +283, 0xc184aa7b +284, 0xedbf2007 +285, 0xbd420ce1 +286, 0x761033a0 +287, 0xff7e351f +288, 0xd6c3780e +289, 0x5844416f +290, 0xc6c0ee1c +291, 0xd2e147db +292, 0x92ac601a +293, 0x393e846b +294, 0x18196cca +295, 0x54a22be +296, 0x32bab1c4 +297, 0x60365183 +298, 0x64fa342 +299, 0xca24a493 +300, 0xd8cc8b83 +301, 0x3faf102b +302, 0x6e09bb58 +303, 0x812f0ea +304, 0x592c95d8 +305, 0xe45ea4c5 +306, 0x23aebf83 +307, 0xbd9691d4 +308, 0xf47b4baa +309, 0x4ac7b487 +310, 0xcce18803 +311, 0x3377556e +312, 0x3ff8e6b6 +313, 0x99d22063 +314, 0x23250bec +315, 0x4e1f9861 +316, 0x8554249b +317, 0x8635c2fc +318, 0xe8426e8a +319, 0x966c29d8 +320, 0x270b6082 +321, 0x3180a8a1 +322, 0xe7e1668b +323, 0x7f868dc +324, 0xcf4c17cf +325, 0xe31de4d1 +326, 0xc8c8aff4 +327, 0xae8db704 +328, 0x3c928cc2 +329, 0xe12cd48 +330, 0xb33ecd04 +331, 0xb93d7cbe +332, 0x49c69d6a +333, 0x7d3bce64 +334, 0x86bc219 +335, 0x8408233b +336, 0x44dc7479 +337, 0xdf80d538 +338, 0xf3db02c3 +339, 0xbbbd31d7 +340, 0x121281f +341, 0x7521e9a3 +342, 0x8859675a +343, 0x75aa6502 +344, 0x430ed15b +345, 0xecf0a28d +346, 0x659774fd +347, 0xd58a2311 +348, 0x512389a9 +349, 0xff65e1ff +350, 0xb6ddf222 +351, 0xe3458895 +352, 0x8b13cd6e +353, 0xd4a22870 +354, 0xe604c50c +355, 0x27f54f26 +356, 0x8f7f422f +357, 0x9735b4cf +358, 0x414072b0 +359, 0x76a1c6d5 +360, 0xa2208c06 +361, 0x83cd0f61 +362, 0x6c4f7ead +363, 0x6553cf76 +364, 0xeffcf44 +365, 0x7f434a3f +366, 0x9dc364bd +367, 0x3cdf52ed +368, 0xad597594 +369, 0x9c3e211b +370, 0x6c04a33f +371, 0x885dafa6 +372, 0xbbdaca71 +373, 0x7ae5dd5c +374, 0x37675644 +375, 0x251853c6 +376, 0x130b086b +377, 0x143fa54b +378, 0x54cdc282 +379, 0x9faff5b3 +380, 0x502a5c8b +381, 0xd9524550 +382, 0xae221aa6 +383, 0x55cf759b +384, 0x24782da4 +385, 0xd715d815 +386, 0x250ea09a +387, 0x4e0744ac +388, 0x11e15814 +389, 0xabe5f9df +390, 0xc8146350 +391, 0xfba67d9b +392, 0x2b82e42f +393, 0xd4ea96fc +394, 0x5ffc179e +395, 0x1598bafe +396, 0x7fb6d662 +397, 0x1a12a0db +398, 0x450cee4a +399, 0x85f8e12 +400, 0xce71b594 +401, 0xd4bb1d19 +402, 0x968f379d +403, 0x54cc1d52 +404, 0x467e6066 +405, 0x7da5f9a9 +406, 0x70977034 +407, 0x49e65c4b +408, 0xd08570d1 +409, 0x7acdf60b +410, 0xdffa038b +411, 0x9ce14e4c +412, 0x107cbbf8 +413, 0xdd746ca0 +414, 0xc6370a46 +415, 0xe7f83312 +416, 0x373fa9ce +417, 0xd822a2c6 +418, 0x1d4efea6 +419, 0xc53dcadb +420, 0x9b4e898f +421, 0x71daa6bf +422, 0x7a0bc78b +423, 0xd7b86f50 +424, 0x1b8b3286 +425, 0xcf9425dd +426, 0xd5263220 +427, 0x4ea0b647 +428, 0xc767fe64 +429, 0xcfc5e67 +430, 0xcc6a2942 +431, 0xa51eff00 +432, 0x76092e1b +433, 0xf606e80f +434, 0x824b5e20 +435, 0xebb55e14 +436, 0x783d96a6 +437, 0x10696512 +438, 0x17ee510a +439, 0x3ab70a1f +440, 0xcce6b210 +441, 0x8f72f0fb +442, 0xf0610b41 +443, 0x83d01fb5 +444, 0x6b3de36 +445, 0xe4c2e84f +446, 0x9c43bb15 +447, 0xddf2905 +448, 0x7dd63556 +449, 0x3662ca09 +450, 0xfb81f35b +451, 0xc2c8a72a +452, 0x8e93c37 +453, 0xa93da2d4 +454, 0xa03af8f1 +455, 0x8d75159a +456, 0x15f010b0 +457, 0xa296ab06 +458, 0xe55962ba +459, 0xeae700a9 +460, 0xe388964a +461, 0x917f2bec +462, 0x1c203fea +463, 0x792a01ba +464, 0xa93a80ac +465, 0x9eb8a197 +466, 0x56c0bc73 +467, 0xb8f05799 +468, 0xf429a8c8 +469, 0xb92cee42 +470, 0xf8864ec +471, 0x62f2518a +472, 0x3a7bfa3e +473, 0x12e56e6d +474, 0xd7a18313 +475, 0x41fa3899 +476, 0xa09c4956 +477, 0xebcfd94a +478, 0xc485f90b +479, 0x4391ce40 +480, 0x742a3333 +481, 0xc932f9e5 +482, 0x75c6c263 +483, 0x80937f0 +484, 0xcf21833c +485, 0x16027520 +486, 0xd42e669f +487, 0xb0f01fb7 +488, 0xb35896f1 +489, 0x763737a9 +490, 0x1bb20209 +491, 0x3551f189 +492, 0x56bc2602 +493, 0xb6eacf4 +494, 0x42ec4d11 +495, 0x245cc68 +496, 0xc27ac43b +497, 0x9d903466 +498, 0xce3f0c05 +499, 0xb708c31c +500, 0xc0fd37eb +501, 0x95938b2c +502, 0xf20175a7 +503, 0x4a86ee9b +504, 0xbe039a58 +505, 0xd41cabe7 +506, 0x83bc99ba +507, 0x761d60e1 +508, 0x7737cc2e +509, 0x2b82fc4b +510, 0x375aa401 +511, 0xfe9597a0 +512, 0x5543806a +513, 0x44f31238 +514, 0x7df31538 +515, 0x74cfa770 +516, 0x8755d881 +517, 0x1fde665a +518, 0xda8bf315 +519, 0x973d8e95 +520, 0x72205228 +521, 0x8fe59717 +522, 0x7bb90b34 +523, 0xef6ed945 +524, 0x16fd4a38 +525, 0x5db44de1 +526, 0xf09f93b3 +527, 0xe84824cc +528, 0x945bb50e +529, 0xd0be4aa5 +530, 0x47c277c2 +531, 0xd3800c28 +532, 0xac1c33ec +533, 0xd3dacce +534, 0x811c8387 +535, 0x6761b36 +536, 0x70d3882f +537, 0xd6e62e3a +538, 0xea25daa2 +539, 0xb07f39d1 +540, 0x391d89d7 +541, 0x84b6fb5e +542, 0x3dda3fca +543, 0x229e80a4 +544, 0x3d94a4b7 +545, 0x5d3d576a +546, 0xad7818a0 +547, 0xce23b03a +548, 0x7aa2079c +549, 0x9a6be555 +550, 0x83f3b34a +551, 0x1848f9d9 +552, 0xd8fefc1c +553, 0x48e6ce48 +554, 0x52e55750 +555, 0xf41a71cf +556, 0xba08e259 +557, 0xfaf06a15 +558, 0xeaaac0fb +559, 0x34f90098 +560, 0xb1dfffbb +561, 0x718daec2 +562, 0xab4dda21 +563, 0xd27cc1ee +564, 0x4aafbc4c +565, 0x356dfb4f +566, 0x83fcdfd6 +567, 0x8f0bcde0 +568, 0x4363f844 +569, 0xadc0f4d5 +570, 0x3bde994e +571, 0x3884d452 +572, 0x21876b4a +573, 0x9c985398 +574, 0xca55a226 +575, 0x3a88c583 +576, 0x916dc33c +577, 0x8f67d1d7 +578, 0x3b26a667 +579, 0xe4ddeb4b +580, 0x1a9d8c33 +581, 0x81c9b74f +582, 0x9ed1e9df +583, 0x6e61aecf +584, 0x95e95a5d +585, 0x68864ff5 +586, 0xb8fa5b9 +587, 0x72b1b3de +588, 0x5e18a86b +589, 0xd7f2337d +590, 0xd70e0925 +591, 0xb573a4c1 +592, 0xc77b3f8a +593, 0x389b20de +594, 0x16cf6afb +595, 0xa39bd275 +596, 0xf491cf01 +597, 0x6f88a802 +598, 0x8510af05 +599, 0xe7cd549a +600, 0x8603179a +601, 0xef43f191 +602, 0xf9b64c60 +603, 0xb00254a7 +604, 0xd7c06a2d +605, 0x17e9380b +606, 0x529e727b +607, 0xaaa8fe0a +608, 0xfb64ff4c +609, 0xcd75af26 +610, 0xfb717c87 +611, 0xa0789899 +612, 0x10391ec9 +613, 0x7e9b40b3 +614, 0x18536554 +615, 0x728c05f7 +616, 0x787dca98 +617, 0xad948d1 +618, 0x44c18def +619, 0x3303f2ec +620, 0xa15acb5 +621, 0xb58d38f4 +622, 0xfe041ef8 +623, 0xd151a956 +624, 0x7b9168e8 +625, 0x5ebeca06 +626, 0x90fe95df +627, 0xf76875aa +628, 0xb2e0d664 +629, 0x2e3253b7 +630, 0x68e34469 +631, 0x1f0c2d89 +632, 0x13a34ac2 +633, 0x5ffeb841 +634, 0xe381e91c +635, 0xb8549a92 +636, 0x3f35cf1 +637, 0xda0f9dcb +638, 0xdd9828a6 +639, 0xe1428f29 +640, 0xf4db80b5 +641, 0xdac30af5 +642, 0x1af1dd17 +643, 0x9a540254 +644, 0xcab68a38 +645, 0x33560361 +646, 0x2fbf3886 +647, 0xbc785923 +648, 0xe081cd10 +649, 0x8e473356 +650, 0xd102c357 +651, 0xeea4fe48 +652, 0x248d3453 +653, 0x1da79ac +654, 0x815a65ff +655, 0x27693e76 +656, 0xb7d5af40 +657, 0x6d245d30 +658, 0x9e06fa8f +659, 0xb0570dcb +660, 0x469f0005 +661, 0x3e0ca132 +662, 0xd89bbf3 +663, 0xd61ccd47 +664, 0x6383878 +665, 0x62b5956 +666, 0x4dc83675 +667, 0x93fd8492 +668, 0x5a0091f5 +669, 0xc9f9bc3 +670, 0xa26e7778 +671, 0xeabf2d01 +672, 0xe612dc06 +673, 0x85d89ff9 +674, 0xd1763179 +675, 0xcb88947b +676, 0x9e8757a5 +677, 0xe100e85c +678, 0x904166eb +679, 0x4996243d +680, 0x4038e1cb +681, 0x2be2c63d +682, 0x77017e81 +683, 0x3b1f556b +684, 0x1c785c77 +685, 0x6869b8bd +686, 0xe1217ed4 +687, 0x4012ab2f +688, 0xc06c0d8e +689, 0x2122eb68 +690, 0xad1783fd +691, 0x5f0c80e3 +692, 0x828f7efa +693, 0x29328399 +694, 0xeadf1087 +695, 0x85dc0037 +696, 0x9691ef26 +697, 0xc0947a53 +698, 0x2a178d2a +699, 0x2a2c7e8f +700, 0x90378380 +701, 0xaad8d326 +702, 0x9cf1c3c8 +703, 0x84eccd44 +704, 0x79e61808 +705, 0x8b3f454e +706, 0x209e6e1 +707, 0x51f88378 +708, 0xc210226f +709, 0xd982adb5 +710, 0x55d44a31 +711, 0x9817d443 +712, 0xa328c626 +713, 0x13455966 +714, 0xb8f681d3 +715, 0x2a3c713b +716, 0xc186959b +717, 0x814a74b0 +718, 0xed7bc90 +719, 0xa88d3d6d +720, 0x88a9f561 +721, 0x73aa1c0a +722, 0xdfeff404 +723, 0xec037e4b +724, 0xa5c209f0 +725, 0xb3a223b4 +726, 0x24ce3709 +727, 0x3184c790 +728, 0xa1398c62 +729, 0x2f92034e +730, 0xbb37a79a +731, 0x605287b4 +732, 0x8faa772c +733, 0x6ce56c1d +734, 0xc035fb4c +735, 0x7cf5b316 +736, 0x6502645 +737, 0xa283d810 +738, 0x778bc2f1 +739, 0xfdf99313 +740, 0x1f513265 +741, 0xbd3837e2 +742, 0x9b84a9a +743, 0x2139ce91 +744, 0x61a8e890 +745, 0xf9ff12db +746, 0xb43d2ea7 +747, 0x88532e61 +748, 0x175a6655 +749, 0x7a6c4f72 +750, 0x6dafc1b7 +751, 0x449b1459 +752, 0x514f654f +753, 0x9a6731e2 +754, 0x8632da43 +755, 0xc81b0422 +756, 0x81fe9005 +757, 0x15b79618 +758, 0xb5fa629f +759, 0x987a474f +760, 0x1c74f54e +761, 0xf9743232 +762, 0xec4b55f +763, 0x87d761e5 +764, 0xd1ad78b7 +765, 0x453d9350 +766, 0xc7a7d85 +767, 0xb2576ff5 +768, 0xcdde49b7 +769, 0x8e1f763e +770, 0x1338583e +771, 0xfd65b9dc +772, 0x4f19c4f4 +773, 0x3a52d73d +774, 0xd3509c4c +775, 0xda24fe31 +776, 0xe2de56ba +777, 0x2db5e540 +778, 0x23172734 +779, 0x4db572f +780, 0xeb941718 +781, 0x84c2649a +782, 0x3b1e5b6a +783, 0x4c9c61b9 +784, 0x3bccd11 +785, 0xb4d7b78e +786, 0x48580ae5 +787, 0xd273ab68 +788, 0x25c11615 +789, 0x470b53f6 +790, 0x329c2068 +791, 0x1693721b +792, 0xf8c9aacf +793, 0x4c3d5693 +794, 0xd778284e +795, 0xae1cb24f +796, 0x3c11d1b3 +797, 0xddd2b0c0 +798, 0x90269fa7 +799, 0x5666e0a2 +800, 0xf9f195a4 +801, 0x61d78eb2 +802, 0xada5a7c0 +803, 0xaa272fbe +804, 0xba3bae2f +805, 0xd0b70fc2 +806, 0x529f32b +807, 0xda7a3e21 +808, 0x9a776a20 +809, 0xb21f9635 +810, 0xb3acc14e +811, 0xac55f56 +812, 0x29dccf41 +813, 0x32dabdb3 +814, 0xaa032f58 +815, 0xfa406af4 +816, 0xce3c415d +817, 0xb44fb4d9 +818, 0x32248d1c +819, 0x680c6440 +820, 0xae2337b +821, 0x294cb597 +822, 0x5bca48fe +823, 0xaef19f40 +824, 0xad60406 +825, 0x4781f090 +826, 0xfd691ffc +827, 0xb6568268 +828, 0xa56c72cb +829, 0xf8a9e0fc +830, 0x9af4fd02 +831, 0x2cd30932 +832, 0x776cefd7 +833, 0xe31f476e +834, 0x6d94a437 +835, 0xb3cab598 +836, 0xf582d13f +837, 0x3bf8759d +838, 0xc3777dc +839, 0x5e425ea8 +840, 0x1c7ff4ed +841, 0x1c2e97d1 +842, 0xc062d2b4 +843, 0x46dc80e0 +844, 0xbcdb47e6 +845, 0x32282fe0 +846, 0xaba89063 +847, 0x5e94e9bb +848, 0x3e667f78 +849, 0xea6eb21a +850, 0xe56e54e8 +851, 0xa0383510 +852, 0x6768fe2b +853, 0xb53ac3e0 +854, 0x779569a0 +855, 0xeca83c6a +856, 0x24db4d2d +857, 0x4585f696 +858, 0xf84748b2 +859, 0xf6a4dd5b +860, 0x31fb524d +861, 0x67ab39fe +862, 0x5882a899 +863, 0x9a05fcf6 +864, 0x712b5674 +865, 0xe8c6958f +866, 0x4b448bb3 +867, 0x530b9abf +868, 0xb491f491 +869, 0x98352c62 +870, 0x2d0a50e3 +871, 0xeb4384da +872, 0x36246f07 +873, 0xcbc5c1a +874, 0xae24031d +875, 0x44d11ed6 +876, 0xf07f1608 +877, 0xf296aadd +878, 0x3bcfe3be +879, 0x8fa1e7df +880, 0xfd317a6e +881, 0xe4975c44 +882, 0x15205892 +883, 0xa762d4df +884, 0xf1167365 +885, 0x6811cc00 +886, 0x8315f23 +887, 0xe045b4b1 +888, 0xa8496414 +889, 0xbed313ae +890, 0xcdae3ddb +891, 0xa9c22c9 +892, 0x275fab1a +893, 0xedd65fa +894, 0x4c188229 +895, 0x63a83e58 +896, 0x18aa9207 +897, 0xa41f2e78 +898, 0xd9f63653 +899, 0xbe2be73b +900, 0xa3364d39 +901, 0x896d5428 +902, 0xc737539e +903, 0x745a78c6 +904, 0xf0b2b042 +905, 0x510773b4 +906, 0x92ad8e37 +907, 0x27f2f8c4 +908, 0x23704cc8 +909, 0x3d95a77f +910, 0xf08587a4 +911, 0xbd696a25 +912, 0x948924f3 +913, 0x8cddb634 +914, 0xcd2a4910 +915, 0x8e0e300e +916, 0x83815a9b +917, 0x67383510 +918, 0x3c18f0d0 +919, 0xc7a7bccc +920, 0x7cc2d3a2 +921, 0x52eb2eeb +922, 0xe4a257e5 +923, 0xec76160e +924, 0x63f9ad68 +925, 0x36d0bbbf +926, 0x957bc4e4 +927, 0xc9ed90ff +928, 0x4cb6059d +929, 0x2f86eca1 +930, 0x3e3665a3 +931, 0x9b7eb6f4 +932, 0x492e7e18 +933, 0xa098aa51 +934, 0x7eb568b2 +935, 0x3fd639ba +936, 0x7bebcf1 +937, 0x99c844ad +938, 0x43cb5ec7 +939, 0x8dfbbef5 +940, 0x5be413ff +941, 0xd93b976d +942, 0xc1c7a86d +943, 0x1f0e93d0 +944, 0x498204a2 +945, 0xe8fe832a +946, 0x2236bd7 +947, 0x89953769 +948, 0x2acc3491 +949, 0x2c4f22c6 +950, 0xd7996277 +951, 0x3bcdc349 +952, 0xfc286630 +953, 0x5f8909fd +954, 0x242677c0 +955, 0x4cb34104 +956, 0xa6ff8100 +957, 0x39ea47ec +958, 0x9bd54140 +959, 0x7502ffe8 +960, 0x7ebef8ae +961, 0x1ed8abe4 +962, 0xfaba8450 +963, 0xc197b65f +964, 0x19431455 +965, 0xe229c176 +966, 0xeb2967da +967, 0xe0c5dc05 +968, 0xa84e3227 +969, 0x10dd9e0f +970, 0xbdb70b02 +971, 0xce24808a +972, 0x423edab8 +973, 0x194caf71 +974, 0x144f150d +975, 0xf811c2d2 +976, 0xc224ee85 +977, 0x2b217a5b +978, 0xf78a5a79 +979, 0x6554a4b1 +980, 0x769582df +981, 0xf4b2cf93 +982, 0x89648483 +983, 0xb3283a3e +984, 0x82b895db +985, 0x79388ef0 +986, 0x54bc42a6 +987, 0xc4dd39d9 +988, 0x45b33b7d +989, 0x8703b2c1 +990, 0x1cc94806 +991, 0xe0f43e49 +992, 0xcaa7b6bc +993, 0x4f88e9af +994, 0x1477cce5 +995, 0x347dd115 +996, 0x36e335fa +997, 0xb93c9a31 +998, 0xaac3a175 +999, 0x68a19647 diff --git a/numpy/random/tests/data/mt19937-testset-2.csv b/numpy/random/tests/data/mt19937-testset-2.csv new file mode 100644 index 000000000000..cdb8e4794ccd --- /dev/null +++ b/numpy/random/tests/data/mt19937-testset-2.csv @@ -0,0 +1,1001 @@ +seed, 0x0 +0, 0x7ab4ea94 +1, 0x9b561119 +2, 0x4957d02e +3, 0x7dd3fdc2 +4, 0x5affe54 +5, 0x5a01741c +6, 0x8b9e8c1f +7, 0xda5bf11a +8, 0x509226 +9, 0x64e2ea17 +10, 0x82c6dab5 +11, 0xe4302515 +12, 0x8198b873 +13, 0xc3ec9a82 +14, 0x829dff28 +15, 0x5278e44f +16, 0x994a7d2c +17, 0xf1c89398 +18, 0xaf2fddec +19, 0x22abc6ee +20, 0x963dbd43 +21, 0xc29edffb +22, 0x41c1ce07 +23, 0x9c90034d +24, 0x1f17a796 +25, 0x3833caa8 +26, 0xb8795528 +27, 0xebc595a2 +28, 0xf8f5b5dd +29, 0xc2881f72 +30, 0x18e5d3f0 +31, 0x9b19ac7a +32, 0xb9992436 +33, 0xc00052b3 +34, 0xb63f4475 +35, 0x962642d9 +36, 0x63506c10 +37, 0x2be6b127 +38, 0x569bdbc6 +39, 0x7f185e01 +40, 0xebb55f53 +41, 0x1c30198c +42, 0x7c8d75c6 +43, 0xd3f2186b +44, 0xaca5b9b1 +45, 0xbc49ff45 +46, 0xc4a802af +47, 0x2cecd86f +48, 0x8e0da529 +49, 0x1f22b00e +50, 0x4559ea80 +51, 0x60f587d8 +52, 0x7c7460e9 +53, 0x67be0a4a +54, 0x987a0183 +55, 0x7bd30f1 +56, 0xab18c4ac +57, 0xffdbfb64 +58, 0x9ea917f9 +59, 0x1239dab7 +60, 0x38efabeb +61, 0x5da91888 +62, 0x8f49ed62 +63, 0x83f60b1e +64, 0x5950a3fc +65, 0xd8911104 +66, 0x19e8859e +67, 0x1a4d89ec +68, 0x968ca180 +69, 0x9e1b6da3 +70, 0x3d99c2c +71, 0x55f76289 +72, 0x8fa28b9e +73, 0x9fe01d33 +74, 0xdade4e38 +75, 0x1ea04290 +76, 0xa7263313 +77, 0xaafc762e +78, 0x460476d6 +79, 0x31226e12 +80, 0x451d3f05 +81, 0xd0d2764b +82, 0xd06e1ab3 +83, 0x1394e3f4 +84, 0x2fc04ea3 +85, 0x5b8401c +86, 0xebd6c929 +87, 0xe881687c +88, 0x94bdd66a +89, 0xabf85983 +90, 0x223ad12d +91, 0x2aaeeaa3 +92, 0x1f704934 +93, 0x2db2efb6 +94, 0xf49b8dfb +95, 0x5bdbbb9d +96, 0xba0cd0db +97, 0x4ec4674e +98, 0xad0129e +99, 0x7a66129b +100, 0x50d12c5e +101, 0x85b1d335 +102, 0x3efda58a +103, 0xecd886fb +104, 0x8ecadd3d +105, 0x60ebac0f +106, 0x5e10fe79 +107, 0xa84f7e5d +108, 0x43931288 +109, 0xfacf448 +110, 0x4ee01997 +111, 0xcdc0a651 +112, 0x33c87037 +113, 0x8b50fc03 +114, 0xf52aad34 +115, 0xda6cd856 +116, 0x7585bea0 +117, 0xe947c762 +118, 0x4ddff5d8 +119, 0xe0e79b3b +120, 0xb804cf09 +121, 0x84765c44 +122, 0x3ff666b4 +123, 0xe31621ad +124, 0x816f2236 +125, 0x228176bc +126, 0xfdc14904 +127, 0x635f5077 +128, 0x6981a817 +129, 0xfd9a0300 +130, 0xd3fa8a24 +131, 0xd67c1a77 +132, 0x903fe97a +133, 0xf7c4a4d5 +134, 0x109f2058 +135, 0x48ab87fe +136, 0xfd6f1928 +137, 0x707e9452 +138, 0xf327db9e +139, 0x7b80d76d +140, 0xfb6ba193 +141, 0x454a1ad0 +142, 0xe20b51e +143, 0xb774d085 +144, 0x6b1ed574 +145, 0xb1e77de4 +146, 0xe2a83b37 +147, 0x33d3176f +148, 0x2f0ca0fc +149, 0x17f51e2 +150, 0x7c1fbf55 +151, 0xf09e9cd0 +152, 0xe3d9bacd +153, 0x4244db0a +154, 0x876c09fc +155, 0x9db4fc2f +156, 0xd3771d60 +157, 0x25fc6a75 +158, 0xb309915c +159, 0xc50ee027 +160, 0xaa5b7b38 +161, 0x4c650ded +162, 0x1acb2879 +163, 0x50db5887 +164, 0x90054847 +165, 0xfef23e5b +166, 0x2dd7b7d5 +167, 0x990b8c2e +168, 0x6001a601 +169, 0xb5d314c4 +170, 0xfbfb7bf9 +171, 0x1aba997d +172, 0x814e7304 +173, 0x989d956a +174, 0x86d5a29c +175, 0x70a9fa08 +176, 0xc4ccba87 +177, 0x7e9cb366 +178, 0xee18eb0a +179, 0x44f5be58 +180, 0x91d4af2d +181, 0x5ab6e593 +182, 0x9fd6bb4d +183, 0x85894ce +184, 0x728a2401 +185, 0xf006f6d4 +186, 0xd782741e +187, 0x842cd5bd +188, 0xfb5883aa +189, 0x7e5a471 +190, 0x83ff6965 +191, 0xc9675c6b +192, 0xb6ced3c7 +193, 0x3de6425b +194, 0x25e14db4 +195, 0x69ca3dec +196, 0x81342d13 +197, 0xd7cd8417 +198, 0x88d15e69 +199, 0xefba17c9 +200, 0x43d595e6 +201, 0x89d4cf25 +202, 0x7cae9b9b +203, 0x2242c621 +204, 0x27fc3598 +205, 0x467b1d84 +206, 0xe84d4622 +207, 0xa26bf980 +208, 0x80411010 +209, 0xe2c2bfea +210, 0xbc6ca25a +211, 0x3ddb592a +212, 0xdd46eb9e +213, 0xdfe8f657 +214, 0x2cedc974 +215, 0xf0dc546b +216, 0xd46be68f +217, 0x26d8a5aa +218, 0x76e96ba3 +219, 0x7d5b5353 +220, 0xf532237c +221, 0x6478b79 +222, 0x9b81a5e5 +223, 0x5fc68e5c +224, 0x68436e70 +225, 0x2a0043f9 +226, 0x108d523c +227, 0x7a4c32a3 +228, 0x9c84c742 +229, 0x6f813dae +230, 0xfcc5bbcc +231, 0x215b6f3a +232, 0x84cb321d +233, 0x7913a248 +234, 0xb1e6b585 +235, 0x49376b31 +236, 0x1dc896b0 +237, 0x347051ad +238, 0x5524c042 +239, 0xda0eef9d +240, 0xf2e73342 +241, 0xbeee2f9d +242, 0x7c702874 +243, 0x9eb3bd34 +244, 0x97b09700 +245, 0xcdbab1d4 +246, 0x4a2f6ed1 +247, 0x2047bda5 +248, 0x3ecc7005 +249, 0x8d0d5e67 +250, 0x40876fb5 +251, 0xb5fd2187 +252, 0xe915d8af +253, 0x9a2351c7 +254, 0xccc658ae +255, 0xebb1eddc +256, 0xc4a83671 +257, 0xffb2548f +258, 0xe4fe387a +259, 0x477aaab4 +260, 0x8475a4e4 +261, 0xf8823e46 +262, 0xe4130f71 +263, 0xbdb54482 +264, 0x98fe0462 +265, 0xf36b27b8 +266, 0xed7733da +267, 0x5f428afc +268, 0x43a3a21a +269, 0xf8370b55 +270, 0xfade1de1 +271, 0xd9a038ea +272, 0x3c69af23 +273, 0x24df7dd0 +274, 0xf66d9353 +275, 0x71d811be +276, 0xcc4d024b +277, 0xb8c30bf0 +278, 0x4198509d +279, 0x8b37ba36 +280, 0xa41ae29a +281, 0x8cf7799e +282, 0x5cd0136a +283, 0xa11324ef +284, 0x2f8b6d4b +285, 0x3657cf17 +286, 0x35b6873f +287, 0xee6e5bd7 +288, 0xbeeaa98 +289, 0x9ad3c581 +290, 0xe2376c3f +291, 0x738027cc +292, 0x536ac839 +293, 0xf066227 +294, 0x6c9cb0f9 +295, 0x84082ae6 +296, 0xab38ae9d +297, 0x493eade9 +298, 0xcb630b3a +299, 0x64d44250 +300, 0xe5efb557 +301, 0xea2424d9 +302, 0x11a690ba +303, 0x30a48ae4 +304, 0x58987e53 +305, 0x94ec6076 +306, 0x5d3308fa +307, 0xf1635ebb +308, 0x56a5ab90 +309, 0x2b2f2ee4 +310, 0x6f9e6483 +311, 0x8b93e327 +312, 0xa7ce140b +313, 0x4c8aa42 +314, 0x7657bb3f +315, 0xf250fd75 +316, 0x1edfcb0f +317, 0xdb42ace3 +318, 0xf8147e16 +319, 0xd1992bd +320, 0x64bb14d1 +321, 0x423e724d +322, 0x7b172f7c +323, 0x17171696 +324, 0x4acaf83b +325, 0x7a83527e +326, 0xfc980c60 +327, 0xc8b56bb +328, 0x2453f77f +329, 0x85ad1bf9 +330, 0x62a85dfe +331, 0x48238c4d +332, 0xbb3ec1eb +333, 0x4c1c039c +334, 0x1f37f571 +335, 0x98aecb63 +336, 0xc3b3ddd6 +337, 0xd22dad4 +338, 0xe49671a3 +339, 0xe3baf945 +340, 0xb9e21680 +341, 0xda562856 +342, 0xe8b88ce4 +343, 0x86f88de2 +344, 0x986faf76 +345, 0x6f0025c3 +346, 0x3fe21234 +347, 0xd8d3f729 +348, 0xc2d11c6f +349, 0xd4f9e8f +350, 0xf61a0aa +351, 0xc48bb313 +352, 0xe944e940 +353, 0xf1801b2e +354, 0x253590be +355, 0x981f069d +356, 0x891454d8 +357, 0xa4f824ad +358, 0x6dd2cc48 +359, 0x3018827e +360, 0x3fb329e6 +361, 0x65276517 +362, 0x8d2c0dd2 +363, 0xc965b48e +364, 0x85d14d90 +365, 0x5a51623c +366, 0xa9573d6a +367, 0x82d00edf +368, 0x5ed7ce07 +369, 0x1d946abc +370, 0x24fa567b +371, 0x83ef5ecc +372, 0x9001724a +373, 0xc4fe48f3 +374, 0x1e07c25c +375, 0xf4d5e65e +376, 0xb734f6e9 +377, 0x327a2df8 +378, 0x766d59b7 +379, 0x625e6b61 +380, 0xe82f32d7 +381, 0x1566c638 +382, 0x2e815871 +383, 0x606514aa +384, 0x36b7386e +385, 0xcaa8ce08 +386, 0xb453fe9c +387, 0x48574e23 +388, 0x71f0da06 +389, 0xa8a79463 +390, 0x6b590210 +391, 0x86e989db +392, 0x42899f4f +393, 0x7a654ef9 +394, 0x4c4fe932 +395, 0x77b2fd10 +396, 0xb6b4565c +397, 0xa2e537a3 +398, 0xef5a3dca +399, 0x41235ea8 +400, 0x95c90541 +401, 0x50ad32c4 +402, 0xc1b8e0a4 +403, 0x498e9aab +404, 0xffc965f1 +405, 0x72633485 +406, 0x3a731aef +407, 0x7cfddd0b +408, 0xb04d4129 +409, 0x184fc28e +410, 0x424369b0 +411, 0xf9ae13a1 +412, 0xaf357c8d +413, 0x7a19228e +414, 0xb46de2a8 +415, 0xeff2ac76 +416, 0xa6c9357b +417, 0x614f19c1 +418, 0x8ee1a53f +419, 0xbe1257b1 +420, 0xf72651fe +421, 0xd347c298 +422, 0x96dd2f23 +423, 0x5bb1d63e +424, 0x32e10887 +425, 0x36a144da +426, 0x9d70e791 +427, 0x5e535a25 +428, 0x214253da +429, 0x2e43dd40 +430, 0xfc0413f4 +431, 0x1f5ea409 +432, 0x1754c126 +433, 0xcdbeebbe +434, 0x1fb44a14 +435, 0xaec7926 +436, 0xb9d9a1e +437, 0x9e4a6577 +438, 0x8b1f04c5 +439, 0x19854e8a +440, 0x531080cd +441, 0xc0cbd73 +442, 0x20399d77 +443, 0x7d8e9ed5 +444, 0x66177598 +445, 0x4d18a5c2 +446, 0xe08ebf58 +447, 0xb1f9c87b +448, 0x66bedb10 +449, 0x26670d21 +450, 0x7a7892da +451, 0x69b69d86 +452, 0xd04f1d1c +453, 0xaf469625 +454, 0x7946b813 +455, 0x1ee596bd +456, 0x7f365d85 +457, 0x795b662b +458, 0x194ad02d +459, 0x5a9649b5 +460, 0x6085e278 +461, 0x2cf54550 +462, 0x9c77ea0b +463, 0x3c6ff8b +464, 0x2141cd34 +465, 0xb90bc671 +466, 0x35037c4b +467, 0xd04c0d76 +468, 0xc75bff8 +469, 0x8f52003b +470, 0xfad3d031 +471, 0x667024bc +472, 0xcb04ea36 +473, 0x3e03d587 +474, 0x2644d3a0 +475, 0xa8fe99ba +476, 0x2b9a55fc +477, 0x45c4d44a +478, 0xd059881 +479, 0xe07fcd20 +480, 0x4e22046c +481, 0x7c2cbf81 +482, 0xbf7f23de +483, 0x69d924c3 +484, 0xe53cd01 +485, 0x3879017c +486, 0xa590e558 +487, 0x263bc076 +488, 0x245465b1 +489, 0x449212c6 +490, 0x249dcb29 +491, 0x703d42d7 +492, 0x140eb9ec +493, 0xc86c5741 +494, 0x7992aa5b +495, 0xb8b76a91 +496, 0x771dac3d +497, 0x4ecd81e3 +498, 0xe5ac30b3 +499, 0xf4d7a5a6 +500, 0xac24b97 +501, 0x63494d78 +502, 0x627ffa89 +503, 0xfa4f330 +504, 0x8098a1aa +505, 0xcc0c61dc +506, 0x34749fa0 +507, 0x7f217822 +508, 0x418d6f15 +509, 0xa4b6e51e +510, 0x1036de68 +511, 0x1436986e +512, 0x44df961d +513, 0x368e4651 +514, 0x6a9e5d8c +515, 0x27d1597e +516, 0xa1926c62 +517, 0x8d1f2b55 +518, 0x5797eb42 +519, 0xa90f9e81 +520, 0x57547b10 +521, 0xdbbcca8e +522, 0x9edd2d86 +523, 0xbb0a7527 +524, 0x7662380c +525, 0xe7c98590 +526, 0x950fbf3f +527, 0xdc2b76b3 +528, 0x8a945102 +529, 0x3f0a1a85 +530, 0xeb215834 +531, 0xc59f2802 +532, 0xe2a4610 +533, 0x8b5a8665 +534, 0x8b2d9933 +535, 0x40a4f0bc +536, 0xaab5bc67 +537, 0x1442a69e +538, 0xdf531193 +539, 0x698d3db4 +540, 0x2d40324e +541, 0x1a25feb2 +542, 0xe8cc898f +543, 0xf12e98f5 +544, 0xc03ad34c +545, 0xf62fceff +546, 0xdd827e1e +547, 0x7d8ccb3b +548, 0xab2d6bc1 +549, 0xc323a124 +550, 0x8184a19a +551, 0xc3c4e934 +552, 0x5487424d +553, 0xd6a81a44 +554, 0x90a8689d +555, 0xe69c4c67 +556, 0xbdae02dd +557, 0x72a18a79 +558, 0x2a88e907 +559, 0x31cf4b5d +560, 0xb157772f +561, 0x206ba601 +562, 0x18529232 +563, 0x7dac90d8 +564, 0x3a5f8a09 +565, 0x9f4b64a3 +566, 0xae373af9 +567, 0x1d79447c +568, 0x2a23684b +569, 0x41fb7ba4 +570, 0x55e4bb9e +571, 0xd7619d3e +572, 0xc04e4dd8 +573, 0x8418d516 +574, 0x2b2ca585 +575, 0xfa8eedf +576, 0x5bafd977 +577, 0x31974fb0 +578, 0x9eb6697b +579, 0xc8be22f5 +580, 0x173b126a +581, 0x8809becf +582, 0x3e41efe1 +583, 0x3d6cbbb8 +584, 0x278c81d8 +585, 0xa6f08434 +586, 0xa0e6601d +587, 0x2fccd88d +588, 0x3cbc8beb +589, 0x5f65d864 +590, 0xa1ff8ddf +591, 0x609dcb7c +592, 0x4a4e1663 +593, 0xeae5531 +594, 0x962a7c85 +595, 0x1e110607 +596, 0x8c5db5d0 +597, 0xc7f2337e +598, 0xc94fcc9c +599, 0xe7f62629 +600, 0x6c9aa9f8 +601, 0x2e27fe0e +602, 0x4d0dae12 +603, 0x9eecf588 +604, 0x977ba3f2 +605, 0xed0a51af +606, 0x3f3ec633 +607, 0xc174b2ec +608, 0x590be8a9 +609, 0x4f630d18 +610, 0xf579e989 +611, 0xe2a55584 +612, 0xee11edcd +613, 0x150a4833 +614, 0xc0a0535c +615, 0xb5e00993 +616, 0xb6435700 +617, 0xa98dbff +618, 0x315716af +619, 0x94395776 +620, 0x6cbd48d9 +621, 0xab17f8fc +622, 0xa794ffb7 +623, 0x6b55e231 +624, 0x89ff5783 +625, 0x431dcb26 +626, 0x270f9bf8 +627, 0x2af1b8d0 +628, 0x881745ed +629, 0x17e1be4e +630, 0x132a0ec4 +631, 0x5712df17 +632, 0x2dfb3334 +633, 0xf5a35519 +634, 0xcafbdac6 +635, 0x73b6189d +636, 0x10107cac +637, 0x18c1045e +638, 0xbc19bbad +639, 0x8b4f05ac +640, 0x5830d038 +641, 0x468cd98a +642, 0x5b83a201 +643, 0xf0ccdd9c +644, 0xcb20c4bd +645, 0x1ff186c9 +646, 0xcdddb47f +647, 0x5c65ce6 +648, 0xb748c580 +649, 0x23b6f262 +650, 0xe2ba8e5c +651, 0x9a164a03 +652, 0x62d3322e +653, 0x918d8b43 +654, 0x45c8b49d +655, 0xce172c6e +656, 0x23febc6 +657, 0x84fdc5b7 +658, 0xe7d1fd82 +659, 0xf0ddf3a6 +660, 0x87050436 +661, 0x13d46375 +662, 0x5b191c78 +663, 0x2cbd99c0 +664, 0x7686c7f +665, 0xcff56c84 +666, 0x7f9b4486 +667, 0xefc997fe +668, 0x984d4588 +669, 0xfa44f36a +670, 0x7a5276c1 +671, 0xcfde6176 +672, 0xcacf7b1d +673, 0xcffae9a7 +674, 0xe98848d5 +675, 0xd4346001 +676, 0xa2196cac +677, 0x217f07dc +678, 0x42d5bef +679, 0x6f2e8838 +680, 0x4677a24 +681, 0x4ad9cd54 +682, 0x43df42af +683, 0x2dde417 +684, 0xaef5acb1 +685, 0xf377f4b3 +686, 0x7d870d40 +687, 0xe53df1c2 +688, 0xaeb5be50 +689, 0x7c92eac0 +690, 0x4f00838c +691, 0x91e05e84 +692, 0x23856c80 +693, 0xc4266fa6 +694, 0x912fddb +695, 0x34d42d22 +696, 0x6c02ffa +697, 0xe47d093 +698, 0x183c55b3 +699, 0xc161d142 +700, 0x3d43ff5f +701, 0xc944a36 +702, 0x27bb9fc6 +703, 0x75c91080 +704, 0x2460d0dc +705, 0xd2174558 +706, 0x68062dbf +707, 0x778e5c6e +708, 0xa4dc9a +709, 0x7a191e69 +710, 0xc084b2ba +711, 0xbb391d2 +712, 0x88849be +713, 0x69c02714 +714, 0x69d4a389 +715, 0x8f51854d +716, 0xaf10bb82 +717, 0x4d5d1c77 +718, 0x53b53109 +719, 0xa0a92aa0 +720, 0x83ecb757 +721, 0x5325752a +722, 0x114e466e +723, 0x4b3f2780 +724, 0xa7a6a39c +725, 0x5e723357 +726, 0xa6b8be9b +727, 0x157c32ff +728, 0x8b898012 +729, 0xd7ff2b1e +730, 0x69cd8444 +731, 0x6ad8030c +732, 0xa08a49ec +733, 0xfbc055d3 +734, 0xedf17e46 +735, 0xc9526200 +736, 0x3849b88a +737, 0x2746860b +738, 0xae13d0c1 +739, 0x4f15154f +740, 0xd65c3975 +741, 0x6a377278 +742, 0x54d501f7 +743, 0x81a054ea +744, 0x143592ba +745, 0x97714ad6 +746, 0x4f9926d9 +747, 0x4f7ac56d +748, 0xe87ca939 +749, 0x58b76f6f +750, 0x60901ad8 +751, 0x3e401bb6 +752, 0xa058468e +753, 0xc0bb14f6 +754, 0x2cb8f02a +755, 0x7c2cf756 +756, 0x34c31de5 +757, 0x9b243e83 +758, 0xa5c85ab4 +759, 0x2741e3b3 +760, 0x1249000e +761, 0x3fc4e72b +762, 0xa3e038a2 +763, 0x952dd92c +764, 0x2b821966 +765, 0xfa81b365 +766, 0x530919b9 +767, 0x4486d66f +768, 0xccf4f3c1 +769, 0xa8bddd1d +770, 0xcc295eb9 +771, 0xfccbe42f +772, 0x38bacd8d +773, 0x2261854f +774, 0x56068c62 +775, 0x9bdaeb8 +776, 0x555fa5b6 +777, 0x20fe615e +778, 0x49fb23d3 +779, 0xd093bad6 +780, 0x54919e86 +781, 0x7373eb24 +782, 0xfbaa7a98 +783, 0x5f62fb39 +784, 0xe03bc9ec +785, 0xa5074d41 +786, 0xa1cefb1 +787, 0x13912d74 +788, 0xf6421b8 +789, 0xfcb48812 +790, 0x8f1db50b +791, 0xc1654b87 +792, 0x948b43c2 +793, 0xf503ef77 +794, 0x117d891d +795, 0x5493ffa +796, 0x171313b1 +797, 0xa4b62e1e +798, 0x77454ea6 +799, 0xbea0aff0 +800, 0x13c36389 +801, 0xe3b60bac +802, 0xa176bed3 +803, 0x2863d428 +804, 0xe2314f46 +805, 0xa85cd3d4 +806, 0x7866e57 +807, 0x8f03f5bc +808, 0x239ae +809, 0x46f279fb +810, 0xcca00559 +811, 0xaa07a104 +812, 0x89123d08 +813, 0x2e6856ba +814, 0x43a9780d +815, 0x676cff25 +816, 0x6744b87d +817, 0xee260d4f +818, 0xb98d8b77 +819, 0x9b0ca455 +820, 0x659f6fe +821, 0x28d20d1c +822, 0x601f2657 +823, 0xdec3073e +824, 0x61263863 +825, 0x1a13435a +826, 0x27497d1e +827, 0x17a8458e +828, 0xdddc407d +829, 0x4bb2e8ac +830, 0x16b2aedb +831, 0x77ccd696 +832, 0x9d108fcd +833, 0x25ad233e +834, 0xaa9bc370 +835, 0xa873ab50 +836, 0xaf19c9d9 +837, 0x696e1e6b +838, 0x1fdc4bf4 +839, 0x4c2ebc81 +840, 0xde4929ed +841, 0xf4d0c10c +842, 0xb6595b76 +843, 0x75cbb1b3 +844, 0xbcb6de49 +845, 0xe23157fd +846, 0x5e596078 +847, 0xa69b0d29 +848, 0x2118a41 +849, 0x7088c16 +850, 0xc75e1e1 +851, 0x6a4af2d6 +852, 0xf19c6521 +853, 0xaff7b3b1 +854, 0x615295c7 +855, 0xbda3a8d7 +856, 0x5b5ca72e +857, 0xdad9d80f +858, 0xfa81c084 +859, 0xf4703fa +860, 0x3ca54540 +861, 0xa8961d51 +862, 0x53d1ecc2 +863, 0x808d83b6 +864, 0x68e8c48e +865, 0x89be2039 +866, 0x9088ea11 +867, 0xb8665d12 +868, 0x91272f9 +869, 0x53dddff2 +870, 0xb7a54ab +871, 0xd2b645ca +872, 0x99fb8590 +873, 0x5315c8e +874, 0x2a913806 +875, 0x7f15eb2b +876, 0xa7f1cc5d +877, 0xbb2ee836 +878, 0xd9fafd60 +879, 0x17448d6f +880, 0x999ec436 +881, 0x482ec606 +882, 0x9b403c0e +883, 0x569eb51b +884, 0xb275d1a6 +885, 0xadd29c31 +886, 0xb7ebdb15 +887, 0xdfef3662 +888, 0x51aba6db +889, 0x6d41946d +890, 0x77bf8896 +891, 0xcafa6fab +892, 0x976ab40f +893, 0x49a6d86b +894, 0x56639e55 +895, 0x9945b996 +896, 0x81459b50 +897, 0xbce97542 +898, 0xe397c9c9 +899, 0x247a5955 +900, 0xb72b1573 +901, 0x86306f86 +902, 0x34f65dc5 +903, 0x909360c0 +904, 0xf3f696ef +905, 0xcb9faae5 +906, 0x93daecd9 +907, 0xde1af7af +908, 0x43a1f2d +909, 0x6d75cde5 +910, 0x9e412b6 +911, 0x5673fed +912, 0x16bb511a +913, 0x35ef4cca +914, 0x4e615aca +915, 0x5cdaf47a +916, 0x26676047 +917, 0x8c199325 +918, 0x2adf0cb9 +919, 0x84f2e6fd +920, 0x5e627f64 +921, 0xb7cee354 +922, 0x542ab4a6 +923, 0xe59cd83b +924, 0x89cc3f10 +925, 0x92b0f5f +926, 0xc1328370 +927, 0x8208d9f7 +928, 0x68eb00cf +929, 0xfadd4ac4 +930, 0x2517784f +931, 0x4042b99 +932, 0x75ce0230 +933, 0x97c5a1b4 +934, 0x1a97f709 +935, 0x4c62781e +936, 0xf530a83 +937, 0x75776413 +938, 0x321c7240 +939, 0x6afe4e36 +940, 0xad00a2b4 +941, 0xbc05477d +942, 0xb0911e80 +943, 0x9935b87d +944, 0xd535eec5 +945, 0x149af45e +946, 0x786934b0 +947, 0xbc13cdac +948, 0x208bfa2e +949, 0xcf4b39cc +950, 0x6ac6c172 +951, 0xbfa9a37 +952, 0x42d28db6 +953, 0x2bf1ea63 +954, 0xbed6e677 +955, 0x50325d27 +956, 0xa79d3b8b +957, 0x52448bb1 +958, 0xefaad1bd +959, 0x833a2e54 +960, 0xd9de549a +961, 0x9f59672f +962, 0x9d5f5f16 +963, 0x1c914489 +964, 0xc08fa058 +965, 0xb188698b +966, 0xdc4672b5 +967, 0x594f720e +968, 0x56ed428f +969, 0x9b0898af +970, 0x8a64d3d5 +971, 0x773308d6 +972, 0x84d62098 +973, 0x46da7cf9 +974, 0x1114eae7 +975, 0xf9f2a092 +976, 0x5363a28 +977, 0xf2db7b3a +978, 0x102c71a9 +979, 0xe8e76aaf +980, 0x77a97b3b +981, 0x77b090d +982, 0x1099620e +983, 0xa6daaae6 +984, 0x86ff4713 +985, 0xc0ef85b8 +986, 0xf621d409 +987, 0xfd1561e2 +988, 0x4bcc687d +989, 0x596f760 +990, 0x7c8819f9 +991, 0x8cb865b8 +992, 0xadea115a +993, 0x56609348 +994, 0xb321ac14 +995, 0x1bac7db2 +996, 0x5fe6ee2 +997, 0xe9bfe072 +998, 0x15549e74 +999, 0xad8c191b diff --git a/numpy/random/tests/data/pcg64-testset-1.csv b/numpy/random/tests/data/pcg64-testset-1.csv new file mode 100644 index 000000000000..0c8271fab6df --- /dev/null +++ b/numpy/random/tests/data/pcg64-testset-1.csv @@ -0,0 +1,1001 @@ +seed, 0xdeadbeaf +0, 0x60d24054e17a0698 +1, 0xd5e79d89856e4f12 +2, 0xd254972fe64bd782 +3, 0xf1e3072a53c72571 +4, 0xd7c1d7393d4115c9 +5, 0x77b75928b763e1e2 +6, 0xee6dee05190f7909 +7, 0x15f7b1c51d7fa319 +8, 0x27e44105f26ac2d7 +9, 0xcc0d88b29e5b415 +10, 0xe07b1a90c685e361 +11, 0xd2e430240de95e38 +12, 0x3260bca9a24ca9da +13, 0x9b3cf2e92385adb7 +14, 0x30b5514548271976 +15, 0xa3a1fa16c124faf9 +16, 0xf53e17e918e45bb6 +17, 0x26f19faaeb833bfc +18, 0x95e1d605730cce1b +19, 0xa7b520c5c093c1aa +20, 0x4b68c010c9b106a3 +21, 0x25e19fe91df703f0 +22, 0x898364bb0bf593cb +23, 0x5bd6ab7dbaa125db +24, 0xd1fe47f25152045c +25, 0x3bb11919addf2409 +26, 0x26a8cb7b3f54af8 +27, 0xe6a27ee11200aa24 +28, 0x7cb585ab01e22000 +29, 0x78e60028676d2ef3 +30, 0x5c32535e5a899528 +31, 0x83e8b6f8c4a46fb3 +32, 0xe56ef7668a161246 +33, 0x36dcbc15aeb73055 +34, 0x5ea247f0bd188acb +35, 0x438b547b84601a80 +36, 0x8acda2a1273e9e3d +37, 0x2b05e30a4b40c24c +38, 0xfd87236bd13af032 +39, 0x471df211d8d985ef +40, 0x18e8a5609a793292 +41, 0x46f0951fab6dc4e3 +42, 0x6c199c4e700f6795 +43, 0xf04aa16bfb7d22cb +44, 0xd763d269fbaffc89 +45, 0x9991930cefbe5c2b +46, 0xb2a11b953f824c96 +47, 0x63fd9f52172c44b0 +48, 0x183bdad907b1d848 +49, 0xe17953cddb931c52 +50, 0x515cf16726ec205a +51, 0x88c327605150711a +52, 0xc7090dd79cbc8dc3 +53, 0xcb487cedeb00a350 +54, 0xc8abf254d87b657 +55, 0xd43cc4cbfb493d1a +56, 0x8705452e5d9ed1e +57, 0xcecd11446769cf43 +58, 0xde72156c8d65bc69 +59, 0x796a8f0f47d52ee8 +60, 0xb4c0da443917d6c3 +61, 0xe07ad7568a8e3dc3 +62, 0xc24a8da39ce6dc21 +63, 0x92b21ea80a8556eb +64, 0x572f21e531edf3af +65, 0x9b917ed56bbed198 +66, 0xe65fd8ddc5ab3d7d +67, 0xf55a80a8ec84fa18 +68, 0x18fc22e1a5227b61 +69, 0x72305dc7eeaa79d3 +70, 0x47ce58a36e7592cf +71, 0x14c6374340c0f7cc +72, 0x6f98273d4eb5a2c +73, 0x59a8702c46fe8f8a +74, 0xb67cbd8113cfe57f +75, 0xaa03c5db5f5b7690 +76, 0x3fb0f77ea4568013 +77, 0x756530990398b26e +78, 0x4c1952b2a3a6a343 +79, 0x1da15c5383074582 +80, 0xb405b21c81c274f7 +81, 0xbe664677a16788b +82, 0x9d2e37550bcee656 +83, 0x8b4589f0d9defe02 +84, 0x2935f018ee06a59 +85, 0x3834bf88be97ed11 +86, 0xa610d049cea79b6d +87, 0xd49ffc0d09a59ea9 +88, 0x4073365b76567adf +89, 0x499eefb9bb7513e2 +90, 0x74a743ee6b0138a9 +91, 0x3bf0880f2d947594 +92, 0x555d1c0498600a99 +93, 0x923b32a88ef2ffa4 +94, 0x7325411065fbedea +95, 0x9f4129ff8b79d300 +96, 0xab2b0a9b8a3785dc +97, 0x11734bdfba3a1713 +98, 0xc8333398841ba585 +99, 0xee2409cc234e6742 +100, 0xf6638e700872ecd2 +101, 0x10875300c13cd284 +102, 0x27a9bbed7c15b2d3 +103, 0x3c87f8fef31ce9bd +104, 0x92be263cd0914a95 +105, 0xa7b0f11bc742307e +106, 0x4a56f788cc1c1a3c +107, 0x4a130fa32257a48b +108, 0x5d4d9eda16e90286 +109, 0x7cc2af564844bedc +110, 0x2532867bfe7cda1a +111, 0xb1c504676611fd17 +112, 0xce8e86cfb4189aee +113, 0x99685898980d1970 +114, 0x8c3b67db23bcf1e +115, 0x73e14c93905b135f +116, 0xf0271b64ac2bd4d3 +117, 0xf4beba82f3ec1b2d +118, 0x1cdbf3ee9f210af +119, 0x2e938557c09c3ea6 +120, 0x2d314ccfa6ffd81d +121, 0x31ad47079950ade4 +122, 0x342b27547b900872 +123, 0x171b0e20b9ef1a76 +124, 0xdf10ce6318b03654 +125, 0x1d625df4aa718897 +126, 0x8712715a9f6e02ec +127, 0xb4a072da725bca3b +128, 0x19d346cb7734bd42 +129, 0xfd4281d311cb2958 +130, 0x58274c9519fc8789 +131, 0x4cacf29d885fd544 +132, 0x784b14d1c2523b80 +133, 0x2d25242131bb2373 +134, 0xcd2a5e43a7d9abf9 +135, 0x15eda3806e650ecb +136, 0xdaac5e277d764d96 +137, 0xdc5a5dd59aaa94e0 +138, 0x40d00237a46d5999 +139, 0x6205dd35a692743f +140, 0xbbd8236740361f09 +141, 0x1625c9f4e7288bf9 +142, 0xb74f12df1479e3ce +143, 0xb2d72a51b43d7131 +144, 0xf006a324b3707c83 +145, 0x28e8ab4abe7655b8 +146, 0xfb480093ad7ab55 +147, 0x3f8abd0d6ff8d272 +148, 0xc81a94177ac26bb7 +149, 0x3cdc178307751b14 +150, 0x9de84cc2b10ba025 +151, 0x3f8ab5aefcd046e2 +152, 0x43bdb894e1ee83b2 +153, 0xe288a40f3f06ac9d +154, 0xdab62a7d04b4f30f +155, 0x49f4e20295e1a805 +156, 0x3643764805e0edef +157, 0x9449954618b6b +158, 0x6c87e0d4508e0ce0 +159, 0x3a334be688a9dd7b +160, 0xb35c39228776e499 +161, 0xc4118bfff938490e +162, 0x88cbde3dcbb034b2 +163, 0xf91b287793c417c3 +164, 0x42b15f731a59f5b3 +165, 0xffa27104bbe4814d +166, 0x1b6789d138beccde +167, 0x542c2c1440d0ceb9 +168, 0x367294504d18fa0d +169, 0xf918b60e804a1b58 +170, 0xd390964e33a9d0e3 +171, 0x23bb1be7c4030fe8 +172, 0x9731054d039a8afb +173, 0x1a6205026b9d139b +174, 0x2fa13b318254a07e +175, 0x69571de7d8520626 +176, 0x641a13d7c03332b7 +177, 0x76a6237818f7a441 +178, 0x4e77860d0c660d81 +179, 0x4441448a1c1cbdb2 +180, 0xccd7783a042046e5 +181, 0xf620d8e0805e3200 +182, 0x7de02971367fdd0c +183, 0x539c263c5914cab1 +184, 0x9c3b9ba1a87bbf08 +185, 0x6d95baa34cda215f +186, 0x2db3f83ace0bac5f +187, 0x7f5af1da2dc670a4 +188, 0xfcc098d16c891bfb +189, 0x81a33df1d7a5ab12 +190, 0x767b0f863c8e9882 +191, 0x7a92983830de483d +192, 0xfa7598c37a79ac25 +193, 0xb89b3ca42ce03053 +194, 0x457a542b8efed4f7 +195, 0x571b7737fd0eeda7 +196, 0xa0f59e524485c0a +197, 0x82dca766b7901efd +198, 0xa68243caf6a3bd5d +199, 0x1bac981c6c740e5e +200, 0xbcd51bedf9103e44 +201, 0x4e197efd3ae5a7bf +202, 0x523568efd782268b +203, 0x5ec4ef1191fef09 +204, 0xed751ed5e31c9ab +205, 0x44eac24de03e1b29 +206, 0x9237d57c011d3fb3 +207, 0xa8c6da0f7692f235 +208, 0x9f9eb6bc15d6cac7 +209, 0x34bb8e0c93427aad +210, 0x115febd738eaac4a +211, 0xa439991ed139d27a +212, 0x45c7c2633d8710a2 +213, 0x48b7475f3405a3ce +214, 0x80158497c77bd00b +215, 0x935c316a5b1657cb +216, 0x59c5d54440e9695e +217, 0x337c78c5b3d0ede2 +218, 0x8c46bb956b93790d +219, 0xbf1dd03e471d71c5 +220, 0x2d375e90a4bef583 +221, 0xd0365428331b3790 +222, 0xfcd3969ac827ecd4 +223, 0x392fb6c580498410 +224, 0x6d6db4ceab5ea6c0 +225, 0x9bf84f1972e24786 +226, 0x798dfd820959dcc5 +227, 0x2e425095e65e8bfb +228, 0x8c1aa11536b1c9c3 +229, 0xd28e2ef9b12f6f74 +230, 0x86583bc98c8f78d2 +231, 0x489877530e3f93e7 +232, 0xb1d9430631104a15 +233, 0x1814f6098e6263bd +234, 0x8e2658a4e0d4cd53 +235, 0x5afe20e2531cdb2a +236, 0x30d02f7c4755c9bf +237, 0xe1e217cda16ed2d2 +238, 0xccb4913a42e3b791 +239, 0xfff21363ac183226 +240, 0xe788690bbda147a7 +241, 0x76905cf5917bfc6a +242, 0x2a8fa58f7916f52c +243, 0xf903c0cc0357815a +244, 0x15d20f243a4998d2 +245, 0x5b7decee5a86ea44 +246, 0x114f7fc421211185 +247, 0x328eb21715764c50 +248, 0xaffaa3f45c0678fd +249, 0x2579e6ef50378393 +250, 0x7610ab7743c19795 +251, 0xf9923d2bd101b197 +252, 0x57e42e7a62ba7e53 +253, 0x9f1dc217b4f02901 +254, 0x88a9ebd86509b234 +255, 0x867fc926aecc8591 +256, 0xaf22c1bfef04c718 +257, 0x39f701f0313f4288 +258, 0x6171ad397e6faab2 +259, 0x239bb5b9abdec4fc +260, 0xd9a591e25dd01c6e +261, 0x826dc4a75b628e49 +262, 0xf112b152c408f47 +263, 0x6843a06110f86c0 +264, 0x965e56a7185c1332 +265, 0x8d84492edbc71710 +266, 0xeee8ec111cfd1319 +267, 0xf2858e94ad98e458 +268, 0xbc9589fdf5f3a97e +269, 0xaf0ceef3bc375130 +270, 0x48f4aaf13fa75c1e +271, 0x111e9db47bee758f +272, 0xea3171df130164ba +273, 0x2a7bbe30bf827ab6 +274, 0xc516c3fdbf758c35 +275, 0xec55097754b04be5 +276, 0x374a997d52b6d3e6 +277, 0x487df5456085ffbc +278, 0x528883b84df8eafe +279, 0x805f77ab5ba26f86 +280, 0x8eb81477dc04f213 +281, 0x471ea08ec6794d72 +282, 0x69d3667ecc4d2176 +283, 0x98b7b6e295548a66 +284, 0x3877713c173f8f2 +285, 0xa00542570d0e8de3 +286, 0xf534b1bfa4033e50 +287, 0x7e1fedeac8bf6b26 +288, 0x8043f37c89628af4 +289, 0x1dd7039ec295e86d +290, 0xce9c05b763a40cc4 +291, 0x246926481e61028f +292, 0xb7cb0f1babf5893b +293, 0xefe6b777f37fc63e +294, 0xebbcabb4cb35cdcb +295, 0x39fa63cd711eeea9 +296, 0xad5d3ba7aaf30c8d +297, 0x8e9e78fe46021990 +298, 0xc7eaef6e7d5a3c62 +299, 0xefccdd5495d3f386 +300, 0x2179557ee8cfc76a +301, 0x88a77f621f0885ce +302, 0xafda62674543d90c +303, 0xb8e6fbe2e13e56c0 +304, 0x8bfbbe26a14f9b1a +305, 0x1404f59f5851f8c3 +306, 0x1140c53a0489566d +307, 0x3edf2d138b5c3f1d +308, 0x75d6bb275d817dc +309, 0x8e660ae27107664e +310, 0x7a8021038ee303e1 +311, 0x2042ef5eefa9079f +312, 0xe3e7b90bbf6d457a +313, 0xf3f819d2bb9405b +314, 0x522e42155cae0c10 +315, 0xf5bfbb975b40e233 +316, 0x2cf82b614dd95cfa +317, 0x183ef4a96bc40e55 +318, 0x9f6e351c5ba4e752 +319, 0x37c1110683c90846 +320, 0x1d89b7a996d8a977 +321, 0x18a444f77c7cb4d9 +322, 0xd0a8a971b78dc893 +323, 0x860232fb9e6543f1 +324, 0x60b6097f51002555 +325, 0xca1e5214123e3894 +326, 0xe03fe695c95f99bb +327, 0x2c7c6779d5f03622 +328, 0xafeeee42f63055d1 +329, 0x670dde905515936a +330, 0x9a922f42b59fb094 +331, 0xddb5ff49af5a651a +332, 0xe61b04c9e58ebbf8 +333, 0x4e459dcf272e7fc4 +334, 0xd549e92c16adceeb +335, 0x7a17dba1299d4a9c +336, 0x825d756109f2b585 +337, 0xba142e61a9cb203e +338, 0xc2a19f00e9c04a30 +339, 0x2d0f8140d23d0652 +340, 0x8b866d4d4d6caaf4 +341, 0x4f11d90dd91f8217 +342, 0xf6efc37373b9e0d +343, 0x248493d6cd6a4736 +344, 0xd12b6ae74a951a3e +345, 0x56e34722070b70a7 +346, 0x22d3f201cc9fa0eb +347, 0xbfdcc320008291b7 +348, 0x1a7a6922e9204fbd +349, 0x831421e0c4945ae4 +350, 0x66316feddddf0e11 +351, 0xa8c86a1517456554 +352, 0x14a9049ad989e335 +353, 0x837022259f141ecd +354, 0xcb71793a06c261f7 +355, 0x4aeefc07ebe09a79 +356, 0x8982f15aa3b6594b +357, 0x67bccfa7ed9b0d5b +358, 0xb377463b523e9dec +359, 0x53d3d594870fecb7 +360, 0xa5274b1caec5a60a +361, 0xd6316d0cb643db39 +362, 0xabc1a9b536de88ce +363, 0xed2fdb1383d2a077 +364, 0x12319c6feb97221b +365, 0x7e0f6cd40ef47403 +366, 0x86135c84fe26dbf8 +367, 0xc96622d3fbbee19b +368, 0xe3989d8d8511573f +369, 0x42cc365554d1fdc7 +370, 0x4c1a1eb8bbce8b4f +371, 0xfc4e30e7ef2034c1 +372, 0xc490444317a91e76 +373, 0x7ccdf469ff5dc81c +374, 0xf5a0da4110cc09d7 +375, 0x505227baf34c0fb5 +376, 0xbe58737e8a35cc88 +377, 0xd449bee91b3e8c41 +378, 0x3e590e23299d0e6 +379, 0x291a7d9e0a64caf7 +380, 0xdc6fafbdfebd2293 +381, 0x8223f1e259fe8a65 +382, 0x6186fbc9efd9e3df +383, 0xfda39b07e4007ffb +384, 0xfc19aea98574dc02 +385, 0xd0e10d354fcacd8c +386, 0xc9619916544a55a5 +387, 0xd454d50a8c8558cd +388, 0xcd94a246712d91e +389, 0x76a771f5d1231cce +390, 0xdd20cb2b7b370ee5 +391, 0xa6f4f50feca57c49 +392, 0x78c8fb431f17ab9c +393, 0x1b692b79a59b43cc +394, 0x4c45045d287da7e6 +395, 0x522132e18bf43928 +396, 0x25c458983138b41c +397, 0x2a1fb426ef229796 +398, 0x74dc324c74e5dd3d +399, 0x6df75e3eb6eb5374 +400, 0xb63f2f4f9ca25b61 +401, 0xac72286112ee54d6 +402, 0x5a966f3d0a6863c4 +403, 0x8d7046bc64a46fc2 +404, 0xa7b740fd6e3087eb +405, 0xcdbcbe0340cfcdf5 +406, 0xcb632613bf312b65 +407, 0xa91b3f2c2aac238b +408, 0xa06deb3f5ae555a3 +409, 0x29d72e1f8db69 +410, 0x2d004bae09728ea6 +411, 0xc6eee5dce0736cc1 +412, 0xa7493145500ff60f +413, 0xc4d68c4aa18ab93c +414, 0x8210c29e79d48d7f +415, 0xd0999d7889ecbef6 +416, 0x6e3bd61e66e93566 +417, 0xe6cc13d47d7d7b1f +418, 0x3d6f181f42e03979 +419, 0xbed4e14fd867604a +420, 0xbe511c84067bd86d +421, 0x49a876d89e697d38 +422, 0xc04c3dde8f889c98 +423, 0xaf293eeab0f53e3f +424, 0x9f6291dd65732cd6 +425, 0xd7811ac01de78c01 +426, 0xe385cf0261d50ec2 +427, 0x5a64134b3542bbf +428, 0xf9d1302bc6f13a68 +429, 0x5d2aabbea37d8c31 +430, 0xd9842e99a5192970 +431, 0x713eadc4cd30e837 +432, 0xb7b002fc72abb413 +433, 0x276cfeea526af1cf +434, 0x8519fe79b633a0ce +435, 0x2f0e87363705a3e2 +436, 0x9adbac0be3c371e7 +437, 0xf3f44ba899a6173c +438, 0x782d6c29618fde2b +439, 0x7f61062acec408f +440, 0x6e79cd836359258f +441, 0x5c8e9b138df5785a +442, 0xa54359c9f39a9a84 +443, 0xeec3f033135084b0 +444, 0x883ee717787a535c +445, 0x9a2422b513a73b00 +446, 0x2dd4beddcdd64a58 +447, 0x90c8a13202239c7b +448, 0x85b352ab759646d9 +449, 0x139f5cb2e46c53aa +450, 0xe1d3ba6c721c66d1 +451, 0xaa66e0edc4b60a98 +452, 0x3521275c75be29b6 +453, 0x490a5190b3edfa5d +454, 0xd2abcdd2ccb2f14e +455, 0x9d9be8bef4a5857d +456, 0xde19676f13ef7755 +457, 0xdac2fee2e42615f3 +458, 0xf4239801cb02f2ab +459, 0xaa8bf923ed91875c +460, 0x61d18a1940e4c7c0 +461, 0x1eb6aa3d5f077a6d +462, 0xee7374c063bf29d8 +463, 0x2f0a59e34d76268d +464, 0xc92e80e17d1eb3e9 +465, 0xafd05b3ec3d2ca72 +466, 0x28a61ad8d6c497b8 +467, 0xa7094d6834ad7d47 +468, 0x57d80ea9eccbb4f +469, 0xb047e0fee6cdaf16 +470, 0x44f41b5eb48c00bb +471, 0xd6dc8e1eb9c8c9ba +472, 0x47adfd2c638c7849 +473, 0x365d63db7d526c68 +474, 0xc21cda439016135d +475, 0x14d10c3f0f98863c +476, 0xa93e56f74e037602 +477, 0x3b4e9c8915bdc9 +478, 0xb46f5ae155e54aa2 +479, 0x8e470d21ce1943e1 +480, 0x60b96301b5ba2e8d +481, 0x1b473a41d381f9ff +482, 0xabcf5a8e3269e73f +483, 0xd410f6e94fb21fa1 +484, 0x65d1a47eebf87e5e +485, 0x48eaa201c61cb843 +486, 0x212c1abc2499bfc5 +487, 0x4255ad8377d2d8d +488, 0x44caeef472010612 +489, 0xffae764524f572f2 +490, 0x78d374d20c9ee550 +491, 0x6e003206c0511cee +492, 0x7998a159145bfb82 +493, 0x921239650bda1d4d +494, 0xae05025509bcfdc5 +495, 0xc6430c980be407b4 +496, 0x78524f1744b153f1 +497, 0x84089e6f468181fe +498, 0x8d0d21d7dfb6c254 +499, 0x90bad90502a33603 +500, 0x3072a403cbd16315 +501, 0xdfadddf3f1c040c2 +502, 0x22f0b0639d9ff975 +503, 0xb49e48a4cad0765b +504, 0x95a0a04f8239709d +505, 0x56e147a24a4c481f +506, 0xacf16ef61dea4c7e +507, 0x424040afd2700de6 +508, 0xc67e8096a3c717a9 +509, 0x39f164181dd0a399 +510, 0x2449cedc1d62198c +511, 0x7a53df11a1f1a61c +512, 0x5596f1d4a3badae3 +513, 0x38ed4c822072b3d0 +514, 0xf07ef346b3fd730a +515, 0xfd349c35c3ed51fd +516, 0x2f15c9c7890f8f32 +517, 0x3b470df52b173c29 +518, 0xd31bfc8981281af7 +519, 0xbbcc9bdf561215bb +520, 0x5782fffea326574f +521, 0xb0ebdcfcc5e03290 +522, 0x7fd89d93d2b3fbef +523, 0x280ea1865d9ba2 +524, 0xe726959845b2c100 +525, 0xd0361f032cd7dbb1 +526, 0x3c65ec2028b81a22 +527, 0x5221e9b2188920bf +528, 0xeb5ab27c4125ec20 +529, 0x80a32dd48b54f0a4 +530, 0x369b5ced1012bebb +531, 0x582d35d76530bc6f +532, 0x7b50dc9b48e1e37d +533, 0x37fdfe8bbacf8dad +534, 0x7a0cb7e6e93840ea +535, 0xa1132c870be0b2ce +536, 0x9d8ac2c68267cd1a +537, 0x470969b647fa7df4 +538, 0xabcb7d8adf7e2d24 +539, 0xacdebec9bdf9eb1c +540, 0xe30f4cbf7eb6a59 +541, 0x746673836c4df41d +542, 0x75120a6b647bb326 +543, 0x2f4eab556c3f6878 +544, 0xd84651ab05405b7a +545, 0x9e695808b9622284 +546, 0xc93b71e56aa6e1a5 +547, 0x2be7f3be4a7b7050 +548, 0x6497e910b6733241 +549, 0xcf7050dfd08076fc +550, 0x4e3cc156eca183f7 +551, 0xf801a33d9326c265 +552, 0x6aa293c8a47d40e6 +553, 0x28c429755faa6230 +554, 0x82b818651f54e7bb +555, 0xa84d726d7acdbead +556, 0x5cfa535d5774965d +557, 0x4a34b7b1cb48d53 +558, 0x86a7b5bce426de84 +559, 0xfcd2307cecdb7318 +560, 0x16dbaaa71181a038 +561, 0x88e7e8cd261c2547 +562, 0x3c09ba6d1d5ea913 +563, 0x5dd3d643734ee5b6 +564, 0x326d725fe8cbb33 +565, 0x7bcca9ca2da8e784 +566, 0x482dcf6b11d7f9a4 +567, 0x1291b605b4cd3e04 +568, 0x6988181b50e2f4a8 +569, 0x649e3c37131fc292 +570, 0x4eeb67b9e21eba54 +571, 0xc051d39073dec45f +572, 0xc99c52e110270d67 +573, 0xcb813d5d77868add +574, 0x423a5f13573e7ac0 +575, 0x231ac4cc4fe73616 +576, 0x4c22b888a6e600ea +577, 0x8059a6dc7c9e25c6 +578, 0x49f498a5b8ad22de +579, 0xf1e812cc6d1826c8 +580, 0xbbaf60abe8b11e00 +581, 0x1d31d7f4d8be9a6a +582, 0xfeadce70a9a10c14 +583, 0xb47c635bc136996a +584, 0xd88e694c8da030cb +585, 0xc41bbe132aff1364 +586, 0x34249ab18a4b0800 +587, 0xf14b5c825aa736cc +588, 0x2710be6b08df78e +589, 0x2ab56bcc9bf9e740 +590, 0x9b7f6e591b5f648 +591, 0xfb665c3772f34135 +592, 0x628a0a5d2db5d8d5 +593, 0xb3e3f251e61b5259 +594, 0x82310ae33faf1b23 +595, 0x24af8723a65cbd0b +596, 0x671c93282fc4ad97 +597, 0x6cabeaac77270cad +598, 0xef4643fe38b02b7f +599, 0x7b011549d1ac6653 +600, 0xe2af87b9fccfe89 +601, 0x36b71ad67197ac8a +602, 0xdbba55d06f2fd93b +603, 0xf571dbd764b7f7e5 +604, 0x38ea402501cdbd45 +605, 0xb8ab5b5b1bab2913 +606, 0xfab973c4d45f32bd +607, 0x9364f1717c2636b9 +608, 0xfad00f4d983e00fe +609, 0xc90c532a11aef75a +610, 0x64a6eda96e44783c +611, 0x35891f2eb84520be +612, 0x28d216080caed43 +613, 0x129629cc5bd206f6 +614, 0x22c3d39822cbb4b3 +615, 0xf1efbf4cce1eaa2b +616, 0x7070cba12524ed08 +617, 0xa7ed0be9deabf20d +618, 0x8ddb4cd6b454f76b +619, 0xb82814b1db37b63 +620, 0x418e83b36de01876 +621, 0x9a538c7f39c6413 +622, 0xee0cd7abf8a2ecb9 +623, 0xa9222b07e95590f3 +624, 0x6296a415d68341e6 +625, 0x981e0a5a8f811929 +626, 0x4bb372d3b0de283d +627, 0xa9805b5971866e16 +628, 0xaf3b5f5183497657 +629, 0x2152b0fd23c3d9f +630, 0xb730c325b7173180 +631, 0x1e3439d231608c19 +632, 0x1c5ba6031379823c +633, 0x87f5d12d6d365cbc +634, 0xd3bc7f29614bc594 +635, 0x63102214bb391268 +636, 0x482bbd5bba648a44 +637, 0x6a23604690759dc4 +638, 0x4091d41408d3a39e +639, 0x7cd017f922101b15 +640, 0x7ce9004ac5f9231 +641, 0x978bc3d8ec7f7fdf +642, 0x5bd0c4d780580c11 +643, 0x4313c068bb040153 +644, 0x3ab7dab7bc38bf80 +645, 0x3aaf9c187728deea +646, 0x6633a4ce8efb88d9 +647, 0x7263b089878f00fc +648, 0xd0d767e96fe00eb8 +649, 0x184a7c0c01908028 +650, 0x1ebdf41e6f76e186 +651, 0xeb740ee1d0402083 +652, 0xfccf4974edb1c339 +653, 0x16e2707aa28306d +654, 0x1684f0bdb018c3a5 +655, 0x887b6b67b88aa862 +656, 0x923d7810a2bea33a +657, 0x56b3560babef5d6b +658, 0xb39a14614c54b8c6 +659, 0x33e4dc545a509fc8 +660, 0x26e21f84142da9b +661, 0xdd07598125756855 +662, 0x572d49a071d7ae0a +663, 0xba3c7e3baea28760 +664, 0x7ecdb2d714db4b61 +665, 0x1c62b4920e1b2fe2 +666, 0x71bfafb70092834a +667, 0xd710a4228f60d56a +668, 0xeb16277d4ce4e95b +669, 0x968168c90b16d3a1 +670, 0xac3439dfe8ad0062 +671, 0x5a8226f9dd5876ad +672, 0xb843affe917291b0 +673, 0xd76d1e67051f8259 +674, 0xb73a6638cce8ccde +675, 0xa0e6afd3c7295f9 +676, 0xff8857b4bbb5f4c6 +677, 0x99becf78938f0426 +678, 0xfcd17edc1e70f004 +679, 0x6223b8b23f2f50 +680, 0xca875f3e84587b4c +681, 0x7d1e81e589f87fb9 +682, 0x9eb621586aa826fc +683, 0xf46fb9ef5b9c2086 +684, 0x2882c9b7092725f3 +685, 0x5493f099bbedcd02 +686, 0x90c1ec979ffa811d +687, 0x963f765025bcc53 +688, 0x56194e3ec3d9d4e9 +689, 0x7ec4720954cac1f0 +690, 0xfab3145171af7f90 +691, 0x52a0b4e41a13b593 +692, 0x740e2d4d5909d126 +693, 0x98f5339c09c94a28 +694, 0x1700e462fe8dec76 +695, 0x3dbffc2aa4695ac3 +696, 0x5763edacabdfe2a1 +697, 0x7b5b623ce49ef21d +698, 0x30addc66f49860df +699, 0xcc7511a6c31bceda +700, 0x1b25b61ca75db43b +701, 0x416bc4c298e59046 +702, 0x4cd11fe2d74e4649 +703, 0xb54458a9229fc978 +704, 0x8c21a27882b6ca35 +705, 0x57887c8b5e01639b +706, 0xf4e893da996680bb +707, 0x8d601297702c9c0d +708, 0x2a27904a30aa53af +709, 0x497800f6917ea8d0 +710, 0xe96db3340ada9c00 +711, 0xcc23166f14c010ee +712, 0x782690d78fa65ec9 +713, 0xf3e00d74a0878eda +714, 0xa7cbb683decca0a3 +715, 0xdd2e038e683a94aa +716, 0xe2096ff8da896ca5 +717, 0xf7c83400afdabe11 +718, 0x395b8c6f6a4086a4 +719, 0x4a164ec05bee71d4 +720, 0xe87aa5d1ca0462fe +721, 0x8dbc5aed6dff9ceb +722, 0x12120d1e9552707b +723, 0x877dca6889b3e6cd +724, 0xbd65605c01e900fb +725, 0xbd6b82c4157c3115 +726, 0x8b60282732caf78a +727, 0x279fcf5e5de9e57f +728, 0x34b34ebfb6a37eae +729, 0xd258cc1a14e03b7b +730, 0x9a528ba3db4a13fb +731, 0xffa0aea59d057746 +732, 0x27fa7f456cd37c4e +733, 0xe1117a57a6fdce63 +734, 0xdc8fc903970a1551 +735, 0x492dd104f30faf29 +736, 0x110def0959e5652b +737, 0x7f8d1997636fdd15 +738, 0xfb77b05e538a9b59 +739, 0x2e41fa35b4b01fc6 +740, 0xbc35ae69a3374085 +741, 0x192c2a681c2d9b4b +742, 0x12566b8866c189d6 +743, 0x9d88ea785c5185c8 +744, 0x30a621ad5f983c4 +745, 0x8b875efe1206f587 +746, 0x224d25c3af6e3423 +747, 0x7503e976a1ac7bcc +748, 0x3c98aa869e823859 +749, 0x3d8835304b646892 +750, 0xf6353330ff970bc2 +751, 0x8a673f5e2edb8acb +752, 0xf2fdcc53493838b9 +753, 0x85ddcd526236af16 +754, 0x60afb99814c676c5 +755, 0x32a1c2749e281ca8 +756, 0x2367a92ae3bee9ca +757, 0x219fe082703743cc +758, 0x34d8b74dc85182a9 +759, 0xdd04164c72db23f +760, 0xe293ac28fe2671a9 +761, 0x9ca7d169cbda6f45 +762, 0x705c47972b4240ed +763, 0xc10eda9eeb536209 +764, 0xc36ddacd0c94e85d +765, 0x8eb592c27e8cd0d2 +766, 0x3e815991c76e7cc4 +767, 0xac9cfce31acf7580 +768, 0xbf7a4cb31c7aee94 +769, 0x663077444aceecf6 +770, 0xe7f614ff386eb568 +771, 0x79d7a229c66912c0 +772, 0x161ed4311f63e1f3 +773, 0x308a5faeb9982ede +774, 0x7b38ddb9b7efd10 +775, 0x1e103a2589b27ecf +776, 0x67b02baf4259f27e +777, 0x868921c115ea2eee +778, 0x959791912200f71e +779, 0x4dd55f36dec10557 +780, 0xe3464d90080cb99d +781, 0xfb2d4f6accce652f +782, 0x109900a9257d77ba +783, 0x3c4bda8e2c83684c +784, 0xc9ae040fb7f868c6 +785, 0x78098ffe994f4905 +786, 0x7a94c33eca77f0b4 +787, 0xbe6a2a95e9b5c0e8 +788, 0x797d39cf963f4837 +789, 0x8d2e249e4425d06d +790, 0x6ae2c30cd5da06f4 +791, 0x904489de762b179f +792, 0x84713e2dfb591e3b +793, 0x6405a40da3f6f51b +794, 0x976b560d663a2df1 +795, 0xed1c544784ba1e22 +796, 0xca658e995ed9344c +797, 0x2b1c6b8e4db49025 +798, 0x52b1513da528bad +799, 0x3c63406d256d9968 +800, 0x63a31ca3d423f85e +801, 0xb05a81f55789a720 +802, 0xd04412992c476c8e +803, 0x828ec2f77a150a3d +804, 0xee50926671bb60c6 +805, 0x5aa70f93e2df61b4 +806, 0x94d60fa2e8655858 +807, 0x3f5e5b770703cc7d +808, 0xc62dfb2688ca7784 +809, 0xaaf02e1e8ba89fe4 +810, 0x4ab74e0d8c047405 +811, 0x31ee04fbac6fcead +812, 0x1203b78b8228f5af +813, 0x412a70836f9aa71a +814, 0xab51cf98c03f1819 +815, 0x783a3ce9ce137f65 +816, 0x8897085b0a072cf2 +817, 0x685dd9bde8798cb +818, 0x9a1fac7b1705e2c1 +819, 0xf3e9ff98de48e9cb +820, 0x5c2d3eb1a1fbe917 +821, 0x3bda718b6b54d82e +822, 0x29f2dd18f22f0821 +823, 0xb992da1572ac3597 +824, 0xacb69e7aa14b34f7 +825, 0xcd36e3ad14f088d1 +826, 0x6aaacc96a1ec55e8 +827, 0xf8ac593f154fe68f +828, 0x18fc9cbff012339f +829, 0x2f3368ccbbb99899 +830, 0x7cec7d17f37031f7 +831, 0x96e86bfaadcb8fc2 +832, 0x74f9e7ee3d42a752 +833, 0xbd52f6c7d9b0733 +834, 0xa48e6d96bb6ce1c9 +835, 0xaefa058254b82133 +836, 0xb7a19edfd0929107 +837, 0x6160ce9125b26e26 +838, 0x6537dbbde1d2aed +839, 0xc567f9a6bec52dde +840, 0xca29fd3f22443342 +841, 0x7732aa6db6a1c476 +842, 0x8f5a4d7df6b11b3 +843, 0x76649262aa7e31e1 +844, 0x60a13eb125fbc829 +845, 0xc81e4d123dd21ac1 +846, 0x643cbb09bb72f86b +847, 0xf971a98fb25555a6 +848, 0xffa2774c66692d56 +849, 0xcb33c16c50b13ea9 +850, 0xfabf388dffda0e9b +851, 0x55d41ec12ca24b9f +852, 0x91cf693a3467e807 +853, 0x6be2c00b2c31d6dd +854, 0xc5cf513b5251ae28 +855, 0xffc4384212403dec +856, 0x45d4e1865255a69d +857, 0xfb1dcf956972086a +858, 0xcae946a55c4c55b8 +859, 0x7351ac7720e385c1 +860, 0x19aa8ffd86240254 +861, 0x8f515ae78f4040da +862, 0x1e1ed2058de50fce +863, 0x22d006dcdb374243 +864, 0x6e0f0ede7c95b441 +865, 0x70e8aa81b53b4d25 +866, 0x998f309ea41e3814 +867, 0x89ed6598fb66f390 +868, 0xb5997dc3278060df +869, 0xb2a021eac4f7e046 +870, 0x3705b60aa2fd0768 +871, 0xfc415079ab9200e +872, 0xf2871ac4cf45ecc9 +873, 0x24bf758d2246175f +874, 0xac503dd6f8141b3 +875, 0x4e879d12d9f03b3 +876, 0x82034af8cf93b644 +877, 0x59899dd7e478a6c7 +878, 0xae90addb6eb11507 +879, 0x1524ddf76730cdef +880, 0x6fd4afd5456b1c9d +881, 0xcddb9221ea001cbc +882, 0x64ff400bbf2e8604 +883, 0x6dda10549b06ed9b +884, 0xed2c85104c261527 +885, 0xc7e09217d29929a8 +886, 0x56284df611a428b1 +887, 0x1a7608289c0a61 +888, 0x7cb63db15166ff66 +889, 0xc6013c76fcdcdc72 +890, 0x8e5dd566c7a5a676 +891, 0x5a8e8565f40d133b +892, 0xe465973455848c44 +893, 0xf92eecbfe0f3c2c0 +894, 0x7d64155d4dcc5cac +895, 0xf17595706f988dad +896, 0xd590a001a6a19c5c +897, 0x82a164475758db3d +898, 0x6b144993ea1bbe32 +899, 0x22a81a7a6e453779 +900, 0x8e8c298df1a68a73 +901, 0x78056afd6d936b4c +902, 0xaaceef0325faaf62 +903, 0xe78bb7699f82266f +904, 0x523a2d283c5a5166 +905, 0x7076d87088f6c6db +906, 0x6087dd54cff5aeb2 +907, 0x7ef82e62cb851680 +908, 0x4e8bcc8ed84d03d8 +909, 0xd12fa0361df3cfd3 +910, 0xefb89c79f8127297 +911, 0xa9af4e2fbce0b1f8 +912, 0x462136685b70331e +913, 0xe9e74c93da699b77 +914, 0x9ec69215fb11d0c3 +915, 0xc10f229939e3e111 +916, 0x3f67fa79e41d2374 +917, 0xd5e7c1a9a7185162 +918, 0xa1dcce9ec91492fe +919, 0xd4e61f0727b5d21b +920, 0xdf6cdce46551800a +921, 0xa3f256ce906982d3 +922, 0x209742a6b9ffc27 +923, 0x4006c96958526a57 +924, 0x9606aebc75a1967e +925, 0x91b9f42fb64189df +926, 0xb27119defcb938bc +927, 0x128cc7a84ba05597 +928, 0x6c3df613c62d0d30 +929, 0x3adf69d48b629ec7 +930, 0xda42ee493837b128 +931, 0xb8e770480e760bb5 +932, 0x9feb55d57c99c626 +933, 0x29812d80afdae3ed +934, 0xae4222a64276a8c7 +935, 0xe3897212a5b4ed53 +936, 0x98bedfd13886e669 +937, 0xca858675d7fc0d0e +938, 0x28a359f665354234 +939, 0xfac2ccabe4128b35 +940, 0x61373cc5d11ca180 +941, 0x7007605a4512a87a +942, 0xe71f8eade7b30b3d +943, 0x3a9e77f9b99bd04d +944, 0x70d3e42488098866 +945, 0xd30fc159c7cd4d99 +946, 0xe4d3f6600d2e2d6f +947, 0x1088324dfa955c25 +948, 0x516437acd4764623 +949, 0x38a31abe50d0aa03 +950, 0x72e1054e9dc02ba +951, 0xe6971dd664d1a2e2 +952, 0xf6698cb095d3b702 +953, 0xad995a5a8c19bd92 +954, 0x34e53c6936f656e6 +955, 0x10de240bc07c757a +956, 0x3e3b9a6861c2bd1c +957, 0x9c0b0b97d3712ec9 +958, 0xabf1505a75043aed +959, 0xbdf93d3de3274179 +960, 0x28fa5904d3f62c28 +961, 0xc3b97b39ef6c5133 +962, 0xf2b2219225b8679d +963, 0x8be4ec0f930c0aaa +964, 0x47de5a56aa590643 +965, 0xb6f871b304129856 +966, 0x80a61c06233ab0f9 +967, 0x3ce6c3af8101b055 +968, 0x85b911708274e7d1 +969, 0x4cab65d093a488b7 +970, 0xaabc4b10661fe28e +971, 0x35b16dea64474a68 +972, 0x1d6eb5b093361223 +973, 0xc39107b92f0fe1fb +974, 0x1d09e048073c4841 +975, 0xc6a02f43aca8cb2f +976, 0xaf6613dbc7da909c +977, 0x5ac2a40c230aa756 +978, 0x33afb5e7c01c39a5 +979, 0xc7b0b20ea8b7d0ef +980, 0xdf7306c8ccb1bbea +981, 0x9710efc0c188b2a0 +982, 0xd6303eadb72c873e +983, 0xa38ca609b118f35a +984, 0x8390613065c6e535 +985, 0xdf9a0106757e431f +986, 0x8bcf77039788e143 +987, 0x6026806a986b378e +988, 0x482ff3b1394cb1dc +989, 0x2a27d0ccac9ede9c +990, 0x53c77f26e271b3ab +991, 0x1ba004cf276cf3f +992, 0xc135b0517dc81f7c +993, 0x5d137838db75e442 +994, 0x3fe505f93d1dbdd7 +995, 0x351654ae7d598294 +996, 0x173f8d182af9d84d +997, 0xf97dfcd164fe11c5 +998, 0xcda423e5ad43b290 +999, 0xa5cb380b8de10d10 diff --git a/numpy/random/tests/data/pcg64-testset-2.csv b/numpy/random/tests/data/pcg64-testset-2.csv new file mode 100644 index 000000000000..7c13e3172d0e --- /dev/null +++ b/numpy/random/tests/data/pcg64-testset-2.csv @@ -0,0 +1,1001 @@ +seed, 0x0 +0, 0xa30febcfd9c2825f +1, 0x4510bdf882d9d721 +2, 0xa7d3da94ecde8b8 +3, 0x43b27b61342f01d +4, 0xd0327a782cde513b +5, 0xe9aa5979a6401c4e +6, 0x9b4c7b7180edb27f +7, 0xbac0495ff8829a45 +8, 0x8b2b01e7a1dc7fbf +9, 0xef60e8078f56bfed +10, 0xd0dbc74d4700374c +11, 0xb37868abbe90b0 +12, 0xdb7ed8bf64e6f5f0 +13, 0x89910738de7951f +14, 0xbacab307c3cfd379 +15, 0x2cf7c449d8b927a6 +16, 0xdcf94b3a16db7f0e +17, 0x8a9d33d905a8792e +18, 0x4cb9eb2014951238 +19, 0x6c353acf7b26d6f1 +20, 0x73ff53d673aa30c +21, 0x1fd10760015eca68 +22, 0xabae0aa9021eeba8 +23, 0xa5ae363a868ee2bb +24, 0x9d89e0f041de6631 +25, 0x6238b133c3991a65 +26, 0xff49267d75fef51a +27, 0xfb180656ce13c53f +28, 0xaf7fadf36128712d +29, 0xa6847fc6f339c63e +30, 0xb03e0b80d71ea5bc +31, 0x63905abcb43969af +32, 0x2295af3ee00a3bba +33, 0xb8b375b994330415 +34, 0x867d9ef1d8716a3b +35, 0x4f6c02f5601b4e18 +36, 0x7c5fb4c16c470d18 +37, 0xe3b57986b804b343 +38, 0xef1d79d212aca692 +39, 0x5b98774c8806209c +40, 0x924fc76bac38a5d1 +41, 0x5266084c412ddeed +42, 0x98240bf9b831d6a3 +43, 0x5681599e81219442 +44, 0x6441248fc2ba92bc +45, 0xe3e9051a540349ea +46, 0x3a2700034390baa3 +47, 0x9f893155b6d402bc +48, 0x158207910c6d8aef +49, 0xd5282ab7608c2cbc +50, 0xc97f4651669dee4f +51, 0x3d4750d95103ed60 +52, 0xe0614542caac1f04 +53, 0xefe5092144cfc6c +54, 0x560bc486abd7e9ae +55, 0x2678b71392daa4b8 +56, 0x734970d3dc2ba416 +57, 0xcbdbe849e51e4aaf +58, 0x3b0b5e28b491556c +59, 0xd51449ac45abd88 +60, 0x6790b59991f1b7ab +61, 0x32d1c039ff2415bc +62, 0x173b9772f24f72e0 +63, 0x9490a9ca9f883b1b +64, 0x4c775989e6214222 +65, 0xac07db37e6ee6114 +66, 0x331371b2e3f10aee +67, 0xf12e5326c21c28e4 +68, 0x5d77dc280c70d614 +69, 0x1b01bd17a2f281ec +70, 0xa10d3b5882938487 +71, 0xed5a0033c394ae8f +72, 0x70bc8ea568ea44b4 +73, 0xf4600ae77965e730 +74, 0x7ff92c0b321ce233 +75, 0x6cdbc87d0cc1d670 +76, 0x9ec64f0cf2000eb1 +77, 0xfebea50259800f68 +78, 0xf2edf9019a8fd343 +79, 0x75c584ac042e5468 +80, 0xc1fa8481d5bf9a1d +81, 0x7f57180168514ac2 +82, 0x878100716b94f81e +83, 0xc929406e3af17fd2 +84, 0x6a26e2c013e4bf4d +85, 0xbc071d8848280955 +86, 0xb60d75abbfd1bdac +87, 0xee9b76afeca9fa69 +88, 0x1d6c399d2f452810 +89, 0xbaa0bc1621e25c83 +90, 0xed6ba792f8671ba5 +91, 0xf7ca02c2ab11d8d7 +92, 0x3c3cadadf0b21e3 +93, 0xdd1784571e864e9c +94, 0xfb2f992015157509 +95, 0xf50bb9f0d3ced743 +96, 0x261565f75c3e185f +97, 0xf8fe33b284513e60 +98, 0xe3d2d10b5e024664 +99, 0xd28717566242cf35 +100, 0x7ae07d133ac5b789 +101, 0x3b7ccaaa53ac338e +102, 0xcd480bace4871650 +103, 0xec6c78f923c080e9 +104, 0x44211d0ff8919d59 +105, 0x89f79af76d2a45fe +106, 0x71583fd8a837548b +107, 0xee57269c261511f5 +108, 0xa5ee8f3b128c5d1 +109, 0xbb64c20ed0765a17 +110, 0x9d4790ab2eeaf7e4 +111, 0x742f3db806d9e98 +112, 0xb81ec97aed6a0d1b +113, 0x41808b34f6a8a23 +114, 0xc20913af175dfd4d +115, 0x834427db263b22bb +116, 0xedd9c632e611828a +117, 0x10eac8524496f571 +118, 0xd76091b97eb00ab7 +119, 0x111298ae9fe95666 +120, 0x5824b2e2a6719c43 +121, 0x6e280ec539e934ed +122, 0xf74fd832df90083e +123, 0x8fee6d0f241c2e97 +124, 0x4244f331c2f19c3c +125, 0x3dde75a845cce97f +126, 0xe35bb8e635a9915b +127, 0x39d2943037f7932e +128, 0x1fe2d134201d0970 +129, 0x49d00b63c749b804 +130, 0x960c2942cd4e4e04 +131, 0x8dd8e009dbc0435f +132, 0xcf493495c3a055cd +133, 0x8f7b5a1c0f9fe9cd +134, 0x49d5f90374641a25 +135, 0x69b3932073d3524c +136, 0xd170603e7de84ee2 +137, 0xa062ba3ed3539948 +138, 0xf5861cc5b5d56c82 +139, 0x5e914998a30c7e76 +140, 0x8d77f2ad1503c0f1 +141, 0x980b6a9e3b4181fb +142, 0xd9299cd50694c084 +143, 0x253dc0f8f1cec4c5 +144, 0x68110fb9d1b3e695 +145, 0xe8f3120d0aabc461 +146, 0xb066e7df0dfb042 +147, 0xd29ce0f797e6b60b +148, 0x6a569bb7ca33bd42 +149, 0xd46e08b2dc2385f8 +150, 0x28c61d11d055767 +151, 0x5d73aa3d1a2bb725 +152, 0x1421191e1c14829a +153, 0xa711bfb6423df35e +154, 0x461af97a86308006 +155, 0xb3e1018ff3519367 +156, 0xf19cf866a268ef2b +157, 0x207715eac9199d1d +158, 0xdd621c410975b78c +159, 0xf390aea68683610 +160, 0x617a2d107a0047d9 +161, 0x6e05ac416e5bebf0 +162, 0x7d253e70506c1bed +163, 0xf9f96f4a7dd53810 +164, 0xc693b29cb1573f73 +165, 0x4f1146b0020ea544 +166, 0x45140608fbd40579 +167, 0xdcf57219828ce6be +168, 0xe19d58cca37b5b32 +169, 0x82bda95b2a161235 +170, 0x5823c3d8a2b6c9ba +171, 0xfeb2e74092fdf89a +172, 0x50e1ad1abc8f869d +173, 0x2ec63d0c105eb8da +174, 0xe14e1c4845a3264a +175, 0xcff53670455eb6aa +176, 0xaafaccd24619fa3e +177, 0xf55a988486e2422a +178, 0xecfba16a90ff4d04 +179, 0xbf8d36c2f644757a +180, 0xdc56ed75a0dd6249 +181, 0x3f45023eff17c3bb +182, 0x2428bbfe90023fab +183, 0xab892c611adcb70c +184, 0xb6f13d8c0c2b9d74 +185, 0x2ac3fb11d224f2a8 +186, 0x65433dcfae2d9351 +187, 0xe906859ae4b45f82 +188, 0x8fb7f5f093d76a3b +189, 0x940dd290b5e88d1a +190, 0x31b27d21bef116e7 +191, 0x86a964e2c83b5296 +192, 0x85ffd17bc079a9e8 +193, 0x16c47c724e7ab7f1 +194, 0xfb6098a9867e7d7f +195, 0x9246fb69092c6cb2 +196, 0x1a4033572760f32 +197, 0xc5cc568a8b273b84 +198, 0xfa6f9f2fbdd44abc +199, 0x9701b8e087718ba3 +200, 0x51d6a7dcf73f8f3a +201, 0x30008172cc6a972d +202, 0xac2ab49a5ca6ac81 +203, 0x31f28ef79461e54c +204, 0x93e35a8da8cc6132 +205, 0x9a2c58beeba3d5b9 +206, 0xf6615c1de266ac39 +207, 0x127ff9f8166b766b +208, 0x7ffe380e80a69556 +209, 0xbe7d2c228e1542f7 +210, 0x2d5ebb4e50ba1746 +211, 0x63585761ae1bf684 +212, 0x1019eb5cee022fea +213, 0xb9d3540ab58da30d +214, 0x1677f4cb45620eb9 +215, 0x6524baee51783822 +216, 0xdf9f2ddcfabb0adc +217, 0x78e8acc43b287935 +218, 0xe9a1974e999222b5 +219, 0xc41324ec2291e780 +220, 0xea52abc9ecdcbc9f +221, 0x209d7bcd46ec6b04 +222, 0x12d504c09803db2e +223, 0x1200e6bf21475d81 +224, 0xde6d3c2b35fd2cfc +225, 0xa2526900ac33bd3c +226, 0x7f1f5290fc432bc5 +227, 0x29ddfb380a3d69c8 +228, 0xac79cb6942a2909d +229, 0x516996685b67a92a +230, 0xb5fc39041cb828bb +231, 0x75d9d8ca0644a276 +232, 0x81e98b76be92a3e9 +233, 0xca27888fafe12179 +234, 0x17be2ae039925765 +235, 0x9429846c0e6d0342 +236, 0x327dfd50439815e9 +237, 0xcee20cd7bc254aeb +238, 0x7d250389f453f29e +239, 0xfd1b232a85c95569 +240, 0x2ed55fac80f3e9e9 +241, 0xf6886c20417a1be7 +242, 0xcd08e61f0b0fdfde +243, 0x7b33e34da5c27bff +244, 0xd043c4b7d5603dd5 +245, 0x9a544e4c70a3b686 +246, 0xa7b60398c381f771 +247, 0xe9e7a3487c4bd4f2 +248, 0x10b58fdfe1ff112c +249, 0xd5c1c9748c0f4ceb +250, 0x61be9d09159d54ff +251, 0x5356f51e8239f510 +252, 0xfe7889d9b202ecef +253, 0xc7fc19ca5d263d5d +254, 0x7c4c07e61dfd9f69 +255, 0x6c315fe5015f300a +256, 0xe0a5bc00039747b4 +257, 0x16397fdcf829ee80 +258, 0xb55aee80d16a5169 +259, 0xca0609944d007eea +260, 0xcc982249f65a02ce +261, 0x528161feb149c148 +262, 0xcbf08ba49b41c006 +263, 0x39af1ff0b6f14138 +264, 0x5cc036be69799aec +265, 0x6adde125b1db21c5 +266, 0x8a99d83d6b613b67 +267, 0x1cd43fca9451f74c +268, 0x682dbb26ecc96365 +269, 0x13b4be2ceb43e3 +270, 0xbe8fbc3b6f4f581e +271, 0xda148a2f4bda5719 +272, 0x239106ca3319f393 +273, 0xb42b4dde641f0dd5 +274, 0xd233cfdf4cb0af74 +275, 0xfb5919d905589afc +276, 0xd802a8860c10b66a +277, 0x6c923e1d00e7b5bc +278, 0xfacce1134f383b89 +279, 0xf9570abda7a6d553 +280, 0x80f0f9796a208f18 +281, 0xc0e1df5280951c57 +282, 0xe9f143f08257bbe0 +283, 0x79e4c6463123d588 +284, 0xdd2118583f2b1684 +285, 0xb399ff5f2329fa18 +286, 0x4b3e9ebae96f813c +287, 0xc484dbf247787384 +288, 0x921865eb97603f2c +289, 0x18063c68e257d300 +290, 0x643181f345e7fc26 +291, 0x12e0b0e8eadf9fa7 +292, 0x79e613fe73dfa354 +293, 0x6db4c59203b7217a +294, 0x6c7a0e9ba6139eaf +295, 0x9617c7ac4e3f6d97 +296, 0x1f68a7b4fb1b4b75 +297, 0xef0b7ab24944f466 +298, 0xaf1dee1f4be1bc89 +299, 0xd2e355c959f5fd8d +300, 0xe594c3fb95d96efc +301, 0x9554766ca3342906 +302, 0xa4bbdc77d12842c +303, 0xb62400211ee489a8 +304, 0x91abadaaa3bbe67c +305, 0xd371eeb91deb42bb +306, 0x883bab35cbd2b6e5 +307, 0xd030c3d9411a9041 +308, 0xff3c110a858ff000 +309, 0x59bdf5ca47d0bde7 +310, 0x2bc80fa3cdba1853 +311, 0x6444ccb652662cb8 +312, 0xc0c7e256b9e90339 +313, 0x70714ea9c9d72302 +314, 0x96a0142f9d897d27 +315, 0x209a9097c5a91ef7 +316, 0xb9e33afc5171e009 +317, 0x47b37af433a58d40 +318, 0x30cc4ffbfa831d26 +319, 0xdcea4a85ff815466 +320, 0x907d5bd027f2e5cc +321, 0x7c081f6852e04a4b +322, 0xe61950749c1d502b +323, 0x1604e937ee69834a +324, 0xb2372d952dd25309 +325, 0x53f6a5b834c72577 +326, 0x2ce7a74395e0b694 +327, 0xacbf9ab4fe91f225 +328, 0x5ce1e63d3a2bb90f +329, 0x54740da3a5ed139b +330, 0xf194ddb39f29880b +331, 0x3305374f5d8ec08b +332, 0x831dd0164927ff4a +333, 0x625baa78e4458cf +334, 0x29d27dc0a4a71152 +335, 0xe227bae9a1401034 +336, 0xca0c209831846b2b +337, 0x8e8cc54b08b5a411 +338, 0x38f2b4acaac27db6 +339, 0x8ec88baac814e86b +340, 0x31c08e46b007bde +341, 0xb686c02722794c09 +342, 0xb77cf8fc682e3907 +343, 0xa56334e7f606f4b2 +344, 0x9c80b127bddd5f4f +345, 0x12df14834cd858bf +346, 0x3f14762a9cf5fb9f +347, 0x930a70941ef5779e +348, 0x64e96c849c30c080 +349, 0xfdf53bfba1300484 +350, 0xec7a9363c21bc616 +351, 0x26e9fd6a115ecb47 +352, 0x9707a84b5bc77fbb +353, 0xb23b2737b20d5903 +354, 0x22f4825ae80f6501 +355, 0x500644b12be6a01b +356, 0xb746645b2af082db +357, 0xe6af051f697892f8 +358, 0x577c724248a1cfc6 +359, 0x3d2b6a434c84eed3 +360, 0xd260f5efd7328314 +361, 0x95c16cc84bb3f55c +362, 0x7a01b2e4e0e80ca7 +363, 0x41930c3ce70a0935 +364, 0x1299bccf39d4e110 +365, 0x494883ba1a8a87f +366, 0x9478ecfe2d918e60 +367, 0x30ec9a5670cda8af +368, 0xf9bc877e833e2b99 +369, 0x1b83a0acfbb4a8db +370, 0x73bc1740c0d18880 +371, 0x65086ca9773cb3e1 +372, 0x3b78c3ccd63cff2e +373, 0xbfae748795acfb31 +374, 0xa4c9d5d56a15ba20 +375, 0xb9cb41721e52b71e +376, 0x1532f15d4dc47748 +377, 0x5a4d647a4b9ee632 +378, 0x8513c7c5a50898d9 +379, 0x6d3d98ccd5461b2e +380, 0xa65e99be2fe98d6 +381, 0x31abc8855334a0e5 +382, 0xf1ed22a661dca5b8 +383, 0x299e2b63229e03be +384, 0xda201a06687bce48 +385, 0xd27794b302142c55 +386, 0x642bd3e1c7898a9d +387, 0x777f1ff00afa1a87 +388, 0xd2f1c84fb3877baa +389, 0xae417583289191fd +390, 0xd641f1d88e0e2d55 +391, 0xc1f1d98fb5d18ebf +392, 0xb0f72aecdadce97b +393, 0xe9b8abc764f6018a +394, 0xd2a37cff8e890594 +395, 0x2dd70d631a528771 +396, 0xbf8ba0478c18e336 +397, 0x1630bf47f372ce0a +398, 0x6d04ea20dc3f46b8 +399, 0x6591881bf34337f2 +400, 0x33c149c7eb5b4103 +401, 0xf01a8c9857c86748 +402, 0x184348cdfc16d215 +403, 0x141168b253d2ed7 +404, 0x52aaf012ef50a6f1 +405, 0xfda1722387e16f4c +406, 0x43c30f57d6c038fa +407, 0xd4a8611f5f96d214 +408, 0x2c512ce17e987f2c +409, 0x961ce450f0fa2822 +410, 0xf55a506ec6cea9cd +411, 0xb76d694d9c7f5ef6 +412, 0xfb029216dbd8e988 +413, 0x93162501896a0081 +414, 0xfbbbd2c5ab300f5c +415, 0xd648b6da7387d491 +416, 0xc73b4697471d9d98 +417, 0xe37412bf1c93ee76 +418, 0xa1a96d96570e6637 +419, 0x5b3ab4f82428f65c +420, 0x873d849b188aa36f +421, 0x39fbee0ffc9fa9ff +422, 0xc70d21b744d677fe +423, 0x2b8a43c23043d209 +424, 0x93c33eaa37370d16 +425, 0x8930ac1880f2b0ef +426, 0xac01d27707036af0 +427, 0xc2af3fee504343a0 +428, 0x1c1dae2ad5535d97 +429, 0x9ffc21804b76a480 +430, 0x69f903412cc13563 +431, 0x9d3c4e2759a0c47d +432, 0xb1a8f894be6302b9 +433, 0x95e1fd7951479506 +434, 0xbb9e6c03cd4ae8e3 +435, 0x85206010c9b737cf +436, 0x767e813694d6238c +437, 0x4969af329ccbb30a +438, 0x3aa9af1075aaea5c +439, 0xb1ff519e8118a993 +440, 0xb21a23a3c91180fe +441, 0x320b24582ca3fd88 +442, 0xf8ca56415fb4e453 +443, 0xabd0899c07205e77 +444, 0x87fdc7a44b4ad50f +445, 0xd75744911641a278 +446, 0x7c8c9a65df6fcb95 +447, 0x79d785e3c7a5b695 +448, 0x421e4565ba1f592f +449, 0x27f87eb2517835cf +450, 0xb62cc4297441c83e +451, 0xd817a80ac815ca6d +452, 0xad84388130df2aa8 +453, 0x5e6b1640452d6ac8 +454, 0x936285e15edce2a3 +455, 0x903bccc4969768e8 +456, 0xefc2cb7b109d3140 +457, 0x633e9dfdda2d903a +458, 0x2a2f3225925678a1 +459, 0xe07eac91a27f8547 +460, 0xe50ced40eda78cb3 +461, 0xc5b22500e1c7441 +462, 0x32becf61bca3aa72 +463, 0xa2e37c4b30671344 +464, 0xc9f1c1910f45d544 +465, 0x9b50333b2dcdf730 +466, 0x310bfd53a1684b94 +467, 0x1e1dc21e66ac6455 +468, 0x81876c2bfb1ed5a1 +469, 0xd0c54a3e25eadc7b +470, 0x3791b6fbbd5c7ba0 +471, 0x133be57356c599fc +472, 0x8d1148eb8e83fdea +473, 0x311aedba0d8b42cc +474, 0x1142ae52745f94bb +475, 0xc5f4ab2fbde8c4a3 +476, 0xd23be827b5b24f6d +477, 0x65f95194cd122715 +478, 0x4b48969d73125922 +479, 0x46f165052b8ff988 +480, 0x5c689f94b9275ff4 +481, 0x93b03823ff2d536b +482, 0x871f3775aa4e3523 +483, 0x5af829f7cc0f66a5 +484, 0xa32e05739cbeac8c +485, 0xacff1856ddace0fe +486, 0x8eeb5e7f991a5322 +487, 0x6325c2720e0dbdea +488, 0x9fb817bc4fdf5200 +489, 0x9786f0d850e43d78 +490, 0x571f76dd7f9fb77a +491, 0x4d9e94e181cbc63f +492, 0x8bb632d3376c547a +493, 0x9cc26d9efd1c88b9 +494, 0x9c5d49579df52b0b +495, 0x6201abf7e1cda07b +496, 0x90d68f0c6c884963 +497, 0xfc5b66188ef7f561 +498, 0x6d9303cf2e0e0f95 +499, 0xd7cfcff535f5ed07 +500, 0x14d1a1228daa4ac6 +501, 0xe00ef5762f66ae50 +502, 0xf113a79471582978 +503, 0x430985281785dc7a +504, 0x31914108c206ed5 +505, 0x7ba6707b6419971c +506, 0x2ec63b033ce112e5 +507, 0xf8bcd36ced3b41e3 +508, 0xe5cf908c8010414b +509, 0xf5ee224b7c703e30 +510, 0x9a9733af0b12338b +511, 0x83e18cc00ace34f8 +512, 0xd52cff39e23008b8 +513, 0xa700578136b9c0c5 +514, 0x3fa179d32ac51f99 +515, 0xef2d5eab6d4ad380 +516, 0x709024a5abd032df +517, 0xc607c7ee349ede87 +518, 0x803d784e9731eb5f +519, 0x2ef06f4ba769282d +520, 0x4bc1dca1e9f07eb9 +521, 0x930c958a7a72f94d +522, 0x249bc8db2cc7a3bf +523, 0x3845305798f9a5d +524, 0x6f137eca9ab6f948 +525, 0xc31f5a963d31bd67 +526, 0x9d39693d5383626f +527, 0x52fb41c335a8b98e +528, 0xb79d1a29a06006ec +529, 0x7c0926a7a3eda2cc +530, 0xffdf5214406fd53e +531, 0xc6aa02a7e94282b9 +532, 0xd4a4431b4aa301ee +533, 0x4271cc0f9420d3ab +534, 0x26fccd7cc7fc2485 +535, 0x330594bb945b8d5a +536, 0x6ea8eaad12e5cb8c +537, 0x831c3467726bede3 +538, 0x31d1eb10017eaa61 +539, 0xc7aa75e41508f5cb +540, 0xde51810f0cadd0b5 +541, 0x50e5b3e73692f80b +542, 0x82107ec55636e188 +543, 0x9828ef175d843ab4 +544, 0xb8edc6a860dd421e +545, 0x25c0c138fd537ac3 +546, 0x47e72a771e8eb563 +547, 0xbb0f8c5333f4a2cc +548, 0x91750d2fb9b2d479 +549, 0xe662d8f6fe38df36 +550, 0x72a6d879fb5619f0 +551, 0x6817c7878dcbf077 +552, 0x4e7741cb484661e8 +553, 0x3b3b3ba0be5711bf +554, 0xa6989f5d25868765 +555, 0x43c276398997e4e0 +556, 0xdcbe16a94da28870 +557, 0x454936980a699c99 +558, 0xac614bfa8f0266c6 +559, 0x9174841392e213d5 +560, 0xa0e2acffc5fc9d1f +561, 0xe53a08a7a0e6521a +562, 0x2b845cf7c24172e0 +563, 0x265a4fc5f7adec0d +564, 0x1f34fbe5f1e49420 +565, 0x139181f6fb647f20 +566, 0x88c35d46e2fcd05e +567, 0x2a6d5b55903c0459 +568, 0xcea28eb621ad7bf1 +569, 0x5c9cdc13e7aaa30 +570, 0x5fe63e14746e7103 +571, 0x7923e53d73835db9 +572, 0x376e661210bf1b06 +573, 0x5b1cab85450efdd5 +574, 0x3908dc096c70b452 +575, 0x4825e303cd1f396f +576, 0xed476bfd702957c3 +577, 0x6acc013aff5db743 +578, 0x62c80b776343d488 +579, 0x9c75edcd5b012697 +580, 0xaa053362a3b9770a +581, 0xa907e236c7c07e94 +582, 0x15b2c380451692c0 +583, 0x94f79142697bd61f +584, 0xbc657d31ea98d44f +585, 0xcbaa5e52517a1f5e +586, 0x96aa2e44a7c4a03f +587, 0x216d3c66db2b515d +588, 0x157001807e3ca88a +589, 0x52b3a596bdd3859a +590, 0xed747e7fc5e3adac +591, 0x78fd765ddb2c448d +592, 0xe53dc7299ed8614e +593, 0x75ad41fb1d7a790a +594, 0xc14f6b944b0e6cb1 +595, 0x7c314b69fce3df1c +596, 0xb56d82eb740d7abc +597, 0x5132a93c41251fdb +598, 0xe3ce35bd2a82f958 +599, 0x440571a981c722f2 +600, 0x194cdfd9f186bc9 +601, 0xb89e522a5db00939 +602, 0xad35f339f68df3c8 +603, 0xa82ab18420322293 +604, 0xaffa6df9b72b27c4 +605, 0x9615694d23beaa2c +606, 0x1d82ebe563abad91 +607, 0xab50ef65fbd94385 +608, 0x1b070dbd70a9a14 +609, 0x2ececa796abbadf0 +610, 0x6bbeafe9e81ab2a2 +611, 0x60dcd0d2a9b76914 +612, 0x1e748039ef05c33f +613, 0x6d4d17f2213ccdff +614, 0x9fa56132957bc987 +615, 0x60a17185de2428eb +616, 0xb56038ddf306479c +617, 0x3b1db5df92d06d8b +618, 0x24d1bba8bdedf580 +619, 0xbfb7e6740ebaa4d9 +620, 0xab31c4473e46f61d +621, 0x6deb3cdd8fd5869f +622, 0x23032e47746d72d6 +623, 0xa9e72d734e10f2e8 +624, 0xbffd199b6157bc23 +625, 0x29f8254df273fb62 +626, 0xb076142130ee55ec +627, 0x5b0b08374126c309 +628, 0xea4536aae979521f +629, 0xc064e7abec91a174 +630, 0x46133ef80c59d935 +631, 0xf0227e2da1b14160 +632, 0x675a76641e1af5a +633, 0x2f50a069b33d198c +634, 0x3ded5a65e1d657eb +635, 0xbb6999b020694f6b +636, 0x86b2f2b33487aed7 +637, 0x76e14e85f8bfb4cf +638, 0x38f7f1e44bd4e0db +639, 0xc1a7d41b7e80d4ae +640, 0x1dfaaf80bbceb42e +641, 0x3f51c11497720c2b +642, 0xce6da1415ddb8b80 +643, 0x7377d8bcd359b5f3 +644, 0xe077208f3f810aca +645, 0x9a06a8a2dacbffce +646, 0xca1f99156b09b735 +647, 0x2ff9a93064d91451 +648, 0x50f3ea93f351a7ef +649, 0x606fceccb07054de +650, 0x7e83d6d2f8f6685d +651, 0x78f3995291c5d407 +652, 0xd28d2460e22d0228 +653, 0x2c5636f68a0054dd +654, 0xd9fafb1c56c8f6cb +655, 0xe39889b5f9d74464 +656, 0x1355372bf5db2cc1 +657, 0x26768426b9ac323 +658, 0x4af1dbdc1111fd89 +659, 0x66973587943b927f +660, 0xf86f5f50684dfb1d +661, 0x1247d574ff79b534 +662, 0xc8039f3259210fe2 +663, 0x79b573235c92a9f5 +664, 0x213f642d8450e2f0 +665, 0x5db7706973376566 +666, 0x6182c12e69b373d7 +667, 0x3e5ac47300aec07f +668, 0x4b5b6c57b1574376 +669, 0x6b7fcceefd56b17c +670, 0xf656c3455cb9d4b8 +671, 0x7577e2e13329721f +672, 0xf33c0c53ce956e8d +673, 0x7d0f328ee356174 +674, 0x10ec9a168088686e +675, 0x71ef1776d062dfa +676, 0xaa7b590a488a6bc4 +677, 0x38612b6dd8049a1c +678, 0x939045e36874f731 +679, 0xcb9d1d74c56d5ac9 +680, 0x54f1c1c8fef1d8ff +681, 0x3ee4b85c8c7e939e +682, 0xb9b4608e019f352c +683, 0x79d4701275d12e6a +684, 0x2632a2d9835c7f19 +685, 0x1662cd9fba293692 +686, 0xbcb70265115ee944 +687, 0xdc43fb9761468604 +688, 0xe3eec4e7d3871352 +689, 0x829531753226989d +690, 0x2748cc67f540e074 +691, 0x39c4af25d607837d +692, 0x741a243f4cb5df99 +693, 0xda1353287e18b49a +694, 0xa6735689d751ea74 +695, 0x46326d587340ce0b +696, 0xc18531df4550012b +697, 0x6f7901e05dd4b818 +698, 0xfb966afc4c001d63 +699, 0x6dc10fca67a9cfdb +700, 0xd6527ffadf0feaae +701, 0x3b900172045e25d +702, 0xb7dd594cdded6a46 +703, 0x6602aee7ec1599fc +704, 0x7fbf12f23747546a +705, 0x32e63f662bd2de0d +706, 0xedf47770b67ed641 +707, 0x331bef83481c5c2a +708, 0x8fc4256fdf05158c +709, 0x98eba48dabccf5e0 +710, 0xdbc2f2cdb7b1c154 +711, 0x7777755616517ad3 +712, 0xd473c147d2628ac1 +713, 0x861e15d1d760b5a7 +714, 0xf4d25926405ecb07 +715, 0xb7739c69effff86e +716, 0xe97fbafa6f96830c +717, 0xf13e8a334e8bede1 +718, 0xcd60010cba4ee4f9 +719, 0x1f537ac2b82e6008 +720, 0x1fda8d781a89140a +721, 0x9dc204f3f4a463f0 +722, 0x456dcd18eb56a1ab +723, 0x629957bc87bd16a1 +724, 0x2c8000ddb8c75253 +725, 0xc31dae9ec8449284 +726, 0xdac05c8baa2b691a +727, 0x21ff7be9ffa3e7ac +728, 0x844f4b5ed4ee08d0 +729, 0x651f913fd636c994 +730, 0xca3e71a2110b2d49 +731, 0x7709bc42253ed09d +732, 0xbb164d45b6569d43 +733, 0x90ec2f040c20a112 +734, 0xfa6e77e9166f5be4 +735, 0x6b6d12c1842d587d +736, 0xfcd7ff8466e25e2a +737, 0x6a5a2ed8bd971297 +738, 0x2ec35f6bba5adcbc +739, 0xc83676e16651249a +740, 0x458f6064cefe10ba +741, 0x90d54d527e6cd028 +742, 0xa5613e88db27c388 +743, 0x331e0c7d85aa1abc +744, 0x8cee4977e210358 +745, 0xfcae379aa6cbff8e +746, 0xd1407afc97a57e86 +747, 0x1fab25c864f094ae +748, 0xd914864a63004552 +749, 0x4214d226a20f1384 +750, 0x3f4e0d80c488b715 +751, 0xc5ca2f654024b7c8 +752, 0xc1e27a124e7c821c +753, 0xd890a915ffc7918c +754, 0x22fba040ce51a9f8 +755, 0xbf61cebd8891617a +756, 0x7846609ee228e319 +757, 0x536d1854375509b8 +758, 0xbbfb45fc6e666f50 +759, 0xd85b4c0527f9d7d6 +760, 0x528cc9c7fa2a84c8 +761, 0x27a1baece647f2cb +762, 0xfddf0cb92fe09dc3 +763, 0xeb5008fe965d8d96 +764, 0x4a3307937eb2e5c8 +765, 0xd07d74c240c6c363 +766, 0x16f62290179d1bbf +767, 0xe99c9bcc9cb1ece7 +768, 0xc64f9be03c8a93be +769, 0x32659effaf666c1f +770, 0x4bb228cfb30b6672 +771, 0x98764870842068a5 +772, 0x5b12ef2d2cd8bdcc +773, 0xbc79d1c1b41f28b8 +774, 0x97a517cf3279fc9a +775, 0x34ffd46c1d4d6025 +776, 0x9c302307ee25c8f0 +777, 0x399604eed1f18a8 +778, 0x1c9b813c2043142a +779, 0x2944ea5e55267fe9 +780, 0x5a8a9f5e728ea667 +781, 0x30c8440adb804a0 +782, 0xee0e6b627099a937 +783, 0x3d50757ada3c52da +784, 0x4548916b32c813ab +785, 0x602a186fe5bf109b +786, 0xf0d440a2227ba304 +787, 0x5a10d4e0ca9ea32b +788, 0x6e5eb90da13ba64c +789, 0x4c6af8fd04241ab2 +790, 0xf9eb31d26e093006 +791, 0x5d674878839fe3ea +792, 0x1562b55b2484e47c +793, 0xa87188c099c1cb61 +794, 0xb7736b8aa02a3392 +795, 0x5f4b301125abb20f +796, 0x361d566984637f44 +797, 0x68c4b3feac8bd0c3 +798, 0x7066c634dd2503c1 +799, 0xfecbf7c9441eb6ea +800, 0xdbc26ae0fc81436b +801, 0x9ef3e2b48252e7a4 +802, 0x31a49b4c339b37c7 +803, 0xb01b2a83cf346cf4 +804, 0xc24dc2347f82fbe3 +805, 0x134cad272dcd410f +806, 0x61260742823ba59c +807, 0x53ac4c193a97c730 +808, 0x9207c9833af34b52 +809, 0xa72e7ee77078d1f5 +810, 0x2e6f6e1b05936885 +811, 0x783b99ce5dbf9464 +812, 0xfdfeb6f0d027bb44 +813, 0x40eeb27096f92b0 +814, 0x5ef96ff5d4a4521f +815, 0x5595806ae873718a +816, 0x67d449eecf4ca1c3 +817, 0xde837ab611364f3f +818, 0x7034c24d2b139be9 +819, 0xe21166603e0a9c86 +820, 0x935694435c1f0d51 +821, 0x6cb3bec90c126088 +822, 0x4096ef662b7a9f89 +823, 0xd2d85b8d238d8c15 +824, 0xa4ea533ce3ec59b2 +825, 0x3654729d80a2db29 +826, 0x214c4cc3906d29d4 +827, 0x201c447e7588e373 +828, 0xe8b8f0ae25f683eb +829, 0x6744aaf5754e38af +830, 0xd1ffb10d6f27a061 +831, 0xe536733a7b3a6c30 +832, 0x39f0f66e47cbf2c9 +833, 0x856a9593526fde2 +834, 0x2e2a817a0098ea4b +835, 0xc5e1eeb551a0e3d3 +836, 0x3f21e2f5e2d50b2 +837, 0x906af56c66dd9f8c +838, 0x30f6dbd70329fac8 +839, 0xc443dfddf3c01a60 +840, 0x7ab85d9aa9675470 +841, 0x8c9080bd39717bfc +842, 0x4b1ccdb3c3597f6f +843, 0x74e2542d70ab5d67 +844, 0xbb3d236aad00f74 +845, 0xcf3cadf9a2804774 +846, 0xe851d9750e42bd07 +847, 0xc0ad82029b1c371f +848, 0x7ee119eb552d6c07 +849, 0xd8024049bd1d784a +850, 0xfa67a899760363 +851, 0xaa7c2f438b178197 +852, 0xc473674a47ffe064 +853, 0x539fbe3fc674c270 +854, 0xdb48484748a76f3b +855, 0xc73b2b092060d +856, 0xa1d2a15345016f5d +857, 0x4d0fe8599f9bba47 +858, 0xa0edc275e6f8f1d1 +859, 0x40590a8655bc8d72 +860, 0x35b4223161f05f75 +861, 0xa04c0c0f616752dc +862, 0x7f371ed2ca45432d +863, 0x2ff1a08f75ac6438 +864, 0xe2dc5c3682282f48 +865, 0xe1e4179fa98d9013 +866, 0x8cb083d6843a73d5 +867, 0xb4c2b5921b706854 +868, 0x738e14c0e7352445 +869, 0xcd2b646f91afd8c7 +870, 0xd5779a5b57a264fd +871, 0xc39ff855586c7d07 +872, 0x3e3f0098c631a859 +873, 0x644e02fae032110 +874, 0xa8834613c0a45278 +875, 0x69482f2c08e10657 +876, 0xe4ee475bdb87e69a +877, 0xdc1ef7b25c0d0019 +878, 0x88a3fa2be18d8744 +879, 0x60a02e0b21c5bec7 +880, 0xb6867b88aa19bc1a +881, 0xb599409affcf10eb +882, 0xaeaa1778a5e59daa +883, 0xd7a91a52c16663e3 +884, 0x93cb269affe07b1c +885, 0x841b6ced3a4ba815 +886, 0x84541768e1540a5c +887, 0xe3943c84f83b3020 +888, 0x5de366fbd7b45258 +889, 0xd787cc3bde91a661 +890, 0x814071446edecb57 +891, 0x15d8c602a1141514 +892, 0x72f07bc8002d1d0d +893, 0x4a8bd8dc9a1f0f3e +894, 0x8723796ae0f20d35 +895, 0xda7283c2051f73b2 +896, 0x2df0cc247f90bd3b +897, 0x79a8522b968f990a +898, 0x951ede190c8b9d02 +899, 0xc512f1a5b14b018a +900, 0xf0e3ddc03b9a4259 +901, 0x8cf4a35ad312e15f +902, 0xebef28926b11094b +903, 0x5628ba687325921c +904, 0xc3aa75e57edc49c3 +905, 0xc38382fa98e762ba +906, 0x8d209e896285848e +907, 0x2c7d6adf592b4a3e +908, 0x62de48e36f8338f3 +909, 0x4a752741e00de30e +910, 0xf7855b70f1f6ec2b +911, 0xa505fa4428199e43 +912, 0xe8b6b423b826bbac +913, 0x4bd1206cf8786d05 +914, 0x6dcf040391fe3bf4 +915, 0x913f500f87e1bba3 +916, 0x5acf775aa180a5d5 +917, 0x74dd28d9432ce739 +918, 0x996c2ff2f0dc2495 +919, 0x73dbfe6c56effe4 +920, 0x56fddd25196f5e40 +921, 0xe87810158f5b7 +922, 0x7b8795e996383f1f +923, 0x9ba5ee7c777c4c82 +924, 0x17ce3908d270fe1c +925, 0x3df9e613c1aedfae +926, 0xcdd26871b32fc8e1 +927, 0xd71cb13afc633979 +928, 0x63427c8ea9b1c79e +929, 0xd070f7664d3b405d +930, 0x46f2a9e32d9fb769 +931, 0xb4c3822a45e9fe9b +932, 0x8ba30b97fe6f5ec7 +933, 0x70aa554ee2fc11f9 +934, 0xa80c99dbe0cfcfaf +935, 0x36d9250cb2d68ed +936, 0x2995e4b9e1cd1db4 +937, 0x4b3803ba57fc570f +938, 0xae3959e7d740eaa5 +939, 0xb4cbd6662adbae08 +940, 0xae46576446e8dbc4 +941, 0xc4828e008a9a8a54 +942, 0x145d7db8e6554b2f +943, 0x1b1b8916a730c371 +944, 0xdaf84b2bebe31963 +945, 0x5b59b80ef23a2403 +946, 0x9180c7e89cab6fd3 +947, 0x80e58f5411babf34 +948, 0xa06cf55185b9b005 +949, 0x13b2c798424173ad +950, 0xc510f8e706311d49 +951, 0x1f974b83b6046d3a +952, 0xae6e8e85e822d1c3 +953, 0x66f2c8dc3274a31a +954, 0x7e04dbcbf65bd377 +955, 0xabf41ede01ec20a4 +956, 0x5efa0948f6bbb2ea +957, 0xbc91c99d8592255 +958, 0xf6d6917911d86d75 +959, 0x85ce273d54e9097a +960, 0xbdfd30f2420fff92 +961, 0x8802f02f610b537c +962, 0xd1d70037ed543229 +963, 0x908aaf97f9693a46 +964, 0x1f6cfeaa0834d53a +965, 0xa453fd1648ce04d2 +966, 0x2c38bb85ebc64af9 +967, 0xd2daff551c90c4f8 +968, 0xae5a0d949797d784 +969, 0xf0974c8552ac9593 +970, 0xa10b70499f65c693 +971, 0x39a449ebd594ddff +972, 0x8ea090f2b17b9b49 +973, 0xc592de318090fd83 +974, 0xb63e4fbc467b6912 +975, 0x57a0c1c5ce0e4dcc +976, 0xa7c517cf3d436b35 +977, 0xef6dcb0f3fad038b +978, 0xaf4fb60315b91287 +979, 0x5e0776f67304f331 +980, 0xe927753b8e6f7932 +981, 0xd3df2dd92559e304 +982, 0xdaed52aa6af44413 +983, 0x1b59f4dac1e181f8 +984, 0x4a73c2293877ef39 +985, 0xca45d0d015fe44de +986, 0x4659c8b7853735a8 +987, 0x12de6466bdf8adeb +988, 0xaeea857a09bfec15 +989, 0xcc9cf4b3c0b88a23 +990, 0xa44ae52396a5e1bf +991, 0x5847a724305d137f +992, 0x8f4d4de223956182 +993, 0x58254dfada867a8 +994, 0x900a98222c2f339e +995, 0xdb575260935d51d5 +996, 0x13fb4bfbbc0d7b53 +997, 0x62213850186bb92b +998, 0x2a34823312c00388 +999, 0x6148329042f743b0 diff --git a/numpy/random/tests/data/pcg64dxsm-testset-1.csv b/numpy/random/tests/data/pcg64dxsm-testset-1.csv new file mode 100644 index 000000000000..39cef057f449 --- /dev/null +++ b/numpy/random/tests/data/pcg64dxsm-testset-1.csv @@ -0,0 +1,1001 @@ +seed, 0xdeadbeaf +0, 0xdf1ddcf1e22521fe +1, 0xc71b2f9c706cf151 +2, 0x6922a8cc24ad96b2 +3, 0x82738c549beccc30 +4, 0x5e8415cdb1f17580 +5, 0x64c54ad0c09cb43 +6, 0x361a17a607dce278 +7, 0x4346f6afb7acad68 +8, 0x6e9f14d4f6398d6b +9, 0xf818d4343f8ed822 +10, 0x6327647daf508ed6 +11, 0xe1d1dbe5496a262a +12, 0xfc081e619076b2e0 +13, 0x37126563a956ab1 +14, 0x8bb46e155db16b9 +15, 0x56449f006c9f3fb4 +16, 0x34a9273550941803 +17, 0x5b4df62660f99462 +18, 0xb8665cad532e3018 +19, 0x72fc3e5f7f84216a +20, 0x71d3c47f6fd59939 +21, 0xfd4218afa1de463b +22, 0xc84054c78e0a9a71 +23, 0xae59034726be61a8 +24, 0xa6a5f21de983654d +25, 0x3b633acf572009da +26, 0x6a0884f347ab54c8 +27, 0x7a907ebe9adcab50 +28, 0xbe779be53d7b8d4a +29, 0xf5976e8c69b9dcd1 +30, 0x1d8302f114699e11 +31, 0x7d37e43042c038a0 +32, 0x2cc1d4edc2a40f35 +33, 0x83e3347bb2d581f1 +34, 0x253f8698651a844d +35, 0x4312dea0dd4e32f6 +36, 0x10f106439964ea3a +37, 0x810eb374844868cc +38, 0x366342a54b1978cc +39, 0x9fb39b13aaddfb5e +40, 0xdb91fd0d9482bed7 +41, 0x89f6ea4ca9c68204 +42, 0x146b31ccca461792 +43, 0x203fd9724deb2486 +44, 0x58a84f23748e25cb +45, 0x2f20eb6aeb94e88 +46, 0x14d3581460e473c +47, 0xad5bd0d25f37d047 +48, 0x1cf88fa16de258b2 +49, 0x3bcab6485b7a341 +50, 0xb2433b37f227d90c +51, 0x2cffd7e0a8360cc8 +52, 0x5d2eeff7c9ebc847 +53, 0x6fd7c7ae23f9f64b +54, 0x381650b2d00f175d +55, 0x9d93edcedc873cae +56, 0x56e369a033d4cb49 +57, 0x7547997116a3bac +58, 0x11debaa897fd4665 +59, 0xdf799d2b73bd6fb8 +60, 0x3747d299c66624d +61, 0xac9346701afd0cfa +62, 0xac90e150fa13c7bf +63, 0x85c56ad2248c2871 +64, 0xdea66bf35c45f195 +65, 0x59cf910ea079fb74 +66, 0x2f841bb782274586 +67, 0x9814df4384d92bd9 +68, 0x15bc70824be09925 +69, 0x16d4d0524c0503a3 +70, 0xf04ea249135c0cc7 +71, 0xa707ab509b7e3032 +72, 0x465459efa869e372 +73, 0x64cbf70a783fab67 +74, 0x36b3541a14ca8ed7 +75, 0x9a4dfae8f4c596bf +76, 0x11d9a04224281be3 +77, 0xe09bbe6d5e98ec32 +78, 0xa6c60d908973aa0d +79, 0x7c524c57dd5915c8 +80, 0xa810c170b27f1fdc +81, 0xce5d409819621583 +82, 0xfe2ee3d5332a3525 +83, 0x162fb7c8b32045eb +84, 0x4a3327156b0b2d83 +85, 0x808d0282f971064 +86, 0x2e6f04cf5ed27e60 +87, 0xaf6800699cca67a9 +88, 0xc7590aae7244c3bf +89, 0x7824345f4713f5f9 +90, 0x8f713505f8fd059b +91, 0x3d5b5b9bb6b1e80e +92, 0x8674f45e5dc40d79 +93, 0xcb1e36846aa14773 +94, 0xe0ae45b2b9b778c1 +95, 0xd7254ce931eefcfb +96, 0xef34e15e4f55ac0a +97, 0xf17cc0ba15a99bc4 +98, 0x77bb0f7ffe7b31f1 +99, 0x6ee86438d2e71d38 +100, 0x584890f86829a455 +101, 0x7baf0d8d30ba70fe +102, 0xb1ac8f326b8403ae +103, 0xcc1963435c874ba7 +104, 0x9c483b953d1334ce +105, 0xc0924bcbf3e10941 +106, 0x21bcc581558717b1 +107, 0x2c5ad1623f8d292b +108, 0xa8ea110f6124557e +109, 0x15f24a6c5c4c591 +110, 0x40fe0d9cd7629126 +111, 0xcfe8f2b3b081484d +112, 0x891383f4b4cac284 +113, 0x76f2fcdef7fa845 +114, 0x4edd12133aed0584 +115, 0xd53c06d12308873d +116, 0xf7f22882c17f86bf +117, 0xfbaa4aad72f35e10 +118, 0x627610da2e3c0cc3 +119, 0x582b16a143634d9a +120, 0x9b4a7f69ed38f4a0 +121, 0x2df694974d1e1cbe +122, 0xe5be6eaafed5d4b +123, 0xc48e2a288ad6605e +124, 0xbcb088149ce27c2b +125, 0x3cb6a7fb06ceecbe +126, 0x516735fff3b9e3ac +127, 0x5cbafc551ee5008d +128, 0xee27d1ab855c5fd5 +129, 0xc99fb341f6baf846 +130, 0x7ad8891b92058e6d +131, 0xf50310d03c1ac6c7 +132, 0x947e281d998cbd3e +133, 0x1d4d94a93824fe80 +134, 0x5568b77289e7ee73 +135, 0x7d82d1b2b41e3c8b +136, 0x1af462c7abc787b +137, 0xcfd8dfe80bfae1ef +138, 0xd314caeb723a63ea +139, 0x1c63ddcfc1145429 +140, 0x3801b7cc6cbf2437 +141, 0xc327d5b9fdafddd3 +142, 0xe140278430ca3c78 +143, 0x4d0345a685cb6ef8 +144, 0x47640dc86e261ff9 +145, 0xab817f158523ebf4 +146, 0x37c51e35fbe65a6b +147, 0xab090f475d30a178 +148, 0x4d3ec225bf599fc1 +149, 0xefd517b0041679b1 +150, 0x20ad50bca4da32c5 +151, 0x75e1f7cd07fad86d +152, 0x348cf781ee655f4b +153, 0x9375f0e5ffc2d2ec +154, 0x7689082fd5f7279c +155, 0x633e56f763561e77 +156, 0x9d1752d70861f9fd +157, 0xa3c994b4e70b0b0f +158, 0xabf7276a58701b88 +159, 0xbfa18d1a0540d000 +160, 0xc6a28a2475646d26 +161, 0x7cdf108583f65085 +162, 0x82dcefb9f32104be +163, 0xc6baadd0adc6b446 +164, 0x7a63cff01075b1b4 +165, 0x67ac62e575c89919 +166, 0x96fa4320a0942035 +167, 0xc4658859385b325f +168, 0xde22c17ff47808f6 +169, 0xbb952c4d89e2f2ec +170, 0x638251fbc55bdc37 +171, 0x38918b307a03b3ea +172, 0xccb60f2cedbb570b +173, 0x3c06f4086a28f012 +174, 0x4e8d238388986e33 +175, 0x1760b7793514a143 +176, 0xa3f924efe49ee7d6 +177, 0xaf6be2dbaebc0bdf +178, 0x6782682090dffe09 +179, 0xb63a4d90d848e8ef +180, 0x5f649c7eaf4c54c5 +181, 0xbe57582426a085ba +182, 0xb5dd825aa52fb76d +183, 0x74cb4e6ca4039617 +184, 0x382e578bf0a49588 +185, 0xc043e8ea6e1dcdae +186, 0xf902addd5c04fa7c +187, 0xf3337994612528db +188, 0x4e8fd48d6d15b4e6 +189, 0x7190a509927c07ab +190, 0x864c2dee5b7108ae +191, 0xbb9972ddc196f467 +192, 0x1ea02ab3ca10a448 +193, 0xe50a8ffde35ddef9 +194, 0x7bd2f59a67183541 +195, 0x5a940b30d8fcd27a +196, 0x82b4cea62623d4d3 +197, 0x6fbda76d4afef445 +198, 0x8b1f6880f418328e +199, 0x8b69a025c72c54b7 +200, 0xb71e0f3986a3835f +201, 0xa4a7ddb8b9816825 +202, 0x945dcda28228b1d8 +203, 0xb471abf2f8044d72 +204, 0xf07d4af64742b1ba +205, 0xfca5190bc4dd6a2a +206, 0xd681497262e11bc5 +207, 0xbe95d5f00c577028 +208, 0x56313439fd8bde19 +209, 0x3f3d9ac9b5ee6522 +210, 0x7b8d457dd2b49bbe +211, 0xe76b5747885d214b +212, 0xa8a695b3deb493ea +213, 0x5292446548c95d71 +214, 0xbf5cdf0d436412df +215, 0x7936abaed779d28d +216, 0x659c6e8073b3a06d +217, 0x86c9ff28f5543b71 +218, 0x6faa748445a99146 +219, 0xdcc1e6ab57904fd7 +220, 0x770bd61233addc5f +221, 0x16963e041e46d94f +222, 0x158e6cb2934157ac +223, 0xb65088a8fd246441 +224, 0x2b12ced6ce8a68c3 +225, 0x59a18d02cd6082b3 +226, 0x4ddbc318cb5488ee +227, 0x3d4cf520b3ed20a1 +228, 0x7028b3a92e2b292d +229, 0xf141da264a250e4d +230, 0x9788d53e86041c37 +231, 0x1bb91238a7c97dbf +232, 0x81953d0ddb634309 +233, 0xfa39ccfe14d2d46 +234, 0xf7c7861c9b7e8399 +235, 0x18d27ca50d9dc249 +236, 0x258dfdf38510d0d9 +237, 0x9e72d8af910ea76f +238, 0x4f8ef24b96de50ad +239, 0xb9d9c12297e03dc9 +240, 0x91994e41b4a1929c +241, 0x8defa79b2ccc83b9 +242, 0x948566748706dac5 +243, 0x7b0454946e70e4cf +244, 0x340b7cb298c70ed7 +245, 0x6602005330cebd95 +246, 0xf71cb803aa61f722 +247, 0x4683fb07fc70ae8a +248, 0xc6db9f0c4de3ed88 +249, 0x3e8dfae2a593cef9 +250, 0x615f7c38e3862b33 +251, 0x676c7996550d857 +252, 0xc6d520d54a5c266a +253, 0x202b1e8eef14aa2e +254, 0xa3a84891a27a582 +255, 0x84dbee451658d47f +256, 0x254c7cd97e777e3a +257, 0xf50b6e977f0eba50 +258, 0x2898b1d3062a4798 +259, 0x4096f7cbbb019773 +260, 0x9fb8e75548062c50 +261, 0x4647071e5ca318ec +262, 0x2b4750bdb3b3b01 +263, 0x88ac41cc69a39786 +264, 0x705e25476ef46fa3 +265, 0xc0c1db19884a48a6 +266, 0x1364c0afdbb465e5 +267, 0x58e98534701272a6 +268, 0x746a5ea9701517c0 +269, 0x523a70bc6b300b67 +270, 0x9b1c098eda8564ad +271, 0xfbaeb28d3637067f +272, 0xddd9a13551fdba65 +273, 0x56461a670559e832 +274, 0xab4fd79be85570ad +275, 0xd4b691ecaff8ca55 +276, 0x11a4495939e7f004 +277, 0x40d069d19477eb47 +278, 0xe790783d285cd81e +279, 0xde8218b16d935bc7 +280, 0x2635e8c65cd4182d +281, 0xeae402623e3454 +282, 0x9f99c833184e0279 +283, 0x3d0f79a0d52d84e7 +284, 0xc1f8edb10c625b90 +285, 0x9b4546363d1f0489 +286, 0x98d86d0b1212a282 +287, 0x386b53863161200d +288, 0xbe1165c7fe48a135 +289, 0xb9658b04dbbfdc8c +290, 0xcea14eddfe84d71a +291, 0x55d03298be74abe7 +292, 0x5be3b50d961ffd7e +293, 0xc76b1045dc4b78e1 +294, 0x7830e3ff3f6c3d4c +295, 0xb617adb36ca3729 +296, 0x4a51bdb194f14aa9 +297, 0x246024e54e6b682a +298, 0x33d42fc9c6d33083 +299, 0xadccba149f31e1d +300, 0x5183e66b9002f8b +301, 0x70eb2416404d51b7 +302, 0x26c25eb225535351 +303, 0xbc2d5b0d23076561 +304, 0x5823019ddead1da +305, 0x85cfa109fca69f62 +306, 0x26017933e7e1efd9 +307, 0x3ec7be9a32212753 +308, 0x697e8a0697cd6f60 +309, 0x44735f6cca03920f +310, 0x8cc655eb94ee212e +311, 0x8b8b74eba84929a0 +312, 0x7708ccedd0c98c80 +313, 0x1b6f21f19777cbe1 +314, 0x363e564bd5fadedb +315, 0x5921543a641591fe +316, 0xc390786d68ea8a1b +317, 0x9b293138dc033fca +318, 0x45447ca8dc843345 +319, 0xee6ef6755bc49c5e +320, 0x70a3a1f5163c3be5 +321, 0xf05e25448b6343b0 +322, 0x4739f4f8717b7e69 +323, 0xb006141975bf957 +324, 0x31874a91b707f452 +325, 0x3a07f2c90bae2869 +326, 0xb73dae5499a55c5e +327, 0x489070893bb51575 +328, 0x7129acf423940575 +329, 0x38c41f4b90130972 +330, 0xc5260ca65f5a84a1 +331, 0x6e76194f39563932 +332, 0x62ca1f9ca3de3ca6 +333, 0xb4a97874e640853f +334, 0x38ed0f71e311cc02 +335, 0xde183b81099e8f47 +336, 0x9bb8bf8e6694346 +337, 0xd15497b6bf81e0f2 +338, 0xaaae52536c00111 +339, 0x4e4e60d1435aaafd +340, 0x5a15512e5d6ea721 +341, 0xff0f1ffabfc6664f +342, 0xba3ffcedc5f97fec +343, 0xef87f391c0c6bfb6 +344, 0x4a888c5d31eb0f98 +345, 0x559a3fbfd7946e95 +346, 0xe45b44a0db5a9bad +347, 0x9457898964190af1 +348, 0xd9357dfaab76cd9e +349, 0xa60e907178d965a1 +350, 0x76b2dc3032dc2f4a +351, 0x13549b9c2802120 +352, 0x8656b965a66a1800 +353, 0x16802e6e22456a23 +354, 0x23b62edc60efaa9 +355, 0x6832a366e1e4ea3b +356, 0x46b1b41093ff2b1e +357, 0x55c857128143f219 +358, 0x7fc35ddf5e138200 +359, 0x790abe78be67467e +360, 0xa4446fc08babd466 +361, 0xc23d70327999b855 +362, 0x2e019d1597148196 +363, 0xfefd98e560403ab8 +364, 0xbe5f0a33da330d58 +365, 0x3078a4e9d43ca395 +366, 0x511bfedd6f12f2b3 +367, 0x8bc138e335be987c +368, 0x24640f803465716d +369, 0xf6530b04d0bd618f +370, 0x9b7833e5aa782716 +371, 0x778cd35aea5841b1 +372, 0xecea3c458cefbc60 +373, 0x5107ae83fc527f46 +374, 0x278ad83d44bd2d1a +375, 0x7014a382295aeb16 +376, 0xf326dd762048743f +377, 0x858633d56279e553 +378, 0x76408154085f01bc +379, 0x3e77d3364d02e746 +380, 0x2f26cea26cadd50b +381, 0x6d6846a4ecb84273 +382, 0x4847e96f2df5f76 +383, 0x5a8610f46e13ff61 +384, 0x4e7a7cac403e10dd +385, 0x754bdf2e20c7bc90 +386, 0x8bdd80e6c51bd0be +387, 0x61c655fae2b4bc52 +388, 0x60873ef48e3d2f03 +389, 0x9d7d8d3698a0b4a4 +390, 0xdf48e9c355cd5d4b +391, 0x69ecf03e20be99ac +392, 0xc1a0c5a339bd1815 +393, 0x2e3263a6a3adccb +394, 0x23557459719adbdc +395, 0xd1b709a3b330e5a +396, 0xade5ab00a5d88b9d +397, 0x69a6bd644120cfad +398, 0x40187ecceee92342 +399, 0x1c41964ba1ac78da +400, 0x9ac5c51cbecabe67 +401, 0xbdc075781cf36d55 +402, 0xeaf5a32246ded56 +403, 0xcda0b67e39c0fb71 +404, 0x4839ee456ef7cc95 +405, 0xf17092fdd41d5658 +406, 0x2b5d422e60ae3253 +407, 0x3effe71102008551 +408, 0x20a47108e83934b7 +409, 0xd02da65fe768a88f +410, 0xeb046bd56afa4026 +411, 0x70c0509c08e0fbe0 +412, 0x1d35c38d4f8bac6c +413, 0x9aa8eb6466f392e0 +414, 0x587bd4a430740f30 +415, 0x82978fe4bad4195 +416, 0xdc4ebc4c0feb50ab +417, 0xd3b7164d0240c06f +418, 0x6e2ad6e5a5003a63 +419, 0xa24b430e2ee6b59c +420, 0x2905f49fd5073094 +421, 0x5f209e4de03aa941 +422, 0x57b7da3e0bedb1dc +423, 0x5e054018875b01f5 +424, 0xb2f2da6145658db3 +425, 0xbd9c94a69a8eb651 +426, 0x9c5f9a07cd6ac749 +427, 0x2296c4af4d529c38 +428, 0x522ed800fafdefab +429, 0xe2a447ced0c66791 +430, 0x937f10d45e455fef +431, 0xc882987d9e29a24 +432, 0x4610bfd6a247ee1a +433, 0x562ba3e50870059 +434, 0x59d8d58793602189 +435, 0xfe9a606e3e34abe +436, 0x6825f7932a5e9282 +437, 0xe77f7061bab476ad +438, 0xbf42001da340ace3 +439, 0x9c3e9230f5e47960 +440, 0x2c0f700d96d5ad58 +441, 0x330048b7cd18f1f9 +442, 0xffc08785eca5cca9 +443, 0xb5879046915f07a5 +444, 0xef51fe26f83c988e +445, 0xfa4c2968e7881a9a +446, 0xc0a9744455a4aad +447, 0xbd2ad686d6313928 +448, 0x6b9f0984c127682a +449, 0xc9aaa00a5da59ed8 +450, 0x762a0c4b98980dbf +451, 0x52d1a2393d3ca2d1 +452, 0x1e9308f2861db15c +453, 0xe7b3c74fe4b4a844 +454, 0x485e15704a7fc594 +455, 0x9e7f67ea44c221f6 +456, 0xbab9ad47fde916e0 +457, 0x50e383912b7fc1f4 +458, 0xaad63db8abcef62d +459, 0xc2f0c5699f47f013 +460, 0xee15b36ada826812 +461, 0x2a1b1cf1e1777142 +462, 0x8adb03ede79e937d +463, 0xf14105ef65643bf3 +464, 0x752bbaefc374a3c7 +465, 0xa4980a08a5a21d23 +466, 0x418a1c05194b2db7 +467, 0xdd6ff32efe1c3cd6 +468, 0x272473ed1f0d3aa2 +469, 0x1e7fdebadabe6c06 +470, 0xd1baa90c17b3842f +471, 0xd3d3a778e9c8404a +472, 0x781ae7fda49fa1a0 +473, 0x61c44fdbdacc672d +474, 0x6d447d0a1404f257 +475, 0x9303e8bdfbfb894d +476, 0x3b3482cdec016244 +477, 0xb149bf245d062e7b +478, 0x96f8d54b14cf992d +479, 0x4741549a01f8c3d0 +480, 0x48270811b2992af +481, 0x7b58f175cd25d147 +482, 0x8f19a840b56f4be9 +483, 0x84a77f43c0951a93 +484, 0x34e1a69381f0c374 +485, 0xb158383c9b4040f +486, 0x372f1abc7cf3a9fa +487, 0x5439819a84571763 +488, 0xabf8515e9084e2fa +489, 0xb02312b9387ff99 +490, 0x238a85bb47a68b12 +491, 0x2068cb83857c49bb +492, 0xc6170e743083664c +493, 0x745cf8470bcb8467 +494, 0xe3a759a301670300 +495, 0x292c7686ad3e67da +496, 0x359efedaff192a45 +497, 0x511f2c31a2d8c475 +498, 0x97fd041bf21c20b3 +499, 0x25ef1fe841b7b3f6 +500, 0xbb71739e656f262d +501, 0x2729b0e989b6b7b8 +502, 0xd2142702ec7dbabf +503, 0x7008decd2488ee3f +504, 0x69daa95e303298d7 +505, 0xc35eca4efb8baa5a +506, 0xf3f16d261cec3b6c +507, 0x22371c1d75396bd3 +508, 0x7aefa08eccae857e +509, 0x255b493c5e3c2a2f +510, 0x779474a077d34241 +511, 0x5199c42686bea241 +512, 0x16c83931e293b8d3 +513, 0xa57fe8db8c0302c7 +514, 0xd7ace619e5312eb1 +515, 0x8740f013306d217c +516, 0xb6a1ad5e29f4d453 +517, 0x31abf7c964688597 +518, 0xbc3d791daed71e7 +519, 0x31ee4ca67b7056ed +520, 0x1ab5416bfe290ea3 +521, 0x93db416f6d3b843a +522, 0xed83bbe5b1dd2fed +523, 0xece38271470d9b6d +524, 0x3a620f42663cd8ae +525, 0x50c87e02acafee5d +526, 0xcabeb8bedbc6dab5 +527, 0x2880a6d09970c729 +528, 0x4aba5dd3bfc81bc +529, 0xaba54edf41080cec +530, 0xb86bb916fc85a169 +531, 0x4c41de87bc79d8ca +532, 0xcce2a202622945fe +533, 0x513f086fad94c107 +534, 0x18b3960c11f8cc96 +535, 0x2f0d1cfd1896e236 +536, 0x1702ae3880d79b15 +537, 0x88923749029ae81 +538, 0x84810d4bdec668eb +539, 0xf85b0a123f4fc68d +540, 0x93efd68974b6e4d1 +541, 0x5d16d6d993a071c9 +542, 0x94436858f94ca43b +543, 0xb3dbb9ed0cb180b6 +544, 0x6447030a010b8c99 +545, 0xd7224897c62925d8 +546, 0xb0c13c1d50605d3a +547, 0xdff02c7cb9d45f30 +548, 0xe8103179f983570d +549, 0xbc552037d6d0a24e +550, 0x775e500b01486b0d +551, 0x2050ac632c694dd6 +552, 0x218910387c4d7ae7 +553, 0xf83e8b68ff885d5d +554, 0xe3374ec25fca51a3 +555, 0xfa750ffa3a60f3af +556, 0x29ee40ba6df5592e +557, 0x70e21a68f48260d2 +558, 0x3805ca72cd40886e +559, 0x2f23e73f8eabf062 +560, 0x2296f80cdf6531ae +561, 0x903099ed968db43a +562, 0xf044445cf9f2929f +563, 0xcd47fdc2de1b7a1 +564, 0xaab1cbd4f849da99 +565, 0x5fc990688da01acb +566, 0xa9cee52ea7dab392 +567, 0xecefc3a4349283a8 +568, 0xdd6b572972e3fafc +569, 0xc1f0b1a2ffb155da +570, 0xc30d53fc17bd25c8 +571, 0x8afa89c77834db28 +572, 0x5569a596fb32896c +573, 0x36f207fc8df3e3d4 +574, 0x57c2bd58517d81db +575, 0xb524693e73d0061c +576, 0xb69f6eb233f5c48b +577, 0x4f0fb23cab8dc695 +578, 0x492c1ad0a48df8df +579, 0xf6dcc348ec8dec1f +580, 0xa4d8708d6eb2e262 +581, 0x4c2072c2c9766ff1 +582, 0xa9bf27c4304875f0 +583, 0xfc8fb8066d4f9ae2 +584, 0x188095f6235fec3c +585, 0x1d8227a2938c2864 +586, 0x89ea50c599010378 +587, 0xcac86df0a7c6d56d +588, 0x47a8c5df84c7d78 +589, 0xe607ae24ea228bfa +590, 0x36624a7996efe104 +591, 0x5d72881c1227d810 +592, 0x78694a6750374c8 +593, 0x7b9a217d4ab5ff45 +594, 0xd53e5d6f7504becc +595, 0x197a72d3f4889a0e +596, 0xfdc70c4755a8df36 +597, 0xd0fda83748c77f74 +598, 0x7ddc919ac9d6dcc9 +599, 0x785c810a6a2dc08b +600, 0xba4be83e7e36896c +601, 0x379d6fe80cf2bffe +602, 0x74cae2dabc429206 +603, 0x1efac32d5d34c917 +604, 0x3cb64e2f98d36e70 +605, 0xc0a7c3cdc3c60aa7 +606, 0x699dfadd38790ebe +607, 0x4861e61b3ecfbeac +608, 0x531744826c345baa +609, 0x5ec26427ad450cba +610, 0xf2c1741479abdcae +611, 0xe9328a78b2595458 +612, 0x30cd1bdf087acd7f +613, 0x7491ced4e009adbe +614, 0xdcd942df1e2e7023 +615, 0xfe63f01689fee35 +616, 0x80282dfe5eaedc42 +617, 0x6ecdea86495f8427 +618, 0xe0adfdd5e9ed31c3 +619, 0xf32bd2a7418127e +620, 0x8aabba078db6ee2 +621, 0xa8a8e60499145aca +622, 0xf76b086ac4e8a0f2 +623, 0x6e55b3c452ff27f8 +624, 0xe18fa7cd025a71bf +625, 0xeed7b685fde0fa25 +626, 0xba9b6c95867fa721 +627, 0x4c2603bc69de2df2 +628, 0xaac87eee1b58cd66 +629, 0x3c9af6656e01282c +630, 0x2dfa05ce8ff476b6 +631, 0xeae9143fcf92f23d +632, 0x3f0699f631be3bc8 +633, 0xa0f5f79f2492bd67 +634, 0x59c47722388131ed +635, 0x5f6e9d2941cef1de +636, 0xe9ad915c09788b7b +637, 0x92c6d37e4f9482f5 +638, 0x57d301b7fdadd911 +639, 0x7e952d23d2a8443 +640, 0xbb2fa5e0704b3871 +641, 0xe5642199be36e2d5 +642, 0x5020b60d54358291 +643, 0xa0b6317ec3f60343 +644, 0xb57b08b99540bc5c +645, 0x21f1890adc997a88 +646, 0xfcf824200dd9da2d +647, 0x8146293d83d425d1 +648, 0xdadfbf5fbb99d420 +649, 0x1eb9bbc5e6482b7d +650, 0xd40ff44f1bbd0f1c +651, 0xa9f948ba2d08afa5 +652, 0x638cc07c5301e601 +653, 0x1f984baa606e14e8 +654, 0x44e153671081f398 +655, 0xb17882eeb1d77a5d +656, 0x5fd8dbee995f14c +657, 0xff3533e87f81b7fe +658, 0x2f44124293c49795 +659, 0x3bf6b51e9360248 +660, 0x72d615edf1436371 +661, 0x8fc5cf4a38adab9d +662, 0xfa517e9022078374 +663, 0xf356733f3e26f4d8 +664, 0x20ea099cdc6aad40 +665, 0xe15b977deb37637d +666, 0xcc85601b89dae88d +667, 0x5768c62f8dd4905c +668, 0xa43cc632b4e56ea +669, 0xc4240cf980e82458 +670, 0xb194e8ffb4b3eeb6 +671, 0xee753cf2219c5fa1 +672, 0xfe2500192181d44d +673, 0x2d03d7d6493dd821 +674, 0xff0e787bb98e7f9b +675, 0xa05cf8d3bd810ce7 +676, 0x718d5d6dcbbdcd65 +677, 0x8d0b5343a06931c +678, 0xae3a00a932e7eaf9 +679, 0x7ed3d8f18f983e18 +680, 0x3bb778ee466dc143 +681, 0x711c685c4e9062c0 +682, 0x104c3af5d7ac9834 +683, 0x17bdbb671fb5d5cf +684, 0xabf26caead4d2292 +685, 0xa45f02866467c005 +686, 0xf3769a32dc945d2d +687, 0xe78d0007f6aabb66 +688, 0x34b60be4acbd8d4b +689, 0x58c0b04b69359084 +690, 0x3a8bb354c212b1 +691, 0x6b82a8f3d70058d5 +692, 0x405bdef80a276a4a +693, 0xe20ca40ee9195cad +694, 0xf5dd96ba2446fefd +695, 0xc1e180c55fe55e3c +696, 0xa329caf6daa952b3 +697, 0xb4809dd0c84a6b0a +698, 0xd27f82661070cee7 +699, 0xa7121f15ee2b0d8a +700, 0x4bdaea70d6b34583 +701, 0xe821dc2f310f7a49 +702, 0x4c00a5a68e76f647 +703, 0x331065b064a2d5ea +704, 0xac0c2ce3dc04fa37 +705, 0x56b32b37b8229008 +706, 0xe757cdb51534fcfa +707, 0xd3ff183576b2fad7 +708, 0x179e1f4190f197a7 +709, 0xf874c626a7c9aae5 +710, 0xd58514ffc37c80e4 +711, 0xc65de31d33fa7fd3 +712, 0x6f6637052025769b +713, 0xca1c6bdadb519cc0 +714, 0xd1f3534cde37828a +715, 0xc858c339eee4830a +716, 0x2371eacc215e02f4 +717, 0x84e5022db85bbbe9 +718, 0x5f71c50bba48610e +719, 0xe420192dad9c323f +720, 0x2889342721fca003 +721, 0x83e64f63334f501d +722, 0xac2617172953f2c +723, 0xfa1f78d8433938ff +724, 0x5578382760051462 +725, 0x375d7a2e3b90af16 +726, 0xb93ff44e6c07552d +727, 0xded1d5ad811e818c +728, 0x7cf256b3b29e3a8c +729, 0x78d581b8e7bf95e8 +730, 0x5b69192f2caa6ad3 +731, 0xa9e25855a52de3ce +732, 0x69d8e8fc45cc188d +733, 0x5dd012c139ad347d +734, 0xfcb01c07b77db606 +735, 0x56253e36ab3d1cce +736, 0x1181edbb3ea2192 +737, 0x325bef47ff19a08d +738, 0xd3e231ceb27e5f7 +739, 0x8e819dd2de7956d2 +740, 0x34a9689fe6f84a51 +741, 0x3e4eeb719a9c2927 +742, 0x5c3b3440581d0aaf +743, 0x57caf51897d7c920 +744, 0xec6a458130464b40 +745, 0xe98f044e0da40e9b +746, 0xbe38662020eeb8e7 +747, 0x7b8c407c632724ae +748, 0x16c7cfa97b33a544 +749, 0xd23359e2e978ae5a +750, 0x4fdba458250933dd +751, 0x3c9e0713cfe616ba +752, 0x6f0df87b13163b42 +753, 0xc460902cb852cc97 +754, 0x289df8fefd6b0bce +755, 0x4ac2a2a1c3fb8029 +756, 0x2fc3e24d8b68eef7 +757, 0x34564386a59aab9a +758, 0x31047391ebd67ce4 +759, 0x6c23d070a0564d41 +760, 0xba6387b2b72545f7 +761, 0xcdcf1008058387af +762, 0xc9308fa98db05192 +763, 0xdbdbb5abd01a9d84 +764, 0x937088275c7804ab +765, 0x6f6accfefe34ee81 +766, 0x5c33c74c49cfdb2c +767, 0x5e1a771edfb92bd3 +768, 0x6e89b009069ecae7 +769, 0x34d64e17ec0e8968 +770, 0x841203d0cde0c330 +771, 0x7642cc9d7eb9e9cb +772, 0xca01d2e8c128b97e +773, 0x5b8390617b3304ab +774, 0x52ec4ed10de1eb2d +775, 0xb90f288b9616f237 +776, 0x5bd43cd49617b2e2 +777, 0x1a53e21d25230596 +778, 0x36ccd15207a21cd6 +779, 0xc8263d780618fd3c +780, 0x6eb520598c6ce1cb +781, 0x493c99a3b341564f +782, 0xab999e9c5aa8764f +783, 0xab2fa4ceaba84b +784, 0xbbd2f17e5cb2331b +785, 0xc8b4d377c0cc4e81 +786, 0x31f71a6e165c4b1e +787, 0xd1011e55fb3addaa +788, 0x5f7ec34728dfa59 +789, 0x2aef59e60a84eb0f +790, 0x5dde6f09aec9ad5f +791, 0x968c6cdbc0ef0438 +792, 0x1957133afa15b13a +793, 0xbaf28f27573a64c2 +794, 0xc6f6ddd543ebf862 +795, 0xdd7534315ec9ae1e +796, 0xd2b80cd2758dd3b +797, 0xa38c3da00cc81538 +798, 0x15c95b82d3f9b0f9 +799, 0x6704930287ce2571 +800, 0x9c40cc2f6f4ecb0c +801, 0xc8de91f50b22e94e +802, 0x39272e8fddbfdf0a +803, 0x879e0aa810a117d +804, 0xa312fff4e9e5f3bd +805, 0x10dd747f2835dfec +806, 0xeb8466db7171cdae +807, 0xaa808d87b9ad040a +808, 0xab4d2229a329243a +809, 0x7c622f70d46f789c +810, 0x5d41cef5965b2a8e +811, 0xce97ec4702410d99 +812, 0x5beba2812c91211b +813, 0xf134b46c93a3fec7 +814, 0x76401d5630127226 +815, 0xc55fc9d9eacd4ec1 +816, 0xaec8cefaa12f813f +817, 0x2f845dcfd7b00722 +818, 0x3380ab4c20885921 +819, 0xdb68ad2597691b74 +820, 0x8a7e4951455f563f +821, 0x2372d007ed761c53 +822, 0xcab691907714c4f1 +823, 0x16bc31d6f3abec1a +824, 0x7dff639fbcf1824 +825, 0x6666985fbcff543d +826, 0xb618948e3d8e6d0c +827, 0x77b87837c794e068 +828, 0xcd48288d54fcb5a8 +829, 0x47a773ed6ae30dc3 +830, 0xba85ae44e203c942 +831, 0xa7a7b21791a25b2d +832, 0x4029dd92e63f19e0 +833, 0xc2ad66ab85e7d5aa +834, 0xa0f237c96fdab0db +835, 0xffefb0ab1ca18ed +836, 0x90cb4500785fd7d5 +837, 0xa7dd3120f4876435 +838, 0x53f7872624694300 +839, 0xea111326ff0040d9 +840, 0x5f83cb4cce40c83b +841, 0x918e04936c3b504d +842, 0x87a8db4c0e15e87c +843, 0x7cff39da6a0dedd0 +844, 0x36f7de2037f85381 +845, 0xd1d8d94022a1e9a7 +846, 0x2c9930127dc33ec9 +847, 0x6cb4719dcd0101c6 +848, 0xc01868cde76935f7 +849, 0x6b86f2ec1ab50143 +850, 0x68af607d8d94ae61 +851, 0xe216c5b95feedf34 +852, 0x4b866bd91efe2e4b +853, 0x4bff79df08f92c99 +854, 0x6ff664ea806acfd1 +855, 0x7fce0b3f9ece39bc +856, 0x29bc90b59cb3db97 +857, 0x833c4b419198607d +858, 0xf3573e36ca4d4768 +859, 0x50d71c0a3c2a3fa8 +860, 0xd754591aea2017e7 +861, 0x3f9126f1ee1ebf3 +862, 0xe775d7f4b1e43de8 +863, 0xe93d51628c263060 +864, 0x83e77f6fb32d6d82 +865, 0x43dd7eef823408e4 +866, 0x1c843c2c90180662 +867, 0xe924dafb9a16066b +868, 0x6af3ee96e7b7fbd9 +869, 0x94d5c4f37befcd1f +870, 0x40ffb04bedef4236 +871, 0x71c17bbc20e553e +872, 0x101f7a0a6208729f +873, 0x5ca34570cf923548 +874, 0x8e3139db2e96e814 +875, 0x3ab96d96263d048d +876, 0x97f3c0bbc6755c3c +877, 0x31fc72daedaef3dc +878, 0x71f8d7855d10789b +879, 0xce6dc97b4662333b +880, 0xfddc2aabd342bc61 +881, 0xefbd4007ff8c7d2e +882, 0xf72cd6c689ef8758 +883, 0x932c8b0c0e755137 +884, 0x94cc4dedd58ff69 +885, 0xde4dfd6890535979 +886, 0xdb00dcd2dcb4a50a +887, 0xb0466240b4548107 +888, 0x9cb9264c7b90d1a3 +889, 0x357e378e9be5766b +890, 0x6e0316ef03367bbf +891, 0x201ea18839544ca +892, 0x803ff3406be5f338 +893, 0xf9d5e82fd4144bb2 +894, 0x1b6b88ca701e9f47 +895, 0xd1fe5ab8e1f89cc0 +896, 0x14171fe176c4bece +897, 0x887948bdef78beaa +898, 0x80449ddc3eb9b977 +899, 0x5f4e1f900fb4bcf3 +900, 0xbe30f8701909f8e2 +901, 0xd1f2a2fb5503306d +902, 0x6b1c77238dc23803 +903, 0x102156a6c9860f66 +904, 0x4cd446e099edf4c1 +905, 0xc79ac6cbc911f33b +906, 0x3ee096ffe3384f1c +907, 0xb58f83b18a306dc7 +908, 0x9f76582141de56b2 +909, 0x9ddfa85e02c13866 +910, 0x4d9a19d4ce90a543 +911, 0xbf81ab39fd17d376 +912, 0x5327e5054c6a74f1 +913, 0xd5062dd31db1a9b7 +914, 0x645853735527edc +915, 0x485393967f91af08 +916, 0xeff9667dcf77ca68 +917, 0xd012313f5fbec464 +918, 0xbeae35bdfae55144 +919, 0x302c41ebac8444a0 +920, 0x9ccdb6c2fe58fba8 +921, 0x567753af68ed23f8 +922, 0xff90f790e43efec3 +923, 0x970cc756fb799696 +924, 0xe59239d1c44915 +925, 0x4d2d189fb3941f05 +926, 0x96f23085db165a9c +927, 0xa1202dec7a37b1a5 +928, 0xc0c1ee74bcd7dc1a +929, 0x9edcf2048b30333a +930, 0xd848588ba7e865fb +931, 0x8d9f0897317cab40 +932, 0x67b96f15e25924fb +933, 0xefc8d8536619ee42 +934, 0xf3f621d22bdde0c2 +935, 0x68610a0de862ae32 +936, 0xa22ca5142de24cbd +937, 0x8815452f4e6b4801 +938, 0x4e9c1b607b2750e5 +939, 0x19b3c09ba6fc9b25 +940, 0x9b2543c8836780ac +941, 0xe702b8f950e56431 +942, 0xb357cc329cac3917 +943, 0x387bf86a17a31e08 +944, 0x9940b983d331b163 +945, 0xf5d89d7fe9095e18 +946, 0x4362682329e5c4d1 +947, 0xd2132573f6ae7b42 +948, 0xc0a5849e23a61606 +949, 0xdadbddf47265bc02 +950, 0x1b96f00339a705f7 +951, 0x94e6642329288913 +952, 0x825ab3f10e6d330b +953, 0x1a1c31ac9d883ea0 +954, 0xb49076b7155c6f47 +955, 0x920cf3085dfe3ccb +956, 0x9743407c9f28e825 +957, 0x6ce8a28622402719 +958, 0xce2fe67e06baf8a6 +959, 0x3a16b34784ecf5e6 +960, 0x140467cc1d162a0c +961, 0x32d4772692ab625 +962, 0xa4f4b28562f43336 +963, 0x885b4335457bd84a +964, 0x499d3ed26c87ad8a +965, 0xc7328bcedb9a545e +966, 0xc6dd76a6cbf5d2b2 +967, 0xba9c22be404ee1aa +968, 0x70e6aee45f23521d +969, 0x61e03a798593c177 +970, 0x171671f809c68213 +971, 0x28d54872fc1d914c +972, 0x43c2fcd9bd098b53 +973, 0x172ad4c4a98b9d37 +974, 0x330860c9460f2516 +975, 0x49547f472df984f4 +976, 0x873b2436d3f0e114 +977, 0x6f99accf4ea050b6 +978, 0x5968ac874ed51613 +979, 0x4939d70d29a3c611 +980, 0x11f381ed28738d3d +981, 0xa97430d36ab3a869 +982, 0xe6fa880801129e22 +983, 0xf84decbd8f48c913 +984, 0x4425c0ed1e9a82a5 +985, 0x7a1f9485e9929d5a +986, 0xc7c51f155dfce1c6 +987, 0x9619a39501d74f2b +988, 0x7c7035955dbf4c1b +989, 0xc61ee569cf57c2c9 +990, 0x3eaf7c5b0df734e1 +991, 0xe71cb4064d1ede05 +992, 0x356e3cec80e418b2 +993, 0xca04306243a15be6 +994, 0x941cf3881fa18896 +995, 0x30dbb0e819d644e0 +996, 0xaae22c0bef02859a +997, 0x7bd30917bbaa8a94 +998, 0x2672547bc8d7d329 +999, 0x4955c92aaa231578 diff --git a/numpy/random/tests/data/pcg64dxsm-testset-2.csv b/numpy/random/tests/data/pcg64dxsm-testset-2.csv new file mode 100644 index 000000000000..878c5ea7c3a5 --- /dev/null +++ b/numpy/random/tests/data/pcg64dxsm-testset-2.csv @@ -0,0 +1,1001 @@ +seed, 0x0 +0, 0xd97e4a147f788a70 +1, 0x8dfa7bce56e3a253 +2, 0x13556ed9f53d3c10 +3, 0x55dbf1c241341e98 +4, 0xa2cd98f722eb0e0a +5, 0x83dfc407203ade8 +6, 0xeaa083df518f030d +7, 0x44968c87e432852b +8, 0x573107b9cb8d9ecc +9, 0x9eedd1da50b9daca +10, 0xb33a6735ca451e3c +11, 0x72830d2b39677262 +12, 0x9da8c512fd0207e8 +13, 0x1fc5c91954a2672b +14, 0xd33479437116e08 +15, 0x9ccdd9390cee46f3 +16, 0x1fd39bb01acd9e76 +17, 0xedc1869a42ff7fe5 +18, 0xbd68ca0b42a6e7e9 +19, 0x620b67df09621b1f +20, 0xfa11d51bd6950221 +21, 0xc8c45b36e7d28d08 +22, 0xe9c91272fbaad777 +23, 0x2dc87a143f220e90 +24, 0x6376a7c82361f49d +25, 0x552c5e434232fe75 +26, 0x468f7f872ac195bc +27, 0x32bed6858125cf89 +28, 0xe4f06111494d09d3 +29, 0xa5c166ffea248b80 +30, 0x4e26605b97064a3f +31, 0xceafd9f6fc5569d +32, 0xb772f2f9eed9e106 +33, 0x672c65e6a93534e2 +34, 0xcdc5e1a28d1bd6a0 +35, 0x1ed9c96daeebd3e3 +36, 0x4d189dcfc0c93c3f +37, 0x50df5a95c62f4b43 +38, 0xcccf4949fa65bbb8 +39, 0x19b8073d53cdc984 +40, 0x6fb40bba35483703 +41, 0xb02de4aef86b515a +42, 0x4d90c63655350310 +43, 0xea44e4089825b16c +44, 0x8d676958b1f9da2b +45, 0x6d313940917ae195 +46, 0x1b1d35a4c1dd19f4 +47, 0x117720f8397337ef +48, 0xcc073cf3ac11eeaa +49, 0x8331ec58a9ff8acb +50, 0xf3dc2a308b6b866f +51, 0x7eba1202663382b6 +52, 0x8269839debeb4e5a +53, 0x87fd3dc0f9181a8e +54, 0xabe62ddd3c925f03 +55, 0x7f56f146944fe8d4 +56, 0xc535972150852068 +57, 0x60b252d453bd3a68 +58, 0x4251f0134634490a +59, 0x338950da210dfeb2 +60, 0xcadfe932971c9471 +61, 0xfb7049457fab470e +62, 0x9bfb8145a4459dff +63, 0x4a89dda3898f9d8a +64, 0x88cc560151483929 +65, 0x277dc820f4b6796e +66, 0x3524bd07ea0afb88 +67, 0x92eb6ffb2bf14311 +68, 0xf6559be0783f3fe9 +69, 0xf0844f9af54af00d +70, 0xdd5e0b59adcef8a +71, 0x4ff7e4f2ab18554c +72, 0x3fa22c8a02634587 +73, 0x1db8e1a9442fe300 +74, 0x40cf15953ad3d3e7 +75, 0x92af15fe1a9f6f0a +76, 0xab4a0e466fb0cfd +77, 0x944f1555a06cca82 +78, 0x10cf48412f1f6066 +79, 0x7f51f9a455f9e8e1 +80, 0x47ee93530f024c7e +81, 0x36cf2f0413e0f6f2 +82, 0xa315e23731969407 +83, 0xd8e2796327cf5f87 +84, 0xa86072696a555c34 +85, 0xee3f0b8804feaab7 +86, 0x41e80dc858f8360b +87, 0x31ec2e9b78f5b29 +88, 0xd397fb9b8561344c +89, 0x28081e724e649b74 +90, 0x5c135fc3fc672348 +91, 0x9a276ca70ce9caa0 +92, 0x9216da059229050a +93, 0xcf7d375ed68007b0 +94, 0xa68ad1963724a770 +95, 0xd4350de8d3b6787c +96, 0xee7d2c2cc275b6d2 +97, 0x71645ec738749735 +98, 0x45abdf8c68d33dbb +99, 0xe71cadb692c705ea +100, 0x60af6f061fd90622 +101, 0x1eabe2072632c99d +102, 0x947dda995a402cb6 +103, 0xbb19f49a3454f3b +104, 0xe6e43e907407758c +105, 0xfe2b67016bd6873a +106, 0x7fdb4dd8ab30a722 +107, 0x39d3265b0ff1a45b +108, 0xed24c0e4fce8d0c2 +109, 0xf6e074f86faf669d +110, 0x9142040df8dc2a79 +111, 0x9682ab16bc939a9c +112, 0x6a4e80c378d971c8 +113, 0x31309c2c7fc2d3d6 +114, 0xb7237ec682993339 +115, 0x6a30c06bb83dccd9 +116, 0x21c8e9b6d8e7c382 +117, 0x258a24ae6f086a19 +118, 0xb76edb5be7df5c35 +119, 0x3c11d7d5c16e7175 +120, 0xbdfc34c31eff66e1 +121, 0x8af66e44be8bf3a2 +122, 0x3053292e193dec28 +123, 0xd0cc44545b454995 +124, 0x408ac01a9289d56 +125, 0x4e02d34318ec2e85 +126, 0x9413ff3777c6eb6b +127, 0xa3a301f8e37eb3df +128, 0x14e6306bd8d8f9f9 +129, 0xd3ea06ce16c4a653 +130, 0x170abe5429122982 +131, 0x7f9e6fddc6cacb85 +132, 0xa41b93e10a10a4c8 +133, 0x239216f9d5b6d0b5 +134, 0x985fcb6cb4190d98 +135, 0xb45e3e7c68f480c6 +136, 0xc1b2fc2e0446211c +137, 0x4596adb28858c498 +138, 0x2dd706f3458ddc75 +139, 0x29c988c86f75464 +140, 0xac33a65aa679a60 +141, 0xa28fef762d39d938 +142, 0x541e6fa48647f53 +143, 0x27838d56b2649735 +144, 0x8e143d318a796212 +145, 0xaea6097745f586b8 +146, 0x636143330f8ee2e6 +147, 0xc2d05fd8b945b172 +148, 0x6e355f9eb4353055 +149, 0xeb64ca42e8bf282e +150, 0xe8202dfd9da0fe5 +151, 0x7305689c9d790cba +152, 0xf122f8b1bef32970 +153, 0x9562887e38c32ba5 +154, 0xf9cd9be121b738d +155, 0x6238e0c398307913 +156, 0x5f2e79bb07c30f47 +157, 0x8ce8e45c465006e +158, 0x39281fe1e99e2441 +159, 0xafb10c2ca2874fea +160, 0x6e52f91633f83cf +161, 0x8ff12c1ac73c4494 +162, 0xe48608a09365af59 +163, 0xefd9bbc7e76e6a33 +164, 0xbe16a39d5c38ec92 +165, 0x6a6ffbcaf5a2330f +166, 0xdd5d6ac7d998d43d +167, 0x207bf978226d4f11 +168, 0xf8eec56bd2a0f62e +169, 0xa5bccf05dce0d975 +170, 0x93cf3ec1afe457a6 +171, 0x38651466d201f736 +172, 0x3ad21473985c9184 +173, 0xc6407a3bd38c92a6 +174, 0xb1ec42c7afa90a25 +175, 0xbdeca984df8b7dd3 +176, 0xb6926b1d00aa6c55 +177, 0x86141d0022352d49 +178, 0x169316256135ee09 +179, 0xffb1c7767af02a5c +180, 0x502af38ad19f5c91 +181, 0xfbf6cbc080086658 +182, 0x33cf9b219edae501 +183, 0x46e69bebd77b8862 +184, 0xf11e0cc91125d041 +185, 0xb4cd1649f85e078f +186, 0xb49be408db4e952 +187, 0xb0b8db46140cce3c +188, 0xba647f2174012be7 +189, 0x4f0a09e406970ac9 +190, 0xf868c7aec9890a5c +191, 0xde4c8fa7498ea090 +192, 0x872ceb197978c1d4 +193, 0x1eb5cd9c3269b258 +194, 0x3ea189f91724f014 +195, 0x41379656f7746f2c +196, 0x7bd18493aca60e51 +197, 0x5380c23b0cbbf15e +198, 0x920b72835f88246b +199, 0x24d7f734a4548b8e +200, 0x9944edb57e5aa145 +201, 0x4628e136ebb8afe1 +202, 0xb4ee6a776356e2a7 +203, 0x481cbe9744ccf7d7 +204, 0x7e8d67e8b0b995d9 +205, 0xeeacde100af7b47e +206, 0x103da08f2487dab7 +207, 0x6b9890a91d831459 +208, 0xd0c5beae37b572c7 +209, 0xfdccc371ee73fcc +210, 0x65438f0a367a2003 +211, 0x5d23b2c818a7e943 +212, 0x9a8ed45ac04b58b3 +213, 0xdaf3c3f1695dce10 +214, 0x5960eec706fa2bc0 +215, 0x98ca652facb80d40 +216, 0x72970ae5e2194143 +217, 0x18c6374d878c5c94 +218, 0x20fa51f997381900 +219, 0x3af253dba26d6e1d +220, 0x1b23d65db15c7f78 +221, 0x9f53ae976259b0e3 +222, 0x9a6addb28dc92d49 +223, 0x1e085c4accd0a7d7 +224, 0xe9d3f4cc9bad6ce5 +225, 0xe018fad78b5b1059 +226, 0x5ef7682232b4b95 +227, 0xb2242aa649f5de80 +228, 0x8f3e6d8dd99b9e4e +229, 0xb9be6cc22949d62a +230, 0xecbdc7beaa5ff1fe +231, 0xd388db43a855bdf0 +232, 0xd71ee3238852568d +233, 0x85ab3056304c04b5 +234, 0x2ed7ae7ad3cfc3cb +235, 0x781d1b03d40b6c48 +236, 0x7d3c740886657e6d +237, 0x982cfa6828daa6b0 +238, 0x278579599c529464 +239, 0x773adecfae9f0e08 +240, 0x63a243ea4b85c5d7 +241, 0x59940074fc3709e1 +242, 0xc914a2eed58a6363 +243, 0x2602b04274dd724c +244, 0xdf636eb7636c2c42 +245, 0x891a334d0d26c547 +246, 0xde8cd586d499e22d +247, 0x3ea1aa4d9b7035b6 +248, 0xd085cff6f9501523 +249, 0xe82a872f374959e +250, 0x55cb495bbd42cc53 +251, 0x5f42b3226e56ca97 +252, 0xea463f6f203493a3 +253, 0xeef3718e57731737 +254, 0x1bd4f9d62b7f9f3c +255, 0x19284f5e74817511 +256, 0xaf6e842c7450ca87 +257, 0x1d27d2b08a6b3600 +258, 0xfb4b912b396a52e3 +259, 0x30804d4c5c710121 +260, 0x4907e82564e36338 +261, 0x6441cf3b2900ddb7 +262, 0xd76de6f51988dc66 +263, 0x4f298ef96fd5e6d2 +264, 0x65432960c009f83d +265, 0x65ebed07e1d2e3df +266, 0xf83ee8078febca20 +267, 0x7bb18e9d74fc5b29 +268, 0x597b5fbc2261d91 +269, 0xea4f8ed0732b15b2 +270, 0xba2267f74f458268 +271, 0x3f304acabd746bbb +272, 0x7bd187af85659a82 +273, 0x88e20dbdb7a08ea3 +274, 0x2a2dc948c772fcb4 +275, 0x87784fec2993c867 +276, 0x89163933cd362d4e +277, 0xfd7b24f04302f957 +278, 0x9bdd544405dfb153 +279, 0xddee0fac58ffc611 +280, 0xa8e8993417e71ec1 +281, 0x55e0ab46ff7757af +282, 0x53e7645f08d3d7df +283, 0xbf78e563bc656ba2 +284, 0x1d162253b45ee2de +285, 0x15e2bfefedf29eb4 +286, 0x4e2a4584aa394702 +287, 0xa89fb12b01525897 +288, 0x825bd98f0544e4df +289, 0xfc6c50da6750700 +290, 0xc24aaabde7d28423 +291, 0x79d6f4660fcb19e5 +292, 0xee7d4fb40c8d659f +293, 0x70bc281b462e811d +294, 0x23ed4dc9636519a7 +295, 0xcb7c3f5a5711b935 +296, 0xe73090e0508c5d9d +297, 0xb25a331f375952a6 +298, 0xa64c86e0c04740f6 +299, 0xb8f3ffc8d56ac124 +300, 0x2479266fc5ee6b15 +301, 0x8d5792d27f5ffbcb +302, 0xb064298be946cd52 +303, 0xf0934a98912ffe26 +304, 0xbe805682c6634d98 +305, 0xe0e6e2c010012b4f +306, 0x58c47d475f75976 +307, 0x358c9a6e646b2b4a +308, 0x7e7c4ffca5b17ba7 +309, 0x43585c8c9a24a04c +310, 0x5154ddbcd68d5c2c +311, 0x4a2b062d3742a5e +312, 0xca5691191da2b946 +313, 0x696a542109457466 +314, 0x9eb5d658a5022ba5 +315, 0x8158cf6b599ab8dc +316, 0x1b95391eaa4af4a6 +317, 0x9953e79bd0fc3107 +318, 0x8639690086748123 +319, 0x2d35781c287c6842 +320, 0x393ef0001cd7bc8f +321, 0xe3a61be8c5f2c22a +322, 0x5e4ff21b847cc29b +323, 0x4c9c9389a370eb84 +324, 0xd43a25a8fc3635fa +325, 0xf6790e4a85385508 +326, 0x37edf0c81cb95e1d +327, 0x52db00d6e6e79af8 +328, 0x3b202bceeb7f096 +329, 0x2a164a1c776136bb +330, 0x73e03ee3fd80fd1b +331, 0xd2c58c0746b8d858 +332, 0x2ed2cb0038153d22 +333, 0x98996d0fc8ceeacc +334, 0xa4ed0589936b37f +335, 0x5f61cf41a6d2c172 +336, 0xa6d4afb538c110d7 +337, 0xe85834541baadf1a +338, 0x4c8967107fd49212 +339, 0x49bafb762ab1a8c1 +340, 0x45d540e2a834bf17 +341, 0x1c0ec8b4ed671dac +342, 0x3d503ce2c83fe883 +343, 0x437bfffd95f42022 +344, 0xc82d1e3d5c2bc8d2 +345, 0x7a0a9cbfcb0d3f24 +346, 0xc0a4f00251b7a3be +347, 0xb5be24e74bb6a1c6 +348, 0xa3104b94b57545b1 +349, 0x86de7d0c4b97b361 +350, 0x879c1483f26538a6 +351, 0xd74c87557f6accfb +352, 0x2f9be40dbf0fe8a1 +353, 0x445a93398f608d89 +354, 0x7b3cb8a7211d7fdc +355, 0xe86cc51290d031e7 +356, 0x33ef3594052ad79f +357, 0xc61911d241dbb590 +358, 0x37cccb0c0e3de461 +359, 0xb75259124080b48b +360, 0xd81e8961beb4abe5 +361, 0xf4542deb84a754e +362, 0x6ea036d00385f02e +363, 0xa7b60b0ac3b88681 +364, 0x108a6c36ca30baf5 +365, 0x4a2adc5bbfe2bf07 +366, 0x4079501f892a5342 +367, 0x55e113963c5448f0 +368, 0x8019ff4903b37242 +369, 0x109c6dcdb7ec6618 +370, 0x1239ac50944da450 +371, 0xe1399c7f94c651c1 +372, 0x5a6bbbae388d365a +373, 0x4d72be57b8810929 +374, 0x3f067df24384e1fb +375, 0x4f8b9e0f7f6c7be +376, 0x202492c342a3b08 +377, 0x250753192af93a3 +378, 0xfba1159d9de2cb8e +379, 0xba964497ab05505c +380, 0x1329ec5d8a709dca +381, 0x32927cacb6cd22bb +382, 0x6b4d7db904187d56 +383, 0xe76adccf8e841e02 +384, 0x8c4bf4b6a788202 +385, 0x3013a3b409831651 +386, 0x7427d125c475412f +387, 0x84dcc4bb2bf43202 +388, 0x117526f1101372a5 +389, 0xfe95d64b8984bd72 +390, 0x524e129934cc55c1 +391, 0xc3db4b0418c36d30 +392, 0xe1cb2047e9c19f7a +393, 0xea43d6c8d8982795 +394, 0xe80ac8a37df89ed +395, 0xfecc2104329ed306 +396, 0xa5c38aac9c1d51ea +397, 0x3abe5d1c01e4fe17 +398, 0x717a805d97fcc7ac +399, 0x94441f8207a1fb78 +400, 0x22d7869c5f002607 +401, 0x349e899f28c3a1b9 +402, 0x5639950cdea92b75 +403, 0x7e08450497c375b +404, 0x94bf898b475d211d +405, 0x75c761a402375104 +406, 0x1930920ec9d2a1e7 +407, 0xb774ba1bc6f6e4e2 +408, 0xf715602412e5d900 +409, 0x87bb995f4a13f0ba +410, 0xa3c787868dfa9c8d +411, 0xa17fd42a5a4f0987 +412, 0x4a9f7d435242b86 +413, 0x240364aff88f8aef +414, 0xe7cd4cf4bf39f144 +415, 0xd030f313ca4c2692 +416, 0xc46696f4e03ec1e9 +417, 0x22c60f1ec21060b3 +418, 0x16c88058fd68986f +419, 0x69ca448e8e6bde3f +420, 0x3466c2cdec218abd +421, 0x837ac4d05e6b117d +422, 0x911210e154690191 +423, 0x9ece851d6fa358b7 +424, 0x42f79cb0c45e7897 +425, 0xbf7583babd7c499b +426, 0x2059fe8031c6e0b9 +427, 0xabbec8fc00f7e51d +428, 0x88809d86a3a256e1 +429, 0xd36056df829fdcb5 +430, 0x515632b6cb914c64 +431, 0xba76d06c2558874 +432, 0x632c54ca4214d253 +433, 0xadec487adf2cb215 +434, 0x521e663e1940513d +435, 0xb1b638b548806694 +436, 0xbe2d5bfbe57d2c72 +437, 0x8b89e7719db02f7 +438, 0x90ba5281c1d56e63 +439, 0x899e1b92fceea102 +440, 0xf90d918e15182fa6 +441, 0x94a489ce96c948c4 +442, 0xad34db453517fcd4 +443, 0xc5264eb2de15930f +444, 0x101b4e6603a21cee +445, 0xef9b6258d6e85fff +446, 0x6075c7d6c048bd7a +447, 0x6f03232c64e438aa +448, 0x18c983d7105ee469 +449, 0x3ffc23f5c1375879 +450, 0xbc1b4a00afb1f9f +451, 0x5afa6b2bb8c6b46e +452, 0xe7fce4af2f2c152a +453, 0x5b00ab5c4b3982c7 +454, 0x2d4b0c9c0eb4bd0c +455, 0x61d926270642f1f2 +456, 0x7219c485c23a2377 +457, 0x7e471c752fecd895 +458, 0x23c4d30a4d17ba1f +459, 0x65cb277fe565ca22 +460, 0xcbb56ed9c701363b +461, 0xfd04ab3a6eba8282 +462, 0x19c9e5c8bab38500 +463, 0xea4c15227676b65b +464, 0x20f3412606c8da6f +465, 0xb06782d3bf61a239 +466, 0xf96e02d5276a9a31 +467, 0x835d256b42aa52a6 +468, 0x25b09151747f39c1 +469, 0x64507386e1103eda +470, 0x51cbc05716ef88e4 +471, 0x998cd9b7989e81cc +472, 0x9d7115416bec28d1 +473, 0xc992ca39de97906b +474, 0xd571e6f7ca598214 +475, 0xafc7fb6ccd9abbf8 +476, 0x88ef456febff7bf4 +477, 0xdbe87ccc55b157d2 +478, 0xaab95e405f8a4f6d +479, 0xad586a385e74af4f +480, 0x23cd15225c8485aa +481, 0x370940bf47900ac7 +482, 0xefd6afda1a4b0ead +483, 0x9cb1a4c90993dd7a +484, 0xff7893e8b2f70b11 +485, 0xb09e1807c0638e8e +486, 0xb10915dcb4978f74 +487, 0x88212ab0051a85eb +488, 0x7af41b76e1ec793f +489, 0x2e5c486406d3fefd +490, 0xebe54eff67f513cc +491, 0xab6c90d0876a79b8 +492, 0x224df82f93fe9089 +493, 0xc51c1ce053dc9cd2 +494, 0x5ef35a4d8a633ee7 +495, 0x4aca033459c2585f +496, 0xd066932c6eefb23d +497, 0x5309768aab9a7591 +498, 0xa2a3e33823df37f9 +499, 0xcec77ff6a359ee9 +500, 0x784dc62d999d3483 +501, 0x84e789fb8acc985d +502, 0xd590237e86aa60f +503, 0x737e2ffe1c8ad600 +504, 0xc019c3a39a99eab8 +505, 0x6a39e9836964c516 +506, 0xe0fe43129535d9da +507, 0xdfc5f603d639d4de +508, 0x7b9a7d048a9c03b6 +509, 0xbb5aa520faa27fdd +510, 0x2a09b4200f398fa2 +511, 0x38cc88107904064e +512, 0xa9a90d0b2d92bb25 +513, 0x9419762f87e987e3 +514, 0x1a52c525153dedcd +515, 0xc26d9973dd65ae99 +516, 0x8e89bd9d0dc6e6a1 +517, 0x2f30868dc01bfb53 +518, 0x20f09d99b46501c4 +519, 0x78b468a563b8f1e9 +520, 0xcccf34b0b6c380c7 +521, 0xf554e7dc815297e6 +522, 0x332a585cfb4a50ef +523, 0xa9fb64a2b6da41d7 +524, 0xdcd2a5a337391ce0 +525, 0x8a9bd3e324c6463d +526, 0x9f4487d725503bdd +527, 0xf72282d82f1d0ff +528, 0x308f4160abb72d42 +529, 0x648de1db3a601b08 +530, 0x36cab5192e7ebd39 +531, 0x7975fbe4ab6a1c66 +532, 0xd515b4d72243864e +533, 0x43a568f8b915e895 +534, 0x15fa9f2057bdb91d +535, 0x7a43858ef7a222dc +536, 0x17b4a9175ac074fe +537, 0xa932c833b8d0f8f8 +538, 0x1d2db93a9a587678 +539, 0x98abd1d146124d27 +540, 0xf0ab0431671740aa +541, 0xa9d182467540ad33 +542, 0x41c8a6cfc331b7fc +543, 0xa52c6bd0fcd1d228 +544, 0x2773c29a34dc6fa3 +545, 0x3098230746fc1f37 +546, 0xd63311bb4f23fabe +547, 0x6712bf530cd2faec +548, 0x342e8f342e42c4dd +549, 0xfbd83331851cdcad +550, 0xe903be1361bbc34d +551, 0xd94372e5077e3ef9 +552, 0x95aaa234f194bd8 +553, 0x20c0c8fb11e27538 +554, 0xfaf47dc90462b30b +555, 0x8ddc6d144147682a +556, 0xf626833fd926af55 +557, 0x5df93c34290d1793 +558, 0xb06a903e6e9fca5e +559, 0x10c792dc851d77ca +560, 0xd9b1b817b18e56cb +561, 0x3a81730c408eb408 +562, 0x65052c04a8d4b63c +563, 0x3328546598e33742 +564, 0xeca44a13f62d156d +565, 0x69f83d1d86b20170 +566, 0x937764200412027d +567, 0xc57eb1b58df0f191 +568, 0xa1c7d67dce81bc41 +569, 0x8e709c59a6a579ce +570, 0x776a2f5155d46c70 +571, 0xd92906fbbc373aa5 +572, 0xe97ad478a2a98bf6 +573, 0xc296c8819ac815f +574, 0x613ede67ba70e93e +575, 0xe145222498f99cde +576, 0xafcdfa7a3c1cf9bf +577, 0x1c89252176db670d +578, 0xad245eda5c0865ff +579, 0x249463d3053eb917 +580, 0xc9be16d337517c0b +581, 0xefcc82bf67b8f731 +582, 0x1e01577d029e0d00 +583, 0xad9c24b2a4f3d418 +584, 0xed2cceb510db4d0f +585, 0xbddadcdb92400c70 +586, 0x67d6b0476ef82186 +587, 0xbc7662ff7bf19f73 +588, 0x9d94452a729e6e92 +589, 0x6b278d8594f55428 +590, 0x6c4b31cceb1b2109 +591, 0xccc6c3a726701e9 +592, 0x6bc28ece07df8925 +593, 0xc0422b7bf150ccc4 +594, 0xab7158f044e73479 +595, 0xdf3347546d9ed83f +596, 0x3b3235a02c70dff4 +597, 0x2551c49c14ea8d77 +598, 0xee2f7f5bb3cc228e +599, 0x39b87bfe8c882d39 +600, 0x7dd420fad380b51c +601, 0xffe64976af093f96 +602, 0x4a4f48dc6e7eaa5f +603, 0x85f2514d32fdc8cc +604, 0x1ab1215fd7f94801 +605, 0x4cd1200fc795b774 +606, 0xcf8af463a38942ee +607, 0x319caa7ce3022721 +608, 0x8cd9798a76d1aea4 +609, 0x2bd3933ac7afd34e +610, 0x85d4c323403cf811 +611, 0xd7b956d3064efa30 +612, 0x67a078dbf1f13068 +613, 0x665fa6c83e87c290 +614, 0x9333ac2416d2469b +615, 0xdfb1fd21a0094977 +616, 0xa1962a6e2c25f8ff +617, 0x1f3b10a7ed5287cf +618, 0x70641efb3d362713 +619, 0xe527a2cf85d00918 +620, 0x9741e45d3f9890a3 +621, 0x6cb74b5d4d36db4b +622, 0xf24734d622bd2209 +623, 0xadd6d94f78e9d378 +624, 0xc3bbdb59225cca7f +625, 0x5ad36614275b30cd +626, 0x495568dd74eea434 +627, 0xf35de47e0ffe1f2d +628, 0xefa209dca719ab18 +629, 0x844ddcaeb5b99ae8 +630, 0x37449670a1dc7b19 +631, 0x5a4612c166f845c1 +632, 0xe70f7782f2087947 +633, 0x98d484deac365721 +634, 0x705302198cf52457 +635, 0x7135ae0f5b77df41 +636, 0x342ac6e44a9b6fc3 +637, 0x2713fd2a59af5826 +638, 0x6e1a3f90f84efa75 +639, 0x9fb3b4dd446ca040 +640, 0x530044ae91e6bd49 +641, 0xe984c4183974dc3e +642, 0x40c1fa961997d066 +643, 0xb7868250d8c21559 +644, 0x8bc929fa085fd1de +645, 0x7bdb63288dc8733e +646, 0xac4faad24326a468 +647, 0x1c6e799833aea0b1 +648, 0xcc8a749e94f20f36 +649, 0x4e7abfd0443547c5 +650, 0xb661c73bb8caa358 +651, 0x4a800f5728ff2351 +652, 0x8c15e15189b9f7ed +653, 0xab367846b811362c +654, 0x4ba7508f0851ca2a +655, 0xe9af891acbafc356 +656, 0xbdebe183989601f8 +657, 0x4c665ea496afc061 +658, 0x3ca1d14a5f2ed7c +659, 0xfbdff10a1027dd21 +660, 0xdfd28f77c8cff968 +661, 0xc4fbaadf8a3e9c77 +662, 0xdac7e448b218c589 +663, 0xb26390b5befd19e2 +664, 0xd2ef14916c66dba9 +665, 0xfab600284b0ff86b +666, 0xf04a1c229b58dabb +667, 0xc21c45637e452476 +668, 0xd1435966f75e0791 +669, 0xc1f28522eda4a2d0 +670, 0x52332ae8f1222185 +671, 0x81c6c0790c0bf47e +672, 0xfebd215e7d8ffb86 +673, 0x68c5dce55dbe962b +674, 0x231d09cb0d2531d1 +675, 0x3218fba199dbbc6b +676, 0x8f23c535f8ea0bf6 +677, 0x6c228963e1df8bd9 +678, 0x9843c7722ed153e3 +679, 0xd032d99e419bddec +680, 0xe2dca88aa7814cab +681, 0x4d53fb8c6a59cdc2 +682, 0x8fb3abc46157b68b +683, 0xa3e733087e09b8e +684, 0x6bdc1aee029d6b96 +685, 0x4089667a8906d65b +686, 0x8f3026a52d39dd03 +687, 0x6d2e0ccb567bae84 +688, 0x74bad450199e464 +689, 0xf114fb68a8f300d5 +690, 0xc7a5cc7b374c7d10 +691, 0xf0e93da639b279d1 +692, 0xb9943841ad493166 +693, 0x77a69290455a3664 +694, 0x41530da2ebea054b +695, 0xe8f9fab03ea24abf +696, 0xaa931f0c9f55a57a +697, 0xb4d68a75d56f97ae +698, 0x3d58ff898b6ba297 +699, 0x49d81e08faf5a3f5 +700, 0xfc5207b9f3697f3b +701, 0xa25911abb3cf19b7 +702, 0x6b8908eb67c3a41 +703, 0xd63ef402e2e3fa33 +704, 0x728e75d3f33b14c5 +705, 0x248cb1b8bc6f379a +706, 0x3aa3d6d2b8c72996 +707, 0x49cc50bd2d3d2860 +708, 0xb4e1387647c72075 +709, 0x435a1630a4a81ed3 +710, 0xa5ea13005d2460cf +711, 0xc7a613df37d159ec +712, 0x95721ccc218b857e +713, 0xd4b70d8c86b124d3 +714, 0x2b82bcc4b612d494 +715, 0xaf13062885276050 +716, 0xcbd8fcf571a33d9c +717, 0x3f7f67ca1125fc15 +718, 0xddf4bb45aac81b4c +719, 0x23606da62de9c040 +720, 0xa3a172375666b636 +721, 0x292f87387a6c6c3c +722, 0xd1d10d00c5496fe1 +723, 0x86b0411ce8a25550 +724, 0x38e0487872e33976 +725, 0x363e49f88ddfd42c +726, 0x45bdf1e9f6b66b0a +727, 0x8a6fff3de394f9b5 +728, 0x8502158bb03f6209 +729, 0x22e24d16dba42907 +730, 0x3fe3ba427cc2b779 +731, 0x77144793f66b3d7e +732, 0xcf8912ccb29b8af9 +733, 0xdc856caff2abd670 +734, 0xe6d3ae0b0d9d4c8b +735, 0xb8f5d40e454c539f +736, 0x79ca953114fbc6b7 +737, 0x478d6f4bbfa38837 +738, 0x9babae1a3ffdc340 +739, 0x40edd56802bae613 +740, 0x97a56c2dcccf0641 +741, 0xafc250257f027f8e +742, 0x8da41ef1edf69125 +743, 0x6574b0280ff9d309 +744, 0x197c776151b8f820 +745, 0x6b03e077c9dac3b6 +746, 0x24a40ebbc5c341c5 +747, 0x50e585169a6a1c4b +748, 0x37783a5a6a3e4e02 +749, 0xb3de81ee6fbad647 +750, 0xf4f292f57ca4591e +751, 0x6214e9e7d44d30a +752, 0x5920190c56d21c12 +753, 0x9ac163419b5e0c9b +754, 0xfc2328761ae8ed93 +755, 0xc68f945b545508c6 +756, 0x687c49a17ce0a5e2 +757, 0x276d8f53d30d4ab4 +758, 0x8201804970343ce1 +759, 0x1b5d323cc2e7fb7e +760, 0x6f351ef04fd904b +761, 0x6c793a7d455d5198 +762, 0x46f5d108430ae91f +763, 0xac16a15b2a0cf77f +764, 0xa0d479d9e4122b9d +765, 0x3afd94604307f19 +766, 0x2573ed6d39d38dbf +767, 0xa58e14ba60b4294b +768, 0xe69c1aed5840d156 +769, 0x4cf6fda7f04855c2 +770, 0x2fb65a56ef5f22da +771, 0xf95819434d5dc220 +772, 0x29c65133623dafba +773, 0x8e997bd018467523 +774, 0xfd08ba9d498461a7 +775, 0xdd52243bc78a5592 +776, 0x39c30108f6db88b3 +777, 0x38af8e1894f259b9 +778, 0x97eedf3b4ae5f6de +779, 0x757825add80c5ece +780, 0xf0fdd90ac14edb14 +781, 0xbbb19d4cc8cac6d4 +782, 0x9a82234edfae05e3 +783, 0x704401c61d1edf1c +784, 0x8b0eb481fb3a1fb2 +785, 0xef6f36e7cc06c002 +786, 0x7a208b17e04b8cd7 +787, 0xf20e33d498838fe9 +788, 0xc2bdb22117058326 +789, 0x6ec31939eb4ca543 +790, 0x6f1654838f507a21 +791, 0xc65ab81a955d2b93 +792, 0x40b1420fdd9531b8 +793, 0xe31f221cab9f4f40 +794, 0x798cdd414c1deb7a +795, 0x9c84e9c7d41cd983 +796, 0x63d6b1ae3b60b7fa +797, 0xb42bfdd1a2f78ffa +798, 0x37e431eaccaaa8e9 +799, 0x7508142a0f73eac9 +800, 0x91662a023df5893a +801, 0x59782070e2fe3031 +802, 0xb2acd589a8ce7961 +803, 0xa224743fa877b292 +804, 0xaa5362aa27e6ed9e +805, 0xa394a4e520c0c1c7 +806, 0xe49b16d2018ffb6f +807, 0xb8074b9f2f1e762b +808, 0xcf5f86143d5c23a7 +809, 0xfd838785db987087 +810, 0x31b1889df389aff8 +811, 0x30aaca876a4383b +812, 0x1731bb71c4c38d4f +813, 0x9a83a65395e05458 +814, 0x99cd0c8d67c8f4fc +815, 0xfbd9fdc849b761a5 +816, 0x82c04834fc466889 +817, 0xdeef9d6e715e8c97 +818, 0x549c281c16da6078 +819, 0x2d70661254ad599d +820, 0x57995793a72acac +821, 0xf1727005116183ba +822, 0xa22bb38945285de3 +823, 0x4f2d687fe45131ff +824, 0x5666c87ddbbc981f +825, 0xbcb4b2d4e7a517d0 +826, 0x5e794dd2e20b785d +827, 0x449ad020149e093c +828, 0x7704ee0412d106f5 +829, 0x83cbdf257b072ac1 +830, 0xae5c4fc9f638b0da +831, 0x7b9e5a64e372ed47 +832, 0x7eddbbb22c2cdf57 +833, 0x3f19ebfa155b08e +834, 0x91d991154dfd7177 +835, 0x611ae74b952d387f +836, 0x3fdf7a335bda36ee +837, 0xdf182433fc7a7c05 +838, 0x62c78598d1f8db0a +839, 0xc3750c69d2c5c1f0 +840, 0xf1318024709efdee +841, 0xaa3fd360d224dc29 +842, 0x62af53b2f307c19 +843, 0xdf527683c58120c2 +844, 0x3281deecc496f93d +845, 0x4f704ad31527ef08 +846, 0x127a14a5e07cfdfc +847, 0x90d0b1f549255c92 +848, 0xbc3406b212c5e1fc +849, 0x4e89f39379dba91d +850, 0x1290ef43c4998e6e +851, 0xecfeb1a1cb1c6e1b +852, 0x2067e90403003bf1 +853, 0x38ae04be30bdbeba +854, 0x8a3537f298baedda +855, 0xd07f3b825cdb2936 +856, 0xea020b5aebae8b45 +857, 0xfcd614ab031132b0 +858, 0x5fb682a4ff2268f5 +859, 0xd1c4662ce65596f4 +860, 0x7026b8270dd0b8dc +861, 0x8101ec4b4beae45a +862, 0xa0e9dc87940610a6 +863, 0x83ec33679d83165b +864, 0x981847ca82e86d41 +865, 0xda84c188a304a0b7 +866, 0x3c37529c5a5bbbb8 +867, 0x34a8491ce3e19a5a +868, 0xd36ad716a2fa6cb8 +869, 0xfd1d1d6a5189a15c +870, 0x9716eb47851e8d8d +871, 0x7dfb13ea3b15c5aa +872, 0xbdf6e707f45113a5 +873, 0xb8118261b04bd097 +874, 0x6191f9895881bec6 +875, 0x7aac257ae11acf9b +876, 0x35a491e1537ff120 +877, 0xe078943432efa71c +878, 0xb3338485dd3dc2b9 +879, 0x456060975d2bb3b5 +880, 0xaddc4c451bdfc44c +881, 0x18bfa7beacf96430 +882, 0x8802ebcaf0f67498 +883, 0xad922a5a825bd780 +884, 0x9fb4587d748f4efa +885, 0xdb2a445136cd5e7 +886, 0xb98b3676ea8e96ac +887, 0xb02d8d244d784878 +888, 0xa1a8442b18860abb +889, 0x6a3029ba1361e5d1 +890, 0xf426d5fac161eb1 +891, 0xfa5ac2b87acecb23 +892, 0xaa659896e50535df +893, 0xf40dd7a3d3c5c8ed +894, 0x3f8367abecb705bc +895, 0x2d60e7525873358f +896, 0xc4a9d3948a0c3937 +897, 0x5ecc04fef6003909 +898, 0x7a865004918cba2 +899, 0x47ae110a678ec10b +900, 0xa0f02f629d91aa67 +901, 0x4848b99e7fac9347 +902, 0xaa858346d63b80ac +903, 0xeb5bf42ee161eeef +904, 0x4d35d723d3c6ba37 +905, 0xdf22ca6ca93b64a7 +906, 0x9d198520f97b25b1 +907, 0x3068415350778efe +908, 0xf3709f2e8793c2fe +909, 0xd1517bac8dd9f16f +910, 0xfb99bccaa15861dc +911, 0xa9ad607d796a2521 +912, 0x55d3793d36bd22e4 +913, 0xf99270d891ff7401 +914, 0x401750a5c4aa8238 +915, 0xd84b3003e6f28309 +916, 0x8a23798b5fa7c98b +917, 0xadd58bbc8f43e399 +918, 0xbd8c741ada62c6a8 +919, 0xbdc6937bc55b49fa +920, 0x4aefa82201b8502 +921, 0x17adf29a717b303 +922, 0xa6ed2197be168f6c +923, 0x1ba47543f4359a95 +924, 0xe34299949ac01ae9 +925, 0x711c76cffc9b62f3 +926, 0xbac259895508a4b7 +927, 0x3c8b3b3626b0d900 +928, 0x1a8d23fbe2ae71bf +929, 0xca984fa3b5a5c3a1 +930, 0xb1986ab7521a9c93 +931, 0xd6b5b2c8d47a75b5 +932, 0xc7f1c4a88afb4957 +933, 0xdeb58033a3acd6cc +934, 0xabe49ddfe1167e67 +935, 0x8d559c10205c06e3 +936, 0xea07a1a7de67a651 +937, 0xcbef60db15b6fef8 +938, 0xbfca142cff280e7 +939, 0x362693eba0732221 +940, 0x7463237e134db103 +941, 0x45574ddb5035e17a +942, 0xfc65e0cb9b94a1aa +943, 0x3154c55f1d86b36d +944, 0x2d93a96dd6ab2d8b +945, 0xbe3bc1d1f2542a25 +946, 0xdd4b541f7385bdaa +947, 0x3b56b919d914e3f8 +948, 0x82fd51468a21895f +949, 0x8988cf120731b916 +950, 0xa06a61db5fb93e32 +951, 0x6ed66c1b36f68623 +952, 0x875ae844d2f01c59 +953, 0x17ccd7ac912e5925 +954, 0x12fe2a66b8e40cb1 +955, 0xf843e5e3923ad791 +956, 0xa17560f2fd4ef48 +957, 0x27a2968191a8ee07 +958, 0xa9aab4d22ff44a3c +959, 0x63cd0dcc3bb083ae +960, 0x7a30b48c6160bf85 +961, 0x956160fb572503b3 +962, 0xc47f6b7546640257 +963, 0xaf4b625f7f49153 +964, 0x2f5c86a790e0c7e8 +965, 0xb52e0610ae07f0b8 +966, 0x38a589292c3d849e +967, 0xc3e9ef655d30b4ef +968, 0xb5695f765cda998a +969, 0xde5d5e692a028e91 +970, 0x839476721555f72e +971, 0x48b20679b17d9ebf +972, 0xe3d4c6b2c26fb0df +973, 0xce5a9834f0b4e71f +974, 0x533abb253d5d420e +975, 0x9eac5ad9aed34627 +976, 0xc0f2a01ab3c90dbb +977, 0x6528eda93f6a066c +978, 0xc16a1b625e467ade +979, 0x1a4a320fb5e8b098 +980, 0x8819cccd8b4ab32f +981, 0x42daa88531fd0bfd +982, 0xcf732226409be17c +983, 0xfddcdb25ccbf378c +984, 0x9b15b603bf589fc1 +985, 0x2436066b95d366fe +986, 0x8d42eff2e9cbda90 +987, 0x694b2fc8a4e8303c +988, 0x8e207f98aaea3ccd +989, 0x4730d7a620f822d9 +990, 0x468dc9ca30fe2fd4 +991, 0x74b36d8a1c0f031b +992, 0x3c1aac1c488c1a94 +993, 0x19d0101042444585 +994, 0x8ec50c56d0c8adf4 +995, 0x721ec629e4d66394 +996, 0x3ca5ad93abeac4a4 +997, 0xaaebc76e71592623 +998, 0x969cc319e3ed6058 +999, 0xc0a277e3b2bfc3de diff --git a/numpy/random/tests/data/philox-testset-1.csv b/numpy/random/tests/data/philox-testset-1.csv new file mode 100644 index 000000000000..e448cbf73cc0 --- /dev/null +++ b/numpy/random/tests/data/philox-testset-1.csv @@ -0,0 +1,1001 @@ +seed, 0xdeadbeaf +0, 0xedc95200e2bd66a5 +1, 0x581d4e43b7682352 +2, 0x4be7278f5e373eab +3, 0xee47f17991a9e7ea +4, 0x38a7d2ae422f2e2c +5, 0xe2a6730a3b4a8a15 +6, 0x1588b7a841486442 +7, 0x13ad777246700504 +8, 0x14d157e0f5e18204 +9, 0xd87c22a7ee8c13f1 +10, 0x30cc389ce3542ba1 +11, 0xb8a53348955bb2e9 +12, 0xc08802e3c454f74f +13, 0xb444f627671a5780 +14, 0x4b6dd42b29cbf567 +15, 0x6109c7dc0bc5f7d5 +16, 0x85c954715d6b5b1e +17, 0x646178d3d9a3a5d5 +18, 0xebbde42b1cd83465 +19, 0x3d015102f6bc9c1a +20, 0x720fe2ec3798d5fd +21, 0x93120961289ceb2e +22, 0xc9207e960a56fae2 +23, 0xa7f042f31d991b98 +24, 0x5fac117415fae74b +25, 0xd0a970ba8dddc287 +26, 0x84b4e7e51b43106 +27, 0x6ad02bf525ea265f +28, 0xcdc7e5992b36ef8f +29, 0x44d4985209261d60 +30, 0x628c02d50f4b902e +31, 0xc7b1914922d1e76d +32, 0xfde99ff895cba51d +33, 0x175a0be050fa985f +34, 0x47297d3699e03228 +35, 0xccf1e9aeaa3339cd +36, 0x9fdd18ebeeaf15b1 +37, 0x7c94c9ab68747011 +38, 0x612d8ef22c1fa80f +39, 0x13f52b860de89ab5 +40, 0x81f264b8c139c43b +41, 0x8d017ba4ef1e85ba +42, 0x6d0556f46219951e +43, 0x8ee7b85663cf67b6 +44, 0x2432fc707645fe67 +45, 0xaf814046051e5941 +46, 0x4d432a83739ac76f +47, 0x59e5060d0983ccdd +48, 0xdd20e828b83d9b53 +49, 0x1b891800d7385f4c +50, 0x10e86a026c52ff5e +51, 0xb932f11723f7b90c +52, 0xb2413d0a1f3582d0 +53, 0xe7cd4edda65fc6b5 +54, 0x6d3808848d56593b +55, 0x192a727c3c7f47d9 +56, 0x9659d8aea5db8c16 +57, 0x4242c79fe2c77c16 +58, 0x605f90c913827cea +59, 0x53e153c8bfc2138a +60, 0xed2158fbdef5910e +61, 0xae9e6e29d4cb5060 +62, 0x7dd51afaad3b11ce +63, 0x2b9ba533d01a5453 +64, 0x7e0e9cf2b6c72c8 +65, 0x1cc8b3c7747ed147 +66, 0x9b102651e2e11b48 +67, 0x30b0b53cbaac33ea +68, 0x70c28aec39b99b85 +69, 0x5f1417ff536fdb75 +70, 0x3a1d91abd53acf58 +71, 0xba116a1772168259 +72, 0xf5369bc9bd284151 +73, 0x67bf11373bf183ca +74, 0xef0b2d44dbd33dc7 +75, 0xbfd567ee1a2953ed +76, 0x7d373f2579b5e5c6 +77, 0x756eeae7bcdd99be +78, 0x75f16eb9faa56f3b +79, 0x96d55ded2b54b9a5 +80, 0x94495191db692c24 +81, 0x32358bdd56bab38c +82, 0x3f6b64078576579 +83, 0x7177e7948bc064c9 +84, 0x2cbf23f09ba9bc91 +85, 0x9b97cc31c26645f5 +86, 0x5af2d239ff9028b1 +87, 0x316fa920e0332abe +88, 0x46535b7d1cae10a0 +89, 0x21f0a6869298022c +90, 0xf395c623b12deb14 +91, 0x8573995180675aa7 +92, 0xc3076509f4dc42d5 +93, 0x15e11e49760c6066 +94, 0xe8a6d311e67a021d +95, 0x7482f389c883339b +96, 0xda6f881573cba403 +97, 0xb110ffb847e42f07 +98, 0x2c3393140605ccf9 +99, 0xba1c8ba37d8bdc33 +100, 0x59adf43db7a86fe0 +101, 0xb4fcbf6aa585ca85 +102, 0xd794a93c18033fa6 +103, 0x6e839c01985f9d4 +104, 0x64065bf28222b2c7 +105, 0x6a6359b293fa0640 +106, 0x5ff610969e383e44 +107, 0xa8172c263f05c7f7 +108, 0x62a0172e8bd75d07 +109, 0x7be66e3c453b65ac +110, 0x6a3b8d5a14014292 +111, 0xa2583e6087450020 +112, 0xd5d3ecc480c627d2 +113, 0xa24e83f1eec8a27c +114, 0xa23febd2a99ee75a +115, 0x9a5fbf91c7310366 +116, 0x5b63156932e039b +117, 0x942af3c569908505 +118, 0x89a850f71ab6a912 +119, 0xfeadc803ac132fe9 +120, 0x67bf60e758250f3 +121, 0x533c25103466a697 +122, 0xb7deede3482f9769 +123, 0x325e043b53bba915 +124, 0x9e8d9e7fde132006 +125, 0x6bacc6860bbc436e +126, 0xb3ea0534c42b1c53 +127, 0xb2389334db583172 +128, 0xa74b1bfbf5242ee4 +129, 0x53a487e2dc51d15c +130, 0xe5a3b538d2c7a82e +131, 0x7b6c70bb0c4cadaf +132, 0xae20791b2081df1 +133, 0xc685c12e3c61d32c +134, 0x60110e6b0286e882 +135, 0x49682119c774045c +136, 0x53dc11a3bbd072e +137, 0xbdc87c6e732d9c2d +138, 0xcc4620861ebac8fd +139, 0x7e9c3558759350cc +140, 0x157408dee34891ba +141, 0x9bcad1855b80651b +142, 0xd81b29141d636908 +143, 0x1ed041a9f319c69d +144, 0x805b2f541208b490 +145, 0x484ef3bba2eb7c66 +146, 0xb6b5e37d50a99691 +147, 0xabc26a7d9e97e85f +148, 0xcba2a3cce0417c2f +149, 0xa030dfffd701993c +150, 0x2bf2dc50582ebf33 +151, 0xd9df13dd3eb9993e +152, 0x31ca28b757232ae5 +153, 0x614562a0ccf37263 +154, 0x44d635b01725afbb +155, 0x5ae230bc9ca9cd +156, 0xb23a124eb98705c6 +157, 0x6395675444981b11 +158, 0xd97314c34119f9ca +159, 0x9de61048327dd980 +160, 0x16bac6bded819707 +161, 0xcea3700e3e84b8c7 +162, 0xaa96955e2ee9c408 +163, 0x95361dcc93b5bc99 +164, 0x306921aed3713287 +165, 0x4df87f3130cd302a +166, 0x37c451daeb6a4af5 +167, 0x8dbbe35f911d5cc1 +168, 0x518157ce61cb10f9 +169, 0x669f577aebc7b35b +170, 0x4b0a5824a8786040 +171, 0x519bc3528de379f5 +172, 0x6128012516b54e02 +173, 0x98e4f165e5e6a6dd +174, 0x6404d03618a9b882 +175, 0x15b6aeb3d9cd8dc5 +176, 0x87ed2c1bae83c35b +177, 0x8377fc0252d41278 +178, 0x843f89d257a9ba02 +179, 0xcdda696ea95d0180 +180, 0xcfc4b23a50a89def +181, 0xf37fd270d5e29902 +182, 0xafe14418f76b7efa +183, 0xf984b81577076842 +184, 0xe8c60649ccb5458d +185, 0x3b7be8e50f8ff27b +186, 0xaa7506f25cef1464 +187, 0x5e513da59f106688 +188, 0x3c585e1f21a90d91 +189, 0x1df0e2075af292a +190, 0x29fdd36d4f72795f +191, 0xb162fe6c24cb4741 +192, 0x45073a8c02bd12c4 +193, 0xcbaaa395c2106f34 +194, 0x5db3c4c6011bc21c +195, 0x1b02aac4f752e377 +196, 0xa2dfb583eb7bec5 +197, 0xfe1d728805d34bb1 +198, 0xf647fb78bb4601ec +199, 0xd17be06f0d1f51ef +200, 0x39ec97c26e3d18a0 +201, 0xb7117c6037e142c8 +202, 0xe3a6ce6e6c71a028 +203, 0xe70a265e5db90bb2 +204, 0x24da4480530def1e +205, 0xfd82b28ce11d9a90 +206, 0x5bf61ead55074a1d +207, 0xbe9899c61dec480d +208, 0xae7d66d21e51ec9e +209, 0x384ee62c26a08419 +210, 0x6648dccb7c2f4abf +211, 0xc72aa0c2c708bdc9 +212, 0x205c5946b2b5ba71 +213, 0xd4d8d0b01890a812 +214, 0x56f185493625378d +215, 0x92f8072c81d39bd0 +216, 0xa60b3ceecb3e4979 +217, 0xfcf41d88b63b5896 +218, 0xf5a49aa845c14003 +219, 0xffcc7e99eee1e705 +220, 0xdd98312a7a43b32d +221, 0xa6339bd7730b004 +222, 0xdac7874ba7e30386 +223, 0xadf6f0b0d321c8 +224, 0x126a173ae4ffa39f +225, 0x5c854b137385c1e7 +226, 0x8173d471b1e69c00 +227, 0x23fa34de43581e27 +228, 0x343b373aef4507b1 +229, 0xa482d262b4ea919c +230, 0xf7fbef1b6f7fbba +231, 0xd8ce559487976613 +232, 0xbf3c8dd1e6ebc654 +233, 0xda41ed375451e988 +234, 0xf54906371fd4b9b3 +235, 0x5b6bb41231a04230 +236, 0x866d816482b29c17 +237, 0x11315b96941f27dc +238, 0xff95c79205c47d50 +239, 0x19c4fff96fbdac98 +240, 0xbfb1ae6e4131d0f4 +241, 0x9d20923f3cdb82c9 +242, 0x282175507c865dff +243, 0xdfd5e58a40fe29be +244, 0xedbd906ff40c8e4f +245, 0x11b04fc82614ccb3 +246, 0xeceb8afda76ae49f +247, 0xa4856913847c2cdf +248, 0x6f1425f15a627f2a +249, 0xdf144ffedf60349e +250, 0x392d7ecfd77cc65f +251, 0x72b8e2531049b2c6 +252, 0x5a7eb2bdb0ec9529 +253, 0xdcfd4306443e78c1 +254, 0x89ad67ed86cd7583 +255, 0x276b06c0779a6c8f +256, 0xb2dbb723196a0ac3 +257, 0x66c86a3b65906016 +258, 0x938348768a730b47 +259, 0x5f5282de938d1a96 +260, 0xa4d4588c4b473b1f +261, 0x8daed5962be4796f +262, 0x9dde8d796985a56e +263, 0x46be06dbd9ed9543 +264, 0xdf98286ceb9c5955 +265, 0xa1da1f52d7a7ca2b +266, 0x5a7f1449f24bbd62 +267, 0x3aedc4e324e525fd +268, 0xced62464cd0154e1 +269, 0x148fc035e7d88ce3 +270, 0x82f8878948f40d4c +271, 0x4c04d9cdd6135c17 +272, 0xdf046948d86b3b93 +273, 0x2f0dec84f403fe40 +274, 0xa61954fb71e63c0d +275, 0x616d8496f00382e8 +276, 0x162c622472746e27 +277, 0x43bcfe48731d2ceb +278, 0xff22432f9ff16d85 +279, 0xc033ed32bb0ad5a4 +280, 0x5d3717cc91c0ce09 +281, 0x7a39a4852d251075 +282, 0x61cd73d71d6e6a6 +283, 0xe37e2ea4783ab1a5 +284, 0x60e1882162579ea8 +285, 0x9258ec33f1a88e00 +286, 0x24b32acf029f0407 +287, 0x1410fc9aea6d3fac +288, 0x6054cf2a3c71d8f7 +289, 0x82f7605157a66183 +290, 0x3b34c1c0dff9eac5 +291, 0xfebe01b6d5c61819 +292, 0x7372187c68b777f2 +293, 0xc6923812cda479f0 +294, 0x386613be41b45156 +295, 0x92cfebe8cc4014b +296, 0x8e13c4595849828b +297, 0x90e47390d412291f +298, 0x6b21a1d93d285138 +299, 0xbf5b1f5922f04b12 +300, 0x21e65d1643b3cb69 +301, 0xf7683b131948ac3c +302, 0xe5d99fc926196ed2 +303, 0x7b138debbec90116 +304, 0x8a2650a75c2c2a5c +305, 0x20689a768f9b347b +306, 0xdfa2900cfb72dc6e +307, 0x98959c3855611cc2 +308, 0x5fdb71b89596cc7c +309, 0x1c14ac5c49568c7b +310, 0x958c4293016091fe +311, 0x7484522eb0087243 +312, 0xc4018dfb34fc190f +313, 0xca638567e9888860 +314, 0x102cd4805f0c0e89 +315, 0xcc3bc438e04548f8 +316, 0xb808944bb56ea5be +317, 0xffd4778dbf945c57 +318, 0xfe42617784c0233b +319, 0x3eccbfeae9b42d3c +320, 0xd9f1b585fd0bfa60 +321, 0x5c063d1b2705d5dd +322, 0x8e8bec3519941b64 +323, 0x9e94c36cbec2a42 +324, 0x1cd19f5b64ffd3ad +325, 0x9632e3aebfc68e66 +326, 0x98960c2d9da4ae45 +327, 0xb76994b1f2bbfc1f +328, 0xca184a737d3971cc +329, 0x964d31b07183adfb +330, 0xe9e0ff351cd276d4 +331, 0xb5747c860b05bbe4 +332, 0x5549ddc3bd3862e2 +333, 0x495496677b27873b +334, 0x53910baa26e3ea18 +335, 0xaa07a07ad0a688d3 +336, 0xbb43bd1f09ecdb1e +337, 0xe2ebc105699dd84 +338, 0x6e815a2729584035 +339, 0x2caab1713b17948a +340, 0x43d39d209fa41c90 +341, 0xfe3e71089d5d1c3a +342, 0xa778646c32f81177 +343, 0x8d42bfb86e6e92d5 +344, 0x175571f70b4fcfbe +345, 0x2a66a6fe10dc3b5b +346, 0xd9545e85235ca709 +347, 0x5642781c77ced48a +348, 0x24facc40b72ccd09 +349, 0xa800fbacce33f6f8 +350, 0x675f58a0ff19fba +351, 0x35aedf57bb5cde1b +352, 0xe5535a6b63f6d068 +353, 0x84dffd0102aaa85d +354, 0x621faad65467aaa7 +355, 0x596ad85b556b112f +356, 0x837545fff8894c7a +357, 0x3d9a4ae1356bc6a6 +358, 0xcd8b7153205d4ad0 +359, 0x98afdd40f1ed09a6 +360, 0xa38b2dc55a5cf87f +361, 0x484aecce2b6838bc +362, 0x6af05c26bdab18d9 +363, 0xf418b7399dcf2e4b +364, 0x1cfa38789b0d2445 +365, 0xfbed23c34166ee67 +366, 0x38e6820039e4912a +367, 0x1fe94911e963591e +368, 0x1291c79aee29ad70 +369, 0x65eccfc89506f963 +370, 0x7d14de3b2f55b1f6 +371, 0x82eb79c36cd2a739 +372, 0x41ffe3b75ea0def5 +373, 0x9eba9156470a51d9 +374, 0xd17c00b981db37d1 +375, 0xf688769a75601aa7 +376, 0xbcf738e9e03d571e +377, 0x14712e56df8f919b +378, 0xab14e227d156e310 +379, 0xf53d193e993e351e +380, 0x857fae46bd312141 +381, 0xc2dd71e41b639966 +382, 0x74f8b987a3d00ad1 +383, 0x5bce8526dc527981 +384, 0x94910926c172a379 +385, 0x503c45557688a9d5 +386, 0x244d03834e05807f +387, 0x6e014cbab9c7a31f +388, 0xae544c638530facf +389, 0x9b853aaaf9cbc22d +390, 0xfb42ab7024d060ed +391, 0x74cc3fba0dfd7ff2 +392, 0x24ec9e8f62144ad5 +393, 0x72f082954307bbe7 +394, 0x36feda21bbf67577 +395, 0x3222191611b832f1 +396, 0xd0584e81bcac8b0b +397, 0xdce8d793ef75e771 +398, 0x978824c6c2578fc +399, 0x6e8f77503b3c2ee4 +400, 0xc85d2d86fecf5d03 +401, 0x3d35b4a5d4d723c4 +402, 0xd3987dfd4727fff3 +403, 0xd3cde63fb6a31add +404, 0xf6699e86165bdaeb +405, 0x9d60ba158ec364c4 +406, 0x920c3c18b346bfc9 +407, 0x770fd1fdfbc236ca +408, 0x45998cfc5fc12ddd +409, 0xd74a3454e888834b +410, 0xbf2aa68081a4a28f +411, 0xea41b26a6f1da1b3 +412, 0x5560a2d24b9d5903 +413, 0xe3791f652a228d8b +414, 0x365116d3b5a8520c +415, 0xb1b2bd46528f8969 +416, 0xfcfe14943ef16ae7 +417, 0xf4d43425e8a535dc +418, 0xe6cf10a78782a7e0 +419, 0x9c7ac0de46556e3e +420, 0xc667ae0856eed9ef +421, 0x47dbb532e16f9c7e +422, 0xdf4785a5d89ee82e +423, 0xbd014925ce79dbcf +424, 0xea0d663fb58fa5be +425, 0x51af07d5cc3821fb +426, 0x27a1bdcdc4159a9d +427, 0x520c986c59b1e140 +428, 0x50b73fd9bacd5b39 +429, 0xae5240641f51e4f3 +430, 0x71faecc164ed9681 +431, 0xda95aa35529a7ee +432, 0xe25ba29b853c1c6d +433, 0x9871a925cda53735 +434, 0xde481ad8540e114d +435, 0xa2997f540e8abca0 +436, 0xc9683c5035e28185 +437, 0x1082471b57182bac +438, 0xbd3ecf0f0b788988 +439, 0xf479760776fbb342 +440, 0x3730929200d91f44 +441, 0xc1762d79ae72809c +442, 0xfaa0a4c7b1686cb3 +443, 0xd581e6d55afdafcd +444, 0x6cf57bdfba2dcf6d +445, 0xdef79d9fe6a5bcef +446, 0x13ed376e18132bd3 +447, 0xbe67efd72defa2a +448, 0x5acc176c468966ea +449, 0x8b35b626af139187 +450, 0x446de3fac0d973ac +451, 0xe1d49e06dc890317 +452, 0x817bc3fd21fc09b7 +453, 0xb71c3958a13d5579 +454, 0x8746e010f73d7148 +455, 0x1b61c06009922e83 +456, 0xba17e62e6b092316 +457, 0x1375fa23c4db8290 +458, 0x3f071230f51245a6 +459, 0x51c99a086a61cd13 +460, 0x5f0f2ae78589e1fd +461, 0x604834e114bbbc27 +462, 0x5eb2a7a34814e9a9 +463, 0x77a6907f386bf11e +464, 0x99525de2bd407eeb +465, 0xb818348c57b3b98f +466, 0x25f5f9e702fbe78d +467, 0x8f66669e6f884473 +468, 0x1e47d46e2af4f919 +469, 0xf6a19df846476833 +470, 0xff00c67bcd06621f +471, 0xe3dfe069795d72d8 +472, 0x8affc88b2fea4d73 +473, 0x66df747e5f827168 +474, 0xf368ec338d898a0e +475, 0x9e1f1a739c5984a2 +476, 0x46a1c90e1ca32cbc +477, 0xc261bc305ed8d762 +478, 0x754d7949f7da9e72 +479, 0x4c8fbbb14ef47b17 +480, 0xccbdc67a3848d80d +481, 0x3c25e6f58bae751d +482, 0x7078b163b936d9b6 +483, 0x440e27463c134ecf +484, 0x6c83ee39f324db0f +485, 0x27cf901b22aea535 +486, 0x57262dec79a3f366 +487, 0x91db09f1dbb524fb +488, 0xd7436eefba865df2 +489, 0x16c86b0a275a3f43 +490, 0x689493e6681deaa9 +491, 0x7e1dc536c1a9ac42 +492, 0x1145beac3ac7f5cc +493, 0x3d05e211a104b2b0 +494, 0x4f9e77ced3c52f44 +495, 0x53de1369354add72 +496, 0x1fb60f835f47cdeb +497, 0x6ab36f089e40c106 +498, 0xaabffcb0d3d04c7 +499, 0xaa399686d921bd25 +500, 0x2bf8dd8b6d6fa7f0 +501, 0x1ddbf4e124329613 +502, 0x466a740241466a72 +503, 0x98d7381eb68a761 +504, 0x817691510bc4857a +505, 0x8837622c0171fe33 +506, 0xcba078873179ee16 +507, 0x13adad1ab7b75af4 +508, 0x3bac3f502428840c +509, 0xbeb3cce138de9a91 +510, 0x30ef556e40b5f0b4 +511, 0x19c22abdf3bbb108 +512, 0x977e66ea4ddc7cf +513, 0x9f4a505f223d3bf3 +514, 0x6bc3f42ac79ec87b +515, 0x31e77712158d6c23 +516, 0x6d8de4295a28af0d +517, 0xee1807dbda72adb7 +518, 0xda54140179cd038f +519, 0x715aa5cdac38e062 +520, 0x5a7e55e99a22fa16 +521, 0xf190c36aa8edbe4f +522, 0xccadd93a82c1d044 +523, 0x7070e6d5012c3f15 +524, 0x50a83341a26c1ba5 +525, 0x11bca7cc634142e5 +526, 0x623a0d27867d8b04 +527, 0x75c18acff54fbf6e +528, 0x455ae7d933497a6f +529, 0xf624cf27d030c3d3 +530, 0x7a852716f8758bac +531, 0xe7a497ac1fa2b5b4 +532, 0xf84f097498f57562 +533, 0xc4bb392f87f65943 +534, 0x618e79a5d499fbfb +535, 0xb3c0b61d82b48b8 +536, 0x4750a10815c78ea7 +537, 0x9cf09cca3ddece69 +538, 0x2a69f1c94cc901a2 +539, 0x347a0e446e1ce86d +540, 0xb06f3a5a5ab37bb1 +541, 0x8035bd0713d591db +542, 0x539c9637042c3a1f +543, 0xd7ba4dc6b273cbd7 +544, 0x12f3f99933444f85 +545, 0x4a9517b9783fb9a4 +546, 0x6422b2ea95093bc5 +547, 0x3a5ecff0f996c2a6 +548, 0x31de504efc76a723 +549, 0x7ccb7c5233c21a9f +550, 0xc687d9e6ce4186e8 +551, 0x6e40769d6940376a +552, 0xf51207314f1f7528 +553, 0x67ee3acb190865e3 +554, 0xe08d586270588761 +555, 0xe387fa489af1a75c +556, 0x73414a52d29d8375 +557, 0x671a38191cf2a357 +558, 0xe00fb25b1aa54008 +559, 0x11a0610e22cf549b +560, 0xc90cc865d57c75be +561, 0x90d0863cc15f2b79 +562, 0x8b3e60d32ebcb856 +563, 0xb28cc55af621e04a +564, 0xcf60bd3cb2a5ab1d +565, 0x212cb5d421948f86 +566, 0xee297b96e0a3363f +567, 0x4e9392ff998760d1 +568, 0x61940c8d0105ba3e +569, 0x14ebcbae72a59a16 +570, 0xdf0f39a3d10c02af +571, 0xfc047b2b3c1c549d +572, 0x91718b5b98e3b286 +573, 0x9ea9539b1547d326 +574, 0x7a5a624a89a165e6 +575, 0x145b37dcaa8c4166 +576, 0x63814bbb90e5616c +577, 0xc4bc3ca6c38bb739 +578, 0x853c3a61ddc6626c +579, 0xa7ce8481c433829a +580, 0x8aff426941cc07b +581, 0x2dc3347ca68d8b95 +582, 0xce69f44f349e9917 +583, 0x2fa5cb8aca009b11 +584, 0xf26bb012115d9aca +585, 0xafa01c2f2d27235a +586, 0xabcba21f1b40305e +587, 0xfec20c896c0c1128 +588, 0xc5f7a71ebacadfa0 +589, 0xc8479ad14bab4eef +590, 0xad86ec9a3e7d3dc +591, 0xbbecd65292b915c5 +592, 0xb1f9e28149e67446 +593, 0x708d081c03dad352 +594, 0xaa8a84dbd1de916c +595, 0x9aa3efb29ba9480b +596, 0xd3c63969ff11443e +597, 0x1e9e9ac861315919 +598, 0x4fe227f91e66b41d +599, 0xefc0212d43d253ab +600, 0x98341437727c42d1 +601, 0x5ea85c0fe9008adc +602, 0x7891b15faa808613 +603, 0x32db2d63989aacfd +604, 0xc92f7f28e88fd7bc +605, 0x3513545eb6549475 +606, 0x49abe0082906fbf8 +607, 0xcee1e1a6551e729c +608, 0x38556672b592a28e +609, 0xc3e61409c4ec2d45 +610, 0x96c67ce2995a0fd4 +611, 0x9b9b0cada870293 +612, 0x82d6dd5dada48037 +613, 0xeea4f415299f1706 +614, 0x371107895f152ab3 +615, 0x2f6686159f4396bb +616, 0x61005a2ff3680089 +617, 0x9d2f2cafb595e6b6 +618, 0x4a812a920f011672 +619, 0x317554d3a77385d7 +620, 0x24c01086727eb74b +621, 0xa15ff76d618a3a9e +622, 0x2121bfd983859940 +623, 0x384d11577eea8114 +624, 0xab0f4299f3c44d88 +625, 0x136fd4b07cfa14d9 +626, 0x665fe45cbfaa972a +627, 0x76c5a23398a314e9 +628, 0x5507036357ccda98 +629, 0xd9b8c5ac9dce632b +630, 0x366bc71781da6e27 +631, 0xdd2b2ba1d6be6d15 +632, 0xf33ed0d50ea6f1a6 +633, 0xf05a9b1900174c18 +634, 0x3947e1419e2787cf +635, 0x6c742b1e029637d0 +636, 0x32aba12196a0d2e8 +637, 0x1b94aab2e82e7df +638, 0x68b617db19229d6 +639, 0x6c88a95ac0a33f98 +640, 0xdc9b95fd60c2d23e +641, 0x999e6971d3afc8b3 +642, 0x7071fc6ad8b60129 +643, 0x41a8184ef62485f6 +644, 0xb68e0605c7d5e713 +645, 0x272b961a1d1bbee +646, 0x23f04e76446187b0 +647, 0x999a7a8f6d33f260 +648, 0xdbd6318df4f168d +649, 0x8f5e74c84c40711e +650, 0x8ccc6b04393a19d6 +651, 0xadcd24b782dd8d3d +652, 0x1a966b4f80ef9499 +653, 0xcb6d4f9ff5a280f0 +654, 0x8095ff2b8484018a +655, 0xbfd3389611b8e771 +656, 0x278eb670b7d12d51 +657, 0x31df54ca8d65c20f +658, 0x121c7fb38af6985e +659, 0x84fb94f38fe1d0a +660, 0x15ae8af1a6d48f02 +661, 0x8d51e4a62cba1a28 +662, 0x58e6b6b3ae0f9e42 +663, 0x9365a0a85669cc99 +664, 0xe56e92f65a2106df +665, 0x68fa299c66b428fc +666, 0x55e51bb0b0a832c6 +667, 0x48b565293f9bc494 +668, 0x73d8132b1cbabb57 +669, 0x9178ac3926c36cbc +670, 0xe2f22c7b28ea5e0f +671, 0x6af45322a99afb12 +672, 0x59072fcb486a46f4 +673, 0x166b717b08d3d8e +674, 0xd4e627a2dfacc4ab +675, 0x33dad6f2921dedaa +676, 0x4b13b806834a6704 +677, 0xe5f7971b398ed54d +678, 0x20bfae65e3e6899b +679, 0x881dab45d2b4fc98 +680, 0x6f248126b5b885be +681, 0x7aeb39e986f9deee +682, 0xf819f9574b8c3a03 +683, 0xff3d93ed6bd9781a +684, 0x3a31e2e24a2f6385 +685, 0x7888a88f8944a5e +686, 0x4faee12f5de95537 +687, 0x7f3e4efccdb2ed67 +688, 0x91e0f2fc12593af5 +689, 0xb5be8a4b886a40d3 +690, 0x998e8288ac3a9b1b +691, 0x85c48fc8b1349e7b +692, 0xf03af25222d8fae5 +693, 0x45467e805b242c2e +694, 0xa2350db793dbebdc +695, 0xfebe5b61d2174553 +696, 0xa9a331f02c54ad0b +697, 0xe94e49a0f905aef3 +698, 0xe54b4c812b55e3da +699, 0xdc454114c6bc0278 +700, 0x99c7765ab476baa2 +701, 0xccd9590e47fdff7c +702, 0xfa2bcae7afd6cb71 +703, 0x2c1bf1a433a6f0f7 +704, 0x53882c62ff0aab28 +705, 0x80ac900f844dacc +706, 0x27ba8eb5c4a44d54 +707, 0x78f3dfb072a46004 +708, 0x34e00e6ec629edce +709, 0x5b88d19b552d1fbd +710, 0xe4df375dc79df432 +711, 0x37446312ff79c3b4 +712, 0xb72256900a95fa6d +713, 0x89f3171fbdff0bfc +714, 0xd37885b048687eba +715, 0xbb033213b283b60e +716, 0xcf10b523ee769030 +717, 0xbf8070b6cfd7bafb +718, 0xb7194da81fd1763b +719, 0xbfc303de88e68d24 +720, 0xb949c7a5aea8a072 +721, 0x844216e7bae90455 +722, 0xf1e7f20840049a33 +723, 0x96e3263ad0cae794 +724, 0x10772d51f6e9ba49 +725, 0xcea24fccae9d23b3 +726, 0xefd378add9dde040 +727, 0xba0c7c5275805976 +728, 0x2e2a04608f64fa8c +729, 0xafb42ec43aa0fa7 +730, 0x30444b84241ac465 +731, 0x19ef384bac4493ab +732, 0xfd1ac615d3ba5ab9 +733, 0x6cc781ba38643aff +734, 0x30ff27ebed875cfd +735, 0xee1a261aca97ae62 +736, 0xc5a92715202bc940 +737, 0x9e6ec76f93c657ff +738, 0x9b9fd55f55191ca5 +739, 0x654b13af008d8f03 +740, 0x1b7f030d9bd0719f +741, 0x6d622e277550cb7f +742, 0x3f8ee6b8830d0538 +743, 0x475462bcd0de190f +744, 0x21380e8a513bdbcd +745, 0x629bf3771b1bd7a4 +746, 0x3b5fd0b62c353709 +747, 0xf95634006ec3867e +748, 0x1be8bb584a6653c2 +749, 0x2e2d3cfa85320ce8 +750, 0x5b904b692252d11d +751, 0x4bfd76631d527990 +752, 0xc019571ca2bec4a0 +753, 0xf2eb730cea4cd751 +754, 0xd4571d709530191a +755, 0x3b5bd947061f5a7d +756, 0x56e2322cd2d1d1c0 +757, 0xa8830a5f62019f83 +758, 0x901d130c1b873cf3 +759, 0xb5dd29b363c61299 +760, 0xbb710bec3a17b26d +761, 0xc0c464daca0f2328 +762, 0x4dc8055df02650f5 +763, 0x3d3cd9bbe8b957af +764, 0xdb79612c2635b828 +765, 0xe25b3a8ad8fa3040 +766, 0xd5875c563cbf236b +767, 0x46861c1c3849c9bc +768, 0xf84bf1a2814dff43 +769, 0x6d8103902e0ad5e6 +770, 0x99f51c9be8af79e5 +771, 0xb0bfa8540ff94a96 +772, 0xaf45109a4e06f7d0 +773, 0x281df3e55aea9bfc +774, 0x6a1155ca8aa40e60 +775, 0x754d32c5de1f5da +776, 0xce1eafb1c6ca916f +777, 0xc4f2185fa8577bd1 +778, 0x4a188e9bdb5501d9 +779, 0xbb14107e99bd5550 +780, 0xf0381d8425ec2962 +781, 0x213dbfffc16ec4f6 +782, 0x7a999c5a28ea65bc +783, 0x23758c2aba7709ff +784, 0xea7e4bb205e93b44 +785, 0x9c5a31e53911c658 +786, 0x7f04d0bbdc689ddc +787, 0xe3ed89ab8d78dcb3 +788, 0x73c38bfb43986210 +789, 0x740c7d787eb8e158 +790, 0x5284fafdfb3fb9ec +791, 0x2e91a58ac1fb1409 +792, 0xb94a600bf0a09af3 +793, 0x533ea4dbe07d81dd +794, 0x48c3f1a736b3c5fd +795, 0x56ae3499fa8720ce +796, 0x526f2def663ca818 +797, 0x2f085759c65665c4 +798, 0xf715f042c69e0db4 +799, 0x110889c399231e60 +800, 0x64584a244866f3a0 +801, 0xf02ec101a39405d3 +802, 0xe73cd5e9a7f17283 +803, 0xfea64869e7028234 +804, 0x97559974ad877891 +805, 0xc8695aba1dc9f2e5 +806, 0x7b62b76ffc2264ec +807, 0xf5e1df172ec5ccd +808, 0xafaeb68765e443bd +809, 0xd3870eb2e8337623 +810, 0x4f944d684138fb39 +811, 0x6977c575038916ad +812, 0x8ada1a225df95a56 +813, 0xe4044c6c58d15e54 +814, 0x4e5121366681cf2 +815, 0xcf8640b079357b0d +816, 0xcd5b157d44106fa3 +817, 0x9d7a5481279e25a1 +818, 0xe10e9db41fb4b34f +819, 0x1052607be1eadff9 +820, 0x3403d67232fe2265 +821, 0xac9358f498c34afc +822, 0x820172da0dc39c9 +823, 0xe186e91a3b826b6a +824, 0x1a838e2a40284445 +825, 0x1870b617ebd7bce6 +826, 0xcb7cba4424be1ed7 +827, 0x6a2e56e40fdf9041 +828, 0xace93bbe108f97ee +829, 0xfeb9bc74ac41ca08 +830, 0x8cb2d05b0f6a1f51 +831, 0x73792309f3fac0a9 +832, 0x2507343d431308ca +833, 0xd0ea1197be615412 +834, 0xb1870812f1d2fa94 +835, 0x6d067b6935dcd23e +836, 0xaf161014e5492c31 +837, 0xd4be0dce97064be4 +838, 0xf8edfe3fc75c20f1 +839, 0x894751dc442d2d9c +840, 0xb4a95f6a6663456c +841, 0x74e93162e2d805db +842, 0x784bc5f3a7a2f645 +843, 0xd234d7c5b0582ea9 +844, 0x491f28d0ab6cb97c +845, 0xa79419e5cf4336c3 +846, 0x66b00141978c849 +847, 0xa7ddbd64698d563f +848, 0xefc33a4a5d97d4b2 +849, 0x95075514a65aebdc +850, 0x40eca5b3e28cd25e +851, 0x90ec7d00e9c9e35d +852, 0x63e84104d5af417a +853, 0xdaca0ea32df5744 +854, 0x7ed54f2587795881 +855, 0x5a73931760af4ee0 +856, 0x857d1a185a3081ec +857, 0x6eac2aabe67fb463 +858, 0xd1f86155d8bfc55f +859, 0x6d56398f3e7877ef +860, 0x7642f61dfc62bc17 +861, 0x1d76b12843246ffa +862, 0xde7817809b8a31d0 +863, 0xbcca9cd091198f9d +864, 0xf71ca566dddcdfd4 +865, 0xea4386ee8b61d082 +866, 0xe351729d6010bac4 +867, 0xfd685d8a49910dd6 +868, 0xa7a20ea6c686bd3 +869, 0x1cdaf82f4dbd5536 +870, 0xa3da1d1e77dda3e0 +871, 0x4f723b3818ff8b2a +872, 0x1290669eca152469 +873, 0xb54158b52d30651b +874, 0xc06b74f2c7f0fee +875, 0x7d5840bcbf702379 +876, 0x19fa4c1254a82ed +877, 0xcf5ce090ad0b38ea +878, 0xd4edd6ac9437e16d +879, 0xc6ebf25eb623b426 +880, 0xd2b6dbdf00d8fea2 +881, 0x949cf98391cc59e1 +882, 0x380a0c7d0356f7b3 +883, 0x8ffefe32465473bf +884, 0x637b6542d27c861e +885, 0x347d12ffc664ecd9 +886, 0xea66e3a0c75a6b37 +887, 0xc3aff6f34fb537a1 +888, 0x67bdf3579959bf49 +889, 0xa17a348e3a74b723 +890, 0x93c9ef26ddadd569 +891, 0x483909059a5ac0b2 +892, 0x26ec9074b56d5a0d +893, 0x6216000d9a48403a +894, 0x79b43909eab1ec05 +895, 0xe4a8e8d03649e0de +896, 0x1435d666f3ccdc08 +897, 0xb9e22ba902650a0e +898, 0x44dffcccc68b41f8 +899, 0x23e60dcc7a559a17 +900, 0x6fd1735eacd81266 +901, 0xf6bda0745ea20c8e +902, 0x85efcaefe271e07c +903, 0x9be996ee931cef42 +904, 0xe78b41c158611d64 +905, 0xd6201df605839830 +906, 0x702e8e47d2769fd3 +907, 0xb8dcf70e18cf14c +908, 0xac2690bab1bf5c17 +909, 0x92b166b71205d696 +910, 0xb0e73c795fc6df28 +911, 0x4bf2322c8b6b6f0d +912, 0xa842fbe67918cea0 +913, 0xb01a8675d9294e54 +914, 0xfbe3c94f03ca5af2 +915, 0x51a5c089600c441f +916, 0x60f0fd7512d85ded +917, 0xef3113d3bc2cadb0 +918, 0xe1ea128ade300d60 +919, 0xde413b7f8d92d746 +920, 0xfc32c6d43f47c5d8 +921, 0x69d551d8c2b54c68 +922, 0xb9bc68c175777943 +923, 0xb9c79c687f0dae90 +924, 0xd799421ef883c06e +925, 0xbff553ca95a29a3e +926, 0xfc9ffac46bd0aca1 +927, 0x4f6c3a30c80c3e5a +928, 0x8b7245bc6dc4a0a +929, 0xaf4e191a4575ff60 +930, 0x41218c4a76b90f0b +931, 0x986052aa51b8e89b +932, 0x284b464ed5622f9 +933, 0xba6bded912626b40 +934, 0x43cad3ed7443cb5c +935, 0x21641fa95725f328 +936, 0x6d99d6d09d755822 +937, 0x8246dfa2d4838492 +938, 0xd2ee70b9056f4726 +939, 0x87db515a786fbb8b +940, 0x7c63e4c1d7786e7d +941, 0xd1a9d548f10b3e88 +942, 0xa00856475f3b74c9 +943, 0x7f1964ce67148bf4 +944, 0x446650ec71e6018c +945, 0xb1805ca07d1b6345 +946, 0x869c0a1625b7271b +947, 0x79d6da06ce2ecfe2 +948, 0xec7b3cafc5e3c85f +949, 0x1745ce21e39f2c3d +950, 0xd9a0a7af6ee97825 +951, 0x680e0e52a6e11d5c +952, 0xd86b3f344ff7f4cd +953, 0xab56af117c840b9c +954, 0x5c5404c7e333a10e +955, 0x4f1eb462f35d990d +956, 0xf857605a5644458e +957, 0x3bb87cdf09262f86 +958, 0xd57295baf6da64b +959, 0xb5993f48472f2894 +960, 0x7d1a501608c060b2 +961, 0x45fabe2d0e54adf0 +962, 0xbb41c3806afb4efe +963, 0xbfbc506049424c8 +964, 0xb7dd6b67f2203344 +965, 0x389ce52eff883b81 +966, 0xe259c55c0cf6d000 +967, 0x70fb3e3824f7d213 +968, 0x9f36d5599ed55f4b +969, 0xd14cf6f12f83c4f7 +970, 0x570a09d56aaa0b66 +971, 0x8accafd527f4598 +972, 0xa42d64c62175adfd +973, 0xddb9c6a87b6e1558 +974, 0xd80b6c69fa1cde2a +975, 0x44ebaac10082207b +976, 0xf99be8889552fa1a +977, 0x38253cd4b38b5dc5 +978, 0x85356c8b02675791 +979, 0xbf91677b2ecdcf55 +980, 0x2316cb85e93f366e +981, 0x9abf35954db6b053 +982, 0xf49f7425e086b45a +983, 0x8f5b625e074afde2 +984, 0xe0d614559791b080 +985, 0xbf7b866afab2a525 +986, 0xde89d7e1641a6412 +987, 0x1d10687d8ae5b86f +988, 0x1f034caa0e904cbd +989, 0x2086357aec8a7a2c +990, 0x22dc476b80c56e1e +991, 0xbef5a73cc0e3a493 +992, 0xddfa3829b26ed797 +993, 0x8917a87ec3d4dc78 +994, 0xfeabe390628c365e +995, 0x581b0c4f6fb2d642 +996, 0x1ef8c590adbf5b9a +997, 0x4d8e13aac0cce879 +998, 0xfe38f71e5977fad0 +999, 0x1f83a32d4adfd2ed diff --git a/numpy/random/tests/data/philox-testset-2.csv b/numpy/random/tests/data/philox-testset-2.csv new file mode 100644 index 000000000000..69d24c38c289 --- /dev/null +++ b/numpy/random/tests/data/philox-testset-2.csv @@ -0,0 +1,1001 @@ +seed, 0x0 +0, 0x399e5b222b82fa9 +1, 0x41fd08c1f00f3bc5 +2, 0x78b8824162ee4d04 +3, 0x176747919e02739d +4, 0xfaa88f002a8d3596 +5, 0x418eb6f592e6c227 +6, 0xef83020b8344dd45 +7, 0x30a74a1a6eaa064b +8, 0x93d43bf97a490c3 +9, 0xe4ba28b442194cc +10, 0xc829083a168a8656 +11, 0x73f45d50f8e22849 +12, 0xf912db57352824cc +13, 0xf524216927b12ada +14, 0x22b7697473b1dfda +15, 0x311e2a936414b39f +16, 0xb905abfdcc425be6 +17, 0x4b14630d031eac9c +18, 0x1cf0c4ae01222bc8 +19, 0xa6c33efc6e82ef3 +20, 0x43b3576937ba0948 +21, 0x1e483d17cdde108a +22, 0x6722784cac11ac88 +23, 0xee87569a48fc45d7 +24, 0xb821dcbe74d18661 +25, 0xa5d1876ef3da1a81 +26, 0xe4121c2af72a483 +27, 0x2d747e355a52cf43 +28, 0x609059957bd03725 +29, 0xc3327244b49e16c5 +30, 0xb5ae6cb000dde769 +31, 0x774315003209017 +32, 0xa2013397ba8db605 +33, 0x73b228945dbcd957 +34, 0x801af7190375d3c0 +35, 0xae6dca29f24c9c67 +36, 0xd1cc0bcb1ca26249 +37, 0x1defa62a5bd853be +38, 0x67c2f5557fa89462 +39, 0xf1729b58122fab02 +40, 0xb67eb71949ec6c42 +41, 0x5456366ec1f8f7d7 +42, 0x44492b32eb7966f5 +43, 0xa801804159f175f1 +44, 0x5a416f23cac70d84 +45, 0x186f55293302303d +46, 0x7339d5d7b6a43639 +47, 0xfc6df38d6a566121 +48, 0xed2fe018f150b39e +49, 0x508e0b04a781fa1b +50, 0x8bee9d50f32eaf50 +51, 0x9870015d37e63cc +52, 0x93c6b12309c14f2d +53, 0xb571cf798abe93ff +54, 0x85c35a297a88ae6e +55, 0x9b1b79afe497a2ae +56, 0x1ca02e5b95d96b8d +57, 0x5bb695a666c0a94a +58, 0x4e3caf9bbab0b208 +59, 0x44a44be1a89f2dc1 +60, 0x4ff37c33445758d1 +61, 0xd0e02875322f35da +62, 0xfd449a91fb92646b +63, 0xbe0b49096b95db4d +64, 0xffa3647cad13ef5d +65, 0x75c127a61acd10c8 +66, 0xd65f697756f5f98e +67, 0x3ced84be93d94434 +68, 0x4da3095c2fc46d68 +69, 0x67564e2a771ee9ac +70, 0x36944775180644a9 +71, 0xf458db1c177cdb60 +72, 0x5b58406dcd034c8 +73, 0x793301a3fdab2a73 +74, 0x1c2a1a16d6db6128 +75, 0xc2dacd4ddddbe56c +76, 0x2e7d15be2301a111 +77, 0xd4f4a6341b3bcd18 +78, 0x3622996bbe6a9e3b +79, 0xaf29aa9a7d6d47da +80, 0x6d7dbb74a4cd68ae +81, 0xc260a17e0f39f841 +82, 0xdee0170f2af66f0d +83, 0xf84ae780d7b5a06e +84, 0x8326247b73f43c3a +85, 0xd44eef44b4f98b84 +86, 0x3d10aee62ec895e3 +87, 0x4f23fef01bf703b3 +88, 0xf8e50aa57d888df6 +89, 0x7da67411e3bef261 +90, 0x1d00f2769b2f96d7 +91, 0x7ef9a15b7444b84e +92, 0xcfa16436cc2b7e21 +93, 0x29ab8cfac00460ff +94, 0x23613de8608b0e70 +95, 0xb1aa0980625798a8 +96, 0xb9256fd29db7df99 +97, 0xdacf311bf3e7fa18 +98, 0xa013c8f9fada20d8 +99, 0xaf5fd4fe8230fe3e +100, 0xd3d59ca55102bc5c +101, 0x9d08e2aa5242767f +102, 0x40278fe131e83b53 +103, 0x56397d03c7c14c98 +104, 0xe874b77b119359b3 +105, 0x926a1ba4304ab19f +106, 0x1e115d5aa695a91d +107, 0xc6a459df441f2fe3 +108, 0x2ca842bc1b0b3c6a +109, 0x24c804cf8e5eed16 +110, 0x7ca00fc4a4c3ebd3 +111, 0x546af7cecc4a4ba6 +112, 0x8faae1fa18fd6e3 +113, 0x40420b0089641a6a +114, 0x88175a35d9abcb83 +115, 0xf7d746d1b8b1357c +116, 0x7dae771a651be970 +117, 0x2f6485247ee4df84 +118, 0x6883702fab2d8ec5 +119, 0xeb7eea829a67f9a6 +120, 0x60d5880b485562ed +121, 0x7d4ca3d7e41a4e7e +122, 0xbb7fef961ab8de18 +123, 0x3b92452fb810c164 +124, 0x5f4b4755348b338 +125, 0xca45a715a7539806 +126, 0xc33efd9da5399dd +127, 0x593d665a51d4aedd +128, 0x75d6b8636563036b +129, 0x7b57caa55e262082 +130, 0x4ede7427969e0dd5 +131, 0xc3f19b6f78ea00b +132, 0xeea7bab9be2181ea +133, 0x652c45fe9c420c04 +134, 0x14ba9e3d175670ee +135, 0xd2ad156ba6490474 +136, 0x4d65ae41065f614 +137, 0x6ff911c8afa28eb1 +138, 0xedc2b33588f3cb68 +139, 0x437c8bc324666a2f +140, 0x828cee25457a3f0 +141, 0x530c986091f31b9b +142, 0x2f34671e8326ade7 +143, 0x4f686a8f4d77f6da +144, 0xa4c1987083498895 +145, 0xbce5a88b672b0fb1 +146, 0x8476115a9e6a00cc +147, 0x16de18a55dd2c238 +148, 0xdf38cf4c416232bc +149, 0x2cb837924e7559f3 +150, 0xfad4727484e982ed +151, 0x32a55d4b7801e4f +152, 0x8b9ef96804bd10a5 +153, 0xa1fd422c9b5cf2a9 +154, 0xf46ddb122eb7e442 +155, 0x6e3842547afa3b33 +156, 0x863dee1c34afe5c4 +157, 0x6a43a1935b6db171 +158, 0x1060a5c2f8145821 +159, 0xf783ec9ed34c4607 +160, 0x1da4a86bf5f8c0b0 +161, 0x4c7714041ba12af8 +162, 0x580da7010be2f192 +163, 0xad682fe795a7ea7a +164, 0x6687b6cb88a9ed2c +165, 0x3c8d4b175517cd18 +166, 0xe9247c3a524a6b6b +167, 0x337ca9cfaa02658 +168, 0xed95399481c6feec +169, 0x58726a088e606062 +170, 0xfe7588a5b4ee342a +171, 0xee434c7ed146fdee +172, 0xe2ade8b60fdc4ba5 +173, 0xd57e4c155de4eaab +174, 0xdefeae12de1137cb +175, 0xb7a276a241316ac1 +176, 0xeb838b1b1df4ca15 +177, 0x6f78965edea32f6f +178, 0x18bebd264d7a5d53 +179, 0x3641c691d77005ec +180, 0xbe70ed7efea8c24c +181, 0x33047fa8d03ca560 +182, 0x3bed0d2221ff0f87 +183, 0x23083a6ffbcf38a2 +184, 0xc23eb827073d3fa5 +185, 0xc873bb3415e9fb9b +186, 0xa4645179e54147fe +187, 0x2c72fb443f66e207 +188, 0x98084915dd89d8f4 +189, 0x88baa2de12c99037 +190, 0x85c74ab238cb795f +191, 0xe122186469ea3a26 +192, 0x4c3bba99b3249292 +193, 0x85d6845d9a015234 +194, 0x147ddd69c13e6a31 +195, 0x255f4d678c9a570b +196, 0x2d7c0c410bf962b4 +197, 0x58eb7649e0aa16ca +198, 0x9d240bf662fe0783 +199, 0x5f74f6fa32d293cc +200, 0x4928e52f0f79d9b9 +201, 0xe61c2b87146b706d +202, 0xcfcd90d100cf5431 +203, 0xf15ea8138e6aa178 +204, 0x6ab8287024f9a819 +205, 0xed8942593db74e01 +206, 0xefc00e4ec2ae36dd +207, 0xc21429fb9387f334 +208, 0xf9a3389e285a9bce +209, 0xacdee8c43aae49b3 +210, 0xefc382f02ad55c25 +211, 0x1153b50e8d406b72 +212, 0xb00d39ebcc2f89d8 +213, 0xde62f0b9831c8850 +214, 0xc076994662eef6c7 +215, 0x66f08f4752f1e3ef +216, 0x283b90619796249a +217, 0x4e4869bc4227499e +218, 0xb45ad78a49efd7ed +219, 0xffe19aa77abf5f4b +220, 0xfce11a0daf913aef +221, 0x7e4e64450d5cdceb +222, 0xe9621997cfd62762 +223, 0x4d2c9e156868081 +224, 0x4e2d96eb7cc9a08 +225, 0xda74849bba6e3bd3 +226, 0x6f4621da935e7fde +227, 0xb94b914aa0497259 +228, 0xd50d03e8b8db1563 +229, 0x1a45c1ce5dca422e +230, 0xc8d30d33276f843f +231, 0xb57245774e4176b4 +232, 0x8d36342c05abbbb1 +233, 0x3591ad893ecf9e78 +234, 0x62f4717239ee0ac8 +235, 0x9b71148a1a1d4200 +236, 0x65f8e0f56dd94463 +237, 0x453b1fcfd4fac8c2 +238, 0x4c25e48e54a55865 +239, 0xa866baa05112ace2 +240, 0x7741d3c69c6e79c5 +241, 0x7deb375e8f4f7a8a +242, 0xc242087ede42abd8 +243, 0x2fa9d1d488750c4b +244, 0xe8940137a935d3d3 +245, 0x1dab4918ca24b2f2 +246, 0xe2368c782168fe3e +247, 0x6e8b2d1d73695909 +248, 0x70455ebea268b33e +249, 0x656a919202e28da1 +250, 0x5a5a8935647da999 +251, 0x428c6f77e118c13c +252, 0xa87aee2b675bb083 +253, 0x3873a6412b239969 +254, 0x5f72c1e91cb8a2ee +255, 0xa25af80a1beb5679 +256, 0x1af65d27c7b4abc3 +257, 0x133437060670e067 +258, 0xb1990fa39a97d32e +259, 0x724adc89ae10ed17 +260, 0x3f682a3f2363a240 +261, 0x29198f8dbd343499 +262, 0xdfaeeaa42bc51105 +263, 0x5baff3901b9480c2 +264, 0x3f760a67043e77f5 +265, 0x610fa7aa355a43ba +266, 0x394856ac09c4f7a7 +267, 0x1d9229d058aee82e +268, 0x19c674804c41aeec +269, 0x74cf12372012f4aa +270, 0xa5d89b353fa2f6ca +271, 0x697e4f672ac363dd +272, 0xde6f55ba73df5af9 +273, 0x679cf537510bd68f +274, 0x3dc916114ae9ef7e +275, 0xd7e31a66ec2ee7ba +276, 0xc21bebb968728495 +277, 0xc5e0781414e2adfd +278, 0x71147b5412ddd4bd +279, 0x3b864b410625cca9 +280, 0x433d67c0036cdc6 +281, 0x48083afa0ae20b1b +282, 0x2d80beecd64ac4e8 +283, 0x2a753c27c3a3ee3e +284, 0xb2c5e6afd1fe051a +285, 0xea677930cd66c46b +286, 0x4c3960932f92810a +287, 0xf1b367a9e527eaba +288, 0xb7d92a8a9a69a98e +289, 0x9f9ad3210bd6b453 +290, 0x817f2889db2dcbd8 +291, 0x4270a665ac15813c +292, 0x90b85353bd2be4dd +293, 0x10c0460f7b2d68d +294, 0x11cef32b94f947f5 +295, 0x3cf29ed8e7d477e8 +296, 0x793aaa9bd50599ef +297, 0xbac15d1190014aad +298, 0x987944ae80b5cb13 +299, 0x460aa51f8d57c484 +300, 0xc77df0385f97c2d3 +301, 0x92e743b7293a3822 +302, 0xbc3458bcfbcbb8c0 +303, 0xe277bcf3d04b4ed7 +304, 0xa537ae5cf1c9a31c +305, 0x95eb00d30bd8cfb2 +306, 0x6376361c24e4f2dd +307, 0x374477fe87b9ea8e +308, 0x8210f1a9a039902e +309, 0xe7628f7031321f68 +310, 0x8b8e9c0888fc1d3d +311, 0x306be461fdc9e0ed +312, 0x510009372f9b56f5 +313, 0xa6e6fa486b7a027a +314, 0x9d3f002025203b5a +315, 0x7a46e0e81ecbef86 +316, 0x41e280c611d04df0 +317, 0xedcec10418a99e8a +318, 0x5c27b6327e0b9dbd +319, 0xa81ed2035b509f07 +320, 0x3581e855983a4cc4 +321, 0x4744594b25e9809d +322, 0xc737ac7c27fbd0ed +323, 0x1b523a307045433a +324, 0x8b4ce9171076f1d9 +325, 0x2db02d817cd5eec0 +326, 0x24a1f1229af50288 +327, 0x5550c0dcf583ff16 +328, 0x3587baaa122ec422 +329, 0xf9d3dc894229e510 +330, 0xf3100430d5cf8e87 +331, 0xc31af79862f8e2fb +332, 0xd20582063b9f3537 +333, 0xac5e90ac95fcc7ad +334, 0x107c4c704d5109d4 +335, 0xebc8628906dbfd70 +336, 0x215242776da8c531 +337, 0xa98002f1dcf08b51 +338, 0xbc3bdc07f3b09718 +339, 0x238677062495b512 +340, 0x53b4796f2a3c49e8 +341, 0x6424286467e22f0e +342, 0x14d0952a11a71bac +343, 0x2f97098149b82514 +344, 0x3777f2fdc425ad2 +345, 0xa32f2382938876d4 +346, 0xda8a39a021f20ae3 +347, 0x364361ef0a6ac32c +348, 0x4413eede008ff05a +349, 0x8dda8ace851aa327 +350, 0x4303cabbdcecd1ee +351, 0x2e69f06d74aa549f +352, 0x4797079cd4d9275c +353, 0xc7b1890917e98307 +354, 0x34031b0e822a4b4c +355, 0xfc79f76b566303ea +356, 0x77014adbe255a930 +357, 0xab6c43dd162f3be5 +358, 0xa430041f3463f6b9 +359, 0x5c191a32ada3f84a +360, 0xe8674a0781645a31 +361, 0x3a11cb667b8d0916 +362, 0xaedc73e80c39fd8a +363, 0xfde12c1b42328765 +364, 0x97abb7dcccdc1a0b +365, 0x52475c14d2167bc8 +366, 0x540e8811196d5aff +367, 0xa867e4ccdb2b4b77 +368, 0x2be04af61e5bcfb9 +369, 0x81b645102bfc5dfd +370, 0x96a52c9a66c6450f +371, 0x632ec2d136889234 +372, 0x4ed530c0b36a6c25 +373, 0x6f4851225546b75 +374, 0x2c065d6ba46a1144 +375, 0xf8a3613ff416551d +376, 0xb5f0fd60e9c971a9 +377, 0x339011a03bb4be65 +378, 0x9439f72b6995ded6 +379, 0xc1b03f3ef3b2292d +380, 0xad12fd221daab3ae +381, 0xf615b770f2cf996f +382, 0x269d0fdcb764172 +383, 0x67837025e8039256 +384, 0x6402831fc823fafa +385, 0x22854146a4abb964 +386, 0x7b5ad9b5a1bad7a8 +387, 0x67170e7beb6ac935 +388, 0xfc2d1e8e24adfaaa +389, 0x7ded4395345ff40d +390, 0x418981760a80dd07 +391, 0xc03bef38022c1d2 +392, 0x3a11850b26eade29 +393, 0xaa56d02c7175c5f4 +394, 0xd83b7917b9bfbff5 +395, 0x3c1df2f8fa6fced3 +396, 0xf3d6e2999c0bb760 +397, 0xc66d683a59a950e3 +398, 0x8e3972a9d73ffabf +399, 0x97720a0443edffd9 +400, 0xa85f5d2fe198444a +401, 0xfc5f0458e1b0de5e +402, 0xe3973f03df632b87 +403, 0xe151073c84c594b3 +404, 0x68eb4e22e7ff8ecf +405, 0x274f36eaed7cae27 +406, 0x3b87b1eb60896b13 +407, 0xbe0b2f831442d70a +408, 0x2782ed7a48a1b328 +409, 0xb3619d890310f704 +410, 0xb03926b11b55921a +411, 0xdb46fc44aa6a0ce4 +412, 0x4b063e2ef2e9453a +413, 0xe1584f1aeec60fb5 +414, 0x7092bd6a879c5a49 +415, 0xb84e1e7c7d52b0e6 +416, 0x29d09ca48db64dfb +417, 0x8f6c4a402066e905 +418, 0x77390795eabc36b +419, 0xcc2dc2e4141cc69f +420, 0x2727f83beb9e3c7c +421, 0x1b29868619331de0 +422, 0xd38c571e192c246f +423, 0x535327479fe37b6f +424, 0xaff9ce5758617eb3 +425, 0x5658539e9288a4e4 +426, 0x8df91d87126c4c6d +427, 0xe931cf8fdba6e255 +428, 0x815dfdf25fbee9e8 +429, 0x5c61f4c7cba91697 +430, 0xdd5f5512fe2313a1 +431, 0x499dd918a92a53cd +432, 0xa7e969d007c97dfd +433, 0xb8d39c6fc81ac0bb +434, 0x1d646983def5746c +435, 0x44d4b3b17432a60c +436, 0x65664232a14db1e3 +437, 0xda8fae6433e7500b +438, 0xbe51b94ff2a3fe94 +439, 0xe9b1bd9a9098ef9f +440, 0xfe47d54176297ef5 +441, 0xb8ab99bc03bb7135 +442, 0xcfad97f608565b38 +443, 0xf05da71f6760d9c1 +444, 0xef8da40a7c70e7b +445, 0xe0465d58dbd5d138 +446, 0xb54a2d70eb1a938 +447, 0xfdd50c905958f2d8 +448, 0x3c41933c90a57d43 +449, 0x678f6d894c6ad0bb +450, 0x403e8f4582274e8 +451, 0x5cbbe975668df6b0 +452, 0x297e6520a7902f03 +453, 0x8f6dded33cd1efd7 +454, 0x8e903c97be8d783b +455, 0x10bd015577e30f77 +456, 0x3fcd69d1c36eab0c +457, 0xb45989f3ca198d3 +458, 0x507655ce02b491a9 +459, 0xa92cf99bb78602ce +460, 0xebfb82055fbc2f0f +461, 0x3334256279289b7a +462, 0xc19d2a0f740ee0ac +463, 0x8bb070dea3934905 +464, 0xa4ab57d3a8d1b3eb +465, 0xfee1b09bcacf7ff4 +466, 0xccc7fb41ceec41fa +467, 0xd4da49094eb5a74d +468, 0xed5c693770af02ed +469, 0x369dabc9bbfaa8e4 +470, 0x7eab9f360d054199 +471, 0xe36dbebf5ee94076 +472, 0xd30840e499b23d7 +473, 0x8678e6cb545015ff +474, 0x3a47932ca0b336e +475, 0xeb7c742b6e93d6fe +476, 0x1404ea51fe5a62a9 +477, 0xa72cd49db978e288 +478, 0xfd7bada020173dcf +479, 0xc9e74fc7abe50054 +480, 0x93197847bb66808d +481, 0x25fd5f053dce5698 +482, 0xe198a9b18cc21f4 +483, 0x5cc27b1689452d5d +484, 0x8b3657af955a98dc +485, 0xc17f7584f54aa1c0 +486, 0xe821b088246b1427 +487, 0x32b5a9f6b45b6fa0 +488, 0x2aef7c315c2bae0c +489, 0xe1af8129846b705a +490, 0x4123b4c091b34614 +491, 0x6999d61ec341c073 +492, 0x14b9a8fcf86831ea +493, 0xfd4cff6548f46c9f +494, 0x350c3b7e6cc8d7d6 +495, 0x202a5047fecafcd5 +496, 0xa82509fe496bb57d +497, 0x835e4b2608b575fe +498, 0xf3abe3da919f54ec +499, 0x8705a21e2c9b8796 +500, 0xfd02d1427005c314 +501, 0xa38458faa637f49b +502, 0x61622f2360e7622a +503, 0xe89335a773c2963b +504, 0x481264b659b0e0d0 +505, 0x1e82ae94ebf62f15 +506, 0x8ea7812de49209d4 +507, 0xff963d764680584 +508, 0x418a68bef717f4af +509, 0x581f0e7621a8ab91 +510, 0x840337e9a0ec4150 +511, 0x951ef61b344be505 +512, 0xc8b1b899feb61ec2 +513, 0x8b78ca13c56f6ed9 +514, 0x3d2fd793715a946f +515, 0xf1c04fabcd0f4084 +516, 0x92b602614a9a9fcc +517, 0x7991bd7a94a65be7 +518, 0x5dead10b06cad2d7 +519, 0xda7719b33f722f06 +520, 0x9d87a722b7bff71e +521, 0xb038e479071409e9 +522, 0xf4e8bbec48054775 +523, 0x4fec2cd7a28a88ea +524, 0x839e28526aad3e56 +525, 0xd37ec57852a98bf0 +526, 0xdef2cbbe00f3a02d +527, 0x1aecfe01a9e4d801 +528, 0x59018d3c8beaf067 +529, 0x892753e6ac8bf3cd +530, 0xefdd3437023d2d1c +531, 0x447bfbd148c8cb88 +532, 0x282380221bd442b8 +533, 0xfce8658d1347384a +534, 0x60b211a7ec6bfa8 +535, 0xd21729cfcc692974 +536, 0x162087ecd5038a47 +537, 0x2b17000c4bce39d2 +538, 0x3a1f75ff6adcdce0 +539, 0x721a411d312f1a2c +540, 0x9c13b6133f66934d +541, 0xaa975d14978980e5 +542, 0x9403dbd4754203fa +543, 0x588c15762fdd643 +544, 0xdd1290f8d0ada73a +545, 0xd9b77380936103f4 +546, 0xb2e2047a356eb829 +547, 0x7019e5e7f76f7a47 +548, 0x3c29a461f62b001d +549, 0xa07dc6cfab59c116 +550, 0x9b97e278433f8eb +551, 0x6affc714e7236588 +552, 0x36170aeb32911a73 +553, 0x4a665104d364a789 +554, 0x4be01464ec276c9c +555, 0x71bb10271a8b4ecf +556, 0xbf62e1d068bc018 +557, 0xc9ada5db2cbbb413 +558, 0x2bded75e726650e5 +559, 0x33d5a7af2f34385d +560, 0x8179c46661d85657 +561, 0x324ebcfd29267359 +562, 0xac4c9311dc9f9110 +563, 0xc14bb6a52f9f9c0 +564, 0xc430abe15e7fb9db +565, 0xf1cce5c14df91c38 +566, 0x651e3efa2c0750d3 +567, 0x38a33604a8be5c75 +568, 0x7aaf77fe7ff56a49 +569, 0xc0d1cc56bbf27706 +570, 0x887aa47324e156c6 +571, 0x12547c004b085e8d +572, 0xd86a8d6fbbbfd011 +573, 0x57c860188c92d7b4 +574, 0xcd5d3843d361b8ca +575, 0x8f586ef05a9cb3ef +576, 0x174456e1ba6267d5 +577, 0xf5dc302c62fe583c +578, 0xa349442fabcdb71 +579, 0xe5123c1a8b6fd08e +580, 0x80681552aa318593 +581, 0xb295396deaef1e31 +582, 0xabb626e0b900e32b +583, 0xf024db8d3f19c15e +584, 0x1d04bb9548e2fb6c +585, 0xd8ed2b2214936c2b +586, 0x618ca1e430a52bc9 +587, 0xccbca44a6088136b +588, 0xd0481855c8b9ccbe +589, 0x3c92a2fade28bdf7 +590, 0x855e9fefc38c0816 +591, 0x1269bbfe55a7b27c +592, 0x1d6c853d83726d43 +593, 0xc8655511cc7fcafc +594, 0x301503eb125a9b0e +595, 0xb3108e4532016b11 +596, 0xbb7ab6245da9cb3d +597, 0x18004c49116d85eb +598, 0x3480849c20f61129 +599, 0xe28f45157463937b +600, 0x8e85e61060f2ce1 +601, 0x1673da4ec589ba5e +602, 0x74b9a6bd1b194712 +603, 0xed39e147fa8b7601 +604, 0x28ce54019102ca77 +605, 0x42e0347f6d7a2f30 +606, 0xb6a908d1c4814731 +607, 0x16c3435e4e9a126d +608, 0x8880190514c1ad54 +609, 0xfffd86229a6f773c +610, 0x4f2420cdb0aa1a93 +611, 0xf8e1acb4120fc1fa +612, 0x63a8c553ab36a2f2 +613, 0x86b88cf3c0a6a190 +614, 0x44d8b2801622c792 +615, 0xf6eae14e93082ff1 +616, 0xd9ed4f5d1b8fac61 +617, 0x1808ce17f4e1f70 +618, 0x446e83ea336f262f +619, 0xc7c802b04c0917b7 +620, 0x626f45fd64968b73 +621, 0x9ffa540edc9b2c5c +622, 0xa96a1e219e486af8 +623, 0x2bb8963884e887a1 +624, 0xba7f68a5d029e3c4 +625, 0xefc45f44392d9ca0 +626, 0x98d77762503c5eab +627, 0xd89bcf62f2da627c +628, 0xa3cab8347f833151 +629, 0xa095b7595907d5c7 +630, 0x3b3041274286181 +631, 0xb518db8919eb71fa +632, 0x187036c14fdc9a36 +633, 0xd06e28301e696f5d +634, 0xdbc71184e0c56492 +635, 0xfe51e9cae6125bfd +636, 0x3b12d17cd014df24 +637, 0x3b95e4e2c986ac1a +638, 0x29c1cce59fb2dea2 +639, 0x58c05793182a49d6 +640, 0xc016477e330d8c00 +641, 0x79ef335133ada5d +642, 0x168e2cad941203f3 +643, 0xf99d0f219d702ef0 +644, 0x655628068f8f135b +645, 0xdcdea51910ae3f92 +646, 0x8e4505039c567892 +647, 0x91a9ec7e947c89ae +648, 0x8717172530f93949 +649, 0x1c80aba9a440171a +650, 0x9c8f83f6ebe7441e +651, 0x6c05e1efea4aa7f9 +652, 0x10af696b777c01b +653, 0x5892e9d9a92fc309 +654, 0xd2ba7da71e709432 +655, 0x46378c7c3269a466 +656, 0x942c63dfe18e772c +657, 0x6245cf02ef2476f +658, 0x6f265b2759ea2aea +659, 0x5aa757f17d17f4a6 +660, 0x1ad6a3c44fa09be6 +661, 0xe861af14e7015fb8 +662, 0x86be2e7db388c77 +663, 0x5c7bba32b519e9a0 +664, 0x3feb314850c4437b +665, 0x97955add60cfb45b +666, 0xfdb536230a540bdc +667, 0xdac9d7bf6e58512e +668, 0x4894c00e474e8120 +669, 0xa1918a37739da366 +670, 0xa8097f2096532807 +671, 0x592afe50e6c5e643 +672, 0xd69050ee6dcb33dc +673, 0xa6956b262dd3c561 +674, 0x1a55c815555e63f7 +675, 0x2ec7fd37516de2bb +676, 0x8ec251d9c70e76ba +677, 0x9b76e4abafd2689 +678, 0x9ce3f5c751a57df1 +679, 0x915c4818bf287bc7 +680, 0x2293a0d1fe07c735 +681, 0x7627dcd5d5a66d3d +682, 0xb5e4f92cc49c7138 +683, 0x6fc51298731d268c +684, 0xd19800aa95441f87 +685, 0x14f70f31162fa115 +686, 0x41a3da3752936f59 +687, 0xbec0652be95652ee +688, 0x7aa4bdb1020a290f +689, 0x4382d0d9bee899ef +690, 0xe6d988ae4277d6ff +691, 0xe618088ccb2a32d1 +692, 0x411669dfaa899e90 +693, 0x234e2bf4ba76d9f +694, 0xe109fe4cb7828687 +695, 0x1fb96b5022b0b360 +696, 0x6b24ad76c061a716 +697, 0x7e1781d4d7ecee15 +698, 0xf20c2dbe82ba38ba +699, 0xeda8e8ae1d943655 +700, 0xa58d196e2a77eaec +701, 0x44564765a5995a0b +702, 0x11902fe871ecae21 +703, 0x2ea60279900e675d +704, 0x38427227c18a9a96 +705, 0xe0af01490a1b1b48 +706, 0x826f91997e057824 +707, 0x1e57308e6e50451 +708, 0xb42d469bbbfdc350 +709, 0xb9734cff1109c49b +710, 0x98967559bb9d364f +711, 0xd6be360041907c12 +712, 0xa86a1279122a1e21 +713, 0x26f99a8527bfc698 +714, 0xfa8b85758f28f5d6 +715, 0xe3057429940806ae +716, 0x4bee2d7e84f93b2b +717, 0x948350a76ea506f4 +718, 0xa139154488045e74 +719, 0x8893579ba5e78085 +720, 0x5f21c215c6a9e397 +721, 0x456134f3a59641dc +722, 0x92c0273f8e97a9c6 +723, 0xd2936c9c3f0c6936 +724, 0xcfa4221e752c4735 +725, 0x28cd5a7457355dca +726, 0xecdfdde23d90999f +727, 0x60631b2d494d032b +728, 0xf67289df269a827f +729, 0xcbe8011ef0f5b7ef +730, 0x20eea973c70a84f5 +731, 0xbe1fd200398557ce +732, 0xd2279ee030191bba +733, 0xf2bd4291dedaf819 +734, 0xfc6d167dbe8c402 +735, 0x39ac298da5d0044b +736, 0xceac026f5f561ce +737, 0x10a5b0bdd8ad60e6 +738, 0xdeb3c626df6d4bcb +739, 0x3c128962e77ff6ca +740, 0xc786262e9c67a0e5 +741, 0x4332855b3febcdc0 +742, 0x7bda9724d1c0e020 +743, 0x6a8c93399bc4df22 +744, 0xa9b20100ac707396 +745, 0xa11a3458502c4eb5 +746, 0xb185461c60478941 +747, 0x13131d56195b7ff6 +748, 0x8d55875ddbd4aa1c +749, 0xc09b67425f469aa5 +750, 0x39e33786cc7594c4 +751, 0x75e96db8e4b08b93 +752, 0xda01cd12a3275d1e +753, 0x2c49e7822344fab5 +754, 0x9bd5f10612514ca7 +755, 0x1c801a5c828e7332 +756, 0x29797d3f4f6c7b4c +757, 0xac992715e21e4e53 +758, 0xe40e89ee887ddb37 +759, 0x15189a2b265a783b +760, 0xa854159a52af5c5 +761, 0xb9d8a5a81c12bead +762, 0x3240cdc9d59e2a58 +763, 0x1d0b872234cf8e23 +764, 0xc01224cf6ce12cff +765, 0x2601e9f3905c8663 +766, 0xd4ecf9890168d6b4 +767, 0xa45db796d89bfdd5 +768, 0x9f389406dad64ab4 +769, 0xa5a851adce43ffe3 +770, 0xd0962c41c26e5aa9 +771, 0x8a671679e48510a4 +772, 0xc196dc0924a6bfeb +773, 0x3ead661043b549cb +774, 0x51af4ca737d405ac +775, 0xf4425b5c62275fb6 +776, 0x71e69d1f818c10f5 +777, 0xacaf4af2d3c70162 +778, 0x2e1f1d4fd7524244 +779, 0xe54fdd8f388890e8 +780, 0xfda0d33e84eb2b83 +781, 0x53965c5e392b81da +782, 0x5c92288267263097 +783, 0xcac1b431c878c66c +784, 0x36c0e1cf417241c6 +785, 0x5cc4d9cd1a36bf2c +786, 0x32e4257bb5d3e470 +787, 0x4aecff904adb44fb +788, 0x4d91a8e0d1d60cac +789, 0xa3b478388385b038 +790, 0x48d955f24eba70be +791, 0x310e4deb07f24f68 +792, 0x8853e73b1f30a5a +793, 0x278aee45c2a65c5 +794, 0xf6932eedbd62fb0b +795, 0xafb95958c82fafad +796, 0x78e807c18616c16c +797, 0xd7abadda7488ed9f +798, 0x2dd72e2572aa2ae6 +799, 0x6ec3791982c2be09 +800, 0x6865bb314fac478f +801, 0xa14dc0ce09000d1a +802, 0xb8081ad134da10f2 +803, 0xc4ac1534aa825ef5 +804, 0xd83aeb48ae2d538f +805, 0x38052027e3074be4 +806, 0xa9833e06ef136582 +807, 0x4f02d790ec9fd78 +808, 0xec2f60bc711c5bdc +809, 0x9253b0d12268e561 +810, 0xa8ac607fdd62c206 +811, 0x895e28ebc920289f +812, 0xe2fd42b154243ac7 +813, 0xc69cac2f776eee19 +814, 0xf4d4ac11db56d0dc +815, 0xa8d37049b9f39833 +816, 0x75abbf8a196c337c +817, 0xb115bb76750d27b8 +818, 0x39426d187839154 +819, 0xd488423e7f38bf83 +820, 0xbb92e0c76ecb6a62 +821, 0x3055a018ce39f4e3 +822, 0xc93fe0e907729bfb +823, 0x65985d17c5863340 +824, 0x2088ae081b2028e1 +825, 0x6e628de873314057 +826, 0x864377cccf573f0e +827, 0xae03f4c9aa63d132 +828, 0xb1db766d6404c66d +829, 0xdce5a22414a374b +830, 0x622155b777819997 +831, 0x69fe96e620371f3c +832, 0xa9c67dbc326d94fc +833, 0x932a84ae5dd43bab +834, 0xe2301a20f6c48c3f +835, 0x795d2e79c6477300 +836, 0xd8e3e631289521e7 +837, 0xae2684979002dfd6 +838, 0xc9c2392377550f89 +839, 0xa1b0c99d508ef7ec +840, 0x593aef3c5a5272ec +841, 0xe32e511a4b7162cd +842, 0xab3b81655f5a2857 +843, 0x1b535e1a0aaf053e +844, 0x5b33f56c1b6a07e2 +845, 0x782dc8cfcac4ef36 +846, 0xb3d4f256eecfd202 +847, 0xf73a6598f58c4f7e +848, 0xd5722189524870ae +849, 0x707878de6b995fc0 +850, 0xc3eb6ba73e3d7e8a +851, 0xca75c017655b75a7 +852, 0x1b29369ea3541e5f +853, 0x352e98858bdb58a3 +854, 0x1e4412d184b6b27d +855, 0x2d375ba0304b2d17 +856, 0x56c30fce69a5d08e +857, 0x6b8c2b0c06584bda +858, 0xde4dfff228c8c91f +859, 0xb7c9edd574e6287f +860, 0xf6078281c9fca2b2 +861, 0xb9b9a51de02a2f1e +862, 0xa411bef31c0103b0 +863, 0xc5facd8fc5e1d7a3 +864, 0x54e631c05ddf7359 +865, 0x815b42b3fd06c474 +866, 0xc9ac07566fda18ec +867, 0xd84ea62957bd8e15 +868, 0x5575f74b5cfd8803 +869, 0x5779a8d460c2e304 +870, 0xfd6e87e264a85587 +871, 0xa1d674daa320b26d +872, 0x2c3c3ec64b35afc4 +873, 0x393a274ff03e6935 +874, 0x1f40ecbac52c50ea +875, 0xc3de64fa324ffc0c +876, 0x56ae828b7f9deb04 +877, 0xe7c1a77b5c1f2cb3 +878, 0xa4c4aab19ea921cc +879, 0xec164c238825822c +880, 0xa6a3304770c03b03 +881, 0x3a63641d5b1e8123 +882, 0x42677be3a54617ef +883, 0xa2680423e3a200c0 +884, 0x8b17cf75f3f37277 +885, 0xe7ce65a49242be3d +886, 0x7f85934271323e4b +887, 0xcfb0f431f79a4fab +888, 0x392e4041a8505b65 +889, 0xd3e5daf0d8b25ea6 +890, 0x9447eff675d80f53 +891, 0xea27a9d53cfaeea8 +892, 0xe3f2335945a83ba +893, 0x8875a43ce216413b +894, 0xe49941f9eabce33e +895, 0x9357c1296683a5b1 +896, 0xf0f16439e81ee701 +897, 0x3181515295ffd79a +898, 0x9d7150fffd169ed8 +899, 0x2d6a1d281e255a72 +900, 0x81bf1286fb3a92b6 +901, 0x566d3079b499e279 +902, 0xc7939ca8f047341 +903, 0xb1f8050e7c2d59f6 +904, 0x605701045e7be192 +905, 0x51b73360e8e31a1c +906, 0x9f4ad54483ba9fe0 +907, 0xd3085b8fcf69d1c8 +908, 0xc3e7475026dc5f0b +909, 0x5800f8554b157354 +910, 0x37dfdf858cfcd963 +911, 0x3a1fce05ce385072 +912, 0xf495c062645c20c3 +913, 0xdcbeec2c3492c773 +914, 0xc38f427589d1d0b4 +915, 0x681ead60216a8184 +916, 0x4bd569c40cc88c41 +917, 0x49b0d442e130b7a2 +918, 0xee349156b7d1fa3f +919, 0x2bde2d2db055135b +920, 0xc6a460d2fbcb2378 +921, 0xd0f170494ff3dbb +922, 0xb294422492528a23 +923, 0xfc95873c854e7b86 +924, 0x6c9c3ad1797bb19c +925, 0xe0c06f2aab65062d +926, 0x58e32ce0f11e3a81 +927, 0xa745fcd729ff5036 +928, 0x599b249b2fc2cdb2 +929, 0x78f23b5b0dd5b082 +930, 0x6de3e957f549ecfc +931, 0x9d0712fa6d878756 +932, 0x9076e8554e4a413a +933, 0xf3185818c0294de8 +934, 0x5de7cdf4b455b9b6 +935, 0xb15f6908ed703f7d +936, 0x98c654dfedc6818 +937, 0x120502ab0e93ae42 +938, 0x67966a98a58dc120 +939, 0x1caa0fc628989482 +940, 0xd8b2c3cd480a8625 +941, 0x85c70071b3aed671 +942, 0xff385f8473714662 +943, 0xe2868e4bf3773b63 +944, 0x96cf8019b279298e +945, 0x8511cc930bd74800 +946, 0x5312e48fdd55f5ab +947, 0xfcdae564b52df78d +948, 0x9eee48373e652176 +949, 0x953788f6bcbc56b0 +950, 0xd1a3855dbd2f6b37 +951, 0x3ad32acf77f4d1e9 +952, 0x917c7be81b003e30 +953, 0x9ce817da1e2e9dfb +954, 0x2968983db162d44d +955, 0x1e005decef5828ad +956, 0xc38fe59d1aa4f3d5 +957, 0xf357f1710dc02f1d +958, 0x2613912a4c83ec67 +959, 0x832a11470b9a17cb +960, 0x5e85508a611f0dad +961, 0x2781131677f59d56 +962, 0xa82358d7d4b0237f +963, 0xfbf8b3cc030c3af6 +964, 0x68b2f68ac8a55adb +965, 0x3b6fcf353add0ada +966, 0xd1956049bcd15bd5 +967, 0x95b76f31c7f98b6d +968, 0x814b6690df971a84 +969, 0xdcf7959cddd819e4 +970, 0xcf8c72c5d804fc88 +971, 0x56883769c8945a22 +972, 0x1f034652f658cf46 +973, 0x41df1324cda235a1 +974, 0xeccd32524504a054 +975, 0x974e0910a04ec02c +976, 0x72104507b821f6db +977, 0x791f8d089f273044 +978, 0xe0f79a4f567f73c3 +979, 0x52fe5bea3997f024 +980, 0x5f8b9b446494f78 +981, 0xfd9f511947059190 +982, 0x3aea9dac6063bce3 +983, 0xbfdae4dfc24aee60 +984, 0xa82cdbbf0a280318 +985, 0xf460aae18d70aa9d +986, 0x997367cb204a57c4 +987, 0x616e21ab95ba05ef +988, 0x9bfc93bec116769f +989, 0x2b2ee27c37a3fa5b +990, 0xb25c6ed54006ee38 +991, 0xab04d4a5c69e69a5 +992, 0x6d2f6b45f2d8438f +993, 0x4ad2f32afc82f092 +994, 0x513d718908f709c0 +995, 0x5272aadc4fffca51 +996, 0xeb3f87e66156ef5d +997, 0xf8a3d5a46a86ba85 +998, 0xdb4548a86f27abfd +999, 0x57c05f47ff62380d diff --git a/numpy/random/tests/data/sfc64-testset-1.csv b/numpy/random/tests/data/sfc64-testset-1.csv new file mode 100644 index 000000000000..4fffe69591fe --- /dev/null +++ b/numpy/random/tests/data/sfc64-testset-1.csv @@ -0,0 +1,1001 @@ +seed, 0xdeadbeaf +0, 0xa475f55fbb6bc638 +1, 0xb2d594b6c29d971c +2, 0x275bc4ece4484fb1 +3, 0x569be72d9b3492fb +4, 0x89a5bb9b206a670c +5, 0xd951bfa06afdc3f9 +6, 0x7ee2e1029d52a265 +7, 0x12ef1d4de0cb4d4c +8, 0x41658ba8f0ef0280 +9, 0x5b650c82e4fe09c5 +10, 0x638a9f3e30ec4e94 +11, 0x147487fb2ba9233e +12, 0x89ef035603d2d1fb +13, 0xe66ca57a190e6cbe +14, 0x330f673740dd61fc +15, 0xc71d3dce2f8bb34e +16, 0x3c07c39ff150b185 +17, 0x5df952b6cae8f099 +18, 0x9f09f2b1f0ceac80 +19, 0x19598eee2d0c4c67 +20, 0x64e06483702e0ebd +21, 0xda04d1fdb545f7fa +22, 0xf2cf53b61a0c4f9b +23, 0xf0bb724ce196f66e +24, 0x71cefde55d9cf0f +25, 0x6323f62824a20048 +26, 0x1e93604680f14b4e +27, 0xd9d8fad1d4654025 +28, 0xf4ee25af2e76ca08 +29, 0x6af3325896befa98 +30, 0xad9e43abf5e04053 +31, 0xbf930e318ce09de3 +32, 0x61f9583b4f9ffe76 +33, 0x9b69d0b3d5ec8958 +34, 0xa608f250f9b2ca41 +35, 0x6fdba7073dc2bb5d +36, 0xa9d57601efea6d26 +37, 0xc24a88a994954105 +38, 0xc728b1f78d88fe5b +39, 0x88da88c2b083b3b2 +40, 0xa9e27f7303c76cfd +41, 0xc4c24608c29176eb +42, 0x5420b58466b972fd +43, 0xd2018a661b6756c8 +44, 0x7caed83d9573fc7 +45, 0x562a3d81b849a06a +46, 0x16588af120c21f2c +47, 0x658109a7e0eb4837 +48, 0x877aabb14d3822e1 +49, 0x95704c342c3745fe +50, 0xeeb8a0dc81603616 +51, 0x431bf94889290419 +52, 0xe4a9410ab92a5863 +53, 0xbc6be64ea60f12ba +54, 0x328a2da920015063 +55, 0x40f6b3bf8271ae07 +56, 0x4068ff00a0e854f8 +57, 0x1b287572ca13fa78 +58, 0xa11624a600490b99 +59, 0x4a04ef29eb7150fa +60, 0xcc9469ab5ffb739 +61, 0x99a6a9f8d95e782 +62, 0x8e90356573e7a070 +63, 0xa740b8fb415c81c4 +64, 0x47eccef67447f3da +65, 0x2c720afe3a62a49b +66, 0xe2a747f0a43eacf4 +67, 0xba063a87ab165576 +68, 0xbc1c78ed27feb5a3 +69, 0x285a19fa3974f9d +70, 0x489c61e704f5f0e3 +71, 0xf5ab04f6b03f238b +72, 0x7e25f88138a110dd +73, 0xc3d1cef3d7c1f1d1 +74, 0xc3de6ec64d0d8e00 +75, 0x73682a15b6cc5088 +76, 0x6fecbeb319163dc5 +77, 0x7e100d5defe570a1 +78, 0xad2af9af076dce57 +79, 0x3c65100e23cd3a9a +80, 0x4b442cc6cfe521bb +81, 0xe89dc50f8ab1ef75 +82, 0x8b3c6fdc2496566 +83, 0xdfc50042bc2c308c +84, 0xe39c5f158b33d2b2 +85, 0x92f6adefdfeb0ac +86, 0xdf5808a949c85b3e +87, 0x437384021c9dace9 +88, 0xa7b5ed0d3d67d8f +89, 0xe1408f8b21da3c34 +90, 0xa1bba125c1e80522 +91, 0x7611dc4710385264 +92, 0xb00a46ea84082917 +93, 0x51bf8002ffa87cef +94, 0x9bb81013e9810adc +95, 0xd28f6600013541cd +96, 0xc2ca3b1fa7791c1f +97, 0x47f9ad58f099c82c +98, 0x4d1bb9458469caf9 +99, 0xca0b165b2844257 +100, 0xc3b2e667d075dc66 +101, 0xde22f71136a3dbb1 +102, 0x23b4e3b6f219e4c3 +103, 0x327e0db4c9782f66 +104, 0x9365506a6c7a1807 +105, 0x3e868382dedd3be7 +106, 0xff04fa6534bcaa99 +107, 0x96621a8862995305 +108, 0x81bf39cb5f8e1df7 +109, 0x79b684bb8c37af7a +110, 0xae3bc073c3cde33c +111, 0x7805674112c899ac +112, 0xd95a27995abb20f2 +113, 0x71a503c57b105c40 +114, 0x5ff00d6a73ec8acc +115, 0x12f96391d91e47c2 +116, 0xd55ca097b3bd4947 +117, 0x794d79d20468b04 +118, 0x35d814efb0d7a07d +119, 0xfa9ac9bd0aae76d3 +120, 0xa77b8a3711e175cd +121, 0xe6694fbf421f9489 +122, 0xd8f1756525a1a0aa +123, 0xe38dfa8426277433 +124, 0x16b640c269bbcd44 +125, 0x2a7a5a67ca24cfeb +126, 0x669039c28d5344b4 +127, 0x2a445ee81fd596bb +128, 0x600df94cf25607e0 +129, 0x9358561a7579abff +130, 0xee1d52ea179fc274 +131, 0x21a8b325e89d31be +132, 0x36fc0917486eec0a +133, 0x3d99f40717a6be9f +134, 0x39ac140051ca55ff +135, 0xcef7447c26711575 +136, 0xf22666870eff441d +137, 0x4a53c6134e1c7268 +138, 0xd26de518ad6bdb1b +139, 0x1a736bf75b8b0e55 +140, 0xef1523f4e6bd0219 +141, 0xb287b32fd615ad92 +142, 0x2583d6af5e841dd5 +143, 0x4b9294aae7ca670c +144, 0xf5aa4a84174f3ca9 +145, 0x886300f9e0dc6376 +146, 0x3611401e475ef130 +147, 0x69b56432b367e1ac +148, 0x30c330e9ab36b7c4 +149, 0x1e0e73079a85b8d5 +150, 0x40fdfc7a5bfaecf +151, 0xd7760f3e8e75a085 +152, 0x1cc1891f7f625313 +153, 0xeece1fe6165b4272 +154, 0xe61111b0c166a3c1 +155, 0x2f1201563312f185 +156, 0xfd10e8ecdd2a57cb +157, 0x51cdc8c9dd3a89bf +158, 0xed13cc93938b5496 +159, 0x843816129750526b +160, 0xd09995cd6819ada +161, 0x4601e778d40607df +162, 0xef9df06bd66c2ea0 +163, 0xae0bdecd3db65d69 +164, 0xbb921a3c65a4ae9a +165, 0xd66698ce8e9361be +166, 0xacdc91647b6068f4 +167, 0xe505ef68f2a5c1c0 +168, 0xd6e62fd27c6ab137 +169, 0x6a2ba2c6a4641d86 +170, 0x9c89143715c3b81 +171, 0xe408c4e00362601a +172, 0x986155cbf5d4bd9d +173, 0xb9e6831728c893a7 +174, 0xb985497c3bf88d8c +175, 0xd0d729214b727bec +176, 0x4e557f75fece38a +177, 0x6572067fdfd623ca +178, 0x178d49bb4d5cd794 +179, 0xe6baf59f60445d82 +180, 0x5607d53518e3a8d2 +181, 0xba7931adb6ebbd61 +182, 0xe853576172611329 +183, 0xe945daff96000c44 +184, 0x565b9ba3d952a176 +185, 0xcdb54d4f88c584c8 +186, 0x482a7499bee9b5e5 +187, 0x76560dd0affe825b +188, 0x2a56221faa5ca22c +189, 0x7729be5b361f5a25 +190, 0xd6f2195795764876 +191, 0x59ef7f8f423f18c5 +192, 0x7ebefed6d02adde1 +193, 0xcfec7265329c73e5 +194, 0x4fd8606a5e59881c +195, 0x95860982ae370b73 +196, 0xdecfa33b1f902acc +197, 0xf9b8a57400b7c0a6 +198, 0xd20b822672ec857b +199, 0x4eb81084096c7364 +200, 0xe535c29a44d9b6ad +201, 0xdef8b48ebacb2e29 +202, 0x1063bc2b8ba0e915 +203, 0xe4e837fb53d76d02 +204, 0x4df935db53579fb8 +205, 0xa30a0c8053869a89 +206, 0xe891ee58a388a7b5 +207, 0x17931a0c64b8a985 +208, 0xaf2d350b494ce1b3 +209, 0x2ab9345ffbcfed82 +210, 0x7de3fe628a2592f0 +211, 0x85cf54fab8b7e79d +212, 0x42d221520edab71b +213, 0x17b695b3af36c233 +214, 0xa4ffe50fe53eb485 +215, 0x1102d242db800e4d +216, 0xc8dc01f0233b3b6 +217, 0x984a030321053d36 +218, 0x27fa8dc7b7112c0e +219, 0xba634dd8294e177f +220, 0xe67ce34b36332eb +221, 0x8f1351e1894fb41a +222, 0xb522a3048761fd30 +223, 0xc350ad9bc6729edc +224, 0xe0ed105bd3c805e1 +225, 0xa14043d2b0825aa7 +226, 0xee7779ce7fc11fdf +227, 0xc0fa8ba23a60ab25 +228, 0xb596d1ce259afbad +229, 0xaa9b8445537fdf62 +230, 0x770ab2c700762e13 +231, 0xe812f1183e40cc1 +232, 0x44bc898e57aefbbd +233, 0xdd8a871df785c996 +234, 0x88836a5e371eb36b +235, 0xb6081c9152623f27 +236, 0x895acbcd6528ca96 +237, 0xfb67e33ddfbed435 +238, 0xaf7af47d323ce26 +239, 0xe354a510c3c39b2d +240, 0x5cacdedda0672ba3 +241, 0xa440d9a2c6c22b09 +242, 0x6395099f48d64304 +243, 0xc11cf04c75f655b5 +244, 0x1c4e054d144ddb30 +245, 0x3e0c2db89d336636 +246, 0x127ecf18a5b0b9a7 +247, 0x3b50551a88ea7a73 +248, 0xbd27003e47f1f684 +249, 0xf32d657782baac9b +250, 0x727f5cabf020bc9 +251, 0x39c1c1c226197dc7 +252, 0x5552c87b35deeb69 +253, 0x64d54067b5ce493f +254, 0x3494b091fe28dda0 +255, 0xdf0278bc85ee2965 +256, 0xdef16fec25efbd66 +257, 0xe2be09f578c4ce28 +258, 0xd27a9271979d3019 +259, 0x427f6fcd71845e3 +260, 0x26b52c5f81ec142b +261, 0x98267efc3986ad46 +262, 0x7bf4165ddb7e4374 +263, 0xd05f7996d7941010 +264, 0x3b3991de97b45f14 +265, 0x9068217fb4f27a30 +266, 0xd8fe295160afc7f3 +267, 0x8a159fab4c3bc06f +268, 0x57855506d19080b6 +269, 0x7636df6b3f2367a4 +270, 0x2844ee3abd1d5ec9 +271, 0xe5788de061f51c16 +272, 0x69e78cc9132a164 +273, 0xacd53cde6d8cd421 +274, 0xb23f3100068e91da +275, 0x4140070a47f53891 +276, 0xe4a422225a96e53a +277, 0xb82a8925a272a2ac +278, 0x7c2f9573590fe3b7 +279, 0xbaf80764db170575 +280, 0x955abffa54358368 +281, 0x355ce7460614a869 +282, 0x3700ede779a4afbf +283, 0x10a6ec01d92d68cd +284, 0x3308f5a0a4c0afef +285, 0x97b892d7601136c9 +286, 0x4955c3b941b8552e +287, 0xca85aa67e941961d +288, 0xb1859ae5db28e9d2 +289, 0x305d072ac1521fbd +290, 0xed52a868996085bb +291, 0x723bfa6a76358852 +292, 0x78d946ecd97c5fb3 +293, 0x39205b30a8e23e79 +294, 0xb927e3d086baadbe +295, 0xa18d6946136e1ff5 +296, 0xdab6f0b51c1eb5ff +297, 0xf0a640bf7a1af60c +298, 0xf0e81db09004d0d4 +299, 0xfe76cebdbe5a4dde +300, 0x2dafe9cc3decc376 +301, 0x4c871fdf1af34205 +302, 0xe79617d0c8fa893b +303, 0xee658aaad3a141f7 +304, 0xfd91aa74863e19f1 +305, 0x841b8f55c103cc22 +306, 0x22766ed65444ad5d +307, 0x56d03d1beca6c17a +308, 0x5fd4c112c92036ae +309, 0x75466ae58a5616dc +310, 0xfbf98b1081e802a9 +311, 0xdc325e957bf6d8f5 +312, 0xb08da7015ebd19b7 +313, 0xf25a9c0944f0c073 +314, 0xf4625bafa0ced718 +315, 0x4349c9e093a9e692 +316, 0x75a9ccd4dd8935cb +317, 0x7e6cf9e539361e91 +318, 0x20fdd22fb6edd475 +319, 0x5973021b57c2311f +320, 0x75392403667edc15 +321, 0xed9b2156ea70d9f1 +322, 0xf40c114db50b64a0 +323, 0xe26bb2c9eef20c62 +324, 0x409c1e3037869f03 +325, 0xcdfd71fdda3b7f91 +326, 0xa0dfae46816777d6 +327, 0xde060a8f61a8deb8 +328, 0x890e082a8b0ca4fc +329, 0xb9f2958eddf2d0db +330, 0xd17c148020d20e30 +331, 0xffdc9cc176fe7201 +332, 0xffb83d925b764c1 +333, 0x817ea639e313da8d +334, 0xa4dd335dd891ca91 +335, 0x1342d25a5e81f488 +336, 0xfa7eb9c3cf466b03 +337, 0xfe0a423d44b185d0 +338, 0x101cfd430ab96049 +339, 0x7b5d3eda9c4504b +340, 0xe20ccc006e0193f1 +341, 0xf54ccddedebc5df0 +342, 0xc0edd142bd58f1db +343, 0x3831f40d378d2430 +344, 0x80132353f0a88289 +345, 0x688f23c419d03ef8 +346, 0x4c6837e697884066 +347, 0x699387bb2e9a3a8f +348, 0x8996f860342448d8 +349, 0xb0f80dff99bfa5cc +350, 0x3e927a7f9ea12c8e +351, 0xd7e498d1e5f9dff3 +352, 0x78ecb97bb3f864cc +353, 0x3c4ffd069a014d38 +354, 0xf8d5073a1e09b4d4 +355, 0x8717e854f9faef23 +356, 0xfbcc5478d8d0ad7 +357, 0xd3cd8b233ca274ff +358, 0x8bd8f11f79beb265 +359, 0xf64498a832d8fd0e +360, 0xb01bba75112131ec +361, 0x55572445a7869781 +362, 0x7b56622f18cb3d7a +363, 0x7f192c9e075bdb83 +364, 0xd9a112f836b83ff3 +365, 0x68673b37269653dc +366, 0xe46a9433fb6a0879 +367, 0x127d756ca4779001 +368, 0xc1378e8b1e8eab94 +369, 0x1006edb0f51d078c +370, 0xc6dd53961232d926 +371, 0x9a4aeef44038256d +372, 0xd357f4fa652d4f5f +373, 0x59f3d2cc3378598 +374, 0xe76e6207a824a7fc +375, 0x5fc5e33712ceffef +376, 0x77d24aeb0ccb1adc +377, 0x5be4b2826805659e +378, 0x257c69d787e64634 +379, 0x58dd52ca6bc727b1 +380, 0x3ab997767235ea33 +381, 0x986a2a7a966fad14 +382, 0xc900f8b27761dcc4 +383, 0x44991bdb13795700 +384, 0xe5c145a4fe733b2 +385, 0x56f041b56bffe0d3 +386, 0x5779c4fef8067996 +387, 0xa0fe8748e829532d +388, 0x840c1277d78d9dd4 +389, 0x37ebcb315432acbc +390, 0xf4bc8738433ba3be +391, 0x8b122993f2e10062 +392, 0xe1fe8481f2681ed5 +393, 0x8e23f1630d9f494a +394, 0xda24661a01b7d0b3 +395, 0x7a02942a179cee36 +396, 0xf1e08a3c09b71ac +397, 0x3dec2cc7ee0bd8fd +398, 0x1f3e480113d805d4 +399, 0xc061b973ad4e3f2c +400, 0x6bea750f17a66836 +401, 0xbc2add72eac84c25 +402, 0xcff058d3f97934ca +403, 0x54ccc30987778ec2 +404, 0x93449ec1e1469558 +405, 0xe2ff369eb0c6836 +406, 0x41c2df2d63bf8e55 +407, 0xf9302629b6c71be2 +408, 0xdd30376b8e5ab29a +409, 0x12db9e04f911d754 +410, 0x8d03d6cd359f1b97 +411, 0xe15956511abf1cee +412, 0x9b68e10e2c2fd940 +413, 0x2e28de6491c1ce53 +414, 0x52b329b72d0c109d +415, 0xc2c0b115f9da2a60 +416, 0x6ca084105271bbff +417, 0x49b92b8676058c1e +418, 0x767fc92a70f7e5a3 +419, 0x87ba4ed4b65a6aa0 +420, 0xf70b052e0a3975e9 +421, 0x3e925c3306db9eec +422, 0x43253f1d96ac9513 +423, 0xe3e04f1a1ea454c4 +424, 0x763e3f4cc81ba0c8 +425, 0x2a2721ac69265705 +426, 0xdf3b0ac6416ea214 +427, 0xa6a6b57450f3e000 +428, 0xc3d3b1ac7dbfe6ac +429, 0xb66e5e6f7d2e4ec0 +430, 0x43c65296f98f0f04 +431, 0xdb0f6e3ff974d842 +432, 0x3d6b48e02ebb203b +433, 0xd74674ebf09d8f27 +434, 0xbe65243c58fc1200 +435, 0x55eb210a68d42625 +436, 0x87badab097dbe883 +437, 0xada3fda85a53824f +438, 0xef2791e8f48cd37a +439, 0x3fe7fceb927a641a +440, 0xd3bffd3ff031ac78 +441, 0xb94efe03da4d18fb +442, 0x162a0ad8da65ea68 +443, 0x300f234ef5b7e4a6 +444, 0xa2a8b4c77024e4fb +445, 0x5950f095ddd7b109 +446, 0xded66dd2b1bb02ba +447, 0x8ec24b7fa509bcb6 +448, 0x9bede53d924bdad6 +449, 0xa9c3f46423be1930 +450, 0x6dfc90597f8de8b4 +451, 0xb7419ebc65b434f0 +452, 0xa6596949238f58b9 +453, 0x966cbade640829b8 +454, 0x58c74877bdcbf65e +455, 0xaa103b8f89b0c453 +456, 0x219f0a86e41179a4 +457, 0x90f534fc06ddc57f +458, 0x8db7cdd644f1affa +459, 0x38f91de0167127ac +460, 0xdcd2a65e4df43daa +461, 0x3e04f34a7e01f834 +462, 0x5b237eea68007768 +463, 0x7ff4d2b015921768 +464, 0xf786b286549d3d51 +465, 0xaefa053fc2c3884c +466, 0x8e6a8ff381515d36 +467, 0x35b94f3d0a1fce3c +468, 0x165266d19e9abb64 +469, 0x1deb5caa5f9d8076 +470, 0x13ab91290c7cfe9d +471, 0x3651ca9856be3e05 +472, 0xe7b705f6e9cccc19 +473, 0xd6e7f79668c127ed +474, 0xa9faf37154896f92 +475, 0x89fbf190603e0ab1 +476, 0xb34d155a86f942d0 +477, 0xb2d4400a78bfdd76 +478, 0x7c0946aca8cfb3f0 +479, 0x7492771591c9d0e8 +480, 0xd084d95c5ca2eb28 +481, 0xb18d12bd3a6023e +482, 0xea217ed7b864d80b +483, 0xe52f69a755dd5c6f +484, 0x127133993d81c4aa +485, 0xe07188fcf1670bfb +486, 0x178fbfe668e4661d +487, 0x1c9ee14bb0cda154 +488, 0x8d043b96b6668f98 +489, 0xbc858986ec96ca2b +490, 0x7660f779d528b6b7 +491, 0xd448c6a1f74ae1d3 +492, 0x178e122cfc2a6862 +493, 0x236f000abaf2d23b +494, 0x171b27f3f0921915 +495, 0x4c3ff07652f50a70 +496, 0x18663e5e7d3a66ca +497, 0xb38c97946c750cc9 +498, 0xc5031aae6f78f909 +499, 0x4d1514e2925e95c1 +500, 0x4c2184a741dabfbb +501, 0xfd410364edf77182 +502, 0xc228157f863ee873 +503, 0x9856fdc735cc09fc +504, 0x660496cd1e41d60e +505, 0x2edf1d7e01954c32 +506, 0xd32e94639bdd98cf +507, 0x8e153f48709a77d +508, 0x89357f332d2d6561 +509, 0x1840d512c97085e6 +510, 0x2f18d035c9e26a85 +511, 0x77b88b1448b26d5b +512, 0xc1ca6ef4cdae0799 +513, 0xcc203f9e4508165f +514, 0xeaf762fbc9e0cbbe +515, 0xc070c687f3c4a290 +516, 0xd49ed321068d5c15 +517, 0x84a55eec17ee64ee +518, 0x4d8ee685298a8871 +519, 0x9ff5f17d7e029793 +520, 0x791d7d0d62e46302 +521, 0xab218b9114e22bc6 +522, 0x4902b7ab3f7119a7 +523, 0x694930f2e29b049e +524, 0x1a3c90650848999f +525, 0x79f1b9d8499c932b +526, 0xfacb6d3d55e3c92f +527, 0x8fd8b4f25a5da9f5 +528, 0xd037dcc3a7e62ae7 +529, 0xfecf57300d8f84f4 +530, 0x32079b1e1dc12d48 +531, 0xe5f8f1e62b288f54 +532, 0x97feba3a9c108894 +533, 0xd279a51e1899a9a0 +534, 0xd68eea8e8e363fa8 +535, 0x7394cf2deeca9386 +536, 0x5f70b0c80f1dbf10 +537, 0x8d646916ed40462 +538, 0xd253bb1c8a12bbb6 +539, 0x38f399a821fbd73e +540, 0x947523a26333ac90 +541, 0xb52e90affbc52a37 +542, 0xcf899cd964654da4 +543, 0xdf66ae9cca8d99e7 +544, 0x6051478e57c21b6a +545, 0xffa7dc975af3c1da +546, 0x195c7bff2d1a8f5 +547, 0x64f12b6575cf984d +548, 0x536034cb842cf9e1 +549, 0x180f247ce5bbfad +550, 0x8ced45081b134867 +551, 0x532bbfdf426710f3 +552, 0x4747933e74c4f54d +553, 0x197a890dc4793401 +554, 0x76c7cc2bd42fae2 +555, 0xdabfd67f69675dd0 +556, 0x85c690a68cdb3197 +557, 0xe482cec89ce8f92 +558, 0x20bc9fb7797011b1 +559, 0x76dc85a2185782ad +560, 0x3df37c164422117a +561, 0x99211f5d231e0ab0 +562, 0xef7fd794a0a91f4 +563, 0x419577151915f5fe +564, 0x3ce14a0a7135dae3 +565, 0x389b57598a075d6a +566, 0x8cc2a9d51b5af9aa +567, 0xe80a9beffbd13f13 +568, 0x65e96b22ea8a54d8 +569, 0x79f38c4164138ede +570, 0xd1955846cba03d81 +571, 0x60359fe58e4f26d6 +572, 0x4ea724f585f8d13e +573, 0x316dfdbadc801a3c +574, 0x20aa29b7c6dd66fe +575, 0x65eaf83a6a008caa +576, 0x407000aff1b9e8cb +577, 0xb4d49bfb2b268c40 +578, 0xd4e6fe8a7a0f14a9 +579, 0xe34afef924e8f58e +580, 0xe377b0c891844824 +581, 0x29c2e20c112d30c8 +582, 0x906aad1fe0c18a95 +583, 0x308385f0efbb6474 +584, 0xf23900481bf70445 +585, 0xfdfe3ade7f937a55 +586, 0xf37aae71c33c4f97 +587, 0x1c81e3775a8bed85 +588, 0x7eb5013882ce35ea +589, 0x37a1c1692495818d +590, 0x3f90ae118622a0ba +591, 0x58e4fe6fea29b037 +592, 0xd10ff1d269808825 +593, 0xbce30edb60c21bba +594, 0x123732329afd6fee +595, 0x429b4059f797d840 +596, 0x421166568a8c4be1 +597, 0x88f895c424c1bd7f +598, 0x2adaf7a7b9f781cb +599, 0xa425644b26cb698 +600, 0x8cc44d2486cc5743 +601, 0xdb9f357a33abf6ba +602, 0x1a57c4ea77a4d70c +603, 0x1dea29be75239e44 +604, 0x463141a137121a06 +605, 0x8fecfbbe0b8a9517 +606, 0x92c83984b3566123 +607, 0x3b1c69180ed28665 +608, 0x14a6073425ea8717 +609, 0x71f4c2b3283238d7 +610, 0xb3d491e3152f19f +611, 0x3a0ba3a11ebac5d2 +612, 0xddb4d1dd4c0f54ac +613, 0xdb8f36fe02414035 +614, 0x1cf5df5031b1902c +615, 0x23a20ed12ef95870 +616, 0xf113e573b2dedcbb +617, 0x308e2395cde0a9fa +618, 0xd377a22581c3a7da +619, 0xe0ced97a947a66fb +620, 0xe44f4de9cd754b00 +621, 0x2344943337d9d1bf +622, 0x4b5ae5e2ea6e749c +623, 0x9b8d2e3ef41d1c01 +624, 0x59a5a53ebbd24c6b +625, 0x4f7611bf9e8a06fb +626, 0xea38c7b61361cd06 +627, 0xf125a2bfdd2c0c7 +628, 0x2df8dcb5926b9ebb +629, 0x233e18720cc56988 +630, 0x974c61379b4aa95e +631, 0xc7fe24c1c868910b +632, 0x818fd1affc82a842 +633, 0xcee92a952a26d38e +634, 0x8962f575ebcbf43 +635, 0x7770687e3678c460 +636, 0xdfb1db4ed1298117 +637, 0xb9db54cb03d434d3 +638, 0x34aebbf2244257ad +639, 0xd836db0cb210c490 +640, 0x935daed7138957cd +641, 0x3cd914b14e7948fd +642, 0xd0472e9ed0a0f7f0 +643, 0xa9df33dca697f75e +644, 0x15e9ea259398721a +645, 0x23eeba0f970abd60 +646, 0x2217fdf8bbe99a12 +647, 0x5ea490a95717b198 +648, 0xf4e2bfc28280b639 +649, 0x9d19916072d6f05c +650, 0x5e0387cab1734c6a +651, 0x93c2c8ac26e5f01e +652, 0xb0d934354d957eb1 +653, 0xee5099a1eef3188c +654, 0x8be0abca8edc1115 +655, 0x989a60845dbf5aa3 +656, 0x181c7ed964eee892 +657, 0x49838ea07481288d +658, 0x17dbc75d66116b2e +659, 0xa4cafb7a87c0117e +660, 0xab2d0ae44cdc2e6e +661, 0xdf802f2457e7da6 +662, 0x4b966c4b9187e124 +663, 0x62de9db6f4811e1a +664, 0x1e20485968bc62 +665, 0xe9ac288265caca94 +666, 0xc5c694d349aa8c1a +667, 0x3d67f2083d9bdf10 +668, 0x9a2468e503085486 +669, 0x9d6acd3dc152d1a3 +670, 0xca951e2aeee8df77 +671, 0x2707371af9cdd7b0 +672, 0x2347ae6a4eb5ecbd +673, 0x16abe5582cb426f +674, 0x523af4ff980bbccb +675, 0xb07a0f043e3694aa +676, 0x14d7c3da81b2de7 +677, 0xf471f1b8ac22305b +678, 0xdb087ffff9e18520 +679, 0x1a352db3574359e8 +680, 0x48d5431502cc7476 +681, 0x7c9b7e7003dfd1bf +682, 0x4f43a48aae987169 +683, 0x9a5d3eb66dedb3e9 +684, 0xa7b331af76a9f817 +685, 0xba440154b118ab2d +686, 0x64d22344ce24c9c6 +687, 0xa22377bd52bd043 +688, 0x9dfa1bb18ca6c5f7 +689, 0xdccf44a92f644c8b +690, 0xf623d0a49fd18145 +691, 0x556d5c37978e28b3 +692, 0xad96e32ce9d2bb8b +693, 0x2e479c120be52798 +694, 0x7501cf871af7b2f7 +695, 0xd02536a5d026a5b8 +696, 0x4b37ff53e76ab5a4 +697, 0xdb3a4039caaeab13 +698, 0x6cbd65e3b700c7be +699, 0x7367abd98761a147 +700, 0xf4f9ba216a35aa77 +701, 0xf88ca25ce921eb86 +702, 0xb211de082ec2cbf2 +703, 0xdd94aa46ec57e12e +704, 0xa967d74ad8210240 +705, 0xdaa1fada8cfa887 +706, 0x85901d081c4488ee +707, 0xcf67f79a699ef06 +708, 0x7f2f1f0de921ee14 +709, 0x28bc61e9d3f2328b +710, 0x3332f2963faf18e5 +711, 0x4167ac71fcf43a6 +712, 0x843c1746b0160b74 +713, 0xd9be80070c578a5e +714, 0xbd7250c9af1473e7 +715, 0x43f78afaa3647899 +716, 0x91c6b5dd715a75a5 +717, 0x29cc66c8a07bfef3 +718, 0x3f5c667311dc22be +719, 0x4f49cd47958260cd +720, 0xbef8be43d920b64e +721, 0x7a892a5f13061d8b +722, 0x9532f40125c819b1 +723, 0x924fca3045f8a564 +724, 0x9b2c6442453b0c20 +725, 0x7e21009085b8e793 +726, 0x9b98c17e17af59d2 +727, 0xba61acb73e3ae89a +728, 0xb9d61a710555c138 +729, 0xc2a425d80978974b +730, 0xa275e13592da7d67 +731, 0xe962103202d9ad0f +732, 0xbdf8367a4d6f33fd +733, 0xe59beb2f8648bdc8 +734, 0xb4c387d8fbc4ac1c +735, 0x5e3f276b63054b75 +736, 0xf27e616aa54d8464 +737, 0x3f271661d1cd7426 +738, 0x43a69dbee7502c78 +739, 0x8066fcea6df059a1 +740, 0x3c10f19409bdc993 +741, 0x6ba6f43fb21f23e0 +742, 0x9e182d70a5bccf09 +743, 0x1520783d2a63a199 +744, 0xba1dcc0c70b9cace +745, 0x1009e1e9b1032d8 +746, 0xf632f6a95fb0315 +747, 0x48e711c7114cbfff +748, 0xef281dcec67debf7 +749, 0x33789894d6abf59b +750, 0x6c8e541fffbe7f9c +751, 0x85417f13b08e0a88 +752, 0x9a581e36d589608f +753, 0x461dca50b1befd35 +754, 0x5a3231680dde6462 +755, 0xcc57acf729780b97 +756, 0x50301efef62e1054 +757, 0x675d042cd4f6bbc9 +758, 0x1652fdd3794384c9 +759, 0x1c93bbeeb763cd4d +760, 0x44b7240c4b105242 +761, 0x4c6af2a1b606ccfb +762, 0x18fc43ece2ec1a40 +763, 0x859a5511aeae8acb +764, 0x2f56826f1996ad2f +765, 0xa8e95ce8bb363bdf +766, 0xf4da396054e50e4b +767, 0x5493865e9895883c +768, 0x768e4c8b332ac0e3 +769, 0x32195d2aa583fca5 +770, 0xf2f353f21266bc15 +771, 0x43cddf1d021307d +772, 0x6031e3aa30300e4a +773, 0x4f1298469ac6088f +774, 0x4b4d450bafac574e +775, 0x23e1cf9c0582a22b +776, 0x2e9036980db49cd0 +777, 0xe4e228b113c411b2 +778, 0x8bddcdb82b51706 +779, 0xd2a7ea8288593629 +780, 0x67fe90e98fdda61 +781, 0x7b63494dba95717b +782, 0x105625904510d782 +783, 0xdf4aa2242454e50a +784, 0x32541d6cd7d6c7e3 +785, 0x5661fb432591cf3b +786, 0xce920a5ed047bce7 +787, 0xed4178a3c96eea8f +788, 0xe378cd996e39863b +789, 0x169e1fdc8e2b05e1 +790, 0xaee1812ef7149a96 +791, 0x648571c7453d12c5 +792, 0xb7b6bc9328573c43 +793, 0xe7fb969078e270d7 +794, 0xdfc2b1b8985f6e6f +795, 0x862b6527ee39a1aa +796, 0x1ee329aea91d7882 +797, 0x20d25324f2fe704 +798, 0xbfcc47401fc3bbfd +799, 0x1515cdc8d48b2904 +800, 0xbd6eefe86284261c +801, 0x9b1f28e3b35f22ee +802, 0x842a29d35e5aecda +803, 0xf2346109ad370765 +804, 0x24d68add5a71afd9 +805, 0x4a691421613d91e2 +806, 0x60e3058b3c244051 +807, 0x79194905cdaa5de8 +808, 0xe0e2df35c01e8987 +809, 0xe29b78beffbb5e4a +810, 0xcdcdbc020218c19e +811, 0x5ae0af8c16feae43 +812, 0x8109292feeaf14fa +813, 0x34113f7508dfa521 +814, 0xc062ac163f56730a +815, 0xf1660e66ec6d4c4c +816, 0x5966c55f60151c80 +817, 0x3865ae8ec934b17 +818, 0x472a7314afb055ec +819, 0x7a24277309a44a44 +820, 0x556e02dd35d38baa +821, 0x9849611a1bc96ec1 +822, 0xd176f5d5a8eb0843 +823, 0x44db12ec60510030 +824, 0x272e3a06a0030078 +825, 0x7c4764dbefc075ea +826, 0x910712f3735c1183 +827, 0xd49a2da74ae7aff6 +828, 0xcf9b3e6e8f776d71 +829, 0x27789fe3ec481a02 +830, 0x86659f82c6b5912b +831, 0xe044b3dbf339158c +832, 0x99d81f6bb62a37b0 +833, 0x5f5830c246fada9a +834, 0xe68abab1eeb432cb +835, 0x49c5c5ace04e104 +836, 0x1ac3871b3fc6771b +837, 0x773b39f32d070652 +838, 0x9c4138c2ae58b1f3 +839, 0xac41c63d7452ac60 +840, 0x9248826b245359e1 +841, 0x99bba1c7a64f1670 +842, 0xe0dc99ff4ebb92f2 +843, 0x113638652740f87c +844, 0xebf51e94da88cfc +845, 0x5441c344b81b2585 +846, 0xe1e69e0bc2de652a +847, 0xe9ab6d64ae42ed1e +848, 0x879af8730e305f31 +849, 0x36b9ad912c7e00d6 +850, 0x83ef5e9fca853886 +851, 0xda54d48bb20ea974 +852, 0x32c6d93aefa92aa2 +853, 0x4e887b2c3391847d +854, 0x50966e815f42b1b8 +855, 0x53411ac087832837 +856, 0x46f64fef79df4f29 +857, 0xb34aae3924cd272c +858, 0xf5ad455869a0adbe +859, 0x8351ded7144edac8 +860, 0xeb558af089677494 +861, 0x36ed71d69293a8d6 +862, 0x659f90bf5431b254 +863, 0x53349102b7519949 +864, 0x3db83e20b1713610 +865, 0x6d63f96090556254 +866, 0x4cc0467e8f45c645 +867, 0xb8840c4bd5cd4091 +868, 0xbd381463cc93d584 +869, 0x203410d878c2066d +870, 0x2ebea06213cf71c8 +871, 0x598e8fb75e3fceb4 +872, 0xdcca41ceba0fce02 +873, 0x61bf69212b56aae5 +874, 0x97eed7f70c9114fa +875, 0xf46f37a8b7a063f9 +876, 0x66c8f4ffe5bd6efa +877, 0xe43fd6efda2d4e32 +878, 0x12d6c799e5ad01de +879, 0x9ac83e7f8b709360 +880, 0xbbb7bb3c1957513d +881, 0x7f87c08d4b3796b0 +882, 0x9a7d1d74b6aa4a5c +883, 0xa4314530ff741b6f +884, 0x99a80c6b6f15fca8 +885, 0xd2fec81d6d5fc3ce +886, 0x15a98be1cc40cea +887, 0x98693eb7719366f3 +888, 0x36ccdc2a9e9d4de8 +889, 0x3c8208f63d77df25 +890, 0xca2e376e2343df6 +891, 0xcc9b17cbb54420c6 +892, 0x8724c44a64d7dcb8 +893, 0x9d00c6949ff33869 +894, 0xf4f8e584d2699372 +895, 0x88f4748cdd5a2d53 +896, 0xe215072a1205bc6d +897, 0x190934fe6d740442 +898, 0x7fac5c0ab2af106d +899, 0x1b86633a0bd84fa1 +900, 0x1293e54318492dfb +901, 0x433324fd390f34b9 +902, 0x4c5eb2c67a44643b +903, 0x59a6e281c388b0dd +904, 0xe78e03f9c44623b7 +905, 0x91307a93c768fc3d +906, 0xde8867b004d8e3ff +907, 0xdf52c3f57b7c5862 +908, 0x993f3e1d10358a92 +909, 0x9ccb10bc3e18662d +910, 0x45093ce48a114c73 +911, 0xd59d05979d26330a +912, 0x417c0e03300119a9 +913, 0x1c336500f90cde81 +914, 0x1c8ccd29ead9b85b +915, 0xb76baf3e55d4d950 +916, 0x133ad6196c75fd7e +917, 0x34200b0cde7ed560 +918, 0x9c7c3dacb213c8d9 +919, 0xd97563c4fd9bf1b6 +920, 0x5d910e871835b6cb +921, 0x7d46c4733a16bdf9 +922, 0xe41d73194ddc87b2 +923, 0x7d3d8a0855a465a9 +924, 0x70c2a8b5d3f90c0f +925, 0x9e7565ca5dccfe12 +926, 0x2c0acb4577aa51b1 +927, 0x3d2cd211145b79c7 +928, 0x15a7b17aa6da7732 +929, 0xab44a3730c27d780 +930, 0xf008bd6c802bde3a +931, 0x82ed86ddf3619f77 +932, 0xaabe982ab15c49f9 +933, 0x9bcad8fa6d8e58a4 +934, 0x8f39ed8243718aa1 +935, 0xe9489340e03e3cb6 +936, 0xc722314f5eefb8d0 +937, 0x870e8869a436df59 +938, 0x4dae75b8087a8204 +939, 0xe1d790f6ec6e425b +940, 0xafd39ea1b1d0ed09 +941, 0xdf2c99e464ddf08f +942, 0x74936d859ab9644d +943, 0x3871302164250e73 +944, 0x764b68921e911886 +945, 0x2a1d024b26bb9d66 +946, 0x797fba43918e75b4 +947, 0x62ec6d24ccca335b +948, 0xf4bd8b951762b520 +949, 0x9d450dede9119397 +950, 0x5393a26d10f8c124 +951, 0x6b74769392896b57 +952, 0x7f61dbcc0e328581 +953, 0x64e1df3884d0d94 +954, 0xba77dcdf23738c37 +955, 0xf8e288bc0a177475 +956, 0x4a8abfd1702ecb7d +957, 0x53f22886694736a7 +958, 0x8fc982597ced3e3 +959, 0x1bc46090f820fff7 +960, 0x8bd31f965d02229f +961, 0x65cd0cb29996ee53 +962, 0x702e0f4fcf8c2e9f +963, 0x293b77bff307a9a0 +964, 0x125a986b8b305788 +965, 0x416b0eea428ebf3c +966, 0xeac85421ab0e8469 +967, 0x7f5496095019aa68 +968, 0x1a96d7afbc708e0 +969, 0xb91262e6766e01e1 +970, 0xd0a549cc4ccc6954 +971, 0x75a9a073f50c8a0d +972, 0xae275d2c1c6cd23c +973, 0xcf159b5ec5d28fd4 +974, 0x75d0838ce9b92b +975, 0xd4eddcee6dc4677f +976, 0x6a0a8ad5df6b75b8 +977, 0x6f3fd0ef0f13ecc4 +978, 0xb75a5826c1a8f8a8 +979, 0xd47098bbc7943766 +980, 0x3d4ddd62d5f23dd1 +981, 0x760a904e4583841c +982, 0x2afeb5022b4cf1f +983, 0x66d5f653729f0a13 +984, 0x9a6a5ab62980d30f +985, 0xc332f5643bbf8d5b +986, 0x848fb702e4056a90 +987, 0xa057beaf3f9e8c5f +988, 0x6cc603e4560a6c6a +989, 0xec761811a7b23211 +990, 0xb14aa4090a82aaa5 +991, 0xe29d9d028a5b2dbb +992, 0x5564e53738d68f97 +993, 0xfabca36542eaaf3b +994, 0xb9912fcb782020a2 +995, 0xe865e01b349284fd +996, 0x540b5ff11c5f9274 +997, 0x3463f64e1e7451dc +998, 0xe15d3e2f33b735f8 +999, 0xf5433336eadef6e diff --git a/numpy/random/tests/data/sfc64-testset-2.csv b/numpy/random/tests/data/sfc64-testset-2.csv new file mode 100644 index 000000000000..70aebd5d5392 --- /dev/null +++ b/numpy/random/tests/data/sfc64-testset-2.csv @@ -0,0 +1,1001 @@ +seed, 0x0 +0, 0x91959e5fb96a6332 +1, 0x3c1dd8a25a7e9f21 +2, 0x657bdffc99798d9e +3, 0x1a04de320b19e022 +4, 0x65b92af0e5f3c61c +5, 0x9c84070ce8f743c0 +6, 0xbb10e573693cdb25 +7, 0xd65ea9e76b37fb6b +8, 0x503efd0e76c8ae66 +9, 0xd711dcd04c26d0f +10, 0x12f53f435814ac8c +11, 0xb392cd402cfc82bd +12, 0x461764550e06c889 +13, 0x716a48b3514e6979 +14, 0xdd0a322213c18ad7 +15, 0x6673a8ca0a05c4d7 +16, 0x2992ef333437f844 +17, 0xc4aaf7e8240b2aad +18, 0x6ab0a1af1f41474f +19, 0xb0bae400c226941d +20, 0xe5f80c2eeeab48c6 +21, 0x3832c6a93a4024bf +22, 0x280bd824fabe8368 +23, 0x66b626228321e5ff +24, 0xe0bdfba5325a307e +25, 0x3a5f65c6ef254e05 +26, 0x99ea12503cb02f94 +27, 0x5d01fd2db77d420b +28, 0x6959bf5f36b2368d +29, 0xd856e30c62b5f5be +30, 0xe33233e1d8140e66 +31, 0xb78be619d415fa8d +32, 0x4f943bb2cc63d3b +33, 0x9b1460b290952d81 +34, 0x19205d794826740e +35, 0x64617bd9d7a6a1ff +36, 0x30442124b55ea76a +37, 0xebbbc3b29d0333fc +38, 0x39235a0fe359751c +39, 0xf9629768891121aa +40, 0x32052f53f366e05a +41, 0x60cc5b412c925bc8 +42, 0xf8b7ecda1c0e5a9 +43, 0x195f036e170a2568 +44, 0xfe06d0381a9ca782 +45, 0x919d89e8b88eebbf +46, 0xa47fb30148cf0d43 +47, 0x5c983e99d5f9fd56 +48, 0xe7492cdb6a1d42cd +49, 0xf9cfe5c865b0cfd8 +50, 0x35b653367bbc3b99 +51, 0xb1d92f6f4d4e440b +52, 0x737e1d5bd87ed9c0 +53, 0x7a880ca1498f8e17 +54, 0x687dae8494f9a3f7 +55, 0x6bae1989f441d5d7 +56, 0x71ad3fa5a9195c2e +57, 0x16b3969779f5d03 +58, 0xd1bce2ac973f15b3 +59, 0xa114b1ee2ce0dcdd +60, 0x270d75c11eb1b8d5 +61, 0xc48ffa087c0a7bc +62, 0xaaf9dc48cda9848d +63, 0x8111cf10ef6e584d +64, 0x6736df6af40ee6f4 +65, 0x1a1a111682fbf98d +66, 0xeb217658e1cb3b5d +67, 0xcaf58a8b79de9dec +68, 0x25d0ffd63c88d7a1 +69, 0x4c498cd871b7f176 +70, 0x4069a6156eb0cf3c +71, 0xdf012f12edcdd867 +72, 0x7734c0ac8edb1689 +73, 0xed6960ac53dbc245 +74, 0x305e20da8868c661 +75, 0x5f0c7a3719956f95 +76, 0x66842bbe3b28895 +77, 0xb608bc9a31eac410 +78, 0xfcb17d5529503abd +79, 0x829ae5cbc29b92ee +80, 0x17f2f0027bc24f3a +81, 0x435926c33d8f44cc +82, 0x3ab899327098dbec +83, 0xaf78573b27f8ead8 +84, 0xa8b334fabcf8dc60 +85, 0xcdf3b366a6a303db +86, 0x8da9379dd62b34c8 +87, 0xb0ba511955f264a7 +88, 0x9d72e21a644f961d +89, 0xfac28382e2e7e710 +90, 0xd457065f048410aa +91, 0x1cae57d952563969 +92, 0x5a160a6223253e03 +93, 0x2c45df736d73c8bd +94, 0x7f651ebc6ad9cec5 +95, 0x77a6be96c7d2e7e7 +96, 0x1721fb1dbfd6546a +97, 0xf73f433ecff3c997 +98, 0xed1e80f680965bfe +99, 0x6705ad67a3003b30 +100, 0xac21134efcadb9f7 +101, 0x4d2ba0a91d456ac +102, 0x59da7b59434eb52b +103, 0x26c1d070fd414b5f +104, 0xed7079ddfce83d9a +105, 0x9277d21f88e0fb7a +106, 0xfae16b9a8d53d282 +107, 0xb08a0e2e405fdf7d +108, 0x2ea20df44229d6ec +109, 0x80e4634cd3612825 +110, 0xbe62e8aeba8f8a1a +111, 0x4981209769c190fb +112, 0xcec96ef14c7e1f65 +113, 0x73fe4457b47e7b53 +114, 0x1d66300677315c31 +115, 0xe26821290498c4cc +116, 0xf6110248fd8fb1c5 +117, 0x30fd7fe32dbd8be3 +118, 0x534ec9b910a2bd72 +119, 0x8f9bfe878bbf7382 +120, 0x4f4eb5295c0c2193 +121, 0xdeb22f03a913be9e +122, 0x40f716f8e2a8886c +123, 0xc65007d0e386cdb1 +124, 0x9bdd26d92b143a14 +125, 0xf644b0b77ea44625 +126, 0x75f5a53f6b01993a +127, 0xfe803e347bf41010 +128, 0x594bff5fa17bc360 +129, 0x3551edfb450373c7 +130, 0x898f9dad433615db +131, 0x923d2406daa26d49 +132, 0x99e07faccbc33426 +133, 0x7389f9ff4470f807 +134, 0xdc2a25957c6df90b +135, 0x33c6d8965ef3053f +136, 0x51a8f07e838f1ab +137, 0x91c5db369380274f +138, 0xc37de65ac56b207e +139, 0xfcc6d2375dde7f14 +140, 0xa4e6418bff505958 +141, 0x4b8b9f78e46953c4 +142, 0x255ab2e0f93cf278 +143, 0xdf650717af3d96ef +144, 0x2caa21cba3aae2b2 +145, 0xce7e46c6f393daa4 +146, 0x1d5b3573f9997ac7 +147, 0x5280c556e850847d +148, 0x32edc31bef920ad7 +149, 0xefaa6b0b08cf2c6 +150, 0x5151c99d97b111c5 +151, 0x35ccf4bf53d17590 +152, 0xa210d7bd8697b385 +153, 0xa9419f95738fbe61 +154, 0xdeccf93a1a4fdc90 +155, 0xd0ea3365b18e7a05 +156, 0x84122df6dcd31b9a +157, 0x33040a2125cea5f5 +158, 0xfe18306a862f6d86 +159, 0xdb97c8392e5c4457 +160, 0xc3e0fa735e80e422 +161, 0x7d106ff36467a0c1 +162, 0xb9825eecc720a76d +163, 0x7fefc6f771647081 +164, 0xf5df3f5b3977bf13 +165, 0x18fb22736d36f1e0 +166, 0xadc4637b4953abfc +167, 0x174e66d3e17974bd +168, 0xf1614c51df4db5db +169, 0x6664ecde5717b293 +170, 0xd5bc5b6839265c26 +171, 0xf6ca9ce1af3f1832 +172, 0xca696789a9d506ea +173, 0x7399c246c8f9d53 +174, 0xadf49049626417e2 +175, 0xbcd84af37d09ab91 +176, 0xbb41c177f3a3fa45 +177, 0x592becc814d55302 +178, 0xa88b4e65f6cfe5f7 +179, 0xa0a55e34ff879426 +180, 0x3c2ea6aa725b42b7 +181, 0x65ac4a407b1f9521 +182, 0xde63d53f7e88b556 +183, 0x18bc76696d015f40 +184, 0xd1363f2cd4c116a8 +185, 0x2fe859be19a48e4a +186, 0x83d6099b1415e656 +187, 0x43f2cbc1a4ee6410 +188, 0xb2eca3d3421c533d +189, 0xc52b98ea3f031f5d +190, 0xfe57eb01da07e9d1 +191, 0xf9377883537a6031 +192, 0x364030c05dac7add +193, 0x6815cb06b35d4404 +194, 0xceae2d4ce31894be +195, 0xc602bcdf6062bf6a +196, 0xc8e4bd8dcc6062e3 +197, 0x9c29e87b92a1a791 +198, 0x41e626b871ca9651 +199, 0x325c3d1fb8efbcd8 +200, 0x7dbbacf8e3419fb3 +201, 0x3602e72516bb7319 +202, 0x537a008ebd94d24b +203, 0xda7714fc9d4d161d +204, 0x1c8c73700e1b621b +205, 0x2749b80937d6c939 +206, 0x76ee6abac5b14d33 +207, 0xf18d1e92cb6a8b5c +208, 0x6ce9579d9291c721 +209, 0x60523c745a40e58 +210, 0x637f837fcc901757 +211, 0x2ff71b19661dc5b3 +212, 0x393ab586326ad16f +213, 0xa0970ea30fe742b7 +214, 0x570222d7f27fe5ae +215, 0x3b5806d43fd38629 +216, 0x129a0ad7420180c5 +217, 0x1c4726355778d52c +218, 0x7c1459cf77656499 +219, 0xfe038a0932132069 +220, 0x4c4cc317a937483a +221, 0xa333d24067e926ba +222, 0x401d9b6ab37f6ef2 +223, 0x87ad0e491ebe4a2a +224, 0xfc02f312e72d121d +225, 0xfde715b3b99767b2 +226, 0xd111c342ba521c92 +227, 0x83b221b10879c617 +228, 0x6a1bf5c01fdf4277 +229, 0x166bfc0c3f5892ee +230, 0x4608d556d7c57856 +231, 0x8d786857c95ece49 +232, 0x2d357445a1aca4ac +233, 0x79620dae28ecd796 +234, 0x90e715dc0f2201c4 +235, 0x173b68b4c9f4b665 +236, 0x4e14d040ebac4eef +237, 0xbd25960b4b892e +238, 0x911a199db6f1989d +239, 0xfe822d7c601fd2e0 +240, 0x9b4c1d58d8223a69 +241, 0x907c1891283843b0 +242, 0xf4868bf54061c4b2 +243, 0x17f8cd1fc24efd85 +244, 0xd44253f9af14c3aa +245, 0x16d0da0cb911d43c +246, 0x3c6a46615828e79a +247, 0x498591c1138e11a5 +248, 0xcc0f26336d0d6141 +249, 0x4d3ebc873212309a +250, 0x16bad7792d5c2c6a +251, 0x474215a80b2bbd11 +252, 0x7159848abd8492fc +253, 0x359341c50973685f +254, 0x27512ee7bf784a4a +255, 0x45228ea080f70447 +256, 0x880cab616500d50e +257, 0x12fae93f9830d56e +258, 0x6744ee64348d9acd +259, 0x484dada28cd2a828 +260, 0x98491d0729e41863 +261, 0x2f15aac43c2863b0 +262, 0x5727a34d77a1da0f +263, 0xa435cebef6a62eed +264, 0xd211697d57b053b0 +265, 0x65aa757b68bd557 +266, 0xe3a1b7a2d8a3e06a +267, 0x2adf64e67252a7a9 +268, 0xadadcb75cadee276 +269, 0x7934bc57ac8d97bf +270, 0xccff0d0f412e0606 +271, 0x101a82aa3e8f3db9 +272, 0xb0f2498094b4575c +273, 0xba2561d9ef26ed8a +274, 0xfbcd1268fc3febe1 +275, 0x9aa10bb19eb152e0 +276, 0xf496217a601a6d72 +277, 0xe4be1e4f2fa91363 +278, 0x473a602bf3dd68eb +279, 0xfe8ed2a48c26f4b5 +280, 0x20e94b1a00159476 +281, 0x93e1cb1c6af86ec7 +282, 0x4fcba3898f7442ba +283, 0x5150c3a3d94891df +284, 0x91cfce6c85b033ea +285, 0x625e8a832a806491 +286, 0x28c97ba72e3ec0b2 +287, 0x8e172de217c71ea1 +288, 0x926b80216c732639 +289, 0x28b19431a649ae3d +290, 0x57c039a6e95a3795 +291, 0xfbc354182fe52718 +292, 0x819dfd7c7d534cef +293, 0xabb4093a619ed44f +294, 0xe785b7ac6f656745 +295, 0xb647b4588b2f942f +296, 0x64cf870a14c72d27 +297, 0x6d4a4a2a0ba9b37e +298, 0x78bfb0427d7ce6b0 +299, 0x8dcc72b8bfc79ac6 +300, 0x1c14d915d5e76c99 +301, 0xaf48ddea6f096d79 +302, 0x51b39b67aa130d8 +303, 0x1aeeb39d4def06de +304, 0xd678092ffedfdd27 +305, 0x8f54787f325111d3 +306, 0xf2ca2e827beaa6bc +307, 0x339d134099e98545 +308, 0x1f6a8a7b33942e43 +309, 0x952c8065dbef669a +310, 0xe066aeb6690147f7 +311, 0xed25aa92cf58ebb6 +312, 0x7601edce215ef521 +313, 0xed1c5b396abd9434 +314, 0x4fd1e407535de9d5 +315, 0xccc8315a0d4d1441 +316, 0x85753e250bb86976 +317, 0xf232e469378761c3 +318, 0x81d691b8e9aef3c6 +319, 0x224a2f9cab0ad0e +320, 0x978f3d3e50007f4e +321, 0xd3713e6a6c0cbe60 +322, 0xcce8f1eadd41f80d +323, 0x34bda028a97d469 +324, 0x90e242fdf0f59183 +325, 0x4d749754fbc5f092 +326, 0x4399f5b7851cc87b +327, 0xcb921a5f25f6c5d7 +328, 0x120bf5d0162101 +329, 0x1304cc2aa352735a +330, 0xf7236c5d0d5d417b +331, 0xc31b320fc1654306 +332, 0xb468c6b23f3fb4e7 +333, 0xb5985b5bfaca4166 +334, 0x898285a1cd2f8375 +335, 0xa13493da372aa7c9 +336, 0x15c80c09c12634e7 +337, 0x9b765c5cc9d438bd +338, 0xee7da816a9201dcb +339, 0x92e269f73b5a248e +340, 0xa8086c5de81400ce +341, 0xe0053901853d42be +342, 0x821df32c012f433e +343, 0x17a6d69ca37387c7 +344, 0x2b10044bfba3501f +345, 0x8dfd262afc2e8515 +346, 0xd68c2c7b60226371 +347, 0xe81ac114e4416774 +348, 0x5896d60061ebc471 +349, 0xa996e3147811dbd1 +350, 0xa819c7b80ecb3661 +351, 0x982ad71b38afbc01 +352, 0xab152b65aa17b7fe +353, 0x4582bc282ef187ef +354, 0xab5a17fe8d9bc669 +355, 0x83664fa9cb0284b7 +356, 0x234c4b0091968f52 +357, 0x8ab5f51805688d37 +358, 0xe9e11186e0c53eda +359, 0x10df37ef1de2eccf +360, 0x780f1b0d52db968f +361, 0x50bd4ff292872cd5 +362, 0x51e681c265f5ad0 +363, 0x842c49660a527566 +364, 0x6e56ee026e9eda87 +365, 0x4cf39e40d8c80393 +366, 0x13e466df371f7e1f +367, 0xf2ce1799f38e028e +368, 0x833c8db7adc6ff0e +369, 0xc6e189abc2ec98f +370, 0xafebb3721283fec5 +371, 0xb49bc1eb5cc17bdc +372, 0xf1d02e818f5e4488 +373, 0xe5e9d5b41a1dd815 +374, 0xce8aca6573b1bfe5 +375, 0x9b0a5d70e268b1d5 +376, 0xf3c0503a8358f4de +377, 0x2681605dd755669d +378, 0xea265ca7601efc70 +379, 0xa93747f0a159439f +380, 0x62a86ede78a23e50 +381, 0xac8a18935c3d063c +382, 0x729c0a298f5059f5 +383, 0xbbf195e5b54399f4 +384, 0x38aa9d551f968900 +385, 0x3b3e700c58778caa +386, 0x68e6e33c4443957a +387, 0x7c56fc13eb269815 +388, 0xaf7daca39711804a +389, 0x50fde6d10f9544b3 +390, 0xf3d37159f6f6c03d +391, 0x82d298f5c1a71685 +392, 0x478661ac54c5002c +393, 0x6053768e1a324ae0 +394, 0xde8fb4a7e56707ea +395, 0xaa2809301faa8cf4 +396, 0x690a8d49fedd0722 +397, 0xe17c481b9c217de9 +398, 0x60d1d8a2b57288e3 +399, 0x149adfaadc6b0886 +400, 0xa3c18b6eb79cd5fa +401, 0x5774e3a091af5f58 +402, 0x2acca57ff30e5712 +403, 0x94454d67367c4b0c +404, 0x581b2985ac2df5ca +405, 0x71618e50744f3e70 +406, 0x270a7f3bd9a94ae6 +407, 0x3ef81af9bb36cd7b +408, 0x8a4a2592875254aa +409, 0x704ac6086fbb414a +410, 0xda774d5d3f57414d +411, 0xe20d3358b918ae9e +412, 0x934a6b9f7b91e247 +413, 0xf91649cde87ec42c +414, 0x248cec5f9b6ced30 +415, 0x56791809fd8d64ba +416, 0xf502b2765c1395f +417, 0x6b04ec973d75aa7f +418, 0xb0339f2794bb26f +419, 0x4c524636efbaea49 +420, 0x6bbf3876e9738748 +421, 0xf686524e754e9e24 +422, 0x8dafa05a42d19cd3 +423, 0xc5f069ab2434008e +424, 0x4fd64cc713cba76 +425, 0xdbf93450c881ed5f +426, 0x492e278ebabb59a2 +427, 0x993fddfde4542642 +428, 0xecde68a72c8d4e52 +429, 0xe0760b3074c311fd +430, 0x68dc0e7e06528707 +431, 0x52b50edf49c0fdc7 +432, 0xb2bd4185c138f412 +433, 0x431496d7e1d86f3 +434, 0xa4e605b037e26c44 +435, 0x58236ae1f0aca2b5 +436, 0x26c72c420fc314d8 +437, 0x20134e982ab99a2b +438, 0x544b59b8b211374b +439, 0x1301c42f3a14d993 +440, 0x52a6ea740f763b0f +441, 0xf209d70c2bebf119 +442, 0xac66a4ebc2aa1be +443, 0x683713ed35878788 +444, 0x2b5578acec06b80c +445, 0x86428efa11c45b36 +446, 0xb49010adb17d291e +447, 0x73b686bd8664b6be +448, 0x6d28ebf57b6884cc +449, 0x9712091230ff58d9 +450, 0xc9c91f74c38b286 +451, 0x776310ac41dc008e +452, 0x2f3739df0bf6a88e +453, 0x5792dc62b94db675 +454, 0x5715910d024b06af +455, 0xeb1dd745458da08 +456, 0xfce7b07ccfa851a7 +457, 0xc305f1e983ac368 +458, 0x485aa9519ac00bb0 +459, 0xa5354f6589fb0ea0 +460, 0x32fee02dfdbf4454 +461, 0x4d1ddc304bbefaaa +462, 0x789a270a1737e57e +463, 0x9f3072f4b1ed8156 +464, 0x4de3c00e89058120 +465, 0xb00a02529e0a86fa +466, 0x539f6f0edd845d9a +467, 0x85e578fe15a8c001 +468, 0xa12c8e1a72cce7d8 +469, 0xc6908abbc2b1828 +470, 0xcf70090774cbb38c +471, 0x3b636a6977b45d4a +472, 0xf0a731b220680b57 +473, 0x18973929f51443a8 +474, 0xe93e1fbe7eadabe +475, 0x8233730f0a6dfa02 +476, 0x66e50b6919b0ab74 +477, 0xb1aba87c97fd08a2 +478, 0xd4dffc1fbc117ad6 +479, 0x6f7fa65724b96e6a +480, 0x4bd5800dee92e0fa +481, 0xe18a959db6256da +482, 0xe53a291bc66df487 +483, 0xb7ec306a08651806 +484, 0x1847a6b80d2821e1 +485, 0xda50391283b14d39 +486, 0xacc4d3cd7cceb97a +487, 0x57f70185165b7bc6 +488, 0x302b6d597c3aaba7 +489, 0xa47f32d037eab51e +490, 0xe1509b4408abc559 +491, 0x4f30a1d7c2934157 +492, 0x2ad03e6c60b650b2 +493, 0x334d9c337b0a9064 +494, 0xc7f442821e7aac12 +495, 0xbcdeb09298694cdd +496, 0xe42402389f8f0fb4 +497, 0xe5de56af539df727 +498, 0x7017f9b2101ee240 +499, 0x1ee5e68d5b10001d +500, 0x436229051836387a +501, 0xcd532d6d6ec38fb7 +502, 0x30a66606fdf38272 +503, 0xfdaa2ab9cf798496 +504, 0x4277b4adec70e7df +505, 0x72cfc30256e0eaef +506, 0x3c3359fd9bd34917 +507, 0xb7aa89598856efb0 +508, 0xf72226f8bf299ef5 +509, 0x258c499275a4356f +510, 0x999a56bfc7f20d76 +511, 0x2b3e7432e20c18b +512, 0x2d1251332f760cb5 +513, 0x7420e0eea62157c5 +514, 0xe85c895aa27cec3d +515, 0x27a0545c7020d57c +516, 0xc68638a65b4fff0d +517, 0xfda473983a4ea747 +518, 0xd19fe65fb4c06062 +519, 0x6b1374e050ee15e4 +520, 0x80065ecd49bc4bef +521, 0x4ee655954bc838de +522, 0xe8fb777504a72299 +523, 0x86b652ea70f4bdde +524, 0xcdc9e0fbde7e4f33 +525, 0x352c0a50cd3ac56 +526, 0x4b8605d368be75dc +527, 0x1ac9ea8129efbc37 +528, 0x470325faa99f39c5 +529, 0x25dd7ef9adccf7a1 +530, 0x5ae2c7a03e965816 +531, 0xf733d2df59dacc7d +532, 0xa05bbf0a8a1a7a70 +533, 0xe8aa3f102846ef5f +534, 0xc9b85ec49ae71789 +535, 0xb904c14ed1cb1936 +536, 0x5ae618230b5f0444 +537, 0x97987fe47b5d7467 +538, 0xabb3aca8865ca761 +539, 0x38bfdf29d4508228 +540, 0x353654f408353330 +541, 0xeb7e92930ae4ef0d +542, 0xec50f1a7ca526b96 +543, 0xd5e2dc08b5697544 +544, 0x24c7fd69d5ec32df +545, 0x6f7e1095568b8620 +546, 0x6ed9c16ca13b3c8 +547, 0xe676ef460002130f +548, 0xa3a01a3992c4b430 +549, 0xe2130406c3b1f202 +550, 0xa8f7263e2aedcd20 +551, 0xc45d71ef2e35f507 +552, 0x37155594021da7ba +553, 0x22dc94f19de73159 +554, 0x7969fc6bffc5443f +555, 0x97def7e44faa6bfe +556, 0x8b940f5e8931d71f +557, 0xd95b1dd3f1a3fdd5 +558, 0x1c83bfdca615701a +559, 0xb7fcb56279ceca6b +560, 0xd84f8950f20dcd0 +561, 0xb03343698de3cbe0 +562, 0xf64565d448d71f71 +563, 0xda52b4676e0ae662 +564, 0xda39c2c05b4ffb91 +565, 0xb35e2560421f6a85 +566, 0x1a7b108d48ac3646 +567, 0xc4e264dc390d79ed +568, 0xa10727dfd9813256 +569, 0x40d23154e720e4f7 +570, 0xd9fa7cd7e313e119 +571, 0xcbf29107859e6013 +572, 0xc357338553d940b7 +573, 0x2641b7ab0bdfcbaa +574, 0xd12f2b6060533ae7 +575, 0xd0435aa626411c56 +576, 0x44af4a488a9cec72 +577, 0xb934232ea8fa5696 +578, 0x760a8b12072b572d +579, 0xfab18f9942cfa9b3 +580, 0x5676834c1fe84d16 +581, 0x9c54e4fddb353236 +582, 0xab49edfc9551f293 +583, 0x567f1fb45a871d +584, 0x32a967c873998834 +585, 0x99240aad380ef8d1 +586, 0x7f66cbd432859a64 +587, 0x4cdc8a4658166822 +588, 0x984e3984a5766492 +589, 0xa3b2d0a3d64d3d94 +590, 0x177f667172f2affc +591, 0xb1a90607a73a303f +592, 0xe600b6c36427f878 +593, 0xf758f9834cb7f466 +594, 0x8ee9fce4a3f36449 +595, 0xcb8f11533e7da347 +596, 0xe7cf647794dabd7c +597, 0xc9d92cfe6110806 +598, 0xea1335fa9145a1ec +599, 0xbc6c29821d094552 +600, 0x37b9d6a858cc8bc3 +601, 0xf24e4c694929893e +602, 0x55d025ce2d7d0004 +603, 0xccdc69acccf4267b +604, 0xc491c04340c222eb +605, 0xba50f75ecec9befb +606, 0x1ec7bd85b8fe3bb9 +607, 0xe4de66498c59ae8a +608, 0x38aa9e912712c889 +609, 0xcee0e43c5cc31566 +610, 0x72b69aa708fc7ed +611, 0xdff70b7f6fa96679 +612, 0xd6d71d82112aadc3 +613, 0x365177892cb78531 +614, 0xa54852b39de4f72c +615, 0x11dd5832bf16dd59 +616, 0x248a0f3369c97097 +617, 0xa14cec0260e26792 +618, 0x3517616ff142bed1 +619, 0x9b693ad39dab7636 +620, 0x739dff825e994434 +621, 0x67711e7356098c9 +622, 0xa81f8515d2fdf458 +623, 0xdac2908113fe568e +624, 0xe99944ebc6e2806a +625, 0x671728ca5b030975 +626, 0xfdad20edb2b4a789 +627, 0xedc6e466bd0369d2 +628, 0x88b5d469821f7e1b +629, 0x2eabf94049a522a5 +630, 0x247794b7a2f5a8e3 +631, 0x278942bdbe02c649 +632, 0xbe5a9a9196ab99c1 +633, 0x75955060866da1b5 +634, 0xdedcfa149273c0b5 +635, 0xdbeb7a57758f3867 +636, 0x7b9053347a2c8d5a +637, 0xa059b3f2eed338a5 +638, 0x59401a46ded3b79f +639, 0x38044ba56a6d19fb +640, 0x72c7221b4e77e779 +641, 0x526df3491a3a34da +642, 0xc3b31184ba16c0c2 +643, 0xd94c7144488624af +644, 0xcf966ee4dc373f91 +645, 0x62049e65dd416266 +646, 0x7c2adccb925bf8f +647, 0xd5fa5c22ed4ef8e1 +648, 0xd00134ebd11f2cd1 +649, 0xfbdf81767bed3634 +650, 0x62e8cc8ff66b6e26 +651, 0x3a72d6bcd4f2dcf7 +652, 0xf1cd45b1b46a86ed +653, 0x1271f98e0938bb9a +654, 0x82e6927e83dc31fa +655, 0x7b9b0e0acb67b92d +656, 0x6df503e397b2e701 +657, 0x93888f6fb561e0c3 +658, 0x393fb6069a40291 +659, 0x967a7d894cc0754d +660, 0x6e298996ad866333 +661, 0x5ff3cf5559d6ab46 +662, 0xd0d70508c40349f5 +663, 0xc64c66c0dd426b33 +664, 0x8fea340ee35c64dd +665, 0xf9cd381eb3060005 +666, 0xfcc37c2799fc0b11 +667, 0x6a37c91d65b489fa +668, 0x57231000fa0a0c9d +669, 0x55f6e292c6703f9a +670, 0xd0508ffbfa55a7a6 +671, 0x885db543276bdac8 +672, 0xc26dbe6a26b0e704 +673, 0x21f884874ebd709e +674, 0x711f0b6c8f732220 +675, 0x354d0a361eaee195 +676, 0x721344d8d30b006a +677, 0xa0e090a0d3a56f07 +678, 0x16b3d5d823a4952b +679, 0x59d7874bc9eae7b6 +680, 0x9bbb32710076455f +681, 0xd4fb22242ffabafd +682, 0xe1d4ac6770be1d89 +683, 0xb259cedebc73dc8a +684, 0x35faaa3b4246ab69 +685, 0x5d26addefdaee89 +686, 0x8e7ec350da0f3545 +687, 0xd0f316eed9f8fc79 +688, 0x98b2a52c9bf291b2 +689, 0xe4d294a8aca6a314 +690, 0x25bd554e6aa7673c +691, 0xcfde5dcba5be2a6c +692, 0xb5e01fb48d2d2107 +693, 0xe1caf28948028536 +694, 0xd434aa0a26f3ee9b +695, 0xd17723381641b8f6 +696, 0xfe73bd1f3f3768a2 +697, 0x1cc6b1abd08d67e9 +698, 0x247e328371a28de0 +699, 0x502e7942e5a9104a +700, 0x6a030fd242eb4502 +701, 0xa2ffe02744014ce8 +702, 0x59290763b18fe04e +703, 0xcf14241564271436 +704, 0xb0fb73c3c1503aff +705, 0x94e27c622f82137a +706, 0x747a5b406ac3e1f0 +707, 0x9a914e96a732031d +708, 0x59f68c6c8f078835 +709, 0x809d012c73eb4724 +710, 0x5b3c3b73e1b37d74 +711, 0xdde60ef3ba49cdf7 +712, 0x87a14e1f9c761986 +713, 0x4109b960604522af +714, 0x122d0e1ed0eb6bb9 +715, 0xadc0d29e80bfe33 +716, 0xa25b1b44f5fc8e4e +717, 0xbab85d8a9b793f20 +718, 0x825f4cbced0e7d1e +719, 0x2d6ae8807acb37ea +720, 0x8234420adce2e39 +721, 0x4a8ad4da6b804807 +722, 0x1e19f9bc215e5245 +723, 0x1d6f4848a916dd5e +724, 0x9ac40dfcdc2d39cc +725, 0x9f3524e3086155ec +726, 0x861fffc43124b2ef +727, 0xe640e3b756396372 +728, 0x41cb0f0c5e149669 +729, 0xe0bd37e1192e4205 +730, 0x62917d3858f4ce47 +731, 0xa36e7eb4d855820a +732, 0x204b90255a3bf724 +733, 0x66ee83a0175535bc +734, 0x2c14ce7c6b0c1423 +735, 0x85d9495fa514f70d +736, 0x5a4fe45ead874dbc +737, 0xe72248dcb8cfc863 +738, 0xfc21ff2932ed98cd +739, 0xcbba1edd735b5cad +740, 0x91ddc32809679bf5 +741, 0x192cdf2c7631ea1f +742, 0xbbc451ddf2ea286f +743, 0xad9e80cae2397a64 +744, 0x6918f0119b95d0e5 +745, 0xa40379017a27d70a +746, 0x1aaeddb600e61e1 +747, 0x15afd93cbd7adda9 +748, 0x156719bc2b757ff4 +749, 0x13d9a59e2b2df49d +750, 0x9a490986eaddf0a +751, 0xef9a350f0b3eb6b4 +752, 0x5de7f6295ba4fa4d +753, 0x7f37fd087c3fdb49 +754, 0xa9fe3749d6f3f209 +755, 0x50912ac036d9bfb +756, 0x982cb4d726a441f8 +757, 0x8ca8d8af59b872d0 +758, 0x7f8adfb0ceeade8a +759, 0xdad390ec742be44 +760, 0xa637944d0045be5b +761, 0x3569a3b3af807061 +762, 0x9599da8eae14511d +763, 0xc333e8d19589b01a +764, 0xfb9b524a20b571e1 +765, 0xbd9dc8b37ce5c3e1 +766, 0x142333005fa389ac +767, 0x1368bc37cd5bcce1 +768, 0x16094907ad6ecf73 +769, 0xb32c90dbba4c1130 +770, 0x82761d97c1747dd0 +771, 0x599f9f267ae3444d +772, 0x79ad3382994852e1 +773, 0x2511f06d9ef06e54 +774, 0xb35e6ab7d5bbddae +775, 0xfca9fa83a2988732 +776, 0x7d4350f0394ac3ba +777, 0xa52a9527bb176ea3 +778, 0xb49fa0ceb2aa8353 +779, 0x1f62e504d1468cc0 +780, 0xe1a77bfccce6efc3 +781, 0x776cdff4dc0d6797 +782, 0x56612e39b652c1f2 +783, 0x5f096a29294eda04 +784, 0x7978abc3aabd8b23 +785, 0x79dd875e0485b979 +786, 0x8a98aa4d5735d778 +787, 0xcca43940f69d2388 +788, 0xb2d4b156f144f93a +789, 0xbd528a676e9a862 +790, 0x2a394939c8e7ec5e +791, 0xb1da900c6efe4abc +792, 0x9869af479de4c034 +793, 0x78dbdfb88ac7c1db +794, 0x18cb169143088041 +795, 0xe69e5461c51a3e13 +796, 0x5389fa16ea98183c +797, 0xed7c80d1be1ea520 +798, 0x87246fc359758ced +799, 0xab323eba95fae4ed +800, 0xbc4c0dde7f8a1828 +801, 0xdb739f7955610b1a +802, 0xecd8c68c3434cc +803, 0x138c2eb88c477f44 +804, 0x28a65f96727aae41 +805, 0xdee879f2cf5629d +806, 0x684f0c90ef20070f +807, 0xa24a819ef5621800 +808, 0x8d0054f870e4fdcb +809, 0x99e8c6e695b600b +810, 0x50b705245891f7c3 +811, 0xc02eed3a6e58e51a +812, 0x443d64e95443606c +813, 0xca24959cfbd2d120 +814, 0xe072609ea48815bc +815, 0xbcc715026590315b +816, 0x3e76df24d7aa5938 +817, 0xd8ff04940d9b79ae +818, 0x54474ce790059bcd +819, 0x278390dd6aa70e81 +820, 0xf4df619fe35414e4 +821, 0x757d71270264e615 +822, 0x1e8a373699c11b23 +823, 0xef68c82046e67dd6 +824, 0xe280006599972620 +825, 0x234e095183b0f4d6 +826, 0xe3b7560ed9839749 +827, 0xcd5ec4086572332e +828, 0xc41c0d4aaa279108 +829, 0x4b9cd6126bc16a6d +830, 0x4a7252734f3e3dd0 +831, 0xb3132df156cc103a +832, 0xf9e4abbf7b64464a +833, 0xf936df27fb3c47b7 +834, 0x9142960873f6d71a +835, 0x4ba6aa3235cdb10d +836, 0x3237a2e765ba7766 +837, 0xd62f0b94c8e99e54 +838, 0x26b682f90a3ae41b +839, 0x40ad5e82072b6f81 +840, 0xd0198101f5484000 +841, 0xe4fac60ba11c332 +842, 0x472d0b0a95ef9d38 +843, 0x8512557aec5a3d8f +844, 0xef83169d3efd4de9 +845, 0x53fe89283e7a7676 +846, 0x2f50933053d69fc4 +847, 0x76f5e4362e2e53a2 +848, 0x8676fdccce28874a +849, 0x2737764c1fb1f821 +850, 0x4a6f70afc066ab55 +851, 0x27f8e151e310fca4 +852, 0xd606960ccbe85161 +853, 0xcce51d7ddd270a32 +854, 0xb4235999794875c2 +855, 0x580084e358e884 +856, 0x2159d5e6dc8586d7 +857, 0x87bd54d8599b3ba4 +858, 0x3e9ade6a2181664 +859, 0x5e6e140406d97623 +860, 0x511545d5aa0080a2 +861, 0xf49d78ed219aac57 +862, 0xbece1f9c90b8ea87 +863, 0x1c741cac36a2c514 +864, 0x7453c141047db967 +865, 0xd751832a5037eba2 +866, 0x71370a3f30ada1f7 +867, 0x7c01cf2dcb408631 +868, 0x1052a4fbdccc0fa1 +869, 0x13d525c9df3fb6c +870, 0xa3aa8dbfee760c55 +871, 0xc0288d200f5155cf +872, 0x79f4bcd12af567c3 +873, 0x8160d163bb548755 +874, 0x5cf2995fb69fd2df +875, 0xcc98ed01396639df +876, 0xad95f1d9cfc8256e +877, 0xa3df27d9fbdbfb9d +878, 0x83e5f5dda4d52929 +879, 0x9adc05043009f55b +880, 0xdfe8329dfde1c001 +881, 0x9980ccdd5298e6a2 +882, 0x636a7bd134f6ef56 +883, 0xef5ff780c4be6ba4 +884, 0x290d71dc77a56d16 +885, 0x6d65db9ff58de1e6 +886, 0x944b063b3805a696 +887, 0xce468ca2cce33008 +888, 0x5ba1ccb840f80f48 +889, 0x28ddce36fc9ad268 +890, 0x4f77ef254d507a21 +891, 0xce9b4057fadf3ab +892, 0xb518bc68298730e6 +893, 0xd2eb5b8e2ec665b0 +894, 0xe1583303a4f87344 +895, 0x9d5a0df4fbe1bed5 +896, 0x2ba9bc03ec8cfd07 +897, 0x479ed880a96ca669 +898, 0xcedf96338324771a +899, 0x312f4fc2da41ffaa +900, 0xa0eb9cf23b5e1ed8 +901, 0xf8f88f975dc3f539 +902, 0x4a37e185d0e96e0f +903, 0xf829654a5c0b46f9 +904, 0x3909cca7a7f8c7fb +905, 0x4c2e1d66ceb45105 +906, 0xaffaa19e1db8af87 +907, 0x9ec498246bd18c76 +908, 0x21d51558edc089da +909, 0xe8984112cd1b1561 +910, 0x7de1d2cf54b0c0e1 +911, 0xa06729aed50bfb9d +912, 0xcf19f733e5db19e1 +913, 0x70edf2624ab777cd +914, 0x46685becad10e078 +915, 0x825e0f6add46785 +916, 0x66d4af3b15f70de4 +917, 0xc676614b0666b21 +918, 0x282a916c864f5cb7 +919, 0x2707283a3f512167 +920, 0x37ff3afda7461623 +921, 0xc767eb1205e4ca86 +922, 0x46b359aecc4ea25b +923, 0x67fbbb797a16dbb1 +924, 0x64fd4ba57122290e +925, 0x8acc2a8ae59d8fac +926, 0x64a49298599acc67 +927, 0xedf00de67177ce30 +928, 0x1ea9d8d7e76d2d2c +929, 0x363fcac323f70eb2 +930, 0x19e6e3ec8a9712eb +931, 0xca541e96b0961f09 +932, 0x4d8fd34c2822ec46 +933, 0x2fdd56a50b32f705 +934, 0xaac2fcf251e3fd3 +935, 0xb0c600299e57045c +936, 0xd951ec589e909e38 +937, 0x4dc8414390cae508 +938, 0x537ef9d5e2321344 +939, 0xa57bc21fd31aa2dc +940, 0xa3a60df564183750 +941, 0xbe69a5ce2e369fb6 +942, 0x7744601f4c053ec8 +943, 0x3838452af42f2612 +944, 0xd4f0dad7115a54e9 +945, 0x629cf68d8009a624 +946, 0x2211c8fa34cb98cb +947, 0x8040b19e2213db83 +948, 0xb2a86d3ba2384fd +949, 0x4b85cec4f93f0dab +950, 0xc8d212d21ea6845d +951, 0x5b271a03a4fe2be0 +952, 0xff4f671319ad8434 +953, 0x8e615a919d5afa96 +954, 0xea7f47c53161160a +955, 0x33273930b13c6efc +956, 0x98eedda27fb59c3c +957, 0x188dc5e92e939677 +958, 0x9dbd0fa0911430f1 +959, 0x5b3dcf3fa75dfd2b +960, 0x3f03846febdb275d +961, 0x20cc24faea9e9cf6 +962, 0x854f3ac66199ff5d +963, 0x31169ac99d341e6f +964, 0xa85daed3c0bc1bbe +965, 0x64633711e71ba5dd +966, 0x530e79978dc73334 +967, 0x636f2ee6e20aef13 +968, 0xf6220f8b6d9a58fb +969, 0x425db8fa32141a7b +970, 0xac7c210f4b02be95 +971, 0x5fe8cfbe197a7754 +972, 0xfff7d40c79420ea +973, 0x5f8bab9ef4697b77 +974, 0xaf6fe54e45b23fe8 +975, 0xce79456ccc70bbce +976, 0x645ef680f48f1c00 +977, 0xa4dfac46e2028595 +978, 0x6bece4c41effc5df +979, 0xd316df886442641f +980, 0xa4f6ff994edd2a6 +981, 0x30281ae3cc49abe4 +982, 0x39acb7b663dea974 +983, 0x5e8829b01a7c06fb +984, 0x87bdb08cf027f13e +985, 0xdfa5ede784e802f6 +986, 0x46d03d55711c38cc +987, 0xa55a961fc9788306 +988, 0xbf09ded495a2e57a +989, 0xcd601b29a639cc16 +990, 0x2193ce026bfd1085 +991, 0x25ba27f3f225be13 +992, 0x6f685be82f64f2fe +993, 0xec8454108229c450 +994, 0x6e79d8d205447a44 +995, 0x9ed7b6a96b9ccd68 +996, 0xae7134b3b7f8ee37 +997, 0x66963de0e5ebcc02 +998, 0x29c8dcd0d17c423f +999, 0xfb8482c827eb90bc diff --git a/numpy/random/tests/test_direct.py b/numpy/random/tests/test_direct.py new file mode 100644 index 000000000000..58d966adff21 --- /dev/null +++ b/numpy/random/tests/test_direct.py @@ -0,0 +1,478 @@ +import os +from os.path import join +import sys + +import numpy as np +from numpy.testing import (assert_equal, assert_allclose, assert_array_equal, + assert_raises) +import pytest + +from numpy.random import ( + Generator, MT19937, PCG64, PCG64DXSM, Philox, RandomState, SeedSequence, + SFC64, default_rng +) +from numpy.random._common import interface + +try: + import cffi # noqa: F401 + + MISSING_CFFI = False +except ImportError: + MISSING_CFFI = True + +try: + import ctypes # noqa: F401 + + MISSING_CTYPES = False +except ImportError: + MISSING_CTYPES = False + +if sys.flags.optimize > 1: + # no docstrings present to inspect when PYTHONOPTIMIZE/Py_OptimizeFlag > 1 + # cffi cannot succeed + MISSING_CFFI = True + + +pwd = os.path.dirname(os.path.abspath(__file__)) + + +def assert_state_equal(actual, target): + for key in actual: + if isinstance(actual[key], dict): + assert_state_equal(actual[key], target[key]) + elif isinstance(actual[key], np.ndarray): + assert_array_equal(actual[key], target[key]) + else: + assert actual[key] == target[key] + + +def uint32_to_float32(u): + return ((u >> np.uint32(8)) * (1.0 / 2**24)).astype(np.float32) + + +def uniform32_from_uint64(x): + x = np.uint64(x) + upper = np.array(x >> np.uint64(32), dtype=np.uint32) + lower = np.uint64(0xffffffff) + lower = np.array(x & lower, dtype=np.uint32) + joined = np.column_stack([lower, upper]).ravel() + return uint32_to_float32(joined) + + +def uniform32_from_uint53(x): + x = np.uint64(x) >> np.uint64(16) + x = np.uint32(x & np.uint64(0xffffffff)) + return uint32_to_float32(x) + + +def uniform32_from_uint32(x): + return uint32_to_float32(x) + + +def uniform32_from_uint(x, bits): + if bits == 64: + return uniform32_from_uint64(x) + elif bits == 53: + return uniform32_from_uint53(x) + elif bits == 32: + return uniform32_from_uint32(x) + else: + raise NotImplementedError + + +def uniform_from_uint(x, bits): + if bits in (64, 63, 53): + return uniform_from_uint64(x) + elif bits == 32: + return uniform_from_uint32(x) + + +def uniform_from_uint64(x): + return (x >> np.uint64(11)) * (1.0 / 9007199254740992.0) + + +def uniform_from_uint32(x): + out = np.empty(len(x) // 2) + for i in range(0, len(x), 2): + a = x[i] >> 5 + b = x[i + 1] >> 6 + out[i // 2] = (a * 67108864.0 + b) / 9007199254740992.0 + return out + + +def uniform_from_dsfmt(x): + return x.view(np.double) - 1.0 + + +def gauss_from_uint(x, n, bits): + if bits in (64, 63): + doubles = uniform_from_uint64(x) + elif bits == 32: + doubles = uniform_from_uint32(x) + else: # bits == 'dsfmt' + doubles = uniform_from_dsfmt(x) + gauss = [] + loc = 0 + x1 = x2 = 0.0 + while len(gauss) < n: + r2 = 2 + while r2 >= 1.0 or r2 == 0.0: + x1 = 2.0 * doubles[loc] - 1.0 + x2 = 2.0 * doubles[loc + 1] - 1.0 + r2 = x1 * x1 + x2 * x2 + loc += 2 + + f = np.sqrt(-2.0 * np.log(r2) / r2) + gauss.append(f * x2) + gauss.append(f * x1) + + return gauss[:n] + + +def test_seedsequence(): + from numpy.random.bit_generator import (ISeedSequence, + ISpawnableSeedSequence, + SeedlessSeedSequence) + + s1 = SeedSequence(range(10), spawn_key=(1, 2), pool_size=6) + s1.spawn(10) + s2 = SeedSequence(**s1.state) + assert_equal(s1.state, s2.state) + assert_equal(s1.n_children_spawned, s2.n_children_spawned) + + # The interfaces cannot be instantiated themselves. + assert_raises(TypeError, ISeedSequence) + assert_raises(TypeError, ISpawnableSeedSequence) + dummy = SeedlessSeedSequence() + assert_raises(NotImplementedError, dummy.generate_state, 10) + assert len(dummy.spawn(10)) == 10 + + +class Base: + dtype = np.uint64 + data2 = data1 = {} + + @classmethod + def setup_class(cls): + cls.bit_generator = PCG64 + cls.bits = 64 + cls.dtype = np.uint64 + cls.seed_error_type = TypeError + cls.invalid_init_types = [] + cls.invalid_init_values = [] + + @classmethod + def _read_csv(cls, filename): + with open(filename) as csv: + seed = csv.readline() + seed = seed.split(',') + seed = [int(s.strip(), 0) for s in seed[1:]] + data = [] + for line in csv: + data.append(int(line.split(',')[-1].strip(), 0)) + return {'seed': seed, 'data': np.array(data, dtype=cls.dtype)} + + def test_raw(self): + bit_generator = self.bit_generator(*self.data1['seed']) + uints = bit_generator.random_raw(1000) + assert_equal(uints, self.data1['data']) + + bit_generator = self.bit_generator(*self.data1['seed']) + uints = bit_generator.random_raw() + assert_equal(uints, self.data1['data'][0]) + + bit_generator = self.bit_generator(*self.data2['seed']) + uints = bit_generator.random_raw(1000) + assert_equal(uints, self.data2['data']) + + def test_random_raw(self): + bit_generator = self.bit_generator(*self.data1['seed']) + uints = bit_generator.random_raw(output=False) + assert uints is None + uints = bit_generator.random_raw(1000, output=False) + assert uints is None + + def test_gauss_inv(self): + n = 25 + rs = RandomState(self.bit_generator(*self.data1['seed'])) + gauss = rs.standard_normal(n) + assert_allclose(gauss, + gauss_from_uint(self.data1['data'], n, self.bits)) + + rs = RandomState(self.bit_generator(*self.data2['seed'])) + gauss = rs.standard_normal(25) + assert_allclose(gauss, + gauss_from_uint(self.data2['data'], n, self.bits)) + + def test_uniform_double(self): + rs = Generator(self.bit_generator(*self.data1['seed'])) + vals = uniform_from_uint(self.data1['data'], self.bits) + uniforms = rs.random(len(vals)) + assert_allclose(uniforms, vals) + assert_equal(uniforms.dtype, np.float64) + + rs = Generator(self.bit_generator(*self.data2['seed'])) + vals = uniform_from_uint(self.data2['data'], self.bits) + uniforms = rs.random(len(vals)) + assert_allclose(uniforms, vals) + assert_equal(uniforms.dtype, np.float64) + + def test_uniform_float(self): + rs = Generator(self.bit_generator(*self.data1['seed'])) + vals = uniform32_from_uint(self.data1['data'], self.bits) + uniforms = rs.random(len(vals), dtype=np.float32) + assert_allclose(uniforms, vals) + assert_equal(uniforms.dtype, np.float32) + + rs = Generator(self.bit_generator(*self.data2['seed'])) + vals = uniform32_from_uint(self.data2['data'], self.bits) + uniforms = rs.random(len(vals), dtype=np.float32) + assert_allclose(uniforms, vals) + assert_equal(uniforms.dtype, np.float32) + + def test_repr(self): + rs = Generator(self.bit_generator(*self.data1['seed'])) + assert 'Generator' in repr(rs) + assert f'{id(rs):#x}'.upper().replace('X', 'x') in repr(rs) + + def test_str(self): + rs = Generator(self.bit_generator(*self.data1['seed'])) + assert 'Generator' in str(rs) + assert str(self.bit_generator.__name__) in str(rs) + assert f'{id(rs):#x}'.upper().replace('X', 'x') not in str(rs) + + def test_pickle(self): + import pickle + + bit_generator = self.bit_generator(*self.data1['seed']) + state = bit_generator.state + bitgen_pkl = pickle.dumps(bit_generator) + reloaded = pickle.loads(bitgen_pkl) + reloaded_state = reloaded.state + assert_array_equal(Generator(bit_generator).standard_normal(1000), + Generator(reloaded).standard_normal(1000)) + assert bit_generator is not reloaded + assert_state_equal(reloaded_state, state) + + ss = SeedSequence(100) + aa = pickle.loads(pickle.dumps(ss)) + assert_equal(ss.state, aa.state) + + def test_invalid_state_type(self): + bit_generator = self.bit_generator(*self.data1['seed']) + with pytest.raises(TypeError): + bit_generator.state = {'1'} + + def test_invalid_state_value(self): + bit_generator = self.bit_generator(*self.data1['seed']) + state = bit_generator.state + state['bit_generator'] = 'otherBitGenerator' + with pytest.raises(ValueError): + bit_generator.state = state + + def test_invalid_init_type(self): + bit_generator = self.bit_generator + for st in self.invalid_init_types: + with pytest.raises(TypeError): + bit_generator(*st) + + def test_invalid_init_values(self): + bit_generator = self.bit_generator + for st in self.invalid_init_values: + with pytest.raises((ValueError, OverflowError)): + bit_generator(*st) + + def test_benchmark(self): + bit_generator = self.bit_generator(*self.data1['seed']) + bit_generator._benchmark(1) + bit_generator._benchmark(1, 'double') + with pytest.raises(ValueError): + bit_generator._benchmark(1, 'int32') + + @pytest.mark.skipif(MISSING_CFFI, reason='cffi not available') + def test_cffi(self): + bit_generator = self.bit_generator(*self.data1['seed']) + cffi_interface = bit_generator.cffi + assert isinstance(cffi_interface, interface) + other_cffi_interface = bit_generator.cffi + assert other_cffi_interface is cffi_interface + + @pytest.mark.skipif(MISSING_CTYPES, reason='ctypes not available') + def test_ctypes(self): + bit_generator = self.bit_generator(*self.data1['seed']) + ctypes_interface = bit_generator.ctypes + assert isinstance(ctypes_interface, interface) + other_ctypes_interface = bit_generator.ctypes + assert other_ctypes_interface is ctypes_interface + + def test_getstate(self): + bit_generator = self.bit_generator(*self.data1['seed']) + state = bit_generator.state + alt_state = bit_generator.__getstate__() + assert_state_equal(state, alt_state) + + +class TestPhilox(Base): + @classmethod + def setup_class(cls): + cls.bit_generator = Philox + cls.bits = 64 + cls.dtype = np.uint64 + cls.data1 = cls._read_csv( + join(pwd, './data/philox-testset-1.csv')) + cls.data2 = cls._read_csv( + join(pwd, './data/philox-testset-2.csv')) + cls.seed_error_type = TypeError + cls.invalid_init_types = [] + cls.invalid_init_values = [(1, None, 1), (-1,), (None, None, 2 ** 257 + 1)] + + def test_set_key(self): + bit_generator = self.bit_generator(*self.data1['seed']) + state = bit_generator.state + keyed = self.bit_generator(counter=state['state']['counter'], + key=state['state']['key']) + assert_state_equal(bit_generator.state, keyed.state) + + +class TestPCG64(Base): + @classmethod + def setup_class(cls): + cls.bit_generator = PCG64 + cls.bits = 64 + cls.dtype = np.uint64 + cls.data1 = cls._read_csv(join(pwd, './data/pcg64-testset-1.csv')) + cls.data2 = cls._read_csv(join(pwd, './data/pcg64-testset-2.csv')) + cls.seed_error_type = (ValueError, TypeError) + cls.invalid_init_types = [(3.2,), ([None],), (1, None)] + cls.invalid_init_values = [(-1,)] + + def test_advance_symmetry(self): + rs = Generator(self.bit_generator(*self.data1['seed'])) + state = rs.bit_generator.state + step = -0x9e3779b97f4a7c150000000000000000 + rs.bit_generator.advance(step) + val_neg = rs.integers(10) + rs.bit_generator.state = state + rs.bit_generator.advance(2**128 + step) + val_pos = rs.integers(10) + rs.bit_generator.state = state + rs.bit_generator.advance(10 * 2**128 + step) + val_big = rs.integers(10) + assert val_neg == val_pos + assert val_big == val_pos + + def test_advange_large(self): + rs = Generator(self.bit_generator(38219308213743)) + pcg = rs.bit_generator + state = pcg.state["state"] + initial_state = 287608843259529770491897792873167516365 + assert state["state"] == initial_state + pcg.advance(sum(2**i for i in (96, 64, 32, 16, 8, 4, 2, 1))) + state = pcg.state["state"] + advanced_state = 135275564607035429730177404003164635391 + assert state["state"] == advanced_state + + +class TestPCG64DXSM(Base): + @classmethod + def setup_class(cls): + cls.bit_generator = PCG64DXSM + cls.bits = 64 + cls.dtype = np.uint64 + cls.data1 = cls._read_csv(join(pwd, './data/pcg64dxsm-testset-1.csv')) + cls.data2 = cls._read_csv(join(pwd, './data/pcg64dxsm-testset-2.csv')) + cls.seed_error_type = (ValueError, TypeError) + cls.invalid_init_types = [(3.2,), ([None],), (1, None)] + cls.invalid_init_values = [(-1,)] + + def test_advance_symmetry(self): + rs = Generator(self.bit_generator(*self.data1['seed'])) + state = rs.bit_generator.state + step = -0x9e3779b97f4a7c150000000000000000 + rs.bit_generator.advance(step) + val_neg = rs.integers(10) + rs.bit_generator.state = state + rs.bit_generator.advance(2**128 + step) + val_pos = rs.integers(10) + rs.bit_generator.state = state + rs.bit_generator.advance(10 * 2**128 + step) + val_big = rs.integers(10) + assert val_neg == val_pos + assert val_big == val_pos + + def test_advange_large(self): + rs = Generator(self.bit_generator(38219308213743)) + pcg = rs.bit_generator + state = pcg.state + initial_state = 287608843259529770491897792873167516365 + assert state["state"]["state"] == initial_state + pcg.advance(sum(2**i for i in (96, 64, 32, 16, 8, 4, 2, 1))) + state = pcg.state["state"] + advanced_state = 277778083536782149546677086420637664879 + assert state["state"] == advanced_state + + +class TestMT19937(Base): + @classmethod + def setup_class(cls): + cls.bit_generator = MT19937 + cls.bits = 32 + cls.dtype = np.uint32 + cls.data1 = cls._read_csv(join(pwd, './data/mt19937-testset-1.csv')) + cls.data2 = cls._read_csv(join(pwd, './data/mt19937-testset-2.csv')) + cls.seed_error_type = ValueError + cls.invalid_init_types = [] + cls.invalid_init_values = [(-1,)] + + def test_seed_float_array(self): + assert_raises(TypeError, self.bit_generator, np.array([np.pi])) + assert_raises(TypeError, self.bit_generator, np.array([-np.pi])) + assert_raises(TypeError, self.bit_generator, np.array([np.pi, -np.pi])) + assert_raises(TypeError, self.bit_generator, np.array([0, np.pi])) + assert_raises(TypeError, self.bit_generator, [np.pi]) + assert_raises(TypeError, self.bit_generator, [0, np.pi]) + + def test_state_tuple(self): + rs = Generator(self.bit_generator(*self.data1['seed'])) + bit_generator = rs.bit_generator + state = bit_generator.state + desired = rs.integers(2 ** 16) + tup = (state['bit_generator'], state['state']['key'], + state['state']['pos']) + bit_generator.state = tup + actual = rs.integers(2 ** 16) + assert_equal(actual, desired) + tup = tup + (0, 0.0) + bit_generator.state = tup + actual = rs.integers(2 ** 16) + assert_equal(actual, desired) + + +class TestSFC64(Base): + @classmethod + def setup_class(cls): + cls.bit_generator = SFC64 + cls.bits = 64 + cls.dtype = np.uint64 + cls.data1 = cls._read_csv( + join(pwd, './data/sfc64-testset-1.csv')) + cls.data2 = cls._read_csv( + join(pwd, './data/sfc64-testset-2.csv')) + cls.seed_error_type = (ValueError, TypeError) + cls.invalid_init_types = [(3.2,), ([None],), (1, None)] + cls.invalid_init_values = [(-1,)] + + +class TestDefaultRNG: + def test_seed(self): + for args in [(), (None,), (1234,), ([1234, 5678],)]: + rg = default_rng(*args) + assert isinstance(rg.bit_generator, PCG64) + + def test_passthrough(self): + bg = Philox() + rg = default_rng(bg) + assert rg.bit_generator is bg + rg2 = default_rng(rg) + assert rg2 is rg + assert rg2.bit_generator is bg diff --git a/numpy/random/tests/test_extending.py b/numpy/random/tests/test_extending.py new file mode 100644 index 000000000000..d362092b5885 --- /dev/null +++ b/numpy/random/tests/test_extending.py @@ -0,0 +1,95 @@ +import os +import pytest +import shutil +import subprocess +import sys +import warnings +import numpy as np +from numpy.distutils.misc_util import exec_mod_from_location + +try: + import cffi +except ImportError: + cffi = None + +if sys.flags.optimize > 1: + # no docstrings present to inspect when PYTHONOPTIMIZE/Py_OptimizeFlag > 1 + # cffi cannot succeed + cffi = None + +try: + with warnings.catch_warnings(record=True) as w: + # numba issue gh-4733 + warnings.filterwarnings('always', '', DeprecationWarning) + import numba +except ImportError: + numba = None + +try: + import cython + from Cython.Compiler.Version import version as cython_version +except ImportError: + cython = None +else: + from distutils.version import LooseVersion + # Cython 0.29.21 is required for Python 3.9 and there are + # other fixes in the 0.29 series that are needed even for earlier + # Python versions. + # Note: keep in sync with the one in pyproject.toml + required_version = LooseVersion('0.29.21') + if LooseVersion(cython_version) < required_version: + # too old or wrong cython, skip the test + cython = None + +@pytest.mark.skipif(cython is None, reason="requires cython") +@pytest.mark.slow +def test_cython(tmp_path): + srcdir = os.path.join(os.path.dirname(__file__), '..') + shutil.copytree(srcdir, tmp_path / 'random') + # build the examples and "install" them into a temporary directory + build_dir = tmp_path / 'random' / '_examples' / 'cython' + subprocess.check_call([sys.executable, 'setup.py', 'build', 'install', + '--prefix', str(tmp_path / 'installdir'), + '--single-version-externally-managed', + '--record', str(tmp_path/ 'tmp_install_log.txt'), + ], + cwd=str(build_dir), + ) + # gh-16162: make sure numpy's __init__.pxd was used for cython + # not really part of this test, but it is a convenient place to check + with open(build_dir / 'extending.c') as fid: + txt_to_find = 'NumPy API declarations from "numpy/__init__.pxd"' + for i, line in enumerate(fid): + if txt_to_find in line: + break + else: + assert False, ("Could not find '{}' in C file, " + "wrong pxd used".format(txt_to_find)) + # get the path to the so's + so1 = so2 = None + with open(tmp_path /'tmp_install_log.txt') as fid: + for line in fid: + if 'extending.' in line: + so1 = line.strip() + if 'extending_distributions' in line: + so2 = line.strip() + assert so1 is not None + assert so2 is not None + # import the so's without adding the directory to sys.path + exec_mod_from_location('extending', so1) + extending_distributions = exec_mod_from_location( + 'extending_distributions', so2) + # actually test the cython c-extension + from numpy.random import PCG64 + values = extending_distributions.uniforms_ex(PCG64(0), 10, 'd') + assert values.shape == (10,) + assert values.dtype == np.float64 + +@pytest.mark.skipif(numba is None or cffi is None, + reason="requires numba and cffi") +def test_numba(): + from numpy.random._examples.numba import extending # noqa: F401 + +@pytest.mark.skipif(cffi is None, reason="requires cffi") +def test_cffi(): + from numpy.random._examples.cffi import extending # noqa: F401 diff --git a/numpy/random/tests/test_generator_mt19937.py b/numpy/random/tests/test_generator_mt19937.py new file mode 100644 index 000000000000..e5411b8ef569 --- /dev/null +++ b/numpy/random/tests/test_generator_mt19937.py @@ -0,0 +1,2671 @@ +import sys +import hashlib + +import pytest + +import numpy as np +from numpy.linalg import LinAlgError +from numpy.testing import ( + assert_, assert_raises, assert_equal, assert_allclose, + assert_warns, assert_no_warnings, assert_array_equal, + assert_array_almost_equal, suppress_warnings) + +from numpy.random import Generator, MT19937, SeedSequence, RandomState + +random = Generator(MT19937()) + +JUMP_TEST_DATA = [ + { + "seed": 0, + "steps": 10, + "initial": {"key_sha256": "bb1636883c2707b51c5b7fc26c6927af4430f2e0785a8c7bc886337f919f9edf", "pos": 9}, + "jumped": {"key_sha256": "ff682ac12bb140f2d72fba8d3506cf4e46817a0db27aae1683867629031d8d55", "pos": 598}, + }, + { + "seed":384908324, + "steps":312, + "initial": {"key_sha256": "16b791a1e04886ccbbb4d448d6ff791267dc458ae599475d08d5cced29d11614", "pos": 311}, + "jumped": {"key_sha256": "a0110a2cf23b56be0feaed8f787a7fc84bef0cb5623003d75b26bdfa1c18002c", "pos": 276}, + }, + { + "seed": [839438204, 980239840, 859048019, 821], + "steps": 511, + "initial": {"key_sha256": "d306cf01314d51bd37892d874308200951a35265ede54d200f1e065004c3e9ea", "pos": 510}, + "jumped": {"key_sha256": "0e00ab449f01a5195a83b4aee0dfbc2ce8d46466a640b92e33977d2e42f777f8", "pos": 475}, + }, +] + +@pytest.fixture(scope='module', params=[True, False]) +def endpoint(request): + return request.param + + +class TestSeed: + def test_scalar(self): + s = Generator(MT19937(0)) + assert_equal(s.integers(1000), 479) + s = Generator(MT19937(4294967295)) + assert_equal(s.integers(1000), 324) + + def test_array(self): + s = Generator(MT19937(range(10))) + assert_equal(s.integers(1000), 465) + s = Generator(MT19937(np.arange(10))) + assert_equal(s.integers(1000), 465) + s = Generator(MT19937([0])) + assert_equal(s.integers(1000), 479) + s = Generator(MT19937([4294967295])) + assert_equal(s.integers(1000), 324) + + def test_seedsequence(self): + s = MT19937(SeedSequence(0)) + assert_equal(s.random_raw(1), 2058676884) + + def test_invalid_scalar(self): + # seed must be an unsigned 32 bit integer + assert_raises(TypeError, MT19937, -0.5) + assert_raises(ValueError, MT19937, -1) + + def test_invalid_array(self): + # seed must be an unsigned integer + assert_raises(TypeError, MT19937, [-0.5]) + assert_raises(ValueError, MT19937, [-1]) + assert_raises(ValueError, MT19937, [1, -2, 4294967296]) + + def test_noninstantized_bitgen(self): + assert_raises(ValueError, Generator, MT19937) + + +class TestBinomial: + def test_n_zero(self): + # Tests the corner case of n == 0 for the binomial distribution. + # binomial(0, p) should be zero for any p in [0, 1]. + # This test addresses issue #3480. + zeros = np.zeros(2, dtype='int') + for p in [0, .5, 1]: + assert_(random.binomial(0, p) == 0) + assert_array_equal(random.binomial(zeros, p), zeros) + + def test_p_is_nan(self): + # Issue #4571. + assert_raises(ValueError, random.binomial, 1, np.nan) + + +class TestMultinomial: + def test_basic(self): + random.multinomial(100, [0.2, 0.8]) + + def test_zero_probability(self): + random.multinomial(100, [0.2, 0.8, 0.0, 0.0, 0.0]) + + def test_int_negative_interval(self): + assert_(-5 <= random.integers(-5, -1) < -1) + x = random.integers(-5, -1, 5) + assert_(np.all(-5 <= x)) + assert_(np.all(x < -1)) + + def test_size(self): + # gh-3173 + p = [0.5, 0.5] + assert_equal(random.multinomial(1, p, np.uint32(1)).shape, (1, 2)) + assert_equal(random.multinomial(1, p, np.uint32(1)).shape, (1, 2)) + assert_equal(random.multinomial(1, p, np.uint32(1)).shape, (1, 2)) + assert_equal(random.multinomial(1, p, [2, 2]).shape, (2, 2, 2)) + assert_equal(random.multinomial(1, p, (2, 2)).shape, (2, 2, 2)) + assert_equal(random.multinomial(1, p, np.array((2, 2))).shape, + (2, 2, 2)) + + assert_raises(TypeError, random.multinomial, 1, p, + float(1)) + + def test_invalid_prob(self): + assert_raises(ValueError, random.multinomial, 100, [1.1, 0.2]) + assert_raises(ValueError, random.multinomial, 100, [-.1, 0.9]) + + def test_invalid_n(self): + assert_raises(ValueError, random.multinomial, -1, [0.8, 0.2]) + assert_raises(ValueError, random.multinomial, [-1] * 10, [0.8, 0.2]) + + def test_p_non_contiguous(self): + p = np.arange(15.) + p /= np.sum(p[1::3]) + pvals = p[1::3] + random = Generator(MT19937(1432985819)) + non_contig = random.multinomial(100, pvals=pvals) + random = Generator(MT19937(1432985819)) + contig = random.multinomial(100, pvals=np.ascontiguousarray(pvals)) + assert_array_equal(non_contig, contig) + + def test_multinomial_pvals_float32(self): + x = np.array([9.9e-01, 9.9e-01, 1.0e-09, 1.0e-09, 1.0e-09, 1.0e-09, + 1.0e-09, 1.0e-09, 1.0e-09, 1.0e-09], dtype=np.float32) + pvals = x / x.sum() + random = Generator(MT19937(1432985819)) + match = r"[\w\s]*pvals array is cast to 64-bit floating" + with pytest.raises(ValueError, match=match): + random.multinomial(1, pvals) + +class TestMultivariateHypergeometric: + + def setup(self): + self.seed = 8675309 + + def test_argument_validation(self): + # Error cases... + + # `colors` must be a 1-d sequence + assert_raises(ValueError, random.multivariate_hypergeometric, + 10, 4) + + # Negative nsample + assert_raises(ValueError, random.multivariate_hypergeometric, + [2, 3, 4], -1) + + # Negative color + assert_raises(ValueError, random.multivariate_hypergeometric, + [-1, 2, 3], 2) + + # nsample exceeds sum(colors) + assert_raises(ValueError, random.multivariate_hypergeometric, + [2, 3, 4], 10) + + # nsample exceeds sum(colors) (edge case of empty colors) + assert_raises(ValueError, random.multivariate_hypergeometric, + [], 1) + + # Validation errors associated with very large values in colors. + assert_raises(ValueError, random.multivariate_hypergeometric, + [999999999, 101], 5, 1, 'marginals') + + int64_info = np.iinfo(np.int64) + max_int64 = int64_info.max + max_int64_index = max_int64 // int64_info.dtype.itemsize + assert_raises(ValueError, random.multivariate_hypergeometric, + [max_int64_index - 100, 101], 5, 1, 'count') + + @pytest.mark.parametrize('method', ['count', 'marginals']) + def test_edge_cases(self, method): + # Set the seed, but in fact, all the results in this test are + # deterministic, so we don't really need this. + random = Generator(MT19937(self.seed)) + + x = random.multivariate_hypergeometric([0, 0, 0], 0, method=method) + assert_array_equal(x, [0, 0, 0]) + + x = random.multivariate_hypergeometric([], 0, method=method) + assert_array_equal(x, []) + + x = random.multivariate_hypergeometric([], 0, size=1, method=method) + assert_array_equal(x, np.empty((1, 0), dtype=np.int64)) + + x = random.multivariate_hypergeometric([1, 2, 3], 0, method=method) + assert_array_equal(x, [0, 0, 0]) + + x = random.multivariate_hypergeometric([9, 0, 0], 3, method=method) + assert_array_equal(x, [3, 0, 0]) + + colors = [1, 1, 0, 1, 1] + x = random.multivariate_hypergeometric(colors, sum(colors), + method=method) + assert_array_equal(x, colors) + + x = random.multivariate_hypergeometric([3, 4, 5], 12, size=3, + method=method) + assert_array_equal(x, [[3, 4, 5]]*3) + + # Cases for nsample: + # nsample < 10 + # 10 <= nsample < colors.sum()/2 + # colors.sum()/2 < nsample < colors.sum() - 10 + # colors.sum() - 10 < nsample < colors.sum() + @pytest.mark.parametrize('nsample', [8, 25, 45, 55]) + @pytest.mark.parametrize('method', ['count', 'marginals']) + @pytest.mark.parametrize('size', [5, (2, 3), 150000]) + def test_typical_cases(self, nsample, method, size): + random = Generator(MT19937(self.seed)) + + colors = np.array([10, 5, 20, 25]) + sample = random.multivariate_hypergeometric(colors, nsample, size, + method=method) + if isinstance(size, int): + expected_shape = (size,) + colors.shape + else: + expected_shape = size + colors.shape + assert_equal(sample.shape, expected_shape) + assert_((sample >= 0).all()) + assert_((sample <= colors).all()) + assert_array_equal(sample.sum(axis=-1), + np.full(size, fill_value=nsample, dtype=int)) + if isinstance(size, int) and size >= 100000: + # This sample is large enough to compare its mean to + # the expected values. + assert_allclose(sample.mean(axis=0), + nsample * colors / colors.sum(), + rtol=1e-3, atol=0.005) + + def test_repeatability1(self): + random = Generator(MT19937(self.seed)) + sample = random.multivariate_hypergeometric([3, 4, 5], 5, size=5, + method='count') + expected = np.array([[2, 1, 2], + [2, 1, 2], + [1, 1, 3], + [2, 0, 3], + [2, 1, 2]]) + assert_array_equal(sample, expected) + + def test_repeatability2(self): + random = Generator(MT19937(self.seed)) + sample = random.multivariate_hypergeometric([20, 30, 50], 50, + size=5, + method='marginals') + expected = np.array([[ 9, 17, 24], + [ 7, 13, 30], + [ 9, 15, 26], + [ 9, 17, 24], + [12, 14, 24]]) + assert_array_equal(sample, expected) + + def test_repeatability3(self): + random = Generator(MT19937(self.seed)) + sample = random.multivariate_hypergeometric([20, 30, 50], 12, + size=5, + method='marginals') + expected = np.array([[2, 3, 7], + [5, 3, 4], + [2, 5, 5], + [5, 3, 4], + [1, 5, 6]]) + assert_array_equal(sample, expected) + + +class TestSetState: + def setup(self): + self.seed = 1234567890 + self.rg = Generator(MT19937(self.seed)) + self.bit_generator = self.rg.bit_generator + self.state = self.bit_generator.state + self.legacy_state = (self.state['bit_generator'], + self.state['state']['key'], + self.state['state']['pos']) + + def test_gaussian_reset(self): + # Make sure the cached every-other-Gaussian is reset. + old = self.rg.standard_normal(size=3) + self.bit_generator.state = self.state + new = self.rg.standard_normal(size=3) + assert_(np.all(old == new)) + + def test_gaussian_reset_in_media_res(self): + # When the state is saved with a cached Gaussian, make sure the + # cached Gaussian is restored. + + self.rg.standard_normal() + state = self.bit_generator.state + old = self.rg.standard_normal(size=3) + self.bit_generator.state = state + new = self.rg.standard_normal(size=3) + assert_(np.all(old == new)) + + def test_negative_binomial(self): + # Ensure that the negative binomial results take floating point + # arguments without truncation. + self.rg.negative_binomial(0.5, 0.5) + + +class TestIntegers: + rfunc = random.integers + + # valid integer/boolean types + itype = [bool, np.int8, np.uint8, np.int16, np.uint16, + np.int32, np.uint32, np.int64, np.uint64] + + def test_unsupported_type(self, endpoint): + assert_raises(TypeError, self.rfunc, 1, endpoint=endpoint, dtype=float) + + def test_bounds_checking(self, endpoint): + for dt in self.itype: + lbnd = 0 if dt is bool else np.iinfo(dt).min + ubnd = 2 if dt is bool else np.iinfo(dt).max + 1 + ubnd = ubnd - 1 if endpoint else ubnd + assert_raises(ValueError, self.rfunc, lbnd - 1, ubnd, + endpoint=endpoint, dtype=dt) + assert_raises(ValueError, self.rfunc, lbnd, ubnd + 1, + endpoint=endpoint, dtype=dt) + assert_raises(ValueError, self.rfunc, ubnd, lbnd, + endpoint=endpoint, dtype=dt) + assert_raises(ValueError, self.rfunc, 1, 0, endpoint=endpoint, + dtype=dt) + + assert_raises(ValueError, self.rfunc, [lbnd - 1], ubnd, + endpoint=endpoint, dtype=dt) + assert_raises(ValueError, self.rfunc, [lbnd], [ubnd + 1], + endpoint=endpoint, dtype=dt) + assert_raises(ValueError, self.rfunc, [ubnd], [lbnd], + endpoint=endpoint, dtype=dt) + assert_raises(ValueError, self.rfunc, 1, [0], + endpoint=endpoint, dtype=dt) + + def test_bounds_checking_array(self, endpoint): + for dt in self.itype: + lbnd = 0 if dt is bool else np.iinfo(dt).min + ubnd = 2 if dt is bool else np.iinfo(dt).max + (not endpoint) + + assert_raises(ValueError, self.rfunc, [lbnd - 1] * 2, [ubnd] * 2, + endpoint=endpoint, dtype=dt) + assert_raises(ValueError, self.rfunc, [lbnd] * 2, + [ubnd + 1] * 2, endpoint=endpoint, dtype=dt) + assert_raises(ValueError, self.rfunc, ubnd, [lbnd] * 2, + endpoint=endpoint, dtype=dt) + assert_raises(ValueError, self.rfunc, [1] * 2, 0, + endpoint=endpoint, dtype=dt) + + def test_rng_zero_and_extremes(self, endpoint): + for dt in self.itype: + lbnd = 0 if dt is bool else np.iinfo(dt).min + ubnd = 2 if dt is bool else np.iinfo(dt).max + 1 + ubnd = ubnd - 1 if endpoint else ubnd + is_open = not endpoint + + tgt = ubnd - 1 + assert_equal(self.rfunc(tgt, tgt + is_open, size=1000, + endpoint=endpoint, dtype=dt), tgt) + assert_equal(self.rfunc([tgt], tgt + is_open, size=1000, + endpoint=endpoint, dtype=dt), tgt) + + tgt = lbnd + assert_equal(self.rfunc(tgt, tgt + is_open, size=1000, + endpoint=endpoint, dtype=dt), tgt) + assert_equal(self.rfunc(tgt, [tgt + is_open], size=1000, + endpoint=endpoint, dtype=dt), tgt) + + tgt = (lbnd + ubnd) // 2 + assert_equal(self.rfunc(tgt, tgt + is_open, size=1000, + endpoint=endpoint, dtype=dt), tgt) + assert_equal(self.rfunc([tgt], [tgt + is_open], + size=1000, endpoint=endpoint, dtype=dt), + tgt) + + def test_rng_zero_and_extremes_array(self, endpoint): + size = 1000 + for dt in self.itype: + lbnd = 0 if dt is bool else np.iinfo(dt).min + ubnd = 2 if dt is bool else np.iinfo(dt).max + 1 + ubnd = ubnd - 1 if endpoint else ubnd + + tgt = ubnd - 1 + assert_equal(self.rfunc([tgt], [tgt + 1], + size=size, dtype=dt), tgt) + assert_equal(self.rfunc( + [tgt] * size, [tgt + 1] * size, dtype=dt), tgt) + assert_equal(self.rfunc( + [tgt] * size, [tgt + 1] * size, size=size, dtype=dt), tgt) + + tgt = lbnd + assert_equal(self.rfunc([tgt], [tgt + 1], + size=size, dtype=dt), tgt) + assert_equal(self.rfunc( + [tgt] * size, [tgt + 1] * size, dtype=dt), tgt) + assert_equal(self.rfunc( + [tgt] * size, [tgt + 1] * size, size=size, dtype=dt), tgt) + + tgt = (lbnd + ubnd) // 2 + assert_equal(self.rfunc([tgt], [tgt + 1], + size=size, dtype=dt), tgt) + assert_equal(self.rfunc( + [tgt] * size, [tgt + 1] * size, dtype=dt), tgt) + assert_equal(self.rfunc( + [tgt] * size, [tgt + 1] * size, size=size, dtype=dt), tgt) + + def test_full_range(self, endpoint): + # Test for ticket #1690 + + for dt in self.itype: + lbnd = 0 if dt is bool else np.iinfo(dt).min + ubnd = 2 if dt is bool else np.iinfo(dt).max + 1 + ubnd = ubnd - 1 if endpoint else ubnd + + try: + self.rfunc(lbnd, ubnd, endpoint=endpoint, dtype=dt) + except Exception as e: + raise AssertionError("No error should have been raised, " + "but one was with the following " + "message:\n\n%s" % str(e)) + + def test_full_range_array(self, endpoint): + # Test for ticket #1690 + + for dt in self.itype: + lbnd = 0 if dt is bool else np.iinfo(dt).min + ubnd = 2 if dt is bool else np.iinfo(dt).max + 1 + ubnd = ubnd - 1 if endpoint else ubnd + + try: + self.rfunc([lbnd] * 2, [ubnd], endpoint=endpoint, dtype=dt) + except Exception as e: + raise AssertionError("No error should have been raised, " + "but one was with the following " + "message:\n\n%s" % str(e)) + + def test_in_bounds_fuzz(self, endpoint): + # Don't use fixed seed + random = Generator(MT19937()) + + for dt in self.itype[1:]: + for ubnd in [4, 8, 16]: + vals = self.rfunc(2, ubnd - endpoint, size=2 ** 16, + endpoint=endpoint, dtype=dt) + assert_(vals.max() < ubnd) + assert_(vals.min() >= 2) + + vals = self.rfunc(0, 2 - endpoint, size=2 ** 16, endpoint=endpoint, + dtype=bool) + assert_(vals.max() < 2) + assert_(vals.min() >= 0) + + def test_scalar_array_equiv(self, endpoint): + for dt in self.itype: + lbnd = 0 if dt is bool else np.iinfo(dt).min + ubnd = 2 if dt is bool else np.iinfo(dt).max + 1 + ubnd = ubnd - 1 if endpoint else ubnd + + size = 1000 + random = Generator(MT19937(1234)) + scalar = random.integers(lbnd, ubnd, size=size, endpoint=endpoint, + dtype=dt) + + random = Generator(MT19937(1234)) + scalar_array = random.integers([lbnd], [ubnd], size=size, + endpoint=endpoint, dtype=dt) + + random = Generator(MT19937(1234)) + array = random.integers([lbnd] * size, [ubnd] * + size, size=size, endpoint=endpoint, dtype=dt) + assert_array_equal(scalar, scalar_array) + assert_array_equal(scalar, array) + + def test_repeatability(self, endpoint): + # We use a sha256 hash of generated sequences of 1000 samples + # in the range [0, 6) for all but bool, where the range + # is [0, 2). Hashes are for little endian numbers. + tgt = {'bool': '053594a9b82d656f967c54869bc6970aa0358cf94ad469c81478459c6a90eee3', + 'int16': '54de9072b6ee9ff7f20b58329556a46a447a8a29d67db51201bf88baa6e4e5d4', + 'int32': 'd3a0d5efb04542b25ac712e50d21f39ac30f312a5052e9bbb1ad3baa791ac84b', + 'int64': '14e224389ac4580bfbdccb5697d6190b496f91227cf67df60989de3d546389b1', + 'int8': '0e203226ff3fbbd1580f15da4621e5f7164d0d8d6b51696dd42d004ece2cbec1', + 'uint16': '54de9072b6ee9ff7f20b58329556a46a447a8a29d67db51201bf88baa6e4e5d4', + 'uint32': 'd3a0d5efb04542b25ac712e50d21f39ac30f312a5052e9bbb1ad3baa791ac84b', + 'uint64': '14e224389ac4580bfbdccb5697d6190b496f91227cf67df60989de3d546389b1', + 'uint8': '0e203226ff3fbbd1580f15da4621e5f7164d0d8d6b51696dd42d004ece2cbec1'} + + for dt in self.itype[1:]: + random = Generator(MT19937(1234)) + + # view as little endian for hash + if sys.byteorder == 'little': + val = random.integers(0, 6 - endpoint, size=1000, endpoint=endpoint, + dtype=dt) + else: + val = random.integers(0, 6 - endpoint, size=1000, endpoint=endpoint, + dtype=dt).byteswap() + + res = hashlib.sha256(val).hexdigest() + assert_(tgt[np.dtype(dt).name] == res) + + # bools do not depend on endianness + random = Generator(MT19937(1234)) + val = random.integers(0, 2 - endpoint, size=1000, endpoint=endpoint, + dtype=bool).view(np.int8) + res = hashlib.sha256(val).hexdigest() + assert_(tgt[np.dtype(bool).name] == res) + + def test_repeatability_broadcasting(self, endpoint): + for dt in self.itype: + lbnd = 0 if dt in (bool, np.bool_) else np.iinfo(dt).min + ubnd = 2 if dt in (bool, np.bool_) else np.iinfo(dt).max + 1 + ubnd = ubnd - 1 if endpoint else ubnd + + # view as little endian for hash + random = Generator(MT19937(1234)) + val = random.integers(lbnd, ubnd, size=1000, endpoint=endpoint, + dtype=dt) + + random = Generator(MT19937(1234)) + val_bc = random.integers([lbnd] * 1000, ubnd, endpoint=endpoint, + dtype=dt) + + assert_array_equal(val, val_bc) + + random = Generator(MT19937(1234)) + val_bc = random.integers([lbnd] * 1000, [ubnd] * 1000, + endpoint=endpoint, dtype=dt) + + assert_array_equal(val, val_bc) + + @pytest.mark.parametrize( + 'bound, expected', + [(2**32 - 1, np.array([517043486, 1364798665, 1733884389, 1353720612, + 3769704066, 1170797179, 4108474671])), + (2**32, np.array([517043487, 1364798666, 1733884390, 1353720613, + 3769704067, 1170797180, 4108474672])), + (2**32 + 1, np.array([517043487, 1733884390, 3769704068, 4108474673, + 1831631863, 1215661561, 3869512430]))] + ) + def test_repeatability_32bit_boundary(self, bound, expected): + for size in [None, len(expected)]: + random = Generator(MT19937(1234)) + x = random.integers(bound, size=size) + assert_equal(x, expected if size is not None else expected[0]) + + def test_repeatability_32bit_boundary_broadcasting(self): + desired = np.array([[[1622936284, 3620788691, 1659384060], + [1417365545, 760222891, 1909653332], + [3788118662, 660249498, 4092002593]], + [[3625610153, 2979601262, 3844162757], + [ 685800658, 120261497, 2694012896], + [1207779440, 1586594375, 3854335050]], + [[3004074748, 2310761796, 3012642217], + [2067714190, 2786677879, 1363865881], + [ 791663441, 1867303284, 2169727960]], + [[1939603804, 1250951100, 298950036], + [1040128489, 3791912209, 3317053765], + [3155528714, 61360675, 2305155588]], + [[ 817688762, 1335621943, 3288952434], + [1770890872, 1102951817, 1957607470], + [3099996017, 798043451, 48334215]]]) + for size in [None, (5, 3, 3)]: + random = Generator(MT19937(12345)) + x = random.integers([[-1], [0], [1]], + [2**32 - 1, 2**32, 2**32 + 1], + size=size) + assert_array_equal(x, desired if size is not None else desired[0]) + + def test_int64_uint64_broadcast_exceptions(self, endpoint): + configs = {np.uint64: ((0, 2**65), (-1, 2**62), (10, 9), (0, 0)), + np.int64: ((0, 2**64), (-(2**64), 2**62), (10, 9), (0, 0), + (-2**63-1, -2**63-1))} + for dtype in configs: + for config in configs[dtype]: + low, high = config + high = high - endpoint + low_a = np.array([[low]*10]) + high_a = np.array([high] * 10) + assert_raises(ValueError, random.integers, low, high, + endpoint=endpoint, dtype=dtype) + assert_raises(ValueError, random.integers, low_a, high, + endpoint=endpoint, dtype=dtype) + assert_raises(ValueError, random.integers, low, high_a, + endpoint=endpoint, dtype=dtype) + assert_raises(ValueError, random.integers, low_a, high_a, + endpoint=endpoint, dtype=dtype) + + low_o = np.array([[low]*10], dtype=object) + high_o = np.array([high] * 10, dtype=object) + assert_raises(ValueError, random.integers, low_o, high, + endpoint=endpoint, dtype=dtype) + assert_raises(ValueError, random.integers, low, high_o, + endpoint=endpoint, dtype=dtype) + assert_raises(ValueError, random.integers, low_o, high_o, + endpoint=endpoint, dtype=dtype) + + def test_int64_uint64_corner_case(self, endpoint): + # When stored in Numpy arrays, `lbnd` is casted + # as np.int64, and `ubnd` is casted as np.uint64. + # Checking whether `lbnd` >= `ubnd` used to be + # done solely via direct comparison, which is incorrect + # because when Numpy tries to compare both numbers, + # it casts both to np.float64 because there is + # no integer superset of np.int64 and np.uint64. However, + # `ubnd` is too large to be represented in np.float64, + # causing it be round down to np.iinfo(np.int64).max, + # leading to a ValueError because `lbnd` now equals + # the new `ubnd`. + + dt = np.int64 + tgt = np.iinfo(np.int64).max + lbnd = np.int64(np.iinfo(np.int64).max) + ubnd = np.uint64(np.iinfo(np.int64).max + 1 - endpoint) + + # None of these function calls should + # generate a ValueError now. + actual = random.integers(lbnd, ubnd, endpoint=endpoint, dtype=dt) + assert_equal(actual, tgt) + + def test_respect_dtype_singleton(self, endpoint): + # See gh-7203 + for dt in self.itype: + lbnd = 0 if dt is bool else np.iinfo(dt).min + ubnd = 2 if dt is bool else np.iinfo(dt).max + 1 + ubnd = ubnd - 1 if endpoint else ubnd + dt = np.bool_ if dt is bool else dt + + sample = self.rfunc(lbnd, ubnd, endpoint=endpoint, dtype=dt) + assert_equal(sample.dtype, dt) + + for dt in (bool, int, np.compat.long): + lbnd = 0 if dt is bool else np.iinfo(dt).min + ubnd = 2 if dt is bool else np.iinfo(dt).max + 1 + ubnd = ubnd - 1 if endpoint else ubnd + + # gh-7284: Ensure that we get Python data types + sample = self.rfunc(lbnd, ubnd, endpoint=endpoint, dtype=dt) + assert not hasattr(sample, 'dtype') + assert_equal(type(sample), dt) + + def test_respect_dtype_array(self, endpoint): + # See gh-7203 + for dt in self.itype: + lbnd = 0 if dt is bool else np.iinfo(dt).min + ubnd = 2 if dt is bool else np.iinfo(dt).max + 1 + ubnd = ubnd - 1 if endpoint else ubnd + dt = np.bool_ if dt is bool else dt + + sample = self.rfunc([lbnd], [ubnd], endpoint=endpoint, dtype=dt) + assert_equal(sample.dtype, dt) + sample = self.rfunc([lbnd] * 2, [ubnd] * 2, endpoint=endpoint, + dtype=dt) + assert_equal(sample.dtype, dt) + + def test_zero_size(self, endpoint): + # See gh-7203 + for dt in self.itype: + sample = self.rfunc(0, 0, (3, 0, 4), endpoint=endpoint, dtype=dt) + assert sample.shape == (3, 0, 4) + assert sample.dtype == dt + assert self.rfunc(0, -10, 0, endpoint=endpoint, + dtype=dt).shape == (0,) + assert_equal(random.integers(0, 0, size=(3, 0, 4)).shape, + (3, 0, 4)) + assert_equal(random.integers(0, -10, size=0).shape, (0,)) + assert_equal(random.integers(10, 10, size=0).shape, (0,)) + + def test_error_byteorder(self): + other_byteord_dt = '<i4' if sys.byteorder == 'big' else '>i4' + with pytest.raises(ValueError): + random.integers(0, 200, size=10, dtype=other_byteord_dt) + + # chi2max is the maximum acceptable chi-squared value. + @pytest.mark.slow + @pytest.mark.parametrize('sample_size,high,dtype,chi2max', + [(5000000, 5, np.int8, 125.0), # p-value ~4.6e-25 + (5000000, 7, np.uint8, 150.0), # p-value ~7.7e-30 + (10000000, 2500, np.int16, 3300.0), # p-value ~3.0e-25 + (50000000, 5000, np.uint16, 6500.0), # p-value ~3.5e-25 + ]) + def test_integers_small_dtype_chisquared(self, sample_size, high, + dtype, chi2max): + # Regression test for gh-14774. + samples = random.integers(high, size=sample_size, dtype=dtype) + + values, counts = np.unique(samples, return_counts=True) + expected = sample_size / high + chi2 = ((counts - expected)**2 / expected).sum() + assert chi2 < chi2max + + +class TestRandomDist: + # Make sure the random distribution returns the correct value for a + # given seed + + def setup(self): + self.seed = 1234567890 + + def test_integers(self): + random = Generator(MT19937(self.seed)) + actual = random.integers(-99, 99, size=(3, 2)) + desired = np.array([[-80, -56], [41, 37], [-83, -16]]) + assert_array_equal(actual, desired) + + def test_integers_masked(self): + # Test masked rejection sampling algorithm to generate array of + # uint32 in an interval. + random = Generator(MT19937(self.seed)) + actual = random.integers(0, 99, size=(3, 2), dtype=np.uint32) + desired = np.array([[9, 21], [70, 68], [8, 41]], dtype=np.uint32) + assert_array_equal(actual, desired) + + def test_integers_closed(self): + random = Generator(MT19937(self.seed)) + actual = random.integers(-99, 99, size=(3, 2), endpoint=True) + desired = np.array([[-80, -56], [ 41, 38], [-83, -15]]) + assert_array_equal(actual, desired) + + def test_integers_max_int(self): + # Tests whether integers with closed=True can generate the + # maximum allowed Python int that can be converted + # into a C long. Previous implementations of this + # method have thrown an OverflowError when attempting + # to generate this integer. + actual = random.integers(np.iinfo('l').max, np.iinfo('l').max, + endpoint=True) + + desired = np.iinfo('l').max + assert_equal(actual, desired) + + def test_random(self): + random = Generator(MT19937(self.seed)) + actual = random.random((3, 2)) + desired = np.array([[0.096999199829214, 0.707517457682192], + [0.084364834598269, 0.767731206553125], + [0.665069021359413, 0.715487190596693]]) + assert_array_almost_equal(actual, desired, decimal=15) + + random = Generator(MT19937(self.seed)) + actual = random.random() + assert_array_almost_equal(actual, desired[0, 0], decimal=15) + + def test_random_float(self): + random = Generator(MT19937(self.seed)) + actual = random.random((3, 2)) + desired = np.array([[0.0969992 , 0.70751746], + [0.08436483, 0.76773121], + [0.66506902, 0.71548719]]) + assert_array_almost_equal(actual, desired, decimal=7) + + def test_random_float_scalar(self): + random = Generator(MT19937(self.seed)) + actual = random.random(dtype=np.float32) + desired = 0.0969992 + assert_array_almost_equal(actual, desired, decimal=7) + + @pytest.mark.parametrize('dtype, uint_view_type', + [(np.float32, np.uint32), + (np.float64, np.uint64)]) + def test_random_distribution_of_lsb(self, dtype, uint_view_type): + random = Generator(MT19937(self.seed)) + sample = random.random(100000, dtype=dtype) + num_ones_in_lsb = np.count_nonzero(sample.view(uint_view_type) & 1) + # The probability of a 1 in the least significant bit is 0.25. + # With a sample size of 100000, the probability that num_ones_in_lsb + # is outside the following range is less than 5e-11. + assert 24100 < num_ones_in_lsb < 25900 + + def test_random_unsupported_type(self): + assert_raises(TypeError, random.random, dtype='int32') + + def test_choice_uniform_replace(self): + random = Generator(MT19937(self.seed)) + actual = random.choice(4, 4) + desired = np.array([0, 0, 2, 2], dtype=np.int64) + assert_array_equal(actual, desired) + + def test_choice_nonuniform_replace(self): + random = Generator(MT19937(self.seed)) + actual = random.choice(4, 4, p=[0.4, 0.4, 0.1, 0.1]) + desired = np.array([0, 1, 0, 1], dtype=np.int64) + assert_array_equal(actual, desired) + + def test_choice_uniform_noreplace(self): + random = Generator(MT19937(self.seed)) + actual = random.choice(4, 3, replace=False) + desired = np.array([2, 0, 3], dtype=np.int64) + assert_array_equal(actual, desired) + actual = random.choice(4, 4, replace=False, shuffle=False) + desired = np.arange(4, dtype=np.int64) + assert_array_equal(actual, desired) + + def test_choice_nonuniform_noreplace(self): + random = Generator(MT19937(self.seed)) + actual = random.choice(4, 3, replace=False, p=[0.1, 0.3, 0.5, 0.1]) + desired = np.array([0, 2, 3], dtype=np.int64) + assert_array_equal(actual, desired) + + def test_choice_noninteger(self): + random = Generator(MT19937(self.seed)) + actual = random.choice(['a', 'b', 'c', 'd'], 4) + desired = np.array(['a', 'a', 'c', 'c']) + assert_array_equal(actual, desired) + + def test_choice_multidimensional_default_axis(self): + random = Generator(MT19937(self.seed)) + actual = random.choice([[0, 1], [2, 3], [4, 5], [6, 7]], 3) + desired = np.array([[0, 1], [0, 1], [4, 5]]) + assert_array_equal(actual, desired) + + def test_choice_multidimensional_custom_axis(self): + random = Generator(MT19937(self.seed)) + actual = random.choice([[0, 1], [2, 3], [4, 5], [6, 7]], 1, axis=1) + desired = np.array([[0], [2], [4], [6]]) + assert_array_equal(actual, desired) + + def test_choice_exceptions(self): + sample = random.choice + assert_raises(ValueError, sample, -1, 3) + assert_raises(ValueError, sample, 3., 3) + assert_raises(ValueError, sample, [], 3) + assert_raises(ValueError, sample, [1, 2, 3, 4], 3, + p=[[0.25, 0.25], [0.25, 0.25]]) + assert_raises(ValueError, sample, [1, 2], 3, p=[0.4, 0.4, 0.2]) + assert_raises(ValueError, sample, [1, 2], 3, p=[1.1, -0.1]) + assert_raises(ValueError, sample, [1, 2], 3, p=[0.4, 0.4]) + assert_raises(ValueError, sample, [1, 2, 3], 4, replace=False) + # gh-13087 + assert_raises(ValueError, sample, [1, 2, 3], -2, replace=False) + assert_raises(ValueError, sample, [1, 2, 3], (-1,), replace=False) + assert_raises(ValueError, sample, [1, 2, 3], (-1, 1), replace=False) + assert_raises(ValueError, sample, [1, 2, 3], 2, + replace=False, p=[1, 0, 0]) + + def test_choice_return_shape(self): + p = [0.1, 0.9] + # Check scalar + assert_(np.isscalar(random.choice(2, replace=True))) + assert_(np.isscalar(random.choice(2, replace=False))) + assert_(np.isscalar(random.choice(2, replace=True, p=p))) + assert_(np.isscalar(random.choice(2, replace=False, p=p))) + assert_(np.isscalar(random.choice([1, 2], replace=True))) + assert_(random.choice([None], replace=True) is None) + a = np.array([1, 2]) + arr = np.empty(1, dtype=object) + arr[0] = a + assert_(random.choice(arr, replace=True) is a) + + # Check 0-d array + s = tuple() + assert_(not np.isscalar(random.choice(2, s, replace=True))) + assert_(not np.isscalar(random.choice(2, s, replace=False))) + assert_(not np.isscalar(random.choice(2, s, replace=True, p=p))) + assert_(not np.isscalar(random.choice(2, s, replace=False, p=p))) + assert_(not np.isscalar(random.choice([1, 2], s, replace=True))) + assert_(random.choice([None], s, replace=True).ndim == 0) + a = np.array([1, 2]) + arr = np.empty(1, dtype=object) + arr[0] = a + assert_(random.choice(arr, s, replace=True).item() is a) + + # Check multi dimensional array + s = (2, 3) + p = [0.1, 0.1, 0.1, 0.1, 0.4, 0.2] + assert_equal(random.choice(6, s, replace=True).shape, s) + assert_equal(random.choice(6, s, replace=False).shape, s) + assert_equal(random.choice(6, s, replace=True, p=p).shape, s) + assert_equal(random.choice(6, s, replace=False, p=p).shape, s) + assert_equal(random.choice(np.arange(6), s, replace=True).shape, s) + + # Check zero-size + assert_equal(random.integers(0, 0, size=(3, 0, 4)).shape, (3, 0, 4)) + assert_equal(random.integers(0, -10, size=0).shape, (0,)) + assert_equal(random.integers(10, 10, size=0).shape, (0,)) + assert_equal(random.choice(0, size=0).shape, (0,)) + assert_equal(random.choice([], size=(0,)).shape, (0,)) + assert_equal(random.choice(['a', 'b'], size=(3, 0, 4)).shape, + (3, 0, 4)) + assert_raises(ValueError, random.choice, [], 10) + + def test_choice_nan_probabilities(self): + a = np.array([42, 1, 2]) + p = [None, None, None] + assert_raises(ValueError, random.choice, a, p=p) + + def test_choice_p_non_contiguous(self): + p = np.ones(10) / 5 + p[1::2] = 3.0 + random = Generator(MT19937(self.seed)) + non_contig = random.choice(5, 3, p=p[::2]) + random = Generator(MT19937(self.seed)) + contig = random.choice(5, 3, p=np.ascontiguousarray(p[::2])) + assert_array_equal(non_contig, contig) + + def test_choice_return_type(self): + # gh 9867 + p = np.ones(4) / 4. + actual = random.choice(4, 2) + assert actual.dtype == np.int64 + actual = random.choice(4, 2, replace=False) + assert actual.dtype == np.int64 + actual = random.choice(4, 2, p=p) + assert actual.dtype == np.int64 + actual = random.choice(4, 2, p=p, replace=False) + assert actual.dtype == np.int64 + + def test_choice_large_sample(self): + choice_hash = '4266599d12bfcfb815213303432341c06b4349f5455890446578877bb322e222' + random = Generator(MT19937(self.seed)) + actual = random.choice(10000, 5000, replace=False) + if sys.byteorder != 'little': + actual = actual.byteswap() + res = hashlib.sha256(actual.view(np.int8)).hexdigest() + assert_(choice_hash == res) + + def test_bytes(self): + random = Generator(MT19937(self.seed)) + actual = random.bytes(10) + desired = b'\x86\xf0\xd4\x18\xe1\x81\t8%\xdd' + assert_equal(actual, desired) + + def test_shuffle(self): + # Test lists, arrays (of various dtypes), and multidimensional versions + # of both, c-contiguous or not: + for conv in [lambda x: np.array([]), + lambda x: x, + lambda x: np.asarray(x).astype(np.int8), + lambda x: np.asarray(x).astype(np.float32), + lambda x: np.asarray(x).astype(np.complex64), + lambda x: np.asarray(x).astype(object), + lambda x: [(i, i) for i in x], + lambda x: np.asarray([[i, i] for i in x]), + lambda x: np.vstack([x, x]).T, + # gh-11442 + lambda x: (np.asarray([(i, i) for i in x], + [("a", int), ("b", int)]) + .view(np.recarray)), + # gh-4270 + lambda x: np.asarray([(i, i) for i in x], + [("a", object, (1,)), + ("b", np.int32, (1,))])]: + random = Generator(MT19937(self.seed)) + alist = conv([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]) + random.shuffle(alist) + actual = alist + desired = conv([4, 1, 9, 8, 0, 5, 3, 6, 2, 7]) + assert_array_equal(actual, desired) + + def test_shuffle_custom_axis(self): + random = Generator(MT19937(self.seed)) + actual = np.arange(16).reshape((4, 4)) + random.shuffle(actual, axis=1) + desired = np.array([[ 0, 3, 1, 2], + [ 4, 7, 5, 6], + [ 8, 11, 9, 10], + [12, 15, 13, 14]]) + assert_array_equal(actual, desired) + random = Generator(MT19937(self.seed)) + actual = np.arange(16).reshape((4, 4)) + random.shuffle(actual, axis=-1) + assert_array_equal(actual, desired) + + def test_shuffle_custom_axis_empty(self): + random = Generator(MT19937(self.seed)) + desired = np.array([]).reshape((0, 6)) + for axis in (0, 1): + actual = np.array([]).reshape((0, 6)) + random.shuffle(actual, axis=axis) + assert_array_equal(actual, desired) + + def test_shuffle_axis_nonsquare(self): + y1 = np.arange(20).reshape(2, 10) + y2 = y1.copy() + random = Generator(MT19937(self.seed)) + random.shuffle(y1, axis=1) + random = Generator(MT19937(self.seed)) + random.shuffle(y2.T) + assert_array_equal(y1, y2) + + def test_shuffle_masked(self): + # gh-3263 + a = np.ma.masked_values(np.reshape(range(20), (5, 4)) % 3 - 1, -1) + b = np.ma.masked_values(np.arange(20) % 3 - 1, -1) + a_orig = a.copy() + b_orig = b.copy() + for i in range(50): + random.shuffle(a) + assert_equal( + sorted(a.data[~a.mask]), sorted(a_orig.data[~a_orig.mask])) + random.shuffle(b) + assert_equal( + sorted(b.data[~b.mask]), sorted(b_orig.data[~b_orig.mask])) + + def test_shuffle_exceptions(self): + random = Generator(MT19937(self.seed)) + arr = np.arange(10) + assert_raises(np.AxisError, random.shuffle, arr, 1) + arr = np.arange(9).reshape((3, 3)) + assert_raises(np.AxisError, random.shuffle, arr, 3) + assert_raises(TypeError, random.shuffle, arr, slice(1, 2, None)) + arr = [[1, 2, 3], [4, 5, 6]] + assert_raises(NotImplementedError, random.shuffle, arr, 1) + + arr = np.array(3) + assert_raises(TypeError, random.shuffle, arr) + arr = np.ones((3, 2)) + assert_raises(np.AxisError, random.shuffle, arr, 2) + + def test_permutation(self): + random = Generator(MT19937(self.seed)) + alist = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] + actual = random.permutation(alist) + desired = [4, 1, 9, 8, 0, 5, 3, 6, 2, 7] + assert_array_equal(actual, desired) + + random = Generator(MT19937(self.seed)) + arr_2d = np.atleast_2d([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]).T + actual = random.permutation(arr_2d) + assert_array_equal(actual, np.atleast_2d(desired).T) + + bad_x_str = "abcd" + assert_raises(np.AxisError, random.permutation, bad_x_str) + + bad_x_float = 1.2 + assert_raises(np.AxisError, random.permutation, bad_x_float) + + random = Generator(MT19937(self.seed)) + integer_val = 10 + desired = [3, 0, 8, 7, 9, 4, 2, 5, 1, 6] + + actual = random.permutation(integer_val) + assert_array_equal(actual, desired) + + def test_permutation_custom_axis(self): + a = np.arange(16).reshape((4, 4)) + desired = np.array([[ 0, 3, 1, 2], + [ 4, 7, 5, 6], + [ 8, 11, 9, 10], + [12, 15, 13, 14]]) + random = Generator(MT19937(self.seed)) + actual = random.permutation(a, axis=1) + assert_array_equal(actual, desired) + random = Generator(MT19937(self.seed)) + actual = random.permutation(a, axis=-1) + assert_array_equal(actual, desired) + + def test_permutation_exceptions(self): + random = Generator(MT19937(self.seed)) + arr = np.arange(10) + assert_raises(np.AxisError, random.permutation, arr, 1) + arr = np.arange(9).reshape((3, 3)) + assert_raises(np.AxisError, random.permutation, arr, 3) + assert_raises(TypeError, random.permutation, arr, slice(1, 2, None)) + + @pytest.mark.parametrize("dtype", [int, object]) + @pytest.mark.parametrize("axis, expected", + [(None, np.array([[3, 7, 0, 9, 10, 11], + [8, 4, 2, 5, 1, 6]])), + (0, np.array([[6, 1, 2, 9, 10, 11], + [0, 7, 8, 3, 4, 5]])), + (1, np.array([[ 5, 3, 4, 0, 2, 1], + [11, 9, 10, 6, 8, 7]]))]) + def test_permuted(self, dtype, axis, expected): + random = Generator(MT19937(self.seed)) + x = np.arange(12).reshape(2, 6).astype(dtype) + random.permuted(x, axis=axis, out=x) + assert_array_equal(x, expected) + + random = Generator(MT19937(self.seed)) + x = np.arange(12).reshape(2, 6).astype(dtype) + y = random.permuted(x, axis=axis) + assert y.dtype == dtype + assert_array_equal(y, expected) + + def test_permuted_with_strides(self): + random = Generator(MT19937(self.seed)) + x0 = np.arange(22).reshape(2, 11) + x1 = x0.copy() + x = x0[:, ::3] + y = random.permuted(x, axis=1, out=x) + expected = np.array([[0, 9, 3, 6], + [14, 20, 11, 17]]) + assert_array_equal(y, expected) + x1[:, ::3] = expected + # Verify that the original x0 was modified in-place as expected. + assert_array_equal(x1, x0) + + def test_permuted_empty(self): + y = random.permuted([]) + assert_array_equal(y, []) + + @pytest.mark.parametrize('outshape', [(2, 3), 5]) + def test_permuted_out_with_wrong_shape(self, outshape): + a = np.array([1, 2, 3]) + out = np.zeros(outshape, dtype=a.dtype) + with pytest.raises(ValueError, match='same shape'): + random.permuted(a, out=out) + + def test_permuted_out_with_wrong_type(self): + out = np.zeros((3, 5), dtype=np.int32) + x = np.ones((3, 5)) + with pytest.raises(TypeError, match='Cannot cast'): + random.permuted(x, axis=1, out=out) + + def test_beta(self): + random = Generator(MT19937(self.seed)) + actual = random.beta(.1, .9, size=(3, 2)) + desired = np.array( + [[1.083029353267698e-10, 2.449965303168024e-11], + [2.397085162969853e-02, 3.590779671820755e-08], + [2.830254190078299e-04, 1.744709918330393e-01]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_binomial(self): + random = Generator(MT19937(self.seed)) + actual = random.binomial(100.123, .456, size=(3, 2)) + desired = np.array([[42, 41], + [42, 48], + [44, 50]]) + assert_array_equal(actual, desired) + + random = Generator(MT19937(self.seed)) + actual = random.binomial(100.123, .456) + desired = 42 + assert_array_equal(actual, desired) + + def test_chisquare(self): + random = Generator(MT19937(self.seed)) + actual = random.chisquare(50, size=(3, 2)) + desired = np.array([[32.9850547060149, 39.0219480493301], + [56.2006134779419, 57.3474165711485], + [55.4243733880198, 55.4209797925213]]) + assert_array_almost_equal(actual, desired, decimal=13) + + def test_dirichlet(self): + random = Generator(MT19937(self.seed)) + alpha = np.array([51.72840233779265162, 39.74494232180943953]) + actual = random.dirichlet(alpha, size=(3, 2)) + desired = np.array([[[0.5439892869558927, 0.45601071304410745], + [0.5588917345860708, 0.4411082654139292 ]], + [[0.5632074165063435, 0.43679258349365657], + [0.54862581112627, 0.45137418887373015]], + [[0.49961831357047226, 0.5003816864295278 ], + [0.52374806183482, 0.47625193816517997]]]) + assert_array_almost_equal(actual, desired, decimal=15) + bad_alpha = np.array([5.4e-01, -1.0e-16]) + assert_raises(ValueError, random.dirichlet, bad_alpha) + + random = Generator(MT19937(self.seed)) + alpha = np.array([51.72840233779265162, 39.74494232180943953]) + actual = random.dirichlet(alpha) + assert_array_almost_equal(actual, desired[0, 0], decimal=15) + + def test_dirichlet_size(self): + # gh-3173 + p = np.array([51.72840233779265162, 39.74494232180943953]) + assert_equal(random.dirichlet(p, np.uint32(1)).shape, (1, 2)) + assert_equal(random.dirichlet(p, np.uint32(1)).shape, (1, 2)) + assert_equal(random.dirichlet(p, np.uint32(1)).shape, (1, 2)) + assert_equal(random.dirichlet(p, [2, 2]).shape, (2, 2, 2)) + assert_equal(random.dirichlet(p, (2, 2)).shape, (2, 2, 2)) + assert_equal(random.dirichlet(p, np.array((2, 2))).shape, (2, 2, 2)) + + assert_raises(TypeError, random.dirichlet, p, float(1)) + + def test_dirichlet_bad_alpha(self): + # gh-2089 + alpha = np.array([5.4e-01, -1.0e-16]) + assert_raises(ValueError, random.dirichlet, alpha) + + # gh-15876 + assert_raises(ValueError, random.dirichlet, [[5, 1]]) + assert_raises(ValueError, random.dirichlet, [[5], [1]]) + assert_raises(ValueError, random.dirichlet, [[[5], [1]], [[1], [5]]]) + assert_raises(ValueError, random.dirichlet, np.array([[5, 1], [1, 5]])) + + def test_dirichlet_alpha_non_contiguous(self): + a = np.array([51.72840233779265162, -1.0, 39.74494232180943953]) + alpha = a[::2] + random = Generator(MT19937(self.seed)) + non_contig = random.dirichlet(alpha, size=(3, 2)) + random = Generator(MT19937(self.seed)) + contig = random.dirichlet(np.ascontiguousarray(alpha), + size=(3, 2)) + assert_array_almost_equal(non_contig, contig) + + def test_dirichlet_small_alpha(self): + eps = 1.0e-9 # 1.0e-10 -> runtime x 10; 1e-11 -> runtime x 200, etc. + alpha = eps * np.array([1., 1.0e-3]) + random = Generator(MT19937(self.seed)) + actual = random.dirichlet(alpha, size=(3, 2)) + expected = np.array([ + [[1., 0.], + [1., 0.]], + [[1., 0.], + [1., 0.]], + [[1., 0.], + [1., 0.]] + ]) + assert_array_almost_equal(actual, expected, decimal=15) + + @pytest.mark.slow + def test_dirichlet_moderately_small_alpha(self): + # Use alpha.max() < 0.1 to trigger stick breaking code path + alpha = np.array([0.02, 0.04, 0.03]) + exact_mean = alpha / alpha.sum() + random = Generator(MT19937(self.seed)) + sample = random.dirichlet(alpha, size=20000000) + sample_mean = sample.mean(axis=0) + assert_allclose(sample_mean, exact_mean, rtol=1e-3) + + def test_exponential(self): + random = Generator(MT19937(self.seed)) + actual = random.exponential(1.1234, size=(3, 2)) + desired = np.array([[0.098845481066258, 1.560752510746964], + [0.075730916041636, 1.769098974710777], + [1.488602544592235, 2.49684815275751 ]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_exponential_0(self): + assert_equal(random.exponential(scale=0), 0) + assert_raises(ValueError, random.exponential, scale=-0.) + + def test_f(self): + random = Generator(MT19937(self.seed)) + actual = random.f(12, 77, size=(3, 2)) + desired = np.array([[0.461720027077085, 1.100441958872451], + [1.100337455217484, 0.91421736740018 ], + [0.500811891303113, 0.826802454552058]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_gamma(self): + random = Generator(MT19937(self.seed)) + actual = random.gamma(5, 3, size=(3, 2)) + desired = np.array([[ 5.03850858902096, 7.9228656732049 ], + [18.73983605132985, 19.57961681699238], + [18.17897755150825, 18.17653912505234]]) + assert_array_almost_equal(actual, desired, decimal=14) + + def test_gamma_0(self): + assert_equal(random.gamma(shape=0, scale=0), 0) + assert_raises(ValueError, random.gamma, shape=-0., scale=-0.) + + def test_geometric(self): + random = Generator(MT19937(self.seed)) + actual = random.geometric(.123456789, size=(3, 2)) + desired = np.array([[1, 11], + [1, 12], + [11, 17]]) + assert_array_equal(actual, desired) + + def test_geometric_exceptions(self): + assert_raises(ValueError, random.geometric, 1.1) + assert_raises(ValueError, random.geometric, [1.1] * 10) + assert_raises(ValueError, random.geometric, -0.1) + assert_raises(ValueError, random.geometric, [-0.1] * 10) + with np.errstate(invalid='ignore'): + assert_raises(ValueError, random.geometric, np.nan) + assert_raises(ValueError, random.geometric, [np.nan] * 10) + + def test_gumbel(self): + random = Generator(MT19937(self.seed)) + actual = random.gumbel(loc=.123456789, scale=2.0, size=(3, 2)) + desired = np.array([[ 4.688397515056245, -0.289514845417841], + [ 4.981176042584683, -0.633224272589149], + [-0.055915275687488, -0.333962478257953]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_gumbel_0(self): + assert_equal(random.gumbel(scale=0), 0) + assert_raises(ValueError, random.gumbel, scale=-0.) + + def test_hypergeometric(self): + random = Generator(MT19937(self.seed)) + actual = random.hypergeometric(10.1, 5.5, 14, size=(3, 2)) + desired = np.array([[ 9, 9], + [ 9, 9], + [10, 9]]) + assert_array_equal(actual, desired) + + # Test nbad = 0 + actual = random.hypergeometric(5, 0, 3, size=4) + desired = np.array([3, 3, 3, 3]) + assert_array_equal(actual, desired) + + actual = random.hypergeometric(15, 0, 12, size=4) + desired = np.array([12, 12, 12, 12]) + assert_array_equal(actual, desired) + + # Test ngood = 0 + actual = random.hypergeometric(0, 5, 3, size=4) + desired = np.array([0, 0, 0, 0]) + assert_array_equal(actual, desired) + + actual = random.hypergeometric(0, 15, 12, size=4) + desired = np.array([0, 0, 0, 0]) + assert_array_equal(actual, desired) + + def test_laplace(self): + random = Generator(MT19937(self.seed)) + actual = random.laplace(loc=.123456789, scale=2.0, size=(3, 2)) + desired = np.array([[-3.156353949272393, 1.195863024830054], + [-3.435458081645966, 1.656882398925444], + [ 0.924824032467446, 1.251116432209336]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_laplace_0(self): + assert_equal(random.laplace(scale=0), 0) + assert_raises(ValueError, random.laplace, scale=-0.) + + def test_logistic(self): + random = Generator(MT19937(self.seed)) + actual = random.logistic(loc=.123456789, scale=2.0, size=(3, 2)) + desired = np.array([[-4.338584631510999, 1.890171436749954], + [-4.64547787337966 , 2.514545562919217], + [ 1.495389489198666, 1.967827627577474]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_lognormal(self): + random = Generator(MT19937(self.seed)) + actual = random.lognormal(mean=.123456789, sigma=2.0, size=(3, 2)) + desired = np.array([[ 0.0268252166335, 13.9534486483053], + [ 0.1204014788936, 2.2422077497792], + [ 4.2484199496128, 12.0093343977523]]) + assert_array_almost_equal(actual, desired, decimal=13) + + def test_lognormal_0(self): + assert_equal(random.lognormal(sigma=0), 1) + assert_raises(ValueError, random.lognormal, sigma=-0.) + + def test_logseries(self): + random = Generator(MT19937(self.seed)) + actual = random.logseries(p=.923456789, size=(3, 2)) + desired = np.array([[14, 17], + [3, 18], + [5, 1]]) + assert_array_equal(actual, desired) + + def test_logseries_exceptions(self): + with np.errstate(invalid='ignore'): + assert_raises(ValueError, random.logseries, np.nan) + assert_raises(ValueError, random.logseries, [np.nan] * 10) + + def test_multinomial(self): + random = Generator(MT19937(self.seed)) + actual = random.multinomial(20, [1 / 6.] * 6, size=(3, 2)) + desired = np.array([[[1, 5, 1, 6, 4, 3], + [4, 2, 6, 2, 4, 2]], + [[5, 3, 2, 6, 3, 1], + [4, 4, 0, 2, 3, 7]], + [[6, 3, 1, 5, 3, 2], + [5, 5, 3, 1, 2, 4]]]) + assert_array_equal(actual, desired) + + @pytest.mark.parametrize("method", ["svd", "eigh", "cholesky"]) + def test_multivariate_normal(self, method): + random = Generator(MT19937(self.seed)) + mean = (.123456789, 10) + cov = [[1, 0], [0, 1]] + size = (3, 2) + actual = random.multivariate_normal(mean, cov, size, method=method) + desired = np.array([[[-1.747478062846581, 11.25613495182354 ], + [-0.9967333370066214, 10.342002097029821 ]], + [[ 0.7850019631242964, 11.181113712443013 ], + [ 0.8901349653255224, 8.873825399642492 ]], + [[ 0.7130260107430003, 9.551628690083056 ], + [ 0.7127098726541128, 11.991709234143173 ]]]) + + assert_array_almost_equal(actual, desired, decimal=15) + + # Check for default size, was raising deprecation warning + actual = random.multivariate_normal(mean, cov, method=method) + desired = np.array([0.233278563284287, 9.424140804347195]) + assert_array_almost_equal(actual, desired, decimal=15) + # Check that non symmetric covariance input raises exception when + # check_valid='raises' if using default svd method. + mean = [0, 0] + cov = [[1, 2], [1, 2]] + assert_raises(ValueError, random.multivariate_normal, mean, cov, + check_valid='raise') + + # Check that non positive-semidefinite covariance warns with + # RuntimeWarning + cov = [[1, 2], [2, 1]] + assert_warns(RuntimeWarning, random.multivariate_normal, mean, cov) + assert_warns(RuntimeWarning, random.multivariate_normal, mean, cov, + method='eigh') + assert_raises(LinAlgError, random.multivariate_normal, mean, cov, + method='cholesky') + + # and that it doesn't warn with RuntimeWarning check_valid='ignore' + assert_no_warnings(random.multivariate_normal, mean, cov, + check_valid='ignore') + + # and that it raises with RuntimeWarning check_valid='raises' + assert_raises(ValueError, random.multivariate_normal, mean, cov, + check_valid='raise') + assert_raises(ValueError, random.multivariate_normal, mean, cov, + check_valid='raise', method='eigh') + + # check degenerate samples from singular covariance matrix + cov = [[1, 1], [1, 1]] + if method in ('svd', 'eigh'): + samples = random.multivariate_normal(mean, cov, size=(3, 2), + method=method) + assert_array_almost_equal(samples[..., 0], samples[..., 1], + decimal=6) + else: + assert_raises(LinAlgError, random.multivariate_normal, mean, cov, + method='cholesky') + + cov = np.array([[1, 0.1], [0.1, 1]], dtype=np.float32) + with suppress_warnings() as sup: + random.multivariate_normal(mean, cov, method=method) + w = sup.record(RuntimeWarning) + assert len(w) == 0 + + mu = np.zeros(2) + cov = np.eye(2) + assert_raises(ValueError, random.multivariate_normal, mean, cov, + check_valid='other') + assert_raises(ValueError, random.multivariate_normal, + np.zeros((2, 1, 1)), cov) + assert_raises(ValueError, random.multivariate_normal, + mu, np.empty((3, 2))) + assert_raises(ValueError, random.multivariate_normal, + mu, np.eye(3)) + + @pytest.mark.parametrize("method", ["svd", "eigh", "cholesky"]) + def test_multivariate_normal_basic_stats(self, method): + random = Generator(MT19937(self.seed)) + n_s = 1000 + mean = np.array([1, 2]) + cov = np.array([[2, 1], [1, 2]]) + s = random.multivariate_normal(mean, cov, size=(n_s,), method=method) + s_center = s - mean + cov_emp = (s_center.T @ s_center) / (n_s - 1) + # these are pretty loose and are only designed to detect major errors + assert np.all(np.abs(s_center.mean(-2)) < 0.1) + assert np.all(np.abs(cov_emp - cov) < 0.2) + + def test_negative_binomial(self): + random = Generator(MT19937(self.seed)) + actual = random.negative_binomial(n=100, p=.12345, size=(3, 2)) + desired = np.array([[543, 727], + [775, 760], + [600, 674]]) + assert_array_equal(actual, desired) + + def test_negative_binomial_exceptions(self): + with np.errstate(invalid='ignore'): + assert_raises(ValueError, random.negative_binomial, 100, np.nan) + assert_raises(ValueError, random.negative_binomial, 100, + [np.nan] * 10) + + def test_negative_binomial_p0_exception(self): + # Verify that p=0 raises an exception. + with assert_raises(ValueError): + x = random.negative_binomial(1, 0) + + def test_noncentral_chisquare(self): + random = Generator(MT19937(self.seed)) + actual = random.noncentral_chisquare(df=5, nonc=5, size=(3, 2)) + desired = np.array([[ 1.70561552362133, 15.97378184942111], + [13.71483425173724, 20.17859633310629], + [11.3615477156643 , 3.67891108738029]]) + assert_array_almost_equal(actual, desired, decimal=14) + + actual = random.noncentral_chisquare(df=.5, nonc=.2, size=(3, 2)) + desired = np.array([[9.41427665607629e-04, 1.70473157518850e-04], + [1.14554372041263e+00, 1.38187755933435e-03], + [1.90659181905387e+00, 1.21772577941822e+00]]) + assert_array_almost_equal(actual, desired, decimal=14) + + random = Generator(MT19937(self.seed)) + actual = random.noncentral_chisquare(df=5, nonc=0, size=(3, 2)) + desired = np.array([[0.82947954590419, 1.80139670767078], + [6.58720057417794, 7.00491463609814], + [6.31101879073157, 6.30982307753005]]) + assert_array_almost_equal(actual, desired, decimal=14) + + def test_noncentral_f(self): + random = Generator(MT19937(self.seed)) + actual = random.noncentral_f(dfnum=5, dfden=2, nonc=1, + size=(3, 2)) + desired = np.array([[0.060310671139 , 0.23866058175939], + [0.86860246709073, 0.2668510459738 ], + [0.23375780078364, 1.88922102885943]]) + assert_array_almost_equal(actual, desired, decimal=14) + + def test_noncentral_f_nan(self): + random = Generator(MT19937(self.seed)) + actual = random.noncentral_f(dfnum=5, dfden=2, nonc=np.nan) + assert np.isnan(actual) + + def test_normal(self): + random = Generator(MT19937(self.seed)) + actual = random.normal(loc=.123456789, scale=2.0, size=(3, 2)) + desired = np.array([[-3.618412914693162, 2.635726692647081], + [-2.116923463013243, 0.807460983059643], + [ 1.446547137248593, 2.485684213886024]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_normal_0(self): + assert_equal(random.normal(scale=0), 0) + assert_raises(ValueError, random.normal, scale=-0.) + + def test_pareto(self): + random = Generator(MT19937(self.seed)) + actual = random.pareto(a=.123456789, size=(3, 2)) + desired = np.array([[1.0394926776069018e+00, 7.7142534343505773e+04], + [7.2640150889064703e-01, 3.4650454783825594e+05], + [4.5852344481994740e+04, 6.5851383009539105e+07]]) + # For some reason on 32-bit x86 Ubuntu 12.10 the [1, 0] entry in this + # matrix differs by 24 nulps. Discussion: + # https://mail.python.org/pipermail/numpy-discussion/2012-September/063801.html + # Consensus is that this is probably some gcc quirk that affects + # rounding but not in any important way, so we just use a looser + # tolerance on this test: + np.testing.assert_array_almost_equal_nulp(actual, desired, nulp=30) + + def test_poisson(self): + random = Generator(MT19937(self.seed)) + actual = random.poisson(lam=.123456789, size=(3, 2)) + desired = np.array([[0, 0], + [0, 0], + [0, 0]]) + assert_array_equal(actual, desired) + + def test_poisson_exceptions(self): + lambig = np.iinfo('int64').max + lamneg = -1 + assert_raises(ValueError, random.poisson, lamneg) + assert_raises(ValueError, random.poisson, [lamneg] * 10) + assert_raises(ValueError, random.poisson, lambig) + assert_raises(ValueError, random.poisson, [lambig] * 10) + with np.errstate(invalid='ignore'): + assert_raises(ValueError, random.poisson, np.nan) + assert_raises(ValueError, random.poisson, [np.nan] * 10) + + def test_power(self): + random = Generator(MT19937(self.seed)) + actual = random.power(a=.123456789, size=(3, 2)) + desired = np.array([[1.977857368842754e-09, 9.806792196620341e-02], + [2.482442984543471e-10, 1.527108843266079e-01], + [8.188283434244285e-02, 3.950547209346948e-01]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_rayleigh(self): + random = Generator(MT19937(self.seed)) + actual = random.rayleigh(scale=10, size=(3, 2)) + desired = np.array([[4.19494429102666, 16.66920198906598], + [3.67184544902662, 17.74695521962917], + [16.27935397855501, 21.08355560691792]]) + assert_array_almost_equal(actual, desired, decimal=14) + + def test_rayleigh_0(self): + assert_equal(random.rayleigh(scale=0), 0) + assert_raises(ValueError, random.rayleigh, scale=-0.) + + def test_standard_cauchy(self): + random = Generator(MT19937(self.seed)) + actual = random.standard_cauchy(size=(3, 2)) + desired = np.array([[-1.489437778266206, -3.275389641569784], + [ 0.560102864910406, -0.680780916282552], + [-1.314912905226277, 0.295852965660225]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_standard_exponential(self): + random = Generator(MT19937(self.seed)) + actual = random.standard_exponential(size=(3, 2), method='inv') + desired = np.array([[0.102031839440643, 1.229350298474972], + [0.088137284693098, 1.459859985522667], + [1.093830802293668, 1.256977002164613]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_standard_expoential_type_error(self): + assert_raises(TypeError, random.standard_exponential, dtype=np.int32) + + def test_standard_gamma(self): + random = Generator(MT19937(self.seed)) + actual = random.standard_gamma(shape=3, size=(3, 2)) + desired = np.array([[0.62970724056362, 1.22379851271008], + [3.899412530884 , 4.12479964250139], + [3.74994102464584, 3.74929307690815]]) + assert_array_almost_equal(actual, desired, decimal=14) + + def test_standard_gammma_scalar_float(self): + random = Generator(MT19937(self.seed)) + actual = random.standard_gamma(3, dtype=np.float32) + desired = 2.9242148399353027 + assert_array_almost_equal(actual, desired, decimal=6) + + def test_standard_gamma_float(self): + random = Generator(MT19937(self.seed)) + actual = random.standard_gamma(shape=3, size=(3, 2)) + desired = np.array([[0.62971, 1.2238 ], + [3.89941, 4.1248 ], + [3.74994, 3.74929]]) + assert_array_almost_equal(actual, desired, decimal=5) + + def test_standard_gammma_float_out(self): + actual = np.zeros((3, 2), dtype=np.float32) + random = Generator(MT19937(self.seed)) + random.standard_gamma(10.0, out=actual, dtype=np.float32) + desired = np.array([[10.14987, 7.87012], + [ 9.46284, 12.56832], + [13.82495, 7.81533]], dtype=np.float32) + assert_array_almost_equal(actual, desired, decimal=5) + + random = Generator(MT19937(self.seed)) + random.standard_gamma(10.0, out=actual, size=(3, 2), dtype=np.float32) + assert_array_almost_equal(actual, desired, decimal=5) + + def test_standard_gamma_unknown_type(self): + assert_raises(TypeError, random.standard_gamma, 1., + dtype='int32') + + def test_out_size_mismatch(self): + out = np.zeros(10) + assert_raises(ValueError, random.standard_gamma, 10.0, size=20, + out=out) + assert_raises(ValueError, random.standard_gamma, 10.0, size=(10, 1), + out=out) + + def test_standard_gamma_0(self): + assert_equal(random.standard_gamma(shape=0), 0) + assert_raises(ValueError, random.standard_gamma, shape=-0.) + + def test_standard_normal(self): + random = Generator(MT19937(self.seed)) + actual = random.standard_normal(size=(3, 2)) + desired = np.array([[-1.870934851846581, 1.25613495182354 ], + [-1.120190126006621, 0.342002097029821], + [ 0.661545174124296, 1.181113712443012]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_standard_normal_unsupported_type(self): + assert_raises(TypeError, random.standard_normal, dtype=np.int32) + + def test_standard_t(self): + random = Generator(MT19937(self.seed)) + actual = random.standard_t(df=10, size=(3, 2)) + desired = np.array([[-1.484666193042647, 0.30597891831161 ], + [ 1.056684299648085, -0.407312602088507], + [ 0.130704414281157, -2.038053410490321]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_triangular(self): + random = Generator(MT19937(self.seed)) + actual = random.triangular(left=5.12, mode=10.23, right=20.34, + size=(3, 2)) + desired = np.array([[ 7.86664070590917, 13.6313848513185 ], + [ 7.68152445215983, 14.36169131136546], + [13.16105603911429, 13.72341621856971]]) + assert_array_almost_equal(actual, desired, decimal=14) + + def test_uniform(self): + random = Generator(MT19937(self.seed)) + actual = random.uniform(low=1.23, high=10.54, size=(3, 2)) + desired = np.array([[2.13306255040998 , 7.816987531021207], + [2.015436610109887, 8.377577533009589], + [7.421792588856135, 7.891185744455209]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_uniform_range_bounds(self): + fmin = np.finfo('float').min + fmax = np.finfo('float').max + + func = random.uniform + assert_raises(OverflowError, func, -np.inf, 0) + assert_raises(OverflowError, func, 0, np.inf) + assert_raises(OverflowError, func, fmin, fmax) + assert_raises(OverflowError, func, [-np.inf], [0]) + assert_raises(OverflowError, func, [0], [np.inf]) + + # (fmax / 1e17) - fmin is within range, so this should not throw + # account for i386 extended precision DBL_MAX / 1e17 + DBL_MAX > + # DBL_MAX by increasing fmin a bit + random.uniform(low=np.nextafter(fmin, 1), high=fmax / 1e17) + + def test_uniform_zero_range(self): + func = random.uniform + result = func(1.5, 1.5) + assert_allclose(result, 1.5) + result = func([0.0, np.pi], [0.0, np.pi]) + assert_allclose(result, [0.0, np.pi]) + result = func([[2145.12], [2145.12]], [2145.12, 2145.12]) + assert_allclose(result, 2145.12 + np.zeros((2, 2))) + + def test_uniform_neg_range(self): + func = random.uniform + assert_raises(ValueError, func, 2, 1) + assert_raises(ValueError, func, [1, 2], [1, 1]) + assert_raises(ValueError, func, [[0, 1],[2, 3]], 2) + + def test_scalar_exception_propagation(self): + # Tests that exceptions are correctly propagated in distributions + # when called with objects that throw exceptions when converted to + # scalars. + # + # Regression test for gh: 8865 + + class ThrowingFloat(np.ndarray): + def __float__(self): + raise TypeError + + throwing_float = np.array(1.0).view(ThrowingFloat) + assert_raises(TypeError, random.uniform, throwing_float, + throwing_float) + + class ThrowingInteger(np.ndarray): + def __int__(self): + raise TypeError + + throwing_int = np.array(1).view(ThrowingInteger) + assert_raises(TypeError, random.hypergeometric, throwing_int, 1, 1) + + def test_vonmises(self): + random = Generator(MT19937(self.seed)) + actual = random.vonmises(mu=1.23, kappa=1.54, size=(3, 2)) + desired = np.array([[ 1.107972248690106, 2.841536476232361], + [ 1.832602376042457, 1.945511926976032], + [-0.260147475776542, 2.058047492231698]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_vonmises_small(self): + # check infinite loop, gh-4720 + random = Generator(MT19937(self.seed)) + r = random.vonmises(mu=0., kappa=1.1e-8, size=10**6) + assert_(np.isfinite(r).all()) + + def test_vonmises_nan(self): + random = Generator(MT19937(self.seed)) + r = random.vonmises(mu=0., kappa=np.nan) + assert_(np.isnan(r)) + + @pytest.mark.parametrize("kappa", [1e4, 1e15]) + def test_vonmises_large_kappa(self, kappa): + random = Generator(MT19937(self.seed)) + rs = RandomState(random.bit_generator) + state = random.bit_generator.state + + random_state_vals = rs.vonmises(0, kappa, size=10) + random.bit_generator.state = state + gen_vals = random.vonmises(0, kappa, size=10) + if kappa < 1e6: + assert_allclose(random_state_vals, gen_vals) + else: + assert np.all(random_state_vals != gen_vals) + + @pytest.mark.parametrize("mu", [-7., -np.pi, -3.1, np.pi, 3.2]) + @pytest.mark.parametrize("kappa", [1e-9, 1e-6, 1, 1e3, 1e15]) + def test_vonmises_large_kappa_range(self, mu, kappa): + random = Generator(MT19937(self.seed)) + r = random.vonmises(mu, kappa, 50) + assert_(np.all(r > -np.pi) and np.all(r <= np.pi)) + + def test_wald(self): + random = Generator(MT19937(self.seed)) + actual = random.wald(mean=1.23, scale=1.54, size=(3, 2)) + desired = np.array([[0.26871721804551, 3.2233942732115 ], + [2.20328374987066, 2.40958405189353], + [2.07093587449261, 0.73073890064369]]) + assert_array_almost_equal(actual, desired, decimal=14) + + def test_weibull(self): + random = Generator(MT19937(self.seed)) + actual = random.weibull(a=1.23, size=(3, 2)) + desired = np.array([[0.138613914769468, 1.306463419753191], + [0.111623365934763, 1.446570494646721], + [1.257145775276011, 1.914247725027957]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_weibull_0(self): + random = Generator(MT19937(self.seed)) + assert_equal(random.weibull(a=0, size=12), np.zeros(12)) + assert_raises(ValueError, random.weibull, a=-0.) + + def test_zipf(self): + random = Generator(MT19937(self.seed)) + actual = random.zipf(a=1.23, size=(3, 2)) + desired = np.array([[ 1, 1], + [ 10, 867], + [354, 2]]) + assert_array_equal(actual, desired) + + +class TestBroadcast: + # tests that functions that broadcast behave + # correctly when presented with non-scalar arguments + def setup(self): + self.seed = 123456789 + + + def test_uniform(self): + random = Generator(MT19937(self.seed)) + low = [0] + high = [1] + uniform = random.uniform + desired = np.array([0.16693771389729, 0.19635129550675, 0.75563050964095]) + + random = Generator(MT19937(self.seed)) + actual = random.uniform(low * 3, high) + assert_array_almost_equal(actual, desired, decimal=14) + + random = Generator(MT19937(self.seed)) + actual = random.uniform(low, high * 3) + assert_array_almost_equal(actual, desired, decimal=14) + + def test_normal(self): + loc = [0] + scale = [1] + bad_scale = [-1] + random = Generator(MT19937(self.seed)) + desired = np.array([-0.38736406738527, 0.79594375042255, 0.0197076236097]) + + random = Generator(MT19937(self.seed)) + actual = random.normal(loc * 3, scale) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, random.normal, loc * 3, bad_scale) + + random = Generator(MT19937(self.seed)) + normal = random.normal + actual = normal(loc, scale * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, normal, loc, bad_scale * 3) + + def test_beta(self): + a = [1] + b = [2] + bad_a = [-1] + bad_b = [-2] + desired = np.array([0.18719338682602, 0.73234824491364, 0.17928615186455]) + + random = Generator(MT19937(self.seed)) + beta = random.beta + actual = beta(a * 3, b) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, beta, bad_a * 3, b) + assert_raises(ValueError, beta, a * 3, bad_b) + + random = Generator(MT19937(self.seed)) + actual = random.beta(a, b * 3) + assert_array_almost_equal(actual, desired, decimal=14) + + def test_exponential(self): + scale = [1] + bad_scale = [-1] + desired = np.array([0.67245993212806, 0.21380495318094, 0.7177848928629]) + + random = Generator(MT19937(self.seed)) + actual = random.exponential(scale * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, random.exponential, bad_scale * 3) + + def test_standard_gamma(self): + shape = [1] + bad_shape = [-1] + desired = np.array([0.67245993212806, 0.21380495318094, 0.7177848928629]) + + random = Generator(MT19937(self.seed)) + std_gamma = random.standard_gamma + actual = std_gamma(shape * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, std_gamma, bad_shape * 3) + + def test_gamma(self): + shape = [1] + scale = [2] + bad_shape = [-1] + bad_scale = [-2] + desired = np.array([1.34491986425611, 0.42760990636187, 1.4355697857258]) + + random = Generator(MT19937(self.seed)) + gamma = random.gamma + actual = gamma(shape * 3, scale) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, gamma, bad_shape * 3, scale) + assert_raises(ValueError, gamma, shape * 3, bad_scale) + + random = Generator(MT19937(self.seed)) + gamma = random.gamma + actual = gamma(shape, scale * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, gamma, bad_shape, scale * 3) + assert_raises(ValueError, gamma, shape, bad_scale * 3) + + def test_f(self): + dfnum = [1] + dfden = [2] + bad_dfnum = [-1] + bad_dfden = [-2] + desired = np.array([0.07765056244107, 7.72951397913186, 0.05786093891763]) + + random = Generator(MT19937(self.seed)) + f = random.f + actual = f(dfnum * 3, dfden) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, f, bad_dfnum * 3, dfden) + assert_raises(ValueError, f, dfnum * 3, bad_dfden) + + random = Generator(MT19937(self.seed)) + f = random.f + actual = f(dfnum, dfden * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, f, bad_dfnum, dfden * 3) + assert_raises(ValueError, f, dfnum, bad_dfden * 3) + + def test_noncentral_f(self): + dfnum = [2] + dfden = [3] + nonc = [4] + bad_dfnum = [0] + bad_dfden = [-1] + bad_nonc = [-2] + desired = np.array([2.02434240411421, 12.91838601070124, 1.24395160354629]) + + random = Generator(MT19937(self.seed)) + nonc_f = random.noncentral_f + actual = nonc_f(dfnum * 3, dfden, nonc) + assert_array_almost_equal(actual, desired, decimal=14) + assert np.all(np.isnan(nonc_f(dfnum, dfden, [np.nan] * 3))) + + assert_raises(ValueError, nonc_f, bad_dfnum * 3, dfden, nonc) + assert_raises(ValueError, nonc_f, dfnum * 3, bad_dfden, nonc) + assert_raises(ValueError, nonc_f, dfnum * 3, dfden, bad_nonc) + + random = Generator(MT19937(self.seed)) + nonc_f = random.noncentral_f + actual = nonc_f(dfnum, dfden * 3, nonc) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, nonc_f, bad_dfnum, dfden * 3, nonc) + assert_raises(ValueError, nonc_f, dfnum, bad_dfden * 3, nonc) + assert_raises(ValueError, nonc_f, dfnum, dfden * 3, bad_nonc) + + random = Generator(MT19937(self.seed)) + nonc_f = random.noncentral_f + actual = nonc_f(dfnum, dfden, nonc * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, nonc_f, bad_dfnum, dfden, nonc * 3) + assert_raises(ValueError, nonc_f, dfnum, bad_dfden, nonc * 3) + assert_raises(ValueError, nonc_f, dfnum, dfden, bad_nonc * 3) + + def test_noncentral_f_small_df(self): + random = Generator(MT19937(self.seed)) + desired = np.array([0.04714867120827, 0.1239390327694]) + actual = random.noncentral_f(0.9, 0.9, 2, size=2) + assert_array_almost_equal(actual, desired, decimal=14) + + def test_chisquare(self): + df = [1] + bad_df = [-1] + desired = np.array([0.05573640064251, 1.47220224353539, 2.9469379318589]) + + random = Generator(MT19937(self.seed)) + actual = random.chisquare(df * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, random.chisquare, bad_df * 3) + + def test_noncentral_chisquare(self): + df = [1] + nonc = [2] + bad_df = [-1] + bad_nonc = [-2] + desired = np.array([0.07710766249436, 5.27829115110304, 0.630732147399]) + + random = Generator(MT19937(self.seed)) + nonc_chi = random.noncentral_chisquare + actual = nonc_chi(df * 3, nonc) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, nonc_chi, bad_df * 3, nonc) + assert_raises(ValueError, nonc_chi, df * 3, bad_nonc) + + random = Generator(MT19937(self.seed)) + nonc_chi = random.noncentral_chisquare + actual = nonc_chi(df, nonc * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, nonc_chi, bad_df, nonc * 3) + assert_raises(ValueError, nonc_chi, df, bad_nonc * 3) + + def test_standard_t(self): + df = [1] + bad_df = [-1] + desired = np.array([-1.39498829447098, -1.23058658835223, 0.17207021065983]) + + random = Generator(MT19937(self.seed)) + actual = random.standard_t(df * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, random.standard_t, bad_df * 3) + + def test_vonmises(self): + mu = [2] + kappa = [1] + bad_kappa = [-1] + desired = np.array([2.25935584988528, 2.23326261461399, -2.84152146503326]) + + random = Generator(MT19937(self.seed)) + actual = random.vonmises(mu * 3, kappa) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, random.vonmises, mu * 3, bad_kappa) + + random = Generator(MT19937(self.seed)) + actual = random.vonmises(mu, kappa * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, random.vonmises, mu, bad_kappa * 3) + + def test_pareto(self): + a = [1] + bad_a = [-1] + desired = np.array([0.95905052946317, 0.2383810889437 , 1.04988745750013]) + + random = Generator(MT19937(self.seed)) + actual = random.pareto(a * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, random.pareto, bad_a * 3) + + def test_weibull(self): + a = [1] + bad_a = [-1] + desired = np.array([0.67245993212806, 0.21380495318094, 0.7177848928629]) + + random = Generator(MT19937(self.seed)) + actual = random.weibull(a * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, random.weibull, bad_a * 3) + + def test_power(self): + a = [1] + bad_a = [-1] + desired = np.array([0.48954864361052, 0.19249412888486, 0.51216834058807]) + + random = Generator(MT19937(self.seed)) + actual = random.power(a * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, random.power, bad_a * 3) + + def test_laplace(self): + loc = [0] + scale = [1] + bad_scale = [-1] + desired = np.array([-1.09698732625119, -0.93470271947368, 0.71592671378202]) + + random = Generator(MT19937(self.seed)) + laplace = random.laplace + actual = laplace(loc * 3, scale) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, laplace, loc * 3, bad_scale) + + random = Generator(MT19937(self.seed)) + laplace = random.laplace + actual = laplace(loc, scale * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, laplace, loc, bad_scale * 3) + + def test_gumbel(self): + loc = [0] + scale = [1] + bad_scale = [-1] + desired = np.array([1.70020068231762, 1.52054354273631, -0.34293267607081]) + + random = Generator(MT19937(self.seed)) + gumbel = random.gumbel + actual = gumbel(loc * 3, scale) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, gumbel, loc * 3, bad_scale) + + random = Generator(MT19937(self.seed)) + gumbel = random.gumbel + actual = gumbel(loc, scale * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, gumbel, loc, bad_scale * 3) + + def test_logistic(self): + loc = [0] + scale = [1] + bad_scale = [-1] + desired = np.array([-1.607487640433, -1.40925686003678, 1.12887112820397]) + + random = Generator(MT19937(self.seed)) + actual = random.logistic(loc * 3, scale) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, random.logistic, loc * 3, bad_scale) + + random = Generator(MT19937(self.seed)) + actual = random.logistic(loc, scale * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, random.logistic, loc, bad_scale * 3) + assert_equal(random.logistic(1.0, 0.0), 1.0) + + def test_lognormal(self): + mean = [0] + sigma = [1] + bad_sigma = [-1] + desired = np.array([0.67884390500697, 2.21653186290321, 1.01990310084276]) + + random = Generator(MT19937(self.seed)) + lognormal = random.lognormal + actual = lognormal(mean * 3, sigma) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, lognormal, mean * 3, bad_sigma) + + random = Generator(MT19937(self.seed)) + actual = random.lognormal(mean, sigma * 3) + assert_raises(ValueError, random.lognormal, mean, bad_sigma * 3) + + def test_rayleigh(self): + scale = [1] + bad_scale = [-1] + desired = np.array( + [1.1597068009872629, + 0.6539188836253857, + 1.1981526554349398] + ) + + random = Generator(MT19937(self.seed)) + actual = random.rayleigh(scale * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, random.rayleigh, bad_scale * 3) + + def test_wald(self): + mean = [0.5] + scale = [1] + bad_mean = [0] + bad_scale = [-2] + desired = np.array([0.38052407392905, 0.50701641508592, 0.484935249864]) + + random = Generator(MT19937(self.seed)) + actual = random.wald(mean * 3, scale) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, random.wald, bad_mean * 3, scale) + assert_raises(ValueError, random.wald, mean * 3, bad_scale) + + random = Generator(MT19937(self.seed)) + actual = random.wald(mean, scale * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, random.wald, bad_mean, scale * 3) + assert_raises(ValueError, random.wald, mean, bad_scale * 3) + + def test_triangular(self): + left = [1] + right = [3] + mode = [2] + bad_left_one = [3] + bad_mode_one = [4] + bad_left_two, bad_mode_two = right * 2 + desired = np.array([1.57781954604754, 1.62665986867957, 2.30090130831326]) + + random = Generator(MT19937(self.seed)) + triangular = random.triangular + actual = triangular(left * 3, mode, right) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, triangular, bad_left_one * 3, mode, right) + assert_raises(ValueError, triangular, left * 3, bad_mode_one, right) + assert_raises(ValueError, triangular, bad_left_two * 3, bad_mode_two, + right) + + random = Generator(MT19937(self.seed)) + triangular = random.triangular + actual = triangular(left, mode * 3, right) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, triangular, bad_left_one, mode * 3, right) + assert_raises(ValueError, triangular, left, bad_mode_one * 3, right) + assert_raises(ValueError, triangular, bad_left_two, bad_mode_two * 3, + right) + + random = Generator(MT19937(self.seed)) + triangular = random.triangular + actual = triangular(left, mode, right * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, triangular, bad_left_one, mode, right * 3) + assert_raises(ValueError, triangular, left, bad_mode_one, right * 3) + assert_raises(ValueError, triangular, bad_left_two, bad_mode_two, + right * 3) + + assert_raises(ValueError, triangular, 10., 0., 20.) + assert_raises(ValueError, triangular, 10., 25., 20.) + assert_raises(ValueError, triangular, 10., 10., 10.) + + def test_binomial(self): + n = [1] + p = [0.5] + bad_n = [-1] + bad_p_one = [-1] + bad_p_two = [1.5] + desired = np.array([0, 0, 1]) + + random = Generator(MT19937(self.seed)) + binom = random.binomial + actual = binom(n * 3, p) + assert_array_equal(actual, desired) + assert_raises(ValueError, binom, bad_n * 3, p) + assert_raises(ValueError, binom, n * 3, bad_p_one) + assert_raises(ValueError, binom, n * 3, bad_p_two) + + random = Generator(MT19937(self.seed)) + actual = random.binomial(n, p * 3) + assert_array_equal(actual, desired) + assert_raises(ValueError, binom, bad_n, p * 3) + assert_raises(ValueError, binom, n, bad_p_one * 3) + assert_raises(ValueError, binom, n, bad_p_two * 3) + + def test_negative_binomial(self): + n = [1] + p = [0.5] + bad_n = [-1] + bad_p_one = [-1] + bad_p_two = [1.5] + desired = np.array([0, 2, 1], dtype=np.int64) + + random = Generator(MT19937(self.seed)) + neg_binom = random.negative_binomial + actual = neg_binom(n * 3, p) + assert_array_equal(actual, desired) + assert_raises(ValueError, neg_binom, bad_n * 3, p) + assert_raises(ValueError, neg_binom, n * 3, bad_p_one) + assert_raises(ValueError, neg_binom, n * 3, bad_p_two) + + random = Generator(MT19937(self.seed)) + neg_binom = random.negative_binomial + actual = neg_binom(n, p * 3) + assert_array_equal(actual, desired) + assert_raises(ValueError, neg_binom, bad_n, p * 3) + assert_raises(ValueError, neg_binom, n, bad_p_one * 3) + assert_raises(ValueError, neg_binom, n, bad_p_two * 3) + + def test_poisson(self): + + lam = [1] + bad_lam_one = [-1] + desired = np.array([0, 0, 3]) + + random = Generator(MT19937(self.seed)) + max_lam = random._poisson_lam_max + bad_lam_two = [max_lam * 2] + poisson = random.poisson + actual = poisson(lam * 3) + assert_array_equal(actual, desired) + assert_raises(ValueError, poisson, bad_lam_one * 3) + assert_raises(ValueError, poisson, bad_lam_two * 3) + + def test_zipf(self): + a = [2] + bad_a = [0] + desired = np.array([1, 8, 1]) + + random = Generator(MT19937(self.seed)) + zipf = random.zipf + actual = zipf(a * 3) + assert_array_equal(actual, desired) + assert_raises(ValueError, zipf, bad_a * 3) + with np.errstate(invalid='ignore'): + assert_raises(ValueError, zipf, np.nan) + assert_raises(ValueError, zipf, [0, 0, np.nan]) + + def test_geometric(self): + p = [0.5] + bad_p_one = [-1] + bad_p_two = [1.5] + desired = np.array([1, 1, 3]) + + random = Generator(MT19937(self.seed)) + geometric = random.geometric + actual = geometric(p * 3) + assert_array_equal(actual, desired) + assert_raises(ValueError, geometric, bad_p_one * 3) + assert_raises(ValueError, geometric, bad_p_two * 3) + + def test_hypergeometric(self): + ngood = [1] + nbad = [2] + nsample = [2] + bad_ngood = [-1] + bad_nbad = [-2] + bad_nsample_one = [-1] + bad_nsample_two = [4] + desired = np.array([0, 0, 1]) + + random = Generator(MT19937(self.seed)) + actual = random.hypergeometric(ngood * 3, nbad, nsample) + assert_array_equal(actual, desired) + assert_raises(ValueError, random.hypergeometric, bad_ngood * 3, nbad, nsample) + assert_raises(ValueError, random.hypergeometric, ngood * 3, bad_nbad, nsample) + assert_raises(ValueError, random.hypergeometric, ngood * 3, nbad, bad_nsample_one) + assert_raises(ValueError, random.hypergeometric, ngood * 3, nbad, bad_nsample_two) + + random = Generator(MT19937(self.seed)) + actual = random.hypergeometric(ngood, nbad * 3, nsample) + assert_array_equal(actual, desired) + assert_raises(ValueError, random.hypergeometric, bad_ngood, nbad * 3, nsample) + assert_raises(ValueError, random.hypergeometric, ngood, bad_nbad * 3, nsample) + assert_raises(ValueError, random.hypergeometric, ngood, nbad * 3, bad_nsample_one) + assert_raises(ValueError, random.hypergeometric, ngood, nbad * 3, bad_nsample_two) + + random = Generator(MT19937(self.seed)) + hypergeom = random.hypergeometric + actual = hypergeom(ngood, nbad, nsample * 3) + assert_array_equal(actual, desired) + assert_raises(ValueError, hypergeom, bad_ngood, nbad, nsample * 3) + assert_raises(ValueError, hypergeom, ngood, bad_nbad, nsample * 3) + assert_raises(ValueError, hypergeom, ngood, nbad, bad_nsample_one * 3) + assert_raises(ValueError, hypergeom, ngood, nbad, bad_nsample_two * 3) + + assert_raises(ValueError, hypergeom, -1, 10, 20) + assert_raises(ValueError, hypergeom, 10, -1, 20) + assert_raises(ValueError, hypergeom, 10, 10, -1) + assert_raises(ValueError, hypergeom, 10, 10, 25) + + # ValueError for arguments that are too big. + assert_raises(ValueError, hypergeom, 2**30, 10, 20) + assert_raises(ValueError, hypergeom, 999, 2**31, 50) + assert_raises(ValueError, hypergeom, 999, [2**29, 2**30], 1000) + + def test_logseries(self): + p = [0.5] + bad_p_one = [2] + bad_p_two = [-1] + desired = np.array([1, 1, 1]) + + random = Generator(MT19937(self.seed)) + logseries = random.logseries + actual = logseries(p * 3) + assert_array_equal(actual, desired) + assert_raises(ValueError, logseries, bad_p_one * 3) + assert_raises(ValueError, logseries, bad_p_two * 3) + + def test_multinomial(self): + random = Generator(MT19937(self.seed)) + actual = random.multinomial([5, 20], [1 / 6.] * 6, size=(3, 2)) + desired = np.array([[[0, 0, 2, 1, 2, 0], + [2, 3, 6, 4, 2, 3]], + [[1, 0, 1, 0, 2, 1], + [7, 2, 2, 1, 4, 4]], + [[0, 2, 0, 1, 2, 0], + [3, 2, 3, 3, 4, 5]]], dtype=np.int64) + assert_array_equal(actual, desired) + + random = Generator(MT19937(self.seed)) + actual = random.multinomial([5, 20], [1 / 6.] * 6) + desired = np.array([[0, 0, 2, 1, 2, 0], + [2, 3, 6, 4, 2, 3]], dtype=np.int64) + assert_array_equal(actual, desired) + + random = Generator(MT19937(self.seed)) + actual = random.multinomial([5, 20], [[1 / 6.] * 6] * 2) + desired = np.array([[0, 0, 2, 1, 2, 0], + [2, 3, 6, 4, 2, 3]], dtype=np.int64) + assert_array_equal(actual, desired) + + random = Generator(MT19937(self.seed)) + actual = random.multinomial([[5], [20]], [[1 / 6.] * 6] * 2) + desired = np.array([[[0, 0, 2, 1, 2, 0], + [0, 0, 2, 1, 1, 1]], + [[4, 2, 3, 3, 5, 3], + [7, 2, 2, 1, 4, 4]]], dtype=np.int64) + assert_array_equal(actual, desired) + + @pytest.mark.parametrize("n", [10, + np.array([10, 10]), + np.array([[[10]], [[10]]]) + ] + ) + def test_multinomial_pval_broadcast(self, n): + random = Generator(MT19937(self.seed)) + pvals = np.array([1 / 4] * 4) + actual = random.multinomial(n, pvals) + n_shape = tuple() if isinstance(n, int) else n.shape + expected_shape = n_shape + (4,) + assert actual.shape == expected_shape + pvals = np.vstack([pvals, pvals]) + actual = random.multinomial(n, pvals) + expected_shape = np.broadcast_shapes(n_shape, pvals.shape[:-1]) + (4,) + assert actual.shape == expected_shape + + pvals = np.vstack([[pvals], [pvals]]) + actual = random.multinomial(n, pvals) + expected_shape = np.broadcast_shapes(n_shape, pvals.shape[:-1]) + assert actual.shape == expected_shape + (4,) + actual = random.multinomial(n, pvals, size=(3, 2) + expected_shape) + assert actual.shape == (3, 2) + expected_shape + (4,) + + with pytest.raises(ValueError): + # Ensure that size is not broadcast + actual = random.multinomial(n, pvals, size=(1,) * 6) + + def test_invalid_pvals_broadcast(self): + random = Generator(MT19937(self.seed)) + pvals = [[1 / 6] * 6, [1 / 4] * 6] + assert_raises(ValueError, random.multinomial, 1, pvals) + assert_raises(ValueError, random.multinomial, 6, 0.5) + + def test_empty_outputs(self): + random = Generator(MT19937(self.seed)) + actual = random.multinomial(np.empty((10, 0, 6), "i8"), [1 / 6] * 6) + assert actual.shape == (10, 0, 6, 6) + actual = random.multinomial(12, np.empty((10, 0, 10))) + assert actual.shape == (10, 0, 10) + actual = random.multinomial(np.empty((3, 0, 7), "i8"), + np.empty((3, 0, 7, 4))) + assert actual.shape == (3, 0, 7, 4) + + +class TestThread: + # make sure each state produces the same sequence even in threads + def setup(self): + self.seeds = range(4) + + def check_function(self, function, sz): + from threading import Thread + + out1 = np.empty((len(self.seeds),) + sz) + out2 = np.empty((len(self.seeds),) + sz) + + # threaded generation + t = [Thread(target=function, args=(Generator(MT19937(s)), o)) + for s, o in zip(self.seeds, out1)] + [x.start() for x in t] + [x.join() for x in t] + + # the same serial + for s, o in zip(self.seeds, out2): + function(Generator(MT19937(s)), o) + + # these platforms change x87 fpu precision mode in threads + if np.intp().dtype.itemsize == 4 and sys.platform == "win32": + assert_array_almost_equal(out1, out2) + else: + assert_array_equal(out1, out2) + + def test_normal(self): + def gen_random(state, out): + out[...] = state.normal(size=10000) + + self.check_function(gen_random, sz=(10000,)) + + def test_exp(self): + def gen_random(state, out): + out[...] = state.exponential(scale=np.ones((100, 1000))) + + self.check_function(gen_random, sz=(100, 1000)) + + def test_multinomial(self): + def gen_random(state, out): + out[...] = state.multinomial(10, [1 / 6.] * 6, size=10000) + + self.check_function(gen_random, sz=(10000, 6)) + + +# See Issue #4263 +class TestSingleEltArrayInput: + def setup(self): + self.argOne = np.array([2]) + self.argTwo = np.array([3]) + self.argThree = np.array([4]) + self.tgtShape = (1,) + + def test_one_arg_funcs(self): + funcs = (random.exponential, random.standard_gamma, + random.chisquare, random.standard_t, + random.pareto, random.weibull, + random.power, random.rayleigh, + random.poisson, random.zipf, + random.geometric, random.logseries) + + probfuncs = (random.geometric, random.logseries) + + for func in funcs: + if func in probfuncs: # p < 1.0 + out = func(np.array([0.5])) + + else: + out = func(self.argOne) + + assert_equal(out.shape, self.tgtShape) + + def test_two_arg_funcs(self): + funcs = (random.uniform, random.normal, + random.beta, random.gamma, + random.f, random.noncentral_chisquare, + random.vonmises, random.laplace, + random.gumbel, random.logistic, + random.lognormal, random.wald, + random.binomial, random.negative_binomial) + + probfuncs = (random.binomial, random.negative_binomial) + + for func in funcs: + if func in probfuncs: # p <= 1 + argTwo = np.array([0.5]) + + else: + argTwo = self.argTwo + + out = func(self.argOne, argTwo) + assert_equal(out.shape, self.tgtShape) + + out = func(self.argOne[0], argTwo) + assert_equal(out.shape, self.tgtShape) + + out = func(self.argOne, argTwo[0]) + assert_equal(out.shape, self.tgtShape) + + def test_integers(self, endpoint): + itype = [np.bool_, np.int8, np.uint8, np.int16, np.uint16, + np.int32, np.uint32, np.int64, np.uint64] + func = random.integers + high = np.array([1]) + low = np.array([0]) + + for dt in itype: + out = func(low, high, endpoint=endpoint, dtype=dt) + assert_equal(out.shape, self.tgtShape) + + out = func(low[0], high, endpoint=endpoint, dtype=dt) + assert_equal(out.shape, self.tgtShape) + + out = func(low, high[0], endpoint=endpoint, dtype=dt) + assert_equal(out.shape, self.tgtShape) + + def test_three_arg_funcs(self): + funcs = [random.noncentral_f, random.triangular, + random.hypergeometric] + + for func in funcs: + out = func(self.argOne, self.argTwo, self.argThree) + assert_equal(out.shape, self.tgtShape) + + out = func(self.argOne[0], self.argTwo, self.argThree) + assert_equal(out.shape, self.tgtShape) + + out = func(self.argOne, self.argTwo[0], self.argThree) + assert_equal(out.shape, self.tgtShape) + + +@pytest.mark.parametrize("config", JUMP_TEST_DATA) +def test_jumped(config): + # Each config contains the initial seed, a number of raw steps + # the sha256 hashes of the initial and the final states' keys and + # the position of of the initial and the final state. + # These were produced using the original C implementation. + seed = config["seed"] + steps = config["steps"] + + mt19937 = MT19937(seed) + # Burn step + mt19937.random_raw(steps) + key = mt19937.state["state"]["key"] + if sys.byteorder == 'big': + key = key.byteswap() + sha256 = hashlib.sha256(key) + assert mt19937.state["state"]["pos"] == config["initial"]["pos"] + assert sha256.hexdigest() == config["initial"]["key_sha256"] + + jumped = mt19937.jumped() + key = jumped.state["state"]["key"] + if sys.byteorder == 'big': + key = key.byteswap() + sha256 = hashlib.sha256(key) + assert jumped.state["state"]["pos"] == config["jumped"]["pos"] + assert sha256.hexdigest() == config["jumped"]["key_sha256"] + + +def test_broadcast_size_error(): + mu = np.ones(3) + sigma = np.ones((4, 3)) + size = (10, 4, 2) + assert random.normal(mu, sigma, size=(5, 4, 3)).shape == (5, 4, 3) + with pytest.raises(ValueError): + random.normal(mu, sigma, size=size) + with pytest.raises(ValueError): + random.normal(mu, sigma, size=(1, 3)) + with pytest.raises(ValueError): + random.normal(mu, sigma, size=(4, 1, 1)) + # 1 arg + shape = np.ones((4, 3)) + with pytest.raises(ValueError): + random.standard_gamma(shape, size=size) + with pytest.raises(ValueError): + random.standard_gamma(shape, size=(3,)) + with pytest.raises(ValueError): + random.standard_gamma(shape, size=3) + # Check out + out = np.empty(size) + with pytest.raises(ValueError): + random.standard_gamma(shape, out=out) + + # 2 arg + with pytest.raises(ValueError): + random.binomial(1, [0.3, 0.7], size=(2, 1)) + with pytest.raises(ValueError): + random.binomial([1, 2], 0.3, size=(2, 1)) + with pytest.raises(ValueError): + random.binomial([1, 2], [0.3, 0.7], size=(2, 1)) + with pytest.raises(ValueError): + random.multinomial([2, 2], [.3, .7], size=(2, 1)) + + # 3 arg + a = random.chisquare(5, size=3) + b = random.chisquare(5, size=(4, 3)) + c = random.chisquare(5, size=(5, 4, 3)) + assert random.noncentral_f(a, b, c).shape == (5, 4, 3) + with pytest.raises(ValueError, match=r"Output size \(6, 5, 1, 1\) is"): + random.noncentral_f(a, b, c, size=(6, 5, 1, 1)) + + +def test_broadcast_size_scalar(): + mu = np.ones(3) + sigma = np.ones(3) + random.normal(mu, sigma, size=3) + with pytest.raises(ValueError): + random.normal(mu, sigma, size=2) + + +def test_ragged_shuffle(): + # GH 18142 + seq = [[], [], 1] + gen = Generator(MT19937(0)) + assert_no_warnings(gen.shuffle, seq) + assert seq == [1, [], []] + + +@pytest.mark.parametrize("high", [-2, [-2]]) +@pytest.mark.parametrize("endpoint", [True, False]) +def test_single_arg_integer_exception(high, endpoint): + # GH 14333 + gen = Generator(MT19937(0)) + msg = 'high < 0' if endpoint else 'high <= 0' + with pytest.raises(ValueError, match=msg): + gen.integers(high, endpoint=endpoint) + msg = 'low > high' if endpoint else 'low >= high' + with pytest.raises(ValueError, match=msg): + gen.integers(-1, high, endpoint=endpoint) + with pytest.raises(ValueError, match=msg): + gen.integers([-1], high, endpoint=endpoint) + + +@pytest.mark.parametrize("dtype", ["f4", "f8"]) +def test_c_contig_req_out(dtype): + # GH 18704 + out = np.empty((2, 3), order="F", dtype=dtype) + shape = [1, 2, 3] + with pytest.raises(ValueError, match="Supplied output array"): + random.standard_gamma(shape, out=out, dtype=dtype) + with pytest.raises(ValueError, match="Supplied output array"): + random.standard_gamma(shape, out=out, size=out.shape, dtype=dtype) + + +@pytest.mark.parametrize("dtype", ["f4", "f8"]) +@pytest.mark.parametrize("order", ["F", "C"]) +@pytest.mark.parametrize("dist", [random.standard_normal, random.random]) +def test_contig_req_out(dist, order, dtype): + # GH 18704 + out = np.empty((2, 3), dtype=dtype, order=order) + variates = dist(out=out, dtype=dtype) + assert variates is out + variates = dist(out=out, dtype=dtype, size=out.shape) + assert variates is out diff --git a/numpy/random/tests/test_generator_mt19937_regressions.py b/numpy/random/tests/test_generator_mt19937_regressions.py new file mode 100644 index 000000000000..0227d6502c88 --- /dev/null +++ b/numpy/random/tests/test_generator_mt19937_regressions.py @@ -0,0 +1,150 @@ +from numpy.testing import (assert_, assert_array_equal) +import numpy as np +import pytest +from numpy.random import Generator, MT19937 + +mt19937 = Generator(MT19937()) + + +class TestRegression: + + def test_vonmises_range(self): + # Make sure generated random variables are in [-pi, pi]. + # Regression test for ticket #986. + for mu in np.linspace(-7., 7., 5): + r = mt19937.vonmises(mu, 1, 50) + assert_(np.all(r > -np.pi) and np.all(r <= np.pi)) + + def test_hypergeometric_range(self): + # Test for ticket #921 + assert_(np.all(mt19937.hypergeometric(3, 18, 11, size=10) < 4)) + assert_(np.all(mt19937.hypergeometric(18, 3, 11, size=10) > 0)) + + # Test for ticket #5623 + args = (2**20 - 2, 2**20 - 2, 2**20 - 2) # Check for 32-bit systems + assert_(mt19937.hypergeometric(*args) > 0) + + def test_logseries_convergence(self): + # Test for ticket #923 + N = 1000 + mt19937 = Generator(MT19937(0)) + rvsn = mt19937.logseries(0.8, size=N) + # these two frequency counts should be close to theoretical + # numbers with this large sample + # theoretical large N result is 0.49706795 + freq = np.sum(rvsn == 1) / N + msg = f'Frequency was {freq:f}, should be > 0.45' + assert_(freq > 0.45, msg) + # theoretical large N result is 0.19882718 + freq = np.sum(rvsn == 2) / N + msg = f'Frequency was {freq:f}, should be < 0.23' + assert_(freq < 0.23, msg) + + def test_shuffle_mixed_dimension(self): + # Test for trac ticket #2074 + for t in [[1, 2, 3, None], + [(1, 1), (2, 2), (3, 3), None], + [1, (2, 2), (3, 3), None], + [(1, 1), 2, 3, None]]: + mt19937 = Generator(MT19937(12345)) + shuffled = np.array(t, dtype=object) + mt19937.shuffle(shuffled) + expected = np.array([t[2], t[0], t[3], t[1]], dtype=object) + assert_array_equal(np.array(shuffled, dtype=object), expected) + + def test_call_within_randomstate(self): + # Check that custom BitGenerator does not call into global state + res = np.array([1, 8, 0, 1, 5, 3, 3, 8, 1, 4]) + for i in range(3): + mt19937 = Generator(MT19937(i)) + m = Generator(MT19937(4321)) + # If m.state is not honored, the result will change + assert_array_equal(m.choice(10, size=10, p=np.ones(10)/10.), res) + + def test_multivariate_normal_size_types(self): + # Test for multivariate_normal issue with 'size' argument. + # Check that the multivariate_normal size argument can be a + # numpy integer. + mt19937.multivariate_normal([0], [[0]], size=1) + mt19937.multivariate_normal([0], [[0]], size=np.int_(1)) + mt19937.multivariate_normal([0], [[0]], size=np.int64(1)) + + def test_beta_small_parameters(self): + # Test that beta with small a and b parameters does not produce + # NaNs due to roundoff errors causing 0 / 0, gh-5851 + mt19937 = Generator(MT19937(1234567890)) + x = mt19937.beta(0.0001, 0.0001, size=100) + assert_(not np.any(np.isnan(x)), 'Nans in mt19937.beta') + + def test_choice_sum_of_probs_tolerance(self): + # The sum of probs should be 1.0 with some tolerance. + # For low precision dtypes the tolerance was too tight. + # See numpy github issue 6123. + mt19937 = Generator(MT19937(1234)) + a = [1, 2, 3] + counts = [4, 4, 2] + for dt in np.float16, np.float32, np.float64: + probs = np.array(counts, dtype=dt) / sum(counts) + c = mt19937.choice(a, p=probs) + assert_(c in a) + with pytest.raises(ValueError): + mt19937.choice(a, p=probs*0.9) + + def test_shuffle_of_array_of_different_length_strings(self): + # Test that permuting an array of different length strings + # will not cause a segfault on garbage collection + # Tests gh-7710 + mt19937 = Generator(MT19937(1234)) + + a = np.array(['a', 'a' * 1000]) + + for _ in range(100): + mt19937.shuffle(a) + + # Force Garbage Collection - should not segfault. + import gc + gc.collect() + + def test_shuffle_of_array_of_objects(self): + # Test that permuting an array of objects will not cause + # a segfault on garbage collection. + # See gh-7719 + mt19937 = Generator(MT19937(1234)) + a = np.array([np.arange(1), np.arange(4)], dtype=object) + + for _ in range(1000): + mt19937.shuffle(a) + + # Force Garbage Collection - should not segfault. + import gc + gc.collect() + + def test_permutation_subclass(self): + class N(np.ndarray): + pass + + mt19937 = Generator(MT19937(1)) + orig = np.arange(3).view(N) + perm = mt19937.permutation(orig) + assert_array_equal(perm, np.array([2, 0, 1])) + assert_array_equal(orig, np.arange(3).view(N)) + + class M: + a = np.arange(5) + + def __array__(self): + return self.a + + mt19937 = Generator(MT19937(1)) + m = M() + perm = mt19937.permutation(m) + assert_array_equal(perm, np.array([4, 1, 3, 0, 2])) + assert_array_equal(m.__array__(), np.arange(5)) + + def test_gamma_0(self): + assert mt19937.standard_gamma(0.0) == 0.0 + assert_array_equal(mt19937.standard_gamma([0.0]), 0.0) + + actual = mt19937.standard_gamma([0.0], dtype='float') + expected = np.array([0.], dtype=np.float32) + assert_array_equal(actual, expected) diff --git a/numpy/random/tests/test_random.py b/numpy/random/tests/test_random.py index 61c6e912dae6..6a584a511e1c 100644 --- a/numpy/random/tests/test_random.py +++ b/numpy/random/tests/test_random.py @@ -1,6 +1,7 @@ -from __future__ import division, absolute_import, print_function import warnings +import pytest + import numpy as np from numpy.testing import ( assert_, assert_raises, assert_equal, assert_warns, @@ -9,10 +10,9 @@ ) from numpy import random import sys -import warnings -class TestSeed(object): +class TestSeed: def test_scalar(self): s = np.random.RandomState(0) assert_equal(s.randint(1000), 684) @@ -44,13 +44,14 @@ def test_invalid_array(self): def test_invalid_array_shape(self): # gh-9832 - assert_raises(ValueError, np.random.RandomState, np.array([], dtype=np.int64)) + assert_raises(ValueError, np.random.RandomState, + np.array([], dtype=np.int64)) assert_raises(ValueError, np.random.RandomState, [[1, 2, 3]]) assert_raises(ValueError, np.random.RandomState, [[1, 2, 3], [4, 5, 6]]) -class TestBinomial(object): +class TestBinomial: def test_n_zero(self): # Tests the corner case of n == 0 for the binomial distribution. # binomial(0, p) should be zero for any p in [0, 1]. @@ -65,7 +66,7 @@ def test_p_is_nan(self): assert_raises(ValueError, random.binomial, 1, np.nan) -class TestMultinomial(object): +class TestMultinomial: def test_basic(self): random.multinomial(100, [0.2, 0.8]) @@ -92,8 +93,14 @@ def test_size(self): assert_raises(TypeError, np.random.multinomial, 1, p, float(1)) + def test_multidimensional_pvals(self): + assert_raises(ValueError, np.random.multinomial, 10, [[0, 1]]) + assert_raises(ValueError, np.random.multinomial, 10, [[0], [1]]) + assert_raises(ValueError, np.random.multinomial, 10, [[[0], [1]], [[1], [0]]]) + assert_raises(ValueError, np.random.multinomial, 10, np.array([[0, 1], [1, 0]])) -class TestSetState(object): + +class TestSetState: def setup(self): self.seed = 1234567890 self.prng = random.RandomState(self.seed) @@ -141,7 +148,7 @@ def test_negative_binomial(self): self.prng.negative_binomial(0.5, 0.5) -class TestRandint(object): +class TestRandint: rfunc = np.random.randint @@ -206,18 +213,18 @@ def test_in_bounds_fuzz(self): def test_repeatability(self): import hashlib - # We use a md5 hash of generated sequences of 1000 samples + # We use a sha256 hash of generated sequences of 1000 samples # in the range [0, 6) for all but bool, where the range # is [0, 2). Hashes are for little endian numbers. - tgt = {'bool': '7dd3170d7aa461d201a65f8bcf3944b0', - 'int16': '1b7741b80964bb190c50d541dca1cac1', - 'int32': '4dc9fcc2b395577ebb51793e58ed1a05', - 'int64': '17db902806f448331b5a758d7d2ee672', - 'int8': '27dd30c4e08a797063dffac2490b0be6', - 'uint16': '1b7741b80964bb190c50d541dca1cac1', - 'uint32': '4dc9fcc2b395577ebb51793e58ed1a05', - 'uint64': '17db902806f448331b5a758d7d2ee672', - 'uint8': '27dd30c4e08a797063dffac2490b0be6'} + tgt = {'bool': '509aea74d792fb931784c4b0135392c65aec64beee12b0cc167548a2c3d31e71', + 'int16': '7b07f1a920e46f6d0fe02314155a2330bcfd7635e708da50e536c5ebb631a7d4', + 'int32': 'e577bfed6c935de944424667e3da285012e741892dcb7051a8f1ce68ab05c92f', + 'int64': '0fbead0b06759df2cfb55e43148822d4a1ff953c7eb19a5b08445a63bb64fa9e', + 'int8': '001aac3a5acb935a9b186cbe14a1ca064b8bb2dd0b045d48abeacf74d0203404', + 'uint16': '7b07f1a920e46f6d0fe02314155a2330bcfd7635e708da50e536c5ebb631a7d4', + 'uint32': 'e577bfed6c935de944424667e3da285012e741892dcb7051a8f1ce68ab05c92f', + 'uint64': '0fbead0b06759df2cfb55e43148822d4a1ff953c7eb19a5b08445a63bb64fa9e', + 'uint8': '001aac3a5acb935a9b186cbe14a1ca064b8bb2dd0b045d48abeacf74d0203404'} for dt in self.itype[1:]: np.random.seed(1234) @@ -228,13 +235,13 @@ def test_repeatability(self): else: val = self.rfunc(0, 6, size=1000, dtype=dt).byteswap() - res = hashlib.md5(val.view(np.int8)).hexdigest() + res = hashlib.sha256(val.view(np.int8)).hexdigest() assert_(tgt[np.dtype(dt).name] == res) # bools do not depend on endianness np.random.seed(1234) val = self.rfunc(0, 2, size=1000, dtype=bool).view(np.int8) - res = hashlib.md5(val).hexdigest() + res = hashlib.sha256(val).hexdigest() assert_(tgt[np.dtype(bool).name] == res) def test_int64_uint64_corner_case(self): @@ -269,7 +276,7 @@ def test_respect_dtype_singleton(self): sample = self.rfunc(lbnd, ubnd, dtype=dt) assert_equal(sample.dtype, np.dtype(dt)) - for dt in (bool, int, np.long): + for dt in (bool, int, np.compat.long): lbnd = 0 if dt is bool else np.iinfo(dt).min ubnd = 2 if dt is bool else np.iinfo(dt).max + 1 @@ -279,7 +286,7 @@ def test_respect_dtype_singleton(self): assert_equal(type(sample), dt) -class TestRandomDist(object): +class TestRandomDist: # Make sure the random distribution returns the correct value for a # given seed @@ -350,9 +357,9 @@ def test_random_integers_deprecated(self): np.random.random_integers, np.iinfo('l').max, np.iinfo('l').max) - def test_random_sample(self): + def test_random(self): np.random.seed(self.seed) - actual = np.random.random_sample((3, 2)) + actual = np.random.random((3, 2)) desired = np.array([[0.61879477158567997, 0.59162362775974664], [0.88868358904449662, 0.89165480011560816], [0.4575674820298663, 0.7781880808593471]]) @@ -401,6 +408,10 @@ def test_choice_exceptions(self): assert_raises(ValueError, sample, [1, 2], 3, p=[1.1, -0.1]) assert_raises(ValueError, sample, [1, 2], 3, p=[0.4, 0.4]) assert_raises(ValueError, sample, [1, 2, 3], 4, replace=False) + # gh-13087 + assert_raises(ValueError, sample, [1, 2, 3], -2, replace=False) + assert_raises(ValueError, sample, [1, 2, 3], (-1,), replace=False) + assert_raises(ValueError, sample, [1, 2, 3], (-1, 1), replace=False) assert_raises(ValueError, sample, [1, 2, 3], 2, replace=False, p=[1, 0, 0]) @@ -440,6 +451,21 @@ def test_choice_return_shape(self): assert_equal(np.random.choice(6, s, replace=False, p=p).shape, s) assert_equal(np.random.choice(np.arange(6), s, replace=True).shape, s) + # Check zero-size + assert_equal(np.random.randint(0, 0, size=(3, 0, 4)).shape, (3, 0, 4)) + assert_equal(np.random.randint(0, -10, size=0).shape, (0,)) + assert_equal(np.random.randint(10, 10, size=0).shape, (0,)) + assert_equal(np.random.choice(0, size=0).shape, (0,)) + assert_equal(np.random.choice([], size=(0,)).shape, (0,)) + assert_equal(np.random.choice(['a', 'b'], size=(3, 0, 4)).shape, + (3, 0, 4)) + assert_raises(ValueError, np.random.choice, [], 10) + + def test_choice_nan_probabilities(self): + a = np.array([42, 1, 2]) + p = [None, None, None] + assert_raises(ValueError, np.random.choice, a, p=p) + def test_bytes(self): np.random.seed(self.seed) actual = np.random.bytes(10) @@ -458,10 +484,13 @@ def test_shuffle(self): lambda x: [(i, i) for i in x], lambda x: np.asarray([[i, i] for i in x]), lambda x: np.vstack([x, x]).T, + # gh-11442 + lambda x: (np.asarray([(i, i) for i in x], + [("a", int), ("b", int)]) + .view(np.recarray)), # gh-4270 lambda x: np.asarray([(i, i) for i in x], - [("a", object, 1), - ("b", np.int32, 1)])]: + [("a", object), ("b", np.int32)])]: np.random.seed(self.seed) alist = conv([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]) np.random.shuffle(alist) @@ -483,6 +512,58 @@ def test_shuffle_masked(self): assert_equal( sorted(b.data[~b.mask]), sorted(b_orig.data[~b_orig.mask])) + @pytest.mark.parametrize("random", + [np.random, np.random.RandomState(), np.random.default_rng()]) + def test_shuffle_untyped_warning(self, random): + # Create a dict works like a sequence but isn't one + values = {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6} + with pytest.warns(UserWarning, + match="you are shuffling a 'dict' object") as rec: + random.shuffle(values) + assert "test_random" in rec[0].filename + + @pytest.mark.parametrize("random", + [np.random, np.random.RandomState(), np.random.default_rng()]) + @pytest.mark.parametrize("use_array_like", [True, False]) + def test_shuffle_no_object_unpacking(self, random, use_array_like): + class MyArr(np.ndarray): + pass + + items = [ + None, np.array([3]), np.float64(3), np.array(10), np.float64(7) + ] + arr = np.array(items, dtype=object) + item_ids = {id(i) for i in items} + if use_array_like: + arr = arr.view(MyArr) + + # The array was created fine, and did not modify any objects: + assert all(id(i) in item_ids for i in arr) + + if use_array_like and not isinstance(random, np.random.Generator): + # The old API gives incorrect results, but warns about it. + with pytest.warns(UserWarning, + match="Shuffling a one dimensional array.*"): + random.shuffle(arr) + else: + random.shuffle(arr) + assert all(id(i) in item_ids for i in arr) + + def test_shuffle_memoryview(self): + # gh-18273 + # allow graceful handling of memoryviews + # (treat the same as arrays) + np.random.seed(self.seed) + a = np.arange(5).data + np.random.shuffle(a) + assert_equal(np.asarray(a), [0, 1, 4, 3, 2]) + rng = np.random.RandomState(self.seed) + rng.shuffle(a) + assert_equal(np.asarray(a), [0, 1, 2, 3, 4]) + rng = np.random.default_rng(self.seed) + rng.shuffle(a) + assert_equal(np.asarray(a), [4, 1, 0, 3, 2]) + def test_beta(self): np.random.seed(self.seed) actual = np.random.beta(.1, .9, size=(3, 2)) @@ -494,7 +575,7 @@ def test_beta(self): def test_binomial(self): np.random.seed(self.seed) - actual = np.random.binomial(100.123, .456, size=(3, 2)) + actual = np.random.binomial(100, .456, size=(3, 2)) desired = np.array([[37, 43], [42, 48], [46, 45]]) @@ -537,6 +618,12 @@ def test_dirichlet_bad_alpha(self): alpha = np.array([5.4e-01, -1.0e-16]) assert_raises(ValueError, np.random.mtrand.dirichlet, alpha) + # gh-15876 + assert_raises(ValueError, random.dirichlet, [[5, 1]]) + assert_raises(ValueError, random.dirichlet, [[5], [1]]) + assert_raises(ValueError, random.dirichlet, [[[5], [1]], [[1], [5]]]) + assert_raises(ValueError, random.dirichlet, np.array([[5, 1], [1, 5]])) + def test_exponential(self): np.random.seed(self.seed) actual = np.random.exponential(1.1234, size=(3, 2)) @@ -591,7 +678,7 @@ def test_gumbel_0(self): def test_hypergeometric(self): np.random.seed(self.seed) - actual = np.random.hypergeometric(10.1, 5.5, 14, size=(3, 2)) + actual = np.random.hypergeometric(10, 5, 14, size=(3, 2)) desired = np.array([[10, 10], [10, 10], [9, 9]]) @@ -672,7 +759,7 @@ def test_multivariate_normal(self): cov = [[1, 0], [0, 1]] size = (3, 2) actual = np.random.multivariate_normal(mean, cov, size) - desired = np.array([[[1.463620246718631, 11.73759122771936 ], + desired = np.array([[[1.463620246718631, 11.73759122771936], [1.622445133300628, 9.771356667546383]], [[2.154490787682787, 12.170324946056553], [1.719909438201865, 9.230548443648306]], @@ -700,6 +787,12 @@ def test_multivariate_normal(self): assert_raises(ValueError, np.random.multivariate_normal, mean, cov, check_valid='raise') + cov = np.array([[1, 0.1], [0.1, 1]], dtype=np.float32) + with suppress_warnings() as sup: + np.random.multivariate_normal(mean, cov) + w = sup.record(RuntimeWarning) + assert len(w) == 0 + def test_negative_binomial(self): np.random.seed(self.seed) actual = np.random.negative_binomial(n=100, p=.12345, size=(3, 2)) @@ -759,7 +852,7 @@ def test_pareto(self): [1.40840323350391515e+02, 1.98390255135251704e+05]]) # For some reason on 32-bit x86 Ubuntu 12.10 the [1, 0] entry in this # matrix differs by 24 nulps. Discussion: - # http://mail.python.org/pipermail/numpy-discussion/2012-September/063801.html + # https://mail.python.org/pipermail/numpy-discussion/2012-September/063801.html # Consensus is that this is probably some gcc quirk that affects # rounding but not in any important way, so we just use a looser # tolerance on this test: @@ -890,12 +983,15 @@ def __float__(self): raise TypeError throwing_float = np.array(1.0).view(ThrowingFloat) - assert_raises(TypeError, np.random.uniform, throwing_float, throwing_float) + assert_raises(TypeError, np.random.uniform, throwing_float, + throwing_float) class ThrowingInteger(np.ndarray): def __int__(self): raise TypeError + __index__ = __int__ + throwing_int = np.array(1).view(ThrowingInteger) assert_raises(TypeError, np.random.hypergeometric, throwing_int, 1, 1) @@ -930,7 +1026,8 @@ def test_weibull(self): assert_array_almost_equal(actual, desired, decimal=15) def test_weibull_0(self): - assert_equal(np.random.weibull(a=0), 0) + np.random.seed(self.seed) + assert_equal(np.random.weibull(a=0, size=12), np.zeros(12)) assert_raises(ValueError, np.random.weibull, a=-0.) def test_zipf(self): @@ -942,7 +1039,7 @@ def test_zipf(self): assert_array_equal(actual, desired) -class TestBroadcast(object): +class TestBroadcast: # tests that functions that broadcast behave # correctly when presented with non-scalar arguments def setup(self): @@ -1336,6 +1433,8 @@ def test_wald(self): assert_array_almost_equal(actual, desired, decimal=14) assert_raises(ValueError, wald, bad_mean, scale * 3) assert_raises(ValueError, wald, mean, bad_scale * 3) + assert_raises(ValueError, wald, 0.0, 1) + assert_raises(ValueError, wald, 0.5, 0.0) def test_triangular(self): left = [1] @@ -1354,21 +1453,24 @@ def test_triangular(self): assert_array_almost_equal(actual, desired, decimal=14) assert_raises(ValueError, triangular, bad_left_one * 3, mode, right) assert_raises(ValueError, triangular, left * 3, bad_mode_one, right) - assert_raises(ValueError, triangular, bad_left_two * 3, bad_mode_two, right) + assert_raises(ValueError, triangular, bad_left_two * 3, bad_mode_two, + right) self.setSeed() actual = triangular(left, mode * 3, right) assert_array_almost_equal(actual, desired, decimal=14) assert_raises(ValueError, triangular, bad_left_one, mode * 3, right) assert_raises(ValueError, triangular, left, bad_mode_one * 3, right) - assert_raises(ValueError, triangular, bad_left_two, bad_mode_two * 3, right) + assert_raises(ValueError, triangular, bad_left_two, bad_mode_two * 3, + right) self.setSeed() actual = triangular(left, mode, right * 3) assert_array_almost_equal(actual, desired, decimal=14) assert_raises(ValueError, triangular, bad_left_one, mode, right * 3) assert_raises(ValueError, triangular, left, bad_mode_one, right * 3) - assert_raises(ValueError, triangular, bad_left_two, bad_mode_two, right * 3) + assert_raises(ValueError, triangular, bad_left_two, bad_mode_two, + right * 3) def test_binomial(self): n = [1] @@ -1417,7 +1519,7 @@ def test_negative_binomial(self): assert_raises(ValueError, neg_binom, n, bad_p_two * 3) def test_poisson(self): - max_lam = np.random.RandomState().poisson_lam_max + max_lam = np.random.RandomState()._poisson_lam_max lam = [1] bad_lam_one = [-1] @@ -1445,7 +1547,6 @@ def test_zipf(self): assert_raises(ValueError, zipf, np.nan) assert_raises(ValueError, zipf, [0, 0, np.nan]) - def test_geometric(self): p = [0.5] bad_p_one = [-1] @@ -1507,7 +1608,8 @@ def test_logseries(self): assert_raises(ValueError, logseries, bad_p_one * 3) assert_raises(ValueError, logseries, bad_p_two * 3) -class TestThread(object): + +class TestThread: # make sure each state produces the same sequence even in threads def setup(self): self.seeds = range(4) @@ -1549,8 +1651,9 @@ def gen_random(state, out): out[...] = state.multinomial(10, [1/6.]*6, size=10000) self.check_function(gen_random, sz=(10000, 6)) + # See Issue #4263 -class TestSingleEltArrayInput(object): +class TestSingleEltArrayInput: def setup(self): self.argOne = np.array([2]) self.argTwo = np.array([3]) diff --git a/numpy/random/tests/test_randomstate.py b/numpy/random/tests/test_randomstate.py new file mode 100644 index 000000000000..861813a95d1f --- /dev/null +++ b/numpy/random/tests/test_randomstate.py @@ -0,0 +1,2022 @@ +import hashlib +import pickle +import sys +import warnings + +import numpy as np +import pytest +from numpy.testing import ( + assert_, assert_raises, assert_equal, assert_warns, + assert_no_warnings, assert_array_equal, assert_array_almost_equal, + suppress_warnings + ) + +from numpy.random import MT19937, PCG64 +from numpy import random + +INT_FUNCS = {'binomial': (100.0, 0.6), + 'geometric': (.5,), + 'hypergeometric': (20, 20, 10), + 'logseries': (.5,), + 'multinomial': (20, np.ones(6) / 6.0), + 'negative_binomial': (100, .5), + 'poisson': (10.0,), + 'zipf': (2,), + } + +if np.iinfo(int).max < 2**32: + # Windows and some 32-bit platforms, e.g., ARM + INT_FUNC_HASHES = {'binomial': '2fbead005fc63942decb5326d36a1f32fe2c9d32c904ee61e46866b88447c263', + 'logseries': '23ead5dcde35d4cfd4ef2c105e4c3d43304b45dc1b1444b7823b9ee4fa144ebb', + 'geometric': '0d764db64f5c3bad48c8c33551c13b4d07a1e7b470f77629bef6c985cac76fcf', + 'hypergeometric': '7b59bf2f1691626c5815cdcd9a49e1dd68697251d4521575219e4d2a1b8b2c67', + 'multinomial': 'd754fa5b92943a38ec07630de92362dd2e02c43577fc147417dc5b9db94ccdd3', + 'negative_binomial': '8eb216f7cb2a63cf55605422845caaff002fddc64a7dc8b2d45acd477a49e824', + 'poisson': '70c891d76104013ebd6f6bcf30d403a9074b886ff62e4e6b8eb605bf1a4673b7', + 'zipf': '01f074f97517cd5d21747148ac6ca4074dde7fcb7acbaec0a936606fecacd93f', + } +else: + INT_FUNC_HASHES = {'binomial': '8626dd9d052cb608e93d8868de0a7b347258b199493871a1dc56e2a26cacb112', + 'geometric': '8edd53d272e49c4fc8fbbe6c7d08d563d62e482921f3131d0a0e068af30f0db9', + 'hypergeometric': '83496cc4281c77b786c9b7ad88b74d42e01603a55c60577ebab81c3ba8d45657', + 'logseries': '65878a38747c176bc00e930ebafebb69d4e1e16cd3a704e264ea8f5e24f548db', + 'multinomial': '7a984ae6dca26fd25374479e118b22f55db0aedccd5a0f2584ceada33db98605', + 'negative_binomial': 'd636d968e6a24ae92ab52fe11c46ac45b0897e98714426764e820a7d77602a61', + 'poisson': '956552176f77e7c9cb20d0118fc9cf690be488d790ed4b4c4747b965e61b0bb4', + 'zipf': 'f84ba7feffda41e606e20b28dfc0f1ea9964a74574513d4a4cbc98433a8bfa45', + } + + +@pytest.fixture(scope='module', params=INT_FUNCS) +def int_func(request): + return (request.param, INT_FUNCS[request.param], + INT_FUNC_HASHES[request.param]) + + +def assert_mt19937_state_equal(a, b): + assert_equal(a['bit_generator'], b['bit_generator']) + assert_array_equal(a['state']['key'], b['state']['key']) + assert_array_equal(a['state']['pos'], b['state']['pos']) + assert_equal(a['has_gauss'], b['has_gauss']) + assert_equal(a['gauss'], b['gauss']) + + +class TestSeed: + def test_scalar(self): + s = random.RandomState(0) + assert_equal(s.randint(1000), 684) + s = random.RandomState(4294967295) + assert_equal(s.randint(1000), 419) + + def test_array(self): + s = random.RandomState(range(10)) + assert_equal(s.randint(1000), 468) + s = random.RandomState(np.arange(10)) + assert_equal(s.randint(1000), 468) + s = random.RandomState([0]) + assert_equal(s.randint(1000), 973) + s = random.RandomState([4294967295]) + assert_equal(s.randint(1000), 265) + + def test_invalid_scalar(self): + # seed must be an unsigned 32 bit integer + assert_raises(TypeError, random.RandomState, -0.5) + assert_raises(ValueError, random.RandomState, -1) + + def test_invalid_array(self): + # seed must be an unsigned 32 bit integer + assert_raises(TypeError, random.RandomState, [-0.5]) + assert_raises(ValueError, random.RandomState, [-1]) + assert_raises(ValueError, random.RandomState, [4294967296]) + assert_raises(ValueError, random.RandomState, [1, 2, 4294967296]) + assert_raises(ValueError, random.RandomState, [1, -2, 4294967296]) + + def test_invalid_array_shape(self): + # gh-9832 + assert_raises(ValueError, random.RandomState, np.array([], + dtype=np.int64)) + assert_raises(ValueError, random.RandomState, [[1, 2, 3]]) + assert_raises(ValueError, random.RandomState, [[1, 2, 3], + [4, 5, 6]]) + + def test_cannot_seed(self): + rs = random.RandomState(PCG64(0)) + with assert_raises(TypeError): + rs.seed(1234) + + def test_invalid_initialization(self): + assert_raises(ValueError, random.RandomState, MT19937) + + +class TestBinomial: + def test_n_zero(self): + # Tests the corner case of n == 0 for the binomial distribution. + # binomial(0, p) should be zero for any p in [0, 1]. + # This test addresses issue #3480. + zeros = np.zeros(2, dtype='int') + for p in [0, .5, 1]: + assert_(random.binomial(0, p) == 0) + assert_array_equal(random.binomial(zeros, p), zeros) + + def test_p_is_nan(self): + # Issue #4571. + assert_raises(ValueError, random.binomial, 1, np.nan) + + +class TestMultinomial: + def test_basic(self): + random.multinomial(100, [0.2, 0.8]) + + def test_zero_probability(self): + random.multinomial(100, [0.2, 0.8, 0.0, 0.0, 0.0]) + + def test_int_negative_interval(self): + assert_(-5 <= random.randint(-5, -1) < -1) + x = random.randint(-5, -1, 5) + assert_(np.all(-5 <= x)) + assert_(np.all(x < -1)) + + def test_size(self): + # gh-3173 + p = [0.5, 0.5] + assert_equal(random.multinomial(1, p, np.uint32(1)).shape, (1, 2)) + assert_equal(random.multinomial(1, p, np.uint32(1)).shape, (1, 2)) + assert_equal(random.multinomial(1, p, np.uint32(1)).shape, (1, 2)) + assert_equal(random.multinomial(1, p, [2, 2]).shape, (2, 2, 2)) + assert_equal(random.multinomial(1, p, (2, 2)).shape, (2, 2, 2)) + assert_equal(random.multinomial(1, p, np.array((2, 2))).shape, + (2, 2, 2)) + + assert_raises(TypeError, random.multinomial, 1, p, + float(1)) + + def test_invalid_prob(self): + assert_raises(ValueError, random.multinomial, 100, [1.1, 0.2]) + assert_raises(ValueError, random.multinomial, 100, [-.1, 0.9]) + + def test_invalid_n(self): + assert_raises(ValueError, random.multinomial, -1, [0.8, 0.2]) + + def test_p_non_contiguous(self): + p = np.arange(15.) + p /= np.sum(p[1::3]) + pvals = p[1::3] + random.seed(1432985819) + non_contig = random.multinomial(100, pvals=pvals) + random.seed(1432985819) + contig = random.multinomial(100, pvals=np.ascontiguousarray(pvals)) + assert_array_equal(non_contig, contig) + + def test_multinomial_pvals_float32(self): + x = np.array([9.9e-01, 9.9e-01, 1.0e-09, 1.0e-09, 1.0e-09, 1.0e-09, + 1.0e-09, 1.0e-09, 1.0e-09, 1.0e-09], dtype=np.float32) + pvals = x / x.sum() + match = r"[\w\s]*pvals array is cast to 64-bit floating" + with pytest.raises(ValueError, match=match): + random.multinomial(1, pvals) + + +class TestSetState: + def setup(self): + self.seed = 1234567890 + self.random_state = random.RandomState(self.seed) + self.state = self.random_state.get_state() + + def test_basic(self): + old = self.random_state.tomaxint(16) + self.random_state.set_state(self.state) + new = self.random_state.tomaxint(16) + assert_(np.all(old == new)) + + def test_gaussian_reset(self): + # Make sure the cached every-other-Gaussian is reset. + old = self.random_state.standard_normal(size=3) + self.random_state.set_state(self.state) + new = self.random_state.standard_normal(size=3) + assert_(np.all(old == new)) + + def test_gaussian_reset_in_media_res(self): + # When the state is saved with a cached Gaussian, make sure the + # cached Gaussian is restored. + + self.random_state.standard_normal() + state = self.random_state.get_state() + old = self.random_state.standard_normal(size=3) + self.random_state.set_state(state) + new = self.random_state.standard_normal(size=3) + assert_(np.all(old == new)) + + def test_backwards_compatibility(self): + # Make sure we can accept old state tuples that do not have the + # cached Gaussian value. + old_state = self.state[:-2] + x1 = self.random_state.standard_normal(size=16) + self.random_state.set_state(old_state) + x2 = self.random_state.standard_normal(size=16) + self.random_state.set_state(self.state) + x3 = self.random_state.standard_normal(size=16) + assert_(np.all(x1 == x2)) + assert_(np.all(x1 == x3)) + + def test_negative_binomial(self): + # Ensure that the negative binomial results take floating point + # arguments without truncation. + self.random_state.negative_binomial(0.5, 0.5) + + def test_get_state_warning(self): + rs = random.RandomState(PCG64()) + with suppress_warnings() as sup: + w = sup.record(RuntimeWarning) + state = rs.get_state() + assert_(len(w) == 1) + assert isinstance(state, dict) + assert state['bit_generator'] == 'PCG64' + + def test_invalid_legacy_state_setting(self): + state = self.random_state.get_state() + new_state = ('Unknown', ) + state[1:] + assert_raises(ValueError, self.random_state.set_state, new_state) + assert_raises(TypeError, self.random_state.set_state, + np.array(new_state, dtype=object)) + state = self.random_state.get_state(legacy=False) + del state['bit_generator'] + assert_raises(ValueError, self.random_state.set_state, state) + + def test_pickle(self): + self.random_state.seed(0) + self.random_state.random_sample(100) + self.random_state.standard_normal() + pickled = self.random_state.get_state(legacy=False) + assert_equal(pickled['has_gauss'], 1) + rs_unpick = pickle.loads(pickle.dumps(self.random_state)) + unpickled = rs_unpick.get_state(legacy=False) + assert_mt19937_state_equal(pickled, unpickled) + + def test_state_setting(self): + attr_state = self.random_state.__getstate__() + self.random_state.standard_normal() + self.random_state.__setstate__(attr_state) + state = self.random_state.get_state(legacy=False) + assert_mt19937_state_equal(attr_state, state) + + def test_repr(self): + assert repr(self.random_state).startswith('RandomState(MT19937)') + + +class TestRandint: + + rfunc = random.randint + + # valid integer/boolean types + itype = [np.bool_, np.int8, np.uint8, np.int16, np.uint16, + np.int32, np.uint32, np.int64, np.uint64] + + def test_unsupported_type(self): + assert_raises(TypeError, self.rfunc, 1, dtype=float) + + def test_bounds_checking(self): + for dt in self.itype: + lbnd = 0 if dt is np.bool_ else np.iinfo(dt).min + ubnd = 2 if dt is np.bool_ else np.iinfo(dt).max + 1 + assert_raises(ValueError, self.rfunc, lbnd - 1, ubnd, dtype=dt) + assert_raises(ValueError, self.rfunc, lbnd, ubnd + 1, dtype=dt) + assert_raises(ValueError, self.rfunc, ubnd, lbnd, dtype=dt) + assert_raises(ValueError, self.rfunc, 1, 0, dtype=dt) + + def test_rng_zero_and_extremes(self): + for dt in self.itype: + lbnd = 0 if dt is np.bool_ else np.iinfo(dt).min + ubnd = 2 if dt is np.bool_ else np.iinfo(dt).max + 1 + + tgt = ubnd - 1 + assert_equal(self.rfunc(tgt, tgt + 1, size=1000, dtype=dt), tgt) + + tgt = lbnd + assert_equal(self.rfunc(tgt, tgt + 1, size=1000, dtype=dt), tgt) + + tgt = (lbnd + ubnd)//2 + assert_equal(self.rfunc(tgt, tgt + 1, size=1000, dtype=dt), tgt) + + def test_full_range(self): + # Test for ticket #1690 + + for dt in self.itype: + lbnd = 0 if dt is np.bool_ else np.iinfo(dt).min + ubnd = 2 if dt is np.bool_ else np.iinfo(dt).max + 1 + + try: + self.rfunc(lbnd, ubnd, dtype=dt) + except Exception as e: + raise AssertionError("No error should have been raised, " + "but one was with the following " + "message:\n\n%s" % str(e)) + + def test_in_bounds_fuzz(self): + # Don't use fixed seed + random.seed() + + for dt in self.itype[1:]: + for ubnd in [4, 8, 16]: + vals = self.rfunc(2, ubnd, size=2**16, dtype=dt) + assert_(vals.max() < ubnd) + assert_(vals.min() >= 2) + + vals = self.rfunc(0, 2, size=2**16, dtype=np.bool_) + + assert_(vals.max() < 2) + assert_(vals.min() >= 0) + + def test_repeatability(self): + # We use a sha256 hash of generated sequences of 1000 samples + # in the range [0, 6) for all but bool, where the range + # is [0, 2). Hashes are for little endian numbers. + tgt = {'bool': '509aea74d792fb931784c4b0135392c65aec64beee12b0cc167548a2c3d31e71', + 'int16': '7b07f1a920e46f6d0fe02314155a2330bcfd7635e708da50e536c5ebb631a7d4', + 'int32': 'e577bfed6c935de944424667e3da285012e741892dcb7051a8f1ce68ab05c92f', + 'int64': '0fbead0b06759df2cfb55e43148822d4a1ff953c7eb19a5b08445a63bb64fa9e', + 'int8': '001aac3a5acb935a9b186cbe14a1ca064b8bb2dd0b045d48abeacf74d0203404', + 'uint16': '7b07f1a920e46f6d0fe02314155a2330bcfd7635e708da50e536c5ebb631a7d4', + 'uint32': 'e577bfed6c935de944424667e3da285012e741892dcb7051a8f1ce68ab05c92f', + 'uint64': '0fbead0b06759df2cfb55e43148822d4a1ff953c7eb19a5b08445a63bb64fa9e', + 'uint8': '001aac3a5acb935a9b186cbe14a1ca064b8bb2dd0b045d48abeacf74d0203404'} + + for dt in self.itype[1:]: + random.seed(1234) + + # view as little endian for hash + if sys.byteorder == 'little': + val = self.rfunc(0, 6, size=1000, dtype=dt) + else: + val = self.rfunc(0, 6, size=1000, dtype=dt).byteswap() + + res = hashlib.sha256(val.view(np.int8)).hexdigest() + assert_(tgt[np.dtype(dt).name] == res) + + # bools do not depend on endianness + random.seed(1234) + val = self.rfunc(0, 2, size=1000, dtype=bool).view(np.int8) + res = hashlib.sha256(val).hexdigest() + assert_(tgt[np.dtype(bool).name] == res) + + @pytest.mark.skipif(np.iinfo('l').max < 2**32, + reason='Cannot test with 32-bit C long') + def test_repeatability_32bit_boundary_broadcasting(self): + desired = np.array([[[3992670689, 2438360420, 2557845020], + [4107320065, 4142558326, 3216529513], + [1605979228, 2807061240, 665605495]], + [[3211410639, 4128781000, 457175120], + [1712592594, 1282922662, 3081439808], + [3997822960, 2008322436, 1563495165]], + [[1398375547, 4269260146, 115316740], + [3414372578, 3437564012, 2112038651], + [3572980305, 2260248732, 3908238631]], + [[2561372503, 223155946, 3127879445], + [ 441282060, 3514786552, 2148440361], + [1629275283, 3479737011, 3003195987]], + [[ 412181688, 940383289, 3047321305], + [2978368172, 764731833, 2282559898], + [ 105711276, 720447391, 3596512484]]]) + for size in [None, (5, 3, 3)]: + random.seed(12345) + x = self.rfunc([[-1], [0], [1]], [2**32 - 1, 2**32, 2**32 + 1], + size=size) + assert_array_equal(x, desired if size is not None else desired[0]) + + def test_int64_uint64_corner_case(self): + # When stored in Numpy arrays, `lbnd` is casted + # as np.int64, and `ubnd` is casted as np.uint64. + # Checking whether `lbnd` >= `ubnd` used to be + # done solely via direct comparison, which is incorrect + # because when Numpy tries to compare both numbers, + # it casts both to np.float64 because there is + # no integer superset of np.int64 and np.uint64. However, + # `ubnd` is too large to be represented in np.float64, + # causing it be round down to np.iinfo(np.int64).max, + # leading to a ValueError because `lbnd` now equals + # the new `ubnd`. + + dt = np.int64 + tgt = np.iinfo(np.int64).max + lbnd = np.int64(np.iinfo(np.int64).max) + ubnd = np.uint64(np.iinfo(np.int64).max + 1) + + # None of these function calls should + # generate a ValueError now. + actual = random.randint(lbnd, ubnd, dtype=dt) + assert_equal(actual, tgt) + + def test_respect_dtype_singleton(self): + # See gh-7203 + for dt in self.itype: + lbnd = 0 if dt is np.bool_ else np.iinfo(dt).min + ubnd = 2 if dt is np.bool_ else np.iinfo(dt).max + 1 + + sample = self.rfunc(lbnd, ubnd, dtype=dt) + assert_equal(sample.dtype, np.dtype(dt)) + + for dt in (bool, int, np.compat.long): + lbnd = 0 if dt is bool else np.iinfo(dt).min + ubnd = 2 if dt is bool else np.iinfo(dt).max + 1 + + # gh-7284: Ensure that we get Python data types + sample = self.rfunc(lbnd, ubnd, dtype=dt) + assert_(not hasattr(sample, 'dtype')) + assert_equal(type(sample), dt) + + +class TestRandomDist: + # Make sure the random distribution returns the correct value for a + # given seed + + def setup(self): + self.seed = 1234567890 + + def test_rand(self): + random.seed(self.seed) + actual = random.rand(3, 2) + desired = np.array([[0.61879477158567997, 0.59162362775974664], + [0.88868358904449662, 0.89165480011560816], + [0.4575674820298663, 0.7781880808593471]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_rand_singleton(self): + random.seed(self.seed) + actual = random.rand() + desired = 0.61879477158567997 + assert_array_almost_equal(actual, desired, decimal=15) + + def test_randn(self): + random.seed(self.seed) + actual = random.randn(3, 2) + desired = np.array([[1.34016345771863121, 1.73759122771936081], + [1.498988344300628, -0.2286433324536169], + [2.031033998682787, 2.17032494605655257]]) + assert_array_almost_equal(actual, desired, decimal=15) + + random.seed(self.seed) + actual = random.randn() + assert_array_almost_equal(actual, desired[0, 0], decimal=15) + + def test_randint(self): + random.seed(self.seed) + actual = random.randint(-99, 99, size=(3, 2)) + desired = np.array([[31, 3], + [-52, 41], + [-48, -66]]) + assert_array_equal(actual, desired) + + def test_random_integers(self): + random.seed(self.seed) + with suppress_warnings() as sup: + w = sup.record(DeprecationWarning) + actual = random.random_integers(-99, 99, size=(3, 2)) + assert_(len(w) == 1) + desired = np.array([[31, 3], + [-52, 41], + [-48, -66]]) + assert_array_equal(actual, desired) + + random.seed(self.seed) + with suppress_warnings() as sup: + w = sup.record(DeprecationWarning) + actual = random.random_integers(198, size=(3, 2)) + assert_(len(w) == 1) + assert_array_equal(actual, desired + 100) + + def test_tomaxint(self): + random.seed(self.seed) + rs = random.RandomState(self.seed) + actual = rs.tomaxint(size=(3, 2)) + if np.iinfo(int).max == 2147483647: + desired = np.array([[1328851649, 731237375], + [1270502067, 320041495], + [1908433478, 499156889]], dtype=np.int64) + else: + desired = np.array([[5707374374421908479, 5456764827585442327], + [8196659375100692377, 8224063923314595285], + [4220315081820346526, 7177518203184491332]], + dtype=np.int64) + + assert_equal(actual, desired) + + rs.seed(self.seed) + actual = rs.tomaxint() + assert_equal(actual, desired[0, 0]) + + def test_random_integers_max_int(self): + # Tests whether random_integers can generate the + # maximum allowed Python int that can be converted + # into a C long. Previous implementations of this + # method have thrown an OverflowError when attempting + # to generate this integer. + with suppress_warnings() as sup: + w = sup.record(DeprecationWarning) + actual = random.random_integers(np.iinfo('l').max, + np.iinfo('l').max) + assert_(len(w) == 1) + + desired = np.iinfo('l').max + assert_equal(actual, desired) + with suppress_warnings() as sup: + w = sup.record(DeprecationWarning) + typer = np.dtype('l').type + actual = random.random_integers(typer(np.iinfo('l').max), + typer(np.iinfo('l').max)) + assert_(len(w) == 1) + assert_equal(actual, desired) + + def test_random_integers_deprecated(self): + with warnings.catch_warnings(): + warnings.simplefilter("error", DeprecationWarning) + + # DeprecationWarning raised with high == None + assert_raises(DeprecationWarning, + random.random_integers, + np.iinfo('l').max) + + # DeprecationWarning raised with high != None + assert_raises(DeprecationWarning, + random.random_integers, + np.iinfo('l').max, np.iinfo('l').max) + + def test_random_sample(self): + random.seed(self.seed) + actual = random.random_sample((3, 2)) + desired = np.array([[0.61879477158567997, 0.59162362775974664], + [0.88868358904449662, 0.89165480011560816], + [0.4575674820298663, 0.7781880808593471]]) + assert_array_almost_equal(actual, desired, decimal=15) + + random.seed(self.seed) + actual = random.random_sample() + assert_array_almost_equal(actual, desired[0, 0], decimal=15) + + def test_choice_uniform_replace(self): + random.seed(self.seed) + actual = random.choice(4, 4) + desired = np.array([2, 3, 2, 3]) + assert_array_equal(actual, desired) + + def test_choice_nonuniform_replace(self): + random.seed(self.seed) + actual = random.choice(4, 4, p=[0.4, 0.4, 0.1, 0.1]) + desired = np.array([1, 1, 2, 2]) + assert_array_equal(actual, desired) + + def test_choice_uniform_noreplace(self): + random.seed(self.seed) + actual = random.choice(4, 3, replace=False) + desired = np.array([0, 1, 3]) + assert_array_equal(actual, desired) + + def test_choice_nonuniform_noreplace(self): + random.seed(self.seed) + actual = random.choice(4, 3, replace=False, p=[0.1, 0.3, 0.5, 0.1]) + desired = np.array([2, 3, 1]) + assert_array_equal(actual, desired) + + def test_choice_noninteger(self): + random.seed(self.seed) + actual = random.choice(['a', 'b', 'c', 'd'], 4) + desired = np.array(['c', 'd', 'c', 'd']) + assert_array_equal(actual, desired) + + def test_choice_exceptions(self): + sample = random.choice + assert_raises(ValueError, sample, -1, 3) + assert_raises(ValueError, sample, 3., 3) + assert_raises(ValueError, sample, [[1, 2], [3, 4]], 3) + assert_raises(ValueError, sample, [], 3) + assert_raises(ValueError, sample, [1, 2, 3, 4], 3, + p=[[0.25, 0.25], [0.25, 0.25]]) + assert_raises(ValueError, sample, [1, 2], 3, p=[0.4, 0.4, 0.2]) + assert_raises(ValueError, sample, [1, 2], 3, p=[1.1, -0.1]) + assert_raises(ValueError, sample, [1, 2], 3, p=[0.4, 0.4]) + assert_raises(ValueError, sample, [1, 2, 3], 4, replace=False) + # gh-13087 + assert_raises(ValueError, sample, [1, 2, 3], -2, replace=False) + assert_raises(ValueError, sample, [1, 2, 3], (-1,), replace=False) + assert_raises(ValueError, sample, [1, 2, 3], (-1, 1), replace=False) + assert_raises(ValueError, sample, [1, 2, 3], 2, + replace=False, p=[1, 0, 0]) + + def test_choice_return_shape(self): + p = [0.1, 0.9] + # Check scalar + assert_(np.isscalar(random.choice(2, replace=True))) + assert_(np.isscalar(random.choice(2, replace=False))) + assert_(np.isscalar(random.choice(2, replace=True, p=p))) + assert_(np.isscalar(random.choice(2, replace=False, p=p))) + assert_(np.isscalar(random.choice([1, 2], replace=True))) + assert_(random.choice([None], replace=True) is None) + a = np.array([1, 2]) + arr = np.empty(1, dtype=object) + arr[0] = a + assert_(random.choice(arr, replace=True) is a) + + # Check 0-d array + s = tuple() + assert_(not np.isscalar(random.choice(2, s, replace=True))) + assert_(not np.isscalar(random.choice(2, s, replace=False))) + assert_(not np.isscalar(random.choice(2, s, replace=True, p=p))) + assert_(not np.isscalar(random.choice(2, s, replace=False, p=p))) + assert_(not np.isscalar(random.choice([1, 2], s, replace=True))) + assert_(random.choice([None], s, replace=True).ndim == 0) + a = np.array([1, 2]) + arr = np.empty(1, dtype=object) + arr[0] = a + assert_(random.choice(arr, s, replace=True).item() is a) + + # Check multi dimensional array + s = (2, 3) + p = [0.1, 0.1, 0.1, 0.1, 0.4, 0.2] + assert_equal(random.choice(6, s, replace=True).shape, s) + assert_equal(random.choice(6, s, replace=False).shape, s) + assert_equal(random.choice(6, s, replace=True, p=p).shape, s) + assert_equal(random.choice(6, s, replace=False, p=p).shape, s) + assert_equal(random.choice(np.arange(6), s, replace=True).shape, s) + + # Check zero-size + assert_equal(random.randint(0, 0, size=(3, 0, 4)).shape, (3, 0, 4)) + assert_equal(random.randint(0, -10, size=0).shape, (0,)) + assert_equal(random.randint(10, 10, size=0).shape, (0,)) + assert_equal(random.choice(0, size=0).shape, (0,)) + assert_equal(random.choice([], size=(0,)).shape, (0,)) + assert_equal(random.choice(['a', 'b'], size=(3, 0, 4)).shape, + (3, 0, 4)) + assert_raises(ValueError, random.choice, [], 10) + + def test_choice_nan_probabilities(self): + a = np.array([42, 1, 2]) + p = [None, None, None] + assert_raises(ValueError, random.choice, a, p=p) + + def test_choice_p_non_contiguous(self): + p = np.ones(10) / 5 + p[1::2] = 3.0 + random.seed(self.seed) + non_contig = random.choice(5, 3, p=p[::2]) + random.seed(self.seed) + contig = random.choice(5, 3, p=np.ascontiguousarray(p[::2])) + assert_array_equal(non_contig, contig) + + def test_bytes(self): + random.seed(self.seed) + actual = random.bytes(10) + desired = b'\x82Ui\x9e\xff\x97+Wf\xa5' + assert_equal(actual, desired) + + def test_shuffle(self): + # Test lists, arrays (of various dtypes), and multidimensional versions + # of both, c-contiguous or not: + for conv in [lambda x: np.array([]), + lambda x: x, + lambda x: np.asarray(x).astype(np.int8), + lambda x: np.asarray(x).astype(np.float32), + lambda x: np.asarray(x).astype(np.complex64), + lambda x: np.asarray(x).astype(object), + lambda x: [(i, i) for i in x], + lambda x: np.asarray([[i, i] for i in x]), + lambda x: np.vstack([x, x]).T, + # gh-11442 + lambda x: (np.asarray([(i, i) for i in x], + [("a", int), ("b", int)]) + .view(np.recarray)), + # gh-4270 + lambda x: np.asarray([(i, i) for i in x], + [("a", object, (1,)), + ("b", np.int32, (1,))])]: + random.seed(self.seed) + alist = conv([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]) + random.shuffle(alist) + actual = alist + desired = conv([0, 1, 9, 6, 2, 4, 5, 8, 7, 3]) + assert_array_equal(actual, desired) + + def test_shuffle_masked(self): + # gh-3263 + a = np.ma.masked_values(np.reshape(range(20), (5, 4)) % 3 - 1, -1) + b = np.ma.masked_values(np.arange(20) % 3 - 1, -1) + a_orig = a.copy() + b_orig = b.copy() + for i in range(50): + random.shuffle(a) + assert_equal( + sorted(a.data[~a.mask]), sorted(a_orig.data[~a_orig.mask])) + random.shuffle(b) + assert_equal( + sorted(b.data[~b.mask]), sorted(b_orig.data[~b_orig.mask])) + + def test_shuffle_invalid_objects(self): + x = np.array(3) + assert_raises(TypeError, random.shuffle, x) + + def test_permutation(self): + random.seed(self.seed) + alist = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] + actual = random.permutation(alist) + desired = [0, 1, 9, 6, 2, 4, 5, 8, 7, 3] + assert_array_equal(actual, desired) + + random.seed(self.seed) + arr_2d = np.atleast_2d([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]).T + actual = random.permutation(arr_2d) + assert_array_equal(actual, np.atleast_2d(desired).T) + + random.seed(self.seed) + bad_x_str = "abcd" + assert_raises(IndexError, random.permutation, bad_x_str) + + random.seed(self.seed) + bad_x_float = 1.2 + assert_raises(IndexError, random.permutation, bad_x_float) + + integer_val = 10 + desired = [9, 0, 8, 5, 1, 3, 4, 7, 6, 2] + + random.seed(self.seed) + actual = random.permutation(integer_val) + assert_array_equal(actual, desired) + + def test_beta(self): + random.seed(self.seed) + actual = random.beta(.1, .9, size=(3, 2)) + desired = np.array( + [[1.45341850513746058e-02, 5.31297615662868145e-04], + [1.85366619058432324e-06, 4.19214516800110563e-03], + [1.58405155108498093e-04, 1.26252891949397652e-04]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_binomial(self): + random.seed(self.seed) + actual = random.binomial(100.123, .456, size=(3, 2)) + desired = np.array([[37, 43], + [42, 48], + [46, 45]]) + assert_array_equal(actual, desired) + + random.seed(self.seed) + actual = random.binomial(100.123, .456) + desired = 37 + assert_array_equal(actual, desired) + + def test_chisquare(self): + random.seed(self.seed) + actual = random.chisquare(50, size=(3, 2)) + desired = np.array([[63.87858175501090585, 68.68407748911370447], + [65.77116116901505904, 47.09686762438974483], + [72.3828403199695174, 74.18408615260374006]]) + assert_array_almost_equal(actual, desired, decimal=13) + + def test_dirichlet(self): + random.seed(self.seed) + alpha = np.array([51.72840233779265162, 39.74494232180943953]) + actual = random.dirichlet(alpha, size=(3, 2)) + desired = np.array([[[0.54539444573611562, 0.45460555426388438], + [0.62345816822039413, 0.37654183177960598]], + [[0.55206000085785778, 0.44793999914214233], + [0.58964023305154301, 0.41035976694845688]], + [[0.59266909280647828, 0.40733090719352177], + [0.56974431743975207, 0.43025568256024799]]]) + assert_array_almost_equal(actual, desired, decimal=15) + bad_alpha = np.array([5.4e-01, -1.0e-16]) + assert_raises(ValueError, random.dirichlet, bad_alpha) + + random.seed(self.seed) + alpha = np.array([51.72840233779265162, 39.74494232180943953]) + actual = random.dirichlet(alpha) + assert_array_almost_equal(actual, desired[0, 0], decimal=15) + + def test_dirichlet_size(self): + # gh-3173 + p = np.array([51.72840233779265162, 39.74494232180943953]) + assert_equal(random.dirichlet(p, np.uint32(1)).shape, (1, 2)) + assert_equal(random.dirichlet(p, np.uint32(1)).shape, (1, 2)) + assert_equal(random.dirichlet(p, np.uint32(1)).shape, (1, 2)) + assert_equal(random.dirichlet(p, [2, 2]).shape, (2, 2, 2)) + assert_equal(random.dirichlet(p, (2, 2)).shape, (2, 2, 2)) + assert_equal(random.dirichlet(p, np.array((2, 2))).shape, (2, 2, 2)) + + assert_raises(TypeError, random.dirichlet, p, float(1)) + + def test_dirichlet_bad_alpha(self): + # gh-2089 + alpha = np.array([5.4e-01, -1.0e-16]) + assert_raises(ValueError, random.dirichlet, alpha) + + def test_dirichlet_alpha_non_contiguous(self): + a = np.array([51.72840233779265162, -1.0, 39.74494232180943953]) + alpha = a[::2] + random.seed(self.seed) + non_contig = random.dirichlet(alpha, size=(3, 2)) + random.seed(self.seed) + contig = random.dirichlet(np.ascontiguousarray(alpha), + size=(3, 2)) + assert_array_almost_equal(non_contig, contig) + + def test_exponential(self): + random.seed(self.seed) + actual = random.exponential(1.1234, size=(3, 2)) + desired = np.array([[1.08342649775011624, 1.00607889924557314], + [2.46628830085216721, 2.49668106809923884], + [0.68717433461363442, 1.69175666993575979]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_exponential_0(self): + assert_equal(random.exponential(scale=0), 0) + assert_raises(ValueError, random.exponential, scale=-0.) + + def test_f(self): + random.seed(self.seed) + actual = random.f(12, 77, size=(3, 2)) + desired = np.array([[1.21975394418575878, 1.75135759791559775], + [1.44803115017146489, 1.22108959480396262], + [1.02176975757740629, 1.34431827623300415]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_gamma(self): + random.seed(self.seed) + actual = random.gamma(5, 3, size=(3, 2)) + desired = np.array([[24.60509188649287182, 28.54993563207210627], + [26.13476110204064184, 12.56988482927716078], + [31.71863275789960568, 33.30143302795922011]]) + assert_array_almost_equal(actual, desired, decimal=14) + + def test_gamma_0(self): + assert_equal(random.gamma(shape=0, scale=0), 0) + assert_raises(ValueError, random.gamma, shape=-0., scale=-0.) + + def test_geometric(self): + random.seed(self.seed) + actual = random.geometric(.123456789, size=(3, 2)) + desired = np.array([[8, 7], + [17, 17], + [5, 12]]) + assert_array_equal(actual, desired) + + def test_geometric_exceptions(self): + assert_raises(ValueError, random.geometric, 1.1) + assert_raises(ValueError, random.geometric, [1.1] * 10) + assert_raises(ValueError, random.geometric, -0.1) + assert_raises(ValueError, random.geometric, [-0.1] * 10) + with suppress_warnings() as sup: + sup.record(RuntimeWarning) + assert_raises(ValueError, random.geometric, np.nan) + assert_raises(ValueError, random.geometric, [np.nan] * 10) + + def test_gumbel(self): + random.seed(self.seed) + actual = random.gumbel(loc=.123456789, scale=2.0, size=(3, 2)) + desired = np.array([[0.19591898743416816, 0.34405539668096674], + [-1.4492522252274278, -1.47374816298446865], + [1.10651090478803416, -0.69535848626236174]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_gumbel_0(self): + assert_equal(random.gumbel(scale=0), 0) + assert_raises(ValueError, random.gumbel, scale=-0.) + + def test_hypergeometric(self): + random.seed(self.seed) + actual = random.hypergeometric(10.1, 5.5, 14, size=(3, 2)) + desired = np.array([[10, 10], + [10, 10], + [9, 9]]) + assert_array_equal(actual, desired) + + # Test nbad = 0 + actual = random.hypergeometric(5, 0, 3, size=4) + desired = np.array([3, 3, 3, 3]) + assert_array_equal(actual, desired) + + actual = random.hypergeometric(15, 0, 12, size=4) + desired = np.array([12, 12, 12, 12]) + assert_array_equal(actual, desired) + + # Test ngood = 0 + actual = random.hypergeometric(0, 5, 3, size=4) + desired = np.array([0, 0, 0, 0]) + assert_array_equal(actual, desired) + + actual = random.hypergeometric(0, 15, 12, size=4) + desired = np.array([0, 0, 0, 0]) + assert_array_equal(actual, desired) + + def test_laplace(self): + random.seed(self.seed) + actual = random.laplace(loc=.123456789, scale=2.0, size=(3, 2)) + desired = np.array([[0.66599721112760157, 0.52829452552221945], + [3.12791959514407125, 3.18202813572992005], + [-0.05391065675859356, 1.74901336242837324]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_laplace_0(self): + assert_equal(random.laplace(scale=0), 0) + assert_raises(ValueError, random.laplace, scale=-0.) + + def test_logistic(self): + random.seed(self.seed) + actual = random.logistic(loc=.123456789, scale=2.0, size=(3, 2)) + desired = np.array([[1.09232835305011444, 0.8648196662399954], + [4.27818590694950185, 4.33897006346929714], + [-0.21682183359214885, 2.63373365386060332]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_lognormal(self): + random.seed(self.seed) + actual = random.lognormal(mean=.123456789, sigma=2.0, size=(3, 2)) + desired = np.array([[16.50698631688883822, 36.54846706092654784], + [22.67886599981281748, 0.71617561058995771], + [65.72798501792723869, 86.84341601437161273]]) + assert_array_almost_equal(actual, desired, decimal=13) + + def test_lognormal_0(self): + assert_equal(random.lognormal(sigma=0), 1) + assert_raises(ValueError, random.lognormal, sigma=-0.) + + def test_logseries(self): + random.seed(self.seed) + actual = random.logseries(p=.923456789, size=(3, 2)) + desired = np.array([[2, 2], + [6, 17], + [3, 6]]) + assert_array_equal(actual, desired) + + def test_logseries_exceptions(self): + with suppress_warnings() as sup: + sup.record(RuntimeWarning) + assert_raises(ValueError, random.logseries, np.nan) + assert_raises(ValueError, random.logseries, [np.nan] * 10) + + def test_multinomial(self): + random.seed(self.seed) + actual = random.multinomial(20, [1 / 6.] * 6, size=(3, 2)) + desired = np.array([[[4, 3, 5, 4, 2, 2], + [5, 2, 8, 2, 2, 1]], + [[3, 4, 3, 6, 0, 4], + [2, 1, 4, 3, 6, 4]], + [[4, 4, 2, 5, 2, 3], + [4, 3, 4, 2, 3, 4]]]) + assert_array_equal(actual, desired) + + def test_multivariate_normal(self): + random.seed(self.seed) + mean = (.123456789, 10) + cov = [[1, 0], [0, 1]] + size = (3, 2) + actual = random.multivariate_normal(mean, cov, size) + desired = np.array([[[1.463620246718631, 11.73759122771936], + [1.622445133300628, 9.771356667546383]], + [[2.154490787682787, 12.170324946056553], + [1.719909438201865, 9.230548443648306]], + [[0.689515026297799, 9.880729819607714], + [-0.023054015651998, 9.201096623542879]]]) + + assert_array_almost_equal(actual, desired, decimal=15) + + # Check for default size, was raising deprecation warning + actual = random.multivariate_normal(mean, cov) + desired = np.array([0.895289569463708, 9.17180864067987]) + assert_array_almost_equal(actual, desired, decimal=15) + + # Check that non positive-semidefinite covariance warns with + # RuntimeWarning + mean = [0, 0] + cov = [[1, 2], [2, 1]] + assert_warns(RuntimeWarning, random.multivariate_normal, mean, cov) + + # and that it doesn't warn with RuntimeWarning check_valid='ignore' + assert_no_warnings(random.multivariate_normal, mean, cov, + check_valid='ignore') + + # and that it raises with RuntimeWarning check_valid='raises' + assert_raises(ValueError, random.multivariate_normal, mean, cov, + check_valid='raise') + + cov = np.array([[1, 0.1], [0.1, 1]], dtype=np.float32) + with suppress_warnings() as sup: + random.multivariate_normal(mean, cov) + w = sup.record(RuntimeWarning) + assert len(w) == 0 + + mu = np.zeros(2) + cov = np.eye(2) + assert_raises(ValueError, random.multivariate_normal, mean, cov, + check_valid='other') + assert_raises(ValueError, random.multivariate_normal, + np.zeros((2, 1, 1)), cov) + assert_raises(ValueError, random.multivariate_normal, + mu, np.empty((3, 2))) + assert_raises(ValueError, random.multivariate_normal, + mu, np.eye(3)) + + def test_negative_binomial(self): + random.seed(self.seed) + actual = random.negative_binomial(n=100, p=.12345, size=(3, 2)) + desired = np.array([[848, 841], + [892, 611], + [779, 647]]) + assert_array_equal(actual, desired) + + def test_negative_binomial_exceptions(self): + with suppress_warnings() as sup: + sup.record(RuntimeWarning) + assert_raises(ValueError, random.negative_binomial, 100, np.nan) + assert_raises(ValueError, random.negative_binomial, 100, + [np.nan] * 10) + + def test_noncentral_chisquare(self): + random.seed(self.seed) + actual = random.noncentral_chisquare(df=5, nonc=5, size=(3, 2)) + desired = np.array([[23.91905354498517511, 13.35324692733826346], + [31.22452661329736401, 16.60047399466177254], + [5.03461598262724586, 17.94973089023519464]]) + assert_array_almost_equal(actual, desired, decimal=14) + + actual = random.noncentral_chisquare(df=.5, nonc=.2, size=(3, 2)) + desired = np.array([[1.47145377828516666, 0.15052899268012659], + [0.00943803056963588, 1.02647251615666169], + [0.332334982684171, 0.15451287602753125]]) + assert_array_almost_equal(actual, desired, decimal=14) + + random.seed(self.seed) + actual = random.noncentral_chisquare(df=5, nonc=0, size=(3, 2)) + desired = np.array([[9.597154162763948, 11.725484450296079], + [10.413711048138335, 3.694475922923986], + [13.484222138963087, 14.377255424602957]]) + assert_array_almost_equal(actual, desired, decimal=14) + + def test_noncentral_f(self): + random.seed(self.seed) + actual = random.noncentral_f(dfnum=5, dfden=2, nonc=1, + size=(3, 2)) + desired = np.array([[1.40598099674926669, 0.34207973179285761], + [3.57715069265772545, 7.92632662577829805], + [0.43741599463544162, 1.1774208752428319]]) + assert_array_almost_equal(actual, desired, decimal=14) + + def test_noncentral_f_nan(self): + random.seed(self.seed) + actual = random.noncentral_f(dfnum=5, dfden=2, nonc=np.nan) + assert np.isnan(actual) + + def test_normal(self): + random.seed(self.seed) + actual = random.normal(loc=.123456789, scale=2.0, size=(3, 2)) + desired = np.array([[2.80378370443726244, 3.59863924443872163], + [3.121433477601256, -0.33382987590723379], + [4.18552478636557357, 4.46410668111310471]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_normal_0(self): + assert_equal(random.normal(scale=0), 0) + assert_raises(ValueError, random.normal, scale=-0.) + + def test_pareto(self): + random.seed(self.seed) + actual = random.pareto(a=.123456789, size=(3, 2)) + desired = np.array( + [[2.46852460439034849e+03, 1.41286880810518346e+03], + [5.28287797029485181e+07, 6.57720981047328785e+07], + [1.40840323350391515e+02, 1.98390255135251704e+05]]) + # For some reason on 32-bit x86 Ubuntu 12.10 the [1, 0] entry in this + # matrix differs by 24 nulps. Discussion: + # https://mail.python.org/pipermail/numpy-discussion/2012-September/063801.html + # Consensus is that this is probably some gcc quirk that affects + # rounding but not in any important way, so we just use a looser + # tolerance on this test: + np.testing.assert_array_almost_equal_nulp(actual, desired, nulp=30) + + def test_poisson(self): + random.seed(self.seed) + actual = random.poisson(lam=.123456789, size=(3, 2)) + desired = np.array([[0, 0], + [1, 0], + [0, 0]]) + assert_array_equal(actual, desired) + + def test_poisson_exceptions(self): + lambig = np.iinfo('l').max + lamneg = -1 + assert_raises(ValueError, random.poisson, lamneg) + assert_raises(ValueError, random.poisson, [lamneg] * 10) + assert_raises(ValueError, random.poisson, lambig) + assert_raises(ValueError, random.poisson, [lambig] * 10) + with suppress_warnings() as sup: + sup.record(RuntimeWarning) + assert_raises(ValueError, random.poisson, np.nan) + assert_raises(ValueError, random.poisson, [np.nan] * 10) + + def test_power(self): + random.seed(self.seed) + actual = random.power(a=.123456789, size=(3, 2)) + desired = np.array([[0.02048932883240791, 0.01424192241128213], + [0.38446073748535298, 0.39499689943484395], + [0.00177699707563439, 0.13115505880863756]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_rayleigh(self): + random.seed(self.seed) + actual = random.rayleigh(scale=10, size=(3, 2)) + desired = np.array([[13.8882496494248393, 13.383318339044731], + [20.95413364294492098, 21.08285015800712614], + [11.06066537006854311, 17.35468505778271009]]) + assert_array_almost_equal(actual, desired, decimal=14) + + def test_rayleigh_0(self): + assert_equal(random.rayleigh(scale=0), 0) + assert_raises(ValueError, random.rayleigh, scale=-0.) + + def test_standard_cauchy(self): + random.seed(self.seed) + actual = random.standard_cauchy(size=(3, 2)) + desired = np.array([[0.77127660196445336, -6.55601161955910605], + [0.93582023391158309, -2.07479293013759447], + [-4.74601644297011926, 0.18338989290760804]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_standard_exponential(self): + random.seed(self.seed) + actual = random.standard_exponential(size=(3, 2)) + desired = np.array([[0.96441739162374596, 0.89556604882105506], + [2.1953785836319808, 2.22243285392490542], + [0.6116915921431676, 1.50592546727413201]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_standard_gamma(self): + random.seed(self.seed) + actual = random.standard_gamma(shape=3, size=(3, 2)) + desired = np.array([[5.50841531318455058, 6.62953470301903103], + [5.93988484943779227, 2.31044849402133989], + [7.54838614231317084, 8.012756093271868]]) + assert_array_almost_equal(actual, desired, decimal=14) + + def test_standard_gamma_0(self): + assert_equal(random.standard_gamma(shape=0), 0) + assert_raises(ValueError, random.standard_gamma, shape=-0.) + + def test_standard_normal(self): + random.seed(self.seed) + actual = random.standard_normal(size=(3, 2)) + desired = np.array([[1.34016345771863121, 1.73759122771936081], + [1.498988344300628, -0.2286433324536169], + [2.031033998682787, 2.17032494605655257]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_randn_singleton(self): + random.seed(self.seed) + actual = random.randn() + desired = np.array(1.34016345771863121) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_standard_t(self): + random.seed(self.seed) + actual = random.standard_t(df=10, size=(3, 2)) + desired = np.array([[0.97140611862659965, -0.08830486548450577], + [1.36311143689505321, -0.55317463909867071], + [-0.18473749069684214, 0.61181537341755321]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_triangular(self): + random.seed(self.seed) + actual = random.triangular(left=5.12, mode=10.23, right=20.34, + size=(3, 2)) + desired = np.array([[12.68117178949215784, 12.4129206149193152], + [16.20131377335158263, 16.25692138747600524], + [11.20400690911820263, 14.4978144835829923]]) + assert_array_almost_equal(actual, desired, decimal=14) + + def test_uniform(self): + random.seed(self.seed) + actual = random.uniform(low=1.23, high=10.54, size=(3, 2)) + desired = np.array([[6.99097932346268003, 6.73801597444323974], + [9.50364421400426274, 9.53130618907631089], + [5.48995325769805476, 8.47493103280052118]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_uniform_range_bounds(self): + fmin = np.finfo('float').min + fmax = np.finfo('float').max + + func = random.uniform + assert_raises(OverflowError, func, -np.inf, 0) + assert_raises(OverflowError, func, 0, np.inf) + assert_raises(OverflowError, func, fmin, fmax) + assert_raises(OverflowError, func, [-np.inf], [0]) + assert_raises(OverflowError, func, [0], [np.inf]) + + # (fmax / 1e17) - fmin is within range, so this should not throw + # account for i386 extended precision DBL_MAX / 1e17 + DBL_MAX > + # DBL_MAX by increasing fmin a bit + random.uniform(low=np.nextafter(fmin, 1), high=fmax / 1e17) + + def test_scalar_exception_propagation(self): + # Tests that exceptions are correctly propagated in distributions + # when called with objects that throw exceptions when converted to + # scalars. + # + # Regression test for gh: 8865 + + class ThrowingFloat(np.ndarray): + def __float__(self): + raise TypeError + + throwing_float = np.array(1.0).view(ThrowingFloat) + assert_raises(TypeError, random.uniform, throwing_float, + throwing_float) + + class ThrowingInteger(np.ndarray): + def __int__(self): + raise TypeError + + throwing_int = np.array(1).view(ThrowingInteger) + assert_raises(TypeError, random.hypergeometric, throwing_int, 1, 1) + + def test_vonmises(self): + random.seed(self.seed) + actual = random.vonmises(mu=1.23, kappa=1.54, size=(3, 2)) + desired = np.array([[2.28567572673902042, 2.89163838442285037], + [0.38198375564286025, 2.57638023113890746], + [1.19153771588353052, 1.83509849681825354]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_vonmises_small(self): + # check infinite loop, gh-4720 + random.seed(self.seed) + r = random.vonmises(mu=0., kappa=1.1e-8, size=10**6) + assert_(np.isfinite(r).all()) + + def test_vonmises_large(self): + # guard against changes in RandomState when Generator is fixed + random.seed(self.seed) + actual = random.vonmises(mu=0., kappa=1e7, size=3) + desired = np.array([4.634253748521111e-04, + 3.558873596114509e-04, + -2.337119622577433e-04]) + assert_array_almost_equal(actual, desired, decimal=8) + + def test_vonmises_nan(self): + random.seed(self.seed) + r = random.vonmises(mu=0., kappa=np.nan) + assert_(np.isnan(r)) + + def test_wald(self): + random.seed(self.seed) + actual = random.wald(mean=1.23, scale=1.54, size=(3, 2)) + desired = np.array([[3.82935265715889983, 5.13125249184285526], + [0.35045403618358717, 1.50832396872003538], + [0.24124319895843183, 0.22031101461955038]]) + assert_array_almost_equal(actual, desired, decimal=14) + + def test_weibull(self): + random.seed(self.seed) + actual = random.weibull(a=1.23, size=(3, 2)) + desired = np.array([[0.97097342648766727, 0.91422896443565516], + [1.89517770034962929, 1.91414357960479564], + [0.67057783752390987, 1.39494046635066793]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_weibull_0(self): + random.seed(self.seed) + assert_equal(random.weibull(a=0, size=12), np.zeros(12)) + assert_raises(ValueError, random.weibull, a=-0.) + + def test_zipf(self): + random.seed(self.seed) + actual = random.zipf(a=1.23, size=(3, 2)) + desired = np.array([[66, 29], + [1, 1], + [3, 13]]) + assert_array_equal(actual, desired) + + +class TestBroadcast: + # tests that functions that broadcast behave + # correctly when presented with non-scalar arguments + def setup(self): + self.seed = 123456789 + + def set_seed(self): + random.seed(self.seed) + + def test_uniform(self): + low = [0] + high = [1] + uniform = random.uniform + desired = np.array([0.53283302478975902, + 0.53413660089041659, + 0.50955303552646702]) + + self.set_seed() + actual = uniform(low * 3, high) + assert_array_almost_equal(actual, desired, decimal=14) + + self.set_seed() + actual = uniform(low, high * 3) + assert_array_almost_equal(actual, desired, decimal=14) + + def test_normal(self): + loc = [0] + scale = [1] + bad_scale = [-1] + normal = random.normal + desired = np.array([2.2129019979039612, + 2.1283977976520019, + 1.8417114045748335]) + + self.set_seed() + actual = normal(loc * 3, scale) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, normal, loc * 3, bad_scale) + + self.set_seed() + actual = normal(loc, scale * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, normal, loc, bad_scale * 3) + + def test_beta(self): + a = [1] + b = [2] + bad_a = [-1] + bad_b = [-2] + beta = random.beta + desired = np.array([0.19843558305989056, + 0.075230336409423643, + 0.24976865978980844]) + + self.set_seed() + actual = beta(a * 3, b) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, beta, bad_a * 3, b) + assert_raises(ValueError, beta, a * 3, bad_b) + + self.set_seed() + actual = beta(a, b * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, beta, bad_a, b * 3) + assert_raises(ValueError, beta, a, bad_b * 3) + + def test_exponential(self): + scale = [1] + bad_scale = [-1] + exponential = random.exponential + desired = np.array([0.76106853658845242, + 0.76386282278691653, + 0.71243813125891797]) + + self.set_seed() + actual = exponential(scale * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, exponential, bad_scale * 3) + + def test_standard_gamma(self): + shape = [1] + bad_shape = [-1] + std_gamma = random.standard_gamma + desired = np.array([0.76106853658845242, + 0.76386282278691653, + 0.71243813125891797]) + + self.set_seed() + actual = std_gamma(shape * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, std_gamma, bad_shape * 3) + + def test_gamma(self): + shape = [1] + scale = [2] + bad_shape = [-1] + bad_scale = [-2] + gamma = random.gamma + desired = np.array([1.5221370731769048, + 1.5277256455738331, + 1.4248762625178359]) + + self.set_seed() + actual = gamma(shape * 3, scale) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, gamma, bad_shape * 3, scale) + assert_raises(ValueError, gamma, shape * 3, bad_scale) + + self.set_seed() + actual = gamma(shape, scale * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, gamma, bad_shape, scale * 3) + assert_raises(ValueError, gamma, shape, bad_scale * 3) + + def test_f(self): + dfnum = [1] + dfden = [2] + bad_dfnum = [-1] + bad_dfden = [-2] + f = random.f + desired = np.array([0.80038951638264799, + 0.86768719635363512, + 2.7251095168386801]) + + self.set_seed() + actual = f(dfnum * 3, dfden) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, f, bad_dfnum * 3, dfden) + assert_raises(ValueError, f, dfnum * 3, bad_dfden) + + self.set_seed() + actual = f(dfnum, dfden * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, f, bad_dfnum, dfden * 3) + assert_raises(ValueError, f, dfnum, bad_dfden * 3) + + def test_noncentral_f(self): + dfnum = [2] + dfden = [3] + nonc = [4] + bad_dfnum = [0] + bad_dfden = [-1] + bad_nonc = [-2] + nonc_f = random.noncentral_f + desired = np.array([9.1393943263705211, + 13.025456344595602, + 8.8018098359100545]) + + self.set_seed() + actual = nonc_f(dfnum * 3, dfden, nonc) + assert_array_almost_equal(actual, desired, decimal=14) + assert np.all(np.isnan(nonc_f(dfnum, dfden, [np.nan] * 3))) + + assert_raises(ValueError, nonc_f, bad_dfnum * 3, dfden, nonc) + assert_raises(ValueError, nonc_f, dfnum * 3, bad_dfden, nonc) + assert_raises(ValueError, nonc_f, dfnum * 3, dfden, bad_nonc) + + self.set_seed() + actual = nonc_f(dfnum, dfden * 3, nonc) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, nonc_f, bad_dfnum, dfden * 3, nonc) + assert_raises(ValueError, nonc_f, dfnum, bad_dfden * 3, nonc) + assert_raises(ValueError, nonc_f, dfnum, dfden * 3, bad_nonc) + + self.set_seed() + actual = nonc_f(dfnum, dfden, nonc * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, nonc_f, bad_dfnum, dfden, nonc * 3) + assert_raises(ValueError, nonc_f, dfnum, bad_dfden, nonc * 3) + assert_raises(ValueError, nonc_f, dfnum, dfden, bad_nonc * 3) + + def test_noncentral_f_small_df(self): + self.set_seed() + desired = np.array([6.869638627492048, 0.785880199263955]) + actual = random.noncentral_f(0.9, 0.9, 2, size=2) + assert_array_almost_equal(actual, desired, decimal=14) + + def test_chisquare(self): + df = [1] + bad_df = [-1] + chisquare = random.chisquare + desired = np.array([0.57022801133088286, + 0.51947702108840776, + 0.1320969254923558]) + + self.set_seed() + actual = chisquare(df * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, chisquare, bad_df * 3) + + def test_noncentral_chisquare(self): + df = [1] + nonc = [2] + bad_df = [-1] + bad_nonc = [-2] + nonc_chi = random.noncentral_chisquare + desired = np.array([9.0015599467913763, + 4.5804135049718742, + 6.0872302432834564]) + + self.set_seed() + actual = nonc_chi(df * 3, nonc) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, nonc_chi, bad_df * 3, nonc) + assert_raises(ValueError, nonc_chi, df * 3, bad_nonc) + + self.set_seed() + actual = nonc_chi(df, nonc * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, nonc_chi, bad_df, nonc * 3) + assert_raises(ValueError, nonc_chi, df, bad_nonc * 3) + + def test_standard_t(self): + df = [1] + bad_df = [-1] + t = random.standard_t + desired = np.array([3.0702872575217643, + 5.8560725167361607, + 1.0274791436474273]) + + self.set_seed() + actual = t(df * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, t, bad_df * 3) + assert_raises(ValueError, random.standard_t, bad_df * 3) + + def test_vonmises(self): + mu = [2] + kappa = [1] + bad_kappa = [-1] + vonmises = random.vonmises + desired = np.array([2.9883443664201312, + -2.7064099483995943, + -1.8672476700665914]) + + self.set_seed() + actual = vonmises(mu * 3, kappa) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, vonmises, mu * 3, bad_kappa) + + self.set_seed() + actual = vonmises(mu, kappa * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, vonmises, mu, bad_kappa * 3) + + def test_pareto(self): + a = [1] + bad_a = [-1] + pareto = random.pareto + desired = np.array([1.1405622680198362, + 1.1465519762044529, + 1.0389564467453547]) + + self.set_seed() + actual = pareto(a * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, pareto, bad_a * 3) + assert_raises(ValueError, random.pareto, bad_a * 3) + + def test_weibull(self): + a = [1] + bad_a = [-1] + weibull = random.weibull + desired = np.array([0.76106853658845242, + 0.76386282278691653, + 0.71243813125891797]) + + self.set_seed() + actual = weibull(a * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, weibull, bad_a * 3) + assert_raises(ValueError, random.weibull, bad_a * 3) + + def test_power(self): + a = [1] + bad_a = [-1] + power = random.power + desired = np.array([0.53283302478975902, + 0.53413660089041659, + 0.50955303552646702]) + + self.set_seed() + actual = power(a * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, power, bad_a * 3) + assert_raises(ValueError, random.power, bad_a * 3) + + def test_laplace(self): + loc = [0] + scale = [1] + bad_scale = [-1] + laplace = random.laplace + desired = np.array([0.067921356028507157, + 0.070715642226971326, + 0.019290950698972624]) + + self.set_seed() + actual = laplace(loc * 3, scale) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, laplace, loc * 3, bad_scale) + + self.set_seed() + actual = laplace(loc, scale * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, laplace, loc, bad_scale * 3) + + def test_gumbel(self): + loc = [0] + scale = [1] + bad_scale = [-1] + gumbel = random.gumbel + desired = np.array([0.2730318639556768, + 0.26936705726291116, + 0.33906220393037939]) + + self.set_seed() + actual = gumbel(loc * 3, scale) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, gumbel, loc * 3, bad_scale) + + self.set_seed() + actual = gumbel(loc, scale * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, gumbel, loc, bad_scale * 3) + + def test_logistic(self): + loc = [0] + scale = [1] + bad_scale = [-1] + logistic = random.logistic + desired = np.array([0.13152135837586171, + 0.13675915696285773, + 0.038216792802833396]) + + self.set_seed() + actual = logistic(loc * 3, scale) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, logistic, loc * 3, bad_scale) + + self.set_seed() + actual = logistic(loc, scale * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, logistic, loc, bad_scale * 3) + assert_equal(random.logistic(1.0, 0.0), 1.0) + + def test_lognormal(self): + mean = [0] + sigma = [1] + bad_sigma = [-1] + lognormal = random.lognormal + desired = np.array([9.1422086044848427, + 8.4013952870126261, + 6.3073234116578671]) + + self.set_seed() + actual = lognormal(mean * 3, sigma) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, lognormal, mean * 3, bad_sigma) + assert_raises(ValueError, random.lognormal, mean * 3, bad_sigma) + + self.set_seed() + actual = lognormal(mean, sigma * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, lognormal, mean, bad_sigma * 3) + assert_raises(ValueError, random.lognormal, mean, bad_sigma * 3) + + def test_rayleigh(self): + scale = [1] + bad_scale = [-1] + rayleigh = random.rayleigh + desired = np.array([1.2337491937897689, + 1.2360119924878694, + 1.1936818095781789]) + + self.set_seed() + actual = rayleigh(scale * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, rayleigh, bad_scale * 3) + + def test_wald(self): + mean = [0.5] + scale = [1] + bad_mean = [0] + bad_scale = [-2] + wald = random.wald + desired = np.array([0.11873681120271318, + 0.12450084820795027, + 0.9096122728408238]) + + self.set_seed() + actual = wald(mean * 3, scale) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, wald, bad_mean * 3, scale) + assert_raises(ValueError, wald, mean * 3, bad_scale) + assert_raises(ValueError, random.wald, bad_mean * 3, scale) + assert_raises(ValueError, random.wald, mean * 3, bad_scale) + + self.set_seed() + actual = wald(mean, scale * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, wald, bad_mean, scale * 3) + assert_raises(ValueError, wald, mean, bad_scale * 3) + assert_raises(ValueError, wald, 0.0, 1) + assert_raises(ValueError, wald, 0.5, 0.0) + + def test_triangular(self): + left = [1] + right = [3] + mode = [2] + bad_left_one = [3] + bad_mode_one = [4] + bad_left_two, bad_mode_two = right * 2 + triangular = random.triangular + desired = np.array([2.03339048710429, + 2.0347400359389356, + 2.0095991069536208]) + + self.set_seed() + actual = triangular(left * 3, mode, right) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, triangular, bad_left_one * 3, mode, right) + assert_raises(ValueError, triangular, left * 3, bad_mode_one, right) + assert_raises(ValueError, triangular, bad_left_two * 3, bad_mode_two, + right) + + self.set_seed() + actual = triangular(left, mode * 3, right) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, triangular, bad_left_one, mode * 3, right) + assert_raises(ValueError, triangular, left, bad_mode_one * 3, right) + assert_raises(ValueError, triangular, bad_left_two, bad_mode_two * 3, + right) + + self.set_seed() + actual = triangular(left, mode, right * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, triangular, bad_left_one, mode, right * 3) + assert_raises(ValueError, triangular, left, bad_mode_one, right * 3) + assert_raises(ValueError, triangular, bad_left_two, bad_mode_two, + right * 3) + + assert_raises(ValueError, triangular, 10., 0., 20.) + assert_raises(ValueError, triangular, 10., 25., 20.) + assert_raises(ValueError, triangular, 10., 10., 10.) + + def test_binomial(self): + n = [1] + p = [0.5] + bad_n = [-1] + bad_p_one = [-1] + bad_p_two = [1.5] + binom = random.binomial + desired = np.array([1, 1, 1]) + + self.set_seed() + actual = binom(n * 3, p) + assert_array_equal(actual, desired) + assert_raises(ValueError, binom, bad_n * 3, p) + assert_raises(ValueError, binom, n * 3, bad_p_one) + assert_raises(ValueError, binom, n * 3, bad_p_two) + + self.set_seed() + actual = binom(n, p * 3) + assert_array_equal(actual, desired) + assert_raises(ValueError, binom, bad_n, p * 3) + assert_raises(ValueError, binom, n, bad_p_one * 3) + assert_raises(ValueError, binom, n, bad_p_two * 3) + + def test_negative_binomial(self): + n = [1] + p = [0.5] + bad_n = [-1] + bad_p_one = [-1] + bad_p_two = [1.5] + neg_binom = random.negative_binomial + desired = np.array([1, 0, 1]) + + self.set_seed() + actual = neg_binom(n * 3, p) + assert_array_equal(actual, desired) + assert_raises(ValueError, neg_binom, bad_n * 3, p) + assert_raises(ValueError, neg_binom, n * 3, bad_p_one) + assert_raises(ValueError, neg_binom, n * 3, bad_p_two) + + self.set_seed() + actual = neg_binom(n, p * 3) + assert_array_equal(actual, desired) + assert_raises(ValueError, neg_binom, bad_n, p * 3) + assert_raises(ValueError, neg_binom, n, bad_p_one * 3) + assert_raises(ValueError, neg_binom, n, bad_p_two * 3) + + def test_poisson(self): + max_lam = random.RandomState()._poisson_lam_max + + lam = [1] + bad_lam_one = [-1] + bad_lam_two = [max_lam * 2] + poisson = random.poisson + desired = np.array([1, 1, 0]) + + self.set_seed() + actual = poisson(lam * 3) + assert_array_equal(actual, desired) + assert_raises(ValueError, poisson, bad_lam_one * 3) + assert_raises(ValueError, poisson, bad_lam_two * 3) + + def test_zipf(self): + a = [2] + bad_a = [0] + zipf = random.zipf + desired = np.array([2, 2, 1]) + + self.set_seed() + actual = zipf(a * 3) + assert_array_equal(actual, desired) + assert_raises(ValueError, zipf, bad_a * 3) + with np.errstate(invalid='ignore'): + assert_raises(ValueError, zipf, np.nan) + assert_raises(ValueError, zipf, [0, 0, np.nan]) + + def test_geometric(self): + p = [0.5] + bad_p_one = [-1] + bad_p_two = [1.5] + geom = random.geometric + desired = np.array([2, 2, 2]) + + self.set_seed() + actual = geom(p * 3) + assert_array_equal(actual, desired) + assert_raises(ValueError, geom, bad_p_one * 3) + assert_raises(ValueError, geom, bad_p_two * 3) + + def test_hypergeometric(self): + ngood = [1] + nbad = [2] + nsample = [2] + bad_ngood = [-1] + bad_nbad = [-2] + bad_nsample_one = [0] + bad_nsample_two = [4] + hypergeom = random.hypergeometric + desired = np.array([1, 1, 1]) + + self.set_seed() + actual = hypergeom(ngood * 3, nbad, nsample) + assert_array_equal(actual, desired) + assert_raises(ValueError, hypergeom, bad_ngood * 3, nbad, nsample) + assert_raises(ValueError, hypergeom, ngood * 3, bad_nbad, nsample) + assert_raises(ValueError, hypergeom, ngood * 3, nbad, bad_nsample_one) + assert_raises(ValueError, hypergeom, ngood * 3, nbad, bad_nsample_two) + + self.set_seed() + actual = hypergeom(ngood, nbad * 3, nsample) + assert_array_equal(actual, desired) + assert_raises(ValueError, hypergeom, bad_ngood, nbad * 3, nsample) + assert_raises(ValueError, hypergeom, ngood, bad_nbad * 3, nsample) + assert_raises(ValueError, hypergeom, ngood, nbad * 3, bad_nsample_one) + assert_raises(ValueError, hypergeom, ngood, nbad * 3, bad_nsample_two) + + self.set_seed() + actual = hypergeom(ngood, nbad, nsample * 3) + assert_array_equal(actual, desired) + assert_raises(ValueError, hypergeom, bad_ngood, nbad, nsample * 3) + assert_raises(ValueError, hypergeom, ngood, bad_nbad, nsample * 3) + assert_raises(ValueError, hypergeom, ngood, nbad, bad_nsample_one * 3) + assert_raises(ValueError, hypergeom, ngood, nbad, bad_nsample_two * 3) + + assert_raises(ValueError, hypergeom, -1, 10, 20) + assert_raises(ValueError, hypergeom, 10, -1, 20) + assert_raises(ValueError, hypergeom, 10, 10, 0) + assert_raises(ValueError, hypergeom, 10, 10, 25) + + def test_logseries(self): + p = [0.5] + bad_p_one = [2] + bad_p_two = [-1] + logseries = random.logseries + desired = np.array([1, 1, 1]) + + self.set_seed() + actual = logseries(p * 3) + assert_array_equal(actual, desired) + assert_raises(ValueError, logseries, bad_p_one * 3) + assert_raises(ValueError, logseries, bad_p_two * 3) + + +class TestThread: + # make sure each state produces the same sequence even in threads + def setup(self): + self.seeds = range(4) + + def check_function(self, function, sz): + from threading import Thread + + out1 = np.empty((len(self.seeds),) + sz) + out2 = np.empty((len(self.seeds),) + sz) + + # threaded generation + t = [Thread(target=function, args=(random.RandomState(s), o)) + for s, o in zip(self.seeds, out1)] + [x.start() for x in t] + [x.join() for x in t] + + # the same serial + for s, o in zip(self.seeds, out2): + function(random.RandomState(s), o) + + # these platforms change x87 fpu precision mode in threads + if np.intp().dtype.itemsize == 4 and sys.platform == "win32": + assert_array_almost_equal(out1, out2) + else: + assert_array_equal(out1, out2) + + def test_normal(self): + def gen_random(state, out): + out[...] = state.normal(size=10000) + + self.check_function(gen_random, sz=(10000,)) + + def test_exp(self): + def gen_random(state, out): + out[...] = state.exponential(scale=np.ones((100, 1000))) + + self.check_function(gen_random, sz=(100, 1000)) + + def test_multinomial(self): + def gen_random(state, out): + out[...] = state.multinomial(10, [1 / 6.] * 6, size=10000) + + self.check_function(gen_random, sz=(10000, 6)) + + +# See Issue #4263 +class TestSingleEltArrayInput: + def setup(self): + self.argOne = np.array([2]) + self.argTwo = np.array([3]) + self.argThree = np.array([4]) + self.tgtShape = (1,) + + def test_one_arg_funcs(self): + funcs = (random.exponential, random.standard_gamma, + random.chisquare, random.standard_t, + random.pareto, random.weibull, + random.power, random.rayleigh, + random.poisson, random.zipf, + random.geometric, random.logseries) + + probfuncs = (random.geometric, random.logseries) + + for func in funcs: + if func in probfuncs: # p < 1.0 + out = func(np.array([0.5])) + + else: + out = func(self.argOne) + + assert_equal(out.shape, self.tgtShape) + + def test_two_arg_funcs(self): + funcs = (random.uniform, random.normal, + random.beta, random.gamma, + random.f, random.noncentral_chisquare, + random.vonmises, random.laplace, + random.gumbel, random.logistic, + random.lognormal, random.wald, + random.binomial, random.negative_binomial) + + probfuncs = (random.binomial, random.negative_binomial) + + for func in funcs: + if func in probfuncs: # p <= 1 + argTwo = np.array([0.5]) + + else: + argTwo = self.argTwo + + out = func(self.argOne, argTwo) + assert_equal(out.shape, self.tgtShape) + + out = func(self.argOne[0], argTwo) + assert_equal(out.shape, self.tgtShape) + + out = func(self.argOne, argTwo[0]) + assert_equal(out.shape, self.tgtShape) + + def test_three_arg_funcs(self): + funcs = [random.noncentral_f, random.triangular, + random.hypergeometric] + + for func in funcs: + out = func(self.argOne, self.argTwo, self.argThree) + assert_equal(out.shape, self.tgtShape) + + out = func(self.argOne[0], self.argTwo, self.argThree) + assert_equal(out.shape, self.tgtShape) + + out = func(self.argOne, self.argTwo[0], self.argThree) + assert_equal(out.shape, self.tgtShape) + + +# Ensure returned array dtype is correct for platform +def test_integer_dtype(int_func): + random.seed(123456789) + fname, args, sha256 = int_func + f = getattr(random, fname) + actual = f(*args, size=2) + assert_(actual.dtype == np.dtype('l')) + + +def test_integer_repeat(int_func): + random.seed(123456789) + fname, args, sha256 = int_func + f = getattr(random, fname) + val = f(*args, size=1000000) + if sys.byteorder != 'little': + val = val.byteswap() + res = hashlib.sha256(val.view(np.int8)).hexdigest() + assert_(res == sha256) + + +def test_broadcast_size_error(): + # GH-16833 + with pytest.raises(ValueError): + random.binomial(1, [0.3, 0.7], size=(2, 1)) + with pytest.raises(ValueError): + random.binomial([1, 2], 0.3, size=(2, 1)) + with pytest.raises(ValueError): + random.binomial([1, 2], [0.3, 0.7], size=(2, 1)) diff --git a/numpy/random/tests/test_randomstate_regression.py b/numpy/random/tests/test_randomstate_regression.py new file mode 100644 index 000000000000..7ad19ab5562b --- /dev/null +++ b/numpy/random/tests/test_randomstate_regression.py @@ -0,0 +1,216 @@ +import sys + +import pytest + +from numpy.testing import ( + assert_, assert_array_equal, assert_raises, + ) +import numpy as np + +from numpy import random + + +class TestRegression: + + def test_VonMises_range(self): + # Make sure generated random variables are in [-pi, pi]. + # Regression test for ticket #986. + for mu in np.linspace(-7., 7., 5): + r = random.vonmises(mu, 1, 50) + assert_(np.all(r > -np.pi) and np.all(r <= np.pi)) + + def test_hypergeometric_range(self): + # Test for ticket #921 + assert_(np.all(random.hypergeometric(3, 18, 11, size=10) < 4)) + assert_(np.all(random.hypergeometric(18, 3, 11, size=10) > 0)) + + # Test for ticket #5623 + args = [ + (2**20 - 2, 2**20 - 2, 2**20 - 2), # Check for 32-bit systems + ] + is_64bits = sys.maxsize > 2**32 + if is_64bits and sys.platform != 'win32': + # Check for 64-bit systems + args.append((2**40 - 2, 2**40 - 2, 2**40 - 2)) + for arg in args: + assert_(random.hypergeometric(*arg) > 0) + + def test_logseries_convergence(self): + # Test for ticket #923 + N = 1000 + random.seed(0) + rvsn = random.logseries(0.8, size=N) + # these two frequency counts should be close to theoretical + # numbers with this large sample + # theoretical large N result is 0.49706795 + freq = np.sum(rvsn == 1) / N + msg = f'Frequency was {freq:f}, should be > 0.45' + assert_(freq > 0.45, msg) + # theoretical large N result is 0.19882718 + freq = np.sum(rvsn == 2) / N + msg = f'Frequency was {freq:f}, should be < 0.23' + assert_(freq < 0.23, msg) + + def test_shuffle_mixed_dimension(self): + # Test for trac ticket #2074 + for t in [[1, 2, 3, None], + [(1, 1), (2, 2), (3, 3), None], + [1, (2, 2), (3, 3), None], + [(1, 1), 2, 3, None]]: + random.seed(12345) + shuffled = list(t) + random.shuffle(shuffled) + expected = np.array([t[0], t[3], t[1], t[2]], dtype=object) + assert_array_equal(np.array(shuffled, dtype=object), expected) + + def test_call_within_randomstate(self): + # Check that custom RandomState does not call into global state + m = random.RandomState() + res = np.array([0, 8, 7, 2, 1, 9, 4, 7, 0, 3]) + for i in range(3): + random.seed(i) + m.seed(4321) + # If m.state is not honored, the result will change + assert_array_equal(m.choice(10, size=10, p=np.ones(10)/10.), res) + + def test_multivariate_normal_size_types(self): + # Test for multivariate_normal issue with 'size' argument. + # Check that the multivariate_normal size argument can be a + # numpy integer. + random.multivariate_normal([0], [[0]], size=1) + random.multivariate_normal([0], [[0]], size=np.int_(1)) + random.multivariate_normal([0], [[0]], size=np.int64(1)) + + def test_beta_small_parameters(self): + # Test that beta with small a and b parameters does not produce + # NaNs due to roundoff errors causing 0 / 0, gh-5851 + random.seed(1234567890) + x = random.beta(0.0001, 0.0001, size=100) + assert_(not np.any(np.isnan(x)), 'Nans in random.beta') + + def test_choice_sum_of_probs_tolerance(self): + # The sum of probs should be 1.0 with some tolerance. + # For low precision dtypes the tolerance was too tight. + # See numpy github issue 6123. + random.seed(1234) + a = [1, 2, 3] + counts = [4, 4, 2] + for dt in np.float16, np.float32, np.float64: + probs = np.array(counts, dtype=dt) / sum(counts) + c = random.choice(a, p=probs) + assert_(c in a) + assert_raises(ValueError, random.choice, a, p=probs*0.9) + + def test_shuffle_of_array_of_different_length_strings(self): + # Test that permuting an array of different length strings + # will not cause a segfault on garbage collection + # Tests gh-7710 + random.seed(1234) + + a = np.array(['a', 'a' * 1000]) + + for _ in range(100): + random.shuffle(a) + + # Force Garbage Collection - should not segfault. + import gc + gc.collect() + + def test_shuffle_of_array_of_objects(self): + # Test that permuting an array of objects will not cause + # a segfault on garbage collection. + # See gh-7719 + random.seed(1234) + a = np.array([np.arange(1), np.arange(4)], dtype=object) + + for _ in range(1000): + random.shuffle(a) + + # Force Garbage Collection - should not segfault. + import gc + gc.collect() + + def test_permutation_subclass(self): + class N(np.ndarray): + pass + + random.seed(1) + orig = np.arange(3).view(N) + perm = random.permutation(orig) + assert_array_equal(perm, np.array([0, 2, 1])) + assert_array_equal(orig, np.arange(3).view(N)) + + class M: + a = np.arange(5) + + def __array__(self): + return self.a + + random.seed(1) + m = M() + perm = random.permutation(m) + assert_array_equal(perm, np.array([2, 1, 4, 0, 3])) + assert_array_equal(m.__array__(), np.arange(5)) + + def test_warns_byteorder(self): + # GH 13159 + other_byteord_dt = '<i4' if sys.byteorder == 'big' else '>i4' + with pytest.deprecated_call(match='non-native byteorder is not'): + random.randint(0, 200, size=10, dtype=other_byteord_dt) + + def test_named_argument_initialization(self): + # GH 13669 + rs1 = np.random.RandomState(123456789) + rs2 = np.random.RandomState(seed=123456789) + assert rs1.randint(0, 100) == rs2.randint(0, 100) + + def test_choice_retun_dtype(self): + # GH 9867 + c = np.random.choice(10, p=[.1]*10, size=2) + assert c.dtype == np.dtype(int) + c = np.random.choice(10, p=[.1]*10, replace=False, size=2) + assert c.dtype == np.dtype(int) + c = np.random.choice(10, size=2) + assert c.dtype == np.dtype(int) + c = np.random.choice(10, replace=False, size=2) + assert c.dtype == np.dtype(int) + + @pytest.mark.skipif(np.iinfo('l').max < 2**32, + reason='Cannot test with 32-bit C long') + def test_randint_117(self): + # GH 14189 + random.seed(0) + expected = np.array([2357136044, 2546248239, 3071714933, 3626093760, + 2588848963, 3684848379, 2340255427, 3638918503, + 1819583497, 2678185683], dtype='int64') + actual = random.randint(2**32, size=10) + assert_array_equal(actual, expected) + + def test_p_zero_stream(self): + # Regression test for gh-14522. Ensure that future versions + # generate the same variates as version 1.16. + np.random.seed(12345) + assert_array_equal(random.binomial(1, [0, 0.25, 0.5, 0.75, 1]), + [0, 0, 0, 1, 1]) + + def test_n_zero_stream(self): + # Regression test for gh-14522. Ensure that future versions + # generate the same variates as version 1.16. + np.random.seed(8675309) + expected = np.array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [3, 4, 2, 3, 3, 1, 5, 3, 1, 3]]) + assert_array_equal(random.binomial([[0], [10]], 0.25, size=(2, 10)), + expected) + + +def test_multinomial_empty(): + # gh-20483 + # Ensure that empty p-vals are correctly handled + assert random.multinomial(10, []).shape == (0,) + assert random.multinomial(3, [], size=(7, 5, 3)).shape == (7, 5, 3, 0) + + +def test_multinomial_1d_pval(): + # gh-20483 + with pytest.raises(TypeError, match="pvals must be a 1-d"): + random.multinomial(10, 0.3) diff --git a/numpy/random/tests/test_regression.py b/numpy/random/tests/test_regression.py index 3b4b4ed40a02..8bf419875b3f 100644 --- a/numpy/random/tests/test_regression.py +++ b/numpy/random/tests/test_regression.py @@ -1,15 +1,12 @@ -from __future__ import division, absolute_import, print_function - import sys from numpy.testing import ( assert_, assert_array_equal, assert_raises, ) from numpy import random -from numpy.compat import long import numpy as np -class TestRegression(object): +class TestRegression: def test_VonMises_range(self): # Make sure generated random variables are in [-pi, pi]. @@ -29,7 +26,8 @@ def test_hypergeometric_range(self): ] is_64bits = sys.maxsize > 2**32 if is_64bits and sys.platform != 'win32': - args.append((2**40 - 2, 2**40 - 2, 2**40 - 2)) # Check for 64-bit systems + # Check for 64-bit systems + args.append((2**40 - 2, 2**40 - 2, 2**40 - 2)) for arg in args: assert_(np.random.hypergeometric(*arg) > 0) @@ -41,21 +39,14 @@ def test_logseries_convergence(self): # these two frequency counts should be close to theoretical # numbers with this large sample # theoretical large N result is 0.49706795 - freq = np.sum(rvsn == 1) / float(N) - msg = "Frequency was %f, should be > 0.45" % freq + freq = np.sum(rvsn == 1) / N + msg = f'Frequency was {freq:f}, should be > 0.45' assert_(freq > 0.45, msg) # theoretical large N result is 0.19882718 - freq = np.sum(rvsn == 2) / float(N) - msg = "Frequency was %f, should be < 0.23" % freq + freq = np.sum(rvsn == 2) / N + msg = f'Frequency was {freq:f}, should be < 0.23' assert_(freq < 0.23, msg) - def test_permutation_longs(self): - np.random.seed(1234) - a = np.random.permutation(12) - np.random.seed(1234) - b = np.random.permutation(long(12)) - assert_array_equal(a, b) - def test_shuffle_mixed_dimension(self): # Test for trac ticket #2074 for t in [[1, 2, 3, None], @@ -65,7 +56,8 @@ def test_shuffle_mixed_dimension(self): np.random.seed(12345) shuffled = list(t) random.shuffle(shuffled) - assert_array_equal(shuffled, [t[0], t[3], t[1], t[2]]) + expected = np.array([t[0], t[3], t[1], t[2]], dtype=object) + assert_array_equal(np.array(shuffled, dtype=object), expected) def test_call_within_randomstate(self): # Check that custom RandomState does not call into global state @@ -125,7 +117,7 @@ def test_shuffle_of_array_of_objects(self): # a segfault on garbage collection. # See gh-7719 np.random.seed(1234) - a = np.array([np.arange(1), np.arange(4)]) + a = np.array([np.arange(1), np.arange(4)], dtype=object) for _ in range(1000): np.random.shuffle(a) @@ -133,3 +125,25 @@ def test_shuffle_of_array_of_objects(self): # Force Garbage Collection - should not segfault. import gc gc.collect() + + def test_permutation_subclass(self): + class N(np.ndarray): + pass + + np.random.seed(1) + orig = np.arange(3).view(N) + perm = np.random.permutation(orig) + assert_array_equal(perm, np.array([0, 2, 1])) + assert_array_equal(orig, np.arange(3).view(N)) + + class M: + a = np.arange(5) + + def __array__(self): + return self.a + + np.random.seed(1) + m = M() + perm = np.random.permutation(m) + assert_array_equal(perm, np.array([2, 1, 4, 0, 3])) + assert_array_equal(m.__array__(), np.arange(5)) diff --git a/numpy/random/tests/test_seed_sequence.py b/numpy/random/tests/test_seed_sequence.py new file mode 100644 index 000000000000..f08cf80faafa --- /dev/null +++ b/numpy/random/tests/test_seed_sequence.py @@ -0,0 +1,80 @@ +import numpy as np +from numpy.testing import assert_array_equal, assert_array_compare + +from numpy.random import SeedSequence + + +def test_reference_data(): + """ Check that SeedSequence generates data the same as the C++ reference. + + https://gist.github.com/imneme/540829265469e673d045 + """ + inputs = [ + [3735928559, 195939070, 229505742, 305419896], + [3668361503, 4165561550, 1661411377, 3634257570], + [164546577, 4166754639, 1765190214, 1303880213], + [446610472, 3941463886, 522937693, 1882353782], + [1864922766, 1719732118, 3882010307, 1776744564], + [4141682960, 3310988675, 553637289, 902896340], + [1134851934, 2352871630, 3699409824, 2648159817], + [1240956131, 3107113773, 1283198141, 1924506131], + [2669565031, 579818610, 3042504477, 2774880435], + [2766103236, 2883057919, 4029656435, 862374500], + ] + outputs = [ + [3914649087, 576849849, 3593928901, 2229911004], + [2240804226, 3691353228, 1365957195, 2654016646], + [3562296087, 3191708229, 1147942216, 3726991905], + [1403443605, 3591372999, 1291086759, 441919183], + [1086200464, 2191331643, 560336446, 3658716651], + [3249937430, 2346751812, 847844327, 2996632307], + [2584285912, 4034195531, 3523502488, 169742686], + [959045797, 3875435559, 1886309314, 359682705], + [3978441347, 432478529, 3223635119, 138903045], + [296367413, 4262059219, 13109864, 3283683422], + ] + outputs64 = [ + [2477551240072187391, 9577394838764454085], + [15854241394484835714, 11398914698975566411], + [13708282465491374871, 16007308345579681096], + [15424829579845884309, 1898028439751125927], + [9411697742461147792, 15714068361935982142], + [10079222287618677782, 12870437757549876199], + [17326737873898640088, 729039288628699544], + [16644868984619524261, 1544825456798124994], + [1857481142255628931, 596584038813451439], + [18305404959516669237, 14103312907920476776], + ] + for seed, expected, expected64 in zip(inputs, outputs, outputs64): + expected = np.array(expected, dtype=np.uint32) + ss = SeedSequence(seed) + state = ss.generate_state(len(expected)) + assert_array_equal(state, expected) + state64 = ss.generate_state(len(expected64), dtype=np.uint64) + assert_array_equal(state64, expected64) + + +def test_zero_padding(): + """ Ensure that the implicit zero-padding does not cause problems. + """ + # Ensure that large integers are inserted in little-endian fashion to avoid + # trailing 0s. + ss0 = SeedSequence(42) + ss1 = SeedSequence(42 << 32) + assert_array_compare( + np.not_equal, + ss0.generate_state(4), + ss1.generate_state(4)) + + # Ensure backwards compatibility with the original 0.17 release for small + # integers and no spawn key. + expected42 = np.array([3444837047, 2669555309, 2046530742, 3581440988], + dtype=np.uint32) + assert_array_equal(SeedSequence(42).generate_state(4), expected42) + + # Regression test for gh-16539 to ensure that the implicit 0s don't + # conflict with spawn keys. + assert_array_compare( + np.not_equal, + SeedSequence(42, spawn_key=(0,)).generate_state(4), + expected42) diff --git a/numpy/random/tests/test_smoke.py b/numpy/random/tests/test_smoke.py new file mode 100644 index 000000000000..9becc434d0d1 --- /dev/null +++ b/numpy/random/tests/test_smoke.py @@ -0,0 +1,818 @@ +import pickle +from functools import partial + +import numpy as np +import pytest +from numpy.testing import assert_equal, assert_, assert_array_equal +from numpy.random import (Generator, MT19937, PCG64, PCG64DXSM, Philox, SFC64) + +@pytest.fixture(scope='module', + params=(np.bool_, np.int8, np.int16, np.int32, np.int64, + np.uint8, np.uint16, np.uint32, np.uint64)) +def dtype(request): + return request.param + + +def params_0(f): + val = f() + assert_(np.isscalar(val)) + val = f(10) + assert_(val.shape == (10,)) + val = f((10, 10)) + assert_(val.shape == (10, 10)) + val = f((10, 10, 10)) + assert_(val.shape == (10, 10, 10)) + val = f(size=(5, 5)) + assert_(val.shape == (5, 5)) + + +def params_1(f, bounded=False): + a = 5.0 + b = np.arange(2.0, 12.0) + c = np.arange(2.0, 102.0).reshape((10, 10)) + d = np.arange(2.0, 1002.0).reshape((10, 10, 10)) + e = np.array([2.0, 3.0]) + g = np.arange(2.0, 12.0).reshape((1, 10, 1)) + if bounded: + a = 0.5 + b = b / (1.5 * b.max()) + c = c / (1.5 * c.max()) + d = d / (1.5 * d.max()) + e = e / (1.5 * e.max()) + g = g / (1.5 * g.max()) + + # Scalar + f(a) + # Scalar - size + f(a, size=(10, 10)) + # 1d + f(b) + # 2d + f(c) + # 3d + f(d) + # 1d size + f(b, size=10) + # 2d - size - broadcast + f(e, size=(10, 2)) + # 3d - size + f(g, size=(10, 10, 10)) + + +def comp_state(state1, state2): + identical = True + if isinstance(state1, dict): + for key in state1: + identical &= comp_state(state1[key], state2[key]) + elif type(state1) != type(state2): + identical &= type(state1) == type(state2) + else: + if (isinstance(state1, (list, tuple, np.ndarray)) and isinstance( + state2, (list, tuple, np.ndarray))): + for s1, s2 in zip(state1, state2): + identical &= comp_state(s1, s2) + else: + identical &= state1 == state2 + return identical + + +def warmup(rg, n=None): + if n is None: + n = 11 + np.random.randint(0, 20) + rg.standard_normal(n) + rg.standard_normal(n) + rg.standard_normal(n, dtype=np.float32) + rg.standard_normal(n, dtype=np.float32) + rg.integers(0, 2 ** 24, n, dtype=np.uint64) + rg.integers(0, 2 ** 48, n, dtype=np.uint64) + rg.standard_gamma(11.0, n) + rg.standard_gamma(11.0, n, dtype=np.float32) + rg.random(n, dtype=np.float64) + rg.random(n, dtype=np.float32) + + +class RNG: + @classmethod + def setup_class(cls): + # Overridden in test classes. Place holder to silence IDE noise + cls.bit_generator = PCG64 + cls.advance = None + cls.seed = [12345] + cls.rg = Generator(cls.bit_generator(*cls.seed)) + cls.initial_state = cls.rg.bit_generator.state + cls.seed_vector_bits = 64 + cls._extra_setup() + + @classmethod + def _extra_setup(cls): + cls.vec_1d = np.arange(2.0, 102.0) + cls.vec_2d = np.arange(2.0, 102.0)[None, :] + cls.mat = np.arange(2.0, 102.0, 0.01).reshape((100, 100)) + cls.seed_error = TypeError + + def _reset_state(self): + self.rg.bit_generator.state = self.initial_state + + def test_init(self): + rg = Generator(self.bit_generator()) + state = rg.bit_generator.state + rg.standard_normal(1) + rg.standard_normal(1) + rg.bit_generator.state = state + new_state = rg.bit_generator.state + assert_(comp_state(state, new_state)) + + def test_advance(self): + state = self.rg.bit_generator.state + if hasattr(self.rg.bit_generator, 'advance'): + self.rg.bit_generator.advance(self.advance) + assert_(not comp_state(state, self.rg.bit_generator.state)) + else: + bitgen_name = self.rg.bit_generator.__class__.__name__ + pytest.skip(f'Advance is not supported by {bitgen_name}') + + def test_jump(self): + state = self.rg.bit_generator.state + if hasattr(self.rg.bit_generator, 'jumped'): + bit_gen2 = self.rg.bit_generator.jumped() + jumped_state = bit_gen2.state + assert_(not comp_state(state, jumped_state)) + self.rg.random(2 * 3 * 5 * 7 * 11 * 13 * 17) + self.rg.bit_generator.state = state + bit_gen3 = self.rg.bit_generator.jumped() + rejumped_state = bit_gen3.state + assert_(comp_state(jumped_state, rejumped_state)) + else: + bitgen_name = self.rg.bit_generator.__class__.__name__ + if bitgen_name not in ('SFC64',): + raise AttributeError(f'no "jumped" in {bitgen_name}') + pytest.skip(f'Jump is not supported by {bitgen_name}') + + def test_uniform(self): + r = self.rg.uniform(-1.0, 0.0, size=10) + assert_(len(r) == 10) + assert_((r > -1).all()) + assert_((r <= 0).all()) + + def test_uniform_array(self): + r = self.rg.uniform(np.array([-1.0] * 10), 0.0, size=10) + assert_(len(r) == 10) + assert_((r > -1).all()) + assert_((r <= 0).all()) + r = self.rg.uniform(np.array([-1.0] * 10), + np.array([0.0] * 10), size=10) + assert_(len(r) == 10) + assert_((r > -1).all()) + assert_((r <= 0).all()) + r = self.rg.uniform(-1.0, np.array([0.0] * 10), size=10) + assert_(len(r) == 10) + assert_((r > -1).all()) + assert_((r <= 0).all()) + + def test_random(self): + assert_(len(self.rg.random(10)) == 10) + params_0(self.rg.random) + + def test_standard_normal_zig(self): + assert_(len(self.rg.standard_normal(10)) == 10) + + def test_standard_normal(self): + assert_(len(self.rg.standard_normal(10)) == 10) + params_0(self.rg.standard_normal) + + def test_standard_gamma(self): + assert_(len(self.rg.standard_gamma(10, 10)) == 10) + assert_(len(self.rg.standard_gamma(np.array([10] * 10), 10)) == 10) + params_1(self.rg.standard_gamma) + + def test_standard_exponential(self): + assert_(len(self.rg.standard_exponential(10)) == 10) + params_0(self.rg.standard_exponential) + + def test_standard_exponential_float(self): + randoms = self.rg.standard_exponential(10, dtype='float32') + assert_(len(randoms) == 10) + assert randoms.dtype == np.float32 + params_0(partial(self.rg.standard_exponential, dtype='float32')) + + def test_standard_exponential_float_log(self): + randoms = self.rg.standard_exponential(10, dtype='float32', + method='inv') + assert_(len(randoms) == 10) + assert randoms.dtype == np.float32 + params_0(partial(self.rg.standard_exponential, dtype='float32', + method='inv')) + + def test_standard_cauchy(self): + assert_(len(self.rg.standard_cauchy(10)) == 10) + params_0(self.rg.standard_cauchy) + + def test_standard_t(self): + assert_(len(self.rg.standard_t(10, 10)) == 10) + params_1(self.rg.standard_t) + + def test_binomial(self): + assert_(self.rg.binomial(10, .5) >= 0) + assert_(self.rg.binomial(1000, .5) >= 0) + + def test_reset_state(self): + state = self.rg.bit_generator.state + int_1 = self.rg.integers(2**31) + self.rg.bit_generator.state = state + int_2 = self.rg.integers(2**31) + assert_(int_1 == int_2) + + def test_entropy_init(self): + rg = Generator(self.bit_generator()) + rg2 = Generator(self.bit_generator()) + assert_(not comp_state(rg.bit_generator.state, + rg2.bit_generator.state)) + + def test_seed(self): + rg = Generator(self.bit_generator(*self.seed)) + rg2 = Generator(self.bit_generator(*self.seed)) + rg.random() + rg2.random() + assert_(comp_state(rg.bit_generator.state, rg2.bit_generator.state)) + + def test_reset_state_gauss(self): + rg = Generator(self.bit_generator(*self.seed)) + rg.standard_normal() + state = rg.bit_generator.state + n1 = rg.standard_normal(size=10) + rg2 = Generator(self.bit_generator()) + rg2.bit_generator.state = state + n2 = rg2.standard_normal(size=10) + assert_array_equal(n1, n2) + + def test_reset_state_uint32(self): + rg = Generator(self.bit_generator(*self.seed)) + rg.integers(0, 2 ** 24, 120, dtype=np.uint32) + state = rg.bit_generator.state + n1 = rg.integers(0, 2 ** 24, 10, dtype=np.uint32) + rg2 = Generator(self.bit_generator()) + rg2.bit_generator.state = state + n2 = rg2.integers(0, 2 ** 24, 10, dtype=np.uint32) + assert_array_equal(n1, n2) + + def test_reset_state_float(self): + rg = Generator(self.bit_generator(*self.seed)) + rg.random(dtype='float32') + state = rg.bit_generator.state + n1 = rg.random(size=10, dtype='float32') + rg2 = Generator(self.bit_generator()) + rg2.bit_generator.state = state + n2 = rg2.random(size=10, dtype='float32') + assert_((n1 == n2).all()) + + def test_shuffle(self): + original = np.arange(200, 0, -1) + permuted = self.rg.permutation(original) + assert_((original != permuted).any()) + + def test_permutation(self): + original = np.arange(200, 0, -1) + permuted = self.rg.permutation(original) + assert_((original != permuted).any()) + + def test_beta(self): + vals = self.rg.beta(2.0, 2.0, 10) + assert_(len(vals) == 10) + vals = self.rg.beta(np.array([2.0] * 10), 2.0) + assert_(len(vals) == 10) + vals = self.rg.beta(2.0, np.array([2.0] * 10)) + assert_(len(vals) == 10) + vals = self.rg.beta(np.array([2.0] * 10), np.array([2.0] * 10)) + assert_(len(vals) == 10) + vals = self.rg.beta(np.array([2.0] * 10), np.array([[2.0]] * 10)) + assert_(vals.shape == (10, 10)) + + def test_bytes(self): + vals = self.rg.bytes(10) + assert_(len(vals) == 10) + + def test_chisquare(self): + vals = self.rg.chisquare(2.0, 10) + assert_(len(vals) == 10) + params_1(self.rg.chisquare) + + def test_exponential(self): + vals = self.rg.exponential(2.0, 10) + assert_(len(vals) == 10) + params_1(self.rg.exponential) + + def test_f(self): + vals = self.rg.f(3, 1000, 10) + assert_(len(vals) == 10) + + def test_gamma(self): + vals = self.rg.gamma(3, 2, 10) + assert_(len(vals) == 10) + + def test_geometric(self): + vals = self.rg.geometric(0.5, 10) + assert_(len(vals) == 10) + params_1(self.rg.exponential, bounded=True) + + def test_gumbel(self): + vals = self.rg.gumbel(2.0, 2.0, 10) + assert_(len(vals) == 10) + + def test_laplace(self): + vals = self.rg.laplace(2.0, 2.0, 10) + assert_(len(vals) == 10) + + def test_logitic(self): + vals = self.rg.logistic(2.0, 2.0, 10) + assert_(len(vals) == 10) + + def test_logseries(self): + vals = self.rg.logseries(0.5, 10) + assert_(len(vals) == 10) + + def test_negative_binomial(self): + vals = self.rg.negative_binomial(10, 0.2, 10) + assert_(len(vals) == 10) + + def test_noncentral_chisquare(self): + vals = self.rg.noncentral_chisquare(10, 2, 10) + assert_(len(vals) == 10) + + def test_noncentral_f(self): + vals = self.rg.noncentral_f(3, 1000, 2, 10) + assert_(len(vals) == 10) + vals = self.rg.noncentral_f(np.array([3] * 10), 1000, 2) + assert_(len(vals) == 10) + vals = self.rg.noncentral_f(3, np.array([1000] * 10), 2) + assert_(len(vals) == 10) + vals = self.rg.noncentral_f(3, 1000, np.array([2] * 10)) + assert_(len(vals) == 10) + + def test_normal(self): + vals = self.rg.normal(10, 0.2, 10) + assert_(len(vals) == 10) + + def test_pareto(self): + vals = self.rg.pareto(3.0, 10) + assert_(len(vals) == 10) + + def test_poisson(self): + vals = self.rg.poisson(10, 10) + assert_(len(vals) == 10) + vals = self.rg.poisson(np.array([10] * 10)) + assert_(len(vals) == 10) + params_1(self.rg.poisson) + + def test_power(self): + vals = self.rg.power(0.2, 10) + assert_(len(vals) == 10) + + def test_integers(self): + vals = self.rg.integers(10, 20, 10) + assert_(len(vals) == 10) + + def test_rayleigh(self): + vals = self.rg.rayleigh(0.2, 10) + assert_(len(vals) == 10) + params_1(self.rg.rayleigh, bounded=True) + + def test_vonmises(self): + vals = self.rg.vonmises(10, 0.2, 10) + assert_(len(vals) == 10) + + def test_wald(self): + vals = self.rg.wald(1.0, 1.0, 10) + assert_(len(vals) == 10) + + def test_weibull(self): + vals = self.rg.weibull(1.0, 10) + assert_(len(vals) == 10) + + def test_zipf(self): + vals = self.rg.zipf(10, 10) + assert_(len(vals) == 10) + vals = self.rg.zipf(self.vec_1d) + assert_(len(vals) == 100) + vals = self.rg.zipf(self.vec_2d) + assert_(vals.shape == (1, 100)) + vals = self.rg.zipf(self.mat) + assert_(vals.shape == (100, 100)) + + def test_hypergeometric(self): + vals = self.rg.hypergeometric(25, 25, 20) + assert_(np.isscalar(vals)) + vals = self.rg.hypergeometric(np.array([25] * 10), 25, 20) + assert_(vals.shape == (10,)) + + def test_triangular(self): + vals = self.rg.triangular(-5, 0, 5) + assert_(np.isscalar(vals)) + vals = self.rg.triangular(-5, np.array([0] * 10), 5) + assert_(vals.shape == (10,)) + + def test_multivariate_normal(self): + mean = [0, 0] + cov = [[1, 0], [0, 100]] # diagonal covariance + x = self.rg.multivariate_normal(mean, cov, 5000) + assert_(x.shape == (5000, 2)) + x_zig = self.rg.multivariate_normal(mean, cov, 5000) + assert_(x.shape == (5000, 2)) + x_inv = self.rg.multivariate_normal(mean, cov, 5000) + assert_(x.shape == (5000, 2)) + assert_((x_zig != x_inv).any()) + + def test_multinomial(self): + vals = self.rg.multinomial(100, [1.0 / 3, 2.0 / 3]) + assert_(vals.shape == (2,)) + vals = self.rg.multinomial(100, [1.0 / 3, 2.0 / 3], size=10) + assert_(vals.shape == (10, 2)) + + def test_dirichlet(self): + s = self.rg.dirichlet((10, 5, 3), 20) + assert_(s.shape == (20, 3)) + + def test_pickle(self): + pick = pickle.dumps(self.rg) + unpick = pickle.loads(pick) + assert_((type(self.rg) == type(unpick))) + assert_(comp_state(self.rg.bit_generator.state, + unpick.bit_generator.state)) + + pick = pickle.dumps(self.rg) + unpick = pickle.loads(pick) + assert_((type(self.rg) == type(unpick))) + assert_(comp_state(self.rg.bit_generator.state, + unpick.bit_generator.state)) + + def test_seed_array(self): + if self.seed_vector_bits is None: + bitgen_name = self.bit_generator.__name__ + pytest.skip(f'Vector seeding is not supported by {bitgen_name}') + + if self.seed_vector_bits == 32: + dtype = np.uint32 + else: + dtype = np.uint64 + seed = np.array([1], dtype=dtype) + bg = self.bit_generator(seed) + state1 = bg.state + bg = self.bit_generator(1) + state2 = bg.state + assert_(comp_state(state1, state2)) + + seed = np.arange(4, dtype=dtype) + bg = self.bit_generator(seed) + state1 = bg.state + bg = self.bit_generator(seed[0]) + state2 = bg.state + assert_(not comp_state(state1, state2)) + + seed = np.arange(1500, dtype=dtype) + bg = self.bit_generator(seed) + state1 = bg.state + bg = self.bit_generator(seed[0]) + state2 = bg.state + assert_(not comp_state(state1, state2)) + + seed = 2 ** np.mod(np.arange(1500, dtype=dtype), + self.seed_vector_bits - 1) + 1 + bg = self.bit_generator(seed) + state1 = bg.state + bg = self.bit_generator(seed[0]) + state2 = bg.state + assert_(not comp_state(state1, state2)) + + def test_uniform_float(self): + rg = Generator(self.bit_generator(12345)) + warmup(rg) + state = rg.bit_generator.state + r1 = rg.random(11, dtype=np.float32) + rg2 = Generator(self.bit_generator()) + warmup(rg2) + rg2.bit_generator.state = state + r2 = rg2.random(11, dtype=np.float32) + assert_array_equal(r1, r2) + assert_equal(r1.dtype, np.float32) + assert_(comp_state(rg.bit_generator.state, rg2.bit_generator.state)) + + def test_gamma_floats(self): + rg = Generator(self.bit_generator()) + warmup(rg) + state = rg.bit_generator.state + r1 = rg.standard_gamma(4.0, 11, dtype=np.float32) + rg2 = Generator(self.bit_generator()) + warmup(rg2) + rg2.bit_generator.state = state + r2 = rg2.standard_gamma(4.0, 11, dtype=np.float32) + assert_array_equal(r1, r2) + assert_equal(r1.dtype, np.float32) + assert_(comp_state(rg.bit_generator.state, rg2.bit_generator.state)) + + def test_normal_floats(self): + rg = Generator(self.bit_generator()) + warmup(rg) + state = rg.bit_generator.state + r1 = rg.standard_normal(11, dtype=np.float32) + rg2 = Generator(self.bit_generator()) + warmup(rg2) + rg2.bit_generator.state = state + r2 = rg2.standard_normal(11, dtype=np.float32) + assert_array_equal(r1, r2) + assert_equal(r1.dtype, np.float32) + assert_(comp_state(rg.bit_generator.state, rg2.bit_generator.state)) + + def test_normal_zig_floats(self): + rg = Generator(self.bit_generator()) + warmup(rg) + state = rg.bit_generator.state + r1 = rg.standard_normal(11, dtype=np.float32) + rg2 = Generator(self.bit_generator()) + warmup(rg2) + rg2.bit_generator.state = state + r2 = rg2.standard_normal(11, dtype=np.float32) + assert_array_equal(r1, r2) + assert_equal(r1.dtype, np.float32) + assert_(comp_state(rg.bit_generator.state, rg2.bit_generator.state)) + + def test_output_fill(self): + rg = self.rg + state = rg.bit_generator.state + size = (31, 7, 97) + existing = np.empty(size) + rg.bit_generator.state = state + rg.standard_normal(out=existing) + rg.bit_generator.state = state + direct = rg.standard_normal(size=size) + assert_equal(direct, existing) + + sized = np.empty(size) + rg.bit_generator.state = state + rg.standard_normal(out=sized, size=sized.shape) + + existing = np.empty(size, dtype=np.float32) + rg.bit_generator.state = state + rg.standard_normal(out=existing, dtype=np.float32) + rg.bit_generator.state = state + direct = rg.standard_normal(size=size, dtype=np.float32) + assert_equal(direct, existing) + + def test_output_filling_uniform(self): + rg = self.rg + state = rg.bit_generator.state + size = (31, 7, 97) + existing = np.empty(size) + rg.bit_generator.state = state + rg.random(out=existing) + rg.bit_generator.state = state + direct = rg.random(size=size) + assert_equal(direct, existing) + + existing = np.empty(size, dtype=np.float32) + rg.bit_generator.state = state + rg.random(out=existing, dtype=np.float32) + rg.bit_generator.state = state + direct = rg.random(size=size, dtype=np.float32) + assert_equal(direct, existing) + + def test_output_filling_exponential(self): + rg = self.rg + state = rg.bit_generator.state + size = (31, 7, 97) + existing = np.empty(size) + rg.bit_generator.state = state + rg.standard_exponential(out=existing) + rg.bit_generator.state = state + direct = rg.standard_exponential(size=size) + assert_equal(direct, existing) + + existing = np.empty(size, dtype=np.float32) + rg.bit_generator.state = state + rg.standard_exponential(out=existing, dtype=np.float32) + rg.bit_generator.state = state + direct = rg.standard_exponential(size=size, dtype=np.float32) + assert_equal(direct, existing) + + def test_output_filling_gamma(self): + rg = self.rg + state = rg.bit_generator.state + size = (31, 7, 97) + existing = np.zeros(size) + rg.bit_generator.state = state + rg.standard_gamma(1.0, out=existing) + rg.bit_generator.state = state + direct = rg.standard_gamma(1.0, size=size) + assert_equal(direct, existing) + + existing = np.zeros(size, dtype=np.float32) + rg.bit_generator.state = state + rg.standard_gamma(1.0, out=existing, dtype=np.float32) + rg.bit_generator.state = state + direct = rg.standard_gamma(1.0, size=size, dtype=np.float32) + assert_equal(direct, existing) + + def test_output_filling_gamma_broadcast(self): + rg = self.rg + state = rg.bit_generator.state + size = (31, 7, 97) + mu = np.arange(97.0) + 1.0 + existing = np.zeros(size) + rg.bit_generator.state = state + rg.standard_gamma(mu, out=existing) + rg.bit_generator.state = state + direct = rg.standard_gamma(mu, size=size) + assert_equal(direct, existing) + + existing = np.zeros(size, dtype=np.float32) + rg.bit_generator.state = state + rg.standard_gamma(mu, out=existing, dtype=np.float32) + rg.bit_generator.state = state + direct = rg.standard_gamma(mu, size=size, dtype=np.float32) + assert_equal(direct, existing) + + def test_output_fill_error(self): + rg = self.rg + size = (31, 7, 97) + existing = np.empty(size) + with pytest.raises(TypeError): + rg.standard_normal(out=existing, dtype=np.float32) + with pytest.raises(ValueError): + rg.standard_normal(out=existing[::3]) + existing = np.empty(size, dtype=np.float32) + with pytest.raises(TypeError): + rg.standard_normal(out=existing, dtype=np.float64) + + existing = np.zeros(size, dtype=np.float32) + with pytest.raises(TypeError): + rg.standard_gamma(1.0, out=existing, dtype=np.float64) + with pytest.raises(ValueError): + rg.standard_gamma(1.0, out=existing[::3], dtype=np.float32) + existing = np.zeros(size, dtype=np.float64) + with pytest.raises(TypeError): + rg.standard_gamma(1.0, out=existing, dtype=np.float32) + with pytest.raises(ValueError): + rg.standard_gamma(1.0, out=existing[::3]) + + def test_integers_broadcast(self, dtype): + if dtype == np.bool_: + upper = 2 + lower = 0 + else: + info = np.iinfo(dtype) + upper = int(info.max) + 1 + lower = info.min + self._reset_state() + a = self.rg.integers(lower, [upper] * 10, dtype=dtype) + self._reset_state() + b = self.rg.integers([lower] * 10, upper, dtype=dtype) + assert_equal(a, b) + self._reset_state() + c = self.rg.integers(lower, upper, size=10, dtype=dtype) + assert_equal(a, c) + self._reset_state() + d = self.rg.integers(np.array( + [lower] * 10), np.array([upper], dtype=object), size=10, + dtype=dtype) + assert_equal(a, d) + self._reset_state() + e = self.rg.integers( + np.array([lower] * 10), np.array([upper] * 10), size=10, + dtype=dtype) + assert_equal(a, e) + + self._reset_state() + a = self.rg.integers(0, upper, size=10, dtype=dtype) + self._reset_state() + b = self.rg.integers([upper] * 10, dtype=dtype) + assert_equal(a, b) + + def test_integers_numpy(self, dtype): + high = np.array([1]) + low = np.array([0]) + + out = self.rg.integers(low, high, dtype=dtype) + assert out.shape == (1,) + + out = self.rg.integers(low[0], high, dtype=dtype) + assert out.shape == (1,) + + out = self.rg.integers(low, high[0], dtype=dtype) + assert out.shape == (1,) + + def test_integers_broadcast_errors(self, dtype): + if dtype == np.bool_: + upper = 2 + lower = 0 + else: + info = np.iinfo(dtype) + upper = int(info.max) + 1 + lower = info.min + with pytest.raises(ValueError): + self.rg.integers(lower, [upper + 1] * 10, dtype=dtype) + with pytest.raises(ValueError): + self.rg.integers(lower - 1, [upper] * 10, dtype=dtype) + with pytest.raises(ValueError): + self.rg.integers([lower - 1], [upper] * 10, dtype=dtype) + with pytest.raises(ValueError): + self.rg.integers([0], [0], dtype=dtype) + + +class TestMT19937(RNG): + @classmethod + def setup_class(cls): + cls.bit_generator = MT19937 + cls.advance = None + cls.seed = [2 ** 21 + 2 ** 16 + 2 ** 5 + 1] + cls.rg = Generator(cls.bit_generator(*cls.seed)) + cls.initial_state = cls.rg.bit_generator.state + cls.seed_vector_bits = 32 + cls._extra_setup() + cls.seed_error = ValueError + + def test_numpy_state(self): + nprg = np.random.RandomState() + nprg.standard_normal(99) + state = nprg.get_state() + self.rg.bit_generator.state = state + state2 = self.rg.bit_generator.state + assert_((state[1] == state2['state']['key']).all()) + assert_((state[2] == state2['state']['pos'])) + + +class TestPhilox(RNG): + @classmethod + def setup_class(cls): + cls.bit_generator = Philox + cls.advance = 2**63 + 2**31 + 2**15 + 1 + cls.seed = [12345] + cls.rg = Generator(cls.bit_generator(*cls.seed)) + cls.initial_state = cls.rg.bit_generator.state + cls.seed_vector_bits = 64 + cls._extra_setup() + + +class TestSFC64(RNG): + @classmethod + def setup_class(cls): + cls.bit_generator = SFC64 + cls.advance = None + cls.seed = [12345] + cls.rg = Generator(cls.bit_generator(*cls.seed)) + cls.initial_state = cls.rg.bit_generator.state + cls.seed_vector_bits = 192 + cls._extra_setup() + + +class TestPCG64(RNG): + @classmethod + def setup_class(cls): + cls.bit_generator = PCG64 + cls.advance = 2**63 + 2**31 + 2**15 + 1 + cls.seed = [12345] + cls.rg = Generator(cls.bit_generator(*cls.seed)) + cls.initial_state = cls.rg.bit_generator.state + cls.seed_vector_bits = 64 + cls._extra_setup() + + +class TestPCG64DXSM(RNG): + @classmethod + def setup_class(cls): + cls.bit_generator = PCG64DXSM + cls.advance = 2**63 + 2**31 + 2**15 + 1 + cls.seed = [12345] + cls.rg = Generator(cls.bit_generator(*cls.seed)) + cls.initial_state = cls.rg.bit_generator.state + cls.seed_vector_bits = 64 + cls._extra_setup() + + +class TestDefaultRNG(RNG): + @classmethod + def setup_class(cls): + # This will duplicate some tests that directly instantiate a fresh + # Generator(), but that's okay. + cls.bit_generator = PCG64 + cls.advance = 2**63 + 2**31 + 2**15 + 1 + cls.seed = [12345] + cls.rg = np.random.default_rng(*cls.seed) + cls.initial_state = cls.rg.bit_generator.state + cls.seed_vector_bits = 64 + cls._extra_setup() + + def test_default_is_pcg64(self): + # In order to change the default BitGenerator, we'll go through + # a deprecation cycle to move to a different function. + assert_(isinstance(self.rg.bit_generator, PCG64)) + + def test_seed(self): + np.random.default_rng() + np.random.default_rng(None) + np.random.default_rng(12345) + np.random.default_rng(0) + np.random.default_rng(43660444402423911716352051725018508569) + np.random.default_rng([43660444402423911716352051725018508569, + 279705150948142787361475340226491943209]) + with pytest.raises(ValueError): + np.random.default_rng(-1) + with pytest.raises(ValueError): + np.random.default_rng([12345, -1]) diff --git a/numpy/setup.py b/numpy/setup.py index 4ccdaeea5e94..a0ca99919b3a 100644 --- a/numpy/setup.py +++ b/numpy/setup.py @@ -1,11 +1,10 @@ -#!/usr/bin/env python -from __future__ import division, print_function - +#!/usr/bin/env python3 def configuration(parent_package='',top_path=None): from numpy.distutils.misc_util import Configuration config = Configuration('numpy', parent_package, top_path) + config.add_subpackage('array_api') config.add_subpackage('compat') config.add_subpackage('core') config.add_subpackage('distutils') @@ -19,8 +18,11 @@ def configuration(parent_package='',top_path=None): config.add_subpackage('polynomial') config.add_subpackage('random') config.add_subpackage('testing') + config.add_subpackage('typing') config.add_data_dir('doc') - config.add_data_dir('tests') + config.add_data_files('py.typed') + config.add_data_files('*.pyi') + config.add_subpackage('tests') config.make_config_py() # installs __config__.py return config diff --git a/numpy/testing/__init__.py b/numpy/testing/__init__.py index a7c85931cf60..6e06c5b49af1 100644 --- a/numpy/testing/__init__.py +++ b/numpy/testing/__init__.py @@ -5,18 +5,17 @@ away. """ -from __future__ import division, absolute_import, print_function - from unittest import TestCase from ._private.utils import * -from ._private import decorators as dec +from ._private.utils import (_assert_valid_refcount, _gen_alignment_data) +from ._private import extbuild, decorators as dec from ._private.nosetester import ( run_module_suite, NoseTester as Tester ) __all__ = _private.utils.__all__ + ['TestCase', 'run_module_suite'] -from ._private.pytesttester import PytestTester +from numpy._pytesttester import PytestTester test = PytestTester(__name__) del PytestTester diff --git a/numpy/testing/__init__.pyi b/numpy/testing/__init__.pyi new file mode 100644 index 000000000000..def0f9f58321 --- /dev/null +++ b/numpy/testing/__init__.pyi @@ -0,0 +1,58 @@ +from typing import List + +from numpy._pytesttester import PytestTester + +from unittest import ( + TestCase as TestCase, +) + +from numpy.testing._private.utils import ( + assert_equal as assert_equal, + assert_almost_equal as assert_almost_equal, + assert_approx_equal as assert_approx_equal, + assert_array_equal as assert_array_equal, + assert_array_less as assert_array_less, + assert_string_equal as assert_string_equal, + assert_array_almost_equal as assert_array_almost_equal, + assert_raises as assert_raises, + build_err_msg as build_err_msg, + decorate_methods as decorate_methods, + jiffies as jiffies, + memusage as memusage, + print_assert_equal as print_assert_equal, + raises as raises, + rundocs as rundocs, + runstring as runstring, + verbose as verbose, + measure as measure, + assert_ as assert_, + assert_array_almost_equal_nulp as assert_array_almost_equal_nulp, + assert_raises_regex as assert_raises_regex, + assert_array_max_ulp as assert_array_max_ulp, + assert_warns as assert_warns, + assert_no_warnings as assert_no_warnings, + assert_allclose as assert_allclose, + IgnoreException as IgnoreException, + clear_and_catch_warnings as clear_and_catch_warnings, + SkipTest as SkipTest, + KnownFailureException as KnownFailureException, + temppath as temppath, + tempdir as tempdir, + IS_PYPY as IS_PYPY, + IS_PYSTON as IS_PYSTON, + HAS_REFCOUNT as HAS_REFCOUNT, + suppress_warnings as suppress_warnings, + assert_array_compare as assert_array_compare, + assert_no_gc_cycles as assert_no_gc_cycles, + break_cycles as break_cycles, + HAS_LAPACK64 as HAS_LAPACK64, +) + +__all__: List[str] +__path__: List[str] +test: PytestTester + +def run_module_suite( + file_to_run: None | str = ..., + argv: None | List[str] = ..., +) -> None: ... diff --git a/numpy/testing/_private/decorators.py b/numpy/testing/_private/decorators.py index 24c4e385d1d1..cb49d9a73473 100644 --- a/numpy/testing/_private/decorators.py +++ b/numpy/testing/_private/decorators.py @@ -13,14 +13,8 @@ ``nose.tools`` for more information. """ -from __future__ import division, absolute_import, print_function - -try: - # Accessing collections abstract classes from collections - # has been deprecated since Python 3.3 - import collections.abc as collections_abc -except ImportError: - import collections as collections_abc +import collections.abc +import warnings from .utils import SkipTest, assert_warns, HAS_REFCOUNT @@ -30,6 +24,10 @@ def slow(t): """ + .. deprecated:: 1.21 + This decorator is retained for compatibility with the nose testing framework, which is being phased out. + Please use the nose2 or pytest frameworks instead. + Label a test as 'slow'. The exact definition of a slow test is obviously both subjective and @@ -59,12 +57,19 @@ def test_big(self): print('Big, slow test') """ + # Numpy 1.21, 2020-12-20 + warnings.warn('the np.testing.dec decorators are included for nose support, and are ' + 'deprecated since NumPy v1.21. Use the nose2 or pytest frameworks instead.', DeprecationWarning, stacklevel=2) t.slow = True return t def setastest(tf=True): """ + .. deprecated:: 1.21 + This decorator is retained for compatibility with the nose testing framework, which is being phased out. + Please use the nose2 or pytest frameworks instead. + Signals to nose that this function is or is not a test. Parameters @@ -91,6 +96,9 @@ def func_with_test_in_name(arg1, arg2): pass """ + # Numpy 1.21, 2020-12-20 + warnings.warn('the np.testing.dec decorators are included for nose support, and are ' + 'deprecated since NumPy v1.21. Use the nose2 or pytest frameworks instead.', DeprecationWarning, stacklevel=2) def set_test(t): t.__test__ = tf return t @@ -98,6 +106,10 @@ def set_test(t): def skipif(skip_condition, msg=None): """ + .. deprecated:: 1.21 + This decorator is retained for compatibility with the nose testing framework, which is being phased out. + Please use the nose2 or pytest frameworks instead. + Make function raise SkipTest exception if a given condition is true. If the condition is a callable, it is used at runtime to dynamically @@ -130,8 +142,12 @@ def skip_decorator(f): # import time overhead at actual test-time. import nose + # Numpy 1.21, 2020-12-20 + warnings.warn('the np.testing.dec decorators are included for nose support, and are ' + 'deprecated since NumPy v1.21. Use the nose2 or pytest frameworks instead.', DeprecationWarning, stacklevel=2) + # Allow for both boolean or callable skip conditions. - if isinstance(skip_condition, collections_abc.Callable): + if isinstance(skip_condition, collections.abc.Callable): skip_val = lambda: skip_condition() else: skip_val = lambda: skip_condition @@ -143,7 +159,7 @@ def get_msg(func,msg=None): else: out = msg - return "Skipping test: %s: %s" % (func.__name__, out) + return f'Skipping test: {func.__name__}: {out}' # We need to define *two* skippers because Python doesn't allow both # return with value and yield inside the same function. @@ -159,8 +175,7 @@ def skipper_gen(*args, **kwargs): if skip_val(): raise SkipTest(get_msg(f, msg)) else: - for x in f(*args, **kwargs): - yield x + yield from f(*args, **kwargs) # Choose the right skipper to use when building the actual decorator. if nose.util.isgenerator(f): @@ -175,6 +190,10 @@ def skipper_gen(*args, **kwargs): def knownfailureif(fail_condition, msg=None): """ + .. deprecated:: 1.21 + This decorator is retained for compatibility with the nose testing framework, which is being phased out. + Please use the nose2 or pytest frameworks instead. + Make function raise KnownFailureException exception if given condition is true. If the condition is a callable, it is used at runtime to dynamically @@ -203,11 +222,15 @@ def knownfailureif(fail_condition, msg=None): function in order to transmit function name, and various other metadata. """ + # Numpy 1.21, 2020-12-20 + warnings.warn('the np.testing.dec decorators are included for nose support, and are ' + 'deprecated since NumPy v1.21. Use the nose2 or pytest frameworks instead.', DeprecationWarning, stacklevel=2) + if msg is None: msg = 'Test skipped due to known failure' # Allow for both boolean or callable known failure conditions. - if isinstance(fail_condition, collections_abc.Callable): + if isinstance(fail_condition, collections.abc.Callable): fail_val = lambda: fail_condition() else: fail_val = lambda: fail_condition @@ -229,6 +252,10 @@ def knownfailer(*args, **kwargs): def deprecated(conditional=True): """ + .. deprecated:: 1.21 + This decorator is retained for compatibility with the nose testing framework, which is being phased out. + Please use the nose2 or pytest frameworks instead. + Filter deprecation warnings while running the test suite. This decorator can be used to filter DeprecationWarning's, to avoid @@ -257,12 +284,16 @@ def deprecate_decorator(f): # import time overhead at actual test-time. import nose + # Numpy 1.21, 2020-12-20 + warnings.warn('the np.testing.dec decorators are included for nose support, and are ' + 'deprecated since NumPy v1.21. Use the nose2 or pytest frameworks instead.', DeprecationWarning, stacklevel=2) + def _deprecated_imp(*args, **kwargs): # Poor man's replacement for the with statement with assert_warns(DeprecationWarning): f(*args, **kwargs) - if isinstance(conditional, collections_abc.Callable): + if isinstance(conditional, collections.abc.Callable): cond = conditional() else: cond = conditional @@ -275,6 +306,10 @@ def _deprecated_imp(*args, **kwargs): def parametrize(vars, input): """ + .. deprecated:: 1.21 + This decorator is retained for compatibility with the nose testing framework, which is being phased out. + Please use the nose2 or pytest frameworks instead. + Pytest compatibility class. This implements the simplest level of pytest.mark.parametrize for use in nose as an aid in making the transition to pytest. It achieves that by adding a dummy var parameter and ignoring @@ -287,6 +322,10 @@ def parametrize(vars, input): """ from .parameterized import parameterized + # Numpy 1.21, 2020-12-20 + warnings.warn('the np.testing.dec decorators are included for nose support, and are ' + 'deprecated since NumPy v1.21. Use the nose2 or pytest frameworks instead.', DeprecationWarning, stacklevel=2) + return parameterized(input) _needs_refcount = skipif(not HAS_REFCOUNT, "python has no sys.getrefcount") diff --git a/numpy/testing/_private/extbuild.py b/numpy/testing/_private/extbuild.py new file mode 100644 index 000000000000..20bf3dceac02 --- /dev/null +++ b/numpy/testing/_private/extbuild.py @@ -0,0 +1,251 @@ +""" +Build a c-extension module on-the-fly in tests. +See build_and_import_extensions for usage hints + +""" + +import os +import pathlib +import sys +import sysconfig +from numpy.distutils.ccompiler import new_compiler +from distutils.errors import CompileError + +__all__ = ['build_and_import_extension', 'compile_extension_module'] + + +def build_and_import_extension( + modname, functions, *, prologue="", build_dir=None, + include_dirs=[], more_init=""): + """ + Build and imports a c-extension module `modname` from a list of function + fragments `functions`. + + + Parameters + ---------- + functions : list of fragments + Each fragment is a sequence of func_name, calling convention, snippet. + prologue : string + Code to preceed the rest, usually extra ``#include`` or ``#define`` + macros. + build_dir : pathlib.Path + Where to build the module, usually a temporary directory + include_dirs : list + Extra directories to find include files when compiling + more_init : string + Code to appear in the module PyMODINIT_FUNC + + Returns + ------- + out: module + The module will have been loaded and is ready for use + + Examples + -------- + >>> functions = [("test_bytes", "METH_O", \"\"\" + if ( !PyBytesCheck(args)) { + Py_RETURN_FALSE; + } + Py_RETURN_TRUE; + \"\"\")] + >>> mod = build_and_import_extension("testme", functions) + >>> assert not mod.test_bytes(u'abc') + >>> assert mod.test_bytes(b'abc') + """ + + body = prologue + _make_methods(functions, modname) + init = """PyObject *mod = PyModule_Create(&moduledef); + """ + if not build_dir: + build_dir = pathlib.Path('.') + if more_init: + init += """#define INITERROR return NULL + """ + init += more_init + init += "\nreturn mod;" + source_string = _make_source(modname, init, body) + try: + mod_so = compile_extension_module( + modname, build_dir, include_dirs, source_string) + except CompileError as e: + # shorten the exception chain + raise RuntimeError(f"could not compile in {build_dir}:") from e + import importlib.util + spec = importlib.util.spec_from_file_location(modname, mod_so) + foo = importlib.util.module_from_spec(spec) + spec.loader.exec_module(foo) + return foo + + +def compile_extension_module( + name, builddir, include_dirs, + source_string, libraries=[], library_dirs=[]): + """ + Build an extension module and return the filename of the resulting + native code file. + + Parameters + ---------- + name : string + name of the module, possibly including dots if it is a module inside a + package. + builddir : pathlib.Path + Where to build the module, usually a temporary directory + include_dirs : list + Extra directories to find include files when compiling + libraries : list + Libraries to link into the extension module + library_dirs: list + Where to find the libraries, ``-L`` passed to the linker + """ + modname = name.split('.')[-1] + dirname = builddir / name + dirname.mkdir(exist_ok=True) + cfile = _convert_str_to_file(source_string, dirname) + include_dirs = [sysconfig.get_config_var('INCLUDEPY')] + include_dirs + + return _c_compile( + cfile, outputfilename=dirname / modname, + include_dirs=include_dirs, libraries=[], library_dirs=[], + ) + + +def _convert_str_to_file(source, dirname): + """Helper function to create a file ``source.c`` in `dirname` that contains + the string in `source`. Returns the file name + """ + filename = dirname / 'source.c' + with filename.open('w') as f: + f.write(str(source)) + return filename + + +def _make_methods(functions, modname): + """ Turns the name, signature, code in functions into complete functions + and lists them in a methods_table. Then turns the methods_table into a + ``PyMethodDef`` structure and returns the resulting code fragment ready + for compilation + """ + methods_table = [] + codes = [] + for funcname, flags, code in functions: + cfuncname = "%s_%s" % (modname, funcname) + if 'METH_KEYWORDS' in flags: + signature = '(PyObject *self, PyObject *args, PyObject *kwargs)' + else: + signature = '(PyObject *self, PyObject *args)' + methods_table.append( + "{\"%s\", (PyCFunction)%s, %s}," % (funcname, cfuncname, flags)) + func_code = """ + static PyObject* {cfuncname}{signature} + {{ + {code} + }} + """.format(cfuncname=cfuncname, signature=signature, code=code) + codes.append(func_code) + + body = "\n".join(codes) + """ + static PyMethodDef methods[] = { + %(methods)s + { NULL } + }; + static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "%(modname)s", /* m_name */ + NULL, /* m_doc */ + -1, /* m_size */ + methods, /* m_methods */ + }; + """ % dict(methods='\n'.join(methods_table), modname=modname) + return body + + +def _make_source(name, init, body): + """ Combines the code fragments into source code ready to be compiled + """ + code = """ + #include <Python.h> + + %(body)s + + PyMODINIT_FUNC + PyInit_%(name)s(void) { + %(init)s + } + """ % dict( + name=name, init=init, body=body, + ) + return code + + +def _c_compile(cfile, outputfilename, include_dirs=[], libraries=[], + library_dirs=[]): + if sys.platform == 'win32': + compile_extra = ["/we4013"] + link_extra = ["/LIBPATH:" + os.path.join(sys.base_prefix, 'libs')] + elif sys.platform.startswith('linux'): + compile_extra = [ + "-O0", "-g", "-Werror=implicit-function-declaration", "-fPIC"] + link_extra = None + else: + compile_extra = link_extra = None + pass + if sys.platform == 'win32': + link_extra = link_extra + ['/DEBUG'] # generate .pdb file + if sys.platform == 'darwin': + # support Fink & Darwinports + for s in ('/sw/', '/opt/local/'): + if (s + 'include' not in include_dirs + and os.path.exists(s + 'include')): + include_dirs.append(s + 'include') + if s + 'lib' not in library_dirs and os.path.exists(s + 'lib'): + library_dirs.append(s + 'lib') + + outputfilename = outputfilename.with_suffix(get_so_suffix()) + saved_environ = os.environ.copy() + try: + build( + cfile, outputfilename, + compile_extra, link_extra, + include_dirs, libraries, library_dirs) + finally: + # workaround for a distutils bugs where some env vars can + # become longer and longer every time it is used + for key, value in saved_environ.items(): + if os.environ.get(key) != value: + os.environ[key] = value + return outputfilename + + +def build(cfile, outputfilename, compile_extra, link_extra, + include_dirs, libraries, library_dirs): + "cd into the directory where the cfile is, use distutils to build" + + compiler = new_compiler(force=1, verbose=2) + compiler.customize('') + objects = [] + + old = os.getcwd() + os.chdir(cfile.parent) + try: + res = compiler.compile( + [str(cfile.name)], + include_dirs=include_dirs, + extra_preargs=compile_extra + ) + objects += [str(cfile.parent / r) for r in res] + finally: + os.chdir(old) + + compiler.link_shared_object( + objects, str(outputfilename), + libraries=libraries, + extra_preargs=link_extra, + library_dirs=library_dirs) + + +def get_so_suffix(): + ret = sysconfig.get_config_var('EXT_SUFFIX') + assert ret + return ret diff --git a/numpy/testing/_private/noseclasses.py b/numpy/testing/_private/noseclasses.py index 08dec0ca98e9..48fa4dc1f5af 100644 --- a/numpy/testing/_private/noseclasses.py +++ b/numpy/testing/_private/noseclasses.py @@ -4,8 +4,6 @@ # Because this module imports nose directly, it should not # be used except by nosetester.py to avoid a general NumPy # dependency on nose. -from __future__ import division, absolute_import, print_function - import os import sys import doctest @@ -26,7 +24,7 @@ #----------------------------------------------------------------------------- # Modified version of the one in the stdlib, that fixes a python bug (doctests -# not found in extension modules, http://bugs.python.org/issue3158) +# not found in extension modules, https://bugs.python.org/issue3158) class NumpyDocTestFinder(doctest.DocTestFinder): def _from_module(self, module, object): @@ -78,7 +76,7 @@ def _find(self, tests, obj, name, module, source_lines, globs, seen): # Look for tests in a module's contained objects. if ismodule(obj) and self._recurse: for valname, val in obj.__dict__.items(): - valname1 = '%s.%s' % (name, valname) + valname1 = f'{name}.{valname}' if ( (isroutine(val) or isclass(val)) and self._from_module(module, val)): @@ -98,7 +96,7 @@ def _find(self, tests, obj, name, module, source_lines, globs, seen): if ((isfunction(val) or isclass(val) or ismethod(val) or isinstance(val, property)) and self._from_module(module, val)): - valname = '%s.%s' % (name, valname) + valname = f'{name}.{valname}' self._find(tests, val, valname, module, source_lines, globs, seen) @@ -212,7 +210,7 @@ def set_test_context(self, test): # starting Python and executing "import numpy as np", and, # for SciPy packages, an additional import of the local # package (so that scipy.linalg.basic.py's doctests have an - # implicit "from scipy import linalg" as well. + # implicit "from scipy import linalg" as well). # # Note: __file__ allows the doctest in NoseTester to run # without producing an error @@ -268,7 +266,7 @@ def wantFile(self, file): return npd.Doctest.wantFile(self, file) -class Unplugger(object): +class Unplugger: """ Nose plugin to remove named plugin late in loading By default it removes the "doctest" plugin. diff --git a/numpy/testing/_private/nosetester.py b/numpy/testing/_private/nosetester.py index c2cf58377083..bccec8236912 100644 --- a/numpy/testing/_private/nosetester.py +++ b/numpy/testing/_private/nosetester.py @@ -4,12 +4,9 @@ This module implements ``test()`` and ``bench()`` functions for NumPy modules. """ -from __future__ import division, absolute_import, print_function - import os import sys import warnings -from numpy.compat import basestring import numpy as np from .utils import import_nose, suppress_warnings @@ -92,7 +89,7 @@ def run_module_suite(file_to_run=None, argv=None): Alternatively, calling:: - >>> run_module_suite(file_to_run="numpy/tests/test_matlib.py") + >>> run_module_suite(file_to_run="numpy/tests/test_matlib.py") # doctest: +SKIP from an interpreter will run all the test routine in 'test_matlib.py'. """ @@ -112,7 +109,7 @@ def run_module_suite(file_to_run=None, argv=None): nose.run(argv=argv, addplugins=[KnownFailurePlugin()]) -class NoseTester(object): +class NoseTester: """ Nose test runner. @@ -214,7 +211,7 @@ def _test_argv(self, label, verbose, extra_argv): ''' argv = [__file__, self.package_path, '-s'] if label and label != 'full': - if not isinstance(label, basestring): + if not isinstance(label, str): raise TypeError('Selection label should be a string') if label == 'fast': label = 'not slow' @@ -236,20 +233,20 @@ def _show_system_info(self): nose = import_nose() import numpy - print("NumPy version %s" % numpy.__version__) + print(f'NumPy version {numpy.__version__}') relaxed_strides = numpy.ones((10, 1), order="C").flags.f_contiguous print("NumPy relaxed strides checking option:", relaxed_strides) npdir = os.path.dirname(numpy.__file__) - print("NumPy is installed in %s" % npdir) + print(f'NumPy is installed in {npdir}') if 'scipy' in self.package_name: import scipy - print("SciPy version %s" % scipy.__version__) + print(f'SciPy version {scipy.__version__}') spdir = os.path.dirname(scipy.__file__) - print("SciPy is installed in %s" % spdir) + print(f'SciPy is installed in {spdir}') pyversion = sys.version.replace('\n', '') - print("Python version %s" % pyversion) + print(f'Python version {pyversion}') print("nose version %d.%d.%d" % nose.__versioninfo__) def _get_custom_doctester(self): @@ -281,7 +278,7 @@ def prepare_test_args(self, label='fast', verbose=1, extra_argv=None, argv = self._test_argv(label, verbose, extra_argv) # our way of doing coverage if coverage: - argv += ['--cover-package=%s' % self.package_name, '--with-coverage', + argv += [f'--cover-package={self.package_name}', '--with-coverage', '--cover-tests', '--cover-erase'] if timer: @@ -338,12 +335,14 @@ def test(self, label='fast', verbose=1, extra_argv=None, Identifies the tests to run. This can be a string to pass to the nosetests executable with the '-A' option, or one of several special values. Special values are: + * 'fast' - the default - which corresponds to the ``nosetests -A`` option of 'not slow'. * 'full' - fast (as above) and slow tests as in the 'no -A' option to nosetests - this is the same as ''. * None or '' - run all tests. - attribute_identifier - string passed directly to nosetests as '-A'. + * attribute_identifier - string passed directly to nosetests as '-A'. + verbose : int, optional Verbosity value for test outputs, in the range 1-10. Default is 1. extra_argv : list, optional @@ -352,16 +351,14 @@ def test(self, label='fast', verbose=1, extra_argv=None, If True, run doctests in module. Default is False. coverage : bool, optional If True, report coverage of NumPy code. Default is False. - (This requires the `coverage module: - <http://nedbatchelder.com/code/modules/coverage.html>`_). + (This requires the + `coverage module <https://pypi.org/project/coverage/>`_). raise_warnings : None, str or sequence of warnings, optional This specifies which warnings to configure as 'raise' instead - of being shown once during the test execution. Valid strings are: + of being shown once during the test execution. Valid strings are: - - "develop" : equals ``(Warning,)`` - - "release" : equals ``()``, don't raise on any warnings. - - The default is to use the class initialization value. + * "develop" : equals ``(Warning,)`` + * "release" : equals ``()``, do not raise on any warnings. timer : bool or int, optional Timing of individual tests with ``nose-timer`` (which needs to be installed). If True, time tests and report on all of them. @@ -406,9 +403,9 @@ def test(self, label='fast', verbose=1, extra_argv=None, label, verbose, extra_argv, doctests, coverage, timer) if doctests: - print("Running unit tests and doctests for %s" % self.package_name) + print(f'Running unit tests and doctests for {self.package_name}') else: - print("Running unit tests for %s" % self.package_name) + print(f'Running unit tests for {self.package_name}') self._show_system_info() @@ -421,7 +418,7 @@ def test(self, label='fast', verbose=1, extra_argv=None, _warn_opts = dict(develop=(Warning,), release=()) - if isinstance(raise_warnings, basestring): + if isinstance(raise_warnings, str): raise_warnings = _warn_opts[raise_warnings] with suppress_warnings("location") as sup: @@ -450,20 +447,6 @@ def test(self, label='fast', verbose=1, extra_argv=None, warnings.simplefilter("always") from ...distutils import cpuinfo sup.filter(category=UserWarning, module=cpuinfo) - # See #7949: Filter out deprecation warnings due to the -3 flag to - # python 2 - if sys.version_info.major == 2 and sys.py3kwarning: - # This is very specific, so using the fragile module filter - # is fine - import threading - sup.filter(DeprecationWarning, - r"sys\.exc_clear\(\) not supported in 3\.x", - module=threading) - sup.filter(DeprecationWarning, message=r"in 3\.x, __setslice__") - sup.filter(DeprecationWarning, message=r"in 3\.x, __getslice__") - sup.filter(DeprecationWarning, message=r"buffer\(\) not supported in 3\.x") - sup.filter(DeprecationWarning, message=r"CObject type is not supported in 3\.x") - sup.filter(DeprecationWarning, message=r"comparing unequal types not supported in 3\.x") # Filter out some deprecation warnings inside nose 1.3.7 when run # on python 3.5b2. See # https://github.com/nose-devs/nose/issues/929 @@ -489,12 +472,14 @@ def bench(self, label='fast', verbose=1, extra_argv=None): Identifies the benchmarks to run. This can be a string to pass to the nosetests executable with the '-A' option, or one of several special values. Special values are: + * 'fast' - the default - which corresponds to the ``nosetests -A`` option of 'not slow'. * 'full' - fast (as above) and slow benchmarks as in the 'no -A' option to nosetests - this is the same as ''. * None or '' - run all tests. - attribute_identifier - string passed directly to nosetests as '-A'. + * attribute_identifier - string passed directly to nosetests as '-A'. + verbose : int, optional Verbosity value for benchmark outputs, in the range 1-10. Default is 1. extra_argv : list, optional @@ -535,7 +520,7 @@ def bench(self, label='fast', verbose=1, extra_argv=None): """ - print("Running benchmarks for %s" % self.package_name) + print(f'Running benchmarks for {self.package_name}') self._show_system_info() argv = self._test_argv(label, verbose, extra_argv) diff --git a/numpy/testing/_private/parameterized.py b/numpy/testing/_private/parameterized.py index 53e67517dd5a..db9629a94680 100644 --- a/numpy/testing/_private/parameterized.py +++ b/numpy/testing/_private/parameterized.py @@ -31,45 +31,14 @@ """ import re -import sys import inspect import warnings from functools import wraps -from types import MethodType as MethodType +from types import MethodType from collections import namedtuple -try: - from collections import OrderedDict as MaybeOrderedDict -except ImportError: - MaybeOrderedDict = dict - from unittest import TestCase -PY3 = sys.version_info[0] == 3 -PY2 = sys.version_info[0] == 2 - - -if PY3: - # Python 3 doesn't have an InstanceType, so just use a dummy type. - class InstanceType(): - pass - lzip = lambda *a: list(zip(*a)) - text_type = str - string_types = str, - bytes_type = bytes - def make_method(func, instance, type): - if instance is None: - return func - return MethodType(func, instance) -else: - from types import InstanceType - lzip = zip - text_type = unicode - bytes_type = str - string_types = basestring, - def make_method(func, instance, type): - return MethodType(func, instance, type) - _param = namedtuple("param", "args kwargs") class param(_param): @@ -123,7 +92,7 @@ def from_decorator(cls, args): """ if isinstance(args, param): return args - elif isinstance(args, string_types): + elif isinstance(args, (str,)): args = (args, ) try: return cls(*args) @@ -139,13 +108,6 @@ def __repr__(self): return "param(*%r, **%r)" %self -class QuietOrderedDict(MaybeOrderedDict): - """ When OrderedDict is available, use it to make sure that the kwargs in - doc strings are consistently ordered. """ - __str__ = dict.__str__ - __repr__ = dict.__repr__ - - def parameterized_argument_value_pairs(func, p): """Return tuples of parameterized arguments and their values. @@ -180,7 +142,7 @@ def parameterized_argument_value_pairs(func, p): named_args = argspec.args[arg_offset:] - result = lzip(named_args, p.args) + result = list(zip(named_args, p.args)) named_args = argspec.args[len(result) + arg_offset:] varargs = p.args[len(result):] @@ -190,8 +152,8 @@ def parameterized_argument_value_pairs(func, p): in zip(named_args, argspec.defaults or []) ]) - seen_arg_names = set([ n for (n, _) in result ]) - keywords = QuietOrderedDict(sorted([ + seen_arg_names = {n for (n, _) in result} + keywords = dict(sorted([ (name, p.kwargs[name]) for name in p.kwargs if name not in seen_arg_names @@ -215,11 +177,11 @@ def short_repr(x, n=64): """ x_repr = repr(x) - if isinstance(x_repr, bytes_type): + if isinstance(x_repr, bytes): try: - x_repr = text_type(x_repr, "utf-8") + x_repr = str(x_repr, "utf-8") except UnicodeDecodeError: - x_repr = text_type(x_repr, "latin1") + x_repr = str(x_repr, "latin1") if len(x_repr) > n: x_repr = x_repr[:n//2] + "..." + x_repr[len(x_repr) - n//2:] return x_repr @@ -231,7 +193,7 @@ def default_doc_func(func, num, p): all_args_with_values = parameterized_argument_value_pairs(func, p) # Assumes that the function passed is a bound method. - descs = ["%s=%s" %(n, short_repr(v)) for n, v in all_args_with_values] + descs = [f'{n}={short_repr(v)}' for n, v in all_args_with_values] # The documentation might be a multiline string, so split it # and just work with the first string, ignoring the period @@ -247,7 +209,7 @@ def default_doc_func(func, num, p): def default_name_func(func, num, p): base_name = func.__name__ name_suffix = "_%s" %(num, ) - if len(p.args) > 0 and isinstance(p.args[0], string_types): + if len(p.args) > 0 and isinstance(p.args[0], (str,)): name_suffix += "_" + parameterized.to_safe_name(p.args[0]) return base_name + name_suffix @@ -272,7 +234,7 @@ def set_test_runner(name): def detect_runner(): """ Guess which test runner we're using by traversing the stack and looking for the first matching module. This *should* be reasonably safe, as - it's done during test disocvery where the test runner should be the + it's done during test discovery where the test runner should be the stack frame immediately outside. """ if _test_runner_override is not None: return _test_runner_override @@ -287,17 +249,14 @@ def detect_runner(): if module in _test_runners: _test_runner_guess = module break - if record[1].endswith("python2.6/unittest.py"): - _test_runner_guess = "unittest" - break else: _test_runner_guess = None return _test_runner_guess -class parameterized(object): +class parameterized: """ Parameterize a test case:: - class TestInt(object): + class TestInt: @parameterized([ ("A", 10), ("F", 15), @@ -325,15 +284,6 @@ def __call__(self, test_func): @wraps(test_func) def wrapper(test_self=None): test_cls = test_self and type(test_self) - if test_self is not None: - if issubclass(test_cls, InstanceType): - raise TypeError(( - "@parameterized can't be used with old-style classes, but " - "%r has an old-style class. Consider using a new-style " - "class, or '@parameterized.expand' " - "(see http://stackoverflow.com/q/54867/71522 for more " - "information on old-style classes)." - ) %(test_self, )) original_doc = wrapper.__doc__ for num, args in enumerate(wrapper.parameterized_input): @@ -366,15 +316,7 @@ def param_as_nose_tuple(self, test_self, func, num, p): # Python 3 doesn't let us pull the function out of a bound method. unbound_func = nose_func if test_self is not None: - # Under nose on Py2 we need to return an unbound method to make - # sure that the `self` in the method is properly shared with the - # `self` used in `setUp` and `tearDown`. But only there. Everyone - # else needs a bound method. - func_self = ( - None if PY2 and detect_runner() == "nose" else - test_self - ) - nose_func = make_method(nose_func, func_self, type(test_self)) + nose_func = MethodType(nose_func, test_self) return unbound_func, (nose_func, ) + p.args + (p.kwargs or {}, ) def assert_not_in_testcase_subclass(self): @@ -385,7 +327,7 @@ def assert_not_in_testcase_subclass(self): "'@parameterized.expand' instead.") def _terrible_magic_get_defining_classes(self): - """ Returns the set of parent classes of the class currently being defined. + """ Returns the list of parent classes of the class currently being defined. Will likely only work if called from the ``parameterized`` decorator. This function is entirely @brandon_rhodes's fault, as he suggested the implementation: http://stackoverflow.com/a/8793684/71522 diff --git a/numpy/testing/_private/utils.py b/numpy/testing/_private/utils.py index 032c4a116e93..0eb945d15cc7 100644 --- a/numpy/testing/_private/utils.py +++ b/numpy/testing/_private/utils.py @@ -2,10 +2,9 @@ Utility function to facilitate testing. """ -from __future__ import division, absolute_import, print_function - import os import sys +import platform import re import gc import operator @@ -18,27 +17,25 @@ from warnings import WarningMessage import pprint +import numpy as np from numpy.core import( - float32, empty, arange, array_repr, ndarray, isnat, array) -from numpy.lib.utils import deprecate + intp, float32, empty, arange, array_repr, ndarray, isnat, array) +import numpy.linalg.lapack_lite -if sys.version_info[0] >= 3: - from io import StringIO -else: - from StringIO import StringIO +from io import StringIO __all__ = [ 'assert_equal', 'assert_almost_equal', 'assert_approx_equal', 'assert_array_equal', 'assert_array_less', 'assert_string_equal', 'assert_array_almost_equal', 'assert_raises', 'build_err_msg', 'decorate_methods', 'jiffies', 'memusage', 'print_assert_equal', - 'raises', 'rand', 'rundocs', 'runstring', 'verbose', 'measure', + 'raises', 'rundocs', 'runstring', 'verbose', 'measure', 'assert_', 'assert_array_almost_equal_nulp', 'assert_raises_regex', 'assert_array_max_ulp', 'assert_warns', 'assert_no_warnings', 'assert_allclose', 'IgnoreException', 'clear_and_catch_warnings', 'SkipTest', 'KnownFailureException', 'temppath', 'tempdir', 'IS_PYPY', 'HAS_REFCOUNT', 'suppress_warnings', 'assert_array_compare', - '_assert_valid_refcount', '_gen_alignment_data', 'assert_no_gc_cycles', + 'assert_no_gc_cycles', 'break_cycles', 'HAS_LAPACK64', 'IS_PYSTON', ] @@ -50,8 +47,10 @@ class KnownFailureException(Exception): KnownFailureTest = KnownFailureException # backwards compat verbose = 0 -IS_PYPY = '__pypy__' in sys.modules -HAS_REFCOUNT = getattr(sys, 'getrefcount', None) is not None +IS_PYPY = platform.python_implementation() == 'PyPy' +IS_PYSTON = hasattr(sys, "pyston_version_info") +HAS_REFCOUNT = getattr(sys, 'getrefcount', None) is not None and not IS_PYSTON +HAS_LAPACK64 = numpy.linalg.lapack_lite._ilp64 def import_nose(): @@ -69,7 +68,7 @@ def import_nose(): if not nose_is_good: msg = ('Need nose >= %d.%d.%d for tests - see ' - 'http://nose.readthedocs.io' % + 'https://nose.readthedocs.io' % minimum_nose_version) raise ImportError(msg) @@ -115,14 +114,14 @@ def gisnan(x): def gisfinite(x): - """like isfinite, but always raise an error if type not supported instead of - returning a TypeError object. + """like isfinite, but always raise an error if type not supported instead + of returning a TypeError object. Notes ----- - isfinite and other ufunc sometimes return a NotImplementedType object instead - of raising any exception. This function is a wrapper to make sure an - exception is always raised. + isfinite and other ufunc sometimes return a NotImplementedType object + instead of raising any exception. This function is a wrapper to make sure + an exception is always raised. This should be removed once this problem is solved at the Ufunc level.""" from numpy.core import isfinite, errstate @@ -152,22 +151,6 @@ def gisinf(x): return st -@deprecate(message="numpy.testing.rand is deprecated in numpy 1.11. " - "Use numpy.random.rand instead.") -def rand(*args): - """Returns an array of random numbers with the given shape. - - This only uses the standard library, so it is useful for testing purposes. - """ - import random - from numpy.core import zeros, float64 - results = zeros(args, float64) - f = results.flat - for i in range(len(f)): - f[i] = random.random() - return results - - if os.name == 'nt': # Code "stolen" from enthought/debug/memusage.py def GetPerformanceAttributes(object, counter, instance=None, @@ -177,13 +160,14 @@ def GetPerformanceAttributes(object, counter, instance=None, # thread's CPU usage is either 0 or 100). To read counters like this, # you should copy this function, but keep the counter open, and call # CollectQueryData() each time you need to know. - # See http://msdn.microsoft.com/library/en-us/dnperfmo/html/perfmonpt2.asp - # My older explanation for this was that the "AddCounter" process forced - # the CPU to 100%, but the above makes more sense :) + # See http://msdn.microsoft.com/library/en-us/dnperfmo/html/perfmonpt2.asp (dead link) + # My older explanation for this was that the "AddCounter" process + # forced the CPU to 100%, but the above makes more sense :) import win32pdh if format is None: format = win32pdh.PDH_FMT_LONG - path = win32pdh.MakeCounterPath( (machine, object, instance, None, inum, counter)) + path = win32pdh.MakeCounterPath( (machine, object, instance, None, + inum, counter)) hq = win32pdh.OpenQuery() try: hc = win32pdh.AddCounter(hq, path) @@ -204,15 +188,14 @@ def memusage(processName="python", instance=0): win32pdh.PDH_FMT_LONG, None) elif sys.platform[:5] == 'linux': - def memusage(_proc_pid_stat='/proc/%s/stat' % (os.getpid())): + def memusage(_proc_pid_stat=f'/proc/{os.getpid()}/stat'): """ Return virtual memory size in bytes of the running python. """ try: - f = open(_proc_pid_stat, 'r') - l = f.readline().split(' ') - f.close() + with open(_proc_pid_stat, 'r') as f: + l = f.readline().split(' ') return int(l[22]) except Exception: return @@ -226,8 +209,7 @@ def memusage(): if sys.platform[:5] == 'linux': - def jiffies(_proc_pid_stat='/proc/%s/stat' % (os.getpid()), - _load_time=[]): + def jiffies(_proc_pid_stat=f'/proc/{os.getpid()}/stat', _load_time=[]): """ Return number of jiffies elapsed. @@ -239,9 +221,8 @@ def jiffies(_proc_pid_stat='/proc/%s/stat' % (os.getpid()), if not _load_time: _load_time.append(time.time()) try: - f = open(_proc_pid_stat, 'r') - l = f.readline().split(' ') - f.close() + with open(_proc_pid_stat, 'r') as f: + l = f.readline().split(' ') return int(l[13]) except Exception: return int(100*(time.time()-_load_time[0])) @@ -283,11 +264,11 @@ def build_err_msg(arrays, err_msg, header='Items are not equal:', try: r = r_func(a) except Exception as exc: - r = '[repr failed for <{}>: {}]'.format(type(a).__name__, exc) + r = f'[repr failed for <{type(a).__name__}>: {exc}]' if r.count('\n') > 3: r = '\n'.join(r.splitlines()[:3]) r += '...' - msg.append(' %s: %s' % (names[i], r)) + msg.append(f' {names[i]}: {r}') return '\n'.join(msg) @@ -299,6 +280,15 @@ def assert_equal(actual, desired, err_msg='', verbose=True): check that all elements of these objects are equal. An exception is raised at the first conflicting values. + When one of `actual` and `desired` is a scalar and the other is array_like, + the function checks that each element of the array_like object is equal to + the scalar. + + This function handles NaN comparisons as if NaN was a "normal" number. + That is, AssertionError is not raised if both objects have NaNs in the same + positions. This is in contrast to the IEEE standard on NaNs, which says + that NaN compared to anything must return False. + Parameters ---------- actual : array_like @@ -318,13 +308,19 @@ def assert_equal(actual, desired, err_msg='', verbose=True): Examples -------- >>> np.testing.assert_equal([4,5], [4,6]) - ... - <type 'exceptions.AssertionError'>: + Traceback (most recent call last): + ... + AssertionError: Items are not equal: item=1 ACTUAL: 5 DESIRED: 6 + The following comparison does not raise an exception. There are NaNs + in the inputs, but they are in the same positions. + + >>> np.testing.assert_equal(np.array([1.0, 2.0, np.nan]), [1, 2, np.nan]) + """ __tracebackhide__ = True # Hide traceback for py.test if isinstance(desired, dict): @@ -334,12 +330,14 @@ def assert_equal(actual, desired, err_msg='', verbose=True): for k, i in desired.items(): if k not in actual: raise AssertionError(repr(k)) - assert_equal(actual[k], desired[k], 'key=%r\n%s' % (k, err_msg), verbose) + assert_equal(actual[k], desired[k], f'key={k!r}\n{err_msg}', + verbose) return if isinstance(desired, (list, tuple)) and isinstance(actual, (list, tuple)): assert_equal(len(actual), len(desired), err_msg, verbose) for k in range(len(desired)): - assert_equal(actual[k], desired[k], 'item=%r\n%s' % (k, err_msg), verbose) + assert_equal(actual[k], desired[k], f'item={k!r}\n{err_msg}', + verbose) return from numpy.core import ndarray, isscalar, signbit from numpy.lib import iscomplexobj, real, imag @@ -352,7 +350,7 @@ def assert_equal(actual, desired, err_msg='', verbose=True): # XXX: catch ValueError for subclasses of ndarray where iscomplex fail try: usecomplex = iscomplexobj(actual) or iscomplexobj(desired) - except ValueError: + except (ValueError, TypeError): usecomplex = False if usecomplex: @@ -378,6 +376,22 @@ def assert_equal(actual, desired, err_msg='', verbose=True): if isscalar(desired) != isscalar(actual): raise AssertionError(msg) + try: + isdesnat = isnat(desired) + isactnat = isnat(actual) + dtypes_match = (np.asarray(desired).dtype.type == + np.asarray(actual).dtype.type) + if isdesnat and isactnat: + # If both are NaT (and have the same dtype -- datetime or + # timedelta) they are considered equal. + if dtypes_match: + return + else: + raise AssertionError(msg) + + except (TypeError, ValueError, NotImplementedError): + pass + # Inf/nan/negative zero handling try: isdesnan = gisnan(desired) @@ -386,6 +400,18 @@ def assert_equal(actual, desired, err_msg='', verbose=True): return # both nan, so equal # handle signed zero specially for floats + array_actual = np.asarray(actual) + array_desired = np.asarray(desired) + if (array_actual.dtype.char in 'Mm' or + array_desired.dtype.char in 'Mm'): + # version 1.18 + # until this version, gisnan failed for datetime64 and timedelta64. + # Now it succeeds but comparison to scalar with a different type + # emits a DeprecationWarning. + # Avoid that by skipping the next check + raise NotImplementedError('cannot compare to a scalar ' + 'with a different type') + if desired == 0 and actual == 0: if not signbit(desired) == signbit(actual): raise AssertionError(msg) @@ -393,21 +419,6 @@ def assert_equal(actual, desired, err_msg='', verbose=True): except (TypeError, ValueError, NotImplementedError): pass - try: - isdesnat = isnat(desired) - isactnat = isnat(actual) - dtypes_match = array(desired).dtype.type == array(actual).dtype.type - if isdesnat and isactnat: - # If both are NaT (and have the same dtype -- datetime or - # timedelta) they are considered equal. - if dtypes_match: - return - else: - raise AssertionError(msg) - - except (TypeError, ValueError, NotImplementedError): - pass - try: # Explicitly use __eq__ for comparison, gh-2552 if not (desired == actual): @@ -472,7 +483,7 @@ def assert_almost_equal(actual,desired,decimal=7,err_msg='',verbose=True): instead of this function for more consistent floating point comparisons. - The test verifies that the elements of ``actual`` and ``desired`` satisfy. + The test verifies that the elements of `actual` and `desired` satisfy. ``abs(desired-actual) < 1.5 * 10**(-decimal)`` @@ -507,24 +518,28 @@ def assert_almost_equal(actual,desired,decimal=7,err_msg='',verbose=True): Examples -------- - >>> import numpy.testing as npt - >>> npt.assert_almost_equal(2.3333333333333, 2.33333334) - >>> npt.assert_almost_equal(2.3333333333333, 2.33333334, decimal=10) - ... - <type 'exceptions.AssertionError'>: - Items are not equal: - ACTUAL: 2.3333333333333002 - DESIRED: 2.3333333399999998 + >>> from numpy.testing import assert_almost_equal + >>> assert_almost_equal(2.3333333333333, 2.33333334) + >>> assert_almost_equal(2.3333333333333, 2.33333334, decimal=10) + Traceback (most recent call last): + ... + AssertionError: + Arrays are not almost equal to 10 decimals + ACTUAL: 2.3333333333333 + DESIRED: 2.33333334 - >>> npt.assert_almost_equal(np.array([1.0,2.3333333333333]), - ... np.array([1.0,2.33333334]), decimal=9) - ... - <type 'exceptions.AssertionError'>: - Arrays are not almost equal + >>> assert_almost_equal(np.array([1.0,2.3333333333333]), + ... np.array([1.0,2.33333334]), decimal=9) + Traceback (most recent call last): + ... + AssertionError: + Arrays are not almost equal to 9 decimals <BLANKLINE> - (mismatch 50.0%) - x: array([ 1. , 2.33333333]) - y: array([ 1. , 2.33333334]) + Mismatched elements: 1 / 2 (50%) + Max absolute difference: 6.66669964e-09 + Max relative difference: 2.85715698e-09 + x: array([1. , 2.333333333]) + y: array([1. , 2.33333334]) """ __tracebackhide__ = True # Hide traceback for py.test @@ -626,14 +641,15 @@ def assert_approx_equal(actual,desired,significant=7,err_msg='',verbose=True): -------- >>> np.testing.assert_approx_equal(0.12345677777777e-20, 0.1234567e-20) >>> np.testing.assert_approx_equal(0.12345670e-20, 0.12345671e-20, - significant=8) + ... significant=8) >>> np.testing.assert_approx_equal(0.12345670e-20, 0.12345672e-20, - significant=8) - ... - <type 'exceptions.AssertionError'>: + ... significant=8) + Traceback (most recent call last): + ... + AssertionError: Items are not equal to 8 significant digits: - ACTUAL: 1.234567e-021 - DESIRED: 1.2345672000000001e-021 + ACTUAL: 1.234567e-21 + DESIRED: 1.2345672e-21 the evaluated condition that raises the exception is @@ -660,10 +676,10 @@ def assert_approx_equal(actual,desired,significant=7,err_msg='',verbose=True): sc_actual = actual/scale except ZeroDivisionError: sc_actual = 0.0 - msg = build_err_msg([actual, desired], err_msg, - header='Items are not equal to %d significant digits:' % - significant, - verbose=verbose) + msg = build_err_msg( + [actual, desired], err_msg, + header='Items are not equal to %d significant digits:' % significant, + verbose=verbose) try: # If one of desired/actual is not finite, handle it specially here: # check that both are nan if any is a nan, and test for equality @@ -682,13 +698,16 @@ def assert_approx_equal(actual,desired,significant=7,err_msg='',verbose=True): raise AssertionError(msg) -def assert_array_compare(comparison, x, y, err_msg='', verbose=True, - header='', precision=6, equal_nan=True, - equal_inf=True): +def assert_array_compare(comparison, x, y, err_msg='', verbose=True, header='', + precision=6, equal_nan=True, equal_inf=True): __tracebackhide__ = True # Hide traceback for py.test - from numpy.core import array, isnan, inf, bool_ - x = array(x, copy=False, subok=True) - y = array(y, copy=False, subok=True) + from numpy.core import array, array2string, isnan, inf, bool_, errstate, all, max, object_ + + x = np.asanyarray(x) + y = np.asanyarray(y) + + # original array for output formatting + ox, oy = x, y def isnumber(x): return x.dtype.char in '?bhilqpBHILQPefdgFDG' @@ -697,14 +716,28 @@ def istime(x): return x.dtype.char in "Mm" def func_assert_same_pos(x, y, func=isnan, hasval='nan'): - """Handling nan/inf: combine results of running func on x and y, - checking that they are True at the same locations.""" - # Both the != True comparison here and the cast to bool_ at - # the end are done to deal with `masked`, which cannot be - # compared usefully, and for which .all() yields masked. + """Handling nan/inf. + + Combine results of running func on x and y, checking that they are True + at the same locations. + + """ + __tracebackhide__ = True # Hide traceback for py.test + x_id = func(x) y_id = func(y) - if (x_id == y_id).all() != True: + # We include work-arounds here to handle three types of slightly + # pathological ndarray subclasses: + # (1) all() on `masked` array scalars can return masked arrays, so we + # use != True + # (2) __eq__ on some ndarray subclasses returns Python booleans + # instead of element-wise comparisons, so we cast to bool_() and + # use isinstance(..., bool) checks + # (3) subclasses with bare-bones __array_function__ implementations may + # not implement np.all(), so favor using the .all() method + # We are not committed to supporting such subclasses, but it's nice to + # support them if possible. + if bool_(x_id == y_id).all() != True: msg = build_err_msg([x, y], err_msg + '\nx and y %s location mismatch:' % (hasval), verbose=verbose, header=header, @@ -712,9 +745,9 @@ def func_assert_same_pos(x, y, func=isnan, hasval='nan'): raise AssertionError(msg) # If there is a scalar, then here we know the array has the same # flag as it everywhere, so we should return the scalar flag. - if x_id.ndim == 0: + if isinstance(x_id, bool) or x_id.ndim == 0: return bool_(x_id) - elif y_id.ndim == 0: + elif isinstance(y_id, bool) or y_id.ndim == 0: return bool_(y_id) else: return y_id @@ -724,8 +757,7 @@ def func_assert_same_pos(x, y, func=isnan, hasval='nan'): if not cond: msg = build_err_msg([x, y], err_msg - + '\n(shapes %s, %s mismatch)' % (x.shape, - y.shape), + + f'\n(shapes {x.shape}, {y.shape} mismatch)', verbose=verbose, header=header, names=('x', 'y'), precision=precision) raise AssertionError(msg) @@ -761,27 +793,59 @@ def func_assert_same_pos(x, y, func=isnan, hasval='nan'): if isinstance(val, bool): cond = val - reduced = [0] + reduced = array([val]) else: reduced = val.ravel() cond = reduced.all() - reduced = reduced.tolist() + # The below comparison is a hack to ensure that fully masked # results, for which val.ravel().all() returns np.ma.masked, # do not trigger a failure (np.ma.masked != True evaluates as # np.ma.masked, which is falsy). if cond != True: - match = 100-100.0*reduced.count(1)/len(reduced) - msg = build_err_msg([x, y], - err_msg - + '\n(mismatch %s%%)' % (match,), + n_mismatch = reduced.size - reduced.sum(dtype=intp) + n_elements = flagged.size if flagged.ndim != 0 else reduced.size + percent_mismatch = 100 * n_mismatch / n_elements + remarks = [ + 'Mismatched elements: {} / {} ({:.3g}%)'.format( + n_mismatch, n_elements, percent_mismatch)] + + with errstate(invalid='ignore', divide='ignore'): + # ignore errors for non-numeric types + with contextlib.suppress(TypeError): + error = abs(x - y) + max_abs_error = max(error) + if getattr(error, 'dtype', object_) == object_: + remarks.append('Max absolute difference: ' + + str(max_abs_error)) + else: + remarks.append('Max absolute difference: ' + + array2string(max_abs_error)) + + # note: this definition of relative error matches that one + # used by assert_allclose (found in np.isclose) + # Filter values where the divisor would be zero + nonzero = bool_(y != 0) + if all(~nonzero): + max_rel_error = array(inf) + else: + max_rel_error = max(error[nonzero] / abs(y[nonzero])) + if getattr(error, 'dtype', object_) == object_: + remarks.append('Max relative difference: ' + + str(max_rel_error)) + else: + remarks.append('Max relative difference: ' + + array2string(max_rel_error)) + + err_msg += '\n' + '\n'.join(remarks) + msg = build_err_msg([ox, oy], err_msg, verbose=verbose, header=header, names=('x', 'y'), precision=precision) raise AssertionError(msg) except ValueError: import traceback efmt = traceback.format_exc() - header = 'error during assertion:\n\n%s\n\n%s' % (efmt, header) + header = f'error during assertion:\n\n{efmt}\n\n{header}' msg = build_err_msg([x, y], err_msg, verbose=verbose, header=header, names=('x', 'y'), precision=precision) @@ -793,10 +857,11 @@ def assert_array_equal(x, y, err_msg='', verbose=True): Raises an AssertionError if two array_like objects are not equal. Given two array_like objects, check that the shape is equal and all - elements of these objects are equal. An exception is raised at - shape mismatch or conflicting values. In contrast to the standard usage - in numpy, NaNs are compared like numbers, no assertion is raised if - both objects have NaNs in the same positions. + elements of these objects are equal (but see the Notes for the special + handling of a scalar). An exception is raised at shape mismatch or + conflicting values. In contrast to the standard usage in numpy, NaNs + are compared like numbers, no assertion is raised if both objects have + NaNs in the same positions. The usual caution for verifying equality with floating point numbers is advised. @@ -823,6 +888,12 @@ def assert_array_equal(x, y, err_msg='', verbose=True): relative and/or absolute precision. assert_array_almost_equal_nulp, assert_array_max_ulp, assert_equal + Notes + ----- + When one of `x` and `y` is a scalar and the other is array_like, the + function checks that each element of the array_like object is equal to + the scalar. + Examples -------- The first assert does not raise an exception: @@ -830,18 +901,20 @@ def assert_array_equal(x, y, err_msg='', verbose=True): >>> np.testing.assert_array_equal([1.0,2.33333,np.nan], ... [np.exp(0),2.33333, np.nan]) - Assert fails with numerical inprecision with floats: + Assert fails with numerical imprecision with floats: >>> np.testing.assert_array_equal([1.0,np.pi,np.nan], ... [1, np.sqrt(np.pi)**2, np.nan]) - ... - <type 'exceptions.ValueError'>: + Traceback (most recent call last): + ... AssertionError: Arrays are not equal <BLANKLINE> - (mismatch 50.0%) - x: array([ 1. , 3.14159265, NaN]) - y: array([ 1. , 3.14159265, NaN]) + Mismatched elements: 1 / 3 (33.3%) + Max absolute difference: 4.4408921e-16 + Max relative difference: 1.41357986e-16 + x: array([1. , 3.141593, nan]) + y: array([1. , 3.141593, nan]) Use `assert_allclose` or one of the nulp (number of floating point values) functions for these cases instead: @@ -850,6 +923,12 @@ def assert_array_equal(x, y, err_msg='', verbose=True): ... [1, np.sqrt(np.pi)**2, np.nan], ... rtol=1e-10, atol=0) + As mentioned in the Notes section, `assert_array_equal` has special + handling for scalars. Here the test checks that each value in `x` is 3: + + >>> x = np.full((2, 5), fill_value=3) + >>> np.testing.assert_array_equal(x, 3) + """ __tracebackhide__ = True # Hide traceback for py.test assert_array_compare(operator.__eq__, x, y, err_msg=err_msg, @@ -906,30 +985,35 @@ def assert_array_almost_equal(x, y, decimal=6, err_msg='', verbose=True): the first assert does not raise an exception >>> np.testing.assert_array_almost_equal([1.0,2.333,np.nan], - [1.0,2.333,np.nan]) + ... [1.0,2.333,np.nan]) >>> np.testing.assert_array_almost_equal([1.0,2.33333,np.nan], ... [1.0,2.33339,np.nan], decimal=5) - ... - <type 'exceptions.AssertionError'>: + Traceback (most recent call last): + ... AssertionError: - Arrays are not almost equal + Arrays are not almost equal to 5 decimals <BLANKLINE> - (mismatch 50.0%) - x: array([ 1. , 2.33333, NaN]) - y: array([ 1. , 2.33339, NaN]) + Mismatched elements: 1 / 3 (33.3%) + Max absolute difference: 6.e-05 + Max relative difference: 2.57136612e-05 + x: array([1. , 2.33333, nan]) + y: array([1. , 2.33339, nan]) >>> np.testing.assert_array_almost_equal([1.0,2.33333,np.nan], ... [1.0,2.33333, 5], decimal=5) - <type 'exceptions.ValueError'>: - ValueError: - Arrays are not almost equal - x: array([ 1. , 2.33333, NaN]) - y: array([ 1. , 2.33333, 5. ]) + Traceback (most recent call last): + ... + AssertionError: + Arrays are not almost equal to 5 decimals + <BLANKLINE> + x and y nan location mismatch: + x: array([1. , 2.33333, nan]) + y: array([1. , 2.33333, 5. ]) """ __tracebackhide__ = True # Hide traceback for py.test - from numpy.core import around, number, float_, result_type, array + from numpy.core import number, float_, result_type, array from numpy.core.numerictypes import issubdtype from numpy.core.fromnumeric import any as npany @@ -951,7 +1035,7 @@ def compare(x, y): # make sure y is an inexact type to avoid abs(MIN_INT); will cause # casting of x later. dtype = result_type(y, 1.) - y = array(y, dtype=dtype, copy=False, subok=True) + y = np.asanyarray(y, dtype) z = abs(x - y) if not issubdtype(z.dtype, number): @@ -1006,27 +1090,37 @@ def assert_array_less(x, y, err_msg='', verbose=True): -------- >>> np.testing.assert_array_less([1.0, 1.0, np.nan], [1.1, 2.0, np.nan]) >>> np.testing.assert_array_less([1.0, 1.0, np.nan], [1, 2.0, np.nan]) - ... - <type 'exceptions.ValueError'>: + Traceback (most recent call last): + ... + AssertionError: Arrays are not less-ordered - (mismatch 50.0%) - x: array([ 1., 1., NaN]) - y: array([ 1., 2., NaN]) + <BLANKLINE> + Mismatched elements: 1 / 3 (33.3%) + Max absolute difference: 1. + Max relative difference: 0.5 + x: array([ 1., 1., nan]) + y: array([ 1., 2., nan]) >>> np.testing.assert_array_less([1.0, 4.0], 3) - ... - <type 'exceptions.ValueError'>: + Traceback (most recent call last): + ... + AssertionError: Arrays are not less-ordered - (mismatch 50.0%) - x: array([ 1., 4.]) + <BLANKLINE> + Mismatched elements: 1 / 2 (50%) + Max absolute difference: 2. + Max relative difference: 0.66666667 + x: array([1., 4.]) y: array(3) >>> np.testing.assert_array_less([1.0, 2.0, 3.0], [4]) - ... - <type 'exceptions.ValueError'>: + Traceback (most recent call last): + ... + AssertionError: Arrays are not less-ordered + <BLANKLINE> (shapes (3,), (1,) mismatch) - x: array([ 1., 2., 3.]) + x: array([1., 2., 3.]) y: array([4]) """ @@ -1075,10 +1169,11 @@ def assert_string_equal(actual, desired): raise AssertionError(repr(type(actual))) if not isinstance(desired, str): raise AssertionError(repr(type(desired))) - if re.match(r'\A'+desired+r'\Z', actual, re.M): + if desired == actual: return - diff = list(difflib.Differ().compare(actual.splitlines(1), desired.splitlines(1))) + diff = list(difflib.Differ().compare(actual.splitlines(True), + desired.splitlines(True))) diff_list = [] while diff: d1 = diff.pop(0) @@ -1099,14 +1194,14 @@ def assert_string_equal(actual, desired): l.append(d3) else: diff.insert(0, d3) - if re.match(r'\A'+d2[2:]+r'\Z', d1[2:]): + if d2[2:] == d1[2:]: continue diff_list.extend(l) continue raise AssertionError(repr(d1)) if not diff_list: return - msg = 'Differences in strings:\n%s' % (''.join(diff_list)).rstrip() + msg = f"Differences in strings:\n{''.join(diff_list).rstrip()}" if actual != desired: raise AssertionError(msg) @@ -1131,15 +1226,15 @@ def rundocs(filename=None, raise_on_error=True): argument to the ``test()`` call. For example, to run all tests (including doctests) for `numpy.lib`: - >>> np.lib.test(doctests=True) #doctest: +SKIP + >>> np.lib.test(doctests=True) # doctest: +SKIP """ - from numpy.compat import npy_load_module + from numpy.distutils.misc_util import exec_mod_from_location import doctest if filename is None: f = sys._getframe(1) filename = f.f_globals['__file__'] name = os.path.splitext(os.path.basename(filename))[0] - m = npy_load_module(name, filename) + m = exec_mod_from_location(name, filename) tests = doctest.DocTestFinder().find(m) runner = doctest.DocTestRunner(verbose=False) @@ -1256,14 +1351,7 @@ def assert_raises_regex(exception_class, expected_regexp, *args, **kwargs): """ __tracebackhide__ = True # Hide traceback for py.test - - if sys.version_info.major >= 3: - funcname = _d.assertRaisesRegex - else: - # Only present in Python 2.7, missing from unittest in 2.6 - funcname = _d.assertRaisesRegexp - - return funcname(exception_class, expected_regexp, *args, **kwargs) + return _d.assertRaisesRegex(exception_class, expected_regexp, *args, **kwargs) def decorate_methods(cls, decorator, testmatch=None): @@ -1313,7 +1401,7 @@ def decorate_methods(cls, decorator, testmatch=None): return -def measure(code_str,times=1,label=None): +def measure(code_str, times=1, label=None): """ Return elapsed time for executing code in the namespace of the caller. @@ -1340,18 +1428,16 @@ def measure(code_str,times=1,label=None): Examples -------- - >>> etime = np.testing.measure('for i in range(1000): np.sqrt(i**2)', - ... times=times) - >>> print("Time for a single execution : ", etime / times, "s") + >>> times = 10 + >>> etime = np.testing.measure('for i in range(1000): np.sqrt(i**2)', times=times) + >>> print("Time for a single execution : ", etime / times, "s") # doctest: +SKIP Time for a single execution : 0.005 s """ frame = sys._getframe(1) locs, globs = frame.f_locals, frame.f_globals - code = compile(code_str, - 'Test name: %s ' % label, - 'exec') + code = compile(code_str, f'Test name: {label} ', 'exec') i = 0 elapsed = jiffies() while i < times: @@ -1368,7 +1454,9 @@ def _assert_valid_refcount(op): """ if not HAS_REFCOUNT: return True - import numpy as np, gc + + import gc + import numpy as np b = np.arange(100*100).reshape(100, 100) c = b @@ -1391,9 +1479,9 @@ def assert_allclose(actual, desired, rtol=1e-7, atol=0, equal_nan=True, Raises an AssertionError if two objects are not equal up to desired tolerance. - The test is equivalent to ``allclose(actual, desired, rtol, atol)``. - It compares the difference between `actual` and `desired` to - ``atol + rtol * abs(desired)``. + The test is equivalent to ``allclose(actual, desired, rtol, atol)`` (note + that ``allclose`` has different default values). It compares the difference + between `actual` and `desired` to ``atol + rtol * abs(desired)``. .. versionadded:: 1.5.0 @@ -1427,7 +1515,7 @@ def assert_allclose(actual, desired, rtol=1e-7, atol=0, equal_nan=True, -------- >>> x = [1e-5, 1e-3, 1e-1] >>> y = np.arccos(np.cos(x)) - >>> assert_allclose(x, y, rtol=1e-5, atol=0) + >>> np.testing.assert_allclose(x, y, rtol=1e-5, atol=0) """ __tracebackhide__ = True # Hide traceback for py.test @@ -1438,7 +1526,7 @@ def compare(x, y): equal_nan=equal_nan) actual, desired = np.asanyarray(actual), np.asanyarray(desired) - header = 'Not equal to tolerance rtol=%g, atol=%g' % (rtol, atol) + header = f'Not equal to tolerance rtol={rtol:g}, atol={atol:g}' assert_array_compare(compare, actual, desired, err_msg=str(err_msg), verbose=verbose, header=header, equal_nan=equal_nan) @@ -1531,6 +1619,12 @@ def assert_array_max_ulp(a, b, maxulp=1, dtype=None): AssertionError If one or more elements differ by more than `maxulp`. + Notes + ----- + For computing the ULP difference, this API does not differentiate between + various representations of NAN (ULP difference between 0x7fc00000 and 0xffc00000 + is zero). + See Also -------- assert_array_almost_equal_nulp : Compare two arrays relatively to their @@ -1546,8 +1640,9 @@ def assert_array_max_ulp(a, b, maxulp=1, dtype=None): import numpy as np ret = nulp_diff(a, b, dtype) if not np.all(ret <= maxulp): - raise AssertionError("Arrays are not almost equal up to %g ULP" % - maxulp) + raise AssertionError("Arrays are not almost equal up to %g " + "ULP (max difference is %g ULP)" % + (maxulp, np.max(ret))) return ret @@ -1570,6 +1665,12 @@ def nulp_diff(x, y, dtype=None): number of representable floating point numbers between each item in x and y. + Notes + ----- + For computing the ULP difference, this API does not differentiate between + various representations of NAN (ULP difference between 0x7fc00000 and 0xffc00000 + is zero). + Examples -------- # By definition, epsilon is the smallest number such as 1 + eps != 1, so @@ -1579,25 +1680,28 @@ def nulp_diff(x, y, dtype=None): """ import numpy as np if dtype: - x = np.array(x, dtype=dtype) - y = np.array(y, dtype=dtype) + x = np.asarray(x, dtype=dtype) + y = np.asarray(y, dtype=dtype) else: - x = np.array(x) - y = np.array(y) + x = np.asarray(x) + y = np.asarray(y) t = np.common_type(x, y) if np.iscomplexobj(x) or np.iscomplexobj(y): raise NotImplementedError("_nulp not implemented for complex array") - x = np.array(x, dtype=t) - y = np.array(y, dtype=t) + x = np.array([x], dtype=t) + y = np.array([y], dtype=t) + + x[np.isnan(x)] = np.nan + y[np.isnan(y)] = np.nan if not x.shape == y.shape: raise ValueError("x and y do not have the same shape: %s - %s" % (x.shape, y.shape)) def _diff(rx, ry, vdt): - diff = np.array(rx-ry, dtype=vdt) + diff = np.asarray(rx-ry, dtype=vdt) return np.abs(diff) rx = integer_repr(x) @@ -1609,7 +1713,7 @@ def _integer_repr(x, vdt, comp): # Reinterpret binary representation of the float as sign-magnitude: # take into account two-complement representation # See also - # http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm + # https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ rx = x.view(vdt) if not (rx.size == 1): rx[rx < 0] = comp - rx[rx < 0] @@ -1621,8 +1725,8 @@ def _integer_repr(x, vdt, comp): def integer_repr(x): - """Return the signed-magnitude interpretation of the binary representation of - x.""" + """Return the signed-magnitude interpretation of the binary representation + of x.""" import numpy as np if x.dtype == np.float16: return _integer_repr(x, np.int16, np.int16(-2**15)) @@ -1631,7 +1735,7 @@ def integer_repr(x): elif x.dtype == np.float64: return _integer_repr(x, np.int64, np.int64(-2**63)) else: - raise ValueError("Unsupported dtype %s" % x.dtype) + raise ValueError(f'Unsupported dtype {x.dtype}') @contextlib.contextmanager @@ -1641,7 +1745,7 @@ def _assert_warns_context(warning_class, name=None): l = sup.record(warning_class) yield if not len(l) > 0: - name_str = " when calling %s" % name if name is not None else "" + name_str = f' when calling {name}' if name is not None else '' raise AssertionError("No warning raised" + name_str) @@ -1667,17 +1771,28 @@ def assert_warns(warning_class, *args, **kwargs): ---------- warning_class : class The class defining the warning that `func` is expected to throw. - func : callable - The callable to test. - \\*args : Arguments - Arguments passed to `func`. - \\*\\*kwargs : Kwargs - Keyword arguments passed to `func`. + func : callable, optional + Callable to test + *args : Arguments + Arguments for `func`. + **kwargs : Kwargs + Keyword arguments for `func`. Returns ------- The value returned by `func`. + Examples + -------- + >>> import warnings + >>> def deprecated_func(num): + ... warnings.warn("Please upgrade", DeprecationWarning) + ... return num*num + >>> with np.testing.assert_warns(DeprecationWarning): + ... assert deprecated_func(4) == 16 + >>> # or passing a func + >>> ret = np.testing.assert_warns(DeprecationWarning, deprecated_func, 4) + >>> assert ret == 16 """ if not args: return _assert_warns_context(warning_class) @@ -1695,8 +1810,8 @@ def _assert_no_warnings_context(name=None): warnings.simplefilter('always') yield if len(l) > 0: - name_str = " when calling %s" % name if name is not None else "" - raise AssertionError("Got warnings%s: %s" % (name_str, l)) + name_str = f' when calling {name}' if name is not None else '' + raise AssertionError(f'Got warnings{name_str}: {l}') def assert_no_warnings(*args, **kwargs): @@ -1881,7 +1996,8 @@ class clear_and_catch_warnings(warnings.catch_warnings): Examples -------- >>> import warnings - >>> with clear_and_catch_warnings(modules=[np.core.fromnumeric]): + >>> with np.testing.clear_and_catch_warnings( + ... modules=[np.core.fromnumeric]): ... warnings.simplefilter('always') ... warnings.filterwarnings('ignore', module='np.core.fromnumeric') ... # do something that raises a warning but ignore those in @@ -1892,7 +2008,7 @@ class clear_and_catch_warnings(warnings.catch_warnings): def __init__(self, record=False, modules=()): self.modules = set(modules).union(self.class_modules) self._warnreg_copies = {} - super(clear_and_catch_warnings, self).__init__(record=record) + super().__init__(record=record) def __enter__(self): for mod in self.modules: @@ -1900,10 +2016,10 @@ def __enter__(self): mod_reg = mod.__warningregistry__ self._warnreg_copies[mod] = mod_reg.copy() mod_reg.clear() - return super(clear_and_catch_warnings, self).__enter__() + return super().__enter__() def __exit__(self, *exc_info): - super(clear_and_catch_warnings, self).__exit__(*exc_info) + super().__exit__(*exc_info) for mod in self.modules: if hasattr(mod, '__warningregistry__'): mod.__warningregistry__.clear() @@ -1911,13 +2027,13 @@ def __exit__(self, *exc_info): mod.__warningregistry__.update(self._warnreg_copies[mod]) -class suppress_warnings(object): +class suppress_warnings: """ Context manager and decorator doing much the same as ``warnings.catch_warnings``. However, it also provides a filter mechanism to work around - http://bugs.python.org/issue4180. + https://bugs.python.org/issue4180. This bug causes Python before 3.4 to not reliably show warnings again after they have been ignored once (even within catch_warnings). It @@ -1962,25 +2078,28 @@ class suppress_warnings(object): Examples -------- - >>> with suppress_warnings() as sup: - ... sup.filter(DeprecationWarning, "Some text") - ... sup.filter(module=np.ma.core) - ... log = sup.record(FutureWarning, "Does this occur?") - ... command_giving_warnings() - ... # The FutureWarning was given once, the filtered warnings were - ... # ignored. All other warnings abide outside settings (may be - ... # printed/error) - ... assert_(len(log) == 1) - ... assert_(len(sup.log) == 1) # also stored in log attribute - - Or as a decorator: - - >>> sup = suppress_warnings() - >>> sup.filter(module=np.ma.core) # module must match exact - >>> @sup - >>> def some_function(): - ... # do something which causes a warning in np.ma.core - ... pass + + With a context manager:: + + with np.testing.suppress_warnings() as sup: + sup.filter(DeprecationWarning, "Some text") + sup.filter(module=np.ma.core) + log = sup.record(FutureWarning, "Does this occur?") + command_giving_warnings() + # The FutureWarning was given once, the filtered warnings were + # ignored. All other warnings abide outside settings (may be + # printed/error) + assert_(len(log) == 1) + assert_(len(sup.log) == 1) # also stored in log attribute + + Or as a decorator:: + + sup = np.testing.suppress_warnings() + sup.filter(module=np.ma.core) # module must match exactly + @sup + def some_function(): + # do something which causes a warning in np.ma.core + pass """ def __init__(self, forwarding_rule="always"): self._entered = False @@ -2123,8 +2242,7 @@ def __exit__(self, *exc_info): del self._filters def _showwarning(self, message, category, filename, lineno, - *args, **kwargs): - use_warnmsg = kwargs.pop("use_warnmsg", None) + *args, use_warnmsg=None, **kwargs): for cat, _, pattern, mod, rec in ( self._suppressions + self._tmp_suppressions)[::-1]: if (issubclass(category, cat) and @@ -2193,6 +2311,7 @@ def _assert_no_gc_cycles_context(name=None): # not meaningful to test if there is no refcounting if not HAS_REFCOUNT: + yield return assert_(gc.isenabled()) @@ -2204,8 +2323,8 @@ def _assert_no_gc_cycles_context(name=None): break else: raise RuntimeError( - "Unable to fully collect garbage - perhaps a __del__ method is " - "creating more reference cycles?") + "Unable to fully collect garbage - perhaps a __del__ method " + "is creating more reference cycles?") gc.set_debug(gc.DEBUG_SAVEALL) yield @@ -2219,7 +2338,7 @@ def _assert_no_gc_cycles_context(name=None): gc.enable() if n_objects_in_cycles: - name_str = " when calling %s" % name if name is not None else "" + name_str = f' when calling {name}' if name is not None else '' raise AssertionError( "Reference cycles were found{}: {} objects were collected, " "of which {} are shown below:{}" @@ -2271,3 +2390,131 @@ def assert_no_gc_cycles(*args, **kwargs): args = args[1:] with _assert_no_gc_cycles_context(name=func.__name__): func(*args, **kwargs) + +def break_cycles(): + """ + Break reference cycles by calling gc.collect + Objects can call other objects' methods (for instance, another object's + __del__) inside their own __del__. On PyPy, the interpreter only runs + between calls to gc.collect, so multiple calls are needed to completely + release all cycles. + """ + + gc.collect() + if IS_PYPY: + # a few more, just to make sure all the finalizers are called + gc.collect() + gc.collect() + gc.collect() + gc.collect() + + +def requires_memory(free_bytes): + """Decorator to skip a test if not enough memory is available""" + import pytest + + def decorator(func): + @wraps(func) + def wrapper(*a, **kw): + msg = check_free_memory(free_bytes) + if msg is not None: + pytest.skip(msg) + + try: + return func(*a, **kw) + except MemoryError: + # Probably ran out of memory regardless: don't regard as failure + pytest.xfail("MemoryError raised") + + return wrapper + + return decorator + + +def check_free_memory(free_bytes): + """ + Check whether `free_bytes` amount of memory is currently free. + Returns: None if enough memory available, otherwise error message + """ + env_var = 'NPY_AVAILABLE_MEM' + env_value = os.environ.get(env_var) + if env_value is not None: + try: + mem_free = _parse_size(env_value) + except ValueError as exc: + raise ValueError(f'Invalid environment variable {env_var}: {exc}') + + msg = (f'{free_bytes/1e9} GB memory required, but environment variable ' + f'NPY_AVAILABLE_MEM={env_value} set') + else: + mem_free = _get_mem_available() + + if mem_free is None: + msg = ("Could not determine available memory; set NPY_AVAILABLE_MEM " + "environment variable (e.g. NPY_AVAILABLE_MEM=16GB) to run " + "the test.") + mem_free = -1 + else: + msg = f'{free_bytes/1e9} GB memory required, but {mem_free/1e9} GB available' + + return msg if mem_free < free_bytes else None + + +def _parse_size(size_str): + """Convert memory size strings ('12 GB' etc.) to float""" + suffixes = {'': 1, 'b': 1, + 'k': 1000, 'm': 1000**2, 'g': 1000**3, 't': 1000**4, + 'kb': 1000, 'mb': 1000**2, 'gb': 1000**3, 'tb': 1000**4, + 'kib': 1024, 'mib': 1024**2, 'gib': 1024**3, 'tib': 1024**4} + + size_re = re.compile(r'^\s*(\d+|\d+\.\d+)\s*({0})\s*$'.format( + '|'.join(suffixes.keys())), re.I) + + m = size_re.match(size_str.lower()) + if not m or m.group(2) not in suffixes: + raise ValueError(f'value {size_str!r} not a valid size') + return int(float(m.group(1)) * suffixes[m.group(2)]) + + +def _get_mem_available(): + """Return available memory in bytes, or None if unknown.""" + try: + import psutil + return psutil.virtual_memory().available + except (ImportError, AttributeError): + pass + + if sys.platform.startswith('linux'): + info = {} + with open('/proc/meminfo', 'r') as f: + for line in f: + p = line.split() + info[p[0].strip(':').lower()] = int(p[1]) * 1024 + + if 'memavailable' in info: + # Linux >= 3.14 + return info['memavailable'] + else: + return info['memfree'] + info['cached'] + + return None + + +def _no_tracing(func): + """ + Decorator to temporarily turn off tracing for the duration of a test. + Needed in tests that check refcounting, otherwise the tracing itself + influences the refcounts + """ + if not hasattr(sys, 'gettrace'): + return func + else: + @wraps(func) + def wrapper(*args, **kwargs): + original_trace = sys.gettrace() + try: + sys.settrace(None) + return func(*args, **kwargs) + finally: + sys.settrace(original_trace) + return wrapper diff --git a/numpy/testing/_private/utils.pyi b/numpy/testing/_private/utils.pyi new file mode 100644 index 000000000000..4ba5d82ee7bf --- /dev/null +++ b/numpy/testing/_private/utils.pyi @@ -0,0 +1,400 @@ +import os +import sys +import ast +import types +import warnings +import unittest +import contextlib +from typing import ( + Literal as L, + Any, + AnyStr, + Callable, + ClassVar, + Dict, + Iterable, + List, + NoReturn, + overload, + Pattern, + Sequence, + Set, + Tuple, + Type, + type_check_only, + TypeVar, + Union, + Final, + SupportsIndex, +) + +from numpy import generic, dtype, number, object_, bool_, _FloatValue +from numpy.typing import ( + NDArray, + ArrayLike, + DTypeLike, + _ArrayLikeNumber_co, + _ArrayLikeObject_co, + _ArrayLikeTD64_co, + _ArrayLikeDT64_co, +) + +from unittest.case import ( + SkipTest as SkipTest, +) + +_T = TypeVar("_T") +_ET = TypeVar("_ET", bound=BaseException) +_FT = TypeVar("_FT", bound=Callable[..., Any]) + +# Must return a bool or an ndarray/generic type +# that is supported by `np.logical_and.reduce` +_ComparisonFunc = Callable[ + [NDArray[Any], NDArray[Any]], + Union[ + bool, + bool_, + number[Any], + NDArray[Union[bool_, number[Any], object_]], + ], +] + +__all__: List[str] + +class KnownFailureException(Exception): ... +class IgnoreException(Exception): ... + +class clear_and_catch_warnings(warnings.catch_warnings): + class_modules: ClassVar[Tuple[types.ModuleType, ...]] + modules: Set[types.ModuleType] + @overload + def __new__( + cls, + record: L[False] = ..., + modules: Iterable[types.ModuleType] = ..., + ) -> _clear_and_catch_warnings_without_records: ... + @overload + def __new__( + cls, + record: L[True], + modules: Iterable[types.ModuleType] = ..., + ) -> _clear_and_catch_warnings_with_records: ... + @overload + def __new__( + cls, + record: bool, + modules: Iterable[types.ModuleType] = ..., + ) -> clear_and_catch_warnings: ... + def __enter__(self) -> None | List[warnings.WarningMessage]: ... + def __exit__( + self, + __exc_type: None | Type[BaseException] = ..., + __exc_val: None | BaseException = ..., + __exc_tb: None | types.TracebackType = ..., + ) -> None: ... + +# Type-check only `clear_and_catch_warnings` subclasses for both values of the +# `record` parameter. Copied from the stdlib `warnings` stubs. + +@type_check_only +class _clear_and_catch_warnings_with_records(clear_and_catch_warnings): + def __enter__(self) -> List[warnings.WarningMessage]: ... + +@type_check_only +class _clear_and_catch_warnings_without_records(clear_and_catch_warnings): + def __enter__(self) -> None: ... + +class suppress_warnings: + log: List[warnings.WarningMessage] + def __init__( + self, + forwarding_rule: L["always", "module", "once", "location"] = ..., + ) -> None: ... + def filter( + self, + category: Type[Warning] = ..., + message: str = ..., + module: None | types.ModuleType = ..., + ) -> None: ... + def record( + self, + category: Type[Warning] = ..., + message: str = ..., + module: None | types.ModuleType = ..., + ) -> List[warnings.WarningMessage]: ... + def __enter__(self: _T) -> _T: ... + def __exit__( + self, + __exc_type: None | Type[BaseException] = ..., + __exc_val: None | BaseException = ..., + __exc_tb: None | types.TracebackType = ..., + ) -> None: ... + def __call__(self, func: _FT) -> _FT: ... + +verbose: int +IS_PYPY: Final[bool] +IS_PYSTON: Final[bool] +HAS_REFCOUNT: Final[bool] +HAS_LAPACK64: Final[bool] + +def assert_(val: object, msg: str | Callable[[], str] = ...) -> None: ... + +# Contrary to runtime we can't do `os.name` checks while type checking, +# only `sys.platform` checks +if sys.platform == "win32" or sys.platform == "cygwin": + def memusage(processName: str = ..., instance: int = ...) -> int: ... +elif sys.platform == "linux": + def memusage(_proc_pid_stat: str | bytes | os.PathLike[Any] = ...) -> None | int: ... +else: + def memusage() -> NoReturn: ... + +if sys.platform == "linux": + def jiffies( + _proc_pid_stat: str | bytes | os.PathLike[Any] = ..., + _load_time: List[float] = ..., + ) -> int: ... +else: + def jiffies(_load_time: List[float] = ...) -> int: ... + +def build_err_msg( + arrays: Iterable[object], + err_msg: str, + header: str = ..., + verbose: bool = ..., + names: Sequence[str] = ..., + precision: None | SupportsIndex = ..., +) -> str: ... + +def assert_equal( + actual: object, + desired: object, + err_msg: str = ..., + verbose: bool = ..., +) -> None: ... + +def print_assert_equal( + test_string: str, + actual: object, + desired: object, +) -> None: ... + +def assert_almost_equal( + actual: _ArrayLikeNumber_co | _ArrayLikeObject_co, + desired: _ArrayLikeNumber_co | _ArrayLikeObject_co, + decimal: int = ..., + err_msg: str = ..., + verbose: bool = ..., +) -> None: ... + +# Anything that can be coerced into `builtins.float` +def assert_approx_equal( + actual: _FloatValue, + desired: _FloatValue, + significant: int = ..., + err_msg: str = ..., + verbose: bool = ..., +) -> None: ... + +def assert_array_compare( + comparison: _ComparisonFunc, + x: ArrayLike, + y: ArrayLike, + err_msg: str = ..., + verbose: bool = ..., + header: str = ..., + precision: SupportsIndex = ..., + equal_nan: bool = ..., + equal_inf: bool = ..., +) -> None: ... + +def assert_array_equal( + x: ArrayLike, + y: ArrayLike, + err_msg: str = ..., + verbose: bool = ..., +) -> None: ... + +def assert_array_almost_equal( + x: _ArrayLikeNumber_co | _ArrayLikeObject_co, + y: _ArrayLikeNumber_co | _ArrayLikeObject_co, + decimal: float = ..., + err_msg: str = ..., + verbose: bool = ..., +) -> None: ... + +@overload +def assert_array_less( + x: _ArrayLikeNumber_co | _ArrayLikeObject_co, + y: _ArrayLikeNumber_co | _ArrayLikeObject_co, + err_msg: str = ..., + verbose: bool = ..., +) -> None: ... +@overload +def assert_array_less( + x: _ArrayLikeTD64_co, + y: _ArrayLikeTD64_co, + err_msg: str = ..., + verbose: bool = ..., +) -> None: ... +@overload +def assert_array_less( + x: _ArrayLikeDT64_co, + y: _ArrayLikeDT64_co, + err_msg: str = ..., + verbose: bool = ..., +) -> None: ... + +def runstring( + astr: str | bytes | types.CodeType, + dict: None | Dict[str, Any], +) -> Any: ... + +def assert_string_equal(actual: str, desired: str) -> None: ... + +def rundocs( + filename: None | str | os.PathLike[str] = ..., + raise_on_error: bool = ..., +) -> None: ... + +def raises(*args: Type[BaseException]) -> Callable[[_FT], _FT]: ... + +@overload +def assert_raises( # type: ignore + expected_exception: Type[BaseException] | Tuple[Type[BaseException], ...], + callable: Callable[..., Any], + /, + *args: Any, + **kwargs: Any, +) -> None: ... +@overload +def assert_raises( + expected_exception: Type[_ET] | Tuple[Type[_ET], ...], + *, + msg: None | str = ..., +) -> unittest.case._AssertRaisesContext[_ET]: ... + +@overload +def assert_raises_regex( + expected_exception: Type[BaseException] | Tuple[Type[BaseException], ...], + expected_regex: str | bytes | Pattern[Any], + callable: Callable[..., Any], + /, + *args: Any, + **kwargs: Any, +) -> None: ... +@overload +def assert_raises_regex( + expected_exception: Type[_ET] | Tuple[Type[_ET], ...], + expected_regex: str | bytes | Pattern[Any], + *, + msg: None | str = ..., +) -> unittest.case._AssertRaisesContext[_ET]: ... + +def decorate_methods( + cls: Type[Any], + decorator: Callable[[Callable[..., Any]], Any], + testmatch: None | str | bytes | Pattern[Any] = ..., +) -> None: ... + +def measure( + code_str: str | bytes | ast.mod | ast.AST, + times: int = ..., + label: None | str = ..., +) -> float: ... + +@overload +def assert_allclose( + actual: _ArrayLikeNumber_co | _ArrayLikeObject_co, + desired: _ArrayLikeNumber_co | _ArrayLikeObject_co, + rtol: float = ..., + atol: float = ..., + equal_nan: bool = ..., + err_msg: str = ..., + verbose: bool = ..., +) -> None: ... +@overload +def assert_allclose( + actual: _ArrayLikeTD64_co, + desired: _ArrayLikeTD64_co, + rtol: float = ..., + atol: float = ..., + equal_nan: bool = ..., + err_msg: str = ..., + verbose: bool = ..., +) -> None: ... + +def assert_array_almost_equal_nulp( + x: _ArrayLikeNumber_co, + y: _ArrayLikeNumber_co, + nulp: float = ..., +) -> None: ... + +def assert_array_max_ulp( + a: _ArrayLikeNumber_co, + b: _ArrayLikeNumber_co, + maxulp: float = ..., + dtype: DTypeLike = ..., +) -> NDArray[Any]: ... + +@overload +def assert_warns( + warning_class: Type[Warning], +) -> contextlib._GeneratorContextManager[None]: ... +@overload +def assert_warns( + warning_class: Type[Warning], + func: Callable[..., _T], + /, + *args: Any, + **kwargs: Any, +) -> _T: ... + +@overload +def assert_no_warnings() -> contextlib._GeneratorContextManager[None]: ... +@overload +def assert_no_warnings( + func: Callable[..., _T], + /, + *args: Any, + **kwargs: Any, +) -> _T: ... + +@overload +def tempdir( + suffix: None = ..., + prefix: None = ..., + dir: None = ..., +) -> contextlib._GeneratorContextManager[str]: ... +@overload +def tempdir( + suffix: None | AnyStr = ..., + prefix: None | AnyStr = ..., + dir: None | AnyStr | os.PathLike[AnyStr] = ..., +) -> contextlib._GeneratorContextManager[AnyStr]: ... + +@overload +def temppath( + suffix: None = ..., + prefix: None = ..., + dir: None = ..., + text: bool = ..., +) -> contextlib._GeneratorContextManager[str]: ... +@overload +def temppath( + suffix: None | AnyStr = ..., + prefix: None | AnyStr = ..., + dir: None | AnyStr | os.PathLike[AnyStr] = ..., + text: bool = ..., +) -> contextlib._GeneratorContextManager[AnyStr]: ... + +@overload +def assert_no_gc_cycles() -> contextlib._GeneratorContextManager[None]: ... +@overload +def assert_no_gc_cycles( + func: Callable[..., Any], + /, + *args: Any, + **kwargs: Any, +) -> None: ... + +def break_cycles() -> None: ... diff --git a/numpy/testing/decorators.py b/numpy/testing/decorators.py deleted file mode 100644 index 68c1554b5490..000000000000 --- a/numpy/testing/decorators.py +++ /dev/null @@ -1,15 +0,0 @@ -""" -Back compatibility decorators module. It will import the appropriate -set of tools - -""" -from __future__ import division, absolute_import, print_function - -import warnings - -# 2018-04-04, numpy 1.15.0 -warnings.warn("Importing from numpy.testing.decorators is deprecated, " - "import from numpy.testing instead.", - DeprecationWarning, stacklevel=2) - -from ._private.decorators import * diff --git a/numpy/testing/noseclasses.py b/numpy/testing/noseclasses.py deleted file mode 100644 index e0e728a32732..000000000000 --- a/numpy/testing/noseclasses.py +++ /dev/null @@ -1,14 +0,0 @@ -""" -Back compatibility noseclasses module. It will import the appropriate -set of tools -""" -from __future__ import division, absolute_import, print_function - -import warnings - -# 2018-04-04, numpy 1.15.0 -warnings.warn("Importing from numpy.testing.noseclasses is deprecated, " - "import from numpy.testing instead", - DeprecationWarning, stacklevel=2) - -from ._private.noseclasses import * diff --git a/numpy/testing/nosetester.py b/numpy/testing/nosetester.py deleted file mode 100644 index c8c7d6e68b4f..000000000000 --- a/numpy/testing/nosetester.py +++ /dev/null @@ -1,19 +0,0 @@ -""" -Back compatibility nosetester module. It will import the appropriate -set of tools - -""" -from __future__ import division, absolute_import, print_function - -import warnings - -# 2018-04-04, numpy 1.15.0 -warnings.warn("Importing from numpy.testing.nosetester is deprecated, " - "import from numpy.testing instead.", - DeprecationWarning, stacklevel=2) - -from ._private.nosetester import * - -__all__ = ['get_package_name', 'run_module_suite', 'NoseTester', - '_numpy_tester', 'get_package_name', 'import_nose', - 'suppress_warnings'] diff --git a/numpy/testing/print_coercion_tables.py b/numpy/testing/print_coercion_tables.py index 3a359f47297e..3a447cd2db5e 100755 --- a/numpy/testing/print_coercion_tables.py +++ b/numpy/testing/print_coercion_tables.py @@ -1,13 +1,12 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """Prints type-coercion tables for the built-in NumPy types """ -from __future__ import division, absolute_import, print_function - import numpy as np +from collections import namedtuple # Generic object that can be added, but doesn't do anything else -class GenericObject(object): +class GenericObject: def __init__(self, v): self.v = v @@ -27,7 +26,17 @@ def print_cancast_table(ntypes): for row in ntypes: print(row, end=' ') for col in ntypes: - print(int(np.can_cast(row, col)), end=' ') + if np.can_cast(row, col, "equiv"): + cast = "#" + elif np.can_cast(row, col, "safe"): + cast = "=" + elif np.can_cast(row, col, "same_kind"): + cast = "~" + elif np.can_cast(row, col, "unsafe"): + cast = "." + else: + cast = " " + print(cast, end=' ') print() def print_coercion_table(ntypes, inputfirstvalue, inputsecondvalue, firstarray, use_promote_types=False): @@ -70,22 +79,121 @@ def print_coercion_table(ntypes, inputfirstvalue, inputsecondvalue, firstarray, print(char, end=' ') print() -print("can cast") -print_cancast_table(np.typecodes['All']) -print() -print("In these tables, ValueError is '!', OverflowError is '@', TypeError is '#'") -print() -print("scalar + scalar") -print_coercion_table(np.typecodes['All'], 0, 0, False) -print() -print("scalar + neg scalar") -print_coercion_table(np.typecodes['All'], 0, -1, False) -print() -print("array + scalar") -print_coercion_table(np.typecodes['All'], 0, 0, True) -print() -print("array + neg scalar") -print_coercion_table(np.typecodes['All'], 0, -1, True) -print() -print("promote_types") -print_coercion_table(np.typecodes['All'], 0, 0, False, True) + +def print_new_cast_table(*, can_cast=True, legacy=False, flags=False): + """Prints new casts, the values given are default "can-cast" values, not + actual ones. + """ + from numpy.core._multiarray_tests import get_all_cast_information + + cast_table = { + 0 : "#", # No cast (classify as equivalent here) + 1 : "#", # equivalent casting + 2 : "=", # safe casting + 3 : "~", # same-kind casting + 4 : ".", # unsafe casting + } + flags_table = { + 0 : "▗", 7: "█", + 1: "▚", 2: "▐", 4: "▄", + 3: "▜", 5: "▙", + 6: "▟", + } + + cast_info = namedtuple("cast_info", ["can_cast", "legacy", "flags"]) + no_cast_info = cast_info(" ", " ", " ") + + casts = get_all_cast_information() + table = {} + dtypes = set() + for cast in casts: + dtypes.add(cast["from"]) + dtypes.add(cast["to"]) + + if cast["from"] not in table: + table[cast["from"]] = {} + to_dict = table[cast["from"]] + + can_cast = cast_table[cast["casting"]] + legacy = "L" if cast["legacy"] else "." + flags = 0 + if cast["requires_pyapi"]: + flags |= 1 + if cast["supports_unaligned"]: + flags |= 2 + if cast["no_floatingpoint_errors"]: + flags |= 4 + + flags = flags_table[flags] + to_dict[cast["to"]] = cast_info(can_cast=can_cast, legacy=legacy, flags=flags) + + # The np.dtype(x.type) is a bit strange, because dtype classes do + # not expose much yet. + types = np.typecodes["All"] + def sorter(x): + # This is a bit weird hack, to get a table as close as possible to + # the one printing all typecodes (but expecting user-dtypes). + dtype = np.dtype(x.type) + try: + indx = types.index(dtype.char) + except ValueError: + indx = np.inf + return (indx, dtype.char) + + dtypes = sorted(dtypes, key=sorter) + + def print_table(field="can_cast"): + print('X', end=' ') + for dt in dtypes: + print(np.dtype(dt.type).char, end=' ') + print() + for from_dt in dtypes: + print(np.dtype(from_dt.type).char, end=' ') + row = table.get(from_dt, {}) + for to_dt in dtypes: + print(getattr(row.get(to_dt, no_cast_info), field), end=' ') + print() + + if can_cast: + # Print the actual table: + print() + print("Casting: # is equivalent, = is safe, ~ is same-kind, and . is unsafe") + print() + print_table("can_cast") + + if legacy: + print() + print("L denotes a legacy cast . a non-legacy one.") + print() + print_table("legacy") + + if flags: + print() + print(f"{flags_table[0]}: no flags, {flags_table[1]}: PyAPI, " + f"{flags_table[2]}: supports unaligned, {flags_table[4]}: no-float-errors") + print() + print_table("flags") + + +if __name__ == '__main__': + print("can cast") + print_cancast_table(np.typecodes['All']) + print() + print("In these tables, ValueError is '!', OverflowError is '@', TypeError is '#'") + print() + print("scalar + scalar") + print_coercion_table(np.typecodes['All'], 0, 0, False) + print() + print("scalar + neg scalar") + print_coercion_table(np.typecodes['All'], 0, -1, False) + print() + print("array + scalar") + print_coercion_table(np.typecodes['All'], 0, 0, True) + print() + print("array + neg scalar") + print_coercion_table(np.typecodes['All'], 0, -1, True) + print() + print("promote_types") + print_coercion_table(np.typecodes['All'], 0, 0, False, True) + print("New casting type promotion:") + print_new_cast_table(can_cast=True, legacy=True, flags=True) diff --git a/numpy/testing/setup.py b/numpy/testing/setup.py index e27a9b85bd4b..6f203e872711 100755 --- a/numpy/testing/setup.py +++ b/numpy/testing/setup.py @@ -1,13 +1,13 @@ -#!/usr/bin/env python -from __future__ import division, print_function - +#!/usr/bin/env python3 def configuration(parent_package='',top_path=None): from numpy.distutils.misc_util import Configuration config = Configuration('testing', parent_package, top_path) config.add_subpackage('_private') - config.add_data_dir('tests') + config.add_subpackage('tests') + config.add_data_files('*.pyi') + config.add_data_files('_private/*.pyi') return config if __name__ == '__main__': @@ -15,7 +15,7 @@ def configuration(parent_package='',top_path=None): setup(maintainer="NumPy Developers", maintainer_email="numpy-dev@numpy.org", description="NumPy test module", - url="http://www.numpy.org", + url="https://www.numpy.org", license="NumPy License (BSD Style)", configuration=configuration, ) diff --git a/numpy/testing/tests/test_decorators.py b/numpy/testing/tests/test_decorators.py deleted file mode 100644 index ea684140dc32..000000000000 --- a/numpy/testing/tests/test_decorators.py +++ /dev/null @@ -1,216 +0,0 @@ -""" -Test the decorators from ``testing.decorators``. - -""" -from __future__ import division, absolute_import, print_function - -import warnings -import pytest - -from numpy.testing import ( - assert_, assert_raises, dec, SkipTest, KnownFailureException, - ) - - -try: - import nose -except ImportError: - HAVE_NOSE = False -else: - HAVE_NOSE = True - - -@pytest.mark.skipif(not HAVE_NOSE, reason="Needs nose") -class TestNoseDecorators(object): - # These tests are run in a class for simplicity while still - # getting a report on each, skipped or success. - - class DidntSkipException(Exception): - pass - - def test_slow(self): - import nose - @dec.slow - def slow_func(x, y, z): - pass - - assert_(slow_func.slow) - - def test_setastest(self): - @dec.setastest() - def f_default(a): - pass - - @dec.setastest(True) - def f_istest(a): - pass - - @dec.setastest(False) - def f_isnottest(a): - pass - - assert_(f_default.__test__) - assert_(f_istest.__test__) - assert_(not f_isnottest.__test__) - - - def test_skip_functions_hardcoded(self): - @dec.skipif(True) - def f1(x): - raise self.DidntSkipException - - try: - f1('a') - except self.DidntSkipException: - raise Exception('Failed to skip') - except SkipTest().__class__: - pass - - @dec.skipif(False) - def f2(x): - raise self.DidntSkipException - - try: - f2('a') - except self.DidntSkipException: - pass - except SkipTest().__class__: - raise Exception('Skipped when not expected to') - - def test_skip_functions_callable(self): - def skip_tester(): - return skip_flag == 'skip me!' - - @dec.skipif(skip_tester) - def f1(x): - raise self.DidntSkipException - - try: - skip_flag = 'skip me!' - f1('a') - except self.DidntSkipException: - raise Exception('Failed to skip') - except SkipTest().__class__: - pass - - @dec.skipif(skip_tester) - def f2(x): - raise self.DidntSkipException - - try: - skip_flag = 'five is right out!' - f2('a') - except self.DidntSkipException: - pass - except SkipTest().__class__: - raise Exception('Skipped when not expected to') - - def test_skip_generators_hardcoded(self): - @dec.knownfailureif(True, "This test is known to fail") - def g1(x): - for i in range(x): - yield i - - try: - for j in g1(10): - pass - except KnownFailureException().__class__: - pass - else: - raise Exception('Failed to mark as known failure') - - @dec.knownfailureif(False, "This test is NOT known to fail") - def g2(x): - for i in range(x): - yield i - raise self.DidntSkipException('FAIL') - - try: - for j in g2(10): - pass - except KnownFailureException().__class__: - raise Exception('Marked incorrectly as known failure') - except self.DidntSkipException: - pass - - def test_skip_generators_callable(self): - def skip_tester(): - return skip_flag == 'skip me!' - - @dec.knownfailureif(skip_tester, "This test is known to fail") - def g1(x): - for i in range(x): - yield i - - try: - skip_flag = 'skip me!' - for j in g1(10): - pass - except KnownFailureException().__class__: - pass - else: - raise Exception('Failed to mark as known failure') - - @dec.knownfailureif(skip_tester, "This test is NOT known to fail") - def g2(x): - for i in range(x): - yield i - raise self.DidntSkipException('FAIL') - - try: - skip_flag = 'do not skip' - for j in g2(10): - pass - except KnownFailureException().__class__: - raise Exception('Marked incorrectly as known failure') - except self.DidntSkipException: - pass - - def test_deprecated(self): - @dec.deprecated(True) - def non_deprecated_func(): - pass - - @dec.deprecated() - def deprecated_func(): - import warnings - warnings.warn("TEST: deprecated func", DeprecationWarning) - - @dec.deprecated() - def deprecated_func2(): - import warnings - warnings.warn("AHHHH") - raise ValueError - - @dec.deprecated() - def deprecated_func3(): - import warnings - warnings.warn("AHHHH") - - # marked as deprecated, but does not raise DeprecationWarning - assert_raises(AssertionError, non_deprecated_func) - # should be silent - deprecated_func() - with warnings.catch_warnings(record=True): - warnings.simplefilter("always") # do not propagate unrelated warnings - # fails if deprecated decorator just disables test. See #1453. - assert_raises(ValueError, deprecated_func2) - # warning is not a DeprecationWarning - assert_raises(AssertionError, deprecated_func3) - - def test_parametrize(self): - # dec.parametrize assumes that it is being run by nose. Because - # we are running under pytest, we need to explicitly check the - # results. - @dec.parametrize('base, power, expected', - [(1, 1, 1), - (2, 1, 2), - (2, 2, 4)]) - def check_parametrize(base, power, expected): - assert_(base**power == expected) - - count = 0 - for test in check_parametrize(): - test[0](*test[1:]) - count += 1 - assert_(count == 3) diff --git a/numpy/testing/tests/test_doctesting.py b/numpy/testing/tests/test_doctesting.py index b77cd93e0b5c..92c2156d814a 100644 --- a/numpy/testing/tests/test_doctesting.py +++ b/numpy/testing/tests/test_doctesting.py @@ -1,8 +1,6 @@ """ Doctests for NumPy-specific nose/doctest modifications """ -from __future__ import division, absolute_import, print_function - #FIXME: None of these tests is run, because 'check' is not a recognized # testing prefix. diff --git a/numpy/testing/tests/test_utils.py b/numpy/testing/tests/test_utils.py index 465c217d4fce..31d2cdc76b3e 100644 --- a/numpy/testing/tests/test_utils.py +++ b/numpy/testing/tests/test_utils.py @@ -1,10 +1,7 @@ -from __future__ import division, absolute_import, print_function - import warnings import sys import os import itertools -import textwrap import pytest import weakref @@ -17,20 +14,17 @@ clear_and_catch_warnings, suppress_warnings, assert_string_equal, assert_, tempdir, temppath, assert_no_gc_cycles, HAS_REFCOUNT ) +from numpy.core.overrides import ARRAY_FUNCTION_ENABLED -class _GenericTest(object): +class _GenericTest: def _test_equal(self, a, b): self._assert_func(a, b) def _test_not_equal(self, a, b): - try: + with assert_raises(AssertionError): self._assert_func(a, b) - except AssertionError: - pass - else: - raise AssertionError("a and b are found equal but are not") def test_array_rank1_eq(self): """Test two equal array of rank 1 are found equal.""" @@ -93,6 +87,21 @@ def foo(t): for t in ['S1', 'U1']: foo(t) + def test_0_ndim_array(self): + x = np.array(473963742225900817127911193656584771) + y = np.array(18535119325151578301457182298393896) + assert_raises(AssertionError, self._assert_func, x, y) + + y = x + self._assert_func(x, y) + + x = np.array(43) + y = np.array(10) + assert_raises(AssertionError, self._assert_func, x, y) + + y = x + self._assert_func(x, y) + def test_generic_rank3(self): """Test rank 3 array for all dtypes.""" def foo(t): @@ -162,8 +171,44 @@ def test_masked_nan_inf(self): self._test_equal(a, b) self._test_equal(b, a) + def test_subclass_that_overrides_eq(self): + # While we cannot guarantee testing functions will always work for + # subclasses, the tests should ideally rely only on subclasses having + # comparison operators, not on them being able to store booleans + # (which, e.g., astropy Quantity cannot usefully do). See gh-8452. + class MyArray(np.ndarray): + def __eq__(self, other): + return bool(np.equal(self, other).all()) + + def __ne__(self, other): + return not self == other + + a = np.array([1., 2.]).view(MyArray) + b = np.array([2., 3.]).view(MyArray) + assert_(type(a == a), bool) + assert_(a == a) + assert_(a != b) + self._test_equal(a, a) + self._test_not_equal(a, b) + self._test_not_equal(b, a) + + @pytest.mark.skipif( + not ARRAY_FUNCTION_ENABLED, reason='requires __array_function__') + def test_subclass_that_does_not_implement_npall(self): + class MyArray(np.ndarray): + def __array_function__(self, *args, **kwargs): + return NotImplemented + + a = np.array([1., 2.]).view(MyArray) + b = np.array([2., 3.]).view(MyArray) + with assert_raises(TypeError): + np.all(a) + self._test_equal(a, a) + self._test_not_equal(a, b) + self._test_not_equal(b, a) + -class TestBuildErrorMessage(object): +class TestBuildErrorMessage: def test_build_err_msg_defaults(self): x = np.array([1.00001, 2.00002, 3.00003]) @@ -295,25 +340,12 @@ def test_complex(self): self._assert_func(x, x) self._test_not_equal(x, y) - def test_error_message(self): - try: - self._assert_func(np.array([1, 2]), np.array([[1, 2]])) - except AssertionError as e: - msg = str(e) - msg2 = msg.replace("shapes (2L,), (1L, 2L)", "shapes (2,), (1, 2)") - msg_reference = textwrap.dedent("""\ - - Arrays are not equal - - (shapes (2,), (1, 2) mismatch) - x: array([1, 2]) - y: array([[1, 2]])""") - try: - assert_equal(msg, msg_reference) - except AssertionError: - assert_equal(msg2, msg_reference) - else: - raise AssertionError("Did not raise") + def test_object(self): + #gh-12942 + import datetime + a = np.array([datetime.datetime(2000, 1, 1), + datetime.datetime(2000, 1, 2)]) + self._test_not_equal(a, a[::-1]) class TestArrayAlmostEqual(_GenericTest): @@ -402,10 +434,10 @@ def test_subclass_that_cannot_be_bool(self): # (which, e.g., astropy Quantity cannot usefully do). See gh-8452. class MyArray(np.ndarray): def __eq__(self, other): - return super(MyArray, self).__eq__(other).view(np.ndarray) + return super().__eq__(other).view(np.ndarray) def __lt__(self, other): - return super(MyArray, self).__lt__(other).view(np.ndarray) + return super().__lt__(other).view(np.ndarray) def all(self, *args, **kwargs): raise NotImplementedError @@ -473,29 +505,78 @@ def test_complex(self): self._test_not_equal(x, z) def test_error_message(self): - """Check the message is formatted correctly for the decimal value""" + """Check the message is formatted correctly for the decimal value. + Also check the message when input includes inf or nan (gh12200)""" x = np.array([1.00000000001, 2.00000000002, 3.00003]) y = np.array([1.00000000002, 2.00000000003, 3.00004]) - # test with a different amount of decimal digits - # note that we only check for the formatting of the arrays themselves - b = ('x: array([1.00000000001, 2.00000000002, 3.00003 ' - ' ])\n y: array([1.00000000002, 2.00000000003, 3.00004 ])') - try: + # Test with a different amount of decimal digits + with pytest.raises(AssertionError) as exc_info: self._assert_func(x, y, decimal=12) - except AssertionError as e: - # remove anything that's not the array string - assert_equal(str(e).split('%)\n ')[1], b) - - # with the default value of decimal digits, only the 3rd element differs - # note that we only check for the formatting of the arrays themselves - b = ('x: array([1. , 2. , 3.00003])\n y: array([1. , ' - '2. , 3.00004])') - try: + msgs = str(exc_info.value).split('\n') + assert_equal(msgs[3], 'Mismatched elements: 3 / 3 (100%)') + assert_equal(msgs[4], 'Max absolute difference: 1.e-05') + assert_equal(msgs[5], 'Max relative difference: 3.33328889e-06') + assert_equal( + msgs[6], + ' x: array([1.00000000001, 2.00000000002, 3.00003 ])') + assert_equal( + msgs[7], + ' y: array([1.00000000002, 2.00000000003, 3.00004 ])') + + # With the default value of decimal digits, only the 3rd element + # differs. Note that we only check for the formatting of the arrays + # themselves. + with pytest.raises(AssertionError) as exc_info: + self._assert_func(x, y) + msgs = str(exc_info.value).split('\n') + assert_equal(msgs[3], 'Mismatched elements: 1 / 3 (33.3%)') + assert_equal(msgs[4], 'Max absolute difference: 1.e-05') + assert_equal(msgs[5], 'Max relative difference: 3.33328889e-06') + assert_equal(msgs[6], ' x: array([1. , 2. , 3.00003])') + assert_equal(msgs[7], ' y: array([1. , 2. , 3.00004])') + + # Check the error message when input includes inf + x = np.array([np.inf, 0]) + y = np.array([np.inf, 1]) + with pytest.raises(AssertionError) as exc_info: + self._assert_func(x, y) + msgs = str(exc_info.value).split('\n') + assert_equal(msgs[3], 'Mismatched elements: 1 / 2 (50%)') + assert_equal(msgs[4], 'Max absolute difference: 1.') + assert_equal(msgs[5], 'Max relative difference: 1.') + assert_equal(msgs[6], ' x: array([inf, 0.])') + assert_equal(msgs[7], ' y: array([inf, 1.])') + + # Check the error message when dividing by zero + x = np.array([1, 2]) + y = np.array([0, 0]) + with pytest.raises(AssertionError) as exc_info: + self._assert_func(x, y) + msgs = str(exc_info.value).split('\n') + assert_equal(msgs[3], 'Mismatched elements: 2 / 2 (100%)') + assert_equal(msgs[4], 'Max absolute difference: 2') + assert_equal(msgs[5], 'Max relative difference: inf') + + def test_error_message_2(self): + """Check the message is formatted correctly when either x or y is a scalar.""" + x = 2 + y = np.ones(20) + with pytest.raises(AssertionError) as exc_info: + self._assert_func(x, y) + msgs = str(exc_info.value).split('\n') + assert_equal(msgs[3], 'Mismatched elements: 20 / 20 (100%)') + assert_equal(msgs[4], 'Max absolute difference: 1.') + assert_equal(msgs[5], 'Max relative difference: 1.') + + y = 2 + x = np.ones(20) + with pytest.raises(AssertionError) as exc_info: self._assert_func(x, y) - except AssertionError as e: - # remove anything that's not the array string - assert_equal(str(e).split('%)\n ')[1], b) + msgs = str(exc_info.value).split('\n') + assert_equal(msgs[3], 'Mismatched elements: 20 / 20 (100%)') + assert_equal(msgs[4], 'Max absolute difference: 1.') + assert_equal(msgs[5], 'Max relative difference: 0.5') def test_subclass_that_cannot_be_bool(self): # While we cannot guarantee testing functions will always work for @@ -504,10 +585,10 @@ def test_subclass_that_cannot_be_bool(self): # (which, e.g., astropy Quantity cannot usefully do). See gh-8452. class MyArray(np.ndarray): def __eq__(self, other): - return super(MyArray, self).__eq__(other).view(np.ndarray) + return super().__eq__(other).view(np.ndarray) def __lt__(self, other): - return super(MyArray, self).__lt__(other).view(np.ndarray) + return super().__lt__(other).view(np.ndarray) def all(self, *args, **kwargs): raise NotImplementedError @@ -516,14 +597,14 @@ def all(self, *args, **kwargs): self._assert_func(a, a) -class TestApproxEqual(object): +class TestApproxEqual: def setup(self): self._assert_func = assert_approx_equal - def test_simple_arrays(self): - x = np.array([1234.22]) - y = np.array([1234.23]) + def test_simple_0d_arrays(self): + x = np.array(1234.22) + y = np.array(1234.23) self._assert_func(x, y, significant=5) self._assert_func(x, y, significant=6) @@ -559,7 +640,7 @@ def test_nan_items(self): assert_raises(AssertionError, lambda: self._assert_func(ainf, anan)) -class TestArrayAssertLess(object): +class TestArrayAssertLess: def setup(self): self._assert_func = assert_array_less @@ -669,7 +750,7 @@ def test_inf_compare_array(self): @pytest.mark.skip(reason="The raises decorator depends on Nose") -class TestRaises(object): +class TestRaises: def setup(self): class MyException(Exception): @@ -703,7 +784,7 @@ def test_catch_no_raise(self): raise AssertionError("should have raised an AssertionError") -class TestWarns(object): +class TestWarns: def test_warn(self): def f(): @@ -754,7 +835,7 @@ def f(): raise AssertionError("wrong warning caught by assert_warn") -class TestAssertAllclose(object): +class TestAssertAllclose: def test_simple(self): x = 1e-3 @@ -784,12 +865,13 @@ def test_min_int(self): def test_report_fail_percentage(self): a = np.array([1, 1, 1, 1]) b = np.array([1, 1, 1, 2]) - try: + + with pytest.raises(AssertionError) as exc_info: assert_allclose(a, b) - msg = '' - except AssertionError as exc: - msg = exc.args[0] - assert_("mismatch 25.0%" in msg) + msg = str(exc_info.value) + assert_('Mismatched elements: 1 / 4 (25%)\n' + 'Max absolute difference: 1\n' + 'Max relative difference: 0.5' in msg) def test_equal_nan(self): a = np.array([np.nan]) @@ -813,8 +895,22 @@ def test_equal_nan_default(self): assert_array_less(a, b) assert_allclose(a, b) + def test_report_max_relative_error(self): + a = np.array([0, 1]) + b = np.array([0, 2]) + + with pytest.raises(AssertionError) as exc_info: + assert_allclose(a, b) + msg = str(exc_info.value) + assert_('Max relative difference: 0.5' in msg) + + def test_timedelta(self): + # see gh-18286 + a = np.array([[1, 2, 3, "NaT"]], dtype="m8[ns]") + assert_allclose(a, a) -class TestArrayAlmostEqualNulp(object): + +class TestArrayAlmostEqualNulp: def test_float64_pass(self): # The number of units of least precision @@ -850,6 +946,17 @@ def test_float64_fail(self): assert_raises(AssertionError, assert_array_almost_equal_nulp, x, y, nulp) + def test_float64_ignore_nan(self): + # Ignore ULP differences between various NAN's + # Note that MIPS may reverse quiet and signaling nans + # so we use the builtin version as a base. + offset = np.uint64(0xffffffff) + nan1_i64 = np.array(np.nan, dtype=np.float64).view(np.uint64) + nan2_i64 = nan1_i64 ^ offset # nan payload on MIPS is all ones. + nan1_f64 = nan1_i64.view(np.float64) + nan2_f64 = nan2_i64.view(np.float64) + assert_array_max_ulp(nan1_f64, nan2_f64, 0) + def test_float32_pass(self): nulp = 5 x = np.linspace(-20, 20, 50, dtype=np.float32) @@ -880,6 +987,17 @@ def test_float32_fail(self): assert_raises(AssertionError, assert_array_almost_equal_nulp, x, y, nulp) + def test_float32_ignore_nan(self): + # Ignore ULP differences between various NAN's + # Note that MIPS may reverse quiet and signaling nans + # so we use the builtin version as a base. + offset = np.uint32(0xffff) + nan1_i32 = np.array(np.nan, dtype=np.float32).view(np.uint32) + nan2_i32 = nan1_i32 ^ offset # nan payload on MIPS is all ones. + nan1_f32 = nan1_i32.view(np.float32) + nan2_f32 = nan2_i32.view(np.float32) + assert_array_max_ulp(nan1_f32, nan2_f32, 0) + def test_float16_pass(self): nulp = 5 x = np.linspace(-4, 4, 10, dtype=np.float16) @@ -910,6 +1028,17 @@ def test_float16_fail(self): assert_raises(AssertionError, assert_array_almost_equal_nulp, x, y, nulp) + def test_float16_ignore_nan(self): + # Ignore ULP differences between various NAN's + # Note that MIPS may reverse quiet and signaling nans + # so we use the builtin version as a base. + offset = np.uint16(0xff) + nan1_i16 = np.array(np.nan, dtype=np.float16).view(np.uint16) + nan2_i16 = nan1_i16 ^ offset # nan payload on MIPS is all ones. + nan1_f16 = nan1_i16.view(np.float16) + nan2_f16 = nan2_i16.view(np.float16) + assert_array_max_ulp(nan1_f16, nan2_f16, 0) + def test_complex128_pass(self): nulp = 5 x = np.linspace(-20, 20, 50, dtype=np.float64) @@ -1011,7 +1140,7 @@ def test_complex64_fail(self): xi, y + y*1j, nulp) -class TestULP(object): +class TestULP: def test_equal(self): x = np.random.randn(10) @@ -1067,24 +1196,39 @@ def test_nan(self): maxulp=maxulp)) -class TestStringEqual(object): +class TestStringEqual: def test_simple(self): assert_string_equal("hello", "hello") assert_string_equal("hello\nmultiline", "hello\nmultiline") - try: + with pytest.raises(AssertionError) as exc_info: assert_string_equal("foo\nbar", "hello\nbar") - except AssertionError as exc: - assert_equal(str(exc), "Differences in strings:\n- foo\n+ hello") - else: - raise AssertionError("exception not raised") + msg = str(exc_info.value) + assert_equal(msg, "Differences in strings:\n- foo\n+ hello") assert_raises(AssertionError, lambda: assert_string_equal("foo", "hello")) + def test_regex(self): + assert_string_equal("a+*b", "a+*b") + + assert_raises(AssertionError, + lambda: assert_string_equal("aaa", "a+b")) + def assert_warn_len_equal(mod, n_in_context, py34=None, py37=None): - mod_warns = mod.__warningregistry__ + try: + mod_warns = mod.__warningregistry__ + except AttributeError: + # the lack of a __warningregistry__ + # attribute means that no warning has + # occurred; this can be triggered in + # a parallel test scenario, while in + # a serial test scenario an initial + # warning (and therefore the attribute) + # are always created first + mod_warns = {} + num_warns = len(mod_warns) # Python 3.4 appears to clear any pre-existing warnings of the same type, # when raising warnings inside a catch_warnings block. So, there is a @@ -1101,11 +1245,38 @@ def assert_warn_len_equal(mod, n_in_context, py34=None, py37=None): if sys.version_info[:2] >= (3, 7): if py37 is not None: n_in_context = py37 - elif sys.version_info[:2] >= (3, 4): + else: if py34 is not None: n_in_context = py34 assert_equal(num_warns, n_in_context) +def test_warn_len_equal_call_scenarios(): + # assert_warn_len_equal is called under + # varying circumstances depending on serial + # vs. parallel test scenarios; this test + # simply aims to probe both code paths and + # check that no assertion is uncaught + + # parallel scenario -- no warning issued yet + class mod: + pass + + mod_inst = mod() + + assert_warn_len_equal(mod=mod_inst, + n_in_context=0) + + # serial test scenario -- the __warningregistry__ + # attribute should be present + class mod: + def __init__(self): + self.__warningregistry__ = {'warning1':1, + 'warning2':2} + + mod_inst = mod() + assert_warn_len_equal(mod=mod_inst, + n_in_context=2) + def _get_fresh_mod(): # Get this module, with warning registry empty @@ -1343,7 +1514,7 @@ def test_tempdir(): def test_temppath(): with temppath() as fpath: - with open(fpath, 'w') as f: + with open(fpath, 'w'): pass assert_(not os.path.isfile(fpath)) @@ -1372,7 +1543,7 @@ def test_clear_and_catch_warnings_inherit(): @pytest.mark.skipif(not HAS_REFCOUNT, reason="Python lacks refcounts") -class TestAssertNoGcCycles(object): +class TestAssertNoGcCycles: """ Test assert_no_gc_cycles """ def test_passes(self): def no_cycle(): @@ -1385,7 +1556,6 @@ def no_cycle(): assert_no_gc_cycles(no_cycle) - def test_asserts(self): def make_cycle(): a = [] @@ -1400,14 +1570,14 @@ def make_cycle(): with assert_raises(AssertionError): assert_no_gc_cycles(make_cycle) - + @pytest.mark.slow def test_fails(self): """ Test that in cases where the garbage cannot be collected, we raise an error, instead of hanging forever trying to clear it. """ - class ReferenceCycleInDel(object): + class ReferenceCycleInDel: """ An object that not only contains a reference cycle, but creates new cycles whenever it's garbage-collected and its __del__ runs diff --git a/numpy/testing/utils.py b/numpy/testing/utils.py index 184adcc74c68..20a883304342 100644 --- a/numpy/testing/utils.py +++ b/numpy/testing/utils.py @@ -3,27 +3,27 @@ set of tools """ -from __future__ import division, absolute_import, print_function - import warnings -# 2018-04-04, numpy 1.15.0 -warnings.warn("Importing from numpy.testing.utils is deprecated, " - "import from numpy.testing instead.", - ImportWarning, stacklevel=2) +# 2018-04-04, numpy 1.15.0 ImportWarning +# 2019-09-18, numpy 1.18.0 DeprecatonWarning (changed) +warnings.warn("Importing from numpy.testing.utils is deprecated " + "since 1.15.0, import from numpy.testing instead.", + DeprecationWarning, stacklevel=2) from ._private.utils import * +from ._private.utils import _assert_valid_refcount, _gen_alignment_data __all__ = [ 'assert_equal', 'assert_almost_equal', 'assert_approx_equal', 'assert_array_equal', 'assert_array_less', 'assert_string_equal', 'assert_array_almost_equal', 'assert_raises', 'build_err_msg', 'decorate_methods', 'jiffies', 'memusage', 'print_assert_equal', - 'raises', 'rand', 'rundocs', 'runstring', 'verbose', 'measure', + 'raises', 'rundocs', 'runstring', 'verbose', 'measure', 'assert_', 'assert_array_almost_equal_nulp', 'assert_raises_regex', 'assert_array_max_ulp', 'assert_warns', 'assert_no_warnings', 'assert_allclose', 'IgnoreException', 'clear_and_catch_warnings', 'SkipTest', 'KnownFailureException', 'temppath', 'tempdir', 'IS_PYPY', 'HAS_REFCOUNT', 'suppress_warnings', 'assert_array_compare', - '_assert_valid_refcount', '_gen_alignment_data', 'assert_no_gc_cycles' + 'assert_no_gc_cycles' ] diff --git a/numpy/tests/test__all__.py b/numpy/tests/test__all__.py new file mode 100644 index 000000000000..e44bda3d58ab --- /dev/null +++ b/numpy/tests/test__all__.py @@ -0,0 +1,9 @@ + +import collections +import numpy as np + + +def test_no_duplicates_in_np__all__(): + # Regression test for gh-10198. + dups = {k: v for k, v in collections.Counter(np.__all__).items() if v > 1} + assert len(dups) == 0 diff --git a/numpy/tests/test_ctypeslib.py b/numpy/tests/test_ctypeslib.py index 75ce9c8ca029..1ea0837008b7 100644 --- a/numpy/tests/test_ctypeslib.py +++ b/numpy/tests/test_ctypeslib.py @@ -1,7 +1,7 @@ -from __future__ import division, absolute_import, print_function - import sys import pytest +import weakref +from pathlib import Path import numpy as np from numpy.ctypeslib import ndpointer, load_library, as_array @@ -9,32 +9,44 @@ from numpy.testing import assert_, assert_array_equal, assert_raises, assert_equal try: + import ctypes +except ImportError: + ctypes = None +else: cdll = None + test_cdll = None if hasattr(sys, 'gettotalrefcount'): try: - cdll = load_library('multiarray_d', np.core.multiarray.__file__) + cdll = load_library('_multiarray_umath_d', np.core._multiarray_umath.__file__) + except OSError: + pass + try: + test_cdll = load_library('_multiarray_tests', np.core._multiarray_tests.__file__) except OSError: pass if cdll is None: - cdll = load_library('multiarray', np.core.multiarray.__file__) - _HAS_CTYPE = True -except ImportError: - _HAS_CTYPE = False + cdll = load_library('_multiarray_umath', np.core._multiarray_umath.__file__) + if test_cdll is None: + test_cdll = load_library('_multiarray_tests', np.core._multiarray_tests.__file__) + c_forward_pointer = test_cdll.forward_pointer -@pytest.mark.skipif(not _HAS_CTYPE, + +@pytest.mark.skipif(ctypes is None, reason="ctypes not available in this python") @pytest.mark.skipif(sys.platform == 'cygwin', reason="Known to fail on cygwin") -class TestLoadLibrary(object): +class TestLoadLibrary: def test_basic(self): - try: - # Should succeed - load_library('multiarray', np.core.multiarray.__file__) - except ImportError as e: - msg = ("ctypes is not available on this python: skipping the test" - " (import error was: %s)" % str(e)) - print(msg) + loader_path = np.core._multiarray_umath.__file__ + + out1 = load_library('_multiarray_umath', loader_path) + out2 = load_library(Path('_multiarray_umath'), loader_path) + out3 = load_library('_multiarray_umath', Path(loader_path)) + out4 = load_library(b'_multiarray_umath', loader_path) + + assert isinstance(out1, ctypes.CDLL) + assert out1 is out2 is out3 is out4 def test_basic2(self): # Regression for #801: load_library with a full library name @@ -43,7 +55,7 @@ def test_basic2(self): try: so = get_shared_lib_extension(is_python_ext=True) # Should succeed - load_library('multiarray%s' % so, np.core.multiarray.__file__) + load_library('_multiarray_umath%s' % so, np.core._multiarray_umath.__file__) except ImportError: print("No distutils available, skipping test.") except ImportError as e: @@ -52,7 +64,7 @@ def test_basic2(self): print(msg) -class TestNdpointer(object): +class TestNdpointer: def test_dtype(self): dt = np.intc p = ndpointer(dtype=dt) @@ -108,14 +120,74 @@ def test_flags(self): assert_raises(TypeError, p.from_param, np.array([[1, 2], [3, 4]])) def test_cache(self): - a1 = ndpointer(dtype=np.float64) - a2 = ndpointer(dtype=np.float64) - assert_(a1 == a2) + assert_(ndpointer(dtype=np.float64) is ndpointer(dtype=np.float64)) + + # shapes are normalized + assert_(ndpointer(shape=2) is ndpointer(shape=(2,))) + + # 1.12 <= v < 1.16 had a bug that made these fail + assert_(ndpointer(shape=2) is not ndpointer(ndim=2)) + assert_(ndpointer(ndim=2) is not ndpointer(shape=2)) + +@pytest.mark.skipif(ctypes is None, + reason="ctypes not available on this python installation") +class TestNdpointerCFunc: + def test_arguments(self): + """ Test that arguments are coerced from arrays """ + c_forward_pointer.restype = ctypes.c_void_p + c_forward_pointer.argtypes = (ndpointer(ndim=2),) + + c_forward_pointer(np.zeros((2, 3))) + # too many dimensions + assert_raises( + ctypes.ArgumentError, c_forward_pointer, np.zeros((2, 3, 4))) + + @pytest.mark.parametrize( + 'dt', [ + float, + np.dtype(dict( + formats=['<i4', '<i4'], + names=['a', 'b'], + offsets=[0, 2], + itemsize=6 + )) + ], ids=[ + 'float', + 'overlapping-fields' + ] + ) + def test_return(self, dt): + """ Test that return values are coerced to arrays """ + arr = np.zeros((2, 3), dt) + ptr_type = ndpointer(shape=arr.shape, dtype=arr.dtype) + + c_forward_pointer.restype = ptr_type + c_forward_pointer.argtypes = (ptr_type,) + + # check that the arrays are equivalent views on the same data + arr2 = c_forward_pointer(arr) + assert_equal(arr2.dtype, arr.dtype) + assert_equal(arr2.shape, arr.shape) + assert_equal( + arr2.__array_interface__['data'], + arr.__array_interface__['data'] + ) + + def test_vague_return_value(self): + """ Test that vague ndpointer return values do not promote to arrays """ + arr = np.zeros((2, 3)) + ptr_type = ndpointer(dtype=arr.dtype) + c_forward_pointer.restype = ptr_type + c_forward_pointer.argtypes = (ptr_type,) -@pytest.mark.skipif(not _HAS_CTYPE, + ret = c_forward_pointer(arr) + assert_(isinstance(ret, ptr_type)) + + +@pytest.mark.skipif(ctypes is None, reason="ctypes not available on this python installation") -class TestAsArray(object): +class TestAsArray: def test_array(self): from ctypes import c_int @@ -170,3 +242,127 @@ def check(x): check(as_array(pointer(c_array), shape=())) check(as_array(pointer(c_array[0]), shape=(2,))) check(as_array(pointer(c_array[0][0]), shape=(2, 3))) + + def test_reference_cycles(self): + # related to gh-6511 + import ctypes + + # create array to work with + # don't use int/long to avoid running into bpo-10746 + N = 100 + a = np.arange(N, dtype=np.short) + + # get pointer to array + pnt = np.ctypeslib.as_ctypes(a) + + with np.testing.assert_no_gc_cycles(): + # decay the array above to a pointer to its first element + newpnt = ctypes.cast(pnt, ctypes.POINTER(ctypes.c_short)) + # and construct an array using this data + b = np.ctypeslib.as_array(newpnt, (N,)) + # now delete both, which should cleanup both objects + del newpnt, b + + def test_segmentation_fault(self): + arr = np.zeros((224, 224, 3)) + c_arr = np.ctypeslib.as_ctypes(arr) + arr_ref = weakref.ref(arr) + del arr + + # check the reference wasn't cleaned up + assert_(arr_ref() is not None) + + # check we avoid the segfault + c_arr[0][0][0] + + +@pytest.mark.skipif(ctypes is None, + reason="ctypes not available on this python installation") +class TestAsCtypesType: + """ Test conversion from dtypes to ctypes types """ + def test_scalar(self): + dt = np.dtype('<u2') + ct = np.ctypeslib.as_ctypes_type(dt) + assert_equal(ct, ctypes.c_uint16.__ctype_le__) + + dt = np.dtype('>u2') + ct = np.ctypeslib.as_ctypes_type(dt) + assert_equal(ct, ctypes.c_uint16.__ctype_be__) + + dt = np.dtype('u2') + ct = np.ctypeslib.as_ctypes_type(dt) + assert_equal(ct, ctypes.c_uint16) + + def test_subarray(self): + dt = np.dtype((np.int32, (2, 3))) + ct = np.ctypeslib.as_ctypes_type(dt) + assert_equal(ct, 2 * (3 * ctypes.c_int32)) + + def test_structure(self): + dt = np.dtype([ + ('a', np.uint16), + ('b', np.uint32), + ]) + + ct = np.ctypeslib.as_ctypes_type(dt) + assert_(issubclass(ct, ctypes.Structure)) + assert_equal(ctypes.sizeof(ct), dt.itemsize) + assert_equal(ct._fields_, [ + ('a', ctypes.c_uint16), + ('b', ctypes.c_uint32), + ]) + + def test_structure_aligned(self): + dt = np.dtype([ + ('a', np.uint16), + ('b', np.uint32), + ], align=True) + + ct = np.ctypeslib.as_ctypes_type(dt) + assert_(issubclass(ct, ctypes.Structure)) + assert_equal(ctypes.sizeof(ct), dt.itemsize) + assert_equal(ct._fields_, [ + ('a', ctypes.c_uint16), + ('', ctypes.c_char * 2), # padding + ('b', ctypes.c_uint32), + ]) + + def test_union(self): + dt = np.dtype(dict( + names=['a', 'b'], + offsets=[0, 0], + formats=[np.uint16, np.uint32] + )) + + ct = np.ctypeslib.as_ctypes_type(dt) + assert_(issubclass(ct, ctypes.Union)) + assert_equal(ctypes.sizeof(ct), dt.itemsize) + assert_equal(ct._fields_, [ + ('a', ctypes.c_uint16), + ('b', ctypes.c_uint32), + ]) + + def test_padded_union(self): + dt = np.dtype(dict( + names=['a', 'b'], + offsets=[0, 0], + formats=[np.uint16, np.uint32], + itemsize=5, + )) + + ct = np.ctypeslib.as_ctypes_type(dt) + assert_(issubclass(ct, ctypes.Union)) + assert_equal(ctypes.sizeof(ct), dt.itemsize) + assert_equal(ct._fields_, [ + ('a', ctypes.c_uint16), + ('b', ctypes.c_uint32), + ('', ctypes.c_char * 5), # padding + ]) + + def test_overlapping(self): + dt = np.dtype(dict( + names=['a', 'b'], + offsets=[0, 2], + formats=[np.uint32, np.uint32] + )) + assert_raises(NotImplementedError, np.ctypeslib.as_ctypes_type, dt) diff --git a/numpy/tests/test_matlib.py b/numpy/tests/test_matlib.py index 38a7e39dfb69..0e93c4848d75 100644 --- a/numpy/tests/test_matlib.py +++ b/numpy/tests/test_matlib.py @@ -1,13 +1,3 @@ -from __future__ import division, absolute_import, print_function - -# As we are testing matrices, we ignore its PendingDeprecationWarnings -try: - import pytest - pytestmark = pytest.mark.filterwarnings( - 'ignore:the matrix subclass is not:PendingDeprecationWarning') -except ImportError: - pass - import numpy as np import numpy.matlib from numpy.testing import assert_array_equal, assert_ diff --git a/numpy/tests/test_numpy_version.py b/numpy/tests/test_numpy_version.py index 7fac8fd22ead..bccbcb8e9cf7 100644 --- a/numpy/tests/test_numpy_version.py +++ b/numpy/tests/test_numpy_version.py @@ -1,5 +1,20 @@ -from __future__ import division, absolute_import, print_function +""" +Check the numpy version is valid. +Note that a development version is marked by the presence of 'dev0' or '+' +in the version string, all else is treated as a release. The version string +itself is set from the output of ``git describe`` which relies on tags. + +Examples +-------- + +Valid Development: 1.22.0.dev0 1.22.0.dev0+5-g7999db4df2 1.22.0+5-g7999db4df2 +Valid Release: 1.21.0.rc1, 1.21.0.b1, 1.21.0 +Invalid: 1.22.0.dev, 1.22.0.dev0-5-g7999db4dfB, 1.21.0.d1, 1.21.a + +Note that a release is determined by the version string, which in turn +is controlled by the result of the ``git describe`` command. +""" import re import numpy as np @@ -9,11 +24,21 @@ def test_valid_numpy_version(): # Verify that the numpy version is a valid one (no .post suffix or other # nonsense). See gh-6431 for an issue caused by an invalid version. - version_pattern = r"^[0-9]+\.[0-9]+\.[0-9]+(|a[0-9]|b[0-9]|rc[0-9])" - dev_suffix = r"(\.dev0\+([0-9a-f]{7}|Unknown))" + version_pattern = r"^[0-9]+\.[0-9]+\.[0-9]+(a[0-9]|b[0-9]|rc[0-9]|)" + dev_suffix = r"(\.dev0|)(\+[0-9]*\.g[0-9a-f]+|)" if np.version.release: - res = re.match(version_pattern, np.__version__) + res = re.match(version_pattern + '$', np.__version__) else: - res = re.match(version_pattern + dev_suffix, np.__version__) + res = re.match(version_pattern + dev_suffix + '$', np.__version__) assert_(res is not None, np.__version__) + + +def test_short_version(): + # Check numpy.short_version actually exists + if np.version.release: + assert_(np.__version__ == np.version.short_version, + "short_version mismatch in release version") + else: + assert_(np.__version__.split("+")[0] == np.version.short_version, + "short_version mismatch in development version") diff --git a/numpy/tests/test_public_api.py b/numpy/tests/test_public_api.py new file mode 100644 index 000000000000..bb15e10e8241 --- /dev/null +++ b/numpy/tests/test_public_api.py @@ -0,0 +1,501 @@ +import sys +import sysconfig +import subprocess +import pkgutil +import types +import importlib +import warnings + +import numpy as np +import numpy +import pytest + +try: + import ctypes +except ImportError: + ctypes = None + + +def check_dir(module, module_name=None): + """Returns a mapping of all objects with the wrong __module__ attribute.""" + if module_name is None: + module_name = module.__name__ + results = {} + for name in dir(module): + item = getattr(module, name) + if (hasattr(item, '__module__') and hasattr(item, '__name__') + and item.__module__ != module_name): + results[name] = item.__module__ + '.' + item.__name__ + return results + + +def test_numpy_namespace(): + # None of these objects are publicly documented to be part of the main + # NumPy namespace (some are useful though, others need to be cleaned up) + undocumented = { + 'Tester': 'numpy.testing._private.nosetester.NoseTester', + '_add_newdoc_ufunc': 'numpy.core._multiarray_umath._add_newdoc_ufunc', + 'add_docstring': 'numpy.core._multiarray_umath.add_docstring', + 'add_newdoc': 'numpy.core.function_base.add_newdoc', + 'add_newdoc_ufunc': 'numpy.core._multiarray_umath._add_newdoc_ufunc', + 'byte_bounds': 'numpy.lib.utils.byte_bounds', + 'compare_chararrays': 'numpy.core._multiarray_umath.compare_chararrays', + 'deprecate': 'numpy.lib.utils.deprecate', + 'deprecate_with_doc': 'numpy.lib.utils.deprecate_with_doc', + 'disp': 'numpy.lib.function_base.disp', + 'fastCopyAndTranspose': 'numpy.core._multiarray_umath._fastCopyAndTranspose', + 'get_array_wrap': 'numpy.lib.shape_base.get_array_wrap', + 'get_include': 'numpy.lib.utils.get_include', + 'recfromcsv': 'numpy.lib.npyio.recfromcsv', + 'recfromtxt': 'numpy.lib.npyio.recfromtxt', + 'safe_eval': 'numpy.lib.utils.safe_eval', + 'set_string_function': 'numpy.core.arrayprint.set_string_function', + 'show_config': 'numpy.__config__.show', + 'who': 'numpy.lib.utils.who', + } + # We override dir to not show these members + allowlist = undocumented + bad_results = check_dir(np) + # pytest gives better error messages with the builtin assert than with + # assert_equal + assert bad_results == allowlist + + +@pytest.mark.parametrize('name', ['testing', 'Tester']) +def test_import_lazy_import(name): + """Make sure we can actually use the modules we lazy load. + + While not exported as part of the public API, it was accessible. With the + use of __getattr__ and __dir__, this isn't always true It can happen that + an infinite recursion may happen. + + This is the only way I found that would force the failure to appear on the + badly implemented code. + + We also test for the presence of the lazily imported modules in dir + + """ + exe = (sys.executable, '-c', "import numpy; numpy." + name) + result = subprocess.check_output(exe) + assert not result + + # Make sure they are still in the __dir__ + assert name in dir(np) + + +def test_dir_testing(): + """Assert that output of dir has only one "testing/tester" + attribute without duplicate""" + assert len(dir(np)) == len(set(dir(np))) + + +def test_numpy_linalg(): + bad_results = check_dir(np.linalg) + assert bad_results == {} + + +def test_numpy_fft(): + bad_results = check_dir(np.fft) + assert bad_results == {} + + +@pytest.mark.skipif(ctypes is None, + reason="ctypes not available in this python") +def test_NPY_NO_EXPORT(): + cdll = ctypes.CDLL(np.core._multiarray_tests.__file__) + # Make sure an arbitrary NPY_NO_EXPORT function is actually hidden + f = getattr(cdll, 'test_not_exported', None) + assert f is None, ("'test_not_exported' is mistakenly exported, " + "NPY_NO_EXPORT does not work") + + +# Historically NumPy has not used leading underscores for private submodules +# much. This has resulted in lots of things that look like public modules +# (i.e. things that can be imported as `import numpy.somesubmodule.somefile`), +# but were never intended to be public. The PUBLIC_MODULES list contains +# modules that are either public because they were meant to be, or because they +# contain public functions/objects that aren't present in any other namespace +# for whatever reason and therefore should be treated as public. +# +# The PRIVATE_BUT_PRESENT_MODULES list contains modules that look public (lack +# of underscores) but should not be used. For many of those modules the +# current status is fine. For others it may make sense to work on making them +# private, to clean up our public API and avoid confusion. +PUBLIC_MODULES = ['numpy.' + s for s in [ + "array_api", + "array_api.linalg", + "ctypeslib", + "distutils", + "distutils.cpuinfo", + "distutils.exec_command", + "distutils.misc_util", + "distutils.log", + "distutils.system_info", + "doc", + "doc.constants", + "doc.ufuncs", + "f2py", + "fft", + "lib", + "lib.format", # was this meant to be public? + "lib.mixins", + "lib.recfunctions", + "lib.scimath", + "lib.stride_tricks", + "linalg", + "ma", + "ma.extras", + "ma.mrecords", + "matlib", + "polynomial", + "polynomial.chebyshev", + "polynomial.hermite", + "polynomial.hermite_e", + "polynomial.laguerre", + "polynomial.legendre", + "polynomial.polynomial", + "random", + "testing", + "typing", + "typing.mypy_plugin", + "version", +]] + + +PUBLIC_ALIASED_MODULES = [ + "numpy.char", + "numpy.emath", + "numpy.rec", +] + + +PRIVATE_BUT_PRESENT_MODULES = ['numpy.' + s for s in [ + "compat", + "compat.py3k", + "conftest", + "core", + "core.arrayprint", + "core.defchararray", + "core.einsumfunc", + "core.fromnumeric", + "core.function_base", + "core.getlimits", + "core.memmap", + "core.multiarray", + "core.numeric", + "core.numerictypes", + "core.overrides", + "core.records", + "core.shape_base", + "core.umath", + "core.umath_tests", + "distutils.armccompiler", + "distutils.ccompiler", + 'distutils.ccompiler_opt', + "distutils.command", + "distutils.command.autodist", + "distutils.command.bdist_rpm", + "distutils.command.build", + "distutils.command.build_clib", + "distutils.command.build_ext", + "distutils.command.build_py", + "distutils.command.build_scripts", + "distutils.command.build_src", + "distutils.command.config", + "distutils.command.config_compiler", + "distutils.command.develop", + "distutils.command.egg_info", + "distutils.command.install", + "distutils.command.install_clib", + "distutils.command.install_data", + "distutils.command.install_headers", + "distutils.command.sdist", + "distutils.conv_template", + "distutils.core", + "distutils.extension", + "distutils.fcompiler", + "distutils.fcompiler.absoft", + "distutils.fcompiler.arm", + "distutils.fcompiler.compaq", + "distutils.fcompiler.environment", + "distutils.fcompiler.g95", + "distutils.fcompiler.gnu", + "distutils.fcompiler.hpux", + "distutils.fcompiler.ibm", + "distutils.fcompiler.intel", + "distutils.fcompiler.lahey", + "distutils.fcompiler.mips", + "distutils.fcompiler.nag", + "distutils.fcompiler.none", + "distutils.fcompiler.pathf95", + "distutils.fcompiler.pg", + "distutils.fcompiler.nv", + "distutils.fcompiler.sun", + "distutils.fcompiler.vast", + "distutils.fcompiler.fujitsu", + "distutils.from_template", + "distutils.intelccompiler", + "distutils.lib2def", + "distutils.line_endings", + "distutils.mingw32ccompiler", + "distutils.msvccompiler", + "distutils.npy_pkg_config", + "distutils.numpy_distribution", + "distutils.pathccompiler", + "distutils.unixccompiler", + "dual", + "f2py.auxfuncs", + "f2py.capi_maps", + "f2py.cb_rules", + "f2py.cfuncs", + "f2py.common_rules", + "f2py.crackfortran", + "f2py.diagnose", + "f2py.f2py2e", + "f2py.f2py_testing", + "f2py.f90mod_rules", + "f2py.func2subr", + "f2py.rules", + "f2py.symbolic", + "f2py.use_rules", + "fft.helper", + "lib.arraypad", + "lib.arraysetops", + "lib.arrayterator", + "lib.function_base", + "lib.histograms", + "lib.index_tricks", + "lib.nanfunctions", + "lib.npyio", + "lib.polynomial", + "lib.shape_base", + "lib.twodim_base", + "lib.type_check", + "lib.ufunclike", + "lib.user_array", # note: not in np.lib, but probably should just be deleted + "lib.utils", + "linalg.lapack_lite", + "linalg.linalg", + "ma.bench", + "ma.core", + "ma.testutils", + "ma.timer_comparison", + "matrixlib", + "matrixlib.defmatrix", + "polynomial.polyutils", + "random.mtrand", + "random.bit_generator", + "testing.print_coercion_tables", + "testing.utils", +]] + + +def is_unexpected(name): + """Check if this needs to be considered.""" + if '._' in name or '.tests' in name or '.setup' in name: + return False + + if name in PUBLIC_MODULES: + return False + + if name in PUBLIC_ALIASED_MODULES: + return False + + if name in PRIVATE_BUT_PRESENT_MODULES: + return False + + return True + + +# These are present in a directory with an __init__.py but cannot be imported +# code_generators/ isn't installed, but present for an inplace build +SKIP_LIST = [ + "numpy.core.code_generators", + "numpy.core.code_generators.genapi", + "numpy.core.code_generators.generate_umath", + "numpy.core.code_generators.ufunc_docstrings", + "numpy.core.code_generators.generate_numpy_api", + "numpy.core.code_generators.generate_ufunc_api", + "numpy.core.code_generators.numpy_api", + "numpy.core.cversions", + "numpy.core.generate_numpy_api", + "numpy.distutils.msvc9compiler", +] + + +def test_all_modules_are_expected(): + """ + Test that we don't add anything that looks like a new public module by + accident. Check is based on filenames. + """ + + modnames = [] + for _, modname, ispkg in pkgutil.walk_packages(path=np.__path__, + prefix=np.__name__ + '.', + onerror=None): + if is_unexpected(modname) and modname not in SKIP_LIST: + # We have a name that is new. If that's on purpose, add it to + # PUBLIC_MODULES. We don't expect to have to add anything to + # PRIVATE_BUT_PRESENT_MODULES. Use an underscore in the name! + modnames.append(modname) + + if modnames: + raise AssertionError(f'Found unexpected modules: {modnames}') + + +# Stuff that clearly shouldn't be in the API and is detected by the next test +# below +SKIP_LIST_2 = [ + 'numpy.math', + 'numpy.distutils.log.sys', + 'numpy.doc.constants.re', + 'numpy.doc.constants.textwrap', + 'numpy.lib.emath', + 'numpy.lib.math', + 'numpy.matlib.char', + 'numpy.matlib.rec', + 'numpy.matlib.emath', + 'numpy.matlib.math', + 'numpy.matlib.linalg', + 'numpy.matlib.fft', + 'numpy.matlib.random', + 'numpy.matlib.ctypeslib', + 'numpy.matlib.ma', +] + + +def test_all_modules_are_expected_2(): + """ + Method checking all objects. The pkgutil-based method in + `test_all_modules_are_expected` does not catch imports into a namespace, + only filenames. So this test is more thorough, and checks this like: + + import .lib.scimath as emath + + To check if something in a module is (effectively) public, one can check if + there's anything in that namespace that's a public function/object but is + not exposed in a higher-level namespace. For example for a `numpy.lib` + submodule:: + + mod = np.lib.mixins + for obj in mod.__all__: + if obj in np.__all__: + continue + elif obj in np.lib.__all__: + continue + + else: + print(obj) + + """ + + def find_unexpected_members(mod_name): + members = [] + module = importlib.import_module(mod_name) + if hasattr(module, '__all__'): + objnames = module.__all__ + else: + objnames = dir(module) + + for objname in objnames: + if not objname.startswith('_'): + fullobjname = mod_name + '.' + objname + if isinstance(getattr(module, objname), types.ModuleType): + if is_unexpected(fullobjname): + if fullobjname not in SKIP_LIST_2: + members.append(fullobjname) + + return members + + unexpected_members = find_unexpected_members("numpy") + for modname in PUBLIC_MODULES: + unexpected_members.extend(find_unexpected_members(modname)) + + if unexpected_members: + raise AssertionError("Found unexpected object(s) that look like " + "modules: {}".format(unexpected_members)) + + +def test_api_importable(): + """ + Check that all submodules listed higher up in this file can be imported + + Note that if a PRIVATE_BUT_PRESENT_MODULES entry goes missing, it may + simply need to be removed from the list (deprecation may or may not be + needed - apply common sense). + """ + def check_importable(module_name): + try: + importlib.import_module(module_name) + except (ImportError, AttributeError): + return False + + return True + + module_names = [] + for module_name in PUBLIC_MODULES: + if not check_importable(module_name): + module_names.append(module_name) + + if module_names: + raise AssertionError("Modules in the public API that cannot be " + "imported: {}".format(module_names)) + + for module_name in PUBLIC_ALIASED_MODULES: + try: + eval(module_name) + except AttributeError: + module_names.append(module_name) + + if module_names: + raise AssertionError("Modules in the public API that were not " + "found: {}".format(module_names)) + + with warnings.catch_warnings(record=True) as w: + warnings.filterwarnings('always', category=DeprecationWarning) + warnings.filterwarnings('always', category=ImportWarning) + for module_name in PRIVATE_BUT_PRESENT_MODULES: + if not check_importable(module_name): + module_names.append(module_name) + + if module_names: + raise AssertionError("Modules that are not really public but looked " + "public and can not be imported: " + "{}".format(module_names)) + + +@pytest.mark.xfail( + sysconfig.get_config_var("Py_DEBUG") is not None, + reason=( + "NumPy possibly built with `USE_DEBUG=True ./tools/travis-test.sh`, " + "which does not expose the `array_api` entry point. " + "See https://github.com/numpy/numpy/pull/19800" + ), +) +def test_array_api_entry_point(): + """ + Entry point for Array API implementation can be found with importlib and + returns the numpy.array_api namespace. + """ + eps = importlib.metadata.entry_points() + try: + xp_eps = eps.select(group="array_api") + except AttributeError: + # The select interface for entry_points was introduced in py3.10, + # deprecating its dict interface. We fallback to dict keys for finding + # Array API entry points so that running this test in <=3.9 will + # still work - see https://github.com/numpy/numpy/pull/19800. + xp_eps = eps.get("array_api", []) + assert len(xp_eps) > 0, "No entry points for 'array_api' found" + + try: + ep = next(ep for ep in xp_eps if ep.name == "numpy") + except StopIteration: + raise AssertionError("'numpy' not in array_api entry points") from None + + xp = ep.load() + msg = ( + f"numpy entry point value '{ep.value}' " + "does not point to our Array API implementation" + ) + assert xp is numpy.array_api, msg diff --git a/numpy/tests/test_reloading.py b/numpy/tests/test_reloading.py index cd42252e35a7..8d8c8aa34be8 100644 --- a/numpy/tests/test_reloading.py +++ b/numpy/tests/test_reloading.py @@ -1,14 +1,11 @@ -from __future__ import division, absolute_import, print_function +from numpy.testing import assert_raises, assert_warns, assert_, assert_equal +from numpy.compat import pickle import sys -import pickle +import subprocess +import textwrap +from importlib import reload -from numpy.testing import assert_raises, assert_, assert_equal - -if sys.version_info[:2] >= (3, 4): - from importlib import reload -else: - from imp import reload def test_numpy_reloading(): # gh-7844. Also check that relevant globals retain their identity. @@ -19,18 +16,49 @@ def test_numpy_reloading(): VisibleDeprecationWarning = np.VisibleDeprecationWarning ModuleDeprecationWarning = np.ModuleDeprecationWarning - reload(np) + with assert_warns(UserWarning): + reload(np) assert_(_NoValue is np._NoValue) assert_(ModuleDeprecationWarning is np.ModuleDeprecationWarning) assert_(VisibleDeprecationWarning is np.VisibleDeprecationWarning) assert_raises(RuntimeError, reload, numpy._globals) - reload(np) + with assert_warns(UserWarning): + reload(np) assert_(_NoValue is np._NoValue) assert_(ModuleDeprecationWarning is np.ModuleDeprecationWarning) assert_(VisibleDeprecationWarning is np.VisibleDeprecationWarning) def test_novalue(): import numpy as np - assert_equal(repr(np._NoValue), '<no value>') - assert_(pickle.loads(pickle.dumps(np._NoValue)) is np._NoValue) + for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): + assert_equal(repr(np._NoValue), '<no value>') + assert_(pickle.loads(pickle.dumps(np._NoValue, + protocol=proto)) is np._NoValue) + + +def test_full_reimport(): + """At the time of writing this, it is *not* truly supported, but + apparently enough users rely on it, for it to be an annoying change + when it started failing previously. + """ + # Test within a new process, to ensure that we do not mess with the + # global state during the test run (could lead to cryptic test failures). + # This is generally unsafe, especially, since we also reload the C-modules. + code = textwrap.dedent(r""" + import sys + from pytest import warns + import numpy as np + + for k in list(sys.modules.keys()): + if "numpy" in k: + del sys.modules[k] + + with warns(UserWarning): + import numpy as np + """) + p = subprocess.run([sys.executable, '-c', code], capture_output=True) + if p.returncode: + raise AssertionError( + f"Non-zero return code: {p.returncode!r}\n\n{p.stderr.decode()}" + ) diff --git a/numpy/tests/test_scripts.py b/numpy/tests/test_scripts.py index ee09390c7a4b..e67a829471dc 100644 --- a/numpy/tests/test_scripts.py +++ b/numpy/tests/test_scripts.py @@ -2,92 +2,45 @@ Test that we can run executable scripts that have been installed with numpy. """ -from __future__ import division, print_function, absolute_import - import sys import os import pytest -from os.path import join as pathjoin, isfile, dirname, basename -from subprocess import Popen, PIPE +from os.path import join as pathjoin, isfile, dirname +import subprocess import numpy as np -from numpy.compat.py3k import basestring -from numpy.testing import assert_, assert_equal +from numpy.testing import assert_equal is_inplace = isfile(pathjoin(dirname(np.__file__), '..', 'setup.py')) -def run_command(cmd, check_code=True): - """ Run command sequence `cmd` returning exit code, stdout, stderr - - Parameters - ---------- - cmd : str or sequence - string with command name or sequence of strings defining command - check_code : {True, False}, optional - If True, raise error for non-zero return code - - Returns - ------- - returncode : int - return code from execution of `cmd` - stdout : bytes (python 3) or str (python 2) - stdout from `cmd` - stderr : bytes (python 3) or str (python 2) - stderr from `cmd` - - Raises - ------ - RuntimeError - If `check_code` is True, and return code !=0 - """ - cmd = [cmd] if isinstance(cmd, basestring) else list(cmd) - if os.name == 'nt': - # Quote any arguments with spaces. The quotes delimit the arguments - # on Windows, and the arguments might be file paths with spaces. - # On Unix the list elements are each separate arguments. - cmd = ['"{0}"'.format(c) if ' ' in c else c for c in cmd] - proc = Popen(cmd, stdout=PIPE, stderr=PIPE) - stdout, stderr = proc.communicate() - if proc.poll() is None: - proc.terminate() - if check_code and proc.returncode != 0: - raise RuntimeError('\n'.join( - ['Command "{0}" failed with', - 'stdout', '------', '{1}', '', - 'stderr', '------', '{2}']).format(cmd, stdout, stderr)) - return proc.returncode, stdout, stderr - - -@pytest.mark.skipif(is_inplace, reason="Cannot test f2py command inplace") -def test_f2py(): - # test that we can run f2py script +def find_f2py_commands(): if sys.platform == 'win32': exe_dir = dirname(sys.executable) - if exe_dir.endswith('Scripts'): # virtualenv - f2py_cmd = r"%s\f2py.py" % exe_dir + return [os.path.join(exe_dir, 'f2py')] else: - f2py_cmd = r"%s\Scripts\f2py.py" % exe_dir - - code, stdout, stderr = run_command([sys.executable, f2py_cmd, '-v']) - success = stdout.strip() == b'2' - assert_(success, "Warning: f2py not found in path") + return [os.path.join(exe_dir, "Scripts", 'f2py')] else: + # Three scripts are installed in Unix-like systems: + # 'f2py', 'f2py{major}', and 'f2py{major.minor}'. For example, + # if installed with python3.7 the scripts would be named + # 'f2py', 'f2py3', and 'f2py3.7'. version = sys.version_info major = str(version.major) minor = str(version.minor) + return ['f2py', 'f2py' + major, 'f2py' + major + '.' + minor] + + +@pytest.mark.skipif(is_inplace, reason="Cannot test f2py command inplace") +@pytest.mark.xfail(reason="Test is unreliable") +@pytest.mark.parametrize('f2py_cmd', find_f2py_commands()) +def test_f2py(f2py_cmd): + # test that we can run f2py script + stdout = subprocess.check_output([f2py_cmd, '-v']) + assert_equal(stdout.strip(), np.__version__.encode('ascii')) - f2py_cmds = ('f2py', 'f2py' + major, 'f2py' + major + '.' + minor) - success = False - for f2py_cmd in f2py_cmds: - try: - code, stdout, stderr = run_command([f2py_cmd, '-v']) - assert_equal(stdout.strip(), b'2') - success = True - break - except Exception: - pass - msg = "Warning: neither %s nor %s nor %s found in path" % f2py_cmds - assert_(success, msg) +def test_pep338(): + stdout = subprocess.check_output([sys.executable, '-mnumpy.f2py', '-v']) + assert_equal(stdout.strip(), np.__version__.encode('ascii')) diff --git a/numpy/tests/test_warnings.py b/numpy/tests/test_warnings.py index aa6f69f7eea9..d7a6d880cbdb 100644 --- a/numpy/tests/test_warnings.py +++ b/numpy/tests/test_warnings.py @@ -2,77 +2,73 @@ Tests which scan for certain occurrences in the code, they may not find all of these occurrences but should catch almost all. """ -from __future__ import division, absolute_import, print_function - -import sys import pytest -if sys.version_info >= (3, 4): - from pathlib import Path - import ast - import tokenize - import numpy - - class ParseCall(ast.NodeVisitor): - def __init__(self): - self.ls = [] - - def visit_Attribute(self, node): - ast.NodeVisitor.generic_visit(self, node) - self.ls.append(node.attr) - - def visit_Name(self, node): - self.ls.append(node.id) +from pathlib import Path +import ast +import tokenize +import numpy +class ParseCall(ast.NodeVisitor): + def __init__(self): + self.ls = [] - class FindFuncs(ast.NodeVisitor): - def __init__(self, filename): - super().__init__() - self.__filename = filename + def visit_Attribute(self, node): + ast.NodeVisitor.generic_visit(self, node) + self.ls.append(node.attr) - def visit_Call(self, node): - p = ParseCall() - p.visit(node.func) - ast.NodeVisitor.generic_visit(self, node) + def visit_Name(self, node): + self.ls.append(node.id) - if p.ls[-1] == 'simplefilter' or p.ls[-1] == 'filterwarnings': - if node.args[0].s == "ignore": - raise AssertionError( - "ignore filter should not be used; found in " - "{} on line {}".format(self.__filename, node.lineno)) - if p.ls[-1] == 'warn' and ( - len(p.ls) == 1 or p.ls[-2] == 'warnings'): +class FindFuncs(ast.NodeVisitor): + def __init__(self, filename): + super().__init__() + self.__filename = filename - if "testing/tests/test_warnings.py" is self.__filename: - # This file - return + def visit_Call(self, node): + p = ParseCall() + p.visit(node.func) + ast.NodeVisitor.generic_visit(self, node) - # See if stacklevel exists: - if len(node.args) == 3: - return - args = {kw.arg for kw in node.keywords} - if "stacklevel" in args: - return + if p.ls[-1] == 'simplefilter' or p.ls[-1] == 'filterwarnings': + if node.args[0].s == "ignore": raise AssertionError( "warnings should have an appropriate stacklevel; found in " "{} on line {}".format(self.__filename, node.lineno)) + if p.ls[-1] == 'warn' and ( + len(p.ls) == 1 or p.ls[-2] == 'warnings'): + + if "testing/tests/test_warnings.py" == self.__filename: + # This file + return + + # See if stacklevel exists: + if len(node.args) == 3: + return + args = {kw.arg for kw in node.keywords} + if "stacklevel" in args: + return + raise AssertionError( + "warnings should have an appropriate stacklevel; found in " + "{} on line {}".format(self.__filename, node.lineno)) + - @pytest.mark.slow - def test_warning_calls(): - # combined "ignore" and stacklevel error - base = Path(numpy.__file__).parent +@pytest.mark.slow +def test_warning_calls(): + # combined "ignore" and stacklevel error + base = Path(numpy.__file__).parent - for path in base.rglob("*.py"): - if base / "testing" in path.parents: - continue - if path == base / "__init__.py": - continue - if path == base / "random" / "__init__.py": - continue - # use tokenize to auto-detect encoding on systems where no - # default encoding is defined (e.g. LANG='C') - with tokenize.open(str(path)) as file: - tree = ast.parse(file.read()) - FindFuncs(path).visit(tree) + for path in base.rglob("*.py"): + if base / "testing" in path.parents: + continue + if path == base / "__init__.py": + continue + if path == base / "random" / "__init__.py": + continue + # use tokenize to auto-detect encoding on systems where no + # default encoding is defined (e.g. LANG='C') + with tokenize.open(str(path)) as file: + tree = ast.parse(file.read()) + FindFuncs(path).visit(tree) diff --git a/numpy/typing/__init__.py b/numpy/typing/__init__.py new file mode 100644 index 000000000000..d5cfbf5acc45 --- /dev/null +++ b/numpy/typing/__init__.py @@ -0,0 +1,394 @@ +""" +============================ +Typing (:mod:`numpy.typing`) +============================ + +.. versionadded:: 1.20 + +Large parts of the NumPy API have PEP-484-style type annotations. In +addition a number of type aliases are available to users, most prominently +the two below: + +- `ArrayLike`: objects that can be converted to arrays +- `DTypeLike`: objects that can be converted to dtypes + +.. _typing-extensions: https://pypi.org/project/typing-extensions/ + +Mypy plugin +----------- + +.. versionadded:: 1.21 + +.. automodule:: numpy.typing.mypy_plugin + +.. currentmodule:: numpy.typing + +Differences from the runtime NumPy API +-------------------------------------- + +NumPy is very flexible. Trying to describe the full range of +possibilities statically would result in types that are not very +helpful. For that reason, the typed NumPy API is often stricter than +the runtime NumPy API. This section describes some notable +differences. + +ArrayLike +~~~~~~~~~ + +The `ArrayLike` type tries to avoid creating object arrays. For +example, + +.. code-block:: python + + >>> np.array(x**2 for x in range(10)) + array(<generator object <genexpr> at ...>, dtype=object) + +is valid NumPy code which will create a 0-dimensional object +array. Type checkers will complain about the above example when using +the NumPy types however. If you really intended to do the above, then +you can either use a ``# type: ignore`` comment: + +.. code-block:: python + + >>> np.array(x**2 for x in range(10)) # type: ignore + +or explicitly type the array like object as `~typing.Any`: + +.. code-block:: python + + >>> from typing import Any + >>> array_like: Any = (x**2 for x in range(10)) + >>> np.array(array_like) + array(<generator object <genexpr> at ...>, dtype=object) + +ndarray +~~~~~~~ + +It's possible to mutate the dtype of an array at runtime. For example, +the following code is valid: + +.. code-block:: python + + >>> x = np.array([1, 2]) + >>> x.dtype = np.bool_ + +This sort of mutation is not allowed by the types. Users who want to +write statically typed code should instead use the `numpy.ndarray.view` +method to create a view of the array with a different dtype. + +DTypeLike +~~~~~~~~~ + +The `DTypeLike` type tries to avoid creation of dtype objects using +dictionary of fields like below: + +.. code-block:: python + + >>> x = np.dtype({"field1": (float, 1), "field2": (int, 3)}) + +Although this is valid NumPy code, the type checker will complain about it, +since its usage is discouraged. +Please see : :ref:`Data type objects <arrays.dtypes>` + +Number precision +~~~~~~~~~~~~~~~~ + +The precision of `numpy.number` subclasses is treated as a covariant generic +parameter (see :class:`~NBitBase`), simplifying the annotating of processes +involving precision-based casting. + +.. code-block:: python + + >>> from typing import TypeVar + >>> import numpy as np + >>> import numpy.typing as npt + + >>> T = TypeVar("T", bound=npt.NBitBase) + >>> def func(a: "np.floating[T]", b: "np.floating[T]") -> "np.floating[T]": + ... ... + +Consequently, the likes of `~numpy.float16`, `~numpy.float32` and +`~numpy.float64` are still sub-types of `~numpy.floating`, but, contrary to +runtime, they're not necessarily considered as sub-classes. + +Timedelta64 +~~~~~~~~~~~ + +The `~numpy.timedelta64` class is not considered a subclass of +`~numpy.signedinteger`, the former only inheriting from `~numpy.generic` +while static type checking. + +0D arrays +~~~~~~~~~ + +During runtime numpy aggressively casts any passed 0D arrays into their +corresponding `~numpy.generic` instance. Until the introduction of shape +typing (see :pep:`646`) it is unfortunately not possible to make the +necessary distinction between 0D and >0D arrays. While thus not strictly +correct, all operations are that can potentially perform a 0D-array -> scalar +cast are currently annotated as exclusively returning an `ndarray`. + +If it is known in advance that an operation _will_ perform a +0D-array -> scalar cast, then one can consider manually remedying the +situation with either `typing.cast` or a ``# type: ignore`` comment. + +Record array dtypes +~~~~~~~~~~~~~~~~~~~ + +The dtype of `numpy.recarray`, and the `numpy.rec` functions in general, +can be specified in one of two ways: + +* Directly via the ``dtype`` argument. +* With up to five helper arguments that operate via `numpy.format_parser`: + ``formats``, ``names``, ``titles``, ``aligned`` and ``byteorder``. + +These two approaches are currently typed as being mutually exclusive, +*i.e.* if ``dtype`` is specified than one may not specify ``formats``. +While this mutual exclusivity is not (strictly) enforced during runtime, +combining both dtype specifiers can lead to unexpected or even downright +buggy behavior. + +API +--- + +""" +# NOTE: The API section will be appended with additional entries +# further down in this file + +from __future__ import annotations + +from numpy import ufunc +from typing import TYPE_CHECKING, final + +if not TYPE_CHECKING: + __all__ = ["ArrayLike", "DTypeLike", "NBitBase", "NDArray"] +else: + # Ensure that all objects within this module are accessible while + # static type checking. This includes private ones, as we need them + # for internal use. + # + # Declare to mypy that `__all__` is a list of strings without assigning + # an explicit value + __all__: list[str] + __path__: list[str] + + +@final # Disallow the creation of arbitrary `NBitBase` subclasses +class NBitBase: + """ + A type representing `numpy.number` precision during static type checking. + + Used exclusively for the purpose static type checking, `NBitBase` + represents the base of a hierarchical set of subclasses. + Each subsequent subclass is herein used for representing a lower level + of precision, *e.g.* ``64Bit > 32Bit > 16Bit``. + + .. versionadded:: 1.20 + + Examples + -------- + Below is a typical usage example: `NBitBase` is herein used for annotating + a function that takes a float and integer of arbitrary precision + as arguments and returns a new float of whichever precision is largest + (*e.g.* ``np.float16 + np.int64 -> np.float64``). + + .. code-block:: python + + >>> from __future__ import annotations + >>> from typing import TypeVar, TYPE_CHECKING + >>> import numpy as np + >>> import numpy.typing as npt + + >>> T1 = TypeVar("T1", bound=npt.NBitBase) + >>> T2 = TypeVar("T2", bound=npt.NBitBase) + + >>> def add(a: np.floating[T1], b: np.integer[T2]) -> np.floating[T1 | T2]: + ... return a + b + + >>> a = np.float16() + >>> b = np.int64() + >>> out = add(a, b) + + >>> if TYPE_CHECKING: + ... reveal_locals() + ... # note: Revealed local types are: + ... # note: a: numpy.floating[numpy.typing._16Bit*] + ... # note: b: numpy.signedinteger[numpy.typing._64Bit*] + ... # note: out: numpy.floating[numpy.typing._64Bit*] + + """ + + def __init_subclass__(cls) -> None: + allowed_names = { + "NBitBase", "_256Bit", "_128Bit", "_96Bit", "_80Bit", + "_64Bit", "_32Bit", "_16Bit", "_8Bit", + } + if cls.__name__ not in allowed_names: + raise TypeError('cannot inherit from final class "NBitBase"') + super().__init_subclass__() + + +# Silence errors about subclassing a `@final`-decorated class +class _256Bit(NBitBase): # type: ignore[misc] + pass + +class _128Bit(_256Bit): # type: ignore[misc] + pass + +class _96Bit(_128Bit): # type: ignore[misc] + pass + +class _80Bit(_96Bit): # type: ignore[misc] + pass + +class _64Bit(_80Bit): # type: ignore[misc] + pass + +class _32Bit(_64Bit): # type: ignore[misc] + pass + +class _16Bit(_32Bit): # type: ignore[misc] + pass + +class _8Bit(_16Bit): # type: ignore[misc] + pass + + +from ._nested_sequence import _NestedSequence +from ._nbit import ( + _NBitByte, + _NBitShort, + _NBitIntC, + _NBitIntP, + _NBitInt, + _NBitLongLong, + _NBitHalf, + _NBitSingle, + _NBitDouble, + _NBitLongDouble, +) +from ._char_codes import ( + _BoolCodes, + _UInt8Codes, + _UInt16Codes, + _UInt32Codes, + _UInt64Codes, + _Int8Codes, + _Int16Codes, + _Int32Codes, + _Int64Codes, + _Float16Codes, + _Float32Codes, + _Float64Codes, + _Complex64Codes, + _Complex128Codes, + _ByteCodes, + _ShortCodes, + _IntCCodes, + _IntPCodes, + _IntCodes, + _LongLongCodes, + _UByteCodes, + _UShortCodes, + _UIntCCodes, + _UIntPCodes, + _UIntCodes, + _ULongLongCodes, + _HalfCodes, + _SingleCodes, + _DoubleCodes, + _LongDoubleCodes, + _CSingleCodes, + _CDoubleCodes, + _CLongDoubleCodes, + _DT64Codes, + _TD64Codes, + _StrCodes, + _BytesCodes, + _VoidCodes, + _ObjectCodes, +) +from ._scalars import ( + _CharLike_co, + _BoolLike_co, + _UIntLike_co, + _IntLike_co, + _FloatLike_co, + _ComplexLike_co, + _TD64Like_co, + _NumberLike_co, + _ScalarLike_co, + _VoidLike_co, +) +from ._shape import _Shape, _ShapeLike +from ._dtype_like import ( + DTypeLike as DTypeLike, + _SupportsDType, + _VoidDTypeLike, + _DTypeLikeBool, + _DTypeLikeUInt, + _DTypeLikeInt, + _DTypeLikeFloat, + _DTypeLikeComplex, + _DTypeLikeTD64, + _DTypeLikeDT64, + _DTypeLikeObject, + _DTypeLikeVoid, + _DTypeLikeStr, + _DTypeLikeBytes, + _DTypeLikeComplex_co, +) +from ._array_like import ( + ArrayLike as ArrayLike, + _ArrayLike, + _FiniteNestedSequence, + _SupportsArray, + _ArrayLikeInt, + _ArrayLikeBool_co, + _ArrayLikeUInt_co, + _ArrayLikeInt_co, + _ArrayLikeFloat_co, + _ArrayLikeComplex_co, + _ArrayLikeNumber_co, + _ArrayLikeTD64_co, + _ArrayLikeDT64_co, + _ArrayLikeObject_co, + _ArrayLikeVoid_co, + _ArrayLikeStr_co, + _ArrayLikeBytes_co, +) +from ._generic_alias import ( + NDArray as NDArray, + _DType, + _GenericAlias, +) + +if TYPE_CHECKING: + from ._ufunc import ( + _UFunc_Nin1_Nout1, + _UFunc_Nin2_Nout1, + _UFunc_Nin1_Nout2, + _UFunc_Nin2_Nout2, + _GUFunc_Nin2_Nout1, + ) +else: + # Declare the (type-check-only) ufunc subclasses as ufunc aliases during + # runtime; this helps autocompletion tools such as Jedi (numpy/numpy#19834) + _UFunc_Nin1_Nout1 = ufunc + _UFunc_Nin2_Nout1 = ufunc + _UFunc_Nin1_Nout2 = ufunc + _UFunc_Nin2_Nout2 = ufunc + _GUFunc_Nin2_Nout1 = ufunc + +# Clean up the namespace +del TYPE_CHECKING, final, ufunc + +if __doc__ is not None: + from ._add_docstring import _docstrings + __doc__ += _docstrings + __doc__ += '\n.. autoclass:: numpy.typing.NBitBase\n' + del _docstrings + +from numpy._pytesttester import PytestTester +test = PytestTester(__name__) +del PytestTester diff --git a/numpy/typing/_add_docstring.py b/numpy/typing/_add_docstring.py new file mode 100644 index 000000000000..10d77f5161d3 --- /dev/null +++ b/numpy/typing/_add_docstring.py @@ -0,0 +1,152 @@ +"""A module for creating docstrings for sphinx ``data`` domains.""" + +import re +import textwrap + +from ._generic_alias import NDArray + +_docstrings_list = [] + + +def add_newdoc(name: str, value: str, doc: str) -> None: + """Append ``_docstrings_list`` with a docstring for `name`. + + Parameters + ---------- + name : str + The name of the object. + value : str + A string-representation of the object. + doc : str + The docstring of the object. + + """ + _docstrings_list.append((name, value, doc)) + + +def _parse_docstrings() -> str: + """Convert all docstrings in ``_docstrings_list`` into a single + sphinx-legible text block. + + """ + type_list_ret = [] + for name, value, doc in _docstrings_list: + s = textwrap.dedent(doc).replace("\n", "\n ") + + # Replace sections by rubrics + lines = s.split("\n") + new_lines = [] + indent = "" + for line in lines: + m = re.match(r'^(\s+)[-=]+\s*$', line) + if m and new_lines: + prev = textwrap.dedent(new_lines.pop()) + if prev == "Examples": + indent = "" + new_lines.append(f'{m.group(1)}.. rubric:: {prev}') + else: + indent = 4 * " " + new_lines.append(f'{m.group(1)}.. admonition:: {prev}') + new_lines.append("") + else: + new_lines.append(f"{indent}{line}") + + s = "\n".join(new_lines) + s_block = f""".. data:: {name}\n :value: {value}\n {s}""" + type_list_ret.append(s_block) + return "\n".join(type_list_ret) + + +add_newdoc('ArrayLike', 'typing.Union[...]', + """ + A `~typing.Union` representing objects that can be coerced + into an `~numpy.ndarray`. + + Among others this includes the likes of: + + * Scalars. + * (Nested) sequences. + * Objects implementing the `~class.__array__` protocol. + + .. versionadded:: 1.20 + + See Also + -------- + :term:`array_like`: + Any scalar or sequence that can be interpreted as an ndarray. + + Examples + -------- + .. code-block:: python + + >>> import numpy as np + >>> import numpy.typing as npt + + >>> def as_array(a: npt.ArrayLike) -> np.ndarray: + ... return np.array(a) + + """) + +add_newdoc('DTypeLike', 'typing.Union[...]', + """ + A `~typing.Union` representing objects that can be coerced + into a `~numpy.dtype`. + + Among others this includes the likes of: + + * :class:`type` objects. + * Character codes or the names of :class:`type` objects. + * Objects with the ``.dtype`` attribute. + + .. versionadded:: 1.20 + + See Also + -------- + :ref:`Specifying and constructing data types <arrays.dtypes.constructing>` + A comprehensive overview of all objects that can be coerced + into data types. + + Examples + -------- + .. code-block:: python + + >>> import numpy as np + >>> import numpy.typing as npt + + >>> def as_dtype(d: npt.DTypeLike) -> np.dtype: + ... return np.dtype(d) + + """) + +add_newdoc('NDArray', repr(NDArray), + """ + A :term:`generic <generic type>` version of + `np.ndarray[Any, np.dtype[+ScalarType]] <numpy.ndarray>`. + + Can be used during runtime for typing arrays with a given dtype + and unspecified shape. + + .. versionadded:: 1.21 + + Examples + -------- + .. code-block:: python + + >>> import numpy as np + >>> import numpy.typing as npt + + >>> print(npt.NDArray) + numpy.ndarray[typing.Any, numpy.dtype[+ScalarType]] + + >>> print(npt.NDArray[np.float64]) + numpy.ndarray[typing.Any, numpy.dtype[numpy.float64]] + + >>> NDArrayInt = npt.NDArray[np.int_] + >>> a: NDArrayInt = np.arange(10) + + >>> def func(a: npt.ArrayLike) -> npt.NDArray[Any]: + ... return np.array(a) + + """) + +_docstrings = _parse_docstrings() diff --git a/numpy/typing/_array_like.py b/numpy/typing/_array_like.py new file mode 100644 index 000000000000..02e5ee573fc7 --- /dev/null +++ b/numpy/typing/_array_like.py @@ -0,0 +1,123 @@ +from __future__ import annotations + +from typing import Any, Sequence, Protocol, Union, TypeVar +from numpy import ( + ndarray, + dtype, + generic, + bool_, + unsignedinteger, + integer, + floating, + complexfloating, + number, + timedelta64, + datetime64, + object_, + void, + str_, + bytes_, +) +from ._nested_sequence import _NestedSequence + +_T = TypeVar("_T") +_ScalarType = TypeVar("_ScalarType", bound=generic) +_DType = TypeVar("_DType", bound="dtype[Any]") +_DType_co = TypeVar("_DType_co", covariant=True, bound="dtype[Any]") + +# The `_SupportsArray` protocol only cares about the default dtype +# (i.e. `dtype=None` or no `dtype` parameter at all) of the to-be returned +# array. +# Concrete implementations of the protocol are responsible for adding +# any and all remaining overloads +class _SupportsArray(Protocol[_DType_co]): + def __array__(self) -> ndarray[Any, _DType_co]: ... + + +# TODO: Wait until mypy supports recursive objects in combination with typevars +_FiniteNestedSequence = Union[ + _T, + Sequence[_T], + Sequence[Sequence[_T]], + Sequence[Sequence[Sequence[_T]]], + Sequence[Sequence[Sequence[Sequence[_T]]]], +] + +# A union representing array-like objects; consists of two typevars: +# One representing types that can be parametrized w.r.t. `np.dtype` +# and another one for the rest +_ArrayLike = Union[ + _SupportsArray[_DType], + _NestedSequence[_SupportsArray[_DType]], + _T, + _NestedSequence[_T], +] + +# TODO: support buffer protocols once +# +# https://bugs.python.org/issue27501 +# +# is resolved. See also the mypy issue: +# +# https://github.com/python/typing/issues/593 +ArrayLike = _ArrayLike[ + dtype, + Union[bool, int, float, complex, str, bytes], +] + +# `ArrayLike<X>_co`: array-like objects that can be coerced into `X` +# given the casting rules `same_kind` +_ArrayLikeBool_co = _ArrayLike[ + "dtype[bool_]", + bool, +] +_ArrayLikeUInt_co = _ArrayLike[ + "dtype[Union[bool_, unsignedinteger[Any]]]", + bool, +] +_ArrayLikeInt_co = _ArrayLike[ + "dtype[Union[bool_, integer[Any]]]", + Union[bool, int], +] +_ArrayLikeFloat_co = _ArrayLike[ + "dtype[Union[bool_, integer[Any], floating[Any]]]", + Union[bool, int, float], +] +_ArrayLikeComplex_co = _ArrayLike[ + "dtype[Union[bool_, integer[Any], floating[Any], complexfloating[Any, Any]]]", + Union[bool, int, float, complex], +] +_ArrayLikeNumber_co = _ArrayLike[ + "dtype[Union[bool_, number[Any]]]", + Union[bool, int, float, complex], +] +_ArrayLikeTD64_co = _ArrayLike[ + "dtype[Union[bool_, integer[Any], timedelta64]]", + Union[bool, int], +] +_ArrayLikeDT64_co = Union[ + _SupportsArray["dtype[datetime64]"], + _NestedSequence[_SupportsArray["dtype[datetime64]"]], +] +_ArrayLikeObject_co = Union[ + _SupportsArray["dtype[object_]"], + _NestedSequence[_SupportsArray["dtype[object_]"]], +] + +_ArrayLikeVoid_co = Union[ + _SupportsArray["dtype[void]"], + _NestedSequence[_SupportsArray["dtype[void]"]], +] +_ArrayLikeStr_co = _ArrayLike[ + "dtype[str_]", + str, +] +_ArrayLikeBytes_co = _ArrayLike[ + "dtype[bytes_]", + bytes, +] + +_ArrayLikeInt = _ArrayLike[ + "dtype[integer[Any]]", + int, +] diff --git a/numpy/typing/_callable.pyi b/numpy/typing/_callable.pyi new file mode 100644 index 000000000000..e1149f26ae87 --- /dev/null +++ b/numpy/typing/_callable.pyi @@ -0,0 +1,327 @@ +""" +A module with various ``typing.Protocol`` subclasses that implement +the ``__call__`` magic method. + +See the `Mypy documentation`_ on protocols for more details. + +.. _`Mypy documentation`: https://mypy.readthedocs.io/en/stable/protocols.html#callback-protocols + +""" + +from __future__ import annotations + +from typing import ( + Union, + TypeVar, + overload, + Any, + Tuple, + NoReturn, + Protocol, +) + +from numpy import ( + ndarray, + dtype, + generic, + bool_, + timedelta64, + number, + integer, + unsignedinteger, + signedinteger, + int8, + int_, + floating, + float64, + complexfloating, + complex128, +) +from ._nbit import _NBitInt, _NBitDouble +from ._scalars import ( + _BoolLike_co, + _IntLike_co, + _FloatLike_co, + _NumberLike_co, +) +from . import NBitBase +from ._generic_alias import NDArray + +_T1 = TypeVar("_T1") +_T2 = TypeVar("_T2") +_T1_contra = TypeVar("_T1_contra", contravariant=True) +_T2_contra = TypeVar("_T2_contra", contravariant=True) +_2Tuple = Tuple[_T1, _T1] + +_NBit1 = TypeVar("_NBit1", bound=NBitBase) +_NBit2 = TypeVar("_NBit2", bound=NBitBase) + +_IntType = TypeVar("_IntType", bound=integer) +_FloatType = TypeVar("_FloatType", bound=floating) +_NumberType = TypeVar("_NumberType", bound=number) +_NumberType_co = TypeVar("_NumberType_co", covariant=True, bound=number) +_GenericType_co = TypeVar("_GenericType_co", covariant=True, bound=generic) + +class _BoolOp(Protocol[_GenericType_co]): + @overload + def __call__(self, other: _BoolLike_co, /) -> _GenericType_co: ... + @overload # platform dependent + def __call__(self, other: int, /) -> int_: ... + @overload + def __call__(self, other: float, /) -> float64: ... + @overload + def __call__(self, other: complex, /) -> complex128: ... + @overload + def __call__(self, other: _NumberType, /) -> _NumberType: ... + +class _BoolBitOp(Protocol[_GenericType_co]): + @overload + def __call__(self, other: _BoolLike_co, /) -> _GenericType_co: ... + @overload # platform dependent + def __call__(self, other: int, /) -> int_: ... + @overload + def __call__(self, other: _IntType, /) -> _IntType: ... + +class _BoolSub(Protocol): + # Note that `other: bool_` is absent here + @overload + def __call__(self, other: bool, /) -> NoReturn: ... + @overload # platform dependent + def __call__(self, other: int, /) -> int_: ... + @overload + def __call__(self, other: float, /) -> float64: ... + @overload + def __call__(self, other: complex, /) -> complex128: ... + @overload + def __call__(self, other: _NumberType, /) -> _NumberType: ... + +class _BoolTrueDiv(Protocol): + @overload + def __call__(self, other: float | _IntLike_co, /) -> float64: ... + @overload + def __call__(self, other: complex, /) -> complex128: ... + @overload + def __call__(self, other: _NumberType, /) -> _NumberType: ... + +class _BoolMod(Protocol): + @overload + def __call__(self, other: _BoolLike_co, /) -> int8: ... + @overload # platform dependent + def __call__(self, other: int, /) -> int_: ... + @overload + def __call__(self, other: float, /) -> float64: ... + @overload + def __call__(self, other: _IntType, /) -> _IntType: ... + @overload + def __call__(self, other: _FloatType, /) -> _FloatType: ... + +class _BoolDivMod(Protocol): + @overload + def __call__(self, other: _BoolLike_co, /) -> _2Tuple[int8]: ... + @overload # platform dependent + def __call__(self, other: int, /) -> _2Tuple[int_]: ... + @overload + def __call__(self, other: float, /) -> _2Tuple[floating[_NBit1 | _NBitDouble]]: ... + @overload + def __call__(self, other: _IntType, /) -> _2Tuple[_IntType]: ... + @overload + def __call__(self, other: _FloatType, /) -> _2Tuple[_FloatType]: ... + +class _TD64Div(Protocol[_NumberType_co]): + @overload + def __call__(self, other: timedelta64, /) -> _NumberType_co: ... + @overload + def __call__(self, other: _BoolLike_co, /) -> NoReturn: ... + @overload + def __call__(self, other: _FloatLike_co, /) -> timedelta64: ... + +class _IntTrueDiv(Protocol[_NBit1]): + @overload + def __call__(self, other: bool, /) -> floating[_NBit1]: ... + @overload + def __call__(self, other: int, /) -> floating[_NBit1 | _NBitInt]: ... + @overload + def __call__(self, other: float, /) -> floating[_NBit1 | _NBitDouble]: ... + @overload + def __call__( + self, other: complex, /, + ) -> complexfloating[_NBit1 | _NBitDouble, _NBit1 | _NBitDouble]: ... + @overload + def __call__(self, other: integer[_NBit2], /) -> floating[_NBit1 | _NBit2]: ... + +class _UnsignedIntOp(Protocol[_NBit1]): + # NOTE: `uint64 + signedinteger -> float64` + @overload + def __call__(self, other: bool, /) -> unsignedinteger[_NBit1]: ... + @overload + def __call__( + self, other: int | signedinteger[Any], / + ) -> Any: ... + @overload + def __call__(self, other: float, /) -> floating[_NBit1 | _NBitDouble]: ... + @overload + def __call__( + self, other: complex, /, + ) -> complexfloating[_NBit1 | _NBitDouble, _NBit1 | _NBitDouble]: ... + @overload + def __call__( + self, other: unsignedinteger[_NBit2], / + ) -> unsignedinteger[_NBit1 | _NBit2]: ... + +class _UnsignedIntBitOp(Protocol[_NBit1]): + @overload + def __call__(self, other: bool, /) -> unsignedinteger[_NBit1]: ... + @overload + def __call__(self, other: int, /) -> signedinteger[Any]: ... + @overload + def __call__(self, other: signedinteger[Any], /) -> signedinteger[Any]: ... + @overload + def __call__( + self, other: unsignedinteger[_NBit2], / + ) -> unsignedinteger[_NBit1 | _NBit2]: ... + +class _UnsignedIntMod(Protocol[_NBit1]): + @overload + def __call__(self, other: bool, /) -> unsignedinteger[_NBit1]: ... + @overload + def __call__( + self, other: int | signedinteger[Any], / + ) -> Any: ... + @overload + def __call__(self, other: float, /) -> floating[_NBit1 | _NBitDouble]: ... + @overload + def __call__( + self, other: unsignedinteger[_NBit2], / + ) -> unsignedinteger[_NBit1 | _NBit2]: ... + +class _UnsignedIntDivMod(Protocol[_NBit1]): + @overload + def __call__(self, other: bool, /) -> _2Tuple[signedinteger[_NBit1]]: ... + @overload + def __call__( + self, other: int | signedinteger[Any], / + ) -> _2Tuple[Any]: ... + @overload + def __call__(self, other: float, /) -> _2Tuple[floating[_NBit1 | _NBitDouble]]: ... + @overload + def __call__( + self, other: unsignedinteger[_NBit2], / + ) -> _2Tuple[unsignedinteger[_NBit1 | _NBit2]]: ... + +class _SignedIntOp(Protocol[_NBit1]): + @overload + def __call__(self, other: bool, /) -> signedinteger[_NBit1]: ... + @overload + def __call__(self, other: int, /) -> signedinteger[_NBit1 | _NBitInt]: ... + @overload + def __call__(self, other: float, /) -> floating[_NBit1 | _NBitDouble]: ... + @overload + def __call__( + self, other: complex, /, + ) -> complexfloating[_NBit1 | _NBitDouble, _NBit1 | _NBitDouble]: ... + @overload + def __call__( + self, other: signedinteger[_NBit2], /, + ) -> signedinteger[_NBit1 | _NBit2]: ... + +class _SignedIntBitOp(Protocol[_NBit1]): + @overload + def __call__(self, other: bool, /) -> signedinteger[_NBit1]: ... + @overload + def __call__(self, other: int, /) -> signedinteger[_NBit1 | _NBitInt]: ... + @overload + def __call__( + self, other: signedinteger[_NBit2], /, + ) -> signedinteger[_NBit1 | _NBit2]: ... + +class _SignedIntMod(Protocol[_NBit1]): + @overload + def __call__(self, other: bool, /) -> signedinteger[_NBit1]: ... + @overload + def __call__(self, other: int, /) -> signedinteger[_NBit1 | _NBitInt]: ... + @overload + def __call__(self, other: float, /) -> floating[_NBit1 | _NBitDouble]: ... + @overload + def __call__( + self, other: signedinteger[_NBit2], /, + ) -> signedinteger[_NBit1 | _NBit2]: ... + +class _SignedIntDivMod(Protocol[_NBit1]): + @overload + def __call__(self, other: bool, /) -> _2Tuple[signedinteger[_NBit1]]: ... + @overload + def __call__(self, other: int, /) -> _2Tuple[signedinteger[_NBit1 | _NBitInt]]: ... + @overload + def __call__(self, other: float, /) -> _2Tuple[floating[_NBit1 | _NBitDouble]]: ... + @overload + def __call__( + self, other: signedinteger[_NBit2], /, + ) -> _2Tuple[signedinteger[_NBit1 | _NBit2]]: ... + +class _FloatOp(Protocol[_NBit1]): + @overload + def __call__(self, other: bool, /) -> floating[_NBit1]: ... + @overload + def __call__(self, other: int, /) -> floating[_NBit1 | _NBitInt]: ... + @overload + def __call__(self, other: float, /) -> floating[_NBit1 | _NBitDouble]: ... + @overload + def __call__( + self, other: complex, /, + ) -> complexfloating[_NBit1 | _NBitDouble, _NBit1 | _NBitDouble]: ... + @overload + def __call__( + self, other: integer[_NBit2] | floating[_NBit2], / + ) -> floating[_NBit1 | _NBit2]: ... + +class _FloatMod(Protocol[_NBit1]): + @overload + def __call__(self, other: bool, /) -> floating[_NBit1]: ... + @overload + def __call__(self, other: int, /) -> floating[_NBit1 | _NBitInt]: ... + @overload + def __call__(self, other: float, /) -> floating[_NBit1 | _NBitDouble]: ... + @overload + def __call__( + self, other: integer[_NBit2] | floating[_NBit2], / + ) -> floating[_NBit1 | _NBit2]: ... + +class _FloatDivMod(Protocol[_NBit1]): + @overload + def __call__(self, other: bool, /) -> _2Tuple[floating[_NBit1]]: ... + @overload + def __call__(self, other: int, /) -> _2Tuple[floating[_NBit1 | _NBitInt]]: ... + @overload + def __call__(self, other: float, /) -> _2Tuple[floating[_NBit1 | _NBitDouble]]: ... + @overload + def __call__( + self, other: integer[_NBit2] | floating[_NBit2], / + ) -> _2Tuple[floating[_NBit1 | _NBit2]]: ... + +class _ComplexOp(Protocol[_NBit1]): + @overload + def __call__(self, other: bool, /) -> complexfloating[_NBit1, _NBit1]: ... + @overload + def __call__(self, other: int, /) -> complexfloating[_NBit1 | _NBitInt, _NBit1 | _NBitInt]: ... + @overload + def __call__( + self, other: complex, /, + ) -> complexfloating[_NBit1 | _NBitDouble, _NBit1 | _NBitDouble]: ... + @overload + def __call__( + self, + other: Union[ + integer[_NBit2], + floating[_NBit2], + complexfloating[_NBit2, _NBit2], + ], /, + ) -> complexfloating[_NBit1 | _NBit2, _NBit1 | _NBit2]: ... + +class _NumberOp(Protocol): + def __call__(self, other: _NumberLike_co, /) -> Any: ... + +class _ComparisonOp(Protocol[_T1_contra, _T2_contra]): + @overload + def __call__(self, other: _T1_contra, /) -> bool_: ... + @overload + def __call__(self, other: _T2_contra, /) -> NDArray[bool_]: ... diff --git a/numpy/typing/_char_codes.py b/numpy/typing/_char_codes.py new file mode 100644 index 000000000000..1394710841fe --- /dev/null +++ b/numpy/typing/_char_codes.py @@ -0,0 +1,111 @@ +from typing import Literal + +_BoolCodes = Literal["?", "=?", "<?", ">?", "bool", "bool_", "bool8"] + +_UInt8Codes = Literal["uint8", "u1", "=u1", "<u1", ">u1"] +_UInt16Codes = Literal["uint16", "u2", "=u2", "<u2", ">u2"] +_UInt32Codes = Literal["uint32", "u4", "=u4", "<u4", ">u4"] +_UInt64Codes = Literal["uint64", "u8", "=u8", "<u8", ">u8"] + +_Int8Codes = Literal["int8", "i1", "=i1", "<i1", ">i1"] +_Int16Codes = Literal["int16", "i2", "=i2", "<i2", ">i2"] +_Int32Codes = Literal["int32", "i4", "=i4", "<i4", ">i4"] +_Int64Codes = Literal["int64", "i8", "=i8", "<i8", ">i8"] + +_Float16Codes = Literal["float16", "f2", "=f2", "<f2", ">f2"] +_Float32Codes = Literal["float32", "f4", "=f4", "<f4", ">f4"] +_Float64Codes = Literal["float64", "f8", "=f8", "<f8", ">f8"] + +_Complex64Codes = Literal["complex64", "c8", "=c8", "<c8", ">c8"] +_Complex128Codes = Literal["complex128", "c16", "=c16", "<c16", ">c16"] + +_ByteCodes = Literal["byte", "b", "=b", "<b", ">b"] +_ShortCodes = Literal["short", "h", "=h", "<h", ">h"] +_IntCCodes = Literal["intc", "i", "=i", "<i", ">i"] +_IntPCodes = Literal["intp", "int0", "p", "=p", "<p", ">p"] +_IntCodes = Literal["long", "int", "int_", "l", "=l", "<l", ">l"] +_LongLongCodes = Literal["longlong", "q", "=q", "<q", ">q"] + +_UByteCodes = Literal["ubyte", "B", "=B", "<B", ">B"] +_UShortCodes = Literal["ushort", "H", "=H", "<H", ">H"] +_UIntCCodes = Literal["uintc", "I", "=I", "<I", ">I"] +_UIntPCodes = Literal["uintp", "uint0", "P", "=P", "<P", ">P"] +_UIntCodes = Literal["uint", "L", "=L", "<L", ">L"] +_ULongLongCodes = Literal["ulonglong", "Q", "=Q", "<Q", ">Q"] + +_HalfCodes = Literal["half", "e", "=e", "<e", ">e"] +_SingleCodes = Literal["single", "f", "=f", "<f", ">f"] +_DoubleCodes = Literal["double", "float", "float_", "d", "=d", "<d", ">d"] +_LongDoubleCodes = Literal["longdouble", "longfloat", "g", "=g", "<g", ">g"] + +_CSingleCodes = Literal["csingle", "singlecomplex", "F", "=F", "<F", ">F"] +_CDoubleCodes = Literal["cdouble", "complex", "complex_", "cfloat", "D", "=D", "<D", ">D"] +_CLongDoubleCodes = Literal["clongdouble", "clongfloat", "longcomplex", "G", "=G", "<G", ">G"] + +_StrCodes = Literal["str", "str_", "str0", "unicode", "unicode_", "U", "=U", "<U", ">U"] +_BytesCodes = Literal["bytes", "bytes_", "bytes0", "S", "=S", "<S", ">S"] +_VoidCodes = Literal["void", "void0", "V", "=V", "<V", ">V"] +_ObjectCodes = Literal["object", "object_", "O", "=O", "<O", ">O"] + +_DT64Codes = Literal[ + "datetime64", "=datetime64", "<datetime64", ">datetime64", + "datetime64[Y]", "=datetime64[Y]", "<datetime64[Y]", ">datetime64[Y]", + "datetime64[M]", "=datetime64[M]", "<datetime64[M]", ">datetime64[M]", + "datetime64[W]", "=datetime64[W]", "<datetime64[W]", ">datetime64[W]", + "datetime64[D]", "=datetime64[D]", "<datetime64[D]", ">datetime64[D]", + "datetime64[h]", "=datetime64[h]", "<datetime64[h]", ">datetime64[h]", + "datetime64[m]", "=datetime64[m]", "<datetime64[m]", ">datetime64[m]", + "datetime64[s]", "=datetime64[s]", "<datetime64[s]", ">datetime64[s]", + "datetime64[ms]", "=datetime64[ms]", "<datetime64[ms]", ">datetime64[ms]", + "datetime64[us]", "=datetime64[us]", "<datetime64[us]", ">datetime64[us]", + "datetime64[ns]", "=datetime64[ns]", "<datetime64[ns]", ">datetime64[ns]", + "datetime64[ps]", "=datetime64[ps]", "<datetime64[ps]", ">datetime64[ps]", + "datetime64[fs]", "=datetime64[fs]", "<datetime64[fs]", ">datetime64[fs]", + "datetime64[as]", "=datetime64[as]", "<datetime64[as]", ">datetime64[as]", + "M", "=M", "<M", ">M", + "M8", "=M8", "<M8", ">M8", + "M8[Y]", "=M8[Y]", "<M8[Y]", ">M8[Y]", + "M8[M]", "=M8[M]", "<M8[M]", ">M8[M]", + "M8[W]", "=M8[W]", "<M8[W]", ">M8[W]", + "M8[D]", "=M8[D]", "<M8[D]", ">M8[D]", + "M8[h]", "=M8[h]", "<M8[h]", ">M8[h]", + "M8[m]", "=M8[m]", "<M8[m]", ">M8[m]", + "M8[s]", "=M8[s]", "<M8[s]", ">M8[s]", + "M8[ms]", "=M8[ms]", "<M8[ms]", ">M8[ms]", + "M8[us]", "=M8[us]", "<M8[us]", ">M8[us]", + "M8[ns]", "=M8[ns]", "<M8[ns]", ">M8[ns]", + "M8[ps]", "=M8[ps]", "<M8[ps]", ">M8[ps]", + "M8[fs]", "=M8[fs]", "<M8[fs]", ">M8[fs]", + "M8[as]", "=M8[as]", "<M8[as]", ">M8[as]", +] +_TD64Codes = Literal[ + "timedelta64", "=timedelta64", "<timedelta64", ">timedelta64", + "timedelta64[Y]", "=timedelta64[Y]", "<timedelta64[Y]", ">timedelta64[Y]", + "timedelta64[M]", "=timedelta64[M]", "<timedelta64[M]", ">timedelta64[M]", + "timedelta64[W]", "=timedelta64[W]", "<timedelta64[W]", ">timedelta64[W]", + "timedelta64[D]", "=timedelta64[D]", "<timedelta64[D]", ">timedelta64[D]", + "timedelta64[h]", "=timedelta64[h]", "<timedelta64[h]", ">timedelta64[h]", + "timedelta64[m]", "=timedelta64[m]", "<timedelta64[m]", ">timedelta64[m]", + "timedelta64[s]", "=timedelta64[s]", "<timedelta64[s]", ">timedelta64[s]", + "timedelta64[ms]", "=timedelta64[ms]", "<timedelta64[ms]", ">timedelta64[ms]", + "timedelta64[us]", "=timedelta64[us]", "<timedelta64[us]", ">timedelta64[us]", + "timedelta64[ns]", "=timedelta64[ns]", "<timedelta64[ns]", ">timedelta64[ns]", + "timedelta64[ps]", "=timedelta64[ps]", "<timedelta64[ps]", ">timedelta64[ps]", + "timedelta64[fs]", "=timedelta64[fs]", "<timedelta64[fs]", ">timedelta64[fs]", + "timedelta64[as]", "=timedelta64[as]", "<timedelta64[as]", ">timedelta64[as]", + "m", "=m", "<m", ">m", + "m8", "=m8", "<m8", ">m8", + "m8[Y]", "=m8[Y]", "<m8[Y]", ">m8[Y]", + "m8[M]", "=m8[M]", "<m8[M]", ">m8[M]", + "m8[W]", "=m8[W]", "<m8[W]", ">m8[W]", + "m8[D]", "=m8[D]", "<m8[D]", ">m8[D]", + "m8[h]", "=m8[h]", "<m8[h]", ">m8[h]", + "m8[m]", "=m8[m]", "<m8[m]", ">m8[m]", + "m8[s]", "=m8[s]", "<m8[s]", ">m8[s]", + "m8[ms]", "=m8[ms]", "<m8[ms]", ">m8[ms]", + "m8[us]", "=m8[us]", "<m8[us]", ">m8[us]", + "m8[ns]", "=m8[ns]", "<m8[ns]", ">m8[ns]", + "m8[ps]", "=m8[ps]", "<m8[ps]", ">m8[ps]", + "m8[fs]", "=m8[fs]", "<m8[fs]", ">m8[fs]", + "m8[as]", "=m8[as]", "<m8[as]", ">m8[as]", +] diff --git a/numpy/typing/_dtype_like.py b/numpy/typing/_dtype_like.py new file mode 100644 index 000000000000..c9bf1a137cb6 --- /dev/null +++ b/numpy/typing/_dtype_like.py @@ -0,0 +1,236 @@ +from typing import ( + Any, + List, + Sequence, + Tuple, + Union, + Type, + TypeVar, + Protocol, + TypedDict, +) + +import numpy as np + +from ._shape import _ShapeLike +from ._generic_alias import _DType as DType + +from ._char_codes import ( + _BoolCodes, + _UInt8Codes, + _UInt16Codes, + _UInt32Codes, + _UInt64Codes, + _Int8Codes, + _Int16Codes, + _Int32Codes, + _Int64Codes, + _Float16Codes, + _Float32Codes, + _Float64Codes, + _Complex64Codes, + _Complex128Codes, + _ByteCodes, + _ShortCodes, + _IntCCodes, + _IntPCodes, + _IntCodes, + _LongLongCodes, + _UByteCodes, + _UShortCodes, + _UIntCCodes, + _UIntPCodes, + _UIntCodes, + _ULongLongCodes, + _HalfCodes, + _SingleCodes, + _DoubleCodes, + _LongDoubleCodes, + _CSingleCodes, + _CDoubleCodes, + _CLongDoubleCodes, + _DT64Codes, + _TD64Codes, + _StrCodes, + _BytesCodes, + _VoidCodes, + _ObjectCodes, +) + +_DTypeLikeNested = Any # TODO: wait for support for recursive types +_DType_co = TypeVar("_DType_co", covariant=True, bound=DType[Any]) + +# Mandatory keys +class _DTypeDictBase(TypedDict): + names: Sequence[str] + formats: Sequence[_DTypeLikeNested] + + +# Mandatory + optional keys +class _DTypeDict(_DTypeDictBase, total=False): + # Only `str` elements are usable as indexing aliases, + # but `titles` can in principle accept any object + offsets: Sequence[int] + titles: Sequence[Any] + itemsize: int + aligned: bool + + +# A protocol for anything with the dtype attribute +class _SupportsDType(Protocol[_DType_co]): + @property + def dtype(self) -> _DType_co: ... + + +# Would create a dtype[np.void] +_VoidDTypeLike = Union[ + # (flexible_dtype, itemsize) + Tuple[_DTypeLikeNested, int], + # (fixed_dtype, shape) + Tuple[_DTypeLikeNested, _ShapeLike], + # [(field_name, field_dtype, field_shape), ...] + # + # The type here is quite broad because NumPy accepts quite a wide + # range of inputs inside the list; see the tests for some + # examples. + List[Any], + # {'names': ..., 'formats': ..., 'offsets': ..., 'titles': ..., + # 'itemsize': ...} + _DTypeDict, + # (base_dtype, new_dtype) + Tuple[_DTypeLikeNested, _DTypeLikeNested], +] + +# Anything that can be coerced into numpy.dtype. +# Reference: https://docs.scipy.org/doc/numpy/reference/arrays.dtypes.html +DTypeLike = Union[ + DType[Any], + # default data type (float64) + None, + # array-scalar types and generic types + Type[Any], # NOTE: We're stuck with `Type[Any]` due to object dtypes + # anything with a dtype attribute + _SupportsDType[DType[Any]], + # character codes, type strings or comma-separated fields, e.g., 'float64' + str, + _VoidDTypeLike, +] + +# NOTE: while it is possible to provide the dtype as a dict of +# dtype-like objects (e.g. `{'field1': ..., 'field2': ..., ...}`), +# this syntax is officially discourged and +# therefore not included in the Union defining `DTypeLike`. +# +# See https://github.com/numpy/numpy/issues/16891 for more details. + +# Aliases for commonly used dtype-like objects. +# Note that the precision of `np.number` subclasses is ignored herein. +_DTypeLikeBool = Union[ + Type[bool], + Type[np.bool_], + DType[np.bool_], + _SupportsDType[DType[np.bool_]], + _BoolCodes, +] +_DTypeLikeUInt = Union[ + Type[np.unsignedinteger], + DType[np.unsignedinteger], + _SupportsDType[DType[np.unsignedinteger]], + _UInt8Codes, + _UInt16Codes, + _UInt32Codes, + _UInt64Codes, + _UByteCodes, + _UShortCodes, + _UIntCCodes, + _UIntPCodes, + _UIntCodes, + _ULongLongCodes, +] +_DTypeLikeInt = Union[ + Type[int], + Type[np.signedinteger], + DType[np.signedinteger], + _SupportsDType[DType[np.signedinteger]], + _Int8Codes, + _Int16Codes, + _Int32Codes, + _Int64Codes, + _ByteCodes, + _ShortCodes, + _IntCCodes, + _IntPCodes, + _IntCodes, + _LongLongCodes, +] +_DTypeLikeFloat = Union[ + Type[float], + Type[np.floating], + DType[np.floating], + _SupportsDType[DType[np.floating]], + _Float16Codes, + _Float32Codes, + _Float64Codes, + _HalfCodes, + _SingleCodes, + _DoubleCodes, + _LongDoubleCodes, +] +_DTypeLikeComplex = Union[ + Type[complex], + Type[np.complexfloating], + DType[np.complexfloating], + _SupportsDType[DType[np.complexfloating]], + _Complex64Codes, + _Complex128Codes, + _CSingleCodes, + _CDoubleCodes, + _CLongDoubleCodes, +] +_DTypeLikeDT64 = Union[ + Type[np.timedelta64], + DType[np.timedelta64], + _SupportsDType[DType[np.timedelta64]], + _TD64Codes, +] +_DTypeLikeTD64 = Union[ + Type[np.datetime64], + DType[np.datetime64], + _SupportsDType[DType[np.datetime64]], + _DT64Codes, +] +_DTypeLikeStr = Union[ + Type[str], + Type[np.str_], + DType[np.str_], + _SupportsDType[DType[np.str_]], + _StrCodes, +] +_DTypeLikeBytes = Union[ + Type[bytes], + Type[np.bytes_], + DType[np.bytes_], + _SupportsDType[DType[np.bytes_]], + _BytesCodes, +] +_DTypeLikeVoid = Union[ + Type[np.void], + DType[np.void], + _SupportsDType[DType[np.void]], + _VoidCodes, + _VoidDTypeLike, +] +_DTypeLikeObject = Union[ + type, + DType[np.object_], + _SupportsDType[DType[np.object_]], + _ObjectCodes, +] + +_DTypeLikeComplex_co = Union[ + _DTypeLikeBool, + _DTypeLikeUInt, + _DTypeLikeInt, + _DTypeLikeFloat, + _DTypeLikeComplex, +] diff --git a/numpy/typing/_extended_precision.py b/numpy/typing/_extended_precision.py new file mode 100644 index 000000000000..edc1778ce16f --- /dev/null +++ b/numpy/typing/_extended_precision.py @@ -0,0 +1,43 @@ +"""A module with platform-specific extended precision +`numpy.number` subclasses. + +The subclasses are defined here (instead of ``__init__.pyi``) such +that they can be imported conditionally via the numpy's mypy plugin. +""" + +from typing import TYPE_CHECKING + +import numpy as np +from . import ( + _80Bit, + _96Bit, + _128Bit, + _256Bit, +) + +if TYPE_CHECKING: + uint128 = np.unsignedinteger[_128Bit] + uint256 = np.unsignedinteger[_256Bit] + int128 = np.signedinteger[_128Bit] + int256 = np.signedinteger[_256Bit] + float80 = np.floating[_80Bit] + float96 = np.floating[_96Bit] + float128 = np.floating[_128Bit] + float256 = np.floating[_256Bit] + complex160 = np.complexfloating[_80Bit, _80Bit] + complex192 = np.complexfloating[_96Bit, _96Bit] + complex256 = np.complexfloating[_128Bit, _128Bit] + complex512 = np.complexfloating[_256Bit, _256Bit] +else: + uint128 = Any + uint256 = Any + int128 = Any + int256 = Any + float80 = Any + float96 = Any + float128 = Any + float256 = Any + complex160 = Any + complex192 = Any + complex256 = Any + complex512 = Any diff --git a/numpy/typing/_generic_alias.py b/numpy/typing/_generic_alias.py new file mode 100644 index 000000000000..1eb2c8c05f02 --- /dev/null +++ b/numpy/typing/_generic_alias.py @@ -0,0 +1,215 @@ +from __future__ import annotations + +import sys +import types +from typing import ( + Any, + ClassVar, + FrozenSet, + Generator, + Iterable, + Iterator, + List, + NoReturn, + Tuple, + Type, + TypeVar, + TYPE_CHECKING, +) + +import numpy as np + +__all__ = ["_GenericAlias", "NDArray"] + +_T = TypeVar("_T", bound="_GenericAlias") + + +def _to_str(obj: object) -> str: + """Helper function for `_GenericAlias.__repr__`.""" + if obj is Ellipsis: + return '...' + elif isinstance(obj, type) and not isinstance(obj, _GENERIC_ALIAS_TYPE): + if obj.__module__ == 'builtins': + return obj.__qualname__ + else: + return f'{obj.__module__}.{obj.__qualname__}' + else: + return repr(obj) + + +def _parse_parameters(args: Iterable[Any]) -> Generator[TypeVar, None, None]: + """Search for all typevars and typevar-containing objects in `args`. + + Helper function for `_GenericAlias.__init__`. + + """ + for i in args: + if hasattr(i, "__parameters__"): + yield from i.__parameters__ + elif isinstance(i, TypeVar): + yield i + + +def _reconstruct_alias(alias: _T, parameters: Iterator[TypeVar]) -> _T: + """Recursively replace all typevars with those from `parameters`. + + Helper function for `_GenericAlias.__getitem__`. + + """ + args = [] + for i in alias.__args__: + if isinstance(i, TypeVar): + value: Any = next(parameters) + elif isinstance(i, _GenericAlias): + value = _reconstruct_alias(i, parameters) + elif hasattr(i, "__parameters__"): + prm_tup = tuple(next(parameters) for _ in i.__parameters__) + value = i[prm_tup] + else: + value = i + args.append(value) + + cls = type(alias) + return cls(alias.__origin__, tuple(args)) + + +class _GenericAlias: + """A python-based backport of the `types.GenericAlias` class. + + E.g. for ``t = list[int]``, ``t.__origin__`` is ``list`` and + ``t.__args__`` is ``(int,)``. + + See Also + -------- + :pep:`585` + The PEP responsible for introducing `types.GenericAlias`. + + """ + + __slots__ = ("__weakref__", "_origin", "_args", "_parameters", "_hash") + + @property + def __origin__(self) -> type: + return super().__getattribute__("_origin") + + @property + def __args__(self) -> Tuple[object, ...]: + return super().__getattribute__("_args") + + @property + def __parameters__(self) -> Tuple[TypeVar, ...]: + """Type variables in the ``GenericAlias``.""" + return super().__getattribute__("_parameters") + + def __init__( + self, + origin: type, + args: object | Tuple[object, ...], + ) -> None: + self._origin = origin + self._args = args if isinstance(args, tuple) else (args,) + self._parameters = tuple(_parse_parameters(self.__args__)) + + @property + def __call__(self) -> type: + return self.__origin__ + + def __reduce__(self: _T) -> Tuple[ + Type[_T], + Tuple[type, Tuple[object, ...]], + ]: + cls = type(self) + return cls, (self.__origin__, self.__args__) + + def __mro_entries__(self, bases: Iterable[object]) -> Tuple[type]: + return (self.__origin__,) + + def __dir__(self) -> List[str]: + """Implement ``dir(self)``.""" + cls = type(self) + dir_origin = set(dir(self.__origin__)) + return sorted(cls._ATTR_EXCEPTIONS | dir_origin) + + def __hash__(self) -> int: + """Return ``hash(self)``.""" + # Attempt to use the cached hash + try: + return super().__getattribute__("_hash") + except AttributeError: + self._hash: int = hash(self.__origin__) ^ hash(self.__args__) + return super().__getattribute__("_hash") + + def __instancecheck__(self, obj: object) -> NoReturn: + """Check if an `obj` is an instance.""" + raise TypeError("isinstance() argument 2 cannot be a " + "parameterized generic") + + def __subclasscheck__(self, cls: type) -> NoReturn: + """Check if a `cls` is a subclass.""" + raise TypeError("issubclass() argument 2 cannot be a " + "parameterized generic") + + def __repr__(self) -> str: + """Return ``repr(self)``.""" + args = ", ".join(_to_str(i) for i in self.__args__) + origin = _to_str(self.__origin__) + return f"{origin}[{args}]" + + def __getitem__(self: _T, key: object | Tuple[object, ...]) -> _T: + """Return ``self[key]``.""" + key_tup = key if isinstance(key, tuple) else (key,) + + if len(self.__parameters__) == 0: + raise TypeError(f"There are no type variables left in {self}") + elif len(key_tup) > len(self.__parameters__): + raise TypeError(f"Too many arguments for {self}") + elif len(key_tup) < len(self.__parameters__): + raise TypeError(f"Too few arguments for {self}") + + key_iter = iter(key_tup) + return _reconstruct_alias(self, key_iter) + + def __eq__(self, value: object) -> bool: + """Return ``self == value``.""" + if not isinstance(value, _GENERIC_ALIAS_TYPE): + return NotImplemented + return ( + self.__origin__ == value.__origin__ and + self.__args__ == value.__args__ + ) + + _ATTR_EXCEPTIONS: ClassVar[FrozenSet[str]] = frozenset({ + "__origin__", + "__args__", + "__parameters__", + "__mro_entries__", + "__reduce__", + "__reduce_ex__", + "__copy__", + "__deepcopy__", + }) + + def __getattribute__(self, name: str) -> Any: + """Return ``getattr(self, name)``.""" + # Pull the attribute from `__origin__` unless its + # name is in `_ATTR_EXCEPTIONS` + cls = type(self) + if name in cls._ATTR_EXCEPTIONS: + return super().__getattribute__(name) + return getattr(self.__origin__, name) + + +# See `_GenericAlias.__eq__` +if sys.version_info >= (3, 9): + _GENERIC_ALIAS_TYPE = (_GenericAlias, types.GenericAlias) +else: + _GENERIC_ALIAS_TYPE = (_GenericAlias,) + +ScalarType = TypeVar("ScalarType", bound=np.generic, covariant=True) + +if TYPE_CHECKING or sys.version_info >= (3, 9): + _DType = np.dtype[ScalarType] + NDArray = np.ndarray[Any, np.dtype[ScalarType]] +else: + _DType = _GenericAlias(np.dtype, (ScalarType,)) + NDArray = _GenericAlias(np.ndarray, (Any, _DType)) diff --git a/numpy/typing/_nbit.py b/numpy/typing/_nbit.py new file mode 100644 index 000000000000..b8d35db4f594 --- /dev/null +++ b/numpy/typing/_nbit.py @@ -0,0 +1,16 @@ +"""A module with the precisions of platform-specific `~numpy.number`s.""" + +from typing import Any + +# To-be replaced with a `npt.NBitBase` subclass by numpy's mypy plugin +_NBitByte = Any +_NBitShort = Any +_NBitIntC = Any +_NBitIntP = Any +_NBitInt = Any +_NBitLongLong = Any + +_NBitHalf = Any +_NBitSingle = Any +_NBitDouble = Any +_NBitLongDouble = Any diff --git a/numpy/typing/_nested_sequence.py b/numpy/typing/_nested_sequence.py new file mode 100644 index 000000000000..a853303ca95d --- /dev/null +++ b/numpy/typing/_nested_sequence.py @@ -0,0 +1,90 @@ +"""A module containing the `_NestedSequence` protocol.""" + +from __future__ import annotations + +from typing import ( + Any, + Iterator, + overload, + TypeVar, + Protocol, +) + +__all__ = ["_NestedSequence"] + +_T_co = TypeVar("_T_co", covariant=True) + + +class _NestedSequence(Protocol[_T_co]): + """A protocol for representing nested sequences. + + Warning + ------- + `_NestedSequence` currently does not work in combination with typevars, + *e.g.* ``def func(a: _NestedSequnce[T]) -> T: ...``. + + See Also + -------- + `collections.abc.Sequence` + ABCs for read-only and mutable :term:`sequences`. + + Examples + -------- + .. code-block:: python + + >>> from __future__ import annotations + + >>> from typing import TYPE_CHECKING + >>> import numpy as np + >>> from numpy.typing import _NestedSequnce + + >>> def get_dtype(seq: _NestedSequnce[float]) -> np.dtype[np.float64]: + ... return np.asarray(seq).dtype + + >>> a = get_dtype([1.0]) + >>> b = get_dtype([[1.0]]) + >>> c = get_dtype([[[1.0]]]) + >>> d = get_dtype([[[[1.0]]]]) + + >>> if TYPE_CHECKING: + ... reveal_locals() + ... # note: Revealed local types are: + ... # note: a: numpy.dtype[numpy.floating[numpy.typing._64Bit]] + ... # note: b: numpy.dtype[numpy.floating[numpy.typing._64Bit]] + ... # note: c: numpy.dtype[numpy.floating[numpy.typing._64Bit]] + ... # note: d: numpy.dtype[numpy.floating[numpy.typing._64Bit]] + + """ + + def __len__(self, /) -> int: + """Implement ``len(self)``.""" + raise NotImplementedError + + @overload + def __getitem__(self, index: int, /) -> _T_co | _NestedSequence[_T_co]: ... + @overload + def __getitem__(self, index: slice, /) -> _NestedSequence[_T_co]: ... + + def __getitem__(self, index, /): + """Implement ``self[x]``.""" + raise NotImplementedError + + def __contains__(self, x: object, /) -> bool: + """Implement ``x in self``.""" + raise NotImplementedError + + def __iter__(self, /) -> Iterator[_T_co | _NestedSequence[_T_co]]: + """Implement ``iter(self)``.""" + raise NotImplementedError + + def __reversed__(self, /) -> Iterator[_T_co | _NestedSequence[_T_co]]: + """Implement ``reversed(self)``.""" + raise NotImplementedError + + def count(self, value: Any, /) -> int: + """Return the number of occurrences of `value`.""" + raise NotImplementedError + + def index(self, value: Any, /) -> int: + """Return the first index of `value`.""" + raise NotImplementedError diff --git a/numpy/typing/_scalars.py b/numpy/typing/_scalars.py new file mode 100644 index 000000000000..516b996dc007 --- /dev/null +++ b/numpy/typing/_scalars.py @@ -0,0 +1,30 @@ +from typing import Union, Tuple, Any + +import numpy as np + +# NOTE: `_StrLike_co` and `_BytesLike_co` are pointless, as `np.str_` and +# `np.bytes_` are already subclasses of their builtin counterpart + +_CharLike_co = Union[str, bytes] + +# The 6 `<X>Like_co` type-aliases below represent all scalars that can be +# coerced into `<X>` (with the casting rule `same_kind`) +_BoolLike_co = Union[bool, np.bool_] +_UIntLike_co = Union[_BoolLike_co, np.unsignedinteger] +_IntLike_co = Union[_BoolLike_co, int, np.integer] +_FloatLike_co = Union[_IntLike_co, float, np.floating] +_ComplexLike_co = Union[_FloatLike_co, complex, np.complexfloating] +_TD64Like_co = Union[_IntLike_co, np.timedelta64] + +_NumberLike_co = Union[int, float, complex, np.number, np.bool_] +_ScalarLike_co = Union[ + int, + float, + complex, + str, + bytes, + np.generic, +] + +# `_VoidLike_co` is technically not a scalar, but it's close enough +_VoidLike_co = Union[Tuple[Any, ...], np.void] diff --git a/numpy/typing/_shape.py b/numpy/typing/_shape.py new file mode 100644 index 000000000000..c28859b19bae --- /dev/null +++ b/numpy/typing/_shape.py @@ -0,0 +1,6 @@ +from typing import Sequence, Tuple, Union, SupportsIndex + +_Shape = Tuple[int, ...] + +# Anything that can be coerced to a shape tuple +_ShapeLike = Union[SupportsIndex, Sequence[SupportsIndex]] diff --git a/numpy/typing/_ufunc.pyi b/numpy/typing/_ufunc.pyi new file mode 100644 index 000000000000..1be3500c127e --- /dev/null +++ b/numpy/typing/_ufunc.pyi @@ -0,0 +1,405 @@ +"""A module with private type-check-only `numpy.ufunc` subclasses. + +The signatures of the ufuncs are too varied to reasonably type +with a single class. So instead, `ufunc` has been expanded into +four private subclasses, one for each combination of +`~ufunc.nin` and `~ufunc.nout`. + +""" + +from typing import ( + Any, + Generic, + List, + overload, + Tuple, + TypeVar, + Literal, + SupportsIndex, +) + +from numpy import ufunc, _CastingKind, _OrderKACF +from numpy.typing import NDArray + +from ._shape import _ShapeLike +from ._scalars import _ScalarLike_co +from ._array_like import ArrayLike, _ArrayLikeBool_co, _ArrayLikeInt_co +from ._dtype_like import DTypeLike + +_T = TypeVar("_T") +_2Tuple = Tuple[_T, _T] +_3Tuple = Tuple[_T, _T, _T] +_4Tuple = Tuple[_T, _T, _T, _T] + +_NTypes = TypeVar("_NTypes", bound=int) +_IDType = TypeVar("_IDType", bound=Any) +_NameType = TypeVar("_NameType", bound=str) + +# NOTE: In reality `extobj` should be a length of list 3 containing an +# int, an int, and a callable, but there's no way to properly express +# non-homogenous lists. +# Use `Any` over `Union` to avoid issues related to lists invariance. + +# NOTE: `reduce`, `accumulate`, `reduceat` and `outer` raise a ValueError for +# ufuncs that don't accept two input arguments and return one output argument. +# In such cases the respective methods are simply typed as `None`. + +# NOTE: Similarly, `at` won't be defined for ufuncs that return +# multiple outputs; in such cases `at` is typed as `None` + +# NOTE: If 2 output types are returned then `out` must be a +# 2-tuple of arrays. Otherwise `None` or a plain array are also acceptable + +class _UFunc_Nin1_Nout1(ufunc, Generic[_NameType, _NTypes, _IDType]): + @property + def __name__(self) -> _NameType: ... + @property + def ntypes(self) -> _NTypes: ... + @property + def identity(self) -> _IDType: ... + @property + def nin(self) -> Literal[1]: ... + @property + def nout(self) -> Literal[1]: ... + @property + def nargs(self) -> Literal[2]: ... + @property + def signature(self) -> None: ... + @property + def reduce(self) -> None: ... + @property + def accumulate(self) -> None: ... + @property + def reduceat(self) -> None: ... + @property + def outer(self) -> None: ... + + @overload + def __call__( + self, + __x1: _ScalarLike_co, + out: None = ..., + *, + where: None | _ArrayLikeBool_co = ..., + casting: _CastingKind = ..., + order: _OrderKACF = ..., + dtype: DTypeLike = ..., + subok: bool = ..., + signature: str | _2Tuple[None | str] = ..., + extobj: List[Any] = ..., + ) -> Any: ... + @overload + def __call__( + self, + __x1: ArrayLike, + out: None | NDArray[Any] | Tuple[NDArray[Any]] = ..., + *, + where: None | _ArrayLikeBool_co = ..., + casting: _CastingKind = ..., + order: _OrderKACF = ..., + dtype: DTypeLike = ..., + subok: bool = ..., + signature: str | _2Tuple[None | str] = ..., + extobj: List[Any] = ..., + ) -> NDArray[Any]: ... + + def at( + self, + a: NDArray[Any], + indices: _ArrayLikeInt_co, + /, + ) -> None: ... + +class _UFunc_Nin2_Nout1(ufunc, Generic[_NameType, _NTypes, _IDType]): + @property + def __name__(self) -> _NameType: ... + @property + def ntypes(self) -> _NTypes: ... + @property + def identity(self) -> _IDType: ... + @property + def nin(self) -> Literal[2]: ... + @property + def nout(self) -> Literal[1]: ... + @property + def nargs(self) -> Literal[3]: ... + @property + def signature(self) -> None: ... + + @overload + def __call__( + self, + __x1: _ScalarLike_co, + __x2: _ScalarLike_co, + out: None = ..., + *, + where: None | _ArrayLikeBool_co = ..., + casting: _CastingKind = ..., + order: _OrderKACF = ..., + dtype: DTypeLike = ..., + subok: bool = ..., + signature: str | _3Tuple[None | str] = ..., + extobj: List[Any] = ..., + ) -> Any: ... + @overload + def __call__( + self, + __x1: ArrayLike, + __x2: ArrayLike, + out: None | NDArray[Any] | Tuple[NDArray[Any]] = ..., + *, + where: None | _ArrayLikeBool_co = ..., + casting: _CastingKind = ..., + order: _OrderKACF = ..., + dtype: DTypeLike = ..., + subok: bool = ..., + signature: str | _3Tuple[None | str] = ..., + extobj: List[Any] = ..., + ) -> NDArray[Any]: ... + + def at( + self, + a: NDArray[Any], + indices: _ArrayLikeInt_co, + b: ArrayLike, + /, + ) -> None: ... + + def reduce( + self, + array: ArrayLike, + axis: None | _ShapeLike = ..., + dtype: DTypeLike = ..., + out: None | NDArray[Any] = ..., + keepdims: bool = ..., + initial: Any = ..., + where: _ArrayLikeBool_co = ..., + ) -> Any: ... + + def accumulate( + self, + array: ArrayLike, + axis: SupportsIndex = ..., + dtype: DTypeLike = ..., + out: None | NDArray[Any] = ..., + ) -> NDArray[Any]: ... + + def reduceat( + self, + array: ArrayLike, + indices: _ArrayLikeInt_co, + axis: SupportsIndex = ..., + dtype: DTypeLike = ..., + out: None | NDArray[Any] = ..., + ) -> NDArray[Any]: ... + + # Expand `**kwargs` into explicit keyword-only arguments + @overload + def outer( + self, + A: _ScalarLike_co, + B: _ScalarLike_co, + /, *, + out: None = ..., + where: None | _ArrayLikeBool_co = ..., + casting: _CastingKind = ..., + order: _OrderKACF = ..., + dtype: DTypeLike = ..., + subok: bool = ..., + signature: str | _3Tuple[None | str] = ..., + extobj: List[Any] = ..., + ) -> Any: ... + @overload + def outer( # type: ignore[misc] + self, + A: ArrayLike, + B: ArrayLike, + /, *, + out: None | NDArray[Any] | Tuple[NDArray[Any]] = ..., + where: None | _ArrayLikeBool_co = ..., + casting: _CastingKind = ..., + order: _OrderKACF = ..., + dtype: DTypeLike = ..., + subok: bool = ..., + signature: str | _3Tuple[None | str] = ..., + extobj: List[Any] = ..., + ) -> NDArray[Any]: ... + +class _UFunc_Nin1_Nout2(ufunc, Generic[_NameType, _NTypes, _IDType]): + @property + def __name__(self) -> _NameType: ... + @property + def ntypes(self) -> _NTypes: ... + @property + def identity(self) -> _IDType: ... + @property + def nin(self) -> Literal[1]: ... + @property + def nout(self) -> Literal[2]: ... + @property + def nargs(self) -> Literal[3]: ... + @property + def signature(self) -> None: ... + @property + def at(self) -> None: ... + @property + def reduce(self) -> None: ... + @property + def accumulate(self) -> None: ... + @property + def reduceat(self) -> None: ... + @property + def outer(self) -> None: ... + + @overload + def __call__( + self, + __x1: _ScalarLike_co, + __out1: None = ..., + __out2: None = ..., + *, + where: None | _ArrayLikeBool_co = ..., + casting: _CastingKind = ..., + order: _OrderKACF = ..., + dtype: DTypeLike = ..., + subok: bool = ..., + signature: str | _3Tuple[None | str] = ..., + extobj: List[Any] = ..., + ) -> _2Tuple[Any]: ... + @overload + def __call__( + self, + __x1: ArrayLike, + __out1: None | NDArray[Any] = ..., + __out2: None | NDArray[Any] = ..., + *, + out: _2Tuple[NDArray[Any]] = ..., + where: None | _ArrayLikeBool_co = ..., + casting: _CastingKind = ..., + order: _OrderKACF = ..., + dtype: DTypeLike = ..., + subok: bool = ..., + signature: str | _3Tuple[None | str] = ..., + extobj: List[Any] = ..., + ) -> _2Tuple[NDArray[Any]]: ... + +class _UFunc_Nin2_Nout2(ufunc, Generic[_NameType, _NTypes, _IDType]): + @property + def __name__(self) -> _NameType: ... + @property + def ntypes(self) -> _NTypes: ... + @property + def identity(self) -> _IDType: ... + @property + def nin(self) -> Literal[2]: ... + @property + def nout(self) -> Literal[2]: ... + @property + def nargs(self) -> Literal[4]: ... + @property + def signature(self) -> None: ... + @property + def at(self) -> None: ... + @property + def reduce(self) -> None: ... + @property + def accumulate(self) -> None: ... + @property + def reduceat(self) -> None: ... + @property + def outer(self) -> None: ... + + @overload + def __call__( + self, + __x1: _ScalarLike_co, + __x2: _ScalarLike_co, + __out1: None = ..., + __out2: None = ..., + *, + where: None | _ArrayLikeBool_co = ..., + casting: _CastingKind = ..., + order: _OrderKACF = ..., + dtype: DTypeLike = ..., + subok: bool = ..., + signature: str | _4Tuple[None | str] = ..., + extobj: List[Any] = ..., + ) -> _2Tuple[Any]: ... + @overload + def __call__( + self, + __x1: ArrayLike, + __x2: ArrayLike, + __out1: None | NDArray[Any] = ..., + __out2: None | NDArray[Any] = ..., + *, + out: _2Tuple[NDArray[Any]] = ..., + where: None | _ArrayLikeBool_co = ..., + casting: _CastingKind = ..., + order: _OrderKACF = ..., + dtype: DTypeLike = ..., + subok: bool = ..., + signature: str | _4Tuple[None | str] = ..., + extobj: List[Any] = ..., + ) -> _2Tuple[NDArray[Any]]: ... + +class _GUFunc_Nin2_Nout1(ufunc, Generic[_NameType, _NTypes, _IDType]): + @property + def __name__(self) -> _NameType: ... + @property + def ntypes(self) -> _NTypes: ... + @property + def identity(self) -> _IDType: ... + @property + def nin(self) -> Literal[2]: ... + @property + def nout(self) -> Literal[1]: ... + @property + def nargs(self) -> Literal[3]: ... + + # NOTE: In practice the only gufunc in the main name is `matmul`, + # so we can use its signature here + @property + def signature(self) -> Literal["(n?,k),(k,m?)->(n?,m?)"]: ... + @property + def reduce(self) -> None: ... + @property + def accumulate(self) -> None: ... + @property + def reduceat(self) -> None: ... + @property + def outer(self) -> None: ... + @property + def at(self) -> None: ... + + # Scalar for 1D array-likes; ndarray otherwise + @overload + def __call__( + self, + __x1: ArrayLike, + __x2: ArrayLike, + out: None = ..., + *, + casting: _CastingKind = ..., + order: _OrderKACF = ..., + dtype: DTypeLike = ..., + subok: bool = ..., + signature: str | _3Tuple[None | str] = ..., + extobj: List[Any] = ..., + axes: List[_2Tuple[SupportsIndex]] = ..., + ) -> Any: ... + @overload + def __call__( + self, + __x1: ArrayLike, + __x2: ArrayLike, + out: NDArray[Any] | Tuple[NDArray[Any]], + *, + casting: _CastingKind = ..., + order: _OrderKACF = ..., + dtype: DTypeLike = ..., + subok: bool = ..., + signature: str | _3Tuple[None | str] = ..., + extobj: List[Any] = ..., + axes: List[_2Tuple[SupportsIndex]] = ..., + ) -> NDArray[Any]: ... diff --git a/numpy/typing/mypy_plugin.py b/numpy/typing/mypy_plugin.py new file mode 100644 index 000000000000..5ac75f94da93 --- /dev/null +++ b/numpy/typing/mypy_plugin.py @@ -0,0 +1,197 @@ +"""A mypy_ plugin for managing a number of platform-specific annotations. +Its functionality can be split into three distinct parts: + +* Assigning the (platform-dependent) precisions of certain `~numpy.number` + subclasses, including the likes of `~numpy.int_`, `~numpy.intp` and + `~numpy.longlong`. See the documentation on + :ref:`scalar types <arrays.scalars.built-in>` for a comprehensive overview + of the affected classes. Without the plugin the precision of all relevant + classes will be inferred as `~typing.Any`. +* Removing all extended-precision `~numpy.number` subclasses that are + unavailable for the platform in question. Most notably this includes the + likes of `~numpy.float128` and `~numpy.complex256`. Without the plugin *all* + extended-precision types will, as far as mypy is concerned, be available + to all platforms. +* Assigning the (platform-dependent) precision of `~numpy.ctypeslib.c_intp`. + Without the plugin the type will default to `ctypes.c_int64`. + + .. versionadded:: 1.22 + +Examples +-------- +To enable the plugin, one must add it to their mypy `configuration file`_: + +.. code-block:: ini + + [mypy] + plugins = numpy.typing.mypy_plugin + +.. _mypy: http://mypy-lang.org/ +.. _configuration file: https://mypy.readthedocs.io/en/stable/config_file.html + +""" + +from __future__ import annotations + +from collections.abc import Iterable +from typing import Final, TYPE_CHECKING, Callable + +import numpy as np + +try: + import mypy.types + from mypy.types import Type + from mypy.plugin import Plugin, AnalyzeTypeContext + from mypy.nodes import MypyFile, ImportFrom, Statement + from mypy.build import PRI_MED + + _HookFunc = Callable[[AnalyzeTypeContext], Type] + MYPY_EX: None | ModuleNotFoundError = None +except ModuleNotFoundError as ex: + MYPY_EX = ex + +__all__: list[str] = [] + + +def _get_precision_dict() -> dict[str, str]: + names = [ + ("_NBitByte", np.byte), + ("_NBitShort", np.short), + ("_NBitIntC", np.intc), + ("_NBitIntP", np.intp), + ("_NBitInt", np.int_), + ("_NBitLongLong", np.longlong), + + ("_NBitHalf", np.half), + ("_NBitSingle", np.single), + ("_NBitDouble", np.double), + ("_NBitLongDouble", np.longdouble), + ] + ret = {} + for name, typ in names: + n: int = 8 * typ().dtype.itemsize + ret[f'numpy.typing._nbit.{name}'] = f"numpy._{n}Bit" + return ret + + +def _get_extended_precision_list() -> list[str]: + extended_types = [np.ulonglong, np.longlong, np.longdouble, np.clongdouble] + extended_names = { + "uint128", + "uint256", + "int128", + "int256", + "float80", + "float96", + "float128", + "float256", + "complex160", + "complex192", + "complex256", + "complex512", + } + return [i.__name__ for i in extended_types if i.__name__ in extended_names] + + +def _get_c_intp_name() -> str: + # Adapted from `np.core._internal._getintp_ctype` + char = np.dtype('p').char + if char == 'i': + return "c_int" + elif char == 'l': + return "c_long" + elif char == 'q': + return "c_longlong" + else: + return "c_long" + + +#: A dictionary mapping type-aliases in `numpy.typing._nbit` to +#: concrete `numpy.typing.NBitBase` subclasses. +_PRECISION_DICT: Final = _get_precision_dict() + +#: A list with the names of all extended precision `np.number` subclasses. +_EXTENDED_PRECISION_LIST: Final = _get_extended_precision_list() + +#: The name of the ctypes quivalent of `np.intp` +_C_INTP: Final = _get_c_intp_name() + + +def _hook(ctx: AnalyzeTypeContext) -> Type: + """Replace a type-alias with a concrete ``NBitBase`` subclass.""" + typ, _, api = ctx + name = typ.name.split(".")[-1] + name_new = _PRECISION_DICT[f"numpy.typing._nbit.{name}"] + return api.named_type(name_new) + + +if TYPE_CHECKING or MYPY_EX is None: + def _index(iterable: Iterable[Statement], id: str) -> int: + """Identify the first ``ImportFrom`` instance the specified `id`.""" + for i, value in enumerate(iterable): + if getattr(value, "id", None) == id: + return i + raise ValueError("Failed to identify a `ImportFrom` instance " + f"with the following id: {id!r}") + + def _override_imports( + file: MypyFile, + module: str, + imports: list[tuple[str, None | str]], + ) -> None: + """Override the first `module`-based import with new `imports`.""" + # Construct a new `from module import y` statement + import_obj = ImportFrom(module, 0, names=imports) + import_obj.is_top_level = True + + # Replace the first `module`-based import statement with `import_obj` + for lst in [file.defs, file.imports]: # type: list[Statement] + i = _index(lst, module) + lst[i] = import_obj + + class _NumpyPlugin(Plugin): + """A mypy plugin for handling versus numpy-specific typing tasks.""" + + def get_type_analyze_hook(self, fullname: str) -> None | _HookFunc: + """Set the precision of platform-specific `numpy.number` + subclasses. + + For example: `numpy.int_`, `numpy.longlong` and `numpy.longdouble`. + """ + if fullname in _PRECISION_DICT: + return _hook + return None + + def get_additional_deps( + self, file: MypyFile + ) -> list[tuple[int, str, int]]: + """Handle all import-based overrides. + + * Import platform-specific extended-precision `numpy.number` + subclasses (*e.g.* `numpy.float96`, `numpy.float128` and + `numpy.complex256`). + * Import the appropriate `ctypes` equivalent to `numpy.intp`. + + """ + ret = [(PRI_MED, file.fullname, -1)] + + if file.fullname == "numpy": + _override_imports( + file, "numpy.typing._extended_precision", + imports=[(v, v) for v in _EXTENDED_PRECISION_LIST], + ) + elif file.fullname == "numpy.ctypeslib": + _override_imports( + file, "ctypes", + imports=[(_C_INTP, "_c_intp")], + ) + return ret + + def plugin(version: str) -> type[_NumpyPlugin]: + """An entry-point for mypy.""" + return _NumpyPlugin + +else: + def plugin(version: str) -> type[_NumpyPlugin]: + """An entry-point for mypy.""" + raise MYPY_EX diff --git a/numpy/typing/setup.py b/numpy/typing/setup.py new file mode 100644 index 000000000000..694a756dc5ab --- /dev/null +++ b/numpy/typing/setup.py @@ -0,0 +1,12 @@ +def configuration(parent_package='', top_path=None): + from numpy.distutils.misc_util import Configuration + config = Configuration('typing', parent_package, top_path) + config.add_subpackage('tests') + config.add_data_dir('tests/data') + config.add_data_files('*.pyi') + return config + + +if __name__ == '__main__': + from numpy.distutils.core import setup + setup(configuration=configuration) diff --git a/numpy/typing/tests/__init__.py b/numpy/typing/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/numpy/typing/tests/data/fail/arithmetic.pyi b/numpy/typing/tests/data/fail/arithmetic.pyi new file mode 100644 index 000000000000..b99b24c1f6b4 --- /dev/null +++ b/numpy/typing/tests/data/fail/arithmetic.pyi @@ -0,0 +1,121 @@ +from typing import List, Any +import numpy as np + +b_ = np.bool_() +dt = np.datetime64(0, "D") +td = np.timedelta64(0, "D") + +AR_b: np.ndarray[Any, np.dtype[np.bool_]] +AR_u: np.ndarray[Any, np.dtype[np.uint32]] +AR_i: np.ndarray[Any, np.dtype[np.int64]] +AR_f: np.ndarray[Any, np.dtype[np.float64]] +AR_c: np.ndarray[Any, np.dtype[np.complex128]] +AR_m: np.ndarray[Any, np.dtype[np.timedelta64]] +AR_M: np.ndarray[Any, np.dtype[np.datetime64]] + +ANY: Any + +AR_LIKE_b: List[bool] +AR_LIKE_u: List[np.uint32] +AR_LIKE_i: List[int] +AR_LIKE_f: List[float] +AR_LIKE_c: List[complex] +AR_LIKE_m: List[np.timedelta64] +AR_LIKE_M: List[np.datetime64] + +# Array subtraction + +# NOTE: mypys `NoReturn` errors are, unfortunately, not that great +_1 = AR_b - AR_LIKE_b # E: Need type annotation +_2 = AR_LIKE_b - AR_b # E: Need type annotation +AR_i - bytes() # E: No overload variant + +AR_f - AR_LIKE_m # E: Unsupported operand types +AR_f - AR_LIKE_M # E: Unsupported operand types +AR_c - AR_LIKE_m # E: Unsupported operand types +AR_c - AR_LIKE_M # E: Unsupported operand types + +AR_m - AR_LIKE_f # E: Unsupported operand types +AR_M - AR_LIKE_f # E: Unsupported operand types +AR_m - AR_LIKE_c # E: Unsupported operand types +AR_M - AR_LIKE_c # E: Unsupported operand types + +AR_m - AR_LIKE_M # E: Unsupported operand types +AR_LIKE_m - AR_M # E: Unsupported operand types + +# array floor division + +AR_M // AR_LIKE_b # E: Unsupported operand types +AR_M // AR_LIKE_u # E: Unsupported operand types +AR_M // AR_LIKE_i # E: Unsupported operand types +AR_M // AR_LIKE_f # E: Unsupported operand types +AR_M // AR_LIKE_c # E: Unsupported operand types +AR_M // AR_LIKE_m # E: Unsupported operand types +AR_M // AR_LIKE_M # E: Unsupported operand types + +AR_b // AR_LIKE_M # E: Unsupported operand types +AR_u // AR_LIKE_M # E: Unsupported operand types +AR_i // AR_LIKE_M # E: Unsupported operand types +AR_f // AR_LIKE_M # E: Unsupported operand types +AR_c // AR_LIKE_M # E: Unsupported operand types +AR_m // AR_LIKE_M # E: Unsupported operand types +AR_M // AR_LIKE_M # E: Unsupported operand types + +_3 = AR_m // AR_LIKE_b # E: Need type annotation +AR_m // AR_LIKE_c # E: Unsupported operand types + +AR_b // AR_LIKE_m # E: Unsupported operand types +AR_u // AR_LIKE_m # E: Unsupported operand types +AR_i // AR_LIKE_m # E: Unsupported operand types +AR_f // AR_LIKE_m # E: Unsupported operand types +AR_c // AR_LIKE_m # E: Unsupported operand types + +# Array multiplication + +AR_b *= AR_LIKE_u # E: incompatible type +AR_b *= AR_LIKE_i # E: incompatible type +AR_b *= AR_LIKE_f # E: incompatible type +AR_b *= AR_LIKE_c # E: incompatible type +AR_b *= AR_LIKE_m # E: incompatible type + +AR_u *= AR_LIKE_i # E: incompatible type +AR_u *= AR_LIKE_f # E: incompatible type +AR_u *= AR_LIKE_c # E: incompatible type +AR_u *= AR_LIKE_m # E: incompatible type + +AR_i *= AR_LIKE_f # E: incompatible type +AR_i *= AR_LIKE_c # E: incompatible type +AR_i *= AR_LIKE_m # E: incompatible type + +AR_f *= AR_LIKE_c # E: incompatible type +AR_f *= AR_LIKE_m # E: incompatible type + +# Array power + +AR_b **= AR_LIKE_b # E: Invalid self argument +AR_b **= AR_LIKE_u # E: Invalid self argument +AR_b **= AR_LIKE_i # E: Invalid self argument +AR_b **= AR_LIKE_f # E: Invalid self argument +AR_b **= AR_LIKE_c # E: Invalid self argument + +AR_u **= AR_LIKE_i # E: incompatible type +AR_u **= AR_LIKE_f # E: incompatible type +AR_u **= AR_LIKE_c # E: incompatible type + +AR_i **= AR_LIKE_f # E: incompatible type +AR_i **= AR_LIKE_c # E: incompatible type + +AR_f **= AR_LIKE_c # E: incompatible type + +# Scalars + +b_ - b_ # E: No overload variant + +dt + dt # E: Unsupported operand types +td - dt # E: Unsupported operand types +td % 1 # E: Unsupported operand types +td / dt # E: No overload +td % dt # E: Unsupported operand types + +-b_ # E: Unsupported operand type ++b_ # E: Unsupported operand type diff --git a/numpy/typing/tests/data/fail/array_constructors.pyi b/numpy/typing/tests/data/fail/array_constructors.pyi new file mode 100644 index 000000000000..4f0a60b5ba93 --- /dev/null +++ b/numpy/typing/tests/data/fail/array_constructors.pyi @@ -0,0 +1,31 @@ +import numpy as np + +a: np.ndarray +generator = (i for i in range(10)) + +np.require(a, requirements=1) # E: No overload variant +np.require(a, requirements="TEST") # E: incompatible type + +np.zeros("test") # E: incompatible type +np.zeros() # E: require at least one argument + +np.ones("test") # E: incompatible type +np.ones() # E: require at least one argument + +np.array(0, float, True) # E: No overload variant + +np.linspace(None, 'bob') # E: No overload variant +np.linspace(0, 2, num=10.0) # E: No overload variant +np.linspace(0, 2, endpoint='True') # E: No overload variant +np.linspace(0, 2, retstep=b'False') # E: No overload variant +np.linspace(0, 2, dtype=0) # E: No overload variant +np.linspace(0, 2, axis=None) # E: No overload variant + +np.logspace(None, 'bob') # E: Argument 1 +np.logspace(0, 2, base=None) # E: Argument "base" + +np.geomspace(None, 'bob') # E: Argument 1 + +np.stack(generator) # E: No overload variant +np.hstack({1, 2}) # E: No overload variant +np.vstack(1) # E: No overload variant diff --git a/numpy/typing/tests/data/fail/array_like.pyi b/numpy/typing/tests/data/fail/array_like.pyi new file mode 100644 index 000000000000..3bbd2906150f --- /dev/null +++ b/numpy/typing/tests/data/fail/array_like.pyi @@ -0,0 +1,16 @@ +import numpy as np +from numpy.typing import ArrayLike + + +class A: + pass + + +x1: ArrayLike = (i for i in range(10)) # E: Incompatible types in assignment +x2: ArrayLike = A() # E: Incompatible types in assignment +x3: ArrayLike = {1: "foo", 2: "bar"} # E: Incompatible types in assignment + +scalar = np.int64(1) +scalar.__array__(dtype=np.float64) # E: No overload variant +array = np.array([1]) +array.__array__(dtype=np.float64) # E: No overload variant diff --git a/numpy/typing/tests/data/fail/array_pad.pyi b/numpy/typing/tests/data/fail/array_pad.pyi new file mode 100644 index 000000000000..2be51a87181d --- /dev/null +++ b/numpy/typing/tests/data/fail/array_pad.pyi @@ -0,0 +1,6 @@ +import numpy as np +import numpy.typing as npt + +AR_i8: npt.NDArray[np.int64] + +np.pad(AR_i8, 2, mode="bob") # E: No overload variant diff --git a/numpy/typing/tests/data/fail/arrayprint.pyi b/numpy/typing/tests/data/fail/arrayprint.pyi new file mode 100644 index 000000000000..86297a0b24a4 --- /dev/null +++ b/numpy/typing/tests/data/fail/arrayprint.pyi @@ -0,0 +1,13 @@ +from typing import Callable, Any +import numpy as np + +AR: np.ndarray +func1: Callable[[Any], str] +func2: Callable[[np.integer[Any]], str] + +np.array2string(AR, style=None) # E: Unexpected keyword argument +np.array2string(AR, legacy="1.14") # E: incompatible type +np.array2string(AR, sign="*") # E: incompatible type +np.array2string(AR, floatmode="default") # E: incompatible type +np.array2string(AR, formatter={"A": func1}) # E: incompatible type +np.array2string(AR, formatter={"float": func2}) # E: Incompatible types diff --git a/numpy/typing/tests/data/fail/arrayterator.pyi b/numpy/typing/tests/data/fail/arrayterator.pyi new file mode 100644 index 000000000000..c50fb2ec4e52 --- /dev/null +++ b/numpy/typing/tests/data/fail/arrayterator.pyi @@ -0,0 +1,14 @@ +from typing import Any +import numpy as np + +AR_i8: np.ndarray[Any, np.dtype[np.int64]] +ar_iter = np.lib.Arrayterator(AR_i8) + +np.lib.Arrayterator(np.int64()) # E: incompatible type +ar_iter.shape = (10, 5) # E: is read-only +ar_iter[None] # E: Invalid index type +ar_iter[None, 1] # E: Invalid index type +ar_iter[np.intp()] # E: Invalid index type +ar_iter[np.intp(), ...] # E: Invalid index type +ar_iter[AR_i8] # E: Invalid index type +ar_iter[AR_i8, :] # E: Invalid index type diff --git a/numpy/typing/tests/data/fail/bitwise_ops.pyi b/numpy/typing/tests/data/fail/bitwise_ops.pyi new file mode 100644 index 000000000000..ee9090007924 --- /dev/null +++ b/numpy/typing/tests/data/fail/bitwise_ops.pyi @@ -0,0 +1,20 @@ +import numpy as np + +i8 = np.int64() +i4 = np.int32() +u8 = np.uint64() +b_ = np.bool_() +i = int() + +f8 = np.float64() + +b_ >> f8 # E: No overload variant +i8 << f8 # E: No overload variant +i | f8 # E: Unsupported operand types +i8 ^ f8 # E: No overload variant +u8 & f8 # E: No overload variant +~f8 # E: Unsupported operand type + +# mypys' error message for `NoReturn` is unfortunately pretty bad +# TODO: Re-enable this once we add support for numerical precision for `number`s +# a = u8 | 0 # E: Need type annotation diff --git a/numpy/typing/tests/data/fail/char.pyi b/numpy/typing/tests/data/fail/char.pyi new file mode 100644 index 000000000000..320f05df5228 --- /dev/null +++ b/numpy/typing/tests/data/fail/char.pyi @@ -0,0 +1,66 @@ +import numpy as np +import numpy.typing as npt + +AR_U: npt.NDArray[np.str_] +AR_S: npt.NDArray[np.bytes_] + +np.char.equal(AR_U, AR_S) # E: incompatible type + +np.char.not_equal(AR_U, AR_S) # E: incompatible type + +np.char.greater_equal(AR_U, AR_S) # E: incompatible type + +np.char.less_equal(AR_U, AR_S) # E: incompatible type + +np.char.greater(AR_U, AR_S) # E: incompatible type + +np.char.less(AR_U, AR_S) # E: incompatible type + +np.char.encode(AR_S) # E: incompatible type +np.char.decode(AR_U) # E: incompatible type + +np.char.join(AR_U, b"_") # E: incompatible type +np.char.join(AR_S, "_") # E: incompatible type + +np.char.ljust(AR_U, 5, fillchar=b"a") # E: incompatible type +np.char.ljust(AR_S, 5, fillchar="a") # E: incompatible type +np.char.rjust(AR_U, 5, fillchar=b"a") # E: incompatible type +np.char.rjust(AR_S, 5, fillchar="a") # E: incompatible type + +np.char.lstrip(AR_U, chars=b"a") # E: incompatible type +np.char.lstrip(AR_S, chars="a") # E: incompatible type +np.char.strip(AR_U, chars=b"a") # E: incompatible type +np.char.strip(AR_S, chars="a") # E: incompatible type +np.char.rstrip(AR_U, chars=b"a") # E: incompatible type +np.char.rstrip(AR_S, chars="a") # E: incompatible type + +np.char.partition(AR_U, b"a") # E: incompatible type +np.char.partition(AR_S, "a") # E: incompatible type +np.char.rpartition(AR_U, b"a") # E: incompatible type +np.char.rpartition(AR_S, "a") # E: incompatible type + +np.char.replace(AR_U, b"_", b"-") # E: incompatible type +np.char.replace(AR_S, "_", "-") # E: incompatible type + +np.char.split(AR_U, b"_") # E: incompatible type +np.char.split(AR_S, "_") # E: incompatible type +np.char.rsplit(AR_U, b"_") # E: incompatible type +np.char.rsplit(AR_S, "_") # E: incompatible type + +np.char.count(AR_U, b"a", start=[1, 2, 3]) # E: incompatible type +np.char.count(AR_S, "a", end=9) # E: incompatible type + +np.char.endswith(AR_U, b"a", start=[1, 2, 3]) # E: incompatible type +np.char.endswith(AR_S, "a", end=9) # E: incompatible type +np.char.startswith(AR_U, b"a", start=[1, 2, 3]) # E: incompatible type +np.char.startswith(AR_S, "a", end=9) # E: incompatible type + +np.char.find(AR_U, b"a", start=[1, 2, 3]) # E: incompatible type +np.char.find(AR_S, "a", end=9) # E: incompatible type +np.char.rfind(AR_U, b"a", start=[1, 2, 3]) # E: incompatible type +np.char.rfind(AR_S, "a", end=9) # E: incompatible type + +np.char.index(AR_U, b"a", start=[1, 2, 3]) # E: incompatible type +np.char.index(AR_S, "a", end=9) # E: incompatible type +np.char.rindex(AR_U, b"a", start=[1, 2, 3]) # E: incompatible type +np.char.rindex(AR_S, "a", end=9) # E: incompatible type diff --git a/numpy/typing/tests/data/fail/chararray.pyi b/numpy/typing/tests/data/fail/chararray.pyi new file mode 100644 index 000000000000..ebc182ec2f04 --- /dev/null +++ b/numpy/typing/tests/data/fail/chararray.pyi @@ -0,0 +1,62 @@ +import numpy as np +from typing import Any + +AR_U: np.chararray[Any, np.dtype[np.str_]] +AR_S: np.chararray[Any, np.dtype[np.bytes_]] + +AR_S.encode() # E: Invalid self argument +AR_U.decode() # E: Invalid self argument + +AR_U.join(b"_") # E: incompatible type +AR_S.join("_") # E: incompatible type + +AR_U.ljust(5, fillchar=b"a") # E: incompatible type +AR_S.ljust(5, fillchar="a") # E: incompatible type +AR_U.rjust(5, fillchar=b"a") # E: incompatible type +AR_S.rjust(5, fillchar="a") # E: incompatible type + +AR_U.lstrip(chars=b"a") # E: incompatible type +AR_S.lstrip(chars="a") # E: incompatible type +AR_U.strip(chars=b"a") # E: incompatible type +AR_S.strip(chars="a") # E: incompatible type +AR_U.rstrip(chars=b"a") # E: incompatible type +AR_S.rstrip(chars="a") # E: incompatible type + +AR_U.partition(b"a") # E: incompatible type +AR_S.partition("a") # E: incompatible type +AR_U.rpartition(b"a") # E: incompatible type +AR_S.rpartition("a") # E: incompatible type + +AR_U.replace(b"_", b"-") # E: incompatible type +AR_S.replace("_", "-") # E: incompatible type + +AR_U.split(b"_") # E: incompatible type +AR_S.split("_") # E: incompatible type +AR_S.split(1) # E: incompatible type +AR_U.rsplit(b"_") # E: incompatible type +AR_S.rsplit("_") # E: incompatible type + +AR_U.count(b"a", start=[1, 2, 3]) # E: incompatible type +AR_S.count("a", end=9) # E: incompatible type + +AR_U.endswith(b"a", start=[1, 2, 3]) # E: incompatible type +AR_S.endswith("a", end=9) # E: incompatible type +AR_U.startswith(b"a", start=[1, 2, 3]) # E: incompatible type +AR_S.startswith("a", end=9) # E: incompatible type + +AR_U.find(b"a", start=[1, 2, 3]) # E: incompatible type +AR_S.find("a", end=9) # E: incompatible type +AR_U.rfind(b"a", start=[1, 2, 3]) # E: incompatible type +AR_S.rfind("a", end=9) # E: incompatible type + +AR_U.index(b"a", start=[1, 2, 3]) # E: incompatible type +AR_S.index("a", end=9) # E: incompatible type +AR_U.rindex(b"a", start=[1, 2, 3]) # E: incompatible type +AR_S.rindex("a", end=9) # E: incompatible type + +AR_U == AR_S # E: Unsupported operand types +AR_U != AR_S # E: Unsupported operand types +AR_U >= AR_S # E: Unsupported operand types +AR_U <= AR_S # E: Unsupported operand types +AR_U > AR_S # E: Unsupported operand types +AR_U < AR_S # E: Unsupported operand types diff --git a/numpy/typing/tests/data/fail/comparisons.pyi b/numpy/typing/tests/data/fail/comparisons.pyi new file mode 100644 index 000000000000..febd0a18c891 --- /dev/null +++ b/numpy/typing/tests/data/fail/comparisons.pyi @@ -0,0 +1,27 @@ +from typing import Any +import numpy as np + +AR_i: np.ndarray[Any, np.dtype[np.int64]] +AR_f: np.ndarray[Any, np.dtype[np.float64]] +AR_c: np.ndarray[Any, np.dtype[np.complex128]] +AR_m: np.ndarray[Any, np.dtype[np.timedelta64]] +AR_M: np.ndarray[Any, np.dtype[np.datetime64]] + +AR_f > AR_m # E: Unsupported operand types +AR_c > AR_m # E: Unsupported operand types + +AR_m > AR_f # E: Unsupported operand types +AR_m > AR_c # E: Unsupported operand types + +AR_i > AR_M # E: Unsupported operand types +AR_f > AR_M # E: Unsupported operand types +AR_m > AR_M # E: Unsupported operand types + +AR_M > AR_i # E: Unsupported operand types +AR_M > AR_f # E: Unsupported operand types +AR_M > AR_m # E: Unsupported operand types + +AR_i > str() # E: No overload variant +AR_i > bytes() # E: No overload variant +str() > AR_M # E: Unsupported operand types +bytes() > AR_M # E: Unsupported operand types diff --git a/numpy/typing/tests/data/fail/constants.pyi b/numpy/typing/tests/data/fail/constants.pyi new file mode 100644 index 000000000000..324cbe9fa735 --- /dev/null +++ b/numpy/typing/tests/data/fail/constants.pyi @@ -0,0 +1,7 @@ +import numpy as np + +np.Inf = np.Inf # E: Cannot assign to final +np.ALLOW_THREADS = np.ALLOW_THREADS # E: Cannot assign to final +np.little_endian = np.little_endian # E: Cannot assign to final +np.UFUNC_PYVALS_NAME = "bob" # E: Incompatible types +np.CLIP = 2 # E: Incompatible types diff --git a/numpy/typing/tests/data/fail/datasource.pyi b/numpy/typing/tests/data/fail/datasource.pyi new file mode 100644 index 000000000000..345277d45370 --- /dev/null +++ b/numpy/typing/tests/data/fail/datasource.pyi @@ -0,0 +1,15 @@ +from pathlib import Path +import numpy as np + +path: Path +d1: np.DataSource + +d1.abspath(path) # E: incompatible type +d1.abspath(b"...") # E: incompatible type + +d1.exists(path) # E: incompatible type +d1.exists(b"...") # E: incompatible type + +d1.open(path, "r") # E: incompatible type +d1.open(b"...", encoding="utf8") # E: incompatible type +d1.open(None, newline="/n") # E: incompatible type diff --git a/numpy/typing/tests/data/fail/dtype.pyi b/numpy/typing/tests/data/fail/dtype.pyi new file mode 100644 index 000000000000..0f3810f3c014 --- /dev/null +++ b/numpy/typing/tests/data/fail/dtype.pyi @@ -0,0 +1,20 @@ +import numpy as np + + +class Test1: + not_dtype = np.dtype(float) + + +class Test2: + dtype = float + + +np.dtype(Test1()) # E: No overload variant of "dtype" matches +np.dtype(Test2()) # E: incompatible type + +np.dtype( # E: No overload variant of "dtype" matches + { + "field1": (float, 1), + "field2": (int, 3), + } +) diff --git a/numpy/typing/tests/data/fail/einsumfunc.pyi b/numpy/typing/tests/data/fail/einsumfunc.pyi new file mode 100644 index 000000000000..33722f861199 --- /dev/null +++ b/numpy/typing/tests/data/fail/einsumfunc.pyi @@ -0,0 +1,15 @@ +from typing import List, Any +import numpy as np + +AR_i: np.ndarray[Any, np.dtype[np.int64]] +AR_f: np.ndarray[Any, np.dtype[np.float64]] +AR_m: np.ndarray[Any, np.dtype[np.timedelta64]] +AR_O: np.ndarray[Any, np.dtype[np.object_]] +AR_U: np.ndarray[Any, np.dtype[np.str_]] + +np.einsum("i,i->i", AR_i, AR_m) # E: incompatible type +np.einsum("i,i->i", AR_O, AR_O) # E: incompatible type +np.einsum("i,i->i", AR_f, AR_f, dtype=np.int32) # E: incompatible type +np.einsum("i,i->i", AR_i, AR_i, dtype=np.timedelta64, casting="unsafe") # E: No overload variant +np.einsum("i,i->i", AR_i, AR_i, out=AR_U) # E: Value of type variable "_ArrayType" of "einsum" cannot be +np.einsum("i,i->i", AR_i, AR_i, out=AR_U, casting="unsafe") # E: No overload variant diff --git a/numpy/typing/tests/data/fail/flatiter.pyi b/numpy/typing/tests/data/fail/flatiter.pyi new file mode 100644 index 000000000000..544ffbe4a7db --- /dev/null +++ b/numpy/typing/tests/data/fail/flatiter.pyi @@ -0,0 +1,25 @@ +from typing import Any + +import numpy as np +from numpy.typing import _SupportsArray + + +class Index: + def __index__(self) -> int: + ... + + +a: "np.flatiter[np.ndarray]" +supports_array: _SupportsArray + +a.base = Any # E: Property "base" defined in "flatiter" is read-only +a.coords = Any # E: Property "coords" defined in "flatiter" is read-only +a.index = Any # E: Property "index" defined in "flatiter" is read-only +a.copy(order='C') # E: Unexpected keyword argument + +# NOTE: Contrary to `ndarray.__getitem__` its counterpart in `flatiter` +# does not accept objects with the `__array__` or `__index__` protocols; +# boolean indexing is just plain broken (gh-17175) +a[np.bool_()] # E: No overload variant of "__getitem__" +a[Index()] # E: No overload variant of "__getitem__" +a[supports_array] # E: No overload variant of "__getitem__" diff --git a/numpy/typing/tests/data/fail/fromnumeric.pyi b/numpy/typing/tests/data/fail/fromnumeric.pyi new file mode 100644 index 000000000000..8fafed1b7705 --- /dev/null +++ b/numpy/typing/tests/data/fail/fromnumeric.pyi @@ -0,0 +1,154 @@ +"""Tests for :mod:`numpy.core.fromnumeric`.""" + +import numpy as np + +A = np.array(True, ndmin=2, dtype=bool) +A.setflags(write=False) + +a = np.bool_(True) + +np.take(a, None) # E: incompatible type +np.take(a, axis=1.0) # E: incompatible type +np.take(A, out=1) # E: incompatible type +np.take(A, mode="bob") # E: incompatible type + +np.reshape(a, None) # E: Argument 2 to "reshape" has incompatible type +np.reshape(A, 1, order="bob") # E: Argument "order" to "reshape" has incompatible type + +np.choose(a, None) # E: incompatible type +np.choose(a, out=1.0) # E: incompatible type +np.choose(A, mode="bob") # E: incompatible type + +np.repeat(a, None) # E: Argument 2 to "repeat" has incompatible type +np.repeat(A, 1, axis=1.0) # E: Argument "axis" to "repeat" has incompatible type + +np.swapaxes(A, None, 1) # E: Argument 2 to "swapaxes" has incompatible type +np.swapaxes(A, 1, [0]) # E: Argument 3 to "swapaxes" has incompatible type + +np.transpose(A, axes=1.0) # E: Argument "axes" to "transpose" has incompatible type + +np.partition(a, None) # E: Argument 2 to "partition" has incompatible type +np.partition( + a, 0, axis="bob" # E: Argument "axis" to "partition" has incompatible type +) +np.partition( + A, 0, kind="bob" # E: Argument "kind" to "partition" has incompatible type +) +np.partition( + A, 0, order=range(5) # E: Argument "order" to "partition" has incompatible type +) + +np.argpartition( + a, None # E: incompatible type +) +np.argpartition( + a, 0, axis="bob" # E: incompatible type +) +np.argpartition( + A, 0, kind="bob" # E: incompatible type +) +np.argpartition( + A, 0, order=range(5) # E: Argument "order" to "argpartition" has incompatible type +) + +np.sort(A, axis="bob") # E: Argument "axis" to "sort" has incompatible type +np.sort(A, kind="bob") # E: Argument "kind" to "sort" has incompatible type +np.sort(A, order=range(5)) # E: Argument "order" to "sort" has incompatible type + +np.argsort(A, axis="bob") # E: Argument "axis" to "argsort" has incompatible type +np.argsort(A, kind="bob") # E: Argument "kind" to "argsort" has incompatible type +np.argsort(A, order=range(5)) # E: Argument "order" to "argsort" has incompatible type + +np.argmax(A, axis="bob") # E: No overload variant of "argmax" matches argument type +np.argmax(A, kind="bob") # E: No overload variant of "argmax" matches argument type + +np.argmin(A, axis="bob") # E: No overload variant of "argmin" matches argument type +np.argmin(A, kind="bob") # E: No overload variant of "argmin" matches argument type + +np.searchsorted( # E: No overload variant of "searchsorted" matches argument type + A[0], 0, side="bob" +) +np.searchsorted( # E: No overload variant of "searchsorted" matches argument type + A[0], 0, sorter=1.0 +) + +np.resize(A, 1.0) # E: Argument 2 to "resize" has incompatible type + +np.squeeze(A, 1.0) # E: No overload variant of "squeeze" matches argument type + +np.diagonal(A, offset=None) # E: Argument "offset" to "diagonal" has incompatible type +np.diagonal(A, axis1="bob") # E: Argument "axis1" to "diagonal" has incompatible type +np.diagonal(A, axis2=[]) # E: Argument "axis2" to "diagonal" has incompatible type + +np.trace(A, offset=None) # E: Argument "offset" to "trace" has incompatible type +np.trace(A, axis1="bob") # E: Argument "axis1" to "trace" has incompatible type +np.trace(A, axis2=[]) # E: Argument "axis2" to "trace" has incompatible type + +np.ravel(a, order="bob") # E: Argument "order" to "ravel" has incompatible type + +np.compress( + [True], A, axis=1.0 # E: Argument "axis" to "compress" has incompatible type +) + +np.clip(a, 1, 2, out=1) # E: No overload variant of "clip" matches argument type +np.clip(1, None, None) # E: No overload variant of "clip" matches argument type + +np.sum(a, axis=1.0) # E: incompatible type +np.sum(a, keepdims=1.0) # E: incompatible type +np.sum(a, initial=[1]) # E: incompatible type + +np.all(a, axis=1.0) # E: No overload variant +np.all(a, keepdims=1.0) # E: No overload variant +np.all(a, out=1.0) # E: No overload variant + +np.any(a, axis=1.0) # E: No overload variant +np.any(a, keepdims=1.0) # E: No overload variant +np.any(a, out=1.0) # E: No overload variant + +np.cumsum(a, axis=1.0) # E: incompatible type +np.cumsum(a, dtype=1.0) # E: incompatible type +np.cumsum(a, out=1.0) # E: incompatible type + +np.ptp(a, axis=1.0) # E: incompatible type +np.ptp(a, keepdims=1.0) # E: incompatible type +np.ptp(a, out=1.0) # E: incompatible type + +np.amax(a, axis=1.0) # E: incompatible type +np.amax(a, keepdims=1.0) # E: incompatible type +np.amax(a, out=1.0) # E: incompatible type +np.amax(a, initial=[1.0]) # E: incompatible type +np.amax(a, where=[1.0]) # E: incompatible type + +np.amin(a, axis=1.0) # E: incompatible type +np.amin(a, keepdims=1.0) # E: incompatible type +np.amin(a, out=1.0) # E: incompatible type +np.amin(a, initial=[1.0]) # E: incompatible type +np.amin(a, where=[1.0]) # E: incompatible type + +np.prod(a, axis=1.0) # E: incompatible type +np.prod(a, out=False) # E: incompatible type +np.prod(a, keepdims=1.0) # E: incompatible type +np.prod(a, initial=int) # E: incompatible type +np.prod(a, where=1.0) # E: incompatible type + +np.cumprod(a, axis=1.0) # E: Argument "axis" to "cumprod" has incompatible type +np.cumprod(a, out=False) # E: Argument "out" to "cumprod" has incompatible type + +np.size(a, axis=1.0) # E: Argument "axis" to "size" has incompatible type + +np.around(a, decimals=1.0) # E: incompatible type +np.around(a, out=type) # E: incompatible type + +np.mean(a, axis=1.0) # E: incompatible type +np.mean(a, out=False) # E: incompatible type +np.mean(a, keepdims=1.0) # E: incompatible type + +np.std(a, axis=1.0) # E: incompatible type +np.std(a, out=False) # E: incompatible type +np.std(a, ddof='test') # E: incompatible type +np.std(a, keepdims=1.0) # E: incompatible type + +np.var(a, axis=1.0) # E: incompatible type +np.var(a, out=False) # E: incompatible type +np.var(a, ddof='test') # E: incompatible type +np.var(a, keepdims=1.0) # E: incompatible type diff --git a/numpy/typing/tests/data/fail/histograms.pyi b/numpy/typing/tests/data/fail/histograms.pyi new file mode 100644 index 000000000000..ad151488dd2e --- /dev/null +++ b/numpy/typing/tests/data/fail/histograms.pyi @@ -0,0 +1,13 @@ +import numpy as np +import numpy.typing as npt + +AR_i8: npt.NDArray[np.int64] +AR_f8: npt.NDArray[np.float64] + +np.histogram_bin_edges(AR_i8, range=(0, 1, 2)) # E: incompatible type + +np.histogram(AR_i8, range=(0, 1, 2)) # E: incompatible type +np.histogram(AR_i8, normed=True) # E: incompatible type + +np.histogramdd(AR_i8, range=(0, 1)) # E: incompatible type +np.histogramdd(AR_i8, range=[(0, 1, 2)]) # E: incompatible type diff --git a/numpy/typing/tests/data/fail/index_tricks.pyi b/numpy/typing/tests/data/fail/index_tricks.pyi new file mode 100644 index 000000000000..c508bf3aeae6 --- /dev/null +++ b/numpy/typing/tests/data/fail/index_tricks.pyi @@ -0,0 +1,14 @@ +from typing import List +import numpy as np + +AR_LIKE_i: List[int] +AR_LIKE_f: List[float] + +np.unravel_index(AR_LIKE_f, (1, 2, 3)) # E: incompatible type +np.ravel_multi_index(AR_LIKE_i, (1, 2, 3), mode="bob") # E: No overload variant +np.mgrid[1] # E: Invalid index type +np.mgrid[...] # E: Invalid index type +np.ogrid[1] # E: Invalid index type +np.ogrid[...] # E: Invalid index type +np.fill_diagonal(AR_LIKE_f, 2) # E: incompatible type +np.diag_indices(1.0) # E: incompatible type diff --git a/numpy/typing/tests/data/fail/lib_function_base.pyi b/numpy/typing/tests/data/fail/lib_function_base.pyi new file mode 100644 index 000000000000..9cad2da03911 --- /dev/null +++ b/numpy/typing/tests/data/fail/lib_function_base.pyi @@ -0,0 +1,53 @@ +from typing import Any + +import numpy as np +import numpy.typing as npt + +AR_f8: npt.NDArray[np.float64] +AR_c16: npt.NDArray[np.complex128] +AR_m: npt.NDArray[np.timedelta64] +AR_M: npt.NDArray[np.datetime64] +AR_O: npt.NDArray[np.object_] + +def func(a: int) -> None: ... + +np.average(AR_m) # E: incompatible type +np.select(1, [AR_f8]) # E: incompatible type +np.angle(AR_m) # E: incompatible type +np.unwrap(AR_m) # E: incompatible type +np.unwrap(AR_c16) # E: incompatible type +np.trim_zeros(1) # E: incompatible type +np.place(1, [True], 1.5) # E: incompatible type +np.vectorize(1) # E: incompatible type +np.add_newdoc("__main__", 1.5, "docstring") # E: incompatible type +np.place(AR_f8, slice(None), 5) # E: incompatible type + +np.interp(AR_f8, AR_c16, AR_f8) # E: incompatible type +np.interp(AR_c16, AR_f8, AR_f8) # E: incompatible type +np.interp(AR_f8, AR_f8, AR_f8, period=AR_c16) # E: No overload variant +np.interp(AR_f8, AR_f8, AR_O) # E: incompatible type + +np.cov(AR_m) # E: incompatible type +np.cov(AR_O) # E: incompatible type +np.corrcoef(AR_m) # E: incompatible type +np.corrcoef(AR_O) # E: incompatible type +np.corrcoef(AR_f8, bias=True) # E: No overload variant +np.corrcoef(AR_f8, ddof=2) # E: No overload variant +np.blackman(1j) # E: incompatible type +np.bartlett(1j) # E: incompatible type +np.hanning(1j) # E: incompatible type +np.hamming(1j) # E: incompatible type +np.hamming(AR_c16) # E: incompatible type +np.kaiser(1j, 1) # E: incompatible type +np.sinc(AR_O) # E: incompatible type +np.median(AR_M) # E: incompatible type + +np.add_newdoc_ufunc(func, "docstring") # E: incompatible type +np.percentile(AR_f8, 50j) # E: No overload variant +np.percentile(AR_f8, 50, interpolation="bob") # E: No overload variant +np.quantile(AR_f8, 0.5j) # E: No overload variant +np.quantile(AR_f8, 0.5, interpolation="bob") # E: No overload variant +np.meshgrid(AR_f8, AR_f8, indexing="bob") # E: incompatible type +np.delete(AR_f8, AR_f8) # E: incompatible type +np.insert(AR_f8, AR_f8, 1.5) # E: incompatible type +np.digitize(AR_f8, 1j) # E: No overload variant diff --git a/numpy/typing/tests/data/fail/lib_polynomial.pyi b/numpy/typing/tests/data/fail/lib_polynomial.pyi new file mode 100644 index 000000000000..ca02d7bde60d --- /dev/null +++ b/numpy/typing/tests/data/fail/lib_polynomial.pyi @@ -0,0 +1,29 @@ +import numpy as np +import numpy.typing as npt + +AR_f8: npt.NDArray[np.float64] +AR_c16: npt.NDArray[np.complex128] +AR_O: npt.NDArray[np.object_] +AR_U: npt.NDArray[np.str_] + +poly_obj: np.poly1d + +np.polyint(AR_U) # E: incompatible type +np.polyint(AR_f8, m=1j) # E: No overload variant + +np.polyder(AR_U) # E: incompatible type +np.polyder(AR_f8, m=1j) # E: No overload variant + +np.polyfit(AR_O, AR_f8, 1) # E: incompatible type +np.polyfit(AR_f8, AR_f8, 1, rcond=1j) # E: No overload variant +np.polyfit(AR_f8, AR_f8, 1, w=AR_c16) # E: incompatible type +np.polyfit(AR_f8, AR_f8, 1, cov="bob") # E: No overload variant + +np.polyval(AR_f8, AR_U) # E: incompatible type +np.polyadd(AR_f8, AR_U) # E: incompatible type +np.polysub(AR_f8, AR_U) # E: incompatible type +np.polymul(AR_f8, AR_U) # E: incompatible type +np.polydiv(AR_f8, AR_U) # E: incompatible type + +5**poly_obj # E: No overload variant +hash(poly_obj) diff --git a/numpy/typing/tests/data/fail/lib_utils.pyi b/numpy/typing/tests/data/fail/lib_utils.pyi new file mode 100644 index 000000000000..e16c926aa645 --- /dev/null +++ b/numpy/typing/tests/data/fail/lib_utils.pyi @@ -0,0 +1,13 @@ +import numpy as np + +np.deprecate(1) # E: No overload variant + +np.deprecate_with_doc(1) # E: incompatible type + +np.byte_bounds(1) # E: incompatible type + +np.who(1) # E: incompatible type + +np.lookfor(None) # E: incompatible type + +np.safe_eval(None) # E: incompatible type diff --git a/numpy/typing/tests/data/fail/lib_version.pyi b/numpy/typing/tests/data/fail/lib_version.pyi new file mode 100644 index 000000000000..2758cfe40438 --- /dev/null +++ b/numpy/typing/tests/data/fail/lib_version.pyi @@ -0,0 +1,6 @@ +from numpy.lib import NumpyVersion + +version: NumpyVersion + +NumpyVersion(b"1.8.0") # E: incompatible type +version >= b"1.8.0" # E: Unsupported operand types diff --git a/numpy/typing/tests/data/fail/linalg.pyi b/numpy/typing/tests/data/fail/linalg.pyi new file mode 100644 index 000000000000..da9390328bd7 --- /dev/null +++ b/numpy/typing/tests/data/fail/linalg.pyi @@ -0,0 +1,48 @@ +import numpy as np +import numpy.typing as npt + +AR_f8: npt.NDArray[np.float64] +AR_O: npt.NDArray[np.object_] +AR_M: npt.NDArray[np.datetime64] + +np.linalg.tensorsolve(AR_O, AR_O) # E: incompatible type + +np.linalg.solve(AR_O, AR_O) # E: incompatible type + +np.linalg.tensorinv(AR_O) # E: incompatible type + +np.linalg.inv(AR_O) # E: incompatible type + +np.linalg.matrix_power(AR_M, 5) # E: incompatible type + +np.linalg.cholesky(AR_O) # E: incompatible type + +np.linalg.qr(AR_O) # E: incompatible type +np.linalg.qr(AR_f8, mode="bob") # E: No overload variant + +np.linalg.eigvals(AR_O) # E: incompatible type + +np.linalg.eigvalsh(AR_O) # E: incompatible type +np.linalg.eigvalsh(AR_O, UPLO="bob") # E: No overload variant + +np.linalg.eig(AR_O) # E: incompatible type + +np.linalg.eigh(AR_O) # E: incompatible type +np.linalg.eigh(AR_O, UPLO="bob") # E: No overload variant + +np.linalg.svd(AR_O) # E: incompatible type + +np.linalg.cond(AR_O) # E: incompatible type +np.linalg.cond(AR_f8, p="bob") # E: incompatible type + +np.linalg.matrix_rank(AR_O) # E: incompatible type + +np.linalg.pinv(AR_O) # E: incompatible type + +np.linalg.slogdet(AR_O) # E: incompatible type + +np.linalg.det(AR_O) # E: incompatible type + +np.linalg.norm(AR_f8, ord="bob") # E: No overload variant + +np.linalg.multi_dot([AR_M]) # E: incompatible type diff --git a/numpy/typing/tests/data/fail/memmap.pyi b/numpy/typing/tests/data/fail/memmap.pyi new file mode 100644 index 000000000000..434870b60e41 --- /dev/null +++ b/numpy/typing/tests/data/fail/memmap.pyi @@ -0,0 +1,5 @@ +import numpy as np + +with open("file.txt", "r") as f: + np.memmap(f) # E: No overload variant +np.memmap("test.txt", shape=[10, 5]) # E: No overload variant diff --git a/numpy/typing/tests/data/fail/modules.pyi b/numpy/typing/tests/data/fail/modules.pyi new file mode 100644 index 000000000000..59e724f22b48 --- /dev/null +++ b/numpy/typing/tests/data/fail/modules.pyi @@ -0,0 +1,18 @@ +import numpy as np + +np.testing.bob # E: Module has no attribute +np.bob # E: Module has no attribute + +# Stdlib modules in the namespace by accident +np.warnings # E: Module has no attribute +np.sys # E: Module has no attribute +np.os # E: Module has no attribute +np.math # E: Module has no attribute + +# Public sub-modules that are not imported to their parent module by default; +# e.g. one must first execute `import numpy.lib.recfunctions` +np.lib.recfunctions # E: Module has no attribute + +np.__NUMPY_SETUP__ # E: Module has no attribute +np.__deprecated_attrs__ # E: Module has no attribute +np.__expired_functions__ # E: Module has no attribute diff --git a/numpy/typing/tests/data/fail/multiarray.pyi b/numpy/typing/tests/data/fail/multiarray.pyi new file mode 100644 index 000000000000..22bcf8c92c90 --- /dev/null +++ b/numpy/typing/tests/data/fail/multiarray.pyi @@ -0,0 +1,56 @@ +from typing import List +import numpy as np +import numpy.typing as npt + +i8: np.int64 + +AR_b: npt.NDArray[np.bool_] +AR_u1: npt.NDArray[np.uint8] +AR_i8: npt.NDArray[np.int64] +AR_f8: npt.NDArray[np.float64] +AR_M: npt.NDArray[np.datetime64] + +M: np.datetime64 + +AR_LIKE_f: List[float] + +def func(a: int) -> None: ... + +np.where(AR_b, 1) # E: No overload variant + +np.can_cast(AR_f8, 1) # E: incompatible type + +np.vdot(AR_M, AR_M) # E: incompatible type + +np.copyto(AR_LIKE_f, AR_f8) # E: incompatible type + +np.putmask(AR_LIKE_f, [True, True, False], 1.5) # E: incompatible type + +np.packbits(AR_f8) # E: incompatible type +np.packbits(AR_u1, bitorder=">") # E: incompatible type + +np.unpackbits(AR_i8) # E: incompatible type +np.unpackbits(AR_u1, bitorder=">") # E: incompatible type + +np.shares_memory(1, 1, max_work=i8) # E: incompatible type +np.may_share_memory(1, 1, max_work=i8) # E: incompatible type + +np.arange(M) # E: No overload variant +np.arange(stop=10) # E: No overload variant + +np.datetime_data(int) # E: incompatible type + +np.busday_offset("2012", 10) # E: incompatible type + +np.datetime_as_string("2012") # E: No overload variant + +np.compare_chararrays("a", b"a", "==", False) # E: No overload variant + +np.add_docstring(func, None) # E: incompatible type + +np.nested_iters([AR_i8, AR_i8]) # E: Missing positional argument +np.nested_iters([AR_i8, AR_i8], 0) # E: incompatible type +np.nested_iters([AR_i8, AR_i8], [0]) # E: incompatible type +np.nested_iters([AR_i8, AR_i8], [[0], [1]], flags=["test"]) # E: incompatible type +np.nested_iters([AR_i8, AR_i8], [[0], [1]], op_flags=[["test"]]) # E: incompatible type +np.nested_iters([AR_i8, AR_i8], [[0], [1]], buffersize=1.0) # E: incompatible type diff --git a/numpy/typing/tests/data/fail/ndarray.pyi b/numpy/typing/tests/data/fail/ndarray.pyi new file mode 100644 index 000000000000..5a5130d40649 --- /dev/null +++ b/numpy/typing/tests/data/fail/ndarray.pyi @@ -0,0 +1,11 @@ +import numpy as np + +# Ban setting dtype since mutating the type of the array in place +# makes having ndarray be generic over dtype impossible. Generally +# users should use `ndarray.view` in this situation anyway. See +# +# https://github.com/numpy/numpy-stubs/issues/7 +# +# for more context. +float_array = np.array([1.0]) +float_array.dtype = np.bool_ # E: Property "dtype" defined in "ndarray" is read-only diff --git a/numpy/typing/tests/data/fail/ndarray_misc.pyi b/numpy/typing/tests/data/fail/ndarray_misc.pyi new file mode 100644 index 000000000000..8320a44f3caa --- /dev/null +++ b/numpy/typing/tests/data/fail/ndarray_misc.pyi @@ -0,0 +1,41 @@ +""" +Tests for miscellaneous (non-magic) ``np.ndarray``/``np.generic`` methods. + +More extensive tests are performed for the methods' +function-based counterpart in `../from_numeric.py`. + +""" + +from typing import Any +import numpy as np + +f8: np.float64 +AR_f8: np.ndarray[Any, np.dtype[np.float64]] +AR_M: np.ndarray[Any, np.dtype[np.datetime64]] +AR_b: np.ndarray[Any, np.dtype[np.bool_]] + +ctypes_obj = AR_f8.ctypes + +reveal_type(ctypes_obj.get_data()) # E: has no attribute +reveal_type(ctypes_obj.get_shape()) # E: has no attribute +reveal_type(ctypes_obj.get_strides()) # E: has no attribute +reveal_type(ctypes_obj.get_as_parameter()) # E: has no attribute + +f8.argpartition(0) # E: has no attribute +f8.diagonal() # E: has no attribute +f8.dot(1) # E: has no attribute +f8.nonzero() # E: has no attribute +f8.partition(0) # E: has no attribute +f8.put(0, 2) # E: has no attribute +f8.setfield(2, np.float64) # E: has no attribute +f8.sort() # E: has no attribute +f8.trace() # E: has no attribute + +AR_M.__int__() # E: Invalid self argument +AR_M.__float__() # E: Invalid self argument +AR_M.__complex__() # E: Invalid self argument +AR_b.__index__() # E: Invalid self argument + +AR_f8[1.5] # E: No overload variant +AR_f8["field_a"] # E: No overload variant +AR_f8[["field_a", "field_b"]] # E: Invalid index type diff --git a/numpy/typing/tests/data/fail/nditer.pyi b/numpy/typing/tests/data/fail/nditer.pyi new file mode 100644 index 000000000000..1e8e37ee5fe0 --- /dev/null +++ b/numpy/typing/tests/data/fail/nditer.pyi @@ -0,0 +1,8 @@ +import numpy as np + +class Test(np.nditer): ... # E: Cannot inherit from final class + +np.nditer([0, 1], flags=["test"]) # E: incompatible type +np.nditer([0, 1], op_flags=[["test"]]) # E: incompatible type +np.nditer([0, 1], itershape=(1.0,)) # E: incompatible type +np.nditer([0, 1], buffersize=1.0) # E: incompatible type diff --git a/numpy/typing/tests/data/fail/nested_sequence.pyi b/numpy/typing/tests/data/fail/nested_sequence.pyi new file mode 100644 index 000000000000..e28661a058e9 --- /dev/null +++ b/numpy/typing/tests/data/fail/nested_sequence.pyi @@ -0,0 +1,17 @@ +from typing import Sequence, Tuple, List +import numpy.typing as npt + +a: Sequence[float] +b: List[complex] +c: Tuple[str, ...] +d: int +e: str + +def func(a: npt._NestedSequence[int]) -> None: + ... + +reveal_type(func(a)) # E: incompatible type +reveal_type(func(b)) # E: incompatible type +reveal_type(func(c)) # E: incompatible type +reveal_type(func(d)) # E: incompatible type +reveal_type(func(e)) # E: incompatible type diff --git a/numpy/typing/tests/data/fail/npyio.pyi b/numpy/typing/tests/data/fail/npyio.pyi new file mode 100644 index 000000000000..c91b4c9cb846 --- /dev/null +++ b/numpy/typing/tests/data/fail/npyio.pyi @@ -0,0 +1,30 @@ +import pathlib +from typing import IO + +import numpy.typing as npt +import numpy as np + +str_path: str +bytes_path: bytes +pathlib_path: pathlib.Path +str_file: IO[str] +AR_i8: npt.NDArray[np.int64] + +np.load(str_file) # E: incompatible type + +np.save(bytes_path, AR_i8) # E: incompatible type +np.save(str_file, AR_i8) # E: incompatible type + +np.savez(bytes_path, AR_i8) # E: incompatible type +np.savez(str_file, AR_i8) # E: incompatible type + +np.savez_compressed(bytes_path, AR_i8) # E: incompatible type +np.savez_compressed(str_file, AR_i8) # E: incompatible type + +np.loadtxt(bytes_path) # E: incompatible type + +np.fromregex(bytes_path, ".", np.int64) # E: No overload variant + +np.recfromtxt(bytes_path) # E: incompatible type + +np.recfromcsv(bytes_path) # E: incompatible type diff --git a/numpy/typing/tests/data/fail/numerictypes.pyi b/numpy/typing/tests/data/fail/numerictypes.pyi new file mode 100644 index 000000000000..a5c2814ef119 --- /dev/null +++ b/numpy/typing/tests/data/fail/numerictypes.pyi @@ -0,0 +1,13 @@ +import numpy as np + +# Technically this works, but probably shouldn't. See +# +# https://github.com/numpy/numpy/issues/16366 +# +np.maximum_sctype(1) # E: No overload variant + +np.issubsctype(1, np.int64) # E: incompatible type + +np.issubdtype(1, np.int64) # E: incompatible type + +np.find_common_type(np.int64, np.int64) # E: incompatible type diff --git a/numpy/typing/tests/data/fail/random.pyi b/numpy/typing/tests/data/fail/random.pyi new file mode 100644 index 000000000000..c4d1e3e3e802 --- /dev/null +++ b/numpy/typing/tests/data/fail/random.pyi @@ -0,0 +1,61 @@ +import numpy as np +from typing import Any, List + +SEED_FLOAT: float = 457.3 +SEED_ARR_FLOAT: np.ndarray[Any, np.dtype[np.float64]] = np.array([1.0, 2, 3, 4]) +SEED_ARRLIKE_FLOAT: List[float] = [1.0, 2.0, 3.0, 4.0] +SEED_SEED_SEQ: np.random.SeedSequence = np.random.SeedSequence(0) +SEED_STR: str = "String seeding not allowed" +# default rng +np.random.default_rng(SEED_FLOAT) # E: incompatible type +np.random.default_rng(SEED_ARR_FLOAT) # E: incompatible type +np.random.default_rng(SEED_ARRLIKE_FLOAT) # E: incompatible type +np.random.default_rng(SEED_STR) # E: incompatible type + +# Seed Sequence +np.random.SeedSequence(SEED_FLOAT) # E: incompatible type +np.random.SeedSequence(SEED_ARR_FLOAT) # E: incompatible type +np.random.SeedSequence(SEED_ARRLIKE_FLOAT) # E: incompatible type +np.random.SeedSequence(SEED_SEED_SEQ) # E: incompatible type +np.random.SeedSequence(SEED_STR) # E: incompatible type + +seed_seq: np.random.bit_generator.SeedSequence = np.random.SeedSequence() +seed_seq.spawn(11.5) # E: incompatible type +seed_seq.generate_state(3.14) # E: incompatible type +seed_seq.generate_state(3, np.uint8) # E: incompatible type +seed_seq.generate_state(3, "uint8") # E: incompatible type +seed_seq.generate_state(3, "u1") # E: incompatible type +seed_seq.generate_state(3, np.uint16) # E: incompatible type +seed_seq.generate_state(3, "uint16") # E: incompatible type +seed_seq.generate_state(3, "u2") # E: incompatible type +seed_seq.generate_state(3, np.int32) # E: incompatible type +seed_seq.generate_state(3, "int32") # E: incompatible type +seed_seq.generate_state(3, "i4") # E: incompatible type + +# Bit Generators +np.random.MT19937(SEED_FLOAT) # E: incompatible type +np.random.MT19937(SEED_ARR_FLOAT) # E: incompatible type +np.random.MT19937(SEED_ARRLIKE_FLOAT) # E: incompatible type +np.random.MT19937(SEED_STR) # E: incompatible type + +np.random.PCG64(SEED_FLOAT) # E: incompatible type +np.random.PCG64(SEED_ARR_FLOAT) # E: incompatible type +np.random.PCG64(SEED_ARRLIKE_FLOAT) # E: incompatible type +np.random.PCG64(SEED_STR) # E: incompatible type + +np.random.Philox(SEED_FLOAT) # E: incompatible type +np.random.Philox(SEED_ARR_FLOAT) # E: incompatible type +np.random.Philox(SEED_ARRLIKE_FLOAT) # E: incompatible type +np.random.Philox(SEED_STR) # E: incompatible type + +np.random.SFC64(SEED_FLOAT) # E: incompatible type +np.random.SFC64(SEED_ARR_FLOAT) # E: incompatible type +np.random.SFC64(SEED_ARRLIKE_FLOAT) # E: incompatible type +np.random.SFC64(SEED_STR) # E: incompatible type + +# Generator +np.random.Generator(None) # E: incompatible type +np.random.Generator(12333283902830213) # E: incompatible type +np.random.Generator("OxFEEDF00D") # E: incompatible type +np.random.Generator([123, 234]) # E: incompatible type +np.random.Generator(np.array([123, 234], dtype="u4")) # E: incompatible type diff --git a/numpy/typing/tests/data/fail/rec.pyi b/numpy/typing/tests/data/fail/rec.pyi new file mode 100644 index 000000000000..a57f1ba27d74 --- /dev/null +++ b/numpy/typing/tests/data/fail/rec.pyi @@ -0,0 +1,17 @@ +import numpy as np +import numpy.typing as npt + +AR_i8: npt.NDArray[np.int64] + +np.rec.fromarrays(1) # E: No overload variant +np.rec.fromarrays([1, 2, 3], dtype=[("f8", "f8")], formats=["f8", "f8"]) # E: No overload variant + +np.rec.fromrecords(AR_i8) # E: incompatible type +np.rec.fromrecords([(1.5,)], dtype=[("f8", "f8")], formats=["f8", "f8"]) # E: No overload variant + +np.rec.fromstring("string", dtype=[("f8", "f8")]) # E: No overload variant +np.rec.fromstring(b"bytes") # E: No overload variant +np.rec.fromstring(b"(1.5,)", dtype=[("f8", "f8")], formats=["f8", "f8"]) # E: No overload variant + +with open("test", "r") as f: + np.rec.fromfile(f, dtype=[("f8", "f8")]) # E: No overload variant diff --git a/numpy/typing/tests/data/fail/scalars.pyi b/numpy/typing/tests/data/fail/scalars.pyi new file mode 100644 index 000000000000..96447053888e --- /dev/null +++ b/numpy/typing/tests/data/fail/scalars.pyi @@ -0,0 +1,93 @@ +import sys +import numpy as np + +f2: np.float16 +f8: np.float64 +c8: np.complex64 + +# Construction + +np.float32(3j) # E: incompatible type + +# Technically the following examples are valid NumPy code. But they +# are not considered a best practice, and people who wish to use the +# stubs should instead do +# +# np.array([1.0, 0.0, 0.0], dtype=np.float32) +# np.array([], dtype=np.complex64) +# +# See e.g. the discussion on the mailing list +# +# https://mail.python.org/pipermail/numpy-discussion/2020-April/080566.html +# +# and the issue +# +# https://github.com/numpy/numpy-stubs/issues/41 +# +# for more context. +np.float32([1.0, 0.0, 0.0]) # E: incompatible type +np.complex64([]) # E: incompatible type + +np.complex64(1, 2) # E: Too many arguments +# TODO: protocols (can't check for non-existent protocols w/ __getattr__) + +np.datetime64(0) # E: No overload variant + +class A: + def __float__(self): + return 1.0 + + +np.int8(A()) # E: incompatible type +np.int16(A()) # E: incompatible type +np.int32(A()) # E: incompatible type +np.int64(A()) # E: incompatible type +np.uint8(A()) # E: incompatible type +np.uint16(A()) # E: incompatible type +np.uint32(A()) # E: incompatible type +np.uint64(A()) # E: incompatible type + +np.void("test") # E: incompatible type + +np.generic(1) # E: Cannot instantiate abstract class +np.number(1) # E: Cannot instantiate abstract class +np.integer(1) # E: Cannot instantiate abstract class +np.inexact(1) # E: Cannot instantiate abstract class +np.character("test") # E: Cannot instantiate abstract class +np.flexible(b"test") # E: Cannot instantiate abstract class + +np.float64(value=0.0) # E: Unexpected keyword argument +np.int64(value=0) # E: Unexpected keyword argument +np.uint64(value=0) # E: Unexpected keyword argument +np.complex128(value=0.0j) # E: Unexpected keyword argument +np.str_(value='bob') # E: No overload variant +np.bytes_(value=b'test') # E: No overload variant +np.void(value=b'test') # E: Unexpected keyword argument +np.bool_(value=True) # E: Unexpected keyword argument +np.datetime64(value="2019") # E: No overload variant +np.timedelta64(value=0) # E: Unexpected keyword argument + +np.bytes_(b"hello", encoding='utf-8') # E: No overload variant +np.str_("hello", encoding='utf-8') # E: No overload variant + +complex(np.bytes_("1")) # E: No overload variant + +f8.item(1) # E: incompatible type +f8.item((0, 1)) # E: incompatible type +f8.squeeze(axis=1) # E: incompatible type +f8.squeeze(axis=(0, 1)) # E: incompatible type +f8.transpose(1) # E: incompatible type + +def func(a: np.float32) -> None: ... + +func(f2) # E: incompatible type +func(f8) # E: incompatible type + +round(c8) # E: No overload variant + +c8.__getnewargs__() # E: Invalid self argument +f2.__getnewargs__() # E: Invalid self argument +f2.hex() # E: Invalid self argument +np.float16.fromhex("0x0.0p+0") # E: Invalid self argument +f2.__trunc__() # E: Invalid self argument +f2.__getformat__("float") # E: Invalid self argument diff --git a/numpy/typing/tests/data/fail/shape_base.pyi b/numpy/typing/tests/data/fail/shape_base.pyi new file mode 100644 index 000000000000..e709741b7935 --- /dev/null +++ b/numpy/typing/tests/data/fail/shape_base.pyi @@ -0,0 +1,8 @@ +import numpy as np + +class DTypeLike: + dtype: np.dtype[np.int_] + +dtype_like: DTypeLike + +np.expand_dims(dtype_like, (5, 10)) # E: No overload variant diff --git a/numpy/typing/tests/data/fail/stride_tricks.pyi b/numpy/typing/tests/data/fail/stride_tricks.pyi new file mode 100644 index 000000000000..f2bfba7432a8 --- /dev/null +++ b/numpy/typing/tests/data/fail/stride_tricks.pyi @@ -0,0 +1,9 @@ +import numpy as np +import numpy.typing as npt + +AR_f8: npt.NDArray[np.float64] + +np.lib.stride_tricks.as_strided(AR_f8, shape=8) # E: No overload variant +np.lib.stride_tricks.as_strided(AR_f8, strides=8) # E: No overload variant + +np.lib.stride_tricks.sliding_window_view(AR_f8, axis=(1,)) # E: No overload variant diff --git a/numpy/typing/tests/data/fail/testing.pyi b/numpy/typing/tests/data/fail/testing.pyi new file mode 100644 index 000000000000..e753a9810ab3 --- /dev/null +++ b/numpy/typing/tests/data/fail/testing.pyi @@ -0,0 +1,26 @@ +import numpy as np +import numpy.typing as npt + +AR_U: npt.NDArray[np.str_] + +def func() -> bool: ... + +np.testing.assert_(True, msg=1) # E: incompatible type +np.testing.build_err_msg(1, "test") # E: incompatible type +np.testing.assert_almost_equal(AR_U, AR_U) # E: incompatible type +np.testing.assert_approx_equal([1, 2, 3], [1, 2, 3]) # E: incompatible type +np.testing.assert_array_almost_equal(AR_U, AR_U) # E: incompatible type +np.testing.assert_array_less(AR_U, AR_U) # E: incompatible type +np.testing.assert_string_equal(b"a", b"a") # E: incompatible type + +np.testing.assert_raises(expected_exception=TypeError, callable=func) # E: No overload variant +np.testing.assert_raises_regex(expected_exception=TypeError, expected_regex="T", callable=func) # E: No overload variant + +np.testing.assert_allclose(AR_U, AR_U) # E: incompatible type +np.testing.assert_array_almost_equal_nulp(AR_U, AR_U) # E: incompatible type +np.testing.assert_array_max_ulp(AR_U, AR_U) # E: incompatible type + +np.testing.assert_warns(warning_class=RuntimeWarning, func=func) # E: No overload variant +np.testing.assert_no_warnings(func=func) # E: No overload variant + +np.testing.assert_no_gc_cycles(func=func) # E: No overload variant diff --git a/numpy/typing/tests/data/fail/twodim_base.pyi b/numpy/typing/tests/data/fail/twodim_base.pyi new file mode 100644 index 000000000000..ab34a374ccf5 --- /dev/null +++ b/numpy/typing/tests/data/fail/twodim_base.pyi @@ -0,0 +1,37 @@ +from typing import Any, List, TypeVar + +import numpy as np +import numpy.typing as npt + + +def func1(ar: npt.NDArray[Any], a: int) -> npt.NDArray[np.str_]: + pass + + +def func2(ar: npt.NDArray[Any], a: float) -> float: + pass + + +AR_b: npt.NDArray[np.bool_] +AR_m: npt.NDArray[np.timedelta64] + +AR_LIKE_b: List[bool] + +np.eye(10, M=20.0) # E: No overload variant +np.eye(10, k=2.5, dtype=int) # E: No overload variant + +np.diag(AR_b, k=0.5) # E: No overload variant +np.diagflat(AR_b, k=0.5) # E: No overload variant + +np.tri(10, M=20.0) # E: No overload variant +np.tri(10, k=2.5, dtype=int) # E: No overload variant + +np.tril(AR_b, k=0.5) # E: No overload variant +np.triu(AR_b, k=0.5) # E: No overload variant + +np.vander(AR_m) # E: incompatible type + +np.histogram2d(AR_m) # E: No overload variant + +np.mask_indices(10, func1) # E: incompatible type +np.mask_indices(10, func2, 10.5) # E: incompatible type diff --git a/numpy/typing/tests/data/fail/type_check.pyi b/numpy/typing/tests/data/fail/type_check.pyi new file mode 100644 index 000000000000..95f52bfbd260 --- /dev/null +++ b/numpy/typing/tests/data/fail/type_check.pyi @@ -0,0 +1,13 @@ +import numpy as np +import numpy.typing as npt + +DTYPE_i8: np.dtype[np.int64] + +np.mintypecode(DTYPE_i8) # E: incompatible type +np.iscomplexobj(DTYPE_i8) # E: incompatible type +np.isrealobj(DTYPE_i8) # E: incompatible type + +np.typename(DTYPE_i8) # E: No overload variant +np.typename("invalid") # E: No overload variant + +np.common_type(np.timedelta64()) # E: incompatible type diff --git a/numpy/typing/tests/data/fail/ufunc_config.pyi b/numpy/typing/tests/data/fail/ufunc_config.pyi new file mode 100644 index 000000000000..f547fbb46b85 --- /dev/null +++ b/numpy/typing/tests/data/fail/ufunc_config.pyi @@ -0,0 +1,21 @@ +"""Typing tests for `numpy.core._ufunc_config`.""" + +import numpy as np + +def func1(a: str, b: int, c: float) -> None: ... +def func2(a: str, *, b: int) -> None: ... + +class Write1: + def write1(self, a: str) -> None: ... + +class Write2: + def write(self, a: str, b: str) -> None: ... + +class Write3: + def write(self, *, a: str) -> None: ... + +np.seterrcall(func1) # E: Argument 1 to "seterrcall" has incompatible type +np.seterrcall(func2) # E: Argument 1 to "seterrcall" has incompatible type +np.seterrcall(Write1()) # E: Argument 1 to "seterrcall" has incompatible type +np.seterrcall(Write2()) # E: Argument 1 to "seterrcall" has incompatible type +np.seterrcall(Write3()) # E: Argument 1 to "seterrcall" has incompatible type diff --git a/numpy/typing/tests/data/fail/ufunclike.pyi b/numpy/typing/tests/data/fail/ufunclike.pyi new file mode 100644 index 000000000000..82a5f3a1d091 --- /dev/null +++ b/numpy/typing/tests/data/fail/ufunclike.pyi @@ -0,0 +1,21 @@ +from typing import List, Any +import numpy as np + +AR_c: np.ndarray[Any, np.dtype[np.complex128]] +AR_m: np.ndarray[Any, np.dtype[np.timedelta64]] +AR_M: np.ndarray[Any, np.dtype[np.datetime64]] +AR_O: np.ndarray[Any, np.dtype[np.object_]] + +np.fix(AR_c) # E: incompatible type +np.fix(AR_m) # E: incompatible type +np.fix(AR_M) # E: incompatible type + +np.isposinf(AR_c) # E: incompatible type +np.isposinf(AR_m) # E: incompatible type +np.isposinf(AR_M) # E: incompatible type +np.isposinf(AR_O) # E: incompatible type + +np.isneginf(AR_c) # E: incompatible type +np.isneginf(AR_m) # E: incompatible type +np.isneginf(AR_M) # E: incompatible type +np.isneginf(AR_O) # E: incompatible type diff --git a/numpy/typing/tests/data/fail/ufuncs.pyi b/numpy/typing/tests/data/fail/ufuncs.pyi new file mode 100644 index 000000000000..e827267c6072 --- /dev/null +++ b/numpy/typing/tests/data/fail/ufuncs.pyi @@ -0,0 +1,41 @@ +import numpy as np +import numpy.typing as npt + +AR_f8: npt.NDArray[np.float64] + +np.sin.nin + "foo" # E: Unsupported operand types +np.sin(1, foo="bar") # E: No overload variant + +np.abs(None) # E: No overload variant + +np.add(1, 1, 1) # E: No overload variant +np.add(1, 1, axis=0) # E: No overload variant + +np.matmul(AR_f8, AR_f8, where=True) # E: No overload variant + +np.frexp(AR_f8, out=None) # E: No overload variant +np.frexp(AR_f8, out=AR_f8) # E: No overload variant + +np.absolute.outer() # E: "None" not callable +np.frexp.outer() # E: "None" not callable +np.divmod.outer() # E: "None" not callable +np.matmul.outer() # E: "None" not callable + +np.absolute.reduceat() # E: "None" not callable +np.frexp.reduceat() # E: "None" not callable +np.divmod.reduceat() # E: "None" not callable +np.matmul.reduceat() # E: "None" not callable + +np.absolute.reduce() # E: "None" not callable +np.frexp.reduce() # E: "None" not callable +np.divmod.reduce() # E: "None" not callable +np.matmul.reduce() # E: "None" not callable + +np.absolute.accumulate() # E: "None" not callable +np.frexp.accumulate() # E: "None" not callable +np.divmod.accumulate() # E: "None" not callable +np.matmul.accumulate() # E: "None" not callable + +np.frexp.at() # E: "None" not callable +np.divmod.at() # E: "None" not callable +np.matmul.at() # E: "None" not callable diff --git a/numpy/typing/tests/data/fail/warnings_and_errors.pyi b/numpy/typing/tests/data/fail/warnings_and_errors.pyi new file mode 100644 index 000000000000..f4fa38293738 --- /dev/null +++ b/numpy/typing/tests/data/fail/warnings_and_errors.pyi @@ -0,0 +1,5 @@ +import numpy as np + +np.AxisError(1.0) # E: No overload variant +np.AxisError(1, ndim=2.0) # E: No overload variant +np.AxisError(2, msg_prefix=404) # E: No overload variant diff --git a/numpy/typing/tests/data/misc/extended_precision.pyi b/numpy/typing/tests/data/misc/extended_precision.pyi new file mode 100644 index 000000000000..1e495e4f3cc4 --- /dev/null +++ b/numpy/typing/tests/data/misc/extended_precision.pyi @@ -0,0 +1,17 @@ +import numpy as np + +reveal_type(np.uint128()) +reveal_type(np.uint256()) + +reveal_type(np.int128()) +reveal_type(np.int256()) + +reveal_type(np.float80()) +reveal_type(np.float96()) +reveal_type(np.float128()) +reveal_type(np.float256()) + +reveal_type(np.complex160()) +reveal_type(np.complex192()) +reveal_type(np.complex256()) +reveal_type(np.complex512()) diff --git a/numpy/typing/tests/data/mypy.ini b/numpy/typing/tests/data/mypy.ini new file mode 100644 index 000000000000..548f762612fc --- /dev/null +++ b/numpy/typing/tests/data/mypy.ini @@ -0,0 +1,9 @@ +[mypy] +plugins = numpy.typing.mypy_plugin +show_absolute_path = True + +[mypy-numpy] +ignore_errors = True + +[mypy-numpy.*] +ignore_errors = True diff --git a/numpy/typing/tests/data/pass/arithmetic.py b/numpy/typing/tests/data/pass/arithmetic.py new file mode 100644 index 000000000000..fe1612906e01 --- /dev/null +++ b/numpy/typing/tests/data/pass/arithmetic.py @@ -0,0 +1,588 @@ +from __future__ import annotations + +from typing import Any +import numpy as np + +c16 = np.complex128(1) +f8 = np.float64(1) +i8 = np.int64(1) +u8 = np.uint64(1) + +c8 = np.complex64(1) +f4 = np.float32(1) +i4 = np.int32(1) +u4 = np.uint32(1) + +dt = np.datetime64(1, "D") +td = np.timedelta64(1, "D") + +b_ = np.bool_(1) + +b = bool(1) +c = complex(1) +f = float(1) +i = int(1) + + +class Object: + def __array__(self) -> np.ndarray[Any, np.dtype[np.object_]]: + ret = np.empty((), dtype=object) + ret[()] = self + return ret + + def __sub__(self, value: Any) -> Object: + return self + + def __rsub__(self, value: Any) -> Object: + return self + + def __floordiv__(self, value: Any) -> Object: + return self + + def __rfloordiv__(self, value: Any) -> Object: + return self + + def __mul__(self, value: Any) -> Object: + return self + + def __rmul__(self, value: Any) -> Object: + return self + + def __pow__(self, value: Any) -> Object: + return self + + def __rpow__(self, value: Any) -> Object: + return self + + +AR_b: np.ndarray[Any, np.dtype[np.bool_]] = np.array([True]) +AR_u: np.ndarray[Any, np.dtype[np.uint32]] = np.array([1], dtype=np.uint32) +AR_i: np.ndarray[Any, np.dtype[np.int64]] = np.array([1]) +AR_f: np.ndarray[Any, np.dtype[np.float64]] = np.array([1.0]) +AR_c: np.ndarray[Any, np.dtype[np.complex128]] = np.array([1j]) +AR_m: np.ndarray[Any, np.dtype[np.timedelta64]] = np.array([np.timedelta64(1, "D")]) +AR_M: np.ndarray[Any, np.dtype[np.datetime64]] = np.array([np.datetime64(1, "D")]) +AR_O: np.ndarray[Any, np.dtype[np.object_]] = np.array([Object()]) + +AR_LIKE_b = [True] +AR_LIKE_u = [np.uint32(1)] +AR_LIKE_i = [1] +AR_LIKE_f = [1.0] +AR_LIKE_c = [1j] +AR_LIKE_m = [np.timedelta64(1, "D")] +AR_LIKE_M = [np.datetime64(1, "D")] +AR_LIKE_O = [Object()] + +# Array subtractions + +AR_b - AR_LIKE_u +AR_b - AR_LIKE_i +AR_b - AR_LIKE_f +AR_b - AR_LIKE_c +AR_b - AR_LIKE_m +AR_b - AR_LIKE_O + +AR_LIKE_u - AR_b +AR_LIKE_i - AR_b +AR_LIKE_f - AR_b +AR_LIKE_c - AR_b +AR_LIKE_m - AR_b +AR_LIKE_M - AR_b +AR_LIKE_O - AR_b + +AR_u - AR_LIKE_b +AR_u - AR_LIKE_u +AR_u - AR_LIKE_i +AR_u - AR_LIKE_f +AR_u - AR_LIKE_c +AR_u - AR_LIKE_m +AR_u - AR_LIKE_O + +AR_LIKE_b - AR_u +AR_LIKE_u - AR_u +AR_LIKE_i - AR_u +AR_LIKE_f - AR_u +AR_LIKE_c - AR_u +AR_LIKE_m - AR_u +AR_LIKE_M - AR_u +AR_LIKE_O - AR_u + +AR_i - AR_LIKE_b +AR_i - AR_LIKE_u +AR_i - AR_LIKE_i +AR_i - AR_LIKE_f +AR_i - AR_LIKE_c +AR_i - AR_LIKE_m +AR_i - AR_LIKE_O + +AR_LIKE_b - AR_i +AR_LIKE_u - AR_i +AR_LIKE_i - AR_i +AR_LIKE_f - AR_i +AR_LIKE_c - AR_i +AR_LIKE_m - AR_i +AR_LIKE_M - AR_i +AR_LIKE_O - AR_i + +AR_f - AR_LIKE_b +AR_f - AR_LIKE_u +AR_f - AR_LIKE_i +AR_f - AR_LIKE_f +AR_f - AR_LIKE_c +AR_f - AR_LIKE_O + +AR_LIKE_b - AR_f +AR_LIKE_u - AR_f +AR_LIKE_i - AR_f +AR_LIKE_f - AR_f +AR_LIKE_c - AR_f +AR_LIKE_O - AR_f + +AR_c - AR_LIKE_b +AR_c - AR_LIKE_u +AR_c - AR_LIKE_i +AR_c - AR_LIKE_f +AR_c - AR_LIKE_c +AR_c - AR_LIKE_O + +AR_LIKE_b - AR_c +AR_LIKE_u - AR_c +AR_LIKE_i - AR_c +AR_LIKE_f - AR_c +AR_LIKE_c - AR_c +AR_LIKE_O - AR_c + +AR_m - AR_LIKE_b +AR_m - AR_LIKE_u +AR_m - AR_LIKE_i +AR_m - AR_LIKE_m + +AR_LIKE_b - AR_m +AR_LIKE_u - AR_m +AR_LIKE_i - AR_m +AR_LIKE_m - AR_m +AR_LIKE_M - AR_m + +AR_M - AR_LIKE_b +AR_M - AR_LIKE_u +AR_M - AR_LIKE_i +AR_M - AR_LIKE_m +AR_M - AR_LIKE_M + +AR_LIKE_M - AR_M + +AR_O - AR_LIKE_b +AR_O - AR_LIKE_u +AR_O - AR_LIKE_i +AR_O - AR_LIKE_f +AR_O - AR_LIKE_c +AR_O - AR_LIKE_O + +AR_LIKE_b - AR_O +AR_LIKE_u - AR_O +AR_LIKE_i - AR_O +AR_LIKE_f - AR_O +AR_LIKE_c - AR_O +AR_LIKE_O - AR_O + +# Array floor division + +AR_b // AR_LIKE_b +AR_b // AR_LIKE_u +AR_b // AR_LIKE_i +AR_b // AR_LIKE_f +AR_b // AR_LIKE_O + +AR_LIKE_b // AR_b +AR_LIKE_u // AR_b +AR_LIKE_i // AR_b +AR_LIKE_f // AR_b +AR_LIKE_O // AR_b + +AR_u // AR_LIKE_b +AR_u // AR_LIKE_u +AR_u // AR_LIKE_i +AR_u // AR_LIKE_f +AR_u // AR_LIKE_O + +AR_LIKE_b // AR_u +AR_LIKE_u // AR_u +AR_LIKE_i // AR_u +AR_LIKE_f // AR_u +AR_LIKE_m // AR_u +AR_LIKE_O // AR_u + +AR_i // AR_LIKE_b +AR_i // AR_LIKE_u +AR_i // AR_LIKE_i +AR_i // AR_LIKE_f +AR_i // AR_LIKE_O + +AR_LIKE_b // AR_i +AR_LIKE_u // AR_i +AR_LIKE_i // AR_i +AR_LIKE_f // AR_i +AR_LIKE_m // AR_i +AR_LIKE_O // AR_i + +AR_f // AR_LIKE_b +AR_f // AR_LIKE_u +AR_f // AR_LIKE_i +AR_f // AR_LIKE_f +AR_f // AR_LIKE_O + +AR_LIKE_b // AR_f +AR_LIKE_u // AR_f +AR_LIKE_i // AR_f +AR_LIKE_f // AR_f +AR_LIKE_m // AR_f +AR_LIKE_O // AR_f + +AR_m // AR_LIKE_u +AR_m // AR_LIKE_i +AR_m // AR_LIKE_f +AR_m // AR_LIKE_m + +AR_LIKE_m // AR_m + +AR_O // AR_LIKE_b +AR_O // AR_LIKE_u +AR_O // AR_LIKE_i +AR_O // AR_LIKE_f +AR_O // AR_LIKE_O + +AR_LIKE_b // AR_O +AR_LIKE_u // AR_O +AR_LIKE_i // AR_O +AR_LIKE_f // AR_O +AR_LIKE_O // AR_O + +# Inplace multiplication + +AR_b *= AR_LIKE_b + +AR_u *= AR_LIKE_b +AR_u *= AR_LIKE_u + +AR_i *= AR_LIKE_b +AR_i *= AR_LIKE_u +AR_i *= AR_LIKE_i + +AR_f *= AR_LIKE_b +AR_f *= AR_LIKE_u +AR_f *= AR_LIKE_i +AR_f *= AR_LIKE_f + +AR_c *= AR_LIKE_b +AR_c *= AR_LIKE_u +AR_c *= AR_LIKE_i +AR_c *= AR_LIKE_f +AR_c *= AR_LIKE_c + +AR_m *= AR_LIKE_b +AR_m *= AR_LIKE_u +AR_m *= AR_LIKE_i +AR_m *= AR_LIKE_f + +AR_O *= AR_LIKE_b +AR_O *= AR_LIKE_u +AR_O *= AR_LIKE_i +AR_O *= AR_LIKE_f +AR_O *= AR_LIKE_c +AR_O *= AR_LIKE_O + +# Inplace power + +AR_u **= AR_LIKE_b +AR_u **= AR_LIKE_u + +AR_i **= AR_LIKE_b +AR_i **= AR_LIKE_u +AR_i **= AR_LIKE_i + +AR_f **= AR_LIKE_b +AR_f **= AR_LIKE_u +AR_f **= AR_LIKE_i +AR_f **= AR_LIKE_f + +AR_c **= AR_LIKE_b +AR_c **= AR_LIKE_u +AR_c **= AR_LIKE_i +AR_c **= AR_LIKE_f +AR_c **= AR_LIKE_c + +AR_O **= AR_LIKE_b +AR_O **= AR_LIKE_u +AR_O **= AR_LIKE_i +AR_O **= AR_LIKE_f +AR_O **= AR_LIKE_c +AR_O **= AR_LIKE_O + +# unary ops + +-c16 +-c8 +-f8 +-f4 +-i8 +-i4 +-u8 +-u4 +-td +-AR_f + ++c16 ++c8 ++f8 ++f4 ++i8 ++i4 ++u8 ++u4 ++td ++AR_f + +abs(c16) +abs(c8) +abs(f8) +abs(f4) +abs(i8) +abs(i4) +abs(u8) +abs(u4) +abs(td) +abs(b_) +abs(AR_f) + +# Time structures + +dt + td +dt + i +dt + i4 +dt + i8 +dt - dt +dt - i +dt - i4 +dt - i8 + +td + td +td + i +td + i4 +td + i8 +td - td +td - i +td - i4 +td - i8 +td / f +td / f4 +td / f8 +td / td +td // td +td % td + + +# boolean + +b_ / b +b_ / b_ +b_ / i +b_ / i8 +b_ / i4 +b_ / u8 +b_ / u4 +b_ / f +b_ / f8 +b_ / f4 +b_ / c +b_ / c16 +b_ / c8 + +b / b_ +b_ / b_ +i / b_ +i8 / b_ +i4 / b_ +u8 / b_ +u4 / b_ +f / b_ +f8 / b_ +f4 / b_ +c / b_ +c16 / b_ +c8 / b_ + +# Complex + +c16 + c16 +c16 + f8 +c16 + i8 +c16 + c8 +c16 + f4 +c16 + i4 +c16 + b_ +c16 + b +c16 + c +c16 + f +c16 + i +c16 + AR_f + +c16 + c16 +f8 + c16 +i8 + c16 +c8 + c16 +f4 + c16 +i4 + c16 +b_ + c16 +b + c16 +c + c16 +f + c16 +i + c16 +AR_f + c16 + +c8 + c16 +c8 + f8 +c8 + i8 +c8 + c8 +c8 + f4 +c8 + i4 +c8 + b_ +c8 + b +c8 + c +c8 + f +c8 + i +c8 + AR_f + +c16 + c8 +f8 + c8 +i8 + c8 +c8 + c8 +f4 + c8 +i4 + c8 +b_ + c8 +b + c8 +c + c8 +f + c8 +i + c8 +AR_f + c8 + +# Float + +f8 + f8 +f8 + i8 +f8 + f4 +f8 + i4 +f8 + b_ +f8 + b +f8 + c +f8 + f +f8 + i +f8 + AR_f + +f8 + f8 +i8 + f8 +f4 + f8 +i4 + f8 +b_ + f8 +b + f8 +c + f8 +f + f8 +i + f8 +AR_f + f8 + +f4 + f8 +f4 + i8 +f4 + f4 +f4 + i4 +f4 + b_ +f4 + b +f4 + c +f4 + f +f4 + i +f4 + AR_f + +f8 + f4 +i8 + f4 +f4 + f4 +i4 + f4 +b_ + f4 +b + f4 +c + f4 +f + f4 +i + f4 +AR_f + f4 + +# Int + +i8 + i8 +i8 + u8 +i8 + i4 +i8 + u4 +i8 + b_ +i8 + b +i8 + c +i8 + f +i8 + i +i8 + AR_f + +u8 + u8 +u8 + i4 +u8 + u4 +u8 + b_ +u8 + b +u8 + c +u8 + f +u8 + i +u8 + AR_f + +i8 + i8 +u8 + i8 +i4 + i8 +u4 + i8 +b_ + i8 +b + i8 +c + i8 +f + i8 +i + i8 +AR_f + i8 + +u8 + u8 +i4 + u8 +u4 + u8 +b_ + u8 +b + u8 +c + u8 +f + u8 +i + u8 +AR_f + u8 + +i4 + i8 +i4 + i4 +i4 + i +i4 + b_ +i4 + b +i4 + AR_f + +u4 + i8 +u4 + i4 +u4 + u8 +u4 + u4 +u4 + i +u4 + b_ +u4 + b +u4 + AR_f + +i8 + i4 +i4 + i4 +i + i4 +b_ + i4 +b + i4 +AR_f + i4 + +i8 + u4 +i4 + u4 +u8 + u4 +u4 + u4 +b_ + u4 +b + u4 +i + u4 +AR_f + u4 diff --git a/numpy/typing/tests/data/pass/array_constructors.py b/numpy/typing/tests/data/pass/array_constructors.py new file mode 100644 index 000000000000..2763d9c9272a --- /dev/null +++ b/numpy/typing/tests/data/pass/array_constructors.py @@ -0,0 +1,138 @@ +import sys +from typing import Any +import numpy as np + + +class Index: + def __index__(self) -> int: + return 0 + + +class SubClass(np.ndarray): + pass + + +def func(i: int, j: int, **kwargs: Any) -> SubClass: + return B + + +i8 = np.int64(1) + +A = np.array([1]) +B = A.view(SubClass).copy() +B_stack = np.array([[1], [1]]).view(SubClass) +C = [1] + +if sys.version_info >= (3, 8): + np.ndarray(Index()) + np.ndarray([Index()]) + +np.array(1, dtype=float) +np.array(1, copy=False) +np.array(1, order='F') +np.array(1, order=None) +np.array(1, subok=True) +np.array(1, ndmin=3) +np.array(1, str, copy=True, order='C', subok=False, ndmin=2) + +np.asarray(A) +np.asarray(B) +np.asarray(C) + +np.asanyarray(A) +np.asanyarray(B) +np.asanyarray(B, dtype=int) +np.asanyarray(C) + +np.ascontiguousarray(A) +np.ascontiguousarray(B) +np.ascontiguousarray(C) + +np.asfortranarray(A) +np.asfortranarray(B) +np.asfortranarray(C) + +np.require(A) +np.require(B) +np.require(B, dtype=int) +np.require(B, requirements=None) +np.require(B, requirements="E") +np.require(B, requirements=["ENSUREARRAY"]) +np.require(B, requirements={"F", "E"}) +np.require(B, requirements=["C", "OWNDATA"]) +np.require(B, requirements="W") +np.require(B, requirements="A") +np.require(C) + +np.linspace(0, 2) +np.linspace(0.5, [0, 1, 2]) +np.linspace([0, 1, 2], 3) +np.linspace(0j, 2) +np.linspace(0, 2, num=10) +np.linspace(0, 2, endpoint=True) +np.linspace(0, 2, retstep=True) +np.linspace(0j, 2j, retstep=True) +np.linspace(0, 2, dtype=bool) +np.linspace([0, 1], [2, 3], axis=Index()) + +np.logspace(0, 2, base=2) +np.logspace(0, 2, base=2) +np.logspace(0, 2, base=[1j, 2j], num=2) + +np.geomspace(1, 2) + +np.zeros_like(A) +np.zeros_like(C) +np.zeros_like(B) +np.zeros_like(B, dtype=np.int64) + +np.ones_like(A) +np.ones_like(C) +np.ones_like(B) +np.ones_like(B, dtype=np.int64) + +np.empty_like(A) +np.empty_like(C) +np.empty_like(B) +np.empty_like(B, dtype=np.int64) + +np.full_like(A, i8) +np.full_like(C, i8) +np.full_like(B, i8) +np.full_like(B, i8, dtype=np.int64) + +np.ones(1) +np.ones([1, 1, 1]) + +np.full(1, i8) +np.full([1, 1, 1], i8) + +np.indices([1, 2, 3]) +np.indices([1, 2, 3], sparse=True) + +np.fromfunction(func, (3, 5)) + +np.identity(10) + +np.atleast_1d(C) +np.atleast_1d(A) +np.atleast_1d(C, C) +np.atleast_1d(C, A) +np.atleast_1d(A, A) + +np.atleast_2d(C) + +np.atleast_3d(C) + +np.vstack([C, C]) +np.vstack([C, A]) +np.vstack([A, A]) + +np.hstack([C, C]) + +np.stack([C, C]) +np.stack([C, C], axis=0) +np.stack([C, C], out=B_stack) + +np.block([[C, C], [C, C]]) +np.block(A) diff --git a/numpy/typing/tests/data/pass/array_like.py b/numpy/typing/tests/data/pass/array_like.py new file mode 100644 index 000000000000..5bd2fda20e5c --- /dev/null +++ b/numpy/typing/tests/data/pass/array_like.py @@ -0,0 +1,39 @@ +from typing import Any, Optional + +import numpy as np +from numpy.typing import ArrayLike, _SupportsArray + +x1: ArrayLike = True +x2: ArrayLike = 5 +x3: ArrayLike = 1.0 +x4: ArrayLike = 1 + 1j +x5: ArrayLike = np.int8(1) +x6: ArrayLike = np.float64(1) +x7: ArrayLike = np.complex128(1) +x8: ArrayLike = np.array([1, 2, 3]) +x9: ArrayLike = [1, 2, 3] +x10: ArrayLike = (1, 2, 3) +x11: ArrayLike = "foo" +x12: ArrayLike = memoryview(b'foo') + + +class A: + def __array__(self, dtype: Optional[np.dtype] = None) -> np.ndarray: + return np.array([1, 2, 3]) + + +x13: ArrayLike = A() + +scalar: _SupportsArray = np.int64(1) +scalar.__array__() +array: _SupportsArray = np.array(1) +array.__array__() + +a: _SupportsArray = A() +a.__array__() +a.__array__() + +# Escape hatch for when you mean to make something like an object +# array. +object_array_scalar: Any = (i for i in range(10)) +np.array(object_array_scalar) diff --git a/numpy/typing/tests/data/pass/arrayprint.py b/numpy/typing/tests/data/pass/arrayprint.py new file mode 100644 index 000000000000..6c704c755570 --- /dev/null +++ b/numpy/typing/tests/data/pass/arrayprint.py @@ -0,0 +1,37 @@ +import numpy as np + +AR = np.arange(10) +AR.setflags(write=False) + +with np.printoptions(): + np.set_printoptions( + precision=1, + threshold=2, + edgeitems=3, + linewidth=4, + suppress=False, + nanstr="Bob", + infstr="Bill", + formatter={}, + sign="+", + floatmode="unique", + ) + np.get_printoptions() + str(AR) + + np.array2string( + AR, + max_line_width=5, + precision=2, + suppress_small=True, + separator=";", + prefix="test", + threshold=5, + floatmode="fixed", + suffix="?", + legacy="1.13", + ) + np.format_float_scientific(1, precision=5) + np.format_float_positional(1, trim="k") + np.array_repr(AR) + np.array_str(AR) diff --git a/numpy/typing/tests/data/pass/arrayterator.py b/numpy/typing/tests/data/pass/arrayterator.py new file mode 100644 index 000000000000..572be5e2fe29 --- /dev/null +++ b/numpy/typing/tests/data/pass/arrayterator.py @@ -0,0 +1,27 @@ + +from __future__ import annotations + +from typing import Any +import numpy as np + +AR_i8: np.ndarray[Any, np.dtype[np.int_]] = np.arange(10) +ar_iter = np.lib.Arrayterator(AR_i8) + +ar_iter.var +ar_iter.buf_size +ar_iter.start +ar_iter.stop +ar_iter.step +ar_iter.shape +ar_iter.flat + +ar_iter.__array__() + +for i in ar_iter: + pass + +ar_iter[0] +ar_iter[...] +ar_iter[:] +ar_iter[0, 0, 0] +ar_iter[..., 0, :] diff --git a/numpy/typing/tests/data/pass/bitwise_ops.py b/numpy/typing/tests/data/pass/bitwise_ops.py new file mode 100644 index 000000000000..67449e2c21d8 --- /dev/null +++ b/numpy/typing/tests/data/pass/bitwise_ops.py @@ -0,0 +1,131 @@ +import numpy as np + +i8 = np.int64(1) +u8 = np.uint64(1) + +i4 = np.int32(1) +u4 = np.uint32(1) + +b_ = np.bool_(1) + +b = bool(1) +i = int(1) + +AR = np.array([0, 1, 2], dtype=np.int32) +AR.setflags(write=False) + + +i8 << i8 +i8 >> i8 +i8 | i8 +i8 ^ i8 +i8 & i8 + +i8 << AR +i8 >> AR +i8 | AR +i8 ^ AR +i8 & AR + +i4 << i4 +i4 >> i4 +i4 | i4 +i4 ^ i4 +i4 & i4 + +i8 << i4 +i8 >> i4 +i8 | i4 +i8 ^ i4 +i8 & i4 + +i8 << i +i8 >> i +i8 | i +i8 ^ i +i8 & i + +i8 << b_ +i8 >> b_ +i8 | b_ +i8 ^ b_ +i8 & b_ + +i8 << b +i8 >> b +i8 | b +i8 ^ b +i8 & b + +u8 << u8 +u8 >> u8 +u8 | u8 +u8 ^ u8 +u8 & u8 + +u8 << AR +u8 >> AR +u8 | AR +u8 ^ AR +u8 & AR + +u4 << u4 +u4 >> u4 +u4 | u4 +u4 ^ u4 +u4 & u4 + +u4 << i4 +u4 >> i4 +u4 | i4 +u4 ^ i4 +u4 & i4 + +u4 << i +u4 >> i +u4 | i +u4 ^ i +u4 & i + +u8 << b_ +u8 >> b_ +u8 | b_ +u8 ^ b_ +u8 & b_ + +u8 << b +u8 >> b +u8 | b +u8 ^ b +u8 & b + +b_ << b_ +b_ >> b_ +b_ | b_ +b_ ^ b_ +b_ & b_ + +b_ << AR +b_ >> AR +b_ | AR +b_ ^ AR +b_ & AR + +b_ << b +b_ >> b +b_ | b +b_ ^ b +b_ & b + +b_ << i +b_ >> i +b_ | i +b_ ^ i +b_ & i + +~i8 +~i4 +~u8 +~u4 +~b_ +~AR diff --git a/numpy/typing/tests/data/pass/comparisons.py b/numpy/typing/tests/data/pass/comparisons.py new file mode 100644 index 000000000000..ce41de43596e --- /dev/null +++ b/numpy/typing/tests/data/pass/comparisons.py @@ -0,0 +1,301 @@ +from __future__ import annotations + +from typing import Any +import numpy as np + +c16 = np.complex128() +f8 = np.float64() +i8 = np.int64() +u8 = np.uint64() + +c8 = np.complex64() +f4 = np.float32() +i4 = np.int32() +u4 = np.uint32() + +dt = np.datetime64(0, "D") +td = np.timedelta64(0, "D") + +b_ = np.bool_() + +b = bool() +c = complex() +f = float() +i = int() + +SEQ = (0, 1, 2, 3, 4) + +AR_b: np.ndarray[Any, np.dtype[np.bool_]] = np.array([True]) +AR_u: np.ndarray[Any, np.dtype[np.uint32]] = np.array([1], dtype=np.uint32) +AR_i: np.ndarray[Any, np.dtype[np.int_]] = np.array([1]) +AR_f: np.ndarray[Any, np.dtype[np.float_]] = np.array([1.0]) +AR_c: np.ndarray[Any, np.dtype[np.complex_]] = np.array([1.0j]) +AR_m: np.ndarray[Any, np.dtype[np.timedelta64]] = np.array([np.timedelta64("1")]) +AR_M: np.ndarray[Any, np.dtype[np.datetime64]] = np.array([np.datetime64("1")]) +AR_O: np.ndarray[Any, np.dtype[np.object_]] = np.array([1], dtype=object) + +# Arrays + +AR_b > AR_b +AR_b > AR_u +AR_b > AR_i +AR_b > AR_f +AR_b > AR_c + +AR_u > AR_b +AR_u > AR_u +AR_u > AR_i +AR_u > AR_f +AR_u > AR_c + +AR_i > AR_b +AR_i > AR_u +AR_i > AR_i +AR_i > AR_f +AR_i > AR_c + +AR_f > AR_b +AR_f > AR_u +AR_f > AR_i +AR_f > AR_f +AR_f > AR_c + +AR_c > AR_b +AR_c > AR_u +AR_c > AR_i +AR_c > AR_f +AR_c > AR_c + +AR_m > AR_b +AR_m > AR_u +AR_m > AR_i +AR_b > AR_m +AR_u > AR_m +AR_i > AR_m + +AR_M > AR_M + +AR_O > AR_O +1 > AR_O +AR_O > 1 + +# Time structures + +dt > dt + +td > td +td > i +td > i4 +td > i8 +td > AR_i +td > SEQ + +# boolean + +b_ > b +b_ > b_ +b_ > i +b_ > i8 +b_ > i4 +b_ > u8 +b_ > u4 +b_ > f +b_ > f8 +b_ > f4 +b_ > c +b_ > c16 +b_ > c8 +b_ > AR_i +b_ > SEQ + +# Complex + +c16 > c16 +c16 > f8 +c16 > i8 +c16 > c8 +c16 > f4 +c16 > i4 +c16 > b_ +c16 > b +c16 > c +c16 > f +c16 > i +c16 > AR_i +c16 > SEQ + +c16 > c16 +f8 > c16 +i8 > c16 +c8 > c16 +f4 > c16 +i4 > c16 +b_ > c16 +b > c16 +c > c16 +f > c16 +i > c16 +AR_i > c16 +SEQ > c16 + +c8 > c16 +c8 > f8 +c8 > i8 +c8 > c8 +c8 > f4 +c8 > i4 +c8 > b_ +c8 > b +c8 > c +c8 > f +c8 > i +c8 > AR_i +c8 > SEQ + +c16 > c8 +f8 > c8 +i8 > c8 +c8 > c8 +f4 > c8 +i4 > c8 +b_ > c8 +b > c8 +c > c8 +f > c8 +i > c8 +AR_i > c8 +SEQ > c8 + +# Float + +f8 > f8 +f8 > i8 +f8 > f4 +f8 > i4 +f8 > b_ +f8 > b +f8 > c +f8 > f +f8 > i +f8 > AR_i +f8 > SEQ + +f8 > f8 +i8 > f8 +f4 > f8 +i4 > f8 +b_ > f8 +b > f8 +c > f8 +f > f8 +i > f8 +AR_i > f8 +SEQ > f8 + +f4 > f8 +f4 > i8 +f4 > f4 +f4 > i4 +f4 > b_ +f4 > b +f4 > c +f4 > f +f4 > i +f4 > AR_i +f4 > SEQ + +f8 > f4 +i8 > f4 +f4 > f4 +i4 > f4 +b_ > f4 +b > f4 +c > f4 +f > f4 +i > f4 +AR_i > f4 +SEQ > f4 + +# Int + +i8 > i8 +i8 > u8 +i8 > i4 +i8 > u4 +i8 > b_ +i8 > b +i8 > c +i8 > f +i8 > i +i8 > AR_i +i8 > SEQ + +u8 > u8 +u8 > i4 +u8 > u4 +u8 > b_ +u8 > b +u8 > c +u8 > f +u8 > i +u8 > AR_i +u8 > SEQ + +i8 > i8 +u8 > i8 +i4 > i8 +u4 > i8 +b_ > i8 +b > i8 +c > i8 +f > i8 +i > i8 +AR_i > i8 +SEQ > i8 + +u8 > u8 +i4 > u8 +u4 > u8 +b_ > u8 +b > u8 +c > u8 +f > u8 +i > u8 +AR_i > u8 +SEQ > u8 + +i4 > i8 +i4 > i4 +i4 > i +i4 > b_ +i4 > b +i4 > AR_i +i4 > SEQ + +u4 > i8 +u4 > i4 +u4 > u8 +u4 > u4 +u4 > i +u4 > b_ +u4 > b +u4 > AR_i +u4 > SEQ + +i8 > i4 +i4 > i4 +i > i4 +b_ > i4 +b > i4 +AR_i > i4 +SEQ > i4 + +i8 > u4 +i4 > u4 +u8 > u4 +u4 > u4 +b_ > u4 +b > u4 +i > u4 +AR_i > u4 +SEQ > u4 diff --git a/numpy/typing/tests/data/pass/dtype.py b/numpy/typing/tests/data/pass/dtype.py new file mode 100644 index 000000000000..e849cfdd4e11 --- /dev/null +++ b/numpy/typing/tests/data/pass/dtype.py @@ -0,0 +1,57 @@ +import numpy as np + +dtype_obj = np.dtype(np.str_) +void_dtype_obj = np.dtype([("f0", np.float64), ("f1", np.float32)]) + +np.dtype(dtype=np.int64) +np.dtype(int) +np.dtype("int") +np.dtype(None) + +np.dtype((int, 2)) +np.dtype((int, (1,))) + +np.dtype({"names": ["a", "b"], "formats": [int, float]}) +np.dtype({"names": ["a"], "formats": [int], "titles": [object]}) +np.dtype({"names": ["a"], "formats": [int], "titles": [object()]}) + +np.dtype([("name", np.unicode_, 16), ("grades", np.float64, (2,)), ("age", "int32")]) + +np.dtype( + { + "names": ["a", "b"], + "formats": [int, float], + "itemsize": 9, + "aligned": False, + "titles": ["x", "y"], + "offsets": [0, 1], + } +) + +np.dtype((np.float_, float)) + + +class Test: + dtype = np.dtype(float) + + +np.dtype(Test()) + +# Methods and attributes +dtype_obj.base +dtype_obj.subdtype +dtype_obj.newbyteorder() +dtype_obj.type +dtype_obj.name +dtype_obj.names + +dtype_obj * 0 +dtype_obj * 2 + +0 * dtype_obj +2 * dtype_obj + +void_dtype_obj["f0"] +void_dtype_obj[0] +void_dtype_obj[["f0", "f1"]] +void_dtype_obj[["f0"]] diff --git a/numpy/typing/tests/data/pass/einsumfunc.py b/numpy/typing/tests/data/pass/einsumfunc.py new file mode 100644 index 000000000000..429764e67ecc --- /dev/null +++ b/numpy/typing/tests/data/pass/einsumfunc.py @@ -0,0 +1,36 @@ +from __future__ import annotations + +from typing import Any + +import numpy as np + +AR_LIKE_b = [True, True, True] +AR_LIKE_u = [np.uint32(1), np.uint32(2), np.uint32(3)] +AR_LIKE_i = [1, 2, 3] +AR_LIKE_f = [1.0, 2.0, 3.0] +AR_LIKE_c = [1j, 2j, 3j] +AR_LIKE_U = ["1", "2", "3"] + +OUT_f: np.ndarray[Any, np.dtype[np.float64]] = np.empty(3, dtype=np.float64) +OUT_c: np.ndarray[Any, np.dtype[np.complex128]] = np.empty(3, dtype=np.complex128) + +np.einsum("i,i->i", AR_LIKE_b, AR_LIKE_b) +np.einsum("i,i->i", AR_LIKE_u, AR_LIKE_u) +np.einsum("i,i->i", AR_LIKE_i, AR_LIKE_i) +np.einsum("i,i->i", AR_LIKE_f, AR_LIKE_f) +np.einsum("i,i->i", AR_LIKE_c, AR_LIKE_c) +np.einsum("i,i->i", AR_LIKE_b, AR_LIKE_i) +np.einsum("i,i,i,i->i", AR_LIKE_b, AR_LIKE_u, AR_LIKE_i, AR_LIKE_c) + +np.einsum("i,i->i", AR_LIKE_f, AR_LIKE_f, dtype="c16") +np.einsum("i,i->i", AR_LIKE_U, AR_LIKE_U, dtype=bool, casting="unsafe") +np.einsum("i,i->i", AR_LIKE_f, AR_LIKE_f, out=OUT_c) +np.einsum("i,i->i", AR_LIKE_U, AR_LIKE_U, dtype=int, casting="unsafe", out=OUT_f) + +np.einsum_path("i,i->i", AR_LIKE_b, AR_LIKE_b) +np.einsum_path("i,i->i", AR_LIKE_u, AR_LIKE_u) +np.einsum_path("i,i->i", AR_LIKE_i, AR_LIKE_i) +np.einsum_path("i,i->i", AR_LIKE_f, AR_LIKE_f) +np.einsum_path("i,i->i", AR_LIKE_c, AR_LIKE_c) +np.einsum_path("i,i->i", AR_LIKE_b, AR_LIKE_i) +np.einsum_path("i,i,i,i->i", AR_LIKE_b, AR_LIKE_u, AR_LIKE_i, AR_LIKE_c) diff --git a/numpy/typing/tests/data/pass/flatiter.py b/numpy/typing/tests/data/pass/flatiter.py new file mode 100644 index 000000000000..63c839af4b23 --- /dev/null +++ b/numpy/typing/tests/data/pass/flatiter.py @@ -0,0 +1,16 @@ +import numpy as np + +a = np.empty((2, 2)).flat + +a.base +a.copy() +a.coords +a.index +iter(a) +next(a) +a[0] +a[[0, 1, 2]] +a[...] +a[:] +a.__array__() +a.__array__(np.dtype(np.float64)) diff --git a/numpy/typing/tests/data/pass/fromnumeric.py b/numpy/typing/tests/data/pass/fromnumeric.py new file mode 100644 index 000000000000..9e936e68465a --- /dev/null +++ b/numpy/typing/tests/data/pass/fromnumeric.py @@ -0,0 +1,260 @@ +"""Tests for :mod:`numpy.core.fromnumeric`.""" + +import numpy as np + +A = np.array(True, ndmin=2, dtype=bool) +B = np.array(1.0, ndmin=2, dtype=np.float32) +A.setflags(write=False) +B.setflags(write=False) + +a = np.bool_(True) +b = np.float32(1.0) +c = 1.0 +d = np.array(1.0, dtype=np.float32) # writeable + +np.take(a, 0) +np.take(b, 0) +np.take(c, 0) +np.take(A, 0) +np.take(B, 0) +np.take(A, [0]) +np.take(B, [0]) + +np.reshape(a, 1) +np.reshape(b, 1) +np.reshape(c, 1) +np.reshape(A, 1) +np.reshape(B, 1) + +np.choose(a, [True, True]) +np.choose(A, [1.0, 1.0]) + +np.repeat(a, 1) +np.repeat(b, 1) +np.repeat(c, 1) +np.repeat(A, 1) +np.repeat(B, 1) + +np.swapaxes(A, 0, 0) +np.swapaxes(B, 0, 0) + +np.transpose(a) +np.transpose(b) +np.transpose(c) +np.transpose(A) +np.transpose(B) + +np.partition(a, 0, axis=None) +np.partition(b, 0, axis=None) +np.partition(c, 0, axis=None) +np.partition(A, 0) +np.partition(B, 0) + +np.argpartition(a, 0) +np.argpartition(b, 0) +np.argpartition(c, 0) +np.argpartition(A, 0) +np.argpartition(B, 0) + +np.sort(A, 0) +np.sort(B, 0) + +np.argsort(A, 0) +np.argsort(B, 0) + +np.argmax(A) +np.argmax(B) +np.argmax(A, axis=0) +np.argmax(B, axis=0) + +np.argmin(A) +np.argmin(B) +np.argmin(A, axis=0) +np.argmin(B, axis=0) + +np.searchsorted(A[0], 0) +np.searchsorted(B[0], 0) +np.searchsorted(A[0], [0]) +np.searchsorted(B[0], [0]) + +np.resize(a, (5, 5)) +np.resize(b, (5, 5)) +np.resize(c, (5, 5)) +np.resize(A, (5, 5)) +np.resize(B, (5, 5)) + +np.squeeze(a) +np.squeeze(b) +np.squeeze(c) +np.squeeze(A) +np.squeeze(B) + +np.diagonal(A) +np.diagonal(B) + +np.trace(A) +np.trace(B) + +np.ravel(a) +np.ravel(b) +np.ravel(c) +np.ravel(A) +np.ravel(B) + +np.nonzero(A) +np.nonzero(B) + +np.shape(a) +np.shape(b) +np.shape(c) +np.shape(A) +np.shape(B) + +np.compress([True], a) +np.compress([True], b) +np.compress([True], c) +np.compress([True], A) +np.compress([True], B) + +np.clip(a, 0, 1.0) +np.clip(b, -1, 1) +np.clip(a, 0, None) +np.clip(b, None, 1) +np.clip(c, 0, 1) +np.clip(A, 0, 1) +np.clip(B, 0, 1) +np.clip(B, [0, 1], [1, 2]) + +np.sum(a) +np.sum(b) +np.sum(c) +np.sum(A) +np.sum(B) +np.sum(A, axis=0) +np.sum(B, axis=0) + +np.all(a) +np.all(b) +np.all(c) +np.all(A) +np.all(B) +np.all(A, axis=0) +np.all(B, axis=0) +np.all(A, keepdims=True) +np.all(B, keepdims=True) + +np.any(a) +np.any(b) +np.any(c) +np.any(A) +np.any(B) +np.any(A, axis=0) +np.any(B, axis=0) +np.any(A, keepdims=True) +np.any(B, keepdims=True) + +np.cumsum(a) +np.cumsum(b) +np.cumsum(c) +np.cumsum(A) +np.cumsum(B) + +np.ptp(b) +np.ptp(c) +np.ptp(B) +np.ptp(B, axis=0) +np.ptp(B, keepdims=True) + +np.amax(a) +np.amax(b) +np.amax(c) +np.amax(A) +np.amax(B) +np.amax(A, axis=0) +np.amax(B, axis=0) +np.amax(A, keepdims=True) +np.amax(B, keepdims=True) + +np.amin(a) +np.amin(b) +np.amin(c) +np.amin(A) +np.amin(B) +np.amin(A, axis=0) +np.amin(B, axis=0) +np.amin(A, keepdims=True) +np.amin(B, keepdims=True) + +np.prod(a) +np.prod(b) +np.prod(c) +np.prod(A) +np.prod(B) +np.prod(a, dtype=None) +np.prod(A, dtype=None) +np.prod(A, axis=0) +np.prod(B, axis=0) +np.prod(A, keepdims=True) +np.prod(B, keepdims=True) +np.prod(b, out=d) +np.prod(B, out=d) + +np.cumprod(a) +np.cumprod(b) +np.cumprod(c) +np.cumprod(A) +np.cumprod(B) + +np.ndim(a) +np.ndim(b) +np.ndim(c) +np.ndim(A) +np.ndim(B) + +np.size(a) +np.size(b) +np.size(c) +np.size(A) +np.size(B) + +np.around(a) +np.around(b) +np.around(c) +np.around(A) +np.around(B) + +np.mean(a) +np.mean(b) +np.mean(c) +np.mean(A) +np.mean(B) +np.mean(A, axis=0) +np.mean(B, axis=0) +np.mean(A, keepdims=True) +np.mean(B, keepdims=True) +np.mean(b, out=d) +np.mean(B, out=d) + +np.std(a) +np.std(b) +np.std(c) +np.std(A) +np.std(B) +np.std(A, axis=0) +np.std(B, axis=0) +np.std(A, keepdims=True) +np.std(B, keepdims=True) +np.std(b, out=d) +np.std(B, out=d) + +np.var(a) +np.var(b) +np.var(c) +np.var(A) +np.var(B) +np.var(A, axis=0) +np.var(B, axis=0) +np.var(A, keepdims=True) +np.var(B, keepdims=True) +np.var(b, out=d) +np.var(B, out=d) diff --git a/numpy/typing/tests/data/pass/index_tricks.py b/numpy/typing/tests/data/pass/index_tricks.py new file mode 100644 index 000000000000..4c4c1195990a --- /dev/null +++ b/numpy/typing/tests/data/pass/index_tricks.py @@ -0,0 +1,64 @@ +from __future__ import annotations +from typing import Any +import numpy as np + +AR_LIKE_b = [[True, True], [True, True]] +AR_LIKE_i = [[1, 2], [3, 4]] +AR_LIKE_f = [[1.0, 2.0], [3.0, 4.0]] +AR_LIKE_U = [["1", "2"], ["3", "4"]] + +AR_i8: np.ndarray[Any, np.dtype[np.int64]] = np.array(AR_LIKE_i, dtype=np.int64) + +np.ndenumerate(AR_i8) +np.ndenumerate(AR_LIKE_f) +np.ndenumerate(AR_LIKE_U) + +np.ndenumerate(AR_i8).iter +np.ndenumerate(AR_LIKE_f).iter +np.ndenumerate(AR_LIKE_U).iter + +next(np.ndenumerate(AR_i8)) +next(np.ndenumerate(AR_LIKE_f)) +next(np.ndenumerate(AR_LIKE_U)) + +iter(np.ndenumerate(AR_i8)) +iter(np.ndenumerate(AR_LIKE_f)) +iter(np.ndenumerate(AR_LIKE_U)) + +iter(np.ndindex(1, 2, 3)) +next(np.ndindex(1, 2, 3)) + +np.unravel_index([22, 41, 37], (7, 6)) +np.unravel_index([31, 41, 13], (7, 6), order='F') +np.unravel_index(1621, (6, 7, 8, 9)) + +np.ravel_multi_index(AR_LIKE_i, (7, 6)) +np.ravel_multi_index(AR_LIKE_i, (7, 6), order='F') +np.ravel_multi_index(AR_LIKE_i, (4, 6), mode='clip') +np.ravel_multi_index(AR_LIKE_i, (4, 4), mode=('clip', 'wrap')) +np.ravel_multi_index((3, 1, 4, 1), (6, 7, 8, 9)) + +np.mgrid[1:1:2] +np.mgrid[1:1:2, None:10] + +np.ogrid[1:1:2] +np.ogrid[1:1:2, None:10] + +np.index_exp[0:1] +np.index_exp[0:1, None:3] +np.index_exp[0, 0:1, ..., [0, 1, 3]] + +np.s_[0:1] +np.s_[0:1, None:3] +np.s_[0, 0:1, ..., [0, 1, 3]] + +np.ix_(AR_LIKE_b[0]) +np.ix_(AR_LIKE_i[0], AR_LIKE_f[0]) +np.ix_(AR_i8[0]) + +np.fill_diagonal(AR_i8, 5) + +np.diag_indices(4) +np.diag_indices(2, 3) + +np.diag_indices_from(AR_i8) diff --git a/numpy/typing/tests/data/pass/lib_utils.py b/numpy/typing/tests/data/pass/lib_utils.py new file mode 100644 index 000000000000..65640c28873d --- /dev/null +++ b/numpy/typing/tests/data/pass/lib_utils.py @@ -0,0 +1,25 @@ +from __future__ import annotations + +from io import StringIO + +import numpy as np + +FILE = StringIO() +AR = np.arange(10, dtype=np.float64) + +def func(a: int) -> bool: ... + +np.deprecate(func) +np.deprecate() + +np.deprecate_with_doc("test") +np.deprecate_with_doc(None) + +np.byte_bounds(AR) +np.byte_bounds(np.float64()) + +np.info(1, output=FILE) + +np.source(np.interp, output=FILE) + +np.lookfor("binary representation", output=FILE) diff --git a/numpy/typing/tests/data/pass/lib_version.py b/numpy/typing/tests/data/pass/lib_version.py new file mode 100644 index 000000000000..f3825eca5247 --- /dev/null +++ b/numpy/typing/tests/data/pass/lib_version.py @@ -0,0 +1,18 @@ +from numpy.lib import NumpyVersion + +version = NumpyVersion("1.8.0") + +version.vstring +version.version +version.major +version.minor +version.bugfix +version.pre_release +version.is_devversion + +version == version +version != version +version < "1.8.0" +version <= version +version > version +version >= "1.8.0" diff --git a/numpy/typing/tests/data/pass/literal.py b/numpy/typing/tests/data/pass/literal.py new file mode 100644 index 000000000000..8eaeb6afb2ad --- /dev/null +++ b/numpy/typing/tests/data/pass/literal.py @@ -0,0 +1,45 @@ +from functools import partial +from typing import Callable, List, Tuple + +import pytest # type: ignore +import numpy as np + +AR = np.array(0) +AR.setflags(write=False) + +KACF = frozenset({None, "K", "A", "C", "F"}) +ACF = frozenset({None, "A", "C", "F"}) +CF = frozenset({None, "C", "F"}) + +order_list: List[Tuple[frozenset, Callable]] = [ + (KACF, partial(np.ndarray, 1)), + (KACF, AR.tobytes), + (KACF, partial(AR.astype, int)), + (KACF, AR.copy), + (ACF, partial(AR.reshape, 1)), + (KACF, AR.flatten), + (KACF, AR.ravel), + (KACF, partial(np.array, 1)), + (CF, partial(np.zeros, 1)), + (CF, partial(np.ones, 1)), + (CF, partial(np.empty, 1)), + (CF, partial(np.full, 1, 1)), + (KACF, partial(np.zeros_like, AR)), + (KACF, partial(np.ones_like, AR)), + (KACF, partial(np.empty_like, AR)), + (KACF, partial(np.full_like, AR, 1)), + (KACF, partial(np.add, 1, 1)), # i.e. np.ufunc.__call__ + (ACF, partial(np.reshape, AR, 1)), + (KACF, partial(np.ravel, AR)), + (KACF, partial(np.asarray, 1)), + (KACF, partial(np.asanyarray, 1)), +] + +for order_set, func in order_list: + for order in order_set: + func(order=order) + + invalid_orders = KACF - order_set + for order in invalid_orders: + with pytest.raises(ValueError): + func(order=order) diff --git a/numpy/typing/tests/data/pass/mod.py b/numpy/typing/tests/data/pass/mod.py new file mode 100644 index 000000000000..b5b9afb2a544 --- /dev/null +++ b/numpy/typing/tests/data/pass/mod.py @@ -0,0 +1,149 @@ +import numpy as np + +f8 = np.float64(1) +i8 = np.int64(1) +u8 = np.uint64(1) + +f4 = np.float32(1) +i4 = np.int32(1) +u4 = np.uint32(1) + +td = np.timedelta64(1, "D") +b_ = np.bool_(1) + +b = bool(1) +f = float(1) +i = int(1) + +AR = np.array([1], dtype=np.bool_) +AR.setflags(write=False) + +AR2 = np.array([1], dtype=np.timedelta64) +AR2.setflags(write=False) + +# Time structures + +td % td +td % AR2 +AR2 % td + +divmod(td, td) +divmod(td, AR2) +divmod(AR2, td) + +# Bool + +b_ % b +b_ % i +b_ % f +b_ % b_ +b_ % i8 +b_ % u8 +b_ % f8 +b_ % AR + +divmod(b_, b) +divmod(b_, i) +divmod(b_, f) +divmod(b_, b_) +divmod(b_, i8) +divmod(b_, u8) +divmod(b_, f8) +divmod(b_, AR) + +b % b_ +i % b_ +f % b_ +b_ % b_ +i8 % b_ +u8 % b_ +f8 % b_ +AR % b_ + +divmod(b, b_) +divmod(i, b_) +divmod(f, b_) +divmod(b_, b_) +divmod(i8, b_) +divmod(u8, b_) +divmod(f8, b_) +divmod(AR, b_) + +# int + +i8 % b +i8 % i +i8 % f +i8 % i8 +i8 % f8 +i4 % i8 +i4 % f8 +i4 % i4 +i4 % f4 +i8 % AR + +divmod(i8, b) +divmod(i8, i) +divmod(i8, f) +divmod(i8, i8) +divmod(i8, f8) +divmod(i8, i4) +divmod(i8, f4) +divmod(i4, i4) +divmod(i4, f4) +divmod(i8, AR) + +b % i8 +i % i8 +f % i8 +i8 % i8 +f8 % i8 +i8 % i4 +f8 % i4 +i4 % i4 +f4 % i4 +AR % i8 + +divmod(b, i8) +divmod(i, i8) +divmod(f, i8) +divmod(i8, i8) +divmod(f8, i8) +divmod(i4, i8) +divmod(f4, i8) +divmod(i4, i4) +divmod(f4, i4) +divmod(AR, i8) + +# float + +f8 % b +f8 % i +f8 % f +i8 % f4 +f4 % f4 +f8 % AR + +divmod(f8, b) +divmod(f8, i) +divmod(f8, f) +divmod(f8, f8) +divmod(f8, f4) +divmod(f4, f4) +divmod(f8, AR) + +b % f8 +i % f8 +f % f8 +f8 % f8 +f8 % f8 +f4 % f4 +AR % f8 + +divmod(b, f8) +divmod(i, f8) +divmod(f, f8) +divmod(f8, f8) +divmod(f4, f8) +divmod(f4, f4) +divmod(AR, f8) diff --git a/numpy/typing/tests/data/pass/modules.py b/numpy/typing/tests/data/pass/modules.py new file mode 100644 index 000000000000..9261874d565a --- /dev/null +++ b/numpy/typing/tests/data/pass/modules.py @@ -0,0 +1,43 @@ +import numpy as np +from numpy import f2py + +np.char +np.ctypeslib +np.emath +np.fft +np.lib +np.linalg +np.ma +np.matrixlib +np.polynomial +np.random +np.rec +np.testing +np.version + +np.lib.format +np.lib.mixins +np.lib.scimath +np.lib.stride_tricks +np.ma.extras +np.polynomial.chebyshev +np.polynomial.hermite +np.polynomial.hermite_e +np.polynomial.laguerre +np.polynomial.legendre +np.polynomial.polynomial + +np.__path__ +np.__version__ +np.__git_version__ + +np.__all__ +np.char.__all__ +np.ctypeslib.__all__ +np.emath.__all__ +np.lib.__all__ +np.ma.__all__ +np.random.__all__ +np.rec.__all__ +np.testing.__all__ +f2py.__all__ diff --git a/numpy/typing/tests/data/pass/multiarray.py b/numpy/typing/tests/data/pass/multiarray.py new file mode 100644 index 000000000000..26cedfd77566 --- /dev/null +++ b/numpy/typing/tests/data/pass/multiarray.py @@ -0,0 +1,76 @@ +import numpy as np +import numpy.typing as npt + +AR_f8: npt.NDArray[np.float64] = np.array([1.0]) +AR_i4 = np.array([1], dtype=np.int32) +AR_u1 = np.array([1], dtype=np.uint8) + +AR_LIKE_f = [1.5] +AR_LIKE_i = [1] + +b_f8 = np.broadcast(AR_f8) +b_i4_f8_f8 = np.broadcast(AR_i4, AR_f8, AR_f8) + +next(b_f8) +b_f8.reset() +b_f8.index +b_f8.iters +b_f8.nd +b_f8.ndim +b_f8.numiter +b_f8.shape +b_f8.size + +next(b_i4_f8_f8) +b_i4_f8_f8.reset() +b_i4_f8_f8.ndim +b_i4_f8_f8.index +b_i4_f8_f8.iters +b_i4_f8_f8.nd +b_i4_f8_f8.numiter +b_i4_f8_f8.shape +b_i4_f8_f8.size + +np.inner(AR_f8, AR_i4) + +np.where([True, True, False]) +np.where([True, True, False], 1, 0) + +np.lexsort([0, 1, 2]) + +np.can_cast(np.dtype("i8"), int) +np.can_cast(AR_f8, "f8") +np.can_cast(AR_f8, np.complex128, casting="unsafe") + +np.min_scalar_type([1]) +np.min_scalar_type(AR_f8) + +np.result_type(int, AR_i4) +np.result_type(AR_f8, AR_u1) +np.result_type(AR_f8, np.complex128) + +np.dot(AR_LIKE_f, AR_i4) +np.dot(AR_u1, 1) +np.dot(1.5j, 1) +np.dot(AR_u1, 1, out=AR_f8) + +np.vdot(AR_LIKE_f, AR_i4) +np.vdot(AR_u1, 1) +np.vdot(1.5j, 1) + +np.bincount(AR_i4) + +np.copyto(AR_f8, [1.6]) + +np.putmask(AR_f8, [True], 1.5) + +np.packbits(AR_i4) +np.packbits(AR_u1) + +np.unpackbits(AR_u1) + +np.shares_memory(1, 2) +np.shares_memory(AR_f8, AR_f8, max_work=1) + +np.may_share_memory(1, 2) +np.may_share_memory(AR_f8, AR_f8, max_work=1) diff --git a/numpy/typing/tests/data/pass/ndarray_conversion.py b/numpy/typing/tests/data/pass/ndarray_conversion.py new file mode 100644 index 000000000000..303cf53e4453 --- /dev/null +++ b/numpy/typing/tests/data/pass/ndarray_conversion.py @@ -0,0 +1,94 @@ +import os +import tempfile + +import numpy as np + +nd = np.array([[1, 2], [3, 4]]) +scalar_array = np.array(1) + +# item +scalar_array.item() +nd.item(1) +nd.item(0, 1) +nd.item((0, 1)) + +# tolist is pretty simple + +# itemset +scalar_array.itemset(3) +nd.itemset(3, 0) +nd.itemset((0, 0), 3) + +# tobytes +nd.tobytes() +nd.tobytes("C") +nd.tobytes(None) + +# tofile +if os.name != "nt": + with tempfile.NamedTemporaryFile(suffix=".txt") as tmp: + nd.tofile(tmp.name) + nd.tofile(tmp.name, "") + nd.tofile(tmp.name, sep="") + + nd.tofile(tmp.name, "", "%s") + nd.tofile(tmp.name, format="%s") + + nd.tofile(tmp) + +# dump is pretty simple +# dumps is pretty simple + +# astype +nd.astype("float") +nd.astype(float) + +nd.astype(float, "K") +nd.astype(float, order="K") + +nd.astype(float, "K", "unsafe") +nd.astype(float, casting="unsafe") + +nd.astype(float, "K", "unsafe", True) +nd.astype(float, subok=True) + +nd.astype(float, "K", "unsafe", True, True) +nd.astype(float, copy=True) + +# byteswap +nd.byteswap() +nd.byteswap(True) + +# copy +nd.copy() +nd.copy("C") + +# view +nd.view() +nd.view(np.int64) +nd.view(dtype=np.int64) +nd.view(np.int64, np.matrix) +nd.view(type=np.matrix) + +# getfield +complex_array = np.array([[1 + 1j, 0], [0, 1 - 1j]], dtype=np.complex128) + +complex_array.getfield("float") +complex_array.getfield(float) + +complex_array.getfield("float", 8) +complex_array.getfield(float, offset=8) + +# setflags +nd.setflags() + +nd.setflags(True) +nd.setflags(write=True) + +nd.setflags(True, True) +nd.setflags(write=True, align=True) + +nd.setflags(True, True, False) +nd.setflags(write=True, align=True, uic=False) + +# fill is pretty simple diff --git a/numpy/typing/tests/data/pass/ndarray_misc.py b/numpy/typing/tests/data/pass/ndarray_misc.py new file mode 100644 index 000000000000..62024603c949 --- /dev/null +++ b/numpy/typing/tests/data/pass/ndarray_misc.py @@ -0,0 +1,185 @@ +""" +Tests for miscellaneous (non-magic) ``np.ndarray``/``np.generic`` methods. + +More extensive tests are performed for the methods' +function-based counterpart in `../from_numeric.py`. + +""" + +from __future__ import annotations + +import operator +from typing import cast, Any + +import numpy as np + +class SubClass(np.ndarray): ... + +i4 = np.int32(1) +A: np.ndarray[Any, np.dtype[np.int32]] = np.array([[1]], dtype=np.int32) +B0 = np.empty((), dtype=np.int32).view(SubClass) +B1 = np.empty((1,), dtype=np.int32).view(SubClass) +B2 = np.empty((1, 1), dtype=np.int32).view(SubClass) +C: np.ndarray[Any, np.dtype[np.int32]] = np.array([0, 1, 2], dtype=np.int32) +D = np.empty(3).view(SubClass) + +i4.all() +A.all() +A.all(axis=0) +A.all(keepdims=True) +A.all(out=B0) + +i4.any() +A.any() +A.any(axis=0) +A.any(keepdims=True) +A.any(out=B0) + +i4.argmax() +A.argmax() +A.argmax(axis=0) +A.argmax(out=B0) + +i4.argmin() +A.argmin() +A.argmin(axis=0) +A.argmin(out=B0) + +i4.argsort() +A.argsort() + +i4.choose([()]) +_choices = np.array([[0, 1, 2], [3, 4, 5], [6, 7, 8]], dtype=np.int32) +C.choose(_choices) +C.choose(_choices, out=D) + +i4.clip(1) +A.clip(1) +A.clip(None, 1) +A.clip(1, out=B2) +A.clip(None, 1, out=B2) + +i4.compress([1]) +A.compress([1]) +A.compress([1], out=B1) + +i4.conj() +A.conj() +B0.conj() + +i4.conjugate() +A.conjugate() +B0.conjugate() + +i4.cumprod() +A.cumprod() +A.cumprod(out=B1) + +i4.cumsum() +A.cumsum() +A.cumsum(out=B1) + +i4.max() +A.max() +A.max(axis=0) +A.max(keepdims=True) +A.max(out=B0) + +i4.mean() +A.mean() +A.mean(axis=0) +A.mean(keepdims=True) +A.mean(out=B0) + +i4.min() +A.min() +A.min(axis=0) +A.min(keepdims=True) +A.min(out=B0) + +i4.newbyteorder() +A.newbyteorder() +B0.newbyteorder('|') + +i4.prod() +A.prod() +A.prod(axis=0) +A.prod(keepdims=True) +A.prod(out=B0) + +i4.ptp() +A.ptp() +A.ptp(axis=0) +A.ptp(keepdims=True) +A.astype(int).ptp(out=B0) + +i4.round() +A.round() +A.round(out=B2) + +i4.repeat(1) +A.repeat(1) +B0.repeat(1) + +i4.std() +A.std() +A.std(axis=0) +A.std(keepdims=True) +A.std(out=B0.astype(np.float64)) + +i4.sum() +A.sum() +A.sum(axis=0) +A.sum(keepdims=True) +A.sum(out=B0) + +i4.take(0) +A.take(0) +A.take([0]) +A.take(0, out=B0) +A.take([0], out=B1) + +i4.var() +A.var() +A.var(axis=0) +A.var(keepdims=True) +A.var(out=B0) + +A.argpartition([0]) + +A.diagonal() + +A.dot(1) +A.dot(1, out=B0) + +A.nonzero() + +C.searchsorted(1) + +A.trace() +A.trace(out=B0) + +void = cast(np.void, np.array(1, dtype=[("f", np.float64)]).take(0)) +void.setfield(10, np.float64) + +A.item(0) +C.item(0) + +A.ravel() +C.ravel() + +A.flatten() +C.flatten() + +A.reshape(1) +C.reshape(3) + +int(np.array(1.0, dtype=np.float64)) +int(np.array("1", dtype=np.str_)) + +float(np.array(1.0, dtype=np.float64)) +float(np.array("1", dtype=np.str_)) + +complex(np.array(1.0, dtype=np.float64)) + +operator.index(np.array(1, dtype=np.int64)) diff --git a/numpy/typing/tests/data/pass/ndarray_shape_manipulation.py b/numpy/typing/tests/data/pass/ndarray_shape_manipulation.py new file mode 100644 index 000000000000..0ca3dff392e1 --- /dev/null +++ b/numpy/typing/tests/data/pass/ndarray_shape_manipulation.py @@ -0,0 +1,47 @@ +import numpy as np + +nd1 = np.array([[1, 2], [3, 4]]) + +# reshape +nd1.reshape(4) +nd1.reshape(2, 2) +nd1.reshape((2, 2)) + +nd1.reshape((2, 2), order="C") +nd1.reshape(4, order="C") + +# resize +nd1.resize() +nd1.resize(4) +nd1.resize(2, 2) +nd1.resize((2, 2)) + +nd1.resize((2, 2), refcheck=True) +nd1.resize(4, refcheck=True) + +nd2 = np.array([[1, 2], [3, 4]]) + +# transpose +nd2.transpose() +nd2.transpose(1, 0) +nd2.transpose((1, 0)) + +# swapaxes +nd2.swapaxes(0, 1) + +# flatten +nd2.flatten() +nd2.flatten("C") + +# ravel +nd2.ravel() +nd2.ravel("C") + +# squeeze +nd2.squeeze() + +nd3 = np.array([[1, 2]]) +nd3.squeeze(0) + +nd4 = np.array([[[1, 2]]]) +nd4.squeeze((0, 1)) diff --git a/numpy/typing/tests/data/pass/numeric.py b/numpy/typing/tests/data/pass/numeric.py new file mode 100644 index 000000000000..34fef7270443 --- /dev/null +++ b/numpy/typing/tests/data/pass/numeric.py @@ -0,0 +1,89 @@ +""" +Tests for :mod:`numpy.core.numeric`. + +Does not include tests which fall under ``array_constructors``. + +""" + +from typing import List +import numpy as np + +class SubClass(np.ndarray): + ... + +i8 = np.int64(1) + +A = np.arange(27).reshape(3, 3, 3) +B: List[List[List[int]]] = A.tolist() +C = np.empty((27, 27)).view(SubClass) + +np.count_nonzero(i8) +np.count_nonzero(A) +np.count_nonzero(B) +np.count_nonzero(A, keepdims=True) +np.count_nonzero(A, axis=0) + +np.isfortran(i8) +np.isfortran(A) + +np.argwhere(i8) +np.argwhere(A) + +np.flatnonzero(i8) +np.flatnonzero(A) + +np.correlate(B[0][0], A.ravel(), mode="valid") +np.correlate(A.ravel(), A.ravel(), mode="same") + +np.convolve(B[0][0], A.ravel(), mode="valid") +np.convolve(A.ravel(), A.ravel(), mode="same") + +np.outer(i8, A) +np.outer(B, A) +np.outer(A, A) +np.outer(A, A, out=C) + +np.tensordot(B, A) +np.tensordot(A, A) +np.tensordot(A, A, axes=0) +np.tensordot(A, A, axes=(0, 1)) + +np.isscalar(i8) +np.isscalar(A) +np.isscalar(B) + +np.roll(A, 1) +np.roll(A, (1, 2)) +np.roll(B, 1) + +np.rollaxis(A, 0, 1) + +np.moveaxis(A, 0, 1) +np.moveaxis(A, (0, 1), (1, 2)) + +np.cross(B, A) +np.cross(A, A) + +np.indices([0, 1, 2]) +np.indices([0, 1, 2], sparse=False) +np.indices([0, 1, 2], sparse=True) + +np.binary_repr(1) + +np.base_repr(1) + +np.allclose(i8, A) +np.allclose(B, A) +np.allclose(A, A) + +np.isclose(i8, A) +np.isclose(B, A) +np.isclose(A, A) + +np.array_equal(i8, A) +np.array_equal(B, A) +np.array_equal(A, A) + +np.array_equiv(i8, A) +np.array_equiv(B, A) +np.array_equiv(A, A) diff --git a/numpy/typing/tests/data/pass/numerictypes.py b/numpy/typing/tests/data/pass/numerictypes.py new file mode 100644 index 000000000000..5af0d171ca04 --- /dev/null +++ b/numpy/typing/tests/data/pass/numerictypes.py @@ -0,0 +1,47 @@ +import numpy as np + +np.maximum_sctype("S8") +np.maximum_sctype(object) + +np.issctype(object) +np.issctype("S8") + +np.obj2sctype(list) +np.obj2sctype(list, default=None) +np.obj2sctype(list, default=np.string_) + +np.issubclass_(np.int32, int) +np.issubclass_(np.float64, float) +np.issubclass_(np.float64, (int, float)) + +np.issubsctype("int64", int) +np.issubsctype(np.array([1]), np.array([1])) + +np.issubdtype("S1", np.string_) +np.issubdtype(np.float64, np.float32) + +np.sctype2char("S1") +np.sctype2char(list) + +np.find_common_type([], [np.int64, np.float32, complex]) +np.find_common_type((), (np.int64, np.float32, complex)) +np.find_common_type([np.int64, np.float32], []) +np.find_common_type([np.float32], [np.int64, np.float64]) + +np.cast[int] +np.cast["i8"] +np.cast[np.int64] + +np.nbytes[int] +np.nbytes["i8"] +np.nbytes[np.int64] + +np.ScalarType +np.ScalarType[0] +np.ScalarType[4] +np.ScalarType[9] +np.ScalarType[11] + +np.typecodes["Character"] +np.typecodes["Complex"] +np.typecodes["All"] diff --git a/numpy/typing/tests/data/pass/random.py b/numpy/typing/tests/data/pass/random.py new file mode 100644 index 000000000000..05bd62112ff2 --- /dev/null +++ b/numpy/typing/tests/data/pass/random.py @@ -0,0 +1,1497 @@ +from __future__ import annotations + +from typing import Any, List, Dict + +import numpy as np + +SEED_NONE = None +SEED_INT = 4579435749574957634658964293569 +SEED_ARR: np.ndarray[Any, np.dtype[np.int64]] = np.array([1, 2, 3, 4], dtype=np.int64) +SEED_ARRLIKE: List[int] = [1, 2, 3, 4] +SEED_SEED_SEQ: np.random.SeedSequence = np.random.SeedSequence(0) +SEED_MT19937: np.random.MT19937 = np.random.MT19937(0) +SEED_PCG64: np.random.PCG64 = np.random.PCG64(0) +SEED_PHILOX: np.random.Philox = np.random.Philox(0) +SEED_SFC64: np.random.SFC64 = np.random.SFC64(0) + +# default rng +np.random.default_rng() +np.random.default_rng(SEED_NONE) +np.random.default_rng(SEED_INT) +np.random.default_rng(SEED_ARR) +np.random.default_rng(SEED_ARRLIKE) +np.random.default_rng(SEED_SEED_SEQ) +np.random.default_rng(SEED_MT19937) +np.random.default_rng(SEED_PCG64) +np.random.default_rng(SEED_PHILOX) +np.random.default_rng(SEED_SFC64) + +# Seed Sequence +np.random.SeedSequence(SEED_NONE) +np.random.SeedSequence(SEED_INT) +np.random.SeedSequence(SEED_ARR) +np.random.SeedSequence(SEED_ARRLIKE) + +# Bit Generators +np.random.MT19937(SEED_NONE) +np.random.MT19937(SEED_INT) +np.random.MT19937(SEED_ARR) +np.random.MT19937(SEED_ARRLIKE) +np.random.MT19937(SEED_SEED_SEQ) + +np.random.PCG64(SEED_NONE) +np.random.PCG64(SEED_INT) +np.random.PCG64(SEED_ARR) +np.random.PCG64(SEED_ARRLIKE) +np.random.PCG64(SEED_SEED_SEQ) + +np.random.Philox(SEED_NONE) +np.random.Philox(SEED_INT) +np.random.Philox(SEED_ARR) +np.random.Philox(SEED_ARRLIKE) +np.random.Philox(SEED_SEED_SEQ) + +np.random.SFC64(SEED_NONE) +np.random.SFC64(SEED_INT) +np.random.SFC64(SEED_ARR) +np.random.SFC64(SEED_ARRLIKE) +np.random.SFC64(SEED_SEED_SEQ) + +seed_seq: np.random.bit_generator.SeedSequence = np.random.SeedSequence(SEED_NONE) +seed_seq.spawn(10) +seed_seq.generate_state(3) +seed_seq.generate_state(3, "u4") +seed_seq.generate_state(3, "uint32") +seed_seq.generate_state(3, "u8") +seed_seq.generate_state(3, "uint64") +seed_seq.generate_state(3, np.uint32) +seed_seq.generate_state(3, np.uint64) + + +def_gen: np.random.Generator = np.random.default_rng() + +D_arr_0p1: np.ndarray[Any, np.dtype[np.float64]] = np.array([0.1]) +D_arr_0p5: np.ndarray[Any, np.dtype[np.float64]] = np.array([0.5]) +D_arr_0p9: np.ndarray[Any, np.dtype[np.float64]] = np.array([0.9]) +D_arr_1p5: np.ndarray[Any, np.dtype[np.float64]] = np.array([1.5]) +I_arr_10: np.ndarray[Any, np.dtype[np.int_]] = np.array([10], dtype=np.int_) +I_arr_20: np.ndarray[Any, np.dtype[np.int_]] = np.array([20], dtype=np.int_) +D_arr_like_0p1: List[float] = [0.1] +D_arr_like_0p5: List[float] = [0.5] +D_arr_like_0p9: List[float] = [0.9] +D_arr_like_1p5: List[float] = [1.5] +I_arr_like_10: List[int] = [10] +I_arr_like_20: List[int] = [20] +D_2D_like: List[List[float]] = [[1, 2], [2, 3], [3, 4], [4, 5.1]] +D_2D: np.ndarray[Any, np.dtype[np.float64]] = np.array(D_2D_like) + +S_out: np.ndarray[Any, np.dtype[np.float32]] = np.empty(1, dtype=np.float32) +D_out: np.ndarray[Any, np.dtype[np.float64]] = np.empty(1) + +def_gen.standard_normal() +def_gen.standard_normal(dtype=np.float32) +def_gen.standard_normal(dtype="float32") +def_gen.standard_normal(dtype="double") +def_gen.standard_normal(dtype=np.float64) +def_gen.standard_normal(size=None) +def_gen.standard_normal(size=1) +def_gen.standard_normal(size=1, dtype=np.float32) +def_gen.standard_normal(size=1, dtype="f4") +def_gen.standard_normal(size=1, dtype="float32", out=S_out) +def_gen.standard_normal(dtype=np.float32, out=S_out) +def_gen.standard_normal(size=1, dtype=np.float64) +def_gen.standard_normal(size=1, dtype="float64") +def_gen.standard_normal(size=1, dtype="f8") +def_gen.standard_normal(out=D_out) +def_gen.standard_normal(size=1, dtype="float64") +def_gen.standard_normal(size=1, dtype="float64", out=D_out) + +def_gen.random() +def_gen.random(dtype=np.float32) +def_gen.random(dtype="float32") +def_gen.random(dtype="double") +def_gen.random(dtype=np.float64) +def_gen.random(size=None) +def_gen.random(size=1) +def_gen.random(size=1, dtype=np.float32) +def_gen.random(size=1, dtype="f4") +def_gen.random(size=1, dtype="float32", out=S_out) +def_gen.random(dtype=np.float32, out=S_out) +def_gen.random(size=1, dtype=np.float64) +def_gen.random(size=1, dtype="float64") +def_gen.random(size=1, dtype="f8") +def_gen.random(out=D_out) +def_gen.random(size=1, dtype="float64") +def_gen.random(size=1, dtype="float64", out=D_out) + +def_gen.standard_cauchy() +def_gen.standard_cauchy(size=None) +def_gen.standard_cauchy(size=1) + +def_gen.standard_exponential() +def_gen.standard_exponential(method="inv") +def_gen.standard_exponential(dtype=np.float32) +def_gen.standard_exponential(dtype="float32") +def_gen.standard_exponential(dtype="double") +def_gen.standard_exponential(dtype=np.float64) +def_gen.standard_exponential(size=None) +def_gen.standard_exponential(size=None, method="inv") +def_gen.standard_exponential(size=1, method="inv") +def_gen.standard_exponential(size=1, dtype=np.float32) +def_gen.standard_exponential(size=1, dtype="f4", method="inv") +def_gen.standard_exponential(size=1, dtype="float32", out=S_out) +def_gen.standard_exponential(dtype=np.float32, out=S_out) +def_gen.standard_exponential(size=1, dtype=np.float64, method="inv") +def_gen.standard_exponential(size=1, dtype="float64") +def_gen.standard_exponential(size=1, dtype="f8") +def_gen.standard_exponential(out=D_out) +def_gen.standard_exponential(size=1, dtype="float64") +def_gen.standard_exponential(size=1, dtype="float64", out=D_out) + +def_gen.zipf(1.5) +def_gen.zipf(1.5, size=None) +def_gen.zipf(1.5, size=1) +def_gen.zipf(D_arr_1p5) +def_gen.zipf(D_arr_1p5, size=1) +def_gen.zipf(D_arr_like_1p5) +def_gen.zipf(D_arr_like_1p5, size=1) + +def_gen.weibull(0.5) +def_gen.weibull(0.5, size=None) +def_gen.weibull(0.5, size=1) +def_gen.weibull(D_arr_0p5) +def_gen.weibull(D_arr_0p5, size=1) +def_gen.weibull(D_arr_like_0p5) +def_gen.weibull(D_arr_like_0p5, size=1) + +def_gen.standard_t(0.5) +def_gen.standard_t(0.5, size=None) +def_gen.standard_t(0.5, size=1) +def_gen.standard_t(D_arr_0p5) +def_gen.standard_t(D_arr_0p5, size=1) +def_gen.standard_t(D_arr_like_0p5) +def_gen.standard_t(D_arr_like_0p5, size=1) + +def_gen.poisson(0.5) +def_gen.poisson(0.5, size=None) +def_gen.poisson(0.5, size=1) +def_gen.poisson(D_arr_0p5) +def_gen.poisson(D_arr_0p5, size=1) +def_gen.poisson(D_arr_like_0p5) +def_gen.poisson(D_arr_like_0p5, size=1) + +def_gen.power(0.5) +def_gen.power(0.5, size=None) +def_gen.power(0.5, size=1) +def_gen.power(D_arr_0p5) +def_gen.power(D_arr_0p5, size=1) +def_gen.power(D_arr_like_0p5) +def_gen.power(D_arr_like_0p5, size=1) + +def_gen.pareto(0.5) +def_gen.pareto(0.5, size=None) +def_gen.pareto(0.5, size=1) +def_gen.pareto(D_arr_0p5) +def_gen.pareto(D_arr_0p5, size=1) +def_gen.pareto(D_arr_like_0p5) +def_gen.pareto(D_arr_like_0p5, size=1) + +def_gen.chisquare(0.5) +def_gen.chisquare(0.5, size=None) +def_gen.chisquare(0.5, size=1) +def_gen.chisquare(D_arr_0p5) +def_gen.chisquare(D_arr_0p5, size=1) +def_gen.chisquare(D_arr_like_0p5) +def_gen.chisquare(D_arr_like_0p5, size=1) + +def_gen.exponential(0.5) +def_gen.exponential(0.5, size=None) +def_gen.exponential(0.5, size=1) +def_gen.exponential(D_arr_0p5) +def_gen.exponential(D_arr_0p5, size=1) +def_gen.exponential(D_arr_like_0p5) +def_gen.exponential(D_arr_like_0p5, size=1) + +def_gen.geometric(0.5) +def_gen.geometric(0.5, size=None) +def_gen.geometric(0.5, size=1) +def_gen.geometric(D_arr_0p5) +def_gen.geometric(D_arr_0p5, size=1) +def_gen.geometric(D_arr_like_0p5) +def_gen.geometric(D_arr_like_0p5, size=1) + +def_gen.logseries(0.5) +def_gen.logseries(0.5, size=None) +def_gen.logseries(0.5, size=1) +def_gen.logseries(D_arr_0p5) +def_gen.logseries(D_arr_0p5, size=1) +def_gen.logseries(D_arr_like_0p5) +def_gen.logseries(D_arr_like_0p5, size=1) + +def_gen.rayleigh(0.5) +def_gen.rayleigh(0.5, size=None) +def_gen.rayleigh(0.5, size=1) +def_gen.rayleigh(D_arr_0p5) +def_gen.rayleigh(D_arr_0p5, size=1) +def_gen.rayleigh(D_arr_like_0p5) +def_gen.rayleigh(D_arr_like_0p5, size=1) + +def_gen.standard_gamma(0.5) +def_gen.standard_gamma(0.5, size=None) +def_gen.standard_gamma(0.5, dtype="float32") +def_gen.standard_gamma(0.5, size=None, dtype="float32") +def_gen.standard_gamma(0.5, size=1) +def_gen.standard_gamma(D_arr_0p5) +def_gen.standard_gamma(D_arr_0p5, dtype="f4") +def_gen.standard_gamma(0.5, size=1, dtype="float32", out=S_out) +def_gen.standard_gamma(D_arr_0p5, dtype=np.float32, out=S_out) +def_gen.standard_gamma(D_arr_0p5, size=1) +def_gen.standard_gamma(D_arr_like_0p5) +def_gen.standard_gamma(D_arr_like_0p5, size=1) +def_gen.standard_gamma(0.5, out=D_out) +def_gen.standard_gamma(D_arr_like_0p5, out=D_out) +def_gen.standard_gamma(D_arr_like_0p5, size=1) +def_gen.standard_gamma(D_arr_like_0p5, size=1, out=D_out, dtype=np.float64) + +def_gen.vonmises(0.5, 0.5) +def_gen.vonmises(0.5, 0.5, size=None) +def_gen.vonmises(0.5, 0.5, size=1) +def_gen.vonmises(D_arr_0p5, 0.5) +def_gen.vonmises(0.5, D_arr_0p5) +def_gen.vonmises(D_arr_0p5, 0.5, size=1) +def_gen.vonmises(0.5, D_arr_0p5, size=1) +def_gen.vonmises(D_arr_like_0p5, 0.5) +def_gen.vonmises(0.5, D_arr_like_0p5) +def_gen.vonmises(D_arr_0p5, D_arr_0p5) +def_gen.vonmises(D_arr_like_0p5, D_arr_like_0p5) +def_gen.vonmises(D_arr_0p5, D_arr_0p5, size=1) +def_gen.vonmises(D_arr_like_0p5, D_arr_like_0p5, size=1) + +def_gen.wald(0.5, 0.5) +def_gen.wald(0.5, 0.5, size=None) +def_gen.wald(0.5, 0.5, size=1) +def_gen.wald(D_arr_0p5, 0.5) +def_gen.wald(0.5, D_arr_0p5) +def_gen.wald(D_arr_0p5, 0.5, size=1) +def_gen.wald(0.5, D_arr_0p5, size=1) +def_gen.wald(D_arr_like_0p5, 0.5) +def_gen.wald(0.5, D_arr_like_0p5) +def_gen.wald(D_arr_0p5, D_arr_0p5) +def_gen.wald(D_arr_like_0p5, D_arr_like_0p5) +def_gen.wald(D_arr_0p5, D_arr_0p5, size=1) +def_gen.wald(D_arr_like_0p5, D_arr_like_0p5, size=1) + +def_gen.uniform(0.5, 0.5) +def_gen.uniform(0.5, 0.5, size=None) +def_gen.uniform(0.5, 0.5, size=1) +def_gen.uniform(D_arr_0p5, 0.5) +def_gen.uniform(0.5, D_arr_0p5) +def_gen.uniform(D_arr_0p5, 0.5, size=1) +def_gen.uniform(0.5, D_arr_0p5, size=1) +def_gen.uniform(D_arr_like_0p5, 0.5) +def_gen.uniform(0.5, D_arr_like_0p5) +def_gen.uniform(D_arr_0p5, D_arr_0p5) +def_gen.uniform(D_arr_like_0p5, D_arr_like_0p5) +def_gen.uniform(D_arr_0p5, D_arr_0p5, size=1) +def_gen.uniform(D_arr_like_0p5, D_arr_like_0p5, size=1) + +def_gen.beta(0.5, 0.5) +def_gen.beta(0.5, 0.5, size=None) +def_gen.beta(0.5, 0.5, size=1) +def_gen.beta(D_arr_0p5, 0.5) +def_gen.beta(0.5, D_arr_0p5) +def_gen.beta(D_arr_0p5, 0.5, size=1) +def_gen.beta(0.5, D_arr_0p5, size=1) +def_gen.beta(D_arr_like_0p5, 0.5) +def_gen.beta(0.5, D_arr_like_0p5) +def_gen.beta(D_arr_0p5, D_arr_0p5) +def_gen.beta(D_arr_like_0p5, D_arr_like_0p5) +def_gen.beta(D_arr_0p5, D_arr_0p5, size=1) +def_gen.beta(D_arr_like_0p5, D_arr_like_0p5, size=1) + +def_gen.f(0.5, 0.5) +def_gen.f(0.5, 0.5, size=None) +def_gen.f(0.5, 0.5, size=1) +def_gen.f(D_arr_0p5, 0.5) +def_gen.f(0.5, D_arr_0p5) +def_gen.f(D_arr_0p5, 0.5, size=1) +def_gen.f(0.5, D_arr_0p5, size=1) +def_gen.f(D_arr_like_0p5, 0.5) +def_gen.f(0.5, D_arr_like_0p5) +def_gen.f(D_arr_0p5, D_arr_0p5) +def_gen.f(D_arr_like_0p5, D_arr_like_0p5) +def_gen.f(D_arr_0p5, D_arr_0p5, size=1) +def_gen.f(D_arr_like_0p5, D_arr_like_0p5, size=1) + +def_gen.gamma(0.5, 0.5) +def_gen.gamma(0.5, 0.5, size=None) +def_gen.gamma(0.5, 0.5, size=1) +def_gen.gamma(D_arr_0p5, 0.5) +def_gen.gamma(0.5, D_arr_0p5) +def_gen.gamma(D_arr_0p5, 0.5, size=1) +def_gen.gamma(0.5, D_arr_0p5, size=1) +def_gen.gamma(D_arr_like_0p5, 0.5) +def_gen.gamma(0.5, D_arr_like_0p5) +def_gen.gamma(D_arr_0p5, D_arr_0p5) +def_gen.gamma(D_arr_like_0p5, D_arr_like_0p5) +def_gen.gamma(D_arr_0p5, D_arr_0p5, size=1) +def_gen.gamma(D_arr_like_0p5, D_arr_like_0p5, size=1) + +def_gen.gumbel(0.5, 0.5) +def_gen.gumbel(0.5, 0.5, size=None) +def_gen.gumbel(0.5, 0.5, size=1) +def_gen.gumbel(D_arr_0p5, 0.5) +def_gen.gumbel(0.5, D_arr_0p5) +def_gen.gumbel(D_arr_0p5, 0.5, size=1) +def_gen.gumbel(0.5, D_arr_0p5, size=1) +def_gen.gumbel(D_arr_like_0p5, 0.5) +def_gen.gumbel(0.5, D_arr_like_0p5) +def_gen.gumbel(D_arr_0p5, D_arr_0p5) +def_gen.gumbel(D_arr_like_0p5, D_arr_like_0p5) +def_gen.gumbel(D_arr_0p5, D_arr_0p5, size=1) +def_gen.gumbel(D_arr_like_0p5, D_arr_like_0p5, size=1) + +def_gen.laplace(0.5, 0.5) +def_gen.laplace(0.5, 0.5, size=None) +def_gen.laplace(0.5, 0.5, size=1) +def_gen.laplace(D_arr_0p5, 0.5) +def_gen.laplace(0.5, D_arr_0p5) +def_gen.laplace(D_arr_0p5, 0.5, size=1) +def_gen.laplace(0.5, D_arr_0p5, size=1) +def_gen.laplace(D_arr_like_0p5, 0.5) +def_gen.laplace(0.5, D_arr_like_0p5) +def_gen.laplace(D_arr_0p5, D_arr_0p5) +def_gen.laplace(D_arr_like_0p5, D_arr_like_0p5) +def_gen.laplace(D_arr_0p5, D_arr_0p5, size=1) +def_gen.laplace(D_arr_like_0p5, D_arr_like_0p5, size=1) + +def_gen.logistic(0.5, 0.5) +def_gen.logistic(0.5, 0.5, size=None) +def_gen.logistic(0.5, 0.5, size=1) +def_gen.logistic(D_arr_0p5, 0.5) +def_gen.logistic(0.5, D_arr_0p5) +def_gen.logistic(D_arr_0p5, 0.5, size=1) +def_gen.logistic(0.5, D_arr_0p5, size=1) +def_gen.logistic(D_arr_like_0p5, 0.5) +def_gen.logistic(0.5, D_arr_like_0p5) +def_gen.logistic(D_arr_0p5, D_arr_0p5) +def_gen.logistic(D_arr_like_0p5, D_arr_like_0p5) +def_gen.logistic(D_arr_0p5, D_arr_0p5, size=1) +def_gen.logistic(D_arr_like_0p5, D_arr_like_0p5, size=1) + +def_gen.lognormal(0.5, 0.5) +def_gen.lognormal(0.5, 0.5, size=None) +def_gen.lognormal(0.5, 0.5, size=1) +def_gen.lognormal(D_arr_0p5, 0.5) +def_gen.lognormal(0.5, D_arr_0p5) +def_gen.lognormal(D_arr_0p5, 0.5, size=1) +def_gen.lognormal(0.5, D_arr_0p5, size=1) +def_gen.lognormal(D_arr_like_0p5, 0.5) +def_gen.lognormal(0.5, D_arr_like_0p5) +def_gen.lognormal(D_arr_0p5, D_arr_0p5) +def_gen.lognormal(D_arr_like_0p5, D_arr_like_0p5) +def_gen.lognormal(D_arr_0p5, D_arr_0p5, size=1) +def_gen.lognormal(D_arr_like_0p5, D_arr_like_0p5, size=1) + +def_gen.noncentral_chisquare(0.5, 0.5) +def_gen.noncentral_chisquare(0.5, 0.5, size=None) +def_gen.noncentral_chisquare(0.5, 0.5, size=1) +def_gen.noncentral_chisquare(D_arr_0p5, 0.5) +def_gen.noncentral_chisquare(0.5, D_arr_0p5) +def_gen.noncentral_chisquare(D_arr_0p5, 0.5, size=1) +def_gen.noncentral_chisquare(0.5, D_arr_0p5, size=1) +def_gen.noncentral_chisquare(D_arr_like_0p5, 0.5) +def_gen.noncentral_chisquare(0.5, D_arr_like_0p5) +def_gen.noncentral_chisquare(D_arr_0p5, D_arr_0p5) +def_gen.noncentral_chisquare(D_arr_like_0p5, D_arr_like_0p5) +def_gen.noncentral_chisquare(D_arr_0p5, D_arr_0p5, size=1) +def_gen.noncentral_chisquare(D_arr_like_0p5, D_arr_like_0p5, size=1) + +def_gen.normal(0.5, 0.5) +def_gen.normal(0.5, 0.5, size=None) +def_gen.normal(0.5, 0.5, size=1) +def_gen.normal(D_arr_0p5, 0.5) +def_gen.normal(0.5, D_arr_0p5) +def_gen.normal(D_arr_0p5, 0.5, size=1) +def_gen.normal(0.5, D_arr_0p5, size=1) +def_gen.normal(D_arr_like_0p5, 0.5) +def_gen.normal(0.5, D_arr_like_0p5) +def_gen.normal(D_arr_0p5, D_arr_0p5) +def_gen.normal(D_arr_like_0p5, D_arr_like_0p5) +def_gen.normal(D_arr_0p5, D_arr_0p5, size=1) +def_gen.normal(D_arr_like_0p5, D_arr_like_0p5, size=1) + +def_gen.triangular(0.1, 0.5, 0.9) +def_gen.triangular(0.1, 0.5, 0.9, size=None) +def_gen.triangular(0.1, 0.5, 0.9, size=1) +def_gen.triangular(D_arr_0p1, 0.5, 0.9) +def_gen.triangular(0.1, D_arr_0p5, 0.9) +def_gen.triangular(D_arr_0p1, 0.5, D_arr_like_0p9, size=1) +def_gen.triangular(0.1, D_arr_0p5, 0.9, size=1) +def_gen.triangular(D_arr_like_0p1, 0.5, D_arr_0p9) +def_gen.triangular(0.5, D_arr_like_0p5, 0.9) +def_gen.triangular(D_arr_0p1, D_arr_0p5, 0.9) +def_gen.triangular(D_arr_like_0p1, D_arr_like_0p5, 0.9) +def_gen.triangular(D_arr_0p1, D_arr_0p5, D_arr_0p9, size=1) +def_gen.triangular(D_arr_like_0p1, D_arr_like_0p5, D_arr_like_0p9, size=1) + +def_gen.noncentral_f(0.1, 0.5, 0.9) +def_gen.noncentral_f(0.1, 0.5, 0.9, size=None) +def_gen.noncentral_f(0.1, 0.5, 0.9, size=1) +def_gen.noncentral_f(D_arr_0p1, 0.5, 0.9) +def_gen.noncentral_f(0.1, D_arr_0p5, 0.9) +def_gen.noncentral_f(D_arr_0p1, 0.5, D_arr_like_0p9, size=1) +def_gen.noncentral_f(0.1, D_arr_0p5, 0.9, size=1) +def_gen.noncentral_f(D_arr_like_0p1, 0.5, D_arr_0p9) +def_gen.noncentral_f(0.5, D_arr_like_0p5, 0.9) +def_gen.noncentral_f(D_arr_0p1, D_arr_0p5, 0.9) +def_gen.noncentral_f(D_arr_like_0p1, D_arr_like_0p5, 0.9) +def_gen.noncentral_f(D_arr_0p1, D_arr_0p5, D_arr_0p9, size=1) +def_gen.noncentral_f(D_arr_like_0p1, D_arr_like_0p5, D_arr_like_0p9, size=1) + +def_gen.binomial(10, 0.5) +def_gen.binomial(10, 0.5, size=None) +def_gen.binomial(10, 0.5, size=1) +def_gen.binomial(I_arr_10, 0.5) +def_gen.binomial(10, D_arr_0p5) +def_gen.binomial(I_arr_10, 0.5, size=1) +def_gen.binomial(10, D_arr_0p5, size=1) +def_gen.binomial(I_arr_like_10, 0.5) +def_gen.binomial(10, D_arr_like_0p5) +def_gen.binomial(I_arr_10, D_arr_0p5) +def_gen.binomial(I_arr_like_10, D_arr_like_0p5) +def_gen.binomial(I_arr_10, D_arr_0p5, size=1) +def_gen.binomial(I_arr_like_10, D_arr_like_0p5, size=1) + +def_gen.negative_binomial(10, 0.5) +def_gen.negative_binomial(10, 0.5, size=None) +def_gen.negative_binomial(10, 0.5, size=1) +def_gen.negative_binomial(I_arr_10, 0.5) +def_gen.negative_binomial(10, D_arr_0p5) +def_gen.negative_binomial(I_arr_10, 0.5, size=1) +def_gen.negative_binomial(10, D_arr_0p5, size=1) +def_gen.negative_binomial(I_arr_like_10, 0.5) +def_gen.negative_binomial(10, D_arr_like_0p5) +def_gen.negative_binomial(I_arr_10, D_arr_0p5) +def_gen.negative_binomial(I_arr_like_10, D_arr_like_0p5) +def_gen.negative_binomial(I_arr_10, D_arr_0p5, size=1) +def_gen.negative_binomial(I_arr_like_10, D_arr_like_0p5, size=1) + +def_gen.hypergeometric(20, 20, 10) +def_gen.hypergeometric(20, 20, 10, size=None) +def_gen.hypergeometric(20, 20, 10, size=1) +def_gen.hypergeometric(I_arr_20, 20, 10) +def_gen.hypergeometric(20, I_arr_20, 10) +def_gen.hypergeometric(I_arr_20, 20, I_arr_like_10, size=1) +def_gen.hypergeometric(20, I_arr_20, 10, size=1) +def_gen.hypergeometric(I_arr_like_20, 20, I_arr_10) +def_gen.hypergeometric(20, I_arr_like_20, 10) +def_gen.hypergeometric(I_arr_20, I_arr_20, 10) +def_gen.hypergeometric(I_arr_like_20, I_arr_like_20, 10) +def_gen.hypergeometric(I_arr_20, I_arr_20, I_arr_10, size=1) +def_gen.hypergeometric(I_arr_like_20, I_arr_like_20, I_arr_like_10, size=1) + +I_int64_100: np.ndarray[Any, np.dtype[np.int64]] = np.array([100], dtype=np.int64) + +def_gen.integers(0, 100) +def_gen.integers(100) +def_gen.integers([100]) +def_gen.integers(0, [100]) + +I_bool_low: np.ndarray[Any, np.dtype[np.bool_]] = np.array([0], dtype=np.bool_) +I_bool_low_like: List[int] = [0] +I_bool_high_open: np.ndarray[Any, np.dtype[np.bool_]] = np.array([1], dtype=np.bool_) +I_bool_high_closed: np.ndarray[Any, np.dtype[np.bool_]] = np.array([1], dtype=np.bool_) + +def_gen.integers(2, dtype=bool) +def_gen.integers(0, 2, dtype=bool) +def_gen.integers(1, dtype=bool, endpoint=True) +def_gen.integers(0, 1, dtype=bool, endpoint=True) +def_gen.integers(I_bool_low_like, 1, dtype=bool, endpoint=True) +def_gen.integers(I_bool_high_open, dtype=bool) +def_gen.integers(I_bool_low, I_bool_high_open, dtype=bool) +def_gen.integers(0, I_bool_high_open, dtype=bool) +def_gen.integers(I_bool_high_closed, dtype=bool, endpoint=True) +def_gen.integers(I_bool_low, I_bool_high_closed, dtype=bool, endpoint=True) +def_gen.integers(0, I_bool_high_closed, dtype=bool, endpoint=True) + +def_gen.integers(2, dtype=np.bool_) +def_gen.integers(0, 2, dtype=np.bool_) +def_gen.integers(1, dtype=np.bool_, endpoint=True) +def_gen.integers(0, 1, dtype=np.bool_, endpoint=True) +def_gen.integers(I_bool_low_like, 1, dtype=np.bool_, endpoint=True) +def_gen.integers(I_bool_high_open, dtype=np.bool_) +def_gen.integers(I_bool_low, I_bool_high_open, dtype=np.bool_) +def_gen.integers(0, I_bool_high_open, dtype=np.bool_) +def_gen.integers(I_bool_high_closed, dtype=np.bool_, endpoint=True) +def_gen.integers(I_bool_low, I_bool_high_closed, dtype=np.bool_, endpoint=True) +def_gen.integers(0, I_bool_high_closed, dtype=np.bool_, endpoint=True) + +I_u1_low: np.ndarray[Any, np.dtype[np.uint8]] = np.array([0], dtype=np.uint8) +I_u1_low_like: List[int] = [0] +I_u1_high_open: np.ndarray[Any, np.dtype[np.uint8]] = np.array([255], dtype=np.uint8) +I_u1_high_closed: np.ndarray[Any, np.dtype[np.uint8]] = np.array([255], dtype=np.uint8) + +def_gen.integers(256, dtype="u1") +def_gen.integers(0, 256, dtype="u1") +def_gen.integers(255, dtype="u1", endpoint=True) +def_gen.integers(0, 255, dtype="u1", endpoint=True) +def_gen.integers(I_u1_low_like, 255, dtype="u1", endpoint=True) +def_gen.integers(I_u1_high_open, dtype="u1") +def_gen.integers(I_u1_low, I_u1_high_open, dtype="u1") +def_gen.integers(0, I_u1_high_open, dtype="u1") +def_gen.integers(I_u1_high_closed, dtype="u1", endpoint=True) +def_gen.integers(I_u1_low, I_u1_high_closed, dtype="u1", endpoint=True) +def_gen.integers(0, I_u1_high_closed, dtype="u1", endpoint=True) + +def_gen.integers(256, dtype="uint8") +def_gen.integers(0, 256, dtype="uint8") +def_gen.integers(255, dtype="uint8", endpoint=True) +def_gen.integers(0, 255, dtype="uint8", endpoint=True) +def_gen.integers(I_u1_low_like, 255, dtype="uint8", endpoint=True) +def_gen.integers(I_u1_high_open, dtype="uint8") +def_gen.integers(I_u1_low, I_u1_high_open, dtype="uint8") +def_gen.integers(0, I_u1_high_open, dtype="uint8") +def_gen.integers(I_u1_high_closed, dtype="uint8", endpoint=True) +def_gen.integers(I_u1_low, I_u1_high_closed, dtype="uint8", endpoint=True) +def_gen.integers(0, I_u1_high_closed, dtype="uint8", endpoint=True) + +def_gen.integers(256, dtype=np.uint8) +def_gen.integers(0, 256, dtype=np.uint8) +def_gen.integers(255, dtype=np.uint8, endpoint=True) +def_gen.integers(0, 255, dtype=np.uint8, endpoint=True) +def_gen.integers(I_u1_low_like, 255, dtype=np.uint8, endpoint=True) +def_gen.integers(I_u1_high_open, dtype=np.uint8) +def_gen.integers(I_u1_low, I_u1_high_open, dtype=np.uint8) +def_gen.integers(0, I_u1_high_open, dtype=np.uint8) +def_gen.integers(I_u1_high_closed, dtype=np.uint8, endpoint=True) +def_gen.integers(I_u1_low, I_u1_high_closed, dtype=np.uint8, endpoint=True) +def_gen.integers(0, I_u1_high_closed, dtype=np.uint8, endpoint=True) + +I_u2_low: np.ndarray[Any, np.dtype[np.uint16]] = np.array([0], dtype=np.uint16) +I_u2_low_like: List[int] = [0] +I_u2_high_open: np.ndarray[Any, np.dtype[np.uint16]] = np.array([65535], dtype=np.uint16) +I_u2_high_closed: np.ndarray[Any, np.dtype[np.uint16]] = np.array([65535], dtype=np.uint16) + +def_gen.integers(65536, dtype="u2") +def_gen.integers(0, 65536, dtype="u2") +def_gen.integers(65535, dtype="u2", endpoint=True) +def_gen.integers(0, 65535, dtype="u2", endpoint=True) +def_gen.integers(I_u2_low_like, 65535, dtype="u2", endpoint=True) +def_gen.integers(I_u2_high_open, dtype="u2") +def_gen.integers(I_u2_low, I_u2_high_open, dtype="u2") +def_gen.integers(0, I_u2_high_open, dtype="u2") +def_gen.integers(I_u2_high_closed, dtype="u2", endpoint=True) +def_gen.integers(I_u2_low, I_u2_high_closed, dtype="u2", endpoint=True) +def_gen.integers(0, I_u2_high_closed, dtype="u2", endpoint=True) + +def_gen.integers(65536, dtype="uint16") +def_gen.integers(0, 65536, dtype="uint16") +def_gen.integers(65535, dtype="uint16", endpoint=True) +def_gen.integers(0, 65535, dtype="uint16", endpoint=True) +def_gen.integers(I_u2_low_like, 65535, dtype="uint16", endpoint=True) +def_gen.integers(I_u2_high_open, dtype="uint16") +def_gen.integers(I_u2_low, I_u2_high_open, dtype="uint16") +def_gen.integers(0, I_u2_high_open, dtype="uint16") +def_gen.integers(I_u2_high_closed, dtype="uint16", endpoint=True) +def_gen.integers(I_u2_low, I_u2_high_closed, dtype="uint16", endpoint=True) +def_gen.integers(0, I_u2_high_closed, dtype="uint16", endpoint=True) + +def_gen.integers(65536, dtype=np.uint16) +def_gen.integers(0, 65536, dtype=np.uint16) +def_gen.integers(65535, dtype=np.uint16, endpoint=True) +def_gen.integers(0, 65535, dtype=np.uint16, endpoint=True) +def_gen.integers(I_u2_low_like, 65535, dtype=np.uint16, endpoint=True) +def_gen.integers(I_u2_high_open, dtype=np.uint16) +def_gen.integers(I_u2_low, I_u2_high_open, dtype=np.uint16) +def_gen.integers(0, I_u2_high_open, dtype=np.uint16) +def_gen.integers(I_u2_high_closed, dtype=np.uint16, endpoint=True) +def_gen.integers(I_u2_low, I_u2_high_closed, dtype=np.uint16, endpoint=True) +def_gen.integers(0, I_u2_high_closed, dtype=np.uint16, endpoint=True) + +I_u4_low: np.ndarray[Any, np.dtype[np.uint32]] = np.array([0], dtype=np.uint32) +I_u4_low_like: List[int] = [0] +I_u4_high_open: np.ndarray[Any, np.dtype[np.uint32]] = np.array([4294967295], dtype=np.uint32) +I_u4_high_closed: np.ndarray[Any, np.dtype[np.uint32]] = np.array([4294967295], dtype=np.uint32) + +def_gen.integers(4294967296, dtype="u4") +def_gen.integers(0, 4294967296, dtype="u4") +def_gen.integers(4294967295, dtype="u4", endpoint=True) +def_gen.integers(0, 4294967295, dtype="u4", endpoint=True) +def_gen.integers(I_u4_low_like, 4294967295, dtype="u4", endpoint=True) +def_gen.integers(I_u4_high_open, dtype="u4") +def_gen.integers(I_u4_low, I_u4_high_open, dtype="u4") +def_gen.integers(0, I_u4_high_open, dtype="u4") +def_gen.integers(I_u4_high_closed, dtype="u4", endpoint=True) +def_gen.integers(I_u4_low, I_u4_high_closed, dtype="u4", endpoint=True) +def_gen.integers(0, I_u4_high_closed, dtype="u4", endpoint=True) + +def_gen.integers(4294967296, dtype="uint32") +def_gen.integers(0, 4294967296, dtype="uint32") +def_gen.integers(4294967295, dtype="uint32", endpoint=True) +def_gen.integers(0, 4294967295, dtype="uint32", endpoint=True) +def_gen.integers(I_u4_low_like, 4294967295, dtype="uint32", endpoint=True) +def_gen.integers(I_u4_high_open, dtype="uint32") +def_gen.integers(I_u4_low, I_u4_high_open, dtype="uint32") +def_gen.integers(0, I_u4_high_open, dtype="uint32") +def_gen.integers(I_u4_high_closed, dtype="uint32", endpoint=True) +def_gen.integers(I_u4_low, I_u4_high_closed, dtype="uint32", endpoint=True) +def_gen.integers(0, I_u4_high_closed, dtype="uint32", endpoint=True) + +def_gen.integers(4294967296, dtype=np.uint32) +def_gen.integers(0, 4294967296, dtype=np.uint32) +def_gen.integers(4294967295, dtype=np.uint32, endpoint=True) +def_gen.integers(0, 4294967295, dtype=np.uint32, endpoint=True) +def_gen.integers(I_u4_low_like, 4294967295, dtype=np.uint32, endpoint=True) +def_gen.integers(I_u4_high_open, dtype=np.uint32) +def_gen.integers(I_u4_low, I_u4_high_open, dtype=np.uint32) +def_gen.integers(0, I_u4_high_open, dtype=np.uint32) +def_gen.integers(I_u4_high_closed, dtype=np.uint32, endpoint=True) +def_gen.integers(I_u4_low, I_u4_high_closed, dtype=np.uint32, endpoint=True) +def_gen.integers(0, I_u4_high_closed, dtype=np.uint32, endpoint=True) + +I_u8_low: np.ndarray[Any, np.dtype[np.uint64]] = np.array([0], dtype=np.uint64) +I_u8_low_like: List[int] = [0] +I_u8_high_open: np.ndarray[Any, np.dtype[np.uint64]] = np.array([18446744073709551615], dtype=np.uint64) +I_u8_high_closed: np.ndarray[Any, np.dtype[np.uint64]] = np.array([18446744073709551615], dtype=np.uint64) + +def_gen.integers(18446744073709551616, dtype="u8") +def_gen.integers(0, 18446744073709551616, dtype="u8") +def_gen.integers(18446744073709551615, dtype="u8", endpoint=True) +def_gen.integers(0, 18446744073709551615, dtype="u8", endpoint=True) +def_gen.integers(I_u8_low_like, 18446744073709551615, dtype="u8", endpoint=True) +def_gen.integers(I_u8_high_open, dtype="u8") +def_gen.integers(I_u8_low, I_u8_high_open, dtype="u8") +def_gen.integers(0, I_u8_high_open, dtype="u8") +def_gen.integers(I_u8_high_closed, dtype="u8", endpoint=True) +def_gen.integers(I_u8_low, I_u8_high_closed, dtype="u8", endpoint=True) +def_gen.integers(0, I_u8_high_closed, dtype="u8", endpoint=True) + +def_gen.integers(18446744073709551616, dtype="uint64") +def_gen.integers(0, 18446744073709551616, dtype="uint64") +def_gen.integers(18446744073709551615, dtype="uint64", endpoint=True) +def_gen.integers(0, 18446744073709551615, dtype="uint64", endpoint=True) +def_gen.integers(I_u8_low_like, 18446744073709551615, dtype="uint64", endpoint=True) +def_gen.integers(I_u8_high_open, dtype="uint64") +def_gen.integers(I_u8_low, I_u8_high_open, dtype="uint64") +def_gen.integers(0, I_u8_high_open, dtype="uint64") +def_gen.integers(I_u8_high_closed, dtype="uint64", endpoint=True) +def_gen.integers(I_u8_low, I_u8_high_closed, dtype="uint64", endpoint=True) +def_gen.integers(0, I_u8_high_closed, dtype="uint64", endpoint=True) + +def_gen.integers(18446744073709551616, dtype=np.uint64) +def_gen.integers(0, 18446744073709551616, dtype=np.uint64) +def_gen.integers(18446744073709551615, dtype=np.uint64, endpoint=True) +def_gen.integers(0, 18446744073709551615, dtype=np.uint64, endpoint=True) +def_gen.integers(I_u8_low_like, 18446744073709551615, dtype=np.uint64, endpoint=True) +def_gen.integers(I_u8_high_open, dtype=np.uint64) +def_gen.integers(I_u8_low, I_u8_high_open, dtype=np.uint64) +def_gen.integers(0, I_u8_high_open, dtype=np.uint64) +def_gen.integers(I_u8_high_closed, dtype=np.uint64, endpoint=True) +def_gen.integers(I_u8_low, I_u8_high_closed, dtype=np.uint64, endpoint=True) +def_gen.integers(0, I_u8_high_closed, dtype=np.uint64, endpoint=True) + +I_i1_low: np.ndarray[Any, np.dtype[np.int8]] = np.array([-128], dtype=np.int8) +I_i1_low_like: List[int] = [-128] +I_i1_high_open: np.ndarray[Any, np.dtype[np.int8]] = np.array([127], dtype=np.int8) +I_i1_high_closed: np.ndarray[Any, np.dtype[np.int8]] = np.array([127], dtype=np.int8) + +def_gen.integers(128, dtype="i1") +def_gen.integers(-128, 128, dtype="i1") +def_gen.integers(127, dtype="i1", endpoint=True) +def_gen.integers(-128, 127, dtype="i1", endpoint=True) +def_gen.integers(I_i1_low_like, 127, dtype="i1", endpoint=True) +def_gen.integers(I_i1_high_open, dtype="i1") +def_gen.integers(I_i1_low, I_i1_high_open, dtype="i1") +def_gen.integers(-128, I_i1_high_open, dtype="i1") +def_gen.integers(I_i1_high_closed, dtype="i1", endpoint=True) +def_gen.integers(I_i1_low, I_i1_high_closed, dtype="i1", endpoint=True) +def_gen.integers(-128, I_i1_high_closed, dtype="i1", endpoint=True) + +def_gen.integers(128, dtype="int8") +def_gen.integers(-128, 128, dtype="int8") +def_gen.integers(127, dtype="int8", endpoint=True) +def_gen.integers(-128, 127, dtype="int8", endpoint=True) +def_gen.integers(I_i1_low_like, 127, dtype="int8", endpoint=True) +def_gen.integers(I_i1_high_open, dtype="int8") +def_gen.integers(I_i1_low, I_i1_high_open, dtype="int8") +def_gen.integers(-128, I_i1_high_open, dtype="int8") +def_gen.integers(I_i1_high_closed, dtype="int8", endpoint=True) +def_gen.integers(I_i1_low, I_i1_high_closed, dtype="int8", endpoint=True) +def_gen.integers(-128, I_i1_high_closed, dtype="int8", endpoint=True) + +def_gen.integers(128, dtype=np.int8) +def_gen.integers(-128, 128, dtype=np.int8) +def_gen.integers(127, dtype=np.int8, endpoint=True) +def_gen.integers(-128, 127, dtype=np.int8, endpoint=True) +def_gen.integers(I_i1_low_like, 127, dtype=np.int8, endpoint=True) +def_gen.integers(I_i1_high_open, dtype=np.int8) +def_gen.integers(I_i1_low, I_i1_high_open, dtype=np.int8) +def_gen.integers(-128, I_i1_high_open, dtype=np.int8) +def_gen.integers(I_i1_high_closed, dtype=np.int8, endpoint=True) +def_gen.integers(I_i1_low, I_i1_high_closed, dtype=np.int8, endpoint=True) +def_gen.integers(-128, I_i1_high_closed, dtype=np.int8, endpoint=True) + +I_i2_low: np.ndarray[Any, np.dtype[np.int16]] = np.array([-32768], dtype=np.int16) +I_i2_low_like: List[int] = [-32768] +I_i2_high_open: np.ndarray[Any, np.dtype[np.int16]] = np.array([32767], dtype=np.int16) +I_i2_high_closed: np.ndarray[Any, np.dtype[np.int16]] = np.array([32767], dtype=np.int16) + +def_gen.integers(32768, dtype="i2") +def_gen.integers(-32768, 32768, dtype="i2") +def_gen.integers(32767, dtype="i2", endpoint=True) +def_gen.integers(-32768, 32767, dtype="i2", endpoint=True) +def_gen.integers(I_i2_low_like, 32767, dtype="i2", endpoint=True) +def_gen.integers(I_i2_high_open, dtype="i2") +def_gen.integers(I_i2_low, I_i2_high_open, dtype="i2") +def_gen.integers(-32768, I_i2_high_open, dtype="i2") +def_gen.integers(I_i2_high_closed, dtype="i2", endpoint=True) +def_gen.integers(I_i2_low, I_i2_high_closed, dtype="i2", endpoint=True) +def_gen.integers(-32768, I_i2_high_closed, dtype="i2", endpoint=True) + +def_gen.integers(32768, dtype="int16") +def_gen.integers(-32768, 32768, dtype="int16") +def_gen.integers(32767, dtype="int16", endpoint=True) +def_gen.integers(-32768, 32767, dtype="int16", endpoint=True) +def_gen.integers(I_i2_low_like, 32767, dtype="int16", endpoint=True) +def_gen.integers(I_i2_high_open, dtype="int16") +def_gen.integers(I_i2_low, I_i2_high_open, dtype="int16") +def_gen.integers(-32768, I_i2_high_open, dtype="int16") +def_gen.integers(I_i2_high_closed, dtype="int16", endpoint=True) +def_gen.integers(I_i2_low, I_i2_high_closed, dtype="int16", endpoint=True) +def_gen.integers(-32768, I_i2_high_closed, dtype="int16", endpoint=True) + +def_gen.integers(32768, dtype=np.int16) +def_gen.integers(-32768, 32768, dtype=np.int16) +def_gen.integers(32767, dtype=np.int16, endpoint=True) +def_gen.integers(-32768, 32767, dtype=np.int16, endpoint=True) +def_gen.integers(I_i2_low_like, 32767, dtype=np.int16, endpoint=True) +def_gen.integers(I_i2_high_open, dtype=np.int16) +def_gen.integers(I_i2_low, I_i2_high_open, dtype=np.int16) +def_gen.integers(-32768, I_i2_high_open, dtype=np.int16) +def_gen.integers(I_i2_high_closed, dtype=np.int16, endpoint=True) +def_gen.integers(I_i2_low, I_i2_high_closed, dtype=np.int16, endpoint=True) +def_gen.integers(-32768, I_i2_high_closed, dtype=np.int16, endpoint=True) + +I_i4_low: np.ndarray[Any, np.dtype[np.int32]] = np.array([-2147483648], dtype=np.int32) +I_i4_low_like: List[int] = [-2147483648] +I_i4_high_open: np.ndarray[Any, np.dtype[np.int32]] = np.array([2147483647], dtype=np.int32) +I_i4_high_closed: np.ndarray[Any, np.dtype[np.int32]] = np.array([2147483647], dtype=np.int32) + +def_gen.integers(2147483648, dtype="i4") +def_gen.integers(-2147483648, 2147483648, dtype="i4") +def_gen.integers(2147483647, dtype="i4", endpoint=True) +def_gen.integers(-2147483648, 2147483647, dtype="i4", endpoint=True) +def_gen.integers(I_i4_low_like, 2147483647, dtype="i4", endpoint=True) +def_gen.integers(I_i4_high_open, dtype="i4") +def_gen.integers(I_i4_low, I_i4_high_open, dtype="i4") +def_gen.integers(-2147483648, I_i4_high_open, dtype="i4") +def_gen.integers(I_i4_high_closed, dtype="i4", endpoint=True) +def_gen.integers(I_i4_low, I_i4_high_closed, dtype="i4", endpoint=True) +def_gen.integers(-2147483648, I_i4_high_closed, dtype="i4", endpoint=True) + +def_gen.integers(2147483648, dtype="int32") +def_gen.integers(-2147483648, 2147483648, dtype="int32") +def_gen.integers(2147483647, dtype="int32", endpoint=True) +def_gen.integers(-2147483648, 2147483647, dtype="int32", endpoint=True) +def_gen.integers(I_i4_low_like, 2147483647, dtype="int32", endpoint=True) +def_gen.integers(I_i4_high_open, dtype="int32") +def_gen.integers(I_i4_low, I_i4_high_open, dtype="int32") +def_gen.integers(-2147483648, I_i4_high_open, dtype="int32") +def_gen.integers(I_i4_high_closed, dtype="int32", endpoint=True) +def_gen.integers(I_i4_low, I_i4_high_closed, dtype="int32", endpoint=True) +def_gen.integers(-2147483648, I_i4_high_closed, dtype="int32", endpoint=True) + +def_gen.integers(2147483648, dtype=np.int32) +def_gen.integers(-2147483648, 2147483648, dtype=np.int32) +def_gen.integers(2147483647, dtype=np.int32, endpoint=True) +def_gen.integers(-2147483648, 2147483647, dtype=np.int32, endpoint=True) +def_gen.integers(I_i4_low_like, 2147483647, dtype=np.int32, endpoint=True) +def_gen.integers(I_i4_high_open, dtype=np.int32) +def_gen.integers(I_i4_low, I_i4_high_open, dtype=np.int32) +def_gen.integers(-2147483648, I_i4_high_open, dtype=np.int32) +def_gen.integers(I_i4_high_closed, dtype=np.int32, endpoint=True) +def_gen.integers(I_i4_low, I_i4_high_closed, dtype=np.int32, endpoint=True) +def_gen.integers(-2147483648, I_i4_high_closed, dtype=np.int32, endpoint=True) + +I_i8_low: np.ndarray[Any, np.dtype[np.int64]] = np.array([-9223372036854775808], dtype=np.int64) +I_i8_low_like: List[int] = [-9223372036854775808] +I_i8_high_open: np.ndarray[Any, np.dtype[np.int64]] = np.array([9223372036854775807], dtype=np.int64) +I_i8_high_closed: np.ndarray[Any, np.dtype[np.int64]] = np.array([9223372036854775807], dtype=np.int64) + +def_gen.integers(9223372036854775808, dtype="i8") +def_gen.integers(-9223372036854775808, 9223372036854775808, dtype="i8") +def_gen.integers(9223372036854775807, dtype="i8", endpoint=True) +def_gen.integers(-9223372036854775808, 9223372036854775807, dtype="i8", endpoint=True) +def_gen.integers(I_i8_low_like, 9223372036854775807, dtype="i8", endpoint=True) +def_gen.integers(I_i8_high_open, dtype="i8") +def_gen.integers(I_i8_low, I_i8_high_open, dtype="i8") +def_gen.integers(-9223372036854775808, I_i8_high_open, dtype="i8") +def_gen.integers(I_i8_high_closed, dtype="i8", endpoint=True) +def_gen.integers(I_i8_low, I_i8_high_closed, dtype="i8", endpoint=True) +def_gen.integers(-9223372036854775808, I_i8_high_closed, dtype="i8", endpoint=True) + +def_gen.integers(9223372036854775808, dtype="int64") +def_gen.integers(-9223372036854775808, 9223372036854775808, dtype="int64") +def_gen.integers(9223372036854775807, dtype="int64", endpoint=True) +def_gen.integers(-9223372036854775808, 9223372036854775807, dtype="int64", endpoint=True) +def_gen.integers(I_i8_low_like, 9223372036854775807, dtype="int64", endpoint=True) +def_gen.integers(I_i8_high_open, dtype="int64") +def_gen.integers(I_i8_low, I_i8_high_open, dtype="int64") +def_gen.integers(-9223372036854775808, I_i8_high_open, dtype="int64") +def_gen.integers(I_i8_high_closed, dtype="int64", endpoint=True) +def_gen.integers(I_i8_low, I_i8_high_closed, dtype="int64", endpoint=True) +def_gen.integers(-9223372036854775808, I_i8_high_closed, dtype="int64", endpoint=True) + +def_gen.integers(9223372036854775808, dtype=np.int64) +def_gen.integers(-9223372036854775808, 9223372036854775808, dtype=np.int64) +def_gen.integers(9223372036854775807, dtype=np.int64, endpoint=True) +def_gen.integers(-9223372036854775808, 9223372036854775807, dtype=np.int64, endpoint=True) +def_gen.integers(I_i8_low_like, 9223372036854775807, dtype=np.int64, endpoint=True) +def_gen.integers(I_i8_high_open, dtype=np.int64) +def_gen.integers(I_i8_low, I_i8_high_open, dtype=np.int64) +def_gen.integers(-9223372036854775808, I_i8_high_open, dtype=np.int64) +def_gen.integers(I_i8_high_closed, dtype=np.int64, endpoint=True) +def_gen.integers(I_i8_low, I_i8_high_closed, dtype=np.int64, endpoint=True) +def_gen.integers(-9223372036854775808, I_i8_high_closed, dtype=np.int64, endpoint=True) + + +def_gen.bit_generator + +def_gen.bytes(2) + +def_gen.choice(5) +def_gen.choice(5, 3) +def_gen.choice(5, 3, replace=True) +def_gen.choice(5, 3, p=[1 / 5] * 5) +def_gen.choice(5, 3, p=[1 / 5] * 5, replace=False) + +def_gen.choice(["pooh", "rabbit", "piglet", "Christopher"]) +def_gen.choice(["pooh", "rabbit", "piglet", "Christopher"], 3) +def_gen.choice(["pooh", "rabbit", "piglet", "Christopher"], 3, p=[1 / 4] * 4) +def_gen.choice(["pooh", "rabbit", "piglet", "Christopher"], 3, replace=True) +def_gen.choice(["pooh", "rabbit", "piglet", "Christopher"], 3, replace=False, p=np.array([1 / 8, 1 / 8, 1 / 2, 1 / 4])) + +def_gen.dirichlet([0.5, 0.5]) +def_gen.dirichlet(np.array([0.5, 0.5])) +def_gen.dirichlet(np.array([0.5, 0.5]), size=3) + +def_gen.multinomial(20, [1 / 6.0] * 6) +def_gen.multinomial(20, np.array([0.5, 0.5])) +def_gen.multinomial(20, [1 / 6.0] * 6, size=2) +def_gen.multinomial([[10], [20]], [1 / 6.0] * 6, size=(2, 2)) +def_gen.multinomial(np.array([[10], [20]]), np.array([0.5, 0.5]), size=(2, 2)) + +def_gen.multivariate_hypergeometric([3, 5, 7], 2) +def_gen.multivariate_hypergeometric(np.array([3, 5, 7]), 2) +def_gen.multivariate_hypergeometric(np.array([3, 5, 7]), 2, size=4) +def_gen.multivariate_hypergeometric(np.array([3, 5, 7]), 2, size=(4, 7)) +def_gen.multivariate_hypergeometric([3, 5, 7], 2, method="count") +def_gen.multivariate_hypergeometric(np.array([3, 5, 7]), 2, method="marginals") + +def_gen.multivariate_normal([0.0], [[1.0]]) +def_gen.multivariate_normal([0.0], np.array([[1.0]])) +def_gen.multivariate_normal(np.array([0.0]), [[1.0]]) +def_gen.multivariate_normal([0.0], np.array([[1.0]])) + +def_gen.permutation(10) +def_gen.permutation([1, 2, 3, 4]) +def_gen.permutation(np.array([1, 2, 3, 4])) +def_gen.permutation(D_2D, axis=1) +def_gen.permuted(D_2D) +def_gen.permuted(D_2D_like) +def_gen.permuted(D_2D, axis=1) +def_gen.permuted(D_2D, out=D_2D) +def_gen.permuted(D_2D_like, out=D_2D) +def_gen.permuted(D_2D_like, out=D_2D) +def_gen.permuted(D_2D, axis=1, out=D_2D) + +def_gen.shuffle(np.arange(10)) +def_gen.shuffle([1, 2, 3, 4, 5]) +def_gen.shuffle(D_2D, axis=1) + +def_gen.__str__() +def_gen.__repr__() +def_gen_state: Dict[str, Any] +def_gen_state = def_gen.__getstate__() +def_gen.__setstate__(def_gen_state) + +# RandomState +random_st: np.random.RandomState = np.random.RandomState() + +random_st.standard_normal() +random_st.standard_normal(size=None) +random_st.standard_normal(size=1) + +random_st.random() +random_st.random(size=None) +random_st.random(size=1) + +random_st.standard_cauchy() +random_st.standard_cauchy(size=None) +random_st.standard_cauchy(size=1) + +random_st.standard_exponential() +random_st.standard_exponential(size=None) +random_st.standard_exponential(size=1) + +random_st.zipf(1.5) +random_st.zipf(1.5, size=None) +random_st.zipf(1.5, size=1) +random_st.zipf(D_arr_1p5) +random_st.zipf(D_arr_1p5, size=1) +random_st.zipf(D_arr_like_1p5) +random_st.zipf(D_arr_like_1p5, size=1) + +random_st.weibull(0.5) +random_st.weibull(0.5, size=None) +random_st.weibull(0.5, size=1) +random_st.weibull(D_arr_0p5) +random_st.weibull(D_arr_0p5, size=1) +random_st.weibull(D_arr_like_0p5) +random_st.weibull(D_arr_like_0p5, size=1) + +random_st.standard_t(0.5) +random_st.standard_t(0.5, size=None) +random_st.standard_t(0.5, size=1) +random_st.standard_t(D_arr_0p5) +random_st.standard_t(D_arr_0p5, size=1) +random_st.standard_t(D_arr_like_0p5) +random_st.standard_t(D_arr_like_0p5, size=1) + +random_st.poisson(0.5) +random_st.poisson(0.5, size=None) +random_st.poisson(0.5, size=1) +random_st.poisson(D_arr_0p5) +random_st.poisson(D_arr_0p5, size=1) +random_st.poisson(D_arr_like_0p5) +random_st.poisson(D_arr_like_0p5, size=1) + +random_st.power(0.5) +random_st.power(0.5, size=None) +random_st.power(0.5, size=1) +random_st.power(D_arr_0p5) +random_st.power(D_arr_0p5, size=1) +random_st.power(D_arr_like_0p5) +random_st.power(D_arr_like_0p5, size=1) + +random_st.pareto(0.5) +random_st.pareto(0.5, size=None) +random_st.pareto(0.5, size=1) +random_st.pareto(D_arr_0p5) +random_st.pareto(D_arr_0p5, size=1) +random_st.pareto(D_arr_like_0p5) +random_st.pareto(D_arr_like_0p5, size=1) + +random_st.chisquare(0.5) +random_st.chisquare(0.5, size=None) +random_st.chisquare(0.5, size=1) +random_st.chisquare(D_arr_0p5) +random_st.chisquare(D_arr_0p5, size=1) +random_st.chisquare(D_arr_like_0p5) +random_st.chisquare(D_arr_like_0p5, size=1) + +random_st.exponential(0.5) +random_st.exponential(0.5, size=None) +random_st.exponential(0.5, size=1) +random_st.exponential(D_arr_0p5) +random_st.exponential(D_arr_0p5, size=1) +random_st.exponential(D_arr_like_0p5) +random_st.exponential(D_arr_like_0p5, size=1) + +random_st.geometric(0.5) +random_st.geometric(0.5, size=None) +random_st.geometric(0.5, size=1) +random_st.geometric(D_arr_0p5) +random_st.geometric(D_arr_0p5, size=1) +random_st.geometric(D_arr_like_0p5) +random_st.geometric(D_arr_like_0p5, size=1) + +random_st.logseries(0.5) +random_st.logseries(0.5, size=None) +random_st.logseries(0.5, size=1) +random_st.logseries(D_arr_0p5) +random_st.logseries(D_arr_0p5, size=1) +random_st.logseries(D_arr_like_0p5) +random_st.logseries(D_arr_like_0p5, size=1) + +random_st.rayleigh(0.5) +random_st.rayleigh(0.5, size=None) +random_st.rayleigh(0.5, size=1) +random_st.rayleigh(D_arr_0p5) +random_st.rayleigh(D_arr_0p5, size=1) +random_st.rayleigh(D_arr_like_0p5) +random_st.rayleigh(D_arr_like_0p5, size=1) + +random_st.standard_gamma(0.5) +random_st.standard_gamma(0.5, size=None) +random_st.standard_gamma(0.5, size=1) +random_st.standard_gamma(D_arr_0p5) +random_st.standard_gamma(D_arr_0p5, size=1) +random_st.standard_gamma(D_arr_like_0p5) +random_st.standard_gamma(D_arr_like_0p5, size=1) +random_st.standard_gamma(D_arr_like_0p5, size=1) + +random_st.vonmises(0.5, 0.5) +random_st.vonmises(0.5, 0.5, size=None) +random_st.vonmises(0.5, 0.5, size=1) +random_st.vonmises(D_arr_0p5, 0.5) +random_st.vonmises(0.5, D_arr_0p5) +random_st.vonmises(D_arr_0p5, 0.5, size=1) +random_st.vonmises(0.5, D_arr_0p5, size=1) +random_st.vonmises(D_arr_like_0p5, 0.5) +random_st.vonmises(0.5, D_arr_like_0p5) +random_st.vonmises(D_arr_0p5, D_arr_0p5) +random_st.vonmises(D_arr_like_0p5, D_arr_like_0p5) +random_st.vonmises(D_arr_0p5, D_arr_0p5, size=1) +random_st.vonmises(D_arr_like_0p5, D_arr_like_0p5, size=1) + +random_st.wald(0.5, 0.5) +random_st.wald(0.5, 0.5, size=None) +random_st.wald(0.5, 0.5, size=1) +random_st.wald(D_arr_0p5, 0.5) +random_st.wald(0.5, D_arr_0p5) +random_st.wald(D_arr_0p5, 0.5, size=1) +random_st.wald(0.5, D_arr_0p5, size=1) +random_st.wald(D_arr_like_0p5, 0.5) +random_st.wald(0.5, D_arr_like_0p5) +random_st.wald(D_arr_0p5, D_arr_0p5) +random_st.wald(D_arr_like_0p5, D_arr_like_0p5) +random_st.wald(D_arr_0p5, D_arr_0p5, size=1) +random_st.wald(D_arr_like_0p5, D_arr_like_0p5, size=1) + +random_st.uniform(0.5, 0.5) +random_st.uniform(0.5, 0.5, size=None) +random_st.uniform(0.5, 0.5, size=1) +random_st.uniform(D_arr_0p5, 0.5) +random_st.uniform(0.5, D_arr_0p5) +random_st.uniform(D_arr_0p5, 0.5, size=1) +random_st.uniform(0.5, D_arr_0p5, size=1) +random_st.uniform(D_arr_like_0p5, 0.5) +random_st.uniform(0.5, D_arr_like_0p5) +random_st.uniform(D_arr_0p5, D_arr_0p5) +random_st.uniform(D_arr_like_0p5, D_arr_like_0p5) +random_st.uniform(D_arr_0p5, D_arr_0p5, size=1) +random_st.uniform(D_arr_like_0p5, D_arr_like_0p5, size=1) + +random_st.beta(0.5, 0.5) +random_st.beta(0.5, 0.5, size=None) +random_st.beta(0.5, 0.5, size=1) +random_st.beta(D_arr_0p5, 0.5) +random_st.beta(0.5, D_arr_0p5) +random_st.beta(D_arr_0p5, 0.5, size=1) +random_st.beta(0.5, D_arr_0p5, size=1) +random_st.beta(D_arr_like_0p5, 0.5) +random_st.beta(0.5, D_arr_like_0p5) +random_st.beta(D_arr_0p5, D_arr_0p5) +random_st.beta(D_arr_like_0p5, D_arr_like_0p5) +random_st.beta(D_arr_0p5, D_arr_0p5, size=1) +random_st.beta(D_arr_like_0p5, D_arr_like_0p5, size=1) + +random_st.f(0.5, 0.5) +random_st.f(0.5, 0.5, size=None) +random_st.f(0.5, 0.5, size=1) +random_st.f(D_arr_0p5, 0.5) +random_st.f(0.5, D_arr_0p5) +random_st.f(D_arr_0p5, 0.5, size=1) +random_st.f(0.5, D_arr_0p5, size=1) +random_st.f(D_arr_like_0p5, 0.5) +random_st.f(0.5, D_arr_like_0p5) +random_st.f(D_arr_0p5, D_arr_0p5) +random_st.f(D_arr_like_0p5, D_arr_like_0p5) +random_st.f(D_arr_0p5, D_arr_0p5, size=1) +random_st.f(D_arr_like_0p5, D_arr_like_0p5, size=1) + +random_st.gamma(0.5, 0.5) +random_st.gamma(0.5, 0.5, size=None) +random_st.gamma(0.5, 0.5, size=1) +random_st.gamma(D_arr_0p5, 0.5) +random_st.gamma(0.5, D_arr_0p5) +random_st.gamma(D_arr_0p5, 0.5, size=1) +random_st.gamma(0.5, D_arr_0p5, size=1) +random_st.gamma(D_arr_like_0p5, 0.5) +random_st.gamma(0.5, D_arr_like_0p5) +random_st.gamma(D_arr_0p5, D_arr_0p5) +random_st.gamma(D_arr_like_0p5, D_arr_like_0p5) +random_st.gamma(D_arr_0p5, D_arr_0p5, size=1) +random_st.gamma(D_arr_like_0p5, D_arr_like_0p5, size=1) + +random_st.gumbel(0.5, 0.5) +random_st.gumbel(0.5, 0.5, size=None) +random_st.gumbel(0.5, 0.5, size=1) +random_st.gumbel(D_arr_0p5, 0.5) +random_st.gumbel(0.5, D_arr_0p5) +random_st.gumbel(D_arr_0p5, 0.5, size=1) +random_st.gumbel(0.5, D_arr_0p5, size=1) +random_st.gumbel(D_arr_like_0p5, 0.5) +random_st.gumbel(0.5, D_arr_like_0p5) +random_st.gumbel(D_arr_0p5, D_arr_0p5) +random_st.gumbel(D_arr_like_0p5, D_arr_like_0p5) +random_st.gumbel(D_arr_0p5, D_arr_0p5, size=1) +random_st.gumbel(D_arr_like_0p5, D_arr_like_0p5, size=1) + +random_st.laplace(0.5, 0.5) +random_st.laplace(0.5, 0.5, size=None) +random_st.laplace(0.5, 0.5, size=1) +random_st.laplace(D_arr_0p5, 0.5) +random_st.laplace(0.5, D_arr_0p5) +random_st.laplace(D_arr_0p5, 0.5, size=1) +random_st.laplace(0.5, D_arr_0p5, size=1) +random_st.laplace(D_arr_like_0p5, 0.5) +random_st.laplace(0.5, D_arr_like_0p5) +random_st.laplace(D_arr_0p5, D_arr_0p5) +random_st.laplace(D_arr_like_0p5, D_arr_like_0p5) +random_st.laplace(D_arr_0p5, D_arr_0p5, size=1) +random_st.laplace(D_arr_like_0p5, D_arr_like_0p5, size=1) + +random_st.logistic(0.5, 0.5) +random_st.logistic(0.5, 0.5, size=None) +random_st.logistic(0.5, 0.5, size=1) +random_st.logistic(D_arr_0p5, 0.5) +random_st.logistic(0.5, D_arr_0p5) +random_st.logistic(D_arr_0p5, 0.5, size=1) +random_st.logistic(0.5, D_arr_0p5, size=1) +random_st.logistic(D_arr_like_0p5, 0.5) +random_st.logistic(0.5, D_arr_like_0p5) +random_st.logistic(D_arr_0p5, D_arr_0p5) +random_st.logistic(D_arr_like_0p5, D_arr_like_0p5) +random_st.logistic(D_arr_0p5, D_arr_0p5, size=1) +random_st.logistic(D_arr_like_0p5, D_arr_like_0p5, size=1) + +random_st.lognormal(0.5, 0.5) +random_st.lognormal(0.5, 0.5, size=None) +random_st.lognormal(0.5, 0.5, size=1) +random_st.lognormal(D_arr_0p5, 0.5) +random_st.lognormal(0.5, D_arr_0p5) +random_st.lognormal(D_arr_0p5, 0.5, size=1) +random_st.lognormal(0.5, D_arr_0p5, size=1) +random_st.lognormal(D_arr_like_0p5, 0.5) +random_st.lognormal(0.5, D_arr_like_0p5) +random_st.lognormal(D_arr_0p5, D_arr_0p5) +random_st.lognormal(D_arr_like_0p5, D_arr_like_0p5) +random_st.lognormal(D_arr_0p5, D_arr_0p5, size=1) +random_st.lognormal(D_arr_like_0p5, D_arr_like_0p5, size=1) + +random_st.noncentral_chisquare(0.5, 0.5) +random_st.noncentral_chisquare(0.5, 0.5, size=None) +random_st.noncentral_chisquare(0.5, 0.5, size=1) +random_st.noncentral_chisquare(D_arr_0p5, 0.5) +random_st.noncentral_chisquare(0.5, D_arr_0p5) +random_st.noncentral_chisquare(D_arr_0p5, 0.5, size=1) +random_st.noncentral_chisquare(0.5, D_arr_0p5, size=1) +random_st.noncentral_chisquare(D_arr_like_0p5, 0.5) +random_st.noncentral_chisquare(0.5, D_arr_like_0p5) +random_st.noncentral_chisquare(D_arr_0p5, D_arr_0p5) +random_st.noncentral_chisquare(D_arr_like_0p5, D_arr_like_0p5) +random_st.noncentral_chisquare(D_arr_0p5, D_arr_0p5, size=1) +random_st.noncentral_chisquare(D_arr_like_0p5, D_arr_like_0p5, size=1) + +random_st.normal(0.5, 0.5) +random_st.normal(0.5, 0.5, size=None) +random_st.normal(0.5, 0.5, size=1) +random_st.normal(D_arr_0p5, 0.5) +random_st.normal(0.5, D_arr_0p5) +random_st.normal(D_arr_0p5, 0.5, size=1) +random_st.normal(0.5, D_arr_0p5, size=1) +random_st.normal(D_arr_like_0p5, 0.5) +random_st.normal(0.5, D_arr_like_0p5) +random_st.normal(D_arr_0p5, D_arr_0p5) +random_st.normal(D_arr_like_0p5, D_arr_like_0p5) +random_st.normal(D_arr_0p5, D_arr_0p5, size=1) +random_st.normal(D_arr_like_0p5, D_arr_like_0p5, size=1) + +random_st.triangular(0.1, 0.5, 0.9) +random_st.triangular(0.1, 0.5, 0.9, size=None) +random_st.triangular(0.1, 0.5, 0.9, size=1) +random_st.triangular(D_arr_0p1, 0.5, 0.9) +random_st.triangular(0.1, D_arr_0p5, 0.9) +random_st.triangular(D_arr_0p1, 0.5, D_arr_like_0p9, size=1) +random_st.triangular(0.1, D_arr_0p5, 0.9, size=1) +random_st.triangular(D_arr_like_0p1, 0.5, D_arr_0p9) +random_st.triangular(0.5, D_arr_like_0p5, 0.9) +random_st.triangular(D_arr_0p1, D_arr_0p5, 0.9) +random_st.triangular(D_arr_like_0p1, D_arr_like_0p5, 0.9) +random_st.triangular(D_arr_0p1, D_arr_0p5, D_arr_0p9, size=1) +random_st.triangular(D_arr_like_0p1, D_arr_like_0p5, D_arr_like_0p9, size=1) + +random_st.noncentral_f(0.1, 0.5, 0.9) +random_st.noncentral_f(0.1, 0.5, 0.9, size=None) +random_st.noncentral_f(0.1, 0.5, 0.9, size=1) +random_st.noncentral_f(D_arr_0p1, 0.5, 0.9) +random_st.noncentral_f(0.1, D_arr_0p5, 0.9) +random_st.noncentral_f(D_arr_0p1, 0.5, D_arr_like_0p9, size=1) +random_st.noncentral_f(0.1, D_arr_0p5, 0.9, size=1) +random_st.noncentral_f(D_arr_like_0p1, 0.5, D_arr_0p9) +random_st.noncentral_f(0.5, D_arr_like_0p5, 0.9) +random_st.noncentral_f(D_arr_0p1, D_arr_0p5, 0.9) +random_st.noncentral_f(D_arr_like_0p1, D_arr_like_0p5, 0.9) +random_st.noncentral_f(D_arr_0p1, D_arr_0p5, D_arr_0p9, size=1) +random_st.noncentral_f(D_arr_like_0p1, D_arr_like_0p5, D_arr_like_0p9, size=1) + +random_st.binomial(10, 0.5) +random_st.binomial(10, 0.5, size=None) +random_st.binomial(10, 0.5, size=1) +random_st.binomial(I_arr_10, 0.5) +random_st.binomial(10, D_arr_0p5) +random_st.binomial(I_arr_10, 0.5, size=1) +random_st.binomial(10, D_arr_0p5, size=1) +random_st.binomial(I_arr_like_10, 0.5) +random_st.binomial(10, D_arr_like_0p5) +random_st.binomial(I_arr_10, D_arr_0p5) +random_st.binomial(I_arr_like_10, D_arr_like_0p5) +random_st.binomial(I_arr_10, D_arr_0p5, size=1) +random_st.binomial(I_arr_like_10, D_arr_like_0p5, size=1) + +random_st.negative_binomial(10, 0.5) +random_st.negative_binomial(10, 0.5, size=None) +random_st.negative_binomial(10, 0.5, size=1) +random_st.negative_binomial(I_arr_10, 0.5) +random_st.negative_binomial(10, D_arr_0p5) +random_st.negative_binomial(I_arr_10, 0.5, size=1) +random_st.negative_binomial(10, D_arr_0p5, size=1) +random_st.negative_binomial(I_arr_like_10, 0.5) +random_st.negative_binomial(10, D_arr_like_0p5) +random_st.negative_binomial(I_arr_10, D_arr_0p5) +random_st.negative_binomial(I_arr_like_10, D_arr_like_0p5) +random_st.negative_binomial(I_arr_10, D_arr_0p5, size=1) +random_st.negative_binomial(I_arr_like_10, D_arr_like_0p5, size=1) + +random_st.hypergeometric(20, 20, 10) +random_st.hypergeometric(20, 20, 10, size=None) +random_st.hypergeometric(20, 20, 10, size=1) +random_st.hypergeometric(I_arr_20, 20, 10) +random_st.hypergeometric(20, I_arr_20, 10) +random_st.hypergeometric(I_arr_20, 20, I_arr_like_10, size=1) +random_st.hypergeometric(20, I_arr_20, 10, size=1) +random_st.hypergeometric(I_arr_like_20, 20, I_arr_10) +random_st.hypergeometric(20, I_arr_like_20, 10) +random_st.hypergeometric(I_arr_20, I_arr_20, 10) +random_st.hypergeometric(I_arr_like_20, I_arr_like_20, 10) +random_st.hypergeometric(I_arr_20, I_arr_20, I_arr_10, size=1) +random_st.hypergeometric(I_arr_like_20, I_arr_like_20, I_arr_like_10, size=1) + +random_st.randint(0, 100) +random_st.randint(100) +random_st.randint([100]) +random_st.randint(0, [100]) + +random_st.randint(2, dtype=bool) +random_st.randint(0, 2, dtype=bool) +random_st.randint(I_bool_high_open, dtype=bool) +random_st.randint(I_bool_low, I_bool_high_open, dtype=bool) +random_st.randint(0, I_bool_high_open, dtype=bool) + +random_st.randint(2, dtype=np.bool_) +random_st.randint(0, 2, dtype=np.bool_) +random_st.randint(I_bool_high_open, dtype=np.bool_) +random_st.randint(I_bool_low, I_bool_high_open, dtype=np.bool_) +random_st.randint(0, I_bool_high_open, dtype=np.bool_) + +random_st.randint(256, dtype="u1") +random_st.randint(0, 256, dtype="u1") +random_st.randint(I_u1_high_open, dtype="u1") +random_st.randint(I_u1_low, I_u1_high_open, dtype="u1") +random_st.randint(0, I_u1_high_open, dtype="u1") + +random_st.randint(256, dtype="uint8") +random_st.randint(0, 256, dtype="uint8") +random_st.randint(I_u1_high_open, dtype="uint8") +random_st.randint(I_u1_low, I_u1_high_open, dtype="uint8") +random_st.randint(0, I_u1_high_open, dtype="uint8") + +random_st.randint(256, dtype=np.uint8) +random_st.randint(0, 256, dtype=np.uint8) +random_st.randint(I_u1_high_open, dtype=np.uint8) +random_st.randint(I_u1_low, I_u1_high_open, dtype=np.uint8) +random_st.randint(0, I_u1_high_open, dtype=np.uint8) + +random_st.randint(65536, dtype="u2") +random_st.randint(0, 65536, dtype="u2") +random_st.randint(I_u2_high_open, dtype="u2") +random_st.randint(I_u2_low, I_u2_high_open, dtype="u2") +random_st.randint(0, I_u2_high_open, dtype="u2") + +random_st.randint(65536, dtype="uint16") +random_st.randint(0, 65536, dtype="uint16") +random_st.randint(I_u2_high_open, dtype="uint16") +random_st.randint(I_u2_low, I_u2_high_open, dtype="uint16") +random_st.randint(0, I_u2_high_open, dtype="uint16") + +random_st.randint(65536, dtype=np.uint16) +random_st.randint(0, 65536, dtype=np.uint16) +random_st.randint(I_u2_high_open, dtype=np.uint16) +random_st.randint(I_u2_low, I_u2_high_open, dtype=np.uint16) +random_st.randint(0, I_u2_high_open, dtype=np.uint16) + +random_st.randint(4294967296, dtype="u4") +random_st.randint(0, 4294967296, dtype="u4") +random_st.randint(I_u4_high_open, dtype="u4") +random_st.randint(I_u4_low, I_u4_high_open, dtype="u4") +random_st.randint(0, I_u4_high_open, dtype="u4") + +random_st.randint(4294967296, dtype="uint32") +random_st.randint(0, 4294967296, dtype="uint32") +random_st.randint(I_u4_high_open, dtype="uint32") +random_st.randint(I_u4_low, I_u4_high_open, dtype="uint32") +random_st.randint(0, I_u4_high_open, dtype="uint32") + +random_st.randint(4294967296, dtype=np.uint32) +random_st.randint(0, 4294967296, dtype=np.uint32) +random_st.randint(I_u4_high_open, dtype=np.uint32) +random_st.randint(I_u4_low, I_u4_high_open, dtype=np.uint32) +random_st.randint(0, I_u4_high_open, dtype=np.uint32) + + +random_st.randint(18446744073709551616, dtype="u8") +random_st.randint(0, 18446744073709551616, dtype="u8") +random_st.randint(I_u8_high_open, dtype="u8") +random_st.randint(I_u8_low, I_u8_high_open, dtype="u8") +random_st.randint(0, I_u8_high_open, dtype="u8") + +random_st.randint(18446744073709551616, dtype="uint64") +random_st.randint(0, 18446744073709551616, dtype="uint64") +random_st.randint(I_u8_high_open, dtype="uint64") +random_st.randint(I_u8_low, I_u8_high_open, dtype="uint64") +random_st.randint(0, I_u8_high_open, dtype="uint64") + +random_st.randint(18446744073709551616, dtype=np.uint64) +random_st.randint(0, 18446744073709551616, dtype=np.uint64) +random_st.randint(I_u8_high_open, dtype=np.uint64) +random_st.randint(I_u8_low, I_u8_high_open, dtype=np.uint64) +random_st.randint(0, I_u8_high_open, dtype=np.uint64) + +random_st.randint(128, dtype="i1") +random_st.randint(-128, 128, dtype="i1") +random_st.randint(I_i1_high_open, dtype="i1") +random_st.randint(I_i1_low, I_i1_high_open, dtype="i1") +random_st.randint(-128, I_i1_high_open, dtype="i1") + +random_st.randint(128, dtype="int8") +random_st.randint(-128, 128, dtype="int8") +random_st.randint(I_i1_high_open, dtype="int8") +random_st.randint(I_i1_low, I_i1_high_open, dtype="int8") +random_st.randint(-128, I_i1_high_open, dtype="int8") + +random_st.randint(128, dtype=np.int8) +random_st.randint(-128, 128, dtype=np.int8) +random_st.randint(I_i1_high_open, dtype=np.int8) +random_st.randint(I_i1_low, I_i1_high_open, dtype=np.int8) +random_st.randint(-128, I_i1_high_open, dtype=np.int8) + +random_st.randint(32768, dtype="i2") +random_st.randint(-32768, 32768, dtype="i2") +random_st.randint(I_i2_high_open, dtype="i2") +random_st.randint(I_i2_low, I_i2_high_open, dtype="i2") +random_st.randint(-32768, I_i2_high_open, dtype="i2") +random_st.randint(32768, dtype="int16") +random_st.randint(-32768, 32768, dtype="int16") +random_st.randint(I_i2_high_open, dtype="int16") +random_st.randint(I_i2_low, I_i2_high_open, dtype="int16") +random_st.randint(-32768, I_i2_high_open, dtype="int16") +random_st.randint(32768, dtype=np.int16) +random_st.randint(-32768, 32768, dtype=np.int16) +random_st.randint(I_i2_high_open, dtype=np.int16) +random_st.randint(I_i2_low, I_i2_high_open, dtype=np.int16) +random_st.randint(-32768, I_i2_high_open, dtype=np.int16) + +random_st.randint(2147483648, dtype="i4") +random_st.randint(-2147483648, 2147483648, dtype="i4") +random_st.randint(I_i4_high_open, dtype="i4") +random_st.randint(I_i4_low, I_i4_high_open, dtype="i4") +random_st.randint(-2147483648, I_i4_high_open, dtype="i4") + +random_st.randint(2147483648, dtype="int32") +random_st.randint(-2147483648, 2147483648, dtype="int32") +random_st.randint(I_i4_high_open, dtype="int32") +random_st.randint(I_i4_low, I_i4_high_open, dtype="int32") +random_st.randint(-2147483648, I_i4_high_open, dtype="int32") + +random_st.randint(2147483648, dtype=np.int32) +random_st.randint(-2147483648, 2147483648, dtype=np.int32) +random_st.randint(I_i4_high_open, dtype=np.int32) +random_st.randint(I_i4_low, I_i4_high_open, dtype=np.int32) +random_st.randint(-2147483648, I_i4_high_open, dtype=np.int32) + +random_st.randint(9223372036854775808, dtype="i8") +random_st.randint(-9223372036854775808, 9223372036854775808, dtype="i8") +random_st.randint(I_i8_high_open, dtype="i8") +random_st.randint(I_i8_low, I_i8_high_open, dtype="i8") +random_st.randint(-9223372036854775808, I_i8_high_open, dtype="i8") + +random_st.randint(9223372036854775808, dtype="int64") +random_st.randint(-9223372036854775808, 9223372036854775808, dtype="int64") +random_st.randint(I_i8_high_open, dtype="int64") +random_st.randint(I_i8_low, I_i8_high_open, dtype="int64") +random_st.randint(-9223372036854775808, I_i8_high_open, dtype="int64") + +random_st.randint(9223372036854775808, dtype=np.int64) +random_st.randint(-9223372036854775808, 9223372036854775808, dtype=np.int64) +random_st.randint(I_i8_high_open, dtype=np.int64) +random_st.randint(I_i8_low, I_i8_high_open, dtype=np.int64) +random_st.randint(-9223372036854775808, I_i8_high_open, dtype=np.int64) + +bg: np.random.BitGenerator = random_st._bit_generator + +random_st.bytes(2) + +random_st.choice(5) +random_st.choice(5, 3) +random_st.choice(5, 3, replace=True) +random_st.choice(5, 3, p=[1 / 5] * 5) +random_st.choice(5, 3, p=[1 / 5] * 5, replace=False) + +random_st.choice(["pooh", "rabbit", "piglet", "Christopher"]) +random_st.choice(["pooh", "rabbit", "piglet", "Christopher"], 3) +random_st.choice(["pooh", "rabbit", "piglet", "Christopher"], 3, p=[1 / 4] * 4) +random_st.choice(["pooh", "rabbit", "piglet", "Christopher"], 3, replace=True) +random_st.choice(["pooh", "rabbit", "piglet", "Christopher"], 3, replace=False, p=np.array([1 / 8, 1 / 8, 1 / 2, 1 / 4])) + +random_st.dirichlet([0.5, 0.5]) +random_st.dirichlet(np.array([0.5, 0.5])) +random_st.dirichlet(np.array([0.5, 0.5]), size=3) + +random_st.multinomial(20, [1 / 6.0] * 6) +random_st.multinomial(20, np.array([0.5, 0.5])) +random_st.multinomial(20, [1 / 6.0] * 6, size=2) + +random_st.multivariate_normal([0.0], [[1.0]]) +random_st.multivariate_normal([0.0], np.array([[1.0]])) +random_st.multivariate_normal(np.array([0.0]), [[1.0]]) +random_st.multivariate_normal([0.0], np.array([[1.0]])) + +random_st.permutation(10) +random_st.permutation([1, 2, 3, 4]) +random_st.permutation(np.array([1, 2, 3, 4])) +random_st.permutation(D_2D) + +random_st.shuffle(np.arange(10)) +random_st.shuffle([1, 2, 3, 4, 5]) +random_st.shuffle(D_2D) + +np.random.RandomState(SEED_PCG64) +np.random.RandomState(0) +np.random.RandomState([0, 1, 2]) +random_st.__str__() +random_st.__repr__() +random_st_state = random_st.__getstate__() +random_st.__setstate__(random_st_state) +random_st.seed() +random_st.seed(1) +random_st.seed([0, 1]) +random_st_get_state = random_st.get_state() +random_st_get_state_legacy = random_st.get_state(legacy=True) +random_st.set_state(random_st_get_state) + +random_st.rand() +random_st.rand(1) +random_st.rand(1, 2) +random_st.randn() +random_st.randn(1) +random_st.randn(1, 2) +random_st.random_sample() +random_st.random_sample(1) +random_st.random_sample(size=(1, 2)) + +random_st.tomaxint() +random_st.tomaxint(1) +random_st.tomaxint((1,)) diff --git a/numpy/typing/tests/data/pass/scalars.py b/numpy/typing/tests/data/pass/scalars.py new file mode 100644 index 000000000000..b258db49fd7c --- /dev/null +++ b/numpy/typing/tests/data/pass/scalars.py @@ -0,0 +1,254 @@ +import sys +import datetime as dt + +import pytest +import numpy as np + +b = np.bool_() +u8 = np.uint64() +i8 = np.int64() +f8 = np.float64() +c16 = np.complex128() +U = np.str_() +S = np.bytes_() + + +# Construction +class D: + def __index__(self) -> int: + return 0 + + +class C: + def __complex__(self) -> complex: + return 3j + + +class B: + def __int__(self) -> int: + return 4 + + +class A: + def __float__(self) -> float: + return 4.0 + + +np.complex64(3j) +np.complex64(A()) +np.complex64(C()) +np.complex128(3j) +np.complex128(C()) +np.complex128(None) +np.complex64("1.2") +np.complex128(b"2j") + +np.int8(4) +np.int16(3.4) +np.int32(4) +np.int64(-1) +np.uint8(B()) +np.uint32() +np.int32("1") +np.int64(b"2") + +np.float16(A()) +np.float32(16) +np.float64(3.0) +np.float64(None) +np.float32("1") +np.float16(b"2.5") + +if sys.version_info >= (3, 8): + np.uint64(D()) + np.float32(D()) + np.complex64(D()) + +np.bytes_(b"hello") +np.bytes_("hello", 'utf-8') +np.bytes_("hello", encoding='utf-8') +np.str_("hello") +np.str_(b"hello", 'utf-8') +np.str_(b"hello", encoding='utf-8') + +# Array-ish semantics +np.int8().real +np.int16().imag +np.int32().data +np.int64().flags + +np.uint8().itemsize * 2 +np.uint16().ndim + 1 +np.uint32().strides +np.uint64().shape + +# Time structures +np.datetime64() +np.datetime64(0, "D") +np.datetime64(0, b"D") +np.datetime64(0, ('ms', 3)) +np.datetime64("2019") +np.datetime64(b"2019") +np.datetime64("2019", "D") +np.datetime64(np.datetime64()) +np.datetime64(dt.datetime(2000, 5, 3)) +np.datetime64(dt.date(2000, 5, 3)) +np.datetime64(None) +np.datetime64(None, "D") + +np.timedelta64() +np.timedelta64(0) +np.timedelta64(0, "D") +np.timedelta64(0, ('ms', 3)) +np.timedelta64(0, b"D") +np.timedelta64("3") +np.timedelta64(b"5") +np.timedelta64(np.timedelta64(2)) +np.timedelta64(dt.timedelta(2)) +np.timedelta64(None) +np.timedelta64(None, "D") + +np.void(1) +np.void(np.int64(1)) +np.void(True) +np.void(np.bool_(True)) +np.void(b"test") +np.void(np.bytes_("test")) + +# Protocols +i8 = np.int64() +u8 = np.uint64() +f8 = np.float64() +c16 = np.complex128() +b_ = np.bool_() +td = np.timedelta64() +U = np.str_("1") +S = np.bytes_("1") +AR = np.array(1, dtype=np.float64) + +int(i8) +int(u8) +int(f8) +int(b_) +int(td) +int(U) +int(S) +int(AR) +with pytest.warns(np.ComplexWarning): + int(c16) + +float(i8) +float(u8) +float(f8) +float(b_) +float(td) +float(U) +float(S) +float(AR) +with pytest.warns(np.ComplexWarning): + float(c16) + +complex(i8) +complex(u8) +complex(f8) +complex(c16) +complex(b_) +complex(td) +complex(U) +complex(AR) + + +# Misc +c16.dtype +c16.real +c16.imag +c16.real.real +c16.real.imag +c16.ndim +c16.size +c16.itemsize +c16.shape +c16.strides +c16.squeeze() +c16.byteswap() +c16.transpose() + +# Aliases +np.str0() +np.bool8() +np.bytes0() +np.string_() +np.object0() +np.void0(0) + +np.byte() +np.short() +np.intc() +np.intp() +np.int0() +np.int_() +np.longlong() + +np.ubyte() +np.ushort() +np.uintc() +np.uintp() +np.uint0() +np.uint() +np.ulonglong() + +np.half() +np.single() +np.double() +np.float_() +np.longdouble() +np.longfloat() + +np.csingle() +np.singlecomplex() +np.cdouble() +np.complex_() +np.cfloat() +np.clongdouble() +np.clongfloat() +np.longcomplex() + +b.item() +i8.item() +u8.item() +f8.item() +c16.item() +U.item() +S.item() + +b.tolist() +i8.tolist() +u8.tolist() +f8.tolist() +c16.tolist() +U.tolist() +S.tolist() + +b.ravel() +i8.ravel() +u8.ravel() +f8.ravel() +c16.ravel() +U.ravel() +S.ravel() + +b.flatten() +i8.flatten() +u8.flatten() +f8.flatten() +c16.flatten() +U.flatten() +S.flatten() + +b.reshape(1) +i8.reshape(1) +u8.reshape(1) +f8.reshape(1) +c16.reshape(1) +U.reshape(1) +S.reshape(1) diff --git a/numpy/typing/tests/data/pass/simple.py b/numpy/typing/tests/data/pass/simple.py new file mode 100644 index 000000000000..85965e0de707 --- /dev/null +++ b/numpy/typing/tests/data/pass/simple.py @@ -0,0 +1,165 @@ +"""Simple expression that should pass with mypy.""" +import operator + +import numpy as np +from typing import Iterable # noqa: F401 + +# Basic checks +array = np.array([1, 2]) + + +def ndarray_func(x): + # type: (np.ndarray) -> np.ndarray + return x + + +ndarray_func(np.array([1, 2])) +array == 1 +array.dtype == float + +# Dtype construction +np.dtype(float) +np.dtype(np.float64) +np.dtype(None) +np.dtype("float64") +np.dtype(np.dtype(float)) +np.dtype(("U", 10)) +np.dtype((np.int32, (2, 2))) +# Define the arguments on the previous line to prevent bidirectional +# type inference in mypy from broadening the types. +two_tuples_dtype = [("R", "u1"), ("G", "u1"), ("B", "u1")] +np.dtype(two_tuples_dtype) + +three_tuples_dtype = [("R", "u1", 2)] +np.dtype(three_tuples_dtype) + +mixed_tuples_dtype = [("R", "u1"), ("G", np.unicode_, 1)] +np.dtype(mixed_tuples_dtype) + +shape_tuple_dtype = [("R", "u1", (2, 2))] +np.dtype(shape_tuple_dtype) + +shape_like_dtype = [("R", "u1", (2, 2)), ("G", np.unicode_, 1)] +np.dtype(shape_like_dtype) + +object_dtype = [("field1", object)] +np.dtype(object_dtype) + +np.dtype((np.int32, (np.int8, 4))) + +# Dtype comparison +np.dtype(float) == float +np.dtype(float) != np.float64 +np.dtype(float) < None +np.dtype(float) <= "float64" +np.dtype(float) > np.dtype(float) +np.dtype(float) >= np.dtype(("U", 10)) + +# Iteration and indexing +def iterable_func(x): + # type: (Iterable) -> Iterable + return x + + +iterable_func(array) +[element for element in array] +iter(array) +zip(array, array) +array[1] +array[:] +array[...] +array[:] = 0 + +array_2d = np.ones((3, 3)) +array_2d[:2, :2] +array_2d[..., 0] +array_2d[:2, :2] = 0 + +# Other special methods +len(array) +str(array) +array_scalar = np.array(1) +int(array_scalar) +float(array_scalar) +# currently does not work due to https://github.com/python/typeshed/issues/1904 +# complex(array_scalar) +bytes(array_scalar) +operator.index(array_scalar) +bool(array_scalar) + +# comparisons +array < 1 +array <= 1 +array == 1 +array != 1 +array > 1 +array >= 1 +1 < array +1 <= array +1 == array +1 != array +1 > array +1 >= array + +# binary arithmetic +array + 1 +1 + array +array += 1 + +array - 1 +1 - array +array -= 1 + +array * 1 +1 * array +array *= 1 + +nonzero_array = np.array([1, 2]) +array / 1 +1 / nonzero_array +float_array = np.array([1.0, 2.0]) +float_array /= 1 + +array // 1 +1 // nonzero_array +array //= 1 + +array % 1 +1 % nonzero_array +array %= 1 + +divmod(array, 1) +divmod(1, nonzero_array) + +array ** 1 +1 ** array +array **= 1 + +array << 1 +1 << array +array <<= 1 + +array >> 1 +1 >> array +array >>= 1 + +array & 1 +1 & array +array &= 1 + +array ^ 1 +1 ^ array +array ^= 1 + +array | 1 +1 | array +array |= 1 + +# unary arithmetic +-array ++array +abs(array) +~array + +# Other methods +np.array([1, 2]).transpose() diff --git a/numpy/typing/tests/data/pass/simple_py3.py b/numpy/typing/tests/data/pass/simple_py3.py new file mode 100644 index 000000000000..c05a1ce612ac --- /dev/null +++ b/numpy/typing/tests/data/pass/simple_py3.py @@ -0,0 +1,6 @@ +import numpy as np + +array = np.array([1, 2]) + +# The @ operator is not in python 2 +array @ array diff --git a/numpy/typing/tests/data/pass/ufunc_config.py b/numpy/typing/tests/data/pass/ufunc_config.py new file mode 100644 index 000000000000..2d13142457df --- /dev/null +++ b/numpy/typing/tests/data/pass/ufunc_config.py @@ -0,0 +1,50 @@ +"""Typing tests for `numpy.core._ufunc_config`.""" + +import numpy as np + +def func1(a: str, b: int) -> None: ... +def func2(a: str, b: int, c: float = ...) -> None: ... +def func3(a: str, b: int) -> int: ... + +class Write1: + def write(self, a: str) -> None: ... + +class Write2: + def write(self, a: str, b: int = ...) -> None: ... + +class Write3: + def write(self, a: str) -> int: ... + + +_err_default = np.geterr() +_bufsize_default = np.getbufsize() +_errcall_default = np.geterrcall() + +try: + np.seterr(all=None) + np.seterr(divide="ignore") + np.seterr(over="warn") + np.seterr(under="call") + np.seterr(invalid="raise") + np.geterr() + + np.setbufsize(4096) + np.getbufsize() + + np.seterrcall(func1) + np.seterrcall(func2) + np.seterrcall(func3) + np.seterrcall(Write1()) + np.seterrcall(Write2()) + np.seterrcall(Write3()) + np.geterrcall() + + with np.errstate(call=func1, all="call"): + pass + with np.errstate(call=Write1(), divide="log", over="log"): + pass + +finally: + np.seterr(**_err_default) + np.setbufsize(_bufsize_default) + np.seterrcall(_errcall_default) diff --git a/numpy/typing/tests/data/pass/ufunclike.py b/numpy/typing/tests/data/pass/ufunclike.py new file mode 100644 index 000000000000..7eac89e8f9aa --- /dev/null +++ b/numpy/typing/tests/data/pass/ufunclike.py @@ -0,0 +1,46 @@ +from __future__ import annotations +from typing import Any +import numpy as np + + +class Object: + def __ceil__(self) -> Object: + return self + + def __floor__(self) -> Object: + return self + + def __ge__(self, value: object) -> bool: + return True + + def __array__(self) -> np.ndarray[Any, np.dtype[np.object_]]: + ret = np.empty((), dtype=object) + ret[()] = self + return ret + + +AR_LIKE_b = [True, True, False] +AR_LIKE_u = [np.uint32(1), np.uint32(2), np.uint32(3)] +AR_LIKE_i = [1, 2, 3] +AR_LIKE_f = [1.0, 2.0, 3.0] +AR_LIKE_O = [Object(), Object(), Object()] +AR_U: np.ndarray[Any, np.dtype[np.str_]] = np.zeros(3, dtype="U5") + +np.fix(AR_LIKE_b) +np.fix(AR_LIKE_u) +np.fix(AR_LIKE_i) +np.fix(AR_LIKE_f) +np.fix(AR_LIKE_O) +np.fix(AR_LIKE_f, out=AR_U) + +np.isposinf(AR_LIKE_b) +np.isposinf(AR_LIKE_u) +np.isposinf(AR_LIKE_i) +np.isposinf(AR_LIKE_f) +np.isposinf(AR_LIKE_f, out=AR_U) + +np.isneginf(AR_LIKE_b) +np.isneginf(AR_LIKE_u) +np.isneginf(AR_LIKE_i) +np.isneginf(AR_LIKE_f) +np.isneginf(AR_LIKE_f, out=AR_U) diff --git a/numpy/typing/tests/data/pass/ufuncs.py b/numpy/typing/tests/data/pass/ufuncs.py new file mode 100644 index 000000000000..3cc31ae5e305 --- /dev/null +++ b/numpy/typing/tests/data/pass/ufuncs.py @@ -0,0 +1,17 @@ +import numpy as np + +np.sin(1) +np.sin([1, 2, 3]) +np.sin(1, out=np.empty(1)) +np.matmul(np.ones((2, 2, 2)), np.ones((2, 2, 2)), axes=[(0, 1), (0, 1), (0, 1)]) +np.sin(1, signature="D->D") +np.sin(1, extobj=[16, 1, lambda: None]) +# NOTE: `np.generic` subclasses are not guaranteed to support addition; +# re-enable this we can infer the exact return type of `np.sin(...)`. +# +# np.sin(1) + np.sin(1) +np.sin.types[0] +np.sin.__name__ +np.sin.__doc__ + +np.abs(np.array([1])) diff --git a/numpy/typing/tests/data/pass/warnings_and_errors.py b/numpy/typing/tests/data/pass/warnings_and_errors.py new file mode 100644 index 000000000000..a556bf6bcb35 --- /dev/null +++ b/numpy/typing/tests/data/pass/warnings_and_errors.py @@ -0,0 +1,6 @@ +import numpy as np + +np.AxisError("test") +np.AxisError(1, ndim=2) +np.AxisError(1, ndim=2, msg_prefix="error") +np.AxisError(1, ndim=2, msg_prefix=None) diff --git a/numpy/typing/tests/data/reveal/arithmetic.pyi b/numpy/typing/tests/data/reveal/arithmetic.pyi new file mode 100644 index 000000000000..c5b46746980d --- /dev/null +++ b/numpy/typing/tests/data/reveal/arithmetic.pyi @@ -0,0 +1,522 @@ +from typing import Any, List +import numpy as np +import numpy.typing as npt + +# Can't directly import `np.float128` as it is not available on all platforms +f16: np.floating[npt._128Bit] + +c16 = np.complex128() +f8 = np.float64() +i8 = np.int64() +u8 = np.uint64() + +c8 = np.complex64() +f4 = np.float32() +i4 = np.int32() +u4 = np.uint32() + +dt = np.datetime64(0, "D") +td = np.timedelta64(0, "D") + +b_ = np.bool_() + +b = bool() +c = complex() +f = float() +i = int() + +AR_b: np.ndarray[Any, np.dtype[np.bool_]] +AR_u: np.ndarray[Any, np.dtype[np.uint32]] +AR_i: np.ndarray[Any, np.dtype[np.int64]] +AR_f: np.ndarray[Any, np.dtype[np.float64]] +AR_c: np.ndarray[Any, np.dtype[np.complex128]] +AR_m: np.ndarray[Any, np.dtype[np.timedelta64]] +AR_M: np.ndarray[Any, np.dtype[np.datetime64]] +AR_O: np.ndarray[Any, np.dtype[np.object_]] + +AR_LIKE_b: List[bool] +AR_LIKE_u: List[np.uint32] +AR_LIKE_i: List[int] +AR_LIKE_f: List[float] +AR_LIKE_c: List[complex] +AR_LIKE_m: List[np.timedelta64] +AR_LIKE_M: List[np.datetime64] +AR_LIKE_O: List[np.object_] + +# Array subtraction + +reveal_type(AR_b - AR_LIKE_u) # E: ndarray[Any, dtype[unsignedinteger[Any]]] +reveal_type(AR_b - AR_LIKE_i) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(AR_b - AR_LIKE_f) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(AR_b - AR_LIKE_c) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] +reveal_type(AR_b - AR_LIKE_m) # E: ndarray[Any, dtype[timedelta64]] +reveal_type(AR_b - AR_LIKE_O) # E: Any + +reveal_type(AR_LIKE_u - AR_b) # E: ndarray[Any, dtype[unsignedinteger[Any]]] +reveal_type(AR_LIKE_i - AR_b) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(AR_LIKE_f - AR_b) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(AR_LIKE_c - AR_b) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] +reveal_type(AR_LIKE_m - AR_b) # E: ndarray[Any, dtype[timedelta64]] +reveal_type(AR_LIKE_M - AR_b) # E: ndarray[Any, dtype[datetime64]] +reveal_type(AR_LIKE_O - AR_b) # E: Any + +reveal_type(AR_u - AR_LIKE_b) # E: ndarray[Any, dtype[unsignedinteger[Any]]] +reveal_type(AR_u - AR_LIKE_u) # E: ndarray[Any, dtype[unsignedinteger[Any]]] +reveal_type(AR_u - AR_LIKE_i) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(AR_u - AR_LIKE_f) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(AR_u - AR_LIKE_c) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] +reveal_type(AR_u - AR_LIKE_m) # E: ndarray[Any, dtype[timedelta64]] +reveal_type(AR_u - AR_LIKE_O) # E: Any + +reveal_type(AR_LIKE_b - AR_u) # E: ndarray[Any, dtype[unsignedinteger[Any]]] +reveal_type(AR_LIKE_u - AR_u) # E: ndarray[Any, dtype[unsignedinteger[Any]]] +reveal_type(AR_LIKE_i - AR_u) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(AR_LIKE_f - AR_u) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(AR_LIKE_c - AR_u) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] +reveal_type(AR_LIKE_m - AR_u) # E: ndarray[Any, dtype[timedelta64]] +reveal_type(AR_LIKE_M - AR_u) # E: ndarray[Any, dtype[datetime64]] +reveal_type(AR_LIKE_O - AR_u) # E: Any + +reveal_type(AR_i - AR_LIKE_b) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(AR_i - AR_LIKE_u) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(AR_i - AR_LIKE_i) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(AR_i - AR_LIKE_f) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(AR_i - AR_LIKE_c) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] +reveal_type(AR_i - AR_LIKE_m) # E: ndarray[Any, dtype[timedelta64]] +reveal_type(AR_i - AR_LIKE_O) # E: Any + +reveal_type(AR_LIKE_b - AR_i) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(AR_LIKE_u - AR_i) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(AR_LIKE_i - AR_i) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(AR_LIKE_f - AR_i) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(AR_LIKE_c - AR_i) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] +reveal_type(AR_LIKE_m - AR_i) # E: ndarray[Any, dtype[timedelta64]] +reveal_type(AR_LIKE_M - AR_i) # E: ndarray[Any, dtype[datetime64]] +reveal_type(AR_LIKE_O - AR_i) # E: Any + +reveal_type(AR_f - AR_LIKE_b) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(AR_f - AR_LIKE_u) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(AR_f - AR_LIKE_i) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(AR_f - AR_LIKE_f) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(AR_f - AR_LIKE_c) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] +reveal_type(AR_f - AR_LIKE_O) # E: Any + +reveal_type(AR_LIKE_b - AR_f) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(AR_LIKE_u - AR_f) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(AR_LIKE_i - AR_f) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(AR_LIKE_f - AR_f) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(AR_LIKE_c - AR_f) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] +reveal_type(AR_LIKE_O - AR_f) # E: Any + +reveal_type(AR_c - AR_LIKE_b) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] +reveal_type(AR_c - AR_LIKE_u) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] +reveal_type(AR_c - AR_LIKE_i) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] +reveal_type(AR_c - AR_LIKE_f) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] +reveal_type(AR_c - AR_LIKE_c) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] +reveal_type(AR_c - AR_LIKE_O) # E: Any + +reveal_type(AR_LIKE_b - AR_c) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] +reveal_type(AR_LIKE_u - AR_c) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] +reveal_type(AR_LIKE_i - AR_c) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] +reveal_type(AR_LIKE_f - AR_c) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] +reveal_type(AR_LIKE_c - AR_c) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] +reveal_type(AR_LIKE_O - AR_c) # E: Any + +reveal_type(AR_m - AR_LIKE_b) # E: ndarray[Any, dtype[timedelta64]] +reveal_type(AR_m - AR_LIKE_u) # E: ndarray[Any, dtype[timedelta64]] +reveal_type(AR_m - AR_LIKE_i) # E: ndarray[Any, dtype[timedelta64]] +reveal_type(AR_m - AR_LIKE_m) # E: ndarray[Any, dtype[timedelta64]] +reveal_type(AR_m - AR_LIKE_O) # E: Any + +reveal_type(AR_LIKE_b - AR_m) # E: ndarray[Any, dtype[timedelta64]] +reveal_type(AR_LIKE_u - AR_m) # E: ndarray[Any, dtype[timedelta64]] +reveal_type(AR_LIKE_i - AR_m) # E: ndarray[Any, dtype[timedelta64]] +reveal_type(AR_LIKE_m - AR_m) # E: ndarray[Any, dtype[timedelta64]] +reveal_type(AR_LIKE_M - AR_m) # E: ndarray[Any, dtype[datetime64]] +reveal_type(AR_LIKE_O - AR_m) # E: Any + +reveal_type(AR_M - AR_LIKE_b) # E: ndarray[Any, dtype[datetime64]] +reveal_type(AR_M - AR_LIKE_u) # E: ndarray[Any, dtype[datetime64]] +reveal_type(AR_M - AR_LIKE_i) # E: ndarray[Any, dtype[datetime64]] +reveal_type(AR_M - AR_LIKE_m) # E: ndarray[Any, dtype[datetime64]] +reveal_type(AR_M - AR_LIKE_M) # E: ndarray[Any, dtype[timedelta64]] +reveal_type(AR_M - AR_LIKE_O) # E: Any + +reveal_type(AR_LIKE_M - AR_M) # E: ndarray[Any, dtype[timedelta64]] +reveal_type(AR_LIKE_O - AR_M) # E: Any + +reveal_type(AR_O - AR_LIKE_b) # E: Any +reveal_type(AR_O - AR_LIKE_u) # E: Any +reveal_type(AR_O - AR_LIKE_i) # E: Any +reveal_type(AR_O - AR_LIKE_f) # E: Any +reveal_type(AR_O - AR_LIKE_c) # E: Any +reveal_type(AR_O - AR_LIKE_m) # E: Any +reveal_type(AR_O - AR_LIKE_M) # E: Any +reveal_type(AR_O - AR_LIKE_O) # E: Any + +reveal_type(AR_LIKE_b - AR_O) # E: Any +reveal_type(AR_LIKE_u - AR_O) # E: Any +reveal_type(AR_LIKE_i - AR_O) # E: Any +reveal_type(AR_LIKE_f - AR_O) # E: Any +reveal_type(AR_LIKE_c - AR_O) # E: Any +reveal_type(AR_LIKE_m - AR_O) # E: Any +reveal_type(AR_LIKE_M - AR_O) # E: Any +reveal_type(AR_LIKE_O - AR_O) # E: Any + +# Array floor division + +reveal_type(AR_b // AR_LIKE_b) # E: ndarray[Any, dtype[{int8}]] +reveal_type(AR_b // AR_LIKE_u) # E: ndarray[Any, dtype[unsignedinteger[Any]]] +reveal_type(AR_b // AR_LIKE_i) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(AR_b // AR_LIKE_f) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(AR_b // AR_LIKE_O) # E: Any + +reveal_type(AR_LIKE_b // AR_b) # E: ndarray[Any, dtype[{int8}]] +reveal_type(AR_LIKE_u // AR_b) # E: ndarray[Any, dtype[unsignedinteger[Any]]] +reveal_type(AR_LIKE_i // AR_b) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(AR_LIKE_f // AR_b) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(AR_LIKE_O // AR_b) # E: Any + +reveal_type(AR_u // AR_LIKE_b) # E: ndarray[Any, dtype[unsignedinteger[Any]]] +reveal_type(AR_u // AR_LIKE_u) # E: ndarray[Any, dtype[unsignedinteger[Any]]] +reveal_type(AR_u // AR_LIKE_i) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(AR_u // AR_LIKE_f) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(AR_u // AR_LIKE_O) # E: Any + +reveal_type(AR_LIKE_b // AR_u) # E: ndarray[Any, dtype[unsignedinteger[Any]]] +reveal_type(AR_LIKE_u // AR_u) # E: ndarray[Any, dtype[unsignedinteger[Any]]] +reveal_type(AR_LIKE_i // AR_u) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(AR_LIKE_f // AR_u) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(AR_LIKE_m // AR_u) # E: ndarray[Any, dtype[timedelta64]] +reveal_type(AR_LIKE_O // AR_u) # E: Any + +reveal_type(AR_i // AR_LIKE_b) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(AR_i // AR_LIKE_u) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(AR_i // AR_LIKE_i) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(AR_i // AR_LIKE_f) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(AR_i // AR_LIKE_O) # E: Any + +reveal_type(AR_LIKE_b // AR_i) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(AR_LIKE_u // AR_i) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(AR_LIKE_i // AR_i) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(AR_LIKE_f // AR_i) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(AR_LIKE_m // AR_i) # E: ndarray[Any, dtype[timedelta64]] +reveal_type(AR_LIKE_O // AR_i) # E: Any + +reveal_type(AR_f // AR_LIKE_b) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(AR_f // AR_LIKE_u) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(AR_f // AR_LIKE_i) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(AR_f // AR_LIKE_f) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(AR_f // AR_LIKE_O) # E: Any + +reveal_type(AR_LIKE_b // AR_f) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(AR_LIKE_u // AR_f) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(AR_LIKE_i // AR_f) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(AR_LIKE_f // AR_f) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(AR_LIKE_m // AR_f) # E: ndarray[Any, dtype[timedelta64]] +reveal_type(AR_LIKE_O // AR_f) # E: Any + +reveal_type(AR_m // AR_LIKE_u) # E: ndarray[Any, dtype[timedelta64]] +reveal_type(AR_m // AR_LIKE_i) # E: ndarray[Any, dtype[timedelta64]] +reveal_type(AR_m // AR_LIKE_f) # E: ndarray[Any, dtype[timedelta64]] +reveal_type(AR_m // AR_LIKE_m) # E: ndarray[Any, dtype[{int64}]] +reveal_type(AR_m // AR_LIKE_O) # E: Any + +reveal_type(AR_LIKE_m // AR_m) # E: ndarray[Any, dtype[{int64}]] +reveal_type(AR_LIKE_O // AR_m) # E: Any + +reveal_type(AR_O // AR_LIKE_b) # E: Any +reveal_type(AR_O // AR_LIKE_u) # E: Any +reveal_type(AR_O // AR_LIKE_i) # E: Any +reveal_type(AR_O // AR_LIKE_f) # E: Any +reveal_type(AR_O // AR_LIKE_m) # E: Any +reveal_type(AR_O // AR_LIKE_M) # E: Any +reveal_type(AR_O // AR_LIKE_O) # E: Any + +reveal_type(AR_LIKE_b // AR_O) # E: Any +reveal_type(AR_LIKE_u // AR_O) # E: Any +reveal_type(AR_LIKE_i // AR_O) # E: Any +reveal_type(AR_LIKE_f // AR_O) # E: Any +reveal_type(AR_LIKE_m // AR_O) # E: Any +reveal_type(AR_LIKE_M // AR_O) # E: Any +reveal_type(AR_LIKE_O // AR_O) # E: Any + +# unary ops + +reveal_type(-f16) # E: {float128} +reveal_type(-c16) # E: {complex128} +reveal_type(-c8) # E: {complex64} +reveal_type(-f8) # E: {float64} +reveal_type(-f4) # E: {float32} +reveal_type(-i8) # E: {int64} +reveal_type(-i4) # E: {int32} +reveal_type(-u8) # E: {uint64} +reveal_type(-u4) # E: {uint32} +reveal_type(-td) # E: timedelta64 +reveal_type(-AR_f) # E: Any + +reveal_type(+f16) # E: {float128} +reveal_type(+c16) # E: {complex128} +reveal_type(+c8) # E: {complex64} +reveal_type(+f8) # E: {float64} +reveal_type(+f4) # E: {float32} +reveal_type(+i8) # E: {int64} +reveal_type(+i4) # E: {int32} +reveal_type(+u8) # E: {uint64} +reveal_type(+u4) # E: {uint32} +reveal_type(+td) # E: timedelta64 +reveal_type(+AR_f) # E: Any + +reveal_type(abs(f16)) # E: {float128} +reveal_type(abs(c16)) # E: {float64} +reveal_type(abs(c8)) # E: {float32} +reveal_type(abs(f8)) # E: {float64} +reveal_type(abs(f4)) # E: {float32} +reveal_type(abs(i8)) # E: {int64} +reveal_type(abs(i4)) # E: {int32} +reveal_type(abs(u8)) # E: {uint64} +reveal_type(abs(u4)) # E: {uint32} +reveal_type(abs(td)) # E: timedelta64 +reveal_type(abs(b_)) # E: bool_ +reveal_type(abs(AR_f)) # E: Any + +# Time structures + +reveal_type(dt + td) # E: datetime64 +reveal_type(dt + i) # E: datetime64 +reveal_type(dt + i4) # E: datetime64 +reveal_type(dt + i8) # E: datetime64 +reveal_type(dt - dt) # E: timedelta64 +reveal_type(dt - i) # E: datetime64 +reveal_type(dt - i4) # E: datetime64 +reveal_type(dt - i8) # E: datetime64 + +reveal_type(td + td) # E: timedelta64 +reveal_type(td + i) # E: timedelta64 +reveal_type(td + i4) # E: timedelta64 +reveal_type(td + i8) # E: timedelta64 +reveal_type(td - td) # E: timedelta64 +reveal_type(td - i) # E: timedelta64 +reveal_type(td - i4) # E: timedelta64 +reveal_type(td - i8) # E: timedelta64 +reveal_type(td / f) # E: timedelta64 +reveal_type(td / f4) # E: timedelta64 +reveal_type(td / f8) # E: timedelta64 +reveal_type(td / td) # E: {float64} +reveal_type(td // td) # E: {int64} + +# boolean + +reveal_type(b_ / b) # E: {float64} +reveal_type(b_ / b_) # E: {float64} +reveal_type(b_ / i) # E: {float64} +reveal_type(b_ / i8) # E: {float64} +reveal_type(b_ / i4) # E: {float64} +reveal_type(b_ / u8) # E: {float64} +reveal_type(b_ / u4) # E: {float64} +reveal_type(b_ / f) # E: {float64} +reveal_type(b_ / f16) # E: {float128} +reveal_type(b_ / f8) # E: {float64} +reveal_type(b_ / f4) # E: {float32} +reveal_type(b_ / c) # E: {complex128} +reveal_type(b_ / c16) # E: {complex128} +reveal_type(b_ / c8) # E: {complex64} + +reveal_type(b / b_) # E: {float64} +reveal_type(b_ / b_) # E: {float64} +reveal_type(i / b_) # E: {float64} +reveal_type(i8 / b_) # E: {float64} +reveal_type(i4 / b_) # E: {float64} +reveal_type(u8 / b_) # E: {float64} +reveal_type(u4 / b_) # E: {float64} +reveal_type(f / b_) # E: {float64} +reveal_type(f16 / b_) # E: {float128} +reveal_type(f8 / b_) # E: {float64} +reveal_type(f4 / b_) # E: {float32} +reveal_type(c / b_) # E: {complex128} +reveal_type(c16 / b_) # E: {complex128} +reveal_type(c8 / b_) # E: {complex64} + +# Complex + +reveal_type(c16 + f16) # E: {complex256} +reveal_type(c16 + c16) # E: {complex128} +reveal_type(c16 + f8) # E: {complex128} +reveal_type(c16 + i8) # E: {complex128} +reveal_type(c16 + c8) # E: {complex128} +reveal_type(c16 + f4) # E: {complex128} +reveal_type(c16 + i4) # E: {complex128} +reveal_type(c16 + b_) # E: {complex128} +reveal_type(c16 + b) # E: {complex128} +reveal_type(c16 + c) # E: {complex128} +reveal_type(c16 + f) # E: {complex128} +reveal_type(c16 + i) # E: {complex128} +reveal_type(c16 + AR_f) # E: Any + +reveal_type(f16 + c16) # E: {complex256} +reveal_type(c16 + c16) # E: {complex128} +reveal_type(f8 + c16) # E: {complex128} +reveal_type(i8 + c16) # E: {complex128} +reveal_type(c8 + c16) # E: {complex128} +reveal_type(f4 + c16) # E: {complex128} +reveal_type(i4 + c16) # E: {complex128} +reveal_type(b_ + c16) # E: {complex128} +reveal_type(b + c16) # E: {complex128} +reveal_type(c + c16) # E: {complex128} +reveal_type(f + c16) # E: {complex128} +reveal_type(i + c16) # E: {complex128} +reveal_type(AR_f + c16) # E: Any + +reveal_type(c8 + f16) # E: {complex256} +reveal_type(c8 + c16) # E: {complex128} +reveal_type(c8 + f8) # E: {complex128} +reveal_type(c8 + i8) # E: {complex128} +reveal_type(c8 + c8) # E: {complex64} +reveal_type(c8 + f4) # E: {complex64} +reveal_type(c8 + i4) # E: {complex64} +reveal_type(c8 + b_) # E: {complex64} +reveal_type(c8 + b) # E: {complex64} +reveal_type(c8 + c) # E: {complex128} +reveal_type(c8 + f) # E: {complex128} +reveal_type(c8 + i) # E: complexfloating[{_NBitInt}, {_NBitInt}] +reveal_type(c8 + AR_f) # E: Any + +reveal_type(f16 + c8) # E: {complex256} +reveal_type(c16 + c8) # E: {complex128} +reveal_type(f8 + c8) # E: {complex128} +reveal_type(i8 + c8) # E: {complex128} +reveal_type(c8 + c8) # E: {complex64} +reveal_type(f4 + c8) # E: {complex64} +reveal_type(i4 + c8) # E: {complex64} +reveal_type(b_ + c8) # E: {complex64} +reveal_type(b + c8) # E: {complex64} +reveal_type(c + c8) # E: {complex128} +reveal_type(f + c8) # E: {complex128} +reveal_type(i + c8) # E: complexfloating[{_NBitInt}, {_NBitInt}] +reveal_type(AR_f + c8) # E: Any + +# Float + +reveal_type(f8 + f16) # E: {float128} +reveal_type(f8 + f8) # E: {float64} +reveal_type(f8 + i8) # E: {float64} +reveal_type(f8 + f4) # E: {float64} +reveal_type(f8 + i4) # E: {float64} +reveal_type(f8 + b_) # E: {float64} +reveal_type(f8 + b) # E: {float64} +reveal_type(f8 + c) # E: {complex128} +reveal_type(f8 + f) # E: {float64} +reveal_type(f8 + i) # E: {float64} +reveal_type(f8 + AR_f) # E: Any + +reveal_type(f16 + f8) # E: {float128} +reveal_type(f8 + f8) # E: {float64} +reveal_type(i8 + f8) # E: {float64} +reveal_type(f4 + f8) # E: {float64} +reveal_type(i4 + f8) # E: {float64} +reveal_type(b_ + f8) # E: {float64} +reveal_type(b + f8) # E: {float64} +reveal_type(c + f8) # E: {complex128} +reveal_type(f + f8) # E: {float64} +reveal_type(i + f8) # E: {float64} +reveal_type(AR_f + f8) # E: Any + +reveal_type(f4 + f16) # E: {float128} +reveal_type(f4 + f8) # E: {float64} +reveal_type(f4 + i8) # E: {float64} +reveal_type(f4 + f4) # E: {float32} +reveal_type(f4 + i4) # E: {float32} +reveal_type(f4 + b_) # E: {float32} +reveal_type(f4 + b) # E: {float32} +reveal_type(f4 + c) # E: {complex128} +reveal_type(f4 + f) # E: {float64} +reveal_type(f4 + i) # E: floating[{_NBitInt}] +reveal_type(f4 + AR_f) # E: Any + +reveal_type(f16 + f4) # E: {float128} +reveal_type(f8 + f4) # E: {float64} +reveal_type(i8 + f4) # E: {float64} +reveal_type(f4 + f4) # E: {float32} +reveal_type(i4 + f4) # E: {float32} +reveal_type(b_ + f4) # E: {float32} +reveal_type(b + f4) # E: {float32} +reveal_type(c + f4) # E: {complex128} +reveal_type(f + f4) # E: {float64} +reveal_type(i + f4) # E: floating[{_NBitInt}] +reveal_type(AR_f + f4) # E: Any + +# Int + +reveal_type(i8 + i8) # E: {int64} +reveal_type(i8 + u8) # E: Any +reveal_type(i8 + i4) # E: {int64} +reveal_type(i8 + u4) # E: Any +reveal_type(i8 + b_) # E: {int64} +reveal_type(i8 + b) # E: {int64} +reveal_type(i8 + c) # E: {complex128} +reveal_type(i8 + f) # E: {float64} +reveal_type(i8 + i) # E: {int64} +reveal_type(i8 + AR_f) # E: Any + +reveal_type(u8 + u8) # E: {uint64} +reveal_type(u8 + i4) # E: Any +reveal_type(u8 + u4) # E: {uint64} +reveal_type(u8 + b_) # E: {uint64} +reveal_type(u8 + b) # E: {uint64} +reveal_type(u8 + c) # E: {complex128} +reveal_type(u8 + f) # E: {float64} +reveal_type(u8 + i) # E: Any +reveal_type(u8 + AR_f) # E: Any + +reveal_type(i8 + i8) # E: {int64} +reveal_type(u8 + i8) # E: Any +reveal_type(i4 + i8) # E: {int64} +reveal_type(u4 + i8) # E: Any +reveal_type(b_ + i8) # E: {int64} +reveal_type(b + i8) # E: {int64} +reveal_type(c + i8) # E: {complex128} +reveal_type(f + i8) # E: {float64} +reveal_type(i + i8) # E: {int64} +reveal_type(AR_f + i8) # E: Any + +reveal_type(u8 + u8) # E: {uint64} +reveal_type(i4 + u8) # E: Any +reveal_type(u4 + u8) # E: {uint64} +reveal_type(b_ + u8) # E: {uint64} +reveal_type(b + u8) # E: {uint64} +reveal_type(c + u8) # E: {complex128} +reveal_type(f + u8) # E: {float64} +reveal_type(i + u8) # E: Any +reveal_type(AR_f + u8) # E: Any + +reveal_type(i4 + i8) # E: {int64} +reveal_type(i4 + i4) # E: {int32} +reveal_type(i4 + i) # E: {int_} +reveal_type(i4 + b_) # E: {int32} +reveal_type(i4 + b) # E: {int32} +reveal_type(i4 + AR_f) # E: Any + +reveal_type(u4 + i8) # E: Any +reveal_type(u4 + i4) # E: Any +reveal_type(u4 + u8) # E: {uint64} +reveal_type(u4 + u4) # E: {uint32} +reveal_type(u4 + i) # E: Any +reveal_type(u4 + b_) # E: {uint32} +reveal_type(u4 + b) # E: {uint32} +reveal_type(u4 + AR_f) # E: Any + +reveal_type(i8 + i4) # E: {int64} +reveal_type(i4 + i4) # E: {int32} +reveal_type(i + i4) # E: {int_} +reveal_type(b_ + i4) # E: {int32} +reveal_type(b + i4) # E: {int32} +reveal_type(AR_f + i4) # E: Any + +reveal_type(i8 + u4) # E: Any +reveal_type(i4 + u4) # E: Any +reveal_type(u8 + u4) # E: {uint64} +reveal_type(u4 + u4) # E: {uint32} +reveal_type(b_ + u4) # E: {uint32} +reveal_type(b + u4) # E: {uint32} +reveal_type(i + u4) # E: Any +reveal_type(AR_f + u4) # E: Any diff --git a/numpy/typing/tests/data/reveal/array_constructors.pyi b/numpy/typing/tests/data/reveal/array_constructors.pyi new file mode 100644 index 000000000000..dc0f107a1735 --- /dev/null +++ b/numpy/typing/tests/data/reveal/array_constructors.pyi @@ -0,0 +1,182 @@ +from typing import List, Any, TypeVar +from pathlib import Path + +import numpy as np +import numpy.typing as npt + +_SCT = TypeVar("_SCT", bound=np.generic, covariant=True) + +class SubClass(np.ndarray[Any, np.dtype[_SCT]]): ... + +i8: np.int64 + +A: npt.NDArray[np.float64] +B: SubClass[np.float64] +C: List[int] + +def func(i: int, j: int, **kwargs: Any) -> SubClass[np.float64]: ... + +reveal_type(np.empty_like(A)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.empty_like(B)) # E: SubClass[{float64}] +reveal_type(np.empty_like([1, 1.0])) # E: ndarray[Any, dtype[Any]] +reveal_type(np.empty_like(A, dtype=np.int64)) # E: ndarray[Any, dtype[{int64}]] +reveal_type(np.empty_like(A, dtype='c16')) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.array(A)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.array(B)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.array(B, subok=True)) # E: SubClass[{float64}] +reveal_type(np.array([1, 1.0])) # E: ndarray[Any, dtype[Any]] +reveal_type(np.array(A, dtype=np.int64)) # E: ndarray[Any, dtype[{int64}]] +reveal_type(np.array(A, dtype='c16')) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.zeros([1, 5, 6])) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.zeros([1, 5, 6], dtype=np.int64)) # E: ndarray[Any, dtype[{int64}]] +reveal_type(np.zeros([1, 5, 6], dtype='c16')) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.empty([1, 5, 6])) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.empty([1, 5, 6], dtype=np.int64)) # E: ndarray[Any, dtype[{int64}]] +reveal_type(np.empty([1, 5, 6], dtype='c16')) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.concatenate(A)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.concatenate([1, 1.0])) # E: ndarray[Any, dtype[Any]] +reveal_type(np.concatenate(A, dtype=np.int64)) # E: ndarray[Any, dtype[{int64}]] +reveal_type(np.concatenate(A, dtype='c16')) # E: ndarray[Any, dtype[Any]] +reveal_type(np.concatenate([1, 1.0], out=A)) # E: ndarray[Any, dtype[{float64}]] + +reveal_type(np.asarray(A)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.asarray(B)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.asarray([1, 1.0])) # E: ndarray[Any, dtype[Any]] +reveal_type(np.asarray(A, dtype=np.int64)) # E: ndarray[Any, dtype[{int64}]] +reveal_type(np.asarray(A, dtype='c16')) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.asanyarray(A)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.asanyarray(B)) # E: SubClass[{float64}] +reveal_type(np.asanyarray([1, 1.0])) # E: ndarray[Any, dtype[Any]] +reveal_type(np.asanyarray(A, dtype=np.int64)) # E: ndarray[Any, dtype[{int64}]] +reveal_type(np.asanyarray(A, dtype='c16')) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.ascontiguousarray(A)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.ascontiguousarray(B)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.ascontiguousarray([1, 1.0])) # E: ndarray[Any, dtype[Any]] +reveal_type(np.ascontiguousarray(A, dtype=np.int64)) # E: ndarray[Any, dtype[{int64}]] +reveal_type(np.ascontiguousarray(A, dtype='c16')) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.asfortranarray(A)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.asfortranarray(B)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.asfortranarray([1, 1.0])) # E: ndarray[Any, dtype[Any]] +reveal_type(np.asfortranarray(A, dtype=np.int64)) # E: ndarray[Any, dtype[{int64}]] +reveal_type(np.asfortranarray(A, dtype='c16')) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.fromstring("1 1 1", sep=" ")) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.fromstring(b"1 1 1", sep=" ")) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.fromstring("1 1 1", dtype=np.int64, sep=" ")) # E: ndarray[Any, dtype[{int64}]] +reveal_type(np.fromstring(b"1 1 1", dtype=np.int64, sep=" ")) # E: ndarray[Any, dtype[{int64}]] +reveal_type(np.fromstring("1 1 1", dtype="c16", sep=" ")) # E: ndarray[Any, dtype[Any]] +reveal_type(np.fromstring(b"1 1 1", dtype="c16", sep=" ")) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.fromfile("test.txt", sep=" ")) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.fromfile("test.txt", dtype=np.int64, sep=" ")) # E: ndarray[Any, dtype[{int64}]] +reveal_type(np.fromfile("test.txt", dtype="c16", sep=" ")) # E: ndarray[Any, dtype[Any]] +with open("test.txt") as f: + reveal_type(np.fromfile(f, sep=" ")) # E: ndarray[Any, dtype[{float64}]] + reveal_type(np.fromfile(b"test.txt", sep=" ")) # E: ndarray[Any, dtype[{float64}]] + reveal_type(np.fromfile(Path("test.txt"), sep=" ")) # E: ndarray[Any, dtype[{float64}]] + +reveal_type(np.fromiter("12345", np.float64)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.fromiter("12345", float)) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.frombuffer(A)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.frombuffer(A, dtype=np.int64)) # E: ndarray[Any, dtype[{int64}]] +reveal_type(np.frombuffer(A, dtype="c16")) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.arange(False, True)) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(np.arange(10)) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(np.arange(0, 10, step=2)) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(np.arange(10.0)) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.arange(start=0, stop=10.0)) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.arange(np.timedelta64(0))) # E: ndarray[Any, dtype[timedelta64]] +reveal_type(np.arange(0, np.timedelta64(10))) # E: ndarray[Any, dtype[timedelta64]] +reveal_type(np.arange(np.datetime64("0"), np.datetime64("10"))) # E: ndarray[Any, dtype[datetime64]] +reveal_type(np.arange(10, dtype=np.float64)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.arange(0, 10, step=2, dtype=np.int16)) # E: ndarray[Any, dtype[{int16}]] +reveal_type(np.arange(10, dtype=int)) # E: ndarray[Any, dtype[Any]] +reveal_type(np.arange(0, 10, dtype="f8")) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.require(A)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.require(B)) # E: SubClass[{float64}] +reveal_type(np.require(B, requirements=None)) # E: SubClass[{float64}] +reveal_type(np.require(B, dtype=int)) # E: ndarray[Any, Any] +reveal_type(np.require(B, requirements="E")) # E: ndarray[Any, Any] +reveal_type(np.require(B, requirements=["ENSUREARRAY"])) # E: ndarray[Any, Any] +reveal_type(np.require(B, requirements={"F", "E"})) # E: ndarray[Any, Any] +reveal_type(np.require(B, requirements=["C", "OWNDATA"])) # E: SubClass[{float64}] +reveal_type(np.require(B, requirements="W")) # E: SubClass[{float64}] +reveal_type(np.require(B, requirements="A")) # E: SubClass[{float64}] +reveal_type(np.require(C)) # E: ndarray[Any, Any] + +reveal_type(np.linspace(0, 10)) # E: ndarray[Any, Any] +reveal_type(np.linspace(0, 10, retstep=True)) # E: Tuple[ndarray[Any, Any], Any] +reveal_type(np.logspace(0, 10)) # E: ndarray[Any, Any] +reveal_type(np.geomspace(1, 10)) # E: ndarray[Any, Any] + +reveal_type(np.zeros_like(A)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.zeros_like(C)) # E: ndarray[Any, dtype[Any]] +reveal_type(np.zeros_like(A, dtype=float)) # E: ndarray[Any, dtype[Any]] +reveal_type(np.zeros_like(B)) # E: SubClass[{float64}] +reveal_type(np.zeros_like(B, dtype=np.int64)) # E: ndarray[Any, dtype[{int64}]] + +reveal_type(np.ones_like(A)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.ones_like(C)) # E: ndarray[Any, dtype[Any]] +reveal_type(np.ones_like(A, dtype=float)) # E: ndarray[Any, dtype[Any]] +reveal_type(np.ones_like(B)) # E: SubClass[{float64}] +reveal_type(np.ones_like(B, dtype=np.int64)) # E: ndarray[Any, dtype[{int64}]] + +reveal_type(np.full_like(A, i8)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.full_like(C, i8)) # E: ndarray[Any, dtype[Any]] +reveal_type(np.full_like(A, i8, dtype=int)) # E: ndarray[Any, dtype[Any]] +reveal_type(np.full_like(B, i8)) # E: SubClass[{float64}] +reveal_type(np.full_like(B, i8, dtype=np.int64)) # E: ndarray[Any, dtype[{int64}]] + +reveal_type(np.ones(1)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.ones([1, 1, 1])) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.ones(5, dtype=np.int64)) # E: ndarray[Any, dtype[{int64}]] +reveal_type(np.ones(5, dtype=int)) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.full(1, i8)) # E: ndarray[Any, dtype[Any]] +reveal_type(np.full([1, 1, 1], i8)) # E: ndarray[Any, dtype[Any]] +reveal_type(np.full(1, i8, dtype=np.float64)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.full(1, i8, dtype=float)) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.indices([1, 2, 3])) # E: ndarray[Any, dtype[{int_}]] +reveal_type(np.indices([1, 2, 3], sparse=True)) # E: tuple[ndarray[Any, dtype[{int_}]]] + +reveal_type(np.fromfunction(func, (3, 5))) # E: SubClass[{float64}] + +reveal_type(np.identity(10)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.identity(10, dtype=np.int64)) # E: ndarray[Any, dtype[{int64}]] +reveal_type(np.identity(10, dtype=int)) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.atleast_1d(A)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.atleast_1d(C)) # E: ndarray[Any, dtype[Any]] +reveal_type(np.atleast_1d(A, A)) # E: list[ndarray[Any, dtype[Any]]] +reveal_type(np.atleast_1d(A, C)) # E: list[ndarray[Any, dtype[Any]]] +reveal_type(np.atleast_1d(C, C)) # E: list[ndarray[Any, dtype[Any]]] + +reveal_type(np.atleast_2d(A)) # E: ndarray[Any, dtype[{float64}]] + +reveal_type(np.atleast_3d(A)) # E: ndarray[Any, dtype[{float64}]] + +reveal_type(np.vstack([A, A])) # E: ndarray[Any, Any] +reveal_type(np.vstack([A, C])) # E: ndarray[Any, dtype[Any]] +reveal_type(np.vstack([C, C])) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.hstack([A, A])) # E: ndarray[Any, Any] + +reveal_type(np.stack([A, A])) # E: Any +reveal_type(np.stack([A, C])) # E: ndarray[Any, dtype[Any]] +reveal_type(np.stack([C, C])) # E: ndarray[Any, dtype[Any]] +reveal_type(np.stack([A, A], axis=0)) # E: Any +reveal_type(np.stack([A, A], out=B)) # E: SubClass[{float64}] + +reveal_type(np.block([[A, A], [A, A]])) # E: ndarray[Any, Any] +reveal_type(np.block(C)) # E: ndarray[Any, dtype[Any]] diff --git a/numpy/typing/tests/data/reveal/arraypad.pyi b/numpy/typing/tests/data/reveal/arraypad.pyi new file mode 100644 index 000000000000..995f82b579e6 --- /dev/null +++ b/numpy/typing/tests/data/reveal/arraypad.pyi @@ -0,0 +1,21 @@ +from typing import List, Any, Mapping, Tuple, SupportsIndex + +import numpy as np +import numpy.typing as npt + +def mode_func( + ar: npt.NDArray[np.number[Any]], + width: Tuple[int, int], + iaxis: SupportsIndex, + kwargs: Mapping[str, Any], +) -> None: ... + +AR_i8: npt.NDArray[np.int64] +AR_f8: npt.NDArray[np.float64] +AR_LIKE: List[int] + +reveal_type(np.pad(AR_i8, (2, 3), "constant")) # E: ndarray[Any, dtype[{int64}]] +reveal_type(np.pad(AR_LIKE, (2, 3), "constant")) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.pad(AR_f8, (2, 3), mode_func)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.pad(AR_f8, (2, 3), mode_func, a=1, b=2)) # E: ndarray[Any, dtype[{float64}]] diff --git a/numpy/typing/tests/data/reveal/arrayprint.pyi b/numpy/typing/tests/data/reveal/arrayprint.pyi new file mode 100644 index 000000000000..e797097ebb94 --- /dev/null +++ b/numpy/typing/tests/data/reveal/arrayprint.pyi @@ -0,0 +1,19 @@ +from typing import Any, Callable +import numpy as np + +AR: np.ndarray[Any, Any] +func_float: Callable[[np.floating[Any]], str] +func_int: Callable[[np.integer[Any]], str] + +reveal_type(np.get_printoptions()) # E: TypedDict +reveal_type(np.array2string( # E: str + AR, formatter={'float_kind': func_float, 'int_kind': func_int} +)) +reveal_type(np.format_float_scientific(1.0)) # E: str +reveal_type(np.format_float_positional(1)) # E: str +reveal_type(np.array_repr(AR)) # E: str +reveal_type(np.array_str(AR)) # E: str + +reveal_type(np.printoptions()) # E: contextlib._GeneratorContextManager +with np.printoptions() as dct: + reveal_type(dct) # E: TypedDict diff --git a/numpy/typing/tests/data/reveal/arraysetops.pyi b/numpy/typing/tests/data/reveal/arraysetops.pyi new file mode 100644 index 000000000000..9deff8a8ea29 --- /dev/null +++ b/numpy/typing/tests/data/reveal/arraysetops.pyi @@ -0,0 +1,60 @@ +import numpy as np +import numpy.typing as npt + +AR_b: npt.NDArray[np.bool_] +AR_i8: npt.NDArray[np.int64] +AR_f8: npt.NDArray[np.float64] +AR_M: npt.NDArray[np.datetime64] +AR_O: npt.NDArray[np.object_] + +AR_LIKE_f8: list[float] + +reveal_type(np.ediff1d(AR_b)) # E: ndarray[Any, dtype[{int8}]] +reveal_type(np.ediff1d(AR_i8, to_end=[1, 2, 3])) # E: ndarray[Any, dtype[{int64}]] +reveal_type(np.ediff1d(AR_M)) # E: ndarray[Any, dtype[timedelta64]] +reveal_type(np.ediff1d(AR_O)) # E: ndarray[Any, dtype[object_]] +reveal_type(np.ediff1d(AR_LIKE_f8, to_begin=[1, 1.5])) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.intersect1d(AR_i8, AR_i8)) # E: ndarray[Any, dtype[{int64}]] +reveal_type(np.intersect1d(AR_M, AR_M, assume_unique=True)) # E: ndarray[Any, dtype[datetime64]] +reveal_type(np.intersect1d(AR_f8, AR_i8)) # E: ndarray[Any, dtype[Any]] +reveal_type(np.intersect1d(AR_f8, AR_f8, return_indices=True)) # E: Tuple[ndarray[Any, dtype[{float64}]], ndarray[Any, dtype[{intp}]], ndarray[Any, dtype[{intp}]]] + +reveal_type(np.setxor1d(AR_i8, AR_i8)) # E: ndarray[Any, dtype[{int64}]] +reveal_type(np.setxor1d(AR_M, AR_M, assume_unique=True)) # E: ndarray[Any, dtype[datetime64]] +reveal_type(np.setxor1d(AR_f8, AR_i8)) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.in1d(AR_i8, AR_i8)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.in1d(AR_M, AR_M, assume_unique=True)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.in1d(AR_f8, AR_i8)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.in1d(AR_f8, AR_LIKE_f8, invert=True)) # E: ndarray[Any, dtype[bool_]] + +reveal_type(np.isin(AR_i8, AR_i8)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.isin(AR_M, AR_M, assume_unique=True)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.isin(AR_f8, AR_i8)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.isin(AR_f8, AR_LIKE_f8, invert=True)) # E: ndarray[Any, dtype[bool_]] + +reveal_type(np.union1d(AR_i8, AR_i8)) # E: ndarray[Any, dtype[{int64}]] +reveal_type(np.union1d(AR_M, AR_M)) # E: ndarray[Any, dtype[datetime64]] +reveal_type(np.union1d(AR_f8, AR_i8)) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.setdiff1d(AR_i8, AR_i8)) # E: ndarray[Any, dtype[{int64}]] +reveal_type(np.setdiff1d(AR_M, AR_M, assume_unique=True)) # E: ndarray[Any, dtype[datetime64]] +reveal_type(np.setdiff1d(AR_f8, AR_i8)) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.unique(AR_f8)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.unique(AR_LIKE_f8, axis=0)) # E: ndarray[Any, dtype[Any]] +reveal_type(np.unique(AR_f8, return_index=True)) # E: Tuple[ndarray[Any, dtype[{float64}]], ndarray[Any, dtype[{intp}]]] +reveal_type(np.unique(AR_LIKE_f8, return_index=True)) # E: Tuple[ndarray[Any, dtype[Any]], ndarray[Any, dtype[{intp}]]] +reveal_type(np.unique(AR_f8, return_inverse=True)) # E: Tuple[ndarray[Any, dtype[{float64}]], ndarray[Any, dtype[{intp}]]] +reveal_type(np.unique(AR_LIKE_f8, return_inverse=True)) # E: Tuple[ndarray[Any, dtype[Any]], ndarray[Any, dtype[{intp}]]] +reveal_type(np.unique(AR_f8, return_counts=True)) # E: Tuple[ndarray[Any, dtype[{float64}]], ndarray[Any, dtype[{intp}]]] +reveal_type(np.unique(AR_LIKE_f8, return_counts=True)) # E: Tuple[ndarray[Any, dtype[Any]], ndarray[Any, dtype[{intp}]]] +reveal_type(np.unique(AR_f8, return_index=True, return_inverse=True)) # E: Tuple[ndarray[Any, dtype[{float64}]], ndarray[Any, dtype[{intp}]], ndarray[Any, dtype[{intp}]]] +reveal_type(np.unique(AR_LIKE_f8, return_index=True, return_inverse=True)) # E: Tuple[ndarray[Any, dtype[Any]], ndarray[Any, dtype[{intp}]], ndarray[Any, dtype[{intp}]]] +reveal_type(np.unique(AR_f8, return_index=True, return_counts=True)) # E: Tuple[ndarray[Any, dtype[{float64}]], ndarray[Any, dtype[{intp}]], ndarray[Any, dtype[{intp}]]] +reveal_type(np.unique(AR_LIKE_f8, return_index=True, return_counts=True)) # E: Tuple[ndarray[Any, dtype[Any]], ndarray[Any, dtype[{intp}]], ndarray[Any, dtype[{intp}]]] +reveal_type(np.unique(AR_f8, return_inverse=True, return_counts=True)) # E: Tuple[ndarray[Any, dtype[{float64}]], ndarray[Any, dtype[{intp}]], ndarray[Any, dtype[{intp}]]] +reveal_type(np.unique(AR_LIKE_f8, return_inverse=True, return_counts=True)) # E: Tuple[ndarray[Any, dtype[Any]], ndarray[Any, dtype[{intp}]], ndarray[Any, dtype[{intp}]]] +reveal_type(np.unique(AR_f8, return_index=True, return_inverse=True, return_counts=True)) # E: Tuple[ndarray[Any, dtype[{float64}]], ndarray[Any, dtype[{intp}]], ndarray[Any, dtype[{intp}]], ndarray[Any, dtype[{intp}]]] +reveal_type(np.unique(AR_LIKE_f8, return_index=True, return_inverse=True, return_counts=True)) # E: Tuple[ndarray[Any, dtype[Any]], ndarray[Any, dtype[{intp}]], ndarray[Any, dtype[{intp}]], ndarray[Any, dtype[{intp}]]] diff --git a/numpy/typing/tests/data/reveal/arrayterator.pyi b/numpy/typing/tests/data/reveal/arrayterator.pyi new file mode 100644 index 000000000000..2dab9d08c7fa --- /dev/null +++ b/numpy/typing/tests/data/reveal/arrayterator.pyi @@ -0,0 +1,24 @@ +from typing import Any +import numpy as np + +AR_i8: np.ndarray[Any, np.dtype[np.int64]] +ar_iter = np.lib.Arrayterator(AR_i8) + +reveal_type(ar_iter.var) # E: ndarray[Any, dtype[{int64}]] +reveal_type(ar_iter.buf_size) # E: Union[None, builtins.int] +reveal_type(ar_iter.start) # E: builtins.list[builtins.int] +reveal_type(ar_iter.stop) # E: builtins.list[builtins.int] +reveal_type(ar_iter.step) # E: builtins.list[builtins.int] +reveal_type(ar_iter.shape) # E: builtins.tuple[builtins.int] +reveal_type(ar_iter.flat) # E: typing.Generator[{int64}, None, None] + +reveal_type(ar_iter.__array__()) # E: ndarray[Any, dtype[{int64}]] + +for i in ar_iter: + reveal_type(i) # E: ndarray[Any, dtype[{int64}]] + +reveal_type(ar_iter[0]) # E: lib.arrayterator.Arrayterator[Any, dtype[{int64}]] +reveal_type(ar_iter[...]) # E: lib.arrayterator.Arrayterator[Any, dtype[{int64}]] +reveal_type(ar_iter[:]) # E: lib.arrayterator.Arrayterator[Any, dtype[{int64}]] +reveal_type(ar_iter[0, 0, 0]) # E: lib.arrayterator.Arrayterator[Any, dtype[{int64}]] +reveal_type(ar_iter[..., 0, :]) # E: lib.arrayterator.Arrayterator[Any, dtype[{int64}]] diff --git a/numpy/typing/tests/data/reveal/bitwise_ops.pyi b/numpy/typing/tests/data/reveal/bitwise_ops.pyi new file mode 100644 index 000000000000..f293ef65b58b --- /dev/null +++ b/numpy/typing/tests/data/reveal/bitwise_ops.pyi @@ -0,0 +1,131 @@ +import numpy as np + +i8 = np.int64(1) +u8 = np.uint64(1) + +i4 = np.int32(1) +u4 = np.uint32(1) + +b_ = np.bool_(1) + +b = bool(1) +i = int(1) + +AR = np.array([0, 1, 2], dtype=np.int32) +AR.setflags(write=False) + + +reveal_type(i8 << i8) # E: {int64} +reveal_type(i8 >> i8) # E: {int64} +reveal_type(i8 | i8) # E: {int64} +reveal_type(i8 ^ i8) # E: {int64} +reveal_type(i8 & i8) # E: {int64} + +reveal_type(i8 << AR) # E: Any +reveal_type(i8 >> AR) # E: Any +reveal_type(i8 | AR) # E: Any +reveal_type(i8 ^ AR) # E: Any +reveal_type(i8 & AR) # E: Any + +reveal_type(i4 << i4) # E: {int32} +reveal_type(i4 >> i4) # E: {int32} +reveal_type(i4 | i4) # E: {int32} +reveal_type(i4 ^ i4) # E: {int32} +reveal_type(i4 & i4) # E: {int32} + +reveal_type(i8 << i4) # E: {int64} +reveal_type(i8 >> i4) # E: {int64} +reveal_type(i8 | i4) # E: {int64} +reveal_type(i8 ^ i4) # E: {int64} +reveal_type(i8 & i4) # E: {int64} + +reveal_type(i8 << i) # E: {int64} +reveal_type(i8 >> i) # E: {int64} +reveal_type(i8 | i) # E: {int64} +reveal_type(i8 ^ i) # E: {int64} +reveal_type(i8 & i) # E: {int64} + +reveal_type(i8 << b_) # E: {int64} +reveal_type(i8 >> b_) # E: {int64} +reveal_type(i8 | b_) # E: {int64} +reveal_type(i8 ^ b_) # E: {int64} +reveal_type(i8 & b_) # E: {int64} + +reveal_type(i8 << b) # E: {int64} +reveal_type(i8 >> b) # E: {int64} +reveal_type(i8 | b) # E: {int64} +reveal_type(i8 ^ b) # E: {int64} +reveal_type(i8 & b) # E: {int64} + +reveal_type(u8 << u8) # E: {uint64} +reveal_type(u8 >> u8) # E: {uint64} +reveal_type(u8 | u8) # E: {uint64} +reveal_type(u8 ^ u8) # E: {uint64} +reveal_type(u8 & u8) # E: {uint64} + +reveal_type(u8 << AR) # E: Any +reveal_type(u8 >> AR) # E: Any +reveal_type(u8 | AR) # E: Any +reveal_type(u8 ^ AR) # E: Any +reveal_type(u8 & AR) # E: Any + +reveal_type(u4 << u4) # E: {uint32} +reveal_type(u4 >> u4) # E: {uint32} +reveal_type(u4 | u4) # E: {uint32} +reveal_type(u4 ^ u4) # E: {uint32} +reveal_type(u4 & u4) # E: {uint32} + +reveal_type(u4 << i4) # E: signedinteger[Any] +reveal_type(u4 >> i4) # E: signedinteger[Any] +reveal_type(u4 | i4) # E: signedinteger[Any] +reveal_type(u4 ^ i4) # E: signedinteger[Any] +reveal_type(u4 & i4) # E: signedinteger[Any] + +reveal_type(u4 << i) # E: signedinteger[Any] +reveal_type(u4 >> i) # E: signedinteger[Any] +reveal_type(u4 | i) # E: signedinteger[Any] +reveal_type(u4 ^ i) # E: signedinteger[Any] +reveal_type(u4 & i) # E: signedinteger[Any] + +reveal_type(u8 << b_) # E: {uint64} +reveal_type(u8 >> b_) # E: {uint64} +reveal_type(u8 | b_) # E: {uint64} +reveal_type(u8 ^ b_) # E: {uint64} +reveal_type(u8 & b_) # E: {uint64} + +reveal_type(u8 << b) # E: {uint64} +reveal_type(u8 >> b) # E: {uint64} +reveal_type(u8 | b) # E: {uint64} +reveal_type(u8 ^ b) # E: {uint64} +reveal_type(u8 & b) # E: {uint64} + +reveal_type(b_ << b_) # E: {int8} +reveal_type(b_ >> b_) # E: {int8} +reveal_type(b_ | b_) # E: bool_ +reveal_type(b_ ^ b_) # E: bool_ +reveal_type(b_ & b_) # E: bool_ + +reveal_type(b_ << AR) # E: Any +reveal_type(b_ >> AR) # E: Any +reveal_type(b_ | AR) # E: Any +reveal_type(b_ ^ AR) # E: Any +reveal_type(b_ & AR) # E: Any + +reveal_type(b_ << b) # E: {int8} +reveal_type(b_ >> b) # E: {int8} +reveal_type(b_ | b) # E: bool_ +reveal_type(b_ ^ b) # E: bool_ +reveal_type(b_ & b) # E: bool_ + +reveal_type(b_ << i) # E: {int_} +reveal_type(b_ >> i) # E: {int_} +reveal_type(b_ | i) # E: {int_} +reveal_type(b_ ^ i) # E: {int_} +reveal_type(b_ & i) # E: {int_} + +reveal_type(~i8) # E: {int64} +reveal_type(~i4) # E: {int32} +reveal_type(~u8) # E: {uint64} +reveal_type(~u4) # E: {uint32} +reveal_type(~b_) # E: bool_ +reveal_type(~AR) # E: Any diff --git a/numpy/typing/tests/data/reveal/char.pyi b/numpy/typing/tests/data/reveal/char.pyi new file mode 100644 index 000000000000..ce8c1b2690a9 --- /dev/null +++ b/numpy/typing/tests/data/reveal/char.pyi @@ -0,0 +1,147 @@ +import numpy as np +import numpy.typing as npt +from typing import Sequence + +AR_U: npt.NDArray[np.str_] +AR_S: npt.NDArray[np.bytes_] + +reveal_type(np.char.equal(AR_U, AR_U)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.char.equal(AR_S, AR_S)) # E: ndarray[Any, dtype[bool_]] + +reveal_type(np.char.not_equal(AR_U, AR_U)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.char.not_equal(AR_S, AR_S)) # E: ndarray[Any, dtype[bool_]] + +reveal_type(np.char.greater_equal(AR_U, AR_U)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.char.greater_equal(AR_S, AR_S)) # E: ndarray[Any, dtype[bool_]] + +reveal_type(np.char.less_equal(AR_U, AR_U)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.char.less_equal(AR_S, AR_S)) # E: ndarray[Any, dtype[bool_]] + +reveal_type(np.char.greater(AR_U, AR_U)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.char.greater(AR_S, AR_S)) # E: ndarray[Any, dtype[bool_]] + +reveal_type(np.char.less(AR_U, AR_U)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.char.less(AR_S, AR_S)) # E: ndarray[Any, dtype[bool_]] + +reveal_type(np.char.multiply(AR_U, 5)) # E: ndarray[Any, dtype[str_]] +reveal_type(np.char.multiply(AR_S, [5, 4, 3])) # E: ndarray[Any, dtype[bytes_]] + +reveal_type(np.char.mod(AR_U, "test")) # E: ndarray[Any, dtype[str_]] +reveal_type(np.char.mod(AR_S, "test")) # E: ndarray[Any, dtype[bytes_]] + +reveal_type(np.char.capitalize(AR_U)) # E: ndarray[Any, dtype[str_]] +reveal_type(np.char.capitalize(AR_S)) # E: ndarray[Any, dtype[bytes_]] + +reveal_type(np.char.center(AR_U, 5)) # E: ndarray[Any, dtype[str_]] +reveal_type(np.char.center(AR_S, [2, 3, 4], b"a")) # E: ndarray[Any, dtype[bytes_]] + +reveal_type(np.char.encode(AR_U)) # E: ndarray[Any, dtype[bytes_]] +reveal_type(np.char.decode(AR_S)) # E: ndarray[Any, dtype[str_]] + +reveal_type(np.char.expandtabs(AR_U)) # E: ndarray[Any, dtype[str_]] +reveal_type(np.char.expandtabs(AR_S, tabsize=4)) # E: ndarray[Any, dtype[bytes_]] + +reveal_type(np.char.join(AR_U, "_")) # E: ndarray[Any, dtype[str_]] +reveal_type(np.char.join(AR_S, [b"_", b""])) # E: ndarray[Any, dtype[bytes_]] + +reveal_type(np.char.ljust(AR_U, 5)) # E: ndarray[Any, dtype[str_]] +reveal_type(np.char.ljust(AR_S, [4, 3, 1], fillchar=[b"a", b"b", b"c"])) # E: ndarray[Any, dtype[bytes_]] +reveal_type(np.char.rjust(AR_U, 5)) # E: ndarray[Any, dtype[str_]] +reveal_type(np.char.rjust(AR_S, [4, 3, 1], fillchar=[b"a", b"b", b"c"])) # E: ndarray[Any, dtype[bytes_]] + +reveal_type(np.char.lstrip(AR_U)) # E: ndarray[Any, dtype[str_]] +reveal_type(np.char.lstrip(AR_S, chars=b"_")) # E: ndarray[Any, dtype[bytes_]] +reveal_type(np.char.rstrip(AR_U)) # E: ndarray[Any, dtype[str_]] +reveal_type(np.char.rstrip(AR_S, chars=b"_")) # E: ndarray[Any, dtype[bytes_]] +reveal_type(np.char.strip(AR_U)) # E: ndarray[Any, dtype[str_]] +reveal_type(np.char.strip(AR_S, chars=b"_")) # E: ndarray[Any, dtype[bytes_]] + +reveal_type(np.char.partition(AR_U, "\n")) # E: ndarray[Any, dtype[str_]] +reveal_type(np.char.partition(AR_S, [b"a", b"b", b"c"])) # E: ndarray[Any, dtype[bytes_]] +reveal_type(np.char.rpartition(AR_U, "\n")) # E: ndarray[Any, dtype[str_]] +reveal_type(np.char.rpartition(AR_S, [b"a", b"b", b"c"])) # E: ndarray[Any, dtype[bytes_]] + +reveal_type(np.char.replace(AR_U, "_", "-")) # E: ndarray[Any, dtype[str_]] +reveal_type(np.char.replace(AR_S, [b"_", b""], [b"a", b"b"])) # E: ndarray[Any, dtype[bytes_]] + +reveal_type(np.char.split(AR_U, "_")) # E: ndarray[Any, dtype[object_]] +reveal_type(np.char.split(AR_S, maxsplit=[1, 2, 3])) # E: ndarray[Any, dtype[object_]] +reveal_type(np.char.rsplit(AR_U, "_")) # E: ndarray[Any, dtype[object_]] +reveal_type(np.char.rsplit(AR_S, maxsplit=[1, 2, 3])) # E: ndarray[Any, dtype[object_]] + +reveal_type(np.char.splitlines(AR_U)) # E: ndarray[Any, dtype[object_]] +reveal_type(np.char.splitlines(AR_S, keepends=[True, True, False])) # E: ndarray[Any, dtype[object_]] + +reveal_type(np.char.swapcase(AR_U)) # E: ndarray[Any, dtype[str_]] +reveal_type(np.char.swapcase(AR_S)) # E: ndarray[Any, dtype[bytes_]] + +reveal_type(np.char.title(AR_U)) # E: ndarray[Any, dtype[str_]] +reveal_type(np.char.title(AR_S)) # E: ndarray[Any, dtype[bytes_]] + +reveal_type(np.char.upper(AR_U)) # E: ndarray[Any, dtype[str_]] +reveal_type(np.char.upper(AR_S)) # E: ndarray[Any, dtype[bytes_]] + +reveal_type(np.char.zfill(AR_U, 5)) # E: ndarray[Any, dtype[str_]] +reveal_type(np.char.zfill(AR_S, [2, 3, 4])) # E: ndarray[Any, dtype[bytes_]] + +reveal_type(np.char.count(AR_U, "a", start=[1, 2, 3])) # E: ndarray[Any, dtype[{int_}]] +reveal_type(np.char.count(AR_S, [b"a", b"b", b"c"], end=9)) # E: ndarray[Any, dtype[{int_}]] + +reveal_type(np.char.endswith(AR_U, "a", start=[1, 2, 3])) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.char.endswith(AR_S, [b"a", b"b", b"c"], end=9)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.char.startswith(AR_U, "a", start=[1, 2, 3])) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.char.startswith(AR_S, [b"a", b"b", b"c"], end=9)) # E: ndarray[Any, dtype[bool_]] + +reveal_type(np.char.find(AR_U, "a", start=[1, 2, 3])) # E: ndarray[Any, dtype[{int_}]] +reveal_type(np.char.find(AR_S, [b"a", b"b", b"c"], end=9)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(np.char.rfind(AR_U, "a", start=[1, 2, 3])) # E: ndarray[Any, dtype[{int_}]] +reveal_type(np.char.rfind(AR_S, [b"a", b"b", b"c"], end=9)) # E: ndarray[Any, dtype[{int_}]] + +reveal_type(np.char.index(AR_U, "a", start=[1, 2, 3])) # E: ndarray[Any, dtype[{int_}]] +reveal_type(np.char.index(AR_S, [b"a", b"b", b"c"], end=9)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(np.char.rindex(AR_U, "a", start=[1, 2, 3])) # E: ndarray[Any, dtype[{int_}]] +reveal_type(np.char.rindex(AR_S, [b"a", b"b", b"c"], end=9)) # E: ndarray[Any, dtype[{int_}]] + +reveal_type(np.char.isalpha(AR_U)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.char.isalpha(AR_S)) # E: ndarray[Any, dtype[bool_]] + +reveal_type(np.char.isalnum(AR_U)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.char.isalnum(AR_S)) # E: ndarray[Any, dtype[bool_]] + +reveal_type(np.char.isdecimal(AR_U)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.char.isdecimal(AR_S)) # E: ndarray[Any, dtype[bool_]] + +reveal_type(np.char.isdigit(AR_U)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.char.isdigit(AR_S)) # E: ndarray[Any, dtype[bool_]] + +reveal_type(np.char.islower(AR_U)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.char.islower(AR_S)) # E: ndarray[Any, dtype[bool_]] + +reveal_type(np.char.isnumeric(AR_U)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.char.isnumeric(AR_S)) # E: ndarray[Any, dtype[bool_]] + +reveal_type(np.char.isspace(AR_U)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.char.isspace(AR_S)) # E: ndarray[Any, dtype[bool_]] + +reveal_type(np.char.istitle(AR_U)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.char.istitle(AR_S)) # E: ndarray[Any, dtype[bool_]] + +reveal_type(np.char.isupper(AR_U)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.char.isupper(AR_S)) # E: ndarray[Any, dtype[bool_]] + +reveal_type(np.char.str_len(AR_U)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(np.char.str_len(AR_S)) # E: ndarray[Any, dtype[{int_}]] + +reveal_type(np.char.array(AR_U)) # E: chararray[Any, dtype[str_]] +reveal_type(np.char.array(AR_S, order="K")) # E: chararray[Any, dtype[bytes_]] +reveal_type(np.char.array("bob", copy=True)) # E: chararray[Any, dtype[str_]] +reveal_type(np.char.array(b"bob", itemsize=5)) # E: chararray[Any, dtype[bytes_]] +reveal_type(np.char.array(1, unicode=False)) # E: chararray[Any, dtype[bytes_]] +reveal_type(np.char.array(1, unicode=True)) # E: chararray[Any, dtype[str_]] + +reveal_type(np.char.asarray(AR_U)) # E: chararray[Any, dtype[str_]] +reveal_type(np.char.asarray(AR_S, order="K")) # E: chararray[Any, dtype[bytes_]] +reveal_type(np.char.asarray("bob")) # E: chararray[Any, dtype[str_]] +reveal_type(np.char.asarray(b"bob", itemsize=5)) # E: chararray[Any, dtype[bytes_]] +reveal_type(np.char.asarray(1, unicode=False)) # E: chararray[Any, dtype[bytes_]] +reveal_type(np.char.asarray(1, unicode=True)) # E: chararray[Any, dtype[str_]] diff --git a/numpy/typing/tests/data/reveal/chararray.pyi b/numpy/typing/tests/data/reveal/chararray.pyi new file mode 100644 index 000000000000..3da2e15993fe --- /dev/null +++ b/numpy/typing/tests/data/reveal/chararray.pyi @@ -0,0 +1,129 @@ +import numpy as np +from typing import Any + +AR_U: np.chararray[Any, np.dtype[np.str_]] +AR_S: np.chararray[Any, np.dtype[np.bytes_]] + +reveal_type(AR_U == AR_U) # E: ndarray[Any, dtype[bool_]] +reveal_type(AR_S == AR_S) # E: ndarray[Any, dtype[bool_]] + +reveal_type(AR_U != AR_U) # E: ndarray[Any, dtype[bool_]] +reveal_type(AR_S != AR_S) # E: ndarray[Any, dtype[bool_]] + +reveal_type(AR_U >= AR_U) # E: ndarray[Any, dtype[bool_]] +reveal_type(AR_S >= AR_S) # E: ndarray[Any, dtype[bool_]] + +reveal_type(AR_U <= AR_U) # E: ndarray[Any, dtype[bool_]] +reveal_type(AR_S <= AR_S) # E: ndarray[Any, dtype[bool_]] + +reveal_type(AR_U > AR_U) # E: ndarray[Any, dtype[bool_]] +reveal_type(AR_S > AR_S) # E: ndarray[Any, dtype[bool_]] + +reveal_type(AR_U < AR_U) # E: ndarray[Any, dtype[bool_]] +reveal_type(AR_S < AR_S) # E: ndarray[Any, dtype[bool_]] + +reveal_type(AR_U * 5) # E: chararray[Any, dtype[str_]] +reveal_type(AR_S * [5]) # E: chararray[Any, dtype[bytes_]] + +reveal_type(AR_U % "test") # E: chararray[Any, dtype[str_]] +reveal_type(AR_S % b"test") # E: chararray[Any, dtype[bytes_]] + +reveal_type(AR_U.capitalize()) # E: chararray[Any, dtype[str_]] +reveal_type(AR_S.capitalize()) # E: chararray[Any, dtype[bytes_]] + +reveal_type(AR_U.center(5)) # E: chararray[Any, dtype[str_]] +reveal_type(AR_S.center([2, 3, 4], b"a")) # E: chararray[Any, dtype[bytes_]] + +reveal_type(AR_U.encode()) # E: chararray[Any, dtype[bytes_]] +reveal_type(AR_S.decode()) # E: chararray[Any, dtype[str_]] + +reveal_type(AR_U.expandtabs()) # E: chararray[Any, dtype[str_]] +reveal_type(AR_S.expandtabs(tabsize=4)) # E: chararray[Any, dtype[bytes_]] + +reveal_type(AR_U.join("_")) # E: chararray[Any, dtype[str_]] +reveal_type(AR_S.join([b"_", b""])) # E: chararray[Any, dtype[bytes_]] + +reveal_type(AR_U.ljust(5)) # E: chararray[Any, dtype[str_]] +reveal_type(AR_S.ljust([4, 3, 1], fillchar=[b"a", b"b", b"c"])) # E: chararray[Any, dtype[bytes_]] +reveal_type(AR_U.rjust(5)) # E: chararray[Any, dtype[str_]] +reveal_type(AR_S.rjust([4, 3, 1], fillchar=[b"a", b"b", b"c"])) # E: chararray[Any, dtype[bytes_]] + +reveal_type(AR_U.lstrip()) # E: chararray[Any, dtype[str_]] +reveal_type(AR_S.lstrip(chars=b"_")) # E: chararray[Any, dtype[bytes_]] +reveal_type(AR_U.rstrip()) # E: chararray[Any, dtype[str_]] +reveal_type(AR_S.rstrip(chars=b"_")) # E: chararray[Any, dtype[bytes_]] +reveal_type(AR_U.strip()) # E: chararray[Any, dtype[str_]] +reveal_type(AR_S.strip(chars=b"_")) # E: chararray[Any, dtype[bytes_]] + +reveal_type(AR_U.partition("\n")) # E: chararray[Any, dtype[str_]] +reveal_type(AR_S.partition([b"a", b"b", b"c"])) # E: chararray[Any, dtype[bytes_]] +reveal_type(AR_U.rpartition("\n")) # E: chararray[Any, dtype[str_]] +reveal_type(AR_S.rpartition([b"a", b"b", b"c"])) # E: chararray[Any, dtype[bytes_]] + +reveal_type(AR_U.replace("_", "-")) # E: chararray[Any, dtype[str_]] +reveal_type(AR_S.replace([b"_", b""], [b"a", b"b"])) # E: chararray[Any, dtype[bytes_]] + +reveal_type(AR_U.split("_")) # E: ndarray[Any, dtype[object_]] +reveal_type(AR_S.split(maxsplit=[1, 2, 3])) # E: ndarray[Any, dtype[object_]] +reveal_type(AR_U.rsplit("_")) # E: ndarray[Any, dtype[object_]] +reveal_type(AR_S.rsplit(maxsplit=[1, 2, 3])) # E: ndarray[Any, dtype[object_]] + +reveal_type(AR_U.splitlines()) # E: ndarray[Any, dtype[object_]] +reveal_type(AR_S.splitlines(keepends=[True, True, False])) # E: ndarray[Any, dtype[object_]] + +reveal_type(AR_U.swapcase()) # E: chararray[Any, dtype[str_]] +reveal_type(AR_S.swapcase()) # E: chararray[Any, dtype[bytes_]] + +reveal_type(AR_U.title()) # E: chararray[Any, dtype[str_]] +reveal_type(AR_S.title()) # E: chararray[Any, dtype[bytes_]] + +reveal_type(AR_U.upper()) # E: chararray[Any, dtype[str_]] +reveal_type(AR_S.upper()) # E: chararray[Any, dtype[bytes_]] + +reveal_type(AR_U.zfill(5)) # E: chararray[Any, dtype[str_]] +reveal_type(AR_S.zfill([2, 3, 4])) # E: chararray[Any, dtype[bytes_]] + +reveal_type(AR_U.count("a", start=[1, 2, 3])) # E: ndarray[Any, dtype[{int_}]] +reveal_type(AR_S.count([b"a", b"b", b"c"], end=9)) # E: ndarray[Any, dtype[{int_}]] + +reveal_type(AR_U.endswith("a", start=[1, 2, 3])) # E: ndarray[Any, dtype[bool_]] +reveal_type(AR_S.endswith([b"a", b"b", b"c"], end=9)) # E: ndarray[Any, dtype[bool_]] +reveal_type(AR_U.startswith("a", start=[1, 2, 3])) # E: ndarray[Any, dtype[bool_]] +reveal_type(AR_S.startswith([b"a", b"b", b"c"], end=9)) # E: ndarray[Any, dtype[bool_]] + +reveal_type(AR_U.find("a", start=[1, 2, 3])) # E: ndarray[Any, dtype[{int_}]] +reveal_type(AR_S.find([b"a", b"b", b"c"], end=9)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(AR_U.rfind("a", start=[1, 2, 3])) # E: ndarray[Any, dtype[{int_}]] +reveal_type(AR_S.rfind([b"a", b"b", b"c"], end=9)) # E: ndarray[Any, dtype[{int_}]] + +reveal_type(AR_U.index("a", start=[1, 2, 3])) # E: ndarray[Any, dtype[{int_}]] +reveal_type(AR_S.index([b"a", b"b", b"c"], end=9)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(AR_U.rindex("a", start=[1, 2, 3])) # E: ndarray[Any, dtype[{int_}]] +reveal_type(AR_S.rindex([b"a", b"b", b"c"], end=9)) # E: ndarray[Any, dtype[{int_}]] + +reveal_type(AR_U.isalpha()) # E: ndarray[Any, dtype[bool_]] +reveal_type(AR_S.isalpha()) # E: ndarray[Any, dtype[bool_]] + +reveal_type(AR_U.isalnum()) # E: ndarray[Any, dtype[bool_]] +reveal_type(AR_S.isalnum()) # E: ndarray[Any, dtype[bool_]] + +reveal_type(AR_U.isdecimal()) # E: ndarray[Any, dtype[bool_]] +reveal_type(AR_S.isdecimal()) # E: ndarray[Any, dtype[bool_]] + +reveal_type(AR_U.isdigit()) # E: ndarray[Any, dtype[bool_]] +reveal_type(AR_S.isdigit()) # E: ndarray[Any, dtype[bool_]] + +reveal_type(AR_U.islower()) # E: ndarray[Any, dtype[bool_]] +reveal_type(AR_S.islower()) # E: ndarray[Any, dtype[bool_]] + +reveal_type(AR_U.isnumeric()) # E: ndarray[Any, dtype[bool_]] +reveal_type(AR_S.isnumeric()) # E: ndarray[Any, dtype[bool_]] + +reveal_type(AR_U.isspace()) # E: ndarray[Any, dtype[bool_]] +reveal_type(AR_S.isspace()) # E: ndarray[Any, dtype[bool_]] + +reveal_type(AR_U.istitle()) # E: ndarray[Any, dtype[bool_]] +reveal_type(AR_S.istitle()) # E: ndarray[Any, dtype[bool_]] + +reveal_type(AR_U.isupper()) # E: ndarray[Any, dtype[bool_]] +reveal_type(AR_S.isupper()) # E: ndarray[Any, dtype[bool_]] diff --git a/numpy/typing/tests/data/reveal/comparisons.pyi b/numpy/typing/tests/data/reveal/comparisons.pyi new file mode 100644 index 000000000000..ecd8ea6908e4 --- /dev/null +++ b/numpy/typing/tests/data/reveal/comparisons.pyi @@ -0,0 +1,252 @@ +import numpy as np + +c16 = np.complex128() +f8 = np.float64() +i8 = np.int64() +u8 = np.uint64() + +c8 = np.complex64() +f4 = np.float32() +i4 = np.int32() +u4 = np.uint32() + +dt = np.datetime64(0, "D") +td = np.timedelta64(0, "D") + +b_ = np.bool_() + +b = bool() +c = complex() +f = float() +i = int() + +AR = np.array([0], dtype=np.int64) +AR.setflags(write=False) + +SEQ = (0, 1, 2, 3, 4) + +# Time structures + +reveal_type(dt > dt) # E: bool_ + +reveal_type(td > td) # E: bool_ +reveal_type(td > i) # E: bool_ +reveal_type(td > i4) # E: bool_ +reveal_type(td > i8) # E: bool_ + +reveal_type(td > AR) # E: ndarray[Any, dtype[bool_]] +reveal_type(td > SEQ) # E: ndarray[Any, dtype[bool_]] +reveal_type(AR > SEQ) # E: ndarray[Any, dtype[bool_]] +reveal_type(AR > td) # E: ndarray[Any, dtype[bool_]] +reveal_type(SEQ > td) # E: ndarray[Any, dtype[bool_]] +reveal_type(SEQ > AR) # E: ndarray[Any, dtype[bool_]] + +# boolean + +reveal_type(b_ > b) # E: bool_ +reveal_type(b_ > b_) # E: bool_ +reveal_type(b_ > i) # E: bool_ +reveal_type(b_ > i8) # E: bool_ +reveal_type(b_ > i4) # E: bool_ +reveal_type(b_ > u8) # E: bool_ +reveal_type(b_ > u4) # E: bool_ +reveal_type(b_ > f) # E: bool_ +reveal_type(b_ > f8) # E: bool_ +reveal_type(b_ > f4) # E: bool_ +reveal_type(b_ > c) # E: bool_ +reveal_type(b_ > c16) # E: bool_ +reveal_type(b_ > c8) # E: bool_ +reveal_type(b_ > AR) # E: ndarray[Any, dtype[bool_]] +reveal_type(b_ > SEQ) # E: ndarray[Any, dtype[bool_]] + +# Complex + +reveal_type(c16 > c16) # E: bool_ +reveal_type(c16 > f8) # E: bool_ +reveal_type(c16 > i8) # E: bool_ +reveal_type(c16 > c8) # E: bool_ +reveal_type(c16 > f4) # E: bool_ +reveal_type(c16 > i4) # E: bool_ +reveal_type(c16 > b_) # E: bool_ +reveal_type(c16 > b) # E: bool_ +reveal_type(c16 > c) # E: bool_ +reveal_type(c16 > f) # E: bool_ +reveal_type(c16 > i) # E: bool_ +reveal_type(c16 > AR) # E: ndarray[Any, dtype[bool_]] +reveal_type(c16 > SEQ) # E: ndarray[Any, dtype[bool_]] + +reveal_type(c16 > c16) # E: bool_ +reveal_type(f8 > c16) # E: bool_ +reveal_type(i8 > c16) # E: bool_ +reveal_type(c8 > c16) # E: bool_ +reveal_type(f4 > c16) # E: bool_ +reveal_type(i4 > c16) # E: bool_ +reveal_type(b_ > c16) # E: bool_ +reveal_type(b > c16) # E: bool_ +reveal_type(c > c16) # E: bool_ +reveal_type(f > c16) # E: bool_ +reveal_type(i > c16) # E: bool_ +reveal_type(AR > c16) # E: ndarray[Any, dtype[bool_]] +reveal_type(SEQ > c16) # E: ndarray[Any, dtype[bool_]] + +reveal_type(c8 > c16) # E: bool_ +reveal_type(c8 > f8) # E: bool_ +reveal_type(c8 > i8) # E: bool_ +reveal_type(c8 > c8) # E: bool_ +reveal_type(c8 > f4) # E: bool_ +reveal_type(c8 > i4) # E: bool_ +reveal_type(c8 > b_) # E: bool_ +reveal_type(c8 > b) # E: bool_ +reveal_type(c8 > c) # E: bool_ +reveal_type(c8 > f) # E: bool_ +reveal_type(c8 > i) # E: bool_ +reveal_type(c8 > AR) # E: ndarray[Any, dtype[bool_]] +reveal_type(c8 > SEQ) # E: ndarray[Any, dtype[bool_]] + +reveal_type(c16 > c8) # E: bool_ +reveal_type(f8 > c8) # E: bool_ +reveal_type(i8 > c8) # E: bool_ +reveal_type(c8 > c8) # E: bool_ +reveal_type(f4 > c8) # E: bool_ +reveal_type(i4 > c8) # E: bool_ +reveal_type(b_ > c8) # E: bool_ +reveal_type(b > c8) # E: bool_ +reveal_type(c > c8) # E: bool_ +reveal_type(f > c8) # E: bool_ +reveal_type(i > c8) # E: bool_ +reveal_type(AR > c8) # E: ndarray[Any, dtype[bool_]] +reveal_type(SEQ > c8) # E: ndarray[Any, dtype[bool_]] + +# Float + +reveal_type(f8 > f8) # E: bool_ +reveal_type(f8 > i8) # E: bool_ +reveal_type(f8 > f4) # E: bool_ +reveal_type(f8 > i4) # E: bool_ +reveal_type(f8 > b_) # E: bool_ +reveal_type(f8 > b) # E: bool_ +reveal_type(f8 > c) # E: bool_ +reveal_type(f8 > f) # E: bool_ +reveal_type(f8 > i) # E: bool_ +reveal_type(f8 > AR) # E: ndarray[Any, dtype[bool_]] +reveal_type(f8 > SEQ) # E: ndarray[Any, dtype[bool_]] + +reveal_type(f8 > f8) # E: bool_ +reveal_type(i8 > f8) # E: bool_ +reveal_type(f4 > f8) # E: bool_ +reveal_type(i4 > f8) # E: bool_ +reveal_type(b_ > f8) # E: bool_ +reveal_type(b > f8) # E: bool_ +reveal_type(c > f8) # E: bool_ +reveal_type(f > f8) # E: bool_ +reveal_type(i > f8) # E: bool_ +reveal_type(AR > f8) # E: ndarray[Any, dtype[bool_]] +reveal_type(SEQ > f8) # E: ndarray[Any, dtype[bool_]] + +reveal_type(f4 > f8) # E: bool_ +reveal_type(f4 > i8) # E: bool_ +reveal_type(f4 > f4) # E: bool_ +reveal_type(f4 > i4) # E: bool_ +reveal_type(f4 > b_) # E: bool_ +reveal_type(f4 > b) # E: bool_ +reveal_type(f4 > c) # E: bool_ +reveal_type(f4 > f) # E: bool_ +reveal_type(f4 > i) # E: bool_ +reveal_type(f4 > AR) # E: ndarray[Any, dtype[bool_]] +reveal_type(f4 > SEQ) # E: ndarray[Any, dtype[bool_]] + +reveal_type(f8 > f4) # E: bool_ +reveal_type(i8 > f4) # E: bool_ +reveal_type(f4 > f4) # E: bool_ +reveal_type(i4 > f4) # E: bool_ +reveal_type(b_ > f4) # E: bool_ +reveal_type(b > f4) # E: bool_ +reveal_type(c > f4) # E: bool_ +reveal_type(f > f4) # E: bool_ +reveal_type(i > f4) # E: bool_ +reveal_type(AR > f4) # E: ndarray[Any, dtype[bool_]] +reveal_type(SEQ > f4) # E: ndarray[Any, dtype[bool_]] + +# Int + +reveal_type(i8 > i8) # E: bool_ +reveal_type(i8 > u8) # E: bool_ +reveal_type(i8 > i4) # E: bool_ +reveal_type(i8 > u4) # E: bool_ +reveal_type(i8 > b_) # E: bool_ +reveal_type(i8 > b) # E: bool_ +reveal_type(i8 > c) # E: bool_ +reveal_type(i8 > f) # E: bool_ +reveal_type(i8 > i) # E: bool_ +reveal_type(i8 > AR) # E: ndarray[Any, dtype[bool_]] +reveal_type(i8 > SEQ) # E: ndarray[Any, dtype[bool_]] + +reveal_type(u8 > u8) # E: bool_ +reveal_type(u8 > i4) # E: bool_ +reveal_type(u8 > u4) # E: bool_ +reveal_type(u8 > b_) # E: bool_ +reveal_type(u8 > b) # E: bool_ +reveal_type(u8 > c) # E: bool_ +reveal_type(u8 > f) # E: bool_ +reveal_type(u8 > i) # E: bool_ +reveal_type(u8 > AR) # E: ndarray[Any, dtype[bool_]] +reveal_type(u8 > SEQ) # E: ndarray[Any, dtype[bool_]] + +reveal_type(i8 > i8) # E: bool_ +reveal_type(u8 > i8) # E: bool_ +reveal_type(i4 > i8) # E: bool_ +reveal_type(u4 > i8) # E: bool_ +reveal_type(b_ > i8) # E: bool_ +reveal_type(b > i8) # E: bool_ +reveal_type(c > i8) # E: bool_ +reveal_type(f > i8) # E: bool_ +reveal_type(i > i8) # E: bool_ +reveal_type(AR > i8) # E: ndarray[Any, dtype[bool_]] +reveal_type(SEQ > i8) # E: ndarray[Any, dtype[bool_]] + +reveal_type(u8 > u8) # E: bool_ +reveal_type(i4 > u8) # E: bool_ +reveal_type(u4 > u8) # E: bool_ +reveal_type(b_ > u8) # E: bool_ +reveal_type(b > u8) # E: bool_ +reveal_type(c > u8) # E: bool_ +reveal_type(f > u8) # E: bool_ +reveal_type(i > u8) # E: bool_ +reveal_type(AR > u8) # E: ndarray[Any, dtype[bool_]] +reveal_type(SEQ > u8) # E: ndarray[Any, dtype[bool_]] + +reveal_type(i4 > i8) # E: bool_ +reveal_type(i4 > i4) # E: bool_ +reveal_type(i4 > i) # E: bool_ +reveal_type(i4 > b_) # E: bool_ +reveal_type(i4 > b) # E: bool_ +reveal_type(i4 > AR) # E: ndarray[Any, dtype[bool_]] +reveal_type(i4 > SEQ) # E: ndarray[Any, dtype[bool_]] + +reveal_type(u4 > i8) # E: bool_ +reveal_type(u4 > i4) # E: bool_ +reveal_type(u4 > u8) # E: bool_ +reveal_type(u4 > u4) # E: bool_ +reveal_type(u4 > i) # E: bool_ +reveal_type(u4 > b_) # E: bool_ +reveal_type(u4 > b) # E: bool_ +reveal_type(u4 > AR) # E: ndarray[Any, dtype[bool_]] +reveal_type(u4 > SEQ) # E: ndarray[Any, dtype[bool_]] + +reveal_type(i8 > i4) # E: bool_ +reveal_type(i4 > i4) # E: bool_ +reveal_type(i > i4) # E: bool_ +reveal_type(b_ > i4) # E: bool_ +reveal_type(b > i4) # E: bool_ +reveal_type(AR > i4) # E: ndarray[Any, dtype[bool_]] +reveal_type(SEQ > i4) # E: ndarray[Any, dtype[bool_]] + +reveal_type(i8 > u4) # E: bool_ +reveal_type(i4 > u4) # E: bool_ +reveal_type(u8 > u4) # E: bool_ +reveal_type(u4 > u4) # E: bool_ +reveal_type(b_ > u4) # E: bool_ +reveal_type(b > u4) # E: bool_ +reveal_type(i > u4) # E: bool_ +reveal_type(AR > u4) # E: ndarray[Any, dtype[bool_]] +reveal_type(SEQ > u4) # E: ndarray[Any, dtype[bool_]] diff --git a/numpy/typing/tests/data/reveal/constants.pyi b/numpy/typing/tests/data/reveal/constants.pyi new file mode 100644 index 000000000000..37f54ccdaba3 --- /dev/null +++ b/numpy/typing/tests/data/reveal/constants.pyi @@ -0,0 +1,52 @@ +import numpy as np + +reveal_type(np.Inf) # E: float +reveal_type(np.Infinity) # E: float +reveal_type(np.NAN) # E: float +reveal_type(np.NINF) # E: float +reveal_type(np.NZERO) # E: float +reveal_type(np.NaN) # E: float +reveal_type(np.PINF) # E: float +reveal_type(np.PZERO) # E: float +reveal_type(np.e) # E: float +reveal_type(np.euler_gamma) # E: float +reveal_type(np.inf) # E: float +reveal_type(np.infty) # E: float +reveal_type(np.nan) # E: float +reveal_type(np.pi) # E: float + +reveal_type(np.ALLOW_THREADS) # E: int +reveal_type(np.BUFSIZE) # E: Literal[8192] +reveal_type(np.CLIP) # E: Literal[0] +reveal_type(np.ERR_CALL) # E: Literal[3] +reveal_type(np.ERR_DEFAULT) # E: Literal[521] +reveal_type(np.ERR_IGNORE) # E: Literal[0] +reveal_type(np.ERR_LOG) # E: Literal[5] +reveal_type(np.ERR_PRINT) # E: Literal[4] +reveal_type(np.ERR_RAISE) # E: Literal[2] +reveal_type(np.ERR_WARN) # E: Literal[1] +reveal_type(np.FLOATING_POINT_SUPPORT) # E: Literal[1] +reveal_type(np.FPE_DIVIDEBYZERO) # E: Literal[1] +reveal_type(np.FPE_INVALID) # E: Literal[8] +reveal_type(np.FPE_OVERFLOW) # E: Literal[2] +reveal_type(np.FPE_UNDERFLOW) # E: Literal[4] +reveal_type(np.MAXDIMS) # E: Literal[32] +reveal_type(np.MAY_SHARE_BOUNDS) # E: Literal[0] +reveal_type(np.MAY_SHARE_EXACT) # E: Literal[-1] +reveal_type(np.RAISE) # E: Literal[2] +reveal_type(np.SHIFT_DIVIDEBYZERO) # E: Literal[0] +reveal_type(np.SHIFT_INVALID) # E: Literal[9] +reveal_type(np.SHIFT_OVERFLOW) # E: Literal[3] +reveal_type(np.SHIFT_UNDERFLOW) # E: Literal[6] +reveal_type(np.UFUNC_BUFSIZE_DEFAULT) # E: Literal[8192] +reveal_type(np.WRAP) # E: Literal[1] +reveal_type(np.tracemalloc_domain) # E: Literal[389047] + +reveal_type(np.little_endian) # E: bool +reveal_type(np.True_) # E: bool_ +reveal_type(np.False_) # E: bool_ + +reveal_type(np.UFUNC_PYVALS_NAME) # E: Literal['UFUNC_PYVALS'] + +reveal_type(np.sctypeDict) # E: dict +reveal_type(np.sctypes) # E: TypedDict diff --git a/numpy/typing/tests/data/reveal/ctypeslib.pyi b/numpy/typing/tests/data/reveal/ctypeslib.pyi new file mode 100644 index 000000000000..ccbdfe36e72b --- /dev/null +++ b/numpy/typing/tests/data/reveal/ctypeslib.pyi @@ -0,0 +1,87 @@ +import ctypes +from typing import Any + +import numpy as np +import numpy.typing as npt + +AR_bool: npt.NDArray[np.bool_] +AR_ubyte: npt.NDArray[np.ubyte] +AR_ushort: npt.NDArray[np.ushort] +AR_uintc: npt.NDArray[np.uintc] +AR_uint: npt.NDArray[np.uint] +AR_ulonglong: npt.NDArray[np.ulonglong] +AR_byte: npt.NDArray[np.byte] +AR_short: npt.NDArray[np.short] +AR_intc: npt.NDArray[np.intc] +AR_int: npt.NDArray[np.int_] +AR_longlong: npt.NDArray[np.longlong] +AR_single: npt.NDArray[np.single] +AR_double: npt.NDArray[np.double] +AR_longdouble: npt.NDArray[np.longdouble] +AR_void: npt.NDArray[np.void] + +pointer: ctypes.pointer[Any] + +reveal_type(np.ctypeslib.c_intp()) # E: {c_intp} + +reveal_type(np.ctypeslib.ndpointer()) # E: Type[ctypeslib._ndptr[None]] +reveal_type(np.ctypeslib.ndpointer(dtype=np.float64)) # E: Type[ctypeslib._ndptr[dtype[{float64}]]] +reveal_type(np.ctypeslib.ndpointer(dtype=float)) # E: Type[ctypeslib._ndptr[dtype[Any]]] +reveal_type(np.ctypeslib.ndpointer(shape=(10, 3))) # E: Type[ctypeslib._ndptr[None]] +reveal_type(np.ctypeslib.ndpointer(np.int64, shape=(10, 3))) # E: Type[ctypeslib._concrete_ndptr[dtype[{int64}]]] +reveal_type(np.ctypeslib.ndpointer(int, shape=(1,))) # E: Type[ctypeslib._concrete_ndptr[dtype[Any]]] + +reveal_type(np.ctypeslib.as_ctypes_type(np.bool_)) # E: Type[ctypes.c_bool] +reveal_type(np.ctypeslib.as_ctypes_type(np.ubyte)) # E: Type[{c_ubyte}] +reveal_type(np.ctypeslib.as_ctypes_type(np.ushort)) # E: Type[{c_ushort}] +reveal_type(np.ctypeslib.as_ctypes_type(np.uintc)) # E: Type[{c_uint}] +reveal_type(np.ctypeslib.as_ctypes_type(np.uint)) # E: Type[{c_ulong}] +reveal_type(np.ctypeslib.as_ctypes_type(np.ulonglong)) # E: Type[{c_ulonglong}] +reveal_type(np.ctypeslib.as_ctypes_type(np.byte)) # E: Type[{c_byte}] +reveal_type(np.ctypeslib.as_ctypes_type(np.short)) # E: Type[{c_short}] +reveal_type(np.ctypeslib.as_ctypes_type(np.intc)) # E: Type[{c_int}] +reveal_type(np.ctypeslib.as_ctypes_type(np.int_)) # E: Type[{c_long}] +reveal_type(np.ctypeslib.as_ctypes_type(np.longlong)) # E: Type[{c_longlong}] +reveal_type(np.ctypeslib.as_ctypes_type(np.single)) # E: Type[{c_float}] +reveal_type(np.ctypeslib.as_ctypes_type(np.double)) # E: Type[{c_double}] +reveal_type(np.ctypeslib.as_ctypes_type(np.longdouble)) # E: Type[{c_longdouble}] +reveal_type(np.ctypeslib.as_ctypes_type(ctypes.c_double)) # E: Type[{c_double}] +reveal_type(np.ctypeslib.as_ctypes_type("q")) # E: Type[ctypes.c_longlong] +reveal_type(np.ctypeslib.as_ctypes_type([("i8", np.int64), ("f8", np.float64)])) # E: Type[Any] +reveal_type(np.ctypeslib.as_ctypes_type("i8")) # E: Type[Any] +reveal_type(np.ctypeslib.as_ctypes_type("f8")) # E: Type[Any] + +reveal_type(np.ctypeslib.as_ctypes(AR_bool.take(0))) # E: ctypes.c_bool +reveal_type(np.ctypeslib.as_ctypes(AR_ubyte.take(0))) # E: {c_ubyte} +reveal_type(np.ctypeslib.as_ctypes(AR_ushort.take(0))) # E: {c_ushort} +reveal_type(np.ctypeslib.as_ctypes(AR_uintc.take(0))) # E: {c_uint} +reveal_type(np.ctypeslib.as_ctypes(AR_uint.take(0))) # E: {c_ulong} +reveal_type(np.ctypeslib.as_ctypes(AR_ulonglong.take(0))) # E: {c_ulonglong} +reveal_type(np.ctypeslib.as_ctypes(AR_byte.take(0))) # E: {c_byte} +reveal_type(np.ctypeslib.as_ctypes(AR_short.take(0))) # E: {c_short} +reveal_type(np.ctypeslib.as_ctypes(AR_intc.take(0))) # E: {c_int} +reveal_type(np.ctypeslib.as_ctypes(AR_int.take(0))) # E: {c_long} +reveal_type(np.ctypeslib.as_ctypes(AR_longlong.take(0))) # E: {c_longlong} +reveal_type(np.ctypeslib.as_ctypes(AR_single.take(0))) # E: {c_float} +reveal_type(np.ctypeslib.as_ctypes(AR_double.take(0))) # E: {c_double} +reveal_type(np.ctypeslib.as_ctypes(AR_longdouble.take(0))) # E: {c_longdouble} +reveal_type(np.ctypeslib.as_ctypes(AR_void.take(0))) # E: Any +reveal_type(np.ctypeslib.as_ctypes(AR_bool)) # E: ctypes.Array[ctypes.c_bool] +reveal_type(np.ctypeslib.as_ctypes(AR_ubyte)) # E: ctypes.Array[{c_ubyte}] +reveal_type(np.ctypeslib.as_ctypes(AR_ushort)) # E: ctypes.Array[{c_ushort}] +reveal_type(np.ctypeslib.as_ctypes(AR_uintc)) # E: ctypes.Array[{c_uint}] +reveal_type(np.ctypeslib.as_ctypes(AR_uint)) # E: ctypes.Array[{c_ulong}] +reveal_type(np.ctypeslib.as_ctypes(AR_ulonglong)) # E: ctypes.Array[{c_ulonglong}] +reveal_type(np.ctypeslib.as_ctypes(AR_byte)) # E: ctypes.Array[{c_byte}] +reveal_type(np.ctypeslib.as_ctypes(AR_short)) # E: ctypes.Array[{c_short}] +reveal_type(np.ctypeslib.as_ctypes(AR_intc)) # E: ctypes.Array[{c_int}] +reveal_type(np.ctypeslib.as_ctypes(AR_int)) # E: ctypes.Array[{c_long}] +reveal_type(np.ctypeslib.as_ctypes(AR_longlong)) # E: ctypes.Array[{c_longlong}] +reveal_type(np.ctypeslib.as_ctypes(AR_single)) # E: ctypes.Array[{c_float}] +reveal_type(np.ctypeslib.as_ctypes(AR_double)) # E: ctypes.Array[{c_double}] +reveal_type(np.ctypeslib.as_ctypes(AR_longdouble)) # E: ctypes.Array[{c_longdouble}] +reveal_type(np.ctypeslib.as_ctypes(AR_void)) # E: ctypes.Array[Any] + +reveal_type(np.ctypeslib.as_array(AR_ubyte)) # E: ndarray[Any, dtype[{ubyte}]] +reveal_type(np.ctypeslib.as_array(1)) # E: ndarray[Any, dtype[Any]] +reveal_type(np.ctypeslib.as_array(pointer)) # E: ndarray[Any, dtype[Any]] diff --git a/numpy/typing/tests/data/reveal/datasource.pyi b/numpy/typing/tests/data/reveal/datasource.pyi new file mode 100644 index 000000000000..245ac7649e96 --- /dev/null +++ b/numpy/typing/tests/data/reveal/datasource.pyi @@ -0,0 +1,21 @@ +from pathlib import Path +import numpy as np + +path1: Path +path2: str + +d1 = np.DataSource(path1) +d2 = np.DataSource(path2) +d3 = np.DataSource(None) + +reveal_type(d1.abspath("...")) # E: str +reveal_type(d2.abspath("...")) # E: str +reveal_type(d3.abspath("...")) # E: str + +reveal_type(d1.exists("...")) # E: bool +reveal_type(d2.exists("...")) # E: bool +reveal_type(d3.exists("...")) # E: bool + +reveal_type(d1.open("...", "r")) # E: IO[Any] +reveal_type(d2.open("...", encoding="utf8")) # E: IO[Any] +reveal_type(d3.open("...", newline="/n")) # E: IO[Any] diff --git a/numpy/typing/tests/data/reveal/dtype.pyi b/numpy/typing/tests/data/reveal/dtype.pyi new file mode 100644 index 000000000000..934d7da5efe2 --- /dev/null +++ b/numpy/typing/tests/data/reveal/dtype.pyi @@ -0,0 +1,76 @@ +import ctypes as ct +import numpy as np + +dtype_U: np.dtype[np.str_] +dtype_V: np.dtype[np.void] +dtype_i8: np.dtype[np.int64] + +reveal_type(np.dtype(np.float64)) # E: dtype[{float64}] +reveal_type(np.dtype(np.int64)) # E: dtype[{int64}] + +# String aliases +reveal_type(np.dtype("float64")) # E: dtype[{float64}] +reveal_type(np.dtype("float32")) # E: dtype[{float32}] +reveal_type(np.dtype("int64")) # E: dtype[{int64}] +reveal_type(np.dtype("int32")) # E: dtype[{int32}] +reveal_type(np.dtype("bool")) # E: dtype[bool_] +reveal_type(np.dtype("bytes")) # E: dtype[bytes_] +reveal_type(np.dtype("str")) # E: dtype[str_] + +# Python types +reveal_type(np.dtype(complex)) # E: dtype[{cdouble}] +reveal_type(np.dtype(float)) # E: dtype[{double}] +reveal_type(np.dtype(int)) # E: dtype[{int_}] +reveal_type(np.dtype(bool)) # E: dtype[bool_] +reveal_type(np.dtype(str)) # E: dtype[str_] +reveal_type(np.dtype(bytes)) # E: dtype[bytes_] +reveal_type(np.dtype(object)) # E: dtype[object_] + +# ctypes +reveal_type(np.dtype(ct.c_double)) # E: dtype[{double}] +reveal_type(np.dtype(ct.c_longlong)) # E: dtype[{longlong}] +reveal_type(np.dtype(ct.c_uint32)) # E: dtype[{uint32}] +reveal_type(np.dtype(ct.c_bool)) # E: dtype[bool_] +reveal_type(np.dtype(ct.c_char)) # E: dtype[bytes_] +reveal_type(np.dtype(ct.py_object)) # E: dtype[object_] + +# Special case for None +reveal_type(np.dtype(None)) # E: dtype[{double}] + +# Dtypes of dtypes +reveal_type(np.dtype(np.dtype(np.float64))) # E: dtype[{float64}] + +# Parameterized dtypes +reveal_type(np.dtype("S8")) # E: dtype + +# Void +reveal_type(np.dtype(("U", 10))) # E: dtype[void] + +# Methods and attributes +reveal_type(dtype_U.base) # E: dtype[Any] +reveal_type(dtype_U.subdtype) # E: Union[None, Tuple[dtype[Any], builtins.tuple[builtins.int]]] +reveal_type(dtype_U.newbyteorder()) # E: dtype[str_] +reveal_type(dtype_U.type) # E: Type[str_] +reveal_type(dtype_U.name) # E: str +reveal_type(dtype_U.names) # E: Union[None, builtins.tuple[builtins.str]] + +reveal_type(dtype_U * 0) # E: dtype[str_] +reveal_type(dtype_U * 1) # E: dtype[str_] +reveal_type(dtype_U * 2) # E: dtype[str_] + +reveal_type(dtype_i8 * 0) # E: dtype[void] +reveal_type(dtype_i8 * 1) # E: dtype[{int64}] +reveal_type(dtype_i8 * 2) # E: dtype[void] + +reveal_type(0 * dtype_U) # E: dtype[str_] +reveal_type(1 * dtype_U) # E: dtype[str_] +reveal_type(2 * dtype_U) # E: dtype[str_] + +reveal_type(0 * dtype_i8) # E: dtype[Any] +reveal_type(1 * dtype_i8) # E: dtype[Any] +reveal_type(2 * dtype_i8) # E: dtype[Any] + +reveal_type(dtype_V["f0"]) # E: dtype[Any] +reveal_type(dtype_V[0]) # E: dtype[Any] +reveal_type(dtype_V[["f0", "f1"]]) # E: dtype[void] +reveal_type(dtype_V[["f0"]]) # E: dtype[void] diff --git a/numpy/typing/tests/data/reveal/einsumfunc.pyi b/numpy/typing/tests/data/reveal/einsumfunc.pyi new file mode 100644 index 000000000000..5b07e6d3c803 --- /dev/null +++ b/numpy/typing/tests/data/reveal/einsumfunc.pyi @@ -0,0 +1,32 @@ +from typing import List, Any +import numpy as np + +AR_LIKE_b: List[bool] +AR_LIKE_u: List[np.uint32] +AR_LIKE_i: List[int] +AR_LIKE_f: List[float] +AR_LIKE_c: List[complex] +AR_LIKE_U: List[str] + +OUT_f: np.ndarray[Any, np.dtype[np.float64]] + +reveal_type(np.einsum("i,i->i", AR_LIKE_b, AR_LIKE_b)) # E: Any +reveal_type(np.einsum("i,i->i", AR_LIKE_u, AR_LIKE_u)) # E: Any +reveal_type(np.einsum("i,i->i", AR_LIKE_i, AR_LIKE_i)) # E: Any +reveal_type(np.einsum("i,i->i", AR_LIKE_f, AR_LIKE_f)) # E: Any +reveal_type(np.einsum("i,i->i", AR_LIKE_c, AR_LIKE_c)) # E: Any +reveal_type(np.einsum("i,i->i", AR_LIKE_b, AR_LIKE_i)) # E: Any +reveal_type(np.einsum("i,i,i,i->i", AR_LIKE_b, AR_LIKE_u, AR_LIKE_i, AR_LIKE_c)) # E: Any + +reveal_type(np.einsum("i,i->i", AR_LIKE_c, AR_LIKE_c, out=OUT_f)) # E: ndarray[Any, dtype[{float64}] +reveal_type(np.einsum("i,i->i", AR_LIKE_U, AR_LIKE_U, dtype=bool, casting="unsafe", out=OUT_f)) # E: ndarray[Any, dtype[{float64}] +reveal_type(np.einsum("i,i->i", AR_LIKE_f, AR_LIKE_f, dtype="c16")) # E: Any +reveal_type(np.einsum("i,i->i", AR_LIKE_U, AR_LIKE_U, dtype=bool, casting="unsafe")) # E: Any + +reveal_type(np.einsum_path("i,i->i", AR_LIKE_b, AR_LIKE_b)) # E: Tuple[builtins.list[Any], builtins.str] +reveal_type(np.einsum_path("i,i->i", AR_LIKE_u, AR_LIKE_u)) # E: Tuple[builtins.list[Any], builtins.str] +reveal_type(np.einsum_path("i,i->i", AR_LIKE_i, AR_LIKE_i)) # E: Tuple[builtins.list[Any], builtins.str] +reveal_type(np.einsum_path("i,i->i", AR_LIKE_f, AR_LIKE_f)) # E: Tuple[builtins.list[Any], builtins.str] +reveal_type(np.einsum_path("i,i->i", AR_LIKE_c, AR_LIKE_c)) # E: Tuple[builtins.list[Any], builtins.str] +reveal_type(np.einsum_path("i,i->i", AR_LIKE_b, AR_LIKE_i)) # E: Tuple[builtins.list[Any], builtins.str] +reveal_type(np.einsum_path("i,i,i,i->i", AR_LIKE_b, AR_LIKE_u, AR_LIKE_i, AR_LIKE_c)) # E: Tuple[builtins.list[Any], builtins.str] diff --git a/numpy/typing/tests/data/reveal/fft.pyi b/numpy/typing/tests/data/reveal/fft.pyi new file mode 100644 index 000000000000..0667938e4478 --- /dev/null +++ b/numpy/typing/tests/data/reveal/fft.pyi @@ -0,0 +1,35 @@ +import numpy as np +import numpy.typing as npt + +AR_f8: npt.NDArray[np.float64] +AR_c16: npt.NDArray[np.complex128] +AR_LIKE_f8: list[float] + +reveal_type(np.fft.fftshift(AR_f8)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.fft.fftshift(AR_LIKE_f8, axes=0)) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.fft.ifftshift(AR_f8)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.fft.ifftshift(AR_LIKE_f8, axes=0)) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.fft.fftfreq(5, AR_f8)) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.fft.fftfreq(np.int64(), AR_c16)) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] + +reveal_type(np.fft.fftfreq(5, AR_f8)) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.fft.fftfreq(np.int64(), AR_c16)) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] + +reveal_type(np.fft.fft(AR_f8)) # E: ndarray[Any, dtype[{complex128}]] +reveal_type(np.fft.ifft(AR_f8, axis=1)) # E: ndarray[Any, dtype[{complex128}]] +reveal_type(np.fft.rfft(AR_f8, n=None)) # E: ndarray[Any, dtype[{complex128}]] +reveal_type(np.fft.irfft(AR_f8, norm="ortho")) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.fft.hfft(AR_f8, n=2)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.fft.ihfft(AR_f8)) # E: ndarray[Any, dtype[{complex128}]] + +reveal_type(np.fft.fftn(AR_f8)) # E: ndarray[Any, dtype[{complex128}]] +reveal_type(np.fft.ifftn(AR_f8)) # E: ndarray[Any, dtype[{complex128}]] +reveal_type(np.fft.rfftn(AR_f8)) # E: ndarray[Any, dtype[{complex128}]] +reveal_type(np.fft.irfftn(AR_f8)) # E: ndarray[Any, dtype[{float64}]] + +reveal_type(np.fft.rfft2(AR_f8)) # E: ndarray[Any, dtype[{complex128}]] +reveal_type(np.fft.ifft2(AR_f8)) # E: ndarray[Any, dtype[{complex128}]] +reveal_type(np.fft.fft2(AR_f8)) # E: ndarray[Any, dtype[{complex128}]] +reveal_type(np.fft.irfft2(AR_f8)) # E: ndarray[Any, dtype[{float64}]] diff --git a/numpy/typing/tests/data/reveal/flatiter.pyi b/numpy/typing/tests/data/reveal/flatiter.pyi new file mode 100644 index 000000000000..ef89acb58a2a --- /dev/null +++ b/numpy/typing/tests/data/reveal/flatiter.pyi @@ -0,0 +1,17 @@ +from typing import Any +import numpy as np + +a: np.flatiter[np.ndarray[Any, np.dtype[np.str_]]] + +reveal_type(a.base) # E: ndarray[Any, dtype[str_]] +reveal_type(a.copy()) # E: ndarray[Any, dtype[str_]] +reveal_type(a.coords) # E: tuple[builtins.int] +reveal_type(a.index) # E: int +reveal_type(iter(a)) # E: Iterator[str_] +reveal_type(next(a)) # E: str_ +reveal_type(a[0]) # E: str_ +reveal_type(a[[0, 1, 2]]) # E: ndarray[Any, dtype[str_]] +reveal_type(a[...]) # E: ndarray[Any, dtype[str_]] +reveal_type(a[:]) # E: ndarray[Any, dtype[str_]] +reveal_type(a.__array__()) # E: ndarray[Any, dtype[str_]] +reveal_type(a.__array__(np.dtype(np.float64))) # E: ndarray[Any, dtype[{float64}]] diff --git a/numpy/typing/tests/data/reveal/fromnumeric.pyi b/numpy/typing/tests/data/reveal/fromnumeric.pyi new file mode 100644 index 000000000000..2ee1952cf7bf --- /dev/null +++ b/numpy/typing/tests/data/reveal/fromnumeric.pyi @@ -0,0 +1,264 @@ +"""Tests for :mod:`core.fromnumeric`.""" + +import numpy as np + +A = np.array(True, ndmin=2, dtype=bool) +B = np.array(1.0, ndmin=2, dtype=np.float32) +A.setflags(write=False) +B.setflags(write=False) + +a = np.bool_(True) +b = np.float32(1.0) +c = 1.0 +d = np.array(1.0, dtype=np.float32) # writeable + +reveal_type(np.take(a, 0)) # E: Any +reveal_type(np.take(b, 0)) # E: Any +reveal_type(np.take(c, 0)) # E: Any +reveal_type(np.take(A, 0)) # E: Any +reveal_type(np.take(B, 0)) # E: Any +reveal_type(np.take(A, [0])) # E: Any +reveal_type(np.take(B, [0])) # E: Any + +reveal_type(np.reshape(a, 1)) # E: ndarray[Any, Any] +reveal_type(np.reshape(b, 1)) # E: ndarray[Any, Any] +reveal_type(np.reshape(c, 1)) # E: ndarray[Any, Any] +reveal_type(np.reshape(A, 1)) # E: ndarray[Any, Any] +reveal_type(np.reshape(B, 1)) # E: ndarray[Any, Any] + +reveal_type(np.choose(a, [True, True])) # E: Any +reveal_type(np.choose(A, [True, True])) # E: Any + +reveal_type(np.repeat(a, 1)) # E: ndarray[Any, Any] +reveal_type(np.repeat(b, 1)) # E: ndarray[Any, Any] +reveal_type(np.repeat(c, 1)) # E: ndarray[Any, Any] +reveal_type(np.repeat(A, 1)) # E: ndarray[Any, Any] +reveal_type(np.repeat(B, 1)) # E: ndarray[Any, Any] + +# TODO: Add tests for np.put() + +reveal_type(np.swapaxes(A, 0, 0)) # E: ndarray[Any, Any] +reveal_type(np.swapaxes(B, 0, 0)) # E: ndarray[Any, Any] + +reveal_type(np.transpose(a)) # E: ndarray[Any, Any] +reveal_type(np.transpose(b)) # E: ndarray[Any, Any] +reveal_type(np.transpose(c)) # E: ndarray[Any, Any] +reveal_type(np.transpose(A)) # E: ndarray[Any, Any] +reveal_type(np.transpose(B)) # E: ndarray[Any, Any] + +reveal_type(np.partition(a, 0, axis=None)) # E: ndarray[Any, Any] +reveal_type(np.partition(b, 0, axis=None)) # E: ndarray[Any, Any] +reveal_type(np.partition(c, 0, axis=None)) # E: ndarray[Any, Any] +reveal_type(np.partition(A, 0)) # E: ndarray[Any, Any] +reveal_type(np.partition(B, 0)) # E: ndarray[Any, Any] + +reveal_type(np.argpartition(a, 0)) # E: Any +reveal_type(np.argpartition(b, 0)) # E: Any +reveal_type(np.argpartition(c, 0)) # E: Any +reveal_type(np.argpartition(A, 0)) # E: Any +reveal_type(np.argpartition(B, 0)) # E: Any + +reveal_type(np.sort(A, 0)) # E: ndarray[Any, Any] +reveal_type(np.sort(B, 0)) # E: ndarray[Any, Any] + +reveal_type(np.argsort(A, 0)) # E: ndarray[Any, Any] +reveal_type(np.argsort(B, 0)) # E: ndarray[Any, Any] + +reveal_type(np.argmax(A)) # E: {intp} +reveal_type(np.argmax(B)) # E: {intp} +reveal_type(np.argmax(A, axis=0)) # E: Any +reveal_type(np.argmax(B, axis=0)) # E: Any + +reveal_type(np.argmin(A)) # E: {intp} +reveal_type(np.argmin(B)) # E: {intp} +reveal_type(np.argmin(A, axis=0)) # E: Any +reveal_type(np.argmin(B, axis=0)) # E: Any + +reveal_type(np.searchsorted(A[0], 0)) # E: {intp} +reveal_type(np.searchsorted(B[0], 0)) # E: {intp} +reveal_type(np.searchsorted(A[0], [0])) # E: ndarray[Any, Any] +reveal_type(np.searchsorted(B[0], [0])) # E: ndarray[Any, Any] + +reveal_type(np.resize(a, (5, 5))) # E: ndarray[Any, Any] +reveal_type(np.resize(b, (5, 5))) # E: ndarray[Any, Any] +reveal_type(np.resize(c, (5, 5))) # E: ndarray[Any, Any] +reveal_type(np.resize(A, (5, 5))) # E: ndarray[Any, Any] +reveal_type(np.resize(B, (5, 5))) # E: ndarray[Any, Any] + +reveal_type(np.squeeze(a)) # E: bool_ +reveal_type(np.squeeze(b)) # E: {float32} +reveal_type(np.squeeze(c)) # E: ndarray[Any, Any] +reveal_type(np.squeeze(A)) # E: ndarray[Any, Any] +reveal_type(np.squeeze(B)) # E: ndarray[Any, Any] + +reveal_type(np.diagonal(A)) # E: ndarray[Any, Any] +reveal_type(np.diagonal(B)) # E: ndarray[Any, Any] + +reveal_type(np.trace(A)) # E: Any +reveal_type(np.trace(B)) # E: Any + +reveal_type(np.ravel(a)) # E: ndarray[Any, Any] +reveal_type(np.ravel(b)) # E: ndarray[Any, Any] +reveal_type(np.ravel(c)) # E: ndarray[Any, Any] +reveal_type(np.ravel(A)) # E: ndarray[Any, Any] +reveal_type(np.ravel(B)) # E: ndarray[Any, Any] + +reveal_type(np.nonzero(a)) # E: tuple[ndarray[Any, Any]] +reveal_type(np.nonzero(b)) # E: tuple[ndarray[Any, Any]] +reveal_type(np.nonzero(c)) # E: tuple[ndarray[Any, Any]] +reveal_type(np.nonzero(A)) # E: tuple[ndarray[Any, Any]] +reveal_type(np.nonzero(B)) # E: tuple[ndarray[Any, Any]] + +reveal_type(np.shape(a)) # E: tuple[builtins.int] +reveal_type(np.shape(b)) # E: tuple[builtins.int] +reveal_type(np.shape(c)) # E: tuple[builtins.int] +reveal_type(np.shape(A)) # E: tuple[builtins.int] +reveal_type(np.shape(B)) # E: tuple[builtins.int] + +reveal_type(np.compress([True], a)) # E: ndarray[Any, Any] +reveal_type(np.compress([True], b)) # E: ndarray[Any, Any] +reveal_type(np.compress([True], c)) # E: ndarray[Any, Any] +reveal_type(np.compress([True], A)) # E: ndarray[Any, Any] +reveal_type(np.compress([True], B)) # E: ndarray[Any, Any] + +reveal_type(np.clip(a, 0, 1.0)) # E: Any +reveal_type(np.clip(b, -1, 1)) # E: Any +reveal_type(np.clip(c, 0, 1)) # E: Any +reveal_type(np.clip(A, 0, 1)) # E: Any +reveal_type(np.clip(B, 0, 1)) # E: Any + +reveal_type(np.sum(a)) # E: Any +reveal_type(np.sum(b)) # E: Any +reveal_type(np.sum(c)) # E: Any +reveal_type(np.sum(A)) # E: Any +reveal_type(np.sum(B)) # E: Any +reveal_type(np.sum(A, axis=0)) # E: Any +reveal_type(np.sum(B, axis=0)) # E: Any + +reveal_type(np.all(a)) # E: bool_ +reveal_type(np.all(b)) # E: bool_ +reveal_type(np.all(c)) # E: bool_ +reveal_type(np.all(A)) # E: bool_ +reveal_type(np.all(B)) # E: bool_ +reveal_type(np.all(A, axis=0)) # E: Any +reveal_type(np.all(B, axis=0)) # E: Any +reveal_type(np.all(A, keepdims=True)) # E: Any +reveal_type(np.all(B, keepdims=True)) # E: Any + +reveal_type(np.any(a)) # E: bool_ +reveal_type(np.any(b)) # E: bool_ +reveal_type(np.any(c)) # E: bool_ +reveal_type(np.any(A)) # E: bool_ +reveal_type(np.any(B)) # E: bool_ +reveal_type(np.any(A, axis=0)) # E: Any +reveal_type(np.any(B, axis=0)) # E: Any +reveal_type(np.any(A, keepdims=True)) # E: Any +reveal_type(np.any(B, keepdims=True)) # E: Any + +reveal_type(np.cumsum(a)) # E: ndarray[Any, Any] +reveal_type(np.cumsum(b)) # E: ndarray[Any, Any] +reveal_type(np.cumsum(c)) # E: ndarray[Any, Any] +reveal_type(np.cumsum(A)) # E: ndarray[Any, Any] +reveal_type(np.cumsum(B)) # E: ndarray[Any, Any] + +reveal_type(np.ptp(a)) # E: Any +reveal_type(np.ptp(b)) # E: Any +reveal_type(np.ptp(c)) # E: Any +reveal_type(np.ptp(A)) # E: Any +reveal_type(np.ptp(B)) # E: Any +reveal_type(np.ptp(A, axis=0)) # E: Any +reveal_type(np.ptp(B, axis=0)) # E: Any +reveal_type(np.ptp(A, keepdims=True)) # E: Any +reveal_type(np.ptp(B, keepdims=True)) # E: Any + +reveal_type(np.amax(a)) # E: Any +reveal_type(np.amax(b)) # E: Any +reveal_type(np.amax(c)) # E: Any +reveal_type(np.amax(A)) # E: Any +reveal_type(np.amax(B)) # E: Any +reveal_type(np.amax(A, axis=0)) # E: Any +reveal_type(np.amax(B, axis=0)) # E: Any +reveal_type(np.amax(A, keepdims=True)) # E: Any +reveal_type(np.amax(B, keepdims=True)) # E: Any + +reveal_type(np.amin(a)) # E: Any +reveal_type(np.amin(b)) # E: Any +reveal_type(np.amin(c)) # E: Any +reveal_type(np.amin(A)) # E: Any +reveal_type(np.amin(B)) # E: Any +reveal_type(np.amin(A, axis=0)) # E: Any +reveal_type(np.amin(B, axis=0)) # E: Any +reveal_type(np.amin(A, keepdims=True)) # E: Any +reveal_type(np.amin(B, keepdims=True)) # E: Any + +reveal_type(np.prod(a)) # E: Any +reveal_type(np.prod(b)) # E: Any +reveal_type(np.prod(c)) # E: Any +reveal_type(np.prod(A)) # E: Any +reveal_type(np.prod(B)) # E: Any +reveal_type(np.prod(A, axis=0)) # E: Any +reveal_type(np.prod(B, axis=0)) # E: Any +reveal_type(np.prod(A, keepdims=True)) # E: Any +reveal_type(np.prod(B, keepdims=True)) # E: Any +reveal_type(np.prod(b, out=d)) # E: Any +reveal_type(np.prod(B, out=d)) # E: Any + +reveal_type(np.cumprod(a)) # E: ndarray[Any, Any] +reveal_type(np.cumprod(b)) # E: ndarray[Any, Any] +reveal_type(np.cumprod(c)) # E: ndarray[Any, Any] +reveal_type(np.cumprod(A)) # E: ndarray[Any, Any] +reveal_type(np.cumprod(B)) # E: ndarray[Any, Any] + +reveal_type(np.ndim(a)) # E: int +reveal_type(np.ndim(b)) # E: int +reveal_type(np.ndim(c)) # E: int +reveal_type(np.ndim(A)) # E: int +reveal_type(np.ndim(B)) # E: int + +reveal_type(np.size(a)) # E: int +reveal_type(np.size(b)) # E: int +reveal_type(np.size(c)) # E: int +reveal_type(np.size(A)) # E: int +reveal_type(np.size(B)) # E: int + +reveal_type(np.around(a)) # E: Any +reveal_type(np.around(b)) # E: Any +reveal_type(np.around(c)) # E: Any +reveal_type(np.around(A)) # E: Any +reveal_type(np.around(B)) # E: Any + +reveal_type(np.mean(a)) # E: Any +reveal_type(np.mean(b)) # E: Any +reveal_type(np.mean(c)) # E: Any +reveal_type(np.mean(A)) # E: Any +reveal_type(np.mean(B)) # E: Any +reveal_type(np.mean(A, axis=0)) # E: Any +reveal_type(np.mean(B, axis=0)) # E: Any +reveal_type(np.mean(A, keepdims=True)) # E: Any +reveal_type(np.mean(B, keepdims=True)) # E: Any +reveal_type(np.mean(b, out=d)) # E: Any +reveal_type(np.mean(B, out=d)) # E: Any + +reveal_type(np.std(a)) # E: Any +reveal_type(np.std(b)) # E: Any +reveal_type(np.std(c)) # E: Any +reveal_type(np.std(A)) # E: Any +reveal_type(np.std(B)) # E: Any +reveal_type(np.std(A, axis=0)) # E: Any +reveal_type(np.std(B, axis=0)) # E: Any +reveal_type(np.std(A, keepdims=True)) # E: Any +reveal_type(np.std(B, keepdims=True)) # E: Any +reveal_type(np.std(b, out=d)) # E: Any +reveal_type(np.std(B, out=d)) # E: Any + +reveal_type(np.var(a)) # E: Any +reveal_type(np.var(b)) # E: Any +reveal_type(np.var(c)) # E: Any +reveal_type(np.var(A)) # E: Any +reveal_type(np.var(B)) # E: Any +reveal_type(np.var(A, axis=0)) # E: Any +reveal_type(np.var(B, axis=0)) # E: Any +reveal_type(np.var(A, keepdims=True)) # E: Any +reveal_type(np.var(B, keepdims=True)) # E: Any +reveal_type(np.var(b, out=d)) # E: Any +reveal_type(np.var(B, out=d)) # E: Any diff --git a/numpy/typing/tests/data/reveal/getlimits.pyi b/numpy/typing/tests/data/reveal/getlimits.pyi new file mode 100644 index 000000000000..1614b577ee14 --- /dev/null +++ b/numpy/typing/tests/data/reveal/getlimits.pyi @@ -0,0 +1,47 @@ +import numpy as np +f: float +f8: np.float64 +c8: np.complex64 + +i: int +i8: np.int64 +u4: np.uint32 + +finfo_f8: np.finfo[np.float64] +iinfo_i8: np.iinfo[np.int64] + +reveal_type(np.finfo(f)) # E: finfo[{double}] +reveal_type(np.finfo(f8)) # E: finfo[{float64}] +reveal_type(np.finfo(c8)) # E: finfo[{float32}] +reveal_type(np.finfo('f2')) # E: finfo[floating[Any]] + +reveal_type(finfo_f8.dtype) # E: dtype[{float64}] +reveal_type(finfo_f8.bits) # E: int +reveal_type(finfo_f8.eps) # E: {float64} +reveal_type(finfo_f8.epsneg) # E: {float64} +reveal_type(finfo_f8.iexp) # E: int +reveal_type(finfo_f8.machep) # E: int +reveal_type(finfo_f8.max) # E: {float64} +reveal_type(finfo_f8.maxexp) # E: int +reveal_type(finfo_f8.min) # E: {float64} +reveal_type(finfo_f8.minexp) # E: int +reveal_type(finfo_f8.negep) # E: int +reveal_type(finfo_f8.nexp) # E: int +reveal_type(finfo_f8.nmant) # E: int +reveal_type(finfo_f8.precision) # E: int +reveal_type(finfo_f8.resolution) # E: {float64} +reveal_type(finfo_f8.tiny) # E: {float64} +reveal_type(finfo_f8.smallest_normal) # E: {float64} +reveal_type(finfo_f8.smallest_subnormal) # E: {float64} + +reveal_type(np.iinfo(i)) # E: iinfo[{int_}] +reveal_type(np.iinfo(i8)) # E: iinfo[{int64}] +reveal_type(np.iinfo(u4)) # E: iinfo[{uint32}] +reveal_type(np.iinfo('i2')) # E: iinfo[Any] + +reveal_type(iinfo_i8.dtype) # E: dtype[{int64}] +reveal_type(iinfo_i8.kind) # E: str +reveal_type(iinfo_i8.bits) # E: int +reveal_type(iinfo_i8.key) # E: str +reveal_type(iinfo_i8.min) # E: int +reveal_type(iinfo_i8.max) # E: int diff --git a/numpy/typing/tests/data/reveal/histograms.pyi b/numpy/typing/tests/data/reveal/histograms.pyi new file mode 100644 index 000000000000..d96e44f096fd --- /dev/null +++ b/numpy/typing/tests/data/reveal/histograms.pyi @@ -0,0 +1,19 @@ +import numpy as np +import numpy.typing as npt + +AR_i8: npt.NDArray[np.int64] +AR_f8: npt.NDArray[np.float64] + +reveal_type(np.histogram_bin_edges(AR_i8, bins="auto")) # E: ndarray[Any, dtype[Any]] +reveal_type(np.histogram_bin_edges(AR_i8, bins="rice", range=(0, 3))) # E: ndarray[Any, dtype[Any]] +reveal_type(np.histogram_bin_edges(AR_i8, bins="scott", weights=AR_f8)) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.histogram(AR_i8, bins="auto")) # E: Tuple[ndarray[Any, dtype[Any]], ndarray[Any, dtype[Any]]] +reveal_type(np.histogram(AR_i8, bins="rice", range=(0, 3))) # E: Tuple[ndarray[Any, dtype[Any]], ndarray[Any, dtype[Any]]] +reveal_type(np.histogram(AR_i8, bins="scott", weights=AR_f8)) # E: Tuple[ndarray[Any, dtype[Any]], ndarray[Any, dtype[Any]]] +reveal_type(np.histogram(AR_f8, bins=1, density=True)) # E: Tuple[ndarray[Any, dtype[Any]], ndarray[Any, dtype[Any]]] + +reveal_type(np.histogramdd(AR_i8, bins=[1])) # E: Tuple[ndarray[Any, dtype[Any]], builtins.list[ndarray[Any, dtype[Any]]]] +reveal_type(np.histogramdd(AR_i8, range=[(0, 3)])) # E: Tuple[ndarray[Any, dtype[Any]], builtins.list[ndarray[Any, dtype[Any]]]] +reveal_type(np.histogramdd(AR_i8, weights=AR_f8)) # E: Tuple[ndarray[Any, dtype[Any]], builtins.list[ndarray[Any, dtype[Any]]]] +reveal_type(np.histogramdd(AR_f8, density=True)) # E: Tuple[ndarray[Any, dtype[Any]], builtins.list[ndarray[Any, dtype[Any]]]] diff --git a/numpy/typing/tests/data/reveal/index_tricks.pyi b/numpy/typing/tests/data/reveal/index_tricks.pyi new file mode 100644 index 000000000000..cee4d8c3e7e6 --- /dev/null +++ b/numpy/typing/tests/data/reveal/index_tricks.pyi @@ -0,0 +1,64 @@ +from typing import Any, List +import numpy as np + +AR_LIKE_b: List[bool] +AR_LIKE_i: List[int] +AR_LIKE_f: List[float] +AR_LIKE_U: List[str] + +AR_i8: np.ndarray[Any, np.dtype[np.int64]] + +reveal_type(np.ndenumerate(AR_i8)) # E: ndenumerate[{int64}] +reveal_type(np.ndenumerate(AR_LIKE_f)) # E: ndenumerate[{double}] +reveal_type(np.ndenumerate(AR_LIKE_U)) # E: ndenumerate[str_] + +reveal_type(np.ndenumerate(AR_i8).iter) # E: flatiter[ndarray[Any, dtype[{int64}]]] +reveal_type(np.ndenumerate(AR_LIKE_f).iter) # E: flatiter[ndarray[Any, dtype[{double}]]] +reveal_type(np.ndenumerate(AR_LIKE_U).iter) # E: flatiter[ndarray[Any, dtype[str_]]] + +reveal_type(next(np.ndenumerate(AR_i8))) # E: Tuple[builtins.tuple[builtins.int], {int64}] +reveal_type(next(np.ndenumerate(AR_LIKE_f))) # E: Tuple[builtins.tuple[builtins.int], {double}] +reveal_type(next(np.ndenumerate(AR_LIKE_U))) # E: Tuple[builtins.tuple[builtins.int], str_] + +reveal_type(iter(np.ndenumerate(AR_i8))) # E: Iterator[Tuple[builtins.tuple[builtins.int], {int64}]] +reveal_type(iter(np.ndenumerate(AR_LIKE_f))) # E: Iterator[Tuple[builtins.tuple[builtins.int], {double}]] +reveal_type(iter(np.ndenumerate(AR_LIKE_U))) # E: Iterator[Tuple[builtins.tuple[builtins.int], str_]] + +reveal_type(iter(np.ndindex(1, 2, 3))) # E: Iterator[builtins.tuple[builtins.int]] +reveal_type(next(np.ndindex(1, 2, 3))) # E: builtins.tuple[builtins.int] + +reveal_type(np.unravel_index([22, 41, 37], (7, 6))) # E: tuple[ndarray[Any, dtype[{intp}]]] +reveal_type(np.unravel_index([31, 41, 13], (7, 6), order="F")) # E: tuple[ndarray[Any, dtype[{intp}]]] +reveal_type(np.unravel_index(1621, (6, 7, 8, 9))) # E: tuple[{intp}] + +reveal_type(np.ravel_multi_index([[1]], (7, 6))) # E: ndarray[Any, dtype[{intp}]] +reveal_type(np.ravel_multi_index(AR_LIKE_i, (7, 6))) # E: {intp} +reveal_type(np.ravel_multi_index(AR_LIKE_i, (7, 6), order="F")) # E: {intp} +reveal_type(np.ravel_multi_index(AR_LIKE_i, (4, 6), mode="clip")) # E: {intp} +reveal_type(np.ravel_multi_index(AR_LIKE_i, (4, 4), mode=("clip", "wrap"))) # E: {intp} +reveal_type(np.ravel_multi_index((3, 1, 4, 1), (6, 7, 8, 9))) # E: {intp} + +reveal_type(np.mgrid[1:1:2]) # E: ndarray[Any, dtype[Any]] +reveal_type(np.mgrid[1:1:2, None:10]) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.ogrid[1:1:2]) # E: list[ndarray[Any, dtype[Any]]] +reveal_type(np.ogrid[1:1:2, None:10]) # E: list[ndarray[Any, dtype[Any]]] + +reveal_type(np.index_exp[0:1]) # E: Tuple[builtins.slice] +reveal_type(np.index_exp[0:1, None:3]) # E: Tuple[builtins.slice, builtins.slice] +reveal_type(np.index_exp[0, 0:1, ..., [0, 1, 3]]) # E: Tuple[Literal[0]?, builtins.slice, builtins.ellipsis, builtins.list[builtins.int]] + +reveal_type(np.s_[0:1]) # E: builtins.slice +reveal_type(np.s_[0:1, None:3]) # E: Tuple[builtins.slice, builtins.slice] +reveal_type(np.s_[0, 0:1, ..., [0, 1, 3]]) # E: Tuple[Literal[0]?, builtins.slice, builtins.ellipsis, builtins.list[builtins.int]] + +reveal_type(np.ix_(AR_LIKE_b)) # E: tuple[ndarray[Any, dtype[bool_]]] +reveal_type(np.ix_(AR_LIKE_i, AR_LIKE_f)) # E: tuple[ndarray[Any, dtype[{double}]]] +reveal_type(np.ix_(AR_i8)) # E: tuple[ndarray[Any, dtype[{int64}]]] + +reveal_type(np.fill_diagonal(AR_i8, 5)) # E: None + +reveal_type(np.diag_indices(4)) # E: tuple[ndarray[Any, dtype[{int_}]]] +reveal_type(np.diag_indices(2, 3)) # E: tuple[ndarray[Any, dtype[{int_}]]] + +reveal_type(np.diag_indices_from(AR_i8)) # E: tuple[ndarray[Any, dtype[{int_}]]] diff --git a/numpy/typing/tests/data/reveal/lib_function_base.pyi b/numpy/typing/tests/data/reveal/lib_function_base.pyi new file mode 100644 index 000000000000..c559eb295893 --- /dev/null +++ b/numpy/typing/tests/data/reveal/lib_function_base.pyi @@ -0,0 +1,180 @@ +from typing import Any + +import numpy as np +import numpy.typing as npt + +vectorized_func: np.vectorize + +f8: np.float64 +AR_LIKE_f8: list[float] + +AR_i8: npt.NDArray[np.int64] +AR_f8: npt.NDArray[np.float64] +AR_c16: npt.NDArray[np.complex128] +AR_m: npt.NDArray[np.timedelta64] +AR_M: npt.NDArray[np.datetime64] +AR_O: npt.NDArray[np.object_] +AR_b: npt.NDArray[np.bool_] +AR_U: npt.NDArray[np.str_] +CHAR_AR_U: np.chararray[Any, np.dtype[np.str_]] + +def func(*args: Any, **kwargs: Any) -> Any: ... + +reveal_type(vectorized_func.pyfunc) # E: def (*Any, **Any) -> Any +reveal_type(vectorized_func.cache) # E: bool +reveal_type(vectorized_func.signature) # E: Union[None, builtins.str] +reveal_type(vectorized_func.otypes) # E: Union[None, builtins.str] +reveal_type(vectorized_func.excluded) # E: set[Union[builtins.int, builtins.str]] +reveal_type(vectorized_func.__doc__) # E: Union[None, builtins.str] +reveal_type(vectorized_func([1])) # E: ndarray[Any, dtype[Any]] +reveal_type(np.vectorize(int)) # E: vectorize +reveal_type(np.vectorize( # E: vectorize + int, otypes="i", doc="doc", excluded=(), cache=True, signature=None +)) + +reveal_type(np.add_newdoc("__main__", "blabla", doc="test doc")) # E: None +reveal_type(np.add_newdoc("__main__", "blabla", doc=("meth", "test doc"))) # E: None +reveal_type(np.add_newdoc("__main__", "blabla", doc=[("meth", "test doc")])) # E: None + +reveal_type(np.rot90(AR_f8, k=2)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.rot90(AR_LIKE_f8, axes=(0, 1))) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.flip(f8)) # E: {float64} +reveal_type(np.flip(1.0)) # E: Any +reveal_type(np.flip(AR_f8, axis=(0, 1))) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.flip(AR_LIKE_f8, axis=0)) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.iterable(1)) # E: bool +reveal_type(np.iterable([1])) # E: bool + +reveal_type(np.average(AR_f8)) # E: floating[Any] +reveal_type(np.average(AR_f8, weights=AR_c16)) # E: complexfloating[Any, Any] +reveal_type(np.average(AR_O)) # E: Any +reveal_type(np.average(AR_f8, returned=True)) # E: Tuple[floating[Any], floating[Any]] +reveal_type(np.average(AR_f8, weights=AR_c16, returned=True)) # E: Tuple[complexfloating[Any, Any], complexfloating[Any, Any]] +reveal_type(np.average(AR_O, returned=True)) # E: Tuple[Any, Any] +reveal_type(np.average(AR_f8, axis=0)) # E: Any +reveal_type(np.average(AR_f8, axis=0, returned=True)) # E: Tuple[Any, Any] + +reveal_type(np.asarray_chkfinite(AR_f8)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.asarray_chkfinite(AR_LIKE_f8)) # E: ndarray[Any, dtype[Any]] +reveal_type(np.asarray_chkfinite(AR_f8, dtype=np.float64)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.asarray_chkfinite(AR_f8, dtype=float)) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.piecewise(AR_f8, AR_b, [func])) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.piecewise(AR_LIKE_f8, AR_b, [func])) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.select([AR_f8], [AR_f8])) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.copy(AR_LIKE_f8)) # E: ndarray[Any, dtype[Any]] +reveal_type(np.copy(AR_U)) # E: ndarray[Any, dtype[str_]] +reveal_type(np.copy(CHAR_AR_U)) # E: ndarray[Any, Any] +reveal_type(np.copy(CHAR_AR_U, "K", subok=True)) # E: chararray[Any, dtype[str_]] +reveal_type(np.copy(CHAR_AR_U, subok=True)) # E: chararray[Any, dtype[str_]] + +reveal_type(np.gradient(AR_f8, axis=None)) # E: Any +reveal_type(np.gradient(AR_LIKE_f8, edge_order=2)) # E: Any + +reveal_type(np.diff("bob", n=0)) # E: str +reveal_type(np.diff(AR_f8, axis=0)) # E: ndarray[Any, dtype[Any]] +reveal_type(np.diff(AR_LIKE_f8, prepend=1.5)) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.angle(AR_f8)) # E: floating[Any] +reveal_type(np.angle(AR_c16, deg=True)) # E: complexfloating[Any, Any] +reveal_type(np.angle(AR_O)) # E: Any + +reveal_type(np.unwrap(AR_f8)) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.unwrap(AR_O)) # E: ndarray[Any, dtype[object_]] + +reveal_type(np.sort_complex(AR_f8)) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] + +reveal_type(np.trim_zeros(AR_f8)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.trim_zeros(AR_LIKE_f8)) # E: list[builtins.float] + +reveal_type(np.extract(AR_i8, AR_f8)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.extract(AR_i8, AR_LIKE_f8)) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.place(AR_f8, mask=AR_i8, vals=5.0)) # E: None + +reveal_type(np.disp(1, linefeed=True)) # E: None +with open("test", "w") as f: + reveal_type(np.disp("message", device=f)) # E: None + +reveal_type(np.cov(AR_f8, bias=True)) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.cov(AR_f8, AR_c16, ddof=1)) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] +reveal_type(np.cov(AR_f8, aweights=AR_f8, dtype=np.float32)) # E: ndarray[Any, dtype[{float32}]] +reveal_type(np.cov(AR_f8, fweights=AR_f8, dtype=float)) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.corrcoef(AR_f8, rowvar=True)) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.corrcoef(AR_f8, AR_c16)) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] +reveal_type(np.corrcoef(AR_f8, dtype=np.float32)) # E: ndarray[Any, dtype[{float32}]] +reveal_type(np.corrcoef(AR_f8, dtype=float)) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.blackman(5)) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.bartlett(6)) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.hanning(4.5)) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.hamming(0)) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.i0(AR_i8)) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.kaiser(4, 5.9)) # E: ndarray[Any, dtype[floating[Any]]] + +reveal_type(np.sinc(1.0)) # E: floating[Any] +reveal_type(np.sinc(1j)) # E: complexfloating[Any, Any] +reveal_type(np.sinc(AR_f8)) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.sinc(AR_c16)) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] + +reveal_type(np.msort(CHAR_AR_U)) # E: Any +reveal_type(np.msort(AR_U)) # E: ndarray[Any, dtype[str_]] +reveal_type(np.msort(AR_LIKE_f8)) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.median(AR_f8, keepdims=False)) # E: floating[Any] +reveal_type(np.median(AR_c16, overwrite_input=True)) # E: complexfloating[Any, Any] +reveal_type(np.median(AR_m)) # E: timedelta64 +reveal_type(np.median(AR_O)) # E: Any +reveal_type(np.median(AR_f8, keepdims=True)) # E: Any +reveal_type(np.median(AR_c16, axis=0)) # E: Any +reveal_type(np.median(AR_LIKE_f8, out=AR_c16)) # E: ndarray[Any, dtype[{complex128}]] + +reveal_type(np.add_newdoc_ufunc(np.add, "docstring")) # E: None + +reveal_type(np.percentile(AR_f8, 50)) # E: floating[Any] +reveal_type(np.percentile(AR_c16, 50)) # E: complexfloating[Any, Any] +reveal_type(np.percentile(AR_m, 50)) # E: timedelta64 +reveal_type(np.percentile(AR_M, 50, overwrite_input=True)) # E: datetime64 +reveal_type(np.percentile(AR_O, 50)) # E: Any +reveal_type(np.percentile(AR_f8, [50])) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.percentile(AR_c16, [50])) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] +reveal_type(np.percentile(AR_m, [50])) # E: ndarray[Any, dtype[timedelta64]] +reveal_type(np.percentile(AR_M, [50], method="nearest")) # E: ndarray[Any, dtype[datetime64]] +reveal_type(np.percentile(AR_O, [50])) # E: ndarray[Any, dtype[object_]] +reveal_type(np.percentile(AR_f8, [50], keepdims=True)) # E: Any +reveal_type(np.percentile(AR_f8, [50], axis=[1])) # E: Any +reveal_type(np.percentile(AR_f8, [50], out=AR_c16)) # E: ndarray[Any, dtype[{complex128}]] + +reveal_type(np.quantile(AR_f8, 0.5)) # E: floating[Any] +reveal_type(np.quantile(AR_c16, 0.5)) # E: complexfloating[Any, Any] +reveal_type(np.quantile(AR_m, 0.5)) # E: timedelta64 +reveal_type(np.quantile(AR_M, 0.5, overwrite_input=True)) # E: datetime64 +reveal_type(np.quantile(AR_O, 0.5)) # E: Any +reveal_type(np.quantile(AR_f8, [0.5])) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.quantile(AR_c16, [0.5])) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] +reveal_type(np.quantile(AR_m, [0.5])) # E: ndarray[Any, dtype[timedelta64]] +reveal_type(np.quantile(AR_M, [0.5], method="nearest")) # E: ndarray[Any, dtype[datetime64]] +reveal_type(np.quantile(AR_O, [0.5])) # E: ndarray[Any, dtype[object_]] +reveal_type(np.quantile(AR_f8, [0.5], keepdims=True)) # E: Any +reveal_type(np.quantile(AR_f8, [0.5], axis=[1])) # E: Any +reveal_type(np.quantile(AR_f8, [0.5], out=AR_c16)) # E: ndarray[Any, dtype[{complex128}]] + +reveal_type(np.meshgrid(AR_f8, AR_i8, copy=False)) # E: list[ndarray[Any, dtype[Any]]] +reveal_type(np.meshgrid(AR_f8, AR_i8, AR_c16, indexing="ij")) # E: list[ndarray[Any, dtype[Any]]] + +reveal_type(np.delete(AR_f8, np.s_[:5])) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.delete(AR_LIKE_f8, [0, 4, 9], axis=0)) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.insert(AR_f8, np.s_[:5], 5)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.insert(AR_LIKE_f8, [0, 4, 9], [0.5, 9.2, 7], axis=0)) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.append(AR_f8, 5)) # E: ndarray[Any, dtype[Any]] +reveal_type(np.append(AR_LIKE_f8, 1j, axis=0)) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.digitize(4.5, [1])) # E: {intp} +reveal_type(np.digitize(AR_f8, [1, 2, 3])) # E: ndarray[Any, dtype[{intp}]] diff --git a/numpy/typing/tests/data/reveal/lib_polynomial.pyi b/numpy/typing/tests/data/reveal/lib_polynomial.pyi new file mode 100644 index 000000000000..de8950724eb9 --- /dev/null +++ b/numpy/typing/tests/data/reveal/lib_polynomial.pyi @@ -0,0 +1,111 @@ +import numpy as np +import numpy.typing as npt + +AR_b: npt.NDArray[np.bool_] +AR_u4: npt.NDArray[np.uint32] +AR_i8: npt.NDArray[np.int64] +AR_f8: npt.NDArray[np.float64] +AR_c16: npt.NDArray[np.complex128] +AR_O: npt.NDArray[np.object_] + +poly_obj: np.poly1d + +reveal_type(poly_obj.variable) # E: str +reveal_type(poly_obj.order) # E: int +reveal_type(poly_obj.o) # E: int +reveal_type(poly_obj.roots) # E: ndarray[Any, dtype[Any]] +reveal_type(poly_obj.r) # E: ndarray[Any, dtype[Any]] +reveal_type(poly_obj.coeffs) # E: ndarray[Any, dtype[Any]] +reveal_type(poly_obj.c) # E: ndarray[Any, dtype[Any]] +reveal_type(poly_obj.coef) # E: ndarray[Any, dtype[Any]] +reveal_type(poly_obj.coefficients) # E: ndarray[Any, dtype[Any]] +reveal_type(poly_obj.__hash__) # E: None + +reveal_type(poly_obj(1)) # E: Any +reveal_type(poly_obj([1])) # E: ndarray[Any, dtype[Any]] +reveal_type(poly_obj(poly_obj)) # E: poly1d + +reveal_type(len(poly_obj)) # E: int +reveal_type(-poly_obj) # E: poly1d +reveal_type(+poly_obj) # E: poly1d + +reveal_type(poly_obj * 5) # E: poly1d +reveal_type(5 * poly_obj) # E: poly1d +reveal_type(poly_obj + 5) # E: poly1d +reveal_type(5 + poly_obj) # E: poly1d +reveal_type(poly_obj - 5) # E: poly1d +reveal_type(5 - poly_obj) # E: poly1d +reveal_type(poly_obj**1) # E: poly1d +reveal_type(poly_obj**1.0) # E: poly1d +reveal_type(poly_obj / 5) # E: poly1d +reveal_type(5 / poly_obj) # E: poly1d + +reveal_type(poly_obj[0]) # E: Any +poly_obj[0] = 5 +reveal_type(iter(poly_obj)) # E: Iterator[Any] +reveal_type(poly_obj.deriv()) # E: poly1d +reveal_type(poly_obj.integ()) # E: poly1d + +reveal_type(np.poly(poly_obj)) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.poly(AR_f8)) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.poly(AR_c16)) # E: ndarray[Any, dtype[floating[Any]]] + +reveal_type(np.polyint(poly_obj)) # E: poly1d +reveal_type(np.polyint(AR_f8)) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.polyint(AR_f8, k=AR_c16)) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] +reveal_type(np.polyint(AR_O, m=2)) # E: ndarray[Any, dtype[object_]] + +reveal_type(np.polyder(poly_obj)) # E: poly1d +reveal_type(np.polyder(AR_f8)) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.polyder(AR_c16)) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] +reveal_type(np.polyder(AR_O, m=2)) # E: ndarray[Any, dtype[object_]] + +reveal_type(np.polyfit(AR_f8, AR_f8, 2)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.polyfit(AR_f8, AR_i8, 1, full=True)) # E: Tuple[ndarray[Any, dtype[{float64}]], ndarray[Any, dtype[{float64}]], ndarray[Any, dtype[signedinteger[typing._32Bit]]], ndarray[Any, dtype[{float64}]], ndarray[Any, dtype[{float64}]]] +reveal_type(np.polyfit(AR_u4, AR_f8, 1.0, cov="unscaled")) # E: Tuple[ndarray[Any, dtype[{float64}]], ndarray[Any, dtype[{float64}]]] +reveal_type(np.polyfit(AR_c16, AR_f8, 2)) # E: ndarray[Any, dtype[{complex128}]] +reveal_type(np.polyfit(AR_f8, AR_c16, 1, full=True)) # E: Tuple[ndarray[Any, dtype[{complex128}]], ndarray[Any, dtype[{float64}]], ndarray[Any, dtype[signedinteger[typing._32Bit]]], ndarray[Any, dtype[{float64}]], ndarray[Any, dtype[{float64}]]] +reveal_type(np.polyfit(AR_u4, AR_c16, 1.0, cov=True)) # E: Tuple[ndarray[Any, dtype[{complex128}]], ndarray[Any, dtype[{complex128}]]] + +reveal_type(np.polyval(AR_b, AR_b)) # E: ndarray[Any, dtype[{int64}]] +reveal_type(np.polyval(AR_u4, AR_b)) # E: ndarray[Any, dtype[unsignedinteger[Any]]] +reveal_type(np.polyval(AR_i8, AR_i8)) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(np.polyval(AR_f8, AR_i8)) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.polyval(AR_i8, AR_c16)) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] +reveal_type(np.polyval(AR_O, AR_O)) # E: ndarray[Any, dtype[object_]] + +reveal_type(np.polyadd(poly_obj, AR_i8)) # E: poly1d +reveal_type(np.polyadd(AR_f8, poly_obj)) # E: poly1d +reveal_type(np.polyadd(AR_b, AR_b)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.polyadd(AR_u4, AR_b)) # E: ndarray[Any, dtype[unsignedinteger[Any]]] +reveal_type(np.polyadd(AR_i8, AR_i8)) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(np.polyadd(AR_f8, AR_i8)) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.polyadd(AR_i8, AR_c16)) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] +reveal_type(np.polyadd(AR_O, AR_O)) # E: ndarray[Any, dtype[object_]] + +reveal_type(np.polysub(poly_obj, AR_i8)) # E: poly1d +reveal_type(np.polysub(AR_f8, poly_obj)) # E: poly1d +reveal_type(np.polysub(AR_b, AR_b)) # E: <nothing> +reveal_type(np.polysub(AR_u4, AR_b)) # E: ndarray[Any, dtype[unsignedinteger[Any]]] +reveal_type(np.polysub(AR_i8, AR_i8)) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(np.polysub(AR_f8, AR_i8)) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.polysub(AR_i8, AR_c16)) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] +reveal_type(np.polysub(AR_O, AR_O)) # E: ndarray[Any, dtype[object_]] + +reveal_type(np.polymul(poly_obj, AR_i8)) # E: poly1d +reveal_type(np.polymul(AR_f8, poly_obj)) # E: poly1d +reveal_type(np.polymul(AR_b, AR_b)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.polymul(AR_u4, AR_b)) # E: ndarray[Any, dtype[unsignedinteger[Any]]] +reveal_type(np.polymul(AR_i8, AR_i8)) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(np.polymul(AR_f8, AR_i8)) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.polymul(AR_i8, AR_c16)) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] +reveal_type(np.polymul(AR_O, AR_O)) # E: ndarray[Any, dtype[object_]] + +reveal_type(np.polydiv(poly_obj, AR_i8)) # E: poly1d +reveal_type(np.polydiv(AR_f8, poly_obj)) # E: poly1d +reveal_type(np.polydiv(AR_b, AR_b)) # E: Tuple[ndarray[Any, dtype[floating[Any]]], ndarray[Any, dtype[floating[Any]]]] +reveal_type(np.polydiv(AR_u4, AR_b)) # E: Tuple[ndarray[Any, dtype[floating[Any]]], ndarray[Any, dtype[floating[Any]]]] +reveal_type(np.polydiv(AR_i8, AR_i8)) # E: Tuple[ndarray[Any, dtype[floating[Any]]], ndarray[Any, dtype[floating[Any]]]] +reveal_type(np.polydiv(AR_f8, AR_i8)) # E: Tuple[ndarray[Any, dtype[floating[Any]]], ndarray[Any, dtype[floating[Any]]]] +reveal_type(np.polydiv(AR_i8, AR_c16)) # E: Tuple[ndarray[Any, dtype[complexfloating[Any, Any]]], ndarray[Any, dtype[complexfloating[Any, Any]]]] +reveal_type(np.polydiv(AR_O, AR_O)) # E: Tuple[ndarray[Any, dtype[Any]], ndarray[Any, dtype[Any]]] diff --git a/numpy/typing/tests/data/reveal/lib_utils.pyi b/numpy/typing/tests/data/reveal/lib_utils.pyi new file mode 100644 index 000000000000..d820127078a3 --- /dev/null +++ b/numpy/typing/tests/data/reveal/lib_utils.pyi @@ -0,0 +1,30 @@ +from io import StringIO +from typing import Any, Dict + +import numpy as np + +AR: np.ndarray[Any, np.dtype[np.float64]] +AR_DICT: Dict[str, np.ndarray[Any, np.dtype[np.float64]]] +FILE: StringIO + +def func(a: int) -> bool: ... + +reveal_type(np.deprecate(func)) # E: def (a: builtins.int) -> builtins.bool +reveal_type(np.deprecate()) # E: _Deprecate + +reveal_type(np.deprecate_with_doc("test")) # E: _Deprecate +reveal_type(np.deprecate_with_doc(None)) # E: _Deprecate + +reveal_type(np.byte_bounds(AR)) # E: Tuple[builtins.int, builtins.int] +reveal_type(np.byte_bounds(np.float64())) # E: Tuple[builtins.int, builtins.int] + +reveal_type(np.who(None)) # E: None +reveal_type(np.who(AR_DICT)) # E: None + +reveal_type(np.info(1, output=FILE)) # E: None + +reveal_type(np.source(np.interp, output=FILE)) # E: None + +reveal_type(np.lookfor("binary representation", output=FILE)) # E: None + +reveal_type(np.safe_eval("1 + 1")) # E: Any diff --git a/numpy/typing/tests/data/reveal/lib_version.pyi b/numpy/typing/tests/data/reveal/lib_version.pyi new file mode 100644 index 000000000000..e6f695558a40 --- /dev/null +++ b/numpy/typing/tests/data/reveal/lib_version.pyi @@ -0,0 +1,18 @@ +from numpy.lib import NumpyVersion + +version = NumpyVersion("1.8.0") + +reveal_type(version.vstring) # E: str +reveal_type(version.version) # E: str +reveal_type(version.major) # E: int +reveal_type(version.minor) # E: int +reveal_type(version.bugfix) # E: int +reveal_type(version.pre_release) # E: str +reveal_type(version.is_devversion) # E: bool + +reveal_type(version == version) # E: bool +reveal_type(version != version) # E: bool +reveal_type(version < "1.8.0") # E: bool +reveal_type(version <= version) # E: bool +reveal_type(version > version) # E: bool +reveal_type(version >= "1.8.0") # E: bool diff --git a/numpy/typing/tests/data/reveal/linalg.pyi b/numpy/typing/tests/data/reveal/linalg.pyi new file mode 100644 index 000000000000..19e13aed6922 --- /dev/null +++ b/numpy/typing/tests/data/reveal/linalg.pyi @@ -0,0 +1,97 @@ +import numpy as np +import numpy.typing as npt + +AR_i8: npt.NDArray[np.int64] +AR_f8: npt.NDArray[np.float64] +AR_c16: npt.NDArray[np.complex128] +AR_O: npt.NDArray[np.object_] +AR_m: npt.NDArray[np.timedelta64] +AR_S: npt.NDArray[np.str_] + +reveal_type(np.linalg.tensorsolve(AR_i8, AR_i8)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.linalg.tensorsolve(AR_i8, AR_f8)) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.linalg.tensorsolve(AR_c16, AR_f8)) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] + +reveal_type(np.linalg.solve(AR_i8, AR_i8)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.linalg.solve(AR_i8, AR_f8)) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.linalg.solve(AR_c16, AR_f8)) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] + +reveal_type(np.linalg.tensorinv(AR_i8)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.linalg.tensorinv(AR_f8)) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.linalg.tensorinv(AR_c16)) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] + +reveal_type(np.linalg.inv(AR_i8)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.linalg.inv(AR_f8)) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.linalg.inv(AR_c16)) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] + +reveal_type(np.linalg.matrix_power(AR_i8, -1)) # E: ndarray[Any, dtype[Any]] +reveal_type(np.linalg.matrix_power(AR_f8, 0)) # E: ndarray[Any, dtype[Any]] +reveal_type(np.linalg.matrix_power(AR_c16, 1)) # E: ndarray[Any, dtype[Any]] +reveal_type(np.linalg.matrix_power(AR_O, 2)) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.linalg.cholesky(AR_i8)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.linalg.cholesky(AR_f8)) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.linalg.cholesky(AR_c16)) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] + +reveal_type(np.linalg.qr(AR_i8)) # E: Tuple[ndarray[Any, dtype[{float64}]], ndarray[Any, dtype[{float64}]]] +reveal_type(np.linalg.qr(AR_f8)) # E: Tuple[ndarray[Any, dtype[floating[Any]]], ndarray[Any, dtype[floating[Any]]]] +reveal_type(np.linalg.qr(AR_c16)) # E: Tuple[ndarray[Any, dtype[complexfloating[Any, Any]]], ndarray[Any, dtype[complexfloating[Any, Any]]]] + +reveal_type(np.linalg.eigvals(AR_i8)) # E: Union[ndarray[Any, dtype[{float64}]], ndarray[Any, dtype[{complex128}]]] +reveal_type(np.linalg.eigvals(AR_f8)) # E: Union[ndarray[Any, dtype[floating[Any]]], ndarray[Any, dtype[complexfloating[Any, Any]]]] +reveal_type(np.linalg.eigvals(AR_c16)) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] + +reveal_type(np.linalg.eigvalsh(AR_i8)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.linalg.eigvalsh(AR_f8)) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.linalg.eigvalsh(AR_c16)) # E: ndarray[Any, dtype[floating[Any]]] + +reveal_type(np.linalg.eig(AR_i8)) # E: Union[Tuple[ndarray[Any, dtype[{float64}]], ndarray[Any, dtype[{float64}]]], Tuple[ndarray[Any, dtype[{complex128}]], ndarray[Any, dtype[{complex128}]]]] +reveal_type(np.linalg.eig(AR_f8)) # E: Union[Tuple[ndarray[Any, dtype[floating[Any]]], ndarray[Any, dtype[floating[Any]]]], Tuple[ndarray[Any, dtype[complexfloating[Any, Any]]], ndarray[Any, dtype[complexfloating[Any, Any]]]]] +reveal_type(np.linalg.eig(AR_c16)) # E: Tuple[ndarray[Any, dtype[complexfloating[Any, Any]]], ndarray[Any, dtype[complexfloating[Any, Any]]]] + +reveal_type(np.linalg.eigh(AR_i8)) # E: Tuple[ndarray[Any, dtype[{float64}]], ndarray[Any, dtype[{float64}]]] +reveal_type(np.linalg.eigh(AR_f8)) # E: Tuple[ndarray[Any, dtype[floating[Any]]], ndarray[Any, dtype[floating[Any]]]] +reveal_type(np.linalg.eigh(AR_c16)) # E: Tuple[ndarray[Any, dtype[floating[Any]]], ndarray[Any, dtype[complexfloating[Any, Any]]]] + +reveal_type(np.linalg.svd(AR_i8)) # E: Tuple[ndarray[Any, dtype[{float64}]], ndarray[Any, dtype[{float64}]], ndarray[Any, dtype[{float64}]]] +reveal_type(np.linalg.svd(AR_f8)) # E: Tuple[ndarray[Any, dtype[floating[Any]]], ndarray[Any, dtype[floating[Any]]], ndarray[Any, dtype[floating[Any]]]] +reveal_type(np.linalg.svd(AR_c16)) # E: Tuple[ndarray[Any, dtype[complexfloating[Any, Any]]], ndarray[Any, dtype[floating[Any]]], ndarray[Any, dtype[complexfloating[Any, Any]]]] +reveal_type(np.linalg.svd(AR_i8, compute_uv=False)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.linalg.svd(AR_f8, compute_uv=False)) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.linalg.svd(AR_c16, compute_uv=False)) # E: ndarray[Any, dtype[floating[Any]]] + +reveal_type(np.linalg.cond(AR_i8)) # E: Any +reveal_type(np.linalg.cond(AR_f8)) # E: Any +reveal_type(np.linalg.cond(AR_c16)) # E: Any + +reveal_type(np.linalg.matrix_rank(AR_i8)) # E: Any +reveal_type(np.linalg.matrix_rank(AR_f8)) # E: Any +reveal_type(np.linalg.matrix_rank(AR_c16)) # E: Any + +reveal_type(np.linalg.pinv(AR_i8)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.linalg.pinv(AR_f8)) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.linalg.pinv(AR_c16)) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] + +reveal_type(np.linalg.slogdet(AR_i8)) # E: Tuple[Any, Any] +reveal_type(np.linalg.slogdet(AR_f8)) # E: Tuple[Any, Any] +reveal_type(np.linalg.slogdet(AR_c16)) # E: Tuple[Any, Any] + +reveal_type(np.linalg.det(AR_i8)) # E: Any +reveal_type(np.linalg.det(AR_f8)) # E: Any +reveal_type(np.linalg.det(AR_c16)) # E: Any + +reveal_type(np.linalg.lstsq(AR_i8, AR_i8)) # E: Tuple[ndarray[Any, dtype[{float64}]], ndarray[Any, dtype[{float64}]], {int32}, ndarray[Any, dtype[{float64}]]] +reveal_type(np.linalg.lstsq(AR_i8, AR_f8)) # E: Tuple[ndarray[Any, dtype[floating[Any]]], ndarray[Any, dtype[floating[Any]]], {int32}, ndarray[Any, dtype[floating[Any]]]] +reveal_type(np.linalg.lstsq(AR_f8, AR_c16)) # E: Tuple[ndarray[Any, dtype[complexfloating[Any, Any]]], ndarray[Any, dtype[floating[Any]]], {int32}, ndarray[Any, dtype[floating[Any]]]] + +reveal_type(np.linalg.norm(AR_i8)) # E: floating[Any] +reveal_type(np.linalg.norm(AR_f8)) # E: floating[Any] +reveal_type(np.linalg.norm(AR_c16)) # E: floating[Any] +reveal_type(np.linalg.norm(AR_S)) # E: floating[Any] +reveal_type(np.linalg.norm(AR_f8, axis=0)) # E: Any + +reveal_type(np.linalg.multi_dot([AR_i8, AR_i8])) # E: Any +reveal_type(np.linalg.multi_dot([AR_i8, AR_f8])) # E: Any +reveal_type(np.linalg.multi_dot([AR_f8, AR_c16])) # E: Any +reveal_type(np.linalg.multi_dot([AR_O, AR_O])) # E: Any +reveal_type(np.linalg.multi_dot([AR_m, AR_m])) # E: Any diff --git a/numpy/typing/tests/data/reveal/matrix.pyi b/numpy/typing/tests/data/reveal/matrix.pyi new file mode 100644 index 000000000000..21c39067e9b8 --- /dev/null +++ b/numpy/typing/tests/data/reveal/matrix.pyi @@ -0,0 +1,69 @@ +from typing import Any +import numpy as np +import numpy.typing as npt + +mat: np.matrix[Any, np.dtype[np.int64]] +ar_f8: npt.NDArray[np.float64] + +reveal_type(mat * 5) # E: matrix[Any, Any] +reveal_type(5 * mat) # E: matrix[Any, Any] +mat *= 5 + +reveal_type(mat**5) # E: matrix[Any, Any] +mat **= 5 + +reveal_type(mat.sum()) # E: Any +reveal_type(mat.mean()) # E: Any +reveal_type(mat.std()) # E: Any +reveal_type(mat.var()) # E: Any +reveal_type(mat.prod()) # E: Any +reveal_type(mat.any()) # E: bool_ +reveal_type(mat.all()) # E: bool_ +reveal_type(mat.max()) # E: {int64} +reveal_type(mat.min()) # E: {int64} +reveal_type(mat.argmax()) # E: {intp} +reveal_type(mat.argmin()) # E: {intp} +reveal_type(mat.ptp()) # E: {int64} + +reveal_type(mat.sum(axis=0)) # E: matrix[Any, Any] +reveal_type(mat.mean(axis=0)) # E: matrix[Any, Any] +reveal_type(mat.std(axis=0)) # E: matrix[Any, Any] +reveal_type(mat.var(axis=0)) # E: matrix[Any, Any] +reveal_type(mat.prod(axis=0)) # E: matrix[Any, Any] +reveal_type(mat.any(axis=0)) # E: matrix[Any, dtype[bool_]] +reveal_type(mat.all(axis=0)) # E: matrix[Any, dtype[bool_]] +reveal_type(mat.max(axis=0)) # E: matrix[Any, dtype[{int64}]] +reveal_type(mat.min(axis=0)) # E: matrix[Any, dtype[{int64}]] +reveal_type(mat.argmax(axis=0)) # E: matrix[Any, dtype[{intp}]] +reveal_type(mat.argmin(axis=0)) # E: matrix[Any, dtype[{intp}]] +reveal_type(mat.ptp(axis=0)) # E: matrix[Any, dtype[{int64}]] + +reveal_type(mat.sum(out=ar_f8)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(mat.mean(out=ar_f8)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(mat.std(out=ar_f8)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(mat.var(out=ar_f8)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(mat.prod(out=ar_f8)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(mat.any(out=ar_f8)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(mat.all(out=ar_f8)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(mat.max(out=ar_f8)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(mat.min(out=ar_f8)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(mat.argmax(out=ar_f8)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(mat.argmin(out=ar_f8)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(mat.ptp(out=ar_f8)) # E: ndarray[Any, dtype[{float64}]] + +reveal_type(mat.T) # E: matrix[Any, dtype[{int64}]] +reveal_type(mat.I) # E: matrix[Any, Any] +reveal_type(mat.A) # E: ndarray[Any, dtype[{int64}]] +reveal_type(mat.A1) # E: ndarray[Any, dtype[{int64}]] +reveal_type(mat.H) # E: matrix[Any, dtype[{int64}]] +reveal_type(mat.getT()) # E: matrix[Any, dtype[{int64}]] +reveal_type(mat.getI()) # E: matrix[Any, Any] +reveal_type(mat.getA()) # E: ndarray[Any, dtype[{int64}]] +reveal_type(mat.getA1()) # E: ndarray[Any, dtype[{int64}]] +reveal_type(mat.getH()) # E: matrix[Any, dtype[{int64}]] + +reveal_type(np.bmat(ar_f8)) # E: matrix[Any, Any] +reveal_type(np.bmat([[0, 1, 2]])) # E: matrix[Any, Any] +reveal_type(np.bmat("mat")) # E: matrix[Any, Any] + +reveal_type(np.asmatrix(ar_f8, dtype=np.int64)) # E: matrix[Any, Any] diff --git a/numpy/typing/tests/data/reveal/memmap.pyi b/numpy/typing/tests/data/reveal/memmap.pyi new file mode 100644 index 000000000000..86de8eb08e28 --- /dev/null +++ b/numpy/typing/tests/data/reveal/memmap.pyi @@ -0,0 +1,16 @@ +import numpy as np +from typing import Any + +memmap_obj: np.memmap[Any, np.dtype[np.str_]] + +reveal_type(np.memmap.__array_priority__) # E: float +reveal_type(memmap_obj.__array_priority__) # E: float +reveal_type(memmap_obj.filename) # E: Union[builtins.str, None] +reveal_type(memmap_obj.offset) # E: int +reveal_type(memmap_obj.mode) # E: str +reveal_type(memmap_obj.flush()) # E: None + +reveal_type(np.memmap("file.txt", offset=5)) # E: memmap[Any, dtype[{uint8}]] +reveal_type(np.memmap(b"file.txt", dtype=np.float64, shape=(10, 3))) # E: memmap[Any, dtype[{float64}]] +with open("file.txt", "rb") as f: + reveal_type(np.memmap(f, dtype=float, order="K")) # E: memmap[Any, dtype[Any]] diff --git a/numpy/typing/tests/data/reveal/mod.pyi b/numpy/typing/tests/data/reveal/mod.pyi new file mode 100644 index 000000000000..b2790b7f3973 --- /dev/null +++ b/numpy/typing/tests/data/reveal/mod.pyi @@ -0,0 +1,147 @@ +from typing import Any +import numpy as np + +f8 = np.float64() +i8 = np.int64() +u8 = np.uint64() + +f4 = np.float32() +i4 = np.int32() +u4 = np.uint32() + +td = np.timedelta64(0, "D") +b_ = np.bool_() + +b = bool() +f = float() +i = int() + +AR_b: np.ndarray[Any, np.dtype[np.bool_]] +AR_m: np.ndarray[Any, np.dtype[np.timedelta64]] + +# Time structures + +reveal_type(td % td) # E: timedelta64 +reveal_type(AR_m % td) # E: Any +reveal_type(td % AR_m) # E: Any + +reveal_type(divmod(td, td)) # E: Tuple[{int64}, timedelta64] +reveal_type(divmod(AR_m, td)) # E: Tuple[ndarray[Any, dtype[signedinteger[typing._64Bit]]], ndarray[Any, dtype[timedelta64]]] +reveal_type(divmod(td, AR_m)) # E: Tuple[ndarray[Any, dtype[signedinteger[typing._64Bit]]], ndarray[Any, dtype[timedelta64]]] + +# Bool + +reveal_type(b_ % b) # E: {int8} +reveal_type(b_ % i) # E: {int_} +reveal_type(b_ % f) # E: {float64} +reveal_type(b_ % b_) # E: {int8} +reveal_type(b_ % i8) # E: {int64} +reveal_type(b_ % u8) # E: {uint64} +reveal_type(b_ % f8) # E: {float64} +reveal_type(b_ % AR_b) # E: ndarray[Any, dtype[{int8}]] + +reveal_type(divmod(b_, b)) # E: Tuple[{int8}, {int8}] +reveal_type(divmod(b_, i)) # E: Tuple[{int_}, {int_}] +reveal_type(divmod(b_, f)) # E: Tuple[{float64}, {float64}] +reveal_type(divmod(b_, b_)) # E: Tuple[{int8}, {int8}] +reveal_type(divmod(b_, i8)) # E: Tuple[{int64}, {int64}] +reveal_type(divmod(b_, u8)) # E: Tuple[{uint64}, {uint64}] +reveal_type(divmod(b_, f8)) # E: Tuple[{float64}, {float64}] +reveal_type(divmod(b_, AR_b)) # E: ndarray[Any, dtype[{int8}]], ndarray[Any, dtype[{int8}]]] + +reveal_type(b % b_) # E: {int8} +reveal_type(i % b_) # E: {int_} +reveal_type(f % b_) # E: {float64} +reveal_type(b_ % b_) # E: {int8} +reveal_type(i8 % b_) # E: {int64} +reveal_type(u8 % b_) # E: {uint64} +reveal_type(f8 % b_) # E: {float64} +reveal_type(AR_b % b_) # E: ndarray[Any, dtype[{int8}]] + +reveal_type(divmod(b, b_)) # E: Tuple[{int8}, {int8}] +reveal_type(divmod(i, b_)) # E: Tuple[{int_}, {int_}] +reveal_type(divmod(f, b_)) # E: Tuple[{float64}, {float64}] +reveal_type(divmod(b_, b_)) # E: Tuple[{int8}, {int8}] +reveal_type(divmod(i8, b_)) # E: Tuple[{int64}, {int64}] +reveal_type(divmod(u8, b_)) # E: Tuple[{uint64}, {uint64}] +reveal_type(divmod(f8, b_)) # E: Tuple[{float64}, {float64}] +reveal_type(divmod(AR_b, b_)) # E: ndarray[Any, dtype[{int8}]], ndarray[Any, dtype[{int8}]]] + +# int + +reveal_type(i8 % b) # E: {int64} +reveal_type(i8 % i) # E: {int64} +reveal_type(i8 % f) # E: {float64} +reveal_type(i8 % i8) # E: {int64} +reveal_type(i8 % f8) # E: {float64} +reveal_type(i4 % i8) # E: {int64} +reveal_type(i4 % f8) # E: {float64} +reveal_type(i4 % i4) # E: {int32} +reveal_type(i4 % f4) # E: {float32} +reveal_type(i8 % AR_b) # E: ndarray[Any, dtype[signedinteger[Any]]] + +reveal_type(divmod(i8, b)) # E: Tuple[{int64}, {int64}] +reveal_type(divmod(i8, i)) # E: Tuple[{int64}, {int64}] +reveal_type(divmod(i8, f)) # E: Tuple[{float64}, {float64}] +reveal_type(divmod(i8, i8)) # E: Tuple[{int64}, {int64}] +reveal_type(divmod(i8, f8)) # E: Tuple[{float64}, {float64}] +reveal_type(divmod(i8, i4)) # E: Tuple[{int64}, {int64}] +reveal_type(divmod(i8, f4)) # E: Tuple[{float64}, {float64}] +reveal_type(divmod(i4, i4)) # E: Tuple[{int32}, {int32}] +reveal_type(divmod(i4, f4)) # E: Tuple[{float32}, {float32}] +reveal_type(divmod(i8, AR_b)) # E: Tuple[ndarray[Any, dtype[signedinteger[Any]]], ndarray[Any, dtype[signedinteger[Any]]]] + +reveal_type(b % i8) # E: {int64} +reveal_type(i % i8) # E: {int64} +reveal_type(f % i8) # E: {float64} +reveal_type(i8 % i8) # E: {int64} +reveal_type(f8 % i8) # E: {float64} +reveal_type(i8 % i4) # E: {int64} +reveal_type(f8 % i4) # E: {float64} +reveal_type(i4 % i4) # E: {int32} +reveal_type(f4 % i4) # E: {float32} +reveal_type(AR_b % i8) # E: ndarray[Any, dtype[signedinteger[Any]]] + +reveal_type(divmod(b, i8)) # E: Tuple[{int64}, {int64}] +reveal_type(divmod(i, i8)) # E: Tuple[{int64}, {int64}] +reveal_type(divmod(f, i8)) # E: Tuple[{float64}, {float64}] +reveal_type(divmod(i8, i8)) # E: Tuple[{int64}, {int64}] +reveal_type(divmod(f8, i8)) # E: Tuple[{float64}, {float64}] +reveal_type(divmod(i4, i8)) # E: Tuple[{int64}, {int64}] +reveal_type(divmod(f4, i8)) # E: Tuple[{float64}, {float64}] +reveal_type(divmod(i4, i4)) # E: Tuple[{int32}, {int32}] +reveal_type(divmod(f4, i4)) # E: Tuple[{float32}, {float32}] +reveal_type(divmod(AR_b, i8)) # E: Tuple[ndarray[Any, dtype[signedinteger[Any]]], ndarray[Any, dtype[signedinteger[Any]]]] + +# float + +reveal_type(f8 % b) # E: {float64} +reveal_type(f8 % i) # E: {float64} +reveal_type(f8 % f) # E: {float64} +reveal_type(i8 % f4) # E: {float64} +reveal_type(f4 % f4) # E: {float32} +reveal_type(f8 % AR_b) # E: ndarray[Any, dtype[floating[Any]]] + +reveal_type(divmod(f8, b)) # E: Tuple[{float64}, {float64}] +reveal_type(divmod(f8, i)) # E: Tuple[{float64}, {float64}] +reveal_type(divmod(f8, f)) # E: Tuple[{float64}, {float64}] +reveal_type(divmod(f8, f8)) # E: Tuple[{float64}, {float64}] +reveal_type(divmod(f8, f4)) # E: Tuple[{float64}, {float64}] +reveal_type(divmod(f4, f4)) # E: Tuple[{float32}, {float32}] +reveal_type(divmod(f8, AR_b)) # E: Tuple[ndarray[Any, dtype[floating[Any]]], ndarray[Any, dtype[floating[Any]]]] + +reveal_type(b % f8) # E: {float64} +reveal_type(i % f8) # E: {float64} +reveal_type(f % f8) # E: {float64} +reveal_type(f8 % f8) # E: {float64} +reveal_type(f8 % f8) # E: {float64} +reveal_type(f4 % f4) # E: {float32} +reveal_type(AR_b % f8) # E: ndarray[Any, dtype[floating[Any]]] + +reveal_type(divmod(b, f8)) # E: Tuple[{float64}, {float64}] +reveal_type(divmod(i, f8)) # E: Tuple[{float64}, {float64}] +reveal_type(divmod(f, f8)) # E: Tuple[{float64}, {float64}] +reveal_type(divmod(f8, f8)) # E: Tuple[{float64}, {float64}] +reveal_type(divmod(f4, f8)) # E: Tuple[{float64}, {float64}] +reveal_type(divmod(f4, f4)) # E: Tuple[{float32}, {float32}] +reveal_type(divmod(AR_b, f8)) # E: Tuple[ndarray[Any, dtype[floating[Any]]], ndarray[Any, dtype[floating[Any]]]] diff --git a/numpy/typing/tests/data/reveal/modules.pyi b/numpy/typing/tests/data/reveal/modules.pyi new file mode 100644 index 000000000000..ba830eb0d332 --- /dev/null +++ b/numpy/typing/tests/data/reveal/modules.pyi @@ -0,0 +1,47 @@ +import numpy as np +from numpy import f2py + +reveal_type(np) # E: ModuleType + +reveal_type(np.char) # E: ModuleType +reveal_type(np.ctypeslib) # E: ModuleType +reveal_type(np.emath) # E: ModuleType +reveal_type(np.fft) # E: ModuleType +reveal_type(np.lib) # E: ModuleType +reveal_type(np.linalg) # E: ModuleType +reveal_type(np.ma) # E: ModuleType +reveal_type(np.matrixlib) # E: ModuleType +reveal_type(np.polynomial) # E: ModuleType +reveal_type(np.random) # E: ModuleType +reveal_type(np.rec) # E: ModuleType +reveal_type(np.testing) # E: ModuleType +reveal_type(np.version) # E: ModuleType + +reveal_type(np.lib.format) # E: ModuleType +reveal_type(np.lib.mixins) # E: ModuleType +reveal_type(np.lib.scimath) # E: ModuleType +reveal_type(np.lib.stride_tricks) # E: ModuleType +reveal_type(np.ma.extras) # E: ModuleType +reveal_type(np.polynomial.chebyshev) # E: ModuleType +reveal_type(np.polynomial.hermite) # E: ModuleType +reveal_type(np.polynomial.hermite_e) # E: ModuleType +reveal_type(np.polynomial.laguerre) # E: ModuleType +reveal_type(np.polynomial.legendre) # E: ModuleType +reveal_type(np.polynomial.polynomial) # E: ModuleType + +reveal_type(np.__path__) # E: list[builtins.str] +reveal_type(np.__version__) # E: str +reveal_type(np.__git_version__) # E: str +reveal_type(np.test) # E: _pytesttester.PytestTester +reveal_type(np.test.module_name) # E: str + +reveal_type(np.__all__) # E: list[builtins.str] +reveal_type(np.char.__all__) # E: list[builtins.str] +reveal_type(np.ctypeslib.__all__) # E: list[builtins.str] +reveal_type(np.emath.__all__) # E: list[builtins.str] +reveal_type(np.lib.__all__) # E: list[builtins.str] +reveal_type(np.ma.__all__) # E: list[builtins.str] +reveal_type(np.random.__all__) # E: list[builtins.str] +reveal_type(np.rec.__all__) # E: list[builtins.str] +reveal_type(np.testing.__all__) # E: list[builtins.str] +reveal_type(f2py.__all__) # E: list[builtins.str] diff --git a/numpy/typing/tests/data/reveal/multiarray.pyi b/numpy/typing/tests/data/reveal/multiarray.pyi new file mode 100644 index 000000000000..0e91a7afdc52 --- /dev/null +++ b/numpy/typing/tests/data/reveal/multiarray.pyi @@ -0,0 +1,132 @@ +from typing import Any, List, TypeVar +from pathlib import Path + +import numpy as np +import numpy.typing as npt + +_SCT = TypeVar("_SCT", bound=np.generic, covariant=True) + +class SubClass(np.ndarray[Any, np.dtype[_SCT]]): ... + +subclass: SubClass[np.float64] + +AR_f8: npt.NDArray[np.float64] +AR_i8: npt.NDArray[np.int64] +AR_u1: npt.NDArray[np.uint8] +AR_m: npt.NDArray[np.timedelta64] +AR_M: npt.NDArray[np.datetime64] + +AR_LIKE_f: List[float] +AR_LIKE_i: List[int] + +m: np.timedelta64 +M: np.datetime64 + +b_f8 = np.broadcast(AR_f8) +b_i8_f8_f8 = np.broadcast(AR_i8, AR_f8, AR_f8) + +nditer_obj: np.nditer + +def func(a: int) -> bool: ... + +reveal_type(next(b_f8)) # E: tuple[Any] +reveal_type(b_f8.reset()) # E: None +reveal_type(b_f8.index) # E: int +reveal_type(b_f8.iters) # E: tuple[flatiter[Any]] +reveal_type(b_f8.nd) # E: int +reveal_type(b_f8.ndim) # E: int +reveal_type(b_f8.numiter) # E: int +reveal_type(b_f8.shape) # E: tuple[builtins.int] +reveal_type(b_f8.size) # E: int + +reveal_type(next(b_i8_f8_f8)) # E: tuple[Any] +reveal_type(b_i8_f8_f8.reset()) # E: None +reveal_type(b_i8_f8_f8.index) # E: int +reveal_type(b_i8_f8_f8.iters) # E: tuple[flatiter[Any]] +reveal_type(b_i8_f8_f8.nd) # E: int +reveal_type(b_i8_f8_f8.ndim) # E: int +reveal_type(b_i8_f8_f8.numiter) # E: int +reveal_type(b_i8_f8_f8.shape) # E: tuple[builtins.int] +reveal_type(b_i8_f8_f8.size) # E: int + +reveal_type(np.inner(AR_f8, AR_i8)) # E: Any + +reveal_type(np.where([True, True, False])) # E: tuple[ndarray[Any, dtype[{intp}]]] +reveal_type(np.where([True, True, False], 1, 0)) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.lexsort([0, 1, 2])) # E: Any + +reveal_type(np.can_cast(np.dtype("i8"), int)) # E: bool +reveal_type(np.can_cast(AR_f8, "f8")) # E: bool +reveal_type(np.can_cast(AR_f8, np.complex128, casting="unsafe")) # E: bool + +reveal_type(np.min_scalar_type([1])) # E: dtype[Any] +reveal_type(np.min_scalar_type(AR_f8)) # E: dtype[Any] + +reveal_type(np.result_type(int, [1])) # E: dtype[Any] +reveal_type(np.result_type(AR_f8, AR_u1)) # E: dtype[Any] +reveal_type(np.result_type(AR_f8, np.complex128)) # E: dtype[Any] + +reveal_type(np.dot(AR_LIKE_f, AR_i8)) # E: Any +reveal_type(np.dot(AR_u1, 1)) # E: Any +reveal_type(np.dot(1.5j, 1)) # E: Any +reveal_type(np.dot(AR_u1, 1, out=AR_f8)) # E: ndarray[Any, dtype[{float64}]] + +reveal_type(np.vdot(AR_LIKE_f, AR_i8)) # E: floating[Any] +reveal_type(np.vdot(AR_u1, 1)) # E: signedinteger[Any] +reveal_type(np.vdot(1.5j, 1)) # E: complexfloating[Any, Any] + +reveal_type(np.bincount(AR_i8)) # E: ndarray[Any, dtype[{intp}]] + +reveal_type(np.copyto(AR_f8, [1., 1.5, 1.6])) # E: None + +reveal_type(np.putmask(AR_f8, [True, True, False], 1.5)) # E: None + +reveal_type(np.packbits(AR_i8)) # ndarray[Any, dtype[{uint8}]] +reveal_type(np.packbits(AR_u1)) # ndarray[Any, dtype[{uint8}]] + +reveal_type(np.unpackbits(AR_u1)) # ndarray[Any, dtype[{uint8}]] + +reveal_type(np.shares_memory(1, 2)) # E: bool +reveal_type(np.shares_memory(AR_f8, AR_f8, max_work=1)) # E: bool + +reveal_type(np.may_share_memory(1, 2)) # E: bool +reveal_type(np.may_share_memory(AR_f8, AR_f8, max_work=1)) # E: bool + +reveal_type(np.geterrobj()) # E: list[Any] + +reveal_type(np.seterrobj([8192, 521, None])) # E: None + +reveal_type(np.promote_types(np.int32, np.int64)) # E: dtype[Any] +reveal_type(np.promote_types("f4", float)) # E: dtype[Any] + +reveal_type(np.frompyfunc(func, 1, 1, identity=None)) # ufunc + +reveal_type(np.datetime_data("m8[D]")) # E: Tuple[builtins.str, builtins.int] +reveal_type(np.datetime_data(np.datetime64)) # E: Tuple[builtins.str, builtins.int] +reveal_type(np.datetime_data(np.dtype(np.timedelta64))) # E: Tuple[builtins.str, builtins.int] + +reveal_type(np.busday_count("2011-01", "2011-02")) # E: {int_} +reveal_type(np.busday_count(["2011-01"], "2011-02")) # E: ndarray[Any, dtype[{int_}]] + +reveal_type(np.busday_offset(M, m)) # E: datetime64 +reveal_type(np.busday_offset(M, 5)) # E: datetime64 +reveal_type(np.busday_offset(AR_M, m)) # E: ndarray[Any, dtype[datetime64]] +reveal_type(np.busday_offset("2011-01", "2011-02", roll="forward")) # E: datetime64 +reveal_type(np.busday_offset(["2011-01"], "2011-02", roll="forward")) # E: ndarray[Any, dtype[datetime64]] + +reveal_type(np.is_busday("2012")) # E: bool_ +reveal_type(np.is_busday(["2012"])) # E: ndarray[Any, dtype[bool_]] + +reveal_type(np.datetime_as_string(M)) # E: str_ +reveal_type(np.datetime_as_string(AR_M)) # E: ndarray[Any, dtype[str_]] + +reveal_type(np.compare_chararrays("a", "b", "!=", rstrip=False)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.compare_chararrays(b"a", b"a", "==", True)) # E: ndarray[Any, dtype[bool_]] + +reveal_type(np.add_docstring(func, "test")) # E: None + +reveal_type(np.nested_iters([AR_i8, AR_i8], [[0], [1]], flags=["c_index"])) # E: tuple[nditer] +reveal_type(np.nested_iters([AR_i8, AR_i8], [[0], [1]], op_flags=[["readonly", "readonly"]])) # E: tuple[nditer] +reveal_type(np.nested_iters([AR_i8, AR_i8], [[0], [1]], op_dtypes=np.int_)) # E: tuple[nditer] +reveal_type(np.nested_iters([AR_i8, AR_i8], [[0], [1]], order="C", casting="no")) # E: tuple[nditer] diff --git a/numpy/typing/tests/data/reveal/nbit_base_example.pyi b/numpy/typing/tests/data/reveal/nbit_base_example.pyi new file mode 100644 index 000000000000..d34f6f69a31d --- /dev/null +++ b/numpy/typing/tests/data/reveal/nbit_base_example.pyi @@ -0,0 +1,19 @@ +from typing import TypeVar, Union +import numpy as np +import numpy.typing as npt + +T1 = TypeVar("T1", bound=npt.NBitBase) +T2 = TypeVar("T2", bound=npt.NBitBase) + +def add(a: np.floating[T1], b: np.integer[T2]) -> np.floating[Union[T1, T2]]: + return a + b + +i8: np.int64 +i4: np.int32 +f8: np.float64 +f4: np.float32 + +reveal_type(add(f8, i8)) # E: {float64} +reveal_type(add(f4, i8)) # E: {float64} +reveal_type(add(f8, i4)) # E: {float64} +reveal_type(add(f4, i4)) # E: {float32} diff --git a/numpy/typing/tests/data/reveal/ndarray_conversion.pyi b/numpy/typing/tests/data/reveal/ndarray_conversion.pyi new file mode 100644 index 000000000000..6885d4fd6574 --- /dev/null +++ b/numpy/typing/tests/data/reveal/ndarray_conversion.pyi @@ -0,0 +1,51 @@ +import numpy as np +import numpy.typing as npt + +nd: npt.NDArray[np.int_] = np.array([[1, 2], [3, 4]]) + +# item +reveal_type(nd.item()) # E: int +reveal_type(nd.item(1)) # E: int +reveal_type(nd.item(0, 1)) # E: int +reveal_type(nd.item((0, 1))) # E: int + +# tolist +reveal_type(nd.tolist()) # E: Any + +# itemset does not return a value +# tostring is pretty simple +# tobytes is pretty simple +# tofile does not return a value +# dump does not return a value +# dumps is pretty simple + +# astype +reveal_type(nd.astype("float")) # E: ndarray[Any, dtype[Any]] +reveal_type(nd.astype(float)) # E: ndarray[Any, dtype[Any]] +reveal_type(nd.astype(np.float64)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(nd.astype(np.float64, "K")) # E: ndarray[Any, dtype[{float64}]] +reveal_type(nd.astype(np.float64, "K", "unsafe")) # E: ndarray[Any, dtype[{float64}]] +reveal_type(nd.astype(np.float64, "K", "unsafe", True)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(nd.astype(np.float64, "K", "unsafe", True, True)) # E: ndarray[Any, dtype[{float64}]] + +# byteswap +reveal_type(nd.byteswap()) # E: ndarray[Any, dtype[{int_}]] +reveal_type(nd.byteswap(True)) # E: ndarray[Any, dtype[{int_}]] + +# copy +reveal_type(nd.copy()) # E: ndarray[Any, dtype[{int_}]] +reveal_type(nd.copy("C")) # E: ndarray[Any, dtype[{int_}]] + +reveal_type(nd.view()) # E: ndarray[Any, dtype[{int_}]] +reveal_type(nd.view(np.float64)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(nd.view(float)) # E: ndarray[Any, dtype[Any]] +reveal_type(nd.view(np.float64, np.matrix)) # E: matrix[Any, Any] + +# getfield +reveal_type(nd.getfield("float")) # E: ndarray[Any, dtype[Any]] +reveal_type(nd.getfield(float)) # E: ndarray[Any, dtype[Any]] +reveal_type(nd.getfield(np.float64)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(nd.getfield(np.float64, 8)) # E: ndarray[Any, dtype[{float64}]] + +# setflags does not return a value +# fill does not return a value diff --git a/numpy/typing/tests/data/reveal/ndarray_misc.pyi b/numpy/typing/tests/data/reveal/ndarray_misc.pyi new file mode 100644 index 000000000000..f91d6351b9c8 --- /dev/null +++ b/numpy/typing/tests/data/reveal/ndarray_misc.pyi @@ -0,0 +1,214 @@ +""" +Tests for miscellaneous (non-magic) ``np.ndarray``/``np.generic`` methods. + +More extensive tests are performed for the methods' +function-based counterpart in `../from_numeric.py`. + +""" + +import operator +import ctypes as ct +from typing import Any + +import numpy as np +from numpy.typing import NDArray + +class SubClass(NDArray[np.object_]): ... + +f8: np.float64 +B: SubClass +AR_f8: NDArray[np.float64] +AR_i8: NDArray[np.int64] +AR_U: NDArray[np.str_] +AR_V: NDArray[np.void] + +ctypes_obj = AR_f8.ctypes + +reveal_type(AR_f8.__dlpack__()) # E: Any +reveal_type(AR_f8.__dlpack_device__()) # E: Tuple[int, Literal[0]] + +reveal_type(ctypes_obj.data) # E: int +reveal_type(ctypes_obj.shape) # E: ctypes.Array[{c_intp}] +reveal_type(ctypes_obj.strides) # E: ctypes.Array[{c_intp}] +reveal_type(ctypes_obj._as_parameter_) # E: ctypes.c_void_p + +reveal_type(ctypes_obj.data_as(ct.c_void_p)) # E: ctypes.c_void_p +reveal_type(ctypes_obj.shape_as(ct.c_longlong)) # E: ctypes.Array[ctypes.c_longlong] +reveal_type(ctypes_obj.strides_as(ct.c_ubyte)) # E: ctypes.Array[ctypes.c_ubyte] + +reveal_type(f8.all()) # E: bool_ +reveal_type(AR_f8.all()) # E: bool_ +reveal_type(AR_f8.all(axis=0)) # E: Any +reveal_type(AR_f8.all(keepdims=True)) # E: Any +reveal_type(AR_f8.all(out=B)) # E: SubClass + +reveal_type(f8.any()) # E: bool_ +reveal_type(AR_f8.any()) # E: bool_ +reveal_type(AR_f8.any(axis=0)) # E: Any +reveal_type(AR_f8.any(keepdims=True)) # E: Any +reveal_type(AR_f8.any(out=B)) # E: SubClass + +reveal_type(f8.argmax()) # E: {intp} +reveal_type(AR_f8.argmax()) # E: {intp} +reveal_type(AR_f8.argmax(axis=0)) # E: Any +reveal_type(AR_f8.argmax(out=B)) # E: SubClass + +reveal_type(f8.argmin()) # E: {intp} +reveal_type(AR_f8.argmin()) # E: {intp} +reveal_type(AR_f8.argmin(axis=0)) # E: Any +reveal_type(AR_f8.argmin(out=B)) # E: SubClass + +reveal_type(f8.argsort()) # E: ndarray[Any, Any] +reveal_type(AR_f8.argsort()) # E: ndarray[Any, Any] + +reveal_type(f8.astype(np.int64).choose([()])) # E: ndarray[Any, Any] +reveal_type(AR_f8.choose([0])) # E: ndarray[Any, Any] +reveal_type(AR_f8.choose([0], out=B)) # E: SubClass + +reveal_type(f8.clip(1)) # E: Any +reveal_type(AR_f8.clip(1)) # E: Any +reveal_type(AR_f8.clip(None, 1)) # E: Any +reveal_type(AR_f8.clip(1, out=B)) # E: SubClass +reveal_type(AR_f8.clip(None, 1, out=B)) # E: SubClass + +reveal_type(f8.compress([0])) # E: ndarray[Any, Any] +reveal_type(AR_f8.compress([0])) # E: ndarray[Any, Any] +reveal_type(AR_f8.compress([0], out=B)) # E: SubClass + +reveal_type(f8.conj()) # E: {float64} +reveal_type(AR_f8.conj()) # E: ndarray[Any, dtype[{float64}]] +reveal_type(B.conj()) # E: SubClass + +reveal_type(f8.conjugate()) # E: {float64} +reveal_type(AR_f8.conjugate()) # E: ndarray[Any, dtype[{float64}]] +reveal_type(B.conjugate()) # E: SubClass + +reveal_type(f8.cumprod()) # E: ndarray[Any, Any] +reveal_type(AR_f8.cumprod()) # E: ndarray[Any, Any] +reveal_type(AR_f8.cumprod(out=B)) # E: SubClass + +reveal_type(f8.cumsum()) # E: ndarray[Any, Any] +reveal_type(AR_f8.cumsum()) # E: ndarray[Any, Any] +reveal_type(AR_f8.cumsum(out=B)) # E: SubClass + +reveal_type(f8.max()) # E: Any +reveal_type(AR_f8.max()) # E: Any +reveal_type(AR_f8.max(axis=0)) # E: Any +reveal_type(AR_f8.max(keepdims=True)) # E: Any +reveal_type(AR_f8.max(out=B)) # E: SubClass + +reveal_type(f8.mean()) # E: Any +reveal_type(AR_f8.mean()) # E: Any +reveal_type(AR_f8.mean(axis=0)) # E: Any +reveal_type(AR_f8.mean(keepdims=True)) # E: Any +reveal_type(AR_f8.mean(out=B)) # E: SubClass + +reveal_type(f8.min()) # E: Any +reveal_type(AR_f8.min()) # E: Any +reveal_type(AR_f8.min(axis=0)) # E: Any +reveal_type(AR_f8.min(keepdims=True)) # E: Any +reveal_type(AR_f8.min(out=B)) # E: SubClass + +reveal_type(f8.newbyteorder()) # E: {float64} +reveal_type(AR_f8.newbyteorder()) # E: ndarray[Any, dtype[{float64}]] +reveal_type(B.newbyteorder('|')) # E: SubClass + +reveal_type(f8.prod()) # E: Any +reveal_type(AR_f8.prod()) # E: Any +reveal_type(AR_f8.prod(axis=0)) # E: Any +reveal_type(AR_f8.prod(keepdims=True)) # E: Any +reveal_type(AR_f8.prod(out=B)) # E: SubClass + +reveal_type(f8.ptp()) # E: Any +reveal_type(AR_f8.ptp()) # E: Any +reveal_type(AR_f8.ptp(axis=0)) # E: Any +reveal_type(AR_f8.ptp(keepdims=True)) # E: Any +reveal_type(AR_f8.ptp(out=B)) # E: SubClass + +reveal_type(f8.round()) # E: {float64} +reveal_type(AR_f8.round()) # E: ndarray[Any, dtype[{float64}]] +reveal_type(AR_f8.round(out=B)) # E: SubClass + +reveal_type(f8.repeat(1)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(AR_f8.repeat(1)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(B.repeat(1)) # E: ndarray[Any, dtype[object_]] + +reveal_type(f8.std()) # E: Any +reveal_type(AR_f8.std()) # E: Any +reveal_type(AR_f8.std(axis=0)) # E: Any +reveal_type(AR_f8.std(keepdims=True)) # E: Any +reveal_type(AR_f8.std(out=B)) # E: SubClass + +reveal_type(f8.sum()) # E: Any +reveal_type(AR_f8.sum()) # E: Any +reveal_type(AR_f8.sum(axis=0)) # E: Any +reveal_type(AR_f8.sum(keepdims=True)) # E: Any +reveal_type(AR_f8.sum(out=B)) # E: SubClass + +reveal_type(f8.take(0)) # E: {float64} +reveal_type(AR_f8.take(0)) # E: {float64} +reveal_type(AR_f8.take([0])) # E: ndarray[Any, dtype[{float64}]] +reveal_type(AR_f8.take(0, out=B)) # E: SubClass +reveal_type(AR_f8.take([0], out=B)) # E: SubClass + +reveal_type(f8.var()) # E: Any +reveal_type(AR_f8.var()) # E: Any +reveal_type(AR_f8.var(axis=0)) # E: Any +reveal_type(AR_f8.var(keepdims=True)) # E: Any +reveal_type(AR_f8.var(out=B)) # E: SubClass + +reveal_type(AR_f8.argpartition([0])) # E: ndarray[Any, dtype[{intp}]] + +reveal_type(AR_f8.diagonal()) # E: ndarray[Any, dtype[{float64}]] + +reveal_type(AR_f8.dot(1)) # E: ndarray[Any, Any] +reveal_type(AR_f8.dot([1])) # E: Any +reveal_type(AR_f8.dot(1, out=B)) # E: SubClass + +reveal_type(AR_f8.nonzero()) # E: tuple[ndarray[Any, dtype[{intp}]]] + +reveal_type(AR_f8.searchsorted(1)) # E: {intp} +reveal_type(AR_f8.searchsorted([1])) # E: ndarray[Any, dtype[{intp}]] + +reveal_type(AR_f8.trace()) # E: Any +reveal_type(AR_f8.trace(out=B)) # E: SubClass + +reveal_type(AR_f8.item()) # E: float +reveal_type(AR_U.item()) # E: str + +reveal_type(AR_f8.ravel()) # E: ndarray[Any, dtype[{float64}]] +reveal_type(AR_U.ravel()) # E: ndarray[Any, dtype[str_]] + +reveal_type(AR_f8.flatten()) # E: ndarray[Any, dtype[{float64}]] +reveal_type(AR_U.flatten()) # E: ndarray[Any, dtype[str_]] + +reveal_type(AR_f8.reshape(1)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(AR_U.reshape(1)) # E: ndarray[Any, dtype[str_]] + +reveal_type(int(AR_f8)) # E: int +reveal_type(int(AR_U)) # E: int + +reveal_type(float(AR_f8)) # E: float +reveal_type(float(AR_U)) # E: float + +reveal_type(complex(AR_f8)) # E: complex + +reveal_type(operator.index(AR_i8)) # E: int + +reveal_type(AR_f8.__array_prepare__(B)) # E: ndarray[Any, dtype[object_]] +reveal_type(AR_f8.__array_wrap__(B)) # E: ndarray[Any, dtype[object_]] + +reveal_type(AR_V[0]) # E: Any +reveal_type(AR_V[0, 0]) # E: Any +reveal_type(AR_V[AR_i8]) # E: Any +reveal_type(AR_V[AR_i8, AR_i8]) # E: Any +reveal_type(AR_V[AR_i8, None]) # E: ndarray[Any, dtype[void]] +reveal_type(AR_V[0, ...]) # E: ndarray[Any, dtype[void]] +reveal_type(AR_V[:]) # E: ndarray[Any, dtype[void]] +reveal_type(AR_V["a"]) # E: ndarray[Any, dtype[Any]] +reveal_type(AR_V[["a", "b"]]) # E: ndarray[Any, dtype[void]] + +reveal_type(AR_f8.dump("test_file")) # E: None +reveal_type(AR_f8.dump(b"test_file")) # E: None +with open("test_file", "wb") as f: + reveal_type(AR_f8.dump(f)) # E: None diff --git a/numpy/typing/tests/data/reveal/ndarray_shape_manipulation.pyi b/numpy/typing/tests/data/reveal/ndarray_shape_manipulation.pyi new file mode 100644 index 000000000000..c000bf45c3f4 --- /dev/null +++ b/numpy/typing/tests/data/reveal/ndarray_shape_manipulation.pyi @@ -0,0 +1,35 @@ +import numpy as np + +nd = np.array([[1, 2], [3, 4]]) + +# reshape +reveal_type(nd.reshape()) # E: ndarray +reveal_type(nd.reshape(4)) # E: ndarray +reveal_type(nd.reshape(2, 2)) # E: ndarray +reveal_type(nd.reshape((2, 2))) # E: ndarray + +reveal_type(nd.reshape((2, 2), order="C")) # E: ndarray +reveal_type(nd.reshape(4, order="C")) # E: ndarray + +# resize does not return a value + +# transpose +reveal_type(nd.transpose()) # E: ndarray +reveal_type(nd.transpose(1, 0)) # E: ndarray +reveal_type(nd.transpose((1, 0))) # E: ndarray + +# swapaxes +reveal_type(nd.swapaxes(0, 1)) # E: ndarray + +# flatten +reveal_type(nd.flatten()) # E: ndarray +reveal_type(nd.flatten("C")) # E: ndarray + +# ravel +reveal_type(nd.ravel()) # E: ndarray +reveal_type(nd.ravel("C")) # E: ndarray + +# squeeze +reveal_type(nd.squeeze()) # E: ndarray +reveal_type(nd.squeeze(0)) # E: ndarray +reveal_type(nd.squeeze((0, 2))) # E: ndarray diff --git a/numpy/typing/tests/data/reveal/nditer.pyi b/numpy/typing/tests/data/reveal/nditer.pyi new file mode 100644 index 000000000000..65861da54c16 --- /dev/null +++ b/numpy/typing/tests/data/reveal/nditer.pyi @@ -0,0 +1,46 @@ +import numpy as np + +nditer_obj: np.nditer + +reveal_type(np.nditer([0, 1], flags=["c_index"])) # E: nditer +reveal_type(np.nditer([0, 1], op_flags=[["readonly", "readonly"]])) # E: nditer +reveal_type(np.nditer([0, 1], op_dtypes=np.int_)) # E: nditer +reveal_type(np.nditer([0, 1], order="C", casting="no")) # E: nditer + +reveal_type(nditer_obj.dtypes) # E: tuple[dtype[Any]] +reveal_type(nditer_obj.finished) # E: bool +reveal_type(nditer_obj.has_delayed_bufalloc) # E: bool +reveal_type(nditer_obj.has_index) # E: bool +reveal_type(nditer_obj.has_multi_index) # E: bool +reveal_type(nditer_obj.index) # E: int +reveal_type(nditer_obj.iterationneedsapi) # E: bool +reveal_type(nditer_obj.iterindex) # E: int +reveal_type(nditer_obj.iterrange) # E: tuple[builtins.int] +reveal_type(nditer_obj.itersize) # E: int +reveal_type(nditer_obj.itviews) # E: tuple[ndarray[Any, dtype[Any]]] +reveal_type(nditer_obj.multi_index) # E: tuple[builtins.int] +reveal_type(nditer_obj.ndim) # E: int +reveal_type(nditer_obj.nop) # E: int +reveal_type(nditer_obj.operands) # E: tuple[ndarray[Any, dtype[Any]]] +reveal_type(nditer_obj.shape) # E: tuple[builtins.int] +reveal_type(nditer_obj.value) # E: tuple[ndarray[Any, dtype[Any]]] + +reveal_type(nditer_obj.close()) # E: None +reveal_type(nditer_obj.copy()) # E: nditer +reveal_type(nditer_obj.debug_print()) # E: None +reveal_type(nditer_obj.enable_external_loop()) # E: None +reveal_type(nditer_obj.iternext()) # E: bool +reveal_type(nditer_obj.remove_axis(0)) # E: None +reveal_type(nditer_obj.remove_multi_index()) # E: None +reveal_type(nditer_obj.reset()) # E: None + +reveal_type(len(nditer_obj)) # E: int +reveal_type(iter(nditer_obj)) # E: Iterator[builtins.tuple[ndarray[Any, dtype[Any]]]] +reveal_type(next(nditer_obj)) # E: tuple[ndarray[Any, dtype[Any]]] +reveal_type(nditer_obj.__copy__()) # E: nditer +with nditer_obj as f: + reveal_type(f) # E: nditer +reveal_type(nditer_obj[0]) # E: ndarray[Any, dtype[Any]] +reveal_type(nditer_obj[:]) # E: tuple[ndarray[Any, dtype[Any]]] +nditer_obj[0] = 0 +nditer_obj[:] = [0, 1] diff --git a/numpy/typing/tests/data/reveal/nested_sequence.pyi b/numpy/typing/tests/data/reveal/nested_sequence.pyi new file mode 100644 index 000000000000..4d3aad467efc --- /dev/null +++ b/numpy/typing/tests/data/reveal/nested_sequence.pyi @@ -0,0 +1,24 @@ +from typing import Sequence, Tuple, List, Any +import numpy.typing as npt + +a: Sequence[int] +b: Sequence[Sequence[int]] +c: Sequence[Sequence[Sequence[int]]] +d: Sequence[Sequence[Sequence[Sequence[int]]]] +e: Sequence[bool] +f: Tuple[int, ...] +g: List[int] +h: Sequence[Any] + +def func(a: npt._NestedSequence[int]) -> None: + ... + +reveal_type(func(a)) # E: None +reveal_type(func(b)) # E: None +reveal_type(func(c)) # E: None +reveal_type(func(d)) # E: None +reveal_type(func(e)) # E: None +reveal_type(func(f)) # E: None +reveal_type(func(g)) # E: None +reveal_type(func(h)) # E: None +reveal_type(func(range(15))) # E: None diff --git a/numpy/typing/tests/data/reveal/npyio.pyi b/numpy/typing/tests/data/reveal/npyio.pyi new file mode 100644 index 000000000000..637bdb6619fd --- /dev/null +++ b/numpy/typing/tests/data/reveal/npyio.pyi @@ -0,0 +1,92 @@ +import re +import pathlib +from typing import IO, List + +import numpy.typing as npt +import numpy as np + +str_path: str +pathlib_path: pathlib.Path +str_file: IO[str] +bytes_file: IO[bytes] + +bag_obj: np.lib.npyio.BagObj[int] +npz_file: np.lib.npyio.NpzFile + +AR_i8: npt.NDArray[np.int64] +AR_LIKE_f8: List[float] + +class BytesWriter: + def write(self, data: bytes) -> None: ... + +class BytesReader: + def read(self, n: int = ...) -> bytes: ... + def seek(self, offset: int, whence: int = ...) -> int: ... + +bytes_writer: BytesWriter +bytes_reader: BytesReader + +reveal_type(bag_obj.a) # E: int +reveal_type(bag_obj.b) # E: int + +reveal_type(npz_file.zip) # E: zipfile.ZipFile +reveal_type(npz_file.fid) # E: Union[None, typing.IO[builtins.str]] +reveal_type(npz_file.files) # E: list[builtins.str] +reveal_type(npz_file.allow_pickle) # E: bool +reveal_type(npz_file.pickle_kwargs) # E: Union[None, typing.Mapping[builtins.str, Any]] +reveal_type(npz_file.f) # E: lib.npyio.BagObj[lib.npyio.NpzFile] +reveal_type(npz_file["test"]) # E: ndarray[Any, dtype[Any]] +reveal_type(len(npz_file)) # E: int +with npz_file as f: + reveal_type(f) # E: lib.npyio.NpzFile + +reveal_type(np.load(bytes_file)) # E: Any +reveal_type(np.load(pathlib_path, allow_pickle=True)) # E: Any +reveal_type(np.load(str_path, encoding="bytes")) # E: Any +reveal_type(np.load(bytes_reader)) # E: Any + +reveal_type(np.save(bytes_file, AR_LIKE_f8)) # E: None +reveal_type(np.save(pathlib_path, AR_i8, allow_pickle=True)) # E: None +reveal_type(np.save(str_path, AR_LIKE_f8)) # E: None +reveal_type(np.save(bytes_writer, AR_LIKE_f8)) # E: None + +reveal_type(np.savez(bytes_file, AR_LIKE_f8)) # E: None +reveal_type(np.savez(pathlib_path, ar1=AR_i8, ar2=AR_i8)) # E: None +reveal_type(np.savez(str_path, AR_LIKE_f8, ar1=AR_i8)) # E: None +reveal_type(np.savez(bytes_writer, AR_LIKE_f8, ar1=AR_i8)) # E: None + +reveal_type(np.savez_compressed(bytes_file, AR_LIKE_f8)) # E: None +reveal_type(np.savez_compressed(pathlib_path, ar1=AR_i8, ar2=AR_i8)) # E: None +reveal_type(np.savez_compressed(str_path, AR_LIKE_f8, ar1=AR_i8)) # E: None +reveal_type(np.savez_compressed(bytes_writer, AR_LIKE_f8, ar1=AR_i8)) # E: None + +reveal_type(np.loadtxt(bytes_file)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.loadtxt(pathlib_path, dtype=np.str_)) # E: ndarray[Any, dtype[str_]] +reveal_type(np.loadtxt(str_path, dtype=str, skiprows=2)) # E: ndarray[Any, dtype[Any]] +reveal_type(np.loadtxt(str_file, comments="test")) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.loadtxt(str_file, comments=None)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.loadtxt(str_path, delimiter="\n")) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.loadtxt(str_path, ndmin=2)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.loadtxt(["1", "2", "3"])) # E: ndarray[Any, dtype[{float64}]] + +reveal_type(np.fromregex(bytes_file, "test", np.float64)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.fromregex(str_file, b"test", dtype=float)) # E: ndarray[Any, dtype[Any]] +reveal_type(np.fromregex(str_path, re.compile("test"), dtype=np.str_, encoding="utf8")) # E: ndarray[Any, dtype[str_]] +reveal_type(np.fromregex(pathlib_path, "test", np.float64)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.fromregex(bytes_reader, "test", np.float64)) # E: ndarray[Any, dtype[{float64}]] + +reveal_type(np.genfromtxt(bytes_file)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.genfromtxt(pathlib_path, dtype=np.str_)) # E: ndarray[Any, dtype[str_]] +reveal_type(np.genfromtxt(str_path, dtype=str, skiprows=2)) # E: ndarray[Any, dtype[Any]] +reveal_type(np.genfromtxt(str_file, comments="test")) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.genfromtxt(str_path, delimiter="\n")) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.genfromtxt(str_path, ndmin=2)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.genfromtxt(["1", "2", "3"], ndmin=2)) # E: ndarray[Any, dtype[{float64}]] + +reveal_type(np.recfromtxt(bytes_file)) # E: recarray[Any, dtype[record]] +reveal_type(np.recfromtxt(pathlib_path, usemask=True)) # E: ma.mrecords.MaskedRecords[Any, dtype[void]] +reveal_type(np.recfromtxt(["1", "2", "3"])) # E: recarray[Any, dtype[record]] + +reveal_type(np.recfromcsv(bytes_file)) # E: recarray[Any, dtype[record]] +reveal_type(np.recfromcsv(pathlib_path, usemask=True)) # E: ma.mrecords.MaskedRecords[Any, dtype[void]] +reveal_type(np.recfromcsv(["1", "2", "3"])) # E: recarray[Any, dtype[record]] diff --git a/numpy/typing/tests/data/reveal/numeric.pyi b/numpy/typing/tests/data/reveal/numeric.pyi new file mode 100644 index 000000000000..bf5653937c19 --- /dev/null +++ b/numpy/typing/tests/data/reveal/numeric.pyi @@ -0,0 +1,134 @@ +""" +Tests for :mod:`core.numeric`. + +Does not include tests which fall under ``array_constructors``. + +""" + +from typing import List +import numpy as np +import numpy.typing as npt + +class SubClass(npt.NDArray[np.int64]): + ... + +i8: np.int64 + +AR_b: npt.NDArray[np.bool_] +AR_u8: npt.NDArray[np.uint64] +AR_i8: npt.NDArray[np.int64] +AR_f8: npt.NDArray[np.float64] +AR_c16: npt.NDArray[np.complex128] +AR_m: npt.NDArray[np.timedelta64] +AR_O: npt.NDArray[np.object_] + +B: List[int] +C: SubClass + +reveal_type(np.count_nonzero(i8)) # E: int +reveal_type(np.count_nonzero(AR_i8)) # E: int +reveal_type(np.count_nonzero(B)) # E: int +reveal_type(np.count_nonzero(AR_i8, keepdims=True)) # E: Any +reveal_type(np.count_nonzero(AR_i8, axis=0)) # E: Any + +reveal_type(np.isfortran(i8)) # E: bool +reveal_type(np.isfortran(AR_i8)) # E: bool + +reveal_type(np.argwhere(i8)) # E: ndarray[Any, dtype[{intp}]] +reveal_type(np.argwhere(AR_i8)) # E: ndarray[Any, dtype[{intp}]] + +reveal_type(np.flatnonzero(i8)) # E: ndarray[Any, dtype[{intp}]] +reveal_type(np.flatnonzero(AR_i8)) # E: ndarray[Any, dtype[{intp}]] + +reveal_type(np.correlate(B, AR_i8, mode="valid")) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(np.correlate(AR_i8, AR_i8, mode="same")) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(np.correlate(AR_b, AR_b)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.correlate(AR_b, AR_u8)) # E: ndarray[Any, dtype[unsignedinteger[Any]]] +reveal_type(np.correlate(AR_i8, AR_b)) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(np.correlate(AR_i8, AR_f8)) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.correlate(AR_i8, AR_c16)) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] +reveal_type(np.correlate(AR_i8, AR_m)) # E: ndarray[Any, dtype[timedelta64]] +reveal_type(np.correlate(AR_O, AR_O)) # E: ndarray[Any, dtype[object_]] + +reveal_type(np.convolve(B, AR_i8, mode="valid")) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(np.convolve(AR_i8, AR_i8, mode="same")) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(np.convolve(AR_b, AR_b)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.convolve(AR_b, AR_u8)) # E: ndarray[Any, dtype[unsignedinteger[Any]]] +reveal_type(np.convolve(AR_i8, AR_b)) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(np.convolve(AR_i8, AR_f8)) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.convolve(AR_i8, AR_c16)) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] +reveal_type(np.convolve(AR_i8, AR_m)) # E: ndarray[Any, dtype[timedelta64]] +reveal_type(np.convolve(AR_O, AR_O)) # E: ndarray[Any, dtype[object_]] + +reveal_type(np.outer(i8, AR_i8)) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(np.outer(B, AR_i8)) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(np.outer(AR_i8, AR_i8)) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(np.outer(AR_i8, AR_i8, out=C)) # E: SubClass +reveal_type(np.outer(AR_b, AR_b)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.outer(AR_b, AR_u8)) # E: ndarray[Any, dtype[unsignedinteger[Any]]] +reveal_type(np.outer(AR_i8, AR_b)) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(np.convolve(AR_i8, AR_f8)) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.outer(AR_i8, AR_c16)) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] +reveal_type(np.outer(AR_i8, AR_m)) # E: ndarray[Any, dtype[timedelta64]] +reveal_type(np.outer(AR_O, AR_O)) # E: ndarray[Any, dtype[object_]] + +reveal_type(np.tensordot(B, AR_i8)) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(np.tensordot(AR_i8, AR_i8)) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(np.tensordot(AR_i8, AR_i8, axes=0)) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(np.tensordot(AR_i8, AR_i8, axes=(0, 1))) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(np.tensordot(AR_b, AR_b)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.tensordot(AR_b, AR_u8)) # E: ndarray[Any, dtype[unsignedinteger[Any]]] +reveal_type(np.tensordot(AR_i8, AR_b)) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(np.tensordot(AR_i8, AR_f8)) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.tensordot(AR_i8, AR_c16)) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] +reveal_type(np.tensordot(AR_i8, AR_m)) # E: ndarray[Any, dtype[timedelta64]] +reveal_type(np.tensordot(AR_O, AR_O)) # E: ndarray[Any, dtype[object_]] + +reveal_type(np.isscalar(i8)) # E: bool +reveal_type(np.isscalar(AR_i8)) # E: bool +reveal_type(np.isscalar(B)) # E: bool + +reveal_type(np.roll(AR_i8, 1)) # E: ndarray[Any, dtype[{int64}]] +reveal_type(np.roll(AR_i8, (1, 2))) # E: ndarray[Any, dtype[{int64}]] +reveal_type(np.roll(B, 1)) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.rollaxis(AR_i8, 0, 1)) # E: ndarray[Any, dtype[{int64}]] + +reveal_type(np.moveaxis(AR_i8, 0, 1)) # E: ndarray[Any, dtype[{int64}]] +reveal_type(np.moveaxis(AR_i8, (0, 1), (1, 2))) # E: ndarray[Any, dtype[{int64}]] + +reveal_type(np.cross(B, AR_i8)) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(np.cross(AR_i8, AR_i8)) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(np.cross(AR_b, AR_u8)) # E: ndarray[Any, dtype[unsignedinteger[Any]]] +reveal_type(np.cross(AR_i8, AR_b)) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(np.cross(AR_i8, AR_f8)) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.cross(AR_i8, AR_c16)) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] +reveal_type(np.cross(AR_O, AR_O)) # E: ndarray[Any, dtype[object_]] + +reveal_type(np.indices([0, 1, 2])) # E: ndarray[Any, dtype[{int_}]] +reveal_type(np.indices([0, 1, 2], sparse=True)) # E: tuple[ndarray[Any, dtype[{int_}]]] +reveal_type(np.indices([0, 1, 2], dtype=np.float64)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.indices([0, 1, 2], sparse=True, dtype=np.float64)) # E: tuple[ndarray[Any, dtype[{float64}]]] +reveal_type(np.indices([0, 1, 2], dtype=float)) # E: ndarray[Any, dtype[Any]] +reveal_type(np.indices([0, 1, 2], sparse=True, dtype=float)) # E: tuple[ndarray[Any, dtype[Any]]] + +reveal_type(np.binary_repr(1)) # E: str + +reveal_type(np.base_repr(1)) # E: str + +reveal_type(np.allclose(i8, AR_i8)) # E: bool +reveal_type(np.allclose(B, AR_i8)) # E: bool +reveal_type(np.allclose(AR_i8, AR_i8)) # E: bool + +reveal_type(np.isclose(i8, i8)) # E: bool_ +reveal_type(np.isclose(i8, AR_i8)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.isclose(B, AR_i8)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.isclose(AR_i8, AR_i8)) # E: ndarray[Any, dtype[bool_]] + +reveal_type(np.array_equal(i8, AR_i8)) # E: bool +reveal_type(np.array_equal(B, AR_i8)) # E: bool +reveal_type(np.array_equal(AR_i8, AR_i8)) # E: bool + +reveal_type(np.array_equiv(i8, AR_i8)) # E: bool +reveal_type(np.array_equiv(B, AR_i8)) # E: bool +reveal_type(np.array_equiv(AR_i8, AR_i8)) # E: bool diff --git a/numpy/typing/tests/data/reveal/numerictypes.pyi b/numpy/typing/tests/data/reveal/numerictypes.pyi new file mode 100644 index 000000000000..cc2335264113 --- /dev/null +++ b/numpy/typing/tests/data/reveal/numerictypes.pyi @@ -0,0 +1,42 @@ +import numpy as np + +reveal_type(np.maximum_sctype(np.float64)) # E: Type[{float64}] +reveal_type(np.maximum_sctype("f8")) # E: Type[Any] + +reveal_type(np.issctype(np.float64)) # E: bool +reveal_type(np.issctype("foo")) # E: Literal[False] + +reveal_type(np.obj2sctype(np.float64)) # E: Union[None, Type[{float64}]] +reveal_type(np.obj2sctype(np.float64, default=False)) # E: Union[builtins.bool, Type[{float64}]] +reveal_type(np.obj2sctype("S8")) # E: Union[None, Type[Any]] +reveal_type(np.obj2sctype("S8", default=None)) # E: Union[None, Type[Any]] +reveal_type(np.obj2sctype("foo", default=False)) # E: Union[builtins.bool, Type[Any]] +reveal_type(np.obj2sctype(1)) # E: None +reveal_type(np.obj2sctype(1, default=False)) # E: bool + +reveal_type(np.issubclass_(np.float64, float)) # E: bool +reveal_type(np.issubclass_(np.float64, (int, float))) # E: bool +reveal_type(np.issubclass_(1, 1)) # E: Literal[False] + +reveal_type(np.sctype2char("S8")) # E: str +reveal_type(np.sctype2char(list)) # E: str + +reveal_type(np.find_common_type([np.int64], [np.int64])) # E: dtype[Any] + +reveal_type(np.cast[int]) # E: _CastFunc +reveal_type(np.cast["i8"]) # E: _CastFunc +reveal_type(np.cast[np.int64]) # E: _CastFunc + +reveal_type(np.nbytes[int]) # E: int +reveal_type(np.nbytes["i8"]) # E: int +reveal_type(np.nbytes[np.int64]) # E: int + +reveal_type(np.ScalarType) # E: Tuple +reveal_type(np.ScalarType[0]) # E: Type[builtins.int] +reveal_type(np.ScalarType[4]) # E: Type[builtins.bool] +reveal_type(np.ScalarType[9]) # E: Type[{csingle}] +reveal_type(np.ScalarType[11]) # E: Type[{clongdouble}] + +reveal_type(np.typecodes["Character"]) # E: Literal['c'] +reveal_type(np.typecodes["Complex"]) # E: Literal['FDG'] +reveal_type(np.typecodes["All"]) # E: Literal['?bhilqpBHILQPefdgFDGSUVOMm'] diff --git a/numpy/typing/tests/data/reveal/random.pyi b/numpy/typing/tests/data/reveal/random.pyi new file mode 100644 index 000000000000..4e06aa7d5bd7 --- /dev/null +++ b/numpy/typing/tests/data/reveal/random.pyi @@ -0,0 +1,1539 @@ +from __future__ import annotations + +from typing import Any, List + +import numpy as np + +def_rng = np.random.default_rng() +seed_seq = np.random.SeedSequence() +mt19937 = np.random.MT19937() +pcg64 = np.random.PCG64() +sfc64 = np.random.SFC64() +philox = np.random.Philox() +seedless_seq = np.random.bit_generator.SeedlessSeedSequence() + +reveal_type(def_rng) # E: random._generator.Generator +reveal_type(mt19937) # E: random._mt19937.MT19937 +reveal_type(pcg64) # E: random._pcg64.PCG64 +reveal_type(sfc64) # E: random._sfc64.SFC64 +reveal_type(philox) # E: random._philox.Philox +reveal_type(seed_seq) # E: random.bit_generator.SeedSequence +reveal_type(seedless_seq) # E: random.bit_generator.SeedlessSeedSequence + +mt19937_jumped = mt19937.jumped() +mt19937_jumped3 = mt19937.jumped(3) +mt19937_raw = mt19937.random_raw() +mt19937_raw_arr = mt19937.random_raw(5) + +reveal_type(mt19937_jumped) # E: random._mt19937.MT19937 +reveal_type(mt19937_jumped3) # E: random._mt19937.MT19937 +reveal_type(mt19937_raw) # E: int +reveal_type(mt19937_raw_arr) # E: ndarray[Any, dtype[unsignedinteger[typing._64Bit]]] +reveal_type(mt19937.lock) # E: threading.Lock + +pcg64_jumped = pcg64.jumped() +pcg64_jumped3 = pcg64.jumped(3) +pcg64_adv = pcg64.advance(3) +pcg64_raw = pcg64.random_raw() +pcg64_raw_arr = pcg64.random_raw(5) + +reveal_type(pcg64_jumped) # E: random._pcg64.PCG64 +reveal_type(pcg64_jumped3) # E: random._pcg64.PCG64 +reveal_type(pcg64_adv) # E: random._pcg64.PCG64 +reveal_type(pcg64_raw) # E: int +reveal_type(pcg64_raw_arr) # E: ndarray[Any, dtype[unsignedinteger[typing._64Bit]]] +reveal_type(pcg64.lock) # E: threading.Lock + +philox_jumped = philox.jumped() +philox_jumped3 = philox.jumped(3) +philox_adv = philox.advance(3) +philox_raw = philox.random_raw() +philox_raw_arr = philox.random_raw(5) + +reveal_type(philox_jumped) # E: random._philox.Philox +reveal_type(philox_jumped3) # E: random._philox.Philox +reveal_type(philox_adv) # E: random._philox.Philox +reveal_type(philox_raw) # E: int +reveal_type(philox_raw_arr) # E: ndarray[Any, dtype[unsignedinteger[typing._64Bit]]] +reveal_type(philox.lock) # E: threading.Lock + +sfc64_raw = sfc64.random_raw() +sfc64_raw_arr = sfc64.random_raw(5) + +reveal_type(sfc64_raw) # E: int +reveal_type(sfc64_raw_arr) # E: ndarray[Any, dtype[unsignedinteger[typing._64Bit]]] +reveal_type(sfc64.lock) # E: threading.Lock + +reveal_type(seed_seq.pool) # ndarray[Any, dtype[unsignedinteger[typing._32Bit]]] +reveal_type(seed_seq.entropy) # E:Union[None, int, Sequence[int]] +reveal_type(seed_seq.spawn(1)) # E: list[random.bit_generator.SeedSequence] +reveal_type(seed_seq.generate_state(8, "uint32")) # E: ndarray[Any, dtype[Union[unsignedinteger[typing._32Bit], unsignedinteger[typing._64Bit]]]] +reveal_type(seed_seq.generate_state(8, "uint64")) # E: ndarray[Any, dtype[Union[unsignedinteger[typing._32Bit], unsignedinteger[typing._64Bit]]]] + + +def_gen: np.random.Generator = np.random.default_rng() + +D_arr_0p1: np.ndarray[Any, np.dtype[np.float64]] = np.array([0.1]) +D_arr_0p5: np.ndarray[Any, np.dtype[np.float64]] = np.array([0.5]) +D_arr_0p9: np.ndarray[Any, np.dtype[np.float64]] = np.array([0.9]) +D_arr_1p5: np.ndarray[Any, np.dtype[np.float64]] = np.array([1.5]) +I_arr_10: np.ndarray[Any, np.dtype[np.int_]] = np.array([10], dtype=np.int_) +I_arr_20: np.ndarray[Any, np.dtype[np.int_]] = np.array([20], dtype=np.int_) +D_arr_like_0p1: List[float] = [0.1] +D_arr_like_0p5: List[float] = [0.5] +D_arr_like_0p9: List[float] = [0.9] +D_arr_like_1p5: List[float] = [1.5] +I_arr_like_10: List[int] = [10] +I_arr_like_20: List[int] = [20] +D_2D_like: List[List[float]] = [[1, 2], [2, 3], [3, 4], [4, 5.1]] +D_2D: np.ndarray[Any, np.dtype[np.float64]] = np.array(D_2D_like) +S_out: np.ndarray[Any, np.dtype[np.float32]] = np.empty(1, dtype=np.float32) +D_out: np.ndarray[Any, np.dtype[np.float64]] = np.empty(1) + +reveal_type(def_gen.standard_normal()) # E: float +reveal_type(def_gen.standard_normal(dtype=np.float32)) # E: float +reveal_type(def_gen.standard_normal(dtype="float32")) # E: float +reveal_type(def_gen.standard_normal(dtype="double")) # E: float +reveal_type(def_gen.standard_normal(dtype=np.float64)) # E: float +reveal_type(def_gen.standard_normal(size=None)) # E: float +reveal_type(def_gen.standard_normal(size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]]] +reveal_type(def_gen.standard_normal(size=1, dtype=np.float32)) # E: ndarray[Any, dtype[floating[typing._32Bit]]] +reveal_type(def_gen.standard_normal(size=1, dtype="f4")) # E: ndarray[Any, dtype[floating[typing._32Bit]]] +reveal_type(def_gen.standard_normal(size=1, dtype="float32", out=S_out)) # E: ndarray[Any, dtype[floating[typing._32Bit]]] +reveal_type(def_gen.standard_normal(dtype=np.float32, out=S_out)) # E: ndarray[Any, dtype[floating[typing._32Bit]]] +reveal_type(def_gen.standard_normal(size=1, dtype=np.float64)) # E: ndarray[Any, dtype[floating[typing._64Bit]]] +reveal_type(def_gen.standard_normal(size=1, dtype="float64")) # E: ndarray[Any, dtype[floating[typing._64Bit]]] +reveal_type(def_gen.standard_normal(size=1, dtype="f8")) # E: ndarray[Any, dtype[floating[typing._64Bit]]] +reveal_type(def_gen.standard_normal(out=D_out)) # E: ndarray[Any, dtype[floating[typing._64Bit]]] +reveal_type(def_gen.standard_normal(size=1, dtype="float64")) # E: ndarray[Any, dtype[floating[typing._64Bit]]] +reveal_type(def_gen.standard_normal(size=1, dtype="float64", out=D_out)) # E: ndarray[Any, dtype[floating[typing._64Bit]]] + +reveal_type(def_gen.random()) # E: float +reveal_type(def_gen.random(dtype=np.float32)) # E: float +reveal_type(def_gen.random(dtype="float32")) # E: float +reveal_type(def_gen.random(dtype="double")) # E: float +reveal_type(def_gen.random(dtype=np.float64)) # E: float +reveal_type(def_gen.random(size=None)) # E: float +reveal_type(def_gen.random(size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]]] +reveal_type(def_gen.random(size=1, dtype=np.float32)) # E: ndarray[Any, dtype[floating[typing._32Bit]]] +reveal_type(def_gen.random(size=1, dtype="f4")) # E: ndarray[Any, dtype[floating[typing._32Bit]]] +reveal_type(def_gen.random(size=1, dtype="float32", out=S_out)) # E: ndarray[Any, dtype[floating[typing._32Bit]]] +reveal_type(def_gen.random(dtype=np.float32, out=S_out)) # E: ndarray[Any, dtype[floating[typing._32Bit]]] +reveal_type(def_gen.random(size=1, dtype=np.float64)) # E: ndarray[Any, dtype[floating[typing._64Bit]]] +reveal_type(def_gen.random(size=1, dtype="float64")) # E: ndarray[Any, dtype[floating[typing._64Bit]]] +reveal_type(def_gen.random(size=1, dtype="f8")) # E: ndarray[Any, dtype[floating[typing._64Bit]]] +reveal_type(def_gen.random(out=D_out)) # E: ndarray[Any, dtype[floating[typing._64Bit]]] +reveal_type(def_gen.random(size=1, dtype="float64")) # E: ndarray[Any, dtype[floating[typing._64Bit]]] +reveal_type(def_gen.random(size=1, dtype="float64", out=D_out)) # E: ndarray[Any, dtype[floating[typing._64Bit]]] + +reveal_type(def_gen.standard_cauchy()) # E: float +reveal_type(def_gen.standard_cauchy(size=None)) # E: float +reveal_type(def_gen.standard_cauchy(size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(def_gen.standard_exponential()) # E: float +reveal_type(def_gen.standard_exponential(method="inv")) # E: float +reveal_type(def_gen.standard_exponential(dtype=np.float32)) # E: float +reveal_type(def_gen.standard_exponential(dtype="float32")) # E: float +reveal_type(def_gen.standard_exponential(dtype="double")) # E: float +reveal_type(def_gen.standard_exponential(dtype=np.float64)) # E: float +reveal_type(def_gen.standard_exponential(size=None)) # E: float +reveal_type(def_gen.standard_exponential(size=None, method="inv")) # E: float +reveal_type(def_gen.standard_exponential(size=1, method="inv")) # E: ndarray[Any, dtype[floating[typing._64Bit]]] +reveal_type(def_gen.standard_exponential(size=1, dtype=np.float32)) # E: ndarray[Any, dtype[floating[typing._32Bit]]] +reveal_type(def_gen.standard_exponential(size=1, dtype="f4", method="inv")) # E: ndarray[Any, dtype[floating[typing._32Bit]]] +reveal_type(def_gen.standard_exponential(size=1, dtype="float32", out=S_out)) # E: ndarray[Any, dtype[floating[typing._32Bit]]] +reveal_type(def_gen.standard_exponential(dtype=np.float32, out=S_out)) # E: ndarray[Any, dtype[floating[typing._32Bit]]] +reveal_type(def_gen.standard_exponential(size=1, dtype=np.float64, method="inv")) # E: ndarray[Any, dtype[floating[typing._64Bit]]] +reveal_type(def_gen.standard_exponential(size=1, dtype="float64")) # E: ndarray[Any, dtype[floating[typing._64Bit]]] +reveal_type(def_gen.standard_exponential(size=1, dtype="f8")) # E: ndarray[Any, dtype[floating[typing._64Bit]]] +reveal_type(def_gen.standard_exponential(out=D_out)) # E: ndarray[Any, dtype[floating[typing._64Bit]]] +reveal_type(def_gen.standard_exponential(size=1, dtype="float64")) # E: ndarray[Any, dtype[floating[typing._64Bit]]] +reveal_type(def_gen.standard_exponential(size=1, dtype="float64", out=D_out)) # E: ndarray[Any, dtype[floating[typing._64Bit]]] + +reveal_type(def_gen.zipf(1.5)) # E: int +reveal_type(def_gen.zipf(1.5, size=None)) # E: int +reveal_type(def_gen.zipf(1.5, size=1)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.zipf(D_arr_1p5)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.zipf(D_arr_1p5, size=1)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.zipf(D_arr_like_1p5)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.zipf(D_arr_like_1p5, size=1)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] + +reveal_type(def_gen.weibull(0.5)) # E: float +reveal_type(def_gen.weibull(0.5, size=None)) # E: float +reveal_type(def_gen.weibull(0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.weibull(D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.weibull(D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.weibull(D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.weibull(D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(def_gen.standard_t(0.5)) # E: float +reveal_type(def_gen.standard_t(0.5, size=None)) # E: float +reveal_type(def_gen.standard_t(0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.standard_t(D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.standard_t(D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.standard_t(D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.standard_t(D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(def_gen.poisson(0.5)) # E: int +reveal_type(def_gen.poisson(0.5, size=None)) # E: int +reveal_type(def_gen.poisson(0.5, size=1)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.poisson(D_arr_0p5)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.poisson(D_arr_0p5, size=1)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.poisson(D_arr_like_0p5)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.poisson(D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] + +reveal_type(def_gen.power(0.5)) # E: float +reveal_type(def_gen.power(0.5, size=None)) # E: float +reveal_type(def_gen.power(0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.power(D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.power(D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.power(D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.power(D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(def_gen.pareto(0.5)) # E: float +reveal_type(def_gen.pareto(0.5, size=None)) # E: float +reveal_type(def_gen.pareto(0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.pareto(D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.pareto(D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.pareto(D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.pareto(D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(def_gen.chisquare(0.5)) # E: float +reveal_type(def_gen.chisquare(0.5, size=None)) # E: float +reveal_type(def_gen.chisquare(0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.chisquare(D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.chisquare(D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.chisquare(D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.chisquare(D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(def_gen.exponential(0.5)) # E: float +reveal_type(def_gen.exponential(0.5, size=None)) # E: float +reveal_type(def_gen.exponential(0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.exponential(D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.exponential(D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.exponential(D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.exponential(D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(def_gen.geometric(0.5)) # E: int +reveal_type(def_gen.geometric(0.5, size=None)) # E: int +reveal_type(def_gen.geometric(0.5, size=1)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.geometric(D_arr_0p5)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.geometric(D_arr_0p5, size=1)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.geometric(D_arr_like_0p5)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.geometric(D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] + +reveal_type(def_gen.logseries(0.5)) # E: int +reveal_type(def_gen.logseries(0.5, size=None)) # E: int +reveal_type(def_gen.logseries(0.5, size=1)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.logseries(D_arr_0p5)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.logseries(D_arr_0p5, size=1)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.logseries(D_arr_like_0p5)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.logseries(D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] + +reveal_type(def_gen.rayleigh(0.5)) # E: float +reveal_type(def_gen.rayleigh(0.5, size=None)) # E: float +reveal_type(def_gen.rayleigh(0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.rayleigh(D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.rayleigh(D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.rayleigh(D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.rayleigh(D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(def_gen.standard_gamma(0.5)) # E: float +reveal_type(def_gen.standard_gamma(0.5, size=None)) # E: float +reveal_type(def_gen.standard_gamma(0.5, dtype="float32")) # E: float +reveal_type(def_gen.standard_gamma(0.5, size=None, dtype="float32")) # E: float +reveal_type(def_gen.standard_gamma(0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]]] +reveal_type(def_gen.standard_gamma(D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]]] +reveal_type(def_gen.standard_gamma(D_arr_0p5, dtype="f4")) # E: ndarray[Any, dtype[floating[typing._32Bit]]] +reveal_type(def_gen.standard_gamma(0.5, size=1, dtype="float32", out=S_out)) # E: ndarray[Any, dtype[floating[typing._32Bit]]] +reveal_type(def_gen.standard_gamma(D_arr_0p5, dtype=np.float32, out=S_out)) # E: ndarray[Any, dtype[floating[typing._32Bit]]] +reveal_type(def_gen.standard_gamma(D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]]] +reveal_type(def_gen.standard_gamma(D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]]] +reveal_type(def_gen.standard_gamma(D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]]] +reveal_type(def_gen.standard_gamma(0.5, out=D_out)) # E: ndarray[Any, dtype[floating[typing._64Bit]]] +reveal_type(def_gen.standard_gamma(D_arr_like_0p5, out=D_out)) # E: ndarray[Any, dtype[floating[typing._64Bit]]] +reveal_type(def_gen.standard_gamma(D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]]] +reveal_type(def_gen.standard_gamma(D_arr_like_0p5, size=1, out=D_out, dtype=np.float64)) # E: ndarray[Any, dtype[floating[typing._64Bit]]] + +reveal_type(def_gen.vonmises(0.5, 0.5)) # E: float +reveal_type(def_gen.vonmises(0.5, 0.5, size=None)) # E: float +reveal_type(def_gen.vonmises(0.5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.vonmises(D_arr_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.vonmises(0.5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.vonmises(D_arr_0p5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.vonmises(0.5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.vonmises(D_arr_like_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.vonmises(0.5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.vonmises(D_arr_0p5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.vonmises(D_arr_like_0p5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.vonmises(D_arr_0p5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.vonmises(D_arr_like_0p5, D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(def_gen.wald(0.5, 0.5)) # E: float +reveal_type(def_gen.wald(0.5, 0.5, size=None)) # E: float +reveal_type(def_gen.wald(0.5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.wald(D_arr_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.wald(0.5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.wald(D_arr_0p5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.wald(0.5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.wald(D_arr_like_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.wald(0.5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.wald(D_arr_0p5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.wald(D_arr_like_0p5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.wald(D_arr_0p5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.wald(D_arr_like_0p5, D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(def_gen.uniform(0.5, 0.5)) # E: float +reveal_type(def_gen.uniform(0.5, 0.5, size=None)) # E: float +reveal_type(def_gen.uniform(0.5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.uniform(D_arr_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.uniform(0.5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.uniform(D_arr_0p5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.uniform(0.5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.uniform(D_arr_like_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.uniform(0.5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.uniform(D_arr_0p5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.uniform(D_arr_like_0p5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.uniform(D_arr_0p5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.uniform(D_arr_like_0p5, D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(def_gen.beta(0.5, 0.5)) # E: float +reveal_type(def_gen.beta(0.5, 0.5, size=None)) # E: float +reveal_type(def_gen.beta(0.5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.beta(D_arr_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.beta(0.5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.beta(D_arr_0p5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.beta(0.5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.beta(D_arr_like_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.beta(0.5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.beta(D_arr_0p5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.beta(D_arr_like_0p5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.beta(D_arr_0p5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.beta(D_arr_like_0p5, D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(def_gen.f(0.5, 0.5)) # E: float +reveal_type(def_gen.f(0.5, 0.5, size=None)) # E: float +reveal_type(def_gen.f(0.5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.f(D_arr_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.f(0.5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.f(D_arr_0p5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.f(0.5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.f(D_arr_like_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.f(0.5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.f(D_arr_0p5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.f(D_arr_like_0p5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.f(D_arr_0p5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.f(D_arr_like_0p5, D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(def_gen.gamma(0.5, 0.5)) # E: float +reveal_type(def_gen.gamma(0.5, 0.5, size=None)) # E: float +reveal_type(def_gen.gamma(0.5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.gamma(D_arr_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.gamma(0.5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.gamma(D_arr_0p5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.gamma(0.5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.gamma(D_arr_like_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.gamma(0.5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.gamma(D_arr_0p5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.gamma(D_arr_like_0p5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.gamma(D_arr_0p5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.gamma(D_arr_like_0p5, D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(def_gen.gumbel(0.5, 0.5)) # E: float +reveal_type(def_gen.gumbel(0.5, 0.5, size=None)) # E: float +reveal_type(def_gen.gumbel(0.5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.gumbel(D_arr_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.gumbel(0.5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.gumbel(D_arr_0p5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.gumbel(0.5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.gumbel(D_arr_like_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.gumbel(0.5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.gumbel(D_arr_0p5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.gumbel(D_arr_like_0p5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.gumbel(D_arr_0p5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.gumbel(D_arr_like_0p5, D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(def_gen.laplace(0.5, 0.5)) # E: float +reveal_type(def_gen.laplace(0.5, 0.5, size=None)) # E: float +reveal_type(def_gen.laplace(0.5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.laplace(D_arr_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.laplace(0.5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.laplace(D_arr_0p5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.laplace(0.5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.laplace(D_arr_like_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.laplace(0.5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.laplace(D_arr_0p5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.laplace(D_arr_like_0p5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.laplace(D_arr_0p5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.laplace(D_arr_like_0p5, D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(def_gen.logistic(0.5, 0.5)) # E: float +reveal_type(def_gen.logistic(0.5, 0.5, size=None)) # E: float +reveal_type(def_gen.logistic(0.5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.logistic(D_arr_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.logistic(0.5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.logistic(D_arr_0p5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.logistic(0.5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.logistic(D_arr_like_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.logistic(0.5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.logistic(D_arr_0p5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.logistic(D_arr_like_0p5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.logistic(D_arr_0p5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.logistic(D_arr_like_0p5, D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(def_gen.lognormal(0.5, 0.5)) # E: float +reveal_type(def_gen.lognormal(0.5, 0.5, size=None)) # E: float +reveal_type(def_gen.lognormal(0.5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.lognormal(D_arr_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.lognormal(0.5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.lognormal(D_arr_0p5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.lognormal(0.5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.lognormal(D_arr_like_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.lognormal(0.5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.lognormal(D_arr_0p5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.lognormal(D_arr_like_0p5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.lognormal(D_arr_0p5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.lognormal(D_arr_like_0p5, D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(def_gen.noncentral_chisquare(0.5, 0.5)) # E: float +reveal_type(def_gen.noncentral_chisquare(0.5, 0.5, size=None)) # E: float +reveal_type(def_gen.noncentral_chisquare(0.5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.noncentral_chisquare(D_arr_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.noncentral_chisquare(0.5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.noncentral_chisquare(D_arr_0p5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.noncentral_chisquare(0.5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.noncentral_chisquare(D_arr_like_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.noncentral_chisquare(0.5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.noncentral_chisquare(D_arr_0p5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.noncentral_chisquare(D_arr_like_0p5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.noncentral_chisquare(D_arr_0p5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.noncentral_chisquare(D_arr_like_0p5, D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(def_gen.normal(0.5, 0.5)) # E: float +reveal_type(def_gen.normal(0.5, 0.5, size=None)) # E: float +reveal_type(def_gen.normal(0.5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.normal(D_arr_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.normal(0.5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.normal(D_arr_0p5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.normal(0.5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.normal(D_arr_like_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.normal(0.5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.normal(D_arr_0p5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.normal(D_arr_like_0p5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.normal(D_arr_0p5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.normal(D_arr_like_0p5, D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(def_gen.triangular(0.1, 0.5, 0.9)) # E: float +reveal_type(def_gen.triangular(0.1, 0.5, 0.9, size=None)) # E: float +reveal_type(def_gen.triangular(0.1, 0.5, 0.9, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.triangular(D_arr_0p1, 0.5, 0.9)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.triangular(0.1, D_arr_0p5, 0.9)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.triangular(D_arr_0p1, 0.5, D_arr_like_0p9, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.triangular(0.1, D_arr_0p5, 0.9, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.triangular(D_arr_like_0p1, 0.5, D_arr_0p9)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.triangular(0.5, D_arr_like_0p5, 0.9)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.triangular(D_arr_0p1, D_arr_0p5, 0.9)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.triangular(D_arr_like_0p1, D_arr_like_0p5, 0.9)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.triangular(D_arr_0p1, D_arr_0p5, D_arr_0p9, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.triangular(D_arr_like_0p1, D_arr_like_0p5, D_arr_like_0p9, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(def_gen.noncentral_f(0.1, 0.5, 0.9)) # E: float +reveal_type(def_gen.noncentral_f(0.1, 0.5, 0.9, size=None)) # E: float +reveal_type(def_gen.noncentral_f(0.1, 0.5, 0.9, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.noncentral_f(D_arr_0p1, 0.5, 0.9)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.noncentral_f(0.1, D_arr_0p5, 0.9)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.noncentral_f(D_arr_0p1, 0.5, D_arr_like_0p9, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.noncentral_f(0.1, D_arr_0p5, 0.9, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.noncentral_f(D_arr_like_0p1, 0.5, D_arr_0p9)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.noncentral_f(0.5, D_arr_like_0p5, 0.9)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.noncentral_f(D_arr_0p1, D_arr_0p5, 0.9)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.noncentral_f(D_arr_like_0p1, D_arr_like_0p5, 0.9)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.noncentral_f(D_arr_0p1, D_arr_0p5, D_arr_0p9, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.noncentral_f(D_arr_like_0p1, D_arr_like_0p5, D_arr_like_0p9, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(def_gen.binomial(10, 0.5)) # E: int +reveal_type(def_gen.binomial(10, 0.5, size=None)) # E: int +reveal_type(def_gen.binomial(10, 0.5, size=1)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.binomial(I_arr_10, 0.5)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.binomial(10, D_arr_0p5)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.binomial(I_arr_10, 0.5, size=1)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.binomial(10, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.binomial(I_arr_like_10, 0.5)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.binomial(10, D_arr_like_0p5)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.binomial(I_arr_10, D_arr_0p5)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.binomial(I_arr_like_10, D_arr_like_0p5)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.binomial(I_arr_10, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.binomial(I_arr_like_10, D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] + +reveal_type(def_gen.negative_binomial(10, 0.5)) # E: int +reveal_type(def_gen.negative_binomial(10, 0.5, size=None)) # E: int +reveal_type(def_gen.negative_binomial(10, 0.5, size=1)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.negative_binomial(I_arr_10, 0.5)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.negative_binomial(10, D_arr_0p5)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.negative_binomial(I_arr_10, 0.5, size=1)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.negative_binomial(10, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.negative_binomial(I_arr_like_10, 0.5)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.negative_binomial(10, D_arr_like_0p5)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.negative_binomial(I_arr_10, D_arr_0p5)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.negative_binomial(I_arr_like_10, D_arr_like_0p5)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.negative_binomial(I_arr_10, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.negative_binomial(I_arr_like_10, D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] + +reveal_type(def_gen.hypergeometric(20, 20, 10)) # E: int +reveal_type(def_gen.hypergeometric(20, 20, 10, size=None)) # E: int +reveal_type(def_gen.hypergeometric(20, 20, 10, size=1)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.hypergeometric(I_arr_20, 20, 10)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.hypergeometric(20, I_arr_20, 10)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.hypergeometric(I_arr_20, 20, I_arr_like_10, size=1)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.hypergeometric(20, I_arr_20, 10, size=1)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.hypergeometric(I_arr_like_20, 20, I_arr_10)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.hypergeometric(20, I_arr_like_20, 10)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.hypergeometric(I_arr_20, I_arr_20, 10)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.hypergeometric(I_arr_like_20, I_arr_like_20, 10)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.hypergeometric(I_arr_20, I_arr_20, I_arr_10, size=1)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.hypergeometric(I_arr_like_20, I_arr_like_20, I_arr_like_10, size=1)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] + +I_int64_100: np.ndarray[Any, np.dtype[np.int64]] = np.array([100], dtype=np.int64) + +reveal_type(def_gen.integers(0, 100)) # E: int +reveal_type(def_gen.integers(100)) # E: int +reveal_type(def_gen.integers([100])) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.integers(0, [100])) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] + +I_bool_low: np.ndarray[Any, np.dtype[np.bool_]] = np.array([0], dtype=np.bool_) +I_bool_low_like: List[int] = [0] +I_bool_high_open: np.ndarray[Any, np.dtype[np.bool_]] = np.array([1], dtype=np.bool_) +I_bool_high_closed: np.ndarray[Any, np.dtype[np.bool_]] = np.array([1], dtype=np.bool_) + +reveal_type(def_gen.integers(2, dtype=bool)) # E: builtins.bool +reveal_type(def_gen.integers(0, 2, dtype=bool)) # E: builtins.bool +reveal_type(def_gen.integers(1, dtype=bool, endpoint=True)) # E: builtins.bool +reveal_type(def_gen.integers(0, 1, dtype=bool, endpoint=True)) # E: builtins.bool +reveal_type(def_gen.integers(I_bool_low_like, 1, dtype=bool, endpoint=True)) # E: ndarray[Any, dtype[bool_] +reveal_type(def_gen.integers(I_bool_high_open, dtype=bool)) # E: ndarray[Any, dtype[bool_] +reveal_type(def_gen.integers(I_bool_low, I_bool_high_open, dtype=bool)) # E: ndarray[Any, dtype[bool_] +reveal_type(def_gen.integers(0, I_bool_high_open, dtype=bool)) # E: ndarray[Any, dtype[bool_] +reveal_type(def_gen.integers(I_bool_high_closed, dtype=bool, endpoint=True)) # E: ndarray[Any, dtype[bool_] +reveal_type(def_gen.integers(I_bool_low, I_bool_high_closed, dtype=bool, endpoint=True)) # E: ndarray[Any, dtype[bool_] +reveal_type(def_gen.integers(0, I_bool_high_closed, dtype=bool, endpoint=True)) # E: ndarray[Any, dtype[bool_] + +reveal_type(def_gen.integers(2, dtype=np.bool_)) # E: builtins.bool +reveal_type(def_gen.integers(0, 2, dtype=np.bool_)) # E: builtins.bool +reveal_type(def_gen.integers(1, dtype=np.bool_, endpoint=True)) # E: builtins.bool +reveal_type(def_gen.integers(0, 1, dtype=np.bool_, endpoint=True)) # E: builtins.bool +reveal_type(def_gen.integers(I_bool_low_like, 1, dtype=np.bool_, endpoint=True)) # E: ndarray[Any, dtype[bool_] +reveal_type(def_gen.integers(I_bool_high_open, dtype=np.bool_)) # E: ndarray[Any, dtype[bool_] +reveal_type(def_gen.integers(I_bool_low, I_bool_high_open, dtype=np.bool_)) # E: ndarray[Any, dtype[bool_] +reveal_type(def_gen.integers(0, I_bool_high_open, dtype=np.bool_)) # E: ndarray[Any, dtype[bool_] +reveal_type(def_gen.integers(I_bool_high_closed, dtype=np.bool_, endpoint=True)) # E: ndarray[Any, dtype[bool_] +reveal_type(def_gen.integers(I_bool_low, I_bool_high_closed, dtype=np.bool_, endpoint=True)) # E: ndarray[Any, dtype[bool_] +reveal_type(def_gen.integers(0, I_bool_high_closed, dtype=np.bool_, endpoint=True)) # E: ndarray[Any, dtype[bool_] + +I_u1_low: np.ndarray[Any, np.dtype[np.uint8]] = np.array([0], dtype=np.uint8) +I_u1_low_like: List[int] = [0] +I_u1_high_open: np.ndarray[Any, np.dtype[np.uint8]] = np.array([255], dtype=np.uint8) +I_u1_high_closed: np.ndarray[Any, np.dtype[np.uint8]] = np.array([255], dtype=np.uint8) + +reveal_type(def_gen.integers(256, dtype="u1")) # E: int +reveal_type(def_gen.integers(0, 256, dtype="u1")) # E: int +reveal_type(def_gen.integers(255, dtype="u1", endpoint=True)) # E: int +reveal_type(def_gen.integers(0, 255, dtype="u1", endpoint=True)) # E: int +reveal_type(def_gen.integers(I_u1_low_like, 255, dtype="u1", endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._8Bit]]] +reveal_type(def_gen.integers(I_u1_high_open, dtype="u1")) # E: ndarray[Any, dtype[unsignedinteger[typing._8Bit]]] +reveal_type(def_gen.integers(I_u1_low, I_u1_high_open, dtype="u1")) # E: ndarray[Any, dtype[unsignedinteger[typing._8Bit]]] +reveal_type(def_gen.integers(0, I_u1_high_open, dtype="u1")) # E: ndarray[Any, dtype[unsignedinteger[typing._8Bit]]] +reveal_type(def_gen.integers(I_u1_high_closed, dtype="u1", endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._8Bit]]] +reveal_type(def_gen.integers(I_u1_low, I_u1_high_closed, dtype="u1", endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._8Bit]]] +reveal_type(def_gen.integers(0, I_u1_high_closed, dtype="u1", endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._8Bit]]] + +reveal_type(def_gen.integers(256, dtype="uint8")) # E: int +reveal_type(def_gen.integers(0, 256, dtype="uint8")) # E: int +reveal_type(def_gen.integers(255, dtype="uint8", endpoint=True)) # E: int +reveal_type(def_gen.integers(0, 255, dtype="uint8", endpoint=True)) # E: int +reveal_type(def_gen.integers(I_u1_low_like, 255, dtype="uint8", endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._8Bit]]] +reveal_type(def_gen.integers(I_u1_high_open, dtype="uint8")) # E: ndarray[Any, dtype[unsignedinteger[typing._8Bit]]] +reveal_type(def_gen.integers(I_u1_low, I_u1_high_open, dtype="uint8")) # E: ndarray[Any, dtype[unsignedinteger[typing._8Bit]]] +reveal_type(def_gen.integers(0, I_u1_high_open, dtype="uint8")) # E: ndarray[Any, dtype[unsignedinteger[typing._8Bit]]] +reveal_type(def_gen.integers(I_u1_high_closed, dtype="uint8", endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._8Bit]]] +reveal_type(def_gen.integers(I_u1_low, I_u1_high_closed, dtype="uint8", endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._8Bit]]] +reveal_type(def_gen.integers(0, I_u1_high_closed, dtype="uint8", endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._8Bit]]] + +reveal_type(def_gen.integers(256, dtype=np.uint8)) # E: int +reveal_type(def_gen.integers(0, 256, dtype=np.uint8)) # E: int +reveal_type(def_gen.integers(255, dtype=np.uint8, endpoint=True)) # E: int +reveal_type(def_gen.integers(0, 255, dtype=np.uint8, endpoint=True)) # E: int +reveal_type(def_gen.integers(I_u1_low_like, 255, dtype=np.uint8, endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._8Bit]]] +reveal_type(def_gen.integers(I_u1_high_open, dtype=np.uint8)) # E: ndarray[Any, dtype[unsignedinteger[typing._8Bit]]] +reveal_type(def_gen.integers(I_u1_low, I_u1_high_open, dtype=np.uint8)) # E: ndarray[Any, dtype[unsignedinteger[typing._8Bit]]] +reveal_type(def_gen.integers(0, I_u1_high_open, dtype=np.uint8)) # E: ndarray[Any, dtype[unsignedinteger[typing._8Bit]]] +reveal_type(def_gen.integers(I_u1_high_closed, dtype=np.uint8, endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._8Bit]]] +reveal_type(def_gen.integers(I_u1_low, I_u1_high_closed, dtype=np.uint8, endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._8Bit]]] +reveal_type(def_gen.integers(0, I_u1_high_closed, dtype=np.uint8, endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._8Bit]]] + +I_u2_low: np.ndarray[Any, np.dtype[np.uint16]] = np.array([0], dtype=np.uint16) +I_u2_low_like: List[int] = [0] +I_u2_high_open: np.ndarray[Any, np.dtype[np.uint16]] = np.array([65535], dtype=np.uint16) +I_u2_high_closed: np.ndarray[Any, np.dtype[np.uint16]] = np.array([65535], dtype=np.uint16) + +reveal_type(def_gen.integers(65536, dtype="u2")) # E: int +reveal_type(def_gen.integers(0, 65536, dtype="u2")) # E: int +reveal_type(def_gen.integers(65535, dtype="u2", endpoint=True)) # E: int +reveal_type(def_gen.integers(0, 65535, dtype="u2", endpoint=True)) # E: int +reveal_type(def_gen.integers(I_u2_low_like, 65535, dtype="u2", endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._16Bit]]] +reveal_type(def_gen.integers(I_u2_high_open, dtype="u2")) # E: ndarray[Any, dtype[unsignedinteger[typing._16Bit]]] +reveal_type(def_gen.integers(I_u2_low, I_u2_high_open, dtype="u2")) # E: ndarray[Any, dtype[unsignedinteger[typing._16Bit]]] +reveal_type(def_gen.integers(0, I_u2_high_open, dtype="u2")) # E: ndarray[Any, dtype[unsignedinteger[typing._16Bit]]] +reveal_type(def_gen.integers(I_u2_high_closed, dtype="u2", endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._16Bit]]] +reveal_type(def_gen.integers(I_u2_low, I_u2_high_closed, dtype="u2", endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._16Bit]]] +reveal_type(def_gen.integers(0, I_u2_high_closed, dtype="u2", endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._16Bit]]] + +reveal_type(def_gen.integers(65536, dtype="uint16")) # E: int +reveal_type(def_gen.integers(0, 65536, dtype="uint16")) # E: int +reveal_type(def_gen.integers(65535, dtype="uint16", endpoint=True)) # E: int +reveal_type(def_gen.integers(0, 65535, dtype="uint16", endpoint=True)) # E: int +reveal_type(def_gen.integers(I_u2_low_like, 65535, dtype="uint16", endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._16Bit]]] +reveal_type(def_gen.integers(I_u2_high_open, dtype="uint16")) # E: ndarray[Any, dtype[unsignedinteger[typing._16Bit]]] +reveal_type(def_gen.integers(I_u2_low, I_u2_high_open, dtype="uint16")) # E: ndarray[Any, dtype[unsignedinteger[typing._16Bit]]] +reveal_type(def_gen.integers(0, I_u2_high_open, dtype="uint16")) # E: ndarray[Any, dtype[unsignedinteger[typing._16Bit]]] +reveal_type(def_gen.integers(I_u2_high_closed, dtype="uint16", endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._16Bit]]] +reveal_type(def_gen.integers(I_u2_low, I_u2_high_closed, dtype="uint16", endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._16Bit]]] +reveal_type(def_gen.integers(0, I_u2_high_closed, dtype="uint16", endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._16Bit]]] + +reveal_type(def_gen.integers(65536, dtype=np.uint16)) # E: int +reveal_type(def_gen.integers(0, 65536, dtype=np.uint16)) # E: int +reveal_type(def_gen.integers(65535, dtype=np.uint16, endpoint=True)) # E: int +reveal_type(def_gen.integers(0, 65535, dtype=np.uint16, endpoint=True)) # E: int +reveal_type(def_gen.integers(I_u2_low_like, 65535, dtype=np.uint16, endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._16Bit]]] +reveal_type(def_gen.integers(I_u2_high_open, dtype=np.uint16)) # E: ndarray[Any, dtype[unsignedinteger[typing._16Bit]]] +reveal_type(def_gen.integers(I_u2_low, I_u2_high_open, dtype=np.uint16)) # E: ndarray[Any, dtype[unsignedinteger[typing._16Bit]]] +reveal_type(def_gen.integers(0, I_u2_high_open, dtype=np.uint16)) # E: ndarray[Any, dtype[unsignedinteger[typing._16Bit]]] +reveal_type(def_gen.integers(I_u2_high_closed, dtype=np.uint16, endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._16Bit]]] +reveal_type(def_gen.integers(I_u2_low, I_u2_high_closed, dtype=np.uint16, endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._16Bit]]] +reveal_type(def_gen.integers(0, I_u2_high_closed, dtype=np.uint16, endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._16Bit]]] + +I_u4_low: np.ndarray[Any, np.dtype[np.uint32]] = np.array([0], dtype=np.uint32) +I_u4_low_like: List[int] = [0] +I_u4_high_open: np.ndarray[Any, np.dtype[np.uint32]] = np.array([4294967295], dtype=np.uint32) +I_u4_high_closed: np.ndarray[Any, np.dtype[np.uint32]] = np.array([4294967295], dtype=np.uint32) + +reveal_type(def_gen.integers(4294967296, dtype=np.int_)) # E: int +reveal_type(def_gen.integers(0, 4294967296, dtype=np.int_)) # E: int +reveal_type(def_gen.integers(4294967295, dtype=np.int_, endpoint=True)) # E: int +reveal_type(def_gen.integers(0, 4294967295, dtype=np.int_, endpoint=True)) # E: int +reveal_type(def_gen.integers(I_u4_low_like, 4294967295, dtype=np.int_, endpoint=True)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(def_gen.integers(I_u4_high_open, dtype=np.int_)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(def_gen.integers(I_u4_low, I_u4_high_open, dtype=np.int_)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(def_gen.integers(0, I_u4_high_open, dtype=np.int_)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(def_gen.integers(I_u4_high_closed, dtype=np.int_, endpoint=True)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(def_gen.integers(I_u4_low, I_u4_high_closed, dtype=np.int_, endpoint=True)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(def_gen.integers(0, I_u4_high_closed, dtype=np.int_, endpoint=True)) # E: ndarray[Any, dtype[{int_}]] + + +reveal_type(def_gen.integers(4294967296, dtype="u4")) # E: int +reveal_type(def_gen.integers(0, 4294967296, dtype="u4")) # E: int +reveal_type(def_gen.integers(4294967295, dtype="u4", endpoint=True)) # E: int +reveal_type(def_gen.integers(0, 4294967295, dtype="u4", endpoint=True)) # E: int +reveal_type(def_gen.integers(I_u4_low_like, 4294967295, dtype="u4", endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._32Bit]]] +reveal_type(def_gen.integers(I_u4_high_open, dtype="u4")) # E: ndarray[Any, dtype[unsignedinteger[typing._32Bit]]] +reveal_type(def_gen.integers(I_u4_low, I_u4_high_open, dtype="u4")) # E: ndarray[Any, dtype[unsignedinteger[typing._32Bit]]] +reveal_type(def_gen.integers(0, I_u4_high_open, dtype="u4")) # E: ndarray[Any, dtype[unsignedinteger[typing._32Bit]]] +reveal_type(def_gen.integers(I_u4_high_closed, dtype="u4", endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._32Bit]]] +reveal_type(def_gen.integers(I_u4_low, I_u4_high_closed, dtype="u4", endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._32Bit]]] +reveal_type(def_gen.integers(0, I_u4_high_closed, dtype="u4", endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._32Bit]]] + +reveal_type(def_gen.integers(4294967296, dtype="uint32")) # E: int +reveal_type(def_gen.integers(0, 4294967296, dtype="uint32")) # E: int +reveal_type(def_gen.integers(4294967295, dtype="uint32", endpoint=True)) # E: int +reveal_type(def_gen.integers(0, 4294967295, dtype="uint32", endpoint=True)) # E: int +reveal_type(def_gen.integers(I_u4_low_like, 4294967295, dtype="uint32", endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._32Bit]]] +reveal_type(def_gen.integers(I_u4_high_open, dtype="uint32")) # E: ndarray[Any, dtype[unsignedinteger[typing._32Bit]]] +reveal_type(def_gen.integers(I_u4_low, I_u4_high_open, dtype="uint32")) # E: ndarray[Any, dtype[unsignedinteger[typing._32Bit]]] +reveal_type(def_gen.integers(0, I_u4_high_open, dtype="uint32")) # E: ndarray[Any, dtype[unsignedinteger[typing._32Bit]]] +reveal_type(def_gen.integers(I_u4_high_closed, dtype="uint32", endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._32Bit]]] +reveal_type(def_gen.integers(I_u4_low, I_u4_high_closed, dtype="uint32", endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._32Bit]]] +reveal_type(def_gen.integers(0, I_u4_high_closed, dtype="uint32", endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._32Bit]]] + +reveal_type(def_gen.integers(4294967296, dtype=np.uint32)) # E: int +reveal_type(def_gen.integers(0, 4294967296, dtype=np.uint32)) # E: int +reveal_type(def_gen.integers(4294967295, dtype=np.uint32, endpoint=True)) # E: int +reveal_type(def_gen.integers(0, 4294967295, dtype=np.uint32, endpoint=True)) # E: int +reveal_type(def_gen.integers(I_u4_low_like, 4294967295, dtype=np.uint32, endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._32Bit]]] +reveal_type(def_gen.integers(I_u4_high_open, dtype=np.uint32)) # E: ndarray[Any, dtype[unsignedinteger[typing._32Bit]]] +reveal_type(def_gen.integers(I_u4_low, I_u4_high_open, dtype=np.uint32)) # E: ndarray[Any, dtype[unsignedinteger[typing._32Bit]]] +reveal_type(def_gen.integers(0, I_u4_high_open, dtype=np.uint32)) # E: ndarray[Any, dtype[unsignedinteger[typing._32Bit]]] +reveal_type(def_gen.integers(I_u4_high_closed, dtype=np.uint32, endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._32Bit]]] +reveal_type(def_gen.integers(I_u4_low, I_u4_high_closed, dtype=np.uint32, endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._32Bit]]] +reveal_type(def_gen.integers(0, I_u4_high_closed, dtype=np.uint32, endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._32Bit]]] + +reveal_type(def_gen.integers(4294967296, dtype=np.uint)) # E: int +reveal_type(def_gen.integers(0, 4294967296, dtype=np.uint)) # E: int +reveal_type(def_gen.integers(4294967295, dtype=np.uint, endpoint=True)) # E: int +reveal_type(def_gen.integers(0, 4294967295, dtype=np.uint, endpoint=True)) # E: int +reveal_type(def_gen.integers(I_u4_low_like, 4294967295, dtype=np.uint, endpoint=True)) # E: ndarray[Any, dtype[{uint}]] +reveal_type(def_gen.integers(I_u4_high_open, dtype=np.uint)) # E: ndarray[Any, dtype[{uint}]] +reveal_type(def_gen.integers(I_u4_low, I_u4_high_open, dtype=np.uint)) # E: ndarray[Any, dtype[{uint}]] +reveal_type(def_gen.integers(0, I_u4_high_open, dtype=np.uint)) # E: ndarray[Any, dtype[{uint}]] +reveal_type(def_gen.integers(I_u4_high_closed, dtype=np.uint, endpoint=True)) # E: ndarray[Any, dtype[{uint}]] +reveal_type(def_gen.integers(I_u4_low, I_u4_high_closed, dtype=np.uint, endpoint=True)) # E: ndarray[Any, dtype[{uint}]] +reveal_type(def_gen.integers(0, I_u4_high_closed, dtype=np.uint, endpoint=True)) # E: ndarray[Any, dtype[{uint}]] + +I_u8_low: np.ndarray[Any, np.dtype[np.uint64]] = np.array([0], dtype=np.uint64) +I_u8_low_like: List[int] = [0] +I_u8_high_open: np.ndarray[Any, np.dtype[np.uint64]] = np.array([18446744073709551615], dtype=np.uint64) +I_u8_high_closed: np.ndarray[Any, np.dtype[np.uint64]] = np.array([18446744073709551615], dtype=np.uint64) + +reveal_type(def_gen.integers(18446744073709551616, dtype="u8")) # E: int +reveal_type(def_gen.integers(0, 18446744073709551616, dtype="u8")) # E: int +reveal_type(def_gen.integers(18446744073709551615, dtype="u8", endpoint=True)) # E: int +reveal_type(def_gen.integers(0, 18446744073709551615, dtype="u8", endpoint=True)) # E: int +reveal_type(def_gen.integers(I_u8_low_like, 18446744073709551615, dtype="u8", endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._64Bit]]] +reveal_type(def_gen.integers(I_u8_high_open, dtype="u8")) # E: ndarray[Any, dtype[unsignedinteger[typing._64Bit]]] +reveal_type(def_gen.integers(I_u8_low, I_u8_high_open, dtype="u8")) # E: ndarray[Any, dtype[unsignedinteger[typing._64Bit]]] +reveal_type(def_gen.integers(0, I_u8_high_open, dtype="u8")) # E: ndarray[Any, dtype[unsignedinteger[typing._64Bit]]] +reveal_type(def_gen.integers(I_u8_high_closed, dtype="u8", endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._64Bit]]] +reveal_type(def_gen.integers(I_u8_low, I_u8_high_closed, dtype="u8", endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._64Bit]]] +reveal_type(def_gen.integers(0, I_u8_high_closed, dtype="u8", endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._64Bit]]] + +reveal_type(def_gen.integers(18446744073709551616, dtype="uint64")) # E: int +reveal_type(def_gen.integers(0, 18446744073709551616, dtype="uint64")) # E: int +reveal_type(def_gen.integers(18446744073709551615, dtype="uint64", endpoint=True)) # E: int +reveal_type(def_gen.integers(0, 18446744073709551615, dtype="uint64", endpoint=True)) # E: int +reveal_type(def_gen.integers(I_u8_low_like, 18446744073709551615, dtype="uint64", endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._64Bit]]] +reveal_type(def_gen.integers(I_u8_high_open, dtype="uint64")) # E: ndarray[Any, dtype[unsignedinteger[typing._64Bit]]] +reveal_type(def_gen.integers(I_u8_low, I_u8_high_open, dtype="uint64")) # E: ndarray[Any, dtype[unsignedinteger[typing._64Bit]]] +reveal_type(def_gen.integers(0, I_u8_high_open, dtype="uint64")) # E: ndarray[Any, dtype[unsignedinteger[typing._64Bit]]] +reveal_type(def_gen.integers(I_u8_high_closed, dtype="uint64", endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._64Bit]]] +reveal_type(def_gen.integers(I_u8_low, I_u8_high_closed, dtype="uint64", endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._64Bit]]] +reveal_type(def_gen.integers(0, I_u8_high_closed, dtype="uint64", endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._64Bit]]] + +reveal_type(def_gen.integers(18446744073709551616, dtype=np.uint64)) # E: int +reveal_type(def_gen.integers(0, 18446744073709551616, dtype=np.uint64)) # E: int +reveal_type(def_gen.integers(18446744073709551615, dtype=np.uint64, endpoint=True)) # E: int +reveal_type(def_gen.integers(0, 18446744073709551615, dtype=np.uint64, endpoint=True)) # E: int +reveal_type(def_gen.integers(I_u8_low_like, 18446744073709551615, dtype=np.uint64, endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._64Bit]]] +reveal_type(def_gen.integers(I_u8_high_open, dtype=np.uint64)) # E: ndarray[Any, dtype[unsignedinteger[typing._64Bit]]] +reveal_type(def_gen.integers(I_u8_low, I_u8_high_open, dtype=np.uint64)) # E: ndarray[Any, dtype[unsignedinteger[typing._64Bit]]] +reveal_type(def_gen.integers(0, I_u8_high_open, dtype=np.uint64)) # E: ndarray[Any, dtype[unsignedinteger[typing._64Bit]]] +reveal_type(def_gen.integers(I_u8_high_closed, dtype=np.uint64, endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._64Bit]]] +reveal_type(def_gen.integers(I_u8_low, I_u8_high_closed, dtype=np.uint64, endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._64Bit]]] +reveal_type(def_gen.integers(0, I_u8_high_closed, dtype=np.uint64, endpoint=True)) # E: ndarray[Any, dtype[unsignedinteger[typing._64Bit]]] + +I_i1_low: np.ndarray[Any, np.dtype[np.int8]] = np.array([-128], dtype=np.int8) +I_i1_low_like: List[int] = [-128] +I_i1_high_open: np.ndarray[Any, np.dtype[np.int8]] = np.array([127], dtype=np.int8) +I_i1_high_closed: np.ndarray[Any, np.dtype[np.int8]] = np.array([127], dtype=np.int8) + +reveal_type(def_gen.integers(128, dtype="i1")) # E: int +reveal_type(def_gen.integers(-128, 128, dtype="i1")) # E: int +reveal_type(def_gen.integers(127, dtype="i1", endpoint=True)) # E: int +reveal_type(def_gen.integers(-128, 127, dtype="i1", endpoint=True)) # E: int +reveal_type(def_gen.integers(I_i1_low_like, 127, dtype="i1", endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._8Bit]]] +reveal_type(def_gen.integers(I_i1_high_open, dtype="i1")) # E: ndarray[Any, dtype[signedinteger[typing._8Bit]]] +reveal_type(def_gen.integers(I_i1_low, I_i1_high_open, dtype="i1")) # E: ndarray[Any, dtype[signedinteger[typing._8Bit]]] +reveal_type(def_gen.integers(-128, I_i1_high_open, dtype="i1")) # E: ndarray[Any, dtype[signedinteger[typing._8Bit]]] +reveal_type(def_gen.integers(I_i1_high_closed, dtype="i1", endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._8Bit]]] +reveal_type(def_gen.integers(I_i1_low, I_i1_high_closed, dtype="i1", endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._8Bit]]] +reveal_type(def_gen.integers(-128, I_i1_high_closed, dtype="i1", endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._8Bit]]] + +reveal_type(def_gen.integers(128, dtype="int8")) # E: int +reveal_type(def_gen.integers(-128, 128, dtype="int8")) # E: int +reveal_type(def_gen.integers(127, dtype="int8", endpoint=True)) # E: int +reveal_type(def_gen.integers(-128, 127, dtype="int8", endpoint=True)) # E: int +reveal_type(def_gen.integers(I_i1_low_like, 127, dtype="int8", endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._8Bit]]] +reveal_type(def_gen.integers(I_i1_high_open, dtype="int8")) # E: ndarray[Any, dtype[signedinteger[typing._8Bit]]] +reveal_type(def_gen.integers(I_i1_low, I_i1_high_open, dtype="int8")) # E: ndarray[Any, dtype[signedinteger[typing._8Bit]]] +reveal_type(def_gen.integers(-128, I_i1_high_open, dtype="int8")) # E: ndarray[Any, dtype[signedinteger[typing._8Bit]]] +reveal_type(def_gen.integers(I_i1_high_closed, dtype="int8", endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._8Bit]]] +reveal_type(def_gen.integers(I_i1_low, I_i1_high_closed, dtype="int8", endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._8Bit]]] +reveal_type(def_gen.integers(-128, I_i1_high_closed, dtype="int8", endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._8Bit]]] + +reveal_type(def_gen.integers(128, dtype=np.int8)) # E: int +reveal_type(def_gen.integers(-128, 128, dtype=np.int8)) # E: int +reveal_type(def_gen.integers(127, dtype=np.int8, endpoint=True)) # E: int +reveal_type(def_gen.integers(-128, 127, dtype=np.int8, endpoint=True)) # E: int +reveal_type(def_gen.integers(I_i1_low_like, 127, dtype=np.int8, endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._8Bit]]] +reveal_type(def_gen.integers(I_i1_high_open, dtype=np.int8)) # E: ndarray[Any, dtype[signedinteger[typing._8Bit]]] +reveal_type(def_gen.integers(I_i1_low, I_i1_high_open, dtype=np.int8)) # E: ndarray[Any, dtype[signedinteger[typing._8Bit]]] +reveal_type(def_gen.integers(-128, I_i1_high_open, dtype=np.int8)) # E: ndarray[Any, dtype[signedinteger[typing._8Bit]]] +reveal_type(def_gen.integers(I_i1_high_closed, dtype=np.int8, endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._8Bit]]] +reveal_type(def_gen.integers(I_i1_low, I_i1_high_closed, dtype=np.int8, endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._8Bit]]] +reveal_type(def_gen.integers(-128, I_i1_high_closed, dtype=np.int8, endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._8Bit]]] + +I_i2_low: np.ndarray[Any, np.dtype[np.int16]] = np.array([-32768], dtype=np.int16) +I_i2_low_like: List[int] = [-32768] +I_i2_high_open: np.ndarray[Any, np.dtype[np.int16]] = np.array([32767], dtype=np.int16) +I_i2_high_closed: np.ndarray[Any, np.dtype[np.int16]] = np.array([32767], dtype=np.int16) + +reveal_type(def_gen.integers(32768, dtype="i2")) # E: int +reveal_type(def_gen.integers(-32768, 32768, dtype="i2")) # E: int +reveal_type(def_gen.integers(32767, dtype="i2", endpoint=True)) # E: int +reveal_type(def_gen.integers(-32768, 32767, dtype="i2", endpoint=True)) # E: int +reveal_type(def_gen.integers(I_i2_low_like, 32767, dtype="i2", endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._16Bit]]] +reveal_type(def_gen.integers(I_i2_high_open, dtype="i2")) # E: ndarray[Any, dtype[signedinteger[typing._16Bit]]] +reveal_type(def_gen.integers(I_i2_low, I_i2_high_open, dtype="i2")) # E: ndarray[Any, dtype[signedinteger[typing._16Bit]]] +reveal_type(def_gen.integers(-32768, I_i2_high_open, dtype="i2")) # E: ndarray[Any, dtype[signedinteger[typing._16Bit]]] +reveal_type(def_gen.integers(I_i2_high_closed, dtype="i2", endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._16Bit]]] +reveal_type(def_gen.integers(I_i2_low, I_i2_high_closed, dtype="i2", endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._16Bit]]] +reveal_type(def_gen.integers(-32768, I_i2_high_closed, dtype="i2", endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._16Bit]]] + +reveal_type(def_gen.integers(32768, dtype="int16")) # E: int +reveal_type(def_gen.integers(-32768, 32768, dtype="int16")) # E: int +reveal_type(def_gen.integers(32767, dtype="int16", endpoint=True)) # E: int +reveal_type(def_gen.integers(-32768, 32767, dtype="int16", endpoint=True)) # E: int +reveal_type(def_gen.integers(I_i2_low_like, 32767, dtype="int16", endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._16Bit]]] +reveal_type(def_gen.integers(I_i2_high_open, dtype="int16")) # E: ndarray[Any, dtype[signedinteger[typing._16Bit]]] +reveal_type(def_gen.integers(I_i2_low, I_i2_high_open, dtype="int16")) # E: ndarray[Any, dtype[signedinteger[typing._16Bit]]] +reveal_type(def_gen.integers(-32768, I_i2_high_open, dtype="int16")) # E: ndarray[Any, dtype[signedinteger[typing._16Bit]]] +reveal_type(def_gen.integers(I_i2_high_closed, dtype="int16", endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._16Bit]]] +reveal_type(def_gen.integers(I_i2_low, I_i2_high_closed, dtype="int16", endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._16Bit]]] +reveal_type(def_gen.integers(-32768, I_i2_high_closed, dtype="int16", endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._16Bit]]] + +reveal_type(def_gen.integers(32768, dtype=np.int16)) # E: int +reveal_type(def_gen.integers(-32768, 32768, dtype=np.int16)) # E: int +reveal_type(def_gen.integers(32767, dtype=np.int16, endpoint=True)) # E: int +reveal_type(def_gen.integers(-32768, 32767, dtype=np.int16, endpoint=True)) # E: int +reveal_type(def_gen.integers(I_i2_low_like, 32767, dtype=np.int16, endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._16Bit]]] +reveal_type(def_gen.integers(I_i2_high_open, dtype=np.int16)) # E: ndarray[Any, dtype[signedinteger[typing._16Bit]]] +reveal_type(def_gen.integers(I_i2_low, I_i2_high_open, dtype=np.int16)) # E: ndarray[Any, dtype[signedinteger[typing._16Bit]]] +reveal_type(def_gen.integers(-32768, I_i2_high_open, dtype=np.int16)) # E: ndarray[Any, dtype[signedinteger[typing._16Bit]]] +reveal_type(def_gen.integers(I_i2_high_closed, dtype=np.int16, endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._16Bit]]] +reveal_type(def_gen.integers(I_i2_low, I_i2_high_closed, dtype=np.int16, endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._16Bit]]] +reveal_type(def_gen.integers(-32768, I_i2_high_closed, dtype=np.int16, endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._16Bit]]] + +I_i4_low: np.ndarray[Any, np.dtype[np.int32]] = np.array([-2147483648], dtype=np.int32) +I_i4_low_like: List[int] = [-2147483648] +I_i4_high_open: np.ndarray[Any, np.dtype[np.int32]] = np.array([2147483647], dtype=np.int32) +I_i4_high_closed: np.ndarray[Any, np.dtype[np.int32]] = np.array([2147483647], dtype=np.int32) + +reveal_type(def_gen.integers(2147483648, dtype="i4")) # E: int +reveal_type(def_gen.integers(-2147483648, 2147483648, dtype="i4")) # E: int +reveal_type(def_gen.integers(2147483647, dtype="i4", endpoint=True)) # E: int +reveal_type(def_gen.integers(-2147483648, 2147483647, dtype="i4", endpoint=True)) # E: int +reveal_type(def_gen.integers(I_i4_low_like, 2147483647, dtype="i4", endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._32Bit]]] +reveal_type(def_gen.integers(I_i4_high_open, dtype="i4")) # E: ndarray[Any, dtype[signedinteger[typing._32Bit]]] +reveal_type(def_gen.integers(I_i4_low, I_i4_high_open, dtype="i4")) # E: ndarray[Any, dtype[signedinteger[typing._32Bit]]] +reveal_type(def_gen.integers(-2147483648, I_i4_high_open, dtype="i4")) # E: ndarray[Any, dtype[signedinteger[typing._32Bit]]] +reveal_type(def_gen.integers(I_i4_high_closed, dtype="i4", endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._32Bit]]] +reveal_type(def_gen.integers(I_i4_low, I_i4_high_closed, dtype="i4", endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._32Bit]]] +reveal_type(def_gen.integers(-2147483648, I_i4_high_closed, dtype="i4", endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._32Bit]]] + +reveal_type(def_gen.integers(2147483648, dtype="int32")) # E: int +reveal_type(def_gen.integers(-2147483648, 2147483648, dtype="int32")) # E: int +reveal_type(def_gen.integers(2147483647, dtype="int32", endpoint=True)) # E: int +reveal_type(def_gen.integers(-2147483648, 2147483647, dtype="int32", endpoint=True)) # E: int +reveal_type(def_gen.integers(I_i4_low_like, 2147483647, dtype="int32", endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._32Bit]]] +reveal_type(def_gen.integers(I_i4_high_open, dtype="int32")) # E: ndarray[Any, dtype[signedinteger[typing._32Bit]]] +reveal_type(def_gen.integers(I_i4_low, I_i4_high_open, dtype="int32")) # E: ndarray[Any, dtype[signedinteger[typing._32Bit]]] +reveal_type(def_gen.integers(-2147483648, I_i4_high_open, dtype="int32")) # E: ndarray[Any, dtype[signedinteger[typing._32Bit]]] +reveal_type(def_gen.integers(I_i4_high_closed, dtype="int32", endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._32Bit]]] +reveal_type(def_gen.integers(I_i4_low, I_i4_high_closed, dtype="int32", endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._32Bit]]] +reveal_type(def_gen.integers(-2147483648, I_i4_high_closed, dtype="int32", endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._32Bit]]] + +reveal_type(def_gen.integers(2147483648, dtype=np.int32)) # E: int +reveal_type(def_gen.integers(-2147483648, 2147483648, dtype=np.int32)) # E: int +reveal_type(def_gen.integers(2147483647, dtype=np.int32, endpoint=True)) # E: int +reveal_type(def_gen.integers(-2147483648, 2147483647, dtype=np.int32, endpoint=True)) # E: int +reveal_type(def_gen.integers(I_i4_low_like, 2147483647, dtype=np.int32, endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._32Bit]]] +reveal_type(def_gen.integers(I_i4_high_open, dtype=np.int32)) # E: ndarray[Any, dtype[signedinteger[typing._32Bit]]] +reveal_type(def_gen.integers(I_i4_low, I_i4_high_open, dtype=np.int32)) # E: ndarray[Any, dtype[signedinteger[typing._32Bit]]] +reveal_type(def_gen.integers(-2147483648, I_i4_high_open, dtype=np.int32)) # E: ndarray[Any, dtype[signedinteger[typing._32Bit]]] +reveal_type(def_gen.integers(I_i4_high_closed, dtype=np.int32, endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._32Bit]]] +reveal_type(def_gen.integers(I_i4_low, I_i4_high_closed, dtype=np.int32, endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._32Bit]]] +reveal_type(def_gen.integers(-2147483648, I_i4_high_closed, dtype=np.int32, endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._32Bit]]] + +I_i8_low: np.ndarray[Any, np.dtype[np.int64]] = np.array([-9223372036854775808], dtype=np.int64) +I_i8_low_like: List[int] = [-9223372036854775808] +I_i8_high_open: np.ndarray[Any, np.dtype[np.int64]] = np.array([9223372036854775807], dtype=np.int64) +I_i8_high_closed: np.ndarray[Any, np.dtype[np.int64]] = np.array([9223372036854775807], dtype=np.int64) + +reveal_type(def_gen.integers(9223372036854775808, dtype="i8")) # E: int +reveal_type(def_gen.integers(-9223372036854775808, 9223372036854775808, dtype="i8")) # E: int +reveal_type(def_gen.integers(9223372036854775807, dtype="i8", endpoint=True)) # E: int +reveal_type(def_gen.integers(-9223372036854775808, 9223372036854775807, dtype="i8", endpoint=True)) # E: int +reveal_type(def_gen.integers(I_i8_low_like, 9223372036854775807, dtype="i8", endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.integers(I_i8_high_open, dtype="i8")) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.integers(I_i8_low, I_i8_high_open, dtype="i8")) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.integers(-9223372036854775808, I_i8_high_open, dtype="i8")) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.integers(I_i8_high_closed, dtype="i8", endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.integers(I_i8_low, I_i8_high_closed, dtype="i8", endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.integers(-9223372036854775808, I_i8_high_closed, dtype="i8", endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] + +reveal_type(def_gen.integers(9223372036854775808, dtype="int64")) # E: int +reveal_type(def_gen.integers(-9223372036854775808, 9223372036854775808, dtype="int64")) # E: int +reveal_type(def_gen.integers(9223372036854775807, dtype="int64", endpoint=True)) # E: int +reveal_type(def_gen.integers(-9223372036854775808, 9223372036854775807, dtype="int64", endpoint=True)) # E: int +reveal_type(def_gen.integers(I_i8_low_like, 9223372036854775807, dtype="int64", endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.integers(I_i8_high_open, dtype="int64")) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.integers(I_i8_low, I_i8_high_open, dtype="int64")) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.integers(-9223372036854775808, I_i8_high_open, dtype="int64")) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.integers(I_i8_high_closed, dtype="int64", endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.integers(I_i8_low, I_i8_high_closed, dtype="int64", endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.integers(-9223372036854775808, I_i8_high_closed, dtype="int64", endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] + +reveal_type(def_gen.integers(9223372036854775808, dtype=np.int64)) # E: int +reveal_type(def_gen.integers(-9223372036854775808, 9223372036854775808, dtype=np.int64)) # E: int +reveal_type(def_gen.integers(9223372036854775807, dtype=np.int64, endpoint=True)) # E: int +reveal_type(def_gen.integers(-9223372036854775808, 9223372036854775807, dtype=np.int64, endpoint=True)) # E: int +reveal_type(def_gen.integers(I_i8_low_like, 9223372036854775807, dtype=np.int64, endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.integers(I_i8_high_open, dtype=np.int64)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.integers(I_i8_low, I_i8_high_open, dtype=np.int64)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.integers(-9223372036854775808, I_i8_high_open, dtype=np.int64)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.integers(I_i8_high_closed, dtype=np.int64, endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.integers(I_i8_low, I_i8_high_closed, dtype=np.int64, endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.integers(-9223372036854775808, I_i8_high_closed, dtype=np.int64, endpoint=True)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] + + +reveal_type(def_gen.bit_generator) # E: BitGenerator + +reveal_type(def_gen.bytes(2)) # E: bytes + +reveal_type(def_gen.choice(5)) # E: int +reveal_type(def_gen.choice(5, 3)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.choice(5, 3, replace=True)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.choice(5, 3, p=[1 / 5] * 5)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.choice(5, 3, p=[1 / 5] * 5, replace=False)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] + +reveal_type(def_gen.choice(["pooh", "rabbit", "piglet", "Christopher"])) # E: Any +reveal_type(def_gen.choice(["pooh", "rabbit", "piglet", "Christopher"], 3)) # E: ndarray[Any, Any] +reveal_type(def_gen.choice(["pooh", "rabbit", "piglet", "Christopher"], 3, p=[1 / 4] * 4)) # E: ndarray[Any, Any] +reveal_type(def_gen.choice(["pooh", "rabbit", "piglet", "Christopher"], 3, replace=True)) # E: ndarray[Any, Any] +reveal_type(def_gen.choice(["pooh", "rabbit", "piglet", "Christopher"], 3, replace=False, p=np.array([1 / 8, 1 / 8, 1 / 2, 1 / 4]))) # E: ndarray[Any, Any] + +reveal_type(def_gen.dirichlet([0.5, 0.5])) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.dirichlet(np.array([0.5, 0.5]))) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.dirichlet(np.array([0.5, 0.5]), size=3)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(def_gen.multinomial(20, [1 / 6.0] * 6)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.multinomial(20, np.array([0.5, 0.5]))) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.multinomial(20, [1 / 6.0] * 6, size=2)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.multinomial([[10], [20]], [1 / 6.0] * 6, size=(2, 2))) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.multinomial(np.array([[10], [20]]), np.array([0.5, 0.5]), size=(2, 2))) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] + +reveal_type(def_gen.multivariate_hypergeometric([3, 5, 7], 2)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.multivariate_hypergeometric(np.array([3, 5, 7]), 2)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.multivariate_hypergeometric(np.array([3, 5, 7]), 2, size=4)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.multivariate_hypergeometric(np.array([3, 5, 7]), 2, size=(4, 7))) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.multivariate_hypergeometric([3, 5, 7], 2, method="count")) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.multivariate_hypergeometric(np.array([3, 5, 7]), 2, method="marginals")) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] + +reveal_type(def_gen.multivariate_normal([0.0], [[1.0]])) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.multivariate_normal([0.0], np.array([[1.0]]))) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.multivariate_normal(np.array([0.0]), [[1.0]])) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(def_gen.multivariate_normal([0.0], np.array([[1.0]]))) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(def_gen.permutation(10)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(def_gen.permutation([1, 2, 3, 4])) # E: ndarray[Any, Any] +reveal_type(def_gen.permutation(np.array([1, 2, 3, 4]))) # E: ndarray[Any, Any] +reveal_type(def_gen.permutation(D_2D, axis=1)) # E: ndarray[Any, Any] +reveal_type(def_gen.permuted(D_2D)) # E: ndarray[Any, Any] +reveal_type(def_gen.permuted(D_2D_like)) # E: ndarray[Any, Any] +reveal_type(def_gen.permuted(D_2D, axis=1)) # E: ndarray[Any, Any] +reveal_type(def_gen.permuted(D_2D, out=D_2D)) # E: ndarray[Any, Any] +reveal_type(def_gen.permuted(D_2D_like, out=D_2D)) # E: ndarray[Any, Any] +reveal_type(def_gen.permuted(D_2D_like, out=D_2D)) # E: ndarray[Any, Any] +reveal_type(def_gen.permuted(D_2D, axis=1, out=D_2D)) # E: ndarray[Any, Any] + +reveal_type(def_gen.shuffle(np.arange(10))) # E: None +reveal_type(def_gen.shuffle([1, 2, 3, 4, 5])) # E: None +reveal_type(def_gen.shuffle(D_2D, axis=1)) # E: None + +reveal_type(np.random.Generator(pcg64)) # E: Generator +reveal_type(def_gen.__str__()) # E: str +reveal_type(def_gen.__repr__()) # E: str +def_gen_state = def_gen.__getstate__() +reveal_type(def_gen_state) # E: builtins.dict[builtins.str, Any] +reveal_type(def_gen.__setstate__(def_gen_state)) # E: None + +# RandomState +random_st: np.random.RandomState = np.random.RandomState() + +reveal_type(random_st.standard_normal()) # E: float +reveal_type(random_st.standard_normal(size=None)) # E: float +reveal_type(random_st.standard_normal(size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]]] + +reveal_type(random_st.random()) # E: float +reveal_type(random_st.random(size=None)) # E: float +reveal_type(random_st.random(size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]]] + +reveal_type(random_st.standard_cauchy()) # E: float +reveal_type(random_st.standard_cauchy(size=None)) # E: float +reveal_type(random_st.standard_cauchy(size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(random_st.standard_exponential()) # E: float +reveal_type(random_st.standard_exponential(size=None)) # E: float +reveal_type(random_st.standard_exponential(size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]]] + +reveal_type(random_st.zipf(1.5)) # E: int +reveal_type(random_st.zipf(1.5, size=None)) # E: int +reveal_type(random_st.zipf(1.5, size=1)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.zipf(D_arr_1p5)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.zipf(D_arr_1p5, size=1)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.zipf(D_arr_like_1p5)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.zipf(D_arr_like_1p5, size=1)) # E: ndarray[Any, dtype[{int_}]] + +reveal_type(random_st.weibull(0.5)) # E: float +reveal_type(random_st.weibull(0.5, size=None)) # E: float +reveal_type(random_st.weibull(0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.weibull(D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.weibull(D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.weibull(D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.weibull(D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(random_st.standard_t(0.5)) # E: float +reveal_type(random_st.standard_t(0.5, size=None)) # E: float +reveal_type(random_st.standard_t(0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.standard_t(D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.standard_t(D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.standard_t(D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.standard_t(D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(random_st.poisson(0.5)) # E: int +reveal_type(random_st.poisson(0.5, size=None)) # E: int +reveal_type(random_st.poisson(0.5, size=1)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.poisson(D_arr_0p5)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.poisson(D_arr_0p5, size=1)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.poisson(D_arr_like_0p5)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.poisson(D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[{int_}]] + +reveal_type(random_st.power(0.5)) # E: float +reveal_type(random_st.power(0.5, size=None)) # E: float +reveal_type(random_st.power(0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.power(D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.power(D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.power(D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.power(D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(random_st.pareto(0.5)) # E: float +reveal_type(random_st.pareto(0.5, size=None)) # E: float +reveal_type(random_st.pareto(0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.pareto(D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.pareto(D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.pareto(D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.pareto(D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(random_st.chisquare(0.5)) # E: float +reveal_type(random_st.chisquare(0.5, size=None)) # E: float +reveal_type(random_st.chisquare(0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.chisquare(D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.chisquare(D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.chisquare(D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.chisquare(D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(random_st.exponential(0.5)) # E: float +reveal_type(random_st.exponential(0.5, size=None)) # E: float +reveal_type(random_st.exponential(0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.exponential(D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.exponential(D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.exponential(D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.exponential(D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(random_st.geometric(0.5)) # E: int +reveal_type(random_st.geometric(0.5, size=None)) # E: int +reveal_type(random_st.geometric(0.5, size=1)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.geometric(D_arr_0p5)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.geometric(D_arr_0p5, size=1)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.geometric(D_arr_like_0p5)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.geometric(D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[{int_}]] + +reveal_type(random_st.logseries(0.5)) # E: int +reveal_type(random_st.logseries(0.5, size=None)) # E: int +reveal_type(random_st.logseries(0.5, size=1)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.logseries(D_arr_0p5)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.logseries(D_arr_0p5, size=1)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.logseries(D_arr_like_0p5)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.logseries(D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[{int_}]] + +reveal_type(random_st.rayleigh(0.5)) # E: float +reveal_type(random_st.rayleigh(0.5, size=None)) # E: float +reveal_type(random_st.rayleigh(0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.rayleigh(D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.rayleigh(D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.rayleigh(D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.rayleigh(D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(random_st.standard_gamma(0.5)) # E: float +reveal_type(random_st.standard_gamma(0.5, size=None)) # E: float +reveal_type(random_st.standard_gamma(0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]]] +reveal_type(random_st.standard_gamma(D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]]] +reveal_type(random_st.standard_gamma(D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]]] +reveal_type(random_st.standard_gamma(D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]]] +reveal_type(random_st.standard_gamma(D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]]] +reveal_type(random_st.standard_gamma(D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]]] + +reveal_type(random_st.vonmises(0.5, 0.5)) # E: float +reveal_type(random_st.vonmises(0.5, 0.5, size=None)) # E: float +reveal_type(random_st.vonmises(0.5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.vonmises(D_arr_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.vonmises(0.5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.vonmises(D_arr_0p5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.vonmises(0.5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.vonmises(D_arr_like_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.vonmises(0.5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.vonmises(D_arr_0p5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.vonmises(D_arr_like_0p5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.vonmises(D_arr_0p5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.vonmises(D_arr_like_0p5, D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(random_st.wald(0.5, 0.5)) # E: float +reveal_type(random_st.wald(0.5, 0.5, size=None)) # E: float +reveal_type(random_st.wald(0.5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.wald(D_arr_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.wald(0.5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.wald(D_arr_0p5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.wald(0.5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.wald(D_arr_like_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.wald(0.5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.wald(D_arr_0p5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.wald(D_arr_like_0p5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.wald(D_arr_0p5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.wald(D_arr_like_0p5, D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(random_st.uniform(0.5, 0.5)) # E: float +reveal_type(random_st.uniform(0.5, 0.5, size=None)) # E: float +reveal_type(random_st.uniform(0.5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.uniform(D_arr_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.uniform(0.5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.uniform(D_arr_0p5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.uniform(0.5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.uniform(D_arr_like_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.uniform(0.5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.uniform(D_arr_0p5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.uniform(D_arr_like_0p5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.uniform(D_arr_0p5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.uniform(D_arr_like_0p5, D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(random_st.beta(0.5, 0.5)) # E: float +reveal_type(random_st.beta(0.5, 0.5, size=None)) # E: float +reveal_type(random_st.beta(0.5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.beta(D_arr_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.beta(0.5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.beta(D_arr_0p5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.beta(0.5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.beta(D_arr_like_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.beta(0.5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.beta(D_arr_0p5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.beta(D_arr_like_0p5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.beta(D_arr_0p5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.beta(D_arr_like_0p5, D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(random_st.f(0.5, 0.5)) # E: float +reveal_type(random_st.f(0.5, 0.5, size=None)) # E: float +reveal_type(random_st.f(0.5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.f(D_arr_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.f(0.5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.f(D_arr_0p5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.f(0.5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.f(D_arr_like_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.f(0.5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.f(D_arr_0p5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.f(D_arr_like_0p5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.f(D_arr_0p5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.f(D_arr_like_0p5, D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(random_st.gamma(0.5, 0.5)) # E: float +reveal_type(random_st.gamma(0.5, 0.5, size=None)) # E: float +reveal_type(random_st.gamma(0.5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.gamma(D_arr_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.gamma(0.5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.gamma(D_arr_0p5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.gamma(0.5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.gamma(D_arr_like_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.gamma(0.5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.gamma(D_arr_0p5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.gamma(D_arr_like_0p5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.gamma(D_arr_0p5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.gamma(D_arr_like_0p5, D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(random_st.gumbel(0.5, 0.5)) # E: float +reveal_type(random_st.gumbel(0.5, 0.5, size=None)) # E: float +reveal_type(random_st.gumbel(0.5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.gumbel(D_arr_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.gumbel(0.5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.gumbel(D_arr_0p5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.gumbel(0.5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.gumbel(D_arr_like_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.gumbel(0.5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.gumbel(D_arr_0p5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.gumbel(D_arr_like_0p5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.gumbel(D_arr_0p5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.gumbel(D_arr_like_0p5, D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(random_st.laplace(0.5, 0.5)) # E: float +reveal_type(random_st.laplace(0.5, 0.5, size=None)) # E: float +reveal_type(random_st.laplace(0.5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.laplace(D_arr_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.laplace(0.5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.laplace(D_arr_0p5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.laplace(0.5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.laplace(D_arr_like_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.laplace(0.5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.laplace(D_arr_0p5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.laplace(D_arr_like_0p5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.laplace(D_arr_0p5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.laplace(D_arr_like_0p5, D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(random_st.logistic(0.5, 0.5)) # E: float +reveal_type(random_st.logistic(0.5, 0.5, size=None)) # E: float +reveal_type(random_st.logistic(0.5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.logistic(D_arr_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.logistic(0.5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.logistic(D_arr_0p5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.logistic(0.5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.logistic(D_arr_like_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.logistic(0.5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.logistic(D_arr_0p5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.logistic(D_arr_like_0p5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.logistic(D_arr_0p5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.logistic(D_arr_like_0p5, D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(random_st.lognormal(0.5, 0.5)) # E: float +reveal_type(random_st.lognormal(0.5, 0.5, size=None)) # E: float +reveal_type(random_st.lognormal(0.5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.lognormal(D_arr_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.lognormal(0.5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.lognormal(D_arr_0p5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.lognormal(0.5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.lognormal(D_arr_like_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.lognormal(0.5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.lognormal(D_arr_0p5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.lognormal(D_arr_like_0p5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.lognormal(D_arr_0p5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.lognormal(D_arr_like_0p5, D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(random_st.noncentral_chisquare(0.5, 0.5)) # E: float +reveal_type(random_st.noncentral_chisquare(0.5, 0.5, size=None)) # E: float +reveal_type(random_st.noncentral_chisquare(0.5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.noncentral_chisquare(D_arr_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.noncentral_chisquare(0.5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.noncentral_chisquare(D_arr_0p5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.noncentral_chisquare(0.5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.noncentral_chisquare(D_arr_like_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.noncentral_chisquare(0.5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.noncentral_chisquare(D_arr_0p5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.noncentral_chisquare(D_arr_like_0p5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.noncentral_chisquare(D_arr_0p5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.noncentral_chisquare(D_arr_like_0p5, D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(random_st.normal(0.5, 0.5)) # E: float +reveal_type(random_st.normal(0.5, 0.5, size=None)) # E: float +reveal_type(random_st.normal(0.5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.normal(D_arr_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.normal(0.5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.normal(D_arr_0p5, 0.5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.normal(0.5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.normal(D_arr_like_0p5, 0.5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.normal(0.5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.normal(D_arr_0p5, D_arr_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.normal(D_arr_like_0p5, D_arr_like_0p5)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.normal(D_arr_0p5, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.normal(D_arr_like_0p5, D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(random_st.triangular(0.1, 0.5, 0.9)) # E: float +reveal_type(random_st.triangular(0.1, 0.5, 0.9, size=None)) # E: float +reveal_type(random_st.triangular(0.1, 0.5, 0.9, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.triangular(D_arr_0p1, 0.5, 0.9)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.triangular(0.1, D_arr_0p5, 0.9)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.triangular(D_arr_0p1, 0.5, D_arr_like_0p9, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.triangular(0.1, D_arr_0p5, 0.9, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.triangular(D_arr_like_0p1, 0.5, D_arr_0p9)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.triangular(0.5, D_arr_like_0p5, 0.9)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.triangular(D_arr_0p1, D_arr_0p5, 0.9)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.triangular(D_arr_like_0p1, D_arr_like_0p5, 0.9)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.triangular(D_arr_0p1, D_arr_0p5, D_arr_0p9, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.triangular(D_arr_like_0p1, D_arr_like_0p5, D_arr_like_0p9, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(random_st.noncentral_f(0.1, 0.5, 0.9)) # E: float +reveal_type(random_st.noncentral_f(0.1, 0.5, 0.9, size=None)) # E: float +reveal_type(random_st.noncentral_f(0.1, 0.5, 0.9, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.noncentral_f(D_arr_0p1, 0.5, 0.9)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.noncentral_f(0.1, D_arr_0p5, 0.9)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.noncentral_f(D_arr_0p1, 0.5, D_arr_like_0p9, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.noncentral_f(0.1, D_arr_0p5, 0.9, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.noncentral_f(D_arr_like_0p1, 0.5, D_arr_0p9)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.noncentral_f(0.5, D_arr_like_0p5, 0.9)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.noncentral_f(D_arr_0p1, D_arr_0p5, 0.9)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.noncentral_f(D_arr_like_0p1, D_arr_like_0p5, 0.9)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.noncentral_f(D_arr_0p1, D_arr_0p5, D_arr_0p9, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.noncentral_f(D_arr_like_0p1, D_arr_like_0p5, D_arr_like_0p9, size=1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(random_st.binomial(10, 0.5)) # E: int +reveal_type(random_st.binomial(10, 0.5, size=None)) # E: int +reveal_type(random_st.binomial(10, 0.5, size=1)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.binomial(I_arr_10, 0.5)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.binomial(10, D_arr_0p5)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.binomial(I_arr_10, 0.5, size=1)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.binomial(10, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.binomial(I_arr_like_10, 0.5)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.binomial(10, D_arr_like_0p5)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.binomial(I_arr_10, D_arr_0p5)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.binomial(I_arr_like_10, D_arr_like_0p5)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.binomial(I_arr_10, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.binomial(I_arr_like_10, D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[{int_}]] + +reveal_type(random_st.negative_binomial(10, 0.5)) # E: int +reveal_type(random_st.negative_binomial(10, 0.5, size=None)) # E: int +reveal_type(random_st.negative_binomial(10, 0.5, size=1)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.negative_binomial(I_arr_10, 0.5)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.negative_binomial(10, D_arr_0p5)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.negative_binomial(I_arr_10, 0.5, size=1)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.negative_binomial(10, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.negative_binomial(I_arr_like_10, 0.5)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.negative_binomial(10, D_arr_like_0p5)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.negative_binomial(I_arr_10, D_arr_0p5)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.negative_binomial(I_arr_like_10, D_arr_like_0p5)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.negative_binomial(I_arr_10, D_arr_0p5, size=1)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.negative_binomial(I_arr_like_10, D_arr_like_0p5, size=1)) # E: ndarray[Any, dtype[{int_}]] + +reveal_type(random_st.hypergeometric(20, 20, 10)) # E: int +reveal_type(random_st.hypergeometric(20, 20, 10, size=None)) # E: int +reveal_type(random_st.hypergeometric(20, 20, 10, size=1)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.hypergeometric(I_arr_20, 20, 10)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.hypergeometric(20, I_arr_20, 10)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.hypergeometric(I_arr_20, 20, I_arr_like_10, size=1)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.hypergeometric(20, I_arr_20, 10, size=1)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.hypergeometric(I_arr_like_20, 20, I_arr_10)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.hypergeometric(20, I_arr_like_20, 10)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.hypergeometric(I_arr_20, I_arr_20, 10)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.hypergeometric(I_arr_like_20, I_arr_like_20, 10)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.hypergeometric(I_arr_20, I_arr_20, I_arr_10, size=1)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.hypergeometric(I_arr_like_20, I_arr_like_20, I_arr_like_10, size=1)) # E: ndarray[Any, dtype[{int_}]] + +reveal_type(random_st.randint(0, 100)) # E: int +reveal_type(random_st.randint(100)) # E: int +reveal_type(random_st.randint([100])) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.randint(0, [100])) # E: ndarray[Any, dtype[{int_}]] + +reveal_type(random_st.randint(2, dtype=bool)) # E: builtins.bool +reveal_type(random_st.randint(0, 2, dtype=bool)) # E: builtins.bool +reveal_type(random_st.randint(I_bool_high_open, dtype=bool)) # E: ndarray[Any, dtype[bool_] +reveal_type(random_st.randint(I_bool_low, I_bool_high_open, dtype=bool)) # E: ndarray[Any, dtype[bool_] +reveal_type(random_st.randint(0, I_bool_high_open, dtype=bool)) # E: ndarray[Any, dtype[bool_] + +reveal_type(random_st.randint(2, dtype=np.bool_)) # E: builtins.bool +reveal_type(random_st.randint(0, 2, dtype=np.bool_)) # E: builtins.bool +reveal_type(random_st.randint(I_bool_high_open, dtype=np.bool_)) # E: ndarray[Any, dtype[bool_] +reveal_type(random_st.randint(I_bool_low, I_bool_high_open, dtype=np.bool_)) # E: ndarray[Any, dtype[bool_] +reveal_type(random_st.randint(0, I_bool_high_open, dtype=np.bool_)) # E: ndarray[Any, dtype[bool_] + +reveal_type(random_st.randint(256, dtype="u1")) # E: int +reveal_type(random_st.randint(0, 256, dtype="u1")) # E: int +reveal_type(random_st.randint(I_u1_high_open, dtype="u1")) # E: ndarray[Any, dtype[unsignedinteger[typing._8Bit]]] +reveal_type(random_st.randint(I_u1_low, I_u1_high_open, dtype="u1")) # E: ndarray[Any, dtype[unsignedinteger[typing._8Bit]]] +reveal_type(random_st.randint(0, I_u1_high_open, dtype="u1")) # E: ndarray[Any, dtype[unsignedinteger[typing._8Bit]]] + +reveal_type(random_st.randint(256, dtype="uint8")) # E: int +reveal_type(random_st.randint(0, 256, dtype="uint8")) # E: int +reveal_type(random_st.randint(I_u1_high_open, dtype="uint8")) # E: ndarray[Any, dtype[unsignedinteger[typing._8Bit]]] +reveal_type(random_st.randint(I_u1_low, I_u1_high_open, dtype="uint8")) # E: ndarray[Any, dtype[unsignedinteger[typing._8Bit]]] +reveal_type(random_st.randint(0, I_u1_high_open, dtype="uint8")) # E: ndarray[Any, dtype[unsignedinteger[typing._8Bit]]] + +reveal_type(random_st.randint(256, dtype=np.uint8)) # E: int +reveal_type(random_st.randint(0, 256, dtype=np.uint8)) # E: int +reveal_type(random_st.randint(I_u1_high_open, dtype=np.uint8)) # E: ndarray[Any, dtype[unsignedinteger[typing._8Bit]]] +reveal_type(random_st.randint(I_u1_low, I_u1_high_open, dtype=np.uint8)) # E: ndarray[Any, dtype[unsignedinteger[typing._8Bit]]] +reveal_type(random_st.randint(0, I_u1_high_open, dtype=np.uint8)) # E: ndarray[Any, dtype[unsignedinteger[typing._8Bit]]] + +reveal_type(random_st.randint(65536, dtype="u2")) # E: int +reveal_type(random_st.randint(0, 65536, dtype="u2")) # E: int +reveal_type(random_st.randint(I_u2_high_open, dtype="u2")) # E: ndarray[Any, dtype[unsignedinteger[typing._16Bit]]] +reveal_type(random_st.randint(I_u2_low, I_u2_high_open, dtype="u2")) # E: ndarray[Any, dtype[unsignedinteger[typing._16Bit]]] +reveal_type(random_st.randint(0, I_u2_high_open, dtype="u2")) # E: ndarray[Any, dtype[unsignedinteger[typing._16Bit]]] + +reveal_type(random_st.randint(65536, dtype="uint16")) # E: int +reveal_type(random_st.randint(0, 65536, dtype="uint16")) # E: int +reveal_type(random_st.randint(I_u2_high_open, dtype="uint16")) # E: ndarray[Any, dtype[unsignedinteger[typing._16Bit]]] +reveal_type(random_st.randint(I_u2_low, I_u2_high_open, dtype="uint16")) # E: ndarray[Any, dtype[unsignedinteger[typing._16Bit]]] +reveal_type(random_st.randint(0, I_u2_high_open, dtype="uint16")) # E: ndarray[Any, dtype[unsignedinteger[typing._16Bit]]] + +reveal_type(random_st.randint(65536, dtype=np.uint16)) # E: int +reveal_type(random_st.randint(0, 65536, dtype=np.uint16)) # E: int +reveal_type(random_st.randint(I_u2_high_open, dtype=np.uint16)) # E: ndarray[Any, dtype[unsignedinteger[typing._16Bit]]] +reveal_type(random_st.randint(I_u2_low, I_u2_high_open, dtype=np.uint16)) # E: ndarray[Any, dtype[unsignedinteger[typing._16Bit]]] +reveal_type(random_st.randint(0, I_u2_high_open, dtype=np.uint16)) # E: ndarray[Any, dtype[unsignedinteger[typing._16Bit]]] + +reveal_type(random_st.randint(4294967296, dtype="u4")) # E: int +reveal_type(random_st.randint(0, 4294967296, dtype="u4")) # E: int +reveal_type(random_st.randint(I_u4_high_open, dtype="u4")) # E: ndarray[Any, dtype[unsignedinteger[typing._32Bit]]] +reveal_type(random_st.randint(I_u4_low, I_u4_high_open, dtype="u4")) # E: ndarray[Any, dtype[unsignedinteger[typing._32Bit]]] +reveal_type(random_st.randint(0, I_u4_high_open, dtype="u4")) # E: ndarray[Any, dtype[unsignedinteger[typing._32Bit]]] + +reveal_type(random_st.randint(4294967296, dtype="uint32")) # E: int +reveal_type(random_st.randint(0, 4294967296, dtype="uint32")) # E: int +reveal_type(random_st.randint(I_u4_high_open, dtype="uint32")) # E: ndarray[Any, dtype[unsignedinteger[typing._32Bit]]] +reveal_type(random_st.randint(I_u4_low, I_u4_high_open, dtype="uint32")) # E: ndarray[Any, dtype[unsignedinteger[typing._32Bit]]] +reveal_type(random_st.randint(0, I_u4_high_open, dtype="uint32")) # E: ndarray[Any, dtype[unsignedinteger[typing._32Bit]]] + +reveal_type(random_st.randint(4294967296, dtype=np.uint32)) # E: int +reveal_type(random_st.randint(0, 4294967296, dtype=np.uint32)) # E: int +reveal_type(random_st.randint(I_u4_high_open, dtype=np.uint32)) # E: ndarray[Any, dtype[unsignedinteger[typing._32Bit]]] +reveal_type(random_st.randint(I_u4_low, I_u4_high_open, dtype=np.uint32)) # E: ndarray[Any, dtype[unsignedinteger[typing._32Bit]]] +reveal_type(random_st.randint(0, I_u4_high_open, dtype=np.uint32)) # E: ndarray[Any, dtype[unsignedinteger[typing._32Bit]]] + +reveal_type(random_st.randint(4294967296, dtype=np.uint)) # E: int +reveal_type(random_st.randint(0, 4294967296, dtype=np.uint)) # E: int +reveal_type(random_st.randint(I_u4_high_open, dtype=np.uint)) # E: ndarray[Any, dtype[{uint}]] +reveal_type(random_st.randint(I_u4_low, I_u4_high_open, dtype=np.uint)) # E: ndarray[Any, dtype[{uint}]] +reveal_type(random_st.randint(0, I_u4_high_open, dtype=np.uint)) # E: ndarray[Any, dtype[{uint}]] + +reveal_type(random_st.randint(18446744073709551616, dtype="u8")) # E: int +reveal_type(random_st.randint(0, 18446744073709551616, dtype="u8")) # E: int +reveal_type(random_st.randint(I_u8_high_open, dtype="u8")) # E: ndarray[Any, dtype[unsignedinteger[typing._64Bit]]] +reveal_type(random_st.randint(I_u8_low, I_u8_high_open, dtype="u8")) # E: ndarray[Any, dtype[unsignedinteger[typing._64Bit]]] +reveal_type(random_st.randint(0, I_u8_high_open, dtype="u8")) # E: ndarray[Any, dtype[unsignedinteger[typing._64Bit]]] + +reveal_type(random_st.randint(18446744073709551616, dtype="uint64")) # E: int +reveal_type(random_st.randint(0, 18446744073709551616, dtype="uint64")) # E: int +reveal_type(random_st.randint(I_u8_high_open, dtype="uint64")) # E: ndarray[Any, dtype[unsignedinteger[typing._64Bit]]] +reveal_type(random_st.randint(I_u8_low, I_u8_high_open, dtype="uint64")) # E: ndarray[Any, dtype[unsignedinteger[typing._64Bit]]] +reveal_type(random_st.randint(0, I_u8_high_open, dtype="uint64")) # E: ndarray[Any, dtype[unsignedinteger[typing._64Bit]]] + +reveal_type(random_st.randint(18446744073709551616, dtype=np.uint64)) # E: int +reveal_type(random_st.randint(0, 18446744073709551616, dtype=np.uint64)) # E: int +reveal_type(random_st.randint(I_u8_high_open, dtype=np.uint64)) # E: ndarray[Any, dtype[unsignedinteger[typing._64Bit]]] +reveal_type(random_st.randint(I_u8_low, I_u8_high_open, dtype=np.uint64)) # E: ndarray[Any, dtype[unsignedinteger[typing._64Bit]]] +reveal_type(random_st.randint(0, I_u8_high_open, dtype=np.uint64)) # E: ndarray[Any, dtype[unsignedinteger[typing._64Bit]]] + +reveal_type(random_st.randint(128, dtype="i1")) # E: int +reveal_type(random_st.randint(-128, 128, dtype="i1")) # E: int +reveal_type(random_st.randint(I_i1_high_open, dtype="i1")) # E: ndarray[Any, dtype[signedinteger[typing._8Bit]]] +reveal_type(random_st.randint(I_i1_low, I_i1_high_open, dtype="i1")) # E: ndarray[Any, dtype[signedinteger[typing._8Bit]]] +reveal_type(random_st.randint(-128, I_i1_high_open, dtype="i1")) # E: ndarray[Any, dtype[signedinteger[typing._8Bit]]] + +reveal_type(random_st.randint(128, dtype="int8")) # E: int +reveal_type(random_st.randint(-128, 128, dtype="int8")) # E: int +reveal_type(random_st.randint(I_i1_high_open, dtype="int8")) # E: ndarray[Any, dtype[signedinteger[typing._8Bit]]] +reveal_type(random_st.randint(I_i1_low, I_i1_high_open, dtype="int8")) # E: ndarray[Any, dtype[signedinteger[typing._8Bit]]] +reveal_type(random_st.randint(-128, I_i1_high_open, dtype="int8")) # E: ndarray[Any, dtype[signedinteger[typing._8Bit]]] + +reveal_type(random_st.randint(128, dtype=np.int8)) # E: int +reveal_type(random_st.randint(-128, 128, dtype=np.int8)) # E: int +reveal_type(random_st.randint(I_i1_high_open, dtype=np.int8)) # E: ndarray[Any, dtype[signedinteger[typing._8Bit]]] +reveal_type(random_st.randint(I_i1_low, I_i1_high_open, dtype=np.int8)) # E: ndarray[Any, dtype[signedinteger[typing._8Bit]]] +reveal_type(random_st.randint(-128, I_i1_high_open, dtype=np.int8)) # E: ndarray[Any, dtype[signedinteger[typing._8Bit]]] + +reveal_type(random_st.randint(32768, dtype="i2")) # E: int +reveal_type(random_st.randint(-32768, 32768, dtype="i2")) # E: int +reveal_type(random_st.randint(I_i2_high_open, dtype="i2")) # E: ndarray[Any, dtype[signedinteger[typing._16Bit]]] +reveal_type(random_st.randint(I_i2_low, I_i2_high_open, dtype="i2")) # E: ndarray[Any, dtype[signedinteger[typing._16Bit]]] +reveal_type(random_st.randint(-32768, I_i2_high_open, dtype="i2")) # E: ndarray[Any, dtype[signedinteger[typing._16Bit]]] +reveal_type(random_st.randint(32768, dtype="int16")) # E: int +reveal_type(random_st.randint(-32768, 32768, dtype="int16")) # E: int +reveal_type(random_st.randint(I_i2_high_open, dtype="int16")) # E: ndarray[Any, dtype[signedinteger[typing._16Bit]]] +reveal_type(random_st.randint(I_i2_low, I_i2_high_open, dtype="int16")) # E: ndarray[Any, dtype[signedinteger[typing._16Bit]]] +reveal_type(random_st.randint(-32768, I_i2_high_open, dtype="int16")) # E: ndarray[Any, dtype[signedinteger[typing._16Bit]]] +reveal_type(random_st.randint(32768, dtype=np.int16)) # E: int +reveal_type(random_st.randint(-32768, 32768, dtype=np.int16)) # E: int +reveal_type(random_st.randint(I_i2_high_open, dtype=np.int16)) # E: ndarray[Any, dtype[signedinteger[typing._16Bit]]] +reveal_type(random_st.randint(I_i2_low, I_i2_high_open, dtype=np.int16)) # E: ndarray[Any, dtype[signedinteger[typing._16Bit]]] +reveal_type(random_st.randint(-32768, I_i2_high_open, dtype=np.int16)) # E: ndarray[Any, dtype[signedinteger[typing._16Bit]]] + +reveal_type(random_st.randint(2147483648, dtype="i4")) # E: int +reveal_type(random_st.randint(-2147483648, 2147483648, dtype="i4")) # E: int +reveal_type(random_st.randint(I_i4_high_open, dtype="i4")) # E: ndarray[Any, dtype[signedinteger[typing._32Bit]]] +reveal_type(random_st.randint(I_i4_low, I_i4_high_open, dtype="i4")) # E: ndarray[Any, dtype[signedinteger[typing._32Bit]]] +reveal_type(random_st.randint(-2147483648, I_i4_high_open, dtype="i4")) # E: ndarray[Any, dtype[signedinteger[typing._32Bit]]] + +reveal_type(random_st.randint(2147483648, dtype="int32")) # E: int +reveal_type(random_st.randint(-2147483648, 2147483648, dtype="int32")) # E: int +reveal_type(random_st.randint(I_i4_high_open, dtype="int32")) # E: ndarray[Any, dtype[signedinteger[typing._32Bit]]] +reveal_type(random_st.randint(I_i4_low, I_i4_high_open, dtype="int32")) # E: ndarray[Any, dtype[signedinteger[typing._32Bit]]] +reveal_type(random_st.randint(-2147483648, I_i4_high_open, dtype="int32")) # E: ndarray[Any, dtype[signedinteger[typing._32Bit]]] + +reveal_type(random_st.randint(2147483648, dtype=np.int32)) # E: int +reveal_type(random_st.randint(-2147483648, 2147483648, dtype=np.int32)) # E: int +reveal_type(random_st.randint(I_i4_high_open, dtype=np.int32)) # E: ndarray[Any, dtype[signedinteger[typing._32Bit]]] +reveal_type(random_st.randint(I_i4_low, I_i4_high_open, dtype=np.int32)) # E: ndarray[Any, dtype[signedinteger[typing._32Bit]]] +reveal_type(random_st.randint(-2147483648, I_i4_high_open, dtype=np.int32)) # E: ndarray[Any, dtype[signedinteger[typing._32Bit]]] + +reveal_type(random_st.randint(2147483648, dtype=np.int_)) # E: int +reveal_type(random_st.randint(-2147483648, 2147483648, dtype=np.int_)) # E: int +reveal_type(random_st.randint(I_i4_high_open, dtype=np.int_)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.randint(I_i4_low, I_i4_high_open, dtype=np.int_)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.randint(-2147483648, I_i4_high_open, dtype=np.int_)) # E: ndarray[Any, dtype[{int_}]] + +reveal_type(random_st.randint(9223372036854775808, dtype="i8")) # E: int +reveal_type(random_st.randint(-9223372036854775808, 9223372036854775808, dtype="i8")) # E: int +reveal_type(random_st.randint(I_i8_high_open, dtype="i8")) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(random_st.randint(I_i8_low, I_i8_high_open, dtype="i8")) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(random_st.randint(-9223372036854775808, I_i8_high_open, dtype="i8")) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] + +reveal_type(random_st.randint(9223372036854775808, dtype="int64")) # E: int +reveal_type(random_st.randint(-9223372036854775808, 9223372036854775808, dtype="int64")) # E: int +reveal_type(random_st.randint(I_i8_high_open, dtype="int64")) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(random_st.randint(I_i8_low, I_i8_high_open, dtype="int64")) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(random_st.randint(-9223372036854775808, I_i8_high_open, dtype="int64")) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] + +reveal_type(random_st.randint(9223372036854775808, dtype=np.int64)) # E: int +reveal_type(random_st.randint(-9223372036854775808, 9223372036854775808, dtype=np.int64)) # E: int +reveal_type(random_st.randint(I_i8_high_open, dtype=np.int64)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(random_st.randint(I_i8_low, I_i8_high_open, dtype=np.int64)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] +reveal_type(random_st.randint(-9223372036854775808, I_i8_high_open, dtype=np.int64)) # E: ndarray[Any, dtype[signedinteger[typing._64Bit]]] + +reveal_type(random_st._bit_generator) # E: BitGenerator + +reveal_type(random_st.bytes(2)) # E: bytes + +reveal_type(random_st.choice(5)) # E: int +reveal_type(random_st.choice(5, 3)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.choice(5, 3, replace=True)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.choice(5, 3, p=[1 / 5] * 5)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.choice(5, 3, p=[1 / 5] * 5, replace=False)) # E: ndarray[Any, dtype[{int_}]] + +reveal_type(random_st.choice(["pooh", "rabbit", "piglet", "Christopher"])) # E: Any +reveal_type(random_st.choice(["pooh", "rabbit", "piglet", "Christopher"], 3)) # E: ndarray[Any, Any] +reveal_type(random_st.choice(["pooh", "rabbit", "piglet", "Christopher"], 3, p=[1 / 4] * 4)) # E: ndarray[Any, Any] +reveal_type(random_st.choice(["pooh", "rabbit", "piglet", "Christopher"], 3, replace=True)) # E: ndarray[Any, Any] +reveal_type(random_st.choice(["pooh", "rabbit", "piglet", "Christopher"], 3, replace=False, p=np.array([1 / 8, 1 / 8, 1 / 2, 1 / 4]))) # E: ndarray[Any, Any] + +reveal_type(random_st.dirichlet([0.5, 0.5])) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.dirichlet(np.array([0.5, 0.5]))) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.dirichlet(np.array([0.5, 0.5]), size=3)) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(random_st.multinomial(20, [1 / 6.0] * 6)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.multinomial(20, np.array([0.5, 0.5]))) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.multinomial(20, [1 / 6.0] * 6, size=2)) # E: ndarray[Any, dtype[{int_}]] + +reveal_type(random_st.multivariate_normal([0.0], [[1.0]])) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.multivariate_normal([0.0], np.array([[1.0]]))) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.multivariate_normal(np.array([0.0]), [[1.0]])) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.multivariate_normal([0.0], np.array([[1.0]]))) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(random_st.permutation(10)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.permutation([1, 2, 3, 4])) # E: ndarray[Any, Any] +reveal_type(random_st.permutation(np.array([1, 2, 3, 4]))) # E: ndarray[Any, Any] +reveal_type(random_st.permutation(D_2D)) # E: ndarray[Any, Any] + +reveal_type(random_st.shuffle(np.arange(10))) # E: None +reveal_type(random_st.shuffle([1, 2, 3, 4, 5])) # E: None +reveal_type(random_st.shuffle(D_2D)) # E: None + +reveal_type(np.random.RandomState(pcg64)) # E: RandomState +reveal_type(np.random.RandomState(0)) # E: RandomState +reveal_type(np.random.RandomState([0, 1, 2])) # E: RandomState +reveal_type(random_st.__str__()) # E: str +reveal_type(random_st.__repr__()) # E: str +random_st_state = random_st.__getstate__() +reveal_type(random_st_state) # E: builtins.dict[builtins.str, Any] +reveal_type(random_st.__setstate__(random_st_state)) # E: None +reveal_type(random_st.seed()) # E: None +reveal_type(random_st.seed(1)) # E: None +reveal_type(random_st.seed([0, 1])) # E: None +random_st_get_state = random_st.get_state() +reveal_type(random_st_state) # E: builtins.dict[builtins.str, Any] +random_st_get_state_legacy = random_st.get_state(legacy=True) +reveal_type(random_st_get_state_legacy) # E: Union[builtins.dict[builtins.str, Any], Tuple[builtins.str, ndarray[Any, dtype[unsignedinteger[typing._32Bit]]], builtins.int, builtins.int, builtins.float]] +reveal_type(random_st.set_state(random_st_get_state)) # E: None + +reveal_type(random_st.rand()) # E: float +reveal_type(random_st.rand(1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.rand(1, 2)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.randn()) # E: float +reveal_type(random_st.randn(1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.randn(1, 2)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.random_sample()) # E: float +reveal_type(random_st.random_sample(1)) # E: ndarray[Any, dtype[floating[typing._64Bit]] +reveal_type(random_st.random_sample(size=(1, 2))) # E: ndarray[Any, dtype[floating[typing._64Bit]] + +reveal_type(random_st.tomaxint()) # E: int +reveal_type(random_st.tomaxint(1)) # E: ndarray[Any, dtype[{int_}]] +reveal_type(random_st.tomaxint((1,))) # E: ndarray[Any, dtype[{int_}]] diff --git a/numpy/typing/tests/data/reveal/rec.pyi b/numpy/typing/tests/data/reveal/rec.pyi new file mode 100644 index 000000000000..9921621f1fd9 --- /dev/null +++ b/numpy/typing/tests/data/reveal/rec.pyi @@ -0,0 +1,127 @@ +import io +from typing import Any, List + +import numpy as np +import numpy.typing as npt + +AR_i8: npt.NDArray[np.int64] +REC_AR_V: np.recarray[Any, np.dtype[np.record]] +AR_LIST: List[npt.NDArray[np.int64]] + +format_parser: np.format_parser +record: np.record +file_obj: io.BufferedIOBase + +reveal_type(np.format_parser( # E: format_parser + formats=[np.float64, np.int64, np.bool_], + names=["f8", "i8", "?"], + titles=None, + aligned=True, +)) +reveal_type(format_parser.dtype) # E: dtype[void] + +reveal_type(record.field_a) # E: Any +reveal_type(record.field_b) # E: Any +reveal_type(record["field_a"]) # E: Any +reveal_type(record["field_b"]) # E: Any +reveal_type(record.pprint()) # E: str +record.field_c = 5 + +reveal_type(REC_AR_V.field(0)) # E: Any +reveal_type(REC_AR_V.field("field_a")) # E: Any +reveal_type(REC_AR_V.field(0, AR_i8)) # E: None +reveal_type(REC_AR_V.field("field_a", AR_i8)) # E: None +reveal_type(REC_AR_V["field_a"]) # E: Any +reveal_type(REC_AR_V.field_a) # E: Any + +reveal_type(np.recarray( # recarray[Any, dtype[record]] + shape=(10, 5), + formats=[np.float64, np.int64, np.bool_], + order="K", + byteorder="|", +)) +reveal_type(np.recarray( # recarray[Any, dtype[Any]] + shape=(10, 5), + dtype=[("f8", np.float64), ("i8", np.int64)], + strides=(5, 5), +)) + +reveal_type(np.rec.fromarrays( # recarray[Any, dtype[record]] + AR_LIST, +)) +reveal_type(np.rec.fromarrays( # recarray[Any, dtype[Any]] + AR_LIST, + dtype=np.int64, +)) +reveal_type(np.rec.fromarrays( # recarray[Any, dtype[Any]] + AR_LIST, + formats=[np.int64, np.float64], + names=["i8", "f8"] +)) + +reveal_type(np.rec.fromrecords( # recarray[Any, dtype[record]] + (1, 1.5), +)) +reveal_type(np.rec.fromrecords( # recarray[Any, dtype[record]] + [(1, 1.5)], + dtype=[("i8", np.int64), ("f8", np.float64)], +)) +reveal_type(np.rec.fromrecords( # recarray[Any, dtype[record]] + REC_AR_V, + formats=[np.int64, np.float64], + names=["i8", "f8"] +)) + +reveal_type(np.rec.fromstring( # recarray[Any, dtype[record]] + b"(1, 1.5)", + dtype=[("i8", np.int64), ("f8", np.float64)], +)) +reveal_type(np.rec.fromstring( # recarray[Any, dtype[record]] + REC_AR_V, + formats=[np.int64, np.float64], + names=["i8", "f8"] +)) + +reveal_type(np.rec.fromfile( # recarray[Any, dtype[Any]] + "test_file.txt", + dtype=[("i8", np.int64), ("f8", np.float64)], +)) +reveal_type(np.rec.fromfile( # recarray[Any, dtype[record]] + file_obj, + formats=[np.int64, np.float64], + names=["i8", "f8"] +)) + +reveal_type(np.rec.array( # recarray[Any, dtype[{int64}]] + AR_i8, +)) +reveal_type(np.rec.array( # recarray[Any, dtype[Any]] + [(1, 1.5)], + dtype=[("i8", np.int64), ("f8", np.float64)], +)) +reveal_type(np.rec.array( # recarray[Any, dtype[record]] + [(1, 1.5)], + formats=[np.int64, np.float64], + names=["i8", "f8"] +)) + +reveal_type(np.rec.array( # recarray[Any, dtype[Any]] + None, + dtype=np.float64, + shape=(10, 3), +)) +reveal_type(np.rec.array( # recarray[Any, dtype[Any]] + None, + formats=[np.int64, np.float64], + names=["i8", "f8"], + shape=(10, 3), +)) +reveal_type(np.rec.array( # recarray[Any, dtype[Any]] + file_obj, + dtype=np.float64, +)) +reveal_type(np.rec.array( # recarray[Any, dtype[Any]] + file_obj, + formats=[np.int64, np.float64], + names=["i8", "f8"], +)) diff --git a/numpy/typing/tests/data/reveal/scalars.pyi b/numpy/typing/tests/data/reveal/scalars.pyi new file mode 100644 index 000000000000..383e40ef08dc --- /dev/null +++ b/numpy/typing/tests/data/reveal/scalars.pyi @@ -0,0 +1,166 @@ +import sys +import numpy as np + +b: np.bool_ +u8: np.uint64 +i8: np.int64 +f8: np.float64 +c8: np.complex64 +c16: np.complex128 +m: np.timedelta64 +U: np.str_ +S: np.bytes_ +V: np.void + +reveal_type(c8.real) # E: {float32} +reveal_type(c8.imag) # E: {float32} + +reveal_type(c8.real.real) # E: {float32} +reveal_type(c8.real.imag) # E: {float32} + +reveal_type(c8.itemsize) # E: int +reveal_type(c8.shape) # E: Tuple[] +reveal_type(c8.strides) # E: Tuple[] + +reveal_type(c8.ndim) # E: Literal[0] +reveal_type(c8.size) # E: Literal[1] + +reveal_type(c8.squeeze()) # E: {complex64} +reveal_type(c8.byteswap()) # E: {complex64} +reveal_type(c8.transpose()) # E: {complex64} + +reveal_type(c8.dtype) # E: dtype[{complex64}] + +reveal_type(c8.real) # E: {float32} +reveal_type(c16.imag) # E: {float64} + +reveal_type(np.unicode_('foo')) # E: str_ +reveal_type(np.str0('foo')) # E: str_ + +reveal_type(V[0]) # E: Any +reveal_type(V["field1"]) # E: Any +reveal_type(V[["field1", "field2"]]) # E: void +V[0] = 5 + +# Aliases +reveal_type(np.unicode_()) # E: str_ +reveal_type(np.str0()) # E: str_ +reveal_type(np.bool8()) # E: bool_ +reveal_type(np.bytes0()) # E: bytes_ +reveal_type(np.string_()) # E: bytes_ +reveal_type(np.object0()) # E: object_ +reveal_type(np.void0(0)) # E: void + +reveal_type(np.byte()) # E: {byte} +reveal_type(np.short()) # E: {short} +reveal_type(np.intc()) # E: {intc} +reveal_type(np.intp()) # E: {intp} +reveal_type(np.int0()) # E: {intp} +reveal_type(np.int_()) # E: {int_} +reveal_type(np.longlong()) # E: {longlong} + +reveal_type(np.ubyte()) # E: {ubyte} +reveal_type(np.ushort()) # E: {ushort} +reveal_type(np.uintc()) # E: {uintc} +reveal_type(np.uintp()) # E: {uintp} +reveal_type(np.uint0()) # E: {uintp} +reveal_type(np.uint()) # E: {uint} +reveal_type(np.ulonglong()) # E: {ulonglong} + +reveal_type(np.half()) # E: {half} +reveal_type(np.single()) # E: {single} +reveal_type(np.double()) # E: {double} +reveal_type(np.float_()) # E: {double} +reveal_type(np.longdouble()) # E: {longdouble} +reveal_type(np.longfloat()) # E: {longdouble} + +reveal_type(np.csingle()) # E: {csingle} +reveal_type(np.singlecomplex()) # E: {csingle} +reveal_type(np.cdouble()) # E: {cdouble} +reveal_type(np.complex_()) # E: {cdouble} +reveal_type(np.cfloat()) # E: {cdouble} +reveal_type(np.clongdouble()) # E: {clongdouble} +reveal_type(np.clongfloat()) # E: {clongdouble} +reveal_type(np.longcomplex()) # E: {clongdouble} + +reveal_type(b.item()) # E: bool +reveal_type(i8.item()) # E: int +reveal_type(u8.item()) # E: int +reveal_type(f8.item()) # E: float +reveal_type(c16.item()) # E: complex +reveal_type(U.item()) # E: str +reveal_type(S.item()) # E: bytes + +reveal_type(b.tolist()) # E: bool +reveal_type(i8.tolist()) # E: int +reveal_type(u8.tolist()) # E: int +reveal_type(f8.tolist()) # E: float +reveal_type(c16.tolist()) # E: complex +reveal_type(U.tolist()) # E: str +reveal_type(S.tolist()) # E: bytes + +reveal_type(b.ravel()) # E: ndarray[Any, dtype[bool_]] +reveal_type(i8.ravel()) # E: ndarray[Any, dtype[{int64}]] +reveal_type(u8.ravel()) # E: ndarray[Any, dtype[{uint64}]] +reveal_type(f8.ravel()) # E: ndarray[Any, dtype[{float64}]] +reveal_type(c16.ravel()) # E: ndarray[Any, dtype[{complex128}]] +reveal_type(U.ravel()) # E: ndarray[Any, dtype[str_]] +reveal_type(S.ravel()) # E: ndarray[Any, dtype[bytes_]] + +reveal_type(b.flatten()) # E: ndarray[Any, dtype[bool_]] +reveal_type(i8.flatten()) # E: ndarray[Any, dtype[{int64}]] +reveal_type(u8.flatten()) # E: ndarray[Any, dtype[{uint64}]] +reveal_type(f8.flatten()) # E: ndarray[Any, dtype[{float64}]] +reveal_type(c16.flatten()) # E: ndarray[Any, dtype[{complex128}]] +reveal_type(U.flatten()) # E: ndarray[Any, dtype[str_]] +reveal_type(S.flatten()) # E: ndarray[Any, dtype[bytes_]] + +reveal_type(b.reshape(1)) # E: ndarray[Any, dtype[bool_]] +reveal_type(i8.reshape(1)) # E: ndarray[Any, dtype[{int64}]] +reveal_type(u8.reshape(1)) # E: ndarray[Any, dtype[{uint64}]] +reveal_type(f8.reshape(1)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(c16.reshape(1)) # E: ndarray[Any, dtype[{complex128}]] +reveal_type(U.reshape(1)) # E: ndarray[Any, dtype[str_]] +reveal_type(S.reshape(1)) # E: ndarray[Any, dtype[bytes_]] + +reveal_type(i8.astype(float)) # E: Any +reveal_type(i8.astype(np.float64)) # E: {float64} + +reveal_type(i8.view()) # E: {int64} +reveal_type(i8.view(np.float64)) # E: {float64} +reveal_type(i8.view(float)) # E: Any +reveal_type(i8.view(np.float64, np.ndarray)) # E: {float64} + +reveal_type(i8.getfield(float)) # E: Any +reveal_type(i8.getfield(np.float64)) # E: {float64} +reveal_type(i8.getfield(np.float64, 8)) # E: {float64} + +reveal_type(f8.as_integer_ratio()) # E: Tuple[builtins.int, builtins.int] +reveal_type(f8.is_integer()) # E: bool +reveal_type(f8.__trunc__()) # E: int +reveal_type(f8.__getformat__("float")) # E: str +reveal_type(f8.hex()) # E: str +reveal_type(np.float64.fromhex("0x0.0p+0")) # E: {float64} + +reveal_type(f8.__getnewargs__()) # E: Tuple[builtins.float] +reveal_type(c16.__getnewargs__()) # E: Tuple[builtins.float, builtins.float] + +reveal_type(i8.numerator) # E: {int64} +reveal_type(i8.denominator) # E: Literal[1] +reveal_type(u8.numerator) # E: {uint64} +reveal_type(u8.denominator) # E: Literal[1] +reveal_type(m.numerator) # E: timedelta64 +reveal_type(m.denominator) # E: Literal[1] + +reveal_type(round(i8)) # E: int +reveal_type(round(i8, 3)) # E: {int64} +reveal_type(round(u8)) # E: int +reveal_type(round(u8, 3)) # E: {uint64} +reveal_type(round(f8)) # E: int +reveal_type(round(f8, 3)) # E: {float64} + +if sys.version_info >= (3, 9): + reveal_type(f8.__ceil__()) # E: int + reveal_type(f8.__floor__()) # E: int + +reveal_type(i8.is_integer()) # E: Literal[True] diff --git a/numpy/typing/tests/data/reveal/shape_base.pyi b/numpy/typing/tests/data/reveal/shape_base.pyi new file mode 100644 index 000000000000..f13678c3af3b --- /dev/null +++ b/numpy/typing/tests/data/reveal/shape_base.pyi @@ -0,0 +1,57 @@ +import numpy as np +from numpy.typing import NDArray +from typing import Any, List + +i8: np.int64 +f8: np.float64 + +AR_b: NDArray[np.bool_] +AR_i8: NDArray[np.int64] +AR_f8: NDArray[np.float64] + +AR_LIKE_f8: List[float] + +reveal_type(np.take_along_axis(AR_f8, AR_i8, axis=1)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.take_along_axis(f8, AR_i8, axis=None)) # E: ndarray[Any, dtype[{float64}]] + +reveal_type(np.put_along_axis(AR_f8, AR_i8, "1.0", axis=1)) # E: None + +reveal_type(np.expand_dims(AR_i8, 2)) # E: ndarray[Any, dtype[{int64}]] +reveal_type(np.expand_dims(AR_LIKE_f8, 2)) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.column_stack([AR_i8])) # E: ndarray[Any, dtype[{int64}]] +reveal_type(np.column_stack([AR_LIKE_f8])) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.dstack([AR_i8])) # E: ndarray[Any, dtype[{int64}]] +reveal_type(np.dstack([AR_LIKE_f8])) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.row_stack([AR_i8])) # E: ndarray[Any, dtype[{int64}]] +reveal_type(np.row_stack([AR_LIKE_f8])) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.array_split(AR_i8, [3, 5, 6, 10])) # E: list[ndarray[Any, dtype[{int64}]]] +reveal_type(np.array_split(AR_LIKE_f8, [3, 5, 6, 10])) # E: list[ndarray[Any, dtype[Any]]] + +reveal_type(np.split(AR_i8, [3, 5, 6, 10])) # E: list[ndarray[Any, dtype[{int64}]]] +reveal_type(np.split(AR_LIKE_f8, [3, 5, 6, 10])) # E: list[ndarray[Any, dtype[Any]]] + +reveal_type(np.hsplit(AR_i8, [3, 5, 6, 10])) # E: list[ndarray[Any, dtype[{int64}]]] +reveal_type(np.hsplit(AR_LIKE_f8, [3, 5, 6, 10])) # E: list[ndarray[Any, dtype[Any]]] + +reveal_type(np.vsplit(AR_i8, [3, 5, 6, 10])) # E: list[ndarray[Any, dtype[{int64}]]] +reveal_type(np.vsplit(AR_LIKE_f8, [3, 5, 6, 10])) # E: list[ndarray[Any, dtype[Any]]] + +reveal_type(np.dsplit(AR_i8, [3, 5, 6, 10])) # E: list[ndarray[Any, dtype[{int64}]]] +reveal_type(np.dsplit(AR_LIKE_f8, [3, 5, 6, 10])) # E: list[ndarray[Any, dtype[Any]]] + +reveal_type(np.lib.shape_base.get_array_prepare(AR_i8)) # E: lib.shape_base._ArrayPrepare +reveal_type(np.lib.shape_base.get_array_prepare(AR_i8, 1)) # E: Union[None, lib.shape_base._ArrayPrepare] + +reveal_type(np.get_array_wrap(AR_i8)) # E: lib.shape_base._ArrayWrap +reveal_type(np.get_array_wrap(AR_i8, 1)) # E: Union[None, lib.shape_base._ArrayWrap] + +reveal_type(np.kron(AR_b, AR_b)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.kron(AR_b, AR_i8)) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(np.kron(AR_f8, AR_f8)) # E: ndarray[Any, dtype[floating[Any]]] + +reveal_type(np.tile(AR_i8, 5)) # E: ndarray[Any, dtype[{int64}]] +reveal_type(np.tile(AR_LIKE_f8, [2, 2])) # E: ndarray[Any, dtype[Any]] diff --git a/numpy/typing/tests/data/reveal/stride_tricks.pyi b/numpy/typing/tests/data/reveal/stride_tricks.pyi new file mode 100644 index 000000000000..0d6dcd388e68 --- /dev/null +++ b/numpy/typing/tests/data/reveal/stride_tricks.pyi @@ -0,0 +1,28 @@ +from typing import List, Dict, Any +import numpy as np +import numpy.typing as npt + +AR_f8: npt.NDArray[np.float64] +AR_LIKE_f: List[float] +interface_dict: Dict[str, Any] + +reveal_type(np.lib.stride_tricks.DummyArray(interface_dict)) # E: lib.stride_tricks.DummyArray + +reveal_type(np.lib.stride_tricks.as_strided(AR_f8)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.lib.stride_tricks.as_strided(AR_LIKE_f)) # E: ndarray[Any, dtype[Any]] +reveal_type(np.lib.stride_tricks.as_strided(AR_f8, strides=(1, 5))) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.lib.stride_tricks.as_strided(AR_f8, shape=[9, 20])) # E: ndarray[Any, dtype[{float64}]] + +reveal_type(np.lib.stride_tricks.sliding_window_view(AR_f8, 5)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.lib.stride_tricks.sliding_window_view(AR_LIKE_f, (1, 5))) # E: ndarray[Any, dtype[Any]] +reveal_type(np.lib.stride_tricks.sliding_window_view(AR_f8, [9], axis=1)) # E: ndarray[Any, dtype[{float64}]] + +reveal_type(np.broadcast_to(AR_f8, 5)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.broadcast_to(AR_LIKE_f, (1, 5))) # E: ndarray[Any, dtype[Any]] +reveal_type(np.broadcast_to(AR_f8, [4, 6], subok=True)) # E: ndarray[Any, dtype[{float64}]] + +reveal_type(np.broadcast_shapes((1, 2), [3, 1], (3, 2))) # E: tuple[builtins.int] +reveal_type(np.broadcast_shapes((6, 7), (5, 6, 1), 7, (5, 1, 7))) # E: tuple[builtins.int] + +reveal_type(np.broadcast_arrays(AR_f8, AR_f8)) # E: list[ndarray[Any, dtype[Any]]] +reveal_type(np.broadcast_arrays(AR_f8, AR_LIKE_f)) # E: list[ndarray[Any, dtype[Any]]] diff --git a/numpy/typing/tests/data/reveal/testing.pyi b/numpy/typing/tests/data/reveal/testing.pyi new file mode 100644 index 000000000000..9813dc723fa3 --- /dev/null +++ b/numpy/typing/tests/data/reveal/testing.pyi @@ -0,0 +1,173 @@ +from __future__ import annotations + +import re +import sys +from typing import Any, Callable, TypeVar +from pathlib import Path + +import numpy as np +import numpy.typing as npt + +AR_f8: npt.NDArray[np.float64] +AR_i8: npt.NDArray[np.int64] + +bool_obj: bool +suppress_obj: np.testing.suppress_warnings +FT = TypeVar("FT", bound=Callable[..., Any]) + +def func() -> int: ... + +def func2( + x: npt.NDArray[np.number[Any]], + y: npt.NDArray[np.number[Any]], +) -> npt.NDArray[np.bool_]: ... + +reveal_type(np.testing.KnownFailureException()) # E: KnownFailureException +reveal_type(np.testing.IgnoreException()) # E: IgnoreException + +reveal_type(np.testing.clear_and_catch_warnings(modules=[np.testing])) # E: _clear_and_catch_warnings_without_records +reveal_type(np.testing.clear_and_catch_warnings(True)) # E: _clear_and_catch_warnings_with_records +reveal_type(np.testing.clear_and_catch_warnings(False)) # E: _clear_and_catch_warnings_without_records +reveal_type(np.testing.clear_and_catch_warnings(bool_obj)) # E: clear_and_catch_warnings +reveal_type(np.testing.clear_and_catch_warnings.class_modules) # E: tuple[types.ModuleType] +reveal_type(np.testing.clear_and_catch_warnings.modules) # E: set[types.ModuleType] + +with np.testing.clear_and_catch_warnings(True) as c1: + reveal_type(c1) # E: builtins.list[warnings.WarningMessage] +with np.testing.clear_and_catch_warnings() as c2: + reveal_type(c2) # E: None + +reveal_type(np.testing.suppress_warnings("once")) # E: suppress_warnings +reveal_type(np.testing.suppress_warnings()(func)) # E: def () -> builtins.int +reveal_type(suppress_obj.filter(RuntimeWarning)) # E: None +reveal_type(suppress_obj.record(RuntimeWarning)) # E: list[warnings.WarningMessage] +with suppress_obj as c3: + reveal_type(c3) # E: suppress_warnings + +reveal_type(np.testing.verbose) # E: int +reveal_type(np.testing.IS_PYPY) # E: bool +reveal_type(np.testing.HAS_REFCOUNT) # E: bool +reveal_type(np.testing.HAS_LAPACK64) # E: bool + +reveal_type(np.testing.assert_(1, msg="test")) # E: None +reveal_type(np.testing.assert_(2, msg=lambda: "test")) # E: None + +if sys.platform == "win32" or sys.platform == "cygwin": + reveal_type(np.testing.memusage()) # E: builtins.int +elif sys.platform == "linux": + reveal_type(np.testing.memusage()) # E: Union[None, builtins.int] +else: + reveal_type(np.testing.memusage()) # E: <nothing> + +reveal_type(np.testing.jiffies()) # E: builtins.int + +reveal_type(np.testing.build_err_msg([0, 1, 2], "test")) # E: str +reveal_type(np.testing.build_err_msg(range(2), "test", header="header")) # E: str +reveal_type(np.testing.build_err_msg(np.arange(9).reshape(3, 3), "test", verbose=False)) # E: str +reveal_type(np.testing.build_err_msg("abc", "test", names=["x", "y"])) # E: str +reveal_type(np.testing.build_err_msg([1.0, 2.0], "test", precision=5)) # E: str + +reveal_type(np.testing.assert_equal({1}, {1})) # E: None +reveal_type(np.testing.assert_equal([1, 2, 3], [1, 2, 3], err_msg="fail")) # E: None +reveal_type(np.testing.assert_equal(1, 1.0, verbose=True)) # E: None + +reveal_type(np.testing.print_assert_equal('Test XYZ of func xyz', [0, 1], [0, 1])) # E: None + +reveal_type(np.testing.assert_almost_equal(1.0, 1.1)) # E: None +reveal_type(np.testing.assert_almost_equal([1, 2, 3], [1, 2, 3], err_msg="fail")) # E: None +reveal_type(np.testing.assert_almost_equal(1, 1.0, verbose=True)) # E: None +reveal_type(np.testing.assert_almost_equal(1, 1.0001, decimal=2)) # E: None + +reveal_type(np.testing.assert_approx_equal(1.0, 1.1)) # E: None +reveal_type(np.testing.assert_approx_equal("1", "2", err_msg="fail")) # E: None +reveal_type(np.testing.assert_approx_equal(1, 1.0, verbose=True)) # E: None +reveal_type(np.testing.assert_approx_equal(1, 1.0001, significant=2)) # E: None + +reveal_type(np.testing.assert_array_compare(func2, AR_i8, AR_f8, err_msg="test")) # E: None +reveal_type(np.testing.assert_array_compare(func2, AR_i8, AR_f8, verbose=True)) # E: None +reveal_type(np.testing.assert_array_compare(func2, AR_i8, AR_f8, header="header")) # E: None +reveal_type(np.testing.assert_array_compare(func2, AR_i8, AR_f8, precision=np.int64())) # E: None +reveal_type(np.testing.assert_array_compare(func2, AR_i8, AR_f8, equal_nan=False)) # E: None +reveal_type(np.testing.assert_array_compare(func2, AR_i8, AR_f8, equal_inf=True)) # E: None + +reveal_type(np.testing.assert_array_equal(AR_i8, AR_f8)) # E: None +reveal_type(np.testing.assert_array_equal(AR_i8, AR_f8, err_msg="test")) # E: None +reveal_type(np.testing.assert_array_equal(AR_i8, AR_f8, verbose=True)) # E: None + +reveal_type(np.testing.assert_array_almost_equal(AR_i8, AR_f8)) # E: None +reveal_type(np.testing.assert_array_almost_equal(AR_i8, AR_f8, err_msg="test")) # E: None +reveal_type(np.testing.assert_array_almost_equal(AR_i8, AR_f8, verbose=True)) # E: None +reveal_type(np.testing.assert_array_almost_equal(AR_i8, AR_f8, decimal=1)) # E: None + +reveal_type(np.testing.assert_array_less(AR_i8, AR_f8)) # E: None +reveal_type(np.testing.assert_array_less(AR_i8, AR_f8, err_msg="test")) # E: None +reveal_type(np.testing.assert_array_less(AR_i8, AR_f8, verbose=True)) # E: None + +reveal_type(np.testing.runstring("1 + 1", {})) # E: Any +reveal_type(np.testing.runstring("int64() + 1", {"int64": np.int64})) # E: Any + +reveal_type(np.testing.assert_string_equal("1", "1")) # E: None + +reveal_type(np.testing.rundocs()) # E: None +reveal_type(np.testing.rundocs("test.py")) # E: None +reveal_type(np.testing.rundocs(Path("test.py"), raise_on_error=True)) # E: None + +@np.testing.raises(RuntimeError, RuntimeWarning) +def func3(a: int) -> bool: ... + +reveal_type(func3) # E: def (a: builtins.int) -> builtins.bool + +reveal_type(np.testing.assert_raises(RuntimeWarning)) # E: _AssertRaisesContext[builtins.RuntimeWarning] +reveal_type(np.testing.assert_raises(RuntimeWarning, func3, 5)) # E: None + +reveal_type(np.testing.assert_raises_regex(RuntimeWarning, r"test")) # E: _AssertRaisesContext[builtins.RuntimeWarning] +reveal_type(np.testing.assert_raises_regex(RuntimeWarning, b"test", func3, 5)) # E: None +reveal_type(np.testing.assert_raises_regex(RuntimeWarning, re.compile(b"test"), func3, 5)) # E: None + +class Test: ... + +def decorate(a: FT) -> FT: + return a + +reveal_type(np.testing.decorate_methods(Test, decorate)) # E: None +reveal_type(np.testing.decorate_methods(Test, decorate, None)) # E: None +reveal_type(np.testing.decorate_methods(Test, decorate, "test")) # E: None +reveal_type(np.testing.decorate_methods(Test, decorate, b"test")) # E: None +reveal_type(np.testing.decorate_methods(Test, decorate, re.compile("test"))) # E: None + +reveal_type(np.testing.measure("for i in range(1000): np.sqrt(i**2)")) # E: float +reveal_type(np.testing.measure(b"for i in range(1000): np.sqrt(i**2)", times=5)) # E: float + +reveal_type(np.testing.assert_allclose(AR_i8, AR_f8)) # E: None +reveal_type(np.testing.assert_allclose(AR_i8, AR_f8, rtol=0.005)) # E: None +reveal_type(np.testing.assert_allclose(AR_i8, AR_f8, atol=1)) # E: None +reveal_type(np.testing.assert_allclose(AR_i8, AR_f8, equal_nan=True)) # E: None +reveal_type(np.testing.assert_allclose(AR_i8, AR_f8, err_msg="err")) # E: None +reveal_type(np.testing.assert_allclose(AR_i8, AR_f8, verbose=False)) # E: None + +reveal_type(np.testing.assert_array_almost_equal_nulp(AR_i8, AR_f8, nulp=2)) # E: None + +reveal_type(np.testing.assert_array_max_ulp(AR_i8, AR_f8, maxulp=2)) # E: ndarray[Any, dtype[Any]] +reveal_type(np.testing.assert_array_max_ulp(AR_i8, AR_f8, dtype=np.float32)) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.testing.assert_warns(RuntimeWarning)) # E: _GeneratorContextManager[None] +reveal_type(np.testing.assert_warns(RuntimeWarning, func3, 5)) # E: bool + +reveal_type(np.testing.assert_no_warnings()) # E: _GeneratorContextManager[None] +reveal_type(np.testing.assert_no_warnings(func3, 5)) # E: bool + +reveal_type(np.testing.tempdir("test_dir")) # E: _GeneratorContextManager[builtins.str] +reveal_type(np.testing.tempdir(prefix=b"test")) # E: _GeneratorContextManager[builtins.bytes] +reveal_type(np.testing.tempdir("test_dir", dir=Path("here"))) # E: _GeneratorContextManager[builtins.str] + +reveal_type(np.testing.temppath("test_dir", text=True)) # E: _GeneratorContextManager[builtins.str] +reveal_type(np.testing.temppath(prefix=b"test")) # E: _GeneratorContextManager[builtins.bytes] +reveal_type(np.testing.temppath("test_dir", dir=Path("here"))) # E: _GeneratorContextManager[builtins.str] + +reveal_type(np.testing.assert_no_gc_cycles()) # E: _GeneratorContextManager[None] +reveal_type(np.testing.assert_no_gc_cycles(func3, 5)) # E: None + +reveal_type(np.testing.break_cycles()) # E: None + +reveal_type(np.testing.TestCase()) # E: unittest.case.TestCase +reveal_type(np.testing.run_module_suite(file_to_run="numpy/tests/test_matlib.py")) # E: None diff --git a/numpy/typing/tests/data/reveal/twodim_base.pyi b/numpy/typing/tests/data/reveal/twodim_base.pyi new file mode 100644 index 000000000000..0318c3cf18a5 --- /dev/null +++ b/numpy/typing/tests/data/reveal/twodim_base.pyi @@ -0,0 +1,72 @@ +from typing import Any, List, TypeVar + +import numpy as np +import numpy.typing as npt + +_SCT = TypeVar("_SCT", bound=np.generic) + + +def func1(ar: npt.NDArray[_SCT], a: int) -> npt.NDArray[_SCT]: + pass + + +def func2(ar: npt.NDArray[np.number[Any]], a: str) -> npt.NDArray[np.float64]: + pass + + +AR_b: npt.NDArray[np.bool_] +AR_u: npt.NDArray[np.uint64] +AR_i: npt.NDArray[np.int64] +AR_f: npt.NDArray[np.float64] +AR_c: npt.NDArray[np.complex128] +AR_O: npt.NDArray[np.object_] + +AR_LIKE_b: List[bool] + +reveal_type(np.fliplr(AR_b)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.fliplr(AR_LIKE_b)) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.flipud(AR_b)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.flipud(AR_LIKE_b)) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.eye(10)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.eye(10, M=20, dtype=np.int64)) # E: ndarray[Any, dtype[{int64}]] +reveal_type(np.eye(10, k=2, dtype=int)) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.diag(AR_b)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.diag(AR_LIKE_b, k=0)) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.diagflat(AR_b)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.diagflat(AR_LIKE_b, k=0)) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.tri(10)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.tri(10, M=20, dtype=np.int64)) # E: ndarray[Any, dtype[{int64}]] +reveal_type(np.tri(10, k=2, dtype=int)) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.tril(AR_b)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.tril(AR_LIKE_b, k=0)) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.triu(AR_b)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.triu(AR_LIKE_b, k=0)) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.vander(AR_b)) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(np.vander(AR_u)) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(np.vander(AR_i, N=2)) # E: ndarray[Any, dtype[signedinteger[Any]]] +reveal_type(np.vander(AR_f, increasing=True)) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.vander(AR_c)) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] +reveal_type(np.vander(AR_O)) # E: ndarray[Any, dtype[object_]] + +reveal_type(np.histogram2d(AR_i, AR_b)) # E: Tuple[ndarray[Any, dtype[{float64}]], ndarray[Any, dtype[floating[Any]]], ndarray[Any, dtype[floating[Any]]]] +reveal_type(np.histogram2d(AR_f, AR_f)) # E: Tuple[ndarray[Any, dtype[{float64}]], ndarray[Any, dtype[floating[Any]]], ndarray[Any, dtype[floating[Any]]]] +reveal_type(np.histogram2d(AR_f, AR_c, weights=AR_LIKE_b)) # E: Tuple[ndarray[Any, dtype[{float64}]], ndarray[Any, dtype[complexfloating[Any, Any]]], ndarray[Any, dtype[complexfloating[Any, Any]]]] + +reveal_type(np.mask_indices(10, func1)) # E: Tuple[ndarray[Any, dtype[{intp}]], ndarray[Any, dtype[{intp}]]] +reveal_type(np.mask_indices(8, func2, "0")) # E: Tuple[ndarray[Any, dtype[{intp}]], ndarray[Any, dtype[{intp}]]] + +reveal_type(np.tril_indices(10)) # E: Tuple[ndarray[Any, dtype[{int_}]], ndarray[Any, dtype[{int_}]]] + +reveal_type(np.tril_indices_from(AR_b)) # E: Tuple[ndarray[Any, dtype[{int_}]], ndarray[Any, dtype[{int_}]]] + +reveal_type(np.triu_indices(10)) # E: Tuple[ndarray[Any, dtype[{int_}]], ndarray[Any, dtype[{int_}]]] + +reveal_type(np.triu_indices_from(AR_b)) # E: Tuple[ndarray[Any, dtype[{int_}]], ndarray[Any, dtype[{int_}]]] diff --git a/numpy/typing/tests/data/reveal/type_check.pyi b/numpy/typing/tests/data/reveal/type_check.pyi new file mode 100644 index 000000000000..13d41d844128 --- /dev/null +++ b/numpy/typing/tests/data/reveal/type_check.pyi @@ -0,0 +1,73 @@ +from typing import List +import numpy as np +import numpy.typing as npt + +f8: np.float64 +f: float + +# NOTE: Avoid importing the platform specific `np.float128` type +AR_i8: npt.NDArray[np.int64] +AR_i4: npt.NDArray[np.int32] +AR_f2: npt.NDArray[np.float16] +AR_f8: npt.NDArray[np.float64] +AR_f16: npt.NDArray[np.floating[npt._128Bit]] +AR_c8: npt.NDArray[np.complex64] +AR_c16: npt.NDArray[np.complex128] + +AR_LIKE_f: List[float] + +class RealObj: + real: slice + +class ImagObj: + imag: slice + +reveal_type(np.mintypecode(["f8"], typeset="qfQF")) + +reveal_type(np.asfarray(AR_f8)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.asfarray(AR_LIKE_f)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.asfarray(AR_f8, dtype="c16")) # E: ndarray[Any, dtype[complexfloating[Any, Any]]] +reveal_type(np.asfarray(AR_f8, dtype="i8")) # E: ndarray[Any, dtype[floating[Any]]] + +reveal_type(np.real(RealObj())) # E: slice +reveal_type(np.real(AR_f8)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.real(AR_c16)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.real(AR_LIKE_f)) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.imag(ImagObj())) # E: slice +reveal_type(np.imag(AR_f8)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.imag(AR_c16)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.imag(AR_LIKE_f)) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.iscomplex(f8)) # E: bool_ +reveal_type(np.iscomplex(AR_f8)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.iscomplex(AR_LIKE_f)) # E: ndarray[Any, dtype[bool_]] + +reveal_type(np.isreal(f8)) # E: bool_ +reveal_type(np.isreal(AR_f8)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.isreal(AR_LIKE_f)) # E: ndarray[Any, dtype[bool_]] + +reveal_type(np.iscomplexobj(f8)) # E: bool +reveal_type(np.isrealobj(f8)) # E: bool + +reveal_type(np.nan_to_num(f8)) # E: {float64} +reveal_type(np.nan_to_num(f, copy=True)) # E: Any +reveal_type(np.nan_to_num(AR_f8, nan=1.5)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.nan_to_num(AR_LIKE_f, posinf=9999)) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.real_if_close(AR_f8)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.real_if_close(AR_c16)) # E: Union[ndarray[Any, dtype[{float64}]], ndarray[Any, dtype[{complex128}]]] +reveal_type(np.real_if_close(AR_c8)) # E: Union[ndarray[Any, dtype[{float32}]], ndarray[Any, dtype[{complex64}]]] +reveal_type(np.real_if_close(AR_LIKE_f)) # E: ndarray[Any, dtype[Any]] + +reveal_type(np.typename("h")) # E: Literal['short'] +reveal_type(np.typename("B")) # E: Literal['unsigned char'] +reveal_type(np.typename("V")) # E: Literal['void'] +reveal_type(np.typename("S1")) # E: Literal['character'] + +reveal_type(np.common_type(AR_i4)) # E: Type[{float64}] +reveal_type(np.common_type(AR_f2)) # E: Type[{float16}] +reveal_type(np.common_type(AR_f2, AR_i4)) # E: Type[{float64}] +reveal_type(np.common_type(AR_f16, AR_i4)) # E: Type[{float128}] +reveal_type(np.common_type(AR_c8, AR_f2)) # E: Type[{complex64}] +reveal_type(np.common_type(AR_f2, AR_c8, AR_i4)) # E: Type[{complex128}] diff --git a/numpy/typing/tests/data/reveal/ufunc_config.pyi b/numpy/typing/tests/data/reveal/ufunc_config.pyi new file mode 100644 index 000000000000..2c6fadf92360 --- /dev/null +++ b/numpy/typing/tests/data/reveal/ufunc_config.pyi @@ -0,0 +1,25 @@ +"""Typing tests for `core._ufunc_config`.""" + +import numpy as np + +def func(a: str, b: int) -> None: ... + +class Write: + def write(self, value: str) -> None: ... + +reveal_type(np.seterr(all=None)) # E: TypedDict('core._ufunc_config._ErrDict' +reveal_type(np.seterr(divide="ignore")) # E: TypedDict('core._ufunc_config._ErrDict' +reveal_type(np.seterr(over="warn")) # E: TypedDict('core._ufunc_config._ErrDict' +reveal_type(np.seterr(under="call")) # E: TypedDict('core._ufunc_config._ErrDict' +reveal_type(np.seterr(invalid="raise")) # E: TypedDict('core._ufunc_config._ErrDict' +reveal_type(np.geterr()) # E: TypedDict('core._ufunc_config._ErrDict' + +reveal_type(np.setbufsize(4096)) # E: int +reveal_type(np.getbufsize()) # E: int + +reveal_type(np.seterrcall(func)) # E: Union[None, def (builtins.str, builtins.int) -> Any, _SupportsWrite[builtins.str]] +reveal_type(np.seterrcall(Write())) # E: Union[None, def (builtins.str, builtins.int) -> Any, _SupportsWrite[builtins.str]] +reveal_type(np.geterrcall()) # E: Union[None, def (builtins.str, builtins.int) -> Any, _SupportsWrite[builtins.str]] + +reveal_type(np.errstate(call=func, all="call")) # E: errstate[def (a: builtins.str, b: builtins.int)] +reveal_type(np.errstate(call=Write(), divide="log", over="log")) # E: errstate[ufunc_config.Write] diff --git a/numpy/typing/tests/data/reveal/ufunclike.pyi b/numpy/typing/tests/data/reveal/ufunclike.pyi new file mode 100644 index 000000000000..2d67c923fe8d --- /dev/null +++ b/numpy/typing/tests/data/reveal/ufunclike.pyi @@ -0,0 +1,29 @@ +from typing import List, Any +import numpy as np + +AR_LIKE_b: List[bool] +AR_LIKE_u: List[np.uint32] +AR_LIKE_i: List[int] +AR_LIKE_f: List[float] +AR_LIKE_O: List[np.object_] + +AR_U: np.ndarray[Any, np.dtype[np.str_]] + +reveal_type(np.fix(AR_LIKE_b)) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.fix(AR_LIKE_u)) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.fix(AR_LIKE_i)) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.fix(AR_LIKE_f)) # E: ndarray[Any, dtype[floating[Any]]] +reveal_type(np.fix(AR_LIKE_O)) # E: Any +reveal_type(np.fix(AR_LIKE_f, out=AR_U)) # E: ndarray[Any, dtype[str_]] + +reveal_type(np.isposinf(AR_LIKE_b)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.isposinf(AR_LIKE_u)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.isposinf(AR_LIKE_i)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.isposinf(AR_LIKE_f)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.isposinf(AR_LIKE_f, out=AR_U)) # E: ndarray[Any, dtype[str_]] + +reveal_type(np.isneginf(AR_LIKE_b)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.isneginf(AR_LIKE_u)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.isneginf(AR_LIKE_i)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.isneginf(AR_LIKE_f)) # E: ndarray[Any, dtype[bool_]] +reveal_type(np.isneginf(AR_LIKE_f, out=AR_U)) # E: ndarray[Any, dtype[str_]] diff --git a/numpy/typing/tests/data/reveal/ufuncs.pyi b/numpy/typing/tests/data/reveal/ufuncs.pyi new file mode 100644 index 000000000000..3bf83c8207bf --- /dev/null +++ b/numpy/typing/tests/data/reveal/ufuncs.pyi @@ -0,0 +1,68 @@ +import numpy as np +import numpy.typing as npt + +f8: np.float64 +AR_f8: npt.NDArray[np.float64] +AR_i8: npt.NDArray[np.int64] + +reveal_type(np.absolute.__doc__) # E: str +reveal_type(np.absolute.types) # E: builtins.list[builtins.str] + +reveal_type(np.absolute.__name__) # E: Literal['absolute'] +reveal_type(np.absolute.ntypes) # E: Literal[20] +reveal_type(np.absolute.identity) # E: None +reveal_type(np.absolute.nin) # E: Literal[1] +reveal_type(np.absolute.nin) # E: Literal[1] +reveal_type(np.absolute.nout) # E: Literal[1] +reveal_type(np.absolute.nargs) # E: Literal[2] +reveal_type(np.absolute.signature) # E: None +reveal_type(np.absolute(f8)) # E: Any +reveal_type(np.absolute(AR_f8)) # E: ndarray +reveal_type(np.absolute.at(AR_f8, AR_i8)) # E: None + +reveal_type(np.add.__name__) # E: Literal['add'] +reveal_type(np.add.ntypes) # E: Literal[22] +reveal_type(np.add.identity) # E: Literal[0] +reveal_type(np.add.nin) # E: Literal[2] +reveal_type(np.add.nout) # E: Literal[1] +reveal_type(np.add.nargs) # E: Literal[3] +reveal_type(np.add.signature) # E: None +reveal_type(np.add(f8, f8)) # E: Any +reveal_type(np.add(AR_f8, f8)) # E: ndarray +reveal_type(np.add.at(AR_f8, AR_i8, f8)) # E: None +reveal_type(np.add.reduce(AR_f8, axis=0)) # E: Any +reveal_type(np.add.accumulate(AR_f8)) # E: ndarray +reveal_type(np.add.reduceat(AR_f8, AR_i8)) # E: ndarray +reveal_type(np.add.outer(f8, f8)) # E: Any +reveal_type(np.add.outer(AR_f8, f8)) # E: ndarray + +reveal_type(np.frexp.__name__) # E: Literal['frexp'] +reveal_type(np.frexp.ntypes) # E: Literal[4] +reveal_type(np.frexp.identity) # E: None +reveal_type(np.frexp.nin) # E: Literal[1] +reveal_type(np.frexp.nout) # E: Literal[2] +reveal_type(np.frexp.nargs) # E: Literal[3] +reveal_type(np.frexp.signature) # E: None +reveal_type(np.frexp(f8)) # E: Tuple[Any, Any] +reveal_type(np.frexp(AR_f8)) # E: Tuple[ndarray[Any, dtype[Any]], ndarray[Any, dtype[Any]]] + +reveal_type(np.divmod.__name__) # E: Literal['divmod'] +reveal_type(np.divmod.ntypes) # E: Literal[15] +reveal_type(np.divmod.identity) # E: None +reveal_type(np.divmod.nin) # E: Literal[2] +reveal_type(np.divmod.nout) # E: Literal[2] +reveal_type(np.divmod.nargs) # E: Literal[4] +reveal_type(np.divmod.signature) # E: None +reveal_type(np.divmod(f8, f8)) # E: Tuple[Any, Any] +reveal_type(np.divmod(AR_f8, f8)) # E: Tuple[ndarray[Any, dtype[Any]], ndarray[Any, dtype[Any]]] + +reveal_type(np.matmul.__name__) # E: Literal['matmul'] +reveal_type(np.matmul.ntypes) # E: Literal[19] +reveal_type(np.matmul.identity) # E: None +reveal_type(np.matmul.nin) # E: Literal[2] +reveal_type(np.matmul.nout) # E: Literal[1] +reveal_type(np.matmul.nargs) # E: Literal[3] +reveal_type(np.matmul.signature) # E: Literal['(n?,k),(k,m?)->(n?,m?)'] +reveal_type(np.matmul.identity) # E: None +reveal_type(np.matmul(AR_f8, AR_f8)) # E: Any +reveal_type(np.matmul(AR_f8, AR_f8, axes=[(0, 1), (0, 1), (0, 1)])) # E: Any diff --git a/numpy/typing/tests/data/reveal/version.pyi b/numpy/typing/tests/data/reveal/version.pyi new file mode 100644 index 000000000000..e53837647655 --- /dev/null +++ b/numpy/typing/tests/data/reveal/version.pyi @@ -0,0 +1,8 @@ +import numpy.version + +reveal_type(numpy.version.version) # E: str +reveal_type(numpy.version.__version__) # E: str +reveal_type(numpy.version.full_version) # E: str +reveal_type(numpy.version.git_revision) # E: str +reveal_type(numpy.version.release) # E: bool +reveal_type(numpy.version.short_version) # E: str diff --git a/numpy/typing/tests/data/reveal/warnings_and_errors.pyi b/numpy/typing/tests/data/reveal/warnings_and_errors.pyi new file mode 100644 index 000000000000..d5c50448ae6c --- /dev/null +++ b/numpy/typing/tests/data/reveal/warnings_and_errors.pyi @@ -0,0 +1,11 @@ +from typing import Type + +import numpy as np + +reveal_type(np.ModuleDeprecationWarning()) # E: ModuleDeprecationWarning +reveal_type(np.VisibleDeprecationWarning()) # E: VisibleDeprecationWarning +reveal_type(np.ComplexWarning()) # E: ComplexWarning +reveal_type(np.RankWarning()) # E: RankWarning +reveal_type(np.TooHardError()) # E: TooHardError +reveal_type(np.AxisError("test")) # E: AxisError +reveal_type(np.AxisError(5, 1)) # E: AxisError diff --git a/numpy/typing/tests/test_generic_alias.py b/numpy/typing/tests/test_generic_alias.py new file mode 100644 index 000000000000..39343420bdc5 --- /dev/null +++ b/numpy/typing/tests/test_generic_alias.py @@ -0,0 +1,144 @@ +from __future__ import annotations + +import sys +import copy +import types +import pickle +import weakref +from typing import TypeVar, Any, Callable, Tuple, Type, Union + +import pytest +import numpy as np +from numpy.typing._generic_alias import _GenericAlias + +ScalarType = TypeVar("ScalarType", bound=np.generic, covariant=True) +T1 = TypeVar("T1") +T2 = TypeVar("T2") +DType = _GenericAlias(np.dtype, (ScalarType,)) +NDArray = _GenericAlias(np.ndarray, (Any, DType)) + +if sys.version_info >= (3, 9): + DType_ref = types.GenericAlias(np.dtype, (ScalarType,)) + NDArray_ref = types.GenericAlias(np.ndarray, (Any, DType_ref)) + FuncType = Callable[[Union[_GenericAlias, types.GenericAlias]], Any] +else: + DType_ref = Any + NDArray_ref = Any + FuncType = Callable[[_GenericAlias], Any] + +GETATTR_NAMES = sorted(set(dir(np.ndarray)) - _GenericAlias._ATTR_EXCEPTIONS) + +BUFFER = np.array([1], dtype=np.int64) +BUFFER.setflags(write=False) + +def _get_subclass_mro(base: type) -> Tuple[type, ...]: + class Subclass(base): # type: ignore[misc,valid-type] + pass + return Subclass.__mro__[1:] + + +class TestGenericAlias: + """Tests for `numpy.typing._generic_alias._GenericAlias`.""" + + @pytest.mark.parametrize("name,func", [ + ("__init__", lambda n: n), + ("__init__", lambda n: _GenericAlias(np.ndarray, Any)), + ("__init__", lambda n: _GenericAlias(np.ndarray, (Any,))), + ("__init__", lambda n: _GenericAlias(np.ndarray, (Any, Any))), + ("__init__", lambda n: _GenericAlias(np.ndarray, T1)), + ("__init__", lambda n: _GenericAlias(np.ndarray, (T1,))), + ("__init__", lambda n: _GenericAlias(np.ndarray, (T1, T2))), + ("__origin__", lambda n: n.__origin__), + ("__args__", lambda n: n.__args__), + ("__parameters__", lambda n: n.__parameters__), + ("__reduce__", lambda n: n.__reduce__()[1:]), + ("__reduce_ex__", lambda n: n.__reduce_ex__(1)[1:]), + ("__mro_entries__", lambda n: n.__mro_entries__([object])), + ("__hash__", lambda n: hash(n)), + ("__repr__", lambda n: repr(n)), + ("__getitem__", lambda n: n[np.float64]), + ("__getitem__", lambda n: n[ScalarType][np.float64]), + ("__getitem__", lambda n: n[Union[np.int64, ScalarType]][np.float64]), + ("__getitem__", lambda n: n[Union[T1, T2]][np.float32, np.float64]), + ("__eq__", lambda n: n == n), + ("__ne__", lambda n: n != np.ndarray), + ("__dir__", lambda n: dir(n)), + ("__call__", lambda n: n((1,), np.int64, BUFFER)), + ("__call__", lambda n: n(shape=(1,), dtype=np.int64, buffer=BUFFER)), + ("subclassing", lambda n: _get_subclass_mro(n)), + ("pickle", lambda n: n == pickle.loads(pickle.dumps(n))), + ]) + def test_pass(self, name: str, func: FuncType) -> None: + """Compare `types.GenericAlias` with its numpy-based backport. + + Checker whether ``func`` runs as intended and that both `GenericAlias` + and `_GenericAlias` return the same result. + + """ + value = func(NDArray) + + if sys.version_info >= (3, 9): + value_ref = func(NDArray_ref) + assert value == value_ref + + @pytest.mark.parametrize("name,func", [ + ("__copy__", lambda n: n == copy.copy(n)), + ("__deepcopy__", lambda n: n == copy.deepcopy(n)), + ]) + def test_copy(self, name: str, func: FuncType) -> None: + value = func(NDArray) + + # xref bpo-45167 + GE_398 = ( + sys.version_info[:2] == (3, 9) and sys.version_info >= (3, 9, 8) + ) + if GE_398 or sys.version_info >= (3, 10, 1): + value_ref = func(NDArray_ref) + assert value == value_ref + + def test_weakref(self) -> None: + """Test ``__weakref__``.""" + value = weakref.ref(NDArray)() + + if sys.version_info >= (3, 9, 1): # xref bpo-42332 + value_ref = weakref.ref(NDArray_ref)() + assert value == value_ref + + @pytest.mark.parametrize("name", GETATTR_NAMES) + def test_getattr(self, name: str) -> None: + """Test that `getattr` wraps around the underlying type, + aka ``__origin__``. + + """ + value = getattr(NDArray, name) + value_ref1 = getattr(np.ndarray, name) + + if sys.version_info >= (3, 9): + value_ref2 = getattr(NDArray_ref, name) + assert value == value_ref1 == value_ref2 + else: + assert value == value_ref1 + + @pytest.mark.parametrize("name,exc_type,func", [ + ("__getitem__", TypeError, lambda n: n[()]), + ("__getitem__", TypeError, lambda n: n[Any, Any]), + ("__getitem__", TypeError, lambda n: n[Any][Any]), + ("isinstance", TypeError, lambda n: isinstance(np.array(1), n)), + ("issublass", TypeError, lambda n: issubclass(np.ndarray, n)), + ("setattr", AttributeError, lambda n: setattr(n, "__origin__", int)), + ("setattr", AttributeError, lambda n: setattr(n, "test", int)), + ("getattr", AttributeError, lambda n: getattr(n, "test")), + ]) + def test_raise( + self, + name: str, + exc_type: Type[BaseException], + func: FuncType, + ) -> None: + """Test operations that are supposed to raise.""" + with pytest.raises(exc_type): + func(NDArray) + + if sys.version_info >= (3, 9): + with pytest.raises(exc_type): + func(NDArray_ref) diff --git a/numpy/typing/tests/test_isfile.py b/numpy/typing/tests/test_isfile.py new file mode 100644 index 000000000000..a898b3e285b9 --- /dev/null +++ b/numpy/typing/tests/test_isfile.py @@ -0,0 +1,30 @@ +import os +from pathlib import Path + +import numpy as np +from numpy.testing import assert_ + +ROOT = Path(np.__file__).parents[0] +FILES = [ + ROOT / "py.typed", + ROOT / "__init__.pyi", + ROOT / "ctypeslib.pyi", + ROOT / "core" / "__init__.pyi", + ROOT / "distutils" / "__init__.pyi", + ROOT / "f2py" / "__init__.pyi", + ROOT / "fft" / "__init__.pyi", + ROOT / "lib" / "__init__.pyi", + ROOT / "linalg" / "__init__.pyi", + ROOT / "ma" / "__init__.pyi", + ROOT / "matrixlib" / "__init__.pyi", + ROOT / "polynomial" / "__init__.pyi", + ROOT / "random" / "__init__.pyi", + ROOT / "testing" / "__init__.pyi", +] + + +class TestIsFile: + def test_isfile(self): + """Test if all ``.pyi`` files are properly installed.""" + for file in FILES: + assert_(os.path.isfile(file)) diff --git a/numpy/typing/tests/test_runtime.py b/numpy/typing/tests/test_runtime.py new file mode 100644 index 000000000000..5b5df49dc571 --- /dev/null +++ b/numpy/typing/tests/test_runtime.py @@ -0,0 +1,82 @@ +"""Test the runtime usage of `numpy.typing`.""" + +from __future__ import annotations + +import sys +from typing import get_type_hints, Union, NamedTuple, get_args, get_origin + +import pytest +import numpy as np +import numpy.typing as npt + + +class TypeTup(NamedTuple): + typ: type + args: tuple[type, ...] + origin: None | type + + +if sys.version_info >= (3, 9): + NDArrayTup = TypeTup(npt.NDArray, npt.NDArray.__args__, np.ndarray) +else: + NDArrayTup = TypeTup(npt.NDArray, (), None) + +TYPES = { + "ArrayLike": TypeTup(npt.ArrayLike, npt.ArrayLike.__args__, Union), + "DTypeLike": TypeTup(npt.DTypeLike, npt.DTypeLike.__args__, Union), + "NBitBase": TypeTup(npt.NBitBase, (), None), + "NDArray": NDArrayTup, +} + + +@pytest.mark.parametrize("name,tup", TYPES.items(), ids=TYPES.keys()) +def test_get_args(name: type, tup: TypeTup) -> None: + """Test `typing.get_args`.""" + typ, ref = tup.typ, tup.args + out = get_args(typ) + assert out == ref + + +@pytest.mark.parametrize("name,tup", TYPES.items(), ids=TYPES.keys()) +def test_get_origin(name: type, tup: TypeTup) -> None: + """Test `typing.get_origin`.""" + typ, ref = tup.typ, tup.origin + out = get_origin(typ) + assert out == ref + + +@pytest.mark.parametrize("name,tup", TYPES.items(), ids=TYPES.keys()) +def test_get_type_hints(name: type, tup: TypeTup) -> None: + """Test `typing.get_type_hints`.""" + typ = tup.typ + + # Explicitly set `__annotations__` in order to circumvent the + # stringification performed by `from __future__ import annotations` + def func(a): pass + func.__annotations__ = {"a": typ, "return": None} + + out = get_type_hints(func) + ref = {"a": typ, "return": type(None)} + assert out == ref + + +@pytest.mark.parametrize("name,tup", TYPES.items(), ids=TYPES.keys()) +def test_get_type_hints_str(name: type, tup: TypeTup) -> None: + """Test `typing.get_type_hints` with string-representation of types.""" + typ_str, typ = f"npt.{name}", tup.typ + + # Explicitly set `__annotations__` in order to circumvent the + # stringification performed by `from __future__ import annotations` + def func(a): pass + func.__annotations__ = {"a": typ_str, "return": None} + + out = get_type_hints(func) + ref = {"a": typ, "return": type(None)} + assert out == ref + + +def test_keys() -> None: + """Test that ``TYPES.keys()`` and ``numpy.typing.__all__`` are synced.""" + keys = TYPES.keys() + ref = set(npt.__all__) + assert keys == ref diff --git a/numpy/typing/tests/test_typing.py b/numpy/typing/tests/test_typing.py new file mode 100644 index 000000000000..fe58a8f4c5e8 --- /dev/null +++ b/numpy/typing/tests/test_typing.py @@ -0,0 +1,455 @@ +from __future__ import annotations + +import importlib.util +import itertools +import os +import re +import shutil +from collections import defaultdict +from collections.abc import Iterator +from typing import IO, TYPE_CHECKING + +import pytest +import numpy as np +import numpy.typing as npt +from numpy.typing.mypy_plugin import ( + _PRECISION_DICT, + _EXTENDED_PRECISION_LIST, + _C_INTP, +) + +try: + from mypy import api +except ImportError: + NO_MYPY = True +else: + NO_MYPY = False + +if TYPE_CHECKING: + # We need this as annotation, but it's located in a private namespace. + # As a compromise, do *not* import it during runtime + from _pytest.mark.structures import ParameterSet + +DATA_DIR = os.path.join(os.path.dirname(__file__), "data") +PASS_DIR = os.path.join(DATA_DIR, "pass") +FAIL_DIR = os.path.join(DATA_DIR, "fail") +REVEAL_DIR = os.path.join(DATA_DIR, "reveal") +MISC_DIR = os.path.join(DATA_DIR, "misc") +MYPY_INI = os.path.join(DATA_DIR, "mypy.ini") +CACHE_DIR = os.path.join(DATA_DIR, ".mypy_cache") + +#: A dictionary with file names as keys and lists of the mypy stdout as values. +#: To-be populated by `run_mypy`. +OUTPUT_MYPY: dict[str, list[str]] = {} + + +def _key_func(key: str) -> str: + """Split at the first occurrence of the ``:`` character. + + Windows drive-letters (*e.g.* ``C:``) are ignored herein. + """ + drive, tail = os.path.splitdrive(key) + return os.path.join(drive, tail.split(":", 1)[0]) + + +def _strip_filename(msg: str) -> str: + """Strip the filename from a mypy message.""" + _, tail = os.path.splitdrive(msg) + return tail.split(":", 1)[-1] + + +def strip_func(match: re.Match[str]) -> str: + """`re.sub` helper function for stripping module names.""" + return match.groups()[1] + + +@pytest.mark.slow +@pytest.mark.skipif(NO_MYPY, reason="Mypy is not installed") +@pytest.fixture(scope="module", autouse=True) +def run_mypy() -> None: + """Clears the cache and run mypy before running any of the typing tests. + + The mypy results are cached in `OUTPUT_MYPY` for further use. + + The cache refresh can be skipped using + + NUMPY_TYPING_TEST_CLEAR_CACHE=0 pytest numpy/typing/tests + """ + if ( + os.path.isdir(CACHE_DIR) + and bool(os.environ.get("NUMPY_TYPING_TEST_CLEAR_CACHE", True)) + ): + shutil.rmtree(CACHE_DIR) + + for directory in (PASS_DIR, REVEAL_DIR, FAIL_DIR, MISC_DIR): + # Run mypy + stdout, stderr, exit_code = api.run([ + "--config-file", + MYPY_INI, + "--cache-dir", + CACHE_DIR, + directory, + ]) + if stderr: + pytest.fail(f"Unexpected mypy standard error\n\n{stderr}") + elif exit_code not in {0, 1}: + pytest.fail(f"Unexpected mypy exit code: {exit_code}\n\n{stdout}") + stdout = stdout.replace('*', '') + + # Parse the output + iterator = itertools.groupby(stdout.split("\n"), key=_key_func) + OUTPUT_MYPY.update((k, list(v)) for k, v in iterator if k) + + +def get_test_cases(directory: str) -> Iterator[ParameterSet]: + for root, _, files in os.walk(directory): + for fname in files: + short_fname, ext = os.path.splitext(fname) + if ext in (".pyi", ".py"): + fullpath = os.path.join(root, fname) + yield pytest.param(fullpath, id=short_fname) + + +@pytest.mark.slow +@pytest.mark.skipif(NO_MYPY, reason="Mypy is not installed") +@pytest.mark.parametrize("path", get_test_cases(PASS_DIR)) +def test_success(path) -> None: + # Alias `OUTPUT_MYPY` so that it appears in the local namespace + output_mypy = OUTPUT_MYPY + if path in output_mypy: + msg = "Unexpected mypy output\n\n" + msg += "\n".join(_strip_filename(v) for v in output_mypy[path]) + raise AssertionError(msg) + + +@pytest.mark.slow +@pytest.mark.skipif(NO_MYPY, reason="Mypy is not installed") +@pytest.mark.parametrize("path", get_test_cases(FAIL_DIR)) +def test_fail(path: str) -> None: + __tracebackhide__ = True + + with open(path) as fin: + lines = fin.readlines() + + errors = defaultdict(lambda: "") + + output_mypy = OUTPUT_MYPY + assert path in output_mypy + for error_line in output_mypy[path]: + error_line = _strip_filename(error_line) + match = re.match( + r"(?P<lineno>\d+): (error|note): .+$", + error_line, + ) + if match is None: + raise ValueError(f"Unexpected error line format: {error_line}") + lineno = int(match.group('lineno')) + errors[lineno] += f'{error_line}\n' + + for i, line in enumerate(lines): + lineno = i + 1 + if ( + line.startswith('#') + or (" E:" not in line and lineno not in errors) + ): + continue + + target_line = lines[lineno - 1] + if "# E:" in target_line: + expression, _, marker = target_line.partition(" # E: ") + expected_error = errors[lineno].strip() + marker = marker.strip() + _test_fail(path, expression, marker, expected_error, lineno) + else: + pytest.fail( + f"Unexpected mypy output at line {lineno}\n\n{errors[lineno]}" + ) + + +_FAIL_MSG1 = """Extra error at line {} + +Expression: {} +Extra error: {!r} +""" + +_FAIL_MSG2 = """Error mismatch at line {} + +Expression: {} +Expected error: {!r} +Observed error: {!r} +""" + + +def _test_fail( + path: str, + expression: str, + error: str, + expected_error: None | str, + lineno: int, +) -> None: + if expected_error is None: + raise AssertionError(_FAIL_MSG1.format(lineno, expression, error)) + elif error not in expected_error: + raise AssertionError(_FAIL_MSG2.format( + lineno, expression, expected_error, error + )) + + +def _construct_ctypes_dict() -> dict[str, str]: + dct = { + "ubyte": "c_ubyte", + "ushort": "c_ushort", + "uintc": "c_uint", + "uint": "c_ulong", + "ulonglong": "c_ulonglong", + "byte": "c_byte", + "short": "c_short", + "intc": "c_int", + "int_": "c_long", + "longlong": "c_longlong", + "single": "c_float", + "double": "c_double", + "longdouble": "c_longdouble", + } + + # Match `ctypes` names to the first ctypes type with a given kind and + # precision, e.g. {"c_double": "c_double", "c_longdouble": "c_double"} + # if both types represent 64-bit floats. + # In this context "first" is defined by the order of `dct` + ret = {} + visited: dict[tuple[str, int], str] = {} + for np_name, ct_name in dct.items(): + np_scalar = getattr(np, np_name)() + + # Find the first `ctypes` type for a given `kind`/`itemsize` combo + key = (np_scalar.dtype.kind, np_scalar.dtype.itemsize) + ret[ct_name] = visited.setdefault(key, f"ctypes.{ct_name}") + return ret + + +def _construct_format_dict() -> dict[str, str]: + dct = {k.split(".")[-1]: v.replace("numpy", "numpy.typing") for + k, v in _PRECISION_DICT.items()} + + return { + "uint8": "numpy.unsignedinteger[numpy.typing._8Bit]", + "uint16": "numpy.unsignedinteger[numpy.typing._16Bit]", + "uint32": "numpy.unsignedinteger[numpy.typing._32Bit]", + "uint64": "numpy.unsignedinteger[numpy.typing._64Bit]", + "uint128": "numpy.unsignedinteger[numpy.typing._128Bit]", + "uint256": "numpy.unsignedinteger[numpy.typing._256Bit]", + "int8": "numpy.signedinteger[numpy.typing._8Bit]", + "int16": "numpy.signedinteger[numpy.typing._16Bit]", + "int32": "numpy.signedinteger[numpy.typing._32Bit]", + "int64": "numpy.signedinteger[numpy.typing._64Bit]", + "int128": "numpy.signedinteger[numpy.typing._128Bit]", + "int256": "numpy.signedinteger[numpy.typing._256Bit]", + "float16": "numpy.floating[numpy.typing._16Bit]", + "float32": "numpy.floating[numpy.typing._32Bit]", + "float64": "numpy.floating[numpy.typing._64Bit]", + "float80": "numpy.floating[numpy.typing._80Bit]", + "float96": "numpy.floating[numpy.typing._96Bit]", + "float128": "numpy.floating[numpy.typing._128Bit]", + "float256": "numpy.floating[numpy.typing._256Bit]", + "complex64": ("numpy.complexfloating" + "[numpy.typing._32Bit, numpy.typing._32Bit]"), + "complex128": ("numpy.complexfloating" + "[numpy.typing._64Bit, numpy.typing._64Bit]"), + "complex160": ("numpy.complexfloating" + "[numpy.typing._80Bit, numpy.typing._80Bit]"), + "complex192": ("numpy.complexfloating" + "[numpy.typing._96Bit, numpy.typing._96Bit]"), + "complex256": ("numpy.complexfloating" + "[numpy.typing._128Bit, numpy.typing._128Bit]"), + "complex512": ("numpy.complexfloating" + "[numpy.typing._256Bit, numpy.typing._256Bit]"), + + "ubyte": f"numpy.unsignedinteger[{dct['_NBitByte']}]", + "ushort": f"numpy.unsignedinteger[{dct['_NBitShort']}]", + "uintc": f"numpy.unsignedinteger[{dct['_NBitIntC']}]", + "uintp": f"numpy.unsignedinteger[{dct['_NBitIntP']}]", + "uint": f"numpy.unsignedinteger[{dct['_NBitInt']}]", + "ulonglong": f"numpy.unsignedinteger[{dct['_NBitLongLong']}]", + "byte": f"numpy.signedinteger[{dct['_NBitByte']}]", + "short": f"numpy.signedinteger[{dct['_NBitShort']}]", + "intc": f"numpy.signedinteger[{dct['_NBitIntC']}]", + "intp": f"numpy.signedinteger[{dct['_NBitIntP']}]", + "int_": f"numpy.signedinteger[{dct['_NBitInt']}]", + "longlong": f"numpy.signedinteger[{dct['_NBitLongLong']}]", + + "half": f"numpy.floating[{dct['_NBitHalf']}]", + "single": f"numpy.floating[{dct['_NBitSingle']}]", + "double": f"numpy.floating[{dct['_NBitDouble']}]", + "longdouble": f"numpy.floating[{dct['_NBitLongDouble']}]", + "csingle": ("numpy.complexfloating" + f"[{dct['_NBitSingle']}, {dct['_NBitSingle']}]"), + "cdouble": ("numpy.complexfloating" + f"[{dct['_NBitDouble']}, {dct['_NBitDouble']}]"), + "clongdouble": ( + "numpy.complexfloating" + f"[{dct['_NBitLongDouble']}, {dct['_NBitLongDouble']}]" + ), + + # numpy.typing + "_NBitInt": dct['_NBitInt'], + + # numpy.ctypeslib + "c_intp": f"ctypes.{_C_INTP}" + } + + +#: A dictionary with all supported format keys (as keys) +#: and matching values +FORMAT_DICT: dict[str, str] = _construct_format_dict() +FORMAT_DICT.update(_construct_ctypes_dict()) + + +def _parse_reveals(file: IO[str]) -> tuple[npt.NDArray[np.str_], list[str]]: + """Extract and parse all ``" # E: "`` comments from the passed + file-like object. + + All format keys will be substituted for their respective value + from `FORMAT_DICT`, *e.g.* ``"{float64}"`` becomes + ``"numpy.floating[numpy.typing._64Bit]"``. + """ + string = file.read().replace("*", "") + + # Grab all `# E:`-based comments and matching expressions + expression_array, _, comments_array = np.char.partition( + string.split("\n"), sep=" # E: " + ).T + comments = "/n".join(comments_array) + + # Only search for the `{*}` pattern within comments, otherwise + # there is the risk of accidentally grabbing dictionaries and sets + key_set = set(re.findall(r"\{(.*?)\}", comments)) + kwargs = { + k: FORMAT_DICT.get(k, f"<UNRECOGNIZED FORMAT KEY {k!r}>") for + k in key_set + } + fmt_str = comments.format(**kwargs) + + return expression_array, fmt_str.split("/n") + + +@pytest.mark.slow +@pytest.mark.skipif(NO_MYPY, reason="Mypy is not installed") +@pytest.mark.parametrize("path", get_test_cases(REVEAL_DIR)) +def test_reveal(path: str) -> None: + """Validate that mypy correctly infers the return-types of + the expressions in `path`. + """ + __tracebackhide__ = True + + with open(path) as fin: + expression_array, reveal_list = _parse_reveals(fin) + + output_mypy = OUTPUT_MYPY + assert path in output_mypy + for error_line in output_mypy[path]: + error_line = _strip_filename(error_line) + match = re.match( + r"(?P<lineno>\d+): note: .+$", + error_line, + ) + if match is None: + raise ValueError(f"Unexpected reveal line format: {error_line}") + lineno = int(match.group('lineno')) - 1 + assert "Revealed type is" in error_line + + marker = reveal_list[lineno] + expression = expression_array[lineno] + _test_reveal(path, expression, marker, error_line, 1 + lineno) + + +_REVEAL_MSG = """Reveal mismatch at line {} + +Expression: {} +Expected reveal: {!r} +Observed reveal: {!r} +""" + + +def _test_reveal( + path: str, + expression: str, + reveal: str, + expected_reveal: str, + lineno: int, +) -> None: + """Error-reporting helper function for `test_reveal`.""" + strip_pattern = re.compile(r"(\w+\.)+(\w+)") + stripped_reveal = strip_pattern.sub(strip_func, reveal) + stripped_expected_reveal = strip_pattern.sub(strip_func, expected_reveal) + if stripped_reveal not in stripped_expected_reveal: + raise AssertionError( + _REVEAL_MSG.format(lineno, + expression, + stripped_expected_reveal, + stripped_reveal) + ) + + +@pytest.mark.slow +@pytest.mark.skipif(NO_MYPY, reason="Mypy is not installed") +@pytest.mark.parametrize("path", get_test_cases(PASS_DIR)) +def test_code_runs(path: str) -> None: + """Validate that the code in `path` properly during runtime.""" + path_without_extension, _ = os.path.splitext(path) + dirname, filename = path.split(os.sep)[-2:] + + spec = importlib.util.spec_from_file_location( + f"{dirname}.{filename}", path + ) + assert spec is not None + assert spec.loader is not None + + test_module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(test_module) + + +LINENO_MAPPING = { + 3: "uint128", + 4: "uint256", + 6: "int128", + 7: "int256", + 9: "float80", + 10: "float96", + 11: "float128", + 12: "float256", + 14: "complex160", + 15: "complex192", + 16: "complex256", + 17: "complex512", +} + + +@pytest.mark.slow +@pytest.mark.skipif(NO_MYPY, reason="Mypy is not installed") +def test_extended_precision() -> None: + path = os.path.join(MISC_DIR, "extended_precision.pyi") + output_mypy = OUTPUT_MYPY + assert path in output_mypy + + with open(path, "r") as f: + expression_list = f.readlines() + + for _msg in output_mypy[path]: + *_, _lineno, msg_typ, msg = _msg.split(":") + + msg = _strip_filename(msg) + lineno = int(_lineno) + expression = expression_list[lineno - 1].rstrip("\n") + msg_typ = msg_typ.strip() + assert msg_typ in {"error", "note"} + + if LINENO_MAPPING[lineno] in _EXTENDED_PRECISION_LIST: + if msg_typ == "error": + raise ValueError(f"Unexpected reveal line format: {lineno}") + else: + marker = FORMAT_DICT[LINENO_MAPPING[lineno]] + _test_reveal(path, expression, marker, msg, lineno) + else: + if msg_typ == "error": + marker = "Module has no attribute" + _test_fail(path, expression, marker, msg, lineno) diff --git a/numpy/version.py b/numpy/version.py new file mode 100644 index 000000000000..d5657d0d08eb --- /dev/null +++ b/numpy/version.py @@ -0,0 +1,15 @@ +from __future__ import annotations + +from ._version import get_versions + +__ALL__ = ['version', '__version__', 'full_version', 'git_revision', 'release'] + +vinfo: dict[str, str] = get_versions() +version = vinfo["version"] +__version__ = vinfo.get("closest-tag", vinfo["version"]) +full_version = vinfo['version'] +git_revision = vinfo['full-revisionid'] +release = 'dev0' not in version and '+' not in version +short_version = vinfo['version'].split("+")[0] + +del get_versions, vinfo diff --git a/pavement.py b/pavement.py index 3484e8029a32..6fdaae975c24 100644 --- a/pavement.py +++ b/pavement.py @@ -4,37 +4,6 @@ independent from the user system as possible (e.g. to make sure the sphinx doc is built against the built numpy, not an installed one). -Building a fancy dmg from scratch -================================= - -Clone the numpy-macosx-installer git repo from on github into the source tree -(numpy-macosx-installer should be in the same directory as setup.py). Then, do -as follows:: - - git clone git://github.com/cournape/macosx-numpy-installer - # remove build dir, and everything generated by previous paver calls - # (included generated installers). Use with care ! - paver nuke - paver bootstrap && source bootstrap/bin/activate - # Installing numpy is necessary to build the correct documentation (because - # of autodoc) - python setup.py install - paver dmg - -Building a simple (no-superpack) windows installer from wine -============================================================ - -It assumes that blas/lapack are in c:\local\lib inside drive_c. - - paver bdist_wininst_simple - -You will have to configure your wine python locations (WINE_PYS). - -The superpack requires all the atlas libraries for every arch to be installed -(see SITECFG), and can then be built as follows:: - - paver bdist_superpack - Building changelog + notes ========================== @@ -43,8 +12,7 @@ paver write_release paver write_note -This automatically put the checksum into NOTES.txt, and write the Changelog -which can be uploaded to sourceforge. +This automatically put the checksum into README.rst, and writes the Changelog. TODO ==== @@ -54,488 +22,75 @@ - fix bdist_mpkg: we build the same source twice -> how to make sure we use the same underlying python for egg install in venv and for bdist_mpkg """ -from __future__ import division, print_function - -# What need to be installed to build everything on mac os x: -# - wine: python 2.6 and 2.5 + makensis + cpuid plugin + mingw, all in the PATH -# - paver + virtualenv -# - full texlive import os import sys import shutil -import subprocess -import re import hashlib +import textwrap +# The paver package needs to be installed to run tasks import paver -from paver.easy import \ - options, Bunch, task, call_task, sh, needs, cmdopts, dry - -sys.path.insert(0, os.path.dirname(__file__)) -try: - setup_py = __import__("setup") - FULLVERSION = setup_py.VERSION - # This is duplicated from setup.py - if os.path.exists('.git'): - GIT_REVISION = setup_py.git_version() - elif os.path.exists('numpy/version.py'): - # must be a source distribution, use existing version file - from numpy.version import git_revision as GIT_REVISION - else: - GIT_REVISION = "Unknown" - - if not setup_py.ISRELEASED: - FULLVERSION += '.dev0+' + GIT_REVISION[:7] -finally: - sys.path.pop(0) +from paver.easy import Bunch, options, task, sh #----------------------------------- # Things to be changed for a release #----------------------------------- -# Source of the release notes -RELEASE_NOTES = 'doc/release/1.15.0-notes.rst' - -# Start/end of the log (from git) -LOG_START = 'maintenance/1.14.x' -LOG_END = 'master' +# Path to the release notes +RELEASE_NOTES = 'doc/source/release/1.22.0-notes.rst' #------------------------------------------------------- # Hardcoded build/install dirs, virtualenv options, etc. #------------------------------------------------------- -DEFAULT_PYTHON = "2.7" - -# Where to put the final installers, as put on sourceforge -SUPERPACK_BUILD = 'build-superpack' -SUPERPACK_BINDIR = os.path.join(SUPERPACK_BUILD, 'binaries') - -options(bootstrap=Bunch(bootstrap_dir="bootstrap"), - virtualenv=Bunch(packages_to_install=["sphinx==1.1.3", "numpydoc"], - no_site_packages=False), - sphinx=Bunch(builddir="build", sourcedir="source", docroot='doc'), - superpack=Bunch(builddir="build-superpack"), - installers=Bunch(releasedir="release", - installersdir=os.path.join("release", "installers")), - doc=Bunch(doc_root="doc", - sdir=os.path.join("doc", "source"), - bdir=os.path.join("doc", "build"), - bdir_latex=os.path.join("doc", "build", "latex"), - destdir_pdf=os.path.join("build_doc", "pdf") - ), - html=Bunch(builddir=os.path.join("build", "html")), - dmg=Bunch(python_version=DEFAULT_PYTHON), - bdist_wininst_simple=Bunch(python_version=DEFAULT_PYTHON), -) - -MPKG_PYTHON = { - "2.6": ["/Library/Frameworks/Python.framework/Versions/2.6/bin/python"], - "2.7": ["/Library/Frameworks/Python.framework/Versions/2.7/bin/python"], - "3.2": ["/Library/Frameworks/Python.framework/Versions/3.2/bin/python3"], - "3.3": ["/Library/Frameworks/Python.framework/Versions/3.3/bin/python3"], - "3.4": ["/Library/Frameworks/Python.framework/Versions/3.4/bin/python3"], -} - -SSE3_CFG = {'ATLAS': r'C:\local\lib\atlas\sse3'} -SSE2_CFG = {'ATLAS': r'C:\local\lib\atlas\sse2'} -NOSSE_CFG = {'BLAS': r'C:\local\lib\atlas\nosse', 'LAPACK': r'C:\local\lib\atlas\nosse'} - -SITECFG = {"sse2" : SSE2_CFG, "sse3" : SSE3_CFG, "nosse" : NOSSE_CFG} - -if sys.platform =="darwin": - WINDOWS_PYTHON = { - "3.4": ["wine", os.environ['HOME'] + "/.wine/drive_c/Python34/python.exe"], - "2.7": ["wine", os.environ['HOME'] + "/.wine/drive_c/Python27/python.exe"], - } - WINDOWS_ENV = os.environ - WINDOWS_ENV["DYLD_FALLBACK_LIBRARY_PATH"] = "/usr/X11/lib:/usr/lib" - MAKENSIS = ["wine", "makensis"] -elif sys.platform == "win32": - WINDOWS_PYTHON = { - "3.4": [r"C:\Python34\python.exe"], - "2.7": [r"C:\Python27\python.exe"], - } - # XXX: find out which env variable is necessary to avoid the pb with python - # 2.6 and random module when importing tempfile - WINDOWS_ENV = os.environ - MAKENSIS = ["makensis"] -else: - WINDOWS_PYTHON = { - "3.4": ["wine", os.environ['HOME'] + "/.wine/drive_c/Python34/python.exe"], - "2.7": ["wine", os.environ['HOME'] + "/.wine/drive_c/Python27/python.exe"], - } - WINDOWS_ENV = os.environ - MAKENSIS = ["wine", "makensis"] - - -#------------------- -# Windows installers -#------------------- -def superpack_name(pyver, numver): - """Return the filename of the superpack installer.""" - return 'numpy-%s-win32-superpack-python%s.exe' % (numver, pyver) - -def internal_wininst_name(arch): - """Return the name of the wininst as it will be inside the superpack (i.e. - with the arch encoded.""" - ext = '.exe' - return "numpy-%s-%s%s" % (FULLVERSION, arch, ext) - -def wininst_name(pyver): - """Return the name of the installer built by wininst command.""" - ext = '.exe' - return "numpy-%s.win32-py%s%s" % (FULLVERSION, pyver, ext) - -def prepare_nsis_script(pyver, numver): - if not os.path.exists(SUPERPACK_BUILD): - os.makedirs(SUPERPACK_BUILD) - - tpl = os.path.join('tools/win32build/nsis_scripts', 'numpy-superinstaller.nsi.in') - source = open(tpl, 'r') - target = open(os.path.join(SUPERPACK_BUILD, 'numpy-superinstaller.nsi'), 'w') - - installer_name = superpack_name(pyver, numver) - cnt = "".join(source.readlines()) - cnt = cnt.replace('@NUMPY_INSTALLER_NAME@', installer_name) - for arch in ['nosse', 'sse2', 'sse3']: - cnt = cnt.replace('@%s_BINARY@' % arch.upper(), - internal_wininst_name(arch)) - - target.write(cnt) - -def bdist_wininst_arch(pyver, arch): - """Arch specific wininst build.""" - if os.path.exists("build"): - shutil.rmtree("build") - - _bdist_wininst(pyver, SITECFG[arch]) - -@task -@cmdopts([("python-version=", "p", "python version")]) -def bdist_superpack(options): - """Build all arch specific wininst installers.""" - pyver = options.python_version - def copy_bdist(arch): - # Copy the wininst in dist into the release directory - source = os.path.join('dist', wininst_name(pyver)) - target = os.path.join(SUPERPACK_BINDIR, internal_wininst_name(arch)) - if os.path.exists(target): - os.remove(target) - if not os.path.exists(os.path.dirname(target)): - os.makedirs(os.path.dirname(target)) - try: - os.rename(source, target) - except OSError: - # When git is installed on OS X but not under Wine, the name of the - # .exe has "-Unknown" in it instead of the correct git revision. - # Try to fix this here: - revidx = source.index(".dev-") + 5 - gitrev = source[revidx:revidx+7] - os.rename(source.replace(gitrev, "Unknown"), target) - - bdist_wininst_arch(pyver, 'nosse') - copy_bdist("nosse") - bdist_wininst_arch(pyver, 'sse2') - copy_bdist("sse2") - bdist_wininst_arch(pyver, 'sse3') - copy_bdist("sse3") - - idirs = options.installers.installersdir - pyver = options.python_version - prepare_nsis_script(pyver, FULLVERSION) - subprocess.check_call(MAKENSIS + ['numpy-superinstaller.nsi'], - cwd=SUPERPACK_BUILD) - - # Copy the superpack into installers dir - if not os.path.exists(idirs): - os.makedirs(idirs) - - source = os.path.join(SUPERPACK_BUILD, superpack_name(pyver, FULLVERSION)) - target = os.path.join(idirs, superpack_name(pyver, FULLVERSION)) - shutil.copy(source, target) - -@task -@cmdopts([("python-version=", "p", "python version")]) -def bdist_wininst_nosse(options): - """Build the nosse wininst installer.""" - bdist_wininst_arch(options.python_version, 'nosse') - -@task -@cmdopts([("python-version=", "p", "python version")]) -def bdist_wininst_sse2(options): - """Build the sse2 wininst installer.""" - bdist_wininst_arch(options.python_version, 'sse2') - -@task -@cmdopts([("python-version=", "p", "python version")]) -def bdist_wininst_sse3(options): - """Build the sse3 wininst installer.""" - bdist_wininst_arch(options.python_version, 'sse3') - -@task -@cmdopts([("python-version=", "p", "python version")]) -def bdist_wininst_simple(): - """Simple wininst-based installer.""" - pyver = options.bdist_wininst_simple.python_version - _bdist_wininst(pyver) - -def _bdist_wininst(pyver, cfg_env=None): - cmd = WINDOWS_PYTHON[pyver] + ['setup.py', 'build', '-c', 'mingw32', 'bdist_wininst'] - if cfg_env: - for k, v in WINDOWS_ENV.items(): - cfg_env[k] = v - else: - cfg_env = WINDOWS_ENV - subprocess.check_call(cmd, env=cfg_env) - -#---------------- -# Bootstrap stuff -#---------------- -@task -def bootstrap(options): - """create virtualenv in ./bootstrap""" - try: - import virtualenv - except ImportError as e: - raise RuntimeError("virtualenv is needed for bootstrap") - - bdir = options.bootstrap_dir - if not os.path.exists(bdir): - os.makedirs(bdir) - bscript = "boostrap.py" - - options.virtualenv.script_name = os.path.join(options.bootstrap_dir, - bscript) - options.virtualenv.no_site_packages = False - options.bootstrap.no_site_packages = False - call_task('paver.virtual.bootstrap') - sh('cd %s; %s %s' % (bdir, sys.executable, bscript)) - -@task -def clean(): - """Remove build, dist, egg-info garbage.""" - d = ['build', 'dist', 'numpy.egg-info'] - for i in d: - if os.path.exists(i): - shutil.rmtree(i) - - bdir = os.path.join('doc', options.sphinx.builddir) - if os.path.exists(bdir): - shutil.rmtree(bdir) - -@task -def clean_bootstrap(): - bdir = os.path.join(options.bootstrap.bootstrap_dir) - if os.path.exists(bdir): - shutil.rmtree(bdir) - -@task -@needs('clean', 'clean_bootstrap') -def nuke(options): - """Remove everything: build dir, installers, bootstrap dirs, etc...""" - for d in [options.superpack.builddir, options.installers.releasedir]: - if os.path.exists(d): - shutil.rmtree(d) - -#--------------------- -# Documentation tasks -#--------------------- -@task -def html(options): - """Build numpy documentation and put it into build/docs""" - # Don't use paver html target because of numpy bootstrapping problems - bdir = os.path.join("doc", options.sphinx.builddir, "html") - if os.path.exists(bdir): - shutil.rmtree(bdir) - subprocess.check_call(["make", "html"], cwd="doc") - html_destdir = options.html.builddir - if os.path.exists(html_destdir): - shutil.rmtree(html_destdir) - shutil.copytree(bdir, html_destdir) - -@task -def latex(): - """Build numpy documentation in latex format.""" - subprocess.check_call(["make", "latex"], cwd="doc") - -@task -@needs('latex') -def pdf(): - sdir = options.doc.sdir - bdir = options.doc.bdir - bdir_latex = options.doc.bdir_latex - destdir_pdf = options.doc.destdir_pdf - - def build_pdf(): - subprocess.check_call(["make", "all-pdf"], cwd=str(bdir_latex)) - dry("Build pdf doc", build_pdf) - - if os.path.exists(destdir_pdf): - shutil.rmtree(destdir_pdf) - os.makedirs(destdir_pdf) - - user = os.path.join(bdir_latex, "numpy-user.pdf") - shutil.copy(user, os.path.join(destdir_pdf, "userguide.pdf")) - ref = os.path.join(bdir_latex, "numpy-ref.pdf") - shutil.copy(ref, os.path.join(destdir_pdf, "reference.pdf")) - -#------------------ -# Mac OS X targets -#------------------ -def dmg_name(fullversion, pyver, osxver=None): - """Return name for dmg installer. - - Notes - ----- - Python 2.7 has two binaries, one for 10.3 (ppc, i386) and one for 10.6 - (i386, x86_64). All other Python versions at python.org at the moment - have binaries for 10.3 only. The "macosx%s" part of the dmg name should - correspond to the python.org naming scheme. - """ - # assume that for the py2.7/osx10.6 build the deployment target is set - # (should be done in the release script). - if not osxver: - osxver = os.environ.get('MACOSX_DEPLOYMENT_TARGET', '10.3') - return "numpy-%s-py%s-python.org-macosx%s.dmg" % (fullversion, pyver, - osxver) - -def macosx_version(): - if not sys.platform == 'darwin': - raise ValueError("Not darwin ??") - st = subprocess.Popen(["sw_vers"], stdout=subprocess.PIPE) - out = st.stdout.readlines() - ver = re.compile(r"ProductVersion:\s+([0-9]+)\.([0-9]+)\.([0-9]+)") - for i in out: - m = ver.match(i) - if m: - return m.groups() - -def mpkg_name(pyver): - maj, min = macosx_version()[:2] - # Note that bdist_mpkg breaks this if building a dev version with a git - # commit string attached. make_fullplatcomponents() in - # bdist_mpkg/cmd_bdist_mpkg.py replaces '-' with '_', comment this out if - # needed. - return "numpy-%s-py%s-macosx%s.%s.mpkg" % (FULLVERSION, pyver, maj, min) - -def _build_mpkg(pyver): - # account for differences between Python 2.7.1 versions from python.org - if os.environ.get('MACOSX_DEPLOYMENT_TARGET', None) == "10.6": - ldflags = "-undefined dynamic_lookup -bundle -arch i386 -arch x86_64 -Wl,-search_paths_first" - else: - ldflags = "-undefined dynamic_lookup -bundle -arch i386 -arch ppc -Wl,-search_paths_first" - ldflags += " -L%s" % os.path.join(os.path.dirname(__file__), "build") - sh("LDFLAGS='%s' %s setup.py bdist_mpkg" % (ldflags, " ".join(MPKG_PYTHON[pyver]))) +# Where to put the release installers +options(installers=Bunch(releasedir="release", + installersdir=os.path.join("release", "installers")),) -@task -def simple_dmg(): - pyver = "2.6" - src_dir = "dmg-source" - # Clean the source dir - if os.path.exists(src_dir): - shutil.rmtree(src_dir) - os.makedirs(src_dir) +#------------------------ +# Get the release version +#------------------------ - # Build the mpkg - clean() - _build_mpkg(pyver) - - # Build the dmg - shutil.copytree(os.path.join("dist", mpkg_name(pyver)), - os.path.join(src_dir, mpkg_name(pyver))) - _create_dmg(pyver, src_dir, "NumPy Universal %s" % FULLVERSION) - -@task -def bdist_mpkg(options): - call_task("clean") - try: - pyver = options.bdist_mpkg.python_version - except AttributeError: - pyver = options.python_version - - _build_mpkg(pyver) - -def _create_dmg(pyver, src_dir, volname=None): - # Build the dmg - image_name = dmg_name(FULLVERSION, pyver) - if os.path.exists(image_name): - os.remove(image_name) - cmd = ["hdiutil", "create", image_name, "-srcdir", src_dir] - if volname: - cmd.extend(["-volname", "'%s'" % volname]) - sh(" ".join(cmd)) - -@task -@cmdopts([("python-version=", "p", "python version")]) -def dmg(options): - try: - pyver = options.dmg.python_version - except Exception: - pyver = DEFAULT_PYTHON - idirs = options.installers.installersdir +sys.path.insert(0, os.path.dirname(__file__)) +try: + from setup import FULLVERSION +finally: + sys.path.pop(0) - # Check if docs exist. If not, say so and quit. - ref = os.path.join(options.doc.destdir_pdf, "reference.pdf") - user = os.path.join(options.doc.destdir_pdf, "userguide.pdf") - if (not os.path.exists(ref)) or (not os.path.exists(user)): - import warnings - warnings.warn("Docs need to be built first! Can't find them.", stacklevel=2) - - # Build the mpkg package - call_task("clean") - _build_mpkg(pyver) - - macosx_installer_dir = "tools/numpy-macosx-installer" - dmg = os.path.join(macosx_installer_dir, dmg_name(FULLVERSION, pyver)) - if os.path.exists(dmg): - os.remove(dmg) - - # Clean the image source - content = os.path.join(macosx_installer_dir, 'content') - if os.path.exists(content): - shutil.rmtree(content) - os.makedirs(content) - - # Copy mpkg into image source - mpkg_source = os.path.join("dist", mpkg_name(pyver)) - mpkg_target = os.path.join(content, "numpy-%s-py%s.mpkg" % (FULLVERSION, pyver)) - shutil.copytree(mpkg_source, mpkg_target) - - # Copy docs into image source - pdf_docs = os.path.join(content, "Documentation") - if os.path.exists(pdf_docs): - shutil.rmtree(pdf_docs) - os.makedirs(pdf_docs) - shutil.copy(user, os.path.join(pdf_docs, "userguide.pdf")) - shutil.copy(ref, os.path.join(pdf_docs, "reference.pdf")) - - # Build the dmg - cmd = ["./new-create-dmg", "--pkgname", os.path.basename(mpkg_target), - "--volname", "numpy", os.path.basename(dmg), "./content"] - st = subprocess.check_call(cmd, cwd=macosx_installer_dir) - - source = dmg - target = os.path.join(idirs, os.path.basename(dmg)) - if not os.path.exists(os.path.dirname(target)): - os.makedirs(os.path.dirname(target)) - shutil.copy(source, target) #-------------------------- # Source distribution stuff #-------------------------- -def tarball_name(type='gztar'): - root = 'numpy-%s' % FULLVERSION - if type == 'gztar': +def tarball_name(ftype='gztar'): + """Generate source distribution name + + Parameters + ---------- + ftype : {'zip', 'gztar'} + Type of archive, default is 'gztar'. + + """ + root = f'numpy-{FULLVERSION}' + if ftype == 'gztar': return root + '.tar.gz' - elif type == 'zip': + elif ftype == 'zip': return root + '.zip' - raise ValueError("Unknown type %s" % type) + raise ValueError(f"Unknown type {type}") + @task def sdist(options): + """Make source distributions. + + Parameters + ---------- + options : + Set by ``task`` decorator. + + """ # First clean the repo and update submodules (for up-to-date doc html theme # and Sphinx extensions) sh('git clean -xdf') @@ -546,103 +101,151 @@ def sdist(options): # do not play well together. # Cython is run over all Cython files in setup.py, so generated C files # will be included. - sh('python setup.py sdist --formats=gztar,zip') + sh('python3 setup.py sdist --formats=gztar,zip') # Copy the superpack into installers dir idirs = options.installers.installersdir if not os.path.exists(idirs): os.makedirs(idirs) - for t in ['gztar', 'zip']: - source = os.path.join('dist', tarball_name(t)) - target = os.path.join(idirs, tarball_name(t)) + for ftype in ['gztar', 'zip']: + source = os.path.join('dist', tarball_name(ftype)) + target = os.path.join(idirs, tarball_name(ftype)) shutil.copy(source, target) -def _compute_hash(idirs, algo): + +#------------- +# README stuff +#------------- + +def _compute_hash(idirs, hashfunc): + """Hash files using given hashfunc. + + Parameters + ---------- + idirs : directory path + Directory containing files to be hashed. + hashfunc : hash function + Function to be used to hash the files. + + """ released = paver.path.path(idirs).listdir() checksums = [] - for f in sorted(released): - with open(f, 'r') as _file: - m = algo(_file.read()) - checksums.append('%s %s' % (m.hexdigest(), os.path.basename(f))) + for fpath in sorted(released): + with open(fpath, 'rb') as fin: + fhash = hashfunc(fin.read()) + checksums.append( + '%s %s' % (fhash.hexdigest(), os.path.basename(fpath))) return checksums + def compute_md5(idirs): + """Compute md5 hash of files in idirs. + + Parameters + ---------- + idirs : directory path + Directory containing files to be hashed. + + """ return _compute_hash(idirs, hashlib.md5) + def compute_sha256(idirs): + """Compute sha256 hash of files in idirs. + + Parameters + ---------- + idirs : directory path + Directory containing files to be hashed. + + """ # better checksum so gpg signed README.rst containing the sums can be used # to verify the binaries instead of signing all binaries return _compute_hash(idirs, hashlib.sha256) + def write_release_task(options, filename='README'): - idirs = options.installers.installersdir - source = paver.path.path(RELEASE_NOTES) - target = paver.path.path(filename + '.rst') - if target.exists(): - target.remove() + """Append hashes of release files to release notes. + + This appends file hashes to the release notes and creates + four README files of the result in various formats: - tmp_target = paver.path.path(filename + '.md') - source.copy(tmp_target) + - README.rst + - README.rst.gpg + - README.md + - README.md.gpg - with open(str(tmp_target), 'a') as ftarget: - ftarget.writelines(""" -Checksums -========= + The md file are created using `pandoc` so that the links are + properly updated. The gpg files are kept separate, so that + the unsigned files may be edited before signing if needed. -MD5 ---- + Parameters + ---------- + options : + Set by ``task`` decorator. + filename : string + Filename of the modified notes. The file is written + in the release directory. -""") - ftarget.writelines([' %s\n' % c for c in compute_md5(idirs)]) - ftarget.writelines(""" -SHA256 ------- + """ + idirs = options.installers.installersdir + notes = paver.path.path(RELEASE_NOTES) + rst_readme = paver.path.path(filename + '.rst') + md_readme = paver.path.path(filename + '.md') -""") - ftarget.writelines([' %s\n' % c for c in compute_sha256(idirs)]) + # append hashes + with open(rst_readme, 'w') as freadme: + with open(notes) as fnotes: + freadme.write(fnotes.read()) - # Sign release - cmd = ['gpg', '--clearsign', '--armor'] - if hasattr(options, 'gpg_key'): - cmd += ['--default-key', options.gpg_key] - cmd += ['--output', str(target), str(tmp_target)] - subprocess.check_call(cmd) - print("signed %s" % (target,)) + freadme.writelines(textwrap.dedent( + """ + Checksums + ========= - # Change PR links for github posting, don't sign this - # as the signing isn't markdown compatible. - with open(str(tmp_target), 'r') as ftarget: - mdtext = ftarget.read() - mdtext = re.sub(r'^\* `(\#[0-9]*).*?`__', r'* \1', mdtext, flags=re.M) - with open(str(tmp_target), 'w') as ftarget: - ftarget.write(mdtext) + MD5 + --- + :: + """)) + freadme.writelines([f' {c}\n' for c in compute_md5(idirs)]) -def write_log_task(options, filename='Changelog'): - st = subprocess.Popen( - ['git', 'log', '--no-merges', '--use-mailmap', - '%s..%s' % (LOG_START, LOG_END)], - stdout=subprocess.PIPE) + freadme.writelines(textwrap.dedent( + """ + SHA256 + ------ + :: - out = st.communicate()[0] - a = open(filename, 'w') - a.writelines(out) - a.close() + """)) + freadme.writelines([f' {c}\n' for c in compute_sha256(idirs)]) + # generate md file using pandoc before signing + sh(f"pandoc -s -o {md_readme} {rst_readme}") -@task -def write_release(options): - write_release_task(options) + # Sign files + if hasattr(options, 'gpg_key'): + cmd = f'gpg --clearsign --armor --default_key {options.gpg_key}' + else: + cmd = 'gpg --clearsign --armor' + + sh(cmd + f' --output {rst_readme}.gpg {rst_readme}') + sh(cmd + f' --output {md_readme}.gpg {md_readme}') @task -def write_log(options): - write_log_task(options) +def write_release(options): + """Write the README files. + Two README files are generated from the release notes, one in ``rst`` + markup for the general release, the other in ``md`` markup for the github + release notes. -@task -def write_release_and_log(options): + Parameters + ---------- + options : + Set by ``task`` decorator. + + """ rdir = options.installers.releasedir write_release_task(options, os.path.join(rdir, 'README')) - write_log_task(options, os.path.join(rdir, 'Changelog')) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 000000000000..39d6fcd98a26 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,76 @@ +[build-system] +# Minimum requirements for the build system to execute. +requires = [ + "packaging==20.5; platform_machine=='arm64'", # macos M1 + "setuptools==59.2.0", + "wheel==0.37.0", + "Cython>=0.29.24,<3.0", # Note: keep in sync with tools/cythonize.py +] + + +[tool.towncrier] + # Do no set this since it is hard to import numpy inside the source directory + # the name is hardcoded. Use "--version 1.18.0" to set the version + single_file = true + filename = "doc/source/release/{version}-notes.rst" + directory = "doc/release/upcoming_changes/" + issue_format = "`gh-{issue} <https://github.com/numpy/numpy/pull/{issue}>`__" + template = "doc/release/upcoming_changes/template.rst" + underlines = "~=" + all_bullets = false + + + [[tool.towncrier.type]] + directory = "highlight" + name = "Highlights" + showcontent = true + + [[tool.towncrier.type]] + directory = "new_function" + name = "New functions" + showcontent = true + + [[tool.towncrier.type]] + directory = "deprecation" + name = "Deprecations" + showcontent = true + + [[tool.towncrier.type]] + directory = "future" + name = "Future Changes" + showcontent = true + + [[tool.towncrier.type]] + directory = "expired" + name = "Expired deprecations" + showcontent = true + + [[tool.towncrier.type]] + directory = "compatibility" + name = "Compatibility notes" + showcontent = true + + [[tool.towncrier.type]] + directory = "c_api" + name = "C API changes" + showcontent = true + + [[tool.towncrier.type]] + directory = "new_feature" + name = "New Features" + showcontent = true + + [[tool.towncrier.type]] + directory = "improvement" + name = "Improvements" + showcontent = true + + [[tool.towncrier.type]] + directory = "performance" + name = "Performance improvements and changes" + showcontent = true + + [[tool.towncrier.type]] + directory = "change" + name = "Changes" + showcontent = true diff --git a/pytest.ini b/pytest.ini index 2423857b097b..1d84f4c4803b 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,7 +1,8 @@ [pytest] addopts = -l norecursedirs = doc tools numpy/linalg/lapack_lite numpy/core/code_generators -doctest_optionflags = NORMALIZE_WHITESPACE +doctest_optionflags = NORMALIZE_WHITESPACE ELLIPSIS ALLOW_UNICODE ALLOW_BYTES +junit_family=xunit2 filterwarnings = error @@ -9,13 +10,13 @@ filterwarnings = ignore:Not importing directory ignore:numpy.dtype size changed ignore:numpy.ufunc size changed -# Ignore python2.7 -3 warnings - ignore:sys\.exc_clear\(\) not supported in 3\.x:DeprecationWarning - ignore:in 3\.x, __setslice__:DeprecationWarning - ignore:in 3\.x, __getslice__:DeprecationWarning - ignore:buffer\(\) not supported in 3\.x:DeprecationWarning - ignore:CObject type is not supported in 3\.x:DeprecationWarning - ignore:comparing unequal types not supported in 3\.x:DeprecationWarning - ignore:the commands module has been removed in Python 3\.0:DeprecationWarning -env = - PYTHONHASHSEED=0 + ignore::UserWarning:cpuinfo, +# Matrix PendingDeprecationWarning. + ignore:the matrix subclass is not + ignore:Importing from numpy.matlib is +# pytest warning when using PYTHONOPTIMIZE + ignore:assertions not in test modules or plugins:pytest.PytestConfigWarning +# TODO: remove below when array_api user warning is removed + ignore:The numpy.array_api submodule is still experimental. See NEP 47. +# Ignore DeprecationWarnings from distutils + ignore::DeprecationWarning:.*distutils diff --git a/release_requirements.txt b/release_requirements.txt new file mode 100644 index 000000000000..c24e39c7849d --- /dev/null +++ b/release_requirements.txt @@ -0,0 +1,17 @@ +# These packages are needed for a release in addition to those needed +# for building, testing, and the creation of documentation. + +# download-wheels.py +urllib3 +beautifulsoup4 + +# changelog.py +pygithub +gitpython + +# uploading wheels +twine + +# building and notes +Paver +towncrier diff --git a/runtests.py b/runtests.py index 35717b319be6..ac057a358dd3 100755 --- a/runtests.py +++ b/runtests.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """ runtests.py [OPTIONS] [-- ARGS] @@ -8,7 +8,10 @@ $ python runtests.py $ python runtests.py -s {SAMPLE_SUBMODULE} + $ # Run a standalone test function: $ python runtests.py -t {SAMPLE_TEST} + $ # Run a test defined as a method of a TestXXX class: + $ python runtests.py -t {SAMPLE_TEST2} $ python runtests.py --ipython $ python runtests.py --python somescript.py $ python runtests.py --bench @@ -18,15 +21,23 @@ $ gdb --args python runtests.py [...other args...] +Disable pytest capturing of output by using its '-s' option: + + $ python runtests.py -- -s + Generate C code coverage listing under build/lcov/: (requires http://ltp.sourceforge.net/coverage/lcov.php) $ python runtests.py --gcov [...other args...] $ python runtests.py --lcov-html -""" -from __future__ import division, print_function +Run lint checks. +Provide target branch name or `uncommitted` to check before committing: + $ python runtests.py --lint main + $ python runtests.py --lint uncommitted + +""" # # This is a generic test runner script for projects using NumPy's test # framework. Change the following values to adapt to your project: @@ -34,7 +45,8 @@ PROJECT_MODULE = "numpy" PROJECT_ROOT_FILES = ['numpy', 'LICENSE.txt', 'setup.py'] -SAMPLE_TEST = "numpy/linalg/tests/test_linalg.py:test_byteorder_check" +SAMPLE_TEST = "numpy/linalg/tests/test_linalg.py::test_byteorder_check" +SAMPLE_TEST2 = "numpy/core/tests/test_memmap.py::TestMemmap::test_open_with_filename" SAMPLE_SUBMODULE = "linalg" EXTRA_PATH = ['/usr/lib/ccache', '/usr/lib/f90cache', @@ -50,7 +62,7 @@ import sys -import os +import os, glob # In case we are run from the source directory, we don't want to import the # project from there: @@ -66,32 +78,44 @@ def main(argv): parser = ArgumentParser(usage=__doc__.lstrip()) parser.add_argument("--verbose", "-v", action="count", default=1, - help="more verbosity") + help="Add one verbosity level to pytest. Default is 0") + parser.add_argument("--debug-info", action="store_true", + help=("Add --verbose-cfg to build_src to show " + "compiler configuration output while creating " + "_numpyconfig.h and config.h")) parser.add_argument("--no-build", "-n", action="store_true", default=False, - help="do not build the project (use system installed version)") - parser.add_argument("--build-only", "-b", action="store_true", default=False, - help="just build, do not run any tests") + help="Do not build the project (use system installed " + "version)") + parser.add_argument("--build-only", "-b", action="store_true", + default=False, help="Just build, do not run any tests") parser.add_argument("--doctests", action="store_true", default=False, help="Run doctests in module") - #parser.add_argument("--refguide-check", action="store_true", default=False, - #help="Run refguide check (do not run regular tests.)") + parser.add_argument("--refguide-check", action="store_true", default=False, + help="Run refguide (doctest) check (do not run " + "regular tests.)") parser.add_argument("--coverage", action="store_true", default=False, - help=("report coverage of project code. HTML output goes " - "under build/coverage")) + help=("Report coverage of project code. HTML output " + "goes under build/coverage")) + parser.add_argument("--lint", default=None, + help="'<Target Branch>' or 'uncommitted', passed to " + "tools/linter.py [--branch BRANCH] " + "[--uncommitted]") parser.add_argument("--durations", action="store", default=-1, type=int, - help=("Time N slowest tests, time all if 0, time none if < 0")) + help=("Time N slowest tests, time all if 0, time none " + "if < 0")) parser.add_argument("--gcov", action="store_true", default=False, - help=("enable C code coverage via gcov (requires GCC). " - "gcov output goes to build/**/*.gc*")) + help=("Enable C code coverage via gcov (requires " + "GCC). gcov output goes to build/**/*.gc*")) parser.add_argument("--lcov-html", action="store_true", default=False, - help=("produce HTML for C code coverage information " + help=("Produce HTML for C code coverage information " "from a previous run with --gcov. " "HTML output goes to build/lcov/")) parser.add_argument("--mode", "-m", default="fast", help="'fast', 'full', or something that could be " "passed to nosetests -A [default: fast]") parser.add_argument("--submodule", "-s", default=None, - help="Submodule whose tests to run (cluster, constants, ...)") + help="Submodule whose tests to run (cluster, " + "constants, ...)") parser.add_argument("--pythonpath", "-p", default=None, help="Paths to prepend to PYTHONPATH") parser.add_argument("--tests", "-t", action='append', @@ -102,10 +126,26 @@ def main(argv): help="Start IPython shell with PYTHONPATH set") parser.add_argument("--shell", action="store_true", help="Start Unix shell with PYTHONPATH set") + parser.add_argument("--mypy", action="store_true", + help="Run mypy on files with NumPy on the MYPYPATH") parser.add_argument("--debug", "-g", action="store_true", help="Debug build") parser.add_argument("--parallel", "-j", type=int, default=0, help="Number of parallel jobs during build") + parser.add_argument("--warn-error", action="store_true", + help="Set -Werror to convert all compiler warnings to " + "errors") + parser.add_argument("--cpu-baseline", default=None, + help="Specify a list of enabled baseline CPU " + "optimizations"), + parser.add_argument("--cpu-dispatch", default=None, + help="Specify a list of dispatched CPU optimizations"), + parser.add_argument("--disable-optimization", action="store_true", + help="Disable CPU optimized code (dispatch, simd, " + "fast, ...)"), + parser.add_argument("--simd-test", default=None, + help="Specify a list of CPU optimizations to be " + "tested against NumPy SIMD interface"), parser.add_argument("--show-build-log", action="store_true", help="Show build output rather than using a log file") parser.add_argument("--bench", action="store_true", @@ -117,7 +157,8 @@ def main(argv): "COMMIT. Note that you need to commit your " "changes first!")) parser.add_argument("args", metavar="ARGS", default=[], nargs=REMAINDER, - help="Arguments to pass to Nose, Python or shell") + help="Arguments to pass to pytest, asv, mypy, Python " + "or shell") args = parser.parse_args(argv) if args.durations < 0: @@ -143,19 +184,29 @@ def main(argv): print("*** Benchmarks should not be run against debug " "version; remove -g flag ***") + if args.lint: + check_lint(args.lint) + if not args.no_build: # we need the noarch path in case the package is pure python. site_dir, site_dir_noarch = build_project(args) sys.path.insert(0, site_dir) sys.path.insert(0, site_dir_noarch) - os.environ['PYTHONPATH'] = site_dir + os.pathsep + site_dir_noarch + os.environ['PYTHONPATH'] = \ + os.pathsep.join(( + site_dir, + site_dir_noarch, + os.environ.get('PYTHONPATH', '') + )) else: _temp = __import__(PROJECT_MODULE) site_dir = os.path.sep.join(_temp.__file__.split(os.path.sep)[:-2]) extra_argv = args.args[:] - if extra_argv and extra_argv[0] == '--': - extra_argv = extra_argv[1:] + if not args.bench: + # extra_argv may also lists selected benchmarks + if extra_argv and extra_argv[0] == '--': + extra_argv = extra_argv[1:] if args.python: # Debugging issues with warnings is much easier if you can see them @@ -173,7 +224,7 @@ def main(argv): sys.modules['__main__'] = types.ModuleType('__main__') ns = dict(__name__='__main__', __file__=extra_argv[0]) - exec_(script, ns) + exec(script, ns) sys.exit(0) else: import code @@ -186,7 +237,7 @@ def main(argv): import warnings; warnings.filterwarnings("always") import IPython import numpy as np - IPython.embed(user_ns={"np": np}) + IPython.embed(colors='neutral', user_ns={"np": np}) sys.exit(0) if args.shell: @@ -195,6 +246,36 @@ def main(argv): subprocess.call([shell] + extra_argv) sys.exit(0) + if args.mypy: + try: + import mypy.api + except ImportError: + raise RuntimeError( + "Mypy not found. Please install it by running " + "pip install -r test_requirements.txt from the repo root" + ) + + os.environ['MYPYPATH'] = site_dir + # By default mypy won't color the output since it isn't being + # invoked from a tty. + os.environ['MYPY_FORCE_COLOR'] = '1' + + config = os.path.join( + site_dir, + "numpy", + "typing", + "tests", + "data", + "mypy.ini", + ) + + report, errors, status = mypy.api.run( + ['--config-file', config] + args.args + ) + print(report, end='') + print(errors, end='', file=sys.stderr) + sys.exit(status) + if args.coverage: dst_dir = os.path.join(ROOT_DIR, 'build', 'coverage') fn = os.path.join(dst_dir, 'coverage_html.js') @@ -202,15 +283,31 @@ def main(argv): shutil.rmtree(dst_dir) extra_argv += ['--cov-report=html:' + dst_dir] + if args.refguide_check: + cmd = [os.path.join(ROOT_DIR, 'tools', 'refguide_check.py'), + '--doctests'] + if args.submodule: + cmd += [args.submodule] + os.execv(sys.executable, [sys.executable] + cmd) + sys.exit(0) + if args.bench: # Run ASV - items = extra_argv + for i, v in enumerate(extra_argv): + if v.startswith("--"): + items = extra_argv[:i] + if v == "--": + i += 1 # skip '--' indicating further are passed on. + bench_args = extra_argv[i:] + break + else: + items = extra_argv + bench_args = [] + if args.tests: items += args.tests if args.submodule: items += [args.submodule] - - bench_args = [] for a in items: bench_args.extend(['--bench', a]) @@ -246,13 +343,19 @@ def main(argv): out = subprocess.check_output(['git', 'rev-parse', commit_a]) commit_a = out.strip().decode('ascii') + # generate config file with the required build options + asv_cfpath = [ + '--config', asv_compare_config( + os.path.join(ROOT_DIR, 'benchmarks'), args, + # to clear the cache if the user changed build options + (commit_a, commit_b) + ) + ] cmd = ['asv', 'continuous', '-e', '-f', '1.05', - commit_a, commit_b] + bench_args + commit_a, commit_b] + asv_cfpath + bench_args ret = subprocess.call(cmd, cwd=os.path.join(ROOT_DIR, 'benchmarks')) sys.exit(ret) - test_dir = os.path.join(ROOT_DIR, 'build', 'test') - if args.build_only: sys.exit(0) else: @@ -299,7 +402,6 @@ def main(argv): else: sys.exit(1) - def build_project(args): """ Build a dev version of the project. @@ -311,7 +413,7 @@ def build_project(args): """ - import distutils.sysconfig + import sysconfig root_ok = [os.path.exists(os.path.join(ROOT_DIR, fn)) for fn in PROJECT_ROOT_FILES] @@ -327,20 +429,22 @@ def build_project(args): # Always use ccache, if installed env['PATH'] = os.pathsep.join(EXTRA_PATH + env.get('PATH', '').split(os.pathsep)) - cvars = distutils.sysconfig.get_config_vars() - if 'gcc' in cvars.get('CC', ''): - # add flags used as werrors - warnings_as_errors = ' '.join([ - # from tools/travis-test.sh - '-Werror=declaration-after-statement', - '-Werror=vla', - '-Werror=nonnull', - '-Werror=pointer-arith', - '-Wlogical-op', - # from sysconfig - '-Werror=unused-function', - ]) - env['CFLAGS'] = warnings_as_errors + ' ' + env.get('CFLAGS', '') + cvars = sysconfig.get_config_vars() + compiler = env.get('CC') or cvars.get('CC', '') + if 'gcc' in compiler: + # Check that this isn't clang masquerading as gcc. + if sys.platform != 'darwin' or 'gnu-gcc' in compiler: + # add flags used as werrors + warnings_as_errors = ' '.join([ + # from tools/travis-test.sh + '-Werror=vla', + '-Werror=nonnull', + '-Werror=pointer-arith', + '-Wlogical-op', + # from sysconfig + '-Werror=unused-function', + ]) + env['CFLAGS'] = warnings_as_errors + ' ' + env.get('CFLAGS', '') if args.debug or args.gcov: # assume everyone uses gcc/gfortran env['OPT'] = '-O0 -ggdb' @@ -358,21 +462,44 @@ def build_project(args): cmd += ["build"] if args.parallel > 1: cmd += ["-j", str(args.parallel)] + if args.warn_error: + cmd += ["--warn-error"] + if args.cpu_baseline: + cmd += ["--cpu-baseline", args.cpu_baseline] + if args.cpu_dispatch: + cmd += ["--cpu-dispatch", args.cpu_dispatch] + if args.disable_optimization: + cmd += ["--disable-optimization"] + if args.simd_test is not None: + cmd += ["--simd-test", args.simd_test] + if args.debug_info: + cmd += ["build_src", "--verbose-cfg"] # Install; avoid producing eggs so numpy can be imported from dst_dir. cmd += ['install', '--prefix=' + dst_dir, '--single-version-externally-managed', '--record=' + dst_dir + 'tmp_install_log.txt'] - from distutils.sysconfig import get_python_lib - site_dir = get_python_lib(prefix=dst_dir, plat_specific=True) - site_dir_noarch = get_python_lib(prefix=dst_dir, plat_specific=False) + config_vars = dict(sysconfig.get_config_vars()) + config_vars["platbase"] = dst_dir + config_vars["base"] = dst_dir + + site_dir_template = os.path.normpath(sysconfig.get_path( + 'platlib', expand=False + )) + site_dir = site_dir_template.format(**config_vars) + noarch_template = os.path.normpath(sysconfig.get_path( + 'purelib', expand=False + )) + site_dir_noarch = noarch_template.format(**config_vars) + # easy_install won't install to a path that Python by default cannot see # and isn't on the PYTHONPATH. Plus, it has to exist. if not os.path.exists(site_dir): os.makedirs(site_dir) if not os.path.exists(site_dir_noarch): os.makedirs(site_dir_noarch) - env['PYTHONPATH'] = site_dir + ':' + site_dir_noarch + env['PYTHONPATH'] = \ + os.pathsep.join((site_dir, site_dir_noarch, env.get('PYTHONPATH', ''))) log_filename = os.path.join(ROOT_DIR, 'build.log') @@ -384,23 +511,27 @@ def build_project(args): with open(log_filename, 'w') as log: p = subprocess.Popen(cmd, env=env, stdout=log, stderr=log, cwd=ROOT_DIR) - - # Wait for it to finish, and print something to indicate the - # process is alive, but only if the log file has grown (to - # allow continuous integration environments kill a hanging - # process accurately if it produces no output) - last_blip = time.time() - last_log_size = os.stat(log_filename).st_size - while p.poll() is None: - time.sleep(0.5) - if time.time() - last_blip > 60: - log_size = os.stat(log_filename).st_size - if log_size > last_log_size: - print(" ... build in progress") - last_blip = time.time() - last_log_size = log_size - - ret = p.wait() + try: + # Wait for it to finish, and print something to indicate the + # process is alive, but only if the log file has grown (to + # allow continuous integration environments kill a hanging + # process accurately if it produces no output) + last_blip = time.time() + last_log_size = os.stat(log_filename).st_size + while p.poll() is None: + time.sleep(0.5) + if time.time() - last_blip > 60: + log_size = os.stat(log_filename).st_size + if log_size > last_log_size: + print(" ... build in progress") + last_blip = time.time() + last_log_size = log_size + + ret = p.wait() + except: + p.kill() + p.wait() + raise if ret == 0: print("Build OK") @@ -413,6 +544,99 @@ def build_project(args): return site_dir, site_dir_noarch +def asv_compare_config(bench_path, args, h_commits): + """ + Fill the required build options through custom variable + 'numpy_build_options' and return the generated config path. + """ + conf_path = os.path.join(bench_path, "asv_compare.conf.json.tpl") + nconf_path = os.path.join(bench_path, "_asv_compare.conf.json") + + # add custom build + build = [] + if args.parallel > 1: + build += ["-j", str(args.parallel)] + if args.cpu_baseline: + build += ["--cpu-baseline", args.cpu_baseline] + if args.cpu_dispatch: + build += ["--cpu-dispatch", args.cpu_dispatch] + if args.disable_optimization: + build += ["--disable-optimization"] + + is_cached = asv_substitute_config(conf_path, nconf_path, + numpy_build_options = ' '.join([f'\\"{v}\\"' for v in build]), + numpy_global_options= ' '.join([f'--global-option=\\"{v}\\"' for v in ["build"] + build]) + ) + if not is_cached: + asv_clear_cache(bench_path, h_commits) + return nconf_path + +def asv_clear_cache(bench_path, h_commits, env_dir="env"): + """ + Force ASV to clear the cache according to specified commit hashes. + """ + # FIXME: only clear the cache from the current environment dir + asv_build_pattern = os.path.join(bench_path, env_dir, "*", "asv-build-cache") + for asv_build_cache in glob.glob(asv_build_pattern, recursive=True): + for c in h_commits: + try: shutil.rmtree(os.path.join(asv_build_cache, c)) + except OSError: pass + +def asv_substitute_config(in_config, out_config, **custom_vars): + """ + A workaround to allow substituting custom tokens within + ASV configuration file since there's no official way to add custom + variables(e.g. env vars). + + Parameters + ---------- + in_config : str + The path of ASV configuration file, e.g. '/path/to/asv.conf.json' + out_config : str + The path of generated configuration file, + e.g. '/path/to/asv_substituted.conf.json'. + + The other keyword arguments represent the custom variables. + + Returns + ------- + True(is cached) if 'out_config' is already generated with + the same '**custom_vars' and updated with latest 'in_config', + False otherwise. + + Examples + -------- + See asv_compare_config(). + """ + assert in_config != out_config + assert len(custom_vars) > 0 + + def sdbm_hash(*factors): + chash = 0 + for f in factors: + for char in str(f): + chash = ord(char) + (chash << 6) + (chash << 16) - chash + chash &= 0xFFFFFFFF + return chash + + vars_hash = sdbm_hash(custom_vars, os.path.getmtime(in_config)) + try: + with open(out_config, "r") as wfd: + hash_line = wfd.readline().split('hash:') + if len(hash_line) > 1 and int(hash_line[1]) == vars_hash: + return True + except OSError: + pass + + custom_vars = {f'{{{k}}}':v for k, v in custom_vars.items()} + with open(in_config, "r") as rfd, open(out_config, "w") as wfd: + wfd.write(f"// hash:{vars_hash}\n") + wfd.write("// This file is automatically generated by runtests.py\n") + for line in rfd: + for key, val in custom_vars.items(): + line = line.replace(key, val) + wfd.write(line) + return False # # GCOV support @@ -454,26 +678,24 @@ def lcov_generate(): else: print("HTML output generated under build/lcov/") +def check_lint(lint_args): + """ + Adds ROOT_DIR to path and performs lint checks. + This functions exits the program with status code of lint check. + """ + sys.path.append(ROOT_DIR) + try: + from tools.linter import DiffLinter + except ModuleNotFoundError as e: + print(f"Error: {e.msg}. " + "Install using linter_requirements.txt.") + sys.exit(1) + + uncommitted = lint_args == "uncommitted" + branch = "main" if uncommitted else lint_args -# -# Python 3 support -# + DiffLinter(branch).run_lint(uncommitted) -if sys.version_info[0] >= 3: - import builtins - exec_ = getattr(builtins, "exec") -else: - def exec_(code, globs=None, locs=None): - """Execute code in a namespace.""" - if globs is None: - frame = sys._getframe(1) - globs = frame.f_globals - if locs is None: - locs = frame.f_locals - del frame - elif locs is None: - locs = globs - exec("""exec code in globs, locs""") if __name__ == "__main__": main(argv=sys.argv[1:]) diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 000000000000..f36b4b624066 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,14 @@ +[codespell] +skip = *-changelog.rst,*-notes.rst,f2c_blas.c,f2c_c_lapack.c,f2c_d_lapack.c,f2c_s_lapack.c,f2c_z_lapack.c + +# See the docstring in versioneer.py for instructions. Note that you must +# re-run 'versioneer.py setup' after changing this section, and commit the +# resulting files. + +[versioneer] +VCS = git +style = pep440 +versionfile_source = numpy/_version.py +versionfile_build = numpy/_version.py +tag_prefix = v +parentdir_prefix = numpy- diff --git a/setup.py b/setup.py index 4af382fa80a2..703fe79e1694 100755 --- a/setup.py +++ b/setup.py @@ -1,160 +1,114 @@ -#!/usr/bin/env python -"""NumPy: array processing for numbers, strings, records, and objects. +#!/usr/bin/env python3 +""" NumPy is the fundamental package for array computing with Python. -NumPy is a general-purpose array-processing package designed to -efficiently manipulate large multi-dimensional arrays of arbitrary -records without sacrificing too much speed for small multi-dimensional -arrays. NumPy is built on the Numeric code base and adds features -introduced by numarray as well as an extended C-API and the ability to -create arrays of arbitrary type which also makes NumPy suitable for -interfacing with general-purpose data-base applications. +It provides: -There are also basic facilities for discrete fourier transform, -basic linear algebra and random number generation. +- a powerful N-dimensional array object +- sophisticated (broadcasting) functions +- tools for integrating C/C++ and Fortran code +- useful linear algebra, Fourier transform, and random number capabilities +- and much more -All numpy wheels distributed from pypi are BSD licensed. +Besides its obvious scientific uses, NumPy can also be used as an efficient +multi-dimensional container of generic data. Arbitrary data-types can be +defined. This allows NumPy to seamlessly and speedily integrate with a wide +variety of databases. -Windows wheels are linked against the ATLAS BLAS / LAPACK library, restricted -to SSE2 instructions, so may not give optimal linear algebra performance for -your machine. See http://docs.scipy.org/doc/numpy/user/install.html for -alternatives. +All NumPy wheels distributed on PyPI are BSD licensed. """ -from __future__ import division, print_function - DOCLINES = (__doc__ or '').split("\n") import os import sys import subprocess import textwrap +import warnings +import builtins +import re + + +# Python supported version checks. Keep right after stdlib imports to ensure we +# get a sensible error for older Python versions +if sys.version_info[:2] < (3, 8): + raise RuntimeError("Python version >= 3.8 required.") + + +import versioneer + + +# This is a bit hackish: we are setting a global variable so that the main +# numpy __init__ can detect if it is being loaded by the setup routine, to +# avoid attempting to load components that aren't built yet. While ugly, it's +# a lot more robust than what was previously being used. +builtins.__NUMPY_SETUP__ = True +# Needed for backwards code compatibility below and in some CI scripts. +# The version components are changed from ints to strings, but only VERSION +# seems to matter outside of this module and it was already a str. +FULLVERSION = versioneer.get_version() + +# Capture the version string: +# 1.22.0.dev0+ ... -> ISRELEASED == False, VERSION == 1.22.0 +# 1.22.0rc1+ ... -> ISRELEASED == False, VERSION == 1.22.0 +# 1.22.0 ... -> ISRELEASED == True, VERSION == 1.22.0 +# 1.22.0rc1 ... -> ISRELEASED == True, VERSION == 1.22.0 +ISRELEASED = re.search(r'(dev|\+)', FULLVERSION) is None +_V_MATCH = re.match(r'(\d+)\.(\d+)\.(\d+)', FULLVERSION) +if _V_MATCH is None: + raise RuntimeError(f'Cannot parse version {FULLVERSION}') +MAJOR, MINOR, MICRO = _V_MATCH.groups() +VERSION = '{}.{}.{}'.format(MAJOR, MINOR, MICRO) + +# The first version not in the `Programming Language :: Python :: ...` classifiers above +if sys.version_info >= (3, 11): + fmt = "NumPy {} may not yet support Python {}.{}." + warnings.warn( + fmt.format(VERSION, *sys.version_info[:2]), + RuntimeWarning) + del fmt -if sys.version_info[:2] < (2, 7) or (3, 0) <= sys.version_info[:2] < (3, 4): - raise RuntimeError("Python version 2.7 or >= 3.4 required.") +# BEFORE importing setuptools, remove MANIFEST. Otherwise it may not be +# properly updated when the contents of directories change (true for distutils, +# not sure about setuptools). +if os.path.exists('MANIFEST'): + os.remove('MANIFEST') -if sys.version_info[0] >= 3: - import builtins -else: - import __builtin__ as builtins +# We need to import setuptools here in order for it to persist in sys.modules. +# Its presence/absence is used in subclassing setup in numpy/distutils/core.py. +# However, we need to run the distutils version of sdist, so import that first +# so that it is in sys.modules +import numpy.distutils.command.sdist +import setuptools +# Initialize cmdclass from versioneer +from numpy.distutils.core import numpy_cmdclass +cmdclass = versioneer.get_cmdclass(numpy_cmdclass) CLASSIFIERS = """\ Development Status :: 5 - Production/Stable Intended Audience :: Science/Research Intended Audience :: Developers -License :: OSI Approved +License :: OSI Approved :: BSD License Programming Language :: C Programming Language :: Python -Programming Language :: Python :: 2 -Programming Language :: Python :: 2.7 Programming Language :: Python :: 3 -Programming Language :: Python :: 3.4 -Programming Language :: Python :: 3.5 -Programming Language :: Python :: 3.6 +Programming Language :: Python :: 3.8 +Programming Language :: Python :: 3.9 +Programming Language :: Python :: 3.10 +Programming Language :: Python :: 3 :: Only Programming Language :: Python :: Implementation :: CPython Topic :: Software Development Topic :: Scientific/Engineering +Typing :: Typed Operating System :: Microsoft :: Windows Operating System :: POSIX Operating System :: Unix Operating System :: MacOS """ -MAJOR = 1 -MINOR = 15 -MICRO = 0 -ISRELEASED = False -VERSION = '%d.%d.%d' % (MAJOR, MINOR, MICRO) - - -# Return the git revision as a string -def git_version(): - def _minimal_ext_cmd(cmd): - # construct minimal environment - env = {} - for k in ['SYSTEMROOT', 'PATH', 'HOME']: - v = os.environ.get(k) - if v is not None: - env[k] = v - # LANGUAGE is used on win32 - env['LANGUAGE'] = 'C' - env['LANG'] = 'C' - env['LC_ALL'] = 'C' - out = subprocess.Popen(cmd, stdout=subprocess.PIPE, env=env).communicate()[0] - return out - - try: - out = _minimal_ext_cmd(['git', 'rev-parse', 'HEAD']) - GIT_REVISION = out.strip().decode('ascii') - except OSError: - GIT_REVISION = "Unknown" - - return GIT_REVISION - -# BEFORE importing setuptools, remove MANIFEST. Otherwise it may not be -# properly updated when the contents of directories change (true for distutils, -# not sure about setuptools). -if os.path.exists('MANIFEST'): - os.remove('MANIFEST') - -# This is a bit hackish: we are setting a global variable so that the main -# numpy __init__ can detect if it is being loaded by the setup routine, to -# avoid attempting to load components that aren't built yet. While ugly, it's -# a lot more robust than what was previously being used. -builtins.__NUMPY_SETUP__ = True - - -def get_version_info(): - # Adding the git rev number needs to be done inside write_version_py(), - # otherwise the import of numpy.version messes up the build under Python 3. - FULLVERSION = VERSION - if os.path.exists('.git'): - GIT_REVISION = git_version() - elif os.path.exists('numpy/version.py'): - # must be a source distribution, use existing version file - try: - from numpy.version import git_revision as GIT_REVISION - except ImportError: - raise ImportError("Unable to import git_revision. Try removing " \ - "numpy/version.py and the build directory " \ - "before building.") - else: - GIT_REVISION = "Unknown" - - if not ISRELEASED: - FULLVERSION += '.dev0+' + GIT_REVISION[:7] - return FULLVERSION, GIT_REVISION - - -def write_version_py(filename='numpy/version.py'): - cnt = """ -# THIS FILE IS GENERATED FROM NUMPY SETUP.PY -# -# To compare versions robustly, use `numpy.lib.NumpyVersion` -short_version = '%(version)s' -version = '%(version)s' -full_version = '%(full_version)s' -git_revision = '%(git_revision)s' -release = %(isrelease)s - -if not release: - version = full_version -""" - FULLVERSION, GIT_REVISION = get_version_info() - - a = open(filename, 'w') - try: - a.write(cnt % {'version': VERSION, - 'full_version': FULLVERSION, - 'git_revision': GIT_REVISION, - 'isrelease': str(ISRELEASED)}) - finally: - a.close() - - -def configuration(parent_package='',top_path=None): +def configuration(parent_package='', top_path=None): from numpy.distutils.misc_util import Configuration config = Configuration(None, parent_package, top_path) @@ -165,8 +119,9 @@ def configuration(parent_package='',top_path=None): config.add_subpackage('numpy') config.add_data_files(('numpy', 'LICENSE.txt')) + config.add_data_files(('numpy', 'numpy/*.pxd')) - config.get_version('numpy/version.py') # sets config.version + config.get_version('numpy/version.py') # sets config.version return config @@ -178,12 +133,11 @@ def check_submodules(): if not os.path.exists('.git'): return with open('.gitmodules') as f: - for l in f: - if 'path' in l: - p = l.split('=')[-1].strip() + for line in f: + if 'path' in line: + p = line.split('=')[-1].strip() if not os.path.exists(p): - raise ValueError('Submodule %s missing' % p) - + raise ValueError('Submodule {} missing'.format(p)) proc = subprocess.Popen(['git', 'submodule', 'status'], stdout=subprocess.PIPE) @@ -191,26 +145,96 @@ def check_submodules(): status = status.decode("ascii", "replace") for line in status.splitlines(): if line.startswith('-') or line.startswith('+'): - raise ValueError('Submodule not clean: %s' % line) + raise ValueError('Submodule not clean: {}'.format(line)) + +class concat_license_files(): + """Merge LICENSE.txt and LICENSES_bundled.txt for sdist creation -from distutils.command.sdist import sdist -class sdist_checked(sdist): + Done this way to keep LICENSE.txt in repo as exact BSD 3-clause (see + gh-13447). This makes GitHub state correctly how NumPy is licensed. + """ + def __init__(self): + self.f1 = 'LICENSE.txt' + self.f2 = 'LICENSES_bundled.txt' + + def __enter__(self): + """Concatenate files and remove LICENSES_bundled.txt""" + with open(self.f1, 'r') as f1: + self.bsd_text = f1.read() + + with open(self.f1, 'a') as f1: + with open(self.f2, 'r') as f2: + self.bundled_text = f2.read() + f1.write('\n\n') + f1.write(self.bundled_text) + + def __exit__(self, exception_type, exception_value, traceback): + """Restore content of both files""" + with open(self.f1, 'w') as f: + f.write(self.bsd_text) + + +# Need to inherit from versioneer version of sdist to get the encoded +# version information. +class sdist_checked(cmdclass['sdist']): """ check submodules on sdist to prevent incomplete tarballs """ def run(self): check_submodules() - sdist.run(self) + with concat_license_files(): + super().run() + + +def get_build_overrides(): + """ + Custom build commands to add `-std=c99` to compilation + """ + from numpy.distutils.command.build_clib import build_clib + from numpy.distutils.command.build_ext import build_ext + from distutils.version import LooseVersion + + def _needs_gcc_c99_flag(obj): + if obj.compiler.compiler_type != 'unix': + return False + + cc = obj.compiler.compiler[0] + if "gcc" not in cc: + return False + + # will print something like '4.2.1\n' + out = subprocess.run([cc, '-dumpversion'], stdout=subprocess.PIPE, + stderr=subprocess.PIPE, universal_newlines=True) + # -std=c99 is default from this version on + if LooseVersion(out.stdout) >= LooseVersion('5.0'): + return False + return True + + class new_build_clib(build_clib): + def build_a_library(self, build_info, lib_name, libraries): + if _needs_gcc_c99_flag(self): + build_info['extra_cflags'] = ['-std=c99'] + build_info['extra_cxxflags'] = ['-std=c++11'] + build_clib.build_a_library(self, build_info, lib_name, libraries) + + class new_build_ext(build_ext): + def build_extension(self, ext): + if _needs_gcc_c99_flag(self): + if '-std=c99' not in ext.extra_compile_args: + ext.extra_compile_args.append('-std=c99') + build_ext.build_extension(self, ext) + return new_build_clib, new_build_ext def generate_cython(): cwd = os.path.abspath(os.path.dirname(__file__)) print("Cythonizing sources") - p = subprocess.call([sys.executable, - os.path.join(cwd, 'tools', 'cythonize.py'), - 'numpy/random'], - cwd=cwd) - if p != 0: - raise RuntimeError("Running cythonize failed!") + for d in ('random',): + p = subprocess.call([sys.executable, + os.path.join(cwd, 'tools', 'cythonize.py'), + 'numpy/{0}'.format(d)], + cwd=cwd) + if p != 0: + raise RuntimeError("Running cythonize failed!") def parse_setuppy_commands(): @@ -230,7 +254,8 @@ def parse_setuppy_commands(): '--maintainer', '--maintainer-email', '--contact', '--contact-email', '--url', '--license', '--description', '--long-description', '--platforms', '--classifiers', - '--keywords', '--provides', '--requires', '--obsoletes'] + '--keywords', '--provides', '--requires', '--obsoletes', + 'version',] for command in info_commands: if command in args: @@ -241,7 +266,8 @@ def parse_setuppy_commands(): # below and not standalone. Hence they're not added to good_commands. good_commands = ('develop', 'sdist', 'build', 'build_ext', 'build_py', 'build_clib', 'build_scripts', 'bdist_wheel', 'bdist_rpm', - 'bdist_wininst', 'bdist_msi', 'bdist_mpkg') + 'bdist_wininst', 'bdist_msi', 'bdist_mpkg', 'build_src', + 'bdist_egg') for command in good_commands: if command in args: @@ -279,7 +305,6 @@ def parse_setuppy_commands(): """)) return False - # The following commands aren't supported. They can only be executed when # the user explicitly adds a --force command-line argument. bad_commands = dict( @@ -297,8 +322,6 @@ def parse_setuppy_commands(): Instead, build what you want to upload and upload those files with `twine upload -s <filenames>` instead. """, - upload_docs="`setup.py upload_docs` is not supported", - easy_install="`setup.py easy_install` is not supported", clean=""" `setup.py clean` is not supported, use one of the following instead: @@ -306,10 +329,6 @@ def parse_setuppy_commands(): - `git clean -Xdf` (cleans all versioned files, doesn't touch files that aren't checked into the git repo) """, - check="`setup.py check` is not supported", - register="`setup.py register` is not supported", - bdist_dumb="`setup.py bdist_dumb` is not supported", - bdist="`setup.py bdist` is not supported", build_sphinx=""" `setup.py build_sphinx` is not supported, use the Makefile under doc/""", @@ -317,8 +336,8 @@ def parse_setuppy_commands(): ) bad_commands['nosetests'] = bad_commands['test'] for command in ('upload_docs', 'easy_install', 'bdist', 'bdist_dumb', - 'register', 'check', 'install_data', 'install_headers', - 'install_lib', 'install_scripts', ): + 'register', 'check', 'install_data', 'install_headers', + 'install_lib', 'install_scripts', ): bad_commands[command] = "`setup.py %s` is not supported" % command for command in bad_commands.keys(): @@ -330,49 +349,69 @@ def parse_setuppy_commands(): # Commands that do more than print info, but also don't need Cython and # template parsing. - other_commands = ['egg_info', 'install_egg_info', 'rotate'] + other_commands = ['egg_info', 'install_egg_info', 'rotate', 'dist_info'] for command in other_commands: if command in args: return False # If we got here, we didn't detect what setup.py command was given - import warnings - warnings.warn("Unrecognized setuptools command, proceeding with " - "generating Cython sources and expanding templates", stacklevel=2) - return True + raise RuntimeError("Unrecognized setuptools command: {}".format(args)) + + +def get_docs_url(): + if 'dev' in VERSION: + return "https://numpy.org/devdocs" + else: + # For releases, this URL ends up on pypi. + # By pinning the version, users looking at old PyPI releases can get + # to the associated docs easily. + return "https://numpy.org/doc/{}.{}".format(MAJOR, MINOR) def setup_package(): - src_path = os.path.dirname(os.path.abspath(sys.argv[0])) + src_path = os.path.dirname(os.path.abspath(__file__)) old_path = os.getcwd() os.chdir(src_path) sys.path.insert(0, src_path) - # Rewrite the version file everytime - write_version_py() + # The f2py scripts that will be installed + if sys.platform == 'win32': + f2py_cmds = [ + 'f2py = numpy.f2py.f2py2e:main', + ] + else: + f2py_cmds = [ + 'f2py = numpy.f2py.f2py2e:main', + 'f2py%s = numpy.f2py.f2py2e:main' % sys.version_info[:1], + 'f2py%s.%s = numpy.f2py.f2py2e:main' % sys.version_info[:2], + ] + cmdclass["sdist"] = sdist_checked metadata = dict( - name = 'numpy', - maintainer = "NumPy Developers", - maintainer_email = "numpy-discussion@python.org", - description = DOCLINES[0], - long_description = "\n".join(DOCLINES[2:]), - url = "http://www.numpy.org", - author = "Travis E. Oliphant et al.", - download_url = "https://pypi.python.org/pypi/numpy", - license = 'BSD', + name='numpy', + maintainer="NumPy Developers", + maintainer_email="numpy-discussion@python.org", + description=DOCLINES[0], + long_description="\n".join(DOCLINES[2:]), + url="https://www.numpy.org", + author="Travis E. Oliphant et al.", + download_url="https://pypi.python.org/pypi/numpy", + project_urls={ + "Bug Tracker": "https://github.com/numpy/numpy/issues", + "Documentation": get_docs_url(), + "Source Code": "https://github.com/numpy/numpy", + }, + license='BSD', classifiers=[_f for _f in CLASSIFIERS.split('\n') if _f], - platforms = ["Windows", "Linux", "Solaris", "Mac OS-X", "Unix"], - test_suite='nose.collector', - cmdclass={"sdist": sdist_checked}, - python_requires='>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*', + platforms=["Windows", "Linux", "Solaris", "Mac OS-X", "Unix"], + test_suite='pytest', + version=versioneer.get_version(), + cmdclass=cmdclass, + python_requires='>=3.8', zip_safe=False, entry_points={ - 'console_scripts': [ - 'f2py = numpy.f2py.__main__:main', - 'conv-template = numpy.distutils.conv_template:main', - 'from-template = numpy.distutils.from_template:main', - ] + 'console_scripts': f2py_cmds, + 'array_api': ['numpy = numpy.array_api'], }, ) @@ -383,19 +422,21 @@ def setup_package(): # Raise errors for unsupported commands, improve help output, etc. run_build = parse_setuppy_commands() - from setuptools import setup if run_build: + # patches distutils, even though we don't use it + #from setuptools import setup from numpy.distutils.core import setup - cwd = os.path.abspath(os.path.dirname(__file__)) - if not os.path.exists(os.path.join(cwd, 'PKG-INFO')): - # Generate Cython sources, unless building from source release + + if 'sdist' not in sys.argv: + # Generate Cython sources, unless we're generating an sdist generate_cython() metadata['configuration'] = configuration + # Customize extension building + cmdclass['build_clib'], cmdclass['build_ext'] = get_build_overrides() else: - # Version number is added to metadata inside configuration() if build - # is run. - metadata['version'] = get_version_info()[0] + #from numpy.distutils.core import setup + from setuptools import setup try: setup(**metadata) diff --git a/site.cfg.example b/site.cfg.example index 21609a3325ea..1a6b36d2c6eb 100644 --- a/site.cfg.example +++ b/site.cfg.example @@ -4,13 +4,13 @@ # packages will use all sections so you should leave out sections that your # package does not use. -# To assist automatic installation like easy_install, the user's home directory +# To assist automatic installation like pip, the user's home directory # will also be checked for the file ~/.numpy-site.cfg . # The format of the file is that of the standard library's ConfigParser module. -# No interpolation is allowed, RawConfigParser class being used to load it. +# No interpolation is allowed; the RawConfigParser class is being used to load it. # -# http://docs.python.org/3/library/configparser.html +# https://docs.python.org/library/configparser.html # # Each section defines settings that apply to one particular dependency. Some of # the settings are general and apply to nearly any section and are defined here. @@ -21,13 +21,14 @@ # with. Note that these should be just the names, not the filenames. For # example, the file "libfoo.so" would become simply "foo". # libraries = lapack,f77blas,cblas,atlas +# This setting is available for *all* sections. # # library_dirs # List of directories to add to the library search path when compiling # extensions with this dependency. Use the character given by os.pathsep # to separate the items in the list. Note that this character is known to # vary on some unix-like systems; if a colon does not work, try a comma. -# This also applies to include_dirs and src_dirs (see below). +# This also applies to include_dirs. # On UN*X-type systems (OS X, most BSD and Linux systems): # library_dirs = /usr/lib:/usr/local/lib # On Windows: @@ -39,15 +40,6 @@ # List of directories to add to the header file search path. # include_dirs = /usr/include:/usr/local/include # -# src_dirs -# List of directories that contain extracted source code for the -# dependency. For some dependencies, numpy.distutils will be able to build -# them from source if binaries cannot be found. The FORTRAN BLAS and -# LAPACK libraries are one example. However, most dependencies are more -# complicated and require actual installation that you need to do -# yourself. -# src_dirs = /home/rkern/src/BLAS_SRC:/home/rkern/src/LAPACK_SRC -# # search_static_first # Boolean (one of (0, false, no, off) for False or (1, true, yes, on) for # True) to tell numpy.distutils to prefer static libraries (.a) over @@ -55,7 +47,7 @@ # search_static_first = false # # runtime_library_dirs/rpath -# List of directories that contains the libraries that should be +# List of directories that contains the libraries that should be # used at runtime, thereby disregarding the LD_LIBRARY_PATH variable. # See 'library_dirs' for formatting on different platforms. # runtime_library_dirs = /opt/blas/lib:/opt/lapack/lib @@ -64,36 +56,39 @@ # # extra_compile_args # Add additional arguments to the compilation of sources. -# Simple variable with no parsing done. +# Split into arguments in a platform-appropriate way. # Provide a single line with all complete flags. # extra_compile_args = -g -ftree-vectorize # # extra_link_args # Add additional arguments when libraries/executables # are linked. -# Simple variable with no parsing done. +# Split into arguments in a platform-appropriate way. # Provide a single line with all complete flags. # extra_link_args = -lgfortran # # Defaults # ======== -# The settings given here will apply to all other sections if not overridden. +# The settings here will apply to all sections as general defaults # This is a good place to add general library and include directories like # /usr/local/{lib,include} -# -#[ALL] -#library_dirs = /usr/local/lib -#include_dirs = /usr/local/include -# +# These settings apply when they are not overridden in the sections below. +# Note that the standard paths (e.g. `/usr/lib`) are not searched if you +# override these settings, unless they are explicitly included. +# The ``:`` is os.pathsep, which is ``;`` on windows +#[DEFAULT] +#library_dirs = /usr/local/lib64:/usr/local/lib:/usr/lib64:/usr/lib +#include_dirs = /usr/local/include:/usr/include + -# Atlas +# ATLAS # ----- -# Atlas is an open source optimized implementation of the BLAS and Lapack -# routines. NumPy will try to build against Atlas by default when available in -# the system library dirs. To build numpy against a custom installation of -# Atlas you can add an explicit section such as the following. Here we assume -# that Atlas was configured with ``prefix=/opt/atlas``. +# ATLAS is an open source optimized implementation of the BLAS and LAPACK +# routines. NumPy will try to build against ATLAS by default when available in +# the system library dirs. To build NumPy against a custom installation of +# ATLAS you can add an explicit section such as the following. Here we assume +# that ATLAS was configured with ``prefix=/opt/atlas``. # # [atlas] # library_dirs = /opt/atlas/lib @@ -101,20 +96,20 @@ # OpenBLAS # -------- -# OpenBLAS is another open source optimized implementation of BLAS and Lapack -# and can be seen as an alternative to Atlas. To build numpy against OpenBLAS -# instead of Atlas, use this section instead of the above, adjusting as needed +# OpenBLAS is another open source optimized implementation of BLAS and LAPACK +# and can be seen as an alternative to ATLAS. To build NumPy against OpenBLAS +# instead of ATLAS, use this section instead of the above, adjusting as needed # for your configuration (in the following example we installed OpenBLAS with # ``make install PREFIX=/opt/OpenBLAS``. # OpenBLAS is generically installed as a shared library, to force the OpenBLAS -# library linked to also be used at runtime you can utilize the +# library linked to also be used at runtime you can utilize the # runtime_library_dirs variable. # # **Warning**: OpenBLAS, by default, is built in multithreaded mode. Due to the # way Python's multiprocessing is implemented, a multithreaded OpenBLAS can # cause programs using both to hang as soon as a worker process is forked on # POSIX systems (Linux, Mac). -# This is fixed in Openblas 0.2.9 for the pthread build, the OpenMP build using +# This is fixed in OpenBLAS 0.2.9 for the pthread build, the OpenMP build using # GNU openmp is as of gcc-4.9 not fixed yet. # Python 3.4 will introduce a new feature in multiprocessing, called the # "forkserver", which solves this problem. For older versions, make sure @@ -122,7 +117,7 @@ # multiprocessing. # (This problem does not exist with multithreaded ATLAS.) # -# http://docs.python.org/3.4/library/multiprocessing.html#contexts-and-start-methods +# https://docs.python.org/library/multiprocessing.html#contexts-and-start-methods # https://github.com/xianyi/OpenBLAS/issues/294 # # [openblas] @@ -131,11 +126,56 @@ # include_dirs = /opt/OpenBLAS/include # runtime_library_dirs = /opt/OpenBLAS/lib +# OpenBLAS (64-bit with suffix) +# ----------------------------- +# OpenBLAS can be compiled with 64-bit integer size and symbol suffix '64_' +# (INTERFACE64=1 SYMBOLSUFFIX=64_). OpenBLAS built with this setting are also +# provided by some Linux distributions (e.g. Fedora's 64-bit openblas packages). +# This is an emerging "standard" for 64-bit BLAS/LAPACK, avoiding symbol clashes +# with 32-bit BLAS/LAPACK. +# +# To build Numpy with such 64-bit BLAS/LAPACK, set environment +# variables NPY_USE_BLAS_ILP64=1, NPY_BLAS_ILP64_ORDER=openblas64_, +# NPY_LAPACK_ILP64_ORDER=openblas64_ at build time. +# +# See: +# https://github.com/xianyi/OpenBLAS/issues/646 +# +# [openblas64_] +# libraries = openblas64_ +# library_dirs = /opt/OpenBLAS/lib +# include_dirs = /opt/OpenBLAS/include +# runtime_library_dirs = /opt/OpenBLAS/lib + +# OpenBLAS (64-bit ILP64) +# ----------------------- +# It is possible to also use OpenBLAS compiled with 64-bit integer +# size (ILP64) but no symbol name changes. To do that, set the +# environment variables NPY_USE_BLAS_ILP64=1, +# NPY_BLAS_ILP64_ORDER=openblas_ilp64, +# NPY_LAPACK_ILP64_ORDER=openblas_ilp64 at build time. +# +# Note that mixing both 64-bit and 32-bit BLAS without symbol suffixes +# in the same application may cause problems due to symbol name +# clashes, especially with embedded Python interpreters. +# +# The name of the library file may vary on different systems, so you +# may need to check your specific OpenBLAS installation and +# uncomment and e.g. set ``libraries = openblas`` below. +# +# [openblas_ilp64] +# libraries = openblas64 +# library_dirs = /opt/OpenBLAS/lib +# include_dirs = /opt/OpenBLAS/include +# runtime_library_dirs = /opt/OpenBLAS/lib +# symbol_prefix = +# symbol_suffix = + # BLIS # ---- # BLIS (https://github.com/flame/blis) also provides a BLAS interface. It's a # relatively new library, its performance in some cases seems to match that of -# MKL and OpenBLAS, but it hasn't been benchmarked with NumPy or Scipy yet. +# MKL and OpenBLAS, but it hasn't been benchmarked with NumPy or SciPy yet. # # Notes on compiling BLIS itself: # - the CBLAS interface (needed by NumPy) isn't built by default; define @@ -152,75 +192,80 @@ # include_dirs = /home/username/blis/include/blis # runtime_library_dirs = /home/username/blis/lib +# libFLAME +# -------- +# libFLAME (https://www.cs.utexas.edu/~flame/web/libFLAME.html) provides a +# LAPACK interface. It's a relatively new library, its performance in some +# cases seems to match that of MKL and OpenBLAS. +# It hasn't been benchmarked with NumPy or SciPy yet. +# +# Notes on compiling libFLAME itself: +# - the LAPACK interface (needed by NumPy) isn't built by default; please +# configure with ``./configure --enable-lapack2flame``. +# +# [flame] +# libraries = flame +# library_dirs = /home/username/flame/lib +# runtime_library_dirs = /home/username/flame/lib + # MKL -#---- -# Intel MKL is Intel's very optimized yet proprietary implementation of BLAS and -# Lapack. Find the latest info on building numpy with Intel MKL in this article: +#---- +# Intel MKL is Intel's very optimized yet proprietary implementation of BLAS and +# LAPACK. Find the latest info on building NumPy with Intel MKL in this article: # https://software.intel.com/en-us/articles/numpyscipy-with-intel-mkl -# Assuming you installed the mkl in /opt/intel/compilers_and_libraries_2018/linux/mkl, -# for 64 bits code at Linux: -# [mkl] +# Assuming you installed the mkl in /opt/intel/compilers_and_libraries_2018/linux/mkl, +# for 64 bits code at Linux: +# [mkl] # library_dirs = /opt/intel/compilers_and_libraries_2018/linux/mkl/lib/intel64 -# include_dirs = /opt/intel/compilers_and_libraries_2018/linux/mkl/include -# mkl_libs = mkl_rt -# lapack_libs =  -# -# For 32 bit code at Linux: +# include_dirs = /opt/intel/compilers_and_libraries_2018/linux/mkl/include +# libraries = mkl_rt +# +# For 32 bit code at Linux: # [mkl] # library_dirs = /opt/intel/compilers_and_libraries_2018/linux/mkl/lib/ia32 -# include_dirs = /opt/intel/compilers_and_libraries_2018/linux/mkl/include -# mkl_libs = mkl_rt -# lapack_libs =  -# -# On win-64, the following options compiles numpy with the MKL library -# dynamically linked. -# [mkl] +# include_dirs = /opt/intel/compilers_and_libraries_2018/linux/mkl/include +# libraries = mkl_rt +# +# On win-64, the following options compiles NumPy with the MKL library +# dynamically linked. +# [mkl] # include_dirs = C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2018\windows\mkl\include # library_dirs = C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2018\windows\mkl\lib\intel64 -# mkl_libs = mkl_rt -# lapack_libs = - -# ACCELERATE -# ---------- -# Accelerate/vecLib is an OSX framework providing a BLAS and LAPACK implementations. -# -# [accelerate] -# libraries = Accelerate, vecLib -# #libraries = None +# libraries = mkl_rt # UMFPACK # ------- -# The UMFPACK library is used in scikits.umfpack to factor large sparse matrices. +# The UMFPACK library is used in scikits.umfpack to factor large sparse matrices. # It, in turn, depends on the AMD library for reordering the matrices for # better performance. Note that the AMD library has nothing to do with AMD # (Advanced Micro Devices), the CPU company. # -# UMFPACK is not used by numpy. +# UMFPACK is not used by NumPy. # -# http://www.cise.ufl.edu/research/sparse/umfpack/ -# http://www.cise.ufl.edu/research/sparse/amd/ -# http://scikits.appspot.com/umfpack +# https://www.cise.ufl.edu/research/sparse/umfpack/ +# https://www.cise.ufl.edu/research/sparse/amd/ +# https://scikit-umfpack.github.io/scikit-umfpack/ # #[amd] -#amd_libs = amd +#libraries = amd # #[umfpack] -#umfpack_libs = umfpack +#libraries = umfpack # FFT libraries # ------------- # There are two FFT libraries that we can configure here: FFTW (2 and 3) and djbfft. -# Note that these libraries are not used by for numpy or scipy. +# Note that these libraries are not used by NumPy or SciPy. # # http://fftw.org/ -# http://cr.yp.to/djbfft.html +# https://cr.yp.to/djbfft.html # # Given only this section, numpy.distutils will try to figure out which version # of FFTW you are using. #[fftw] #libraries = fftw3 # -# For djbfft, numpy.distutils will look for either djbfft.a or libdjbfft.a . +# For djbfft, numpy.distutils will look for either djbfft.a or libdjbfft.a . #[djbfft] #include_dirs = /usr/local/djbfft/include #library_dirs = /usr/local/djbfft/lib diff --git a/test_requirements.txt b/test_requirements.txt new file mode 100644 index 000000000000..e33649c4a37f --- /dev/null +++ b/test_requirements.txt @@ -0,0 +1,12 @@ +cython==0.29.24 +wheel==0.37.0 +setuptools==59.2.0 +hypothesis==6.24.1 +pytest==6.2.5 +pytz==2021.3 +pytest-cov==3.0.0 +# for numpy.random.test.test_extending +cffi; python_version < '3.10' +# For testing types. Notes on the restrictions: +# - Mypy relies on C API features not present in PyPy +mypy==0.930; platform_python_implementation != "PyPy" diff --git a/tools/allocation_tracking/alloc_hook.pyx b/tools/allocation_tracking/alloc_hook.pyx index d1e656f90254..eeefe1704a6c 100644 --- a/tools/allocation_tracking/alloc_hook.pyx +++ b/tools/allocation_tracking/alloc_hook.pyx @@ -22,7 +22,7 @@ cdef void pyhook(void *old, void *new, size_t size, void *user_data): PyLong_FromVoidPtr(new), size) -class NumpyAllocHook(object): +class NumpyAllocHook: def __init__(self, callback): self.callback = callback diff --git a/tools/allocation_tracking/setup.py b/tools/allocation_tracking/setup.py index a75c95e911e2..4462f9f4ec8c 100644 --- a/tools/allocation_tracking/setup.py +++ b/tools/allocation_tracking/setup.py @@ -1,5 +1,3 @@ -from __future__ import division, print_function - from distutils.core import setup from distutils.extension import Extension from Cython.Distutils import build_ext diff --git a/tools/allocation_tracking/sorttable.js b/tools/allocation_tracking/sorttable.js index 25bccb2b6b91..c9528873e011 100644 --- a/tools/allocation_tracking/sorttable.js +++ b/tools/allocation_tracking/sorttable.js @@ -2,7 +2,7 @@ SortTable version 2 7th April 2007 - Stuart Langridge, http://www.kryogenix.org/code/browser/sorttable/ + Stuart Langridge, https://www.kryogenix.org/code/browser/sorttable/ Instructions: Download this file @@ -11,7 +11,7 @@ Click on the headers to sort Thanks to many, many people for contributions and suggestions. - Licenced as X11: http://www.kryogenix.org/code/browser/licence.html + Licenced as X11: https://www.kryogenix.org/code/browser/licence.html This basically means: do what you want with it. */ @@ -301,7 +301,7 @@ sorttable = { shaker_sort: function(list, comp_func) { // A stable sort function to allow multi-level sorting of data - // see: http://en.wikipedia.org/wiki/Cocktail_sort + // see: https://en.wikipedia.org/wiki/Cocktail_shaker_sort // thanks to Joseph Nahmias var b = 0; var t = list.length - 1; @@ -441,7 +441,7 @@ fixEvent.stopPropagation = function() { /* forEach, version 1.0 Copyright 2006, Dean Edwards - License: http://www.opensource.org/licenses/mit-license.php + License: https://www.opensource.org/licenses/mit-license.php */ // array-like enumeration diff --git a/tools/allocation_tracking/track_allocations.py b/tools/allocation_tracking/track_allocations.py index d259938002f9..2a80d8f877ea 100644 --- a/tools/allocation_tracking/track_allocations.py +++ b/tools/allocation_tracking/track_allocations.py @@ -1,11 +1,9 @@ -from __future__ import division, absolute_import, print_function - import numpy as np import gc import inspect from alloc_hook import NumpyAllocHook -class AllocationTracker(object): +class AllocationTracker: def __init__(self, threshold=0): '''track numpy allocations of size threshold bytes or more.''' @@ -108,30 +106,29 @@ def check_line_changed(self): self.current_line = line def write_html(self, filename): - f = open(filename, "w") - f.write('<HTML><HEAD><script src="sorttable.js"></script></HEAD><BODY>\n') - f.write('<TABLE class="sortable" width=100%>\n') - f.write("<TR>\n") - cols = "event#,lineinfo,bytes allocated,bytes freed,#allocations,#frees,max memory usage,long lived bytes".split(',') - for header in cols: - f.write(" <TH>{0}</TH>".format(header)) - f.write("\n</TR>\n") - for idx, event in enumerate(self.allocation_trace): + with open(filename, "w") as f: + f.write('<HTML><HEAD><script src="sorttable.js"></script></HEAD><BODY>\n') + f.write('<TABLE class="sortable" width=100%>\n') f.write("<TR>\n") - event = [idx] + list(event) - for col, val in zip(cols, event): - if col == 'lineinfo': - # special handling - try: - filename, line, module, code, index = val - val = "{0}({1}): {2}".format(filename, line, code[index]) - except Exception: - # sometimes this info is not available (from eval()?) - val = str(val) - f.write(" <TD>{0}</TD>".format(val)) + cols = "event#,lineinfo,bytes allocated,bytes freed,#allocations,#frees,max memory usage,long lived bytes".split(',') + for header in cols: + f.write(" <TH>{0}</TH>".format(header)) f.write("\n</TR>\n") - f.write("</TABLE></BODY></HTML>\n") - f.close() + for idx, event in enumerate(self.allocation_trace): + f.write("<TR>\n") + event = [idx] + list(event) + for col, val in zip(cols, event): + if col == 'lineinfo': + # special handling + try: + filename, line, module, code, index = val + val = "{0}({1}): {2}".format(filename, line, code[index]) + except Exception: + # sometimes this info is not available (from eval()?) + val = str(val) + f.write(" <TD>{0}</TD>".format(val)) + f.write("\n</TR>\n") + f.write("</TABLE></BODY></HTML>\n") if __name__ == '__main__': diff --git a/tools/c_coverage/c_coverage_report.py b/tools/c_coverage/c_coverage_report.py index 327f6dc05b0e..bd3eeaee9776 100755 --- a/tools/c_coverage/c_coverage_report.py +++ b/tools/c_coverage/c_coverage_report.py @@ -1,12 +1,9 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """ A script to create C code-coverage reports based on the output of valgrind's callgrind tool. """ -from __future__ import division, absolute_import, print_function - -import optparse import os import re import sys @@ -145,39 +142,43 @@ def collect_stats(files, fd, pattern): if __name__ == '__main__': - parser = optparse.OptionParser( - usage="[options] callgrind_file(s)") - parser.add_option( - '-d', '--directory', dest='directory', - default='coverage', - help='Destination directory for output [default: coverage]') - parser.add_option( - '-p', '--pattern', dest='pattern', - default='numpy', - help='Regex pattern to match against source file paths [default: numpy]') - parser.add_option( - '-f', '--format', dest='format', default=[], - action='append', type='choice', choices=('text', 'html'), - help="Output format(s) to generate, may be 'text' or 'html' [default: both]") - (options, args) = parser.parse_args() + import argparse + + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + 'callgrind_file', nargs='+', + help='One or more callgrind files') + parser.add_argument( + '-d', '--directory', default='coverage', + help='Destination directory for output (default: %(default)s)') + parser.add_argument( + '-p', '--pattern', default='numpy', + help='Regex pattern to match against source file paths ' + '(default: %(default)s)') + parser.add_argument( + '-f', '--format', action='append', default=[], + choices=['text', 'html'], + help="Output format(s) to generate. " + "If option not provided, both will be generated.") + args = parser.parse_args() files = SourceFiles() - for log_file in args: + for log_file in args.callgrind_file: log_fd = open(log_file, 'r') - collect_stats(files, log_fd, options.pattern) + collect_stats(files, log_fd, args.pattern) log_fd.close() - if not os.path.exists(options.directory): - os.makedirs(options.directory) + if not os.path.exists(args.directory): + os.makedirs(args.directory) - if options.format == []: + if args.format == []: formats = ['text', 'html'] else: - formats = options.format + formats = args.format if 'text' in formats: - files.write_text(options.directory) + files.write_text(args.directory) if 'html' in formats: if not has_pygments: print("Pygments 0.11 or later is required to generate HTML") sys.exit(1) - files.write_html(options.directory) + files.write_html(args.directory) diff --git a/tools/changelog.py b/tools/changelog.py index 84e046c5f83c..444d96882216 100755 --- a/tools/changelog.py +++ b/tools/changelog.py @@ -1,5 +1,4 @@ -#!/usr/bin/env python -# -*- encoding:utf-8 -*- +#!/usr/bin/env python3 """ Script to generate contributor and pull request lists @@ -22,6 +21,7 @@ - gitpython - pygithub +- git >= 2.29.0 Some code was copied from scipy `tools/gh_list.py` and `tools/authors.py`. @@ -33,46 +33,53 @@ $ ./tools/announce $GITHUB v1.13.0..v1.14.0 > 1.14.0-changelog.rst """ -from __future__ import print_function, division - import os import sys import re -import codecs from git import Repo from github import Github -UTF8Writer = codecs.getwriter('utf8') -sys.stdout = UTF8Writer(sys.stdout) +if sys.version_info[:2] < (3, 6): + raise RuntimeError("Python version must be >= 3.6") + this_repo = Repo(os.path.join(os.path.dirname(__file__), "..")) author_msg =\ -u""" +""" A total of %d people contributed to this release. People with a "+" by their names contributed a patch for the first time. """ pull_request_msg =\ -u""" +""" A total of %d pull requests were merged for this release. """ + def get_authors(revision_range): - pat = u'^.*\\t(.*)$' lst_release, cur_release = [r.strip() for r in revision_range.split('..')] + authors_pat = r'^.*\t(.*)$' + + # authors and co-authors in current and previous releases. + grp1 = '--group=author' + grp2 = '--group=trailer:co-authored-by' + cur = this_repo.git.shortlog('-s', grp1, grp2, revision_range) + pre = this_repo.git.shortlog('-s', grp1, grp2, lst_release) + authors_cur = set(re.findall(authors_pat, cur, re.M)) + authors_pre = set(re.findall(authors_pat, pre, re.M)) - # authors, in current release and previous to current release. - cur = set(re.findall(pat, this_repo.git.shortlog('-s', revision_range), - re.M)) - pre = set(re.findall(pat, this_repo.git.shortlog('-s', lst_release), - re.M)) + # Ignore the bot Homu. + authors_cur.discard('Homu') + authors_pre.discard('Homu') - # Homu is the author of auto merges, clean him out. - cur.discard('Homu') - pre.discard('Homu') + # Ignore the bot dependabot-preview + authors_cur.discard('dependabot-preview') + authors_pre.discard('dependabot-preview') # Append '+' to new authors. - authors = [s + u' +' for s in cur - pre] + [s for s in cur & pre] + authors_new = [s + ' +' for s in authors_cur - authors_pre] + authors_old = [s for s in authors_cur & authors_pre] + authors = authors_new + authors_old authors.sort() return authors @@ -83,18 +90,18 @@ def get_pull_requests(repo, revision_range): # From regular merges merges = this_repo.git.log( '--oneline', '--merges', revision_range) - issues = re.findall(u"Merge pull request \\#(\\d*)", merges) + issues = re.findall(r"Merge pull request \#(\d*)", merges) prnums.extend(int(s) for s in issues) # From Homu merges (Auto merges) - issues = re. findall(u"Auto merge of \\#(\\d*)", merges) + issues = re. findall(r"Auto merge of \#(\d*)", merges) prnums.extend(int(s) for s in issues) # From fast forward squash-merges commits = this_repo.git.log( '--oneline', '--no-merges', '--first-parent', revision_range) - issues = re.findall(u'^.*\\(\\#(\\d+)\\)$', commits, re.M) - prnums.extend(int(s) for s in issues) + issues = re.findall(r'^.*\((\#|gh-|gh-\#)(\d+)\)$', commits, re.M) + prnums.extend(int(s[1]) for s in issues) # get PR data from github repo prnums.sort() @@ -110,31 +117,31 @@ def main(token, revision_range): # document authors authors = get_authors(revision_range) - heading = u"Contributors" + heading = "Contributors" print() print(heading) - print(u"="*len(heading)) + print("="*len(heading)) print(author_msg % len(authors)) for s in authors: - print(u'* ' + s) + print('* ' + s) # document pull requests pull_requests = get_pull_requests(github_repo, revision_range) - heading = u"Pull requests merged" - pull_msg = u"* `#{0} <{1}>`__: {2}" + heading = "Pull requests merged" + pull_msg = "* `#{0} <{1}>`__: {2}" print() print(heading) - print(u"="*len(heading)) + print("="*len(heading)) print(pull_request_msg % len(pull_requests)) for pull in pull_requests: - title = re.sub(u"\\s+", u" ", pull.title.strip()) + title = re.sub(r"\s+", " ", pull.title.strip()) if len(title) > 60: - remainder = re.sub(u"\\s.*$", u"...", title[60:]) + remainder = re.sub(r"\s.*$", "...", title[60:]) if len(remainder) > 20: - remainder = title[:80] + u"..." + remainder = title[:80] + "..." else: title = title[:60] + remainder print(pull_msg.format(pull.number, pull.html_url, title)) diff --git a/tools/ci/appveyor/requirements.txt b/tools/ci/appveyor/requirements.txt deleted file mode 100644 index fba8260da79c..000000000000 --- a/tools/ci/appveyor/requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -cython -nose -pytest-timeout -pytest-xdist -pytest-env -pytest-faulthandler diff --git a/tools/ci/push_docs_to_repo.py b/tools/ci/push_docs_to_repo.py index a98966880d0f..058f748ec1af 100755 --- a/tools/ci/push_docs_to_repo.py +++ b/tools/ci/push_docs_to_repo.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import argparse import subprocess @@ -45,6 +45,9 @@ def run(cmd, stdout=True): os.chdir(workdir) run(['git', 'init']) +# ensure the working branch is called "main" +# (`--initial-branch=main` appared to have failed on older git versions): +run(['git', 'checkout', '-b', 'main']) run(['git', 'remote', 'add', 'origin', args.remote]) run(['git', 'config', '--local', 'user.name', args.committer]) run(['git', 'config', '--local', 'user.email', args.email]) @@ -56,7 +59,7 @@ def run(cmd, stdout=True): print('- uploading as %s <%s>' % (args.committer, args.email)) if args.force: - run(['git', 'push', 'origin', 'master', '--force']) + run(['git', 'push', 'origin', 'main', '--force']) else: print('\n!! No `--force` argument specified; aborting') print('!! Before enabling that flag, make sure you know what it does\n') diff --git a/tools/ci/test_all_newsfragments_used.py b/tools/ci/test_all_newsfragments_used.py new file mode 100755 index 000000000000..62c9a05f95da --- /dev/null +++ b/tools/ci/test_all_newsfragments_used.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 + +import sys +import toml +import os + +path = toml.load("pyproject.toml")["tool"]["towncrier"]["directory"] + +fragments = os.listdir(path) +fragments.remove("README.rst") +fragments.remove("template.rst") + +if fragments: + print("The following files were not found by towncrier:") + print(" " + "\n ".join(fragments)) + sys.exit(1) diff --git a/tools/commitstats.py b/tools/commitstats.py index a35d7b724df6..534f0a1b8416 100644 --- a/tools/commitstats.py +++ b/tools/commitstats.py @@ -1,12 +1,10 @@ -from __future__ import division, absolute_import, print_function - # Run svn log -l <some number> import re import numpy as np import os -names = re.compile(r'r\d+\s[|]\s(.*)\s[|]\s200') +names = re.compile(r'r\d+\s\|\s(.*)\s\|\s200') def get_count(filename, repo): mystr = open(filename).read() diff --git a/tools/cythonize.py b/tools/cythonize.py index 6ef908958290..c06962cf98a9 100755 --- a/tools/cythonize.py +++ b/tools/cythonize.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """ cythonize Cythonize pyx files into C files as needed. @@ -30,8 +30,6 @@ operates on the Cython .pyx files. """ -from __future__ import division, print_function, absolute_import - import os import re import sys @@ -42,45 +40,42 @@ DEFAULT_ROOT = 'numpy' VENDOR = 'NumPy' -# WindowsError is not defined on unix systems -try: - WindowsError -except NameError: - WindowsError = None - # # Rules # def process_pyx(fromfile, tofile): + flags = ['-3', '--fast-fail'] + if tofile.endswith('.cxx'): + flags.append('--cplus') + try: + # try the cython in the installed python first (somewhat related to scipy/scipy#2397) + import Cython from Cython.Compiler.Version import version as cython_version + except ImportError as e: + # The `cython` command need not point to the version installed in the + # Python running this script, so raise an error to avoid the chance of + # using the wrong version of Cython. + msg = 'Cython needs to be installed in Python as a module' + raise OSError(msg) from e + else: + # check the version, and invoke through python from distutils.version import LooseVersion - if LooseVersion(cython_version) < LooseVersion('0.19'): - raise Exception('Building %s requires Cython >= 0.19' % VENDOR) - except ImportError: - pass + # Cython 0.29.21 is required for Python 3.9 and there are + # other fixes in the 0.29 series that are needed even for earlier + # Python versions. + # Note: keep in sync with that in pyproject.toml + # Update for Python 3.10 + required_version = LooseVersion('0.29.24') - flags = ['--fast-fail'] - if tofile.endswith('.cxx'): - flags += ['--cplus'] + if LooseVersion(cython_version) < required_version: + cython_path = Cython.__file__ + raise RuntimeError(f'Building {VENDOR} requires Cython >= {required_version}' + f', found {cython_version} at {cython_path}') + subprocess.check_call( + [sys.executable, '-m', 'cython'] + flags + ["-o", tofile, fromfile]) - try: - try: - r = subprocess.call(['cython'] + flags + ["-o", tofile, fromfile]) - if r != 0: - raise Exception('Cython failed') - except OSError: - # There are ways of installing Cython that don't result in a cython - # executable on the path, see gh-2397. - r = subprocess.call([sys.executable, '-c', - 'import sys; from Cython.Compiler.Main import ' - 'setuptools_main as main; sys.exit(main())'] + flags + - ["-o", tofile, fromfile]) - if r != 0: - raise Exception('Cython failed') - except OSError: - raise OSError('Cython needs to be installed') def process_tempita_pyx(fromfile, tofile): import npy_tempita as tempita @@ -95,6 +90,17 @@ def process_tempita_pyx(fromfile, tofile): process_pyx(pyxfile, tofile) +def process_tempita_pyd(fromfile, tofile): + import npy_tempita as tempita + + assert fromfile.endswith('.pxd.in') + assert tofile.endswith('.pxd') + with open(fromfile, "r") as f: + tmpl = f.read() + pyxcontent = tempita.sub(tmpl) + with open(tofile, "w") as f: + f.write(pyxcontent) + def process_tempita_pxi(fromfile, tofile): import npy_tempita as tempita @@ -106,16 +112,30 @@ def process_tempita_pxi(fromfile, tofile): with open(tofile, "w") as f: f.write(pyxcontent) +def process_tempita_pxd(fromfile, tofile): + import npy_tempita as tempita + + assert fromfile.endswith('.pxd.in') + assert tofile.endswith('.pxd') + with open(fromfile, "r") as f: + tmpl = f.read() + pyxcontent = tempita.sub(tmpl) + with open(tofile, "w") as f: + f.write(pyxcontent) + rules = { - # fromext : function - '.pyx' : process_pyx, - '.pyx.in' : process_tempita_pyx + # fromext : function, toext + '.pyx' : (process_pyx, '.c'), + '.pyx.in' : (process_tempita_pyx, '.c'), + '.pxi.in' : (process_tempita_pxi, '.pxi'), + '.pxd.in' : (process_tempita_pxd, '.pxd'), + '.pyd.in' : (process_tempita_pyd, '.pyd'), } # # Hash db # def load_hashes(filename): - # Return { filename : (sha1 of input, sha1 of output) } + # Return { filename : (sha256 of input, sha256 of output) } if os.path.isfile(filename): hashes = {} with open(filename, 'r') as f: @@ -131,8 +151,8 @@ def save_hashes(hash_db, filename): for key, value in sorted(hash_db.items()): f.write("%s %s %s\n" % (key, value[0], value[1])) -def sha1_of_file(filename): - h = hashlib.sha1() +def sha256_of_file(filename): + h = hashlib.sha256() with open(filename, "rb") as f: h.update(f.read()) return h.hexdigest() @@ -148,8 +168,8 @@ def normpath(path): return path def get_hash(frompath, topath): - from_hash = sha1_of_file(frompath) - to_hash = sha1_of_file(topath) if os.path.exists(topath) else None + from_hash = sha256_of_file(frompath) + to_hash = sha256_of_file(topath) if os.path.exists(topath) else None return (from_hash, to_hash) def process(path, fromfile, tofile, processor_function, hash_db): @@ -157,13 +177,13 @@ def process(path, fromfile, tofile, processor_function, hash_db): fulltopath = os.path.join(path, tofile) current_hash = get_hash(fullfrompath, fulltopath) if current_hash == hash_db.get(normpath(fullfrompath), None): - print('%s has not changed' % fullfrompath) + print(f'{fullfrompath} has not changed') return orig_cwd = os.getcwd() try: os.chdir(path) - print('Processing %s' % fullfrompath) + print(f'Processing {fullfrompath}') processor_function(fromfile, tofile) finally: os.chdir(orig_cwd) @@ -175,38 +195,32 @@ def process(path, fromfile, tofile, processor_function, hash_db): def find_process_files(root_dir): hash_db = load_hashes(HASH_FILE) - for cur_dir, dirs, files in os.walk(root_dir): - # .pxi or .pxi.in files are most likely dependencies for - # .pyx files, so we need to process them first - files.sort(key=lambda name: (name.endswith('.pxi') or - name.endswith('.pxi.in')), - reverse=True) - - for filename in files: - in_file = os.path.join(cur_dir, filename + ".in") - if filename.endswith('.pyx') and os.path.isfile(in_file): - continue - elif filename.endswith('.pxi.in'): - toext = '.pxi' - fromext = '.pxi.in' + files = [x for x in os.listdir(root_dir) if not os.path.isdir(x)] + # .pxi or .pxi.in files are most likely dependencies for + # .pyx files, so we need to process them first + files.sort(key=lambda name: (name.endswith('.pxi') or + name.endswith('.pxi.in') or + name.endswith('.pxd.in')), + reverse=True) + + for filename in files: + in_file = os.path.join(root_dir, filename + ".in") + for fromext, value in rules.items(): + if filename.endswith(fromext): + if not value: + break + function, toext = value + if toext == '.c': + with open(os.path.join(root_dir, filename), 'rb') as f: + data = f.read() + m = re.search(br"^\s*#\s*distutils:\s*language\s*=\s*c\+\+\s*$", data, re.I|re.M) + if m: + toext = ".cxx" fromfile = filename - function = process_tempita_pxi tofile = filename[:-len(fromext)] + toext - process(cur_dir, fromfile, tofile, function, hash_db) + process(root_dir, fromfile, tofile, function, hash_db) save_hashes(hash_db, HASH_FILE) - else: - for fromext, function in rules.items(): - if filename.endswith(fromext): - toext = ".c" - with open(os.path.join(cur_dir, filename), 'rb') as f: - data = f.read() - m = re.search(br"^\s*#\s*distutils:\s*language\s*=\s*c\+\+\s*$", data, re.I|re.M) - if m: - toext = ".cxx" - fromfile = filename - tofile = filename[:-len(fromext)] + toext - process(cur_dir, fromfile, tofile, function, hash_db) - save_hashes(hash_db, HASH_FILE) + break def main(): try: diff --git a/tools/download-wheels.py b/tools/download-wheels.py new file mode 100644 index 000000000000..dd066d9adba9 --- /dev/null +++ b/tools/download-wheels.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python3 +""" +Script to download NumPy wheels from the Anaconda staging area. + +Usage:: + + $ ./tools/download-wheels.py <version> -w <optional-wheelhouse> + +The default wheelhouse is ``release/installers``. + +Dependencies +------------ + +- beautifulsoup4 +- urllib3 + +Examples +-------- + +While in the repository root:: + + $ python tools/download-wheels.py 1.19.0 + $ python tools/download-wheels.py 1.19.0 -w ~/wheelhouse + +""" +import os +import re +import shutil +import argparse + +import urllib3 +from bs4 import BeautifulSoup + +__version__ = '0.1' + +# Edit these for other projects. +STAGING_URL = 'https://anaconda.org/multibuild-wheels-staging/numpy' +PREFIX = 'numpy' + + +def get_wheel_names(version): + """ Get wheel names from Anaconda HTML directory. + + This looks in the Anaconda multibuild-wheels-staging page and + parses the HTML to get all the wheel names for a release version. + + Parameters + ---------- + version : str + The release version. For instance, "1.18.3". + + """ + http = urllib3.PoolManager(cert_reqs='CERT_REQUIRED') + tmpl = re.compile(rf"^.*{PREFIX}-{version}-.*\.whl$") + index_url = f"{STAGING_URL}/files" + index_html = http.request('GET', index_url) + soup = BeautifulSoup(index_html.data, 'html.parser') + return soup.findAll(text=tmpl) + + +def download_wheels(version, wheelhouse): + """Download release wheels. + + The release wheels for the given NumPy version are downloaded + into the given directory. + + Parameters + ---------- + version : str + The release version. For instance, "1.18.3". + wheelhouse : str + Directory in which to download the wheels. + + """ + http = urllib3.PoolManager(cert_reqs='CERT_REQUIRED') + wheel_names = get_wheel_names(version) + + for i, wheel_name in enumerate(wheel_names): + wheel_url = f"{STAGING_URL}/{version}/download/{wheel_name}" + wheel_path = os.path.join(wheelhouse, wheel_name) + with open(wheel_path, 'wb') as f: + with http.request('GET', wheel_url, preload_content=False,) as r: + print(f"{i + 1:<4}{wheel_name}") + shutil.copyfileobj(r, f) + print(f"\nTotal files downloaded: {len(wheel_names)}") + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument( + "version", + help="NumPy version to download.") + parser.add_argument( + "-w", "--wheelhouse", + default=os.path.join(os.getcwd(), "release", "installers"), + help="Directory in which to store downloaded wheels\n" + "[defaults to <cwd>/release/installers]") + + args = parser.parse_args() + + wheelhouse = os.path.expanduser(args.wheelhouse) + if not os.path.isdir(wheelhouse): + raise RuntimeError( + f"{wheelhouse} wheelhouse directory is not present." + " Perhaps you need to use the '-w' flag to specify one.") + + download_wheels(args.version, wheelhouse) diff --git a/tools/find_deprecated_escaped_characters.py b/tools/find_deprecated_escaped_characters.py index 6f90001ca6b1..22efaae65b69 100644 --- a/tools/find_deprecated_escaped_characters.py +++ b/tools/find_deprecated_escaped_characters.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python +#!/usr/bin/env python3 r""" Look for escape sequences deprecated in Python 3.6. @@ -7,8 +7,6 @@ be written as '\\(' or r'\('. """ -from __future__ import division, absolute_import, print_function - import sys def main(root): diff --git a/tools/functions_missing_types.py b/tools/functions_missing_types.py new file mode 100755 index 000000000000..0461aabd3634 --- /dev/null +++ b/tools/functions_missing_types.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python +"""Find the functions in a module missing type annotations. + +To use it run + +./functions_missing_types.py <module> + +and it will print out a list of functions in the module that don't +have types. + +""" +import argparse +import ast +import importlib +import os + +NUMPY_ROOT = os.path.dirname(os.path.join( + os.path.abspath(__file__), "..", +)) + +# Technically "public" functions (they don't start with an underscore) +# that we don't want to include. +EXCLUDE_LIST = { + "numpy": { + # Stdlib modules in the namespace by accident + "absolute_import", + "division", + "print_function", + "warnings", + "sys", + "os", + "math", + # Accidentally public, deprecated, or shouldn't be used + "Tester", + "alen", + "add_docstring", + "add_newdoc", + "add_newdoc_ufunc", + "core", + "compat", + "fastCopyAndTranspose", + "get_array_wrap", + "int_asbuffer", + "numarray", + "oldnumeric", + "safe_eval", + "set_numeric_ops", + "test", + "typeDict", + # Builtins + "bool", + "complex", + "float", + "int", + "long", + "object", + "str", + "unicode", + # More standard names should be preferred + "alltrue", # all + "sometrue", # any + } +} + + +class FindAttributes(ast.NodeVisitor): + """Find top-level attributes/functions/classes in stubs files. + + Do this by walking the stubs ast. See e.g. + + https://greentreesnakes.readthedocs.io/en/latest/index.html + + for more information on working with Python's ast. + + """ + + def __init__(self): + self.attributes = set() + + def visit_FunctionDef(self, node): + if node.name == "__getattr__": + # Not really a module member. + return + self.attributes.add(node.name) + # Do not call self.generic_visit; we are only interested in + # top-level functions. + return + + def visit_ClassDef(self, node): + if not node.name.startswith("_"): + self.attributes.add(node.name) + return + + def visit_AnnAssign(self, node): + self.attributes.add(node.target.id) + + +def find_missing(module_name): + module_path = os.path.join( + NUMPY_ROOT, + module_name.replace(".", os.sep), + "__init__.pyi", + ) + + module = importlib.import_module(module_name) + module_attributes = { + attribute for attribute in dir(module) if not attribute.startswith("_") + } + + if os.path.isfile(module_path): + with open(module_path) as f: + tree = ast.parse(f.read()) + ast_visitor = FindAttributes() + ast_visitor.visit(tree) + stubs_attributes = ast_visitor.attributes + else: + # No stubs for this module yet. + stubs_attributes = set() + + exclude_list = EXCLUDE_LIST.get(module_name, set()) + + missing = module_attributes - stubs_attributes - exclude_list + print("\n".join(sorted(missing))) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("module") + args = parser.parse_args() + + find_missing(args.module) + + +if __name__ == "__main__": + main() diff --git a/tools/gitpod/Dockerfile b/tools/gitpod/Dockerfile new file mode 100644 index 000000000000..e2e0e1bc9571 --- /dev/null +++ b/tools/gitpod/Dockerfile @@ -0,0 +1,101 @@ +# +# Dockerfile for NumPy development +# +# Usage: +# ------- +# +# To make a local build of the container, from the 'Docker-dev' directory: +# docker build --rm -f "Dockerfile" -t <build-tag> "." +# +# To use the container use the following command. It assumes that you are in +# the root folder of the NumPy git repository, making it available as +# /home/numpy in the container. Whatever changes you make to that directory +# are visible in the host and container. +# The docker image is retrieved from the NumPy dockerhub repository +# +# docker run --rm -it -v $(pwd):/home/numpy numpy/numpy-dev:<image-tag> +# +# By default the container will activate the conda environment numpy-dev +# which contains all the dependencies needed for NumPy development +# +# To build NumPy run: python setup.py build_ext --inplace +# +# To run the tests use: python runtests.py +# +# This image is based on: Ubuntu 20.04 (focal) +# https://hub.docker.com/_/ubuntu/?tab=tags&name=focal +# OS/ARCH: linux/amd64 +FROM gitpod/workspace-base:latest + +ARG MAMBAFORGE_VERSION="4.10.0-0" +ARG CONDA_ENV=numpy-dev + + +# ---- Configure environment ---- +ENV CONDA_DIR=/home/gitpod/mambaforge3 \ + SHELL=/bin/bash +ENV PATH=${CONDA_DIR}/bin:$PATH \ + WORKSPACE=/workspace/numpy + + +# ----------------------------------------------------------------------------- +# ---- Creating as root - note: make sure to change to gitpod in the end ---- +USER root + +# hadolint ignore=DL3008 +RUN apt-get update && \ + apt-get install -yq --no-install-recommends \ + ca-certificates \ + dirmngr \ + dvisvgm \ + gnupg \ + gpg-agent \ + texlive-latex-extra \ + vim && \ + # this needs to be done after installing dirmngr + apt-key adv --keyserver keyserver.ubuntu.com --recv-key C99B11DEB97541F0 && \ + apt-add-repository https://cli.github.com/packages && \ + apt-get install -yq --no-install-recommends \ + gh && \ + locale-gen en_US.UTF-8 && \ + apt-get clean && \ + rm -rf /var/cache/apt/* &&\ + rm -rf /var/lib/apt/lists/* &&\ + rm -rf /tmp/* + +# Allows this Dockerfile to activate conda environments +SHELL ["/bin/bash", "--login", "-o", "pipefail", "-c"] + +# ----------------------------------------------------------------------------- +# ---- Installing mamba ---- +RUN wget -q -O mambaforge3.sh \ + "https://github.com/conda-forge/miniforge/releases/download/$MAMBAFORGE_VERSION/Mambaforge-$MAMBAFORGE_VERSION-Linux-x86_64.sh" && \ + bash mambaforge3.sh -p ${CONDA_DIR} -b && \ + rm mambaforge3.sh + +# ----------------------------------------------------------------------------- +# ---- Copy needed files ---- +# basic workspace configurations +COPY ./tools/gitpod/workspace_config /usr/local/bin/workspace_config + +RUN chmod a+rx /usr/local/bin/workspace_config && \ + workspace_config + +# Copy conda environment file into the container - this needs to exists inside +# the container to create a conda environment from it +COPY environment.yml /tmp/environment.yml + +# ----------------------------------------------------------------------------- +# ---- Create conda environment ---- +# Install NumPy dependencies +RUN mamba env create -f /tmp/environment.yml && \ + conda activate ${CONDA_ENV} && \ + mamba install ccache -y && \ + # needed for docs rendering later on + python -m pip install --no-cache-dir sphinx-autobuild && \ + conda clean --all -f -y && \ + rm -rf /tmp/* + +# ----------------------------------------------------------------------------- +# Always make sure we are not root +USER gitpod \ No newline at end of file diff --git a/tools/gitpod/gitpod.Dockerfile b/tools/gitpod/gitpod.Dockerfile new file mode 100644 index 000000000000..7894be5bc358 --- /dev/null +++ b/tools/gitpod/gitpod.Dockerfile @@ -0,0 +1,48 @@ +# Doing a local shallow clone - keeps the container secure +# and much slimmer than using COPY directly or making a +# remote clone +ARG BASE_CONTAINER="numpy/numpy-dev:latest" +FROM gitpod/workspace-base:latest as clone + +COPY --chown=gitpod . /tmp/numpy_repo + +# the clone should be deep enough for versioneer to work +RUN git clone --shallow-since=2021-05-22 file:////tmp/numpy_repo /tmp/numpy + +# ----------------------------------------------------------------------------- +# Using the numpy-dev Docker image as a base +# This way, we ensure we have all the needed compilers and dependencies +# while reducing the build time +FROM ${BASE_CONTAINER} as build + +# ----------------------------------------------------------------------------- +USER root + +# ----------------------------------------------------------------------------- +# ---- ENV variables ---- +# ---- Directories needed ---- +ENV WORKSPACE=/workspace/numpy/ \ + CONDA_ENV=numpy-dev + +# Allows this Dockerfile to activate conda environments +SHELL ["/bin/bash", "--login", "-o", "pipefail", "-c"] + +# Copy over the shallow clone +COPY --from=clone --chown=gitpod /tmp/numpy ${WORKSPACE} + +# Everything happens in the /workspace/numpy directory +WORKDIR ${WORKSPACE} + +# Build numpy to populate the cache used by ccache +RUN git submodule update --init --depth=1 -- numpy/core/src/umath/svml +RUN conda activate ${CONDA_ENV} && \ + python setup.py build_ext --inplace && \ + ccache -s + +# Gitpod will load the repository into /workspace/numpy. We remove the +# directory from the image to prevent conflicts +RUN rm -rf ${WORKSPACE} + +# ----------------------------------------------------------------------------- +# Always return to non privileged user +USER gitpod diff --git a/tools/gitpod/settings.json b/tools/gitpod/settings.json new file mode 100644 index 000000000000..8f070c04c05a --- /dev/null +++ b/tools/gitpod/settings.json @@ -0,0 +1,9 @@ +{ + "restructuredtext.languageServer.disabled": true, + "restructuredtext.builtDocumentationPath": "${workspaceRoot}/doc/build/html", + "restructuredtext.confPath": "", + "restructuredtext.updateOnTextChanged": "true", + "restructuredtext.updateDelay": 300, + "restructuredtext.linter.disabled": true, + "python.pythonPath": "/home/gitpod/mambaforge3/envs/numpy-dev/bin/python" +} \ No newline at end of file diff --git a/tools/gitpod/workspace_config b/tools/gitpod/workspace_config new file mode 100644 index 000000000000..aa859c9be4d0 --- /dev/null +++ b/tools/gitpod/workspace_config @@ -0,0 +1,58 @@ +#!/bin/bash +# Basic configurations for the workspace + +set -e + +# gitpod/workspace-base needs at least one file here +touch /home/gitpod/.bashrc.d/empty + +# Add git aliases +git config --global alias.co checkout +git config --global alias.ci commit +git config --global alias.st status +git config --global alias.br branch +git config --global alias.hist "log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short" +git config --global alias.type 'cat-file -t' +git config --global alias.dump 'cat-file -p' + +# Enable basic vim defaults in ~/.vimrc +echo "filetype plugin indent on" >>~/.vimrc +echo "set colorcolumn=80" >>~/.vimrc +echo "set number" >>~/.vimrc +echo "syntax enable" >>~/.vimrc + +# Vanity custom bash prompt - makes it more legible +echo "PS1='\[\e]0;\u \w\a\]\[\033[01;36m\]\u\[\033[m\] > \[\033[38;5;141m\]\w\[\033[m\] \\$ '" >>~/.bashrc + +# Enable prompt color in the skeleton .bashrc +# hadolint ignore=SC2016 +sed -i 's/^#force_color_prompt=yes/force_color_prompt=yes/' /etc/skel/.bashrc + +# .gitpod.yml is configured to install NumPy from /workspace/numpy +echo "export PYTHONPATH=${WORKSPACE}" >>~/.bashrc + +# make conda activate command available from /bin/bash (login and interactive) +if [[ ! -f "/etc/profile.d/conda.sh" ]]; then + ln -s ${CONDA_DIR}/etc/profile.d/conda.sh /etc/profile.d/conda.sh +fi +echo ". ${CONDA_DIR}/etc/profile.d/conda.sh" >>~/.bashrc +echo "conda activate numpy-dev" >>~/.bashrc + +# Enable prompt color in the skeleton .bashrc +# hadolint ignore=SC2016 +sed -i 's/^#force_color_prompt=yes/force_color_prompt=yes/' /etc/skel/.bashrc + +# .gitpod.yml is configured to install numpy from /workspace/numpy +echo "export PYTHONPATH=/workspace/numpy" >>~/.bashrc + +# Set up ccache for compilers for this Dockerfile +# REF: https://github.com/conda-forge/compilers-feedstock/issues/31 +echo "conda activate numpy-dev" >>~/.startuprc +echo "export CC=\"ccache \$CC\"" >>~/.startuprc +echo "export CXX=\"ccache \$CXX\"" >>~/.startuprc +echo "export F77=\"ccache \$F77\"" >>~/.startuprc +echo "export F90=\"ccache \$F90\"" >>~/.startuprc +echo "export GFORTRAN=\"ccache \$GFORTRAN\"" >>~/.startuprc +echo "export FC=\"ccache \$FC\"" >>~/.startuprc +echo "source ~/.startuprc" >>~/.profile +echo "source ~/.startuprc" >>~/.bashrc diff --git a/tools/lint_diff.ini b/tools/lint_diff.ini new file mode 100644 index 000000000000..9e31050b78a4 --- /dev/null +++ b/tools/lint_diff.ini @@ -0,0 +1,5 @@ +[pycodestyle] +max_line_length = 79 +statistics = True +ignore = E121,E122,E123,E125,E126,E127,E128,E226,E241,E251,E265,E266,E302,E402,E704,E712,E721,E731,E741,W291,W293,W391,W503,W504 +exclude = numpy/__config__.py,numpy/typing/tests/data diff --git a/tools/linter.py b/tools/linter.py new file mode 100644 index 000000000000..0031ff83a479 --- /dev/null +++ b/tools/linter.py @@ -0,0 +1,85 @@ +import os +import sys +import subprocess +from argparse import ArgumentParser +from git import Repo, exc + +CONFIG = os.path.join( + os.path.abspath(os.path.dirname(__file__)), + 'lint_diff.ini', +) + +# NOTE: The `diff` and `exclude` options of pycodestyle seem to be +# incompatible, so instead just exclude the necessary files when +# computing the diff itself. +EXCLUDE = ( + "numpy/typing/tests/data/", + "numpy/typing/_char_codes.py", + "numpy/__config__.py", + "numpy/f2py", +) + + +class DiffLinter: + def __init__(self, branch): + self.branch = branch + self.repo = Repo('.') + self.head = self.repo.head.commit + + def get_branch_diff(self, uncommitted = False): + """ + Determine the first common ancestor commit. + Find diff between branch and FCA commit. + Note: if `uncommitted` is set, check only + uncommitted changes + """ + try: + commit = self.repo.merge_base(self.branch, self.head)[0] + except exc.GitCommandError: + print(f"Branch with name `{self.branch}` does not exist") + sys.exit(1) + + exclude = [f':(exclude){i}' for i in EXCLUDE] + if uncommitted: + diff = self.repo.git.diff( + self.head, '--unified=0', '***.py', *exclude + ) + else: + diff = self.repo.git.diff( + commit, self.head, '--unified=0', '***.py', *exclude + ) + return diff + + def run_pycodestyle(self, diff): + """ + Original Author: Josh Wilson (@person142) + Source: + https://github.com/scipy/scipy/blob/main/tools/lint_diff.py + Run pycodestyle on the given diff. + """ + res = subprocess.run( + ['pycodestyle', '--diff', '--config', CONFIG], + input=diff, + stdout=subprocess.PIPE, + encoding='utf-8', + ) + return res.returncode, res.stdout + + def run_lint(self, uncommitted): + diff = self.get_branch_diff(uncommitted) + retcode, errors = self.run_pycodestyle(diff) + + errors and print(errors) + + sys.exit(retcode) + + +if __name__ == '__main__': + parser = ArgumentParser() + parser.add_argument("--branch", type=str, default='main', + help="The branch to diff against") + parser.add_argument("--uncommitted", action='store_true', + help="Check only uncommitted changes") + args = parser.parse_args() + + DiffLinter(args.branch).run_lint(args.uncommitted) diff --git a/tools/list_installed_dll_dependencies_cygwin.sh b/tools/list_installed_dll_dependencies_cygwin.sh new file mode 100644 index 000000000000..ee06ae0d0888 --- /dev/null +++ b/tools/list_installed_dll_dependencies_cygwin.sh @@ -0,0 +1,35 @@ +#!/bin/dash +# Check permissions and dependencies on installed DLLs +# DLLs need execute permissions to be used +# DLLs must be able to find their dependencies +# This checks both of those, then does a direct test +# The best way of checking whether a C extension module is importable +# is trying to import it. The rest is trying to give reasons why it +# isn't importing. +# +# One of the tools and the extension for shared libraries are +# Cygwin-specific, but the rest should work on most platforms with +# /bin/sh + +py_ver=${1} +dll_list=`/bin/dash tools/list_numpy_dlls.sh ${py_ver}` +echo "Checks for existence, permissions and file type" +ls -l ${dll_list} +file ${dll_list} +echo "Dependency checks" +ldd ${dll_list} | grep -F -e " => not found" && exit 1 +cygcheck ${dll_list} >cygcheck_dll_list 2>cygcheck_missing_deps +grep -F -e "cygcheck: track_down: could not find " cygcheck_missing_deps && exit 1 +echo "Import tests" +mkdir -p dist/ +cd dist/ +for name in ${dll_list}; +do + echo ${name} + ext_module=`echo ${name} | \ + sed -E \ + -e "s/^\/+(home|usr).*?site-packages\/+//" \ + -e "s/.cpython-3.m?-x86(_64)?-cygwin.dll$//" \ + -e "s/\//./g"` + python${py_ver} -c "import ${ext_module}" +done diff --git a/tools/list_numpy_dlls.sh b/tools/list_numpy_dlls.sh new file mode 100644 index 000000000000..fedd2097ba67 --- /dev/null +++ b/tools/list_numpy_dlls.sh @@ -0,0 +1,9 @@ +#!/bin/dash +# Print the list of dlls installed by NumPy + +py_ver=${1} +site_packages=`python${py_ver} -m pip show numpy | \ + grep Location | cut -d " " -f 2 -`; +dll_list=`for name in $(python${py_ver} -m pip show -f numpy | \ + grep -F .dll); do echo ${site_packages}/${name}; done` +echo ${dll_list} diff --git a/tools/npy_tempita/__init__.py b/tools/npy_tempita/__init__.py index dfb40e9652ff..fedcd91f45b8 100644 --- a/tools/npy_tempita/__init__.py +++ b/tools/npy_tempita/__init__.py @@ -32,8 +32,6 @@ def foo(bar): with a few changes to remove the six dependency. """ -from __future__ import absolute_import, division, print_function - import re import sys try: @@ -48,7 +46,7 @@ def foo(bar): import tokenize from ._looper import looper from .compat3 import ( - PY3, bytes, basestring_, next, is_unicode, coerce_text, iteritems) + bytes, basestring_, next, is_unicode, coerce_text, iteritems) __all__ = ['TemplateError', 'Template', 'sub', 'HTMLTemplate', @@ -92,7 +90,7 @@ def get_file_template(name, from_template): get_template=from_template.get_template) -class Template(object): +class Template: default_namespace = { 'start_braces': '{{', @@ -105,21 +103,21 @@ class Template(object): def __init__(self, content, name=None, namespace=None, stacklevel=None, get_template=None, default_inherit=None, line_offset=0, - delimeters=None): + delimiters=None): self.content = content - # set delimeters - if delimeters is None: - delimeters = (self.default_namespace['start_braces'], + # set delimiters + if delimiters is None: + delimiters = (self.default_namespace['start_braces'], self.default_namespace['end_braces']) else: - assert len(delimeters) == 2 and all( - [isinstance(delimeter, basestring_) - for delimeter in delimeters]) + assert len(delimiters) == 2 and all( + [isinstance(delimiter, basestring_) + for delimiter in delimiters]) self.default_namespace = self.__class__.default_namespace.copy() - self.default_namespace['start_braces'] = delimeters[0] - self.default_namespace['end_braces'] = delimeters[1] - self.delimeters = delimeters + self.default_namespace['start_braces'] = delimiters[0] + self.default_namespace['end_braces'] = delimiters[1] + self.delimiters = delimiters self._unicode = is_unicode(content) if name is None and stacklevel is not None: @@ -143,7 +141,7 @@ def __init__(self, content, name=None, namespace=None, stacklevel=None, self.name = name self._parsed = parse( content, name=name, line_offset=line_offset, - delimeters=self.delimeters) + delimiters=self.delimiters) if namespace is None: namespace = {} self.namespace = namespace @@ -157,7 +155,7 @@ def from_filename(cls, filename, namespace=None, encoding=None, c = f.read() if encoding: c = c.decode(encoding) - elif PY3: + else: c = c.decode('latin-1') return cls(content=c, name=filename, namespace=namespace, default_inherit=default_inherit, get_template=get_template) @@ -319,11 +317,8 @@ def _eval(self, code, ns, pos): arg0 = e_value.args[0] else: arg0 = coerce_text(e_value) - e_value.args = (self._add_line_info(arg0, pos),) - if PY3: - raise e_value - else: - exec('raise e_type, e_value, e_traceback') + e_value.args = (self._add_line_info(arg0, pos),) + raise e_value def _exec(self, code, ns, pos): # __traceback_hide__ = True @@ -335,10 +330,7 @@ def _exec(self, code, ns, pos): e_value.args = (self._add_line_info(e_value.args[0], pos),) else: e_value.args = (self._add_line_info(None, pos),) - if PY3: - raise e_value - else: - exec('raise e_type, e_value, e_traceback') + raise e_value def _repr(self, value, pos): # __traceback_hide__ = True @@ -357,10 +349,7 @@ def _repr(self, value, pos): except: e_type, e_value, e_traceback = sys.exc_info() e_value.args = (self._add_line_info(e_value.args[0], pos),) - if PY3: - raise e_value - else: - exec('raise e_type, e_value, e_traceback') + raise e_value else: if self._unicode and isinstance(value, bytes): if not self.default_encoding: @@ -392,9 +381,9 @@ def _add_line_info(self, msg, pos): return msg -def sub(content, delimeters=None, **kw): +def sub(content, delimiters=None, **kw): name = kw.get('__name') - tmpl = Template(content, name=name, delimeters=delimeters) + tmpl = Template(content, name=name, delimiters=delimiters) return tmpl.substitute(kw) @@ -440,7 +429,7 @@ def __repr__(self): ############################################################ -class html(object): +class html: def __init__(self, value): self.value = value @@ -463,14 +452,11 @@ def html_quote(value, force=True): return '' if not isinstance(value, basestring_): value = coerce_text(value) - if sys.version >= "3" and isinstance(value, bytes): + if isinstance(value, bytes): value = html_escape(value.decode('latin1'), 1) value = value.encode('latin1') else: value = html_escape(value, 1) - if sys.version < "3": - if is_unicode(value): - value = value.encode('ascii', 'xmlcharrefreplace') return value @@ -522,7 +508,7 @@ def sub_html(content, **kw): return tmpl.substitute(kw) -class TemplateDef(object): +class TemplateDef: def __init__(self, template, func_name, func_signature, body, ns, pos, bound_self=None): self._template = template @@ -599,7 +585,7 @@ def _parse_signature(self, args, kw): return values -class TemplateObject(object): +class TemplateObject: def __init__(self, name): self.__name = name @@ -609,7 +595,7 @@ def __repr__(self): return '<%s %s>' % (self.__class__.__name__, self.__name) -class TemplateObjectGetter(object): +class TemplateObjectGetter: def __init__(self, template_obj): self.__template_obj = template_obj @@ -622,7 +608,7 @@ def __repr__(self): self.__class__.__name__, self.__template_obj) -class _Empty(object): +class _Empty: def __call__(self, *args, **kw): return self @@ -633,7 +619,7 @@ def __repr__(self): return 'Empty' def __unicode__(self): - return '' if PY3 else u'' + return '' def __iter__(self): return iter(()) @@ -641,9 +627,6 @@ def __iter__(self): def __bool__(self): return False - if sys.version < "3": - __nonzero__ = __bool__ - Empty = _Empty() del _Empty @@ -652,28 +635,49 @@ def __bool__(self): ############################################################ -def lex(s, name=None, trim_whitespace=True, line_offset=0, delimeters=None): - if delimeters is None: - delimeters = (Template.default_namespace['start_braces'], +def lex(s, name=None, trim_whitespace=True, line_offset=0, delimiters=None): + """ + Lex a string into chunks: + + >>> lex('hey') + ['hey'] + >>> lex('hey {{you}}') + ['hey ', ('you', (1, 7))] + >>> lex('hey {{') + Traceback (most recent call last): + ... + tempita.TemplateError: No }} to finish last expression at line 1 column 7 + >>> lex('hey }}') + Traceback (most recent call last): + ... + tempita.TemplateError: }} outside expression at line 1 column 7 + >>> lex('hey {{ {{') + Traceback (most recent call last): + ... + tempita.TemplateError: {{ inside expression at line 1 column 10 + """ + + if delimiters is None: + delimiters = (Template.default_namespace['start_braces'], Template.default_namespace['end_braces']) in_expr = False chunks = [] last = 0 last_pos = (line_offset + 1, 1) - token_re = re.compile(r'%s|%s' % (re.escape(delimeters[0]), - re.escape(delimeters[1]))) + token_re = re.compile(r'%s|%s' % (re.escape(delimiters[0]), + re.escape(delimiters[1]))) for match in token_re.finditer(s): expr = match.group(0) pos = find_position(s, match.end(), last, last_pos) - if expr == delimeters[0] and in_expr: - raise TemplateError('%s inside expression' % delimeters[0], + if expr == delimiters[0] and in_expr: + raise TemplateError('%s inside expression' % delimiters[0], position=pos, name=name) - elif expr == delimeters[1] and not in_expr: - raise TemplateError('%s outside expression' % delimeters[1], + elif expr == delimiters[1] and not in_expr: + raise TemplateError('%s outside expression' % delimiters[1], position=pos, name=name) - if expr == delimeters[0]: + if expr == delimiters[0]: part = s[last:match.start()] if part: chunks.append(part) @@ -684,7 +688,7 @@ def lex(s, name=None, trim_whitespace=True, line_offset=0, delimeters=None): last = match.end() last_pos = pos if in_expr: - raise TemplateError('No %s to finish last expression' % delimeters[1], + raise TemplateError('No %s to finish last expression' % delimiters[1], name=name, position=last_pos) part = s[last:] if part: @@ -693,48 +697,6 @@ def lex(s, name=None, trim_whitespace=True, line_offset=0, delimeters=None): chunks = trim_lex(chunks) return chunks -lex.__doc__ = """ -Lex a string into chunks: - - >>> lex('hey') - ['hey'] - >>> lex('hey {{you}}') - ['hey ', ('you', (1, 7))] - >>> lex('hey {{') - Traceback (most recent call last): - ... - tempita.TemplateError: No }} to finish last expression at line 1 column 7 - >>> lex('hey }}') - Traceback (most recent call last): - ... - tempita.TemplateError: }} outside expression at line 1 column 7 - >>> lex('hey {{ {{') - Traceback (most recent call last): - ... - tempita.TemplateError: {{ inside expression at line 1 column 10 - -""" if PY3 else """ -Lex a string into chunks: - - >>> lex('hey') - ['hey'] - >>> lex('hey {{you}}') - ['hey ', ('you', (1, 7))] - >>> lex('hey {{') - Traceback (most recent call last): - ... - TemplateError: No }} to finish last expression at line 1 column 7 - >>> lex('hey }}') - Traceback (most recent call last): - ... - TemplateError: }} outside expression at line 1 column 7 - >>> lex('hey {{ {{') - Traceback (most recent call last): - ... - TemplateError: {{ inside expression at line 1 column 10 - -""" - statement_re = re.compile(r'^(?:if |elif |for |def |inherit |default |py:)') single_statements = ['else', 'endif', 'endfor', 'enddef', 'continue', 'break'] trail_whitespace_re = re.compile(r'\n\r?[\t ]*$') @@ -742,6 +704,16 @@ def lex(s, name=None, trim_whitespace=True, line_offset=0, delimeters=None): def trim_lex(tokens): + r""" + Takes a lexed list of tokens, and removes whitespace when there is + a directive on a line by itself: + + >>> tokens = lex('{{if x}}\nx\n{{endif}}\ny', trim_whitespace=False) + >>> tokens + [('if x', (1, 3)), '\nx\n', ('endif', (3, 3)), '\ny'] + >>> trim_lex(tokens) + [('if x', (1, 3)), 'x\n', ('endif', (3, 3)), 'y'] + """ last_trim = None for i in range(len(tokens)): current = tokens[i] @@ -789,26 +761,6 @@ def trim_lex(tokens): tokens[i + 1] = next_chunk return tokens -trim_lex.__doc__ = r""" - Takes a lexed set of tokens, and removes whitespace when there is - a directive on a line by itself: - - >>> tokens = lex('{{if x}}\nx\n{{endif}}\ny', trim_whitespace=False) - >>> tokens - [('if x', (1, 3)), '\nx\n', ('endif', (3, 3)), '\ny'] - >>> trim_lex(tokens) - [('if x', (1, 3)), 'x\n', ('endif', (3, 3)), 'y'] - """ if PY3 else r""" - Takes a lexed set of tokens, and removes whitespace when there is - a directive on a line by itself: - - >>> tokens = lex('{{if x}}\nx\n{{endif}}\ny', trim_whitespace=False) - >>> tokens - [('if x', (1, 3)), '\nx\n', ('endif', (3, 3)), '\ny'] - >>> trim_lex(tokens) - [('if x', (1, 3)), 'x\n', ('endif', (3, 3)), 'y'] - """ - def find_position(string, index, last_index, last_pos): """ @@ -822,19 +774,8 @@ def find_position(string, index, last_index, last_pos): return (last_pos[0] + lines, column) -def parse(s, name=None, line_offset=0, delimeters=None): - - if delimeters is None: - delimeters = (Template.default_namespace['start_braces'], - Template.default_namespace['end_braces']) - tokens = lex(s, name=name, line_offset=line_offset, delimeters=delimeters) - result = [] - while tokens: - next_chunk, tokens = parse_expr(tokens, name) - result.append(next_chunk) - return result - -parse.__doc__ = r""" +def parse(s, name=None, line_offset=0, delimiters=None): + r""" Parses a string into a kind of AST >>> parse('{{x}}') @@ -889,63 +830,18 @@ def parse(s, name=None, line_offset=0, delimeters=None): ... tempita.TemplateError: Multi-line py blocks must start with a newline at line 1 column 3 - """ if PY3 else r""" - Parses a string into a kind of AST - - >>> parse('{{x}}') - [('expr', (1, 3), 'x')] - >>> parse('foo') - ['foo'] - >>> parse('{{if x}}test{{endif}}') - [('cond', (1, 3), ('if', (1, 3), 'x', ['test']))] - >>> parse( - ... 'series->{{for x in y}}x={{x}}{{endfor}}' - ... ) #doctest: +NORMALIZE_WHITESPACE - ['series->', - ('for', (1, 11), ('x',), 'y', ['x=', ('expr', (1, 27), 'x')])] - >>> parse('{{for x, y in z:}}{{continue}}{{endfor}}') - [('for', (1, 3), ('x', 'y'), 'z', [('continue', (1, 21))])] - >>> parse('{{py:x=1}}') - [('py', (1, 3), 'x=1')] - >>> parse( - ... '{{if x}}a{{elif y}}b{{else}}c{{endif}}' - ... ) #doctest: +NORMALIZE_WHITESPACE - [('cond', (1, 3), ('if', (1, 3), 'x', ['a']), - ('elif', (1, 12), 'y', ['b']), ('else', (1, 23), None, ['c']))] - - Some exceptions:: - - >>> parse('{{continue}}') - Traceback (most recent call last): - ... - TemplateError: continue outside of for loop at line 1 column 3 - >>> parse('{{if x}}foo') - Traceback (most recent call last): - ... - TemplateError: No {{endif}} at line 1 column 3 - >>> parse('{{else}}') - Traceback (most recent call last): - ... - TemplateError: else outside of an if block at line 1 column 3 - >>> parse('{{if x}}{{for x in y}}{{endif}}{{endfor}}') - Traceback (most recent call last): - ... - TemplateError: Unexpected endif at line 1 column 25 - >>> parse('{{if}}{{endif}}') - Traceback (most recent call last): - ... - TemplateError: if with no expression at line 1 column 3 - >>> parse('{{for x y}}{{endfor}}') - Traceback (most recent call last): - ... - TemplateError: Bad for (no "in") in 'x y' at line 1 column 3 - >>> parse('{{py:x=1\ny=2}}') #doctest: +NORMALIZE_WHITESPACE - Traceback (most recent call last): - ... - TemplateError: Multi-line py blocks must start - with a newline at line 1 column 3 """ + if delimiters is None: + delimiters = (Template.default_namespace['start_braces'], + Template.default_namespace['end_braces']) + tokens = lex(s, name=name, line_offset=line_offset, delimiters=delimiters) + result = [] + while tokens: + next_chunk, tokens = parse_expr(tokens, name) + result.append(next_chunk) + return result + def parse_expr(tokens, name, context=()): if isinstance(tokens[0], basestring_): @@ -1291,7 +1187,7 @@ def fill_command(args=None): template_content = sys.stdin.read() template_name = '<stdin>' else: - with open(template_name, 'rb', encoding="latin-1") as f: + with open(template_name, 'rb', encoding="latin-1") as f: template_content = f.read() if options.use_html: TemplateClass = HTMLTemplate @@ -1300,7 +1196,7 @@ def fill_command(args=None): template = TemplateClass(template_content, name=template_name) result = template.substitute(vars) if options.output: - with open(options.output, 'wb') as f: + with open(options.output, 'wb') as f: f.write(result) else: sys.stdout.write(result) diff --git a/tools/npy_tempita/_looper.py b/tools/npy_tempita/_looper.py index dcb20664234b..8a1156678d0f 100644 --- a/tools/npy_tempita/_looper.py +++ b/tools/npy_tempita/_looper.py @@ -17,15 +17,13 @@ 3 c """ -from __future__ import absolute_import, division, print_function - import sys from .compat3 import basestring_ __all__ = ['looper'] -class looper(object): +class looper: """ Helper for looping (particularly in templates) @@ -47,7 +45,7 @@ def __repr__(self): self.__class__.__name__, self.seq) -class looper_iter(object): +class looper_iter: def __init__(self, seq): self.seq = list(seq) @@ -63,11 +61,8 @@ def __next__(self): self.pos += 1 return result - if sys.version < "3": - next = __next__ - -class loop_pos(object): +class loop_pos: def __init__(self, seq, pos): self.seq = seq @@ -77,53 +72,50 @@ def __repr__(self): return '<loop pos=%r at %r>' % ( self.seq[self.pos], self.pos) + @property def index(self): return self.pos - index = property(index) + @property def number(self): return self.pos + 1 - number = property(number) + @property def item(self): return self.seq[self.pos] - item = property(item) + @property def __next__(self): try: return self.seq[self.pos + 1] except IndexError: return None - __next__ = property(__next__) - - if sys.version < "3": - next = __next__ + @property def previous(self): if self.pos == 0: return None return self.seq[self.pos - 1] - previous = property(previous) + @property def odd(self): return not self.pos % 2 - odd = property(odd) + @property def even(self): return self.pos % 2 - even = property(even) + @property def first(self): return self.pos == 0 - first = property(first) + @property def last(self): return self.pos == len(self.seq) - 1 - last = property(last) + @property def length(self): return len(self.seq) - length = property(length) def first_group(self, getter=None): """ diff --git a/tools/npy_tempita/compat3.py b/tools/npy_tempita/compat3.py index eb890ca14abf..d9d682ff55f5 100644 --- a/tools/npy_tempita/compat3.py +++ b/tools/npy_tempita/compat3.py @@ -1,11 +1,9 @@ -from __future__ import absolute_import, division, print_function - import sys __all__ = ['PY3', 'b', 'basestring_', 'bytes', 'next', 'is_unicode', 'iteritems'] -PY3 = True if sys.version_info[0] == 3 else False +PY3 = True if sys.version_info[0] >= 3 else False if sys.version_info[0] < 3: diff --git a/tools/openblas_support.py b/tools/openblas_support.py new file mode 100644 index 000000000000..4eb72dbc9b4b --- /dev/null +++ b/tools/openblas_support.py @@ -0,0 +1,341 @@ +import glob +import hashlib +import os +import platform +import sysconfig +import sys +import shutil +import tarfile +import textwrap +import zipfile + +from tempfile import mkstemp, gettempdir +from urllib.request import urlopen, Request +from urllib.error import HTTPError + +OPENBLAS_V = '0.3.18' +OPENBLAS_LONG = 'v0.3.18' +BASE_LOC = 'https://anaconda.org/multibuild-wheels-staging/openblas-libs' +BASEURL = f'{BASE_LOC}/{OPENBLAS_LONG}/download' +SUPPORTED_PLATFORMS = [ + 'linux-aarch64', + 'linux-x86_64', + 'linux-i686', + 'linux-ppc64le', + 'linux-s390x', + 'win-amd64', + 'win-32', + 'macosx-x86_64', + 'macosx-arm64', +] +IS_32BIT = sys.maxsize < 2**32 + + +def get_plat(): + plat = sysconfig.get_platform() + plat_split = plat.split("-") + arch = plat_split[-1] + if arch == "win32": + plat = "win-32" + elif arch in ["universal2", "intel"]: + plat = f"macosx-{platform.uname().machine}" + elif len(plat_split) > 2: + plat = f"{plat_split[0]}-{arch}" + assert plat in SUPPORTED_PLATFORMS, f'invalid platform {plat}' + return plat + + +def get_ilp64(): + if os.environ.get("NPY_USE_BLAS_ILP64", "0") == "0": + return None + if IS_32BIT: + raise RuntimeError("NPY_USE_BLAS_ILP64 set on 32-bit arch") + return "64_" + + +def get_manylinux(arch): + if arch in ('x86_64', 'i686'): + default = '2010' + else: + default = '2014' + ret = os.environ.get("MB_ML_VER", default) + # XXX For PEP 600 this can be a glibc version + assert ret in ('1', '2010', '2014', '_2_24'), f'invalid MB_ML_VER {ret}' + return ret + + +def download_openblas(target, plat, ilp64): + osname, arch = plat.split("-") + fnsuffix = {None: "", "64_": "64_"}[ilp64] + filename = '' + headers = {'User-Agent': + ('Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 ; ' + '(KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.3')} + suffix = None + if osname == "linux": + ml_ver = get_manylinux(arch) + suffix = f'manylinux{ml_ver}_{arch}.tar.gz' + typ = 'tar.gz' + elif plat == 'macosx-x86_64': + suffix = 'macosx_10_9_x86_64-gf_1becaaa.tar.gz' + typ = 'tar.gz' + elif plat == 'macosx-arm64': + suffix = 'macosx_11_0_arm64-gf_f26990f.tar.gz' + typ = 'tar.gz' + elif osname == 'win': + if plat == "win-32": + suffix = 'win32-gcc_8_1_0.zip' + else: + suffix = 'win_amd64-gcc_8_1_0.zip' + typ = 'zip' + + if not suffix: + return None + filename = f'{BASEURL}/openblas{fnsuffix}-{OPENBLAS_LONG}-{suffix}' + req = Request(url=filename, headers=headers) + try: + response = urlopen(req) + except HTTPError: + print(f'Could not download "{filename}"', file=sys.stderr) + raise + length = response.getheader('content-length') + if response.status != 200: + print(f'Could not download "{filename}"', file=sys.stderr) + return None + print(f"Downloading {length} from {filename}", file=sys.stderr) + data = response.read() + # Verify hash + key = os.path.basename(filename) + print("Saving to file", file=sys.stderr) + with open(target, 'wb') as fid: + fid.write(data) + return typ + + +def setup_openblas(plat=get_plat(), ilp64=get_ilp64()): + ''' + Download and setup an openblas library for building. If successful, + the configuration script will find it automatically. + + Returns + ------- + msg : str + path to extracted files on success, otherwise indicates what went wrong + To determine success, do ``os.path.exists(msg)`` + ''' + _, tmp = mkstemp() + if not plat: + raise ValueError('unknown platform') + typ = download_openblas(tmp, plat, ilp64) + if not typ: + return '' + osname, arch = plat.split("-") + if osname == 'win': + if not typ == 'zip': + return f'expecting to download zipfile on windows, not {typ}' + return unpack_windows_zip(tmp) + else: + if not typ == 'tar.gz': + return 'expecting to download tar.gz, not %s' % str(typ) + return unpack_targz(tmp) + + +def unpack_windows_zip(fname): + with zipfile.ZipFile(fname, 'r') as zf: + # Get the openblas.a file, but not openblas.dll.a nor openblas.dev.a + lib = [x for x in zf.namelist() if OPENBLAS_LONG in x and + x.endswith('a') and not x.endswith('dll.a') and + not x.endswith('dev.a')] + if not lib: + return 'could not find libopenblas_%s*.a ' \ + 'in downloaded zipfile' % OPENBLAS_LONG + if get_ilp64() is None: + target = os.path.join(gettempdir(), 'openblas.a') + else: + target = os.path.join(gettempdir(), 'openblas64_.a') + with open(target, 'wb') as fid: + fid.write(zf.read(lib[0])) + return target + + +def unpack_targz(fname): + target = os.path.join(gettempdir(), 'openblas') + if not os.path.exists(target): + os.mkdir(target) + with tarfile.open(fname, 'r') as zf: + # Strip common prefix from paths when unpacking + prefix = os.path.commonpath(zf.getnames()) + extract_tarfile_to(zf, target, prefix) + return target + + +def extract_tarfile_to(tarfileobj, target_path, archive_path): + """Extract TarFile contents under archive_path/ to target_path/""" + + target_path = os.path.abspath(target_path) + + def get_members(): + for member in tarfileobj.getmembers(): + if archive_path: + norm_path = os.path.normpath(member.name) + if norm_path.startswith(archive_path + os.path.sep): + member.name = norm_path[len(archive_path)+1:] + else: + continue + + dst_path = os.path.abspath(os.path.join(target_path, member.name)) + if os.path.commonpath([target_path, dst_path]) != target_path: + # Path not under target_path, probably contains ../ + continue + + yield member + + tarfileobj.extractall(target_path, members=get_members()) + + +def make_init(dirname): + ''' + Create a _distributor_init.py file for OpenBlas + ''' + with open(os.path.join(dirname, '_distributor_init.py'), 'wt') as fid: + fid.write(textwrap.dedent(""" + ''' + Helper to preload windows dlls to prevent dll not found errors. + Once a DLL is preloaded, its namespace is made available to any + subsequent DLL. This file originated in the numpy-wheels repo, + and is created as part of the scripts that build the wheel. + ''' + import os + import glob + if os.name == 'nt': + # convention for storing / loading the DLL from + # numpy/.libs/, if present + try: + from ctypes import WinDLL + basedir = os.path.dirname(__file__) + except: + pass + else: + libs_dir = os.path.abspath(os.path.join(basedir, '.libs')) + DLL_filenames = [] + if os.path.isdir(libs_dir): + for filename in glob.glob(os.path.join(libs_dir, + '*openblas*dll')): + # NOTE: would it change behavior to load ALL + # DLLs at this path vs. the name restriction? + WinDLL(os.path.abspath(filename)) + DLL_filenames.append(filename) + if len(DLL_filenames) > 1: + import warnings + warnings.warn("loaded more than 1 DLL from .libs:" + "\\n%s" % "\\n".join(DLL_filenames), + stacklevel=1) + """)) + + +def test_setup(plats): + ''' + Make sure all the downloadable files exist and can be opened + ''' + def items(): + """ yields all combinations of arch, ilp64 + """ + for plat in plats: + yield plat, None + osname, arch = plat.split("-") + if arch not in ('i686', 'arm64', '32'): + yield plat, '64_' + if osname == "linux" and arch in ('i686', 'x86_64'): + oldval = os.environ.get('MB_ML_VER', None) + os.environ['MB_ML_VER'] = '1' + yield plat, None + # Once we create x86_64 and i686 manylinux2014 wheels... + # os.environ['MB_ML_VER'] = '2014' + # yield arch, None, False + if oldval: + os.environ['MB_ML_VER'] = oldval + else: + os.environ.pop('MB_ML_VER') + + errs = [] + for plat, ilp64 in items(): + osname, _ = plat.split("-") + if plat not in plats: + continue + target = None + try: + try: + target = setup_openblas(plat, ilp64) + except Exception as e: + print(f'Could not setup {plat} with ilp64 {ilp64}, ') + print(e) + errs.append(e) + continue + if not target: + raise RuntimeError(f'Could not setup {plat}') + print(target) + if osname == 'win': + if not target.endswith('.a'): + raise RuntimeError("Not .a extracted!") + else: + files = glob.glob(os.path.join(target, "lib", "*.a")) + if not files: + raise RuntimeError("No lib/*.a unpacked!") + finally: + if target is not None: + if os.path.isfile(target): + os.unlink(target) + else: + shutil.rmtree(target) + if errs: + raise errs[0] + + +def test_version(expected_version, ilp64=get_ilp64()): + """ + Assert that expected OpenBLAS version is + actually available via NumPy + """ + import numpy + import ctypes + + dll = ctypes.CDLL(numpy.core._multiarray_umath.__file__) + if ilp64 == "64_": + get_config = dll.openblas_get_config64_ + else: + get_config = dll.openblas_get_config + get_config.restype = ctypes.c_char_p + res = get_config() + print('OpenBLAS get_config returned', str(res)) + if not expected_version: + expected_version = OPENBLAS_V + check_str = b'OpenBLAS %s' % expected_version.encode() + print(check_str) + assert check_str in res, f'{expected_version} not found in {res}' + if ilp64: + assert b"USE64BITINT" in res + else: + assert b"USE64BITINT" not in res + + +if __name__ == '__main__': + import argparse + parser = argparse.ArgumentParser( + description='Download and expand an OpenBLAS archive for this ' + 'architecture') + parser.add_argument('--test', nargs='*', default=None, + help='Test different architectures. "all", or any of ' + f'{SUPPORTED_PLATFORMS}') + parser.add_argument('--check_version', nargs='?', default='', + help='Check provided OpenBLAS version string ' + 'against available OpenBLAS') + args = parser.parse_args() + if args.check_version != '': + test_version(args.check_version) + elif args.test is None: + print(setup_openblas()) + else: + if len(args.test) == 0 or 'all' in args.test: + test_setup(SUPPORTED_PLATFORMS) + else: + test_setup(args.test) diff --git a/tools/pypy-test.sh b/tools/pypy-test.sh new file mode 100755 index 000000000000..e6c6ae719c91 --- /dev/null +++ b/tools/pypy-test.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash + +# Exit if a command fails +set -e +set -o pipefail +# Print expanded commands +set -x + +sudo apt-get -yq update +sudo apt-get -yq install gfortran-5 +export F77=gfortran-5 +export F90=gfortran-5 + +# Download the proper OpenBLAS x64 precompiled library +target=$(python3 tools/openblas_support.py) +ls -lR "$target" +echo getting OpenBLAS into $target +export LD_LIBRARY_PATH=$target/lib +export LIB=$target/lib +export INCLUDE=$target/include + +# Use a site.cfg to build with local openblas +cat << EOF > site.cfg +[openblas] +libraries = openblas +library_dirs = $target/lib:$LIB +include_dirs = $target/lib:$LIB +runtime_library_dirs = $target/lib +EOF + +echo getting PyPy 3.6-v7.3.2 +wget -q https://downloads.python.org/pypy/pypy3.6-v7.3.2-linux64.tar.bz2 -O pypy.tar.bz2 +mkdir -p pypy3 +(cd pypy3; tar --strip-components=1 -xf ../pypy.tar.bz2) +pypy3/bin/pypy3 -mensurepip +pypy3/bin/pypy3 -m pip install --upgrade pip +pypy3/bin/pypy3 -m pip install --user -r test_requirements.txt --no-warn-script-location + +echo +echo pypy3 version +pypy3/bin/pypy3 -c "import sys; print(sys.version)" +echo + +pypy3/bin/pypy3 runtests.py --debug-info --show-build-log -v -- -rsx \ + --junitxml=junit/test-results.xml --durations 10 + +echo Make sure the correct openblas has been linked in +pypy3/bin/pypy3 -mpip install --no-build-isolation . +pypy3/bin/pypy3 tools/openblas_support.py --check_version diff --git a/tools/rebase_installed_dlls_cygwin.sh b/tools/rebase_installed_dlls_cygwin.sh new file mode 100644 index 000000000000..f772879d9d7d --- /dev/null +++ b/tools/rebase_installed_dlls_cygwin.sh @@ -0,0 +1,5 @@ +#!/bin/dash +# Rebase the dlls installed by NumPy + +py_ver=${1} +/usr/bin/rebase --database --oblivious `/bin/dash tools/list_numpy_dlls.sh ${py_ver}` diff --git a/tools/refguide_check.py b/tools/refguide_check.py new file mode 100644 index 000000000000..21ba5a448dcc --- /dev/null +++ b/tools/refguide_check.py @@ -0,0 +1,1258 @@ +#!/usr/bin/env python3 +""" +refguide_check.py [OPTIONS] [-- ARGS] + +- Check for a NumPy submodule whether the objects in its __all__ dict + correspond to the objects included in the reference guide. +- Check docstring examples +- Check example blocks in RST files + +Example of usage:: + + $ python refguide_check.py optimize + +Note that this is a helper script to be able to check if things are missing; +the output of this script does need to be checked manually. In some cases +objects are left out of the refguide for a good reason (it's an alias of +another function, or deprecated, or ...) + +Another use of this helper script is to check validity of code samples +in docstrings:: + + $ python tools/refguide_check.py --doctests ma + +or in RST-based documentations:: + + $ python tools/refguide_check.py --rst doc/source + +""" +import copy +import doctest +import inspect +import io +import os +import re +import shutil +import sys +import tempfile +import warnings +import docutils.core +from argparse import ArgumentParser +from contextlib import contextmanager, redirect_stderr +from doctest import NORMALIZE_WHITESPACE, ELLIPSIS, IGNORE_EXCEPTION_DETAIL + +from docutils.parsers.rst import directives +from pkg_resources import parse_version + +import sphinx +import numpy as np + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'doc', 'sphinxext')) +from numpydoc.docscrape_sphinx import get_doc_object + +SKIPBLOCK = doctest.register_optionflag('SKIPBLOCK') + +if parse_version(sphinx.__version__) >= parse_version('1.5'): + # Enable specific Sphinx directives + from sphinx.directives.other import SeeAlso, Only + directives.register_directive('seealso', SeeAlso) + directives.register_directive('only', Only) +else: + # Remove sphinx directives that don't run without Sphinx environment. + # Sphinx < 1.5 installs all directives on import... + directives._directives.pop('versionadded', None) + directives._directives.pop('versionchanged', None) + directives._directives.pop('moduleauthor', None) + directives._directives.pop('sectionauthor', None) + directives._directives.pop('codeauthor', None) + directives._directives.pop('toctree', None) + + +BASE_MODULE = "numpy" + +PUBLIC_SUBMODULES = [ + 'core', + 'f2py', + 'linalg', + 'lib', + 'lib.recfunctions', + 'fft', + 'ma', + 'polynomial', + 'matrixlib', + 'random', + 'testing', +] + +# Docs for these modules are included in the parent module +OTHER_MODULE_DOCS = { + 'fftpack.convolve': 'fftpack', + 'io.wavfile': 'io', + 'io.arff': 'io', +} + +# these names are known to fail doctesting and we like to keep it that way +# e.g. sometimes pseudocode is acceptable etc +# +# Optionally, a subset of methods can be skipped by setting dict-values +# to a container of method-names +DOCTEST_SKIPDICT = { + # cases where NumPy docstrings import things from SciPy: + 'numpy.lib.vectorize': None, + 'numpy.random.standard_gamma': None, + 'numpy.random.gamma': None, + 'numpy.random.vonmises': None, + 'numpy.random.power': None, + 'numpy.random.zipf': None, + # remote / local file IO with DataSource is problematic in doctest: + 'numpy.lib.DataSource': None, + 'numpy.lib.Repository': None, +} +if sys.version_info < (3, 9): + DOCTEST_SKIPDICT.update({ + "numpy.core.ndarray": {"__class_getitem__"}, + "numpy.core.dtype": {"__class_getitem__"}, + "numpy.core.number": {"__class_getitem__"}, + }) + +# Skip non-numpy RST files, historical release notes +# Any single-directory exact match will skip the directory and all subdirs. +# Any exact match (like 'doc/release') will scan subdirs but skip files in +# the matched directory. +# Any filename will skip that file +RST_SKIPLIST = [ + 'scipy-sphinx-theme', + 'sphinxext', + 'neps', + 'changelog', + 'doc/release', + 'doc/source/release', + 'doc/release/upcoming_changes', + 'c-info.ufunc-tutorial.rst', + 'c-info.python-as-glue.rst', + 'f2py.getting-started.rst', + 'arrays.nditer.cython.rst', + # See PR 17222, these should be fixed + 'basics.byteswapping.rst', + 'basics.dispatch.rst', + 'basics.indexing.rst', + 'basics.subclassing.rst', + 'basics.types.rst', + 'misc.rst', +] + +# these names are not required to be present in ALL despite being in +# autosummary:: listing +REFGUIDE_ALL_SKIPLIST = [ + r'scipy\.sparse\.linalg', + r'scipy\.spatial\.distance', + r'scipy\.linalg\.blas\.[sdczi].*', + r'scipy\.linalg\.lapack\.[sdczi].*', +] + +# these names are not required to be in an autosummary:: listing +# despite being in ALL +REFGUIDE_AUTOSUMMARY_SKIPLIST = [ + # NOTE: should NumPy have a better match between autosummary + # listings and __all__? For now, TR isn't convinced this is a + # priority -- focus on just getting docstrings executed / correct + r'numpy\.*', +] +# deprecated windows in scipy.signal namespace +for name in ('barthann', 'bartlett', 'blackmanharris', 'blackman', 'bohman', + 'boxcar', 'chebwin', 'cosine', 'exponential', 'flattop', + 'gaussian', 'general_gaussian', 'hamming', 'hann', 'hanning', + 'kaiser', 'nuttall', 'parzen', 'slepian', 'triang', 'tukey'): + REFGUIDE_AUTOSUMMARY_SKIPLIST.append(r'scipy\.signal\.' + name) + +HAVE_MATPLOTLIB = False + + +def short_path(path, cwd=None): + """ + Return relative or absolute path name, whichever is shortest. + + Parameters + ---------- + path : str or None + cwd : str or None + + Returns + ------- + str + Relative path or absolute path based on current working directory + """ + if not isinstance(path, str): + return path + if cwd is None: + cwd = os.getcwd() + abspath = os.path.abspath(path) + relpath = os.path.relpath(path, cwd) + if len(abspath) <= len(relpath): + return abspath + return relpath + + +def find_names(module, names_dict): + """ + Finds the occurrences of function names, special directives like data + and functions and scipy constants in the docstrings of `module`. The + following patterns are searched for: + + * 3 spaces followed by function name, and maybe some spaces, some + dashes, and an explanation; only function names listed in + refguide are formatted like this (mostly, there may be some false + positives + * special directives, such as data and function + * (scipy.constants only): quoted list + + The `names_dict` is updated by reference and accessible in calling method + + Parameters + ---------- + module : ModuleType + The module, whose docstrings is to be searched + names_dict : dict + Dictionary which contains module name as key and a set of found + function names and directives as value + + Returns + ------- + None + """ + patterns = [ + r"^\s\s\s([a-z_0-9A-Z]+)(\s+-+.*)?$", + r"^\.\. (?:data|function)::\s*([a-z_0-9A-Z]+)\s*$" + ] + + if module.__name__ == 'scipy.constants': + patterns += ["^``([a-z_0-9A-Z]+)``"] + + patterns = [re.compile(pattern) for pattern in patterns] + module_name = module.__name__ + + for line in module.__doc__.splitlines(): + res = re.search(r"^\s*\.\. (?:currentmodule|module):: ([a-z0-9A-Z_.]+)\s*$", line) + if res: + module_name = res.group(1) + continue + + for pattern in patterns: + res = re.match(pattern, line) + if res is not None: + name = res.group(1) + entry = '.'.join([module_name, name]) + names_dict.setdefault(module_name, set()).add(name) + break + + +def get_all_dict(module): + """ + Return a copy of the __all__ dict with irrelevant items removed. + + Parameters + ---------- + module : ModuleType + The module whose __all__ dict has to be processed + + Returns + ------- + deprecated : list + List of callable and deprecated sub modules + not_deprecated : list + List of non callable or non deprecated sub modules + others : list + List of remaining types of sub modules + """ + if hasattr(module, "__all__"): + all_dict = copy.deepcopy(module.__all__) + else: + all_dict = copy.deepcopy(dir(module)) + all_dict = [name for name in all_dict + if not name.startswith("_")] + for name in ['absolute_import', 'division', 'print_function']: + try: + all_dict.remove(name) + except ValueError: + pass + if not all_dict: + # Must be a pure documentation module + all_dict.append('__doc__') + + # Modules are almost always private; real submodules need a separate + # run of refguide_check. + all_dict = [name for name in all_dict + if not inspect.ismodule(getattr(module, name, None))] + + deprecated = [] + not_deprecated = [] + for name in all_dict: + f = getattr(module, name, None) + if callable(f) and is_deprecated(f): + deprecated.append(name) + else: + not_deprecated.append(name) + + others = set(dir(module)).difference(set(deprecated)).difference(set(not_deprecated)) + + return not_deprecated, deprecated, others + + +def compare(all_dict, others, names, module_name): + """ + Return sets of objects from all_dict. + Will return three sets: + {in module_name.__all__}, + {in REFGUIDE*}, + and {missing from others} + + Parameters + ---------- + all_dict : list + List of non deprecated sub modules for module_name + others : list + List of sub modules for module_name + names : set + Set of function names or special directives present in + docstring of module_name + module_name : ModuleType + + Returns + ------- + only_all : set + only_ref : set + missing : set + """ + only_all = set() + for name in all_dict: + if name not in names: + for pat in REFGUIDE_AUTOSUMMARY_SKIPLIST: + if re.match(pat, module_name + '.' + name): + break + else: + only_all.add(name) + + only_ref = set() + missing = set() + for name in names: + if name not in all_dict: + for pat in REFGUIDE_ALL_SKIPLIST: + if re.match(pat, module_name + '.' + name): + if name not in others: + missing.add(name) + break + else: + only_ref.add(name) + + return only_all, only_ref, missing + + +def is_deprecated(f): + """ + Check if module `f` is deprecated + + Parameters + ---------- + f : ModuleType + + Returns + ------- + bool + """ + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("error") + try: + f(**{"not a kwarg":None}) + except DeprecationWarning: + return True + except Exception: + pass + return False + + +def check_items(all_dict, names, deprecated, others, module_name, dots=True): + """ + Check that `all_dict` is consistent with the `names` in `module_name` + For instance, that there are no deprecated or extra objects. + + Parameters + ---------- + all_dict : list + + names : set + + deprecated : list + + others : list + + module_name : ModuleType + + dots : bool + Whether to print a dot for each check + + Returns + ------- + list + List of [(name, success_flag, output)...] + """ + num_all = len(all_dict) + num_ref = len(names) + + output = "" + + output += "Non-deprecated objects in __all__: %i\n" % num_all + output += "Objects in refguide: %i\n\n" % num_ref + + only_all, only_ref, missing = compare(all_dict, others, names, module_name) + dep_in_ref = only_ref.intersection(deprecated) + only_ref = only_ref.difference(deprecated) + + if len(dep_in_ref) > 0: + output += "Deprecated objects in refguide::\n\n" + for name in sorted(deprecated): + output += " " + name + "\n" + + if len(only_all) == len(only_ref) == len(missing) == 0: + if dots: + output_dot('.') + return [(None, True, output)] + else: + if len(only_all) > 0: + output += "ERROR: objects in %s.__all__ but not in refguide::\n\n" % module_name + for name in sorted(only_all): + output += " " + name + "\n" + + output += "\nThis issue can be fixed by adding these objects to\n" + output += "the function listing in __init__.py for this module\n" + + if len(only_ref) > 0: + output += "ERROR: objects in refguide but not in %s.__all__::\n\n" % module_name + for name in sorted(only_ref): + output += " " + name + "\n" + + output += "\nThis issue should likely be fixed by removing these objects\n" + output += "from the function listing in __init__.py for this module\n" + output += "or adding them to __all__.\n" + + if len(missing) > 0: + output += "ERROR: missing objects::\n\n" + for name in sorted(missing): + output += " " + name + "\n" + + if dots: + output_dot('F') + return [(None, False, output)] + + +def validate_rst_syntax(text, name, dots=True): + """ + Validates the doc string in a snippet of documentation + `text` from file `name` + Parameters + ---------- + text : str + Docstring text + name : str + File name for which the doc string is to be validated + dots : bool + Whether to print a dot symbol for each check + Returns + ------- + (bool, str) + """ + if text is None: + if dots: + output_dot('E') + return False, "ERROR: %s: no documentation" % (name,) + + ok_unknown_items = set([ + 'mod', 'doc', 'currentmodule', 'autosummary', 'data', 'attr', + 'obj', 'versionadded', 'versionchanged', 'module', 'class', + 'ref', 'func', 'toctree', 'moduleauthor', 'term', 'c:member', + 'sectionauthor', 'codeauthor', 'eq', 'doi', 'DOI', 'arXiv', 'arxiv' + ]) + + # Run through docutils + error_stream = io.StringIO() + + def resolve(name, is_label=False): + return ("http://foo", name) + + token = '<RST-VALIDATE-SYNTAX-CHECK>' + + docutils.core.publish_doctree( + text, token, + settings_overrides = dict(halt_level=5, + traceback=True, + default_reference_context='title-reference', + default_role='emphasis', + link_base='', + resolve_name=resolve, + stylesheet_path='', + raw_enabled=0, + file_insertion_enabled=0, + warning_stream=error_stream)) + + # Print errors, disregarding unimportant ones + error_msg = error_stream.getvalue() + errors = error_msg.split(token) + success = True + output = "" + + for error in errors: + lines = error.splitlines() + if not lines: + continue + + m = re.match(r'.*Unknown (?:interpreted text role|directive type) "(.*)".*$', lines[0]) + if m: + if m.group(1) in ok_unknown_items: + continue + + m = re.match(r'.*Error in "math" directive:.*unknown option: "label"', " ".join(lines), re.S) + if m: + continue + + output += name + lines[0] + "::\n " + "\n ".join(lines[1:]).rstrip() + "\n" + success = False + + if not success: + output += " " + "-"*72 + "\n" + for lineno, line in enumerate(text.splitlines()): + output += " %-4d %s\n" % (lineno+1, line) + output += " " + "-"*72 + "\n\n" + + if dots: + output_dot('.' if success else 'F') + return success, output + + +def output_dot(msg='.', stream=sys.stderr): + stream.write(msg) + stream.flush() + + +def check_rest(module, names, dots=True): + """ + Check reStructuredText formatting of docstrings + + Parameters + ---------- + module : ModuleType + + names : set + + Returns + ------- + result : list + List of [(module_name, success_flag, output),...] + """ + + try: + skip_types = (dict, str, unicode, float, int) + except NameError: + # python 3 + skip_types = (dict, str, float, int) + + + results = [] + + if module.__name__[6:] not in OTHER_MODULE_DOCS: + results += [(module.__name__,) + + validate_rst_syntax(inspect.getdoc(module), + module.__name__, dots=dots)] + + for name in names: + full_name = module.__name__ + '.' + name + obj = getattr(module, name, None) + + if obj is None: + results.append((full_name, False, "%s has no docstring" % (full_name,))) + continue + elif isinstance(obj, skip_types): + continue + + if inspect.ismodule(obj): + text = inspect.getdoc(obj) + else: + try: + text = str(get_doc_object(obj)) + except Exception: + import traceback + results.append((full_name, False, + "Error in docstring format!\n" + + traceback.format_exc())) + continue + + m = re.search("([\x00-\x09\x0b-\x1f])", text) + if m: + msg = ("Docstring contains a non-printable character %r! " + "Maybe forgot r\"\"\"?" % (m.group(1),)) + results.append((full_name, False, msg)) + continue + + try: + src_file = short_path(inspect.getsourcefile(obj)) + except TypeError: + src_file = None + + if src_file: + file_full_name = src_file + ':' + full_name + else: + file_full_name = full_name + + results.append((full_name,) + validate_rst_syntax(text, file_full_name, dots=dots)) + + return results + + +### Doctest helpers #### + +# the namespace to run examples in +DEFAULT_NAMESPACE = {'np': np} + +# the namespace to do checks in +CHECK_NAMESPACE = { + 'np': np, + 'numpy': np, + 'assert_allclose': np.testing.assert_allclose, + 'assert_equal': np.testing.assert_equal, + # recognize numpy repr's + 'array': np.array, + 'matrix': np.matrix, + 'int64': np.int64, + 'uint64': np.uint64, + 'int8': np.int8, + 'int32': np.int32, + 'float32': np.float32, + 'float64': np.float64, + 'dtype': np.dtype, + 'nan': np.nan, + 'NaN': np.nan, + 'inf': np.inf, + 'Inf': np.inf, + 'StringIO': io.StringIO, +} + + +class DTRunner(doctest.DocTestRunner): + """ + The doctest runner + """ + DIVIDER = "\n" + + def __init__(self, item_name, checker=None, verbose=None, optionflags=0): + self._item_name = item_name + doctest.DocTestRunner.__init__(self, checker=checker, verbose=verbose, + optionflags=optionflags) + + def _report_item_name(self, out, new_line=False): + if self._item_name is not None: + if new_line: + out("\n") + self._item_name = None + + def report_start(self, out, test, example): + self._checker._source = example.source + return doctest.DocTestRunner.report_start(self, out, test, example) + + def report_success(self, out, test, example, got): + if self._verbose: + self._report_item_name(out, new_line=True) + return doctest.DocTestRunner.report_success(self, out, test, example, got) + + def report_unexpected_exception(self, out, test, example, exc_info): + self._report_item_name(out) + return doctest.DocTestRunner.report_unexpected_exception( + self, out, test, example, exc_info) + + def report_failure(self, out, test, example, got): + self._report_item_name(out) + return doctest.DocTestRunner.report_failure(self, out, test, + example, got) + +class Checker(doctest.OutputChecker): + """ + Check the docstrings + """ + obj_pattern = re.compile('at 0x[0-9a-fA-F]+>') + vanilla = doctest.OutputChecker() + rndm_markers = {'# random', '# Random', '#random', '#Random', "# may vary", + "# uninitialized", "#uninitialized"} + stopwords = {'plt.', '.hist', '.show', '.ylim', '.subplot(', + 'set_title', 'imshow', 'plt.show', '.axis(', '.plot(', + '.bar(', '.title', '.ylabel', '.xlabel', 'set_ylim', 'set_xlim', + '# reformatted', '.set_xlabel(', '.set_ylabel(', '.set_zlabel(', + '.set(xlim=', '.set(ylim=', '.set(xlabel=', '.set(ylabel='} + + def __init__(self, parse_namedtuples=True, ns=None, atol=1e-8, rtol=1e-2): + self.parse_namedtuples = parse_namedtuples + self.atol, self.rtol = atol, rtol + if ns is None: + self.ns = CHECK_NAMESPACE + else: + self.ns = ns + + def check_output(self, want, got, optionflags): + # cut it short if they are equal + if want == got: + return True + + # skip stopwords in source + if any(word in self._source for word in self.stopwords): + return True + + # skip random stuff + if any(word in want for word in self.rndm_markers): + return True + + # skip function/object addresses + if self.obj_pattern.search(got): + return True + + # ignore comments (e.g. signal.freqresp) + if want.lstrip().startswith("#"): + return True + + # try the standard doctest + try: + if self.vanilla.check_output(want, got, optionflags): + return True + except Exception: + pass + + # OK then, convert strings to objects + try: + a_want = eval(want, dict(self.ns)) + a_got = eval(got, dict(self.ns)) + except Exception: + # Maybe we're printing a numpy array? This produces invalid python + # code: `print(np.arange(3))` produces "[0 1 2]" w/o commas between + # values. So, reinsert commas and retry. + # TODO: handle (1) abberivation (`print(np.arange(10000))`), and + # (2) n-dim arrays with n > 1 + s_want = want.strip() + s_got = got.strip() + cond = (s_want.startswith("[") and s_want.endswith("]") and + s_got.startswith("[") and s_got.endswith("]")) + if cond: + s_want = ", ".join(s_want[1:-1].split()) + s_got = ", ".join(s_got[1:-1].split()) + return self.check_output(s_want, s_got, optionflags) + + if not self.parse_namedtuples: + return False + # suppose that "want" is a tuple, and "got" is smth like + # MoodResult(statistic=10, pvalue=0.1). + # Then convert the latter to the tuple (10, 0.1), + # and then compare the tuples. + try: + num = len(a_want) + regex = (r'[\w\d_]+\(' + + ', '.join([r'[\w\d_]+=(.+)']*num) + + r'\)') + grp = re.findall(regex, got.replace('\n', ' ')) + if len(grp) > 1: # no more than one for now + return False + # fold it back to a tuple + got_again = '(' + ', '.join(grp[0]) + ')' + return self.check_output(want, got_again, optionflags) + except Exception: + return False + + # ... and defer to numpy + try: + return self._do_check(a_want, a_got) + except Exception: + # heterog tuple, eg (1, np.array([1., 2.])) + try: + return all(self._do_check(w, g) for w, g in zip(a_want, a_got)) + except (TypeError, ValueError): + return False + + def _do_check(self, want, got): + # This should be done exactly as written to correctly handle all of + # numpy-comparable objects, strings, and heterogeneous tuples + try: + if want == got: + return True + except Exception: + pass + return np.allclose(want, got, atol=self.atol, rtol=self.rtol) + + +def _run_doctests(tests, full_name, verbose, doctest_warnings): + """ + Run modified doctests for the set of `tests`. + + Parameters + ---------- + tests : list + + full_name : str + + verbose : bool + doctest_warnings : bool + + Returns + ------- + tuple(bool, list) + Tuple of (success, output) + """ + flags = NORMALIZE_WHITESPACE | ELLIPSIS + runner = DTRunner(full_name, checker=Checker(), optionflags=flags, + verbose=verbose) + + output = io.StringIO(newline='') + success = True + + # Redirect stderr to the stdout or output + tmp_stderr = sys.stdout if doctest_warnings else output + + @contextmanager + def temp_cwd(): + cwd = os.getcwd() + tmpdir = tempfile.mkdtemp() + try: + os.chdir(tmpdir) + yield tmpdir + finally: + os.chdir(cwd) + shutil.rmtree(tmpdir) + + # Run tests, trying to restore global state afterward + cwd = os.getcwd() + with np.errstate(), np.printoptions(), temp_cwd() as tmpdir, \ + redirect_stderr(tmp_stderr): + # try to ensure random seed is NOT reproducible + np.random.seed(None) + + ns = {} + for t in tests: + # We broke the tests up into chunks to try to avoid PSEUDOCODE + # This has the unfortunate side effect of restarting the global + # namespace for each test chunk, so variables will be "lost" after + # a chunk. Chain the globals to avoid this + t.globs.update(ns) + t.filename = short_path(t.filename, cwd) + # Process our options + if any([SKIPBLOCK in ex.options for ex in t.examples]): + continue + fails, successes = runner.run(t, out=output.write, clear_globs=False) + if fails > 0: + success = False + ns = t.globs + + output.seek(0) + return success, output.read() + + +def check_doctests(module, verbose, ns=None, + dots=True, doctest_warnings=False): + """ + Check code in docstrings of the module's public symbols. + + Parameters + ---------- + module : ModuleType + Name of module + verbose : bool + Should the result be verbose + ns : dict + Name space of module + dots : bool + + doctest_warnings : bool + + Returns + ------- + results : list + List of [(item_name, success_flag, output), ...] + """ + if ns is None: + ns = dict(DEFAULT_NAMESPACE) + + # Loop over non-deprecated items + results = [] + + for name in get_all_dict(module)[0]: + full_name = module.__name__ + '.' + name + + if full_name in DOCTEST_SKIPDICT: + skip_methods = DOCTEST_SKIPDICT[full_name] + if skip_methods is None: + continue + else: + skip_methods = None + + try: + obj = getattr(module, name) + except AttributeError: + import traceback + results.append((full_name, False, + "Missing item!\n" + + traceback.format_exc())) + continue + + finder = doctest.DocTestFinder() + try: + tests = finder.find(obj, name, globs=dict(ns)) + except Exception: + import traceback + results.append((full_name, False, + "Failed to get doctests!\n" + + traceback.format_exc())) + continue + + if skip_methods is not None: + tests = [i for i in tests if + i.name.partition(".")[2] not in skip_methods] + + success, output = _run_doctests(tests, full_name, verbose, + doctest_warnings) + + if dots: + output_dot('.' if success else 'F') + + results.append((full_name, success, output)) + + if HAVE_MATPLOTLIB: + import matplotlib.pyplot as plt + plt.close('all') + + return results + + +def check_doctests_testfile(fname, verbose, ns=None, + dots=True, doctest_warnings=False): + """ + Check code in a text file. + + Mimic `check_doctests` above, differing mostly in test discovery. + (which is borrowed from stdlib's doctest.testfile here, + https://github.com/python-git/python/blob/master/Lib/doctest.py) + + Parameters + ---------- + fname : str + File name + verbose : bool + + ns : dict + Name space + + dots : bool + + doctest_warnings : bool + + Returns + ------- + list + List of [(item_name, success_flag, output), ...] + + Notes + ----- + + refguide can be signalled to skip testing code by adding + ``#doctest: +SKIP`` to the end of the line. If the output varies or is + random, add ``# may vary`` or ``# random`` to the comment. for example + + >>> plt.plot(...) # doctest: +SKIP + >>> random.randint(0,10) + 5 # random + + We also try to weed out pseudocode: + * We maintain a list of exceptions which signal pseudocode, + * We split the text file into "blocks" of code separated by empty lines + and/or intervening text. + * If a block contains a marker, the whole block is then assumed to be + pseudocode. It is then not being doctested. + + The rationale is that typically, the text looks like this: + + blah + <BLANKLINE> + >>> from numpy import some_module # pseudocode! + >>> func = some_module.some_function + >>> func(42) # still pseudocode + 146 + <BLANKLINE> + blah + <BLANKLINE> + >>> 2 + 3 # real code, doctest it + 5 + + """ + if ns is None: + ns = CHECK_NAMESPACE + results = [] + + _, short_name = os.path.split(fname) + if short_name in DOCTEST_SKIPDICT: + return results + + full_name = fname + with open(fname, encoding='utf-8') as f: + text = f.read() + + PSEUDOCODE = set(['some_function', 'some_module', 'import example', + 'ctypes.CDLL', # likely need compiling, skip it + 'integrate.nquad(func,' # ctypes integrate tutotial + ]) + + # split the text into "blocks" and try to detect and omit pseudocode blocks. + parser = doctest.DocTestParser() + good_parts = [] + base_line_no = 0 + for part in text.split('\n\n'): + try: + tests = parser.get_doctest(part, ns, fname, fname, base_line_no) + except ValueError as e: + if e.args[0].startswith('line '): + # fix line number since `parser.get_doctest` does not increment + # the reported line number by base_line_no in the error message + parts = e.args[0].split() + parts[1] = str(int(parts[1]) + base_line_no) + e.args = (' '.join(parts),) + e.args[1:] + raise + if any(word in ex.source for word in PSEUDOCODE + for ex in tests.examples): + # omit it + pass + else: + # `part` looks like a good code, let's doctest it + good_parts.append((part, base_line_no)) + base_line_no += part.count('\n') + 2 + + # Reassemble the good bits and doctest them: + tests = [] + for good_text, line_no in good_parts: + tests.append(parser.get_doctest(good_text, ns, fname, fname, line_no)) + success, output = _run_doctests(tests, full_name, verbose, + doctest_warnings) + + if dots: + output_dot('.' if success else 'F') + + results.append((full_name, success, output)) + + if HAVE_MATPLOTLIB: + import matplotlib.pyplot as plt + plt.close('all') + + return results + + +def iter_included_files(base_path, verbose=0, suffixes=('.rst',)): + """ + Generator function to walk `base_path` and its subdirectories, skipping + files or directories in RST_SKIPLIST, and yield each file with a suffix in + `suffixes` + + Parameters + ---------- + base_path : str + Base path of the directory to be processed + verbose : int + + suffixes : tuple + + Yields + ------ + path + Path of the directory and its sub directories + """ + if os.path.exists(base_path) and os.path.isfile(base_path): + yield base_path + for dir_name, subdirs, files in os.walk(base_path, topdown=True): + if dir_name in RST_SKIPLIST: + if verbose > 0: + sys.stderr.write('skipping files in %s' % dir_name) + files = [] + for p in RST_SKIPLIST: + if p in subdirs: + if verbose > 0: + sys.stderr.write('skipping %s and subdirs' % p) + subdirs.remove(p) + for f in files: + if (os.path.splitext(f)[1] in suffixes and + f not in RST_SKIPLIST): + yield os.path.join(dir_name, f) + + +def check_documentation(base_path, results, args, dots): + """ + Check examples in any *.rst located inside `base_path`. + Add the output to `results`. + + See Also + -------- + check_doctests_testfile + """ + for filename in iter_included_files(base_path, args.verbose): + if dots: + sys.stderr.write(filename + ' ') + sys.stderr.flush() + + tut_results = check_doctests_testfile( + filename, + (args.verbose >= 2), dots=dots, + doctest_warnings=args.doctest_warnings) + + # stub out a "module" which is needed when reporting the result + def scratch(): + pass + scratch.__name__ = filename + results.append((scratch, tut_results)) + if dots: + sys.stderr.write('\n') + sys.stderr.flush() + + +def init_matplotlib(): + """ + Check feasibility of matplotlib initialization. + """ + global HAVE_MATPLOTLIB + + try: + import matplotlib + matplotlib.use('Agg') + HAVE_MATPLOTLIB = True + except ImportError: + HAVE_MATPLOTLIB = False + + +def main(argv): + """ + Validates the docstrings of all the pre decided set of + modules for errors and docstring standards. + """ + parser = ArgumentParser(usage=__doc__.lstrip()) + parser.add_argument("module_names", metavar="SUBMODULES", default=[], + nargs='*', help="Submodules to check (default: all public)") + parser.add_argument("--doctests", action="store_true", + help="Run also doctests on ") + parser.add_argument("-v", "--verbose", action="count", default=0) + parser.add_argument("--doctest-warnings", action="store_true", + help="Enforce warning checking for doctests") + parser.add_argument("--rst", nargs='?', const='doc', default=None, + help=("Run also examples from *rst files " + "discovered walking the directory(s) specified, " + "defaults to 'doc'")) + args = parser.parse_args(argv) + + modules = [] + names_dict = {} + + if not args.module_names: + args.module_names = list(PUBLIC_SUBMODULES) + + os.environ['SCIPY_PIL_IMAGE_VIEWER'] = 'true' + + module_names = list(args.module_names) + for name in module_names: + if name in OTHER_MODULE_DOCS: + name = OTHER_MODULE_DOCS[name] + if name not in module_names: + module_names.append(name) + + dots = True + success = True + results = [] + errormsgs = [] + + + if args.doctests or args.rst: + init_matplotlib() + + for submodule_name in module_names: + module_name = BASE_MODULE + '.' + submodule_name + __import__(module_name) + module = sys.modules[module_name] + + if submodule_name not in OTHER_MODULE_DOCS: + find_names(module, names_dict) + + if submodule_name in args.module_names: + modules.append(module) + + + if args.doctests or not args.rst: + print("Running checks for %d modules:" % (len(modules),)) + for module in modules: + if dots: + sys.stderr.write(module.__name__ + ' ') + sys.stderr.flush() + + all_dict, deprecated, others = get_all_dict(module) + names = names_dict.get(module.__name__, set()) + + mod_results = [] + mod_results += check_items(all_dict, names, deprecated, others, + module.__name__) + mod_results += check_rest(module, set(names).difference(deprecated), + dots=dots) + if args.doctests: + mod_results += check_doctests(module, (args.verbose >= 2), dots=dots, + doctest_warnings=args.doctest_warnings) + + for v in mod_results: + assert isinstance(v, tuple), v + + results.append((module, mod_results)) + + if dots: + sys.stderr.write('\n') + sys.stderr.flush() + + if args.rst: + base_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)), '..') + rst_path = os.path.relpath(os.path.join(base_dir, args.rst)) + if os.path.exists(rst_path): + print('\nChecking files in %s:' % rst_path) + check_documentation(rst_path, results, args, dots) + else: + sys.stderr.write(f'\ninvalid --rst argument "{args.rst}"') + errormsgs.append('invalid directory argument to --rst') + if dots: + sys.stderr.write("\n") + sys.stderr.flush() + + # Report results + for module, mod_results in results: + success = all(x[1] for x in mod_results) + if not success: + errormsgs.append(f'failed checking {module.__name__}') + + if success and args.verbose == 0: + continue + + print("") + print("=" * len(module.__name__)) + print(module.__name__) + print("=" * len(module.__name__)) + print("") + + for name, success, output in mod_results: + if name is None: + if not success or args.verbose >= 1: + print(output.strip()) + print("") + elif not success or (args.verbose >= 2 and output.strip()): + print(name) + print("-"*len(name)) + print("") + print(output.strip()) + print("") + + if len(errormsgs) == 0: + print("\nOK: all checks passed!") + sys.exit(0) + else: + print('\nERROR: ', '\n '.join(errormsgs)) + sys.exit(1) + + +if __name__ == '__main__': + main(argv=sys.argv[1:]) diff --git a/tools/swig/numpy.i b/tools/swig/numpy.i index 36bb55c98cff..99ed073abe11 100644 --- a/tools/swig/numpy.i +++ b/tools/swig/numpy.i @@ -114,17 +114,12 @@ if (py_obj == NULL ) return "C NULL value"; if (py_obj == Py_None ) return "Python None" ; if (PyCallable_Check(py_obj)) return "callable" ; - if (PyString_Check( py_obj)) return "string" ; - if (PyInt_Check( py_obj)) return "int" ; + if (PyBytes_Check( py_obj)) return "string" ; + if (PyLong_Check( py_obj)) return "int" ; if (PyFloat_Check( py_obj)) return "float" ; if (PyDict_Check( py_obj)) return "dict" ; if (PyList_Check( py_obj)) return "list" ; if (PyTuple_Check( py_obj)) return "tuple" ; -%#if PY_MAJOR_VERSION < 3 - if (PyFile_Check( py_obj)) return "file" ; - if (PyModule_Check( py_obj)) return "module" ; - if (PyInstance_Check(py_obj)) return "instance" ; -%#endif return "unknown type"; } @@ -545,7 +540,7 @@ const npy_intp *dims = array_dimensions(ary); for (i=0; i < nd; ++i) n_non_one += (dims[i] != 1) ? 1 : 0; - if (n_non_one > 1) + if (n_non_one > 1) array_clearflags(ary,NPY_ARRAY_CARRAY); array_enableflags(ary,NPY_ARRAY_FARRAY); /* Recompute the strides */ @@ -2007,7 +2002,7 @@ (PyObject* array = NULL) { npy_intp dims[1]; - if (!PyInt_Check($input)) + if (!PyLong_Check($input)) { const char* typestring = pytype_string($input); PyErr_Format(PyExc_TypeError, @@ -2015,7 +2010,8 @@ typestring); SWIG_fail; } - $2 = (DIM_TYPE) PyInt_AsLong($input); + $2 = (DIM_TYPE) PyLong_AsSsize_t($input); + if ($2 == -1 && PyErr_Occurred()) SWIG_fail; dims[0] = (npy_intp) $2; array = PyArray_SimpleNew(1, dims, DATA_TYPECODE); if (!array) SWIG_fail; @@ -2035,7 +2031,7 @@ (PyObject* array = NULL) { npy_intp dims[1]; - if (!PyInt_Check($input)) + if (!PyLong_Check($input)) { const char* typestring = pytype_string($input); PyErr_Format(PyExc_TypeError, @@ -2043,7 +2039,8 @@ typestring); SWIG_fail; } - $1 = (DIM_TYPE) PyInt_AsLong($input); + $1 = (DIM_TYPE) PyLong_AsSsize_t($input); + if ($1 == -1 && PyErr_Occurred()) SWIG_fail; dims[0] = (npy_intp) $1; array = PyArray_SimpleNew(1, dims, DATA_TYPECODE); if (!array) SWIG_fail; @@ -2497,9 +2494,9 @@ if (!array) SWIG_fail; %#ifdef SWIGPY_USE_CAPSULE - PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); + PyObject* cap = PyCapsule_New((void*)(*$2), SWIGPY_CAPSULE_NAME, free_cap); %#else - PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); + PyObject* cap = PyCObject_FromVoidPtr((void*)(*$2), free); %#endif %#if NPY_API_VERSION < 0x00000007 @@ -2567,9 +2564,9 @@ if (!array) SWIG_fail; %#ifdef SWIGPY_USE_CAPSULE - PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); + PyObject* cap = PyCapsule_New((void*)(*$3), SWIGPY_CAPSULE_NAME, free_cap); %#else - PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); + PyObject* cap = PyCObject_FromVoidPtr((void*)(*$3), free); %#endif %#if NPY_API_VERSION < 0x00000007 @@ -2637,9 +2634,9 @@ if (!array || !require_fortran(array)) SWIG_fail; %#ifdef SWIGPY_USE_CAPSULE - PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); + PyObject* cap = PyCapsule_New((void*)(*$3), SWIGPY_CAPSULE_NAME, free_cap); %#else - PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); + PyObject* cap = PyCObject_FromVoidPtr((void*)(*$3), free); %#endif %#if NPY_API_VERSION < 0x00000007 @@ -2711,9 +2708,9 @@ if (!array) SWIG_fail; %#ifdef SWIGPY_USE_CAPSULE - PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); + PyObject* cap = PyCapsule_New((void*)(*$4), SWIGPY_CAPSULE_NAME, free_cap); %#else - PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); + PyObject* cap = PyCObject_FromVoidPtr((void*)(*$4), free); %#endif %#if NPY_API_VERSION < 0x00000007 @@ -2785,161 +2782,9 @@ if (!array || !require_fortran(array)) SWIG_fail; %#ifdef SWIGPY_USE_CAPSULE - PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); -%#else - PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); -%#endif - -%#if NPY_API_VERSION < 0x00000007 - PyArray_BASE(array) = cap; -%#else - PyArray_SetBaseObject(array,cap); -%#endif - - $result = SWIG_Python_AppendOutput($result,obj); -} - -/* Typemap suite for (DATA_TYPE** ARGOUTVIEWM_ARRAY4, DIM_TYPE* DIM1, DIM_TYPE* DIM2, - DIM_TYPE* DIM3, DIM_TYPE* DIM4) - */ -%typemap(in,numinputs=0) - (DATA_TYPE** ARGOUTVIEWM_ARRAY4, DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 , DIM_TYPE* DIM4 ) - (DATA_TYPE* data_temp = NULL , DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DIM_TYPE dim4_temp) -{ - $1 = &data_temp; - $2 = &dim1_temp; - $3 = &dim2_temp; - $4 = &dim3_temp; - $5 = &dim4_temp; -} -%typemap(argout, - fragment="NumPy_Backward_Compatibility,NumPy_Utilities") - (DATA_TYPE** ARGOUTVIEWM_ARRAY4, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4) -{ - npy_intp dims[4] = { *$2, *$3, *$4 , *$5 }; - PyObject* obj = PyArray_SimpleNewFromData(4, dims, DATA_TYPECODE, (void*)(*$1)); - PyArrayObject* array = (PyArrayObject*) obj; - - if (!array) SWIG_fail; - -%#ifdef SWIGPY_USE_CAPSULE - PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); -%#else - PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); -%#endif - -%#if NPY_API_VERSION < 0x00000007 - PyArray_BASE(array) = cap; -%#else - PyArray_SetBaseObject(array,cap); -%#endif - - $result = SWIG_Python_AppendOutput($result,obj); -} - -/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, - DATA_TYPE** ARGOUTVIEWM_ARRAY4) - */ -%typemap(in,numinputs=0) - (DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 , DIM_TYPE* DIM4 , DATA_TYPE** ARGOUTVIEWM_ARRAY4) - (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DIM_TYPE dim4_temp, DATA_TYPE* data_temp = NULL ) -{ - $1 = &dim1_temp; - $2 = &dim2_temp; - $3 = &dim3_temp; - $4 = &dim4_temp; - $5 = &data_temp; -} -%typemap(argout, - fragment="NumPy_Backward_Compatibility,NumPy_Utilities") - (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, DATA_TYPE** ARGOUTVIEWM_ARRAY4) -{ - npy_intp dims[4] = { *$1, *$2, *$3 , *$4 }; - PyObject* obj = PyArray_SimpleNewFromData(4, dims, DATA_TYPECODE, (void*)(*$5)); - PyArrayObject* array = (PyArrayObject*) obj; - - if (!array) SWIG_fail; - -%#ifdef SWIGPY_USE_CAPSULE - PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); -%#else - PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); -%#endif - -%#if NPY_API_VERSION < 0x00000007 - PyArray_BASE(array) = cap; -%#else - PyArray_SetBaseObject(array,cap); -%#endif - - $result = SWIG_Python_AppendOutput($result,obj); -} - -/* Typemap suite for (DATA_TYPE** ARGOUTVIEWM_FARRAY4, DIM_TYPE* DIM1, DIM_TYPE* DIM2, - DIM_TYPE* DIM3, DIM_TYPE* DIM4) - */ -%typemap(in,numinputs=0) - (DATA_TYPE** ARGOUTVIEWM_FARRAY4, DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 , DIM_TYPE* DIM4 ) - (DATA_TYPE* data_temp = NULL , DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DIM_TYPE dim4_temp) -{ - $1 = &data_temp; - $2 = &dim1_temp; - $3 = &dim2_temp; - $4 = &dim3_temp; - $5 = &dim4_temp; -} -%typemap(argout, - fragment="NumPy_Backward_Compatibility,NumPy_Array_Requirements,NumPy_Utilities") - (DATA_TYPE** ARGOUTVIEWM_FARRAY4, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3) -{ - npy_intp dims[4] = { *$2, *$3, *$4 , *$5 }; - PyObject* obj = PyArray_SimpleNewFromData(4, dims, DATA_TYPECODE, (void*)(*$1)); - PyArrayObject* array = (PyArrayObject*) obj; - - if (!array || !require_fortran(array)) SWIG_fail; - -%#ifdef SWIGPY_USE_CAPSULE - PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); + PyObject* cap = PyCapsule_New((void*)(*$4), SWIGPY_CAPSULE_NAME, free_cap); %#else - PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); -%#endif - -%#if NPY_API_VERSION < 0x00000007 - PyArray_BASE(array) = cap; -%#else - PyArray_SetBaseObject(array,cap); -%#endif - - $result = SWIG_Python_AppendOutput($result,obj); -} - -/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, - DATA_TYPE** ARGOUTVIEWM_FARRAY4) - */ -%typemap(in,numinputs=0) - (DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 , DIM_TYPE* DIM4 , DATA_TYPE** ARGOUTVIEWM_FARRAY4) - (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DIM_TYPE dim4_temp, DATA_TYPE* data_temp = NULL ) -{ - $1 = &dim1_temp; - $2 = &dim2_temp; - $3 = &dim3_temp; - $4 = &dim4_temp; - $5 = &data_temp; -} -%typemap(argout, - fragment="NumPy_Backward_Compatibility,NumPy_Array_Requirements,NumPy_Utilities") - (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, DATA_TYPE** ARGOUTVIEWM_FARRAY4) -{ - npy_intp dims[4] = { *$1, *$2, *$3 , *$4 }; - PyObject* obj = PyArray_SimpleNewFromData(4, dims, DATA_TYPECODE, (void*)(*$5)); - PyArrayObject* array = (PyArrayObject*) obj; - - if (!array || !require_fortran(array)) SWIG_fail; - -%#ifdef SWIGPY_USE_CAPSULE - PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); -%#else - PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); + PyObject* cap = PyCObject_FromVoidPtr((void*)(*$4), free); %#endif %#if NPY_API_VERSION < 0x00000007 @@ -3013,9 +2858,9 @@ if (!array) SWIG_fail; %#ifdef SWIGPY_USE_CAPSULE - PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); + PyObject* cap = PyCapsule_New((void*)(*$5), SWIGPY_CAPSULE_NAME, free_cap); %#else - PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); + PyObject* cap = PyCObject_FromVoidPtr((void*)(*$5), free); %#endif %#if NPY_API_VERSION < 0x00000007 @@ -3089,9 +2934,9 @@ if (!array || !require_fortran(array)) SWIG_fail; %#ifdef SWIGPY_USE_CAPSULE - PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); + PyObject* cap = PyCapsule_New((void*)(*$5), SWIGPY_CAPSULE_NAME, free_cap); %#else - PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); + PyObject* cap = PyCObject_FromVoidPtr((void*)(*$5), free); %#endif %#if NPY_API_VERSION < 0x00000007 diff --git a/tools/swig/pyfragments.swg b/tools/swig/pyfragments.swg index 901e6ed9dcb0..558633733da3 100644 --- a/tools/swig/pyfragments.swg +++ b/tools/swig/pyfragments.swg @@ -22,13 +22,9 @@ SWIGINTERN int SWIG_AsVal_dec(long)(PyObject * obj, long * val) { - PyArray_Descr * longDescr = PyArray_DescrNewFromType(NPY_LONG); - if (PyInt_Check(obj)) { - if (val) *val = PyInt_AsLong(obj); - return SWIG_OK; - } else if (PyLong_Check(obj)) { + if (PyLong_Check(obj)) { long v = PyLong_AsLong(obj); - if (!PyErr_Occurred()) { + if (v != -1 || !PyErr_Occurred()) { if (val) *val = v; return SWIG_OK; } else { @@ -38,8 +34,8 @@ %#ifdef SWIG_PYTHON_CAST_MODE { int dispatch = 0; - long v = PyInt_AsLong(obj); - if (!PyErr_Occurred()) { + long v = PyLong_AsLong(obj); + if (v != -1 || !PyErr_Occurred()) { if (val) *val = v; return SWIG_AddCast(SWIG_OK); } else { @@ -56,7 +52,9 @@ } %#endif if (!PyArray_IsScalar(obj,Integer)) return SWIG_TypeError; + PyArray_Descr * longDescr = PyArray_DescrNewFromType(NPY_LONG); PyArray_CastScalarToCtype(obj, (void*)val, longDescr); + Py_DECREF(longDescr); return SWIG_OK; } } @@ -74,22 +72,6 @@ SWIGINTERN int SWIG_AsVal_dec(unsigned long)(PyObject *obj, unsigned long *val) { - PyArray_Descr * ulongDescr = PyArray_DescrNewFromType(NPY_ULONG); - %#if PY_VERSION_HEX < 0x03000000 - if (PyInt_Check(obj)) - { - long v = PyInt_AsLong(obj); - if (v >= 0) - { - if (val) *val = v; - return SWIG_OK; - } - else - { - return SWIG_OverflowError; - } - } else - %#endif if (PyLong_Check(obj)) { unsigned long v = PyLong_AsUnsignedLong(obj); if (!PyErr_Occurred()) { @@ -120,7 +102,9 @@ } %#endif if (!PyArray_IsScalar(obj,Integer)) return SWIG_TypeError; + PyArray_Descr * ulongDescr = PyArray_DescrNewFromType(NPY_ULONG); PyArray_CastScalarToCtype(obj, (void*)val, ulongDescr); + Py_DECREF(ulongDescr); return SWIG_OK; } } diff --git a/tools/swig/test/Array2.cxx b/tools/swig/test/Array2.cxx index e3558f786157..2da61f728569 100644 --- a/tools/swig/test/Array2.cxx +++ b/tools/swig/test/Array2.cxx @@ -90,6 +90,11 @@ void Array2::resize(int nrows, int ncols, long* data) } } +void Array2::resize(int nrows, int ncols) +{ + resize(nrows, ncols, nullptr); +} + // Set item accessor Array1 & Array2::operator[](int i) { diff --git a/tools/swig/test/Array2.h b/tools/swig/test/Array2.h index 7f8d4ca65874..7ab68827b93b 100644 --- a/tools/swig/test/Array2.h +++ b/tools/swig/test/Array2.h @@ -31,9 +31,10 @@ class Array2 int nrows() const; int ncols() const; - // Resize array - void resize(int nrows, int ncols, long* data=0); - + // Resize array + void resize(int nrows, int ncols, long* data); + void resize(int nrows, int ncols); + // Set item accessor Array1 & operator[](int i); diff --git a/tools/swig/test/setup.py b/tools/swig/test/setup.py index 4ff870e19385..71830fd2cc53 100755 --- a/tools/swig/test/setup.py +++ b/tools/swig/test/setup.py @@ -1,9 +1,6 @@ -#! /usr/bin/env python -from __future__ import division, print_function - +#!/usr/bin/env python3 # System imports -from distutils.core import * -from distutils import sysconfig +from distutils.core import Extension, setup # Third-party modules - we depend on numpy for everything import numpy diff --git a/tools/swig/test/testArray.py b/tools/swig/test/testArray.py index 8d9c7977223b..49011bb13304 100755 --- a/tools/swig/test/testArray.py +++ b/tools/swig/test/testArray.py @@ -1,9 +1,5 @@ -#! /usr/bin/env python -from __future__ import division, absolute_import, print_function - +#!/usr/bin/env python3 # System imports -from distutils.util import get_platform -import os import sys import unittest @@ -28,24 +24,24 @@ def setUp(self): def testConstructor0(self): "Test Array1 default constructor" a = Array.Array1() - self.failUnless(isinstance(a, Array.Array1)) - self.failUnless(len(a) == 0) + self.assertTrue(isinstance(a, Array.Array1)) + self.assertTrue(len(a) == 0) def testConstructor1(self): "Test Array1 length constructor" - self.failUnless(isinstance(self.array1, Array.Array1)) + self.assertTrue(isinstance(self.array1, Array.Array1)) def testConstructor2(self): "Test Array1 array constructor" na = np.arange(self.length) aa = Array.Array1(na) - self.failUnless(isinstance(aa, Array.Array1)) + self.assertTrue(isinstance(aa, Array.Array1)) def testConstructor3(self): "Test Array1 copy constructor" for i in range(self.array1.length()): self.array1[i] = i arrayCopy = Array.Array1(self.array1) - self.failUnless(arrayCopy == self.array1) + self.assertTrue(arrayCopy == self.array1) def testConstructorBad(self): "Test Array1 length constructor, negative" @@ -53,23 +49,23 @@ def testConstructorBad(self): def testLength(self): "Test Array1 length method" - self.failUnless(self.array1.length() == self.length) + self.assertTrue(self.array1.length() == self.length) def testLen(self): "Test Array1 __len__ method" - self.failUnless(len(self.array1) == self.length) + self.assertTrue(len(self.array1) == self.length) def testResize0(self): "Test Array1 resize method, length" newLen = 2 * self.length self.array1.resize(newLen) - self.failUnless(len(self.array1) == newLen) + self.assertTrue(len(self.array1) == newLen) def testResize1(self): "Test Array1 resize method, array" a = np.zeros((2*self.length,), dtype='l') self.array1.resize(a) - self.failUnless(len(self.array1) == a.size) + self.assertTrue(len(self.array1) == a.size) def testResizeBad(self): "Test Array1 resize method, negative length" @@ -81,7 +77,7 @@ def testSetGet(self): for i in range(n): self.array1[i] = i*i for i in range(n): - self.failUnless(self.array1[i] == i*i) + self.assertTrue(self.array1[i] == i*i) def testSetBad1(self): "Test Array1 __setitem__ method, negative index" @@ -102,20 +98,20 @@ def testGetBad2(self): def testAsString(self): "Test Array1 asString method" for i in range(self.array1.length()): self.array1[i] = i+1 - self.failUnless(self.array1.asString() == "[ 1, 2, 3, 4, 5 ]") + self.assertTrue(self.array1.asString() == "[ 1, 2, 3, 4, 5 ]") def testStr(self): "Test Array1 __str__ method" for i in range(self.array1.length()): self.array1[i] = i-2 - self.failUnless(str(self.array1) == "[ -2, -1, 0, 1, 2 ]") + self.assertTrue(str(self.array1) == "[ -2, -1, 0, 1, 2 ]") def testView(self): "Test Array1 view method" for i in range(self.array1.length()): self.array1[i] = i+1 a = self.array1.view() - self.failUnless(isinstance(a, np.ndarray)) - self.failUnless(len(a) == self.length) - self.failUnless((a == [1, 2, 3, 4, 5]).all()) + self.assertTrue(isinstance(a, np.ndarray)) + self.assertTrue(len(a) == self.length) + self.assertTrue((a == [1, 2, 3, 4, 5]).all()) ###################################################################### @@ -129,18 +125,18 @@ def setUp(self): def testConstructor0(self): "Test Array2 default constructor" a = Array.Array2() - self.failUnless(isinstance(a, Array.Array2)) - self.failUnless(len(a) == 0) + self.assertTrue(isinstance(a, Array.Array2)) + self.assertTrue(len(a) == 0) def testConstructor1(self): "Test Array2 nrows, ncols constructor" - self.failUnless(isinstance(self.array2, Array.Array2)) + self.assertTrue(isinstance(self.array2, Array.Array2)) def testConstructor2(self): "Test Array2 array constructor" na = np.zeros((3, 4), dtype="l") aa = Array.Array2(na) - self.failUnless(isinstance(aa, Array.Array2)) + self.assertTrue(isinstance(aa, Array.Array2)) def testConstructor3(self): "Test Array2 copy constructor" @@ -148,7 +144,7 @@ def testConstructor3(self): for j in range(self.ncols): self.array2[i][j] = i * j arrayCopy = Array.Array2(self.array2) - self.failUnless(arrayCopy == self.array2) + self.assertTrue(arrayCopy == self.array2) def testConstructorBad1(self): "Test Array2 nrows, ncols constructor, negative nrows" @@ -160,28 +156,28 @@ def testConstructorBad2(self): def testNrows(self): "Test Array2 nrows method" - self.failUnless(self.array2.nrows() == self.nrows) + self.assertTrue(self.array2.nrows() == self.nrows) def testNcols(self): "Test Array2 ncols method" - self.failUnless(self.array2.ncols() == self.ncols) + self.assertTrue(self.array2.ncols() == self.ncols) def testLen(self): "Test Array2 __len__ method" - self.failUnless(len(self.array2) == self.nrows*self.ncols) + self.assertTrue(len(self.array2) == self.nrows*self.ncols) def testResize0(self): "Test Array2 resize method, size" newRows = 2 * self.nrows newCols = 2 * self.ncols self.array2.resize(newRows, newCols) - self.failUnless(len(self.array2) == newRows * newCols) + self.assertTrue(len(self.array2) == newRows * newCols) def testResize1(self): "Test Array2 resize method, array" a = np.zeros((2*self.nrows, 2*self.ncols), dtype='l') self.array2.resize(a) - self.failUnless(len(self.array2) == a.size) + self.assertTrue(len(self.array2) == a.size) def testResizeBad1(self): "Test Array2 resize method, negative nrows" @@ -202,7 +198,7 @@ def testSetGet1(self): for i in range(m): self.array2[i] = array1[i] for i in range(m): - self.failUnless(self.array2[i] == array1[i]) + self.assertTrue(self.array2[i] == array1[i]) def testSetGet2(self): "Test Array2 chained __setitem__, __getitem__ methods" @@ -213,7 +209,7 @@ def testSetGet2(self): self.array2[i][j] = i*j for i in range(m): for j in range(n): - self.failUnless(self.array2[i][j] == i*j) + self.assertTrue(self.array2[i][j] == i*j) def testSetBad1(self): "Test Array2 __setitem__ method, negative index" @@ -245,7 +241,7 @@ def testAsString(self): for i in range(self.nrows): for j in range(self.ncols): self.array2[i][j] = i+j - self.failUnless(self.array2.asString() == result) + self.assertTrue(self.array2.asString() == result) def testStr(self): "Test Array2 __str__ method" @@ -259,13 +255,13 @@ def testStr(self): for i in range(self.nrows): for j in range(self.ncols): self.array2[i][j] = i-j - self.failUnless(str(self.array2) == result) + self.assertTrue(str(self.array2) == result) def testView(self): "Test Array2 view method" a = self.array2.view() - self.failUnless(isinstance(a, np.ndarray)) - self.failUnless(len(a) == self.nrows) + self.assertTrue(isinstance(a, np.ndarray)) + self.assertTrue(len(a) == self.nrows) ###################################################################### @@ -278,24 +274,24 @@ def setUp(self): def testConstructor0(self): "Test ArrayZ default constructor" a = Array.ArrayZ() - self.failUnless(isinstance(a, Array.ArrayZ)) - self.failUnless(len(a) == 0) + self.assertTrue(isinstance(a, Array.ArrayZ)) + self.assertTrue(len(a) == 0) def testConstructor1(self): "Test ArrayZ length constructor" - self.failUnless(isinstance(self.array3, Array.ArrayZ)) + self.assertTrue(isinstance(self.array3, Array.ArrayZ)) def testConstructor2(self): "Test ArrayZ array constructor" na = np.arange(self.length, dtype=np.complex128) aa = Array.ArrayZ(na) - self.failUnless(isinstance(aa, Array.ArrayZ)) + self.assertTrue(isinstance(aa, Array.ArrayZ)) def testConstructor3(self): "Test ArrayZ copy constructor" for i in range(self.array3.length()): self.array3[i] = complex(i,-i) arrayCopy = Array.ArrayZ(self.array3) - self.failUnless(arrayCopy == self.array3) + self.assertTrue(arrayCopy == self.array3) def testConstructorBad(self): "Test ArrayZ length constructor, negative" @@ -303,23 +299,23 @@ def testConstructorBad(self): def testLength(self): "Test ArrayZ length method" - self.failUnless(self.array3.length() == self.length) + self.assertTrue(self.array3.length() == self.length) def testLen(self): "Test ArrayZ __len__ method" - self.failUnless(len(self.array3) == self.length) + self.assertTrue(len(self.array3) == self.length) def testResize0(self): "Test ArrayZ resize method, length" newLen = 2 * self.length self.array3.resize(newLen) - self.failUnless(len(self.array3) == newLen) + self.assertTrue(len(self.array3) == newLen) def testResize1(self): "Test ArrayZ resize method, array" a = np.zeros((2*self.length,), dtype=np.complex128) self.array3.resize(a) - self.failUnless(len(self.array3) == a.size) + self.assertTrue(len(self.array3) == a.size) def testResizeBad(self): "Test ArrayZ resize method, negative length" @@ -331,7 +327,7 @@ def testSetGet(self): for i in range(n): self.array3[i] = i*i for i in range(n): - self.failUnless(self.array3[i] == i*i) + self.assertTrue(self.array3[i] == i*i) def testSetBad1(self): "Test ArrayZ __setitem__ method, negative index" @@ -352,20 +348,20 @@ def testGetBad2(self): def testAsString(self): "Test ArrayZ asString method" for i in range(self.array3.length()): self.array3[i] = complex(i+1,-i-1) - self.failUnless(self.array3.asString() == "[ (1,-1), (2,-2), (3,-3), (4,-4), (5,-5) ]") + self.assertTrue(self.array3.asString() == "[ (1,-1), (2,-2), (3,-3), (4,-4), (5,-5) ]") def testStr(self): "Test ArrayZ __str__ method" for i in range(self.array3.length()): self.array3[i] = complex(i-2,(i-2)*2) - self.failUnless(str(self.array3) == "[ (-2,-4), (-1,-2), (0,0), (1,2), (2,4) ]") + self.assertTrue(str(self.array3) == "[ (-2,-4), (-1,-2), (0,0), (1,2), (2,4) ]") def testView(self): "Test ArrayZ view method" for i in range(self.array3.length()): self.array3[i] = complex(i+1,i+2) a = self.array3.view() - self.failUnless(isinstance(a, np.ndarray)) - self.failUnless(len(a) == self.length) - self.failUnless((a == [1+2j, 2+3j, 3+4j, 4+5j, 5+6j]).all()) + self.assertTrue(isinstance(a, np.ndarray)) + self.assertTrue(len(a) == self.length) + self.assertTrue((a == [1+2j, 2+3j, 3+4j, 4+5j, 5+6j]).all()) ###################################################################### diff --git a/tools/swig/test/testFarray.py b/tools/swig/test/testFarray.py index 0037dc9b3c42..29bf96fe2f68 100755 --- a/tools/swig/test/testFarray.py +++ b/tools/swig/test/testFarray.py @@ -1,6 +1,4 @@ -#! /usr/bin/env python -from __future__ import division, absolute_import, print_function - +#!/usr/bin/env python3 # System imports from distutils.util import get_platform import os @@ -15,7 +13,7 @@ # Add the distutils-generated build directory to the python search path and then # import the extension module -libDir = "lib.%s-%s" % (get_platform(), sys.version[:3]) +libDir = "lib.{}-{}.{}".format(get_platform(), *sys.version_info[:2]) sys.path.insert(0, os.path.join("build", libDir)) import Farray @@ -30,7 +28,7 @@ def setUp(self): def testConstructor1(self): "Test Farray size constructor" - self.failUnless(isinstance(self.array, Farray.Farray)) + self.assertTrue(isinstance(self.array, Farray.Farray)) def testConstructor2(self): "Test Farray copy constructor" @@ -38,7 +36,7 @@ def testConstructor2(self): for j in range(self.ncols): self.array[i, j] = i + j arrayCopy = Farray.Farray(self.array) - self.failUnless(arrayCopy == self.array) + self.assertTrue(arrayCopy == self.array) def testConstructorBad1(self): "Test Farray size constructor, negative nrows" @@ -50,15 +48,15 @@ def testConstructorBad2(self): def testNrows(self): "Test Farray nrows method" - self.failUnless(self.array.nrows() == self.nrows) + self.assertTrue(self.array.nrows() == self.nrows) def testNcols(self): "Test Farray ncols method" - self.failUnless(self.array.ncols() == self.ncols) + self.assertTrue(self.array.ncols() == self.ncols) def testLen(self): "Test Farray __len__ method" - self.failUnless(len(self.array) == self.nrows*self.ncols) + self.assertTrue(len(self.array) == self.nrows*self.ncols) def testSetGet(self): "Test Farray __setitem__, __getitem__ methods" @@ -69,7 +67,7 @@ def testSetGet(self): self.array[i, j] = i*j for i in range(m): for j in range(n): - self.failUnless(self.array[i, j] == i*j) + self.assertTrue(self.array[i, j] == i*j) def testSetBad1(self): "Test Farray __setitem__ method, negative row" @@ -115,7 +113,7 @@ def testAsString(self): for i in range(self.nrows): for j in range(self.ncols): self.array[i, j] = i+j - self.failUnless(self.array.asString() == result) + self.assertTrue(self.array.asString() == result) def testStr(self): "Test Farray __str__ method" @@ -129,7 +127,7 @@ def testStr(self): for i in range(self.nrows): for j in range(self.ncols): self.array[i, j] = i-j - self.failUnless(str(self.array) == result) + self.assertTrue(str(self.array) == result) def testView(self): "Test Farray view method" @@ -137,11 +135,11 @@ def testView(self): for j in range(self.ncols): self.array[i, j] = i+j a = self.array.view() - self.failUnless(isinstance(a, np.ndarray)) - self.failUnless(a.flags.f_contiguous) + self.assertTrue(isinstance(a, np.ndarray)) + self.assertTrue(a.flags.f_contiguous) for i in range(self.nrows): for j in range(self.ncols): - self.failUnless(a[i, j] == i+j) + self.assertTrue(a[i, j] == i+j) ###################################################################### diff --git a/tools/swig/test/testFlat.py b/tools/swig/test/testFlat.py index bd96bc77806c..e3e456a56415 100755 --- a/tools/swig/test/testFlat.py +++ b/tools/swig/test/testFlat.py @@ -1,9 +1,5 @@ -#! /usr/bin/env python -from __future__ import division, absolute_import, print_function - +#!/usr/bin/env python3 # System imports -from distutils.util import get_platform -import os import sys import unittest @@ -31,45 +27,45 @@ def testProcess1D(self): "Test Process function 1D array" print(self.typeStr, "... ", end=' ', file=sys.stderr) process = Flat.__dict__[self.typeStr + "Process"] - pack_output = '' + pack_output = b'' for i in range(10): pack_output += struct.pack(self.typeCode,i) x = np.frombuffer(pack_output, dtype=self.typeCode) y = x.copy() process(y) - self.assertEquals(np.all((x+1)==y),True) + self.assertEqual(np.all((x+1)==y),True) def testProcess3D(self): "Test Process function 3D array" print(self.typeStr, "... ", end=' ', file=sys.stderr) process = Flat.__dict__[self.typeStr + "Process"] - pack_output = '' + pack_output = b'' for i in range(24): pack_output += struct.pack(self.typeCode,i) x = np.frombuffer(pack_output, dtype=self.typeCode) x.shape = (2,3,4) y = x.copy() process(y) - self.assertEquals(np.all((x+1)==y),True) + self.assertEqual(np.all((x+1)==y),True) def testProcess3DTranspose(self): "Test Process function 3D array, FORTRAN order" print(self.typeStr, "... ", end=' ', file=sys.stderr) process = Flat.__dict__[self.typeStr + "Process"] - pack_output = '' + pack_output = b'' for i in range(24): pack_output += struct.pack(self.typeCode,i) x = np.frombuffer(pack_output, dtype=self.typeCode) x.shape = (2,3,4) y = x.copy() process(y.T) - self.assertEquals(np.all((x.T+1)==y.T),True) + self.assertEqual(np.all((x.T+1)==y.T),True) def testProcessNoncontiguous(self): "Test Process function with non-contiguous array, which should raise an error" print(self.typeStr, "... ", end=' ', file=sys.stderr) process = Flat.__dict__[self.typeStr + "Process"] - pack_output = '' + pack_output = b'' for i in range(24): pack_output += struct.pack(self.typeCode,i) x = np.frombuffer(pack_output, dtype=self.typeCode) diff --git a/tools/swig/test/testFortran.py b/tools/swig/test/testFortran.py index b7783be90969..348355afcba8 100644 --- a/tools/swig/test/testFortran.py +++ b/tools/swig/test/testFortran.py @@ -1,9 +1,5 @@ -#! /usr/bin/env python -from __future__ import division, absolute_import, print_function - +#!/usr/bin/env python3 # System imports -from distutils.util import get_platform -import os import sys import unittest @@ -31,14 +27,14 @@ def testSecondElementFortran(self): second = Fortran.__dict__[self.typeStr + "SecondElement"] matrix = np.asfortranarray(np.arange(9).reshape(3, 3), self.typeCode) - self.assertEquals(second(matrix), 3) + self.assertEqual(second(matrix), 3) def testSecondElementObject(self): "Test Fortran matrix initialized from nested list fortranarray" print(self.typeStr, "... ", end=' ', file=sys.stderr) second = Fortran.__dict__[self.typeStr + "SecondElement"] matrix = np.asfortranarray([[0, 1, 2], [3, 4, 5], [6, 7, 8]], self.typeCode) - self.assertEquals(second(matrix), 3) + self.assertEqual(second(matrix), 3) ###################################################################### diff --git a/tools/swig/test/testMatrix.py b/tools/swig/test/testMatrix.py index 7127678f763c..814c0d578039 100755 --- a/tools/swig/test/testMatrix.py +++ b/tools/swig/test/testMatrix.py @@ -1,9 +1,5 @@ -#! /usr/bin/env python -from __future__ import division, absolute_import, print_function - +#!/usr/bin/env python3 # System imports -from distutils.util import get_platform -import os import sys import unittest @@ -30,7 +26,7 @@ def testDet(self): print(self.typeStr, "... ", end=' ', file=sys.stderr) det = Matrix.__dict__[self.typeStr + "Det"] matrix = [[8, 7], [6, 9]] - self.assertEquals(det(matrix), 30) + self.assertEqual(det(matrix), 30) # Test (type IN_ARRAY2[ANY][ANY]) typemap def testDetBadList(self): @@ -69,7 +65,7 @@ def testMax(self): print(self.typeStr, "... ", end=' ', file=sys.stderr) max = Matrix.__dict__[self.typeStr + "Max"] matrix = [[6, 5, 4], [3, 2, 1]] - self.assertEquals(max(matrix), 6) + self.assertEqual(max(matrix), 6) # Test (type* IN_ARRAY2, int DIM1, int DIM2) typemap def testMaxBadList(self): @@ -99,7 +95,7 @@ def testMin(self): print(self.typeStr, "... ", end=' ', file=sys.stderr) min = Matrix.__dict__[self.typeStr + "Min"] matrix = [[9, 8], [7, 6], [5, 4]] - self.assertEquals(min(matrix), 4) + self.assertEqual(min(matrix), 4) # Test (int DIM1, int DIM2, type* IN_ARRAY2) typemap def testMinBadList(self): @@ -130,7 +126,7 @@ def testScale(self): scale = Matrix.__dict__[self.typeStr + "Scale"] matrix = np.array([[1, 2, 3], [2, 1, 2], [3, 2, 1]], self.typeCode) scale(matrix, 4) - self.assertEquals((matrix == [[4, 8, 12], [8, 4, 8], [12, 8, 4]]).all(), True) + self.assertEqual((matrix == [[4, 8, 12], [8, 4, 8], [12, 8, 4]]).all(), True) # Test (type INPLACE_ARRAY2[ANY][ANY]) typemap def testScaleWrongDim(self): @@ -236,8 +232,8 @@ def testLUSplit(self): print(self.typeStr, "... ", end=' ', file=sys.stderr) luSplit = Matrix.__dict__[self.typeStr + "LUSplit"] lower, upper = luSplit([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - self.assertEquals((lower == [[1, 0, 0], [4, 5, 0], [7, 8, 9]]).all(), True) - self.assertEquals((upper == [[0, 2, 3], [0, 0, 6], [0, 0, 0]]).all(), True) + self.assertEqual((lower == [[1, 0, 0], [4, 5, 0], [7, 8, 9]]).all(), True) + self.assertEqual((upper == [[0, 2, 3], [0, 0, 6], [0, 0, 0]]).all(), True) ###################################################################### diff --git a/tools/swig/test/testSuperTensor.py b/tools/swig/test/testSuperTensor.py index cdd88530b2be..121c4a405805 100644 --- a/tools/swig/test/testSuperTensor.py +++ b/tools/swig/test/testSuperTensor.py @@ -1,10 +1,5 @@ -#! /usr/bin/env python -from __future__ import division, print_function - +#!/usr/bin/env python3 # System imports -from distutils.util import get_platform -from math import sqrt -import os import sys import unittest @@ -73,7 +68,7 @@ def testMax(self): print(self.typeStr, "... ", file=sys.stderr) max = SuperTensor.__dict__[self.typeStr + "Max"] supertensor = [[[[1, 2], [3, 4]], [[5, 6], [7, 8]]], [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]] - self.assertEquals(max(supertensor), 8) + self.assertEqual(max(supertensor), 8) # Test (type* IN_ARRAY3, int DIM1, int DIM2, int DIM3) typemap def testMaxBadList(self): @@ -103,7 +98,7 @@ def testMin(self): print(self.typeStr, "... ", file=sys.stderr) min = SuperTensor.__dict__[self.typeStr + "Min"] supertensor = [[[[9, 8], [7, 6]], [[5, 4], [3, 2]]], [[[9, 8], [7, 6]], [[5, 4], [3, 2]]]] - self.assertEquals(min(supertensor), 2) + self.assertEqual(min(supertensor), 2) # Test (int DIM1, int DIM2, int DIM3, type* IN_ARRAY3) typemap def testMinBadList(self): @@ -135,7 +130,7 @@ def testScale(self): supertensor = np.arange(3*3*3*3, dtype=self.typeCode).reshape((3, 3, 3, 3)) answer = supertensor.copy()*4 scale(supertensor, 4) - self.assertEquals((supertensor == answer).all(), True) + self.assertEqual((supertensor == answer).all(), True) # Test (type INPLACE_ARRAY3[ANY][ANY][ANY]) typemap def testScaleWrongType(self): @@ -252,8 +247,8 @@ def testLUSplit(self): answer_upper = [[[[0, 0], [0, 1]], [[0, 1], [1, 1]]], [[[0, 1], [1, 1]], [[1, 1], [1, 1]]]] answer_lower = [[[[1, 1], [1, 0]], [[1, 0], [0, 0]]], [[[1, 0], [0, 0]], [[0, 0], [0, 0]]]] lower, upper = luSplit(supertensor) - self.assertEquals((lower == answer_lower).all(), True) - self.assertEquals((upper == answer_upper).all(), True) + self.assertEqual((lower == answer_lower).all(), True) + self.assertEqual((upper == answer_upper).all(), True) ###################################################################### diff --git a/tools/swig/test/testTensor.py b/tools/swig/test/testTensor.py index 61dc820904b1..164ceb2d5626 100755 --- a/tools/swig/test/testTensor.py +++ b/tools/swig/test/testTensor.py @@ -1,10 +1,6 @@ -#! /usr/bin/env python -from __future__ import division, absolute_import, print_function - +#!/usr/bin/env python3 # System imports -from distutils.util import get_platform from math import sqrt -import os import sys import unittest @@ -34,7 +30,7 @@ def testNorm(self): tensor = [[[0, 1], [2, 3]], [[3, 2], [1, 0]]] if isinstance(self.result, int): - self.assertEquals(norm(tensor), self.result) + self.assertEqual(norm(tensor), self.result) else: self.assertAlmostEqual(norm(tensor), self.result, 6) @@ -79,7 +75,7 @@ def testMax(self): max = Tensor.__dict__[self.typeStr + "Max"] tensor = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]] - self.assertEquals(max(tensor), 8) + self.assertEqual(max(tensor), 8) # Test (type* IN_ARRAY3, int DIM1, int DIM2, int DIM3) typemap def testMaxBadList(self): @@ -111,7 +107,7 @@ def testMin(self): min = Tensor.__dict__[self.typeStr + "Min"] tensor = [[[9, 8], [7, 6]], [[5, 4], [3, 2]]] - self.assertEquals(min(tensor), 2) + self.assertEqual(min(tensor), 2) # Test (int DIM1, int DIM2, int DIM3, type* IN_ARRAY3) typemap def testMinBadList(self): @@ -145,7 +141,7 @@ def testScale(self): [[0, 1, 0], [1, 0, 1], [0, 1, 0]], [[1, 0, 1], [0, 1, 0], [1, 0, 1]]], self.typeCode) scale(tensor, 4) - self.assertEquals((tensor == [[[4, 0, 4], [0, 4, 0], [4, 0, 4]], + self.assertEqual((tensor == [[[4, 0, 4], [0, 4, 0], [4, 0, 4]], [[0, 4, 0], [4, 0, 4], [0, 4, 0]], [[4, 0, 4], [0, 4, 0], [4, 0, 4]]]).all(), True) @@ -264,9 +260,9 @@ def testLUSplit(self): luSplit = Tensor.__dict__[self.typeStr + "LUSplit"] lower, upper = luSplit([[[1, 1], [1, 1]], [[1, 1], [1, 1]]]) - self.assertEquals((lower == [[[1, 1], [1, 0]], + self.assertEqual((lower == [[[1, 1], [1, 0]], [[1, 0], [0, 0]]]).all(), True) - self.assertEquals((upper == [[[0, 0], [0, 1]], + self.assertEqual((upper == [[[0, 0], [0, 1]], [[0, 1], [1, 1]]]).all(), True) ###################################################################### diff --git a/tools/swig/test/testVector.py b/tools/swig/test/testVector.py index eaaa751029a8..1a663d1db83b 100755 --- a/tools/swig/test/testVector.py +++ b/tools/swig/test/testVector.py @@ -1,9 +1,5 @@ -#! /usr/bin/env python -from __future__ import division, absolute_import, print_function - +#!/usr/bin/env python3 # System imports -from distutils.util import get_platform -import os import sys import unittest @@ -29,7 +25,7 @@ def testLength(self): "Test length function" print(self.typeStr, "... ", end=' ', file=sys.stderr) length = Vector.__dict__[self.typeStr + "Length"] - self.assertEquals(length([5, 12, 0]), 13) + self.assertEqual(length([5, 12, 0]), 13) # Test the (type IN_ARRAY1[ANY]) typemap def testLengthBadList(self): @@ -64,7 +60,7 @@ def testProd(self): "Test prod function" print(self.typeStr, "... ", end=' ', file=sys.stderr) prod = Vector.__dict__[self.typeStr + "Prod"] - self.assertEquals(prod([1, 2, 3, 4]), 24) + self.assertEqual(prod([1, 2, 3, 4]), 24) # Test the (type* IN_ARRAY1, int DIM1) typemap def testProdBadList(self): @@ -92,7 +88,7 @@ def testSum(self): "Test sum function" print(self.typeStr, "... ", end=' ', file=sys.stderr) sum = Vector.__dict__[self.typeStr + "Sum"] - self.assertEquals(sum([5, 6, 7, 8]), 26) + self.assertEqual(sum([5, 6, 7, 8]), 26) # Test the (int DIM1, type* IN_ARRAY1) typemap def testSumBadList(self): @@ -122,7 +118,7 @@ def testReverse(self): reverse = Vector.__dict__[self.typeStr + "Reverse"] vector = np.array([1, 2, 4], self.typeCode) reverse(vector) - self.assertEquals((vector == [4, 2, 1]).all(), True) + self.assertEqual((vector == [4, 2, 1]).all(), True) # Test the (type INPLACE_ARRAY1[ANY]) typemap def testReverseWrongDim(self): @@ -225,8 +221,8 @@ def testEOSplit(self): print(self.typeStr, "... ", end=' ', file=sys.stderr) eoSplit = Vector.__dict__[self.typeStr + "EOSplit"] even, odd = eoSplit([1, 2, 3]) - self.assertEquals((even == [1, 0, 3]).all(), True) - self.assertEquals((odd == [0, 2, 0]).all(), True) + self.assertEqual((even == [1, 0, 3]).all(), True) + self.assertEqual((odd == [0, 2, 0]).all(), True) # Test the (type* ARGOUT_ARRAY1, int DIM1) typemap def testTwos(self): @@ -234,7 +230,7 @@ def testTwos(self): print(self.typeStr, "... ", end=' ', file=sys.stderr) twos = Vector.__dict__[self.typeStr + "Twos"] vector = twos(5) - self.assertEquals((vector == [2, 2, 2, 2, 2]).all(), True) + self.assertEqual((vector == [2, 2, 2, 2, 2]).all(), True) # Test the (type* ARGOUT_ARRAY1, int DIM1) typemap def testTwosNonInt(self): @@ -249,7 +245,7 @@ def testThrees(self): print(self.typeStr, "... ", end=' ', file=sys.stderr) threes = Vector.__dict__[self.typeStr + "Threes"] vector = threes(6) - self.assertEquals((vector == [3, 3, 3, 3, 3, 3]).all(), True) + self.assertEqual((vector == [3, 3, 3, 3, 3, 3]).all(), True) # Test the (type* ARGOUT_ARRAY1, int DIM1) typemap def testThreesNonInt(self): diff --git a/tools/test-installed-numpy.py b/tools/test-installed-numpy.py deleted file mode 100644 index 04a2a1da2183..000000000000 --- a/tools/test-installed-numpy.py +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env python -from __future__ import division, absolute_import, print_function - -# A simple script to test the installed version of numpy by calling -# 'numpy.test()'. Key features: -# -- convenient command-line syntax -# -- sets exit status appropriately, useful for automated test environments - -# It would be better to set this up as a module in the numpy namespace, so -# that it could be run as: -# python -m numpy.run_tests <args> -# But, python2.4's -m switch only works with top-level modules, not modules -# that are inside packages. So, once we drop 2.4 support, maybe... - -import sys, os -# In case we are run from the source directory, we don't want to import numpy -# from there, we want to import the installed version: -sys.path.pop(0) - -from optparse import OptionParser -parser = OptionParser("usage: %prog [options] -- [nosetests options]") -parser.add_option("-v", "--verbose", - action="count", dest="verbose", default=1, - help="increase verbosity") -parser.add_option("--doctests", - action="store_true", dest="doctests", default=False, - help="Run doctests in module") -parser.add_option("--coverage", - action="store_true", dest="coverage", default=False, - help="report coverage of NumPy code (requires 'pytest-cov' module") -parser.add_option("-m", "--mode", - action="store", dest="mode", default="fast", - help="'fast', 'full', or something that could be " - "passed to pytest [default: %default]") -(options, args) = parser.parse_args() - -import numpy - -# Check that NPY_RELAXED_STRIDES_CHECKING is active when set. -# The same flags check is also used in the tests to switch behavior. -if (os.environ.get('NPY_RELAXED_STRIDES_CHECKING', "1") != "0"): - if not numpy.ones((10, 1), order='C').flags.f_contiguous: - print('NPY_RELAXED_STRIDES_CHECKING set, but not active.') - sys.exit(1) -elif numpy.ones((10, 1), order='C').flags.f_contiguous: - print('NPY_RELAXED_STRIDES_CHECKING not set, but active.') - sys.exit(1) - -result = numpy.test(options.mode, - verbose=options.verbose, - extra_argv=args, - doctests=options.doctests, - coverage=options.coverage) - -if result: - sys.exit(0) -else: - sys.exit(1) diff --git a/tools/travis-before-install.sh b/tools/travis-before-install.sh index 1671d35b42b4..056e9747278f 100755 --- a/tools/travis-before-install.sh +++ b/tools/travis-before-install.sh @@ -1,31 +1,61 @@ #!/bin/bash +# Exit the script immediately if a command exits with a non-zero status, +# and print commands and their arguments as they are executed. +set -ex + uname -a free -m df -h ulimit -a + +sudo apt update +sudo apt install gfortran eatmydata libgfortran5 + +if [ "$USE_DEBUG" ] +then + sudo apt install python3-dbg python3-dev python3-setuptools +fi + mkdir builds pushd builds # Build into own virtualenv # We therefore control our own environment, avoid travis' numpy -# -# Some change in virtualenv 14.0.5 caused `test_f2py` to fail. So, we have -# pinned `virtualenv` to the last known working version to avoid this failure. -# Appears we had some issues with certificates on Travis. It looks like -# bumping to 14.0.6 will help. -pip install -U 'virtualenv==14.0.6' if [ -n "$USE_DEBUG" ] then - virtualenv --python=python3-dbg venv + python3-dbg -m venv venv else - virtualenv --python=python venv + python -m venv venv fi source venv/bin/activate python -V -pip install --upgrade pip setuptools -pip install nose pytz cython pytest -if [ -n "$USE_ASV" ]; then pip install asv; fi +gcc --version + popd + +pip install --upgrade pip 'setuptools<49.2.0' wheel + +# 'setuptools', 'wheel' and 'cython' are build dependencies. This information +# is stored in pyproject.toml, but there is not yet a standard way to install +# those dependencies with, say, a pip command, so we'll just hard-code their +# installation here. We only need to install them separately for the cases +# where numpy is installed with setup.py, which is the case for the Travis jobs +# where the environment variables USE_DEBUG or USE_WHEEL are set. When pip is +# used to install numpy, pip gets the build dependencies from pyproject.toml. +# A specific version of cython is required, so we read the cython package +# requirement using `grep cython test_requirements.txt` instead of simply +# writing 'pip install setuptools wheel cython'. +pip install `grep cython test_requirements.txt` + +if [ -n "$DOWNLOAD_OPENBLAS" ]; then + pwd + target=$(python tools/openblas_support.py) + sudo cp -r $target/lib/* /usr/lib + sudo cp $target/include/* /usr/include +fi + + +if [ -n "$USE_ASV" ]; then pip install asv; fi diff --git a/tools/travis-sorter.py b/tools/travis-sorter.py new file mode 100755 index 000000000000..416f9fe761d0 --- /dev/null +++ b/tools/travis-sorter.py @@ -0,0 +1,287 @@ +#!/usr/bin/env python3 +""" +Run with a repo/build number or list of Travis CI build times to show the optimal build +order to run faster and make full use of all available parallel build jobs. + +Requires the Travis Client CLI + +https://github.com/travis-ci/travis.rb#installation + +# Example + +$ # Check build 22 of hugovk/numpy, and skip the first job (it's a single stage) +$ travis-sorter.py hugovk/numpy 22 --skip 1 +travis show -r hugovk/numpy 22 +[8, 7, 8, 10, 9, 18, 8, 11, 8, 10, 8, 8, 17, 8, 26] +[7, 8, 10, 9, 18, 8, 11, 8, 10, 8, 8, 17, 8, 26] +Before: + +ID Duration in mins + 1 ******* + 2 ******** + 3 ********** + 4 ********* + 5 ****************** + 6 ******** + 7 *********** + 8 ******** + 9 ********** +10 ******** +11 ******** +12 ***************** +13 ******** +14 ************************** +End: 46 + ---------------------------------------------- + +After: + +ID Duration in mins +14 ************************** + 5 ****************** +12 ***************** + 7 *********** + 3 ********** + 9 ********** + 4 ********* + 2 ******** + 6 ******** + 8 ******** +10 ******** +11 ******** +13 ******** + 1 ******* +End: 34 + ---------------------------------- + +# Example + +$ python travis-sorter.py 4 4 4 4 4 12 19 + +Before: + +**** +**** +**** +**** +**** + ************ + ******************* +12345678901234567890123 = 23 minutes + +After: + +******************* +************ +**** +**** +**** + **** + **** +1234567890123456789 = 19 minutes +""" +import argparse +import re +import subprocess +import sys + +count = 1 + + +def summarise(jobs): + end = 0 + print("ID Duration in mins") + for job in jobs: + before = " " * job.started + active = "*" * job.length + print("{:2d} {}{}".format(job.id, before, active)) + if job.started + job.length > end: + end = job.started + job.length + # for job in jobs: + # print(job) + print("End:", end) + print(" " + "-" * end) + + +class Job: + def __init__(self, length): + global count + self.id = count + count += 1 + self.length = length + self.started = -1 + self.status = "not started" + self.ended = False + + def __str__(self): + return "{}\tLength: {}\tStarted: {}\tEnded: {}".format( + self.id, self.length, self.started, self.ended + ) + + +def count_status(jobs, status): + number = 0 + for job in jobs: + if job.status == status: + number += 1 + return number + + +def simulate(jobs, limit): + + time = 0 + + # summarise(jobs) + + while True: + # Check if any have ended + for job in jobs: + if job.status == "active": + if time >= job.started + job.length: + # print("{}/{} Finished:".format(count_status(jobs, "active"), limit)) + job.ended = time + job.status = "finished" + # print(job) + + # Check if any can start + for job in jobs: + if job.status == "not started": + if count_status(jobs, "active") < limit: + # print("{}/{} Starting:".format(count_status(jobs, "active"), limit)) + job.started = time + job.status = "active" + # print(job) + + time += 1 + + # Exit loop? + if count_status(jobs, "finished") == len(jobs): + break + + summarise(jobs) + + +def do_thing(repo, number): + cmd = f"travis show -r {repo} {number or ''}" + # cmd = f"travis show --com -r {repo} {number or ''}" + print(cmd) + + exitcode = 0 + # For offline testing + output = """Build #4: Upgrade Python syntax with pyupgrade https://github.com/asottile/pyupgrade +State: passed +Type: push +Branch: add-3.7 +Compare URL: https://github.com/hugovk/diff-cover/compare/4ae7cf97c6fa...7eeddb300175 +Duration: 16 min 7 sec +Started: 2018-10-17 19:03:01 +Finished: 2018-10-17 19:09:53 + +#4.1 passed: 1 min os: linux, env: TOXENV=py27, python: 2.7 +#4.2 passed: 1 min 43 sec os: linux, env: TOXENV=py34, python: 3.4 +#4.3 passed: 1 min 52 sec os: linux, env: TOXENV=py35, python: 3.5 +#4.4 passed: 1 min 38 sec os: linux, env: TOXENV=py36, python: 3.6 +#4.5 passed: 1 min 47 sec os: linux, env: TOXENV=py37, python: 3.7 +#4.6 passed: 4 min 35 sec os: linux, env: TOXENV=pypy, python: pypy +#4.7 passed: 3 min 17 sec os: linux, env: TOXENV=pypy3, python: pypy3""" + + # For offline testing + output = """Build #9: :arrows_clockwise: [EngCom] Public Pull Requests - 2.3-develop +State: errored +Type: push +Branch: 2.3-develop +Compare URL: https://github.com/hugovk/magento2/compare/80469a61e061...77af5d65ef4f +Duration: 4 hrs 12 min 13 sec +Started: 2018-10-27 17:50:51 +Finished: 2018-10-27 18:54:14 + +#9.1 passed: 3 min 30 sec os: linux, env: TEST_SUITE=unit, php: 7.1 +#9.2 passed: 3 min 35 sec os: linux, env: TEST_SUITE=unit, php: 7.2 +#9.3 passed: 3 min 41 sec os: linux, env: TEST_SUITE=static, php: 7.2 +#9.4 passed: 8 min 48 sec os: linux, env: TEST_SUITE=js GRUNT_COMMAND=spec, php: 7.2 +#9.5 passed: 3 min 24 sec os: linux, env: TEST_SUITE=js GRUNT_COMMAND=static, php: 7.2 +#9.6 errored: 50 min os: linux, env: TEST_SUITE=integration INTEGRATION_INDEX=1, php: 7.1 +#9.7 passed: 49 min 25 sec os: linux, env: TEST_SUITE=integration INTEGRATION_INDEX=1, php: 7.2 +#9.8 passed: 31 min 54 sec os: linux, env: TEST_SUITE=integration INTEGRATION_INDEX=2, php: 7.1 +#9.9 passed: 31 min 24 sec os: linux, env: TEST_SUITE=integration INTEGRATION_INDEX=2, php: 7.2 +#9.10 passed: 27 min 23 sec os: linux, env: TEST_SUITE=integration INTEGRATION_INDEX=3, php: 7.1 +#9.11 passed: 26 min 9 sec os: linux, env: TEST_SUITE=integration INTEGRATION_INDEX=3, php: 7.2 +#9.12 passed: 13 min os: linux, env: TEST_SUITE=functional, php: 7.2""" + + # Real use + exitcode, output = subprocess.getstatusoutput(cmd) + + # print(exitcode) + # print(output) + if exitcode != 0: + print(output) + sys.exit(exitcode) + + minutes = [] + matches = re.findall(r"(pass|fail|error)ed.* (\d+) min (\d+)? ", output) + for match in matches: + status, m, s = match + s = 0 if s == "" else int(s) + s += int(m) * 60 + minutes.append(round(s / 60)) + + # print(minutes) + return minutes + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description="Either give minutes for --jobs (3 5 3 2 5), " + "or --repo slug (hugovk/test) and build --number (5)", + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) + parser.add_argument( + "input", + nargs="+", + help="Either: times for each build job (minutes), " + "or an org/repo slug and optionally build number", + ) + parser.add_argument( + "-l", "--limit", type=int, default=5, help="Concurrent jobs limit" + ) + parser.add_argument( + "-s", "--skip", type=int, default=0, help="Skip X jobs at the start" + ) + args = parser.parse_args() + + # If all ints + try: + for x in args.input: + int(x) + job_times = args.input + except ValueError: + try: + number = args.input[1] + except IndexError: + number = None + job_times = do_thing(args.input[0], number) + + job_times = job_times[args.skip :] + # print(job_times) + + print("Before:") + print() + + jobs = [] + for job_time in job_times: + job = Job(job_time) + jobs.append(job) + + simulate(jobs, args.limit) + + print() + print("After:") + print() + + # Sort with longest first + jobs.sort(key=lambda job: job.length, reverse=True) + # Reset status + for job in jobs: + job.status = "not started" + + simulate(jobs, args.limit) diff --git a/tools/travis-test.sh b/tools/travis-test.sh index b99866f0d810..b395942fba8a 100755 --- a/tools/travis-test.sh +++ b/tools/travis-test.sh @@ -25,31 +25,49 @@ if [ -n "$PYTHON_OPTS" ]; then fi # make some warnings fatal, mostly to match windows compilers -werrors="-Werror=declaration-after-statement -Werror=vla " -werrors+="-Werror=nonnull -Werror=pointer-arith" +werrors="-Werror=vla -Werror=nonnull -Werror=pointer-arith" +werrors="$werrors -Werror=implicit-function-declaration" + +# build with c99 by default setup_base() { - # use default python flags but remoge sign-compare + # use default python flags but remove sign-compare sysflags="$($PYTHON -c "from distutils import sysconfig; \ print (sysconfig.get_config_var('CFLAGS'))")" export CFLAGS="$sysflags $werrors -Wlogical-op -Wno-sign-compare" + # SIMD extensions that need to be tested on both runtime and compile-time via (test_simd.py) + # any specified features will be ignored if they're not supported by compiler or platform + # note: it almost the same default value of --simd-test execpt adding policy `$werror` to treat all + # warnings as errors + simd_test="\$werror BASELINE SSE2 SSE42 XOP FMA4 (FMA3 AVX2) AVX512F AVX512_SKX VSX VSX2 VSX3 NEON ASIMD" # We used to use 'setup.py install' here, but that has the terrible # behaviour that if a copy of the package is already installed in the # install location, then the new copy just gets dropped on top of it. # Travis typically has a stable numpy release pre-installed, and if we # don't remove it, then we can accidentally end up e.g. running old # test modules that were in the stable release but have been removed - # from master. (See gh-2765, gh-2768.) Using 'pip install' also has + # from main. (See gh-2765, gh-2768.) Using 'pip install' also has # the advantage that it tests that numpy is 'pip install' compatible, # see e.g. gh-2766... if [ -z "$USE_DEBUG" ]; then - $PIP install -v . 2>&1 | tee log + # activates '-Werror=undef' when DEBUG isn't enabled since _cffi_backend' + # extension breaks the build due to the following error: + # + # error: "HAVE_FFI_PREP_CIF_VAR" is not defined, evaluates to 0 [-Werror=undef] + # #if !HAVE_FFI_PREP_CIF_VAR && defined(__arm64__) && defined(__APPLE__) + # + export CFLAGS="$CFLAGS -Werror=undef" + $PYTHON setup.py build --simd-test "$simd_test" install 2>&1 | tee log else - $PYTHON setup.py build_ext --inplace 2>&1 | tee log + # The job run with USE_DEBUG=1 on travis needs this. + export CFLAGS=$CFLAGS" -Wno-maybe-uninitialized" + $PYTHON setup.py build --simd-test "$simd_test" build_src --verbose-cfg build_ext --inplace 2>&1 | tee log fi grep -v "_configtest" log \ - | grep -vE "ld returned 1|no previously-included files matching|manifest_maker: standard file '-c'" \ + | grep -vE "ld returned 1|no files found matching" \ + | grep -vE "no previously-included files matching" \ + | grep -vE "manifest_maker: standard file '-c'" \ | grep -E "warning\>" \ | tee warnings if [ "$LAPACK" != "None" ]; then @@ -57,52 +75,24 @@ setup_base() fi } -setup_chroot() -{ - # this can all be replaced with: - # apt-get install libpython2.7-dev:i386 - # CC="gcc -m32" LDSHARED="gcc -m32 -shared" LDFLAGS="-m32 -shared" \ - # linux32 python setup.py build - # when travis updates to ubuntu 14.04 - # - # NumPy may not distinguish between 64 and 32 bit ATLAS in the - # configuration stage. - DIR=$1 - set -u - sudo debootstrap --variant=buildd --include=fakeroot,build-essential \ - --arch=$ARCH --foreign $DIST $DIR - sudo chroot $DIR ./debootstrap/debootstrap --second-stage - - # put the numpy repo in the chroot directory - sudo rsync -a $TRAVIS_BUILD_DIR $DIR/ - - # set up repos in the chroot directory for installing packages - echo deb http://archive.ubuntu.com/ubuntu/ \ - $DIST main restricted universe multiverse \ - | sudo tee -a $DIR/etc/apt/sources.list - echo deb http://archive.ubuntu.com/ubuntu/ \ - $DIST-updates main restricted universe multiverse \ - | sudo tee -a $DIR/etc/apt/sources.list - echo deb http://security.ubuntu.com/ubuntu \ - $DIST-security main restricted universe multiverse \ - | sudo tee -a $DIR/etc/apt/sources.list - - sudo chroot $DIR bash -c "apt-get update" - # faster operation with preloaded eatmydata - sudo chroot $DIR bash -c "apt-get install -qq -y eatmydata" - echo '/usr/$LIB/libeatmydata.so' | \ - sudo tee -a $DIR/etc/ld.so.preload - - # install needed packages - sudo chroot $DIR bash -c "apt-get install -qq -y \ - libatlas-base-dev gfortran python-dev python-nose python-pip cython \ - python-pytest" -} - run_test() { + # Install the test dependencies. + # Clear PYTHONOPTIMIZE when running `pip install -r test_requirements.txt` + # because version 2.19 of pycparser (a dependency of one of the packages + # in test_requirements.txt) does not provide a wheel, and the source tar + # file does not install correctly when Python's optimization level is set + # to strip docstrings (see https://github.com/eliben/pycparser/issues/291). + PYTHONOPTIMIZE="" $PIP install -r test_requirements.txt + DURATIONS_FLAG="--durations 10" + if [ -n "$USE_DEBUG" ]; then export PYTHONPATH=$PWD + export MYPYPATH=$PWD + fi + + if [ -n "$RUN_COVERAGE" ]; then + COVERAGE_FLAG=--coverage fi # We change directories to make sure that python won't find the copy @@ -112,13 +102,42 @@ run_test() INSTALLDIR=$($PYTHON -c \ "import os; import numpy; print(os.path.dirname(numpy.__file__))") export PYTHONWARNINGS=default + + if [ -n "$CHECK_BLAS" ]; then + $PYTHON ../tools/openblas_support.py --check_version + fi + if [ -n "$RUN_FULL_TESTS" ]; then - $PYTHON ../tools/test-installed-numpy.py -v --mode=full + export PYTHONWARNINGS="ignore::DeprecationWarning:virtualenv" + $PYTHON -b ../runtests.py -n -v --mode=full $DURATIONS_FLAG $COVERAGE_FLAG else - $PYTHON ../tools/test-installed-numpy.py -v + $PYTHON ../runtests.py -n -v $DURATIONS_FLAG -- -rs + fi + + if [ -n "$RUN_COVERAGE" ]; then + # move back up to the source dir because we want to execute + # gcov on the source files after the tests have gone through + # the code paths + cd .. + + # execute gcov on source files + find . -name '*.gcno' -type f -exec gcov -pb {} + + + # move the C line coverage report files to the same path + # as the Python report data + mv *.gcov empty + + # move back to the previous path for good measure + # as the Python coverage data is there + cd empty + + # Upload coverage files to codecov + bash <(curl -s https://codecov.io/bash) -X gcov -X coveragepy fi + if [ -n "$USE_ASV" ]; then pushd ../benchmarks + $PYTHON `which asv` check --python=same $PYTHON `which asv` machine --machine travis $PYTHON `which asv` dev 2>&1| tee asv-output.log if grep -q Traceback asv-output.log; then @@ -129,56 +148,47 @@ run_test() fi } + export PYTHON export PIP -$PIP install setuptools if [ -n "$USE_WHEEL" ] && [ $# -eq 0 ]; then - # Build wheel - $PIP install wheel - # ensure that the pip / setuptools versions deployed inside - # the venv are recent enough - $PIP install -U virtualenv # ensure some warnings are not issued export CFLAGS=$CFLAGS" -Wno-sign-compare -Wno-unused-result" - $PYTHON setup.py bdist_wheel + # adjust gcc flags if C coverage requested + if [ -n "$RUN_COVERAGE" ]; then + export NPY_DISTUTILS_APPEND_FLAGS=1 + export CC='gcc --coverage' + export F77='gfortran --coverage' + export F90='gfortran --coverage' + export LDFLAGS='--coverage' + fi + $PYTHON setup.py build --warn-error build_src --verbose-cfg bdist_wheel # Make another virtualenv to install into - virtualenv --python=`which $PYTHON` venv-for-wheel + $PYTHON -m venv venv-for-wheel . venv-for-wheel/bin/activate # Move out of source directory to avoid finding local numpy pushd dist - pip install --pre --no-index --upgrade --find-links=. numpy - pip install nose pytest + $PIP install --pre --no-index --upgrade --find-links=. numpy popd + run_test + elif [ -n "$USE_SDIST" ] && [ $# -eq 0 ]; then - # use an up-to-date pip / setuptools inside the venv - $PIP install -U virtualenv # temporary workaround for sdist failures. $PYTHON -c "import fcntl; fcntl.fcntl(1, fcntl.F_SETFL, 0)" # ensure some warnings are not issued export CFLAGS=$CFLAGS" -Wno-sign-compare -Wno-unused-result" $PYTHON setup.py sdist # Make another virtualenv to install into - virtualenv --python=`which $PYTHON` venv-for-wheel + $PYTHON -m venv venv-for-wheel . venv-for-wheel/bin/activate # Move out of source directory to avoid finding local numpy pushd dist - pip install numpy* - pip install nose pytest + $PIP install numpy* popd run_test -elif [ -n "$USE_CHROOT" ] && [ $# -eq 0 ]; then - DIR=/chroot - setup_chroot $DIR - # the chroot'ed environment will not have the current locale, - # avoid any warnings which may disturb testing - export LANG=C LC_ALL=C - # run again in chroot with this time testing - sudo linux32 chroot $DIR bash -c \ - "cd numpy && PYTHON=python PIP=pip IN_CHROOT=1 $0 test" else setup_base run_test fi - diff --git a/tools/travis-upload-wheel.sh b/tools/travis-upload-wheel.sh deleted file mode 100755 index 06a8f3ebac54..000000000000 --- a/tools/travis-upload-wheel.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash -# -set -ex - -export CLOUD_CONTAINER_NAME=travis-dev-wheels - -if [[ ( ${USE_WHEEL} == 1 ) \ - && ( "${TRAVIS_BRANCH}" == "master" ) \ - && ( "${TRAVIS_PULL_REQUEST}" == "false" ) ]]; then - pip install wheelhouse_uploader - python -m wheelhouse_uploader upload --local-folder \ - ${TRAVIS_BUILD_DIR}/dist/ ${CLOUD_CONTAINER_NAME} -fi diff --git a/tools/wheels/LICENSE_linux.txt b/tools/wheels/LICENSE_linux.txt new file mode 100644 index 000000000000..9ea808afce50 --- /dev/null +++ b/tools/wheels/LICENSE_linux.txt @@ -0,0 +1,880 @@ + +---- + +This binary distribution of NumPy also bundles the following software: + + +Name: OpenBLAS +Files: .libs/libopenb*.so +Description: bundled as a dynamically linked library +Availability: https://github.com/xianyi/OpenBLAS/ +License: 3-clause BSD + Copyright (c) 2011-2014, The OpenBLAS Project + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. Neither the name of the OpenBLAS project nor the names of + its contributors may be used to endorse or promote products + derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +Name: LAPACK +Files: .libs/libopenb*.so +Description: bundled in OpenBLAS +Availability: https://github.com/xianyi/OpenBLAS/ +License 3-clause BSD + Copyright (c) 1992-2013 The University of Tennessee and The University + of Tennessee Research Foundation. All rights + reserved. + Copyright (c) 2000-2013 The University of California Berkeley. All + rights reserved. + Copyright (c) 2006-2013 The University of Colorado Denver. All rights + reserved. + + $COPYRIGHT$ + + Additional copyrights may follow + + $HEADER$ + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer listed + in this license in the documentation and/or other materials + provided with the distribution. + + - Neither the name of the copyright holders nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + The copyright holders provide no reassurances that the source code + provided does not infringe any patent, copyright, or any other + intellectual property rights of third parties. The copyright holders + disclaim any liability to any recipient for claims brought against + recipient by any third party for infringement of that parties + intellectual property rights. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +Name: GCC runtime library +Files: .libs/libgfortran*.so +Description: dynamically linked to files compiled with gcc +Availability: https://gcc.gnu.org/viewcvs/gcc/ +License: GPLv3 + runtime exception + Copyright (C) 2002-2017 Free Software Foundation, Inc. + + Libgfortran is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + Libgfortran is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + Under Section 7 of GPL version 3, you are granted additional + permissions described in the GCC Runtime Library Exception, version + 3.1, as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License and + a copy of the GCC Runtime Library Exception along with this program; + see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + <http://www.gnu.org/licenses/>. + +---- + +Full text of license texts referred to above follows (that they are +listed below does not necessarily imply the conditions apply to the +present binary release): + +---- + +GCC RUNTIME LIBRARY EXCEPTION + +Version 3.1, 31 March 2009 + +Copyright (C) 2009 Free Software Foundation, Inc. <http://fsf.org/> + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + +This GCC Runtime Library Exception ("Exception") is an additional +permission under section 7 of the GNU General Public License, version +3 ("GPLv3"). It applies to a given file (the "Runtime Library") that +bears a notice placed by the copyright holder of the file stating that +the file is governed by GPLv3 along with this Exception. + +When you use GCC to compile a program, GCC may combine portions of +certain GCC header files and runtime libraries with the compiled +program. The purpose of this Exception is to allow compilation of +non-GPL (including proprietary) programs to use, in this way, the +header files and runtime libraries covered by this Exception. + +0. Definitions. + +A file is an "Independent Module" if it either requires the Runtime +Library for execution after a Compilation Process, or makes use of an +interface provided by the Runtime Library, but is not otherwise based +on the Runtime Library. + +"GCC" means a version of the GNU Compiler Collection, with or without +modifications, governed by version 3 (or a specified later version) of +the GNU General Public License (GPL) with the option of using any +subsequent versions published by the FSF. + +"GPL-compatible Software" is software whose conditions of propagation, +modification and use would permit combination with GCC in accord with +the license of GCC. + +"Target Code" refers to output from any compiler for a real or virtual +target processor architecture, in executable form or suitable for +input to an assembler, loader, linker and/or execution +phase. Notwithstanding that, Target Code does not include data in any +format that is used as a compiler intermediate representation, or used +for producing a compiler intermediate representation. + +The "Compilation Process" transforms code entirely represented in +non-intermediate languages designed for human-written code, and/or in +Java Virtual Machine byte code, into Target Code. Thus, for example, +use of source code generators and preprocessors need not be considered +part of the Compilation Process, since the Compilation Process can be +understood as starting with the output of the generators or +preprocessors. + +A Compilation Process is "Eligible" if it is done using GCC, alone or +with other GPL-compatible software, or if it is done without using any +work based on GCC. For example, using non-GPL-compatible Software to +optimize any GCC intermediate representations would not qualify as an +Eligible Compilation Process. + +1. Grant of Additional Permission. + +You have permission to propagate a work of Target Code formed by +combining the Runtime Library with Independent Modules, even if such +propagation would otherwise violate the terms of GPLv3, provided that +all Target Code was generated by Eligible Compilation Processes. You +may then convey such a combination under terms of your choice, +consistent with the licensing of the Independent Modules. + +2. No Weakening of GCC Copyleft. + +The availability of this Exception does not imply any general +presumption that third-party software is unaffected by the copyleft +requirements of the license of GCC. + +---- + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<http://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<http://www.gnu.org/philosophy/why-not-lgpl.html>. diff --git a/tools/wheels/LICENSE_osx.txt b/tools/wheels/LICENSE_osx.txt new file mode 100644 index 000000000000..9a687c3b6b05 --- /dev/null +++ b/tools/wheels/LICENSE_osx.txt @@ -0,0 +1,789 @@ + +---- + +This binary distribution of NumPy also bundles the following software: + + +Name: GCC runtime library +Files: .dylibs/* +Description: dynamically linked to files compiled with gcc +Availability: https://gcc.gnu.org/viewcvs/gcc/ +License: GPLv3 + runtime exception + Copyright (C) 2002-2017 Free Software Foundation, Inc. + + Libgfortran is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + Libgfortran is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + Under Section 7 of GPL version 3, you are granted additional + permissions described in the GCC Runtime Library Exception, version + 3.1, as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License and + a copy of the GCC Runtime Library Exception along with this program; + see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + <http://www.gnu.org/licenses/>. + +---- + +Full text of license texts referred to above follows (that they are +listed below does not necessarily imply the conditions apply to the +present binary release): + +---- + +GCC RUNTIME LIBRARY EXCEPTION + +Version 3.1, 31 March 2009 + +Copyright (C) 2009 Free Software Foundation, Inc. <http://fsf.org/> + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + +This GCC Runtime Library Exception ("Exception") is an additional +permission under section 7 of the GNU General Public License, version +3 ("GPLv3"). It applies to a given file (the "Runtime Library") that +bears a notice placed by the copyright holder of the file stating that +the file is governed by GPLv3 along with this Exception. + +When you use GCC to compile a program, GCC may combine portions of +certain GCC header files and runtime libraries with the compiled +program. The purpose of this Exception is to allow compilation of +non-GPL (including proprietary) programs to use, in this way, the +header files and runtime libraries covered by this Exception. + +0. Definitions. + +A file is an "Independent Module" if it either requires the Runtime +Library for execution after a Compilation Process, or makes use of an +interface provided by the Runtime Library, but is not otherwise based +on the Runtime Library. + +"GCC" means a version of the GNU Compiler Collection, with or without +modifications, governed by version 3 (or a specified later version) of +the GNU General Public License (GPL) with the option of using any +subsequent versions published by the FSF. + +"GPL-compatible Software" is software whose conditions of propagation, +modification and use would permit combination with GCC in accord with +the license of GCC. + +"Target Code" refers to output from any compiler for a real or virtual +target processor architecture, in executable form or suitable for +input to an assembler, loader, linker and/or execution +phase. Notwithstanding that, Target Code does not include data in any +format that is used as a compiler intermediate representation, or used +for producing a compiler intermediate representation. + +The "Compilation Process" transforms code entirely represented in +non-intermediate languages designed for human-written code, and/or in +Java Virtual Machine byte code, into Target Code. Thus, for example, +use of source code generators and preprocessors need not be considered +part of the Compilation Process, since the Compilation Process can be +understood as starting with the output of the generators or +preprocessors. + +A Compilation Process is "Eligible" if it is done using GCC, alone or +with other GPL-compatible software, or if it is done without using any +work based on GCC. For example, using non-GPL-compatible Software to +optimize any GCC intermediate representations would not qualify as an +Eligible Compilation Process. + +1. Grant of Additional Permission. + +You have permission to propagate a work of Target Code formed by +combining the Runtime Library with Independent Modules, even if such +propagation would otherwise violate the terms of GPLv3, provided that +all Target Code was generated by Eligible Compilation Processes. You +may then convey such a combination under terms of your choice, +consistent with the licensing of the Independent Modules. + +2. No Weakening of GCC Copyleft. + +The availability of this Exception does not imply any general +presumption that third-party software is unaffected by the copyleft +requirements of the license of GCC. + +---- + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<http://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<http://www.gnu.org/philosophy/why-not-lgpl.html>. diff --git a/tools/wheels/check_license.py b/tools/wheels/check_license.py new file mode 100644 index 000000000000..0fe7356c0c4f --- /dev/null +++ b/tools/wheels/check_license.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python +""" +check_license.py [MODULE] + +Check the presence of a LICENSE.txt in the installed module directory, +and that it appears to contain text prevalent for a NumPy binary +distribution. + +""" +import os +import sys +import io +import re +import argparse + + +def check_text(text): + ok = "Copyright (c)" in text and re.search( + r"This binary distribution of \w+ also bundles the following software", + text, + ) + return ok + + +def main(): + p = argparse.ArgumentParser(usage=__doc__.rstrip()) + p.add_argument("module", nargs="?", default="numpy") + args = p.parse_args() + + # Drop '' from sys.path + sys.path.pop(0) + + # Find module path + __import__(args.module) + mod = sys.modules[args.module] + + # Check license text + license_txt = os.path.join(os.path.dirname(mod.__file__), "LICENSE.txt") + with io.open(license_txt, "r", encoding="utf-8") as f: + text = f.read() + + ok = check_text(text) + if not ok: + print( + "ERROR: License text {} does not contain expected " + "text fragments\n".format(license_txt) + ) + print(text) + sys.exit(1) + + sys.exit(0) + + +if __name__ == "__main__": + main() diff --git a/tools/wheels/cibw_before_build.sh b/tools/wheels/cibw_before_build.sh new file mode 100644 index 000000000000..36410ba1fa34 --- /dev/null +++ b/tools/wheels/cibw_before_build.sh @@ -0,0 +1,37 @@ +set -xe + +PROJECT_DIR="$1" +UNAME="$(uname)" + +# Update license +if [[ $UNAME == "Linux" ]] ; then + cat $PROJECT_DIR/tools/wheels/LICENSE_linux.txt >> $PROJECT_DIR/LICENSE.txt +elif [[ $UNAME == "Darwin" ]]; then + cat $PROJECT_DIR/tools/wheels/LICENSE_osx.txt >> $PROJECT_DIR/LICENSE.txt +fi + +# Install Openblas +if [[ $UNAME == "Linux" || $UNAME == "Darwin" ]] ; then + basedir=$(python tools/openblas_support.py) + cp -r $basedir/lib/* /usr/local/lib + cp $basedir/include/* /usr/local/include +fi + +# Install GFortran +if [[ $UNAME == "Darwin" ]]; then + # same version of gfortran as the openblas-libs and numpy-wheel builds + curl -L https://github.com/MacPython/gfortran-install/raw/master/archives/gfortran-4.9.0-Mavericks.dmg -o gfortran.dmg + GFORTRAN_SHA256=$(shasum -a 256 gfortran.dmg) + KNOWN_SHA256="d2d5ca5ba8332d63bbe23a07201c4a0a5d7e09ee56f0298a96775f928c3c4b30 gfortran.dmg" + if [ "$GFORTRAN_SHA256" != "$KNOWN_SHA256" ]; then + echo sha256 mismatch + exit 1 + fi + hdiutil attach -mountpoint /Volumes/gfortran gfortran.dmg + sudo installer -pkg /Volumes/gfortran/gfortran.pkg -target / + otool -L /usr/local/gfortran/lib/libgfortran.3.dylib + # Manually symlink gfortran-4.9 to plain gfortran for f2py. + # No longer needed after Feb 13 2020 as gfortran is already present + # and the attempted link errors. Keep this for future reference. + # ln -s /usr/local/bin/gfortran-4.9 /usr/local/bin/gfortran +fi diff --git a/tools/wheels/cibw_test_command.sh b/tools/wheels/cibw_test_command.sh new file mode 100644 index 000000000000..f09395e847a1 --- /dev/null +++ b/tools/wheels/cibw_test_command.sh @@ -0,0 +1,15 @@ +# This script is used by .github/workflows/wheels.yml to build wheels with +# cibuildwheel. It runs the full test suite, checks for lincense inclusion +# and that the openblas version is correct. +set -xe + +PROJECT_DIR="$1" +UNAME="$(uname)" + +python -c "import numpy; numpy.show_config()" +python -c "import sys; import numpy; sys.exit(not numpy.test('full', extra_argv=['-vv']))" + +python $PROJECT_DIR/tools/wheels/check_license.py +if [[ $UNAME == "Linux" || $UNAME == "Darwin" ]] ; then + python $PROJECT_DIR/tools/openblas_support.py --check_version +fi diff --git a/tox.ini b/tox.ini index e6f1b124f6ce..9bc2bbac36dd 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,7 @@ # 'Tox' is a tool for automating sdist/build/test cycles against # multiple Python versions: -# http://pypi.python.org/pypi/tox -# http://tox.testrun.org/ +# https://pypi.python.org/pypi/tox +# https://tox.readthedocs.io/ # Running the command 'tox' while in the root of the numpy source # directory will: @@ -13,38 +13,33 @@ # - Use pip to install the numpy sdist into the virtualenv # - Run the numpy tests # To run against a specific subset of Python versions, use: -# tox -e py27 +# tox -e py37 -# Extra arguments will be passed to test-installed-numpy.py. To run +# Extra arguments will be passed to runtests.py. To run # the full testsuite: # tox full # To run with extra verbosity: # tox -- -v # Tox assumes that you have appropriate Python interpreters already -# installed and that they can be run as 'python2.7', 'python3.3', etc. +# installed and that they can be run as (e.g.) 'python3.8' [tox] envlist = - py27,py34,py35,py36, - py27-not-relaxed-strides,py34-not-relaxed-strides + py37,py38,py39, + py37-not-relaxed-strides [testenv] -deps= - nose +deps= -Ur{toxinidir}/test_requirements.txt changedir={envdir} -commands={envpython} {toxinidir}/tools/test-installed-numpy.py --mode=full {posargs:} +commands={envpython} -b {toxinidir}/runtests.py --mode=full {posargs:} -[testenv:py27-not-relaxed-strides] -basepython=python2.7 -env=NPY_RELAXED_STRIDES_CHECKING=0 - -[testenv:py34-not-relaxed-strides] -basepython=python3.4 +[testenv:py37-not-relaxed-strides] +basepython=python3.7 env=NPY_RELAXED_STRIDES_CHECKING=0 # Not run by default. Set up the way you want then use 'tox -e debug' # if you want it: [testenv:debug] basepython=python-dbg -commands=gdb --args {envpython} {toxinidir}/tools/test-installed-numpy.py --mode=full {posargs:} +commands=gdb --args {envpython} {toxinidir}/runtests.py --mode=full {posargs:} diff --git a/versioneer.py b/versioneer.py new file mode 100644 index 000000000000..7aa415ac8a9b --- /dev/null +++ b/versioneer.py @@ -0,0 +1,1852 @@ + +# Version: 0.19 + +"""The Versioneer - like a rocketeer, but for versions. + +The Versioneer +============== + +* like a rocketeer, but for versions! +* https://github.com/python-versioneer/python-versioneer +* Brian Warner +* License: Public Domain +* Compatible with: Python 3.6, 3.7, 3.8, 3.9 and pypy3 +* [![Latest Version][pypi-image]][pypi-url] +* [![Build Status][travis-image]][travis-url] + +This is a tool for managing a recorded version number in distutils-based +python projects. The goal is to remove the tedious and error-prone "update +the embedded version string" step from your release process. Making a new +release should be as easy as recording a new tag in your version-control +system, and maybe making new tarballs. + + +## Quick Install + +* `pip install versioneer` to somewhere in your $PATH +* add a `[versioneer]` section to your setup.cfg (see [Install](INSTALL.md)) +* run `versioneer install` in your source tree, commit the results +* Verify version information with `python setup.py version` + +## Version Identifiers + +Source trees come from a variety of places: + +* a version-control system checkout (mostly used by developers) +* a nightly tarball, produced by build automation +* a snapshot tarball, produced by a web-based VCS browser, like github's + "tarball from tag" feature +* a release tarball, produced by "setup.py sdist", distributed through PyPI + +Within each source tree, the version identifier (either a string or a number, +this tool is format-agnostic) can come from a variety of places: + +* ask the VCS tool itself, e.g. "git describe" (for checkouts), which knows + about recent "tags" and an absolute revision-id +* the name of the directory into which the tarball was unpacked +* an expanded VCS keyword ($Id$, etc) +* a `_version.py` created by some earlier build step + +For released software, the version identifier is closely related to a VCS +tag. Some projects use tag names that include more than just the version +string (e.g. "myproject-1.2" instead of just "1.2"), in which case the tool +needs to strip the tag prefix to extract the version identifier. For +unreleased software (between tags), the version identifier should provide +enough information to help developers recreate the same tree, while also +giving them an idea of roughly how old the tree is (after version 1.2, before +version 1.3). Many VCS systems can report a description that captures this, +for example `git describe --tags --dirty --always` reports things like +"0.7-1-g574ab98-dirty" to indicate that the checkout is one revision past the +0.7 tag, has a unique revision id of "574ab98", and is "dirty" (it has +uncommitted changes). + +The version identifier is used for multiple purposes: + +* to allow the module to self-identify its version: `myproject.__version__` +* to choose a name and prefix for a 'setup.py sdist' tarball + +## Theory of Operation + +Versioneer works by adding a special `_version.py` file into your source +tree, where your `__init__.py` can import it. This `_version.py` knows how to +dynamically ask the VCS tool for version information at import time. + +`_version.py` also contains `$Revision$` markers, and the installation +process marks `_version.py` to have this marker rewritten with a tag name +during the `git archive` command. As a result, generated tarballs will +contain enough information to get the proper version. + +To allow `setup.py` to compute a version too, a `versioneer.py` is added to +the top level of your source tree, next to `setup.py` and the `setup.cfg` +that configures it. This overrides several distutils/setuptools commands to +compute the version when invoked, and changes `setup.py build` and `setup.py +sdist` to replace `_version.py` with a small static file that contains just +the generated version data. + +## Installation + +See [INSTALL.md](./INSTALL.md) for detailed installation instructions. + +## Version-String Flavors + +Code which uses Versioneer can learn about its version string at runtime by +importing `_version` from your main `__init__.py` file and running the +`get_versions()` function. From the "outside" (e.g. in `setup.py`), you can +import the top-level `versioneer.py` and run `get_versions()`. + +Both functions return a dictionary with different flavors of version +information: + +* `['version']`: A condensed version string, rendered using the selected + style. This is the most commonly used value for the project's version + string. The default "pep440" style yields strings like `0.11`, + `0.11+2.g1076c97`, or `0.11+2.g1076c97.dirty`. See the "Styles" section + below for alternative styles. + +* `['full-revisionid']`: detailed revision identifier. For Git, this is the + full SHA1 commit id, e.g. "1076c978a8d3cfc70f408fe5974aa6c092c949ac". + +* `['date']`: Date and time of the latest `HEAD` commit. For Git, it is the + commit date in ISO 8601 format. This will be None if the date is not + available. + +* `['dirty']`: a boolean, True if the tree has uncommitted changes. Note that + this is only accurate if run in a VCS checkout, otherwise it is likely to + be False or None + +* `['error']`: if the version string could not be computed, this will be set + to a string describing the problem, otherwise it will be None. It may be + useful to throw an exception in setup.py if this is set, to avoid e.g. + creating tarballs with a version string of "unknown". + +Some variants are more useful than others. Including `full-revisionid` in a +bug report should allow developers to reconstruct the exact code being tested +(or indicate the presence of local changes that should be shared with the +developers). `version` is suitable for display in an "about" box or a CLI +`--version` output: it can be easily compared against release notes and lists +of bugs fixed in various releases. + +The installer adds the following text to your `__init__.py` to place a basic +version in `YOURPROJECT.__version__`: + + from ._version import get_versions + __version__ = get_versions()['version'] + del get_versions + +## Styles + +The setup.cfg `style=` configuration controls how the VCS information is +rendered into a version string. + +The default style, "pep440", produces a PEP440-compliant string, equal to the +un-prefixed tag name for actual releases, and containing an additional "local +version" section with more detail for in-between builds. For Git, this is +TAG[+DISTANCE.gHEX[.dirty]] , using information from `git describe --tags +--dirty --always`. For example "0.11+2.g1076c97.dirty" indicates that the +tree is like the "1076c97" commit but has uncommitted changes (".dirty"), and +that this commit is two revisions ("+2") beyond the "0.11" tag. For released +software (exactly equal to a known tag), the identifier will only contain the +stripped tag, e.g. "0.11". + +Other styles are available. See [details.md](details.md) in the Versioneer +source tree for descriptions. + +## Debugging + +Versioneer tries to avoid fatal errors: if something goes wrong, it will tend +to return a version of "0+unknown". To investigate the problem, run `setup.py +version`, which will run the version-lookup code in a verbose mode, and will +display the full contents of `get_versions()` (including the `error` string, +which may help identify what went wrong). + +## Known Limitations + +Some situations are known to cause problems for Versioneer. This details the +most significant ones. More can be found on Github +[issues page](https://github.com/python-versioneer/python-versioneer/issues). + +### Subprojects + +Versioneer has limited support for source trees in which `setup.py` is not in +the root directory (e.g. `setup.py` and `.git/` are *not* siblings). The are +two common reasons why `setup.py` might not be in the root: + +* Source trees which contain multiple subprojects, such as + [Buildbot](https://github.com/buildbot/buildbot), which contains both + "master" and "slave" subprojects, each with their own `setup.py`, + `setup.cfg`, and `tox.ini`. Projects like these produce multiple PyPI + distributions (and upload multiple independently-installable tarballs). +* Source trees whose main purpose is to contain a C library, but which also + provide bindings to Python (and perhaps other languages) in subdirectories. + +Versioneer will look for `.git` in parent directories, and most operations +should get the right version string. However `pip` and `setuptools` have bugs +and implementation details which frequently cause `pip install .` from a +subproject directory to fail to find a correct version string (so it usually +defaults to `0+unknown`). + +`pip install --editable .` should work correctly. `setup.py install` might +work too. + +Pip-8.1.1 is known to have this problem, but hopefully it will get fixed in +some later version. + +[Bug #38](https://github.com/python-versioneer/python-versioneer/issues/38) is tracking +this issue. The discussion in +[PR #61](https://github.com/python-versioneer/python-versioneer/pull/61) describes the +issue from the Versioneer side in more detail. +[pip PR#3176](https://github.com/pypa/pip/pull/3176) and +[pip PR#3615](https://github.com/pypa/pip/pull/3615) contain work to improve +pip to let Versioneer work correctly. + +Versioneer-0.16 and earlier only looked for a `.git` directory next to the +`setup.cfg`, so subprojects were completely unsupported with those releases. + +### Editable installs with setuptools <= 18.5 + +`setup.py develop` and `pip install --editable .` allow you to install a +project into a virtualenv once, then continue editing the source code (and +test) without re-installing after every change. + +"Entry-point scripts" (`setup(entry_points={"console_scripts": ..})`) are a +convenient way to specify executable scripts that should be installed along +with the python package. + +These both work as expected when using modern setuptools. When using +setuptools-18.5 or earlier, however, certain operations will cause +`pkg_resources.DistributionNotFound` errors when running the entrypoint +script, which must be resolved by re-installing the package. This happens +when the install happens with one version, then the egg_info data is +regenerated while a different version is checked out. Many setup.py commands +cause egg_info to be rebuilt (including `sdist`, `wheel`, and installing into +a different virtualenv), so this can be surprising. + +[Bug #83](https://github.com/python-versioneer/python-versioneer/issues/83) describes +this one, but upgrading to a newer version of setuptools should probably +resolve it. + + +## Updating Versioneer + +To upgrade your project to a new release of Versioneer, do the following: + +* install the new Versioneer (`pip install -U versioneer` or equivalent) +* edit `setup.cfg`, if necessary, to include any new configuration settings + indicated by the release notes. See [UPGRADING](./UPGRADING.md) for details. +* re-run `versioneer install` in your source tree, to replace + `SRC/_version.py` +* commit any changed files + +## Future Directions + +This tool is designed to make it easily extended to other version-control +systems: all VCS-specific components are in separate directories like +src/git/ . The top-level `versioneer.py` script is assembled from these +components by running make-versioneer.py . In the future, make-versioneer.py +will take a VCS name as an argument, and will construct a version of +`versioneer.py` that is specific to the given VCS. It might also take the +configuration arguments that are currently provided manually during +installation by editing setup.py . Alternatively, it might go the other +direction and include code from all supported VCS systems, reducing the +number of intermediate scripts. + +## Similar projects + +* [setuptools_scm](https://github.com/pypa/setuptools_scm/) - a non-vendored build-time + dependency +* [minver](https://github.com/jbweston/miniver) - a lightweight reimplementation of + versioneer + +## License + +To make Versioneer easier to embed, all its code is dedicated to the public +domain. The `_version.py` that it creates is also in the public domain. +Specifically, both are released under the Creative Commons "Public Domain +Dedication" license (CC0-1.0), as described in +https://creativecommons.org/publicdomain/zero/1.0/ . + +[pypi-image]: https://img.shields.io/pypi/v/versioneer.svg +[pypi-url]: https://pypi.python.org/pypi/versioneer/ +[travis-image]: +https://img.shields.io/travis/com/python-versioneer/python-versioneer.svg +[travis-url]: https://travis-ci.com/github/python-versioneer/python-versioneer + +""" + +import configparser +import errno +import json +import os +import re +import subprocess +import sys + + +class VersioneerConfig: + """Container for Versioneer configuration parameters.""" + + +def get_root(): + """Get the project root directory. + + We require that all commands are run from the project root, i.e. the + directory that contains setup.py, setup.cfg, and versioneer.py . + """ + root = os.path.realpath(os.path.abspath(os.getcwd())) + setup_py = os.path.join(root, "setup.py") + versioneer_py = os.path.join(root, "versioneer.py") + if not (os.path.exists(setup_py) or os.path.exists(versioneer_py)): + # allow 'python path/to/setup.py COMMAND' + root = os.path.dirname(os.path.realpath(os.path.abspath(sys.argv[0]))) + setup_py = os.path.join(root, "setup.py") + versioneer_py = os.path.join(root, "versioneer.py") + if not (os.path.exists(setup_py) or os.path.exists(versioneer_py)): + err = ("Versioneer was unable to run the project root directory. " + "Versioneer requires setup.py to be executed from " + "its immediate directory (like 'python setup.py COMMAND'), " + "or in a way that lets it use sys.argv[0] to find the root " + "(like 'python path/to/setup.py COMMAND').") + raise VersioneerBadRootError(err) + try: + # Certain runtime workflows (setup.py install/develop in a setuptools + # tree) execute all dependencies in a single python process, so + # "versioneer" may be imported multiple times, and python's shared + # module-import table will cache the first one. So we can't use + # os.path.dirname(__file__), as that will find whichever + # versioneer.py was first imported, even in later projects. + me = os.path.realpath(os.path.abspath(__file__)) + me_dir = os.path.normcase(os.path.splitext(me)[0]) + vsr_dir = os.path.normcase(os.path.splitext(versioneer_py)[0]) + if me_dir != vsr_dir: + print("Warning: build in %s is using versioneer.py from %s" + % (os.path.dirname(me), versioneer_py)) + except NameError: + pass + return root + + +def get_config_from_root(root): + """Read the project setup.cfg file to determine Versioneer config.""" + # This might raise EnvironmentError (if setup.cfg is missing), or + # configparser.NoSectionError (if it lacks a [versioneer] section), or + # configparser.NoOptionError (if it lacks "VCS="). See the docstring at + # the top of versioneer.py for instructions on writing your setup.cfg . + setup_cfg = os.path.join(root, "setup.cfg") + parser = configparser.ConfigParser() + with open(setup_cfg, "r") as f: + parser.read_file(f) + VCS = parser.get("versioneer", "VCS") # mandatory + + def get(parser, name): + if parser.has_option("versioneer", name): + return parser.get("versioneer", name) + return None + cfg = VersioneerConfig() + cfg.VCS = VCS + cfg.style = get(parser, "style") or "" + cfg.versionfile_source = get(parser, "versionfile_source") + cfg.versionfile_build = get(parser, "versionfile_build") + cfg.tag_prefix = get(parser, "tag_prefix") + if cfg.tag_prefix in ("''", '""'): + cfg.tag_prefix = "" + cfg.parentdir_prefix = get(parser, "parentdir_prefix") + cfg.verbose = get(parser, "verbose") + return cfg + + +class NotThisMethod(Exception): + """Exception raised if a method is not valid for the current scenario.""" + + +# these dictionaries contain VCS-specific tools +LONG_VERSION_PY = {} +HANDLERS = {} + + +def register_vcs_handler(vcs, method): # decorator + """Create decorator to mark a method as the handler of a VCS.""" + def decorate(f): + """Store f in HANDLERS[vcs][method].""" + if vcs not in HANDLERS: + HANDLERS[vcs] = {} + HANDLERS[vcs][method] = f + return f + return decorate + + +def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, + env=None): + """Call the given command(s).""" + assert isinstance(commands, list) + p = None + for c in commands: + try: + dispcmd = str([c] + args) + # remember shell=False, so use git.cmd on windows, not just git + p = subprocess.Popen([c] + args, cwd=cwd, env=env, + stdout=subprocess.PIPE, + stderr=(subprocess.PIPE if hide_stderr + else None)) + break + except EnvironmentError: + e = sys.exc_info()[1] + if e.errno == errno.ENOENT: + continue + if verbose: + print("unable to run %s" % dispcmd) + print(e) + return None, None + else: + if verbose: + print("unable to find command, tried %s" % (commands,)) + return None, None + stdout = p.communicate()[0].strip().decode() + if p.returncode != 0: + if verbose: + print("unable to run %s (error)" % dispcmd) + print("stdout was %s" % stdout) + return None, p.returncode + return stdout, p.returncode + + +LONG_VERSION_PY['git'] = r''' +# This file helps to compute a version number in source trees obtained from +# git-archive tarball (such as those provided by githubs download-from-tag +# feature). Distribution tarballs (built by setup.py sdist) and build +# directories (produced by setup.py build) will contain a much shorter file +# that just contains the computed version number. + +# This file is released into the public domain. Generated by +# versioneer-0.19 (https://github.com/python-versioneer/python-versioneer) + +"""Git implementation of _version.py.""" + +import errno +import os +import re +import subprocess +import sys + + +def get_keywords(): + """Get the keywords needed to look up the version information.""" + # these strings will be replaced by git during git-archive. + # setup.py/versioneer.py will grep for the variable names, so they must + # each be defined on a line of their own. _version.py will just call + # get_keywords(). + git_refnames = "%(DOLLAR)sFormat:%%d%(DOLLAR)s" + git_full = "%(DOLLAR)sFormat:%%H%(DOLLAR)s" + git_date = "%(DOLLAR)sFormat:%%ci%(DOLLAR)s" + keywords = {"refnames": git_refnames, "full": git_full, "date": git_date} + return keywords + + +class VersioneerConfig: + """Container for Versioneer configuration parameters.""" + + +def get_config(): + """Create, populate and return the VersioneerConfig() object.""" + # these strings are filled in when 'setup.py versioneer' creates + # _version.py + cfg = VersioneerConfig() + cfg.VCS = "git" + cfg.style = "%(STYLE)s" + cfg.tag_prefix = "%(TAG_PREFIX)s" + cfg.parentdir_prefix = "%(PARENTDIR_PREFIX)s" + cfg.versionfile_source = "%(VERSIONFILE_SOURCE)s" + cfg.verbose = False + return cfg + + +class NotThisMethod(Exception): + """Exception raised if a method is not valid for the current scenario.""" + + +LONG_VERSION_PY = {} +HANDLERS = {} + + +def register_vcs_handler(vcs, method): # decorator + """Create decorator to mark a method as the handler of a VCS.""" + def decorate(f): + """Store f in HANDLERS[vcs][method].""" + if vcs not in HANDLERS: + HANDLERS[vcs] = {} + HANDLERS[vcs][method] = f + return f + return decorate + + +def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, + env=None): + """Call the given command(s).""" + assert isinstance(commands, list) + p = None + for c in commands: + try: + dispcmd = str([c] + args) + # remember shell=False, so use git.cmd on windows, not just git + p = subprocess.Popen([c] + args, cwd=cwd, env=env, + stdout=subprocess.PIPE, + stderr=(subprocess.PIPE if hide_stderr + else None)) + break + except EnvironmentError: + e = sys.exc_info()[1] + if e.errno == errno.ENOENT: + continue + if verbose: + print("unable to run %%s" %% dispcmd) + print(e) + return None, None + else: + if verbose: + print("unable to find command, tried %%s" %% (commands,)) + return None, None + stdout = p.communicate()[0].strip().decode() + if p.returncode != 0: + if verbose: + print("unable to run %%s (error)" %% dispcmd) + print("stdout was %%s" %% stdout) + return None, p.returncode + return stdout, p.returncode + + +def versions_from_parentdir(parentdir_prefix, root, verbose): + """Try to determine the version from the parent directory name. + + Source tarballs conventionally unpack into a directory that includes both + the project name and a version string. We will also support searching up + two directory levels for an appropriately named parent directory + """ + rootdirs = [] + + for i in range(3): + dirname = os.path.basename(root) + if dirname.startswith(parentdir_prefix): + return {"version": dirname[len(parentdir_prefix):], + "full-revisionid": None, + "dirty": False, "error": None, "date": None} + else: + rootdirs.append(root) + root = os.path.dirname(root) # up a level + + if verbose: + print("Tried directories %%s but none started with prefix %%s" %% + (str(rootdirs), parentdir_prefix)) + raise NotThisMethod("rootdir doesn't start with parentdir_prefix") + + +@register_vcs_handler("git", "get_keywords") +def git_get_keywords(versionfile_abs): + """Extract version information from the given file.""" + # the code embedded in _version.py can just fetch the value of these + # keywords. When used from setup.py, we don't want to import _version.py, + # so we do it with a regexp instead. This function is not used from + # _version.py. + keywords = {} + try: + f = open(versionfile_abs, "r") + for line in f.readlines(): + if line.strip().startswith("git_refnames ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["refnames"] = mo.group(1) + if line.strip().startswith("git_full ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["full"] = mo.group(1) + if line.strip().startswith("git_date ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["date"] = mo.group(1) + f.close() + except EnvironmentError: + pass + return keywords + + +@register_vcs_handler("git", "keywords") +def git_versions_from_keywords(keywords, tag_prefix, verbose): + """Get version information from git keywords.""" + if not keywords: + raise NotThisMethod("no keywords at all, weird") + date = keywords.get("date") + if date is not None: + # Use only the last line. Previous lines may contain GPG signature + # information. + date = date.splitlines()[-1] + + # git-2.2.0 added "%%cI", which expands to an ISO-8601 -compliant + # datestamp. However we prefer "%%ci" (which expands to an "ISO-8601 + # -like" string, which we must then edit to make compliant), because + # it's been around since git-1.5.3, and it's too difficult to + # discover which version we're using, or to work around using an + # older one. + date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) + refnames = keywords["refnames"].strip() + if refnames.startswith("$Format"): + if verbose: + print("keywords are unexpanded, not using") + raise NotThisMethod("unexpanded keywords, not a git-archive tarball") + refs = set([r.strip() for r in refnames.strip("()").split(",")]) + # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of + # just "foo-1.0". If we see a "tag: " prefix, prefer those. + TAG = "tag: " + tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)]) + if not tags: + # Either we're using git < 1.8.3, or there really are no tags. We use + # a heuristic: assume all version tags have a digit. The old git %%d + # expansion behaves like git log --decorate=short and strips out the + # refs/heads/ and refs/tags/ prefixes that would let us distinguish + # between branches and tags. By ignoring refnames without digits, we + # filter out many common branch names like "release" and + # "stabilization", as well as "HEAD" and "master". + tags = set([r for r in refs if re.search(r'\d', r)]) + if verbose: + print("discarding '%%s', no digits" %% ",".join(refs - tags)) + if verbose: + print("likely tags: %%s" %% ",".join(sorted(tags))) + for ref in sorted(tags): + # sorting will prefer e.g. "2.0" over "2.0rc1" + if ref.startswith(tag_prefix): + r = ref[len(tag_prefix):] + if verbose: + print("picking %%s" %% r) + return {"version": r, + "full-revisionid": keywords["full"].strip(), + "dirty": False, "error": None, + "date": date} + # no suitable tags, so version is "0+unknown", but full hex is still there + if verbose: + print("no suitable tags, using unknown + full revision id") + return {"version": "0+unknown", + "full-revisionid": keywords["full"].strip(), + "dirty": False, "error": "no suitable tags", "date": None} + + +@register_vcs_handler("git", "pieces_from_vcs") +def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): + """Get version from 'git describe' in the root of the source tree. + + This only gets called if the git-archive 'subst' keywords were *not* + expanded, and _version.py hasn't already been rewritten with a short + version string, meaning we're inside a checked out source tree. + """ + GITS = ["git"] + if sys.platform == "win32": + GITS = ["git.cmd", "git.exe"] + + out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root, + hide_stderr=True) + if rc != 0: + if verbose: + print("Directory %%s not under git control" %% root) + raise NotThisMethod("'git rev-parse --git-dir' returned error") + + # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] + # if there isn't one, this yields HEX[-dirty] (no NUM) + describe_out, rc = run_command(GITS, ["describe", "--tags", "--dirty=", + "--always", "--long", + "--match", "%%s*" %% tag_prefix], + cwd=root) + # --long was added in git-1.5.5 + if describe_out is None: + raise NotThisMethod("'git describe' failed") + describe_out = describe_out.strip() + full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) + if full_out is None: + raise NotThisMethod("'git rev-parse' failed") + full_out = full_out.strip() + + pieces = {} + pieces["long"] = full_out + pieces["short"] = full_out[:7] # maybe improved later + pieces["error"] = None + + # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] + # TAG might have hyphens. + git_describe = describe_out + + # look for -dirty suffix + dirty = git_describe.endswith("-dirty") + pieces["dirty"] = dirty + if dirty: + git_describe = git_describe[:git_describe.rindex("-dirty")] + + # now we have TAG-NUM-gHEX or HEX + + if "-" in git_describe: + # TAG-NUM-gHEX + mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe) + if not mo: + # unparseable. Maybe git-describe is misbehaving? + pieces["error"] = ("unable to parse git-describe output: '%%s'" + %% describe_out) + return pieces + + # tag + full_tag = mo.group(1) + if not full_tag.startswith(tag_prefix): + if verbose: + fmt = "tag '%%s' doesn't start with prefix '%%s'" + print(fmt %% (full_tag, tag_prefix)) + pieces["error"] = ("tag '%%s' doesn't start with prefix '%%s'" + %% (full_tag, tag_prefix)) + return pieces + pieces["closest-tag"] = full_tag[len(tag_prefix):] + + # distance: number of commits since tag + pieces["distance"] = int(mo.group(2)) + + # commit: short hex revision ID + pieces["short"] = mo.group(3) + + else: + # HEX: no tags + pieces["closest-tag"] = None + count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"], + cwd=root) + pieces["distance"] = int(count_out) # total number of commits + + # commit date: see ISO-8601 comment in git_versions_from_keywords() + date = run_command(GITS, ["show", "-s", "--format=%%ci", "HEAD"], + cwd=root)[0].strip() + # Use only the last line. Previous lines may contain GPG signature + # information. + date = date.splitlines()[-1] + pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) + + return pieces + + +def plus_or_dot(pieces): + """Return a + if we don't already have one, else return a .""" + if "+" in pieces.get("closest-tag", ""): + return "." + return "+" + + +def render_pep440(pieces): + """Build up version string, with post-release "local version identifier". + + Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you + get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty + + Exceptions: + 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + rendered += plus_or_dot(pieces) + rendered += "%%d.g%%s" %% (pieces["distance"], pieces["short"]) + if pieces["dirty"]: + rendered += ".dirty" + else: + # exception #1 + rendered = "0+untagged.%%d.g%%s" %% (pieces["distance"], + pieces["short"]) + if pieces["dirty"]: + rendered += ".dirty" + return rendered + + +def render_pep440_pre(pieces): + """TAG[.post0.devDISTANCE] -- No -dirty. + + Exceptions: + 1: no tags. 0.post0.devDISTANCE + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"]: + rendered += ".post0.dev%%d" %% pieces["distance"] + else: + # exception #1 + rendered = "0.post0.dev%%d" %% pieces["distance"] + return rendered + + +def render_pep440_post(pieces): + """TAG[.postDISTANCE[.dev0]+gHEX] . + + The ".dev0" means dirty. Note that .dev0 sorts backwards + (a dirty tree will appear "older" than the corresponding clean one), + but you shouldn't be releasing software with -dirty anyways. + + Exceptions: + 1: no tags. 0.postDISTANCE[.dev0] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + rendered += ".post%%d" %% pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + rendered += plus_or_dot(pieces) + rendered += "g%%s" %% pieces["short"] + else: + # exception #1 + rendered = "0.post%%d" %% pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + rendered += "+g%%s" %% pieces["short"] + return rendered + + +def render_pep440_old(pieces): + """TAG[.postDISTANCE[.dev0]] . + + The ".dev0" means dirty. + + Exceptions: + 1: no tags. 0.postDISTANCE[.dev0] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + rendered += ".post%%d" %% pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + else: + # exception #1 + rendered = "0.post%%d" %% pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + return rendered + + +def render_git_describe(pieces): + """TAG[-DISTANCE-gHEX][-dirty]. + + Like 'git describe --tags --dirty --always'. + + Exceptions: + 1: no tags. HEX[-dirty] (note: no 'g' prefix) + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"]: + rendered += "-%%d-g%%s" %% (pieces["distance"], pieces["short"]) + else: + # exception #1 + rendered = pieces["short"] + if pieces["dirty"]: + rendered += "-dirty" + return rendered + + +def render_git_describe_long(pieces): + """TAG-DISTANCE-gHEX[-dirty]. + + Like 'git describe --tags --dirty --always -long'. + The distance/hash is unconditional. + + Exceptions: + 1: no tags. HEX[-dirty] (note: no 'g' prefix) + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + rendered += "-%%d-g%%s" %% (pieces["distance"], pieces["short"]) + else: + # exception #1 + rendered = pieces["short"] + if pieces["dirty"]: + rendered += "-dirty" + return rendered + + +def render(pieces, style): + """Render the given version pieces into the requested style.""" + if pieces["error"]: + return {"version": "unknown", + "full-revisionid": pieces.get("long"), + "dirty": None, + "error": pieces["error"], + "date": None} + + if not style or style == "default": + style = "pep440" # the default + + if style == "pep440": + rendered = render_pep440(pieces) + elif style == "pep440-pre": + rendered = render_pep440_pre(pieces) + elif style == "pep440-post": + rendered = render_pep440_post(pieces) + elif style == "pep440-old": + rendered = render_pep440_old(pieces) + elif style == "git-describe": + rendered = render_git_describe(pieces) + elif style == "git-describe-long": + rendered = render_git_describe_long(pieces) + else: + raise ValueError("unknown style '%%s'" %% style) + + return {"version": rendered, "full-revisionid": pieces["long"], + "dirty": pieces["dirty"], "error": None, + "date": pieces.get("date")} + + +def get_versions(): + """Get version information or return default if unable to do so.""" + # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have + # __file__, we can work backwards from there to the root. Some + # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which + # case we can only use expanded keywords. + + cfg = get_config() + verbose = cfg.verbose + + try: + return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, + verbose) + except NotThisMethod: + pass + + try: + root = os.path.realpath(__file__) + # versionfile_source is the relative path from the top of the source + # tree (where the .git directory might live) to this file. Invert + # this to find the root from __file__. + for i in cfg.versionfile_source.split('/'): + root = os.path.dirname(root) + except NameError: + return {"version": "0+unknown", "full-revisionid": None, + "dirty": None, + "error": "unable to find root of source tree", + "date": None} + + try: + pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose) + return render(pieces, cfg.style) + except NotThisMethod: + pass + + try: + if cfg.parentdir_prefix: + return versions_from_parentdir(cfg.parentdir_prefix, root, verbose) + except NotThisMethod: + pass + + return {"version": "0+unknown", "full-revisionid": None, + "dirty": None, + "error": "unable to compute version", "date": None} +''' + + +@register_vcs_handler("git", "get_keywords") +def git_get_keywords(versionfile_abs): + """Extract version information from the given file.""" + # the code embedded in _version.py can just fetch the value of these + # keywords. When used from setup.py, we don't want to import _version.py, + # so we do it with a regexp instead. This function is not used from + # _version.py. + keywords = {} + try: + with open(versionfile_abs, "r") as f: + for line in f.readlines(): + if line.strip().startswith("git_refnames ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["refnames"] = mo.group(1) + if line.strip().startswith("git_full ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["full"] = mo.group(1) + if line.strip().startswith("git_date ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["date"] = mo.group(1) + except EnvironmentError: + pass + return keywords + + +@register_vcs_handler("git", "keywords") +def git_versions_from_keywords(keywords, tag_prefix, verbose): + """Get version information from git keywords.""" + if not keywords: + raise NotThisMethod("no keywords at all, weird") + date = keywords.get("date") + if date is not None: + # Use only the last line. Previous lines may contain GPG signature + # information. + date = date.splitlines()[-1] + + # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant + # datestamp. However we prefer "%ci" (which expands to an "ISO-8601 + # -like" string, which we must then edit to make compliant), because + # it's been around since git-1.5.3, and it's too difficult to + # discover which version we're using, or to work around using an + # older one. + date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) + refnames = keywords["refnames"].strip() + if refnames.startswith("$Format"): + if verbose: + print("keywords are unexpanded, not using") + raise NotThisMethod("unexpanded keywords, not a git-archive tarball") + refs = set([r.strip() for r in refnames.strip("()").split(",")]) + # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of + # just "foo-1.0". If we see a "tag: " prefix, prefer those. + TAG = "tag: " + tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)]) + if not tags: + # Either we're using git < 1.8.3, or there really are no tags. We use + # a heuristic: assume all version tags have a digit. The old git %d + # expansion behaves like git log --decorate=short and strips out the + # refs/heads/ and refs/tags/ prefixes that would let us distinguish + # between branches and tags. By ignoring refnames without digits, we + # filter out many common branch names like "release" and + # "stabilization", as well as "HEAD" and "master". + tags = set([r for r in refs if re.search(r'\d', r)]) + if verbose: + print("discarding '%s', no digits" % ",".join(refs - tags)) + if verbose: + print("likely tags: %s" % ",".join(sorted(tags))) + for ref in sorted(tags): + # sorting will prefer e.g. "2.0" over "2.0rc1" + if ref.startswith(tag_prefix): + r = ref[len(tag_prefix):] + if verbose: + print("picking %s" % r) + return {"version": r, + "full-revisionid": keywords["full"].strip(), + "dirty": False, "error": None, + "date": date} + # no suitable tags, so version is "0+unknown", but full hex is still there + if verbose: + print("no suitable tags, using unknown + full revision id") + return {"version": "0+unknown", + "full-revisionid": keywords["full"].strip(), + "dirty": False, "error": "no suitable tags", "date": None} + + +@register_vcs_handler("git", "pieces_from_vcs") +def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): + """Get version from 'git describe' in the root of the source tree. + + This only gets called if the git-archive 'subst' keywords were *not* + expanded, and _version.py hasn't already been rewritten with a short + version string, meaning we're inside a checked out source tree. + """ + GITS = ["git"] + if sys.platform == "win32": + GITS = ["git.cmd", "git.exe"] + + out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root, + hide_stderr=True) + if rc != 0: + if verbose: + print("Directory %s not under git control" % root) + raise NotThisMethod("'git rev-parse --git-dir' returned error") + + # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] + # if there isn't one, this yields HEX[-dirty] (no NUM) + describe_out, rc = run_command(GITS, ["describe", "--tags", "--dirty=", + "--always", "--long", + "--match", "%s*" % tag_prefix], + cwd=root) + # --long was added in git-1.5.5 + if describe_out is None: + raise NotThisMethod("'git describe' failed") + describe_out = describe_out.strip() + full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) + if full_out is None: + raise NotThisMethod("'git rev-parse' failed") + full_out = full_out.strip() + + pieces = {} + pieces["long"] = full_out + pieces["short"] = full_out[:7] # maybe improved later + pieces["error"] = None + + # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] + # TAG might have hyphens. + git_describe = describe_out + + # look for -dirty suffix + dirty = git_describe.endswith("-dirty") + pieces["dirty"] = dirty + if dirty: + git_describe = git_describe[:git_describe.rindex("-dirty")] + + # now we have TAG-NUM-gHEX or HEX + + if "-" in git_describe: + # TAG-NUM-gHEX + mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe) + if not mo: + # unparseable. Maybe git-describe is misbehaving? + pieces["error"] = ("unable to parse git-describe output: '%s'" + % describe_out) + return pieces + + # tag + full_tag = mo.group(1) + if not full_tag.startswith(tag_prefix): + if verbose: + fmt = "tag '%s' doesn't start with prefix '%s'" + print(fmt % (full_tag, tag_prefix)) + pieces["error"] = ("tag '%s' doesn't start with prefix '%s'" + % (full_tag, tag_prefix)) + return pieces + pieces["closest-tag"] = full_tag[len(tag_prefix):] + + # distance: number of commits since tag + pieces["distance"] = int(mo.group(2)) + + # commit: short hex revision ID + pieces["short"] = mo.group(3) + + else: + # HEX: no tags + pieces["closest-tag"] = None + count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"], + cwd=root) + pieces["distance"] = int(count_out) # total number of commits + + # commit date: see ISO-8601 comment in git_versions_from_keywords() + date = run_command(GITS, ["show", "-s", "--format=%ci", "HEAD"], + cwd=root)[0].strip() + # Use only the last line. Previous lines may contain GPG signature + # information. + date = date.splitlines()[-1] + pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) + + return pieces + + +def do_vcs_install(manifest_in, versionfile_source, ipy): + """Git-specific installation logic for Versioneer. + + For Git, this means creating/changing .gitattributes to mark _version.py + for export-subst keyword substitution. + """ + GITS = ["git"] + if sys.platform == "win32": + GITS = ["git.cmd", "git.exe"] + files = [manifest_in, versionfile_source] + if ipy: + files.append(ipy) + try: + me = __file__ + if me.endswith(".pyc") or me.endswith(".pyo"): + me = os.path.splitext(me)[0] + ".py" + versioneer_file = os.path.relpath(me) + except NameError: + versioneer_file = "versioneer.py" + files.append(versioneer_file) + present = False + try: + with open(".gitattributes", "r") as f: + for line in f.readlines(): + if line.strip().startswith(versionfile_source): + if "export-subst" in line.strip().split()[1:]: + present = True + except EnvironmentError: + pass + if not present: + with open(".gitattributes", "a+") as f: + f.write("%s export-subst\n" % versionfile_source) + files.append(".gitattributes") + run_command(GITS, ["add", "--"] + files) + + +def versions_from_parentdir(parentdir_prefix, root, verbose): + """Try to determine the version from the parent directory name. + + Source tarballs conventionally unpack into a directory that includes both + the project name and a version string. We will also support searching up + two directory levels for an appropriately named parent directory + """ + rootdirs = [] + + for i in range(3): + dirname = os.path.basename(root) + if dirname.startswith(parentdir_prefix): + return {"version": dirname[len(parentdir_prefix):], + "full-revisionid": None, + "dirty": False, "error": None, "date": None} + else: + rootdirs.append(root) + root = os.path.dirname(root) # up a level + + if verbose: + print("Tried directories %s but none started with prefix %s" % + (str(rootdirs), parentdir_prefix)) + raise NotThisMethod("rootdir doesn't start with parentdir_prefix") + + +SHORT_VERSION_PY = """ +# This file was generated by 'versioneer.py' (0.19) from +# revision-control system data, or from the parent directory name of an +# unpacked source archive. Distribution tarballs contain a pre-generated copy +# of this file. + +import json + +version_json = ''' +%s +''' # END VERSION_JSON + + +def get_versions(): + return json.loads(version_json) +""" + + +def versions_from_file(filename): + """Try to determine the version from _version.py if present.""" + try: + with open(filename) as f: + contents = f.read() + except EnvironmentError: + raise NotThisMethod("unable to read _version.py") + mo = re.search(r"version_json = '''\n(.*)''' # END VERSION_JSON", + contents, re.M | re.S) + if not mo: + mo = re.search(r"version_json = '''\r\n(.*)''' # END VERSION_JSON", + contents, re.M | re.S) + if not mo: + raise NotThisMethod("no version_json in _version.py") + return json.loads(mo.group(1)) + + +def write_to_version_file(filename, versions): + """Write the given version number to the given _version.py file.""" + os.unlink(filename) + contents = json.dumps(versions, sort_keys=True, + indent=1, separators=(",", ": ")) + with open(filename, "w") as f: + f.write(SHORT_VERSION_PY % contents) + + print("set %s to '%s'" % (filename, versions["version"])) + + +def plus_or_dot(pieces): + """Return a + if we don't already have one, else return a .""" + if "+" in pieces.get("closest-tag", ""): + return "." + return "+" + + +def render_pep440(pieces): + """Build up version string, with post-release "local version identifier". + + Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you + get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty + + Exceptions: + 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + rendered += plus_or_dot(pieces) + rendered += "%d.g%s" % (pieces["distance"], pieces["short"]) + if pieces["dirty"]: + rendered += ".dirty" + else: + # exception #1 + rendered = "0+untagged.%d.g%s" % (pieces["distance"], + pieces["short"]) + if pieces["dirty"]: + rendered += ".dirty" + return rendered + + +def render_pep440_pre(pieces): + """TAG[.post0.devDISTANCE] -- No -dirty. + + Exceptions: + 1: no tags. 0.post0.devDISTANCE + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"]: + rendered += ".post0.dev%d" % pieces["distance"] + else: + # exception #1 + rendered = "0.post0.dev%d" % pieces["distance"] + return rendered + + +def render_pep440_post(pieces): + """TAG[.postDISTANCE[.dev0]+gHEX] . + + The ".dev0" means dirty. Note that .dev0 sorts backwards + (a dirty tree will appear "older" than the corresponding clean one), + but you shouldn't be releasing software with -dirty anyways. + + Exceptions: + 1: no tags. 0.postDISTANCE[.dev0] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + rendered += ".post%d" % pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + rendered += plus_or_dot(pieces) + rendered += "g%s" % pieces["short"] + else: + # exception #1 + rendered = "0.post%d" % pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + rendered += "+g%s" % pieces["short"] + return rendered + + +def render_pep440_old(pieces): + """TAG[.postDISTANCE[.dev0]] . + + The ".dev0" means dirty. + + Exceptions: + 1: no tags. 0.postDISTANCE[.dev0] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + rendered += ".post%d" % pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + else: + # exception #1 + rendered = "0.post%d" % pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + return rendered + + +def render_git_describe(pieces): + """TAG[-DISTANCE-gHEX][-dirty]. + + Like 'git describe --tags --dirty --always'. + + Exceptions: + 1: no tags. HEX[-dirty] (note: no 'g' prefix) + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"]: + rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) + else: + # exception #1 + rendered = pieces["short"] + if pieces["dirty"]: + rendered += "-dirty" + return rendered + + +def render_git_describe_long(pieces): + """TAG-DISTANCE-gHEX[-dirty]. + + Like 'git describe --tags --dirty --always -long'. + The distance/hash is unconditional. + + Exceptions: + 1: no tags. HEX[-dirty] (note: no 'g' prefix) + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) + else: + # exception #1 + rendered = pieces["short"] + if pieces["dirty"]: + rendered += "-dirty" + return rendered + + +def render(pieces, style): + """Render the given version pieces into the requested style.""" + if pieces["error"]: + return {"version": "unknown", + "full-revisionid": pieces.get("long"), + "dirty": None, + "error": pieces["error"], + "date": None} + + if not style or style == "default": + style = "pep440" # the default + + if style == "pep440": + rendered = render_pep440(pieces) + elif style == "pep440-pre": + rendered = render_pep440_pre(pieces) + elif style == "pep440-post": + rendered = render_pep440_post(pieces) + elif style == "pep440-old": + rendered = render_pep440_old(pieces) + elif style == "git-describe": + rendered = render_git_describe(pieces) + elif style == "git-describe-long": + rendered = render_git_describe_long(pieces) + else: + raise ValueError("unknown style '%s'" % style) + + return {"version": rendered, "full-revisionid": pieces["long"], + "dirty": pieces["dirty"], "error": None, + "date": pieces.get("date")} + + +class VersioneerBadRootError(Exception): + """The project root directory is unknown or missing key files.""" + + +def get_versions(verbose=False): + """Get the project version from whatever source is available. + + Returns dict with two keys: 'version' and 'full'. + """ + if "versioneer" in sys.modules: + # see the discussion in cmdclass.py:get_cmdclass() + del sys.modules["versioneer"] + + root = get_root() + cfg = get_config_from_root(root) + + assert cfg.VCS is not None, "please set [versioneer]VCS= in setup.cfg" + handlers = HANDLERS.get(cfg.VCS) + assert handlers, "unrecognized VCS '%s'" % cfg.VCS + verbose = verbose or cfg.verbose + assert cfg.versionfile_source is not None, \ + "please set versioneer.versionfile_source" + assert cfg.tag_prefix is not None, "please set versioneer.tag_prefix" + + versionfile_abs = os.path.join(root, cfg.versionfile_source) + + # extract version from first of: _version.py, VCS command (e.g. 'git + # describe'), parentdir. This is meant to work for developers using a + # source checkout, for users of a tarball created by 'setup.py sdist', + # and for users of a tarball/zipball created by 'git archive' or github's + # download-from-tag feature or the equivalent in other VCSes. + + get_keywords_f = handlers.get("get_keywords") + from_keywords_f = handlers.get("keywords") + if get_keywords_f and from_keywords_f: + try: + keywords = get_keywords_f(versionfile_abs) + ver = from_keywords_f(keywords, cfg.tag_prefix, verbose) + if verbose: + print("got version from expanded keyword %s" % ver) + return ver + except NotThisMethod: + pass + + try: + ver = versions_from_file(versionfile_abs) + if verbose: + print("got version from file %s %s" % (versionfile_abs, ver)) + return ver + except NotThisMethod: + pass + + from_vcs_f = handlers.get("pieces_from_vcs") + if from_vcs_f: + try: + pieces = from_vcs_f(cfg.tag_prefix, root, verbose) + ver = render(pieces, cfg.style) + if verbose: + print("got version from VCS %s" % ver) + return ver + except NotThisMethod: + pass + + try: + if cfg.parentdir_prefix: + ver = versions_from_parentdir(cfg.parentdir_prefix, root, verbose) + if verbose: + print("got version from parentdir %s" % ver) + return ver + except NotThisMethod: + pass + + if verbose: + print("unable to compute version") + + return {"version": "0+unknown", "full-revisionid": None, + "dirty": None, "error": "unable to compute version", + "date": None} + + +def get_version(): + """Get the short version string for this project.""" + return get_versions()["version"] + + +def get_cmdclass(cmdclass=None): + """Get the custom setuptools/distutils subclasses used by Versioneer. + + If the package uses a different cmdclass (e.g. one from numpy), it + should be provide as an argument. + """ + if "versioneer" in sys.modules: + del sys.modules["versioneer"] + # this fixes the "python setup.py develop" case (also 'install' and + # 'easy_install .'), in which subdependencies of the main project are + # built (using setup.py bdist_egg) in the same python process. Assume + # a main project A and a dependency B, which use different versions + # of Versioneer. A's setup.py imports A's Versioneer, leaving it in + # sys.modules by the time B's setup.py is executed, causing B to run + # with the wrong versioneer. Setuptools wraps the sub-dep builds in a + # sandbox that restores sys.modules to it's pre-build state, so the + # parent is protected against the child's "import versioneer". By + # removing ourselves from sys.modules here, before the child build + # happens, we protect the child from the parent's versioneer too. + # Also see https://github.com/python-versioneer/python-versioneer/issues/52 + + cmds = {} if cmdclass is None else cmdclass.copy() + + # we add "version" to both distutils and setuptools + from distutils.core import Command + + class cmd_version(Command): + description = "report generated version string" + user_options = [] + boolean_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + vers = get_versions(verbose=True) + print("Version: %s" % vers["version"]) + print(" full-revisionid: %s" % vers.get("full-revisionid")) + print(" dirty: %s" % vers.get("dirty")) + print(" date: %s" % vers.get("date")) + if vers["error"]: + print(" error: %s" % vers["error"]) + cmds["version"] = cmd_version + + # we override "build_py" in both distutils and setuptools + # + # most invocation pathways end up running build_py: + # distutils/build -> build_py + # distutils/install -> distutils/build ->.. + # setuptools/bdist_wheel -> distutils/install ->.. + # setuptools/bdist_egg -> distutils/install_lib -> build_py + # setuptools/install -> bdist_egg ->.. + # setuptools/develop -> ? + # pip install: + # copies source tree to a tempdir before running egg_info/etc + # if .git isn't copied too, 'git describe' will fail + # then does setup.py bdist_wheel, or sometimes setup.py install + # setup.py egg_info -> ? + + # we override different "build_py" commands for both environments + if 'build_py' in cmds: + _build_py = cmds['build_py'] + elif "setuptools" in sys.modules: + from setuptools.command.build_py import build_py as _build_py + else: + from distutils.command.build_py import build_py as _build_py + + class cmd_build_py(_build_py): + def run(self): + root = get_root() + cfg = get_config_from_root(root) + versions = get_versions() + _build_py.run(self) + # now locate _version.py in the new build/ directory and replace + # it with an updated value + if cfg.versionfile_build: + target_versionfile = os.path.join(self.build_lib, + cfg.versionfile_build) + print("UPDATING %s" % target_versionfile) + write_to_version_file(target_versionfile, versions) + cmds["build_py"] = cmd_build_py + + if "setuptools" in sys.modules: + from setuptools.command.build_ext import build_ext as _build_ext + else: + from distutils.command.build_ext import build_ext as _build_ext + + class cmd_build_ext(_build_ext): + def run(self): + root = get_root() + cfg = get_config_from_root(root) + versions = get_versions() + _build_ext.run(self) + if self.inplace: + # build_ext --inplace will only build extensions in + # build/lib<..> dir with no _version.py to write to. + # As in place builds will already have a _version.py + # in the module dir, we do not need to write one. + return + # now locate _version.py in the new build/ directory and replace + # it with an updated value + target_versionfile = os.path.join(self.build_lib, + cfg.versionfile_source) + print("UPDATING %s" % target_versionfile) + write_to_version_file(target_versionfile, versions) + cmds["build_ext"] = cmd_build_ext + + if "cx_Freeze" in sys.modules: # cx_freeze enabled? + from cx_Freeze.dist import build_exe as _build_exe + # nczeczulin reports that py2exe won't like the pep440-style string + # as FILEVERSION, but it can be used for PRODUCTVERSION, e.g. + # setup(console=[{ + # "version": versioneer.get_version().split("+", 1)[0], # FILEVERSION + # "product_version": versioneer.get_version(), + # ... + + class cmd_build_exe(_build_exe): + def run(self): + root = get_root() + cfg = get_config_from_root(root) + versions = get_versions() + target_versionfile = cfg.versionfile_source + print("UPDATING %s" % target_versionfile) + write_to_version_file(target_versionfile, versions) + + _build_exe.run(self) + os.unlink(target_versionfile) + with open(cfg.versionfile_source, "w") as f: + LONG = LONG_VERSION_PY[cfg.VCS] + f.write(LONG % + {"DOLLAR": "$", + "STYLE": cfg.style, + "TAG_PREFIX": cfg.tag_prefix, + "PARENTDIR_PREFIX": cfg.parentdir_prefix, + "VERSIONFILE_SOURCE": cfg.versionfile_source, + }) + cmds["build_exe"] = cmd_build_exe + del cmds["build_py"] + + if 'py2exe' in sys.modules: # py2exe enabled? + from py2exe.distutils_buildexe import py2exe as _py2exe + + class cmd_py2exe(_py2exe): + def run(self): + root = get_root() + cfg = get_config_from_root(root) + versions = get_versions() + target_versionfile = cfg.versionfile_source + print("UPDATING %s" % target_versionfile) + write_to_version_file(target_versionfile, versions) + + _py2exe.run(self) + os.unlink(target_versionfile) + with open(cfg.versionfile_source, "w") as f: + LONG = LONG_VERSION_PY[cfg.VCS] + f.write(LONG % + {"DOLLAR": "$", + "STYLE": cfg.style, + "TAG_PREFIX": cfg.tag_prefix, + "PARENTDIR_PREFIX": cfg.parentdir_prefix, + "VERSIONFILE_SOURCE": cfg.versionfile_source, + }) + cmds["py2exe"] = cmd_py2exe + + # we override different "sdist" commands for both environments + if 'sdist' in cmds: + _sdist = cmds['sdist'] + elif "setuptools" in sys.modules: + from setuptools.command.sdist import sdist as _sdist + else: + from distutils.command.sdist import sdist as _sdist + + class cmd_sdist(_sdist): + def run(self): + versions = get_versions() + self._versioneer_generated_versions = versions + # unless we update this, the command will keep using the old + # version + self.distribution.metadata.version = versions["version"] + return _sdist.run(self) + + def make_release_tree(self, base_dir, files): + root = get_root() + cfg = get_config_from_root(root) + _sdist.make_release_tree(self, base_dir, files) + # now locate _version.py in the new base_dir directory + # (remembering that it may be a hardlink) and replace it with an + # updated value + target_versionfile = os.path.join(base_dir, cfg.versionfile_source) + print("UPDATING %s" % target_versionfile) + write_to_version_file(target_versionfile, + self._versioneer_generated_versions) + cmds["sdist"] = cmd_sdist + + return cmds + + +CONFIG_ERROR = """ +setup.cfg is missing the necessary Versioneer configuration. You need +a section like: + + [versioneer] + VCS = git + style = pep440 + versionfile_source = src/myproject/_version.py + versionfile_build = myproject/_version.py + tag_prefix = + parentdir_prefix = myproject- + +You will also need to edit your setup.py to use the results: + + import versioneer + setup(version=versioneer.get_version(), + cmdclass=versioneer.get_cmdclass(), ...) + +Please read the docstring in ./versioneer.py for configuration instructions, +edit setup.cfg, and re-run the installer or 'python versioneer.py setup'. +""" + +SAMPLE_CONFIG = """ +# See the docstring in versioneer.py for instructions. Note that you must +# re-run 'versioneer.py setup' after changing this section, and commit the +# resulting files. + +[versioneer] +#VCS = git +#style = pep440 +#versionfile_source = +#versionfile_build = +#tag_prefix = +#parentdir_prefix = + +""" + +INIT_PY_SNIPPET = """ +from ._version import get_versions +__version__ = get_versions()['version'] +del get_versions +""" + + +def do_setup(): + """Do main VCS-independent setup function for installing Versioneer.""" + root = get_root() + try: + cfg = get_config_from_root(root) + except (EnvironmentError, configparser.NoSectionError, + configparser.NoOptionError) as e: + if isinstance(e, (EnvironmentError, configparser.NoSectionError)): + print("Adding sample versioneer config to setup.cfg", + file=sys.stderr) + with open(os.path.join(root, "setup.cfg"), "a") as f: + f.write(SAMPLE_CONFIG) + print(CONFIG_ERROR, file=sys.stderr) + return 1 + + print(" creating %s" % cfg.versionfile_source) + with open(cfg.versionfile_source, "w") as f: + LONG = LONG_VERSION_PY[cfg.VCS] + f.write(LONG % {"DOLLAR": "$", + "STYLE": cfg.style, + "TAG_PREFIX": cfg.tag_prefix, + "PARENTDIR_PREFIX": cfg.parentdir_prefix, + "VERSIONFILE_SOURCE": cfg.versionfile_source, + }) + + ipy = os.path.join(os.path.dirname(cfg.versionfile_source), + "__init__.py") + if os.path.exists(ipy): + try: + with open(ipy, "r") as f: + old = f.read() + except EnvironmentError: + old = "" + if INIT_PY_SNIPPET not in old: + print(" appending to %s" % ipy) + with open(ipy, "a") as f: + f.write(INIT_PY_SNIPPET) + else: + print(" %s unmodified" % ipy) + else: + print(" %s doesn't exist, ok" % ipy) + ipy = None + + # Make sure both the top-level "versioneer.py" and versionfile_source + # (PKG/_version.py, used by runtime code) are in MANIFEST.in, so + # they'll be copied into source distributions. Pip won't be able to + # install the package without this. + manifest_in = os.path.join(root, "MANIFEST.in") + simple_includes = set() + try: + with open(manifest_in, "r") as f: + for line in f: + if line.startswith("include "): + for include in line.split()[1:]: + simple_includes.add(include) + except EnvironmentError: + pass + # That doesn't cover everything MANIFEST.in can do + # (http://docs.python.org/2/distutils/sourcedist.html#commands), so + # it might give some false negatives. Appending redundant 'include' + # lines is safe, though. + if "versioneer.py" not in simple_includes: + print(" appending 'versioneer.py' to MANIFEST.in") + with open(manifest_in, "a") as f: + f.write("include versioneer.py\n") + else: + print(" 'versioneer.py' already in MANIFEST.in") + if cfg.versionfile_source not in simple_includes: + print(" appending versionfile_source ('%s') to MANIFEST.in" % + cfg.versionfile_source) + with open(manifest_in, "a") as f: + f.write("include %s\n" % cfg.versionfile_source) + else: + print(" versionfile_source already in MANIFEST.in") + + # Make VCS-specific changes. For git, this means creating/changing + # .gitattributes to mark _version.py for export-subst keyword + # substitution. + do_vcs_install(manifest_in, cfg.versionfile_source, ipy) + return 0 + + +def scan_setup_py(): + """Validate the contents of setup.py against Versioneer's expectations.""" + found = set() + setters = False + errors = 0 + with open("setup.py", "r") as f: + for line in f.readlines(): + if "import versioneer" in line: + found.add("import") + if "versioneer.get_cmdclass()" in line: + found.add("cmdclass") + if "versioneer.get_version()" in line: + found.add("get_version") + if "versioneer.VCS" in line: + setters = True + if "versioneer.versionfile_source" in line: + setters = True + if len(found) != 3: + print("") + print("Your setup.py appears to be missing some important items") + print("(but I might be wrong). Please make sure it has something") + print("roughly like the following:") + print("") + print(" import versioneer") + print(" setup( version=versioneer.get_version(),") + print(" cmdclass=versioneer.get_cmdclass(), ...)") + print("") + errors += 1 + if setters: + print("You should remove lines like 'versioneer.VCS = ' and") + print("'versioneer.versionfile_source = ' . This configuration") + print("now lives in setup.cfg, and should be removed from setup.py") + print("") + errors += 1 + return errors + + +if __name__ == "__main__": + cmd = sys.argv[1] + if cmd == "setup": + errors = do_setup() + errors += scan_setup_py() + if errors: + sys.exit(1)